/* game framework (3rd libs) * - rlyeh, public domain */ #ifdef V4K_3RD #define GLAD_GL_IMPLEMENTATION // glad #endif #line 1 "3rd_glad.h" #ifndef __EMSCRIPTEN__ #ifndef _GNU_SOURCE // juicy linux headers #define _GNU_SOURCE #endif #if defined(_MSC_VER) && defined(_WIN32) // for VC IDE #define _CRT_SECURE_NO_WARNINGS #define _CRT_NONSTDC_NO_DEPRECATE #define _WINSOCK_DEPRECATED_NO_WARNINGS #endif /** * Loader generated by glad 2.0.0-beta on Sat Mar 28 10:21:14 2020 * * Generator: C/C++ * Specification: gl * Extensions: 183 * * APIs: * - gl:compatibility=3.3 * * Options: * - MX_GLOBAL = False * - ON_DEMAND = False * - LOADER = False * - ALIAS = False * - HEADER_ONLY = True * - DEBUG = False * - MX = False * - MERGE = True * * Commandline: * --api='gl:compatibility=3.3' --extensions='GL_ARB_ES2_compatibility,GL_ARB_ES3_1_compatibility,GL_ARB_ES3_2_compatibility,GL_ARB_ES3_compatibility,GL_ARB_arrays_of_arrays,GL_ARB_base_instance,GL_ARB_bindless_texture,GL_ARB_blend_func_extended,GL_ARB_buffer_storage,GL_ARB_cl_event,GL_ARB_clear_buffer_object,GL_ARB_clear_texture,GL_ARB_clip_control,GL_ARB_color_buffer_float,GL_ARB_compatibility,GL_ARB_compressed_texture_pixel_storage,GL_ARB_compute_shader,GL_ARB_compute_variable_group_size,GL_ARB_conditional_render_inverted,GL_ARB_conservative_depth,GL_ARB_copy_buffer,GL_ARB_copy_image,GL_ARB_cull_distance,GL_ARB_debug_output,GL_ARB_depth_buffer_float,GL_ARB_depth_clamp,GL_ARB_depth_texture,GL_ARB_derivative_control,GL_ARB_direct_state_access,GL_ARB_draw_buffers,GL_ARB_draw_buffers_blend,GL_ARB_draw_elements_base_vertex,GL_ARB_draw_indirect,GL_ARB_draw_instanced,GL_ARB_enhanced_layouts,GL_ARB_explicit_attrib_location,GL_ARB_explicit_uniform_location,GL_ARB_fragment_coord_conventions,GL_ARB_fragment_layer_viewport,GL_ARB_fragment_program,GL_ARB_fragment_program_shadow,GL_ARB_fragment_shader,GL_ARB_fragment_shader_interlock,GL_ARB_framebuffer_no_attachments,GL_ARB_framebuffer_object,GL_ARB_framebuffer_sRGB,GL_ARB_geometry_shader4,GL_ARB_get_program_binary,GL_ARB_get_texture_sub_image,GL_ARB_gl_spirv,GL_ARB_gpu_shader5,GL_ARB_gpu_shader_fp64,GL_ARB_gpu_shader_int64,GL_ARB_half_float_pixel,GL_ARB_half_float_vertex,GL_ARB_imaging,GL_ARB_indirect_parameters,GL_ARB_instanced_arrays,GL_ARB_internalformat_query,GL_ARB_internalformat_query2,GL_ARB_invalidate_subdata,GL_ARB_map_buffer_alignment,GL_ARB_map_buffer_range,GL_ARB_matrix_palette,GL_ARB_multi_bind,GL_ARB_multi_draw_indirect,GL_ARB_multisample,GL_ARB_multitexture,GL_ARB_occlusion_query,GL_ARB_occlusion_query2,GL_ARB_parallel_shader_compile,GL_ARB_pipeline_statistics_query,GL_ARB_pixel_buffer_object,GL_ARB_point_parameters,GL_ARB_point_sprite,GL_ARB_polygon_offset_clamp,GL_ARB_post_depth_coverage,GL_ARB_program_interface_query,GL_ARB_provoking_vertex,GL_ARB_query_buffer_object,GL_ARB_robust_buffer_access_behavior,GL_ARB_robustness,GL_ARB_robustness_isolation,GL_ARB_sample_locations,GL_ARB_sample_shading,GL_ARB_sampler_objects,GL_ARB_seamless_cube_map,GL_ARB_seamless_cubemap_per_texture,GL_ARB_separate_shader_objects,GL_ARB_shader_atomic_counter_ops,GL_ARB_shader_atomic_counters,GL_ARB_shader_ballot,GL_ARB_shader_bit_encoding,GL_ARB_shader_clock,GL_ARB_shader_draw_parameters,GL_ARB_shader_group_vote,GL_ARB_shader_image_load_store,GL_ARB_shader_image_size,GL_ARB_shader_objects,GL_ARB_shader_precision,GL_ARB_shader_stencil_export,GL_ARB_shader_storage_buffer_object,GL_ARB_shader_subroutine,GL_ARB_shader_texture_image_samples,GL_ARB_shader_texture_lod,GL_ARB_shader_viewport_layer_array,GL_ARB_shading_language_100,GL_ARB_shading_language_420pack,GL_ARB_shading_language_include,GL_ARB_shading_language_packing,GL_ARB_shadow,GL_ARB_shadow_ambient,GL_ARB_sparse_buffer,GL_ARB_sparse_texture,GL_ARB_sparse_texture2,GL_ARB_sparse_texture_clamp,GL_ARB_spirv_extensions,GL_ARB_stencil_texturing,GL_ARB_sync,GL_ARB_tessellation_shader,GL_ARB_texture_barrier,GL_ARB_texture_border_clamp,GL_ARB_texture_buffer_object,GL_ARB_texture_buffer_object_rgb32,GL_ARB_texture_buffer_range,GL_ARB_texture_compression,GL_ARB_texture_compression_bptc,GL_ARB_texture_compression_rgtc,GL_ARB_texture_cube_map,GL_ARB_texture_cube_map_array,GL_ARB_texture_env_add,GL_ARB_texture_env_combine,GL_ARB_texture_env_crossbar,GL_ARB_texture_env_dot3,GL_ARB_texture_filter_anisotropic,GL_ARB_texture_filter_minmax,GL_ARB_texture_float,GL_ARB_texture_gather,GL_ARB_texture_mirror_clamp_to_edge,GL_ARB_texture_mirrored_repeat,GL_ARB_texture_multisample,GL_ARB_texture_non_power_of_two,GL_ARB_texture_query_levels,GL_ARB_texture_query_lod,GL_ARB_texture_rectangle,GL_ARB_texture_rg,GL_ARB_texture_rgb10_a2ui,GL_ARB_texture_stencil8,GL_ARB_texture_storage,GL_ARB_texture_storage_multisample,GL_ARB_texture_swizzle,GL_ARB_texture_view,GL_ARB_timer_query,GL_ARB_transform_feedback2,GL_ARB_transform_feedback3,GL_ARB_transform_feedback_instanced,GL_ARB_transform_feedback_overflow_query,GL_ARB_transpose_matrix,GL_ARB_uniform_buffer_object,GL_ARB_vertex_array_bgra,GL_ARB_vertex_array_object,GL_ARB_vertex_attrib_64bit,GL_ARB_vertex_attrib_binding,GL_ARB_vertex_blend,GL_ARB_vertex_buffer_object,GL_ARB_vertex_program,GL_ARB_vertex_shader,GL_ARB_vertex_type_10f_11f_11f_rev,GL_ARB_vertex_type_2_10_10_10_rev,GL_ARB_viewport_array,GL_ARB_window_pos,GL_KHR_blend_equation_advanced,GL_KHR_blend_equation_advanced_coherent,GL_KHR_context_flush_control,GL_KHR_debug,GL_KHR_no_error,GL_KHR_parallel_shader_compile,GL_KHR_robust_buffer_access_behavior,GL_KHR_robustness,GL_KHR_shader_subgroup,GL_KHR_texture_compression_astc_hdr,GL_KHR_texture_compression_astc_ldr,GL_KHR_texture_compression_astc_sliced_3d' c --header-only * * Online: * http://glad.sh/#api=gl%3Acompatibility%3D3.3&generator=c&options=HEADER_ONLY * */ #ifndef GLAD_GL_H_ #define GLAD_GL_H_ #ifdef __gl_h_ #error OpenGL header already included (API: gl), remove previous include! #endif #define __gl_h_ 1 #define GLAD_GL //#define GLAD_OPTION_GL_HEADER_ONLY #ifdef __cplusplus extern "C" { #endif #ifndef GLAD_PLATFORM_H_ #define GLAD_PLATFORM_H_ #ifndef GLAD_PLATFORM_WIN32 #if defined(_WIN32) || defined(__WIN32__) || defined(WIN32) || defined(__MINGW32__) #define GLAD_PLATFORM_WIN32 1 #else #define GLAD_PLATFORM_WIN32 0 #endif #endif #ifndef GLAD_PLATFORM_APPLE #ifdef __APPLE__ #define GLAD_PLATFORM_APPLE 1 #else #define GLAD_PLATFORM_APPLE 0 #endif #endif #ifndef GLAD_PLATFORM_EMSCRIPTEN #ifdef __EMSCRIPTEN__ #define GLAD_PLATFORM_EMSCRIPTEN 1 #else #define GLAD_PLATFORM_EMSCRIPTEN 0 #endif #endif #ifndef GLAD_PLATFORM_UWP #if defined(_MSC_VER) && !defined(GLAD_INTERNAL_HAVE_WINAPIFAMILY) #ifdef __has_include #if __has_include() #define GLAD_INTERNAL_HAVE_WINAPIFAMILY 1 #endif #elif _MSC_VER >= 1700 && !_USING_V110_SDK71_ #define GLAD_INTERNAL_HAVE_WINAPIFAMILY 1 #endif #endif #ifdef GLAD_INTERNAL_HAVE_WINAPIFAMILY #include #if !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP) #define GLAD_PLATFORM_UWP 1 #endif #endif #ifndef GLAD_PLATFORM_UWP #define GLAD_PLATFORM_UWP 0 #endif #endif #ifdef __GNUC__ #define GLAD_GNUC_EXTENSION __extension__ #else #define GLAD_GNUC_EXTENSION #endif #ifndef GLAD_API_CALL #if defined(GLAD_API_CALL_EXPORT) || (defined API) // @r-lyeh #if GLAD_PLATFORM_WIN32 || defined(__CYGWIN__) #if defined(GLAD_API_CALL_EXPORT_BUILD) #if defined(__GNUC__) #define GLAD_API_CALL __attribute__ ((dllexport)) extern #else #define GLAD_API_CALL __declspec(dllexport) extern #endif #else #if defined(__GNUC__) #define GLAD_API_CALL __attribute__ ((dllimport)) extern #else #define GLAD_API_CALL __declspec(dllimport) extern #endif #endif #elif defined(__GNUC__) && defined(GLAD_API_CALL_EXPORT_BUILD) #define GLAD_API_CALL __attribute__ ((visibility ("default"))) extern #else #define GLAD_API_CALL extern #endif #else #define GLAD_API_CALL extern #endif #endif #ifdef APIENTRY #define GLAD_API_PTR APIENTRY #elif GLAD_PLATFORM_WIN32 #define GLAD_API_PTR __stdcall #else #define GLAD_API_PTR #endif #ifndef GLAPI #define GLAPI GLAD_API_CALL #endif #ifndef GLAPIENTRY #define GLAPIENTRY GLAD_API_PTR #endif #define GLAD_MAKE_VERSION(major, minor) (major * 10000 + minor) #define GLAD_VERSION_MAJOR(version) (version / 10000) #define GLAD_VERSION_MINOR(version) (version % 10000) #define GLAD_GENERATOR_VERSION "2.0.0-beta" typedef void (*GLADapiproc)(void); typedef GLADapiproc (*GLADloadfunc)(const char *name); typedef GLADapiproc (*GLADuserptrloadfunc)(void *userptr, const char *name); typedef void (*GLADprecallback)(const char *name, GLADapiproc apiproc, int len_args, ...); typedef void (*GLADpostcallback)(void *ret, const char *name, GLADapiproc apiproc, int len_args, ...); #endif /* GLAD_PLATFORM_H_ */ #define GL_2D 0x0600 #define GL_2_BYTES 0x1407 #define GL_3D 0x0601 #define GL_3D_COLOR 0x0602 #define GL_3D_COLOR_TEXTURE 0x0603 #define GL_3_BYTES 0x1408 #define GL_4D_COLOR_TEXTURE 0x0604 #define GL_4_BYTES 0x1409 #define GL_ACCUM 0x0100 #define GL_ACCUM_ALPHA_BITS 0x0D5B #define GL_ACCUM_BLUE_BITS 0x0D5A #define GL_ACCUM_BUFFER_BIT 0x00000200 #define GL_ACCUM_CLEAR_VALUE 0x0B80 #define GL_ACCUM_GREEN_BITS 0x0D59 #define GL_ACCUM_RED_BITS 0x0D58 #define GL_ACTIVE_ATOMIC_COUNTER_BUFFERS 0x92D9 #define GL_ACTIVE_ATTRIBUTES 0x8B89 #define GL_ACTIVE_ATTRIBUTE_MAX_LENGTH 0x8B8A #define GL_ACTIVE_PROGRAM 0x8259 #define GL_ACTIVE_RESOURCES 0x92F5 #define GL_ACTIVE_SUBROUTINES 0x8DE5 #define GL_ACTIVE_SUBROUTINE_MAX_LENGTH 0x8E48 #define GL_ACTIVE_SUBROUTINE_UNIFORMS 0x8DE6 #define GL_ACTIVE_SUBROUTINE_UNIFORM_LOCATIONS 0x8E47 #define GL_ACTIVE_SUBROUTINE_UNIFORM_MAX_LENGTH 0x8E49 #define GL_ACTIVE_TEXTURE 0x84E0 #define GL_ACTIVE_TEXTURE_ARB 0x84E0 #define GL_ACTIVE_UNIFORMS 0x8B86 #define GL_ACTIVE_UNIFORM_BLOCKS 0x8A36 #define GL_ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH 0x8A35 #define GL_ACTIVE_UNIFORM_MAX_LENGTH 0x8B87 #define GL_ACTIVE_VARIABLES 0x9305 #define GL_ACTIVE_VERTEX_UNITS_ARB 0x86A5 #define GL_ADD 0x0104 #define GL_ADD_SIGNED 0x8574 #define GL_ADD_SIGNED_ARB 0x8574 #define GL_ALIASED_LINE_WIDTH_RANGE 0x846E #define GL_ALIASED_POINT_SIZE_RANGE 0x846D #define GL_ALL_ATTRIB_BITS 0xFFFFFFFF #define GL_ALL_BARRIER_BITS 0xFFFFFFFF #define GL_ALL_SHADER_BITS 0xFFFFFFFF #define GL_ALPHA 0x1906 #define GL_ALPHA12 0x803D #define GL_ALPHA16 0x803E #define GL_ALPHA16F_ARB 0x881C #define GL_ALPHA32F_ARB 0x8816 #define GL_ALPHA4 0x803B #define GL_ALPHA8 0x803C #define GL_ALPHA_BIAS 0x0D1D #define GL_ALPHA_BITS 0x0D55 #define GL_ALPHA_INTEGER 0x8D97 #define GL_ALPHA_SCALE 0x0D1C #define GL_ALPHA_TEST 0x0BC0 #define GL_ALPHA_TEST_FUNC 0x0BC1 #define GL_ALPHA_TEST_REF 0x0BC2 #define GL_ALREADY_SIGNALED 0x911A #define GL_ALWAYS 0x0207 #define GL_AMBIENT 0x1200 #define GL_AMBIENT_AND_DIFFUSE 0x1602 #define GL_AND 0x1501 #define GL_AND_INVERTED 0x1504 #define GL_AND_REVERSE 0x1502 #define GL_ANY_SAMPLES_PASSED 0x8C2F #define GL_ANY_SAMPLES_PASSED_CONSERVATIVE 0x8D6A #define GL_ARRAY_BUFFER 0x8892 #define GL_ARRAY_BUFFER_ARB 0x8892 #define GL_ARRAY_BUFFER_BINDING 0x8894 #define GL_ARRAY_BUFFER_BINDING_ARB 0x8894 #define GL_ARRAY_SIZE 0x92FB #define GL_ARRAY_STRIDE 0x92FE #define GL_ATOMIC_COUNTER_BARRIER_BIT 0x00001000 #define GL_ATOMIC_COUNTER_BUFFER 0x92C0 #define GL_ATOMIC_COUNTER_BUFFER_ACTIVE_ATOMIC_COUNTERS 0x92C5 #define GL_ATOMIC_COUNTER_BUFFER_ACTIVE_ATOMIC_COUNTER_INDICES 0x92C6 #define GL_ATOMIC_COUNTER_BUFFER_BINDING 0x92C1 #define GL_ATOMIC_COUNTER_BUFFER_DATA_SIZE 0x92C4 #define GL_ATOMIC_COUNTER_BUFFER_INDEX 0x9301 #define GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_COMPUTE_SHADER 0x90ED #define GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_FRAGMENT_SHADER 0x92CB #define GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_GEOMETRY_SHADER 0x92CA #define GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_TESS_CONTROL_SHADER 0x92C8 #define GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_TESS_EVALUATION_SHADER 0x92C9 #define GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_VERTEX_SHADER 0x92C7 #define GL_ATOMIC_COUNTER_BUFFER_SIZE 0x92C3 #define GL_ATOMIC_COUNTER_BUFFER_START 0x92C2 #define GL_ATTACHED_SHADERS 0x8B85 #define GL_ATTRIB_STACK_DEPTH 0x0BB0 #define GL_AUTO_GENERATE_MIPMAP 0x8295 #define GL_AUTO_NORMAL 0x0D80 #define GL_AUX0 0x0409 #define GL_AUX1 0x040A #define GL_AUX2 0x040B #define GL_AUX3 0x040C #define GL_AUX_BUFFERS 0x0C00 #define GL_BACK 0x0405 #define GL_BACK_LEFT 0x0402 #define GL_BACK_RIGHT 0x0403 #define GL_BGR 0x80E0 #define GL_BGRA 0x80E1 #define GL_BGRA_INTEGER 0x8D9B #define GL_BGR_INTEGER 0x8D9A #define GL_BITMAP 0x1A00 #define GL_BITMAP_TOKEN 0x0704 #define GL_BLEND 0x0BE2 #define GL_BLEND_ADVANCED_COHERENT_KHR 0x9285 #define GL_BLEND_COLOR 0x8005 #define GL_BLEND_DST 0x0BE0 #define GL_BLEND_DST_ALPHA 0x80CA #define GL_BLEND_DST_RGB 0x80C8 #define GL_BLEND_EQUATION 0x8009 #define GL_BLEND_EQUATION_ALPHA 0x883D #define GL_BLEND_EQUATION_RGB 0x8009 #define GL_BLEND_SRC 0x0BE1 #define GL_BLEND_SRC_ALPHA 0x80CB #define GL_BLEND_SRC_RGB 0x80C9 #define GL_BLOCK_INDEX 0x92FD #define GL_BLUE 0x1905 #define GL_BLUE_BIAS 0x0D1B #define GL_BLUE_BITS 0x0D54 #define GL_BLUE_INTEGER 0x8D96 #define GL_BLUE_SCALE 0x0D1A #define GL_BOOL 0x8B56 #define GL_BOOL_ARB 0x8B56 #define GL_BOOL_VEC2 0x8B57 #define GL_BOOL_VEC2_ARB 0x8B57 #define GL_BOOL_VEC3 0x8B58 #define GL_BOOL_VEC3_ARB 0x8B58 #define GL_BOOL_VEC4 0x8B59 #define GL_BOOL_VEC4_ARB 0x8B59 #define GL_BUFFER 0x82E0 #define GL_BUFFER_ACCESS 0x88BB #define GL_BUFFER_ACCESS_ARB 0x88BB #define GL_BUFFER_ACCESS_FLAGS 0x911F #define GL_BUFFER_BINDING 0x9302 #define GL_BUFFER_DATA_SIZE 0x9303 #define GL_BUFFER_IMMUTABLE_STORAGE 0x821F #define GL_BUFFER_MAPPED 0x88BC #define GL_BUFFER_MAPPED_ARB 0x88BC #define GL_BUFFER_MAP_LENGTH 0x9120 #define GL_BUFFER_MAP_OFFSET 0x9121 #define GL_BUFFER_MAP_POINTER 0x88BD #define GL_BUFFER_MAP_POINTER_ARB 0x88BD #define GL_BUFFER_SIZE 0x8764 #define GL_BUFFER_SIZE_ARB 0x8764 #define GL_BUFFER_STORAGE_FLAGS 0x8220 #define GL_BUFFER_UPDATE_BARRIER_BIT 0x00000200 #define GL_BUFFER_USAGE 0x8765 #define GL_BUFFER_USAGE_ARB 0x8765 #define GL_BUFFER_VARIABLE 0x92E5 #define GL_BYTE 0x1400 #define GL_C3F_V3F 0x2A24 #define GL_C4F_N3F_V3F 0x2A26 #define GL_C4UB_V2F 0x2A22 #define GL_C4UB_V3F 0x2A23 #define GL_CAVEAT_SUPPORT 0x82B8 #define GL_CCW 0x0901 #define GL_CLAMP 0x2900 #define GL_CLAMP_FRAGMENT_COLOR 0x891B #define GL_CLAMP_FRAGMENT_COLOR_ARB 0x891B #define GL_CLAMP_READ_COLOR 0x891C #define GL_CLAMP_READ_COLOR_ARB 0x891C #define GL_CLAMP_TO_BORDER 0x812D #define GL_CLAMP_TO_BORDER_ARB 0x812D #define GL_CLAMP_TO_EDGE 0x812F #define GL_CLAMP_VERTEX_COLOR 0x891A #define GL_CLAMP_VERTEX_COLOR_ARB 0x891A #define GL_CLEAR 0x1500 #define GL_CLEAR_BUFFER 0x82B4 #define GL_CLEAR_TEXTURE 0x9365 #define GL_CLIENT_ACTIVE_TEXTURE 0x84E1 #define GL_CLIENT_ACTIVE_TEXTURE_ARB 0x84E1 #define GL_CLIENT_ALL_ATTRIB_BITS 0xFFFFFFFF #define GL_CLIENT_ATTRIB_STACK_DEPTH 0x0BB1 #define GL_CLIENT_MAPPED_BUFFER_BARRIER_BIT 0x00004000 #define GL_CLIENT_PIXEL_STORE_BIT 0x00000001 #define GL_CLIENT_STORAGE_BIT 0x0200 #define GL_CLIENT_VERTEX_ARRAY_BIT 0x00000002 #define GL_CLIPPING_INPUT_PRIMITIVES 0x82F6 #define GL_CLIPPING_INPUT_PRIMITIVES_ARB 0x82F6 #define GL_CLIPPING_OUTPUT_PRIMITIVES 0x82F7 #define GL_CLIPPING_OUTPUT_PRIMITIVES_ARB 0x82F7 #define GL_CLIP_DEPTH_MODE 0x935D #define GL_CLIP_DISTANCE0 0x3000 #define GL_CLIP_DISTANCE1 0x3001 #define GL_CLIP_DISTANCE2 0x3002 #define GL_CLIP_DISTANCE3 0x3003 #define GL_CLIP_DISTANCE4 0x3004 #define GL_CLIP_DISTANCE5 0x3005 #define GL_CLIP_DISTANCE6 0x3006 #define GL_CLIP_DISTANCE7 0x3007 #define GL_CLIP_ORIGIN 0x935C #define GL_CLIP_PLANE0 0x3000 #define GL_CLIP_PLANE1 0x3001 #define GL_CLIP_PLANE2 0x3002 #define GL_CLIP_PLANE3 0x3003 #define GL_CLIP_PLANE4 0x3004 #define GL_CLIP_PLANE5 0x3005 #define GL_COEFF 0x0A00 #define GL_COLOR 0x1800 #define GL_COLORBURN_KHR 0x929A #define GL_COLORDODGE_KHR 0x9299 #define GL_COLOR_ARRAY 0x8076 #define GL_COLOR_ARRAY_BUFFER_BINDING 0x8898 #define GL_COLOR_ARRAY_BUFFER_BINDING_ARB 0x8898 #define GL_COLOR_ARRAY_POINTER 0x8090 #define GL_COLOR_ARRAY_SIZE 0x8081 #define GL_COLOR_ARRAY_STRIDE 0x8083 #define GL_COLOR_ARRAY_TYPE 0x8082 #define GL_COLOR_ATTACHMENT0 0x8CE0 #define GL_COLOR_ATTACHMENT1 0x8CE1 #define GL_COLOR_ATTACHMENT10 0x8CEA #define GL_COLOR_ATTACHMENT11 0x8CEB #define GL_COLOR_ATTACHMENT12 0x8CEC #define GL_COLOR_ATTACHMENT13 0x8CED #define GL_COLOR_ATTACHMENT14 0x8CEE #define GL_COLOR_ATTACHMENT15 0x8CEF #define GL_COLOR_ATTACHMENT16 0x8CF0 #define GL_COLOR_ATTACHMENT17 0x8CF1 #define GL_COLOR_ATTACHMENT18 0x8CF2 #define GL_COLOR_ATTACHMENT19 0x8CF3 #define GL_COLOR_ATTACHMENT2 0x8CE2 #define GL_COLOR_ATTACHMENT20 0x8CF4 #define GL_COLOR_ATTACHMENT21 0x8CF5 #define GL_COLOR_ATTACHMENT22 0x8CF6 #define GL_COLOR_ATTACHMENT23 0x8CF7 #define GL_COLOR_ATTACHMENT24 0x8CF8 #define GL_COLOR_ATTACHMENT25 0x8CF9 #define GL_COLOR_ATTACHMENT26 0x8CFA #define GL_COLOR_ATTACHMENT27 0x8CFB #define GL_COLOR_ATTACHMENT28 0x8CFC #define GL_COLOR_ATTACHMENT29 0x8CFD #define GL_COLOR_ATTACHMENT3 0x8CE3 #define GL_COLOR_ATTACHMENT30 0x8CFE #define GL_COLOR_ATTACHMENT31 0x8CFF #define GL_COLOR_ATTACHMENT4 0x8CE4 #define GL_COLOR_ATTACHMENT5 0x8CE5 #define GL_COLOR_ATTACHMENT6 0x8CE6 #define GL_COLOR_ATTACHMENT7 0x8CE7 #define GL_COLOR_ATTACHMENT8 0x8CE8 #define GL_COLOR_ATTACHMENT9 0x8CE9 #define GL_COLOR_BUFFER_BIT 0x00004000 #define GL_COLOR_CLEAR_VALUE 0x0C22 #define GL_COLOR_COMPONENTS 0x8283 #define GL_COLOR_ENCODING 0x8296 #define GL_COLOR_INDEX 0x1900 #define GL_COLOR_INDEXES 0x1603 #define GL_COLOR_LOGIC_OP 0x0BF2 #define GL_COLOR_MATERIAL 0x0B57 #define GL_COLOR_MATERIAL_FACE 0x0B55 #define GL_COLOR_MATERIAL_PARAMETER 0x0B56 #define GL_COLOR_MATRIX 0x80B1 #define GL_COLOR_MATRIX_STACK_DEPTH 0x80B2 #define GL_COLOR_RENDERABLE 0x8286 #define GL_COLOR_SUM 0x8458 #define GL_COLOR_SUM_ARB 0x8458 #define GL_COLOR_TABLE 0x80D0 #define GL_COLOR_TABLE_ALPHA_SIZE 0x80DD #define GL_COLOR_TABLE_BIAS 0x80D7 #define GL_COLOR_TABLE_BLUE_SIZE 0x80DC #define GL_COLOR_TABLE_FORMAT 0x80D8 #define GL_COLOR_TABLE_GREEN_SIZE 0x80DB #define GL_COLOR_TABLE_INTENSITY_SIZE 0x80DF #define GL_COLOR_TABLE_LUMINANCE_SIZE 0x80DE #define GL_COLOR_TABLE_RED_SIZE 0x80DA #define GL_COLOR_TABLE_SCALE 0x80D6 #define GL_COLOR_TABLE_WIDTH 0x80D9 #define GL_COLOR_WRITEMASK 0x0C23 #define GL_COMBINE 0x8570 #define GL_COMBINE_ALPHA 0x8572 #define GL_COMBINE_ALPHA_ARB 0x8572 #define GL_COMBINE_ARB 0x8570 #define GL_COMBINE_RGB 0x8571 #define GL_COMBINE_RGB_ARB 0x8571 #define GL_COMMAND_BARRIER_BIT 0x00000040 #define GL_COMPARE_REF_TO_TEXTURE 0x884E #define GL_COMPARE_R_TO_TEXTURE 0x884E #define GL_COMPARE_R_TO_TEXTURE_ARB 0x884E #define GL_COMPATIBLE_SUBROUTINES 0x8E4B #define GL_COMPILE 0x1300 #define GL_COMPILE_AND_EXECUTE 0x1301 #define GL_COMPILE_STATUS 0x8B81 #define GL_COMPLETION_STATUS_ARB 0x91B1 #define GL_COMPLETION_STATUS_KHR 0x91B1 #define GL_COMPRESSED_ALPHA 0x84E9 #define GL_COMPRESSED_ALPHA_ARB 0x84E9 #define GL_COMPRESSED_INTENSITY 0x84EC #define GL_COMPRESSED_INTENSITY_ARB 0x84EC #define GL_COMPRESSED_LUMINANCE 0x84EA #define GL_COMPRESSED_LUMINANCE_ALPHA 0x84EB #define GL_COMPRESSED_LUMINANCE_ALPHA_ARB 0x84EB #define GL_COMPRESSED_LUMINANCE_ARB 0x84EA #define GL_COMPRESSED_R11_EAC 0x9270 #define GL_COMPRESSED_RED 0x8225 #define GL_COMPRESSED_RED_RGTC1 0x8DBB #define GL_COMPRESSED_RG 0x8226 #define GL_COMPRESSED_RG11_EAC 0x9272 #define GL_COMPRESSED_RGB 0x84ED #define GL_COMPRESSED_RGB8_ETC2 0x9274 #define GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2 0x9276 #define GL_COMPRESSED_RGBA 0x84EE #define GL_COMPRESSED_RGBA8_ETC2_EAC 0x9278 #define GL_COMPRESSED_RGBA_ARB 0x84EE #define GL_COMPRESSED_RGBA_ASTC_10x10_KHR 0x93BB #define GL_COMPRESSED_RGBA_ASTC_10x5_KHR 0x93B8 #define GL_COMPRESSED_RGBA_ASTC_10x6_KHR 0x93B9 #define GL_COMPRESSED_RGBA_ASTC_10x8_KHR 0x93BA #define GL_COMPRESSED_RGBA_ASTC_12x10_KHR 0x93BC #define GL_COMPRESSED_RGBA_ASTC_12x12_KHR 0x93BD #define GL_COMPRESSED_RGBA_ASTC_4x4_KHR 0x93B0 #define GL_COMPRESSED_RGBA_ASTC_5x4_KHR 0x93B1 #define GL_COMPRESSED_RGBA_ASTC_5x5_KHR 0x93B2 #define GL_COMPRESSED_RGBA_ASTC_6x5_KHR 0x93B3 #define GL_COMPRESSED_RGBA_ASTC_6x6_KHR 0x93B4 #define GL_COMPRESSED_RGBA_ASTC_8x5_KHR 0x93B5 #define GL_COMPRESSED_RGBA_ASTC_8x6_KHR 0x93B6 #define GL_COMPRESSED_RGBA_ASTC_8x8_KHR 0x93B7 #define GL_COMPRESSED_RGBA_BPTC_UNORM_ARB 0x8E8C #define GL_COMPRESSED_RGB_ARB 0x84ED #define GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT_ARB 0x8E8E #define GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT_ARB 0x8E8F #define GL_COMPRESSED_RG_RGTC2 0x8DBD #define GL_COMPRESSED_SIGNED_R11_EAC 0x9271 #define GL_COMPRESSED_SIGNED_RED_RGTC1 0x8DBC #define GL_COMPRESSED_SIGNED_RG11_EAC 0x9273 #define GL_COMPRESSED_SIGNED_RG_RGTC2 0x8DBE #define GL_COMPRESSED_SLUMINANCE 0x8C4A #define GL_COMPRESSED_SLUMINANCE_ALPHA 0x8C4B #define GL_COMPRESSED_SRGB 0x8C48 #define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR 0x93DB #define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR 0x93D8 #define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR 0x93D9 #define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR 0x93DA #define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR 0x93DC #define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR 0x93DD #define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR 0x93D0 #define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR 0x93D1 #define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR 0x93D2 #define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR 0x93D3 #define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR 0x93D4 #define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR 0x93D5 #define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR 0x93D6 #define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR 0x93D7 #define GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC 0x9279 #define GL_COMPRESSED_SRGB8_ETC2 0x9275 #define GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2 0x9277 #define GL_COMPRESSED_SRGB_ALPHA 0x8C49 #define GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM_ARB 0x8E8D #define GL_COMPRESSED_TEXTURE_FORMATS 0x86A3 #define GL_COMPRESSED_TEXTURE_FORMATS_ARB 0x86A3 #define GL_COMPUTE_SHADER 0x91B9 #define GL_COMPUTE_SHADER_BIT 0x00000020 #define GL_COMPUTE_SHADER_INVOCATIONS 0x82F5 #define GL_COMPUTE_SHADER_INVOCATIONS_ARB 0x82F5 #define GL_COMPUTE_SUBROUTINE 0x92ED #define GL_COMPUTE_SUBROUTINE_UNIFORM 0x92F3 #define GL_COMPUTE_TEXTURE 0x82A0 #define GL_COMPUTE_WORK_GROUP_SIZE 0x8267 #define GL_CONDITION_SATISFIED 0x911C #define GL_CONSTANT 0x8576 #define GL_CONSTANT_ALPHA 0x8003 #define GL_CONSTANT_ARB 0x8576 #define GL_CONSTANT_ATTENUATION 0x1207 #define GL_CONSTANT_BORDER 0x8151 #define GL_CONSTANT_COLOR 0x8001 #define GL_CONTEXT_COMPATIBILITY_PROFILE_BIT 0x00000002 #define GL_CONTEXT_CORE_PROFILE_BIT 0x00000001 #define GL_CONTEXT_FLAGS 0x821E #define GL_CONTEXT_FLAG_DEBUG_BIT 0x00000002 #define GL_CONTEXT_FLAG_FORWARD_COMPATIBLE_BIT 0x00000001 #define GL_CONTEXT_FLAG_NO_ERROR_BIT 0x00000008 #define GL_CONTEXT_FLAG_NO_ERROR_BIT_KHR 0x00000008 #define GL_CONTEXT_FLAG_ROBUST_ACCESS_BIT_ARB 0x00000004 #define GL_CONTEXT_LOST 0x0507 #define GL_CONTEXT_PROFILE_MASK 0x9126 #define GL_CONTEXT_RELEASE_BEHAVIOR 0x82FB #define GL_CONTEXT_RELEASE_BEHAVIOR_FLUSH 0x82FC #define GL_CONTEXT_ROBUST_ACCESS 0x90F3 #define GL_CONVOLUTION_1D 0x8010 #define GL_CONVOLUTION_2D 0x8011 #define GL_CONVOLUTION_BORDER_COLOR 0x8154 #define GL_CONVOLUTION_BORDER_MODE 0x8013 #define GL_CONVOLUTION_FILTER_BIAS 0x8015 #define GL_CONVOLUTION_FILTER_SCALE 0x8014 #define GL_CONVOLUTION_FORMAT 0x8017 #define GL_CONVOLUTION_HEIGHT 0x8019 #define GL_CONVOLUTION_WIDTH 0x8018 #define GL_COORD_REPLACE 0x8862 #define GL_COORD_REPLACE_ARB 0x8862 #define GL_COPY 0x1503 #define GL_COPY_INVERTED 0x150C #define GL_COPY_PIXEL_TOKEN 0x0706 #define GL_COPY_READ_BUFFER 0x8F36 #define GL_COPY_WRITE_BUFFER 0x8F37 #define GL_CULL_FACE 0x0B44 #define GL_CULL_FACE_MODE 0x0B45 #define GL_CURRENT_BIT 0x00000001 #define GL_CURRENT_COLOR 0x0B00 #define GL_CURRENT_FOG_COORD 0x8453 #define GL_CURRENT_FOG_COORDINATE 0x8453 #define GL_CURRENT_INDEX 0x0B01 #define GL_CURRENT_MATRIX_ARB 0x8641 #define GL_CURRENT_MATRIX_INDEX_ARB 0x8845 #define GL_CURRENT_MATRIX_STACK_DEPTH_ARB 0x8640 #define GL_CURRENT_NORMAL 0x0B02 #define GL_CURRENT_PALETTE_MATRIX_ARB 0x8843 #define GL_CURRENT_PROGRAM 0x8B8D #define GL_CURRENT_QUERY 0x8865 #define GL_CURRENT_QUERY_ARB 0x8865 #define GL_CURRENT_RASTER_COLOR 0x0B04 #define GL_CURRENT_RASTER_DISTANCE 0x0B09 #define GL_CURRENT_RASTER_INDEX 0x0B05 #define GL_CURRENT_RASTER_POSITION 0x0B07 #define GL_CURRENT_RASTER_POSITION_VALID 0x0B08 #define GL_CURRENT_RASTER_SECONDARY_COLOR 0x845F #define GL_CURRENT_RASTER_TEXTURE_COORDS 0x0B06 #define GL_CURRENT_SECONDARY_COLOR 0x8459 #define GL_CURRENT_TEXTURE_COORDS 0x0B03 #define GL_CURRENT_VERTEX_ATTRIB 0x8626 #define GL_CURRENT_VERTEX_ATTRIB_ARB 0x8626 #define GL_CURRENT_WEIGHT_ARB 0x86A8 #define GL_CW 0x0900 #define GL_DARKEN_KHR 0x9297 #define GL_DEBUG_CALLBACK_FUNCTION 0x8244 #define GL_DEBUG_CALLBACK_FUNCTION_ARB 0x8244 #define GL_DEBUG_CALLBACK_USER_PARAM 0x8245 #define GL_DEBUG_CALLBACK_USER_PARAM_ARB 0x8245 #define GL_DEBUG_GROUP_STACK_DEPTH 0x826D #define GL_DEBUG_LOGGED_MESSAGES 0x9145 #define GL_DEBUG_LOGGED_MESSAGES_ARB 0x9145 #define GL_DEBUG_NEXT_LOGGED_MESSAGE_LENGTH 0x8243 #define GL_DEBUG_NEXT_LOGGED_MESSAGE_LENGTH_ARB 0x8243 #define GL_DEBUG_OUTPUT 0x92E0 #define GL_DEBUG_OUTPUT_SYNCHRONOUS 0x8242 #define GL_DEBUG_OUTPUT_SYNCHRONOUS_ARB 0x8242 #define GL_DEBUG_SEVERITY_HIGH 0x9146 #define GL_DEBUG_SEVERITY_HIGH_ARB 0x9146 #define GL_DEBUG_SEVERITY_LOW 0x9148 #define GL_DEBUG_SEVERITY_LOW_ARB 0x9148 #define GL_DEBUG_SEVERITY_MEDIUM 0x9147 #define GL_DEBUG_SEVERITY_MEDIUM_ARB 0x9147 #define GL_DEBUG_SEVERITY_NOTIFICATION 0x826B #define GL_DEBUG_SOURCE_API 0x8246 #define GL_DEBUG_SOURCE_API_ARB 0x8246 #define GL_DEBUG_SOURCE_APPLICATION 0x824A #define GL_DEBUG_SOURCE_APPLICATION_ARB 0x824A #define GL_DEBUG_SOURCE_OTHER 0x824B #define GL_DEBUG_SOURCE_OTHER_ARB 0x824B #define GL_DEBUG_SOURCE_SHADER_COMPILER 0x8248 #define GL_DEBUG_SOURCE_SHADER_COMPILER_ARB 0x8248 #define GL_DEBUG_SOURCE_THIRD_PARTY 0x8249 #define GL_DEBUG_SOURCE_THIRD_PARTY_ARB 0x8249 #define GL_DEBUG_SOURCE_WINDOW_SYSTEM 0x8247 #define GL_DEBUG_SOURCE_WINDOW_SYSTEM_ARB 0x8247 #define GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR 0x824D #define GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR_ARB 0x824D #define GL_DEBUG_TYPE_ERROR 0x824C #define GL_DEBUG_TYPE_ERROR_ARB 0x824C #define GL_DEBUG_TYPE_MARKER 0x8268 #define GL_DEBUG_TYPE_OTHER 0x8251 #define GL_DEBUG_TYPE_OTHER_ARB 0x8251 #define GL_DEBUG_TYPE_PERFORMANCE 0x8250 #define GL_DEBUG_TYPE_PERFORMANCE_ARB 0x8250 #define GL_DEBUG_TYPE_POP_GROUP 0x826A #define GL_DEBUG_TYPE_PORTABILITY 0x824F #define GL_DEBUG_TYPE_PORTABILITY_ARB 0x824F #define GL_DEBUG_TYPE_PUSH_GROUP 0x8269 #define GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR 0x824E #define GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR_ARB 0x824E #define GL_DECAL 0x2101 #define GL_DECR 0x1E03 #define GL_DECR_WRAP 0x8508 #define GL_DELETE_STATUS 0x8B80 #define GL_DEPTH 0x1801 #define GL_DEPTH24_STENCIL8 0x88F0 #define GL_DEPTH32F_STENCIL8 0x8CAD #define GL_DEPTH_ATTACHMENT 0x8D00 #define GL_DEPTH_BIAS 0x0D1F #define GL_DEPTH_BITS 0x0D56 #define GL_DEPTH_BUFFER_BIT 0x00000100 #define GL_DEPTH_CLAMP 0x864F #define GL_DEPTH_CLEAR_VALUE 0x0B73 #define GL_DEPTH_COMPONENT 0x1902 #define GL_DEPTH_COMPONENT16 0x81A5 #define GL_DEPTH_COMPONENT16_ARB 0x81A5 #define GL_DEPTH_COMPONENT24 0x81A6 #define GL_DEPTH_COMPONENT24_ARB 0x81A6 #define GL_DEPTH_COMPONENT32 0x81A7 #define GL_DEPTH_COMPONENT32F 0x8CAC #define GL_DEPTH_COMPONENT32_ARB 0x81A7 #define GL_DEPTH_COMPONENTS 0x8284 #define GL_DEPTH_FUNC 0x0B74 #define GL_DEPTH_RANGE 0x0B70 #define GL_DEPTH_RENDERABLE 0x8287 #define GL_DEPTH_SCALE 0x0D1E #define GL_DEPTH_STENCIL 0x84F9 #define GL_DEPTH_STENCIL_ATTACHMENT 0x821A #define GL_DEPTH_STENCIL_TEXTURE_MODE 0x90EA #define GL_DEPTH_TEST 0x0B71 #define GL_DEPTH_TEXTURE_MODE 0x884B #define GL_DEPTH_TEXTURE_MODE_ARB 0x884B #define GL_DEPTH_WRITEMASK 0x0B72 #define GL_DIFFERENCE_KHR 0x929E #define GL_DIFFUSE 0x1201 #define GL_DISPATCH_INDIRECT_BUFFER 0x90EE #define GL_DISPATCH_INDIRECT_BUFFER_BINDING 0x90EF #define GL_DISPLAY_LIST 0x82E7 #define GL_DITHER 0x0BD0 #define GL_DOMAIN 0x0A02 #define GL_DONT_CARE 0x1100 #define GL_DOT3_RGB 0x86AE #define GL_DOT3_RGBA 0x86AF #define GL_DOT3_RGBA_ARB 0x86AF #define GL_DOT3_RGB_ARB 0x86AE #define GL_DOUBLE 0x140A #define GL_DOUBLEBUFFER 0x0C32 #define GL_DOUBLE_MAT2 0x8F46 #define GL_DOUBLE_MAT2x3 0x8F49 #define GL_DOUBLE_MAT2x4 0x8F4A #define GL_DOUBLE_MAT3 0x8F47 #define GL_DOUBLE_MAT3x2 0x8F4B #define GL_DOUBLE_MAT3x4 0x8F4C #define GL_DOUBLE_MAT4 0x8F48 #define GL_DOUBLE_MAT4x2 0x8F4D #define GL_DOUBLE_MAT4x3 0x8F4E #define GL_DOUBLE_VEC2 0x8FFC #define GL_DOUBLE_VEC3 0x8FFD #define GL_DOUBLE_VEC4 0x8FFE #define GL_DRAW_BUFFER 0x0C01 #define GL_DRAW_BUFFER0 0x8825 #define GL_DRAW_BUFFER0_ARB 0x8825 #define GL_DRAW_BUFFER1 0x8826 #define GL_DRAW_BUFFER10 0x882F #define GL_DRAW_BUFFER10_ARB 0x882F #define GL_DRAW_BUFFER11 0x8830 #define GL_DRAW_BUFFER11_ARB 0x8830 #define GL_DRAW_BUFFER12 0x8831 #define GL_DRAW_BUFFER12_ARB 0x8831 #define GL_DRAW_BUFFER13 0x8832 #define GL_DRAW_BUFFER13_ARB 0x8832 #define GL_DRAW_BUFFER14 0x8833 #define GL_DRAW_BUFFER14_ARB 0x8833 #define GL_DRAW_BUFFER15 0x8834 #define GL_DRAW_BUFFER15_ARB 0x8834 #define GL_DRAW_BUFFER1_ARB 0x8826 #define GL_DRAW_BUFFER2 0x8827 #define GL_DRAW_BUFFER2_ARB 0x8827 #define GL_DRAW_BUFFER3 0x8828 #define GL_DRAW_BUFFER3_ARB 0x8828 #define GL_DRAW_BUFFER4 0x8829 #define GL_DRAW_BUFFER4_ARB 0x8829 #define GL_DRAW_BUFFER5 0x882A #define GL_DRAW_BUFFER5_ARB 0x882A #define GL_DRAW_BUFFER6 0x882B #define GL_DRAW_BUFFER6_ARB 0x882B #define GL_DRAW_BUFFER7 0x882C #define GL_DRAW_BUFFER7_ARB 0x882C #define GL_DRAW_BUFFER8 0x882D #define GL_DRAW_BUFFER8_ARB 0x882D #define GL_DRAW_BUFFER9 0x882E #define GL_DRAW_BUFFER9_ARB 0x882E #define GL_DRAW_FRAMEBUFFER 0x8CA9 #define GL_DRAW_FRAMEBUFFER_BINDING 0x8CA6 #define GL_DRAW_INDIRECT_BUFFER 0x8F3F #define GL_DRAW_INDIRECT_BUFFER_BINDING 0x8F43 #define GL_DRAW_PIXEL_TOKEN 0x0705 #define GL_DST_ALPHA 0x0304 #define GL_DST_COLOR 0x0306 #define GL_DYNAMIC_COPY 0x88EA #define GL_DYNAMIC_COPY_ARB 0x88EA #define GL_DYNAMIC_DRAW 0x88E8 #define GL_DYNAMIC_DRAW_ARB 0x88E8 #define GL_DYNAMIC_READ 0x88E9 #define GL_DYNAMIC_READ_ARB 0x88E9 #define GL_DYNAMIC_STORAGE_BIT 0x0100 #define GL_EDGE_FLAG 0x0B43 #define GL_EDGE_FLAG_ARRAY 0x8079 #define GL_EDGE_FLAG_ARRAY_BUFFER_BINDING 0x889B #define GL_EDGE_FLAG_ARRAY_BUFFER_BINDING_ARB 0x889B #define GL_EDGE_FLAG_ARRAY_POINTER 0x8093 #define GL_EDGE_FLAG_ARRAY_STRIDE 0x808C #define GL_ELEMENT_ARRAY_BARRIER_BIT 0x00000002 #define GL_ELEMENT_ARRAY_BUFFER 0x8893 #define GL_ELEMENT_ARRAY_BUFFER_ARB 0x8893 #define GL_ELEMENT_ARRAY_BUFFER_BINDING 0x8895 #define GL_ELEMENT_ARRAY_BUFFER_BINDING_ARB 0x8895 #define GL_EMISSION 0x1600 #define GL_ENABLE_BIT 0x00002000 #define GL_EQUAL 0x0202 #define GL_EQUIV 0x1509 #define GL_EVAL_BIT 0x00010000 #define GL_EXCLUSION_KHR 0x92A0 #define GL_EXP 0x0800 #define GL_EXP2 0x0801 #define GL_EXTENSIONS 0x1F03 #define GL_EYE_LINEAR 0x2400 #define GL_EYE_PLANE 0x2502 #define GL_FALSE 0 #define GL_FASTEST 0x1101 #define GL_FEEDBACK 0x1C01 #define GL_FEEDBACK_BUFFER_POINTER 0x0DF0 #define GL_FEEDBACK_BUFFER_SIZE 0x0DF1 #define GL_FEEDBACK_BUFFER_TYPE 0x0DF2 #define GL_FILL 0x1B02 #define GL_FILTER 0x829A #define GL_FIRST_VERTEX_CONVENTION 0x8E4D #define GL_FIXED 0x140C #define GL_FIXED_ONLY 0x891D #define GL_FIXED_ONLY_ARB 0x891D #define GL_FLAT 0x1D00 #define GL_FLOAT 0x1406 #define GL_FLOAT_32_UNSIGNED_INT_24_8_REV 0x8DAD #define GL_FLOAT_MAT2 0x8B5A #define GL_FLOAT_MAT2_ARB 0x8B5A #define GL_FLOAT_MAT2x3 0x8B65 #define GL_FLOAT_MAT2x4 0x8B66 #define GL_FLOAT_MAT3 0x8B5B #define GL_FLOAT_MAT3_ARB 0x8B5B #define GL_FLOAT_MAT3x2 0x8B67 #define GL_FLOAT_MAT3x4 0x8B68 #define GL_FLOAT_MAT4 0x8B5C #define GL_FLOAT_MAT4_ARB 0x8B5C #define GL_FLOAT_MAT4x2 0x8B69 #define GL_FLOAT_MAT4x3 0x8B6A #define GL_FLOAT_VEC2 0x8B50 #define GL_FLOAT_VEC2_ARB 0x8B50 #define GL_FLOAT_VEC3 0x8B51 #define GL_FLOAT_VEC3_ARB 0x8B51 #define GL_FLOAT_VEC4 0x8B52 #define GL_FLOAT_VEC4_ARB 0x8B52 #define GL_FOG 0x0B60 #define GL_FOG_BIT 0x00000080 #define GL_FOG_COLOR 0x0B66 #define GL_FOG_COORD 0x8451 #define GL_FOG_COORDINATE 0x8451 #define GL_FOG_COORDINATE_ARRAY 0x8457 #define GL_FOG_COORDINATE_ARRAY_BUFFER_BINDING 0x889D #define GL_FOG_COORDINATE_ARRAY_BUFFER_BINDING_ARB 0x889D #define GL_FOG_COORDINATE_ARRAY_POINTER 0x8456 #define GL_FOG_COORDINATE_ARRAY_STRIDE 0x8455 #define GL_FOG_COORDINATE_ARRAY_TYPE 0x8454 #define GL_FOG_COORDINATE_SOURCE 0x8450 #define GL_FOG_COORD_ARRAY 0x8457 #define GL_FOG_COORD_ARRAY_BUFFER_BINDING 0x889D #define GL_FOG_COORD_ARRAY_POINTER 0x8456 #define GL_FOG_COORD_ARRAY_STRIDE 0x8455 #define GL_FOG_COORD_ARRAY_TYPE 0x8454 #define GL_FOG_COORD_SRC 0x8450 #define GL_FOG_DENSITY 0x0B62 #define GL_FOG_END 0x0B64 #define GL_FOG_HINT 0x0C54 #define GL_FOG_INDEX 0x0B61 #define GL_FOG_MODE 0x0B65 #define GL_FOG_START 0x0B63 #define GL_FRACTIONAL_EVEN 0x8E7C #define GL_FRACTIONAL_ODD 0x8E7B #define GL_FRAGMENT_DEPTH 0x8452 #define GL_FRAGMENT_INTERPOLATION_OFFSET_BITS 0x8E5D #define GL_FRAGMENT_PROGRAM_ARB 0x8804 #define GL_FRAGMENT_SHADER 0x8B30 #define GL_FRAGMENT_SHADER_ARB 0x8B30 #define GL_FRAGMENT_SHADER_BIT 0x00000002 #define GL_FRAGMENT_SHADER_DERIVATIVE_HINT 0x8B8B #define GL_FRAGMENT_SHADER_DERIVATIVE_HINT_ARB 0x8B8B #define GL_FRAGMENT_SHADER_INVOCATIONS 0x82F4 #define GL_FRAGMENT_SHADER_INVOCATIONS_ARB 0x82F4 #define GL_FRAGMENT_SUBROUTINE 0x92EC #define GL_FRAGMENT_SUBROUTINE_UNIFORM 0x92F2 #define GL_FRAGMENT_TEXTURE 0x829F #define GL_FRAMEBUFFER 0x8D40 #define GL_FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE 0x8215 #define GL_FRAMEBUFFER_ATTACHMENT_BLUE_SIZE 0x8214 #define GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING 0x8210 #define GL_FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE 0x8211 #define GL_FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE 0x8216 #define GL_FRAMEBUFFER_ATTACHMENT_GREEN_SIZE 0x8213 #define GL_FRAMEBUFFER_ATTACHMENT_LAYERED 0x8DA7 #define GL_FRAMEBUFFER_ATTACHMENT_LAYERED_ARB 0x8DA7 #define GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME 0x8CD1 #define GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE 0x8CD0 #define GL_FRAMEBUFFER_ATTACHMENT_RED_SIZE 0x8212 #define GL_FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE 0x8217 #define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE 0x8CD3 #define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LAYER 0x8CD4 #define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL 0x8CD2 #define GL_FRAMEBUFFER_BARRIER_BIT 0x00000400 #define GL_FRAMEBUFFER_BINDING 0x8CA6 #define GL_FRAMEBUFFER_BLEND 0x828B #define GL_FRAMEBUFFER_COMPLETE 0x8CD5 #define GL_FRAMEBUFFER_DEFAULT 0x8218 #define GL_FRAMEBUFFER_DEFAULT_FIXED_SAMPLE_LOCATIONS 0x9314 #define GL_FRAMEBUFFER_DEFAULT_HEIGHT 0x9311 #define GL_FRAMEBUFFER_DEFAULT_LAYERS 0x9312 #define GL_FRAMEBUFFER_DEFAULT_SAMPLES 0x9313 #define GL_FRAMEBUFFER_DEFAULT_WIDTH 0x9310 #define GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT 0x8CD6 #define GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER 0x8CDB #define GL_FRAMEBUFFER_INCOMPLETE_LAYER_COUNT_ARB 0x8DA9 #define GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS 0x8DA8 #define GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS_ARB 0x8DA8 #define GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT 0x8CD7 #define GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE 0x8D56 #define GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER 0x8CDC #define GL_FRAMEBUFFER_PROGRAMMABLE_SAMPLE_LOCATIONS_ARB 0x9342 #define GL_FRAMEBUFFER_RENDERABLE 0x8289 #define GL_FRAMEBUFFER_RENDERABLE_LAYERED 0x828A #define GL_FRAMEBUFFER_SAMPLE_LOCATION_PIXEL_GRID_ARB 0x9343 #define GL_FRAMEBUFFER_SRGB 0x8DB9 #define GL_FRAMEBUFFER_UNDEFINED 0x8219 #define GL_FRAMEBUFFER_UNSUPPORTED 0x8CDD #define GL_FRONT 0x0404 #define GL_FRONT_AND_BACK 0x0408 #define GL_FRONT_FACE 0x0B46 #define GL_FRONT_LEFT 0x0400 #define GL_FRONT_RIGHT 0x0401 #define GL_FULL_SUPPORT 0x82B7 #define GL_FUNC_ADD 0x8006 #define GL_FUNC_REVERSE_SUBTRACT 0x800B #define GL_FUNC_SUBTRACT 0x800A #define GL_GENERATE_MIPMAP 0x8191 #define GL_GENERATE_MIPMAP_HINT 0x8192 #define GL_GEOMETRY_INPUT_TYPE 0x8917 #define GL_GEOMETRY_INPUT_TYPE_ARB 0x8DDB #define GL_GEOMETRY_OUTPUT_TYPE 0x8918 #define GL_GEOMETRY_OUTPUT_TYPE_ARB 0x8DDC #define GL_GEOMETRY_SHADER 0x8DD9 #define GL_GEOMETRY_SHADER_ARB 0x8DD9 #define GL_GEOMETRY_SHADER_BIT 0x00000004 #define GL_GEOMETRY_SHADER_INVOCATIONS 0x887F #define GL_GEOMETRY_SHADER_PRIMITIVES_EMITTED 0x82F3 #define GL_GEOMETRY_SHADER_PRIMITIVES_EMITTED_ARB 0x82F3 #define GL_GEOMETRY_SUBROUTINE 0x92EB #define GL_GEOMETRY_SUBROUTINE_UNIFORM 0x92F1 #define GL_GEOMETRY_TEXTURE 0x829E #define GL_GEOMETRY_VERTICES_OUT 0x8916 #define GL_GEOMETRY_VERTICES_OUT_ARB 0x8DDA #define GL_GEQUAL 0x0206 #define GL_GET_TEXTURE_IMAGE_FORMAT 0x8291 #define GL_GET_TEXTURE_IMAGE_TYPE 0x8292 #define GL_GREATER 0x0204 #define GL_GREEN 0x1904 #define GL_GREEN_BIAS 0x0D19 #define GL_GREEN_BITS 0x0D53 #define GL_GREEN_INTEGER 0x8D95 #define GL_GREEN_SCALE 0x0D18 #define GL_GUILTY_CONTEXT_RESET 0x8253 #define GL_GUILTY_CONTEXT_RESET_ARB 0x8253 #define GL_HALF_FLOAT 0x140B #define GL_HALF_FLOAT_ARB 0x140B #define GL_HARDLIGHT_KHR 0x929B #define GL_HIGH_FLOAT 0x8DF2 #define GL_HIGH_INT 0x8DF5 #define GL_HINT_BIT 0x00008000 #define GL_HISTOGRAM 0x8024 #define GL_HISTOGRAM_ALPHA_SIZE 0x802B #define GL_HISTOGRAM_BLUE_SIZE 0x802A #define GL_HISTOGRAM_FORMAT 0x8027 #define GL_HISTOGRAM_GREEN_SIZE 0x8029 #define GL_HISTOGRAM_LUMINANCE_SIZE 0x802C #define GL_HISTOGRAM_RED_SIZE 0x8028 #define GL_HISTOGRAM_SINK 0x802D #define GL_HISTOGRAM_WIDTH 0x8026 #define GL_HSL_COLOR_KHR 0x92AF #define GL_HSL_HUE_KHR 0x92AD #define GL_HSL_LUMINOSITY_KHR 0x92B0 #define GL_HSL_SATURATION_KHR 0x92AE #define GL_IMAGE_1D 0x904C #define GL_IMAGE_1D_ARRAY 0x9052 #define GL_IMAGE_2D 0x904D #define GL_IMAGE_2D_ARRAY 0x9053 #define GL_IMAGE_2D_MULTISAMPLE 0x9055 #define GL_IMAGE_2D_MULTISAMPLE_ARRAY 0x9056 #define GL_IMAGE_2D_RECT 0x904F #define GL_IMAGE_3D 0x904E #define GL_IMAGE_BINDING_ACCESS 0x8F3E #define GL_IMAGE_BINDING_FORMAT 0x906E #define GL_IMAGE_BINDING_LAYER 0x8F3D #define GL_IMAGE_BINDING_LAYERED 0x8F3C #define GL_IMAGE_BINDING_LEVEL 0x8F3B #define GL_IMAGE_BINDING_NAME 0x8F3A #define GL_IMAGE_BUFFER 0x9051 #define GL_IMAGE_CLASS_10_10_10_2 0x82C3 #define GL_IMAGE_CLASS_11_11_10 0x82C2 #define GL_IMAGE_CLASS_1_X_16 0x82BE #define GL_IMAGE_CLASS_1_X_32 0x82BB #define GL_IMAGE_CLASS_1_X_8 0x82C1 #define GL_IMAGE_CLASS_2_X_16 0x82BD #define GL_IMAGE_CLASS_2_X_32 0x82BA #define GL_IMAGE_CLASS_2_X_8 0x82C0 #define GL_IMAGE_CLASS_4_X_16 0x82BC #define GL_IMAGE_CLASS_4_X_32 0x82B9 #define GL_IMAGE_CLASS_4_X_8 0x82BF #define GL_IMAGE_COMPATIBILITY_CLASS 0x82A8 #define GL_IMAGE_CUBE 0x9050 #define GL_IMAGE_CUBE_MAP_ARRAY 0x9054 #define GL_IMAGE_FORMAT_COMPATIBILITY_BY_CLASS 0x90C9 #define GL_IMAGE_FORMAT_COMPATIBILITY_BY_SIZE 0x90C8 #define GL_IMAGE_FORMAT_COMPATIBILITY_TYPE 0x90C7 #define GL_IMAGE_PIXEL_FORMAT 0x82A9 #define GL_IMAGE_PIXEL_TYPE 0x82AA #define GL_IMAGE_TEXEL_SIZE 0x82A7 #define GL_IMPLEMENTATION_COLOR_READ_FORMAT 0x8B9B #define GL_IMPLEMENTATION_COLOR_READ_TYPE 0x8B9A #define GL_INCR 0x1E02 #define GL_INCR_WRAP 0x8507 #define GL_INDEX 0x8222 #define GL_INDEX_ARRAY 0x8077 #define GL_INDEX_ARRAY_BUFFER_BINDING 0x8899 #define GL_INDEX_ARRAY_BUFFER_BINDING_ARB 0x8899 #define GL_INDEX_ARRAY_POINTER 0x8091 #define GL_INDEX_ARRAY_STRIDE 0x8086 #define GL_INDEX_ARRAY_TYPE 0x8085 #define GL_INDEX_BITS 0x0D51 #define GL_INDEX_CLEAR_VALUE 0x0C20 #define GL_INDEX_LOGIC_OP 0x0BF1 #define GL_INDEX_MODE 0x0C30 #define GL_INDEX_OFFSET 0x0D13 #define GL_INDEX_SHIFT 0x0D12 #define GL_INDEX_WRITEMASK 0x0C21 #define GL_INFO_LOG_LENGTH 0x8B84 #define GL_INNOCENT_CONTEXT_RESET 0x8254 #define GL_INNOCENT_CONTEXT_RESET_ARB 0x8254 #define GL_INT 0x1404 #define GL_INT64_ARB 0x140E #define GL_INT64_VEC2_ARB 0x8FE9 #define GL_INT64_VEC3_ARB 0x8FEA #define GL_INT64_VEC4_ARB 0x8FEB #define GL_INTENSITY 0x8049 #define GL_INTENSITY12 0x804C #define GL_INTENSITY16 0x804D #define GL_INTENSITY16F_ARB 0x881D #define GL_INTENSITY32F_ARB 0x8817 #define GL_INTENSITY4 0x804A #define GL_INTENSITY8 0x804B #define GL_INTERLEAVED_ATTRIBS 0x8C8C #define GL_INTERNALFORMAT_ALPHA_SIZE 0x8274 #define GL_INTERNALFORMAT_ALPHA_TYPE 0x827B #define GL_INTERNALFORMAT_BLUE_SIZE 0x8273 #define GL_INTERNALFORMAT_BLUE_TYPE 0x827A #define GL_INTERNALFORMAT_DEPTH_SIZE 0x8275 #define GL_INTERNALFORMAT_DEPTH_TYPE 0x827C #define GL_INTERNALFORMAT_GREEN_SIZE 0x8272 #define GL_INTERNALFORMAT_GREEN_TYPE 0x8279 #define GL_INTERNALFORMAT_PREFERRED 0x8270 #define GL_INTERNALFORMAT_RED_SIZE 0x8271 #define GL_INTERNALFORMAT_RED_TYPE 0x8278 #define GL_INTERNALFORMAT_SHARED_SIZE 0x8277 #define GL_INTERNALFORMAT_STENCIL_SIZE 0x8276 #define GL_INTERNALFORMAT_STENCIL_TYPE 0x827D #define GL_INTERNALFORMAT_SUPPORTED 0x826F #define GL_INTERPOLATE 0x8575 #define GL_INTERPOLATE_ARB 0x8575 #define GL_INT_2_10_10_10_REV 0x8D9F #define GL_INT_IMAGE_1D 0x9057 #define GL_INT_IMAGE_1D_ARRAY 0x905D #define GL_INT_IMAGE_2D 0x9058 #define GL_INT_IMAGE_2D_ARRAY 0x905E #define GL_INT_IMAGE_2D_MULTISAMPLE 0x9060 #define GL_INT_IMAGE_2D_MULTISAMPLE_ARRAY 0x9061 #define GL_INT_IMAGE_2D_RECT 0x905A #define GL_INT_IMAGE_3D 0x9059 #define GL_INT_IMAGE_BUFFER 0x905C #define GL_INT_IMAGE_CUBE 0x905B #define GL_INT_IMAGE_CUBE_MAP_ARRAY 0x905F #define GL_INT_SAMPLER_1D 0x8DC9 #define GL_INT_SAMPLER_1D_ARRAY 0x8DCE #define GL_INT_SAMPLER_2D 0x8DCA #define GL_INT_SAMPLER_2D_ARRAY 0x8DCF #define GL_INT_SAMPLER_2D_MULTISAMPLE 0x9109 #define GL_INT_SAMPLER_2D_MULTISAMPLE_ARRAY 0x910C #define GL_INT_SAMPLER_2D_RECT 0x8DCD #define GL_INT_SAMPLER_3D 0x8DCB #define GL_INT_SAMPLER_BUFFER 0x8DD0 #define GL_INT_SAMPLER_CUBE 0x8DCC #define GL_INT_SAMPLER_CUBE_MAP_ARRAY_ARB 0x900E #define GL_INT_VEC2 0x8B53 #define GL_INT_VEC2_ARB 0x8B53 #define GL_INT_VEC3 0x8B54 #define GL_INT_VEC3_ARB 0x8B54 #define GL_INT_VEC4 0x8B55 #define GL_INT_VEC4_ARB 0x8B55 #define GL_INVALID_ENUM 0x0500 #define GL_INVALID_FRAMEBUFFER_OPERATION 0x0506 #define GL_INVALID_INDEX 0xFFFFFFFF #define GL_INVALID_OPERATION 0x0502 #define GL_INVALID_VALUE 0x0501 #define GL_INVERT 0x150A #define GL_ISOLINES 0x8E7A #define GL_IS_PER_PATCH 0x92E7 #define GL_IS_ROW_MAJOR 0x9300 #define GL_KEEP 0x1E00 #define GL_LAST_VERTEX_CONVENTION 0x8E4E #define GL_LAYER_PROVOKING_VERTEX 0x825E #define GL_LEFT 0x0406 #define GL_LEQUAL 0x0203 #define GL_LESS 0x0201 #define GL_LIGHT0 0x4000 #define GL_LIGHT1 0x4001 #define GL_LIGHT2 0x4002 #define GL_LIGHT3 0x4003 #define GL_LIGHT4 0x4004 #define GL_LIGHT5 0x4005 #define GL_LIGHT6 0x4006 #define GL_LIGHT7 0x4007 #define GL_LIGHTEN_KHR 0x9298 #define GL_LIGHTING 0x0B50 #define GL_LIGHTING_BIT 0x00000040 #define GL_LIGHT_MODEL_AMBIENT 0x0B53 #define GL_LIGHT_MODEL_COLOR_CONTROL 0x81F8 #define GL_LIGHT_MODEL_LOCAL_VIEWER 0x0B51 #define GL_LIGHT_MODEL_TWO_SIDE 0x0B52 #define GL_LINE 0x1B01 #define GL_LINEAR 0x2601 #define GL_LINEAR_ATTENUATION 0x1208 #define GL_LINEAR_MIPMAP_LINEAR 0x2703 #define GL_LINEAR_MIPMAP_NEAREST 0x2701 #define GL_LINES 0x0001 #define GL_LINES_ADJACENCY 0x000A #define GL_LINES_ADJACENCY_ARB 0x000A #define GL_LINE_BIT 0x00000004 #define GL_LINE_LOOP 0x0002 #define GL_LINE_RESET_TOKEN 0x0707 #define GL_LINE_SMOOTH 0x0B20 #define GL_LINE_SMOOTH_HINT 0x0C52 #define GL_LINE_STIPPLE 0x0B24 #define GL_LINE_STIPPLE_PATTERN 0x0B25 #define GL_LINE_STIPPLE_REPEAT 0x0B26 #define GL_LINE_STRIP 0x0003 #define GL_LINE_STRIP_ADJACENCY 0x000B #define GL_LINE_STRIP_ADJACENCY_ARB 0x000B #define GL_LINE_TOKEN 0x0702 #define GL_LINE_WIDTH 0x0B21 #define GL_LINE_WIDTH_GRANULARITY 0x0B23 #define GL_LINE_WIDTH_RANGE 0x0B22 #define GL_LINK_STATUS 0x8B82 #define GL_LIST_BASE 0x0B32 #define GL_LIST_BIT 0x00020000 #define GL_LIST_INDEX 0x0B33 #define GL_LIST_MODE 0x0B30 #define GL_LOAD 0x0101 #define GL_LOCATION 0x930E #define GL_LOCATION_COMPONENT 0x934A #define GL_LOCATION_INDEX 0x930F #define GL_LOGIC_OP 0x0BF1 #define GL_LOGIC_OP_MODE 0x0BF0 #define GL_LOSE_CONTEXT_ON_RESET 0x8252 #define GL_LOSE_CONTEXT_ON_RESET_ARB 0x8252 #define GL_LOWER_LEFT 0x8CA1 #define GL_LOW_FLOAT 0x8DF0 #define GL_LOW_INT 0x8DF3 #define GL_LUMINANCE 0x1909 #define GL_LUMINANCE12 0x8041 #define GL_LUMINANCE12_ALPHA12 0x8047 #define GL_LUMINANCE12_ALPHA4 0x8046 #define GL_LUMINANCE16 0x8042 #define GL_LUMINANCE16F_ARB 0x881E #define GL_LUMINANCE16_ALPHA16 0x8048 #define GL_LUMINANCE32F_ARB 0x8818 #define GL_LUMINANCE4 0x803F #define GL_LUMINANCE4_ALPHA4 0x8043 #define GL_LUMINANCE6_ALPHA2 0x8044 #define GL_LUMINANCE8 0x8040 #define GL_LUMINANCE8_ALPHA8 0x8045 #define GL_LUMINANCE_ALPHA 0x190A #define GL_LUMINANCE_ALPHA16F_ARB 0x881F #define GL_LUMINANCE_ALPHA32F_ARB 0x8819 #define GL_MAJOR_VERSION 0x821B #define GL_MANUAL_GENERATE_MIPMAP 0x8294 #define GL_MAP1_COLOR_4 0x0D90 #define GL_MAP1_GRID_DOMAIN 0x0DD0 #define GL_MAP1_GRID_SEGMENTS 0x0DD1 #define GL_MAP1_INDEX 0x0D91 #define GL_MAP1_NORMAL 0x0D92 #define GL_MAP1_TEXTURE_COORD_1 0x0D93 #define GL_MAP1_TEXTURE_COORD_2 0x0D94 #define GL_MAP1_TEXTURE_COORD_3 0x0D95 #define GL_MAP1_TEXTURE_COORD_4 0x0D96 #define GL_MAP1_VERTEX_3 0x0D97 #define GL_MAP1_VERTEX_4 0x0D98 #define GL_MAP2_COLOR_4 0x0DB0 #define GL_MAP2_GRID_DOMAIN 0x0DD2 #define GL_MAP2_GRID_SEGMENTS 0x0DD3 #define GL_MAP2_INDEX 0x0DB1 #define GL_MAP2_NORMAL 0x0DB2 #define GL_MAP2_TEXTURE_COORD_1 0x0DB3 #define GL_MAP2_TEXTURE_COORD_2 0x0DB4 #define GL_MAP2_TEXTURE_COORD_3 0x0DB5 #define GL_MAP2_TEXTURE_COORD_4 0x0DB6 #define GL_MAP2_VERTEX_3 0x0DB7 #define GL_MAP2_VERTEX_4 0x0DB8 #define GL_MAP_COHERENT_BIT 0x0080 #define GL_MAP_COLOR 0x0D10 #define GL_MAP_FLUSH_EXPLICIT_BIT 0x0010 #define GL_MAP_INVALIDATE_BUFFER_BIT 0x0008 #define GL_MAP_INVALIDATE_RANGE_BIT 0x0004 #define GL_MAP_PERSISTENT_BIT 0x0040 #define GL_MAP_READ_BIT 0x0001 #define GL_MAP_STENCIL 0x0D11 #define GL_MAP_UNSYNCHRONIZED_BIT 0x0020 #define GL_MAP_WRITE_BIT 0x0002 #define GL_MATRIX0_ARB 0x88C0 #define GL_MATRIX10_ARB 0x88CA #define GL_MATRIX11_ARB 0x88CB #define GL_MATRIX12_ARB 0x88CC #define GL_MATRIX13_ARB 0x88CD #define GL_MATRIX14_ARB 0x88CE #define GL_MATRIX15_ARB 0x88CF #define GL_MATRIX16_ARB 0x88D0 #define GL_MATRIX17_ARB 0x88D1 #define GL_MATRIX18_ARB 0x88D2 #define GL_MATRIX19_ARB 0x88D3 #define GL_MATRIX1_ARB 0x88C1 #define GL_MATRIX20_ARB 0x88D4 #define GL_MATRIX21_ARB 0x88D5 #define GL_MATRIX22_ARB 0x88D6 #define GL_MATRIX23_ARB 0x88D7 #define GL_MATRIX24_ARB 0x88D8 #define GL_MATRIX25_ARB 0x88D9 #define GL_MATRIX26_ARB 0x88DA #define GL_MATRIX27_ARB 0x88DB #define GL_MATRIX28_ARB 0x88DC #define GL_MATRIX29_ARB 0x88DD #define GL_MATRIX2_ARB 0x88C2 #define GL_MATRIX30_ARB 0x88DE #define GL_MATRIX31_ARB 0x88DF #define GL_MATRIX3_ARB 0x88C3 #define GL_MATRIX4_ARB 0x88C4 #define GL_MATRIX5_ARB 0x88C5 #define GL_MATRIX6_ARB 0x88C6 #define GL_MATRIX7_ARB 0x88C7 #define GL_MATRIX8_ARB 0x88C8 #define GL_MATRIX9_ARB 0x88C9 #define GL_MATRIX_INDEX_ARRAY_ARB 0x8844 #define GL_MATRIX_INDEX_ARRAY_POINTER_ARB 0x8849 #define GL_MATRIX_INDEX_ARRAY_SIZE_ARB 0x8846 #define GL_MATRIX_INDEX_ARRAY_STRIDE_ARB 0x8848 #define GL_MATRIX_INDEX_ARRAY_TYPE_ARB 0x8847 #define GL_MATRIX_MODE 0x0BA0 #define GL_MATRIX_PALETTE_ARB 0x8840 #define GL_MATRIX_STRIDE 0x92FF #define GL_MAX 0x8008 #define GL_MAX_3D_TEXTURE_SIZE 0x8073 #define GL_MAX_ARRAY_TEXTURE_LAYERS 0x88FF #define GL_MAX_ATOMIC_COUNTER_BUFFER_BINDINGS 0x92DC #define GL_MAX_ATOMIC_COUNTER_BUFFER_SIZE 0x92D8 #define GL_MAX_ATTRIB_STACK_DEPTH 0x0D35 #define GL_MAX_CLIENT_ATTRIB_STACK_DEPTH 0x0D3B #define GL_MAX_CLIP_DISTANCES 0x0D32 #define GL_MAX_CLIP_PLANES 0x0D32 #define GL_MAX_COLOR_ATTACHMENTS 0x8CDF #define GL_MAX_COLOR_MATRIX_STACK_DEPTH 0x80B3 #define GL_MAX_COLOR_TEXTURE_SAMPLES 0x910E #define GL_MAX_COMBINED_ATOMIC_COUNTERS 0x92D7 #define GL_MAX_COMBINED_ATOMIC_COUNTER_BUFFERS 0x92D1 #define GL_MAX_COMBINED_CLIP_AND_CULL_DISTANCES 0x82FA #define GL_MAX_COMBINED_COMPUTE_UNIFORM_COMPONENTS 0x8266 #define GL_MAX_COMBINED_DIMENSIONS 0x8282 #define GL_MAX_COMBINED_FRAGMENT_UNIFORM_COMPONENTS 0x8A33 #define GL_MAX_COMBINED_GEOMETRY_UNIFORM_COMPONENTS 0x8A32 #define GL_MAX_COMBINED_IMAGE_UNIFORMS 0x90CF #define GL_MAX_COMBINED_IMAGE_UNITS_AND_FRAGMENT_OUTPUTS 0x8F39 #define GL_MAX_COMBINED_SHADER_OUTPUT_RESOURCES 0x8F39 #define GL_MAX_COMBINED_SHADER_STORAGE_BLOCKS 0x90DC #define GL_MAX_COMBINED_TESS_CONTROL_UNIFORM_COMPONENTS 0x8E1E #define GL_MAX_COMBINED_TESS_EVALUATION_UNIFORM_COMPONENTS 0x8E1F #define GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS 0x8B4D #define GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS_ARB 0x8B4D #define GL_MAX_COMBINED_UNIFORM_BLOCKS 0x8A2E #define GL_MAX_COMBINED_VERTEX_UNIFORM_COMPONENTS 0x8A31 #define GL_MAX_COMPUTE_ATOMIC_COUNTERS 0x8265 #define GL_MAX_COMPUTE_ATOMIC_COUNTER_BUFFERS 0x8264 #define GL_MAX_COMPUTE_FIXED_GROUP_INVOCATIONS_ARB 0x90EB #define GL_MAX_COMPUTE_FIXED_GROUP_SIZE_ARB 0x91BF #define GL_MAX_COMPUTE_IMAGE_UNIFORMS 0x91BD #define GL_MAX_COMPUTE_SHADER_STORAGE_BLOCKS 0x90DB #define GL_MAX_COMPUTE_SHARED_MEMORY_SIZE 0x8262 #define GL_MAX_COMPUTE_TEXTURE_IMAGE_UNITS 0x91BC #define GL_MAX_COMPUTE_UNIFORM_BLOCKS 0x91BB #define GL_MAX_COMPUTE_UNIFORM_COMPONENTS 0x8263 #define GL_MAX_COMPUTE_VARIABLE_GROUP_INVOCATIONS_ARB 0x9344 #define GL_MAX_COMPUTE_VARIABLE_GROUP_SIZE_ARB 0x9345 #define GL_MAX_COMPUTE_WORK_GROUP_COUNT 0x91BE #define GL_MAX_COMPUTE_WORK_GROUP_INVOCATIONS 0x90EB #define GL_MAX_COMPUTE_WORK_GROUP_SIZE 0x91BF #define GL_MAX_CONVOLUTION_HEIGHT 0x801B #define GL_MAX_CONVOLUTION_WIDTH 0x801A #define GL_MAX_CUBE_MAP_TEXTURE_SIZE 0x851C #define GL_MAX_CUBE_MAP_TEXTURE_SIZE_ARB 0x851C #define GL_MAX_CULL_DISTANCES 0x82F9 #define GL_MAX_DEBUG_GROUP_STACK_DEPTH 0x826C #define GL_MAX_DEBUG_LOGGED_MESSAGES 0x9144 #define GL_MAX_DEBUG_LOGGED_MESSAGES_ARB 0x9144 #define GL_MAX_DEBUG_MESSAGE_LENGTH 0x9143 #define GL_MAX_DEBUG_MESSAGE_LENGTH_ARB 0x9143 #define GL_MAX_DEPTH 0x8280 #define GL_MAX_DEPTH_TEXTURE_SAMPLES 0x910F #define GL_MAX_DRAW_BUFFERS 0x8824 #define GL_MAX_DRAW_BUFFERS_ARB 0x8824 #define GL_MAX_DUAL_SOURCE_DRAW_BUFFERS 0x88FC #define GL_MAX_ELEMENTS_INDICES 0x80E9 #define GL_MAX_ELEMENTS_VERTICES 0x80E8 #define GL_MAX_ELEMENT_INDEX 0x8D6B #define GL_MAX_EVAL_ORDER 0x0D30 #define GL_MAX_FRAGMENT_ATOMIC_COUNTERS 0x92D6 #define GL_MAX_FRAGMENT_ATOMIC_COUNTER_BUFFERS 0x92D0 #define GL_MAX_FRAGMENT_IMAGE_UNIFORMS 0x90CE #define GL_MAX_FRAGMENT_INPUT_COMPONENTS 0x9125 #define GL_MAX_FRAGMENT_INTERPOLATION_OFFSET 0x8E5C #define GL_MAX_FRAGMENT_SHADER_STORAGE_BLOCKS 0x90DA #define GL_MAX_FRAGMENT_UNIFORM_BLOCKS 0x8A2D #define GL_MAX_FRAGMENT_UNIFORM_COMPONENTS 0x8B49 #define GL_MAX_FRAGMENT_UNIFORM_COMPONENTS_ARB 0x8B49 #define GL_MAX_FRAGMENT_UNIFORM_VECTORS 0x8DFD #define GL_MAX_FRAMEBUFFER_HEIGHT 0x9316 #define GL_MAX_FRAMEBUFFER_LAYERS 0x9317 #define GL_MAX_FRAMEBUFFER_SAMPLES 0x9318 #define GL_MAX_FRAMEBUFFER_WIDTH 0x9315 #define GL_MAX_GEOMETRY_ATOMIC_COUNTERS 0x92D5 #define GL_MAX_GEOMETRY_ATOMIC_COUNTER_BUFFERS 0x92CF #define GL_MAX_GEOMETRY_IMAGE_UNIFORMS 0x90CD #define GL_MAX_GEOMETRY_INPUT_COMPONENTS 0x9123 #define GL_MAX_GEOMETRY_OUTPUT_COMPONENTS 0x9124 #define GL_MAX_GEOMETRY_OUTPUT_VERTICES 0x8DE0 #define GL_MAX_GEOMETRY_OUTPUT_VERTICES_ARB 0x8DE0 #define GL_MAX_GEOMETRY_SHADER_INVOCATIONS 0x8E5A #define GL_MAX_GEOMETRY_SHADER_STORAGE_BLOCKS 0x90D7 #define GL_MAX_GEOMETRY_TEXTURE_IMAGE_UNITS 0x8C29 #define GL_MAX_GEOMETRY_TEXTURE_IMAGE_UNITS_ARB 0x8C29 #define GL_MAX_GEOMETRY_TOTAL_OUTPUT_COMPONENTS 0x8DE1 #define GL_MAX_GEOMETRY_TOTAL_OUTPUT_COMPONENTS_ARB 0x8DE1 #define GL_MAX_GEOMETRY_UNIFORM_BLOCKS 0x8A2C #define GL_MAX_GEOMETRY_UNIFORM_COMPONENTS 0x8DDF #define GL_MAX_GEOMETRY_UNIFORM_COMPONENTS_ARB 0x8DDF #define GL_MAX_GEOMETRY_VARYING_COMPONENTS_ARB 0x8DDD #define GL_MAX_HEIGHT 0x827F #define GL_MAX_IMAGE_SAMPLES 0x906D #define GL_MAX_IMAGE_UNITS 0x8F38 #define GL_MAX_INTEGER_SAMPLES 0x9110 #define GL_MAX_LABEL_LENGTH 0x82E8 #define GL_MAX_LAYERS 0x8281 #define GL_MAX_LIGHTS 0x0D31 #define GL_MAX_LIST_NESTING 0x0B31 #define GL_MAX_MATRIX_PALETTE_STACK_DEPTH_ARB 0x8841 #define GL_MAX_MODELVIEW_STACK_DEPTH 0x0D36 #define GL_MAX_NAME_LENGTH 0x92F6 #define GL_MAX_NAME_STACK_DEPTH 0x0D37 #define GL_MAX_NUM_ACTIVE_VARIABLES 0x92F7 #define GL_MAX_NUM_COMPATIBLE_SUBROUTINES 0x92F8 #define GL_MAX_PALETTE_MATRICES_ARB 0x8842 #define GL_MAX_PATCH_VERTICES 0x8E7D #define GL_MAX_PIXEL_MAP_TABLE 0x0D34 #define GL_MAX_PROGRAM_ADDRESS_REGISTERS_ARB 0x88B1 #define GL_MAX_PROGRAM_ALU_INSTRUCTIONS_ARB 0x880B #define GL_MAX_PROGRAM_ATTRIBS_ARB 0x88AD #define GL_MAX_PROGRAM_ENV_PARAMETERS_ARB 0x88B5 #define GL_MAX_PROGRAM_INSTRUCTIONS_ARB 0x88A1 #define GL_MAX_PROGRAM_LOCAL_PARAMETERS_ARB 0x88B4 #define GL_MAX_PROGRAM_MATRICES_ARB 0x862F #define GL_MAX_PROGRAM_MATRIX_STACK_DEPTH_ARB 0x862E #define GL_MAX_PROGRAM_NATIVE_ADDRESS_REGISTERS_ARB 0x88B3 #define GL_MAX_PROGRAM_NATIVE_ALU_INSTRUCTIONS_ARB 0x880E #define GL_MAX_PROGRAM_NATIVE_ATTRIBS_ARB 0x88AF #define GL_MAX_PROGRAM_NATIVE_INSTRUCTIONS_ARB 0x88A3 #define GL_MAX_PROGRAM_NATIVE_PARAMETERS_ARB 0x88AB #define GL_MAX_PROGRAM_NATIVE_TEMPORARIES_ARB 0x88A7 #define GL_MAX_PROGRAM_NATIVE_TEX_INDIRECTIONS_ARB 0x8810 #define GL_MAX_PROGRAM_NATIVE_TEX_INSTRUCTIONS_ARB 0x880F #define GL_MAX_PROGRAM_PARAMETERS_ARB 0x88A9 #define GL_MAX_PROGRAM_TEMPORARIES_ARB 0x88A5 #define GL_MAX_PROGRAM_TEXEL_OFFSET 0x8905 #define GL_MAX_PROGRAM_TEXTURE_GATHER_COMPONENTS_ARB 0x8F9F #define GL_MAX_PROGRAM_TEXTURE_GATHER_OFFSET_ARB 0x8E5F #define GL_MAX_PROGRAM_TEX_INDIRECTIONS_ARB 0x880D #define GL_MAX_PROGRAM_TEX_INSTRUCTIONS_ARB 0x880C #define GL_MAX_PROJECTION_STACK_DEPTH 0x0D38 #define GL_MAX_RECTANGLE_TEXTURE_SIZE 0x84F8 #define GL_MAX_RECTANGLE_TEXTURE_SIZE_ARB 0x84F8 #define GL_MAX_RENDERBUFFER_SIZE 0x84E8 #define GL_MAX_SAMPLES 0x8D57 #define GL_MAX_SAMPLE_MASK_WORDS 0x8E59 #define GL_MAX_SERVER_WAIT_TIMEOUT 0x9111 #define GL_MAX_SHADER_COMPILER_THREADS_ARB 0x91B0 #define GL_MAX_SHADER_COMPILER_THREADS_KHR 0x91B0 #define GL_MAX_SHADER_STORAGE_BLOCK_SIZE 0x90DE #define GL_MAX_SHADER_STORAGE_BUFFER_BINDINGS 0x90DD #define GL_MAX_SPARSE_3D_TEXTURE_SIZE_ARB 0x9199 #define GL_MAX_SPARSE_ARRAY_TEXTURE_LAYERS_ARB 0x919A #define GL_MAX_SPARSE_TEXTURE_SIZE_ARB 0x9198 #define GL_MAX_SUBROUTINES 0x8DE7 #define GL_MAX_SUBROUTINE_UNIFORM_LOCATIONS 0x8DE8 #define GL_MAX_TESS_CONTROL_ATOMIC_COUNTERS 0x92D3 #define GL_MAX_TESS_CONTROL_ATOMIC_COUNTER_BUFFERS 0x92CD #define GL_MAX_TESS_CONTROL_IMAGE_UNIFORMS 0x90CB #define GL_MAX_TESS_CONTROL_INPUT_COMPONENTS 0x886C #define GL_MAX_TESS_CONTROL_OUTPUT_COMPONENTS 0x8E83 #define GL_MAX_TESS_CONTROL_SHADER_STORAGE_BLOCKS 0x90D8 #define GL_MAX_TESS_CONTROL_TEXTURE_IMAGE_UNITS 0x8E81 #define GL_MAX_TESS_CONTROL_TOTAL_OUTPUT_COMPONENTS 0x8E85 #define GL_MAX_TESS_CONTROL_UNIFORM_BLOCKS 0x8E89 #define GL_MAX_TESS_CONTROL_UNIFORM_COMPONENTS 0x8E7F #define GL_MAX_TESS_EVALUATION_ATOMIC_COUNTERS 0x92D4 #define GL_MAX_TESS_EVALUATION_ATOMIC_COUNTER_BUFFERS 0x92CE #define GL_MAX_TESS_EVALUATION_IMAGE_UNIFORMS 0x90CC #define GL_MAX_TESS_EVALUATION_INPUT_COMPONENTS 0x886D #define GL_MAX_TESS_EVALUATION_OUTPUT_COMPONENTS 0x8E86 #define GL_MAX_TESS_EVALUATION_SHADER_STORAGE_BLOCKS 0x90D9 #define GL_MAX_TESS_EVALUATION_TEXTURE_IMAGE_UNITS 0x8E82 #define GL_MAX_TESS_EVALUATION_UNIFORM_BLOCKS 0x8E8A #define GL_MAX_TESS_EVALUATION_UNIFORM_COMPONENTS 0x8E80 #define GL_MAX_TESS_GEN_LEVEL 0x8E7E #define GL_MAX_TESS_PATCH_COMPONENTS 0x8E84 #define GL_MAX_TEXTURE_BUFFER_SIZE 0x8C2B #define GL_MAX_TEXTURE_BUFFER_SIZE_ARB 0x8C2B #define GL_MAX_TEXTURE_COORDS 0x8871 #define GL_MAX_TEXTURE_COORDS_ARB 0x8871 #define GL_MAX_TEXTURE_IMAGE_UNITS 0x8872 #define GL_MAX_TEXTURE_IMAGE_UNITS_ARB 0x8872 #define GL_MAX_TEXTURE_LOD_BIAS 0x84FD #define GL_MAX_TEXTURE_MAX_ANISOTROPY 0x84FF #define GL_MAX_TEXTURE_SIZE 0x0D33 #define GL_MAX_TEXTURE_STACK_DEPTH 0x0D39 #define GL_MAX_TEXTURE_UNITS 0x84E2 #define GL_MAX_TEXTURE_UNITS_ARB 0x84E2 #define GL_MAX_TRANSFORM_FEEDBACK_BUFFERS 0x8E70 #define GL_MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS 0x8C8A #define GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS 0x8C8B #define GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS 0x8C80 #define GL_MAX_UNIFORM_BLOCK_SIZE 0x8A30 #define GL_MAX_UNIFORM_BUFFER_BINDINGS 0x8A2F #define GL_MAX_UNIFORM_LOCATIONS 0x826E #define GL_MAX_VARYING_COMPONENTS 0x8B4B #define GL_MAX_VARYING_FLOATS 0x8B4B #define GL_MAX_VARYING_FLOATS_ARB 0x8B4B #define GL_MAX_VARYING_VECTORS 0x8DFC #define GL_MAX_VERTEX_ATOMIC_COUNTERS 0x92D2 #define GL_MAX_VERTEX_ATOMIC_COUNTER_BUFFERS 0x92CC #define GL_MAX_VERTEX_ATTRIBS 0x8869 #define GL_MAX_VERTEX_ATTRIBS_ARB 0x8869 #define GL_MAX_VERTEX_ATTRIB_BINDINGS 0x82DA #define GL_MAX_VERTEX_ATTRIB_RELATIVE_OFFSET 0x82D9 #define GL_MAX_VERTEX_IMAGE_UNIFORMS 0x90CA #define GL_MAX_VERTEX_OUTPUT_COMPONENTS 0x9122 #define GL_MAX_VERTEX_SHADER_STORAGE_BLOCKS 0x90D6 #define GL_MAX_VERTEX_STREAMS 0x8E71 #define GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS 0x8B4C #define GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS_ARB 0x8B4C #define GL_MAX_VERTEX_UNIFORM_BLOCKS 0x8A2B #define GL_MAX_VERTEX_UNIFORM_COMPONENTS 0x8B4A #define GL_MAX_VERTEX_UNIFORM_COMPONENTS_ARB 0x8B4A #define GL_MAX_VERTEX_UNIFORM_VECTORS 0x8DFB #define GL_MAX_VERTEX_UNITS_ARB 0x86A4 #define GL_MAX_VERTEX_VARYING_COMPONENTS_ARB 0x8DDE #define GL_MAX_VIEWPORTS 0x825B #define GL_MAX_VIEWPORT_DIMS 0x0D3A #define GL_MAX_WIDTH 0x827E #define GL_MEDIUM_FLOAT 0x8DF1 #define GL_MEDIUM_INT 0x8DF4 #define GL_MIN 0x8007 #define GL_MINMAX 0x802E #define GL_MINMAX_FORMAT 0x802F #define GL_MINMAX_SINK 0x8030 #define GL_MINOR_VERSION 0x821C #define GL_MIN_FRAGMENT_INTERPOLATION_OFFSET 0x8E5B #define GL_MIN_MAP_BUFFER_ALIGNMENT 0x90BC #define GL_MIN_PROGRAM_TEXEL_OFFSET 0x8904 #define GL_MIN_PROGRAM_TEXTURE_GATHER_OFFSET_ARB 0x8E5E #define GL_MIN_SAMPLE_SHADING_VALUE_ARB 0x8C37 #define GL_MIPMAP 0x8293 #define GL_MIRRORED_REPEAT 0x8370 #define GL_MIRRORED_REPEAT_ARB 0x8370 #define GL_MIRROR_CLAMP_TO_EDGE 0x8743 #define GL_MODELVIEW 0x1700 #define GL_MODELVIEW0_ARB 0x1700 #define GL_MODELVIEW10_ARB 0x872A #define GL_MODELVIEW11_ARB 0x872B #define GL_MODELVIEW12_ARB 0x872C #define GL_MODELVIEW13_ARB 0x872D #define GL_MODELVIEW14_ARB 0x872E #define GL_MODELVIEW15_ARB 0x872F #define GL_MODELVIEW16_ARB 0x8730 #define GL_MODELVIEW17_ARB 0x8731 #define GL_MODELVIEW18_ARB 0x8732 #define GL_MODELVIEW19_ARB 0x8733 #define GL_MODELVIEW1_ARB 0x850A #define GL_MODELVIEW20_ARB 0x8734 #define GL_MODELVIEW21_ARB 0x8735 #define GL_MODELVIEW22_ARB 0x8736 #define GL_MODELVIEW23_ARB 0x8737 #define GL_MODELVIEW24_ARB 0x8738 #define GL_MODELVIEW25_ARB 0x8739 #define GL_MODELVIEW26_ARB 0x873A #define GL_MODELVIEW27_ARB 0x873B #define GL_MODELVIEW28_ARB 0x873C #define GL_MODELVIEW29_ARB 0x873D #define GL_MODELVIEW2_ARB 0x8722 #define GL_MODELVIEW30_ARB 0x873E #define GL_MODELVIEW31_ARB 0x873F #define GL_MODELVIEW3_ARB 0x8723 #define GL_MODELVIEW4_ARB 0x8724 #define GL_MODELVIEW5_ARB 0x8725 #define GL_MODELVIEW6_ARB 0x8726 #define GL_MODELVIEW7_ARB 0x8727 #define GL_MODELVIEW8_ARB 0x8728 #define GL_MODELVIEW9_ARB 0x8729 #define GL_MODELVIEW_MATRIX 0x0BA6 #define GL_MODELVIEW_STACK_DEPTH 0x0BA3 #define GL_MODULATE 0x2100 #define GL_MULT 0x0103 #define GL_MULTIPLY_KHR 0x9294 #define GL_MULTISAMPLE 0x809D #define GL_MULTISAMPLE_ARB 0x809D #define GL_MULTISAMPLE_BIT 0x20000000 #define GL_MULTISAMPLE_BIT_ARB 0x20000000 #define GL_MULTISAMPLE_LINE_WIDTH_GRANULARITY_ARB 0x9382 #define GL_MULTISAMPLE_LINE_WIDTH_RANGE_ARB 0x9381 #define GL_N3F_V3F 0x2A25 #define GL_NAMED_STRING_LENGTH_ARB 0x8DE9 #define GL_NAMED_STRING_TYPE_ARB 0x8DEA #define GL_NAME_LENGTH 0x92F9 #define GL_NAME_STACK_DEPTH 0x0D70 #define GL_NAND 0x150E #define GL_NEAREST 0x2600 #define GL_NEAREST_MIPMAP_LINEAR 0x2702 #define GL_NEAREST_MIPMAP_NEAREST 0x2700 #define GL_NEGATIVE_ONE_TO_ONE 0x935E #define GL_NEVER 0x0200 #define GL_NICEST 0x1102 #define GL_NONE 0 #define GL_NOOP 0x1505 #define GL_NOR 0x1508 #define GL_NORMALIZE 0x0BA1 #define GL_NORMAL_ARRAY 0x8075 #define GL_NORMAL_ARRAY_BUFFER_BINDING 0x8897 #define GL_NORMAL_ARRAY_BUFFER_BINDING_ARB 0x8897 #define GL_NORMAL_ARRAY_POINTER 0x808F #define GL_NORMAL_ARRAY_STRIDE 0x807F #define GL_NORMAL_ARRAY_TYPE 0x807E #define GL_NORMAL_MAP 0x8511 #define GL_NORMAL_MAP_ARB 0x8511 #define GL_NOTEQUAL 0x0205 #define GL_NO_ERROR 0 #define GL_NO_RESET_NOTIFICATION 0x8261 #define GL_NO_RESET_NOTIFICATION_ARB 0x8261 #define GL_NUM_ACTIVE_VARIABLES 0x9304 #define GL_NUM_COMPATIBLE_SUBROUTINES 0x8E4A #define GL_NUM_COMPRESSED_TEXTURE_FORMATS 0x86A2 #define GL_NUM_COMPRESSED_TEXTURE_FORMATS_ARB 0x86A2 #define GL_NUM_EXTENSIONS 0x821D #define GL_NUM_PROGRAM_BINARY_FORMATS 0x87FE #define GL_NUM_SAMPLE_COUNTS 0x9380 #define GL_NUM_SHADER_BINARY_FORMATS 0x8DF9 #define GL_NUM_SPARSE_LEVELS_ARB 0x91AA #define GL_NUM_SPIR_V_EXTENSIONS 0x9554 #define GL_NUM_VIRTUAL_PAGE_SIZES_ARB 0x91A8 #define GL_OBJECT_ACTIVE_ATTRIBUTES_ARB 0x8B89 #define GL_OBJECT_ACTIVE_ATTRIBUTE_MAX_LENGTH_ARB 0x8B8A #define GL_OBJECT_ACTIVE_UNIFORMS_ARB 0x8B86 #define GL_OBJECT_ACTIVE_UNIFORM_MAX_LENGTH_ARB 0x8B87 #define GL_OBJECT_ATTACHED_OBJECTS_ARB 0x8B85 #define GL_OBJECT_COMPILE_STATUS_ARB 0x8B81 #define GL_OBJECT_DELETE_STATUS_ARB 0x8B80 #define GL_OBJECT_INFO_LOG_LENGTH_ARB 0x8B84 #define GL_OBJECT_LINEAR 0x2401 #define GL_OBJECT_LINK_STATUS_ARB 0x8B82 #define GL_OBJECT_PLANE 0x2501 #define GL_OBJECT_SHADER_SOURCE_LENGTH_ARB 0x8B88 #define GL_OBJECT_SUBTYPE_ARB 0x8B4F #define GL_OBJECT_TYPE 0x9112 #define GL_OBJECT_TYPE_ARB 0x8B4E #define GL_OBJECT_VALIDATE_STATUS_ARB 0x8B83 #define GL_OFFSET 0x92FC #define GL_ONE 1 #define GL_ONE_MINUS_CONSTANT_ALPHA 0x8004 #define GL_ONE_MINUS_CONSTANT_COLOR 0x8002 #define GL_ONE_MINUS_DST_ALPHA 0x0305 #define GL_ONE_MINUS_DST_COLOR 0x0307 #define GL_ONE_MINUS_SRC1_ALPHA 0x88FB #define GL_ONE_MINUS_SRC1_COLOR 0x88FA #define GL_ONE_MINUS_SRC_ALPHA 0x0303 #define GL_ONE_MINUS_SRC_COLOR 0x0301 #define GL_OPERAND0_ALPHA 0x8598 #define GL_OPERAND0_ALPHA_ARB 0x8598 #define GL_OPERAND0_RGB 0x8590 #define GL_OPERAND0_RGB_ARB 0x8590 #define GL_OPERAND1_ALPHA 0x8599 #define GL_OPERAND1_ALPHA_ARB 0x8599 #define GL_OPERAND1_RGB 0x8591 #define GL_OPERAND1_RGB_ARB 0x8591 #define GL_OPERAND2_ALPHA 0x859A #define GL_OPERAND2_ALPHA_ARB 0x859A #define GL_OPERAND2_RGB 0x8592 #define GL_OPERAND2_RGB_ARB 0x8592 #define GL_OR 0x1507 #define GL_ORDER 0x0A01 #define GL_OR_INVERTED 0x150D #define GL_OR_REVERSE 0x150B #define GL_OUT_OF_MEMORY 0x0505 #define GL_OVERLAY_KHR 0x9296 #define GL_PACK_ALIGNMENT 0x0D05 #define GL_PACK_COMPRESSED_BLOCK_DEPTH 0x912D #define GL_PACK_COMPRESSED_BLOCK_HEIGHT 0x912C #define GL_PACK_COMPRESSED_BLOCK_SIZE 0x912E #define GL_PACK_COMPRESSED_BLOCK_WIDTH 0x912B #define GL_PACK_IMAGE_HEIGHT 0x806C #define GL_PACK_LSB_FIRST 0x0D01 #define GL_PACK_ROW_LENGTH 0x0D02 #define GL_PACK_SKIP_IMAGES 0x806B #define GL_PACK_SKIP_PIXELS 0x0D04 #define GL_PACK_SKIP_ROWS 0x0D03 #define GL_PACK_SWAP_BYTES 0x0D00 #define GL_PARAMETER_BUFFER 0x80EE #define GL_PARAMETER_BUFFER_ARB 0x80EE #define GL_PARAMETER_BUFFER_BINDING 0x80EF #define GL_PARAMETER_BUFFER_BINDING_ARB 0x80EF #define GL_PASS_THROUGH_TOKEN 0x0700 #define GL_PATCHES 0x000E #define GL_PATCH_DEFAULT_INNER_LEVEL 0x8E73 #define GL_PATCH_DEFAULT_OUTER_LEVEL 0x8E74 #define GL_PATCH_VERTICES 0x8E72 #define GL_PERSPECTIVE_CORRECTION_HINT 0x0C50 #define GL_PIXEL_BUFFER_BARRIER_BIT 0x00000080 #define GL_PIXEL_MAP_A_TO_A 0x0C79 #define GL_PIXEL_MAP_A_TO_A_SIZE 0x0CB9 #define GL_PIXEL_MAP_B_TO_B 0x0C78 #define GL_PIXEL_MAP_B_TO_B_SIZE 0x0CB8 #define GL_PIXEL_MAP_G_TO_G 0x0C77 #define GL_PIXEL_MAP_G_TO_G_SIZE 0x0CB7 #define GL_PIXEL_MAP_I_TO_A 0x0C75 #define GL_PIXEL_MAP_I_TO_A_SIZE 0x0CB5 #define GL_PIXEL_MAP_I_TO_B 0x0C74 #define GL_PIXEL_MAP_I_TO_B_SIZE 0x0CB4 #define GL_PIXEL_MAP_I_TO_G 0x0C73 #define GL_PIXEL_MAP_I_TO_G_SIZE 0x0CB3 #define GL_PIXEL_MAP_I_TO_I 0x0C70 #define GL_PIXEL_MAP_I_TO_I_SIZE 0x0CB0 #define GL_PIXEL_MAP_I_TO_R 0x0C72 #define GL_PIXEL_MAP_I_TO_R_SIZE 0x0CB2 #define GL_PIXEL_MAP_R_TO_R 0x0C76 #define GL_PIXEL_MAP_R_TO_R_SIZE 0x0CB6 #define GL_PIXEL_MAP_S_TO_S 0x0C71 #define GL_PIXEL_MAP_S_TO_S_SIZE 0x0CB1 #define GL_PIXEL_MODE_BIT 0x00000020 #define GL_PIXEL_PACK_BUFFER 0x88EB #define GL_PIXEL_PACK_BUFFER_ARB 0x88EB #define GL_PIXEL_PACK_BUFFER_BINDING 0x88ED #define GL_PIXEL_PACK_BUFFER_BINDING_ARB 0x88ED #define GL_PIXEL_UNPACK_BUFFER 0x88EC #define GL_PIXEL_UNPACK_BUFFER_ARB 0x88EC #define GL_PIXEL_UNPACK_BUFFER_BINDING 0x88EF #define GL_PIXEL_UNPACK_BUFFER_BINDING_ARB 0x88EF #define GL_POINT 0x1B00 #define GL_POINTS 0x0000 #define GL_POINT_BIT 0x00000002 #define GL_POINT_DISTANCE_ATTENUATION 0x8129 #define GL_POINT_DISTANCE_ATTENUATION_ARB 0x8129 #define GL_POINT_FADE_THRESHOLD_SIZE 0x8128 #define GL_POINT_FADE_THRESHOLD_SIZE_ARB 0x8128 #define GL_POINT_SIZE 0x0B11 #define GL_POINT_SIZE_GRANULARITY 0x0B13 #define GL_POINT_SIZE_MAX 0x8127 #define GL_POINT_SIZE_MAX_ARB 0x8127 #define GL_POINT_SIZE_MIN 0x8126 #define GL_POINT_SIZE_MIN_ARB 0x8126 #define GL_POINT_SIZE_RANGE 0x0B12 #define GL_POINT_SMOOTH 0x0B10 #define GL_POINT_SMOOTH_HINT 0x0C51 #define GL_POINT_SPRITE 0x8861 #define GL_POINT_SPRITE_ARB 0x8861 #define GL_POINT_SPRITE_COORD_ORIGIN 0x8CA0 #define GL_POINT_TOKEN 0x0701 #define GL_POLYGON 0x0009 #define GL_POLYGON_BIT 0x00000008 #define GL_POLYGON_MODE 0x0B40 #define GL_POLYGON_OFFSET_CLAMP 0x8E1B #define GL_POLYGON_OFFSET_FACTOR 0x8038 #define GL_POLYGON_OFFSET_FILL 0x8037 #define GL_POLYGON_OFFSET_LINE 0x2A02 #define GL_POLYGON_OFFSET_POINT 0x2A01 #define GL_POLYGON_OFFSET_UNITS 0x2A00 #define GL_POLYGON_SMOOTH 0x0B41 #define GL_POLYGON_SMOOTH_HINT 0x0C53 #define GL_POLYGON_STIPPLE 0x0B42 #define GL_POLYGON_STIPPLE_BIT 0x00000010 #define GL_POLYGON_TOKEN 0x0703 #define GL_POSITION 0x1203 #define GL_POST_COLOR_MATRIX_ALPHA_BIAS 0x80BB #define GL_POST_COLOR_MATRIX_ALPHA_SCALE 0x80B7 #define GL_POST_COLOR_MATRIX_BLUE_BIAS 0x80BA #define GL_POST_COLOR_MATRIX_BLUE_SCALE 0x80B6 #define GL_POST_COLOR_MATRIX_COLOR_TABLE 0x80D2 #define GL_POST_COLOR_MATRIX_GREEN_BIAS 0x80B9 #define GL_POST_COLOR_MATRIX_GREEN_SCALE 0x80B5 #define GL_POST_COLOR_MATRIX_RED_BIAS 0x80B8 #define GL_POST_COLOR_MATRIX_RED_SCALE 0x80B4 #define GL_POST_CONVOLUTION_ALPHA_BIAS 0x8023 #define GL_POST_CONVOLUTION_ALPHA_SCALE 0x801F #define GL_POST_CONVOLUTION_BLUE_BIAS 0x8022 #define GL_POST_CONVOLUTION_BLUE_SCALE 0x801E #define GL_POST_CONVOLUTION_COLOR_TABLE 0x80D1 #define GL_POST_CONVOLUTION_GREEN_BIAS 0x8021 #define GL_POST_CONVOLUTION_GREEN_SCALE 0x801D #define GL_POST_CONVOLUTION_RED_BIAS 0x8020 #define GL_POST_CONVOLUTION_RED_SCALE 0x801C #define GL_PREVIOUS 0x8578 #define GL_PREVIOUS_ARB 0x8578 #define GL_PRIMARY_COLOR 0x8577 #define GL_PRIMARY_COLOR_ARB 0x8577 #define GL_PRIMITIVES_GENERATED 0x8C87 #define GL_PRIMITIVES_SUBMITTED 0x82EF #define GL_PRIMITIVES_SUBMITTED_ARB 0x82EF #define GL_PRIMITIVE_BOUNDING_BOX_ARB 0x92BE #define GL_PRIMITIVE_RESTART 0x8F9D #define GL_PRIMITIVE_RESTART_FIXED_INDEX 0x8D69 #define GL_PRIMITIVE_RESTART_INDEX 0x8F9E #define GL_PROGRAM 0x82E2 #define GL_PROGRAMMABLE_SAMPLE_LOCATION_ARB 0x9341 #define GL_PROGRAMMABLE_SAMPLE_LOCATION_TABLE_SIZE_ARB 0x9340 #define GL_PROGRAM_ADDRESS_REGISTERS_ARB 0x88B0 #define GL_PROGRAM_ALU_INSTRUCTIONS_ARB 0x8805 #define GL_PROGRAM_ATTRIBS_ARB 0x88AC #define GL_PROGRAM_BINARY_FORMATS 0x87FF #define GL_PROGRAM_BINARY_LENGTH 0x8741 #define GL_PROGRAM_BINARY_RETRIEVABLE_HINT 0x8257 #define GL_PROGRAM_BINDING_ARB 0x8677 #define GL_PROGRAM_ERROR_POSITION_ARB 0x864B #define GL_PROGRAM_ERROR_STRING_ARB 0x8874 #define GL_PROGRAM_FORMAT_ARB 0x8876 #define GL_PROGRAM_FORMAT_ASCII_ARB 0x8875 #define GL_PROGRAM_INPUT 0x92E3 #define GL_PROGRAM_INSTRUCTIONS_ARB 0x88A0 #define GL_PROGRAM_LENGTH_ARB 0x8627 #define GL_PROGRAM_NATIVE_ADDRESS_REGISTERS_ARB 0x88B2 #define GL_PROGRAM_NATIVE_ALU_INSTRUCTIONS_ARB 0x8808 #define GL_PROGRAM_NATIVE_ATTRIBS_ARB 0x88AE #define GL_PROGRAM_NATIVE_INSTRUCTIONS_ARB 0x88A2 #define GL_PROGRAM_NATIVE_PARAMETERS_ARB 0x88AA #define GL_PROGRAM_NATIVE_TEMPORARIES_ARB 0x88A6 #define GL_PROGRAM_NATIVE_TEX_INDIRECTIONS_ARB 0x880A #define GL_PROGRAM_NATIVE_TEX_INSTRUCTIONS_ARB 0x8809 #define GL_PROGRAM_OBJECT_ARB 0x8B40 #define GL_PROGRAM_OUTPUT 0x92E4 #define GL_PROGRAM_PARAMETERS_ARB 0x88A8 #define GL_PROGRAM_PIPELINE 0x82E4 #define GL_PROGRAM_PIPELINE_BINDING 0x825A #define GL_PROGRAM_POINT_SIZE 0x8642 #define GL_PROGRAM_POINT_SIZE_ARB 0x8642 #define GL_PROGRAM_SEPARABLE 0x8258 #define GL_PROGRAM_STRING_ARB 0x8628 #define GL_PROGRAM_TEMPORARIES_ARB 0x88A4 #define GL_PROGRAM_TEX_INDIRECTIONS_ARB 0x8807 #define GL_PROGRAM_TEX_INSTRUCTIONS_ARB 0x8806 #define GL_PROGRAM_UNDER_NATIVE_LIMITS_ARB 0x88B6 #define GL_PROJECTION 0x1701 #define GL_PROJECTION_MATRIX 0x0BA7 #define GL_PROJECTION_STACK_DEPTH 0x0BA4 #define GL_PROVOKING_VERTEX 0x8E4F #define GL_PROXY_COLOR_TABLE 0x80D3 #define GL_PROXY_HISTOGRAM 0x8025 #define GL_PROXY_POST_COLOR_MATRIX_COLOR_TABLE 0x80D5 #define GL_PROXY_POST_CONVOLUTION_COLOR_TABLE 0x80D4 #define GL_PROXY_TEXTURE_1D 0x8063 #define GL_PROXY_TEXTURE_1D_ARRAY 0x8C19 #define GL_PROXY_TEXTURE_2D 0x8064 #define GL_PROXY_TEXTURE_2D_ARRAY 0x8C1B #define GL_PROXY_TEXTURE_2D_MULTISAMPLE 0x9101 #define GL_PROXY_TEXTURE_2D_MULTISAMPLE_ARRAY 0x9103 #define GL_PROXY_TEXTURE_3D 0x8070 #define GL_PROXY_TEXTURE_CUBE_MAP 0x851B #define GL_PROXY_TEXTURE_CUBE_MAP_ARB 0x851B #define GL_PROXY_TEXTURE_CUBE_MAP_ARRAY_ARB 0x900B #define GL_PROXY_TEXTURE_RECTANGLE 0x84F7 #define GL_PROXY_TEXTURE_RECTANGLE_ARB 0x84F7 #define GL_Q 0x2003 #define GL_QUADRATIC_ATTENUATION 0x1209 #define GL_QUADS 0x0007 #define GL_QUADS_FOLLOW_PROVOKING_VERTEX_CONVENTION 0x8E4C #define GL_QUAD_STRIP 0x0008 #define GL_QUERY 0x82E3 #define GL_QUERY_BUFFER 0x9192 #define GL_QUERY_BUFFER_BARRIER_BIT 0x00008000 #define GL_QUERY_BUFFER_BINDING 0x9193 #define GL_QUERY_BY_REGION_NO_WAIT 0x8E16 #define GL_QUERY_BY_REGION_NO_WAIT_INVERTED 0x8E1A #define GL_QUERY_BY_REGION_WAIT 0x8E15 #define GL_QUERY_BY_REGION_WAIT_INVERTED 0x8E19 #define GL_QUERY_COUNTER_BITS 0x8864 #define GL_QUERY_COUNTER_BITS_ARB 0x8864 #define GL_QUERY_NO_WAIT 0x8E14 #define GL_QUERY_NO_WAIT_INVERTED 0x8E18 #define GL_QUERY_RESULT 0x8866 #define GL_QUERY_RESULT_ARB 0x8866 #define GL_QUERY_RESULT_AVAILABLE 0x8867 #define GL_QUERY_RESULT_AVAILABLE_ARB 0x8867 #define GL_QUERY_RESULT_NO_WAIT 0x9194 #define GL_QUERY_TARGET 0x82EA #define GL_QUERY_WAIT 0x8E13 #define GL_QUERY_WAIT_INVERTED 0x8E17 #define GL_R 0x2002 #define GL_R11F_G11F_B10F 0x8C3A #define GL_R16 0x822A #define GL_R16F 0x822D #define GL_R16I 0x8233 #define GL_R16UI 0x8234 #define GL_R16_SNORM 0x8F98 #define GL_R32F 0x822E #define GL_R32I 0x8235 #define GL_R32UI 0x8236 #define GL_R3_G3_B2 0x2A10 #define GL_R8 0x8229 #define GL_R8I 0x8231 #define GL_R8UI 0x8232 #define GL_R8_SNORM 0x8F94 #define GL_RASTERIZER_DISCARD 0x8C89 #define GL_READ_BUFFER 0x0C02 #define GL_READ_FRAMEBUFFER 0x8CA8 #define GL_READ_FRAMEBUFFER_BINDING 0x8CAA #define GL_READ_ONLY 0x88B8 #define GL_READ_ONLY_ARB 0x88B8 #define GL_READ_PIXELS 0x828C #define GL_READ_PIXELS_FORMAT 0x828D #define GL_READ_PIXELS_TYPE 0x828E #define GL_READ_WRITE 0x88BA #define GL_READ_WRITE_ARB 0x88BA #define GL_RED 0x1903 #define GL_REDUCE 0x8016 #define GL_RED_BIAS 0x0D15 #define GL_RED_BITS 0x0D52 #define GL_RED_INTEGER 0x8D94 #define GL_RED_SCALE 0x0D14 #define GL_REFERENCED_BY_COMPUTE_SHADER 0x930B #define GL_REFERENCED_BY_FRAGMENT_SHADER 0x930A #define GL_REFERENCED_BY_GEOMETRY_SHADER 0x9309 #define GL_REFERENCED_BY_TESS_CONTROL_SHADER 0x9307 #define GL_REFERENCED_BY_TESS_EVALUATION_SHADER 0x9308 #define GL_REFERENCED_BY_VERTEX_SHADER 0x9306 #define GL_REFLECTION_MAP 0x8512 #define GL_REFLECTION_MAP_ARB 0x8512 #define GL_RENDER 0x1C00 #define GL_RENDERBUFFER 0x8D41 #define GL_RENDERBUFFER_ALPHA_SIZE 0x8D53 #define GL_RENDERBUFFER_BINDING 0x8CA7 #define GL_RENDERBUFFER_BLUE_SIZE 0x8D52 #define GL_RENDERBUFFER_DEPTH_SIZE 0x8D54 #define GL_RENDERBUFFER_GREEN_SIZE 0x8D51 #define GL_RENDERBUFFER_HEIGHT 0x8D43 #define GL_RENDERBUFFER_INTERNAL_FORMAT 0x8D44 #define GL_RENDERBUFFER_RED_SIZE 0x8D50 #define GL_RENDERBUFFER_SAMPLES 0x8CAB #define GL_RENDERBUFFER_STENCIL_SIZE 0x8D55 #define GL_RENDERBUFFER_WIDTH 0x8D42 #define GL_RENDERER 0x1F01 #define GL_RENDER_MODE 0x0C40 #define GL_REPEAT 0x2901 #define GL_REPLACE 0x1E01 #define GL_REPLICATE_BORDER 0x8153 #define GL_RESCALE_NORMAL 0x803A #define GL_RESET_NOTIFICATION_STRATEGY 0x8256 #define GL_RESET_NOTIFICATION_STRATEGY_ARB 0x8256 #define GL_RETURN 0x0102 #define GL_RG 0x8227 #define GL_RG16 0x822C #define GL_RG16F 0x822F #define GL_RG16I 0x8239 #define GL_RG16UI 0x823A #define GL_RG16_SNORM 0x8F99 #define GL_RG32F 0x8230 #define GL_RG32I 0x823B #define GL_RG32UI 0x823C #define GL_RG8 0x822B #define GL_RG8I 0x8237 #define GL_RG8UI 0x8238 #define GL_RG8_SNORM 0x8F95 #define GL_RGB 0x1907 #define GL_RGB10 0x8052 #define GL_RGB10_A2 0x8059 #define GL_RGB10_A2UI 0x906F #define GL_RGB12 0x8053 #define GL_RGB16 0x8054 #define GL_RGB16F 0x881B #define GL_RGB16F_ARB 0x881B #define GL_RGB16I 0x8D89 #define GL_RGB16UI 0x8D77 #define GL_RGB16_SNORM 0x8F9A #define GL_RGB32F 0x8815 #define GL_RGB32F_ARB 0x8815 #define GL_RGB32I 0x8D83 #define GL_RGB32UI 0x8D71 #define GL_RGB4 0x804F #define GL_RGB5 0x8050 #define GL_RGB565 0x8D62 #define GL_RGB5_A1 0x8057 #define GL_RGB8 0x8051 #define GL_RGB8I 0x8D8F #define GL_RGB8UI 0x8D7D #define GL_RGB8_SNORM 0x8F96 #define GL_RGB9_E5 0x8C3D #define GL_RGBA 0x1908 #define GL_RGBA12 0x805A #define GL_RGBA16 0x805B #define GL_RGBA16F 0x881A #define GL_RGBA16F_ARB 0x881A #define GL_RGBA16I 0x8D88 #define GL_RGBA16UI 0x8D76 #define GL_RGBA16_SNORM 0x8F9B #define GL_RGBA2 0x8055 #define GL_RGBA32F 0x8814 #define GL_RGBA32F_ARB 0x8814 #define GL_RGBA32I 0x8D82 #define GL_RGBA32UI 0x8D70 #define GL_RGBA4 0x8056 #define GL_RGBA8 0x8058 #define GL_RGBA8I 0x8D8E #define GL_RGBA8UI 0x8D7C #define GL_RGBA8_SNORM 0x8F97 #define GL_RGBA_FLOAT_MODE_ARB 0x8820 #define GL_RGBA_INTEGER 0x8D99 #define GL_RGBA_MODE 0x0C31 #define GL_RGB_INTEGER 0x8D98 #define GL_RGB_SCALE 0x8573 #define GL_RGB_SCALE_ARB 0x8573 #define GL_RG_INTEGER 0x8228 #define GL_RIGHT 0x0407 #define GL_S 0x2000 #define GL_SAMPLER 0x82E6 #define GL_SAMPLER_1D 0x8B5D #define GL_SAMPLER_1D_ARB 0x8B5D #define GL_SAMPLER_1D_ARRAY 0x8DC0 #define GL_SAMPLER_1D_ARRAY_SHADOW 0x8DC3 #define GL_SAMPLER_1D_SHADOW 0x8B61 #define GL_SAMPLER_1D_SHADOW_ARB 0x8B61 #define GL_SAMPLER_2D 0x8B5E #define GL_SAMPLER_2D_ARB 0x8B5E #define GL_SAMPLER_2D_ARRAY 0x8DC1 #define GL_SAMPLER_2D_ARRAY_SHADOW 0x8DC4 #define GL_SAMPLER_2D_MULTISAMPLE 0x9108 #define GL_SAMPLER_2D_MULTISAMPLE_ARRAY 0x910B #define GL_SAMPLER_2D_RECT 0x8B63 #define GL_SAMPLER_2D_RECT_ARB 0x8B63 #define GL_SAMPLER_2D_RECT_SHADOW 0x8B64 #define GL_SAMPLER_2D_RECT_SHADOW_ARB 0x8B64 #define GL_SAMPLER_2D_SHADOW 0x8B62 #define GL_SAMPLER_2D_SHADOW_ARB 0x8B62 #define GL_SAMPLER_3D 0x8B5F #define GL_SAMPLER_3D_ARB 0x8B5F #define GL_SAMPLER_BINDING 0x8919 #define GL_SAMPLER_BUFFER 0x8DC2 #define GL_SAMPLER_CUBE 0x8B60 #define GL_SAMPLER_CUBE_ARB 0x8B60 #define GL_SAMPLER_CUBE_MAP_ARRAY_ARB 0x900C #define GL_SAMPLER_CUBE_MAP_ARRAY_SHADOW_ARB 0x900D #define GL_SAMPLER_CUBE_SHADOW 0x8DC5 #define GL_SAMPLES 0x80A9 #define GL_SAMPLES_ARB 0x80A9 #define GL_SAMPLES_PASSED 0x8914 #define GL_SAMPLES_PASSED_ARB 0x8914 #define GL_SAMPLE_ALPHA_TO_COVERAGE 0x809E #define GL_SAMPLE_ALPHA_TO_COVERAGE_ARB 0x809E #define GL_SAMPLE_ALPHA_TO_ONE 0x809F #define GL_SAMPLE_ALPHA_TO_ONE_ARB 0x809F #define GL_SAMPLE_BUFFERS 0x80A8 #define GL_SAMPLE_BUFFERS_ARB 0x80A8 #define GL_SAMPLE_COVERAGE 0x80A0 #define GL_SAMPLE_COVERAGE_ARB 0x80A0 #define GL_SAMPLE_COVERAGE_INVERT 0x80AB #define GL_SAMPLE_COVERAGE_INVERT_ARB 0x80AB #define GL_SAMPLE_COVERAGE_VALUE 0x80AA #define GL_SAMPLE_COVERAGE_VALUE_ARB 0x80AA #define GL_SAMPLE_LOCATION_ARB 0x8E50 #define GL_SAMPLE_LOCATION_PIXEL_GRID_HEIGHT_ARB 0x933F #define GL_SAMPLE_LOCATION_PIXEL_GRID_WIDTH_ARB 0x933E #define GL_SAMPLE_LOCATION_SUBPIXEL_BITS_ARB 0x933D #define GL_SAMPLE_MASK 0x8E51 #define GL_SAMPLE_MASK_VALUE 0x8E52 #define GL_SAMPLE_POSITION 0x8E50 #define GL_SAMPLE_SHADING_ARB 0x8C36 #define GL_SCISSOR_BIT 0x00080000 #define GL_SCISSOR_BOX 0x0C10 #define GL_SCISSOR_TEST 0x0C11 #define GL_SCREEN_KHR 0x9295 #define GL_SECONDARY_COLOR_ARRAY 0x845E #define GL_SECONDARY_COLOR_ARRAY_BUFFER_BINDING 0x889C #define GL_SECONDARY_COLOR_ARRAY_BUFFER_BINDING_ARB 0x889C #define GL_SECONDARY_COLOR_ARRAY_POINTER 0x845D #define GL_SECONDARY_COLOR_ARRAY_SIZE 0x845A #define GL_SECONDARY_COLOR_ARRAY_STRIDE 0x845C #define GL_SECONDARY_COLOR_ARRAY_TYPE 0x845B #define GL_SELECT 0x1C02 #define GL_SELECTION_BUFFER_POINTER 0x0DF3 #define GL_SELECTION_BUFFER_SIZE 0x0DF4 #define GL_SEPARABLE_2D 0x8012 #define GL_SEPARATE_ATTRIBS 0x8C8D #define GL_SEPARATE_SPECULAR_COLOR 0x81FA #define GL_SET 0x150F #define GL_SHADER 0x82E1 #define GL_SHADER_BINARY_FORMATS 0x8DF8 #define GL_SHADER_BINARY_FORMAT_SPIR_V 0x9551 #define GL_SHADER_BINARY_FORMAT_SPIR_V_ARB 0x9551 #define GL_SHADER_COMPILER 0x8DFA #define GL_SHADER_IMAGE_ACCESS_BARRIER_BIT 0x00000020 #define GL_SHADER_IMAGE_ATOMIC 0x82A6 #define GL_SHADER_IMAGE_LOAD 0x82A4 #define GL_SHADER_IMAGE_STORE 0x82A5 #define GL_SHADER_INCLUDE_ARB 0x8DAE #define GL_SHADER_OBJECT_ARB 0x8B48 #define GL_SHADER_SOURCE_LENGTH 0x8B88 #define GL_SHADER_STORAGE_BARRIER_BIT 0x00002000 #define GL_SHADER_STORAGE_BLOCK 0x92E6 #define GL_SHADER_STORAGE_BUFFER 0x90D2 #define GL_SHADER_STORAGE_BUFFER_BINDING 0x90D3 #define GL_SHADER_STORAGE_BUFFER_OFFSET_ALIGNMENT 0x90DF #define GL_SHADER_STORAGE_BUFFER_SIZE 0x90D5 #define GL_SHADER_STORAGE_BUFFER_START 0x90D4 #define GL_SHADER_TYPE 0x8B4F #define GL_SHADE_MODEL 0x0B54 #define GL_SHADING_LANGUAGE_VERSION 0x8B8C #define GL_SHADING_LANGUAGE_VERSION_ARB 0x8B8C #define GL_SHININESS 0x1601 #define GL_SHORT 0x1402 #define GL_SIGNALED 0x9119 #define GL_SIGNED_NORMALIZED 0x8F9C #define GL_SIMULTANEOUS_TEXTURE_AND_DEPTH_TEST 0x82AC #define GL_SIMULTANEOUS_TEXTURE_AND_DEPTH_WRITE 0x82AE #define GL_SIMULTANEOUS_TEXTURE_AND_STENCIL_TEST 0x82AD #define GL_SIMULTANEOUS_TEXTURE_AND_STENCIL_WRITE 0x82AF #define GL_SINGLE_COLOR 0x81F9 #define GL_SLUMINANCE 0x8C46 #define GL_SLUMINANCE8 0x8C47 #define GL_SLUMINANCE8_ALPHA8 0x8C45 #define GL_SLUMINANCE_ALPHA 0x8C44 #define GL_SMOOTH 0x1D01 #define GL_SMOOTH_LINE_WIDTH_GRANULARITY 0x0B23 #define GL_SMOOTH_LINE_WIDTH_RANGE 0x0B22 #define GL_SMOOTH_POINT_SIZE_GRANULARITY 0x0B13 #define GL_SMOOTH_POINT_SIZE_RANGE 0x0B12 #define GL_SOFTLIGHT_KHR 0x929C #define GL_SOURCE0_ALPHA 0x8588 #define GL_SOURCE0_ALPHA_ARB 0x8588 #define GL_SOURCE0_RGB 0x8580 #define GL_SOURCE0_RGB_ARB 0x8580 #define GL_SOURCE1_ALPHA 0x8589 #define GL_SOURCE1_ALPHA_ARB 0x8589 #define GL_SOURCE1_RGB 0x8581 #define GL_SOURCE1_RGB_ARB 0x8581 #define GL_SOURCE2_ALPHA 0x858A #define GL_SOURCE2_ALPHA_ARB 0x858A #define GL_SOURCE2_RGB 0x8582 #define GL_SOURCE2_RGB_ARB 0x8582 #define GL_SPARSE_BUFFER_PAGE_SIZE_ARB 0x82F8 #define GL_SPARSE_STORAGE_BIT_ARB 0x0400 #define GL_SPARSE_TEXTURE_FULL_ARRAY_CUBE_MIPMAPS_ARB 0x91A9 #define GL_SPECULAR 0x1202 #define GL_SPHERE_MAP 0x2402 #define GL_SPIR_V_BINARY 0x9552 #define GL_SPIR_V_BINARY_ARB 0x9552 #define GL_SPIR_V_EXTENSIONS 0x9553 #define GL_SPOT_CUTOFF 0x1206 #define GL_SPOT_DIRECTION 0x1204 #define GL_SPOT_EXPONENT 0x1205 #define GL_SRC0_ALPHA 0x8588 #define GL_SRC0_RGB 0x8580 #define GL_SRC1_ALPHA 0x8589 #define GL_SRC1_COLOR 0x88F9 #define GL_SRC1_RGB 0x8581 #define GL_SRC2_ALPHA 0x858A #define GL_SRC2_RGB 0x8582 #define GL_SRC_ALPHA 0x0302 #define GL_SRC_ALPHA_SATURATE 0x0308 #define GL_SRC_COLOR 0x0300 #define GL_SRGB 0x8C40 #define GL_SRGB8 0x8C41 #define GL_SRGB8_ALPHA8 0x8C43 #define GL_SRGB_ALPHA 0x8C42 #define GL_SRGB_DECODE_ARB 0x8299 #define GL_SRGB_READ 0x8297 #define GL_SRGB_WRITE 0x8298 #define GL_STACK_OVERFLOW 0x0503 #define GL_STACK_UNDERFLOW 0x0504 #define GL_STATIC_COPY 0x88E6 #define GL_STATIC_COPY_ARB 0x88E6 #define GL_STATIC_DRAW 0x88E4 #define GL_STATIC_DRAW_ARB 0x88E4 #define GL_STATIC_READ 0x88E5 #define GL_STATIC_READ_ARB 0x88E5 #define GL_STENCIL 0x1802 #define GL_STENCIL_ATTACHMENT 0x8D20 #define GL_STENCIL_BACK_FAIL 0x8801 #define GL_STENCIL_BACK_FUNC 0x8800 #define GL_STENCIL_BACK_PASS_DEPTH_FAIL 0x8802 #define GL_STENCIL_BACK_PASS_DEPTH_PASS 0x8803 #define GL_STENCIL_BACK_REF 0x8CA3 #define GL_STENCIL_BACK_VALUE_MASK 0x8CA4 #define GL_STENCIL_BACK_WRITEMASK 0x8CA5 #define GL_STENCIL_BITS 0x0D57 #define GL_STENCIL_BUFFER_BIT 0x00000400 #define GL_STENCIL_CLEAR_VALUE 0x0B91 #define GL_STENCIL_COMPONENTS 0x8285 #define GL_STENCIL_FAIL 0x0B94 #define GL_STENCIL_FUNC 0x0B92 #define GL_STENCIL_INDEX 0x1901 #define GL_STENCIL_INDEX1 0x8D46 #define GL_STENCIL_INDEX16 0x8D49 #define GL_STENCIL_INDEX4 0x8D47 #define GL_STENCIL_INDEX8 0x8D48 #define GL_STENCIL_PASS_DEPTH_FAIL 0x0B95 #define GL_STENCIL_PASS_DEPTH_PASS 0x0B96 #define GL_STENCIL_REF 0x0B97 #define GL_STENCIL_RENDERABLE 0x8288 #define GL_STENCIL_TEST 0x0B90 #define GL_STENCIL_VALUE_MASK 0x0B93 #define GL_STENCIL_WRITEMASK 0x0B98 #define GL_STEREO 0x0C33 #define GL_STREAM_COPY 0x88E2 #define GL_STREAM_COPY_ARB 0x88E2 #define GL_STREAM_DRAW 0x88E0 #define GL_STREAM_DRAW_ARB 0x88E0 #define GL_STREAM_READ 0x88E1 #define GL_STREAM_READ_ARB 0x88E1 #define GL_SUBGROUP_FEATURE_ARITHMETIC_BIT_KHR 0x00000004 #define GL_SUBGROUP_FEATURE_BALLOT_BIT_KHR 0x00000008 #define GL_SUBGROUP_FEATURE_BASIC_BIT_KHR 0x00000001 #define GL_SUBGROUP_FEATURE_CLUSTERED_BIT_KHR 0x00000040 #define GL_SUBGROUP_FEATURE_QUAD_BIT_KHR 0x00000080 #define GL_SUBGROUP_FEATURE_SHUFFLE_BIT_KHR 0x00000010 #define GL_SUBGROUP_FEATURE_SHUFFLE_RELATIVE_BIT_KHR 0x00000020 #define GL_SUBGROUP_FEATURE_VOTE_BIT_KHR 0x00000002 #define GL_SUBGROUP_QUAD_ALL_STAGES_KHR 0x9535 #define GL_SUBGROUP_SIZE_KHR 0x9532 #define GL_SUBGROUP_SUPPORTED_FEATURES_KHR 0x9534 #define GL_SUBGROUP_SUPPORTED_STAGES_KHR 0x9533 #define GL_SUBPIXEL_BITS 0x0D50 #define GL_SUBTRACT 0x84E7 #define GL_SUBTRACT_ARB 0x84E7 #define GL_SYNC_CL_EVENT_ARB 0x8240 #define GL_SYNC_CL_EVENT_COMPLETE_ARB 0x8241 #define GL_SYNC_CONDITION 0x9113 #define GL_SYNC_FENCE 0x9116 #define GL_SYNC_FLAGS 0x9115 #define GL_SYNC_FLUSH_COMMANDS_BIT 0x00000001 #define GL_SYNC_GPU_COMMANDS_COMPLETE 0x9117 #define GL_SYNC_STATUS 0x9114 #define GL_T 0x2001 #define GL_T2F_C3F_V3F 0x2A2A #define GL_T2F_C4F_N3F_V3F 0x2A2C #define GL_T2F_C4UB_V3F 0x2A29 #define GL_T2F_N3F_V3F 0x2A2B #define GL_T2F_V3F 0x2A27 #define GL_T4F_C4F_N3F_V4F 0x2A2D #define GL_T4F_V4F 0x2A28 #define GL_TABLE_TOO_LARGE 0x8031 #define GL_TESS_CONTROL_OUTPUT_VERTICES 0x8E75 #define GL_TESS_CONTROL_SHADER 0x8E88 #define GL_TESS_CONTROL_SHADER_BIT 0x00000008 #define GL_TESS_CONTROL_SHADER_PATCHES 0x82F1 #define GL_TESS_CONTROL_SHADER_PATCHES_ARB 0x82F1 #define GL_TESS_CONTROL_SUBROUTINE 0x92E9 #define GL_TESS_CONTROL_SUBROUTINE_UNIFORM 0x92EF #define GL_TESS_CONTROL_TEXTURE 0x829C #define GL_TESS_EVALUATION_SHADER 0x8E87 #define GL_TESS_EVALUATION_SHADER_BIT 0x00000010 #define GL_TESS_EVALUATION_SHADER_INVOCATIONS 0x82F2 #define GL_TESS_EVALUATION_SHADER_INVOCATIONS_ARB 0x82F2 #define GL_TESS_EVALUATION_SUBROUTINE 0x92EA #define GL_TESS_EVALUATION_SUBROUTINE_UNIFORM 0x92F0 #define GL_TESS_EVALUATION_TEXTURE 0x829D #define GL_TESS_GEN_MODE 0x8E76 #define GL_TESS_GEN_POINT_MODE 0x8E79 #define GL_TESS_GEN_SPACING 0x8E77 #define GL_TESS_GEN_VERTEX_ORDER 0x8E78 #define GL_TEXTURE 0x1702 #define GL_TEXTURE0 0x84C0 #define GL_TEXTURE0_ARB 0x84C0 #define GL_TEXTURE1 0x84C1 #define GL_TEXTURE10 0x84CA #define GL_TEXTURE10_ARB 0x84CA #define GL_TEXTURE11 0x84CB #define GL_TEXTURE11_ARB 0x84CB #define GL_TEXTURE12 0x84CC #define GL_TEXTURE12_ARB 0x84CC #define GL_TEXTURE13 0x84CD #define GL_TEXTURE13_ARB 0x84CD #define GL_TEXTURE14 0x84CE #define GL_TEXTURE14_ARB 0x84CE #define GL_TEXTURE15 0x84CF #define GL_TEXTURE15_ARB 0x84CF #define GL_TEXTURE16 0x84D0 #define GL_TEXTURE16_ARB 0x84D0 #define GL_TEXTURE17 0x84D1 #define GL_TEXTURE17_ARB 0x84D1 #define GL_TEXTURE18 0x84D2 #define GL_TEXTURE18_ARB 0x84D2 #define GL_TEXTURE19 0x84D3 #define GL_TEXTURE19_ARB 0x84D3 #define GL_TEXTURE1_ARB 0x84C1 #define GL_TEXTURE2 0x84C2 #define GL_TEXTURE20 0x84D4 #define GL_TEXTURE20_ARB 0x84D4 #define GL_TEXTURE21 0x84D5 #define GL_TEXTURE21_ARB 0x84D5 #define GL_TEXTURE22 0x84D6 #define GL_TEXTURE22_ARB 0x84D6 #define GL_TEXTURE23 0x84D7 #define GL_TEXTURE23_ARB 0x84D7 #define GL_TEXTURE24 0x84D8 #define GL_TEXTURE24_ARB 0x84D8 #define GL_TEXTURE25 0x84D9 #define GL_TEXTURE25_ARB 0x84D9 #define GL_TEXTURE26 0x84DA #define GL_TEXTURE26_ARB 0x84DA #define GL_TEXTURE27 0x84DB #define GL_TEXTURE27_ARB 0x84DB #define GL_TEXTURE28 0x84DC #define GL_TEXTURE28_ARB 0x84DC #define GL_TEXTURE29 0x84DD #define GL_TEXTURE29_ARB 0x84DD #define GL_TEXTURE2_ARB 0x84C2 #define GL_TEXTURE3 0x84C3 #define GL_TEXTURE30 0x84DE #define GL_TEXTURE30_ARB 0x84DE #define GL_TEXTURE31 0x84DF #define GL_TEXTURE31_ARB 0x84DF #define GL_TEXTURE3_ARB 0x84C3 #define GL_TEXTURE4 0x84C4 #define GL_TEXTURE4_ARB 0x84C4 #define GL_TEXTURE5 0x84C5 #define GL_TEXTURE5_ARB 0x84C5 #define GL_TEXTURE6 0x84C6 #define GL_TEXTURE6_ARB 0x84C6 #define GL_TEXTURE7 0x84C7 #define GL_TEXTURE7_ARB 0x84C7 #define GL_TEXTURE8 0x84C8 #define GL_TEXTURE8_ARB 0x84C8 #define GL_TEXTURE9 0x84C9 #define GL_TEXTURE9_ARB 0x84C9 #define GL_TEXTURE_1D 0x0DE0 #define GL_TEXTURE_1D_ARRAY 0x8C18 #define GL_TEXTURE_2D 0x0DE1 #define GL_TEXTURE_2D_ARRAY 0x8C1A #define GL_TEXTURE_2D_MULTISAMPLE 0x9100 #define GL_TEXTURE_2D_MULTISAMPLE_ARRAY 0x9102 #define GL_TEXTURE_3D 0x806F #define GL_TEXTURE_ALPHA_SIZE 0x805F #define GL_TEXTURE_ALPHA_TYPE 0x8C13 #define GL_TEXTURE_ALPHA_TYPE_ARB 0x8C13 #define GL_TEXTURE_BASE_LEVEL 0x813C #define GL_TEXTURE_BINDING_1D 0x8068 #define GL_TEXTURE_BINDING_1D_ARRAY 0x8C1C #define GL_TEXTURE_BINDING_2D 0x8069 #define GL_TEXTURE_BINDING_2D_ARRAY 0x8C1D #define GL_TEXTURE_BINDING_2D_MULTISAMPLE 0x9104 #define GL_TEXTURE_BINDING_2D_MULTISAMPLE_ARRAY 0x9105 #define GL_TEXTURE_BINDING_3D 0x806A #define GL_TEXTURE_BINDING_BUFFER 0x8C2C #define GL_TEXTURE_BINDING_BUFFER_ARB 0x8C2C #define GL_TEXTURE_BINDING_CUBE_MAP 0x8514 #define GL_TEXTURE_BINDING_CUBE_MAP_ARB 0x8514 #define GL_TEXTURE_BINDING_CUBE_MAP_ARRAY 0x900A #define GL_TEXTURE_BINDING_CUBE_MAP_ARRAY_ARB 0x900A #define GL_TEXTURE_BINDING_RECTANGLE 0x84F6 #define GL_TEXTURE_BINDING_RECTANGLE_ARB 0x84F6 #define GL_TEXTURE_BIT 0x00040000 #define GL_TEXTURE_BLUE_SIZE 0x805E #define GL_TEXTURE_BLUE_TYPE 0x8C12 #define GL_TEXTURE_BLUE_TYPE_ARB 0x8C12 #define GL_TEXTURE_BORDER 0x1005 #define GL_TEXTURE_BORDER_COLOR 0x1004 #define GL_TEXTURE_BUFFER 0x8C2A #define GL_TEXTURE_BUFFER_ARB 0x8C2A #define GL_TEXTURE_BUFFER_DATA_STORE_BINDING 0x8C2D #define GL_TEXTURE_BUFFER_DATA_STORE_BINDING_ARB 0x8C2D #define GL_TEXTURE_BUFFER_FORMAT_ARB 0x8C2E #define GL_TEXTURE_BUFFER_OFFSET 0x919D #define GL_TEXTURE_BUFFER_OFFSET_ALIGNMENT 0x919F #define GL_TEXTURE_BUFFER_SIZE 0x919E #define GL_TEXTURE_COMPARE_FAIL_VALUE_ARB 0x80BF #define GL_TEXTURE_COMPARE_FUNC 0x884D #define GL_TEXTURE_COMPARE_FUNC_ARB 0x884D #define GL_TEXTURE_COMPARE_MODE 0x884C #define GL_TEXTURE_COMPARE_MODE_ARB 0x884C #define GL_TEXTURE_COMPONENTS 0x1003 #define GL_TEXTURE_COMPRESSED 0x86A1 #define GL_TEXTURE_COMPRESSED_ARB 0x86A1 #define GL_TEXTURE_COMPRESSED_BLOCK_HEIGHT 0x82B2 #define GL_TEXTURE_COMPRESSED_BLOCK_SIZE 0x82B3 #define GL_TEXTURE_COMPRESSED_BLOCK_WIDTH 0x82B1 #define GL_TEXTURE_COMPRESSED_IMAGE_SIZE 0x86A0 #define GL_TEXTURE_COMPRESSED_IMAGE_SIZE_ARB 0x86A0 #define GL_TEXTURE_COMPRESSION_HINT 0x84EF #define GL_TEXTURE_COMPRESSION_HINT_ARB 0x84EF #define GL_TEXTURE_COORD_ARRAY 0x8078 #define GL_TEXTURE_COORD_ARRAY_BUFFER_BINDING 0x889A #define GL_TEXTURE_COORD_ARRAY_BUFFER_BINDING_ARB 0x889A #define GL_TEXTURE_COORD_ARRAY_POINTER 0x8092 #define GL_TEXTURE_COORD_ARRAY_SIZE 0x8088 #define GL_TEXTURE_COORD_ARRAY_STRIDE 0x808A #define GL_TEXTURE_COORD_ARRAY_TYPE 0x8089 #define GL_TEXTURE_CUBE_MAP 0x8513 #define GL_TEXTURE_CUBE_MAP_ARB 0x8513 #define GL_TEXTURE_CUBE_MAP_ARRAY 0x9009 #define GL_TEXTURE_CUBE_MAP_ARRAY_ARB 0x9009 #define GL_TEXTURE_CUBE_MAP_NEGATIVE_X 0x8516 #define GL_TEXTURE_CUBE_MAP_NEGATIVE_X_ARB 0x8516 #define GL_TEXTURE_CUBE_MAP_NEGATIVE_Y 0x8518 #define GL_TEXTURE_CUBE_MAP_NEGATIVE_Y_ARB 0x8518 #define GL_TEXTURE_CUBE_MAP_NEGATIVE_Z 0x851A #define GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_ARB 0x851A #define GL_TEXTURE_CUBE_MAP_POSITIVE_X 0x8515 #define GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB 0x8515 #define GL_TEXTURE_CUBE_MAP_POSITIVE_Y 0x8517 #define GL_TEXTURE_CUBE_MAP_POSITIVE_Y_ARB 0x8517 #define GL_TEXTURE_CUBE_MAP_POSITIVE_Z 0x8519 #define GL_TEXTURE_CUBE_MAP_POSITIVE_Z_ARB 0x8519 #define GL_TEXTURE_CUBE_MAP_SEAMLESS 0x884F #define GL_TEXTURE_DEPTH 0x8071 #define GL_TEXTURE_DEPTH_SIZE 0x884A #define GL_TEXTURE_DEPTH_SIZE_ARB 0x884A #define GL_TEXTURE_DEPTH_TYPE 0x8C16 #define GL_TEXTURE_DEPTH_TYPE_ARB 0x8C16 #define GL_TEXTURE_ENV 0x2300 #define GL_TEXTURE_ENV_COLOR 0x2201 #define GL_TEXTURE_ENV_MODE 0x2200 #define GL_TEXTURE_FETCH_BARRIER_BIT 0x00000008 #define GL_TEXTURE_FILTER_CONTROL 0x8500 #define GL_TEXTURE_FIXED_SAMPLE_LOCATIONS 0x9107 #define GL_TEXTURE_GATHER 0x82A2 #define GL_TEXTURE_GATHER_SHADOW 0x82A3 #define GL_TEXTURE_GEN_MODE 0x2500 #define GL_TEXTURE_GEN_Q 0x0C63 #define GL_TEXTURE_GEN_R 0x0C62 #define GL_TEXTURE_GEN_S 0x0C60 #define GL_TEXTURE_GEN_T 0x0C61 #define GL_TEXTURE_GREEN_SIZE 0x805D #define GL_TEXTURE_GREEN_TYPE 0x8C11 #define GL_TEXTURE_GREEN_TYPE_ARB 0x8C11 #define GL_TEXTURE_HEIGHT 0x1001 #define GL_TEXTURE_IMAGE_FORMAT 0x828F #define GL_TEXTURE_IMAGE_TYPE 0x8290 #define GL_TEXTURE_IMMUTABLE_FORMAT 0x912F #define GL_TEXTURE_IMMUTABLE_LEVELS 0x82DF #define GL_TEXTURE_INTENSITY_SIZE 0x8061 #define GL_TEXTURE_INTENSITY_TYPE 0x8C15 #define GL_TEXTURE_INTENSITY_TYPE_ARB 0x8C15 #define GL_TEXTURE_INTERNAL_FORMAT 0x1003 #define GL_TEXTURE_LOD_BIAS 0x8501 #define GL_TEXTURE_LUMINANCE_SIZE 0x8060 #define GL_TEXTURE_LUMINANCE_TYPE 0x8C14 #define GL_TEXTURE_LUMINANCE_TYPE_ARB 0x8C14 #define GL_TEXTURE_MAG_FILTER 0x2800 #define GL_TEXTURE_MATRIX 0x0BA8 #define GL_TEXTURE_MAX_ANISOTROPY 0x84FE #define GL_TEXTURE_MAX_LEVEL 0x813D #define GL_TEXTURE_MAX_LOD 0x813B #define GL_TEXTURE_MIN_FILTER 0x2801 #define GL_TEXTURE_MIN_LOD 0x813A #define GL_TEXTURE_PRIORITY 0x8066 #define GL_TEXTURE_RECTANGLE 0x84F5 #define GL_TEXTURE_RECTANGLE_ARB 0x84F5 #define GL_TEXTURE_REDUCTION_MODE_ARB 0x9366 #define GL_TEXTURE_RED_SIZE 0x805C #define GL_TEXTURE_RED_TYPE 0x8C10 #define GL_TEXTURE_RED_TYPE_ARB 0x8C10 #define GL_TEXTURE_RESIDENT 0x8067 #define GL_TEXTURE_SAMPLES 0x9106 #define GL_TEXTURE_SHADOW 0x82A1 #define GL_TEXTURE_SHARED_SIZE 0x8C3F #define GL_TEXTURE_SPARSE_ARB 0x91A6 #define GL_TEXTURE_STACK_DEPTH 0x0BA5 #define GL_TEXTURE_STENCIL_SIZE 0x88F1 #define GL_TEXTURE_SWIZZLE_A 0x8E45 #define GL_TEXTURE_SWIZZLE_B 0x8E44 #define GL_TEXTURE_SWIZZLE_G 0x8E43 #define GL_TEXTURE_SWIZZLE_R 0x8E42 #define GL_TEXTURE_SWIZZLE_RGBA 0x8E46 #define GL_TEXTURE_TARGET 0x1006 #define GL_TEXTURE_UPDATE_BARRIER_BIT 0x00000100 #define GL_TEXTURE_VIEW 0x82B5 #define GL_TEXTURE_VIEW_MIN_LAYER 0x82DD #define GL_TEXTURE_VIEW_MIN_LEVEL 0x82DB #define GL_TEXTURE_VIEW_NUM_LAYERS 0x82DE #define GL_TEXTURE_VIEW_NUM_LEVELS 0x82DC #define GL_TEXTURE_WIDTH 0x1000 #define GL_TEXTURE_WRAP_R 0x8072 #define GL_TEXTURE_WRAP_S 0x2802 #define GL_TEXTURE_WRAP_T 0x2803 #define GL_TIMEOUT_EXPIRED 0x911B #define GL_TIMEOUT_IGNORED 0xFFFFFFFFFFFFFFFF #define GL_TIMESTAMP 0x8E28 #define GL_TIME_ELAPSED 0x88BF #define GL_TOP_LEVEL_ARRAY_SIZE 0x930C #define GL_TOP_LEVEL_ARRAY_STRIDE 0x930D #define GL_TRANSFORM_BIT 0x00001000 #define GL_TRANSFORM_FEEDBACK 0x8E22 #define GL_TRANSFORM_FEEDBACK_BARRIER_BIT 0x00000800 #define GL_TRANSFORM_FEEDBACK_BINDING 0x8E25 #define GL_TRANSFORM_FEEDBACK_BUFFER 0x8C8E #define GL_TRANSFORM_FEEDBACK_BUFFER_ACTIVE 0x8E24 #define GL_TRANSFORM_FEEDBACK_BUFFER_BINDING 0x8C8F #define GL_TRANSFORM_FEEDBACK_BUFFER_INDEX 0x934B #define GL_TRANSFORM_FEEDBACK_BUFFER_MODE 0x8C7F #define GL_TRANSFORM_FEEDBACK_BUFFER_PAUSED 0x8E23 #define GL_TRANSFORM_FEEDBACK_BUFFER_SIZE 0x8C85 #define GL_TRANSFORM_FEEDBACK_BUFFER_START 0x8C84 #define GL_TRANSFORM_FEEDBACK_BUFFER_STRIDE 0x934C #define GL_TRANSFORM_FEEDBACK_OVERFLOW 0x82EC #define GL_TRANSFORM_FEEDBACK_OVERFLOW_ARB 0x82EC #define GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN 0x8C88 #define GL_TRANSFORM_FEEDBACK_STREAM_OVERFLOW 0x82ED #define GL_TRANSFORM_FEEDBACK_STREAM_OVERFLOW_ARB 0x82ED #define GL_TRANSFORM_FEEDBACK_VARYING 0x92F4 #define GL_TRANSFORM_FEEDBACK_VARYINGS 0x8C83 #define GL_TRANSFORM_FEEDBACK_VARYING_MAX_LENGTH 0x8C76 #define GL_TRANSPOSE_COLOR_MATRIX 0x84E6 #define GL_TRANSPOSE_COLOR_MATRIX_ARB 0x84E6 #define GL_TRANSPOSE_CURRENT_MATRIX_ARB 0x88B7 #define GL_TRANSPOSE_MODELVIEW_MATRIX 0x84E3 #define GL_TRANSPOSE_MODELVIEW_MATRIX_ARB 0x84E3 #define GL_TRANSPOSE_PROJECTION_MATRIX 0x84E4 #define GL_TRANSPOSE_PROJECTION_MATRIX_ARB 0x84E4 #define GL_TRANSPOSE_TEXTURE_MATRIX 0x84E5 #define GL_TRANSPOSE_TEXTURE_MATRIX_ARB 0x84E5 #define GL_TRIANGLES 0x0004 #define GL_TRIANGLES_ADJACENCY 0x000C #define GL_TRIANGLES_ADJACENCY_ARB 0x000C #define GL_TRIANGLE_FAN 0x0006 #define GL_TRIANGLE_STRIP 0x0005 #define GL_TRIANGLE_STRIP_ADJACENCY 0x000D #define GL_TRIANGLE_STRIP_ADJACENCY_ARB 0x000D #define GL_TRUE 1 #define GL_TYPE 0x92FA #define GL_UNDEFINED_VERTEX 0x8260 #define GL_UNIFORM 0x92E1 #define GL_UNIFORM_ARRAY_STRIDE 0x8A3C #define GL_UNIFORM_ATOMIC_COUNTER_BUFFER_INDEX 0x92DA #define GL_UNIFORM_BARRIER_BIT 0x00000004 #define GL_UNIFORM_BLOCK 0x92E2 #define GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS 0x8A42 #define GL_UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES 0x8A43 #define GL_UNIFORM_BLOCK_BINDING 0x8A3F #define GL_UNIFORM_BLOCK_DATA_SIZE 0x8A40 #define GL_UNIFORM_BLOCK_INDEX 0x8A3A #define GL_UNIFORM_BLOCK_NAME_LENGTH 0x8A41 #define GL_UNIFORM_BLOCK_REFERENCED_BY_COMPUTE_SHADER 0x90EC #define GL_UNIFORM_BLOCK_REFERENCED_BY_FRAGMENT_SHADER 0x8A46 #define GL_UNIFORM_BLOCK_REFERENCED_BY_GEOMETRY_SHADER 0x8A45 #define GL_UNIFORM_BLOCK_REFERENCED_BY_TESS_CONTROL_SHADER 0x84F0 #define GL_UNIFORM_BLOCK_REFERENCED_BY_TESS_EVALUATION_SHADER 0x84F1 #define GL_UNIFORM_BLOCK_REFERENCED_BY_VERTEX_SHADER 0x8A44 #define GL_UNIFORM_BUFFER 0x8A11 #define GL_UNIFORM_BUFFER_BINDING 0x8A28 #define GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT 0x8A34 #define GL_UNIFORM_BUFFER_SIZE 0x8A2A #define GL_UNIFORM_BUFFER_START 0x8A29 #define GL_UNIFORM_IS_ROW_MAJOR 0x8A3E #define GL_UNIFORM_MATRIX_STRIDE 0x8A3D #define GL_UNIFORM_NAME_LENGTH 0x8A39 #define GL_UNIFORM_OFFSET 0x8A3B #define GL_UNIFORM_SIZE 0x8A38 #define GL_UNIFORM_TYPE 0x8A37 #define GL_UNKNOWN_CONTEXT_RESET 0x8255 #define GL_UNKNOWN_CONTEXT_RESET_ARB 0x8255 #define GL_UNPACK_ALIGNMENT 0x0CF5 #define GL_UNPACK_COMPRESSED_BLOCK_DEPTH 0x9129 #define GL_UNPACK_COMPRESSED_BLOCK_HEIGHT 0x9128 #define GL_UNPACK_COMPRESSED_BLOCK_SIZE 0x912A #define GL_UNPACK_COMPRESSED_BLOCK_WIDTH 0x9127 #define GL_UNPACK_IMAGE_HEIGHT 0x806E #define GL_UNPACK_LSB_FIRST 0x0CF1 #define GL_UNPACK_ROW_LENGTH 0x0CF2 #define GL_UNPACK_SKIP_IMAGES 0x806D #define GL_UNPACK_SKIP_PIXELS 0x0CF4 #define GL_UNPACK_SKIP_ROWS 0x0CF3 #define GL_UNPACK_SWAP_BYTES 0x0CF0 #define GL_UNSIGNALED 0x9118 #define GL_UNSIGNED_BYTE 0x1401 #define GL_UNSIGNED_BYTE_2_3_3_REV 0x8362 #define GL_UNSIGNED_BYTE_3_3_2 0x8032 #define GL_UNSIGNED_INT 0x1405 #define GL_UNSIGNED_INT64_ARB 0x140F #define GL_UNSIGNED_INT64_VEC2_ARB 0x8FF5 #define GL_UNSIGNED_INT64_VEC3_ARB 0x8FF6 #define GL_UNSIGNED_INT64_VEC4_ARB 0x8FF7 #define GL_UNSIGNED_INT_10F_11F_11F_REV 0x8C3B #define GL_UNSIGNED_INT_10_10_10_2 0x8036 #define GL_UNSIGNED_INT_24_8 0x84FA #define GL_UNSIGNED_INT_2_10_10_10_REV 0x8368 #define GL_UNSIGNED_INT_5_9_9_9_REV 0x8C3E #define GL_UNSIGNED_INT_8_8_8_8 0x8035 #define GL_UNSIGNED_INT_8_8_8_8_REV 0x8367 #define GL_UNSIGNED_INT_ATOMIC_COUNTER 0x92DB #define GL_UNSIGNED_INT_IMAGE_1D 0x9062 #define GL_UNSIGNED_INT_IMAGE_1D_ARRAY 0x9068 #define GL_UNSIGNED_INT_IMAGE_2D 0x9063 #define GL_UNSIGNED_INT_IMAGE_2D_ARRAY 0x9069 #define GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE 0x906B #define GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE_ARRAY 0x906C #define GL_UNSIGNED_INT_IMAGE_2D_RECT 0x9065 #define GL_UNSIGNED_INT_IMAGE_3D 0x9064 #define GL_UNSIGNED_INT_IMAGE_BUFFER 0x9067 #define GL_UNSIGNED_INT_IMAGE_CUBE 0x9066 #define GL_UNSIGNED_INT_IMAGE_CUBE_MAP_ARRAY 0x906A #define GL_UNSIGNED_INT_SAMPLER_1D 0x8DD1 #define GL_UNSIGNED_INT_SAMPLER_1D_ARRAY 0x8DD6 #define GL_UNSIGNED_INT_SAMPLER_2D 0x8DD2 #define GL_UNSIGNED_INT_SAMPLER_2D_ARRAY 0x8DD7 #define GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE 0x910A #define GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY 0x910D #define GL_UNSIGNED_INT_SAMPLER_2D_RECT 0x8DD5 #define GL_UNSIGNED_INT_SAMPLER_3D 0x8DD3 #define GL_UNSIGNED_INT_SAMPLER_BUFFER 0x8DD8 #define GL_UNSIGNED_INT_SAMPLER_CUBE 0x8DD4 #define GL_UNSIGNED_INT_SAMPLER_CUBE_MAP_ARRAY_ARB 0x900F #define GL_UNSIGNED_INT_VEC2 0x8DC6 #define GL_UNSIGNED_INT_VEC3 0x8DC7 #define GL_UNSIGNED_INT_VEC4 0x8DC8 #define GL_UNSIGNED_NORMALIZED 0x8C17 #define GL_UNSIGNED_NORMALIZED_ARB 0x8C17 #define GL_UNSIGNED_SHORT 0x1403 #define GL_UNSIGNED_SHORT_1_5_5_5_REV 0x8366 #define GL_UNSIGNED_SHORT_4_4_4_4 0x8033 #define GL_UNSIGNED_SHORT_4_4_4_4_REV 0x8365 #define GL_UNSIGNED_SHORT_5_5_5_1 0x8034 #define GL_UNSIGNED_SHORT_5_6_5 0x8363 #define GL_UNSIGNED_SHORT_5_6_5_REV 0x8364 #define GL_UPPER_LEFT 0x8CA2 #define GL_V2F 0x2A20 #define GL_V3F 0x2A21 #define GL_VALIDATE_STATUS 0x8B83 #define GL_VENDOR 0x1F00 #define GL_VERSION 0x1F02 #define GL_VERTEX_ARRAY 0x8074 #define GL_VERTEX_ARRAY_BINDING 0x85B5 #define GL_VERTEX_ARRAY_BUFFER_BINDING 0x8896 #define GL_VERTEX_ARRAY_BUFFER_BINDING_ARB 0x8896 #define GL_VERTEX_ARRAY_POINTER 0x808E #define GL_VERTEX_ARRAY_SIZE 0x807A #define GL_VERTEX_ARRAY_STRIDE 0x807C #define GL_VERTEX_ARRAY_TYPE 0x807B #define GL_VERTEX_ATTRIB_ARRAY_BARRIER_BIT 0x00000001 #define GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING 0x889F #define GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING_ARB 0x889F #define GL_VERTEX_ATTRIB_ARRAY_DIVISOR 0x88FE #define GL_VERTEX_ATTRIB_ARRAY_DIVISOR_ARB 0x88FE #define GL_VERTEX_ATTRIB_ARRAY_ENABLED 0x8622 #define GL_VERTEX_ATTRIB_ARRAY_ENABLED_ARB 0x8622 #define GL_VERTEX_ATTRIB_ARRAY_INTEGER 0x88FD #define GL_VERTEX_ATTRIB_ARRAY_NORMALIZED 0x886A #define GL_VERTEX_ATTRIB_ARRAY_NORMALIZED_ARB 0x886A #define GL_VERTEX_ATTRIB_ARRAY_POINTER 0x8645 #define GL_VERTEX_ATTRIB_ARRAY_POINTER_ARB 0x8645 #define GL_VERTEX_ATTRIB_ARRAY_SIZE 0x8623 #define GL_VERTEX_ATTRIB_ARRAY_SIZE_ARB 0x8623 #define GL_VERTEX_ATTRIB_ARRAY_STRIDE 0x8624 #define GL_VERTEX_ATTRIB_ARRAY_STRIDE_ARB 0x8624 #define GL_VERTEX_ATTRIB_ARRAY_TYPE 0x8625 #define GL_VERTEX_ATTRIB_ARRAY_TYPE_ARB 0x8625 #define GL_VERTEX_ATTRIB_BINDING 0x82D4 #define GL_VERTEX_ATTRIB_RELATIVE_OFFSET 0x82D5 #define GL_VERTEX_BINDING_DIVISOR 0x82D6 #define GL_VERTEX_BINDING_OFFSET 0x82D7 #define GL_VERTEX_BINDING_STRIDE 0x82D8 #define GL_VERTEX_BLEND_ARB 0x86A7 #define GL_VERTEX_PROGRAM_ARB 0x8620 #define GL_VERTEX_PROGRAM_POINT_SIZE 0x8642 #define GL_VERTEX_PROGRAM_POINT_SIZE_ARB 0x8642 #define GL_VERTEX_PROGRAM_TWO_SIDE 0x8643 #define GL_VERTEX_PROGRAM_TWO_SIDE_ARB 0x8643 #define GL_VERTEX_SHADER 0x8B31 #define GL_VERTEX_SHADER_ARB 0x8B31 #define GL_VERTEX_SHADER_BIT 0x00000001 #define GL_VERTEX_SHADER_INVOCATIONS 0x82F0 #define GL_VERTEX_SHADER_INVOCATIONS_ARB 0x82F0 #define GL_VERTEX_SUBROUTINE 0x92E8 #define GL_VERTEX_SUBROUTINE_UNIFORM 0x92EE #define GL_VERTEX_TEXTURE 0x829B #define GL_VERTICES_SUBMITTED 0x82EE #define GL_VERTICES_SUBMITTED_ARB 0x82EE #define GL_VIEWPORT 0x0BA2 #define GL_VIEWPORT_BIT 0x00000800 #define GL_VIEWPORT_BOUNDS_RANGE 0x825D #define GL_VIEWPORT_INDEX_PROVOKING_VERTEX 0x825F #define GL_VIEWPORT_SUBPIXEL_BITS 0x825C #define GL_VIEW_CLASS_128_BITS 0x82C4 #define GL_VIEW_CLASS_16_BITS 0x82CA #define GL_VIEW_CLASS_24_BITS 0x82C9 #define GL_VIEW_CLASS_32_BITS 0x82C8 #define GL_VIEW_CLASS_48_BITS 0x82C7 #define GL_VIEW_CLASS_64_BITS 0x82C6 #define GL_VIEW_CLASS_8_BITS 0x82CB #define GL_VIEW_CLASS_96_BITS 0x82C5 #define GL_VIEW_CLASS_ASTC_10x10_RGBA 0x9393 #define GL_VIEW_CLASS_ASTC_10x5_RGBA 0x9390 #define GL_VIEW_CLASS_ASTC_10x6_RGBA 0x9391 #define GL_VIEW_CLASS_ASTC_10x8_RGBA 0x9392 #define GL_VIEW_CLASS_ASTC_12x10_RGBA 0x9394 #define GL_VIEW_CLASS_ASTC_12x12_RGBA 0x9395 #define GL_VIEW_CLASS_ASTC_4x4_RGBA 0x9388 #define GL_VIEW_CLASS_ASTC_5x4_RGBA 0x9389 #define GL_VIEW_CLASS_ASTC_5x5_RGBA 0x938A #define GL_VIEW_CLASS_ASTC_6x5_RGBA 0x938B #define GL_VIEW_CLASS_ASTC_6x6_RGBA 0x938C #define GL_VIEW_CLASS_ASTC_8x5_RGBA 0x938D #define GL_VIEW_CLASS_ASTC_8x6_RGBA 0x938E #define GL_VIEW_CLASS_ASTC_8x8_RGBA 0x938F #define GL_VIEW_CLASS_BPTC_FLOAT 0x82D3 #define GL_VIEW_CLASS_BPTC_UNORM 0x82D2 #define GL_VIEW_CLASS_EAC_R11 0x9383 #define GL_VIEW_CLASS_EAC_RG11 0x9384 #define GL_VIEW_CLASS_ETC2_EAC_RGBA 0x9387 #define GL_VIEW_CLASS_ETC2_RGB 0x9385 #define GL_VIEW_CLASS_ETC2_RGBA 0x9386 #define GL_VIEW_CLASS_RGTC1_RED 0x82D0 #define GL_VIEW_CLASS_RGTC2_RG 0x82D1 #define GL_VIEW_CLASS_S3TC_DXT1_RGB 0x82CC #define GL_VIEW_CLASS_S3TC_DXT1_RGBA 0x82CD #define GL_VIEW_CLASS_S3TC_DXT3_RGBA 0x82CE #define GL_VIEW_CLASS_S3TC_DXT5_RGBA 0x82CF #define GL_VIEW_COMPATIBILITY_CLASS 0x82B6 #define GL_VIRTUAL_PAGE_SIZE_INDEX_ARB 0x91A7 #define GL_VIRTUAL_PAGE_SIZE_X_ARB 0x9195 #define GL_VIRTUAL_PAGE_SIZE_Y_ARB 0x9196 #define GL_VIRTUAL_PAGE_SIZE_Z_ARB 0x9197 #define GL_WAIT_FAILED 0x911D #define GL_WEIGHTED_AVERAGE_ARB 0x9367 #define GL_WEIGHT_ARRAY_ARB 0x86AD #define GL_WEIGHT_ARRAY_BUFFER_BINDING 0x889E #define GL_WEIGHT_ARRAY_BUFFER_BINDING_ARB 0x889E #define GL_WEIGHT_ARRAY_POINTER_ARB 0x86AC #define GL_WEIGHT_ARRAY_SIZE_ARB 0x86AB #define GL_WEIGHT_ARRAY_STRIDE_ARB 0x86AA #define GL_WEIGHT_ARRAY_TYPE_ARB 0x86A9 #define GL_WEIGHT_SUM_UNITY_ARB 0x86A6 #define GL_WRITE_ONLY 0x88B9 #define GL_WRITE_ONLY_ARB 0x88B9 #define GL_XOR 0x1506 #define GL_ZERO 0 #define GL_ZERO_TO_ONE 0x935F #define GL_ZOOM_X 0x0D16 #define GL_ZOOM_Y 0x0D17 #ifndef __khrplatform_h_ #define __khrplatform_h_ /* ** Copyright (c) 2008-2018 The Khronos Group Inc. ** ** Permission is hereby granted, free of charge, to any person obtaining a ** copy of this software and/or associated documentation files (the ** "Materials"), to deal in the Materials without restriction, including ** without limitation the rights to use, copy, modify, merge, publish, ** distribute, sublicense, and/or sell copies of the Materials, and to ** permit persons to whom the Materials are 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 Materials. ** ** THE MATERIALS ARE 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 ** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. */ /* Khronos platform-specific types and definitions. * * The master copy of khrplatform.h is maintained in the Khronos EGL * Registry repository at https://github.com/KhronosGroup/EGL-Registry * The last semantic modification to khrplatform.h was at commit ID: * 67a3e0864c2d75ea5287b9f3d2eb74a745936692 * * Adopters may modify this file to suit their platform. Adopters are * encouraged to submit platform specific modifications to the Khronos * group so that they can be included in future versions of this file. * Please submit changes by filing pull requests or issues on * the EGL Registry repository linked above. * * * See the Implementer's Guidelines for information about where this file * should be located on your system and for more details of its use: * http://www.khronos.org/registry/implementers_guide.pdf * * This file should be included as * #include * by Khronos client API header files that use its types and defines. * * The types in khrplatform.h should only be used to define API-specific types. * * Types defined in khrplatform.h: * khronos_int8_t signed 8 bit * khronos_uint8_t unsigned 8 bit * khronos_int16_t signed 16 bit * khronos_uint16_t unsigned 16 bit * khronos_int32_t signed 32 bit * khronos_uint32_t unsigned 32 bit * khronos_int64_t signed 64 bit * khronos_uint64_t unsigned 64 bit * khronos_intptr_t signed same number of bits as a pointer * khronos_uintptr_t unsigned same number of bits as a pointer * khronos_ssize_t signed size * khronos_usize_t unsigned size * khronos_float_t signed 32 bit floating point * khronos_time_ns_t unsigned 64 bit time in nanoseconds * khronos_utime_nanoseconds_t unsigned time interval or absolute time in * nanoseconds * khronos_stime_nanoseconds_t signed time interval in nanoseconds * khronos_boolean_enum_t enumerated boolean type. This should * only be used as a base type when a client API's boolean type is * an enum. Client APIs which use an integer or other type for * booleans cannot use this as the base type for their boolean. * * Tokens defined in khrplatform.h: * * KHRONOS_FALSE, KHRONOS_TRUE Enumerated boolean false/true values. * * KHRONOS_SUPPORT_INT64 is 1 if 64 bit integers are supported; otherwise 0. * KHRONOS_SUPPORT_FLOAT is 1 if floats are supported; otherwise 0. * * Calling convention macros defined in this file: * KHRONOS_APICALL * KHRONOS_GLAD_API_PTR * KHRONOS_APIATTRIBUTES * * These may be used in function prototypes as: * * KHRONOS_APICALL void KHRONOS_GLAD_API_PTR funcname( * int arg1, * int arg2) KHRONOS_APIATTRIBUTES; */ #if defined(__SCITECH_SNAP__) && !defined(KHRONOS_STATIC) # define KHRONOS_STATIC 1 #endif /*------------------------------------------------------------------------- * Definition of KHRONOS_APICALL *------------------------------------------------------------------------- * This precedes the return type of the function in the function prototype. */ #if defined(KHRONOS_STATIC) /* If the preprocessor constant KHRONOS_STATIC is defined, make the * header compatible with static linking. */ # define KHRONOS_APICALL #elif defined(_WIN32) # define KHRONOS_APICALL __declspec(dllimport) #elif defined (__SYMBIAN32__) # define KHRONOS_APICALL IMPORT_C #elif defined(__ANDROID__) # define KHRONOS_APICALL __attribute__((visibility("default"))) #else # define KHRONOS_APICALL #endif /*------------------------------------------------------------------------- * Definition of KHRONOS_GLAD_API_PTR *------------------------------------------------------------------------- * This follows the return type of the function and precedes the function * name in the function prototype. */ #if defined(_WIN32) && !defined(_WIN32_WCE) && !defined(KHRONOS_STATIC) /* Win32 but not WinCE */ # define KHRONOS_GLAD_API_PTR __stdcall #else # define KHRONOS_GLAD_API_PTR #endif /*------------------------------------------------------------------------- * Definition of KHRONOS_APIATTRIBUTES *------------------------------------------------------------------------- * This follows the closing parenthesis of the function prototype arguments. */ #if defined (__ARMCC_2__) #define KHRONOS_APIATTRIBUTES __softfp #else #define KHRONOS_APIATTRIBUTES #endif /*------------------------------------------------------------------------- * basic type definitions *-----------------------------------------------------------------------*/ #if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(__GNUC__) || defined(__SCO__) || defined(__USLC__) /* * Using */ #include typedef int32_t khronos_int32_t; typedef uint32_t khronos_uint32_t; typedef int64_t khronos_int64_t; typedef uint64_t khronos_uint64_t; #define KHRONOS_SUPPORT_INT64 1 #define KHRONOS_SUPPORT_FLOAT 1 #elif defined(__VMS ) || defined(__sgi) /* * Using */ #include typedef int32_t khronos_int32_t; typedef uint32_t khronos_uint32_t; typedef int64_t khronos_int64_t; typedef uint64_t khronos_uint64_t; #define KHRONOS_SUPPORT_INT64 1 #define KHRONOS_SUPPORT_FLOAT 1 #elif defined(_WIN32) && !defined(__SCITECH_SNAP__) /* * Win32 */ typedef __int32 khronos_int32_t; typedef unsigned __int32 khronos_uint32_t; typedef __int64 khronos_int64_t; typedef unsigned __int64 khronos_uint64_t; #define KHRONOS_SUPPORT_INT64 1 #define KHRONOS_SUPPORT_FLOAT 1 #elif defined(__sun__) || defined(__digital__) /* * Sun or Digital */ typedef int khronos_int32_t; typedef unsigned int khronos_uint32_t; #if defined(__arch64__) || defined(_LP64) typedef long int khronos_int64_t; typedef unsigned long int khronos_uint64_t; #else typedef long long int khronos_int64_t; typedef unsigned long long int khronos_uint64_t; #endif /* __arch64__ */ #define KHRONOS_SUPPORT_INT64 1 #define KHRONOS_SUPPORT_FLOAT 1 #elif 0 /* * Hypothetical platform with no float or int64 support */ typedef int khronos_int32_t; typedef unsigned int khronos_uint32_t; #define KHRONOS_SUPPORT_INT64 0 #define KHRONOS_SUPPORT_FLOAT 0 #else /* * Generic fallback */ #include typedef int32_t khronos_int32_t; typedef uint32_t khronos_uint32_t; typedef int64_t khronos_int64_t; typedef uint64_t khronos_uint64_t; #define KHRONOS_SUPPORT_INT64 1 #define KHRONOS_SUPPORT_FLOAT 1 #endif /* * Types that are (so far) the same on all platforms */ typedef signed char khronos_int8_t; typedef unsigned char khronos_uint8_t; typedef signed short int khronos_int16_t; typedef unsigned short int khronos_uint16_t; /* * Types that differ between LLP64 and LP64 architectures - in LLP64, * pointers are 64 bits, but 'long' is still 32 bits. Win64 appears * to be the only LLP64 architecture in current use. */ #ifdef _WIN64 typedef signed long long int khronos_intptr_t; typedef unsigned long long int khronos_uintptr_t; typedef signed long long int khronos_ssize_t; typedef unsigned long long int khronos_usize_t; #else typedef signed long int khronos_intptr_t; typedef unsigned long int khronos_uintptr_t; typedef signed long int khronos_ssize_t; typedef unsigned long int khronos_usize_t; #endif #if KHRONOS_SUPPORT_FLOAT /* * Float type */ typedef float khronos_float_t; #endif #if KHRONOS_SUPPORT_INT64 /* Time types * * These types can be used to represent a time interval in nanoseconds or * an absolute Unadjusted System Time. Unadjusted System Time is the number * of nanoseconds since some arbitrary system event (e.g. since the last * time the system booted). The Unadjusted System Time is an unsigned * 64 bit value that wraps back to 0 every 584 years. Time intervals * may be either signed or unsigned. */ typedef khronos_uint64_t khronos_utime_nanoseconds_t; typedef khronos_int64_t khronos_stime_nanoseconds_t; #endif /* * Dummy value used to pad enum types to 32 bits. */ #ifndef KHRONOS_MAX_ENUM #define KHRONOS_MAX_ENUM 0x7FFFFFFF #endif /* * Enumerated boolean type * * Values other than zero should be considered to be true. Therefore * comparisons should not be made against KHRONOS_TRUE. */ typedef enum { KHRONOS_FALSE = 0, KHRONOS_TRUE = 1, KHRONOS_BOOLEAN_ENUM_FORCE_SIZE = KHRONOS_MAX_ENUM } khronos_boolean_enum_t; #endif /* __khrplatform_h_ */ typedef unsigned int GLenum; typedef unsigned char GLboolean; typedef unsigned int GLbitfield; typedef void GLvoid; typedef khronos_int8_t GLbyte; typedef khronos_uint8_t GLubyte; typedef khronos_int16_t GLshort; typedef khronos_uint16_t GLushort; typedef int GLint; typedef unsigned int GLuint; typedef khronos_int32_t GLclampx; typedef int GLsizei; typedef khronos_float_t GLfloat; typedef khronos_float_t GLclampf; typedef double GLdouble; typedef double GLclampd; typedef void *GLeglClientBufferEXT; typedef void *GLeglImageOES; typedef char GLchar; typedef char GLcharARB; #ifdef __APPLE__ typedef void *GLhandleARB; #else typedef unsigned int GLhandleARB; #endif typedef khronos_uint16_t GLhalf; typedef khronos_uint16_t GLhalfARB; typedef khronos_int32_t GLfixed; #if defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) && (__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ > 1060) typedef khronos_intptr_t GLintptr; #else typedef khronos_intptr_t GLintptr; #endif #if defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) && (__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ > 1060) typedef khronos_intptr_t GLintptrARB; #else typedef khronos_intptr_t GLintptrARB; #endif #if defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) && (__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ > 1060) typedef khronos_ssize_t GLsizeiptr; #else typedef khronos_ssize_t GLsizeiptr; #endif #if defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) && (__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ > 1060) typedef khronos_ssize_t GLsizeiptrARB; #else typedef khronos_ssize_t GLsizeiptrARB; #endif typedef khronos_int64_t GLint64; typedef khronos_int64_t GLint64EXT; typedef khronos_uint64_t GLuint64; typedef khronos_uint64_t GLuint64EXT; typedef struct __GLsync *GLsync; struct _cl_context; struct _cl_event; typedef void (GLAD_API_PTR *GLDEBUGPROC)(GLenum source,GLenum type,GLuint id,GLenum severity,GLsizei length,const GLchar *message,const void *userParam); typedef void (GLAD_API_PTR *GLDEBUGPROCARB)(GLenum source,GLenum type,GLuint id,GLenum severity,GLsizei length,const GLchar *message,const void *userParam); typedef void (GLAD_API_PTR *GLDEBUGPROCKHR)(GLenum source,GLenum type,GLuint id,GLenum severity,GLsizei length,const GLchar *message,const void *userParam); typedef void (GLAD_API_PTR *GLDEBUGPROCAMD)(GLuint id,GLenum category,GLenum severity,GLsizei length,const GLchar *message,void *userParam); typedef unsigned short GLhalfNV; typedef GLintptr GLvdpauSurfaceNV; typedef void (GLAD_API_PTR *GLVULKANPROCNV)(void); #define GL_VERSION_1_0 1 GLAD_API_CALL int GLAD_GL_VERSION_1_0; #define GL_VERSION_1_1 1 GLAD_API_CALL int GLAD_GL_VERSION_1_1; #define GL_VERSION_1_2 1 GLAD_API_CALL int GLAD_GL_VERSION_1_2; #define GL_VERSION_1_3 1 GLAD_API_CALL int GLAD_GL_VERSION_1_3; #define GL_VERSION_1_4 1 GLAD_API_CALL int GLAD_GL_VERSION_1_4; #define GL_VERSION_1_5 1 GLAD_API_CALL int GLAD_GL_VERSION_1_5; #define GL_VERSION_2_0 1 GLAD_API_CALL int GLAD_GL_VERSION_2_0; #define GL_VERSION_2_1 1 GLAD_API_CALL int GLAD_GL_VERSION_2_1; #define GL_VERSION_3_0 1 GLAD_API_CALL int GLAD_GL_VERSION_3_0; #define GL_VERSION_3_1 1 GLAD_API_CALL int GLAD_GL_VERSION_3_1; #define GL_VERSION_3_2 1 GLAD_API_CALL int GLAD_GL_VERSION_3_2; #define GL_VERSION_3_3 1 GLAD_API_CALL int GLAD_GL_VERSION_3_3; #define GL_ARB_ES2_compatibility 1 GLAD_API_CALL int GLAD_GL_ARB_ES2_compatibility; #define GL_ARB_ES3_1_compatibility 1 GLAD_API_CALL int GLAD_GL_ARB_ES3_1_compatibility; #define GL_ARB_ES3_2_compatibility 1 GLAD_API_CALL int GLAD_GL_ARB_ES3_2_compatibility; #define GL_ARB_ES3_compatibility 1 GLAD_API_CALL int GLAD_GL_ARB_ES3_compatibility; #define GL_ARB_arrays_of_arrays 1 GLAD_API_CALL int GLAD_GL_ARB_arrays_of_arrays; #define GL_ARB_base_instance 1 GLAD_API_CALL int GLAD_GL_ARB_base_instance; #define GL_ARB_bindless_texture 1 GLAD_API_CALL int GLAD_GL_ARB_bindless_texture; #define GL_ARB_blend_func_extended 1 GLAD_API_CALL int GLAD_GL_ARB_blend_func_extended; #define GL_ARB_buffer_storage 1 GLAD_API_CALL int GLAD_GL_ARB_buffer_storage; #define GL_ARB_cl_event 1 GLAD_API_CALL int GLAD_GL_ARB_cl_event; #define GL_ARB_clear_buffer_object 1 GLAD_API_CALL int GLAD_GL_ARB_clear_buffer_object; #define GL_ARB_clear_texture 1 GLAD_API_CALL int GLAD_GL_ARB_clear_texture; #define GL_ARB_clip_control 1 GLAD_API_CALL int GLAD_GL_ARB_clip_control; #define GL_ARB_color_buffer_float 1 GLAD_API_CALL int GLAD_GL_ARB_color_buffer_float; #define GL_ARB_compatibility 1 GLAD_API_CALL int GLAD_GL_ARB_compatibility; #define GL_ARB_compressed_texture_pixel_storage 1 GLAD_API_CALL int GLAD_GL_ARB_compressed_texture_pixel_storage; #define GL_ARB_compute_shader 1 GLAD_API_CALL int GLAD_GL_ARB_compute_shader; #define GL_ARB_compute_variable_group_size 1 GLAD_API_CALL int GLAD_GL_ARB_compute_variable_group_size; #define GL_ARB_conditional_render_inverted 1 GLAD_API_CALL int GLAD_GL_ARB_conditional_render_inverted; #define GL_ARB_conservative_depth 1 GLAD_API_CALL int GLAD_GL_ARB_conservative_depth; #define GL_ARB_copy_buffer 1 GLAD_API_CALL int GLAD_GL_ARB_copy_buffer; #define GL_ARB_copy_image 1 GLAD_API_CALL int GLAD_GL_ARB_copy_image; #define GL_ARB_cull_distance 1 GLAD_API_CALL int GLAD_GL_ARB_cull_distance; #define GL_ARB_debug_output 1 GLAD_API_CALL int GLAD_GL_ARB_debug_output; #define GL_ARB_depth_buffer_float 1 GLAD_API_CALL int GLAD_GL_ARB_depth_buffer_float; #define GL_ARB_depth_clamp 1 GLAD_API_CALL int GLAD_GL_ARB_depth_clamp; #define GL_ARB_depth_texture 1 GLAD_API_CALL int GLAD_GL_ARB_depth_texture; #define GL_ARB_derivative_control 1 GLAD_API_CALL int GLAD_GL_ARB_derivative_control; #define GL_ARB_direct_state_access 1 GLAD_API_CALL int GLAD_GL_ARB_direct_state_access; #define GL_ARB_draw_buffers 1 GLAD_API_CALL int GLAD_GL_ARB_draw_buffers; #define GL_ARB_draw_buffers_blend 1 GLAD_API_CALL int GLAD_GL_ARB_draw_buffers_blend; #define GL_ARB_draw_elements_base_vertex 1 GLAD_API_CALL int GLAD_GL_ARB_draw_elements_base_vertex; #define GL_ARB_draw_indirect 1 GLAD_API_CALL int GLAD_GL_ARB_draw_indirect; #define GL_ARB_draw_instanced 1 GLAD_API_CALL int GLAD_GL_ARB_draw_instanced; #define GL_ARB_enhanced_layouts 1 GLAD_API_CALL int GLAD_GL_ARB_enhanced_layouts; #define GL_ARB_explicit_attrib_location 1 GLAD_API_CALL int GLAD_GL_ARB_explicit_attrib_location; #define GL_ARB_explicit_uniform_location 1 GLAD_API_CALL int GLAD_GL_ARB_explicit_uniform_location; #define GL_ARB_fragment_coord_conventions 1 GLAD_API_CALL int GLAD_GL_ARB_fragment_coord_conventions; #define GL_ARB_fragment_layer_viewport 1 GLAD_API_CALL int GLAD_GL_ARB_fragment_layer_viewport; #define GL_ARB_fragment_program 1 GLAD_API_CALL int GLAD_GL_ARB_fragment_program; #define GL_ARB_fragment_program_shadow 1 GLAD_API_CALL int GLAD_GL_ARB_fragment_program_shadow; #define GL_ARB_fragment_shader 1 GLAD_API_CALL int GLAD_GL_ARB_fragment_shader; #define GL_ARB_fragment_shader_interlock 1 GLAD_API_CALL int GLAD_GL_ARB_fragment_shader_interlock; #define GL_ARB_framebuffer_no_attachments 1 GLAD_API_CALL int GLAD_GL_ARB_framebuffer_no_attachments; #define GL_ARB_framebuffer_object 1 GLAD_API_CALL int GLAD_GL_ARB_framebuffer_object; #define GL_ARB_framebuffer_sRGB 1 GLAD_API_CALL int GLAD_GL_ARB_framebuffer_sRGB; #define GL_ARB_geometry_shader4 1 GLAD_API_CALL int GLAD_GL_ARB_geometry_shader4; #define GL_ARB_get_program_binary 1 GLAD_API_CALL int GLAD_GL_ARB_get_program_binary; #define GL_ARB_get_texture_sub_image 1 GLAD_API_CALL int GLAD_GL_ARB_get_texture_sub_image; #define GL_ARB_gl_spirv 1 GLAD_API_CALL int GLAD_GL_ARB_gl_spirv; #define GL_ARB_gpu_shader5 1 GLAD_API_CALL int GLAD_GL_ARB_gpu_shader5; #define GL_ARB_gpu_shader_fp64 1 GLAD_API_CALL int GLAD_GL_ARB_gpu_shader_fp64; #define GL_ARB_gpu_shader_int64 1 GLAD_API_CALL int GLAD_GL_ARB_gpu_shader_int64; #define GL_ARB_half_float_pixel 1 GLAD_API_CALL int GLAD_GL_ARB_half_float_pixel; #define GL_ARB_half_float_vertex 1 GLAD_API_CALL int GLAD_GL_ARB_half_float_vertex; #define GL_ARB_imaging 1 GLAD_API_CALL int GLAD_GL_ARB_imaging; #define GL_ARB_indirect_parameters 1 GLAD_API_CALL int GLAD_GL_ARB_indirect_parameters; #define GL_ARB_instanced_arrays 1 GLAD_API_CALL int GLAD_GL_ARB_instanced_arrays; #define GL_ARB_internalformat_query 1 GLAD_API_CALL int GLAD_GL_ARB_internalformat_query; #define GL_ARB_internalformat_query2 1 GLAD_API_CALL int GLAD_GL_ARB_internalformat_query2; #define GL_ARB_invalidate_subdata 1 GLAD_API_CALL int GLAD_GL_ARB_invalidate_subdata; #define GL_ARB_map_buffer_alignment 1 GLAD_API_CALL int GLAD_GL_ARB_map_buffer_alignment; #define GL_ARB_map_buffer_range 1 GLAD_API_CALL int GLAD_GL_ARB_map_buffer_range; #define GL_ARB_matrix_palette 1 GLAD_API_CALL int GLAD_GL_ARB_matrix_palette; #define GL_ARB_multi_bind 1 GLAD_API_CALL int GLAD_GL_ARB_multi_bind; #define GL_ARB_multi_draw_indirect 1 GLAD_API_CALL int GLAD_GL_ARB_multi_draw_indirect; #define GL_ARB_multisample 1 GLAD_API_CALL int GLAD_GL_ARB_multisample; #define GL_ARB_multitexture 1 GLAD_API_CALL int GLAD_GL_ARB_multitexture; #define GL_ARB_occlusion_query 1 GLAD_API_CALL int GLAD_GL_ARB_occlusion_query; #define GL_ARB_occlusion_query2 1 GLAD_API_CALL int GLAD_GL_ARB_occlusion_query2; #define GL_ARB_parallel_shader_compile 1 GLAD_API_CALL int GLAD_GL_ARB_parallel_shader_compile; #define GL_ARB_pipeline_statistics_query 1 GLAD_API_CALL int GLAD_GL_ARB_pipeline_statistics_query; #define GL_ARB_pixel_buffer_object 1 GLAD_API_CALL int GLAD_GL_ARB_pixel_buffer_object; #define GL_ARB_point_parameters 1 GLAD_API_CALL int GLAD_GL_ARB_point_parameters; #define GL_ARB_point_sprite 1 GLAD_API_CALL int GLAD_GL_ARB_point_sprite; #define GL_ARB_polygon_offset_clamp 1 GLAD_API_CALL int GLAD_GL_ARB_polygon_offset_clamp; #define GL_ARB_post_depth_coverage 1 GLAD_API_CALL int GLAD_GL_ARB_post_depth_coverage; #define GL_ARB_program_interface_query 1 GLAD_API_CALL int GLAD_GL_ARB_program_interface_query; #define GL_ARB_provoking_vertex 1 GLAD_API_CALL int GLAD_GL_ARB_provoking_vertex; #define GL_ARB_query_buffer_object 1 GLAD_API_CALL int GLAD_GL_ARB_query_buffer_object; #define GL_ARB_robust_buffer_access_behavior 1 GLAD_API_CALL int GLAD_GL_ARB_robust_buffer_access_behavior; #define GL_ARB_robustness 1 GLAD_API_CALL int GLAD_GL_ARB_robustness; #define GL_ARB_robustness_isolation 1 GLAD_API_CALL int GLAD_GL_ARB_robustness_isolation; #define GL_ARB_sample_locations 1 GLAD_API_CALL int GLAD_GL_ARB_sample_locations; #define GL_ARB_sample_shading 1 GLAD_API_CALL int GLAD_GL_ARB_sample_shading; #define GL_ARB_sampler_objects 1 GLAD_API_CALL int GLAD_GL_ARB_sampler_objects; #define GL_ARB_seamless_cube_map 1 GLAD_API_CALL int GLAD_GL_ARB_seamless_cube_map; #define GL_ARB_seamless_cubemap_per_texture 1 GLAD_API_CALL int GLAD_GL_ARB_seamless_cubemap_per_texture; #define GL_ARB_separate_shader_objects 1 GLAD_API_CALL int GLAD_GL_ARB_separate_shader_objects; #define GL_ARB_shader_atomic_counter_ops 1 GLAD_API_CALL int GLAD_GL_ARB_shader_atomic_counter_ops; #define GL_ARB_shader_atomic_counters 1 GLAD_API_CALL int GLAD_GL_ARB_shader_atomic_counters; #define GL_ARB_shader_ballot 1 GLAD_API_CALL int GLAD_GL_ARB_shader_ballot; #define GL_ARB_shader_bit_encoding 1 GLAD_API_CALL int GLAD_GL_ARB_shader_bit_encoding; #define GL_ARB_shader_clock 1 GLAD_API_CALL int GLAD_GL_ARB_shader_clock; #define GL_ARB_shader_draw_parameters 1 GLAD_API_CALL int GLAD_GL_ARB_shader_draw_parameters; #define GL_ARB_shader_group_vote 1 GLAD_API_CALL int GLAD_GL_ARB_shader_group_vote; #define GL_ARB_shader_image_load_store 1 GLAD_API_CALL int GLAD_GL_ARB_shader_image_load_store; #define GL_ARB_shader_image_size 1 GLAD_API_CALL int GLAD_GL_ARB_shader_image_size; #define GL_ARB_shader_objects 1 GLAD_API_CALL int GLAD_GL_ARB_shader_objects; #define GL_ARB_shader_precision 1 GLAD_API_CALL int GLAD_GL_ARB_shader_precision; #define GL_ARB_shader_stencil_export 1 GLAD_API_CALL int GLAD_GL_ARB_shader_stencil_export; #define GL_ARB_shader_storage_buffer_object 1 GLAD_API_CALL int GLAD_GL_ARB_shader_storage_buffer_object; #define GL_ARB_shader_subroutine 1 GLAD_API_CALL int GLAD_GL_ARB_shader_subroutine; #define GL_ARB_shader_texture_image_samples 1 GLAD_API_CALL int GLAD_GL_ARB_shader_texture_image_samples; #define GL_ARB_shader_texture_lod 1 GLAD_API_CALL int GLAD_GL_ARB_shader_texture_lod; #define GL_ARB_shader_viewport_layer_array 1 GLAD_API_CALL int GLAD_GL_ARB_shader_viewport_layer_array; #define GL_ARB_shading_language_100 1 GLAD_API_CALL int GLAD_GL_ARB_shading_language_100; #define GL_ARB_shading_language_420pack 1 GLAD_API_CALL int GLAD_GL_ARB_shading_language_420pack; #define GL_ARB_shading_language_include 1 GLAD_API_CALL int GLAD_GL_ARB_shading_language_include; #define GL_ARB_shading_language_packing 1 GLAD_API_CALL int GLAD_GL_ARB_shading_language_packing; #define GL_ARB_shadow 1 GLAD_API_CALL int GLAD_GL_ARB_shadow; #define GL_ARB_shadow_ambient 1 GLAD_API_CALL int GLAD_GL_ARB_shadow_ambient; #define GL_ARB_sparse_buffer 1 GLAD_API_CALL int GLAD_GL_ARB_sparse_buffer; #define GL_ARB_sparse_texture 1 GLAD_API_CALL int GLAD_GL_ARB_sparse_texture; #define GL_ARB_sparse_texture2 1 GLAD_API_CALL int GLAD_GL_ARB_sparse_texture2; #define GL_ARB_sparse_texture_clamp 1 GLAD_API_CALL int GLAD_GL_ARB_sparse_texture_clamp; #define GL_ARB_spirv_extensions 1 GLAD_API_CALL int GLAD_GL_ARB_spirv_extensions; #define GL_ARB_stencil_texturing 1 GLAD_API_CALL int GLAD_GL_ARB_stencil_texturing; #define GL_ARB_sync 1 GLAD_API_CALL int GLAD_GL_ARB_sync; #define GL_ARB_tessellation_shader 1 GLAD_API_CALL int GLAD_GL_ARB_tessellation_shader; #define GL_ARB_texture_barrier 1 GLAD_API_CALL int GLAD_GL_ARB_texture_barrier; #define GL_ARB_texture_border_clamp 1 GLAD_API_CALL int GLAD_GL_ARB_texture_border_clamp; #define GL_ARB_texture_buffer_object 1 GLAD_API_CALL int GLAD_GL_ARB_texture_buffer_object; #define GL_ARB_texture_buffer_object_rgb32 1 GLAD_API_CALL int GLAD_GL_ARB_texture_buffer_object_rgb32; #define GL_ARB_texture_buffer_range 1 GLAD_API_CALL int GLAD_GL_ARB_texture_buffer_range; #define GL_ARB_texture_compression 1 GLAD_API_CALL int GLAD_GL_ARB_texture_compression; #define GL_ARB_texture_compression_bptc 1 GLAD_API_CALL int GLAD_GL_ARB_texture_compression_bptc; #define GL_ARB_texture_compression_rgtc 1 GLAD_API_CALL int GLAD_GL_ARB_texture_compression_rgtc; #define GL_ARB_texture_cube_map 1 GLAD_API_CALL int GLAD_GL_ARB_texture_cube_map; #define GL_ARB_texture_cube_map_array 1 GLAD_API_CALL int GLAD_GL_ARB_texture_cube_map_array; #define GL_ARB_texture_env_add 1 GLAD_API_CALL int GLAD_GL_ARB_texture_env_add; #define GL_ARB_texture_env_combine 1 GLAD_API_CALL int GLAD_GL_ARB_texture_env_combine; #define GL_ARB_texture_env_crossbar 1 GLAD_API_CALL int GLAD_GL_ARB_texture_env_crossbar; #define GL_ARB_texture_env_dot3 1 GLAD_API_CALL int GLAD_GL_ARB_texture_env_dot3; #define GL_ARB_texture_filter_anisotropic 1 GLAD_API_CALL int GLAD_GL_ARB_texture_filter_anisotropic; #define GL_ARB_texture_filter_minmax 1 GLAD_API_CALL int GLAD_GL_ARB_texture_filter_minmax; #define GL_ARB_texture_float 1 GLAD_API_CALL int GLAD_GL_ARB_texture_float; #define GL_ARB_texture_gather 1 GLAD_API_CALL int GLAD_GL_ARB_texture_gather; #define GL_ARB_texture_mirror_clamp_to_edge 1 GLAD_API_CALL int GLAD_GL_ARB_texture_mirror_clamp_to_edge; #define GL_ARB_texture_mirrored_repeat 1 GLAD_API_CALL int GLAD_GL_ARB_texture_mirrored_repeat; #define GL_ARB_texture_multisample 1 GLAD_API_CALL int GLAD_GL_ARB_texture_multisample; #define GL_ARB_texture_non_power_of_two 1 GLAD_API_CALL int GLAD_GL_ARB_texture_non_power_of_two; #define GL_ARB_texture_query_levels 1 GLAD_API_CALL int GLAD_GL_ARB_texture_query_levels; #define GL_ARB_texture_query_lod 1 GLAD_API_CALL int GLAD_GL_ARB_texture_query_lod; #define GL_ARB_texture_rectangle 1 GLAD_API_CALL int GLAD_GL_ARB_texture_rectangle; #define GL_ARB_texture_rg 1 GLAD_API_CALL int GLAD_GL_ARB_texture_rg; #define GL_ARB_texture_rgb10_a2ui 1 GLAD_API_CALL int GLAD_GL_ARB_texture_rgb10_a2ui; #define GL_ARB_texture_stencil8 1 GLAD_API_CALL int GLAD_GL_ARB_texture_stencil8; #define GL_ARB_texture_storage 1 GLAD_API_CALL int GLAD_GL_ARB_texture_storage; #define GL_ARB_texture_storage_multisample 1 GLAD_API_CALL int GLAD_GL_ARB_texture_storage_multisample; #define GL_ARB_texture_swizzle 1 GLAD_API_CALL int GLAD_GL_ARB_texture_swizzle; #define GL_ARB_texture_view 1 GLAD_API_CALL int GLAD_GL_ARB_texture_view; #define GL_ARB_timer_query 1 GLAD_API_CALL int GLAD_GL_ARB_timer_query; #define GL_ARB_transform_feedback2 1 GLAD_API_CALL int GLAD_GL_ARB_transform_feedback2; #define GL_ARB_transform_feedback3 1 GLAD_API_CALL int GLAD_GL_ARB_transform_feedback3; #define GL_ARB_transform_feedback_instanced 1 GLAD_API_CALL int GLAD_GL_ARB_transform_feedback_instanced; #define GL_ARB_transform_feedback_overflow_query 1 GLAD_API_CALL int GLAD_GL_ARB_transform_feedback_overflow_query; #define GL_ARB_transpose_matrix 1 GLAD_API_CALL int GLAD_GL_ARB_transpose_matrix; #define GL_ARB_uniform_buffer_object 1 GLAD_API_CALL int GLAD_GL_ARB_uniform_buffer_object; #define GL_ARB_vertex_array_bgra 1 GLAD_API_CALL int GLAD_GL_ARB_vertex_array_bgra; #define GL_ARB_vertex_array_object 1 GLAD_API_CALL int GLAD_GL_ARB_vertex_array_object; #define GL_ARB_vertex_attrib_64bit 1 GLAD_API_CALL int GLAD_GL_ARB_vertex_attrib_64bit; #define GL_ARB_vertex_attrib_binding 1 GLAD_API_CALL int GLAD_GL_ARB_vertex_attrib_binding; #define GL_ARB_vertex_blend 1 GLAD_API_CALL int GLAD_GL_ARB_vertex_blend; #define GL_ARB_vertex_buffer_object 1 GLAD_API_CALL int GLAD_GL_ARB_vertex_buffer_object; #define GL_ARB_vertex_program 1 GLAD_API_CALL int GLAD_GL_ARB_vertex_program; #define GL_ARB_vertex_shader 1 GLAD_API_CALL int GLAD_GL_ARB_vertex_shader; #define GL_ARB_vertex_type_10f_11f_11f_rev 1 GLAD_API_CALL int GLAD_GL_ARB_vertex_type_10f_11f_11f_rev; #define GL_ARB_vertex_type_2_10_10_10_rev 1 GLAD_API_CALL int GLAD_GL_ARB_vertex_type_2_10_10_10_rev; #define GL_ARB_viewport_array 1 GLAD_API_CALL int GLAD_GL_ARB_viewport_array; #define GL_ARB_window_pos 1 GLAD_API_CALL int GLAD_GL_ARB_window_pos; #define GL_KHR_blend_equation_advanced 1 GLAD_API_CALL int GLAD_GL_KHR_blend_equation_advanced; #define GL_KHR_blend_equation_advanced_coherent 1 GLAD_API_CALL int GLAD_GL_KHR_blend_equation_advanced_coherent; #define GL_KHR_context_flush_control 1 GLAD_API_CALL int GLAD_GL_KHR_context_flush_control; #define GL_KHR_debug 1 GLAD_API_CALL int GLAD_GL_KHR_debug; #define GL_KHR_no_error 1 GLAD_API_CALL int GLAD_GL_KHR_no_error; #define GL_KHR_parallel_shader_compile 1 GLAD_API_CALL int GLAD_GL_KHR_parallel_shader_compile; #define GL_KHR_robust_buffer_access_behavior 1 GLAD_API_CALL int GLAD_GL_KHR_robust_buffer_access_behavior; #define GL_KHR_robustness 1 GLAD_API_CALL int GLAD_GL_KHR_robustness; #define GL_KHR_shader_subgroup 1 GLAD_API_CALL int GLAD_GL_KHR_shader_subgroup; #define GL_KHR_texture_compression_astc_hdr 1 GLAD_API_CALL int GLAD_GL_KHR_texture_compression_astc_hdr; #define GL_KHR_texture_compression_astc_ldr 1 GLAD_API_CALL int GLAD_GL_KHR_texture_compression_astc_ldr; #define GL_KHR_texture_compression_astc_sliced_3d 1 GLAD_API_CALL int GLAD_GL_KHR_texture_compression_astc_sliced_3d; typedef void (GLAD_API_PTR *PFNGLACCUMPROC)(GLenum op, GLfloat value); typedef void (GLAD_API_PTR *PFNGLACTIVESHADERPROGRAMPROC)(GLuint pipeline, GLuint program); typedef void (GLAD_API_PTR *PFNGLACTIVETEXTUREPROC)(GLenum texture); typedef void (GLAD_API_PTR *PFNGLACTIVETEXTUREARBPROC)(GLenum texture); typedef void (GLAD_API_PTR *PFNGLALPHAFUNCPROC)(GLenum func, GLfloat ref); typedef GLboolean (GLAD_API_PTR *PFNGLARETEXTURESRESIDENTPROC)(GLsizei n, const GLuint * textures, GLboolean * residences); typedef void (GLAD_API_PTR *PFNGLARRAYELEMENTPROC)(GLint i); typedef void (GLAD_API_PTR *PFNGLATTACHOBJECTARBPROC)(GLhandleARB containerObj, GLhandleARB obj); typedef void (GLAD_API_PTR *PFNGLATTACHSHADERPROC)(GLuint program, GLuint shader); typedef void (GLAD_API_PTR *PFNGLBEGINPROC)(GLenum mode); typedef void (GLAD_API_PTR *PFNGLBEGINCONDITIONALRENDERPROC)(GLuint id, GLenum mode); typedef void (GLAD_API_PTR *PFNGLBEGINQUERYPROC)(GLenum target, GLuint id); typedef void (GLAD_API_PTR *PFNGLBEGINQUERYARBPROC)(GLenum target, GLuint id); typedef void (GLAD_API_PTR *PFNGLBEGINQUERYINDEXEDPROC)(GLenum target, GLuint index, GLuint id); typedef void (GLAD_API_PTR *PFNGLBEGINTRANSFORMFEEDBACKPROC)(GLenum primitiveMode); typedef void (GLAD_API_PTR *PFNGLBINDATTRIBLOCATIONPROC)(GLuint program, GLuint index, const GLchar * name); typedef void (GLAD_API_PTR *PFNGLBINDATTRIBLOCATIONARBPROC)(GLhandleARB programObj, GLuint index, const GLcharARB * name); typedef void (GLAD_API_PTR *PFNGLBINDBUFFERPROC)(GLenum target, GLuint buffer); typedef void (GLAD_API_PTR *PFNGLBINDBUFFERARBPROC)(GLenum target, GLuint buffer); typedef void (GLAD_API_PTR *PFNGLBINDBUFFERBASEPROC)(GLenum target, GLuint index, GLuint buffer); typedef void (GLAD_API_PTR *PFNGLBINDBUFFERRANGEPROC)(GLenum target, GLuint index, GLuint buffer, GLintptr offset, GLsizeiptr size); typedef void (GLAD_API_PTR *PFNGLBINDBUFFERSBASEPROC)(GLenum target, GLuint first, GLsizei count, const GLuint * buffers); typedef void (GLAD_API_PTR *PFNGLBINDBUFFERSRANGEPROC)(GLenum target, GLuint first, GLsizei count, const GLuint * buffers, const GLintptr * offsets, const GLsizeiptr * sizes); typedef void (GLAD_API_PTR *PFNGLBINDFRAGDATALOCATIONPROC)(GLuint program, GLuint color, const GLchar * name); typedef void (GLAD_API_PTR *PFNGLBINDFRAGDATALOCATIONINDEXEDPROC)(GLuint program, GLuint colorNumber, GLuint index, const GLchar * name); typedef void (GLAD_API_PTR *PFNGLBINDFRAMEBUFFERPROC)(GLenum target, GLuint framebuffer); typedef void (GLAD_API_PTR *PFNGLBINDIMAGETEXTUREPROC)(GLuint unit, GLuint texture, GLint level, GLboolean layered, GLint layer, GLenum access, GLenum format); typedef void (GLAD_API_PTR *PFNGLBINDIMAGETEXTURESPROC)(GLuint first, GLsizei count, const GLuint * textures); typedef void (GLAD_API_PTR *PFNGLBINDPROGRAMARBPROC)(GLenum target, GLuint program); typedef void (GLAD_API_PTR *PFNGLBINDPROGRAMPIPELINEPROC)(GLuint pipeline); typedef void (GLAD_API_PTR *PFNGLBINDRENDERBUFFERPROC)(GLenum target, GLuint renderbuffer); typedef void (GLAD_API_PTR *PFNGLBINDSAMPLERPROC)(GLuint unit, GLuint sampler); typedef void (GLAD_API_PTR *PFNGLBINDSAMPLERSPROC)(GLuint first, GLsizei count, const GLuint * samplers); typedef void (GLAD_API_PTR *PFNGLBINDTEXTUREPROC)(GLenum target, GLuint texture); typedef void (GLAD_API_PTR *PFNGLBINDTEXTUREUNITPROC)(GLuint unit, GLuint texture); typedef void (GLAD_API_PTR *PFNGLBINDTEXTURESPROC)(GLuint first, GLsizei count, const GLuint * textures); typedef void (GLAD_API_PTR *PFNGLBINDTRANSFORMFEEDBACKPROC)(GLenum target, GLuint id); typedef void (GLAD_API_PTR *PFNGLBINDVERTEXARRAYPROC)(GLuint array); typedef void (GLAD_API_PTR *PFNGLBINDVERTEXBUFFERPROC)(GLuint bindingindex, GLuint buffer, GLintptr offset, GLsizei stride); typedef void (GLAD_API_PTR *PFNGLBINDVERTEXBUFFERSPROC)(GLuint first, GLsizei count, const GLuint * buffers, const GLintptr * offsets, const GLsizei * strides); typedef void (GLAD_API_PTR *PFNGLBITMAPPROC)(GLsizei width, GLsizei height, GLfloat xorig, GLfloat yorig, GLfloat xmove, GLfloat ymove, const GLubyte * bitmap); typedef void (GLAD_API_PTR *PFNGLBLENDBARRIERPROC)(void); typedef void (GLAD_API_PTR *PFNGLBLENDBARRIERKHRPROC)(void); typedef void (GLAD_API_PTR *PFNGLBLENDCOLORPROC)(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha); typedef void (GLAD_API_PTR *PFNGLBLENDEQUATIONPROC)(GLenum mode); typedef void (GLAD_API_PTR *PFNGLBLENDEQUATIONSEPARATEPROC)(GLenum modeRGB, GLenum modeAlpha); typedef void (GLAD_API_PTR *PFNGLBLENDEQUATIONSEPARATEIPROC)(GLuint buf, GLenum modeRGB, GLenum modeAlpha); typedef void (GLAD_API_PTR *PFNGLBLENDEQUATIONSEPARATEIARBPROC)(GLuint buf, GLenum modeRGB, GLenum modeAlpha); typedef void (GLAD_API_PTR *PFNGLBLENDEQUATIONIPROC)(GLuint buf, GLenum mode); typedef void (GLAD_API_PTR *PFNGLBLENDEQUATIONIARBPROC)(GLuint buf, GLenum mode); typedef void (GLAD_API_PTR *PFNGLBLENDFUNCPROC)(GLenum sfactor, GLenum dfactor); typedef void (GLAD_API_PTR *PFNGLBLENDFUNCSEPARATEPROC)(GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha, GLenum dfactorAlpha); typedef void (GLAD_API_PTR *PFNGLBLENDFUNCSEPARATEIPROC)(GLuint buf, GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha); typedef void (GLAD_API_PTR *PFNGLBLENDFUNCSEPARATEIARBPROC)(GLuint buf, GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha); typedef void (GLAD_API_PTR *PFNGLBLENDFUNCIPROC)(GLuint buf, GLenum src, GLenum dst); typedef void (GLAD_API_PTR *PFNGLBLENDFUNCIARBPROC)(GLuint buf, GLenum src, GLenum dst); typedef void (GLAD_API_PTR *PFNGLBLITFRAMEBUFFERPROC)(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter); typedef void (GLAD_API_PTR *PFNGLBLITNAMEDFRAMEBUFFERPROC)(GLuint readFramebuffer, GLuint drawFramebuffer, GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter); typedef void (GLAD_API_PTR *PFNGLBUFFERDATAPROC)(GLenum target, GLsizeiptr size, const void * data, GLenum usage); typedef void (GLAD_API_PTR *PFNGLBUFFERDATAARBPROC)(GLenum target, GLsizeiptrARB size, const void * data, GLenum usage); typedef void (GLAD_API_PTR *PFNGLBUFFERPAGECOMMITMENTARBPROC)(GLenum target, GLintptr offset, GLsizeiptr size, GLboolean commit); typedef void (GLAD_API_PTR *PFNGLBUFFERSTORAGEPROC)(GLenum target, GLsizeiptr size, const void * data, GLbitfield flags); typedef void (GLAD_API_PTR *PFNGLBUFFERSUBDATAPROC)(GLenum target, GLintptr offset, GLsizeiptr size, const void * data); typedef void (GLAD_API_PTR *PFNGLBUFFERSUBDATAARBPROC)(GLenum target, GLintptrARB offset, GLsizeiptrARB size, const void * data); typedef void (GLAD_API_PTR *PFNGLCALLLISTPROC)(GLuint list); typedef void (GLAD_API_PTR *PFNGLCALLLISTSPROC)(GLsizei n, GLenum type, const void * lists); typedef GLenum (GLAD_API_PTR *PFNGLCHECKFRAMEBUFFERSTATUSPROC)(GLenum target); typedef GLenum (GLAD_API_PTR *PFNGLCHECKNAMEDFRAMEBUFFERSTATUSPROC)(GLuint framebuffer, GLenum target); typedef void (GLAD_API_PTR *PFNGLCLAMPCOLORPROC)(GLenum target, GLenum clamp); typedef void (GLAD_API_PTR *PFNGLCLAMPCOLORARBPROC)(GLenum target, GLenum clamp); typedef void (GLAD_API_PTR *PFNGLCLEARPROC)(GLbitfield mask); typedef void (GLAD_API_PTR *PFNGLCLEARACCUMPROC)(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha); typedef void (GLAD_API_PTR *PFNGLCLEARBUFFERDATAPROC)(GLenum target, GLenum internalformat, GLenum format, GLenum type, const void * data); typedef void (GLAD_API_PTR *PFNGLCLEARBUFFERSUBDATAPROC)(GLenum target, GLenum internalformat, GLintptr offset, GLsizeiptr size, GLenum format, GLenum type, const void * data); typedef void (GLAD_API_PTR *PFNGLCLEARBUFFERFIPROC)(GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil); typedef void (GLAD_API_PTR *PFNGLCLEARBUFFERFVPROC)(GLenum buffer, GLint drawbuffer, const GLfloat * value); typedef void (GLAD_API_PTR *PFNGLCLEARBUFFERIVPROC)(GLenum buffer, GLint drawbuffer, const GLint * value); typedef void (GLAD_API_PTR *PFNGLCLEARBUFFERUIVPROC)(GLenum buffer, GLint drawbuffer, const GLuint * value); typedef void (GLAD_API_PTR *PFNGLCLEARCOLORPROC)(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha); typedef void (GLAD_API_PTR *PFNGLCLEARDEPTHPROC)(GLdouble depth); typedef void (GLAD_API_PTR *PFNGLCLEARDEPTHFPROC)(GLfloat d); typedef void (GLAD_API_PTR *PFNGLCLEARINDEXPROC)(GLfloat c); typedef void (GLAD_API_PTR *PFNGLCLEARNAMEDBUFFERDATAPROC)(GLuint buffer, GLenum internalformat, GLenum format, GLenum type, const void * data); typedef void (GLAD_API_PTR *PFNGLCLEARNAMEDBUFFERSUBDATAPROC)(GLuint buffer, GLenum internalformat, GLintptr offset, GLsizeiptr size, GLenum format, GLenum type, const void * data); typedef void (GLAD_API_PTR *PFNGLCLEARNAMEDFRAMEBUFFERFIPROC)(GLuint framebuffer, GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil); typedef void (GLAD_API_PTR *PFNGLCLEARNAMEDFRAMEBUFFERFVPROC)(GLuint framebuffer, GLenum buffer, GLint drawbuffer, const GLfloat * value); typedef void (GLAD_API_PTR *PFNGLCLEARNAMEDFRAMEBUFFERIVPROC)(GLuint framebuffer, GLenum buffer, GLint drawbuffer, const GLint * value); typedef void (GLAD_API_PTR *PFNGLCLEARNAMEDFRAMEBUFFERUIVPROC)(GLuint framebuffer, GLenum buffer, GLint drawbuffer, const GLuint * value); typedef void (GLAD_API_PTR *PFNGLCLEARSTENCILPROC)(GLint s); typedef void (GLAD_API_PTR *PFNGLCLEARTEXIMAGEPROC)(GLuint texture, GLint level, GLenum format, GLenum type, const void * data); typedef void (GLAD_API_PTR *PFNGLCLEARTEXSUBIMAGEPROC)(GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void * data); typedef void (GLAD_API_PTR *PFNGLCLIENTACTIVETEXTUREPROC)(GLenum texture); typedef void (GLAD_API_PTR *PFNGLCLIENTACTIVETEXTUREARBPROC)(GLenum texture); typedef GLenum (GLAD_API_PTR *PFNGLCLIENTWAITSYNCPROC)(GLsync sync, GLbitfield flags, GLuint64 timeout); typedef void (GLAD_API_PTR *PFNGLCLIPCONTROLPROC)(GLenum origin, GLenum depth); typedef void (GLAD_API_PTR *PFNGLCLIPPLANEPROC)(GLenum plane, const GLdouble * equation); typedef void (GLAD_API_PTR *PFNGLCOLOR3BPROC)(GLbyte red, GLbyte green, GLbyte blue); typedef void (GLAD_API_PTR *PFNGLCOLOR3BVPROC)(const GLbyte * v); typedef void (GLAD_API_PTR *PFNGLCOLOR3DPROC)(GLdouble red, GLdouble green, GLdouble blue); typedef void (GLAD_API_PTR *PFNGLCOLOR3DVPROC)(const GLdouble * v); typedef void (GLAD_API_PTR *PFNGLCOLOR3FPROC)(GLfloat red, GLfloat green, GLfloat blue); typedef void (GLAD_API_PTR *PFNGLCOLOR3FVPROC)(const GLfloat * v); typedef void (GLAD_API_PTR *PFNGLCOLOR3IPROC)(GLint red, GLint green, GLint blue); typedef void (GLAD_API_PTR *PFNGLCOLOR3IVPROC)(const GLint * v); typedef void (GLAD_API_PTR *PFNGLCOLOR3SPROC)(GLshort red, GLshort green, GLshort blue); typedef void (GLAD_API_PTR *PFNGLCOLOR3SVPROC)(const GLshort * v); typedef void (GLAD_API_PTR *PFNGLCOLOR3UBPROC)(GLubyte red, GLubyte green, GLubyte blue); typedef void (GLAD_API_PTR *PFNGLCOLOR3UBVPROC)(const GLubyte * v); typedef void (GLAD_API_PTR *PFNGLCOLOR3UIPROC)(GLuint red, GLuint green, GLuint blue); typedef void (GLAD_API_PTR *PFNGLCOLOR3UIVPROC)(const GLuint * v); typedef void (GLAD_API_PTR *PFNGLCOLOR3USPROC)(GLushort red, GLushort green, GLushort blue); typedef void (GLAD_API_PTR *PFNGLCOLOR3USVPROC)(const GLushort * v); typedef void (GLAD_API_PTR *PFNGLCOLOR4BPROC)(GLbyte red, GLbyte green, GLbyte blue, GLbyte alpha); typedef void (GLAD_API_PTR *PFNGLCOLOR4BVPROC)(const GLbyte * v); typedef void (GLAD_API_PTR *PFNGLCOLOR4DPROC)(GLdouble red, GLdouble green, GLdouble blue, GLdouble alpha); typedef void (GLAD_API_PTR *PFNGLCOLOR4DVPROC)(const GLdouble * v); typedef void (GLAD_API_PTR *PFNGLCOLOR4FPROC)(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha); typedef void (GLAD_API_PTR *PFNGLCOLOR4FVPROC)(const GLfloat * v); typedef void (GLAD_API_PTR *PFNGLCOLOR4IPROC)(GLint red, GLint green, GLint blue, GLint alpha); typedef void (GLAD_API_PTR *PFNGLCOLOR4IVPROC)(const GLint * v); typedef void (GLAD_API_PTR *PFNGLCOLOR4SPROC)(GLshort red, GLshort green, GLshort blue, GLshort alpha); typedef void (GLAD_API_PTR *PFNGLCOLOR4SVPROC)(const GLshort * v); typedef void (GLAD_API_PTR *PFNGLCOLOR4UBPROC)(GLubyte red, GLubyte green, GLubyte blue, GLubyte alpha); typedef void (GLAD_API_PTR *PFNGLCOLOR4UBVPROC)(const GLubyte * v); typedef void (GLAD_API_PTR *PFNGLCOLOR4UIPROC)(GLuint red, GLuint green, GLuint blue, GLuint alpha); typedef void (GLAD_API_PTR *PFNGLCOLOR4UIVPROC)(const GLuint * v); typedef void (GLAD_API_PTR *PFNGLCOLOR4USPROC)(GLushort red, GLushort green, GLushort blue, GLushort alpha); typedef void (GLAD_API_PTR *PFNGLCOLOR4USVPROC)(const GLushort * v); typedef void (GLAD_API_PTR *PFNGLCOLORMASKPROC)(GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha); typedef void (GLAD_API_PTR *PFNGLCOLORMASKIPROC)(GLuint index, GLboolean r, GLboolean g, GLboolean b, GLboolean a); typedef void (GLAD_API_PTR *PFNGLCOLORMATERIALPROC)(GLenum face, GLenum mode); typedef void (GLAD_API_PTR *PFNGLCOLORP3UIPROC)(GLenum type, GLuint color); typedef void (GLAD_API_PTR *PFNGLCOLORP3UIVPROC)(GLenum type, const GLuint * color); typedef void (GLAD_API_PTR *PFNGLCOLORP4UIPROC)(GLenum type, GLuint color); typedef void (GLAD_API_PTR *PFNGLCOLORP4UIVPROC)(GLenum type, const GLuint * color); typedef void (GLAD_API_PTR *PFNGLCOLORPOINTERPROC)(GLint size, GLenum type, GLsizei stride, const void * pointer); typedef void (GLAD_API_PTR *PFNGLCOLORSUBTABLEPROC)(GLenum target, GLsizei start, GLsizei count, GLenum format, GLenum type, const void * data); typedef void (GLAD_API_PTR *PFNGLCOLORTABLEPROC)(GLenum target, GLenum internalformat, GLsizei width, GLenum format, GLenum type, const void * table); typedef void (GLAD_API_PTR *PFNGLCOLORTABLEPARAMETERFVPROC)(GLenum target, GLenum pname, const GLfloat * params); typedef void (GLAD_API_PTR *PFNGLCOLORTABLEPARAMETERIVPROC)(GLenum target, GLenum pname, const GLint * params); typedef void (GLAD_API_PTR *PFNGLCOMPILESHADERPROC)(GLuint shader); typedef void (GLAD_API_PTR *PFNGLCOMPILESHADERARBPROC)(GLhandleARB shaderObj); typedef void (GLAD_API_PTR *PFNGLCOMPILESHADERINCLUDEARBPROC)(GLuint shader, GLsizei count, const GLchar *const* path, const GLint * length); typedef void (GLAD_API_PTR *PFNGLCOMPRESSEDTEXIMAGE1DPROC)(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLint border, GLsizei imageSize, const void * data); typedef void (GLAD_API_PTR *PFNGLCOMPRESSEDTEXIMAGE1DARBPROC)(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLint border, GLsizei imageSize, const void * data); typedef void (GLAD_API_PTR *PFNGLCOMPRESSEDTEXIMAGE2DPROC)(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void * data); typedef void (GLAD_API_PTR *PFNGLCOMPRESSEDTEXIMAGE2DARBPROC)(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void * data); typedef void (GLAD_API_PTR *PFNGLCOMPRESSEDTEXIMAGE3DPROC)(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const void * data); typedef void (GLAD_API_PTR *PFNGLCOMPRESSEDTEXIMAGE3DARBPROC)(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const void * data); typedef void (GLAD_API_PTR *PFNGLCOMPRESSEDTEXSUBIMAGE1DPROC)(GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLsizei imageSize, const void * data); typedef void (GLAD_API_PTR *PFNGLCOMPRESSEDTEXSUBIMAGE1DARBPROC)(GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLsizei imageSize, const void * data); typedef void (GLAD_API_PTR *PFNGLCOMPRESSEDTEXSUBIMAGE2DPROC)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void * data); typedef void (GLAD_API_PTR *PFNGLCOMPRESSEDTEXSUBIMAGE2DARBPROC)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void * data); typedef void (GLAD_API_PTR *PFNGLCOMPRESSEDTEXSUBIMAGE3DPROC)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void * data); typedef void (GLAD_API_PTR *PFNGLCOMPRESSEDTEXSUBIMAGE3DARBPROC)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void * data); typedef void (GLAD_API_PTR *PFNGLCOMPRESSEDTEXTURESUBIMAGE1DPROC)(GLuint texture, GLint level, GLint xoffset, GLsizei width, GLenum format, GLsizei imageSize, const void * data); typedef void (GLAD_API_PTR *PFNGLCOMPRESSEDTEXTURESUBIMAGE2DPROC)(GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void * data); typedef void (GLAD_API_PTR *PFNGLCOMPRESSEDTEXTURESUBIMAGE3DPROC)(GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void * data); typedef void (GLAD_API_PTR *PFNGLCONVOLUTIONFILTER1DPROC)(GLenum target, GLenum internalformat, GLsizei width, GLenum format, GLenum type, const void * image); typedef void (GLAD_API_PTR *PFNGLCONVOLUTIONFILTER2DPROC)(GLenum target, GLenum internalformat, GLsizei width, GLsizei height, GLenum format, GLenum type, const void * image); typedef void (GLAD_API_PTR *PFNGLCONVOLUTIONPARAMETERFPROC)(GLenum target, GLenum pname, GLfloat params); typedef void (GLAD_API_PTR *PFNGLCONVOLUTIONPARAMETERFVPROC)(GLenum target, GLenum pname, const GLfloat * params); typedef void (GLAD_API_PTR *PFNGLCONVOLUTIONPARAMETERIPROC)(GLenum target, GLenum pname, GLint params); typedef void (GLAD_API_PTR *PFNGLCONVOLUTIONPARAMETERIVPROC)(GLenum target, GLenum pname, const GLint * params); typedef void (GLAD_API_PTR *PFNGLCOPYBUFFERSUBDATAPROC)(GLenum readTarget, GLenum writeTarget, GLintptr readOffset, GLintptr writeOffset, GLsizeiptr size); typedef void (GLAD_API_PTR *PFNGLCOPYCOLORSUBTABLEPROC)(GLenum target, GLsizei start, GLint x, GLint y, GLsizei width); typedef void (GLAD_API_PTR *PFNGLCOPYCOLORTABLEPROC)(GLenum target, GLenum internalformat, GLint x, GLint y, GLsizei width); typedef void (GLAD_API_PTR *PFNGLCOPYCONVOLUTIONFILTER1DPROC)(GLenum target, GLenum internalformat, GLint x, GLint y, GLsizei width); typedef void (GLAD_API_PTR *PFNGLCOPYCONVOLUTIONFILTER2DPROC)(GLenum target, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height); typedef void (GLAD_API_PTR *PFNGLCOPYIMAGESUBDATAPROC)(GLuint srcName, GLenum srcTarget, GLint srcLevel, GLint srcX, GLint srcY, GLint srcZ, GLuint dstName, GLenum dstTarget, GLint dstLevel, GLint dstX, GLint dstY, GLint dstZ, GLsizei srcWidth, GLsizei srcHeight, GLsizei srcDepth); typedef void (GLAD_API_PTR *PFNGLCOPYNAMEDBUFFERSUBDATAPROC)(GLuint readBuffer, GLuint writeBuffer, GLintptr readOffset, GLintptr writeOffset, GLsizeiptr size); typedef void (GLAD_API_PTR *PFNGLCOPYPIXELSPROC)(GLint x, GLint y, GLsizei width, GLsizei height, GLenum type); typedef void (GLAD_API_PTR *PFNGLCOPYTEXIMAGE1DPROC)(GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLint border); typedef void (GLAD_API_PTR *PFNGLCOPYTEXIMAGE2DPROC)(GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border); typedef void (GLAD_API_PTR *PFNGLCOPYTEXSUBIMAGE1DPROC)(GLenum target, GLint level, GLint xoffset, GLint x, GLint y, GLsizei width); typedef void (GLAD_API_PTR *PFNGLCOPYTEXSUBIMAGE2DPROC)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height); typedef void (GLAD_API_PTR *PFNGLCOPYTEXSUBIMAGE3DPROC)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height); typedef void (GLAD_API_PTR *PFNGLCOPYTEXTURESUBIMAGE1DPROC)(GLuint texture, GLint level, GLint xoffset, GLint x, GLint y, GLsizei width); typedef void (GLAD_API_PTR *PFNGLCOPYTEXTURESUBIMAGE2DPROC)(GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height); typedef void (GLAD_API_PTR *PFNGLCOPYTEXTURESUBIMAGE3DPROC)(GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height); typedef void (GLAD_API_PTR *PFNGLCREATEBUFFERSPROC)(GLsizei n, GLuint * buffers); typedef void (GLAD_API_PTR *PFNGLCREATEFRAMEBUFFERSPROC)(GLsizei n, GLuint * framebuffers); typedef GLuint (GLAD_API_PTR *PFNGLCREATEPROGRAMPROC)(void); typedef GLhandleARB (GLAD_API_PTR *PFNGLCREATEPROGRAMOBJECTARBPROC)(void); typedef void (GLAD_API_PTR *PFNGLCREATEPROGRAMPIPELINESPROC)(GLsizei n, GLuint * pipelines); typedef void (GLAD_API_PTR *PFNGLCREATEQUERIESPROC)(GLenum target, GLsizei n, GLuint * ids); typedef void (GLAD_API_PTR *PFNGLCREATERENDERBUFFERSPROC)(GLsizei n, GLuint * renderbuffers); typedef void (GLAD_API_PTR *PFNGLCREATESAMPLERSPROC)(GLsizei n, GLuint * samplers); typedef GLuint (GLAD_API_PTR *PFNGLCREATESHADERPROC)(GLenum type); typedef GLhandleARB (GLAD_API_PTR *PFNGLCREATESHADEROBJECTARBPROC)(GLenum shaderType); typedef GLuint (GLAD_API_PTR *PFNGLCREATESHADERPROGRAMVPROC)(GLenum type, GLsizei count, const GLchar *const* strings); typedef GLsync (GLAD_API_PTR *PFNGLCREATESYNCFROMCLEVENTARBPROC)(struct _cl_context * context, struct _cl_event * event, GLbitfield flags); typedef void (GLAD_API_PTR *PFNGLCREATETEXTURESPROC)(GLenum target, GLsizei n, GLuint * textures); typedef void (GLAD_API_PTR *PFNGLCREATETRANSFORMFEEDBACKSPROC)(GLsizei n, GLuint * ids); typedef void (GLAD_API_PTR *PFNGLCREATEVERTEXARRAYSPROC)(GLsizei n, GLuint * arrays); typedef void (GLAD_API_PTR *PFNGLCULLFACEPROC)(GLenum mode); typedef void (GLAD_API_PTR *PFNGLCURRENTPALETTEMATRIXARBPROC)(GLint index); typedef void (GLAD_API_PTR *PFNGLDEBUGMESSAGECALLBACKPROC)(GLDEBUGPROC callback, const void * userParam); typedef void (GLAD_API_PTR *PFNGLDEBUGMESSAGECALLBACKARBPROC)(GLDEBUGPROCARB callback, const void * userParam); typedef void (GLAD_API_PTR *PFNGLDEBUGMESSAGECONTROLPROC)(GLenum source, GLenum type, GLenum severity, GLsizei count, const GLuint * ids, GLboolean enabled); typedef void (GLAD_API_PTR *PFNGLDEBUGMESSAGECONTROLARBPROC)(GLenum source, GLenum type, GLenum severity, GLsizei count, const GLuint * ids, GLboolean enabled); typedef void (GLAD_API_PTR *PFNGLDEBUGMESSAGEINSERTPROC)(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar * buf); typedef void (GLAD_API_PTR *PFNGLDEBUGMESSAGEINSERTARBPROC)(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar * buf); typedef void (GLAD_API_PTR *PFNGLDELETEBUFFERSPROC)(GLsizei n, const GLuint * buffers); typedef void (GLAD_API_PTR *PFNGLDELETEBUFFERSARBPROC)(GLsizei n, const GLuint * buffers); typedef void (GLAD_API_PTR *PFNGLDELETEFRAMEBUFFERSPROC)(GLsizei n, const GLuint * framebuffers); typedef void (GLAD_API_PTR *PFNGLDELETELISTSPROC)(GLuint list, GLsizei range); typedef void (GLAD_API_PTR *PFNGLDELETENAMEDSTRINGARBPROC)(GLint namelen, const GLchar * name); typedef void (GLAD_API_PTR *PFNGLDELETEOBJECTARBPROC)(GLhandleARB obj); typedef void (GLAD_API_PTR *PFNGLDELETEPROGRAMPROC)(GLuint program); typedef void (GLAD_API_PTR *PFNGLDELETEPROGRAMPIPELINESPROC)(GLsizei n, const GLuint * pipelines); typedef void (GLAD_API_PTR *PFNGLDELETEPROGRAMSARBPROC)(GLsizei n, const GLuint * programs); typedef void (GLAD_API_PTR *PFNGLDELETEQUERIESPROC)(GLsizei n, const GLuint * ids); typedef void (GLAD_API_PTR *PFNGLDELETEQUERIESARBPROC)(GLsizei n, const GLuint * ids); typedef void (GLAD_API_PTR *PFNGLDELETERENDERBUFFERSPROC)(GLsizei n, const GLuint * renderbuffers); typedef void (GLAD_API_PTR *PFNGLDELETESAMPLERSPROC)(GLsizei count, const GLuint * samplers); typedef void (GLAD_API_PTR *PFNGLDELETESHADERPROC)(GLuint shader); typedef void (GLAD_API_PTR *PFNGLDELETESYNCPROC)(GLsync sync); typedef void (GLAD_API_PTR *PFNGLDELETETEXTURESPROC)(GLsizei n, const GLuint * textures); typedef void (GLAD_API_PTR *PFNGLDELETETRANSFORMFEEDBACKSPROC)(GLsizei n, const GLuint * ids); typedef void (GLAD_API_PTR *PFNGLDELETEVERTEXARRAYSPROC)(GLsizei n, const GLuint * arrays); typedef void (GLAD_API_PTR *PFNGLDEPTHFUNCPROC)(GLenum func); typedef void (GLAD_API_PTR *PFNGLDEPTHMASKPROC)(GLboolean flag); typedef void (GLAD_API_PTR *PFNGLDEPTHRANGEPROC)(GLdouble n, GLdouble f); typedef void (GLAD_API_PTR *PFNGLDEPTHRANGEARRAYDVNVPROC)(GLuint first, GLsizei count, const GLdouble * v); typedef void (GLAD_API_PTR *PFNGLDEPTHRANGEARRAYVPROC)(GLuint first, GLsizei count, const GLdouble * v); typedef void (GLAD_API_PTR *PFNGLDEPTHRANGEINDEXEDPROC)(GLuint index, GLdouble n, GLdouble f); typedef void (GLAD_API_PTR *PFNGLDEPTHRANGEINDEXEDDNVPROC)(GLuint index, GLdouble n, GLdouble f); typedef void (GLAD_API_PTR *PFNGLDEPTHRANGEFPROC)(GLfloat n, GLfloat f); typedef void (GLAD_API_PTR *PFNGLDETACHOBJECTARBPROC)(GLhandleARB containerObj, GLhandleARB attachedObj); typedef void (GLAD_API_PTR *PFNGLDETACHSHADERPROC)(GLuint program, GLuint shader); typedef void (GLAD_API_PTR *PFNGLDISABLEPROC)(GLenum cap); typedef void (GLAD_API_PTR *PFNGLDISABLECLIENTSTATEPROC)(GLenum array); typedef void (GLAD_API_PTR *PFNGLDISABLEVERTEXARRAYATTRIBPROC)(GLuint vaobj, GLuint index); typedef void (GLAD_API_PTR *PFNGLDISABLEVERTEXATTRIBARRAYPROC)(GLuint index); typedef void (GLAD_API_PTR *PFNGLDISABLEVERTEXATTRIBARRAYARBPROC)(GLuint index); typedef void (GLAD_API_PTR *PFNGLDISABLEIPROC)(GLenum target, GLuint index); typedef void (GLAD_API_PTR *PFNGLDISPATCHCOMPUTEPROC)(GLuint num_groups_x, GLuint num_groups_y, GLuint num_groups_z); typedef void (GLAD_API_PTR *PFNGLDISPATCHCOMPUTEGROUPSIZEARBPROC)(GLuint num_groups_x, GLuint num_groups_y, GLuint num_groups_z, GLuint group_size_x, GLuint group_size_y, GLuint group_size_z); typedef void (GLAD_API_PTR *PFNGLDISPATCHCOMPUTEINDIRECTPROC)(GLintptr indirect); typedef void (GLAD_API_PTR *PFNGLDRAWARRAYSPROC)(GLenum mode, GLint first, GLsizei count); typedef void (GLAD_API_PTR *PFNGLDRAWARRAYSINDIRECTPROC)(GLenum mode, const void * indirect); typedef void (GLAD_API_PTR *PFNGLDRAWARRAYSINSTANCEDPROC)(GLenum mode, GLint first, GLsizei count, GLsizei instancecount); typedef void (GLAD_API_PTR *PFNGLDRAWARRAYSINSTANCEDARBPROC)(GLenum mode, GLint first, GLsizei count, GLsizei primcount); typedef void (GLAD_API_PTR *PFNGLDRAWARRAYSINSTANCEDBASEINSTANCEPROC)(GLenum mode, GLint first, GLsizei count, GLsizei instancecount, GLuint baseinstance); typedef void (GLAD_API_PTR *PFNGLDRAWBUFFERPROC)(GLenum buf); typedef void (GLAD_API_PTR *PFNGLDRAWBUFFERSPROC)(GLsizei n, const GLenum * bufs); typedef void (GLAD_API_PTR *PFNGLDRAWBUFFERSARBPROC)(GLsizei n, const GLenum * bufs); typedef void (GLAD_API_PTR *PFNGLDRAWELEMENTSPROC)(GLenum mode, GLsizei count, GLenum type, const void * indices); typedef void (GLAD_API_PTR *PFNGLDRAWELEMENTSBASEVERTEXPROC)(GLenum mode, GLsizei count, GLenum type, const void * indices, GLint basevertex); typedef void (GLAD_API_PTR *PFNGLDRAWELEMENTSINDIRECTPROC)(GLenum mode, GLenum type, const void * indirect); typedef void (GLAD_API_PTR *PFNGLDRAWELEMENTSINSTANCEDPROC)(GLenum mode, GLsizei count, GLenum type, const void * indices, GLsizei instancecount); typedef void (GLAD_API_PTR *PFNGLDRAWELEMENTSINSTANCEDARBPROC)(GLenum mode, GLsizei count, GLenum type, const void * indices, GLsizei primcount); typedef void (GLAD_API_PTR *PFNGLDRAWELEMENTSINSTANCEDBASEINSTANCEPROC)(GLenum mode, GLsizei count, GLenum type, const void * indices, GLsizei instancecount, GLuint baseinstance); typedef void (GLAD_API_PTR *PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXPROC)(GLenum mode, GLsizei count, GLenum type, const void * indices, GLsizei instancecount, GLint basevertex); typedef void (GLAD_API_PTR *PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXBASEINSTANCEPROC)(GLenum mode, GLsizei count, GLenum type, const void * indices, GLsizei instancecount, GLint basevertex, GLuint baseinstance); typedef void (GLAD_API_PTR *PFNGLDRAWPIXELSPROC)(GLsizei width, GLsizei height, GLenum format, GLenum type, const void * pixels); typedef void (GLAD_API_PTR *PFNGLDRAWRANGEELEMENTSPROC)(GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const void * indices); typedef void (GLAD_API_PTR *PFNGLDRAWRANGEELEMENTSBASEVERTEXPROC)(GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const void * indices, GLint basevertex); typedef void (GLAD_API_PTR *PFNGLDRAWTRANSFORMFEEDBACKPROC)(GLenum mode, GLuint id); typedef void (GLAD_API_PTR *PFNGLDRAWTRANSFORMFEEDBACKINSTANCEDPROC)(GLenum mode, GLuint id, GLsizei instancecount); typedef void (GLAD_API_PTR *PFNGLDRAWTRANSFORMFEEDBACKSTREAMPROC)(GLenum mode, GLuint id, GLuint stream); typedef void (GLAD_API_PTR *PFNGLDRAWTRANSFORMFEEDBACKSTREAMINSTANCEDPROC)(GLenum mode, GLuint id, GLuint stream, GLsizei instancecount); typedef void (GLAD_API_PTR *PFNGLEDGEFLAGPROC)(GLboolean flag); typedef void (GLAD_API_PTR *PFNGLEDGEFLAGPOINTERPROC)(GLsizei stride, const void * pointer); typedef void (GLAD_API_PTR *PFNGLEDGEFLAGVPROC)(const GLboolean * flag); typedef void (GLAD_API_PTR *PFNGLENABLEPROC)(GLenum cap); typedef void (GLAD_API_PTR *PFNGLENABLECLIENTSTATEPROC)(GLenum array); typedef void (GLAD_API_PTR *PFNGLENABLEVERTEXARRAYATTRIBPROC)(GLuint vaobj, GLuint index); typedef void (GLAD_API_PTR *PFNGLENABLEVERTEXATTRIBARRAYPROC)(GLuint index); typedef void (GLAD_API_PTR *PFNGLENABLEVERTEXATTRIBARRAYARBPROC)(GLuint index); typedef void (GLAD_API_PTR *PFNGLENABLEIPROC)(GLenum target, GLuint index); typedef void (GLAD_API_PTR *PFNGLENDPROC)(void); typedef void (GLAD_API_PTR *PFNGLENDCONDITIONALRENDERPROC)(void); typedef void (GLAD_API_PTR *PFNGLENDLISTPROC)(void); typedef void (GLAD_API_PTR *PFNGLENDQUERYPROC)(GLenum target); typedef void (GLAD_API_PTR *PFNGLENDQUERYARBPROC)(GLenum target); typedef void (GLAD_API_PTR *PFNGLENDQUERYINDEXEDPROC)(GLenum target, GLuint index); typedef void (GLAD_API_PTR *PFNGLENDTRANSFORMFEEDBACKPROC)(void); typedef void (GLAD_API_PTR *PFNGLEVALCOORD1DPROC)(GLdouble u); typedef void (GLAD_API_PTR *PFNGLEVALCOORD1DVPROC)(const GLdouble * u); typedef void (GLAD_API_PTR *PFNGLEVALCOORD1FPROC)(GLfloat u); typedef void (GLAD_API_PTR *PFNGLEVALCOORD1FVPROC)(const GLfloat * u); typedef void (GLAD_API_PTR *PFNGLEVALCOORD2DPROC)(GLdouble u, GLdouble v); typedef void (GLAD_API_PTR *PFNGLEVALCOORD2DVPROC)(const GLdouble * u); typedef void (GLAD_API_PTR *PFNGLEVALCOORD2FPROC)(GLfloat u, GLfloat v); typedef void (GLAD_API_PTR *PFNGLEVALCOORD2FVPROC)(const GLfloat * u); typedef void (GLAD_API_PTR *PFNGLEVALMESH1PROC)(GLenum mode, GLint i1, GLint i2); typedef void (GLAD_API_PTR *PFNGLEVALMESH2PROC)(GLenum mode, GLint i1, GLint i2, GLint j1, GLint j2); typedef void (GLAD_API_PTR *PFNGLEVALPOINT1PROC)(GLint i); typedef void (GLAD_API_PTR *PFNGLEVALPOINT2PROC)(GLint i, GLint j); typedef void (GLAD_API_PTR *PFNGLEVALUATEDEPTHVALUESARBPROC)(void); typedef void (GLAD_API_PTR *PFNGLFEEDBACKBUFFERPROC)(GLsizei size, GLenum type, GLfloat * buffer); typedef GLsync (GLAD_API_PTR *PFNGLFENCESYNCPROC)(GLenum condition, GLbitfield flags); typedef void (GLAD_API_PTR *PFNGLFINISHPROC)(void); typedef void (GLAD_API_PTR *PFNGLFLUSHPROC)(void); typedef void (GLAD_API_PTR *PFNGLFLUSHMAPPEDBUFFERRANGEPROC)(GLenum target, GLintptr offset, GLsizeiptr length); typedef void (GLAD_API_PTR *PFNGLFLUSHMAPPEDNAMEDBUFFERRANGEPROC)(GLuint buffer, GLintptr offset, GLsizeiptr length); typedef void (GLAD_API_PTR *PFNGLFOGCOORDPOINTERPROC)(GLenum type, GLsizei stride, const void * pointer); typedef void (GLAD_API_PTR *PFNGLFOGCOORDDPROC)(GLdouble coord); typedef void (GLAD_API_PTR *PFNGLFOGCOORDDVPROC)(const GLdouble * coord); typedef void (GLAD_API_PTR *PFNGLFOGCOORDFPROC)(GLfloat coord); typedef void (GLAD_API_PTR *PFNGLFOGCOORDFVPROC)(const GLfloat * coord); typedef void (GLAD_API_PTR *PFNGLFOGFPROC)(GLenum pname, GLfloat param); typedef void (GLAD_API_PTR *PFNGLFOGFVPROC)(GLenum pname, const GLfloat * params); typedef void (GLAD_API_PTR *PFNGLFOGIPROC)(GLenum pname, GLint param); typedef void (GLAD_API_PTR *PFNGLFOGIVPROC)(GLenum pname, const GLint * params); typedef void (GLAD_API_PTR *PFNGLFRAMEBUFFERPARAMETERIPROC)(GLenum target, GLenum pname, GLint param); typedef void (GLAD_API_PTR *PFNGLFRAMEBUFFERRENDERBUFFERPROC)(GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer); typedef void (GLAD_API_PTR *PFNGLFRAMEBUFFERSAMPLELOCATIONSFVARBPROC)(GLenum target, GLuint start, GLsizei count, const GLfloat * v); typedef void (GLAD_API_PTR *PFNGLFRAMEBUFFERTEXTUREPROC)(GLenum target, GLenum attachment, GLuint texture, GLint level); typedef void (GLAD_API_PTR *PFNGLFRAMEBUFFERTEXTURE1DPROC)(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level); typedef void (GLAD_API_PTR *PFNGLFRAMEBUFFERTEXTURE2DPROC)(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level); typedef void (GLAD_API_PTR *PFNGLFRAMEBUFFERTEXTURE3DPROC)(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLint zoffset); typedef void (GLAD_API_PTR *PFNGLFRAMEBUFFERTEXTUREARBPROC)(GLenum target, GLenum attachment, GLuint texture, GLint level); typedef void (GLAD_API_PTR *PFNGLFRAMEBUFFERTEXTUREFACEARBPROC)(GLenum target, GLenum attachment, GLuint texture, GLint level, GLenum face); typedef void (GLAD_API_PTR *PFNGLFRAMEBUFFERTEXTURELAYERPROC)(GLenum target, GLenum attachment, GLuint texture, GLint level, GLint layer); typedef void (GLAD_API_PTR *PFNGLFRAMEBUFFERTEXTURELAYERARBPROC)(GLenum target, GLenum attachment, GLuint texture, GLint level, GLint layer); typedef void (GLAD_API_PTR *PFNGLFRONTFACEPROC)(GLenum mode); typedef void (GLAD_API_PTR *PFNGLFRUSTUMPROC)(GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble zNear, GLdouble zFar); typedef void (GLAD_API_PTR *PFNGLGENBUFFERSPROC)(GLsizei n, GLuint * buffers); typedef void (GLAD_API_PTR *PFNGLGENBUFFERSARBPROC)(GLsizei n, GLuint * buffers); typedef void (GLAD_API_PTR *PFNGLGENFRAMEBUFFERSPROC)(GLsizei n, GLuint * framebuffers); typedef GLuint (GLAD_API_PTR *PFNGLGENLISTSPROC)(GLsizei range); typedef void (GLAD_API_PTR *PFNGLGENPROGRAMPIPELINESPROC)(GLsizei n, GLuint * pipelines); typedef void (GLAD_API_PTR *PFNGLGENPROGRAMSARBPROC)(GLsizei n, GLuint * programs); typedef void (GLAD_API_PTR *PFNGLGENQUERIESPROC)(GLsizei n, GLuint * ids); typedef void (GLAD_API_PTR *PFNGLGENQUERIESARBPROC)(GLsizei n, GLuint * ids); typedef void (GLAD_API_PTR *PFNGLGENRENDERBUFFERSPROC)(GLsizei n, GLuint * renderbuffers); typedef void (GLAD_API_PTR *PFNGLGENSAMPLERSPROC)(GLsizei count, GLuint * samplers); typedef void (GLAD_API_PTR *PFNGLGENTEXTURESPROC)(GLsizei n, GLuint * textures); typedef void (GLAD_API_PTR *PFNGLGENTRANSFORMFEEDBACKSPROC)(GLsizei n, GLuint * ids); typedef void (GLAD_API_PTR *PFNGLGENVERTEXARRAYSPROC)(GLsizei n, GLuint * arrays); typedef void (GLAD_API_PTR *PFNGLGENERATEMIPMAPPROC)(GLenum target); typedef void (GLAD_API_PTR *PFNGLGENERATETEXTUREMIPMAPPROC)(GLuint texture); typedef void (GLAD_API_PTR *PFNGLGETACTIVEATOMICCOUNTERBUFFERIVPROC)(GLuint program, GLuint bufferIndex, GLenum pname, GLint * params); typedef void (GLAD_API_PTR *PFNGLGETACTIVEATTRIBPROC)(GLuint program, GLuint index, GLsizei bufSize, GLsizei * length, GLint * size, GLenum * type, GLchar * name); typedef void (GLAD_API_PTR *PFNGLGETACTIVEATTRIBARBPROC)(GLhandleARB programObj, GLuint index, GLsizei maxLength, GLsizei * length, GLint * size, GLenum * type, GLcharARB * name); typedef void (GLAD_API_PTR *PFNGLGETACTIVESUBROUTINENAMEPROC)(GLuint program, GLenum shadertype, GLuint index, GLsizei bufSize, GLsizei * length, GLchar * name); typedef void (GLAD_API_PTR *PFNGLGETACTIVESUBROUTINEUNIFORMNAMEPROC)(GLuint program, GLenum shadertype, GLuint index, GLsizei bufSize, GLsizei * length, GLchar * name); typedef void (GLAD_API_PTR *PFNGLGETACTIVESUBROUTINEUNIFORMIVPROC)(GLuint program, GLenum shadertype, GLuint index, GLenum pname, GLint * values); typedef void (GLAD_API_PTR *PFNGLGETACTIVEUNIFORMPROC)(GLuint program, GLuint index, GLsizei bufSize, GLsizei * length, GLint * size, GLenum * type, GLchar * name); typedef void (GLAD_API_PTR *PFNGLGETACTIVEUNIFORMARBPROC)(GLhandleARB programObj, GLuint index, GLsizei maxLength, GLsizei * length, GLint * size, GLenum * type, GLcharARB * name); typedef void (GLAD_API_PTR *PFNGLGETACTIVEUNIFORMBLOCKNAMEPROC)(GLuint program, GLuint uniformBlockIndex, GLsizei bufSize, GLsizei * length, GLchar * uniformBlockName); typedef void (GLAD_API_PTR *PFNGLGETACTIVEUNIFORMBLOCKIVPROC)(GLuint program, GLuint uniformBlockIndex, GLenum pname, GLint * params); typedef void (GLAD_API_PTR *PFNGLGETACTIVEUNIFORMNAMEPROC)(GLuint program, GLuint uniformIndex, GLsizei bufSize, GLsizei * length, GLchar * uniformName); typedef void (GLAD_API_PTR *PFNGLGETACTIVEUNIFORMSIVPROC)(GLuint program, GLsizei uniformCount, const GLuint * uniformIndices, GLenum pname, GLint * params); typedef void (GLAD_API_PTR *PFNGLGETATTACHEDOBJECTSARBPROC)(GLhandleARB containerObj, GLsizei maxCount, GLsizei * count, GLhandleARB * obj); typedef void (GLAD_API_PTR *PFNGLGETATTACHEDSHADERSPROC)(GLuint program, GLsizei maxCount, GLsizei * count, GLuint * shaders); typedef GLint (GLAD_API_PTR *PFNGLGETATTRIBLOCATIONPROC)(GLuint program, const GLchar * name); typedef GLint (GLAD_API_PTR *PFNGLGETATTRIBLOCATIONARBPROC)(GLhandleARB programObj, const GLcharARB * name); typedef void (GLAD_API_PTR *PFNGLGETBOOLEANI_VPROC)(GLenum target, GLuint index, GLboolean * data); typedef void (GLAD_API_PTR *PFNGLGETBOOLEANVPROC)(GLenum pname, GLboolean * data); typedef void (GLAD_API_PTR *PFNGLGETBUFFERPARAMETERI64VPROC)(GLenum target, GLenum pname, GLint64 * params); typedef void (GLAD_API_PTR *PFNGLGETBUFFERPARAMETERIVPROC)(GLenum target, GLenum pname, GLint * params); typedef void (GLAD_API_PTR *PFNGLGETBUFFERPARAMETERIVARBPROC)(GLenum target, GLenum pname, GLint * params); typedef void (GLAD_API_PTR *PFNGLGETBUFFERPOINTERVPROC)(GLenum target, GLenum pname, void ** params); typedef void (GLAD_API_PTR *PFNGLGETBUFFERPOINTERVARBPROC)(GLenum target, GLenum pname, void ** params); typedef void (GLAD_API_PTR *PFNGLGETBUFFERSUBDATAPROC)(GLenum target, GLintptr offset, GLsizeiptr size, void * data); typedef void (GLAD_API_PTR *PFNGLGETBUFFERSUBDATAARBPROC)(GLenum target, GLintptrARB offset, GLsizeiptrARB size, void * data); typedef void (GLAD_API_PTR *PFNGLGETCLIPPLANEPROC)(GLenum plane, GLdouble * equation); typedef void (GLAD_API_PTR *PFNGLGETCOLORTABLEPROC)(GLenum target, GLenum format, GLenum type, void * table); typedef void (GLAD_API_PTR *PFNGLGETCOLORTABLEPARAMETERFVPROC)(GLenum target, GLenum pname, GLfloat * params); typedef void (GLAD_API_PTR *PFNGLGETCOLORTABLEPARAMETERIVPROC)(GLenum target, GLenum pname, GLint * params); typedef void (GLAD_API_PTR *PFNGLGETCOMPRESSEDTEXIMAGEPROC)(GLenum target, GLint level, void * img); typedef void (GLAD_API_PTR *PFNGLGETCOMPRESSEDTEXIMAGEARBPROC)(GLenum target, GLint level, void * img); typedef void (GLAD_API_PTR *PFNGLGETCOMPRESSEDTEXTUREIMAGEPROC)(GLuint texture, GLint level, GLsizei bufSize, void * pixels); typedef void (GLAD_API_PTR *PFNGLGETCOMPRESSEDTEXTURESUBIMAGEPROC)(GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLsizei bufSize, void * pixels); typedef void (GLAD_API_PTR *PFNGLGETCONVOLUTIONFILTERPROC)(GLenum target, GLenum format, GLenum type, void * image); typedef void (GLAD_API_PTR *PFNGLGETCONVOLUTIONPARAMETERFVPROC)(GLenum target, GLenum pname, GLfloat * params); typedef void (GLAD_API_PTR *PFNGLGETCONVOLUTIONPARAMETERIVPROC)(GLenum target, GLenum pname, GLint * params); typedef GLuint (GLAD_API_PTR *PFNGLGETDEBUGMESSAGELOGPROC)(GLuint count, GLsizei bufSize, GLenum * sources, GLenum * types, GLuint * ids, GLenum * severities, GLsizei * lengths, GLchar * messageLog); typedef GLuint (GLAD_API_PTR *PFNGLGETDEBUGMESSAGELOGARBPROC)(GLuint count, GLsizei bufSize, GLenum * sources, GLenum * types, GLuint * ids, GLenum * severities, GLsizei * lengths, GLchar * messageLog); typedef void (GLAD_API_PTR *PFNGLGETDOUBLEI_VPROC)(GLenum target, GLuint index, GLdouble * data); typedef void (GLAD_API_PTR *PFNGLGETDOUBLEVPROC)(GLenum pname, GLdouble * data); typedef GLenum (GLAD_API_PTR *PFNGLGETERRORPROC)(void); typedef void (GLAD_API_PTR *PFNGLGETFLOATI_VPROC)(GLenum target, GLuint index, GLfloat * data); typedef void (GLAD_API_PTR *PFNGLGETFLOATVPROC)(GLenum pname, GLfloat * data); typedef GLint (GLAD_API_PTR *PFNGLGETFRAGDATAINDEXPROC)(GLuint program, const GLchar * name); typedef GLint (GLAD_API_PTR *PFNGLGETFRAGDATALOCATIONPROC)(GLuint program, const GLchar * name); typedef void (GLAD_API_PTR *PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVPROC)(GLenum target, GLenum attachment, GLenum pname, GLint * params); typedef void (GLAD_API_PTR *PFNGLGETFRAMEBUFFERPARAMETERIVPROC)(GLenum target, GLenum pname, GLint * params); typedef GLenum (GLAD_API_PTR *PFNGLGETGRAPHICSRESETSTATUSPROC)(void); typedef GLenum (GLAD_API_PTR *PFNGLGETGRAPHICSRESETSTATUSARBPROC)(void); typedef GLhandleARB (GLAD_API_PTR *PFNGLGETHANDLEARBPROC)(GLenum pname); typedef void (GLAD_API_PTR *PFNGLGETHISTOGRAMPROC)(GLenum target, GLboolean reset, GLenum format, GLenum type, void * values); typedef void (GLAD_API_PTR *PFNGLGETHISTOGRAMPARAMETERFVPROC)(GLenum target, GLenum pname, GLfloat * params); typedef void (GLAD_API_PTR *PFNGLGETHISTOGRAMPARAMETERIVPROC)(GLenum target, GLenum pname, GLint * params); typedef GLuint64 (GLAD_API_PTR *PFNGLGETIMAGEHANDLEARBPROC)(GLuint texture, GLint level, GLboolean layered, GLint layer, GLenum format); typedef void (GLAD_API_PTR *PFNGLGETINFOLOGARBPROC)(GLhandleARB obj, GLsizei maxLength, GLsizei * length, GLcharARB * infoLog); typedef void (GLAD_API_PTR *PFNGLGETINTEGER64I_VPROC)(GLenum target, GLuint index, GLint64 * data); typedef void (GLAD_API_PTR *PFNGLGETINTEGER64VPROC)(GLenum pname, GLint64 * data); typedef void (GLAD_API_PTR *PFNGLGETINTEGERI_VPROC)(GLenum target, GLuint index, GLint * data); typedef void (GLAD_API_PTR *PFNGLGETINTEGERVPROC)(GLenum pname, GLint * data); typedef void (GLAD_API_PTR *PFNGLGETINTERNALFORMATI64VPROC)(GLenum target, GLenum internalformat, GLenum pname, GLsizei count, GLint64 * params); typedef void (GLAD_API_PTR *PFNGLGETINTERNALFORMATIVPROC)(GLenum target, GLenum internalformat, GLenum pname, GLsizei count, GLint * params); typedef void (GLAD_API_PTR *PFNGLGETLIGHTFVPROC)(GLenum light, GLenum pname, GLfloat * params); typedef void (GLAD_API_PTR *PFNGLGETLIGHTIVPROC)(GLenum light, GLenum pname, GLint * params); typedef void (GLAD_API_PTR *PFNGLGETMAPDVPROC)(GLenum target, GLenum query, GLdouble * v); typedef void (GLAD_API_PTR *PFNGLGETMAPFVPROC)(GLenum target, GLenum query, GLfloat * v); typedef void (GLAD_API_PTR *PFNGLGETMAPIVPROC)(GLenum target, GLenum query, GLint * v); typedef void (GLAD_API_PTR *PFNGLGETMATERIALFVPROC)(GLenum face, GLenum pname, GLfloat * params); typedef void (GLAD_API_PTR *PFNGLGETMATERIALIVPROC)(GLenum face, GLenum pname, GLint * params); typedef void (GLAD_API_PTR *PFNGLGETMINMAXPROC)(GLenum target, GLboolean reset, GLenum format, GLenum type, void * values); typedef void (GLAD_API_PTR *PFNGLGETMINMAXPARAMETERFVPROC)(GLenum target, GLenum pname, GLfloat * params); typedef void (GLAD_API_PTR *PFNGLGETMINMAXPARAMETERIVPROC)(GLenum target, GLenum pname, GLint * params); typedef void (GLAD_API_PTR *PFNGLGETMULTISAMPLEFVPROC)(GLenum pname, GLuint index, GLfloat * val); typedef void (GLAD_API_PTR *PFNGLGETNAMEDBUFFERPARAMETERI64VPROC)(GLuint buffer, GLenum pname, GLint64 * params); typedef void (GLAD_API_PTR *PFNGLGETNAMEDBUFFERPARAMETERIVPROC)(GLuint buffer, GLenum pname, GLint * params); typedef void (GLAD_API_PTR *PFNGLGETNAMEDBUFFERPOINTERVPROC)(GLuint buffer, GLenum pname, void ** params); typedef void (GLAD_API_PTR *PFNGLGETNAMEDBUFFERSUBDATAPROC)(GLuint buffer, GLintptr offset, GLsizeiptr size, void * data); typedef void (GLAD_API_PTR *PFNGLGETNAMEDFRAMEBUFFERATTACHMENTPARAMETERIVPROC)(GLuint framebuffer, GLenum attachment, GLenum pname, GLint * params); typedef void (GLAD_API_PTR *PFNGLGETNAMEDFRAMEBUFFERPARAMETERIVPROC)(GLuint framebuffer, GLenum pname, GLint * param); typedef void (GLAD_API_PTR *PFNGLGETNAMEDRENDERBUFFERPARAMETERIVPROC)(GLuint renderbuffer, GLenum pname, GLint * params); typedef void (GLAD_API_PTR *PFNGLGETNAMEDSTRINGARBPROC)(GLint namelen, const GLchar * name, GLsizei bufSize, GLint * stringlen, GLchar * string); typedef void (GLAD_API_PTR *PFNGLGETNAMEDSTRINGIVARBPROC)(GLint namelen, const GLchar * name, GLenum pname, GLint * params); typedef void (GLAD_API_PTR *PFNGLGETOBJECTLABELPROC)(GLenum identifier, GLuint name, GLsizei bufSize, GLsizei * length, GLchar * label); typedef void (GLAD_API_PTR *PFNGLGETOBJECTPARAMETERFVARBPROC)(GLhandleARB obj, GLenum pname, GLfloat * params); typedef void (GLAD_API_PTR *PFNGLGETOBJECTPARAMETERIVARBPROC)(GLhandleARB obj, GLenum pname, GLint * params); typedef void (GLAD_API_PTR *PFNGLGETOBJECTPTRLABELPROC)(const void * ptr, GLsizei bufSize, GLsizei * length, GLchar * label); typedef void (GLAD_API_PTR *PFNGLGETPIXELMAPFVPROC)(GLenum map, GLfloat * values); typedef void (GLAD_API_PTR *PFNGLGETPIXELMAPUIVPROC)(GLenum map, GLuint * values); typedef void (GLAD_API_PTR *PFNGLGETPIXELMAPUSVPROC)(GLenum map, GLushort * values); typedef void (GLAD_API_PTR *PFNGLGETPOINTERVPROC)(GLenum pname, void ** params); typedef void (GLAD_API_PTR *PFNGLGETPOLYGONSTIPPLEPROC)(GLubyte * mask); typedef void (GLAD_API_PTR *PFNGLGETPROGRAMBINARYPROC)(GLuint program, GLsizei bufSize, GLsizei * length, GLenum * binaryFormat, void * binary); typedef void (GLAD_API_PTR *PFNGLGETPROGRAMENVPARAMETERDVARBPROC)(GLenum target, GLuint index, GLdouble * params); typedef void (GLAD_API_PTR *PFNGLGETPROGRAMENVPARAMETERFVARBPROC)(GLenum target, GLuint index, GLfloat * params); typedef void (GLAD_API_PTR *PFNGLGETPROGRAMINFOLOGPROC)(GLuint program, GLsizei bufSize, GLsizei * length, GLchar * infoLog); typedef void (GLAD_API_PTR *PFNGLGETPROGRAMINTERFACEIVPROC)(GLuint program, GLenum programInterface, GLenum pname, GLint * params); typedef void (GLAD_API_PTR *PFNGLGETPROGRAMLOCALPARAMETERDVARBPROC)(GLenum target, GLuint index, GLdouble * params); typedef void (GLAD_API_PTR *PFNGLGETPROGRAMLOCALPARAMETERFVARBPROC)(GLenum target, GLuint index, GLfloat * params); typedef void (GLAD_API_PTR *PFNGLGETPROGRAMPIPELINEINFOLOGPROC)(GLuint pipeline, GLsizei bufSize, GLsizei * length, GLchar * infoLog); typedef void (GLAD_API_PTR *PFNGLGETPROGRAMPIPELINEIVPROC)(GLuint pipeline, GLenum pname, GLint * params); typedef GLuint (GLAD_API_PTR *PFNGLGETPROGRAMRESOURCEINDEXPROC)(GLuint program, GLenum programInterface, const GLchar * name); typedef GLint (GLAD_API_PTR *PFNGLGETPROGRAMRESOURCELOCATIONPROC)(GLuint program, GLenum programInterface, const GLchar * name); typedef GLint (GLAD_API_PTR *PFNGLGETPROGRAMRESOURCELOCATIONINDEXPROC)(GLuint program, GLenum programInterface, const GLchar * name); typedef void (GLAD_API_PTR *PFNGLGETPROGRAMRESOURCENAMEPROC)(GLuint program, GLenum programInterface, GLuint index, GLsizei bufSize, GLsizei * length, GLchar * name); typedef void (GLAD_API_PTR *PFNGLGETPROGRAMRESOURCEIVPROC)(GLuint program, GLenum programInterface, GLuint index, GLsizei propCount, const GLenum * props, GLsizei count, GLsizei * length, GLint * params); typedef void (GLAD_API_PTR *PFNGLGETPROGRAMSTAGEIVPROC)(GLuint program, GLenum shadertype, GLenum pname, GLint * values); typedef void (GLAD_API_PTR *PFNGLGETPROGRAMSTRINGARBPROC)(GLenum target, GLenum pname, void * string); typedef void (GLAD_API_PTR *PFNGLGETPROGRAMIVPROC)(GLuint program, GLenum pname, GLint * params); typedef void (GLAD_API_PTR *PFNGLGETPROGRAMIVARBPROC)(GLenum target, GLenum pname, GLint * params); typedef void (GLAD_API_PTR *PFNGLGETQUERYBUFFEROBJECTI64VPROC)(GLuint id, GLuint buffer, GLenum pname, GLintptr offset); typedef void (GLAD_API_PTR *PFNGLGETQUERYBUFFEROBJECTIVPROC)(GLuint id, GLuint buffer, GLenum pname, GLintptr offset); typedef void (GLAD_API_PTR *PFNGLGETQUERYBUFFEROBJECTUI64VPROC)(GLuint id, GLuint buffer, GLenum pname, GLintptr offset); typedef void (GLAD_API_PTR *PFNGLGETQUERYBUFFEROBJECTUIVPROC)(GLuint id, GLuint buffer, GLenum pname, GLintptr offset); typedef void (GLAD_API_PTR *PFNGLGETQUERYINDEXEDIVPROC)(GLenum target, GLuint index, GLenum pname, GLint * params); typedef void (GLAD_API_PTR *PFNGLGETQUERYOBJECTI64VPROC)(GLuint id, GLenum pname, GLint64 * params); typedef void (GLAD_API_PTR *PFNGLGETQUERYOBJECTIVPROC)(GLuint id, GLenum pname, GLint * params); typedef void (GLAD_API_PTR *PFNGLGETQUERYOBJECTIVARBPROC)(GLuint id, GLenum pname, GLint * params); typedef void (GLAD_API_PTR *PFNGLGETQUERYOBJECTUI64VPROC)(GLuint id, GLenum pname, GLuint64 * params); typedef void (GLAD_API_PTR *PFNGLGETQUERYOBJECTUIVPROC)(GLuint id, GLenum pname, GLuint * params); typedef void (GLAD_API_PTR *PFNGLGETQUERYOBJECTUIVARBPROC)(GLuint id, GLenum pname, GLuint * params); typedef void (GLAD_API_PTR *PFNGLGETQUERYIVPROC)(GLenum target, GLenum pname, GLint * params); typedef void (GLAD_API_PTR *PFNGLGETQUERYIVARBPROC)(GLenum target, GLenum pname, GLint * params); typedef void (GLAD_API_PTR *PFNGLGETRENDERBUFFERPARAMETERIVPROC)(GLenum target, GLenum pname, GLint * params); typedef void (GLAD_API_PTR *PFNGLGETSAMPLERPARAMETERIIVPROC)(GLuint sampler, GLenum pname, GLint * params); typedef void (GLAD_API_PTR *PFNGLGETSAMPLERPARAMETERIUIVPROC)(GLuint sampler, GLenum pname, GLuint * params); typedef void (GLAD_API_PTR *PFNGLGETSAMPLERPARAMETERFVPROC)(GLuint sampler, GLenum pname, GLfloat * params); typedef void (GLAD_API_PTR *PFNGLGETSAMPLERPARAMETERIVPROC)(GLuint sampler, GLenum pname, GLint * params); typedef void (GLAD_API_PTR *PFNGLGETSEPARABLEFILTERPROC)(GLenum target, GLenum format, GLenum type, void * row, void * column, void * span); typedef void (GLAD_API_PTR *PFNGLGETSHADERINFOLOGPROC)(GLuint shader, GLsizei bufSize, GLsizei * length, GLchar * infoLog); typedef void (GLAD_API_PTR *PFNGLGETSHADERPRECISIONFORMATPROC)(GLenum shadertype, GLenum precisiontype, GLint * range, GLint * precision); typedef void (GLAD_API_PTR *PFNGLGETSHADERSOURCEPROC)(GLuint shader, GLsizei bufSize, GLsizei * length, GLchar * source); typedef void (GLAD_API_PTR *PFNGLGETSHADERSOURCEARBPROC)(GLhandleARB obj, GLsizei maxLength, GLsizei * length, GLcharARB * source); typedef void (GLAD_API_PTR *PFNGLGETSHADERIVPROC)(GLuint shader, GLenum pname, GLint * params); typedef const GLubyte * (GLAD_API_PTR *PFNGLGETSTRINGPROC)(GLenum name); typedef const GLubyte * (GLAD_API_PTR *PFNGLGETSTRINGIPROC)(GLenum name, GLuint index); typedef GLuint (GLAD_API_PTR *PFNGLGETSUBROUTINEINDEXPROC)(GLuint program, GLenum shadertype, const GLchar * name); typedef GLint (GLAD_API_PTR *PFNGLGETSUBROUTINEUNIFORMLOCATIONPROC)(GLuint program, GLenum shadertype, const GLchar * name); typedef void (GLAD_API_PTR *PFNGLGETSYNCIVPROC)(GLsync sync, GLenum pname, GLsizei count, GLsizei * length, GLint * values); typedef void (GLAD_API_PTR *PFNGLGETTEXENVFVPROC)(GLenum target, GLenum pname, GLfloat * params); typedef void (GLAD_API_PTR *PFNGLGETTEXENVIVPROC)(GLenum target, GLenum pname, GLint * params); typedef void (GLAD_API_PTR *PFNGLGETTEXGENDVPROC)(GLenum coord, GLenum pname, GLdouble * params); typedef void (GLAD_API_PTR *PFNGLGETTEXGENFVPROC)(GLenum coord, GLenum pname, GLfloat * params); typedef void (GLAD_API_PTR *PFNGLGETTEXGENIVPROC)(GLenum coord, GLenum pname, GLint * params); typedef void (GLAD_API_PTR *PFNGLGETTEXIMAGEPROC)(GLenum target, GLint level, GLenum format, GLenum type, void * pixels); typedef void (GLAD_API_PTR *PFNGLGETTEXLEVELPARAMETERFVPROC)(GLenum target, GLint level, GLenum pname, GLfloat * params); typedef void (GLAD_API_PTR *PFNGLGETTEXLEVELPARAMETERIVPROC)(GLenum target, GLint level, GLenum pname, GLint * params); typedef void (GLAD_API_PTR *PFNGLGETTEXPARAMETERIIVPROC)(GLenum target, GLenum pname, GLint * params); typedef void (GLAD_API_PTR *PFNGLGETTEXPARAMETERIUIVPROC)(GLenum target, GLenum pname, GLuint * params); typedef void (GLAD_API_PTR *PFNGLGETTEXPARAMETERFVPROC)(GLenum target, GLenum pname, GLfloat * params); typedef void (GLAD_API_PTR *PFNGLGETTEXPARAMETERIVPROC)(GLenum target, GLenum pname, GLint * params); typedef GLuint64 (GLAD_API_PTR *PFNGLGETTEXTUREHANDLEARBPROC)(GLuint texture); typedef void (GLAD_API_PTR *PFNGLGETTEXTUREIMAGEPROC)(GLuint texture, GLint level, GLenum format, GLenum type, GLsizei bufSize, void * pixels); typedef void (GLAD_API_PTR *PFNGLGETTEXTURELEVELPARAMETERFVPROC)(GLuint texture, GLint level, GLenum pname, GLfloat * params); typedef void (GLAD_API_PTR *PFNGLGETTEXTURELEVELPARAMETERIVPROC)(GLuint texture, GLint level, GLenum pname, GLint * params); typedef void (GLAD_API_PTR *PFNGLGETTEXTUREPARAMETERIIVPROC)(GLuint texture, GLenum pname, GLint * params); typedef void (GLAD_API_PTR *PFNGLGETTEXTUREPARAMETERIUIVPROC)(GLuint texture, GLenum pname, GLuint * params); typedef void (GLAD_API_PTR *PFNGLGETTEXTUREPARAMETERFVPROC)(GLuint texture, GLenum pname, GLfloat * params); typedef void (GLAD_API_PTR *PFNGLGETTEXTUREPARAMETERIVPROC)(GLuint texture, GLenum pname, GLint * params); typedef GLuint64 (GLAD_API_PTR *PFNGLGETTEXTURESAMPLERHANDLEARBPROC)(GLuint texture, GLuint sampler); typedef void (GLAD_API_PTR *PFNGLGETTEXTURESUBIMAGEPROC)(GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, GLsizei bufSize, void * pixels); typedef void (GLAD_API_PTR *PFNGLGETTRANSFORMFEEDBACKVARYINGPROC)(GLuint program, GLuint index, GLsizei bufSize, GLsizei * length, GLsizei * size, GLenum * type, GLchar * name); typedef void (GLAD_API_PTR *PFNGLGETTRANSFORMFEEDBACKI64_VPROC)(GLuint xfb, GLenum pname, GLuint index, GLint64 * param); typedef void (GLAD_API_PTR *PFNGLGETTRANSFORMFEEDBACKI_VPROC)(GLuint xfb, GLenum pname, GLuint index, GLint * param); typedef void (GLAD_API_PTR *PFNGLGETTRANSFORMFEEDBACKIVPROC)(GLuint xfb, GLenum pname, GLint * param); typedef GLuint (GLAD_API_PTR *PFNGLGETUNIFORMBLOCKINDEXPROC)(GLuint program, const GLchar * uniformBlockName); typedef void (GLAD_API_PTR *PFNGLGETUNIFORMINDICESPROC)(GLuint program, GLsizei uniformCount, const GLchar *const* uniformNames, GLuint * uniformIndices); typedef GLint (GLAD_API_PTR *PFNGLGETUNIFORMLOCATIONPROC)(GLuint program, const GLchar * name); typedef GLint (GLAD_API_PTR *PFNGLGETUNIFORMLOCATIONARBPROC)(GLhandleARB programObj, const GLcharARB * name); typedef void (GLAD_API_PTR *PFNGLGETUNIFORMSUBROUTINEUIVPROC)(GLenum shadertype, GLint location, GLuint * params); typedef void (GLAD_API_PTR *PFNGLGETUNIFORMDVPROC)(GLuint program, GLint location, GLdouble * params); typedef void (GLAD_API_PTR *PFNGLGETUNIFORMFVPROC)(GLuint program, GLint location, GLfloat * params); typedef void (GLAD_API_PTR *PFNGLGETUNIFORMFVARBPROC)(GLhandleARB programObj, GLint location, GLfloat * params); typedef void (GLAD_API_PTR *PFNGLGETUNIFORMI64VARBPROC)(GLuint program, GLint location, GLint64 * params); typedef void (GLAD_API_PTR *PFNGLGETUNIFORMIVPROC)(GLuint program, GLint location, GLint * params); typedef void (GLAD_API_PTR *PFNGLGETUNIFORMIVARBPROC)(GLhandleARB programObj, GLint location, GLint * params); typedef void (GLAD_API_PTR *PFNGLGETUNIFORMUI64VARBPROC)(GLuint program, GLint location, GLuint64 * params); typedef void (GLAD_API_PTR *PFNGLGETUNIFORMUIVPROC)(GLuint program, GLint location, GLuint * params); typedef void (GLAD_API_PTR *PFNGLGETVERTEXARRAYINDEXED64IVPROC)(GLuint vaobj, GLuint index, GLenum pname, GLint64 * param); typedef void (GLAD_API_PTR *PFNGLGETVERTEXARRAYINDEXEDIVPROC)(GLuint vaobj, GLuint index, GLenum pname, GLint * param); typedef void (GLAD_API_PTR *PFNGLGETVERTEXARRAYIVPROC)(GLuint vaobj, GLenum pname, GLint * param); typedef void (GLAD_API_PTR *PFNGLGETVERTEXATTRIBIIVPROC)(GLuint index, GLenum pname, GLint * params); typedef void (GLAD_API_PTR *PFNGLGETVERTEXATTRIBIUIVPROC)(GLuint index, GLenum pname, GLuint * params); typedef void (GLAD_API_PTR *PFNGLGETVERTEXATTRIBLDVPROC)(GLuint index, GLenum pname, GLdouble * params); typedef void (GLAD_API_PTR *PFNGLGETVERTEXATTRIBLUI64VARBPROC)(GLuint index, GLenum pname, GLuint64EXT * params); typedef void (GLAD_API_PTR *PFNGLGETVERTEXATTRIBPOINTERVPROC)(GLuint index, GLenum pname, void ** pointer); typedef void (GLAD_API_PTR *PFNGLGETVERTEXATTRIBPOINTERVARBPROC)(GLuint index, GLenum pname, void ** pointer); typedef void (GLAD_API_PTR *PFNGLGETVERTEXATTRIBDVPROC)(GLuint index, GLenum pname, GLdouble * params); typedef void (GLAD_API_PTR *PFNGLGETVERTEXATTRIBDVARBPROC)(GLuint index, GLenum pname, GLdouble * params); typedef void (GLAD_API_PTR *PFNGLGETVERTEXATTRIBFVPROC)(GLuint index, GLenum pname, GLfloat * params); typedef void (GLAD_API_PTR *PFNGLGETVERTEXATTRIBFVARBPROC)(GLuint index, GLenum pname, GLfloat * params); typedef void (GLAD_API_PTR *PFNGLGETVERTEXATTRIBIVPROC)(GLuint index, GLenum pname, GLint * params); typedef void (GLAD_API_PTR *PFNGLGETVERTEXATTRIBIVARBPROC)(GLuint index, GLenum pname, GLint * params); typedef void (GLAD_API_PTR *PFNGLGETNCOLORTABLEARBPROC)(GLenum target, GLenum format, GLenum type, GLsizei bufSize, void * table); typedef void (GLAD_API_PTR *PFNGLGETNCOMPRESSEDTEXIMAGEARBPROC)(GLenum target, GLint lod, GLsizei bufSize, void * img); typedef void (GLAD_API_PTR *PFNGLGETNCONVOLUTIONFILTERARBPROC)(GLenum target, GLenum format, GLenum type, GLsizei bufSize, void * image); typedef void (GLAD_API_PTR *PFNGLGETNHISTOGRAMARBPROC)(GLenum target, GLboolean reset, GLenum format, GLenum type, GLsizei bufSize, void * values); typedef void (GLAD_API_PTR *PFNGLGETNMAPDVARBPROC)(GLenum target, GLenum query, GLsizei bufSize, GLdouble * v); typedef void (GLAD_API_PTR *PFNGLGETNMAPFVARBPROC)(GLenum target, GLenum query, GLsizei bufSize, GLfloat * v); typedef void (GLAD_API_PTR *PFNGLGETNMAPIVARBPROC)(GLenum target, GLenum query, GLsizei bufSize, GLint * v); typedef void (GLAD_API_PTR *PFNGLGETNMINMAXARBPROC)(GLenum target, GLboolean reset, GLenum format, GLenum type, GLsizei bufSize, void * values); typedef void (GLAD_API_PTR *PFNGLGETNPIXELMAPFVARBPROC)(GLenum map, GLsizei bufSize, GLfloat * values); typedef void (GLAD_API_PTR *PFNGLGETNPIXELMAPUIVARBPROC)(GLenum map, GLsizei bufSize, GLuint * values); typedef void (GLAD_API_PTR *PFNGLGETNPIXELMAPUSVARBPROC)(GLenum map, GLsizei bufSize, GLushort * values); typedef void (GLAD_API_PTR *PFNGLGETNPOLYGONSTIPPLEARBPROC)(GLsizei bufSize, GLubyte * pattern); typedef void (GLAD_API_PTR *PFNGLGETNSEPARABLEFILTERARBPROC)(GLenum target, GLenum format, GLenum type, GLsizei rowBufSize, void * row, GLsizei columnBufSize, void * column, void * span); typedef void (GLAD_API_PTR *PFNGLGETNTEXIMAGEARBPROC)(GLenum target, GLint level, GLenum format, GLenum type, GLsizei bufSize, void * img); typedef void (GLAD_API_PTR *PFNGLGETNUNIFORMDVARBPROC)(GLuint program, GLint location, GLsizei bufSize, GLdouble * params); typedef void (GLAD_API_PTR *PFNGLGETNUNIFORMFVPROC)(GLuint program, GLint location, GLsizei bufSize, GLfloat * params); typedef void (GLAD_API_PTR *PFNGLGETNUNIFORMFVARBPROC)(GLuint program, GLint location, GLsizei bufSize, GLfloat * params); typedef void (GLAD_API_PTR *PFNGLGETNUNIFORMI64VARBPROC)(GLuint program, GLint location, GLsizei bufSize, GLint64 * params); typedef void (GLAD_API_PTR *PFNGLGETNUNIFORMIVPROC)(GLuint program, GLint location, GLsizei bufSize, GLint * params); typedef void (GLAD_API_PTR *PFNGLGETNUNIFORMIVARBPROC)(GLuint program, GLint location, GLsizei bufSize, GLint * params); typedef void (GLAD_API_PTR *PFNGLGETNUNIFORMUI64VARBPROC)(GLuint program, GLint location, GLsizei bufSize, GLuint64 * params); typedef void (GLAD_API_PTR *PFNGLGETNUNIFORMUIVPROC)(GLuint program, GLint location, GLsizei bufSize, GLuint * params); typedef void (GLAD_API_PTR *PFNGLGETNUNIFORMUIVARBPROC)(GLuint program, GLint location, GLsizei bufSize, GLuint * params); typedef void (GLAD_API_PTR *PFNGLHINTPROC)(GLenum target, GLenum mode); typedef void (GLAD_API_PTR *PFNGLHISTOGRAMPROC)(GLenum target, GLsizei width, GLenum internalformat, GLboolean sink); typedef void (GLAD_API_PTR *PFNGLINDEXMASKPROC)(GLuint mask); typedef void (GLAD_API_PTR *PFNGLINDEXPOINTERPROC)(GLenum type, GLsizei stride, const void * pointer); typedef void (GLAD_API_PTR *PFNGLINDEXDPROC)(GLdouble c); typedef void (GLAD_API_PTR *PFNGLINDEXDVPROC)(const GLdouble * c); typedef void (GLAD_API_PTR *PFNGLINDEXFPROC)(GLfloat c); typedef void (GLAD_API_PTR *PFNGLINDEXFVPROC)(const GLfloat * c); typedef void (GLAD_API_PTR *PFNGLINDEXIPROC)(GLint c); typedef void (GLAD_API_PTR *PFNGLINDEXIVPROC)(const GLint * c); typedef void (GLAD_API_PTR *PFNGLINDEXSPROC)(GLshort c); typedef void (GLAD_API_PTR *PFNGLINDEXSVPROC)(const GLshort * c); typedef void (GLAD_API_PTR *PFNGLINDEXUBPROC)(GLubyte c); typedef void (GLAD_API_PTR *PFNGLINDEXUBVPROC)(const GLubyte * c); typedef void (GLAD_API_PTR *PFNGLINITNAMESPROC)(void); typedef void (GLAD_API_PTR *PFNGLINTERLEAVEDARRAYSPROC)(GLenum format, GLsizei stride, const void * pointer); typedef void (GLAD_API_PTR *PFNGLINVALIDATEBUFFERDATAPROC)(GLuint buffer); typedef void (GLAD_API_PTR *PFNGLINVALIDATEBUFFERSUBDATAPROC)(GLuint buffer, GLintptr offset, GLsizeiptr length); typedef void (GLAD_API_PTR *PFNGLINVALIDATEFRAMEBUFFERPROC)(GLenum target, GLsizei numAttachments, const GLenum * attachments); typedef void (GLAD_API_PTR *PFNGLINVALIDATENAMEDFRAMEBUFFERDATAPROC)(GLuint framebuffer, GLsizei numAttachments, const GLenum * attachments); typedef void (GLAD_API_PTR *PFNGLINVALIDATENAMEDFRAMEBUFFERSUBDATAPROC)(GLuint framebuffer, GLsizei numAttachments, const GLenum * attachments, GLint x, GLint y, GLsizei width, GLsizei height); typedef void (GLAD_API_PTR *PFNGLINVALIDATESUBFRAMEBUFFERPROC)(GLenum target, GLsizei numAttachments, const GLenum * attachments, GLint x, GLint y, GLsizei width, GLsizei height); typedef void (GLAD_API_PTR *PFNGLINVALIDATETEXIMAGEPROC)(GLuint texture, GLint level); typedef void (GLAD_API_PTR *PFNGLINVALIDATETEXSUBIMAGEPROC)(GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth); typedef GLboolean (GLAD_API_PTR *PFNGLISBUFFERPROC)(GLuint buffer); typedef GLboolean (GLAD_API_PTR *PFNGLISBUFFERARBPROC)(GLuint buffer); typedef GLboolean (GLAD_API_PTR *PFNGLISENABLEDPROC)(GLenum cap); typedef GLboolean (GLAD_API_PTR *PFNGLISENABLEDIPROC)(GLenum target, GLuint index); typedef GLboolean (GLAD_API_PTR *PFNGLISFRAMEBUFFERPROC)(GLuint framebuffer); typedef GLboolean (GLAD_API_PTR *PFNGLISIMAGEHANDLERESIDENTARBPROC)(GLuint64 handle); typedef GLboolean (GLAD_API_PTR *PFNGLISLISTPROC)(GLuint list); typedef GLboolean (GLAD_API_PTR *PFNGLISNAMEDSTRINGARBPROC)(GLint namelen, const GLchar * name); typedef GLboolean (GLAD_API_PTR *PFNGLISPROGRAMPROC)(GLuint program); typedef GLboolean (GLAD_API_PTR *PFNGLISPROGRAMARBPROC)(GLuint program); typedef GLboolean (GLAD_API_PTR *PFNGLISPROGRAMPIPELINEPROC)(GLuint pipeline); typedef GLboolean (GLAD_API_PTR *PFNGLISQUERYPROC)(GLuint id); typedef GLboolean (GLAD_API_PTR *PFNGLISQUERYARBPROC)(GLuint id); typedef GLboolean (GLAD_API_PTR *PFNGLISRENDERBUFFERPROC)(GLuint renderbuffer); typedef GLboolean (GLAD_API_PTR *PFNGLISSAMPLERPROC)(GLuint sampler); typedef GLboolean (GLAD_API_PTR *PFNGLISSHADERPROC)(GLuint shader); typedef GLboolean (GLAD_API_PTR *PFNGLISSYNCPROC)(GLsync sync); typedef GLboolean (GLAD_API_PTR *PFNGLISTEXTUREPROC)(GLuint texture); typedef GLboolean (GLAD_API_PTR *PFNGLISTEXTUREHANDLERESIDENTARBPROC)(GLuint64 handle); typedef GLboolean (GLAD_API_PTR *PFNGLISTRANSFORMFEEDBACKPROC)(GLuint id); typedef GLboolean (GLAD_API_PTR *PFNGLISVERTEXARRAYPROC)(GLuint array); typedef void (GLAD_API_PTR *PFNGLLIGHTMODELFPROC)(GLenum pname, GLfloat param); typedef void (GLAD_API_PTR *PFNGLLIGHTMODELFVPROC)(GLenum pname, const GLfloat * params); typedef void (GLAD_API_PTR *PFNGLLIGHTMODELIPROC)(GLenum pname, GLint param); typedef void (GLAD_API_PTR *PFNGLLIGHTMODELIVPROC)(GLenum pname, const GLint * params); typedef void (GLAD_API_PTR *PFNGLLIGHTFPROC)(GLenum light, GLenum pname, GLfloat param); typedef void (GLAD_API_PTR *PFNGLLIGHTFVPROC)(GLenum light, GLenum pname, const GLfloat * params); typedef void (GLAD_API_PTR *PFNGLLIGHTIPROC)(GLenum light, GLenum pname, GLint param); typedef void (GLAD_API_PTR *PFNGLLIGHTIVPROC)(GLenum light, GLenum pname, const GLint * params); typedef void (GLAD_API_PTR *PFNGLLINESTIPPLEPROC)(GLint factor, GLushort pattern); typedef void (GLAD_API_PTR *PFNGLLINEWIDTHPROC)(GLfloat width); typedef void (GLAD_API_PTR *PFNGLLINKPROGRAMPROC)(GLuint program); typedef void (GLAD_API_PTR *PFNGLLINKPROGRAMARBPROC)(GLhandleARB programObj); typedef void (GLAD_API_PTR *PFNGLLISTBASEPROC)(GLuint base); typedef void (GLAD_API_PTR *PFNGLLOADIDENTITYPROC)(void); typedef void (GLAD_API_PTR *PFNGLLOADMATRIXDPROC)(const GLdouble * m); typedef void (GLAD_API_PTR *PFNGLLOADMATRIXFPROC)(const GLfloat * m); typedef void (GLAD_API_PTR *PFNGLLOADNAMEPROC)(GLuint name); typedef void (GLAD_API_PTR *PFNGLLOADTRANSPOSEMATRIXDPROC)(const GLdouble * m); typedef void (GLAD_API_PTR *PFNGLLOADTRANSPOSEMATRIXDARBPROC)(const GLdouble * m); typedef void (GLAD_API_PTR *PFNGLLOADTRANSPOSEMATRIXFPROC)(const GLfloat * m); typedef void (GLAD_API_PTR *PFNGLLOADTRANSPOSEMATRIXFARBPROC)(const GLfloat * m); typedef void (GLAD_API_PTR *PFNGLLOGICOPPROC)(GLenum opcode); typedef void (GLAD_API_PTR *PFNGLMAKEIMAGEHANDLENONRESIDENTARBPROC)(GLuint64 handle); typedef void (GLAD_API_PTR *PFNGLMAKEIMAGEHANDLERESIDENTARBPROC)(GLuint64 handle, GLenum access); typedef void (GLAD_API_PTR *PFNGLMAKETEXTUREHANDLENONRESIDENTARBPROC)(GLuint64 handle); typedef void (GLAD_API_PTR *PFNGLMAKETEXTUREHANDLERESIDENTARBPROC)(GLuint64 handle); typedef void (GLAD_API_PTR *PFNGLMAP1DPROC)(GLenum target, GLdouble u1, GLdouble u2, GLint stride, GLint order, const GLdouble * points); typedef void (GLAD_API_PTR *PFNGLMAP1FPROC)(GLenum target, GLfloat u1, GLfloat u2, GLint stride, GLint order, const GLfloat * points); typedef void (GLAD_API_PTR *PFNGLMAP2DPROC)(GLenum target, GLdouble u1, GLdouble u2, GLint ustride, GLint uorder, GLdouble v1, GLdouble v2, GLint vstride, GLint vorder, const GLdouble * points); typedef void (GLAD_API_PTR *PFNGLMAP2FPROC)(GLenum target, GLfloat u1, GLfloat u2, GLint ustride, GLint uorder, GLfloat v1, GLfloat v2, GLint vstride, GLint vorder, const GLfloat * points); typedef void * (GLAD_API_PTR *PFNGLMAPBUFFERPROC)(GLenum target, GLenum access); typedef void * (GLAD_API_PTR *PFNGLMAPBUFFERARBPROC)(GLenum target, GLenum access); typedef void * (GLAD_API_PTR *PFNGLMAPBUFFERRANGEPROC)(GLenum target, GLintptr offset, GLsizeiptr length, GLbitfield access); typedef void (GLAD_API_PTR *PFNGLMAPGRID1DPROC)(GLint un, GLdouble u1, GLdouble u2); typedef void (GLAD_API_PTR *PFNGLMAPGRID1FPROC)(GLint un, GLfloat u1, GLfloat u2); typedef void (GLAD_API_PTR *PFNGLMAPGRID2DPROC)(GLint un, GLdouble u1, GLdouble u2, GLint vn, GLdouble v1, GLdouble v2); typedef void (GLAD_API_PTR *PFNGLMAPGRID2FPROC)(GLint un, GLfloat u1, GLfloat u2, GLint vn, GLfloat v1, GLfloat v2); typedef void * (GLAD_API_PTR *PFNGLMAPNAMEDBUFFERPROC)(GLuint buffer, GLenum access); typedef void * (GLAD_API_PTR *PFNGLMAPNAMEDBUFFERRANGEPROC)(GLuint buffer, GLintptr offset, GLsizeiptr length, GLbitfield access); typedef void (GLAD_API_PTR *PFNGLMATERIALFPROC)(GLenum face, GLenum pname, GLfloat param); typedef void (GLAD_API_PTR *PFNGLMATERIALFVPROC)(GLenum face, GLenum pname, const GLfloat * params); typedef void (GLAD_API_PTR *PFNGLMATERIALIPROC)(GLenum face, GLenum pname, GLint param); typedef void (GLAD_API_PTR *PFNGLMATERIALIVPROC)(GLenum face, GLenum pname, const GLint * params); typedef void (GLAD_API_PTR *PFNGLMATRIXINDEXPOINTERARBPROC)(GLint size, GLenum type, GLsizei stride, const void * pointer); typedef void (GLAD_API_PTR *PFNGLMATRIXINDEXUBVARBPROC)(GLint size, const GLubyte * indices); typedef void (GLAD_API_PTR *PFNGLMATRIXINDEXUIVARBPROC)(GLint size, const GLuint * indices); typedef void (GLAD_API_PTR *PFNGLMATRIXINDEXUSVARBPROC)(GLint size, const GLushort * indices); typedef void (GLAD_API_PTR *PFNGLMATRIXMODEPROC)(GLenum mode); typedef void (GLAD_API_PTR *PFNGLMAXSHADERCOMPILERTHREADSARBPROC)(GLuint count); typedef void (GLAD_API_PTR *PFNGLMAXSHADERCOMPILERTHREADSKHRPROC)(GLuint count); typedef void (GLAD_API_PTR *PFNGLMEMORYBARRIERPROC)(GLbitfield barriers); typedef void (GLAD_API_PTR *PFNGLMEMORYBARRIERBYREGIONPROC)(GLbitfield barriers); typedef void (GLAD_API_PTR *PFNGLMINSAMPLESHADINGPROC)(GLfloat value); typedef void (GLAD_API_PTR *PFNGLMINSAMPLESHADINGARBPROC)(GLfloat value); typedef void (GLAD_API_PTR *PFNGLMINMAXPROC)(GLenum target, GLenum internalformat, GLboolean sink); typedef void (GLAD_API_PTR *PFNGLMULTMATRIXDPROC)(const GLdouble * m); typedef void (GLAD_API_PTR *PFNGLMULTMATRIXFPROC)(const GLfloat * m); typedef void (GLAD_API_PTR *PFNGLMULTTRANSPOSEMATRIXDPROC)(const GLdouble * m); typedef void (GLAD_API_PTR *PFNGLMULTTRANSPOSEMATRIXDARBPROC)(const GLdouble * m); typedef void (GLAD_API_PTR *PFNGLMULTTRANSPOSEMATRIXFPROC)(const GLfloat * m); typedef void (GLAD_API_PTR *PFNGLMULTTRANSPOSEMATRIXFARBPROC)(const GLfloat * m); typedef void (GLAD_API_PTR *PFNGLMULTIDRAWARRAYSPROC)(GLenum mode, const GLint * first, const GLsizei * count, GLsizei drawcount); typedef void (GLAD_API_PTR *PFNGLMULTIDRAWARRAYSINDIRECTPROC)(GLenum mode, const void * indirect, GLsizei drawcount, GLsizei stride); typedef void (GLAD_API_PTR *PFNGLMULTIDRAWARRAYSINDIRECTCOUNTPROC)(GLenum mode, const void * indirect, GLintptr drawcount, GLsizei maxdrawcount, GLsizei stride); typedef void (GLAD_API_PTR *PFNGLMULTIDRAWARRAYSINDIRECTCOUNTARBPROC)(GLenum mode, const void * indirect, GLintptr drawcount, GLsizei maxdrawcount, GLsizei stride); typedef void (GLAD_API_PTR *PFNGLMULTIDRAWELEMENTSPROC)(GLenum mode, const GLsizei * count, GLenum type, const void *const* indices, GLsizei drawcount); typedef void (GLAD_API_PTR *PFNGLMULTIDRAWELEMENTSBASEVERTEXPROC)(GLenum mode, const GLsizei * count, GLenum type, const void *const* indices, GLsizei drawcount, const GLint * basevertex); typedef void (GLAD_API_PTR *PFNGLMULTIDRAWELEMENTSINDIRECTPROC)(GLenum mode, GLenum type, const void * indirect, GLsizei drawcount, GLsizei stride); typedef void (GLAD_API_PTR *PFNGLMULTIDRAWELEMENTSINDIRECTCOUNTPROC)(GLenum mode, GLenum type, const void * indirect, GLintptr drawcount, GLsizei maxdrawcount, GLsizei stride); typedef void (GLAD_API_PTR *PFNGLMULTIDRAWELEMENTSINDIRECTCOUNTARBPROC)(GLenum mode, GLenum type, const void * indirect, GLintptr drawcount, GLsizei maxdrawcount, GLsizei stride); typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD1DPROC)(GLenum target, GLdouble s); typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD1DARBPROC)(GLenum target, GLdouble s); typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD1DVPROC)(GLenum target, const GLdouble * v); typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD1DVARBPROC)(GLenum target, const GLdouble * v); typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD1FPROC)(GLenum target, GLfloat s); typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD1FARBPROC)(GLenum target, GLfloat s); typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD1FVPROC)(GLenum target, const GLfloat * v); typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD1FVARBPROC)(GLenum target, const GLfloat * v); typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD1IPROC)(GLenum target, GLint s); typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD1IARBPROC)(GLenum target, GLint s); typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD1IVPROC)(GLenum target, const GLint * v); typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD1IVARBPROC)(GLenum target, const GLint * v); typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD1SPROC)(GLenum target, GLshort s); typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD1SARBPROC)(GLenum target, GLshort s); typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD1SVPROC)(GLenum target, const GLshort * v); typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD1SVARBPROC)(GLenum target, const GLshort * v); typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD2DPROC)(GLenum target, GLdouble s, GLdouble t); typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD2DARBPROC)(GLenum target, GLdouble s, GLdouble t); typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD2DVPROC)(GLenum target, const GLdouble * v); typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD2DVARBPROC)(GLenum target, const GLdouble * v); typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD2FPROC)(GLenum target, GLfloat s, GLfloat t); typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD2FARBPROC)(GLenum target, GLfloat s, GLfloat t); typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD2FVPROC)(GLenum target, const GLfloat * v); typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD2FVARBPROC)(GLenum target, const GLfloat * v); typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD2IPROC)(GLenum target, GLint s, GLint t); typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD2IARBPROC)(GLenum target, GLint s, GLint t); typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD2IVPROC)(GLenum target, const GLint * v); typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD2IVARBPROC)(GLenum target, const GLint * v); typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD2SPROC)(GLenum target, GLshort s, GLshort t); typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD2SARBPROC)(GLenum target, GLshort s, GLshort t); typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD2SVPROC)(GLenum target, const GLshort * v); typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD2SVARBPROC)(GLenum target, const GLshort * v); typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD3DPROC)(GLenum target, GLdouble s, GLdouble t, GLdouble r); typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD3DARBPROC)(GLenum target, GLdouble s, GLdouble t, GLdouble r); typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD3DVPROC)(GLenum target, const GLdouble * v); typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD3DVARBPROC)(GLenum target, const GLdouble * v); typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD3FPROC)(GLenum target, GLfloat s, GLfloat t, GLfloat r); typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD3FARBPROC)(GLenum target, GLfloat s, GLfloat t, GLfloat r); typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD3FVPROC)(GLenum target, const GLfloat * v); typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD3FVARBPROC)(GLenum target, const GLfloat * v); typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD3IPROC)(GLenum target, GLint s, GLint t, GLint r); typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD3IARBPROC)(GLenum target, GLint s, GLint t, GLint r); typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD3IVPROC)(GLenum target, const GLint * v); typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD3IVARBPROC)(GLenum target, const GLint * v); typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD3SPROC)(GLenum target, GLshort s, GLshort t, GLshort r); typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD3SARBPROC)(GLenum target, GLshort s, GLshort t, GLshort r); typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD3SVPROC)(GLenum target, const GLshort * v); typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD3SVARBPROC)(GLenum target, const GLshort * v); typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD4DPROC)(GLenum target, GLdouble s, GLdouble t, GLdouble r, GLdouble q); typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD4DARBPROC)(GLenum target, GLdouble s, GLdouble t, GLdouble r, GLdouble q); typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD4DVPROC)(GLenum target, const GLdouble * v); typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD4DVARBPROC)(GLenum target, const GLdouble * v); typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD4FPROC)(GLenum target, GLfloat s, GLfloat t, GLfloat r, GLfloat q); typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD4FARBPROC)(GLenum target, GLfloat s, GLfloat t, GLfloat r, GLfloat q); typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD4FVPROC)(GLenum target, const GLfloat * v); typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD4FVARBPROC)(GLenum target, const GLfloat * v); typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD4IPROC)(GLenum target, GLint s, GLint t, GLint r, GLint q); typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD4IARBPROC)(GLenum target, GLint s, GLint t, GLint r, GLint q); typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD4IVPROC)(GLenum target, const GLint * v); typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD4IVARBPROC)(GLenum target, const GLint * v); typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD4SPROC)(GLenum target, GLshort s, GLshort t, GLshort r, GLshort q); typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD4SARBPROC)(GLenum target, GLshort s, GLshort t, GLshort r, GLshort q); typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD4SVPROC)(GLenum target, const GLshort * v); typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORD4SVARBPROC)(GLenum target, const GLshort * v); typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORDP1UIPROC)(GLenum texture, GLenum type, GLuint coords); typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORDP1UIVPROC)(GLenum texture, GLenum type, const GLuint * coords); typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORDP2UIPROC)(GLenum texture, GLenum type, GLuint coords); typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORDP2UIVPROC)(GLenum texture, GLenum type, const GLuint * coords); typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORDP3UIPROC)(GLenum texture, GLenum type, GLuint coords); typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORDP3UIVPROC)(GLenum texture, GLenum type, const GLuint * coords); typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORDP4UIPROC)(GLenum texture, GLenum type, GLuint coords); typedef void (GLAD_API_PTR *PFNGLMULTITEXCOORDP4UIVPROC)(GLenum texture, GLenum type, const GLuint * coords); typedef void (GLAD_API_PTR *PFNGLNAMEDBUFFERDATAPROC)(GLuint buffer, GLsizeiptr size, const void * data, GLenum usage); typedef void (GLAD_API_PTR *PFNGLNAMEDBUFFERPAGECOMMITMENTARBPROC)(GLuint buffer, GLintptr offset, GLsizeiptr size, GLboolean commit); typedef void (GLAD_API_PTR *PFNGLNAMEDBUFFERPAGECOMMITMENTEXTPROC)(GLuint buffer, GLintptr offset, GLsizeiptr size, GLboolean commit); typedef void (GLAD_API_PTR *PFNGLNAMEDBUFFERSTORAGEPROC)(GLuint buffer, GLsizeiptr size, const void * data, GLbitfield flags); typedef void (GLAD_API_PTR *PFNGLNAMEDBUFFERSUBDATAPROC)(GLuint buffer, GLintptr offset, GLsizeiptr size, const void * data); typedef void (GLAD_API_PTR *PFNGLNAMEDFRAMEBUFFERDRAWBUFFERPROC)(GLuint framebuffer, GLenum buf); typedef void (GLAD_API_PTR *PFNGLNAMEDFRAMEBUFFERDRAWBUFFERSPROC)(GLuint framebuffer, GLsizei n, const GLenum * bufs); typedef void (GLAD_API_PTR *PFNGLNAMEDFRAMEBUFFERPARAMETERIPROC)(GLuint framebuffer, GLenum pname, GLint param); typedef void (GLAD_API_PTR *PFNGLNAMEDFRAMEBUFFERREADBUFFERPROC)(GLuint framebuffer, GLenum src); typedef void (GLAD_API_PTR *PFNGLNAMEDFRAMEBUFFERRENDERBUFFERPROC)(GLuint framebuffer, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer); typedef void (GLAD_API_PTR *PFNGLNAMEDFRAMEBUFFERSAMPLELOCATIONSFVARBPROC)(GLuint framebuffer, GLuint start, GLsizei count, const GLfloat * v); typedef void (GLAD_API_PTR *PFNGLNAMEDFRAMEBUFFERTEXTUREPROC)(GLuint framebuffer, GLenum attachment, GLuint texture, GLint level); typedef void (GLAD_API_PTR *PFNGLNAMEDFRAMEBUFFERTEXTURELAYERPROC)(GLuint framebuffer, GLenum attachment, GLuint texture, GLint level, GLint layer); typedef void (GLAD_API_PTR *PFNGLNAMEDRENDERBUFFERSTORAGEPROC)(GLuint renderbuffer, GLenum internalformat, GLsizei width, GLsizei height); typedef void (GLAD_API_PTR *PFNGLNAMEDRENDERBUFFERSTORAGEMULTISAMPLEPROC)(GLuint renderbuffer, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height); typedef void (GLAD_API_PTR *PFNGLNAMEDSTRINGARBPROC)(GLenum type, GLint namelen, const GLchar * name, GLint stringlen, const GLchar * string); typedef void (GLAD_API_PTR *PFNGLNEWLISTPROC)(GLuint list, GLenum mode); typedef void (GLAD_API_PTR *PFNGLNORMAL3BPROC)(GLbyte nx, GLbyte ny, GLbyte nz); typedef void (GLAD_API_PTR *PFNGLNORMAL3BVPROC)(const GLbyte * v); typedef void (GLAD_API_PTR *PFNGLNORMAL3DPROC)(GLdouble nx, GLdouble ny, GLdouble nz); typedef void (GLAD_API_PTR *PFNGLNORMAL3DVPROC)(const GLdouble * v); typedef void (GLAD_API_PTR *PFNGLNORMAL3FPROC)(GLfloat nx, GLfloat ny, GLfloat nz); typedef void (GLAD_API_PTR *PFNGLNORMAL3FVPROC)(const GLfloat * v); typedef void (GLAD_API_PTR *PFNGLNORMAL3IPROC)(GLint nx, GLint ny, GLint nz); typedef void (GLAD_API_PTR *PFNGLNORMAL3IVPROC)(const GLint * v); typedef void (GLAD_API_PTR *PFNGLNORMAL3SPROC)(GLshort nx, GLshort ny, GLshort nz); typedef void (GLAD_API_PTR *PFNGLNORMAL3SVPROC)(const GLshort * v); typedef void (GLAD_API_PTR *PFNGLNORMALP3UIPROC)(GLenum type, GLuint coords); typedef void (GLAD_API_PTR *PFNGLNORMALP3UIVPROC)(GLenum type, const GLuint * coords); typedef void (GLAD_API_PTR *PFNGLNORMALPOINTERPROC)(GLenum type, GLsizei stride, const void * pointer); typedef void (GLAD_API_PTR *PFNGLOBJECTLABELPROC)(GLenum identifier, GLuint name, GLsizei length, const GLchar * label); typedef void (GLAD_API_PTR *PFNGLOBJECTPTRLABELPROC)(const void * ptr, GLsizei length, const GLchar * label); typedef void (GLAD_API_PTR *PFNGLORTHOPROC)(GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble zNear, GLdouble zFar); typedef void (GLAD_API_PTR *PFNGLPASSTHROUGHPROC)(GLfloat token); typedef void (GLAD_API_PTR *PFNGLPATCHPARAMETERFVPROC)(GLenum pname, const GLfloat * values); typedef void (GLAD_API_PTR *PFNGLPATCHPARAMETERIPROC)(GLenum pname, GLint value); typedef void (GLAD_API_PTR *PFNGLPAUSETRANSFORMFEEDBACKPROC)(void); typedef void (GLAD_API_PTR *PFNGLPIXELMAPFVPROC)(GLenum map, GLsizei mapsize, const GLfloat * values); typedef void (GLAD_API_PTR *PFNGLPIXELMAPUIVPROC)(GLenum map, GLsizei mapsize, const GLuint * values); typedef void (GLAD_API_PTR *PFNGLPIXELMAPUSVPROC)(GLenum map, GLsizei mapsize, const GLushort * values); typedef void (GLAD_API_PTR *PFNGLPIXELSTOREFPROC)(GLenum pname, GLfloat param); typedef void (GLAD_API_PTR *PFNGLPIXELSTOREIPROC)(GLenum pname, GLint param); typedef void (GLAD_API_PTR *PFNGLPIXELTRANSFERFPROC)(GLenum pname, GLfloat param); typedef void (GLAD_API_PTR *PFNGLPIXELTRANSFERIPROC)(GLenum pname, GLint param); typedef void (GLAD_API_PTR *PFNGLPIXELZOOMPROC)(GLfloat xfactor, GLfloat yfactor); typedef void (GLAD_API_PTR *PFNGLPOINTPARAMETERFPROC)(GLenum pname, GLfloat param); typedef void (GLAD_API_PTR *PFNGLPOINTPARAMETERFARBPROC)(GLenum pname, GLfloat param); typedef void (GLAD_API_PTR *PFNGLPOINTPARAMETERFVPROC)(GLenum pname, const GLfloat * params); typedef void (GLAD_API_PTR *PFNGLPOINTPARAMETERFVARBPROC)(GLenum pname, const GLfloat * params); typedef void (GLAD_API_PTR *PFNGLPOINTPARAMETERIPROC)(GLenum pname, GLint param); typedef void (GLAD_API_PTR *PFNGLPOINTPARAMETERIVPROC)(GLenum pname, const GLint * params); typedef void (GLAD_API_PTR *PFNGLPOINTSIZEPROC)(GLfloat size); typedef void (GLAD_API_PTR *PFNGLPOLYGONMODEPROC)(GLenum face, GLenum mode); typedef void (GLAD_API_PTR *PFNGLPOLYGONOFFSETPROC)(GLfloat factor, GLfloat units); typedef void (GLAD_API_PTR *PFNGLPOLYGONOFFSETCLAMPPROC)(GLfloat factor, GLfloat units, GLfloat clamp); typedef void (GLAD_API_PTR *PFNGLPOLYGONSTIPPLEPROC)(const GLubyte * mask); typedef void (GLAD_API_PTR *PFNGLPOPATTRIBPROC)(void); typedef void (GLAD_API_PTR *PFNGLPOPCLIENTATTRIBPROC)(void); typedef void (GLAD_API_PTR *PFNGLPOPDEBUGGROUPPROC)(void); typedef void (GLAD_API_PTR *PFNGLPOPMATRIXPROC)(void); typedef void (GLAD_API_PTR *PFNGLPOPNAMEPROC)(void); typedef void (GLAD_API_PTR *PFNGLPRIMITIVEBOUNDINGBOXPROC)(GLfloat minX, GLfloat minY, GLfloat minZ, GLfloat minW, GLfloat maxX, GLfloat maxY, GLfloat maxZ, GLfloat maxW); typedef void (GLAD_API_PTR *PFNGLPRIMITIVEBOUNDINGBOXARBPROC)(GLfloat minX, GLfloat minY, GLfloat minZ, GLfloat minW, GLfloat maxX, GLfloat maxY, GLfloat maxZ, GLfloat maxW); typedef void (GLAD_API_PTR *PFNGLPRIMITIVERESTARTINDEXPROC)(GLuint index); typedef void (GLAD_API_PTR *PFNGLPRIORITIZETEXTURESPROC)(GLsizei n, const GLuint * textures, const GLfloat * priorities); typedef void (GLAD_API_PTR *PFNGLPROGRAMBINARYPROC)(GLuint program, GLenum binaryFormat, const void * binary, GLsizei length); typedef void (GLAD_API_PTR *PFNGLPROGRAMENVPARAMETER4DARBPROC)(GLenum target, GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); typedef void (GLAD_API_PTR *PFNGLPROGRAMENVPARAMETER4DVARBPROC)(GLenum target, GLuint index, const GLdouble * params); typedef void (GLAD_API_PTR *PFNGLPROGRAMENVPARAMETER4FARBPROC)(GLenum target, GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w); typedef void (GLAD_API_PTR *PFNGLPROGRAMENVPARAMETER4FVARBPROC)(GLenum target, GLuint index, const GLfloat * params); typedef void (GLAD_API_PTR *PFNGLPROGRAMLOCALPARAMETER4DARBPROC)(GLenum target, GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); typedef void (GLAD_API_PTR *PFNGLPROGRAMLOCALPARAMETER4DVARBPROC)(GLenum target, GLuint index, const GLdouble * params); typedef void (GLAD_API_PTR *PFNGLPROGRAMLOCALPARAMETER4FARBPROC)(GLenum target, GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w); typedef void (GLAD_API_PTR *PFNGLPROGRAMLOCALPARAMETER4FVARBPROC)(GLenum target, GLuint index, const GLfloat * params); typedef void (GLAD_API_PTR *PFNGLPROGRAMPARAMETERIPROC)(GLuint program, GLenum pname, GLint value); typedef void (GLAD_API_PTR *PFNGLPROGRAMPARAMETERIARBPROC)(GLuint program, GLenum pname, GLint value); typedef void (GLAD_API_PTR *PFNGLPROGRAMSTRINGARBPROC)(GLenum target, GLenum format, GLsizei len, const void * string); typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORM1DPROC)(GLuint program, GLint location, GLdouble v0); typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORM1DVPROC)(GLuint program, GLint location, GLsizei count, const GLdouble * value); typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORM1FPROC)(GLuint program, GLint location, GLfloat v0); typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORM1FVPROC)(GLuint program, GLint location, GLsizei count, const GLfloat * value); typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORM1IPROC)(GLuint program, GLint location, GLint v0); typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORM1I64ARBPROC)(GLuint program, GLint location, GLint64 x); typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORM1I64VARBPROC)(GLuint program, GLint location, GLsizei count, const GLint64 * value); typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORM1IVPROC)(GLuint program, GLint location, GLsizei count, const GLint * value); typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORM1UIPROC)(GLuint program, GLint location, GLuint v0); typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORM1UI64ARBPROC)(GLuint program, GLint location, GLuint64 x); typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORM1UI64VARBPROC)(GLuint program, GLint location, GLsizei count, const GLuint64 * value); typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORM1UIVPROC)(GLuint program, GLint location, GLsizei count, const GLuint * value); typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORM2DPROC)(GLuint program, GLint location, GLdouble v0, GLdouble v1); typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORM2DVPROC)(GLuint program, GLint location, GLsizei count, const GLdouble * value); typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORM2FPROC)(GLuint program, GLint location, GLfloat v0, GLfloat v1); typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORM2FVPROC)(GLuint program, GLint location, GLsizei count, const GLfloat * value); typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORM2IPROC)(GLuint program, GLint location, GLint v0, GLint v1); typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORM2I64ARBPROC)(GLuint program, GLint location, GLint64 x, GLint64 y); typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORM2I64VARBPROC)(GLuint program, GLint location, GLsizei count, const GLint64 * value); typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORM2IVPROC)(GLuint program, GLint location, GLsizei count, const GLint * value); typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORM2UIPROC)(GLuint program, GLint location, GLuint v0, GLuint v1); typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORM2UI64ARBPROC)(GLuint program, GLint location, GLuint64 x, GLuint64 y); typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORM2UI64VARBPROC)(GLuint program, GLint location, GLsizei count, const GLuint64 * value); typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORM2UIVPROC)(GLuint program, GLint location, GLsizei count, const GLuint * value); typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORM3DPROC)(GLuint program, GLint location, GLdouble v0, GLdouble v1, GLdouble v2); typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORM3DVPROC)(GLuint program, GLint location, GLsizei count, const GLdouble * value); typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORM3FPROC)(GLuint program, GLint location, GLfloat v0, GLfloat v1, GLfloat v2); typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORM3FVPROC)(GLuint program, GLint location, GLsizei count, const GLfloat * value); typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORM3IPROC)(GLuint program, GLint location, GLint v0, GLint v1, GLint v2); typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORM3I64ARBPROC)(GLuint program, GLint location, GLint64 x, GLint64 y, GLint64 z); typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORM3I64VARBPROC)(GLuint program, GLint location, GLsizei count, const GLint64 * value); typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORM3IVPROC)(GLuint program, GLint location, GLsizei count, const GLint * value); typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORM3UIPROC)(GLuint program, GLint location, GLuint v0, GLuint v1, GLuint v2); typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORM3UI64ARBPROC)(GLuint program, GLint location, GLuint64 x, GLuint64 y, GLuint64 z); typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORM3UI64VARBPROC)(GLuint program, GLint location, GLsizei count, const GLuint64 * value); typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORM3UIVPROC)(GLuint program, GLint location, GLsizei count, const GLuint * value); typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORM4DPROC)(GLuint program, GLint location, GLdouble v0, GLdouble v1, GLdouble v2, GLdouble v3); typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORM4DVPROC)(GLuint program, GLint location, GLsizei count, const GLdouble * value); typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORM4FPROC)(GLuint program, GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3); typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORM4FVPROC)(GLuint program, GLint location, GLsizei count, const GLfloat * value); typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORM4IPROC)(GLuint program, GLint location, GLint v0, GLint v1, GLint v2, GLint v3); typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORM4I64ARBPROC)(GLuint program, GLint location, GLint64 x, GLint64 y, GLint64 z, GLint64 w); typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORM4I64VARBPROC)(GLuint program, GLint location, GLsizei count, const GLint64 * value); typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORM4IVPROC)(GLuint program, GLint location, GLsizei count, const GLint * value); typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORM4UIPROC)(GLuint program, GLint location, GLuint v0, GLuint v1, GLuint v2, GLuint v3); typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORM4UI64ARBPROC)(GLuint program, GLint location, GLuint64 x, GLuint64 y, GLuint64 z, GLuint64 w); typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORM4UI64VARBPROC)(GLuint program, GLint location, GLsizei count, const GLuint64 * value); typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORM4UIVPROC)(GLuint program, GLint location, GLsizei count, const GLuint * value); typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORMHANDLEUI64ARBPROC)(GLuint program, GLint location, GLuint64 value); typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORMHANDLEUI64VARBPROC)(GLuint program, GLint location, GLsizei count, const GLuint64 * values); typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORMMATRIX2DVPROC)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble * value); typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORMMATRIX2FVPROC)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat * value); typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORMMATRIX2X3DVPROC)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble * value); typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORMMATRIX2X3FVPROC)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat * value); typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORMMATRIX2X4DVPROC)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble * value); typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORMMATRIX2X4FVPROC)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat * value); typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORMMATRIX3DVPROC)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble * value); typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORMMATRIX3FVPROC)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat * value); typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORMMATRIX3X2DVPROC)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble * value); typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORMMATRIX3X2FVPROC)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat * value); typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORMMATRIX3X4DVPROC)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble * value); typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORMMATRIX3X4FVPROC)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat * value); typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORMMATRIX4DVPROC)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble * value); typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORMMATRIX4FVPROC)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat * value); typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORMMATRIX4X2DVPROC)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble * value); typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORMMATRIX4X2FVPROC)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat * value); typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORMMATRIX4X3DVPROC)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble * value); typedef void (GLAD_API_PTR *PFNGLPROGRAMUNIFORMMATRIX4X3FVPROC)(GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat * value); typedef void (GLAD_API_PTR *PFNGLPROVOKINGVERTEXPROC)(GLenum mode); typedef void (GLAD_API_PTR *PFNGLPUSHATTRIBPROC)(GLbitfield mask); typedef void (GLAD_API_PTR *PFNGLPUSHCLIENTATTRIBPROC)(GLbitfield mask); typedef void (GLAD_API_PTR *PFNGLPUSHDEBUGGROUPPROC)(GLenum source, GLuint id, GLsizei length, const GLchar * message); typedef void (GLAD_API_PTR *PFNGLPUSHMATRIXPROC)(void); typedef void (GLAD_API_PTR *PFNGLPUSHNAMEPROC)(GLuint name); typedef void (GLAD_API_PTR *PFNGLQUERYCOUNTERPROC)(GLuint id, GLenum target); typedef void (GLAD_API_PTR *PFNGLRASTERPOS2DPROC)(GLdouble x, GLdouble y); typedef void (GLAD_API_PTR *PFNGLRASTERPOS2DVPROC)(const GLdouble * v); typedef void (GLAD_API_PTR *PFNGLRASTERPOS2FPROC)(GLfloat x, GLfloat y); typedef void (GLAD_API_PTR *PFNGLRASTERPOS2FVPROC)(const GLfloat * v); typedef void (GLAD_API_PTR *PFNGLRASTERPOS2IPROC)(GLint x, GLint y); typedef void (GLAD_API_PTR *PFNGLRASTERPOS2IVPROC)(const GLint * v); typedef void (GLAD_API_PTR *PFNGLRASTERPOS2SPROC)(GLshort x, GLshort y); typedef void (GLAD_API_PTR *PFNGLRASTERPOS2SVPROC)(const GLshort * v); typedef void (GLAD_API_PTR *PFNGLRASTERPOS3DPROC)(GLdouble x, GLdouble y, GLdouble z); typedef void (GLAD_API_PTR *PFNGLRASTERPOS3DVPROC)(const GLdouble * v); typedef void (GLAD_API_PTR *PFNGLRASTERPOS3FPROC)(GLfloat x, GLfloat y, GLfloat z); typedef void (GLAD_API_PTR *PFNGLRASTERPOS3FVPROC)(const GLfloat * v); typedef void (GLAD_API_PTR *PFNGLRASTERPOS3IPROC)(GLint x, GLint y, GLint z); typedef void (GLAD_API_PTR *PFNGLRASTERPOS3IVPROC)(const GLint * v); typedef void (GLAD_API_PTR *PFNGLRASTERPOS3SPROC)(GLshort x, GLshort y, GLshort z); typedef void (GLAD_API_PTR *PFNGLRASTERPOS3SVPROC)(const GLshort * v); typedef void (GLAD_API_PTR *PFNGLRASTERPOS4DPROC)(GLdouble x, GLdouble y, GLdouble z, GLdouble w); typedef void (GLAD_API_PTR *PFNGLRASTERPOS4DVPROC)(const GLdouble * v); typedef void (GLAD_API_PTR *PFNGLRASTERPOS4FPROC)(GLfloat x, GLfloat y, GLfloat z, GLfloat w); typedef void (GLAD_API_PTR *PFNGLRASTERPOS4FVPROC)(const GLfloat * v); typedef void (GLAD_API_PTR *PFNGLRASTERPOS4IPROC)(GLint x, GLint y, GLint z, GLint w); typedef void (GLAD_API_PTR *PFNGLRASTERPOS4IVPROC)(const GLint * v); typedef void (GLAD_API_PTR *PFNGLRASTERPOS4SPROC)(GLshort x, GLshort y, GLshort z, GLshort w); typedef void (GLAD_API_PTR *PFNGLRASTERPOS4SVPROC)(const GLshort * v); typedef void (GLAD_API_PTR *PFNGLREADBUFFERPROC)(GLenum src); typedef void (GLAD_API_PTR *PFNGLREADPIXELSPROC)(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, void * pixels); typedef void (GLAD_API_PTR *PFNGLREADNPIXELSPROC)(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLsizei bufSize, void * data); typedef void (GLAD_API_PTR *PFNGLREADNPIXELSARBPROC)(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLsizei bufSize, void * data); typedef void (GLAD_API_PTR *PFNGLRECTDPROC)(GLdouble x1, GLdouble y1, GLdouble x2, GLdouble y2); typedef void (GLAD_API_PTR *PFNGLRECTDVPROC)(const GLdouble * v1, const GLdouble * v2); typedef void (GLAD_API_PTR *PFNGLRECTFPROC)(GLfloat x1, GLfloat y1, GLfloat x2, GLfloat y2); typedef void (GLAD_API_PTR *PFNGLRECTFVPROC)(const GLfloat * v1, const GLfloat * v2); typedef void (GLAD_API_PTR *PFNGLRECTIPROC)(GLint x1, GLint y1, GLint x2, GLint y2); typedef void (GLAD_API_PTR *PFNGLRECTIVPROC)(const GLint * v1, const GLint * v2); typedef void (GLAD_API_PTR *PFNGLRECTSPROC)(GLshort x1, GLshort y1, GLshort x2, GLshort y2); typedef void (GLAD_API_PTR *PFNGLRECTSVPROC)(const GLshort * v1, const GLshort * v2); typedef void (GLAD_API_PTR *PFNGLRELEASESHADERCOMPILERPROC)(void); typedef GLint (GLAD_API_PTR *PFNGLRENDERMODEPROC)(GLenum mode); typedef void (GLAD_API_PTR *PFNGLRENDERBUFFERSTORAGEPROC)(GLenum target, GLenum internalformat, GLsizei width, GLsizei height); typedef void (GLAD_API_PTR *PFNGLRENDERBUFFERSTORAGEMULTISAMPLEPROC)(GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height); typedef void (GLAD_API_PTR *PFNGLRESETHISTOGRAMPROC)(GLenum target); typedef void (GLAD_API_PTR *PFNGLRESETMINMAXPROC)(GLenum target); typedef void (GLAD_API_PTR *PFNGLRESUMETRANSFORMFEEDBACKPROC)(void); typedef void (GLAD_API_PTR *PFNGLROTATEDPROC)(GLdouble angle, GLdouble x, GLdouble y, GLdouble z); typedef void (GLAD_API_PTR *PFNGLROTATEFPROC)(GLfloat angle, GLfloat x, GLfloat y, GLfloat z); typedef void (GLAD_API_PTR *PFNGLSAMPLECOVERAGEPROC)(GLfloat value, GLboolean invert); typedef void (GLAD_API_PTR *PFNGLSAMPLECOVERAGEARBPROC)(GLfloat value, GLboolean invert); typedef void (GLAD_API_PTR *PFNGLSAMPLEMASKIPROC)(GLuint maskNumber, GLbitfield mask); typedef void (GLAD_API_PTR *PFNGLSAMPLERPARAMETERIIVPROC)(GLuint sampler, GLenum pname, const GLint * param); typedef void (GLAD_API_PTR *PFNGLSAMPLERPARAMETERIUIVPROC)(GLuint sampler, GLenum pname, const GLuint * param); typedef void (GLAD_API_PTR *PFNGLSAMPLERPARAMETERFPROC)(GLuint sampler, GLenum pname, GLfloat param); typedef void (GLAD_API_PTR *PFNGLSAMPLERPARAMETERFVPROC)(GLuint sampler, GLenum pname, const GLfloat * param); typedef void (GLAD_API_PTR *PFNGLSAMPLERPARAMETERIPROC)(GLuint sampler, GLenum pname, GLint param); typedef void (GLAD_API_PTR *PFNGLSAMPLERPARAMETERIVPROC)(GLuint sampler, GLenum pname, const GLint * param); typedef void (GLAD_API_PTR *PFNGLSCALEDPROC)(GLdouble x, GLdouble y, GLdouble z); typedef void (GLAD_API_PTR *PFNGLSCALEFPROC)(GLfloat x, GLfloat y, GLfloat z); typedef void (GLAD_API_PTR *PFNGLSCISSORPROC)(GLint x, GLint y, GLsizei width, GLsizei height); typedef void (GLAD_API_PTR *PFNGLSCISSORARRAYVPROC)(GLuint first, GLsizei count, const GLint * v); typedef void (GLAD_API_PTR *PFNGLSCISSORINDEXEDPROC)(GLuint index, GLint left, GLint bottom, GLsizei width, GLsizei height); typedef void (GLAD_API_PTR *PFNGLSCISSORINDEXEDVPROC)(GLuint index, const GLint * v); typedef void (GLAD_API_PTR *PFNGLSECONDARYCOLOR3BPROC)(GLbyte red, GLbyte green, GLbyte blue); typedef void (GLAD_API_PTR *PFNGLSECONDARYCOLOR3BVPROC)(const GLbyte * v); typedef void (GLAD_API_PTR *PFNGLSECONDARYCOLOR3DPROC)(GLdouble red, GLdouble green, GLdouble blue); typedef void (GLAD_API_PTR *PFNGLSECONDARYCOLOR3DVPROC)(const GLdouble * v); typedef void (GLAD_API_PTR *PFNGLSECONDARYCOLOR3FPROC)(GLfloat red, GLfloat green, GLfloat blue); typedef void (GLAD_API_PTR *PFNGLSECONDARYCOLOR3FVPROC)(const GLfloat * v); typedef void (GLAD_API_PTR *PFNGLSECONDARYCOLOR3IPROC)(GLint red, GLint green, GLint blue); typedef void (GLAD_API_PTR *PFNGLSECONDARYCOLOR3IVPROC)(const GLint * v); typedef void (GLAD_API_PTR *PFNGLSECONDARYCOLOR3SPROC)(GLshort red, GLshort green, GLshort blue); typedef void (GLAD_API_PTR *PFNGLSECONDARYCOLOR3SVPROC)(const GLshort * v); typedef void (GLAD_API_PTR *PFNGLSECONDARYCOLOR3UBPROC)(GLubyte red, GLubyte green, GLubyte blue); typedef void (GLAD_API_PTR *PFNGLSECONDARYCOLOR3UBVPROC)(const GLubyte * v); typedef void (GLAD_API_PTR *PFNGLSECONDARYCOLOR3UIPROC)(GLuint red, GLuint green, GLuint blue); typedef void (GLAD_API_PTR *PFNGLSECONDARYCOLOR3UIVPROC)(const GLuint * v); typedef void (GLAD_API_PTR *PFNGLSECONDARYCOLOR3USPROC)(GLushort red, GLushort green, GLushort blue); typedef void (GLAD_API_PTR *PFNGLSECONDARYCOLOR3USVPROC)(const GLushort * v); typedef void (GLAD_API_PTR *PFNGLSECONDARYCOLORP3UIPROC)(GLenum type, GLuint color); typedef void (GLAD_API_PTR *PFNGLSECONDARYCOLORP3UIVPROC)(GLenum type, const GLuint * color); typedef void (GLAD_API_PTR *PFNGLSECONDARYCOLORPOINTERPROC)(GLint size, GLenum type, GLsizei stride, const void * pointer); typedef void (GLAD_API_PTR *PFNGLSELECTBUFFERPROC)(GLsizei size, GLuint * buffer); typedef void (GLAD_API_PTR *PFNGLSEPARABLEFILTER2DPROC)(GLenum target, GLenum internalformat, GLsizei width, GLsizei height, GLenum format, GLenum type, const void * row, const void * column); typedef void (GLAD_API_PTR *PFNGLSHADEMODELPROC)(GLenum mode); typedef void (GLAD_API_PTR *PFNGLSHADERBINARYPROC)(GLsizei count, const GLuint * shaders, GLenum binaryformat, const void * binary, GLsizei length); typedef void (GLAD_API_PTR *PFNGLSHADERSOURCEPROC)(GLuint shader, GLsizei count, const GLchar *const* string, const GLint * length); typedef void (GLAD_API_PTR *PFNGLSHADERSOURCEARBPROC)(GLhandleARB shaderObj, GLsizei count, const GLcharARB ** string, const GLint * length); typedef void (GLAD_API_PTR *PFNGLSHADERSTORAGEBLOCKBINDINGPROC)(GLuint program, GLuint storageBlockIndex, GLuint storageBlockBinding); typedef void (GLAD_API_PTR *PFNGLSPECIALIZESHADERPROC)(GLuint shader, const GLchar * pEntryPoint, GLuint numSpecializationConstants, const GLuint * pConstantIndex, const GLuint * pConstantValue); typedef void (GLAD_API_PTR *PFNGLSPECIALIZESHADERARBPROC)(GLuint shader, const GLchar * pEntryPoint, GLuint numSpecializationConstants, const GLuint * pConstantIndex, const GLuint * pConstantValue); typedef void (GLAD_API_PTR *PFNGLSTENCILFUNCPROC)(GLenum func, GLint ref, GLuint mask); typedef void (GLAD_API_PTR *PFNGLSTENCILFUNCSEPARATEPROC)(GLenum face, GLenum func, GLint ref, GLuint mask); typedef void (GLAD_API_PTR *PFNGLSTENCILMASKPROC)(GLuint mask); typedef void (GLAD_API_PTR *PFNGLSTENCILMASKSEPARATEPROC)(GLenum face, GLuint mask); typedef void (GLAD_API_PTR *PFNGLSTENCILOPPROC)(GLenum fail, GLenum zfail, GLenum zpass); typedef void (GLAD_API_PTR *PFNGLSTENCILOPSEPARATEPROC)(GLenum face, GLenum sfail, GLenum dpfail, GLenum dppass); typedef void (GLAD_API_PTR *PFNGLTEXBUFFERPROC)(GLenum target, GLenum internalformat, GLuint buffer); typedef void (GLAD_API_PTR *PFNGLTEXBUFFERARBPROC)(GLenum target, GLenum internalformat, GLuint buffer); typedef void (GLAD_API_PTR *PFNGLTEXBUFFERRANGEPROC)(GLenum target, GLenum internalformat, GLuint buffer, GLintptr offset, GLsizeiptr size); typedef void (GLAD_API_PTR *PFNGLTEXCOORD1DPROC)(GLdouble s); typedef void (GLAD_API_PTR *PFNGLTEXCOORD1DVPROC)(const GLdouble * v); typedef void (GLAD_API_PTR *PFNGLTEXCOORD1FPROC)(GLfloat s); typedef void (GLAD_API_PTR *PFNGLTEXCOORD1FVPROC)(const GLfloat * v); typedef void (GLAD_API_PTR *PFNGLTEXCOORD1IPROC)(GLint s); typedef void (GLAD_API_PTR *PFNGLTEXCOORD1IVPROC)(const GLint * v); typedef void (GLAD_API_PTR *PFNGLTEXCOORD1SPROC)(GLshort s); typedef void (GLAD_API_PTR *PFNGLTEXCOORD1SVPROC)(const GLshort * v); typedef void (GLAD_API_PTR *PFNGLTEXCOORD2DPROC)(GLdouble s, GLdouble t); typedef void (GLAD_API_PTR *PFNGLTEXCOORD2DVPROC)(const GLdouble * v); typedef void (GLAD_API_PTR *PFNGLTEXCOORD2FPROC)(GLfloat s, GLfloat t); typedef void (GLAD_API_PTR *PFNGLTEXCOORD2FVPROC)(const GLfloat * v); typedef void (GLAD_API_PTR *PFNGLTEXCOORD2IPROC)(GLint s, GLint t); typedef void (GLAD_API_PTR *PFNGLTEXCOORD2IVPROC)(const GLint * v); typedef void (GLAD_API_PTR *PFNGLTEXCOORD2SPROC)(GLshort s, GLshort t); typedef void (GLAD_API_PTR *PFNGLTEXCOORD2SVPROC)(const GLshort * v); typedef void (GLAD_API_PTR *PFNGLTEXCOORD3DPROC)(GLdouble s, GLdouble t, GLdouble r); typedef void (GLAD_API_PTR *PFNGLTEXCOORD3DVPROC)(const GLdouble * v); typedef void (GLAD_API_PTR *PFNGLTEXCOORD3FPROC)(GLfloat s, GLfloat t, GLfloat r); typedef void (GLAD_API_PTR *PFNGLTEXCOORD3FVPROC)(const GLfloat * v); typedef void (GLAD_API_PTR *PFNGLTEXCOORD3IPROC)(GLint s, GLint t, GLint r); typedef void (GLAD_API_PTR *PFNGLTEXCOORD3IVPROC)(const GLint * v); typedef void (GLAD_API_PTR *PFNGLTEXCOORD3SPROC)(GLshort s, GLshort t, GLshort r); typedef void (GLAD_API_PTR *PFNGLTEXCOORD3SVPROC)(const GLshort * v); typedef void (GLAD_API_PTR *PFNGLTEXCOORD4DPROC)(GLdouble s, GLdouble t, GLdouble r, GLdouble q); typedef void (GLAD_API_PTR *PFNGLTEXCOORD4DVPROC)(const GLdouble * v); typedef void (GLAD_API_PTR *PFNGLTEXCOORD4FPROC)(GLfloat s, GLfloat t, GLfloat r, GLfloat q); typedef void (GLAD_API_PTR *PFNGLTEXCOORD4FVPROC)(const GLfloat * v); typedef void (GLAD_API_PTR *PFNGLTEXCOORD4IPROC)(GLint s, GLint t, GLint r, GLint q); typedef void (GLAD_API_PTR *PFNGLTEXCOORD4IVPROC)(const GLint * v); typedef void (GLAD_API_PTR *PFNGLTEXCOORD4SPROC)(GLshort s, GLshort t, GLshort r, GLshort q); typedef void (GLAD_API_PTR *PFNGLTEXCOORD4SVPROC)(const GLshort * v); typedef void (GLAD_API_PTR *PFNGLTEXCOORDP1UIPROC)(GLenum type, GLuint coords); typedef void (GLAD_API_PTR *PFNGLTEXCOORDP1UIVPROC)(GLenum type, const GLuint * coords); typedef void (GLAD_API_PTR *PFNGLTEXCOORDP2UIPROC)(GLenum type, GLuint coords); typedef void (GLAD_API_PTR *PFNGLTEXCOORDP2UIVPROC)(GLenum type, const GLuint * coords); typedef void (GLAD_API_PTR *PFNGLTEXCOORDP3UIPROC)(GLenum type, GLuint coords); typedef void (GLAD_API_PTR *PFNGLTEXCOORDP3UIVPROC)(GLenum type, const GLuint * coords); typedef void (GLAD_API_PTR *PFNGLTEXCOORDP4UIPROC)(GLenum type, GLuint coords); typedef void (GLAD_API_PTR *PFNGLTEXCOORDP4UIVPROC)(GLenum type, const GLuint * coords); typedef void (GLAD_API_PTR *PFNGLTEXCOORDPOINTERPROC)(GLint size, GLenum type, GLsizei stride, const void * pointer); typedef void (GLAD_API_PTR *PFNGLTEXENVFPROC)(GLenum target, GLenum pname, GLfloat param); typedef void (GLAD_API_PTR *PFNGLTEXENVFVPROC)(GLenum target, GLenum pname, const GLfloat * params); typedef void (GLAD_API_PTR *PFNGLTEXENVIPROC)(GLenum target, GLenum pname, GLint param); typedef void (GLAD_API_PTR *PFNGLTEXENVIVPROC)(GLenum target, GLenum pname, const GLint * params); typedef void (GLAD_API_PTR *PFNGLTEXGENDPROC)(GLenum coord, GLenum pname, GLdouble param); typedef void (GLAD_API_PTR *PFNGLTEXGENDVPROC)(GLenum coord, GLenum pname, const GLdouble * params); typedef void (GLAD_API_PTR *PFNGLTEXGENFPROC)(GLenum coord, GLenum pname, GLfloat param); typedef void (GLAD_API_PTR *PFNGLTEXGENFVPROC)(GLenum coord, GLenum pname, const GLfloat * params); typedef void (GLAD_API_PTR *PFNGLTEXGENIPROC)(GLenum coord, GLenum pname, GLint param); typedef void (GLAD_API_PTR *PFNGLTEXGENIVPROC)(GLenum coord, GLenum pname, const GLint * params); typedef void (GLAD_API_PTR *PFNGLTEXIMAGE1DPROC)(GLenum target, GLint level, GLint internalformat, GLsizei width, GLint border, GLenum format, GLenum type, const void * pixels); typedef void (GLAD_API_PTR *PFNGLTEXIMAGE2DPROC)(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void * pixels); typedef void (GLAD_API_PTR *PFNGLTEXIMAGE2DMULTISAMPLEPROC)(GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLboolean fixedsamplelocations); typedef void (GLAD_API_PTR *PFNGLTEXIMAGE3DPROC)(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const void * pixels); typedef void (GLAD_API_PTR *PFNGLTEXIMAGE3DMULTISAMPLEPROC)(GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedsamplelocations); typedef void (GLAD_API_PTR *PFNGLTEXPAGECOMMITMENTARBPROC)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLboolean commit); typedef void (GLAD_API_PTR *PFNGLTEXPARAMETERIIVPROC)(GLenum target, GLenum pname, const GLint * params); typedef void (GLAD_API_PTR *PFNGLTEXPARAMETERIUIVPROC)(GLenum target, GLenum pname, const GLuint * params); typedef void (GLAD_API_PTR *PFNGLTEXPARAMETERFPROC)(GLenum target, GLenum pname, GLfloat param); typedef void (GLAD_API_PTR *PFNGLTEXPARAMETERFVPROC)(GLenum target, GLenum pname, const GLfloat * params); typedef void (GLAD_API_PTR *PFNGLTEXPARAMETERIPROC)(GLenum target, GLenum pname, GLint param); typedef void (GLAD_API_PTR *PFNGLTEXPARAMETERIVPROC)(GLenum target, GLenum pname, const GLint * params); typedef void (GLAD_API_PTR *PFNGLTEXSTORAGE1DPROC)(GLenum target, GLsizei levels, GLenum internalformat, GLsizei width); typedef void (GLAD_API_PTR *PFNGLTEXSTORAGE2DPROC)(GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height); typedef void (GLAD_API_PTR *PFNGLTEXSTORAGE2DMULTISAMPLEPROC)(GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLboolean fixedsamplelocations); typedef void (GLAD_API_PTR *PFNGLTEXSTORAGE3DPROC)(GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth); typedef void (GLAD_API_PTR *PFNGLTEXSTORAGE3DMULTISAMPLEPROC)(GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedsamplelocations); typedef void (GLAD_API_PTR *PFNGLTEXSUBIMAGE1DPROC)(GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLenum type, const void * pixels); typedef void (GLAD_API_PTR *PFNGLTEXSUBIMAGE2DPROC)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void * pixels); typedef void (GLAD_API_PTR *PFNGLTEXSUBIMAGE3DPROC)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void * pixels); typedef void (GLAD_API_PTR *PFNGLTEXTUREBARRIERPROC)(void); typedef void (GLAD_API_PTR *PFNGLTEXTUREBUFFERPROC)(GLuint texture, GLenum internalformat, GLuint buffer); typedef void (GLAD_API_PTR *PFNGLTEXTUREBUFFERRANGEPROC)(GLuint texture, GLenum internalformat, GLuint buffer, GLintptr offset, GLsizeiptr size); typedef void (GLAD_API_PTR *PFNGLTEXTUREPARAMETERIIVPROC)(GLuint texture, GLenum pname, const GLint * params); typedef void (GLAD_API_PTR *PFNGLTEXTUREPARAMETERIUIVPROC)(GLuint texture, GLenum pname, const GLuint * params); typedef void (GLAD_API_PTR *PFNGLTEXTUREPARAMETERFPROC)(GLuint texture, GLenum pname, GLfloat param); typedef void (GLAD_API_PTR *PFNGLTEXTUREPARAMETERFVPROC)(GLuint texture, GLenum pname, const GLfloat * param); typedef void (GLAD_API_PTR *PFNGLTEXTUREPARAMETERIPROC)(GLuint texture, GLenum pname, GLint param); typedef void (GLAD_API_PTR *PFNGLTEXTUREPARAMETERIVPROC)(GLuint texture, GLenum pname, const GLint * param); typedef void (GLAD_API_PTR *PFNGLTEXTURESTORAGE1DPROC)(GLuint texture, GLsizei levels, GLenum internalformat, GLsizei width); typedef void (GLAD_API_PTR *PFNGLTEXTURESTORAGE2DPROC)(GLuint texture, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height); typedef void (GLAD_API_PTR *PFNGLTEXTURESTORAGE2DMULTISAMPLEPROC)(GLuint texture, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLboolean fixedsamplelocations); typedef void (GLAD_API_PTR *PFNGLTEXTURESTORAGE3DPROC)(GLuint texture, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth); typedef void (GLAD_API_PTR *PFNGLTEXTURESTORAGE3DMULTISAMPLEPROC)(GLuint texture, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedsamplelocations); typedef void (GLAD_API_PTR *PFNGLTEXTURESUBIMAGE1DPROC)(GLuint texture, GLint level, GLint xoffset, GLsizei width, GLenum format, GLenum type, const void * pixels); typedef void (GLAD_API_PTR *PFNGLTEXTURESUBIMAGE2DPROC)(GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void * pixels); typedef void (GLAD_API_PTR *PFNGLTEXTURESUBIMAGE3DPROC)(GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void * pixels); typedef void (GLAD_API_PTR *PFNGLTEXTUREVIEWPROC)(GLuint texture, GLenum target, GLuint origtexture, GLenum internalformat, GLuint minlevel, GLuint numlevels, GLuint minlayer, GLuint numlayers); typedef void (GLAD_API_PTR *PFNGLTRANSFORMFEEDBACKBUFFERBASEPROC)(GLuint xfb, GLuint index, GLuint buffer); typedef void (GLAD_API_PTR *PFNGLTRANSFORMFEEDBACKBUFFERRANGEPROC)(GLuint xfb, GLuint index, GLuint buffer, GLintptr offset, GLsizeiptr size); typedef void (GLAD_API_PTR *PFNGLTRANSFORMFEEDBACKVARYINGSPROC)(GLuint program, GLsizei count, const GLchar *const* varyings, GLenum bufferMode); typedef void (GLAD_API_PTR *PFNGLTRANSLATEDPROC)(GLdouble x, GLdouble y, GLdouble z); typedef void (GLAD_API_PTR *PFNGLTRANSLATEFPROC)(GLfloat x, GLfloat y, GLfloat z); typedef void (GLAD_API_PTR *PFNGLUNIFORM1DPROC)(GLint location, GLdouble x); typedef void (GLAD_API_PTR *PFNGLUNIFORM1DVPROC)(GLint location, GLsizei count, const GLdouble * value); typedef void (GLAD_API_PTR *PFNGLUNIFORM1FPROC)(GLint location, GLfloat v0); typedef void (GLAD_API_PTR *PFNGLUNIFORM1FARBPROC)(GLint location, GLfloat v0); typedef void (GLAD_API_PTR *PFNGLUNIFORM1FVPROC)(GLint location, GLsizei count, const GLfloat * value); typedef void (GLAD_API_PTR *PFNGLUNIFORM1FVARBPROC)(GLint location, GLsizei count, const GLfloat * value); typedef void (GLAD_API_PTR *PFNGLUNIFORM1IPROC)(GLint location, GLint v0); typedef void (GLAD_API_PTR *PFNGLUNIFORM1I64ARBPROC)(GLint location, GLint64 x); typedef void (GLAD_API_PTR *PFNGLUNIFORM1I64VARBPROC)(GLint location, GLsizei count, const GLint64 * value); typedef void (GLAD_API_PTR *PFNGLUNIFORM1IARBPROC)(GLint location, GLint v0); typedef void (GLAD_API_PTR *PFNGLUNIFORM1IVPROC)(GLint location, GLsizei count, const GLint * value); typedef void (GLAD_API_PTR *PFNGLUNIFORM1IVARBPROC)(GLint location, GLsizei count, const GLint * value); typedef void (GLAD_API_PTR *PFNGLUNIFORM1UIPROC)(GLint location, GLuint v0); typedef void (GLAD_API_PTR *PFNGLUNIFORM1UI64ARBPROC)(GLint location, GLuint64 x); typedef void (GLAD_API_PTR *PFNGLUNIFORM1UI64VARBPROC)(GLint location, GLsizei count, const GLuint64 * value); typedef void (GLAD_API_PTR *PFNGLUNIFORM1UIVPROC)(GLint location, GLsizei count, const GLuint * value); typedef void (GLAD_API_PTR *PFNGLUNIFORM2DPROC)(GLint location, GLdouble x, GLdouble y); typedef void (GLAD_API_PTR *PFNGLUNIFORM2DVPROC)(GLint location, GLsizei count, const GLdouble * value); typedef void (GLAD_API_PTR *PFNGLUNIFORM2FPROC)(GLint location, GLfloat v0, GLfloat v1); typedef void (GLAD_API_PTR *PFNGLUNIFORM2FARBPROC)(GLint location, GLfloat v0, GLfloat v1); typedef void (GLAD_API_PTR *PFNGLUNIFORM2FVPROC)(GLint location, GLsizei count, const GLfloat * value); typedef void (GLAD_API_PTR *PFNGLUNIFORM2FVARBPROC)(GLint location, GLsizei count, const GLfloat * value); typedef void (GLAD_API_PTR *PFNGLUNIFORM2IPROC)(GLint location, GLint v0, GLint v1); typedef void (GLAD_API_PTR *PFNGLUNIFORM2I64ARBPROC)(GLint location, GLint64 x, GLint64 y); typedef void (GLAD_API_PTR *PFNGLUNIFORM2I64VARBPROC)(GLint location, GLsizei count, const GLint64 * value); typedef void (GLAD_API_PTR *PFNGLUNIFORM2IARBPROC)(GLint location, GLint v0, GLint v1); typedef void (GLAD_API_PTR *PFNGLUNIFORM2IVPROC)(GLint location, GLsizei count, const GLint * value); typedef void (GLAD_API_PTR *PFNGLUNIFORM2IVARBPROC)(GLint location, GLsizei count, const GLint * value); typedef void (GLAD_API_PTR *PFNGLUNIFORM2UIPROC)(GLint location, GLuint v0, GLuint v1); typedef void (GLAD_API_PTR *PFNGLUNIFORM2UI64ARBPROC)(GLint location, GLuint64 x, GLuint64 y); typedef void (GLAD_API_PTR *PFNGLUNIFORM2UI64VARBPROC)(GLint location, GLsizei count, const GLuint64 * value); typedef void (GLAD_API_PTR *PFNGLUNIFORM2UIVPROC)(GLint location, GLsizei count, const GLuint * value); typedef void (GLAD_API_PTR *PFNGLUNIFORM3DPROC)(GLint location, GLdouble x, GLdouble y, GLdouble z); typedef void (GLAD_API_PTR *PFNGLUNIFORM3DVPROC)(GLint location, GLsizei count, const GLdouble * value); typedef void (GLAD_API_PTR *PFNGLUNIFORM3FPROC)(GLint location, GLfloat v0, GLfloat v1, GLfloat v2); typedef void (GLAD_API_PTR *PFNGLUNIFORM3FARBPROC)(GLint location, GLfloat v0, GLfloat v1, GLfloat v2); typedef void (GLAD_API_PTR *PFNGLUNIFORM3FVPROC)(GLint location, GLsizei count, const GLfloat * value); typedef void (GLAD_API_PTR *PFNGLUNIFORM3FVARBPROC)(GLint location, GLsizei count, const GLfloat * value); typedef void (GLAD_API_PTR *PFNGLUNIFORM3IPROC)(GLint location, GLint v0, GLint v1, GLint v2); typedef void (GLAD_API_PTR *PFNGLUNIFORM3I64ARBPROC)(GLint location, GLint64 x, GLint64 y, GLint64 z); typedef void (GLAD_API_PTR *PFNGLUNIFORM3I64VARBPROC)(GLint location, GLsizei count, const GLint64 * value); typedef void (GLAD_API_PTR *PFNGLUNIFORM3IARBPROC)(GLint location, GLint v0, GLint v1, GLint v2); typedef void (GLAD_API_PTR *PFNGLUNIFORM3IVPROC)(GLint location, GLsizei count, const GLint * value); typedef void (GLAD_API_PTR *PFNGLUNIFORM3IVARBPROC)(GLint location, GLsizei count, const GLint * value); typedef void (GLAD_API_PTR *PFNGLUNIFORM3UIPROC)(GLint location, GLuint v0, GLuint v1, GLuint v2); typedef void (GLAD_API_PTR *PFNGLUNIFORM3UI64ARBPROC)(GLint location, GLuint64 x, GLuint64 y, GLuint64 z); typedef void (GLAD_API_PTR *PFNGLUNIFORM3UI64VARBPROC)(GLint location, GLsizei count, const GLuint64 * value); typedef void (GLAD_API_PTR *PFNGLUNIFORM3UIVPROC)(GLint location, GLsizei count, const GLuint * value); typedef void (GLAD_API_PTR *PFNGLUNIFORM4DPROC)(GLint location, GLdouble x, GLdouble y, GLdouble z, GLdouble w); typedef void (GLAD_API_PTR *PFNGLUNIFORM4DVPROC)(GLint location, GLsizei count, const GLdouble * value); typedef void (GLAD_API_PTR *PFNGLUNIFORM4FPROC)(GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3); typedef void (GLAD_API_PTR *PFNGLUNIFORM4FARBPROC)(GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3); typedef void (GLAD_API_PTR *PFNGLUNIFORM4FVPROC)(GLint location, GLsizei count, const GLfloat * value); typedef void (GLAD_API_PTR *PFNGLUNIFORM4FVARBPROC)(GLint location, GLsizei count, const GLfloat * value); typedef void (GLAD_API_PTR *PFNGLUNIFORM4IPROC)(GLint location, GLint v0, GLint v1, GLint v2, GLint v3); typedef void (GLAD_API_PTR *PFNGLUNIFORM4I64ARBPROC)(GLint location, GLint64 x, GLint64 y, GLint64 z, GLint64 w); typedef void (GLAD_API_PTR *PFNGLUNIFORM4I64VARBPROC)(GLint location, GLsizei count, const GLint64 * value); typedef void (GLAD_API_PTR *PFNGLUNIFORM4IARBPROC)(GLint location, GLint v0, GLint v1, GLint v2, GLint v3); typedef void (GLAD_API_PTR *PFNGLUNIFORM4IVPROC)(GLint location, GLsizei count, const GLint * value); typedef void (GLAD_API_PTR *PFNGLUNIFORM4IVARBPROC)(GLint location, GLsizei count, const GLint * value); typedef void (GLAD_API_PTR *PFNGLUNIFORM4UIPROC)(GLint location, GLuint v0, GLuint v1, GLuint v2, GLuint v3); typedef void (GLAD_API_PTR *PFNGLUNIFORM4UI64ARBPROC)(GLint location, GLuint64 x, GLuint64 y, GLuint64 z, GLuint64 w); typedef void (GLAD_API_PTR *PFNGLUNIFORM4UI64VARBPROC)(GLint location, GLsizei count, const GLuint64 * value); typedef void (GLAD_API_PTR *PFNGLUNIFORM4UIVPROC)(GLint location, GLsizei count, const GLuint * value); typedef void (GLAD_API_PTR *PFNGLUNIFORMBLOCKBINDINGPROC)(GLuint program, GLuint uniformBlockIndex, GLuint uniformBlockBinding); typedef void (GLAD_API_PTR *PFNGLUNIFORMHANDLEUI64ARBPROC)(GLint location, GLuint64 value); typedef void (GLAD_API_PTR *PFNGLUNIFORMHANDLEUI64VARBPROC)(GLint location, GLsizei count, const GLuint64 * value); typedef void (GLAD_API_PTR *PFNGLUNIFORMMATRIX2DVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLdouble * value); typedef void (GLAD_API_PTR *PFNGLUNIFORMMATRIX2FVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat * value); typedef void (GLAD_API_PTR *PFNGLUNIFORMMATRIX2FVARBPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat * value); typedef void (GLAD_API_PTR *PFNGLUNIFORMMATRIX2X3DVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLdouble * value); typedef void (GLAD_API_PTR *PFNGLUNIFORMMATRIX2X3FVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat * value); typedef void (GLAD_API_PTR *PFNGLUNIFORMMATRIX2X4DVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLdouble * value); typedef void (GLAD_API_PTR *PFNGLUNIFORMMATRIX2X4FVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat * value); typedef void (GLAD_API_PTR *PFNGLUNIFORMMATRIX3DVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLdouble * value); typedef void (GLAD_API_PTR *PFNGLUNIFORMMATRIX3FVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat * value); typedef void (GLAD_API_PTR *PFNGLUNIFORMMATRIX3FVARBPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat * value); typedef void (GLAD_API_PTR *PFNGLUNIFORMMATRIX3X2DVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLdouble * value); typedef void (GLAD_API_PTR *PFNGLUNIFORMMATRIX3X2FVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat * value); typedef void (GLAD_API_PTR *PFNGLUNIFORMMATRIX3X4DVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLdouble * value); typedef void (GLAD_API_PTR *PFNGLUNIFORMMATRIX3X4FVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat * value); typedef void (GLAD_API_PTR *PFNGLUNIFORMMATRIX4DVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLdouble * value); typedef void (GLAD_API_PTR *PFNGLUNIFORMMATRIX4FVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat * value); typedef void (GLAD_API_PTR *PFNGLUNIFORMMATRIX4FVARBPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat * value); typedef void (GLAD_API_PTR *PFNGLUNIFORMMATRIX4X2DVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLdouble * value); typedef void (GLAD_API_PTR *PFNGLUNIFORMMATRIX4X2FVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat * value); typedef void (GLAD_API_PTR *PFNGLUNIFORMMATRIX4X3DVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLdouble * value); typedef void (GLAD_API_PTR *PFNGLUNIFORMMATRIX4X3FVPROC)(GLint location, GLsizei count, GLboolean transpose, const GLfloat * value); typedef void (GLAD_API_PTR *PFNGLUNIFORMSUBROUTINESUIVPROC)(GLenum shadertype, GLsizei count, const GLuint * indices); typedef GLboolean (GLAD_API_PTR *PFNGLUNMAPBUFFERPROC)(GLenum target); typedef GLboolean (GLAD_API_PTR *PFNGLUNMAPBUFFERARBPROC)(GLenum target); typedef GLboolean (GLAD_API_PTR *PFNGLUNMAPNAMEDBUFFERPROC)(GLuint buffer); typedef void (GLAD_API_PTR *PFNGLUSEPROGRAMPROC)(GLuint program); typedef void (GLAD_API_PTR *PFNGLUSEPROGRAMOBJECTARBPROC)(GLhandleARB programObj); typedef void (GLAD_API_PTR *PFNGLUSEPROGRAMSTAGESPROC)(GLuint pipeline, GLbitfield stages, GLuint program); typedef void (GLAD_API_PTR *PFNGLVALIDATEPROGRAMPROC)(GLuint program); typedef void (GLAD_API_PTR *PFNGLVALIDATEPROGRAMARBPROC)(GLhandleARB programObj); typedef void (GLAD_API_PTR *PFNGLVALIDATEPROGRAMPIPELINEPROC)(GLuint pipeline); typedef void (GLAD_API_PTR *PFNGLVERTEX2DPROC)(GLdouble x, GLdouble y); typedef void (GLAD_API_PTR *PFNGLVERTEX2DVPROC)(const GLdouble * v); typedef void (GLAD_API_PTR *PFNGLVERTEX2FPROC)(GLfloat x, GLfloat y); typedef void (GLAD_API_PTR *PFNGLVERTEX2FVPROC)(const GLfloat * v); typedef void (GLAD_API_PTR *PFNGLVERTEX2IPROC)(GLint x, GLint y); typedef void (GLAD_API_PTR *PFNGLVERTEX2IVPROC)(const GLint * v); typedef void (GLAD_API_PTR *PFNGLVERTEX2SPROC)(GLshort x, GLshort y); typedef void (GLAD_API_PTR *PFNGLVERTEX2SVPROC)(const GLshort * v); typedef void (GLAD_API_PTR *PFNGLVERTEX3DPROC)(GLdouble x, GLdouble y, GLdouble z); typedef void (GLAD_API_PTR *PFNGLVERTEX3DVPROC)(const GLdouble * v); typedef void (GLAD_API_PTR *PFNGLVERTEX3FPROC)(GLfloat x, GLfloat y, GLfloat z); typedef void (GLAD_API_PTR *PFNGLVERTEX3FVPROC)(const GLfloat * v); typedef void (GLAD_API_PTR *PFNGLVERTEX3IPROC)(GLint x, GLint y, GLint z); typedef void (GLAD_API_PTR *PFNGLVERTEX3IVPROC)(const GLint * v); typedef void (GLAD_API_PTR *PFNGLVERTEX3SPROC)(GLshort x, GLshort y, GLshort z); typedef void (GLAD_API_PTR *PFNGLVERTEX3SVPROC)(const GLshort * v); typedef void (GLAD_API_PTR *PFNGLVERTEX4DPROC)(GLdouble x, GLdouble y, GLdouble z, GLdouble w); typedef void (GLAD_API_PTR *PFNGLVERTEX4DVPROC)(const GLdouble * v); typedef void (GLAD_API_PTR *PFNGLVERTEX4FPROC)(GLfloat x, GLfloat y, GLfloat z, GLfloat w); typedef void (GLAD_API_PTR *PFNGLVERTEX4FVPROC)(const GLfloat * v); typedef void (GLAD_API_PTR *PFNGLVERTEX4IPROC)(GLint x, GLint y, GLint z, GLint w); typedef void (GLAD_API_PTR *PFNGLVERTEX4IVPROC)(const GLint * v); typedef void (GLAD_API_PTR *PFNGLVERTEX4SPROC)(GLshort x, GLshort y, GLshort z, GLshort w); typedef void (GLAD_API_PTR *PFNGLVERTEX4SVPROC)(const GLshort * v); typedef void (GLAD_API_PTR *PFNGLVERTEXARRAYATTRIBBINDINGPROC)(GLuint vaobj, GLuint attribindex, GLuint bindingindex); typedef void (GLAD_API_PTR *PFNGLVERTEXARRAYATTRIBFORMATPROC)(GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLboolean normalized, GLuint relativeoffset); typedef void (GLAD_API_PTR *PFNGLVERTEXARRAYATTRIBIFORMATPROC)(GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset); typedef void (GLAD_API_PTR *PFNGLVERTEXARRAYATTRIBLFORMATPROC)(GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset); typedef void (GLAD_API_PTR *PFNGLVERTEXARRAYBINDINGDIVISORPROC)(GLuint vaobj, GLuint bindingindex, GLuint divisor); typedef void (GLAD_API_PTR *PFNGLVERTEXARRAYELEMENTBUFFERPROC)(GLuint vaobj, GLuint buffer); typedef void (GLAD_API_PTR *PFNGLVERTEXARRAYVERTEXBUFFERPROC)(GLuint vaobj, GLuint bindingindex, GLuint buffer, GLintptr offset, GLsizei stride); typedef void (GLAD_API_PTR *PFNGLVERTEXARRAYVERTEXBUFFERSPROC)(GLuint vaobj, GLuint first, GLsizei count, const GLuint * buffers, const GLintptr * offsets, const GLsizei * strides); typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB1DPROC)(GLuint index, GLdouble x); typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB1DARBPROC)(GLuint index, GLdouble x); typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB1DVPROC)(GLuint index, const GLdouble * v); typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB1DVARBPROC)(GLuint index, const GLdouble * v); typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB1FPROC)(GLuint index, GLfloat x); typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB1FARBPROC)(GLuint index, GLfloat x); typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB1FVPROC)(GLuint index, const GLfloat * v); typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB1FVARBPROC)(GLuint index, const GLfloat * v); typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB1SPROC)(GLuint index, GLshort x); typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB1SARBPROC)(GLuint index, GLshort x); typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB1SVPROC)(GLuint index, const GLshort * v); typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB1SVARBPROC)(GLuint index, const GLshort * v); typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB2DPROC)(GLuint index, GLdouble x, GLdouble y); typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB2DARBPROC)(GLuint index, GLdouble x, GLdouble y); typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB2DVPROC)(GLuint index, const GLdouble * v); typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB2DVARBPROC)(GLuint index, const GLdouble * v); typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB2FPROC)(GLuint index, GLfloat x, GLfloat y); typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB2FARBPROC)(GLuint index, GLfloat x, GLfloat y); typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB2FVPROC)(GLuint index, const GLfloat * v); typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB2FVARBPROC)(GLuint index, const GLfloat * v); typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB2SPROC)(GLuint index, GLshort x, GLshort y); typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB2SARBPROC)(GLuint index, GLshort x, GLshort y); typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB2SVPROC)(GLuint index, const GLshort * v); typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB2SVARBPROC)(GLuint index, const GLshort * v); typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB3DPROC)(GLuint index, GLdouble x, GLdouble y, GLdouble z); typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB3DARBPROC)(GLuint index, GLdouble x, GLdouble y, GLdouble z); typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB3DVPROC)(GLuint index, const GLdouble * v); typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB3DVARBPROC)(GLuint index, const GLdouble * v); typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB3FPROC)(GLuint index, GLfloat x, GLfloat y, GLfloat z); typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB3FARBPROC)(GLuint index, GLfloat x, GLfloat y, GLfloat z); typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB3FVPROC)(GLuint index, const GLfloat * v); typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB3FVARBPROC)(GLuint index, const GLfloat * v); typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB3SPROC)(GLuint index, GLshort x, GLshort y, GLshort z); typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB3SARBPROC)(GLuint index, GLshort x, GLshort y, GLshort z); typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB3SVPROC)(GLuint index, const GLshort * v); typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB3SVARBPROC)(GLuint index, const GLshort * v); typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB4NBVPROC)(GLuint index, const GLbyte * v); typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB4NBVARBPROC)(GLuint index, const GLbyte * v); typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB4NIVPROC)(GLuint index, const GLint * v); typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB4NIVARBPROC)(GLuint index, const GLint * v); typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB4NSVPROC)(GLuint index, const GLshort * v); typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB4NSVARBPROC)(GLuint index, const GLshort * v); typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB4NUBPROC)(GLuint index, GLubyte x, GLubyte y, GLubyte z, GLubyte w); typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB4NUBARBPROC)(GLuint index, GLubyte x, GLubyte y, GLubyte z, GLubyte w); typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB4NUBVPROC)(GLuint index, const GLubyte * v); typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB4NUBVARBPROC)(GLuint index, const GLubyte * v); typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB4NUIVPROC)(GLuint index, const GLuint * v); typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB4NUIVARBPROC)(GLuint index, const GLuint * v); typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB4NUSVPROC)(GLuint index, const GLushort * v); typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB4NUSVARBPROC)(GLuint index, const GLushort * v); typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB4BVPROC)(GLuint index, const GLbyte * v); typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB4BVARBPROC)(GLuint index, const GLbyte * v); typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB4DPROC)(GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB4DARBPROC)(GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB4DVPROC)(GLuint index, const GLdouble * v); typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB4DVARBPROC)(GLuint index, const GLdouble * v); typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB4FPROC)(GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w); typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB4FARBPROC)(GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w); typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB4FVPROC)(GLuint index, const GLfloat * v); typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB4FVARBPROC)(GLuint index, const GLfloat * v); typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB4IVPROC)(GLuint index, const GLint * v); typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB4IVARBPROC)(GLuint index, const GLint * v); typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB4SPROC)(GLuint index, GLshort x, GLshort y, GLshort z, GLshort w); typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB4SARBPROC)(GLuint index, GLshort x, GLshort y, GLshort z, GLshort w); typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB4SVPROC)(GLuint index, const GLshort * v); typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB4SVARBPROC)(GLuint index, const GLshort * v); typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB4UBVPROC)(GLuint index, const GLubyte * v); typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB4UBVARBPROC)(GLuint index, const GLubyte * v); typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB4UIVPROC)(GLuint index, const GLuint * v); typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB4UIVARBPROC)(GLuint index, const GLuint * v); typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB4USVPROC)(GLuint index, const GLushort * v); typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIB4USVARBPROC)(GLuint index, const GLushort * v); typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBBINDINGPROC)(GLuint attribindex, GLuint bindingindex); typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBDIVISORPROC)(GLuint index, GLuint divisor); typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBDIVISORARBPROC)(GLuint index, GLuint divisor); typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBFORMATPROC)(GLuint attribindex, GLint size, GLenum type, GLboolean normalized, GLuint relativeoffset); typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBI1IPROC)(GLuint index, GLint x); typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBI1IVPROC)(GLuint index, const GLint * v); typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBI1UIPROC)(GLuint index, GLuint x); typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBI1UIVPROC)(GLuint index, const GLuint * v); typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBI2IPROC)(GLuint index, GLint x, GLint y); typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBI2IVPROC)(GLuint index, const GLint * v); typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBI2UIPROC)(GLuint index, GLuint x, GLuint y); typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBI2UIVPROC)(GLuint index, const GLuint * v); typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBI3IPROC)(GLuint index, GLint x, GLint y, GLint z); typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBI3IVPROC)(GLuint index, const GLint * v); typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBI3UIPROC)(GLuint index, GLuint x, GLuint y, GLuint z); typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBI3UIVPROC)(GLuint index, const GLuint * v); typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBI4BVPROC)(GLuint index, const GLbyte * v); typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBI4IPROC)(GLuint index, GLint x, GLint y, GLint z, GLint w); typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBI4IVPROC)(GLuint index, const GLint * v); typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBI4SVPROC)(GLuint index, const GLshort * v); typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBI4UBVPROC)(GLuint index, const GLubyte * v); typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBI4UIPROC)(GLuint index, GLuint x, GLuint y, GLuint z, GLuint w); typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBI4UIVPROC)(GLuint index, const GLuint * v); typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBI4USVPROC)(GLuint index, const GLushort * v); typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBIFORMATPROC)(GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset); typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBIPOINTERPROC)(GLuint index, GLint size, GLenum type, GLsizei stride, const void * pointer); typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBL1DPROC)(GLuint index, GLdouble x); typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBL1DVPROC)(GLuint index, const GLdouble * v); typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBL1UI64ARBPROC)(GLuint index, GLuint64EXT x); typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBL1UI64VARBPROC)(GLuint index, const GLuint64EXT * v); typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBL2DPROC)(GLuint index, GLdouble x, GLdouble y); typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBL2DVPROC)(GLuint index, const GLdouble * v); typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBL3DPROC)(GLuint index, GLdouble x, GLdouble y, GLdouble z); typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBL3DVPROC)(GLuint index, const GLdouble * v); typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBL4DPROC)(GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBL4DVPROC)(GLuint index, const GLdouble * v); typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBLFORMATPROC)(GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset); typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBLPOINTERPROC)(GLuint index, GLint size, GLenum type, GLsizei stride, const void * pointer); typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBP1UIPROC)(GLuint index, GLenum type, GLboolean normalized, GLuint value); typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBP1UIVPROC)(GLuint index, GLenum type, GLboolean normalized, const GLuint * value); typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBP2UIPROC)(GLuint index, GLenum type, GLboolean normalized, GLuint value); typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBP2UIVPROC)(GLuint index, GLenum type, GLboolean normalized, const GLuint * value); typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBP3UIPROC)(GLuint index, GLenum type, GLboolean normalized, GLuint value); typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBP3UIVPROC)(GLuint index, GLenum type, GLboolean normalized, const GLuint * value); typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBP4UIPROC)(GLuint index, GLenum type, GLboolean normalized, GLuint value); typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBP4UIVPROC)(GLuint index, GLenum type, GLboolean normalized, const GLuint * value); typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBPOINTERPROC)(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void * pointer); typedef void (GLAD_API_PTR *PFNGLVERTEXATTRIBPOINTERARBPROC)(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void * pointer); typedef void (GLAD_API_PTR *PFNGLVERTEXBINDINGDIVISORPROC)(GLuint bindingindex, GLuint divisor); typedef void (GLAD_API_PTR *PFNGLVERTEXBLENDARBPROC)(GLint count); typedef void (GLAD_API_PTR *PFNGLVERTEXP2UIPROC)(GLenum type, GLuint value); typedef void (GLAD_API_PTR *PFNGLVERTEXP2UIVPROC)(GLenum type, const GLuint * value); typedef void (GLAD_API_PTR *PFNGLVERTEXP3UIPROC)(GLenum type, GLuint value); typedef void (GLAD_API_PTR *PFNGLVERTEXP3UIVPROC)(GLenum type, const GLuint * value); typedef void (GLAD_API_PTR *PFNGLVERTEXP4UIPROC)(GLenum type, GLuint value); typedef void (GLAD_API_PTR *PFNGLVERTEXP4UIVPROC)(GLenum type, const GLuint * value); typedef void (GLAD_API_PTR *PFNGLVERTEXPOINTERPROC)(GLint size, GLenum type, GLsizei stride, const void * pointer); typedef void (GLAD_API_PTR *PFNGLVIEWPORTPROC)(GLint x, GLint y, GLsizei width, GLsizei height); typedef void (GLAD_API_PTR *PFNGLVIEWPORTARRAYVPROC)(GLuint first, GLsizei count, const GLfloat * v); typedef void (GLAD_API_PTR *PFNGLVIEWPORTINDEXEDFPROC)(GLuint index, GLfloat x, GLfloat y, GLfloat w, GLfloat h); typedef void (GLAD_API_PTR *PFNGLVIEWPORTINDEXEDFVPROC)(GLuint index, const GLfloat * v); typedef void (GLAD_API_PTR *PFNGLWAITSYNCPROC)(GLsync sync, GLbitfield flags, GLuint64 timeout); typedef void (GLAD_API_PTR *PFNGLWEIGHTPOINTERARBPROC)(GLint size, GLenum type, GLsizei stride, const void * pointer); typedef void (GLAD_API_PTR *PFNGLWEIGHTBVARBPROC)(GLint size, const GLbyte * weights); typedef void (GLAD_API_PTR *PFNGLWEIGHTDVARBPROC)(GLint size, const GLdouble * weights); typedef void (GLAD_API_PTR *PFNGLWEIGHTFVARBPROC)(GLint size, const GLfloat * weights); typedef void (GLAD_API_PTR *PFNGLWEIGHTIVARBPROC)(GLint size, const GLint * weights); typedef void (GLAD_API_PTR *PFNGLWEIGHTSVARBPROC)(GLint size, const GLshort * weights); typedef void (GLAD_API_PTR *PFNGLWEIGHTUBVARBPROC)(GLint size, const GLubyte * weights); typedef void (GLAD_API_PTR *PFNGLWEIGHTUIVARBPROC)(GLint size, const GLuint * weights); typedef void (GLAD_API_PTR *PFNGLWEIGHTUSVARBPROC)(GLint size, const GLushort * weights); typedef void (GLAD_API_PTR *PFNGLWINDOWPOS2DPROC)(GLdouble x, GLdouble y); typedef void (GLAD_API_PTR *PFNGLWINDOWPOS2DARBPROC)(GLdouble x, GLdouble y); typedef void (GLAD_API_PTR *PFNGLWINDOWPOS2DVPROC)(const GLdouble * v); typedef void (GLAD_API_PTR *PFNGLWINDOWPOS2DVARBPROC)(const GLdouble * v); typedef void (GLAD_API_PTR *PFNGLWINDOWPOS2FPROC)(GLfloat x, GLfloat y); typedef void (GLAD_API_PTR *PFNGLWINDOWPOS2FARBPROC)(GLfloat x, GLfloat y); typedef void (GLAD_API_PTR *PFNGLWINDOWPOS2FVPROC)(const GLfloat * v); typedef void (GLAD_API_PTR *PFNGLWINDOWPOS2FVARBPROC)(const GLfloat * v); typedef void (GLAD_API_PTR *PFNGLWINDOWPOS2IPROC)(GLint x, GLint y); typedef void (GLAD_API_PTR *PFNGLWINDOWPOS2IARBPROC)(GLint x, GLint y); typedef void (GLAD_API_PTR *PFNGLWINDOWPOS2IVPROC)(const GLint * v); typedef void (GLAD_API_PTR *PFNGLWINDOWPOS2IVARBPROC)(const GLint * v); typedef void (GLAD_API_PTR *PFNGLWINDOWPOS2SPROC)(GLshort x, GLshort y); typedef void (GLAD_API_PTR *PFNGLWINDOWPOS2SARBPROC)(GLshort x, GLshort y); typedef void (GLAD_API_PTR *PFNGLWINDOWPOS2SVPROC)(const GLshort * v); typedef void (GLAD_API_PTR *PFNGLWINDOWPOS2SVARBPROC)(const GLshort * v); typedef void (GLAD_API_PTR *PFNGLWINDOWPOS3DPROC)(GLdouble x, GLdouble y, GLdouble z); typedef void (GLAD_API_PTR *PFNGLWINDOWPOS3DARBPROC)(GLdouble x, GLdouble y, GLdouble z); typedef void (GLAD_API_PTR *PFNGLWINDOWPOS3DVPROC)(const GLdouble * v); typedef void (GLAD_API_PTR *PFNGLWINDOWPOS3DVARBPROC)(const GLdouble * v); typedef void (GLAD_API_PTR *PFNGLWINDOWPOS3FPROC)(GLfloat x, GLfloat y, GLfloat z); typedef void (GLAD_API_PTR *PFNGLWINDOWPOS3FARBPROC)(GLfloat x, GLfloat y, GLfloat z); typedef void (GLAD_API_PTR *PFNGLWINDOWPOS3FVPROC)(const GLfloat * v); typedef void (GLAD_API_PTR *PFNGLWINDOWPOS3FVARBPROC)(const GLfloat * v); typedef void (GLAD_API_PTR *PFNGLWINDOWPOS3IPROC)(GLint x, GLint y, GLint z); typedef void (GLAD_API_PTR *PFNGLWINDOWPOS3IARBPROC)(GLint x, GLint y, GLint z); typedef void (GLAD_API_PTR *PFNGLWINDOWPOS3IVPROC)(const GLint * v); typedef void (GLAD_API_PTR *PFNGLWINDOWPOS3IVARBPROC)(const GLint * v); typedef void (GLAD_API_PTR *PFNGLWINDOWPOS3SPROC)(GLshort x, GLshort y, GLshort z); typedef void (GLAD_API_PTR *PFNGLWINDOWPOS3SARBPROC)(GLshort x, GLshort y, GLshort z); typedef void (GLAD_API_PTR *PFNGLWINDOWPOS3SVPROC)(const GLshort * v); typedef void (GLAD_API_PTR *PFNGLWINDOWPOS3SVARBPROC)(const GLshort * v); GLAD_API_CALL PFNGLACCUMPROC glad_glAccum; #define glAccum glad_glAccum GLAD_API_CALL PFNGLACTIVESHADERPROGRAMPROC glad_glActiveShaderProgram; #define glActiveShaderProgram glad_glActiveShaderProgram GLAD_API_CALL PFNGLACTIVETEXTUREPROC glad_glActiveTexture; #define glActiveTexture glad_glActiveTexture GLAD_API_CALL PFNGLACTIVETEXTUREARBPROC glad_glActiveTextureARB; #define glActiveTextureARB glad_glActiveTextureARB GLAD_API_CALL PFNGLALPHAFUNCPROC glad_glAlphaFunc; #define glAlphaFunc glad_glAlphaFunc GLAD_API_CALL PFNGLARETEXTURESRESIDENTPROC glad_glAreTexturesResident; #define glAreTexturesResident glad_glAreTexturesResident GLAD_API_CALL PFNGLARRAYELEMENTPROC glad_glArrayElement; #define glArrayElement glad_glArrayElement GLAD_API_CALL PFNGLATTACHOBJECTARBPROC glad_glAttachObjectARB; #define glAttachObjectARB glad_glAttachObjectARB GLAD_API_CALL PFNGLATTACHSHADERPROC glad_glAttachShader; #define glAttachShader glad_glAttachShader GLAD_API_CALL PFNGLBEGINPROC glad_glBegin; #define glBegin glad_glBegin GLAD_API_CALL PFNGLBEGINCONDITIONALRENDERPROC glad_glBeginConditionalRender; #define glBeginConditionalRender glad_glBeginConditionalRender GLAD_API_CALL PFNGLBEGINQUERYPROC glad_glBeginQuery; #define glBeginQuery glad_glBeginQuery GLAD_API_CALL PFNGLBEGINQUERYARBPROC glad_glBeginQueryARB; #define glBeginQueryARB glad_glBeginQueryARB GLAD_API_CALL PFNGLBEGINQUERYINDEXEDPROC glad_glBeginQueryIndexed; #define glBeginQueryIndexed glad_glBeginQueryIndexed GLAD_API_CALL PFNGLBEGINTRANSFORMFEEDBACKPROC glad_glBeginTransformFeedback; #define glBeginTransformFeedback glad_glBeginTransformFeedback GLAD_API_CALL PFNGLBINDATTRIBLOCATIONPROC glad_glBindAttribLocation; #define glBindAttribLocation glad_glBindAttribLocation GLAD_API_CALL PFNGLBINDATTRIBLOCATIONARBPROC glad_glBindAttribLocationARB; #define glBindAttribLocationARB glad_glBindAttribLocationARB GLAD_API_CALL PFNGLBINDBUFFERPROC glad_glBindBuffer; #define glBindBuffer glad_glBindBuffer GLAD_API_CALL PFNGLBINDBUFFERARBPROC glad_glBindBufferARB; #define glBindBufferARB glad_glBindBufferARB GLAD_API_CALL PFNGLBINDBUFFERBASEPROC glad_glBindBufferBase; #define glBindBufferBase glad_glBindBufferBase GLAD_API_CALL PFNGLBINDBUFFERRANGEPROC glad_glBindBufferRange; #define glBindBufferRange glad_glBindBufferRange GLAD_API_CALL PFNGLBINDBUFFERSBASEPROC glad_glBindBuffersBase; #define glBindBuffersBase glad_glBindBuffersBase GLAD_API_CALL PFNGLBINDBUFFERSRANGEPROC glad_glBindBuffersRange; #define glBindBuffersRange glad_glBindBuffersRange GLAD_API_CALL PFNGLBINDFRAGDATALOCATIONPROC glad_glBindFragDataLocation; #define glBindFragDataLocation glad_glBindFragDataLocation GLAD_API_CALL PFNGLBINDFRAGDATALOCATIONINDEXEDPROC glad_glBindFragDataLocationIndexed; #define glBindFragDataLocationIndexed glad_glBindFragDataLocationIndexed GLAD_API_CALL PFNGLBINDFRAMEBUFFERPROC glad_glBindFramebuffer; #define glBindFramebuffer glad_glBindFramebuffer GLAD_API_CALL PFNGLBINDIMAGETEXTUREPROC glad_glBindImageTexture; #define glBindImageTexture glad_glBindImageTexture GLAD_API_CALL PFNGLBINDIMAGETEXTURESPROC glad_glBindImageTextures; #define glBindImageTextures glad_glBindImageTextures GLAD_API_CALL PFNGLBINDPROGRAMARBPROC glad_glBindProgramARB; #define glBindProgramARB glad_glBindProgramARB GLAD_API_CALL PFNGLBINDPROGRAMPIPELINEPROC glad_glBindProgramPipeline; #define glBindProgramPipeline glad_glBindProgramPipeline GLAD_API_CALL PFNGLBINDRENDERBUFFERPROC glad_glBindRenderbuffer; #define glBindRenderbuffer glad_glBindRenderbuffer GLAD_API_CALL PFNGLBINDSAMPLERPROC glad_glBindSampler; #define glBindSampler glad_glBindSampler GLAD_API_CALL PFNGLBINDSAMPLERSPROC glad_glBindSamplers; #define glBindSamplers glad_glBindSamplers GLAD_API_CALL PFNGLBINDTEXTUREPROC glad_glBindTexture; #define glBindTexture glad_glBindTexture GLAD_API_CALL PFNGLBINDTEXTUREUNITPROC glad_glBindTextureUnit; #define glBindTextureUnit glad_glBindTextureUnit GLAD_API_CALL PFNGLBINDTEXTURESPROC glad_glBindTextures; #define glBindTextures glad_glBindTextures GLAD_API_CALL PFNGLBINDTRANSFORMFEEDBACKPROC glad_glBindTransformFeedback; #define glBindTransformFeedback glad_glBindTransformFeedback GLAD_API_CALL PFNGLBINDVERTEXARRAYPROC glad_glBindVertexArray; #define glBindVertexArray glad_glBindVertexArray GLAD_API_CALL PFNGLBINDVERTEXBUFFERPROC glad_glBindVertexBuffer; #define glBindVertexBuffer glad_glBindVertexBuffer GLAD_API_CALL PFNGLBINDVERTEXBUFFERSPROC glad_glBindVertexBuffers; #define glBindVertexBuffers glad_glBindVertexBuffers GLAD_API_CALL PFNGLBITMAPPROC glad_glBitmap; #define glBitmap glad_glBitmap GLAD_API_CALL PFNGLBLENDBARRIERPROC glad_glBlendBarrier; #define glBlendBarrier glad_glBlendBarrier GLAD_API_CALL PFNGLBLENDBARRIERKHRPROC glad_glBlendBarrierKHR; #define glBlendBarrierKHR glad_glBlendBarrierKHR GLAD_API_CALL PFNGLBLENDCOLORPROC glad_glBlendColor; #define glBlendColor glad_glBlendColor GLAD_API_CALL PFNGLBLENDEQUATIONPROC glad_glBlendEquation; #define glBlendEquation glad_glBlendEquation GLAD_API_CALL PFNGLBLENDEQUATIONSEPARATEPROC glad_glBlendEquationSeparate; #define glBlendEquationSeparate glad_glBlendEquationSeparate GLAD_API_CALL PFNGLBLENDEQUATIONSEPARATEIPROC glad_glBlendEquationSeparatei; #define glBlendEquationSeparatei glad_glBlendEquationSeparatei GLAD_API_CALL PFNGLBLENDEQUATIONSEPARATEIARBPROC glad_glBlendEquationSeparateiARB; #define glBlendEquationSeparateiARB glad_glBlendEquationSeparateiARB GLAD_API_CALL PFNGLBLENDEQUATIONIPROC glad_glBlendEquationi; #define glBlendEquationi glad_glBlendEquationi GLAD_API_CALL PFNGLBLENDEQUATIONIARBPROC glad_glBlendEquationiARB; #define glBlendEquationiARB glad_glBlendEquationiARB GLAD_API_CALL PFNGLBLENDFUNCPROC glad_glBlendFunc; #define glBlendFunc glad_glBlendFunc GLAD_API_CALL PFNGLBLENDFUNCSEPARATEPROC glad_glBlendFuncSeparate; #define glBlendFuncSeparate glad_glBlendFuncSeparate GLAD_API_CALL PFNGLBLENDFUNCSEPARATEIPROC glad_glBlendFuncSeparatei; #define glBlendFuncSeparatei glad_glBlendFuncSeparatei GLAD_API_CALL PFNGLBLENDFUNCSEPARATEIARBPROC glad_glBlendFuncSeparateiARB; #define glBlendFuncSeparateiARB glad_glBlendFuncSeparateiARB GLAD_API_CALL PFNGLBLENDFUNCIPROC glad_glBlendFunci; #define glBlendFunci glad_glBlendFunci GLAD_API_CALL PFNGLBLENDFUNCIARBPROC glad_glBlendFunciARB; #define glBlendFunciARB glad_glBlendFunciARB GLAD_API_CALL PFNGLBLITFRAMEBUFFERPROC glad_glBlitFramebuffer; #define glBlitFramebuffer glad_glBlitFramebuffer GLAD_API_CALL PFNGLBLITNAMEDFRAMEBUFFERPROC glad_glBlitNamedFramebuffer; #define glBlitNamedFramebuffer glad_glBlitNamedFramebuffer GLAD_API_CALL PFNGLBUFFERDATAPROC glad_glBufferData; #define glBufferData glad_glBufferData GLAD_API_CALL PFNGLBUFFERDATAARBPROC glad_glBufferDataARB; #define glBufferDataARB glad_glBufferDataARB GLAD_API_CALL PFNGLBUFFERPAGECOMMITMENTARBPROC glad_glBufferPageCommitmentARB; #define glBufferPageCommitmentARB glad_glBufferPageCommitmentARB GLAD_API_CALL PFNGLBUFFERSTORAGEPROC glad_glBufferStorage; #define glBufferStorage glad_glBufferStorage GLAD_API_CALL PFNGLBUFFERSUBDATAPROC glad_glBufferSubData; #define glBufferSubData glad_glBufferSubData GLAD_API_CALL PFNGLBUFFERSUBDATAARBPROC glad_glBufferSubDataARB; #define glBufferSubDataARB glad_glBufferSubDataARB GLAD_API_CALL PFNGLCALLLISTPROC glad_glCallList; #define glCallList glad_glCallList GLAD_API_CALL PFNGLCALLLISTSPROC glad_glCallLists; #define glCallLists glad_glCallLists GLAD_API_CALL PFNGLCHECKFRAMEBUFFERSTATUSPROC glad_glCheckFramebufferStatus; #define glCheckFramebufferStatus glad_glCheckFramebufferStatus GLAD_API_CALL PFNGLCHECKNAMEDFRAMEBUFFERSTATUSPROC glad_glCheckNamedFramebufferStatus; #define glCheckNamedFramebufferStatus glad_glCheckNamedFramebufferStatus GLAD_API_CALL PFNGLCLAMPCOLORPROC glad_glClampColor; #define glClampColor glad_glClampColor GLAD_API_CALL PFNGLCLAMPCOLORARBPROC glad_glClampColorARB; #define glClampColorARB glad_glClampColorARB GLAD_API_CALL PFNGLCLEARPROC glad_glClear; #define glClear glad_glClear GLAD_API_CALL PFNGLCLEARACCUMPROC glad_glClearAccum; #define glClearAccum glad_glClearAccum GLAD_API_CALL PFNGLCLEARBUFFERDATAPROC glad_glClearBufferData; #define glClearBufferData glad_glClearBufferData GLAD_API_CALL PFNGLCLEARBUFFERSUBDATAPROC glad_glClearBufferSubData; #define glClearBufferSubData glad_glClearBufferSubData GLAD_API_CALL PFNGLCLEARBUFFERFIPROC glad_glClearBufferfi; #define glClearBufferfi glad_glClearBufferfi GLAD_API_CALL PFNGLCLEARBUFFERFVPROC glad_glClearBufferfv; #define glClearBufferfv glad_glClearBufferfv GLAD_API_CALL PFNGLCLEARBUFFERIVPROC glad_glClearBufferiv; #define glClearBufferiv glad_glClearBufferiv GLAD_API_CALL PFNGLCLEARBUFFERUIVPROC glad_glClearBufferuiv; #define glClearBufferuiv glad_glClearBufferuiv GLAD_API_CALL PFNGLCLEARCOLORPROC glad_glClearColor; #define glClearColor glad_glClearColor GLAD_API_CALL PFNGLCLEARDEPTHPROC glad_glClearDepth; #define glClearDepth glad_glClearDepth GLAD_API_CALL PFNGLCLEARDEPTHFPROC glad_glClearDepthf; #define glClearDepthf glad_glClearDepthf GLAD_API_CALL PFNGLCLEARINDEXPROC glad_glClearIndex; #define glClearIndex glad_glClearIndex GLAD_API_CALL PFNGLCLEARNAMEDBUFFERDATAPROC glad_glClearNamedBufferData; #define glClearNamedBufferData glad_glClearNamedBufferData GLAD_API_CALL PFNGLCLEARNAMEDBUFFERSUBDATAPROC glad_glClearNamedBufferSubData; #define glClearNamedBufferSubData glad_glClearNamedBufferSubData GLAD_API_CALL PFNGLCLEARNAMEDFRAMEBUFFERFIPROC glad_glClearNamedFramebufferfi; #define glClearNamedFramebufferfi glad_glClearNamedFramebufferfi GLAD_API_CALL PFNGLCLEARNAMEDFRAMEBUFFERFVPROC glad_glClearNamedFramebufferfv; #define glClearNamedFramebufferfv glad_glClearNamedFramebufferfv GLAD_API_CALL PFNGLCLEARNAMEDFRAMEBUFFERIVPROC glad_glClearNamedFramebufferiv; #define glClearNamedFramebufferiv glad_glClearNamedFramebufferiv GLAD_API_CALL PFNGLCLEARNAMEDFRAMEBUFFERUIVPROC glad_glClearNamedFramebufferuiv; #define glClearNamedFramebufferuiv glad_glClearNamedFramebufferuiv GLAD_API_CALL PFNGLCLEARSTENCILPROC glad_glClearStencil; #define glClearStencil glad_glClearStencil GLAD_API_CALL PFNGLCLEARTEXIMAGEPROC glad_glClearTexImage; #define glClearTexImage glad_glClearTexImage GLAD_API_CALL PFNGLCLEARTEXSUBIMAGEPROC glad_glClearTexSubImage; #define glClearTexSubImage glad_glClearTexSubImage GLAD_API_CALL PFNGLCLIENTACTIVETEXTUREPROC glad_glClientActiveTexture; #define glClientActiveTexture glad_glClientActiveTexture GLAD_API_CALL PFNGLCLIENTACTIVETEXTUREARBPROC glad_glClientActiveTextureARB; #define glClientActiveTextureARB glad_glClientActiveTextureARB GLAD_API_CALL PFNGLCLIENTWAITSYNCPROC glad_glClientWaitSync; #define glClientWaitSync glad_glClientWaitSync GLAD_API_CALL PFNGLCLIPCONTROLPROC glad_glClipControl; #define glClipControl glad_glClipControl GLAD_API_CALL PFNGLCLIPPLANEPROC glad_glClipPlane; #define glClipPlane glad_glClipPlane GLAD_API_CALL PFNGLCOLOR3BPROC glad_glColor3b; #define glColor3b glad_glColor3b GLAD_API_CALL PFNGLCOLOR3BVPROC glad_glColor3bv; #define glColor3bv glad_glColor3bv GLAD_API_CALL PFNGLCOLOR3DPROC glad_glColor3d; #define glColor3d glad_glColor3d GLAD_API_CALL PFNGLCOLOR3DVPROC glad_glColor3dv; #define glColor3dv glad_glColor3dv GLAD_API_CALL PFNGLCOLOR3FPROC glad_glColor3f; #define glColor3f glad_glColor3f GLAD_API_CALL PFNGLCOLOR3FVPROC glad_glColor3fv; #define glColor3fv glad_glColor3fv GLAD_API_CALL PFNGLCOLOR3IPROC glad_glColor3i; #define glColor3i glad_glColor3i GLAD_API_CALL PFNGLCOLOR3IVPROC glad_glColor3iv; #define glColor3iv glad_glColor3iv GLAD_API_CALL PFNGLCOLOR3SPROC glad_glColor3s; #define glColor3s glad_glColor3s GLAD_API_CALL PFNGLCOLOR3SVPROC glad_glColor3sv; #define glColor3sv glad_glColor3sv GLAD_API_CALL PFNGLCOLOR3UBPROC glad_glColor3ub; #define glColor3ub glad_glColor3ub GLAD_API_CALL PFNGLCOLOR3UBVPROC glad_glColor3ubv; #define glColor3ubv glad_glColor3ubv GLAD_API_CALL PFNGLCOLOR3UIPROC glad_glColor3ui; #define glColor3ui glad_glColor3ui GLAD_API_CALL PFNGLCOLOR3UIVPROC glad_glColor3uiv; #define glColor3uiv glad_glColor3uiv GLAD_API_CALL PFNGLCOLOR3USPROC glad_glColor3us; #define glColor3us glad_glColor3us GLAD_API_CALL PFNGLCOLOR3USVPROC glad_glColor3usv; #define glColor3usv glad_glColor3usv GLAD_API_CALL PFNGLCOLOR4BPROC glad_glColor4b; #define glColor4b glad_glColor4b GLAD_API_CALL PFNGLCOLOR4BVPROC glad_glColor4bv; #define glColor4bv glad_glColor4bv GLAD_API_CALL PFNGLCOLOR4DPROC glad_glColor4d; #define glColor4d glad_glColor4d GLAD_API_CALL PFNGLCOLOR4DVPROC glad_glColor4dv; #define glColor4dv glad_glColor4dv GLAD_API_CALL PFNGLCOLOR4FPROC glad_glColor4f; #define glColor4f glad_glColor4f GLAD_API_CALL PFNGLCOLOR4FVPROC glad_glColor4fv; #define glColor4fv glad_glColor4fv GLAD_API_CALL PFNGLCOLOR4IPROC glad_glColor4i; #define glColor4i glad_glColor4i GLAD_API_CALL PFNGLCOLOR4IVPROC glad_glColor4iv; #define glColor4iv glad_glColor4iv GLAD_API_CALL PFNGLCOLOR4SPROC glad_glColor4s; #define glColor4s glad_glColor4s GLAD_API_CALL PFNGLCOLOR4SVPROC glad_glColor4sv; #define glColor4sv glad_glColor4sv GLAD_API_CALL PFNGLCOLOR4UBPROC glad_glColor4ub; #define glColor4ub glad_glColor4ub GLAD_API_CALL PFNGLCOLOR4UBVPROC glad_glColor4ubv; #define glColor4ubv glad_glColor4ubv GLAD_API_CALL PFNGLCOLOR4UIPROC glad_glColor4ui; #define glColor4ui glad_glColor4ui GLAD_API_CALL PFNGLCOLOR4UIVPROC glad_glColor4uiv; #define glColor4uiv glad_glColor4uiv GLAD_API_CALL PFNGLCOLOR4USPROC glad_glColor4us; #define glColor4us glad_glColor4us GLAD_API_CALL PFNGLCOLOR4USVPROC glad_glColor4usv; #define glColor4usv glad_glColor4usv GLAD_API_CALL PFNGLCOLORMASKPROC glad_glColorMask; #define glColorMask glad_glColorMask GLAD_API_CALL PFNGLCOLORMASKIPROC glad_glColorMaski; #define glColorMaski glad_glColorMaski GLAD_API_CALL PFNGLCOLORMATERIALPROC glad_glColorMaterial; #define glColorMaterial glad_glColorMaterial GLAD_API_CALL PFNGLCOLORP3UIPROC glad_glColorP3ui; #define glColorP3ui glad_glColorP3ui GLAD_API_CALL PFNGLCOLORP3UIVPROC glad_glColorP3uiv; #define glColorP3uiv glad_glColorP3uiv GLAD_API_CALL PFNGLCOLORP4UIPROC glad_glColorP4ui; #define glColorP4ui glad_glColorP4ui GLAD_API_CALL PFNGLCOLORP4UIVPROC glad_glColorP4uiv; #define glColorP4uiv glad_glColorP4uiv GLAD_API_CALL PFNGLCOLORPOINTERPROC glad_glColorPointer; #define glColorPointer glad_glColorPointer GLAD_API_CALL PFNGLCOLORSUBTABLEPROC glad_glColorSubTable; #define glColorSubTable glad_glColorSubTable GLAD_API_CALL PFNGLCOLORTABLEPROC glad_glColorTable; #define glColorTable glad_glColorTable GLAD_API_CALL PFNGLCOLORTABLEPARAMETERFVPROC glad_glColorTableParameterfv; #define glColorTableParameterfv glad_glColorTableParameterfv GLAD_API_CALL PFNGLCOLORTABLEPARAMETERIVPROC glad_glColorTableParameteriv; #define glColorTableParameteriv glad_glColorTableParameteriv GLAD_API_CALL PFNGLCOMPILESHADERPROC glad_glCompileShader; #define glCompileShader glad_glCompileShader GLAD_API_CALL PFNGLCOMPILESHADERARBPROC glad_glCompileShaderARB; #define glCompileShaderARB glad_glCompileShaderARB GLAD_API_CALL PFNGLCOMPILESHADERINCLUDEARBPROC glad_glCompileShaderIncludeARB; #define glCompileShaderIncludeARB glad_glCompileShaderIncludeARB GLAD_API_CALL PFNGLCOMPRESSEDTEXIMAGE1DPROC glad_glCompressedTexImage1D; #define glCompressedTexImage1D glad_glCompressedTexImage1D GLAD_API_CALL PFNGLCOMPRESSEDTEXIMAGE1DARBPROC glad_glCompressedTexImage1DARB; #define glCompressedTexImage1DARB glad_glCompressedTexImage1DARB GLAD_API_CALL PFNGLCOMPRESSEDTEXIMAGE2DPROC glad_glCompressedTexImage2D; #define glCompressedTexImage2D glad_glCompressedTexImage2D GLAD_API_CALL PFNGLCOMPRESSEDTEXIMAGE2DARBPROC glad_glCompressedTexImage2DARB; #define glCompressedTexImage2DARB glad_glCompressedTexImage2DARB GLAD_API_CALL PFNGLCOMPRESSEDTEXIMAGE3DPROC glad_glCompressedTexImage3D; #define glCompressedTexImage3D glad_glCompressedTexImage3D GLAD_API_CALL PFNGLCOMPRESSEDTEXIMAGE3DARBPROC glad_glCompressedTexImage3DARB; #define glCompressedTexImage3DARB glad_glCompressedTexImage3DARB GLAD_API_CALL PFNGLCOMPRESSEDTEXSUBIMAGE1DPROC glad_glCompressedTexSubImage1D; #define glCompressedTexSubImage1D glad_glCompressedTexSubImage1D GLAD_API_CALL PFNGLCOMPRESSEDTEXSUBIMAGE1DARBPROC glad_glCompressedTexSubImage1DARB; #define glCompressedTexSubImage1DARB glad_glCompressedTexSubImage1DARB GLAD_API_CALL PFNGLCOMPRESSEDTEXSUBIMAGE2DPROC glad_glCompressedTexSubImage2D; #define glCompressedTexSubImage2D glad_glCompressedTexSubImage2D GLAD_API_CALL PFNGLCOMPRESSEDTEXSUBIMAGE2DARBPROC glad_glCompressedTexSubImage2DARB; #define glCompressedTexSubImage2DARB glad_glCompressedTexSubImage2DARB GLAD_API_CALL PFNGLCOMPRESSEDTEXSUBIMAGE3DPROC glad_glCompressedTexSubImage3D; #define glCompressedTexSubImage3D glad_glCompressedTexSubImage3D GLAD_API_CALL PFNGLCOMPRESSEDTEXSUBIMAGE3DARBPROC glad_glCompressedTexSubImage3DARB; #define glCompressedTexSubImage3DARB glad_glCompressedTexSubImage3DARB GLAD_API_CALL PFNGLCOMPRESSEDTEXTURESUBIMAGE1DPROC glad_glCompressedTextureSubImage1D; #define glCompressedTextureSubImage1D glad_glCompressedTextureSubImage1D GLAD_API_CALL PFNGLCOMPRESSEDTEXTURESUBIMAGE2DPROC glad_glCompressedTextureSubImage2D; #define glCompressedTextureSubImage2D glad_glCompressedTextureSubImage2D GLAD_API_CALL PFNGLCOMPRESSEDTEXTURESUBIMAGE3DPROC glad_glCompressedTextureSubImage3D; #define glCompressedTextureSubImage3D glad_glCompressedTextureSubImage3D GLAD_API_CALL PFNGLCONVOLUTIONFILTER1DPROC glad_glConvolutionFilter1D; #define glConvolutionFilter1D glad_glConvolutionFilter1D GLAD_API_CALL PFNGLCONVOLUTIONFILTER2DPROC glad_glConvolutionFilter2D; #define glConvolutionFilter2D glad_glConvolutionFilter2D GLAD_API_CALL PFNGLCONVOLUTIONPARAMETERFPROC glad_glConvolutionParameterf; #define glConvolutionParameterf glad_glConvolutionParameterf GLAD_API_CALL PFNGLCONVOLUTIONPARAMETERFVPROC glad_glConvolutionParameterfv; #define glConvolutionParameterfv glad_glConvolutionParameterfv GLAD_API_CALL PFNGLCONVOLUTIONPARAMETERIPROC glad_glConvolutionParameteri; #define glConvolutionParameteri glad_glConvolutionParameteri GLAD_API_CALL PFNGLCONVOLUTIONPARAMETERIVPROC glad_glConvolutionParameteriv; #define glConvolutionParameteriv glad_glConvolutionParameteriv GLAD_API_CALL PFNGLCOPYBUFFERSUBDATAPROC glad_glCopyBufferSubData; #define glCopyBufferSubData glad_glCopyBufferSubData GLAD_API_CALL PFNGLCOPYCOLORSUBTABLEPROC glad_glCopyColorSubTable; #define glCopyColorSubTable glad_glCopyColorSubTable GLAD_API_CALL PFNGLCOPYCOLORTABLEPROC glad_glCopyColorTable; #define glCopyColorTable glad_glCopyColorTable GLAD_API_CALL PFNGLCOPYCONVOLUTIONFILTER1DPROC glad_glCopyConvolutionFilter1D; #define glCopyConvolutionFilter1D glad_glCopyConvolutionFilter1D GLAD_API_CALL PFNGLCOPYCONVOLUTIONFILTER2DPROC glad_glCopyConvolutionFilter2D; #define glCopyConvolutionFilter2D glad_glCopyConvolutionFilter2D GLAD_API_CALL PFNGLCOPYIMAGESUBDATAPROC glad_glCopyImageSubData; #define glCopyImageSubData glad_glCopyImageSubData GLAD_API_CALL PFNGLCOPYNAMEDBUFFERSUBDATAPROC glad_glCopyNamedBufferSubData; #define glCopyNamedBufferSubData glad_glCopyNamedBufferSubData GLAD_API_CALL PFNGLCOPYPIXELSPROC glad_glCopyPixels; #define glCopyPixels glad_glCopyPixels GLAD_API_CALL PFNGLCOPYTEXIMAGE1DPROC glad_glCopyTexImage1D; #define glCopyTexImage1D glad_glCopyTexImage1D GLAD_API_CALL PFNGLCOPYTEXIMAGE2DPROC glad_glCopyTexImage2D; #define glCopyTexImage2D glad_glCopyTexImage2D GLAD_API_CALL PFNGLCOPYTEXSUBIMAGE1DPROC glad_glCopyTexSubImage1D; #define glCopyTexSubImage1D glad_glCopyTexSubImage1D GLAD_API_CALL PFNGLCOPYTEXSUBIMAGE2DPROC glad_glCopyTexSubImage2D; #define glCopyTexSubImage2D glad_glCopyTexSubImage2D GLAD_API_CALL PFNGLCOPYTEXSUBIMAGE3DPROC glad_glCopyTexSubImage3D; #define glCopyTexSubImage3D glad_glCopyTexSubImage3D GLAD_API_CALL PFNGLCOPYTEXTURESUBIMAGE1DPROC glad_glCopyTextureSubImage1D; #define glCopyTextureSubImage1D glad_glCopyTextureSubImage1D GLAD_API_CALL PFNGLCOPYTEXTURESUBIMAGE2DPROC glad_glCopyTextureSubImage2D; #define glCopyTextureSubImage2D glad_glCopyTextureSubImage2D GLAD_API_CALL PFNGLCOPYTEXTURESUBIMAGE3DPROC glad_glCopyTextureSubImage3D; #define glCopyTextureSubImage3D glad_glCopyTextureSubImage3D GLAD_API_CALL PFNGLCREATEBUFFERSPROC glad_glCreateBuffers; #define glCreateBuffers glad_glCreateBuffers GLAD_API_CALL PFNGLCREATEFRAMEBUFFERSPROC glad_glCreateFramebuffers; #define glCreateFramebuffers glad_glCreateFramebuffers GLAD_API_CALL PFNGLCREATEPROGRAMPROC glad_glCreateProgram; #define glCreateProgram glad_glCreateProgram GLAD_API_CALL PFNGLCREATEPROGRAMOBJECTARBPROC glad_glCreateProgramObjectARB; #define glCreateProgramObjectARB glad_glCreateProgramObjectARB GLAD_API_CALL PFNGLCREATEPROGRAMPIPELINESPROC glad_glCreateProgramPipelines; #define glCreateProgramPipelines glad_glCreateProgramPipelines GLAD_API_CALL PFNGLCREATEQUERIESPROC glad_glCreateQueries; #define glCreateQueries glad_glCreateQueries GLAD_API_CALL PFNGLCREATERENDERBUFFERSPROC glad_glCreateRenderbuffers; #define glCreateRenderbuffers glad_glCreateRenderbuffers GLAD_API_CALL PFNGLCREATESAMPLERSPROC glad_glCreateSamplers; #define glCreateSamplers glad_glCreateSamplers GLAD_API_CALL PFNGLCREATESHADERPROC glad_glCreateShader; #define glCreateShader glad_glCreateShader GLAD_API_CALL PFNGLCREATESHADEROBJECTARBPROC glad_glCreateShaderObjectARB; #define glCreateShaderObjectARB glad_glCreateShaderObjectARB GLAD_API_CALL PFNGLCREATESHADERPROGRAMVPROC glad_glCreateShaderProgramv; #define glCreateShaderProgramv glad_glCreateShaderProgramv GLAD_API_CALL PFNGLCREATESYNCFROMCLEVENTARBPROC glad_glCreateSyncFromCLeventARB; #define glCreateSyncFromCLeventARB glad_glCreateSyncFromCLeventARB GLAD_API_CALL PFNGLCREATETEXTURESPROC glad_glCreateTextures; #define glCreateTextures glad_glCreateTextures GLAD_API_CALL PFNGLCREATETRANSFORMFEEDBACKSPROC glad_glCreateTransformFeedbacks; #define glCreateTransformFeedbacks glad_glCreateTransformFeedbacks GLAD_API_CALL PFNGLCREATEVERTEXARRAYSPROC glad_glCreateVertexArrays; #define glCreateVertexArrays glad_glCreateVertexArrays GLAD_API_CALL PFNGLCULLFACEPROC glad_glCullFace; #define glCullFace glad_glCullFace GLAD_API_CALL PFNGLCURRENTPALETTEMATRIXARBPROC glad_glCurrentPaletteMatrixARB; #define glCurrentPaletteMatrixARB glad_glCurrentPaletteMatrixARB GLAD_API_CALL PFNGLDEBUGMESSAGECALLBACKPROC glad_glDebugMessageCallback; #define glDebugMessageCallback glad_glDebugMessageCallback GLAD_API_CALL PFNGLDEBUGMESSAGECALLBACKARBPROC glad_glDebugMessageCallbackARB; #define glDebugMessageCallbackARB glad_glDebugMessageCallbackARB GLAD_API_CALL PFNGLDEBUGMESSAGECONTROLPROC glad_glDebugMessageControl; #define glDebugMessageControl glad_glDebugMessageControl GLAD_API_CALL PFNGLDEBUGMESSAGECONTROLARBPROC glad_glDebugMessageControlARB; #define glDebugMessageControlARB glad_glDebugMessageControlARB GLAD_API_CALL PFNGLDEBUGMESSAGEINSERTPROC glad_glDebugMessageInsert; #define glDebugMessageInsert glad_glDebugMessageInsert GLAD_API_CALL PFNGLDEBUGMESSAGEINSERTARBPROC glad_glDebugMessageInsertARB; #define glDebugMessageInsertARB glad_glDebugMessageInsertARB GLAD_API_CALL PFNGLDELETEBUFFERSPROC glad_glDeleteBuffers; #define glDeleteBuffers glad_glDeleteBuffers GLAD_API_CALL PFNGLDELETEBUFFERSARBPROC glad_glDeleteBuffersARB; #define glDeleteBuffersARB glad_glDeleteBuffersARB GLAD_API_CALL PFNGLDELETEFRAMEBUFFERSPROC glad_glDeleteFramebuffers; #define glDeleteFramebuffers glad_glDeleteFramebuffers GLAD_API_CALL PFNGLDELETELISTSPROC glad_glDeleteLists; #define glDeleteLists glad_glDeleteLists GLAD_API_CALL PFNGLDELETENAMEDSTRINGARBPROC glad_glDeleteNamedStringARB; #define glDeleteNamedStringARB glad_glDeleteNamedStringARB GLAD_API_CALL PFNGLDELETEOBJECTARBPROC glad_glDeleteObjectARB; #define glDeleteObjectARB glad_glDeleteObjectARB GLAD_API_CALL PFNGLDELETEPROGRAMPROC glad_glDeleteProgram; #define glDeleteProgram glad_glDeleteProgram GLAD_API_CALL PFNGLDELETEPROGRAMPIPELINESPROC glad_glDeleteProgramPipelines; #define glDeleteProgramPipelines glad_glDeleteProgramPipelines GLAD_API_CALL PFNGLDELETEPROGRAMSARBPROC glad_glDeleteProgramsARB; #define glDeleteProgramsARB glad_glDeleteProgramsARB GLAD_API_CALL PFNGLDELETEQUERIESPROC glad_glDeleteQueries; #define glDeleteQueries glad_glDeleteQueries GLAD_API_CALL PFNGLDELETEQUERIESARBPROC glad_glDeleteQueriesARB; #define glDeleteQueriesARB glad_glDeleteQueriesARB GLAD_API_CALL PFNGLDELETERENDERBUFFERSPROC glad_glDeleteRenderbuffers; #define glDeleteRenderbuffers glad_glDeleteRenderbuffers GLAD_API_CALL PFNGLDELETESAMPLERSPROC glad_glDeleteSamplers; #define glDeleteSamplers glad_glDeleteSamplers GLAD_API_CALL PFNGLDELETESHADERPROC glad_glDeleteShader; #define glDeleteShader glad_glDeleteShader GLAD_API_CALL PFNGLDELETESYNCPROC glad_glDeleteSync; #define glDeleteSync glad_glDeleteSync GLAD_API_CALL PFNGLDELETETEXTURESPROC glad_glDeleteTextures; #define glDeleteTextures glad_glDeleteTextures GLAD_API_CALL PFNGLDELETETRANSFORMFEEDBACKSPROC glad_glDeleteTransformFeedbacks; #define glDeleteTransformFeedbacks glad_glDeleteTransformFeedbacks GLAD_API_CALL PFNGLDELETEVERTEXARRAYSPROC glad_glDeleteVertexArrays; #define glDeleteVertexArrays glad_glDeleteVertexArrays GLAD_API_CALL PFNGLDEPTHFUNCPROC glad_glDepthFunc; #define glDepthFunc glad_glDepthFunc GLAD_API_CALL PFNGLDEPTHMASKPROC glad_glDepthMask; #define glDepthMask glad_glDepthMask GLAD_API_CALL PFNGLDEPTHRANGEPROC glad_glDepthRange; #define glDepthRange glad_glDepthRange GLAD_API_CALL PFNGLDEPTHRANGEARRAYDVNVPROC glad_glDepthRangeArraydvNV; #define glDepthRangeArraydvNV glad_glDepthRangeArraydvNV GLAD_API_CALL PFNGLDEPTHRANGEARRAYVPROC glad_glDepthRangeArrayv; #define glDepthRangeArrayv glad_glDepthRangeArrayv GLAD_API_CALL PFNGLDEPTHRANGEINDEXEDPROC glad_glDepthRangeIndexed; #define glDepthRangeIndexed glad_glDepthRangeIndexed GLAD_API_CALL PFNGLDEPTHRANGEINDEXEDDNVPROC glad_glDepthRangeIndexeddNV; #define glDepthRangeIndexeddNV glad_glDepthRangeIndexeddNV GLAD_API_CALL PFNGLDEPTHRANGEFPROC glad_glDepthRangef; #define glDepthRangef glad_glDepthRangef GLAD_API_CALL PFNGLDETACHOBJECTARBPROC glad_glDetachObjectARB; #define glDetachObjectARB glad_glDetachObjectARB GLAD_API_CALL PFNGLDETACHSHADERPROC glad_glDetachShader; #define glDetachShader glad_glDetachShader GLAD_API_CALL PFNGLDISABLEPROC glad_glDisable; #define glDisable glad_glDisable GLAD_API_CALL PFNGLDISABLECLIENTSTATEPROC glad_glDisableClientState; #define glDisableClientState glad_glDisableClientState GLAD_API_CALL PFNGLDISABLEVERTEXARRAYATTRIBPROC glad_glDisableVertexArrayAttrib; #define glDisableVertexArrayAttrib glad_glDisableVertexArrayAttrib GLAD_API_CALL PFNGLDISABLEVERTEXATTRIBARRAYPROC glad_glDisableVertexAttribArray; #define glDisableVertexAttribArray glad_glDisableVertexAttribArray GLAD_API_CALL PFNGLDISABLEVERTEXATTRIBARRAYARBPROC glad_glDisableVertexAttribArrayARB; #define glDisableVertexAttribArrayARB glad_glDisableVertexAttribArrayARB GLAD_API_CALL PFNGLDISABLEIPROC glad_glDisablei; #define glDisablei glad_glDisablei GLAD_API_CALL PFNGLDISPATCHCOMPUTEPROC glad_glDispatchCompute; #define glDispatchCompute glad_glDispatchCompute GLAD_API_CALL PFNGLDISPATCHCOMPUTEGROUPSIZEARBPROC glad_glDispatchComputeGroupSizeARB; #define glDispatchComputeGroupSizeARB glad_glDispatchComputeGroupSizeARB GLAD_API_CALL PFNGLDISPATCHCOMPUTEINDIRECTPROC glad_glDispatchComputeIndirect; #define glDispatchComputeIndirect glad_glDispatchComputeIndirect GLAD_API_CALL PFNGLDRAWARRAYSPROC glad_glDrawArrays; #define glDrawArrays glad_glDrawArrays GLAD_API_CALL PFNGLDRAWARRAYSINDIRECTPROC glad_glDrawArraysIndirect; #define glDrawArraysIndirect glad_glDrawArraysIndirect GLAD_API_CALL PFNGLDRAWARRAYSINSTANCEDPROC glad_glDrawArraysInstanced; #define glDrawArraysInstanced glad_glDrawArraysInstanced GLAD_API_CALL PFNGLDRAWARRAYSINSTANCEDARBPROC glad_glDrawArraysInstancedARB; #define glDrawArraysInstancedARB glad_glDrawArraysInstancedARB GLAD_API_CALL PFNGLDRAWARRAYSINSTANCEDBASEINSTANCEPROC glad_glDrawArraysInstancedBaseInstance; #define glDrawArraysInstancedBaseInstance glad_glDrawArraysInstancedBaseInstance GLAD_API_CALL PFNGLDRAWBUFFERPROC glad_glDrawBuffer; #define glDrawBuffer glad_glDrawBuffer GLAD_API_CALL PFNGLDRAWBUFFERSPROC glad_glDrawBuffers; #define glDrawBuffers glad_glDrawBuffers GLAD_API_CALL PFNGLDRAWBUFFERSARBPROC glad_glDrawBuffersARB; #define glDrawBuffersARB glad_glDrawBuffersARB GLAD_API_CALL PFNGLDRAWELEMENTSPROC glad_glDrawElements; #define glDrawElements glad_glDrawElements GLAD_API_CALL PFNGLDRAWELEMENTSBASEVERTEXPROC glad_glDrawElementsBaseVertex; #define glDrawElementsBaseVertex glad_glDrawElementsBaseVertex GLAD_API_CALL PFNGLDRAWELEMENTSINDIRECTPROC glad_glDrawElementsIndirect; #define glDrawElementsIndirect glad_glDrawElementsIndirect GLAD_API_CALL PFNGLDRAWELEMENTSINSTANCEDPROC glad_glDrawElementsInstanced; #define glDrawElementsInstanced glad_glDrawElementsInstanced GLAD_API_CALL PFNGLDRAWELEMENTSINSTANCEDARBPROC glad_glDrawElementsInstancedARB; #define glDrawElementsInstancedARB glad_glDrawElementsInstancedARB GLAD_API_CALL PFNGLDRAWELEMENTSINSTANCEDBASEINSTANCEPROC glad_glDrawElementsInstancedBaseInstance; #define glDrawElementsInstancedBaseInstance glad_glDrawElementsInstancedBaseInstance GLAD_API_CALL PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXPROC glad_glDrawElementsInstancedBaseVertex; #define glDrawElementsInstancedBaseVertex glad_glDrawElementsInstancedBaseVertex GLAD_API_CALL PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXBASEINSTANCEPROC glad_glDrawElementsInstancedBaseVertexBaseInstance; #define glDrawElementsInstancedBaseVertexBaseInstance glad_glDrawElementsInstancedBaseVertexBaseInstance GLAD_API_CALL PFNGLDRAWPIXELSPROC glad_glDrawPixels; #define glDrawPixels glad_glDrawPixels GLAD_API_CALL PFNGLDRAWRANGEELEMENTSPROC glad_glDrawRangeElements; #define glDrawRangeElements glad_glDrawRangeElements GLAD_API_CALL PFNGLDRAWRANGEELEMENTSBASEVERTEXPROC glad_glDrawRangeElementsBaseVertex; #define glDrawRangeElementsBaseVertex glad_glDrawRangeElementsBaseVertex GLAD_API_CALL PFNGLDRAWTRANSFORMFEEDBACKPROC glad_glDrawTransformFeedback; #define glDrawTransformFeedback glad_glDrawTransformFeedback GLAD_API_CALL PFNGLDRAWTRANSFORMFEEDBACKINSTANCEDPROC glad_glDrawTransformFeedbackInstanced; #define glDrawTransformFeedbackInstanced glad_glDrawTransformFeedbackInstanced GLAD_API_CALL PFNGLDRAWTRANSFORMFEEDBACKSTREAMPROC glad_glDrawTransformFeedbackStream; #define glDrawTransformFeedbackStream glad_glDrawTransformFeedbackStream GLAD_API_CALL PFNGLDRAWTRANSFORMFEEDBACKSTREAMINSTANCEDPROC glad_glDrawTransformFeedbackStreamInstanced; #define glDrawTransformFeedbackStreamInstanced glad_glDrawTransformFeedbackStreamInstanced GLAD_API_CALL PFNGLEDGEFLAGPROC glad_glEdgeFlag; #define glEdgeFlag glad_glEdgeFlag GLAD_API_CALL PFNGLEDGEFLAGPOINTERPROC glad_glEdgeFlagPointer; #define glEdgeFlagPointer glad_glEdgeFlagPointer GLAD_API_CALL PFNGLEDGEFLAGVPROC glad_glEdgeFlagv; #define glEdgeFlagv glad_glEdgeFlagv GLAD_API_CALL PFNGLENABLEPROC glad_glEnable; #define glEnable glad_glEnable GLAD_API_CALL PFNGLENABLECLIENTSTATEPROC glad_glEnableClientState; #define glEnableClientState glad_glEnableClientState GLAD_API_CALL PFNGLENABLEVERTEXARRAYATTRIBPROC glad_glEnableVertexArrayAttrib; #define glEnableVertexArrayAttrib glad_glEnableVertexArrayAttrib GLAD_API_CALL PFNGLENABLEVERTEXATTRIBARRAYPROC glad_glEnableVertexAttribArray; #define glEnableVertexAttribArray glad_glEnableVertexAttribArray GLAD_API_CALL PFNGLENABLEVERTEXATTRIBARRAYARBPROC glad_glEnableVertexAttribArrayARB; #define glEnableVertexAttribArrayARB glad_glEnableVertexAttribArrayARB GLAD_API_CALL PFNGLENABLEIPROC glad_glEnablei; #define glEnablei glad_glEnablei GLAD_API_CALL PFNGLENDPROC glad_glEnd; #define glEnd glad_glEnd GLAD_API_CALL PFNGLENDCONDITIONALRENDERPROC glad_glEndConditionalRender; #define glEndConditionalRender glad_glEndConditionalRender GLAD_API_CALL PFNGLENDLISTPROC glad_glEndList; #define glEndList glad_glEndList GLAD_API_CALL PFNGLENDQUERYPROC glad_glEndQuery; #define glEndQuery glad_glEndQuery GLAD_API_CALL PFNGLENDQUERYARBPROC glad_glEndQueryARB; #define glEndQueryARB glad_glEndQueryARB GLAD_API_CALL PFNGLENDQUERYINDEXEDPROC glad_glEndQueryIndexed; #define glEndQueryIndexed glad_glEndQueryIndexed GLAD_API_CALL PFNGLENDTRANSFORMFEEDBACKPROC glad_glEndTransformFeedback; #define glEndTransformFeedback glad_glEndTransformFeedback GLAD_API_CALL PFNGLEVALCOORD1DPROC glad_glEvalCoord1d; #define glEvalCoord1d glad_glEvalCoord1d GLAD_API_CALL PFNGLEVALCOORD1DVPROC glad_glEvalCoord1dv; #define glEvalCoord1dv glad_glEvalCoord1dv GLAD_API_CALL PFNGLEVALCOORD1FPROC glad_glEvalCoord1f; #define glEvalCoord1f glad_glEvalCoord1f GLAD_API_CALL PFNGLEVALCOORD1FVPROC glad_glEvalCoord1fv; #define glEvalCoord1fv glad_glEvalCoord1fv GLAD_API_CALL PFNGLEVALCOORD2DPROC glad_glEvalCoord2d; #define glEvalCoord2d glad_glEvalCoord2d GLAD_API_CALL PFNGLEVALCOORD2DVPROC glad_glEvalCoord2dv; #define glEvalCoord2dv glad_glEvalCoord2dv GLAD_API_CALL PFNGLEVALCOORD2FPROC glad_glEvalCoord2f; #define glEvalCoord2f glad_glEvalCoord2f GLAD_API_CALL PFNGLEVALCOORD2FVPROC glad_glEvalCoord2fv; #define glEvalCoord2fv glad_glEvalCoord2fv GLAD_API_CALL PFNGLEVALMESH1PROC glad_glEvalMesh1; #define glEvalMesh1 glad_glEvalMesh1 GLAD_API_CALL PFNGLEVALMESH2PROC glad_glEvalMesh2; #define glEvalMesh2 glad_glEvalMesh2 GLAD_API_CALL PFNGLEVALPOINT1PROC glad_glEvalPoint1; #define glEvalPoint1 glad_glEvalPoint1 GLAD_API_CALL PFNGLEVALPOINT2PROC glad_glEvalPoint2; #define glEvalPoint2 glad_glEvalPoint2 GLAD_API_CALL PFNGLEVALUATEDEPTHVALUESARBPROC glad_glEvaluateDepthValuesARB; #define glEvaluateDepthValuesARB glad_glEvaluateDepthValuesARB GLAD_API_CALL PFNGLFEEDBACKBUFFERPROC glad_glFeedbackBuffer; #define glFeedbackBuffer glad_glFeedbackBuffer GLAD_API_CALL PFNGLFENCESYNCPROC glad_glFenceSync; #define glFenceSync glad_glFenceSync GLAD_API_CALL PFNGLFINISHPROC glad_glFinish; #define glFinish glad_glFinish GLAD_API_CALL PFNGLFLUSHPROC glad_glFlush; #define glFlush glad_glFlush GLAD_API_CALL PFNGLFLUSHMAPPEDBUFFERRANGEPROC glad_glFlushMappedBufferRange; #define glFlushMappedBufferRange glad_glFlushMappedBufferRange GLAD_API_CALL PFNGLFLUSHMAPPEDNAMEDBUFFERRANGEPROC glad_glFlushMappedNamedBufferRange; #define glFlushMappedNamedBufferRange glad_glFlushMappedNamedBufferRange GLAD_API_CALL PFNGLFOGCOORDPOINTERPROC glad_glFogCoordPointer; #define glFogCoordPointer glad_glFogCoordPointer GLAD_API_CALL PFNGLFOGCOORDDPROC glad_glFogCoordd; #define glFogCoordd glad_glFogCoordd GLAD_API_CALL PFNGLFOGCOORDDVPROC glad_glFogCoorddv; #define glFogCoorddv glad_glFogCoorddv GLAD_API_CALL PFNGLFOGCOORDFPROC glad_glFogCoordf; #define glFogCoordf glad_glFogCoordf GLAD_API_CALL PFNGLFOGCOORDFVPROC glad_glFogCoordfv; #define glFogCoordfv glad_glFogCoordfv GLAD_API_CALL PFNGLFOGFPROC glad_glFogf; #define glFogf glad_glFogf GLAD_API_CALL PFNGLFOGFVPROC glad_glFogfv; #define glFogfv glad_glFogfv GLAD_API_CALL PFNGLFOGIPROC glad_glFogi; #define glFogi glad_glFogi GLAD_API_CALL PFNGLFOGIVPROC glad_glFogiv; #define glFogiv glad_glFogiv GLAD_API_CALL PFNGLFRAMEBUFFERPARAMETERIPROC glad_glFramebufferParameteri; #define glFramebufferParameteri glad_glFramebufferParameteri GLAD_API_CALL PFNGLFRAMEBUFFERRENDERBUFFERPROC glad_glFramebufferRenderbuffer; #define glFramebufferRenderbuffer glad_glFramebufferRenderbuffer GLAD_API_CALL PFNGLFRAMEBUFFERSAMPLELOCATIONSFVARBPROC glad_glFramebufferSampleLocationsfvARB; #define glFramebufferSampleLocationsfvARB glad_glFramebufferSampleLocationsfvARB GLAD_API_CALL PFNGLFRAMEBUFFERTEXTUREPROC glad_glFramebufferTexture; #define glFramebufferTexture glad_glFramebufferTexture GLAD_API_CALL PFNGLFRAMEBUFFERTEXTURE1DPROC glad_glFramebufferTexture1D; #define glFramebufferTexture1D glad_glFramebufferTexture1D GLAD_API_CALL PFNGLFRAMEBUFFERTEXTURE2DPROC glad_glFramebufferTexture2D; #define glFramebufferTexture2D glad_glFramebufferTexture2D GLAD_API_CALL PFNGLFRAMEBUFFERTEXTURE3DPROC glad_glFramebufferTexture3D; #define glFramebufferTexture3D glad_glFramebufferTexture3D GLAD_API_CALL PFNGLFRAMEBUFFERTEXTUREARBPROC glad_glFramebufferTextureARB; #define glFramebufferTextureARB glad_glFramebufferTextureARB GLAD_API_CALL PFNGLFRAMEBUFFERTEXTUREFACEARBPROC glad_glFramebufferTextureFaceARB; #define glFramebufferTextureFaceARB glad_glFramebufferTextureFaceARB GLAD_API_CALL PFNGLFRAMEBUFFERTEXTURELAYERPROC glad_glFramebufferTextureLayer; #define glFramebufferTextureLayer glad_glFramebufferTextureLayer GLAD_API_CALL PFNGLFRAMEBUFFERTEXTURELAYERARBPROC glad_glFramebufferTextureLayerARB; #define glFramebufferTextureLayerARB glad_glFramebufferTextureLayerARB GLAD_API_CALL PFNGLFRONTFACEPROC glad_glFrontFace; #define glFrontFace glad_glFrontFace GLAD_API_CALL PFNGLFRUSTUMPROC glad_glFrustum; #define glFrustum glad_glFrustum GLAD_API_CALL PFNGLGENBUFFERSPROC glad_glGenBuffers; #define glGenBuffers glad_glGenBuffers GLAD_API_CALL PFNGLGENBUFFERSARBPROC glad_glGenBuffersARB; #define glGenBuffersARB glad_glGenBuffersARB GLAD_API_CALL PFNGLGENFRAMEBUFFERSPROC glad_glGenFramebuffers; #define glGenFramebuffers glad_glGenFramebuffers GLAD_API_CALL PFNGLGENLISTSPROC glad_glGenLists; #define glGenLists glad_glGenLists GLAD_API_CALL PFNGLGENPROGRAMPIPELINESPROC glad_glGenProgramPipelines; #define glGenProgramPipelines glad_glGenProgramPipelines GLAD_API_CALL PFNGLGENPROGRAMSARBPROC glad_glGenProgramsARB; #define glGenProgramsARB glad_glGenProgramsARB GLAD_API_CALL PFNGLGENQUERIESPROC glad_glGenQueries; #define glGenQueries glad_glGenQueries GLAD_API_CALL PFNGLGENQUERIESARBPROC glad_glGenQueriesARB; #define glGenQueriesARB glad_glGenQueriesARB GLAD_API_CALL PFNGLGENRENDERBUFFERSPROC glad_glGenRenderbuffers; #define glGenRenderbuffers glad_glGenRenderbuffers GLAD_API_CALL PFNGLGENSAMPLERSPROC glad_glGenSamplers; #define glGenSamplers glad_glGenSamplers GLAD_API_CALL PFNGLGENTEXTURESPROC glad_glGenTextures; #define glGenTextures glad_glGenTextures GLAD_API_CALL PFNGLGENTRANSFORMFEEDBACKSPROC glad_glGenTransformFeedbacks; #define glGenTransformFeedbacks glad_glGenTransformFeedbacks GLAD_API_CALL PFNGLGENVERTEXARRAYSPROC glad_glGenVertexArrays; #define glGenVertexArrays glad_glGenVertexArrays GLAD_API_CALL PFNGLGENERATEMIPMAPPROC glad_glGenerateMipmap; #define glGenerateMipmap glad_glGenerateMipmap GLAD_API_CALL PFNGLGENERATETEXTUREMIPMAPPROC glad_glGenerateTextureMipmap; #define glGenerateTextureMipmap glad_glGenerateTextureMipmap GLAD_API_CALL PFNGLGETACTIVEATOMICCOUNTERBUFFERIVPROC glad_glGetActiveAtomicCounterBufferiv; #define glGetActiveAtomicCounterBufferiv glad_glGetActiveAtomicCounterBufferiv GLAD_API_CALL PFNGLGETACTIVEATTRIBPROC glad_glGetActiveAttrib; #define glGetActiveAttrib glad_glGetActiveAttrib GLAD_API_CALL PFNGLGETACTIVEATTRIBARBPROC glad_glGetActiveAttribARB; #define glGetActiveAttribARB glad_glGetActiveAttribARB GLAD_API_CALL PFNGLGETACTIVESUBROUTINENAMEPROC glad_glGetActiveSubroutineName; #define glGetActiveSubroutineName glad_glGetActiveSubroutineName GLAD_API_CALL PFNGLGETACTIVESUBROUTINEUNIFORMNAMEPROC glad_glGetActiveSubroutineUniformName; #define glGetActiveSubroutineUniformName glad_glGetActiveSubroutineUniformName GLAD_API_CALL PFNGLGETACTIVESUBROUTINEUNIFORMIVPROC glad_glGetActiveSubroutineUniformiv; #define glGetActiveSubroutineUniformiv glad_glGetActiveSubroutineUniformiv GLAD_API_CALL PFNGLGETACTIVEUNIFORMPROC glad_glGetActiveUniform; #define glGetActiveUniform glad_glGetActiveUniform GLAD_API_CALL PFNGLGETACTIVEUNIFORMARBPROC glad_glGetActiveUniformARB; #define glGetActiveUniformARB glad_glGetActiveUniformARB GLAD_API_CALL PFNGLGETACTIVEUNIFORMBLOCKNAMEPROC glad_glGetActiveUniformBlockName; #define glGetActiveUniformBlockName glad_glGetActiveUniformBlockName GLAD_API_CALL PFNGLGETACTIVEUNIFORMBLOCKIVPROC glad_glGetActiveUniformBlockiv; #define glGetActiveUniformBlockiv glad_glGetActiveUniformBlockiv GLAD_API_CALL PFNGLGETACTIVEUNIFORMNAMEPROC glad_glGetActiveUniformName; #define glGetActiveUniformName glad_glGetActiveUniformName GLAD_API_CALL PFNGLGETACTIVEUNIFORMSIVPROC glad_glGetActiveUniformsiv; #define glGetActiveUniformsiv glad_glGetActiveUniformsiv GLAD_API_CALL PFNGLGETATTACHEDOBJECTSARBPROC glad_glGetAttachedObjectsARB; #define glGetAttachedObjectsARB glad_glGetAttachedObjectsARB GLAD_API_CALL PFNGLGETATTACHEDSHADERSPROC glad_glGetAttachedShaders; #define glGetAttachedShaders glad_glGetAttachedShaders GLAD_API_CALL PFNGLGETATTRIBLOCATIONPROC glad_glGetAttribLocation; #define glGetAttribLocation glad_glGetAttribLocation GLAD_API_CALL PFNGLGETATTRIBLOCATIONARBPROC glad_glGetAttribLocationARB; #define glGetAttribLocationARB glad_glGetAttribLocationARB GLAD_API_CALL PFNGLGETBOOLEANI_VPROC glad_glGetBooleani_v; #define glGetBooleani_v glad_glGetBooleani_v GLAD_API_CALL PFNGLGETBOOLEANVPROC glad_glGetBooleanv; #define glGetBooleanv glad_glGetBooleanv GLAD_API_CALL PFNGLGETBUFFERPARAMETERI64VPROC glad_glGetBufferParameteri64v; #define glGetBufferParameteri64v glad_glGetBufferParameteri64v GLAD_API_CALL PFNGLGETBUFFERPARAMETERIVPROC glad_glGetBufferParameteriv; #define glGetBufferParameteriv glad_glGetBufferParameteriv GLAD_API_CALL PFNGLGETBUFFERPARAMETERIVARBPROC glad_glGetBufferParameterivARB; #define glGetBufferParameterivARB glad_glGetBufferParameterivARB GLAD_API_CALL PFNGLGETBUFFERPOINTERVPROC glad_glGetBufferPointerv; #define glGetBufferPointerv glad_glGetBufferPointerv GLAD_API_CALL PFNGLGETBUFFERPOINTERVARBPROC glad_glGetBufferPointervARB; #define glGetBufferPointervARB glad_glGetBufferPointervARB GLAD_API_CALL PFNGLGETBUFFERSUBDATAPROC glad_glGetBufferSubData; #define glGetBufferSubData glad_glGetBufferSubData GLAD_API_CALL PFNGLGETBUFFERSUBDATAARBPROC glad_glGetBufferSubDataARB; #define glGetBufferSubDataARB glad_glGetBufferSubDataARB GLAD_API_CALL PFNGLGETCLIPPLANEPROC glad_glGetClipPlane; #define glGetClipPlane glad_glGetClipPlane GLAD_API_CALL PFNGLGETCOLORTABLEPROC glad_glGetColorTable; #define glGetColorTable glad_glGetColorTable GLAD_API_CALL PFNGLGETCOLORTABLEPARAMETERFVPROC glad_glGetColorTableParameterfv; #define glGetColorTableParameterfv glad_glGetColorTableParameterfv GLAD_API_CALL PFNGLGETCOLORTABLEPARAMETERIVPROC glad_glGetColorTableParameteriv; #define glGetColorTableParameteriv glad_glGetColorTableParameteriv GLAD_API_CALL PFNGLGETCOMPRESSEDTEXIMAGEPROC glad_glGetCompressedTexImage; #define glGetCompressedTexImage glad_glGetCompressedTexImage GLAD_API_CALL PFNGLGETCOMPRESSEDTEXIMAGEARBPROC glad_glGetCompressedTexImageARB; #define glGetCompressedTexImageARB glad_glGetCompressedTexImageARB GLAD_API_CALL PFNGLGETCOMPRESSEDTEXTUREIMAGEPROC glad_glGetCompressedTextureImage; #define glGetCompressedTextureImage glad_glGetCompressedTextureImage GLAD_API_CALL PFNGLGETCOMPRESSEDTEXTURESUBIMAGEPROC glad_glGetCompressedTextureSubImage; #define glGetCompressedTextureSubImage glad_glGetCompressedTextureSubImage GLAD_API_CALL PFNGLGETCONVOLUTIONFILTERPROC glad_glGetConvolutionFilter; #define glGetConvolutionFilter glad_glGetConvolutionFilter GLAD_API_CALL PFNGLGETCONVOLUTIONPARAMETERFVPROC glad_glGetConvolutionParameterfv; #define glGetConvolutionParameterfv glad_glGetConvolutionParameterfv GLAD_API_CALL PFNGLGETCONVOLUTIONPARAMETERIVPROC glad_glGetConvolutionParameteriv; #define glGetConvolutionParameteriv glad_glGetConvolutionParameteriv GLAD_API_CALL PFNGLGETDEBUGMESSAGELOGPROC glad_glGetDebugMessageLog; #define glGetDebugMessageLog glad_glGetDebugMessageLog GLAD_API_CALL PFNGLGETDEBUGMESSAGELOGARBPROC glad_glGetDebugMessageLogARB; #define glGetDebugMessageLogARB glad_glGetDebugMessageLogARB GLAD_API_CALL PFNGLGETDOUBLEI_VPROC glad_glGetDoublei_v; #define glGetDoublei_v glad_glGetDoublei_v GLAD_API_CALL PFNGLGETDOUBLEVPROC glad_glGetDoublev; #define glGetDoublev glad_glGetDoublev GLAD_API_CALL PFNGLGETERRORPROC glad_glGetError; #define glGetError glad_glGetError GLAD_API_CALL PFNGLGETFLOATI_VPROC glad_glGetFloati_v; #define glGetFloati_v glad_glGetFloati_v GLAD_API_CALL PFNGLGETFLOATVPROC glad_glGetFloatv; #define glGetFloatv glad_glGetFloatv GLAD_API_CALL PFNGLGETFRAGDATAINDEXPROC glad_glGetFragDataIndex; #define glGetFragDataIndex glad_glGetFragDataIndex GLAD_API_CALL PFNGLGETFRAGDATALOCATIONPROC glad_glGetFragDataLocation; #define glGetFragDataLocation glad_glGetFragDataLocation GLAD_API_CALL PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVPROC glad_glGetFramebufferAttachmentParameteriv; #define glGetFramebufferAttachmentParameteriv glad_glGetFramebufferAttachmentParameteriv GLAD_API_CALL PFNGLGETFRAMEBUFFERPARAMETERIVPROC glad_glGetFramebufferParameteriv; #define glGetFramebufferParameteriv glad_glGetFramebufferParameteriv GLAD_API_CALL PFNGLGETGRAPHICSRESETSTATUSPROC glad_glGetGraphicsResetStatus; #define glGetGraphicsResetStatus glad_glGetGraphicsResetStatus GLAD_API_CALL PFNGLGETGRAPHICSRESETSTATUSARBPROC glad_glGetGraphicsResetStatusARB; #define glGetGraphicsResetStatusARB glad_glGetGraphicsResetStatusARB GLAD_API_CALL PFNGLGETHANDLEARBPROC glad_glGetHandleARB; #define glGetHandleARB glad_glGetHandleARB GLAD_API_CALL PFNGLGETHISTOGRAMPROC glad_glGetHistogram; #define glGetHistogram glad_glGetHistogram GLAD_API_CALL PFNGLGETHISTOGRAMPARAMETERFVPROC glad_glGetHistogramParameterfv; #define glGetHistogramParameterfv glad_glGetHistogramParameterfv GLAD_API_CALL PFNGLGETHISTOGRAMPARAMETERIVPROC glad_glGetHistogramParameteriv; #define glGetHistogramParameteriv glad_glGetHistogramParameteriv GLAD_API_CALL PFNGLGETIMAGEHANDLEARBPROC glad_glGetImageHandleARB; #define glGetImageHandleARB glad_glGetImageHandleARB GLAD_API_CALL PFNGLGETINFOLOGARBPROC glad_glGetInfoLogARB; #define glGetInfoLogARB glad_glGetInfoLogARB GLAD_API_CALL PFNGLGETINTEGER64I_VPROC glad_glGetInteger64i_v; #define glGetInteger64i_v glad_glGetInteger64i_v GLAD_API_CALL PFNGLGETINTEGER64VPROC glad_glGetInteger64v; #define glGetInteger64v glad_glGetInteger64v GLAD_API_CALL PFNGLGETINTEGERI_VPROC glad_glGetIntegeri_v; #define glGetIntegeri_v glad_glGetIntegeri_v GLAD_API_CALL PFNGLGETINTEGERVPROC glad_glGetIntegerv; #define glGetIntegerv glad_glGetIntegerv GLAD_API_CALL PFNGLGETINTERNALFORMATI64VPROC glad_glGetInternalformati64v; #define glGetInternalformati64v glad_glGetInternalformati64v GLAD_API_CALL PFNGLGETINTERNALFORMATIVPROC glad_glGetInternalformativ; #define glGetInternalformativ glad_glGetInternalformativ GLAD_API_CALL PFNGLGETLIGHTFVPROC glad_glGetLightfv; #define glGetLightfv glad_glGetLightfv GLAD_API_CALL PFNGLGETLIGHTIVPROC glad_glGetLightiv; #define glGetLightiv glad_glGetLightiv GLAD_API_CALL PFNGLGETMAPDVPROC glad_glGetMapdv; #define glGetMapdv glad_glGetMapdv GLAD_API_CALL PFNGLGETMAPFVPROC glad_glGetMapfv; #define glGetMapfv glad_glGetMapfv GLAD_API_CALL PFNGLGETMAPIVPROC glad_glGetMapiv; #define glGetMapiv glad_glGetMapiv GLAD_API_CALL PFNGLGETMATERIALFVPROC glad_glGetMaterialfv; #define glGetMaterialfv glad_glGetMaterialfv GLAD_API_CALL PFNGLGETMATERIALIVPROC glad_glGetMaterialiv; #define glGetMaterialiv glad_glGetMaterialiv GLAD_API_CALL PFNGLGETMINMAXPROC glad_glGetMinmax; #define glGetMinmax glad_glGetMinmax GLAD_API_CALL PFNGLGETMINMAXPARAMETERFVPROC glad_glGetMinmaxParameterfv; #define glGetMinmaxParameterfv glad_glGetMinmaxParameterfv GLAD_API_CALL PFNGLGETMINMAXPARAMETERIVPROC glad_glGetMinmaxParameteriv; #define glGetMinmaxParameteriv glad_glGetMinmaxParameteriv GLAD_API_CALL PFNGLGETMULTISAMPLEFVPROC glad_glGetMultisamplefv; #define glGetMultisamplefv glad_glGetMultisamplefv GLAD_API_CALL PFNGLGETNAMEDBUFFERPARAMETERI64VPROC glad_glGetNamedBufferParameteri64v; #define glGetNamedBufferParameteri64v glad_glGetNamedBufferParameteri64v GLAD_API_CALL PFNGLGETNAMEDBUFFERPARAMETERIVPROC glad_glGetNamedBufferParameteriv; #define glGetNamedBufferParameteriv glad_glGetNamedBufferParameteriv GLAD_API_CALL PFNGLGETNAMEDBUFFERPOINTERVPROC glad_glGetNamedBufferPointerv; #define glGetNamedBufferPointerv glad_glGetNamedBufferPointerv GLAD_API_CALL PFNGLGETNAMEDBUFFERSUBDATAPROC glad_glGetNamedBufferSubData; #define glGetNamedBufferSubData glad_glGetNamedBufferSubData GLAD_API_CALL PFNGLGETNAMEDFRAMEBUFFERATTACHMENTPARAMETERIVPROC glad_glGetNamedFramebufferAttachmentParameteriv; #define glGetNamedFramebufferAttachmentParameteriv glad_glGetNamedFramebufferAttachmentParameteriv GLAD_API_CALL PFNGLGETNAMEDFRAMEBUFFERPARAMETERIVPROC glad_glGetNamedFramebufferParameteriv; #define glGetNamedFramebufferParameteriv glad_glGetNamedFramebufferParameteriv GLAD_API_CALL PFNGLGETNAMEDRENDERBUFFERPARAMETERIVPROC glad_glGetNamedRenderbufferParameteriv; #define glGetNamedRenderbufferParameteriv glad_glGetNamedRenderbufferParameteriv GLAD_API_CALL PFNGLGETNAMEDSTRINGARBPROC glad_glGetNamedStringARB; #define glGetNamedStringARB glad_glGetNamedStringARB GLAD_API_CALL PFNGLGETNAMEDSTRINGIVARBPROC glad_glGetNamedStringivARB; #define glGetNamedStringivARB glad_glGetNamedStringivARB GLAD_API_CALL PFNGLGETOBJECTLABELPROC glad_glGetObjectLabel; #define glGetObjectLabel glad_glGetObjectLabel GLAD_API_CALL PFNGLGETOBJECTPARAMETERFVARBPROC glad_glGetObjectParameterfvARB; #define glGetObjectParameterfvARB glad_glGetObjectParameterfvARB GLAD_API_CALL PFNGLGETOBJECTPARAMETERIVARBPROC glad_glGetObjectParameterivARB; #define glGetObjectParameterivARB glad_glGetObjectParameterivARB GLAD_API_CALL PFNGLGETOBJECTPTRLABELPROC glad_glGetObjectPtrLabel; #define glGetObjectPtrLabel glad_glGetObjectPtrLabel GLAD_API_CALL PFNGLGETPIXELMAPFVPROC glad_glGetPixelMapfv; #define glGetPixelMapfv glad_glGetPixelMapfv GLAD_API_CALL PFNGLGETPIXELMAPUIVPROC glad_glGetPixelMapuiv; #define glGetPixelMapuiv glad_glGetPixelMapuiv GLAD_API_CALL PFNGLGETPIXELMAPUSVPROC glad_glGetPixelMapusv; #define glGetPixelMapusv glad_glGetPixelMapusv GLAD_API_CALL PFNGLGETPOINTERVPROC glad_glGetPointerv; #define glGetPointerv glad_glGetPointerv GLAD_API_CALL PFNGLGETPOLYGONSTIPPLEPROC glad_glGetPolygonStipple; #define glGetPolygonStipple glad_glGetPolygonStipple GLAD_API_CALL PFNGLGETPROGRAMBINARYPROC glad_glGetProgramBinary; #define glGetProgramBinary glad_glGetProgramBinary GLAD_API_CALL PFNGLGETPROGRAMENVPARAMETERDVARBPROC glad_glGetProgramEnvParameterdvARB; #define glGetProgramEnvParameterdvARB glad_glGetProgramEnvParameterdvARB GLAD_API_CALL PFNGLGETPROGRAMENVPARAMETERFVARBPROC glad_glGetProgramEnvParameterfvARB; #define glGetProgramEnvParameterfvARB glad_glGetProgramEnvParameterfvARB GLAD_API_CALL PFNGLGETPROGRAMINFOLOGPROC glad_glGetProgramInfoLog; #define glGetProgramInfoLog glad_glGetProgramInfoLog GLAD_API_CALL PFNGLGETPROGRAMINTERFACEIVPROC glad_glGetProgramInterfaceiv; #define glGetProgramInterfaceiv glad_glGetProgramInterfaceiv GLAD_API_CALL PFNGLGETPROGRAMLOCALPARAMETERDVARBPROC glad_glGetProgramLocalParameterdvARB; #define glGetProgramLocalParameterdvARB glad_glGetProgramLocalParameterdvARB GLAD_API_CALL PFNGLGETPROGRAMLOCALPARAMETERFVARBPROC glad_glGetProgramLocalParameterfvARB; #define glGetProgramLocalParameterfvARB glad_glGetProgramLocalParameterfvARB GLAD_API_CALL PFNGLGETPROGRAMPIPELINEINFOLOGPROC glad_glGetProgramPipelineInfoLog; #define glGetProgramPipelineInfoLog glad_glGetProgramPipelineInfoLog GLAD_API_CALL PFNGLGETPROGRAMPIPELINEIVPROC glad_glGetProgramPipelineiv; #define glGetProgramPipelineiv glad_glGetProgramPipelineiv GLAD_API_CALL PFNGLGETPROGRAMRESOURCEINDEXPROC glad_glGetProgramResourceIndex; #define glGetProgramResourceIndex glad_glGetProgramResourceIndex GLAD_API_CALL PFNGLGETPROGRAMRESOURCELOCATIONPROC glad_glGetProgramResourceLocation; #define glGetProgramResourceLocation glad_glGetProgramResourceLocation GLAD_API_CALL PFNGLGETPROGRAMRESOURCELOCATIONINDEXPROC glad_glGetProgramResourceLocationIndex; #define glGetProgramResourceLocationIndex glad_glGetProgramResourceLocationIndex GLAD_API_CALL PFNGLGETPROGRAMRESOURCENAMEPROC glad_glGetProgramResourceName; #define glGetProgramResourceName glad_glGetProgramResourceName GLAD_API_CALL PFNGLGETPROGRAMRESOURCEIVPROC glad_glGetProgramResourceiv; #define glGetProgramResourceiv glad_glGetProgramResourceiv GLAD_API_CALL PFNGLGETPROGRAMSTAGEIVPROC glad_glGetProgramStageiv; #define glGetProgramStageiv glad_glGetProgramStageiv GLAD_API_CALL PFNGLGETPROGRAMSTRINGARBPROC glad_glGetProgramStringARB; #define glGetProgramStringARB glad_glGetProgramStringARB GLAD_API_CALL PFNGLGETPROGRAMIVPROC glad_glGetProgramiv; #define glGetProgramiv glad_glGetProgramiv GLAD_API_CALL PFNGLGETPROGRAMIVARBPROC glad_glGetProgramivARB; #define glGetProgramivARB glad_glGetProgramivARB GLAD_API_CALL PFNGLGETQUERYBUFFEROBJECTI64VPROC glad_glGetQueryBufferObjecti64v; #define glGetQueryBufferObjecti64v glad_glGetQueryBufferObjecti64v GLAD_API_CALL PFNGLGETQUERYBUFFEROBJECTIVPROC glad_glGetQueryBufferObjectiv; #define glGetQueryBufferObjectiv glad_glGetQueryBufferObjectiv GLAD_API_CALL PFNGLGETQUERYBUFFEROBJECTUI64VPROC glad_glGetQueryBufferObjectui64v; #define glGetQueryBufferObjectui64v glad_glGetQueryBufferObjectui64v GLAD_API_CALL PFNGLGETQUERYBUFFEROBJECTUIVPROC glad_glGetQueryBufferObjectuiv; #define glGetQueryBufferObjectuiv glad_glGetQueryBufferObjectuiv GLAD_API_CALL PFNGLGETQUERYINDEXEDIVPROC glad_glGetQueryIndexediv; #define glGetQueryIndexediv glad_glGetQueryIndexediv GLAD_API_CALL PFNGLGETQUERYOBJECTI64VPROC glad_glGetQueryObjecti64v; #define glGetQueryObjecti64v glad_glGetQueryObjecti64v GLAD_API_CALL PFNGLGETQUERYOBJECTIVPROC glad_glGetQueryObjectiv; #define glGetQueryObjectiv glad_glGetQueryObjectiv GLAD_API_CALL PFNGLGETQUERYOBJECTIVARBPROC glad_glGetQueryObjectivARB; #define glGetQueryObjectivARB glad_glGetQueryObjectivARB GLAD_API_CALL PFNGLGETQUERYOBJECTUI64VPROC glad_glGetQueryObjectui64v; #define glGetQueryObjectui64v glad_glGetQueryObjectui64v GLAD_API_CALL PFNGLGETQUERYOBJECTUIVPROC glad_glGetQueryObjectuiv; #define glGetQueryObjectuiv glad_glGetQueryObjectuiv GLAD_API_CALL PFNGLGETQUERYOBJECTUIVARBPROC glad_glGetQueryObjectuivARB; #define glGetQueryObjectuivARB glad_glGetQueryObjectuivARB GLAD_API_CALL PFNGLGETQUERYIVPROC glad_glGetQueryiv; #define glGetQueryiv glad_glGetQueryiv GLAD_API_CALL PFNGLGETQUERYIVARBPROC glad_glGetQueryivARB; #define glGetQueryivARB glad_glGetQueryivARB GLAD_API_CALL PFNGLGETRENDERBUFFERPARAMETERIVPROC glad_glGetRenderbufferParameteriv; #define glGetRenderbufferParameteriv glad_glGetRenderbufferParameteriv GLAD_API_CALL PFNGLGETSAMPLERPARAMETERIIVPROC glad_glGetSamplerParameterIiv; #define glGetSamplerParameterIiv glad_glGetSamplerParameterIiv GLAD_API_CALL PFNGLGETSAMPLERPARAMETERIUIVPROC glad_glGetSamplerParameterIuiv; #define glGetSamplerParameterIuiv glad_glGetSamplerParameterIuiv GLAD_API_CALL PFNGLGETSAMPLERPARAMETERFVPROC glad_glGetSamplerParameterfv; #define glGetSamplerParameterfv glad_glGetSamplerParameterfv GLAD_API_CALL PFNGLGETSAMPLERPARAMETERIVPROC glad_glGetSamplerParameteriv; #define glGetSamplerParameteriv glad_glGetSamplerParameteriv GLAD_API_CALL PFNGLGETSEPARABLEFILTERPROC glad_glGetSeparableFilter; #define glGetSeparableFilter glad_glGetSeparableFilter GLAD_API_CALL PFNGLGETSHADERINFOLOGPROC glad_glGetShaderInfoLog; #define glGetShaderInfoLog glad_glGetShaderInfoLog GLAD_API_CALL PFNGLGETSHADERPRECISIONFORMATPROC glad_glGetShaderPrecisionFormat; #define glGetShaderPrecisionFormat glad_glGetShaderPrecisionFormat GLAD_API_CALL PFNGLGETSHADERSOURCEPROC glad_glGetShaderSource; #define glGetShaderSource glad_glGetShaderSource GLAD_API_CALL PFNGLGETSHADERSOURCEARBPROC glad_glGetShaderSourceARB; #define glGetShaderSourceARB glad_glGetShaderSourceARB GLAD_API_CALL PFNGLGETSHADERIVPROC glad_glGetShaderiv; #define glGetShaderiv glad_glGetShaderiv GLAD_API_CALL PFNGLGETSTRINGPROC glad_glGetString; #define glGetString glad_glGetString GLAD_API_CALL PFNGLGETSTRINGIPROC glad_glGetStringi; #define glGetStringi glad_glGetStringi GLAD_API_CALL PFNGLGETSUBROUTINEINDEXPROC glad_glGetSubroutineIndex; #define glGetSubroutineIndex glad_glGetSubroutineIndex GLAD_API_CALL PFNGLGETSUBROUTINEUNIFORMLOCATIONPROC glad_glGetSubroutineUniformLocation; #define glGetSubroutineUniformLocation glad_glGetSubroutineUniformLocation GLAD_API_CALL PFNGLGETSYNCIVPROC glad_glGetSynciv; #define glGetSynciv glad_glGetSynciv GLAD_API_CALL PFNGLGETTEXENVFVPROC glad_glGetTexEnvfv; #define glGetTexEnvfv glad_glGetTexEnvfv GLAD_API_CALL PFNGLGETTEXENVIVPROC glad_glGetTexEnviv; #define glGetTexEnviv glad_glGetTexEnviv GLAD_API_CALL PFNGLGETTEXGENDVPROC glad_glGetTexGendv; #define glGetTexGendv glad_glGetTexGendv GLAD_API_CALL PFNGLGETTEXGENFVPROC glad_glGetTexGenfv; #define glGetTexGenfv glad_glGetTexGenfv GLAD_API_CALL PFNGLGETTEXGENIVPROC glad_glGetTexGeniv; #define glGetTexGeniv glad_glGetTexGeniv GLAD_API_CALL PFNGLGETTEXIMAGEPROC glad_glGetTexImage; #define glGetTexImage glad_glGetTexImage GLAD_API_CALL PFNGLGETTEXLEVELPARAMETERFVPROC glad_glGetTexLevelParameterfv; #define glGetTexLevelParameterfv glad_glGetTexLevelParameterfv GLAD_API_CALL PFNGLGETTEXLEVELPARAMETERIVPROC glad_glGetTexLevelParameteriv; #define glGetTexLevelParameteriv glad_glGetTexLevelParameteriv GLAD_API_CALL PFNGLGETTEXPARAMETERIIVPROC glad_glGetTexParameterIiv; #define glGetTexParameterIiv glad_glGetTexParameterIiv GLAD_API_CALL PFNGLGETTEXPARAMETERIUIVPROC glad_glGetTexParameterIuiv; #define glGetTexParameterIuiv glad_glGetTexParameterIuiv GLAD_API_CALL PFNGLGETTEXPARAMETERFVPROC glad_glGetTexParameterfv; #define glGetTexParameterfv glad_glGetTexParameterfv GLAD_API_CALL PFNGLGETTEXPARAMETERIVPROC glad_glGetTexParameteriv; #define glGetTexParameteriv glad_glGetTexParameteriv GLAD_API_CALL PFNGLGETTEXTUREHANDLEARBPROC glad_glGetTextureHandleARB; #define glGetTextureHandleARB glad_glGetTextureHandleARB GLAD_API_CALL PFNGLGETTEXTUREIMAGEPROC glad_glGetTextureImage; #define glGetTextureImage glad_glGetTextureImage GLAD_API_CALL PFNGLGETTEXTURELEVELPARAMETERFVPROC glad_glGetTextureLevelParameterfv; #define glGetTextureLevelParameterfv glad_glGetTextureLevelParameterfv GLAD_API_CALL PFNGLGETTEXTURELEVELPARAMETERIVPROC glad_glGetTextureLevelParameteriv; #define glGetTextureLevelParameteriv glad_glGetTextureLevelParameteriv GLAD_API_CALL PFNGLGETTEXTUREPARAMETERIIVPROC glad_glGetTextureParameterIiv; #define glGetTextureParameterIiv glad_glGetTextureParameterIiv GLAD_API_CALL PFNGLGETTEXTUREPARAMETERIUIVPROC glad_glGetTextureParameterIuiv; #define glGetTextureParameterIuiv glad_glGetTextureParameterIuiv GLAD_API_CALL PFNGLGETTEXTUREPARAMETERFVPROC glad_glGetTextureParameterfv; #define glGetTextureParameterfv glad_glGetTextureParameterfv GLAD_API_CALL PFNGLGETTEXTUREPARAMETERIVPROC glad_glGetTextureParameteriv; #define glGetTextureParameteriv glad_glGetTextureParameteriv GLAD_API_CALL PFNGLGETTEXTURESAMPLERHANDLEARBPROC glad_glGetTextureSamplerHandleARB; #define glGetTextureSamplerHandleARB glad_glGetTextureSamplerHandleARB GLAD_API_CALL PFNGLGETTEXTURESUBIMAGEPROC glad_glGetTextureSubImage; #define glGetTextureSubImage glad_glGetTextureSubImage GLAD_API_CALL PFNGLGETTRANSFORMFEEDBACKVARYINGPROC glad_glGetTransformFeedbackVarying; #define glGetTransformFeedbackVarying glad_glGetTransformFeedbackVarying GLAD_API_CALL PFNGLGETTRANSFORMFEEDBACKI64_VPROC glad_glGetTransformFeedbacki64_v; #define glGetTransformFeedbacki64_v glad_glGetTransformFeedbacki64_v GLAD_API_CALL PFNGLGETTRANSFORMFEEDBACKI_VPROC glad_glGetTransformFeedbacki_v; #define glGetTransformFeedbacki_v glad_glGetTransformFeedbacki_v GLAD_API_CALL PFNGLGETTRANSFORMFEEDBACKIVPROC glad_glGetTransformFeedbackiv; #define glGetTransformFeedbackiv glad_glGetTransformFeedbackiv GLAD_API_CALL PFNGLGETUNIFORMBLOCKINDEXPROC glad_glGetUniformBlockIndex; #define glGetUniformBlockIndex glad_glGetUniformBlockIndex GLAD_API_CALL PFNGLGETUNIFORMINDICESPROC glad_glGetUniformIndices; #define glGetUniformIndices glad_glGetUniformIndices GLAD_API_CALL PFNGLGETUNIFORMLOCATIONPROC glad_glGetUniformLocation; #define glGetUniformLocation glad_glGetUniformLocation GLAD_API_CALL PFNGLGETUNIFORMLOCATIONARBPROC glad_glGetUniformLocationARB; #define glGetUniformLocationARB glad_glGetUniformLocationARB GLAD_API_CALL PFNGLGETUNIFORMSUBROUTINEUIVPROC glad_glGetUniformSubroutineuiv; #define glGetUniformSubroutineuiv glad_glGetUniformSubroutineuiv GLAD_API_CALL PFNGLGETUNIFORMDVPROC glad_glGetUniformdv; #define glGetUniformdv glad_glGetUniformdv GLAD_API_CALL PFNGLGETUNIFORMFVPROC glad_glGetUniformfv; #define glGetUniformfv glad_glGetUniformfv GLAD_API_CALL PFNGLGETUNIFORMFVARBPROC glad_glGetUniformfvARB; #define glGetUniformfvARB glad_glGetUniformfvARB GLAD_API_CALL PFNGLGETUNIFORMI64VARBPROC glad_glGetUniformi64vARB; #define glGetUniformi64vARB glad_glGetUniformi64vARB GLAD_API_CALL PFNGLGETUNIFORMIVPROC glad_glGetUniformiv; #define glGetUniformiv glad_glGetUniformiv GLAD_API_CALL PFNGLGETUNIFORMIVARBPROC glad_glGetUniformivARB; #define glGetUniformivARB glad_glGetUniformivARB GLAD_API_CALL PFNGLGETUNIFORMUI64VARBPROC glad_glGetUniformui64vARB; #define glGetUniformui64vARB glad_glGetUniformui64vARB GLAD_API_CALL PFNGLGETUNIFORMUIVPROC glad_glGetUniformuiv; #define glGetUniformuiv glad_glGetUniformuiv GLAD_API_CALL PFNGLGETVERTEXARRAYINDEXED64IVPROC glad_glGetVertexArrayIndexed64iv; #define glGetVertexArrayIndexed64iv glad_glGetVertexArrayIndexed64iv GLAD_API_CALL PFNGLGETVERTEXARRAYINDEXEDIVPROC glad_glGetVertexArrayIndexediv; #define glGetVertexArrayIndexediv glad_glGetVertexArrayIndexediv GLAD_API_CALL PFNGLGETVERTEXARRAYIVPROC glad_glGetVertexArrayiv; #define glGetVertexArrayiv glad_glGetVertexArrayiv GLAD_API_CALL PFNGLGETVERTEXATTRIBIIVPROC glad_glGetVertexAttribIiv; #define glGetVertexAttribIiv glad_glGetVertexAttribIiv GLAD_API_CALL PFNGLGETVERTEXATTRIBIUIVPROC glad_glGetVertexAttribIuiv; #define glGetVertexAttribIuiv glad_glGetVertexAttribIuiv GLAD_API_CALL PFNGLGETVERTEXATTRIBLDVPROC glad_glGetVertexAttribLdv; #define glGetVertexAttribLdv glad_glGetVertexAttribLdv GLAD_API_CALL PFNGLGETVERTEXATTRIBLUI64VARBPROC glad_glGetVertexAttribLui64vARB; #define glGetVertexAttribLui64vARB glad_glGetVertexAttribLui64vARB GLAD_API_CALL PFNGLGETVERTEXATTRIBPOINTERVPROC glad_glGetVertexAttribPointerv; #define glGetVertexAttribPointerv glad_glGetVertexAttribPointerv GLAD_API_CALL PFNGLGETVERTEXATTRIBPOINTERVARBPROC glad_glGetVertexAttribPointervARB; #define glGetVertexAttribPointervARB glad_glGetVertexAttribPointervARB GLAD_API_CALL PFNGLGETVERTEXATTRIBDVPROC glad_glGetVertexAttribdv; #define glGetVertexAttribdv glad_glGetVertexAttribdv GLAD_API_CALL PFNGLGETVERTEXATTRIBDVARBPROC glad_glGetVertexAttribdvARB; #define glGetVertexAttribdvARB glad_glGetVertexAttribdvARB GLAD_API_CALL PFNGLGETVERTEXATTRIBFVPROC glad_glGetVertexAttribfv; #define glGetVertexAttribfv glad_glGetVertexAttribfv GLAD_API_CALL PFNGLGETVERTEXATTRIBFVARBPROC glad_glGetVertexAttribfvARB; #define glGetVertexAttribfvARB glad_glGetVertexAttribfvARB GLAD_API_CALL PFNGLGETVERTEXATTRIBIVPROC glad_glGetVertexAttribiv; #define glGetVertexAttribiv glad_glGetVertexAttribiv GLAD_API_CALL PFNGLGETVERTEXATTRIBIVARBPROC glad_glGetVertexAttribivARB; #define glGetVertexAttribivARB glad_glGetVertexAttribivARB GLAD_API_CALL PFNGLGETNCOLORTABLEARBPROC glad_glGetnColorTableARB; #define glGetnColorTableARB glad_glGetnColorTableARB GLAD_API_CALL PFNGLGETNCOMPRESSEDTEXIMAGEARBPROC glad_glGetnCompressedTexImageARB; #define glGetnCompressedTexImageARB glad_glGetnCompressedTexImageARB GLAD_API_CALL PFNGLGETNCONVOLUTIONFILTERARBPROC glad_glGetnConvolutionFilterARB; #define glGetnConvolutionFilterARB glad_glGetnConvolutionFilterARB GLAD_API_CALL PFNGLGETNHISTOGRAMARBPROC glad_glGetnHistogramARB; #define glGetnHistogramARB glad_glGetnHistogramARB GLAD_API_CALL PFNGLGETNMAPDVARBPROC glad_glGetnMapdvARB; #define glGetnMapdvARB glad_glGetnMapdvARB GLAD_API_CALL PFNGLGETNMAPFVARBPROC glad_glGetnMapfvARB; #define glGetnMapfvARB glad_glGetnMapfvARB GLAD_API_CALL PFNGLGETNMAPIVARBPROC glad_glGetnMapivARB; #define glGetnMapivARB glad_glGetnMapivARB GLAD_API_CALL PFNGLGETNMINMAXARBPROC glad_glGetnMinmaxARB; #define glGetnMinmaxARB glad_glGetnMinmaxARB GLAD_API_CALL PFNGLGETNPIXELMAPFVARBPROC glad_glGetnPixelMapfvARB; #define glGetnPixelMapfvARB glad_glGetnPixelMapfvARB GLAD_API_CALL PFNGLGETNPIXELMAPUIVARBPROC glad_glGetnPixelMapuivARB; #define glGetnPixelMapuivARB glad_glGetnPixelMapuivARB GLAD_API_CALL PFNGLGETNPIXELMAPUSVARBPROC glad_glGetnPixelMapusvARB; #define glGetnPixelMapusvARB glad_glGetnPixelMapusvARB GLAD_API_CALL PFNGLGETNPOLYGONSTIPPLEARBPROC glad_glGetnPolygonStippleARB; #define glGetnPolygonStippleARB glad_glGetnPolygonStippleARB GLAD_API_CALL PFNGLGETNSEPARABLEFILTERARBPROC glad_glGetnSeparableFilterARB; #define glGetnSeparableFilterARB glad_glGetnSeparableFilterARB GLAD_API_CALL PFNGLGETNTEXIMAGEARBPROC glad_glGetnTexImageARB; #define glGetnTexImageARB glad_glGetnTexImageARB GLAD_API_CALL PFNGLGETNUNIFORMDVARBPROC glad_glGetnUniformdvARB; #define glGetnUniformdvARB glad_glGetnUniformdvARB GLAD_API_CALL PFNGLGETNUNIFORMFVPROC glad_glGetnUniformfv; #define glGetnUniformfv glad_glGetnUniformfv GLAD_API_CALL PFNGLGETNUNIFORMFVARBPROC glad_glGetnUniformfvARB; #define glGetnUniformfvARB glad_glGetnUniformfvARB GLAD_API_CALL PFNGLGETNUNIFORMI64VARBPROC glad_glGetnUniformi64vARB; #define glGetnUniformi64vARB glad_glGetnUniformi64vARB GLAD_API_CALL PFNGLGETNUNIFORMIVPROC glad_glGetnUniformiv; #define glGetnUniformiv glad_glGetnUniformiv GLAD_API_CALL PFNGLGETNUNIFORMIVARBPROC glad_glGetnUniformivARB; #define glGetnUniformivARB glad_glGetnUniformivARB GLAD_API_CALL PFNGLGETNUNIFORMUI64VARBPROC glad_glGetnUniformui64vARB; #define glGetnUniformui64vARB glad_glGetnUniformui64vARB GLAD_API_CALL PFNGLGETNUNIFORMUIVPROC glad_glGetnUniformuiv; #define glGetnUniformuiv glad_glGetnUniformuiv GLAD_API_CALL PFNGLGETNUNIFORMUIVARBPROC glad_glGetnUniformuivARB; #define glGetnUniformuivARB glad_glGetnUniformuivARB GLAD_API_CALL PFNGLHINTPROC glad_glHint; #define glHint glad_glHint GLAD_API_CALL PFNGLHISTOGRAMPROC glad_glHistogram; #define glHistogram glad_glHistogram GLAD_API_CALL PFNGLINDEXMASKPROC glad_glIndexMask; #define glIndexMask glad_glIndexMask GLAD_API_CALL PFNGLINDEXPOINTERPROC glad_glIndexPointer; #define glIndexPointer glad_glIndexPointer GLAD_API_CALL PFNGLINDEXDPROC glad_glIndexd; #define glIndexd glad_glIndexd GLAD_API_CALL PFNGLINDEXDVPROC glad_glIndexdv; #define glIndexdv glad_glIndexdv GLAD_API_CALL PFNGLINDEXFPROC glad_glIndexf; #define glIndexf glad_glIndexf GLAD_API_CALL PFNGLINDEXFVPROC glad_glIndexfv; #define glIndexfv glad_glIndexfv GLAD_API_CALL PFNGLINDEXIPROC glad_glIndexi; #define glIndexi glad_glIndexi GLAD_API_CALL PFNGLINDEXIVPROC glad_glIndexiv; #define glIndexiv glad_glIndexiv GLAD_API_CALL PFNGLINDEXSPROC glad_glIndexs; #define glIndexs glad_glIndexs GLAD_API_CALL PFNGLINDEXSVPROC glad_glIndexsv; #define glIndexsv glad_glIndexsv GLAD_API_CALL PFNGLINDEXUBPROC glad_glIndexub; #define glIndexub glad_glIndexub GLAD_API_CALL PFNGLINDEXUBVPROC glad_glIndexubv; #define glIndexubv glad_glIndexubv GLAD_API_CALL PFNGLINITNAMESPROC glad_glInitNames; #define glInitNames glad_glInitNames GLAD_API_CALL PFNGLINTERLEAVEDARRAYSPROC glad_glInterleavedArrays; #define glInterleavedArrays glad_glInterleavedArrays GLAD_API_CALL PFNGLINVALIDATEBUFFERDATAPROC glad_glInvalidateBufferData; #define glInvalidateBufferData glad_glInvalidateBufferData GLAD_API_CALL PFNGLINVALIDATEBUFFERSUBDATAPROC glad_glInvalidateBufferSubData; #define glInvalidateBufferSubData glad_glInvalidateBufferSubData GLAD_API_CALL PFNGLINVALIDATEFRAMEBUFFERPROC glad_glInvalidateFramebuffer; #define glInvalidateFramebuffer glad_glInvalidateFramebuffer GLAD_API_CALL PFNGLINVALIDATENAMEDFRAMEBUFFERDATAPROC glad_glInvalidateNamedFramebufferData; #define glInvalidateNamedFramebufferData glad_glInvalidateNamedFramebufferData GLAD_API_CALL PFNGLINVALIDATENAMEDFRAMEBUFFERSUBDATAPROC glad_glInvalidateNamedFramebufferSubData; #define glInvalidateNamedFramebufferSubData glad_glInvalidateNamedFramebufferSubData GLAD_API_CALL PFNGLINVALIDATESUBFRAMEBUFFERPROC glad_glInvalidateSubFramebuffer; #define glInvalidateSubFramebuffer glad_glInvalidateSubFramebuffer GLAD_API_CALL PFNGLINVALIDATETEXIMAGEPROC glad_glInvalidateTexImage; #define glInvalidateTexImage glad_glInvalidateTexImage GLAD_API_CALL PFNGLINVALIDATETEXSUBIMAGEPROC glad_glInvalidateTexSubImage; #define glInvalidateTexSubImage glad_glInvalidateTexSubImage GLAD_API_CALL PFNGLISBUFFERPROC glad_glIsBuffer; #define glIsBuffer glad_glIsBuffer GLAD_API_CALL PFNGLISBUFFERARBPROC glad_glIsBufferARB; #define glIsBufferARB glad_glIsBufferARB GLAD_API_CALL PFNGLISENABLEDPROC glad_glIsEnabled; #define glIsEnabled glad_glIsEnabled GLAD_API_CALL PFNGLISENABLEDIPROC glad_glIsEnabledi; #define glIsEnabledi glad_glIsEnabledi GLAD_API_CALL PFNGLISFRAMEBUFFERPROC glad_glIsFramebuffer; #define glIsFramebuffer glad_glIsFramebuffer GLAD_API_CALL PFNGLISIMAGEHANDLERESIDENTARBPROC glad_glIsImageHandleResidentARB; #define glIsImageHandleResidentARB glad_glIsImageHandleResidentARB GLAD_API_CALL PFNGLISLISTPROC glad_glIsList; #define glIsList glad_glIsList GLAD_API_CALL PFNGLISNAMEDSTRINGARBPROC glad_glIsNamedStringARB; #define glIsNamedStringARB glad_glIsNamedStringARB GLAD_API_CALL PFNGLISPROGRAMPROC glad_glIsProgram; #define glIsProgram glad_glIsProgram GLAD_API_CALL PFNGLISPROGRAMARBPROC glad_glIsProgramARB; #define glIsProgramARB glad_glIsProgramARB GLAD_API_CALL PFNGLISPROGRAMPIPELINEPROC glad_glIsProgramPipeline; #define glIsProgramPipeline glad_glIsProgramPipeline GLAD_API_CALL PFNGLISQUERYPROC glad_glIsQuery; #define glIsQuery glad_glIsQuery GLAD_API_CALL PFNGLISQUERYARBPROC glad_glIsQueryARB; #define glIsQueryARB glad_glIsQueryARB GLAD_API_CALL PFNGLISRENDERBUFFERPROC glad_glIsRenderbuffer; #define glIsRenderbuffer glad_glIsRenderbuffer GLAD_API_CALL PFNGLISSAMPLERPROC glad_glIsSampler; #define glIsSampler glad_glIsSampler GLAD_API_CALL PFNGLISSHADERPROC glad_glIsShader; #define glIsShader glad_glIsShader GLAD_API_CALL PFNGLISSYNCPROC glad_glIsSync; #define glIsSync glad_glIsSync GLAD_API_CALL PFNGLISTEXTUREPROC glad_glIsTexture; #define glIsTexture glad_glIsTexture GLAD_API_CALL PFNGLISTEXTUREHANDLERESIDENTARBPROC glad_glIsTextureHandleResidentARB; #define glIsTextureHandleResidentARB glad_glIsTextureHandleResidentARB GLAD_API_CALL PFNGLISTRANSFORMFEEDBACKPROC glad_glIsTransformFeedback; #define glIsTransformFeedback glad_glIsTransformFeedback GLAD_API_CALL PFNGLISVERTEXARRAYPROC glad_glIsVertexArray; #define glIsVertexArray glad_glIsVertexArray GLAD_API_CALL PFNGLLIGHTMODELFPROC glad_glLightModelf; #define glLightModelf glad_glLightModelf GLAD_API_CALL PFNGLLIGHTMODELFVPROC glad_glLightModelfv; #define glLightModelfv glad_glLightModelfv GLAD_API_CALL PFNGLLIGHTMODELIPROC glad_glLightModeli; #define glLightModeli glad_glLightModeli GLAD_API_CALL PFNGLLIGHTMODELIVPROC glad_glLightModeliv; #define glLightModeliv glad_glLightModeliv GLAD_API_CALL PFNGLLIGHTFPROC glad_glLightf; #define glLightf glad_glLightf GLAD_API_CALL PFNGLLIGHTFVPROC glad_glLightfv; #define glLightfv glad_glLightfv GLAD_API_CALL PFNGLLIGHTIPROC glad_glLighti; #define glLighti glad_glLighti GLAD_API_CALL PFNGLLIGHTIVPROC glad_glLightiv; #define glLightiv glad_glLightiv GLAD_API_CALL PFNGLLINESTIPPLEPROC glad_glLineStipple; #define glLineStipple glad_glLineStipple GLAD_API_CALL PFNGLLINEWIDTHPROC glad_glLineWidth; #define glLineWidth glad_glLineWidth GLAD_API_CALL PFNGLLINKPROGRAMPROC glad_glLinkProgram; #define glLinkProgram glad_glLinkProgram GLAD_API_CALL PFNGLLINKPROGRAMARBPROC glad_glLinkProgramARB; #define glLinkProgramARB glad_glLinkProgramARB GLAD_API_CALL PFNGLLISTBASEPROC glad_glListBase; #define glListBase glad_glListBase GLAD_API_CALL PFNGLLOADIDENTITYPROC glad_glLoadIdentity; #define glLoadIdentity glad_glLoadIdentity GLAD_API_CALL PFNGLLOADMATRIXDPROC glad_glLoadMatrixd; #define glLoadMatrixd glad_glLoadMatrixd GLAD_API_CALL PFNGLLOADMATRIXFPROC glad_glLoadMatrixf; #define glLoadMatrixf glad_glLoadMatrixf GLAD_API_CALL PFNGLLOADNAMEPROC glad_glLoadName; #define glLoadName glad_glLoadName GLAD_API_CALL PFNGLLOADTRANSPOSEMATRIXDPROC glad_glLoadTransposeMatrixd; #define glLoadTransposeMatrixd glad_glLoadTransposeMatrixd GLAD_API_CALL PFNGLLOADTRANSPOSEMATRIXDARBPROC glad_glLoadTransposeMatrixdARB; #define glLoadTransposeMatrixdARB glad_glLoadTransposeMatrixdARB GLAD_API_CALL PFNGLLOADTRANSPOSEMATRIXFPROC glad_glLoadTransposeMatrixf; #define glLoadTransposeMatrixf glad_glLoadTransposeMatrixf GLAD_API_CALL PFNGLLOADTRANSPOSEMATRIXFARBPROC glad_glLoadTransposeMatrixfARB; #define glLoadTransposeMatrixfARB glad_glLoadTransposeMatrixfARB GLAD_API_CALL PFNGLLOGICOPPROC glad_glLogicOp; #define glLogicOp glad_glLogicOp GLAD_API_CALL PFNGLMAKEIMAGEHANDLENONRESIDENTARBPROC glad_glMakeImageHandleNonResidentARB; #define glMakeImageHandleNonResidentARB glad_glMakeImageHandleNonResidentARB GLAD_API_CALL PFNGLMAKEIMAGEHANDLERESIDENTARBPROC glad_glMakeImageHandleResidentARB; #define glMakeImageHandleResidentARB glad_glMakeImageHandleResidentARB GLAD_API_CALL PFNGLMAKETEXTUREHANDLENONRESIDENTARBPROC glad_glMakeTextureHandleNonResidentARB; #define glMakeTextureHandleNonResidentARB glad_glMakeTextureHandleNonResidentARB GLAD_API_CALL PFNGLMAKETEXTUREHANDLERESIDENTARBPROC glad_glMakeTextureHandleResidentARB; #define glMakeTextureHandleResidentARB glad_glMakeTextureHandleResidentARB GLAD_API_CALL PFNGLMAP1DPROC glad_glMap1d; #define glMap1d glad_glMap1d GLAD_API_CALL PFNGLMAP1FPROC glad_glMap1f; #define glMap1f glad_glMap1f GLAD_API_CALL PFNGLMAP2DPROC glad_glMap2d; #define glMap2d glad_glMap2d GLAD_API_CALL PFNGLMAP2FPROC glad_glMap2f; #define glMap2f glad_glMap2f GLAD_API_CALL PFNGLMAPBUFFERPROC glad_glMapBuffer; #define glMapBuffer glad_glMapBuffer GLAD_API_CALL PFNGLMAPBUFFERARBPROC glad_glMapBufferARB; #define glMapBufferARB glad_glMapBufferARB GLAD_API_CALL PFNGLMAPBUFFERRANGEPROC glad_glMapBufferRange; #define glMapBufferRange glad_glMapBufferRange GLAD_API_CALL PFNGLMAPGRID1DPROC glad_glMapGrid1d; #define glMapGrid1d glad_glMapGrid1d GLAD_API_CALL PFNGLMAPGRID1FPROC glad_glMapGrid1f; #define glMapGrid1f glad_glMapGrid1f GLAD_API_CALL PFNGLMAPGRID2DPROC glad_glMapGrid2d; #define glMapGrid2d glad_glMapGrid2d GLAD_API_CALL PFNGLMAPGRID2FPROC glad_glMapGrid2f; #define glMapGrid2f glad_glMapGrid2f GLAD_API_CALL PFNGLMAPNAMEDBUFFERPROC glad_glMapNamedBuffer; #define glMapNamedBuffer glad_glMapNamedBuffer GLAD_API_CALL PFNGLMAPNAMEDBUFFERRANGEPROC glad_glMapNamedBufferRange; #define glMapNamedBufferRange glad_glMapNamedBufferRange GLAD_API_CALL PFNGLMATERIALFPROC glad_glMaterialf; #define glMaterialf glad_glMaterialf GLAD_API_CALL PFNGLMATERIALFVPROC glad_glMaterialfv; #define glMaterialfv glad_glMaterialfv GLAD_API_CALL PFNGLMATERIALIPROC glad_glMateriali; #define glMateriali glad_glMateriali GLAD_API_CALL PFNGLMATERIALIVPROC glad_glMaterialiv; #define glMaterialiv glad_glMaterialiv GLAD_API_CALL PFNGLMATRIXINDEXPOINTERARBPROC glad_glMatrixIndexPointerARB; #define glMatrixIndexPointerARB glad_glMatrixIndexPointerARB GLAD_API_CALL PFNGLMATRIXINDEXUBVARBPROC glad_glMatrixIndexubvARB; #define glMatrixIndexubvARB glad_glMatrixIndexubvARB GLAD_API_CALL PFNGLMATRIXINDEXUIVARBPROC glad_glMatrixIndexuivARB; #define glMatrixIndexuivARB glad_glMatrixIndexuivARB GLAD_API_CALL PFNGLMATRIXINDEXUSVARBPROC glad_glMatrixIndexusvARB; #define glMatrixIndexusvARB glad_glMatrixIndexusvARB GLAD_API_CALL PFNGLMATRIXMODEPROC glad_glMatrixMode; #define glMatrixMode glad_glMatrixMode GLAD_API_CALL PFNGLMAXSHADERCOMPILERTHREADSARBPROC glad_glMaxShaderCompilerThreadsARB; #define glMaxShaderCompilerThreadsARB glad_glMaxShaderCompilerThreadsARB GLAD_API_CALL PFNGLMAXSHADERCOMPILERTHREADSKHRPROC glad_glMaxShaderCompilerThreadsKHR; #define glMaxShaderCompilerThreadsKHR glad_glMaxShaderCompilerThreadsKHR GLAD_API_CALL PFNGLMEMORYBARRIERPROC glad_glMemoryBarrier; #define glMemoryBarrier glad_glMemoryBarrier GLAD_API_CALL PFNGLMEMORYBARRIERBYREGIONPROC glad_glMemoryBarrierByRegion; #define glMemoryBarrierByRegion glad_glMemoryBarrierByRegion GLAD_API_CALL PFNGLMINSAMPLESHADINGPROC glad_glMinSampleShading; #define glMinSampleShading glad_glMinSampleShading GLAD_API_CALL PFNGLMINSAMPLESHADINGARBPROC glad_glMinSampleShadingARB; #define glMinSampleShadingARB glad_glMinSampleShadingARB GLAD_API_CALL PFNGLMINMAXPROC glad_glMinmax; #define glMinmax glad_glMinmax GLAD_API_CALL PFNGLMULTMATRIXDPROC glad_glMultMatrixd; #define glMultMatrixd glad_glMultMatrixd GLAD_API_CALL PFNGLMULTMATRIXFPROC glad_glMultMatrixf; #define glMultMatrixf glad_glMultMatrixf GLAD_API_CALL PFNGLMULTTRANSPOSEMATRIXDPROC glad_glMultTransposeMatrixd; #define glMultTransposeMatrixd glad_glMultTransposeMatrixd GLAD_API_CALL PFNGLMULTTRANSPOSEMATRIXDARBPROC glad_glMultTransposeMatrixdARB; #define glMultTransposeMatrixdARB glad_glMultTransposeMatrixdARB GLAD_API_CALL PFNGLMULTTRANSPOSEMATRIXFPROC glad_glMultTransposeMatrixf; #define glMultTransposeMatrixf glad_glMultTransposeMatrixf GLAD_API_CALL PFNGLMULTTRANSPOSEMATRIXFARBPROC glad_glMultTransposeMatrixfARB; #define glMultTransposeMatrixfARB glad_glMultTransposeMatrixfARB GLAD_API_CALL PFNGLMULTIDRAWARRAYSPROC glad_glMultiDrawArrays; #define glMultiDrawArrays glad_glMultiDrawArrays GLAD_API_CALL PFNGLMULTIDRAWARRAYSINDIRECTPROC glad_glMultiDrawArraysIndirect; #define glMultiDrawArraysIndirect glad_glMultiDrawArraysIndirect GLAD_API_CALL PFNGLMULTIDRAWARRAYSINDIRECTCOUNTPROC glad_glMultiDrawArraysIndirectCount; #define glMultiDrawArraysIndirectCount glad_glMultiDrawArraysIndirectCount GLAD_API_CALL PFNGLMULTIDRAWARRAYSINDIRECTCOUNTARBPROC glad_glMultiDrawArraysIndirectCountARB; #define glMultiDrawArraysIndirectCountARB glad_glMultiDrawArraysIndirectCountARB GLAD_API_CALL PFNGLMULTIDRAWELEMENTSPROC glad_glMultiDrawElements; #define glMultiDrawElements glad_glMultiDrawElements GLAD_API_CALL PFNGLMULTIDRAWELEMENTSBASEVERTEXPROC glad_glMultiDrawElementsBaseVertex; #define glMultiDrawElementsBaseVertex glad_glMultiDrawElementsBaseVertex GLAD_API_CALL PFNGLMULTIDRAWELEMENTSINDIRECTPROC glad_glMultiDrawElementsIndirect; #define glMultiDrawElementsIndirect glad_glMultiDrawElementsIndirect GLAD_API_CALL PFNGLMULTIDRAWELEMENTSINDIRECTCOUNTPROC glad_glMultiDrawElementsIndirectCount; #define glMultiDrawElementsIndirectCount glad_glMultiDrawElementsIndirectCount GLAD_API_CALL PFNGLMULTIDRAWELEMENTSINDIRECTCOUNTARBPROC glad_glMultiDrawElementsIndirectCountARB; #define glMultiDrawElementsIndirectCountARB glad_glMultiDrawElementsIndirectCountARB GLAD_API_CALL PFNGLMULTITEXCOORD1DPROC glad_glMultiTexCoord1d; #define glMultiTexCoord1d glad_glMultiTexCoord1d GLAD_API_CALL PFNGLMULTITEXCOORD1DARBPROC glad_glMultiTexCoord1dARB; #define glMultiTexCoord1dARB glad_glMultiTexCoord1dARB GLAD_API_CALL PFNGLMULTITEXCOORD1DVPROC glad_glMultiTexCoord1dv; #define glMultiTexCoord1dv glad_glMultiTexCoord1dv GLAD_API_CALL PFNGLMULTITEXCOORD1DVARBPROC glad_glMultiTexCoord1dvARB; #define glMultiTexCoord1dvARB glad_glMultiTexCoord1dvARB GLAD_API_CALL PFNGLMULTITEXCOORD1FPROC glad_glMultiTexCoord1f; #define glMultiTexCoord1f glad_glMultiTexCoord1f GLAD_API_CALL PFNGLMULTITEXCOORD1FARBPROC glad_glMultiTexCoord1fARB; #define glMultiTexCoord1fARB glad_glMultiTexCoord1fARB GLAD_API_CALL PFNGLMULTITEXCOORD1FVPROC glad_glMultiTexCoord1fv; #define glMultiTexCoord1fv glad_glMultiTexCoord1fv GLAD_API_CALL PFNGLMULTITEXCOORD1FVARBPROC glad_glMultiTexCoord1fvARB; #define glMultiTexCoord1fvARB glad_glMultiTexCoord1fvARB GLAD_API_CALL PFNGLMULTITEXCOORD1IPROC glad_glMultiTexCoord1i; #define glMultiTexCoord1i glad_glMultiTexCoord1i GLAD_API_CALL PFNGLMULTITEXCOORD1IARBPROC glad_glMultiTexCoord1iARB; #define glMultiTexCoord1iARB glad_glMultiTexCoord1iARB GLAD_API_CALL PFNGLMULTITEXCOORD1IVPROC glad_glMultiTexCoord1iv; #define glMultiTexCoord1iv glad_glMultiTexCoord1iv GLAD_API_CALL PFNGLMULTITEXCOORD1IVARBPROC glad_glMultiTexCoord1ivARB; #define glMultiTexCoord1ivARB glad_glMultiTexCoord1ivARB GLAD_API_CALL PFNGLMULTITEXCOORD1SPROC glad_glMultiTexCoord1s; #define glMultiTexCoord1s glad_glMultiTexCoord1s GLAD_API_CALL PFNGLMULTITEXCOORD1SARBPROC glad_glMultiTexCoord1sARB; #define glMultiTexCoord1sARB glad_glMultiTexCoord1sARB GLAD_API_CALL PFNGLMULTITEXCOORD1SVPROC glad_glMultiTexCoord1sv; #define glMultiTexCoord1sv glad_glMultiTexCoord1sv GLAD_API_CALL PFNGLMULTITEXCOORD1SVARBPROC glad_glMultiTexCoord1svARB; #define glMultiTexCoord1svARB glad_glMultiTexCoord1svARB GLAD_API_CALL PFNGLMULTITEXCOORD2DPROC glad_glMultiTexCoord2d; #define glMultiTexCoord2d glad_glMultiTexCoord2d GLAD_API_CALL PFNGLMULTITEXCOORD2DARBPROC glad_glMultiTexCoord2dARB; #define glMultiTexCoord2dARB glad_glMultiTexCoord2dARB GLAD_API_CALL PFNGLMULTITEXCOORD2DVPROC glad_glMultiTexCoord2dv; #define glMultiTexCoord2dv glad_glMultiTexCoord2dv GLAD_API_CALL PFNGLMULTITEXCOORD2DVARBPROC glad_glMultiTexCoord2dvARB; #define glMultiTexCoord2dvARB glad_glMultiTexCoord2dvARB GLAD_API_CALL PFNGLMULTITEXCOORD2FPROC glad_glMultiTexCoord2f; #define glMultiTexCoord2f glad_glMultiTexCoord2f GLAD_API_CALL PFNGLMULTITEXCOORD2FARBPROC glad_glMultiTexCoord2fARB; #define glMultiTexCoord2fARB glad_glMultiTexCoord2fARB GLAD_API_CALL PFNGLMULTITEXCOORD2FVPROC glad_glMultiTexCoord2fv; #define glMultiTexCoord2fv glad_glMultiTexCoord2fv GLAD_API_CALL PFNGLMULTITEXCOORD2FVARBPROC glad_glMultiTexCoord2fvARB; #define glMultiTexCoord2fvARB glad_glMultiTexCoord2fvARB GLAD_API_CALL PFNGLMULTITEXCOORD2IPROC glad_glMultiTexCoord2i; #define glMultiTexCoord2i glad_glMultiTexCoord2i GLAD_API_CALL PFNGLMULTITEXCOORD2IARBPROC glad_glMultiTexCoord2iARB; #define glMultiTexCoord2iARB glad_glMultiTexCoord2iARB GLAD_API_CALL PFNGLMULTITEXCOORD2IVPROC glad_glMultiTexCoord2iv; #define glMultiTexCoord2iv glad_glMultiTexCoord2iv GLAD_API_CALL PFNGLMULTITEXCOORD2IVARBPROC glad_glMultiTexCoord2ivARB; #define glMultiTexCoord2ivARB glad_glMultiTexCoord2ivARB GLAD_API_CALL PFNGLMULTITEXCOORD2SPROC glad_glMultiTexCoord2s; #define glMultiTexCoord2s glad_glMultiTexCoord2s GLAD_API_CALL PFNGLMULTITEXCOORD2SARBPROC glad_glMultiTexCoord2sARB; #define glMultiTexCoord2sARB glad_glMultiTexCoord2sARB GLAD_API_CALL PFNGLMULTITEXCOORD2SVPROC glad_glMultiTexCoord2sv; #define glMultiTexCoord2sv glad_glMultiTexCoord2sv GLAD_API_CALL PFNGLMULTITEXCOORD2SVARBPROC glad_glMultiTexCoord2svARB; #define glMultiTexCoord2svARB glad_glMultiTexCoord2svARB GLAD_API_CALL PFNGLMULTITEXCOORD3DPROC glad_glMultiTexCoord3d; #define glMultiTexCoord3d glad_glMultiTexCoord3d GLAD_API_CALL PFNGLMULTITEXCOORD3DARBPROC glad_glMultiTexCoord3dARB; #define glMultiTexCoord3dARB glad_glMultiTexCoord3dARB GLAD_API_CALL PFNGLMULTITEXCOORD3DVPROC glad_glMultiTexCoord3dv; #define glMultiTexCoord3dv glad_glMultiTexCoord3dv GLAD_API_CALL PFNGLMULTITEXCOORD3DVARBPROC glad_glMultiTexCoord3dvARB; #define glMultiTexCoord3dvARB glad_glMultiTexCoord3dvARB GLAD_API_CALL PFNGLMULTITEXCOORD3FPROC glad_glMultiTexCoord3f; #define glMultiTexCoord3f glad_glMultiTexCoord3f GLAD_API_CALL PFNGLMULTITEXCOORD3FARBPROC glad_glMultiTexCoord3fARB; #define glMultiTexCoord3fARB glad_glMultiTexCoord3fARB GLAD_API_CALL PFNGLMULTITEXCOORD3FVPROC glad_glMultiTexCoord3fv; #define glMultiTexCoord3fv glad_glMultiTexCoord3fv GLAD_API_CALL PFNGLMULTITEXCOORD3FVARBPROC glad_glMultiTexCoord3fvARB; #define glMultiTexCoord3fvARB glad_glMultiTexCoord3fvARB GLAD_API_CALL PFNGLMULTITEXCOORD3IPROC glad_glMultiTexCoord3i; #define glMultiTexCoord3i glad_glMultiTexCoord3i GLAD_API_CALL PFNGLMULTITEXCOORD3IARBPROC glad_glMultiTexCoord3iARB; #define glMultiTexCoord3iARB glad_glMultiTexCoord3iARB GLAD_API_CALL PFNGLMULTITEXCOORD3IVPROC glad_glMultiTexCoord3iv; #define glMultiTexCoord3iv glad_glMultiTexCoord3iv GLAD_API_CALL PFNGLMULTITEXCOORD3IVARBPROC glad_glMultiTexCoord3ivARB; #define glMultiTexCoord3ivARB glad_glMultiTexCoord3ivARB GLAD_API_CALL PFNGLMULTITEXCOORD3SPROC glad_glMultiTexCoord3s; #define glMultiTexCoord3s glad_glMultiTexCoord3s GLAD_API_CALL PFNGLMULTITEXCOORD3SARBPROC glad_glMultiTexCoord3sARB; #define glMultiTexCoord3sARB glad_glMultiTexCoord3sARB GLAD_API_CALL PFNGLMULTITEXCOORD3SVPROC glad_glMultiTexCoord3sv; #define glMultiTexCoord3sv glad_glMultiTexCoord3sv GLAD_API_CALL PFNGLMULTITEXCOORD3SVARBPROC glad_glMultiTexCoord3svARB; #define glMultiTexCoord3svARB glad_glMultiTexCoord3svARB GLAD_API_CALL PFNGLMULTITEXCOORD4DPROC glad_glMultiTexCoord4d; #define glMultiTexCoord4d glad_glMultiTexCoord4d GLAD_API_CALL PFNGLMULTITEXCOORD4DARBPROC glad_glMultiTexCoord4dARB; #define glMultiTexCoord4dARB glad_glMultiTexCoord4dARB GLAD_API_CALL PFNGLMULTITEXCOORD4DVPROC glad_glMultiTexCoord4dv; #define glMultiTexCoord4dv glad_glMultiTexCoord4dv GLAD_API_CALL PFNGLMULTITEXCOORD4DVARBPROC glad_glMultiTexCoord4dvARB; #define glMultiTexCoord4dvARB glad_glMultiTexCoord4dvARB GLAD_API_CALL PFNGLMULTITEXCOORD4FPROC glad_glMultiTexCoord4f; #define glMultiTexCoord4f glad_glMultiTexCoord4f GLAD_API_CALL PFNGLMULTITEXCOORD4FARBPROC glad_glMultiTexCoord4fARB; #define glMultiTexCoord4fARB glad_glMultiTexCoord4fARB GLAD_API_CALL PFNGLMULTITEXCOORD4FVPROC glad_glMultiTexCoord4fv; #define glMultiTexCoord4fv glad_glMultiTexCoord4fv GLAD_API_CALL PFNGLMULTITEXCOORD4FVARBPROC glad_glMultiTexCoord4fvARB; #define glMultiTexCoord4fvARB glad_glMultiTexCoord4fvARB GLAD_API_CALL PFNGLMULTITEXCOORD4IPROC glad_glMultiTexCoord4i; #define glMultiTexCoord4i glad_glMultiTexCoord4i GLAD_API_CALL PFNGLMULTITEXCOORD4IARBPROC glad_glMultiTexCoord4iARB; #define glMultiTexCoord4iARB glad_glMultiTexCoord4iARB GLAD_API_CALL PFNGLMULTITEXCOORD4IVPROC glad_glMultiTexCoord4iv; #define glMultiTexCoord4iv glad_glMultiTexCoord4iv GLAD_API_CALL PFNGLMULTITEXCOORD4IVARBPROC glad_glMultiTexCoord4ivARB; #define glMultiTexCoord4ivARB glad_glMultiTexCoord4ivARB GLAD_API_CALL PFNGLMULTITEXCOORD4SPROC glad_glMultiTexCoord4s; #define glMultiTexCoord4s glad_glMultiTexCoord4s GLAD_API_CALL PFNGLMULTITEXCOORD4SARBPROC glad_glMultiTexCoord4sARB; #define glMultiTexCoord4sARB glad_glMultiTexCoord4sARB GLAD_API_CALL PFNGLMULTITEXCOORD4SVPROC glad_glMultiTexCoord4sv; #define glMultiTexCoord4sv glad_glMultiTexCoord4sv GLAD_API_CALL PFNGLMULTITEXCOORD4SVARBPROC glad_glMultiTexCoord4svARB; #define glMultiTexCoord4svARB glad_glMultiTexCoord4svARB GLAD_API_CALL PFNGLMULTITEXCOORDP1UIPROC glad_glMultiTexCoordP1ui; #define glMultiTexCoordP1ui glad_glMultiTexCoordP1ui GLAD_API_CALL PFNGLMULTITEXCOORDP1UIVPROC glad_glMultiTexCoordP1uiv; #define glMultiTexCoordP1uiv glad_glMultiTexCoordP1uiv GLAD_API_CALL PFNGLMULTITEXCOORDP2UIPROC glad_glMultiTexCoordP2ui; #define glMultiTexCoordP2ui glad_glMultiTexCoordP2ui GLAD_API_CALL PFNGLMULTITEXCOORDP2UIVPROC glad_glMultiTexCoordP2uiv; #define glMultiTexCoordP2uiv glad_glMultiTexCoordP2uiv GLAD_API_CALL PFNGLMULTITEXCOORDP3UIPROC glad_glMultiTexCoordP3ui; #define glMultiTexCoordP3ui glad_glMultiTexCoordP3ui GLAD_API_CALL PFNGLMULTITEXCOORDP3UIVPROC glad_glMultiTexCoordP3uiv; #define glMultiTexCoordP3uiv glad_glMultiTexCoordP3uiv GLAD_API_CALL PFNGLMULTITEXCOORDP4UIPROC glad_glMultiTexCoordP4ui; #define glMultiTexCoordP4ui glad_glMultiTexCoordP4ui GLAD_API_CALL PFNGLMULTITEXCOORDP4UIVPROC glad_glMultiTexCoordP4uiv; #define glMultiTexCoordP4uiv glad_glMultiTexCoordP4uiv GLAD_API_CALL PFNGLNAMEDBUFFERDATAPROC glad_glNamedBufferData; #define glNamedBufferData glad_glNamedBufferData GLAD_API_CALL PFNGLNAMEDBUFFERPAGECOMMITMENTARBPROC glad_glNamedBufferPageCommitmentARB; #define glNamedBufferPageCommitmentARB glad_glNamedBufferPageCommitmentARB GLAD_API_CALL PFNGLNAMEDBUFFERPAGECOMMITMENTEXTPROC glad_glNamedBufferPageCommitmentEXT; #define glNamedBufferPageCommitmentEXT glad_glNamedBufferPageCommitmentEXT GLAD_API_CALL PFNGLNAMEDBUFFERSTORAGEPROC glad_glNamedBufferStorage; #define glNamedBufferStorage glad_glNamedBufferStorage GLAD_API_CALL PFNGLNAMEDBUFFERSUBDATAPROC glad_glNamedBufferSubData; #define glNamedBufferSubData glad_glNamedBufferSubData GLAD_API_CALL PFNGLNAMEDFRAMEBUFFERDRAWBUFFERPROC glad_glNamedFramebufferDrawBuffer; #define glNamedFramebufferDrawBuffer glad_glNamedFramebufferDrawBuffer GLAD_API_CALL PFNGLNAMEDFRAMEBUFFERDRAWBUFFERSPROC glad_glNamedFramebufferDrawBuffers; #define glNamedFramebufferDrawBuffers glad_glNamedFramebufferDrawBuffers GLAD_API_CALL PFNGLNAMEDFRAMEBUFFERPARAMETERIPROC glad_glNamedFramebufferParameteri; #define glNamedFramebufferParameteri glad_glNamedFramebufferParameteri GLAD_API_CALL PFNGLNAMEDFRAMEBUFFERREADBUFFERPROC glad_glNamedFramebufferReadBuffer; #define glNamedFramebufferReadBuffer glad_glNamedFramebufferReadBuffer GLAD_API_CALL PFNGLNAMEDFRAMEBUFFERRENDERBUFFERPROC glad_glNamedFramebufferRenderbuffer; #define glNamedFramebufferRenderbuffer glad_glNamedFramebufferRenderbuffer GLAD_API_CALL PFNGLNAMEDFRAMEBUFFERSAMPLELOCATIONSFVARBPROC glad_glNamedFramebufferSampleLocationsfvARB; #define glNamedFramebufferSampleLocationsfvARB glad_glNamedFramebufferSampleLocationsfvARB GLAD_API_CALL PFNGLNAMEDFRAMEBUFFERTEXTUREPROC glad_glNamedFramebufferTexture; #define glNamedFramebufferTexture glad_glNamedFramebufferTexture GLAD_API_CALL PFNGLNAMEDFRAMEBUFFERTEXTURELAYERPROC glad_glNamedFramebufferTextureLayer; #define glNamedFramebufferTextureLayer glad_glNamedFramebufferTextureLayer GLAD_API_CALL PFNGLNAMEDRENDERBUFFERSTORAGEPROC glad_glNamedRenderbufferStorage; #define glNamedRenderbufferStorage glad_glNamedRenderbufferStorage GLAD_API_CALL PFNGLNAMEDRENDERBUFFERSTORAGEMULTISAMPLEPROC glad_glNamedRenderbufferStorageMultisample; #define glNamedRenderbufferStorageMultisample glad_glNamedRenderbufferStorageMultisample GLAD_API_CALL PFNGLNAMEDSTRINGARBPROC glad_glNamedStringARB; #define glNamedStringARB glad_glNamedStringARB GLAD_API_CALL PFNGLNEWLISTPROC glad_glNewList; #define glNewList glad_glNewList GLAD_API_CALL PFNGLNORMAL3BPROC glad_glNormal3b; #define glNormal3b glad_glNormal3b GLAD_API_CALL PFNGLNORMAL3BVPROC glad_glNormal3bv; #define glNormal3bv glad_glNormal3bv GLAD_API_CALL PFNGLNORMAL3DPROC glad_glNormal3d; #define glNormal3d glad_glNormal3d GLAD_API_CALL PFNGLNORMAL3DVPROC glad_glNormal3dv; #define glNormal3dv glad_glNormal3dv GLAD_API_CALL PFNGLNORMAL3FPROC glad_glNormal3f; #define glNormal3f glad_glNormal3f GLAD_API_CALL PFNGLNORMAL3FVPROC glad_glNormal3fv; #define glNormal3fv glad_glNormal3fv GLAD_API_CALL PFNGLNORMAL3IPROC glad_glNormal3i; #define glNormal3i glad_glNormal3i GLAD_API_CALL PFNGLNORMAL3IVPROC glad_glNormal3iv; #define glNormal3iv glad_glNormal3iv GLAD_API_CALL PFNGLNORMAL3SPROC glad_glNormal3s; #define glNormal3s glad_glNormal3s GLAD_API_CALL PFNGLNORMAL3SVPROC glad_glNormal3sv; #define glNormal3sv glad_glNormal3sv GLAD_API_CALL PFNGLNORMALP3UIPROC glad_glNormalP3ui; #define glNormalP3ui glad_glNormalP3ui GLAD_API_CALL PFNGLNORMALP3UIVPROC glad_glNormalP3uiv; #define glNormalP3uiv glad_glNormalP3uiv GLAD_API_CALL PFNGLNORMALPOINTERPROC glad_glNormalPointer; #define glNormalPointer glad_glNormalPointer GLAD_API_CALL PFNGLOBJECTLABELPROC glad_glObjectLabel; #define glObjectLabel glad_glObjectLabel GLAD_API_CALL PFNGLOBJECTPTRLABELPROC glad_glObjectPtrLabel; #define glObjectPtrLabel glad_glObjectPtrLabel GLAD_API_CALL PFNGLORTHOPROC glad_glOrtho; #define glOrtho glad_glOrtho GLAD_API_CALL PFNGLPASSTHROUGHPROC glad_glPassThrough; #define glPassThrough glad_glPassThrough GLAD_API_CALL PFNGLPATCHPARAMETERFVPROC glad_glPatchParameterfv; #define glPatchParameterfv glad_glPatchParameterfv GLAD_API_CALL PFNGLPATCHPARAMETERIPROC glad_glPatchParameteri; #define glPatchParameteri glad_glPatchParameteri GLAD_API_CALL PFNGLPAUSETRANSFORMFEEDBACKPROC glad_glPauseTransformFeedback; #define glPauseTransformFeedback glad_glPauseTransformFeedback GLAD_API_CALL PFNGLPIXELMAPFVPROC glad_glPixelMapfv; #define glPixelMapfv glad_glPixelMapfv GLAD_API_CALL PFNGLPIXELMAPUIVPROC glad_glPixelMapuiv; #define glPixelMapuiv glad_glPixelMapuiv GLAD_API_CALL PFNGLPIXELMAPUSVPROC glad_glPixelMapusv; #define glPixelMapusv glad_glPixelMapusv GLAD_API_CALL PFNGLPIXELSTOREFPROC glad_glPixelStoref; #define glPixelStoref glad_glPixelStoref GLAD_API_CALL PFNGLPIXELSTOREIPROC glad_glPixelStorei; #define glPixelStorei glad_glPixelStorei GLAD_API_CALL PFNGLPIXELTRANSFERFPROC glad_glPixelTransferf; #define glPixelTransferf glad_glPixelTransferf GLAD_API_CALL PFNGLPIXELTRANSFERIPROC glad_glPixelTransferi; #define glPixelTransferi glad_glPixelTransferi GLAD_API_CALL PFNGLPIXELZOOMPROC glad_glPixelZoom; #define glPixelZoom glad_glPixelZoom GLAD_API_CALL PFNGLPOINTPARAMETERFPROC glad_glPointParameterf; #define glPointParameterf glad_glPointParameterf GLAD_API_CALL PFNGLPOINTPARAMETERFARBPROC glad_glPointParameterfARB; #define glPointParameterfARB glad_glPointParameterfARB GLAD_API_CALL PFNGLPOINTPARAMETERFVPROC glad_glPointParameterfv; #define glPointParameterfv glad_glPointParameterfv GLAD_API_CALL PFNGLPOINTPARAMETERFVARBPROC glad_glPointParameterfvARB; #define glPointParameterfvARB glad_glPointParameterfvARB GLAD_API_CALL PFNGLPOINTPARAMETERIPROC glad_glPointParameteri; #define glPointParameteri glad_glPointParameteri GLAD_API_CALL PFNGLPOINTPARAMETERIVPROC glad_glPointParameteriv; #define glPointParameteriv glad_glPointParameteriv GLAD_API_CALL PFNGLPOINTSIZEPROC glad_glPointSize; #define glPointSize glad_glPointSize GLAD_API_CALL PFNGLPOLYGONMODEPROC glad_glPolygonMode; #define glPolygonMode glad_glPolygonMode GLAD_API_CALL PFNGLPOLYGONOFFSETPROC glad_glPolygonOffset; #define glPolygonOffset glad_glPolygonOffset GLAD_API_CALL PFNGLPOLYGONOFFSETCLAMPPROC glad_glPolygonOffsetClamp; #define glPolygonOffsetClamp glad_glPolygonOffsetClamp GLAD_API_CALL PFNGLPOLYGONSTIPPLEPROC glad_glPolygonStipple; #define glPolygonStipple glad_glPolygonStipple GLAD_API_CALL PFNGLPOPATTRIBPROC glad_glPopAttrib; #define glPopAttrib glad_glPopAttrib GLAD_API_CALL PFNGLPOPCLIENTATTRIBPROC glad_glPopClientAttrib; #define glPopClientAttrib glad_glPopClientAttrib GLAD_API_CALL PFNGLPOPDEBUGGROUPPROC glad_glPopDebugGroup; #define glPopDebugGroup glad_glPopDebugGroup GLAD_API_CALL PFNGLPOPMATRIXPROC glad_glPopMatrix; #define glPopMatrix glad_glPopMatrix GLAD_API_CALL PFNGLPOPNAMEPROC glad_glPopName; #define glPopName glad_glPopName GLAD_API_CALL PFNGLPRIMITIVEBOUNDINGBOXPROC glad_glPrimitiveBoundingBox; #define glPrimitiveBoundingBox glad_glPrimitiveBoundingBox GLAD_API_CALL PFNGLPRIMITIVEBOUNDINGBOXARBPROC glad_glPrimitiveBoundingBoxARB; #define glPrimitiveBoundingBoxARB glad_glPrimitiveBoundingBoxARB GLAD_API_CALL PFNGLPRIMITIVERESTARTINDEXPROC glad_glPrimitiveRestartIndex; #define glPrimitiveRestartIndex glad_glPrimitiveRestartIndex GLAD_API_CALL PFNGLPRIORITIZETEXTURESPROC glad_glPrioritizeTextures; #define glPrioritizeTextures glad_glPrioritizeTextures GLAD_API_CALL PFNGLPROGRAMBINARYPROC glad_glProgramBinary; #define glProgramBinary glad_glProgramBinary GLAD_API_CALL PFNGLPROGRAMENVPARAMETER4DARBPROC glad_glProgramEnvParameter4dARB; #define glProgramEnvParameter4dARB glad_glProgramEnvParameter4dARB GLAD_API_CALL PFNGLPROGRAMENVPARAMETER4DVARBPROC glad_glProgramEnvParameter4dvARB; #define glProgramEnvParameter4dvARB glad_glProgramEnvParameter4dvARB GLAD_API_CALL PFNGLPROGRAMENVPARAMETER4FARBPROC glad_glProgramEnvParameter4fARB; #define glProgramEnvParameter4fARB glad_glProgramEnvParameter4fARB GLAD_API_CALL PFNGLPROGRAMENVPARAMETER4FVARBPROC glad_glProgramEnvParameter4fvARB; #define glProgramEnvParameter4fvARB glad_glProgramEnvParameter4fvARB GLAD_API_CALL PFNGLPROGRAMLOCALPARAMETER4DARBPROC glad_glProgramLocalParameter4dARB; #define glProgramLocalParameter4dARB glad_glProgramLocalParameter4dARB GLAD_API_CALL PFNGLPROGRAMLOCALPARAMETER4DVARBPROC glad_glProgramLocalParameter4dvARB; #define glProgramLocalParameter4dvARB glad_glProgramLocalParameter4dvARB GLAD_API_CALL PFNGLPROGRAMLOCALPARAMETER4FARBPROC glad_glProgramLocalParameter4fARB; #define glProgramLocalParameter4fARB glad_glProgramLocalParameter4fARB GLAD_API_CALL PFNGLPROGRAMLOCALPARAMETER4FVARBPROC glad_glProgramLocalParameter4fvARB; #define glProgramLocalParameter4fvARB glad_glProgramLocalParameter4fvARB GLAD_API_CALL PFNGLPROGRAMPARAMETERIPROC glad_glProgramParameteri; #define glProgramParameteri glad_glProgramParameteri GLAD_API_CALL PFNGLPROGRAMPARAMETERIARBPROC glad_glProgramParameteriARB; #define glProgramParameteriARB glad_glProgramParameteriARB GLAD_API_CALL PFNGLPROGRAMSTRINGARBPROC glad_glProgramStringARB; #define glProgramStringARB glad_glProgramStringARB GLAD_API_CALL PFNGLPROGRAMUNIFORM1DPROC glad_glProgramUniform1d; #define glProgramUniform1d glad_glProgramUniform1d GLAD_API_CALL PFNGLPROGRAMUNIFORM1DVPROC glad_glProgramUniform1dv; #define glProgramUniform1dv glad_glProgramUniform1dv GLAD_API_CALL PFNGLPROGRAMUNIFORM1FPROC glad_glProgramUniform1f; #define glProgramUniform1f glad_glProgramUniform1f GLAD_API_CALL PFNGLPROGRAMUNIFORM1FVPROC glad_glProgramUniform1fv; #define glProgramUniform1fv glad_glProgramUniform1fv GLAD_API_CALL PFNGLPROGRAMUNIFORM1IPROC glad_glProgramUniform1i; #define glProgramUniform1i glad_glProgramUniform1i GLAD_API_CALL PFNGLPROGRAMUNIFORM1I64ARBPROC glad_glProgramUniform1i64ARB; #define glProgramUniform1i64ARB glad_glProgramUniform1i64ARB GLAD_API_CALL PFNGLPROGRAMUNIFORM1I64VARBPROC glad_glProgramUniform1i64vARB; #define glProgramUniform1i64vARB glad_glProgramUniform1i64vARB GLAD_API_CALL PFNGLPROGRAMUNIFORM1IVPROC glad_glProgramUniform1iv; #define glProgramUniform1iv glad_glProgramUniform1iv GLAD_API_CALL PFNGLPROGRAMUNIFORM1UIPROC glad_glProgramUniform1ui; #define glProgramUniform1ui glad_glProgramUniform1ui GLAD_API_CALL PFNGLPROGRAMUNIFORM1UI64ARBPROC glad_glProgramUniform1ui64ARB; #define glProgramUniform1ui64ARB glad_glProgramUniform1ui64ARB GLAD_API_CALL PFNGLPROGRAMUNIFORM1UI64VARBPROC glad_glProgramUniform1ui64vARB; #define glProgramUniform1ui64vARB glad_glProgramUniform1ui64vARB GLAD_API_CALL PFNGLPROGRAMUNIFORM1UIVPROC glad_glProgramUniform1uiv; #define glProgramUniform1uiv glad_glProgramUniform1uiv GLAD_API_CALL PFNGLPROGRAMUNIFORM2DPROC glad_glProgramUniform2d; #define glProgramUniform2d glad_glProgramUniform2d GLAD_API_CALL PFNGLPROGRAMUNIFORM2DVPROC glad_glProgramUniform2dv; #define glProgramUniform2dv glad_glProgramUniform2dv GLAD_API_CALL PFNGLPROGRAMUNIFORM2FPROC glad_glProgramUniform2f; #define glProgramUniform2f glad_glProgramUniform2f GLAD_API_CALL PFNGLPROGRAMUNIFORM2FVPROC glad_glProgramUniform2fv; #define glProgramUniform2fv glad_glProgramUniform2fv GLAD_API_CALL PFNGLPROGRAMUNIFORM2IPROC glad_glProgramUniform2i; #define glProgramUniform2i glad_glProgramUniform2i GLAD_API_CALL PFNGLPROGRAMUNIFORM2I64ARBPROC glad_glProgramUniform2i64ARB; #define glProgramUniform2i64ARB glad_glProgramUniform2i64ARB GLAD_API_CALL PFNGLPROGRAMUNIFORM2I64VARBPROC glad_glProgramUniform2i64vARB; #define glProgramUniform2i64vARB glad_glProgramUniform2i64vARB GLAD_API_CALL PFNGLPROGRAMUNIFORM2IVPROC glad_glProgramUniform2iv; #define glProgramUniform2iv glad_glProgramUniform2iv GLAD_API_CALL PFNGLPROGRAMUNIFORM2UIPROC glad_glProgramUniform2ui; #define glProgramUniform2ui glad_glProgramUniform2ui GLAD_API_CALL PFNGLPROGRAMUNIFORM2UI64ARBPROC glad_glProgramUniform2ui64ARB; #define glProgramUniform2ui64ARB glad_glProgramUniform2ui64ARB GLAD_API_CALL PFNGLPROGRAMUNIFORM2UI64VARBPROC glad_glProgramUniform2ui64vARB; #define glProgramUniform2ui64vARB glad_glProgramUniform2ui64vARB GLAD_API_CALL PFNGLPROGRAMUNIFORM2UIVPROC glad_glProgramUniform2uiv; #define glProgramUniform2uiv glad_glProgramUniform2uiv GLAD_API_CALL PFNGLPROGRAMUNIFORM3DPROC glad_glProgramUniform3d; #define glProgramUniform3d glad_glProgramUniform3d GLAD_API_CALL PFNGLPROGRAMUNIFORM3DVPROC glad_glProgramUniform3dv; #define glProgramUniform3dv glad_glProgramUniform3dv GLAD_API_CALL PFNGLPROGRAMUNIFORM3FPROC glad_glProgramUniform3f; #define glProgramUniform3f glad_glProgramUniform3f GLAD_API_CALL PFNGLPROGRAMUNIFORM3FVPROC glad_glProgramUniform3fv; #define glProgramUniform3fv glad_glProgramUniform3fv GLAD_API_CALL PFNGLPROGRAMUNIFORM3IPROC glad_glProgramUniform3i; #define glProgramUniform3i glad_glProgramUniform3i GLAD_API_CALL PFNGLPROGRAMUNIFORM3I64ARBPROC glad_glProgramUniform3i64ARB; #define glProgramUniform3i64ARB glad_glProgramUniform3i64ARB GLAD_API_CALL PFNGLPROGRAMUNIFORM3I64VARBPROC glad_glProgramUniform3i64vARB; #define glProgramUniform3i64vARB glad_glProgramUniform3i64vARB GLAD_API_CALL PFNGLPROGRAMUNIFORM3IVPROC glad_glProgramUniform3iv; #define glProgramUniform3iv glad_glProgramUniform3iv GLAD_API_CALL PFNGLPROGRAMUNIFORM3UIPROC glad_glProgramUniform3ui; #define glProgramUniform3ui glad_glProgramUniform3ui GLAD_API_CALL PFNGLPROGRAMUNIFORM3UI64ARBPROC glad_glProgramUniform3ui64ARB; #define glProgramUniform3ui64ARB glad_glProgramUniform3ui64ARB GLAD_API_CALL PFNGLPROGRAMUNIFORM3UI64VARBPROC glad_glProgramUniform3ui64vARB; #define glProgramUniform3ui64vARB glad_glProgramUniform3ui64vARB GLAD_API_CALL PFNGLPROGRAMUNIFORM3UIVPROC glad_glProgramUniform3uiv; #define glProgramUniform3uiv glad_glProgramUniform3uiv GLAD_API_CALL PFNGLPROGRAMUNIFORM4DPROC glad_glProgramUniform4d; #define glProgramUniform4d glad_glProgramUniform4d GLAD_API_CALL PFNGLPROGRAMUNIFORM4DVPROC glad_glProgramUniform4dv; #define glProgramUniform4dv glad_glProgramUniform4dv GLAD_API_CALL PFNGLPROGRAMUNIFORM4FPROC glad_glProgramUniform4f; #define glProgramUniform4f glad_glProgramUniform4f GLAD_API_CALL PFNGLPROGRAMUNIFORM4FVPROC glad_glProgramUniform4fv; #define glProgramUniform4fv glad_glProgramUniform4fv GLAD_API_CALL PFNGLPROGRAMUNIFORM4IPROC glad_glProgramUniform4i; #define glProgramUniform4i glad_glProgramUniform4i GLAD_API_CALL PFNGLPROGRAMUNIFORM4I64ARBPROC glad_glProgramUniform4i64ARB; #define glProgramUniform4i64ARB glad_glProgramUniform4i64ARB GLAD_API_CALL PFNGLPROGRAMUNIFORM4I64VARBPROC glad_glProgramUniform4i64vARB; #define glProgramUniform4i64vARB glad_glProgramUniform4i64vARB GLAD_API_CALL PFNGLPROGRAMUNIFORM4IVPROC glad_glProgramUniform4iv; #define glProgramUniform4iv glad_glProgramUniform4iv GLAD_API_CALL PFNGLPROGRAMUNIFORM4UIPROC glad_glProgramUniform4ui; #define glProgramUniform4ui glad_glProgramUniform4ui GLAD_API_CALL PFNGLPROGRAMUNIFORM4UI64ARBPROC glad_glProgramUniform4ui64ARB; #define glProgramUniform4ui64ARB glad_glProgramUniform4ui64ARB GLAD_API_CALL PFNGLPROGRAMUNIFORM4UI64VARBPROC glad_glProgramUniform4ui64vARB; #define glProgramUniform4ui64vARB glad_glProgramUniform4ui64vARB GLAD_API_CALL PFNGLPROGRAMUNIFORM4UIVPROC glad_glProgramUniform4uiv; #define glProgramUniform4uiv glad_glProgramUniform4uiv GLAD_API_CALL PFNGLPROGRAMUNIFORMHANDLEUI64ARBPROC glad_glProgramUniformHandleui64ARB; #define glProgramUniformHandleui64ARB glad_glProgramUniformHandleui64ARB GLAD_API_CALL PFNGLPROGRAMUNIFORMHANDLEUI64VARBPROC glad_glProgramUniformHandleui64vARB; #define glProgramUniformHandleui64vARB glad_glProgramUniformHandleui64vARB GLAD_API_CALL PFNGLPROGRAMUNIFORMMATRIX2DVPROC glad_glProgramUniformMatrix2dv; #define glProgramUniformMatrix2dv glad_glProgramUniformMatrix2dv GLAD_API_CALL PFNGLPROGRAMUNIFORMMATRIX2FVPROC glad_glProgramUniformMatrix2fv; #define glProgramUniformMatrix2fv glad_glProgramUniformMatrix2fv GLAD_API_CALL PFNGLPROGRAMUNIFORMMATRIX2X3DVPROC glad_glProgramUniformMatrix2x3dv; #define glProgramUniformMatrix2x3dv glad_glProgramUniformMatrix2x3dv GLAD_API_CALL PFNGLPROGRAMUNIFORMMATRIX2X3FVPROC glad_glProgramUniformMatrix2x3fv; #define glProgramUniformMatrix2x3fv glad_glProgramUniformMatrix2x3fv GLAD_API_CALL PFNGLPROGRAMUNIFORMMATRIX2X4DVPROC glad_glProgramUniformMatrix2x4dv; #define glProgramUniformMatrix2x4dv glad_glProgramUniformMatrix2x4dv GLAD_API_CALL PFNGLPROGRAMUNIFORMMATRIX2X4FVPROC glad_glProgramUniformMatrix2x4fv; #define glProgramUniformMatrix2x4fv glad_glProgramUniformMatrix2x4fv GLAD_API_CALL PFNGLPROGRAMUNIFORMMATRIX3DVPROC glad_glProgramUniformMatrix3dv; #define glProgramUniformMatrix3dv glad_glProgramUniformMatrix3dv GLAD_API_CALL PFNGLPROGRAMUNIFORMMATRIX3FVPROC glad_glProgramUniformMatrix3fv; #define glProgramUniformMatrix3fv glad_glProgramUniformMatrix3fv GLAD_API_CALL PFNGLPROGRAMUNIFORMMATRIX3X2DVPROC glad_glProgramUniformMatrix3x2dv; #define glProgramUniformMatrix3x2dv glad_glProgramUniformMatrix3x2dv GLAD_API_CALL PFNGLPROGRAMUNIFORMMATRIX3X2FVPROC glad_glProgramUniformMatrix3x2fv; #define glProgramUniformMatrix3x2fv glad_glProgramUniformMatrix3x2fv GLAD_API_CALL PFNGLPROGRAMUNIFORMMATRIX3X4DVPROC glad_glProgramUniformMatrix3x4dv; #define glProgramUniformMatrix3x4dv glad_glProgramUniformMatrix3x4dv GLAD_API_CALL PFNGLPROGRAMUNIFORMMATRIX3X4FVPROC glad_glProgramUniformMatrix3x4fv; #define glProgramUniformMatrix3x4fv glad_glProgramUniformMatrix3x4fv GLAD_API_CALL PFNGLPROGRAMUNIFORMMATRIX4DVPROC glad_glProgramUniformMatrix4dv; #define glProgramUniformMatrix4dv glad_glProgramUniformMatrix4dv GLAD_API_CALL PFNGLPROGRAMUNIFORMMATRIX4FVPROC glad_glProgramUniformMatrix4fv; #define glProgramUniformMatrix4fv glad_glProgramUniformMatrix4fv GLAD_API_CALL PFNGLPROGRAMUNIFORMMATRIX4X2DVPROC glad_glProgramUniformMatrix4x2dv; #define glProgramUniformMatrix4x2dv glad_glProgramUniformMatrix4x2dv GLAD_API_CALL PFNGLPROGRAMUNIFORMMATRIX4X2FVPROC glad_glProgramUniformMatrix4x2fv; #define glProgramUniformMatrix4x2fv glad_glProgramUniformMatrix4x2fv GLAD_API_CALL PFNGLPROGRAMUNIFORMMATRIX4X3DVPROC glad_glProgramUniformMatrix4x3dv; #define glProgramUniformMatrix4x3dv glad_glProgramUniformMatrix4x3dv GLAD_API_CALL PFNGLPROGRAMUNIFORMMATRIX4X3FVPROC glad_glProgramUniformMatrix4x3fv; #define glProgramUniformMatrix4x3fv glad_glProgramUniformMatrix4x3fv GLAD_API_CALL PFNGLPROVOKINGVERTEXPROC glad_glProvokingVertex; #define glProvokingVertex glad_glProvokingVertex GLAD_API_CALL PFNGLPUSHATTRIBPROC glad_glPushAttrib; #define glPushAttrib glad_glPushAttrib GLAD_API_CALL PFNGLPUSHCLIENTATTRIBPROC glad_glPushClientAttrib; #define glPushClientAttrib glad_glPushClientAttrib GLAD_API_CALL PFNGLPUSHDEBUGGROUPPROC glad_glPushDebugGroup; #define glPushDebugGroup glad_glPushDebugGroup GLAD_API_CALL PFNGLPUSHMATRIXPROC glad_glPushMatrix; #define glPushMatrix glad_glPushMatrix GLAD_API_CALL PFNGLPUSHNAMEPROC glad_glPushName; #define glPushName glad_glPushName GLAD_API_CALL PFNGLQUERYCOUNTERPROC glad_glQueryCounter; #define glQueryCounter glad_glQueryCounter GLAD_API_CALL PFNGLRASTERPOS2DPROC glad_glRasterPos2d; #define glRasterPos2d glad_glRasterPos2d GLAD_API_CALL PFNGLRASTERPOS2DVPROC glad_glRasterPos2dv; #define glRasterPos2dv glad_glRasterPos2dv GLAD_API_CALL PFNGLRASTERPOS2FPROC glad_glRasterPos2f; #define glRasterPos2f glad_glRasterPos2f GLAD_API_CALL PFNGLRASTERPOS2FVPROC glad_glRasterPos2fv; #define glRasterPos2fv glad_glRasterPos2fv GLAD_API_CALL PFNGLRASTERPOS2IPROC glad_glRasterPos2i; #define glRasterPos2i glad_glRasterPos2i GLAD_API_CALL PFNGLRASTERPOS2IVPROC glad_glRasterPos2iv; #define glRasterPos2iv glad_glRasterPos2iv GLAD_API_CALL PFNGLRASTERPOS2SPROC glad_glRasterPos2s; #define glRasterPos2s glad_glRasterPos2s GLAD_API_CALL PFNGLRASTERPOS2SVPROC glad_glRasterPos2sv; #define glRasterPos2sv glad_glRasterPos2sv GLAD_API_CALL PFNGLRASTERPOS3DPROC glad_glRasterPos3d; #define glRasterPos3d glad_glRasterPos3d GLAD_API_CALL PFNGLRASTERPOS3DVPROC glad_glRasterPos3dv; #define glRasterPos3dv glad_glRasterPos3dv GLAD_API_CALL PFNGLRASTERPOS3FPROC glad_glRasterPos3f; #define glRasterPos3f glad_glRasterPos3f GLAD_API_CALL PFNGLRASTERPOS3FVPROC glad_glRasterPos3fv; #define glRasterPos3fv glad_glRasterPos3fv GLAD_API_CALL PFNGLRASTERPOS3IPROC glad_glRasterPos3i; #define glRasterPos3i glad_glRasterPos3i GLAD_API_CALL PFNGLRASTERPOS3IVPROC glad_glRasterPos3iv; #define glRasterPos3iv glad_glRasterPos3iv GLAD_API_CALL PFNGLRASTERPOS3SPROC glad_glRasterPos3s; #define glRasterPos3s glad_glRasterPos3s GLAD_API_CALL PFNGLRASTERPOS3SVPROC glad_glRasterPos3sv; #define glRasterPos3sv glad_glRasterPos3sv GLAD_API_CALL PFNGLRASTERPOS4DPROC glad_glRasterPos4d; #define glRasterPos4d glad_glRasterPos4d GLAD_API_CALL PFNGLRASTERPOS4DVPROC glad_glRasterPos4dv; #define glRasterPos4dv glad_glRasterPos4dv GLAD_API_CALL PFNGLRASTERPOS4FPROC glad_glRasterPos4f; #define glRasterPos4f glad_glRasterPos4f GLAD_API_CALL PFNGLRASTERPOS4FVPROC glad_glRasterPos4fv; #define glRasterPos4fv glad_glRasterPos4fv GLAD_API_CALL PFNGLRASTERPOS4IPROC glad_glRasterPos4i; #define glRasterPos4i glad_glRasterPos4i GLAD_API_CALL PFNGLRASTERPOS4IVPROC glad_glRasterPos4iv; #define glRasterPos4iv glad_glRasterPos4iv GLAD_API_CALL PFNGLRASTERPOS4SPROC glad_glRasterPos4s; #define glRasterPos4s glad_glRasterPos4s GLAD_API_CALL PFNGLRASTERPOS4SVPROC glad_glRasterPos4sv; #define glRasterPos4sv glad_glRasterPos4sv GLAD_API_CALL PFNGLREADBUFFERPROC glad_glReadBuffer; #define glReadBuffer glad_glReadBuffer GLAD_API_CALL PFNGLREADPIXELSPROC glad_glReadPixels; #define glReadPixels glad_glReadPixels GLAD_API_CALL PFNGLREADNPIXELSPROC glad_glReadnPixels; #define glReadnPixels glad_glReadnPixels GLAD_API_CALL PFNGLREADNPIXELSARBPROC glad_glReadnPixelsARB; #define glReadnPixelsARB glad_glReadnPixelsARB GLAD_API_CALL PFNGLRECTDPROC glad_glRectd; #define glRectd glad_glRectd GLAD_API_CALL PFNGLRECTDVPROC glad_glRectdv; #define glRectdv glad_glRectdv GLAD_API_CALL PFNGLRECTFPROC glad_glRectf; #define glRectf glad_glRectf GLAD_API_CALL PFNGLRECTFVPROC glad_glRectfv; #define glRectfv glad_glRectfv GLAD_API_CALL PFNGLRECTIPROC glad_glRecti; #define glRecti glad_glRecti GLAD_API_CALL PFNGLRECTIVPROC glad_glRectiv; #define glRectiv glad_glRectiv GLAD_API_CALL PFNGLRECTSPROC glad_glRects; #define glRects glad_glRects GLAD_API_CALL PFNGLRECTSVPROC glad_glRectsv; #define glRectsv glad_glRectsv GLAD_API_CALL PFNGLRELEASESHADERCOMPILERPROC glad_glReleaseShaderCompiler; #define glReleaseShaderCompiler glad_glReleaseShaderCompiler GLAD_API_CALL PFNGLRENDERMODEPROC glad_glRenderMode; #define glRenderMode glad_glRenderMode GLAD_API_CALL PFNGLRENDERBUFFERSTORAGEPROC glad_glRenderbufferStorage; #define glRenderbufferStorage glad_glRenderbufferStorage GLAD_API_CALL PFNGLRENDERBUFFERSTORAGEMULTISAMPLEPROC glad_glRenderbufferStorageMultisample; #define glRenderbufferStorageMultisample glad_glRenderbufferStorageMultisample GLAD_API_CALL PFNGLRESETHISTOGRAMPROC glad_glResetHistogram; #define glResetHistogram glad_glResetHistogram GLAD_API_CALL PFNGLRESETMINMAXPROC glad_glResetMinmax; #define glResetMinmax glad_glResetMinmax GLAD_API_CALL PFNGLRESUMETRANSFORMFEEDBACKPROC glad_glResumeTransformFeedback; #define glResumeTransformFeedback glad_glResumeTransformFeedback GLAD_API_CALL PFNGLROTATEDPROC glad_glRotated; #define glRotated glad_glRotated GLAD_API_CALL PFNGLROTATEFPROC glad_glRotatef; #define glRotatef glad_glRotatef GLAD_API_CALL PFNGLSAMPLECOVERAGEPROC glad_glSampleCoverage; #define glSampleCoverage glad_glSampleCoverage GLAD_API_CALL PFNGLSAMPLECOVERAGEARBPROC glad_glSampleCoverageARB; #define glSampleCoverageARB glad_glSampleCoverageARB GLAD_API_CALL PFNGLSAMPLEMASKIPROC glad_glSampleMaski; #define glSampleMaski glad_glSampleMaski GLAD_API_CALL PFNGLSAMPLERPARAMETERIIVPROC glad_glSamplerParameterIiv; #define glSamplerParameterIiv glad_glSamplerParameterIiv GLAD_API_CALL PFNGLSAMPLERPARAMETERIUIVPROC glad_glSamplerParameterIuiv; #define glSamplerParameterIuiv glad_glSamplerParameterIuiv GLAD_API_CALL PFNGLSAMPLERPARAMETERFPROC glad_glSamplerParameterf; #define glSamplerParameterf glad_glSamplerParameterf GLAD_API_CALL PFNGLSAMPLERPARAMETERFVPROC glad_glSamplerParameterfv; #define glSamplerParameterfv glad_glSamplerParameterfv GLAD_API_CALL PFNGLSAMPLERPARAMETERIPROC glad_glSamplerParameteri; #define glSamplerParameteri glad_glSamplerParameteri GLAD_API_CALL PFNGLSAMPLERPARAMETERIVPROC glad_glSamplerParameteriv; #define glSamplerParameteriv glad_glSamplerParameteriv GLAD_API_CALL PFNGLSCALEDPROC glad_glScaled; #define glScaled glad_glScaled GLAD_API_CALL PFNGLSCALEFPROC glad_glScalef; #define glScalef glad_glScalef GLAD_API_CALL PFNGLSCISSORPROC glad_glScissor; #define glScissor glad_glScissor GLAD_API_CALL PFNGLSCISSORARRAYVPROC glad_glScissorArrayv; #define glScissorArrayv glad_glScissorArrayv GLAD_API_CALL PFNGLSCISSORINDEXEDPROC glad_glScissorIndexed; #define glScissorIndexed glad_glScissorIndexed GLAD_API_CALL PFNGLSCISSORINDEXEDVPROC glad_glScissorIndexedv; #define glScissorIndexedv glad_glScissorIndexedv GLAD_API_CALL PFNGLSECONDARYCOLOR3BPROC glad_glSecondaryColor3b; #define glSecondaryColor3b glad_glSecondaryColor3b GLAD_API_CALL PFNGLSECONDARYCOLOR3BVPROC glad_glSecondaryColor3bv; #define glSecondaryColor3bv glad_glSecondaryColor3bv GLAD_API_CALL PFNGLSECONDARYCOLOR3DPROC glad_glSecondaryColor3d; #define glSecondaryColor3d glad_glSecondaryColor3d GLAD_API_CALL PFNGLSECONDARYCOLOR3DVPROC glad_glSecondaryColor3dv; #define glSecondaryColor3dv glad_glSecondaryColor3dv GLAD_API_CALL PFNGLSECONDARYCOLOR3FPROC glad_glSecondaryColor3f; #define glSecondaryColor3f glad_glSecondaryColor3f GLAD_API_CALL PFNGLSECONDARYCOLOR3FVPROC glad_glSecondaryColor3fv; #define glSecondaryColor3fv glad_glSecondaryColor3fv GLAD_API_CALL PFNGLSECONDARYCOLOR3IPROC glad_glSecondaryColor3i; #define glSecondaryColor3i glad_glSecondaryColor3i GLAD_API_CALL PFNGLSECONDARYCOLOR3IVPROC glad_glSecondaryColor3iv; #define glSecondaryColor3iv glad_glSecondaryColor3iv GLAD_API_CALL PFNGLSECONDARYCOLOR3SPROC glad_glSecondaryColor3s; #define glSecondaryColor3s glad_glSecondaryColor3s GLAD_API_CALL PFNGLSECONDARYCOLOR3SVPROC glad_glSecondaryColor3sv; #define glSecondaryColor3sv glad_glSecondaryColor3sv GLAD_API_CALL PFNGLSECONDARYCOLOR3UBPROC glad_glSecondaryColor3ub; #define glSecondaryColor3ub glad_glSecondaryColor3ub GLAD_API_CALL PFNGLSECONDARYCOLOR3UBVPROC glad_glSecondaryColor3ubv; #define glSecondaryColor3ubv glad_glSecondaryColor3ubv GLAD_API_CALL PFNGLSECONDARYCOLOR3UIPROC glad_glSecondaryColor3ui; #define glSecondaryColor3ui glad_glSecondaryColor3ui GLAD_API_CALL PFNGLSECONDARYCOLOR3UIVPROC glad_glSecondaryColor3uiv; #define glSecondaryColor3uiv glad_glSecondaryColor3uiv GLAD_API_CALL PFNGLSECONDARYCOLOR3USPROC glad_glSecondaryColor3us; #define glSecondaryColor3us glad_glSecondaryColor3us GLAD_API_CALL PFNGLSECONDARYCOLOR3USVPROC glad_glSecondaryColor3usv; #define glSecondaryColor3usv glad_glSecondaryColor3usv GLAD_API_CALL PFNGLSECONDARYCOLORP3UIPROC glad_glSecondaryColorP3ui; #define glSecondaryColorP3ui glad_glSecondaryColorP3ui GLAD_API_CALL PFNGLSECONDARYCOLORP3UIVPROC glad_glSecondaryColorP3uiv; #define glSecondaryColorP3uiv glad_glSecondaryColorP3uiv GLAD_API_CALL PFNGLSECONDARYCOLORPOINTERPROC glad_glSecondaryColorPointer; #define glSecondaryColorPointer glad_glSecondaryColorPointer GLAD_API_CALL PFNGLSELECTBUFFERPROC glad_glSelectBuffer; #define glSelectBuffer glad_glSelectBuffer GLAD_API_CALL PFNGLSEPARABLEFILTER2DPROC glad_glSeparableFilter2D; #define glSeparableFilter2D glad_glSeparableFilter2D GLAD_API_CALL PFNGLSHADEMODELPROC glad_glShadeModel; #define glShadeModel glad_glShadeModel GLAD_API_CALL PFNGLSHADERBINARYPROC glad_glShaderBinary; #define glShaderBinary glad_glShaderBinary GLAD_API_CALL PFNGLSHADERSOURCEPROC glad_glShaderSource; #define glShaderSource glad_glShaderSource GLAD_API_CALL PFNGLSHADERSOURCEARBPROC glad_glShaderSourceARB; #define glShaderSourceARB glad_glShaderSourceARB GLAD_API_CALL PFNGLSHADERSTORAGEBLOCKBINDINGPROC glad_glShaderStorageBlockBinding; #define glShaderStorageBlockBinding glad_glShaderStorageBlockBinding GLAD_API_CALL PFNGLSPECIALIZESHADERPROC glad_glSpecializeShader; #define glSpecializeShader glad_glSpecializeShader GLAD_API_CALL PFNGLSPECIALIZESHADERARBPROC glad_glSpecializeShaderARB; #define glSpecializeShaderARB glad_glSpecializeShaderARB GLAD_API_CALL PFNGLSTENCILFUNCPROC glad_glStencilFunc; #define glStencilFunc glad_glStencilFunc GLAD_API_CALL PFNGLSTENCILFUNCSEPARATEPROC glad_glStencilFuncSeparate; #define glStencilFuncSeparate glad_glStencilFuncSeparate GLAD_API_CALL PFNGLSTENCILMASKPROC glad_glStencilMask; #define glStencilMask glad_glStencilMask GLAD_API_CALL PFNGLSTENCILMASKSEPARATEPROC glad_glStencilMaskSeparate; #define glStencilMaskSeparate glad_glStencilMaskSeparate GLAD_API_CALL PFNGLSTENCILOPPROC glad_glStencilOp; #define glStencilOp glad_glStencilOp GLAD_API_CALL PFNGLSTENCILOPSEPARATEPROC glad_glStencilOpSeparate; #define glStencilOpSeparate glad_glStencilOpSeparate GLAD_API_CALL PFNGLTEXBUFFERPROC glad_glTexBuffer; #define glTexBuffer glad_glTexBuffer GLAD_API_CALL PFNGLTEXBUFFERARBPROC glad_glTexBufferARB; #define glTexBufferARB glad_glTexBufferARB GLAD_API_CALL PFNGLTEXBUFFERRANGEPROC glad_glTexBufferRange; #define glTexBufferRange glad_glTexBufferRange GLAD_API_CALL PFNGLTEXCOORD1DPROC glad_glTexCoord1d; #define glTexCoord1d glad_glTexCoord1d GLAD_API_CALL PFNGLTEXCOORD1DVPROC glad_glTexCoord1dv; #define glTexCoord1dv glad_glTexCoord1dv GLAD_API_CALL PFNGLTEXCOORD1FPROC glad_glTexCoord1f; #define glTexCoord1f glad_glTexCoord1f GLAD_API_CALL PFNGLTEXCOORD1FVPROC glad_glTexCoord1fv; #define glTexCoord1fv glad_glTexCoord1fv GLAD_API_CALL PFNGLTEXCOORD1IPROC glad_glTexCoord1i; #define glTexCoord1i glad_glTexCoord1i GLAD_API_CALL PFNGLTEXCOORD1IVPROC glad_glTexCoord1iv; #define glTexCoord1iv glad_glTexCoord1iv GLAD_API_CALL PFNGLTEXCOORD1SPROC glad_glTexCoord1s; #define glTexCoord1s glad_glTexCoord1s GLAD_API_CALL PFNGLTEXCOORD1SVPROC glad_glTexCoord1sv; #define glTexCoord1sv glad_glTexCoord1sv GLAD_API_CALL PFNGLTEXCOORD2DPROC glad_glTexCoord2d; #define glTexCoord2d glad_glTexCoord2d GLAD_API_CALL PFNGLTEXCOORD2DVPROC glad_glTexCoord2dv; #define glTexCoord2dv glad_glTexCoord2dv GLAD_API_CALL PFNGLTEXCOORD2FPROC glad_glTexCoord2f; #define glTexCoord2f glad_glTexCoord2f GLAD_API_CALL PFNGLTEXCOORD2FVPROC glad_glTexCoord2fv; #define glTexCoord2fv glad_glTexCoord2fv GLAD_API_CALL PFNGLTEXCOORD2IPROC glad_glTexCoord2i; #define glTexCoord2i glad_glTexCoord2i GLAD_API_CALL PFNGLTEXCOORD2IVPROC glad_glTexCoord2iv; #define glTexCoord2iv glad_glTexCoord2iv GLAD_API_CALL PFNGLTEXCOORD2SPROC glad_glTexCoord2s; #define glTexCoord2s glad_glTexCoord2s GLAD_API_CALL PFNGLTEXCOORD2SVPROC glad_glTexCoord2sv; #define glTexCoord2sv glad_glTexCoord2sv GLAD_API_CALL PFNGLTEXCOORD3DPROC glad_glTexCoord3d; #define glTexCoord3d glad_glTexCoord3d GLAD_API_CALL PFNGLTEXCOORD3DVPROC glad_glTexCoord3dv; #define glTexCoord3dv glad_glTexCoord3dv GLAD_API_CALL PFNGLTEXCOORD3FPROC glad_glTexCoord3f; #define glTexCoord3f glad_glTexCoord3f GLAD_API_CALL PFNGLTEXCOORD3FVPROC glad_glTexCoord3fv; #define glTexCoord3fv glad_glTexCoord3fv GLAD_API_CALL PFNGLTEXCOORD3IPROC glad_glTexCoord3i; #define glTexCoord3i glad_glTexCoord3i GLAD_API_CALL PFNGLTEXCOORD3IVPROC glad_glTexCoord3iv; #define glTexCoord3iv glad_glTexCoord3iv GLAD_API_CALL PFNGLTEXCOORD3SPROC glad_glTexCoord3s; #define glTexCoord3s glad_glTexCoord3s GLAD_API_CALL PFNGLTEXCOORD3SVPROC glad_glTexCoord3sv; #define glTexCoord3sv glad_glTexCoord3sv GLAD_API_CALL PFNGLTEXCOORD4DPROC glad_glTexCoord4d; #define glTexCoord4d glad_glTexCoord4d GLAD_API_CALL PFNGLTEXCOORD4DVPROC glad_glTexCoord4dv; #define glTexCoord4dv glad_glTexCoord4dv GLAD_API_CALL PFNGLTEXCOORD4FPROC glad_glTexCoord4f; #define glTexCoord4f glad_glTexCoord4f GLAD_API_CALL PFNGLTEXCOORD4FVPROC glad_glTexCoord4fv; #define glTexCoord4fv glad_glTexCoord4fv GLAD_API_CALL PFNGLTEXCOORD4IPROC glad_glTexCoord4i; #define glTexCoord4i glad_glTexCoord4i GLAD_API_CALL PFNGLTEXCOORD4IVPROC glad_glTexCoord4iv; #define glTexCoord4iv glad_glTexCoord4iv GLAD_API_CALL PFNGLTEXCOORD4SPROC glad_glTexCoord4s; #define glTexCoord4s glad_glTexCoord4s GLAD_API_CALL PFNGLTEXCOORD4SVPROC glad_glTexCoord4sv; #define glTexCoord4sv glad_glTexCoord4sv GLAD_API_CALL PFNGLTEXCOORDP1UIPROC glad_glTexCoordP1ui; #define glTexCoordP1ui glad_glTexCoordP1ui GLAD_API_CALL PFNGLTEXCOORDP1UIVPROC glad_glTexCoordP1uiv; #define glTexCoordP1uiv glad_glTexCoordP1uiv GLAD_API_CALL PFNGLTEXCOORDP2UIPROC glad_glTexCoordP2ui; #define glTexCoordP2ui glad_glTexCoordP2ui GLAD_API_CALL PFNGLTEXCOORDP2UIVPROC glad_glTexCoordP2uiv; #define glTexCoordP2uiv glad_glTexCoordP2uiv GLAD_API_CALL PFNGLTEXCOORDP3UIPROC glad_glTexCoordP3ui; #define glTexCoordP3ui glad_glTexCoordP3ui GLAD_API_CALL PFNGLTEXCOORDP3UIVPROC glad_glTexCoordP3uiv; #define glTexCoordP3uiv glad_glTexCoordP3uiv GLAD_API_CALL PFNGLTEXCOORDP4UIPROC glad_glTexCoordP4ui; #define glTexCoordP4ui glad_glTexCoordP4ui GLAD_API_CALL PFNGLTEXCOORDP4UIVPROC glad_glTexCoordP4uiv; #define glTexCoordP4uiv glad_glTexCoordP4uiv GLAD_API_CALL PFNGLTEXCOORDPOINTERPROC glad_glTexCoordPointer; #define glTexCoordPointer glad_glTexCoordPointer GLAD_API_CALL PFNGLTEXENVFPROC glad_glTexEnvf; #define glTexEnvf glad_glTexEnvf GLAD_API_CALL PFNGLTEXENVFVPROC glad_glTexEnvfv; #define glTexEnvfv glad_glTexEnvfv GLAD_API_CALL PFNGLTEXENVIPROC glad_glTexEnvi; #define glTexEnvi glad_glTexEnvi GLAD_API_CALL PFNGLTEXENVIVPROC glad_glTexEnviv; #define glTexEnviv glad_glTexEnviv GLAD_API_CALL PFNGLTEXGENDPROC glad_glTexGend; #define glTexGend glad_glTexGend GLAD_API_CALL PFNGLTEXGENDVPROC glad_glTexGendv; #define glTexGendv glad_glTexGendv GLAD_API_CALL PFNGLTEXGENFPROC glad_glTexGenf; #define glTexGenf glad_glTexGenf GLAD_API_CALL PFNGLTEXGENFVPROC glad_glTexGenfv; #define glTexGenfv glad_glTexGenfv GLAD_API_CALL PFNGLTEXGENIPROC glad_glTexGeni; #define glTexGeni glad_glTexGeni GLAD_API_CALL PFNGLTEXGENIVPROC glad_glTexGeniv; #define glTexGeniv glad_glTexGeniv GLAD_API_CALL PFNGLTEXIMAGE1DPROC glad_glTexImage1D; #define glTexImage1D glad_glTexImage1D GLAD_API_CALL PFNGLTEXIMAGE2DPROC glad_glTexImage2D; #define glTexImage2D glad_glTexImage2D GLAD_API_CALL PFNGLTEXIMAGE2DMULTISAMPLEPROC glad_glTexImage2DMultisample; #define glTexImage2DMultisample glad_glTexImage2DMultisample GLAD_API_CALL PFNGLTEXIMAGE3DPROC glad_glTexImage3D; #define glTexImage3D glad_glTexImage3D GLAD_API_CALL PFNGLTEXIMAGE3DMULTISAMPLEPROC glad_glTexImage3DMultisample; #define glTexImage3DMultisample glad_glTexImage3DMultisample GLAD_API_CALL PFNGLTEXPAGECOMMITMENTARBPROC glad_glTexPageCommitmentARB; #define glTexPageCommitmentARB glad_glTexPageCommitmentARB GLAD_API_CALL PFNGLTEXPARAMETERIIVPROC glad_glTexParameterIiv; #define glTexParameterIiv glad_glTexParameterIiv GLAD_API_CALL PFNGLTEXPARAMETERIUIVPROC glad_glTexParameterIuiv; #define glTexParameterIuiv glad_glTexParameterIuiv GLAD_API_CALL PFNGLTEXPARAMETERFPROC glad_glTexParameterf; #define glTexParameterf glad_glTexParameterf GLAD_API_CALL PFNGLTEXPARAMETERFVPROC glad_glTexParameterfv; #define glTexParameterfv glad_glTexParameterfv GLAD_API_CALL PFNGLTEXPARAMETERIPROC glad_glTexParameteri; #define glTexParameteri glad_glTexParameteri GLAD_API_CALL PFNGLTEXPARAMETERIVPROC glad_glTexParameteriv; #define glTexParameteriv glad_glTexParameteriv GLAD_API_CALL PFNGLTEXSTORAGE1DPROC glad_glTexStorage1D; #define glTexStorage1D glad_glTexStorage1D GLAD_API_CALL PFNGLTEXSTORAGE2DPROC glad_glTexStorage2D; #define glTexStorage2D glad_glTexStorage2D GLAD_API_CALL PFNGLTEXSTORAGE2DMULTISAMPLEPROC glad_glTexStorage2DMultisample; #define glTexStorage2DMultisample glad_glTexStorage2DMultisample GLAD_API_CALL PFNGLTEXSTORAGE3DPROC glad_glTexStorage3D; #define glTexStorage3D glad_glTexStorage3D GLAD_API_CALL PFNGLTEXSTORAGE3DMULTISAMPLEPROC glad_glTexStorage3DMultisample; #define glTexStorage3DMultisample glad_glTexStorage3DMultisample GLAD_API_CALL PFNGLTEXSUBIMAGE1DPROC glad_glTexSubImage1D; #define glTexSubImage1D glad_glTexSubImage1D GLAD_API_CALL PFNGLTEXSUBIMAGE2DPROC glad_glTexSubImage2D; #define glTexSubImage2D glad_glTexSubImage2D GLAD_API_CALL PFNGLTEXSUBIMAGE3DPROC glad_glTexSubImage3D; #define glTexSubImage3D glad_glTexSubImage3D GLAD_API_CALL PFNGLTEXTUREBARRIERPROC glad_glTextureBarrier; #define glTextureBarrier glad_glTextureBarrier GLAD_API_CALL PFNGLTEXTUREBUFFERPROC glad_glTextureBuffer; #define glTextureBuffer glad_glTextureBuffer GLAD_API_CALL PFNGLTEXTUREBUFFERRANGEPROC glad_glTextureBufferRange; #define glTextureBufferRange glad_glTextureBufferRange GLAD_API_CALL PFNGLTEXTUREPARAMETERIIVPROC glad_glTextureParameterIiv; #define glTextureParameterIiv glad_glTextureParameterIiv GLAD_API_CALL PFNGLTEXTUREPARAMETERIUIVPROC glad_glTextureParameterIuiv; #define glTextureParameterIuiv glad_glTextureParameterIuiv GLAD_API_CALL PFNGLTEXTUREPARAMETERFPROC glad_glTextureParameterf; #define glTextureParameterf glad_glTextureParameterf GLAD_API_CALL PFNGLTEXTUREPARAMETERFVPROC glad_glTextureParameterfv; #define glTextureParameterfv glad_glTextureParameterfv GLAD_API_CALL PFNGLTEXTUREPARAMETERIPROC glad_glTextureParameteri; #define glTextureParameteri glad_glTextureParameteri GLAD_API_CALL PFNGLTEXTUREPARAMETERIVPROC glad_glTextureParameteriv; #define glTextureParameteriv glad_glTextureParameteriv GLAD_API_CALL PFNGLTEXTURESTORAGE1DPROC glad_glTextureStorage1D; #define glTextureStorage1D glad_glTextureStorage1D GLAD_API_CALL PFNGLTEXTURESTORAGE2DPROC glad_glTextureStorage2D; #define glTextureStorage2D glad_glTextureStorage2D GLAD_API_CALL PFNGLTEXTURESTORAGE2DMULTISAMPLEPROC glad_glTextureStorage2DMultisample; #define glTextureStorage2DMultisample glad_glTextureStorage2DMultisample GLAD_API_CALL PFNGLTEXTURESTORAGE3DPROC glad_glTextureStorage3D; #define glTextureStorage3D glad_glTextureStorage3D GLAD_API_CALL PFNGLTEXTURESTORAGE3DMULTISAMPLEPROC glad_glTextureStorage3DMultisample; #define glTextureStorage3DMultisample glad_glTextureStorage3DMultisample GLAD_API_CALL PFNGLTEXTURESUBIMAGE1DPROC glad_glTextureSubImage1D; #define glTextureSubImage1D glad_glTextureSubImage1D GLAD_API_CALL PFNGLTEXTURESUBIMAGE2DPROC glad_glTextureSubImage2D; #define glTextureSubImage2D glad_glTextureSubImage2D GLAD_API_CALL PFNGLTEXTURESUBIMAGE3DPROC glad_glTextureSubImage3D; #define glTextureSubImage3D glad_glTextureSubImage3D GLAD_API_CALL PFNGLTEXTUREVIEWPROC glad_glTextureView; #define glTextureView glad_glTextureView GLAD_API_CALL PFNGLTRANSFORMFEEDBACKBUFFERBASEPROC glad_glTransformFeedbackBufferBase; #define glTransformFeedbackBufferBase glad_glTransformFeedbackBufferBase GLAD_API_CALL PFNGLTRANSFORMFEEDBACKBUFFERRANGEPROC glad_glTransformFeedbackBufferRange; #define glTransformFeedbackBufferRange glad_glTransformFeedbackBufferRange GLAD_API_CALL PFNGLTRANSFORMFEEDBACKVARYINGSPROC glad_glTransformFeedbackVaryings; #define glTransformFeedbackVaryings glad_glTransformFeedbackVaryings GLAD_API_CALL PFNGLTRANSLATEDPROC glad_glTranslated; #define glTranslated glad_glTranslated GLAD_API_CALL PFNGLTRANSLATEFPROC glad_glTranslatef; #define glTranslatef glad_glTranslatef GLAD_API_CALL PFNGLUNIFORM1DPROC glad_glUniform1d; #define glUniform1d glad_glUniform1d GLAD_API_CALL PFNGLUNIFORM1DVPROC glad_glUniform1dv; #define glUniform1dv glad_glUniform1dv GLAD_API_CALL PFNGLUNIFORM1FPROC glad_glUniform1f; #define glUniform1f glad_glUniform1f GLAD_API_CALL PFNGLUNIFORM1FARBPROC glad_glUniform1fARB; #define glUniform1fARB glad_glUniform1fARB GLAD_API_CALL PFNGLUNIFORM1FVPROC glad_glUniform1fv; #define glUniform1fv glad_glUniform1fv GLAD_API_CALL PFNGLUNIFORM1FVARBPROC glad_glUniform1fvARB; #define glUniform1fvARB glad_glUniform1fvARB GLAD_API_CALL PFNGLUNIFORM1IPROC glad_glUniform1i; #define glUniform1i glad_glUniform1i GLAD_API_CALL PFNGLUNIFORM1I64ARBPROC glad_glUniform1i64ARB; #define glUniform1i64ARB glad_glUniform1i64ARB GLAD_API_CALL PFNGLUNIFORM1I64VARBPROC glad_glUniform1i64vARB; #define glUniform1i64vARB glad_glUniform1i64vARB GLAD_API_CALL PFNGLUNIFORM1IARBPROC glad_glUniform1iARB; #define glUniform1iARB glad_glUniform1iARB GLAD_API_CALL PFNGLUNIFORM1IVPROC glad_glUniform1iv; #define glUniform1iv glad_glUniform1iv GLAD_API_CALL PFNGLUNIFORM1IVARBPROC glad_glUniform1ivARB; #define glUniform1ivARB glad_glUniform1ivARB GLAD_API_CALL PFNGLUNIFORM1UIPROC glad_glUniform1ui; #define glUniform1ui glad_glUniform1ui GLAD_API_CALL PFNGLUNIFORM1UI64ARBPROC glad_glUniform1ui64ARB; #define glUniform1ui64ARB glad_glUniform1ui64ARB GLAD_API_CALL PFNGLUNIFORM1UI64VARBPROC glad_glUniform1ui64vARB; #define glUniform1ui64vARB glad_glUniform1ui64vARB GLAD_API_CALL PFNGLUNIFORM1UIVPROC glad_glUniform1uiv; #define glUniform1uiv glad_glUniform1uiv GLAD_API_CALL PFNGLUNIFORM2DPROC glad_glUniform2d; #define glUniform2d glad_glUniform2d GLAD_API_CALL PFNGLUNIFORM2DVPROC glad_glUniform2dv; #define glUniform2dv glad_glUniform2dv GLAD_API_CALL PFNGLUNIFORM2FPROC glad_glUniform2f; #define glUniform2f glad_glUniform2f GLAD_API_CALL PFNGLUNIFORM2FARBPROC glad_glUniform2fARB; #define glUniform2fARB glad_glUniform2fARB GLAD_API_CALL PFNGLUNIFORM2FVPROC glad_glUniform2fv; #define glUniform2fv glad_glUniform2fv GLAD_API_CALL PFNGLUNIFORM2FVARBPROC glad_glUniform2fvARB; #define glUniform2fvARB glad_glUniform2fvARB GLAD_API_CALL PFNGLUNIFORM2IPROC glad_glUniform2i; #define glUniform2i glad_glUniform2i GLAD_API_CALL PFNGLUNIFORM2I64ARBPROC glad_glUniform2i64ARB; #define glUniform2i64ARB glad_glUniform2i64ARB GLAD_API_CALL PFNGLUNIFORM2I64VARBPROC glad_glUniform2i64vARB; #define glUniform2i64vARB glad_glUniform2i64vARB GLAD_API_CALL PFNGLUNIFORM2IARBPROC glad_glUniform2iARB; #define glUniform2iARB glad_glUniform2iARB GLAD_API_CALL PFNGLUNIFORM2IVPROC glad_glUniform2iv; #define glUniform2iv glad_glUniform2iv GLAD_API_CALL PFNGLUNIFORM2IVARBPROC glad_glUniform2ivARB; #define glUniform2ivARB glad_glUniform2ivARB GLAD_API_CALL PFNGLUNIFORM2UIPROC glad_glUniform2ui; #define glUniform2ui glad_glUniform2ui GLAD_API_CALL PFNGLUNIFORM2UI64ARBPROC glad_glUniform2ui64ARB; #define glUniform2ui64ARB glad_glUniform2ui64ARB GLAD_API_CALL PFNGLUNIFORM2UI64VARBPROC glad_glUniform2ui64vARB; #define glUniform2ui64vARB glad_glUniform2ui64vARB GLAD_API_CALL PFNGLUNIFORM2UIVPROC glad_glUniform2uiv; #define glUniform2uiv glad_glUniform2uiv GLAD_API_CALL PFNGLUNIFORM3DPROC glad_glUniform3d; #define glUniform3d glad_glUniform3d GLAD_API_CALL PFNGLUNIFORM3DVPROC glad_glUniform3dv; #define glUniform3dv glad_glUniform3dv GLAD_API_CALL PFNGLUNIFORM3FPROC glad_glUniform3f; #define glUniform3f glad_glUniform3f GLAD_API_CALL PFNGLUNIFORM3FARBPROC glad_glUniform3fARB; #define glUniform3fARB glad_glUniform3fARB GLAD_API_CALL PFNGLUNIFORM3FVPROC glad_glUniform3fv; #define glUniform3fv glad_glUniform3fv GLAD_API_CALL PFNGLUNIFORM3FVARBPROC glad_glUniform3fvARB; #define glUniform3fvARB glad_glUniform3fvARB GLAD_API_CALL PFNGLUNIFORM3IPROC glad_glUniform3i; #define glUniform3i glad_glUniform3i GLAD_API_CALL PFNGLUNIFORM3I64ARBPROC glad_glUniform3i64ARB; #define glUniform3i64ARB glad_glUniform3i64ARB GLAD_API_CALL PFNGLUNIFORM3I64VARBPROC glad_glUniform3i64vARB; #define glUniform3i64vARB glad_glUniform3i64vARB GLAD_API_CALL PFNGLUNIFORM3IARBPROC glad_glUniform3iARB; #define glUniform3iARB glad_glUniform3iARB GLAD_API_CALL PFNGLUNIFORM3IVPROC glad_glUniform3iv; #define glUniform3iv glad_glUniform3iv GLAD_API_CALL PFNGLUNIFORM3IVARBPROC glad_glUniform3ivARB; #define glUniform3ivARB glad_glUniform3ivARB GLAD_API_CALL PFNGLUNIFORM3UIPROC glad_glUniform3ui; #define glUniform3ui glad_glUniform3ui GLAD_API_CALL PFNGLUNIFORM3UI64ARBPROC glad_glUniform3ui64ARB; #define glUniform3ui64ARB glad_glUniform3ui64ARB GLAD_API_CALL PFNGLUNIFORM3UI64VARBPROC glad_glUniform3ui64vARB; #define glUniform3ui64vARB glad_glUniform3ui64vARB GLAD_API_CALL PFNGLUNIFORM3UIVPROC glad_glUniform3uiv; #define glUniform3uiv glad_glUniform3uiv GLAD_API_CALL PFNGLUNIFORM4DPROC glad_glUniform4d; #define glUniform4d glad_glUniform4d GLAD_API_CALL PFNGLUNIFORM4DVPROC glad_glUniform4dv; #define glUniform4dv glad_glUniform4dv GLAD_API_CALL PFNGLUNIFORM4FPROC glad_glUniform4f; #define glUniform4f glad_glUniform4f GLAD_API_CALL PFNGLUNIFORM4FARBPROC glad_glUniform4fARB; #define glUniform4fARB glad_glUniform4fARB GLAD_API_CALL PFNGLUNIFORM4FVPROC glad_glUniform4fv; #define glUniform4fv glad_glUniform4fv GLAD_API_CALL PFNGLUNIFORM4FVARBPROC glad_glUniform4fvARB; #define glUniform4fvARB glad_glUniform4fvARB GLAD_API_CALL PFNGLUNIFORM4IPROC glad_glUniform4i; #define glUniform4i glad_glUniform4i GLAD_API_CALL PFNGLUNIFORM4I64ARBPROC glad_glUniform4i64ARB; #define glUniform4i64ARB glad_glUniform4i64ARB GLAD_API_CALL PFNGLUNIFORM4I64VARBPROC glad_glUniform4i64vARB; #define glUniform4i64vARB glad_glUniform4i64vARB GLAD_API_CALL PFNGLUNIFORM4IARBPROC glad_glUniform4iARB; #define glUniform4iARB glad_glUniform4iARB GLAD_API_CALL PFNGLUNIFORM4IVPROC glad_glUniform4iv; #define glUniform4iv glad_glUniform4iv GLAD_API_CALL PFNGLUNIFORM4IVARBPROC glad_glUniform4ivARB; #define glUniform4ivARB glad_glUniform4ivARB GLAD_API_CALL PFNGLUNIFORM4UIPROC glad_glUniform4ui; #define glUniform4ui glad_glUniform4ui GLAD_API_CALL PFNGLUNIFORM4UI64ARBPROC glad_glUniform4ui64ARB; #define glUniform4ui64ARB glad_glUniform4ui64ARB GLAD_API_CALL PFNGLUNIFORM4UI64VARBPROC glad_glUniform4ui64vARB; #define glUniform4ui64vARB glad_glUniform4ui64vARB GLAD_API_CALL PFNGLUNIFORM4UIVPROC glad_glUniform4uiv; #define glUniform4uiv glad_glUniform4uiv GLAD_API_CALL PFNGLUNIFORMBLOCKBINDINGPROC glad_glUniformBlockBinding; #define glUniformBlockBinding glad_glUniformBlockBinding GLAD_API_CALL PFNGLUNIFORMHANDLEUI64ARBPROC glad_glUniformHandleui64ARB; #define glUniformHandleui64ARB glad_glUniformHandleui64ARB GLAD_API_CALL PFNGLUNIFORMHANDLEUI64VARBPROC glad_glUniformHandleui64vARB; #define glUniformHandleui64vARB glad_glUniformHandleui64vARB GLAD_API_CALL PFNGLUNIFORMMATRIX2DVPROC glad_glUniformMatrix2dv; #define glUniformMatrix2dv glad_glUniformMatrix2dv GLAD_API_CALL PFNGLUNIFORMMATRIX2FVPROC glad_glUniformMatrix2fv; #define glUniformMatrix2fv glad_glUniformMatrix2fv GLAD_API_CALL PFNGLUNIFORMMATRIX2FVARBPROC glad_glUniformMatrix2fvARB; #define glUniformMatrix2fvARB glad_glUniformMatrix2fvARB GLAD_API_CALL PFNGLUNIFORMMATRIX2X3DVPROC glad_glUniformMatrix2x3dv; #define glUniformMatrix2x3dv glad_glUniformMatrix2x3dv GLAD_API_CALL PFNGLUNIFORMMATRIX2X3FVPROC glad_glUniformMatrix2x3fv; #define glUniformMatrix2x3fv glad_glUniformMatrix2x3fv GLAD_API_CALL PFNGLUNIFORMMATRIX2X4DVPROC glad_glUniformMatrix2x4dv; #define glUniformMatrix2x4dv glad_glUniformMatrix2x4dv GLAD_API_CALL PFNGLUNIFORMMATRIX2X4FVPROC glad_glUniformMatrix2x4fv; #define glUniformMatrix2x4fv glad_glUniformMatrix2x4fv GLAD_API_CALL PFNGLUNIFORMMATRIX3DVPROC glad_glUniformMatrix3dv; #define glUniformMatrix3dv glad_glUniformMatrix3dv GLAD_API_CALL PFNGLUNIFORMMATRIX3FVPROC glad_glUniformMatrix3fv; #define glUniformMatrix3fv glad_glUniformMatrix3fv GLAD_API_CALL PFNGLUNIFORMMATRIX3FVARBPROC glad_glUniformMatrix3fvARB; #define glUniformMatrix3fvARB glad_glUniformMatrix3fvARB GLAD_API_CALL PFNGLUNIFORMMATRIX3X2DVPROC glad_glUniformMatrix3x2dv; #define glUniformMatrix3x2dv glad_glUniformMatrix3x2dv GLAD_API_CALL PFNGLUNIFORMMATRIX3X2FVPROC glad_glUniformMatrix3x2fv; #define glUniformMatrix3x2fv glad_glUniformMatrix3x2fv GLAD_API_CALL PFNGLUNIFORMMATRIX3X4DVPROC glad_glUniformMatrix3x4dv; #define glUniformMatrix3x4dv glad_glUniformMatrix3x4dv GLAD_API_CALL PFNGLUNIFORMMATRIX3X4FVPROC glad_glUniformMatrix3x4fv; #define glUniformMatrix3x4fv glad_glUniformMatrix3x4fv GLAD_API_CALL PFNGLUNIFORMMATRIX4DVPROC glad_glUniformMatrix4dv; #define glUniformMatrix4dv glad_glUniformMatrix4dv GLAD_API_CALL PFNGLUNIFORMMATRIX4FVPROC glad_glUniformMatrix4fv; #define glUniformMatrix4fv glad_glUniformMatrix4fv GLAD_API_CALL PFNGLUNIFORMMATRIX4FVARBPROC glad_glUniformMatrix4fvARB; #define glUniformMatrix4fvARB glad_glUniformMatrix4fvARB GLAD_API_CALL PFNGLUNIFORMMATRIX4X2DVPROC glad_glUniformMatrix4x2dv; #define glUniformMatrix4x2dv glad_glUniformMatrix4x2dv GLAD_API_CALL PFNGLUNIFORMMATRIX4X2FVPROC glad_glUniformMatrix4x2fv; #define glUniformMatrix4x2fv glad_glUniformMatrix4x2fv GLAD_API_CALL PFNGLUNIFORMMATRIX4X3DVPROC glad_glUniformMatrix4x3dv; #define glUniformMatrix4x3dv glad_glUniformMatrix4x3dv GLAD_API_CALL PFNGLUNIFORMMATRIX4X3FVPROC glad_glUniformMatrix4x3fv; #define glUniformMatrix4x3fv glad_glUniformMatrix4x3fv GLAD_API_CALL PFNGLUNIFORMSUBROUTINESUIVPROC glad_glUniformSubroutinesuiv; #define glUniformSubroutinesuiv glad_glUniformSubroutinesuiv GLAD_API_CALL PFNGLUNMAPBUFFERPROC glad_glUnmapBuffer; #define glUnmapBuffer glad_glUnmapBuffer GLAD_API_CALL PFNGLUNMAPBUFFERARBPROC glad_glUnmapBufferARB; #define glUnmapBufferARB glad_glUnmapBufferARB GLAD_API_CALL PFNGLUNMAPNAMEDBUFFERPROC glad_glUnmapNamedBuffer; #define glUnmapNamedBuffer glad_glUnmapNamedBuffer GLAD_API_CALL PFNGLUSEPROGRAMPROC glad_glUseProgram; #define glUseProgram glad_glUseProgram GLAD_API_CALL PFNGLUSEPROGRAMOBJECTARBPROC glad_glUseProgramObjectARB; #define glUseProgramObjectARB glad_glUseProgramObjectARB GLAD_API_CALL PFNGLUSEPROGRAMSTAGESPROC glad_glUseProgramStages; #define glUseProgramStages glad_glUseProgramStages GLAD_API_CALL PFNGLVALIDATEPROGRAMPROC glad_glValidateProgram; #define glValidateProgram glad_glValidateProgram GLAD_API_CALL PFNGLVALIDATEPROGRAMARBPROC glad_glValidateProgramARB; #define glValidateProgramARB glad_glValidateProgramARB GLAD_API_CALL PFNGLVALIDATEPROGRAMPIPELINEPROC glad_glValidateProgramPipeline; #define glValidateProgramPipeline glad_glValidateProgramPipeline GLAD_API_CALL PFNGLVERTEX2DPROC glad_glVertex2d; #define glVertex2d glad_glVertex2d GLAD_API_CALL PFNGLVERTEX2DVPROC glad_glVertex2dv; #define glVertex2dv glad_glVertex2dv GLAD_API_CALL PFNGLVERTEX2FPROC glad_glVertex2f; #define glVertex2f glad_glVertex2f GLAD_API_CALL PFNGLVERTEX2FVPROC glad_glVertex2fv; #define glVertex2fv glad_glVertex2fv GLAD_API_CALL PFNGLVERTEX2IPROC glad_glVertex2i; #define glVertex2i glad_glVertex2i GLAD_API_CALL PFNGLVERTEX2IVPROC glad_glVertex2iv; #define glVertex2iv glad_glVertex2iv GLAD_API_CALL PFNGLVERTEX2SPROC glad_glVertex2s; #define glVertex2s glad_glVertex2s GLAD_API_CALL PFNGLVERTEX2SVPROC glad_glVertex2sv; #define glVertex2sv glad_glVertex2sv GLAD_API_CALL PFNGLVERTEX3DPROC glad_glVertex3d; #define glVertex3d glad_glVertex3d GLAD_API_CALL PFNGLVERTEX3DVPROC glad_glVertex3dv; #define glVertex3dv glad_glVertex3dv GLAD_API_CALL PFNGLVERTEX3FPROC glad_glVertex3f; #define glVertex3f glad_glVertex3f GLAD_API_CALL PFNGLVERTEX3FVPROC glad_glVertex3fv; #define glVertex3fv glad_glVertex3fv GLAD_API_CALL PFNGLVERTEX3IPROC glad_glVertex3i; #define glVertex3i glad_glVertex3i GLAD_API_CALL PFNGLVERTEX3IVPROC glad_glVertex3iv; #define glVertex3iv glad_glVertex3iv GLAD_API_CALL PFNGLVERTEX3SPROC glad_glVertex3s; #define glVertex3s glad_glVertex3s GLAD_API_CALL PFNGLVERTEX3SVPROC glad_glVertex3sv; #define glVertex3sv glad_glVertex3sv GLAD_API_CALL PFNGLVERTEX4DPROC glad_glVertex4d; #define glVertex4d glad_glVertex4d GLAD_API_CALL PFNGLVERTEX4DVPROC glad_glVertex4dv; #define glVertex4dv glad_glVertex4dv GLAD_API_CALL PFNGLVERTEX4FPROC glad_glVertex4f; #define glVertex4f glad_glVertex4f GLAD_API_CALL PFNGLVERTEX4FVPROC glad_glVertex4fv; #define glVertex4fv glad_glVertex4fv GLAD_API_CALL PFNGLVERTEX4IPROC glad_glVertex4i; #define glVertex4i glad_glVertex4i GLAD_API_CALL PFNGLVERTEX4IVPROC glad_glVertex4iv; #define glVertex4iv glad_glVertex4iv GLAD_API_CALL PFNGLVERTEX4SPROC glad_glVertex4s; #define glVertex4s glad_glVertex4s GLAD_API_CALL PFNGLVERTEX4SVPROC glad_glVertex4sv; #define glVertex4sv glad_glVertex4sv GLAD_API_CALL PFNGLVERTEXARRAYATTRIBBINDINGPROC glad_glVertexArrayAttribBinding; #define glVertexArrayAttribBinding glad_glVertexArrayAttribBinding GLAD_API_CALL PFNGLVERTEXARRAYATTRIBFORMATPROC glad_glVertexArrayAttribFormat; #define glVertexArrayAttribFormat glad_glVertexArrayAttribFormat GLAD_API_CALL PFNGLVERTEXARRAYATTRIBIFORMATPROC glad_glVertexArrayAttribIFormat; #define glVertexArrayAttribIFormat glad_glVertexArrayAttribIFormat GLAD_API_CALL PFNGLVERTEXARRAYATTRIBLFORMATPROC glad_glVertexArrayAttribLFormat; #define glVertexArrayAttribLFormat glad_glVertexArrayAttribLFormat GLAD_API_CALL PFNGLVERTEXARRAYBINDINGDIVISORPROC glad_glVertexArrayBindingDivisor; #define glVertexArrayBindingDivisor glad_glVertexArrayBindingDivisor GLAD_API_CALL PFNGLVERTEXARRAYELEMENTBUFFERPROC glad_glVertexArrayElementBuffer; #define glVertexArrayElementBuffer glad_glVertexArrayElementBuffer GLAD_API_CALL PFNGLVERTEXARRAYVERTEXBUFFERPROC glad_glVertexArrayVertexBuffer; #define glVertexArrayVertexBuffer glad_glVertexArrayVertexBuffer GLAD_API_CALL PFNGLVERTEXARRAYVERTEXBUFFERSPROC glad_glVertexArrayVertexBuffers; #define glVertexArrayVertexBuffers glad_glVertexArrayVertexBuffers GLAD_API_CALL PFNGLVERTEXATTRIB1DPROC glad_glVertexAttrib1d; #define glVertexAttrib1d glad_glVertexAttrib1d GLAD_API_CALL PFNGLVERTEXATTRIB1DARBPROC glad_glVertexAttrib1dARB; #define glVertexAttrib1dARB glad_glVertexAttrib1dARB GLAD_API_CALL PFNGLVERTEXATTRIB1DVPROC glad_glVertexAttrib1dv; #define glVertexAttrib1dv glad_glVertexAttrib1dv GLAD_API_CALL PFNGLVERTEXATTRIB1DVARBPROC glad_glVertexAttrib1dvARB; #define glVertexAttrib1dvARB glad_glVertexAttrib1dvARB GLAD_API_CALL PFNGLVERTEXATTRIB1FPROC glad_glVertexAttrib1f; #define glVertexAttrib1f glad_glVertexAttrib1f GLAD_API_CALL PFNGLVERTEXATTRIB1FARBPROC glad_glVertexAttrib1fARB; #define glVertexAttrib1fARB glad_glVertexAttrib1fARB GLAD_API_CALL PFNGLVERTEXATTRIB1FVPROC glad_glVertexAttrib1fv; #define glVertexAttrib1fv glad_glVertexAttrib1fv GLAD_API_CALL PFNGLVERTEXATTRIB1FVARBPROC glad_glVertexAttrib1fvARB; #define glVertexAttrib1fvARB glad_glVertexAttrib1fvARB GLAD_API_CALL PFNGLVERTEXATTRIB1SPROC glad_glVertexAttrib1s; #define glVertexAttrib1s glad_glVertexAttrib1s GLAD_API_CALL PFNGLVERTEXATTRIB1SARBPROC glad_glVertexAttrib1sARB; #define glVertexAttrib1sARB glad_glVertexAttrib1sARB GLAD_API_CALL PFNGLVERTEXATTRIB1SVPROC glad_glVertexAttrib1sv; #define glVertexAttrib1sv glad_glVertexAttrib1sv GLAD_API_CALL PFNGLVERTEXATTRIB1SVARBPROC glad_glVertexAttrib1svARB; #define glVertexAttrib1svARB glad_glVertexAttrib1svARB GLAD_API_CALL PFNGLVERTEXATTRIB2DPROC glad_glVertexAttrib2d; #define glVertexAttrib2d glad_glVertexAttrib2d GLAD_API_CALL PFNGLVERTEXATTRIB2DARBPROC glad_glVertexAttrib2dARB; #define glVertexAttrib2dARB glad_glVertexAttrib2dARB GLAD_API_CALL PFNGLVERTEXATTRIB2DVPROC glad_glVertexAttrib2dv; #define glVertexAttrib2dv glad_glVertexAttrib2dv GLAD_API_CALL PFNGLVERTEXATTRIB2DVARBPROC glad_glVertexAttrib2dvARB; #define glVertexAttrib2dvARB glad_glVertexAttrib2dvARB GLAD_API_CALL PFNGLVERTEXATTRIB2FPROC glad_glVertexAttrib2f; #define glVertexAttrib2f glad_glVertexAttrib2f GLAD_API_CALL PFNGLVERTEXATTRIB2FARBPROC glad_glVertexAttrib2fARB; #define glVertexAttrib2fARB glad_glVertexAttrib2fARB GLAD_API_CALL PFNGLVERTEXATTRIB2FVPROC glad_glVertexAttrib2fv; #define glVertexAttrib2fv glad_glVertexAttrib2fv GLAD_API_CALL PFNGLVERTEXATTRIB2FVARBPROC glad_glVertexAttrib2fvARB; #define glVertexAttrib2fvARB glad_glVertexAttrib2fvARB GLAD_API_CALL PFNGLVERTEXATTRIB2SPROC glad_glVertexAttrib2s; #define glVertexAttrib2s glad_glVertexAttrib2s GLAD_API_CALL PFNGLVERTEXATTRIB2SARBPROC glad_glVertexAttrib2sARB; #define glVertexAttrib2sARB glad_glVertexAttrib2sARB GLAD_API_CALL PFNGLVERTEXATTRIB2SVPROC glad_glVertexAttrib2sv; #define glVertexAttrib2sv glad_glVertexAttrib2sv GLAD_API_CALL PFNGLVERTEXATTRIB2SVARBPROC glad_glVertexAttrib2svARB; #define glVertexAttrib2svARB glad_glVertexAttrib2svARB GLAD_API_CALL PFNGLVERTEXATTRIB3DPROC glad_glVertexAttrib3d; #define glVertexAttrib3d glad_glVertexAttrib3d GLAD_API_CALL PFNGLVERTEXATTRIB3DARBPROC glad_glVertexAttrib3dARB; #define glVertexAttrib3dARB glad_glVertexAttrib3dARB GLAD_API_CALL PFNGLVERTEXATTRIB3DVPROC glad_glVertexAttrib3dv; #define glVertexAttrib3dv glad_glVertexAttrib3dv GLAD_API_CALL PFNGLVERTEXATTRIB3DVARBPROC glad_glVertexAttrib3dvARB; #define glVertexAttrib3dvARB glad_glVertexAttrib3dvARB GLAD_API_CALL PFNGLVERTEXATTRIB3FPROC glad_glVertexAttrib3f; #define glVertexAttrib3f glad_glVertexAttrib3f GLAD_API_CALL PFNGLVERTEXATTRIB3FARBPROC glad_glVertexAttrib3fARB; #define glVertexAttrib3fARB glad_glVertexAttrib3fARB GLAD_API_CALL PFNGLVERTEXATTRIB3FVPROC glad_glVertexAttrib3fv; #define glVertexAttrib3fv glad_glVertexAttrib3fv GLAD_API_CALL PFNGLVERTEXATTRIB3FVARBPROC glad_glVertexAttrib3fvARB; #define glVertexAttrib3fvARB glad_glVertexAttrib3fvARB GLAD_API_CALL PFNGLVERTEXATTRIB3SPROC glad_glVertexAttrib3s; #define glVertexAttrib3s glad_glVertexAttrib3s GLAD_API_CALL PFNGLVERTEXATTRIB3SARBPROC glad_glVertexAttrib3sARB; #define glVertexAttrib3sARB glad_glVertexAttrib3sARB GLAD_API_CALL PFNGLVERTEXATTRIB3SVPROC glad_glVertexAttrib3sv; #define glVertexAttrib3sv glad_glVertexAttrib3sv GLAD_API_CALL PFNGLVERTEXATTRIB3SVARBPROC glad_glVertexAttrib3svARB; #define glVertexAttrib3svARB glad_glVertexAttrib3svARB GLAD_API_CALL PFNGLVERTEXATTRIB4NBVPROC glad_glVertexAttrib4Nbv; #define glVertexAttrib4Nbv glad_glVertexAttrib4Nbv GLAD_API_CALL PFNGLVERTEXATTRIB4NBVARBPROC glad_glVertexAttrib4NbvARB; #define glVertexAttrib4NbvARB glad_glVertexAttrib4NbvARB GLAD_API_CALL PFNGLVERTEXATTRIB4NIVPROC glad_glVertexAttrib4Niv; #define glVertexAttrib4Niv glad_glVertexAttrib4Niv GLAD_API_CALL PFNGLVERTEXATTRIB4NIVARBPROC glad_glVertexAttrib4NivARB; #define glVertexAttrib4NivARB glad_glVertexAttrib4NivARB GLAD_API_CALL PFNGLVERTEXATTRIB4NSVPROC glad_glVertexAttrib4Nsv; #define glVertexAttrib4Nsv glad_glVertexAttrib4Nsv GLAD_API_CALL PFNGLVERTEXATTRIB4NSVARBPROC glad_glVertexAttrib4NsvARB; #define glVertexAttrib4NsvARB glad_glVertexAttrib4NsvARB GLAD_API_CALL PFNGLVERTEXATTRIB4NUBPROC glad_glVertexAttrib4Nub; #define glVertexAttrib4Nub glad_glVertexAttrib4Nub GLAD_API_CALL PFNGLVERTEXATTRIB4NUBARBPROC glad_glVertexAttrib4NubARB; #define glVertexAttrib4NubARB glad_glVertexAttrib4NubARB GLAD_API_CALL PFNGLVERTEXATTRIB4NUBVPROC glad_glVertexAttrib4Nubv; #define glVertexAttrib4Nubv glad_glVertexAttrib4Nubv GLAD_API_CALL PFNGLVERTEXATTRIB4NUBVARBPROC glad_glVertexAttrib4NubvARB; #define glVertexAttrib4NubvARB glad_glVertexAttrib4NubvARB GLAD_API_CALL PFNGLVERTEXATTRIB4NUIVPROC glad_glVertexAttrib4Nuiv; #define glVertexAttrib4Nuiv glad_glVertexAttrib4Nuiv GLAD_API_CALL PFNGLVERTEXATTRIB4NUIVARBPROC glad_glVertexAttrib4NuivARB; #define glVertexAttrib4NuivARB glad_glVertexAttrib4NuivARB GLAD_API_CALL PFNGLVERTEXATTRIB4NUSVPROC glad_glVertexAttrib4Nusv; #define glVertexAttrib4Nusv glad_glVertexAttrib4Nusv GLAD_API_CALL PFNGLVERTEXATTRIB4NUSVARBPROC glad_glVertexAttrib4NusvARB; #define glVertexAttrib4NusvARB glad_glVertexAttrib4NusvARB GLAD_API_CALL PFNGLVERTEXATTRIB4BVPROC glad_glVertexAttrib4bv; #define glVertexAttrib4bv glad_glVertexAttrib4bv GLAD_API_CALL PFNGLVERTEXATTRIB4BVARBPROC glad_glVertexAttrib4bvARB; #define glVertexAttrib4bvARB glad_glVertexAttrib4bvARB GLAD_API_CALL PFNGLVERTEXATTRIB4DPROC glad_glVertexAttrib4d; #define glVertexAttrib4d glad_glVertexAttrib4d GLAD_API_CALL PFNGLVERTEXATTRIB4DARBPROC glad_glVertexAttrib4dARB; #define glVertexAttrib4dARB glad_glVertexAttrib4dARB GLAD_API_CALL PFNGLVERTEXATTRIB4DVPROC glad_glVertexAttrib4dv; #define glVertexAttrib4dv glad_glVertexAttrib4dv GLAD_API_CALL PFNGLVERTEXATTRIB4DVARBPROC glad_glVertexAttrib4dvARB; #define glVertexAttrib4dvARB glad_glVertexAttrib4dvARB GLAD_API_CALL PFNGLVERTEXATTRIB4FPROC glad_glVertexAttrib4f; #define glVertexAttrib4f glad_glVertexAttrib4f GLAD_API_CALL PFNGLVERTEXATTRIB4FARBPROC glad_glVertexAttrib4fARB; #define glVertexAttrib4fARB glad_glVertexAttrib4fARB GLAD_API_CALL PFNGLVERTEXATTRIB4FVPROC glad_glVertexAttrib4fv; #define glVertexAttrib4fv glad_glVertexAttrib4fv GLAD_API_CALL PFNGLVERTEXATTRIB4FVARBPROC glad_glVertexAttrib4fvARB; #define glVertexAttrib4fvARB glad_glVertexAttrib4fvARB GLAD_API_CALL PFNGLVERTEXATTRIB4IVPROC glad_glVertexAttrib4iv; #define glVertexAttrib4iv glad_glVertexAttrib4iv GLAD_API_CALL PFNGLVERTEXATTRIB4IVARBPROC glad_glVertexAttrib4ivARB; #define glVertexAttrib4ivARB glad_glVertexAttrib4ivARB GLAD_API_CALL PFNGLVERTEXATTRIB4SPROC glad_glVertexAttrib4s; #define glVertexAttrib4s glad_glVertexAttrib4s GLAD_API_CALL PFNGLVERTEXATTRIB4SARBPROC glad_glVertexAttrib4sARB; #define glVertexAttrib4sARB glad_glVertexAttrib4sARB GLAD_API_CALL PFNGLVERTEXATTRIB4SVPROC glad_glVertexAttrib4sv; #define glVertexAttrib4sv glad_glVertexAttrib4sv GLAD_API_CALL PFNGLVERTEXATTRIB4SVARBPROC glad_glVertexAttrib4svARB; #define glVertexAttrib4svARB glad_glVertexAttrib4svARB GLAD_API_CALL PFNGLVERTEXATTRIB4UBVPROC glad_glVertexAttrib4ubv; #define glVertexAttrib4ubv glad_glVertexAttrib4ubv GLAD_API_CALL PFNGLVERTEXATTRIB4UBVARBPROC glad_glVertexAttrib4ubvARB; #define glVertexAttrib4ubvARB glad_glVertexAttrib4ubvARB GLAD_API_CALL PFNGLVERTEXATTRIB4UIVPROC glad_glVertexAttrib4uiv; #define glVertexAttrib4uiv glad_glVertexAttrib4uiv GLAD_API_CALL PFNGLVERTEXATTRIB4UIVARBPROC glad_glVertexAttrib4uivARB; #define glVertexAttrib4uivARB glad_glVertexAttrib4uivARB GLAD_API_CALL PFNGLVERTEXATTRIB4USVPROC glad_glVertexAttrib4usv; #define glVertexAttrib4usv glad_glVertexAttrib4usv GLAD_API_CALL PFNGLVERTEXATTRIB4USVARBPROC glad_glVertexAttrib4usvARB; #define glVertexAttrib4usvARB glad_glVertexAttrib4usvARB GLAD_API_CALL PFNGLVERTEXATTRIBBINDINGPROC glad_glVertexAttribBinding; #define glVertexAttribBinding glad_glVertexAttribBinding GLAD_API_CALL PFNGLVERTEXATTRIBDIVISORPROC glad_glVertexAttribDivisor; #define glVertexAttribDivisor glad_glVertexAttribDivisor GLAD_API_CALL PFNGLVERTEXATTRIBDIVISORARBPROC glad_glVertexAttribDivisorARB; #define glVertexAttribDivisorARB glad_glVertexAttribDivisorARB GLAD_API_CALL PFNGLVERTEXATTRIBFORMATPROC glad_glVertexAttribFormat; #define glVertexAttribFormat glad_glVertexAttribFormat GLAD_API_CALL PFNGLVERTEXATTRIBI1IPROC glad_glVertexAttribI1i; #define glVertexAttribI1i glad_glVertexAttribI1i GLAD_API_CALL PFNGLVERTEXATTRIBI1IVPROC glad_glVertexAttribI1iv; #define glVertexAttribI1iv glad_glVertexAttribI1iv GLAD_API_CALL PFNGLVERTEXATTRIBI1UIPROC glad_glVertexAttribI1ui; #define glVertexAttribI1ui glad_glVertexAttribI1ui GLAD_API_CALL PFNGLVERTEXATTRIBI1UIVPROC glad_glVertexAttribI1uiv; #define glVertexAttribI1uiv glad_glVertexAttribI1uiv GLAD_API_CALL PFNGLVERTEXATTRIBI2IPROC glad_glVertexAttribI2i; #define glVertexAttribI2i glad_glVertexAttribI2i GLAD_API_CALL PFNGLVERTEXATTRIBI2IVPROC glad_glVertexAttribI2iv; #define glVertexAttribI2iv glad_glVertexAttribI2iv GLAD_API_CALL PFNGLVERTEXATTRIBI2UIPROC glad_glVertexAttribI2ui; #define glVertexAttribI2ui glad_glVertexAttribI2ui GLAD_API_CALL PFNGLVERTEXATTRIBI2UIVPROC glad_glVertexAttribI2uiv; #define glVertexAttribI2uiv glad_glVertexAttribI2uiv GLAD_API_CALL PFNGLVERTEXATTRIBI3IPROC glad_glVertexAttribI3i; #define glVertexAttribI3i glad_glVertexAttribI3i GLAD_API_CALL PFNGLVERTEXATTRIBI3IVPROC glad_glVertexAttribI3iv; #define glVertexAttribI3iv glad_glVertexAttribI3iv GLAD_API_CALL PFNGLVERTEXATTRIBI3UIPROC glad_glVertexAttribI3ui; #define glVertexAttribI3ui glad_glVertexAttribI3ui GLAD_API_CALL PFNGLVERTEXATTRIBI3UIVPROC glad_glVertexAttribI3uiv; #define glVertexAttribI3uiv glad_glVertexAttribI3uiv GLAD_API_CALL PFNGLVERTEXATTRIBI4BVPROC glad_glVertexAttribI4bv; #define glVertexAttribI4bv glad_glVertexAttribI4bv GLAD_API_CALL PFNGLVERTEXATTRIBI4IPROC glad_glVertexAttribI4i; #define glVertexAttribI4i glad_glVertexAttribI4i GLAD_API_CALL PFNGLVERTEXATTRIBI4IVPROC glad_glVertexAttribI4iv; #define glVertexAttribI4iv glad_glVertexAttribI4iv GLAD_API_CALL PFNGLVERTEXATTRIBI4SVPROC glad_glVertexAttribI4sv; #define glVertexAttribI4sv glad_glVertexAttribI4sv GLAD_API_CALL PFNGLVERTEXATTRIBI4UBVPROC glad_glVertexAttribI4ubv; #define glVertexAttribI4ubv glad_glVertexAttribI4ubv GLAD_API_CALL PFNGLVERTEXATTRIBI4UIPROC glad_glVertexAttribI4ui; #define glVertexAttribI4ui glad_glVertexAttribI4ui GLAD_API_CALL PFNGLVERTEXATTRIBI4UIVPROC glad_glVertexAttribI4uiv; #define glVertexAttribI4uiv glad_glVertexAttribI4uiv GLAD_API_CALL PFNGLVERTEXATTRIBI4USVPROC glad_glVertexAttribI4usv; #define glVertexAttribI4usv glad_glVertexAttribI4usv GLAD_API_CALL PFNGLVERTEXATTRIBIFORMATPROC glad_glVertexAttribIFormat; #define glVertexAttribIFormat glad_glVertexAttribIFormat GLAD_API_CALL PFNGLVERTEXATTRIBIPOINTERPROC glad_glVertexAttribIPointer; #define glVertexAttribIPointer glad_glVertexAttribIPointer GLAD_API_CALL PFNGLVERTEXATTRIBL1DPROC glad_glVertexAttribL1d; #define glVertexAttribL1d glad_glVertexAttribL1d GLAD_API_CALL PFNGLVERTEXATTRIBL1DVPROC glad_glVertexAttribL1dv; #define glVertexAttribL1dv glad_glVertexAttribL1dv GLAD_API_CALL PFNGLVERTEXATTRIBL1UI64ARBPROC glad_glVertexAttribL1ui64ARB; #define glVertexAttribL1ui64ARB glad_glVertexAttribL1ui64ARB GLAD_API_CALL PFNGLVERTEXATTRIBL1UI64VARBPROC glad_glVertexAttribL1ui64vARB; #define glVertexAttribL1ui64vARB glad_glVertexAttribL1ui64vARB GLAD_API_CALL PFNGLVERTEXATTRIBL2DPROC glad_glVertexAttribL2d; #define glVertexAttribL2d glad_glVertexAttribL2d GLAD_API_CALL PFNGLVERTEXATTRIBL2DVPROC glad_glVertexAttribL2dv; #define glVertexAttribL2dv glad_glVertexAttribL2dv GLAD_API_CALL PFNGLVERTEXATTRIBL3DPROC glad_glVertexAttribL3d; #define glVertexAttribL3d glad_glVertexAttribL3d GLAD_API_CALL PFNGLVERTEXATTRIBL3DVPROC glad_glVertexAttribL3dv; #define glVertexAttribL3dv glad_glVertexAttribL3dv GLAD_API_CALL PFNGLVERTEXATTRIBL4DPROC glad_glVertexAttribL4d; #define glVertexAttribL4d glad_glVertexAttribL4d GLAD_API_CALL PFNGLVERTEXATTRIBL4DVPROC glad_glVertexAttribL4dv; #define glVertexAttribL4dv glad_glVertexAttribL4dv GLAD_API_CALL PFNGLVERTEXATTRIBLFORMATPROC glad_glVertexAttribLFormat; #define glVertexAttribLFormat glad_glVertexAttribLFormat GLAD_API_CALL PFNGLVERTEXATTRIBLPOINTERPROC glad_glVertexAttribLPointer; #define glVertexAttribLPointer glad_glVertexAttribLPointer GLAD_API_CALL PFNGLVERTEXATTRIBP1UIPROC glad_glVertexAttribP1ui; #define glVertexAttribP1ui glad_glVertexAttribP1ui GLAD_API_CALL PFNGLVERTEXATTRIBP1UIVPROC glad_glVertexAttribP1uiv; #define glVertexAttribP1uiv glad_glVertexAttribP1uiv GLAD_API_CALL PFNGLVERTEXATTRIBP2UIPROC glad_glVertexAttribP2ui; #define glVertexAttribP2ui glad_glVertexAttribP2ui GLAD_API_CALL PFNGLVERTEXATTRIBP2UIVPROC glad_glVertexAttribP2uiv; #define glVertexAttribP2uiv glad_glVertexAttribP2uiv GLAD_API_CALL PFNGLVERTEXATTRIBP3UIPROC glad_glVertexAttribP3ui; #define glVertexAttribP3ui glad_glVertexAttribP3ui GLAD_API_CALL PFNGLVERTEXATTRIBP3UIVPROC glad_glVertexAttribP3uiv; #define glVertexAttribP3uiv glad_glVertexAttribP3uiv GLAD_API_CALL PFNGLVERTEXATTRIBP4UIPROC glad_glVertexAttribP4ui; #define glVertexAttribP4ui glad_glVertexAttribP4ui GLAD_API_CALL PFNGLVERTEXATTRIBP4UIVPROC glad_glVertexAttribP4uiv; #define glVertexAttribP4uiv glad_glVertexAttribP4uiv GLAD_API_CALL PFNGLVERTEXATTRIBPOINTERPROC glad_glVertexAttribPointer; #define glVertexAttribPointer glad_glVertexAttribPointer GLAD_API_CALL PFNGLVERTEXATTRIBPOINTERARBPROC glad_glVertexAttribPointerARB; #define glVertexAttribPointerARB glad_glVertexAttribPointerARB GLAD_API_CALL PFNGLVERTEXBINDINGDIVISORPROC glad_glVertexBindingDivisor; #define glVertexBindingDivisor glad_glVertexBindingDivisor GLAD_API_CALL PFNGLVERTEXBLENDARBPROC glad_glVertexBlendARB; #define glVertexBlendARB glad_glVertexBlendARB GLAD_API_CALL PFNGLVERTEXP2UIPROC glad_glVertexP2ui; #define glVertexP2ui glad_glVertexP2ui GLAD_API_CALL PFNGLVERTEXP2UIVPROC glad_glVertexP2uiv; #define glVertexP2uiv glad_glVertexP2uiv GLAD_API_CALL PFNGLVERTEXP3UIPROC glad_glVertexP3ui; #define glVertexP3ui glad_glVertexP3ui GLAD_API_CALL PFNGLVERTEXP3UIVPROC glad_glVertexP3uiv; #define glVertexP3uiv glad_glVertexP3uiv GLAD_API_CALL PFNGLVERTEXP4UIPROC glad_glVertexP4ui; #define glVertexP4ui glad_glVertexP4ui GLAD_API_CALL PFNGLVERTEXP4UIVPROC glad_glVertexP4uiv; #define glVertexP4uiv glad_glVertexP4uiv GLAD_API_CALL PFNGLVERTEXPOINTERPROC glad_glVertexPointer; #define glVertexPointer glad_glVertexPointer GLAD_API_CALL PFNGLVIEWPORTPROC glad_glViewport; #define glViewport glad_glViewport GLAD_API_CALL PFNGLVIEWPORTARRAYVPROC glad_glViewportArrayv; #define glViewportArrayv glad_glViewportArrayv GLAD_API_CALL PFNGLVIEWPORTINDEXEDFPROC glad_glViewportIndexedf; #define glViewportIndexedf glad_glViewportIndexedf GLAD_API_CALL PFNGLVIEWPORTINDEXEDFVPROC glad_glViewportIndexedfv; #define glViewportIndexedfv glad_glViewportIndexedfv GLAD_API_CALL PFNGLWAITSYNCPROC glad_glWaitSync; #define glWaitSync glad_glWaitSync GLAD_API_CALL PFNGLWEIGHTPOINTERARBPROC glad_glWeightPointerARB; #define glWeightPointerARB glad_glWeightPointerARB GLAD_API_CALL PFNGLWEIGHTBVARBPROC glad_glWeightbvARB; #define glWeightbvARB glad_glWeightbvARB GLAD_API_CALL PFNGLWEIGHTDVARBPROC glad_glWeightdvARB; #define glWeightdvARB glad_glWeightdvARB GLAD_API_CALL PFNGLWEIGHTFVARBPROC glad_glWeightfvARB; #define glWeightfvARB glad_glWeightfvARB GLAD_API_CALL PFNGLWEIGHTIVARBPROC glad_glWeightivARB; #define glWeightivARB glad_glWeightivARB GLAD_API_CALL PFNGLWEIGHTSVARBPROC glad_glWeightsvARB; #define glWeightsvARB glad_glWeightsvARB GLAD_API_CALL PFNGLWEIGHTUBVARBPROC glad_glWeightubvARB; #define glWeightubvARB glad_glWeightubvARB GLAD_API_CALL PFNGLWEIGHTUIVARBPROC glad_glWeightuivARB; #define glWeightuivARB glad_glWeightuivARB GLAD_API_CALL PFNGLWEIGHTUSVARBPROC glad_glWeightusvARB; #define glWeightusvARB glad_glWeightusvARB GLAD_API_CALL PFNGLWINDOWPOS2DPROC glad_glWindowPos2d; #define glWindowPos2d glad_glWindowPos2d GLAD_API_CALL PFNGLWINDOWPOS2DARBPROC glad_glWindowPos2dARB; #define glWindowPos2dARB glad_glWindowPos2dARB GLAD_API_CALL PFNGLWINDOWPOS2DVPROC glad_glWindowPos2dv; #define glWindowPos2dv glad_glWindowPos2dv GLAD_API_CALL PFNGLWINDOWPOS2DVARBPROC glad_glWindowPos2dvARB; #define glWindowPos2dvARB glad_glWindowPos2dvARB GLAD_API_CALL PFNGLWINDOWPOS2FPROC glad_glWindowPos2f; #define glWindowPos2f glad_glWindowPos2f GLAD_API_CALL PFNGLWINDOWPOS2FARBPROC glad_glWindowPos2fARB; #define glWindowPos2fARB glad_glWindowPos2fARB GLAD_API_CALL PFNGLWINDOWPOS2FVPROC glad_glWindowPos2fv; #define glWindowPos2fv glad_glWindowPos2fv GLAD_API_CALL PFNGLWINDOWPOS2FVARBPROC glad_glWindowPos2fvARB; #define glWindowPos2fvARB glad_glWindowPos2fvARB GLAD_API_CALL PFNGLWINDOWPOS2IPROC glad_glWindowPos2i; #define glWindowPos2i glad_glWindowPos2i GLAD_API_CALL PFNGLWINDOWPOS2IARBPROC glad_glWindowPos2iARB; #define glWindowPos2iARB glad_glWindowPos2iARB GLAD_API_CALL PFNGLWINDOWPOS2IVPROC glad_glWindowPos2iv; #define glWindowPos2iv glad_glWindowPos2iv GLAD_API_CALL PFNGLWINDOWPOS2IVARBPROC glad_glWindowPos2ivARB; #define glWindowPos2ivARB glad_glWindowPos2ivARB GLAD_API_CALL PFNGLWINDOWPOS2SPROC glad_glWindowPos2s; #define glWindowPos2s glad_glWindowPos2s GLAD_API_CALL PFNGLWINDOWPOS2SARBPROC glad_glWindowPos2sARB; #define glWindowPos2sARB glad_glWindowPos2sARB GLAD_API_CALL PFNGLWINDOWPOS2SVPROC glad_glWindowPos2sv; #define glWindowPos2sv glad_glWindowPos2sv GLAD_API_CALL PFNGLWINDOWPOS2SVARBPROC glad_glWindowPos2svARB; #define glWindowPos2svARB glad_glWindowPos2svARB GLAD_API_CALL PFNGLWINDOWPOS3DPROC glad_glWindowPos3d; #define glWindowPos3d glad_glWindowPos3d GLAD_API_CALL PFNGLWINDOWPOS3DARBPROC glad_glWindowPos3dARB; #define glWindowPos3dARB glad_glWindowPos3dARB GLAD_API_CALL PFNGLWINDOWPOS3DVPROC glad_glWindowPos3dv; #define glWindowPos3dv glad_glWindowPos3dv GLAD_API_CALL PFNGLWINDOWPOS3DVARBPROC glad_glWindowPos3dvARB; #define glWindowPos3dvARB glad_glWindowPos3dvARB GLAD_API_CALL PFNGLWINDOWPOS3FPROC glad_glWindowPos3f; #define glWindowPos3f glad_glWindowPos3f GLAD_API_CALL PFNGLWINDOWPOS3FARBPROC glad_glWindowPos3fARB; #define glWindowPos3fARB glad_glWindowPos3fARB GLAD_API_CALL PFNGLWINDOWPOS3FVPROC glad_glWindowPos3fv; #define glWindowPos3fv glad_glWindowPos3fv GLAD_API_CALL PFNGLWINDOWPOS3FVARBPROC glad_glWindowPos3fvARB; #define glWindowPos3fvARB glad_glWindowPos3fvARB GLAD_API_CALL PFNGLWINDOWPOS3IPROC glad_glWindowPos3i; #define glWindowPos3i glad_glWindowPos3i GLAD_API_CALL PFNGLWINDOWPOS3IARBPROC glad_glWindowPos3iARB; #define glWindowPos3iARB glad_glWindowPos3iARB GLAD_API_CALL PFNGLWINDOWPOS3IVPROC glad_glWindowPos3iv; #define glWindowPos3iv glad_glWindowPos3iv GLAD_API_CALL PFNGLWINDOWPOS3IVARBPROC glad_glWindowPos3ivARB; #define glWindowPos3ivARB glad_glWindowPos3ivARB GLAD_API_CALL PFNGLWINDOWPOS3SPROC glad_glWindowPos3s; #define glWindowPos3s glad_glWindowPos3s GLAD_API_CALL PFNGLWINDOWPOS3SARBPROC glad_glWindowPos3sARB; #define glWindowPos3sARB glad_glWindowPos3sARB GLAD_API_CALL PFNGLWINDOWPOS3SVPROC glad_glWindowPos3sv; #define glWindowPos3sv glad_glWindowPos3sv GLAD_API_CALL PFNGLWINDOWPOS3SVARBPROC glad_glWindowPos3svARB; #define glWindowPos3svARB glad_glWindowPos3svARB GLAD_API_CALL int gladLoadGLUserPtr( GLADuserptrloadfunc load, void *userptr); GLAD_API_CALL int gladLoadGL( GLADloadfunc load); #ifdef __cplusplus } #endif #endif /* Source */ #ifdef GLAD_GL_IMPLEMENTATION #include #include #include #ifndef GLAD_IMPL_UTIL_C_ #define GLAD_IMPL_UTIL_C_ #ifdef _MSC_VER #define GLAD_IMPL_UTIL_SSCANF sscanf_s #else #define GLAD_IMPL_UTIL_SSCANF sscanf #endif #endif /* GLAD_IMPL_UTIL_C_ */ int GLAD_GL_VERSION_1_0 = 0; int GLAD_GL_VERSION_1_1 = 0; int GLAD_GL_VERSION_1_2 = 0; int GLAD_GL_VERSION_1_3 = 0; int GLAD_GL_VERSION_1_4 = 0; int GLAD_GL_VERSION_1_5 = 0; int GLAD_GL_VERSION_2_0 = 0; int GLAD_GL_VERSION_2_1 = 0; int GLAD_GL_VERSION_3_0 = 0; int GLAD_GL_VERSION_3_1 = 0; int GLAD_GL_VERSION_3_2 = 0; int GLAD_GL_VERSION_3_3 = 0; int GLAD_GL_ARB_ES2_compatibility = 0; int GLAD_GL_ARB_ES3_1_compatibility = 0; int GLAD_GL_ARB_ES3_2_compatibility = 0; int GLAD_GL_ARB_ES3_compatibility = 0; int GLAD_GL_ARB_arrays_of_arrays = 0; int GLAD_GL_ARB_base_instance = 0; int GLAD_GL_ARB_bindless_texture = 0; int GLAD_GL_ARB_blend_func_extended = 0; int GLAD_GL_ARB_buffer_storage = 0; int GLAD_GL_ARB_cl_event = 0; int GLAD_GL_ARB_clear_buffer_object = 0; int GLAD_GL_ARB_clear_texture = 0; int GLAD_GL_ARB_clip_control = 0; int GLAD_GL_ARB_color_buffer_float = 0; int GLAD_GL_ARB_compatibility = 0; int GLAD_GL_ARB_compressed_texture_pixel_storage = 0; int GLAD_GL_ARB_compute_shader = 0; int GLAD_GL_ARB_compute_variable_group_size = 0; int GLAD_GL_ARB_conditional_render_inverted = 0; int GLAD_GL_ARB_conservative_depth = 0; int GLAD_GL_ARB_copy_buffer = 0; int GLAD_GL_ARB_copy_image = 0; int GLAD_GL_ARB_cull_distance = 0; int GLAD_GL_ARB_debug_output = 0; int GLAD_GL_ARB_depth_buffer_float = 0; int GLAD_GL_ARB_depth_clamp = 0; int GLAD_GL_ARB_depth_texture = 0; int GLAD_GL_ARB_derivative_control = 0; int GLAD_GL_ARB_direct_state_access = 0; int GLAD_GL_ARB_draw_buffers = 0; int GLAD_GL_ARB_draw_buffers_blend = 0; int GLAD_GL_ARB_draw_elements_base_vertex = 0; int GLAD_GL_ARB_draw_indirect = 0; int GLAD_GL_ARB_draw_instanced = 0; int GLAD_GL_ARB_enhanced_layouts = 0; int GLAD_GL_ARB_explicit_attrib_location = 0; int GLAD_GL_ARB_explicit_uniform_location = 0; int GLAD_GL_ARB_fragment_coord_conventions = 0; int GLAD_GL_ARB_fragment_layer_viewport = 0; int GLAD_GL_ARB_fragment_program = 0; int GLAD_GL_ARB_fragment_program_shadow = 0; int GLAD_GL_ARB_fragment_shader = 0; int GLAD_GL_ARB_fragment_shader_interlock = 0; int GLAD_GL_ARB_framebuffer_no_attachments = 0; int GLAD_GL_ARB_framebuffer_object = 0; int GLAD_GL_ARB_framebuffer_sRGB = 0; int GLAD_GL_ARB_geometry_shader4 = 0; int GLAD_GL_ARB_get_program_binary = 0; int GLAD_GL_ARB_get_texture_sub_image = 0; int GLAD_GL_ARB_gl_spirv = 0; int GLAD_GL_ARB_gpu_shader5 = 0; int GLAD_GL_ARB_gpu_shader_fp64 = 0; int GLAD_GL_ARB_gpu_shader_int64 = 0; int GLAD_GL_ARB_half_float_pixel = 0; int GLAD_GL_ARB_half_float_vertex = 0; int GLAD_GL_ARB_imaging = 0; int GLAD_GL_ARB_indirect_parameters = 0; int GLAD_GL_ARB_instanced_arrays = 0; int GLAD_GL_ARB_internalformat_query = 0; int GLAD_GL_ARB_internalformat_query2 = 0; int GLAD_GL_ARB_invalidate_subdata = 0; int GLAD_GL_ARB_map_buffer_alignment = 0; int GLAD_GL_ARB_map_buffer_range = 0; int GLAD_GL_ARB_matrix_palette = 0; int GLAD_GL_ARB_multi_bind = 0; int GLAD_GL_ARB_multi_draw_indirect = 0; int GLAD_GL_ARB_multisample = 0; int GLAD_GL_ARB_multitexture = 0; int GLAD_GL_ARB_occlusion_query = 0; int GLAD_GL_ARB_occlusion_query2 = 0; int GLAD_GL_ARB_parallel_shader_compile = 0; int GLAD_GL_ARB_pipeline_statistics_query = 0; int GLAD_GL_ARB_pixel_buffer_object = 0; int GLAD_GL_ARB_point_parameters = 0; int GLAD_GL_ARB_point_sprite = 0; int GLAD_GL_ARB_polygon_offset_clamp = 0; int GLAD_GL_ARB_post_depth_coverage = 0; int GLAD_GL_ARB_program_interface_query = 0; int GLAD_GL_ARB_provoking_vertex = 0; int GLAD_GL_ARB_query_buffer_object = 0; int GLAD_GL_ARB_robust_buffer_access_behavior = 0; int GLAD_GL_ARB_robustness = 0; int GLAD_GL_ARB_robustness_isolation = 0; int GLAD_GL_ARB_sample_locations = 0; int GLAD_GL_ARB_sample_shading = 0; int GLAD_GL_ARB_sampler_objects = 0; int GLAD_GL_ARB_seamless_cube_map = 0; int GLAD_GL_ARB_seamless_cubemap_per_texture = 0; int GLAD_GL_ARB_separate_shader_objects = 0; int GLAD_GL_ARB_shader_atomic_counter_ops = 0; int GLAD_GL_ARB_shader_atomic_counters = 0; int GLAD_GL_ARB_shader_ballot = 0; int GLAD_GL_ARB_shader_bit_encoding = 0; int GLAD_GL_ARB_shader_clock = 0; int GLAD_GL_ARB_shader_draw_parameters = 0; int GLAD_GL_ARB_shader_group_vote = 0; int GLAD_GL_ARB_shader_image_load_store = 0; int GLAD_GL_ARB_shader_image_size = 0; int GLAD_GL_ARB_shader_objects = 0; int GLAD_GL_ARB_shader_precision = 0; int GLAD_GL_ARB_shader_stencil_export = 0; int GLAD_GL_ARB_shader_storage_buffer_object = 0; int GLAD_GL_ARB_shader_subroutine = 0; int GLAD_GL_ARB_shader_texture_image_samples = 0; int GLAD_GL_ARB_shader_texture_lod = 0; int GLAD_GL_ARB_shader_viewport_layer_array = 0; int GLAD_GL_ARB_shading_language_100 = 0; int GLAD_GL_ARB_shading_language_420pack = 0; int GLAD_GL_ARB_shading_language_include = 0; int GLAD_GL_ARB_shading_language_packing = 0; int GLAD_GL_ARB_shadow = 0; int GLAD_GL_ARB_shadow_ambient = 0; int GLAD_GL_ARB_sparse_buffer = 0; int GLAD_GL_ARB_sparse_texture = 0; int GLAD_GL_ARB_sparse_texture2 = 0; int GLAD_GL_ARB_sparse_texture_clamp = 0; int GLAD_GL_ARB_spirv_extensions = 0; int GLAD_GL_ARB_stencil_texturing = 0; int GLAD_GL_ARB_sync = 0; int GLAD_GL_ARB_tessellation_shader = 0; int GLAD_GL_ARB_texture_barrier = 0; int GLAD_GL_ARB_texture_border_clamp = 0; int GLAD_GL_ARB_texture_buffer_object = 0; int GLAD_GL_ARB_texture_buffer_object_rgb32 = 0; int GLAD_GL_ARB_texture_buffer_range = 0; int GLAD_GL_ARB_texture_compression = 0; int GLAD_GL_ARB_texture_compression_bptc = 0; int GLAD_GL_ARB_texture_compression_rgtc = 0; int GLAD_GL_ARB_texture_cube_map = 0; int GLAD_GL_ARB_texture_cube_map_array = 0; int GLAD_GL_ARB_texture_env_add = 0; int GLAD_GL_ARB_texture_env_combine = 0; int GLAD_GL_ARB_texture_env_crossbar = 0; int GLAD_GL_ARB_texture_env_dot3 = 0; int GLAD_GL_ARB_texture_filter_anisotropic = 0; int GLAD_GL_ARB_texture_filter_minmax = 0; int GLAD_GL_ARB_texture_float = 0; int GLAD_GL_ARB_texture_gather = 0; int GLAD_GL_ARB_texture_mirror_clamp_to_edge = 0; int GLAD_GL_ARB_texture_mirrored_repeat = 0; int GLAD_GL_ARB_texture_multisample = 0; int GLAD_GL_ARB_texture_non_power_of_two = 0; int GLAD_GL_ARB_texture_query_levels = 0; int GLAD_GL_ARB_texture_query_lod = 0; int GLAD_GL_ARB_texture_rectangle = 0; int GLAD_GL_ARB_texture_rg = 0; int GLAD_GL_ARB_texture_rgb10_a2ui = 0; int GLAD_GL_ARB_texture_stencil8 = 0; int GLAD_GL_ARB_texture_storage = 0; int GLAD_GL_ARB_texture_storage_multisample = 0; int GLAD_GL_ARB_texture_swizzle = 0; int GLAD_GL_ARB_texture_view = 0; int GLAD_GL_ARB_timer_query = 0; int GLAD_GL_ARB_transform_feedback2 = 0; int GLAD_GL_ARB_transform_feedback3 = 0; int GLAD_GL_ARB_transform_feedback_instanced = 0; int GLAD_GL_ARB_transform_feedback_overflow_query = 0; int GLAD_GL_ARB_transpose_matrix = 0; int GLAD_GL_ARB_uniform_buffer_object = 0; int GLAD_GL_ARB_vertex_array_bgra = 0; int GLAD_GL_ARB_vertex_array_object = 0; int GLAD_GL_ARB_vertex_attrib_64bit = 0; int GLAD_GL_ARB_vertex_attrib_binding = 0; int GLAD_GL_ARB_vertex_blend = 0; int GLAD_GL_ARB_vertex_buffer_object = 0; int GLAD_GL_ARB_vertex_program = 0; int GLAD_GL_ARB_vertex_shader = 0; int GLAD_GL_ARB_vertex_type_10f_11f_11f_rev = 0; int GLAD_GL_ARB_vertex_type_2_10_10_10_rev = 0; int GLAD_GL_ARB_viewport_array = 0; int GLAD_GL_ARB_window_pos = 0; int GLAD_GL_KHR_blend_equation_advanced = 0; int GLAD_GL_KHR_blend_equation_advanced_coherent = 0; int GLAD_GL_KHR_context_flush_control = 0; int GLAD_GL_KHR_debug = 0; int GLAD_GL_KHR_no_error = 0; int GLAD_GL_KHR_parallel_shader_compile = 0; int GLAD_GL_KHR_robust_buffer_access_behavior = 0; int GLAD_GL_KHR_robustness = 0; int GLAD_GL_KHR_shader_subgroup = 0; int GLAD_GL_KHR_texture_compression_astc_hdr = 0; int GLAD_GL_KHR_texture_compression_astc_ldr = 0; int GLAD_GL_KHR_texture_compression_astc_sliced_3d = 0; PFNGLACCUMPROC glad_glAccum = NULL; PFNGLACTIVESHADERPROGRAMPROC glad_glActiveShaderProgram = NULL; PFNGLACTIVETEXTUREPROC glad_glActiveTexture = NULL; PFNGLACTIVETEXTUREARBPROC glad_glActiveTextureARB = NULL; PFNGLALPHAFUNCPROC glad_glAlphaFunc = NULL; PFNGLARETEXTURESRESIDENTPROC glad_glAreTexturesResident = NULL; PFNGLARRAYELEMENTPROC glad_glArrayElement = NULL; PFNGLATTACHOBJECTARBPROC glad_glAttachObjectARB = NULL; PFNGLATTACHSHADERPROC glad_glAttachShader = NULL; PFNGLBEGINPROC glad_glBegin = NULL; PFNGLBEGINCONDITIONALRENDERPROC glad_glBeginConditionalRender = NULL; PFNGLBEGINQUERYPROC glad_glBeginQuery = NULL; PFNGLBEGINQUERYARBPROC glad_glBeginQueryARB = NULL; PFNGLBEGINQUERYINDEXEDPROC glad_glBeginQueryIndexed = NULL; PFNGLBEGINTRANSFORMFEEDBACKPROC glad_glBeginTransformFeedback = NULL; PFNGLBINDATTRIBLOCATIONPROC glad_glBindAttribLocation = NULL; PFNGLBINDATTRIBLOCATIONARBPROC glad_glBindAttribLocationARB = NULL; PFNGLBINDBUFFERPROC glad_glBindBuffer = NULL; PFNGLBINDBUFFERARBPROC glad_glBindBufferARB = NULL; PFNGLBINDBUFFERBASEPROC glad_glBindBufferBase = NULL; PFNGLBINDBUFFERRANGEPROC glad_glBindBufferRange = NULL; PFNGLBINDBUFFERSBASEPROC glad_glBindBuffersBase = NULL; PFNGLBINDBUFFERSRANGEPROC glad_glBindBuffersRange = NULL; PFNGLBINDFRAGDATALOCATIONPROC glad_glBindFragDataLocation = NULL; PFNGLBINDFRAGDATALOCATIONINDEXEDPROC glad_glBindFragDataLocationIndexed = NULL; PFNGLBINDFRAMEBUFFERPROC glad_glBindFramebuffer = NULL; PFNGLBINDIMAGETEXTUREPROC glad_glBindImageTexture = NULL; PFNGLBINDIMAGETEXTURESPROC glad_glBindImageTextures = NULL; PFNGLBINDPROGRAMARBPROC glad_glBindProgramARB = NULL; PFNGLBINDPROGRAMPIPELINEPROC glad_glBindProgramPipeline = NULL; PFNGLBINDRENDERBUFFERPROC glad_glBindRenderbuffer = NULL; PFNGLBINDSAMPLERPROC glad_glBindSampler = NULL; PFNGLBINDSAMPLERSPROC glad_glBindSamplers = NULL; PFNGLBINDTEXTUREPROC glad_glBindTexture = NULL; PFNGLBINDTEXTUREUNITPROC glad_glBindTextureUnit = NULL; PFNGLBINDTEXTURESPROC glad_glBindTextures = NULL; PFNGLBINDTRANSFORMFEEDBACKPROC glad_glBindTransformFeedback = NULL; PFNGLBINDVERTEXARRAYPROC glad_glBindVertexArray = NULL; PFNGLBINDVERTEXBUFFERPROC glad_glBindVertexBuffer = NULL; PFNGLBINDVERTEXBUFFERSPROC glad_glBindVertexBuffers = NULL; PFNGLBITMAPPROC glad_glBitmap = NULL; PFNGLBLENDBARRIERPROC glad_glBlendBarrier = NULL; PFNGLBLENDBARRIERKHRPROC glad_glBlendBarrierKHR = NULL; PFNGLBLENDCOLORPROC glad_glBlendColor = NULL; PFNGLBLENDEQUATIONPROC glad_glBlendEquation = NULL; PFNGLBLENDEQUATIONSEPARATEPROC glad_glBlendEquationSeparate = NULL; PFNGLBLENDEQUATIONSEPARATEIPROC glad_glBlendEquationSeparatei = NULL; PFNGLBLENDEQUATIONSEPARATEIARBPROC glad_glBlendEquationSeparateiARB = NULL; PFNGLBLENDEQUATIONIPROC glad_glBlendEquationi = NULL; PFNGLBLENDEQUATIONIARBPROC glad_glBlendEquationiARB = NULL; PFNGLBLENDFUNCPROC glad_glBlendFunc = NULL; PFNGLBLENDFUNCSEPARATEPROC glad_glBlendFuncSeparate = NULL; PFNGLBLENDFUNCSEPARATEIPROC glad_glBlendFuncSeparatei = NULL; PFNGLBLENDFUNCSEPARATEIARBPROC glad_glBlendFuncSeparateiARB = NULL; PFNGLBLENDFUNCIPROC glad_glBlendFunci = NULL; PFNGLBLENDFUNCIARBPROC glad_glBlendFunciARB = NULL; PFNGLBLITFRAMEBUFFERPROC glad_glBlitFramebuffer = NULL; PFNGLBLITNAMEDFRAMEBUFFERPROC glad_glBlitNamedFramebuffer = NULL; PFNGLBUFFERDATAPROC glad_glBufferData = NULL; PFNGLBUFFERDATAARBPROC glad_glBufferDataARB = NULL; PFNGLBUFFERPAGECOMMITMENTARBPROC glad_glBufferPageCommitmentARB = NULL; PFNGLBUFFERSTORAGEPROC glad_glBufferStorage = NULL; PFNGLBUFFERSUBDATAPROC glad_glBufferSubData = NULL; PFNGLBUFFERSUBDATAARBPROC glad_glBufferSubDataARB = NULL; PFNGLCALLLISTPROC glad_glCallList = NULL; PFNGLCALLLISTSPROC glad_glCallLists = NULL; PFNGLCHECKFRAMEBUFFERSTATUSPROC glad_glCheckFramebufferStatus = NULL; PFNGLCHECKNAMEDFRAMEBUFFERSTATUSPROC glad_glCheckNamedFramebufferStatus = NULL; PFNGLCLAMPCOLORPROC glad_glClampColor = NULL; PFNGLCLAMPCOLORARBPROC glad_glClampColorARB = NULL; PFNGLCLEARPROC glad_glClear = NULL; PFNGLCLEARACCUMPROC glad_glClearAccum = NULL; PFNGLCLEARBUFFERDATAPROC glad_glClearBufferData = NULL; PFNGLCLEARBUFFERSUBDATAPROC glad_glClearBufferSubData = NULL; PFNGLCLEARBUFFERFIPROC glad_glClearBufferfi = NULL; PFNGLCLEARBUFFERFVPROC glad_glClearBufferfv = NULL; PFNGLCLEARBUFFERIVPROC glad_glClearBufferiv = NULL; PFNGLCLEARBUFFERUIVPROC glad_glClearBufferuiv = NULL; PFNGLCLEARCOLORPROC glad_glClearColor = NULL; PFNGLCLEARDEPTHPROC glad_glClearDepth = NULL; PFNGLCLEARDEPTHFPROC glad_glClearDepthf = NULL; PFNGLCLEARINDEXPROC glad_glClearIndex = NULL; PFNGLCLEARNAMEDBUFFERDATAPROC glad_glClearNamedBufferData = NULL; PFNGLCLEARNAMEDBUFFERSUBDATAPROC glad_glClearNamedBufferSubData = NULL; PFNGLCLEARNAMEDFRAMEBUFFERFIPROC glad_glClearNamedFramebufferfi = NULL; PFNGLCLEARNAMEDFRAMEBUFFERFVPROC glad_glClearNamedFramebufferfv = NULL; PFNGLCLEARNAMEDFRAMEBUFFERIVPROC glad_glClearNamedFramebufferiv = NULL; PFNGLCLEARNAMEDFRAMEBUFFERUIVPROC glad_glClearNamedFramebufferuiv = NULL; PFNGLCLEARSTENCILPROC glad_glClearStencil = NULL; PFNGLCLEARTEXIMAGEPROC glad_glClearTexImage = NULL; PFNGLCLEARTEXSUBIMAGEPROC glad_glClearTexSubImage = NULL; PFNGLCLIENTACTIVETEXTUREPROC glad_glClientActiveTexture = NULL; PFNGLCLIENTACTIVETEXTUREARBPROC glad_glClientActiveTextureARB = NULL; PFNGLCLIENTWAITSYNCPROC glad_glClientWaitSync = NULL; PFNGLCLIPCONTROLPROC glad_glClipControl = NULL; PFNGLCLIPPLANEPROC glad_glClipPlane = NULL; PFNGLCOLOR3BPROC glad_glColor3b = NULL; PFNGLCOLOR3BVPROC glad_glColor3bv = NULL; PFNGLCOLOR3DPROC glad_glColor3d = NULL; PFNGLCOLOR3DVPROC glad_glColor3dv = NULL; PFNGLCOLOR3FPROC glad_glColor3f = NULL; PFNGLCOLOR3FVPROC glad_glColor3fv = NULL; PFNGLCOLOR3IPROC glad_glColor3i = NULL; PFNGLCOLOR3IVPROC glad_glColor3iv = NULL; PFNGLCOLOR3SPROC glad_glColor3s = NULL; PFNGLCOLOR3SVPROC glad_glColor3sv = NULL; PFNGLCOLOR3UBPROC glad_glColor3ub = NULL; PFNGLCOLOR3UBVPROC glad_glColor3ubv = NULL; PFNGLCOLOR3UIPROC glad_glColor3ui = NULL; PFNGLCOLOR3UIVPROC glad_glColor3uiv = NULL; PFNGLCOLOR3USPROC glad_glColor3us = NULL; PFNGLCOLOR3USVPROC glad_glColor3usv = NULL; PFNGLCOLOR4BPROC glad_glColor4b = NULL; PFNGLCOLOR4BVPROC glad_glColor4bv = NULL; PFNGLCOLOR4DPROC glad_glColor4d = NULL; PFNGLCOLOR4DVPROC glad_glColor4dv = NULL; PFNGLCOLOR4FPROC glad_glColor4f = NULL; PFNGLCOLOR4FVPROC glad_glColor4fv = NULL; PFNGLCOLOR4IPROC glad_glColor4i = NULL; PFNGLCOLOR4IVPROC glad_glColor4iv = NULL; PFNGLCOLOR4SPROC glad_glColor4s = NULL; PFNGLCOLOR4SVPROC glad_glColor4sv = NULL; PFNGLCOLOR4UBPROC glad_glColor4ub = NULL; PFNGLCOLOR4UBVPROC glad_glColor4ubv = NULL; PFNGLCOLOR4UIPROC glad_glColor4ui = NULL; PFNGLCOLOR4UIVPROC glad_glColor4uiv = NULL; PFNGLCOLOR4USPROC glad_glColor4us = NULL; PFNGLCOLOR4USVPROC glad_glColor4usv = NULL; PFNGLCOLORMASKPROC glad_glColorMask = NULL; PFNGLCOLORMASKIPROC glad_glColorMaski = NULL; PFNGLCOLORMATERIALPROC glad_glColorMaterial = NULL; PFNGLCOLORP3UIPROC glad_glColorP3ui = NULL; PFNGLCOLORP3UIVPROC glad_glColorP3uiv = NULL; PFNGLCOLORP4UIPROC glad_glColorP4ui = NULL; PFNGLCOLORP4UIVPROC glad_glColorP4uiv = NULL; PFNGLCOLORPOINTERPROC glad_glColorPointer = NULL; PFNGLCOLORSUBTABLEPROC glad_glColorSubTable = NULL; PFNGLCOLORTABLEPROC glad_glColorTable = NULL; PFNGLCOLORTABLEPARAMETERFVPROC glad_glColorTableParameterfv = NULL; PFNGLCOLORTABLEPARAMETERIVPROC glad_glColorTableParameteriv = NULL; PFNGLCOMPILESHADERPROC glad_glCompileShader = NULL; PFNGLCOMPILESHADERARBPROC glad_glCompileShaderARB = NULL; PFNGLCOMPILESHADERINCLUDEARBPROC glad_glCompileShaderIncludeARB = NULL; PFNGLCOMPRESSEDTEXIMAGE1DPROC glad_glCompressedTexImage1D = NULL; PFNGLCOMPRESSEDTEXIMAGE1DARBPROC glad_glCompressedTexImage1DARB = NULL; PFNGLCOMPRESSEDTEXIMAGE2DPROC glad_glCompressedTexImage2D = NULL; PFNGLCOMPRESSEDTEXIMAGE2DARBPROC glad_glCompressedTexImage2DARB = NULL; PFNGLCOMPRESSEDTEXIMAGE3DPROC glad_glCompressedTexImage3D = NULL; PFNGLCOMPRESSEDTEXIMAGE3DARBPROC glad_glCompressedTexImage3DARB = NULL; PFNGLCOMPRESSEDTEXSUBIMAGE1DPROC glad_glCompressedTexSubImage1D = NULL; PFNGLCOMPRESSEDTEXSUBIMAGE1DARBPROC glad_glCompressedTexSubImage1DARB = NULL; PFNGLCOMPRESSEDTEXSUBIMAGE2DPROC glad_glCompressedTexSubImage2D = NULL; PFNGLCOMPRESSEDTEXSUBIMAGE2DARBPROC glad_glCompressedTexSubImage2DARB = NULL; PFNGLCOMPRESSEDTEXSUBIMAGE3DPROC glad_glCompressedTexSubImage3D = NULL; PFNGLCOMPRESSEDTEXSUBIMAGE3DARBPROC glad_glCompressedTexSubImage3DARB = NULL; PFNGLCOMPRESSEDTEXTURESUBIMAGE1DPROC glad_glCompressedTextureSubImage1D = NULL; PFNGLCOMPRESSEDTEXTURESUBIMAGE2DPROC glad_glCompressedTextureSubImage2D = NULL; PFNGLCOMPRESSEDTEXTURESUBIMAGE3DPROC glad_glCompressedTextureSubImage3D = NULL; PFNGLCONVOLUTIONFILTER1DPROC glad_glConvolutionFilter1D = NULL; PFNGLCONVOLUTIONFILTER2DPROC glad_glConvolutionFilter2D = NULL; PFNGLCONVOLUTIONPARAMETERFPROC glad_glConvolutionParameterf = NULL; PFNGLCONVOLUTIONPARAMETERFVPROC glad_glConvolutionParameterfv = NULL; PFNGLCONVOLUTIONPARAMETERIPROC glad_glConvolutionParameteri = NULL; PFNGLCONVOLUTIONPARAMETERIVPROC glad_glConvolutionParameteriv = NULL; PFNGLCOPYBUFFERSUBDATAPROC glad_glCopyBufferSubData = NULL; PFNGLCOPYCOLORSUBTABLEPROC glad_glCopyColorSubTable = NULL; PFNGLCOPYCOLORTABLEPROC glad_glCopyColorTable = NULL; PFNGLCOPYCONVOLUTIONFILTER1DPROC glad_glCopyConvolutionFilter1D = NULL; PFNGLCOPYCONVOLUTIONFILTER2DPROC glad_glCopyConvolutionFilter2D = NULL; PFNGLCOPYIMAGESUBDATAPROC glad_glCopyImageSubData = NULL; PFNGLCOPYNAMEDBUFFERSUBDATAPROC glad_glCopyNamedBufferSubData = NULL; PFNGLCOPYPIXELSPROC glad_glCopyPixels = NULL; PFNGLCOPYTEXIMAGE1DPROC glad_glCopyTexImage1D = NULL; PFNGLCOPYTEXIMAGE2DPROC glad_glCopyTexImage2D = NULL; PFNGLCOPYTEXSUBIMAGE1DPROC glad_glCopyTexSubImage1D = NULL; PFNGLCOPYTEXSUBIMAGE2DPROC glad_glCopyTexSubImage2D = NULL; PFNGLCOPYTEXSUBIMAGE3DPROC glad_glCopyTexSubImage3D = NULL; PFNGLCOPYTEXTURESUBIMAGE1DPROC glad_glCopyTextureSubImage1D = NULL; PFNGLCOPYTEXTURESUBIMAGE2DPROC glad_glCopyTextureSubImage2D = NULL; PFNGLCOPYTEXTURESUBIMAGE3DPROC glad_glCopyTextureSubImage3D = NULL; PFNGLCREATEBUFFERSPROC glad_glCreateBuffers = NULL; PFNGLCREATEFRAMEBUFFERSPROC glad_glCreateFramebuffers = NULL; PFNGLCREATEPROGRAMPROC glad_glCreateProgram = NULL; PFNGLCREATEPROGRAMOBJECTARBPROC glad_glCreateProgramObjectARB = NULL; PFNGLCREATEPROGRAMPIPELINESPROC glad_glCreateProgramPipelines = NULL; PFNGLCREATEQUERIESPROC glad_glCreateQueries = NULL; PFNGLCREATERENDERBUFFERSPROC glad_glCreateRenderbuffers = NULL; PFNGLCREATESAMPLERSPROC glad_glCreateSamplers = NULL; PFNGLCREATESHADERPROC glad_glCreateShader = NULL; PFNGLCREATESHADEROBJECTARBPROC glad_glCreateShaderObjectARB = NULL; PFNGLCREATESHADERPROGRAMVPROC glad_glCreateShaderProgramv = NULL; PFNGLCREATESYNCFROMCLEVENTARBPROC glad_glCreateSyncFromCLeventARB = NULL; PFNGLCREATETEXTURESPROC glad_glCreateTextures = NULL; PFNGLCREATETRANSFORMFEEDBACKSPROC glad_glCreateTransformFeedbacks = NULL; PFNGLCREATEVERTEXARRAYSPROC glad_glCreateVertexArrays = NULL; PFNGLCULLFACEPROC glad_glCullFace = NULL; PFNGLCURRENTPALETTEMATRIXARBPROC glad_glCurrentPaletteMatrixARB = NULL; PFNGLDEBUGMESSAGECALLBACKPROC glad_glDebugMessageCallback = NULL; PFNGLDEBUGMESSAGECALLBACKARBPROC glad_glDebugMessageCallbackARB = NULL; PFNGLDEBUGMESSAGECONTROLPROC glad_glDebugMessageControl = NULL; PFNGLDEBUGMESSAGECONTROLARBPROC glad_glDebugMessageControlARB = NULL; PFNGLDEBUGMESSAGEINSERTPROC glad_glDebugMessageInsert = NULL; PFNGLDEBUGMESSAGEINSERTARBPROC glad_glDebugMessageInsertARB = NULL; PFNGLDELETEBUFFERSPROC glad_glDeleteBuffers = NULL; PFNGLDELETEBUFFERSARBPROC glad_glDeleteBuffersARB = NULL; PFNGLDELETEFRAMEBUFFERSPROC glad_glDeleteFramebuffers = NULL; PFNGLDELETELISTSPROC glad_glDeleteLists = NULL; PFNGLDELETENAMEDSTRINGARBPROC glad_glDeleteNamedStringARB = NULL; PFNGLDELETEOBJECTARBPROC glad_glDeleteObjectARB = NULL; PFNGLDELETEPROGRAMPROC glad_glDeleteProgram = NULL; PFNGLDELETEPROGRAMPIPELINESPROC glad_glDeleteProgramPipelines = NULL; PFNGLDELETEPROGRAMSARBPROC glad_glDeleteProgramsARB = NULL; PFNGLDELETEQUERIESPROC glad_glDeleteQueries = NULL; PFNGLDELETEQUERIESARBPROC glad_glDeleteQueriesARB = NULL; PFNGLDELETERENDERBUFFERSPROC glad_glDeleteRenderbuffers = NULL; PFNGLDELETESAMPLERSPROC glad_glDeleteSamplers = NULL; PFNGLDELETESHADERPROC glad_glDeleteShader = NULL; PFNGLDELETESYNCPROC glad_glDeleteSync = NULL; PFNGLDELETETEXTURESPROC glad_glDeleteTextures = NULL; PFNGLDELETETRANSFORMFEEDBACKSPROC glad_glDeleteTransformFeedbacks = NULL; PFNGLDELETEVERTEXARRAYSPROC glad_glDeleteVertexArrays = NULL; PFNGLDEPTHFUNCPROC glad_glDepthFunc = NULL; PFNGLDEPTHMASKPROC glad_glDepthMask = NULL; PFNGLDEPTHRANGEPROC glad_glDepthRange = NULL; PFNGLDEPTHRANGEARRAYDVNVPROC glad_glDepthRangeArraydvNV = NULL; PFNGLDEPTHRANGEARRAYVPROC glad_glDepthRangeArrayv = NULL; PFNGLDEPTHRANGEINDEXEDPROC glad_glDepthRangeIndexed = NULL; PFNGLDEPTHRANGEINDEXEDDNVPROC glad_glDepthRangeIndexeddNV = NULL; PFNGLDEPTHRANGEFPROC glad_glDepthRangef = NULL; PFNGLDETACHOBJECTARBPROC glad_glDetachObjectARB = NULL; PFNGLDETACHSHADERPROC glad_glDetachShader = NULL; PFNGLDISABLEPROC glad_glDisable = NULL; PFNGLDISABLECLIENTSTATEPROC glad_glDisableClientState = NULL; PFNGLDISABLEVERTEXARRAYATTRIBPROC glad_glDisableVertexArrayAttrib = NULL; PFNGLDISABLEVERTEXATTRIBARRAYPROC glad_glDisableVertexAttribArray = NULL; PFNGLDISABLEVERTEXATTRIBARRAYARBPROC glad_glDisableVertexAttribArrayARB = NULL; PFNGLDISABLEIPROC glad_glDisablei = NULL; PFNGLDISPATCHCOMPUTEPROC glad_glDispatchCompute = NULL; PFNGLDISPATCHCOMPUTEGROUPSIZEARBPROC glad_glDispatchComputeGroupSizeARB = NULL; PFNGLDISPATCHCOMPUTEINDIRECTPROC glad_glDispatchComputeIndirect = NULL; PFNGLDRAWARRAYSPROC glad_glDrawArrays = NULL; PFNGLDRAWARRAYSINDIRECTPROC glad_glDrawArraysIndirect = NULL; PFNGLDRAWARRAYSINSTANCEDPROC glad_glDrawArraysInstanced = NULL; PFNGLDRAWARRAYSINSTANCEDARBPROC glad_glDrawArraysInstancedARB = NULL; PFNGLDRAWARRAYSINSTANCEDBASEINSTANCEPROC glad_glDrawArraysInstancedBaseInstance = NULL; PFNGLDRAWBUFFERPROC glad_glDrawBuffer = NULL; PFNGLDRAWBUFFERSPROC glad_glDrawBuffers = NULL; PFNGLDRAWBUFFERSARBPROC glad_glDrawBuffersARB = NULL; PFNGLDRAWELEMENTSPROC glad_glDrawElements = NULL; PFNGLDRAWELEMENTSBASEVERTEXPROC glad_glDrawElementsBaseVertex = NULL; PFNGLDRAWELEMENTSINDIRECTPROC glad_glDrawElementsIndirect = NULL; PFNGLDRAWELEMENTSINSTANCEDPROC glad_glDrawElementsInstanced = NULL; PFNGLDRAWELEMENTSINSTANCEDARBPROC glad_glDrawElementsInstancedARB = NULL; PFNGLDRAWELEMENTSINSTANCEDBASEINSTANCEPROC glad_glDrawElementsInstancedBaseInstance = NULL; PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXPROC glad_glDrawElementsInstancedBaseVertex = NULL; PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXBASEINSTANCEPROC glad_glDrawElementsInstancedBaseVertexBaseInstance = NULL; PFNGLDRAWPIXELSPROC glad_glDrawPixels = NULL; PFNGLDRAWRANGEELEMENTSPROC glad_glDrawRangeElements = NULL; PFNGLDRAWRANGEELEMENTSBASEVERTEXPROC glad_glDrawRangeElementsBaseVertex = NULL; PFNGLDRAWTRANSFORMFEEDBACKPROC glad_glDrawTransformFeedback = NULL; PFNGLDRAWTRANSFORMFEEDBACKINSTANCEDPROC glad_glDrawTransformFeedbackInstanced = NULL; PFNGLDRAWTRANSFORMFEEDBACKSTREAMPROC glad_glDrawTransformFeedbackStream = NULL; PFNGLDRAWTRANSFORMFEEDBACKSTREAMINSTANCEDPROC glad_glDrawTransformFeedbackStreamInstanced = NULL; PFNGLEDGEFLAGPROC glad_glEdgeFlag = NULL; PFNGLEDGEFLAGPOINTERPROC glad_glEdgeFlagPointer = NULL; PFNGLEDGEFLAGVPROC glad_glEdgeFlagv = NULL; PFNGLENABLEPROC glad_glEnable = NULL; PFNGLENABLECLIENTSTATEPROC glad_glEnableClientState = NULL; PFNGLENABLEVERTEXARRAYATTRIBPROC glad_glEnableVertexArrayAttrib = NULL; PFNGLENABLEVERTEXATTRIBARRAYPROC glad_glEnableVertexAttribArray = NULL; PFNGLENABLEVERTEXATTRIBARRAYARBPROC glad_glEnableVertexAttribArrayARB = NULL; PFNGLENABLEIPROC glad_glEnablei = NULL; PFNGLENDPROC glad_glEnd = NULL; PFNGLENDCONDITIONALRENDERPROC glad_glEndConditionalRender = NULL; PFNGLENDLISTPROC glad_glEndList = NULL; PFNGLENDQUERYPROC glad_glEndQuery = NULL; PFNGLENDQUERYARBPROC glad_glEndQueryARB = NULL; PFNGLENDQUERYINDEXEDPROC glad_glEndQueryIndexed = NULL; PFNGLENDTRANSFORMFEEDBACKPROC glad_glEndTransformFeedback = NULL; PFNGLEVALCOORD1DPROC glad_glEvalCoord1d = NULL; PFNGLEVALCOORD1DVPROC glad_glEvalCoord1dv = NULL; PFNGLEVALCOORD1FPROC glad_glEvalCoord1f = NULL; PFNGLEVALCOORD1FVPROC glad_glEvalCoord1fv = NULL; PFNGLEVALCOORD2DPROC glad_glEvalCoord2d = NULL; PFNGLEVALCOORD2DVPROC glad_glEvalCoord2dv = NULL; PFNGLEVALCOORD2FPROC glad_glEvalCoord2f = NULL; PFNGLEVALCOORD2FVPROC glad_glEvalCoord2fv = NULL; PFNGLEVALMESH1PROC glad_glEvalMesh1 = NULL; PFNGLEVALMESH2PROC glad_glEvalMesh2 = NULL; PFNGLEVALPOINT1PROC glad_glEvalPoint1 = NULL; PFNGLEVALPOINT2PROC glad_glEvalPoint2 = NULL; PFNGLEVALUATEDEPTHVALUESARBPROC glad_glEvaluateDepthValuesARB = NULL; PFNGLFEEDBACKBUFFERPROC glad_glFeedbackBuffer = NULL; PFNGLFENCESYNCPROC glad_glFenceSync = NULL; PFNGLFINISHPROC glad_glFinish = NULL; PFNGLFLUSHPROC glad_glFlush = NULL; PFNGLFLUSHMAPPEDBUFFERRANGEPROC glad_glFlushMappedBufferRange = NULL; PFNGLFLUSHMAPPEDNAMEDBUFFERRANGEPROC glad_glFlushMappedNamedBufferRange = NULL; PFNGLFOGCOORDPOINTERPROC glad_glFogCoordPointer = NULL; PFNGLFOGCOORDDPROC glad_glFogCoordd = NULL; PFNGLFOGCOORDDVPROC glad_glFogCoorddv = NULL; PFNGLFOGCOORDFPROC glad_glFogCoordf = NULL; PFNGLFOGCOORDFVPROC glad_glFogCoordfv = NULL; PFNGLFOGFPROC glad_glFogf = NULL; PFNGLFOGFVPROC glad_glFogfv = NULL; PFNGLFOGIPROC glad_glFogi = NULL; PFNGLFOGIVPROC glad_glFogiv = NULL; PFNGLFRAMEBUFFERPARAMETERIPROC glad_glFramebufferParameteri = NULL; PFNGLFRAMEBUFFERRENDERBUFFERPROC glad_glFramebufferRenderbuffer = NULL; PFNGLFRAMEBUFFERSAMPLELOCATIONSFVARBPROC glad_glFramebufferSampleLocationsfvARB = NULL; PFNGLFRAMEBUFFERTEXTUREPROC glad_glFramebufferTexture = NULL; PFNGLFRAMEBUFFERTEXTURE1DPROC glad_glFramebufferTexture1D = NULL; PFNGLFRAMEBUFFERTEXTURE2DPROC glad_glFramebufferTexture2D = NULL; PFNGLFRAMEBUFFERTEXTURE3DPROC glad_glFramebufferTexture3D = NULL; PFNGLFRAMEBUFFERTEXTUREARBPROC glad_glFramebufferTextureARB = NULL; PFNGLFRAMEBUFFERTEXTUREFACEARBPROC glad_glFramebufferTextureFaceARB = NULL; PFNGLFRAMEBUFFERTEXTURELAYERPROC glad_glFramebufferTextureLayer = NULL; PFNGLFRAMEBUFFERTEXTURELAYERARBPROC glad_glFramebufferTextureLayerARB = NULL; PFNGLFRONTFACEPROC glad_glFrontFace = NULL; PFNGLFRUSTUMPROC glad_glFrustum = NULL; PFNGLGENBUFFERSPROC glad_glGenBuffers = NULL; PFNGLGENBUFFERSARBPROC glad_glGenBuffersARB = NULL; PFNGLGENFRAMEBUFFERSPROC glad_glGenFramebuffers = NULL; PFNGLGENLISTSPROC glad_glGenLists = NULL; PFNGLGENPROGRAMPIPELINESPROC glad_glGenProgramPipelines = NULL; PFNGLGENPROGRAMSARBPROC glad_glGenProgramsARB = NULL; PFNGLGENQUERIESPROC glad_glGenQueries = NULL; PFNGLGENQUERIESARBPROC glad_glGenQueriesARB = NULL; PFNGLGENRENDERBUFFERSPROC glad_glGenRenderbuffers = NULL; PFNGLGENSAMPLERSPROC glad_glGenSamplers = NULL; PFNGLGENTEXTURESPROC glad_glGenTextures = NULL; PFNGLGENTRANSFORMFEEDBACKSPROC glad_glGenTransformFeedbacks = NULL; PFNGLGENVERTEXARRAYSPROC glad_glGenVertexArrays = NULL; PFNGLGENERATEMIPMAPPROC glad_glGenerateMipmap = NULL; PFNGLGENERATETEXTUREMIPMAPPROC glad_glGenerateTextureMipmap = NULL; PFNGLGETACTIVEATOMICCOUNTERBUFFERIVPROC glad_glGetActiveAtomicCounterBufferiv = NULL; PFNGLGETACTIVEATTRIBPROC glad_glGetActiveAttrib = NULL; PFNGLGETACTIVEATTRIBARBPROC glad_glGetActiveAttribARB = NULL; PFNGLGETACTIVESUBROUTINENAMEPROC glad_glGetActiveSubroutineName = NULL; PFNGLGETACTIVESUBROUTINEUNIFORMNAMEPROC glad_glGetActiveSubroutineUniformName = NULL; PFNGLGETACTIVESUBROUTINEUNIFORMIVPROC glad_glGetActiveSubroutineUniformiv = NULL; PFNGLGETACTIVEUNIFORMPROC glad_glGetActiveUniform = NULL; PFNGLGETACTIVEUNIFORMARBPROC glad_glGetActiveUniformARB = NULL; PFNGLGETACTIVEUNIFORMBLOCKNAMEPROC glad_glGetActiveUniformBlockName = NULL; PFNGLGETACTIVEUNIFORMBLOCKIVPROC glad_glGetActiveUniformBlockiv = NULL; PFNGLGETACTIVEUNIFORMNAMEPROC glad_glGetActiveUniformName = NULL; PFNGLGETACTIVEUNIFORMSIVPROC glad_glGetActiveUniformsiv = NULL; PFNGLGETATTACHEDOBJECTSARBPROC glad_glGetAttachedObjectsARB = NULL; PFNGLGETATTACHEDSHADERSPROC glad_glGetAttachedShaders = NULL; PFNGLGETATTRIBLOCATIONPROC glad_glGetAttribLocation = NULL; PFNGLGETATTRIBLOCATIONARBPROC glad_glGetAttribLocationARB = NULL; PFNGLGETBOOLEANI_VPROC glad_glGetBooleani_v = NULL; PFNGLGETBOOLEANVPROC glad_glGetBooleanv = NULL; PFNGLGETBUFFERPARAMETERI64VPROC glad_glGetBufferParameteri64v = NULL; PFNGLGETBUFFERPARAMETERIVPROC glad_glGetBufferParameteriv = NULL; PFNGLGETBUFFERPARAMETERIVARBPROC glad_glGetBufferParameterivARB = NULL; PFNGLGETBUFFERPOINTERVPROC glad_glGetBufferPointerv = NULL; PFNGLGETBUFFERPOINTERVARBPROC glad_glGetBufferPointervARB = NULL; PFNGLGETBUFFERSUBDATAPROC glad_glGetBufferSubData = NULL; PFNGLGETBUFFERSUBDATAARBPROC glad_glGetBufferSubDataARB = NULL; PFNGLGETCLIPPLANEPROC glad_glGetClipPlane = NULL; PFNGLGETCOLORTABLEPROC glad_glGetColorTable = NULL; PFNGLGETCOLORTABLEPARAMETERFVPROC glad_glGetColorTableParameterfv = NULL; PFNGLGETCOLORTABLEPARAMETERIVPROC glad_glGetColorTableParameteriv = NULL; PFNGLGETCOMPRESSEDTEXIMAGEPROC glad_glGetCompressedTexImage = NULL; PFNGLGETCOMPRESSEDTEXIMAGEARBPROC glad_glGetCompressedTexImageARB = NULL; PFNGLGETCOMPRESSEDTEXTUREIMAGEPROC glad_glGetCompressedTextureImage = NULL; PFNGLGETCOMPRESSEDTEXTURESUBIMAGEPROC glad_glGetCompressedTextureSubImage = NULL; PFNGLGETCONVOLUTIONFILTERPROC glad_glGetConvolutionFilter = NULL; PFNGLGETCONVOLUTIONPARAMETERFVPROC glad_glGetConvolutionParameterfv = NULL; PFNGLGETCONVOLUTIONPARAMETERIVPROC glad_glGetConvolutionParameteriv = NULL; PFNGLGETDEBUGMESSAGELOGPROC glad_glGetDebugMessageLog = NULL; PFNGLGETDEBUGMESSAGELOGARBPROC glad_glGetDebugMessageLogARB = NULL; PFNGLGETDOUBLEI_VPROC glad_glGetDoublei_v = NULL; PFNGLGETDOUBLEVPROC glad_glGetDoublev = NULL; PFNGLGETERRORPROC glad_glGetError = NULL; PFNGLGETFLOATI_VPROC glad_glGetFloati_v = NULL; PFNGLGETFLOATVPROC glad_glGetFloatv = NULL; PFNGLGETFRAGDATAINDEXPROC glad_glGetFragDataIndex = NULL; PFNGLGETFRAGDATALOCATIONPROC glad_glGetFragDataLocation = NULL; PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVPROC glad_glGetFramebufferAttachmentParameteriv = NULL; PFNGLGETFRAMEBUFFERPARAMETERIVPROC glad_glGetFramebufferParameteriv = NULL; PFNGLGETGRAPHICSRESETSTATUSPROC glad_glGetGraphicsResetStatus = NULL; PFNGLGETGRAPHICSRESETSTATUSARBPROC glad_glGetGraphicsResetStatusARB = NULL; PFNGLGETHANDLEARBPROC glad_glGetHandleARB = NULL; PFNGLGETHISTOGRAMPROC glad_glGetHistogram = NULL; PFNGLGETHISTOGRAMPARAMETERFVPROC glad_glGetHistogramParameterfv = NULL; PFNGLGETHISTOGRAMPARAMETERIVPROC glad_glGetHistogramParameteriv = NULL; PFNGLGETIMAGEHANDLEARBPROC glad_glGetImageHandleARB = NULL; PFNGLGETINFOLOGARBPROC glad_glGetInfoLogARB = NULL; PFNGLGETINTEGER64I_VPROC glad_glGetInteger64i_v = NULL; PFNGLGETINTEGER64VPROC glad_glGetInteger64v = NULL; PFNGLGETINTEGERI_VPROC glad_glGetIntegeri_v = NULL; PFNGLGETINTEGERVPROC glad_glGetIntegerv = NULL; PFNGLGETINTERNALFORMATI64VPROC glad_glGetInternalformati64v = NULL; PFNGLGETINTERNALFORMATIVPROC glad_glGetInternalformativ = NULL; PFNGLGETLIGHTFVPROC glad_glGetLightfv = NULL; PFNGLGETLIGHTIVPROC glad_glGetLightiv = NULL; PFNGLGETMAPDVPROC glad_glGetMapdv = NULL; PFNGLGETMAPFVPROC glad_glGetMapfv = NULL; PFNGLGETMAPIVPROC glad_glGetMapiv = NULL; PFNGLGETMATERIALFVPROC glad_glGetMaterialfv = NULL; PFNGLGETMATERIALIVPROC glad_glGetMaterialiv = NULL; PFNGLGETMINMAXPROC glad_glGetMinmax = NULL; PFNGLGETMINMAXPARAMETERFVPROC glad_glGetMinmaxParameterfv = NULL; PFNGLGETMINMAXPARAMETERIVPROC glad_glGetMinmaxParameteriv = NULL; PFNGLGETMULTISAMPLEFVPROC glad_glGetMultisamplefv = NULL; PFNGLGETNAMEDBUFFERPARAMETERI64VPROC glad_glGetNamedBufferParameteri64v = NULL; PFNGLGETNAMEDBUFFERPARAMETERIVPROC glad_glGetNamedBufferParameteriv = NULL; PFNGLGETNAMEDBUFFERPOINTERVPROC glad_glGetNamedBufferPointerv = NULL; PFNGLGETNAMEDBUFFERSUBDATAPROC glad_glGetNamedBufferSubData = NULL; PFNGLGETNAMEDFRAMEBUFFERATTACHMENTPARAMETERIVPROC glad_glGetNamedFramebufferAttachmentParameteriv = NULL; PFNGLGETNAMEDFRAMEBUFFERPARAMETERIVPROC glad_glGetNamedFramebufferParameteriv = NULL; PFNGLGETNAMEDRENDERBUFFERPARAMETERIVPROC glad_glGetNamedRenderbufferParameteriv = NULL; PFNGLGETNAMEDSTRINGARBPROC glad_glGetNamedStringARB = NULL; PFNGLGETNAMEDSTRINGIVARBPROC glad_glGetNamedStringivARB = NULL; PFNGLGETOBJECTLABELPROC glad_glGetObjectLabel = NULL; PFNGLGETOBJECTPARAMETERFVARBPROC glad_glGetObjectParameterfvARB = NULL; PFNGLGETOBJECTPARAMETERIVARBPROC glad_glGetObjectParameterivARB = NULL; PFNGLGETOBJECTPTRLABELPROC glad_glGetObjectPtrLabel = NULL; PFNGLGETPIXELMAPFVPROC glad_glGetPixelMapfv = NULL; PFNGLGETPIXELMAPUIVPROC glad_glGetPixelMapuiv = NULL; PFNGLGETPIXELMAPUSVPROC glad_glGetPixelMapusv = NULL; PFNGLGETPOINTERVPROC glad_glGetPointerv = NULL; PFNGLGETPOLYGONSTIPPLEPROC glad_glGetPolygonStipple = NULL; PFNGLGETPROGRAMBINARYPROC glad_glGetProgramBinary = NULL; PFNGLGETPROGRAMENVPARAMETERDVARBPROC glad_glGetProgramEnvParameterdvARB = NULL; PFNGLGETPROGRAMENVPARAMETERFVARBPROC glad_glGetProgramEnvParameterfvARB = NULL; PFNGLGETPROGRAMINFOLOGPROC glad_glGetProgramInfoLog = NULL; PFNGLGETPROGRAMINTERFACEIVPROC glad_glGetProgramInterfaceiv = NULL; PFNGLGETPROGRAMLOCALPARAMETERDVARBPROC glad_glGetProgramLocalParameterdvARB = NULL; PFNGLGETPROGRAMLOCALPARAMETERFVARBPROC glad_glGetProgramLocalParameterfvARB = NULL; PFNGLGETPROGRAMPIPELINEINFOLOGPROC glad_glGetProgramPipelineInfoLog = NULL; PFNGLGETPROGRAMPIPELINEIVPROC glad_glGetProgramPipelineiv = NULL; PFNGLGETPROGRAMRESOURCEINDEXPROC glad_glGetProgramResourceIndex = NULL; PFNGLGETPROGRAMRESOURCELOCATIONPROC glad_glGetProgramResourceLocation = NULL; PFNGLGETPROGRAMRESOURCELOCATIONINDEXPROC glad_glGetProgramResourceLocationIndex = NULL; PFNGLGETPROGRAMRESOURCENAMEPROC glad_glGetProgramResourceName = NULL; PFNGLGETPROGRAMRESOURCEIVPROC glad_glGetProgramResourceiv = NULL; PFNGLGETPROGRAMSTAGEIVPROC glad_glGetProgramStageiv = NULL; PFNGLGETPROGRAMSTRINGARBPROC glad_glGetProgramStringARB = NULL; PFNGLGETPROGRAMIVPROC glad_glGetProgramiv = NULL; PFNGLGETPROGRAMIVARBPROC glad_glGetProgramivARB = NULL; PFNGLGETQUERYBUFFEROBJECTI64VPROC glad_glGetQueryBufferObjecti64v = NULL; PFNGLGETQUERYBUFFEROBJECTIVPROC glad_glGetQueryBufferObjectiv = NULL; PFNGLGETQUERYBUFFEROBJECTUI64VPROC glad_glGetQueryBufferObjectui64v = NULL; PFNGLGETQUERYBUFFEROBJECTUIVPROC glad_glGetQueryBufferObjectuiv = NULL; PFNGLGETQUERYINDEXEDIVPROC glad_glGetQueryIndexediv = NULL; PFNGLGETQUERYOBJECTI64VPROC glad_glGetQueryObjecti64v = NULL; PFNGLGETQUERYOBJECTIVPROC glad_glGetQueryObjectiv = NULL; PFNGLGETQUERYOBJECTIVARBPROC glad_glGetQueryObjectivARB = NULL; PFNGLGETQUERYOBJECTUI64VPROC glad_glGetQueryObjectui64v = NULL; PFNGLGETQUERYOBJECTUIVPROC glad_glGetQueryObjectuiv = NULL; PFNGLGETQUERYOBJECTUIVARBPROC glad_glGetQueryObjectuivARB = NULL; PFNGLGETQUERYIVPROC glad_glGetQueryiv = NULL; PFNGLGETQUERYIVARBPROC glad_glGetQueryivARB = NULL; PFNGLGETRENDERBUFFERPARAMETERIVPROC glad_glGetRenderbufferParameteriv = NULL; PFNGLGETSAMPLERPARAMETERIIVPROC glad_glGetSamplerParameterIiv = NULL; PFNGLGETSAMPLERPARAMETERIUIVPROC glad_glGetSamplerParameterIuiv = NULL; PFNGLGETSAMPLERPARAMETERFVPROC glad_glGetSamplerParameterfv = NULL; PFNGLGETSAMPLERPARAMETERIVPROC glad_glGetSamplerParameteriv = NULL; PFNGLGETSEPARABLEFILTERPROC glad_glGetSeparableFilter = NULL; PFNGLGETSHADERINFOLOGPROC glad_glGetShaderInfoLog = NULL; PFNGLGETSHADERPRECISIONFORMATPROC glad_glGetShaderPrecisionFormat = NULL; PFNGLGETSHADERSOURCEPROC glad_glGetShaderSource = NULL; PFNGLGETSHADERSOURCEARBPROC glad_glGetShaderSourceARB = NULL; PFNGLGETSHADERIVPROC glad_glGetShaderiv = NULL; PFNGLGETSTRINGPROC glad_glGetString = NULL; PFNGLGETSTRINGIPROC glad_glGetStringi = NULL; PFNGLGETSUBROUTINEINDEXPROC glad_glGetSubroutineIndex = NULL; PFNGLGETSUBROUTINEUNIFORMLOCATIONPROC glad_glGetSubroutineUniformLocation = NULL; PFNGLGETSYNCIVPROC glad_glGetSynciv = NULL; PFNGLGETTEXENVFVPROC glad_glGetTexEnvfv = NULL; PFNGLGETTEXENVIVPROC glad_glGetTexEnviv = NULL; PFNGLGETTEXGENDVPROC glad_glGetTexGendv = NULL; PFNGLGETTEXGENFVPROC glad_glGetTexGenfv = NULL; PFNGLGETTEXGENIVPROC glad_glGetTexGeniv = NULL; PFNGLGETTEXIMAGEPROC glad_glGetTexImage = NULL; PFNGLGETTEXLEVELPARAMETERFVPROC glad_glGetTexLevelParameterfv = NULL; PFNGLGETTEXLEVELPARAMETERIVPROC glad_glGetTexLevelParameteriv = NULL; PFNGLGETTEXPARAMETERIIVPROC glad_glGetTexParameterIiv = NULL; PFNGLGETTEXPARAMETERIUIVPROC glad_glGetTexParameterIuiv = NULL; PFNGLGETTEXPARAMETERFVPROC glad_glGetTexParameterfv = NULL; PFNGLGETTEXPARAMETERIVPROC glad_glGetTexParameteriv = NULL; PFNGLGETTEXTUREHANDLEARBPROC glad_glGetTextureHandleARB = NULL; PFNGLGETTEXTUREIMAGEPROC glad_glGetTextureImage = NULL; PFNGLGETTEXTURELEVELPARAMETERFVPROC glad_glGetTextureLevelParameterfv = NULL; PFNGLGETTEXTURELEVELPARAMETERIVPROC glad_glGetTextureLevelParameteriv = NULL; PFNGLGETTEXTUREPARAMETERIIVPROC glad_glGetTextureParameterIiv = NULL; PFNGLGETTEXTUREPARAMETERIUIVPROC glad_glGetTextureParameterIuiv = NULL; PFNGLGETTEXTUREPARAMETERFVPROC glad_glGetTextureParameterfv = NULL; PFNGLGETTEXTUREPARAMETERIVPROC glad_glGetTextureParameteriv = NULL; PFNGLGETTEXTURESAMPLERHANDLEARBPROC glad_glGetTextureSamplerHandleARB = NULL; PFNGLGETTEXTURESUBIMAGEPROC glad_glGetTextureSubImage = NULL; PFNGLGETTRANSFORMFEEDBACKVARYINGPROC glad_glGetTransformFeedbackVarying = NULL; PFNGLGETTRANSFORMFEEDBACKI64_VPROC glad_glGetTransformFeedbacki64_v = NULL; PFNGLGETTRANSFORMFEEDBACKI_VPROC glad_glGetTransformFeedbacki_v = NULL; PFNGLGETTRANSFORMFEEDBACKIVPROC glad_glGetTransformFeedbackiv = NULL; PFNGLGETUNIFORMBLOCKINDEXPROC glad_glGetUniformBlockIndex = NULL; PFNGLGETUNIFORMINDICESPROC glad_glGetUniformIndices = NULL; PFNGLGETUNIFORMLOCATIONPROC glad_glGetUniformLocation = NULL; PFNGLGETUNIFORMLOCATIONARBPROC glad_glGetUniformLocationARB = NULL; PFNGLGETUNIFORMSUBROUTINEUIVPROC glad_glGetUniformSubroutineuiv = NULL; PFNGLGETUNIFORMDVPROC glad_glGetUniformdv = NULL; PFNGLGETUNIFORMFVPROC glad_glGetUniformfv = NULL; PFNGLGETUNIFORMFVARBPROC glad_glGetUniformfvARB = NULL; PFNGLGETUNIFORMI64VARBPROC glad_glGetUniformi64vARB = NULL; PFNGLGETUNIFORMIVPROC glad_glGetUniformiv = NULL; PFNGLGETUNIFORMIVARBPROC glad_glGetUniformivARB = NULL; PFNGLGETUNIFORMUI64VARBPROC glad_glGetUniformui64vARB = NULL; PFNGLGETUNIFORMUIVPROC glad_glGetUniformuiv = NULL; PFNGLGETVERTEXARRAYINDEXED64IVPROC glad_glGetVertexArrayIndexed64iv = NULL; PFNGLGETVERTEXARRAYINDEXEDIVPROC glad_glGetVertexArrayIndexediv = NULL; PFNGLGETVERTEXARRAYIVPROC glad_glGetVertexArrayiv = NULL; PFNGLGETVERTEXATTRIBIIVPROC glad_glGetVertexAttribIiv = NULL; PFNGLGETVERTEXATTRIBIUIVPROC glad_glGetVertexAttribIuiv = NULL; PFNGLGETVERTEXATTRIBLDVPROC glad_glGetVertexAttribLdv = NULL; PFNGLGETVERTEXATTRIBLUI64VARBPROC glad_glGetVertexAttribLui64vARB = NULL; PFNGLGETVERTEXATTRIBPOINTERVPROC glad_glGetVertexAttribPointerv = NULL; PFNGLGETVERTEXATTRIBPOINTERVARBPROC glad_glGetVertexAttribPointervARB = NULL; PFNGLGETVERTEXATTRIBDVPROC glad_glGetVertexAttribdv = NULL; PFNGLGETVERTEXATTRIBDVARBPROC glad_glGetVertexAttribdvARB = NULL; PFNGLGETVERTEXATTRIBFVPROC glad_glGetVertexAttribfv = NULL; PFNGLGETVERTEXATTRIBFVARBPROC glad_glGetVertexAttribfvARB = NULL; PFNGLGETVERTEXATTRIBIVPROC glad_glGetVertexAttribiv = NULL; PFNGLGETVERTEXATTRIBIVARBPROC glad_glGetVertexAttribivARB = NULL; PFNGLGETNCOLORTABLEARBPROC glad_glGetnColorTableARB = NULL; PFNGLGETNCOMPRESSEDTEXIMAGEARBPROC glad_glGetnCompressedTexImageARB = NULL; PFNGLGETNCONVOLUTIONFILTERARBPROC glad_glGetnConvolutionFilterARB = NULL; PFNGLGETNHISTOGRAMARBPROC glad_glGetnHistogramARB = NULL; PFNGLGETNMAPDVARBPROC glad_glGetnMapdvARB = NULL; PFNGLGETNMAPFVARBPROC glad_glGetnMapfvARB = NULL; PFNGLGETNMAPIVARBPROC glad_glGetnMapivARB = NULL; PFNGLGETNMINMAXARBPROC glad_glGetnMinmaxARB = NULL; PFNGLGETNPIXELMAPFVARBPROC glad_glGetnPixelMapfvARB = NULL; PFNGLGETNPIXELMAPUIVARBPROC glad_glGetnPixelMapuivARB = NULL; PFNGLGETNPIXELMAPUSVARBPROC glad_glGetnPixelMapusvARB = NULL; PFNGLGETNPOLYGONSTIPPLEARBPROC glad_glGetnPolygonStippleARB = NULL; PFNGLGETNSEPARABLEFILTERARBPROC glad_glGetnSeparableFilterARB = NULL; PFNGLGETNTEXIMAGEARBPROC glad_glGetnTexImageARB = NULL; PFNGLGETNUNIFORMDVARBPROC glad_glGetnUniformdvARB = NULL; PFNGLGETNUNIFORMFVPROC glad_glGetnUniformfv = NULL; PFNGLGETNUNIFORMFVARBPROC glad_glGetnUniformfvARB = NULL; PFNGLGETNUNIFORMI64VARBPROC glad_glGetnUniformi64vARB = NULL; PFNGLGETNUNIFORMIVPROC glad_glGetnUniformiv = NULL; PFNGLGETNUNIFORMIVARBPROC glad_glGetnUniformivARB = NULL; PFNGLGETNUNIFORMUI64VARBPROC glad_glGetnUniformui64vARB = NULL; PFNGLGETNUNIFORMUIVPROC glad_glGetnUniformuiv = NULL; PFNGLGETNUNIFORMUIVARBPROC glad_glGetnUniformuivARB = NULL; PFNGLHINTPROC glad_glHint = NULL; PFNGLHISTOGRAMPROC glad_glHistogram = NULL; PFNGLINDEXMASKPROC glad_glIndexMask = NULL; PFNGLINDEXPOINTERPROC glad_glIndexPointer = NULL; PFNGLINDEXDPROC glad_glIndexd = NULL; PFNGLINDEXDVPROC glad_glIndexdv = NULL; PFNGLINDEXFPROC glad_glIndexf = NULL; PFNGLINDEXFVPROC glad_glIndexfv = NULL; PFNGLINDEXIPROC glad_glIndexi = NULL; PFNGLINDEXIVPROC glad_glIndexiv = NULL; PFNGLINDEXSPROC glad_glIndexs = NULL; PFNGLINDEXSVPROC glad_glIndexsv = NULL; PFNGLINDEXUBPROC glad_glIndexub = NULL; PFNGLINDEXUBVPROC glad_glIndexubv = NULL; PFNGLINITNAMESPROC glad_glInitNames = NULL; PFNGLINTERLEAVEDARRAYSPROC glad_glInterleavedArrays = NULL; PFNGLINVALIDATEBUFFERDATAPROC glad_glInvalidateBufferData = NULL; PFNGLINVALIDATEBUFFERSUBDATAPROC glad_glInvalidateBufferSubData = NULL; PFNGLINVALIDATEFRAMEBUFFERPROC glad_glInvalidateFramebuffer = NULL; PFNGLINVALIDATENAMEDFRAMEBUFFERDATAPROC glad_glInvalidateNamedFramebufferData = NULL; PFNGLINVALIDATENAMEDFRAMEBUFFERSUBDATAPROC glad_glInvalidateNamedFramebufferSubData = NULL; PFNGLINVALIDATESUBFRAMEBUFFERPROC glad_glInvalidateSubFramebuffer = NULL; PFNGLINVALIDATETEXIMAGEPROC glad_glInvalidateTexImage = NULL; PFNGLINVALIDATETEXSUBIMAGEPROC glad_glInvalidateTexSubImage = NULL; PFNGLISBUFFERPROC glad_glIsBuffer = NULL; PFNGLISBUFFERARBPROC glad_glIsBufferARB = NULL; PFNGLISENABLEDPROC glad_glIsEnabled = NULL; PFNGLISENABLEDIPROC glad_glIsEnabledi = NULL; PFNGLISFRAMEBUFFERPROC glad_glIsFramebuffer = NULL; PFNGLISIMAGEHANDLERESIDENTARBPROC glad_glIsImageHandleResidentARB = NULL; PFNGLISLISTPROC glad_glIsList = NULL; PFNGLISNAMEDSTRINGARBPROC glad_glIsNamedStringARB = NULL; PFNGLISPROGRAMPROC glad_glIsProgram = NULL; PFNGLISPROGRAMARBPROC glad_glIsProgramARB = NULL; PFNGLISPROGRAMPIPELINEPROC glad_glIsProgramPipeline = NULL; PFNGLISQUERYPROC glad_glIsQuery = NULL; PFNGLISQUERYARBPROC glad_glIsQueryARB = NULL; PFNGLISRENDERBUFFERPROC glad_glIsRenderbuffer = NULL; PFNGLISSAMPLERPROC glad_glIsSampler = NULL; PFNGLISSHADERPROC glad_glIsShader = NULL; PFNGLISSYNCPROC glad_glIsSync = NULL; PFNGLISTEXTUREPROC glad_glIsTexture = NULL; PFNGLISTEXTUREHANDLERESIDENTARBPROC glad_glIsTextureHandleResidentARB = NULL; PFNGLISTRANSFORMFEEDBACKPROC glad_glIsTransformFeedback = NULL; PFNGLISVERTEXARRAYPROC glad_glIsVertexArray = NULL; PFNGLLIGHTMODELFPROC glad_glLightModelf = NULL; PFNGLLIGHTMODELFVPROC glad_glLightModelfv = NULL; PFNGLLIGHTMODELIPROC glad_glLightModeli = NULL; PFNGLLIGHTMODELIVPROC glad_glLightModeliv = NULL; PFNGLLIGHTFPROC glad_glLightf = NULL; PFNGLLIGHTFVPROC glad_glLightfv = NULL; PFNGLLIGHTIPROC glad_glLighti = NULL; PFNGLLIGHTIVPROC glad_glLightiv = NULL; PFNGLLINESTIPPLEPROC glad_glLineStipple = NULL; PFNGLLINEWIDTHPROC glad_glLineWidth = NULL; PFNGLLINKPROGRAMPROC glad_glLinkProgram = NULL; PFNGLLINKPROGRAMARBPROC glad_glLinkProgramARB = NULL; PFNGLLISTBASEPROC glad_glListBase = NULL; PFNGLLOADIDENTITYPROC glad_glLoadIdentity = NULL; PFNGLLOADMATRIXDPROC glad_glLoadMatrixd = NULL; PFNGLLOADMATRIXFPROC glad_glLoadMatrixf = NULL; PFNGLLOADNAMEPROC glad_glLoadName = NULL; PFNGLLOADTRANSPOSEMATRIXDPROC glad_glLoadTransposeMatrixd = NULL; PFNGLLOADTRANSPOSEMATRIXDARBPROC glad_glLoadTransposeMatrixdARB = NULL; PFNGLLOADTRANSPOSEMATRIXFPROC glad_glLoadTransposeMatrixf = NULL; PFNGLLOADTRANSPOSEMATRIXFARBPROC glad_glLoadTransposeMatrixfARB = NULL; PFNGLLOGICOPPROC glad_glLogicOp = NULL; PFNGLMAKEIMAGEHANDLENONRESIDENTARBPROC glad_glMakeImageHandleNonResidentARB = NULL; PFNGLMAKEIMAGEHANDLERESIDENTARBPROC glad_glMakeImageHandleResidentARB = NULL; PFNGLMAKETEXTUREHANDLENONRESIDENTARBPROC glad_glMakeTextureHandleNonResidentARB = NULL; PFNGLMAKETEXTUREHANDLERESIDENTARBPROC glad_glMakeTextureHandleResidentARB = NULL; PFNGLMAP1DPROC glad_glMap1d = NULL; PFNGLMAP1FPROC glad_glMap1f = NULL; PFNGLMAP2DPROC glad_glMap2d = NULL; PFNGLMAP2FPROC glad_glMap2f = NULL; PFNGLMAPBUFFERPROC glad_glMapBuffer = NULL; PFNGLMAPBUFFERARBPROC glad_glMapBufferARB = NULL; PFNGLMAPBUFFERRANGEPROC glad_glMapBufferRange = NULL; PFNGLMAPGRID1DPROC glad_glMapGrid1d = NULL; PFNGLMAPGRID1FPROC glad_glMapGrid1f = NULL; PFNGLMAPGRID2DPROC glad_glMapGrid2d = NULL; PFNGLMAPGRID2FPROC glad_glMapGrid2f = NULL; PFNGLMAPNAMEDBUFFERPROC glad_glMapNamedBuffer = NULL; PFNGLMAPNAMEDBUFFERRANGEPROC glad_glMapNamedBufferRange = NULL; PFNGLMATERIALFPROC glad_glMaterialf = NULL; PFNGLMATERIALFVPROC glad_glMaterialfv = NULL; PFNGLMATERIALIPROC glad_glMateriali = NULL; PFNGLMATERIALIVPROC glad_glMaterialiv = NULL; PFNGLMATRIXINDEXPOINTERARBPROC glad_glMatrixIndexPointerARB = NULL; PFNGLMATRIXINDEXUBVARBPROC glad_glMatrixIndexubvARB = NULL; PFNGLMATRIXINDEXUIVARBPROC glad_glMatrixIndexuivARB = NULL; PFNGLMATRIXINDEXUSVARBPROC glad_glMatrixIndexusvARB = NULL; PFNGLMATRIXMODEPROC glad_glMatrixMode = NULL; PFNGLMAXSHADERCOMPILERTHREADSARBPROC glad_glMaxShaderCompilerThreadsARB = NULL; PFNGLMAXSHADERCOMPILERTHREADSKHRPROC glad_glMaxShaderCompilerThreadsKHR = NULL; PFNGLMEMORYBARRIERPROC glad_glMemoryBarrier = NULL; PFNGLMEMORYBARRIERBYREGIONPROC glad_glMemoryBarrierByRegion = NULL; PFNGLMINSAMPLESHADINGPROC glad_glMinSampleShading = NULL; PFNGLMINSAMPLESHADINGARBPROC glad_glMinSampleShadingARB = NULL; PFNGLMINMAXPROC glad_glMinmax = NULL; PFNGLMULTMATRIXDPROC glad_glMultMatrixd = NULL; PFNGLMULTMATRIXFPROC glad_glMultMatrixf = NULL; PFNGLMULTTRANSPOSEMATRIXDPROC glad_glMultTransposeMatrixd = NULL; PFNGLMULTTRANSPOSEMATRIXDARBPROC glad_glMultTransposeMatrixdARB = NULL; PFNGLMULTTRANSPOSEMATRIXFPROC glad_glMultTransposeMatrixf = NULL; PFNGLMULTTRANSPOSEMATRIXFARBPROC glad_glMultTransposeMatrixfARB = NULL; PFNGLMULTIDRAWARRAYSPROC glad_glMultiDrawArrays = NULL; PFNGLMULTIDRAWARRAYSINDIRECTPROC glad_glMultiDrawArraysIndirect = NULL; PFNGLMULTIDRAWARRAYSINDIRECTCOUNTPROC glad_glMultiDrawArraysIndirectCount = NULL; PFNGLMULTIDRAWARRAYSINDIRECTCOUNTARBPROC glad_glMultiDrawArraysIndirectCountARB = NULL; PFNGLMULTIDRAWELEMENTSPROC glad_glMultiDrawElements = NULL; PFNGLMULTIDRAWELEMENTSBASEVERTEXPROC glad_glMultiDrawElementsBaseVertex = NULL; PFNGLMULTIDRAWELEMENTSINDIRECTPROC glad_glMultiDrawElementsIndirect = NULL; PFNGLMULTIDRAWELEMENTSINDIRECTCOUNTPROC glad_glMultiDrawElementsIndirectCount = NULL; PFNGLMULTIDRAWELEMENTSINDIRECTCOUNTARBPROC glad_glMultiDrawElementsIndirectCountARB = NULL; PFNGLMULTITEXCOORD1DPROC glad_glMultiTexCoord1d = NULL; PFNGLMULTITEXCOORD1DARBPROC glad_glMultiTexCoord1dARB = NULL; PFNGLMULTITEXCOORD1DVPROC glad_glMultiTexCoord1dv = NULL; PFNGLMULTITEXCOORD1DVARBPROC glad_glMultiTexCoord1dvARB = NULL; PFNGLMULTITEXCOORD1FPROC glad_glMultiTexCoord1f = NULL; PFNGLMULTITEXCOORD1FARBPROC glad_glMultiTexCoord1fARB = NULL; PFNGLMULTITEXCOORD1FVPROC glad_glMultiTexCoord1fv = NULL; PFNGLMULTITEXCOORD1FVARBPROC glad_glMultiTexCoord1fvARB = NULL; PFNGLMULTITEXCOORD1IPROC glad_glMultiTexCoord1i = NULL; PFNGLMULTITEXCOORD1IARBPROC glad_glMultiTexCoord1iARB = NULL; PFNGLMULTITEXCOORD1IVPROC glad_glMultiTexCoord1iv = NULL; PFNGLMULTITEXCOORD1IVARBPROC glad_glMultiTexCoord1ivARB = NULL; PFNGLMULTITEXCOORD1SPROC glad_glMultiTexCoord1s = NULL; PFNGLMULTITEXCOORD1SARBPROC glad_glMultiTexCoord1sARB = NULL; PFNGLMULTITEXCOORD1SVPROC glad_glMultiTexCoord1sv = NULL; PFNGLMULTITEXCOORD1SVARBPROC glad_glMultiTexCoord1svARB = NULL; PFNGLMULTITEXCOORD2DPROC glad_glMultiTexCoord2d = NULL; PFNGLMULTITEXCOORD2DARBPROC glad_glMultiTexCoord2dARB = NULL; PFNGLMULTITEXCOORD2DVPROC glad_glMultiTexCoord2dv = NULL; PFNGLMULTITEXCOORD2DVARBPROC glad_glMultiTexCoord2dvARB = NULL; PFNGLMULTITEXCOORD2FPROC glad_glMultiTexCoord2f = NULL; PFNGLMULTITEXCOORD2FARBPROC glad_glMultiTexCoord2fARB = NULL; PFNGLMULTITEXCOORD2FVPROC glad_glMultiTexCoord2fv = NULL; PFNGLMULTITEXCOORD2FVARBPROC glad_glMultiTexCoord2fvARB = NULL; PFNGLMULTITEXCOORD2IPROC glad_glMultiTexCoord2i = NULL; PFNGLMULTITEXCOORD2IARBPROC glad_glMultiTexCoord2iARB = NULL; PFNGLMULTITEXCOORD2IVPROC glad_glMultiTexCoord2iv = NULL; PFNGLMULTITEXCOORD2IVARBPROC glad_glMultiTexCoord2ivARB = NULL; PFNGLMULTITEXCOORD2SPROC glad_glMultiTexCoord2s = NULL; PFNGLMULTITEXCOORD2SARBPROC glad_glMultiTexCoord2sARB = NULL; PFNGLMULTITEXCOORD2SVPROC glad_glMultiTexCoord2sv = NULL; PFNGLMULTITEXCOORD2SVARBPROC glad_glMultiTexCoord2svARB = NULL; PFNGLMULTITEXCOORD3DPROC glad_glMultiTexCoord3d = NULL; PFNGLMULTITEXCOORD3DARBPROC glad_glMultiTexCoord3dARB = NULL; PFNGLMULTITEXCOORD3DVPROC glad_glMultiTexCoord3dv = NULL; PFNGLMULTITEXCOORD3DVARBPROC glad_glMultiTexCoord3dvARB = NULL; PFNGLMULTITEXCOORD3FPROC glad_glMultiTexCoord3f = NULL; PFNGLMULTITEXCOORD3FARBPROC glad_glMultiTexCoord3fARB = NULL; PFNGLMULTITEXCOORD3FVPROC glad_glMultiTexCoord3fv = NULL; PFNGLMULTITEXCOORD3FVARBPROC glad_glMultiTexCoord3fvARB = NULL; PFNGLMULTITEXCOORD3IPROC glad_glMultiTexCoord3i = NULL; PFNGLMULTITEXCOORD3IARBPROC glad_glMultiTexCoord3iARB = NULL; PFNGLMULTITEXCOORD3IVPROC glad_glMultiTexCoord3iv = NULL; PFNGLMULTITEXCOORD3IVARBPROC glad_glMultiTexCoord3ivARB = NULL; PFNGLMULTITEXCOORD3SPROC glad_glMultiTexCoord3s = NULL; PFNGLMULTITEXCOORD3SARBPROC glad_glMultiTexCoord3sARB = NULL; PFNGLMULTITEXCOORD3SVPROC glad_glMultiTexCoord3sv = NULL; PFNGLMULTITEXCOORD3SVARBPROC glad_glMultiTexCoord3svARB = NULL; PFNGLMULTITEXCOORD4DPROC glad_glMultiTexCoord4d = NULL; PFNGLMULTITEXCOORD4DARBPROC glad_glMultiTexCoord4dARB = NULL; PFNGLMULTITEXCOORD4DVPROC glad_glMultiTexCoord4dv = NULL; PFNGLMULTITEXCOORD4DVARBPROC glad_glMultiTexCoord4dvARB = NULL; PFNGLMULTITEXCOORD4FPROC glad_glMultiTexCoord4f = NULL; PFNGLMULTITEXCOORD4FARBPROC glad_glMultiTexCoord4fARB = NULL; PFNGLMULTITEXCOORD4FVPROC glad_glMultiTexCoord4fv = NULL; PFNGLMULTITEXCOORD4FVARBPROC glad_glMultiTexCoord4fvARB = NULL; PFNGLMULTITEXCOORD4IPROC glad_glMultiTexCoord4i = NULL; PFNGLMULTITEXCOORD4IARBPROC glad_glMultiTexCoord4iARB = NULL; PFNGLMULTITEXCOORD4IVPROC glad_glMultiTexCoord4iv = NULL; PFNGLMULTITEXCOORD4IVARBPROC glad_glMultiTexCoord4ivARB = NULL; PFNGLMULTITEXCOORD4SPROC glad_glMultiTexCoord4s = NULL; PFNGLMULTITEXCOORD4SARBPROC glad_glMultiTexCoord4sARB = NULL; PFNGLMULTITEXCOORD4SVPROC glad_glMultiTexCoord4sv = NULL; PFNGLMULTITEXCOORD4SVARBPROC glad_glMultiTexCoord4svARB = NULL; PFNGLMULTITEXCOORDP1UIPROC glad_glMultiTexCoordP1ui = NULL; PFNGLMULTITEXCOORDP1UIVPROC glad_glMultiTexCoordP1uiv = NULL; PFNGLMULTITEXCOORDP2UIPROC glad_glMultiTexCoordP2ui = NULL; PFNGLMULTITEXCOORDP2UIVPROC glad_glMultiTexCoordP2uiv = NULL; PFNGLMULTITEXCOORDP3UIPROC glad_glMultiTexCoordP3ui = NULL; PFNGLMULTITEXCOORDP3UIVPROC glad_glMultiTexCoordP3uiv = NULL; PFNGLMULTITEXCOORDP4UIPROC glad_glMultiTexCoordP4ui = NULL; PFNGLMULTITEXCOORDP4UIVPROC glad_glMultiTexCoordP4uiv = NULL; PFNGLNAMEDBUFFERDATAPROC glad_glNamedBufferData = NULL; PFNGLNAMEDBUFFERPAGECOMMITMENTARBPROC glad_glNamedBufferPageCommitmentARB = NULL; PFNGLNAMEDBUFFERPAGECOMMITMENTEXTPROC glad_glNamedBufferPageCommitmentEXT = NULL; PFNGLNAMEDBUFFERSTORAGEPROC glad_glNamedBufferStorage = NULL; PFNGLNAMEDBUFFERSUBDATAPROC glad_glNamedBufferSubData = NULL; PFNGLNAMEDFRAMEBUFFERDRAWBUFFERPROC glad_glNamedFramebufferDrawBuffer = NULL; PFNGLNAMEDFRAMEBUFFERDRAWBUFFERSPROC glad_glNamedFramebufferDrawBuffers = NULL; PFNGLNAMEDFRAMEBUFFERPARAMETERIPROC glad_glNamedFramebufferParameteri = NULL; PFNGLNAMEDFRAMEBUFFERREADBUFFERPROC glad_glNamedFramebufferReadBuffer = NULL; PFNGLNAMEDFRAMEBUFFERRENDERBUFFERPROC glad_glNamedFramebufferRenderbuffer = NULL; PFNGLNAMEDFRAMEBUFFERSAMPLELOCATIONSFVARBPROC glad_glNamedFramebufferSampleLocationsfvARB = NULL; PFNGLNAMEDFRAMEBUFFERTEXTUREPROC glad_glNamedFramebufferTexture = NULL; PFNGLNAMEDFRAMEBUFFERTEXTURELAYERPROC glad_glNamedFramebufferTextureLayer = NULL; PFNGLNAMEDRENDERBUFFERSTORAGEPROC glad_glNamedRenderbufferStorage = NULL; PFNGLNAMEDRENDERBUFFERSTORAGEMULTISAMPLEPROC glad_glNamedRenderbufferStorageMultisample = NULL; PFNGLNAMEDSTRINGARBPROC glad_glNamedStringARB = NULL; PFNGLNEWLISTPROC glad_glNewList = NULL; PFNGLNORMAL3BPROC glad_glNormal3b = NULL; PFNGLNORMAL3BVPROC glad_glNormal3bv = NULL; PFNGLNORMAL3DPROC glad_glNormal3d = NULL; PFNGLNORMAL3DVPROC glad_glNormal3dv = NULL; PFNGLNORMAL3FPROC glad_glNormal3f = NULL; PFNGLNORMAL3FVPROC glad_glNormal3fv = NULL; PFNGLNORMAL3IPROC glad_glNormal3i = NULL; PFNGLNORMAL3IVPROC glad_glNormal3iv = NULL; PFNGLNORMAL3SPROC glad_glNormal3s = NULL; PFNGLNORMAL3SVPROC glad_glNormal3sv = NULL; PFNGLNORMALP3UIPROC glad_glNormalP3ui = NULL; PFNGLNORMALP3UIVPROC glad_glNormalP3uiv = NULL; PFNGLNORMALPOINTERPROC glad_glNormalPointer = NULL; PFNGLOBJECTLABELPROC glad_glObjectLabel = NULL; PFNGLOBJECTPTRLABELPROC glad_glObjectPtrLabel = NULL; PFNGLORTHOPROC glad_glOrtho = NULL; PFNGLPASSTHROUGHPROC glad_glPassThrough = NULL; PFNGLPATCHPARAMETERFVPROC glad_glPatchParameterfv = NULL; PFNGLPATCHPARAMETERIPROC glad_glPatchParameteri = NULL; PFNGLPAUSETRANSFORMFEEDBACKPROC glad_glPauseTransformFeedback = NULL; PFNGLPIXELMAPFVPROC glad_glPixelMapfv = NULL; PFNGLPIXELMAPUIVPROC glad_glPixelMapuiv = NULL; PFNGLPIXELMAPUSVPROC glad_glPixelMapusv = NULL; PFNGLPIXELSTOREFPROC glad_glPixelStoref = NULL; PFNGLPIXELSTOREIPROC glad_glPixelStorei = NULL; PFNGLPIXELTRANSFERFPROC glad_glPixelTransferf = NULL; PFNGLPIXELTRANSFERIPROC glad_glPixelTransferi = NULL; PFNGLPIXELZOOMPROC glad_glPixelZoom = NULL; PFNGLPOINTPARAMETERFPROC glad_glPointParameterf = NULL; PFNGLPOINTPARAMETERFARBPROC glad_glPointParameterfARB = NULL; PFNGLPOINTPARAMETERFVPROC glad_glPointParameterfv = NULL; PFNGLPOINTPARAMETERFVARBPROC glad_glPointParameterfvARB = NULL; PFNGLPOINTPARAMETERIPROC glad_glPointParameteri = NULL; PFNGLPOINTPARAMETERIVPROC glad_glPointParameteriv = NULL; PFNGLPOINTSIZEPROC glad_glPointSize = NULL; PFNGLPOLYGONMODEPROC glad_glPolygonMode = NULL; PFNGLPOLYGONOFFSETPROC glad_glPolygonOffset = NULL; PFNGLPOLYGONOFFSETCLAMPPROC glad_glPolygonOffsetClamp = NULL; PFNGLPOLYGONSTIPPLEPROC glad_glPolygonStipple = NULL; PFNGLPOPATTRIBPROC glad_glPopAttrib = NULL; PFNGLPOPCLIENTATTRIBPROC glad_glPopClientAttrib = NULL; PFNGLPOPDEBUGGROUPPROC glad_glPopDebugGroup = NULL; PFNGLPOPMATRIXPROC glad_glPopMatrix = NULL; PFNGLPOPNAMEPROC glad_glPopName = NULL; PFNGLPRIMITIVEBOUNDINGBOXPROC glad_glPrimitiveBoundingBox = NULL; PFNGLPRIMITIVEBOUNDINGBOXARBPROC glad_glPrimitiveBoundingBoxARB = NULL; PFNGLPRIMITIVERESTARTINDEXPROC glad_glPrimitiveRestartIndex = NULL; PFNGLPRIORITIZETEXTURESPROC glad_glPrioritizeTextures = NULL; PFNGLPROGRAMBINARYPROC glad_glProgramBinary = NULL; PFNGLPROGRAMENVPARAMETER4DARBPROC glad_glProgramEnvParameter4dARB = NULL; PFNGLPROGRAMENVPARAMETER4DVARBPROC glad_glProgramEnvParameter4dvARB = NULL; PFNGLPROGRAMENVPARAMETER4FARBPROC glad_glProgramEnvParameter4fARB = NULL; PFNGLPROGRAMENVPARAMETER4FVARBPROC glad_glProgramEnvParameter4fvARB = NULL; PFNGLPROGRAMLOCALPARAMETER4DARBPROC glad_glProgramLocalParameter4dARB = NULL; PFNGLPROGRAMLOCALPARAMETER4DVARBPROC glad_glProgramLocalParameter4dvARB = NULL; PFNGLPROGRAMLOCALPARAMETER4FARBPROC glad_glProgramLocalParameter4fARB = NULL; PFNGLPROGRAMLOCALPARAMETER4FVARBPROC glad_glProgramLocalParameter4fvARB = NULL; PFNGLPROGRAMPARAMETERIPROC glad_glProgramParameteri = NULL; PFNGLPROGRAMPARAMETERIARBPROC glad_glProgramParameteriARB = NULL; PFNGLPROGRAMSTRINGARBPROC glad_glProgramStringARB = NULL; PFNGLPROGRAMUNIFORM1DPROC glad_glProgramUniform1d = NULL; PFNGLPROGRAMUNIFORM1DVPROC glad_glProgramUniform1dv = NULL; PFNGLPROGRAMUNIFORM1FPROC glad_glProgramUniform1f = NULL; PFNGLPROGRAMUNIFORM1FVPROC glad_glProgramUniform1fv = NULL; PFNGLPROGRAMUNIFORM1IPROC glad_glProgramUniform1i = NULL; PFNGLPROGRAMUNIFORM1I64ARBPROC glad_glProgramUniform1i64ARB = NULL; PFNGLPROGRAMUNIFORM1I64VARBPROC glad_glProgramUniform1i64vARB = NULL; PFNGLPROGRAMUNIFORM1IVPROC glad_glProgramUniform1iv = NULL; PFNGLPROGRAMUNIFORM1UIPROC glad_glProgramUniform1ui = NULL; PFNGLPROGRAMUNIFORM1UI64ARBPROC glad_glProgramUniform1ui64ARB = NULL; PFNGLPROGRAMUNIFORM1UI64VARBPROC glad_glProgramUniform1ui64vARB = NULL; PFNGLPROGRAMUNIFORM1UIVPROC glad_glProgramUniform1uiv = NULL; PFNGLPROGRAMUNIFORM2DPROC glad_glProgramUniform2d = NULL; PFNGLPROGRAMUNIFORM2DVPROC glad_glProgramUniform2dv = NULL; PFNGLPROGRAMUNIFORM2FPROC glad_glProgramUniform2f = NULL; PFNGLPROGRAMUNIFORM2FVPROC glad_glProgramUniform2fv = NULL; PFNGLPROGRAMUNIFORM2IPROC glad_glProgramUniform2i = NULL; PFNGLPROGRAMUNIFORM2I64ARBPROC glad_glProgramUniform2i64ARB = NULL; PFNGLPROGRAMUNIFORM2I64VARBPROC glad_glProgramUniform2i64vARB = NULL; PFNGLPROGRAMUNIFORM2IVPROC glad_glProgramUniform2iv = NULL; PFNGLPROGRAMUNIFORM2UIPROC glad_glProgramUniform2ui = NULL; PFNGLPROGRAMUNIFORM2UI64ARBPROC glad_glProgramUniform2ui64ARB = NULL; PFNGLPROGRAMUNIFORM2UI64VARBPROC glad_glProgramUniform2ui64vARB = NULL; PFNGLPROGRAMUNIFORM2UIVPROC glad_glProgramUniform2uiv = NULL; PFNGLPROGRAMUNIFORM3DPROC glad_glProgramUniform3d = NULL; PFNGLPROGRAMUNIFORM3DVPROC glad_glProgramUniform3dv = NULL; PFNGLPROGRAMUNIFORM3FPROC glad_glProgramUniform3f = NULL; PFNGLPROGRAMUNIFORM3FVPROC glad_glProgramUniform3fv = NULL; PFNGLPROGRAMUNIFORM3IPROC glad_glProgramUniform3i = NULL; PFNGLPROGRAMUNIFORM3I64ARBPROC glad_glProgramUniform3i64ARB = NULL; PFNGLPROGRAMUNIFORM3I64VARBPROC glad_glProgramUniform3i64vARB = NULL; PFNGLPROGRAMUNIFORM3IVPROC glad_glProgramUniform3iv = NULL; PFNGLPROGRAMUNIFORM3UIPROC glad_glProgramUniform3ui = NULL; PFNGLPROGRAMUNIFORM3UI64ARBPROC glad_glProgramUniform3ui64ARB = NULL; PFNGLPROGRAMUNIFORM3UI64VARBPROC glad_glProgramUniform3ui64vARB = NULL; PFNGLPROGRAMUNIFORM3UIVPROC glad_glProgramUniform3uiv = NULL; PFNGLPROGRAMUNIFORM4DPROC glad_glProgramUniform4d = NULL; PFNGLPROGRAMUNIFORM4DVPROC glad_glProgramUniform4dv = NULL; PFNGLPROGRAMUNIFORM4FPROC glad_glProgramUniform4f = NULL; PFNGLPROGRAMUNIFORM4FVPROC glad_glProgramUniform4fv = NULL; PFNGLPROGRAMUNIFORM4IPROC glad_glProgramUniform4i = NULL; PFNGLPROGRAMUNIFORM4I64ARBPROC glad_glProgramUniform4i64ARB = NULL; PFNGLPROGRAMUNIFORM4I64VARBPROC glad_glProgramUniform4i64vARB = NULL; PFNGLPROGRAMUNIFORM4IVPROC glad_glProgramUniform4iv = NULL; PFNGLPROGRAMUNIFORM4UIPROC glad_glProgramUniform4ui = NULL; PFNGLPROGRAMUNIFORM4UI64ARBPROC glad_glProgramUniform4ui64ARB = NULL; PFNGLPROGRAMUNIFORM4UI64VARBPROC glad_glProgramUniform4ui64vARB = NULL; PFNGLPROGRAMUNIFORM4UIVPROC glad_glProgramUniform4uiv = NULL; PFNGLPROGRAMUNIFORMHANDLEUI64ARBPROC glad_glProgramUniformHandleui64ARB = NULL; PFNGLPROGRAMUNIFORMHANDLEUI64VARBPROC glad_glProgramUniformHandleui64vARB = NULL; PFNGLPROGRAMUNIFORMMATRIX2DVPROC glad_glProgramUniformMatrix2dv = NULL; PFNGLPROGRAMUNIFORMMATRIX2FVPROC glad_glProgramUniformMatrix2fv = NULL; PFNGLPROGRAMUNIFORMMATRIX2X3DVPROC glad_glProgramUniformMatrix2x3dv = NULL; PFNGLPROGRAMUNIFORMMATRIX2X3FVPROC glad_glProgramUniformMatrix2x3fv = NULL; PFNGLPROGRAMUNIFORMMATRIX2X4DVPROC glad_glProgramUniformMatrix2x4dv = NULL; PFNGLPROGRAMUNIFORMMATRIX2X4FVPROC glad_glProgramUniformMatrix2x4fv = NULL; PFNGLPROGRAMUNIFORMMATRIX3DVPROC glad_glProgramUniformMatrix3dv = NULL; PFNGLPROGRAMUNIFORMMATRIX3FVPROC glad_glProgramUniformMatrix3fv = NULL; PFNGLPROGRAMUNIFORMMATRIX3X2DVPROC glad_glProgramUniformMatrix3x2dv = NULL; PFNGLPROGRAMUNIFORMMATRIX3X2FVPROC glad_glProgramUniformMatrix3x2fv = NULL; PFNGLPROGRAMUNIFORMMATRIX3X4DVPROC glad_glProgramUniformMatrix3x4dv = NULL; PFNGLPROGRAMUNIFORMMATRIX3X4FVPROC glad_glProgramUniformMatrix3x4fv = NULL; PFNGLPROGRAMUNIFORMMATRIX4DVPROC glad_glProgramUniformMatrix4dv = NULL; PFNGLPROGRAMUNIFORMMATRIX4FVPROC glad_glProgramUniformMatrix4fv = NULL; PFNGLPROGRAMUNIFORMMATRIX4X2DVPROC glad_glProgramUniformMatrix4x2dv = NULL; PFNGLPROGRAMUNIFORMMATRIX4X2FVPROC glad_glProgramUniformMatrix4x2fv = NULL; PFNGLPROGRAMUNIFORMMATRIX4X3DVPROC glad_glProgramUniformMatrix4x3dv = NULL; PFNGLPROGRAMUNIFORMMATRIX4X3FVPROC glad_glProgramUniformMatrix4x3fv = NULL; PFNGLPROVOKINGVERTEXPROC glad_glProvokingVertex = NULL; PFNGLPUSHATTRIBPROC glad_glPushAttrib = NULL; PFNGLPUSHCLIENTATTRIBPROC glad_glPushClientAttrib = NULL; PFNGLPUSHDEBUGGROUPPROC glad_glPushDebugGroup = NULL; PFNGLPUSHMATRIXPROC glad_glPushMatrix = NULL; PFNGLPUSHNAMEPROC glad_glPushName = NULL; PFNGLQUERYCOUNTERPROC glad_glQueryCounter = NULL; PFNGLRASTERPOS2DPROC glad_glRasterPos2d = NULL; PFNGLRASTERPOS2DVPROC glad_glRasterPos2dv = NULL; PFNGLRASTERPOS2FPROC glad_glRasterPos2f = NULL; PFNGLRASTERPOS2FVPROC glad_glRasterPos2fv = NULL; PFNGLRASTERPOS2IPROC glad_glRasterPos2i = NULL; PFNGLRASTERPOS2IVPROC glad_glRasterPos2iv = NULL; PFNGLRASTERPOS2SPROC glad_glRasterPos2s = NULL; PFNGLRASTERPOS2SVPROC glad_glRasterPos2sv = NULL; PFNGLRASTERPOS3DPROC glad_glRasterPos3d = NULL; PFNGLRASTERPOS3DVPROC glad_glRasterPos3dv = NULL; PFNGLRASTERPOS3FPROC glad_glRasterPos3f = NULL; PFNGLRASTERPOS3FVPROC glad_glRasterPos3fv = NULL; PFNGLRASTERPOS3IPROC glad_glRasterPos3i = NULL; PFNGLRASTERPOS3IVPROC glad_glRasterPos3iv = NULL; PFNGLRASTERPOS3SPROC glad_glRasterPos3s = NULL; PFNGLRASTERPOS3SVPROC glad_glRasterPos3sv = NULL; PFNGLRASTERPOS4DPROC glad_glRasterPos4d = NULL; PFNGLRASTERPOS4DVPROC glad_glRasterPos4dv = NULL; PFNGLRASTERPOS4FPROC glad_glRasterPos4f = NULL; PFNGLRASTERPOS4FVPROC glad_glRasterPos4fv = NULL; PFNGLRASTERPOS4IPROC glad_glRasterPos4i = NULL; PFNGLRASTERPOS4IVPROC glad_glRasterPos4iv = NULL; PFNGLRASTERPOS4SPROC glad_glRasterPos4s = NULL; PFNGLRASTERPOS4SVPROC glad_glRasterPos4sv = NULL; PFNGLREADBUFFERPROC glad_glReadBuffer = NULL; PFNGLREADPIXELSPROC glad_glReadPixels = NULL; PFNGLREADNPIXELSPROC glad_glReadnPixels = NULL; PFNGLREADNPIXELSARBPROC glad_glReadnPixelsARB = NULL; PFNGLRECTDPROC glad_glRectd = NULL; PFNGLRECTDVPROC glad_glRectdv = NULL; PFNGLRECTFPROC glad_glRectf = NULL; PFNGLRECTFVPROC glad_glRectfv = NULL; PFNGLRECTIPROC glad_glRecti = NULL; PFNGLRECTIVPROC glad_glRectiv = NULL; PFNGLRECTSPROC glad_glRects = NULL; PFNGLRECTSVPROC glad_glRectsv = NULL; PFNGLRELEASESHADERCOMPILERPROC glad_glReleaseShaderCompiler = NULL; PFNGLRENDERMODEPROC glad_glRenderMode = NULL; PFNGLRENDERBUFFERSTORAGEPROC glad_glRenderbufferStorage = NULL; PFNGLRENDERBUFFERSTORAGEMULTISAMPLEPROC glad_glRenderbufferStorageMultisample = NULL; PFNGLRESETHISTOGRAMPROC glad_glResetHistogram = NULL; PFNGLRESETMINMAXPROC glad_glResetMinmax = NULL; PFNGLRESUMETRANSFORMFEEDBACKPROC glad_glResumeTransformFeedback = NULL; PFNGLROTATEDPROC glad_glRotated = NULL; PFNGLROTATEFPROC glad_glRotatef = NULL; PFNGLSAMPLECOVERAGEPROC glad_glSampleCoverage = NULL; PFNGLSAMPLECOVERAGEARBPROC glad_glSampleCoverageARB = NULL; PFNGLSAMPLEMASKIPROC glad_glSampleMaski = NULL; PFNGLSAMPLERPARAMETERIIVPROC glad_glSamplerParameterIiv = NULL; PFNGLSAMPLERPARAMETERIUIVPROC glad_glSamplerParameterIuiv = NULL; PFNGLSAMPLERPARAMETERFPROC glad_glSamplerParameterf = NULL; PFNGLSAMPLERPARAMETERFVPROC glad_glSamplerParameterfv = NULL; PFNGLSAMPLERPARAMETERIPROC glad_glSamplerParameteri = NULL; PFNGLSAMPLERPARAMETERIVPROC glad_glSamplerParameteriv = NULL; PFNGLSCALEDPROC glad_glScaled = NULL; PFNGLSCALEFPROC glad_glScalef = NULL; PFNGLSCISSORPROC glad_glScissor = NULL; PFNGLSCISSORARRAYVPROC glad_glScissorArrayv = NULL; PFNGLSCISSORINDEXEDPROC glad_glScissorIndexed = NULL; PFNGLSCISSORINDEXEDVPROC glad_glScissorIndexedv = NULL; PFNGLSECONDARYCOLOR3BPROC glad_glSecondaryColor3b = NULL; PFNGLSECONDARYCOLOR3BVPROC glad_glSecondaryColor3bv = NULL; PFNGLSECONDARYCOLOR3DPROC glad_glSecondaryColor3d = NULL; PFNGLSECONDARYCOLOR3DVPROC glad_glSecondaryColor3dv = NULL; PFNGLSECONDARYCOLOR3FPROC glad_glSecondaryColor3f = NULL; PFNGLSECONDARYCOLOR3FVPROC glad_glSecondaryColor3fv = NULL; PFNGLSECONDARYCOLOR3IPROC glad_glSecondaryColor3i = NULL; PFNGLSECONDARYCOLOR3IVPROC glad_glSecondaryColor3iv = NULL; PFNGLSECONDARYCOLOR3SPROC glad_glSecondaryColor3s = NULL; PFNGLSECONDARYCOLOR3SVPROC glad_glSecondaryColor3sv = NULL; PFNGLSECONDARYCOLOR3UBPROC glad_glSecondaryColor3ub = NULL; PFNGLSECONDARYCOLOR3UBVPROC glad_glSecondaryColor3ubv = NULL; PFNGLSECONDARYCOLOR3UIPROC glad_glSecondaryColor3ui = NULL; PFNGLSECONDARYCOLOR3UIVPROC glad_glSecondaryColor3uiv = NULL; PFNGLSECONDARYCOLOR3USPROC glad_glSecondaryColor3us = NULL; PFNGLSECONDARYCOLOR3USVPROC glad_glSecondaryColor3usv = NULL; PFNGLSECONDARYCOLORP3UIPROC glad_glSecondaryColorP3ui = NULL; PFNGLSECONDARYCOLORP3UIVPROC glad_glSecondaryColorP3uiv = NULL; PFNGLSECONDARYCOLORPOINTERPROC glad_glSecondaryColorPointer = NULL; PFNGLSELECTBUFFERPROC glad_glSelectBuffer = NULL; PFNGLSEPARABLEFILTER2DPROC glad_glSeparableFilter2D = NULL; PFNGLSHADEMODELPROC glad_glShadeModel = NULL; PFNGLSHADERBINARYPROC glad_glShaderBinary = NULL; PFNGLSHADERSOURCEPROC glad_glShaderSource = NULL; PFNGLSHADERSOURCEARBPROC glad_glShaderSourceARB = NULL; PFNGLSHADERSTORAGEBLOCKBINDINGPROC glad_glShaderStorageBlockBinding = NULL; PFNGLSPECIALIZESHADERPROC glad_glSpecializeShader = NULL; PFNGLSPECIALIZESHADERARBPROC glad_glSpecializeShaderARB = NULL; PFNGLSTENCILFUNCPROC glad_glStencilFunc = NULL; PFNGLSTENCILFUNCSEPARATEPROC glad_glStencilFuncSeparate = NULL; PFNGLSTENCILMASKPROC glad_glStencilMask = NULL; PFNGLSTENCILMASKSEPARATEPROC glad_glStencilMaskSeparate = NULL; PFNGLSTENCILOPPROC glad_glStencilOp = NULL; PFNGLSTENCILOPSEPARATEPROC glad_glStencilOpSeparate = NULL; PFNGLTEXBUFFERPROC glad_glTexBuffer = NULL; PFNGLTEXBUFFERARBPROC glad_glTexBufferARB = NULL; PFNGLTEXBUFFERRANGEPROC glad_glTexBufferRange = NULL; PFNGLTEXCOORD1DPROC glad_glTexCoord1d = NULL; PFNGLTEXCOORD1DVPROC glad_glTexCoord1dv = NULL; PFNGLTEXCOORD1FPROC glad_glTexCoord1f = NULL; PFNGLTEXCOORD1FVPROC glad_glTexCoord1fv = NULL; PFNGLTEXCOORD1IPROC glad_glTexCoord1i = NULL; PFNGLTEXCOORD1IVPROC glad_glTexCoord1iv = NULL; PFNGLTEXCOORD1SPROC glad_glTexCoord1s = NULL; PFNGLTEXCOORD1SVPROC glad_glTexCoord1sv = NULL; PFNGLTEXCOORD2DPROC glad_glTexCoord2d = NULL; PFNGLTEXCOORD2DVPROC glad_glTexCoord2dv = NULL; PFNGLTEXCOORD2FPROC glad_glTexCoord2f = NULL; PFNGLTEXCOORD2FVPROC glad_glTexCoord2fv = NULL; PFNGLTEXCOORD2IPROC glad_glTexCoord2i = NULL; PFNGLTEXCOORD2IVPROC glad_glTexCoord2iv = NULL; PFNGLTEXCOORD2SPROC glad_glTexCoord2s = NULL; PFNGLTEXCOORD2SVPROC glad_glTexCoord2sv = NULL; PFNGLTEXCOORD3DPROC glad_glTexCoord3d = NULL; PFNGLTEXCOORD3DVPROC glad_glTexCoord3dv = NULL; PFNGLTEXCOORD3FPROC glad_glTexCoord3f = NULL; PFNGLTEXCOORD3FVPROC glad_glTexCoord3fv = NULL; PFNGLTEXCOORD3IPROC glad_glTexCoord3i = NULL; PFNGLTEXCOORD3IVPROC glad_glTexCoord3iv = NULL; PFNGLTEXCOORD3SPROC glad_glTexCoord3s = NULL; PFNGLTEXCOORD3SVPROC glad_glTexCoord3sv = NULL; PFNGLTEXCOORD4DPROC glad_glTexCoord4d = NULL; PFNGLTEXCOORD4DVPROC glad_glTexCoord4dv = NULL; PFNGLTEXCOORD4FPROC glad_glTexCoord4f = NULL; PFNGLTEXCOORD4FVPROC glad_glTexCoord4fv = NULL; PFNGLTEXCOORD4IPROC glad_glTexCoord4i = NULL; PFNGLTEXCOORD4IVPROC glad_glTexCoord4iv = NULL; PFNGLTEXCOORD4SPROC glad_glTexCoord4s = NULL; PFNGLTEXCOORD4SVPROC glad_glTexCoord4sv = NULL; PFNGLTEXCOORDP1UIPROC glad_glTexCoordP1ui = NULL; PFNGLTEXCOORDP1UIVPROC glad_glTexCoordP1uiv = NULL; PFNGLTEXCOORDP2UIPROC glad_glTexCoordP2ui = NULL; PFNGLTEXCOORDP2UIVPROC glad_glTexCoordP2uiv = NULL; PFNGLTEXCOORDP3UIPROC glad_glTexCoordP3ui = NULL; PFNGLTEXCOORDP3UIVPROC glad_glTexCoordP3uiv = NULL; PFNGLTEXCOORDP4UIPROC glad_glTexCoordP4ui = NULL; PFNGLTEXCOORDP4UIVPROC glad_glTexCoordP4uiv = NULL; PFNGLTEXCOORDPOINTERPROC glad_glTexCoordPointer = NULL; PFNGLTEXENVFPROC glad_glTexEnvf = NULL; PFNGLTEXENVFVPROC glad_glTexEnvfv = NULL; PFNGLTEXENVIPROC glad_glTexEnvi = NULL; PFNGLTEXENVIVPROC glad_glTexEnviv = NULL; PFNGLTEXGENDPROC glad_glTexGend = NULL; PFNGLTEXGENDVPROC glad_glTexGendv = NULL; PFNGLTEXGENFPROC glad_glTexGenf = NULL; PFNGLTEXGENFVPROC glad_glTexGenfv = NULL; PFNGLTEXGENIPROC glad_glTexGeni = NULL; PFNGLTEXGENIVPROC glad_glTexGeniv = NULL; PFNGLTEXIMAGE1DPROC glad_glTexImage1D = NULL; PFNGLTEXIMAGE2DPROC glad_glTexImage2D = NULL; PFNGLTEXIMAGE2DMULTISAMPLEPROC glad_glTexImage2DMultisample = NULL; PFNGLTEXIMAGE3DPROC glad_glTexImage3D = NULL; PFNGLTEXIMAGE3DMULTISAMPLEPROC glad_glTexImage3DMultisample = NULL; PFNGLTEXPAGECOMMITMENTARBPROC glad_glTexPageCommitmentARB = NULL; PFNGLTEXPARAMETERIIVPROC glad_glTexParameterIiv = NULL; PFNGLTEXPARAMETERIUIVPROC glad_glTexParameterIuiv = NULL; PFNGLTEXPARAMETERFPROC glad_glTexParameterf = NULL; PFNGLTEXPARAMETERFVPROC glad_glTexParameterfv = NULL; PFNGLTEXPARAMETERIPROC glad_glTexParameteri = NULL; PFNGLTEXPARAMETERIVPROC glad_glTexParameteriv = NULL; PFNGLTEXSTORAGE1DPROC glad_glTexStorage1D = NULL; PFNGLTEXSTORAGE2DPROC glad_glTexStorage2D = NULL; PFNGLTEXSTORAGE2DMULTISAMPLEPROC glad_glTexStorage2DMultisample = NULL; PFNGLTEXSTORAGE3DPROC glad_glTexStorage3D = NULL; PFNGLTEXSTORAGE3DMULTISAMPLEPROC glad_glTexStorage3DMultisample = NULL; PFNGLTEXSUBIMAGE1DPROC glad_glTexSubImage1D = NULL; PFNGLTEXSUBIMAGE2DPROC glad_glTexSubImage2D = NULL; PFNGLTEXSUBIMAGE3DPROC glad_glTexSubImage3D = NULL; PFNGLTEXTUREBARRIERPROC glad_glTextureBarrier = NULL; PFNGLTEXTUREBUFFERPROC glad_glTextureBuffer = NULL; PFNGLTEXTUREBUFFERRANGEPROC glad_glTextureBufferRange = NULL; PFNGLTEXTUREPARAMETERIIVPROC glad_glTextureParameterIiv = NULL; PFNGLTEXTUREPARAMETERIUIVPROC glad_glTextureParameterIuiv = NULL; PFNGLTEXTUREPARAMETERFPROC glad_glTextureParameterf = NULL; PFNGLTEXTUREPARAMETERFVPROC glad_glTextureParameterfv = NULL; PFNGLTEXTUREPARAMETERIPROC glad_glTextureParameteri = NULL; PFNGLTEXTUREPARAMETERIVPROC glad_glTextureParameteriv = NULL; PFNGLTEXTURESTORAGE1DPROC glad_glTextureStorage1D = NULL; PFNGLTEXTURESTORAGE2DPROC glad_glTextureStorage2D = NULL; PFNGLTEXTURESTORAGE2DMULTISAMPLEPROC glad_glTextureStorage2DMultisample = NULL; PFNGLTEXTURESTORAGE3DPROC glad_glTextureStorage3D = NULL; PFNGLTEXTURESTORAGE3DMULTISAMPLEPROC glad_glTextureStorage3DMultisample = NULL; PFNGLTEXTURESUBIMAGE1DPROC glad_glTextureSubImage1D = NULL; PFNGLTEXTURESUBIMAGE2DPROC glad_glTextureSubImage2D = NULL; PFNGLTEXTURESUBIMAGE3DPROC glad_glTextureSubImage3D = NULL; PFNGLTEXTUREVIEWPROC glad_glTextureView = NULL; PFNGLTRANSFORMFEEDBACKBUFFERBASEPROC glad_glTransformFeedbackBufferBase = NULL; PFNGLTRANSFORMFEEDBACKBUFFERRANGEPROC glad_glTransformFeedbackBufferRange = NULL; PFNGLTRANSFORMFEEDBACKVARYINGSPROC glad_glTransformFeedbackVaryings = NULL; PFNGLTRANSLATEDPROC glad_glTranslated = NULL; PFNGLTRANSLATEFPROC glad_glTranslatef = NULL; PFNGLUNIFORM1DPROC glad_glUniform1d = NULL; PFNGLUNIFORM1DVPROC glad_glUniform1dv = NULL; PFNGLUNIFORM1FPROC glad_glUniform1f = NULL; PFNGLUNIFORM1FARBPROC glad_glUniform1fARB = NULL; PFNGLUNIFORM1FVPROC glad_glUniform1fv = NULL; PFNGLUNIFORM1FVARBPROC glad_glUniform1fvARB = NULL; PFNGLUNIFORM1IPROC glad_glUniform1i = NULL; PFNGLUNIFORM1I64ARBPROC glad_glUniform1i64ARB = NULL; PFNGLUNIFORM1I64VARBPROC glad_glUniform1i64vARB = NULL; PFNGLUNIFORM1IARBPROC glad_glUniform1iARB = NULL; PFNGLUNIFORM1IVPROC glad_glUniform1iv = NULL; PFNGLUNIFORM1IVARBPROC glad_glUniform1ivARB = NULL; PFNGLUNIFORM1UIPROC glad_glUniform1ui = NULL; PFNGLUNIFORM1UI64ARBPROC glad_glUniform1ui64ARB = NULL; PFNGLUNIFORM1UI64VARBPROC glad_glUniform1ui64vARB = NULL; PFNGLUNIFORM1UIVPROC glad_glUniform1uiv = NULL; PFNGLUNIFORM2DPROC glad_glUniform2d = NULL; PFNGLUNIFORM2DVPROC glad_glUniform2dv = NULL; PFNGLUNIFORM2FPROC glad_glUniform2f = NULL; PFNGLUNIFORM2FARBPROC glad_glUniform2fARB = NULL; PFNGLUNIFORM2FVPROC glad_glUniform2fv = NULL; PFNGLUNIFORM2FVARBPROC glad_glUniform2fvARB = NULL; PFNGLUNIFORM2IPROC glad_glUniform2i = NULL; PFNGLUNIFORM2I64ARBPROC glad_glUniform2i64ARB = NULL; PFNGLUNIFORM2I64VARBPROC glad_glUniform2i64vARB = NULL; PFNGLUNIFORM2IARBPROC glad_glUniform2iARB = NULL; PFNGLUNIFORM2IVPROC glad_glUniform2iv = NULL; PFNGLUNIFORM2IVARBPROC glad_glUniform2ivARB = NULL; PFNGLUNIFORM2UIPROC glad_glUniform2ui = NULL; PFNGLUNIFORM2UI64ARBPROC glad_glUniform2ui64ARB = NULL; PFNGLUNIFORM2UI64VARBPROC glad_glUniform2ui64vARB = NULL; PFNGLUNIFORM2UIVPROC glad_glUniform2uiv = NULL; PFNGLUNIFORM3DPROC glad_glUniform3d = NULL; PFNGLUNIFORM3DVPROC glad_glUniform3dv = NULL; PFNGLUNIFORM3FPROC glad_glUniform3f = NULL; PFNGLUNIFORM3FARBPROC glad_glUniform3fARB = NULL; PFNGLUNIFORM3FVPROC glad_glUniform3fv = NULL; PFNGLUNIFORM3FVARBPROC glad_glUniform3fvARB = NULL; PFNGLUNIFORM3IPROC glad_glUniform3i = NULL; PFNGLUNIFORM3I64ARBPROC glad_glUniform3i64ARB = NULL; PFNGLUNIFORM3I64VARBPROC glad_glUniform3i64vARB = NULL; PFNGLUNIFORM3IARBPROC glad_glUniform3iARB = NULL; PFNGLUNIFORM3IVPROC glad_glUniform3iv = NULL; PFNGLUNIFORM3IVARBPROC glad_glUniform3ivARB = NULL; PFNGLUNIFORM3UIPROC glad_glUniform3ui = NULL; PFNGLUNIFORM3UI64ARBPROC glad_glUniform3ui64ARB = NULL; PFNGLUNIFORM3UI64VARBPROC glad_glUniform3ui64vARB = NULL; PFNGLUNIFORM3UIVPROC glad_glUniform3uiv = NULL; PFNGLUNIFORM4DPROC glad_glUniform4d = NULL; PFNGLUNIFORM4DVPROC glad_glUniform4dv = NULL; PFNGLUNIFORM4FPROC glad_glUniform4f = NULL; PFNGLUNIFORM4FARBPROC glad_glUniform4fARB = NULL; PFNGLUNIFORM4FVPROC glad_glUniform4fv = NULL; PFNGLUNIFORM4FVARBPROC glad_glUniform4fvARB = NULL; PFNGLUNIFORM4IPROC glad_glUniform4i = NULL; PFNGLUNIFORM4I64ARBPROC glad_glUniform4i64ARB = NULL; PFNGLUNIFORM4I64VARBPROC glad_glUniform4i64vARB = NULL; PFNGLUNIFORM4IARBPROC glad_glUniform4iARB = NULL; PFNGLUNIFORM4IVPROC glad_glUniform4iv = NULL; PFNGLUNIFORM4IVARBPROC glad_glUniform4ivARB = NULL; PFNGLUNIFORM4UIPROC glad_glUniform4ui = NULL; PFNGLUNIFORM4UI64ARBPROC glad_glUniform4ui64ARB = NULL; PFNGLUNIFORM4UI64VARBPROC glad_glUniform4ui64vARB = NULL; PFNGLUNIFORM4UIVPROC glad_glUniform4uiv = NULL; PFNGLUNIFORMBLOCKBINDINGPROC glad_glUniformBlockBinding = NULL; PFNGLUNIFORMHANDLEUI64ARBPROC glad_glUniformHandleui64ARB = NULL; PFNGLUNIFORMHANDLEUI64VARBPROC glad_glUniformHandleui64vARB = NULL; PFNGLUNIFORMMATRIX2DVPROC glad_glUniformMatrix2dv = NULL; PFNGLUNIFORMMATRIX2FVPROC glad_glUniformMatrix2fv = NULL; PFNGLUNIFORMMATRIX2FVARBPROC glad_glUniformMatrix2fvARB = NULL; PFNGLUNIFORMMATRIX2X3DVPROC glad_glUniformMatrix2x3dv = NULL; PFNGLUNIFORMMATRIX2X3FVPROC glad_glUniformMatrix2x3fv = NULL; PFNGLUNIFORMMATRIX2X4DVPROC glad_glUniformMatrix2x4dv = NULL; PFNGLUNIFORMMATRIX2X4FVPROC glad_glUniformMatrix2x4fv = NULL; PFNGLUNIFORMMATRIX3DVPROC glad_glUniformMatrix3dv = NULL; PFNGLUNIFORMMATRIX3FVPROC glad_glUniformMatrix3fv = NULL; PFNGLUNIFORMMATRIX3FVARBPROC glad_glUniformMatrix3fvARB = NULL; PFNGLUNIFORMMATRIX3X2DVPROC glad_glUniformMatrix3x2dv = NULL; PFNGLUNIFORMMATRIX3X2FVPROC glad_glUniformMatrix3x2fv = NULL; PFNGLUNIFORMMATRIX3X4DVPROC glad_glUniformMatrix3x4dv = NULL; PFNGLUNIFORMMATRIX3X4FVPROC glad_glUniformMatrix3x4fv = NULL; PFNGLUNIFORMMATRIX4DVPROC glad_glUniformMatrix4dv = NULL; PFNGLUNIFORMMATRIX4FVPROC glad_glUniformMatrix4fv = NULL; PFNGLUNIFORMMATRIX4FVARBPROC glad_glUniformMatrix4fvARB = NULL; PFNGLUNIFORMMATRIX4X2DVPROC glad_glUniformMatrix4x2dv = NULL; PFNGLUNIFORMMATRIX4X2FVPROC glad_glUniformMatrix4x2fv = NULL; PFNGLUNIFORMMATRIX4X3DVPROC glad_glUniformMatrix4x3dv = NULL; PFNGLUNIFORMMATRIX4X3FVPROC glad_glUniformMatrix4x3fv = NULL; PFNGLUNIFORMSUBROUTINESUIVPROC glad_glUniformSubroutinesuiv = NULL; PFNGLUNMAPBUFFERPROC glad_glUnmapBuffer = NULL; PFNGLUNMAPBUFFERARBPROC glad_glUnmapBufferARB = NULL; PFNGLUNMAPNAMEDBUFFERPROC glad_glUnmapNamedBuffer = NULL; PFNGLUSEPROGRAMPROC glad_glUseProgram = NULL; PFNGLUSEPROGRAMOBJECTARBPROC glad_glUseProgramObjectARB = NULL; PFNGLUSEPROGRAMSTAGESPROC glad_glUseProgramStages = NULL; PFNGLVALIDATEPROGRAMPROC glad_glValidateProgram = NULL; PFNGLVALIDATEPROGRAMARBPROC glad_glValidateProgramARB = NULL; PFNGLVALIDATEPROGRAMPIPELINEPROC glad_glValidateProgramPipeline = NULL; PFNGLVERTEX2DPROC glad_glVertex2d = NULL; PFNGLVERTEX2DVPROC glad_glVertex2dv = NULL; PFNGLVERTEX2FPROC glad_glVertex2f = NULL; PFNGLVERTEX2FVPROC glad_glVertex2fv = NULL; PFNGLVERTEX2IPROC glad_glVertex2i = NULL; PFNGLVERTEX2IVPROC glad_glVertex2iv = NULL; PFNGLVERTEX2SPROC glad_glVertex2s = NULL; PFNGLVERTEX2SVPROC glad_glVertex2sv = NULL; PFNGLVERTEX3DPROC glad_glVertex3d = NULL; PFNGLVERTEX3DVPROC glad_glVertex3dv = NULL; PFNGLVERTEX3FPROC glad_glVertex3f = NULL; PFNGLVERTEX3FVPROC glad_glVertex3fv = NULL; PFNGLVERTEX3IPROC glad_glVertex3i = NULL; PFNGLVERTEX3IVPROC glad_glVertex3iv = NULL; PFNGLVERTEX3SPROC glad_glVertex3s = NULL; PFNGLVERTEX3SVPROC glad_glVertex3sv = NULL; PFNGLVERTEX4DPROC glad_glVertex4d = NULL; PFNGLVERTEX4DVPROC glad_glVertex4dv = NULL; PFNGLVERTEX4FPROC glad_glVertex4f = NULL; PFNGLVERTEX4FVPROC glad_glVertex4fv = NULL; PFNGLVERTEX4IPROC glad_glVertex4i = NULL; PFNGLVERTEX4IVPROC glad_glVertex4iv = NULL; PFNGLVERTEX4SPROC glad_glVertex4s = NULL; PFNGLVERTEX4SVPROC glad_glVertex4sv = NULL; PFNGLVERTEXARRAYATTRIBBINDINGPROC glad_glVertexArrayAttribBinding = NULL; PFNGLVERTEXARRAYATTRIBFORMATPROC glad_glVertexArrayAttribFormat = NULL; PFNGLVERTEXARRAYATTRIBIFORMATPROC glad_glVertexArrayAttribIFormat = NULL; PFNGLVERTEXARRAYATTRIBLFORMATPROC glad_glVertexArrayAttribLFormat = NULL; PFNGLVERTEXARRAYBINDINGDIVISORPROC glad_glVertexArrayBindingDivisor = NULL; PFNGLVERTEXARRAYELEMENTBUFFERPROC glad_glVertexArrayElementBuffer = NULL; PFNGLVERTEXARRAYVERTEXBUFFERPROC glad_glVertexArrayVertexBuffer = NULL; PFNGLVERTEXARRAYVERTEXBUFFERSPROC glad_glVertexArrayVertexBuffers = NULL; PFNGLVERTEXATTRIB1DPROC glad_glVertexAttrib1d = NULL; PFNGLVERTEXATTRIB1DARBPROC glad_glVertexAttrib1dARB = NULL; PFNGLVERTEXATTRIB1DVPROC glad_glVertexAttrib1dv = NULL; PFNGLVERTEXATTRIB1DVARBPROC glad_glVertexAttrib1dvARB = NULL; PFNGLVERTEXATTRIB1FPROC glad_glVertexAttrib1f = NULL; PFNGLVERTEXATTRIB1FARBPROC glad_glVertexAttrib1fARB = NULL; PFNGLVERTEXATTRIB1FVPROC glad_glVertexAttrib1fv = NULL; PFNGLVERTEXATTRIB1FVARBPROC glad_glVertexAttrib1fvARB = NULL; PFNGLVERTEXATTRIB1SPROC glad_glVertexAttrib1s = NULL; PFNGLVERTEXATTRIB1SARBPROC glad_glVertexAttrib1sARB = NULL; PFNGLVERTEXATTRIB1SVPROC glad_glVertexAttrib1sv = NULL; PFNGLVERTEXATTRIB1SVARBPROC glad_glVertexAttrib1svARB = NULL; PFNGLVERTEXATTRIB2DPROC glad_glVertexAttrib2d = NULL; PFNGLVERTEXATTRIB2DARBPROC glad_glVertexAttrib2dARB = NULL; PFNGLVERTEXATTRIB2DVPROC glad_glVertexAttrib2dv = NULL; PFNGLVERTEXATTRIB2DVARBPROC glad_glVertexAttrib2dvARB = NULL; PFNGLVERTEXATTRIB2FPROC glad_glVertexAttrib2f = NULL; PFNGLVERTEXATTRIB2FARBPROC glad_glVertexAttrib2fARB = NULL; PFNGLVERTEXATTRIB2FVPROC glad_glVertexAttrib2fv = NULL; PFNGLVERTEXATTRIB2FVARBPROC glad_glVertexAttrib2fvARB = NULL; PFNGLVERTEXATTRIB2SPROC glad_glVertexAttrib2s = NULL; PFNGLVERTEXATTRIB2SARBPROC glad_glVertexAttrib2sARB = NULL; PFNGLVERTEXATTRIB2SVPROC glad_glVertexAttrib2sv = NULL; PFNGLVERTEXATTRIB2SVARBPROC glad_glVertexAttrib2svARB = NULL; PFNGLVERTEXATTRIB3DPROC glad_glVertexAttrib3d = NULL; PFNGLVERTEXATTRIB3DARBPROC glad_glVertexAttrib3dARB = NULL; PFNGLVERTEXATTRIB3DVPROC glad_glVertexAttrib3dv = NULL; PFNGLVERTEXATTRIB3DVARBPROC glad_glVertexAttrib3dvARB = NULL; PFNGLVERTEXATTRIB3FPROC glad_glVertexAttrib3f = NULL; PFNGLVERTEXATTRIB3FARBPROC glad_glVertexAttrib3fARB = NULL; PFNGLVERTEXATTRIB3FVPROC glad_glVertexAttrib3fv = NULL; PFNGLVERTEXATTRIB3FVARBPROC glad_glVertexAttrib3fvARB = NULL; PFNGLVERTEXATTRIB3SPROC glad_glVertexAttrib3s = NULL; PFNGLVERTEXATTRIB3SARBPROC glad_glVertexAttrib3sARB = NULL; PFNGLVERTEXATTRIB3SVPROC glad_glVertexAttrib3sv = NULL; PFNGLVERTEXATTRIB3SVARBPROC glad_glVertexAttrib3svARB = NULL; PFNGLVERTEXATTRIB4NBVPROC glad_glVertexAttrib4Nbv = NULL; PFNGLVERTEXATTRIB4NBVARBPROC glad_glVertexAttrib4NbvARB = NULL; PFNGLVERTEXATTRIB4NIVPROC glad_glVertexAttrib4Niv = NULL; PFNGLVERTEXATTRIB4NIVARBPROC glad_glVertexAttrib4NivARB = NULL; PFNGLVERTEXATTRIB4NSVPROC glad_glVertexAttrib4Nsv = NULL; PFNGLVERTEXATTRIB4NSVARBPROC glad_glVertexAttrib4NsvARB = NULL; PFNGLVERTEXATTRIB4NUBPROC glad_glVertexAttrib4Nub = NULL; PFNGLVERTEXATTRIB4NUBARBPROC glad_glVertexAttrib4NubARB = NULL; PFNGLVERTEXATTRIB4NUBVPROC glad_glVertexAttrib4Nubv = NULL; PFNGLVERTEXATTRIB4NUBVARBPROC glad_glVertexAttrib4NubvARB = NULL; PFNGLVERTEXATTRIB4NUIVPROC glad_glVertexAttrib4Nuiv = NULL; PFNGLVERTEXATTRIB4NUIVARBPROC glad_glVertexAttrib4NuivARB = NULL; PFNGLVERTEXATTRIB4NUSVPROC glad_glVertexAttrib4Nusv = NULL; PFNGLVERTEXATTRIB4NUSVARBPROC glad_glVertexAttrib4NusvARB = NULL; PFNGLVERTEXATTRIB4BVPROC glad_glVertexAttrib4bv = NULL; PFNGLVERTEXATTRIB4BVARBPROC glad_glVertexAttrib4bvARB = NULL; PFNGLVERTEXATTRIB4DPROC glad_glVertexAttrib4d = NULL; PFNGLVERTEXATTRIB4DARBPROC glad_glVertexAttrib4dARB = NULL; PFNGLVERTEXATTRIB4DVPROC glad_glVertexAttrib4dv = NULL; PFNGLVERTEXATTRIB4DVARBPROC glad_glVertexAttrib4dvARB = NULL; PFNGLVERTEXATTRIB4FPROC glad_glVertexAttrib4f = NULL; PFNGLVERTEXATTRIB4FARBPROC glad_glVertexAttrib4fARB = NULL; PFNGLVERTEXATTRIB4FVPROC glad_glVertexAttrib4fv = NULL; PFNGLVERTEXATTRIB4FVARBPROC glad_glVertexAttrib4fvARB = NULL; PFNGLVERTEXATTRIB4IVPROC glad_glVertexAttrib4iv = NULL; PFNGLVERTEXATTRIB4IVARBPROC glad_glVertexAttrib4ivARB = NULL; PFNGLVERTEXATTRIB4SPROC glad_glVertexAttrib4s = NULL; PFNGLVERTEXATTRIB4SARBPROC glad_glVertexAttrib4sARB = NULL; PFNGLVERTEXATTRIB4SVPROC glad_glVertexAttrib4sv = NULL; PFNGLVERTEXATTRIB4SVARBPROC glad_glVertexAttrib4svARB = NULL; PFNGLVERTEXATTRIB4UBVPROC glad_glVertexAttrib4ubv = NULL; PFNGLVERTEXATTRIB4UBVARBPROC glad_glVertexAttrib4ubvARB = NULL; PFNGLVERTEXATTRIB4UIVPROC glad_glVertexAttrib4uiv = NULL; PFNGLVERTEXATTRIB4UIVARBPROC glad_glVertexAttrib4uivARB = NULL; PFNGLVERTEXATTRIB4USVPROC glad_glVertexAttrib4usv = NULL; PFNGLVERTEXATTRIB4USVARBPROC glad_glVertexAttrib4usvARB = NULL; PFNGLVERTEXATTRIBBINDINGPROC glad_glVertexAttribBinding = NULL; PFNGLVERTEXATTRIBDIVISORPROC glad_glVertexAttribDivisor = NULL; PFNGLVERTEXATTRIBDIVISORARBPROC glad_glVertexAttribDivisorARB = NULL; PFNGLVERTEXATTRIBFORMATPROC glad_glVertexAttribFormat = NULL; PFNGLVERTEXATTRIBI1IPROC glad_glVertexAttribI1i = NULL; PFNGLVERTEXATTRIBI1IVPROC glad_glVertexAttribI1iv = NULL; PFNGLVERTEXATTRIBI1UIPROC glad_glVertexAttribI1ui = NULL; PFNGLVERTEXATTRIBI1UIVPROC glad_glVertexAttribI1uiv = NULL; PFNGLVERTEXATTRIBI2IPROC glad_glVertexAttribI2i = NULL; PFNGLVERTEXATTRIBI2IVPROC glad_glVertexAttribI2iv = NULL; PFNGLVERTEXATTRIBI2UIPROC glad_glVertexAttribI2ui = NULL; PFNGLVERTEXATTRIBI2UIVPROC glad_glVertexAttribI2uiv = NULL; PFNGLVERTEXATTRIBI3IPROC glad_glVertexAttribI3i = NULL; PFNGLVERTEXATTRIBI3IVPROC glad_glVertexAttribI3iv = NULL; PFNGLVERTEXATTRIBI3UIPROC glad_glVertexAttribI3ui = NULL; PFNGLVERTEXATTRIBI3UIVPROC glad_glVertexAttribI3uiv = NULL; PFNGLVERTEXATTRIBI4BVPROC glad_glVertexAttribI4bv = NULL; PFNGLVERTEXATTRIBI4IPROC glad_glVertexAttribI4i = NULL; PFNGLVERTEXATTRIBI4IVPROC glad_glVertexAttribI4iv = NULL; PFNGLVERTEXATTRIBI4SVPROC glad_glVertexAttribI4sv = NULL; PFNGLVERTEXATTRIBI4UBVPROC glad_glVertexAttribI4ubv = NULL; PFNGLVERTEXATTRIBI4UIPROC glad_glVertexAttribI4ui = NULL; PFNGLVERTEXATTRIBI4UIVPROC glad_glVertexAttribI4uiv = NULL; PFNGLVERTEXATTRIBI4USVPROC glad_glVertexAttribI4usv = NULL; PFNGLVERTEXATTRIBIFORMATPROC glad_glVertexAttribIFormat = NULL; PFNGLVERTEXATTRIBIPOINTERPROC glad_glVertexAttribIPointer = NULL; PFNGLVERTEXATTRIBL1DPROC glad_glVertexAttribL1d = NULL; PFNGLVERTEXATTRIBL1DVPROC glad_glVertexAttribL1dv = NULL; PFNGLVERTEXATTRIBL1UI64ARBPROC glad_glVertexAttribL1ui64ARB = NULL; PFNGLVERTEXATTRIBL1UI64VARBPROC glad_glVertexAttribL1ui64vARB = NULL; PFNGLVERTEXATTRIBL2DPROC glad_glVertexAttribL2d = NULL; PFNGLVERTEXATTRIBL2DVPROC glad_glVertexAttribL2dv = NULL; PFNGLVERTEXATTRIBL3DPROC glad_glVertexAttribL3d = NULL; PFNGLVERTEXATTRIBL3DVPROC glad_glVertexAttribL3dv = NULL; PFNGLVERTEXATTRIBL4DPROC glad_glVertexAttribL4d = NULL; PFNGLVERTEXATTRIBL4DVPROC glad_glVertexAttribL4dv = NULL; PFNGLVERTEXATTRIBLFORMATPROC glad_glVertexAttribLFormat = NULL; PFNGLVERTEXATTRIBLPOINTERPROC glad_glVertexAttribLPointer = NULL; PFNGLVERTEXATTRIBP1UIPROC glad_glVertexAttribP1ui = NULL; PFNGLVERTEXATTRIBP1UIVPROC glad_glVertexAttribP1uiv = NULL; PFNGLVERTEXATTRIBP2UIPROC glad_glVertexAttribP2ui = NULL; PFNGLVERTEXATTRIBP2UIVPROC glad_glVertexAttribP2uiv = NULL; PFNGLVERTEXATTRIBP3UIPROC glad_glVertexAttribP3ui = NULL; PFNGLVERTEXATTRIBP3UIVPROC glad_glVertexAttribP3uiv = NULL; PFNGLVERTEXATTRIBP4UIPROC glad_glVertexAttribP4ui = NULL; PFNGLVERTEXATTRIBP4UIVPROC glad_glVertexAttribP4uiv = NULL; PFNGLVERTEXATTRIBPOINTERPROC glad_glVertexAttribPointer = NULL; PFNGLVERTEXATTRIBPOINTERARBPROC glad_glVertexAttribPointerARB = NULL; PFNGLVERTEXBINDINGDIVISORPROC glad_glVertexBindingDivisor = NULL; PFNGLVERTEXBLENDARBPROC glad_glVertexBlendARB = NULL; PFNGLVERTEXP2UIPROC glad_glVertexP2ui = NULL; PFNGLVERTEXP2UIVPROC glad_glVertexP2uiv = NULL; PFNGLVERTEXP3UIPROC glad_glVertexP3ui = NULL; PFNGLVERTEXP3UIVPROC glad_glVertexP3uiv = NULL; PFNGLVERTEXP4UIPROC glad_glVertexP4ui = NULL; PFNGLVERTEXP4UIVPROC glad_glVertexP4uiv = NULL; PFNGLVERTEXPOINTERPROC glad_glVertexPointer = NULL; PFNGLVIEWPORTPROC glad_glViewport = NULL; PFNGLVIEWPORTARRAYVPROC glad_glViewportArrayv = NULL; PFNGLVIEWPORTINDEXEDFPROC glad_glViewportIndexedf = NULL; PFNGLVIEWPORTINDEXEDFVPROC glad_glViewportIndexedfv = NULL; PFNGLWAITSYNCPROC glad_glWaitSync = NULL; PFNGLWEIGHTPOINTERARBPROC glad_glWeightPointerARB = NULL; PFNGLWEIGHTBVARBPROC glad_glWeightbvARB = NULL; PFNGLWEIGHTDVARBPROC glad_glWeightdvARB = NULL; PFNGLWEIGHTFVARBPROC glad_glWeightfvARB = NULL; PFNGLWEIGHTIVARBPROC glad_glWeightivARB = NULL; PFNGLWEIGHTSVARBPROC glad_glWeightsvARB = NULL; PFNGLWEIGHTUBVARBPROC glad_glWeightubvARB = NULL; PFNGLWEIGHTUIVARBPROC glad_glWeightuivARB = NULL; PFNGLWEIGHTUSVARBPROC glad_glWeightusvARB = NULL; PFNGLWINDOWPOS2DPROC glad_glWindowPos2d = NULL; PFNGLWINDOWPOS2DARBPROC glad_glWindowPos2dARB = NULL; PFNGLWINDOWPOS2DVPROC glad_glWindowPos2dv = NULL; PFNGLWINDOWPOS2DVARBPROC glad_glWindowPos2dvARB = NULL; PFNGLWINDOWPOS2FPROC glad_glWindowPos2f = NULL; PFNGLWINDOWPOS2FARBPROC glad_glWindowPos2fARB = NULL; PFNGLWINDOWPOS2FVPROC glad_glWindowPos2fv = NULL; PFNGLWINDOWPOS2FVARBPROC glad_glWindowPos2fvARB = NULL; PFNGLWINDOWPOS2IPROC glad_glWindowPos2i = NULL; PFNGLWINDOWPOS2IARBPROC glad_glWindowPos2iARB = NULL; PFNGLWINDOWPOS2IVPROC glad_glWindowPos2iv = NULL; PFNGLWINDOWPOS2IVARBPROC glad_glWindowPos2ivARB = NULL; PFNGLWINDOWPOS2SPROC glad_glWindowPos2s = NULL; PFNGLWINDOWPOS2SARBPROC glad_glWindowPos2sARB = NULL; PFNGLWINDOWPOS2SVPROC glad_glWindowPos2sv = NULL; PFNGLWINDOWPOS2SVARBPROC glad_glWindowPos2svARB = NULL; PFNGLWINDOWPOS3DPROC glad_glWindowPos3d = NULL; PFNGLWINDOWPOS3DARBPROC glad_glWindowPos3dARB = NULL; PFNGLWINDOWPOS3DVPROC glad_glWindowPos3dv = NULL; PFNGLWINDOWPOS3DVARBPROC glad_glWindowPos3dvARB = NULL; PFNGLWINDOWPOS3FPROC glad_glWindowPos3f = NULL; PFNGLWINDOWPOS3FARBPROC glad_glWindowPos3fARB = NULL; PFNGLWINDOWPOS3FVPROC glad_glWindowPos3fv = NULL; PFNGLWINDOWPOS3FVARBPROC glad_glWindowPos3fvARB = NULL; PFNGLWINDOWPOS3IPROC glad_glWindowPos3i = NULL; PFNGLWINDOWPOS3IARBPROC glad_glWindowPos3iARB = NULL; PFNGLWINDOWPOS3IVPROC glad_glWindowPos3iv = NULL; PFNGLWINDOWPOS3IVARBPROC glad_glWindowPos3ivARB = NULL; PFNGLWINDOWPOS3SPROC glad_glWindowPos3s = NULL; PFNGLWINDOWPOS3SARBPROC glad_glWindowPos3sARB = NULL; PFNGLWINDOWPOS3SVPROC glad_glWindowPos3sv = NULL; PFNGLWINDOWPOS3SVARBPROC glad_glWindowPos3svARB = NULL; static void glad_gl_load_GL_VERSION_1_0( GLADuserptrloadfunc load, void* userptr) { if(!GLAD_GL_VERSION_1_0) return; glad_glAccum = (PFNGLACCUMPROC) load(userptr, "glAccum"); glad_glAlphaFunc = (PFNGLALPHAFUNCPROC) load(userptr, "glAlphaFunc"); glad_glBegin = (PFNGLBEGINPROC) load(userptr, "glBegin"); glad_glBitmap = (PFNGLBITMAPPROC) load(userptr, "glBitmap"); glad_glBlendFunc = (PFNGLBLENDFUNCPROC) load(userptr, "glBlendFunc"); glad_glCallList = (PFNGLCALLLISTPROC) load(userptr, "glCallList"); glad_glCallLists = (PFNGLCALLLISTSPROC) load(userptr, "glCallLists"); glad_glClear = (PFNGLCLEARPROC) load(userptr, "glClear"); glad_glClearAccum = (PFNGLCLEARACCUMPROC) load(userptr, "glClearAccum"); glad_glClearColor = (PFNGLCLEARCOLORPROC) load(userptr, "glClearColor"); glad_glClearDepth = (PFNGLCLEARDEPTHPROC) load(userptr, "glClearDepth"); glad_glClearIndex = (PFNGLCLEARINDEXPROC) load(userptr, "glClearIndex"); glad_glClearStencil = (PFNGLCLEARSTENCILPROC) load(userptr, "glClearStencil"); glad_glClipPlane = (PFNGLCLIPPLANEPROC) load(userptr, "glClipPlane"); glad_glColor3b = (PFNGLCOLOR3BPROC) load(userptr, "glColor3b"); glad_glColor3bv = (PFNGLCOLOR3BVPROC) load(userptr, "glColor3bv"); glad_glColor3d = (PFNGLCOLOR3DPROC) load(userptr, "glColor3d"); glad_glColor3dv = (PFNGLCOLOR3DVPROC) load(userptr, "glColor3dv"); glad_glColor3f = (PFNGLCOLOR3FPROC) load(userptr, "glColor3f"); glad_glColor3fv = (PFNGLCOLOR3FVPROC) load(userptr, "glColor3fv"); glad_glColor3i = (PFNGLCOLOR3IPROC) load(userptr, "glColor3i"); glad_glColor3iv = (PFNGLCOLOR3IVPROC) load(userptr, "glColor3iv"); glad_glColor3s = (PFNGLCOLOR3SPROC) load(userptr, "glColor3s"); glad_glColor3sv = (PFNGLCOLOR3SVPROC) load(userptr, "glColor3sv"); glad_glColor3ub = (PFNGLCOLOR3UBPROC) load(userptr, "glColor3ub"); glad_glColor3ubv = (PFNGLCOLOR3UBVPROC) load(userptr, "glColor3ubv"); glad_glColor3ui = (PFNGLCOLOR3UIPROC) load(userptr, "glColor3ui"); glad_glColor3uiv = (PFNGLCOLOR3UIVPROC) load(userptr, "glColor3uiv"); glad_glColor3us = (PFNGLCOLOR3USPROC) load(userptr, "glColor3us"); glad_glColor3usv = (PFNGLCOLOR3USVPROC) load(userptr, "glColor3usv"); glad_glColor4b = (PFNGLCOLOR4BPROC) load(userptr, "glColor4b"); glad_glColor4bv = (PFNGLCOLOR4BVPROC) load(userptr, "glColor4bv"); glad_glColor4d = (PFNGLCOLOR4DPROC) load(userptr, "glColor4d"); glad_glColor4dv = (PFNGLCOLOR4DVPROC) load(userptr, "glColor4dv"); glad_glColor4f = (PFNGLCOLOR4FPROC) load(userptr, "glColor4f"); glad_glColor4fv = (PFNGLCOLOR4FVPROC) load(userptr, "glColor4fv"); glad_glColor4i = (PFNGLCOLOR4IPROC) load(userptr, "glColor4i"); glad_glColor4iv = (PFNGLCOLOR4IVPROC) load(userptr, "glColor4iv"); glad_glColor4s = (PFNGLCOLOR4SPROC) load(userptr, "glColor4s"); glad_glColor4sv = (PFNGLCOLOR4SVPROC) load(userptr, "glColor4sv"); glad_glColor4ub = (PFNGLCOLOR4UBPROC) load(userptr, "glColor4ub"); glad_glColor4ubv = (PFNGLCOLOR4UBVPROC) load(userptr, "glColor4ubv"); glad_glColor4ui = (PFNGLCOLOR4UIPROC) load(userptr, "glColor4ui"); glad_glColor4uiv = (PFNGLCOLOR4UIVPROC) load(userptr, "glColor4uiv"); glad_glColor4us = (PFNGLCOLOR4USPROC) load(userptr, "glColor4us"); glad_glColor4usv = (PFNGLCOLOR4USVPROC) load(userptr, "glColor4usv"); glad_glColorMask = (PFNGLCOLORMASKPROC) load(userptr, "glColorMask"); glad_glColorMaterial = (PFNGLCOLORMATERIALPROC) load(userptr, "glColorMaterial"); glad_glCopyPixels = (PFNGLCOPYPIXELSPROC) load(userptr, "glCopyPixels"); glad_glCullFace = (PFNGLCULLFACEPROC) load(userptr, "glCullFace"); glad_glDeleteLists = (PFNGLDELETELISTSPROC) load(userptr, "glDeleteLists"); glad_glDepthFunc = (PFNGLDEPTHFUNCPROC) load(userptr, "glDepthFunc"); glad_glDepthMask = (PFNGLDEPTHMASKPROC) load(userptr, "glDepthMask"); glad_glDepthRange = (PFNGLDEPTHRANGEPROC) load(userptr, "glDepthRange"); glad_glDisable = (PFNGLDISABLEPROC) load(userptr, "glDisable"); glad_glDrawBuffer = (PFNGLDRAWBUFFERPROC) load(userptr, "glDrawBuffer"); glad_glDrawPixels = (PFNGLDRAWPIXELSPROC) load(userptr, "glDrawPixels"); glad_glEdgeFlag = (PFNGLEDGEFLAGPROC) load(userptr, "glEdgeFlag"); glad_glEdgeFlagv = (PFNGLEDGEFLAGVPROC) load(userptr, "glEdgeFlagv"); glad_glEnable = (PFNGLENABLEPROC) load(userptr, "glEnable"); glad_glEnd = (PFNGLENDPROC) load(userptr, "glEnd"); glad_glEndList = (PFNGLENDLISTPROC) load(userptr, "glEndList"); glad_glEvalCoord1d = (PFNGLEVALCOORD1DPROC) load(userptr, "glEvalCoord1d"); glad_glEvalCoord1dv = (PFNGLEVALCOORD1DVPROC) load(userptr, "glEvalCoord1dv"); glad_glEvalCoord1f = (PFNGLEVALCOORD1FPROC) load(userptr, "glEvalCoord1f"); glad_glEvalCoord1fv = (PFNGLEVALCOORD1FVPROC) load(userptr, "glEvalCoord1fv"); glad_glEvalCoord2d = (PFNGLEVALCOORD2DPROC) load(userptr, "glEvalCoord2d"); glad_glEvalCoord2dv = (PFNGLEVALCOORD2DVPROC) load(userptr, "glEvalCoord2dv"); glad_glEvalCoord2f = (PFNGLEVALCOORD2FPROC) load(userptr, "glEvalCoord2f"); glad_glEvalCoord2fv = (PFNGLEVALCOORD2FVPROC) load(userptr, "glEvalCoord2fv"); glad_glEvalMesh1 = (PFNGLEVALMESH1PROC) load(userptr, "glEvalMesh1"); glad_glEvalMesh2 = (PFNGLEVALMESH2PROC) load(userptr, "glEvalMesh2"); glad_glEvalPoint1 = (PFNGLEVALPOINT1PROC) load(userptr, "glEvalPoint1"); glad_glEvalPoint2 = (PFNGLEVALPOINT2PROC) load(userptr, "glEvalPoint2"); glad_glFeedbackBuffer = (PFNGLFEEDBACKBUFFERPROC) load(userptr, "glFeedbackBuffer"); glad_glFinish = (PFNGLFINISHPROC) load(userptr, "glFinish"); glad_glFlush = (PFNGLFLUSHPROC) load(userptr, "glFlush"); glad_glFogf = (PFNGLFOGFPROC) load(userptr, "glFogf"); glad_glFogfv = (PFNGLFOGFVPROC) load(userptr, "glFogfv"); glad_glFogi = (PFNGLFOGIPROC) load(userptr, "glFogi"); glad_glFogiv = (PFNGLFOGIVPROC) load(userptr, "glFogiv"); glad_glFrontFace = (PFNGLFRONTFACEPROC) load(userptr, "glFrontFace"); glad_glFrustum = (PFNGLFRUSTUMPROC) load(userptr, "glFrustum"); glad_glGenLists = (PFNGLGENLISTSPROC) load(userptr, "glGenLists"); glad_glGetBooleanv = (PFNGLGETBOOLEANVPROC) load(userptr, "glGetBooleanv"); glad_glGetClipPlane = (PFNGLGETCLIPPLANEPROC) load(userptr, "glGetClipPlane"); glad_glGetDoublev = (PFNGLGETDOUBLEVPROC) load(userptr, "glGetDoublev"); glad_glGetError = (PFNGLGETERRORPROC) load(userptr, "glGetError"); glad_glGetFloatv = (PFNGLGETFLOATVPROC) load(userptr, "glGetFloatv"); glad_glGetIntegerv = (PFNGLGETINTEGERVPROC) load(userptr, "glGetIntegerv"); glad_glGetLightfv = (PFNGLGETLIGHTFVPROC) load(userptr, "glGetLightfv"); glad_glGetLightiv = (PFNGLGETLIGHTIVPROC) load(userptr, "glGetLightiv"); glad_glGetMapdv = (PFNGLGETMAPDVPROC) load(userptr, "glGetMapdv"); glad_glGetMapfv = (PFNGLGETMAPFVPROC) load(userptr, "glGetMapfv"); glad_glGetMapiv = (PFNGLGETMAPIVPROC) load(userptr, "glGetMapiv"); glad_glGetMaterialfv = (PFNGLGETMATERIALFVPROC) load(userptr, "glGetMaterialfv"); glad_glGetMaterialiv = (PFNGLGETMATERIALIVPROC) load(userptr, "glGetMaterialiv"); glad_glGetPixelMapfv = (PFNGLGETPIXELMAPFVPROC) load(userptr, "glGetPixelMapfv"); glad_glGetPixelMapuiv = (PFNGLGETPIXELMAPUIVPROC) load(userptr, "glGetPixelMapuiv"); glad_glGetPixelMapusv = (PFNGLGETPIXELMAPUSVPROC) load(userptr, "glGetPixelMapusv"); glad_glGetPolygonStipple = (PFNGLGETPOLYGONSTIPPLEPROC) load(userptr, "glGetPolygonStipple"); glad_glGetString = (PFNGLGETSTRINGPROC) load(userptr, "glGetString"); glad_glGetTexEnvfv = (PFNGLGETTEXENVFVPROC) load(userptr, "glGetTexEnvfv"); glad_glGetTexEnviv = (PFNGLGETTEXENVIVPROC) load(userptr, "glGetTexEnviv"); glad_glGetTexGendv = (PFNGLGETTEXGENDVPROC) load(userptr, "glGetTexGendv"); glad_glGetTexGenfv = (PFNGLGETTEXGENFVPROC) load(userptr, "glGetTexGenfv"); glad_glGetTexGeniv = (PFNGLGETTEXGENIVPROC) load(userptr, "glGetTexGeniv"); glad_glGetTexImage = (PFNGLGETTEXIMAGEPROC) load(userptr, "glGetTexImage"); glad_glGetTexLevelParameterfv = (PFNGLGETTEXLEVELPARAMETERFVPROC) load(userptr, "glGetTexLevelParameterfv"); glad_glGetTexLevelParameteriv = (PFNGLGETTEXLEVELPARAMETERIVPROC) load(userptr, "glGetTexLevelParameteriv"); glad_glGetTexParameterfv = (PFNGLGETTEXPARAMETERFVPROC) load(userptr, "glGetTexParameterfv"); glad_glGetTexParameteriv = (PFNGLGETTEXPARAMETERIVPROC) load(userptr, "glGetTexParameteriv"); glad_glHint = (PFNGLHINTPROC) load(userptr, "glHint"); glad_glIndexMask = (PFNGLINDEXMASKPROC) load(userptr, "glIndexMask"); glad_glIndexd = (PFNGLINDEXDPROC) load(userptr, "glIndexd"); glad_glIndexdv = (PFNGLINDEXDVPROC) load(userptr, "glIndexdv"); glad_glIndexf = (PFNGLINDEXFPROC) load(userptr, "glIndexf"); glad_glIndexfv = (PFNGLINDEXFVPROC) load(userptr, "glIndexfv"); glad_glIndexi = (PFNGLINDEXIPROC) load(userptr, "glIndexi"); glad_glIndexiv = (PFNGLINDEXIVPROC) load(userptr, "glIndexiv"); glad_glIndexs = (PFNGLINDEXSPROC) load(userptr, "glIndexs"); glad_glIndexsv = (PFNGLINDEXSVPROC) load(userptr, "glIndexsv"); glad_glInitNames = (PFNGLINITNAMESPROC) load(userptr, "glInitNames"); glad_glIsEnabled = (PFNGLISENABLEDPROC) load(userptr, "glIsEnabled"); glad_glIsList = (PFNGLISLISTPROC) load(userptr, "glIsList"); glad_glLightModelf = (PFNGLLIGHTMODELFPROC) load(userptr, "glLightModelf"); glad_glLightModelfv = (PFNGLLIGHTMODELFVPROC) load(userptr, "glLightModelfv"); glad_glLightModeli = (PFNGLLIGHTMODELIPROC) load(userptr, "glLightModeli"); glad_glLightModeliv = (PFNGLLIGHTMODELIVPROC) load(userptr, "glLightModeliv"); glad_glLightf = (PFNGLLIGHTFPROC) load(userptr, "glLightf"); glad_glLightfv = (PFNGLLIGHTFVPROC) load(userptr, "glLightfv"); glad_glLighti = (PFNGLLIGHTIPROC) load(userptr, "glLighti"); glad_glLightiv = (PFNGLLIGHTIVPROC) load(userptr, "glLightiv"); glad_glLineStipple = (PFNGLLINESTIPPLEPROC) load(userptr, "glLineStipple"); glad_glLineWidth = (PFNGLLINEWIDTHPROC) load(userptr, "glLineWidth"); glad_glListBase = (PFNGLLISTBASEPROC) load(userptr, "glListBase"); glad_glLoadIdentity = (PFNGLLOADIDENTITYPROC) load(userptr, "glLoadIdentity"); glad_glLoadMatrixd = (PFNGLLOADMATRIXDPROC) load(userptr, "glLoadMatrixd"); glad_glLoadMatrixf = (PFNGLLOADMATRIXFPROC) load(userptr, "glLoadMatrixf"); glad_glLoadName = (PFNGLLOADNAMEPROC) load(userptr, "glLoadName"); glad_glLogicOp = (PFNGLLOGICOPPROC) load(userptr, "glLogicOp"); glad_glMap1d = (PFNGLMAP1DPROC) load(userptr, "glMap1d"); glad_glMap1f = (PFNGLMAP1FPROC) load(userptr, "glMap1f"); glad_glMap2d = (PFNGLMAP2DPROC) load(userptr, "glMap2d"); glad_glMap2f = (PFNGLMAP2FPROC) load(userptr, "glMap2f"); glad_glMapGrid1d = (PFNGLMAPGRID1DPROC) load(userptr, "glMapGrid1d"); glad_glMapGrid1f = (PFNGLMAPGRID1FPROC) load(userptr, "glMapGrid1f"); glad_glMapGrid2d = (PFNGLMAPGRID2DPROC) load(userptr, "glMapGrid2d"); glad_glMapGrid2f = (PFNGLMAPGRID2FPROC) load(userptr, "glMapGrid2f"); glad_glMaterialf = (PFNGLMATERIALFPROC) load(userptr, "glMaterialf"); glad_glMaterialfv = (PFNGLMATERIALFVPROC) load(userptr, "glMaterialfv"); glad_glMateriali = (PFNGLMATERIALIPROC) load(userptr, "glMateriali"); glad_glMaterialiv = (PFNGLMATERIALIVPROC) load(userptr, "glMaterialiv"); glad_glMatrixMode = (PFNGLMATRIXMODEPROC) load(userptr, "glMatrixMode"); glad_glMultMatrixd = (PFNGLMULTMATRIXDPROC) load(userptr, "glMultMatrixd"); glad_glMultMatrixf = (PFNGLMULTMATRIXFPROC) load(userptr, "glMultMatrixf"); glad_glNewList = (PFNGLNEWLISTPROC) load(userptr, "glNewList"); glad_glNormal3b = (PFNGLNORMAL3BPROC) load(userptr, "glNormal3b"); glad_glNormal3bv = (PFNGLNORMAL3BVPROC) load(userptr, "glNormal3bv"); glad_glNormal3d = (PFNGLNORMAL3DPROC) load(userptr, "glNormal3d"); glad_glNormal3dv = (PFNGLNORMAL3DVPROC) load(userptr, "glNormal3dv"); glad_glNormal3f = (PFNGLNORMAL3FPROC) load(userptr, "glNormal3f"); glad_glNormal3fv = (PFNGLNORMAL3FVPROC) load(userptr, "glNormal3fv"); glad_glNormal3i = (PFNGLNORMAL3IPROC) load(userptr, "glNormal3i"); glad_glNormal3iv = (PFNGLNORMAL3IVPROC) load(userptr, "glNormal3iv"); glad_glNormal3s = (PFNGLNORMAL3SPROC) load(userptr, "glNormal3s"); glad_glNormal3sv = (PFNGLNORMAL3SVPROC) load(userptr, "glNormal3sv"); glad_glOrtho = (PFNGLORTHOPROC) load(userptr, "glOrtho"); glad_glPassThrough = (PFNGLPASSTHROUGHPROC) load(userptr, "glPassThrough"); glad_glPixelMapfv = (PFNGLPIXELMAPFVPROC) load(userptr, "glPixelMapfv"); glad_glPixelMapuiv = (PFNGLPIXELMAPUIVPROC) load(userptr, "glPixelMapuiv"); glad_glPixelMapusv = (PFNGLPIXELMAPUSVPROC) load(userptr, "glPixelMapusv"); glad_glPixelStoref = (PFNGLPIXELSTOREFPROC) load(userptr, "glPixelStoref"); glad_glPixelStorei = (PFNGLPIXELSTOREIPROC) load(userptr, "glPixelStorei"); glad_glPixelTransferf = (PFNGLPIXELTRANSFERFPROC) load(userptr, "glPixelTransferf"); glad_glPixelTransferi = (PFNGLPIXELTRANSFERIPROC) load(userptr, "glPixelTransferi"); glad_glPixelZoom = (PFNGLPIXELZOOMPROC) load(userptr, "glPixelZoom"); glad_glPointSize = (PFNGLPOINTSIZEPROC) load(userptr, "glPointSize"); glad_glPolygonMode = (PFNGLPOLYGONMODEPROC) load(userptr, "glPolygonMode"); glad_glPolygonStipple = (PFNGLPOLYGONSTIPPLEPROC) load(userptr, "glPolygonStipple"); glad_glPopAttrib = (PFNGLPOPATTRIBPROC) load(userptr, "glPopAttrib"); glad_glPopMatrix = (PFNGLPOPMATRIXPROC) load(userptr, "glPopMatrix"); glad_glPopName = (PFNGLPOPNAMEPROC) load(userptr, "glPopName"); glad_glPushAttrib = (PFNGLPUSHATTRIBPROC) load(userptr, "glPushAttrib"); glad_glPushMatrix = (PFNGLPUSHMATRIXPROC) load(userptr, "glPushMatrix"); glad_glPushName = (PFNGLPUSHNAMEPROC) load(userptr, "glPushName"); glad_glRasterPos2d = (PFNGLRASTERPOS2DPROC) load(userptr, "glRasterPos2d"); glad_glRasterPos2dv = (PFNGLRASTERPOS2DVPROC) load(userptr, "glRasterPos2dv"); glad_glRasterPos2f = (PFNGLRASTERPOS2FPROC) load(userptr, "glRasterPos2f"); glad_glRasterPos2fv = (PFNGLRASTERPOS2FVPROC) load(userptr, "glRasterPos2fv"); glad_glRasterPos2i = (PFNGLRASTERPOS2IPROC) load(userptr, "glRasterPos2i"); glad_glRasterPos2iv = (PFNGLRASTERPOS2IVPROC) load(userptr, "glRasterPos2iv"); glad_glRasterPos2s = (PFNGLRASTERPOS2SPROC) load(userptr, "glRasterPos2s"); glad_glRasterPos2sv = (PFNGLRASTERPOS2SVPROC) load(userptr, "glRasterPos2sv"); glad_glRasterPos3d = (PFNGLRASTERPOS3DPROC) load(userptr, "glRasterPos3d"); glad_glRasterPos3dv = (PFNGLRASTERPOS3DVPROC) load(userptr, "glRasterPos3dv"); glad_glRasterPos3f = (PFNGLRASTERPOS3FPROC) load(userptr, "glRasterPos3f"); glad_glRasterPos3fv = (PFNGLRASTERPOS3FVPROC) load(userptr, "glRasterPos3fv"); glad_glRasterPos3i = (PFNGLRASTERPOS3IPROC) load(userptr, "glRasterPos3i"); glad_glRasterPos3iv = (PFNGLRASTERPOS3IVPROC) load(userptr, "glRasterPos3iv"); glad_glRasterPos3s = (PFNGLRASTERPOS3SPROC) load(userptr, "glRasterPos3s"); glad_glRasterPos3sv = (PFNGLRASTERPOS3SVPROC) load(userptr, "glRasterPos3sv"); glad_glRasterPos4d = (PFNGLRASTERPOS4DPROC) load(userptr, "glRasterPos4d"); glad_glRasterPos4dv = (PFNGLRASTERPOS4DVPROC) load(userptr, "glRasterPos4dv"); glad_glRasterPos4f = (PFNGLRASTERPOS4FPROC) load(userptr, "glRasterPos4f"); glad_glRasterPos4fv = (PFNGLRASTERPOS4FVPROC) load(userptr, "glRasterPos4fv"); glad_glRasterPos4i = (PFNGLRASTERPOS4IPROC) load(userptr, "glRasterPos4i"); glad_glRasterPos4iv = (PFNGLRASTERPOS4IVPROC) load(userptr, "glRasterPos4iv"); glad_glRasterPos4s = (PFNGLRASTERPOS4SPROC) load(userptr, "glRasterPos4s"); glad_glRasterPos4sv = (PFNGLRASTERPOS4SVPROC) load(userptr, "glRasterPos4sv"); glad_glReadBuffer = (PFNGLREADBUFFERPROC) load(userptr, "glReadBuffer"); glad_glReadPixels = (PFNGLREADPIXELSPROC) load(userptr, "glReadPixels"); glad_glRectd = (PFNGLRECTDPROC) load(userptr, "glRectd"); glad_glRectdv = (PFNGLRECTDVPROC) load(userptr, "glRectdv"); glad_glRectf = (PFNGLRECTFPROC) load(userptr, "glRectf"); glad_glRectfv = (PFNGLRECTFVPROC) load(userptr, "glRectfv"); glad_glRecti = (PFNGLRECTIPROC) load(userptr, "glRecti"); glad_glRectiv = (PFNGLRECTIVPROC) load(userptr, "glRectiv"); glad_glRects = (PFNGLRECTSPROC) load(userptr, "glRects"); glad_glRectsv = (PFNGLRECTSVPROC) load(userptr, "glRectsv"); glad_glRenderMode = (PFNGLRENDERMODEPROC) load(userptr, "glRenderMode"); glad_glRotated = (PFNGLROTATEDPROC) load(userptr, "glRotated"); glad_glRotatef = (PFNGLROTATEFPROC) load(userptr, "glRotatef"); glad_glScaled = (PFNGLSCALEDPROC) load(userptr, "glScaled"); glad_glScalef = (PFNGLSCALEFPROC) load(userptr, "glScalef"); glad_glScissor = (PFNGLSCISSORPROC) load(userptr, "glScissor"); glad_glSelectBuffer = (PFNGLSELECTBUFFERPROC) load(userptr, "glSelectBuffer"); glad_glShadeModel = (PFNGLSHADEMODELPROC) load(userptr, "glShadeModel"); glad_glStencilFunc = (PFNGLSTENCILFUNCPROC) load(userptr, "glStencilFunc"); glad_glStencilMask = (PFNGLSTENCILMASKPROC) load(userptr, "glStencilMask"); glad_glStencilOp = (PFNGLSTENCILOPPROC) load(userptr, "glStencilOp"); glad_glTexCoord1d = (PFNGLTEXCOORD1DPROC) load(userptr, "glTexCoord1d"); glad_glTexCoord1dv = (PFNGLTEXCOORD1DVPROC) load(userptr, "glTexCoord1dv"); glad_glTexCoord1f = (PFNGLTEXCOORD1FPROC) load(userptr, "glTexCoord1f"); glad_glTexCoord1fv = (PFNGLTEXCOORD1FVPROC) load(userptr, "glTexCoord1fv"); glad_glTexCoord1i = (PFNGLTEXCOORD1IPROC) load(userptr, "glTexCoord1i"); glad_glTexCoord1iv = (PFNGLTEXCOORD1IVPROC) load(userptr, "glTexCoord1iv"); glad_glTexCoord1s = (PFNGLTEXCOORD1SPROC) load(userptr, "glTexCoord1s"); glad_glTexCoord1sv = (PFNGLTEXCOORD1SVPROC) load(userptr, "glTexCoord1sv"); glad_glTexCoord2d = (PFNGLTEXCOORD2DPROC) load(userptr, "glTexCoord2d"); glad_glTexCoord2dv = (PFNGLTEXCOORD2DVPROC) load(userptr, "glTexCoord2dv"); glad_glTexCoord2f = (PFNGLTEXCOORD2FPROC) load(userptr, "glTexCoord2f"); glad_glTexCoord2fv = (PFNGLTEXCOORD2FVPROC) load(userptr, "glTexCoord2fv"); glad_glTexCoord2i = (PFNGLTEXCOORD2IPROC) load(userptr, "glTexCoord2i"); glad_glTexCoord2iv = (PFNGLTEXCOORD2IVPROC) load(userptr, "glTexCoord2iv"); glad_glTexCoord2s = (PFNGLTEXCOORD2SPROC) load(userptr, "glTexCoord2s"); glad_glTexCoord2sv = (PFNGLTEXCOORD2SVPROC) load(userptr, "glTexCoord2sv"); glad_glTexCoord3d = (PFNGLTEXCOORD3DPROC) load(userptr, "glTexCoord3d"); glad_glTexCoord3dv = (PFNGLTEXCOORD3DVPROC) load(userptr, "glTexCoord3dv"); glad_glTexCoord3f = (PFNGLTEXCOORD3FPROC) load(userptr, "glTexCoord3f"); glad_glTexCoord3fv = (PFNGLTEXCOORD3FVPROC) load(userptr, "glTexCoord3fv"); glad_glTexCoord3i = (PFNGLTEXCOORD3IPROC) load(userptr, "glTexCoord3i"); glad_glTexCoord3iv = (PFNGLTEXCOORD3IVPROC) load(userptr, "glTexCoord3iv"); glad_glTexCoord3s = (PFNGLTEXCOORD3SPROC) load(userptr, "glTexCoord3s"); glad_glTexCoord3sv = (PFNGLTEXCOORD3SVPROC) load(userptr, "glTexCoord3sv"); glad_glTexCoord4d = (PFNGLTEXCOORD4DPROC) load(userptr, "glTexCoord4d"); glad_glTexCoord4dv = (PFNGLTEXCOORD4DVPROC) load(userptr, "glTexCoord4dv"); glad_glTexCoord4f = (PFNGLTEXCOORD4FPROC) load(userptr, "glTexCoord4f"); glad_glTexCoord4fv = (PFNGLTEXCOORD4FVPROC) load(userptr, "glTexCoord4fv"); glad_glTexCoord4i = (PFNGLTEXCOORD4IPROC) load(userptr, "glTexCoord4i"); glad_glTexCoord4iv = (PFNGLTEXCOORD4IVPROC) load(userptr, "glTexCoord4iv"); glad_glTexCoord4s = (PFNGLTEXCOORD4SPROC) load(userptr, "glTexCoord4s"); glad_glTexCoord4sv = (PFNGLTEXCOORD4SVPROC) load(userptr, "glTexCoord4sv"); glad_glTexEnvf = (PFNGLTEXENVFPROC) load(userptr, "glTexEnvf"); glad_glTexEnvfv = (PFNGLTEXENVFVPROC) load(userptr, "glTexEnvfv"); glad_glTexEnvi = (PFNGLTEXENVIPROC) load(userptr, "glTexEnvi"); glad_glTexEnviv = (PFNGLTEXENVIVPROC) load(userptr, "glTexEnviv"); glad_glTexGend = (PFNGLTEXGENDPROC) load(userptr, "glTexGend"); glad_glTexGendv = (PFNGLTEXGENDVPROC) load(userptr, "glTexGendv"); glad_glTexGenf = (PFNGLTEXGENFPROC) load(userptr, "glTexGenf"); glad_glTexGenfv = (PFNGLTEXGENFVPROC) load(userptr, "glTexGenfv"); glad_glTexGeni = (PFNGLTEXGENIPROC) load(userptr, "glTexGeni"); glad_glTexGeniv = (PFNGLTEXGENIVPROC) load(userptr, "glTexGeniv"); glad_glTexImage1D = (PFNGLTEXIMAGE1DPROC) load(userptr, "glTexImage1D"); glad_glTexImage2D = (PFNGLTEXIMAGE2DPROC) load(userptr, "glTexImage2D"); glad_glTexParameterf = (PFNGLTEXPARAMETERFPROC) load(userptr, "glTexParameterf"); glad_glTexParameterfv = (PFNGLTEXPARAMETERFVPROC) load(userptr, "glTexParameterfv"); glad_glTexParameteri = (PFNGLTEXPARAMETERIPROC) load(userptr, "glTexParameteri"); glad_glTexParameteriv = (PFNGLTEXPARAMETERIVPROC) load(userptr, "glTexParameteriv"); glad_glTranslated = (PFNGLTRANSLATEDPROC) load(userptr, "glTranslated"); glad_glTranslatef = (PFNGLTRANSLATEFPROC) load(userptr, "glTranslatef"); glad_glVertex2d = (PFNGLVERTEX2DPROC) load(userptr, "glVertex2d"); glad_glVertex2dv = (PFNGLVERTEX2DVPROC) load(userptr, "glVertex2dv"); glad_glVertex2f = (PFNGLVERTEX2FPROC) load(userptr, "glVertex2f"); glad_glVertex2fv = (PFNGLVERTEX2FVPROC) load(userptr, "glVertex2fv"); glad_glVertex2i = (PFNGLVERTEX2IPROC) load(userptr, "glVertex2i"); glad_glVertex2iv = (PFNGLVERTEX2IVPROC) load(userptr, "glVertex2iv"); glad_glVertex2s = (PFNGLVERTEX2SPROC) load(userptr, "glVertex2s"); glad_glVertex2sv = (PFNGLVERTEX2SVPROC) load(userptr, "glVertex2sv"); glad_glVertex3d = (PFNGLVERTEX3DPROC) load(userptr, "glVertex3d"); glad_glVertex3dv = (PFNGLVERTEX3DVPROC) load(userptr, "glVertex3dv"); glad_glVertex3f = (PFNGLVERTEX3FPROC) load(userptr, "glVertex3f"); glad_glVertex3fv = (PFNGLVERTEX3FVPROC) load(userptr, "glVertex3fv"); glad_glVertex3i = (PFNGLVERTEX3IPROC) load(userptr, "glVertex3i"); glad_glVertex3iv = (PFNGLVERTEX3IVPROC) load(userptr, "glVertex3iv"); glad_glVertex3s = (PFNGLVERTEX3SPROC) load(userptr, "glVertex3s"); glad_glVertex3sv = (PFNGLVERTEX3SVPROC) load(userptr, "glVertex3sv"); glad_glVertex4d = (PFNGLVERTEX4DPROC) load(userptr, "glVertex4d"); glad_glVertex4dv = (PFNGLVERTEX4DVPROC) load(userptr, "glVertex4dv"); glad_glVertex4f = (PFNGLVERTEX4FPROC) load(userptr, "glVertex4f"); glad_glVertex4fv = (PFNGLVERTEX4FVPROC) load(userptr, "glVertex4fv"); glad_glVertex4i = (PFNGLVERTEX4IPROC) load(userptr, "glVertex4i"); glad_glVertex4iv = (PFNGLVERTEX4IVPROC) load(userptr, "glVertex4iv"); glad_glVertex4s = (PFNGLVERTEX4SPROC) load(userptr, "glVertex4s"); glad_glVertex4sv = (PFNGLVERTEX4SVPROC) load(userptr, "glVertex4sv"); glad_glViewport = (PFNGLVIEWPORTPROC) load(userptr, "glViewport"); } static void glad_gl_load_GL_VERSION_1_1( GLADuserptrloadfunc load, void* userptr) { if(!GLAD_GL_VERSION_1_1) return; glad_glAreTexturesResident = (PFNGLARETEXTURESRESIDENTPROC) load(userptr, "glAreTexturesResident"); glad_glArrayElement = (PFNGLARRAYELEMENTPROC) load(userptr, "glArrayElement"); glad_glBindTexture = (PFNGLBINDTEXTUREPROC) load(userptr, "glBindTexture"); glad_glColorPointer = (PFNGLCOLORPOINTERPROC) load(userptr, "glColorPointer"); glad_glCopyTexImage1D = (PFNGLCOPYTEXIMAGE1DPROC) load(userptr, "glCopyTexImage1D"); glad_glCopyTexImage2D = (PFNGLCOPYTEXIMAGE2DPROC) load(userptr, "glCopyTexImage2D"); glad_glCopyTexSubImage1D = (PFNGLCOPYTEXSUBIMAGE1DPROC) load(userptr, "glCopyTexSubImage1D"); glad_glCopyTexSubImage2D = (PFNGLCOPYTEXSUBIMAGE2DPROC) load(userptr, "glCopyTexSubImage2D"); glad_glDeleteTextures = (PFNGLDELETETEXTURESPROC) load(userptr, "glDeleteTextures"); glad_glDisableClientState = (PFNGLDISABLECLIENTSTATEPROC) load(userptr, "glDisableClientState"); glad_glDrawArrays = (PFNGLDRAWARRAYSPROC) load(userptr, "glDrawArrays"); glad_glDrawElements = (PFNGLDRAWELEMENTSPROC) load(userptr, "glDrawElements"); glad_glEdgeFlagPointer = (PFNGLEDGEFLAGPOINTERPROC) load(userptr, "glEdgeFlagPointer"); glad_glEnableClientState = (PFNGLENABLECLIENTSTATEPROC) load(userptr, "glEnableClientState"); glad_glGenTextures = (PFNGLGENTEXTURESPROC) load(userptr, "glGenTextures"); glad_glGetPointerv = (PFNGLGETPOINTERVPROC) load(userptr, "glGetPointerv"); glad_glIndexPointer = (PFNGLINDEXPOINTERPROC) load(userptr, "glIndexPointer"); glad_glIndexub = (PFNGLINDEXUBPROC) load(userptr, "glIndexub"); glad_glIndexubv = (PFNGLINDEXUBVPROC) load(userptr, "glIndexubv"); glad_glInterleavedArrays = (PFNGLINTERLEAVEDARRAYSPROC) load(userptr, "glInterleavedArrays"); glad_glIsTexture = (PFNGLISTEXTUREPROC) load(userptr, "glIsTexture"); glad_glNormalPointer = (PFNGLNORMALPOINTERPROC) load(userptr, "glNormalPointer"); glad_glPolygonOffset = (PFNGLPOLYGONOFFSETPROC) load(userptr, "glPolygonOffset"); glad_glPopClientAttrib = (PFNGLPOPCLIENTATTRIBPROC) load(userptr, "glPopClientAttrib"); glad_glPrioritizeTextures = (PFNGLPRIORITIZETEXTURESPROC) load(userptr, "glPrioritizeTextures"); glad_glPushClientAttrib = (PFNGLPUSHCLIENTATTRIBPROC) load(userptr, "glPushClientAttrib"); glad_glTexCoordPointer = (PFNGLTEXCOORDPOINTERPROC) load(userptr, "glTexCoordPointer"); glad_glTexSubImage1D = (PFNGLTEXSUBIMAGE1DPROC) load(userptr, "glTexSubImage1D"); glad_glTexSubImage2D = (PFNGLTEXSUBIMAGE2DPROC) load(userptr, "glTexSubImage2D"); glad_glVertexPointer = (PFNGLVERTEXPOINTERPROC) load(userptr, "glVertexPointer"); } static void glad_gl_load_GL_VERSION_1_2( GLADuserptrloadfunc load, void* userptr) { if(!GLAD_GL_VERSION_1_2) return; glad_glCopyTexSubImage3D = (PFNGLCOPYTEXSUBIMAGE3DPROC) load(userptr, "glCopyTexSubImage3D"); glad_glDrawRangeElements = (PFNGLDRAWRANGEELEMENTSPROC) load(userptr, "glDrawRangeElements"); glad_glTexImage3D = (PFNGLTEXIMAGE3DPROC) load(userptr, "glTexImage3D"); glad_glTexSubImage3D = (PFNGLTEXSUBIMAGE3DPROC) load(userptr, "glTexSubImage3D"); } static void glad_gl_load_GL_VERSION_1_3( GLADuserptrloadfunc load, void* userptr) { if(!GLAD_GL_VERSION_1_3) return; glad_glActiveTexture = (PFNGLACTIVETEXTUREPROC) load(userptr, "glActiveTexture"); glad_glClientActiveTexture = (PFNGLCLIENTACTIVETEXTUREPROC) load(userptr, "glClientActiveTexture"); glad_glCompressedTexImage1D = (PFNGLCOMPRESSEDTEXIMAGE1DPROC) load(userptr, "glCompressedTexImage1D"); glad_glCompressedTexImage2D = (PFNGLCOMPRESSEDTEXIMAGE2DPROC) load(userptr, "glCompressedTexImage2D"); glad_glCompressedTexImage3D = (PFNGLCOMPRESSEDTEXIMAGE3DPROC) load(userptr, "glCompressedTexImage3D"); glad_glCompressedTexSubImage1D = (PFNGLCOMPRESSEDTEXSUBIMAGE1DPROC) load(userptr, "glCompressedTexSubImage1D"); glad_glCompressedTexSubImage2D = (PFNGLCOMPRESSEDTEXSUBIMAGE2DPROC) load(userptr, "glCompressedTexSubImage2D"); glad_glCompressedTexSubImage3D = (PFNGLCOMPRESSEDTEXSUBIMAGE3DPROC) load(userptr, "glCompressedTexSubImage3D"); glad_glGetCompressedTexImage = (PFNGLGETCOMPRESSEDTEXIMAGEPROC) load(userptr, "glGetCompressedTexImage"); glad_glLoadTransposeMatrixd = (PFNGLLOADTRANSPOSEMATRIXDPROC) load(userptr, "glLoadTransposeMatrixd"); glad_glLoadTransposeMatrixf = (PFNGLLOADTRANSPOSEMATRIXFPROC) load(userptr, "glLoadTransposeMatrixf"); glad_glMultTransposeMatrixd = (PFNGLMULTTRANSPOSEMATRIXDPROC) load(userptr, "glMultTransposeMatrixd"); glad_glMultTransposeMatrixf = (PFNGLMULTTRANSPOSEMATRIXFPROC) load(userptr, "glMultTransposeMatrixf"); glad_glMultiTexCoord1d = (PFNGLMULTITEXCOORD1DPROC) load(userptr, "glMultiTexCoord1d"); glad_glMultiTexCoord1dv = (PFNGLMULTITEXCOORD1DVPROC) load(userptr, "glMultiTexCoord1dv"); glad_glMultiTexCoord1f = (PFNGLMULTITEXCOORD1FPROC) load(userptr, "glMultiTexCoord1f"); glad_glMultiTexCoord1fv = (PFNGLMULTITEXCOORD1FVPROC) load(userptr, "glMultiTexCoord1fv"); glad_glMultiTexCoord1i = (PFNGLMULTITEXCOORD1IPROC) load(userptr, "glMultiTexCoord1i"); glad_glMultiTexCoord1iv = (PFNGLMULTITEXCOORD1IVPROC) load(userptr, "glMultiTexCoord1iv"); glad_glMultiTexCoord1s = (PFNGLMULTITEXCOORD1SPROC) load(userptr, "glMultiTexCoord1s"); glad_glMultiTexCoord1sv = (PFNGLMULTITEXCOORD1SVPROC) load(userptr, "glMultiTexCoord1sv"); glad_glMultiTexCoord2d = (PFNGLMULTITEXCOORD2DPROC) load(userptr, "glMultiTexCoord2d"); glad_glMultiTexCoord2dv = (PFNGLMULTITEXCOORD2DVPROC) load(userptr, "glMultiTexCoord2dv"); glad_glMultiTexCoord2f = (PFNGLMULTITEXCOORD2FPROC) load(userptr, "glMultiTexCoord2f"); glad_glMultiTexCoord2fv = (PFNGLMULTITEXCOORD2FVPROC) load(userptr, "glMultiTexCoord2fv"); glad_glMultiTexCoord2i = (PFNGLMULTITEXCOORD2IPROC) load(userptr, "glMultiTexCoord2i"); glad_glMultiTexCoord2iv = (PFNGLMULTITEXCOORD2IVPROC) load(userptr, "glMultiTexCoord2iv"); glad_glMultiTexCoord2s = (PFNGLMULTITEXCOORD2SPROC) load(userptr, "glMultiTexCoord2s"); glad_glMultiTexCoord2sv = (PFNGLMULTITEXCOORD2SVPROC) load(userptr, "glMultiTexCoord2sv"); glad_glMultiTexCoord3d = (PFNGLMULTITEXCOORD3DPROC) load(userptr, "glMultiTexCoord3d"); glad_glMultiTexCoord3dv = (PFNGLMULTITEXCOORD3DVPROC) load(userptr, "glMultiTexCoord3dv"); glad_glMultiTexCoord3f = (PFNGLMULTITEXCOORD3FPROC) load(userptr, "glMultiTexCoord3f"); glad_glMultiTexCoord3fv = (PFNGLMULTITEXCOORD3FVPROC) load(userptr, "glMultiTexCoord3fv"); glad_glMultiTexCoord3i = (PFNGLMULTITEXCOORD3IPROC) load(userptr, "glMultiTexCoord3i"); glad_glMultiTexCoord3iv = (PFNGLMULTITEXCOORD3IVPROC) load(userptr, "glMultiTexCoord3iv"); glad_glMultiTexCoord3s = (PFNGLMULTITEXCOORD3SPROC) load(userptr, "glMultiTexCoord3s"); glad_glMultiTexCoord3sv = (PFNGLMULTITEXCOORD3SVPROC) load(userptr, "glMultiTexCoord3sv"); glad_glMultiTexCoord4d = (PFNGLMULTITEXCOORD4DPROC) load(userptr, "glMultiTexCoord4d"); glad_glMultiTexCoord4dv = (PFNGLMULTITEXCOORD4DVPROC) load(userptr, "glMultiTexCoord4dv"); glad_glMultiTexCoord4f = (PFNGLMULTITEXCOORD4FPROC) load(userptr, "glMultiTexCoord4f"); glad_glMultiTexCoord4fv = (PFNGLMULTITEXCOORD4FVPROC) load(userptr, "glMultiTexCoord4fv"); glad_glMultiTexCoord4i = (PFNGLMULTITEXCOORD4IPROC) load(userptr, "glMultiTexCoord4i"); glad_glMultiTexCoord4iv = (PFNGLMULTITEXCOORD4IVPROC) load(userptr, "glMultiTexCoord4iv"); glad_glMultiTexCoord4s = (PFNGLMULTITEXCOORD4SPROC) load(userptr, "glMultiTexCoord4s"); glad_glMultiTexCoord4sv = (PFNGLMULTITEXCOORD4SVPROC) load(userptr, "glMultiTexCoord4sv"); glad_glSampleCoverage = (PFNGLSAMPLECOVERAGEPROC) load(userptr, "glSampleCoverage"); } static void glad_gl_load_GL_VERSION_1_4( GLADuserptrloadfunc load, void* userptr) { if(!GLAD_GL_VERSION_1_4) return; glad_glBlendColor = (PFNGLBLENDCOLORPROC) load(userptr, "glBlendColor"); glad_glBlendEquation = (PFNGLBLENDEQUATIONPROC) load(userptr, "glBlendEquation"); glad_glBlendFuncSeparate = (PFNGLBLENDFUNCSEPARATEPROC) load(userptr, "glBlendFuncSeparate"); glad_glFogCoordPointer = (PFNGLFOGCOORDPOINTERPROC) load(userptr, "glFogCoordPointer"); glad_glFogCoordd = (PFNGLFOGCOORDDPROC) load(userptr, "glFogCoordd"); glad_glFogCoorddv = (PFNGLFOGCOORDDVPROC) load(userptr, "glFogCoorddv"); glad_glFogCoordf = (PFNGLFOGCOORDFPROC) load(userptr, "glFogCoordf"); glad_glFogCoordfv = (PFNGLFOGCOORDFVPROC) load(userptr, "glFogCoordfv"); glad_glMultiDrawArrays = (PFNGLMULTIDRAWARRAYSPROC) load(userptr, "glMultiDrawArrays"); glad_glMultiDrawElements = (PFNGLMULTIDRAWELEMENTSPROC) load(userptr, "glMultiDrawElements"); glad_glPointParameterf = (PFNGLPOINTPARAMETERFPROC) load(userptr, "glPointParameterf"); glad_glPointParameterfv = (PFNGLPOINTPARAMETERFVPROC) load(userptr, "glPointParameterfv"); glad_glPointParameteri = (PFNGLPOINTPARAMETERIPROC) load(userptr, "glPointParameteri"); glad_glPointParameteriv = (PFNGLPOINTPARAMETERIVPROC) load(userptr, "glPointParameteriv"); glad_glSecondaryColor3b = (PFNGLSECONDARYCOLOR3BPROC) load(userptr, "glSecondaryColor3b"); glad_glSecondaryColor3bv = (PFNGLSECONDARYCOLOR3BVPROC) load(userptr, "glSecondaryColor3bv"); glad_glSecondaryColor3d = (PFNGLSECONDARYCOLOR3DPROC) load(userptr, "glSecondaryColor3d"); glad_glSecondaryColor3dv = (PFNGLSECONDARYCOLOR3DVPROC) load(userptr, "glSecondaryColor3dv"); glad_glSecondaryColor3f = (PFNGLSECONDARYCOLOR3FPROC) load(userptr, "glSecondaryColor3f"); glad_glSecondaryColor3fv = (PFNGLSECONDARYCOLOR3FVPROC) load(userptr, "glSecondaryColor3fv"); glad_glSecondaryColor3i = (PFNGLSECONDARYCOLOR3IPROC) load(userptr, "glSecondaryColor3i"); glad_glSecondaryColor3iv = (PFNGLSECONDARYCOLOR3IVPROC) load(userptr, "glSecondaryColor3iv"); glad_glSecondaryColor3s = (PFNGLSECONDARYCOLOR3SPROC) load(userptr, "glSecondaryColor3s"); glad_glSecondaryColor3sv = (PFNGLSECONDARYCOLOR3SVPROC) load(userptr, "glSecondaryColor3sv"); glad_glSecondaryColor3ub = (PFNGLSECONDARYCOLOR3UBPROC) load(userptr, "glSecondaryColor3ub"); glad_glSecondaryColor3ubv = (PFNGLSECONDARYCOLOR3UBVPROC) load(userptr, "glSecondaryColor3ubv"); glad_glSecondaryColor3ui = (PFNGLSECONDARYCOLOR3UIPROC) load(userptr, "glSecondaryColor3ui"); glad_glSecondaryColor3uiv = (PFNGLSECONDARYCOLOR3UIVPROC) load(userptr, "glSecondaryColor3uiv"); glad_glSecondaryColor3us = (PFNGLSECONDARYCOLOR3USPROC) load(userptr, "glSecondaryColor3us"); glad_glSecondaryColor3usv = (PFNGLSECONDARYCOLOR3USVPROC) load(userptr, "glSecondaryColor3usv"); glad_glSecondaryColorPointer = (PFNGLSECONDARYCOLORPOINTERPROC) load(userptr, "glSecondaryColorPointer"); glad_glWindowPos2d = (PFNGLWINDOWPOS2DPROC) load(userptr, "glWindowPos2d"); glad_glWindowPos2dv = (PFNGLWINDOWPOS2DVPROC) load(userptr, "glWindowPos2dv"); glad_glWindowPos2f = (PFNGLWINDOWPOS2FPROC) load(userptr, "glWindowPos2f"); glad_glWindowPos2fv = (PFNGLWINDOWPOS2FVPROC) load(userptr, "glWindowPos2fv"); glad_glWindowPos2i = (PFNGLWINDOWPOS2IPROC) load(userptr, "glWindowPos2i"); glad_glWindowPos2iv = (PFNGLWINDOWPOS2IVPROC) load(userptr, "glWindowPos2iv"); glad_glWindowPos2s = (PFNGLWINDOWPOS2SPROC) load(userptr, "glWindowPos2s"); glad_glWindowPos2sv = (PFNGLWINDOWPOS2SVPROC) load(userptr, "glWindowPos2sv"); glad_glWindowPos3d = (PFNGLWINDOWPOS3DPROC) load(userptr, "glWindowPos3d"); glad_glWindowPos3dv = (PFNGLWINDOWPOS3DVPROC) load(userptr, "glWindowPos3dv"); glad_glWindowPos3f = (PFNGLWINDOWPOS3FPROC) load(userptr, "glWindowPos3f"); glad_glWindowPos3fv = (PFNGLWINDOWPOS3FVPROC) load(userptr, "glWindowPos3fv"); glad_glWindowPos3i = (PFNGLWINDOWPOS3IPROC) load(userptr, "glWindowPos3i"); glad_glWindowPos3iv = (PFNGLWINDOWPOS3IVPROC) load(userptr, "glWindowPos3iv"); glad_glWindowPos3s = (PFNGLWINDOWPOS3SPROC) load(userptr, "glWindowPos3s"); glad_glWindowPos3sv = (PFNGLWINDOWPOS3SVPROC) load(userptr, "glWindowPos3sv"); } static void glad_gl_load_GL_VERSION_1_5( GLADuserptrloadfunc load, void* userptr) { if(!GLAD_GL_VERSION_1_5) return; glad_glBeginQuery = (PFNGLBEGINQUERYPROC) load(userptr, "glBeginQuery"); glad_glBindBuffer = (PFNGLBINDBUFFERPROC) load(userptr, "glBindBuffer"); glad_glBufferData = (PFNGLBUFFERDATAPROC) load(userptr, "glBufferData"); glad_glBufferSubData = (PFNGLBUFFERSUBDATAPROC) load(userptr, "glBufferSubData"); glad_glDeleteBuffers = (PFNGLDELETEBUFFERSPROC) load(userptr, "glDeleteBuffers"); glad_glDeleteQueries = (PFNGLDELETEQUERIESPROC) load(userptr, "glDeleteQueries"); glad_glEndQuery = (PFNGLENDQUERYPROC) load(userptr, "glEndQuery"); glad_glGenBuffers = (PFNGLGENBUFFERSPROC) load(userptr, "glGenBuffers"); glad_glGenQueries = (PFNGLGENQUERIESPROC) load(userptr, "glGenQueries"); glad_glGetBufferParameteriv = (PFNGLGETBUFFERPARAMETERIVPROC) load(userptr, "glGetBufferParameteriv"); glad_glGetBufferPointerv = (PFNGLGETBUFFERPOINTERVPROC) load(userptr, "glGetBufferPointerv"); glad_glGetBufferSubData = (PFNGLGETBUFFERSUBDATAPROC) load(userptr, "glGetBufferSubData"); glad_glGetQueryObjectiv = (PFNGLGETQUERYOBJECTIVPROC) load(userptr, "glGetQueryObjectiv"); glad_glGetQueryObjectuiv = (PFNGLGETQUERYOBJECTUIVPROC) load(userptr, "glGetQueryObjectuiv"); glad_glGetQueryiv = (PFNGLGETQUERYIVPROC) load(userptr, "glGetQueryiv"); glad_glIsBuffer = (PFNGLISBUFFERPROC) load(userptr, "glIsBuffer"); glad_glIsQuery = (PFNGLISQUERYPROC) load(userptr, "glIsQuery"); glad_glMapBuffer = (PFNGLMAPBUFFERPROC) load(userptr, "glMapBuffer"); glad_glUnmapBuffer = (PFNGLUNMAPBUFFERPROC) load(userptr, "glUnmapBuffer"); } static void glad_gl_load_GL_VERSION_2_0( GLADuserptrloadfunc load, void* userptr) { if(!GLAD_GL_VERSION_2_0) return; glad_glAttachShader = (PFNGLATTACHSHADERPROC) load(userptr, "glAttachShader"); glad_glBindAttribLocation = (PFNGLBINDATTRIBLOCATIONPROC) load(userptr, "glBindAttribLocation"); glad_glBlendEquationSeparate = (PFNGLBLENDEQUATIONSEPARATEPROC) load(userptr, "glBlendEquationSeparate"); glad_glCompileShader = (PFNGLCOMPILESHADERPROC) load(userptr, "glCompileShader"); glad_glCreateProgram = (PFNGLCREATEPROGRAMPROC) load(userptr, "glCreateProgram"); glad_glCreateShader = (PFNGLCREATESHADERPROC) load(userptr, "glCreateShader"); glad_glDeleteProgram = (PFNGLDELETEPROGRAMPROC) load(userptr, "glDeleteProgram"); glad_glDeleteShader = (PFNGLDELETESHADERPROC) load(userptr, "glDeleteShader"); glad_glDetachShader = (PFNGLDETACHSHADERPROC) load(userptr, "glDetachShader"); glad_glDisableVertexAttribArray = (PFNGLDISABLEVERTEXATTRIBARRAYPROC) load(userptr, "glDisableVertexAttribArray"); glad_glDrawBuffers = (PFNGLDRAWBUFFERSPROC) load(userptr, "glDrawBuffers"); glad_glEnableVertexAttribArray = (PFNGLENABLEVERTEXATTRIBARRAYPROC) load(userptr, "glEnableVertexAttribArray"); glad_glGetActiveAttrib = (PFNGLGETACTIVEATTRIBPROC) load(userptr, "glGetActiveAttrib"); glad_glGetActiveUniform = (PFNGLGETACTIVEUNIFORMPROC) load(userptr, "glGetActiveUniform"); glad_glGetAttachedShaders = (PFNGLGETATTACHEDSHADERSPROC) load(userptr, "glGetAttachedShaders"); glad_glGetAttribLocation = (PFNGLGETATTRIBLOCATIONPROC) load(userptr, "glGetAttribLocation"); glad_glGetProgramInfoLog = (PFNGLGETPROGRAMINFOLOGPROC) load(userptr, "glGetProgramInfoLog"); glad_glGetProgramiv = (PFNGLGETPROGRAMIVPROC) load(userptr, "glGetProgramiv"); glad_glGetShaderInfoLog = (PFNGLGETSHADERINFOLOGPROC) load(userptr, "glGetShaderInfoLog"); glad_glGetShaderSource = (PFNGLGETSHADERSOURCEPROC) load(userptr, "glGetShaderSource"); glad_glGetShaderiv = (PFNGLGETSHADERIVPROC) load(userptr, "glGetShaderiv"); glad_glGetUniformLocation = (PFNGLGETUNIFORMLOCATIONPROC) load(userptr, "glGetUniformLocation"); glad_glGetUniformfv = (PFNGLGETUNIFORMFVPROC) load(userptr, "glGetUniformfv"); glad_glGetUniformiv = (PFNGLGETUNIFORMIVPROC) load(userptr, "glGetUniformiv"); glad_glGetVertexAttribPointerv = (PFNGLGETVERTEXATTRIBPOINTERVPROC) load(userptr, "glGetVertexAttribPointerv"); glad_glGetVertexAttribdv = (PFNGLGETVERTEXATTRIBDVPROC) load(userptr, "glGetVertexAttribdv"); glad_glGetVertexAttribfv = (PFNGLGETVERTEXATTRIBFVPROC) load(userptr, "glGetVertexAttribfv"); glad_glGetVertexAttribiv = (PFNGLGETVERTEXATTRIBIVPROC) load(userptr, "glGetVertexAttribiv"); glad_glIsProgram = (PFNGLISPROGRAMPROC) load(userptr, "glIsProgram"); glad_glIsShader = (PFNGLISSHADERPROC) load(userptr, "glIsShader"); glad_glLinkProgram = (PFNGLLINKPROGRAMPROC) load(userptr, "glLinkProgram"); glad_glShaderSource = (PFNGLSHADERSOURCEPROC) load(userptr, "glShaderSource"); glad_glStencilFuncSeparate = (PFNGLSTENCILFUNCSEPARATEPROC) load(userptr, "glStencilFuncSeparate"); glad_glStencilMaskSeparate = (PFNGLSTENCILMASKSEPARATEPROC) load(userptr, "glStencilMaskSeparate"); glad_glStencilOpSeparate = (PFNGLSTENCILOPSEPARATEPROC) load(userptr, "glStencilOpSeparate"); glad_glUniform1f = (PFNGLUNIFORM1FPROC) load(userptr, "glUniform1f"); glad_glUniform1fv = (PFNGLUNIFORM1FVPROC) load(userptr, "glUniform1fv"); glad_glUniform1i = (PFNGLUNIFORM1IPROC) load(userptr, "glUniform1i"); glad_glUniform1iv = (PFNGLUNIFORM1IVPROC) load(userptr, "glUniform1iv"); glad_glUniform2f = (PFNGLUNIFORM2FPROC) load(userptr, "glUniform2f"); glad_glUniform2fv = (PFNGLUNIFORM2FVPROC) load(userptr, "glUniform2fv"); glad_glUniform2i = (PFNGLUNIFORM2IPROC) load(userptr, "glUniform2i"); glad_glUniform2iv = (PFNGLUNIFORM2IVPROC) load(userptr, "glUniform2iv"); glad_glUniform3f = (PFNGLUNIFORM3FPROC) load(userptr, "glUniform3f"); glad_glUniform3fv = (PFNGLUNIFORM3FVPROC) load(userptr, "glUniform3fv"); glad_glUniform3i = (PFNGLUNIFORM3IPROC) load(userptr, "glUniform3i"); glad_glUniform3iv = (PFNGLUNIFORM3IVPROC) load(userptr, "glUniform3iv"); glad_glUniform4f = (PFNGLUNIFORM4FPROC) load(userptr, "glUniform4f"); glad_glUniform4fv = (PFNGLUNIFORM4FVPROC) load(userptr, "glUniform4fv"); glad_glUniform4i = (PFNGLUNIFORM4IPROC) load(userptr, "glUniform4i"); glad_glUniform4iv = (PFNGLUNIFORM4IVPROC) load(userptr, "glUniform4iv"); glad_glUniformMatrix2fv = (PFNGLUNIFORMMATRIX2FVPROC) load(userptr, "glUniformMatrix2fv"); glad_glUniformMatrix3fv = (PFNGLUNIFORMMATRIX3FVPROC) load(userptr, "glUniformMatrix3fv"); glad_glUniformMatrix4fv = (PFNGLUNIFORMMATRIX4FVPROC) load(userptr, "glUniformMatrix4fv"); glad_glUseProgram = (PFNGLUSEPROGRAMPROC) load(userptr, "glUseProgram"); glad_glValidateProgram = (PFNGLVALIDATEPROGRAMPROC) load(userptr, "glValidateProgram"); glad_glVertexAttrib1d = (PFNGLVERTEXATTRIB1DPROC) load(userptr, "glVertexAttrib1d"); glad_glVertexAttrib1dv = (PFNGLVERTEXATTRIB1DVPROC) load(userptr, "glVertexAttrib1dv"); glad_glVertexAttrib1f = (PFNGLVERTEXATTRIB1FPROC) load(userptr, "glVertexAttrib1f"); glad_glVertexAttrib1fv = (PFNGLVERTEXATTRIB1FVPROC) load(userptr, "glVertexAttrib1fv"); glad_glVertexAttrib1s = (PFNGLVERTEXATTRIB1SPROC) load(userptr, "glVertexAttrib1s"); glad_glVertexAttrib1sv = (PFNGLVERTEXATTRIB1SVPROC) load(userptr, "glVertexAttrib1sv"); glad_glVertexAttrib2d = (PFNGLVERTEXATTRIB2DPROC) load(userptr, "glVertexAttrib2d"); glad_glVertexAttrib2dv = (PFNGLVERTEXATTRIB2DVPROC) load(userptr, "glVertexAttrib2dv"); glad_glVertexAttrib2f = (PFNGLVERTEXATTRIB2FPROC) load(userptr, "glVertexAttrib2f"); glad_glVertexAttrib2fv = (PFNGLVERTEXATTRIB2FVPROC) load(userptr, "glVertexAttrib2fv"); glad_glVertexAttrib2s = (PFNGLVERTEXATTRIB2SPROC) load(userptr, "glVertexAttrib2s"); glad_glVertexAttrib2sv = (PFNGLVERTEXATTRIB2SVPROC) load(userptr, "glVertexAttrib2sv"); glad_glVertexAttrib3d = (PFNGLVERTEXATTRIB3DPROC) load(userptr, "glVertexAttrib3d"); glad_glVertexAttrib3dv = (PFNGLVERTEXATTRIB3DVPROC) load(userptr, "glVertexAttrib3dv"); glad_glVertexAttrib3f = (PFNGLVERTEXATTRIB3FPROC) load(userptr, "glVertexAttrib3f"); glad_glVertexAttrib3fv = (PFNGLVERTEXATTRIB3FVPROC) load(userptr, "glVertexAttrib3fv"); glad_glVertexAttrib3s = (PFNGLVERTEXATTRIB3SPROC) load(userptr, "glVertexAttrib3s"); glad_glVertexAttrib3sv = (PFNGLVERTEXATTRIB3SVPROC) load(userptr, "glVertexAttrib3sv"); glad_glVertexAttrib4Nbv = (PFNGLVERTEXATTRIB4NBVPROC) load(userptr, "glVertexAttrib4Nbv"); glad_glVertexAttrib4Niv = (PFNGLVERTEXATTRIB4NIVPROC) load(userptr, "glVertexAttrib4Niv"); glad_glVertexAttrib4Nsv = (PFNGLVERTEXATTRIB4NSVPROC) load(userptr, "glVertexAttrib4Nsv"); glad_glVertexAttrib4Nub = (PFNGLVERTEXATTRIB4NUBPROC) load(userptr, "glVertexAttrib4Nub"); glad_glVertexAttrib4Nubv = (PFNGLVERTEXATTRIB4NUBVPROC) load(userptr, "glVertexAttrib4Nubv"); glad_glVertexAttrib4Nuiv = (PFNGLVERTEXATTRIB4NUIVPROC) load(userptr, "glVertexAttrib4Nuiv"); glad_glVertexAttrib4Nusv = (PFNGLVERTEXATTRIB4NUSVPROC) load(userptr, "glVertexAttrib4Nusv"); glad_glVertexAttrib4bv = (PFNGLVERTEXATTRIB4BVPROC) load(userptr, "glVertexAttrib4bv"); glad_glVertexAttrib4d = (PFNGLVERTEXATTRIB4DPROC) load(userptr, "glVertexAttrib4d"); glad_glVertexAttrib4dv = (PFNGLVERTEXATTRIB4DVPROC) load(userptr, "glVertexAttrib4dv"); glad_glVertexAttrib4f = (PFNGLVERTEXATTRIB4FPROC) load(userptr, "glVertexAttrib4f"); glad_glVertexAttrib4fv = (PFNGLVERTEXATTRIB4FVPROC) load(userptr, "glVertexAttrib4fv"); glad_glVertexAttrib4iv = (PFNGLVERTEXATTRIB4IVPROC) load(userptr, "glVertexAttrib4iv"); glad_glVertexAttrib4s = (PFNGLVERTEXATTRIB4SPROC) load(userptr, "glVertexAttrib4s"); glad_glVertexAttrib4sv = (PFNGLVERTEXATTRIB4SVPROC) load(userptr, "glVertexAttrib4sv"); glad_glVertexAttrib4ubv = (PFNGLVERTEXATTRIB4UBVPROC) load(userptr, "glVertexAttrib4ubv"); glad_glVertexAttrib4uiv = (PFNGLVERTEXATTRIB4UIVPROC) load(userptr, "glVertexAttrib4uiv"); glad_glVertexAttrib4usv = (PFNGLVERTEXATTRIB4USVPROC) load(userptr, "glVertexAttrib4usv"); glad_glVertexAttribPointer = (PFNGLVERTEXATTRIBPOINTERPROC) load(userptr, "glVertexAttribPointer"); } static void glad_gl_load_GL_VERSION_2_1( GLADuserptrloadfunc load, void* userptr) { if(!GLAD_GL_VERSION_2_1) return; glad_glUniformMatrix2x3fv = (PFNGLUNIFORMMATRIX2X3FVPROC) load(userptr, "glUniformMatrix2x3fv"); glad_glUniformMatrix2x4fv = (PFNGLUNIFORMMATRIX2X4FVPROC) load(userptr, "glUniformMatrix2x4fv"); glad_glUniformMatrix3x2fv = (PFNGLUNIFORMMATRIX3X2FVPROC) load(userptr, "glUniformMatrix3x2fv"); glad_glUniformMatrix3x4fv = (PFNGLUNIFORMMATRIX3X4FVPROC) load(userptr, "glUniformMatrix3x4fv"); glad_glUniformMatrix4x2fv = (PFNGLUNIFORMMATRIX4X2FVPROC) load(userptr, "glUniformMatrix4x2fv"); glad_glUniformMatrix4x3fv = (PFNGLUNIFORMMATRIX4X3FVPROC) load(userptr, "glUniformMatrix4x3fv"); } static void glad_gl_load_GL_VERSION_3_0( GLADuserptrloadfunc load, void* userptr) { if(!GLAD_GL_VERSION_3_0) return; glad_glBeginConditionalRender = (PFNGLBEGINCONDITIONALRENDERPROC) load(userptr, "glBeginConditionalRender"); glad_glBeginTransformFeedback = (PFNGLBEGINTRANSFORMFEEDBACKPROC) load(userptr, "glBeginTransformFeedback"); glad_glBindBufferBase = (PFNGLBINDBUFFERBASEPROC) load(userptr, "glBindBufferBase"); glad_glBindBufferRange = (PFNGLBINDBUFFERRANGEPROC) load(userptr, "glBindBufferRange"); glad_glBindFragDataLocation = (PFNGLBINDFRAGDATALOCATIONPROC) load(userptr, "glBindFragDataLocation"); glad_glBindFramebuffer = (PFNGLBINDFRAMEBUFFERPROC) load(userptr, "glBindFramebuffer"); glad_glBindRenderbuffer = (PFNGLBINDRENDERBUFFERPROC) load(userptr, "glBindRenderbuffer"); glad_glBindVertexArray = (PFNGLBINDVERTEXARRAYPROC) load(userptr, "glBindVertexArray"); glad_glBlitFramebuffer = (PFNGLBLITFRAMEBUFFERPROC) load(userptr, "glBlitFramebuffer"); glad_glCheckFramebufferStatus = (PFNGLCHECKFRAMEBUFFERSTATUSPROC) load(userptr, "glCheckFramebufferStatus"); glad_glClampColor = (PFNGLCLAMPCOLORPROC) load(userptr, "glClampColor"); glad_glClearBufferfi = (PFNGLCLEARBUFFERFIPROC) load(userptr, "glClearBufferfi"); glad_glClearBufferfv = (PFNGLCLEARBUFFERFVPROC) load(userptr, "glClearBufferfv"); glad_glClearBufferiv = (PFNGLCLEARBUFFERIVPROC) load(userptr, "glClearBufferiv"); glad_glClearBufferuiv = (PFNGLCLEARBUFFERUIVPROC) load(userptr, "glClearBufferuiv"); glad_glColorMaski = (PFNGLCOLORMASKIPROC) load(userptr, "glColorMaski"); glad_glDeleteFramebuffers = (PFNGLDELETEFRAMEBUFFERSPROC) load(userptr, "glDeleteFramebuffers"); glad_glDeleteRenderbuffers = (PFNGLDELETERENDERBUFFERSPROC) load(userptr, "glDeleteRenderbuffers"); glad_glDeleteVertexArrays = (PFNGLDELETEVERTEXARRAYSPROC) load(userptr, "glDeleteVertexArrays"); glad_glDisablei = (PFNGLDISABLEIPROC) load(userptr, "glDisablei"); glad_glEnablei = (PFNGLENABLEIPROC) load(userptr, "glEnablei"); glad_glEndConditionalRender = (PFNGLENDCONDITIONALRENDERPROC) load(userptr, "glEndConditionalRender"); glad_glEndTransformFeedback = (PFNGLENDTRANSFORMFEEDBACKPROC) load(userptr, "glEndTransformFeedback"); glad_glFlushMappedBufferRange = (PFNGLFLUSHMAPPEDBUFFERRANGEPROC) load(userptr, "glFlushMappedBufferRange"); glad_glFramebufferRenderbuffer = (PFNGLFRAMEBUFFERRENDERBUFFERPROC) load(userptr, "glFramebufferRenderbuffer"); glad_glFramebufferTexture1D = (PFNGLFRAMEBUFFERTEXTURE1DPROC) load(userptr, "glFramebufferTexture1D"); glad_glFramebufferTexture2D = (PFNGLFRAMEBUFFERTEXTURE2DPROC) load(userptr, "glFramebufferTexture2D"); glad_glFramebufferTexture3D = (PFNGLFRAMEBUFFERTEXTURE3DPROC) load(userptr, "glFramebufferTexture3D"); glad_glFramebufferTextureLayer = (PFNGLFRAMEBUFFERTEXTURELAYERPROC) load(userptr, "glFramebufferTextureLayer"); glad_glGenFramebuffers = (PFNGLGENFRAMEBUFFERSPROC) load(userptr, "glGenFramebuffers"); glad_glGenRenderbuffers = (PFNGLGENRENDERBUFFERSPROC) load(userptr, "glGenRenderbuffers"); glad_glGenVertexArrays = (PFNGLGENVERTEXARRAYSPROC) load(userptr, "glGenVertexArrays"); glad_glGenerateMipmap = (PFNGLGENERATEMIPMAPPROC) load(userptr, "glGenerateMipmap"); glad_glGetBooleani_v = (PFNGLGETBOOLEANI_VPROC) load(userptr, "glGetBooleani_v"); glad_glGetFragDataLocation = (PFNGLGETFRAGDATALOCATIONPROC) load(userptr, "glGetFragDataLocation"); glad_glGetFramebufferAttachmentParameteriv = (PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVPROC) load(userptr, "glGetFramebufferAttachmentParameteriv"); glad_glGetIntegeri_v = (PFNGLGETINTEGERI_VPROC) load(userptr, "glGetIntegeri_v"); glad_glGetRenderbufferParameteriv = (PFNGLGETRENDERBUFFERPARAMETERIVPROC) load(userptr, "glGetRenderbufferParameteriv"); glad_glGetStringi = (PFNGLGETSTRINGIPROC) load(userptr, "glGetStringi"); glad_glGetTexParameterIiv = (PFNGLGETTEXPARAMETERIIVPROC) load(userptr, "glGetTexParameterIiv"); glad_glGetTexParameterIuiv = (PFNGLGETTEXPARAMETERIUIVPROC) load(userptr, "glGetTexParameterIuiv"); glad_glGetTransformFeedbackVarying = (PFNGLGETTRANSFORMFEEDBACKVARYINGPROC) load(userptr, "glGetTransformFeedbackVarying"); glad_glGetUniformuiv = (PFNGLGETUNIFORMUIVPROC) load(userptr, "glGetUniformuiv"); glad_glGetVertexAttribIiv = (PFNGLGETVERTEXATTRIBIIVPROC) load(userptr, "glGetVertexAttribIiv"); glad_glGetVertexAttribIuiv = (PFNGLGETVERTEXATTRIBIUIVPROC) load(userptr, "glGetVertexAttribIuiv"); glad_glIsEnabledi = (PFNGLISENABLEDIPROC) load(userptr, "glIsEnabledi"); glad_glIsFramebuffer = (PFNGLISFRAMEBUFFERPROC) load(userptr, "glIsFramebuffer"); glad_glIsRenderbuffer = (PFNGLISRENDERBUFFERPROC) load(userptr, "glIsRenderbuffer"); glad_glIsVertexArray = (PFNGLISVERTEXARRAYPROC) load(userptr, "glIsVertexArray"); glad_glMapBufferRange = (PFNGLMAPBUFFERRANGEPROC) load(userptr, "glMapBufferRange"); glad_glRenderbufferStorage = (PFNGLRENDERBUFFERSTORAGEPROC) load(userptr, "glRenderbufferStorage"); glad_glRenderbufferStorageMultisample = (PFNGLRENDERBUFFERSTORAGEMULTISAMPLEPROC) load(userptr, "glRenderbufferStorageMultisample"); glad_glTexParameterIiv = (PFNGLTEXPARAMETERIIVPROC) load(userptr, "glTexParameterIiv"); glad_glTexParameterIuiv = (PFNGLTEXPARAMETERIUIVPROC) load(userptr, "glTexParameterIuiv"); glad_glTransformFeedbackVaryings = (PFNGLTRANSFORMFEEDBACKVARYINGSPROC) load(userptr, "glTransformFeedbackVaryings"); glad_glUniform1ui = (PFNGLUNIFORM1UIPROC) load(userptr, "glUniform1ui"); glad_glUniform1uiv = (PFNGLUNIFORM1UIVPROC) load(userptr, "glUniform1uiv"); glad_glUniform2ui = (PFNGLUNIFORM2UIPROC) load(userptr, "glUniform2ui"); glad_glUniform2uiv = (PFNGLUNIFORM2UIVPROC) load(userptr, "glUniform2uiv"); glad_glUniform3ui = (PFNGLUNIFORM3UIPROC) load(userptr, "glUniform3ui"); glad_glUniform3uiv = (PFNGLUNIFORM3UIVPROC) load(userptr, "glUniform3uiv"); glad_glUniform4ui = (PFNGLUNIFORM4UIPROC) load(userptr, "glUniform4ui"); glad_glUniform4uiv = (PFNGLUNIFORM4UIVPROC) load(userptr, "glUniform4uiv"); glad_glVertexAttribI1i = (PFNGLVERTEXATTRIBI1IPROC) load(userptr, "glVertexAttribI1i"); glad_glVertexAttribI1iv = (PFNGLVERTEXATTRIBI1IVPROC) load(userptr, "glVertexAttribI1iv"); glad_glVertexAttribI1ui = (PFNGLVERTEXATTRIBI1UIPROC) load(userptr, "glVertexAttribI1ui"); glad_glVertexAttribI1uiv = (PFNGLVERTEXATTRIBI1UIVPROC) load(userptr, "glVertexAttribI1uiv"); glad_glVertexAttribI2i = (PFNGLVERTEXATTRIBI2IPROC) load(userptr, "glVertexAttribI2i"); glad_glVertexAttribI2iv = (PFNGLVERTEXATTRIBI2IVPROC) load(userptr, "glVertexAttribI2iv"); glad_glVertexAttribI2ui = (PFNGLVERTEXATTRIBI2UIPROC) load(userptr, "glVertexAttribI2ui"); glad_glVertexAttribI2uiv = (PFNGLVERTEXATTRIBI2UIVPROC) load(userptr, "glVertexAttribI2uiv"); glad_glVertexAttribI3i = (PFNGLVERTEXATTRIBI3IPROC) load(userptr, "glVertexAttribI3i"); glad_glVertexAttribI3iv = (PFNGLVERTEXATTRIBI3IVPROC) load(userptr, "glVertexAttribI3iv"); glad_glVertexAttribI3ui = (PFNGLVERTEXATTRIBI3UIPROC) load(userptr, "glVertexAttribI3ui"); glad_glVertexAttribI3uiv = (PFNGLVERTEXATTRIBI3UIVPROC) load(userptr, "glVertexAttribI3uiv"); glad_glVertexAttribI4bv = (PFNGLVERTEXATTRIBI4BVPROC) load(userptr, "glVertexAttribI4bv"); glad_glVertexAttribI4i = (PFNGLVERTEXATTRIBI4IPROC) load(userptr, "glVertexAttribI4i"); glad_glVertexAttribI4iv = (PFNGLVERTEXATTRIBI4IVPROC) load(userptr, "glVertexAttribI4iv"); glad_glVertexAttribI4sv = (PFNGLVERTEXATTRIBI4SVPROC) load(userptr, "glVertexAttribI4sv"); glad_glVertexAttribI4ubv = (PFNGLVERTEXATTRIBI4UBVPROC) load(userptr, "glVertexAttribI4ubv"); glad_glVertexAttribI4ui = (PFNGLVERTEXATTRIBI4UIPROC) load(userptr, "glVertexAttribI4ui"); glad_glVertexAttribI4uiv = (PFNGLVERTEXATTRIBI4UIVPROC) load(userptr, "glVertexAttribI4uiv"); glad_glVertexAttribI4usv = (PFNGLVERTEXATTRIBI4USVPROC) load(userptr, "glVertexAttribI4usv"); glad_glVertexAttribIPointer = (PFNGLVERTEXATTRIBIPOINTERPROC) load(userptr, "glVertexAttribIPointer"); } static void glad_gl_load_GL_VERSION_3_1( GLADuserptrloadfunc load, void* userptr) { if(!GLAD_GL_VERSION_3_1) return; glad_glBindBufferBase = (PFNGLBINDBUFFERBASEPROC) load(userptr, "glBindBufferBase"); glad_glBindBufferRange = (PFNGLBINDBUFFERRANGEPROC) load(userptr, "glBindBufferRange"); glad_glCopyBufferSubData = (PFNGLCOPYBUFFERSUBDATAPROC) load(userptr, "glCopyBufferSubData"); glad_glDrawArraysInstanced = (PFNGLDRAWARRAYSINSTANCEDPROC) load(userptr, "glDrawArraysInstanced"); glad_glDrawElementsInstanced = (PFNGLDRAWELEMENTSINSTANCEDPROC) load(userptr, "glDrawElementsInstanced"); glad_glGetActiveUniformBlockName = (PFNGLGETACTIVEUNIFORMBLOCKNAMEPROC) load(userptr, "glGetActiveUniformBlockName"); glad_glGetActiveUniformBlockiv = (PFNGLGETACTIVEUNIFORMBLOCKIVPROC) load(userptr, "glGetActiveUniformBlockiv"); glad_glGetActiveUniformName = (PFNGLGETACTIVEUNIFORMNAMEPROC) load(userptr, "glGetActiveUniformName"); glad_glGetActiveUniformsiv = (PFNGLGETACTIVEUNIFORMSIVPROC) load(userptr, "glGetActiveUniformsiv"); glad_glGetIntegeri_v = (PFNGLGETINTEGERI_VPROC) load(userptr, "glGetIntegeri_v"); glad_glGetUniformBlockIndex = (PFNGLGETUNIFORMBLOCKINDEXPROC) load(userptr, "glGetUniformBlockIndex"); glad_glGetUniformIndices = (PFNGLGETUNIFORMINDICESPROC) load(userptr, "glGetUniformIndices"); glad_glPrimitiveRestartIndex = (PFNGLPRIMITIVERESTARTINDEXPROC) load(userptr, "glPrimitiveRestartIndex"); glad_glTexBuffer = (PFNGLTEXBUFFERPROC) load(userptr, "glTexBuffer"); glad_glUniformBlockBinding = (PFNGLUNIFORMBLOCKBINDINGPROC) load(userptr, "glUniformBlockBinding"); } static void glad_gl_load_GL_VERSION_3_2( GLADuserptrloadfunc load, void* userptr) { if(!GLAD_GL_VERSION_3_2) return; glad_glClientWaitSync = (PFNGLCLIENTWAITSYNCPROC) load(userptr, "glClientWaitSync"); glad_glDeleteSync = (PFNGLDELETESYNCPROC) load(userptr, "glDeleteSync"); glad_glDrawElementsBaseVertex = (PFNGLDRAWELEMENTSBASEVERTEXPROC) load(userptr, "glDrawElementsBaseVertex"); glad_glDrawElementsInstancedBaseVertex = (PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXPROC) load(userptr, "glDrawElementsInstancedBaseVertex"); glad_glDrawRangeElementsBaseVertex = (PFNGLDRAWRANGEELEMENTSBASEVERTEXPROC) load(userptr, "glDrawRangeElementsBaseVertex"); glad_glFenceSync = (PFNGLFENCESYNCPROC) load(userptr, "glFenceSync"); glad_glFramebufferTexture = (PFNGLFRAMEBUFFERTEXTUREPROC) load(userptr, "glFramebufferTexture"); glad_glGetBufferParameteri64v = (PFNGLGETBUFFERPARAMETERI64VPROC) load(userptr, "glGetBufferParameteri64v"); glad_glGetInteger64i_v = (PFNGLGETINTEGER64I_VPROC) load(userptr, "glGetInteger64i_v"); glad_glGetInteger64v = (PFNGLGETINTEGER64VPROC) load(userptr, "glGetInteger64v"); glad_glGetMultisamplefv = (PFNGLGETMULTISAMPLEFVPROC) load(userptr, "glGetMultisamplefv"); glad_glGetSynciv = (PFNGLGETSYNCIVPROC) load(userptr, "glGetSynciv"); glad_glIsSync = (PFNGLISSYNCPROC) load(userptr, "glIsSync"); glad_glMultiDrawElementsBaseVertex = (PFNGLMULTIDRAWELEMENTSBASEVERTEXPROC) load(userptr, "glMultiDrawElementsBaseVertex"); glad_glProvokingVertex = (PFNGLPROVOKINGVERTEXPROC) load(userptr, "glProvokingVertex"); glad_glSampleMaski = (PFNGLSAMPLEMASKIPROC) load(userptr, "glSampleMaski"); glad_glTexImage2DMultisample = (PFNGLTEXIMAGE2DMULTISAMPLEPROC) load(userptr, "glTexImage2DMultisample"); glad_glTexImage3DMultisample = (PFNGLTEXIMAGE3DMULTISAMPLEPROC) load(userptr, "glTexImage3DMultisample"); glad_glWaitSync = (PFNGLWAITSYNCPROC) load(userptr, "glWaitSync"); } static void glad_gl_load_GL_VERSION_3_3( GLADuserptrloadfunc load, void* userptr) { if(!GLAD_GL_VERSION_3_3) return; glad_glBindFragDataLocationIndexed = (PFNGLBINDFRAGDATALOCATIONINDEXEDPROC) load(userptr, "glBindFragDataLocationIndexed"); glad_glBindSampler = (PFNGLBINDSAMPLERPROC) load(userptr, "glBindSampler"); glad_glColorP3ui = (PFNGLCOLORP3UIPROC) load(userptr, "glColorP3ui"); glad_glColorP3uiv = (PFNGLCOLORP3UIVPROC) load(userptr, "glColorP3uiv"); glad_glColorP4ui = (PFNGLCOLORP4UIPROC) load(userptr, "glColorP4ui"); glad_glColorP4uiv = (PFNGLCOLORP4UIVPROC) load(userptr, "glColorP4uiv"); glad_glDeleteSamplers = (PFNGLDELETESAMPLERSPROC) load(userptr, "glDeleteSamplers"); glad_glGenSamplers = (PFNGLGENSAMPLERSPROC) load(userptr, "glGenSamplers"); glad_glGetFragDataIndex = (PFNGLGETFRAGDATAINDEXPROC) load(userptr, "glGetFragDataIndex"); glad_glGetQueryObjecti64v = (PFNGLGETQUERYOBJECTI64VPROC) load(userptr, "glGetQueryObjecti64v"); glad_glGetQueryObjectui64v = (PFNGLGETQUERYOBJECTUI64VPROC) load(userptr, "glGetQueryObjectui64v"); glad_glGetSamplerParameterIiv = (PFNGLGETSAMPLERPARAMETERIIVPROC) load(userptr, "glGetSamplerParameterIiv"); glad_glGetSamplerParameterIuiv = (PFNGLGETSAMPLERPARAMETERIUIVPROC) load(userptr, "glGetSamplerParameterIuiv"); glad_glGetSamplerParameterfv = (PFNGLGETSAMPLERPARAMETERFVPROC) load(userptr, "glGetSamplerParameterfv"); glad_glGetSamplerParameteriv = (PFNGLGETSAMPLERPARAMETERIVPROC) load(userptr, "glGetSamplerParameteriv"); glad_glIsSampler = (PFNGLISSAMPLERPROC) load(userptr, "glIsSampler"); glad_glMultiTexCoordP1ui = (PFNGLMULTITEXCOORDP1UIPROC) load(userptr, "glMultiTexCoordP1ui"); glad_glMultiTexCoordP1uiv = (PFNGLMULTITEXCOORDP1UIVPROC) load(userptr, "glMultiTexCoordP1uiv"); glad_glMultiTexCoordP2ui = (PFNGLMULTITEXCOORDP2UIPROC) load(userptr, "glMultiTexCoordP2ui"); glad_glMultiTexCoordP2uiv = (PFNGLMULTITEXCOORDP2UIVPROC) load(userptr, "glMultiTexCoordP2uiv"); glad_glMultiTexCoordP3ui = (PFNGLMULTITEXCOORDP3UIPROC) load(userptr, "glMultiTexCoordP3ui"); glad_glMultiTexCoordP3uiv = (PFNGLMULTITEXCOORDP3UIVPROC) load(userptr, "glMultiTexCoordP3uiv"); glad_glMultiTexCoordP4ui = (PFNGLMULTITEXCOORDP4UIPROC) load(userptr, "glMultiTexCoordP4ui"); glad_glMultiTexCoordP4uiv = (PFNGLMULTITEXCOORDP4UIVPROC) load(userptr, "glMultiTexCoordP4uiv"); glad_glNormalP3ui = (PFNGLNORMALP3UIPROC) load(userptr, "glNormalP3ui"); glad_glNormalP3uiv = (PFNGLNORMALP3UIVPROC) load(userptr, "glNormalP3uiv"); glad_glQueryCounter = (PFNGLQUERYCOUNTERPROC) load(userptr, "glQueryCounter"); glad_glSamplerParameterIiv = (PFNGLSAMPLERPARAMETERIIVPROC) load(userptr, "glSamplerParameterIiv"); glad_glSamplerParameterIuiv = (PFNGLSAMPLERPARAMETERIUIVPROC) load(userptr, "glSamplerParameterIuiv"); glad_glSamplerParameterf = (PFNGLSAMPLERPARAMETERFPROC) load(userptr, "glSamplerParameterf"); glad_glSamplerParameterfv = (PFNGLSAMPLERPARAMETERFVPROC) load(userptr, "glSamplerParameterfv"); glad_glSamplerParameteri = (PFNGLSAMPLERPARAMETERIPROC) load(userptr, "glSamplerParameteri"); glad_glSamplerParameteriv = (PFNGLSAMPLERPARAMETERIVPROC) load(userptr, "glSamplerParameteriv"); glad_glSecondaryColorP3ui = (PFNGLSECONDARYCOLORP3UIPROC) load(userptr, "glSecondaryColorP3ui"); glad_glSecondaryColorP3uiv = (PFNGLSECONDARYCOLORP3UIVPROC) load(userptr, "glSecondaryColorP3uiv"); glad_glTexCoordP1ui = (PFNGLTEXCOORDP1UIPROC) load(userptr, "glTexCoordP1ui"); glad_glTexCoordP1uiv = (PFNGLTEXCOORDP1UIVPROC) load(userptr, "glTexCoordP1uiv"); glad_glTexCoordP2ui = (PFNGLTEXCOORDP2UIPROC) load(userptr, "glTexCoordP2ui"); glad_glTexCoordP2uiv = (PFNGLTEXCOORDP2UIVPROC) load(userptr, "glTexCoordP2uiv"); glad_glTexCoordP3ui = (PFNGLTEXCOORDP3UIPROC) load(userptr, "glTexCoordP3ui"); glad_glTexCoordP3uiv = (PFNGLTEXCOORDP3UIVPROC) load(userptr, "glTexCoordP3uiv"); glad_glTexCoordP4ui = (PFNGLTEXCOORDP4UIPROC) load(userptr, "glTexCoordP4ui"); glad_glTexCoordP4uiv = (PFNGLTEXCOORDP4UIVPROC) load(userptr, "glTexCoordP4uiv"); glad_glVertexAttribDivisor = (PFNGLVERTEXATTRIBDIVISORPROC) load(userptr, "glVertexAttribDivisor"); glad_glVertexAttribP1ui = (PFNGLVERTEXATTRIBP1UIPROC) load(userptr, "glVertexAttribP1ui"); glad_glVertexAttribP1uiv = (PFNGLVERTEXATTRIBP1UIVPROC) load(userptr, "glVertexAttribP1uiv"); glad_glVertexAttribP2ui = (PFNGLVERTEXATTRIBP2UIPROC) load(userptr, "glVertexAttribP2ui"); glad_glVertexAttribP2uiv = (PFNGLVERTEXATTRIBP2UIVPROC) load(userptr, "glVertexAttribP2uiv"); glad_glVertexAttribP3ui = (PFNGLVERTEXATTRIBP3UIPROC) load(userptr, "glVertexAttribP3ui"); glad_glVertexAttribP3uiv = (PFNGLVERTEXATTRIBP3UIVPROC) load(userptr, "glVertexAttribP3uiv"); glad_glVertexAttribP4ui = (PFNGLVERTEXATTRIBP4UIPROC) load(userptr, "glVertexAttribP4ui"); glad_glVertexAttribP4uiv = (PFNGLVERTEXATTRIBP4UIVPROC) load(userptr, "glVertexAttribP4uiv"); glad_glVertexP2ui = (PFNGLVERTEXP2UIPROC) load(userptr, "glVertexP2ui"); glad_glVertexP2uiv = (PFNGLVERTEXP2UIVPROC) load(userptr, "glVertexP2uiv"); glad_glVertexP3ui = (PFNGLVERTEXP3UIPROC) load(userptr, "glVertexP3ui"); glad_glVertexP3uiv = (PFNGLVERTEXP3UIVPROC) load(userptr, "glVertexP3uiv"); glad_glVertexP4ui = (PFNGLVERTEXP4UIPROC) load(userptr, "glVertexP4ui"); glad_glVertexP4uiv = (PFNGLVERTEXP4UIVPROC) load(userptr, "glVertexP4uiv"); } static void glad_gl_load_GL_ARB_ES2_compatibility( GLADuserptrloadfunc load, void* userptr) { if(!GLAD_GL_ARB_ES2_compatibility) return; glad_glClearDepthf = (PFNGLCLEARDEPTHFPROC) load(userptr, "glClearDepthf"); glad_glDepthRangef = (PFNGLDEPTHRANGEFPROC) load(userptr, "glDepthRangef"); glad_glGetShaderPrecisionFormat = (PFNGLGETSHADERPRECISIONFORMATPROC) load(userptr, "glGetShaderPrecisionFormat"); glad_glReleaseShaderCompiler = (PFNGLRELEASESHADERCOMPILERPROC) load(userptr, "glReleaseShaderCompiler"); glad_glShaderBinary = (PFNGLSHADERBINARYPROC) load(userptr, "glShaderBinary"); } static void glad_gl_load_GL_ARB_ES3_1_compatibility( GLADuserptrloadfunc load, void* userptr) { if(!GLAD_GL_ARB_ES3_1_compatibility) return; glad_glMemoryBarrierByRegion = (PFNGLMEMORYBARRIERBYREGIONPROC) load(userptr, "glMemoryBarrierByRegion"); } static void glad_gl_load_GL_ARB_ES3_2_compatibility( GLADuserptrloadfunc load, void* userptr) { if(!GLAD_GL_ARB_ES3_2_compatibility) return; glad_glPrimitiveBoundingBox = (PFNGLPRIMITIVEBOUNDINGBOXPROC) load(userptr, "glPrimitiveBoundingBox"); glad_glPrimitiveBoundingBoxARB = (PFNGLPRIMITIVEBOUNDINGBOXARBPROC) load(userptr, "glPrimitiveBoundingBoxARB"); } static void glad_gl_load_GL_ARB_base_instance( GLADuserptrloadfunc load, void* userptr) { if(!GLAD_GL_ARB_base_instance) return; glad_glDrawArraysInstancedBaseInstance = (PFNGLDRAWARRAYSINSTANCEDBASEINSTANCEPROC) load(userptr, "glDrawArraysInstancedBaseInstance"); glad_glDrawElementsInstancedBaseInstance = (PFNGLDRAWELEMENTSINSTANCEDBASEINSTANCEPROC) load(userptr, "glDrawElementsInstancedBaseInstance"); glad_glDrawElementsInstancedBaseVertexBaseInstance = (PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXBASEINSTANCEPROC) load(userptr, "glDrawElementsInstancedBaseVertexBaseInstance"); } static void glad_gl_load_GL_ARB_bindless_texture( GLADuserptrloadfunc load, void* userptr) { if(!GLAD_GL_ARB_bindless_texture) return; glad_glGetImageHandleARB = (PFNGLGETIMAGEHANDLEARBPROC) load(userptr, "glGetImageHandleARB"); glad_glGetTextureHandleARB = (PFNGLGETTEXTUREHANDLEARBPROC) load(userptr, "glGetTextureHandleARB"); glad_glGetTextureSamplerHandleARB = (PFNGLGETTEXTURESAMPLERHANDLEARBPROC) load(userptr, "glGetTextureSamplerHandleARB"); glad_glGetVertexAttribLui64vARB = (PFNGLGETVERTEXATTRIBLUI64VARBPROC) load(userptr, "glGetVertexAttribLui64vARB"); glad_glIsImageHandleResidentARB = (PFNGLISIMAGEHANDLERESIDENTARBPROC) load(userptr, "glIsImageHandleResidentARB"); glad_glIsTextureHandleResidentARB = (PFNGLISTEXTUREHANDLERESIDENTARBPROC) load(userptr, "glIsTextureHandleResidentARB"); glad_glMakeImageHandleNonResidentARB = (PFNGLMAKEIMAGEHANDLENONRESIDENTARBPROC) load(userptr, "glMakeImageHandleNonResidentARB"); glad_glMakeImageHandleResidentARB = (PFNGLMAKEIMAGEHANDLERESIDENTARBPROC) load(userptr, "glMakeImageHandleResidentARB"); glad_glMakeTextureHandleNonResidentARB = (PFNGLMAKETEXTUREHANDLENONRESIDENTARBPROC) load(userptr, "glMakeTextureHandleNonResidentARB"); glad_glMakeTextureHandleResidentARB = (PFNGLMAKETEXTUREHANDLERESIDENTARBPROC) load(userptr, "glMakeTextureHandleResidentARB"); glad_glProgramUniformHandleui64ARB = (PFNGLPROGRAMUNIFORMHANDLEUI64ARBPROC) load(userptr, "glProgramUniformHandleui64ARB"); glad_glProgramUniformHandleui64vARB = (PFNGLPROGRAMUNIFORMHANDLEUI64VARBPROC) load(userptr, "glProgramUniformHandleui64vARB"); glad_glUniformHandleui64ARB = (PFNGLUNIFORMHANDLEUI64ARBPROC) load(userptr, "glUniformHandleui64ARB"); glad_glUniformHandleui64vARB = (PFNGLUNIFORMHANDLEUI64VARBPROC) load(userptr, "glUniformHandleui64vARB"); glad_glVertexAttribL1ui64ARB = (PFNGLVERTEXATTRIBL1UI64ARBPROC) load(userptr, "glVertexAttribL1ui64ARB"); glad_glVertexAttribL1ui64vARB = (PFNGLVERTEXATTRIBL1UI64VARBPROC) load(userptr, "glVertexAttribL1ui64vARB"); } static void glad_gl_load_GL_ARB_blend_func_extended( GLADuserptrloadfunc load, void* userptr) { if(!GLAD_GL_ARB_blend_func_extended) return; glad_glBindFragDataLocationIndexed = (PFNGLBINDFRAGDATALOCATIONINDEXEDPROC) load(userptr, "glBindFragDataLocationIndexed"); glad_glGetFragDataIndex = (PFNGLGETFRAGDATAINDEXPROC) load(userptr, "glGetFragDataIndex"); } static void glad_gl_load_GL_ARB_buffer_storage( GLADuserptrloadfunc load, void* userptr) { if(!GLAD_GL_ARB_buffer_storage) return; glad_glBufferStorage = (PFNGLBUFFERSTORAGEPROC) load(userptr, "glBufferStorage"); } static void glad_gl_load_GL_ARB_cl_event( GLADuserptrloadfunc load, void* userptr) { if(!GLAD_GL_ARB_cl_event) return; glad_glCreateSyncFromCLeventARB = (PFNGLCREATESYNCFROMCLEVENTARBPROC) load(userptr, "glCreateSyncFromCLeventARB"); } static void glad_gl_load_GL_ARB_clear_buffer_object( GLADuserptrloadfunc load, void* userptr) { if(!GLAD_GL_ARB_clear_buffer_object) return; glad_glClearBufferData = (PFNGLCLEARBUFFERDATAPROC) load(userptr, "glClearBufferData"); glad_glClearBufferSubData = (PFNGLCLEARBUFFERSUBDATAPROC) load(userptr, "glClearBufferSubData"); } static void glad_gl_load_GL_ARB_clear_texture( GLADuserptrloadfunc load, void* userptr) { if(!GLAD_GL_ARB_clear_texture) return; glad_glClearTexImage = (PFNGLCLEARTEXIMAGEPROC) load(userptr, "glClearTexImage"); glad_glClearTexSubImage = (PFNGLCLEARTEXSUBIMAGEPROC) load(userptr, "glClearTexSubImage"); } static void glad_gl_load_GL_ARB_clip_control( GLADuserptrloadfunc load, void* userptr) { if(!GLAD_GL_ARB_clip_control) return; glad_glClipControl = (PFNGLCLIPCONTROLPROC) load(userptr, "glClipControl"); } static void glad_gl_load_GL_ARB_color_buffer_float( GLADuserptrloadfunc load, void* userptr) { if(!GLAD_GL_ARB_color_buffer_float) return; glad_glClampColor = (PFNGLCLAMPCOLORPROC) load(userptr, "glClampColor"); glad_glClampColorARB = (PFNGLCLAMPCOLORARBPROC) load(userptr, "glClampColorARB"); } static void glad_gl_load_GL_ARB_compute_shader( GLADuserptrloadfunc load, void* userptr) { if(!GLAD_GL_ARB_compute_shader) return; glad_glDispatchCompute = (PFNGLDISPATCHCOMPUTEPROC) load(userptr, "glDispatchCompute"); glad_glDispatchComputeIndirect = (PFNGLDISPATCHCOMPUTEINDIRECTPROC) load(userptr, "glDispatchComputeIndirect"); } static void glad_gl_load_GL_ARB_compute_variable_group_size( GLADuserptrloadfunc load, void* userptr) { if(!GLAD_GL_ARB_compute_variable_group_size) return; glad_glDispatchComputeGroupSizeARB = (PFNGLDISPATCHCOMPUTEGROUPSIZEARBPROC) load(userptr, "glDispatchComputeGroupSizeARB"); } static void glad_gl_load_GL_ARB_copy_buffer( GLADuserptrloadfunc load, void* userptr) { if(!GLAD_GL_ARB_copy_buffer) return; glad_glCopyBufferSubData = (PFNGLCOPYBUFFERSUBDATAPROC) load(userptr, "glCopyBufferSubData"); } static void glad_gl_load_GL_ARB_copy_image( GLADuserptrloadfunc load, void* userptr) { if(!GLAD_GL_ARB_copy_image) return; glad_glCopyImageSubData = (PFNGLCOPYIMAGESUBDATAPROC) load(userptr, "glCopyImageSubData"); } static void glad_gl_load_GL_ARB_debug_output( GLADuserptrloadfunc load, void* userptr) { if(!GLAD_GL_ARB_debug_output) return; glad_glDebugMessageCallback = (PFNGLDEBUGMESSAGECALLBACKPROC) load(userptr, "glDebugMessageCallback"); glad_glDebugMessageCallbackARB = (PFNGLDEBUGMESSAGECALLBACKARBPROC) load(userptr, "glDebugMessageCallbackARB"); glad_glDebugMessageControl = (PFNGLDEBUGMESSAGECONTROLPROC) load(userptr, "glDebugMessageControl"); glad_glDebugMessageControlARB = (PFNGLDEBUGMESSAGECONTROLARBPROC) load(userptr, "glDebugMessageControlARB"); glad_glDebugMessageInsert = (PFNGLDEBUGMESSAGEINSERTPROC) load(userptr, "glDebugMessageInsert"); glad_glDebugMessageInsertARB = (PFNGLDEBUGMESSAGEINSERTARBPROC) load(userptr, "glDebugMessageInsertARB"); glad_glGetDebugMessageLog = (PFNGLGETDEBUGMESSAGELOGPROC) load(userptr, "glGetDebugMessageLog"); glad_glGetDebugMessageLogARB = (PFNGLGETDEBUGMESSAGELOGARBPROC) load(userptr, "glGetDebugMessageLogARB"); } static void glad_gl_load_GL_ARB_direct_state_access( GLADuserptrloadfunc load, void* userptr) { if(!GLAD_GL_ARB_direct_state_access) return; glad_glBindTextureUnit = (PFNGLBINDTEXTUREUNITPROC) load(userptr, "glBindTextureUnit"); glad_glBlitNamedFramebuffer = (PFNGLBLITNAMEDFRAMEBUFFERPROC) load(userptr, "glBlitNamedFramebuffer"); glad_glCheckNamedFramebufferStatus = (PFNGLCHECKNAMEDFRAMEBUFFERSTATUSPROC) load(userptr, "glCheckNamedFramebufferStatus"); glad_glClearNamedBufferData = (PFNGLCLEARNAMEDBUFFERDATAPROC) load(userptr, "glClearNamedBufferData"); glad_glClearNamedBufferSubData = (PFNGLCLEARNAMEDBUFFERSUBDATAPROC) load(userptr, "glClearNamedBufferSubData"); glad_glClearNamedFramebufferfi = (PFNGLCLEARNAMEDFRAMEBUFFERFIPROC) load(userptr, "glClearNamedFramebufferfi"); glad_glClearNamedFramebufferfv = (PFNGLCLEARNAMEDFRAMEBUFFERFVPROC) load(userptr, "glClearNamedFramebufferfv"); glad_glClearNamedFramebufferiv = (PFNGLCLEARNAMEDFRAMEBUFFERIVPROC) load(userptr, "glClearNamedFramebufferiv"); glad_glClearNamedFramebufferuiv = (PFNGLCLEARNAMEDFRAMEBUFFERUIVPROC) load(userptr, "glClearNamedFramebufferuiv"); glad_glCompressedTextureSubImage1D = (PFNGLCOMPRESSEDTEXTURESUBIMAGE1DPROC) load(userptr, "glCompressedTextureSubImage1D"); glad_glCompressedTextureSubImage2D = (PFNGLCOMPRESSEDTEXTURESUBIMAGE2DPROC) load(userptr, "glCompressedTextureSubImage2D"); glad_glCompressedTextureSubImage3D = (PFNGLCOMPRESSEDTEXTURESUBIMAGE3DPROC) load(userptr, "glCompressedTextureSubImage3D"); glad_glCopyNamedBufferSubData = (PFNGLCOPYNAMEDBUFFERSUBDATAPROC) load(userptr, "glCopyNamedBufferSubData"); glad_glCopyTextureSubImage1D = (PFNGLCOPYTEXTURESUBIMAGE1DPROC) load(userptr, "glCopyTextureSubImage1D"); glad_glCopyTextureSubImage2D = (PFNGLCOPYTEXTURESUBIMAGE2DPROC) load(userptr, "glCopyTextureSubImage2D"); glad_glCopyTextureSubImage3D = (PFNGLCOPYTEXTURESUBIMAGE3DPROC) load(userptr, "glCopyTextureSubImage3D"); glad_glCreateBuffers = (PFNGLCREATEBUFFERSPROC) load(userptr, "glCreateBuffers"); glad_glCreateFramebuffers = (PFNGLCREATEFRAMEBUFFERSPROC) load(userptr, "glCreateFramebuffers"); glad_glCreateProgramPipelines = (PFNGLCREATEPROGRAMPIPELINESPROC) load(userptr, "glCreateProgramPipelines"); glad_glCreateQueries = (PFNGLCREATEQUERIESPROC) load(userptr, "glCreateQueries"); glad_glCreateRenderbuffers = (PFNGLCREATERENDERBUFFERSPROC) load(userptr, "glCreateRenderbuffers"); glad_glCreateSamplers = (PFNGLCREATESAMPLERSPROC) load(userptr, "glCreateSamplers"); glad_glCreateTextures = (PFNGLCREATETEXTURESPROC) load(userptr, "glCreateTextures"); glad_glCreateTransformFeedbacks = (PFNGLCREATETRANSFORMFEEDBACKSPROC) load(userptr, "glCreateTransformFeedbacks"); glad_glCreateVertexArrays = (PFNGLCREATEVERTEXARRAYSPROC) load(userptr, "glCreateVertexArrays"); glad_glDisableVertexArrayAttrib = (PFNGLDISABLEVERTEXARRAYATTRIBPROC) load(userptr, "glDisableVertexArrayAttrib"); glad_glEnableVertexArrayAttrib = (PFNGLENABLEVERTEXARRAYATTRIBPROC) load(userptr, "glEnableVertexArrayAttrib"); glad_glFlushMappedNamedBufferRange = (PFNGLFLUSHMAPPEDNAMEDBUFFERRANGEPROC) load(userptr, "glFlushMappedNamedBufferRange"); glad_glGenerateTextureMipmap = (PFNGLGENERATETEXTUREMIPMAPPROC) load(userptr, "glGenerateTextureMipmap"); glad_glGetCompressedTextureImage = (PFNGLGETCOMPRESSEDTEXTUREIMAGEPROC) load(userptr, "glGetCompressedTextureImage"); glad_glGetNamedBufferParameteri64v = (PFNGLGETNAMEDBUFFERPARAMETERI64VPROC) load(userptr, "glGetNamedBufferParameteri64v"); glad_glGetNamedBufferParameteriv = (PFNGLGETNAMEDBUFFERPARAMETERIVPROC) load(userptr, "glGetNamedBufferParameteriv"); glad_glGetNamedBufferPointerv = (PFNGLGETNAMEDBUFFERPOINTERVPROC) load(userptr, "glGetNamedBufferPointerv"); glad_glGetNamedBufferSubData = (PFNGLGETNAMEDBUFFERSUBDATAPROC) load(userptr, "glGetNamedBufferSubData"); glad_glGetNamedFramebufferAttachmentParameteriv = (PFNGLGETNAMEDFRAMEBUFFERATTACHMENTPARAMETERIVPROC) load(userptr, "glGetNamedFramebufferAttachmentParameteriv"); glad_glGetNamedFramebufferParameteriv = (PFNGLGETNAMEDFRAMEBUFFERPARAMETERIVPROC) load(userptr, "glGetNamedFramebufferParameteriv"); glad_glGetNamedRenderbufferParameteriv = (PFNGLGETNAMEDRENDERBUFFERPARAMETERIVPROC) load(userptr, "glGetNamedRenderbufferParameteriv"); glad_glGetQueryBufferObjecti64v = (PFNGLGETQUERYBUFFEROBJECTI64VPROC) load(userptr, "glGetQueryBufferObjecti64v"); glad_glGetQueryBufferObjectiv = (PFNGLGETQUERYBUFFEROBJECTIVPROC) load(userptr, "glGetQueryBufferObjectiv"); glad_glGetQueryBufferObjectui64v = (PFNGLGETQUERYBUFFEROBJECTUI64VPROC) load(userptr, "glGetQueryBufferObjectui64v"); glad_glGetQueryBufferObjectuiv = (PFNGLGETQUERYBUFFEROBJECTUIVPROC) load(userptr, "glGetQueryBufferObjectuiv"); glad_glGetTextureImage = (PFNGLGETTEXTUREIMAGEPROC) load(userptr, "glGetTextureImage"); glad_glGetTextureLevelParameterfv = (PFNGLGETTEXTURELEVELPARAMETERFVPROC) load(userptr, "glGetTextureLevelParameterfv"); glad_glGetTextureLevelParameteriv = (PFNGLGETTEXTURELEVELPARAMETERIVPROC) load(userptr, "glGetTextureLevelParameteriv"); glad_glGetTextureParameterIiv = (PFNGLGETTEXTUREPARAMETERIIVPROC) load(userptr, "glGetTextureParameterIiv"); glad_glGetTextureParameterIuiv = (PFNGLGETTEXTUREPARAMETERIUIVPROC) load(userptr, "glGetTextureParameterIuiv"); glad_glGetTextureParameterfv = (PFNGLGETTEXTUREPARAMETERFVPROC) load(userptr, "glGetTextureParameterfv"); glad_glGetTextureParameteriv = (PFNGLGETTEXTUREPARAMETERIVPROC) load(userptr, "glGetTextureParameteriv"); glad_glGetTransformFeedbacki64_v = (PFNGLGETTRANSFORMFEEDBACKI64_VPROC) load(userptr, "glGetTransformFeedbacki64_v"); glad_glGetTransformFeedbacki_v = (PFNGLGETTRANSFORMFEEDBACKI_VPROC) load(userptr, "glGetTransformFeedbacki_v"); glad_glGetTransformFeedbackiv = (PFNGLGETTRANSFORMFEEDBACKIVPROC) load(userptr, "glGetTransformFeedbackiv"); glad_glGetVertexArrayIndexed64iv = (PFNGLGETVERTEXARRAYINDEXED64IVPROC) load(userptr, "glGetVertexArrayIndexed64iv"); glad_glGetVertexArrayIndexediv = (PFNGLGETVERTEXARRAYINDEXEDIVPROC) load(userptr, "glGetVertexArrayIndexediv"); glad_glGetVertexArrayiv = (PFNGLGETVERTEXARRAYIVPROC) load(userptr, "glGetVertexArrayiv"); glad_glInvalidateNamedFramebufferData = (PFNGLINVALIDATENAMEDFRAMEBUFFERDATAPROC) load(userptr, "glInvalidateNamedFramebufferData"); glad_glInvalidateNamedFramebufferSubData = (PFNGLINVALIDATENAMEDFRAMEBUFFERSUBDATAPROC) load(userptr, "glInvalidateNamedFramebufferSubData"); glad_glMapNamedBuffer = (PFNGLMAPNAMEDBUFFERPROC) load(userptr, "glMapNamedBuffer"); glad_glMapNamedBufferRange = (PFNGLMAPNAMEDBUFFERRANGEPROC) load(userptr, "glMapNamedBufferRange"); glad_glNamedBufferData = (PFNGLNAMEDBUFFERDATAPROC) load(userptr, "glNamedBufferData"); glad_glNamedBufferStorage = (PFNGLNAMEDBUFFERSTORAGEPROC) load(userptr, "glNamedBufferStorage"); glad_glNamedBufferSubData = (PFNGLNAMEDBUFFERSUBDATAPROC) load(userptr, "glNamedBufferSubData"); glad_glNamedFramebufferDrawBuffer = (PFNGLNAMEDFRAMEBUFFERDRAWBUFFERPROC) load(userptr, "glNamedFramebufferDrawBuffer"); glad_glNamedFramebufferDrawBuffers = (PFNGLNAMEDFRAMEBUFFERDRAWBUFFERSPROC) load(userptr, "glNamedFramebufferDrawBuffers"); glad_glNamedFramebufferParameteri = (PFNGLNAMEDFRAMEBUFFERPARAMETERIPROC) load(userptr, "glNamedFramebufferParameteri"); glad_glNamedFramebufferReadBuffer = (PFNGLNAMEDFRAMEBUFFERREADBUFFERPROC) load(userptr, "glNamedFramebufferReadBuffer"); glad_glNamedFramebufferRenderbuffer = (PFNGLNAMEDFRAMEBUFFERRENDERBUFFERPROC) load(userptr, "glNamedFramebufferRenderbuffer"); glad_glNamedFramebufferTexture = (PFNGLNAMEDFRAMEBUFFERTEXTUREPROC) load(userptr, "glNamedFramebufferTexture"); glad_glNamedFramebufferTextureLayer = (PFNGLNAMEDFRAMEBUFFERTEXTURELAYERPROC) load(userptr, "glNamedFramebufferTextureLayer"); glad_glNamedRenderbufferStorage = (PFNGLNAMEDRENDERBUFFERSTORAGEPROC) load(userptr, "glNamedRenderbufferStorage"); glad_glNamedRenderbufferStorageMultisample = (PFNGLNAMEDRENDERBUFFERSTORAGEMULTISAMPLEPROC) load(userptr, "glNamedRenderbufferStorageMultisample"); glad_glTextureBuffer = (PFNGLTEXTUREBUFFERPROC) load(userptr, "glTextureBuffer"); glad_glTextureBufferRange = (PFNGLTEXTUREBUFFERRANGEPROC) load(userptr, "glTextureBufferRange"); glad_glTextureParameterIiv = (PFNGLTEXTUREPARAMETERIIVPROC) load(userptr, "glTextureParameterIiv"); glad_glTextureParameterIuiv = (PFNGLTEXTUREPARAMETERIUIVPROC) load(userptr, "glTextureParameterIuiv"); glad_glTextureParameterf = (PFNGLTEXTUREPARAMETERFPROC) load(userptr, "glTextureParameterf"); glad_glTextureParameterfv = (PFNGLTEXTUREPARAMETERFVPROC) load(userptr, "glTextureParameterfv"); glad_glTextureParameteri = (PFNGLTEXTUREPARAMETERIPROC) load(userptr, "glTextureParameteri"); glad_glTextureParameteriv = (PFNGLTEXTUREPARAMETERIVPROC) load(userptr, "glTextureParameteriv"); glad_glTextureStorage1D = (PFNGLTEXTURESTORAGE1DPROC) load(userptr, "glTextureStorage1D"); glad_glTextureStorage2D = (PFNGLTEXTURESTORAGE2DPROC) load(userptr, "glTextureStorage2D"); glad_glTextureStorage2DMultisample = (PFNGLTEXTURESTORAGE2DMULTISAMPLEPROC) load(userptr, "glTextureStorage2DMultisample"); glad_glTextureStorage3D = (PFNGLTEXTURESTORAGE3DPROC) load(userptr, "glTextureStorage3D"); glad_glTextureStorage3DMultisample = (PFNGLTEXTURESTORAGE3DMULTISAMPLEPROC) load(userptr, "glTextureStorage3DMultisample"); glad_glTextureSubImage1D = (PFNGLTEXTURESUBIMAGE1DPROC) load(userptr, "glTextureSubImage1D"); glad_glTextureSubImage2D = (PFNGLTEXTURESUBIMAGE2DPROC) load(userptr, "glTextureSubImage2D"); glad_glTextureSubImage3D = (PFNGLTEXTURESUBIMAGE3DPROC) load(userptr, "glTextureSubImage3D"); glad_glTransformFeedbackBufferBase = (PFNGLTRANSFORMFEEDBACKBUFFERBASEPROC) load(userptr, "glTransformFeedbackBufferBase"); glad_glTransformFeedbackBufferRange = (PFNGLTRANSFORMFEEDBACKBUFFERRANGEPROC) load(userptr, "glTransformFeedbackBufferRange"); glad_glUnmapNamedBuffer = (PFNGLUNMAPNAMEDBUFFERPROC) load(userptr, "glUnmapNamedBuffer"); glad_glVertexArrayAttribBinding = (PFNGLVERTEXARRAYATTRIBBINDINGPROC) load(userptr, "glVertexArrayAttribBinding"); glad_glVertexArrayAttribFormat = (PFNGLVERTEXARRAYATTRIBFORMATPROC) load(userptr, "glVertexArrayAttribFormat"); glad_glVertexArrayAttribIFormat = (PFNGLVERTEXARRAYATTRIBIFORMATPROC) load(userptr, "glVertexArrayAttribIFormat"); glad_glVertexArrayAttribLFormat = (PFNGLVERTEXARRAYATTRIBLFORMATPROC) load(userptr, "glVertexArrayAttribLFormat"); glad_glVertexArrayBindingDivisor = (PFNGLVERTEXARRAYBINDINGDIVISORPROC) load(userptr, "glVertexArrayBindingDivisor"); glad_glVertexArrayElementBuffer = (PFNGLVERTEXARRAYELEMENTBUFFERPROC) load(userptr, "glVertexArrayElementBuffer"); glad_glVertexArrayVertexBuffer = (PFNGLVERTEXARRAYVERTEXBUFFERPROC) load(userptr, "glVertexArrayVertexBuffer"); glad_glVertexArrayVertexBuffers = (PFNGLVERTEXARRAYVERTEXBUFFERSPROC) load(userptr, "glVertexArrayVertexBuffers"); } static void glad_gl_load_GL_ARB_draw_buffers( GLADuserptrloadfunc load, void* userptr) { if(!GLAD_GL_ARB_draw_buffers) return; glad_glDrawBuffers = (PFNGLDRAWBUFFERSPROC) load(userptr, "glDrawBuffers"); glad_glDrawBuffersARB = (PFNGLDRAWBUFFERSARBPROC) load(userptr, "glDrawBuffersARB"); } static void glad_gl_load_GL_ARB_draw_buffers_blend( GLADuserptrloadfunc load, void* userptr) { if(!GLAD_GL_ARB_draw_buffers_blend) return; glad_glBlendEquationSeparatei = (PFNGLBLENDEQUATIONSEPARATEIPROC) load(userptr, "glBlendEquationSeparatei"); glad_glBlendEquationSeparateiARB = (PFNGLBLENDEQUATIONSEPARATEIARBPROC) load(userptr, "glBlendEquationSeparateiARB"); glad_glBlendEquationi = (PFNGLBLENDEQUATIONIPROC) load(userptr, "glBlendEquationi"); glad_glBlendEquationiARB = (PFNGLBLENDEQUATIONIARBPROC) load(userptr, "glBlendEquationiARB"); glad_glBlendFuncSeparatei = (PFNGLBLENDFUNCSEPARATEIPROC) load(userptr, "glBlendFuncSeparatei"); glad_glBlendFuncSeparateiARB = (PFNGLBLENDFUNCSEPARATEIARBPROC) load(userptr, "glBlendFuncSeparateiARB"); glad_glBlendFunci = (PFNGLBLENDFUNCIPROC) load(userptr, "glBlendFunci"); glad_glBlendFunciARB = (PFNGLBLENDFUNCIARBPROC) load(userptr, "glBlendFunciARB"); } static void glad_gl_load_GL_ARB_draw_elements_base_vertex( GLADuserptrloadfunc load, void* userptr) { if(!GLAD_GL_ARB_draw_elements_base_vertex) return; glad_glDrawElementsBaseVertex = (PFNGLDRAWELEMENTSBASEVERTEXPROC) load(userptr, "glDrawElementsBaseVertex"); glad_glDrawElementsInstancedBaseVertex = (PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXPROC) load(userptr, "glDrawElementsInstancedBaseVertex"); glad_glDrawRangeElementsBaseVertex = (PFNGLDRAWRANGEELEMENTSBASEVERTEXPROC) load(userptr, "glDrawRangeElementsBaseVertex"); glad_glMultiDrawElementsBaseVertex = (PFNGLMULTIDRAWELEMENTSBASEVERTEXPROC) load(userptr, "glMultiDrawElementsBaseVertex"); } static void glad_gl_load_GL_ARB_draw_indirect( GLADuserptrloadfunc load, void* userptr) { if(!GLAD_GL_ARB_draw_indirect) return; glad_glDrawArraysIndirect = (PFNGLDRAWARRAYSINDIRECTPROC) load(userptr, "glDrawArraysIndirect"); glad_glDrawElementsIndirect = (PFNGLDRAWELEMENTSINDIRECTPROC) load(userptr, "glDrawElementsIndirect"); } static void glad_gl_load_GL_ARB_draw_instanced( GLADuserptrloadfunc load, void* userptr) { if(!GLAD_GL_ARB_draw_instanced) return; glad_glDrawArraysInstanced = (PFNGLDRAWARRAYSINSTANCEDPROC) load(userptr, "glDrawArraysInstanced"); glad_glDrawArraysInstancedARB = (PFNGLDRAWARRAYSINSTANCEDARBPROC) load(userptr, "glDrawArraysInstancedARB"); glad_glDrawElementsInstanced = (PFNGLDRAWELEMENTSINSTANCEDPROC) load(userptr, "glDrawElementsInstanced"); glad_glDrawElementsInstancedARB = (PFNGLDRAWELEMENTSINSTANCEDARBPROC) load(userptr, "glDrawElementsInstancedARB"); } static void glad_gl_load_GL_ARB_fragment_program( GLADuserptrloadfunc load, void* userptr) { if(!GLAD_GL_ARB_fragment_program) return; glad_glBindProgramARB = (PFNGLBINDPROGRAMARBPROC) load(userptr, "glBindProgramARB"); glad_glDeleteProgramsARB = (PFNGLDELETEPROGRAMSARBPROC) load(userptr, "glDeleteProgramsARB"); glad_glGenProgramsARB = (PFNGLGENPROGRAMSARBPROC) load(userptr, "glGenProgramsARB"); glad_glGetProgramEnvParameterdvARB = (PFNGLGETPROGRAMENVPARAMETERDVARBPROC) load(userptr, "glGetProgramEnvParameterdvARB"); glad_glGetProgramEnvParameterfvARB = (PFNGLGETPROGRAMENVPARAMETERFVARBPROC) load(userptr, "glGetProgramEnvParameterfvARB"); glad_glGetProgramLocalParameterdvARB = (PFNGLGETPROGRAMLOCALPARAMETERDVARBPROC) load(userptr, "glGetProgramLocalParameterdvARB"); glad_glGetProgramLocalParameterfvARB = (PFNGLGETPROGRAMLOCALPARAMETERFVARBPROC) load(userptr, "glGetProgramLocalParameterfvARB"); glad_glGetProgramStringARB = (PFNGLGETPROGRAMSTRINGARBPROC) load(userptr, "glGetProgramStringARB"); glad_glGetProgramivARB = (PFNGLGETPROGRAMIVARBPROC) load(userptr, "glGetProgramivARB"); glad_glIsProgramARB = (PFNGLISPROGRAMARBPROC) load(userptr, "glIsProgramARB"); glad_glProgramEnvParameter4dARB = (PFNGLPROGRAMENVPARAMETER4DARBPROC) load(userptr, "glProgramEnvParameter4dARB"); glad_glProgramEnvParameter4dvARB = (PFNGLPROGRAMENVPARAMETER4DVARBPROC) load(userptr, "glProgramEnvParameter4dvARB"); glad_glProgramEnvParameter4fARB = (PFNGLPROGRAMENVPARAMETER4FARBPROC) load(userptr, "glProgramEnvParameter4fARB"); glad_glProgramEnvParameter4fvARB = (PFNGLPROGRAMENVPARAMETER4FVARBPROC) load(userptr, "glProgramEnvParameter4fvARB"); glad_glProgramLocalParameter4dARB = (PFNGLPROGRAMLOCALPARAMETER4DARBPROC) load(userptr, "glProgramLocalParameter4dARB"); glad_glProgramLocalParameter4dvARB = (PFNGLPROGRAMLOCALPARAMETER4DVARBPROC) load(userptr, "glProgramLocalParameter4dvARB"); glad_glProgramLocalParameter4fARB = (PFNGLPROGRAMLOCALPARAMETER4FARBPROC) load(userptr, "glProgramLocalParameter4fARB"); glad_glProgramLocalParameter4fvARB = (PFNGLPROGRAMLOCALPARAMETER4FVARBPROC) load(userptr, "glProgramLocalParameter4fvARB"); glad_glProgramStringARB = (PFNGLPROGRAMSTRINGARBPROC) load(userptr, "glProgramStringARB"); } static void glad_gl_load_GL_ARB_framebuffer_no_attachments( GLADuserptrloadfunc load, void* userptr) { if(!GLAD_GL_ARB_framebuffer_no_attachments) return; glad_glFramebufferParameteri = (PFNGLFRAMEBUFFERPARAMETERIPROC) load(userptr, "glFramebufferParameteri"); glad_glGetFramebufferParameteriv = (PFNGLGETFRAMEBUFFERPARAMETERIVPROC) load(userptr, "glGetFramebufferParameteriv"); } static void glad_gl_load_GL_ARB_framebuffer_object( GLADuserptrloadfunc load, void* userptr) { if(!GLAD_GL_ARB_framebuffer_object) return; glad_glBindFramebuffer = (PFNGLBINDFRAMEBUFFERPROC) load(userptr, "glBindFramebuffer"); glad_glBindRenderbuffer = (PFNGLBINDRENDERBUFFERPROC) load(userptr, "glBindRenderbuffer"); glad_glBlitFramebuffer = (PFNGLBLITFRAMEBUFFERPROC) load(userptr, "glBlitFramebuffer"); glad_glCheckFramebufferStatus = (PFNGLCHECKFRAMEBUFFERSTATUSPROC) load(userptr, "glCheckFramebufferStatus"); glad_glDeleteFramebuffers = (PFNGLDELETEFRAMEBUFFERSPROC) load(userptr, "glDeleteFramebuffers"); glad_glDeleteRenderbuffers = (PFNGLDELETERENDERBUFFERSPROC) load(userptr, "glDeleteRenderbuffers"); glad_glFramebufferRenderbuffer = (PFNGLFRAMEBUFFERRENDERBUFFERPROC) load(userptr, "glFramebufferRenderbuffer"); glad_glFramebufferTexture1D = (PFNGLFRAMEBUFFERTEXTURE1DPROC) load(userptr, "glFramebufferTexture1D"); glad_glFramebufferTexture2D = (PFNGLFRAMEBUFFERTEXTURE2DPROC) load(userptr, "glFramebufferTexture2D"); glad_glFramebufferTexture3D = (PFNGLFRAMEBUFFERTEXTURE3DPROC) load(userptr, "glFramebufferTexture3D"); glad_glFramebufferTextureLayer = (PFNGLFRAMEBUFFERTEXTURELAYERPROC) load(userptr, "glFramebufferTextureLayer"); glad_glGenFramebuffers = (PFNGLGENFRAMEBUFFERSPROC) load(userptr, "glGenFramebuffers"); glad_glGenRenderbuffers = (PFNGLGENRENDERBUFFERSPROC) load(userptr, "glGenRenderbuffers"); glad_glGenerateMipmap = (PFNGLGENERATEMIPMAPPROC) load(userptr, "glGenerateMipmap"); glad_glGetFramebufferAttachmentParameteriv = (PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVPROC) load(userptr, "glGetFramebufferAttachmentParameteriv"); glad_glGetRenderbufferParameteriv = (PFNGLGETRENDERBUFFERPARAMETERIVPROC) load(userptr, "glGetRenderbufferParameteriv"); glad_glIsFramebuffer = (PFNGLISFRAMEBUFFERPROC) load(userptr, "glIsFramebuffer"); glad_glIsRenderbuffer = (PFNGLISRENDERBUFFERPROC) load(userptr, "glIsRenderbuffer"); glad_glRenderbufferStorage = (PFNGLRENDERBUFFERSTORAGEPROC) load(userptr, "glRenderbufferStorage"); glad_glRenderbufferStorageMultisample = (PFNGLRENDERBUFFERSTORAGEMULTISAMPLEPROC) load(userptr, "glRenderbufferStorageMultisample"); } static void glad_gl_load_GL_ARB_geometry_shader4( GLADuserptrloadfunc load, void* userptr) { if(!GLAD_GL_ARB_geometry_shader4) return; glad_glFramebufferTexture = (PFNGLFRAMEBUFFERTEXTUREPROC) load(userptr, "glFramebufferTexture"); glad_glFramebufferTextureARB = (PFNGLFRAMEBUFFERTEXTUREARBPROC) load(userptr, "glFramebufferTextureARB"); glad_glFramebufferTextureFaceARB = (PFNGLFRAMEBUFFERTEXTUREFACEARBPROC) load(userptr, "glFramebufferTextureFaceARB"); glad_glFramebufferTextureLayer = (PFNGLFRAMEBUFFERTEXTURELAYERPROC) load(userptr, "glFramebufferTextureLayer"); glad_glFramebufferTextureLayerARB = (PFNGLFRAMEBUFFERTEXTURELAYERARBPROC) load(userptr, "glFramebufferTextureLayerARB"); glad_glProgramParameteri = (PFNGLPROGRAMPARAMETERIPROC) load(userptr, "glProgramParameteri"); glad_glProgramParameteriARB = (PFNGLPROGRAMPARAMETERIARBPROC) load(userptr, "glProgramParameteriARB"); } static void glad_gl_load_GL_ARB_get_program_binary( GLADuserptrloadfunc load, void* userptr) { if(!GLAD_GL_ARB_get_program_binary) return; glad_glGetProgramBinary = (PFNGLGETPROGRAMBINARYPROC) load(userptr, "glGetProgramBinary"); glad_glProgramBinary = (PFNGLPROGRAMBINARYPROC) load(userptr, "glProgramBinary"); glad_glProgramParameteri = (PFNGLPROGRAMPARAMETERIPROC) load(userptr, "glProgramParameteri"); } static void glad_gl_load_GL_ARB_get_texture_sub_image( GLADuserptrloadfunc load, void* userptr) { if(!GLAD_GL_ARB_get_texture_sub_image) return; glad_glGetCompressedTextureSubImage = (PFNGLGETCOMPRESSEDTEXTURESUBIMAGEPROC) load(userptr, "glGetCompressedTextureSubImage"); glad_glGetTextureSubImage = (PFNGLGETTEXTURESUBIMAGEPROC) load(userptr, "glGetTextureSubImage"); } static void glad_gl_load_GL_ARB_gl_spirv( GLADuserptrloadfunc load, void* userptr) { if(!GLAD_GL_ARB_gl_spirv) return; glad_glSpecializeShader = (PFNGLSPECIALIZESHADERPROC) load(userptr, "glSpecializeShader"); glad_glSpecializeShaderARB = (PFNGLSPECIALIZESHADERARBPROC) load(userptr, "glSpecializeShaderARB"); } static void glad_gl_load_GL_ARB_gpu_shader_fp64( GLADuserptrloadfunc load, void* userptr) { if(!GLAD_GL_ARB_gpu_shader_fp64) return; glad_glGetUniformdv = (PFNGLGETUNIFORMDVPROC) load(userptr, "glGetUniformdv"); glad_glUniform1d = (PFNGLUNIFORM1DPROC) load(userptr, "glUniform1d"); glad_glUniform1dv = (PFNGLUNIFORM1DVPROC) load(userptr, "glUniform1dv"); glad_glUniform2d = (PFNGLUNIFORM2DPROC) load(userptr, "glUniform2d"); glad_glUniform2dv = (PFNGLUNIFORM2DVPROC) load(userptr, "glUniform2dv"); glad_glUniform3d = (PFNGLUNIFORM3DPROC) load(userptr, "glUniform3d"); glad_glUniform3dv = (PFNGLUNIFORM3DVPROC) load(userptr, "glUniform3dv"); glad_glUniform4d = (PFNGLUNIFORM4DPROC) load(userptr, "glUniform4d"); glad_glUniform4dv = (PFNGLUNIFORM4DVPROC) load(userptr, "glUniform4dv"); glad_glUniformMatrix2dv = (PFNGLUNIFORMMATRIX2DVPROC) load(userptr, "glUniformMatrix2dv"); glad_glUniformMatrix2x3dv = (PFNGLUNIFORMMATRIX2X3DVPROC) load(userptr, "glUniformMatrix2x3dv"); glad_glUniformMatrix2x4dv = (PFNGLUNIFORMMATRIX2X4DVPROC) load(userptr, "glUniformMatrix2x4dv"); glad_glUniformMatrix3dv = (PFNGLUNIFORMMATRIX3DVPROC) load(userptr, "glUniformMatrix3dv"); glad_glUniformMatrix3x2dv = (PFNGLUNIFORMMATRIX3X2DVPROC) load(userptr, "glUniformMatrix3x2dv"); glad_glUniformMatrix3x4dv = (PFNGLUNIFORMMATRIX3X4DVPROC) load(userptr, "glUniformMatrix3x4dv"); glad_glUniformMatrix4dv = (PFNGLUNIFORMMATRIX4DVPROC) load(userptr, "glUniformMatrix4dv"); glad_glUniformMatrix4x2dv = (PFNGLUNIFORMMATRIX4X2DVPROC) load(userptr, "glUniformMatrix4x2dv"); glad_glUniformMatrix4x3dv = (PFNGLUNIFORMMATRIX4X3DVPROC) load(userptr, "glUniformMatrix4x3dv"); } static void glad_gl_load_GL_ARB_gpu_shader_int64( GLADuserptrloadfunc load, void* userptr) { if(!GLAD_GL_ARB_gpu_shader_int64) return; glad_glGetUniformi64vARB = (PFNGLGETUNIFORMI64VARBPROC) load(userptr, "glGetUniformi64vARB"); glad_glGetUniformui64vARB = (PFNGLGETUNIFORMUI64VARBPROC) load(userptr, "glGetUniformui64vARB"); glad_glGetnUniformi64vARB = (PFNGLGETNUNIFORMI64VARBPROC) load(userptr, "glGetnUniformi64vARB"); glad_glGetnUniformui64vARB = (PFNGLGETNUNIFORMUI64VARBPROC) load(userptr, "glGetnUniformui64vARB"); glad_glProgramUniform1i64ARB = (PFNGLPROGRAMUNIFORM1I64ARBPROC) load(userptr, "glProgramUniform1i64ARB"); glad_glProgramUniform1i64vARB = (PFNGLPROGRAMUNIFORM1I64VARBPROC) load(userptr, "glProgramUniform1i64vARB"); glad_glProgramUniform1ui64ARB = (PFNGLPROGRAMUNIFORM1UI64ARBPROC) load(userptr, "glProgramUniform1ui64ARB"); glad_glProgramUniform1ui64vARB = (PFNGLPROGRAMUNIFORM1UI64VARBPROC) load(userptr, "glProgramUniform1ui64vARB"); glad_glProgramUniform2i64ARB = (PFNGLPROGRAMUNIFORM2I64ARBPROC) load(userptr, "glProgramUniform2i64ARB"); glad_glProgramUniform2i64vARB = (PFNGLPROGRAMUNIFORM2I64VARBPROC) load(userptr, "glProgramUniform2i64vARB"); glad_glProgramUniform2ui64ARB = (PFNGLPROGRAMUNIFORM2UI64ARBPROC) load(userptr, "glProgramUniform2ui64ARB"); glad_glProgramUniform2ui64vARB = (PFNGLPROGRAMUNIFORM2UI64VARBPROC) load(userptr, "glProgramUniform2ui64vARB"); glad_glProgramUniform3i64ARB = (PFNGLPROGRAMUNIFORM3I64ARBPROC) load(userptr, "glProgramUniform3i64ARB"); glad_glProgramUniform3i64vARB = (PFNGLPROGRAMUNIFORM3I64VARBPROC) load(userptr, "glProgramUniform3i64vARB"); glad_glProgramUniform3ui64ARB = (PFNGLPROGRAMUNIFORM3UI64ARBPROC) load(userptr, "glProgramUniform3ui64ARB"); glad_glProgramUniform3ui64vARB = (PFNGLPROGRAMUNIFORM3UI64VARBPROC) load(userptr, "glProgramUniform3ui64vARB"); glad_glProgramUniform4i64ARB = (PFNGLPROGRAMUNIFORM4I64ARBPROC) load(userptr, "glProgramUniform4i64ARB"); glad_glProgramUniform4i64vARB = (PFNGLPROGRAMUNIFORM4I64VARBPROC) load(userptr, "glProgramUniform4i64vARB"); glad_glProgramUniform4ui64ARB = (PFNGLPROGRAMUNIFORM4UI64ARBPROC) load(userptr, "glProgramUniform4ui64ARB"); glad_glProgramUniform4ui64vARB = (PFNGLPROGRAMUNIFORM4UI64VARBPROC) load(userptr, "glProgramUniform4ui64vARB"); glad_glUniform1i64ARB = (PFNGLUNIFORM1I64ARBPROC) load(userptr, "glUniform1i64ARB"); glad_glUniform1i64vARB = (PFNGLUNIFORM1I64VARBPROC) load(userptr, "glUniform1i64vARB"); glad_glUniform1ui64ARB = (PFNGLUNIFORM1UI64ARBPROC) load(userptr, "glUniform1ui64ARB"); glad_glUniform1ui64vARB = (PFNGLUNIFORM1UI64VARBPROC) load(userptr, "glUniform1ui64vARB"); glad_glUniform2i64ARB = (PFNGLUNIFORM2I64ARBPROC) load(userptr, "glUniform2i64ARB"); glad_glUniform2i64vARB = (PFNGLUNIFORM2I64VARBPROC) load(userptr, "glUniform2i64vARB"); glad_glUniform2ui64ARB = (PFNGLUNIFORM2UI64ARBPROC) load(userptr, "glUniform2ui64ARB"); glad_glUniform2ui64vARB = (PFNGLUNIFORM2UI64VARBPROC) load(userptr, "glUniform2ui64vARB"); glad_glUniform3i64ARB = (PFNGLUNIFORM3I64ARBPROC) load(userptr, "glUniform3i64ARB"); glad_glUniform3i64vARB = (PFNGLUNIFORM3I64VARBPROC) load(userptr, "glUniform3i64vARB"); glad_glUniform3ui64ARB = (PFNGLUNIFORM3UI64ARBPROC) load(userptr, "glUniform3ui64ARB"); glad_glUniform3ui64vARB = (PFNGLUNIFORM3UI64VARBPROC) load(userptr, "glUniform3ui64vARB"); glad_glUniform4i64ARB = (PFNGLUNIFORM4I64ARBPROC) load(userptr, "glUniform4i64ARB"); glad_glUniform4i64vARB = (PFNGLUNIFORM4I64VARBPROC) load(userptr, "glUniform4i64vARB"); glad_glUniform4ui64ARB = (PFNGLUNIFORM4UI64ARBPROC) load(userptr, "glUniform4ui64ARB"); glad_glUniform4ui64vARB = (PFNGLUNIFORM4UI64VARBPROC) load(userptr, "glUniform4ui64vARB"); } static void glad_gl_load_GL_ARB_imaging( GLADuserptrloadfunc load, void* userptr) { if(!GLAD_GL_ARB_imaging) return; glad_glBlendColor = (PFNGLBLENDCOLORPROC) load(userptr, "glBlendColor"); glad_glBlendEquation = (PFNGLBLENDEQUATIONPROC) load(userptr, "glBlendEquation"); glad_glColorSubTable = (PFNGLCOLORSUBTABLEPROC) load(userptr, "glColorSubTable"); glad_glColorTable = (PFNGLCOLORTABLEPROC) load(userptr, "glColorTable"); glad_glColorTableParameterfv = (PFNGLCOLORTABLEPARAMETERFVPROC) load(userptr, "glColorTableParameterfv"); glad_glColorTableParameteriv = (PFNGLCOLORTABLEPARAMETERIVPROC) load(userptr, "glColorTableParameteriv"); glad_glConvolutionFilter1D = (PFNGLCONVOLUTIONFILTER1DPROC) load(userptr, "glConvolutionFilter1D"); glad_glConvolutionFilter2D = (PFNGLCONVOLUTIONFILTER2DPROC) load(userptr, "glConvolutionFilter2D"); glad_glConvolutionParameterf = (PFNGLCONVOLUTIONPARAMETERFPROC) load(userptr, "glConvolutionParameterf"); glad_glConvolutionParameterfv = (PFNGLCONVOLUTIONPARAMETERFVPROC) load(userptr, "glConvolutionParameterfv"); glad_glConvolutionParameteri = (PFNGLCONVOLUTIONPARAMETERIPROC) load(userptr, "glConvolutionParameteri"); glad_glConvolutionParameteriv = (PFNGLCONVOLUTIONPARAMETERIVPROC) load(userptr, "glConvolutionParameteriv"); glad_glCopyColorSubTable = (PFNGLCOPYCOLORSUBTABLEPROC) load(userptr, "glCopyColorSubTable"); glad_glCopyColorTable = (PFNGLCOPYCOLORTABLEPROC) load(userptr, "glCopyColorTable"); glad_glCopyConvolutionFilter1D = (PFNGLCOPYCONVOLUTIONFILTER1DPROC) load(userptr, "glCopyConvolutionFilter1D"); glad_glCopyConvolutionFilter2D = (PFNGLCOPYCONVOLUTIONFILTER2DPROC) load(userptr, "glCopyConvolutionFilter2D"); glad_glGetColorTable = (PFNGLGETCOLORTABLEPROC) load(userptr, "glGetColorTable"); glad_glGetColorTableParameterfv = (PFNGLGETCOLORTABLEPARAMETERFVPROC) load(userptr, "glGetColorTableParameterfv"); glad_glGetColorTableParameteriv = (PFNGLGETCOLORTABLEPARAMETERIVPROC) load(userptr, "glGetColorTableParameteriv"); glad_glGetConvolutionFilter = (PFNGLGETCONVOLUTIONFILTERPROC) load(userptr, "glGetConvolutionFilter"); glad_glGetConvolutionParameterfv = (PFNGLGETCONVOLUTIONPARAMETERFVPROC) load(userptr, "glGetConvolutionParameterfv"); glad_glGetConvolutionParameteriv = (PFNGLGETCONVOLUTIONPARAMETERIVPROC) load(userptr, "glGetConvolutionParameteriv"); glad_glGetHistogram = (PFNGLGETHISTOGRAMPROC) load(userptr, "glGetHistogram"); glad_glGetHistogramParameterfv = (PFNGLGETHISTOGRAMPARAMETERFVPROC) load(userptr, "glGetHistogramParameterfv"); glad_glGetHistogramParameteriv = (PFNGLGETHISTOGRAMPARAMETERIVPROC) load(userptr, "glGetHistogramParameteriv"); glad_glGetMinmax = (PFNGLGETMINMAXPROC) load(userptr, "glGetMinmax"); glad_glGetMinmaxParameterfv = (PFNGLGETMINMAXPARAMETERFVPROC) load(userptr, "glGetMinmaxParameterfv"); glad_glGetMinmaxParameteriv = (PFNGLGETMINMAXPARAMETERIVPROC) load(userptr, "glGetMinmaxParameteriv"); glad_glGetSeparableFilter = (PFNGLGETSEPARABLEFILTERPROC) load(userptr, "glGetSeparableFilter"); glad_glHistogram = (PFNGLHISTOGRAMPROC) load(userptr, "glHistogram"); glad_glMinmax = (PFNGLMINMAXPROC) load(userptr, "glMinmax"); glad_glResetHistogram = (PFNGLRESETHISTOGRAMPROC) load(userptr, "glResetHistogram"); glad_glResetMinmax = (PFNGLRESETMINMAXPROC) load(userptr, "glResetMinmax"); glad_glSeparableFilter2D = (PFNGLSEPARABLEFILTER2DPROC) load(userptr, "glSeparableFilter2D"); } static void glad_gl_load_GL_ARB_indirect_parameters( GLADuserptrloadfunc load, void* userptr) { if(!GLAD_GL_ARB_indirect_parameters) return; glad_glMultiDrawArraysIndirectCount = (PFNGLMULTIDRAWARRAYSINDIRECTCOUNTPROC) load(userptr, "glMultiDrawArraysIndirectCount"); glad_glMultiDrawArraysIndirectCountARB = (PFNGLMULTIDRAWARRAYSINDIRECTCOUNTARBPROC) load(userptr, "glMultiDrawArraysIndirectCountARB"); glad_glMultiDrawElementsIndirectCount = (PFNGLMULTIDRAWELEMENTSINDIRECTCOUNTPROC) load(userptr, "glMultiDrawElementsIndirectCount"); glad_glMultiDrawElementsIndirectCountARB = (PFNGLMULTIDRAWELEMENTSINDIRECTCOUNTARBPROC) load(userptr, "glMultiDrawElementsIndirectCountARB"); } static void glad_gl_load_GL_ARB_instanced_arrays( GLADuserptrloadfunc load, void* userptr) { if(!GLAD_GL_ARB_instanced_arrays) return; glad_glVertexAttribDivisor = (PFNGLVERTEXATTRIBDIVISORPROC) load(userptr, "glVertexAttribDivisor"); glad_glVertexAttribDivisorARB = (PFNGLVERTEXATTRIBDIVISORARBPROC) load(userptr, "glVertexAttribDivisorARB"); } static void glad_gl_load_GL_ARB_internalformat_query( GLADuserptrloadfunc load, void* userptr) { if(!GLAD_GL_ARB_internalformat_query) return; glad_glGetInternalformativ = (PFNGLGETINTERNALFORMATIVPROC) load(userptr, "glGetInternalformativ"); } static void glad_gl_load_GL_ARB_internalformat_query2( GLADuserptrloadfunc load, void* userptr) { if(!GLAD_GL_ARB_internalformat_query2) return; glad_glGetInternalformati64v = (PFNGLGETINTERNALFORMATI64VPROC) load(userptr, "glGetInternalformati64v"); } static void glad_gl_load_GL_ARB_invalidate_subdata( GLADuserptrloadfunc load, void* userptr) { if(!GLAD_GL_ARB_invalidate_subdata) return; glad_glInvalidateBufferData = (PFNGLINVALIDATEBUFFERDATAPROC) load(userptr, "glInvalidateBufferData"); glad_glInvalidateBufferSubData = (PFNGLINVALIDATEBUFFERSUBDATAPROC) load(userptr, "glInvalidateBufferSubData"); glad_glInvalidateFramebuffer = (PFNGLINVALIDATEFRAMEBUFFERPROC) load(userptr, "glInvalidateFramebuffer"); glad_glInvalidateSubFramebuffer = (PFNGLINVALIDATESUBFRAMEBUFFERPROC) load(userptr, "glInvalidateSubFramebuffer"); glad_glInvalidateTexImage = (PFNGLINVALIDATETEXIMAGEPROC) load(userptr, "glInvalidateTexImage"); glad_glInvalidateTexSubImage = (PFNGLINVALIDATETEXSUBIMAGEPROC) load(userptr, "glInvalidateTexSubImage"); } static void glad_gl_load_GL_ARB_map_buffer_range( GLADuserptrloadfunc load, void* userptr) { if(!GLAD_GL_ARB_map_buffer_range) return; glad_glFlushMappedBufferRange = (PFNGLFLUSHMAPPEDBUFFERRANGEPROC) load(userptr, "glFlushMappedBufferRange"); glad_glMapBufferRange = (PFNGLMAPBUFFERRANGEPROC) load(userptr, "glMapBufferRange"); } static void glad_gl_load_GL_ARB_matrix_palette( GLADuserptrloadfunc load, void* userptr) { if(!GLAD_GL_ARB_matrix_palette) return; glad_glCurrentPaletteMatrixARB = (PFNGLCURRENTPALETTEMATRIXARBPROC) load(userptr, "glCurrentPaletteMatrixARB"); glad_glMatrixIndexPointerARB = (PFNGLMATRIXINDEXPOINTERARBPROC) load(userptr, "glMatrixIndexPointerARB"); glad_glMatrixIndexubvARB = (PFNGLMATRIXINDEXUBVARBPROC) load(userptr, "glMatrixIndexubvARB"); glad_glMatrixIndexuivARB = (PFNGLMATRIXINDEXUIVARBPROC) load(userptr, "glMatrixIndexuivARB"); glad_glMatrixIndexusvARB = (PFNGLMATRIXINDEXUSVARBPROC) load(userptr, "glMatrixIndexusvARB"); } static void glad_gl_load_GL_ARB_multi_bind( GLADuserptrloadfunc load, void* userptr) { if(!GLAD_GL_ARB_multi_bind) return; glad_glBindBuffersBase = (PFNGLBINDBUFFERSBASEPROC) load(userptr, "glBindBuffersBase"); glad_glBindBuffersRange = (PFNGLBINDBUFFERSRANGEPROC) load(userptr, "glBindBuffersRange"); glad_glBindImageTextures = (PFNGLBINDIMAGETEXTURESPROC) load(userptr, "glBindImageTextures"); glad_glBindSamplers = (PFNGLBINDSAMPLERSPROC) load(userptr, "glBindSamplers"); glad_glBindTextures = (PFNGLBINDTEXTURESPROC) load(userptr, "glBindTextures"); glad_glBindVertexBuffers = (PFNGLBINDVERTEXBUFFERSPROC) load(userptr, "glBindVertexBuffers"); } static void glad_gl_load_GL_ARB_multi_draw_indirect( GLADuserptrloadfunc load, void* userptr) { if(!GLAD_GL_ARB_multi_draw_indirect) return; glad_glMultiDrawArraysIndirect = (PFNGLMULTIDRAWARRAYSINDIRECTPROC) load(userptr, "glMultiDrawArraysIndirect"); glad_glMultiDrawElementsIndirect = (PFNGLMULTIDRAWELEMENTSINDIRECTPROC) load(userptr, "glMultiDrawElementsIndirect"); } static void glad_gl_load_GL_ARB_multisample( GLADuserptrloadfunc load, void* userptr) { if(!GLAD_GL_ARB_multisample) return; glad_glSampleCoverage = (PFNGLSAMPLECOVERAGEPROC) load(userptr, "glSampleCoverage"); glad_glSampleCoverageARB = (PFNGLSAMPLECOVERAGEARBPROC) load(userptr, "glSampleCoverageARB"); } static void glad_gl_load_GL_ARB_multitexture( GLADuserptrloadfunc load, void* userptr) { if(!GLAD_GL_ARB_multitexture) return; glad_glActiveTexture = (PFNGLACTIVETEXTUREPROC) load(userptr, "glActiveTexture"); glad_glActiveTextureARB = (PFNGLACTIVETEXTUREARBPROC) load(userptr, "glActiveTextureARB"); glad_glClientActiveTexture = (PFNGLCLIENTACTIVETEXTUREPROC) load(userptr, "glClientActiveTexture"); glad_glClientActiveTextureARB = (PFNGLCLIENTACTIVETEXTUREARBPROC) load(userptr, "glClientActiveTextureARB"); glad_glMultiTexCoord1d = (PFNGLMULTITEXCOORD1DPROC) load(userptr, "glMultiTexCoord1d"); glad_glMultiTexCoord1dARB = (PFNGLMULTITEXCOORD1DARBPROC) load(userptr, "glMultiTexCoord1dARB"); glad_glMultiTexCoord1dv = (PFNGLMULTITEXCOORD1DVPROC) load(userptr, "glMultiTexCoord1dv"); glad_glMultiTexCoord1dvARB = (PFNGLMULTITEXCOORD1DVARBPROC) load(userptr, "glMultiTexCoord1dvARB"); glad_glMultiTexCoord1f = (PFNGLMULTITEXCOORD1FPROC) load(userptr, "glMultiTexCoord1f"); glad_glMultiTexCoord1fARB = (PFNGLMULTITEXCOORD1FARBPROC) load(userptr, "glMultiTexCoord1fARB"); glad_glMultiTexCoord1fv = (PFNGLMULTITEXCOORD1FVPROC) load(userptr, "glMultiTexCoord1fv"); glad_glMultiTexCoord1fvARB = (PFNGLMULTITEXCOORD1FVARBPROC) load(userptr, "glMultiTexCoord1fvARB"); glad_glMultiTexCoord1i = (PFNGLMULTITEXCOORD1IPROC) load(userptr, "glMultiTexCoord1i"); glad_glMultiTexCoord1iARB = (PFNGLMULTITEXCOORD1IARBPROC) load(userptr, "glMultiTexCoord1iARB"); glad_glMultiTexCoord1iv = (PFNGLMULTITEXCOORD1IVPROC) load(userptr, "glMultiTexCoord1iv"); glad_glMultiTexCoord1ivARB = (PFNGLMULTITEXCOORD1IVARBPROC) load(userptr, "glMultiTexCoord1ivARB"); glad_glMultiTexCoord1s = (PFNGLMULTITEXCOORD1SPROC) load(userptr, "glMultiTexCoord1s"); glad_glMultiTexCoord1sARB = (PFNGLMULTITEXCOORD1SARBPROC) load(userptr, "glMultiTexCoord1sARB"); glad_glMultiTexCoord1sv = (PFNGLMULTITEXCOORD1SVPROC) load(userptr, "glMultiTexCoord1sv"); glad_glMultiTexCoord1svARB = (PFNGLMULTITEXCOORD1SVARBPROC) load(userptr, "glMultiTexCoord1svARB"); glad_glMultiTexCoord2d = (PFNGLMULTITEXCOORD2DPROC) load(userptr, "glMultiTexCoord2d"); glad_glMultiTexCoord2dARB = (PFNGLMULTITEXCOORD2DARBPROC) load(userptr, "glMultiTexCoord2dARB"); glad_glMultiTexCoord2dv = (PFNGLMULTITEXCOORD2DVPROC) load(userptr, "glMultiTexCoord2dv"); glad_glMultiTexCoord2dvARB = (PFNGLMULTITEXCOORD2DVARBPROC) load(userptr, "glMultiTexCoord2dvARB"); glad_glMultiTexCoord2f = (PFNGLMULTITEXCOORD2FPROC) load(userptr, "glMultiTexCoord2f"); glad_glMultiTexCoord2fARB = (PFNGLMULTITEXCOORD2FARBPROC) load(userptr, "glMultiTexCoord2fARB"); glad_glMultiTexCoord2fv = (PFNGLMULTITEXCOORD2FVPROC) load(userptr, "glMultiTexCoord2fv"); glad_glMultiTexCoord2fvARB = (PFNGLMULTITEXCOORD2FVARBPROC) load(userptr, "glMultiTexCoord2fvARB"); glad_glMultiTexCoord2i = (PFNGLMULTITEXCOORD2IPROC) load(userptr, "glMultiTexCoord2i"); glad_glMultiTexCoord2iARB = (PFNGLMULTITEXCOORD2IARBPROC) load(userptr, "glMultiTexCoord2iARB"); glad_glMultiTexCoord2iv = (PFNGLMULTITEXCOORD2IVPROC) load(userptr, "glMultiTexCoord2iv"); glad_glMultiTexCoord2ivARB = (PFNGLMULTITEXCOORD2IVARBPROC) load(userptr, "glMultiTexCoord2ivARB"); glad_glMultiTexCoord2s = (PFNGLMULTITEXCOORD2SPROC) load(userptr, "glMultiTexCoord2s"); glad_glMultiTexCoord2sARB = (PFNGLMULTITEXCOORD2SARBPROC) load(userptr, "glMultiTexCoord2sARB"); glad_glMultiTexCoord2sv = (PFNGLMULTITEXCOORD2SVPROC) load(userptr, "glMultiTexCoord2sv"); glad_glMultiTexCoord2svARB = (PFNGLMULTITEXCOORD2SVARBPROC) load(userptr, "glMultiTexCoord2svARB"); glad_glMultiTexCoord3d = (PFNGLMULTITEXCOORD3DPROC) load(userptr, "glMultiTexCoord3d"); glad_glMultiTexCoord3dARB = (PFNGLMULTITEXCOORD3DARBPROC) load(userptr, "glMultiTexCoord3dARB"); glad_glMultiTexCoord3dv = (PFNGLMULTITEXCOORD3DVPROC) load(userptr, "glMultiTexCoord3dv"); glad_glMultiTexCoord3dvARB = (PFNGLMULTITEXCOORD3DVARBPROC) load(userptr, "glMultiTexCoord3dvARB"); glad_glMultiTexCoord3f = (PFNGLMULTITEXCOORD3FPROC) load(userptr, "glMultiTexCoord3f"); glad_glMultiTexCoord3fARB = (PFNGLMULTITEXCOORD3FARBPROC) load(userptr, "glMultiTexCoord3fARB"); glad_glMultiTexCoord3fv = (PFNGLMULTITEXCOORD3FVPROC) load(userptr, "glMultiTexCoord3fv"); glad_glMultiTexCoord3fvARB = (PFNGLMULTITEXCOORD3FVARBPROC) load(userptr, "glMultiTexCoord3fvARB"); glad_glMultiTexCoord3i = (PFNGLMULTITEXCOORD3IPROC) load(userptr, "glMultiTexCoord3i"); glad_glMultiTexCoord3iARB = (PFNGLMULTITEXCOORD3IARBPROC) load(userptr, "glMultiTexCoord3iARB"); glad_glMultiTexCoord3iv = (PFNGLMULTITEXCOORD3IVPROC) load(userptr, "glMultiTexCoord3iv"); glad_glMultiTexCoord3ivARB = (PFNGLMULTITEXCOORD3IVARBPROC) load(userptr, "glMultiTexCoord3ivARB"); glad_glMultiTexCoord3s = (PFNGLMULTITEXCOORD3SPROC) load(userptr, "glMultiTexCoord3s"); glad_glMultiTexCoord3sARB = (PFNGLMULTITEXCOORD3SARBPROC) load(userptr, "glMultiTexCoord3sARB"); glad_glMultiTexCoord3sv = (PFNGLMULTITEXCOORD3SVPROC) load(userptr, "glMultiTexCoord3sv"); glad_glMultiTexCoord3svARB = (PFNGLMULTITEXCOORD3SVARBPROC) load(userptr, "glMultiTexCoord3svARB"); glad_glMultiTexCoord4d = (PFNGLMULTITEXCOORD4DPROC) load(userptr, "glMultiTexCoord4d"); glad_glMultiTexCoord4dARB = (PFNGLMULTITEXCOORD4DARBPROC) load(userptr, "glMultiTexCoord4dARB"); glad_glMultiTexCoord4dv = (PFNGLMULTITEXCOORD4DVPROC) load(userptr, "glMultiTexCoord4dv"); glad_glMultiTexCoord4dvARB = (PFNGLMULTITEXCOORD4DVARBPROC) load(userptr, "glMultiTexCoord4dvARB"); glad_glMultiTexCoord4f = (PFNGLMULTITEXCOORD4FPROC) load(userptr, "glMultiTexCoord4f"); glad_glMultiTexCoord4fARB = (PFNGLMULTITEXCOORD4FARBPROC) load(userptr, "glMultiTexCoord4fARB"); glad_glMultiTexCoord4fv = (PFNGLMULTITEXCOORD4FVPROC) load(userptr, "glMultiTexCoord4fv"); glad_glMultiTexCoord4fvARB = (PFNGLMULTITEXCOORD4FVARBPROC) load(userptr, "glMultiTexCoord4fvARB"); glad_glMultiTexCoord4i = (PFNGLMULTITEXCOORD4IPROC) load(userptr, "glMultiTexCoord4i"); glad_glMultiTexCoord4iARB = (PFNGLMULTITEXCOORD4IARBPROC) load(userptr, "glMultiTexCoord4iARB"); glad_glMultiTexCoord4iv = (PFNGLMULTITEXCOORD4IVPROC) load(userptr, "glMultiTexCoord4iv"); glad_glMultiTexCoord4ivARB = (PFNGLMULTITEXCOORD4IVARBPROC) load(userptr, "glMultiTexCoord4ivARB"); glad_glMultiTexCoord4s = (PFNGLMULTITEXCOORD4SPROC) load(userptr, "glMultiTexCoord4s"); glad_glMultiTexCoord4sARB = (PFNGLMULTITEXCOORD4SARBPROC) load(userptr, "glMultiTexCoord4sARB"); glad_glMultiTexCoord4sv = (PFNGLMULTITEXCOORD4SVPROC) load(userptr, "glMultiTexCoord4sv"); glad_glMultiTexCoord4svARB = (PFNGLMULTITEXCOORD4SVARBPROC) load(userptr, "glMultiTexCoord4svARB"); } static void glad_gl_load_GL_ARB_occlusion_query( GLADuserptrloadfunc load, void* userptr) { if(!GLAD_GL_ARB_occlusion_query) return; glad_glBeginQuery = (PFNGLBEGINQUERYPROC) load(userptr, "glBeginQuery"); glad_glBeginQueryARB = (PFNGLBEGINQUERYARBPROC) load(userptr, "glBeginQueryARB"); glad_glDeleteQueries = (PFNGLDELETEQUERIESPROC) load(userptr, "glDeleteQueries"); glad_glDeleteQueriesARB = (PFNGLDELETEQUERIESARBPROC) load(userptr, "glDeleteQueriesARB"); glad_glEndQuery = (PFNGLENDQUERYPROC) load(userptr, "glEndQuery"); glad_glEndQueryARB = (PFNGLENDQUERYARBPROC) load(userptr, "glEndQueryARB"); glad_glGenQueries = (PFNGLGENQUERIESPROC) load(userptr, "glGenQueries"); glad_glGenQueriesARB = (PFNGLGENQUERIESARBPROC) load(userptr, "glGenQueriesARB"); glad_glGetQueryObjectiv = (PFNGLGETQUERYOBJECTIVPROC) load(userptr, "glGetQueryObjectiv"); glad_glGetQueryObjectivARB = (PFNGLGETQUERYOBJECTIVARBPROC) load(userptr, "glGetQueryObjectivARB"); glad_glGetQueryObjectuiv = (PFNGLGETQUERYOBJECTUIVPROC) load(userptr, "glGetQueryObjectuiv"); glad_glGetQueryObjectuivARB = (PFNGLGETQUERYOBJECTUIVARBPROC) load(userptr, "glGetQueryObjectuivARB"); glad_glGetQueryiv = (PFNGLGETQUERYIVPROC) load(userptr, "glGetQueryiv"); glad_glGetQueryivARB = (PFNGLGETQUERYIVARBPROC) load(userptr, "glGetQueryivARB"); glad_glIsQuery = (PFNGLISQUERYPROC) load(userptr, "glIsQuery"); glad_glIsQueryARB = (PFNGLISQUERYARBPROC) load(userptr, "glIsQueryARB"); } static void glad_gl_load_GL_ARB_parallel_shader_compile( GLADuserptrloadfunc load, void* userptr) { if(!GLAD_GL_ARB_parallel_shader_compile) return; glad_glMaxShaderCompilerThreadsARB = (PFNGLMAXSHADERCOMPILERTHREADSARBPROC) load(userptr, "glMaxShaderCompilerThreadsARB"); glad_glMaxShaderCompilerThreadsKHR = (PFNGLMAXSHADERCOMPILERTHREADSKHRPROC) load(userptr, "glMaxShaderCompilerThreadsKHR"); } static void glad_gl_load_GL_ARB_point_parameters( GLADuserptrloadfunc load, void* userptr) { if(!GLAD_GL_ARB_point_parameters) return; glad_glPointParameterf = (PFNGLPOINTPARAMETERFPROC) load(userptr, "glPointParameterf"); glad_glPointParameterfARB = (PFNGLPOINTPARAMETERFARBPROC) load(userptr, "glPointParameterfARB"); glad_glPointParameterfv = (PFNGLPOINTPARAMETERFVPROC) load(userptr, "glPointParameterfv"); glad_glPointParameterfvARB = (PFNGLPOINTPARAMETERFVARBPROC) load(userptr, "glPointParameterfvARB"); } static void glad_gl_load_GL_ARB_polygon_offset_clamp( GLADuserptrloadfunc load, void* userptr) { if(!GLAD_GL_ARB_polygon_offset_clamp) return; glad_glPolygonOffsetClamp = (PFNGLPOLYGONOFFSETCLAMPPROC) load(userptr, "glPolygonOffsetClamp"); } static void glad_gl_load_GL_ARB_program_interface_query( GLADuserptrloadfunc load, void* userptr) { if(!GLAD_GL_ARB_program_interface_query) return; glad_glGetProgramInterfaceiv = (PFNGLGETPROGRAMINTERFACEIVPROC) load(userptr, "glGetProgramInterfaceiv"); glad_glGetProgramResourceIndex = (PFNGLGETPROGRAMRESOURCEINDEXPROC) load(userptr, "glGetProgramResourceIndex"); glad_glGetProgramResourceLocation = (PFNGLGETPROGRAMRESOURCELOCATIONPROC) load(userptr, "glGetProgramResourceLocation"); glad_glGetProgramResourceLocationIndex = (PFNGLGETPROGRAMRESOURCELOCATIONINDEXPROC) load(userptr, "glGetProgramResourceLocationIndex"); glad_glGetProgramResourceName = (PFNGLGETPROGRAMRESOURCENAMEPROC) load(userptr, "glGetProgramResourceName"); glad_glGetProgramResourceiv = (PFNGLGETPROGRAMRESOURCEIVPROC) load(userptr, "glGetProgramResourceiv"); } static void glad_gl_load_GL_ARB_provoking_vertex( GLADuserptrloadfunc load, void* userptr) { if(!GLAD_GL_ARB_provoking_vertex) return; glad_glProvokingVertex = (PFNGLPROVOKINGVERTEXPROC) load(userptr, "glProvokingVertex"); } static void glad_gl_load_GL_ARB_robustness( GLADuserptrloadfunc load, void* userptr) { if(!GLAD_GL_ARB_robustness) return; glad_glGetGraphicsResetStatusARB = (PFNGLGETGRAPHICSRESETSTATUSARBPROC) load(userptr, "glGetGraphicsResetStatusARB"); glad_glGetnColorTableARB = (PFNGLGETNCOLORTABLEARBPROC) load(userptr, "glGetnColorTableARB"); glad_glGetnCompressedTexImageARB = (PFNGLGETNCOMPRESSEDTEXIMAGEARBPROC) load(userptr, "glGetnCompressedTexImageARB"); glad_glGetnConvolutionFilterARB = (PFNGLGETNCONVOLUTIONFILTERARBPROC) load(userptr, "glGetnConvolutionFilterARB"); glad_glGetnHistogramARB = (PFNGLGETNHISTOGRAMARBPROC) load(userptr, "glGetnHistogramARB"); glad_glGetnMapdvARB = (PFNGLGETNMAPDVARBPROC) load(userptr, "glGetnMapdvARB"); glad_glGetnMapfvARB = (PFNGLGETNMAPFVARBPROC) load(userptr, "glGetnMapfvARB"); glad_glGetnMapivARB = (PFNGLGETNMAPIVARBPROC) load(userptr, "glGetnMapivARB"); glad_glGetnMinmaxARB = (PFNGLGETNMINMAXARBPROC) load(userptr, "glGetnMinmaxARB"); glad_glGetnPixelMapfvARB = (PFNGLGETNPIXELMAPFVARBPROC) load(userptr, "glGetnPixelMapfvARB"); glad_glGetnPixelMapuivARB = (PFNGLGETNPIXELMAPUIVARBPROC) load(userptr, "glGetnPixelMapuivARB"); glad_glGetnPixelMapusvARB = (PFNGLGETNPIXELMAPUSVARBPROC) load(userptr, "glGetnPixelMapusvARB"); glad_glGetnPolygonStippleARB = (PFNGLGETNPOLYGONSTIPPLEARBPROC) load(userptr, "glGetnPolygonStippleARB"); glad_glGetnSeparableFilterARB = (PFNGLGETNSEPARABLEFILTERARBPROC) load(userptr, "glGetnSeparableFilterARB"); glad_glGetnTexImageARB = (PFNGLGETNTEXIMAGEARBPROC) load(userptr, "glGetnTexImageARB"); glad_glGetnUniformdvARB = (PFNGLGETNUNIFORMDVARBPROC) load(userptr, "glGetnUniformdvARB"); glad_glGetnUniformfvARB = (PFNGLGETNUNIFORMFVARBPROC) load(userptr, "glGetnUniformfvARB"); glad_glGetnUniformivARB = (PFNGLGETNUNIFORMIVARBPROC) load(userptr, "glGetnUniformivARB"); glad_glGetnUniformuivARB = (PFNGLGETNUNIFORMUIVARBPROC) load(userptr, "glGetnUniformuivARB"); glad_glReadnPixels = (PFNGLREADNPIXELSPROC) load(userptr, "glReadnPixels"); glad_glReadnPixelsARB = (PFNGLREADNPIXELSARBPROC) load(userptr, "glReadnPixelsARB"); } static void glad_gl_load_GL_ARB_sample_locations( GLADuserptrloadfunc load, void* userptr) { if(!GLAD_GL_ARB_sample_locations) return; glad_glEvaluateDepthValuesARB = (PFNGLEVALUATEDEPTHVALUESARBPROC) load(userptr, "glEvaluateDepthValuesARB"); glad_glFramebufferSampleLocationsfvARB = (PFNGLFRAMEBUFFERSAMPLELOCATIONSFVARBPROC) load(userptr, "glFramebufferSampleLocationsfvARB"); glad_glNamedFramebufferSampleLocationsfvARB = (PFNGLNAMEDFRAMEBUFFERSAMPLELOCATIONSFVARBPROC) load(userptr, "glNamedFramebufferSampleLocationsfvARB"); } static void glad_gl_load_GL_ARB_sample_shading( GLADuserptrloadfunc load, void* userptr) { if(!GLAD_GL_ARB_sample_shading) return; glad_glMinSampleShading = (PFNGLMINSAMPLESHADINGPROC) load(userptr, "glMinSampleShading"); glad_glMinSampleShadingARB = (PFNGLMINSAMPLESHADINGARBPROC) load(userptr, "glMinSampleShadingARB"); } static void glad_gl_load_GL_ARB_sampler_objects( GLADuserptrloadfunc load, void* userptr) { if(!GLAD_GL_ARB_sampler_objects) return; glad_glBindSampler = (PFNGLBINDSAMPLERPROC) load(userptr, "glBindSampler"); glad_glDeleteSamplers = (PFNGLDELETESAMPLERSPROC) load(userptr, "glDeleteSamplers"); glad_glGenSamplers = (PFNGLGENSAMPLERSPROC) load(userptr, "glGenSamplers"); glad_glGetSamplerParameterIiv = (PFNGLGETSAMPLERPARAMETERIIVPROC) load(userptr, "glGetSamplerParameterIiv"); glad_glGetSamplerParameterIuiv = (PFNGLGETSAMPLERPARAMETERIUIVPROC) load(userptr, "glGetSamplerParameterIuiv"); glad_glGetSamplerParameterfv = (PFNGLGETSAMPLERPARAMETERFVPROC) load(userptr, "glGetSamplerParameterfv"); glad_glGetSamplerParameteriv = (PFNGLGETSAMPLERPARAMETERIVPROC) load(userptr, "glGetSamplerParameteriv"); glad_glIsSampler = (PFNGLISSAMPLERPROC) load(userptr, "glIsSampler"); glad_glSamplerParameterIiv = (PFNGLSAMPLERPARAMETERIIVPROC) load(userptr, "glSamplerParameterIiv"); glad_glSamplerParameterIuiv = (PFNGLSAMPLERPARAMETERIUIVPROC) load(userptr, "glSamplerParameterIuiv"); glad_glSamplerParameterf = (PFNGLSAMPLERPARAMETERFPROC) load(userptr, "glSamplerParameterf"); glad_glSamplerParameterfv = (PFNGLSAMPLERPARAMETERFVPROC) load(userptr, "glSamplerParameterfv"); glad_glSamplerParameteri = (PFNGLSAMPLERPARAMETERIPROC) load(userptr, "glSamplerParameteri"); glad_glSamplerParameteriv = (PFNGLSAMPLERPARAMETERIVPROC) load(userptr, "glSamplerParameteriv"); } static void glad_gl_load_GL_ARB_separate_shader_objects( GLADuserptrloadfunc load, void* userptr) { if(!GLAD_GL_ARB_separate_shader_objects) return; glad_glActiveShaderProgram = (PFNGLACTIVESHADERPROGRAMPROC) load(userptr, "glActiveShaderProgram"); glad_glBindProgramPipeline = (PFNGLBINDPROGRAMPIPELINEPROC) load(userptr, "glBindProgramPipeline"); glad_glCreateShaderProgramv = (PFNGLCREATESHADERPROGRAMVPROC) load(userptr, "glCreateShaderProgramv"); glad_glDeleteProgramPipelines = (PFNGLDELETEPROGRAMPIPELINESPROC) load(userptr, "glDeleteProgramPipelines"); glad_glGenProgramPipelines = (PFNGLGENPROGRAMPIPELINESPROC) load(userptr, "glGenProgramPipelines"); glad_glGetProgramPipelineInfoLog = (PFNGLGETPROGRAMPIPELINEINFOLOGPROC) load(userptr, "glGetProgramPipelineInfoLog"); glad_glGetProgramPipelineiv = (PFNGLGETPROGRAMPIPELINEIVPROC) load(userptr, "glGetProgramPipelineiv"); glad_glIsProgramPipeline = (PFNGLISPROGRAMPIPELINEPROC) load(userptr, "glIsProgramPipeline"); glad_glProgramParameteri = (PFNGLPROGRAMPARAMETERIPROC) load(userptr, "glProgramParameteri"); glad_glProgramUniform1d = (PFNGLPROGRAMUNIFORM1DPROC) load(userptr, "glProgramUniform1d"); glad_glProgramUniform1dv = (PFNGLPROGRAMUNIFORM1DVPROC) load(userptr, "glProgramUniform1dv"); glad_glProgramUniform1f = (PFNGLPROGRAMUNIFORM1FPROC) load(userptr, "glProgramUniform1f"); glad_glProgramUniform1fv = (PFNGLPROGRAMUNIFORM1FVPROC) load(userptr, "glProgramUniform1fv"); glad_glProgramUniform1i = (PFNGLPROGRAMUNIFORM1IPROC) load(userptr, "glProgramUniform1i"); glad_glProgramUniform1iv = (PFNGLPROGRAMUNIFORM1IVPROC) load(userptr, "glProgramUniform1iv"); glad_glProgramUniform1ui = (PFNGLPROGRAMUNIFORM1UIPROC) load(userptr, "glProgramUniform1ui"); glad_glProgramUniform1uiv = (PFNGLPROGRAMUNIFORM1UIVPROC) load(userptr, "glProgramUniform1uiv"); glad_glProgramUniform2d = (PFNGLPROGRAMUNIFORM2DPROC) load(userptr, "glProgramUniform2d"); glad_glProgramUniform2dv = (PFNGLPROGRAMUNIFORM2DVPROC) load(userptr, "glProgramUniform2dv"); glad_glProgramUniform2f = (PFNGLPROGRAMUNIFORM2FPROC) load(userptr, "glProgramUniform2f"); glad_glProgramUniform2fv = (PFNGLPROGRAMUNIFORM2FVPROC) load(userptr, "glProgramUniform2fv"); glad_glProgramUniform2i = (PFNGLPROGRAMUNIFORM2IPROC) load(userptr, "glProgramUniform2i"); glad_glProgramUniform2iv = (PFNGLPROGRAMUNIFORM2IVPROC) load(userptr, "glProgramUniform2iv"); glad_glProgramUniform2ui = (PFNGLPROGRAMUNIFORM2UIPROC) load(userptr, "glProgramUniform2ui"); glad_glProgramUniform2uiv = (PFNGLPROGRAMUNIFORM2UIVPROC) load(userptr, "glProgramUniform2uiv"); glad_glProgramUniform3d = (PFNGLPROGRAMUNIFORM3DPROC) load(userptr, "glProgramUniform3d"); glad_glProgramUniform3dv = (PFNGLPROGRAMUNIFORM3DVPROC) load(userptr, "glProgramUniform3dv"); glad_glProgramUniform3f = (PFNGLPROGRAMUNIFORM3FPROC) load(userptr, "glProgramUniform3f"); glad_glProgramUniform3fv = (PFNGLPROGRAMUNIFORM3FVPROC) load(userptr, "glProgramUniform3fv"); glad_glProgramUniform3i = (PFNGLPROGRAMUNIFORM3IPROC) load(userptr, "glProgramUniform3i"); glad_glProgramUniform3iv = (PFNGLPROGRAMUNIFORM3IVPROC) load(userptr, "glProgramUniform3iv"); glad_glProgramUniform3ui = (PFNGLPROGRAMUNIFORM3UIPROC) load(userptr, "glProgramUniform3ui"); glad_glProgramUniform3uiv = (PFNGLPROGRAMUNIFORM3UIVPROC) load(userptr, "glProgramUniform3uiv"); glad_glProgramUniform4d = (PFNGLPROGRAMUNIFORM4DPROC) load(userptr, "glProgramUniform4d"); glad_glProgramUniform4dv = (PFNGLPROGRAMUNIFORM4DVPROC) load(userptr, "glProgramUniform4dv"); glad_glProgramUniform4f = (PFNGLPROGRAMUNIFORM4FPROC) load(userptr, "glProgramUniform4f"); glad_glProgramUniform4fv = (PFNGLPROGRAMUNIFORM4FVPROC) load(userptr, "glProgramUniform4fv"); glad_glProgramUniform4i = (PFNGLPROGRAMUNIFORM4IPROC) load(userptr, "glProgramUniform4i"); glad_glProgramUniform4iv = (PFNGLPROGRAMUNIFORM4IVPROC) load(userptr, "glProgramUniform4iv"); glad_glProgramUniform4ui = (PFNGLPROGRAMUNIFORM4UIPROC) load(userptr, "glProgramUniform4ui"); glad_glProgramUniform4uiv = (PFNGLPROGRAMUNIFORM4UIVPROC) load(userptr, "glProgramUniform4uiv"); glad_glProgramUniformMatrix2dv = (PFNGLPROGRAMUNIFORMMATRIX2DVPROC) load(userptr, "glProgramUniformMatrix2dv"); glad_glProgramUniformMatrix2fv = (PFNGLPROGRAMUNIFORMMATRIX2FVPROC) load(userptr, "glProgramUniformMatrix2fv"); glad_glProgramUniformMatrix2x3dv = (PFNGLPROGRAMUNIFORMMATRIX2X3DVPROC) load(userptr, "glProgramUniformMatrix2x3dv"); glad_glProgramUniformMatrix2x3fv = (PFNGLPROGRAMUNIFORMMATRIX2X3FVPROC) load(userptr, "glProgramUniformMatrix2x3fv"); glad_glProgramUniformMatrix2x4dv = (PFNGLPROGRAMUNIFORMMATRIX2X4DVPROC) load(userptr, "glProgramUniformMatrix2x4dv"); glad_glProgramUniformMatrix2x4fv = (PFNGLPROGRAMUNIFORMMATRIX2X4FVPROC) load(userptr, "glProgramUniformMatrix2x4fv"); glad_glProgramUniformMatrix3dv = (PFNGLPROGRAMUNIFORMMATRIX3DVPROC) load(userptr, "glProgramUniformMatrix3dv"); glad_glProgramUniformMatrix3fv = (PFNGLPROGRAMUNIFORMMATRIX3FVPROC) load(userptr, "glProgramUniformMatrix3fv"); glad_glProgramUniformMatrix3x2dv = (PFNGLPROGRAMUNIFORMMATRIX3X2DVPROC) load(userptr, "glProgramUniformMatrix3x2dv"); glad_glProgramUniformMatrix3x2fv = (PFNGLPROGRAMUNIFORMMATRIX3X2FVPROC) load(userptr, "glProgramUniformMatrix3x2fv"); glad_glProgramUniformMatrix3x4dv = (PFNGLPROGRAMUNIFORMMATRIX3X4DVPROC) load(userptr, "glProgramUniformMatrix3x4dv"); glad_glProgramUniformMatrix3x4fv = (PFNGLPROGRAMUNIFORMMATRIX3X4FVPROC) load(userptr, "glProgramUniformMatrix3x4fv"); glad_glProgramUniformMatrix4dv = (PFNGLPROGRAMUNIFORMMATRIX4DVPROC) load(userptr, "glProgramUniformMatrix4dv"); glad_glProgramUniformMatrix4fv = (PFNGLPROGRAMUNIFORMMATRIX4FVPROC) load(userptr, "glProgramUniformMatrix4fv"); glad_glProgramUniformMatrix4x2dv = (PFNGLPROGRAMUNIFORMMATRIX4X2DVPROC) load(userptr, "glProgramUniformMatrix4x2dv"); glad_glProgramUniformMatrix4x2fv = (PFNGLPROGRAMUNIFORMMATRIX4X2FVPROC) load(userptr, "glProgramUniformMatrix4x2fv"); glad_glProgramUniformMatrix4x3dv = (PFNGLPROGRAMUNIFORMMATRIX4X3DVPROC) load(userptr, "glProgramUniformMatrix4x3dv"); glad_glProgramUniformMatrix4x3fv = (PFNGLPROGRAMUNIFORMMATRIX4X3FVPROC) load(userptr, "glProgramUniformMatrix4x3fv"); glad_glUseProgramStages = (PFNGLUSEPROGRAMSTAGESPROC) load(userptr, "glUseProgramStages"); glad_glValidateProgramPipeline = (PFNGLVALIDATEPROGRAMPIPELINEPROC) load(userptr, "glValidateProgramPipeline"); } static void glad_gl_load_GL_ARB_shader_atomic_counters( GLADuserptrloadfunc load, void* userptr) { if(!GLAD_GL_ARB_shader_atomic_counters) return; glad_glGetActiveAtomicCounterBufferiv = (PFNGLGETACTIVEATOMICCOUNTERBUFFERIVPROC) load(userptr, "glGetActiveAtomicCounterBufferiv"); } static void glad_gl_load_GL_ARB_shader_image_load_store( GLADuserptrloadfunc load, void* userptr) { if(!GLAD_GL_ARB_shader_image_load_store) return; glad_glBindImageTexture = (PFNGLBINDIMAGETEXTUREPROC) load(userptr, "glBindImageTexture"); glad_glMemoryBarrier = (PFNGLMEMORYBARRIERPROC) load(userptr, "glMemoryBarrier"); } static void glad_gl_load_GL_ARB_shader_objects( GLADuserptrloadfunc load, void* userptr) { if(!GLAD_GL_ARB_shader_objects) return; glad_glAttachObjectARB = (PFNGLATTACHOBJECTARBPROC) load(userptr, "glAttachObjectARB"); glad_glAttachShader = (PFNGLATTACHSHADERPROC) load(userptr, "glAttachShader"); glad_glCompileShader = (PFNGLCOMPILESHADERPROC) load(userptr, "glCompileShader"); glad_glCompileShaderARB = (PFNGLCOMPILESHADERARBPROC) load(userptr, "glCompileShaderARB"); glad_glCreateProgram = (PFNGLCREATEPROGRAMPROC) load(userptr, "glCreateProgram"); glad_glCreateProgramObjectARB = (PFNGLCREATEPROGRAMOBJECTARBPROC) load(userptr, "glCreateProgramObjectARB"); glad_glCreateShader = (PFNGLCREATESHADERPROC) load(userptr, "glCreateShader"); glad_glCreateShaderObjectARB = (PFNGLCREATESHADEROBJECTARBPROC) load(userptr, "glCreateShaderObjectARB"); glad_glDeleteObjectARB = (PFNGLDELETEOBJECTARBPROC) load(userptr, "glDeleteObjectARB"); glad_glDetachObjectARB = (PFNGLDETACHOBJECTARBPROC) load(userptr, "glDetachObjectARB"); glad_glDetachShader = (PFNGLDETACHSHADERPROC) load(userptr, "glDetachShader"); glad_glGetActiveUniform = (PFNGLGETACTIVEUNIFORMPROC) load(userptr, "glGetActiveUniform"); glad_glGetActiveUniformARB = (PFNGLGETACTIVEUNIFORMARBPROC) load(userptr, "glGetActiveUniformARB"); glad_glGetAttachedObjectsARB = (PFNGLGETATTACHEDOBJECTSARBPROC) load(userptr, "glGetAttachedObjectsARB"); glad_glGetHandleARB = (PFNGLGETHANDLEARBPROC) load(userptr, "glGetHandleARB"); glad_glGetInfoLogARB = (PFNGLGETINFOLOGARBPROC) load(userptr, "glGetInfoLogARB"); glad_glGetObjectParameterfvARB = (PFNGLGETOBJECTPARAMETERFVARBPROC) load(userptr, "glGetObjectParameterfvARB"); glad_glGetObjectParameterivARB = (PFNGLGETOBJECTPARAMETERIVARBPROC) load(userptr, "glGetObjectParameterivARB"); glad_glGetShaderSource = (PFNGLGETSHADERSOURCEPROC) load(userptr, "glGetShaderSource"); glad_glGetShaderSourceARB = (PFNGLGETSHADERSOURCEARBPROC) load(userptr, "glGetShaderSourceARB"); glad_glGetUniformLocation = (PFNGLGETUNIFORMLOCATIONPROC) load(userptr, "glGetUniformLocation"); glad_glGetUniformLocationARB = (PFNGLGETUNIFORMLOCATIONARBPROC) load(userptr, "glGetUniformLocationARB"); glad_glGetUniformfv = (PFNGLGETUNIFORMFVPROC) load(userptr, "glGetUniformfv"); glad_glGetUniformfvARB = (PFNGLGETUNIFORMFVARBPROC) load(userptr, "glGetUniformfvARB"); glad_glGetUniformiv = (PFNGLGETUNIFORMIVPROC) load(userptr, "glGetUniformiv"); glad_glGetUniformivARB = (PFNGLGETUNIFORMIVARBPROC) load(userptr, "glGetUniformivARB"); glad_glLinkProgram = (PFNGLLINKPROGRAMPROC) load(userptr, "glLinkProgram"); glad_glLinkProgramARB = (PFNGLLINKPROGRAMARBPROC) load(userptr, "glLinkProgramARB"); glad_glShaderSource = (PFNGLSHADERSOURCEPROC) load(userptr, "glShaderSource"); glad_glShaderSourceARB = (PFNGLSHADERSOURCEARBPROC) load(userptr, "glShaderSourceARB"); glad_glUniform1f = (PFNGLUNIFORM1FPROC) load(userptr, "glUniform1f"); glad_glUniform1fARB = (PFNGLUNIFORM1FARBPROC) load(userptr, "glUniform1fARB"); glad_glUniform1fv = (PFNGLUNIFORM1FVPROC) load(userptr, "glUniform1fv"); glad_glUniform1fvARB = (PFNGLUNIFORM1FVARBPROC) load(userptr, "glUniform1fvARB"); glad_glUniform1i = (PFNGLUNIFORM1IPROC) load(userptr, "glUniform1i"); glad_glUniform1iARB = (PFNGLUNIFORM1IARBPROC) load(userptr, "glUniform1iARB"); glad_glUniform1iv = (PFNGLUNIFORM1IVPROC) load(userptr, "glUniform1iv"); glad_glUniform1ivARB = (PFNGLUNIFORM1IVARBPROC) load(userptr, "glUniform1ivARB"); glad_glUniform2f = (PFNGLUNIFORM2FPROC) load(userptr, "glUniform2f"); glad_glUniform2fARB = (PFNGLUNIFORM2FARBPROC) load(userptr, "glUniform2fARB"); glad_glUniform2fv = (PFNGLUNIFORM2FVPROC) load(userptr, "glUniform2fv"); glad_glUniform2fvARB = (PFNGLUNIFORM2FVARBPROC) load(userptr, "glUniform2fvARB"); glad_glUniform2i = (PFNGLUNIFORM2IPROC) load(userptr, "glUniform2i"); glad_glUniform2iARB = (PFNGLUNIFORM2IARBPROC) load(userptr, "glUniform2iARB"); glad_glUniform2iv = (PFNGLUNIFORM2IVPROC) load(userptr, "glUniform2iv"); glad_glUniform2ivARB = (PFNGLUNIFORM2IVARBPROC) load(userptr, "glUniform2ivARB"); glad_glUniform3f = (PFNGLUNIFORM3FPROC) load(userptr, "glUniform3f"); glad_glUniform3fARB = (PFNGLUNIFORM3FARBPROC) load(userptr, "glUniform3fARB"); glad_glUniform3fv = (PFNGLUNIFORM3FVPROC) load(userptr, "glUniform3fv"); glad_glUniform3fvARB = (PFNGLUNIFORM3FVARBPROC) load(userptr, "glUniform3fvARB"); glad_glUniform3i = (PFNGLUNIFORM3IPROC) load(userptr, "glUniform3i"); glad_glUniform3iARB = (PFNGLUNIFORM3IARBPROC) load(userptr, "glUniform3iARB"); glad_glUniform3iv = (PFNGLUNIFORM3IVPROC) load(userptr, "glUniform3iv"); glad_glUniform3ivARB = (PFNGLUNIFORM3IVARBPROC) load(userptr, "glUniform3ivARB"); glad_glUniform4f = (PFNGLUNIFORM4FPROC) load(userptr, "glUniform4f"); glad_glUniform4fARB = (PFNGLUNIFORM4FARBPROC) load(userptr, "glUniform4fARB"); glad_glUniform4fv = (PFNGLUNIFORM4FVPROC) load(userptr, "glUniform4fv"); glad_glUniform4fvARB = (PFNGLUNIFORM4FVARBPROC) load(userptr, "glUniform4fvARB"); glad_glUniform4i = (PFNGLUNIFORM4IPROC) load(userptr, "glUniform4i"); glad_glUniform4iARB = (PFNGLUNIFORM4IARBPROC) load(userptr, "glUniform4iARB"); glad_glUniform4iv = (PFNGLUNIFORM4IVPROC) load(userptr, "glUniform4iv"); glad_glUniform4ivARB = (PFNGLUNIFORM4IVARBPROC) load(userptr, "glUniform4ivARB"); glad_glUniformMatrix2fv = (PFNGLUNIFORMMATRIX2FVPROC) load(userptr, "glUniformMatrix2fv"); glad_glUniformMatrix2fvARB = (PFNGLUNIFORMMATRIX2FVARBPROC) load(userptr, "glUniformMatrix2fvARB"); glad_glUniformMatrix3fv = (PFNGLUNIFORMMATRIX3FVPROC) load(userptr, "glUniformMatrix3fv"); glad_glUniformMatrix3fvARB = (PFNGLUNIFORMMATRIX3FVARBPROC) load(userptr, "glUniformMatrix3fvARB"); glad_glUniformMatrix4fv = (PFNGLUNIFORMMATRIX4FVPROC) load(userptr, "glUniformMatrix4fv"); glad_glUniformMatrix4fvARB = (PFNGLUNIFORMMATRIX4FVARBPROC) load(userptr, "glUniformMatrix4fvARB"); glad_glUseProgram = (PFNGLUSEPROGRAMPROC) load(userptr, "glUseProgram"); glad_glUseProgramObjectARB = (PFNGLUSEPROGRAMOBJECTARBPROC) load(userptr, "glUseProgramObjectARB"); glad_glValidateProgram = (PFNGLVALIDATEPROGRAMPROC) load(userptr, "glValidateProgram"); glad_glValidateProgramARB = (PFNGLVALIDATEPROGRAMARBPROC) load(userptr, "glValidateProgramARB"); } static void glad_gl_load_GL_ARB_shader_storage_buffer_object( GLADuserptrloadfunc load, void* userptr) { if(!GLAD_GL_ARB_shader_storage_buffer_object) return; glad_glShaderStorageBlockBinding = (PFNGLSHADERSTORAGEBLOCKBINDINGPROC) load(userptr, "glShaderStorageBlockBinding"); } static void glad_gl_load_GL_ARB_shader_subroutine( GLADuserptrloadfunc load, void* userptr) { if(!GLAD_GL_ARB_shader_subroutine) return; glad_glGetActiveSubroutineName = (PFNGLGETACTIVESUBROUTINENAMEPROC) load(userptr, "glGetActiveSubroutineName"); glad_glGetActiveSubroutineUniformName = (PFNGLGETACTIVESUBROUTINEUNIFORMNAMEPROC) load(userptr, "glGetActiveSubroutineUniformName"); glad_glGetActiveSubroutineUniformiv = (PFNGLGETACTIVESUBROUTINEUNIFORMIVPROC) load(userptr, "glGetActiveSubroutineUniformiv"); glad_glGetProgramStageiv = (PFNGLGETPROGRAMSTAGEIVPROC) load(userptr, "glGetProgramStageiv"); glad_glGetSubroutineIndex = (PFNGLGETSUBROUTINEINDEXPROC) load(userptr, "glGetSubroutineIndex"); glad_glGetSubroutineUniformLocation = (PFNGLGETSUBROUTINEUNIFORMLOCATIONPROC) load(userptr, "glGetSubroutineUniformLocation"); glad_glGetUniformSubroutineuiv = (PFNGLGETUNIFORMSUBROUTINEUIVPROC) load(userptr, "glGetUniformSubroutineuiv"); glad_glUniformSubroutinesuiv = (PFNGLUNIFORMSUBROUTINESUIVPROC) load(userptr, "glUniformSubroutinesuiv"); } static void glad_gl_load_GL_ARB_shading_language_include( GLADuserptrloadfunc load, void* userptr) { if(!GLAD_GL_ARB_shading_language_include) return; glad_glCompileShaderIncludeARB = (PFNGLCOMPILESHADERINCLUDEARBPROC) load(userptr, "glCompileShaderIncludeARB"); glad_glDeleteNamedStringARB = (PFNGLDELETENAMEDSTRINGARBPROC) load(userptr, "glDeleteNamedStringARB"); glad_glGetNamedStringARB = (PFNGLGETNAMEDSTRINGARBPROC) load(userptr, "glGetNamedStringARB"); glad_glGetNamedStringivARB = (PFNGLGETNAMEDSTRINGIVARBPROC) load(userptr, "glGetNamedStringivARB"); glad_glIsNamedStringARB = (PFNGLISNAMEDSTRINGARBPROC) load(userptr, "glIsNamedStringARB"); glad_glNamedStringARB = (PFNGLNAMEDSTRINGARBPROC) load(userptr, "glNamedStringARB"); } static void glad_gl_load_GL_ARB_sparse_buffer( GLADuserptrloadfunc load, void* userptr) { if(!GLAD_GL_ARB_sparse_buffer) return; glad_glBufferPageCommitmentARB = (PFNGLBUFFERPAGECOMMITMENTARBPROC) load(userptr, "glBufferPageCommitmentARB"); glad_glNamedBufferPageCommitmentARB = (PFNGLNAMEDBUFFERPAGECOMMITMENTARBPROC) load(userptr, "glNamedBufferPageCommitmentARB"); glad_glNamedBufferPageCommitmentEXT = (PFNGLNAMEDBUFFERPAGECOMMITMENTEXTPROC) load(userptr, "glNamedBufferPageCommitmentEXT"); } static void glad_gl_load_GL_ARB_sparse_texture( GLADuserptrloadfunc load, void* userptr) { if(!GLAD_GL_ARB_sparse_texture) return; glad_glTexPageCommitmentARB = (PFNGLTEXPAGECOMMITMENTARBPROC) load(userptr, "glTexPageCommitmentARB"); } static void glad_gl_load_GL_ARB_sync( GLADuserptrloadfunc load, void* userptr) { if(!GLAD_GL_ARB_sync) return; glad_glClientWaitSync = (PFNGLCLIENTWAITSYNCPROC) load(userptr, "glClientWaitSync"); glad_glDeleteSync = (PFNGLDELETESYNCPROC) load(userptr, "glDeleteSync"); glad_glFenceSync = (PFNGLFENCESYNCPROC) load(userptr, "glFenceSync"); glad_glGetInteger64v = (PFNGLGETINTEGER64VPROC) load(userptr, "glGetInteger64v"); glad_glGetSynciv = (PFNGLGETSYNCIVPROC) load(userptr, "glGetSynciv"); glad_glIsSync = (PFNGLISSYNCPROC) load(userptr, "glIsSync"); glad_glWaitSync = (PFNGLWAITSYNCPROC) load(userptr, "glWaitSync"); } static void glad_gl_load_GL_ARB_tessellation_shader( GLADuserptrloadfunc load, void* userptr) { if(!GLAD_GL_ARB_tessellation_shader) return; glad_glPatchParameterfv = (PFNGLPATCHPARAMETERFVPROC) load(userptr, "glPatchParameterfv"); glad_glPatchParameteri = (PFNGLPATCHPARAMETERIPROC) load(userptr, "glPatchParameteri"); } static void glad_gl_load_GL_ARB_texture_barrier( GLADuserptrloadfunc load, void* userptr) { if(!GLAD_GL_ARB_texture_barrier) return; glad_glTextureBarrier = (PFNGLTEXTUREBARRIERPROC) load(userptr, "glTextureBarrier"); } static void glad_gl_load_GL_ARB_texture_buffer_object( GLADuserptrloadfunc load, void* userptr) { if(!GLAD_GL_ARB_texture_buffer_object) return; glad_glTexBuffer = (PFNGLTEXBUFFERPROC) load(userptr, "glTexBuffer"); glad_glTexBufferARB = (PFNGLTEXBUFFERARBPROC) load(userptr, "glTexBufferARB"); } static void glad_gl_load_GL_ARB_texture_buffer_range( GLADuserptrloadfunc load, void* userptr) { if(!GLAD_GL_ARB_texture_buffer_range) return; glad_glTexBufferRange = (PFNGLTEXBUFFERRANGEPROC) load(userptr, "glTexBufferRange"); } static void glad_gl_load_GL_ARB_texture_compression( GLADuserptrloadfunc load, void* userptr) { if(!GLAD_GL_ARB_texture_compression) return; glad_glCompressedTexImage1D = (PFNGLCOMPRESSEDTEXIMAGE1DPROC) load(userptr, "glCompressedTexImage1D"); glad_glCompressedTexImage1DARB = (PFNGLCOMPRESSEDTEXIMAGE1DARBPROC) load(userptr, "glCompressedTexImage1DARB"); glad_glCompressedTexImage2D = (PFNGLCOMPRESSEDTEXIMAGE2DPROC) load(userptr, "glCompressedTexImage2D"); glad_glCompressedTexImage2DARB = (PFNGLCOMPRESSEDTEXIMAGE2DARBPROC) load(userptr, "glCompressedTexImage2DARB"); glad_glCompressedTexImage3D = (PFNGLCOMPRESSEDTEXIMAGE3DPROC) load(userptr, "glCompressedTexImage3D"); glad_glCompressedTexImage3DARB = (PFNGLCOMPRESSEDTEXIMAGE3DARBPROC) load(userptr, "glCompressedTexImage3DARB"); glad_glCompressedTexSubImage1D = (PFNGLCOMPRESSEDTEXSUBIMAGE1DPROC) load(userptr, "glCompressedTexSubImage1D"); glad_glCompressedTexSubImage1DARB = (PFNGLCOMPRESSEDTEXSUBIMAGE1DARBPROC) load(userptr, "glCompressedTexSubImage1DARB"); glad_glCompressedTexSubImage2D = (PFNGLCOMPRESSEDTEXSUBIMAGE2DPROC) load(userptr, "glCompressedTexSubImage2D"); glad_glCompressedTexSubImage2DARB = (PFNGLCOMPRESSEDTEXSUBIMAGE2DARBPROC) load(userptr, "glCompressedTexSubImage2DARB"); glad_glCompressedTexSubImage3D = (PFNGLCOMPRESSEDTEXSUBIMAGE3DPROC) load(userptr, "glCompressedTexSubImage3D"); glad_glCompressedTexSubImage3DARB = (PFNGLCOMPRESSEDTEXSUBIMAGE3DARBPROC) load(userptr, "glCompressedTexSubImage3DARB"); glad_glGetCompressedTexImage = (PFNGLGETCOMPRESSEDTEXIMAGEPROC) load(userptr, "glGetCompressedTexImage"); glad_glGetCompressedTexImageARB = (PFNGLGETCOMPRESSEDTEXIMAGEARBPROC) load(userptr, "glGetCompressedTexImageARB"); } static void glad_gl_load_GL_ARB_texture_multisample( GLADuserptrloadfunc load, void* userptr) { if(!GLAD_GL_ARB_texture_multisample) return; glad_glGetMultisamplefv = (PFNGLGETMULTISAMPLEFVPROC) load(userptr, "glGetMultisamplefv"); glad_glSampleMaski = (PFNGLSAMPLEMASKIPROC) load(userptr, "glSampleMaski"); glad_glTexImage2DMultisample = (PFNGLTEXIMAGE2DMULTISAMPLEPROC) load(userptr, "glTexImage2DMultisample"); glad_glTexImage3DMultisample = (PFNGLTEXIMAGE3DMULTISAMPLEPROC) load(userptr, "glTexImage3DMultisample"); } static void glad_gl_load_GL_ARB_texture_storage( GLADuserptrloadfunc load, void* userptr) { if(!GLAD_GL_ARB_texture_storage) return; glad_glTexStorage1D = (PFNGLTEXSTORAGE1DPROC) load(userptr, "glTexStorage1D"); glad_glTexStorage2D = (PFNGLTEXSTORAGE2DPROC) load(userptr, "glTexStorage2D"); glad_glTexStorage3D = (PFNGLTEXSTORAGE3DPROC) load(userptr, "glTexStorage3D"); } static void glad_gl_load_GL_ARB_texture_storage_multisample( GLADuserptrloadfunc load, void* userptr) { if(!GLAD_GL_ARB_texture_storage_multisample) return; glad_glTexStorage2DMultisample = (PFNGLTEXSTORAGE2DMULTISAMPLEPROC) load(userptr, "glTexStorage2DMultisample"); glad_glTexStorage3DMultisample = (PFNGLTEXSTORAGE3DMULTISAMPLEPROC) load(userptr, "glTexStorage3DMultisample"); } static void glad_gl_load_GL_ARB_texture_view( GLADuserptrloadfunc load, void* userptr) { if(!GLAD_GL_ARB_texture_view) return; glad_glTextureView = (PFNGLTEXTUREVIEWPROC) load(userptr, "glTextureView"); } static void glad_gl_load_GL_ARB_timer_query( GLADuserptrloadfunc load, void* userptr) { if(!GLAD_GL_ARB_timer_query) return; glad_glGetQueryObjecti64v = (PFNGLGETQUERYOBJECTI64VPROC) load(userptr, "glGetQueryObjecti64v"); glad_glGetQueryObjectui64v = (PFNGLGETQUERYOBJECTUI64VPROC) load(userptr, "glGetQueryObjectui64v"); glad_glQueryCounter = (PFNGLQUERYCOUNTERPROC) load(userptr, "glQueryCounter"); } static void glad_gl_load_GL_ARB_transform_feedback2( GLADuserptrloadfunc load, void* userptr) { if(!GLAD_GL_ARB_transform_feedback2) return; glad_glBindTransformFeedback = (PFNGLBINDTRANSFORMFEEDBACKPROC) load(userptr, "glBindTransformFeedback"); glad_glDeleteTransformFeedbacks = (PFNGLDELETETRANSFORMFEEDBACKSPROC) load(userptr, "glDeleteTransformFeedbacks"); glad_glDrawTransformFeedback = (PFNGLDRAWTRANSFORMFEEDBACKPROC) load(userptr, "glDrawTransformFeedback"); glad_glGenTransformFeedbacks = (PFNGLGENTRANSFORMFEEDBACKSPROC) load(userptr, "glGenTransformFeedbacks"); glad_glIsTransformFeedback = (PFNGLISTRANSFORMFEEDBACKPROC) load(userptr, "glIsTransformFeedback"); glad_glPauseTransformFeedback = (PFNGLPAUSETRANSFORMFEEDBACKPROC) load(userptr, "glPauseTransformFeedback"); glad_glResumeTransformFeedback = (PFNGLRESUMETRANSFORMFEEDBACKPROC) load(userptr, "glResumeTransformFeedback"); } static void glad_gl_load_GL_ARB_transform_feedback3( GLADuserptrloadfunc load, void* userptr) { if(!GLAD_GL_ARB_transform_feedback3) return; glad_glBeginQueryIndexed = (PFNGLBEGINQUERYINDEXEDPROC) load(userptr, "glBeginQueryIndexed"); glad_glDrawTransformFeedbackStream = (PFNGLDRAWTRANSFORMFEEDBACKSTREAMPROC) load(userptr, "glDrawTransformFeedbackStream"); glad_glEndQueryIndexed = (PFNGLENDQUERYINDEXEDPROC) load(userptr, "glEndQueryIndexed"); glad_glGetQueryIndexediv = (PFNGLGETQUERYINDEXEDIVPROC) load(userptr, "glGetQueryIndexediv"); } static void glad_gl_load_GL_ARB_transform_feedback_instanced( GLADuserptrloadfunc load, void* userptr) { if(!GLAD_GL_ARB_transform_feedback_instanced) return; glad_glDrawTransformFeedbackInstanced = (PFNGLDRAWTRANSFORMFEEDBACKINSTANCEDPROC) load(userptr, "glDrawTransformFeedbackInstanced"); glad_glDrawTransformFeedbackStreamInstanced = (PFNGLDRAWTRANSFORMFEEDBACKSTREAMINSTANCEDPROC) load(userptr, "glDrawTransformFeedbackStreamInstanced"); } static void glad_gl_load_GL_ARB_transpose_matrix( GLADuserptrloadfunc load, void* userptr) { if(!GLAD_GL_ARB_transpose_matrix) return; glad_glLoadTransposeMatrixd = (PFNGLLOADTRANSPOSEMATRIXDPROC) load(userptr, "glLoadTransposeMatrixd"); glad_glLoadTransposeMatrixdARB = (PFNGLLOADTRANSPOSEMATRIXDARBPROC) load(userptr, "glLoadTransposeMatrixdARB"); glad_glLoadTransposeMatrixf = (PFNGLLOADTRANSPOSEMATRIXFPROC) load(userptr, "glLoadTransposeMatrixf"); glad_glLoadTransposeMatrixfARB = (PFNGLLOADTRANSPOSEMATRIXFARBPROC) load(userptr, "glLoadTransposeMatrixfARB"); glad_glMultTransposeMatrixd = (PFNGLMULTTRANSPOSEMATRIXDPROC) load(userptr, "glMultTransposeMatrixd"); glad_glMultTransposeMatrixdARB = (PFNGLMULTTRANSPOSEMATRIXDARBPROC) load(userptr, "glMultTransposeMatrixdARB"); glad_glMultTransposeMatrixf = (PFNGLMULTTRANSPOSEMATRIXFPROC) load(userptr, "glMultTransposeMatrixf"); glad_glMultTransposeMatrixfARB = (PFNGLMULTTRANSPOSEMATRIXFARBPROC) load(userptr, "glMultTransposeMatrixfARB"); } static void glad_gl_load_GL_ARB_uniform_buffer_object( GLADuserptrloadfunc load, void* userptr) { if(!GLAD_GL_ARB_uniform_buffer_object) return; glad_glBindBufferBase = (PFNGLBINDBUFFERBASEPROC) load(userptr, "glBindBufferBase"); glad_glBindBufferRange = (PFNGLBINDBUFFERRANGEPROC) load(userptr, "glBindBufferRange"); glad_glGetActiveUniformBlockName = (PFNGLGETACTIVEUNIFORMBLOCKNAMEPROC) load(userptr, "glGetActiveUniformBlockName"); glad_glGetActiveUniformBlockiv = (PFNGLGETACTIVEUNIFORMBLOCKIVPROC) load(userptr, "glGetActiveUniformBlockiv"); glad_glGetActiveUniformName = (PFNGLGETACTIVEUNIFORMNAMEPROC) load(userptr, "glGetActiveUniformName"); glad_glGetActiveUniformsiv = (PFNGLGETACTIVEUNIFORMSIVPROC) load(userptr, "glGetActiveUniformsiv"); glad_glGetIntegeri_v = (PFNGLGETINTEGERI_VPROC) load(userptr, "glGetIntegeri_v"); glad_glGetUniformBlockIndex = (PFNGLGETUNIFORMBLOCKINDEXPROC) load(userptr, "glGetUniformBlockIndex"); glad_glGetUniformIndices = (PFNGLGETUNIFORMINDICESPROC) load(userptr, "glGetUniformIndices"); glad_glUniformBlockBinding = (PFNGLUNIFORMBLOCKBINDINGPROC) load(userptr, "glUniformBlockBinding"); } static void glad_gl_load_GL_ARB_vertex_array_object( GLADuserptrloadfunc load, void* userptr) { if(!GLAD_GL_ARB_vertex_array_object) return; glad_glBindVertexArray = (PFNGLBINDVERTEXARRAYPROC) load(userptr, "glBindVertexArray"); glad_glDeleteVertexArrays = (PFNGLDELETEVERTEXARRAYSPROC) load(userptr, "glDeleteVertexArrays"); glad_glGenVertexArrays = (PFNGLGENVERTEXARRAYSPROC) load(userptr, "glGenVertexArrays"); glad_glIsVertexArray = (PFNGLISVERTEXARRAYPROC) load(userptr, "glIsVertexArray"); } static void glad_gl_load_GL_ARB_vertex_attrib_64bit( GLADuserptrloadfunc load, void* userptr) { if(!GLAD_GL_ARB_vertex_attrib_64bit) return; glad_glGetVertexAttribLdv = (PFNGLGETVERTEXATTRIBLDVPROC) load(userptr, "glGetVertexAttribLdv"); glad_glVertexAttribL1d = (PFNGLVERTEXATTRIBL1DPROC) load(userptr, "glVertexAttribL1d"); glad_glVertexAttribL1dv = (PFNGLVERTEXATTRIBL1DVPROC) load(userptr, "glVertexAttribL1dv"); glad_glVertexAttribL2d = (PFNGLVERTEXATTRIBL2DPROC) load(userptr, "glVertexAttribL2d"); glad_glVertexAttribL2dv = (PFNGLVERTEXATTRIBL2DVPROC) load(userptr, "glVertexAttribL2dv"); glad_glVertexAttribL3d = (PFNGLVERTEXATTRIBL3DPROC) load(userptr, "glVertexAttribL3d"); glad_glVertexAttribL3dv = (PFNGLVERTEXATTRIBL3DVPROC) load(userptr, "glVertexAttribL3dv"); glad_glVertexAttribL4d = (PFNGLVERTEXATTRIBL4DPROC) load(userptr, "glVertexAttribL4d"); glad_glVertexAttribL4dv = (PFNGLVERTEXATTRIBL4DVPROC) load(userptr, "glVertexAttribL4dv"); glad_glVertexAttribLPointer = (PFNGLVERTEXATTRIBLPOINTERPROC) load(userptr, "glVertexAttribLPointer"); } static void glad_gl_load_GL_ARB_vertex_attrib_binding( GLADuserptrloadfunc load, void* userptr) { if(!GLAD_GL_ARB_vertex_attrib_binding) return; glad_glBindVertexBuffer = (PFNGLBINDVERTEXBUFFERPROC) load(userptr, "glBindVertexBuffer"); glad_glVertexAttribBinding = (PFNGLVERTEXATTRIBBINDINGPROC) load(userptr, "glVertexAttribBinding"); glad_glVertexAttribFormat = (PFNGLVERTEXATTRIBFORMATPROC) load(userptr, "glVertexAttribFormat"); glad_glVertexAttribIFormat = (PFNGLVERTEXATTRIBIFORMATPROC) load(userptr, "glVertexAttribIFormat"); glad_glVertexAttribLFormat = (PFNGLVERTEXATTRIBLFORMATPROC) load(userptr, "glVertexAttribLFormat"); glad_glVertexBindingDivisor = (PFNGLVERTEXBINDINGDIVISORPROC) load(userptr, "glVertexBindingDivisor"); } static void glad_gl_load_GL_ARB_vertex_blend( GLADuserptrloadfunc load, void* userptr) { if(!GLAD_GL_ARB_vertex_blend) return; glad_glVertexBlendARB = (PFNGLVERTEXBLENDARBPROC) load(userptr, "glVertexBlendARB"); glad_glWeightPointerARB = (PFNGLWEIGHTPOINTERARBPROC) load(userptr, "glWeightPointerARB"); glad_glWeightbvARB = (PFNGLWEIGHTBVARBPROC) load(userptr, "glWeightbvARB"); glad_glWeightdvARB = (PFNGLWEIGHTDVARBPROC) load(userptr, "glWeightdvARB"); glad_glWeightfvARB = (PFNGLWEIGHTFVARBPROC) load(userptr, "glWeightfvARB"); glad_glWeightivARB = (PFNGLWEIGHTIVARBPROC) load(userptr, "glWeightivARB"); glad_glWeightsvARB = (PFNGLWEIGHTSVARBPROC) load(userptr, "glWeightsvARB"); glad_glWeightubvARB = (PFNGLWEIGHTUBVARBPROC) load(userptr, "glWeightubvARB"); glad_glWeightuivARB = (PFNGLWEIGHTUIVARBPROC) load(userptr, "glWeightuivARB"); glad_glWeightusvARB = (PFNGLWEIGHTUSVARBPROC) load(userptr, "glWeightusvARB"); } static void glad_gl_load_GL_ARB_vertex_buffer_object( GLADuserptrloadfunc load, void* userptr) { if(!GLAD_GL_ARB_vertex_buffer_object) return; glad_glBindBuffer = (PFNGLBINDBUFFERPROC) load(userptr, "glBindBuffer"); glad_glBindBufferARB = (PFNGLBINDBUFFERARBPROC) load(userptr, "glBindBufferARB"); glad_glBufferData = (PFNGLBUFFERDATAPROC) load(userptr, "glBufferData"); glad_glBufferDataARB = (PFNGLBUFFERDATAARBPROC) load(userptr, "glBufferDataARB"); glad_glBufferSubData = (PFNGLBUFFERSUBDATAPROC) load(userptr, "glBufferSubData"); glad_glBufferSubDataARB = (PFNGLBUFFERSUBDATAARBPROC) load(userptr, "glBufferSubDataARB"); glad_glDeleteBuffers = (PFNGLDELETEBUFFERSPROC) load(userptr, "glDeleteBuffers"); glad_glDeleteBuffersARB = (PFNGLDELETEBUFFERSARBPROC) load(userptr, "glDeleteBuffersARB"); glad_glGenBuffers = (PFNGLGENBUFFERSPROC) load(userptr, "glGenBuffers"); glad_glGenBuffersARB = (PFNGLGENBUFFERSARBPROC) load(userptr, "glGenBuffersARB"); glad_glGetBufferParameteriv = (PFNGLGETBUFFERPARAMETERIVPROC) load(userptr, "glGetBufferParameteriv"); glad_glGetBufferParameterivARB = (PFNGLGETBUFFERPARAMETERIVARBPROC) load(userptr, "glGetBufferParameterivARB"); glad_glGetBufferPointerv = (PFNGLGETBUFFERPOINTERVPROC) load(userptr, "glGetBufferPointerv"); glad_glGetBufferPointervARB = (PFNGLGETBUFFERPOINTERVARBPROC) load(userptr, "glGetBufferPointervARB"); glad_glGetBufferSubData = (PFNGLGETBUFFERSUBDATAPROC) load(userptr, "glGetBufferSubData"); glad_glGetBufferSubDataARB = (PFNGLGETBUFFERSUBDATAARBPROC) load(userptr, "glGetBufferSubDataARB"); glad_glIsBuffer = (PFNGLISBUFFERPROC) load(userptr, "glIsBuffer"); glad_glIsBufferARB = (PFNGLISBUFFERARBPROC) load(userptr, "glIsBufferARB"); glad_glMapBuffer = (PFNGLMAPBUFFERPROC) load(userptr, "glMapBuffer"); glad_glMapBufferARB = (PFNGLMAPBUFFERARBPROC) load(userptr, "glMapBufferARB"); glad_glUnmapBuffer = (PFNGLUNMAPBUFFERPROC) load(userptr, "glUnmapBuffer"); glad_glUnmapBufferARB = (PFNGLUNMAPBUFFERARBPROC) load(userptr, "glUnmapBufferARB"); } static void glad_gl_load_GL_ARB_vertex_program( GLADuserptrloadfunc load, void* userptr) { if(!GLAD_GL_ARB_vertex_program) return; glad_glBindProgramARB = (PFNGLBINDPROGRAMARBPROC) load(userptr, "glBindProgramARB"); glad_glDeleteProgramsARB = (PFNGLDELETEPROGRAMSARBPROC) load(userptr, "glDeleteProgramsARB"); glad_glDisableVertexAttribArray = (PFNGLDISABLEVERTEXATTRIBARRAYPROC) load(userptr, "glDisableVertexAttribArray"); glad_glDisableVertexAttribArrayARB = (PFNGLDISABLEVERTEXATTRIBARRAYARBPROC) load(userptr, "glDisableVertexAttribArrayARB"); glad_glEnableVertexAttribArray = (PFNGLENABLEVERTEXATTRIBARRAYPROC) load(userptr, "glEnableVertexAttribArray"); glad_glEnableVertexAttribArrayARB = (PFNGLENABLEVERTEXATTRIBARRAYARBPROC) load(userptr, "glEnableVertexAttribArrayARB"); glad_glGenProgramsARB = (PFNGLGENPROGRAMSARBPROC) load(userptr, "glGenProgramsARB"); glad_glGetProgramEnvParameterdvARB = (PFNGLGETPROGRAMENVPARAMETERDVARBPROC) load(userptr, "glGetProgramEnvParameterdvARB"); glad_glGetProgramEnvParameterfvARB = (PFNGLGETPROGRAMENVPARAMETERFVARBPROC) load(userptr, "glGetProgramEnvParameterfvARB"); glad_glGetProgramLocalParameterdvARB = (PFNGLGETPROGRAMLOCALPARAMETERDVARBPROC) load(userptr, "glGetProgramLocalParameterdvARB"); glad_glGetProgramLocalParameterfvARB = (PFNGLGETPROGRAMLOCALPARAMETERFVARBPROC) load(userptr, "glGetProgramLocalParameterfvARB"); glad_glGetProgramStringARB = (PFNGLGETPROGRAMSTRINGARBPROC) load(userptr, "glGetProgramStringARB"); glad_glGetProgramivARB = (PFNGLGETPROGRAMIVARBPROC) load(userptr, "glGetProgramivARB"); glad_glGetVertexAttribPointerv = (PFNGLGETVERTEXATTRIBPOINTERVPROC) load(userptr, "glGetVertexAttribPointerv"); glad_glGetVertexAttribPointervARB = (PFNGLGETVERTEXATTRIBPOINTERVARBPROC) load(userptr, "glGetVertexAttribPointervARB"); glad_glGetVertexAttribdv = (PFNGLGETVERTEXATTRIBDVPROC) load(userptr, "glGetVertexAttribdv"); glad_glGetVertexAttribdvARB = (PFNGLGETVERTEXATTRIBDVARBPROC) load(userptr, "glGetVertexAttribdvARB"); glad_glGetVertexAttribfv = (PFNGLGETVERTEXATTRIBFVPROC) load(userptr, "glGetVertexAttribfv"); glad_glGetVertexAttribfvARB = (PFNGLGETVERTEXATTRIBFVARBPROC) load(userptr, "glGetVertexAttribfvARB"); glad_glGetVertexAttribiv = (PFNGLGETVERTEXATTRIBIVPROC) load(userptr, "glGetVertexAttribiv"); glad_glGetVertexAttribivARB = (PFNGLGETVERTEXATTRIBIVARBPROC) load(userptr, "glGetVertexAttribivARB"); glad_glIsProgramARB = (PFNGLISPROGRAMARBPROC) load(userptr, "glIsProgramARB"); glad_glProgramEnvParameter4dARB = (PFNGLPROGRAMENVPARAMETER4DARBPROC) load(userptr, "glProgramEnvParameter4dARB"); glad_glProgramEnvParameter4dvARB = (PFNGLPROGRAMENVPARAMETER4DVARBPROC) load(userptr, "glProgramEnvParameter4dvARB"); glad_glProgramEnvParameter4fARB = (PFNGLPROGRAMENVPARAMETER4FARBPROC) load(userptr, "glProgramEnvParameter4fARB"); glad_glProgramEnvParameter4fvARB = (PFNGLPROGRAMENVPARAMETER4FVARBPROC) load(userptr, "glProgramEnvParameter4fvARB"); glad_glProgramLocalParameter4dARB = (PFNGLPROGRAMLOCALPARAMETER4DARBPROC) load(userptr, "glProgramLocalParameter4dARB"); glad_glProgramLocalParameter4dvARB = (PFNGLPROGRAMLOCALPARAMETER4DVARBPROC) load(userptr, "glProgramLocalParameter4dvARB"); glad_glProgramLocalParameter4fARB = (PFNGLPROGRAMLOCALPARAMETER4FARBPROC) load(userptr, "glProgramLocalParameter4fARB"); glad_glProgramLocalParameter4fvARB = (PFNGLPROGRAMLOCALPARAMETER4FVARBPROC) load(userptr, "glProgramLocalParameter4fvARB"); glad_glProgramStringARB = (PFNGLPROGRAMSTRINGARBPROC) load(userptr, "glProgramStringARB"); glad_glVertexAttrib1d = (PFNGLVERTEXATTRIB1DPROC) load(userptr, "glVertexAttrib1d"); glad_glVertexAttrib1dARB = (PFNGLVERTEXATTRIB1DARBPROC) load(userptr, "glVertexAttrib1dARB"); glad_glVertexAttrib1dv = (PFNGLVERTEXATTRIB1DVPROC) load(userptr, "glVertexAttrib1dv"); glad_glVertexAttrib1dvARB = (PFNGLVERTEXATTRIB1DVARBPROC) load(userptr, "glVertexAttrib1dvARB"); glad_glVertexAttrib1f = (PFNGLVERTEXATTRIB1FPROC) load(userptr, "glVertexAttrib1f"); glad_glVertexAttrib1fARB = (PFNGLVERTEXATTRIB1FARBPROC) load(userptr, "glVertexAttrib1fARB"); glad_glVertexAttrib1fv = (PFNGLVERTEXATTRIB1FVPROC) load(userptr, "glVertexAttrib1fv"); glad_glVertexAttrib1fvARB = (PFNGLVERTEXATTRIB1FVARBPROC) load(userptr, "glVertexAttrib1fvARB"); glad_glVertexAttrib1s = (PFNGLVERTEXATTRIB1SPROC) load(userptr, "glVertexAttrib1s"); glad_glVertexAttrib1sARB = (PFNGLVERTEXATTRIB1SARBPROC) load(userptr, "glVertexAttrib1sARB"); glad_glVertexAttrib1sv = (PFNGLVERTEXATTRIB1SVPROC) load(userptr, "glVertexAttrib1sv"); glad_glVertexAttrib1svARB = (PFNGLVERTEXATTRIB1SVARBPROC) load(userptr, "glVertexAttrib1svARB"); glad_glVertexAttrib2d = (PFNGLVERTEXATTRIB2DPROC) load(userptr, "glVertexAttrib2d"); glad_glVertexAttrib2dARB = (PFNGLVERTEXATTRIB2DARBPROC) load(userptr, "glVertexAttrib2dARB"); glad_glVertexAttrib2dv = (PFNGLVERTEXATTRIB2DVPROC) load(userptr, "glVertexAttrib2dv"); glad_glVertexAttrib2dvARB = (PFNGLVERTEXATTRIB2DVARBPROC) load(userptr, "glVertexAttrib2dvARB"); glad_glVertexAttrib2f = (PFNGLVERTEXATTRIB2FPROC) load(userptr, "glVertexAttrib2f"); glad_glVertexAttrib2fARB = (PFNGLVERTEXATTRIB2FARBPROC) load(userptr, "glVertexAttrib2fARB"); glad_glVertexAttrib2fv = (PFNGLVERTEXATTRIB2FVPROC) load(userptr, "glVertexAttrib2fv"); glad_glVertexAttrib2fvARB = (PFNGLVERTEXATTRIB2FVARBPROC) load(userptr, "glVertexAttrib2fvARB"); glad_glVertexAttrib2s = (PFNGLVERTEXATTRIB2SPROC) load(userptr, "glVertexAttrib2s"); glad_glVertexAttrib2sARB = (PFNGLVERTEXATTRIB2SARBPROC) load(userptr, "glVertexAttrib2sARB"); glad_glVertexAttrib2sv = (PFNGLVERTEXATTRIB2SVPROC) load(userptr, "glVertexAttrib2sv"); glad_glVertexAttrib2svARB = (PFNGLVERTEXATTRIB2SVARBPROC) load(userptr, "glVertexAttrib2svARB"); glad_glVertexAttrib3d = (PFNGLVERTEXATTRIB3DPROC) load(userptr, "glVertexAttrib3d"); glad_glVertexAttrib3dARB = (PFNGLVERTEXATTRIB3DARBPROC) load(userptr, "glVertexAttrib3dARB"); glad_glVertexAttrib3dv = (PFNGLVERTEXATTRIB3DVPROC) load(userptr, "glVertexAttrib3dv"); glad_glVertexAttrib3dvARB = (PFNGLVERTEXATTRIB3DVARBPROC) load(userptr, "glVertexAttrib3dvARB"); glad_glVertexAttrib3f = (PFNGLVERTEXATTRIB3FPROC) load(userptr, "glVertexAttrib3f"); glad_glVertexAttrib3fARB = (PFNGLVERTEXATTRIB3FARBPROC) load(userptr, "glVertexAttrib3fARB"); glad_glVertexAttrib3fv = (PFNGLVERTEXATTRIB3FVPROC) load(userptr, "glVertexAttrib3fv"); glad_glVertexAttrib3fvARB = (PFNGLVERTEXATTRIB3FVARBPROC) load(userptr, "glVertexAttrib3fvARB"); glad_glVertexAttrib3s = (PFNGLVERTEXATTRIB3SPROC) load(userptr, "glVertexAttrib3s"); glad_glVertexAttrib3sARB = (PFNGLVERTEXATTRIB3SARBPROC) load(userptr, "glVertexAttrib3sARB"); glad_glVertexAttrib3sv = (PFNGLVERTEXATTRIB3SVPROC) load(userptr, "glVertexAttrib3sv"); glad_glVertexAttrib3svARB = (PFNGLVERTEXATTRIB3SVARBPROC) load(userptr, "glVertexAttrib3svARB"); glad_glVertexAttrib4Nbv = (PFNGLVERTEXATTRIB4NBVPROC) load(userptr, "glVertexAttrib4Nbv"); glad_glVertexAttrib4NbvARB = (PFNGLVERTEXATTRIB4NBVARBPROC) load(userptr, "glVertexAttrib4NbvARB"); glad_glVertexAttrib4Niv = (PFNGLVERTEXATTRIB4NIVPROC) load(userptr, "glVertexAttrib4Niv"); glad_glVertexAttrib4NivARB = (PFNGLVERTEXATTRIB4NIVARBPROC) load(userptr, "glVertexAttrib4NivARB"); glad_glVertexAttrib4Nsv = (PFNGLVERTEXATTRIB4NSVPROC) load(userptr, "glVertexAttrib4Nsv"); glad_glVertexAttrib4NsvARB = (PFNGLVERTEXATTRIB4NSVARBPROC) load(userptr, "glVertexAttrib4NsvARB"); glad_glVertexAttrib4Nub = (PFNGLVERTEXATTRIB4NUBPROC) load(userptr, "glVertexAttrib4Nub"); glad_glVertexAttrib4NubARB = (PFNGLVERTEXATTRIB4NUBARBPROC) load(userptr, "glVertexAttrib4NubARB"); glad_glVertexAttrib4Nubv = (PFNGLVERTEXATTRIB4NUBVPROC) load(userptr, "glVertexAttrib4Nubv"); glad_glVertexAttrib4NubvARB = (PFNGLVERTEXATTRIB4NUBVARBPROC) load(userptr, "glVertexAttrib4NubvARB"); glad_glVertexAttrib4Nuiv = (PFNGLVERTEXATTRIB4NUIVPROC) load(userptr, "glVertexAttrib4Nuiv"); glad_glVertexAttrib4NuivARB = (PFNGLVERTEXATTRIB4NUIVARBPROC) load(userptr, "glVertexAttrib4NuivARB"); glad_glVertexAttrib4Nusv = (PFNGLVERTEXATTRIB4NUSVPROC) load(userptr, "glVertexAttrib4Nusv"); glad_glVertexAttrib4NusvARB = (PFNGLVERTEXATTRIB4NUSVARBPROC) load(userptr, "glVertexAttrib4NusvARB"); glad_glVertexAttrib4bv = (PFNGLVERTEXATTRIB4BVPROC) load(userptr, "glVertexAttrib4bv"); glad_glVertexAttrib4bvARB = (PFNGLVERTEXATTRIB4BVARBPROC) load(userptr, "glVertexAttrib4bvARB"); glad_glVertexAttrib4d = (PFNGLVERTEXATTRIB4DPROC) load(userptr, "glVertexAttrib4d"); glad_glVertexAttrib4dARB = (PFNGLVERTEXATTRIB4DARBPROC) load(userptr, "glVertexAttrib4dARB"); glad_glVertexAttrib4dv = (PFNGLVERTEXATTRIB4DVPROC) load(userptr, "glVertexAttrib4dv"); glad_glVertexAttrib4dvARB = (PFNGLVERTEXATTRIB4DVARBPROC) load(userptr, "glVertexAttrib4dvARB"); glad_glVertexAttrib4f = (PFNGLVERTEXATTRIB4FPROC) load(userptr, "glVertexAttrib4f"); glad_glVertexAttrib4fARB = (PFNGLVERTEXATTRIB4FARBPROC) load(userptr, "glVertexAttrib4fARB"); glad_glVertexAttrib4fv = (PFNGLVERTEXATTRIB4FVPROC) load(userptr, "glVertexAttrib4fv"); glad_glVertexAttrib4fvARB = (PFNGLVERTEXATTRIB4FVARBPROC) load(userptr, "glVertexAttrib4fvARB"); glad_glVertexAttrib4iv = (PFNGLVERTEXATTRIB4IVPROC) load(userptr, "glVertexAttrib4iv"); glad_glVertexAttrib4ivARB = (PFNGLVERTEXATTRIB4IVARBPROC) load(userptr, "glVertexAttrib4ivARB"); glad_glVertexAttrib4s = (PFNGLVERTEXATTRIB4SPROC) load(userptr, "glVertexAttrib4s"); glad_glVertexAttrib4sARB = (PFNGLVERTEXATTRIB4SARBPROC) load(userptr, "glVertexAttrib4sARB"); glad_glVertexAttrib4sv = (PFNGLVERTEXATTRIB4SVPROC) load(userptr, "glVertexAttrib4sv"); glad_glVertexAttrib4svARB = (PFNGLVERTEXATTRIB4SVARBPROC) load(userptr, "glVertexAttrib4svARB"); glad_glVertexAttrib4ubv = (PFNGLVERTEXATTRIB4UBVPROC) load(userptr, "glVertexAttrib4ubv"); glad_glVertexAttrib4ubvARB = (PFNGLVERTEXATTRIB4UBVARBPROC) load(userptr, "glVertexAttrib4ubvARB"); glad_glVertexAttrib4uiv = (PFNGLVERTEXATTRIB4UIVPROC) load(userptr, "glVertexAttrib4uiv"); glad_glVertexAttrib4uivARB = (PFNGLVERTEXATTRIB4UIVARBPROC) load(userptr, "glVertexAttrib4uivARB"); glad_glVertexAttrib4usv = (PFNGLVERTEXATTRIB4USVPROC) load(userptr, "glVertexAttrib4usv"); glad_glVertexAttrib4usvARB = (PFNGLVERTEXATTRIB4USVARBPROC) load(userptr, "glVertexAttrib4usvARB"); glad_glVertexAttribPointer = (PFNGLVERTEXATTRIBPOINTERPROC) load(userptr, "glVertexAttribPointer"); glad_glVertexAttribPointerARB = (PFNGLVERTEXATTRIBPOINTERARBPROC) load(userptr, "glVertexAttribPointerARB"); } static void glad_gl_load_GL_ARB_vertex_shader( GLADuserptrloadfunc load, void* userptr) { if(!GLAD_GL_ARB_vertex_shader) return; glad_glBindAttribLocation = (PFNGLBINDATTRIBLOCATIONPROC) load(userptr, "glBindAttribLocation"); glad_glBindAttribLocationARB = (PFNGLBINDATTRIBLOCATIONARBPROC) load(userptr, "glBindAttribLocationARB"); glad_glDisableVertexAttribArray = (PFNGLDISABLEVERTEXATTRIBARRAYPROC) load(userptr, "glDisableVertexAttribArray"); glad_glDisableVertexAttribArrayARB = (PFNGLDISABLEVERTEXATTRIBARRAYARBPROC) load(userptr, "glDisableVertexAttribArrayARB"); glad_glEnableVertexAttribArray = (PFNGLENABLEVERTEXATTRIBARRAYPROC) load(userptr, "glEnableVertexAttribArray"); glad_glEnableVertexAttribArrayARB = (PFNGLENABLEVERTEXATTRIBARRAYARBPROC) load(userptr, "glEnableVertexAttribArrayARB"); glad_glGetActiveAttrib = (PFNGLGETACTIVEATTRIBPROC) load(userptr, "glGetActiveAttrib"); glad_glGetActiveAttribARB = (PFNGLGETACTIVEATTRIBARBPROC) load(userptr, "glGetActiveAttribARB"); glad_glGetAttribLocation = (PFNGLGETATTRIBLOCATIONPROC) load(userptr, "glGetAttribLocation"); glad_glGetAttribLocationARB = (PFNGLGETATTRIBLOCATIONARBPROC) load(userptr, "glGetAttribLocationARB"); glad_glGetVertexAttribPointerv = (PFNGLGETVERTEXATTRIBPOINTERVPROC) load(userptr, "glGetVertexAttribPointerv"); glad_glGetVertexAttribPointervARB = (PFNGLGETVERTEXATTRIBPOINTERVARBPROC) load(userptr, "glGetVertexAttribPointervARB"); glad_glGetVertexAttribdv = (PFNGLGETVERTEXATTRIBDVPROC) load(userptr, "glGetVertexAttribdv"); glad_glGetVertexAttribdvARB = (PFNGLGETVERTEXATTRIBDVARBPROC) load(userptr, "glGetVertexAttribdvARB"); glad_glGetVertexAttribfv = (PFNGLGETVERTEXATTRIBFVPROC) load(userptr, "glGetVertexAttribfv"); glad_glGetVertexAttribfvARB = (PFNGLGETVERTEXATTRIBFVARBPROC) load(userptr, "glGetVertexAttribfvARB"); glad_glGetVertexAttribiv = (PFNGLGETVERTEXATTRIBIVPROC) load(userptr, "glGetVertexAttribiv"); glad_glGetVertexAttribivARB = (PFNGLGETVERTEXATTRIBIVARBPROC) load(userptr, "glGetVertexAttribivARB"); glad_glVertexAttrib1d = (PFNGLVERTEXATTRIB1DPROC) load(userptr, "glVertexAttrib1d"); glad_glVertexAttrib1dARB = (PFNGLVERTEXATTRIB1DARBPROC) load(userptr, "glVertexAttrib1dARB"); glad_glVertexAttrib1dv = (PFNGLVERTEXATTRIB1DVPROC) load(userptr, "glVertexAttrib1dv"); glad_glVertexAttrib1dvARB = (PFNGLVERTEXATTRIB1DVARBPROC) load(userptr, "glVertexAttrib1dvARB"); glad_glVertexAttrib1f = (PFNGLVERTEXATTRIB1FPROC) load(userptr, "glVertexAttrib1f"); glad_glVertexAttrib1fARB = (PFNGLVERTEXATTRIB1FARBPROC) load(userptr, "glVertexAttrib1fARB"); glad_glVertexAttrib1fv = (PFNGLVERTEXATTRIB1FVPROC) load(userptr, "glVertexAttrib1fv"); glad_glVertexAttrib1fvARB = (PFNGLVERTEXATTRIB1FVARBPROC) load(userptr, "glVertexAttrib1fvARB"); glad_glVertexAttrib1s = (PFNGLVERTEXATTRIB1SPROC) load(userptr, "glVertexAttrib1s"); glad_glVertexAttrib1sARB = (PFNGLVERTEXATTRIB1SARBPROC) load(userptr, "glVertexAttrib1sARB"); glad_glVertexAttrib1sv = (PFNGLVERTEXATTRIB1SVPROC) load(userptr, "glVertexAttrib1sv"); glad_glVertexAttrib1svARB = (PFNGLVERTEXATTRIB1SVARBPROC) load(userptr, "glVertexAttrib1svARB"); glad_glVertexAttrib2d = (PFNGLVERTEXATTRIB2DPROC) load(userptr, "glVertexAttrib2d"); glad_glVertexAttrib2dARB = (PFNGLVERTEXATTRIB2DARBPROC) load(userptr, "glVertexAttrib2dARB"); glad_glVertexAttrib2dv = (PFNGLVERTEXATTRIB2DVPROC) load(userptr, "glVertexAttrib2dv"); glad_glVertexAttrib2dvARB = (PFNGLVERTEXATTRIB2DVARBPROC) load(userptr, "glVertexAttrib2dvARB"); glad_glVertexAttrib2f = (PFNGLVERTEXATTRIB2FPROC) load(userptr, "glVertexAttrib2f"); glad_glVertexAttrib2fARB = (PFNGLVERTEXATTRIB2FARBPROC) load(userptr, "glVertexAttrib2fARB"); glad_glVertexAttrib2fv = (PFNGLVERTEXATTRIB2FVPROC) load(userptr, "glVertexAttrib2fv"); glad_glVertexAttrib2fvARB = (PFNGLVERTEXATTRIB2FVARBPROC) load(userptr, "glVertexAttrib2fvARB"); glad_glVertexAttrib2s = (PFNGLVERTEXATTRIB2SPROC) load(userptr, "glVertexAttrib2s"); glad_glVertexAttrib2sARB = (PFNGLVERTEXATTRIB2SARBPROC) load(userptr, "glVertexAttrib2sARB"); glad_glVertexAttrib2sv = (PFNGLVERTEXATTRIB2SVPROC) load(userptr, "glVertexAttrib2sv"); glad_glVertexAttrib2svARB = (PFNGLVERTEXATTRIB2SVARBPROC) load(userptr, "glVertexAttrib2svARB"); glad_glVertexAttrib3d = (PFNGLVERTEXATTRIB3DPROC) load(userptr, "glVertexAttrib3d"); glad_glVertexAttrib3dARB = (PFNGLVERTEXATTRIB3DARBPROC) load(userptr, "glVertexAttrib3dARB"); glad_glVertexAttrib3dv = (PFNGLVERTEXATTRIB3DVPROC) load(userptr, "glVertexAttrib3dv"); glad_glVertexAttrib3dvARB = (PFNGLVERTEXATTRIB3DVARBPROC) load(userptr, "glVertexAttrib3dvARB"); glad_glVertexAttrib3f = (PFNGLVERTEXATTRIB3FPROC) load(userptr, "glVertexAttrib3f"); glad_glVertexAttrib3fARB = (PFNGLVERTEXATTRIB3FARBPROC) load(userptr, "glVertexAttrib3fARB"); glad_glVertexAttrib3fv = (PFNGLVERTEXATTRIB3FVPROC) load(userptr, "glVertexAttrib3fv"); glad_glVertexAttrib3fvARB = (PFNGLVERTEXATTRIB3FVARBPROC) load(userptr, "glVertexAttrib3fvARB"); glad_glVertexAttrib3s = (PFNGLVERTEXATTRIB3SPROC) load(userptr, "glVertexAttrib3s"); glad_glVertexAttrib3sARB = (PFNGLVERTEXATTRIB3SARBPROC) load(userptr, "glVertexAttrib3sARB"); glad_glVertexAttrib3sv = (PFNGLVERTEXATTRIB3SVPROC) load(userptr, "glVertexAttrib3sv"); glad_glVertexAttrib3svARB = (PFNGLVERTEXATTRIB3SVARBPROC) load(userptr, "glVertexAttrib3svARB"); glad_glVertexAttrib4Nbv = (PFNGLVERTEXATTRIB4NBVPROC) load(userptr, "glVertexAttrib4Nbv"); glad_glVertexAttrib4NbvARB = (PFNGLVERTEXATTRIB4NBVARBPROC) load(userptr, "glVertexAttrib4NbvARB"); glad_glVertexAttrib4Niv = (PFNGLVERTEXATTRIB4NIVPROC) load(userptr, "glVertexAttrib4Niv"); glad_glVertexAttrib4NivARB = (PFNGLVERTEXATTRIB4NIVARBPROC) load(userptr, "glVertexAttrib4NivARB"); glad_glVertexAttrib4Nsv = (PFNGLVERTEXATTRIB4NSVPROC) load(userptr, "glVertexAttrib4Nsv"); glad_glVertexAttrib4NsvARB = (PFNGLVERTEXATTRIB4NSVARBPROC) load(userptr, "glVertexAttrib4NsvARB"); glad_glVertexAttrib4Nub = (PFNGLVERTEXATTRIB4NUBPROC) load(userptr, "glVertexAttrib4Nub"); glad_glVertexAttrib4NubARB = (PFNGLVERTEXATTRIB4NUBARBPROC) load(userptr, "glVertexAttrib4NubARB"); glad_glVertexAttrib4Nubv = (PFNGLVERTEXATTRIB4NUBVPROC) load(userptr, "glVertexAttrib4Nubv"); glad_glVertexAttrib4NubvARB = (PFNGLVERTEXATTRIB4NUBVARBPROC) load(userptr, "glVertexAttrib4NubvARB"); glad_glVertexAttrib4Nuiv = (PFNGLVERTEXATTRIB4NUIVPROC) load(userptr, "glVertexAttrib4Nuiv"); glad_glVertexAttrib4NuivARB = (PFNGLVERTEXATTRIB4NUIVARBPROC) load(userptr, "glVertexAttrib4NuivARB"); glad_glVertexAttrib4Nusv = (PFNGLVERTEXATTRIB4NUSVPROC) load(userptr, "glVertexAttrib4Nusv"); glad_glVertexAttrib4NusvARB = (PFNGLVERTEXATTRIB4NUSVARBPROC) load(userptr, "glVertexAttrib4NusvARB"); glad_glVertexAttrib4bv = (PFNGLVERTEXATTRIB4BVPROC) load(userptr, "glVertexAttrib4bv"); glad_glVertexAttrib4bvARB = (PFNGLVERTEXATTRIB4BVARBPROC) load(userptr, "glVertexAttrib4bvARB"); glad_glVertexAttrib4d = (PFNGLVERTEXATTRIB4DPROC) load(userptr, "glVertexAttrib4d"); glad_glVertexAttrib4dARB = (PFNGLVERTEXATTRIB4DARBPROC) load(userptr, "glVertexAttrib4dARB"); glad_glVertexAttrib4dv = (PFNGLVERTEXATTRIB4DVPROC) load(userptr, "glVertexAttrib4dv"); glad_glVertexAttrib4dvARB = (PFNGLVERTEXATTRIB4DVARBPROC) load(userptr, "glVertexAttrib4dvARB"); glad_glVertexAttrib4f = (PFNGLVERTEXATTRIB4FPROC) load(userptr, "glVertexAttrib4f"); glad_glVertexAttrib4fARB = (PFNGLVERTEXATTRIB4FARBPROC) load(userptr, "glVertexAttrib4fARB"); glad_glVertexAttrib4fv = (PFNGLVERTEXATTRIB4FVPROC) load(userptr, "glVertexAttrib4fv"); glad_glVertexAttrib4fvARB = (PFNGLVERTEXATTRIB4FVARBPROC) load(userptr, "glVertexAttrib4fvARB"); glad_glVertexAttrib4iv = (PFNGLVERTEXATTRIB4IVPROC) load(userptr, "glVertexAttrib4iv"); glad_glVertexAttrib4ivARB = (PFNGLVERTEXATTRIB4IVARBPROC) load(userptr, "glVertexAttrib4ivARB"); glad_glVertexAttrib4s = (PFNGLVERTEXATTRIB4SPROC) load(userptr, "glVertexAttrib4s"); glad_glVertexAttrib4sARB = (PFNGLVERTEXATTRIB4SARBPROC) load(userptr, "glVertexAttrib4sARB"); glad_glVertexAttrib4sv = (PFNGLVERTEXATTRIB4SVPROC) load(userptr, "glVertexAttrib4sv"); glad_glVertexAttrib4svARB = (PFNGLVERTEXATTRIB4SVARBPROC) load(userptr, "glVertexAttrib4svARB"); glad_glVertexAttrib4ubv = (PFNGLVERTEXATTRIB4UBVPROC) load(userptr, "glVertexAttrib4ubv"); glad_glVertexAttrib4ubvARB = (PFNGLVERTEXATTRIB4UBVARBPROC) load(userptr, "glVertexAttrib4ubvARB"); glad_glVertexAttrib4uiv = (PFNGLVERTEXATTRIB4UIVPROC) load(userptr, "glVertexAttrib4uiv"); glad_glVertexAttrib4uivARB = (PFNGLVERTEXATTRIB4UIVARBPROC) load(userptr, "glVertexAttrib4uivARB"); glad_glVertexAttrib4usv = (PFNGLVERTEXATTRIB4USVPROC) load(userptr, "glVertexAttrib4usv"); glad_glVertexAttrib4usvARB = (PFNGLVERTEXATTRIB4USVARBPROC) load(userptr, "glVertexAttrib4usvARB"); glad_glVertexAttribPointer = (PFNGLVERTEXATTRIBPOINTERPROC) load(userptr, "glVertexAttribPointer"); glad_glVertexAttribPointerARB = (PFNGLVERTEXATTRIBPOINTERARBPROC) load(userptr, "glVertexAttribPointerARB"); } static void glad_gl_load_GL_ARB_vertex_type_2_10_10_10_rev( GLADuserptrloadfunc load, void* userptr) { if(!GLAD_GL_ARB_vertex_type_2_10_10_10_rev) return; glad_glColorP3ui = (PFNGLCOLORP3UIPROC) load(userptr, "glColorP3ui"); glad_glColorP3uiv = (PFNGLCOLORP3UIVPROC) load(userptr, "glColorP3uiv"); glad_glColorP4ui = (PFNGLCOLORP4UIPROC) load(userptr, "glColorP4ui"); glad_glColorP4uiv = (PFNGLCOLORP4UIVPROC) load(userptr, "glColorP4uiv"); glad_glMultiTexCoordP1ui = (PFNGLMULTITEXCOORDP1UIPROC) load(userptr, "glMultiTexCoordP1ui"); glad_glMultiTexCoordP1uiv = (PFNGLMULTITEXCOORDP1UIVPROC) load(userptr, "glMultiTexCoordP1uiv"); glad_glMultiTexCoordP2ui = (PFNGLMULTITEXCOORDP2UIPROC) load(userptr, "glMultiTexCoordP2ui"); glad_glMultiTexCoordP2uiv = (PFNGLMULTITEXCOORDP2UIVPROC) load(userptr, "glMultiTexCoordP2uiv"); glad_glMultiTexCoordP3ui = (PFNGLMULTITEXCOORDP3UIPROC) load(userptr, "glMultiTexCoordP3ui"); glad_glMultiTexCoordP3uiv = (PFNGLMULTITEXCOORDP3UIVPROC) load(userptr, "glMultiTexCoordP3uiv"); glad_glMultiTexCoordP4ui = (PFNGLMULTITEXCOORDP4UIPROC) load(userptr, "glMultiTexCoordP4ui"); glad_glMultiTexCoordP4uiv = (PFNGLMULTITEXCOORDP4UIVPROC) load(userptr, "glMultiTexCoordP4uiv"); glad_glNormalP3ui = (PFNGLNORMALP3UIPROC) load(userptr, "glNormalP3ui"); glad_glNormalP3uiv = (PFNGLNORMALP3UIVPROC) load(userptr, "glNormalP3uiv"); glad_glSecondaryColorP3ui = (PFNGLSECONDARYCOLORP3UIPROC) load(userptr, "glSecondaryColorP3ui"); glad_glSecondaryColorP3uiv = (PFNGLSECONDARYCOLORP3UIVPROC) load(userptr, "glSecondaryColorP3uiv"); glad_glTexCoordP1ui = (PFNGLTEXCOORDP1UIPROC) load(userptr, "glTexCoordP1ui"); glad_glTexCoordP1uiv = (PFNGLTEXCOORDP1UIVPROC) load(userptr, "glTexCoordP1uiv"); glad_glTexCoordP2ui = (PFNGLTEXCOORDP2UIPROC) load(userptr, "glTexCoordP2ui"); glad_glTexCoordP2uiv = (PFNGLTEXCOORDP2UIVPROC) load(userptr, "glTexCoordP2uiv"); glad_glTexCoordP3ui = (PFNGLTEXCOORDP3UIPROC) load(userptr, "glTexCoordP3ui"); glad_glTexCoordP3uiv = (PFNGLTEXCOORDP3UIVPROC) load(userptr, "glTexCoordP3uiv"); glad_glTexCoordP4ui = (PFNGLTEXCOORDP4UIPROC) load(userptr, "glTexCoordP4ui"); glad_glTexCoordP4uiv = (PFNGLTEXCOORDP4UIVPROC) load(userptr, "glTexCoordP4uiv"); glad_glVertexAttribP1ui = (PFNGLVERTEXATTRIBP1UIPROC) load(userptr, "glVertexAttribP1ui"); glad_glVertexAttribP1uiv = (PFNGLVERTEXATTRIBP1UIVPROC) load(userptr, "glVertexAttribP1uiv"); glad_glVertexAttribP2ui = (PFNGLVERTEXATTRIBP2UIPROC) load(userptr, "glVertexAttribP2ui"); glad_glVertexAttribP2uiv = (PFNGLVERTEXATTRIBP2UIVPROC) load(userptr, "glVertexAttribP2uiv"); glad_glVertexAttribP3ui = (PFNGLVERTEXATTRIBP3UIPROC) load(userptr, "glVertexAttribP3ui"); glad_glVertexAttribP3uiv = (PFNGLVERTEXATTRIBP3UIVPROC) load(userptr, "glVertexAttribP3uiv"); glad_glVertexAttribP4ui = (PFNGLVERTEXATTRIBP4UIPROC) load(userptr, "glVertexAttribP4ui"); glad_glVertexAttribP4uiv = (PFNGLVERTEXATTRIBP4UIVPROC) load(userptr, "glVertexAttribP4uiv"); glad_glVertexP2ui = (PFNGLVERTEXP2UIPROC) load(userptr, "glVertexP2ui"); glad_glVertexP2uiv = (PFNGLVERTEXP2UIVPROC) load(userptr, "glVertexP2uiv"); glad_glVertexP3ui = (PFNGLVERTEXP3UIPROC) load(userptr, "glVertexP3ui"); glad_glVertexP3uiv = (PFNGLVERTEXP3UIVPROC) load(userptr, "glVertexP3uiv"); glad_glVertexP4ui = (PFNGLVERTEXP4UIPROC) load(userptr, "glVertexP4ui"); glad_glVertexP4uiv = (PFNGLVERTEXP4UIVPROC) load(userptr, "glVertexP4uiv"); } static void glad_gl_load_GL_ARB_viewport_array( GLADuserptrloadfunc load, void* userptr) { if(!GLAD_GL_ARB_viewport_array) return; glad_glDepthRangeArraydvNV = (PFNGLDEPTHRANGEARRAYDVNVPROC) load(userptr, "glDepthRangeArraydvNV"); glad_glDepthRangeArrayv = (PFNGLDEPTHRANGEARRAYVPROC) load(userptr, "glDepthRangeArrayv"); glad_glDepthRangeIndexed = (PFNGLDEPTHRANGEINDEXEDPROC) load(userptr, "glDepthRangeIndexed"); glad_glDepthRangeIndexeddNV = (PFNGLDEPTHRANGEINDEXEDDNVPROC) load(userptr, "glDepthRangeIndexeddNV"); glad_glGetDoublei_v = (PFNGLGETDOUBLEI_VPROC) load(userptr, "glGetDoublei_v"); glad_glGetFloati_v = (PFNGLGETFLOATI_VPROC) load(userptr, "glGetFloati_v"); glad_glScissorArrayv = (PFNGLSCISSORARRAYVPROC) load(userptr, "glScissorArrayv"); glad_glScissorIndexed = (PFNGLSCISSORINDEXEDPROC) load(userptr, "glScissorIndexed"); glad_glScissorIndexedv = (PFNGLSCISSORINDEXEDVPROC) load(userptr, "glScissorIndexedv"); glad_glViewportArrayv = (PFNGLVIEWPORTARRAYVPROC) load(userptr, "glViewportArrayv"); glad_glViewportIndexedf = (PFNGLVIEWPORTINDEXEDFPROC) load(userptr, "glViewportIndexedf"); glad_glViewportIndexedfv = (PFNGLVIEWPORTINDEXEDFVPROC) load(userptr, "glViewportIndexedfv"); } static void glad_gl_load_GL_ARB_window_pos( GLADuserptrloadfunc load, void* userptr) { if(!GLAD_GL_ARB_window_pos) return; glad_glWindowPos2d = (PFNGLWINDOWPOS2DPROC) load(userptr, "glWindowPos2d"); glad_glWindowPos2dARB = (PFNGLWINDOWPOS2DARBPROC) load(userptr, "glWindowPos2dARB"); glad_glWindowPos2dv = (PFNGLWINDOWPOS2DVPROC) load(userptr, "glWindowPos2dv"); glad_glWindowPos2dvARB = (PFNGLWINDOWPOS2DVARBPROC) load(userptr, "glWindowPos2dvARB"); glad_glWindowPos2f = (PFNGLWINDOWPOS2FPROC) load(userptr, "glWindowPos2f"); glad_glWindowPos2fARB = (PFNGLWINDOWPOS2FARBPROC) load(userptr, "glWindowPos2fARB"); glad_glWindowPos2fv = (PFNGLWINDOWPOS2FVPROC) load(userptr, "glWindowPos2fv"); glad_glWindowPos2fvARB = (PFNGLWINDOWPOS2FVARBPROC) load(userptr, "glWindowPos2fvARB"); glad_glWindowPos2i = (PFNGLWINDOWPOS2IPROC) load(userptr, "glWindowPos2i"); glad_glWindowPos2iARB = (PFNGLWINDOWPOS2IARBPROC) load(userptr, "glWindowPos2iARB"); glad_glWindowPos2iv = (PFNGLWINDOWPOS2IVPROC) load(userptr, "glWindowPos2iv"); glad_glWindowPos2ivARB = (PFNGLWINDOWPOS2IVARBPROC) load(userptr, "glWindowPos2ivARB"); glad_glWindowPos2s = (PFNGLWINDOWPOS2SPROC) load(userptr, "glWindowPos2s"); glad_glWindowPos2sARB = (PFNGLWINDOWPOS2SARBPROC) load(userptr, "glWindowPos2sARB"); glad_glWindowPos2sv = (PFNGLWINDOWPOS2SVPROC) load(userptr, "glWindowPos2sv"); glad_glWindowPos2svARB = (PFNGLWINDOWPOS2SVARBPROC) load(userptr, "glWindowPos2svARB"); glad_glWindowPos3d = (PFNGLWINDOWPOS3DPROC) load(userptr, "glWindowPos3d"); glad_glWindowPos3dARB = (PFNGLWINDOWPOS3DARBPROC) load(userptr, "glWindowPos3dARB"); glad_glWindowPos3dv = (PFNGLWINDOWPOS3DVPROC) load(userptr, "glWindowPos3dv"); glad_glWindowPos3dvARB = (PFNGLWINDOWPOS3DVARBPROC) load(userptr, "glWindowPos3dvARB"); glad_glWindowPos3f = (PFNGLWINDOWPOS3FPROC) load(userptr, "glWindowPos3f"); glad_glWindowPos3fARB = (PFNGLWINDOWPOS3FARBPROC) load(userptr, "glWindowPos3fARB"); glad_glWindowPos3fv = (PFNGLWINDOWPOS3FVPROC) load(userptr, "glWindowPos3fv"); glad_glWindowPos3fvARB = (PFNGLWINDOWPOS3FVARBPROC) load(userptr, "glWindowPos3fvARB"); glad_glWindowPos3i = (PFNGLWINDOWPOS3IPROC) load(userptr, "glWindowPos3i"); glad_glWindowPos3iARB = (PFNGLWINDOWPOS3IARBPROC) load(userptr, "glWindowPos3iARB"); glad_glWindowPos3iv = (PFNGLWINDOWPOS3IVPROC) load(userptr, "glWindowPos3iv"); glad_glWindowPos3ivARB = (PFNGLWINDOWPOS3IVARBPROC) load(userptr, "glWindowPos3ivARB"); glad_glWindowPos3s = (PFNGLWINDOWPOS3SPROC) load(userptr, "glWindowPos3s"); glad_glWindowPos3sARB = (PFNGLWINDOWPOS3SARBPROC) load(userptr, "glWindowPos3sARB"); glad_glWindowPos3sv = (PFNGLWINDOWPOS3SVPROC) load(userptr, "glWindowPos3sv"); glad_glWindowPos3svARB = (PFNGLWINDOWPOS3SVARBPROC) load(userptr, "glWindowPos3svARB"); } static void glad_gl_load_GL_KHR_blend_equation_advanced( GLADuserptrloadfunc load, void* userptr) { if(!GLAD_GL_KHR_blend_equation_advanced) return; glad_glBlendBarrier = (PFNGLBLENDBARRIERPROC) load(userptr, "glBlendBarrier"); glad_glBlendBarrierKHR = (PFNGLBLENDBARRIERKHRPROC) load(userptr, "glBlendBarrierKHR"); } static void glad_gl_load_GL_KHR_debug( GLADuserptrloadfunc load, void* userptr) { if(!GLAD_GL_KHR_debug) return; glad_glDebugMessageCallback = (PFNGLDEBUGMESSAGECALLBACKPROC) load(userptr, "glDebugMessageCallback"); glad_glDebugMessageControl = (PFNGLDEBUGMESSAGECONTROLPROC) load(userptr, "glDebugMessageControl"); glad_glDebugMessageInsert = (PFNGLDEBUGMESSAGEINSERTPROC) load(userptr, "glDebugMessageInsert"); glad_glGetDebugMessageLog = (PFNGLGETDEBUGMESSAGELOGPROC) load(userptr, "glGetDebugMessageLog"); glad_glGetObjectLabel = (PFNGLGETOBJECTLABELPROC) load(userptr, "glGetObjectLabel"); glad_glGetObjectPtrLabel = (PFNGLGETOBJECTPTRLABELPROC) load(userptr, "glGetObjectPtrLabel"); glad_glGetPointerv = (PFNGLGETPOINTERVPROC) load(userptr, "glGetPointerv"); glad_glObjectLabel = (PFNGLOBJECTLABELPROC) load(userptr, "glObjectLabel"); glad_glObjectPtrLabel = (PFNGLOBJECTPTRLABELPROC) load(userptr, "glObjectPtrLabel"); glad_glPopDebugGroup = (PFNGLPOPDEBUGGROUPPROC) load(userptr, "glPopDebugGroup"); glad_glPushDebugGroup = (PFNGLPUSHDEBUGGROUPPROC) load(userptr, "glPushDebugGroup"); } static void glad_gl_load_GL_KHR_parallel_shader_compile( GLADuserptrloadfunc load, void* userptr) { if(!GLAD_GL_KHR_parallel_shader_compile) return; glad_glMaxShaderCompilerThreadsKHR = (PFNGLMAXSHADERCOMPILERTHREADSKHRPROC) load(userptr, "glMaxShaderCompilerThreadsKHR"); } static void glad_gl_load_GL_KHR_robustness( GLADuserptrloadfunc load, void* userptr) { if(!GLAD_GL_KHR_robustness) return; glad_glGetGraphicsResetStatus = (PFNGLGETGRAPHICSRESETSTATUSPROC) load(userptr, "glGetGraphicsResetStatus"); glad_glGetnUniformfv = (PFNGLGETNUNIFORMFVPROC) load(userptr, "glGetnUniformfv"); glad_glGetnUniformiv = (PFNGLGETNUNIFORMIVPROC) load(userptr, "glGetnUniformiv"); glad_glGetnUniformuiv = (PFNGLGETNUNIFORMUIVPROC) load(userptr, "glGetnUniformuiv"); glad_glReadnPixels = (PFNGLREADNPIXELSPROC) load(userptr, "glReadnPixels"); } #if defined(GL_ES_VERSION_3_0) || defined(GL_VERSION_3_0) #define GLAD_GL_IS_SOME_NEW_VERSION 1 #else #define GLAD_GL_IS_SOME_NEW_VERSION 0 #endif static int glad_gl_get_extensions( int version, const char **out_exts, unsigned int *out_num_exts_i, char ***out_exts_i) { #if GLAD_GL_IS_SOME_NEW_VERSION if(GLAD_VERSION_MAJOR(version) < 3) { #else (void) version; (void) out_num_exts_i; (void) out_exts_i; #endif if (glad_glGetString == NULL) { return 0; } *out_exts = (const char *)glad_glGetString(GL_EXTENSIONS); #if GLAD_GL_IS_SOME_NEW_VERSION } else { unsigned int index = 0; unsigned int num_exts_i = 0; char **exts_i = NULL; if (glad_glGetStringi == NULL || glad_glGetIntegerv == NULL) { return 0; } glad_glGetIntegerv(GL_NUM_EXTENSIONS, (int*) &num_exts_i); if (num_exts_i > 0) { exts_i = (char **) malloc(num_exts_i * (sizeof *exts_i)); } if (exts_i == NULL) { return 0; } for(index = 0; index < num_exts_i; index++) { const char *gl_str_tmp = (const char*) glad_glGetStringi(GL_EXTENSIONS, index); size_t len = strlen(gl_str_tmp) + 1; char *local_str = (char*) malloc(len * sizeof(char)); if(local_str != NULL) { memcpy(local_str, gl_str_tmp, len * sizeof(char)); } exts_i[index] = local_str; } *out_num_exts_i = num_exts_i; *out_exts_i = exts_i; } #endif return 1; } static void glad_gl_free_extensions(char **exts_i, unsigned int num_exts_i) { if (exts_i != NULL) { unsigned int index; for(index = 0; index < num_exts_i; index++) { free((void *) (exts_i[index])); } free((void *)exts_i); exts_i = NULL; } } static int glad_gl_has_extension(int version, const char *exts, unsigned int num_exts_i, char **exts_i, const char *ext) { if(GLAD_VERSION_MAJOR(version) < 3 || !GLAD_GL_IS_SOME_NEW_VERSION) { const char *extensions; const char *loc; const char *terminator; extensions = exts; if(extensions == NULL || ext == NULL) { return 0; } while(1) { loc = strstr(extensions, ext); if(loc == NULL) { return 0; } terminator = loc + strlen(ext); if((loc == extensions || *(loc - 1) == ' ') && (*terminator == ' ' || *terminator == '\0')) { return 1; } extensions = terminator; } } else { unsigned int index; for(index = 0; index < num_exts_i; index++) { const char *e = exts_i[index]; if(strcmp(e, ext) == 0) { return 1; } } } return 0; } static GLADapiproc glad_gl_get_proc_from_userptr(void *userptr, const char* name) { return (GLAD_GNUC_EXTENSION (GLADapiproc (*)(const char *name)) userptr)(name); } static int glad_gl_find_extensions_gl( int version) { const char *exts = NULL; unsigned int num_exts_i = 0; char **exts_i = NULL; if (!glad_gl_get_extensions(version, &exts, &num_exts_i, &exts_i)) return 0; GLAD_GL_ARB_ES2_compatibility = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_ES2_compatibility"); GLAD_GL_ARB_ES3_1_compatibility = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_ES3_1_compatibility"); GLAD_GL_ARB_ES3_2_compatibility = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_ES3_2_compatibility"); GLAD_GL_ARB_ES3_compatibility = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_ES3_compatibility"); GLAD_GL_ARB_arrays_of_arrays = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_arrays_of_arrays"); GLAD_GL_ARB_base_instance = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_base_instance"); GLAD_GL_ARB_bindless_texture = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_bindless_texture"); GLAD_GL_ARB_blend_func_extended = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_blend_func_extended"); GLAD_GL_ARB_buffer_storage = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_buffer_storage"); GLAD_GL_ARB_cl_event = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_cl_event"); GLAD_GL_ARB_clear_buffer_object = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_clear_buffer_object"); GLAD_GL_ARB_clear_texture = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_clear_texture"); GLAD_GL_ARB_clip_control = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_clip_control"); GLAD_GL_ARB_color_buffer_float = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_color_buffer_float"); GLAD_GL_ARB_compatibility = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_compatibility"); GLAD_GL_ARB_compressed_texture_pixel_storage = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_compressed_texture_pixel_storage"); GLAD_GL_ARB_compute_shader = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_compute_shader"); GLAD_GL_ARB_compute_variable_group_size = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_compute_variable_group_size"); GLAD_GL_ARB_conditional_render_inverted = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_conditional_render_inverted"); GLAD_GL_ARB_conservative_depth = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_conservative_depth"); GLAD_GL_ARB_copy_buffer = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_copy_buffer"); GLAD_GL_ARB_copy_image = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_copy_image"); GLAD_GL_ARB_cull_distance = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_cull_distance"); GLAD_GL_ARB_debug_output = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_debug_output"); GLAD_GL_ARB_depth_buffer_float = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_depth_buffer_float"); GLAD_GL_ARB_depth_clamp = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_depth_clamp"); GLAD_GL_ARB_depth_texture = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_depth_texture"); GLAD_GL_ARB_derivative_control = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_derivative_control"); GLAD_GL_ARB_direct_state_access = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_direct_state_access"); GLAD_GL_ARB_draw_buffers = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_draw_buffers"); GLAD_GL_ARB_draw_buffers_blend = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_draw_buffers_blend"); GLAD_GL_ARB_draw_elements_base_vertex = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_draw_elements_base_vertex"); GLAD_GL_ARB_draw_indirect = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_draw_indirect"); GLAD_GL_ARB_draw_instanced = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_draw_instanced"); GLAD_GL_ARB_enhanced_layouts = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_enhanced_layouts"); GLAD_GL_ARB_explicit_attrib_location = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_explicit_attrib_location"); GLAD_GL_ARB_explicit_uniform_location = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_explicit_uniform_location"); GLAD_GL_ARB_fragment_coord_conventions = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_fragment_coord_conventions"); GLAD_GL_ARB_fragment_layer_viewport = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_fragment_layer_viewport"); GLAD_GL_ARB_fragment_program = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_fragment_program"); GLAD_GL_ARB_fragment_program_shadow = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_fragment_program_shadow"); GLAD_GL_ARB_fragment_shader = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_fragment_shader"); GLAD_GL_ARB_fragment_shader_interlock = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_fragment_shader_interlock"); GLAD_GL_ARB_framebuffer_no_attachments = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_framebuffer_no_attachments"); GLAD_GL_ARB_framebuffer_object = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_framebuffer_object"); GLAD_GL_ARB_framebuffer_sRGB = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_framebuffer_sRGB"); GLAD_GL_ARB_geometry_shader4 = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_geometry_shader4"); GLAD_GL_ARB_get_program_binary = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_get_program_binary"); GLAD_GL_ARB_get_texture_sub_image = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_get_texture_sub_image"); GLAD_GL_ARB_gl_spirv = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_gl_spirv"); GLAD_GL_ARB_gpu_shader5 = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_gpu_shader5"); GLAD_GL_ARB_gpu_shader_fp64 = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_gpu_shader_fp64"); GLAD_GL_ARB_gpu_shader_int64 = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_gpu_shader_int64"); GLAD_GL_ARB_half_float_pixel = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_half_float_pixel"); GLAD_GL_ARB_half_float_vertex = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_half_float_vertex"); GLAD_GL_ARB_imaging = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_imaging"); GLAD_GL_ARB_indirect_parameters = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_indirect_parameters"); GLAD_GL_ARB_instanced_arrays = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_instanced_arrays"); GLAD_GL_ARB_internalformat_query = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_internalformat_query"); GLAD_GL_ARB_internalformat_query2 = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_internalformat_query2"); GLAD_GL_ARB_invalidate_subdata = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_invalidate_subdata"); GLAD_GL_ARB_map_buffer_alignment = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_map_buffer_alignment"); GLAD_GL_ARB_map_buffer_range = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_map_buffer_range"); GLAD_GL_ARB_matrix_palette = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_matrix_palette"); GLAD_GL_ARB_multi_bind = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_multi_bind"); GLAD_GL_ARB_multi_draw_indirect = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_multi_draw_indirect"); GLAD_GL_ARB_multisample = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_multisample"); GLAD_GL_ARB_multitexture = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_multitexture"); GLAD_GL_ARB_occlusion_query = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_occlusion_query"); GLAD_GL_ARB_occlusion_query2 = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_occlusion_query2"); GLAD_GL_ARB_parallel_shader_compile = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_parallel_shader_compile"); GLAD_GL_ARB_pipeline_statistics_query = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_pipeline_statistics_query"); GLAD_GL_ARB_pixel_buffer_object = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_pixel_buffer_object"); GLAD_GL_ARB_point_parameters = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_point_parameters"); GLAD_GL_ARB_point_sprite = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_point_sprite"); GLAD_GL_ARB_polygon_offset_clamp = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_polygon_offset_clamp"); GLAD_GL_ARB_post_depth_coverage = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_post_depth_coverage"); GLAD_GL_ARB_program_interface_query = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_program_interface_query"); GLAD_GL_ARB_provoking_vertex = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_provoking_vertex"); GLAD_GL_ARB_query_buffer_object = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_query_buffer_object"); GLAD_GL_ARB_robust_buffer_access_behavior = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_robust_buffer_access_behavior"); GLAD_GL_ARB_robustness = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_robustness"); GLAD_GL_ARB_robustness_isolation = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_robustness_isolation"); GLAD_GL_ARB_sample_locations = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_sample_locations"); GLAD_GL_ARB_sample_shading = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_sample_shading"); GLAD_GL_ARB_sampler_objects = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_sampler_objects"); GLAD_GL_ARB_seamless_cube_map = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_seamless_cube_map"); GLAD_GL_ARB_seamless_cubemap_per_texture = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_seamless_cubemap_per_texture"); GLAD_GL_ARB_separate_shader_objects = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_separate_shader_objects"); GLAD_GL_ARB_shader_atomic_counter_ops = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_shader_atomic_counter_ops"); GLAD_GL_ARB_shader_atomic_counters = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_shader_atomic_counters"); GLAD_GL_ARB_shader_ballot = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_shader_ballot"); GLAD_GL_ARB_shader_bit_encoding = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_shader_bit_encoding"); GLAD_GL_ARB_shader_clock = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_shader_clock"); GLAD_GL_ARB_shader_draw_parameters = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_shader_draw_parameters"); GLAD_GL_ARB_shader_group_vote = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_shader_group_vote"); GLAD_GL_ARB_shader_image_load_store = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_shader_image_load_store"); GLAD_GL_ARB_shader_image_size = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_shader_image_size"); GLAD_GL_ARB_shader_objects = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_shader_objects"); GLAD_GL_ARB_shader_precision = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_shader_precision"); GLAD_GL_ARB_shader_stencil_export = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_shader_stencil_export"); GLAD_GL_ARB_shader_storage_buffer_object = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_shader_storage_buffer_object"); GLAD_GL_ARB_shader_subroutine = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_shader_subroutine"); GLAD_GL_ARB_shader_texture_image_samples = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_shader_texture_image_samples"); GLAD_GL_ARB_shader_texture_lod = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_shader_texture_lod"); GLAD_GL_ARB_shader_viewport_layer_array = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_shader_viewport_layer_array"); GLAD_GL_ARB_shading_language_100 = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_shading_language_100"); GLAD_GL_ARB_shading_language_420pack = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_shading_language_420pack"); GLAD_GL_ARB_shading_language_include = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_shading_language_include"); GLAD_GL_ARB_shading_language_packing = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_shading_language_packing"); GLAD_GL_ARB_shadow = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_shadow"); GLAD_GL_ARB_shadow_ambient = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_shadow_ambient"); GLAD_GL_ARB_sparse_buffer = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_sparse_buffer"); GLAD_GL_ARB_sparse_texture = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_sparse_texture"); GLAD_GL_ARB_sparse_texture2 = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_sparse_texture2"); GLAD_GL_ARB_sparse_texture_clamp = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_sparse_texture_clamp"); GLAD_GL_ARB_spirv_extensions = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_spirv_extensions"); GLAD_GL_ARB_stencil_texturing = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_stencil_texturing"); GLAD_GL_ARB_sync = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_sync"); GLAD_GL_ARB_tessellation_shader = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_tessellation_shader"); GLAD_GL_ARB_texture_barrier = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_texture_barrier"); GLAD_GL_ARB_texture_border_clamp = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_texture_border_clamp"); GLAD_GL_ARB_texture_buffer_object = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_texture_buffer_object"); GLAD_GL_ARB_texture_buffer_object_rgb32 = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_texture_buffer_object_rgb32"); GLAD_GL_ARB_texture_buffer_range = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_texture_buffer_range"); GLAD_GL_ARB_texture_compression = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_texture_compression"); GLAD_GL_ARB_texture_compression_bptc = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_texture_compression_bptc"); GLAD_GL_ARB_texture_compression_rgtc = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_texture_compression_rgtc"); GLAD_GL_ARB_texture_cube_map = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_texture_cube_map"); GLAD_GL_ARB_texture_cube_map_array = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_texture_cube_map_array"); GLAD_GL_ARB_texture_env_add = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_texture_env_add"); GLAD_GL_ARB_texture_env_combine = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_texture_env_combine"); GLAD_GL_ARB_texture_env_crossbar = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_texture_env_crossbar"); GLAD_GL_ARB_texture_env_dot3 = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_texture_env_dot3"); GLAD_GL_ARB_texture_filter_anisotropic = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_texture_filter_anisotropic"); GLAD_GL_ARB_texture_filter_minmax = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_texture_filter_minmax"); GLAD_GL_ARB_texture_float = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_texture_float"); GLAD_GL_ARB_texture_gather = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_texture_gather"); GLAD_GL_ARB_texture_mirror_clamp_to_edge = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_texture_mirror_clamp_to_edge"); GLAD_GL_ARB_texture_mirrored_repeat = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_texture_mirrored_repeat"); GLAD_GL_ARB_texture_multisample = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_texture_multisample"); GLAD_GL_ARB_texture_non_power_of_two = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_texture_non_power_of_two"); GLAD_GL_ARB_texture_query_levels = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_texture_query_levels"); GLAD_GL_ARB_texture_query_lod = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_texture_query_lod"); GLAD_GL_ARB_texture_rectangle = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_texture_rectangle"); GLAD_GL_ARB_texture_rg = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_texture_rg"); GLAD_GL_ARB_texture_rgb10_a2ui = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_texture_rgb10_a2ui"); GLAD_GL_ARB_texture_stencil8 = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_texture_stencil8"); GLAD_GL_ARB_texture_storage = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_texture_storage"); GLAD_GL_ARB_texture_storage_multisample = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_texture_storage_multisample"); GLAD_GL_ARB_texture_swizzle = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_texture_swizzle"); GLAD_GL_ARB_texture_view = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_texture_view"); GLAD_GL_ARB_timer_query = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_timer_query"); GLAD_GL_ARB_transform_feedback2 = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_transform_feedback2"); GLAD_GL_ARB_transform_feedback3 = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_transform_feedback3"); GLAD_GL_ARB_transform_feedback_instanced = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_transform_feedback_instanced"); GLAD_GL_ARB_transform_feedback_overflow_query = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_transform_feedback_overflow_query"); GLAD_GL_ARB_transpose_matrix = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_transpose_matrix"); GLAD_GL_ARB_uniform_buffer_object = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_uniform_buffer_object"); GLAD_GL_ARB_vertex_array_bgra = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_vertex_array_bgra"); GLAD_GL_ARB_vertex_array_object = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_vertex_array_object"); GLAD_GL_ARB_vertex_attrib_64bit = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_vertex_attrib_64bit"); GLAD_GL_ARB_vertex_attrib_binding = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_vertex_attrib_binding"); GLAD_GL_ARB_vertex_blend = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_vertex_blend"); GLAD_GL_ARB_vertex_buffer_object = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_vertex_buffer_object"); GLAD_GL_ARB_vertex_program = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_vertex_program"); GLAD_GL_ARB_vertex_shader = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_vertex_shader"); GLAD_GL_ARB_vertex_type_10f_11f_11f_rev = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_vertex_type_10f_11f_11f_rev"); GLAD_GL_ARB_vertex_type_2_10_10_10_rev = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_vertex_type_2_10_10_10_rev"); GLAD_GL_ARB_viewport_array = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_viewport_array"); GLAD_GL_ARB_window_pos = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_ARB_window_pos"); GLAD_GL_KHR_blend_equation_advanced = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_KHR_blend_equation_advanced"); GLAD_GL_KHR_blend_equation_advanced_coherent = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_KHR_blend_equation_advanced_coherent"); GLAD_GL_KHR_context_flush_control = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_KHR_context_flush_control"); GLAD_GL_KHR_debug = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_KHR_debug"); GLAD_GL_KHR_no_error = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_KHR_no_error"); GLAD_GL_KHR_parallel_shader_compile = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_KHR_parallel_shader_compile"); GLAD_GL_KHR_robust_buffer_access_behavior = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_KHR_robust_buffer_access_behavior"); GLAD_GL_KHR_robustness = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_KHR_robustness"); GLAD_GL_KHR_shader_subgroup = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_KHR_shader_subgroup"); GLAD_GL_KHR_texture_compression_astc_hdr = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_KHR_texture_compression_astc_hdr"); GLAD_GL_KHR_texture_compression_astc_ldr = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_KHR_texture_compression_astc_ldr"); GLAD_GL_KHR_texture_compression_astc_sliced_3d = glad_gl_has_extension(version, exts, num_exts_i, exts_i, "GL_KHR_texture_compression_astc_sliced_3d"); glad_gl_free_extensions(exts_i, num_exts_i); return 1; } static int glad_gl_find_core_gl(void) { int i, major, minor; const char* version; const char* prefixes[] = { "OpenGL ES-CM ", "OpenGL ES-CL ", "OpenGL ES ", NULL }; version = (const char*) glad_glGetString(GL_VERSION); if (!version) return 0; for (i = 0; prefixes[i]; i++) { const size_t length = strlen(prefixes[i]); if (strncmp(version, prefixes[i], length) == 0) { version += length; break; } } GLAD_IMPL_UTIL_SSCANF(version, "%d.%d", &major, &minor); GLAD_GL_VERSION_1_0 = (major == 1 && minor >= 0) || major > 1; GLAD_GL_VERSION_1_1 = (major == 1 && minor >= 1) || major > 1; GLAD_GL_VERSION_1_2 = (major == 1 && minor >= 2) || major > 1; GLAD_GL_VERSION_1_3 = (major == 1 && minor >= 3) || major > 1; GLAD_GL_VERSION_1_4 = (major == 1 && minor >= 4) || major > 1; GLAD_GL_VERSION_1_5 = (major == 1 && minor >= 5) || major > 1; GLAD_GL_VERSION_2_0 = (major == 2 && minor >= 0) || major > 2; GLAD_GL_VERSION_2_1 = (major == 2 && minor >= 1) || major > 2; GLAD_GL_VERSION_3_0 = (major == 3 && minor >= 0) || major > 3; GLAD_GL_VERSION_3_1 = (major == 3 && minor >= 1) || major > 3; GLAD_GL_VERSION_3_2 = (major == 3 && minor >= 2) || major > 3; GLAD_GL_VERSION_3_3 = (major == 3 && minor >= 3) || major > 3; return GLAD_MAKE_VERSION(major, minor); } int gladLoadGLUserPtr( GLADuserptrloadfunc load, void *userptr) { int version; glad_glGetString = (PFNGLGETSTRINGPROC) load(userptr, "glGetString"); if(glad_glGetString == NULL) return 0; if(glad_glGetString(GL_VERSION) == NULL) return 0; version = glad_gl_find_core_gl(); glad_gl_load_GL_VERSION_1_0(load, userptr); glad_gl_load_GL_VERSION_1_1(load, userptr); glad_gl_load_GL_VERSION_1_2(load, userptr); glad_gl_load_GL_VERSION_1_3(load, userptr); glad_gl_load_GL_VERSION_1_4(load, userptr); glad_gl_load_GL_VERSION_1_5(load, userptr); glad_gl_load_GL_VERSION_2_0(load, userptr); glad_gl_load_GL_VERSION_2_1(load, userptr); glad_gl_load_GL_VERSION_3_0(load, userptr); glad_gl_load_GL_VERSION_3_1(load, userptr); glad_gl_load_GL_VERSION_3_2(load, userptr); glad_gl_load_GL_VERSION_3_3(load, userptr); if (!glad_gl_find_extensions_gl(version)) return 0; glad_gl_load_GL_ARB_ES2_compatibility(load, userptr); glad_gl_load_GL_ARB_ES3_1_compatibility(load, userptr); glad_gl_load_GL_ARB_ES3_2_compatibility(load, userptr); glad_gl_load_GL_ARB_base_instance(load, userptr); glad_gl_load_GL_ARB_bindless_texture(load, userptr); glad_gl_load_GL_ARB_blend_func_extended(load, userptr); glad_gl_load_GL_ARB_buffer_storage(load, userptr); glad_gl_load_GL_ARB_cl_event(load, userptr); glad_gl_load_GL_ARB_clear_buffer_object(load, userptr); glad_gl_load_GL_ARB_clear_texture(load, userptr); glad_gl_load_GL_ARB_clip_control(load, userptr); glad_gl_load_GL_ARB_color_buffer_float(load, userptr); glad_gl_load_GL_ARB_compute_shader(load, userptr); glad_gl_load_GL_ARB_compute_variable_group_size(load, userptr); glad_gl_load_GL_ARB_copy_buffer(load, userptr); glad_gl_load_GL_ARB_copy_image(load, userptr); glad_gl_load_GL_ARB_debug_output(load, userptr); glad_gl_load_GL_ARB_direct_state_access(load, userptr); glad_gl_load_GL_ARB_draw_buffers(load, userptr); glad_gl_load_GL_ARB_draw_buffers_blend(load, userptr); glad_gl_load_GL_ARB_draw_elements_base_vertex(load, userptr); glad_gl_load_GL_ARB_draw_indirect(load, userptr); glad_gl_load_GL_ARB_draw_instanced(load, userptr); glad_gl_load_GL_ARB_fragment_program(load, userptr); glad_gl_load_GL_ARB_framebuffer_no_attachments(load, userptr); glad_gl_load_GL_ARB_framebuffer_object(load, userptr); glad_gl_load_GL_ARB_geometry_shader4(load, userptr); glad_gl_load_GL_ARB_get_program_binary(load, userptr); glad_gl_load_GL_ARB_get_texture_sub_image(load, userptr); glad_gl_load_GL_ARB_gl_spirv(load, userptr); glad_gl_load_GL_ARB_gpu_shader_fp64(load, userptr); glad_gl_load_GL_ARB_gpu_shader_int64(load, userptr); glad_gl_load_GL_ARB_imaging(load, userptr); glad_gl_load_GL_ARB_indirect_parameters(load, userptr); glad_gl_load_GL_ARB_instanced_arrays(load, userptr); glad_gl_load_GL_ARB_internalformat_query(load, userptr); glad_gl_load_GL_ARB_internalformat_query2(load, userptr); glad_gl_load_GL_ARB_invalidate_subdata(load, userptr); glad_gl_load_GL_ARB_map_buffer_range(load, userptr); glad_gl_load_GL_ARB_matrix_palette(load, userptr); glad_gl_load_GL_ARB_multi_bind(load, userptr); glad_gl_load_GL_ARB_multi_draw_indirect(load, userptr); glad_gl_load_GL_ARB_multisample(load, userptr); glad_gl_load_GL_ARB_multitexture(load, userptr); glad_gl_load_GL_ARB_occlusion_query(load, userptr); glad_gl_load_GL_ARB_parallel_shader_compile(load, userptr); glad_gl_load_GL_ARB_point_parameters(load, userptr); glad_gl_load_GL_ARB_polygon_offset_clamp(load, userptr); glad_gl_load_GL_ARB_program_interface_query(load, userptr); glad_gl_load_GL_ARB_provoking_vertex(load, userptr); glad_gl_load_GL_ARB_robustness(load, userptr); glad_gl_load_GL_ARB_sample_locations(load, userptr); glad_gl_load_GL_ARB_sample_shading(load, userptr); glad_gl_load_GL_ARB_sampler_objects(load, userptr); glad_gl_load_GL_ARB_separate_shader_objects(load, userptr); glad_gl_load_GL_ARB_shader_atomic_counters(load, userptr); glad_gl_load_GL_ARB_shader_image_load_store(load, userptr); glad_gl_load_GL_ARB_shader_objects(load, userptr); glad_gl_load_GL_ARB_shader_storage_buffer_object(load, userptr); glad_gl_load_GL_ARB_shader_subroutine(load, userptr); glad_gl_load_GL_ARB_shading_language_include(load, userptr); glad_gl_load_GL_ARB_sparse_buffer(load, userptr); glad_gl_load_GL_ARB_sparse_texture(load, userptr); glad_gl_load_GL_ARB_sync(load, userptr); glad_gl_load_GL_ARB_tessellation_shader(load, userptr); glad_gl_load_GL_ARB_texture_barrier(load, userptr); glad_gl_load_GL_ARB_texture_buffer_object(load, userptr); glad_gl_load_GL_ARB_texture_buffer_range(load, userptr); glad_gl_load_GL_ARB_texture_compression(load, userptr); glad_gl_load_GL_ARB_texture_multisample(load, userptr); glad_gl_load_GL_ARB_texture_storage(load, userptr); glad_gl_load_GL_ARB_texture_storage_multisample(load, userptr); glad_gl_load_GL_ARB_texture_view(load, userptr); glad_gl_load_GL_ARB_timer_query(load, userptr); glad_gl_load_GL_ARB_transform_feedback2(load, userptr); glad_gl_load_GL_ARB_transform_feedback3(load, userptr); glad_gl_load_GL_ARB_transform_feedback_instanced(load, userptr); glad_gl_load_GL_ARB_transpose_matrix(load, userptr); glad_gl_load_GL_ARB_uniform_buffer_object(load, userptr); glad_gl_load_GL_ARB_vertex_array_object(load, userptr); glad_gl_load_GL_ARB_vertex_attrib_64bit(load, userptr); glad_gl_load_GL_ARB_vertex_attrib_binding(load, userptr); glad_gl_load_GL_ARB_vertex_blend(load, userptr); glad_gl_load_GL_ARB_vertex_buffer_object(load, userptr); glad_gl_load_GL_ARB_vertex_program(load, userptr); glad_gl_load_GL_ARB_vertex_shader(load, userptr); glad_gl_load_GL_ARB_vertex_type_2_10_10_10_rev(load, userptr); glad_gl_load_GL_ARB_viewport_array(load, userptr); glad_gl_load_GL_ARB_window_pos(load, userptr); glad_gl_load_GL_KHR_blend_equation_advanced(load, userptr); glad_gl_load_GL_KHR_debug(load, userptr); glad_gl_load_GL_KHR_parallel_shader_compile(load, userptr); glad_gl_load_GL_KHR_robustness(load, userptr); return version; } int gladLoadGL( GLADloadfunc load) { return gladLoadGLUserPtr( glad_gl_get_proc_from_userptr, GLAD_GNUC_EXTENSION (void*) load); } #endif /* GLAD_GL_IMPLEMENTATION */ #endif /* __EMSCRIPTEN__ */ #line 0 #line 1 "3rd_font_md.h" // Generated by https://github.com/juliettef/IconFontCppHeaders script GenerateIconFontCppHeaders.py for languages C and C++ // from https://github.com/google/material-design-icons/raw/master/font/MaterialIcons-Regular.codepoints // for use with https://github.com/google/material-design-icons/blob/master/font/MaterialIcons-Regular.ttf #ifndef ICON_MD_H #define ICON_MD_H #define FONT_ICON_FILE_NAME_MD "MaterialIcons-Regular.ttf" #define ICON_MIN_MD 0xe000 #define ICON_MAX_16_MD 0xf8ff #define ICON_MAX_MD 0x10fffd #define ICON_MD_10K "\xee\xa5\x91" // U+e951 #define ICON_MD_10MP "\xee\xa5\x92" // U+e952 #define ICON_MD_11MP "\xee\xa5\x93" // U+e953 #define ICON_MD_123 "\xee\xae\x8d" // U+eb8d #define ICON_MD_12MP "\xee\xa5\x94" // U+e954 #define ICON_MD_13MP "\xee\xa5\x95" // U+e955 #define ICON_MD_14MP "\xee\xa5\x96" // U+e956 #define ICON_MD_15MP "\xee\xa5\x97" // U+e957 #define ICON_MD_16MP "\xee\xa5\x98" // U+e958 #define ICON_MD_17MP "\xee\xa5\x99" // U+e959 #define ICON_MD_18_UP_RATING "\xef\xa3\xbd" // U+f8fd #define ICON_MD_18MP "\xee\xa5\x9a" // U+e95a #define ICON_MD_19MP "\xee\xa5\x9b" // U+e95b #define ICON_MD_1K "\xee\xa5\x9c" // U+e95c #define ICON_MD_1K_PLUS "\xee\xa5\x9d" // U+e95d #define ICON_MD_1X_MOBILEDATA "\xee\xbf\x8d" // U+efcd #define ICON_MD_20MP "\xee\xa5\x9e" // U+e95e #define ICON_MD_21MP "\xee\xa5\x9f" // U+e95f #define ICON_MD_22MP "\xee\xa5\xa0" // U+e960 #define ICON_MD_23MP "\xee\xa5\xa1" // U+e961 #define ICON_MD_24MP "\xee\xa5\xa2" // U+e962 #define ICON_MD_2K "\xee\xa5\xa3" // U+e963 #define ICON_MD_2K_PLUS "\xee\xa5\xa4" // U+e964 #define ICON_MD_2MP "\xee\xa5\xa5" // U+e965 #define ICON_MD_30FPS "\xee\xbf\x8e" // U+efce #define ICON_MD_30FPS_SELECT "\xee\xbf\x8f" // U+efcf #define ICON_MD_360 "\xee\x95\xb7" // U+e577 #define ICON_MD_3D_ROTATION "\xee\xa1\x8d" // U+e84d #define ICON_MD_3G_MOBILEDATA "\xee\xbf\x90" // U+efd0 #define ICON_MD_3K "\xee\xa5\xa6" // U+e966 #define ICON_MD_3K_PLUS "\xee\xa5\xa7" // U+e967 #define ICON_MD_3MP "\xee\xa5\xa8" // U+e968 #define ICON_MD_3P "\xee\xbf\x91" // U+efd1 #define ICON_MD_4G_MOBILEDATA "\xee\xbf\x92" // U+efd2 #define ICON_MD_4G_PLUS_MOBILEDATA "\xee\xbf\x93" // U+efd3 #define ICON_MD_4K "\xee\x81\xb2" // U+e072 #define ICON_MD_4K_PLUS "\xee\xa5\xa9" // U+e969 #define ICON_MD_4MP "\xee\xa5\xaa" // U+e96a #define ICON_MD_5G "\xee\xbc\xb8" // U+ef38 #define ICON_MD_5K "\xee\xa5\xab" // U+e96b #define ICON_MD_5K_PLUS "\xee\xa5\xac" // U+e96c #define ICON_MD_5MP "\xee\xa5\xad" // U+e96d #define ICON_MD_60FPS "\xee\xbf\x94" // U+efd4 #define ICON_MD_60FPS_SELECT "\xee\xbf\x95" // U+efd5 #define ICON_MD_6_FT_APART "\xef\x88\x9e" // U+f21e #define ICON_MD_6K "\xee\xa5\xae" // U+e96e #define ICON_MD_6K_PLUS "\xee\xa5\xaf" // U+e96f #define ICON_MD_6MP "\xee\xa5\xb0" // U+e970 #define ICON_MD_7K "\xee\xa5\xb1" // U+e971 #define ICON_MD_7K_PLUS "\xee\xa5\xb2" // U+e972 #define ICON_MD_7MP "\xee\xa5\xb3" // U+e973 #define ICON_MD_8K "\xee\xa5\xb4" // U+e974 #define ICON_MD_8K_PLUS "\xee\xa5\xb5" // U+e975 #define ICON_MD_8MP "\xee\xa5\xb6" // U+e976 #define ICON_MD_9K "\xee\xa5\xb7" // U+e977 #define ICON_MD_9K_PLUS "\xee\xa5\xb8" // U+e978 #define ICON_MD_9MP "\xee\xa5\xb9" // U+e979 #define ICON_MD_ABC "\xee\xae\x94" // U+eb94 #define ICON_MD_AC_UNIT "\xee\xac\xbb" // U+eb3b #define ICON_MD_ACCESS_ALARM "\xee\x86\x90" // U+e190 #define ICON_MD_ACCESS_ALARMS "\xee\x86\x91" // U+e191 #define ICON_MD_ACCESS_TIME "\xee\x86\x92" // U+e192 #define ICON_MD_ACCESS_TIME_FILLED "\xee\xbf\x96" // U+efd6 #define ICON_MD_ACCESSIBILITY "\xee\xa1\x8e" // U+e84e #define ICON_MD_ACCESSIBILITY_NEW "\xee\xa4\xac" // U+e92c #define ICON_MD_ACCESSIBLE "\xee\xa4\x94" // U+e914 #define ICON_MD_ACCESSIBLE_FORWARD "\xee\xa4\xb4" // U+e934 #define ICON_MD_ACCOUNT_BALANCE "\xee\xa1\x8f" // U+e84f #define ICON_MD_ACCOUNT_BALANCE_WALLET "\xee\xa1\x90" // U+e850 #define ICON_MD_ACCOUNT_BOX "\xee\xa1\x91" // U+e851 #define ICON_MD_ACCOUNT_CIRCLE "\xee\xa1\x93" // U+e853 #define ICON_MD_ACCOUNT_TREE "\xee\xa5\xba" // U+e97a #define ICON_MD_AD_UNITS "\xee\xbc\xb9" // U+ef39 #define ICON_MD_ADB "\xee\x98\x8e" // U+e60e #define ICON_MD_ADD "\xee\x85\x85" // U+e145 #define ICON_MD_ADD_A_PHOTO "\xee\x90\xb9" // U+e439 #define ICON_MD_ADD_ALARM "\xee\x86\x93" // U+e193 #define ICON_MD_ADD_ALERT "\xee\x80\x83" // U+e003 #define ICON_MD_ADD_BOX "\xee\x85\x86" // U+e146 #define ICON_MD_ADD_BUSINESS "\xee\x9c\xa9" // U+e729 #define ICON_MD_ADD_CALL "\xee\x83\xa8" // U+e0e8 #define ICON_MD_ADD_CARD "\xee\xae\x86" // U+eb86 #define ICON_MD_ADD_CHART "\xee\xa5\xbb" // U+e97b #define ICON_MD_ADD_CIRCLE "\xee\x85\x87" // U+e147 #define ICON_MD_ADD_CIRCLE_OUTLINE "\xee\x85\x88" // U+e148 #define ICON_MD_ADD_COMMENT "\xee\x89\xa6" // U+e266 #define ICON_MD_ADD_HOME "\xef\xa3\xab" // U+f8eb #define ICON_MD_ADD_HOME_WORK "\xef\xa3\xad" // U+f8ed #define ICON_MD_ADD_IC_CALL "\xee\xa5\xbc" // U+e97c #define ICON_MD_ADD_LINK "\xee\x85\xb8" // U+e178 #define ICON_MD_ADD_LOCATION "\xee\x95\xa7" // U+e567 #define ICON_MD_ADD_LOCATION_ALT "\xee\xbc\xba" // U+ef3a #define ICON_MD_ADD_MODERATOR "\xee\xa5\xbd" // U+e97d #define ICON_MD_ADD_PHOTO_ALTERNATE "\xee\x90\xbe" // U+e43e #define ICON_MD_ADD_REACTION "\xee\x87\x93" // U+e1d3 #define ICON_MD_ADD_ROAD "\xee\xbc\xbb" // U+ef3b #define ICON_MD_ADD_SHOPPING_CART "\xee\xa1\x94" // U+e854 #define ICON_MD_ADD_TASK "\xef\x88\xba" // U+f23a #define ICON_MD_ADD_TO_DRIVE "\xee\x99\x9c" // U+e65c #define ICON_MD_ADD_TO_HOME_SCREEN "\xee\x87\xbe" // U+e1fe #define ICON_MD_ADD_TO_PHOTOS "\xee\x8e\x9d" // U+e39d #define ICON_MD_ADD_TO_QUEUE "\xee\x81\x9c" // U+e05c #define ICON_MD_ADDCHART "\xee\xbc\xbc" // U+ef3c #define ICON_MD_ADF_SCANNER "\xee\xab\x9a" // U+eada #define ICON_MD_ADJUST "\xee\x8e\x9e" // U+e39e #define ICON_MD_ADMIN_PANEL_SETTINGS "\xee\xbc\xbd" // U+ef3d #define ICON_MD_ADOBE "\xee\xaa\x96" // U+ea96 #define ICON_MD_ADS_CLICK "\xee\x9d\xa2" // U+e762 #define ICON_MD_AGRICULTURE "\xee\xa9\xb9" // U+ea79 #define ICON_MD_AIR "\xee\xbf\x98" // U+efd8 #define ICON_MD_AIRLINE_SEAT_FLAT "\xee\x98\xb0" // U+e630 #define ICON_MD_AIRLINE_SEAT_FLAT_ANGLED "\xee\x98\xb1" // U+e631 #define ICON_MD_AIRLINE_SEAT_INDIVIDUAL_SUITE "\xee\x98\xb2" // U+e632 #define ICON_MD_AIRLINE_SEAT_LEGROOM_EXTRA "\xee\x98\xb3" // U+e633 #define ICON_MD_AIRLINE_SEAT_LEGROOM_NORMAL "\xee\x98\xb4" // U+e634 #define ICON_MD_AIRLINE_SEAT_LEGROOM_REDUCED "\xee\x98\xb5" // U+e635 #define ICON_MD_AIRLINE_SEAT_RECLINE_EXTRA "\xee\x98\xb6" // U+e636 #define ICON_MD_AIRLINE_SEAT_RECLINE_NORMAL "\xee\x98\xb7" // U+e637 #define ICON_MD_AIRLINE_STOPS "\xee\x9f\x90" // U+e7d0 #define ICON_MD_AIRLINES "\xee\x9f\x8a" // U+e7ca #define ICON_MD_AIRPLANE_TICKET "\xee\xbf\x99" // U+efd9 #define ICON_MD_AIRPLANEMODE_ACTIVE "\xee\x86\x95" // U+e195 #define ICON_MD_AIRPLANEMODE_INACTIVE "\xee\x86\x94" // U+e194 #define ICON_MD_AIRPLANEMODE_OFF "\xee\x86\x94" // U+e194 #define ICON_MD_AIRPLANEMODE_ON "\xee\x86\x95" // U+e195 #define ICON_MD_AIRPLAY "\xee\x81\x95" // U+e055 #define ICON_MD_AIRPORT_SHUTTLE "\xee\xac\xbc" // U+eb3c #define ICON_MD_ALARM "\xee\xa1\x95" // U+e855 #define ICON_MD_ALARM_ADD "\xee\xa1\x96" // U+e856 #define ICON_MD_ALARM_OFF "\xee\xa1\x97" // U+e857 #define ICON_MD_ALARM_ON "\xee\xa1\x98" // U+e858 #define ICON_MD_ALBUM "\xee\x80\x99" // U+e019 #define ICON_MD_ALIGN_HORIZONTAL_CENTER "\xee\x80\x8f" // U+e00f #define ICON_MD_ALIGN_HORIZONTAL_LEFT "\xee\x80\x8d" // U+e00d #define ICON_MD_ALIGN_HORIZONTAL_RIGHT "\xee\x80\x90" // U+e010 #define ICON_MD_ALIGN_VERTICAL_BOTTOM "\xee\x80\x95" // U+e015 #define ICON_MD_ALIGN_VERTICAL_CENTER "\xee\x80\x91" // U+e011 #define ICON_MD_ALIGN_VERTICAL_TOP "\xee\x80\x8c" // U+e00c #define ICON_MD_ALL_INBOX "\xee\xa5\xbf" // U+e97f #define ICON_MD_ALL_INCLUSIVE "\xee\xac\xbd" // U+eb3d #define ICON_MD_ALL_OUT "\xee\xa4\x8b" // U+e90b #define ICON_MD_ALT_ROUTE "\xef\x86\x84" // U+f184 #define ICON_MD_ALTERNATE_EMAIL "\xee\x83\xa6" // U+e0e6 #define ICON_MD_AMP_STORIES "\xee\xa8\x93" // U+ea13 #define ICON_MD_ANALYTICS "\xee\xbc\xbe" // U+ef3e #define ICON_MD_ANCHOR "\xef\x87\x8d" // U+f1cd #define ICON_MD_ANDROID "\xee\xa1\x99" // U+e859 #define ICON_MD_ANIMATION "\xee\x9c\x9c" // U+e71c #define ICON_MD_ANNOUNCEMENT "\xee\xa1\x9a" // U+e85a #define ICON_MD_AOD "\xee\xbf\x9a" // U+efda #define ICON_MD_APARTMENT "\xee\xa9\x80" // U+ea40 #define ICON_MD_API "\xef\x86\xb7" // U+f1b7 #define ICON_MD_APP_BLOCKING "\xee\xbc\xbf" // U+ef3f #define ICON_MD_APP_REGISTRATION "\xee\xbd\x80" // U+ef40 #define ICON_MD_APP_SETTINGS_ALT "\xee\xbd\x81" // U+ef41 #define ICON_MD_APP_SHORTCUT "\xee\xab\xa4" // U+eae4 #define ICON_MD_APPLE "\xee\xaa\x80" // U+ea80 #define ICON_MD_APPROVAL "\xee\xa6\x82" // U+e982 #define ICON_MD_APPS "\xee\x97\x83" // U+e5c3 #define ICON_MD_APPS_OUTAGE "\xee\x9f\x8c" // U+e7cc #define ICON_MD_ARCHITECTURE "\xee\xa8\xbb" // U+ea3b #define ICON_MD_ARCHIVE "\xee\x85\x89" // U+e149 #define ICON_MD_AREA_CHART "\xee\x9d\xb0" // U+e770 #define ICON_MD_ARROW_BACK "\xee\x97\x84" // U+e5c4 #define ICON_MD_ARROW_BACK_IOS "\xee\x97\xa0" // U+e5e0 #define ICON_MD_ARROW_BACK_IOS_NEW "\xee\x8b\xaa" // U+e2ea #define ICON_MD_ARROW_CIRCLE_DOWN "\xef\x86\x81" // U+f181 #define ICON_MD_ARROW_CIRCLE_LEFT "\xee\xaa\xa7" // U+eaa7 #define ICON_MD_ARROW_CIRCLE_RIGHT "\xee\xaa\xaa" // U+eaaa #define ICON_MD_ARROW_CIRCLE_UP "\xef\x86\x82" // U+f182 #define ICON_MD_ARROW_DOWNWARD "\xee\x97\x9b" // U+e5db #define ICON_MD_ARROW_DROP_DOWN "\xee\x97\x85" // U+e5c5 #define ICON_MD_ARROW_DROP_DOWN_CIRCLE "\xee\x97\x86" // U+e5c6 #define ICON_MD_ARROW_DROP_UP "\xee\x97\x87" // U+e5c7 #define ICON_MD_ARROW_FORWARD "\xee\x97\x88" // U+e5c8 #define ICON_MD_ARROW_FORWARD_IOS "\xee\x97\xa1" // U+e5e1 #define ICON_MD_ARROW_LEFT "\xee\x97\x9e" // U+e5de #define ICON_MD_ARROW_OUTWARD "\xef\xa3\x8e" // U+f8ce #define ICON_MD_ARROW_RIGHT "\xee\x97\x9f" // U+e5df #define ICON_MD_ARROW_RIGHT_ALT "\xee\xa5\x81" // U+e941 #define ICON_MD_ARROW_UPWARD "\xee\x97\x98" // U+e5d8 #define ICON_MD_ART_TRACK "\xee\x81\xa0" // U+e060 #define ICON_MD_ARTICLE "\xee\xbd\x82" // U+ef42 #define ICON_MD_ASPECT_RATIO "\xee\xa1\x9b" // U+e85b #define ICON_MD_ASSESSMENT "\xee\xa1\x9c" // U+e85c #define ICON_MD_ASSIGNMENT "\xee\xa1\x9d" // U+e85d #define ICON_MD_ASSIGNMENT_IND "\xee\xa1\x9e" // U+e85e #define ICON_MD_ASSIGNMENT_LATE "\xee\xa1\x9f" // U+e85f #define ICON_MD_ASSIGNMENT_RETURN "\xee\xa1\xa0" // U+e860 #define ICON_MD_ASSIGNMENT_RETURNED "\xee\xa1\xa1" // U+e861 #define ICON_MD_ASSIGNMENT_TURNED_IN "\xee\xa1\xa2" // U+e862 #define ICON_MD_ASSIST_WALKER "\xef\xa3\x95" // U+f8d5 #define ICON_MD_ASSISTANT "\xee\x8e\x9f" // U+e39f #define ICON_MD_ASSISTANT_DIRECTION "\xee\xa6\x88" // U+e988 #define ICON_MD_ASSISTANT_NAVIGATION "\xee\xa6\x89" // U+e989 #define ICON_MD_ASSISTANT_PHOTO "\xee\x8e\xa0" // U+e3a0 #define ICON_MD_ASSURED_WORKLOAD "\xee\xad\xaf" // U+eb6f #define ICON_MD_ATM "\xee\x95\xb3" // U+e573 #define ICON_MD_ATTACH_EMAIL "\xee\xa9\x9e" // U+ea5e #define ICON_MD_ATTACH_FILE "\xee\x88\xa6" // U+e226 #define ICON_MD_ATTACH_MONEY "\xee\x88\xa7" // U+e227 #define ICON_MD_ATTACHMENT "\xee\x8a\xbc" // U+e2bc #define ICON_MD_ATTRACTIONS "\xee\xa9\x92" // U+ea52 #define ICON_MD_ATTRIBUTION "\xee\xbf\x9b" // U+efdb #define ICON_MD_AUDIO_FILE "\xee\xae\x82" // U+eb82 #define ICON_MD_AUDIOTRACK "\xee\x8e\xa1" // U+e3a1 #define ICON_MD_AUTO_AWESOME "\xee\x99\x9f" // U+e65f #define ICON_MD_AUTO_AWESOME_MOSAIC "\xee\x99\xa0" // U+e660 #define ICON_MD_AUTO_AWESOME_MOTION "\xee\x99\xa1" // U+e661 #define ICON_MD_AUTO_DELETE "\xee\xa9\x8c" // U+ea4c #define ICON_MD_AUTO_FIX_HIGH "\xee\x99\xa3" // U+e663 #define ICON_MD_AUTO_FIX_NORMAL "\xee\x99\xa4" // U+e664 #define ICON_MD_AUTO_FIX_OFF "\xee\x99\xa5" // U+e665 #define ICON_MD_AUTO_GRAPH "\xee\x93\xbb" // U+e4fb #define ICON_MD_AUTO_MODE "\xee\xb0\xa0" // U+ec20 #define ICON_MD_AUTO_STORIES "\xee\x99\xa6" // U+e666 #define ICON_MD_AUTOFPS_SELECT "\xee\xbf\x9c" // U+efdc #define ICON_MD_AUTORENEW "\xee\xa1\xa3" // U+e863 #define ICON_MD_AV_TIMER "\xee\x80\x9b" // U+e01b #define ICON_MD_BABY_CHANGING_STATION "\xef\x86\x9b" // U+f19b #define ICON_MD_BACK_HAND "\xee\x9d\xa4" // U+e764 #define ICON_MD_BACKPACK "\xef\x86\x9c" // U+f19c #define ICON_MD_BACKSPACE "\xee\x85\x8a" // U+e14a #define ICON_MD_BACKUP "\xee\xa1\xa4" // U+e864 #define ICON_MD_BACKUP_TABLE "\xee\xbd\x83" // U+ef43 #define ICON_MD_BADGE "\xee\xa9\xa7" // U+ea67 #define ICON_MD_BAKERY_DINING "\xee\xa9\x93" // U+ea53 #define ICON_MD_BALANCE "\xee\xab\xb6" // U+eaf6 #define ICON_MD_BALCONY "\xee\x96\x8f" // U+e58f #define ICON_MD_BALLOT "\xee\x85\xb2" // U+e172 #define ICON_MD_BAR_CHART "\xee\x89\xab" // U+e26b #define ICON_MD_BATCH_PREDICTION "\xef\x83\xb5" // U+f0f5 #define ICON_MD_BATHROOM "\xee\xbf\x9d" // U+efdd #define ICON_MD_BATHTUB "\xee\xa9\x81" // U+ea41 #define ICON_MD_BATTERY_0_BAR "\xee\xaf\x9c" // U+ebdc #define ICON_MD_BATTERY_1_BAR "\xee\xaf\x99" // U+ebd9 #define ICON_MD_BATTERY_2_BAR "\xee\xaf\xa0" // U+ebe0 #define ICON_MD_BATTERY_3_BAR "\xee\xaf\x9d" // U+ebdd #define ICON_MD_BATTERY_4_BAR "\xee\xaf\xa2" // U+ebe2 #define ICON_MD_BATTERY_5_BAR "\xee\xaf\x94" // U+ebd4 #define ICON_MD_BATTERY_6_BAR "\xee\xaf\x92" // U+ebd2 #define ICON_MD_BATTERY_ALERT "\xee\x86\x9c" // U+e19c #define ICON_MD_BATTERY_CHARGING_FULL "\xee\x86\xa3" // U+e1a3 #define ICON_MD_BATTERY_FULL "\xee\x86\xa4" // U+e1a4 #define ICON_MD_BATTERY_SAVER "\xee\xbf\x9e" // U+efde #define ICON_MD_BATTERY_STD "\xee\x86\xa5" // U+e1a5 #define ICON_MD_BATTERY_UNKNOWN "\xee\x86\xa6" // U+e1a6 #define ICON_MD_BEACH_ACCESS "\xee\xac\xbe" // U+eb3e #define ICON_MD_BED "\xee\xbf\x9f" // U+efdf #define ICON_MD_BEDROOM_BABY "\xee\xbf\xa0" // U+efe0 #define ICON_MD_BEDROOM_CHILD "\xee\xbf\xa1" // U+efe1 #define ICON_MD_BEDROOM_PARENT "\xee\xbf\xa2" // U+efe2 #define ICON_MD_BEDTIME "\xee\xbd\x84" // U+ef44 #define ICON_MD_BEDTIME_OFF "\xee\xad\xb6" // U+eb76 #define ICON_MD_BEENHERE "\xee\x94\xad" // U+e52d #define ICON_MD_BENTO "\xef\x87\xb4" // U+f1f4 #define ICON_MD_BIKE_SCOOTER "\xee\xbd\x85" // U+ef45 #define ICON_MD_BIOTECH "\xee\xa8\xba" // U+ea3a #define ICON_MD_BLENDER "\xee\xbf\xa3" // U+efe3 #define ICON_MD_BLIND "\xef\xa3\x96" // U+f8d6 #define ICON_MD_BLINDS "\xee\x8a\x86" // U+e286 #define ICON_MD_BLINDS_CLOSED "\xee\xb0\x9f" // U+ec1f #define ICON_MD_BLOCK "\xee\x85\x8b" // U+e14b #define ICON_MD_BLOCK_FLIPPED "\xee\xbd\x86" // U+ef46 #define ICON_MD_BLOODTYPE "\xee\xbf\xa4" // U+efe4 #define ICON_MD_BLUETOOTH "\xee\x86\xa7" // U+e1a7 #define ICON_MD_BLUETOOTH_AUDIO "\xee\x98\x8f" // U+e60f #define ICON_MD_BLUETOOTH_CONNECTED "\xee\x86\xa8" // U+e1a8 #define ICON_MD_BLUETOOTH_DISABLED "\xee\x86\xa9" // U+e1a9 #define ICON_MD_BLUETOOTH_DRIVE "\xee\xbf\xa5" // U+efe5 #define ICON_MD_BLUETOOTH_SEARCHING "\xee\x86\xaa" // U+e1aa #define ICON_MD_BLUR_CIRCULAR "\xee\x8e\xa2" // U+e3a2 #define ICON_MD_BLUR_LINEAR "\xee\x8e\xa3" // U+e3a3 #define ICON_MD_BLUR_OFF "\xee\x8e\xa4" // U+e3a4 #define ICON_MD_BLUR_ON "\xee\x8e\xa5" // U+e3a5 #define ICON_MD_BOLT "\xee\xa8\x8b" // U+ea0b #define ICON_MD_BOOK "\xee\xa1\xa5" // U+e865 #define ICON_MD_BOOK_ONLINE "\xef\x88\x97" // U+f217 #define ICON_MD_BOOKMARK "\xee\xa1\xa6" // U+e866 #define ICON_MD_BOOKMARK_ADD "\xee\x96\x98" // U+e598 #define ICON_MD_BOOKMARK_ADDED "\xee\x96\x99" // U+e599 #define ICON_MD_BOOKMARK_BORDER "\xee\xa1\xa7" // U+e867 #define ICON_MD_BOOKMARK_OUTLINE "\xee\xa1\xa7" // U+e867 #define ICON_MD_BOOKMARK_REMOVE "\xee\x96\x9a" // U+e59a #define ICON_MD_BOOKMARKS "\xee\xa6\x8b" // U+e98b #define ICON_MD_BORDER_ALL "\xee\x88\xa8" // U+e228 #define ICON_MD_BORDER_BOTTOM "\xee\x88\xa9" // U+e229 #define ICON_MD_BORDER_CLEAR "\xee\x88\xaa" // U+e22a #define ICON_MD_BORDER_COLOR "\xee\x88\xab" // U+e22b #define ICON_MD_BORDER_HORIZONTAL "\xee\x88\xac" // U+e22c #define ICON_MD_BORDER_INNER "\xee\x88\xad" // U+e22d #define ICON_MD_BORDER_LEFT "\xee\x88\xae" // U+e22e #define ICON_MD_BORDER_OUTER "\xee\x88\xaf" // U+e22f #define ICON_MD_BORDER_RIGHT "\xee\x88\xb0" // U+e230 #define ICON_MD_BORDER_STYLE "\xee\x88\xb1" // U+e231 #define ICON_MD_BORDER_TOP "\xee\x88\xb2" // U+e232 #define ICON_MD_BORDER_VERTICAL "\xee\x88\xb3" // U+e233 #define ICON_MD_BOY "\xee\xad\xa7" // U+eb67 #define ICON_MD_BRANDING_WATERMARK "\xee\x81\xab" // U+e06b #define ICON_MD_BREAKFAST_DINING "\xee\xa9\x94" // U+ea54 #define ICON_MD_BRIGHTNESS_1 "\xee\x8e\xa6" // U+e3a6 #define ICON_MD_BRIGHTNESS_2 "\xee\x8e\xa7" // U+e3a7 #define ICON_MD_BRIGHTNESS_3 "\xee\x8e\xa8" // U+e3a8 #define ICON_MD_BRIGHTNESS_4 "\xee\x8e\xa9" // U+e3a9 #define ICON_MD_BRIGHTNESS_5 "\xee\x8e\xaa" // U+e3aa #define ICON_MD_BRIGHTNESS_6 "\xee\x8e\xab" // U+e3ab #define ICON_MD_BRIGHTNESS_7 "\xee\x8e\xac" // U+e3ac #define ICON_MD_BRIGHTNESS_AUTO "\xee\x86\xab" // U+e1ab #define ICON_MD_BRIGHTNESS_HIGH "\xee\x86\xac" // U+e1ac #define ICON_MD_BRIGHTNESS_LOW "\xee\x86\xad" // U+e1ad #define ICON_MD_BRIGHTNESS_MEDIUM "\xee\x86\xae" // U+e1ae #define ICON_MD_BROADCAST_ON_HOME "\xef\xa3\xb8" // U+f8f8 #define ICON_MD_BROADCAST_ON_PERSONAL "\xef\xa3\xb9" // U+f8f9 #define ICON_MD_BROKEN_IMAGE "\xee\x8e\xad" // U+e3ad #define ICON_MD_BROWSE_GALLERY "\xee\xaf\x91" // U+ebd1 #define ICON_MD_BROWSER_NOT_SUPPORTED "\xee\xbd\x87" // U+ef47 #define ICON_MD_BROWSER_UPDATED "\xee\x9f\x8f" // U+e7cf #define ICON_MD_BRUNCH_DINING "\xee\xa9\xb3" // U+ea73 #define ICON_MD_BRUSH "\xee\x8e\xae" // U+e3ae #define ICON_MD_BUBBLE_CHART "\xee\x9b\x9d" // U+e6dd #define ICON_MD_BUG_REPORT "\xee\xa1\xa8" // U+e868 #define ICON_MD_BUILD "\xee\xa1\xa9" // U+e869 #define ICON_MD_BUILD_CIRCLE "\xee\xbd\x88" // U+ef48 #define ICON_MD_BUNGALOW "\xee\x96\x91" // U+e591 #define ICON_MD_BURST_MODE "\xee\x90\xbc" // U+e43c #define ICON_MD_BUS_ALERT "\xee\xa6\x8f" // U+e98f #define ICON_MD_BUSINESS "\xee\x82\xaf" // U+e0af #define ICON_MD_BUSINESS_CENTER "\xee\xac\xbf" // U+eb3f #define ICON_MD_CABIN "\xee\x96\x89" // U+e589 #define ICON_MD_CABLE "\xee\xbf\xa6" // U+efe6 #define ICON_MD_CACHED "\xee\xa1\xaa" // U+e86a #define ICON_MD_CAKE "\xee\x9f\xa9" // U+e7e9 #define ICON_MD_CALCULATE "\xee\xa9\x9f" // U+ea5f #define ICON_MD_CALENDAR_MONTH "\xee\xaf\x8c" // U+ebcc #define ICON_MD_CALENDAR_TODAY "\xee\xa4\xb5" // U+e935 #define ICON_MD_CALENDAR_VIEW_DAY "\xee\xa4\xb6" // U+e936 #define ICON_MD_CALENDAR_VIEW_MONTH "\xee\xbf\xa7" // U+efe7 #define ICON_MD_CALENDAR_VIEW_WEEK "\xee\xbf\xa8" // U+efe8 #define ICON_MD_CALL "\xee\x82\xb0" // U+e0b0 #define ICON_MD_CALL_END "\xee\x82\xb1" // U+e0b1 #define ICON_MD_CALL_MADE "\xee\x82\xb2" // U+e0b2 #define ICON_MD_CALL_MERGE "\xee\x82\xb3" // U+e0b3 #define ICON_MD_CALL_MISSED "\xee\x82\xb4" // U+e0b4 #define ICON_MD_CALL_MISSED_OUTGOING "\xee\x83\xa4" // U+e0e4 #define ICON_MD_CALL_RECEIVED "\xee\x82\xb5" // U+e0b5 #define ICON_MD_CALL_SPLIT "\xee\x82\xb6" // U+e0b6 #define ICON_MD_CALL_TO_ACTION "\xee\x81\xac" // U+e06c #define ICON_MD_CAMERA "\xee\x8e\xaf" // U+e3af #define ICON_MD_CAMERA_ALT "\xee\x8e\xb0" // U+e3b0 #define ICON_MD_CAMERA_ENHANCE "\xee\xa3\xbc" // U+e8fc #define ICON_MD_CAMERA_FRONT "\xee\x8e\xb1" // U+e3b1 #define ICON_MD_CAMERA_INDOOR "\xee\xbf\xa9" // U+efe9 #define ICON_MD_CAMERA_OUTDOOR "\xee\xbf\xaa" // U+efea #define ICON_MD_CAMERA_REAR "\xee\x8e\xb2" // U+e3b2 #define ICON_MD_CAMERA_ROLL "\xee\x8e\xb3" // U+e3b3 #define ICON_MD_CAMERASWITCH "\xee\xbf\xab" // U+efeb #define ICON_MD_CAMPAIGN "\xee\xbd\x89" // U+ef49 #define ICON_MD_CANCEL "\xee\x97\x89" // U+e5c9 #define ICON_MD_CANCEL_PRESENTATION "\xee\x83\xa9" // U+e0e9 #define ICON_MD_CANCEL_SCHEDULE_SEND "\xee\xa8\xb9" // U+ea39 #define ICON_MD_CANDLESTICK_CHART "\xee\xab\x94" // U+ead4 #define ICON_MD_CAR_CRASH "\xee\xaf\xb2" // U+ebf2 #define ICON_MD_CAR_RENTAL "\xee\xa9\x95" // U+ea55 #define ICON_MD_CAR_REPAIR "\xee\xa9\x96" // U+ea56 #define ICON_MD_CARD_GIFTCARD "\xee\xa3\xb6" // U+e8f6 #define ICON_MD_CARD_MEMBERSHIP "\xee\xa3\xb7" // U+e8f7 #define ICON_MD_CARD_TRAVEL "\xee\xa3\xb8" // U+e8f8 #define ICON_MD_CARPENTER "\xef\x87\xb8" // U+f1f8 #define ICON_MD_CASES "\xee\xa6\x92" // U+e992 #define ICON_MD_CASINO "\xee\xad\x80" // U+eb40 #define ICON_MD_CAST "\xee\x8c\x87" // U+e307 #define ICON_MD_CAST_CONNECTED "\xee\x8c\x88" // U+e308 #define ICON_MD_CAST_FOR_EDUCATION "\xee\xbf\xac" // U+efec #define ICON_MD_CASTLE "\xee\xaa\xb1" // U+eab1 #define ICON_MD_CATCHING_POKEMON "\xee\x94\x88" // U+e508 #define ICON_MD_CATEGORY "\xee\x95\xb4" // U+e574 #define ICON_MD_CELEBRATION "\xee\xa9\xa5" // U+ea65 #define ICON_MD_CELL_TOWER "\xee\xae\xba" // U+ebba #define ICON_MD_CELL_WIFI "\xee\x83\xac" // U+e0ec #define ICON_MD_CENTER_FOCUS_STRONG "\xee\x8e\xb4" // U+e3b4 #define ICON_MD_CENTER_FOCUS_WEAK "\xee\x8e\xb5" // U+e3b5 #define ICON_MD_CHAIR "\xee\xbf\xad" // U+efed #define ICON_MD_CHAIR_ALT "\xee\xbf\xae" // U+efee #define ICON_MD_CHALET "\xee\x96\x85" // U+e585 #define ICON_MD_CHANGE_CIRCLE "\xee\x8b\xa7" // U+e2e7 #define ICON_MD_CHANGE_HISTORY "\xee\xa1\xab" // U+e86b #define ICON_MD_CHARGING_STATION "\xef\x86\x9d" // U+f19d #define ICON_MD_CHAT "\xee\x82\xb7" // U+e0b7 #define ICON_MD_CHAT_BUBBLE "\xee\x83\x8a" // U+e0ca #define ICON_MD_CHAT_BUBBLE_OUTLINE "\xee\x83\x8b" // U+e0cb #define ICON_MD_CHECK "\xee\x97\x8a" // U+e5ca #define ICON_MD_CHECK_BOX "\xee\xa0\xb4" // U+e834 #define ICON_MD_CHECK_BOX_OUTLINE_BLANK "\xee\xa0\xb5" // U+e835 #define ICON_MD_CHECK_CIRCLE "\xee\xa1\xac" // U+e86c #define ICON_MD_CHECK_CIRCLE_OUTLINE "\xee\xa4\xad" // U+e92d #define ICON_MD_CHECKLIST "\xee\x9a\xb1" // U+e6b1 #define ICON_MD_CHECKLIST_RTL "\xee\x9a\xb3" // U+e6b3 #define ICON_MD_CHECKROOM "\xef\x86\x9e" // U+f19e #define ICON_MD_CHEVRON_LEFT "\xee\x97\x8b" // U+e5cb #define ICON_MD_CHEVRON_RIGHT "\xee\x97\x8c" // U+e5cc #define ICON_MD_CHILD_CARE "\xee\xad\x81" // U+eb41 #define ICON_MD_CHILD_FRIENDLY "\xee\xad\x82" // U+eb42 #define ICON_MD_CHROME_READER_MODE "\xee\xa1\xad" // U+e86d #define ICON_MD_CHURCH "\xee\xaa\xae" // U+eaae #define ICON_MD_CIRCLE "\xee\xbd\x8a" // U+ef4a #define ICON_MD_CIRCLE_NOTIFICATIONS "\xee\xa6\x94" // U+e994 #define ICON_MD_CLASS "\xee\xa1\xae" // U+e86e #define ICON_MD_CLEAN_HANDS "\xef\x88\x9f" // U+f21f #define ICON_MD_CLEANING_SERVICES "\xef\x83\xbf" // U+f0ff #define ICON_MD_CLEAR "\xee\x85\x8c" // U+e14c #define ICON_MD_CLEAR_ALL "\xee\x82\xb8" // U+e0b8 #define ICON_MD_CLOSE "\xee\x97\x8d" // U+e5cd #define ICON_MD_CLOSE_FULLSCREEN "\xef\x87\x8f" // U+f1cf #define ICON_MD_CLOSED_CAPTION "\xee\x80\x9c" // U+e01c #define ICON_MD_CLOSED_CAPTION_DISABLED "\xef\x87\x9c" // U+f1dc #define ICON_MD_CLOSED_CAPTION_OFF "\xee\xa6\x96" // U+e996 #define ICON_MD_CLOUD "\xee\x8a\xbd" // U+e2bd #define ICON_MD_CLOUD_CIRCLE "\xee\x8a\xbe" // U+e2be #define ICON_MD_CLOUD_DONE "\xee\x8a\xbf" // U+e2bf #define ICON_MD_CLOUD_DOWNLOAD "\xee\x8b\x80" // U+e2c0 #define ICON_MD_CLOUD_OFF "\xee\x8b\x81" // U+e2c1 #define ICON_MD_CLOUD_QUEUE "\xee\x8b\x82" // U+e2c2 #define ICON_MD_CLOUD_SYNC "\xee\xad\x9a" // U+eb5a #define ICON_MD_CLOUD_UPLOAD "\xee\x8b\x83" // U+e2c3 #define ICON_MD_CLOUDY_SNOWING "\xee\xa0\x90" // U+e810 #define ICON_MD_CO2 "\xee\x9e\xb0" // U+e7b0 #define ICON_MD_CO_PRESENT "\xee\xab\xb0" // U+eaf0 #define ICON_MD_CODE "\xee\xa1\xaf" // U+e86f #define ICON_MD_CODE_OFF "\xee\x93\xb3" // U+e4f3 #define ICON_MD_COFFEE "\xee\xbf\xaf" // U+efef #define ICON_MD_COFFEE_MAKER "\xee\xbf\xb0" // U+eff0 #define ICON_MD_COLLECTIONS "\xee\x8e\xb6" // U+e3b6 #define ICON_MD_COLLECTIONS_BOOKMARK "\xee\x90\xb1" // U+e431 #define ICON_MD_COLOR_LENS "\xee\x8e\xb7" // U+e3b7 #define ICON_MD_COLORIZE "\xee\x8e\xb8" // U+e3b8 #define ICON_MD_COMMENT "\xee\x82\xb9" // U+e0b9 #define ICON_MD_COMMENT_BANK "\xee\xa9\x8e" // U+ea4e #define ICON_MD_COMMENTS_DISABLED "\xee\x9e\xa2" // U+e7a2 #define ICON_MD_COMMIT "\xee\xab\xb5" // U+eaf5 #define ICON_MD_COMMUTE "\xee\xa5\x80" // U+e940 #define ICON_MD_COMPARE "\xee\x8e\xb9" // U+e3b9 #define ICON_MD_COMPARE_ARROWS "\xee\xa4\x95" // U+e915 #define ICON_MD_COMPASS_CALIBRATION "\xee\x95\xbc" // U+e57c #define ICON_MD_COMPOST "\xee\x9d\xa1" // U+e761 #define ICON_MD_COMPRESS "\xee\xa5\x8d" // U+e94d #define ICON_MD_COMPUTER "\xee\x8c\x8a" // U+e30a #define ICON_MD_CONFIRMATION_NUM "\xee\x98\xb8" // U+e638 #define ICON_MD_CONFIRMATION_NUMBER "\xee\x98\xb8" // U+e638 #define ICON_MD_CONNECT_WITHOUT_CONTACT "\xef\x88\xa3" // U+f223 #define ICON_MD_CONNECTED_TV "\xee\xa6\x98" // U+e998 #define ICON_MD_CONNECTING_AIRPORTS "\xee\x9f\x89" // U+e7c9 #define ICON_MD_CONSTRUCTION "\xee\xa8\xbc" // U+ea3c #define ICON_MD_CONTACT_EMERGENCY "\xef\xa3\x91" // U+f8d1 #define ICON_MD_CONTACT_MAIL "\xee\x83\x90" // U+e0d0 #define ICON_MD_CONTACT_PAGE "\xef\x88\xae" // U+f22e #define ICON_MD_CONTACT_PHONE "\xee\x83\x8f" // U+e0cf #define ICON_MD_CONTACT_SUPPORT "\xee\xa5\x8c" // U+e94c #define ICON_MD_CONTACTLESS "\xee\xa9\xb1" // U+ea71 #define ICON_MD_CONTACTS "\xee\x82\xba" // U+e0ba #define ICON_MD_CONTENT_COPY "\xee\x85\x8d" // U+e14d #define ICON_MD_CONTENT_CUT "\xee\x85\x8e" // U+e14e #define ICON_MD_CONTENT_PASTE "\xee\x85\x8f" // U+e14f #define ICON_MD_CONTENT_PASTE_GO "\xee\xaa\x8e" // U+ea8e #define ICON_MD_CONTENT_PASTE_OFF "\xee\x93\xb8" // U+e4f8 #define ICON_MD_CONTENT_PASTE_SEARCH "\xee\xaa\x9b" // U+ea9b #define ICON_MD_CONTRAST "\xee\xac\xb7" // U+eb37 #define ICON_MD_CONTROL_CAMERA "\xee\x81\xb4" // U+e074 #define ICON_MD_CONTROL_POINT "\xee\x8e\xba" // U+e3ba #define ICON_MD_CONTROL_POINT_DUPLICATE "\xee\x8e\xbb" // U+e3bb #define ICON_MD_COOKIE "\xee\xaa\xac" // U+eaac #define ICON_MD_COPY_ALL "\xee\x8b\xac" // U+e2ec #define ICON_MD_COPYRIGHT "\xee\xa4\x8c" // U+e90c #define ICON_MD_CORONAVIRUS "\xef\x88\xa1" // U+f221 #define ICON_MD_CORPORATE_FARE "\xef\x87\x90" // U+f1d0 #define ICON_MD_COTTAGE "\xee\x96\x87" // U+e587 #define ICON_MD_COUNTERTOPS "\xef\x87\xb7" // U+f1f7 #define ICON_MD_CREATE "\xee\x85\x90" // U+e150 #define ICON_MD_CREATE_NEW_FOLDER "\xee\x8b\x8c" // U+e2cc #define ICON_MD_CREDIT_CARD "\xee\xa1\xb0" // U+e870 #define ICON_MD_CREDIT_CARD_OFF "\xee\x93\xb4" // U+e4f4 #define ICON_MD_CREDIT_SCORE "\xee\xbf\xb1" // U+eff1 #define ICON_MD_CRIB "\xee\x96\x88" // U+e588 #define ICON_MD_CRISIS_ALERT "\xee\xaf\xa9" // U+ebe9 #define ICON_MD_CROP "\xee\x8e\xbe" // U+e3be #define ICON_MD_CROP_16_9 "\xee\x8e\xbc" // U+e3bc #define ICON_MD_CROP_3_2 "\xee\x8e\xbd" // U+e3bd #define ICON_MD_CROP_5_4 "\xee\x8e\xbf" // U+e3bf #define ICON_MD_CROP_7_5 "\xee\x8f\x80" // U+e3c0 #define ICON_MD_CROP_DIN "\xee\x8f\x81" // U+e3c1 #define ICON_MD_CROP_FREE "\xee\x8f\x82" // U+e3c2 #define ICON_MD_CROP_LANDSCAPE "\xee\x8f\x83" // U+e3c3 #define ICON_MD_CROP_ORIGINAL "\xee\x8f\x84" // U+e3c4 #define ICON_MD_CROP_PORTRAIT "\xee\x8f\x85" // U+e3c5 #define ICON_MD_CROP_ROTATE "\xee\x90\xb7" // U+e437 #define ICON_MD_CROP_SQUARE "\xee\x8f\x86" // U+e3c6 #define ICON_MD_CRUELTY_FREE "\xee\x9e\x99" // U+e799 #define ICON_MD_CSS "\xee\xae\x93" // U+eb93 #define ICON_MD_CURRENCY_BITCOIN "\xee\xaf\x85" // U+ebc5 #define ICON_MD_CURRENCY_EXCHANGE "\xee\xad\xb0" // U+eb70 #define ICON_MD_CURRENCY_FRANC "\xee\xab\xba" // U+eafa #define ICON_MD_CURRENCY_LIRA "\xee\xab\xaf" // U+eaef #define ICON_MD_CURRENCY_POUND "\xee\xab\xb1" // U+eaf1 #define ICON_MD_CURRENCY_RUBLE "\xee\xab\xac" // U+eaec #define ICON_MD_CURRENCY_RUPEE "\xee\xab\xb7" // U+eaf7 #define ICON_MD_CURRENCY_YEN "\xee\xab\xbb" // U+eafb #define ICON_MD_CURRENCY_YUAN "\xee\xab\xb9" // U+eaf9 #define ICON_MD_CURTAINS "\xee\xb0\x9e" // U+ec1e #define ICON_MD_CURTAINS_CLOSED "\xee\xb0\x9d" // U+ec1d #define ICON_MD_CYCLONE "\xee\xaf\x95" // U+ebd5 #define ICON_MD_DANGEROUS "\xee\xa6\x9a" // U+e99a #define ICON_MD_DARK_MODE "\xee\x94\x9c" // U+e51c #define ICON_MD_DASHBOARD "\xee\xa1\xb1" // U+e871 #define ICON_MD_DASHBOARD_CUSTOMIZE "\xee\xa6\x9b" // U+e99b #define ICON_MD_DATA_ARRAY "\xee\xab\x91" // U+ead1 #define ICON_MD_DATA_EXPLORATION "\xee\x9d\xaf" // U+e76f #define ICON_MD_DATA_OBJECT "\xee\xab\x93" // U+ead3 #define ICON_MD_DATA_SAVER_OFF "\xee\xbf\xb2" // U+eff2 #define ICON_MD_DATA_SAVER_ON "\xee\xbf\xb3" // U+eff3 #define ICON_MD_DATA_THRESHOLDING "\xee\xae\x9f" // U+eb9f #define ICON_MD_DATA_USAGE "\xee\x86\xaf" // U+e1af #define ICON_MD_DATASET "\xef\xa3\xae" // U+f8ee #define ICON_MD_DATASET_LINKED "\xef\xa3\xaf" // U+f8ef #define ICON_MD_DATE_RANGE "\xee\xa4\x96" // U+e916 #define ICON_MD_DEBLUR "\xee\xad\xb7" // U+eb77 #define ICON_MD_DECK "\xee\xa9\x82" // U+ea42 #define ICON_MD_DEHAZE "\xee\x8f\x87" // U+e3c7 #define ICON_MD_DELETE "\xee\xa1\xb2" // U+e872 #define ICON_MD_DELETE_FOREVER "\xee\xa4\xab" // U+e92b #define ICON_MD_DELETE_OUTLINE "\xee\xa4\xae" // U+e92e #define ICON_MD_DELETE_SWEEP "\xee\x85\xac" // U+e16c #define ICON_MD_DELIVERY_DINING "\xee\xa9\xb2" // U+ea72 #define ICON_MD_DENSITY_LARGE "\xee\xae\xa9" // U+eba9 #define ICON_MD_DENSITY_MEDIUM "\xee\xae\x9e" // U+eb9e #define ICON_MD_DENSITY_SMALL "\xee\xae\xa8" // U+eba8 #define ICON_MD_DEPARTURE_BOARD "\xee\x95\xb6" // U+e576 #define ICON_MD_DESCRIPTION "\xee\xa1\xb3" // U+e873 #define ICON_MD_DESELECT "\xee\xae\xb6" // U+ebb6 #define ICON_MD_DESIGN_SERVICES "\xef\x84\x8a" // U+f10a #define ICON_MD_DESK "\xef\xa3\xb4" // U+f8f4 #define ICON_MD_DESKTOP_ACCESS_DISABLED "\xee\xa6\x9d" // U+e99d #define ICON_MD_DESKTOP_MAC "\xee\x8c\x8b" // U+e30b #define ICON_MD_DESKTOP_WINDOWS "\xee\x8c\x8c" // U+e30c #define ICON_MD_DETAILS "\xee\x8f\x88" // U+e3c8 #define ICON_MD_DEVELOPER_BOARD "\xee\x8c\x8d" // U+e30d #define ICON_MD_DEVELOPER_BOARD_OFF "\xee\x93\xbf" // U+e4ff #define ICON_MD_DEVELOPER_MODE "\xee\x86\xb0" // U+e1b0 #define ICON_MD_DEVICE_HUB "\xee\x8c\xb5" // U+e335 #define ICON_MD_DEVICE_THERMOSTAT "\xee\x87\xbf" // U+e1ff #define ICON_MD_DEVICE_UNKNOWN "\xee\x8c\xb9" // U+e339 #define ICON_MD_DEVICES "\xee\x86\xb1" // U+e1b1 #define ICON_MD_DEVICES_FOLD "\xee\xaf\x9e" // U+ebde #define ICON_MD_DEVICES_OTHER "\xee\x8c\xb7" // U+e337 #define ICON_MD_DIALER_SIP "\xee\x82\xbb" // U+e0bb #define ICON_MD_DIALPAD "\xee\x82\xbc" // U+e0bc #define ICON_MD_DIAMOND "\xee\xab\x95" // U+ead5 #define ICON_MD_DIFFERENCE "\xee\xad\xbd" // U+eb7d #define ICON_MD_DINING "\xee\xbf\xb4" // U+eff4 #define ICON_MD_DINNER_DINING "\xee\xa9\x97" // U+ea57 #define ICON_MD_DIRECTIONS "\xee\x94\xae" // U+e52e #define ICON_MD_DIRECTIONS_BIKE "\xee\x94\xaf" // U+e52f #define ICON_MD_DIRECTIONS_BOAT "\xee\x94\xb2" // U+e532 #define ICON_MD_DIRECTIONS_BOAT_FILLED "\xee\xbf\xb5" // U+eff5 #define ICON_MD_DIRECTIONS_BUS "\xee\x94\xb0" // U+e530 #define ICON_MD_DIRECTIONS_BUS_FILLED "\xee\xbf\xb6" // U+eff6 #define ICON_MD_DIRECTIONS_CAR "\xee\x94\xb1" // U+e531 #define ICON_MD_DIRECTIONS_CAR_FILLED "\xee\xbf\xb7" // U+eff7 #define ICON_MD_DIRECTIONS_FERRY "\xee\x94\xb2" // U+e532 #define ICON_MD_DIRECTIONS_OFF "\xef\x84\x8f" // U+f10f #define ICON_MD_DIRECTIONS_RAILWAY "\xee\x94\xb4" // U+e534 #define ICON_MD_DIRECTIONS_RAILWAY_FILLED "\xee\xbf\xb8" // U+eff8 #define ICON_MD_DIRECTIONS_RUN "\xee\x95\xa6" // U+e566 #define ICON_MD_DIRECTIONS_SUBWAY "\xee\x94\xb3" // U+e533 #define ICON_MD_DIRECTIONS_SUBWAY_FILLED "\xee\xbf\xb9" // U+eff9 #define ICON_MD_DIRECTIONS_TRAIN "\xee\x94\xb4" // U+e534 #define ICON_MD_DIRECTIONS_TRANSIT "\xee\x94\xb5" // U+e535 #define ICON_MD_DIRECTIONS_TRANSIT_FILLED "\xee\xbf\xba" // U+effa #define ICON_MD_DIRECTIONS_WALK "\xee\x94\xb6" // U+e536 #define ICON_MD_DIRTY_LENS "\xee\xbd\x8b" // U+ef4b #define ICON_MD_DISABLED_BY_DEFAULT "\xef\x88\xb0" // U+f230 #define ICON_MD_DISABLED_VISIBLE "\xee\x9d\xae" // U+e76e #define ICON_MD_DISC_FULL "\xee\x98\x90" // U+e610 #define ICON_MD_DISCORD "\xee\xa9\xac" // U+ea6c #define ICON_MD_DISCOUNT "\xee\xaf\x89" // U+ebc9 #define ICON_MD_DISPLAY_SETTINGS "\xee\xae\x97" // U+eb97 #define ICON_MD_DIVERSITY_1 "\xef\xa3\x97" // U+f8d7 #define ICON_MD_DIVERSITY_2 "\xef\xa3\x98" // U+f8d8 #define ICON_MD_DIVERSITY_3 "\xef\xa3\x99" // U+f8d9 #define ICON_MD_DND_FORWARDSLASH "\xee\x98\x91" // U+e611 #define ICON_MD_DNS "\xee\xa1\xb5" // U+e875 #define ICON_MD_DO_DISTURB "\xef\x82\x8c" // U+f08c #define ICON_MD_DO_DISTURB_ALT "\xef\x82\x8d" // U+f08d #define ICON_MD_DO_DISTURB_OFF "\xef\x82\x8e" // U+f08e #define ICON_MD_DO_DISTURB_ON "\xef\x82\x8f" // U+f08f #define ICON_MD_DO_NOT_DISTURB "\xee\x98\x92" // U+e612 #define ICON_MD_DO_NOT_DISTURB_ALT "\xee\x98\x91" // U+e611 #define ICON_MD_DO_NOT_DISTURB_OFF "\xee\x99\x83" // U+e643 #define ICON_MD_DO_NOT_DISTURB_ON "\xee\x99\x84" // U+e644 #define ICON_MD_DO_NOT_DISTURB_ON_TOTAL_SILENCE "\xee\xbf\xbb" // U+effb #define ICON_MD_DO_NOT_STEP "\xef\x86\x9f" // U+f19f #define ICON_MD_DO_NOT_TOUCH "\xef\x86\xb0" // U+f1b0 #define ICON_MD_DOCK "\xee\x8c\x8e" // U+e30e #define ICON_MD_DOCUMENT_SCANNER "\xee\x97\xba" // U+e5fa #define ICON_MD_DOMAIN "\xee\x9f\xae" // U+e7ee #define ICON_MD_DOMAIN_ADD "\xee\xad\xa2" // U+eb62 #define ICON_MD_DOMAIN_DISABLED "\xee\x83\xaf" // U+e0ef #define ICON_MD_DOMAIN_VERIFICATION "\xee\xbd\x8c" // U+ef4c #define ICON_MD_DONE "\xee\xa1\xb6" // U+e876 #define ICON_MD_DONE_ALL "\xee\xa1\xb7" // U+e877 #define ICON_MD_DONE_OUTLINE "\xee\xa4\xaf" // U+e92f #define ICON_MD_DONUT_LARGE "\xee\xa4\x97" // U+e917 #define ICON_MD_DONUT_SMALL "\xee\xa4\x98" // U+e918 #define ICON_MD_DOOR_BACK "\xee\xbf\xbc" // U+effc #define ICON_MD_DOOR_FRONT "\xee\xbf\xbd" // U+effd #define ICON_MD_DOOR_SLIDING "\xee\xbf\xbe" // U+effe #define ICON_MD_DOORBELL "\xee\xbf\xbf" // U+efff #define ICON_MD_DOUBLE_ARROW "\xee\xa9\x90" // U+ea50 #define ICON_MD_DOWNHILL_SKIING "\xee\x94\x89" // U+e509 #define ICON_MD_DOWNLOAD "\xef\x82\x90" // U+f090 #define ICON_MD_DOWNLOAD_DONE "\xef\x82\x91" // U+f091 #define ICON_MD_DOWNLOAD_FOR_OFFLINE "\xef\x80\x80" // U+f000 #define ICON_MD_DOWNLOADING "\xef\x80\x81" // U+f001 #define ICON_MD_DRAFTS "\xee\x85\x91" // U+e151 #define ICON_MD_DRAG_HANDLE "\xee\x89\x9d" // U+e25d #define ICON_MD_DRAG_INDICATOR "\xee\xa5\x85" // U+e945 #define ICON_MD_DRAW "\xee\x9d\x86" // U+e746 #define ICON_MD_DRIVE_ETA "\xee\x98\x93" // U+e613 #define ICON_MD_DRIVE_FILE_MOVE "\xee\x99\xb5" // U+e675 #define ICON_MD_DRIVE_FILE_MOVE_OUTLINE "\xee\xa6\xa1" // U+e9a1 #define ICON_MD_DRIVE_FILE_MOVE_RTL "\xee\x9d\xad" // U+e76d #define ICON_MD_DRIVE_FILE_RENAME_OUTLINE "\xee\xa6\xa2" // U+e9a2 #define ICON_MD_DRIVE_FOLDER_UPLOAD "\xee\xa6\xa3" // U+e9a3 #define ICON_MD_DRY "\xef\x86\xb3" // U+f1b3 #define ICON_MD_DRY_CLEANING "\xee\xa9\x98" // U+ea58 #define ICON_MD_DUO "\xee\xa6\xa5" // U+e9a5 #define ICON_MD_DVR "\xee\x86\xb2" // U+e1b2 #define ICON_MD_DYNAMIC_FEED "\xee\xa8\x94" // U+ea14 #define ICON_MD_DYNAMIC_FORM "\xef\x86\xbf" // U+f1bf #define ICON_MD_E_MOBILEDATA "\xef\x80\x82" // U+f002 #define ICON_MD_EARBUDS "\xef\x80\x83" // U+f003 #define ICON_MD_EARBUDS_BATTERY "\xef\x80\x84" // U+f004 #define ICON_MD_EAST "\xef\x87\x9f" // U+f1df #define ICON_MD_ECO "\xee\xa8\xb5" // U+ea35 #define ICON_MD_EDGESENSOR_HIGH "\xef\x80\x85" // U+f005 #define ICON_MD_EDGESENSOR_LOW "\xef\x80\x86" // U+f006 #define ICON_MD_EDIT "\xee\x8f\x89" // U+e3c9 #define ICON_MD_EDIT_ATTRIBUTES "\xee\x95\xb8" // U+e578 #define ICON_MD_EDIT_CALENDAR "\xee\x9d\x82" // U+e742 #define ICON_MD_EDIT_LOCATION "\xee\x95\xa8" // U+e568 #define ICON_MD_EDIT_LOCATION_ALT "\xee\x87\x85" // U+e1c5 #define ICON_MD_EDIT_NOTE "\xee\x9d\x85" // U+e745 #define ICON_MD_EDIT_NOTIFICATIONS "\xee\x94\xa5" // U+e525 #define ICON_MD_EDIT_OFF "\xee\xa5\x90" // U+e950 #define ICON_MD_EDIT_ROAD "\xee\xbd\x8d" // U+ef4d #define ICON_MD_EGG "\xee\xab\x8c" // U+eacc #define ICON_MD_EGG_ALT "\xee\xab\x88" // U+eac8 #define ICON_MD_EJECT "\xee\xa3\xbb" // U+e8fb #define ICON_MD_ELDERLY "\xef\x88\x9a" // U+f21a #define ICON_MD_ELDERLY_WOMAN "\xee\xad\xa9" // U+eb69 #define ICON_MD_ELECTRIC_BIKE "\xee\xac\x9b" // U+eb1b #define ICON_MD_ELECTRIC_BOLT "\xee\xb0\x9c" // U+ec1c #define ICON_MD_ELECTRIC_CAR "\xee\xac\x9c" // U+eb1c #define ICON_MD_ELECTRIC_METER "\xee\xb0\x9b" // U+ec1b #define ICON_MD_ELECTRIC_MOPED "\xee\xac\x9d" // U+eb1d #define ICON_MD_ELECTRIC_RICKSHAW "\xee\xac\x9e" // U+eb1e #define ICON_MD_ELECTRIC_SCOOTER "\xee\xac\x9f" // U+eb1f #define ICON_MD_ELECTRICAL_SERVICES "\xef\x84\x82" // U+f102 #define ICON_MD_ELEVATOR "\xef\x86\xa0" // U+f1a0 #define ICON_MD_EMAIL "\xee\x82\xbe" // U+e0be #define ICON_MD_EMERGENCY "\xee\x87\xab" // U+e1eb #define ICON_MD_EMERGENCY_RECORDING "\xee\xaf\xb4" // U+ebf4 #define ICON_MD_EMERGENCY_SHARE "\xee\xaf\xb6" // U+ebf6 #define ICON_MD_EMOJI_EMOTIONS "\xee\xa8\xa2" // U+ea22 #define ICON_MD_EMOJI_EVENTS "\xee\xa8\xa3" // U+ea23 #define ICON_MD_EMOJI_FLAGS "\xee\xa8\x9a" // U+ea1a #define ICON_MD_EMOJI_FOOD_BEVERAGE "\xee\xa8\x9b" // U+ea1b #define ICON_MD_EMOJI_NATURE "\xee\xa8\x9c" // U+ea1c #define ICON_MD_EMOJI_OBJECTS "\xee\xa8\xa4" // U+ea24 #define ICON_MD_EMOJI_PEOPLE "\xee\xa8\x9d" // U+ea1d #define ICON_MD_EMOJI_SYMBOLS "\xee\xa8\x9e" // U+ea1e #define ICON_MD_EMOJI_TRANSPORTATION "\xee\xa8\x9f" // U+ea1f #define ICON_MD_ENERGY_SAVINGS_LEAF "\xee\xb0\x9a" // U+ec1a #define ICON_MD_ENGINEERING "\xee\xa8\xbd" // U+ea3d #define ICON_MD_ENHANCE_PHOTO_TRANSLATE "\xee\xa3\xbc" // U+e8fc #define ICON_MD_ENHANCED_ENCRYPTION "\xee\x98\xbf" // U+e63f #define ICON_MD_EQUALIZER "\xee\x80\x9d" // U+e01d #define ICON_MD_ERROR "\xee\x80\x80" // U+e000 #define ICON_MD_ERROR_OUTLINE "\xee\x80\x81" // U+e001 #define ICON_MD_ESCALATOR "\xef\x86\xa1" // U+f1a1 #define ICON_MD_ESCALATOR_WARNING "\xef\x86\xac" // U+f1ac #define ICON_MD_EURO "\xee\xa8\x95" // U+ea15 #define ICON_MD_EURO_SYMBOL "\xee\xa4\xa6" // U+e926 #define ICON_MD_EV_STATION "\xee\x95\xad" // U+e56d #define ICON_MD_EVENT "\xee\xa1\xb8" // U+e878 #define ICON_MD_EVENT_AVAILABLE "\xee\x98\x94" // U+e614 #define ICON_MD_EVENT_BUSY "\xee\x98\x95" // U+e615 #define ICON_MD_EVENT_NOTE "\xee\x98\x96" // U+e616 #define ICON_MD_EVENT_REPEAT "\xee\xad\xbb" // U+eb7b #define ICON_MD_EVENT_SEAT "\xee\xa4\x83" // U+e903 #define ICON_MD_EXIT_TO_APP "\xee\xa1\xb9" // U+e879 #define ICON_MD_EXPAND "\xee\xa5\x8f" // U+e94f #define ICON_MD_EXPAND_CIRCLE_DOWN "\xee\x9f\x8d" // U+e7cd #define ICON_MD_EXPAND_LESS "\xee\x97\x8e" // U+e5ce #define ICON_MD_EXPAND_MORE "\xee\x97\x8f" // U+e5cf #define ICON_MD_EXPLICIT "\xee\x80\x9e" // U+e01e #define ICON_MD_EXPLORE "\xee\xa1\xba" // U+e87a #define ICON_MD_EXPLORE_OFF "\xee\xa6\xa8" // U+e9a8 #define ICON_MD_EXPOSURE "\xee\x8f\x8a" // U+e3ca #define ICON_MD_EXPOSURE_MINUS_1 "\xee\x8f\x8b" // U+e3cb #define ICON_MD_EXPOSURE_MINUS_2 "\xee\x8f\x8c" // U+e3cc #define ICON_MD_EXPOSURE_NEG_1 "\xee\x8f\x8b" // U+e3cb #define ICON_MD_EXPOSURE_NEG_2 "\xee\x8f\x8c" // U+e3cc #define ICON_MD_EXPOSURE_PLUS_1 "\xee\x8f\x8d" // U+e3cd #define ICON_MD_EXPOSURE_PLUS_2 "\xee\x8f\x8e" // U+e3ce #define ICON_MD_EXPOSURE_ZERO "\xee\x8f\x8f" // U+e3cf #define ICON_MD_EXTENSION "\xee\xa1\xbb" // U+e87b #define ICON_MD_EXTENSION_OFF "\xee\x93\xb5" // U+e4f5 #define ICON_MD_FACE "\xee\xa1\xbc" // U+e87c #define ICON_MD_FACE_2 "\xef\xa3\x9a" // U+f8da #define ICON_MD_FACE_3 "\xef\xa3\x9b" // U+f8db #define ICON_MD_FACE_4 "\xef\xa3\x9c" // U+f8dc #define ICON_MD_FACE_5 "\xef\xa3\x9d" // U+f8dd #define ICON_MD_FACE_6 "\xef\xa3\x9e" // U+f8de #define ICON_MD_FACE_RETOUCHING_NATURAL "\xee\xbd\x8e" // U+ef4e #define ICON_MD_FACE_RETOUCHING_OFF "\xef\x80\x87" // U+f007 #define ICON_MD_FACEBOOK "\xef\x88\xb4" // U+f234 #define ICON_MD_FACT_CHECK "\xef\x83\x85" // U+f0c5 #define ICON_MD_FACTORY "\xee\xae\xbc" // U+ebbc #define ICON_MD_FAMILY_RESTROOM "\xef\x86\xa2" // U+f1a2 #define ICON_MD_FAST_FORWARD "\xee\x80\x9f" // U+e01f #define ICON_MD_FAST_REWIND "\xee\x80\xa0" // U+e020 #define ICON_MD_FASTFOOD "\xee\x95\xba" // U+e57a #define ICON_MD_FAVORITE "\xee\xa1\xbd" // U+e87d #define ICON_MD_FAVORITE_BORDER "\xee\xa1\xbe" // U+e87e #define ICON_MD_FAVORITE_OUTLINE "\xee\xa1\xbe" // U+e87e #define ICON_MD_FAX "\xee\xab\x98" // U+ead8 #define ICON_MD_FEATURED_PLAY_LIST "\xee\x81\xad" // U+e06d #define ICON_MD_FEATURED_VIDEO "\xee\x81\xae" // U+e06e #define ICON_MD_FEED "\xef\x80\x89" // U+f009 #define ICON_MD_FEEDBACK "\xee\xa1\xbf" // U+e87f #define ICON_MD_FEMALE "\xee\x96\x90" // U+e590 #define ICON_MD_FENCE "\xef\x87\xb6" // U+f1f6 #define ICON_MD_FESTIVAL "\xee\xa9\xa8" // U+ea68 #define ICON_MD_FIBER_DVR "\xee\x81\x9d" // U+e05d #define ICON_MD_FIBER_MANUAL_RECORD "\xee\x81\xa1" // U+e061 #define ICON_MD_FIBER_NEW "\xee\x81\x9e" // U+e05e #define ICON_MD_FIBER_PIN "\xee\x81\xaa" // U+e06a #define ICON_MD_FIBER_SMART_RECORD "\xee\x81\xa2" // U+e062 #define ICON_MD_FILE_COPY "\xee\x85\xb3" // U+e173 #define ICON_MD_FILE_DOWNLOAD "\xee\x8b\x84" // U+e2c4 #define ICON_MD_FILE_DOWNLOAD_DONE "\xee\xa6\xaa" // U+e9aa #define ICON_MD_FILE_DOWNLOAD_OFF "\xee\x93\xbe" // U+e4fe #define ICON_MD_FILE_OPEN "\xee\xab\xb3" // U+eaf3 #define ICON_MD_FILE_PRESENT "\xee\xa8\x8e" // U+ea0e #define ICON_MD_FILE_UPLOAD "\xee\x8b\x86" // U+e2c6 #define ICON_MD_FILTER "\xee\x8f\x93" // U+e3d3 #define ICON_MD_FILTER_1 "\xee\x8f\x90" // U+e3d0 #define ICON_MD_FILTER_2 "\xee\x8f\x91" // U+e3d1 #define ICON_MD_FILTER_3 "\xee\x8f\x92" // U+e3d2 #define ICON_MD_FILTER_4 "\xee\x8f\x94" // U+e3d4 #define ICON_MD_FILTER_5 "\xee\x8f\x95" // U+e3d5 #define ICON_MD_FILTER_6 "\xee\x8f\x96" // U+e3d6 #define ICON_MD_FILTER_7 "\xee\x8f\x97" // U+e3d7 #define ICON_MD_FILTER_8 "\xee\x8f\x98" // U+e3d8 #define ICON_MD_FILTER_9 "\xee\x8f\x99" // U+e3d9 #define ICON_MD_FILTER_9_PLUS "\xee\x8f\x9a" // U+e3da #define ICON_MD_FILTER_ALT "\xee\xbd\x8f" // U+ef4f #define ICON_MD_FILTER_ALT_OFF "\xee\xac\xb2" // U+eb32 #define ICON_MD_FILTER_B_AND_W "\xee\x8f\x9b" // U+e3db #define ICON_MD_FILTER_CENTER_FOCUS "\xee\x8f\x9c" // U+e3dc #define ICON_MD_FILTER_DRAMA "\xee\x8f\x9d" // U+e3dd #define ICON_MD_FILTER_FRAMES "\xee\x8f\x9e" // U+e3de #define ICON_MD_FILTER_HDR "\xee\x8f\x9f" // U+e3df #define ICON_MD_FILTER_LIST "\xee\x85\x92" // U+e152 #define ICON_MD_FILTER_LIST_ALT "\xee\xa5\x8e" // U+e94e #define ICON_MD_FILTER_LIST_OFF "\xee\xad\x97" // U+eb57 #define ICON_MD_FILTER_NONE "\xee\x8f\xa0" // U+e3e0 #define ICON_MD_FILTER_TILT_SHIFT "\xee\x8f\xa2" // U+e3e2 #define ICON_MD_FILTER_VINTAGE "\xee\x8f\xa3" // U+e3e3 #define ICON_MD_FIND_IN_PAGE "\xee\xa2\x80" // U+e880 #define ICON_MD_FIND_REPLACE "\xee\xa2\x81" // U+e881 #define ICON_MD_FINGERPRINT "\xee\xa4\x8d" // U+e90d #define ICON_MD_FIRE_EXTINGUISHER "\xef\x87\x98" // U+f1d8 #define ICON_MD_FIRE_HYDRANT "\xef\x86\xa3" // U+f1a3 #define ICON_MD_FIRE_HYDRANT_ALT "\xef\xa3\xb1" // U+f8f1 #define ICON_MD_FIRE_TRUCK "\xef\xa3\xb2" // U+f8f2 #define ICON_MD_FIREPLACE "\xee\xa9\x83" // U+ea43 #define ICON_MD_FIRST_PAGE "\xee\x97\x9c" // U+e5dc #define ICON_MD_FIT_SCREEN "\xee\xa8\x90" // U+ea10 #define ICON_MD_FITBIT "\xee\xa0\xab" // U+e82b #define ICON_MD_FITNESS_CENTER "\xee\xad\x83" // U+eb43 #define ICON_MD_FLAG "\xee\x85\x93" // U+e153 #define ICON_MD_FLAG_CIRCLE "\xee\xab\xb8" // U+eaf8 #define ICON_MD_FLAKY "\xee\xbd\x90" // U+ef50 #define ICON_MD_FLARE "\xee\x8f\xa4" // U+e3e4 #define ICON_MD_FLASH_AUTO "\xee\x8f\xa5" // U+e3e5 #define ICON_MD_FLASH_OFF "\xee\x8f\xa6" // U+e3e6 #define ICON_MD_FLASH_ON "\xee\x8f\xa7" // U+e3e7 #define ICON_MD_FLASHLIGHT_OFF "\xef\x80\x8a" // U+f00a #define ICON_MD_FLASHLIGHT_ON "\xef\x80\x8b" // U+f00b #define ICON_MD_FLATWARE "\xef\x80\x8c" // U+f00c #define ICON_MD_FLIGHT "\xee\x94\xb9" // U+e539 #define ICON_MD_FLIGHT_CLASS "\xee\x9f\x8b" // U+e7cb #define ICON_MD_FLIGHT_LAND "\xee\xa4\x84" // U+e904 #define ICON_MD_FLIGHT_TAKEOFF "\xee\xa4\x85" // U+e905 #define ICON_MD_FLIP "\xee\x8f\xa8" // U+e3e8 #define ICON_MD_FLIP_CAMERA_ANDROID "\xee\xa8\xb7" // U+ea37 #define ICON_MD_FLIP_CAMERA_IOS "\xee\xa8\xb8" // U+ea38 #define ICON_MD_FLIP_TO_BACK "\xee\xa2\x82" // U+e882 #define ICON_MD_FLIP_TO_FRONT "\xee\xa2\x83" // U+e883 #define ICON_MD_FLOOD "\xee\xaf\xa6" // U+ebe6 #define ICON_MD_FLOURESCENT "\xef\x80\x8d" // U+f00d #define ICON_MD_FLUTTER_DASH "\xee\x80\x8b" // U+e00b #define ICON_MD_FMD_BAD "\xef\x80\x8e" // U+f00e #define ICON_MD_FMD_GOOD "\xef\x80\x8f" // U+f00f #define ICON_MD_FOGGY "\xee\xa0\x98" // U+e818 #define ICON_MD_FOLDER "\xee\x8b\x87" // U+e2c7 #define ICON_MD_FOLDER_COPY "\xee\xae\xbd" // U+ebbd #define ICON_MD_FOLDER_DELETE "\xee\xac\xb4" // U+eb34 #define ICON_MD_FOLDER_OFF "\xee\xae\x83" // U+eb83 #define ICON_MD_FOLDER_OPEN "\xee\x8b\x88" // U+e2c8 #define ICON_MD_FOLDER_SHARED "\xee\x8b\x89" // U+e2c9 #define ICON_MD_FOLDER_SPECIAL "\xee\x98\x97" // U+e617 #define ICON_MD_FOLDER_ZIP "\xee\xac\xac" // U+eb2c #define ICON_MD_FOLLOW_THE_SIGNS "\xef\x88\xa2" // U+f222 #define ICON_MD_FONT_DOWNLOAD "\xee\x85\xa7" // U+e167 #define ICON_MD_FONT_DOWNLOAD_OFF "\xee\x93\xb9" // U+e4f9 #define ICON_MD_FOOD_BANK "\xef\x87\xb2" // U+f1f2 #define ICON_MD_FOREST "\xee\xaa\x99" // U+ea99 #define ICON_MD_FORK_LEFT "\xee\xae\xa0" // U+eba0 #define ICON_MD_FORK_RIGHT "\xee\xae\xac" // U+ebac #define ICON_MD_FORMAT_ALIGN_CENTER "\xee\x88\xb4" // U+e234 #define ICON_MD_FORMAT_ALIGN_JUSTIFY "\xee\x88\xb5" // U+e235 #define ICON_MD_FORMAT_ALIGN_LEFT "\xee\x88\xb6" // U+e236 #define ICON_MD_FORMAT_ALIGN_RIGHT "\xee\x88\xb7" // U+e237 #define ICON_MD_FORMAT_BOLD "\xee\x88\xb8" // U+e238 #define ICON_MD_FORMAT_CLEAR "\xee\x88\xb9" // U+e239 #define ICON_MD_FORMAT_COLOR_FILL "\xee\x88\xba" // U+e23a #define ICON_MD_FORMAT_COLOR_RESET "\xee\x88\xbb" // U+e23b #define ICON_MD_FORMAT_COLOR_TEXT "\xee\x88\xbc" // U+e23c #define ICON_MD_FORMAT_INDENT_DECREASE "\xee\x88\xbd" // U+e23d #define ICON_MD_FORMAT_INDENT_INCREASE "\xee\x88\xbe" // U+e23e #define ICON_MD_FORMAT_ITALIC "\xee\x88\xbf" // U+e23f #define ICON_MD_FORMAT_LINE_SPACING "\xee\x89\x80" // U+e240 #define ICON_MD_FORMAT_LIST_BULLETED "\xee\x89\x81" // U+e241 #define ICON_MD_FORMAT_LIST_NUMBERED "\xee\x89\x82" // U+e242 #define ICON_MD_FORMAT_LIST_NUMBERED_RTL "\xee\x89\xa7" // U+e267 #define ICON_MD_FORMAT_OVERLINE "\xee\xad\xa5" // U+eb65 #define ICON_MD_FORMAT_PAINT "\xee\x89\x83" // U+e243 #define ICON_MD_FORMAT_QUOTE "\xee\x89\x84" // U+e244 #define ICON_MD_FORMAT_SHAPES "\xee\x89\x9e" // U+e25e #define ICON_MD_FORMAT_SIZE "\xee\x89\x85" // U+e245 #define ICON_MD_FORMAT_STRIKETHROUGH "\xee\x89\x86" // U+e246 #define ICON_MD_FORMAT_TEXTDIRECTION_L_TO_R "\xee\x89\x87" // U+e247 #define ICON_MD_FORMAT_TEXTDIRECTION_R_TO_L "\xee\x89\x88" // U+e248 #define ICON_MD_FORMAT_UNDERLINE "\xee\x89\x89" // U+e249 #define ICON_MD_FORMAT_UNDERLINED "\xee\x89\x89" // U+e249 #define ICON_MD_FORT "\xee\xaa\xad" // U+eaad #define ICON_MD_FORUM "\xee\x82\xbf" // U+e0bf #define ICON_MD_FORWARD "\xee\x85\x94" // U+e154 #define ICON_MD_FORWARD_10 "\xee\x81\x96" // U+e056 #define ICON_MD_FORWARD_30 "\xee\x81\x97" // U+e057 #define ICON_MD_FORWARD_5 "\xee\x81\x98" // U+e058 #define ICON_MD_FORWARD_TO_INBOX "\xef\x86\x87" // U+f187 #define ICON_MD_FOUNDATION "\xef\x88\x80" // U+f200 #define ICON_MD_FREE_BREAKFAST "\xee\xad\x84" // U+eb44 #define ICON_MD_FREE_CANCELLATION "\xee\x9d\x88" // U+e748 #define ICON_MD_FRONT_HAND "\xee\x9d\xa9" // U+e769 #define ICON_MD_FULLSCREEN "\xee\x97\x90" // U+e5d0 #define ICON_MD_FULLSCREEN_EXIT "\xee\x97\x91" // U+e5d1 #define ICON_MD_FUNCTIONS "\xee\x89\x8a" // U+e24a #define ICON_MD_G_MOBILEDATA "\xef\x80\x90" // U+f010 #define ICON_MD_G_TRANSLATE "\xee\xa4\xa7" // U+e927 #define ICON_MD_GAMEPAD "\xee\x8c\x8f" // U+e30f #define ICON_MD_GAMES "\xee\x80\xa1" // U+e021 #define ICON_MD_GARAGE "\xef\x80\x91" // U+f011 #define ICON_MD_GAS_METER "\xee\xb0\x99" // U+ec19 #define ICON_MD_GAVEL "\xee\xa4\x8e" // U+e90e #define ICON_MD_GENERATING_TOKENS "\xee\x9d\x89" // U+e749 #define ICON_MD_GESTURE "\xee\x85\x95" // U+e155 #define ICON_MD_GET_APP "\xee\xa2\x84" // U+e884 #define ICON_MD_GIF "\xee\xa4\x88" // U+e908 #define ICON_MD_GIF_BOX "\xee\x9e\xa3" // U+e7a3 #define ICON_MD_GIRL "\xee\xad\xa8" // U+eb68 #define ICON_MD_GITE "\xee\x96\x8b" // U+e58b #define ICON_MD_GOAT "\xf4\x8f\xbf\xbd" // U+10fffd #define ICON_MD_GOLF_COURSE "\xee\xad\x85" // U+eb45 #define ICON_MD_GPP_BAD "\xef\x80\x92" // U+f012 #define ICON_MD_GPP_GOOD "\xef\x80\x93" // U+f013 #define ICON_MD_GPP_MAYBE "\xef\x80\x94" // U+f014 #define ICON_MD_GPS_FIXED "\xee\x86\xb3" // U+e1b3 #define ICON_MD_GPS_NOT_FIXED "\xee\x86\xb4" // U+e1b4 #define ICON_MD_GPS_OFF "\xee\x86\xb5" // U+e1b5 #define ICON_MD_GRADE "\xee\xa2\x85" // U+e885 #define ICON_MD_GRADIENT "\xee\x8f\xa9" // U+e3e9 #define ICON_MD_GRADING "\xee\xa9\x8f" // U+ea4f #define ICON_MD_GRAIN "\xee\x8f\xaa" // U+e3ea #define ICON_MD_GRAPHIC_EQ "\xee\x86\xb8" // U+e1b8 #define ICON_MD_GRASS "\xef\x88\x85" // U+f205 #define ICON_MD_GRID_3X3 "\xef\x80\x95" // U+f015 #define ICON_MD_GRID_4X4 "\xef\x80\x96" // U+f016 #define ICON_MD_GRID_GOLDENRATIO "\xef\x80\x97" // U+f017 #define ICON_MD_GRID_OFF "\xee\x8f\xab" // U+e3eb #define ICON_MD_GRID_ON "\xee\x8f\xac" // U+e3ec #define ICON_MD_GRID_VIEW "\xee\xa6\xb0" // U+e9b0 #define ICON_MD_GROUP "\xee\x9f\xaf" // U+e7ef #define ICON_MD_GROUP_ADD "\xee\x9f\xb0" // U+e7f0 #define ICON_MD_GROUP_OFF "\xee\x9d\x87" // U+e747 #define ICON_MD_GROUP_REMOVE "\xee\x9e\xad" // U+e7ad #define ICON_MD_GROUP_WORK "\xee\xa2\x86" // U+e886 #define ICON_MD_GROUPS "\xef\x88\xb3" // U+f233 #define ICON_MD_GROUPS_2 "\xef\xa3\x9f" // U+f8df #define ICON_MD_GROUPS_3 "\xef\xa3\xa0" // U+f8e0 #define ICON_MD_H_MOBILEDATA "\xef\x80\x98" // U+f018 #define ICON_MD_H_PLUS_MOBILEDATA "\xef\x80\x99" // U+f019 #define ICON_MD_HAIL "\xee\xa6\xb1" // U+e9b1 #define ICON_MD_HANDSHAKE "\xee\xaf\x8b" // U+ebcb #define ICON_MD_HANDYMAN "\xef\x84\x8b" // U+f10b #define ICON_MD_HARDWARE "\xee\xa9\x99" // U+ea59 #define ICON_MD_HD "\xee\x81\x92" // U+e052 #define ICON_MD_HDR_AUTO "\xef\x80\x9a" // U+f01a #define ICON_MD_HDR_AUTO_SELECT "\xef\x80\x9b" // U+f01b #define ICON_MD_HDR_ENHANCED_SELECT "\xee\xbd\x91" // U+ef51 #define ICON_MD_HDR_OFF "\xee\x8f\xad" // U+e3ed #define ICON_MD_HDR_OFF_SELECT "\xef\x80\x9c" // U+f01c #define ICON_MD_HDR_ON "\xee\x8f\xae" // U+e3ee #define ICON_MD_HDR_ON_SELECT "\xef\x80\x9d" // U+f01d #define ICON_MD_HDR_PLUS "\xef\x80\x9e" // U+f01e #define ICON_MD_HDR_STRONG "\xee\x8f\xb1" // U+e3f1 #define ICON_MD_HDR_WEAK "\xee\x8f\xb2" // U+e3f2 #define ICON_MD_HEADPHONES "\xef\x80\x9f" // U+f01f #define ICON_MD_HEADPHONES_BATTERY "\xef\x80\xa0" // U+f020 #define ICON_MD_HEADSET "\xee\x8c\x90" // U+e310 #define ICON_MD_HEADSET_MIC "\xee\x8c\x91" // U+e311 #define ICON_MD_HEADSET_OFF "\xee\x8c\xba" // U+e33a #define ICON_MD_HEALING "\xee\x8f\xb3" // U+e3f3 #define ICON_MD_HEALTH_AND_SAFETY "\xee\x87\x95" // U+e1d5 #define ICON_MD_HEARING "\xee\x80\xa3" // U+e023 #define ICON_MD_HEARING_DISABLED "\xef\x84\x84" // U+f104 #define ICON_MD_HEART_BROKEN "\xee\xab\x82" // U+eac2 #define ICON_MD_HEAT_PUMP "\xee\xb0\x98" // U+ec18 #define ICON_MD_HEIGHT "\xee\xa8\x96" // U+ea16 #define ICON_MD_HELP "\xee\xa2\x87" // U+e887 #define ICON_MD_HELP_CENTER "\xef\x87\x80" // U+f1c0 #define ICON_MD_HELP_OUTLINE "\xee\xa3\xbd" // U+e8fd #define ICON_MD_HEVC "\xef\x80\xa1" // U+f021 #define ICON_MD_HEXAGON "\xee\xac\xb9" // U+eb39 #define ICON_MD_HIDE_IMAGE "\xef\x80\xa2" // U+f022 #define ICON_MD_HIDE_SOURCE "\xef\x80\xa3" // U+f023 #define ICON_MD_HIGH_QUALITY "\xee\x80\xa4" // U+e024 #define ICON_MD_HIGHLIGHT "\xee\x89\x9f" // U+e25f #define ICON_MD_HIGHLIGHT_ALT "\xee\xbd\x92" // U+ef52 #define ICON_MD_HIGHLIGHT_OFF "\xee\xa2\x88" // U+e888 #define ICON_MD_HIGHLIGHT_REMOVE "\xee\xa2\x88" // U+e888 #define ICON_MD_HIKING "\xee\x94\x8a" // U+e50a #define ICON_MD_HISTORY "\xee\xa2\x89" // U+e889 #define ICON_MD_HISTORY_EDU "\xee\xa8\xbe" // U+ea3e #define ICON_MD_HISTORY_TOGGLE_OFF "\xef\x85\xbd" // U+f17d #define ICON_MD_HIVE "\xee\xaa\xa6" // U+eaa6 #define ICON_MD_HLS "\xee\xae\x8a" // U+eb8a #define ICON_MD_HLS_OFF "\xee\xae\x8c" // U+eb8c #define ICON_MD_HOLIDAY_VILLAGE "\xee\x96\x8a" // U+e58a #define ICON_MD_HOME "\xee\xa2\x8a" // U+e88a #define ICON_MD_HOME_FILLED "\xee\xa6\xb2" // U+e9b2 #define ICON_MD_HOME_MAX "\xef\x80\xa4" // U+f024 #define ICON_MD_HOME_MINI "\xef\x80\xa5" // U+f025 #define ICON_MD_HOME_REPAIR_SERVICE "\xef\x84\x80" // U+f100 #define ICON_MD_HOME_WORK "\xee\xa8\x89" // U+ea09 #define ICON_MD_HORIZONTAL_DISTRIBUTE "\xee\x80\x94" // U+e014 #define ICON_MD_HORIZONTAL_RULE "\xef\x84\x88" // U+f108 #define ICON_MD_HORIZONTAL_SPLIT "\xee\xa5\x87" // U+e947 #define ICON_MD_HOT_TUB "\xee\xad\x86" // U+eb46 #define ICON_MD_HOTEL "\xee\x94\xba" // U+e53a #define ICON_MD_HOTEL_CLASS "\xee\x9d\x83" // U+e743 #define ICON_MD_HOURGLASS_BOTTOM "\xee\xa9\x9c" // U+ea5c #define ICON_MD_HOURGLASS_DISABLED "\xee\xbd\x93" // U+ef53 #define ICON_MD_HOURGLASS_EMPTY "\xee\xa2\x8b" // U+e88b #define ICON_MD_HOURGLASS_FULL "\xee\xa2\x8c" // U+e88c #define ICON_MD_HOURGLASS_TOP "\xee\xa9\x9b" // U+ea5b #define ICON_MD_HOUSE "\xee\xa9\x84" // U+ea44 #define ICON_MD_HOUSE_SIDING "\xef\x88\x82" // U+f202 #define ICON_MD_HOUSEBOAT "\xee\x96\x84" // U+e584 #define ICON_MD_HOW_TO_REG "\xee\x85\xb4" // U+e174 #define ICON_MD_HOW_TO_VOTE "\xee\x85\xb5" // U+e175 #define ICON_MD_HTML "\xee\xad\xbe" // U+eb7e #define ICON_MD_HTTP "\xee\xa4\x82" // U+e902 #define ICON_MD_HTTPS "\xee\xa2\x8d" // U+e88d #define ICON_MD_HUB "\xee\xa7\xb4" // U+e9f4 #define ICON_MD_HVAC "\xef\x84\x8e" // U+f10e #define ICON_MD_ICE_SKATING "\xee\x94\x8b" // U+e50b #define ICON_MD_ICECREAM "\xee\xa9\xa9" // U+ea69 #define ICON_MD_IMAGE "\xee\x8f\xb4" // U+e3f4 #define ICON_MD_IMAGE_ASPECT_RATIO "\xee\x8f\xb5" // U+e3f5 #define ICON_MD_IMAGE_NOT_SUPPORTED "\xef\x84\x96" // U+f116 #define ICON_MD_IMAGE_SEARCH "\xee\x90\xbf" // U+e43f #define ICON_MD_IMAGESEARCH_ROLLER "\xee\xa6\xb4" // U+e9b4 #define ICON_MD_IMPORT_CONTACTS "\xee\x83\xa0" // U+e0e0 #define ICON_MD_IMPORT_EXPORT "\xee\x83\x83" // U+e0c3 #define ICON_MD_IMPORTANT_DEVICES "\xee\xa4\x92" // U+e912 #define ICON_MD_INBOX "\xee\x85\x96" // U+e156 #define ICON_MD_INCOMPLETE_CIRCLE "\xee\x9e\x9b" // U+e79b #define ICON_MD_INDETERMINATE_CHECK_BOX "\xee\xa4\x89" // U+e909 #define ICON_MD_INFO "\xee\xa2\x8e" // U+e88e #define ICON_MD_INFO_OUTLINE "\xee\xa2\x8f" // U+e88f #define ICON_MD_INPUT "\xee\xa2\x90" // U+e890 #define ICON_MD_INSERT_CHART "\xee\x89\x8b" // U+e24b #define ICON_MD_INSERT_CHART_OUTLINED "\xee\x89\xaa" // U+e26a #define ICON_MD_INSERT_COMMENT "\xee\x89\x8c" // U+e24c #define ICON_MD_INSERT_DRIVE_FILE "\xee\x89\x8d" // U+e24d #define ICON_MD_INSERT_EMOTICON "\xee\x89\x8e" // U+e24e #define ICON_MD_INSERT_INVITATION "\xee\x89\x8f" // U+e24f #define ICON_MD_INSERT_LINK "\xee\x89\x90" // U+e250 #define ICON_MD_INSERT_PAGE_BREAK "\xee\xab\x8a" // U+eaca #define ICON_MD_INSERT_PHOTO "\xee\x89\x91" // U+e251 #define ICON_MD_INSIGHTS "\xef\x82\x92" // U+f092 #define ICON_MD_INSTALL_DESKTOP "\xee\xad\xb1" // U+eb71 #define ICON_MD_INSTALL_MOBILE "\xee\xad\xb2" // U+eb72 #define ICON_MD_INTEGRATION_INSTRUCTIONS "\xee\xbd\x94" // U+ef54 #define ICON_MD_INTERESTS "\xee\x9f\x88" // U+e7c8 #define ICON_MD_INTERPRETER_MODE "\xee\xa0\xbb" // U+e83b #define ICON_MD_INVENTORY "\xee\x85\xb9" // U+e179 #define ICON_MD_INVENTORY_2 "\xee\x86\xa1" // U+e1a1 #define ICON_MD_INVERT_COLORS "\xee\xa2\x91" // U+e891 #define ICON_MD_INVERT_COLORS_OFF "\xee\x83\x84" // U+e0c4 #define ICON_MD_INVERT_COLORS_ON "\xee\xa2\x91" // U+e891 #define ICON_MD_IOS_SHARE "\xee\x9a\xb8" // U+e6b8 #define ICON_MD_IRON "\xee\x96\x83" // U+e583 #define ICON_MD_ISO "\xee\x8f\xb6" // U+e3f6 #define ICON_MD_JAVASCRIPT "\xee\xad\xbc" // U+eb7c #define ICON_MD_JOIN_FULL "\xee\xab\xab" // U+eaeb #define ICON_MD_JOIN_INNER "\xee\xab\xb4" // U+eaf4 #define ICON_MD_JOIN_LEFT "\xee\xab\xb2" // U+eaf2 #define ICON_MD_JOIN_RIGHT "\xee\xab\xaa" // U+eaea #define ICON_MD_KAYAKING "\xee\x94\x8c" // U+e50c #define ICON_MD_KEBAB_DINING "\xee\xa1\x82" // U+e842 #define ICON_MD_KEY "\xee\x9c\xbc" // U+e73c #define ICON_MD_KEY_OFF "\xee\xae\x84" // U+eb84 #define ICON_MD_KEYBOARD "\xee\x8c\x92" // U+e312 #define ICON_MD_KEYBOARD_ALT "\xef\x80\xa8" // U+f028 #define ICON_MD_KEYBOARD_ARROW_DOWN "\xee\x8c\x93" // U+e313 #define ICON_MD_KEYBOARD_ARROW_LEFT "\xee\x8c\x94" // U+e314 #define ICON_MD_KEYBOARD_ARROW_RIGHT "\xee\x8c\x95" // U+e315 #define ICON_MD_KEYBOARD_ARROW_UP "\xee\x8c\x96" // U+e316 #define ICON_MD_KEYBOARD_BACKSPACE "\xee\x8c\x97" // U+e317 #define ICON_MD_KEYBOARD_CAPSLOCK "\xee\x8c\x98" // U+e318 #define ICON_MD_KEYBOARD_COMMAND "\xee\xab\xa0" // U+eae0 #define ICON_MD_KEYBOARD_COMMAND_KEY "\xee\xab\xa7" // U+eae7 #define ICON_MD_KEYBOARD_CONTROL "\xee\x97\x93" // U+e5d3 #define ICON_MD_KEYBOARD_CONTROL_KEY "\xee\xab\xa6" // U+eae6 #define ICON_MD_KEYBOARD_DOUBLE_ARROW_DOWN "\xee\xab\x90" // U+ead0 #define ICON_MD_KEYBOARD_DOUBLE_ARROW_LEFT "\xee\xab\x83" // U+eac3 #define ICON_MD_KEYBOARD_DOUBLE_ARROW_RIGHT "\xee\xab\x89" // U+eac9 #define ICON_MD_KEYBOARD_DOUBLE_ARROW_UP "\xee\xab\x8f" // U+eacf #define ICON_MD_KEYBOARD_HIDE "\xee\x8c\x9a" // U+e31a #define ICON_MD_KEYBOARD_OPTION "\xee\xab\x9f" // U+eadf #define ICON_MD_KEYBOARD_OPTION_KEY "\xee\xab\xa8" // U+eae8 #define ICON_MD_KEYBOARD_RETURN "\xee\x8c\x9b" // U+e31b #define ICON_MD_KEYBOARD_TAB "\xee\x8c\x9c" // U+e31c #define ICON_MD_KEYBOARD_VOICE "\xee\x8c\x9d" // U+e31d #define ICON_MD_KING_BED "\xee\xa9\x85" // U+ea45 #define ICON_MD_KITCHEN "\xee\xad\x87" // U+eb47 #define ICON_MD_KITESURFING "\xee\x94\x8d" // U+e50d #define ICON_MD_LABEL "\xee\xa2\x92" // U+e892 #define ICON_MD_LABEL_IMPORTANT "\xee\xa4\xb7" // U+e937 #define ICON_MD_LABEL_IMPORTANT_OUTLINE "\xee\xa5\x88" // U+e948 #define ICON_MD_LABEL_OFF "\xee\xa6\xb6" // U+e9b6 #define ICON_MD_LABEL_OUTLINE "\xee\xa2\x93" // U+e893 #define ICON_MD_LAN "\xee\xac\xaf" // U+eb2f #define ICON_MD_LANDSCAPE "\xee\x8f\xb7" // U+e3f7 #define ICON_MD_LANDSLIDE "\xee\xaf\x97" // U+ebd7 #define ICON_MD_LANGUAGE "\xee\xa2\x94" // U+e894 #define ICON_MD_LAPTOP "\xee\x8c\x9e" // U+e31e #define ICON_MD_LAPTOP_CHROMEBOOK "\xee\x8c\x9f" // U+e31f #define ICON_MD_LAPTOP_MAC "\xee\x8c\xa0" // U+e320 #define ICON_MD_LAPTOP_WINDOWS "\xee\x8c\xa1" // U+e321 #define ICON_MD_LAST_PAGE "\xee\x97\x9d" // U+e5dd #define ICON_MD_LAUNCH "\xee\xa2\x95" // U+e895 #define ICON_MD_LAYERS "\xee\x94\xbb" // U+e53b #define ICON_MD_LAYERS_CLEAR "\xee\x94\xbc" // U+e53c #define ICON_MD_LEADERBOARD "\xef\x88\x8c" // U+f20c #define ICON_MD_LEAK_ADD "\xee\x8f\xb8" // U+e3f8 #define ICON_MD_LEAK_REMOVE "\xee\x8f\xb9" // U+e3f9 #define ICON_MD_LEAVE_BAGS_AT_HOME "\xef\x88\x9b" // U+f21b #define ICON_MD_LEGEND_TOGGLE "\xef\x84\x9b" // U+f11b #define ICON_MD_LENS "\xee\x8f\xba" // U+e3fa #define ICON_MD_LENS_BLUR "\xef\x80\xa9" // U+f029 #define ICON_MD_LIBRARY_ADD "\xee\x80\xae" // U+e02e #define ICON_MD_LIBRARY_ADD_CHECK "\xee\xa6\xb7" // U+e9b7 #define ICON_MD_LIBRARY_BOOKS "\xee\x80\xaf" // U+e02f #define ICON_MD_LIBRARY_MUSIC "\xee\x80\xb0" // U+e030 #define ICON_MD_LIGHT "\xef\x80\xaa" // U+f02a #define ICON_MD_LIGHT_MODE "\xee\x94\x98" // U+e518 #define ICON_MD_LIGHTBULB "\xee\x83\xb0" // U+e0f0 #define ICON_MD_LIGHTBULB_CIRCLE "\xee\xaf\xbe" // U+ebfe #define ICON_MD_LIGHTBULB_OUTLINE "\xee\xa4\x8f" // U+e90f #define ICON_MD_LINE_AXIS "\xee\xaa\x9a" // U+ea9a #define ICON_MD_LINE_STYLE "\xee\xa4\x99" // U+e919 #define ICON_MD_LINE_WEIGHT "\xee\xa4\x9a" // U+e91a #define ICON_MD_LINEAR_SCALE "\xee\x89\xa0" // U+e260 #define ICON_MD_LINK "\xee\x85\x97" // U+e157 #define ICON_MD_LINK_OFF "\xee\x85\xaf" // U+e16f #define ICON_MD_LINKED_CAMERA "\xee\x90\xb8" // U+e438 #define ICON_MD_LIQUOR "\xee\xa9\xa0" // U+ea60 #define ICON_MD_LIST "\xee\xa2\x96" // U+e896 #define ICON_MD_LIST_ALT "\xee\x83\xae" // U+e0ee #define ICON_MD_LIVE_HELP "\xee\x83\x86" // U+e0c6 #define ICON_MD_LIVE_TV "\xee\x98\xb9" // U+e639 #define ICON_MD_LIVING "\xef\x80\xab" // U+f02b #define ICON_MD_LOCAL_ACTIVITY "\xee\x94\xbf" // U+e53f #define ICON_MD_LOCAL_AIRPORT "\xee\x94\xbd" // U+e53d #define ICON_MD_LOCAL_ATM "\xee\x94\xbe" // U+e53e #define ICON_MD_LOCAL_ATTRACTION "\xee\x94\xbf" // U+e53f #define ICON_MD_LOCAL_BAR "\xee\x95\x80" // U+e540 #define ICON_MD_LOCAL_CAFE "\xee\x95\x81" // U+e541 #define ICON_MD_LOCAL_CAR_WASH "\xee\x95\x82" // U+e542 #define ICON_MD_LOCAL_CONVENIENCE_STORE "\xee\x95\x83" // U+e543 #define ICON_MD_LOCAL_DINING "\xee\x95\x96" // U+e556 #define ICON_MD_LOCAL_DRINK "\xee\x95\x84" // U+e544 #define ICON_MD_LOCAL_FIRE_DEPARTMENT "\xee\xbd\x95" // U+ef55 #define ICON_MD_LOCAL_FLORIST "\xee\x95\x85" // U+e545 #define ICON_MD_LOCAL_GAS_STATION "\xee\x95\x86" // U+e546 #define ICON_MD_LOCAL_GROCERY_STORE "\xee\x95\x87" // U+e547 #define ICON_MD_LOCAL_HOSPITAL "\xee\x95\x88" // U+e548 #define ICON_MD_LOCAL_HOTEL "\xee\x95\x89" // U+e549 #define ICON_MD_LOCAL_LAUNDRY_SERVICE "\xee\x95\x8a" // U+e54a #define ICON_MD_LOCAL_LIBRARY "\xee\x95\x8b" // U+e54b #define ICON_MD_LOCAL_MALL "\xee\x95\x8c" // U+e54c #define ICON_MD_LOCAL_MOVIES "\xee\x95\x8d" // U+e54d #define ICON_MD_LOCAL_OFFER "\xee\x95\x8e" // U+e54e #define ICON_MD_LOCAL_PARKING "\xee\x95\x8f" // U+e54f #define ICON_MD_LOCAL_PHARMACY "\xee\x95\x90" // U+e550 #define ICON_MD_LOCAL_PHONE "\xee\x95\x91" // U+e551 #define ICON_MD_LOCAL_PIZZA "\xee\x95\x92" // U+e552 #define ICON_MD_LOCAL_PLAY "\xee\x95\x93" // U+e553 #define ICON_MD_LOCAL_POLICE "\xee\xbd\x96" // U+ef56 #define ICON_MD_LOCAL_POST_OFFICE "\xee\x95\x94" // U+e554 #define ICON_MD_LOCAL_PRINT_SHOP "\xee\x95\x95" // U+e555 #define ICON_MD_LOCAL_PRINTSHOP "\xee\x95\x95" // U+e555 #define ICON_MD_LOCAL_RESTAURANT "\xee\x95\x96" // U+e556 #define ICON_MD_LOCAL_SEE "\xee\x95\x97" // U+e557 #define ICON_MD_LOCAL_SHIPPING "\xee\x95\x98" // U+e558 #define ICON_MD_LOCAL_TAXI "\xee\x95\x99" // U+e559 #define ICON_MD_LOCATION_CITY "\xee\x9f\xb1" // U+e7f1 #define ICON_MD_LOCATION_DISABLED "\xee\x86\xb6" // U+e1b6 #define ICON_MD_LOCATION_HISTORY "\xee\x95\x9a" // U+e55a #define ICON_MD_LOCATION_OFF "\xee\x83\x87" // U+e0c7 #define ICON_MD_LOCATION_ON "\xee\x83\x88" // U+e0c8 #define ICON_MD_LOCATION_PIN "\xef\x87\x9b" // U+f1db #define ICON_MD_LOCATION_SEARCHING "\xee\x86\xb7" // U+e1b7 #define ICON_MD_LOCK "\xee\xa2\x97" // U+e897 #define ICON_MD_LOCK_CLOCK "\xee\xbd\x97" // U+ef57 #define ICON_MD_LOCK_OPEN "\xee\xa2\x98" // U+e898 #define ICON_MD_LOCK_OUTLINE "\xee\xa2\x99" // U+e899 #define ICON_MD_LOCK_PERSON "\xef\xa3\xb3" // U+f8f3 #define ICON_MD_LOCK_RESET "\xee\xab\x9e" // U+eade #define ICON_MD_LOGIN "\xee\xa9\xb7" // U+ea77 #define ICON_MD_LOGO_DEV "\xee\xab\x96" // U+ead6 #define ICON_MD_LOGOUT "\xee\xa6\xba" // U+e9ba #define ICON_MD_LOOKS "\xee\x8f\xbc" // U+e3fc #define ICON_MD_LOOKS_3 "\xee\x8f\xbb" // U+e3fb #define ICON_MD_LOOKS_4 "\xee\x8f\xbd" // U+e3fd #define ICON_MD_LOOKS_5 "\xee\x8f\xbe" // U+e3fe #define ICON_MD_LOOKS_6 "\xee\x8f\xbf" // U+e3ff #define ICON_MD_LOOKS_ONE "\xee\x90\x80" // U+e400 #define ICON_MD_LOOKS_TWO "\xee\x90\x81" // U+e401 #define ICON_MD_LOOP "\xee\x80\xa8" // U+e028 #define ICON_MD_LOUPE "\xee\x90\x82" // U+e402 #define ICON_MD_LOW_PRIORITY "\xee\x85\xad" // U+e16d #define ICON_MD_LOYALTY "\xee\xa2\x9a" // U+e89a #define ICON_MD_LTE_MOBILEDATA "\xef\x80\xac" // U+f02c #define ICON_MD_LTE_PLUS_MOBILEDATA "\xef\x80\xad" // U+f02d #define ICON_MD_LUGGAGE "\xef\x88\xb5" // U+f235 #define ICON_MD_LUNCH_DINING "\xee\xa9\xa1" // U+ea61 #define ICON_MD_LYRICS "\xee\xb0\x8b" // U+ec0b #define ICON_MD_MACRO_OFF "\xef\xa3\x92" // U+f8d2 #define ICON_MD_MAIL "\xee\x85\x98" // U+e158 #define ICON_MD_MAIL_LOCK "\xee\xb0\x8a" // U+ec0a #define ICON_MD_MAIL_OUTLINE "\xee\x83\xa1" // U+e0e1 #define ICON_MD_MALE "\xee\x96\x8e" // U+e58e #define ICON_MD_MAN "\xee\x93\xab" // U+e4eb #define ICON_MD_MAN_2 "\xef\xa3\xa1" // U+f8e1 #define ICON_MD_MAN_3 "\xef\xa3\xa2" // U+f8e2 #define ICON_MD_MAN_4 "\xef\xa3\xa3" // U+f8e3 #define ICON_MD_MANAGE_ACCOUNTS "\xef\x80\xae" // U+f02e #define ICON_MD_MANAGE_HISTORY "\xee\xaf\xa7" // U+ebe7 #define ICON_MD_MANAGE_SEARCH "\xef\x80\xaf" // U+f02f #define ICON_MD_MAP "\xee\x95\x9b" // U+e55b #define ICON_MD_MAPS_HOME_WORK "\xef\x80\xb0" // U+f030 #define ICON_MD_MAPS_UGC "\xee\xbd\x98" // U+ef58 #define ICON_MD_MARGIN "\xee\xa6\xbb" // U+e9bb #define ICON_MD_MARK_AS_UNREAD "\xee\xa6\xbc" // U+e9bc #define ICON_MD_MARK_CHAT_READ "\xef\x86\x8b" // U+f18b #define ICON_MD_MARK_CHAT_UNREAD "\xef\x86\x89" // U+f189 #define ICON_MD_MARK_EMAIL_READ "\xef\x86\x8c" // U+f18c #define ICON_MD_MARK_EMAIL_UNREAD "\xef\x86\x8a" // U+f18a #define ICON_MD_MARK_UNREAD_CHAT_ALT "\xee\xae\x9d" // U+eb9d #define ICON_MD_MARKUNREAD "\xee\x85\x99" // U+e159 #define ICON_MD_MARKUNREAD_MAILBOX "\xee\xa2\x9b" // U+e89b #define ICON_MD_MASKS "\xef\x88\x98" // U+f218 #define ICON_MD_MAXIMIZE "\xee\xa4\xb0" // U+e930 #define ICON_MD_MEDIA_BLUETOOTH_OFF "\xef\x80\xb1" // U+f031 #define ICON_MD_MEDIA_BLUETOOTH_ON "\xef\x80\xb2" // U+f032 #define ICON_MD_MEDIATION "\xee\xbe\xa7" // U+efa7 #define ICON_MD_MEDICAL_INFORMATION "\xee\xaf\xad" // U+ebed #define ICON_MD_MEDICAL_SERVICES "\xef\x84\x89" // U+f109 #define ICON_MD_MEDICATION "\xef\x80\xb3" // U+f033 #define ICON_MD_MEDICATION_LIQUID "\xee\xaa\x87" // U+ea87 #define ICON_MD_MEETING_ROOM "\xee\xad\x8f" // U+eb4f #define ICON_MD_MEMORY "\xee\x8c\xa2" // U+e322 #define ICON_MD_MENU "\xee\x97\x92" // U+e5d2 #define ICON_MD_MENU_BOOK "\xee\xa8\x99" // U+ea19 #define ICON_MD_MENU_OPEN "\xee\xa6\xbd" // U+e9bd #define ICON_MD_MERGE "\xee\xae\x98" // U+eb98 #define ICON_MD_MERGE_TYPE "\xee\x89\x92" // U+e252 #define ICON_MD_MESSAGE "\xee\x83\x89" // U+e0c9 #define ICON_MD_MESSENGER "\xee\x83\x8a" // U+e0ca #define ICON_MD_MESSENGER_OUTLINE "\xee\x83\x8b" // U+e0cb #define ICON_MD_MIC "\xee\x80\xa9" // U+e029 #define ICON_MD_MIC_EXTERNAL_OFF "\xee\xbd\x99" // U+ef59 #define ICON_MD_MIC_EXTERNAL_ON "\xee\xbd\x9a" // U+ef5a #define ICON_MD_MIC_NONE "\xee\x80\xaa" // U+e02a #define ICON_MD_MIC_OFF "\xee\x80\xab" // U+e02b #define ICON_MD_MICROWAVE "\xef\x88\x84" // U+f204 #define ICON_MD_MILITARY_TECH "\xee\xa8\xbf" // U+ea3f #define ICON_MD_MINIMIZE "\xee\xa4\xb1" // U+e931 #define ICON_MD_MINOR_CRASH "\xee\xaf\xb1" // U+ebf1 #define ICON_MD_MISCELLANEOUS_SERVICES "\xef\x84\x8c" // U+f10c #define ICON_MD_MISSED_VIDEO_CALL "\xee\x81\xb3" // U+e073 #define ICON_MD_MMS "\xee\x98\x98" // U+e618 #define ICON_MD_MOBILE_FRIENDLY "\xee\x88\x80" // U+e200 #define ICON_MD_MOBILE_OFF "\xee\x88\x81" // U+e201 #define ICON_MD_MOBILE_SCREEN_SHARE "\xee\x83\xa7" // U+e0e7 #define ICON_MD_MOBILEDATA_OFF "\xef\x80\xb4" // U+f034 #define ICON_MD_MODE "\xef\x82\x97" // U+f097 #define ICON_MD_MODE_COMMENT "\xee\x89\x93" // U+e253 #define ICON_MD_MODE_EDIT "\xee\x89\x94" // U+e254 #define ICON_MD_MODE_EDIT_OUTLINE "\xef\x80\xb5" // U+f035 #define ICON_MD_MODE_FAN_OFF "\xee\xb0\x97" // U+ec17 #define ICON_MD_MODE_NIGHT "\xef\x80\xb6" // U+f036 #define ICON_MD_MODE_OF_TRAVEL "\xee\x9f\x8e" // U+e7ce #define ICON_MD_MODE_STANDBY "\xef\x80\xb7" // U+f037 #define ICON_MD_MODEL_TRAINING "\xef\x83\x8f" // U+f0cf #define ICON_MD_MONETIZATION_ON "\xee\x89\xa3" // U+e263 #define ICON_MD_MONEY "\xee\x95\xbd" // U+e57d #define ICON_MD_MONEY_OFF "\xee\x89\x9c" // U+e25c #define ICON_MD_MONEY_OFF_CSRED "\xef\x80\xb8" // U+f038 #define ICON_MD_MONITOR "\xee\xbd\x9b" // U+ef5b #define ICON_MD_MONITOR_HEART "\xee\xaa\xa2" // U+eaa2 #define ICON_MD_MONITOR_WEIGHT "\xef\x80\xb9" // U+f039 #define ICON_MD_MONOCHROME_PHOTOS "\xee\x90\x83" // U+e403 #define ICON_MD_MOOD "\xee\x9f\xb2" // U+e7f2 #define ICON_MD_MOOD_BAD "\xee\x9f\xb3" // U+e7f3 #define ICON_MD_MOPED "\xee\xac\xa8" // U+eb28 #define ICON_MD_MORE "\xee\x98\x99" // U+e619 #define ICON_MD_MORE_HORIZ "\xee\x97\x93" // U+e5d3 #define ICON_MD_MORE_TIME "\xee\xa9\x9d" // U+ea5d #define ICON_MD_MORE_VERT "\xee\x97\x94" // U+e5d4 #define ICON_MD_MOSQUE "\xee\xaa\xb2" // U+eab2 #define ICON_MD_MOTION_PHOTOS_AUTO "\xef\x80\xba" // U+f03a #define ICON_MD_MOTION_PHOTOS_OFF "\xee\xa7\x80" // U+e9c0 #define ICON_MD_MOTION_PHOTOS_ON "\xee\xa7\x81" // U+e9c1 #define ICON_MD_MOTION_PHOTOS_PAUSE "\xef\x88\xa7" // U+f227 #define ICON_MD_MOTION_PHOTOS_PAUSED "\xee\xa7\x82" // U+e9c2 #define ICON_MD_MOTORCYCLE "\xee\xa4\x9b" // U+e91b #define ICON_MD_MOUSE "\xee\x8c\xa3" // U+e323 #define ICON_MD_MOVE_DOWN "\xee\xad\xa1" // U+eb61 #define ICON_MD_MOVE_TO_INBOX "\xee\x85\xa8" // U+e168 #define ICON_MD_MOVE_UP "\xee\xad\xa4" // U+eb64 #define ICON_MD_MOVIE "\xee\x80\xac" // U+e02c #define ICON_MD_MOVIE_CREATION "\xee\x90\x84" // U+e404 #define ICON_MD_MOVIE_FILTER "\xee\x90\xba" // U+e43a #define ICON_MD_MOVING "\xee\x94\x81" // U+e501 #define ICON_MD_MP "\xee\xa7\x83" // U+e9c3 #define ICON_MD_MULTILINE_CHART "\xee\x9b\x9f" // U+e6df #define ICON_MD_MULTIPLE_STOP "\xef\x86\xb9" // U+f1b9 #define ICON_MD_MULTITRACK_AUDIO "\xee\x86\xb8" // U+e1b8 #define ICON_MD_MUSEUM "\xee\xa8\xb6" // U+ea36 #define ICON_MD_MUSIC_NOTE "\xee\x90\x85" // U+e405 #define ICON_MD_MUSIC_OFF "\xee\x91\x80" // U+e440 #define ICON_MD_MUSIC_VIDEO "\xee\x81\xa3" // U+e063 #define ICON_MD_MY_LIBRARY_ADD "\xee\x80\xae" // U+e02e #define ICON_MD_MY_LIBRARY_BOOKS "\xee\x80\xaf" // U+e02f #define ICON_MD_MY_LIBRARY_MUSIC "\xee\x80\xb0" // U+e030 #define ICON_MD_MY_LOCATION "\xee\x95\x9c" // U+e55c #define ICON_MD_NAT "\xee\xbd\x9c" // U+ef5c #define ICON_MD_NATURE "\xee\x90\x86" // U+e406 #define ICON_MD_NATURE_PEOPLE "\xee\x90\x87" // U+e407 #define ICON_MD_NAVIGATE_BEFORE "\xee\x90\x88" // U+e408 #define ICON_MD_NAVIGATE_NEXT "\xee\x90\x89" // U+e409 #define ICON_MD_NAVIGATION "\xee\x95\x9d" // U+e55d #define ICON_MD_NEAR_ME "\xee\x95\xa9" // U+e569 #define ICON_MD_NEAR_ME_DISABLED "\xef\x87\xaf" // U+f1ef #define ICON_MD_NEARBY_ERROR "\xef\x80\xbb" // U+f03b #define ICON_MD_NEARBY_OFF "\xef\x80\xbc" // U+f03c #define ICON_MD_NEST_CAM_WIRED_STAND "\xee\xb0\x96" // U+ec16 #define ICON_MD_NETWORK_CELL "\xee\x86\xb9" // U+e1b9 #define ICON_MD_NETWORK_CHECK "\xee\x99\x80" // U+e640 #define ICON_MD_NETWORK_LOCKED "\xee\x98\x9a" // U+e61a #define ICON_MD_NETWORK_PING "\xee\xaf\x8a" // U+ebca #define ICON_MD_NETWORK_WIFI "\xee\x86\xba" // U+e1ba #define ICON_MD_NETWORK_WIFI_1_BAR "\xee\xaf\xa4" // U+ebe4 #define ICON_MD_NETWORK_WIFI_2_BAR "\xee\xaf\x96" // U+ebd6 #define ICON_MD_NETWORK_WIFI_3_BAR "\xee\xaf\xa1" // U+ebe1 #define ICON_MD_NEW_LABEL "\xee\x98\x89" // U+e609 #define ICON_MD_NEW_RELEASES "\xee\x80\xb1" // U+e031 #define ICON_MD_NEWSPAPER "\xee\xae\x81" // U+eb81 #define ICON_MD_NEXT_PLAN "\xee\xbd\x9d" // U+ef5d #define ICON_MD_NEXT_WEEK "\xee\x85\xaa" // U+e16a #define ICON_MD_NFC "\xee\x86\xbb" // U+e1bb #define ICON_MD_NIGHT_SHELTER "\xef\x87\xb1" // U+f1f1 #define ICON_MD_NIGHTLIFE "\xee\xa9\xa2" // U+ea62 #define ICON_MD_NIGHTLIGHT "\xef\x80\xbd" // U+f03d #define ICON_MD_NIGHTLIGHT_ROUND "\xee\xbd\x9e" // U+ef5e #define ICON_MD_NIGHTS_STAY "\xee\xa9\x86" // U+ea46 #define ICON_MD_NO_ACCOUNTS "\xef\x80\xbe" // U+f03e #define ICON_MD_NO_ADULT_CONTENT "\xef\xa3\xbe" // U+f8fe #define ICON_MD_NO_BACKPACK "\xef\x88\xb7" // U+f237 #define ICON_MD_NO_CELL "\xef\x86\xa4" // U+f1a4 #define ICON_MD_NO_CRASH "\xee\xaf\xb0" // U+ebf0 #define ICON_MD_NO_DRINKS "\xef\x86\xa5" // U+f1a5 #define ICON_MD_NO_ENCRYPTION "\xee\x99\x81" // U+e641 #define ICON_MD_NO_ENCRYPTION_GMAILERRORRED "\xef\x80\xbf" // U+f03f #define ICON_MD_NO_FLASH "\xef\x86\xa6" // U+f1a6 #define ICON_MD_NO_FOOD "\xef\x86\xa7" // U+f1a7 #define ICON_MD_NO_LUGGAGE "\xef\x88\xbb" // U+f23b #define ICON_MD_NO_MEALS "\xef\x87\x96" // U+f1d6 #define ICON_MD_NO_MEALS_OULINE "\xef\x88\xa9" // U+f229 #define ICON_MD_NO_MEETING_ROOM "\xee\xad\x8e" // U+eb4e #define ICON_MD_NO_PHOTOGRAPHY "\xef\x86\xa8" // U+f1a8 #define ICON_MD_NO_SIM "\xee\x83\x8c" // U+e0cc #define ICON_MD_NO_STROLLER "\xef\x86\xaf" // U+f1af #define ICON_MD_NO_TRANSFER "\xef\x87\x95" // U+f1d5 #define ICON_MD_NOISE_AWARE "\xee\xaf\xac" // U+ebec #define ICON_MD_NOISE_CONTROL_OFF "\xee\xaf\xb3" // U+ebf3 #define ICON_MD_NORDIC_WALKING "\xee\x94\x8e" // U+e50e #define ICON_MD_NORTH "\xef\x87\xa0" // U+f1e0 #define ICON_MD_NORTH_EAST "\xef\x87\xa1" // U+f1e1 #define ICON_MD_NORTH_WEST "\xef\x87\xa2" // U+f1e2 #define ICON_MD_NOT_ACCESSIBLE "\xef\x83\xbe" // U+f0fe #define ICON_MD_NOT_INTERESTED "\xee\x80\xb3" // U+e033 #define ICON_MD_NOT_LISTED_LOCATION "\xee\x95\xb5" // U+e575 #define ICON_MD_NOT_STARTED "\xef\x83\x91" // U+f0d1 #define ICON_MD_NOTE "\xee\x81\xaf" // U+e06f #define ICON_MD_NOTE_ADD "\xee\xa2\x9c" // U+e89c #define ICON_MD_NOTE_ALT "\xef\x81\x80" // U+f040 #define ICON_MD_NOTES "\xee\x89\xac" // U+e26c #define ICON_MD_NOTIFICATION_ADD "\xee\x8e\x99" // U+e399 #define ICON_MD_NOTIFICATION_IMPORTANT "\xee\x80\x84" // U+e004 #define ICON_MD_NOTIFICATIONS "\xee\x9f\xb4" // U+e7f4 #define ICON_MD_NOTIFICATIONS_ACTIVE "\xee\x9f\xb7" // U+e7f7 #define ICON_MD_NOTIFICATIONS_NONE "\xee\x9f\xb5" // U+e7f5 #define ICON_MD_NOTIFICATIONS_OFF "\xee\x9f\xb6" // U+e7f6 #define ICON_MD_NOTIFICATIONS_ON "\xee\x9f\xb7" // U+e7f7 #define ICON_MD_NOTIFICATIONS_PAUSED "\xee\x9f\xb8" // U+e7f8 #define ICON_MD_NOW_WALLPAPER "\xee\x86\xbc" // U+e1bc #define ICON_MD_NOW_WIDGETS "\xee\x86\xbd" // U+e1bd #define ICON_MD_NUMBERS "\xee\xab\x87" // U+eac7 #define ICON_MD_OFFLINE_BOLT "\xee\xa4\xb2" // U+e932 #define ICON_MD_OFFLINE_PIN "\xee\xa4\x8a" // U+e90a #define ICON_MD_OFFLINE_SHARE "\xee\xa7\x85" // U+e9c5 #define ICON_MD_OIL_BARREL "\xee\xb0\x95" // U+ec15 #define ICON_MD_ON_DEVICE_TRAINING "\xee\xaf\xbd" // U+ebfd #define ICON_MD_ONDEMAND_VIDEO "\xee\x98\xba" // U+e63a #define ICON_MD_ONLINE_PREDICTION "\xef\x83\xab" // U+f0eb #define ICON_MD_OPACITY "\xee\xa4\x9c" // U+e91c #define ICON_MD_OPEN_IN_BROWSER "\xee\xa2\x9d" // U+e89d #define ICON_MD_OPEN_IN_FULL "\xef\x87\x8e" // U+f1ce #define ICON_MD_OPEN_IN_NEW "\xee\xa2\x9e" // U+e89e #define ICON_MD_OPEN_IN_NEW_OFF "\xee\x93\xb6" // U+e4f6 #define ICON_MD_OPEN_WITH "\xee\xa2\x9f" // U+e89f #define ICON_MD_OTHER_HOUSES "\xee\x96\x8c" // U+e58c #define ICON_MD_OUTBOND "\xef\x88\xa8" // U+f228 #define ICON_MD_OUTBOUND "\xee\x87\x8a" // U+e1ca #define ICON_MD_OUTBOX "\xee\xbd\x9f" // U+ef5f #define ICON_MD_OUTDOOR_GRILL "\xee\xa9\x87" // U+ea47 #define ICON_MD_OUTGOING_MAIL "\xef\x83\x92" // U+f0d2 #define ICON_MD_OUTLET "\xef\x87\x94" // U+f1d4 #define ICON_MD_OUTLINED_FLAG "\xee\x85\xae" // U+e16e #define ICON_MD_OUTPUT "\xee\xae\xbe" // U+ebbe #define ICON_MD_PADDING "\xee\xa7\x88" // U+e9c8 #define ICON_MD_PAGES "\xee\x9f\xb9" // U+e7f9 #define ICON_MD_PAGEVIEW "\xee\xa2\xa0" // U+e8a0 #define ICON_MD_PAID "\xef\x81\x81" // U+f041 #define ICON_MD_PALETTE "\xee\x90\x8a" // U+e40a #define ICON_MD_PAN_TOOL "\xee\xa4\xa5" // U+e925 #define ICON_MD_PAN_TOOL_ALT "\xee\xae\xb9" // U+ebb9 #define ICON_MD_PANORAMA "\xee\x90\x8b" // U+e40b #define ICON_MD_PANORAMA_FISH_EYE "\xee\x90\x8c" // U+e40c #define ICON_MD_PANORAMA_FISHEYE "\xee\x90\x8c" // U+e40c #define ICON_MD_PANORAMA_HORIZONTAL "\xee\x90\x8d" // U+e40d #define ICON_MD_PANORAMA_HORIZONTAL_SELECT "\xee\xbd\xa0" // U+ef60 #define ICON_MD_PANORAMA_PHOTOSPHERE "\xee\xa7\x89" // U+e9c9 #define ICON_MD_PANORAMA_PHOTOSPHERE_SELECT "\xee\xa7\x8a" // U+e9ca #define ICON_MD_PANORAMA_VERTICAL "\xee\x90\x8e" // U+e40e #define ICON_MD_PANORAMA_VERTICAL_SELECT "\xee\xbd\xa1" // U+ef61 #define ICON_MD_PANORAMA_WIDE_ANGLE "\xee\x90\x8f" // U+e40f #define ICON_MD_PANORAMA_WIDE_ANGLE_SELECT "\xee\xbd\xa2" // U+ef62 #define ICON_MD_PARAGLIDING "\xee\x94\x8f" // U+e50f #define ICON_MD_PARK "\xee\xa9\xa3" // U+ea63 #define ICON_MD_PARTY_MODE "\xee\x9f\xba" // U+e7fa #define ICON_MD_PASSWORD "\xef\x81\x82" // U+f042 #define ICON_MD_PATTERN "\xef\x81\x83" // U+f043 #define ICON_MD_PAUSE "\xee\x80\xb4" // U+e034 #define ICON_MD_PAUSE_CIRCLE "\xee\x86\xa2" // U+e1a2 #define ICON_MD_PAUSE_CIRCLE_FILLED "\xee\x80\xb5" // U+e035 #define ICON_MD_PAUSE_CIRCLE_OUTLINE "\xee\x80\xb6" // U+e036 #define ICON_MD_PAUSE_PRESENTATION "\xee\x83\xaa" // U+e0ea #define ICON_MD_PAYMENT "\xee\xa2\xa1" // U+e8a1 #define ICON_MD_PAYMENTS "\xee\xbd\xa3" // U+ef63 #define ICON_MD_PAYPAL "\xee\xaa\x8d" // U+ea8d #define ICON_MD_PEDAL_BIKE "\xee\xac\xa9" // U+eb29 #define ICON_MD_PENDING "\xee\xbd\xa4" // U+ef64 #define ICON_MD_PENDING_ACTIONS "\xef\x86\xbb" // U+f1bb #define ICON_MD_PENTAGON "\xee\xad\x90" // U+eb50 #define ICON_MD_PEOPLE "\xee\x9f\xbb" // U+e7fb #define ICON_MD_PEOPLE_ALT "\xee\xa8\xa1" // U+ea21 #define ICON_MD_PEOPLE_OUTLINE "\xee\x9f\xbc" // U+e7fc #define ICON_MD_PERCENT "\xee\xad\x98" // U+eb58 #define ICON_MD_PERM_CAMERA_MIC "\xee\xa2\xa2" // U+e8a2 #define ICON_MD_PERM_CONTACT_CAL "\xee\xa2\xa3" // U+e8a3 #define ICON_MD_PERM_CONTACT_CALENDAR "\xee\xa2\xa3" // U+e8a3 #define ICON_MD_PERM_DATA_SETTING "\xee\xa2\xa4" // U+e8a4 #define ICON_MD_PERM_DEVICE_INFO "\xee\xa2\xa5" // U+e8a5 #define ICON_MD_PERM_DEVICE_INFORMATION "\xee\xa2\xa5" // U+e8a5 #define ICON_MD_PERM_IDENTITY "\xee\xa2\xa6" // U+e8a6 #define ICON_MD_PERM_MEDIA "\xee\xa2\xa7" // U+e8a7 #define ICON_MD_PERM_PHONE_MSG "\xee\xa2\xa8" // U+e8a8 #define ICON_MD_PERM_SCAN_WIFI "\xee\xa2\xa9" // U+e8a9 #define ICON_MD_PERSON "\xee\x9f\xbd" // U+e7fd #define ICON_MD_PERSON_2 "\xef\xa3\xa4" // U+f8e4 #define ICON_MD_PERSON_3 "\xef\xa3\xa5" // U+f8e5 #define ICON_MD_PERSON_4 "\xef\xa3\xa6" // U+f8e6 #define ICON_MD_PERSON_ADD "\xee\x9f\xbe" // U+e7fe #define ICON_MD_PERSON_ADD_ALT "\xee\xa9\x8d" // U+ea4d #define ICON_MD_PERSON_ADD_ALT_1 "\xee\xbd\xa5" // U+ef65 #define ICON_MD_PERSON_ADD_DISABLED "\xee\xa7\x8b" // U+e9cb #define ICON_MD_PERSON_OFF "\xee\x94\x90" // U+e510 #define ICON_MD_PERSON_OUTLINE "\xee\x9f\xbf" // U+e7ff #define ICON_MD_PERSON_PIN "\xee\x95\x9a" // U+e55a #define ICON_MD_PERSON_PIN_CIRCLE "\xee\x95\xaa" // U+e56a #define ICON_MD_PERSON_REMOVE "\xee\xbd\xa6" // U+ef66 #define ICON_MD_PERSON_REMOVE_ALT_1 "\xee\xbd\xa7" // U+ef67 #define ICON_MD_PERSON_SEARCH "\xef\x84\x86" // U+f106 #define ICON_MD_PERSONAL_INJURY "\xee\x9b\x9a" // U+e6da #define ICON_MD_PERSONAL_VIDEO "\xee\x98\xbb" // U+e63b #define ICON_MD_PEST_CONTROL "\xef\x83\xba" // U+f0fa #define ICON_MD_PEST_CONTROL_RODENT "\xef\x83\xbd" // U+f0fd #define ICON_MD_PETS "\xee\xa4\x9d" // U+e91d #define ICON_MD_PHISHING "\xee\xab\x97" // U+ead7 #define ICON_MD_PHONE "\xee\x83\x8d" // U+e0cd #define ICON_MD_PHONE_ANDROID "\xee\x8c\xa4" // U+e324 #define ICON_MD_PHONE_BLUETOOTH_SPEAKER "\xee\x98\x9b" // U+e61b #define ICON_MD_PHONE_CALLBACK "\xee\x99\x89" // U+e649 #define ICON_MD_PHONE_DISABLED "\xee\xa7\x8c" // U+e9cc #define ICON_MD_PHONE_ENABLED "\xee\xa7\x8d" // U+e9cd #define ICON_MD_PHONE_FORWARDED "\xee\x98\x9c" // U+e61c #define ICON_MD_PHONE_IN_TALK "\xee\x98\x9d" // U+e61d #define ICON_MD_PHONE_IPHONE "\xee\x8c\xa5" // U+e325 #define ICON_MD_PHONE_LOCKED "\xee\x98\x9e" // U+e61e #define ICON_MD_PHONE_MISSED "\xee\x98\x9f" // U+e61f #define ICON_MD_PHONE_PAUSED "\xee\x98\xa0" // U+e620 #define ICON_MD_PHONELINK "\xee\x8c\xa6" // U+e326 #define ICON_MD_PHONELINK_ERASE "\xee\x83\x9b" // U+e0db #define ICON_MD_PHONELINK_LOCK "\xee\x83\x9c" // U+e0dc #define ICON_MD_PHONELINK_OFF "\xee\x8c\xa7" // U+e327 #define ICON_MD_PHONELINK_RING "\xee\x83\x9d" // U+e0dd #define ICON_MD_PHONELINK_SETUP "\xee\x83\x9e" // U+e0de #define ICON_MD_PHOTO "\xee\x90\x90" // U+e410 #define ICON_MD_PHOTO_ALBUM "\xee\x90\x91" // U+e411 #define ICON_MD_PHOTO_CAMERA "\xee\x90\x92" // U+e412 #define ICON_MD_PHOTO_CAMERA_BACK "\xee\xbd\xa8" // U+ef68 #define ICON_MD_PHOTO_CAMERA_FRONT "\xee\xbd\xa9" // U+ef69 #define ICON_MD_PHOTO_FILTER "\xee\x90\xbb" // U+e43b #define ICON_MD_PHOTO_LIBRARY "\xee\x90\x93" // U+e413 #define ICON_MD_PHOTO_SIZE_SELECT_ACTUAL "\xee\x90\xb2" // U+e432 #define ICON_MD_PHOTO_SIZE_SELECT_LARGE "\xee\x90\xb3" // U+e433 #define ICON_MD_PHOTO_SIZE_SELECT_SMALL "\xee\x90\xb4" // U+e434 #define ICON_MD_PHP "\xee\xae\x8f" // U+eb8f #define ICON_MD_PIANO "\xee\x94\xa1" // U+e521 #define ICON_MD_PIANO_OFF "\xee\x94\xa0" // U+e520 #define ICON_MD_PICTURE_AS_PDF "\xee\x90\x95" // U+e415 #define ICON_MD_PICTURE_IN_PICTURE "\xee\xa2\xaa" // U+e8aa #define ICON_MD_PICTURE_IN_PICTURE_ALT "\xee\xa4\x91" // U+e911 #define ICON_MD_PIE_CHART "\xee\x9b\x84" // U+e6c4 #define ICON_MD_PIE_CHART_OUTLINE "\xef\x81\x84" // U+f044 #define ICON_MD_PIE_CHART_OUTLINED "\xee\x9b\x85" // U+e6c5 #define ICON_MD_PIN "\xef\x81\x85" // U+f045 #define ICON_MD_PIN_DROP "\xee\x95\x9e" // U+e55e #define ICON_MD_PIN_END "\xee\x9d\xa7" // U+e767 #define ICON_MD_PIN_INVOKE "\xee\x9d\xa3" // U+e763 #define ICON_MD_PINCH "\xee\xac\xb8" // U+eb38 #define ICON_MD_PIVOT_TABLE_CHART "\xee\xa7\x8e" // U+e9ce #define ICON_MD_PIX "\xee\xaa\xa3" // U+eaa3 #define ICON_MD_PLACE "\xee\x95\x9f" // U+e55f #define ICON_MD_PLAGIARISM "\xee\xa9\x9a" // U+ea5a #define ICON_MD_PLAY_ARROW "\xee\x80\xb7" // U+e037 #define ICON_MD_PLAY_CIRCLE "\xee\x87\x84" // U+e1c4 #define ICON_MD_PLAY_CIRCLE_FILL "\xee\x80\xb8" // U+e038 #define ICON_MD_PLAY_CIRCLE_FILLED "\xee\x80\xb8" // U+e038 #define ICON_MD_PLAY_CIRCLE_OUTLINE "\xee\x80\xb9" // U+e039 #define ICON_MD_PLAY_DISABLED "\xee\xbd\xaa" // U+ef6a #define ICON_MD_PLAY_FOR_WORK "\xee\xa4\x86" // U+e906 #define ICON_MD_PLAY_LESSON "\xef\x81\x87" // U+f047 #define ICON_MD_PLAYLIST_ADD "\xee\x80\xbb" // U+e03b #define ICON_MD_PLAYLIST_ADD_CHECK "\xee\x81\xa5" // U+e065 #define ICON_MD_PLAYLIST_ADD_CHECK_CIRCLE "\xee\x9f\xa6" // U+e7e6 #define ICON_MD_PLAYLIST_ADD_CIRCLE "\xee\x9f\xa5" // U+e7e5 #define ICON_MD_PLAYLIST_PLAY "\xee\x81\x9f" // U+e05f #define ICON_MD_PLAYLIST_REMOVE "\xee\xae\x80" // U+eb80 #define ICON_MD_PLUMBING "\xef\x84\x87" // U+f107 #define ICON_MD_PLUS_ONE "\xee\xa0\x80" // U+e800 #define ICON_MD_PODCASTS "\xef\x81\x88" // U+f048 #define ICON_MD_POINT_OF_SALE "\xef\x85\xbe" // U+f17e #define ICON_MD_POLICY "\xee\xa8\x97" // U+ea17 #define ICON_MD_POLL "\xee\xa0\x81" // U+e801 #define ICON_MD_POLYLINE "\xee\xae\xbb" // U+ebbb #define ICON_MD_POLYMER "\xee\xa2\xab" // U+e8ab #define ICON_MD_POOL "\xee\xad\x88" // U+eb48 #define ICON_MD_PORTABLE_WIFI_OFF "\xee\x83\x8e" // U+e0ce #define ICON_MD_PORTRAIT "\xee\x90\x96" // U+e416 #define ICON_MD_POST_ADD "\xee\xa8\xa0" // U+ea20 #define ICON_MD_POWER "\xee\x98\xbc" // U+e63c #define ICON_MD_POWER_INPUT "\xee\x8c\xb6" // U+e336 #define ICON_MD_POWER_OFF "\xee\x99\x86" // U+e646 #define ICON_MD_POWER_SETTINGS_NEW "\xee\xa2\xac" // U+e8ac #define ICON_MD_PRECISION_MANUFACTURING "\xef\x81\x89" // U+f049 #define ICON_MD_PREGNANT_WOMAN "\xee\xa4\x9e" // U+e91e #define ICON_MD_PRESENT_TO_ALL "\xee\x83\x9f" // U+e0df #define ICON_MD_PREVIEW "\xef\x87\x85" // U+f1c5 #define ICON_MD_PRICE_CHANGE "\xef\x81\x8a" // U+f04a #define ICON_MD_PRICE_CHECK "\xef\x81\x8b" // U+f04b #define ICON_MD_PRINT "\xee\xa2\xad" // U+e8ad #define ICON_MD_PRINT_DISABLED "\xee\xa7\x8f" // U+e9cf #define ICON_MD_PRIORITY_HIGH "\xee\x99\x85" // U+e645 #define ICON_MD_PRIVACY_TIP "\xef\x83\x9c" // U+f0dc #define ICON_MD_PRIVATE_CONNECTIVITY "\xee\x9d\x84" // U+e744 #define ICON_MD_PRODUCTION_QUANTITY_LIMITS "\xee\x87\x91" // U+e1d1 #define ICON_MD_PROPANE "\xee\xb0\x94" // U+ec14 #define ICON_MD_PROPANE_TANK "\xee\xb0\x93" // U+ec13 #define ICON_MD_PSYCHOLOGY "\xee\xa9\x8a" // U+ea4a #define ICON_MD_PSYCHOLOGY_ALT "\xef\xa3\xaa" // U+f8ea #define ICON_MD_PUBLIC "\xee\xa0\x8b" // U+e80b #define ICON_MD_PUBLIC_OFF "\xef\x87\x8a" // U+f1ca #define ICON_MD_PUBLISH "\xee\x89\x95" // U+e255 #define ICON_MD_PUBLISHED_WITH_CHANGES "\xef\x88\xb2" // U+f232 #define ICON_MD_PUNCH_CLOCK "\xee\xaa\xa8" // U+eaa8 #define ICON_MD_PUSH_PIN "\xef\x84\x8d" // U+f10d #define ICON_MD_QR_CODE "\xee\xbd\xab" // U+ef6b #define ICON_MD_QR_CODE_2 "\xee\x80\x8a" // U+e00a #define ICON_MD_QR_CODE_SCANNER "\xef\x88\x86" // U+f206 #define ICON_MD_QUERY_BUILDER "\xee\xa2\xae" // U+e8ae #define ICON_MD_QUERY_STATS "\xee\x93\xbc" // U+e4fc #define ICON_MD_QUESTION_ANSWER "\xee\xa2\xaf" // U+e8af #define ICON_MD_QUESTION_MARK "\xee\xae\x8b" // U+eb8b #define ICON_MD_QUEUE "\xee\x80\xbc" // U+e03c #define ICON_MD_QUEUE_MUSIC "\xee\x80\xbd" // U+e03d #define ICON_MD_QUEUE_PLAY_NEXT "\xee\x81\xa6" // U+e066 #define ICON_MD_QUICK_CONTACTS_DIALER "\xee\x83\x8f" // U+e0cf #define ICON_MD_QUICK_CONTACTS_MAIL "\xee\x83\x90" // U+e0d0 #define ICON_MD_QUICKREPLY "\xee\xbd\xac" // U+ef6c #define ICON_MD_QUIZ "\xef\x81\x8c" // U+f04c #define ICON_MD_QUORA "\xee\xaa\x98" // U+ea98 #define ICON_MD_R_MOBILEDATA "\xef\x81\x8d" // U+f04d #define ICON_MD_RADAR "\xef\x81\x8e" // U+f04e #define ICON_MD_RADIO "\xee\x80\xbe" // U+e03e #define ICON_MD_RADIO_BUTTON_CHECKED "\xee\xa0\xb7" // U+e837 #define ICON_MD_RADIO_BUTTON_OFF "\xee\xa0\xb6" // U+e836 #define ICON_MD_RADIO_BUTTON_ON "\xee\xa0\xb7" // U+e837 #define ICON_MD_RADIO_BUTTON_UNCHECKED "\xee\xa0\xb6" // U+e836 #define ICON_MD_RAILWAY_ALERT "\xee\xa7\x91" // U+e9d1 #define ICON_MD_RAMEN_DINING "\xee\xa9\xa4" // U+ea64 #define ICON_MD_RAMP_LEFT "\xee\xae\x9c" // U+eb9c #define ICON_MD_RAMP_RIGHT "\xee\xae\x96" // U+eb96 #define ICON_MD_RATE_REVIEW "\xee\x95\xa0" // U+e560 #define ICON_MD_RAW_OFF "\xef\x81\x8f" // U+f04f #define ICON_MD_RAW_ON "\xef\x81\x90" // U+f050 #define ICON_MD_READ_MORE "\xee\xbd\xad" // U+ef6d #define ICON_MD_REAL_ESTATE_AGENT "\xee\x9c\xba" // U+e73a #define ICON_MD_RECEIPT "\xee\xa2\xb0" // U+e8b0 #define ICON_MD_RECEIPT_LONG "\xee\xbd\xae" // U+ef6e #define ICON_MD_RECENT_ACTORS "\xee\x80\xbf" // U+e03f #define ICON_MD_RECOMMEND "\xee\xa7\x92" // U+e9d2 #define ICON_MD_RECORD_VOICE_OVER "\xee\xa4\x9f" // U+e91f #define ICON_MD_RECTANGLE "\xee\xad\x94" // U+eb54 #define ICON_MD_RECYCLING "\xee\x9d\xa0" // U+e760 #define ICON_MD_REDDIT "\xee\xaa\xa0" // U+eaa0 #define ICON_MD_REDEEM "\xee\xa2\xb1" // U+e8b1 #define ICON_MD_REDO "\xee\x85\x9a" // U+e15a #define ICON_MD_REDUCE_CAPACITY "\xef\x88\x9c" // U+f21c #define ICON_MD_REFRESH "\xee\x97\x95" // U+e5d5 #define ICON_MD_REMEMBER_ME "\xef\x81\x91" // U+f051 #define ICON_MD_REMOVE "\xee\x85\x9b" // U+e15b #define ICON_MD_REMOVE_CIRCLE "\xee\x85\x9c" // U+e15c #define ICON_MD_REMOVE_CIRCLE_OUTLINE "\xee\x85\x9d" // U+e15d #define ICON_MD_REMOVE_DONE "\xee\xa7\x93" // U+e9d3 #define ICON_MD_REMOVE_FROM_QUEUE "\xee\x81\xa7" // U+e067 #define ICON_MD_REMOVE_MODERATOR "\xee\xa7\x94" // U+e9d4 #define ICON_MD_REMOVE_RED_EYE "\xee\x90\x97" // U+e417 #define ICON_MD_REMOVE_ROAD "\xee\xaf\xbc" // U+ebfc #define ICON_MD_REMOVE_SHOPPING_CART "\xee\xa4\xa8" // U+e928 #define ICON_MD_REORDER "\xee\xa3\xbe" // U+e8fe #define ICON_MD_REPARTITION "\xef\xa3\xa8" // U+f8e8 #define ICON_MD_REPEAT "\xee\x81\x80" // U+e040 #define ICON_MD_REPEAT_ON "\xee\xa7\x96" // U+e9d6 #define ICON_MD_REPEAT_ONE "\xee\x81\x81" // U+e041 #define ICON_MD_REPEAT_ONE_ON "\xee\xa7\x97" // U+e9d7 #define ICON_MD_REPLAY "\xee\x81\x82" // U+e042 #define ICON_MD_REPLAY_10 "\xee\x81\x99" // U+e059 #define ICON_MD_REPLAY_30 "\xee\x81\x9a" // U+e05a #define ICON_MD_REPLAY_5 "\xee\x81\x9b" // U+e05b #define ICON_MD_REPLAY_CIRCLE_FILLED "\xee\xa7\x98" // U+e9d8 #define ICON_MD_REPLY "\xee\x85\x9e" // U+e15e #define ICON_MD_REPLY_ALL "\xee\x85\x9f" // U+e15f #define ICON_MD_REPORT "\xee\x85\xa0" // U+e160 #define ICON_MD_REPORT_GMAILERRORRED "\xef\x81\x92" // U+f052 #define ICON_MD_REPORT_OFF "\xee\x85\xb0" // U+e170 #define ICON_MD_REPORT_PROBLEM "\xee\xa2\xb2" // U+e8b2 #define ICON_MD_REQUEST_PAGE "\xef\x88\xac" // U+f22c #define ICON_MD_REQUEST_QUOTE "\xef\x86\xb6" // U+f1b6 #define ICON_MD_RESET_TV "\xee\xa7\x99" // U+e9d9 #define ICON_MD_RESTART_ALT "\xef\x81\x93" // U+f053 #define ICON_MD_RESTAURANT "\xee\x95\xac" // U+e56c #define ICON_MD_RESTAURANT_MENU "\xee\x95\xa1" // U+e561 #define ICON_MD_RESTORE "\xee\xa2\xb3" // U+e8b3 #define ICON_MD_RESTORE_FROM_TRASH "\xee\xa4\xb8" // U+e938 #define ICON_MD_RESTORE_PAGE "\xee\xa4\xa9" // U+e929 #define ICON_MD_REVIEWS "\xef\x81\x94" // U+f054 #define ICON_MD_RICE_BOWL "\xef\x87\xb5" // U+f1f5 #define ICON_MD_RING_VOLUME "\xee\x83\x91" // U+e0d1 #define ICON_MD_ROCKET "\xee\xae\xa5" // U+eba5 #define ICON_MD_ROCKET_LAUNCH "\xee\xae\x9b" // U+eb9b #define ICON_MD_ROLLER_SHADES "\xee\xb0\x92" // U+ec12 #define ICON_MD_ROLLER_SHADES_CLOSED "\xee\xb0\x91" // U+ec11 #define ICON_MD_ROLLER_SKATING "\xee\xaf\x8d" // U+ebcd #define ICON_MD_ROOFING "\xef\x88\x81" // U+f201 #define ICON_MD_ROOM "\xee\xa2\xb4" // U+e8b4 #define ICON_MD_ROOM_PREFERENCES "\xef\x86\xb8" // U+f1b8 #define ICON_MD_ROOM_SERVICE "\xee\xad\x89" // U+eb49 #define ICON_MD_ROTATE_90_DEGREES_CCW "\xee\x90\x98" // U+e418 #define ICON_MD_ROTATE_90_DEGREES_CW "\xee\xaa\xab" // U+eaab #define ICON_MD_ROTATE_LEFT "\xee\x90\x99" // U+e419 #define ICON_MD_ROTATE_RIGHT "\xee\x90\x9a" // U+e41a #define ICON_MD_ROUNDABOUT_LEFT "\xee\xae\x99" // U+eb99 #define ICON_MD_ROUNDABOUT_RIGHT "\xee\xae\xa3" // U+eba3 #define ICON_MD_ROUNDED_CORNER "\xee\xa4\xa0" // U+e920 #define ICON_MD_ROUTE "\xee\xab\x8d" // U+eacd #define ICON_MD_ROUTER "\xee\x8c\xa8" // U+e328 #define ICON_MD_ROWING "\xee\xa4\xa1" // U+e921 #define ICON_MD_RSS_FEED "\xee\x83\xa5" // U+e0e5 #define ICON_MD_RSVP "\xef\x81\x95" // U+f055 #define ICON_MD_RTT "\xee\xa6\xad" // U+e9ad #define ICON_MD_RULE "\xef\x87\x82" // U+f1c2 #define ICON_MD_RULE_FOLDER "\xef\x87\x89" // U+f1c9 #define ICON_MD_RUN_CIRCLE "\xee\xbd\xaf" // U+ef6f #define ICON_MD_RUNNING_WITH_ERRORS "\xee\x94\x9d" // U+e51d #define ICON_MD_RV_HOOKUP "\xee\x99\x82" // U+e642 #define ICON_MD_SAFETY_CHECK "\xee\xaf\xaf" // U+ebef #define ICON_MD_SAFETY_DIVIDER "\xee\x87\x8c" // U+e1cc #define ICON_MD_SAILING "\xee\x94\x82" // U+e502 #define ICON_MD_SANITIZER "\xef\x88\x9d" // U+f21d #define ICON_MD_SATELLITE "\xee\x95\xa2" // U+e562 #define ICON_MD_SATELLITE_ALT "\xee\xac\xba" // U+eb3a #define ICON_MD_SAVE "\xee\x85\xa1" // U+e161 #define ICON_MD_SAVE_ALT "\xee\x85\xb1" // U+e171 #define ICON_MD_SAVE_AS "\xee\xad\xa0" // U+eb60 #define ICON_MD_SAVED_SEARCH "\xee\xa8\x91" // U+ea11 #define ICON_MD_SAVINGS "\xee\x8b\xab" // U+e2eb #define ICON_MD_SCALE "\xee\xad\x9f" // U+eb5f #define ICON_MD_SCANNER "\xee\x8c\xa9" // U+e329 #define ICON_MD_SCATTER_PLOT "\xee\x89\xa8" // U+e268 #define ICON_MD_SCHEDULE "\xee\xa2\xb5" // U+e8b5 #define ICON_MD_SCHEDULE_SEND "\xee\xa8\x8a" // U+ea0a #define ICON_MD_SCHEMA "\xee\x93\xbd" // U+e4fd #define ICON_MD_SCHOOL "\xee\xa0\x8c" // U+e80c #define ICON_MD_SCIENCE "\xee\xa9\x8b" // U+ea4b #define ICON_MD_SCORE "\xee\x89\xa9" // U+e269 #define ICON_MD_SCOREBOARD "\xee\xaf\x90" // U+ebd0 #define ICON_MD_SCREEN_LOCK_LANDSCAPE "\xee\x86\xbe" // U+e1be #define ICON_MD_SCREEN_LOCK_PORTRAIT "\xee\x86\xbf" // U+e1bf #define ICON_MD_SCREEN_LOCK_ROTATION "\xee\x87\x80" // U+e1c0 #define ICON_MD_SCREEN_ROTATION "\xee\x87\x81" // U+e1c1 #define ICON_MD_SCREEN_ROTATION_ALT "\xee\xaf\xae" // U+ebee #define ICON_MD_SCREEN_SEARCH_DESKTOP "\xee\xbd\xb0" // U+ef70 #define ICON_MD_SCREEN_SHARE "\xee\x83\xa2" // U+e0e2 #define ICON_MD_SCREENSHOT "\xef\x81\x96" // U+f056 #define ICON_MD_SCREENSHOT_MONITOR "\xee\xb0\x88" // U+ec08 #define ICON_MD_SCUBA_DIVING "\xee\xaf\x8e" // U+ebce #define ICON_MD_SD "\xee\xa7\x9d" // U+e9dd #define ICON_MD_SD_CARD "\xee\x98\xa3" // U+e623 #define ICON_MD_SD_CARD_ALERT "\xef\x81\x97" // U+f057 #define ICON_MD_SD_STORAGE "\xee\x87\x82" // U+e1c2 #define ICON_MD_SEARCH "\xee\xa2\xb6" // U+e8b6 #define ICON_MD_SEARCH_OFF "\xee\xa9\xb6" // U+ea76 #define ICON_MD_SECURITY "\xee\x8c\xaa" // U+e32a #define ICON_MD_SECURITY_UPDATE "\xef\x81\x98" // U+f058 #define ICON_MD_SECURITY_UPDATE_GOOD "\xef\x81\x99" // U+f059 #define ICON_MD_SECURITY_UPDATE_WARNING "\xef\x81\x9a" // U+f05a #define ICON_MD_SEGMENT "\xee\xa5\x8b" // U+e94b #define ICON_MD_SELECT_ALL "\xee\x85\xa2" // U+e162 #define ICON_MD_SELF_IMPROVEMENT "\xee\xa9\xb8" // U+ea78 #define ICON_MD_SELL "\xef\x81\x9b" // U+f05b #define ICON_MD_SEND "\xee\x85\xa3" // U+e163 #define ICON_MD_SEND_AND_ARCHIVE "\xee\xa8\x8c" // U+ea0c #define ICON_MD_SEND_TIME_EXTENSION "\xee\xab\x9b" // U+eadb #define ICON_MD_SEND_TO_MOBILE "\xef\x81\x9c" // U+f05c #define ICON_MD_SENSOR_DOOR "\xef\x86\xb5" // U+f1b5 #define ICON_MD_SENSOR_OCCUPIED "\xee\xb0\x90" // U+ec10 #define ICON_MD_SENSOR_WINDOW "\xef\x86\xb4" // U+f1b4 #define ICON_MD_SENSORS "\xee\x94\x9e" // U+e51e #define ICON_MD_SENSORS_OFF "\xee\x94\x9f" // U+e51f #define ICON_MD_SENTIMENT_DISSATISFIED "\xee\xa0\x91" // U+e811 #define ICON_MD_SENTIMENT_NEUTRAL "\xee\xa0\x92" // U+e812 #define ICON_MD_SENTIMENT_SATISFIED "\xee\xa0\x93" // U+e813 #define ICON_MD_SENTIMENT_SATISFIED_ALT "\xee\x83\xad" // U+e0ed #define ICON_MD_SENTIMENT_VERY_DISSATISFIED "\xee\xa0\x94" // U+e814 #define ICON_MD_SENTIMENT_VERY_SATISFIED "\xee\xa0\x95" // U+e815 #define ICON_MD_SET_MEAL "\xef\x87\xaa" // U+f1ea #define ICON_MD_SETTINGS "\xee\xa2\xb8" // U+e8b8 #define ICON_MD_SETTINGS_ACCESSIBILITY "\xef\x81\x9d" // U+f05d #define ICON_MD_SETTINGS_APPLICATIONS "\xee\xa2\xb9" // U+e8b9 #define ICON_MD_SETTINGS_BACKUP_RESTORE "\xee\xa2\xba" // U+e8ba #define ICON_MD_SETTINGS_BLUETOOTH "\xee\xa2\xbb" // U+e8bb #define ICON_MD_SETTINGS_BRIGHTNESS "\xee\xa2\xbd" // U+e8bd #define ICON_MD_SETTINGS_CELL "\xee\xa2\xbc" // U+e8bc #define ICON_MD_SETTINGS_DISPLAY "\xee\xa2\xbd" // U+e8bd #define ICON_MD_SETTINGS_ETHERNET "\xee\xa2\xbe" // U+e8be #define ICON_MD_SETTINGS_INPUT_ANTENNA "\xee\xa2\xbf" // U+e8bf #define ICON_MD_SETTINGS_INPUT_COMPONENT "\xee\xa3\x80" // U+e8c0 #define ICON_MD_SETTINGS_INPUT_COMPOSITE "\xee\xa3\x81" // U+e8c1 #define ICON_MD_SETTINGS_INPUT_HDMI "\xee\xa3\x82" // U+e8c2 #define ICON_MD_SETTINGS_INPUT_SVIDEO "\xee\xa3\x83" // U+e8c3 #define ICON_MD_SETTINGS_OVERSCAN "\xee\xa3\x84" // U+e8c4 #define ICON_MD_SETTINGS_PHONE "\xee\xa3\x85" // U+e8c5 #define ICON_MD_SETTINGS_POWER "\xee\xa3\x86" // U+e8c6 #define ICON_MD_SETTINGS_REMOTE "\xee\xa3\x87" // U+e8c7 #define ICON_MD_SETTINGS_SUGGEST "\xef\x81\x9e" // U+f05e #define ICON_MD_SETTINGS_SYSTEM_DAYDREAM "\xee\x87\x83" // U+e1c3 #define ICON_MD_SETTINGS_VOICE "\xee\xa3\x88" // U+e8c8 #define ICON_MD_SEVERE_COLD "\xee\xaf\x93" // U+ebd3 #define ICON_MD_SHAPE_LINE "\xef\xa3\x93" // U+f8d3 #define ICON_MD_SHARE "\xee\xa0\x8d" // U+e80d #define ICON_MD_SHARE_ARRIVAL_TIME "\xee\x94\xa4" // U+e524 #define ICON_MD_SHARE_LOCATION "\xef\x81\x9f" // U+f05f #define ICON_MD_SHIELD "\xee\xa7\xa0" // U+e9e0 #define ICON_MD_SHIELD_MOON "\xee\xaa\xa9" // U+eaa9 #define ICON_MD_SHOP "\xee\xa3\x89" // U+e8c9 #define ICON_MD_SHOP_2 "\xee\x86\x9e" // U+e19e #define ICON_MD_SHOP_TWO "\xee\xa3\x8a" // U+e8ca #define ICON_MD_SHOPIFY "\xee\xaa\x9d" // U+ea9d #define ICON_MD_SHOPPING_BAG "\xef\x87\x8c" // U+f1cc #define ICON_MD_SHOPPING_BASKET "\xee\xa3\x8b" // U+e8cb #define ICON_MD_SHOPPING_CART "\xee\xa3\x8c" // U+e8cc #define ICON_MD_SHOPPING_CART_CHECKOUT "\xee\xae\x88" // U+eb88 #define ICON_MD_SHORT_TEXT "\xee\x89\xa1" // U+e261 #define ICON_MD_SHORTCUT "\xef\x81\xa0" // U+f060 #define ICON_MD_SHOW_CHART "\xee\x9b\xa1" // U+e6e1 #define ICON_MD_SHOWER "\xef\x81\xa1" // U+f061 #define ICON_MD_SHUFFLE "\xee\x81\x83" // U+e043 #define ICON_MD_SHUFFLE_ON "\xee\xa7\xa1" // U+e9e1 #define ICON_MD_SHUTTER_SPEED "\xee\x90\xbd" // U+e43d #define ICON_MD_SICK "\xef\x88\xa0" // U+f220 #define ICON_MD_SIGN_LANGUAGE "\xee\xaf\xa5" // U+ebe5 #define ICON_MD_SIGNAL_CELLULAR_0_BAR "\xef\x82\xa8" // U+f0a8 #define ICON_MD_SIGNAL_CELLULAR_4_BAR "\xee\x87\x88" // U+e1c8 #define ICON_MD_SIGNAL_CELLULAR_ALT "\xee\x88\x82" // U+e202 #define ICON_MD_SIGNAL_CELLULAR_ALT_1_BAR "\xee\xaf\x9f" // U+ebdf #define ICON_MD_SIGNAL_CELLULAR_ALT_2_BAR "\xee\xaf\xa3" // U+ebe3 #define ICON_MD_SIGNAL_CELLULAR_CONNECTED_NO_INTERNET_0_BAR "\xef\x82\xac" // U+f0ac #define ICON_MD_SIGNAL_CELLULAR_CONNECTED_NO_INTERNET_4_BAR "\xee\x87\x8d" // U+e1cd #define ICON_MD_SIGNAL_CELLULAR_NO_SIM "\xee\x87\x8e" // U+e1ce #define ICON_MD_SIGNAL_CELLULAR_NODATA "\xef\x81\xa2" // U+f062 #define ICON_MD_SIGNAL_CELLULAR_NULL "\xee\x87\x8f" // U+e1cf #define ICON_MD_SIGNAL_CELLULAR_OFF "\xee\x87\x90" // U+e1d0 #define ICON_MD_SIGNAL_WIFI_0_BAR "\xef\x82\xb0" // U+f0b0 #define ICON_MD_SIGNAL_WIFI_4_BAR "\xee\x87\x98" // U+e1d8 #define ICON_MD_SIGNAL_WIFI_4_BAR_LOCK "\xee\x87\x99" // U+e1d9 #define ICON_MD_SIGNAL_WIFI_BAD "\xef\x81\xa3" // U+f063 #define ICON_MD_SIGNAL_WIFI_CONNECTED_NO_INTERNET_4 "\xef\x81\xa4" // U+f064 #define ICON_MD_SIGNAL_WIFI_OFF "\xee\x87\x9a" // U+e1da #define ICON_MD_SIGNAL_WIFI_STATUSBAR_4_BAR "\xef\x81\xa5" // U+f065 #define ICON_MD_SIGNAL_WIFI_STATUSBAR_CONNECTED_NO_INTERNET_4 "\xef\x81\xa6" // U+f066 #define ICON_MD_SIGNAL_WIFI_STATUSBAR_NULL "\xef\x81\xa7" // U+f067 #define ICON_MD_SIGNPOST "\xee\xae\x91" // U+eb91 #define ICON_MD_SIM_CARD "\xee\x8c\xab" // U+e32b #define ICON_MD_SIM_CARD_ALERT "\xee\x98\xa4" // U+e624 #define ICON_MD_SIM_CARD_DOWNLOAD "\xef\x81\xa8" // U+f068 #define ICON_MD_SINGLE_BED "\xee\xa9\x88" // U+ea48 #define ICON_MD_SIP "\xef\x81\xa9" // U+f069 #define ICON_MD_SKATEBOARDING "\xee\x94\x91" // U+e511 #define ICON_MD_SKIP_NEXT "\xee\x81\x84" // U+e044 #define ICON_MD_SKIP_PREVIOUS "\xee\x81\x85" // U+e045 #define ICON_MD_SLEDDING "\xee\x94\x92" // U+e512 #define ICON_MD_SLIDESHOW "\xee\x90\x9b" // U+e41b #define ICON_MD_SLOW_MOTION_VIDEO "\xee\x81\xa8" // U+e068 #define ICON_MD_SMART_BUTTON "\xef\x87\x81" // U+f1c1 #define ICON_MD_SMART_DISPLAY "\xef\x81\xaa" // U+f06a #define ICON_MD_SMART_SCREEN "\xef\x81\xab" // U+f06b #define ICON_MD_SMART_TOY "\xef\x81\xac" // U+f06c #define ICON_MD_SMARTPHONE "\xee\x8c\xac" // U+e32c #define ICON_MD_SMOKE_FREE "\xee\xad\x8a" // U+eb4a #define ICON_MD_SMOKING_ROOMS "\xee\xad\x8b" // U+eb4b #define ICON_MD_SMS "\xee\x98\xa5" // U+e625 #define ICON_MD_SMS_FAILED "\xee\x98\xa6" // U+e626 #define ICON_MD_SNAPCHAT "\xee\xa9\xae" // U+ea6e #define ICON_MD_SNIPPET_FOLDER "\xef\x87\x87" // U+f1c7 #define ICON_MD_SNOOZE "\xee\x81\x86" // U+e046 #define ICON_MD_SNOWBOARDING "\xee\x94\x93" // U+e513 #define ICON_MD_SNOWING "\xee\xa0\x8f" // U+e80f #define ICON_MD_SNOWMOBILE "\xee\x94\x83" // U+e503 #define ICON_MD_SNOWSHOEING "\xee\x94\x94" // U+e514 #define ICON_MD_SOAP "\xef\x86\xb2" // U+f1b2 #define ICON_MD_SOCIAL_DISTANCE "\xee\x87\x8b" // U+e1cb #define ICON_MD_SOLAR_POWER "\xee\xb0\x8f" // U+ec0f #define ICON_MD_SORT "\xee\x85\xa4" // U+e164 #define ICON_MD_SORT_BY_ALPHA "\xee\x81\x93" // U+e053 #define ICON_MD_SOS "\xee\xaf\xb7" // U+ebf7 #define ICON_MD_SOUP_KITCHEN "\xee\x9f\x93" // U+e7d3 #define ICON_MD_SOURCE "\xef\x87\x84" // U+f1c4 #define ICON_MD_SOUTH "\xef\x87\xa3" // U+f1e3 #define ICON_MD_SOUTH_AMERICA "\xee\x9f\xa4" // U+e7e4 #define ICON_MD_SOUTH_EAST "\xef\x87\xa4" // U+f1e4 #define ICON_MD_SOUTH_WEST "\xef\x87\xa5" // U+f1e5 #define ICON_MD_SPA "\xee\xad\x8c" // U+eb4c #define ICON_MD_SPACE_BAR "\xee\x89\x96" // U+e256 #define ICON_MD_SPACE_DASHBOARD "\xee\x99\xab" // U+e66b #define ICON_MD_SPATIAL_AUDIO "\xee\xaf\xab" // U+ebeb #define ICON_MD_SPATIAL_AUDIO_OFF "\xee\xaf\xa8" // U+ebe8 #define ICON_MD_SPATIAL_TRACKING "\xee\xaf\xaa" // U+ebea #define ICON_MD_SPEAKER "\xee\x8c\xad" // U+e32d #define ICON_MD_SPEAKER_GROUP "\xee\x8c\xae" // U+e32e #define ICON_MD_SPEAKER_NOTES "\xee\xa3\x8d" // U+e8cd #define ICON_MD_SPEAKER_NOTES_OFF "\xee\xa4\xaa" // U+e92a #define ICON_MD_SPEAKER_PHONE "\xee\x83\x92" // U+e0d2 #define ICON_MD_SPEED "\xee\xa7\xa4" // U+e9e4 #define ICON_MD_SPELLCHECK "\xee\xa3\x8e" // U+e8ce #define ICON_MD_SPLITSCREEN "\xef\x81\xad" // U+f06d #define ICON_MD_SPOKE "\xee\xa6\xa7" // U+e9a7 #define ICON_MD_SPORTS "\xee\xa8\xb0" // U+ea30 #define ICON_MD_SPORTS_BAR "\xef\x87\xb3" // U+f1f3 #define ICON_MD_SPORTS_BASEBALL "\xee\xa9\x91" // U+ea51 #define ICON_MD_SPORTS_BASKETBALL "\xee\xa8\xa6" // U+ea26 #define ICON_MD_SPORTS_CRICKET "\xee\xa8\xa7" // U+ea27 #define ICON_MD_SPORTS_ESPORTS "\xee\xa8\xa8" // U+ea28 #define ICON_MD_SPORTS_FOOTBALL "\xee\xa8\xa9" // U+ea29 #define ICON_MD_SPORTS_GOLF "\xee\xa8\xaa" // U+ea2a #define ICON_MD_SPORTS_GYMNASTICS "\xee\xaf\x84" // U+ebc4 #define ICON_MD_SPORTS_HANDBALL "\xee\xa8\xb3" // U+ea33 #define ICON_MD_SPORTS_HOCKEY "\xee\xa8\xab" // U+ea2b #define ICON_MD_SPORTS_KABADDI "\xee\xa8\xb4" // U+ea34 #define ICON_MD_SPORTS_MARTIAL_ARTS "\xee\xab\xa9" // U+eae9 #define ICON_MD_SPORTS_MMA "\xee\xa8\xac" // U+ea2c #define ICON_MD_SPORTS_MOTORSPORTS "\xee\xa8\xad" // U+ea2d #define ICON_MD_SPORTS_RUGBY "\xee\xa8\xae" // U+ea2e #define ICON_MD_SPORTS_SCORE "\xef\x81\xae" // U+f06e #define ICON_MD_SPORTS_SOCCER "\xee\xa8\xaf" // U+ea2f #define ICON_MD_SPORTS_TENNIS "\xee\xa8\xb2" // U+ea32 #define ICON_MD_SPORTS_VOLLEYBALL "\xee\xa8\xb1" // U+ea31 #define ICON_MD_SQUARE "\xee\xac\xb6" // U+eb36 #define ICON_MD_SQUARE_FOOT "\xee\xa9\x89" // U+ea49 #define ICON_MD_SSID_CHART "\xee\xad\xa6" // U+eb66 #define ICON_MD_STACKED_BAR_CHART "\xee\xa7\xa6" // U+e9e6 #define ICON_MD_STACKED_LINE_CHART "\xef\x88\xab" // U+f22b #define ICON_MD_STADIUM "\xee\xae\x90" // U+eb90 #define ICON_MD_STAIRS "\xef\x86\xa9" // U+f1a9 #define ICON_MD_STAR "\xee\xa0\xb8" // U+e838 #define ICON_MD_STAR_BORDER "\xee\xa0\xba" // U+e83a #define ICON_MD_STAR_BORDER_PURPLE500 "\xef\x82\x99" // U+f099 #define ICON_MD_STAR_HALF "\xee\xa0\xb9" // U+e839 #define ICON_MD_STAR_OUTLINE "\xef\x81\xaf" // U+f06f #define ICON_MD_STAR_PURPLE500 "\xef\x82\x9a" // U+f09a #define ICON_MD_STAR_RATE "\xef\x83\xac" // U+f0ec #define ICON_MD_STARS "\xee\xa3\x90" // U+e8d0 #define ICON_MD_START "\xee\x82\x89" // U+e089 #define ICON_MD_STAY_CURRENT_LANDSCAPE "\xee\x83\x93" // U+e0d3 #define ICON_MD_STAY_CURRENT_PORTRAIT "\xee\x83\x94" // U+e0d4 #define ICON_MD_STAY_PRIMARY_LANDSCAPE "\xee\x83\x95" // U+e0d5 #define ICON_MD_STAY_PRIMARY_PORTRAIT "\xee\x83\x96" // U+e0d6 #define ICON_MD_STICKY_NOTE_2 "\xef\x87\xbc" // U+f1fc #define ICON_MD_STOP "\xee\x81\x87" // U+e047 #define ICON_MD_STOP_CIRCLE "\xee\xbd\xb1" // U+ef71 #define ICON_MD_STOP_SCREEN_SHARE "\xee\x83\xa3" // U+e0e3 #define ICON_MD_STORAGE "\xee\x87\x9b" // U+e1db #define ICON_MD_STORE "\xee\xa3\x91" // U+e8d1 #define ICON_MD_STORE_MALL_DIRECTORY "\xee\x95\xa3" // U+e563 #define ICON_MD_STOREFRONT "\xee\xa8\x92" // U+ea12 #define ICON_MD_STORM "\xef\x81\xb0" // U+f070 #define ICON_MD_STRAIGHT "\xee\xae\x95" // U+eb95 #define ICON_MD_STRAIGHTEN "\xee\x90\x9c" // U+e41c #define ICON_MD_STREAM "\xee\xa7\xa9" // U+e9e9 #define ICON_MD_STREETVIEW "\xee\x95\xae" // U+e56e #define ICON_MD_STRIKETHROUGH_S "\xee\x89\x97" // U+e257 #define ICON_MD_STROLLER "\xef\x86\xae" // U+f1ae #define ICON_MD_STYLE "\xee\x90\x9d" // U+e41d #define ICON_MD_SUBDIRECTORY_ARROW_LEFT "\xee\x97\x99" // U+e5d9 #define ICON_MD_SUBDIRECTORY_ARROW_RIGHT "\xee\x97\x9a" // U+e5da #define ICON_MD_SUBJECT "\xee\xa3\x92" // U+e8d2 #define ICON_MD_SUBSCRIPT "\xef\x84\x91" // U+f111 #define ICON_MD_SUBSCRIPTIONS "\xee\x81\xa4" // U+e064 #define ICON_MD_SUBTITLES "\xee\x81\x88" // U+e048 #define ICON_MD_SUBTITLES_OFF "\xee\xbd\xb2" // U+ef72 #define ICON_MD_SUBWAY "\xee\x95\xaf" // U+e56f #define ICON_MD_SUMMARIZE "\xef\x81\xb1" // U+f071 #define ICON_MD_SUNNY "\xee\xa0\x9a" // U+e81a #define ICON_MD_SUNNY_SNOWING "\xee\xa0\x99" // U+e819 #define ICON_MD_SUPERSCRIPT "\xef\x84\x92" // U+f112 #define ICON_MD_SUPERVISED_USER_CIRCLE "\xee\xa4\xb9" // U+e939 #define ICON_MD_SUPERVISOR_ACCOUNT "\xee\xa3\x93" // U+e8d3 #define ICON_MD_SUPPORT "\xee\xbd\xb3" // U+ef73 #define ICON_MD_SUPPORT_AGENT "\xef\x83\xa2" // U+f0e2 #define ICON_MD_SURFING "\xee\x94\x95" // U+e515 #define ICON_MD_SURROUND_SOUND "\xee\x81\x89" // U+e049 #define ICON_MD_SWAP_CALLS "\xee\x83\x97" // U+e0d7 #define ICON_MD_SWAP_HORIZ "\xee\xa3\x94" // U+e8d4 #define ICON_MD_SWAP_HORIZONTAL_CIRCLE "\xee\xa4\xb3" // U+e933 #define ICON_MD_SWAP_VERT "\xee\xa3\x95" // U+e8d5 #define ICON_MD_SWAP_VERT_CIRCLE "\xee\xa3\x96" // U+e8d6 #define ICON_MD_SWAP_VERTICAL_CIRCLE "\xee\xa3\x96" // U+e8d6 #define ICON_MD_SWIPE "\xee\xa7\xac" // U+e9ec #define ICON_MD_SWIPE_DOWN "\xee\xad\x93" // U+eb53 #define ICON_MD_SWIPE_DOWN_ALT "\xee\xac\xb0" // U+eb30 #define ICON_MD_SWIPE_LEFT "\xee\xad\x99" // U+eb59 #define ICON_MD_SWIPE_LEFT_ALT "\xee\xac\xb3" // U+eb33 #define ICON_MD_SWIPE_RIGHT "\xee\xad\x92" // U+eb52 #define ICON_MD_SWIPE_RIGHT_ALT "\xee\xad\x96" // U+eb56 #define ICON_MD_SWIPE_UP "\xee\xac\xae" // U+eb2e #define ICON_MD_SWIPE_UP_ALT "\xee\xac\xb5" // U+eb35 #define ICON_MD_SWIPE_VERTICAL "\xee\xad\x91" // U+eb51 #define ICON_MD_SWITCH_ACCESS_SHORTCUT "\xee\x9f\xa1" // U+e7e1 #define ICON_MD_SWITCH_ACCESS_SHORTCUT_ADD "\xee\x9f\xa2" // U+e7e2 #define ICON_MD_SWITCH_ACCOUNT "\xee\xa7\xad" // U+e9ed #define ICON_MD_SWITCH_CAMERA "\xee\x90\x9e" // U+e41e #define ICON_MD_SWITCH_LEFT "\xef\x87\x91" // U+f1d1 #define ICON_MD_SWITCH_RIGHT "\xef\x87\x92" // U+f1d2 #define ICON_MD_SWITCH_VIDEO "\xee\x90\x9f" // U+e41f #define ICON_MD_SYNAGOGUE "\xee\xaa\xb0" // U+eab0 #define ICON_MD_SYNC "\xee\x98\xa7" // U+e627 #define ICON_MD_SYNC_ALT "\xee\xa8\x98" // U+ea18 #define ICON_MD_SYNC_DISABLED "\xee\x98\xa8" // U+e628 #define ICON_MD_SYNC_LOCK "\xee\xab\xae" // U+eaee #define ICON_MD_SYNC_PROBLEM "\xee\x98\xa9" // U+e629 #define ICON_MD_SYSTEM_SECURITY_UPDATE "\xef\x81\xb2" // U+f072 #define ICON_MD_SYSTEM_SECURITY_UPDATE_GOOD "\xef\x81\xb3" // U+f073 #define ICON_MD_SYSTEM_SECURITY_UPDATE_WARNING "\xef\x81\xb4" // U+f074 #define ICON_MD_SYSTEM_UPDATE "\xee\x98\xaa" // U+e62a #define ICON_MD_SYSTEM_UPDATE_ALT "\xee\xa3\x97" // U+e8d7 #define ICON_MD_SYSTEM_UPDATE_TV "\xee\xa3\x97" // U+e8d7 #define ICON_MD_TAB "\xee\xa3\x98" // U+e8d8 #define ICON_MD_TAB_UNSELECTED "\xee\xa3\x99" // U+e8d9 #define ICON_MD_TABLE_BAR "\xee\xab\x92" // U+ead2 #define ICON_MD_TABLE_CHART "\xee\x89\xa5" // U+e265 #define ICON_MD_TABLE_RESTAURANT "\xee\xab\x86" // U+eac6 #define ICON_MD_TABLE_ROWS "\xef\x84\x81" // U+f101 #define ICON_MD_TABLE_VIEW "\xef\x86\xbe" // U+f1be #define ICON_MD_TABLET "\xee\x8c\xaf" // U+e32f #define ICON_MD_TABLET_ANDROID "\xee\x8c\xb0" // U+e330 #define ICON_MD_TABLET_MAC "\xee\x8c\xb1" // U+e331 #define ICON_MD_TAG "\xee\xa7\xaf" // U+e9ef #define ICON_MD_TAG_FACES "\xee\x90\xa0" // U+e420 #define ICON_MD_TAKEOUT_DINING "\xee\xa9\xb4" // U+ea74 #define ICON_MD_TAP_AND_PLAY "\xee\x98\xab" // U+e62b #define ICON_MD_TAPAS "\xef\x87\xa9" // U+f1e9 #define ICON_MD_TASK "\xef\x81\xb5" // U+f075 #define ICON_MD_TASK_ALT "\xee\x8b\xa6" // U+e2e6 #define ICON_MD_TAXI_ALERT "\xee\xbd\xb4" // U+ef74 #define ICON_MD_TELEGRAM "\xee\xa9\xab" // U+ea6b #define ICON_MD_TEMPLE_BUDDHIST "\xee\xaa\xb3" // U+eab3 #define ICON_MD_TEMPLE_HINDU "\xee\xaa\xaf" // U+eaaf #define ICON_MD_TERMINAL "\xee\xae\x8e" // U+eb8e #define ICON_MD_TERRAIN "\xee\x95\xa4" // U+e564 #define ICON_MD_TEXT_DECREASE "\xee\xab\x9d" // U+eadd #define ICON_MD_TEXT_FIELDS "\xee\x89\xa2" // U+e262 #define ICON_MD_TEXT_FORMAT "\xee\x85\xa5" // U+e165 #define ICON_MD_TEXT_INCREASE "\xee\xab\xa2" // U+eae2 #define ICON_MD_TEXT_ROTATE_UP "\xee\xa4\xba" // U+e93a #define ICON_MD_TEXT_ROTATE_VERTICAL "\xee\xa4\xbb" // U+e93b #define ICON_MD_TEXT_ROTATION_ANGLEDOWN "\xee\xa4\xbc" // U+e93c #define ICON_MD_TEXT_ROTATION_ANGLEUP "\xee\xa4\xbd" // U+e93d #define ICON_MD_TEXT_ROTATION_DOWN "\xee\xa4\xbe" // U+e93e #define ICON_MD_TEXT_ROTATION_NONE "\xee\xa4\xbf" // U+e93f #define ICON_MD_TEXT_SNIPPET "\xef\x87\x86" // U+f1c6 #define ICON_MD_TEXTSMS "\xee\x83\x98" // U+e0d8 #define ICON_MD_TEXTURE "\xee\x90\xa1" // U+e421 #define ICON_MD_THEATER_COMEDY "\xee\xa9\xa6" // U+ea66 #define ICON_MD_THEATERS "\xee\xa3\x9a" // U+e8da #define ICON_MD_THERMOSTAT "\xef\x81\xb6" // U+f076 #define ICON_MD_THERMOSTAT_AUTO "\xef\x81\xb7" // U+f077 #define ICON_MD_THUMB_DOWN "\xee\xa3\x9b" // U+e8db #define ICON_MD_THUMB_DOWN_ALT "\xee\xa0\x96" // U+e816 #define ICON_MD_THUMB_DOWN_OFF_ALT "\xee\xa7\xb2" // U+e9f2 #define ICON_MD_THUMB_UP "\xee\xa3\x9c" // U+e8dc #define ICON_MD_THUMB_UP_ALT "\xee\xa0\x97" // U+e817 #define ICON_MD_THUMB_UP_OFF_ALT "\xee\xa7\xb3" // U+e9f3 #define ICON_MD_THUMBS_UP_DOWN "\xee\xa3\x9d" // U+e8dd #define ICON_MD_THUNDERSTORM "\xee\xaf\x9b" // U+ebdb #define ICON_MD_TIKTOK "\xee\xa9\xbe" // U+ea7e #define ICON_MD_TIME_TO_LEAVE "\xee\x98\xac" // U+e62c #define ICON_MD_TIMELAPSE "\xee\x90\xa2" // U+e422 #define ICON_MD_TIMELINE "\xee\xa4\xa2" // U+e922 #define ICON_MD_TIMER "\xee\x90\xa5" // U+e425 #define ICON_MD_TIMER_10 "\xee\x90\xa3" // U+e423 #define ICON_MD_TIMER_10_SELECT "\xef\x81\xba" // U+f07a #define ICON_MD_TIMER_3 "\xee\x90\xa4" // U+e424 #define ICON_MD_TIMER_3_SELECT "\xef\x81\xbb" // U+f07b #define ICON_MD_TIMER_OFF "\xee\x90\xa6" // U+e426 #define ICON_MD_TIPS_AND_UPDATES "\xee\x9e\x9a" // U+e79a #define ICON_MD_TIRE_REPAIR "\xee\xaf\x88" // U+ebc8 #define ICON_MD_TITLE "\xee\x89\xa4" // U+e264 #define ICON_MD_TOC "\xee\xa3\x9e" // U+e8de #define ICON_MD_TODAY "\xee\xa3\x9f" // U+e8df #define ICON_MD_TOGGLE_OFF "\xee\xa7\xb5" // U+e9f5 #define ICON_MD_TOGGLE_ON "\xee\xa7\xb6" // U+e9f6 #define ICON_MD_TOKEN "\xee\xa8\xa5" // U+ea25 #define ICON_MD_TOLL "\xee\xa3\xa0" // U+e8e0 #define ICON_MD_TONALITY "\xee\x90\xa7" // U+e427 #define ICON_MD_TOPIC "\xef\x87\x88" // U+f1c8 #define ICON_MD_TORNADO "\xee\x86\x99" // U+e199 #define ICON_MD_TOUCH_APP "\xee\xa4\x93" // U+e913 #define ICON_MD_TOUR "\xee\xbd\xb5" // U+ef75 #define ICON_MD_TOYS "\xee\x8c\xb2" // U+e332 #define ICON_MD_TRACK_CHANGES "\xee\xa3\xa1" // U+e8e1 #define ICON_MD_TRAFFIC "\xee\x95\xa5" // U+e565 #define ICON_MD_TRAIN "\xee\x95\xb0" // U+e570 #define ICON_MD_TRAM "\xee\x95\xb1" // U+e571 #define ICON_MD_TRANSCRIBE "\xef\xa3\xac" // U+f8ec #define ICON_MD_TRANSFER_WITHIN_A_STATION "\xee\x95\xb2" // U+e572 #define ICON_MD_TRANSFORM "\xee\x90\xa8" // U+e428 #define ICON_MD_TRANSGENDER "\xee\x96\x8d" // U+e58d #define ICON_MD_TRANSIT_ENTEREXIT "\xee\x95\xb9" // U+e579 #define ICON_MD_TRANSLATE "\xee\xa3\xa2" // U+e8e2 #define ICON_MD_TRAVEL_EXPLORE "\xee\x8b\x9b" // U+e2db #define ICON_MD_TRENDING_DOWN "\xee\xa3\xa3" // U+e8e3 #define ICON_MD_TRENDING_FLAT "\xee\xa3\xa4" // U+e8e4 #define ICON_MD_TRENDING_NEUTRAL "\xee\xa3\xa4" // U+e8e4 #define ICON_MD_TRENDING_UP "\xee\xa3\xa5" // U+e8e5 #define ICON_MD_TRIP_ORIGIN "\xee\x95\xbb" // U+e57b #define ICON_MD_TROUBLESHOOT "\xee\x87\x92" // U+e1d2 #define ICON_MD_TRY "\xef\x81\xbc" // U+f07c #define ICON_MD_TSUNAMI "\xee\xaf\x98" // U+ebd8 #define ICON_MD_TTY "\xef\x86\xaa" // U+f1aa #define ICON_MD_TUNE "\xee\x90\xa9" // U+e429 #define ICON_MD_TUNGSTEN "\xef\x81\xbd" // U+f07d #define ICON_MD_TURN_LEFT "\xee\xae\xa6" // U+eba6 #define ICON_MD_TURN_RIGHT "\xee\xae\xab" // U+ebab #define ICON_MD_TURN_SHARP_LEFT "\xee\xae\xa7" // U+eba7 #define ICON_MD_TURN_SHARP_RIGHT "\xee\xae\xaa" // U+ebaa #define ICON_MD_TURN_SLIGHT_LEFT "\xee\xae\xa4" // U+eba4 #define ICON_MD_TURN_SLIGHT_RIGHT "\xee\xae\x9a" // U+eb9a #define ICON_MD_TURNED_IN "\xee\xa3\xa6" // U+e8e6 #define ICON_MD_TURNED_IN_NOT "\xee\xa3\xa7" // U+e8e7 #define ICON_MD_TV "\xee\x8c\xb3" // U+e333 #define ICON_MD_TV_OFF "\xee\x99\x87" // U+e647 #define ICON_MD_TWO_WHEELER "\xee\xa7\xb9" // U+e9f9 #define ICON_MD_TYPE_SPECIMEN "\xef\xa3\xb0" // U+f8f0 #define ICON_MD_U_TURN_LEFT "\xee\xae\xa1" // U+eba1 #define ICON_MD_U_TURN_RIGHT "\xee\xae\xa2" // U+eba2 #define ICON_MD_UMBRELLA "\xef\x86\xad" // U+f1ad #define ICON_MD_UNARCHIVE "\xee\x85\xa9" // U+e169 #define ICON_MD_UNDO "\xee\x85\xa6" // U+e166 #define ICON_MD_UNFOLD_LESS "\xee\x97\x96" // U+e5d6 #define ICON_MD_UNFOLD_LESS_DOUBLE "\xef\xa3\x8f" // U+f8cf #define ICON_MD_UNFOLD_MORE "\xee\x97\x97" // U+e5d7 #define ICON_MD_UNFOLD_MORE_DOUBLE "\xef\xa3\x90" // U+f8d0 #define ICON_MD_UNPUBLISHED "\xef\x88\xb6" // U+f236 #define ICON_MD_UNSUBSCRIBE "\xee\x83\xab" // U+e0eb #define ICON_MD_UPCOMING "\xef\x81\xbe" // U+f07e #define ICON_MD_UPDATE "\xee\xa4\xa3" // U+e923 #define ICON_MD_UPDATE_DISABLED "\xee\x81\xb5" // U+e075 #define ICON_MD_UPGRADE "\xef\x83\xbb" // U+f0fb #define ICON_MD_UPLOAD "\xef\x82\x9b" // U+f09b #define ICON_MD_UPLOAD_FILE "\xee\xa7\xbc" // U+e9fc #define ICON_MD_USB "\xee\x87\xa0" // U+e1e0 #define ICON_MD_USB_OFF "\xee\x93\xba" // U+e4fa #define ICON_MD_VACCINES "\xee\x84\xb8" // U+e138 #define ICON_MD_VAPE_FREE "\xee\xaf\x86" // U+ebc6 #define ICON_MD_VAPING_ROOMS "\xee\xaf\x8f" // U+ebcf #define ICON_MD_VERIFIED "\xee\xbd\xb6" // U+ef76 #define ICON_MD_VERIFIED_USER "\xee\xa3\xa8" // U+e8e8 #define ICON_MD_VERTICAL_ALIGN_BOTTOM "\xee\x89\x98" // U+e258 #define ICON_MD_VERTICAL_ALIGN_CENTER "\xee\x89\x99" // U+e259 #define ICON_MD_VERTICAL_ALIGN_TOP "\xee\x89\x9a" // U+e25a #define ICON_MD_VERTICAL_DISTRIBUTE "\xee\x81\xb6" // U+e076 #define ICON_MD_VERTICAL_SHADES "\xee\xb0\x8e" // U+ec0e #define ICON_MD_VERTICAL_SHADES_CLOSED "\xee\xb0\x8d" // U+ec0d #define ICON_MD_VERTICAL_SPLIT "\xee\xa5\x89" // U+e949 #define ICON_MD_VIBRATION "\xee\x98\xad" // U+e62d #define ICON_MD_VIDEO_CALL "\xee\x81\xb0" // U+e070 #define ICON_MD_VIDEO_CAMERA_BACK "\xef\x81\xbf" // U+f07f #define ICON_MD_VIDEO_CAMERA_FRONT "\xef\x82\x80" // U+f080 #define ICON_MD_VIDEO_COLLECTION "\xee\x81\x8a" // U+e04a #define ICON_MD_VIDEO_FILE "\xee\xae\x87" // U+eb87 #define ICON_MD_VIDEO_LABEL "\xee\x81\xb1" // U+e071 #define ICON_MD_VIDEO_LIBRARY "\xee\x81\x8a" // U+e04a #define ICON_MD_VIDEO_SETTINGS "\xee\xa9\xb5" // U+ea75 #define ICON_MD_VIDEO_STABLE "\xef\x82\x81" // U+f081 #define ICON_MD_VIDEOCAM "\xee\x81\x8b" // U+e04b #define ICON_MD_VIDEOCAM_OFF "\xee\x81\x8c" // U+e04c #define ICON_MD_VIDEOGAME_ASSET "\xee\x8c\xb8" // U+e338 #define ICON_MD_VIDEOGAME_ASSET_OFF "\xee\x94\x80" // U+e500 #define ICON_MD_VIEW_AGENDA "\xee\xa3\xa9" // U+e8e9 #define ICON_MD_VIEW_ARRAY "\xee\xa3\xaa" // U+e8ea #define ICON_MD_VIEW_CAROUSEL "\xee\xa3\xab" // U+e8eb #define ICON_MD_VIEW_COLUMN "\xee\xa3\xac" // U+e8ec #define ICON_MD_VIEW_COMFORTABLE "\xee\x90\xaa" // U+e42a #define ICON_MD_VIEW_COMFY "\xee\x90\xaa" // U+e42a #define ICON_MD_VIEW_COMFY_ALT "\xee\xad\xb3" // U+eb73 #define ICON_MD_VIEW_COMPACT "\xee\x90\xab" // U+e42b #define ICON_MD_VIEW_COMPACT_ALT "\xee\xad\xb4" // U+eb74 #define ICON_MD_VIEW_COZY "\xee\xad\xb5" // U+eb75 #define ICON_MD_VIEW_DAY "\xee\xa3\xad" // U+e8ed #define ICON_MD_VIEW_HEADLINE "\xee\xa3\xae" // U+e8ee #define ICON_MD_VIEW_IN_AR "\xee\xa7\xbe" // U+e9fe #define ICON_MD_VIEW_KANBAN "\xee\xad\xbf" // U+eb7f #define ICON_MD_VIEW_LIST "\xee\xa3\xaf" // U+e8ef #define ICON_MD_VIEW_MODULE "\xee\xa3\xb0" // U+e8f0 #define ICON_MD_VIEW_QUILT "\xee\xa3\xb1" // U+e8f1 #define ICON_MD_VIEW_SIDEBAR "\xef\x84\x94" // U+f114 #define ICON_MD_VIEW_STREAM "\xee\xa3\xb2" // U+e8f2 #define ICON_MD_VIEW_TIMELINE "\xee\xae\x85" // U+eb85 #define ICON_MD_VIEW_WEEK "\xee\xa3\xb3" // U+e8f3 #define ICON_MD_VIGNETTE "\xee\x90\xb5" // U+e435 #define ICON_MD_VILLA "\xee\x96\x86" // U+e586 #define ICON_MD_VISIBILITY "\xee\xa3\xb4" // U+e8f4 #define ICON_MD_VISIBILITY_OFF "\xee\xa3\xb5" // U+e8f5 #define ICON_MD_VOICE_CHAT "\xee\x98\xae" // U+e62e #define ICON_MD_VOICE_OVER_OFF "\xee\xa5\x8a" // U+e94a #define ICON_MD_VOICEMAIL "\xee\x83\x99" // U+e0d9 #define ICON_MD_VOLCANO "\xee\xaf\x9a" // U+ebda #define ICON_MD_VOLUME_DOWN "\xee\x81\x8d" // U+e04d #define ICON_MD_VOLUME_DOWN_ALT "\xee\x9e\x9c" // U+e79c #define ICON_MD_VOLUME_MUTE "\xee\x81\x8e" // U+e04e #define ICON_MD_VOLUME_OFF "\xee\x81\x8f" // U+e04f #define ICON_MD_VOLUME_UP "\xee\x81\x90" // U+e050 #define ICON_MD_VOLUNTEER_ACTIVISM "\xee\xa9\xb0" // U+ea70 #define ICON_MD_VPN_KEY "\xee\x83\x9a" // U+e0da #define ICON_MD_VPN_KEY_OFF "\xee\xad\xba" // U+eb7a #define ICON_MD_VPN_LOCK "\xee\x98\xaf" // U+e62f #define ICON_MD_VRPANO "\xef\x82\x82" // U+f082 #define ICON_MD_WALLET "\xef\xa3\xbf" // U+f8ff #define ICON_MD_WALLET_GIFTCARD "\xee\xa3\xb6" // U+e8f6 #define ICON_MD_WALLET_MEMBERSHIP "\xee\xa3\xb7" // U+e8f7 #define ICON_MD_WALLET_TRAVEL "\xee\xa3\xb8" // U+e8f8 #define ICON_MD_WALLPAPER "\xee\x86\xbc" // U+e1bc #define ICON_MD_WAREHOUSE "\xee\xae\xb8" // U+ebb8 #define ICON_MD_WARNING "\xee\x80\x82" // U+e002 #define ICON_MD_WARNING_AMBER "\xef\x82\x83" // U+f083 #define ICON_MD_WASH "\xef\x86\xb1" // U+f1b1 #define ICON_MD_WATCH "\xee\x8c\xb4" // U+e334 #define ICON_MD_WATCH_LATER "\xee\xa4\xa4" // U+e924 #define ICON_MD_WATCH_OFF "\xee\xab\xa3" // U+eae3 #define ICON_MD_WATER "\xef\x82\x84" // U+f084 #define ICON_MD_WATER_DAMAGE "\xef\x88\x83" // U+f203 #define ICON_MD_WATER_DROP "\xee\x9e\x98" // U+e798 #define ICON_MD_WATERFALL_CHART "\xee\xa8\x80" // U+ea00 #define ICON_MD_WAVES "\xee\x85\xb6" // U+e176 #define ICON_MD_WAVING_HAND "\xee\x9d\xa6" // U+e766 #define ICON_MD_WB_AUTO "\xee\x90\xac" // U+e42c #define ICON_MD_WB_CLOUDY "\xee\x90\xad" // U+e42d #define ICON_MD_WB_INCANDESCENT "\xee\x90\xae" // U+e42e #define ICON_MD_WB_IRIDESCENT "\xee\x90\xb6" // U+e436 #define ICON_MD_WB_SHADE "\xee\xa8\x81" // U+ea01 #define ICON_MD_WB_SUNNY "\xee\x90\xb0" // U+e430 #define ICON_MD_WB_TWIGHLIGHT "\xee\xa8\x82" // U+ea02 #define ICON_MD_WB_TWILIGHT "\xee\x87\x86" // U+e1c6 #define ICON_MD_WC "\xee\x98\xbd" // U+e63d #define ICON_MD_WEB "\xee\x81\x91" // U+e051 #define ICON_MD_WEB_ASSET "\xee\x81\xa9" // U+e069 #define ICON_MD_WEB_ASSET_OFF "\xee\x93\xb7" // U+e4f7 #define ICON_MD_WEB_STORIES "\xee\x96\x95" // U+e595 #define ICON_MD_WEBHOOK "\xee\xae\x92" // U+eb92 #define ICON_MD_WECHAT "\xee\xaa\x81" // U+ea81 #define ICON_MD_WEEKEND "\xee\x85\xab" // U+e16b #define ICON_MD_WEST "\xef\x87\xa6" // U+f1e6 #define ICON_MD_WHATSAPP "\xee\xaa\x9c" // U+ea9c #define ICON_MD_WHATSHOT "\xee\xa0\x8e" // U+e80e #define ICON_MD_WHEELCHAIR_PICKUP "\xef\x86\xab" // U+f1ab #define ICON_MD_WHERE_TO_VOTE "\xee\x85\xb7" // U+e177 #define ICON_MD_WIDGETS "\xee\x86\xbd" // U+e1bd #define ICON_MD_WIDTH_FULL "\xef\xa3\xb5" // U+f8f5 #define ICON_MD_WIDTH_NORMAL "\xef\xa3\xb6" // U+f8f6 #define ICON_MD_WIDTH_WIDE "\xef\xa3\xb7" // U+f8f7 #define ICON_MD_WIFI "\xee\x98\xbe" // U+e63e #define ICON_MD_WIFI_1_BAR "\xee\x93\x8a" // U+e4ca #define ICON_MD_WIFI_2_BAR "\xee\x93\x99" // U+e4d9 #define ICON_MD_WIFI_CALLING "\xee\xbd\xb7" // U+ef77 #define ICON_MD_WIFI_CALLING_3 "\xef\x82\x85" // U+f085 #define ICON_MD_WIFI_CHANNEL "\xee\xad\xaa" // U+eb6a #define ICON_MD_WIFI_FIND "\xee\xac\xb1" // U+eb31 #define ICON_MD_WIFI_LOCK "\xee\x87\xa1" // U+e1e1 #define ICON_MD_WIFI_OFF "\xee\x99\x88" // U+e648 #define ICON_MD_WIFI_PASSWORD "\xee\xad\xab" // U+eb6b #define ICON_MD_WIFI_PROTECTED_SETUP "\xef\x83\xbc" // U+f0fc #define ICON_MD_WIFI_TETHERING "\xee\x87\xa2" // U+e1e2 #define ICON_MD_WIFI_TETHERING_ERROR "\xee\xab\x99" // U+ead9 #define ICON_MD_WIFI_TETHERING_ERROR_ROUNDED "\xef\x82\x86" // U+f086 #define ICON_MD_WIFI_TETHERING_OFF "\xef\x82\x87" // U+f087 #define ICON_MD_WIND_POWER "\xee\xb0\x8c" // U+ec0c #define ICON_MD_WINDOW "\xef\x82\x88" // U+f088 #define ICON_MD_WINE_BAR "\xef\x87\xa8" // U+f1e8 #define ICON_MD_WOMAN "\xee\x84\xbe" // U+e13e #define ICON_MD_WOMAN_2 "\xef\xa3\xa7" // U+f8e7 #define ICON_MD_WOO_COMMERCE "\xee\xa9\xad" // U+ea6d #define ICON_MD_WORDPRESS "\xee\xaa\x9f" // U+ea9f #define ICON_MD_WORK "\xee\xa3\xb9" // U+e8f9 #define ICON_MD_WORK_HISTORY "\xee\xb0\x89" // U+ec09 #define ICON_MD_WORK_OFF "\xee\xa5\x82" // U+e942 #define ICON_MD_WORK_OUTLINE "\xee\xa5\x83" // U+e943 #define ICON_MD_WORKSPACE_PREMIUM "\xee\x9e\xaf" // U+e7af #define ICON_MD_WORKSPACES "\xee\x86\xa0" // U+e1a0 #define ICON_MD_WORKSPACES_FILLED "\xee\xa8\x8d" // U+ea0d #define ICON_MD_WORKSPACES_OUTLINE "\xee\xa8\x8f" // U+ea0f #define ICON_MD_WRAP_TEXT "\xee\x89\x9b" // U+e25b #define ICON_MD_WRONG_LOCATION "\xee\xbd\xb8" // U+ef78 #define ICON_MD_WYSIWYG "\xef\x87\x83" // U+f1c3 #define ICON_MD_YARD "\xef\x82\x89" // U+f089 #define ICON_MD_YOUTUBE_SEARCHED_FOR "\xee\xa3\xba" // U+e8fa #define ICON_MD_ZOOM_IN "\xee\xa3\xbf" // U+e8ff #define ICON_MD_ZOOM_IN_MAP "\xee\xac\xad" // U+eb2d #define ICON_MD_ZOOM_OUT "\xee\xa4\x80" // U+e900 #define ICON_MD_ZOOM_OUT_MAP "\xee\x95\xab" // U+e56b #endif // ICON_MD_H #line 0 #ifdef V4K_3RD //----------------------------------------------------------------------------- // 3rd party libs #define ARCHIVE_C // archive.c #define COMPRESS_C // compress.c #define ENET_IMPLEMENTATION // enet #define GJK_C // gjk #define _GLFW_IMPLEMENTATION // glfw337 #define GLFW_INCLUDE_NONE // glfw337 #define HTTPS_IMPLEMENTATION // https #define JO_MPEG_COMPONENTS 3 // jo_mpeg #define JSON5_C // json5 #define LUA_IMPL // lua544 #define MINIAUDIO_IMPLEMENTATION // miniaudio #define MA_NO_FLAC // miniaudio #define NK_GLFW_GL3_IMPLEMENTATION // nuklear #define NK_IMPLEMENTATION // nuklear #define NK_INCLUDE_DEFAULT_ALLOCATOR // nuklear #define NK_INCLUDE_DEFAULT_FONT // nuklear #define NK_INCLUDE_FIXED_TYPES // nuklear #define NK_INCLUDE_FONT_BAKING // nuklear #define NK_INCLUDE_STANDARD_IO // nuklear #define NK_INCLUDE_STANDARD_VARARGS // nuklear #define NK_INCLUDE_VERTEX_BUFFER_OUTPUT // nuklear #define NK_MAX_LAYOUT_ROW_TEMPLATE_COLUMNS 64 // nuklear #define NK_KEYSTATE_BASED_INPUT // nuklear #define PL_MPEG_IMPLEMENTATION // pl_mpeg #define STB_IMAGE_IMPLEMENTATION // stbi #define STB_IMAGE_WRITE_IMPLEMENTATION // stbi_write #define STB_SPRINTF_IMPLEMENTATION // stb_sprintf #define STB_SPRINTF_NOUNALIGNED // stb_sprintf #define STS_MIXER_IMPLEMENTATION // sts_mixer #define SIMPLEX_C // simplex #define SWRAP_IMPLEMENTATION // swrap #define SWRAP_STATIC // swrap #define THREAD_IMPLEMENTATION // thread #define TFD_IMPLEMENTATION // tinyfiledialogs #define BQ_PLATFORM_IMPLEMENTATION // websocket #define BQ_WEBSOCKET_IMPLEMENTATION // websocket #define XML_C // xml #ifdef __APPLE__ #define _GLFW_COCOA // glfw osx #elif defined _WIN32 #define _GLFW_WIN32 // glfw win32 #else #define _GLFW_X11 // glfw linux, also _GLFW_OSMESA or _GLFW_WAYLAND #endif #if defined __TINYC__ && defined _WIN32 #define MAPVK_VSC_TO_VK 1 #define MAPVK_VK_TO_VSC 0 #define IPV6_V6ONLY 0 #define _WIN32_WINNT_VISTA 0 #define _WIN32_WINNT_WINXP 0 #define _WIN32_WINNT_WIN7 0 #endif #ifdef __TINYC__ #define STBI_NO_SIMD // no uint128_t (3rd_https.h) char* strtok_s( char* str, const char* delimiters, char** context ); #endif #if defined __clang__ && defined _WIN32 int execv(const char *path, char *const argv[]); #elif (is(tcc) /*|| defined __clang__*/) && defined _WIN32 int execv(const char *path, char *const argv[]); errno_t strerror_s( char *buffer, size_t sizeInBytes, int errnum ); typedef int socklen_t; #if is(tcc) #define restrict const char *inet_ntop(int af, const void *restrict src, char *restrict dst, socklen_t size); int inet_pton(int af, const char *restrict src, void *restrict dst); #endif errno_t fopen_s( FILE** pFile, const char *filename, const char *mode ); #endif //--- #line 1 "3rd_glfw3.h" #ifndef __EMSCRIPTEN__ // forked from https://github.com/SasLuca/glfw-single-header (CC0-1.0 licensed) // Define _GLFW_IMPLEMENTATION to unroll the implementation into a single compilation unit. // Also, before including do define one of these: // _GLFW_COCOA // _GLFW_WIN32 // _GLFW_X11 // _GLFW_WAYLAND // _GLFW_OSMESA #ifdef _MSC_VER #pragma comment(lib, "gdi32") //< @r-lyeh #pragma comment(lib, "user32") //< @r-lyeh #pragma comment(lib, "shell32") //< @r-lyeh #endif /************************************************************************* * GLFW 3.3.7 - www.glfw.org * A library for OpenGL, window and input *------------------------------------------------------------------------ * Copyright (c) 2002-2006 Marcus Geelnard * Copyright (c) 2006-2019 Camilla Löwy * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages * arising from the use of this software. * * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: * * 1. The origin of this software must not be misrepresented; you must not * claim that you wrote the original software. If you use this software * in a product, an acknowledgment in the product documentation would * be appreciated but is not required. * * 2. Altered source versions must be plainly marked as such, and must not * be misrepresented as being the original software. * * 3. This notice may not be removed or altered from any source * distribution. * *************************************************************************/ #ifndef _glfw3_h_ #define _glfw3_h_ #ifdef __cplusplus extern "C" { #endif /************************************************************************* * Doxygen documentation *************************************************************************/ /*! @file glfw3.h * @brief The header of the GLFW 3 API. * * This is the header file of the GLFW 3 API. It defines all its types and * declares all its functions. * * For more information about how to use this file, see @ref build_include. */ /*! @defgroup context Context reference * @brief Functions and types related to OpenGL and OpenGL ES contexts. * * This is the reference documentation for OpenGL and OpenGL ES context related * functions. For more task-oriented information, see the @ref context_guide. */ /*! @defgroup vulkan Vulkan support reference * @brief Functions and types related to Vulkan. * * This is the reference documentation for Vulkan related functions and types. * For more task-oriented information, see the @ref vulkan_guide. */ /*! @defgroup init Initialization, version and error reference * @brief Functions and types related to initialization and error handling. * * This is the reference documentation for initialization and termination of * the library, version management and error handling. For more task-oriented * information, see the @ref intro_guide. */ /*! @defgroup input Input reference * @brief Functions and types related to input handling. * * This is the reference documentation for input related functions and types. * For more task-oriented information, see the @ref input_guide. */ /*! @defgroup monitor Monitor reference * @brief Functions and types related to monitors. * * This is the reference documentation for monitor related functions and types. * For more task-oriented information, see the @ref monitor_guide. */ /*! @defgroup window Window reference * @brief Functions and types related to windows. * * This is the reference documentation for window related functions and types, * including creation, deletion and event polling. For more task-oriented * information, see the @ref window_guide. */ /************************************************************************* * Compiler- and platform-specific preprocessor work *************************************************************************/ /* If we are we on Windows, we want a single define for it. */ #if !defined(_WIN32) && (defined(__WIN32__) || defined(WIN32) || defined(__MINGW32__)) #define _WIN32 #endif /* _WIN32 */ /* Include because most Windows GLU headers need wchar_t and * the macOS OpenGL header blocks the definition of ptrdiff_t by glext.h. * Include it unconditionally to avoid surprising side-effects. */ #include /* Include because it is needed by Vulkan and related functions. * Include it unconditionally to avoid surprising side-effects. */ #include #if defined(GLFW_INCLUDE_VULKAN) #include #endif /* Vulkan header */ /* The Vulkan header may have indirectly included windows.h (because of * VK_USE_PLATFORM_WIN32_KHR) so we offer our replacement symbols after it. */ /* It is customary to use APIENTRY for OpenGL function pointer declarations on * all platforms. Additionally, the Windows OpenGL header needs APIENTRY. */ #if !defined(APIENTRY) #if defined(_WIN32) #define APIENTRY __stdcall #else #define APIENTRY #endif #define GLFW_APIENTRY_DEFINED #endif /* APIENTRY */ /* Some Windows OpenGL headers need this. */ #if !defined(WINGDIAPI) && defined(_WIN32) #define WINGDIAPI __declspec(dllimport) #define GLFW_WINGDIAPI_DEFINED #endif /* WINGDIAPI */ /* Some Windows GLU headers need this. */ #if !defined(CALLBACK) && defined(_WIN32) #define CALLBACK __stdcall #define GLFW_CALLBACK_DEFINED #endif /* CALLBACK */ /* Include the chosen OpenGL or OpenGL ES headers. */ #if defined(GLFW_INCLUDE_ES1) #include #if defined(GLFW_INCLUDE_GLEXT) #include #endif #elif defined(GLFW_INCLUDE_ES2) #include #if defined(GLFW_INCLUDE_GLEXT) #include #endif #elif defined(GLFW_INCLUDE_ES3) #include #if defined(GLFW_INCLUDE_GLEXT) #include #endif #elif defined(GLFW_INCLUDE_ES31) #include #if defined(GLFW_INCLUDE_GLEXT) #include #endif #elif defined(GLFW_INCLUDE_ES32) #include #if defined(GLFW_INCLUDE_GLEXT) #include #endif #elif defined(GLFW_INCLUDE_GLCOREARB) #if defined(__APPLE__) #include #if defined(GLFW_INCLUDE_GLEXT) #include #endif /*GLFW_INCLUDE_GLEXT*/ #else /*__APPLE__*/ #include #if defined(GLFW_INCLUDE_GLEXT) #include #endif #endif /*__APPLE__*/ #elif defined(GLFW_INCLUDE_GLU) #if defined(__APPLE__) #if defined(GLFW_INCLUDE_GLU) #include #endif #else /*__APPLE__*/ #if defined(GLFW_INCLUDE_GLU) #include #endif #endif /*__APPLE__*/ #elif !defined(GLFW_INCLUDE_NONE) && \ !defined(__gl_h_) && \ !defined(__gles1_gl_h_) && \ !defined(__gles2_gl2_h_) && \ !defined(__gles2_gl3_h_) && \ !defined(__gles2_gl31_h_) && \ !defined(__gles2_gl32_h_) && \ !defined(__gl_glcorearb_h_) && \ !defined(__gl2_h_) /*legacy*/ && \ !defined(__gl3_h_) /*legacy*/ && \ !defined(__gl31_h_) /*legacy*/ && \ !defined(__gl32_h_) /*legacy*/ && \ !defined(__glcorearb_h_) /*legacy*/ && \ !defined(__GL_H__) /*non-standard*/ && \ !defined(__gltypes_h_) /*non-standard*/ && \ !defined(__glee_h_) /*non-standard*/ #if defined(__APPLE__) #if !defined(GLFW_INCLUDE_GLEXT) #define GL_GLEXT_LEGACY #endif #include #else /*__APPLE__*/ #include #if defined(GLFW_INCLUDE_GLEXT) #include #endif #endif /*__APPLE__*/ #endif /* OpenGL and OpenGL ES headers */ #if defined(GLFW_DLL) && defined(_GLFW_BUILD_DLL) /* GLFW_DLL must be defined by applications that are linking against the DLL * version of the GLFW library. _GLFW_BUILD_DLL is defined by the GLFW * configuration header when compiling the DLL version of the library. */ #error "You must not have both GLFW_DLL and _GLFW_BUILD_DLL defined" #endif /* GLFWAPI is used to declare public API functions for export * from the DLL / shared library / dynamic library. */ #if defined(_WIN32) && defined(_GLFW_BUILD_DLL) /* We are building GLFW as a Win32 DLL */ #define GLFWAPI __declspec(dllexport) #elif defined(_WIN32) && defined(GLFW_DLL) /* We are calling GLFW as a Win32 DLL */ #define GLFWAPI __declspec(dllimport) #elif defined(__GNUC__) && defined(_GLFW_BUILD_DLL) /* We are building GLFW as a shared / dynamic library */ #define GLFWAPI __attribute__((visibility("default"))) #else /* We are building or calling GLFW as a static library */ #define GLFWAPI #endif /************************************************************************* * GLFW API tokens *************************************************************************/ /*! @name GLFW version macros * @{ */ /*! @brief The major version number of the GLFW header. * * The major version number of the GLFW header. This is incremented when the * API is changed in non-compatible ways. * @ingroup init */ #define GLFW_VERSION_MAJOR 3 /*! @brief The minor version number of the GLFW header. * * The minor version number of the GLFW header. This is incremented when * features are added to the API but it remains backward-compatible. * @ingroup init */ #define GLFW_VERSION_MINOR 3 /*! @brief The revision number of the GLFW header. * * The revision number of the GLFW header. This is incremented when a bug fix * release is made that does not contain any API changes. * @ingroup init */ #define GLFW_VERSION_REVISION 7 /*! @} */ /*! @brief One. * * This is only semantic sugar for the number 1. You can instead use `1` or * `true` or `_True` or `GL_TRUE` or `VK_TRUE` or anything else that is equal * to one. * * @ingroup init */ #define GLFW_TRUE 1 /*! @brief Zero. * * This is only semantic sugar for the number 0. You can instead use `0` or * `false` or `_False` or `GL_FALSE` or `VK_FALSE` or anything else that is * equal to zero. * * @ingroup init */ #define GLFW_FALSE 0 /*! @name Key and button actions * @{ */ /*! @brief The key or mouse button was released. * * The key or mouse button was released. * * @ingroup input */ #define GLFW_RELEASE 0 /*! @brief The key or mouse button was pressed. * * The key or mouse button was pressed. * * @ingroup input */ #define GLFW_PRESS 1 /*! @brief The key was held down until it repeated. * * The key was held down until it repeated. * * @ingroup input */ #define GLFW_REPEAT 2 /*! @} */ /*! @defgroup hat_state Joystick hat states * @brief Joystick hat states. * * See [joystick hat input](@ref joystick_hat) for how these are used. * * @ingroup input * @{ */ #define GLFW_HAT_CENTERED 0 #define GLFW_HAT_UP 1 #define GLFW_HAT_RIGHT 2 #define GLFW_HAT_DOWN 4 #define GLFW_HAT_LEFT 8 #define GLFW_HAT_RIGHT_UP (GLFW_HAT_RIGHT | GLFW_HAT_UP) #define GLFW_HAT_RIGHT_DOWN (GLFW_HAT_RIGHT | GLFW_HAT_DOWN) #define GLFW_HAT_LEFT_UP (GLFW_HAT_LEFT | GLFW_HAT_UP) #define GLFW_HAT_LEFT_DOWN (GLFW_HAT_LEFT | GLFW_HAT_DOWN) /*! @} */ /*! @defgroup keys Keyboard keys * @brief Keyboard key IDs. * * See [key input](@ref input_key) for how these are used. * * These key codes are inspired by the _USB HID Usage Tables v1.12_ (p. 53-60), * but re-arranged to map to 7-bit ASCII for printable keys (function keys are * put in the 256+ range). * * The naming of the key codes follow these rules: * - The US keyboard layout is used * - Names of printable alpha-numeric characters are used (e.g. "A", "R", * "3", etc.) * - For non-alphanumeric characters, Unicode:ish names are used (e.g. * "COMMA", "LEFT_SQUARE_BRACKET", etc.). Note that some names do not * correspond to the Unicode standard (usually for brevity) * - Keys that lack a clear US mapping are named "WORLD_x" * - For non-printable keys, custom names are used (e.g. "F4", * "BACKSPACE", etc.) * * @ingroup input * @{ */ /* The unknown key */ #define GLFW_KEY_UNKNOWN -1 /* Printable keys */ #define GLFW_KEY_SPACE 32 #define GLFW_KEY_APOSTROPHE 39 /* ' */ #define GLFW_KEY_COMMA 44 /* , */ #define GLFW_KEY_MINUS 45 /* - */ #define GLFW_KEY_PERIOD 46 /* . */ #define GLFW_KEY_SLASH 47 /* / */ #define GLFW_KEY_0 48 #define GLFW_KEY_1 49 #define GLFW_KEY_2 50 #define GLFW_KEY_3 51 #define GLFW_KEY_4 52 #define GLFW_KEY_5 53 #define GLFW_KEY_6 54 #define GLFW_KEY_7 55 #define GLFW_KEY_8 56 #define GLFW_KEY_9 57 #define GLFW_KEY_SEMICOLON 59 /* ; */ #define GLFW_KEY_EQUAL 61 /* = */ #define GLFW_KEY_A 65 #define GLFW_KEY_B 66 #define GLFW_KEY_C 67 #define GLFW_KEY_D 68 #define GLFW_KEY_E 69 #define GLFW_KEY_F 70 #define GLFW_KEY_G 71 #define GLFW_KEY_H 72 #define GLFW_KEY_I 73 #define GLFW_KEY_J 74 #define GLFW_KEY_K 75 #define GLFW_KEY_L 76 #define GLFW_KEY_M 77 #define GLFW_KEY_N 78 #define GLFW_KEY_O 79 #define GLFW_KEY_P 80 #define GLFW_KEY_Q 81 #define GLFW_KEY_R 82 #define GLFW_KEY_S 83 #define GLFW_KEY_T 84 #define GLFW_KEY_U 85 #define GLFW_KEY_V 86 #define GLFW_KEY_W 87 #define GLFW_KEY_X 88 #define GLFW_KEY_Y 89 #define GLFW_KEY_Z 90 #define GLFW_KEY_LEFT_BRACKET 91 /* [ */ #define GLFW_KEY_BACKSLASH 92 /* \ */ #define GLFW_KEY_RIGHT_BRACKET 93 /* ] */ #define GLFW_KEY_GRAVE_ACCENT 96 /* ` */ #define GLFW_KEY_WORLD_1 161 /* non-US #1 */ #define GLFW_KEY_WORLD_2 162 /* non-US #2 */ /* Function keys */ #define GLFW_KEY_ESCAPE 256 #define GLFW_KEY_ENTER 257 #define GLFW_KEY_TAB 258 #define GLFW_KEY_BACKSPACE 259 #define GLFW_KEY_INSERT 260 #define GLFW_KEY_DELETE 261 #define GLFW_KEY_RIGHT 262 #define GLFW_KEY_LEFT 263 #define GLFW_KEY_DOWN 264 #define GLFW_KEY_UP 265 #define GLFW_KEY_PAGE_UP 266 #define GLFW_KEY_PAGE_DOWN 267 #define GLFW_KEY_HOME 268 #define GLFW_KEY_END 269 #define GLFW_KEY_CAPS_LOCK 280 #define GLFW_KEY_SCROLL_LOCK 281 #define GLFW_KEY_NUM_LOCK 282 #define GLFW_KEY_PRINT_SCREEN 283 #define GLFW_KEY_PAUSE 284 #define GLFW_KEY_F1 290 #define GLFW_KEY_F2 291 #define GLFW_KEY_F3 292 #define GLFW_KEY_F4 293 #define GLFW_KEY_F5 294 #define GLFW_KEY_F6 295 #define GLFW_KEY_F7 296 #define GLFW_KEY_F8 297 #define GLFW_KEY_F9 298 #define GLFW_KEY_F10 299 #define GLFW_KEY_F11 300 #define GLFW_KEY_F12 301 #define GLFW_KEY_F13 302 #define GLFW_KEY_F14 303 #define GLFW_KEY_F15 304 #define GLFW_KEY_F16 305 #define GLFW_KEY_F17 306 #define GLFW_KEY_F18 307 #define GLFW_KEY_F19 308 #define GLFW_KEY_F20 309 #define GLFW_KEY_F21 310 #define GLFW_KEY_F22 311 #define GLFW_KEY_F23 312 #define GLFW_KEY_F24 313 #define GLFW_KEY_F25 314 #define GLFW_KEY_KP_0 320 #define GLFW_KEY_KP_1 321 #define GLFW_KEY_KP_2 322 #define GLFW_KEY_KP_3 323 #define GLFW_KEY_KP_4 324 #define GLFW_KEY_KP_5 325 #define GLFW_KEY_KP_6 326 #define GLFW_KEY_KP_7 327 #define GLFW_KEY_KP_8 328 #define GLFW_KEY_KP_9 329 #define GLFW_KEY_KP_DECIMAL 330 #define GLFW_KEY_KP_DIVIDE 331 #define GLFW_KEY_KP_MULTIPLY 332 #define GLFW_KEY_KP_SUBTRACT 333 #define GLFW_KEY_KP_ADD 334 #define GLFW_KEY_KP_ENTER 335 #define GLFW_KEY_KP_EQUAL 336 #define GLFW_KEY_LEFT_SHIFT 340 #define GLFW_KEY_LEFT_CONTROL 341 #define GLFW_KEY_LEFT_ALT 342 #define GLFW_KEY_LEFT_SUPER 343 #define GLFW_KEY_RIGHT_SHIFT 344 #define GLFW_KEY_RIGHT_CONTROL 345 #define GLFW_KEY_RIGHT_ALT 346 #define GLFW_KEY_RIGHT_SUPER 347 #define GLFW_KEY_MENU 348 #define GLFW_KEY_LAST GLFW_KEY_MENU /*! @} */ /*! @defgroup mods Modifier key flags * @brief Modifier key flags. * * See [key input](@ref input_key) for how these are used. * * @ingroup input * @{ */ /*! @brief If this bit is set one or more Shift keys were held down. * * If this bit is set one or more Shift keys were held down. */ #define GLFW_MOD_SHIFT 0x0001 /*! @brief If this bit is set one or more Control keys were held down. * * If this bit is set one or more Control keys were held down. */ #define GLFW_MOD_CONTROL 0x0002 /*! @brief If this bit is set one or more Alt keys were held down. * * If this bit is set one or more Alt keys were held down. */ #define GLFW_MOD_ALT 0x0004 /*! @brief If this bit is set one or more Super keys were held down. * * If this bit is set one or more Super keys were held down. */ #define GLFW_MOD_SUPER 0x0008 /*! @brief If this bit is set the Caps Lock key is enabled. * * If this bit is set the Caps Lock key is enabled and the @ref * GLFW_LOCK_KEY_MODS input mode is set. */ #define GLFW_MOD_CAPS_LOCK 0x0010 /*! @brief If this bit is set the Num Lock key is enabled. * * If this bit is set the Num Lock key is enabled and the @ref * GLFW_LOCK_KEY_MODS input mode is set. */ #define GLFW_MOD_NUM_LOCK 0x0020 /*! @} */ /*! @defgroup buttons Mouse buttons * @brief Mouse button IDs. * * See [mouse button input](@ref input_mouse_button) for how these are used. * * @ingroup input * @{ */ #define GLFW_MOUSE_BUTTON_1 0 #define GLFW_MOUSE_BUTTON_2 1 #define GLFW_MOUSE_BUTTON_3 2 #define GLFW_MOUSE_BUTTON_4 3 #define GLFW_MOUSE_BUTTON_5 4 #define GLFW_MOUSE_BUTTON_6 5 #define GLFW_MOUSE_BUTTON_7 6 #define GLFW_MOUSE_BUTTON_8 7 #define GLFW_MOUSE_BUTTON_LAST GLFW_MOUSE_BUTTON_8 #define GLFW_MOUSE_BUTTON_LEFT GLFW_MOUSE_BUTTON_1 #define GLFW_MOUSE_BUTTON_RIGHT GLFW_MOUSE_BUTTON_2 #define GLFW_MOUSE_BUTTON_MIDDLE GLFW_MOUSE_BUTTON_3 /*! @} */ /*! @defgroup joysticks Joysticks * @brief Joystick IDs. * * See [joystick input](@ref joystick) for how these are used. * * @ingroup input * @{ */ #define GLFW_JOYSTICK_1 0 #define GLFW_JOYSTICK_2 1 #define GLFW_JOYSTICK_3 2 #define GLFW_JOYSTICK_4 3 #define GLFW_JOYSTICK_5 4 #define GLFW_JOYSTICK_6 5 #define GLFW_JOYSTICK_7 6 #define GLFW_JOYSTICK_8 7 #define GLFW_JOYSTICK_9 8 #define GLFW_JOYSTICK_10 9 #define GLFW_JOYSTICK_11 10 #define GLFW_JOYSTICK_12 11 #define GLFW_JOYSTICK_13 12 #define GLFW_JOYSTICK_14 13 #define GLFW_JOYSTICK_15 14 #define GLFW_JOYSTICK_16 15 #define GLFW_JOYSTICK_LAST GLFW_JOYSTICK_16 /*! @} */ /*! @defgroup gamepad_buttons Gamepad buttons * @brief Gamepad buttons. * * See @ref gamepad for how these are used. * * @ingroup input * @{ */ #define GLFW_GAMEPAD_BUTTON_A 0 #define GLFW_GAMEPAD_BUTTON_B 1 #define GLFW_GAMEPAD_BUTTON_X 2 #define GLFW_GAMEPAD_BUTTON_Y 3 #define GLFW_GAMEPAD_BUTTON_LEFT_BUMPER 4 #define GLFW_GAMEPAD_BUTTON_RIGHT_BUMPER 5 #define GLFW_GAMEPAD_BUTTON_BACK 6 #define GLFW_GAMEPAD_BUTTON_START 7 #define GLFW_GAMEPAD_BUTTON_GUIDE 8 #define GLFW_GAMEPAD_BUTTON_LEFT_THUMB 9 #define GLFW_GAMEPAD_BUTTON_RIGHT_THUMB 10 #define GLFW_GAMEPAD_BUTTON_DPAD_UP 11 #define GLFW_GAMEPAD_BUTTON_DPAD_RIGHT 12 #define GLFW_GAMEPAD_BUTTON_DPAD_DOWN 13 #define GLFW_GAMEPAD_BUTTON_DPAD_LEFT 14 #define GLFW_GAMEPAD_BUTTON_LAST GLFW_GAMEPAD_BUTTON_DPAD_LEFT #define GLFW_GAMEPAD_BUTTON_CROSS GLFW_GAMEPAD_BUTTON_A #define GLFW_GAMEPAD_BUTTON_CIRCLE GLFW_GAMEPAD_BUTTON_B #define GLFW_GAMEPAD_BUTTON_SQUARE GLFW_GAMEPAD_BUTTON_X #define GLFW_GAMEPAD_BUTTON_TRIANGLE GLFW_GAMEPAD_BUTTON_Y /*! @} */ /*! @defgroup gamepad_axes Gamepad axes * @brief Gamepad axes. * * See @ref gamepad for how these are used. * * @ingroup input * @{ */ #define GLFW_GAMEPAD_AXIS_LEFT_X 0 #define GLFW_GAMEPAD_AXIS_LEFT_Y 1 #define GLFW_GAMEPAD_AXIS_RIGHT_X 2 #define GLFW_GAMEPAD_AXIS_RIGHT_Y 3 #define GLFW_GAMEPAD_AXIS_LEFT_TRIGGER 4 #define GLFW_GAMEPAD_AXIS_RIGHT_TRIGGER 5 #define GLFW_GAMEPAD_AXIS_LAST GLFW_GAMEPAD_AXIS_RIGHT_TRIGGER /*! @} */ /*! @defgroup errors Error codes * @brief Error codes. * * See [error handling](@ref error_handling) for how these are used. * * @ingroup init * @{ */ /*! @brief No error has occurred. * * No error has occurred. * * @analysis Yay. */ #define GLFW_NO_ERROR 0 /*! @brief GLFW has not been initialized. * * This occurs if a GLFW function was called that must not be called unless the * library is [initialized](@ref intro_init). * * @analysis Application programmer error. Initialize GLFW before calling any * function that requires initialization. */ #define GLFW_NOT_INITIALIZED 0x00010001 /*! @brief No context is current for this thread. * * This occurs if a GLFW function was called that needs and operates on the * current OpenGL or OpenGL ES context but no context is current on the calling * thread. One such function is @ref glfwSwapInterval. * * @analysis Application programmer error. Ensure a context is current before * calling functions that require a current context. */ #define GLFW_NO_CURRENT_CONTEXT 0x00010002 /*! @brief One of the arguments to the function was an invalid enum value. * * One of the arguments to the function was an invalid enum value, for example * requesting @ref GLFW_RED_BITS with @ref glfwGetWindowAttrib. * * @analysis Application programmer error. Fix the offending call. */ #define GLFW_INVALID_ENUM 0x00010003 /*! @brief One of the arguments to the function was an invalid value. * * One of the arguments to the function was an invalid value, for example * requesting a non-existent OpenGL or OpenGL ES version like 2.7. * * Requesting a valid but unavailable OpenGL or OpenGL ES version will instead * result in a @ref GLFW_VERSION_UNAVAILABLE error. * * @analysis Application programmer error. Fix the offending call. */ #define GLFW_INVALID_VALUE 0x00010004 /*! @brief A memory allocation failed. * * A memory allocation failed. * * @analysis A bug in GLFW or the underlying operating system. Report the bug * to our [issue tracker](https://github.com/glfw/glfw/issues). */ #define GLFW_OUT_OF_MEMORY 0x00010005 /*! @brief GLFW could not find support for the requested API on the system. * * GLFW could not find support for the requested API on the system. * * @analysis The installed graphics driver does not support the requested * API, or does not support it via the chosen context creation backend. * Below are a few examples. * * @par * Some pre-installed Windows graphics drivers do not support OpenGL. AMD only * supports OpenGL ES via EGL, while Nvidia and Intel only support it via * a WGL or GLX extension. macOS does not provide OpenGL ES at all. The Mesa * EGL, OpenGL and OpenGL ES libraries do not interface with the Nvidia binary * driver. Older graphics drivers do not support Vulkan. */ #define GLFW_API_UNAVAILABLE 0x00010006 /*! @brief The requested OpenGL or OpenGL ES version is not available. * * The requested OpenGL or OpenGL ES version (including any requested context * or framebuffer hints) is not available on this machine. * * @analysis The machine does not support your requirements. If your * application is sufficiently flexible, downgrade your requirements and try * again. Otherwise, inform the user that their machine does not match your * requirements. * * @par * Future invalid OpenGL and OpenGL ES versions, for example OpenGL 4.8 if 5.0 * comes out before the 4.x series gets that far, also fail with this error and * not @ref GLFW_INVALID_VALUE, because GLFW cannot know what future versions * will exist. */ #define GLFW_VERSION_UNAVAILABLE 0x00010007 /*! @brief A platform-specific error occurred that does not match any of the * more specific categories. * * A platform-specific error occurred that does not match any of the more * specific categories. * * @analysis A bug or configuration error in GLFW, the underlying operating * system or its drivers, or a lack of required resources. Report the issue to * our [issue tracker](https://github.com/glfw/glfw/issues). */ #define GLFW_PLATFORM_ERROR 0x00010008 /*! @brief The requested format is not supported or available. * * If emitted during window creation, the requested pixel format is not * supported. * * If emitted when querying the clipboard, the contents of the clipboard could * not be converted to the requested format. * * @analysis If emitted during window creation, one or more * [hard constraints](@ref window_hints_hard) did not match any of the * available pixel formats. If your application is sufficiently flexible, * downgrade your requirements and try again. Otherwise, inform the user that * their machine does not match your requirements. * * @par * If emitted when querying the clipboard, ignore the error or report it to * the user, as appropriate. */ #define GLFW_FORMAT_UNAVAILABLE 0x00010009 /*! @brief The specified window does not have an OpenGL or OpenGL ES context. * * A window that does not have an OpenGL or OpenGL ES context was passed to * a function that requires it to have one. * * @analysis Application programmer error. Fix the offending call. */ #define GLFW_NO_WINDOW_CONTEXT 0x0001000A /*! @} */ /*! @addtogroup window * @{ */ /*! @brief Input focus window hint and attribute * * Input focus [window hint](@ref GLFW_FOCUSED_hint) or * [window attribute](@ref GLFW_FOCUSED_attrib). */ #define GLFW_FOCUSED 0x00020001 /*! @brief Window iconification window attribute * * Window iconification [window attribute](@ref GLFW_ICONIFIED_attrib). */ #define GLFW_ICONIFIED 0x00020002 /*! @brief Window resize-ability window hint and attribute * * Window resize-ability [window hint](@ref GLFW_RESIZABLE_hint) and * [window attribute](@ref GLFW_RESIZABLE_attrib). */ #define GLFW_RESIZABLE 0x00020003 /*! @brief Window visibility window hint and attribute * * Window visibility [window hint](@ref GLFW_VISIBLE_hint) and * [window attribute](@ref GLFW_VISIBLE_attrib). */ #define GLFW_VISIBLE 0x00020004 /*! @brief Window decoration window hint and attribute * * Window decoration [window hint](@ref GLFW_DECORATED_hint) and * [window attribute](@ref GLFW_DECORATED_attrib). */ #define GLFW_DECORATED 0x00020005 /*! @brief Window auto-iconification window hint and attribute * * Window auto-iconification [window hint](@ref GLFW_AUTO_ICONIFY_hint) and * [window attribute](@ref GLFW_AUTO_ICONIFY_attrib). */ #define GLFW_AUTO_ICONIFY 0x00020006 /*! @brief Window decoration window hint and attribute * * Window decoration [window hint](@ref GLFW_FLOATING_hint) and * [window attribute](@ref GLFW_FLOATING_attrib). */ #define GLFW_FLOATING 0x00020007 /*! @brief Window maximization window hint and attribute * * Window maximization [window hint](@ref GLFW_MAXIMIZED_hint) and * [window attribute](@ref GLFW_MAXIMIZED_attrib). */ #define GLFW_MAXIMIZED 0x00020008 /*! @brief Cursor centering window hint * * Cursor centering [window hint](@ref GLFW_CENTER_CURSOR_hint). */ #define GLFW_CENTER_CURSOR 0x00020009 /*! @brief Window framebuffer transparency hint and attribute * * Window framebuffer transparency * [window hint](@ref GLFW_TRANSPARENT_FRAMEBUFFER_hint) and * [window attribute](@ref GLFW_TRANSPARENT_FRAMEBUFFER_attrib). */ #define GLFW_TRANSPARENT_FRAMEBUFFER 0x0002000A /*! @brief Mouse cursor hover window attribute. * * Mouse cursor hover [window attribute](@ref GLFW_HOVERED_attrib). */ #define GLFW_HOVERED 0x0002000B /*! @brief Input focus on calling show window hint and attribute * * Input focus [window hint](@ref GLFW_FOCUS_ON_SHOW_hint) or * [window attribute](@ref GLFW_FOCUS_ON_SHOW_attrib). */ #define GLFW_FOCUS_ON_SHOW 0x0002000C /*! @brief Framebuffer bit depth hint. * * Framebuffer bit depth [hint](@ref GLFW_RED_BITS). */ #define GLFW_RED_BITS 0x00021001 /*! @brief Framebuffer bit depth hint. * * Framebuffer bit depth [hint](@ref GLFW_GREEN_BITS). */ #define GLFW_GREEN_BITS 0x00021002 /*! @brief Framebuffer bit depth hint. * * Framebuffer bit depth [hint](@ref GLFW_BLUE_BITS). */ #define GLFW_BLUE_BITS 0x00021003 /*! @brief Framebuffer bit depth hint. * * Framebuffer bit depth [hint](@ref GLFW_ALPHA_BITS). */ #define GLFW_ALPHA_BITS 0x00021004 /*! @brief Framebuffer bit depth hint. * * Framebuffer bit depth [hint](@ref GLFW_DEPTH_BITS). */ #define GLFW_DEPTH_BITS 0x00021005 /*! @brief Framebuffer bit depth hint. * * Framebuffer bit depth [hint](@ref GLFW_STENCIL_BITS). */ #define GLFW_STENCIL_BITS 0x00021006 /*! @brief Framebuffer bit depth hint. * * Framebuffer bit depth [hint](@ref GLFW_ACCUM_RED_BITS). */ #define GLFW_ACCUM_RED_BITS 0x00021007 /*! @brief Framebuffer bit depth hint. * * Framebuffer bit depth [hint](@ref GLFW_ACCUM_GREEN_BITS). */ #define GLFW_ACCUM_GREEN_BITS 0x00021008 /*! @brief Framebuffer bit depth hint. * * Framebuffer bit depth [hint](@ref GLFW_ACCUM_BLUE_BITS). */ #define GLFW_ACCUM_BLUE_BITS 0x00021009 /*! @brief Framebuffer bit depth hint. * * Framebuffer bit depth [hint](@ref GLFW_ACCUM_ALPHA_BITS). */ #define GLFW_ACCUM_ALPHA_BITS 0x0002100A /*! @brief Framebuffer auxiliary buffer hint. * * Framebuffer auxiliary buffer [hint](@ref GLFW_AUX_BUFFERS). */ #define GLFW_AUX_BUFFERS 0x0002100B /*! @brief OpenGL stereoscopic rendering hint. * * OpenGL stereoscopic rendering [hint](@ref GLFW_STEREO). */ #define GLFW_STEREO 0x0002100C /*! @brief Framebuffer MSAA samples hint. * * Framebuffer MSAA samples [hint](@ref GLFW_SAMPLES). */ #define GLFW_SAMPLES 0x0002100D /*! @brief Framebuffer sRGB hint. * * Framebuffer sRGB [hint](@ref GLFW_SRGB_CAPABLE). */ #define GLFW_SRGB_CAPABLE 0x0002100E /*! @brief Monitor refresh rate hint. * * Monitor refresh rate [hint](@ref GLFW_REFRESH_RATE). */ #define GLFW_REFRESH_RATE 0x0002100F /*! @brief Framebuffer double buffering hint. * * Framebuffer double buffering [hint](@ref GLFW_DOUBLEBUFFER). */ #define GLFW_DOUBLEBUFFER 0x00021010 /*! @brief Context client API hint and attribute. * * Context client API [hint](@ref GLFW_CLIENT_API_hint) and * [attribute](@ref GLFW_CLIENT_API_attrib). */ #define GLFW_CLIENT_API 0x00022001 /*! @brief Context client API major version hint and attribute. * * Context client API major version [hint](@ref GLFW_CONTEXT_VERSION_MAJOR_hint) * and [attribute](@ref GLFW_CONTEXT_VERSION_MAJOR_attrib). */ #define GLFW_CONTEXT_VERSION_MAJOR 0x00022002 /*! @brief Context client API minor version hint and attribute. * * Context client API minor version [hint](@ref GLFW_CONTEXT_VERSION_MINOR_hint) * and [attribute](@ref GLFW_CONTEXT_VERSION_MINOR_attrib). */ #define GLFW_CONTEXT_VERSION_MINOR 0x00022003 /*! @brief Context client API revision number attribute. * * Context client API revision number * [attribute](@ref GLFW_CONTEXT_REVISION_attrib). */ #define GLFW_CONTEXT_REVISION 0x00022004 /*! @brief Context robustness hint and attribute. * * Context client API revision number [hint](@ref GLFW_CONTEXT_ROBUSTNESS_hint) * and [attribute](@ref GLFW_CONTEXT_ROBUSTNESS_attrib). */ #define GLFW_CONTEXT_ROBUSTNESS 0x00022005 /*! @brief OpenGL forward-compatibility hint and attribute. * * OpenGL forward-compatibility [hint](@ref GLFW_OPENGL_FORWARD_COMPAT_hint) * and [attribute](@ref GLFW_OPENGL_FORWARD_COMPAT_attrib). */ #define GLFW_OPENGL_FORWARD_COMPAT 0x00022006 /*! @brief Debug mode context hint and attribute. * * Debug mode context [hint](@ref GLFW_OPENGL_DEBUG_CONTEXT_hint) and * [attribute](@ref GLFW_OPENGL_DEBUG_CONTEXT_attrib). */ #define GLFW_OPENGL_DEBUG_CONTEXT 0x00022007 /*! @brief OpenGL profile hint and attribute. * * OpenGL profile [hint](@ref GLFW_OPENGL_PROFILE_hint) and * [attribute](@ref GLFW_OPENGL_PROFILE_attrib). */ #define GLFW_OPENGL_PROFILE 0x00022008 /*! @brief Context flush-on-release hint and attribute. * * Context flush-on-release [hint](@ref GLFW_CONTEXT_RELEASE_BEHAVIOR_hint) and * [attribute](@ref GLFW_CONTEXT_RELEASE_BEHAVIOR_attrib). */ #define GLFW_CONTEXT_RELEASE_BEHAVIOR 0x00022009 /*! @brief Context error suppression hint and attribute. * * Context error suppression [hint](@ref GLFW_CONTEXT_NO_ERROR_hint) and * [attribute](@ref GLFW_CONTEXT_NO_ERROR_attrib). */ #define GLFW_CONTEXT_NO_ERROR 0x0002200A /*! @brief Context creation API hint and attribute. * * Context creation API [hint](@ref GLFW_CONTEXT_CREATION_API_hint) and * [attribute](@ref GLFW_CONTEXT_CREATION_API_attrib). */ #define GLFW_CONTEXT_CREATION_API 0x0002200B /*! @brief Window content area scaling window * [window hint](@ref GLFW_SCALE_TO_MONITOR). */ #define GLFW_SCALE_TO_MONITOR 0x0002200C /*! @brief macOS specific * [window hint](@ref GLFW_COCOA_RETINA_FRAMEBUFFER_hint). */ #define GLFW_COCOA_RETINA_FRAMEBUFFER 0x00023001 /*! @brief macOS specific * [window hint](@ref GLFW_COCOA_FRAME_NAME_hint). */ #define GLFW_COCOA_FRAME_NAME 0x00023002 /*! @brief macOS specific * [window hint](@ref GLFW_COCOA_GRAPHICS_SWITCHING_hint). */ #define GLFW_COCOA_GRAPHICS_SWITCHING 0x00023003 /*! @brief X11 specific * [window hint](@ref GLFW_X11_CLASS_NAME_hint). */ #define GLFW_X11_CLASS_NAME 0x00024001 /*! @brief X11 specific * [window hint](@ref GLFW_X11_CLASS_NAME_hint). */ #define GLFW_X11_INSTANCE_NAME 0x00024002 /*! @} */ #define GLFW_NO_API 0 #define GLFW_OPENGL_API 0x00030001 #define GLFW_OPENGL_ES_API 0x00030002 #define GLFW_NO_ROBUSTNESS 0 #define GLFW_NO_RESET_NOTIFICATION 0x00031001 #define GLFW_LOSE_CONTEXT_ON_RESET 0x00031002 #define GLFW_OPENGL_ANY_PROFILE 0 #define GLFW_OPENGL_CORE_PROFILE 0x00032001 #define GLFW_OPENGL_COMPAT_PROFILE 0x00032002 #define GLFW_CURSOR 0x00033001 #define GLFW_STICKY_KEYS 0x00033002 #define GLFW_STICKY_MOUSE_BUTTONS 0x00033003 #define GLFW_LOCK_KEY_MODS 0x00033004 #define GLFW_RAW_MOUSE_MOTION 0x00033005 #define GLFW_CURSOR_NORMAL 0x00034001 #define GLFW_CURSOR_HIDDEN 0x00034002 #define GLFW_CURSOR_DISABLED 0x00034003 #define GLFW_ANY_RELEASE_BEHAVIOR 0 #define GLFW_RELEASE_BEHAVIOR_FLUSH 0x00035001 #define GLFW_RELEASE_BEHAVIOR_NONE 0x00035002 #define GLFW_NATIVE_CONTEXT_API 0x00036001 #define GLFW_EGL_CONTEXT_API 0x00036002 #define GLFW_OSMESA_CONTEXT_API 0x00036003 /*! @defgroup shapes Standard cursor shapes * @brief Standard system cursor shapes. * * See [standard cursor creation](@ref cursor_standard) for how these are used. * * @ingroup input * @{ */ /*! @brief The regular arrow cursor shape. * * The regular arrow cursor. */ #define GLFW_ARROW_CURSOR 0x00036001 /*! @brief The text input I-beam cursor shape. * * The text input I-beam cursor shape. */ #define GLFW_IBEAM_CURSOR 0x00036002 /*! @brief The crosshair shape. * * The crosshair shape. */ #define GLFW_CROSSHAIR_CURSOR 0x00036003 /*! @brief The hand shape. * * The hand shape. */ #define GLFW_HAND_CURSOR 0x00036004 /*! @brief The horizontal resize arrow shape. * * The horizontal resize arrow shape. */ #define GLFW_HRESIZE_CURSOR 0x00036005 /*! @brief The vertical resize arrow shape. * * The vertical resize arrow shape. */ #define GLFW_VRESIZE_CURSOR 0x00036006 /*! @} */ #define GLFW_CONNECTED 0x00040001 #define GLFW_DISCONNECTED 0x00040002 /*! @addtogroup init * @{ */ /*! @brief Joystick hat buttons init hint. * * Joystick hat buttons [init hint](@ref GLFW_JOYSTICK_HAT_BUTTONS). */ #define GLFW_JOYSTICK_HAT_BUTTONS 0x00050001 /*! @brief macOS specific init hint. * * macOS specific [init hint](@ref GLFW_COCOA_CHDIR_RESOURCES_hint). */ #define GLFW_COCOA_CHDIR_RESOURCES 0x00051001 /*! @brief macOS specific init hint. * * macOS specific [init hint](@ref GLFW_COCOA_MENUBAR_hint). */ #define GLFW_COCOA_MENUBAR 0x00051002 /*! @} */ #define GLFW_DONT_CARE -1 /************************************************************************* * GLFW API types *************************************************************************/ /*! @brief Client API function pointer type. * * Generic function pointer used for returning client API function pointers * without forcing a cast from a regular pointer. * * @sa @ref context_glext * @sa @ref glfwGetProcAddress * * @since Added in version 3.0. * * @ingroup context */ typedef void (*GLFWglproc)(void); /*! @brief Vulkan API function pointer type. * * Generic function pointer used for returning Vulkan API function pointers * without forcing a cast from a regular pointer. * * @sa @ref vulkan_proc * @sa @ref glfwGetInstanceProcAddress * * @since Added in version 3.2. * * @ingroup vulkan */ typedef void (*GLFWvkproc)(void); /*! @brief Opaque monitor object. * * Opaque monitor object. * * @see @ref monitor_object * * @since Added in version 3.0. * * @ingroup monitor */ typedef struct GLFWmonitor GLFWmonitor; /*! @brief Opaque window object. * * Opaque window object. * * @see @ref window_object * * @since Added in version 3.0. * * @ingroup window */ typedef struct GLFWwindow GLFWwindow; /*! @brief Opaque cursor object. * * Opaque cursor object. * * @see @ref cursor_object * * @since Added in version 3.1. * * @ingroup input */ typedef struct GLFWcursor GLFWcursor; /*! @brief The function pointer type for error callbacks. * * This is the function pointer type for error callbacks. An error callback * function has the following signature: * @code * void callback_name(int error_code, const char* description) * @endcode * * @param[in] error_code An [error code](@ref errors). Future releases may add * more error codes. * @param[in] description A UTF-8 encoded string describing the error. * * @pointer_lifetime The error description string is valid until the callback * function returns. * * @sa @ref error_handling * @sa @ref glfwSetErrorCallback * * @since Added in version 3.0. * * @ingroup init */ typedef void (* GLFWerrorfun)(int error_code, const char* description); /*! @brief The function pointer type for window position callbacks. * * This is the function pointer type for window position callbacks. A window * position callback function has the following signature: * @code * void callback_name(GLFWwindow* window, int xpos, int ypos) * @endcode * * @param[in] window The window that was moved. * @param[in] xpos The new x-coordinate, in screen coordinates, of the * upper-left corner of the content area of the window. * @param[in] ypos The new y-coordinate, in screen coordinates, of the * upper-left corner of the content area of the window. * * @sa @ref window_pos * @sa @ref glfwSetWindowPosCallback * * @since Added in version 3.0. * * @ingroup window */ typedef void (* GLFWwindowposfun)(GLFWwindow* window, int xpos, int ypos); /*! @brief The function pointer type for window size callbacks. * * This is the function pointer type for window size callbacks. A window size * callback function has the following signature: * @code * void callback_name(GLFWwindow* window, int width, int height) * @endcode * * @param[in] window The window that was resized. * @param[in] width The new width, in screen coordinates, of the window. * @param[in] height The new height, in screen coordinates, of the window. * * @sa @ref window_size * @sa @ref glfwSetWindowSizeCallback * * @since Added in version 1.0. * @glfw3 Added window handle parameter. * * @ingroup window */ typedef void (* GLFWwindowsizefun)(GLFWwindow* window, int width, int height); /*! @brief The function pointer type for window close callbacks. * * This is the function pointer type for window close callbacks. A window * close callback function has the following signature: * @code * void function_name(GLFWwindow* window) * @endcode * * @param[in] window The window that the user attempted to close. * * @sa @ref window_close * @sa @ref glfwSetWindowCloseCallback * * @since Added in version 2.5. * @glfw3 Added window handle parameter. * * @ingroup window */ typedef void (* GLFWwindowclosefun)(GLFWwindow* window); /*! @brief The function pointer type for window content refresh callbacks. * * This is the function pointer type for window content refresh callbacks. * A window content refresh callback function has the following signature: * @code * void function_name(GLFWwindow* window); * @endcode * * @param[in] window The window whose content needs to be refreshed. * * @sa @ref window_refresh * @sa @ref glfwSetWindowRefreshCallback * * @since Added in version 2.5. * @glfw3 Added window handle parameter. * * @ingroup window */ typedef void (* GLFWwindowrefreshfun)(GLFWwindow* window); /*! @brief The function pointer type for window focus callbacks. * * This is the function pointer type for window focus callbacks. A window * focus callback function has the following signature: * @code * void function_name(GLFWwindow* window, int focused) * @endcode * * @param[in] window The window that gained or lost input focus. * @param[in] focused `GLFW_TRUE` if the window was given input focus, or * `GLFW_FALSE` if it lost it. * * @sa @ref window_focus * @sa @ref glfwSetWindowFocusCallback * * @since Added in version 3.0. * * @ingroup window */ typedef void (* GLFWwindowfocusfun)(GLFWwindow* window, int focused); /*! @brief The function pointer type for window iconify callbacks. * * This is the function pointer type for window iconify callbacks. A window * iconify callback function has the following signature: * @code * void function_name(GLFWwindow* window, int iconified) * @endcode * * @param[in] window The window that was iconified or restored. * @param[in] iconified `GLFW_TRUE` if the window was iconified, or * `GLFW_FALSE` if it was restored. * * @sa @ref window_iconify * @sa @ref glfwSetWindowIconifyCallback * * @since Added in version 3.0. * * @ingroup window */ typedef void (* GLFWwindowiconifyfun)(GLFWwindow* window, int iconified); /*! @brief The function pointer type for window maximize callbacks. * * This is the function pointer type for window maximize callbacks. A window * maximize callback function has the following signature: * @code * void function_name(GLFWwindow* window, int maximized) * @endcode * * @param[in] window The window that was maximized or restored. * @param[in] maximized `GLFW_TRUE` if the window was maximized, or * `GLFW_FALSE` if it was restored. * * @sa @ref window_maximize * @sa glfwSetWindowMaximizeCallback * * @since Added in version 3.3. * * @ingroup window */ typedef void (* GLFWwindowmaximizefun)(GLFWwindow* window, int maximized); /*! @brief The function pointer type for framebuffer size callbacks. * * This is the function pointer type for framebuffer size callbacks. * A framebuffer size callback function has the following signature: * @code * void function_name(GLFWwindow* window, int width, int height) * @endcode * * @param[in] window The window whose framebuffer was resized. * @param[in] width The new width, in pixels, of the framebuffer. * @param[in] height The new height, in pixels, of the framebuffer. * * @sa @ref window_fbsize * @sa @ref glfwSetFramebufferSizeCallback * * @since Added in version 3.0. * * @ingroup window */ typedef void (* GLFWframebuffersizefun)(GLFWwindow* window, int width, int height); /*! @brief The function pointer type for window content scale callbacks. * * This is the function pointer type for window content scale callbacks. * A window content scale callback function has the following signature: * @code * void function_name(GLFWwindow* window, float xscale, float yscale) * @endcode * * @param[in] window The window whose content scale changed. * @param[in] xscale The new x-axis content scale of the window. * @param[in] yscale The new y-axis content scale of the window. * * @sa @ref window_scale * @sa @ref glfwSetWindowContentScaleCallback * * @since Added in version 3.3. * * @ingroup window */ typedef void (* GLFWwindowcontentscalefun)(GLFWwindow* window, float xscale, float yscale); /*! @brief The function pointer type for mouse button callbacks. * * This is the function pointer type for mouse button callback functions. * A mouse button callback function has the following signature: * @code * void function_name(GLFWwindow* window, int button, int action, int mods) * @endcode * * @param[in] window The window that received the event. * @param[in] button The [mouse button](@ref buttons) that was pressed or * released. * @param[in] action One of `GLFW_PRESS` or `GLFW_RELEASE`. Future releases * may add more actions. * @param[in] mods Bit field describing which [modifier keys](@ref mods) were * held down. * * @sa @ref input_mouse_button * @sa @ref glfwSetMouseButtonCallback * * @since Added in version 1.0. * @glfw3 Added window handle and modifier mask parameters. * * @ingroup input */ typedef void (* GLFWmousebuttonfun)(GLFWwindow* window, int button, int action, int mods); /*! @brief The function pointer type for cursor position callbacks. * * This is the function pointer type for cursor position callbacks. A cursor * position callback function has the following signature: * @code * void function_name(GLFWwindow* window, double xpos, double ypos); * @endcode * * @param[in] window The window that received the event. * @param[in] xpos The new cursor x-coordinate, relative to the left edge of * the content area. * @param[in] ypos The new cursor y-coordinate, relative to the top edge of the * content area. * * @sa @ref cursor_pos * @sa @ref glfwSetCursorPosCallback * * @since Added in version 3.0. Replaces `GLFWmouseposfun`. * * @ingroup input */ typedef void (* GLFWcursorposfun)(GLFWwindow* window, double xpos, double ypos); /*! @brief The function pointer type for cursor enter/leave callbacks. * * This is the function pointer type for cursor enter/leave callbacks. * A cursor enter/leave callback function has the following signature: * @code * void function_name(GLFWwindow* window, int entered) * @endcode * * @param[in] window The window that received the event. * @param[in] entered `GLFW_TRUE` if the cursor entered the window's content * area, or `GLFW_FALSE` if it left it. * * @sa @ref cursor_enter * @sa @ref glfwSetCursorEnterCallback * * @since Added in version 3.0. * * @ingroup input */ typedef void (* GLFWcursorenterfun)(GLFWwindow* window, int entered); /*! @brief The function pointer type for scroll callbacks. * * This is the function pointer type for scroll callbacks. A scroll callback * function has the following signature: * @code * void function_name(GLFWwindow* window, double xoffset, double yoffset) * @endcode * * @param[in] window The window that received the event. * @param[in] xoffset The scroll offset along the x-axis. * @param[in] yoffset The scroll offset along the y-axis. * * @sa @ref scrolling * @sa @ref glfwSetScrollCallback * * @since Added in version 3.0. Replaces `GLFWmousewheelfun`. * * @ingroup input */ typedef void (* GLFWscrollfun)(GLFWwindow* window, double xoffset, double yoffset); /*! @brief The function pointer type for keyboard key callbacks. * * This is the function pointer type for keyboard key callbacks. A keyboard * key callback function has the following signature: * @code * void function_name(GLFWwindow* window, int key, int scancode, int action, int mods) * @endcode * * @param[in] window The window that received the event. * @param[in] key The [keyboard key](@ref keys) that was pressed or released. * @param[in] scancode The system-specific scancode of the key. * @param[in] action `GLFW_PRESS`, `GLFW_RELEASE` or `GLFW_REPEAT`. Future * releases may add more actions. * @param[in] mods Bit field describing which [modifier keys](@ref mods) were * held down. * * @sa @ref input_key * @sa @ref glfwSetKeyCallback * * @since Added in version 1.0. * @glfw3 Added window handle, scancode and modifier mask parameters. * * @ingroup input */ typedef void (* GLFWkeyfun)(GLFWwindow* window, int key, int scancode, int action, int mods); /*! @brief The function pointer type for Unicode character callbacks. * * This is the function pointer type for Unicode character callbacks. * A Unicode character callback function has the following signature: * @code * void function_name(GLFWwindow* window, unsigned int codepoint) * @endcode * * @param[in] window The window that received the event. * @param[in] codepoint The Unicode code point of the character. * * @sa @ref input_char * @sa @ref glfwSetCharCallback * * @since Added in version 2.4. * @glfw3 Added window handle parameter. * * @ingroup input */ typedef void (* GLFWcharfun)(GLFWwindow* window, unsigned int codepoint); /*! @brief The function pointer type for Unicode character with modifiers * callbacks. * * This is the function pointer type for Unicode character with modifiers * callbacks. It is called for each input character, regardless of what * modifier keys are held down. A Unicode character with modifiers callback * function has the following signature: * @code * void function_name(GLFWwindow* window, unsigned int codepoint, int mods) * @endcode * * @param[in] window The window that received the event. * @param[in] codepoint The Unicode code point of the character. * @param[in] mods Bit field describing which [modifier keys](@ref mods) were * held down. * * @sa @ref input_char * @sa @ref glfwSetCharModsCallback * * @deprecated Scheduled for removal in version 4.0. * * @since Added in version 3.1. * * @ingroup input */ typedef void (* GLFWcharmodsfun)(GLFWwindow* window, unsigned int codepoint, int mods); /*! @brief The function pointer type for path drop callbacks. * * This is the function pointer type for path drop callbacks. A path drop * callback function has the following signature: * @code * void function_name(GLFWwindow* window, int path_count, const char* paths[]) * @endcode * * @param[in] window The window that received the event. * @param[in] path_count The number of dropped paths. * @param[in] paths The UTF-8 encoded file and/or directory path names. * * @pointer_lifetime The path array and its strings are valid until the * callback function returns. * * @sa @ref path_drop * @sa @ref glfwSetDropCallback * * @since Added in version 3.1. * * @ingroup input */ typedef void (* GLFWdropfun)(GLFWwindow* window, int path_count, const char* paths[]); /*! @brief The function pointer type for monitor configuration callbacks. * * This is the function pointer type for monitor configuration callbacks. * A monitor callback function has the following signature: * @code * void function_name(GLFWmonitor* monitor, int event) * @endcode * * @param[in] monitor The monitor that was connected or disconnected. * @param[in] event One of `GLFW_CONNECTED` or `GLFW_DISCONNECTED`. Future * releases may add more events. * * @sa @ref monitor_event * @sa @ref glfwSetMonitorCallback * * @since Added in version 3.0. * * @ingroup monitor */ typedef void (* GLFWmonitorfun)(GLFWmonitor* monitor, int event); /*! @brief The function pointer type for joystick configuration callbacks. * * This is the function pointer type for joystick configuration callbacks. * A joystick configuration callback function has the following signature: * @code * void function_name(int jid, int event) * @endcode * * @param[in] jid The joystick that was connected or disconnected. * @param[in] event One of `GLFW_CONNECTED` or `GLFW_DISCONNECTED`. Future * releases may add more events. * * @sa @ref joystick_event * @sa @ref glfwSetJoystickCallback * * @since Added in version 3.2. * * @ingroup input */ typedef void (* GLFWjoystickfun)(int jid, int event); /*! @brief Video mode type. * * This describes a single video mode. * * @sa @ref monitor_modes * @sa @ref glfwGetVideoMode * @sa @ref glfwGetVideoModes * * @since Added in version 1.0. * @glfw3 Added refresh rate member. * * @ingroup monitor */ typedef struct GLFWvidmode { /*! The width, in screen coordinates, of the video mode. */ int width; /*! The height, in screen coordinates, of the video mode. */ int height; /*! The bit depth of the red channel of the video mode. */ int redBits; /*! The bit depth of the green channel of the video mode. */ int greenBits; /*! The bit depth of the blue channel of the video mode. */ int blueBits; /*! The refresh rate, in Hz, of the video mode. */ int refreshRate; } GLFWvidmode; /*! @brief Gamma ramp. * * This describes the gamma ramp for a monitor. * * @sa @ref monitor_gamma * @sa @ref glfwGetGammaRamp * @sa @ref glfwSetGammaRamp * * @since Added in version 3.0. * * @ingroup monitor */ typedef struct GLFWgammaramp { /*! An array of value describing the response of the red channel. */ unsigned short* red; /*! An array of value describing the response of the green channel. */ unsigned short* green; /*! An array of value describing the response of the blue channel. */ unsigned short* blue; /*! The number of elements in each array. */ unsigned int size; } GLFWgammaramp; /*! @brief Image data. * * This describes a single 2D image. See the documentation for each related * function what the expected pixel format is. * * @sa @ref cursor_custom * @sa @ref window_icon * * @since Added in version 2.1. * @glfw3 Removed format and bytes-per-pixel members. * * @ingroup window */ typedef struct GLFWimage { /*! The width, in pixels, of this image. */ int width; /*! The height, in pixels, of this image. */ int height; /*! The pixel data of this image, arranged left-to-right, top-to-bottom. */ unsigned char* pixels; } GLFWimage; /*! @brief Gamepad input state * * This describes the input state of a gamepad. * * @sa @ref gamepad * @sa @ref glfwGetGamepadState * * @since Added in version 3.3. * * @ingroup input */ typedef struct GLFWgamepadstate { /*! The states of each [gamepad button](@ref gamepad_buttons), `GLFW_PRESS` * or `GLFW_RELEASE`. */ unsigned char buttons[15]; /*! The states of each [gamepad axis](@ref gamepad_axes), in the range -1.0 * to 1.0 inclusive. */ float axes[6]; } GLFWgamepadstate; /************************************************************************* * GLFW API functions *************************************************************************/ /*! @brief Initializes the GLFW library. * * This function initializes the GLFW library. Before most GLFW functions can * be used, GLFW must be initialized, and before an application terminates GLFW * should be terminated in order to free any resources allocated during or * after initialization. * * If this function fails, it calls @ref glfwTerminate before returning. If it * succeeds, you should call @ref glfwTerminate before the application exits. * * Additional calls to this function after successful initialization but before * termination will return `GLFW_TRUE` immediately. * * @return `GLFW_TRUE` if successful, or `GLFW_FALSE` if an * [error](@ref error_handling) occurred. * * @errors Possible errors include @ref GLFW_PLATFORM_ERROR. * * @remark @macos This function will change the current directory of the * application to the `Contents/Resources` subdirectory of the application's * bundle, if present. This can be disabled with the @ref * GLFW_COCOA_CHDIR_RESOURCES init hint. * * @remark @x11 This function will set the `LC_CTYPE` category of the * application locale according to the current environment if that category is * still "C". This is because the "C" locale breaks Unicode text input. * * @thread_safety This function must only be called from the main thread. * * @sa @ref intro_init * @sa @ref glfwTerminate * * @since Added in version 1.0. * * @ingroup init */ GLFWAPI int glfwInit(void); /*! @brief Terminates the GLFW library. * * This function destroys all remaining windows and cursors, restores any * modified gamma ramps and frees any other allocated resources. Once this * function is called, you must again call @ref glfwInit successfully before * you will be able to use most GLFW functions. * * If GLFW has been successfully initialized, this function should be called * before the application exits. If initialization fails, there is no need to * call this function, as it is called by @ref glfwInit before it returns * failure. * * This function has no effect if GLFW is not initialized. * * @errors Possible errors include @ref GLFW_PLATFORM_ERROR. * * @remark This function may be called before @ref glfwInit. * * @warning The contexts of any remaining windows must not be current on any * other thread when this function is called. * * @reentrancy This function must not be called from a callback. * * @thread_safety This function must only be called from the main thread. * * @sa @ref intro_init * @sa @ref glfwInit * * @since Added in version 1.0. * * @ingroup init */ GLFWAPI void glfwTerminate(void); /*! @brief Sets the specified init hint to the desired value. * * This function sets hints for the next initialization of GLFW. * * The values you set hints to are never reset by GLFW, but they only take * effect during initialization. Once GLFW has been initialized, any values * you set will be ignored until the library is terminated and initialized * again. * * Some hints are platform specific. These may be set on any platform but they * will only affect their specific platform. Other platforms will ignore them. * Setting these hints requires no platform specific headers or functions. * * @param[in] hint The [init hint](@ref init_hints) to set. * @param[in] value The new value of the init hint. * * @errors Possible errors include @ref GLFW_INVALID_ENUM and @ref * GLFW_INVALID_VALUE. * * @remarks This function may be called before @ref glfwInit. * * @thread_safety This function must only be called from the main thread. * * @sa init_hints * @sa glfwInit * * @since Added in version 3.3. * * @ingroup init */ GLFWAPI void glfwInitHint(int hint, int value); /*! @brief Retrieves the version of the GLFW library. * * This function retrieves the major, minor and revision numbers of the GLFW * library. It is intended for when you are using GLFW as a shared library and * want to ensure that you are using the minimum required version. * * Any or all of the version arguments may be `NULL`. * * @param[out] major Where to store the major version number, or `NULL`. * @param[out] minor Where to store the minor version number, or `NULL`. * @param[out] rev Where to store the revision number, or `NULL`. * * @errors None. * * @remark This function may be called before @ref glfwInit. * * @thread_safety This function may be called from any thread. * * @sa @ref intro_version * @sa @ref glfwGetVersionString * * @since Added in version 1.0. * * @ingroup init */ GLFWAPI void glfwGetVersion(int* major, int* minor, int* rev); /*! @brief Returns a string describing the compile-time configuration. * * This function returns the compile-time generated * [version string](@ref intro_version_string) of the GLFW library binary. It * describes the version, platform, compiler and any platform-specific * compile-time options. It should not be confused with the OpenGL or OpenGL * ES version string, queried with `glGetString`. * * __Do not use the version string__ to parse the GLFW library version. The * @ref glfwGetVersion function provides the version of the running library * binary in numerical format. * * @return The ASCII encoded GLFW version string. * * @errors None. * * @remark This function may be called before @ref glfwInit. * * @pointer_lifetime The returned string is static and compile-time generated. * * @thread_safety This function may be called from any thread. * * @sa @ref intro_version * @sa @ref glfwGetVersion * * @since Added in version 3.0. * * @ingroup init */ GLFWAPI const char* glfwGetVersionString(void); /*! @brief Returns and clears the last error for the calling thread. * * This function returns and clears the [error code](@ref errors) of the last * error that occurred on the calling thread, and optionally a UTF-8 encoded * human-readable description of it. If no error has occurred since the last * call, it returns @ref GLFW_NO_ERROR (zero) and the description pointer is * set to `NULL`. * * @param[in] description Where to store the error description pointer, or `NULL`. * @return The last error code for the calling thread, or @ref GLFW_NO_ERROR * (zero). * * @errors None. * * @pointer_lifetime The returned string is allocated and freed by GLFW. You * should not free it yourself. It is guaranteed to be valid only until the * next error occurs or the library is terminated. * * @remark This function may be called before @ref glfwInit. * * @thread_safety This function may be called from any thread. * * @sa @ref error_handling * @sa @ref glfwSetErrorCallback * * @since Added in version 3.3. * * @ingroup init */ GLFWAPI int glfwGetError(const char** description); /*! @brief Sets the error callback. * * This function sets the error callback, which is called with an error code * and a human-readable description each time a GLFW error occurs. * * The error code is set before the callback is called. Calling @ref * glfwGetError from the error callback will return the same value as the error * code argument. * * The error callback is called on the thread where the error occurred. If you * are using GLFW from multiple threads, your error callback needs to be * written accordingly. * * Because the description string may have been generated specifically for that * error, it is not guaranteed to be valid after the callback has returned. If * you wish to use it after the callback returns, you need to make a copy. * * Once set, the error callback remains set even after the library has been * terminated. * * @param[in] callback The new callback, or `NULL` to remove the currently set * callback. * @return The previously set callback, or `NULL` if no callback was set. * * @callback_signature * @code * void callback_name(int error_code, const char* description) * @endcode * For more information about the callback parameters, see the * [callback pointer type](@ref GLFWerrorfun). * * @errors None. * * @remark This function may be called before @ref glfwInit. * * @thread_safety This function must only be called from the main thread. * * @sa @ref error_handling * @sa @ref glfwGetError * * @since Added in version 3.0. * * @ingroup init */ GLFWAPI GLFWerrorfun glfwSetErrorCallback(GLFWerrorfun callback); /*! @brief Returns the currently connected monitors. * * This function returns an array of handles for all currently connected * monitors. The primary monitor is always first in the returned array. If no * monitors were found, this function returns `NULL`. * * @param[out] count Where to store the number of monitors in the returned * array. This is set to zero if an error occurred. * @return An array of monitor handles, or `NULL` if no monitors were found or * if an [error](@ref error_handling) occurred. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. * * @pointer_lifetime The returned array is allocated and freed by GLFW. You * should not free it yourself. It is guaranteed to be valid only until the * monitor configuration changes or the library is terminated. * * @thread_safety This function must only be called from the main thread. * * @sa @ref monitor_monitors * @sa @ref monitor_event * @sa @ref glfwGetPrimaryMonitor * * @since Added in version 3.0. * * @ingroup monitor */ GLFWAPI GLFWmonitor** glfwGetMonitors(int* count); /*! @brief Returns the primary monitor. * * This function returns the primary monitor. This is usually the monitor * where elements like the task bar or global menu bar are located. * * @return The primary monitor, or `NULL` if no monitors were found or if an * [error](@ref error_handling) occurred. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. * * @thread_safety This function must only be called from the main thread. * * @remark The primary monitor is always first in the array returned by @ref * glfwGetMonitors. * * @sa @ref monitor_monitors * @sa @ref glfwGetMonitors * * @since Added in version 3.0. * * @ingroup monitor */ GLFWAPI GLFWmonitor* glfwGetPrimaryMonitor(void); /*! @brief Returns the position of the monitor's viewport on the virtual screen. * * This function returns the position, in screen coordinates, of the upper-left * corner of the specified monitor. * * Any or all of the position arguments may be `NULL`. If an error occurs, all * non-`NULL` position arguments will be set to zero. * * @param[in] monitor The monitor to query. * @param[out] xpos Where to store the monitor x-coordinate, or `NULL`. * @param[out] ypos Where to store the monitor y-coordinate, or `NULL`. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref * GLFW_PLATFORM_ERROR. * * @thread_safety This function must only be called from the main thread. * * @sa @ref monitor_properties * * @since Added in version 3.0. * * @ingroup monitor */ GLFWAPI void glfwGetMonitorPos(GLFWmonitor* monitor, int* xpos, int* ypos); /*! @brief Retrieves the work area of the monitor. * * This function returns the position, in screen coordinates, of the upper-left * corner of the work area of the specified monitor along with the work area * size in screen coordinates. The work area is defined as the area of the * monitor not occluded by the operating system task bar where present. If no * task bar exists then the work area is the monitor resolution in screen * coordinates. * * Any or all of the position and size arguments may be `NULL`. If an error * occurs, all non-`NULL` position and size arguments will be set to zero. * * @param[in] monitor The monitor to query. * @param[out] xpos Where to store the monitor x-coordinate, or `NULL`. * @param[out] ypos Where to store the monitor y-coordinate, or `NULL`. * @param[out] width Where to store the monitor width, or `NULL`. * @param[out] height Where to store the monitor height, or `NULL`. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref * GLFW_PLATFORM_ERROR. * * @thread_safety This function must only be called from the main thread. * * @sa @ref monitor_workarea * * @since Added in version 3.3. * * @ingroup monitor */ GLFWAPI void glfwGetMonitorWorkarea(GLFWmonitor* monitor, int* xpos, int* ypos, int* width, int* height); /*! @brief Returns the physical size of the monitor. * * This function returns the size, in millimetres, of the display area of the * specified monitor. * * Some systems do not provide accurate monitor size information, either * because the monitor * [EDID](https://en.wikipedia.org/wiki/Extended_display_identification_data) * data is incorrect or because the driver does not report it accurately. * * Any or all of the size arguments may be `NULL`. If an error occurs, all * non-`NULL` size arguments will be set to zero. * * @param[in] monitor The monitor to query. * @param[out] widthMM Where to store the width, in millimetres, of the * monitor's display area, or `NULL`. * @param[out] heightMM Where to store the height, in millimetres, of the * monitor's display area, or `NULL`. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. * * @remark @win32 On Windows 8 and earlier the physical size is calculated from * the current resolution and system DPI instead of querying the monitor EDID data. * * @thread_safety This function must only be called from the main thread. * * @sa @ref monitor_properties * * @since Added in version 3.0. * * @ingroup monitor */ GLFWAPI void glfwGetMonitorPhysicalSize(GLFWmonitor* monitor, int* widthMM, int* heightMM); /*! @brief Retrieves the content scale for the specified monitor. * * This function retrieves the content scale for the specified monitor. The * content scale is the ratio between the current DPI and the platform's * default DPI. This is especially important for text and any UI elements. If * the pixel dimensions of your UI scaled by this look appropriate on your * machine then it should appear at a reasonable size on other machines * regardless of their DPI and scaling settings. This relies on the system DPI * and scaling settings being somewhat correct. * * The content scale may depend on both the monitor resolution and pixel * density and on user settings. It may be very different from the raw DPI * calculated from the physical size and current resolution. * * @param[in] monitor The monitor to query. * @param[out] xscale Where to store the x-axis content scale, or `NULL`. * @param[out] yscale Where to store the y-axis content scale, or `NULL`. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref * GLFW_PLATFORM_ERROR. * * @thread_safety This function must only be called from the main thread. * * @sa @ref monitor_scale * @sa @ref glfwGetWindowContentScale * * @since Added in version 3.3. * * @ingroup monitor */ GLFWAPI void glfwGetMonitorContentScale(GLFWmonitor* monitor, float* xscale, float* yscale); /*! @brief Returns the name of the specified monitor. * * This function returns a human-readable name, encoded as UTF-8, of the * specified monitor. The name typically reflects the make and model of the * monitor and is not guaranteed to be unique among the connected monitors. * * @param[in] monitor The monitor to query. * @return The UTF-8 encoded name of the monitor, or `NULL` if an * [error](@ref error_handling) occurred. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. * * @pointer_lifetime The returned string is allocated and freed by GLFW. You * should not free it yourself. It is valid until the specified monitor is * disconnected or the library is terminated. * * @thread_safety This function must only be called from the main thread. * * @sa @ref monitor_properties * * @since Added in version 3.0. * * @ingroup monitor */ GLFWAPI const char* glfwGetMonitorName(GLFWmonitor* monitor); /*! @brief Sets the user pointer of the specified monitor. * * This function sets the user-defined pointer of the specified monitor. The * current value is retained until the monitor is disconnected. The initial * value is `NULL`. * * This function may be called from the monitor callback, even for a monitor * that is being disconnected. * * @param[in] monitor The monitor whose pointer to set. * @param[in] pointer The new value. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. * * @thread_safety This function may be called from any thread. Access is not * synchronized. * * @sa @ref monitor_userptr * @sa @ref glfwGetMonitorUserPointer * * @since Added in version 3.3. * * @ingroup monitor */ GLFWAPI void glfwSetMonitorUserPointer(GLFWmonitor* monitor, void* pointer); /*! @brief Returns the user pointer of the specified monitor. * * This function returns the current value of the user-defined pointer of the * specified monitor. The initial value is `NULL`. * * This function may be called from the monitor callback, even for a monitor * that is being disconnected. * * @param[in] monitor The monitor whose pointer to return. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. * * @thread_safety This function may be called from any thread. Access is not * synchronized. * * @sa @ref monitor_userptr * @sa @ref glfwSetMonitorUserPointer * * @since Added in version 3.3. * * @ingroup monitor */ GLFWAPI void* glfwGetMonitorUserPointer(GLFWmonitor* monitor); /*! @brief Sets the monitor configuration callback. * * This function sets the monitor configuration callback, or removes the * currently set callback. This is called when a monitor is connected to or * disconnected from the system. * * @param[in] callback The new callback, or `NULL` to remove the currently set * callback. * @return The previously set callback, or `NULL` if no callback was set or the * library had not been [initialized](@ref intro_init). * * @callback_signature * @code * void function_name(GLFWmonitor* monitor, int event) * @endcode * For more information about the callback parameters, see the * [function pointer type](@ref GLFWmonitorfun). * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. * * @thread_safety This function must only be called from the main thread. * * @sa @ref monitor_event * * @since Added in version 3.0. * * @ingroup monitor */ GLFWAPI GLFWmonitorfun glfwSetMonitorCallback(GLFWmonitorfun callback); /*! @brief Returns the available video modes for the specified monitor. * * This function returns an array of all video modes supported by the specified * monitor. The returned array is sorted in ascending order, first by color * bit depth (the sum of all channel depths), then by resolution area (the * product of width and height), then resolution width and finally by refresh * rate. * * @param[in] monitor The monitor to query. * @param[out] count Where to store the number of video modes in the returned * array. This is set to zero if an error occurred. * @return An array of video modes, or `NULL` if an * [error](@ref error_handling) occurred. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref * GLFW_PLATFORM_ERROR. * * @pointer_lifetime The returned array is allocated and freed by GLFW. You * should not free it yourself. It is valid until the specified monitor is * disconnected, this function is called again for that monitor or the library * is terminated. * * @thread_safety This function must only be called from the main thread. * * @sa @ref monitor_modes * @sa @ref glfwGetVideoMode * * @since Added in version 1.0. * @glfw3 Changed to return an array of modes for a specific monitor. * * @ingroup monitor */ GLFWAPI const GLFWvidmode* glfwGetVideoModes(GLFWmonitor* monitor, int* count); /*! @brief Returns the current mode of the specified monitor. * * This function returns the current video mode of the specified monitor. If * you have created a full screen window for that monitor, the return value * will depend on whether that window is iconified. * * @param[in] monitor The monitor to query. * @return The current mode of the monitor, or `NULL` if an * [error](@ref error_handling) occurred. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref * GLFW_PLATFORM_ERROR. * * @pointer_lifetime The returned array is allocated and freed by GLFW. You * should not free it yourself. It is valid until the specified monitor is * disconnected or the library is terminated. * * @thread_safety This function must only be called from the main thread. * * @sa @ref monitor_modes * @sa @ref glfwGetVideoModes * * @since Added in version 3.0. Replaces `glfwGetDesktopMode`. * * @ingroup monitor */ GLFWAPI const GLFWvidmode* glfwGetVideoMode(GLFWmonitor* monitor); /*! @brief Generates a gamma ramp and sets it for the specified monitor. * * This function generates an appropriately sized gamma ramp from the specified * exponent and then calls @ref glfwSetGammaRamp with it. The value must be * a finite number greater than zero. * * The software controlled gamma ramp is applied _in addition_ to the hardware * gamma correction, which today is usually an approximation of sRGB gamma. * This means that setting a perfectly linear ramp, or gamma 1.0, will produce * the default (usually sRGB-like) behavior. * * For gamma correct rendering with OpenGL or OpenGL ES, see the @ref * GLFW_SRGB_CAPABLE hint. * * @param[in] monitor The monitor whose gamma ramp to set. * @param[in] gamma The desired exponent. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref * GLFW_INVALID_VALUE and @ref GLFW_PLATFORM_ERROR. * * @remark @wayland Gamma handling is a privileged protocol, this function * will thus never be implemented and emits @ref GLFW_PLATFORM_ERROR. * * @thread_safety This function must only be called from the main thread. * * @sa @ref monitor_gamma * * @since Added in version 3.0. * * @ingroup monitor */ GLFWAPI void glfwSetGamma(GLFWmonitor* monitor, float gamma); /*! @brief Returns the current gamma ramp for the specified monitor. * * This function returns the current gamma ramp of the specified monitor. * * @param[in] monitor The monitor to query. * @return The current gamma ramp, or `NULL` if an * [error](@ref error_handling) occurred. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref * GLFW_PLATFORM_ERROR. * * @remark @wayland Gamma handling is a privileged protocol, this function * will thus never be implemented and emits @ref GLFW_PLATFORM_ERROR while * returning `NULL`. * * @pointer_lifetime The returned structure and its arrays are allocated and * freed by GLFW. You should not free them yourself. They are valid until the * specified monitor is disconnected, this function is called again for that * monitor or the library is terminated. * * @thread_safety This function must only be called from the main thread. * * @sa @ref monitor_gamma * * @since Added in version 3.0. * * @ingroup monitor */ GLFWAPI const GLFWgammaramp* glfwGetGammaRamp(GLFWmonitor* monitor); /*! @brief Sets the current gamma ramp for the specified monitor. * * This function sets the current gamma ramp for the specified monitor. The * original gamma ramp for that monitor is saved by GLFW the first time this * function is called and is restored by @ref glfwTerminate. * * The software controlled gamma ramp is applied _in addition_ to the hardware * gamma correction, which today is usually an approximation of sRGB gamma. * This means that setting a perfectly linear ramp, or gamma 1.0, will produce * the default (usually sRGB-like) behavior. * * For gamma correct rendering with OpenGL or OpenGL ES, see the @ref * GLFW_SRGB_CAPABLE hint. * * @param[in] monitor The monitor whose gamma ramp to set. * @param[in] ramp The gamma ramp to use. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref * GLFW_PLATFORM_ERROR. * * @remark The size of the specified gamma ramp should match the size of the * current ramp for that monitor. * * @remark @win32 The gamma ramp size must be 256. * * @remark @wayland Gamma handling is a privileged protocol, this function * will thus never be implemented and emits @ref GLFW_PLATFORM_ERROR. * * @pointer_lifetime The specified gamma ramp is copied before this function * returns. * * @thread_safety This function must only be called from the main thread. * * @sa @ref monitor_gamma * * @since Added in version 3.0. * * @ingroup monitor */ GLFWAPI void glfwSetGammaRamp(GLFWmonitor* monitor, const GLFWgammaramp* ramp); /*! @brief Resets all window hints to their default values. * * This function resets all window hints to their * [default values](@ref window_hints_values). * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. * * @thread_safety This function must only be called from the main thread. * * @sa @ref window_hints * @sa @ref glfwWindowHint * @sa @ref glfwWindowHintString * * @since Added in version 3.0. * * @ingroup window */ GLFWAPI void glfwDefaultWindowHints(void); /*! @brief Sets the specified window hint to the desired value. * * This function sets hints for the next call to @ref glfwCreateWindow. The * hints, once set, retain their values until changed by a call to this * function or @ref glfwDefaultWindowHints, or until the library is terminated. * * Only integer value hints can be set with this function. String value hints * are set with @ref glfwWindowHintString. * * This function does not check whether the specified hint values are valid. * If you set hints to invalid values this will instead be reported by the next * call to @ref glfwCreateWindow. * * Some hints are platform specific. These may be set on any platform but they * will only affect their specific platform. Other platforms will ignore them. * Setting these hints requires no platform specific headers or functions. * * @param[in] hint The [window hint](@ref window_hints) to set. * @param[in] value The new value of the window hint. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref * GLFW_INVALID_ENUM. * * @thread_safety This function must only be called from the main thread. * * @sa @ref window_hints * @sa @ref glfwWindowHintString * @sa @ref glfwDefaultWindowHints * * @since Added in version 3.0. Replaces `glfwOpenWindowHint`. * * @ingroup window */ GLFWAPI void glfwWindowHint(int hint, int value); /*! @brief Sets the specified window hint to the desired value. * * This function sets hints for the next call to @ref glfwCreateWindow. The * hints, once set, retain their values until changed by a call to this * function or @ref glfwDefaultWindowHints, or until the library is terminated. * * Only string type hints can be set with this function. Integer value hints * are set with @ref glfwWindowHint. * * This function does not check whether the specified hint values are valid. * If you set hints to invalid values this will instead be reported by the next * call to @ref glfwCreateWindow. * * Some hints are platform specific. These may be set on any platform but they * will only affect their specific platform. Other platforms will ignore them. * Setting these hints requires no platform specific headers or functions. * * @param[in] hint The [window hint](@ref window_hints) to set. * @param[in] value The new value of the window hint. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref * GLFW_INVALID_ENUM. * * @pointer_lifetime The specified string is copied before this function * returns. * * @thread_safety This function must only be called from the main thread. * * @sa @ref window_hints * @sa @ref glfwWindowHint * @sa @ref glfwDefaultWindowHints * * @since Added in version 3.3. * * @ingroup window */ GLFWAPI void glfwWindowHintString(int hint, const char* value); /*! @brief Creates a window and its associated context. * * This function creates a window and its associated OpenGL or OpenGL ES * context. Most of the options controlling how the window and its context * should be created are specified with [window hints](@ref window_hints). * * Successful creation does not change which context is current. Before you * can use the newly created context, you need to * [make it current](@ref context_current). For information about the `share` * parameter, see @ref context_sharing. * * The created window, framebuffer and context may differ from what you * requested, as not all parameters and hints are * [hard constraints](@ref window_hints_hard). This includes the size of the * window, especially for full screen windows. To query the actual attributes * of the created window, framebuffer and context, see @ref * glfwGetWindowAttrib, @ref glfwGetWindowSize and @ref glfwGetFramebufferSize. * * To create a full screen window, you need to specify the monitor the window * will cover. If no monitor is specified, the window will be windowed mode. * Unless you have a way for the user to choose a specific monitor, it is * recommended that you pick the primary monitor. For more information on how * to query connected monitors, see @ref monitor_monitors. * * For full screen windows, the specified size becomes the resolution of the * window's _desired video mode_. As long as a full screen window is not * iconified, the supported video mode most closely matching the desired video * mode is set for the specified monitor. For more information about full * screen windows, including the creation of so called _windowed full screen_ * or _borderless full screen_ windows, see @ref window_windowed_full_screen. * * Once you have created the window, you can switch it between windowed and * full screen mode with @ref glfwSetWindowMonitor. This will not affect its * OpenGL or OpenGL ES context. * * By default, newly created windows use the placement recommended by the * window system. To create the window at a specific position, make it * initially invisible using the [GLFW_VISIBLE](@ref GLFW_VISIBLE_hint) window * hint, set its [position](@ref window_pos) and then [show](@ref window_hide) * it. * * As long as at least one full screen window is not iconified, the screensaver * is prohibited from starting. * * Window systems put limits on window sizes. Very large or very small window * dimensions may be overridden by the window system on creation. Check the * actual [size](@ref window_size) after creation. * * The [swap interval](@ref buffer_swap) is not set during window creation and * the initial value may vary depending on driver settings and defaults. * * @param[in] width The desired width, in screen coordinates, of the window. * This must be greater than zero. * @param[in] height The desired height, in screen coordinates, of the window. * This must be greater than zero. * @param[in] title The initial, UTF-8 encoded window title. * @param[in] monitor The monitor to use for full screen mode, or `NULL` for * windowed mode. * @param[in] share The window whose context to share resources with, or `NULL` * to not share resources. * @return The handle of the created window, or `NULL` if an * [error](@ref error_handling) occurred. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref * GLFW_INVALID_ENUM, @ref GLFW_INVALID_VALUE, @ref GLFW_API_UNAVAILABLE, @ref * GLFW_VERSION_UNAVAILABLE, @ref GLFW_FORMAT_UNAVAILABLE and @ref * GLFW_PLATFORM_ERROR. * * @remark @win32 Window creation will fail if the Microsoft GDI software * OpenGL implementation is the only one available. * * @remark @win32 If the executable has an icon resource named `GLFW_ICON,` it * will be set as the initial icon for the window. If no such icon is present, * the `IDI_APPLICATION` icon will be used instead. To set a different icon, * see @ref glfwSetWindowIcon. * * @remark @win32 The context to share resources with must not be current on * any other thread. * * @remark @macos The OS only supports forward-compatible core profile contexts * for OpenGL versions 3.2 and later. Before creating an OpenGL context of * version 3.2 or later you must set the * [GLFW_OPENGL_FORWARD_COMPAT](@ref GLFW_OPENGL_FORWARD_COMPAT_hint) and * [GLFW_OPENGL_PROFILE](@ref GLFW_OPENGL_PROFILE_hint) hints accordingly. * OpenGL 3.0 and 3.1 contexts are not supported at all on macOS. * * @remark @macos The GLFW window has no icon, as it is not a document * window, but the dock icon will be the same as the application bundle's icon. * For more information on bundles, see the * [Bundle Programming Guide](https://developer.apple.com/library/mac/documentation/CoreFoundation/Conceptual/CFBundles/) * in the Mac Developer Library. * * @remark @macos The first time a window is created the menu bar is created. * If GLFW finds a `MainMenu.nib` it is loaded and assumed to contain a menu * bar. Otherwise a minimal menu bar is created manually with common commands * like Hide, Quit and About. The About entry opens a minimal about dialog * with information from the application's bundle. Menu bar creation can be * disabled entirely with the @ref GLFW_COCOA_MENUBAR init hint. * * @remark @macos On OS X 10.10 and later the window frame will not be rendered * at full resolution on Retina displays unless the * [GLFW_COCOA_RETINA_FRAMEBUFFER](@ref GLFW_COCOA_RETINA_FRAMEBUFFER_hint) * hint is `GLFW_TRUE` and the `NSHighResolutionCapable` key is enabled in the * application bundle's `Info.plist`. For more information, see * [High Resolution Guidelines for OS X](https://developer.apple.com/library/mac/documentation/GraphicsAnimation/Conceptual/HighResolutionOSX/Explained/Explained.html) * in the Mac Developer Library. The GLFW test and example programs use * a custom `Info.plist` template for this, which can be found as * `CMake/MacOSXBundleInfo.plist.in` in the source tree. * * @remark @macos When activating frame autosaving with * [GLFW_COCOA_FRAME_NAME](@ref GLFW_COCOA_FRAME_NAME_hint), the specified * window size and position may be overridden by previously saved values. * * @remark @x11 Some window managers will not respect the placement of * initially hidden windows. * * @remark @x11 Due to the asynchronous nature of X11, it may take a moment for * a window to reach its requested state. This means you may not be able to * query the final size, position or other attributes directly after window * creation. * * @remark @x11 The class part of the `WM_CLASS` window property will by * default be set to the window title passed to this function. The instance * part will use the contents of the `RESOURCE_NAME` environment variable, if * present and not empty, or fall back to the window title. Set the * [GLFW_X11_CLASS_NAME](@ref GLFW_X11_CLASS_NAME_hint) and * [GLFW_X11_INSTANCE_NAME](@ref GLFW_X11_INSTANCE_NAME_hint) window hints to * override this. * * @remark @wayland Compositors should implement the xdg-decoration protocol * for GLFW to decorate the window properly. If this protocol isn't * supported, or if the compositor prefers client-side decorations, a very * simple fallback frame will be drawn using the wp_viewporter protocol. A * compositor can still emit close, maximize or fullscreen events, using for * instance a keybind mechanism. If neither of these protocols is supported, * the window won't be decorated. * * @remark @wayland A full screen window will not attempt to change the mode, * no matter what the requested size or refresh rate. * * @remark @wayland Screensaver inhibition requires the idle-inhibit protocol * to be implemented in the user's compositor. * * @thread_safety This function must only be called from the main thread. * * @sa @ref window_creation * @sa @ref glfwDestroyWindow * * @since Added in version 3.0. Replaces `glfwOpenWindow`. * * @ingroup window */ GLFWAPI GLFWwindow* glfwCreateWindow(int width, int height, const char* title, GLFWmonitor* monitor, GLFWwindow* share); /*! @brief Destroys the specified window and its context. * * This function destroys the specified window and its context. On calling * this function, no further callbacks will be called for that window. * * If the context of the specified window is current on the main thread, it is * detached before being destroyed. * * @param[in] window The window to destroy. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref * GLFW_PLATFORM_ERROR. * * @note The context of the specified window must not be current on any other * thread when this function is called. * * @reentrancy This function must not be called from a callback. * * @thread_safety This function must only be called from the main thread. * * @sa @ref window_creation * @sa @ref glfwCreateWindow * * @since Added in version 3.0. Replaces `glfwCloseWindow`. * * @ingroup window */ GLFWAPI void glfwDestroyWindow(GLFWwindow* window); /*! @brief Checks the close flag of the specified window. * * This function returns the value of the close flag of the specified window. * * @param[in] window The window to query. * @return The value of the close flag. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. * * @thread_safety This function may be called from any thread. Access is not * synchronized. * * @sa @ref window_close * * @since Added in version 3.0. * * @ingroup window */ GLFWAPI int glfwWindowShouldClose(GLFWwindow* window); /*! @brief Sets the close flag of the specified window. * * This function sets the value of the close flag of the specified window. * This can be used to override the user's attempt to close the window, or * to signal that it should be closed. * * @param[in] window The window whose flag to change. * @param[in] value The new value. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. * * @thread_safety This function may be called from any thread. Access is not * synchronized. * * @sa @ref window_close * * @since Added in version 3.0. * * @ingroup window */ GLFWAPI void glfwSetWindowShouldClose(GLFWwindow* window, int value); /*! @brief Sets the title of the specified window. * * This function sets the window title, encoded as UTF-8, of the specified * window. * * @param[in] window The window whose title to change. * @param[in] title The UTF-8 encoded window title. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref * GLFW_PLATFORM_ERROR. * * @remark @macos The window title will not be updated until the next time you * process events. * * @thread_safety This function must only be called from the main thread. * * @sa @ref window_title * * @since Added in version 1.0. * @glfw3 Added window handle parameter. * * @ingroup window */ GLFWAPI void glfwSetWindowTitle(GLFWwindow* window, const char* title); /*! @brief Sets the icon for the specified window. * * This function sets the icon of the specified window. If passed an array of * candidate images, those of or closest to the sizes desired by the system are * selected. If no images are specified, the window reverts to its default * icon. * * The pixels are 32-bit, little-endian, non-premultiplied RGBA, i.e. eight * bits per channel with the red channel first. They are arranged canonically * as packed sequential rows, starting from the top-left corner. * * The desired image sizes varies depending on platform and system settings. * The selected images will be rescaled as needed. Good sizes include 16x16, * 32x32 and 48x48. * * @param[in] window The window whose icon to set. * @param[in] count The number of images in the specified array, or zero to * revert to the default window icon. * @param[in] images The images to create the icon from. This is ignored if * count is zero. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref * GLFW_PLATFORM_ERROR. * * @pointer_lifetime The specified image data is copied before this function * returns. * * @remark @macos The GLFW window has no icon, as it is not a document * window, so this function does nothing. The dock icon will be the same as * the application bundle's icon. For more information on bundles, see the * [Bundle Programming Guide](https://developer.apple.com/library/mac/documentation/CoreFoundation/Conceptual/CFBundles/) * in the Mac Developer Library. * * @remark @wayland There is no existing protocol to change an icon, the * window will thus inherit the one defined in the application's desktop file. * This function always emits @ref GLFW_PLATFORM_ERROR. * * @thread_safety This function must only be called from the main thread. * * @sa @ref window_icon * * @since Added in version 3.2. * * @ingroup window */ GLFWAPI void glfwSetWindowIcon(GLFWwindow* window, int count, const GLFWimage* images); /*! @brief Retrieves the position of the content area of the specified window. * * This function retrieves the position, in screen coordinates, of the * upper-left corner of the content area of the specified window. * * Any or all of the position arguments may be `NULL`. If an error occurs, all * non-`NULL` position arguments will be set to zero. * * @param[in] window The window to query. * @param[out] xpos Where to store the x-coordinate of the upper-left corner of * the content area, or `NULL`. * @param[out] ypos Where to store the y-coordinate of the upper-left corner of * the content area, or `NULL`. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref * GLFW_PLATFORM_ERROR. * * @remark @wayland There is no way for an application to retrieve the global * position of its windows, this function will always emit @ref * GLFW_PLATFORM_ERROR. * * @thread_safety This function must only be called from the main thread. * * @sa @ref window_pos * @sa @ref glfwSetWindowPos * * @since Added in version 3.0. * * @ingroup window */ GLFWAPI void glfwGetWindowPos(GLFWwindow* window, int* xpos, int* ypos); /*! @brief Sets the position of the content area of the specified window. * * This function sets the position, in screen coordinates, of the upper-left * corner of the content area of the specified windowed mode window. If the * window is a full screen window, this function does nothing. * * __Do not use this function__ to move an already visible window unless you * have very good reasons for doing so, as it will confuse and annoy the user. * * The window manager may put limits on what positions are allowed. GLFW * cannot and should not override these limits. * * @param[in] window The window to query. * @param[in] xpos The x-coordinate of the upper-left corner of the content area. * @param[in] ypos The y-coordinate of the upper-left corner of the content area. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref * GLFW_PLATFORM_ERROR. * * @remark @wayland There is no way for an application to set the global * position of its windows, this function will always emit @ref * GLFW_PLATFORM_ERROR. * * @thread_safety This function must only be called from the main thread. * * @sa @ref window_pos * @sa @ref glfwGetWindowPos * * @since Added in version 1.0. * @glfw3 Added window handle parameter. * * @ingroup window */ GLFWAPI void glfwSetWindowPos(GLFWwindow* window, int xpos, int ypos); /*! @brief Retrieves the size of the content area of the specified window. * * This function retrieves the size, in screen coordinates, of the content area * of the specified window. If you wish to retrieve the size of the * framebuffer of the window in pixels, see @ref glfwGetFramebufferSize. * * Any or all of the size arguments may be `NULL`. If an error occurs, all * non-`NULL` size arguments will be set to zero. * * @param[in] window The window whose size to retrieve. * @param[out] width Where to store the width, in screen coordinates, of the * content area, or `NULL`. * @param[out] height Where to store the height, in screen coordinates, of the * content area, or `NULL`. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref * GLFW_PLATFORM_ERROR. * * @thread_safety This function must only be called from the main thread. * * @sa @ref window_size * @sa @ref glfwSetWindowSize * * @since Added in version 1.0. * @glfw3 Added window handle parameter. * * @ingroup window */ GLFWAPI void glfwGetWindowSize(GLFWwindow* window, int* width, int* height); /*! @brief Sets the size limits of the specified window. * * This function sets the size limits of the content area of the specified * window. If the window is full screen, the size limits only take effect * once it is made windowed. If the window is not resizable, this function * does nothing. * * The size limits are applied immediately to a windowed mode window and may * cause it to be resized. * * The maximum dimensions must be greater than or equal to the minimum * dimensions and all must be greater than or equal to zero. * * @param[in] window The window to set limits for. * @param[in] minwidth The minimum width, in screen coordinates, of the content * area, or `GLFW_DONT_CARE`. * @param[in] minheight The minimum height, in screen coordinates, of the * content area, or `GLFW_DONT_CARE`. * @param[in] maxwidth The maximum width, in screen coordinates, of the content * area, or `GLFW_DONT_CARE`. * @param[in] maxheight The maximum height, in screen coordinates, of the * content area, or `GLFW_DONT_CARE`. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref * GLFW_INVALID_VALUE and @ref GLFW_PLATFORM_ERROR. * * @remark If you set size limits and an aspect ratio that conflict, the * results are undefined. * * @remark @wayland The size limits will not be applied until the window is * actually resized, either by the user or by the compositor. * * @thread_safety This function must only be called from the main thread. * * @sa @ref window_sizelimits * @sa @ref glfwSetWindowAspectRatio * * @since Added in version 3.2. * * @ingroup window */ GLFWAPI void glfwSetWindowSizeLimits(GLFWwindow* window, int minwidth, int minheight, int maxwidth, int maxheight); /*! @brief Sets the aspect ratio of the specified window. * * This function sets the required aspect ratio of the content area of the * specified window. If the window is full screen, the aspect ratio only takes * effect once it is made windowed. If the window is not resizable, this * function does nothing. * * The aspect ratio is specified as a numerator and a denominator and both * values must be greater than zero. For example, the common 16:9 aspect ratio * is specified as 16 and 9, respectively. * * If the numerator and denominator is set to `GLFW_DONT_CARE` then the aspect * ratio limit is disabled. * * The aspect ratio is applied immediately to a windowed mode window and may * cause it to be resized. * * @param[in] window The window to set limits for. * @param[in] numer The numerator of the desired aspect ratio, or * `GLFW_DONT_CARE`. * @param[in] denom The denominator of the desired aspect ratio, or * `GLFW_DONT_CARE`. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref * GLFW_INVALID_VALUE and @ref GLFW_PLATFORM_ERROR. * * @remark If you set size limits and an aspect ratio that conflict, the * results are undefined. * * @remark @wayland The aspect ratio will not be applied until the window is * actually resized, either by the user or by the compositor. * * @thread_safety This function must only be called from the main thread. * * @sa @ref window_sizelimits * @sa @ref glfwSetWindowSizeLimits * * @since Added in version 3.2. * * @ingroup window */ GLFWAPI void glfwSetWindowAspectRatio(GLFWwindow* window, int numer, int denom); /*! @brief Sets the size of the content area of the specified window. * * This function sets the size, in screen coordinates, of the content area of * the specified window. * * For full screen windows, this function updates the resolution of its desired * video mode and switches to the video mode closest to it, without affecting * the window's context. As the context is unaffected, the bit depths of the * framebuffer remain unchanged. * * If you wish to update the refresh rate of the desired video mode in addition * to its resolution, see @ref glfwSetWindowMonitor. * * The window manager may put limits on what sizes are allowed. GLFW cannot * and should not override these limits. * * @param[in] window The window to resize. * @param[in] width The desired width, in screen coordinates, of the window * content area. * @param[in] height The desired height, in screen coordinates, of the window * content area. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref * GLFW_PLATFORM_ERROR. * * @remark @wayland A full screen window will not attempt to change the mode, * no matter what the requested size. * * @thread_safety This function must only be called from the main thread. * * @sa @ref window_size * @sa @ref glfwGetWindowSize * @sa @ref glfwSetWindowMonitor * * @since Added in version 1.0. * @glfw3 Added window handle parameter. * * @ingroup window */ GLFWAPI void glfwSetWindowSize(GLFWwindow* window, int width, int height); /*! @brief Retrieves the size of the framebuffer of the specified window. * * This function retrieves the size, in pixels, of the framebuffer of the * specified window. If you wish to retrieve the size of the window in screen * coordinates, see @ref glfwGetWindowSize. * * Any or all of the size arguments may be `NULL`. If an error occurs, all * non-`NULL` size arguments will be set to zero. * * @param[in] window The window whose framebuffer to query. * @param[out] width Where to store the width, in pixels, of the framebuffer, * or `NULL`. * @param[out] height Where to store the height, in pixels, of the framebuffer, * or `NULL`. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref * GLFW_PLATFORM_ERROR. * * @thread_safety This function must only be called from the main thread. * * @sa @ref window_fbsize * @sa @ref glfwSetFramebufferSizeCallback * * @since Added in version 3.0. * * @ingroup window */ GLFWAPI void glfwGetFramebufferSize(GLFWwindow* window, int* width, int* height); /*! @brief Retrieves the size of the frame of the window. * * This function retrieves the size, in screen coordinates, of each edge of the * frame of the specified window. This size includes the title bar, if the * window has one. The size of the frame may vary depending on the * [window-related hints](@ref window_hints_wnd) used to create it. * * Because this function retrieves the size of each window frame edge and not * the offset along a particular coordinate axis, the retrieved values will * always be zero or positive. * * Any or all of the size arguments may be `NULL`. If an error occurs, all * non-`NULL` size arguments will be set to zero. * * @param[in] window The window whose frame size to query. * @param[out] left Where to store the size, in screen coordinates, of the left * edge of the window frame, or `NULL`. * @param[out] top Where to store the size, in screen coordinates, of the top * edge of the window frame, or `NULL`. * @param[out] right Where to store the size, in screen coordinates, of the * right edge of the window frame, or `NULL`. * @param[out] bottom Where to store the size, in screen coordinates, of the * bottom edge of the window frame, or `NULL`. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref * GLFW_PLATFORM_ERROR. * * @thread_safety This function must only be called from the main thread. * * @sa @ref window_size * * @since Added in version 3.1. * * @ingroup window */ GLFWAPI void glfwGetWindowFrameSize(GLFWwindow* window, int* left, int* top, int* right, int* bottom); /*! @brief Retrieves the content scale for the specified window. * * This function retrieves the content scale for the specified window. The * content scale is the ratio between the current DPI and the platform's * default DPI. This is especially important for text and any UI elements. If * the pixel dimensions of your UI scaled by this look appropriate on your * machine then it should appear at a reasonable size on other machines * regardless of their DPI and scaling settings. This relies on the system DPI * and scaling settings being somewhat correct. * * On systems where each monitors can have its own content scale, the window * content scale will depend on which monitor the system considers the window * to be on. * * @param[in] window The window to query. * @param[out] xscale Where to store the x-axis content scale, or `NULL`. * @param[out] yscale Where to store the y-axis content scale, or `NULL`. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref * GLFW_PLATFORM_ERROR. * * @thread_safety This function must only be called from the main thread. * * @sa @ref window_scale * @sa @ref glfwSetWindowContentScaleCallback * @sa @ref glfwGetMonitorContentScale * * @since Added in version 3.3. * * @ingroup window */ GLFWAPI void glfwGetWindowContentScale(GLFWwindow* window, float* xscale, float* yscale); /*! @brief Returns the opacity of the whole window. * * This function returns the opacity of the window, including any decorations. * * The opacity (or alpha) value is a positive finite number between zero and * one, where zero is fully transparent and one is fully opaque. If the system * does not support whole window transparency, this function always returns one. * * The initial opacity value for newly created windows is one. * * @param[in] window The window to query. * @return The opacity value of the specified window. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref * GLFW_PLATFORM_ERROR. * * @thread_safety This function must only be called from the main thread. * * @sa @ref window_transparency * @sa @ref glfwSetWindowOpacity * * @since Added in version 3.3. * * @ingroup window */ GLFWAPI float glfwGetWindowOpacity(GLFWwindow* window); /*! @brief Sets the opacity of the whole window. * * This function sets the opacity of the window, including any decorations. * * The opacity (or alpha) value is a positive finite number between zero and * one, where zero is fully transparent and one is fully opaque. * * The initial opacity value for newly created windows is one. * * A window created with framebuffer transparency may not use whole window * transparency. The results of doing this are undefined. * * @param[in] window The window to set the opacity for. * @param[in] opacity The desired opacity of the specified window. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref * GLFW_PLATFORM_ERROR. * * @thread_safety This function must only be called from the main thread. * * @sa @ref window_transparency * @sa @ref glfwGetWindowOpacity * * @since Added in version 3.3. * * @ingroup window */ GLFWAPI void glfwSetWindowOpacity(GLFWwindow* window, float opacity); /*! @brief Iconifies the specified window. * * This function iconifies (minimizes) the specified window if it was * previously restored. If the window is already iconified, this function does * nothing. * * If the specified window is a full screen window, the original monitor * resolution is restored until the window is restored. * * @param[in] window The window to iconify. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref * GLFW_PLATFORM_ERROR. * * @remark @wayland There is no concept of iconification in wl_shell, this * function will emit @ref GLFW_PLATFORM_ERROR when using this deprecated * protocol. * * @thread_safety This function must only be called from the main thread. * * @sa @ref window_iconify * @sa @ref glfwRestoreWindow * @sa @ref glfwMaximizeWindow * * @since Added in version 2.1. * @glfw3 Added window handle parameter. * * @ingroup window */ GLFWAPI void glfwIconifyWindow(GLFWwindow* window); /*! @brief Restores the specified window. * * This function restores the specified window if it was previously iconified * (minimized) or maximized. If the window is already restored, this function * does nothing. * * If the specified window is a full screen window, the resolution chosen for * the window is restored on the selected monitor. * * @param[in] window The window to restore. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref * GLFW_PLATFORM_ERROR. * * @thread_safety This function must only be called from the main thread. * * @sa @ref window_iconify * @sa @ref glfwIconifyWindow * @sa @ref glfwMaximizeWindow * * @since Added in version 2.1. * @glfw3 Added window handle parameter. * * @ingroup window */ GLFWAPI void glfwRestoreWindow(GLFWwindow* window); /*! @brief Maximizes the specified window. * * This function maximizes the specified window if it was previously not * maximized. If the window is already maximized, this function does nothing. * * If the specified window is a full screen window, this function does nothing. * * @param[in] window The window to maximize. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref * GLFW_PLATFORM_ERROR. * * @par Thread Safety * This function may only be called from the main thread. * * @sa @ref window_iconify * @sa @ref glfwIconifyWindow * @sa @ref glfwRestoreWindow * * @since Added in GLFW 3.2. * * @ingroup window */ GLFWAPI void glfwMaximizeWindow(GLFWwindow* window); /*! @brief Makes the specified window visible. * * This function makes the specified window visible if it was previously * hidden. If the window is already visible or is in full screen mode, this * function does nothing. * * By default, windowed mode windows are focused when shown * Set the [GLFW_FOCUS_ON_SHOW](@ref GLFW_FOCUS_ON_SHOW_hint) window hint * to change this behavior for all newly created windows, or change the * behavior for an existing window with @ref glfwSetWindowAttrib. * * @param[in] window The window to make visible. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref * GLFW_PLATFORM_ERROR. * * @remark @wayland Because Wayland wants every frame of the desktop to be * complete, this function does not immediately make the window visible. * Instead it will become visible the next time the window framebuffer is * updated after this call. * * @thread_safety This function must only be called from the main thread. * * @sa @ref window_hide * @sa @ref glfwHideWindow * * @since Added in version 3.0. * * @ingroup window */ GLFWAPI void glfwShowWindow(GLFWwindow* window); /*! @brief Hides the specified window. * * This function hides the specified window if it was previously visible. If * the window is already hidden or is in full screen mode, this function does * nothing. * * @param[in] window The window to hide. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref * GLFW_PLATFORM_ERROR. * * @thread_safety This function must only be called from the main thread. * * @sa @ref window_hide * @sa @ref glfwShowWindow * * @since Added in version 3.0. * * @ingroup window */ GLFWAPI void glfwHideWindow(GLFWwindow* window); /*! @brief Brings the specified window to front and sets input focus. * * This function brings the specified window to front and sets input focus. * The window should already be visible and not iconified. * * By default, both windowed and full screen mode windows are focused when * initially created. Set the [GLFW_FOCUSED](@ref GLFW_FOCUSED_hint) to * disable this behavior. * * Also by default, windowed mode windows are focused when shown * with @ref glfwShowWindow. Set the * [GLFW_FOCUS_ON_SHOW](@ref GLFW_FOCUS_ON_SHOW_hint) to disable this behavior. * * __Do not use this function__ to steal focus from other applications unless * you are certain that is what the user wants. Focus stealing can be * extremely disruptive. * * For a less disruptive way of getting the user's attention, see * [attention requests](@ref window_attention). * * @param[in] window The window to give input focus. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref * GLFW_PLATFORM_ERROR. * * @remark @wayland It is not possible for an application to bring its windows * to front, this function will always emit @ref GLFW_PLATFORM_ERROR. * * @thread_safety This function must only be called from the main thread. * * @sa @ref window_focus * @sa @ref window_attention * * @since Added in version 3.2. * * @ingroup window */ GLFWAPI void glfwFocusWindow(GLFWwindow* window); /*! @brief Requests user attention to the specified window. * * This function requests user attention to the specified window. On * platforms where this is not supported, attention is requested to the * application as a whole. * * Once the user has given attention, usually by focusing the window or * application, the system will end the request automatically. * * @param[in] window The window to request attention to. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref * GLFW_PLATFORM_ERROR. * * @remark @macos Attention is requested to the application as a whole, not the * specific window. * * @thread_safety This function must only be called from the main thread. * * @sa @ref window_attention * * @since Added in version 3.3. * * @ingroup window */ GLFWAPI void glfwRequestWindowAttention(GLFWwindow* window); /*! @brief Returns the monitor that the window uses for full screen mode. * * This function returns the handle of the monitor that the specified window is * in full screen on. * * @param[in] window The window to query. * @return The monitor, or `NULL` if the window is in windowed mode or an * [error](@ref error_handling) occurred. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. * * @thread_safety This function must only be called from the main thread. * * @sa @ref window_monitor * @sa @ref glfwSetWindowMonitor * * @since Added in version 3.0. * * @ingroup window */ GLFWAPI GLFWmonitor* glfwGetWindowMonitor(GLFWwindow* window); /*! @brief Sets the mode, monitor, video mode and placement of a window. * * This function sets the monitor that the window uses for full screen mode or, * if the monitor is `NULL`, makes it windowed mode. * * When setting a monitor, this function updates the width, height and refresh * rate of the desired video mode and switches to the video mode closest to it. * The window position is ignored when setting a monitor. * * When the monitor is `NULL`, the position, width and height are used to * place the window content area. The refresh rate is ignored when no monitor * is specified. * * If you only wish to update the resolution of a full screen window or the * size of a windowed mode window, see @ref glfwSetWindowSize. * * When a window transitions from full screen to windowed mode, this function * restores any previous window settings such as whether it is decorated, * floating, resizable, has size or aspect ratio limits, etc. * * @param[in] window The window whose monitor, size or video mode to set. * @param[in] monitor The desired monitor, or `NULL` to set windowed mode. * @param[in] xpos The desired x-coordinate of the upper-left corner of the * content area. * @param[in] ypos The desired y-coordinate of the upper-left corner of the * content area. * @param[in] width The desired with, in screen coordinates, of the content * area or video mode. * @param[in] height The desired height, in screen coordinates, of the content * area or video mode. * @param[in] refreshRate The desired refresh rate, in Hz, of the video mode, * or `GLFW_DONT_CARE`. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref * GLFW_PLATFORM_ERROR. * * @remark The OpenGL or OpenGL ES context will not be destroyed or otherwise * affected by any resizing or mode switching, although you may need to update * your viewport if the framebuffer size has changed. * * @remark @wayland The desired window position is ignored, as there is no way * for an application to set this property. * * @remark @wayland Setting the window to full screen will not attempt to * change the mode, no matter what the requested size or refresh rate. * * @thread_safety This function must only be called from the main thread. * * @sa @ref window_monitor * @sa @ref window_full_screen * @sa @ref glfwGetWindowMonitor * @sa @ref glfwSetWindowSize * * @since Added in version 3.2. * * @ingroup window */ GLFWAPI void glfwSetWindowMonitor(GLFWwindow* window, GLFWmonitor* monitor, int xpos, int ypos, int width, int height, int refreshRate); /*! @brief Returns an attribute of the specified window. * * This function returns the value of an attribute of the specified window or * its OpenGL or OpenGL ES context. * * @param[in] window The window to query. * @param[in] attrib The [window attribute](@ref window_attribs) whose value to * return. * @return The value of the attribute, or zero if an * [error](@ref error_handling) occurred. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref * GLFW_INVALID_ENUM and @ref GLFW_PLATFORM_ERROR. * * @remark Framebuffer related hints are not window attributes. See @ref * window_attribs_fb for more information. * * @remark Zero is a valid value for many window and context related * attributes so you cannot use a return value of zero as an indication of * errors. However, this function should not fail as long as it is passed * valid arguments and the library has been [initialized](@ref intro_init). * * @thread_safety This function must only be called from the main thread. * * @sa @ref window_attribs * @sa @ref glfwSetWindowAttrib * * @since Added in version 3.0. Replaces `glfwGetWindowParam` and * `glfwGetGLVersion`. * * @ingroup window */ GLFWAPI int glfwGetWindowAttrib(GLFWwindow* window, int attrib); /*! @brief Sets an attribute of the specified window. * * This function sets the value of an attribute of the specified window. * * The supported attributes are [GLFW_DECORATED](@ref GLFW_DECORATED_attrib), * [GLFW_RESIZABLE](@ref GLFW_RESIZABLE_attrib), * [GLFW_FLOATING](@ref GLFW_FLOATING_attrib), * [GLFW_AUTO_ICONIFY](@ref GLFW_AUTO_ICONIFY_attrib) and * [GLFW_FOCUS_ON_SHOW](@ref GLFW_FOCUS_ON_SHOW_attrib). * * Some of these attributes are ignored for full screen windows. The new * value will take effect if the window is later made windowed. * * Some of these attributes are ignored for windowed mode windows. The new * value will take effect if the window is later made full screen. * * @param[in] window The window to set the attribute for. * @param[in] attrib A supported window attribute. * @param[in] value `GLFW_TRUE` or `GLFW_FALSE`. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref * GLFW_INVALID_ENUM, @ref GLFW_INVALID_VALUE and @ref GLFW_PLATFORM_ERROR. * * @remark Calling @ref glfwGetWindowAttrib will always return the latest * value, even if that value is ignored by the current mode of the window. * * @thread_safety This function must only be called from the main thread. * * @sa @ref window_attribs * @sa @ref glfwGetWindowAttrib * * @since Added in version 3.3. * * @ingroup window */ GLFWAPI void glfwSetWindowAttrib(GLFWwindow* window, int attrib, int value); /*! @brief Sets the user pointer of the specified window. * * This function sets the user-defined pointer of the specified window. The * current value is retained until the window is destroyed. The initial value * is `NULL`. * * @param[in] window The window whose pointer to set. * @param[in] pointer The new value. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. * * @thread_safety This function may be called from any thread. Access is not * synchronized. * * @sa @ref window_userptr * @sa @ref glfwGetWindowUserPointer * * @since Added in version 3.0. * * @ingroup window */ GLFWAPI void glfwSetWindowUserPointer(GLFWwindow* window, void* pointer); /*! @brief Returns the user pointer of the specified window. * * This function returns the current value of the user-defined pointer of the * specified window. The initial value is `NULL`. * * @param[in] window The window whose pointer to return. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. * * @thread_safety This function may be called from any thread. Access is not * synchronized. * * @sa @ref window_userptr * @sa @ref glfwSetWindowUserPointer * * @since Added in version 3.0. * * @ingroup window */ GLFWAPI void* glfwGetWindowUserPointer(GLFWwindow* window); /*! @brief Sets the position callback for the specified window. * * This function sets the position callback of the specified window, which is * called when the window is moved. The callback is provided with the * position, in screen coordinates, of the upper-left corner of the content * area of the window. * * @param[in] window The window whose callback to set. * @param[in] callback The new callback, or `NULL` to remove the currently set * callback. * @return The previously set callback, or `NULL` if no callback was set or the * library had not been [initialized](@ref intro_init). * * @callback_signature * @code * void function_name(GLFWwindow* window, int xpos, int ypos) * @endcode * For more information about the callback parameters, see the * [function pointer type](@ref GLFWwindowposfun). * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. * * @remark @wayland This callback will never be called, as there is no way for * an application to know its global position. * * @thread_safety This function must only be called from the main thread. * * @sa @ref window_pos * * @since Added in version 3.0. * * @ingroup window */ GLFWAPI GLFWwindowposfun glfwSetWindowPosCallback(GLFWwindow* window, GLFWwindowposfun callback); /*! @brief Sets the size callback for the specified window. * * This function sets the size callback of the specified window, which is * called when the window is resized. The callback is provided with the size, * in screen coordinates, of the content area of the window. * * @param[in] window The window whose callback to set. * @param[in] callback The new callback, or `NULL` to remove the currently set * callback. * @return The previously set callback, or `NULL` if no callback was set or the * library had not been [initialized](@ref intro_init). * * @callback_signature * @code * void function_name(GLFWwindow* window, int width, int height) * @endcode * For more information about the callback parameters, see the * [function pointer type](@ref GLFWwindowsizefun). * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. * * @thread_safety This function must only be called from the main thread. * * @sa @ref window_size * * @since Added in version 1.0. * @glfw3 Added window handle parameter and return value. * * @ingroup window */ GLFWAPI GLFWwindowsizefun glfwSetWindowSizeCallback(GLFWwindow* window, GLFWwindowsizefun callback); /*! @brief Sets the close callback for the specified window. * * This function sets the close callback of the specified window, which is * called when the user attempts to close the window, for example by clicking * the close widget in the title bar. * * The close flag is set before this callback is called, but you can modify it * at any time with @ref glfwSetWindowShouldClose. * * The close callback is not triggered by @ref glfwDestroyWindow. * * @param[in] window The window whose callback to set. * @param[in] callback The new callback, or `NULL` to remove the currently set * callback. * @return The previously set callback, or `NULL` if no callback was set or the * library had not been [initialized](@ref intro_init). * * @callback_signature * @code * void function_name(GLFWwindow* window) * @endcode * For more information about the callback parameters, see the * [function pointer type](@ref GLFWwindowclosefun). * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. * * @remark @macos Selecting Quit from the application menu will trigger the * close callback for all windows. * * @thread_safety This function must only be called from the main thread. * * @sa @ref window_close * * @since Added in version 2.5. * @glfw3 Added window handle parameter and return value. * * @ingroup window */ GLFWAPI GLFWwindowclosefun glfwSetWindowCloseCallback(GLFWwindow* window, GLFWwindowclosefun callback); /*! @brief Sets the refresh callback for the specified window. * * This function sets the refresh callback of the specified window, which is * called when the content area of the window needs to be redrawn, for example * if the window has been exposed after having been covered by another window. * * On compositing window systems such as Aero, Compiz, Aqua or Wayland, where * the window contents are saved off-screen, this callback may be called only * very infrequently or never at all. * * @param[in] window The window whose callback to set. * @param[in] callback The new callback, or `NULL` to remove the currently set * callback. * @return The previously set callback, or `NULL` if no callback was set or the * library had not been [initialized](@ref intro_init). * * @callback_signature * @code * void function_name(GLFWwindow* window); * @endcode * For more information about the callback parameters, see the * [function pointer type](@ref GLFWwindowrefreshfun). * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. * * @thread_safety This function must only be called from the main thread. * * @sa @ref window_refresh * * @since Added in version 2.5. * @glfw3 Added window handle parameter and return value. * * @ingroup window */ GLFWAPI GLFWwindowrefreshfun glfwSetWindowRefreshCallback(GLFWwindow* window, GLFWwindowrefreshfun callback); /*! @brief Sets the focus callback for the specified window. * * This function sets the focus callback of the specified window, which is * called when the window gains or loses input focus. * * After the focus callback is called for a window that lost input focus, * synthetic key and mouse button release events will be generated for all such * that had been pressed. For more information, see @ref glfwSetKeyCallback * and @ref glfwSetMouseButtonCallback. * * @param[in] window The window whose callback to set. * @param[in] callback The new callback, or `NULL` to remove the currently set * callback. * @return The previously set callback, or `NULL` if no callback was set or the * library had not been [initialized](@ref intro_init). * * @callback_signature * @code * void function_name(GLFWwindow* window, int focused) * @endcode * For more information about the callback parameters, see the * [function pointer type](@ref GLFWwindowfocusfun). * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. * * @thread_safety This function must only be called from the main thread. * * @sa @ref window_focus * * @since Added in version 3.0. * * @ingroup window */ GLFWAPI GLFWwindowfocusfun glfwSetWindowFocusCallback(GLFWwindow* window, GLFWwindowfocusfun callback); /*! @brief Sets the iconify callback for the specified window. * * This function sets the iconification callback of the specified window, which * is called when the window is iconified or restored. * * @param[in] window The window whose callback to set. * @param[in] callback The new callback, or `NULL` to remove the currently set * callback. * @return The previously set callback, or `NULL` if no callback was set or the * library had not been [initialized](@ref intro_init). * * @callback_signature * @code * void function_name(GLFWwindow* window, int iconified) * @endcode * For more information about the callback parameters, see the * [function pointer type](@ref GLFWwindowiconifyfun). * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. * * @remark @wayland The wl_shell protocol has no concept of iconification, * this callback will never be called when using this deprecated protocol. * * @thread_safety This function must only be called from the main thread. * * @sa @ref window_iconify * * @since Added in version 3.0. * * @ingroup window */ GLFWAPI GLFWwindowiconifyfun glfwSetWindowIconifyCallback(GLFWwindow* window, GLFWwindowiconifyfun callback); /*! @brief Sets the maximize callback for the specified window. * * This function sets the maximization callback of the specified window, which * is called when the window is maximized or restored. * * @param[in] window The window whose callback to set. * @param[in] callback The new callback, or `NULL` to remove the currently set * callback. * @return The previously set callback, or `NULL` if no callback was set or the * library had not been [initialized](@ref intro_init). * * @callback_signature * @code * void function_name(GLFWwindow* window, int maximized) * @endcode * For more information about the callback parameters, see the * [function pointer type](@ref GLFWwindowmaximizefun). * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. * * @thread_safety This function must only be called from the main thread. * * @sa @ref window_maximize * * @since Added in version 3.3. * * @ingroup window */ GLFWAPI GLFWwindowmaximizefun glfwSetWindowMaximizeCallback(GLFWwindow* window, GLFWwindowmaximizefun callback); /*! @brief Sets the framebuffer resize callback for the specified window. * * This function sets the framebuffer resize callback of the specified window, * which is called when the framebuffer of the specified window is resized. * * @param[in] window The window whose callback to set. * @param[in] callback The new callback, or `NULL` to remove the currently set * callback. * @return The previously set callback, or `NULL` if no callback was set or the * library had not been [initialized](@ref intro_init). * * @callback_signature * @code * void function_name(GLFWwindow* window, int width, int height) * @endcode * For more information about the callback parameters, see the * [function pointer type](@ref GLFWframebuffersizefun). * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. * * @thread_safety This function must only be called from the main thread. * * @sa @ref window_fbsize * * @since Added in version 3.0. * * @ingroup window */ GLFWAPI GLFWframebuffersizefun glfwSetFramebufferSizeCallback(GLFWwindow* window, GLFWframebuffersizefun callback); /*! @brief Sets the window content scale callback for the specified window. * * This function sets the window content scale callback of the specified window, * which is called when the content scale of the specified window changes. * * @param[in] window The window whose callback to set. * @param[in] callback The new callback, or `NULL` to remove the currently set * callback. * @return The previously set callback, or `NULL` if no callback was set or the * library had not been [initialized](@ref intro_init). * * @callback_signature * @code * void function_name(GLFWwindow* window, float xscale, float yscale) * @endcode * For more information about the callback parameters, see the * [function pointer type](@ref GLFWwindowcontentscalefun). * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. * * @thread_safety This function must only be called from the main thread. * * @sa @ref window_scale * @sa @ref glfwGetWindowContentScale * * @since Added in version 3.3. * * @ingroup window */ GLFWAPI GLFWwindowcontentscalefun glfwSetWindowContentScaleCallback(GLFWwindow* window, GLFWwindowcontentscalefun callback); /*! @brief Processes all pending events. * * This function processes only those events that are already in the event * queue and then returns immediately. Processing events will cause the window * and input callbacks associated with those events to be called. * * On some platforms, a window move, resize or menu operation will cause event * processing to block. This is due to how event processing is designed on * those platforms. You can use the * [window refresh callback](@ref window_refresh) to redraw the contents of * your window when necessary during such operations. * * Do not assume that callbacks you set will _only_ be called in response to * event processing functions like this one. While it is necessary to poll for * events, window systems that require GLFW to register callbacks of its own * can pass events to GLFW in response to many window system function calls. * GLFW will pass those events on to the application callbacks before * returning. * * Event processing is not required for joystick input to work. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref * GLFW_PLATFORM_ERROR. * * @reentrancy This function must not be called from a callback. * * @thread_safety This function must only be called from the main thread. * * @sa @ref events * @sa @ref glfwWaitEvents * @sa @ref glfwWaitEventsTimeout * * @since Added in version 1.0. * * @ingroup window */ GLFWAPI void glfwPollEvents(void); /*! @brief Waits until events are queued and processes them. * * This function puts the calling thread to sleep until at least one event is * available in the event queue. Once one or more events are available, * it behaves exactly like @ref glfwPollEvents, i.e. the events in the queue * are processed and the function then returns immediately. Processing events * will cause the window and input callbacks associated with those events to be * called. * * Since not all events are associated with callbacks, this function may return * without a callback having been called even if you are monitoring all * callbacks. * * On some platforms, a window move, resize or menu operation will cause event * processing to block. This is due to how event processing is designed on * those platforms. You can use the * [window refresh callback](@ref window_refresh) to redraw the contents of * your window when necessary during such operations. * * Do not assume that callbacks you set will _only_ be called in response to * event processing functions like this one. While it is necessary to poll for * events, window systems that require GLFW to register callbacks of its own * can pass events to GLFW in response to many window system function calls. * GLFW will pass those events on to the application callbacks before * returning. * * Event processing is not required for joystick input to work. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref * GLFW_PLATFORM_ERROR. * * @reentrancy This function must not be called from a callback. * * @thread_safety This function must only be called from the main thread. * * @sa @ref events * @sa @ref glfwPollEvents * @sa @ref glfwWaitEventsTimeout * * @since Added in version 2.5. * * @ingroup window */ GLFWAPI void glfwWaitEvents(void); /*! @brief Waits with timeout until events are queued and processes them. * * This function puts the calling thread to sleep until at least one event is * available in the event queue, or until the specified timeout is reached. If * one or more events are available, it behaves exactly like @ref * glfwPollEvents, i.e. the events in the queue are processed and the function * then returns immediately. Processing events will cause the window and input * callbacks associated with those events to be called. * * The timeout value must be a positive finite number. * * Since not all events are associated with callbacks, this function may return * without a callback having been called even if you are monitoring all * callbacks. * * On some platforms, a window move, resize or menu operation will cause event * processing to block. This is due to how event processing is designed on * those platforms. You can use the * [window refresh callback](@ref window_refresh) to redraw the contents of * your window when necessary during such operations. * * Do not assume that callbacks you set will _only_ be called in response to * event processing functions like this one. While it is necessary to poll for * events, window systems that require GLFW to register callbacks of its own * can pass events to GLFW in response to many window system function calls. * GLFW will pass those events on to the application callbacks before * returning. * * Event processing is not required for joystick input to work. * * @param[in] timeout The maximum amount of time, in seconds, to wait. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref * GLFW_INVALID_VALUE and @ref GLFW_PLATFORM_ERROR. * * @reentrancy This function must not be called from a callback. * * @thread_safety This function must only be called from the main thread. * * @sa @ref events * @sa @ref glfwPollEvents * @sa @ref glfwWaitEvents * * @since Added in version 3.2. * * @ingroup window */ GLFWAPI void glfwWaitEventsTimeout(double timeout); /*! @brief Posts an empty event to the event queue. * * This function posts an empty event from the current thread to the event * queue, causing @ref glfwWaitEvents or @ref glfwWaitEventsTimeout to return. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref * GLFW_PLATFORM_ERROR. * * @thread_safety This function may be called from any thread. * * @sa @ref events * @sa @ref glfwWaitEvents * @sa @ref glfwWaitEventsTimeout * * @since Added in version 3.1. * * @ingroup window */ GLFWAPI void glfwPostEmptyEvent(void); /*! @brief Returns the value of an input option for the specified window. * * This function returns the value of an input option for the specified window. * The mode must be one of @ref GLFW_CURSOR, @ref GLFW_STICKY_KEYS, * @ref GLFW_STICKY_MOUSE_BUTTONS, @ref GLFW_LOCK_KEY_MODS or * @ref GLFW_RAW_MOUSE_MOTION. * * @param[in] window The window to query. * @param[in] mode One of `GLFW_CURSOR`, `GLFW_STICKY_KEYS`, * `GLFW_STICKY_MOUSE_BUTTONS`, `GLFW_LOCK_KEY_MODS` or * `GLFW_RAW_MOUSE_MOTION`. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref * GLFW_INVALID_ENUM. * * @thread_safety This function must only be called from the main thread. * * @sa @ref glfwSetInputMode * * @since Added in version 3.0. * * @ingroup input */ GLFWAPI int glfwGetInputMode(GLFWwindow* window, int mode); /*! @brief Sets an input option for the specified window. * * This function sets an input mode option for the specified window. The mode * must be one of @ref GLFW_CURSOR, @ref GLFW_STICKY_KEYS, * @ref GLFW_STICKY_MOUSE_BUTTONS, @ref GLFW_LOCK_KEY_MODS or * @ref GLFW_RAW_MOUSE_MOTION. * * If the mode is `GLFW_CURSOR`, the value must be one of the following cursor * modes: * - `GLFW_CURSOR_NORMAL` makes the cursor visible and behaving normally. * - `GLFW_CURSOR_HIDDEN` makes the cursor invisible when it is over the * content area of the window but does not restrict the cursor from leaving. * - `GLFW_CURSOR_DISABLED` hides and grabs the cursor, providing virtual * and unlimited cursor movement. This is useful for implementing for * example 3D camera controls. * * If the mode is `GLFW_STICKY_KEYS`, the value must be either `GLFW_TRUE` to * enable sticky keys, or `GLFW_FALSE` to disable it. If sticky keys are * enabled, a key press will ensure that @ref glfwGetKey returns `GLFW_PRESS` * the next time it is called even if the key had been released before the * call. This is useful when you are only interested in whether keys have been * pressed but not when or in which order. * * If the mode is `GLFW_STICKY_MOUSE_BUTTONS`, the value must be either * `GLFW_TRUE` to enable sticky mouse buttons, or `GLFW_FALSE` to disable it. * If sticky mouse buttons are enabled, a mouse button press will ensure that * @ref glfwGetMouseButton returns `GLFW_PRESS` the next time it is called even * if the mouse button had been released before the call. This is useful when * you are only interested in whether mouse buttons have been pressed but not * when or in which order. * * If the mode is `GLFW_LOCK_KEY_MODS`, the value must be either `GLFW_TRUE` to * enable lock key modifier bits, or `GLFW_FALSE` to disable them. If enabled, * callbacks that receive modifier bits will also have the @ref * GLFW_MOD_CAPS_LOCK bit set when the event was generated with Caps Lock on, * and the @ref GLFW_MOD_NUM_LOCK bit when Num Lock was on. * * If the mode is `GLFW_RAW_MOUSE_MOTION`, the value must be either `GLFW_TRUE` * to enable raw (unscaled and unaccelerated) mouse motion when the cursor is * disabled, or `GLFW_FALSE` to disable it. If raw motion is not supported, * attempting to set this will emit @ref GLFW_PLATFORM_ERROR. Call @ref * glfwRawMouseMotionSupported to check for support. * * @param[in] window The window whose input mode to set. * @param[in] mode One of `GLFW_CURSOR`, `GLFW_STICKY_KEYS`, * `GLFW_STICKY_MOUSE_BUTTONS`, `GLFW_LOCK_KEY_MODS` or * `GLFW_RAW_MOUSE_MOTION`. * @param[in] value The new value of the specified input mode. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref * GLFW_INVALID_ENUM and @ref GLFW_PLATFORM_ERROR. * * @thread_safety This function must only be called from the main thread. * * @sa @ref glfwGetInputMode * * @since Added in version 3.0. Replaces `glfwEnable` and `glfwDisable`. * * @ingroup input */ GLFWAPI void glfwSetInputMode(GLFWwindow* window, int mode, int value); /*! @brief Returns whether raw mouse motion is supported. * * This function returns whether raw mouse motion is supported on the current * system. This status does not change after GLFW has been initialized so you * only need to check this once. If you attempt to enable raw motion on * a system that does not support it, @ref GLFW_PLATFORM_ERROR will be emitted. * * Raw mouse motion is closer to the actual motion of the mouse across * a surface. It is not affected by the scaling and acceleration applied to * the motion of the desktop cursor. That processing is suitable for a cursor * while raw motion is better for controlling for example a 3D camera. Because * of this, raw mouse motion is only provided when the cursor is disabled. * * @return `GLFW_TRUE` if raw mouse motion is supported on the current machine, * or `GLFW_FALSE` otherwise. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. * * @thread_safety This function must only be called from the main thread. * * @sa @ref raw_mouse_motion * @sa @ref glfwSetInputMode * * @since Added in version 3.3. * * @ingroup input */ GLFWAPI int glfwRawMouseMotionSupported(void); /*! @brief Returns the layout-specific name of the specified printable key. * * This function returns the name of the specified printable key, encoded as * UTF-8. This is typically the character that key would produce without any * modifier keys, intended for displaying key bindings to the user. For dead * keys, it is typically the diacritic it would add to a character. * * __Do not use this function__ for [text input](@ref input_char). You will * break text input for many languages even if it happens to work for yours. * * If the key is `GLFW_KEY_UNKNOWN`, the scancode is used to identify the key, * otherwise the scancode is ignored. If you specify a non-printable key, or * `GLFW_KEY_UNKNOWN` and a scancode that maps to a non-printable key, this * function returns `NULL` but does not emit an error. * * This behavior allows you to always pass in the arguments in the * [key callback](@ref input_key) without modification. * * The printable keys are: * - `GLFW_KEY_APOSTROPHE` * - `GLFW_KEY_COMMA` * - `GLFW_KEY_MINUS` * - `GLFW_KEY_PERIOD` * - `GLFW_KEY_SLASH` * - `GLFW_KEY_SEMICOLON` * - `GLFW_KEY_EQUAL` * - `GLFW_KEY_LEFT_BRACKET` * - `GLFW_KEY_RIGHT_BRACKET` * - `GLFW_KEY_BACKSLASH` * - `GLFW_KEY_WORLD_1` * - `GLFW_KEY_WORLD_2` * - `GLFW_KEY_0` to `GLFW_KEY_9` * - `GLFW_KEY_A` to `GLFW_KEY_Z` * - `GLFW_KEY_KP_0` to `GLFW_KEY_KP_9` * - `GLFW_KEY_KP_DECIMAL` * - `GLFW_KEY_KP_DIVIDE` * - `GLFW_KEY_KP_MULTIPLY` * - `GLFW_KEY_KP_SUBTRACT` * - `GLFW_KEY_KP_ADD` * - `GLFW_KEY_KP_EQUAL` * * Names for printable keys depend on keyboard layout, while names for * non-printable keys are the same across layouts but depend on the application * language and should be localized along with other user interface text. * * @param[in] key The key to query, or `GLFW_KEY_UNKNOWN`. * @param[in] scancode The scancode of the key to query. * @return The UTF-8 encoded, layout-specific name of the key, or `NULL`. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref * GLFW_PLATFORM_ERROR. * * @remark The contents of the returned string may change when a keyboard * layout change event is received. * * @pointer_lifetime The returned string is allocated and freed by GLFW. You * should not free it yourself. It is valid until the library is terminated. * * @thread_safety This function must only be called from the main thread. * * @sa @ref input_key_name * * @since Added in version 3.2. * * @ingroup input */ GLFWAPI const char* glfwGetKeyName(int key, int scancode); GLFWAPI const char* glfwGetKeys(GLFWwindow* handle); //<< @r-lyeh added /*! @brief Returns the platform-specific scancode of the specified key. * * This function returns the platform-specific scancode of the specified key. * * If the key is `GLFW_KEY_UNKNOWN` or does not exist on the keyboard this * method will return `-1`. * * @param[in] key Any [named key](@ref keys). * @return The platform-specific scancode for the key, or `-1` if an * [error](@ref error_handling) occurred. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref * GLFW_INVALID_ENUM and @ref GLFW_PLATFORM_ERROR. * * @thread_safety This function may be called from any thread. * * @sa @ref input_key * * @since Added in version 3.3. * * @ingroup input */ GLFWAPI int glfwGetKeyScancode(int key); /*! @brief Returns the last reported state of a keyboard key for the specified * window. * * This function returns the last state reported for the specified key to the * specified window. The returned state is one of `GLFW_PRESS` or * `GLFW_RELEASE`. The higher-level action `GLFW_REPEAT` is only reported to * the key callback. * * If the @ref GLFW_STICKY_KEYS input mode is enabled, this function returns * `GLFW_PRESS` the first time you call it for a key that was pressed, even if * that key has already been released. * * The key functions deal with physical keys, with [key tokens](@ref keys) * named after their use on the standard US keyboard layout. If you want to * input text, use the Unicode character callback instead. * * The [modifier key bit masks](@ref mods) are not key tokens and cannot be * used with this function. * * __Do not use this function__ to implement [text input](@ref input_char). * * @param[in] window The desired window. * @param[in] key The desired [keyboard key](@ref keys). `GLFW_KEY_UNKNOWN` is * not a valid key for this function. * @return One of `GLFW_PRESS` or `GLFW_RELEASE`. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref * GLFW_INVALID_ENUM. * * @thread_safety This function must only be called from the main thread. * * @sa @ref input_key * * @since Added in version 1.0. * @glfw3 Added window handle parameter. * * @ingroup input */ GLFWAPI int glfwGetKey(GLFWwindow* window, int key); /*! @brief Returns the last reported state of a mouse button for the specified * window. * * This function returns the last state reported for the specified mouse button * to the specified window. The returned state is one of `GLFW_PRESS` or * `GLFW_RELEASE`. * * If the @ref GLFW_STICKY_MOUSE_BUTTONS input mode is enabled, this function * returns `GLFW_PRESS` the first time you call it for a mouse button that was * pressed, even if that mouse button has already been released. * * @param[in] window The desired window. * @param[in] button The desired [mouse button](@ref buttons). * @return One of `GLFW_PRESS` or `GLFW_RELEASE`. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref * GLFW_INVALID_ENUM. * * @thread_safety This function must only be called from the main thread. * * @sa @ref input_mouse_button * * @since Added in version 1.0. * @glfw3 Added window handle parameter. * * @ingroup input */ GLFWAPI int glfwGetMouseButton(GLFWwindow* window, int button); /*! @brief Retrieves the position of the cursor relative to the content area of * the window. * * This function returns the position of the cursor, in screen coordinates, * relative to the upper-left corner of the content area of the specified * window. * * If the cursor is disabled (with `GLFW_CURSOR_DISABLED`) then the cursor * position is unbounded and limited only by the minimum and maximum values of * a `double`. * * The coordinate can be converted to their integer equivalents with the * `floor` function. Casting directly to an integer type works for positive * coordinates, but fails for negative ones. * * Any or all of the position arguments may be `NULL`. If an error occurs, all * non-`NULL` position arguments will be set to zero. * * @param[in] window The desired window. * @param[out] xpos Where to store the cursor x-coordinate, relative to the * left edge of the content area, or `NULL`. * @param[out] ypos Where to store the cursor y-coordinate, relative to the to * top edge of the content area, or `NULL`. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref * GLFW_PLATFORM_ERROR. * * @thread_safety This function must only be called from the main thread. * * @sa @ref cursor_pos * @sa @ref glfwSetCursorPos * * @since Added in version 3.0. Replaces `glfwGetMousePos`. * * @ingroup input */ GLFWAPI void glfwGetCursorPos(GLFWwindow* window, double* xpos, double* ypos); /*! @brief Sets the position of the cursor, relative to the content area of the * window. * * This function sets the position, in screen coordinates, of the cursor * relative to the upper-left corner of the content area of the specified * window. The window must have input focus. If the window does not have * input focus when this function is called, it fails silently. * * __Do not use this function__ to implement things like camera controls. GLFW * already provides the `GLFW_CURSOR_DISABLED` cursor mode that hides the * cursor, transparently re-centers it and provides unconstrained cursor * motion. See @ref glfwSetInputMode for more information. * * If the cursor mode is `GLFW_CURSOR_DISABLED` then the cursor position is * unconstrained and limited only by the minimum and maximum values of * a `double`. * * @param[in] window The desired window. * @param[in] xpos The desired x-coordinate, relative to the left edge of the * content area. * @param[in] ypos The desired y-coordinate, relative to the top edge of the * content area. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref * GLFW_PLATFORM_ERROR. * * @remark @wayland This function will only work when the cursor mode is * `GLFW_CURSOR_DISABLED`, otherwise it will do nothing. * * @thread_safety This function must only be called from the main thread. * * @sa @ref cursor_pos * @sa @ref glfwGetCursorPos * * @since Added in version 3.0. Replaces `glfwSetMousePos`. * * @ingroup input */ GLFWAPI void glfwSetCursorPos(GLFWwindow* window, double xpos, double ypos); /*! @brief Creates a custom cursor. * * Creates a new custom cursor image that can be set for a window with @ref * glfwSetCursor. The cursor can be destroyed with @ref glfwDestroyCursor. * Any remaining cursors are destroyed by @ref glfwTerminate. * * The pixels are 32-bit, little-endian, non-premultiplied RGBA, i.e. eight * bits per channel with the red channel first. They are arranged canonically * as packed sequential rows, starting from the top-left corner. * * The cursor hotspot is specified in pixels, relative to the upper-left corner * of the cursor image. Like all other coordinate systems in GLFW, the X-axis * points to the right and the Y-axis points down. * * @param[in] image The desired cursor image. * @param[in] xhot The desired x-coordinate, in pixels, of the cursor hotspot. * @param[in] yhot The desired y-coordinate, in pixels, of the cursor hotspot. * @return The handle of the created cursor, or `NULL` if an * [error](@ref error_handling) occurred. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref * GLFW_PLATFORM_ERROR. * * @pointer_lifetime The specified image data is copied before this function * returns. * * @thread_safety This function must only be called from the main thread. * * @sa @ref cursor_object * @sa @ref glfwDestroyCursor * @sa @ref glfwCreateStandardCursor * * @since Added in version 3.1. * * @ingroup input */ GLFWAPI GLFWcursor* glfwCreateCursor(const GLFWimage* image, int xhot, int yhot); /*! @brief Creates a cursor with a standard shape. * * Returns a cursor with a [standard shape](@ref shapes), that can be set for * a window with @ref glfwSetCursor. * * @param[in] shape One of the [standard shapes](@ref shapes). * @return A new cursor ready to use or `NULL` if an * [error](@ref error_handling) occurred. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref * GLFW_INVALID_ENUM and @ref GLFW_PLATFORM_ERROR. * * @thread_safety This function must only be called from the main thread. * * @sa @ref cursor_object * @sa @ref glfwCreateCursor * * @since Added in version 3.1. * * @ingroup input */ GLFWAPI GLFWcursor* glfwCreateStandardCursor(int shape); /*! @brief Destroys a cursor. * * This function destroys a cursor previously created with @ref * glfwCreateCursor. Any remaining cursors will be destroyed by @ref * glfwTerminate. * * If the specified cursor is current for any window, that window will be * reverted to the default cursor. This does not affect the cursor mode. * * @param[in] cursor The cursor object to destroy. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref * GLFW_PLATFORM_ERROR. * * @reentrancy This function must not be called from a callback. * * @thread_safety This function must only be called from the main thread. * * @sa @ref cursor_object * @sa @ref glfwCreateCursor * * @since Added in version 3.1. * * @ingroup input */ GLFWAPI void glfwDestroyCursor(GLFWcursor* cursor); /*! @brief Sets the cursor for the window. * * This function sets the cursor image to be used when the cursor is over the * content area of the specified window. The set cursor will only be visible * when the [cursor mode](@ref cursor_mode) of the window is * `GLFW_CURSOR_NORMAL`. * * On some platforms, the set cursor may not be visible unless the window also * has input focus. * * @param[in] window The window to set the cursor for. * @param[in] cursor The cursor to set, or `NULL` to switch back to the default * arrow cursor. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref * GLFW_PLATFORM_ERROR. * * @thread_safety This function must only be called from the main thread. * * @sa @ref cursor_object * * @since Added in version 3.1. * * @ingroup input */ GLFWAPI void glfwSetCursor(GLFWwindow* window, GLFWcursor* cursor); /*! @brief Sets the key callback. * * This function sets the key callback of the specified window, which is called * when a key is pressed, repeated or released. * * The key functions deal with physical keys, with layout independent * [key tokens](@ref keys) named after their values in the standard US keyboard * layout. If you want to input text, use the * [character callback](@ref glfwSetCharCallback) instead. * * When a window loses input focus, it will generate synthetic key release * events for all pressed keys. You can tell these events from user-generated * events by the fact that the synthetic ones are generated after the focus * loss event has been processed, i.e. after the * [window focus callback](@ref glfwSetWindowFocusCallback) has been called. * * The scancode of a key is specific to that platform or sometimes even to that * machine. Scancodes are intended to allow users to bind keys that don't have * a GLFW key token. Such keys have `key` set to `GLFW_KEY_UNKNOWN`, their * state is not saved and so it cannot be queried with @ref glfwGetKey. * * Sometimes GLFW needs to generate synthetic key events, in which case the * scancode may be zero. * * @param[in] window The window whose callback to set. * @param[in] callback The new key callback, or `NULL` to remove the currently * set callback. * @return The previously set callback, or `NULL` if no callback was set or the * library had not been [initialized](@ref intro_init). * * @callback_signature * @code * void function_name(GLFWwindow* window, int key, int scancode, int action, int mods) * @endcode * For more information about the callback parameters, see the * [function pointer type](@ref GLFWkeyfun). * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. * * @thread_safety This function must only be called from the main thread. * * @sa @ref input_key * * @since Added in version 1.0. * @glfw3 Added window handle parameter and return value. * * @ingroup input */ GLFWAPI GLFWkeyfun glfwSetKeyCallback(GLFWwindow* window, GLFWkeyfun callback); /*! @brief Sets the Unicode character callback. * * This function sets the character callback of the specified window, which is * called when a Unicode character is input. * * The character callback is intended for Unicode text input. As it deals with * characters, it is keyboard layout dependent, whereas the * [key callback](@ref glfwSetKeyCallback) is not. Characters do not map 1:1 * to physical keys, as a key may produce zero, one or more characters. If you * want to know whether a specific physical key was pressed or released, see * the key callback instead. * * The character callback behaves as system text input normally does and will * not be called if modifier keys are held down that would prevent normal text * input on that platform, for example a Super (Command) key on macOS or Alt key * on Windows. * * @param[in] window The window whose callback to set. * @param[in] callback The new callback, or `NULL` to remove the currently set * callback. * @return The previously set callback, or `NULL` if no callback was set or the * library had not been [initialized](@ref intro_init). * * @callback_signature * @code * void function_name(GLFWwindow* window, unsigned int codepoint) * @endcode * For more information about the callback parameters, see the * [function pointer type](@ref GLFWcharfun). * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. * * @thread_safety This function must only be called from the main thread. * * @sa @ref input_char * * @since Added in version 2.4. * @glfw3 Added window handle parameter and return value. * * @ingroup input */ GLFWAPI GLFWcharfun glfwSetCharCallback(GLFWwindow* window, GLFWcharfun callback); /*! @brief Sets the Unicode character with modifiers callback. * * This function sets the character with modifiers callback of the specified * window, which is called when a Unicode character is input regardless of what * modifier keys are used. * * The character with modifiers callback is intended for implementing custom * Unicode character input. For regular Unicode text input, see the * [character callback](@ref glfwSetCharCallback). Like the character * callback, the character with modifiers callback deals with characters and is * keyboard layout dependent. Characters do not map 1:1 to physical keys, as * a key may produce zero, one or more characters. If you want to know whether * a specific physical key was pressed or released, see the * [key callback](@ref glfwSetKeyCallback) instead. * * @param[in] window The window whose callback to set. * @param[in] callback The new callback, or `NULL` to remove the currently set * callback. * @return The previously set callback, or `NULL` if no callback was set or an * [error](@ref error_handling) occurred. * * @callback_signature * @code * void function_name(GLFWwindow* window, unsigned int codepoint, int mods) * @endcode * For more information about the callback parameters, see the * [function pointer type](@ref GLFWcharmodsfun). * * @deprecated Scheduled for removal in version 4.0. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. * * @thread_safety This function must only be called from the main thread. * * @sa @ref input_char * * @since Added in version 3.1. * * @ingroup input */ GLFWAPI GLFWcharmodsfun glfwSetCharModsCallback(GLFWwindow* window, GLFWcharmodsfun callback); /*! @brief Sets the mouse button callback. * * This function sets the mouse button callback of the specified window, which * is called when a mouse button is pressed or released. * * When a window loses input focus, it will generate synthetic mouse button * release events for all pressed mouse buttons. You can tell these events * from user-generated events by the fact that the synthetic ones are generated * after the focus loss event has been processed, i.e. after the * [window focus callback](@ref glfwSetWindowFocusCallback) has been called. * * @param[in] window The window whose callback to set. * @param[in] callback The new callback, or `NULL` to remove the currently set * callback. * @return The previously set callback, or `NULL` if no callback was set or the * library had not been [initialized](@ref intro_init). * * @callback_signature * @code * void function_name(GLFWwindow* window, int button, int action, int mods) * @endcode * For more information about the callback parameters, see the * [function pointer type](@ref GLFWmousebuttonfun). * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. * * @thread_safety This function must only be called from the main thread. * * @sa @ref input_mouse_button * * @since Added in version 1.0. * @glfw3 Added window handle parameter and return value. * * @ingroup input */ GLFWAPI GLFWmousebuttonfun glfwSetMouseButtonCallback(GLFWwindow* window, GLFWmousebuttonfun callback); /*! @brief Sets the cursor position callback. * * This function sets the cursor position callback of the specified window, * which is called when the cursor is moved. The callback is provided with the * position, in screen coordinates, relative to the upper-left corner of the * content area of the window. * * @param[in] window The window whose callback to set. * @param[in] callback The new callback, or `NULL` to remove the currently set * callback. * @return The previously set callback, or `NULL` if no callback was set or the * library had not been [initialized](@ref intro_init). * * @callback_signature * @code * void function_name(GLFWwindow* window, double xpos, double ypos); * @endcode * For more information about the callback parameters, see the * [function pointer type](@ref GLFWcursorposfun). * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. * * @thread_safety This function must only be called from the main thread. * * @sa @ref cursor_pos * * @since Added in version 3.0. Replaces `glfwSetMousePosCallback`. * * @ingroup input */ GLFWAPI GLFWcursorposfun glfwSetCursorPosCallback(GLFWwindow* window, GLFWcursorposfun callback); /*! @brief Sets the cursor enter/leave callback. * * This function sets the cursor boundary crossing callback of the specified * window, which is called when the cursor enters or leaves the content area of * the window. * * @param[in] window The window whose callback to set. * @param[in] callback The new callback, or `NULL` to remove the currently set * callback. * @return The previously set callback, or `NULL` if no callback was set or the * library had not been [initialized](@ref intro_init). * * @callback_signature * @code * void function_name(GLFWwindow* window, int entered) * @endcode * For more information about the callback parameters, see the * [function pointer type](@ref GLFWcursorenterfun). * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. * * @thread_safety This function must only be called from the main thread. * * @sa @ref cursor_enter * * @since Added in version 3.0. * * @ingroup input */ GLFWAPI GLFWcursorenterfun glfwSetCursorEnterCallback(GLFWwindow* window, GLFWcursorenterfun callback); /*! @brief Sets the scroll callback. * * This function sets the scroll callback of the specified window, which is * called when a scrolling device is used, such as a mouse wheel or scrolling * area of a touchpad. * * The scroll callback receives all scrolling input, like that from a mouse * wheel or a touchpad scrolling area. * * @param[in] window The window whose callback to set. * @param[in] callback The new scroll callback, or `NULL` to remove the * currently set callback. * @return The previously set callback, or `NULL` if no callback was set or the * library had not been [initialized](@ref intro_init). * * @callback_signature * @code * void function_name(GLFWwindow* window, double xoffset, double yoffset) * @endcode * For more information about the callback parameters, see the * [function pointer type](@ref GLFWscrollfun). * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. * * @thread_safety This function must only be called from the main thread. * * @sa @ref scrolling * * @since Added in version 3.0. Replaces `glfwSetMouseWheelCallback`. * * @ingroup input */ GLFWAPI GLFWscrollfun glfwSetScrollCallback(GLFWwindow* window, GLFWscrollfun callback); /*! @brief Sets the path drop callback. * * This function sets the path drop callback of the specified window, which is * called when one or more dragged paths are dropped on the window. * * Because the path array and its strings may have been generated specifically * for that event, they are not guaranteed to be valid after the callback has * returned. If you wish to use them after the callback returns, you need to * make a deep copy. * * @param[in] window The window whose callback to set. * @param[in] callback The new file drop callback, or `NULL` to remove the * currently set callback. * @return The previously set callback, or `NULL` if no callback was set or the * library had not been [initialized](@ref intro_init). * * @callback_signature * @code * void function_name(GLFWwindow* window, int path_count, const char* paths[]) * @endcode * For more information about the callback parameters, see the * [function pointer type](@ref GLFWdropfun). * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. * * @remark @wayland File drop is currently unimplemented. * * @thread_safety This function must only be called from the main thread. * * @sa @ref path_drop * * @since Added in version 3.1. * * @ingroup input */ GLFWAPI GLFWdropfun glfwSetDropCallback(GLFWwindow* window, GLFWdropfun callback); /*! @brief Returns whether the specified joystick is present. * * This function returns whether the specified joystick is present. * * There is no need to call this function before other functions that accept * a joystick ID, as they all check for presence before performing any other * work. * * @param[in] jid The [joystick](@ref joysticks) to query. * @return `GLFW_TRUE` if the joystick is present, or `GLFW_FALSE` otherwise. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref * GLFW_INVALID_ENUM and @ref GLFW_PLATFORM_ERROR. * * @thread_safety This function must only be called from the main thread. * * @sa @ref joystick * * @since Added in version 3.0. Replaces `glfwGetJoystickParam`. * * @ingroup input */ GLFWAPI int glfwJoystickPresent(int jid); /*! @brief Returns the values of all axes of the specified joystick. * * This function returns the values of all axes of the specified joystick. * Each element in the array is a value between -1.0 and 1.0. * * If the specified joystick is not present this function will return `NULL` * but will not generate an error. This can be used instead of first calling * @ref glfwJoystickPresent. * * @param[in] jid The [joystick](@ref joysticks) to query. * @param[out] count Where to store the number of axis values in the returned * array. This is set to zero if the joystick is not present or an error * occurred. * @return An array of axis values, or `NULL` if the joystick is not present or * an [error](@ref error_handling) occurred. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref * GLFW_INVALID_ENUM and @ref GLFW_PLATFORM_ERROR. * * @pointer_lifetime The returned array is allocated and freed by GLFW. You * should not free it yourself. It is valid until the specified joystick is * disconnected or the library is terminated. * * @thread_safety This function must only be called from the main thread. * * @sa @ref joystick_axis * * @since Added in version 3.0. Replaces `glfwGetJoystickPos`. * * @ingroup input */ GLFWAPI const float* glfwGetJoystickAxes(int jid, int* count); /*! @brief Returns the state of all buttons of the specified joystick. * * This function returns the state of all buttons of the specified joystick. * Each element in the array is either `GLFW_PRESS` or `GLFW_RELEASE`. * * For backward compatibility with earlier versions that did not have @ref * glfwGetJoystickHats, the button array also includes all hats, each * represented as four buttons. The hats are in the same order as returned by * __glfwGetJoystickHats__ and are in the order _up_, _right_, _down_ and * _left_. To disable these extra buttons, set the @ref * GLFW_JOYSTICK_HAT_BUTTONS init hint before initialization. * * If the specified joystick is not present this function will return `NULL` * but will not generate an error. This can be used instead of first calling * @ref glfwJoystickPresent. * * @param[in] jid The [joystick](@ref joysticks) to query. * @param[out] count Where to store the number of button states in the returned * array. This is set to zero if the joystick is not present or an error * occurred. * @return An array of button states, or `NULL` if the joystick is not present * or an [error](@ref error_handling) occurred. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref * GLFW_INVALID_ENUM and @ref GLFW_PLATFORM_ERROR. * * @pointer_lifetime The returned array is allocated and freed by GLFW. You * should not free it yourself. It is valid until the specified joystick is * disconnected or the library is terminated. * * @thread_safety This function must only be called from the main thread. * * @sa @ref joystick_button * * @since Added in version 2.2. * @glfw3 Changed to return a dynamic array. * * @ingroup input */ GLFWAPI const unsigned char* glfwGetJoystickButtons(int jid, int* count); /*! @brief Returns the state of all hats of the specified joystick. * * This function returns the state of all hats of the specified joystick. * Each element in the array is one of the following values: * * Name | Value * ---- | ----- * `GLFW_HAT_CENTERED` | 0 * `GLFW_HAT_UP` | 1 * `GLFW_HAT_RIGHT` | 2 * `GLFW_HAT_DOWN` | 4 * `GLFW_HAT_LEFT` | 8 * `GLFW_HAT_RIGHT_UP` | `GLFW_HAT_RIGHT` \| `GLFW_HAT_UP` * `GLFW_HAT_RIGHT_DOWN` | `GLFW_HAT_RIGHT` \| `GLFW_HAT_DOWN` * `GLFW_HAT_LEFT_UP` | `GLFW_HAT_LEFT` \| `GLFW_HAT_UP` * `GLFW_HAT_LEFT_DOWN` | `GLFW_HAT_LEFT` \| `GLFW_HAT_DOWN` * * The diagonal directions are bitwise combinations of the primary (up, right, * down and left) directions and you can test for these individually by ANDing * it with the corresponding direction. * * @code * if (hats[2] & GLFW_HAT_RIGHT) * { * // State of hat 2 could be right-up, right or right-down * } * @endcode * * If the specified joystick is not present this function will return `NULL` * but will not generate an error. This can be used instead of first calling * @ref glfwJoystickPresent. * * @param[in] jid The [joystick](@ref joysticks) to query. * @param[out] count Where to store the number of hat states in the returned * array. This is set to zero if the joystick is not present or an error * occurred. * @return An array of hat states, or `NULL` if the joystick is not present * or an [error](@ref error_handling) occurred. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref * GLFW_INVALID_ENUM and @ref GLFW_PLATFORM_ERROR. * * @pointer_lifetime The returned array is allocated and freed by GLFW. You * should not free it yourself. It is valid until the specified joystick is * disconnected, this function is called again for that joystick or the library * is terminated. * * @thread_safety This function must only be called from the main thread. * * @sa @ref joystick_hat * * @since Added in version 3.3. * * @ingroup input */ GLFWAPI const unsigned char* glfwGetJoystickHats(int jid, int* count); /*! @brief Returns the name of the specified joystick. * * This function returns the name, encoded as UTF-8, of the specified joystick. * The returned string is allocated and freed by GLFW. You should not free it * yourself. * * If the specified joystick is not present this function will return `NULL` * but will not generate an error. This can be used instead of first calling * @ref glfwJoystickPresent. * * @param[in] jid The [joystick](@ref joysticks) to query. * @return The UTF-8 encoded name of the joystick, or `NULL` if the joystick * is not present or an [error](@ref error_handling) occurred. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref * GLFW_INVALID_ENUM and @ref GLFW_PLATFORM_ERROR. * * @pointer_lifetime The returned string is allocated and freed by GLFW. You * should not free it yourself. It is valid until the specified joystick is * disconnected or the library is terminated. * * @thread_safety This function must only be called from the main thread. * * @sa @ref joystick_name * * @since Added in version 3.0. * * @ingroup input */ GLFWAPI const char* glfwGetJoystickName(int jid); /*! @brief Returns the SDL compatible GUID of the specified joystick. * * This function returns the SDL compatible GUID, as a UTF-8 encoded * hexadecimal string, of the specified joystick. The returned string is * allocated and freed by GLFW. You should not free it yourself. * * The GUID is what connects a joystick to a gamepad mapping. A connected * joystick will always have a GUID even if there is no gamepad mapping * assigned to it. * * If the specified joystick is not present this function will return `NULL` * but will not generate an error. This can be used instead of first calling * @ref glfwJoystickPresent. * * The GUID uses the format introduced in SDL 2.0.5. This GUID tries to * uniquely identify the make and model of a joystick but does not identify * a specific unit, e.g. all wired Xbox 360 controllers will have the same * GUID on that platform. The GUID for a unit may vary between platforms * depending on what hardware information the platform specific APIs provide. * * @param[in] jid The [joystick](@ref joysticks) to query. * @return The UTF-8 encoded GUID of the joystick, or `NULL` if the joystick * is not present or an [error](@ref error_handling) occurred. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref * GLFW_INVALID_ENUM and @ref GLFW_PLATFORM_ERROR. * * @pointer_lifetime The returned string is allocated and freed by GLFW. You * should not free it yourself. It is valid until the specified joystick is * disconnected or the library is terminated. * * @thread_safety This function must only be called from the main thread. * * @sa @ref gamepad * * @since Added in version 3.3. * * @ingroup input */ GLFWAPI const char* glfwGetJoystickGUID(int jid); /*! @brief Sets the user pointer of the specified joystick. * * This function sets the user-defined pointer of the specified joystick. The * current value is retained until the joystick is disconnected. The initial * value is `NULL`. * * This function may be called from the joystick callback, even for a joystick * that is being disconnected. * * @param[in] jid The joystick whose pointer to set. * @param[in] pointer The new value. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. * * @thread_safety This function may be called from any thread. Access is not * synchronized. * * @sa @ref joystick_userptr * @sa @ref glfwGetJoystickUserPointer * * @since Added in version 3.3. * * @ingroup input */ GLFWAPI void glfwSetJoystickUserPointer(int jid, void* pointer); /*! @brief Returns the user pointer of the specified joystick. * * This function returns the current value of the user-defined pointer of the * specified joystick. The initial value is `NULL`. * * This function may be called from the joystick callback, even for a joystick * that is being disconnected. * * @param[in] jid The joystick whose pointer to return. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. * * @thread_safety This function may be called from any thread. Access is not * synchronized. * * @sa @ref joystick_userptr * @sa @ref glfwSetJoystickUserPointer * * @since Added in version 3.3. * * @ingroup input */ GLFWAPI void* glfwGetJoystickUserPointer(int jid); /*! @brief Returns whether the specified joystick has a gamepad mapping. * * This function returns whether the specified joystick is both present and has * a gamepad mapping. * * If the specified joystick is present but does not have a gamepad mapping * this function will return `GLFW_FALSE` but will not generate an error. Call * @ref glfwJoystickPresent to check if a joystick is present regardless of * whether it has a mapping. * * @param[in] jid The [joystick](@ref joysticks) to query. * @return `GLFW_TRUE` if a joystick is both present and has a gamepad mapping, * or `GLFW_FALSE` otherwise. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref * GLFW_INVALID_ENUM. * * @thread_safety This function must only be called from the main thread. * * @sa @ref gamepad * @sa @ref glfwGetGamepadState * * @since Added in version 3.3. * * @ingroup input */ GLFWAPI int glfwJoystickIsGamepad(int jid); /*! @brief Sets the joystick configuration callback. * * This function sets the joystick configuration callback, or removes the * currently set callback. This is called when a joystick is connected to or * disconnected from the system. * * For joystick connection and disconnection events to be delivered on all * platforms, you need to call one of the [event processing](@ref events) * functions. Joystick disconnection may also be detected and the callback * called by joystick functions. The function will then return whatever it * returns if the joystick is not present. * * @param[in] callback The new callback, or `NULL` to remove the currently set * callback. * @return The previously set callback, or `NULL` if no callback was set or the * library had not been [initialized](@ref intro_init). * * @callback_signature * @code * void function_name(int jid, int event) * @endcode * For more information about the callback parameters, see the * [function pointer type](@ref GLFWjoystickfun). * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. * * @thread_safety This function must only be called from the main thread. * * @sa @ref joystick_event * * @since Added in version 3.2. * * @ingroup input */ GLFWAPI GLFWjoystickfun glfwSetJoystickCallback(GLFWjoystickfun callback); /*! @brief Adds the specified SDL_GameControllerDB gamepad mappings. * * This function parses the specified ASCII encoded string and updates the * internal list with any gamepad mappings it finds. This string may * contain either a single gamepad mapping or many mappings separated by * newlines. The parser supports the full format of the `gamecontrollerdb.txt` * source file including empty lines and comments. * * See @ref gamepad_mapping for a description of the format. * * If there is already a gamepad mapping for a given GUID in the internal list, * it will be replaced by the one passed to this function. If the library is * terminated and re-initialized the internal list will revert to the built-in * default. * * @param[in] string The string containing the gamepad mappings. * @return `GLFW_TRUE` if successful, or `GLFW_FALSE` if an * [error](@ref error_handling) occurred. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref * GLFW_INVALID_VALUE. * * @thread_safety This function must only be called from the main thread. * * @sa @ref gamepad * @sa @ref glfwJoystickIsGamepad * @sa @ref glfwGetGamepadName * * @since Added in version 3.3. * * @ingroup input */ GLFWAPI int glfwUpdateGamepadMappings(const char* string); /*! @brief Returns the human-readable gamepad name for the specified joystick. * * This function returns the human-readable name of the gamepad from the * gamepad mapping assigned to the specified joystick. * * If the specified joystick is not present or does not have a gamepad mapping * this function will return `NULL` but will not generate an error. Call * @ref glfwJoystickPresent to check whether it is present regardless of * whether it has a mapping. * * @param[in] jid The [joystick](@ref joysticks) to query. * @return The UTF-8 encoded name of the gamepad, or `NULL` if the * joystick is not present, does not have a mapping or an * [error](@ref error_handling) occurred. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref GLFW_INVALID_ENUM. * * @pointer_lifetime The returned string is allocated and freed by GLFW. You * should not free it yourself. It is valid until the specified joystick is * disconnected, the gamepad mappings are updated or the library is terminated. * * @thread_safety This function must only be called from the main thread. * * @sa @ref gamepad * @sa @ref glfwJoystickIsGamepad * * @since Added in version 3.3. * * @ingroup input */ GLFWAPI const char* glfwGetGamepadName(int jid); /*! @brief Retrieves the state of the specified joystick remapped as a gamepad. * * This function retrieves the state of the specified joystick remapped to * an Xbox-like gamepad. * * If the specified joystick is not present or does not have a gamepad mapping * this function will return `GLFW_FALSE` but will not generate an error. Call * @ref glfwJoystickPresent to check whether it is present regardless of * whether it has a mapping. * * The Guide button may not be available for input as it is often hooked by the * system or the Steam client. * * Not all devices have all the buttons or axes provided by @ref * GLFWgamepadstate. Unavailable buttons and axes will always report * `GLFW_RELEASE` and 0.0 respectively. * * @param[in] jid The [joystick](@ref joysticks) to query. * @param[out] state The gamepad input state of the joystick. * @return `GLFW_TRUE` if successful, or `GLFW_FALSE` if no joystick is * connected, it has no gamepad mapping or an [error](@ref error_handling) * occurred. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref * GLFW_INVALID_ENUM. * * @thread_safety This function must only be called from the main thread. * * @sa @ref gamepad * @sa @ref glfwUpdateGamepadMappings * @sa @ref glfwJoystickIsGamepad * * @since Added in version 3.3. * * @ingroup input */ GLFWAPI int glfwGetGamepadState(int jid, GLFWgamepadstate* state); /*! @brief Sets the clipboard to the specified string. * * This function sets the system clipboard to the specified, UTF-8 encoded * string. * * @param[in] window Deprecated. Any valid window or `NULL`. * @param[in] string A UTF-8 encoded string. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref * GLFW_PLATFORM_ERROR. * * @pointer_lifetime The specified string is copied before this function * returns. * * @thread_safety This function must only be called from the main thread. * * @sa @ref clipboard * @sa @ref glfwGetClipboardString * * @since Added in version 3.0. * * @ingroup input */ GLFWAPI void glfwSetClipboardString(GLFWwindow* window, const char* string); /*! @brief Returns the contents of the clipboard as a string. * * This function returns the contents of the system clipboard, if it contains * or is convertible to a UTF-8 encoded string. If the clipboard is empty or * if its contents cannot be converted, `NULL` is returned and a @ref * GLFW_FORMAT_UNAVAILABLE error is generated. * * @param[in] window Deprecated. Any valid window or `NULL`. * @return The contents of the clipboard as a UTF-8 encoded string, or `NULL` * if an [error](@ref error_handling) occurred. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref * GLFW_FORMAT_UNAVAILABLE and @ref GLFW_PLATFORM_ERROR. * * @pointer_lifetime The returned string is allocated and freed by GLFW. You * should not free it yourself. It is valid until the next call to @ref * glfwGetClipboardString or @ref glfwSetClipboardString, or until the library * is terminated. * * @thread_safety This function must only be called from the main thread. * * @sa @ref clipboard * @sa @ref glfwSetClipboardString * * @since Added in version 3.0. * * @ingroup input */ GLFWAPI const char* glfwGetClipboardString(GLFWwindow* window); /*! @brief Returns the GLFW time. * * This function returns the current GLFW time, in seconds. Unless the time * has been set using @ref glfwSetTime it measures time elapsed since GLFW was * initialized. * * This function and @ref glfwSetTime are helper functions on top of @ref * glfwGetTimerFrequency and @ref glfwGetTimerValue. * * The resolution of the timer is system dependent, but is usually on the order * of a few micro- or nanoseconds. It uses the highest-resolution monotonic * time source on each supported platform. * * @return The current time, in seconds, or zero if an * [error](@ref error_handling) occurred. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. * * @thread_safety This function may be called from any thread. Reading and * writing of the internal base time is not atomic, so it needs to be * externally synchronized with calls to @ref glfwSetTime. * * @sa @ref time * * @since Added in version 1.0. * * @ingroup input */ GLFWAPI double glfwGetTime(void); /*! @brief Sets the GLFW time. * * This function sets the current GLFW time, in seconds. The value must be * a positive finite number less than or equal to 18446744073.0, which is * approximately 584.5 years. * * This function and @ref glfwGetTime are helper functions on top of @ref * glfwGetTimerFrequency and @ref glfwGetTimerValue. * * @param[in] time The new value, in seconds. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref * GLFW_INVALID_VALUE. * * @remark The upper limit of GLFW time is calculated as * floor((264 - 1) / 109) and is due to implementations * storing nanoseconds in 64 bits. The limit may be increased in the future. * * @thread_safety This function may be called from any thread. Reading and * writing of the internal base time is not atomic, so it needs to be * externally synchronized with calls to @ref glfwGetTime. * * @sa @ref time * * @since Added in version 2.2. * * @ingroup input */ GLFWAPI void glfwSetTime(double time); /*! @brief Returns the current value of the raw timer. * * This function returns the current value of the raw timer, measured in * 1 / frequency seconds. To get the frequency, call @ref * glfwGetTimerFrequency. * * @return The value of the timer, or zero if an * [error](@ref error_handling) occurred. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. * * @thread_safety This function may be called from any thread. * * @sa @ref time * @sa @ref glfwGetTimerFrequency * * @since Added in version 3.2. * * @ingroup input */ GLFWAPI uint64_t glfwGetTimerValue(void); /*! @brief Returns the frequency, in Hz, of the raw timer. * * This function returns the frequency, in Hz, of the raw timer. * * @return The frequency of the timer, in Hz, or zero if an * [error](@ref error_handling) occurred. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. * * @thread_safety This function may be called from any thread. * * @sa @ref time * @sa @ref glfwGetTimerValue * * @since Added in version 3.2. * * @ingroup input */ GLFWAPI uint64_t glfwGetTimerFrequency(void); /*! @brief Makes the context of the specified window current for the calling * thread. * * This function makes the OpenGL or OpenGL ES context of the specified window * current on the calling thread. A context must only be made current on * a single thread at a time and each thread can have only a single current * context at a time. * * When moving a context between threads, you must make it non-current on the * old thread before making it current on the new one. * * By default, making a context non-current implicitly forces a pipeline flush. * On machines that support `GL_KHR_context_flush_control`, you can control * whether a context performs this flush by setting the * [GLFW_CONTEXT_RELEASE_BEHAVIOR](@ref GLFW_CONTEXT_RELEASE_BEHAVIOR_hint) * hint. * * The specified window must have an OpenGL or OpenGL ES context. Specifying * a window without a context will generate a @ref GLFW_NO_WINDOW_CONTEXT * error. * * @param[in] window The window whose context to make current, or `NULL` to * detach the current context. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref * GLFW_NO_WINDOW_CONTEXT and @ref GLFW_PLATFORM_ERROR. * * @thread_safety This function may be called from any thread. * * @sa @ref context_current * @sa @ref glfwGetCurrentContext * * @since Added in version 3.0. * * @ingroup context */ GLFWAPI void glfwMakeContextCurrent(GLFWwindow* window); /*! @brief Returns the window whose context is current on the calling thread. * * This function returns the window whose OpenGL or OpenGL ES context is * current on the calling thread. * * @return The window whose context is current, or `NULL` if no window's * context is current. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. * * @thread_safety This function may be called from any thread. * * @sa @ref context_current * @sa @ref glfwMakeContextCurrent * * @since Added in version 3.0. * * @ingroup context */ GLFWAPI GLFWwindow* glfwGetCurrentContext(void); /*! @brief Swaps the front and back buffers of the specified window. * * This function swaps the front and back buffers of the specified window when * rendering with OpenGL or OpenGL ES. If the swap interval is greater than * zero, the GPU driver waits the specified number of screen updates before * swapping the buffers. * * The specified window must have an OpenGL or OpenGL ES context. Specifying * a window without a context will generate a @ref GLFW_NO_WINDOW_CONTEXT * error. * * This function does not apply to Vulkan. If you are rendering with Vulkan, * see `vkQueuePresentKHR` instead. * * @param[in] window The window whose buffers to swap. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref * GLFW_NO_WINDOW_CONTEXT and @ref GLFW_PLATFORM_ERROR. * * @remark __EGL:__ The context of the specified window must be current on the * calling thread. * * @thread_safety This function may be called from any thread. * * @sa @ref buffer_swap * @sa @ref glfwSwapInterval * * @since Added in version 1.0. * @glfw3 Added window handle parameter. * * @ingroup window */ GLFWAPI void glfwSwapBuffers(GLFWwindow* window); /*! @brief Sets the swap interval for the current context. * * This function sets the swap interval for the current OpenGL or OpenGL ES * context, i.e. the number of screen updates to wait from the time @ref * glfwSwapBuffers was called before swapping the buffers and returning. This * is sometimes called _vertical synchronization_, _vertical retrace * synchronization_ or just _vsync_. * * A context that supports either of the `WGL_EXT_swap_control_tear` and * `GLX_EXT_swap_control_tear` extensions also accepts _negative_ swap * intervals, which allows the driver to swap immediately even if a frame * arrives a little bit late. You can check for these extensions with @ref * glfwExtensionSupported. * * A context must be current on the calling thread. Calling this function * without a current context will cause a @ref GLFW_NO_CURRENT_CONTEXT error. * * This function does not apply to Vulkan. If you are rendering with Vulkan, * see the present mode of your swapchain instead. * * @param[in] interval The minimum number of screen updates to wait for * until the buffers are swapped by @ref glfwSwapBuffers. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref * GLFW_NO_CURRENT_CONTEXT and @ref GLFW_PLATFORM_ERROR. * * @remark This function is not called during context creation, leaving the * swap interval set to whatever is the default on that platform. This is done * because some swap interval extensions used by GLFW do not allow the swap * interval to be reset to zero once it has been set to a non-zero value. * * @remark Some GPU drivers do not honor the requested swap interval, either * because of a user setting that overrides the application's request or due to * bugs in the driver. * * @thread_safety This function may be called from any thread. * * @sa @ref buffer_swap * @sa @ref glfwSwapBuffers * * @since Added in version 1.0. * * @ingroup context */ GLFWAPI void glfwSwapInterval(int interval); /*! @brief Returns whether the specified extension is available. * * This function returns whether the specified * [API extension](@ref context_glext) is supported by the current OpenGL or * OpenGL ES context. It searches both for client API extension and context * creation API extensions. * * A context must be current on the calling thread. Calling this function * without a current context will cause a @ref GLFW_NO_CURRENT_CONTEXT error. * * As this functions retrieves and searches one or more extension strings each * call, it is recommended that you cache its results if it is going to be used * frequently. The extension strings will not change during the lifetime of * a context, so there is no danger in doing this. * * This function does not apply to Vulkan. If you are using Vulkan, see @ref * glfwGetRequiredInstanceExtensions, `vkEnumerateInstanceExtensionProperties` * and `vkEnumerateDeviceExtensionProperties` instead. * * @param[in] extension The ASCII encoded name of the extension. * @return `GLFW_TRUE` if the extension is available, or `GLFW_FALSE` * otherwise. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref * GLFW_NO_CURRENT_CONTEXT, @ref GLFW_INVALID_VALUE and @ref * GLFW_PLATFORM_ERROR. * * @thread_safety This function may be called from any thread. * * @sa @ref context_glext * @sa @ref glfwGetProcAddress * * @since Added in version 1.0. * * @ingroup context */ GLFWAPI int glfwExtensionSupported(const char* extension); /*! @brief Returns the address of the specified function for the current * context. * * This function returns the address of the specified OpenGL or OpenGL ES * [core or extension function](@ref context_glext), if it is supported * by the current context. * * A context must be current on the calling thread. Calling this function * without a current context will cause a @ref GLFW_NO_CURRENT_CONTEXT error. * * This function does not apply to Vulkan. If you are rendering with Vulkan, * see @ref glfwGetInstanceProcAddress, `vkGetInstanceProcAddr` and * `vkGetDeviceProcAddr` instead. * * @param[in] procname The ASCII encoded name of the function. * @return The address of the function, or `NULL` if an * [error](@ref error_handling) occurred. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref * GLFW_NO_CURRENT_CONTEXT and @ref GLFW_PLATFORM_ERROR. * * @remark The address of a given function is not guaranteed to be the same * between contexts. * * @remark This function may return a non-`NULL` address despite the * associated version or extension not being available. Always check the * context version or extension string first. * * @pointer_lifetime The returned function pointer is valid until the context * is destroyed or the library is terminated. * * @thread_safety This function may be called from any thread. * * @sa @ref context_glext * @sa @ref glfwExtensionSupported * * @since Added in version 1.0. * * @ingroup context */ GLFWAPI GLFWglproc glfwGetProcAddress(const char* procname); /*! @brief Returns whether the Vulkan loader and an ICD have been found. * * This function returns whether the Vulkan loader and any minimally functional * ICD have been found. * * The availability of a Vulkan loader and even an ICD does not by itself guarantee that * surface creation or even instance creation is possible. Call @ref * glfwGetRequiredInstanceExtensions to check whether the extensions necessary for Vulkan * surface creation are available and @ref glfwGetPhysicalDevicePresentationSupport to * check whether a queue family of a physical device supports image presentation. * * @return `GLFW_TRUE` if Vulkan is minimally available, or `GLFW_FALSE` * otherwise. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. * * @thread_safety This function may be called from any thread. * * @sa @ref vulkan_support * * @since Added in version 3.2. * * @ingroup vulkan */ GLFWAPI int glfwVulkanSupported(void); /*! @brief Returns the Vulkan instance extensions required by GLFW. * * This function returns an array of names of Vulkan instance extensions required * by GLFW for creating Vulkan surfaces for GLFW windows. If successful, the * list will always contain `VK_KHR_surface`, so if you don't require any * additional extensions you can pass this list directly to the * `VkInstanceCreateInfo` struct. * * If Vulkan is not available on the machine, this function returns `NULL` and * generates a @ref GLFW_API_UNAVAILABLE error. Call @ref glfwVulkanSupported * to check whether Vulkan is at least minimally available. * * If Vulkan is available but no set of extensions allowing window surface * creation was found, this function returns `NULL`. You may still use Vulkan * for off-screen rendering and compute work. * * @param[out] count Where to store the number of extensions in the returned * array. This is set to zero if an error occurred. * @return An array of ASCII encoded extension names, or `NULL` if an * [error](@ref error_handling) occurred. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref * GLFW_API_UNAVAILABLE. * * @remark Additional extensions may be required by future versions of GLFW. * You should check if any extensions you wish to enable are already in the * returned array, as it is an error to specify an extension more than once in * the `VkInstanceCreateInfo` struct. * * @pointer_lifetime The returned array is allocated and freed by GLFW. You * should not free it yourself. It is guaranteed to be valid only until the * library is terminated. * * @thread_safety This function may be called from any thread. * * @sa @ref vulkan_ext * @sa @ref glfwCreateWindowSurface * * @since Added in version 3.2. * * @ingroup vulkan */ GLFWAPI const char** glfwGetRequiredInstanceExtensions(uint32_t* count); #if defined(VK_VERSION_1_0) /*! @brief Returns the address of the specified Vulkan instance function. * * This function returns the address of the specified Vulkan core or extension * function for the specified instance. If instance is set to `NULL` it can * return any function exported from the Vulkan loader, including at least the * following functions: * * - `vkEnumerateInstanceExtensionProperties` * - `vkEnumerateInstanceLayerProperties` * - `vkCreateInstance` * - `vkGetInstanceProcAddr` * * If Vulkan is not available on the machine, this function returns `NULL` and * generates a @ref GLFW_API_UNAVAILABLE error. Call @ref glfwVulkanSupported * to check whether Vulkan is at least minimally available. * * This function is equivalent to calling `vkGetInstanceProcAddr` with * a platform-specific query of the Vulkan loader as a fallback. * * @param[in] instance The Vulkan instance to query, or `NULL` to retrieve * functions related to instance creation. * @param[in] procname The ASCII encoded name of the function. * @return The address of the function, or `NULL` if an * [error](@ref error_handling) occurred. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref * GLFW_API_UNAVAILABLE. * * @pointer_lifetime The returned function pointer is valid until the library * is terminated. * * @thread_safety This function may be called from any thread. * * @sa @ref vulkan_proc * * @since Added in version 3.2. * * @ingroup vulkan */ GLFWAPI GLFWvkproc glfwGetInstanceProcAddress(VkInstance instance, const char* procname); /*! @brief Returns whether the specified queue family can present images. * * This function returns whether the specified queue family of the specified * physical device supports presentation to the platform GLFW was built for. * * If Vulkan or the required window surface creation instance extensions are * not available on the machine, or if the specified instance was not created * with the required extensions, this function returns `GLFW_FALSE` and * generates a @ref GLFW_API_UNAVAILABLE error. Call @ref glfwVulkanSupported * to check whether Vulkan is at least minimally available and @ref * glfwGetRequiredInstanceExtensions to check what instance extensions are * required. * * @param[in] instance The instance that the physical device belongs to. * @param[in] device The physical device that the queue family belongs to. * @param[in] queuefamily The index of the queue family to query. * @return `GLFW_TRUE` if the queue family supports presentation, or * `GLFW_FALSE` otherwise. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref * GLFW_API_UNAVAILABLE and @ref GLFW_PLATFORM_ERROR. * * @remark @macos This function currently always returns `GLFW_TRUE`, as the * `VK_MVK_macos_surface` and `VK_EXT_metal_surface` extensions do not provide * a `vkGetPhysicalDevice*PresentationSupport` type function. * * @thread_safety This function may be called from any thread. For * synchronization details of Vulkan objects, see the Vulkan specification. * * @sa @ref vulkan_present * * @since Added in version 3.2. * * @ingroup vulkan */ GLFWAPI int glfwGetPhysicalDevicePresentationSupport(VkInstance instance, VkPhysicalDevice device, uint32_t queuefamily); /*! @brief Creates a Vulkan surface for the specified window. * * This function creates a Vulkan surface for the specified window. * * If the Vulkan loader or at least one minimally functional ICD were not found, * this function returns `VK_ERROR_INITIALIZATION_FAILED` and generates a @ref * GLFW_API_UNAVAILABLE error. Call @ref glfwVulkanSupported to check whether * Vulkan is at least minimally available. * * If the required window surface creation instance extensions are not * available or if the specified instance was not created with these extensions * enabled, this function returns `VK_ERROR_EXTENSION_NOT_PRESENT` and * generates a @ref GLFW_API_UNAVAILABLE error. Call @ref * glfwGetRequiredInstanceExtensions to check what instance extensions are * required. * * The window surface cannot be shared with another API so the window must * have been created with the [client api hint](@ref GLFW_CLIENT_API_attrib) * set to `GLFW_NO_API` otherwise it generates a @ref GLFW_INVALID_VALUE error * and returns `VK_ERROR_NATIVE_WINDOW_IN_USE_KHR`. * * The window surface must be destroyed before the specified Vulkan instance. * It is the responsibility of the caller to destroy the window surface. GLFW * does not destroy it for you. Call `vkDestroySurfaceKHR` to destroy the * surface. * * @param[in] instance The Vulkan instance to create the surface in. * @param[in] window The window to create the surface for. * @param[in] allocator The allocator to use, or `NULL` to use the default * allocator. * @param[out] surface Where to store the handle of the surface. This is set * to `VK_NULL_HANDLE` if an error occurred. * @return `VK_SUCCESS` if successful, or a Vulkan error code if an * [error](@ref error_handling) occurred. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref * GLFW_API_UNAVAILABLE, @ref GLFW_PLATFORM_ERROR and @ref GLFW_INVALID_VALUE * * @remark If an error occurs before the creation call is made, GLFW returns * the Vulkan error code most appropriate for the error. Appropriate use of * @ref glfwVulkanSupported and @ref glfwGetRequiredInstanceExtensions should * eliminate almost all occurrences of these errors. * * @remark @macos GLFW prefers the `VK_EXT_metal_surface` extension, with the * `VK_MVK_macos_surface` extension as a fallback. The name of the selected * extension, if any, is included in the array returned by @ref * glfwGetRequiredInstanceExtensions. * * @remark @macos This function creates and sets a `CAMetalLayer` instance for * the window content view, which is required for MoltenVK to function. * * @thread_safety This function may be called from any thread. For * synchronization details of Vulkan objects, see the Vulkan specification. * * @sa @ref vulkan_surface * @sa @ref glfwGetRequiredInstanceExtensions * * @since Added in version 3.2. * * @ingroup vulkan */ GLFWAPI VkResult glfwCreateWindowSurface(VkInstance instance, GLFWwindow* window, const VkAllocationCallbacks* allocator, VkSurfaceKHR* surface); #endif /*VK_VERSION_1_0*/ /************************************************************************* * Global definition cleanup *************************************************************************/ /* ------------------- BEGIN SYSTEM/COMPILER SPECIFIC -------------------- */ #ifdef GLFW_WINGDIAPI_DEFINED #undef WINGDIAPI #undef GLFW_WINGDIAPI_DEFINED #endif #ifdef GLFW_CALLBACK_DEFINED #undef CALLBACK #undef GLFW_CALLBACK_DEFINED #endif /* Some OpenGL related headers need GLAPIENTRY, but it is unconditionally * defined by some gl.h variants (OpenBSD) so define it after if needed. */ #ifndef GLAPIENTRY #define GLAPIENTRY APIENTRY #endif /* -------------------- END SYSTEM/COMPILER SPECIFIC --------------------- */ #ifdef __cplusplus } #endif #endif /* _glfw3_h_ */ /************************************************************************* * GLFW 3.3.7 - www.glfw.org * A library for OpenGL, window and input *------------------------------------------------------------------------ * Copyright (c) 2002-2006 Marcus Geelnard * Copyright (c) 2006-2018 Camilla Löwy * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages * arising from the use of this software. * * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: * * 1. The origin of this software must not be misrepresented; you must not * claim that you wrote the original software. If you use this software * in a product, an acknowledgment in the product documentation would * be appreciated but is not required. * * 2. Altered source versions must be plainly marked as such, and must not * be misrepresented as being the original software. * * 3. This notice may not be removed or altered from any source * distribution. * *************************************************************************/ #ifndef _glfw3_native_h_ #define _glfw3_native_h_ #ifdef __cplusplus extern "C" { #endif /************************************************************************* * Doxygen documentation *************************************************************************/ /*! @file glfw3native.h * @brief The header of the native access functions. * * This is the header file of the native access functions. See @ref native for * more information. */ /*! @defgroup native Native access * @brief Functions related to accessing native handles. * * **By using the native access functions you assert that you know what you're * doing and how to fix problems caused by using them. If you don't, you * shouldn't be using them.** * * Before the inclusion of @ref glfw3native.h, you may define zero or more * window system API macro and zero or more context creation API macros. * * The chosen backends must match those the library was compiled for. Failure * to do this will cause a link-time error. * * The available window API macros are: * * `GLFW_EXPOSE_NATIVE_WIN32` * * `GLFW_EXPOSE_NATIVE_COCOA` * * `GLFW_EXPOSE_NATIVE_X11` * * `GLFW_EXPOSE_NATIVE_WAYLAND` * * The available context API macros are: * * `GLFW_EXPOSE_NATIVE_WGL` * * `GLFW_EXPOSE_NATIVE_NSGL` * * `GLFW_EXPOSE_NATIVE_GLX` * * `GLFW_EXPOSE_NATIVE_EGL` * * `GLFW_EXPOSE_NATIVE_OSMESA` * * These macros select which of the native access functions that are declared * and which platform-specific headers to include. It is then up your (by * definition platform-specific) code to handle which of these should be * defined. */ /************************************************************************* * System headers and types *************************************************************************/ #if defined(GLFW_EXPOSE_NATIVE_WIN32) || defined(GLFW_EXPOSE_NATIVE_WGL) // This is a workaround for the fact that glfw3.h needs to export APIENTRY (for // example to allow applications to correctly declare a GL_KHR_debug callback) // but windows.h assumes no one will define APIENTRY before it does #if defined(GLFW_APIENTRY_DEFINED) #undef APIENTRY #undef GLFW_APIENTRY_DEFINED #endif #include #elif defined(GLFW_EXPOSE_NATIVE_COCOA) || defined(GLFW_EXPOSE_NATIVE_NSGL) #if defined(__OBJC__) #import #else #include typedef void* id; #endif #elif defined(GLFW_EXPOSE_NATIVE_X11) || defined(GLFW_EXPOSE_NATIVE_GLX) #include #include #elif defined(GLFW_EXPOSE_NATIVE_WAYLAND) #include #endif #if defined(GLFW_EXPOSE_NATIVE_WGL) /* WGL is declared by windows.h */ #endif #if defined(GLFW_EXPOSE_NATIVE_NSGL) /* NSGL is declared by Cocoa.h */ #endif #if defined(GLFW_EXPOSE_NATIVE_GLX) #include #endif #if defined(GLFW_EXPOSE_NATIVE_EGL) #include #endif #if defined(GLFW_EXPOSE_NATIVE_OSMESA) #include #endif /************************************************************************* * Functions *************************************************************************/ #if defined(GLFW_EXPOSE_NATIVE_WIN32) /*! @brief Returns the adapter device name of the specified monitor. * * @return The UTF-8 encoded adapter device name (for example `\\.\DISPLAY1`) * of the specified monitor, or `NULL` if an [error](@ref error_handling) * occurred. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. * * @thread_safety This function may be called from any thread. Access is not * synchronized. * * @since Added in version 3.1. * * @ingroup native */ GLFWAPI const char* glfwGetWin32Adapter(GLFWmonitor* monitor); /*! @brief Returns the display device name of the specified monitor. * * @return The UTF-8 encoded display device name (for example * `\\.\DISPLAY1\Monitor0`) of the specified monitor, or `NULL` if an * [error](@ref error_handling) occurred. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. * * @thread_safety This function may be called from any thread. Access is not * synchronized. * * @since Added in version 3.1. * * @ingroup native */ GLFWAPI const char* glfwGetWin32Monitor(GLFWmonitor* monitor); /*! @brief Returns the `HWND` of the specified window. * * @return The `HWND` of the specified window, or `NULL` if an * [error](@ref error_handling) occurred. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. * * @remark The `HDC` associated with the window can be queried with the * [GetDC](https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getdc) * function. * @code * HDC dc = GetDC(glfwGetWin32Window(window)); * @endcode * This DC is private and does not need to be released. * * @thread_safety This function may be called from any thread. Access is not * synchronized. * * @since Added in version 3.0. * * @ingroup native */ GLFWAPI HWND glfwGetWin32Window(GLFWwindow* window); #endif #if defined(GLFW_EXPOSE_NATIVE_WGL) /*! @brief Returns the `HGLRC` of the specified window. * * @return The `HGLRC` of the specified window, or `NULL` if an * [error](@ref error_handling) occurred. * * @errors Possible errors include @ref GLFW_NO_WINDOW_CONTEXT and @ref * GLFW_NOT_INITIALIZED. * * @remark The `HDC` associated with the window can be queried with the * [GetDC](https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getdc) * function. * @code * HDC dc = GetDC(glfwGetWin32Window(window)); * @endcode * This DC is private and does not need to be released. * * @thread_safety This function may be called from any thread. Access is not * synchronized. * * @since Added in version 3.0. * * @ingroup native */ GLFWAPI HGLRC glfwGetWGLContext(GLFWwindow* window); #endif #if defined(GLFW_EXPOSE_NATIVE_COCOA) /*! @brief Returns the `CGDirectDisplayID` of the specified monitor. * * @return The `CGDirectDisplayID` of the specified monitor, or * `kCGNullDirectDisplay` if an [error](@ref error_handling) occurred. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. * * @thread_safety This function may be called from any thread. Access is not * synchronized. * * @since Added in version 3.1. * * @ingroup native */ GLFWAPI CGDirectDisplayID glfwGetCocoaMonitor(GLFWmonitor* monitor); /*! @brief Returns the `NSWindow` of the specified window. * * @return The `NSWindow` of the specified window, or `nil` if an * [error](@ref error_handling) occurred. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. * * @thread_safety This function may be called from any thread. Access is not * synchronized. * * @since Added in version 3.0. * * @ingroup native */ GLFWAPI id glfwGetCocoaWindow(GLFWwindow* window); #endif #if defined(GLFW_EXPOSE_NATIVE_NSGL) /*! @brief Returns the `NSOpenGLContext` of the specified window. * * @return The `NSOpenGLContext` of the specified window, or `nil` if an * [error](@ref error_handling) occurred. * * @errors Possible errors include @ref GLFW_NO_WINDOW_CONTEXT and @ref * GLFW_NOT_INITIALIZED. * * @thread_safety This function may be called from any thread. Access is not * synchronized. * * @since Added in version 3.0. * * @ingroup native */ GLFWAPI id glfwGetNSGLContext(GLFWwindow* window); #endif #if defined(GLFW_EXPOSE_NATIVE_X11) /*! @brief Returns the `Display` used by GLFW. * * @return The `Display` used by GLFW, or `NULL` if an * [error](@ref error_handling) occurred. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. * * @thread_safety This function may be called from any thread. Access is not * synchronized. * * @since Added in version 3.0. * * @ingroup native */ GLFWAPI Display* glfwGetX11Display(void); /*! @brief Returns the `RRCrtc` of the specified monitor. * * @return The `RRCrtc` of the specified monitor, or `None` if an * [error](@ref error_handling) occurred. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. * * @thread_safety This function may be called from any thread. Access is not * synchronized. * * @since Added in version 3.1. * * @ingroup native */ GLFWAPI RRCrtc glfwGetX11Adapter(GLFWmonitor* monitor); /*! @brief Returns the `RROutput` of the specified monitor. * * @return The `RROutput` of the specified monitor, or `None` if an * [error](@ref error_handling) occurred. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. * * @thread_safety This function may be called from any thread. Access is not * synchronized. * * @since Added in version 3.1. * * @ingroup native */ GLFWAPI RROutput glfwGetX11Monitor(GLFWmonitor* monitor); /*! @brief Returns the `Window` of the specified window. * * @return The `Window` of the specified window, or `None` if an * [error](@ref error_handling) occurred. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. * * @thread_safety This function may be called from any thread. Access is not * synchronized. * * @since Added in version 3.0. * * @ingroup native */ GLFWAPI Window glfwGetX11Window(GLFWwindow* window); /*! @brief Sets the current primary selection to the specified string. * * @param[in] string A UTF-8 encoded string. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref * GLFW_PLATFORM_ERROR. * * @pointer_lifetime The specified string is copied before this function * returns. * * @thread_safety This function must only be called from the main thread. * * @sa @ref clipboard * @sa glfwGetX11SelectionString * @sa glfwSetClipboardString * * @since Added in version 3.3. * * @ingroup native */ GLFWAPI void glfwSetX11SelectionString(const char* string); /*! @brief Returns the contents of the current primary selection as a string. * * If the selection is empty or if its contents cannot be converted, `NULL` * is returned and a @ref GLFW_FORMAT_UNAVAILABLE error is generated. * * @return The contents of the selection as a UTF-8 encoded string, or `NULL` * if an [error](@ref error_handling) occurred. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref * GLFW_PLATFORM_ERROR. * * @pointer_lifetime The returned string is allocated and freed by GLFW. You * should not free it yourself. It is valid until the next call to @ref * glfwGetX11SelectionString or @ref glfwSetX11SelectionString, or until the * library is terminated. * * @thread_safety This function must only be called from the main thread. * * @sa @ref clipboard * @sa glfwSetX11SelectionString * @sa glfwGetClipboardString * * @since Added in version 3.3. * * @ingroup native */ GLFWAPI const char* glfwGetX11SelectionString(void); #endif #if defined(GLFW_EXPOSE_NATIVE_GLX) /*! @brief Returns the `GLXContext` of the specified window. * * @return The `GLXContext` of the specified window, or `NULL` if an * [error](@ref error_handling) occurred. * * @errors Possible errors include @ref GLFW_NO_WINDOW_CONTEXT and @ref * GLFW_NOT_INITIALIZED. * * @thread_safety This function may be called from any thread. Access is not * synchronized. * * @since Added in version 3.0. * * @ingroup native */ GLFWAPI GLXContext glfwGetGLXContext(GLFWwindow* window); /*! @brief Returns the `GLXWindow` of the specified window. * * @return The `GLXWindow` of the specified window, or `None` if an * [error](@ref error_handling) occurred. * * @errors Possible errors include @ref GLFW_NO_WINDOW_CONTEXT and @ref * GLFW_NOT_INITIALIZED. * * @thread_safety This function may be called from any thread. Access is not * synchronized. * * @since Added in version 3.2. * * @ingroup native */ GLFWAPI GLXWindow glfwGetGLXWindow(GLFWwindow* window); #endif #if defined(GLFW_EXPOSE_NATIVE_WAYLAND) /*! @brief Returns the `struct wl_display*` used by GLFW. * * @return The `struct wl_display*` used by GLFW, or `NULL` if an * [error](@ref error_handling) occurred. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. * * @thread_safety This function may be called from any thread. Access is not * synchronized. * * @since Added in version 3.2. * * @ingroup native */ GLFWAPI struct wl_display* glfwGetWaylandDisplay(void); /*! @brief Returns the `struct wl_output*` of the specified monitor. * * @return The `struct wl_output*` of the specified monitor, or `NULL` if an * [error](@ref error_handling) occurred. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. * * @thread_safety This function may be called from any thread. Access is not * synchronized. * * @since Added in version 3.2. * * @ingroup native */ GLFWAPI struct wl_output* glfwGetWaylandMonitor(GLFWmonitor* monitor); /*! @brief Returns the main `struct wl_surface*` of the specified window. * * @return The main `struct wl_surface*` of the specified window, or `NULL` if * an [error](@ref error_handling) occurred. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. * * @thread_safety This function may be called from any thread. Access is not * synchronized. * * @since Added in version 3.2. * * @ingroup native */ GLFWAPI struct wl_surface* glfwGetWaylandWindow(GLFWwindow* window); #endif #if defined(GLFW_EXPOSE_NATIVE_EGL) /*! @brief Returns the `EGLDisplay` used by GLFW. * * @return The `EGLDisplay` used by GLFW, or `EGL_NO_DISPLAY` if an * [error](@ref error_handling) occurred. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. * * @thread_safety This function may be called from any thread. Access is not * synchronized. * * @since Added in version 3.0. * * @ingroup native */ GLFWAPI EGLDisplay glfwGetEGLDisplay(void); /*! @brief Returns the `EGLContext` of the specified window. * * @return The `EGLContext` of the specified window, or `EGL_NO_CONTEXT` if an * [error](@ref error_handling) occurred. * * @errors Possible errors include @ref GLFW_NO_WINDOW_CONTEXT and @ref * GLFW_NOT_INITIALIZED. * * @thread_safety This function may be called from any thread. Access is not * synchronized. * * @since Added in version 3.0. * * @ingroup native */ GLFWAPI EGLContext glfwGetEGLContext(GLFWwindow* window); /*! @brief Returns the `EGLSurface` of the specified window. * * @return The `EGLSurface` of the specified window, or `EGL_NO_SURFACE` if an * [error](@ref error_handling) occurred. * * @errors Possible errors include @ref GLFW_NO_WINDOW_CONTEXT and @ref * GLFW_NOT_INITIALIZED. * * @thread_safety This function may be called from any thread. Access is not * synchronized. * * @since Added in version 3.0. * * @ingroup native */ GLFWAPI EGLSurface glfwGetEGLSurface(GLFWwindow* window); #endif #if defined(GLFW_EXPOSE_NATIVE_OSMESA) /*! @brief Retrieves the color buffer associated with the specified window. * * @param[in] window The window whose color buffer to retrieve. * @param[out] width Where to store the width of the color buffer, or `NULL`. * @param[out] height Where to store the height of the color buffer, or `NULL`. * @param[out] format Where to store the OSMesa pixel format of the color * buffer, or `NULL`. * @param[out] buffer Where to store the address of the color buffer, or * `NULL`. * @return `GLFW_TRUE` if successful, or `GLFW_FALSE` if an * [error](@ref error_handling) occurred. * * @errors Possible errors include @ref GLFW_NO_WINDOW_CONTEXT and @ref * GLFW_NOT_INITIALIZED. * * @thread_safety This function may be called from any thread. Access is not * synchronized. * * @since Added in version 3.3. * * @ingroup native */ GLFWAPI int glfwGetOSMesaColorBuffer(GLFWwindow* window, int* width, int* height, int* format, void** buffer); /*! @brief Retrieves the depth buffer associated with the specified window. * * @param[in] window The window whose depth buffer to retrieve. * @param[out] width Where to store the width of the depth buffer, or `NULL`. * @param[out] height Where to store the height of the depth buffer, or `NULL`. * @param[out] bytesPerValue Where to store the number of bytes per depth * buffer element, or `NULL`. * @param[out] buffer Where to store the address of the depth buffer, or * `NULL`. * @return `GLFW_TRUE` if successful, or `GLFW_FALSE` if an * [error](@ref error_handling) occurred. * * @errors Possible errors include @ref GLFW_NO_WINDOW_CONTEXT and @ref * GLFW_NOT_INITIALIZED. * * @thread_safety This function may be called from any thread. Access is not * synchronized. * * @since Added in version 3.3. * * @ingroup native */ GLFWAPI int glfwGetOSMesaDepthBuffer(GLFWwindow* window, int* width, int* height, int* bytesPerValue, void** buffer); /*! @brief Returns the `OSMesaContext` of the specified window. * * @return The `OSMesaContext` of the specified window, or `NULL` if an * [error](@ref error_handling) occurred. * * @errors Possible errors include @ref GLFW_NO_WINDOW_CONTEXT and @ref * GLFW_NOT_INITIALIZED. * * @thread_safety This function may be called from any thread. Access is not * synchronized. * * @since Added in version 3.3. * * @ingroup native */ GLFWAPI OSMesaContext glfwGetOSMesaContext(GLFWwindow* window); #endif #ifdef __cplusplus } #endif #endif /* _glfw3_native_h_ */ #ifdef _GLFW_IMPLEMENTATION #ifndef HEADER_GUARD_INTERNAL_H #define HEADER_GUARD_INTERNAL_H //======================================================================== // GLFW 3.3.7 - www.glfw.org //------------------------------------------------------------------------ // Copyright (c) 2002-2006 Marcus Geelnard // Copyright (c) 2006-2019 Camilla Löwy // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would // be appreciated but is not required. // // 2. Altered source versions must be plainly marked as such, and must not // be misrepresented as being the original software. // // 3. This notice may not be removed or altered from any source // distribution. // //======================================================================== #pragma once #if defined(_GLFW_USE_CONFIG_H) #include "glfw_config.h" #endif #if defined(GLFW_INCLUDE_GLCOREARB) || \ defined(GLFW_INCLUDE_ES1) || \ defined(GLFW_INCLUDE_ES2) || \ defined(GLFW_INCLUDE_ES3) || \ defined(GLFW_INCLUDE_ES31) || \ defined(GLFW_INCLUDE_ES32) || \ defined(GLFW_INCLUDE_NONE) || \ defined(GLFW_INCLUDE_GLEXT) || \ defined(GLFW_INCLUDE_GLU) || \ defined(GLFW_INCLUDE_VULKAN) || \ defined(GLFW_DLL) //#error "You must not define any header option macros when compiling GLFW" #endif #define GLFW_INCLUDE_NONE //#include "../include/GLFW/glfw3.h" #define _GLFW_INSERT_FIRST 0 #define _GLFW_INSERT_LAST 1 #define _GLFW_POLL_PRESENCE 0 #define _GLFW_POLL_AXES 1 #define _GLFW_POLL_BUTTONS 2 #define _GLFW_POLL_ALL (_GLFW_POLL_AXES | _GLFW_POLL_BUTTONS) #define _GLFW_MESSAGE_SIZE 1024 typedef int GLFWbool; typedef struct _GLFWerror _GLFWerror; typedef struct _GLFWinitconfig _GLFWinitconfig; typedef struct _GLFWwndconfig _GLFWwndconfig; typedef struct _GLFWctxconfig _GLFWctxconfig; typedef struct _GLFWfbconfig _GLFWfbconfig; typedef struct _GLFWcontext _GLFWcontext; typedef struct _GLFWwindow _GLFWwindow; typedef struct _GLFWlibrary _GLFWlibrary; typedef struct _GLFWmonitor _GLFWmonitor; typedef struct _GLFWcursor _GLFWcursor; typedef struct _GLFWmapelement _GLFWmapelement; typedef struct _GLFWmapping _GLFWmapping; typedef struct _GLFWjoystick _GLFWjoystick; typedef struct _GLFWtls _GLFWtls; typedef struct _GLFWmutex _GLFWmutex; typedef void (* _GLFWmakecontextcurrentfun)(_GLFWwindow*); typedef void (* _GLFWswapbuffersfun)(_GLFWwindow*); typedef void (* _GLFWswapintervalfun)(int); typedef int (* _GLFWextensionsupportedfun)(const char*); typedef GLFWglproc (* _GLFWgetprocaddressfun)(const char*); typedef void (* _GLFWdestroycontextfun)(_GLFWwindow*); //#define GL_VERSION 0x1f02 #define GL_NONE 0 #define GL_COLOR_BUFFER_BIT 0x00004000 #define GL_UNSIGNED_BYTE 0x1401 //#define GL_EXTENSIONS 0x1f03 //#define GL_NUM_EXTENSIONS 0x821d //#define GL_CONTEXT_FLAGS 0x821e #define GL_CONTEXT_FLAG_FORWARD_COMPATIBLE_BIT 0x00000001 #define GL_CONTEXT_FLAG_DEBUG_BIT 0x00000002 #define GL_CONTEXT_PROFILE_MASK 0x9126 #define GL_CONTEXT_COMPATIBILITY_PROFILE_BIT 0x00000002 #define GL_CONTEXT_CORE_PROFILE_BIT 0x00000001 #define GL_RESET_NOTIFICATION_STRATEGY_ARB 0x8256 #define GL_LOSE_CONTEXT_ON_RESET_ARB 0x8252 #define GL_NO_RESET_NOTIFICATION_ARB 0x8261 //#define GL_CONTEXT_RELEASE_BEHAVIOR 0x82fb ////#define GL_CONTEXT_RELEASE_BEHAVIOR_FLUSH 0x82fc #define GL_CONTEXT_FLAG_NO_ERROR_BIT_KHR 0x00000008 typedef int GLint; typedef unsigned int GLuint; typedef unsigned int GLenum; typedef unsigned int GLbitfield; typedef unsigned char GLubyte; typedef void (APIENTRY * PFNGLCLEARPROC)(GLbitfield); typedef const GLubyte* (APIENTRY * PFNGLGETSTRINGPROC)(GLenum); typedef void (APIENTRY * PFNGLGETINTEGERVPROC)(GLenum,GLint*); typedef const GLubyte* (APIENTRY * PFNGLGETSTRINGIPROC)(GLenum,GLuint); #define VK_NULL_HANDLE 0 typedef void* VkInstance; typedef void* VkPhysicalDevice; typedef uint64_t VkSurfaceKHR; typedef uint32_t VkFlags; typedef uint32_t VkBool32; typedef enum VkStructureType { VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR = 1000004000, VK_STRUCTURE_TYPE_XCB_SURFACE_CREATE_INFO_KHR = 1000005000, VK_STRUCTURE_TYPE_WAYLAND_SURFACE_CREATE_INFO_KHR = 1000006000, VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR = 1000009000, VK_STRUCTURE_TYPE_MACOS_SURFACE_CREATE_INFO_MVK = 1000123000, VK_STRUCTURE_TYPE_METAL_SURFACE_CREATE_INFO_EXT = 1000217000, VK_STRUCTURE_TYPE_MAX_ENUM = 0x7FFFFFFF } VkStructureType; typedef enum VkResult { VK_SUCCESS = 0, VK_NOT_READY = 1, VK_TIMEOUT = 2, VK_EVENT_SET = 3, VK_EVENT_RESET = 4, VK_INCOMPLETE = 5, VK_ERROR_OUT_OF_HOST_MEMORY = -1, VK_ERROR_OUT_OF_DEVICE_MEMORY = -2, VK_ERROR_INITIALIZATION_FAILED = -3, VK_ERROR_DEVICE_LOST = -4, VK_ERROR_MEMORY_MAP_FAILED = -5, VK_ERROR_LAYER_NOT_PRESENT = -6, VK_ERROR_EXTENSION_NOT_PRESENT = -7, VK_ERROR_FEATURE_NOT_PRESENT = -8, VK_ERROR_INCOMPATIBLE_DRIVER = -9, VK_ERROR_TOO_MANY_OBJECTS = -10, VK_ERROR_FORMAT_NOT_SUPPORTED = -11, VK_ERROR_SURFACE_LOST_KHR = -1000000000, VK_SUBOPTIMAL_KHR = 1000001003, VK_ERROR_OUT_OF_DATE_KHR = -1000001004, VK_ERROR_INCOMPATIBLE_DISPLAY_KHR = -1000003001, VK_ERROR_NATIVE_WINDOW_IN_USE_KHR = -1000000001, VK_ERROR_VALIDATION_FAILED_EXT = -1000011001, VK_RESULT_MAX_ENUM = 0x7FFFFFFF } VkResult; typedef struct VkAllocationCallbacks VkAllocationCallbacks; typedef struct VkExtensionProperties { char extensionName[256]; uint32_t specVersion; } VkExtensionProperties; typedef void (APIENTRY * PFN_vkVoidFunction)(void); #if defined(_GLFW_VULKAN_STATIC) PFN_vkVoidFunction vkGetInstanceProcAddr(VkInstance,const char*); VkResult vkEnumerateInstanceExtensionProperties(const char*,uint32_t*,VkExtensionProperties*); #else typedef PFN_vkVoidFunction (APIENTRY * PFN_vkGetInstanceProcAddr)(VkInstance,const char*); typedef VkResult (APIENTRY * PFN_vkEnumerateInstanceExtensionProperties)(const char*,uint32_t*,VkExtensionProperties*); #define vkEnumerateInstanceExtensionProperties _glfw.vk.EnumerateInstanceExtensionProperties #define vkGetInstanceProcAddr _glfw.vk.GetInstanceProcAddr #endif #if defined(_GLFW_COCOA) #ifndef HEADER_GUARD_COCOA_PLATFORM_H #define HEADER_GUARD_COCOA_PLATFORM_H //======================================================================== // GLFW 3.3.7 macOS - www.glfw.org //------------------------------------------------------------------------ // Copyright (c) 2009-2019 Camilla Löwy // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would // be appreciated but is not required. // // 2. Altered source versions must be plainly marked as such, and must not // be misrepresented as being the original software. // // 3. This notice may not be removed or altered from any source // distribution. // //======================================================================== #include #include #include // NOTE: All of NSGL was deprecated in the 10.14 SDK // This disables the pointless warnings for every symbol we use #ifndef GL_SILENCE_DEPRECATION #define GL_SILENCE_DEPRECATION #endif #if defined(__OBJC__) #import #else typedef void* id; #endif // NOTE: Many Cocoa enum values have been renamed and we need to build across // SDK versions where one is unavailable or deprecated. // We use the newer names in code and replace them with the older names if // the base SDK does not provide the newer names. #if MAC_OS_X_VERSION_MAX_ALLOWED < 101200 #define NSBitmapFormatAlphaNonpremultiplied NSAlphaNonpremultipliedBitmapFormat #define NSEventMaskAny NSAnyEventMask #define NSEventMaskKeyUp NSKeyUpMask #define NSEventModifierFlagCapsLock NSAlphaShiftKeyMask #define NSEventModifierFlagCommand NSCommandKeyMask #define NSEventModifierFlagControl NSControlKeyMask #define NSEventModifierFlagDeviceIndependentFlagsMask NSDeviceIndependentModifierFlagsMask #define NSEventModifierFlagOption NSAlternateKeyMask #define NSEventModifierFlagShift NSShiftKeyMask #define NSEventTypeApplicationDefined NSApplicationDefined #define NSWindowStyleMaskBorderless NSBorderlessWindowMask #define NSWindowStyleMaskClosable NSClosableWindowMask #define NSWindowStyleMaskMiniaturizable NSMiniaturizableWindowMask #define NSWindowStyleMaskResizable NSResizableWindowMask #define NSWindowStyleMaskTitled NSTitledWindowMask #endif // NOTE: Many Cocoa dynamically linked constants have been renamed and we need // to build across SDK versions where one is unavailable or deprecated. // We use the newer names in code and replace them with the older names if // the deployment target is older than the newer names. #if MAC_OS_X_VERSION_MIN_REQUIRED < 101300 #define NSPasteboardTypeURL NSURLPboardType #endif typedef VkFlags VkMacOSSurfaceCreateFlagsMVK; typedef VkFlags VkMetalSurfaceCreateFlagsEXT; typedef struct VkMacOSSurfaceCreateInfoMVK { VkStructureType sType; const void* pNext; VkMacOSSurfaceCreateFlagsMVK flags; const void* pView; } VkMacOSSurfaceCreateInfoMVK; typedef struct VkMetalSurfaceCreateInfoEXT { VkStructureType sType; const void* pNext; VkMetalSurfaceCreateFlagsEXT flags; const void* pLayer; } VkMetalSurfaceCreateInfoEXT; typedef VkResult (APIENTRY *PFN_vkCreateMacOSSurfaceMVK)(VkInstance,const VkMacOSSurfaceCreateInfoMVK*,const VkAllocationCallbacks*,VkSurfaceKHR*); typedef VkResult (APIENTRY *PFN_vkCreateMetalSurfaceEXT)(VkInstance,const VkMetalSurfaceCreateInfoEXT*,const VkAllocationCallbacks*,VkSurfaceKHR*); #ifndef HEADER_GUARD_POSIX_THREAD_H #define HEADER_GUARD_POSIX_THREAD_H //======================================================================== // GLFW 3.3.7 POSIX - www.glfw.org //------------------------------------------------------------------------ // Copyright (c) 2002-2006 Marcus Geelnard // Copyright (c) 2006-2017 Camilla Löwy // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would // be appreciated but is not required. // // 2. Altered source versions must be plainly marked as such, and must not // be misrepresented as being the original software. // // 3. This notice may not be removed or altered from any source // distribution. // //======================================================================== #include #define _GLFW_PLATFORM_TLS_STATE _GLFWtlsPOSIX posix #define _GLFW_PLATFORM_MUTEX_STATE _GLFWmutexPOSIX posix // POSIX-specific thread local storage data // typedef struct _GLFWtlsPOSIX { GLFWbool allocated; pthread_key_t key; } _GLFWtlsPOSIX; // POSIX-specific mutex data // typedef struct _GLFWmutexPOSIX { GLFWbool allocated; pthread_mutex_t handle; } _GLFWmutexPOSIX; #endif #ifndef HEADER_GUARD_COCOA_JOYSTICK_H #define HEADER_GUARD_COCOA_JOYSTICK_H //======================================================================== // GLFW 3.3.7 Cocoa - www.glfw.org //------------------------------------------------------------------------ // Copyright (c) 2006-2017 Camilla Löwy // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would // be appreciated but is not required. // // 2. Altered source versions must be plainly marked as such, and must not // be misrepresented as being the original software. // // 3. This notice may not be removed or altered from any source // distribution. // //======================================================================== #include #include #include #include #define _GLFW_PLATFORM_JOYSTICK_STATE _GLFWjoystickNS ns #define _GLFW_PLATFORM_LIBRARY_JOYSTICK_STATE struct { int dummyJoystick; } #define _GLFW_PLATFORM_MAPPING_NAME "Mac OS X" #define GLFW_BUILD_COCOA_MAPPINGS // Cocoa-specific per-joystick data // typedef struct _GLFWjoystickNS { IOHIDDeviceRef device; CFMutableArrayRef axes; CFMutableArrayRef buttons; CFMutableArrayRef hats; } _GLFWjoystickNS; void _glfwInitJoysticksNS(void); void _glfwTerminateJoysticksNS(void); #endif #ifndef HEADER_GUARD_NSGL_CONTEXT_H #define HEADER_GUARD_NSGL_CONTEXT_H //======================================================================== // GLFW 3.3.7 macOS - www.glfw.org //------------------------------------------------------------------------ // Copyright (c) 2009-2019 Camilla Löwy // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would // be appreciated but is not required. // // 2. Altered source versions must be plainly marked as such, and must not // be misrepresented as being the original software. // // 3. This notice may not be removed or altered from any source // distribution. // //======================================================================== // NOTE: Many Cocoa enum values have been renamed and we need to build across // SDK versions where one is unavailable or deprecated. // We use the newer names in code and replace them with the older names if // the base SDK does not provide the newer names. #if MAC_OS_X_VERSION_MAX_ALLOWED < 101400 #define NSOpenGLContextParameterSwapInterval NSOpenGLCPSwapInterval #define NSOpenGLContextParameterSurfaceOpacity NSOpenGLCPSurfaceOpacity #endif #define _GLFW_PLATFORM_CONTEXT_STATE _GLFWcontextNSGL nsgl #define _GLFW_PLATFORM_LIBRARY_CONTEXT_STATE _GLFWlibraryNSGL nsgl #include // NSGL-specific per-context data // typedef struct _GLFWcontextNSGL { id pixelFormat; id object; } _GLFWcontextNSGL; // NSGL-specific global data // typedef struct _GLFWlibraryNSGL { // dlopen handle for OpenGL.framework (for glfwGetProcAddress) CFBundleRef framework; } _GLFWlibraryNSGL; GLFWbool _glfwInitNSGL(void); void _glfwTerminateNSGL(void); GLFWbool _glfwCreateContextNSGL(_GLFWwindow* window, const _GLFWctxconfig* ctxconfig, const _GLFWfbconfig* fbconfig); void _glfwDestroyContextNSGL(_GLFWwindow* window); #endif #ifndef HEADER_GUARD_EGL_CONTEXT_H #define HEADER_GUARD_EGL_CONTEXT_H //======================================================================== // GLFW 3.3.7 EGL - www.glfw.org //------------------------------------------------------------------------ // Copyright (c) 2002-2006 Marcus Geelnard // Copyright (c) 2006-2017 Camilla Löwy // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would // be appreciated but is not required. // // 2. Altered source versions must be plainly marked as such, and must not // be misrepresented as being the original software. // // 3. This notice may not be removed or altered from any source // distribution. // //======================================================================== #if defined(_GLFW_USE_EGLPLATFORM_H) #include #elif defined(_GLFW_WIN32) #define EGLAPIENTRY __stdcall typedef HDC EGLNativeDisplayType; typedef HWND EGLNativeWindowType; #elif defined(_GLFW_COCOA) #define EGLAPIENTRY typedef void* EGLNativeDisplayType; typedef id EGLNativeWindowType; #elif defined(_GLFW_X11) #define EGLAPIENTRY typedef Display* EGLNativeDisplayType; typedef Window EGLNativeWindowType; #elif defined(_GLFW_WAYLAND) #define EGLAPIENTRY typedef struct wl_display* EGLNativeDisplayType; typedef struct wl_egl_window* EGLNativeWindowType; #else #error "No supported EGL platform selected" #endif #define EGL_SUCCESS 0x3000 #define EGL_NOT_INITIALIZED 0x3001 #define EGL_BAD_ACCESS 0x3002 #define EGL_BAD_ALLOC 0x3003 #define EGL_BAD_ATTRIBUTE 0x3004 #define EGL_BAD_CONFIG 0x3005 #define EGL_BAD_CONTEXT 0x3006 #define EGL_BAD_CURRENT_SURFACE 0x3007 #define EGL_BAD_DISPLAY 0x3008 #define EGL_BAD_MATCH 0x3009 #define EGL_BAD_NATIVE_PIXMAP 0x300a #define EGL_BAD_NATIVE_WINDOW 0x300b #define EGL_BAD_PARAMETER 0x300c #define EGL_BAD_SURFACE 0x300d #define EGL_CONTEXT_LOST 0x300e #define EGL_COLOR_BUFFER_TYPE 0x303f #define EGL_RGB_BUFFER 0x308e #define EGL_SURFACE_TYPE 0x3033 #define EGL_WINDOW_BIT 0x0004 #define EGL_RENDERABLE_TYPE 0x3040 #define EGL_OPENGL_ES_BIT 0x0001 #define EGL_OPENGL_ES2_BIT 0x0004 #define EGL_OPENGL_BIT 0x0008 #define EGL_ALPHA_SIZE 0x3021 #define EGL_BLUE_SIZE 0x3022 #define EGL_GREEN_SIZE 0x3023 #define EGL_RED_SIZE 0x3024 #define EGL_DEPTH_SIZE 0x3025 #define EGL_STENCIL_SIZE 0x3026 #define EGL_SAMPLES 0x3031 #define EGL_OPENGL_ES_API 0x30a0 #define EGL_OPENGL_API 0x30a2 #define EGL_NONE 0x3038 #define EGL_RENDER_BUFFER 0x3086 #define EGL_SINGLE_BUFFER 0x3085 #define EGL_EXTENSIONS 0x3055 #define EGL_CONTEXT_CLIENT_VERSION 0x3098 #define EGL_NATIVE_VISUAL_ID 0x302e #define EGL_NO_SURFACE ((EGLSurface) 0) #define EGL_NO_DISPLAY ((EGLDisplay) 0) #define EGL_NO_CONTEXT ((EGLContext) 0) #define EGL_DEFAULT_DISPLAY ((EGLNativeDisplayType) 0) #define EGL_CONTEXT_OPENGL_FORWARD_COMPATIBLE_BIT_KHR 0x00000002 #define EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT_KHR 0x00000001 #define EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT_KHR 0x00000002 #define EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR 0x00000001 #define EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_KHR 0x31bd #define EGL_NO_RESET_NOTIFICATION_KHR 0x31be #define EGL_LOSE_CONTEXT_ON_RESET_KHR 0x31bf #define EGL_CONTEXT_OPENGL_ROBUST_ACCESS_BIT_KHR 0x00000004 #define EGL_CONTEXT_MAJOR_VERSION_KHR 0x3098 #define EGL_CONTEXT_MINOR_VERSION_KHR 0x30fb #define EGL_CONTEXT_OPENGL_PROFILE_MASK_KHR 0x30fd #define EGL_CONTEXT_FLAGS_KHR 0x30fc #define EGL_CONTEXT_OPENGL_NO_ERROR_KHR 0x31b3 #define EGL_GL_COLORSPACE_KHR 0x309d #define EGL_GL_COLORSPACE_SRGB_KHR 0x3089 #define EGL_CONTEXT_RELEASE_BEHAVIOR_KHR 0x2097 #define EGL_CONTEXT_RELEASE_BEHAVIOR_NONE_KHR 0 #define EGL_CONTEXT_RELEASE_BEHAVIOR_FLUSH_KHR 0x2098 #define EGL_PRESENT_OPAQUE_EXT 0x31df typedef int EGLint; typedef unsigned int EGLBoolean; typedef unsigned int EGLenum; typedef void* EGLConfig; typedef void* EGLContext; typedef void* EGLDisplay; typedef void* EGLSurface; // EGL function pointer typedefs typedef EGLBoolean (EGLAPIENTRY * PFN_eglGetConfigAttrib)(EGLDisplay,EGLConfig,EGLint,EGLint*); typedef EGLBoolean (EGLAPIENTRY * PFN_eglGetConfigs)(EGLDisplay,EGLConfig*,EGLint,EGLint*); typedef EGLDisplay (EGLAPIENTRY * PFN_eglGetDisplay)(EGLNativeDisplayType); typedef EGLint (EGLAPIENTRY * PFN_eglGetError)(void); typedef EGLBoolean (EGLAPIENTRY * PFN_eglInitialize)(EGLDisplay,EGLint*,EGLint*); typedef EGLBoolean (EGLAPIENTRY * PFN_eglTerminate)(EGLDisplay); typedef EGLBoolean (EGLAPIENTRY * PFN_eglBindAPI)(EGLenum); typedef EGLContext (EGLAPIENTRY * PFN_eglCreateContext)(EGLDisplay,EGLConfig,EGLContext,const EGLint*); typedef EGLBoolean (EGLAPIENTRY * PFN_eglDestroySurface)(EGLDisplay,EGLSurface); typedef EGLBoolean (EGLAPIENTRY * PFN_eglDestroyContext)(EGLDisplay,EGLContext); typedef EGLSurface (EGLAPIENTRY * PFN_eglCreateWindowSurface)(EGLDisplay,EGLConfig,EGLNativeWindowType,const EGLint*); typedef EGLBoolean (EGLAPIENTRY * PFN_eglMakeCurrent)(EGLDisplay,EGLSurface,EGLSurface,EGLContext); typedef EGLBoolean (EGLAPIENTRY * PFN_eglSwapBuffers)(EGLDisplay,EGLSurface); typedef EGLBoolean (EGLAPIENTRY * PFN_eglSwapInterval)(EGLDisplay,EGLint); typedef const char* (EGLAPIENTRY * PFN_eglQueryString)(EGLDisplay,EGLint); typedef GLFWglproc (EGLAPIENTRY * PFN_eglGetProcAddress)(const char*); #define eglGetConfigAttrib _glfw.egl.GetConfigAttrib #define eglGetConfigs _glfw.egl.GetConfigs #define eglGetDisplay _glfw.egl.GetDisplay #define eglGetError _glfw.egl.GetError #define eglInitialize _glfw.egl.Initialize #define eglTerminate _glfw.egl.Terminate #define eglBindAPI _glfw.egl.BindAPI #define eglCreateContext _glfw.egl.CreateContext #define eglDestroySurface _glfw.egl.DestroySurface #define eglDestroyContext _glfw.egl.DestroyContext #define eglCreateWindowSurface _glfw.egl.CreateWindowSurface #define eglMakeCurrent _glfw.egl.MakeCurrent #define eglSwapBuffers _glfw.egl.SwapBuffers #define eglSwapInterval _glfw.egl.SwapInterval #define eglQueryString _glfw.egl.QueryString #define eglGetProcAddress _glfw.egl.GetProcAddress #define _GLFW_EGL_CONTEXT_STATE _GLFWcontextEGL egl #define _GLFW_EGL_LIBRARY_CONTEXT_STATE _GLFWlibraryEGL egl // EGL-specific per-context data // typedef struct _GLFWcontextEGL { EGLConfig config; EGLContext handle; EGLSurface surface; void* client; } _GLFWcontextEGL; // EGL-specific global data // typedef struct _GLFWlibraryEGL { EGLDisplay display; EGLint major, minor; GLFWbool prefix; GLFWbool KHR_create_context; GLFWbool KHR_create_context_no_error; GLFWbool KHR_gl_colorspace; GLFWbool KHR_get_all_proc_addresses; GLFWbool KHR_context_flush_control; GLFWbool EXT_present_opaque; void* handle; PFN_eglGetConfigAttrib GetConfigAttrib; PFN_eglGetConfigs GetConfigs; PFN_eglGetDisplay GetDisplay; PFN_eglGetError GetError; PFN_eglInitialize Initialize; PFN_eglTerminate Terminate; PFN_eglBindAPI BindAPI; PFN_eglCreateContext CreateContext; PFN_eglDestroySurface DestroySurface; PFN_eglDestroyContext DestroyContext; PFN_eglCreateWindowSurface CreateWindowSurface; PFN_eglMakeCurrent MakeCurrent; PFN_eglSwapBuffers SwapBuffers; PFN_eglSwapInterval SwapInterval; PFN_eglQueryString QueryString; PFN_eglGetProcAddress GetProcAddress; } _GLFWlibraryEGL; GLFWbool _glfwInitEGL(void); void _glfwTerminateEGL(void); GLFWbool _glfwCreateContextEGL(_GLFWwindow* window, const _GLFWctxconfig* ctxconfig, const _GLFWfbconfig* fbconfig); #if defined(_GLFW_X11) GLFWbool _glfwChooseVisualEGL(const _GLFWwndconfig* wndconfig, const _GLFWctxconfig* ctxconfig, const _GLFWfbconfig* fbconfig, Visual** visual, int* depth); #endif /*_GLFW_X11*/ #endif #ifndef HEADER_GUARD_OSMESA_CONTEXT_H #define HEADER_GUARD_OSMESA_CONTEXT_H //======================================================================== // GLFW 3.3.7 OSMesa - www.glfw.org //------------------------------------------------------------------------ // Copyright (c) 2016 Google Inc. // Copyright (c) 2016-2017 Camilla Löwy // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would // be appreciated but is not required. // // 2. Altered source versions must be plainly marked as such, and must not // be misrepresented as being the original software. // // 3. This notice may not be removed or altered from any source // distribution. // //======================================================================== #define OSMESA_RGBA 0x1908 #define OSMESA_FORMAT 0x22 #define OSMESA_DEPTH_BITS 0x30 #define OSMESA_STENCIL_BITS 0x31 #define OSMESA_ACCUM_BITS 0x32 #define OSMESA_PROFILE 0x33 #define OSMESA_CORE_PROFILE 0x34 #define OSMESA_COMPAT_PROFILE 0x35 #define OSMESA_CONTEXT_MAJOR_VERSION 0x36 #define OSMESA_CONTEXT_MINOR_VERSION 0x37 typedef void* OSMesaContext; typedef void (*OSMESAproc)(void); typedef OSMesaContext (GLAPIENTRY * PFN_OSMesaCreateContextExt)(GLenum,GLint,GLint,GLint,OSMesaContext); typedef OSMesaContext (GLAPIENTRY * PFN_OSMesaCreateContextAttribs)(const int*,OSMesaContext); typedef void (GLAPIENTRY * PFN_OSMesaDestroyContext)(OSMesaContext); typedef int (GLAPIENTRY * PFN_OSMesaMakeCurrent)(OSMesaContext,void*,int,int,int); typedef int (GLAPIENTRY * PFN_OSMesaGetColorBuffer)(OSMesaContext,int*,int*,int*,void**); typedef int (GLAPIENTRY * PFN_OSMesaGetDepthBuffer)(OSMesaContext,int*,int*,int*,void**); typedef GLFWglproc (GLAPIENTRY * PFN_OSMesaGetProcAddress)(const char*); #define OSMesaCreateContextExt _glfw.osmesa.CreateContextExt #define OSMesaCreateContextAttribs _glfw.osmesa.CreateContextAttribs #define OSMesaDestroyContext _glfw.osmesa.DestroyContext #define OSMesaMakeCurrent _glfw.osmesa.MakeCurrent #define OSMesaGetColorBuffer _glfw.osmesa.GetColorBuffer #define OSMesaGetDepthBuffer _glfw.osmesa.GetDepthBuffer #define OSMesaGetProcAddress _glfw.osmesa.GetProcAddress #define _GLFW_OSMESA_CONTEXT_STATE _GLFWcontextOSMesa osmesa #define _GLFW_OSMESA_LIBRARY_CONTEXT_STATE _GLFWlibraryOSMesa osmesa // OSMesa-specific per-context data // typedef struct _GLFWcontextOSMesa { OSMesaContext handle; int width; int height; void* buffer; } _GLFWcontextOSMesa; // OSMesa-specific global data // typedef struct _GLFWlibraryOSMesa { void* handle; PFN_OSMesaCreateContextExt CreateContextExt; PFN_OSMesaCreateContextAttribs CreateContextAttribs; PFN_OSMesaDestroyContext DestroyContext; PFN_OSMesaMakeCurrent MakeCurrent; PFN_OSMesaGetColorBuffer GetColorBuffer; PFN_OSMesaGetDepthBuffer GetDepthBuffer; PFN_OSMesaGetProcAddress GetProcAddress; } _GLFWlibraryOSMesa; GLFWbool _glfwInitOSMesa(void); void _glfwTerminateOSMesa(void); GLFWbool _glfwCreateContextOSMesa(_GLFWwindow* window, const _GLFWctxconfig* ctxconfig, const _GLFWfbconfig* fbconfig); #endif #define _glfw_dlopen(name) dlopen(name, RTLD_LAZY | RTLD_LOCAL) #define _glfw_dlclose(handle) dlclose(handle) #define _glfw_dlsym(handle, name) dlsym(handle, name) #define _GLFW_EGL_NATIVE_WINDOW ((EGLNativeWindowType) window->ns.layer) #define _GLFW_EGL_NATIVE_DISPLAY EGL_DEFAULT_DISPLAY #define _GLFW_PLATFORM_WINDOW_STATE _GLFWwindowNS ns #define _GLFW_PLATFORM_LIBRARY_WINDOW_STATE _GLFWlibraryNS ns #define _GLFW_PLATFORM_LIBRARY_TIMER_STATE _GLFWtimerNS ns #define _GLFW_PLATFORM_MONITOR_STATE _GLFWmonitorNS ns #define _GLFW_PLATFORM_CURSOR_STATE _GLFWcursorNS ns // HIToolbox.framework pointer typedefs #define kTISPropertyUnicodeKeyLayoutData _glfw.ns.tis.kPropertyUnicodeKeyLayoutData typedef TISInputSourceRef (*PFN_TISCopyCurrentKeyboardLayoutInputSource)(void); #define TISCopyCurrentKeyboardLayoutInputSource _glfw.ns.tis.CopyCurrentKeyboardLayoutInputSource typedef void* (*PFN_TISGetInputSourceProperty)(TISInputSourceRef,CFStringRef); #define TISGetInputSourceProperty _glfw.ns.tis.GetInputSourceProperty typedef UInt8 (*PFN_LMGetKbdType)(void); #define LMGetKbdType _glfw.ns.tis.GetKbdType // Cocoa-specific per-window data // typedef struct _GLFWwindowNS { id object; id delegate; id view; id layer; GLFWbool maximized; GLFWbool occluded; GLFWbool retina; // Cached window properties to filter out duplicate events int width, height; int fbWidth, fbHeight; float xscale, yscale; // The total sum of the distances the cursor has been warped // since the last cursor motion event was processed // This is kept to counteract Cocoa doing the same internally double cursorWarpDeltaX, cursorWarpDeltaY; } _GLFWwindowNS; // Cocoa-specific global data // typedef struct _GLFWlibraryNS { CGEventSourceRef eventSource; id delegate; GLFWbool finishedLaunching; GLFWbool cursorHidden; TISInputSourceRef inputSource; IOHIDManagerRef hidManager; id unicodeData; id helper; id keyUpMonitor; id nibObjects; char keynames[GLFW_KEY_LAST + 1][17]; short int keycodes[256]; short int scancodes[GLFW_KEY_LAST + 1]; char* clipboardString; CGPoint cascadePoint; // Where to place the cursor when re-enabled double restoreCursorPosX, restoreCursorPosY; // The window whose disabled cursor mode is active _GLFWwindow* disabledCursorWindow; struct { CFBundleRef bundle; PFN_TISCopyCurrentKeyboardLayoutInputSource CopyCurrentKeyboardLayoutInputSource; PFN_TISGetInputSourceProperty GetInputSourceProperty; PFN_LMGetKbdType GetKbdType; CFStringRef kPropertyUnicodeKeyLayoutData; } tis; } _GLFWlibraryNS; // Cocoa-specific per-monitor data // typedef struct _GLFWmonitorNS { CGDirectDisplayID displayID; CGDisplayModeRef previousMode; uint32_t unitNumber; id screen; double fallbackRefreshRate; } _GLFWmonitorNS; // Cocoa-specific per-cursor data // typedef struct _GLFWcursorNS { id object; } _GLFWcursorNS; // Cocoa-specific global timer data // typedef struct _GLFWtimerNS { uint64_t frequency; } _GLFWtimerNS; void _glfwInitTimerNS(void); void _glfwPollMonitorsNS(void); void _glfwSetVideoModeNS(_GLFWmonitor* monitor, const GLFWvidmode* desired); void _glfwRestoreVideoModeNS(_GLFWmonitor* monitor); float _glfwTransformYNS(float y); void* _glfwLoadLocalVulkanLoaderNS(void); #endif #elif defined(_GLFW_WIN32) #ifndef HEADER_GUARD_WIN32_PLATFORM_H #define HEADER_GUARD_WIN32_PLATFORM_H //======================================================================== // GLFW 3.3.7 Win32 - www.glfw.org //------------------------------------------------------------------------ // Copyright (c) 2002-2006 Marcus Geelnard // Copyright (c) 2006-2019 Camilla Löwy // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would // be appreciated but is not required. // // 2. Altered source versions must be plainly marked as such, and must not // be misrepresented as being the original software. // // 3. This notice may not be removed or altered from any source // distribution. // //======================================================================== // We don't need all the fancy stuff #ifndef NOMINMAX #define NOMINMAX #endif #ifndef VC_EXTRALEAN #define VC_EXTRALEAN #endif #ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN #endif // This is a workaround for the fact that glfw3.h needs to export APIENTRY (for // example to allow applications to correctly declare a GL_KHR_debug callback) // but windows.h assumes no one will define APIENTRY before it does #undef APIENTRY // GLFW on Windows is Unicode only and does not work in MBCS mode #ifndef UNICODE #define UNICODE #endif // GLFW requires Windows XP or later #if WINVER < 0x0501 #undef WINVER #define WINVER 0x0501 #endif #if _WIN32_WINNT < 0x0501 #undef _WIN32_WINNT #define _WIN32_WINNT 0x0501 #endif // GLFW uses DirectInput8 interfaces #define DIRECTINPUT_VERSION 0x0800 // GLFW uses OEM cursor resources #define OEMRESOURCE #include #include #include #include #include // HACK: Define macros that some windows.h variants don't #ifndef WM_MOUSEHWHEEL #define WM_MOUSEHWHEEL 0x020E #endif #ifndef WM_DWMCOMPOSITIONCHANGED #define WM_DWMCOMPOSITIONCHANGED 0x031E #endif #ifndef WM_DWMCOLORIZATIONCOLORCHANGED #define WM_DWMCOLORIZATIONCOLORCHANGED 0x0320 #endif #ifndef WM_COPYGLOBALDATA #define WM_COPYGLOBALDATA 0x0049 #endif #ifndef WM_UNICHAR #define WM_UNICHAR 0x0109 #endif #ifndef UNICODE_NOCHAR #define UNICODE_NOCHAR 0xFFFF #endif #ifndef WM_DPICHANGED #define WM_DPICHANGED 0x02E0 #endif #ifndef GET_XBUTTON_WPARAM #define GET_XBUTTON_WPARAM(w) (HIWORD(w)) #endif #ifndef EDS_ROTATEDMODE #define EDS_ROTATEDMODE 0x00000004 #endif #ifndef DISPLAY_DEVICE_ACTIVE #define DISPLAY_DEVICE_ACTIVE 0x00000001 #endif #ifndef _WIN32_WINNT_WINBLUE #define _WIN32_WINNT_WINBLUE 0x0603 #endif #ifndef _WIN32_WINNT_WIN8 #define _WIN32_WINNT_WIN8 0x0602 #endif #ifndef WM_GETDPISCALEDSIZE #define WM_GETDPISCALEDSIZE 0x02e4 #endif #ifndef USER_DEFAULT_SCREEN_DPI #define USER_DEFAULT_SCREEN_DPI 96 #endif #ifndef OCR_HAND #define OCR_HAND 32649 #endif #if WINVER < 0x0601 typedef struct { DWORD cbSize; DWORD ExtStatus; } CHANGEFILTERSTRUCT; #ifndef MSGFLT_ALLOW #define MSGFLT_ALLOW 1 #endif #endif /*Windows 7*/ #if WINVER < 0x0600 #define DWM_BB_ENABLE 0x00000001 #define DWM_BB_BLURREGION 0x00000002 typedef struct { DWORD dwFlags; BOOL fEnable; HRGN hRgnBlur; BOOL fTransitionOnMaximized; } DWM_BLURBEHIND; #else #include #endif /*Windows Vista*/ #ifndef DPI_ENUMS_DECLARED typedef enum { PROCESS_DPI_UNAWARE = 0, PROCESS_SYSTEM_DPI_AWARE = 1, PROCESS_PER_MONITOR_DPI_AWARE = 2 } PROCESS_DPI_AWARENESS; typedef enum { MDT_EFFECTIVE_DPI = 0, MDT_ANGULAR_DPI = 1, MDT_RAW_DPI = 2, MDT_DEFAULT = MDT_EFFECTIVE_DPI } MONITOR_DPI_TYPE; #endif /*DPI_ENUMS_DECLARED*/ #ifndef DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 #define DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 ((HANDLE) -4) #endif /*DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2*/ // Replacement for versionhelpers.h macros, as we cannot rely on the // application having a correct embedded manifest // #define IsWindowsXPOrGreater() \ _glfwIsWindowsVersionOrGreaterWin32(HIBYTE(_WIN32_WINNT_WINXP), \ LOBYTE(_WIN32_WINNT_WINXP), 0) #define IsWindowsVistaOrGreater() \ _glfwIsWindowsVersionOrGreaterWin32(HIBYTE(_WIN32_WINNT_VISTA), \ LOBYTE(_WIN32_WINNT_VISTA), 0) #define IsWindows7OrGreater() \ _glfwIsWindowsVersionOrGreaterWin32(HIBYTE(_WIN32_WINNT_WIN7), \ LOBYTE(_WIN32_WINNT_WIN7), 0) #define IsWindows8OrGreater() \ _glfwIsWindowsVersionOrGreaterWin32(HIBYTE(_WIN32_WINNT_WIN8), \ LOBYTE(_WIN32_WINNT_WIN8), 0) #define IsWindows8Point1OrGreater() \ _glfwIsWindowsVersionOrGreaterWin32(HIBYTE(_WIN32_WINNT_WINBLUE), \ LOBYTE(_WIN32_WINNT_WINBLUE), 0) #define _glfwIsWindows10AnniversaryUpdateOrGreaterWin32() \ _glfwIsWindows10BuildOrGreaterWin32(14393) #define _glfwIsWindows10CreatorsUpdateOrGreaterWin32() \ _glfwIsWindows10BuildOrGreaterWin32(15063) // HACK: Define macros that some xinput.h variants don't #ifndef XINPUT_CAPS_WIRELESS #define XINPUT_CAPS_WIRELESS 0x0002 #endif #ifndef XINPUT_DEVSUBTYPE_WHEEL #define XINPUT_DEVSUBTYPE_WHEEL 0x02 #endif #ifndef XINPUT_DEVSUBTYPE_ARCADE_STICK #define XINPUT_DEVSUBTYPE_ARCADE_STICK 0x03 #endif #ifndef XINPUT_DEVSUBTYPE_FLIGHT_STICK #define XINPUT_DEVSUBTYPE_FLIGHT_STICK 0x04 #endif #ifndef XINPUT_DEVSUBTYPE_DANCE_PAD #define XINPUT_DEVSUBTYPE_DANCE_PAD 0x05 #endif #ifndef XINPUT_DEVSUBTYPE_GUITAR #define XINPUT_DEVSUBTYPE_GUITAR 0x06 #endif #ifndef XINPUT_DEVSUBTYPE_DRUM_KIT #define XINPUT_DEVSUBTYPE_DRUM_KIT 0x08 #endif #ifndef XINPUT_DEVSUBTYPE_ARCADE_PAD #define XINPUT_DEVSUBTYPE_ARCADE_PAD 0x13 #endif #ifndef XUSER_MAX_COUNT #define XUSER_MAX_COUNT 4 #endif // HACK: Define macros that some dinput.h variants don't #ifndef DIDFT_OPTIONAL #define DIDFT_OPTIONAL 0x80000000 #endif // xinput.dll function pointer typedefs typedef DWORD (WINAPI * PFN_XInputGetCapabilities)(DWORD,DWORD,XINPUT_CAPABILITIES*); typedef DWORD (WINAPI * PFN_XInputGetState)(DWORD,XINPUT_STATE*); #define XInputGetCapabilities _glfw.win32.xinput.GetCapabilities #define XInputGetState _glfw.win32.xinput.GetState // dinput8.dll function pointer typedefs typedef HRESULT (WINAPI * PFN_DirectInput8Create)(HINSTANCE,DWORD,REFIID,LPVOID*,LPUNKNOWN); #define DirectInput8Create _glfw.win32.dinput8.Create // user32.dll function pointer typedefs typedef BOOL (WINAPI * PFN_SetProcessDPIAware)(void); typedef BOOL (WINAPI * PFN_ChangeWindowMessageFilterEx)(HWND,UINT,DWORD,CHANGEFILTERSTRUCT*); typedef BOOL (WINAPI * PFN_EnableNonClientDpiScaling)(HWND); typedef BOOL (WINAPI * PFN_SetProcessDpiAwarenessContext)(HANDLE); typedef UINT (WINAPI * PFN_GetDpiForWindow)(HWND); typedef BOOL (WINAPI * PFN_AdjustWindowRectExForDpi)(LPRECT,DWORD,BOOL,DWORD,UINT); typedef int (WINAPI * PFN_GetSystemMetricsForDpi)(int,UINT); #define SetProcessDPIAware _glfw.win32.user32.SetProcessDPIAware_ #define ChangeWindowMessageFilterEx _glfw.win32.user32.ChangeWindowMessageFilterEx_ #define EnableNonClientDpiScaling _glfw.win32.user32.EnableNonClientDpiScaling_ #define SetProcessDpiAwarenessContext _glfw.win32.user32.SetProcessDpiAwarenessContext_ #define GetDpiForWindow _glfw.win32.user32.GetDpiForWindow_ #define AdjustWindowRectExForDpi _glfw.win32.user32.AdjustWindowRectExForDpi_ #define GetSystemMetricsForDpi _glfw.win32.user32.GetSystemMetricsForDpi_ // dwmapi.dll function pointer typedefs typedef HRESULT (WINAPI * PFN_DwmIsCompositionEnabled)(BOOL*); typedef HRESULT (WINAPI * PFN_DwmFlush)(VOID); typedef HRESULT(WINAPI * PFN_DwmEnableBlurBehindWindow)(HWND,const DWM_BLURBEHIND*); typedef HRESULT (WINAPI * PFN_DwmGetColorizationColor)(DWORD*,BOOL*); #define DwmIsCompositionEnabled _glfw.win32.dwmapi.IsCompositionEnabled #define DwmFlush _glfw.win32.dwmapi.Flush #define DwmEnableBlurBehindWindow _glfw.win32.dwmapi.EnableBlurBehindWindow #define DwmGetColorizationColor _glfw.win32.dwmapi.GetColorizationColor // shcore.dll function pointer typedefs typedef HRESULT (WINAPI * PFN_SetProcessDpiAwareness)(PROCESS_DPI_AWARENESS); typedef HRESULT (WINAPI * PFN_GetDpiForMonitor)(HMONITOR,MONITOR_DPI_TYPE,UINT*,UINT*); #define SetProcessDpiAwareness _glfw.win32.shcore.SetProcessDpiAwareness_ #define GetDpiForMonitor _glfw.win32.shcore.GetDpiForMonitor_ // ntdll.dll function pointer typedefs typedef LONG (WINAPI * PFN_RtlVerifyVersionInfo)(OSVERSIONINFOEXW*,ULONG,ULONGLONG); #define RtlVerifyVersionInfo _glfw.win32.ntdll.RtlVerifyVersionInfo_ typedef VkFlags VkWin32SurfaceCreateFlagsKHR; typedef struct VkWin32SurfaceCreateInfoKHR { VkStructureType sType; const void* pNext; VkWin32SurfaceCreateFlagsKHR flags; HINSTANCE hinstance; HWND hwnd; } VkWin32SurfaceCreateInfoKHR; typedef VkResult (APIENTRY *PFN_vkCreateWin32SurfaceKHR)(VkInstance,const VkWin32SurfaceCreateInfoKHR*,const VkAllocationCallbacks*,VkSurfaceKHR*); typedef VkBool32 (APIENTRY *PFN_vkGetPhysicalDeviceWin32PresentationSupportKHR)(VkPhysicalDevice,uint32_t); #ifndef HEADER_GUARD_WIN32_JOYSTICK_H #define HEADER_GUARD_WIN32_JOYSTICK_H //======================================================================== // GLFW 3.3.7 Win32 - www.glfw.org //------------------------------------------------------------------------ // Copyright (c) 2006-2017 Camilla Löwy // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would // be appreciated but is not required. // // 2. Altered source versions must be plainly marked as such, and must not // be misrepresented as being the original software. // // 3. This notice may not be removed or altered from any source // distribution. // //======================================================================== #define _GLFW_PLATFORM_JOYSTICK_STATE _GLFWjoystickWin32 win32 #define _GLFW_PLATFORM_LIBRARY_JOYSTICK_STATE struct { int dummyLibraryJoystick; } #define _GLFW_PLATFORM_MAPPING_NAME "Windows" #define GLFW_BUILD_WIN32_MAPPINGS // Joystick element (axis, button or slider) // typedef struct _GLFWjoyobjectWin32 { int offset; int type; } _GLFWjoyobjectWin32; // Win32-specific per-joystick data // typedef struct _GLFWjoystickWin32 { _GLFWjoyobjectWin32* objects; int objectCount; IDirectInputDevice8W* device; DWORD index; GUID guid; } _GLFWjoystickWin32; void _glfwInitJoysticksWin32(void); void _glfwTerminateJoysticksWin32(void); void _glfwDetectJoystickConnectionWin32(void); void _glfwDetectJoystickDisconnectionWin32(void); #endif #ifndef HEADER_GUARD_WGL_CONTEXT_H #define HEADER_GUARD_WGL_CONTEXT_H //======================================================================== // GLFW 3.3.7 WGL - www.glfw.org //------------------------------------------------------------------------ // Copyright (c) 2002-2006 Marcus Geelnard // Copyright (c) 2006-2018 Camilla Löwy // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would // be appreciated but is not required. // // 2. Altered source versions must be plainly marked as such, and must not // be misrepresented as being the original software. // // 3. This notice may not be removed or altered from any source // distribution. // //======================================================================== #define WGL_NUMBER_PIXEL_FORMATS_ARB 0x2000 #define WGL_SUPPORT_OPENGL_ARB 0x2010 #define WGL_DRAW_TO_WINDOW_ARB 0x2001 #define WGL_PIXEL_TYPE_ARB 0x2013 #define WGL_TYPE_RGBA_ARB 0x202b #define WGL_ACCELERATION_ARB 0x2003 #define WGL_NO_ACCELERATION_ARB 0x2025 #define WGL_RED_BITS_ARB 0x2015 #define WGL_RED_SHIFT_ARB 0x2016 #define WGL_GREEN_BITS_ARB 0x2017 #define WGL_GREEN_SHIFT_ARB 0x2018 #define WGL_BLUE_BITS_ARB 0x2019 #define WGL_BLUE_SHIFT_ARB 0x201a #define WGL_ALPHA_BITS_ARB 0x201b #define WGL_ALPHA_SHIFT_ARB 0x201c #define WGL_ACCUM_BITS_ARB 0x201d #define WGL_ACCUM_RED_BITS_ARB 0x201e #define WGL_ACCUM_GREEN_BITS_ARB 0x201f #define WGL_ACCUM_BLUE_BITS_ARB 0x2020 #define WGL_ACCUM_ALPHA_BITS_ARB 0x2021 #define WGL_DEPTH_BITS_ARB 0x2022 #define WGL_STENCIL_BITS_ARB 0x2023 #define WGL_AUX_BUFFERS_ARB 0x2024 #define WGL_STEREO_ARB 0x2012 #define WGL_DOUBLE_BUFFER_ARB 0x2011 #define WGL_SAMPLES_ARB 0x2042 #define WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB 0x20a9 #define WGL_CONTEXT_DEBUG_BIT_ARB 0x00000001 #define WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB 0x00000002 #define WGL_CONTEXT_PROFILE_MASK_ARB 0x9126 #define WGL_CONTEXT_CORE_PROFILE_BIT_ARB 0x00000001 #define WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB 0x00000002 #define WGL_CONTEXT_MAJOR_VERSION_ARB 0x2091 #define WGL_CONTEXT_MINOR_VERSION_ARB 0x2092 #define WGL_CONTEXT_FLAGS_ARB 0x2094 #define WGL_CONTEXT_ES2_PROFILE_BIT_EXT 0x00000004 #define WGL_CONTEXT_ROBUST_ACCESS_BIT_ARB 0x00000004 #define WGL_LOSE_CONTEXT_ON_RESET_ARB 0x8252 #define WGL_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB 0x8256 #define WGL_NO_RESET_NOTIFICATION_ARB 0x8261 #define WGL_CONTEXT_RELEASE_BEHAVIOR_ARB 0x2097 #define WGL_CONTEXT_RELEASE_BEHAVIOR_NONE_ARB 0 #define WGL_CONTEXT_RELEASE_BEHAVIOR_FLUSH_ARB 0x2098 #define WGL_CONTEXT_OPENGL_NO_ERROR_ARB 0x31b3 #define WGL_COLORSPACE_EXT 0x309d #define WGL_COLORSPACE_SRGB_EXT 0x3089 #define ERROR_INVALID_VERSION_ARB 0x2095 #define ERROR_INVALID_PROFILE_ARB 0x2096 #define ERROR_INCOMPATIBLE_DEVICE_CONTEXTS_ARB 0x2054 // WGL extension pointer typedefs typedef BOOL (WINAPI * PFNWGLSWAPINTERVALEXTPROC)(int); typedef BOOL (WINAPI * PFNWGLGETPIXELFORMATATTRIBIVARBPROC)(HDC,int,int,UINT,const int*,int*); typedef const char* (WINAPI * PFNWGLGETEXTENSIONSSTRINGEXTPROC)(void); typedef const char* (WINAPI * PFNWGLGETEXTENSIONSSTRINGARBPROC)(HDC); typedef HGLRC (WINAPI * PFNWGLCREATECONTEXTATTRIBSARBPROC)(HDC,HGLRC,const int*); #define wglSwapIntervalEXT _glfw.wgl.SwapIntervalEXT #define wglGetPixelFormatAttribivARB _glfw.wgl.GetPixelFormatAttribivARB #define wglGetExtensionsStringEXT _glfw.wgl.GetExtensionsStringEXT #define wglGetExtensionsStringARB _glfw.wgl.GetExtensionsStringARB #define wglCreateContextAttribsARB _glfw.wgl.CreateContextAttribsARB // opengl32.dll function pointer typedefs typedef HGLRC (WINAPI * PFN_wglCreateContext)(HDC); typedef BOOL (WINAPI * PFN_wglDeleteContext)(HGLRC); typedef PROC (WINAPI * PFN_wglGetProcAddress)(LPCSTR); typedef HDC (WINAPI * PFN_wglGetCurrentDC)(void); typedef HGLRC (WINAPI * PFN_wglGetCurrentContext)(void); typedef BOOL (WINAPI * PFN_wglMakeCurrent)(HDC,HGLRC); typedef BOOL (WINAPI * PFN_wglShareLists)(HGLRC,HGLRC); #define wglCreateContext _glfw.wgl.CreateContext #define wglDeleteContext _glfw.wgl.DeleteContext #define wglGetProcAddress _glfw.wgl.GetProcAddress #define wglGetCurrentDC _glfw.wgl.GetCurrentDC #define wglGetCurrentContext _glfw.wgl.GetCurrentContext #define wglMakeCurrent _glfw.wgl.MakeCurrent #define wglShareLists _glfw.wgl.ShareLists #define _GLFW_PLATFORM_CONTEXT_STATE _GLFWcontextWGL wgl #define _GLFW_PLATFORM_LIBRARY_CONTEXT_STATE _GLFWlibraryWGL wgl // WGL-specific per-context data // typedef struct _GLFWcontextWGL { HDC dc; HGLRC handle; int interval; } _GLFWcontextWGL; // WGL-specific global data // typedef struct _GLFWlibraryWGL { HINSTANCE instance; PFN_wglCreateContext CreateContext; PFN_wglDeleteContext DeleteContext; PFN_wglGetProcAddress GetProcAddress; PFN_wglGetCurrentDC GetCurrentDC; PFN_wglGetCurrentContext GetCurrentContext; PFN_wglMakeCurrent MakeCurrent; PFN_wglShareLists ShareLists; PFNWGLSWAPINTERVALEXTPROC SwapIntervalEXT; PFNWGLGETPIXELFORMATATTRIBIVARBPROC GetPixelFormatAttribivARB; PFNWGLGETEXTENSIONSSTRINGEXTPROC GetExtensionsStringEXT; PFNWGLGETEXTENSIONSSTRINGARBPROC GetExtensionsStringARB; PFNWGLCREATECONTEXTATTRIBSARBPROC CreateContextAttribsARB; GLFWbool EXT_swap_control; GLFWbool EXT_colorspace; GLFWbool ARB_multisample; GLFWbool ARB_framebuffer_sRGB; GLFWbool EXT_framebuffer_sRGB; GLFWbool ARB_pixel_format; GLFWbool ARB_create_context; GLFWbool ARB_create_context_profile; GLFWbool EXT_create_context_es2_profile; GLFWbool ARB_create_context_robustness; GLFWbool ARB_create_context_no_error; GLFWbool ARB_context_flush_control; } _GLFWlibraryWGL; GLFWbool _glfwInitWGL(void); void _glfwTerminateWGL(void); GLFWbool _glfwCreateContextWGL(_GLFWwindow* window, const _GLFWctxconfig* ctxconfig, const _GLFWfbconfig* fbconfig); #endif #ifndef HEADER_GUARD_EGL_CONTEXT_H #define HEADER_GUARD_EGL_CONTEXT_H //======================================================================== // GLFW 3.3.7 EGL - www.glfw.org //------------------------------------------------------------------------ // Copyright (c) 2002-2006 Marcus Geelnard // Copyright (c) 2006-2017 Camilla Löwy // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would // be appreciated but is not required. // // 2. Altered source versions must be plainly marked as such, and must not // be misrepresented as being the original software. // // 3. This notice may not be removed or altered from any source // distribution. // //======================================================================== #if defined(_GLFW_USE_EGLPLATFORM_H) #include #elif defined(_GLFW_WIN32) #define EGLAPIENTRY __stdcall typedef HDC EGLNativeDisplayType; typedef HWND EGLNativeWindowType; #elif defined(_GLFW_COCOA) #define EGLAPIENTRY typedef void* EGLNativeDisplayType; typedef id EGLNativeWindowType; #elif defined(_GLFW_X11) #define EGLAPIENTRY typedef Display* EGLNativeDisplayType; typedef Window EGLNativeWindowType; #elif defined(_GLFW_WAYLAND) #define EGLAPIENTRY typedef struct wl_display* EGLNativeDisplayType; typedef struct wl_egl_window* EGLNativeWindowType; #else #error "No supported EGL platform selected" #endif #define EGL_SUCCESS 0x3000 #define EGL_NOT_INITIALIZED 0x3001 #define EGL_BAD_ACCESS 0x3002 #define EGL_BAD_ALLOC 0x3003 #define EGL_BAD_ATTRIBUTE 0x3004 #define EGL_BAD_CONFIG 0x3005 #define EGL_BAD_CONTEXT 0x3006 #define EGL_BAD_CURRENT_SURFACE 0x3007 #define EGL_BAD_DISPLAY 0x3008 #define EGL_BAD_MATCH 0x3009 #define EGL_BAD_NATIVE_PIXMAP 0x300a #define EGL_BAD_NATIVE_WINDOW 0x300b #define EGL_BAD_PARAMETER 0x300c #define EGL_BAD_SURFACE 0x300d #define EGL_CONTEXT_LOST 0x300e #define EGL_COLOR_BUFFER_TYPE 0x303f #define EGL_RGB_BUFFER 0x308e #define EGL_SURFACE_TYPE 0x3033 #define EGL_WINDOW_BIT 0x0004 #define EGL_RENDERABLE_TYPE 0x3040 #define EGL_OPENGL_ES_BIT 0x0001 #define EGL_OPENGL_ES2_BIT 0x0004 #define EGL_OPENGL_BIT 0x0008 #define EGL_ALPHA_SIZE 0x3021 #define EGL_BLUE_SIZE 0x3022 #define EGL_GREEN_SIZE 0x3023 #define EGL_RED_SIZE 0x3024 #define EGL_DEPTH_SIZE 0x3025 #define EGL_STENCIL_SIZE 0x3026 #define EGL_SAMPLES 0x3031 #define EGL_OPENGL_ES_API 0x30a0 #define EGL_OPENGL_API 0x30a2 #define EGL_NONE 0x3038 #define EGL_RENDER_BUFFER 0x3086 #define EGL_SINGLE_BUFFER 0x3085 #define EGL_EXTENSIONS 0x3055 #define EGL_CONTEXT_CLIENT_VERSION 0x3098 #define EGL_NATIVE_VISUAL_ID 0x302e #define EGL_NO_SURFACE ((EGLSurface) 0) #define EGL_NO_DISPLAY ((EGLDisplay) 0) #define EGL_NO_CONTEXT ((EGLContext) 0) #define EGL_DEFAULT_DISPLAY ((EGLNativeDisplayType) 0) #define EGL_CONTEXT_OPENGL_FORWARD_COMPATIBLE_BIT_KHR 0x00000002 #define EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT_KHR 0x00000001 #define EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT_KHR 0x00000002 #define EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR 0x00000001 #define EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_KHR 0x31bd #define EGL_NO_RESET_NOTIFICATION_KHR 0x31be #define EGL_LOSE_CONTEXT_ON_RESET_KHR 0x31bf #define EGL_CONTEXT_OPENGL_ROBUST_ACCESS_BIT_KHR 0x00000004 #define EGL_CONTEXT_MAJOR_VERSION_KHR 0x3098 #define EGL_CONTEXT_MINOR_VERSION_KHR 0x30fb #define EGL_CONTEXT_OPENGL_PROFILE_MASK_KHR 0x30fd #define EGL_CONTEXT_FLAGS_KHR 0x30fc #define EGL_CONTEXT_OPENGL_NO_ERROR_KHR 0x31b3 #define EGL_GL_COLORSPACE_KHR 0x309d #define EGL_GL_COLORSPACE_SRGB_KHR 0x3089 #define EGL_CONTEXT_RELEASE_BEHAVIOR_KHR 0x2097 #define EGL_CONTEXT_RELEASE_BEHAVIOR_NONE_KHR 0 #define EGL_CONTEXT_RELEASE_BEHAVIOR_FLUSH_KHR 0x2098 #define EGL_PRESENT_OPAQUE_EXT 0x31df typedef int EGLint; typedef unsigned int EGLBoolean; typedef unsigned int EGLenum; typedef void* EGLConfig; typedef void* EGLContext; typedef void* EGLDisplay; typedef void* EGLSurface; // EGL function pointer typedefs typedef EGLBoolean (EGLAPIENTRY * PFN_eglGetConfigAttrib)(EGLDisplay,EGLConfig,EGLint,EGLint*); typedef EGLBoolean (EGLAPIENTRY * PFN_eglGetConfigs)(EGLDisplay,EGLConfig*,EGLint,EGLint*); typedef EGLDisplay (EGLAPIENTRY * PFN_eglGetDisplay)(EGLNativeDisplayType); typedef EGLint (EGLAPIENTRY * PFN_eglGetError)(void); typedef EGLBoolean (EGLAPIENTRY * PFN_eglInitialize)(EGLDisplay,EGLint*,EGLint*); typedef EGLBoolean (EGLAPIENTRY * PFN_eglTerminate)(EGLDisplay); typedef EGLBoolean (EGLAPIENTRY * PFN_eglBindAPI)(EGLenum); typedef EGLContext (EGLAPIENTRY * PFN_eglCreateContext)(EGLDisplay,EGLConfig,EGLContext,const EGLint*); typedef EGLBoolean (EGLAPIENTRY * PFN_eglDestroySurface)(EGLDisplay,EGLSurface); typedef EGLBoolean (EGLAPIENTRY * PFN_eglDestroyContext)(EGLDisplay,EGLContext); typedef EGLSurface (EGLAPIENTRY * PFN_eglCreateWindowSurface)(EGLDisplay,EGLConfig,EGLNativeWindowType,const EGLint*); typedef EGLBoolean (EGLAPIENTRY * PFN_eglMakeCurrent)(EGLDisplay,EGLSurface,EGLSurface,EGLContext); typedef EGLBoolean (EGLAPIENTRY * PFN_eglSwapBuffers)(EGLDisplay,EGLSurface); typedef EGLBoolean (EGLAPIENTRY * PFN_eglSwapInterval)(EGLDisplay,EGLint); typedef const char* (EGLAPIENTRY * PFN_eglQueryString)(EGLDisplay,EGLint); typedef GLFWglproc (EGLAPIENTRY * PFN_eglGetProcAddress)(const char*); #define eglGetConfigAttrib _glfw.egl.GetConfigAttrib #define eglGetConfigs _glfw.egl.GetConfigs #define eglGetDisplay _glfw.egl.GetDisplay #define eglGetError _glfw.egl.GetError #define eglInitialize _glfw.egl.Initialize #define eglTerminate _glfw.egl.Terminate #define eglBindAPI _glfw.egl.BindAPI #define eglCreateContext _glfw.egl.CreateContext #define eglDestroySurface _glfw.egl.DestroySurface #define eglDestroyContext _glfw.egl.DestroyContext #define eglCreateWindowSurface _glfw.egl.CreateWindowSurface #define eglMakeCurrent _glfw.egl.MakeCurrent #define eglSwapBuffers _glfw.egl.SwapBuffers #define eglSwapInterval _glfw.egl.SwapInterval #define eglQueryString _glfw.egl.QueryString #define eglGetProcAddress _glfw.egl.GetProcAddress #define _GLFW_EGL_CONTEXT_STATE _GLFWcontextEGL egl #define _GLFW_EGL_LIBRARY_CONTEXT_STATE _GLFWlibraryEGL egl // EGL-specific per-context data // typedef struct _GLFWcontextEGL { EGLConfig config; EGLContext handle; EGLSurface surface; void* client; } _GLFWcontextEGL; // EGL-specific global data // typedef struct _GLFWlibraryEGL { EGLDisplay display; EGLint major, minor; GLFWbool prefix; GLFWbool KHR_create_context; GLFWbool KHR_create_context_no_error; GLFWbool KHR_gl_colorspace; GLFWbool KHR_get_all_proc_addresses; GLFWbool KHR_context_flush_control; GLFWbool EXT_present_opaque; void* handle; PFN_eglGetConfigAttrib GetConfigAttrib; PFN_eglGetConfigs GetConfigs; PFN_eglGetDisplay GetDisplay; PFN_eglGetError GetError; PFN_eglInitialize Initialize; PFN_eglTerminate Terminate; PFN_eglBindAPI BindAPI; PFN_eglCreateContext CreateContext; PFN_eglDestroySurface DestroySurface; PFN_eglDestroyContext DestroyContext; PFN_eglCreateWindowSurface CreateWindowSurface; PFN_eglMakeCurrent MakeCurrent; PFN_eglSwapBuffers SwapBuffers; PFN_eglSwapInterval SwapInterval; PFN_eglQueryString QueryString; PFN_eglGetProcAddress GetProcAddress; } _GLFWlibraryEGL; GLFWbool _glfwInitEGL(void); void _glfwTerminateEGL(void); GLFWbool _glfwCreateContextEGL(_GLFWwindow* window, const _GLFWctxconfig* ctxconfig, const _GLFWfbconfig* fbconfig); #if defined(_GLFW_X11) GLFWbool _glfwChooseVisualEGL(const _GLFWwndconfig* wndconfig, const _GLFWctxconfig* ctxconfig, const _GLFWfbconfig* fbconfig, Visual** visual, int* depth); #endif /*_GLFW_X11*/ #endif #ifndef HEADER_GUARD_OSMESA_CONTEXT_H #define HEADER_GUARD_OSMESA_CONTEXT_H //======================================================================== // GLFW 3.3.7 OSMesa - www.glfw.org //------------------------------------------------------------------------ // Copyright (c) 2016 Google Inc. // Copyright (c) 2016-2017 Camilla Löwy // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would // be appreciated but is not required. // // 2. Altered source versions must be plainly marked as such, and must not // be misrepresented as being the original software. // // 3. This notice may not be removed or altered from any source // distribution. // //======================================================================== #define OSMESA_RGBA 0x1908 #define OSMESA_FORMAT 0x22 #define OSMESA_DEPTH_BITS 0x30 #define OSMESA_STENCIL_BITS 0x31 #define OSMESA_ACCUM_BITS 0x32 #define OSMESA_PROFILE 0x33 #define OSMESA_CORE_PROFILE 0x34 #define OSMESA_COMPAT_PROFILE 0x35 #define OSMESA_CONTEXT_MAJOR_VERSION 0x36 #define OSMESA_CONTEXT_MINOR_VERSION 0x37 typedef void* OSMesaContext; typedef void (*OSMESAproc)(void); typedef OSMesaContext (GLAPIENTRY * PFN_OSMesaCreateContextExt)(GLenum,GLint,GLint,GLint,OSMesaContext); typedef OSMesaContext (GLAPIENTRY * PFN_OSMesaCreateContextAttribs)(const int*,OSMesaContext); typedef void (GLAPIENTRY * PFN_OSMesaDestroyContext)(OSMesaContext); typedef int (GLAPIENTRY * PFN_OSMesaMakeCurrent)(OSMesaContext,void*,int,int,int); typedef int (GLAPIENTRY * PFN_OSMesaGetColorBuffer)(OSMesaContext,int*,int*,int*,void**); typedef int (GLAPIENTRY * PFN_OSMesaGetDepthBuffer)(OSMesaContext,int*,int*,int*,void**); typedef GLFWglproc (GLAPIENTRY * PFN_OSMesaGetProcAddress)(const char*); #define OSMesaCreateContextExt _glfw.osmesa.CreateContextExt #define OSMesaCreateContextAttribs _glfw.osmesa.CreateContextAttribs #define OSMesaDestroyContext _glfw.osmesa.DestroyContext #define OSMesaMakeCurrent _glfw.osmesa.MakeCurrent #define OSMesaGetColorBuffer _glfw.osmesa.GetColorBuffer #define OSMesaGetDepthBuffer _glfw.osmesa.GetDepthBuffer #define OSMesaGetProcAddress _glfw.osmesa.GetProcAddress #define _GLFW_OSMESA_CONTEXT_STATE _GLFWcontextOSMesa osmesa #define _GLFW_OSMESA_LIBRARY_CONTEXT_STATE _GLFWlibraryOSMesa osmesa // OSMesa-specific per-context data // typedef struct _GLFWcontextOSMesa { OSMesaContext handle; int width; int height; void* buffer; } _GLFWcontextOSMesa; // OSMesa-specific global data // typedef struct _GLFWlibraryOSMesa { void* handle; PFN_OSMesaCreateContextExt CreateContextExt; PFN_OSMesaCreateContextAttribs CreateContextAttribs; PFN_OSMesaDestroyContext DestroyContext; PFN_OSMesaMakeCurrent MakeCurrent; PFN_OSMesaGetColorBuffer GetColorBuffer; PFN_OSMesaGetDepthBuffer GetDepthBuffer; PFN_OSMesaGetProcAddress GetProcAddress; } _GLFWlibraryOSMesa; GLFWbool _glfwInitOSMesa(void); void _glfwTerminateOSMesa(void); GLFWbool _glfwCreateContextOSMesa(_GLFWwindow* window, const _GLFWctxconfig* ctxconfig, const _GLFWfbconfig* fbconfig); #endif #if !defined(_GLFW_WNDCLASSNAME) #define _GLFW_WNDCLASSNAME L"GLFW30" #endif #define _glfw_dlopen(name) LoadLibraryA(name) #define _glfw_dlclose(handle) FreeLibrary((HMODULE) handle) #define _glfw_dlsym(handle, name) GetProcAddress((HMODULE) handle, name) #define _GLFW_EGL_NATIVE_WINDOW ((EGLNativeWindowType) window->win32.handle) #define _GLFW_EGL_NATIVE_DISPLAY EGL_DEFAULT_DISPLAY #define _GLFW_PLATFORM_WINDOW_STATE _GLFWwindowWin32 win32 #define _GLFW_PLATFORM_LIBRARY_WINDOW_STATE _GLFWlibraryWin32 win32 #define _GLFW_PLATFORM_LIBRARY_TIMER_STATE _GLFWtimerWin32 win32 #define _GLFW_PLATFORM_MONITOR_STATE _GLFWmonitorWin32 win32 #define _GLFW_PLATFORM_CURSOR_STATE _GLFWcursorWin32 win32 #define _GLFW_PLATFORM_TLS_STATE _GLFWtlsWin32 win32 #define _GLFW_PLATFORM_MUTEX_STATE _GLFWmutexWin32 win32 // Win32-specific per-window data // typedef struct _GLFWwindowWin32 { HWND handle; HICON bigIcon; HICON smallIcon; GLFWbool cursorTracked; GLFWbool frameAction; GLFWbool iconified; GLFWbool maximized; // Whether to enable framebuffer transparency on DWM GLFWbool transparent; GLFWbool scaleToMonitor; // Cached size used to filter out duplicate events int width, height; // The last received cursor position, regardless of source int lastCursorPosX, lastCursorPosY; // The last recevied high surrogate when decoding pairs of UTF-16 messages WCHAR highSurrogate; } _GLFWwindowWin32; // Win32-specific global data // typedef struct _GLFWlibraryWin32 { HWND helperWindowHandle; HDEVNOTIFY deviceNotificationHandle; DWORD foregroundLockTimeout; int acquiredMonitorCount; char* clipboardString; short int keycodes[512]; short int scancodes[GLFW_KEY_LAST + 1]; char keynames[GLFW_KEY_LAST + 1][5]; // Where to place the cursor when re-enabled double restoreCursorPosX, restoreCursorPosY; // The window whose disabled cursor mode is active _GLFWwindow* disabledCursorWindow; RAWINPUT* rawInput; int rawInputSize; UINT mouseTrailSize; struct { HINSTANCE instance; PFN_DirectInput8Create Create; IDirectInput8W* api; } dinput8; struct { HINSTANCE instance; PFN_XInputGetCapabilities GetCapabilities; PFN_XInputGetState GetState; } xinput; struct { HINSTANCE instance; PFN_SetProcessDPIAware SetProcessDPIAware_; PFN_ChangeWindowMessageFilterEx ChangeWindowMessageFilterEx_; PFN_EnableNonClientDpiScaling EnableNonClientDpiScaling_; PFN_SetProcessDpiAwarenessContext SetProcessDpiAwarenessContext_; PFN_GetDpiForWindow GetDpiForWindow_; PFN_AdjustWindowRectExForDpi AdjustWindowRectExForDpi_; PFN_GetSystemMetricsForDpi GetSystemMetricsForDpi_; } user32; struct { HINSTANCE instance; PFN_DwmIsCompositionEnabled IsCompositionEnabled; PFN_DwmFlush Flush; PFN_DwmEnableBlurBehindWindow EnableBlurBehindWindow; PFN_DwmGetColorizationColor GetColorizationColor; } dwmapi; struct { HINSTANCE instance; PFN_SetProcessDpiAwareness SetProcessDpiAwareness_; PFN_GetDpiForMonitor GetDpiForMonitor_; } shcore; struct { HINSTANCE instance; PFN_RtlVerifyVersionInfo RtlVerifyVersionInfo_; } ntdll; } _GLFWlibraryWin32; // Win32-specific per-monitor data // typedef struct _GLFWmonitorWin32 { HMONITOR handle; // This size matches the static size of DISPLAY_DEVICE.DeviceName WCHAR adapterName[32]; WCHAR displayName[32]; char publicAdapterName[32]; char publicDisplayName[32]; GLFWbool modesPruned; GLFWbool modeChanged; } _GLFWmonitorWin32; // Win32-specific per-cursor data // typedef struct _GLFWcursorWin32 { HCURSOR handle; } _GLFWcursorWin32; // Win32-specific global timer data // typedef struct _GLFWtimerWin32 { uint64_t frequency; } _GLFWtimerWin32; // Win32-specific thread local storage data // typedef struct _GLFWtlsWin32 { GLFWbool allocated; DWORD index; } _GLFWtlsWin32; // Win32-specific mutex data // typedef struct _GLFWmutexWin32 { GLFWbool allocated; CRITICAL_SECTION section; } _GLFWmutexWin32; GLFWbool _glfwRegisterWindowClassWin32(void); void _glfwUnregisterWindowClassWin32(void); WCHAR* _glfwCreateWideStringFromUTF8Win32(const char* source); char* _glfwCreateUTF8FromWideStringWin32(const WCHAR* source); BOOL _glfwIsWindowsVersionOrGreaterWin32(WORD major, WORD minor, WORD sp); BOOL _glfwIsWindows10BuildOrGreaterWin32(WORD build); void _glfwInputErrorWin32(int error, const char* description); void _glfwUpdateKeyNamesWin32(void); void _glfwInitTimerWin32(void); void _glfwPollMonitorsWin32(void); void _glfwSetVideoModeWin32(_GLFWmonitor* monitor, const GLFWvidmode* desired); void _glfwRestoreVideoModeWin32(_GLFWmonitor* monitor); void _glfwGetMonitorContentScaleWin32(HMONITOR handle, float* xscale, float* yscale); #endif #elif defined(_GLFW_X11) #ifndef HEADER_GUARD_X11_PLATFORM_H #define HEADER_GUARD_X11_PLATFORM_H //======================================================================== // GLFW 3.3.7 X11 - www.glfw.org //------------------------------------------------------------------------ // Copyright (c) 2002-2006 Marcus Geelnard // Copyright (c) 2006-2019 Camilla Löwy // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would // be appreciated but is not required. // // 2. Altered source versions must be plainly marked as such, and must not // be misrepresented as being the original software. // // 3. This notice may not be removed or altered from any source // distribution. // //======================================================================== #include #include #include #include #include #include #include #include // The XRandR extension provides mode setting and gamma control #include // The Xkb extension provides improved keyboard support #include // The Xinerama extension provides legacy monitor indices #include // The XInput extension provides raw mouse motion input #include typedef XRRCrtcGamma* (* PFN_XRRAllocGamma)(int); typedef void (* PFN_XRRFreeCrtcInfo)(XRRCrtcInfo*); typedef void (* PFN_XRRFreeGamma)(XRRCrtcGamma*); typedef void (* PFN_XRRFreeOutputInfo)(XRROutputInfo*); typedef void (* PFN_XRRFreeScreenResources)(XRRScreenResources*); typedef XRRCrtcGamma* (* PFN_XRRGetCrtcGamma)(Display*,RRCrtc); typedef int (* PFN_XRRGetCrtcGammaSize)(Display*,RRCrtc); typedef XRRCrtcInfo* (* PFN_XRRGetCrtcInfo) (Display*,XRRScreenResources*,RRCrtc); typedef XRROutputInfo* (* PFN_XRRGetOutputInfo)(Display*,XRRScreenResources*,RROutput); typedef RROutput (* PFN_XRRGetOutputPrimary)(Display*,Window); typedef XRRScreenResources* (* PFN_XRRGetScreenResourcesCurrent)(Display*,Window); typedef Bool (* PFN_XRRQueryExtension)(Display*,int*,int*); typedef Status (* PFN_XRRQueryVersion)(Display*,int*,int*); typedef void (* PFN_XRRSelectInput)(Display*,Window,int); typedef Status (* PFN_XRRSetCrtcConfig)(Display*,XRRScreenResources*,RRCrtc,Time,int,int,RRMode,Rotation,RROutput*,int); typedef void (* PFN_XRRSetCrtcGamma)(Display*,RRCrtc,XRRCrtcGamma*); typedef int (* PFN_XRRUpdateConfiguration)(XEvent*); #define XRRAllocGamma _glfw.x11.randr.AllocGamma #define XRRFreeCrtcInfo _glfw.x11.randr.FreeCrtcInfo #define XRRFreeGamma _glfw.x11.randr.FreeGamma #define XRRFreeOutputInfo _glfw.x11.randr.FreeOutputInfo #define XRRFreeScreenResources _glfw.x11.randr.FreeScreenResources #define XRRGetCrtcGamma _glfw.x11.randr.GetCrtcGamma #define XRRGetCrtcGammaSize _glfw.x11.randr.GetCrtcGammaSize #define XRRGetCrtcInfo _glfw.x11.randr.GetCrtcInfo #define XRRGetOutputInfo _glfw.x11.randr.GetOutputInfo #define XRRGetOutputPrimary _glfw.x11.randr.GetOutputPrimary #define XRRGetScreenResourcesCurrent _glfw.x11.randr.GetScreenResourcesCurrent #define XRRQueryExtension _glfw.x11.randr.QueryExtension #define XRRQueryVersion _glfw.x11.randr.QueryVersion #define XRRSelectInput _glfw.x11.randr.SelectInput #define XRRSetCrtcConfig _glfw.x11.randr.SetCrtcConfig #define XRRSetCrtcGamma _glfw.x11.randr.SetCrtcGamma #define XRRUpdateConfiguration _glfw.x11.randr.UpdateConfiguration typedef XcursorImage* (* PFN_XcursorImageCreate)(int,int); typedef void (* PFN_XcursorImageDestroy)(XcursorImage*); typedef Cursor (* PFN_XcursorImageLoadCursor)(Display*,const XcursorImage*); #define XcursorImageCreate _glfw.x11.xcursor.ImageCreate #define XcursorImageDestroy _glfw.x11.xcursor.ImageDestroy #define XcursorImageLoadCursor _glfw.x11.xcursor.ImageLoadCursor typedef Bool (* PFN_XineramaIsActive)(Display*); typedef Bool (* PFN_XineramaQueryExtension)(Display*,int*,int*); typedef XineramaScreenInfo* (* PFN_XineramaQueryScreens)(Display*,int*); #define XineramaIsActive _glfw.x11.xinerama.IsActive #define XineramaQueryExtension _glfw.x11.xinerama.QueryExtension #define XineramaQueryScreens _glfw.x11.xinerama.QueryScreens typedef XID xcb_window_t; typedef XID xcb_visualid_t; typedef struct xcb_connection_t xcb_connection_t; typedef xcb_connection_t* (* PFN_XGetXCBConnection)(Display*); #define XGetXCBConnection _glfw.x11.x11xcb.GetXCBConnection typedef Bool (* PFN_XF86VidModeQueryExtension)(Display*,int*,int*); typedef Bool (* PFN_XF86VidModeGetGammaRamp)(Display*,int,int,unsigned short*,unsigned short*,unsigned short*); typedef Bool (* PFN_XF86VidModeSetGammaRamp)(Display*,int,int,unsigned short*,unsigned short*,unsigned short*); typedef Bool (* PFN_XF86VidModeGetGammaRampSize)(Display*,int,int*); #define XF86VidModeQueryExtension _glfw.x11.vidmode.QueryExtension #define XF86VidModeGetGammaRamp _glfw.x11.vidmode.GetGammaRamp #define XF86VidModeSetGammaRamp _glfw.x11.vidmode.SetGammaRamp #define XF86VidModeGetGammaRampSize _glfw.x11.vidmode.GetGammaRampSize typedef Status (* PFN_XIQueryVersion)(Display*,int*,int*); typedef int (* PFN_XISelectEvents)(Display*,Window,XIEventMask*,int); #define XIQueryVersion _glfw.x11.xi.QueryVersion #define XISelectEvents _glfw.x11.xi.SelectEvents typedef Bool (* PFN_XRenderQueryExtension)(Display*,int*,int*); typedef Status (* PFN_XRenderQueryVersion)(Display*dpy,int*,int*); typedef XRenderPictFormat* (* PFN_XRenderFindVisualFormat)(Display*,Visual const*); #define XRenderQueryExtension _glfw.x11.xrender.QueryExtension #define XRenderQueryVersion _glfw.x11.xrender.QueryVersion #define XRenderFindVisualFormat _glfw.x11.xrender.FindVisualFormat typedef VkFlags VkXlibSurfaceCreateFlagsKHR; typedef VkFlags VkXcbSurfaceCreateFlagsKHR; typedef struct VkXlibSurfaceCreateInfoKHR { VkStructureType sType; const void* pNext; VkXlibSurfaceCreateFlagsKHR flags; Display* dpy; Window window; } VkXlibSurfaceCreateInfoKHR; typedef struct VkXcbSurfaceCreateInfoKHR { VkStructureType sType; const void* pNext; VkXcbSurfaceCreateFlagsKHR flags; xcb_connection_t* connection; xcb_window_t window; } VkXcbSurfaceCreateInfoKHR; typedef VkResult (APIENTRY *PFN_vkCreateXlibSurfaceKHR)(VkInstance,const VkXlibSurfaceCreateInfoKHR*,const VkAllocationCallbacks*,VkSurfaceKHR*); typedef VkBool32 (APIENTRY *PFN_vkGetPhysicalDeviceXlibPresentationSupportKHR)(VkPhysicalDevice,uint32_t,Display*,VisualID); typedef VkResult (APIENTRY *PFN_vkCreateXcbSurfaceKHR)(VkInstance,const VkXcbSurfaceCreateInfoKHR*,const VkAllocationCallbacks*,VkSurfaceKHR*); typedef VkBool32 (APIENTRY *PFN_vkGetPhysicalDeviceXcbPresentationSupportKHR)(VkPhysicalDevice,uint32_t,xcb_connection_t*,xcb_visualid_t); #ifndef HEADER_GUARD_POSIX_THREAD_H #define HEADER_GUARD_POSIX_THREAD_H //======================================================================== // GLFW 3.3.7 POSIX - www.glfw.org //------------------------------------------------------------------------ // Copyright (c) 2002-2006 Marcus Geelnard // Copyright (c) 2006-2017 Camilla Löwy // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would // be appreciated but is not required. // // 2. Altered source versions must be plainly marked as such, and must not // be misrepresented as being the original software. // // 3. This notice may not be removed or altered from any source // distribution. // //======================================================================== #include #define _GLFW_PLATFORM_TLS_STATE _GLFWtlsPOSIX posix #define _GLFW_PLATFORM_MUTEX_STATE _GLFWmutexPOSIX posix // POSIX-specific thread local storage data // typedef struct _GLFWtlsPOSIX { GLFWbool allocated; pthread_key_t key; } _GLFWtlsPOSIX; // POSIX-specific mutex data // typedef struct _GLFWmutexPOSIX { GLFWbool allocated; pthread_mutex_t handle; } _GLFWmutexPOSIX; #endif #ifndef HEADER_GUARD_POSIX_TIME_H #define HEADER_GUARD_POSIX_TIME_H //======================================================================== // GLFW 3.3.7 POSIX - www.glfw.org //------------------------------------------------------------------------ // Copyright (c) 2002-2006 Marcus Geelnard // Copyright (c) 2006-2017 Camilla Löwy // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would // be appreciated but is not required. // // 2. Altered source versions must be plainly marked as such, and must not // be misrepresented as being the original software. // // 3. This notice may not be removed or altered from any source // distribution. // //======================================================================== #define _GLFW_PLATFORM_LIBRARY_TIMER_STATE _GLFWtimerPOSIX posix #include // POSIX-specific global timer data // typedef struct _GLFWtimerPOSIX { GLFWbool monotonic; uint64_t frequency; } _GLFWtimerPOSIX; void _glfwInitTimerPOSIX(void); #endif #ifndef HEADER_GUARD_XKB_UNICODE_H #define HEADER_GUARD_XKB_UNICODE_H //======================================================================== // GLFW 3.3.7 Linux - www.glfw.org //------------------------------------------------------------------------ // Copyright (c) 2014 Jonas Ã…dahl // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would // be appreciated but is not required. // // 2. Altered source versions must be plainly marked as such, and must not // be misrepresented as being the original software. // // 3. This notice may not be removed or altered from any source // distribution. // //======================================================================== #define GLFW_INVALID_CODEPOINT 0xffffffffu uint32_t _glfwKeySym2Unicode(unsigned int keysym); #endif #ifndef HEADER_GUARD_GLX_CONTEXT_H #define HEADER_GUARD_GLX_CONTEXT_H //======================================================================== // GLFW 3.3.7 GLX - www.glfw.org //------------------------------------------------------------------------ // Copyright (c) 2002-2006 Marcus Geelnard // Copyright (c) 2006-2017 Camilla Löwy // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would // be appreciated but is not required. // // 2. Altered source versions must be plainly marked as such, and must not // be misrepresented as being the original software. // // 3. This notice may not be removed or altered from any source // distribution. // //======================================================================== #define GLX_VENDOR 1 #define GLX_RGBA_BIT 0x00000001 #define GLX_WINDOW_BIT 0x00000001 #define GLX_DRAWABLE_TYPE 0x8010 #define GLX_RENDER_TYPE 0x8011 #define GLX_RGBA_TYPE 0x8014 #define GLX_DOUBLEBUFFER 5 #define GLX_STEREO 6 #define GLX_AUX_BUFFERS 7 #define GLX_RED_SIZE 8 #define GLX_GREEN_SIZE 9 #define GLX_BLUE_SIZE 10 #define GLX_ALPHA_SIZE 11 #define GLX_DEPTH_SIZE 12 #define GLX_STENCIL_SIZE 13 #define GLX_ACCUM_RED_SIZE 14 #define GLX_ACCUM_GREEN_SIZE 15 #define GLX_ACCUM_BLUE_SIZE 16 #define GLX_ACCUM_ALPHA_SIZE 17 #define GLX_SAMPLES 0x186a1 #define GLX_VISUAL_ID 0x800b #define GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB 0x20b2 #define GLX_CONTEXT_DEBUG_BIT_ARB 0x00000001 #define GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB 0x00000002 #define GLX_CONTEXT_CORE_PROFILE_BIT_ARB 0x00000001 #define GLX_CONTEXT_PROFILE_MASK_ARB 0x9126 #define GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB 0x00000002 #define GLX_CONTEXT_MAJOR_VERSION_ARB 0x2091 #define GLX_CONTEXT_MINOR_VERSION_ARB 0x2092 #define GLX_CONTEXT_FLAGS_ARB 0x2094 #define GLX_CONTEXT_ES2_PROFILE_BIT_EXT 0x00000004 #define GLX_CONTEXT_ROBUST_ACCESS_BIT_ARB 0x00000004 #define GLX_LOSE_CONTEXT_ON_RESET_ARB 0x8252 #define GLX_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB 0x8256 #define GLX_NO_RESET_NOTIFICATION_ARB 0x8261 #define GLX_CONTEXT_RELEASE_BEHAVIOR_ARB 0x2097 #define GLX_CONTEXT_RELEASE_BEHAVIOR_NONE_ARB 0 #define GLX_CONTEXT_RELEASE_BEHAVIOR_FLUSH_ARB 0x2098 #define GLX_CONTEXT_OPENGL_NO_ERROR_ARB 0x31b3 typedef XID GLXWindow; typedef XID GLXDrawable; typedef struct __GLXFBConfig* GLXFBConfig; typedef struct __GLXcontext* GLXContext; typedef void (*__GLXextproc)(void); typedef int (*PFNGLXGETFBCONFIGATTRIBPROC)(Display*,GLXFBConfig,int,int*); typedef const char* (*PFNGLXGETCLIENTSTRINGPROC)(Display*,int); typedef Bool (*PFNGLXQUERYEXTENSIONPROC)(Display*,int*,int*); typedef Bool (*PFNGLXQUERYVERSIONPROC)(Display*,int*,int*); typedef void (*PFNGLXDESTROYCONTEXTPROC)(Display*,GLXContext); typedef Bool (*PFNGLXMAKECURRENTPROC)(Display*,GLXDrawable,GLXContext); typedef void (*PFNGLXSWAPBUFFERSPROC)(Display*,GLXDrawable); typedef const char* (*PFNGLXQUERYEXTENSIONSSTRINGPROC)(Display*,int); typedef GLXFBConfig* (*PFNGLXGETFBCONFIGSPROC)(Display*,int,int*); typedef GLXContext (*PFNGLXCREATENEWCONTEXTPROC)(Display*,GLXFBConfig,int,GLXContext,Bool); typedef __GLXextproc (* PFNGLXGETPROCADDRESSPROC)(const GLubyte *procName); typedef void (*PFNGLXSWAPINTERVALEXTPROC)(Display*,GLXDrawable,int); typedef XVisualInfo* (*PFNGLXGETVISUALFROMFBCONFIGPROC)(Display*,GLXFBConfig); typedef GLXWindow (*PFNGLXCREATEWINDOWPROC)(Display*,GLXFBConfig,Window,const int*); typedef void (*PFNGLXDESTROYWINDOWPROC)(Display*,GLXWindow); typedef int (*PFNGLXSWAPINTERVALMESAPROC)(int); typedef int (*PFNGLXSWAPINTERVALSGIPROC)(int); typedef GLXContext (*PFNGLXCREATECONTEXTATTRIBSARBPROC)(Display*,GLXFBConfig,GLXContext,Bool,const int*); // libGL.so function pointer typedefs #define glXGetFBConfigs _glfw.glx.GetFBConfigs #define glXGetFBConfigAttrib _glfw.glx.GetFBConfigAttrib #define glXGetClientString _glfw.glx.GetClientString #define glXQueryExtension _glfw.glx.QueryExtension #define glXQueryVersion _glfw.glx.QueryVersion #define glXDestroyContext _glfw.glx.DestroyContext #define glXMakeCurrent _glfw.glx.MakeCurrent #define glXSwapBuffers _glfw.glx.SwapBuffers #define glXQueryExtensionsString _glfw.glx.QueryExtensionsString #define glXCreateNewContext _glfw.glx.CreateNewContext #define glXGetVisualFromFBConfig _glfw.glx.GetVisualFromFBConfig #define glXCreateWindow _glfw.glx.CreateWindow #define glXDestroyWindow _glfw.glx.DestroyWindow #define _GLFW_PLATFORM_CONTEXT_STATE _GLFWcontextGLX glx #define _GLFW_PLATFORM_LIBRARY_CONTEXT_STATE _GLFWlibraryGLX glx // GLX-specific per-context data // typedef struct _GLFWcontextGLX { GLXContext handle; GLXWindow window; } _GLFWcontextGLX; // GLX-specific global data // typedef struct _GLFWlibraryGLX { int major, minor; int eventBase; int errorBase; // dlopen handle for libGL.so.1 void* handle; // GLX 1.3 functions PFNGLXGETFBCONFIGSPROC GetFBConfigs; PFNGLXGETFBCONFIGATTRIBPROC GetFBConfigAttrib; PFNGLXGETCLIENTSTRINGPROC GetClientString; PFNGLXQUERYEXTENSIONPROC QueryExtension; PFNGLXQUERYVERSIONPROC QueryVersion; PFNGLXDESTROYCONTEXTPROC DestroyContext; PFNGLXMAKECURRENTPROC MakeCurrent; PFNGLXSWAPBUFFERSPROC SwapBuffers; PFNGLXQUERYEXTENSIONSSTRINGPROC QueryExtensionsString; PFNGLXCREATENEWCONTEXTPROC CreateNewContext; PFNGLXGETVISUALFROMFBCONFIGPROC GetVisualFromFBConfig; PFNGLXCREATEWINDOWPROC CreateWindow; PFNGLXDESTROYWINDOWPROC DestroyWindow; // GLX 1.4 and extension functions PFNGLXGETPROCADDRESSPROC GetProcAddress; PFNGLXGETPROCADDRESSPROC GetProcAddressARB; PFNGLXSWAPINTERVALSGIPROC SwapIntervalSGI; PFNGLXSWAPINTERVALEXTPROC SwapIntervalEXT; PFNGLXSWAPINTERVALMESAPROC SwapIntervalMESA; PFNGLXCREATECONTEXTATTRIBSARBPROC CreateContextAttribsARB; GLFWbool SGI_swap_control; GLFWbool EXT_swap_control; GLFWbool MESA_swap_control; GLFWbool ARB_multisample; GLFWbool ARB_framebuffer_sRGB; GLFWbool EXT_framebuffer_sRGB; GLFWbool ARB_create_context; GLFWbool ARB_create_context_profile; GLFWbool ARB_create_context_robustness; GLFWbool EXT_create_context_es2_profile; GLFWbool ARB_create_context_no_error; GLFWbool ARB_context_flush_control; } _GLFWlibraryGLX; GLFWbool _glfwInitGLX(void); void _glfwTerminateGLX(void); GLFWbool _glfwCreateContextGLX(_GLFWwindow* window, const _GLFWctxconfig* ctxconfig, const _GLFWfbconfig* fbconfig); void _glfwDestroyContextGLX(_GLFWwindow* window); GLFWbool _glfwChooseVisualGLX(const _GLFWwndconfig* wndconfig, const _GLFWctxconfig* ctxconfig, const _GLFWfbconfig* fbconfig, Visual** visual, int* depth); #endif #ifndef HEADER_GUARD_EGL_CONTEXT_H #define HEADER_GUARD_EGL_CONTEXT_H //======================================================================== // GLFW 3.3.7 EGL - www.glfw.org //------------------------------------------------------------------------ // Copyright (c) 2002-2006 Marcus Geelnard // Copyright (c) 2006-2017 Camilla Löwy // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would // be appreciated but is not required. // // 2. Altered source versions must be plainly marked as such, and must not // be misrepresented as being the original software. // // 3. This notice may not be removed or altered from any source // distribution. // //======================================================================== #if defined(_GLFW_USE_EGLPLATFORM_H) #include #elif defined(_GLFW_WIN32) #define EGLAPIENTRY __stdcall typedef HDC EGLNativeDisplayType; typedef HWND EGLNativeWindowType; #elif defined(_GLFW_COCOA) #define EGLAPIENTRY typedef void* EGLNativeDisplayType; typedef id EGLNativeWindowType; #elif defined(_GLFW_X11) #define EGLAPIENTRY typedef Display* EGLNativeDisplayType; typedef Window EGLNativeWindowType; #elif defined(_GLFW_WAYLAND) #define EGLAPIENTRY typedef struct wl_display* EGLNativeDisplayType; typedef struct wl_egl_window* EGLNativeWindowType; #else #error "No supported EGL platform selected" #endif #define EGL_SUCCESS 0x3000 #define EGL_NOT_INITIALIZED 0x3001 #define EGL_BAD_ACCESS 0x3002 #define EGL_BAD_ALLOC 0x3003 #define EGL_BAD_ATTRIBUTE 0x3004 #define EGL_BAD_CONFIG 0x3005 #define EGL_BAD_CONTEXT 0x3006 #define EGL_BAD_CURRENT_SURFACE 0x3007 #define EGL_BAD_DISPLAY 0x3008 #define EGL_BAD_MATCH 0x3009 #define EGL_BAD_NATIVE_PIXMAP 0x300a #define EGL_BAD_NATIVE_WINDOW 0x300b #define EGL_BAD_PARAMETER 0x300c #define EGL_BAD_SURFACE 0x300d #define EGL_CONTEXT_LOST 0x300e #define EGL_COLOR_BUFFER_TYPE 0x303f #define EGL_RGB_BUFFER 0x308e #define EGL_SURFACE_TYPE 0x3033 #define EGL_WINDOW_BIT 0x0004 #define EGL_RENDERABLE_TYPE 0x3040 #define EGL_OPENGL_ES_BIT 0x0001 #define EGL_OPENGL_ES2_BIT 0x0004 #define EGL_OPENGL_BIT 0x0008 #define EGL_ALPHA_SIZE 0x3021 #define EGL_BLUE_SIZE 0x3022 #define EGL_GREEN_SIZE 0x3023 #define EGL_RED_SIZE 0x3024 #define EGL_DEPTH_SIZE 0x3025 #define EGL_STENCIL_SIZE 0x3026 #define EGL_SAMPLES 0x3031 #define EGL_OPENGL_ES_API 0x30a0 #define EGL_OPENGL_API 0x30a2 #define EGL_NONE 0x3038 #define EGL_RENDER_BUFFER 0x3086 #define EGL_SINGLE_BUFFER 0x3085 #define EGL_EXTENSIONS 0x3055 #define EGL_CONTEXT_CLIENT_VERSION 0x3098 #define EGL_NATIVE_VISUAL_ID 0x302e #define EGL_NO_SURFACE ((EGLSurface) 0) #define EGL_NO_DISPLAY ((EGLDisplay) 0) #define EGL_NO_CONTEXT ((EGLContext) 0) #define EGL_DEFAULT_DISPLAY ((EGLNativeDisplayType) 0) #define EGL_CONTEXT_OPENGL_FORWARD_COMPATIBLE_BIT_KHR 0x00000002 #define EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT_KHR 0x00000001 #define EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT_KHR 0x00000002 #define EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR 0x00000001 #define EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_KHR 0x31bd #define EGL_NO_RESET_NOTIFICATION_KHR 0x31be #define EGL_LOSE_CONTEXT_ON_RESET_KHR 0x31bf #define EGL_CONTEXT_OPENGL_ROBUST_ACCESS_BIT_KHR 0x00000004 #define EGL_CONTEXT_MAJOR_VERSION_KHR 0x3098 #define EGL_CONTEXT_MINOR_VERSION_KHR 0x30fb #define EGL_CONTEXT_OPENGL_PROFILE_MASK_KHR 0x30fd #define EGL_CONTEXT_FLAGS_KHR 0x30fc #define EGL_CONTEXT_OPENGL_NO_ERROR_KHR 0x31b3 #define EGL_GL_COLORSPACE_KHR 0x309d #define EGL_GL_COLORSPACE_SRGB_KHR 0x3089 #define EGL_CONTEXT_RELEASE_BEHAVIOR_KHR 0x2097 #define EGL_CONTEXT_RELEASE_BEHAVIOR_NONE_KHR 0 #define EGL_CONTEXT_RELEASE_BEHAVIOR_FLUSH_KHR 0x2098 #define EGL_PRESENT_OPAQUE_EXT 0x31df typedef int EGLint; typedef unsigned int EGLBoolean; typedef unsigned int EGLenum; typedef void* EGLConfig; typedef void* EGLContext; typedef void* EGLDisplay; typedef void* EGLSurface; // EGL function pointer typedefs typedef EGLBoolean (EGLAPIENTRY * PFN_eglGetConfigAttrib)(EGLDisplay,EGLConfig,EGLint,EGLint*); typedef EGLBoolean (EGLAPIENTRY * PFN_eglGetConfigs)(EGLDisplay,EGLConfig*,EGLint,EGLint*); typedef EGLDisplay (EGLAPIENTRY * PFN_eglGetDisplay)(EGLNativeDisplayType); typedef EGLint (EGLAPIENTRY * PFN_eglGetError)(void); typedef EGLBoolean (EGLAPIENTRY * PFN_eglInitialize)(EGLDisplay,EGLint*,EGLint*); typedef EGLBoolean (EGLAPIENTRY * PFN_eglTerminate)(EGLDisplay); typedef EGLBoolean (EGLAPIENTRY * PFN_eglBindAPI)(EGLenum); typedef EGLContext (EGLAPIENTRY * PFN_eglCreateContext)(EGLDisplay,EGLConfig,EGLContext,const EGLint*); typedef EGLBoolean (EGLAPIENTRY * PFN_eglDestroySurface)(EGLDisplay,EGLSurface); typedef EGLBoolean (EGLAPIENTRY * PFN_eglDestroyContext)(EGLDisplay,EGLContext); typedef EGLSurface (EGLAPIENTRY * PFN_eglCreateWindowSurface)(EGLDisplay,EGLConfig,EGLNativeWindowType,const EGLint*); typedef EGLBoolean (EGLAPIENTRY * PFN_eglMakeCurrent)(EGLDisplay,EGLSurface,EGLSurface,EGLContext); typedef EGLBoolean (EGLAPIENTRY * PFN_eglSwapBuffers)(EGLDisplay,EGLSurface); typedef EGLBoolean (EGLAPIENTRY * PFN_eglSwapInterval)(EGLDisplay,EGLint); typedef const char* (EGLAPIENTRY * PFN_eglQueryString)(EGLDisplay,EGLint); typedef GLFWglproc (EGLAPIENTRY * PFN_eglGetProcAddress)(const char*); #define eglGetConfigAttrib _glfw.egl.GetConfigAttrib #define eglGetConfigs _glfw.egl.GetConfigs #define eglGetDisplay _glfw.egl.GetDisplay #define eglGetError _glfw.egl.GetError #define eglInitialize _glfw.egl.Initialize #define eglTerminate _glfw.egl.Terminate #define eglBindAPI _glfw.egl.BindAPI #define eglCreateContext _glfw.egl.CreateContext #define eglDestroySurface _glfw.egl.DestroySurface #define eglDestroyContext _glfw.egl.DestroyContext #define eglCreateWindowSurface _glfw.egl.CreateWindowSurface #define eglMakeCurrent _glfw.egl.MakeCurrent #define eglSwapBuffers _glfw.egl.SwapBuffers #define eglSwapInterval _glfw.egl.SwapInterval #define eglQueryString _glfw.egl.QueryString #define eglGetProcAddress _glfw.egl.GetProcAddress #define _GLFW_EGL_CONTEXT_STATE _GLFWcontextEGL egl #define _GLFW_EGL_LIBRARY_CONTEXT_STATE _GLFWlibraryEGL egl // EGL-specific per-context data // typedef struct _GLFWcontextEGL { EGLConfig config; EGLContext handle; EGLSurface surface; void* client; } _GLFWcontextEGL; // EGL-specific global data // typedef struct _GLFWlibraryEGL { EGLDisplay display; EGLint major, minor; GLFWbool prefix; GLFWbool KHR_create_context; GLFWbool KHR_create_context_no_error; GLFWbool KHR_gl_colorspace; GLFWbool KHR_get_all_proc_addresses; GLFWbool KHR_context_flush_control; GLFWbool EXT_present_opaque; void* handle; PFN_eglGetConfigAttrib GetConfigAttrib; PFN_eglGetConfigs GetConfigs; PFN_eglGetDisplay GetDisplay; PFN_eglGetError GetError; PFN_eglInitialize Initialize; PFN_eglTerminate Terminate; PFN_eglBindAPI BindAPI; PFN_eglCreateContext CreateContext; PFN_eglDestroySurface DestroySurface; PFN_eglDestroyContext DestroyContext; PFN_eglCreateWindowSurface CreateWindowSurface; PFN_eglMakeCurrent MakeCurrent; PFN_eglSwapBuffers SwapBuffers; PFN_eglSwapInterval SwapInterval; PFN_eglQueryString QueryString; PFN_eglGetProcAddress GetProcAddress; } _GLFWlibraryEGL; GLFWbool _glfwInitEGL(void); void _glfwTerminateEGL(void); GLFWbool _glfwCreateContextEGL(_GLFWwindow* window, const _GLFWctxconfig* ctxconfig, const _GLFWfbconfig* fbconfig); #if defined(_GLFW_X11) GLFWbool _glfwChooseVisualEGL(const _GLFWwndconfig* wndconfig, const _GLFWctxconfig* ctxconfig, const _GLFWfbconfig* fbconfig, Visual** visual, int* depth); #endif /*_GLFW_X11*/ #endif #ifndef HEADER_GUARD_OSMESA_CONTEXT_H #define HEADER_GUARD_OSMESA_CONTEXT_H //======================================================================== // GLFW 3.3.7 OSMesa - www.glfw.org //------------------------------------------------------------------------ // Copyright (c) 2016 Google Inc. // Copyright (c) 2016-2017 Camilla Löwy // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would // be appreciated but is not required. // // 2. Altered source versions must be plainly marked as such, and must not // be misrepresented as being the original software. // // 3. This notice may not be removed or altered from any source // distribution. // //======================================================================== #define OSMESA_RGBA 0x1908 #define OSMESA_FORMAT 0x22 #define OSMESA_DEPTH_BITS 0x30 #define OSMESA_STENCIL_BITS 0x31 #define OSMESA_ACCUM_BITS 0x32 #define OSMESA_PROFILE 0x33 #define OSMESA_CORE_PROFILE 0x34 #define OSMESA_COMPAT_PROFILE 0x35 #define OSMESA_CONTEXT_MAJOR_VERSION 0x36 #define OSMESA_CONTEXT_MINOR_VERSION 0x37 typedef void* OSMesaContext; typedef void (*OSMESAproc)(void); typedef OSMesaContext (GLAPIENTRY * PFN_OSMesaCreateContextExt)(GLenum,GLint,GLint,GLint,OSMesaContext); typedef OSMesaContext (GLAPIENTRY * PFN_OSMesaCreateContextAttribs)(const int*,OSMesaContext); typedef void (GLAPIENTRY * PFN_OSMesaDestroyContext)(OSMesaContext); typedef int (GLAPIENTRY * PFN_OSMesaMakeCurrent)(OSMesaContext,void*,int,int,int); typedef int (GLAPIENTRY * PFN_OSMesaGetColorBuffer)(OSMesaContext,int*,int*,int*,void**); typedef int (GLAPIENTRY * PFN_OSMesaGetDepthBuffer)(OSMesaContext,int*,int*,int*,void**); typedef GLFWglproc (GLAPIENTRY * PFN_OSMesaGetProcAddress)(const char*); #define OSMesaCreateContextExt _glfw.osmesa.CreateContextExt #define OSMesaCreateContextAttribs _glfw.osmesa.CreateContextAttribs #define OSMesaDestroyContext _glfw.osmesa.DestroyContext #define OSMesaMakeCurrent _glfw.osmesa.MakeCurrent #define OSMesaGetColorBuffer _glfw.osmesa.GetColorBuffer #define OSMesaGetDepthBuffer _glfw.osmesa.GetDepthBuffer #define OSMesaGetProcAddress _glfw.osmesa.GetProcAddress #define _GLFW_OSMESA_CONTEXT_STATE _GLFWcontextOSMesa osmesa #define _GLFW_OSMESA_LIBRARY_CONTEXT_STATE _GLFWlibraryOSMesa osmesa // OSMesa-specific per-context data // typedef struct _GLFWcontextOSMesa { OSMesaContext handle; int width; int height; void* buffer; } _GLFWcontextOSMesa; // OSMesa-specific global data // typedef struct _GLFWlibraryOSMesa { void* handle; PFN_OSMesaCreateContextExt CreateContextExt; PFN_OSMesaCreateContextAttribs CreateContextAttribs; PFN_OSMesaDestroyContext DestroyContext; PFN_OSMesaMakeCurrent MakeCurrent; PFN_OSMesaGetColorBuffer GetColorBuffer; PFN_OSMesaGetDepthBuffer GetDepthBuffer; PFN_OSMesaGetProcAddress GetProcAddress; } _GLFWlibraryOSMesa; GLFWbool _glfwInitOSMesa(void); void _glfwTerminateOSMesa(void); GLFWbool _glfwCreateContextOSMesa(_GLFWwindow* window, const _GLFWctxconfig* ctxconfig, const _GLFWfbconfig* fbconfig); #endif #if defined(__linux__) #ifndef HEADER_GUARD_LINUX_JOYSTICK_H #define HEADER_GUARD_LINUX_JOYSTICK_H //======================================================================== // GLFW 3.3.7 Linux - www.glfw.org //------------------------------------------------------------------------ // Copyright (c) 2014 Jonas Ã…dahl // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would // be appreciated but is not required. // // 2. Altered source versions must be plainly marked as such, and must not // be misrepresented as being the original software. // // 3. This notice may not be removed or altered from any source // distribution. // //======================================================================== #include #include #include #define _GLFW_PLATFORM_JOYSTICK_STATE _GLFWjoystickLinux linjs #define _GLFW_PLATFORM_LIBRARY_JOYSTICK_STATE _GLFWlibraryLinux linjs #define _GLFW_PLATFORM_MAPPING_NAME "Linux" #define GLFW_BUILD_LINUX_MAPPINGS // Linux-specific joystick data // typedef struct _GLFWjoystickLinux { int fd; char path[PATH_MAX]; int keyMap[KEY_CNT - BTN_MISC]; int absMap[ABS_CNT]; struct input_absinfo absInfo[ABS_CNT]; int hats[4][2]; } _GLFWjoystickLinux; // Linux-specific joystick API data // typedef struct _GLFWlibraryLinux { int inotify; int watch; regex_t regex; GLFWbool dropped; } _GLFWlibraryLinux; GLFWbool _glfwInitJoysticksLinux(void); void _glfwTerminateJoysticksLinux(void); void _glfwDetectJoystickConnectionLinux(void); #endif #else #ifndef HEADER_GUARD_NULL_JOYSTICK_H #define HEADER_GUARD_NULL_JOYSTICK_H //======================================================================== // GLFW 3.3.7 - www.glfw.org //------------------------------------------------------------------------ // Copyright (c) 2006-2017 Camilla Löwy // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would // be appreciated but is not required. // // 2. Altered source versions must be plainly marked as such, and must not // be misrepresented as being the original software. // // 3. This notice may not be removed or altered from any source // distribution. // //======================================================================== #define _GLFW_PLATFORM_JOYSTICK_STATE struct { int dummyJoystick; } #define _GLFW_PLATFORM_LIBRARY_JOYSTICK_STATE struct { int dummyLibraryJoystick; } #define _GLFW_PLATFORM_MAPPING_NAME "" #endif #endif #define _glfw_dlopen(name) dlopen(name, RTLD_LAZY | RTLD_LOCAL) #define _glfw_dlclose(handle) dlclose(handle) #define _glfw_dlsym(handle, name) dlsym(handle, name) #define _GLFW_EGL_NATIVE_WINDOW ((EGLNativeWindowType) window->x11.handle) #define _GLFW_EGL_NATIVE_DISPLAY ((EGLNativeDisplayType) _glfw.x11.display) #define _GLFW_PLATFORM_WINDOW_STATE _GLFWwindowX11 x11 #define _GLFW_PLATFORM_LIBRARY_WINDOW_STATE _GLFWlibraryX11 x11 #define _GLFW_PLATFORM_MONITOR_STATE _GLFWmonitorX11 x11 #define _GLFW_PLATFORM_CURSOR_STATE _GLFWcursorX11 x11 // X11-specific per-window data // typedef struct _GLFWwindowX11 { Colormap colormap; Window handle; Window parent; XIC ic; GLFWbool overrideRedirect; GLFWbool iconified; GLFWbool maximized; // Whether the visual supports framebuffer transparency GLFWbool transparent; // Cached position and size used to filter out duplicate events int width, height; int xpos, ypos; // The last received cursor position, regardless of source int lastCursorPosX, lastCursorPosY; // The last position the cursor was warped to by GLFW int warpCursorPosX, warpCursorPosY; // The time of the last KeyPress event per keycode, for discarding // duplicate key events generated for some keys by ibus Time keyPressTimes[256]; } _GLFWwindowX11; // X11-specific global data // typedef struct _GLFWlibraryX11 { Display* display; int screen; Window root; // System content scale float contentScaleX, contentScaleY; // Helper window for IPC Window helperWindowHandle; // Invisible cursor for hidden cursor mode Cursor hiddenCursorHandle; // Context for mapping window XIDs to _GLFWwindow pointers XContext context; // XIM input method XIM im; // Most recent error code received by X error handler int errorCode; // Primary selection string (while the primary selection is owned) char* primarySelectionString; // Clipboard string (while the selection is owned) char* clipboardString; // Key name string char keynames[GLFW_KEY_LAST + 1][5]; // X11 keycode to GLFW key LUT short int keycodes[256]; // GLFW key to X11 keycode LUT short int scancodes[GLFW_KEY_LAST + 1]; // Where to place the cursor when re-enabled double restoreCursorPosX, restoreCursorPosY; // The window whose disabled cursor mode is active _GLFWwindow* disabledCursorWindow; int emptyEventPipe[2]; // Window manager atoms Atom NET_SUPPORTED; Atom NET_SUPPORTING_WM_CHECK; Atom WM_PROTOCOLS; Atom WM_STATE; Atom WM_DELETE_WINDOW; Atom NET_WM_NAME; Atom NET_WM_ICON_NAME; Atom NET_WM_ICON; Atom NET_WM_PID; Atom NET_WM_PING; Atom NET_WM_WINDOW_TYPE; Atom NET_WM_WINDOW_TYPE_NORMAL; Atom NET_WM_STATE; Atom NET_WM_STATE_ABOVE; Atom NET_WM_STATE_FULLSCREEN; Atom NET_WM_STATE_MAXIMIZED_VERT; Atom NET_WM_STATE_MAXIMIZED_HORZ; Atom NET_WM_STATE_DEMANDS_ATTENTION; Atom NET_WM_BYPASS_COMPOSITOR; Atom NET_WM_FULLSCREEN_MONITORS; Atom NET_WM_WINDOW_OPACITY; Atom NET_WM_CM_Sx; Atom NET_WORKAREA; Atom NET_CURRENT_DESKTOP; Atom NET_ACTIVE_WINDOW; Atom NET_FRAME_EXTENTS; Atom NET_REQUEST_FRAME_EXTENTS; Atom MOTIF_WM_HINTS; // Xdnd (drag and drop) atoms Atom XdndAware; Atom XdndEnter; Atom XdndPosition; Atom XdndStatus; Atom XdndActionCopy; Atom XdndDrop; Atom XdndFinished; Atom XdndSelection; Atom XdndTypeList; Atom text_uri_list; // Selection (clipboard) atoms Atom TARGETS; Atom MULTIPLE; Atom INCR; Atom CLIPBOARD; Atom PRIMARY; Atom CLIPBOARD_MANAGER; Atom SAVE_TARGETS; Atom NULL_; Atom UTF8_STRING; Atom COMPOUND_STRING; Atom ATOM_PAIR; Atom GLFW_SELECTION; struct { GLFWbool available; void* handle; int eventBase; int errorBase; int major; int minor; GLFWbool gammaBroken; GLFWbool monitorBroken; PFN_XRRAllocGamma AllocGamma; PFN_XRRFreeCrtcInfo FreeCrtcInfo; PFN_XRRFreeGamma FreeGamma; PFN_XRRFreeOutputInfo FreeOutputInfo; PFN_XRRFreeScreenResources FreeScreenResources; PFN_XRRGetCrtcGamma GetCrtcGamma; PFN_XRRGetCrtcGammaSize GetCrtcGammaSize; PFN_XRRGetCrtcInfo GetCrtcInfo; PFN_XRRGetOutputInfo GetOutputInfo; PFN_XRRGetOutputPrimary GetOutputPrimary; PFN_XRRGetScreenResourcesCurrent GetScreenResourcesCurrent; PFN_XRRQueryExtension QueryExtension; PFN_XRRQueryVersion QueryVersion; PFN_XRRSelectInput SelectInput; PFN_XRRSetCrtcConfig SetCrtcConfig; PFN_XRRSetCrtcGamma SetCrtcGamma; PFN_XRRUpdateConfiguration UpdateConfiguration; } randr; struct { GLFWbool available; GLFWbool detectable; int majorOpcode; int eventBase; int errorBase; int major; int minor; unsigned int group; } xkb; struct { int count; int timeout; int interval; int blanking; int exposure; } saver; struct { int version; Window source; Atom format; } xdnd; struct { void* handle; PFN_XcursorImageCreate ImageCreate; PFN_XcursorImageDestroy ImageDestroy; PFN_XcursorImageLoadCursor ImageLoadCursor; } xcursor; struct { GLFWbool available; void* handle; int major; int minor; PFN_XineramaIsActive IsActive; PFN_XineramaQueryExtension QueryExtension; PFN_XineramaQueryScreens QueryScreens; } xinerama; struct { void* handle; PFN_XGetXCBConnection GetXCBConnection; } x11xcb; struct { GLFWbool available; void* handle; int eventBase; int errorBase; PFN_XF86VidModeQueryExtension QueryExtension; PFN_XF86VidModeGetGammaRamp GetGammaRamp; PFN_XF86VidModeSetGammaRamp SetGammaRamp; PFN_XF86VidModeGetGammaRampSize GetGammaRampSize; } vidmode; struct { GLFWbool available; void* handle; int majorOpcode; int eventBase; int errorBase; int major; int minor; PFN_XIQueryVersion QueryVersion; PFN_XISelectEvents SelectEvents; } xi; struct { GLFWbool available; void* handle; int major; int minor; int eventBase; int errorBase; PFN_XRenderQueryExtension QueryExtension; PFN_XRenderQueryVersion QueryVersion; PFN_XRenderFindVisualFormat FindVisualFormat; } xrender; } _GLFWlibraryX11; // X11-specific per-monitor data // typedef struct _GLFWmonitorX11 { RROutput output; RRCrtc crtc; RRMode oldMode; // Index of corresponding Xinerama screen, // for EWMH full screen window placement int index; } _GLFWmonitorX11; // X11-specific per-cursor data // typedef struct _GLFWcursorX11 { Cursor handle; } _GLFWcursorX11; void _glfwPollMonitorsX11(void); void _glfwSetVideoModeX11(_GLFWmonitor* monitor, const GLFWvidmode* desired); void _glfwRestoreVideoModeX11(_GLFWmonitor* monitor); Cursor _glfwCreateCursorX11(const GLFWimage* image, int xhot, int yhot); unsigned long _glfwGetWindowPropertyX11(Window window, Atom property, Atom type, unsigned char** value); GLFWbool _glfwIsVisualTransparentX11(Visual* visual); void _glfwGrabErrorHandlerX11(void); void _glfwReleaseErrorHandlerX11(void); void _glfwInputErrorX11(int error, const char* message); void _glfwPushSelectionToManagerX11(void); #endif #elif defined(_GLFW_WAYLAND) #ifndef HEADER_GUARD_WL_PLATFORM_H #define HEADER_GUARD_WL_PLATFORM_H //======================================================================== // GLFW 3.3.7 Wayland - www.glfw.org //------------------------------------------------------------------------ // Copyright (c) 2014 Jonas Ã…dahl // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would // be appreciated but is not required. // // 2. Altered source versions must be plainly marked as such, and must not // be misrepresented as being the original software. // // 3. This notice may not be removed or altered from any source // distribution. // //======================================================================== #include #include #include #include typedef VkFlags VkWaylandSurfaceCreateFlagsKHR; typedef struct VkWaylandSurfaceCreateInfoKHR { VkStructureType sType; const void* pNext; VkWaylandSurfaceCreateFlagsKHR flags; struct wl_display* display; struct wl_surface* surface; } VkWaylandSurfaceCreateInfoKHR; typedef VkResult (APIENTRY *PFN_vkCreateWaylandSurfaceKHR)(VkInstance,const VkWaylandSurfaceCreateInfoKHR*,const VkAllocationCallbacks*,VkSurfaceKHR*); typedef VkBool32 (APIENTRY *PFN_vkGetPhysicalDeviceWaylandPresentationSupportKHR)(VkPhysicalDevice,uint32_t,struct wl_display*); #ifndef HEADER_GUARD_POSIX_THREAD_H #define HEADER_GUARD_POSIX_THREAD_H //======================================================================== // GLFW 3.3.7 POSIX - www.glfw.org //------------------------------------------------------------------------ // Copyright (c) 2002-2006 Marcus Geelnard // Copyright (c) 2006-2017 Camilla Löwy // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would // be appreciated but is not required. // // 2. Altered source versions must be plainly marked as such, and must not // be misrepresented as being the original software. // // 3. This notice may not be removed or altered from any source // distribution. // //======================================================================== #include #define _GLFW_PLATFORM_TLS_STATE _GLFWtlsPOSIX posix #define _GLFW_PLATFORM_MUTEX_STATE _GLFWmutexPOSIX posix // POSIX-specific thread local storage data // typedef struct _GLFWtlsPOSIX { GLFWbool allocated; pthread_key_t key; } _GLFWtlsPOSIX; // POSIX-specific mutex data // typedef struct _GLFWmutexPOSIX { GLFWbool allocated; pthread_mutex_t handle; } _GLFWmutexPOSIX; #endif #ifndef HEADER_GUARD_POSIX_TIME_H #define HEADER_GUARD_POSIX_TIME_H //======================================================================== // GLFW 3.3.7 POSIX - www.glfw.org //------------------------------------------------------------------------ // Copyright (c) 2002-2006 Marcus Geelnard // Copyright (c) 2006-2017 Camilla Löwy // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would // be appreciated but is not required. // // 2. Altered source versions must be plainly marked as such, and must not // be misrepresented as being the original software. // // 3. This notice may not be removed or altered from any source // distribution. // //======================================================================== #define _GLFW_PLATFORM_LIBRARY_TIMER_STATE _GLFWtimerPOSIX posix #include // POSIX-specific global timer data // typedef struct _GLFWtimerPOSIX { GLFWbool monotonic; uint64_t frequency; } _GLFWtimerPOSIX; void _glfwInitTimerPOSIX(void); #endif #ifdef __linux__ #ifndef HEADER_GUARD_LINUX_JOYSTICK_H #define HEADER_GUARD_LINUX_JOYSTICK_H //======================================================================== // GLFW 3.3.7 Linux - www.glfw.org //------------------------------------------------------------------------ // Copyright (c) 2014 Jonas Ã…dahl // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would // be appreciated but is not required. // // 2. Altered source versions must be plainly marked as such, and must not // be misrepresented as being the original software. // // 3. This notice may not be removed or altered from any source // distribution. // //======================================================================== #include #include #include #define _GLFW_PLATFORM_JOYSTICK_STATE _GLFWjoystickLinux linjs #define _GLFW_PLATFORM_LIBRARY_JOYSTICK_STATE _GLFWlibraryLinux linjs #define _GLFW_PLATFORM_MAPPING_NAME "Linux" #define GLFW_BUILD_LINUX_MAPPINGS // Linux-specific joystick data // typedef struct _GLFWjoystickLinux { int fd; char path[PATH_MAX]; int keyMap[KEY_CNT - BTN_MISC]; int absMap[ABS_CNT]; struct input_absinfo absInfo[ABS_CNT]; int hats[4][2]; } _GLFWjoystickLinux; // Linux-specific joystick API data // typedef struct _GLFWlibraryLinux { int inotify; int watch; regex_t regex; GLFWbool dropped; } _GLFWlibraryLinux; GLFWbool _glfwInitJoysticksLinux(void); void _glfwTerminateJoysticksLinux(void); void _glfwDetectJoystickConnectionLinux(void); #endif #else #ifndef HEADER_GUARD_NULL_JOYSTICK_H #define HEADER_GUARD_NULL_JOYSTICK_H //======================================================================== // GLFW 3.3.7 - www.glfw.org //------------------------------------------------------------------------ // Copyright (c) 2006-2017 Camilla Löwy // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would // be appreciated but is not required. // // 2. Altered source versions must be plainly marked as such, and must not // be misrepresented as being the original software. // // 3. This notice may not be removed or altered from any source // distribution. // //======================================================================== #define _GLFW_PLATFORM_JOYSTICK_STATE struct { int dummyJoystick; } #define _GLFW_PLATFORM_LIBRARY_JOYSTICK_STATE struct { int dummyLibraryJoystick; } #define _GLFW_PLATFORM_MAPPING_NAME "" #endif #endif #ifndef HEADER_GUARD_XKB_UNICODE_H #define HEADER_GUARD_XKB_UNICODE_H //======================================================================== // GLFW 3.3.7 Linux - www.glfw.org //------------------------------------------------------------------------ // Copyright (c) 2014 Jonas Ã…dahl // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would // be appreciated but is not required. // // 2. Altered source versions must be plainly marked as such, and must not // be misrepresented as being the original software. // // 3. This notice may not be removed or altered from any source // distribution. // //======================================================================== #define GLFW_INVALID_CODEPOINT 0xffffffffu uint32_t _glfwKeySym2Unicode(unsigned int keysym); #endif #ifndef HEADER_GUARD_EGL_CONTEXT_H #define HEADER_GUARD_EGL_CONTEXT_H //======================================================================== // GLFW 3.3.7 EGL - www.glfw.org //------------------------------------------------------------------------ // Copyright (c) 2002-2006 Marcus Geelnard // Copyright (c) 2006-2017 Camilla Löwy // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would // be appreciated but is not required. // // 2. Altered source versions must be plainly marked as such, and must not // be misrepresented as being the original software. // // 3. This notice may not be removed or altered from any source // distribution. // //======================================================================== #if defined(_GLFW_USE_EGLPLATFORM_H) #include #elif defined(_GLFW_WIN32) #define EGLAPIENTRY __stdcall typedef HDC EGLNativeDisplayType; typedef HWND EGLNativeWindowType; #elif defined(_GLFW_COCOA) #define EGLAPIENTRY typedef void* EGLNativeDisplayType; typedef id EGLNativeWindowType; #elif defined(_GLFW_X11) #define EGLAPIENTRY typedef Display* EGLNativeDisplayType; typedef Window EGLNativeWindowType; #elif defined(_GLFW_WAYLAND) #define EGLAPIENTRY typedef struct wl_display* EGLNativeDisplayType; typedef struct wl_egl_window* EGLNativeWindowType; #else #error "No supported EGL platform selected" #endif #define EGL_SUCCESS 0x3000 #define EGL_NOT_INITIALIZED 0x3001 #define EGL_BAD_ACCESS 0x3002 #define EGL_BAD_ALLOC 0x3003 #define EGL_BAD_ATTRIBUTE 0x3004 #define EGL_BAD_CONFIG 0x3005 #define EGL_BAD_CONTEXT 0x3006 #define EGL_BAD_CURRENT_SURFACE 0x3007 #define EGL_BAD_DISPLAY 0x3008 #define EGL_BAD_MATCH 0x3009 #define EGL_BAD_NATIVE_PIXMAP 0x300a #define EGL_BAD_NATIVE_WINDOW 0x300b #define EGL_BAD_PARAMETER 0x300c #define EGL_BAD_SURFACE 0x300d #define EGL_CONTEXT_LOST 0x300e #define EGL_COLOR_BUFFER_TYPE 0x303f #define EGL_RGB_BUFFER 0x308e #define EGL_SURFACE_TYPE 0x3033 #define EGL_WINDOW_BIT 0x0004 #define EGL_RENDERABLE_TYPE 0x3040 #define EGL_OPENGL_ES_BIT 0x0001 #define EGL_OPENGL_ES2_BIT 0x0004 #define EGL_OPENGL_BIT 0x0008 #define EGL_ALPHA_SIZE 0x3021 #define EGL_BLUE_SIZE 0x3022 #define EGL_GREEN_SIZE 0x3023 #define EGL_RED_SIZE 0x3024 #define EGL_DEPTH_SIZE 0x3025 #define EGL_STENCIL_SIZE 0x3026 #define EGL_SAMPLES 0x3031 #define EGL_OPENGL_ES_API 0x30a0 #define EGL_OPENGL_API 0x30a2 #define EGL_NONE 0x3038 #define EGL_RENDER_BUFFER 0x3086 #define EGL_SINGLE_BUFFER 0x3085 #define EGL_EXTENSIONS 0x3055 #define EGL_CONTEXT_CLIENT_VERSION 0x3098 #define EGL_NATIVE_VISUAL_ID 0x302e #define EGL_NO_SURFACE ((EGLSurface) 0) #define EGL_NO_DISPLAY ((EGLDisplay) 0) #define EGL_NO_CONTEXT ((EGLContext) 0) #define EGL_DEFAULT_DISPLAY ((EGLNativeDisplayType) 0) #define EGL_CONTEXT_OPENGL_FORWARD_COMPATIBLE_BIT_KHR 0x00000002 #define EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT_KHR 0x00000001 #define EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT_KHR 0x00000002 #define EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR 0x00000001 #define EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_KHR 0x31bd #define EGL_NO_RESET_NOTIFICATION_KHR 0x31be #define EGL_LOSE_CONTEXT_ON_RESET_KHR 0x31bf #define EGL_CONTEXT_OPENGL_ROBUST_ACCESS_BIT_KHR 0x00000004 #define EGL_CONTEXT_MAJOR_VERSION_KHR 0x3098 #define EGL_CONTEXT_MINOR_VERSION_KHR 0x30fb #define EGL_CONTEXT_OPENGL_PROFILE_MASK_KHR 0x30fd #define EGL_CONTEXT_FLAGS_KHR 0x30fc #define EGL_CONTEXT_OPENGL_NO_ERROR_KHR 0x31b3 #define EGL_GL_COLORSPACE_KHR 0x309d #define EGL_GL_COLORSPACE_SRGB_KHR 0x3089 #define EGL_CONTEXT_RELEASE_BEHAVIOR_KHR 0x2097 #define EGL_CONTEXT_RELEASE_BEHAVIOR_NONE_KHR 0 #define EGL_CONTEXT_RELEASE_BEHAVIOR_FLUSH_KHR 0x2098 #define EGL_PRESENT_OPAQUE_EXT 0x31df typedef int EGLint; typedef unsigned int EGLBoolean; typedef unsigned int EGLenum; typedef void* EGLConfig; typedef void* EGLContext; typedef void* EGLDisplay; typedef void* EGLSurface; // EGL function pointer typedefs typedef EGLBoolean (EGLAPIENTRY * PFN_eglGetConfigAttrib)(EGLDisplay,EGLConfig,EGLint,EGLint*); typedef EGLBoolean (EGLAPIENTRY * PFN_eglGetConfigs)(EGLDisplay,EGLConfig*,EGLint,EGLint*); typedef EGLDisplay (EGLAPIENTRY * PFN_eglGetDisplay)(EGLNativeDisplayType); typedef EGLint (EGLAPIENTRY * PFN_eglGetError)(void); typedef EGLBoolean (EGLAPIENTRY * PFN_eglInitialize)(EGLDisplay,EGLint*,EGLint*); typedef EGLBoolean (EGLAPIENTRY * PFN_eglTerminate)(EGLDisplay); typedef EGLBoolean (EGLAPIENTRY * PFN_eglBindAPI)(EGLenum); typedef EGLContext (EGLAPIENTRY * PFN_eglCreateContext)(EGLDisplay,EGLConfig,EGLContext,const EGLint*); typedef EGLBoolean (EGLAPIENTRY * PFN_eglDestroySurface)(EGLDisplay,EGLSurface); typedef EGLBoolean (EGLAPIENTRY * PFN_eglDestroyContext)(EGLDisplay,EGLContext); typedef EGLSurface (EGLAPIENTRY * PFN_eglCreateWindowSurface)(EGLDisplay,EGLConfig,EGLNativeWindowType,const EGLint*); typedef EGLBoolean (EGLAPIENTRY * PFN_eglMakeCurrent)(EGLDisplay,EGLSurface,EGLSurface,EGLContext); typedef EGLBoolean (EGLAPIENTRY * PFN_eglSwapBuffers)(EGLDisplay,EGLSurface); typedef EGLBoolean (EGLAPIENTRY * PFN_eglSwapInterval)(EGLDisplay,EGLint); typedef const char* (EGLAPIENTRY * PFN_eglQueryString)(EGLDisplay,EGLint); typedef GLFWglproc (EGLAPIENTRY * PFN_eglGetProcAddress)(const char*); #define eglGetConfigAttrib _glfw.egl.GetConfigAttrib #define eglGetConfigs _glfw.egl.GetConfigs #define eglGetDisplay _glfw.egl.GetDisplay #define eglGetError _glfw.egl.GetError #define eglInitialize _glfw.egl.Initialize #define eglTerminate _glfw.egl.Terminate #define eglBindAPI _glfw.egl.BindAPI #define eglCreateContext _glfw.egl.CreateContext #define eglDestroySurface _glfw.egl.DestroySurface #define eglDestroyContext _glfw.egl.DestroyContext #define eglCreateWindowSurface _glfw.egl.CreateWindowSurface #define eglMakeCurrent _glfw.egl.MakeCurrent #define eglSwapBuffers _glfw.egl.SwapBuffers #define eglSwapInterval _glfw.egl.SwapInterval #define eglQueryString _glfw.egl.QueryString #define eglGetProcAddress _glfw.egl.GetProcAddress #define _GLFW_EGL_CONTEXT_STATE _GLFWcontextEGL egl #define _GLFW_EGL_LIBRARY_CONTEXT_STATE _GLFWlibraryEGL egl // EGL-specific per-context data // typedef struct _GLFWcontextEGL { EGLConfig config; EGLContext handle; EGLSurface surface; void* client; } _GLFWcontextEGL; // EGL-specific global data // typedef struct _GLFWlibraryEGL { EGLDisplay display; EGLint major, minor; GLFWbool prefix; GLFWbool KHR_create_context; GLFWbool KHR_create_context_no_error; GLFWbool KHR_gl_colorspace; GLFWbool KHR_get_all_proc_addresses; GLFWbool KHR_context_flush_control; GLFWbool EXT_present_opaque; void* handle; PFN_eglGetConfigAttrib GetConfigAttrib; PFN_eglGetConfigs GetConfigs; PFN_eglGetDisplay GetDisplay; PFN_eglGetError GetError; PFN_eglInitialize Initialize; PFN_eglTerminate Terminate; PFN_eglBindAPI BindAPI; PFN_eglCreateContext CreateContext; PFN_eglDestroySurface DestroySurface; PFN_eglDestroyContext DestroyContext; PFN_eglCreateWindowSurface CreateWindowSurface; PFN_eglMakeCurrent MakeCurrent; PFN_eglSwapBuffers SwapBuffers; PFN_eglSwapInterval SwapInterval; PFN_eglQueryString QueryString; PFN_eglGetProcAddress GetProcAddress; } _GLFWlibraryEGL; GLFWbool _glfwInitEGL(void); void _glfwTerminateEGL(void); GLFWbool _glfwCreateContextEGL(_GLFWwindow* window, const _GLFWctxconfig* ctxconfig, const _GLFWfbconfig* fbconfig); #if defined(_GLFW_X11) GLFWbool _glfwChooseVisualEGL(const _GLFWwndconfig* wndconfig, const _GLFWctxconfig* ctxconfig, const _GLFWfbconfig* fbconfig, Visual** visual, int* depth); #endif /*_GLFW_X11*/ #endif #ifndef HEADER_GUARD_OSMESA_CONTEXT_H #define HEADER_GUARD_OSMESA_CONTEXT_H //======================================================================== // GLFW 3.3.7 OSMesa - www.glfw.org //------------------------------------------------------------------------ // Copyright (c) 2016 Google Inc. // Copyright (c) 2016-2017 Camilla Löwy // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would // be appreciated but is not required. // // 2. Altered source versions must be plainly marked as such, and must not // be misrepresented as being the original software. // // 3. This notice may not be removed or altered from any source // distribution. // //======================================================================== #define OSMESA_RGBA 0x1908 #define OSMESA_FORMAT 0x22 #define OSMESA_DEPTH_BITS 0x30 #define OSMESA_STENCIL_BITS 0x31 #define OSMESA_ACCUM_BITS 0x32 #define OSMESA_PROFILE 0x33 #define OSMESA_CORE_PROFILE 0x34 #define OSMESA_COMPAT_PROFILE 0x35 #define OSMESA_CONTEXT_MAJOR_VERSION 0x36 #define OSMESA_CONTEXT_MINOR_VERSION 0x37 typedef void* OSMesaContext; typedef void (*OSMESAproc)(void); typedef OSMesaContext (GLAPIENTRY * PFN_OSMesaCreateContextExt)(GLenum,GLint,GLint,GLint,OSMesaContext); typedef OSMesaContext (GLAPIENTRY * PFN_OSMesaCreateContextAttribs)(const int*,OSMesaContext); typedef void (GLAPIENTRY * PFN_OSMesaDestroyContext)(OSMesaContext); typedef int (GLAPIENTRY * PFN_OSMesaMakeCurrent)(OSMesaContext,void*,int,int,int); typedef int (GLAPIENTRY * PFN_OSMesaGetColorBuffer)(OSMesaContext,int*,int*,int*,void**); typedef int (GLAPIENTRY * PFN_OSMesaGetDepthBuffer)(OSMesaContext,int*,int*,int*,void**); typedef GLFWglproc (GLAPIENTRY * PFN_OSMesaGetProcAddress)(const char*); #define OSMesaCreateContextExt _glfw.osmesa.CreateContextExt #define OSMesaCreateContextAttribs _glfw.osmesa.CreateContextAttribs #define OSMesaDestroyContext _glfw.osmesa.DestroyContext #define OSMesaMakeCurrent _glfw.osmesa.MakeCurrent #define OSMesaGetColorBuffer _glfw.osmesa.GetColorBuffer #define OSMesaGetDepthBuffer _glfw.osmesa.GetDepthBuffer #define OSMesaGetProcAddress _glfw.osmesa.GetProcAddress #define _GLFW_OSMESA_CONTEXT_STATE _GLFWcontextOSMesa osmesa #define _GLFW_OSMESA_LIBRARY_CONTEXT_STATE _GLFWlibraryOSMesa osmesa // OSMesa-specific per-context data // typedef struct _GLFWcontextOSMesa { OSMesaContext handle; int width; int height; void* buffer; } _GLFWcontextOSMesa; // OSMesa-specific global data // typedef struct _GLFWlibraryOSMesa { void* handle; PFN_OSMesaCreateContextExt CreateContextExt; PFN_OSMesaCreateContextAttribs CreateContextAttribs; PFN_OSMesaDestroyContext DestroyContext; PFN_OSMesaMakeCurrent MakeCurrent; PFN_OSMesaGetColorBuffer GetColorBuffer; PFN_OSMesaGetDepthBuffer GetDepthBuffer; PFN_OSMesaGetProcAddress GetProcAddress; } _GLFWlibraryOSMesa; GLFWbool _glfwInitOSMesa(void); void _glfwTerminateOSMesa(void); GLFWbool _glfwCreateContextOSMesa(_GLFWwindow* window, const _GLFWctxconfig* ctxconfig, const _GLFWfbconfig* fbconfig); #endif #include "wayland-xdg-shell-client-protocol.h" #include "wayland-xdg-decoration-client-protocol.h" #include "wayland-viewporter-client-protocol.h" #include "wayland-relative-pointer-unstable-v1-client-protocol.h" #include "wayland-pointer-constraints-unstable-v1-client-protocol.h" #include "wayland-idle-inhibit-unstable-v1-client-protocol.h" #define _glfw_dlopen(name) dlopen(name, RTLD_LAZY | RTLD_LOCAL) #define _glfw_dlclose(handle) dlclose(handle) #define _glfw_dlsym(handle, name) dlsym(handle, name) #define _GLFW_EGL_NATIVE_WINDOW ((EGLNativeWindowType) window->wl.native) #define _GLFW_EGL_NATIVE_DISPLAY ((EGLNativeDisplayType) _glfw.wl.display) #define _GLFW_PLATFORM_WINDOW_STATE _GLFWwindowWayland wl #define _GLFW_PLATFORM_LIBRARY_WINDOW_STATE _GLFWlibraryWayland wl #define _GLFW_PLATFORM_MONITOR_STATE _GLFWmonitorWayland wl #define _GLFW_PLATFORM_CURSOR_STATE _GLFWcursorWayland wl #define _GLFW_PLATFORM_CONTEXT_STATE struct { int dummyContext; } #define _GLFW_PLATFORM_LIBRARY_CONTEXT_STATE struct { int dummyLibraryContext; } struct wl_cursor_image { uint32_t width; uint32_t height; uint32_t hotspot_x; uint32_t hotspot_y; uint32_t delay; }; struct wl_cursor { unsigned int image_count; struct wl_cursor_image** images; char* name; }; typedef struct wl_cursor_theme* (* PFN_wl_cursor_theme_load)(const char*, int, struct wl_shm*); typedef void (* PFN_wl_cursor_theme_destroy)(struct wl_cursor_theme*); typedef struct wl_cursor* (* PFN_wl_cursor_theme_get_cursor)(struct wl_cursor_theme*, const char*); typedef struct wl_buffer* (* PFN_wl_cursor_image_get_buffer)(struct wl_cursor_image*); #define wl_cursor_theme_load _glfw.wl.cursor.theme_load #define wl_cursor_theme_destroy _glfw.wl.cursor.theme_destroy #define wl_cursor_theme_get_cursor _glfw.wl.cursor.theme_get_cursor #define wl_cursor_image_get_buffer _glfw.wl.cursor.image_get_buffer typedef struct wl_egl_window* (* PFN_wl_egl_window_create)(struct wl_surface*, int, int); typedef void (* PFN_wl_egl_window_destroy)(struct wl_egl_window*); typedef void (* PFN_wl_egl_window_resize)(struct wl_egl_window*, int, int, int, int); #define wl_egl_window_create _glfw.wl.egl.window_create #define wl_egl_window_destroy _glfw.wl.egl.window_destroy #define wl_egl_window_resize _glfw.wl.egl.window_resize typedef struct xkb_context* (* PFN_xkb_context_new)(enum xkb_context_flags); typedef void (* PFN_xkb_context_unref)(struct xkb_context*); typedef struct xkb_keymap* (* PFN_xkb_keymap_new_from_string)(struct xkb_context*, const char*, enum xkb_keymap_format, enum xkb_keymap_compile_flags); typedef void (* PFN_xkb_keymap_unref)(struct xkb_keymap*); typedef xkb_mod_index_t (* PFN_xkb_keymap_mod_get_index)(struct xkb_keymap*, const char*); typedef int (* PFN_xkb_keymap_key_repeats)(struct xkb_keymap*, xkb_keycode_t); typedef int (* PFN_xkb_keymap_key_get_syms_by_level)(struct xkb_keymap*,xkb_keycode_t,xkb_layout_index_t,xkb_level_index_t,const xkb_keysym_t**); typedef struct xkb_state* (* PFN_xkb_state_new)(struct xkb_keymap*); typedef void (* PFN_xkb_state_unref)(struct xkb_state*); typedef int (* PFN_xkb_state_key_get_syms)(struct xkb_state*, xkb_keycode_t, const xkb_keysym_t**); typedef enum xkb_state_component (* PFN_xkb_state_update_mask)(struct xkb_state*, xkb_mod_mask_t, xkb_mod_mask_t, xkb_mod_mask_t, xkb_layout_index_t, xkb_layout_index_t, xkb_layout_index_t); typedef xkb_mod_mask_t (* PFN_xkb_state_serialize_mods)(struct xkb_state*, enum xkb_state_component); typedef xkb_layout_index_t (* PFN_xkb_state_key_get_layout)(struct xkb_state*,xkb_keycode_t); #define xkb_context_new _glfw.wl.xkb.context_new #define xkb_context_unref _glfw.wl.xkb.context_unref #define xkb_keymap_new_from_string _glfw.wl.xkb.keymap_new_from_string #define xkb_keymap_unref _glfw.wl.xkb.keymap_unref #define xkb_keymap_mod_get_index _glfw.wl.xkb.keymap_mod_get_index #define xkb_keymap_key_repeats _glfw.wl.xkb.keymap_key_repeats #define xkb_keymap_key_get_syms_by_level _glfw.wl.xkb.keymap_key_get_syms_by_level #define xkb_state_new _glfw.wl.xkb.state_new #define xkb_state_unref _glfw.wl.xkb.state_unref #define xkb_state_key_get_syms _glfw.wl.xkb.state_key_get_syms #define xkb_state_update_mask _glfw.wl.xkb.state_update_mask #define xkb_state_serialize_mods _glfw.wl.xkb.state_serialize_mods #define xkb_state_key_get_layout _glfw.wl.xkb.state_key_get_layout typedef struct xkb_compose_table* (* PFN_xkb_compose_table_new_from_locale)(struct xkb_context*, const char*, enum xkb_compose_compile_flags); typedef void (* PFN_xkb_compose_table_unref)(struct xkb_compose_table*); typedef struct xkb_compose_state* (* PFN_xkb_compose_state_new)(struct xkb_compose_table*, enum xkb_compose_state_flags); typedef void (* PFN_xkb_compose_state_unref)(struct xkb_compose_state*); typedef enum xkb_compose_feed_result (* PFN_xkb_compose_state_feed)(struct xkb_compose_state*, xkb_keysym_t); typedef enum xkb_compose_status (* PFN_xkb_compose_state_get_status)(struct xkb_compose_state*); typedef xkb_keysym_t (* PFN_xkb_compose_state_get_one_sym)(struct xkb_compose_state*); #define xkb_compose_table_new_from_locale _glfw.wl.xkb.compose_table_new_from_locale #define xkb_compose_table_unref _glfw.wl.xkb.compose_table_unref #define xkb_compose_state_new _glfw.wl.xkb.compose_state_new #define xkb_compose_state_unref _glfw.wl.xkb.compose_state_unref #define xkb_compose_state_feed _glfw.wl.xkb.compose_state_feed #define xkb_compose_state_get_status _glfw.wl.xkb.compose_state_get_status #define xkb_compose_state_get_one_sym _glfw.wl.xkb.compose_state_get_one_sym #define _GLFW_DECORATION_WIDTH 4 #define _GLFW_DECORATION_TOP 24 #define _GLFW_DECORATION_VERTICAL (_GLFW_DECORATION_TOP + _GLFW_DECORATION_WIDTH) #define _GLFW_DECORATION_HORIZONTAL (2 * _GLFW_DECORATION_WIDTH) typedef enum _GLFWdecorationSideWayland { mainWindow, topDecoration, leftDecoration, rightDecoration, bottomDecoration, } _GLFWdecorationSideWayland; typedef struct _GLFWdecorationWayland { struct wl_surface* surface; struct wl_subsurface* subsurface; struct wp_viewport* viewport; } _GLFWdecorationWayland; // Wayland-specific per-window data // typedef struct _GLFWwindowWayland { int width, height; GLFWbool visible; GLFWbool maximized; GLFWbool hovered; GLFWbool transparent; struct wl_surface* surface; struct wl_egl_window* native; struct wl_shell_surface* shellSurface; struct wl_callback* callback; struct { struct xdg_surface* surface; struct xdg_toplevel* toplevel; struct zxdg_toplevel_decoration_v1* decoration; } xdg; _GLFWcursor* currentCursor; double cursorPosX, cursorPosY; char* title; // We need to track the monitors the window spans on to calculate the // optimal scaling factor. int scale; _GLFWmonitor** monitors; int monitorsCount; int monitorsSize; struct { struct zwp_relative_pointer_v1* relativePointer; struct zwp_locked_pointer_v1* lockedPointer; } pointerLock; struct zwp_idle_inhibitor_v1* idleInhibitor; GLFWbool wasFullscreen; struct { GLFWbool serverSide; struct wl_buffer* buffer; _GLFWdecorationWayland top, left, right, bottom; int focus; } decorations; } _GLFWwindowWayland; // Wayland-specific global data // typedef struct _GLFWlibraryWayland { struct wl_display* display; struct wl_registry* registry; struct wl_compositor* compositor; struct wl_subcompositor* subcompositor; struct wl_shell* shell; struct wl_shm* shm; struct wl_seat* seat; struct wl_pointer* pointer; struct wl_keyboard* keyboard; struct wl_data_device_manager* dataDeviceManager; struct wl_data_device* dataDevice; struct wl_data_offer* dataOffer; struct wl_data_source* dataSource; struct xdg_wm_base* wmBase; struct zxdg_decoration_manager_v1* decorationManager; struct wp_viewporter* viewporter; struct zwp_relative_pointer_manager_v1* relativePointerManager; struct zwp_pointer_constraints_v1* pointerConstraints; struct zwp_idle_inhibit_manager_v1* idleInhibitManager; int compositorVersion; int seatVersion; struct wl_cursor_theme* cursorTheme; struct wl_cursor_theme* cursorThemeHiDPI; struct wl_surface* cursorSurface; const char* cursorPreviousName; int cursorTimerfd; uint32_t serial; uint32_t pointerEnterSerial; int32_t keyboardRepeatRate; int32_t keyboardRepeatDelay; int keyboardLastKey; int keyboardLastScancode; char* clipboardString; size_t clipboardSize; char* clipboardSendString; size_t clipboardSendSize; int timerfd; short int keycodes[256]; short int scancodes[GLFW_KEY_LAST + 1]; char keynames[GLFW_KEY_LAST + 1][5]; struct { void* handle; struct xkb_context* context; struct xkb_keymap* keymap; struct xkb_state* state; struct xkb_compose_state* composeState; xkb_mod_mask_t controlMask; xkb_mod_mask_t altMask; xkb_mod_mask_t shiftMask; xkb_mod_mask_t superMask; xkb_mod_mask_t capsLockMask; xkb_mod_mask_t numLockMask; unsigned int modifiers; PFN_xkb_context_new context_new; PFN_xkb_context_unref context_unref; PFN_xkb_keymap_new_from_string keymap_new_from_string; PFN_xkb_keymap_unref keymap_unref; PFN_xkb_keymap_mod_get_index keymap_mod_get_index; PFN_xkb_keymap_key_repeats keymap_key_repeats; PFN_xkb_keymap_key_get_syms_by_level keymap_key_get_syms_by_level; PFN_xkb_state_new state_new; PFN_xkb_state_unref state_unref; PFN_xkb_state_key_get_syms state_key_get_syms; PFN_xkb_state_update_mask state_update_mask; PFN_xkb_state_serialize_mods state_serialize_mods; PFN_xkb_state_key_get_layout state_key_get_layout; PFN_xkb_compose_table_new_from_locale compose_table_new_from_locale; PFN_xkb_compose_table_unref compose_table_unref; PFN_xkb_compose_state_new compose_state_new; PFN_xkb_compose_state_unref compose_state_unref; PFN_xkb_compose_state_feed compose_state_feed; PFN_xkb_compose_state_get_status compose_state_get_status; PFN_xkb_compose_state_get_one_sym compose_state_get_one_sym; } xkb; _GLFWwindow* pointerFocus; _GLFWwindow* keyboardFocus; struct { void* handle; PFN_wl_cursor_theme_load theme_load; PFN_wl_cursor_theme_destroy theme_destroy; PFN_wl_cursor_theme_get_cursor theme_get_cursor; PFN_wl_cursor_image_get_buffer image_get_buffer; } cursor; struct { void* handle; PFN_wl_egl_window_create window_create; PFN_wl_egl_window_destroy window_destroy; PFN_wl_egl_window_resize window_resize; } egl; } _GLFWlibraryWayland; // Wayland-specific per-monitor data // typedef struct _GLFWmonitorWayland { struct wl_output* output; uint32_t name; int currentMode; int x; int y; int scale; } _GLFWmonitorWayland; // Wayland-specific per-cursor data // typedef struct _GLFWcursorWayland { struct wl_cursor* cursor; struct wl_cursor* cursorHiDPI; struct wl_buffer* buffer; int width, height; int xhot, yhot; int currentImage; } _GLFWcursorWayland; void _glfwAddOutputWayland(uint32_t name, uint32_t version); GLFWbool _glfwInputTextWayland(_GLFWwindow* window, uint32_t scancode); #endif #elif defined(_GLFW_OSMESA) #ifndef HEADER_GUARD_NULL_PLATFORM_H #define HEADER_GUARD_NULL_PLATFORM_H //======================================================================== // GLFW 3.3.7 - www.glfw.org //------------------------------------------------------------------------ // Copyright (c) 2016 Google Inc. // Copyright (c) 2016-2017 Camilla Löwy // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would // be appreciated but is not required. // // 2. Altered source versions must be plainly marked as such, and must not // be misrepresented as being the original software. // // 3. This notice may not be removed or altered from any source // distribution. // //======================================================================== #include #define _GLFW_PLATFORM_WINDOW_STATE _GLFWwindowNull null #define _GLFW_PLATFORM_CONTEXT_STATE struct { int dummyContext; } #define _GLFW_PLATFORM_MONITOR_STATE struct { int dummyMonitor; } #define _GLFW_PLATFORM_CURSOR_STATE struct { int dummyCursor; } #define _GLFW_PLATFORM_LIBRARY_WINDOW_STATE struct { int dummyLibraryWindow; } #define _GLFW_PLATFORM_LIBRARY_CONTEXT_STATE struct { int dummyLibraryContext; } #define _GLFW_EGL_CONTEXT_STATE struct { int dummyEGLContext; } #define _GLFW_EGL_LIBRARY_CONTEXT_STATE struct { int dummyEGLLibraryContext; } #ifndef HEADER_GUARD_OSMESA_CONTEXT_H #define HEADER_GUARD_OSMESA_CONTEXT_H //======================================================================== // GLFW 3.3.7 OSMesa - www.glfw.org //------------------------------------------------------------------------ // Copyright (c) 2016 Google Inc. // Copyright (c) 2016-2017 Camilla Löwy // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would // be appreciated but is not required. // // 2. Altered source versions must be plainly marked as such, and must not // be misrepresented as being the original software. // // 3. This notice may not be removed or altered from any source // distribution. // //======================================================================== #define OSMESA_RGBA 0x1908 #define OSMESA_FORMAT 0x22 #define OSMESA_DEPTH_BITS 0x30 #define OSMESA_STENCIL_BITS 0x31 #define OSMESA_ACCUM_BITS 0x32 #define OSMESA_PROFILE 0x33 #define OSMESA_CORE_PROFILE 0x34 #define OSMESA_COMPAT_PROFILE 0x35 #define OSMESA_CONTEXT_MAJOR_VERSION 0x36 #define OSMESA_CONTEXT_MINOR_VERSION 0x37 typedef void* OSMesaContext; typedef void (*OSMESAproc)(void); typedef OSMesaContext (GLAPIENTRY * PFN_OSMesaCreateContextExt)(GLenum,GLint,GLint,GLint,OSMesaContext); typedef OSMesaContext (GLAPIENTRY * PFN_OSMesaCreateContextAttribs)(const int*,OSMesaContext); typedef void (GLAPIENTRY * PFN_OSMesaDestroyContext)(OSMesaContext); typedef int (GLAPIENTRY * PFN_OSMesaMakeCurrent)(OSMesaContext,void*,int,int,int); typedef int (GLAPIENTRY * PFN_OSMesaGetColorBuffer)(OSMesaContext,int*,int*,int*,void**); typedef int (GLAPIENTRY * PFN_OSMesaGetDepthBuffer)(OSMesaContext,int*,int*,int*,void**); typedef GLFWglproc (GLAPIENTRY * PFN_OSMesaGetProcAddress)(const char*); #define OSMesaCreateContextExt _glfw.osmesa.CreateContextExt #define OSMesaCreateContextAttribs _glfw.osmesa.CreateContextAttribs #define OSMesaDestroyContext _glfw.osmesa.DestroyContext #define OSMesaMakeCurrent _glfw.osmesa.MakeCurrent #define OSMesaGetColorBuffer _glfw.osmesa.GetColorBuffer #define OSMesaGetDepthBuffer _glfw.osmesa.GetDepthBuffer #define OSMesaGetProcAddress _glfw.osmesa.GetProcAddress #define _GLFW_OSMESA_CONTEXT_STATE _GLFWcontextOSMesa osmesa #define _GLFW_OSMESA_LIBRARY_CONTEXT_STATE _GLFWlibraryOSMesa osmesa // OSMesa-specific per-context data // typedef struct _GLFWcontextOSMesa { OSMesaContext handle; int width; int height; void* buffer; } _GLFWcontextOSMesa; // OSMesa-specific global data // typedef struct _GLFWlibraryOSMesa { void* handle; PFN_OSMesaCreateContextExt CreateContextExt; PFN_OSMesaCreateContextAttribs CreateContextAttribs; PFN_OSMesaDestroyContext DestroyContext; PFN_OSMesaMakeCurrent MakeCurrent; PFN_OSMesaGetColorBuffer GetColorBuffer; PFN_OSMesaGetDepthBuffer GetDepthBuffer; PFN_OSMesaGetProcAddress GetProcAddress; } _GLFWlibraryOSMesa; GLFWbool _glfwInitOSMesa(void); void _glfwTerminateOSMesa(void); GLFWbool _glfwCreateContextOSMesa(_GLFWwindow* window, const _GLFWctxconfig* ctxconfig, const _GLFWfbconfig* fbconfig); #endif #ifndef HEADER_GUARD_POSIX_TIME_H #define HEADER_GUARD_POSIX_TIME_H //======================================================================== // GLFW 3.3.7 POSIX - www.glfw.org //------------------------------------------------------------------------ // Copyright (c) 2002-2006 Marcus Geelnard // Copyright (c) 2006-2017 Camilla Löwy // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would // be appreciated but is not required. // // 2. Altered source versions must be plainly marked as such, and must not // be misrepresented as being the original software. // // 3. This notice may not be removed or altered from any source // distribution. // //======================================================================== #define _GLFW_PLATFORM_LIBRARY_TIMER_STATE _GLFWtimerPOSIX posix #include // POSIX-specific global timer data // typedef struct _GLFWtimerPOSIX { GLFWbool monotonic; uint64_t frequency; } _GLFWtimerPOSIX; void _glfwInitTimerPOSIX(void); #endif #ifndef HEADER_GUARD_POSIX_THREAD_H #define HEADER_GUARD_POSIX_THREAD_H //======================================================================== // GLFW 3.3.7 POSIX - www.glfw.org //------------------------------------------------------------------------ // Copyright (c) 2002-2006 Marcus Geelnard // Copyright (c) 2006-2017 Camilla Löwy // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would // be appreciated but is not required. // // 2. Altered source versions must be plainly marked as such, and must not // be misrepresented as being the original software. // // 3. This notice may not be removed or altered from any source // distribution. // //======================================================================== #include #define _GLFW_PLATFORM_TLS_STATE _GLFWtlsPOSIX posix #define _GLFW_PLATFORM_MUTEX_STATE _GLFWmutexPOSIX posix // POSIX-specific thread local storage data // typedef struct _GLFWtlsPOSIX { GLFWbool allocated; pthread_key_t key; } _GLFWtlsPOSIX; // POSIX-specific mutex data // typedef struct _GLFWmutexPOSIX { GLFWbool allocated; pthread_mutex_t handle; } _GLFWmutexPOSIX; #endif #ifndef HEADER_GUARD_NULL_JOYSTICK_H #define HEADER_GUARD_NULL_JOYSTICK_H //======================================================================== // GLFW 3.3.7 - www.glfw.org //------------------------------------------------------------------------ // Copyright (c) 2006-2017 Camilla Löwy // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would // be appreciated but is not required. // // 2. Altered source versions must be plainly marked as such, and must not // be misrepresented as being the original software. // // 3. This notice may not be removed or altered from any source // distribution. // //======================================================================== #define _GLFW_PLATFORM_JOYSTICK_STATE struct { int dummyJoystick; } #define _GLFW_PLATFORM_LIBRARY_JOYSTICK_STATE struct { int dummyLibraryJoystick; } #define _GLFW_PLATFORM_MAPPING_NAME "" #endif #if defined(_GLFW_WIN32) #define _glfw_dlopen(name) LoadLibraryA(name) #define _glfw_dlclose(handle) FreeLibrary((HMODULE) handle) #define _glfw_dlsym(handle, name) GetProcAddress((HMODULE) handle, name) #else #define _glfw_dlopen(name) dlopen(name, RTLD_LAZY | RTLD_LOCAL) #define _glfw_dlclose(handle) dlclose(handle) #define _glfw_dlsym(handle, name) dlsym(handle, name) #endif // Null-specific per-window data // typedef struct _GLFWwindowNull { int width; int height; } _GLFWwindowNull; #endif #else #error "No supported window creation API selected" #endif // Constructs a version number string from the public header macros #define _GLFW_CONCAT_VERSION(m, n, r) #m "." #n "." #r #define _GLFW_MAKE_VERSION(m, n, r) _GLFW_CONCAT_VERSION(m, n, r) #define _GLFW_VERSION_NUMBER _GLFW_MAKE_VERSION(GLFW_VERSION_MAJOR, \ GLFW_VERSION_MINOR, \ GLFW_VERSION_REVISION) // Checks for whether the library has been initialized #define _GLFW_REQUIRE_INIT() \ if (!_glfw.initialized) \ { \ _glfwInputError(GLFW_NOT_INITIALIZED, NULL); \ return; \ } #define _GLFW_REQUIRE_INIT_OR_RETURN(x) \ if (!_glfw.initialized) \ { \ _glfwInputError(GLFW_NOT_INITIALIZED, NULL); \ return x; \ } // Swaps the provided pointers #define _GLFW_SWAP_POINTERS(x, y) \ { \ void* t; \ t = x; \ x = y; \ y = t; \ } // Per-thread error structure // struct _GLFWerror { _GLFWerror* next; int code; char description[_GLFW_MESSAGE_SIZE]; }; // Initialization configuration // // Parameters relating to the initialization of the library // struct _GLFWinitconfig { GLFWbool hatButtons; struct { GLFWbool menubar; GLFWbool chdir; } ns; }; // Window configuration // // Parameters relating to the creation of the window but not directly related // to the framebuffer. This is used to pass window creation parameters from // shared code to the platform API. // struct _GLFWwndconfig { int width; int height; const char* title; GLFWbool resizable; GLFWbool visible; GLFWbool decorated; GLFWbool focused; GLFWbool autoIconify; GLFWbool floating; GLFWbool maximized; GLFWbool centerCursor; GLFWbool focusOnShow; GLFWbool scaleToMonitor; struct { GLFWbool retina; char frameName[256]; } ns; struct { char className[256]; char instanceName[256]; } x11; }; // Context configuration // // Parameters relating to the creation of the context but not directly related // to the framebuffer. This is used to pass context creation parameters from // shared code to the platform API. // struct _GLFWctxconfig { int client; int source; int major; int minor; GLFWbool forward; GLFWbool debug; GLFWbool noerror; int profile; int robustness; int release; _GLFWwindow* share; struct { GLFWbool offline; } nsgl; }; // Framebuffer configuration // // This describes buffers and their sizes. It also contains // a platform-specific ID used to map back to the backend API object. // // It is used to pass framebuffer parameters from shared code to the platform // API and also to enumerate and select available framebuffer configs. // struct _GLFWfbconfig { int redBits; int greenBits; int blueBits; int alphaBits; int depthBits; int stencilBits; int accumRedBits; int accumGreenBits; int accumBlueBits; int accumAlphaBits; int auxBuffers; GLFWbool stereo; int samples; GLFWbool sRGB; GLFWbool doublebuffer; GLFWbool transparent; uintptr_t handle; }; // Context structure // struct _GLFWcontext { int client; int source; int major, minor, revision; GLFWbool forward, debug, noerror; int profile; int robustness; int release; PFNGLGETSTRINGIPROC GetStringi; PFNGLGETINTEGERVPROC GetIntegerv; PFNGLGETSTRINGPROC GetString; _GLFWmakecontextcurrentfun makeCurrent; _GLFWswapbuffersfun swapBuffers; _GLFWswapintervalfun swapInterval; _GLFWextensionsupportedfun extensionSupported; _GLFWgetprocaddressfun getProcAddress; _GLFWdestroycontextfun destroy; // This is defined in the context API's context.h _GLFW_PLATFORM_CONTEXT_STATE; // This is defined in egl_context.h _GLFW_EGL_CONTEXT_STATE; // This is defined in osmesa_context.h _GLFW_OSMESA_CONTEXT_STATE; }; // Window and context structure // struct _GLFWwindow { struct _GLFWwindow* next; // Window settings and state GLFWbool resizable; GLFWbool decorated; GLFWbool autoIconify; GLFWbool floating; GLFWbool focusOnShow; GLFWbool shouldClose; void* userPointer; GLFWbool doublebuffer; GLFWvidmode videoMode; _GLFWmonitor* monitor; _GLFWcursor* cursor; int minwidth, minheight; int maxwidth, maxheight; int numer, denom; GLFWbool stickyKeys; GLFWbool stickyMouseButtons; GLFWbool lockKeyMods; int cursorMode; char mouseButtons[GLFW_MOUSE_BUTTON_LAST + 1]; char keys[GLFW_KEY_LAST + 1]; // Virtual cursor position when cursor is disabled double virtualCursorPosX, virtualCursorPosY; GLFWbool rawMouseMotion; _GLFWcontext context; struct { GLFWwindowposfun pos; GLFWwindowsizefun size; GLFWwindowclosefun close; GLFWwindowrefreshfun refresh; GLFWwindowfocusfun focus; GLFWwindowiconifyfun iconify; GLFWwindowmaximizefun maximize; GLFWframebuffersizefun fbsize; GLFWwindowcontentscalefun scale; GLFWmousebuttonfun mouseButton; GLFWcursorposfun cursorPos; GLFWcursorenterfun cursorEnter; GLFWscrollfun scroll; GLFWkeyfun key; GLFWcharfun character; GLFWcharmodsfun charmods; GLFWdropfun drop; } callbacks; // This is defined in the window API's platform.h _GLFW_PLATFORM_WINDOW_STATE; }; // Monitor structure // struct _GLFWmonitor { char name[128]; void* userPointer; // Physical dimensions in millimeters. int widthMM, heightMM; // The window whose video mode is current on this monitor _GLFWwindow* window; GLFWvidmode* modes; int modeCount; GLFWvidmode currentMode; GLFWgammaramp originalRamp; GLFWgammaramp currentRamp; // This is defined in the window API's platform.h _GLFW_PLATFORM_MONITOR_STATE; }; // Cursor structure // struct _GLFWcursor { _GLFWcursor* next; // This is defined in the window API's platform.h _GLFW_PLATFORM_CURSOR_STATE; }; // Gamepad mapping element structure // struct _GLFWmapelement { uint8_t type; uint8_t index; int8_t axisScale; int8_t axisOffset; }; // Gamepad mapping structure // struct _GLFWmapping { char name[128]; char guid[33]; _GLFWmapelement buttons[15]; _GLFWmapelement axes[6]; }; // Joystick structure // struct _GLFWjoystick { GLFWbool present; float* axes; int axisCount; unsigned char* buttons; int buttonCount; unsigned char* hats; int hatCount; char name[128]; void* userPointer; char guid[33]; _GLFWmapping* mapping; // This is defined in the joystick API's joystick.h _GLFW_PLATFORM_JOYSTICK_STATE; }; // Thread local storage structure // struct _GLFWtls { // This is defined in the platform's thread.h _GLFW_PLATFORM_TLS_STATE; }; // Mutex structure // struct _GLFWmutex { // This is defined in the platform's thread.h _GLFW_PLATFORM_MUTEX_STATE; }; // Library global data // struct _GLFWlibrary { GLFWbool initialized; struct { _GLFWinitconfig init; _GLFWfbconfig framebuffer; _GLFWwndconfig window; _GLFWctxconfig context; int refreshRate; } hints; _GLFWerror* errorListHead; _GLFWcursor* cursorListHead; _GLFWwindow* windowListHead; _GLFWmonitor** monitors; int monitorCount; _GLFWjoystick joysticks[GLFW_JOYSTICK_LAST + 1]; _GLFWmapping* mappings; int mappingCount; _GLFWtls errorSlot; _GLFWtls contextSlot; _GLFWmutex errorLock; struct { uint64_t offset; // This is defined in the platform's time.h _GLFW_PLATFORM_LIBRARY_TIMER_STATE; } timer; struct { GLFWbool available; void* handle; char* extensions[2]; #if !defined(_GLFW_VULKAN_STATIC) PFN_vkEnumerateInstanceExtensionProperties EnumerateInstanceExtensionProperties; PFN_vkGetInstanceProcAddr GetInstanceProcAddr; #endif GLFWbool KHR_surface; #if defined(_GLFW_WIN32) GLFWbool KHR_win32_surface; #elif defined(_GLFW_COCOA) GLFWbool MVK_macos_surface; GLFWbool EXT_metal_surface; #elif defined(_GLFW_X11) GLFWbool KHR_xlib_surface; GLFWbool KHR_xcb_surface; #elif defined(_GLFW_WAYLAND) GLFWbool KHR_wayland_surface; #endif } vk; struct { GLFWmonitorfun monitor; GLFWjoystickfun joystick; } callbacks; // This is defined in the window API's platform.h _GLFW_PLATFORM_LIBRARY_WINDOW_STATE; // This is defined in the context API's context.h _GLFW_PLATFORM_LIBRARY_CONTEXT_STATE; // This is defined in the platform's joystick.h _GLFW_PLATFORM_LIBRARY_JOYSTICK_STATE; // This is defined in egl_context.h _GLFW_EGL_LIBRARY_CONTEXT_STATE; // This is defined in osmesa_context.h _GLFW_OSMESA_LIBRARY_CONTEXT_STATE; }; // Global state shared between compilation units of GLFW // extern _GLFWlibrary _glfw; ////////////////////////////////////////////////////////////////////////// ////// GLFW platform API ////// ////////////////////////////////////////////////////////////////////////// int _glfwPlatformInit(void); void _glfwPlatformTerminate(void); const char* _glfwPlatformGetVersionString(void); void _glfwPlatformGetCursorPos(_GLFWwindow* window, double* xpos, double* ypos); void _glfwPlatformSetCursorPos(_GLFWwindow* window, double xpos, double ypos); void _glfwPlatformSetCursorMode(_GLFWwindow* window, int mode); void _glfwPlatformSetRawMouseMotion(_GLFWwindow *window, GLFWbool enabled); GLFWbool _glfwPlatformRawMouseMotionSupported(void); int _glfwPlatformCreateCursor(_GLFWcursor* cursor, const GLFWimage* image, int xhot, int yhot); int _glfwPlatformCreateStandardCursor(_GLFWcursor* cursor, int shape); void _glfwPlatformDestroyCursor(_GLFWcursor* cursor); void _glfwPlatformSetCursor(_GLFWwindow* window, _GLFWcursor* cursor); const char* _glfwPlatformGetScancodeName(int scancode); int _glfwPlatformGetKeyScancode(int key); void _glfwPlatformFreeMonitor(_GLFWmonitor* monitor); void _glfwPlatformGetMonitorPos(_GLFWmonitor* monitor, int* xpos, int* ypos); void _glfwPlatformGetMonitorContentScale(_GLFWmonitor* monitor, float* xscale, float* yscale); void _glfwPlatformGetMonitorWorkarea(_GLFWmonitor* monitor, int* xpos, int* ypos, int *width, int *height); GLFWvidmode* _glfwPlatformGetVideoModes(_GLFWmonitor* monitor, int* count); void _glfwPlatformGetVideoMode(_GLFWmonitor* monitor, GLFWvidmode* mode); GLFWbool _glfwPlatformGetGammaRamp(_GLFWmonitor* monitor, GLFWgammaramp* ramp); void _glfwPlatformSetGammaRamp(_GLFWmonitor* monitor, const GLFWgammaramp* ramp); void _glfwPlatformSetClipboardString(const char* string); const char* _glfwPlatformGetClipboardString(void); int _glfwPlatformPollJoystick(_GLFWjoystick* js, int mode); void _glfwPlatformUpdateGamepadGUID(char* guid); uint64_t _glfwPlatformGetTimerValue(void); uint64_t _glfwPlatformGetTimerFrequency(void); int _glfwPlatformCreateWindow(_GLFWwindow* window, const _GLFWwndconfig* wndconfig, const _GLFWctxconfig* ctxconfig, const _GLFWfbconfig* fbconfig); void _glfwPlatformDestroyWindow(_GLFWwindow* window); void _glfwPlatformSetWindowTitle(_GLFWwindow* window, const char* title); void _glfwPlatformSetWindowIcon(_GLFWwindow* window, int count, const GLFWimage* images); void _glfwPlatformGetWindowPos(_GLFWwindow* window, int* xpos, int* ypos); void _glfwPlatformSetWindowPos(_GLFWwindow* window, int xpos, int ypos); void _glfwPlatformGetWindowSize(_GLFWwindow* window, int* width, int* height); void _glfwPlatformSetWindowSize(_GLFWwindow* window, int width, int height); void _glfwPlatformSetWindowSizeLimits(_GLFWwindow* window, int minwidth, int minheight, int maxwidth, int maxheight); void _glfwPlatformSetWindowAspectRatio(_GLFWwindow* window, int numer, int denom); void _glfwPlatformGetFramebufferSize(_GLFWwindow* window, int* width, int* height); void _glfwPlatformGetWindowFrameSize(_GLFWwindow* window, int* left, int* top, int* right, int* bottom); void _glfwPlatformGetWindowContentScale(_GLFWwindow* window, float* xscale, float* yscale); void _glfwPlatformIconifyWindow(_GLFWwindow* window); void _glfwPlatformRestoreWindow(_GLFWwindow* window); void _glfwPlatformMaximizeWindow(_GLFWwindow* window); void _glfwPlatformShowWindow(_GLFWwindow* window); void _glfwPlatformHideWindow(_GLFWwindow* window); void _glfwPlatformRequestWindowAttention(_GLFWwindow* window); void _glfwPlatformFocusWindow(_GLFWwindow* window); void _glfwPlatformSetWindowMonitor(_GLFWwindow* window, _GLFWmonitor* monitor, int xpos, int ypos, int width, int height, int refreshRate); int _glfwPlatformWindowFocused(_GLFWwindow* window); int _glfwPlatformWindowIconified(_GLFWwindow* window); int _glfwPlatformWindowVisible(_GLFWwindow* window); int _glfwPlatformWindowMaximized(_GLFWwindow* window); int _glfwPlatformWindowHovered(_GLFWwindow* window); int _glfwPlatformFramebufferTransparent(_GLFWwindow* window); float _glfwPlatformGetWindowOpacity(_GLFWwindow* window); void _glfwPlatformSetWindowResizable(_GLFWwindow* window, GLFWbool enabled); void _glfwPlatformSetWindowDecorated(_GLFWwindow* window, GLFWbool enabled); void _glfwPlatformSetWindowFloating(_GLFWwindow* window, GLFWbool enabled); void _glfwPlatformSetWindowOpacity(_GLFWwindow* window, float opacity); void _glfwPlatformPollEvents(void); void _glfwPlatformWaitEvents(void); void _glfwPlatformWaitEventsTimeout(double timeout); void _glfwPlatformPostEmptyEvent(void); void _glfwPlatformGetRequiredInstanceExtensions(char** extensions); int _glfwPlatformGetPhysicalDevicePresentationSupport(VkInstance instance, VkPhysicalDevice device, uint32_t queuefamily); VkResult _glfwPlatformCreateWindowSurface(VkInstance instance, _GLFWwindow* window, const VkAllocationCallbacks* allocator, VkSurfaceKHR* surface); GLFWbool _glfwPlatformCreateTls(_GLFWtls* tls); void _glfwPlatformDestroyTls(_GLFWtls* tls); void* _glfwPlatformGetTls(_GLFWtls* tls); void _glfwPlatformSetTls(_GLFWtls* tls, void* value); GLFWbool _glfwPlatformCreateMutex(_GLFWmutex* mutex); void _glfwPlatformDestroyMutex(_GLFWmutex* mutex); void _glfwPlatformLockMutex(_GLFWmutex* mutex); void _glfwPlatformUnlockMutex(_GLFWmutex* mutex); ////////////////////////////////////////////////////////////////////////// ////// GLFW event API ////// ////////////////////////////////////////////////////////////////////////// void _glfwInputWindowFocus(_GLFWwindow* window, GLFWbool focused); void _glfwInputWindowPos(_GLFWwindow* window, int xpos, int ypos); void _glfwInputWindowSize(_GLFWwindow* window, int width, int height); void _glfwInputFramebufferSize(_GLFWwindow* window, int width, int height); void _glfwInputWindowContentScale(_GLFWwindow* window, float xscale, float yscale); void _glfwInputWindowIconify(_GLFWwindow* window, GLFWbool iconified); void _glfwInputWindowMaximize(_GLFWwindow* window, GLFWbool maximized); void _glfwInputWindowDamage(_GLFWwindow* window); void _glfwInputWindowCloseRequest(_GLFWwindow* window); void _glfwInputWindowMonitor(_GLFWwindow* window, _GLFWmonitor* monitor); void _glfwInputKey(_GLFWwindow* window, int key, int scancode, int action, int mods); void _glfwInputChar(_GLFWwindow* window, uint32_t codepoint, int mods, GLFWbool plain); void _glfwInputScroll(_GLFWwindow* window, double xoffset, double yoffset); void _glfwInputMouseClick(_GLFWwindow* window, int button, int action, int mods); void _glfwInputCursorPos(_GLFWwindow* window, double xpos, double ypos); void _glfwInputCursorEnter(_GLFWwindow* window, GLFWbool entered); void _glfwInputDrop(_GLFWwindow* window, int count, const char** names); void _glfwInputJoystick(_GLFWjoystick* js, int event); void _glfwInputJoystickAxis(_GLFWjoystick* js, int axis, float value); void _glfwInputJoystickButton(_GLFWjoystick* js, int button, char value); void _glfwInputJoystickHat(_GLFWjoystick* js, int hat, char value); void _glfwInputMonitor(_GLFWmonitor* monitor, int action, int placement); void _glfwInputMonitorWindow(_GLFWmonitor* monitor, _GLFWwindow* window); #if defined(__GNUC__) void _glfwInputError(int code, const char* format, ...) __attribute__((format(printf, 2, 3))); #else void _glfwInputError(int code, const char* format, ...); #endif ////////////////////////////////////////////////////////////////////////// ////// GLFW internal API ////// ////////////////////////////////////////////////////////////////////////// GLFWbool _glfwStringInExtensionString(const char* string, const char* extensions); const _GLFWfbconfig* _glfwChooseFBConfig(const _GLFWfbconfig* desired, const _GLFWfbconfig* alternatives, unsigned int count); GLFWbool _glfwRefreshContextAttribs(_GLFWwindow* window, const _GLFWctxconfig* ctxconfig); GLFWbool _glfwIsValidContextConfig(const _GLFWctxconfig* ctxconfig); const GLFWvidmode* _glfwChooseVideoMode(_GLFWmonitor* monitor, const GLFWvidmode* desired); int _glfwCompareVideoModes(const GLFWvidmode* first, const GLFWvidmode* second); _GLFWmonitor* _glfwAllocMonitor(const char* name, int widthMM, int heightMM); void _glfwFreeMonitor(_GLFWmonitor* monitor); void _glfwAllocGammaArrays(GLFWgammaramp* ramp, unsigned int size); void _glfwFreeGammaArrays(GLFWgammaramp* ramp); void _glfwSplitBPP(int bpp, int* red, int* green, int* blue); void _glfwInitGamepadMappings(void); _GLFWjoystick* _glfwAllocJoystick(const char* name, const char* guid, int axisCount, int buttonCount, int hatCount); void _glfwFreeJoystick(_GLFWjoystick* js); void _glfwCenterCursorInContentArea(_GLFWwindow* window); GLFWbool _glfwInitVulkan(int mode); void _glfwTerminateVulkan(void); const char* _glfwGetVulkanResultString(VkResult result); size_t _glfwEncodeUTF8(char* s, uint32_t codepoint); char* _glfw_strdup(const char* source); float _glfw_fminf(float a, float b); float _glfw_fmaxf(float a, float b); #endif #ifndef HEADER_GUARD_OSMESA_CONTEXT_C #define HEADER_GUARD_OSMESA_CONTEXT_C //======================================================================== // GLFW 3.3.7 OSMesa - www.glfw.org //------------------------------------------------------------------------ // Copyright (c) 2016 Google Inc. // Copyright (c) 2016-2017 Camilla Löwy // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would // be appreciated but is not required. // // 2. Altered source versions must be plainly marked as such, and must not // be misrepresented as being the original software. // // 3. This notice may not be removed or altered from any source // distribution. // //======================================================================== // Please use C89 style variable declarations in this file because VS 2010 //======================================================================== #include #include #include static void makeContextCurrentOSMesa(_GLFWwindow* window) { if (window) { int width, height; _glfwPlatformGetFramebufferSize(window, &width, &height); // Check to see if we need to allocate a new buffer if ((window->context.osmesa.buffer == NULL) || (width != window->context.osmesa.width) || (height != window->context.osmesa.height)) { free(window->context.osmesa.buffer); // Allocate the new buffer (width * height * 8-bit RGBA) window->context.osmesa.buffer = calloc(4, (size_t) width * height); window->context.osmesa.width = width; window->context.osmesa.height = height; } if (!OSMesaMakeCurrent(window->context.osmesa.handle, window->context.osmesa.buffer, GL_UNSIGNED_BYTE, width, height)) { _glfwInputError(GLFW_PLATFORM_ERROR, "OSMesa: Failed to make context current"); return; } } _glfwPlatformSetTls(&_glfw.contextSlot, window); } static GLFWglproc getProcAddressOSMesa(const char* procname) { return (GLFWglproc) OSMesaGetProcAddress(procname); } static void destroyContextOSMesa(_GLFWwindow* window) { if (window->context.osmesa.handle) { OSMesaDestroyContext(window->context.osmesa.handle); window->context.osmesa.handle = NULL; } if (window->context.osmesa.buffer) { free(window->context.osmesa.buffer); window->context.osmesa.width = 0; window->context.osmesa.height = 0; } } static void swapBuffersOSMesa(_GLFWwindow* window) { // No double buffering on OSMesa } static void swapIntervalOSMesa(int interval) { // No swap interval on OSMesa } static int extensionSupportedOSMesa(const char* extension) { // OSMesa does not have extensions return GLFW_FALSE; } ////////////////////////////////////////////////////////////////////////// ////// GLFW internal API ////// ////////////////////////////////////////////////////////////////////////// GLFWbool _glfwInitOSMesa(void) { int i; const char* sonames[] = { #if defined(_GLFW_OSMESA_LIBRARY) _GLFW_OSMESA_LIBRARY, #elif defined(_WIN32) "libOSMesa.dll", "OSMesa.dll", #elif defined(__APPLE__) "libOSMesa.8.dylib", #elif defined(__CYGWIN__) "libOSMesa-8.so", #elif defined(__OpenBSD__) || defined(__NetBSD__) "libOSMesa.so", #else "libOSMesa.so.8", "libOSMesa.so.6", #endif NULL }; if (_glfw.osmesa.handle) return GLFW_TRUE; for (i = 0; sonames[i]; i++) { _glfw.osmesa.handle = _glfw_dlopen(sonames[i]); if (_glfw.osmesa.handle) break; } if (!_glfw.osmesa.handle) { _glfwInputError(GLFW_API_UNAVAILABLE, "OSMesa: Library not found"); return GLFW_FALSE; } _glfw.osmesa.CreateContextExt = (PFN_OSMesaCreateContextExt) _glfw_dlsym(_glfw.osmesa.handle, "OSMesaCreateContextExt"); _glfw.osmesa.CreateContextAttribs = (PFN_OSMesaCreateContextAttribs) _glfw_dlsym(_glfw.osmesa.handle, "OSMesaCreateContextAttribs"); _glfw.osmesa.DestroyContext = (PFN_OSMesaDestroyContext) _glfw_dlsym(_glfw.osmesa.handle, "OSMesaDestroyContext"); _glfw.osmesa.MakeCurrent = (PFN_OSMesaMakeCurrent) _glfw_dlsym(_glfw.osmesa.handle, "OSMesaMakeCurrent"); _glfw.osmesa.GetColorBuffer = (PFN_OSMesaGetColorBuffer) _glfw_dlsym(_glfw.osmesa.handle, "OSMesaGetColorBuffer"); _glfw.osmesa.GetDepthBuffer = (PFN_OSMesaGetDepthBuffer) _glfw_dlsym(_glfw.osmesa.handle, "OSMesaGetDepthBuffer"); _glfw.osmesa.GetProcAddress = (PFN_OSMesaGetProcAddress) _glfw_dlsym(_glfw.osmesa.handle, "OSMesaGetProcAddress"); if (!_glfw.osmesa.CreateContextExt || !_glfw.osmesa.DestroyContext || !_glfw.osmesa.MakeCurrent || !_glfw.osmesa.GetColorBuffer || !_glfw.osmesa.GetDepthBuffer || !_glfw.osmesa.GetProcAddress) { _glfwInputError(GLFW_PLATFORM_ERROR, "OSMesa: Failed to load required entry points"); _glfwTerminateOSMesa(); return GLFW_FALSE; } return GLFW_TRUE; } void _glfwTerminateOSMesa(void) { if (_glfw.osmesa.handle) { _glfw_dlclose(_glfw.osmesa.handle); _glfw.osmesa.handle = NULL; } } #define setAttrib(a, v) \ { \ assert(((size_t) index + 1) < sizeof(attribs) / sizeof(attribs[0])); \ attribs[index++] = a; \ attribs[index++] = v; \ } GLFWbool _glfwCreateContextOSMesa(_GLFWwindow* window, const _GLFWctxconfig* ctxconfig, const _GLFWfbconfig* fbconfig) { OSMesaContext share = NULL; const int accumBits = fbconfig->accumRedBits + fbconfig->accumGreenBits + fbconfig->accumBlueBits + fbconfig->accumAlphaBits; if (ctxconfig->client == GLFW_OPENGL_ES_API) { _glfwInputError(GLFW_API_UNAVAILABLE, "OSMesa: OpenGL ES is not available on OSMesa"); return GLFW_FALSE; } if (ctxconfig->share) share = ctxconfig->share->context.osmesa.handle; if (OSMesaCreateContextAttribs) { int index = 0, attribs[40]; setAttrib(OSMESA_FORMAT, OSMESA_RGBA); setAttrib(OSMESA_DEPTH_BITS, fbconfig->depthBits); setAttrib(OSMESA_STENCIL_BITS, fbconfig->stencilBits); setAttrib(OSMESA_ACCUM_BITS, accumBits); if (ctxconfig->profile == GLFW_OPENGL_CORE_PROFILE) { setAttrib(OSMESA_PROFILE, OSMESA_CORE_PROFILE); } else if (ctxconfig->profile == GLFW_OPENGL_COMPAT_PROFILE) { setAttrib(OSMESA_PROFILE, OSMESA_COMPAT_PROFILE); } if (ctxconfig->major != 1 || ctxconfig->minor != 0) { setAttrib(OSMESA_CONTEXT_MAJOR_VERSION, ctxconfig->major); setAttrib(OSMESA_CONTEXT_MINOR_VERSION, ctxconfig->minor); } if (ctxconfig->forward) { _glfwInputError(GLFW_VERSION_UNAVAILABLE, "OSMesa: Forward-compatible contexts not supported"); return GLFW_FALSE; } setAttrib(0, 0); window->context.osmesa.handle = OSMesaCreateContextAttribs(attribs, share); } else { if (ctxconfig->profile) { _glfwInputError(GLFW_VERSION_UNAVAILABLE, "OSMesa: OpenGL profiles unavailable"); return GLFW_FALSE; } window->context.osmesa.handle = OSMesaCreateContextExt(OSMESA_RGBA, fbconfig->depthBits, fbconfig->stencilBits, accumBits, share); } if (window->context.osmesa.handle == NULL) { _glfwInputError(GLFW_VERSION_UNAVAILABLE, "OSMesa: Failed to create context"); return GLFW_FALSE; } window->context.makeCurrent = makeContextCurrentOSMesa; window->context.swapBuffers = swapBuffersOSMesa; window->context.swapInterval = swapIntervalOSMesa; window->context.extensionSupported = extensionSupportedOSMesa; window->context.getProcAddress = getProcAddressOSMesa; window->context.destroy = destroyContextOSMesa; return GLFW_TRUE; } #undef setAttrib ////////////////////////////////////////////////////////////////////////// ////// GLFW native API ////// ////////////////////////////////////////////////////////////////////////// GLFWAPI int glfwGetOSMesaColorBuffer(GLFWwindow* handle, int* width, int* height, int* format, void** buffer) { void* mesaBuffer; GLint mesaWidth, mesaHeight, mesaFormat; _GLFWwindow* window = (_GLFWwindow*) handle; assert(window != NULL); _GLFW_REQUIRE_INIT_OR_RETURN(GLFW_FALSE); if (window->context.source != GLFW_OSMESA_CONTEXT_API) { _glfwInputError(GLFW_NO_WINDOW_CONTEXT, NULL); return GLFW_FALSE; } if (!OSMesaGetColorBuffer(window->context.osmesa.handle, &mesaWidth, &mesaHeight, &mesaFormat, &mesaBuffer)) { _glfwInputError(GLFW_PLATFORM_ERROR, "OSMesa: Failed to retrieve color buffer"); return GLFW_FALSE; } if (width) *width = mesaWidth; if (height) *height = mesaHeight; if (format) *format = mesaFormat; if (buffer) *buffer = mesaBuffer; return GLFW_TRUE; } GLFWAPI int glfwGetOSMesaDepthBuffer(GLFWwindow* handle, int* width, int* height, int* bytesPerValue, void** buffer) { void* mesaBuffer; GLint mesaWidth, mesaHeight, mesaBytes; _GLFWwindow* window = (_GLFWwindow*) handle; assert(window != NULL); _GLFW_REQUIRE_INIT_OR_RETURN(GLFW_FALSE); if (window->context.source != GLFW_OSMESA_CONTEXT_API) { _glfwInputError(GLFW_NO_WINDOW_CONTEXT, NULL); return GLFW_FALSE; } if (!OSMesaGetDepthBuffer(window->context.osmesa.handle, &mesaWidth, &mesaHeight, &mesaBytes, &mesaBuffer)) { _glfwInputError(GLFW_PLATFORM_ERROR, "OSMesa: Failed to retrieve depth buffer"); return GLFW_FALSE; } if (width) *width = mesaWidth; if (height) *height = mesaHeight; if (bytesPerValue) *bytesPerValue = mesaBytes; if (buffer) *buffer = mesaBuffer; return GLFW_TRUE; } GLFWAPI OSMesaContext glfwGetOSMesaContext(GLFWwindow* handle) { _GLFWwindow* window = (_GLFWwindow*) handle; _GLFW_REQUIRE_INIT_OR_RETURN(NULL); if (window->context.source != GLFW_OSMESA_CONTEXT_API) { _glfwInputError(GLFW_NO_WINDOW_CONTEXT, NULL); return NULL; } return window->context.osmesa.handle; } #endif #ifndef HEADER_GUARD_EGL_CONTEXT_C #define HEADER_GUARD_EGL_CONTEXT_C //======================================================================== // GLFW 3.3.7 EGL - www.glfw.org //------------------------------------------------------------------------ // Copyright (c) 2002-2006 Marcus Geelnard // Copyright (c) 2006-2019 Camilla Löwy // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would // be appreciated but is not required. // // 2. Altered source versions must be plainly marked as such, and must not // be misrepresented as being the original software. // // 3. This notice may not be removed or altered from any source // distribution. // //======================================================================== // Please use C89 style variable declarations in this file because VS 2010 //======================================================================== #include #include #include #include // Return a description of the specified EGL error // static const char* getEGLErrorString(EGLint error) { switch (error) { case EGL_SUCCESS: return "Success"; case EGL_NOT_INITIALIZED: return "EGL is not or could not be initialized"; case EGL_BAD_ACCESS: return "EGL cannot access a requested resource"; case EGL_BAD_ALLOC: return "EGL failed to allocate resources for the requested operation"; case EGL_BAD_ATTRIBUTE: return "An unrecognized attribute or attribute value was passed in the attribute list"; case EGL_BAD_CONTEXT: return "An EGLContext argument does not name a valid EGL rendering context"; case EGL_BAD_CONFIG: return "An EGLConfig argument does not name a valid EGL frame buffer configuration"; case EGL_BAD_CURRENT_SURFACE: return "The current surface of the calling thread is a window, pixel buffer or pixmap that is no longer valid"; case EGL_BAD_DISPLAY: return "An EGLDisplay argument does not name a valid EGL display connection"; case EGL_BAD_SURFACE: return "An EGLSurface argument does not name a valid surface configured for GL rendering"; case EGL_BAD_MATCH: return "Arguments are inconsistent"; case EGL_BAD_PARAMETER: return "One or more argument values are invalid"; case EGL_BAD_NATIVE_PIXMAP: return "A NativePixmapType argument does not refer to a valid native pixmap"; case EGL_BAD_NATIVE_WINDOW: return "A NativeWindowType argument does not refer to a valid native window"; case EGL_CONTEXT_LOST: return "The application must destroy all contexts and reinitialise"; default: return "ERROR: UNKNOWN EGL ERROR"; } } // Returns the specified attribute of the specified EGLConfig // static int getEGLConfigAttrib(EGLConfig config, int attrib) { int value; eglGetConfigAttrib(_glfw.egl.display, config, attrib, &value); return value; } // Return the EGLConfig most closely matching the specified hints // static GLFWbool chooseEGLConfig(const _GLFWctxconfig* ctxconfig, const _GLFWfbconfig* desired, EGLConfig* result) { EGLConfig* nativeConfigs; _GLFWfbconfig* usableConfigs; const _GLFWfbconfig* closest; int i, nativeCount, usableCount; eglGetConfigs(_glfw.egl.display, NULL, 0, &nativeCount); if (!nativeCount) { _glfwInputError(GLFW_API_UNAVAILABLE, "EGL: No EGLConfigs returned"); return GLFW_FALSE; } nativeConfigs = calloc(nativeCount, sizeof(EGLConfig)); eglGetConfigs(_glfw.egl.display, nativeConfigs, nativeCount, &nativeCount); usableConfigs = calloc(nativeCount, sizeof(_GLFWfbconfig)); usableCount = 0; for (i = 0; i < nativeCount; i++) { const EGLConfig n = nativeConfigs[i]; _GLFWfbconfig* u = usableConfigs + usableCount; // Only consider RGB(A) EGLConfigs if (getEGLConfigAttrib(n, EGL_COLOR_BUFFER_TYPE) != EGL_RGB_BUFFER) continue; // Only consider window EGLConfigs if (!(getEGLConfigAttrib(n, EGL_SURFACE_TYPE) & EGL_WINDOW_BIT)) continue; #if defined(_GLFW_X11) { XVisualInfo vi = {0}; // Only consider EGLConfigs with associated Visuals vi.visualid = getEGLConfigAttrib(n, EGL_NATIVE_VISUAL_ID); if (!vi.visualid) continue; if (desired->transparent) { int count; XVisualInfo* vis = XGetVisualInfo(_glfw.x11.display, VisualIDMask, &vi, &count); if (vis) { u->transparent = _glfwIsVisualTransparentX11(vis[0].visual); XFree(vis); } } } #endif // _GLFW_X11 if (ctxconfig->client == GLFW_OPENGL_ES_API) { if (ctxconfig->major == 1) { if (!(getEGLConfigAttrib(n, EGL_RENDERABLE_TYPE) & EGL_OPENGL_ES_BIT)) continue; } else { if (!(getEGLConfigAttrib(n, EGL_RENDERABLE_TYPE) & EGL_OPENGL_ES2_BIT)) continue; } } else if (ctxconfig->client == GLFW_OPENGL_API) { if (!(getEGLConfigAttrib(n, EGL_RENDERABLE_TYPE) & EGL_OPENGL_BIT)) continue; } u->redBits = getEGLConfigAttrib(n, EGL_RED_SIZE); u->greenBits = getEGLConfigAttrib(n, EGL_GREEN_SIZE); u->blueBits = getEGLConfigAttrib(n, EGL_BLUE_SIZE); u->alphaBits = getEGLConfigAttrib(n, EGL_ALPHA_SIZE); u->depthBits = getEGLConfigAttrib(n, EGL_DEPTH_SIZE); u->stencilBits = getEGLConfigAttrib(n, EGL_STENCIL_SIZE); u->samples = getEGLConfigAttrib(n, EGL_SAMPLES); u->doublebuffer = desired->doublebuffer; u->handle = (uintptr_t) n; usableCount++; } closest = _glfwChooseFBConfig(desired, usableConfigs, usableCount); if (closest) *result = (EGLConfig) closest->handle; free(nativeConfigs); free(usableConfigs); return closest != NULL; } static void makeContextCurrentEGL(_GLFWwindow* window) { if (window) { if (!eglMakeCurrent(_glfw.egl.display, window->context.egl.surface, window->context.egl.surface, window->context.egl.handle)) { _glfwInputError(GLFW_PLATFORM_ERROR, "EGL: Failed to make context current: %s", getEGLErrorString(eglGetError())); return; } } else { if (!eglMakeCurrent(_glfw.egl.display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT)) { _glfwInputError(GLFW_PLATFORM_ERROR, "EGL: Failed to clear current context: %s", getEGLErrorString(eglGetError())); return; } } _glfwPlatformSetTls(&_glfw.contextSlot, window); } static void swapBuffersEGL(_GLFWwindow* window) { if (window != _glfwPlatformGetTls(&_glfw.contextSlot)) { _glfwInputError(GLFW_PLATFORM_ERROR, "EGL: The context must be current on the calling thread when swapping buffers"); return; } #if defined(_GLFW_WAYLAND) // NOTE: Swapping buffers on a hidden window on Wayland makes it visible if (!window->wl.visible) return; #endif eglSwapBuffers(_glfw.egl.display, window->context.egl.surface); } static void swapIntervalEGL(int interval) { eglSwapInterval(_glfw.egl.display, interval); } static int extensionSupportedEGL(const char* extension) { const char* extensions = eglQueryString(_glfw.egl.display, EGL_EXTENSIONS); if (extensions) { if (_glfwStringInExtensionString(extension, extensions)) return GLFW_TRUE; } return GLFW_FALSE; } static GLFWglproc getProcAddressEGL(const char* procname) { _GLFWwindow* window = _glfwPlatformGetTls(&_glfw.contextSlot); if (window->context.egl.client) { GLFWglproc proc = (GLFWglproc) _glfw_dlsym(window->context.egl.client, procname); if (proc) return proc; } return eglGetProcAddress(procname); } static void destroyContextEGL(_GLFWwindow* window) { #if defined(_GLFW_X11) // NOTE: Do not unload libGL.so.1 while the X11 display is still open, // as it will make XCloseDisplay segfault if (window->context.client != GLFW_OPENGL_API) #endif // _GLFW_X11 { if (window->context.egl.client) { _glfw_dlclose(window->context.egl.client); window->context.egl.client = NULL; } } if (window->context.egl.surface) { eglDestroySurface(_glfw.egl.display, window->context.egl.surface); window->context.egl.surface = EGL_NO_SURFACE; } if (window->context.egl.handle) { eglDestroyContext(_glfw.egl.display, window->context.egl.handle); window->context.egl.handle = EGL_NO_CONTEXT; } } ////////////////////////////////////////////////////////////////////////// ////// GLFW internal API ////// ////////////////////////////////////////////////////////////////////////// // Initialize EGL // GLFWbool _glfwInitEGL(void) { int i; const char* sonames[] = { #if defined(_GLFW_EGL_LIBRARY) _GLFW_EGL_LIBRARY, #elif defined(_GLFW_WIN32) "libEGL.dll", "EGL.dll", #elif defined(_GLFW_COCOA) "libEGL.dylib", #elif defined(__CYGWIN__) "libEGL-1.so", #elif defined(__OpenBSD__) || defined(__NetBSD__) "libEGL.so", #else "libEGL.so.1", #endif NULL }; if (_glfw.egl.handle) return GLFW_TRUE; for (i = 0; sonames[i]; i++) { _glfw.egl.handle = _glfw_dlopen(sonames[i]); if (_glfw.egl.handle) break; } if (!_glfw.egl.handle) { _glfwInputError(GLFW_API_UNAVAILABLE, "EGL: Library not found"); return GLFW_FALSE; } _glfw.egl.prefix = (strncmp(sonames[i], "lib", 3) == 0); _glfw.egl.GetConfigAttrib = (PFN_eglGetConfigAttrib) _glfw_dlsym(_glfw.egl.handle, "eglGetConfigAttrib"); _glfw.egl.GetConfigs = (PFN_eglGetConfigs) _glfw_dlsym(_glfw.egl.handle, "eglGetConfigs"); _glfw.egl.GetDisplay = (PFN_eglGetDisplay) _glfw_dlsym(_glfw.egl.handle, "eglGetDisplay"); _glfw.egl.GetError = (PFN_eglGetError) _glfw_dlsym(_glfw.egl.handle, "eglGetError"); _glfw.egl.Initialize = (PFN_eglInitialize) _glfw_dlsym(_glfw.egl.handle, "eglInitialize"); _glfw.egl.Terminate = (PFN_eglTerminate) _glfw_dlsym(_glfw.egl.handle, "eglTerminate"); _glfw.egl.BindAPI = (PFN_eglBindAPI) _glfw_dlsym(_glfw.egl.handle, "eglBindAPI"); _glfw.egl.CreateContext = (PFN_eglCreateContext) _glfw_dlsym(_glfw.egl.handle, "eglCreateContext"); _glfw.egl.DestroySurface = (PFN_eglDestroySurface) _glfw_dlsym(_glfw.egl.handle, "eglDestroySurface"); _glfw.egl.DestroyContext = (PFN_eglDestroyContext) _glfw_dlsym(_glfw.egl.handle, "eglDestroyContext"); _glfw.egl.CreateWindowSurface = (PFN_eglCreateWindowSurface) _glfw_dlsym(_glfw.egl.handle, "eglCreateWindowSurface"); _glfw.egl.MakeCurrent = (PFN_eglMakeCurrent) _glfw_dlsym(_glfw.egl.handle, "eglMakeCurrent"); _glfw.egl.SwapBuffers = (PFN_eglSwapBuffers) _glfw_dlsym(_glfw.egl.handle, "eglSwapBuffers"); _glfw.egl.SwapInterval = (PFN_eglSwapInterval) _glfw_dlsym(_glfw.egl.handle, "eglSwapInterval"); _glfw.egl.QueryString = (PFN_eglQueryString) _glfw_dlsym(_glfw.egl.handle, "eglQueryString"); _glfw.egl.GetProcAddress = (PFN_eglGetProcAddress) _glfw_dlsym(_glfw.egl.handle, "eglGetProcAddress"); if (!_glfw.egl.GetConfigAttrib || !_glfw.egl.GetConfigs || !_glfw.egl.GetDisplay || !_glfw.egl.GetError || !_glfw.egl.Initialize || !_glfw.egl.Terminate || !_glfw.egl.BindAPI || !_glfw.egl.CreateContext || !_glfw.egl.DestroySurface || !_glfw.egl.DestroyContext || !_glfw.egl.CreateWindowSurface || !_glfw.egl.MakeCurrent || !_glfw.egl.SwapBuffers || !_glfw.egl.SwapInterval || !_glfw.egl.QueryString || !_glfw.egl.GetProcAddress) { _glfwInputError(GLFW_PLATFORM_ERROR, "EGL: Failed to load required entry points"); _glfwTerminateEGL(); return GLFW_FALSE; } _glfw.egl.display = eglGetDisplay(_GLFW_EGL_NATIVE_DISPLAY); if (_glfw.egl.display == EGL_NO_DISPLAY) { _glfwInputError(GLFW_API_UNAVAILABLE, "EGL: Failed to get EGL display: %s", getEGLErrorString(eglGetError())); _glfwTerminateEGL(); return GLFW_FALSE; } if (!eglInitialize(_glfw.egl.display, &_glfw.egl.major, &_glfw.egl.minor)) { _glfwInputError(GLFW_API_UNAVAILABLE, "EGL: Failed to initialize EGL: %s", getEGLErrorString(eglGetError())); _glfwTerminateEGL(); return GLFW_FALSE; } _glfw.egl.KHR_create_context = extensionSupportedEGL("EGL_KHR_create_context"); _glfw.egl.KHR_create_context_no_error = extensionSupportedEGL("EGL_KHR_create_context_no_error"); _glfw.egl.KHR_gl_colorspace = extensionSupportedEGL("EGL_KHR_gl_colorspace"); _glfw.egl.KHR_get_all_proc_addresses = extensionSupportedEGL("EGL_KHR_get_all_proc_addresses"); _glfw.egl.KHR_context_flush_control = extensionSupportedEGL("EGL_KHR_context_flush_control"); _glfw.egl.EXT_present_opaque = extensionSupportedEGL("EGL_EXT_present_opaque"); return GLFW_TRUE; } // Terminate EGL // void _glfwTerminateEGL(void) { if (_glfw.egl.display) { eglTerminate(_glfw.egl.display); _glfw.egl.display = EGL_NO_DISPLAY; } if (_glfw.egl.handle) { _glfw_dlclose(_glfw.egl.handle); _glfw.egl.handle = NULL; } } #define setAttrib(a, v) \ { \ assert(((size_t) index + 1) < sizeof(attribs) / sizeof(attribs[0])); \ attribs[index++] = a; \ attribs[index++] = v; \ } // Create the OpenGL or OpenGL ES context // GLFWbool _glfwCreateContextEGL(_GLFWwindow* window, const _GLFWctxconfig* ctxconfig, const _GLFWfbconfig* fbconfig) { EGLint attribs[40]; EGLConfig config; EGLContext share = NULL; int index = 0; if (!_glfw.egl.display) { _glfwInputError(GLFW_API_UNAVAILABLE, "EGL: API not available"); return GLFW_FALSE; } if (ctxconfig->share) share = ctxconfig->share->context.egl.handle; if (!chooseEGLConfig(ctxconfig, fbconfig, &config)) { _glfwInputError(GLFW_FORMAT_UNAVAILABLE, "EGL: Failed to find a suitable EGLConfig"); return GLFW_FALSE; } if (ctxconfig->client == GLFW_OPENGL_ES_API) { if (!eglBindAPI(EGL_OPENGL_ES_API)) { _glfwInputError(GLFW_API_UNAVAILABLE, "EGL: Failed to bind OpenGL ES: %s", getEGLErrorString(eglGetError())); return GLFW_FALSE; } } else { if (!eglBindAPI(EGL_OPENGL_API)) { _glfwInputError(GLFW_API_UNAVAILABLE, "EGL: Failed to bind OpenGL: %s", getEGLErrorString(eglGetError())); return GLFW_FALSE; } } if (_glfw.egl.KHR_create_context) { int mask = 0, flags = 0; if (ctxconfig->client == GLFW_OPENGL_API) { if (ctxconfig->forward) flags |= EGL_CONTEXT_OPENGL_FORWARD_COMPATIBLE_BIT_KHR; if (ctxconfig->profile == GLFW_OPENGL_CORE_PROFILE) mask |= EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT_KHR; else if (ctxconfig->profile == GLFW_OPENGL_COMPAT_PROFILE) mask |= EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT_KHR; } if (ctxconfig->debug) flags |= EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR; if (ctxconfig->robustness) { if (ctxconfig->robustness == GLFW_NO_RESET_NOTIFICATION) { setAttrib(EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_KHR, EGL_NO_RESET_NOTIFICATION_KHR); } else if (ctxconfig->robustness == GLFW_LOSE_CONTEXT_ON_RESET) { setAttrib(EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_KHR, EGL_LOSE_CONTEXT_ON_RESET_KHR); } flags |= EGL_CONTEXT_OPENGL_ROBUST_ACCESS_BIT_KHR; } if (ctxconfig->noerror) { if (_glfw.egl.KHR_create_context_no_error) setAttrib(EGL_CONTEXT_OPENGL_NO_ERROR_KHR, GLFW_TRUE); } if (ctxconfig->major != 1 || ctxconfig->minor != 0) { setAttrib(EGL_CONTEXT_MAJOR_VERSION_KHR, ctxconfig->major); setAttrib(EGL_CONTEXT_MINOR_VERSION_KHR, ctxconfig->minor); } if (mask) setAttrib(EGL_CONTEXT_OPENGL_PROFILE_MASK_KHR, mask); if (flags) setAttrib(EGL_CONTEXT_FLAGS_KHR, flags); } else { if (ctxconfig->client == GLFW_OPENGL_ES_API) setAttrib(EGL_CONTEXT_CLIENT_VERSION, ctxconfig->major); } if (_glfw.egl.KHR_context_flush_control) { if (ctxconfig->release == GLFW_RELEASE_BEHAVIOR_NONE) { setAttrib(EGL_CONTEXT_RELEASE_BEHAVIOR_KHR, EGL_CONTEXT_RELEASE_BEHAVIOR_NONE_KHR); } else if (ctxconfig->release == GLFW_RELEASE_BEHAVIOR_FLUSH) { setAttrib(EGL_CONTEXT_RELEASE_BEHAVIOR_KHR, EGL_CONTEXT_RELEASE_BEHAVIOR_FLUSH_KHR); } } setAttrib(EGL_NONE, EGL_NONE); window->context.egl.handle = eglCreateContext(_glfw.egl.display, config, share, attribs); if (window->context.egl.handle == EGL_NO_CONTEXT) { _glfwInputError(GLFW_VERSION_UNAVAILABLE, "EGL: Failed to create context: %s", getEGLErrorString(eglGetError())); return GLFW_FALSE; } // Set up attributes for surface creation index = 0; if (fbconfig->sRGB) { if (_glfw.egl.KHR_gl_colorspace) setAttrib(EGL_GL_COLORSPACE_KHR, EGL_GL_COLORSPACE_SRGB_KHR); } if (!fbconfig->doublebuffer) setAttrib(EGL_RENDER_BUFFER, EGL_SINGLE_BUFFER); if (_glfw.egl.EXT_present_opaque) setAttrib(EGL_PRESENT_OPAQUE_EXT, !fbconfig->transparent); setAttrib(EGL_NONE, EGL_NONE); window->context.egl.surface = eglCreateWindowSurface(_glfw.egl.display, config, _GLFW_EGL_NATIVE_WINDOW, attribs); if (window->context.egl.surface == EGL_NO_SURFACE) { _glfwInputError(GLFW_PLATFORM_ERROR, "EGL: Failed to create window surface: %s", getEGLErrorString(eglGetError())); return GLFW_FALSE; } window->context.egl.config = config; // Load the appropriate client library if (!_glfw.egl.KHR_get_all_proc_addresses) { int i; const char** sonames; const char* es1sonames[] = { #if defined(_GLFW_GLESV1_LIBRARY) _GLFW_GLESV1_LIBRARY, #elif defined(_GLFW_WIN32) "GLESv1_CM.dll", "libGLES_CM.dll", #elif defined(_GLFW_COCOA) "libGLESv1_CM.dylib", #elif defined(__OpenBSD__) || defined(__NetBSD__) "libGLESv1_CM.so", #else "libGLESv1_CM.so.1", "libGLES_CM.so.1", #endif NULL }; const char* es2sonames[] = { #if defined(_GLFW_GLESV2_LIBRARY) _GLFW_GLESV2_LIBRARY, #elif defined(_GLFW_WIN32) "GLESv2.dll", "libGLESv2.dll", #elif defined(_GLFW_COCOA) "libGLESv2.dylib", #elif defined(__CYGWIN__) "libGLESv2-2.so", #elif defined(__OpenBSD__) || defined(__NetBSD__) "libGLESv2.so", #else "libGLESv2.so.2", #endif NULL }; const char* glsonames[] = { #if defined(_GLFW_OPENGL_LIBRARY) _GLFW_OPENGL_LIBRARY, #elif defined(_GLFW_WIN32) #elif defined(_GLFW_COCOA) #elif defined(__OpenBSD__) || defined(__NetBSD__) "libGL.so", #else "libGL.so.1", #endif NULL }; if (ctxconfig->client == GLFW_OPENGL_ES_API) { if (ctxconfig->major == 1) sonames = es1sonames; else sonames = es2sonames; } else sonames = glsonames; for (i = 0; sonames[i]; i++) { // HACK: Match presence of lib prefix to increase chance of finding // a matching pair in the jungle that is Win32 EGL/GLES if (_glfw.egl.prefix != (strncmp(sonames[i], "lib", 3) == 0)) continue; window->context.egl.client = _glfw_dlopen(sonames[i]); if (window->context.egl.client) break; } if (!window->context.egl.client) { _glfwInputError(GLFW_API_UNAVAILABLE, "EGL: Failed to load client library"); return GLFW_FALSE; } } window->context.makeCurrent = makeContextCurrentEGL; window->context.swapBuffers = swapBuffersEGL; window->context.swapInterval = swapIntervalEGL; window->context.extensionSupported = extensionSupportedEGL; window->context.getProcAddress = getProcAddressEGL; window->context.destroy = destroyContextEGL; return GLFW_TRUE; } #undef setAttrib // Returns the Visual and depth of the chosen EGLConfig // #if defined(_GLFW_X11) GLFWbool _glfwChooseVisualEGL(const _GLFWwndconfig* wndconfig, const _GLFWctxconfig* ctxconfig, const _GLFWfbconfig* fbconfig, Visual** visual, int* depth) { XVisualInfo* result; XVisualInfo desired; EGLConfig native; EGLint visualID = 0, count = 0; const long vimask = VisualScreenMask | VisualIDMask; if (!chooseEGLConfig(ctxconfig, fbconfig, &native)) { _glfwInputError(GLFW_FORMAT_UNAVAILABLE, "EGL: Failed to find a suitable EGLConfig"); return GLFW_FALSE; } eglGetConfigAttrib(_glfw.egl.display, native, EGL_NATIVE_VISUAL_ID, &visualID); desired.screen = _glfw.x11.screen; desired.visualid = visualID; result = XGetVisualInfo(_glfw.x11.display, vimask, &desired, &count); if (!result) { _glfwInputError(GLFW_PLATFORM_ERROR, "EGL: Failed to retrieve Visual for EGLConfig"); return GLFW_FALSE; } *visual = result->visual; *depth = result->depth; XFree(result); return GLFW_TRUE; } #endif // _GLFW_X11 ////////////////////////////////////////////////////////////////////////// ////// GLFW native API ////// ////////////////////////////////////////////////////////////////////////// GLFWAPI EGLDisplay glfwGetEGLDisplay(void) { _GLFW_REQUIRE_INIT_OR_RETURN(EGL_NO_DISPLAY); return _glfw.egl.display; } GLFWAPI EGLContext glfwGetEGLContext(GLFWwindow* handle) { _GLFWwindow* window = (_GLFWwindow*) handle; _GLFW_REQUIRE_INIT_OR_RETURN(EGL_NO_CONTEXT); if (window->context.source != GLFW_EGL_CONTEXT_API) { _glfwInputError(GLFW_NO_WINDOW_CONTEXT, NULL); return EGL_NO_CONTEXT; } return window->context.egl.handle; } GLFWAPI EGLSurface glfwGetEGLSurface(GLFWwindow* handle) { _GLFWwindow* window = (_GLFWwindow*) handle; _GLFW_REQUIRE_INIT_OR_RETURN(EGL_NO_SURFACE); if (window->context.source != GLFW_EGL_CONTEXT_API) { _glfwInputError(GLFW_NO_WINDOW_CONTEXT, NULL); return EGL_NO_SURFACE; } return window->context.egl.surface; } #endif #ifndef HEADER_GUARD_CONTEXT_C #define HEADER_GUARD_CONTEXT_C //======================================================================== // GLFW 3.3.7 - www.glfw.org //------------------------------------------------------------------------ // Copyright (c) 2002-2006 Marcus Geelnard // Copyright (c) 2006-2016 Camilla Löwy // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would // be appreciated but is not required. // // 2. Altered source versions must be plainly marked as such, and must not // be misrepresented as being the original software. // // 3. This notice may not be removed or altered from any source // distribution. // //======================================================================== // Please use C89 style variable declarations in this file because VS 2010 //======================================================================== #include #include #include #include #include ////////////////////////////////////////////////////////////////////////// ////// GLFW internal API ////// ////////////////////////////////////////////////////////////////////////// // Checks whether the desired context attributes are valid // // This function checks things like whether the specified client API version // exists and whether all relevant options have supported and non-conflicting // values // GLFWbool _glfwIsValidContextConfig(const _GLFWctxconfig* ctxconfig) { if (ctxconfig->share) { if (ctxconfig->client == GLFW_NO_API || ctxconfig->share->context.client == GLFW_NO_API) { _glfwInputError(GLFW_NO_WINDOW_CONTEXT, NULL); return GLFW_FALSE; } } if (ctxconfig->source != GLFW_NATIVE_CONTEXT_API && ctxconfig->source != GLFW_EGL_CONTEXT_API && ctxconfig->source != GLFW_OSMESA_CONTEXT_API) { _glfwInputError(GLFW_INVALID_ENUM, "Invalid context creation API 0x%08X", ctxconfig->source); return GLFW_FALSE; } if (ctxconfig->client != GLFW_NO_API && ctxconfig->client != GLFW_OPENGL_API && ctxconfig->client != GLFW_OPENGL_ES_API) { _glfwInputError(GLFW_INVALID_ENUM, "Invalid client API 0x%08X", ctxconfig->client); return GLFW_FALSE; } if (ctxconfig->client == GLFW_OPENGL_API) { if ((ctxconfig->major < 1 || ctxconfig->minor < 0) || (ctxconfig->major == 1 && ctxconfig->minor > 5) || (ctxconfig->major == 2 && ctxconfig->minor > 1) || (ctxconfig->major == 3 && ctxconfig->minor > 3)) { // OpenGL 1.0 is the smallest valid version // OpenGL 1.x series ended with version 1.5 // OpenGL 2.x series ended with version 2.1 // OpenGL 3.x series ended with version 3.3 // For now, let everything else through _glfwInputError(GLFW_INVALID_VALUE, "Invalid OpenGL version %i.%i", ctxconfig->major, ctxconfig->minor); return GLFW_FALSE; } if (ctxconfig->profile) { if (ctxconfig->profile != GLFW_OPENGL_CORE_PROFILE && ctxconfig->profile != GLFW_OPENGL_COMPAT_PROFILE) { _glfwInputError(GLFW_INVALID_ENUM, "Invalid OpenGL profile 0x%08X", ctxconfig->profile); return GLFW_FALSE; } if (ctxconfig->major <= 2 || (ctxconfig->major == 3 && ctxconfig->minor < 2)) { // Desktop OpenGL context profiles are only defined for version 3.2 // and above _glfwInputError(GLFW_INVALID_VALUE, "Context profiles are only defined for OpenGL version 3.2 and above"); return GLFW_FALSE; } } if (ctxconfig->forward && ctxconfig->major <= 2) { // Forward-compatible contexts are only defined for OpenGL version 3.0 and above _glfwInputError(GLFW_INVALID_VALUE, "Forward-compatibility is only defined for OpenGL version 3.0 and above"); return GLFW_FALSE; } } else if (ctxconfig->client == GLFW_OPENGL_ES_API) { if (ctxconfig->major < 1 || ctxconfig->minor < 0 || (ctxconfig->major == 1 && ctxconfig->minor > 1) || (ctxconfig->major == 2 && ctxconfig->minor > 0)) { // OpenGL ES 1.0 is the smallest valid version // OpenGL ES 1.x series ended with version 1.1 // OpenGL ES 2.x series ended with version 2.0 // For now, let everything else through _glfwInputError(GLFW_INVALID_VALUE, "Invalid OpenGL ES version %i.%i", ctxconfig->major, ctxconfig->minor); return GLFW_FALSE; } } if (ctxconfig->robustness) { if (ctxconfig->robustness != GLFW_NO_RESET_NOTIFICATION && ctxconfig->robustness != GLFW_LOSE_CONTEXT_ON_RESET) { _glfwInputError(GLFW_INVALID_ENUM, "Invalid context robustness mode 0x%08X", ctxconfig->robustness); return GLFW_FALSE; } } if (ctxconfig->release) { if (ctxconfig->release != GLFW_RELEASE_BEHAVIOR_NONE && ctxconfig->release != GLFW_RELEASE_BEHAVIOR_FLUSH) { _glfwInputError(GLFW_INVALID_ENUM, "Invalid context release behavior 0x%08X", ctxconfig->release); return GLFW_FALSE; } } return GLFW_TRUE; } // Chooses the framebuffer config that best matches the desired one // const _GLFWfbconfig* _glfwChooseFBConfig(const _GLFWfbconfig* desired, const _GLFWfbconfig* alternatives, unsigned int count) { unsigned int i; unsigned int missing, leastMissing = UINT_MAX; unsigned int colorDiff, leastColorDiff = UINT_MAX; unsigned int extraDiff, leastExtraDiff = UINT_MAX; const _GLFWfbconfig* current; const _GLFWfbconfig* closest = NULL; for (i = 0; i < count; i++) { current = alternatives + i; if (desired->stereo > 0 && current->stereo == 0) { // Stereo is a hard constraint continue; } // Count number of missing buffers { missing = 0; if (desired->alphaBits > 0 && current->alphaBits == 0) missing++; if (desired->depthBits > 0 && current->depthBits == 0) missing++; if (desired->stencilBits > 0 && current->stencilBits == 0) missing++; if (desired->auxBuffers > 0 && current->auxBuffers < desired->auxBuffers) { missing += desired->auxBuffers - current->auxBuffers; } if (desired->samples > 0 && current->samples == 0) { // Technically, several multisampling buffers could be // involved, but that's a lower level implementation detail and // not important to us here, so we count them as one missing++; } if (desired->transparent != current->transparent) missing++; } // These polynomials make many small channel size differences matter // less than one large channel size difference // Calculate color channel size difference value { colorDiff = 0; if (desired->redBits != GLFW_DONT_CARE) { colorDiff += (desired->redBits - current->redBits) * (desired->redBits - current->redBits); } if (desired->greenBits != GLFW_DONT_CARE) { colorDiff += (desired->greenBits - current->greenBits) * (desired->greenBits - current->greenBits); } if (desired->blueBits != GLFW_DONT_CARE) { colorDiff += (desired->blueBits - current->blueBits) * (desired->blueBits - current->blueBits); } } // Calculate non-color channel size difference value { extraDiff = 0; if (desired->alphaBits != GLFW_DONT_CARE) { extraDiff += (desired->alphaBits - current->alphaBits) * (desired->alphaBits - current->alphaBits); } if (desired->depthBits != GLFW_DONT_CARE) { extraDiff += (desired->depthBits - current->depthBits) * (desired->depthBits - current->depthBits); } if (desired->stencilBits != GLFW_DONT_CARE) { extraDiff += (desired->stencilBits - current->stencilBits) * (desired->stencilBits - current->stencilBits); } if (desired->accumRedBits != GLFW_DONT_CARE) { extraDiff += (desired->accumRedBits - current->accumRedBits) * (desired->accumRedBits - current->accumRedBits); } if (desired->accumGreenBits != GLFW_DONT_CARE) { extraDiff += (desired->accumGreenBits - current->accumGreenBits) * (desired->accumGreenBits - current->accumGreenBits); } if (desired->accumBlueBits != GLFW_DONT_CARE) { extraDiff += (desired->accumBlueBits - current->accumBlueBits) * (desired->accumBlueBits - current->accumBlueBits); } if (desired->accumAlphaBits != GLFW_DONT_CARE) { extraDiff += (desired->accumAlphaBits - current->accumAlphaBits) * (desired->accumAlphaBits - current->accumAlphaBits); } if (desired->samples != GLFW_DONT_CARE) { extraDiff += (desired->samples - current->samples) * (desired->samples - current->samples); } if (desired->sRGB && !current->sRGB) extraDiff++; } // Figure out if the current one is better than the best one found so far // Least number of missing buffers is the most important heuristic, // then color buffer size match and lastly size match for other buffers if (missing < leastMissing) closest = current; else if (missing == leastMissing) { if ((colorDiff < leastColorDiff) || (colorDiff == leastColorDiff && extraDiff < leastExtraDiff)) { closest = current; } } if (current == closest) { leastMissing = missing; leastColorDiff = colorDiff; leastExtraDiff = extraDiff; } } return closest; } // Retrieves the attributes of the current context // GLFWbool _glfwRefreshContextAttribs(_GLFWwindow* window, const _GLFWctxconfig* ctxconfig) { int i; _GLFWwindow* previous; const char* version; const char* prefixes[] = { "OpenGL ES-CM ", "OpenGL ES-CL ", "OpenGL ES ", NULL }; window->context.source = ctxconfig->source; window->context.client = GLFW_OPENGL_API; previous = _glfwPlatformGetTls(&_glfw.contextSlot); glfwMakeContextCurrent((GLFWwindow*) window); window->context.GetIntegerv = (PFNGLGETINTEGERVPROC) window->context.getProcAddress("glGetIntegerv"); window->context.GetString = (PFNGLGETSTRINGPROC) window->context.getProcAddress("glGetString"); if (!window->context.GetIntegerv || !window->context.GetString) { _glfwInputError(GLFW_PLATFORM_ERROR, "Entry point retrieval is broken"); glfwMakeContextCurrent((GLFWwindow*) previous); return GLFW_FALSE; } version = (const char*) window->context.GetString(GL_VERSION); if (!version) { if (ctxconfig->client == GLFW_OPENGL_API) { _glfwInputError(GLFW_PLATFORM_ERROR, "OpenGL version string retrieval is broken"); } else { _glfwInputError(GLFW_PLATFORM_ERROR, "OpenGL ES version string retrieval is broken"); } glfwMakeContextCurrent((GLFWwindow*) previous); return GLFW_FALSE; } for (i = 0; prefixes[i]; i++) { const size_t length = strlen(prefixes[i]); if (strncmp(version, prefixes[i], length) == 0) { version += length; window->context.client = GLFW_OPENGL_ES_API; break; } } if (!sscanf(version, "%d.%d.%d", &window->context.major, &window->context.minor, &window->context.revision)) { if (window->context.client == GLFW_OPENGL_API) { _glfwInputError(GLFW_PLATFORM_ERROR, "No version found in OpenGL version string"); } else { _glfwInputError(GLFW_PLATFORM_ERROR, "No version found in OpenGL ES version string"); } glfwMakeContextCurrent((GLFWwindow*) previous); return GLFW_FALSE; } if (window->context.major < ctxconfig->major || (window->context.major == ctxconfig->major && window->context.minor < ctxconfig->minor)) { // The desired OpenGL version is greater than the actual version // This only happens if the machine lacks {GLX|WGL}_ARB_create_context // /and/ the user has requested an OpenGL version greater than 1.0 // For API consistency, we emulate the behavior of the // {GLX|WGL}_ARB_create_context extension and fail here if (window->context.client == GLFW_OPENGL_API) { _glfwInputError(GLFW_VERSION_UNAVAILABLE, "Requested OpenGL version %i.%i, got version %i.%i", ctxconfig->major, ctxconfig->minor, window->context.major, window->context.minor); } else { _glfwInputError(GLFW_VERSION_UNAVAILABLE, "Requested OpenGL ES version %i.%i, got version %i.%i", ctxconfig->major, ctxconfig->minor, window->context.major, window->context.minor); } glfwMakeContextCurrent((GLFWwindow*) previous); return GLFW_FALSE; } if (window->context.major >= 3) { // OpenGL 3.0+ uses a different function for extension string retrieval // We cache it here instead of in glfwExtensionSupported mostly to alert // users as early as possible that their build may be broken window->context.GetStringi = (PFNGLGETSTRINGIPROC) window->context.getProcAddress("glGetStringi"); if (!window->context.GetStringi) { _glfwInputError(GLFW_PLATFORM_ERROR, "Entry point retrieval is broken"); glfwMakeContextCurrent((GLFWwindow*) previous); return GLFW_FALSE; } } if (window->context.client == GLFW_OPENGL_API) { // Read back context flags (OpenGL 3.0 and above) if (window->context.major >= 3) { GLint flags; window->context.GetIntegerv(GL_CONTEXT_FLAGS, &flags); if (flags & GL_CONTEXT_FLAG_FORWARD_COMPATIBLE_BIT) window->context.forward = GLFW_TRUE; if (flags & GL_CONTEXT_FLAG_DEBUG_BIT) window->context.debug = GLFW_TRUE; else if (glfwExtensionSupported("GL_ARB_debug_output") && ctxconfig->debug) { // HACK: This is a workaround for older drivers (pre KHR_debug) // not setting the debug bit in the context flags for // debug contexts window->context.debug = GLFW_TRUE; } if (flags & GL_CONTEXT_FLAG_NO_ERROR_BIT_KHR) window->context.noerror = GLFW_TRUE; } // Read back OpenGL context profile (OpenGL 3.2 and above) if (window->context.major >= 4 || (window->context.major == 3 && window->context.minor >= 2)) { GLint mask; window->context.GetIntegerv(GL_CONTEXT_PROFILE_MASK, &mask); if (mask & GL_CONTEXT_COMPATIBILITY_PROFILE_BIT) window->context.profile = GLFW_OPENGL_COMPAT_PROFILE; else if (mask & GL_CONTEXT_CORE_PROFILE_BIT) window->context.profile = GLFW_OPENGL_CORE_PROFILE; else if (glfwExtensionSupported("GL_ARB_compatibility")) { // HACK: This is a workaround for the compatibility profile bit // not being set in the context flags if an OpenGL 3.2+ // context was created without having requested a specific // version window->context.profile = GLFW_OPENGL_COMPAT_PROFILE; } } // Read back robustness strategy if (glfwExtensionSupported("GL_ARB_robustness")) { // NOTE: We avoid using the context flags for detection, as they are // only present from 3.0 while the extension applies from 1.1 GLint strategy; window->context.GetIntegerv(GL_RESET_NOTIFICATION_STRATEGY_ARB, &strategy); if (strategy == GL_LOSE_CONTEXT_ON_RESET_ARB) window->context.robustness = GLFW_LOSE_CONTEXT_ON_RESET; else if (strategy == GL_NO_RESET_NOTIFICATION_ARB) window->context.robustness = GLFW_NO_RESET_NOTIFICATION; } } else { // Read back robustness strategy if (glfwExtensionSupported("GL_EXT_robustness")) { // NOTE: The values of these constants match those of the OpenGL ARB // one, so we can reuse them here GLint strategy; window->context.GetIntegerv(GL_RESET_NOTIFICATION_STRATEGY_ARB, &strategy); if (strategy == GL_LOSE_CONTEXT_ON_RESET_ARB) window->context.robustness = GLFW_LOSE_CONTEXT_ON_RESET; else if (strategy == GL_NO_RESET_NOTIFICATION_ARB) window->context.robustness = GLFW_NO_RESET_NOTIFICATION; } } if (glfwExtensionSupported("GL_KHR_context_flush_control")) { GLint behavior; window->context.GetIntegerv(GL_CONTEXT_RELEASE_BEHAVIOR, &behavior); if (behavior == GL_NONE) window->context.release = GLFW_RELEASE_BEHAVIOR_NONE; else if (behavior == GL_CONTEXT_RELEASE_BEHAVIOR_FLUSH) window->context.release = GLFW_RELEASE_BEHAVIOR_FLUSH; } // Clearing the front buffer to black to avoid garbage pixels left over from // previous uses of our bit of VRAM { PFNGLCLEARPROC glClear = (PFNGLCLEARPROC) window->context.getProcAddress("glClear"); glClear(GL_COLOR_BUFFER_BIT); if (window->doublebuffer) window->context.swapBuffers(window); } glfwMakeContextCurrent((GLFWwindow*) previous); return GLFW_TRUE; } // Searches an extension string for the specified extension // GLFWbool _glfwStringInExtensionString(const char* string, const char* extensions) { const char* start = extensions; for (;;) { const char* where; const char* terminator; where = strstr(start, string); if (!where) return GLFW_FALSE; terminator = where + strlen(string); if (where == start || *(where - 1) == ' ') { if (*terminator == ' ' || *terminator == '\0') break; } start = terminator; } return GLFW_TRUE; } ////////////////////////////////////////////////////////////////////////// ////// GLFW public API ////// ////////////////////////////////////////////////////////////////////////// GLFWAPI void glfwMakeContextCurrent(GLFWwindow* handle) { _GLFWwindow* window = (_GLFWwindow*) handle; _GLFWwindow* previous = _glfwPlatformGetTls(&_glfw.contextSlot); _GLFW_REQUIRE_INIT(); if (window && window->context.client == GLFW_NO_API) { _glfwInputError(GLFW_NO_WINDOW_CONTEXT, "Cannot make current with a window that has no OpenGL or OpenGL ES context"); return; } if (previous) { if (!window || window->context.source != previous->context.source) previous->context.makeCurrent(NULL); } if (window) window->context.makeCurrent(window); } GLFWAPI GLFWwindow* glfwGetCurrentContext(void) { _GLFW_REQUIRE_INIT_OR_RETURN(NULL); return _glfwPlatformGetTls(&_glfw.contextSlot); } GLFWAPI void glfwSwapBuffers(GLFWwindow* handle) { _GLFWwindow* window = (_GLFWwindow*) handle; assert(window != NULL); _GLFW_REQUIRE_INIT(); if (window->context.client == GLFW_NO_API) { _glfwInputError(GLFW_NO_WINDOW_CONTEXT, "Cannot swap buffers of a window that has no OpenGL or OpenGL ES context"); return; } window->context.swapBuffers(window); } GLFWAPI void glfwSwapInterval(int interval) { _GLFWwindow* window; _GLFW_REQUIRE_INIT(); window = _glfwPlatformGetTls(&_glfw.contextSlot); if (!window) { _glfwInputError(GLFW_NO_CURRENT_CONTEXT, "Cannot set swap interval without a current OpenGL or OpenGL ES context"); return; } window->context.swapInterval(interval); } GLFWAPI int glfwExtensionSupported(const char* extension) { _GLFWwindow* window; assert(extension != NULL); _GLFW_REQUIRE_INIT_OR_RETURN(GLFW_FALSE); window = _glfwPlatformGetTls(&_glfw.contextSlot); if (!window) { _glfwInputError(GLFW_NO_CURRENT_CONTEXT, "Cannot query extension without a current OpenGL or OpenGL ES context"); return GLFW_FALSE; } if (*extension == '\0') { _glfwInputError(GLFW_INVALID_VALUE, "Extension name cannot be an empty string"); return GLFW_FALSE; } if (window->context.major >= 3) { int i; GLint count; // Check if extension is in the modern OpenGL extensions string list window->context.GetIntegerv(GL_NUM_EXTENSIONS, &count); for (i = 0; i < count; i++) { const char* en = (const char*) window->context.GetStringi(GL_EXTENSIONS, i); if (!en) { _glfwInputError(GLFW_PLATFORM_ERROR, "Extension string retrieval is broken"); return GLFW_FALSE; } if (strcmp(en, extension) == 0) return GLFW_TRUE; } } else { // Check if extension is in the old style OpenGL extensions string const char* extensions = (const char*) window->context.GetString(GL_EXTENSIONS); if (!extensions) { _glfwInputError(GLFW_PLATFORM_ERROR, "Extension string retrieval is broken"); return GLFW_FALSE; } if (_glfwStringInExtensionString(extension, extensions)) return GLFW_TRUE; } // Check if extension is in the platform-specific string return window->context.extensionSupported(extension); } GLFWAPI GLFWglproc glfwGetProcAddress(const char* procname) { _GLFWwindow* window; assert(procname != NULL); _GLFW_REQUIRE_INIT_OR_RETURN(NULL); window = _glfwPlatformGetTls(&_glfw.contextSlot); if (!window) { _glfwInputError(GLFW_NO_CURRENT_CONTEXT, "Cannot query entry point without a current OpenGL or OpenGL ES context"); return NULL; } return window->context.getProcAddress(procname); } #endif #ifndef HEADER_GUARD_INIT_C #define HEADER_GUARD_INIT_C //======================================================================== // GLFW 3.3.7 - www.glfw.org //------------------------------------------------------------------------ // Copyright (c) 2002-2006 Marcus Geelnard // Copyright (c) 2006-2018 Camilla Löwy // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would // be appreciated but is not required. // // 2. Altered source versions must be plainly marked as such, and must not // be misrepresented as being the original software. // // 3. This notice may not be removed or altered from any source // distribution. // //======================================================================== // Please use C89 style variable declarations in this file because VS 2010 //======================================================================== #include #include #include #include #include // NOTE: The global variables below comprise all mutable global data in GLFW // Any other mutable global variable is a bug // This contains all mutable state shared between compilation units of GLFW // _GLFWlibrary _glfw = { GLFW_FALSE }; // These are outside of _glfw so they can be used before initialization and // after termination without special handling when _glfw is cleared to zero // static _GLFWerror _glfwMainThreadError; static GLFWerrorfun _glfwErrorCallback; static _GLFWinitconfig _glfwInitHints = { GLFW_TRUE, // hat buttons { GLFW_TRUE, // macOS menu bar GLFW_TRUE // macOS bundle chdir } }; // Terminate the library // static void terminate(void) { int i; memset(&_glfw.callbacks, 0, sizeof(_glfw.callbacks)); while (_glfw.windowListHead) glfwDestroyWindow((GLFWwindow*) _glfw.windowListHead); while (_glfw.cursorListHead) glfwDestroyCursor((GLFWcursor*) _glfw.cursorListHead); for (i = 0; i < _glfw.monitorCount; i++) { _GLFWmonitor* monitor = _glfw.monitors[i]; if (monitor->originalRamp.size) _glfwPlatformSetGammaRamp(monitor, &monitor->originalRamp); _glfwFreeMonitor(monitor); } free(_glfw.monitors); _glfw.monitors = NULL; _glfw.monitorCount = 0; free(_glfw.mappings); _glfw.mappings = NULL; _glfw.mappingCount = 0; _glfwTerminateVulkan(); _glfwPlatformTerminate(); _glfw.initialized = GLFW_FALSE; while (_glfw.errorListHead) { _GLFWerror* error = _glfw.errorListHead; _glfw.errorListHead = error->next; free(error); } _glfwPlatformDestroyTls(&_glfw.contextSlot); _glfwPlatformDestroyTls(&_glfw.errorSlot); _glfwPlatformDestroyMutex(&_glfw.errorLock); memset(&_glfw, 0, sizeof(_glfw)); } ////////////////////////////////////////////////////////////////////////// ////// GLFW internal API ////// ////////////////////////////////////////////////////////////////////////// // Encode a Unicode code point to a UTF-8 stream // Based on cutef8 by Jeff Bezanson (Public Domain) // size_t _glfwEncodeUTF8(char* s, uint32_t codepoint) { size_t count = 0; if (codepoint < 0x80) s[count++] = (char) codepoint; else if (codepoint < 0x800) { s[count++] = (codepoint >> 6) | 0xc0; s[count++] = (codepoint & 0x3f) | 0x80; } else if (codepoint < 0x10000) { s[count++] = (codepoint >> 12) | 0xe0; s[count++] = ((codepoint >> 6) & 0x3f) | 0x80; s[count++] = (codepoint & 0x3f) | 0x80; } else if (codepoint < 0x110000) { s[count++] = (codepoint >> 18) | 0xf0; s[count++] = ((codepoint >> 12) & 0x3f) | 0x80; s[count++] = ((codepoint >> 6) & 0x3f) | 0x80; s[count++] = (codepoint & 0x3f) | 0x80; } return count; } char* _glfw_strdup(const char* source) { const size_t length = strlen(source); char* result = calloc(length + 1, 1); strcpy(result, source); return result; } float _glfw_fminf(float a, float b) { if (a != a) return b; else if (b != b) return a; else if (a < b) return a; else return b; } float _glfw_fmaxf(float a, float b) { if (a != a) return b; else if (b != b) return a; else if (a > b) return a; else return b; } ////////////////////////////////////////////////////////////////////////// ////// GLFW event API ////// ////////////////////////////////////////////////////////////////////////// // Notifies shared code of an error // void _glfwInputError(int code, const char* format, ...) { _GLFWerror* error; char description[_GLFW_MESSAGE_SIZE]; if (format) { va_list vl; va_start(vl, format); vsnprintf(description, sizeof(description), format, vl); va_end(vl); description[sizeof(description) - 1] = '\0'; } else { if (code == GLFW_NOT_INITIALIZED) strcpy(description, "The GLFW library is not initialized"); else if (code == GLFW_NO_CURRENT_CONTEXT) strcpy(description, "There is no current context"); else if (code == GLFW_INVALID_ENUM) strcpy(description, "Invalid argument for enum parameter"); else if (code == GLFW_INVALID_VALUE) strcpy(description, "Invalid value for parameter"); else if (code == GLFW_OUT_OF_MEMORY) strcpy(description, "Out of memory"); else if (code == GLFW_API_UNAVAILABLE) strcpy(description, "The requested API is unavailable"); else if (code == GLFW_VERSION_UNAVAILABLE) strcpy(description, "The requested API version is unavailable"); else if (code == GLFW_PLATFORM_ERROR) strcpy(description, "A platform-specific error occurred"); else if (code == GLFW_FORMAT_UNAVAILABLE) strcpy(description, "The requested format is unavailable"); else if (code == GLFW_NO_WINDOW_CONTEXT) strcpy(description, "The specified window has no context"); else strcpy(description, "ERROR: UNKNOWN GLFW ERROR"); } if (_glfw.initialized) { error = _glfwPlatformGetTls(&_glfw.errorSlot); if (!error) { error = calloc(1, sizeof(_GLFWerror)); _glfwPlatformSetTls(&_glfw.errorSlot, error); _glfwPlatformLockMutex(&_glfw.errorLock); error->next = _glfw.errorListHead; _glfw.errorListHead = error; _glfwPlatformUnlockMutex(&_glfw.errorLock); } } else error = &_glfwMainThreadError; error->code = code; strcpy(error->description, description); if (_glfwErrorCallback) _glfwErrorCallback(code, description); } ////////////////////////////////////////////////////////////////////////// ////// GLFW public API ////// ////////////////////////////////////////////////////////////////////////// GLFWAPI int glfwInit(void) { if (_glfw.initialized) return GLFW_TRUE; memset(&_glfw, 0, sizeof(_glfw)); _glfw.hints.init = _glfwInitHints; if (!_glfwPlatformInit()) { terminate(); return GLFW_FALSE; } if (!_glfwPlatformCreateMutex(&_glfw.errorLock) || !_glfwPlatformCreateTls(&_glfw.errorSlot) || !_glfwPlatformCreateTls(&_glfw.contextSlot)) { terminate(); return GLFW_FALSE; } _glfwPlatformSetTls(&_glfw.errorSlot, &_glfwMainThreadError); _glfwInitGamepadMappings(); _glfw.initialized = GLFW_TRUE; _glfw.timer.offset = _glfwPlatformGetTimerValue(); glfwDefaultWindowHints(); return GLFW_TRUE; } GLFWAPI void glfwTerminate(void) { if (!_glfw.initialized) return; terminate(); } GLFWAPI void glfwInitHint(int hint, int value) { switch (hint) { case GLFW_JOYSTICK_HAT_BUTTONS: _glfwInitHints.hatButtons = value; return; case GLFW_COCOA_CHDIR_RESOURCES: _glfwInitHints.ns.chdir = value; return; case GLFW_COCOA_MENUBAR: _glfwInitHints.ns.menubar = value; return; } _glfwInputError(GLFW_INVALID_ENUM, "Invalid init hint 0x%08X", hint); } GLFWAPI void glfwGetVersion(int* major, int* minor, int* rev) { if (major != NULL) *major = GLFW_VERSION_MAJOR; if (minor != NULL) *minor = GLFW_VERSION_MINOR; if (rev != NULL) *rev = GLFW_VERSION_REVISION; } GLFWAPI const char* glfwGetVersionString(void) { return _glfwPlatformGetVersionString(); } GLFWAPI int glfwGetError(const char** description) { _GLFWerror* error; int code = GLFW_NO_ERROR; if (description) *description = NULL; if (_glfw.initialized) error = _glfwPlatformGetTls(&_glfw.errorSlot); else error = &_glfwMainThreadError; if (error) { code = error->code; error->code = GLFW_NO_ERROR; if (description && code) *description = error->description; } return code; } GLFWAPI GLFWerrorfun glfwSetErrorCallback(GLFWerrorfun cbfun) { _GLFW_SWAP_POINTERS(_glfwErrorCallback, cbfun); return cbfun; } #endif #ifndef HEADER_GUARD_INPUT_C #define HEADER_GUARD_INPUT_C //======================================================================== // GLFW 3.3.7 - www.glfw.org //------------------------------------------------------------------------ // Copyright (c) 2002-2006 Marcus Geelnard // Copyright (c) 2006-2019 Camilla Löwy // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would // be appreciated but is not required. // // 2. Altered source versions must be plainly marked as such, and must not // be misrepresented as being the original software. // // 3. This notice may not be removed or altered from any source // distribution. // //======================================================================== // Please use C89 style variable declarations in this file because VS 2010 //======================================================================== #ifndef HEADER_GUARD_MAPPINGS_H #define HEADER_GUARD_MAPPINGS_H //======================================================================== // GLFW 3.3.7 - www.glfw.org //------------------------------------------------------------------------ // Copyright (c) 2006-2018 Camilla Löwy // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would // be appreciated but is not required. // // 2. Altered source versions must be plainly marked as such, and must not // be misrepresented as being the original software. // // 3. This notice may not be removed or altered from any source // distribution. // //======================================================================== // As mappings.h.in, this file is used by CMake to produce the mappings.h // header file. If you are adding a GLFW specific gamepad mapping, this is // where to put it. //======================================================================== // As mappings.h, this provides all pre-defined gamepad mappings, including // all available in SDL_GameControllerDB. Do not edit this file. Any gamepad // mappings not specific to GLFW should be submitted to SDL_GameControllerDB. // This file can be re-generated from mappings.h.in and the upstream // gamecontrollerdb.txt with the 'update_mappings' CMake target. //======================================================================== // All gamepad mappings not labeled GLFW are copied from the // SDL_GameControllerDB project under the following license: // // Simple DirectMedia Layer // Copyright (C) 1997-2013 Sam Lantinga // // This software is provided 'as-is', without any express or implied warranty. // In no event will the authors be held liable for any damages arising from the // use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would // be appreciated but is not required. // // 2. Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. // // 3. This notice may not be removed or altered from any source distribution. const char* _glfwDefaultMappings[] = { #if defined(GLFW_BUILD_WIN32_MAPPINGS) "03000000fa2d00000100000000000000,3DRUDDER,leftx:a0,lefty:a1,rightx:a5,righty:a2,platform:Windows,", "03000000c82d00002038000000000000,8bitdo,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows,", "03000000c82d00000951000000000000,8BitDo Dogbone Modkit,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,start:b11,platform:Windows,", "03000000c82d000011ab000000000000,8BitDo F30,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows,", "03000000c82d00001038000000000000,8BitDo F30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b4,y:b3,platform:Windows,", "03000000c82d00000090000000000000,8BitDo FC30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows,", "03000000c82d00000650000000000000,8BitDo M30,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:a4,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,start:b11,x:b3,y:b4,platform:Windows,", "03000000c82d00005106000000000000,8BitDo M30 Gamepad,a:b1,b:b0,back:b10,guide:b2,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,start:b11,x:b4,y:b3,platform:Windows,", "03000000c82d00000151000000000000,8BitDo M30 ModKit,a:b0,b:b1,back:b10,dpdown:+a2,dpleft:-a0,dpright:+a0,dpup:-a2,rightshoulder:b6,righttrigger:b7,start:b11,x:b3,y:b4,platform:Windows,", "03000000c82d00000310000000000000,8BitDo N30,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Windows,", "03000000c82d00002028000000000000,8BitDo N30,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b4,y:b3,platform:Windows,", "03000000c82d00008010000000000000,8BitDo N30,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Windows,", "03000000c82d00000451000000000000,8BitDo N30 Modkit,a:b1,b:b0,back:b10,dpdown:+a2,dpleft:-a0,dpright:+a0,dpup:-a2,start:b11,platform:Windows,", "03000000c82d00000190000000000000,8BitDo N30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows,", "03000000c82d00001590000000000000,8BitDo N30 Pro 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b4,y:b3,platform:Windows,", "03000000c82d00006528000000000000,8BitDo N30 Pro 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows,", "03000000022000000090000000000000,8Bitdo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows,", "03000000203800000900000000000000,8Bitdo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows,", "03000000c82d00000360000000000000,8BitDo Pro 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows,", "03000000c82d00002867000000000000,8BitDo S30 Modkit,a:b0,b:b1,dpdown:+a2,dpleft:-a0,dpright:+a0,dpup:-a2,leftshoulder:b8,lefttrigger:b9,rightshoulder:b6,righttrigger:b7,start:b11,x:b3,y:b4,platform:Windows,", "03000000c82d00000130000000000000,8BitDo SF30,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b4,y:b3,platform:Windows,", "03000000c82d00000060000000000000,8Bitdo SF30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Windows,", "03000000c82d00000061000000000000,8Bitdo SF30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Windows,", "03000000c82d000021ab000000000000,8BitDo SFC30,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Windows,", "03000000102800000900000000000000,8Bitdo SFC30 GamePad,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Windows,", "03000000c82d00003028000000000000,8Bitdo SFC30 GamePad,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Windows,", "03000000c82d00000030000000000000,8BitDo SN30,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Windows,", "03000000c82d00001290000000000000,8BitDo SN30,a:b1,b:b0,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Windows,", "03000000c82d000020ab000000000000,8BitDo SN30,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b4,y:b3,platform:Windows,", "03000000c82d00004028000000000000,8BitDo SN30,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b4,y:b3,platform:Windows,", "03000000c82d00006228000000000000,8BitDo SN30,a:b1,b:b0,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Windows,", "03000000c82d00000351000000000000,8BitDo SN30 Modkit,a:b1,b:b0,back:b10,dpdown:+a2,dpleft:-a0,dpright:+a0,dpup:-a2,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Windows,", "03000000c82d00000160000000000000,8BitDo SN30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Windows,", "03000000c82d00000161000000000000,8BitDo SN30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Windows,", "03000000c82d00000121000000000000,8BitDo SN30 Pro for Android,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,", "03000000c82d00000260000000000000,8BitDo SN30 Pro+,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Windows,", "03000000c82d00000261000000000000,8BitDo SN30 Pro+,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Windows,", "03000000c82d00000031000000000000,8BitDo Wireless Adapter,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,", "03000000c82d00001890000000000000,8BitDo Zero 2,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Windows,", "03000000c82d00003032000000000000,8BitDo Zero 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Windows,", "03000000a00500003232000000000000,8Bitdo Zero GamePad,a:b0,b:b1,back:b10,dpdown:+a2,dpleft:-a0,dpright:+a0,dpup:-a2,leftshoulder:b6,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Windows,", "03000000a30c00002700000000000000,Astro City Mini,a:b2,b:b1,back:b8,leftx:a3,lefty:a4,rightshoulder:b4,righttrigger:b5,start:b9,x:b3,y:b0,platform:Windows,", "03000000a30c00002800000000000000,Astro City Mini,a:b2,b:b1,back:b8,leftx:a3,lefty:a4,rightshoulder:b4,righttrigger:b5,start:b9,x:b3,y:b0,platform:Windows,", "030000008f0e00001200000000000000,Acme GA-02,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b2,y:b3,platform:Windows,", "03000000c01100000355000011010000,ACRUX USB GAME PAD,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", "03000000fa190000f0ff000000000000,Acteck AGJ-3200,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,", "030000006f0e00001413000000000000,Afterglow,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", "03000000341a00003608000000000000,Afterglow PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", "030000006f0e00000263000000000000,Afterglow PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", "030000006f0e00001101000000000000,Afterglow PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", "030000006f0e00001401000000000000,Afterglow PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", "030000006f0e00001402000000000000,Afterglow PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", "030000006f0e00001901000000000000,Afterglow PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", "030000006f0e00001a01000000000000,Afterglow PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", "03000000d62000001d57000000000000,Airflo PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", "03000000491900001904000000000000,Amazon Luna Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b9,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b7,x:b2,y:b3,platform:Windows,", "03000000710100001904000000000000,Amazon Luna Controller,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b11,leftshoulder:b5,leftstick:b8,leftx:a0,lefty:a1,misc1:b9,rightshoulder:b4,rightstick:b7,rightx:a3,righty:a4,start:b6,x:b3,y:b2,platform:Windows,", "03000000ef0500000300000000000000,AxisPad,a:b2,b:b3,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a3,righty:a2,start:b11,x:b0,y:b1,platform:Windows,", "03000000d6200000e557000000000000,Batarang,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", "03000000c01100001352000000000000,Battalife Joystick,a:b6,b:b7,back:b2,leftshoulder:b0,leftx:a0,lefty:a1,rightshoulder:b1,start:b3,x:b4,y:b5,platform:Windows,", "030000006f0e00003201000000000000,Battlefield 4 PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", "03000000d62000002a79000000000000,BDA PS4 Fightpad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", "03000000bc2000006012000000000000,Betop 2126F,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,", "03000000bc2000000055000000000000,Betop BFM Gamepad,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,", "03000000bc2000006312000000000000,Betop Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,", "03000000bc2000006321000000000000,BETOP CONTROLLER,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,", "03000000bc2000006412000000000000,Betop Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,", "03000000c01100000555000000000000,Betop Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,", "03000000c01100000655000000000000,Betop Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,", "03000000790000000700000000000000,Betop Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a4,start:b9,x:b3,y:b0,platform:Windows,", "03000000808300000300000000000000,Betop Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a4,start:b9,x:b3,y:b0,platform:Windows,", "030000006b1400000055000000000000,Bigben PS3 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,", "030000006b1400000103000000000000,Bigben PS3 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b2,platform:Windows,", "03000000120c0000210e000000000000,Brook Mars,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", "0300000066f700000500000000000000,BrutalLegendTest,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b3,platform:Windows,", "03000000d81d00000b00000000000000,BUFFALO BSGP1601 Series ,a:b5,b:b3,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b8,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b9,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b13,x:b4,y:b2,platform:Windows,", "03000000e82000006058000000000000,Cideko AK08b,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,", "03000000457500000401000000000000,Cobra,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", "030000005e0400008e02000000000000,Controller (XBOX 360 For Windows),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:+a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:-a2,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,", "030000005e040000a102000000000000,Controller (Xbox 360 Wireless Receiver for Windows),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:+a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:-a2,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,", "030000005e040000ff02000000000000,Controller (Xbox One For Windows) - Wired,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:+a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:-a2,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,", "030000005e040000ea02000000000000,Controller (Xbox One For Windows) - Wireless,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:+a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:-a2,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,", "03000000260900008888000000000000,Cyber Gadget GameCube Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:a4,rightx:a2,righty:a3~,start:b7,x:b2,y:b3,platform:Windows,", "03000000a306000022f6000000000000,Cyborg V.3 Rumble Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:+a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:-a3,rightx:a2,righty:a4,start:b9,x:b0,y:b3,platform:Windows,", "03000000451300000830000000000000,Defender Game Racer X7,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,", "030000007d0400000840000000000000,Destroyer Tiltpad,+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b1,b:b2,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,x:b0,y:b3,platform:Windows,", "03000000791d00000103000000000000,Dual Box WII,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,", "03000000bd12000002e0000000000000,Dual USB Vibration Joystick,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b9,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b10,righttrigger:b5,rightx:a3,righty:a2,start:b11,x:b3,y:b0,platform:Windows,", "030000008f0e00000910000000000000,DualShock 2,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b9,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b10,righttrigger:b5,rightx:a3,righty:a2,start:b11,x:b3,y:b0,platform:Windows,", "030000006f0e00003001000000000000,EA SPORTS PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", "03000000b80500000410000000000000,Elecom Gamepad,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b1,platform:Windows,", "03000000b80500000610000000000000,Elecom Gamepad,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b1,platform:Windows,", "03000000120c0000f61c000000000000,Elite,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", "030000008f0e00000f31000000000000,EXEQ,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b2,platform:Windows,", "03000000341a00000108000000000000,EXEQ RF USB Gamepad 8206,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,", "030000006f0e00008401000000000000,Faceoff Deluxe+ Audio Wired Controller for Nintendo Switch,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", "030000006f0e00008001000000000000,Faceoff Wired Pro Controller for Nintendo Switch,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", "03000000852100000201000000000000,FF-GP1,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", "030000000d0f00008500000000000000,Fighting Commander 2016 PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", "030000000d0f00008400000000000000,Fighting Commander 5,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", "030000000d0f00008700000000000000,Fighting Stick mini 4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,", "030000000d0f00008800000000000000,Fighting Stick mini 4,a:b1,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b8,x:b0,y:b3,platform:Windows,", "030000000d0f00002700000000000000,FIGHTING STICK V3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,", "78696e70757403000000000000000000,Fightstick TES,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,start:b7,x:b2,y:b3,platform:Windows,", "03000000790000002201000000000000,Game Controller for PC,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,", "0300000066f700000100000000000000,Game VIB Joystick,a:b2,b:b3,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a3,righty:a2,start:b11,x:b0,y:b1,platform:Windows,", "03000000260900002625000000000000,Gamecube Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b6,lefttrigger:a4,leftx:a0,lefty:a1,righttrigger:a5,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Windows,", "03000000790000004618000000000000,GameCube Controller Adapter,a:b1,b:b2,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,rightx:a5,righty:a2,start:b9,x:b0,y:b3,platform:Windows,", "030000008f0e00000d31000000000000,GAMEPAD 3 TURBO,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", "03000000280400000140000000000000,GamePad Pro USB,a:b1,b:b2,back:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,", "03000000ac0500003d03000000000000,GameSir,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,", "03000000ac0500004d04000000000000,GameSir,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,", "03000000ffff00000000000000000000,GameStop Gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,", "03000000c01100000140000000000000,GameStop PS4 Fun Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", "030000009b2800003200000000000000,GC/N64 to USB v3.4,a:b0,b:b7,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,lefttrigger:+a5,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:+a2,rightx:a3,righty:a4,start:b3,x:b1,y:b8,platform:Windows,", "030000009b2800006000000000000000,GC/N64 to USB v3.6,a:b0,b:b7,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,lefttrigger:+a5,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:+a2,rightx:a3,righty:a4,start:b3,x:b1,y:b8,platform:Windows,", "030000008305000009a0000000000000,Genius,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,", "030000008305000031b0000000000000,Genius Maxfire Blaze 3,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,", "03000000451300000010000000000000,Genius Maxfire Grandias 12,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,", "030000005c1a00003330000000000000,Genius MaxFire Grandias 12V,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b4,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b2,y:b3,platform:Windows,", "03000000300f00000b01000000000000,GGE909 Recoil Pad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Windows,", "03000000f0250000c283000000000000,Gioteck,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,", "03000000f025000021c1000000000000,Gioteck PS3 Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,", "03000000f0250000c383000000000000,Gioteck VX2 Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,", "03000000f0250000c483000000000000,Gioteck VX2 Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,", "030000007d0400000540000000000000,Gravis Eliminator GamePad Pro,a:b1,b:b2,back:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,", "03000000341a00000302000000000000,Hama Scorpad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", "030000000d0f00004900000000000000,Hatsune Miku Sho Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", "030000001008000001e1000000000000,Havit HV-G60,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b3,y:b0,platform:Windows,", "03000000d81400000862000000000000,HitBox Edition Cthulhu+,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b5,lefttrigger:b4,rightshoulder:b7,righttrigger:b6,start:b9,x:b0,y:b3,platform:Windows,", "03000000632500002605000000000000,HJD-X,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,", "030000000d0f00002d00000000000000,Hori Fighting Commander 3 Pro,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", "030000000d0f00005f00000000000000,Hori Fighting Commander 4 (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", "030000000d0f00005e00000000000000,Hori Fighting Commander 4 (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", "030000000d0f00004000000000000000,Hori Fighting Stick Mini 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b5,lefttrigger:b4,rightshoulder:b7,righttrigger:b6,start:b9,x:b0,y:b3,platform:Windows,", "030000000d0f00005400000000000000,Hori Pad 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", "030000000d0f00000900000000000000,Hori Pad 3 Turbo,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", "030000000d0f00004d00000000000000,Hori Pad A,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", "030000000d0f00009200000000000000,Hori Pokken Tournament DX Pro Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,", "030000000d0f00001600000000007803,HORI Real Arcade Pro EX-SE (Xbox 360),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,start:b7,x:b2,y:b3,platform:Windows,", "030000000d0f00009c00000000000000,Hori TAC Pro,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", "030000000d0f0000c100000000000000,Horipad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", "030000000d0f00006e00000000000000,HORIPAD 4 (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", "030000000d0f00006600000000000000,HORIPAD 4 (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", "030000000d0f00005500000000000000,Horipad 4 FPS,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", "030000000d0f0000ee00000000000000,HORIPAD mini4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", "03000000250900000017000000000000,HRAP2 on PS/SS/N64 Joypad to USB BOX,a:b2,b:b1,back:b9,leftshoulder:b5,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b6,start:b8,x:b3,y:b0,platform:Windows,", "030000008f0e00001330000000000000,HuiJia SNES Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b9,x:b3,y:b0,platform:Windows,", "03000000d81d00000f00000000000000,iBUFFALO BSGP1204 Series,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,", "03000000d81d00001000000000000000,iBUFFALO BSGP1204P Series,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,", "03000000830500006020000000000000,iBuffalo SNES Controller,a:b1,b:b0,back:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b7,x:b3,y:b2,platform:Windows,", "03000000b50700001403000000000000,Impact Black,a:b2,b:b3,back:b8,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Windows,", "030000006f0e00002401000000000000,INJUSTICE FightStick PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,", "03000000ac0500002c02000000000000,IPEGA,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b8,leftstick:b13,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b9,rightstick:b14,righttrigger:b7,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,", "03000000491900000204000000000000,Ipega PG-9023,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,", "03000000491900000304000000000000,Ipega PG-9087 - Bluetooth Gamepad,+righty:+a5,-righty:-a4,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,start:b11,x:b3,y:b4,platform:Windows,", "030000006e0500000a20000000000000,JC-DUX60 ELECOM MMO Gamepad,a:b2,b:b3,back:b17,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b8,leftstick:b14,lefttrigger:b12,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b15,righttrigger:b13,rightx:a3,righty:a4,start:b20,x:b0,y:b1,platform:Windows,", "030000006e0500000520000000000000,JC-P301U,a:b2,b:b3,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3,start:b11,x:b0,y:b1,platform:Windows,", "030000006e0500000320000000000000,JC-U3613M (DInput),a:b2,b:b3,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3,start:b11,x:b0,y:b1,platform:Windows,", "030000006e0500000720000000000000,JC-W01U,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b1,platform:Windows,", "030000007e0500000620000000000000,Joy-Con (L),+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b0,b:b1,back:b13,leftshoulder:b4,leftstick:b10,rightshoulder:b5,start:b8,x:b2,y:b3,platform:Windows,", "030000007e0500000620000001000000,Joy-Con (L),+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b0,b:b1,back:b13,leftshoulder:b4,leftstick:b10,rightshoulder:b5,start:b8,x:b2,y:b3,platform:Windows,", "030000007e0500000720000000000000,Joy-Con (R),+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b0,b:b1,back:b12,leftshoulder:b4,leftstick:b11,rightshoulder:b5,start:b9,x:b2,y:b3,platform:Windows,", "030000007e0500000720000001000000,Joy-Con (R),+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b0,b:b1,back:b12,leftshoulder:b4,leftstick:b11,rightshoulder:b5,start:b9,x:b2,y:b3,platform:Windows,", "03000000bd12000003c0000010010000,Joypad Alpha Shock,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", "03000000bd12000003c0000000000000,JY-P70UR,a:b1,b:b0,back:b5,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b8,rightstick:b11,righttrigger:b9,rightx:a3,righty:a2,start:b4,x:b3,y:b2,platform:Windows,", "03000000242f00002d00000000000000,JYS Wireless Adapter,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,", "03000000242f00008a00000000000000,JYS Wireless Adapter,a:b1,b:b4,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b0,y:b3,platform:Windows,", "03000000790000000200000000000000,King PS3 Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a4,start:b9,x:b3,y:b0,platform:Windows,", "030000006d040000d1ca000000000000,Logitech ChillStream,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", "030000006d040000d2ca000000000000,Logitech Cordless Precision,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", "030000006d04000011c2000000000000,Logitech Cordless Wingman,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b5,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b2,righttrigger:b7,rightx:a3,righty:a4,x:b4,platform:Windows,", "030000006d04000016c2000000000000,Logitech Dual Action,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", "030000006d04000018c2000000000000,Logitech F510 Gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", "030000006d04000019c2000000000000,Logitech F710 Gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", "030000006d0400001ac2000000000000,Logitech Precision Gamepad,a:b1,b:b2,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,", "030000006d0400000ac2000000000000,Logitech WingMan RumblePad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b2,rightx:a3,righty:a4,x:b3,y:b4,platform:Windows,", "03000000380700006652000000000000,Mad Catz C.T.R.L.R,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b0,y:b3,platform:Windows,", "03000000380700005032000000000000,Mad Catz FightPad PRO (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", "03000000380700005082000000000000,Mad Catz FightPad PRO (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", "03000000380700008433000000000000,Mad Catz FightStick TE S+ (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", "03000000380700008483000000000000,Mad Catz FightStick TE S+ (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", "03000000380700008134000000000000,Mad Catz FightStick TE2+ PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b7,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b4,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", "03000000380700008184000000000000,Mad Catz FightStick TE2+ PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b5,leftstick:b10,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b4,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", "03000000380700006252000000000000,Mad Catz Micro C.T.R.L.R,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b0,y:b3,platform:Windows,", "03000000380700008034000000000000,Mad Catz TE2 PS3 Fightstick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", "03000000380700008084000000000000,Mad Catz TE2 PS4 Fightstick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", "03000000380700008532000000000000,Madcatz Arcade Fightstick TE S PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", "03000000380700003888000000000000,Madcatz Arcade Fightstick TE S+ PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", "03000000380700001888000000000000,MadCatz SFIV FightStick PS3,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b5,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b4,righttrigger:b6,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,", "03000000380700008081000000000000,MADCATZ SFV Arcade FightStick Alpha PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", "030000002a0600001024000000000000,Matricom,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b2,y:b3,platform:Windows,", "030000009f000000adbb000000000000,MaxJoypad Virtual Controller,a:b1,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Windows,", "03000000250900000128000000000000,Mayflash Arcade Stick,a:b1,b:b2,back:b8,leftshoulder:b0,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b3,righttrigger:b7,start:b9,x:b5,y:b6,platform:Windows,", "03000000790000004418000000000000,Mayflash GameCube Controller,a:b1,b:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b0,y:b3,platform:Windows,", "03000000790000004318000000000000,Mayflash GameCube Controller Adapter,a:b1,b:b2,back:b0,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b0,leftshoulder:b4,leftstick:b0,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b0,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b0,y:b3,platform:Windows,", "03000000242f00007300000000000000,Mayflash Magic NS,a:b1,b:b4,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b0,y:b3,platform:Windows,", "0300000079000000d218000000000000,Mayflash Magic NS,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,", "03000000d620000010a7000000000000,Mayflash Magic NS,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", "030000008f0e00001030000000000000,Mayflash USB Adapter for original Sega Saturn controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b5,rightshoulder:b2,righttrigger:b7,start:b9,x:b3,y:b4,platform:Windows,", "0300000025090000e803000000000000,Mayflash Wii Classic Controller,a:b1,b:b0,back:b8,dpdown:b13,dpleft:b12,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b2,platform:Windows,", "03000000790000000018000000000000,Mayflash WiiU Pro Game Controller Adapter (DInput),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", "03000000790000002418000000000000,Mega Drive,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,rightshoulder:b2,start:b9,x:b3,y:b4,platform:Windows,", "03000000380700006382000000000000,MLG GamePad PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", "03000000c62400002a89000000000000,MOGA XP5-A Plus,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b15,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,", "03000000c62400002b89000000000000,MOGA XP5-A Plus,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,", "03000000c62400001a89000000000000,MOGA XP5-X Plus,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,", "03000000c62400001b89000000000000,MOGA XP5-X Plus,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,", "03000000efbe0000edfe000000000000,Monect Virtual Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b0,platform:Windows,", "03000000250900006688000000000000,MP-8866 Super Dual Box,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Windows,", "030000006b140000010c000000000000,NACON GC-400ES,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,", "03000000921200004b46000000000000,NES 2-port Adapter,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,start:b11,platform:Windows,", "03000000790000004518000000000000,NEXILUX GAMECUBE Controller Adapter,platform:Windows,a:b1,b:b0,x:b2,y:b3,start:b9,rightshoulder:b7,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a5,righty:a2,lefttrigger:a3,righttrigger:a4,", "030000001008000001e5000000000000,NEXT SNES Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,righttrigger:b6,start:b9,x:b3,y:b0,platform:Windows,", "03000000152000000182000000000000,NGDS,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b3,y:b0,platform:Windows,", "03000000bd12000015d0000000000000,Nintendo Retrolink USB Super SNES Classic Controller,a:b2,b:b1,back:b8,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Windows,", "030000007e0500000920000000000000,Nintendo Switch Pro Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,", "030000000d0500000308000000000000,Nostromo N45,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b4,leftstick:b12,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b10,x:b2,y:b3,platform:Windows,", "03000000550900001472000000000000,NVIDIA Controller v01.04,a:b11,b:b10,back:b13,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b7,leftstick:b5,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b4,righttrigger:a5,rightx:a3,righty:a6,start:b3,x:b9,y:b8,platform:Windows,", "030000004b120000014d000000000000,NYKO AIRFLO,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:a3,leftstick:a0,lefttrigger:b6,rightshoulder:b5,rightstick:a2,righttrigger:b7,start:b9,x:b2,y:b3,platform:Windows,", "03000000d620000013a7000000000000,NSW wired controller,platform:Windows,a:b1,b:b2,x:b0,y:b3,back:b8,guide:b12,start:b9,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,", "03000000782300000a10000000000000,Onlive Wireless Controller,a:b15,b:b14,back:b7,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b11,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a3,righty:a4,start:b6,x:b13,y:b12,platform:Windows,", "03000000d62000006d57000000000000,OPP PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", "030000006b14000001a1000000000000,Orange Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b2,y:b3,platform:Windows,", "03000000362800000100000000000000,OUYA Game Controller,a:b0,b:b3,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,guide:b14,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:b13,rightx:a3,righty:a4,x:b1,y:b2,platform:Windows,", "03000000120c0000f60e000000000000,P4 Wired Gamepad,a:b1,b:b2,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b5,lefttrigger:b7,rightshoulder:b4,righttrigger:b6,start:b9,x:b0,y:b3,platform:Windows,", "030000006f0e00000901000000000000,PDP Versus Fighting Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,", "030000008f0e00000300000000000000,Piranha xtreme,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Windows,", "030000004c050000da0c000000000000,PlayStation Classic Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,lefttrigger:b4,rightshoulder:b7,righttrigger:b5,start:b9,x:b3,y:b0,platform:Windows,", "030000004c0500003713000000000000,PlayStation Vita,a:b1,b:b2,back:b8,dpdown:b13,dpleft:b15,dpright:b14,dpup:b12,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,rightx:a3,righty:a4,start:b9,x:b0,y:b3,platform:Windows,", "03000000d62000006dca000000000000,PowerA Pro Ex,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", "03000000d62000009557000000000000,Pro Elite PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", "03000000d62000009f31000000000000,Pro Ex mini PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", "03000000d6200000c757000000000000,Pro Ex mini PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", "03000000632500002306000000000000,PS Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Windows,", "03000000e30500009605000000000000,PS to USB convert cable,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Windows,", "03000000100800000100000000000000,PS1 Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Windows,", "030000008f0e00007530000000000000,PS1 Controller,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b1,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", "03000000100800000300000000000000,PS2 Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a4,righty:a2,start:b9,x:b3,y:b0,platform:Windows,", "03000000250900008888000000000000,PS2 Controller,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Windows,", "03000000666600006706000000000000,PS2 Controller,a:b2,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,leftshoulder:b6,leftstick:b9,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b10,righttrigger:b5,rightx:a2,righty:a3,start:b11,x:b3,y:b0,platform:Windows,", "030000006b1400000303000000000000,PS2 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,", "030000009d0d00001330000000000000,PS2 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,", "03000000250900000500000000000000,PS3 Controller,a:b2,b:b1,back:b9,dpdown:h0.8,dpleft:h0.4,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b0,y:b3,platform:Windows,", "030000004c0500006802000000000000,PS3 Controller,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b10,lefttrigger:a3~,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:a4~,rightx:a2,righty:a5,start:b8,x:b3,y:b0,platform:Windows,", "03000000632500007505000000000000,PS3 Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,", "03000000888800000803000000000000,PS3 Controller,a:b2,b:b1,back:b8,dpdown:h0.8,dpleft:h0.4,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b9,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:b7,rightx:a3,righty:a4,start:b11,x:b0,y:b3,platform:Windows,", "030000008f0e00001431000000000000,PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", "030000003807000056a8000000000000,PS3 RF pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", "03000000100000008200000000000000,PS360+ v1.66,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:h0.4,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,", "030000004c050000a00b000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", "030000004c050000c405000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", "030000004c050000cc09000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", "030000004c050000e60c000000000000,PS5 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", "03000000ff000000cb01000000000000,PSP,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b7,x:b2,y:b3,platform:Windows,", "03000000300f00000011000000000000,QanBa Arcade JoyStick 1008,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b10,x:b0,y:b3,platform:Windows,", "03000000300f00001611000000000000,QanBa Arcade JoyStick 4018,a:b1,b:b2,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b8,x:b0,y:b3,platform:Windows,", "03000000222c00000020000000000000,QANBA DRONE ARCADE JOYSTICK,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,rightshoulder:b5,righttrigger:a4,start:b9,x:b0,y:b3,platform:Windows,", "03000000300f00001210000000000000,QanBa Joystick Plus,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,rightshoulder:b5,start:b9,x:b2,y:b3,platform:Windows,", "03000000341a00000104000000000000,QanBa Joystick Q4RAF,a:b5,b:b6,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b0,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b3,righttrigger:b7,start:b9,x:b1,y:b2,platform:Windows,", "03000000222c00000223000000000000,Qanba Obsidian Arcade Joystick PS3 Mode,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", "03000000222c00000023000000000000,Qanba Obsidian Arcade Joystick PS4 Mode,a:b1,b:b2,back:b13,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", "03000000321500000003000000000000,Razer Hydra,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a2,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,", "03000000321500000204000000000000,Razer Panthera (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", "03000000321500000104000000000000,Razer Panthera (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", "03000000321500000507000000000000,Razer Raiju Mobile,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,", "03000000321500000707000000000000,Razer Raiju Mobile,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,", "03000000321500000011000000000000,Razer Raion Fightpad for PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", "03000000321500000009000000000000,Razer Serval,+lefty:+a2,-lefty:-a1,a:b0,b:b1,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,leftx:a0,rightshoulder:b5,rightstick:b9,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,", "030000000d0f00001100000000000000,REAL ARCADE PRO.3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,rightshoulder:b5,rightstick:b11,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,", "030000000d0f00006a00000000000000,Real Arcade Pro.4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", "030000000d0f00006b00000000000000,Real Arcade Pro.4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", "030000000d0f00008a00000000000000,Real Arcade Pro.4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", "030000000d0f00008b00000000000000,Real Arcade Pro.4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", "030000000d0f00007000000000000000,REAL ARCADE PRO.4 VLX,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,rightshoulder:b5,rightstick:b11,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,", "030000000d0f00002200000000000000,REAL ARCADE Pro.V3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", "030000000d0f00005b00000000000000,Real Arcade Pro.V4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", "030000000d0f00005c00000000000000,Real Arcade Pro.V4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", "03000000790000001100000000000000,Retrolink SNES Controller,a:b2,b:b1,back:b8,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-a4,leftshoulder:b4,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Windows,", "03000000bd12000013d0000000000000,Retrolink USB SEGA Saturn Classic,a:b0,b:b1,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b5,lefttrigger:b6,rightshoulder:b2,righttrigger:b7,start:b8,x:b3,y:b4,platform:Windows,", "0300000000f000000300000000000000,RetroUSB.com RetroPad,a:b1,b:b5,back:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b0,y:b4,platform:Windows,", "0300000000f00000f100000000000000,RetroUSB.com Super RetroPort,a:b1,b:b5,back:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b0,y:b4,platform:Windows,", "030000006b140000010d000000000000,Revolution Pro Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", "030000006b140000020d000000000000,Revolution Pro Controller 2(1/2),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", "030000006b140000130d000000000000,Revolution Pro Controller 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", "030000006f0e00001e01000000000000,Rock Candy PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", "030000006f0e00002801000000000000,Rock Candy PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", "030000006f0e00002f01000000000000,Rock Candy PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", "030000004f04000003d0000000000000,run'n'drive,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b7,leftshoulder:a3,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:a4,rightstick:b11,righttrigger:b5,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", "03000000a30600001af5000000000000,Saitek Cyborg,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b0,y:b3,platform:Windows,", "03000000a306000023f6000000000000,Saitek Cyborg V.1 Game pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a4,start:b9,x:b0,y:b3,platform:Windows,", "03000000300f00001201000000000000,Saitek Dual Analog Pad,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Windows,", "03000000a30600000701000000000000,Saitek P220,a:b2,b:b3,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,lefttrigger:b7,rightshoulder:b4,righttrigger:b5,x:b0,y:b1,platform:Windows,", "03000000a30600000cff000000000000,Saitek P2500 Force Rumble Pad,a:b2,b:b3,back:b11,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3,start:b10,x:b0,y:b1,platform:Windows,", "03000000a30600000c04000000000000,Saitek P2900,a:b1,b:b2,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b3,platform:Windows,", "03000000300f00001001000000000000,Saitek P480 Rumble Pad,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Windows,", "03000000a30600000b04000000000000,Saitek P990,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b3,platform:Windows,", "03000000a30600000b04000000010000,Saitek P990 Dual Analog Pad,a:b1,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b8,x:b0,y:b3,platform:Windows,", "03000000a30600002106000000000000,Saitek PS1000,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a4,start:b9,x:b0,y:b3,platform:Windows,", "03000000a306000020f6000000000000,Saitek PS2700,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a4,start:b9,x:b0,y:b3,platform:Windows,", "03000000300f00001101000000000000,Saitek Rumble Pad,a:b2,b:b3,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Windows,", "03000000730700000401000000000000,Sanwa PlayOnline Mobile,a:b0,b:b1,back:b2,leftx:a0,lefty:a1,start:b3,platform:Windows,", "0300000000050000289b000000000000,Saturn_Adapter_2.0,a:b1,b:b2,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,start:b9,x:b0,y:b3,platform:Windows,", "030000009b2800000500000000000000,Saturn_Adapter_2.0,a:b1,b:b2,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,start:b9,x:b0,y:b3,platform:Windows,", "03000000a30c00002500000000000000,Sega Genesis Mini 3B controller,a:b2,b:b1,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-a4,righttrigger:b5,start:b9,platform:Windows,", "03000000a30c00002400000000000000,Sega Mega Drive Mini 6B controller,a:b2,b:b1,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-a4,rightshoulder:b4,righttrigger:b5,start:b9,x:b3,y:b0,platform:Windows,", "03000000341a00000208000000000000,SL-6555-SBK,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:-a4,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a4,rightx:a3,righty:a2,start:b7,x:b2,y:b3,platform:Windows,", "03000000341a00000908000000000000,SL-6566,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,", "030000008f0e00000800000000000000,SpeedLink Strike FX,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,", "03000000c01100000591000000000000,Speedlink Torid,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,", "03000000d11800000094000000000000,Stadia Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:b12,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:b11,rightx:a3,righty:a4,start:b9,x:b2,y:b3,platform:Windows,", "03000000110100001914000000000000,SteelSeries,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftstick:b13,lefttrigger:b6,leftx:a0,lefty:a1,rightstick:b14,righttrigger:b7,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,", "03000000381000001214000000000000,SteelSeries Free,a:b0,b:b1,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Windows,", "03000000110100003114000000000000,SteelSeries Stratus Duo,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,", "03000000381000001814000000000000,SteelSeries Stratus XL,a:b0,b:b1,back:b18,dpdown:b13,dpleft:b14,dpright:b15,dpup:b12,guide:b19,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b2,y:b3,platform:Windows,", "03000000790000001c18000000000000,STK-7024X,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,", "03000000ff1100003133000000000000,SVEN X-PAD,a:b2,b:b3,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a4,start:b5,x:b0,y:b1,platform:Windows,", "03000000d620000011a7000000000000,Switch,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", "03000000457500002211000000000000,SZMY-POWER PC Gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", "030000004f04000007d0000000000000,T Mini Wireless,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", "030000004f0400000ab1000000000000,T.16000M,a:b0,b:b1,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b11,leftshoulder:b4,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,start:b10,x:b2,y:b3,platform:Windows,", "03000000fa1900000706000000000000,Team 5,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,", "03000000b50700001203000000000000,Techmobility X6-38V,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Windows,", "030000004f04000015b3000000000000,Thrustmaster Dual Analog 4,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,platform:Windows,", "030000004f04000023b3000000000000,Thrustmaster Dual Trigger 3-in-1,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", "030000004f0400000ed0000000000000,ThrustMaster eSwap PRO Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", "030000004f04000000b3000000000000,Thrustmaster Firestorm Dual Power,a:b0,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b11,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b10,x:b1,y:b3,platform:Windows,", "030000004f04000004b3000000000000,Thrustmaster Firestorm Dual Power 3,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,platform:Windows,", "03000000666600000488000000000000,TigerGame PS/PS2 Game Controller Adapter,a:b2,b:b1,back:b9,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Windows,", "03000000d62000006000000000000000,Tournament PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", "030000005f140000c501000000000000,Trust Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,", "03000000b80500000210000000000000,Trust Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,", "030000004f04000087b6000000000000,TWCS Throttle,dpdown:b8,dpleft:b9,dpright:b7,dpup:b6,leftstick:b5,lefttrigger:-a5,leftx:a0,lefty:a1,righttrigger:+a5,platform:Windows,", "03000000d90400000200000000000000,TwinShock PS2,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Windows,", "030000006e0500001320000000000000,U4113,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", "03000000101c0000171c000000000000,uRage Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,", "03000000300f00000701000000000000,USB 4-Axis 12-Button Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Windows,", "03000000341a00002308000000000000,USB gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,", "030000005509000000b4000000000000,USB gamepad,a:b10,b:b11,back:b5,dpdown:b1,dpleft:b2,dpright:b3,dpup:b0,guide:b14,leftshoulder:b8,leftstick:b6,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b9,rightstick:b7,righttrigger:a5,rightx:a2,righty:a3,start:b4,x:b12,y:b13,platform:Windows,", "030000006b1400000203000000000000,USB gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,", "03000000790000000a00000000000000,USB gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a4,start:b9,x:b3,y:b0,platform:Windows,", "03000000f0250000c183000000000000,USB gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", "03000000ff1100004133000000000000,USB gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a4,righty:a2,start:b9,x:b3,y:b0,platform:Windows,", "03000000632500002305000000000000,USB Vibration Joystick (BM),a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,", "03000000790000001a18000000000000,Venom,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,", "03000000790000001b18000000000000,Venom Arcade Joystick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,", "030000006f0e00000302000000000000,Victrix Pro Fight Stick for PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,", "030000006f0e00000702000000000000,Victrix Pro Fight Stick for PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,", "0300000034120000adbe000000000000,vJoy Device,a:b0,b:b1,back:b15,dpdown:b6,dpleft:b7,dpright:b8,dpup:b5,guide:b16,leftshoulder:b9,leftstick:b13,lefttrigger:b11,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b14,righttrigger:b12,rightx:a3,righty:a4,start:b4,x:b2,y:b3,platform:Windows,", "030000005e0400000a0b000000000000,Xbox Adaptive Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:+a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:-a2,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,", "030000005e040000130b000000000000,Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,", "03000000341a00000608000000000000,Xeox,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,", "03000000450c00002043000000000000,XEOX Gamepad SL-6556-BK,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,", "03000000ac0500005b05000000000000,Xiaoji Gamesir-G3w,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,", "03000000172700004431000000000000,XiaoMi Game Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b20,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a7,rightx:a2,righty:a5,start:b11,x:b3,y:b4,platform:Windows,", "03000000786901006e70000000000000,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,", "03000000790000004f18000000000000,ZD-T Android,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,", "03000000120c0000101e000000000000,ZEROPLUS P4 Wired Gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,", "78696e70757401000000000000000000,XInput Gamepad (GLFW),platform:Windows,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,back:b6,start:b7,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,dpup:h0.1,dpright:h0.2,dpdown:h0.4,dpleft:h0.8,", "78696e70757402000000000000000000,XInput Wheel (GLFW),platform:Windows,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,back:b6,start:b7,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,dpup:h0.1,dpright:h0.2,dpdown:h0.4,dpleft:h0.8,", "78696e70757403000000000000000000,XInput Arcade Stick (GLFW),platform:Windows,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,back:b6,start:b7,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,dpup:h0.1,dpright:h0.2,dpdown:h0.4,dpleft:h0.8,", "78696e70757404000000000000000000,XInput Flight Stick (GLFW),platform:Windows,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,back:b6,start:b7,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,dpup:h0.1,dpright:h0.2,dpdown:h0.4,dpleft:h0.8,", "78696e70757405000000000000000000,XInput Dance Pad (GLFW),platform:Windows,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,back:b6,start:b7,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,dpup:h0.1,dpright:h0.2,dpdown:h0.4,dpleft:h0.8,", "78696e70757406000000000000000000,XInput Guitar (GLFW),platform:Windows,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,back:b6,start:b7,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,dpup:h0.1,dpright:h0.2,dpdown:h0.4,dpleft:h0.8,", "78696e70757408000000000000000000,XInput Drum Kit (GLFW),platform:Windows,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,back:b6,start:b7,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,dpup:h0.1,dpright:h0.2,dpdown:h0.4,dpleft:h0.8,", #endif // GLFW_BUILD_WIN32_MAPPINGS #if defined(GLFW_BUILD_COCOA_MAPPINGS) "030000008f0e00000300000009010000,2In1 USB Joystick,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Mac OS X,", "03000000c82d00000090000001000000,8BitDo FC30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X,", "03000000c82d00001038000000010000,8BitDo FC30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X,", "03000000c82d00000650000001000000,8BitDo M30,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b8,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,start:b11,x:b3,y:b4,platform:Mac OS X,", "03000000c82d00005106000000010000,8BitDo M30 Gamepad,a:b1,b:b0,back:b10,guide:b2,leftshoulder:b6,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,start:b11,x:b4,y:b3,platform:Mac OS X,", "03000000c82d00001590000001000000,8BitDo N30 Pro 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X,", "03000000c82d00006528000000010000,8BitDo N30 Pro 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X,", "030000003512000012ab000001000000,8BitDo NES30 Gamepad,a:b1,b:b0,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Mac OS X,", "03000000022000000090000001000000,8Bitdo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X,", "03000000203800000900000000010000,8Bitdo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X,", "03000000c82d00000190000001000000,8Bitdo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X,", "03000000102800000900000000000000,8Bitdo SFC30 GamePad Joystick,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Mac OS X,", "03000000c82d00001290000001000000,8BitDo SN30 Gamepad,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Mac OS X,", "03000000c82d00004028000000010000,8Bitdo SN30 GamePad,a:b1,b:b0,x:b4,y:b3,back:b10,start:b11,leftshoulder:b6,rightshoulder:b7,dpup:-a1,dpdown:+a1,dpleft:-a0,dpright:+a0,platform:Mac OS X,", "03000000c82d00000160000001000000,8BitDo SN30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X,", "03000000c82d00000161000000010000,8BitDo SN30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b4,y:b3,platform:Mac OS X,", "03000000c82d00000260000001000000,8BitDo SN30 Pro+,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X,", "03000000c82d00000261000000010000,8BitDo SN30 Pro+,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X,", "03000000c82d00000031000001000000,8BitDo Wireless Adapter,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,", "03000000c82d00001890000001000000,8BitDo Zero 2,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Mac OS X,", "03000000c82d00003032000000010000,8BitDo Zero 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,rightx:a2,righty:a31,start:b11,x:b4,y:b3,platform:Mac OS X,", "03000000a00500003232000008010000,8Bitdo Zero GamePad,a:b0,b:b1,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Mac OS X,", "03000000a00500003232000009010000,8Bitdo Zero GamePad,a:b0,b:b1,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Mac OS X,", "03000000a30c00002700000003030000,Astro City Mini,a:b2,b:b1,back:b8,leftx:a3,lefty:a4,rightshoulder:b4,righttrigger:b5,start:b9,x:b3,y:b0,platform:Mac OS X,", "03000000a30c00002800000003030000,Astro City Mini,a:b2,b:b1,back:b8,leftx:a3,lefty:a4,rightshoulder:b4,righttrigger:b5,start:b9,x:b3,y:b0,platform:Mac OS X,", "03000000050b00000045000031000000,ASUS Gamepad,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Mac OS X,", "03000000ef0500000300000000020000,AxisPad,a:b2,b:b3,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a3,righty:a2,start:b11,x:b0,y:b1,platform:Mac OS X,", "03000000491900001904000001010000,Amazon Luna Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b9,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b7,x:b2,y:b3,platform:Mac OS X,", "03000000710100001904000000010000,Amazon Luna Controller,a:b0,b:b1,back:b11,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b9,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Mac OS X,", "03000000c62400001a89000000010000,BDA MOGA XP5-X Plus,a:b0,b:b1,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b14,leftshoulder:b6,leftstick:b15,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b16,righttrigger:a4,rightx:a2,righty:a3,start:b13,x:b3,y:b4,platform:Mac OS X,", "03000000c62400001b89000000010000,BDA MOGA XP5-X Plus,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,", "03000000d62000002a79000000010000,BDA PS4 Fightpad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,", "03000000120c0000200e000000010000,Brook Mars,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,", "03000000120c0000210e000000010000,Brook Mars,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,", "030000008305000031b0000000000000,Cideko AK08b,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,", "03000000260900008888000088020000,Cyber Gadget GameCube Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:a5,rightx:a2,righty:a3~,start:b7,x:b2,y:b3,platform:Mac OS X,", "03000000a306000022f6000001030000,Cyborg V.3 Rumble Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:+a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:-a3,rightx:a2,righty:a4,start:b9,x:b0,y:b3,platform:Mac OS X,", "03000000790000004618000000010000,GameCube Controller Adapter,a:b4,b:b0,dpdown:b56,dpleft:b60,dpright:b52,dpup:b48,lefttrigger:a12,leftx:a0,lefty:a4,rightshoulder:b28,righttrigger:a16,rightx:a20,righty:a8,start:b36,x:b8,y:b12,platform:Mac OS X,", "03000000ad1b000001f9000000000000,Gamestop BB-070 X360 Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,", "0500000047532047616d657061640000,GameStop Gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Mac OS X,", "03000000c01100000140000000010000,GameStop PS4 Fun Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,", "030000006f0e00000102000000000000,GameStop Xbox 360 Wired Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,", "030000007d0400000540000001010000,Gravis Eliminator GamePad Pro,a:b1,b:b2,back:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Mac OS X,", "03000000280400000140000000020000,Gravis Gamepad Pro,a:b1,b:b2,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Mac OS X,", "030000008f0e00000300000007010000,GreenAsia Inc. USB Joystick,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Mac OS X,", "030000000d0f00002d00000000100000,Hori Fighting Commander 3 Pro,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,", "030000000d0f00005f00000000010000,Hori Fighting Commander 4 (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,", "030000000d0f00005e00000000010000,Hori Fighting Commander 4 (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,", "030000000d0f00005f00000000000000,HORI Fighting Commander 4 PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,", "030000000d0f00005e00000000000000,HORI Fighting Commander 4 PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,", "030000000d0f00004d00000000000000,HORI Gem Pad 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,", "030000000d0f00009200000000010000,Hori Pokken Tournament DX Pro Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Mac OS X,", "030000000d0f00006e00000000010000,HORIPAD 4 (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,", "030000000d0f00006600000000010000,HORIPAD 4 (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,", "030000000d0f00006600000000000000,HORIPAD FPS PLUS 4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,", "030000000d0f0000ee00000000010000,HORIPAD mini4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,", "030000008f0e00001330000011010000,HuiJia SNES Controller,a:b4,b:b2,back:b16,dpdown:+a2,dpleft:-a0,dpright:+a0,dpup:-a2,leftshoulder:b12,rightshoulder:b14,start:b18,x:b6,y:b0,platform:Mac OS X,", "03000000830500006020000000010000,iBuffalo SNES Controller,a:b1,b:b0,back:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b7,x:b3,y:b2,platform:Mac OS X,", "03000000830500006020000000000000,iBuffalo USB 2-axis 8-button Gamepad,a:b1,b:b0,back:b6,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b7,x:b3,y:b2,platform:Mac OS X,", "030000007e0500000620000001000000,Joy-Con (L),+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b0,b:b1,back:b13,leftshoulder:b4,leftstick:b10,rightshoulder:b5,start:b8,x:b2,y:b3,platform:Mac OS X,", "030000007e0500000720000001000000,Joy-Con (R),+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b0,b:b1,back:b12,leftshoulder:b4,leftstick:b11,rightshoulder:b5,start:b9,x:b2,y:b3,platform:Mac OS X,", "03000000242f00002d00000007010000,JYS Wireless Adapter,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Mac OS X,", "030000006d04000016c2000000020000,Logitech Dual Action,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,", "030000006d04000016c2000000030000,Logitech Dual Action,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,", "030000006d04000016c2000014040000,Logitech Dual Action,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,", "030000006d04000016c2000000000000,Logitech F310 Gamepad (DInput),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,", "030000006d04000018c2000000000000,Logitech F510 Gamepad (DInput),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,", "030000006d04000019c2000005030000,Logitech F710,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,", "030000006d0400001fc2000000000000,Logitech F710 Gamepad (XInput),a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,", "030000006d04000018c2000000010000,Logitech RumblePad 2 USB,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3~,start:b9,x:b0,y:b3,platform:Mac OS X,", "030000006d04000019c2000000000000,Logitech Wireless Gamepad (DInput),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,", "03000000380700005032000000010000,Mad Catz FightPad PRO (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,", "03000000380700005082000000010000,Mad Catz FightPad PRO (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,", "03000000380700008433000000010000,Mad Catz FightStick TE S+ (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,", "03000000380700008483000000010000,Mad Catz FightStick TE S+ (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,", "03000000790000000600000007010000,Marvo GT-004,a:b2,b:b1,x:b3,y:b0,back:b8,start:b9,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,platform:Mac OS X,", "03000000790000004418000000010000,Mayflash GameCube Controller,a:b1,b:b2,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b0,y:b3,platform:Mac OS X,", "03000000242f00007300000000020000,Mayflash Magic NS,a:b1,b:b4,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b0,y:b3,platform:Mac OS X,", "0300000079000000d218000026010000,Mayflash Magic NS,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Mac OS X,", "03000000d620000010a7000003010000,Mayflash Magic NS,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,", "0300000025090000e803000000000000,Mayflash Wii Classic Controller,a:b1,b:b0,back:b8,dpdown:b13,dpleft:b12,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b2,platform:Mac OS X,", "03000000790000000018000000010000,Mayflash Wii U Pro Controller Adapter,a:b4,b:b8,back:b32,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b16,leftstick:b40,lefttrigger:b24,leftx:a0,lefty:a4,rightshoulder:b20,rightstick:b44,righttrigger:b28,rightx:a8,righty:a12,start:b36,x:b0,y:b12,platform:Mac OS X,", "03000000790000000018000000000000,Mayflash WiiU Pro Game Controller Adapter (DInput),a:b4,b:b8,back:b32,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b16,leftstick:b40,lefttrigger:b24,leftx:a0,lefty:a4,rightshoulder:b20,rightstick:b44,righttrigger:b28,rightx:a8,righty:a12,start:b36,x:b0,y:b12,platform:Mac OS X,", "03000000d8140000cecf000000000000,MC Cthulhu,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Mac OS X,", "030000005e0400002700000001010000,Microsoft SideWinder Plug & Play Game Pad,a:b0,b:b1,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,lefttrigger:b4,leftx:a0,lefty:a1,righttrigger:b5,x:b2,y:b3,platform:Mac OS X,", "03000000d62000007162000001000000,Moga Pro 2 HID,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Mac OS X,", "03000000c62400002a89000000010000,MOGA XP5-A Plus,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b21,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,", "03000000c62400002b89000000010000,MOGA XP5-A Plus,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,", "03000000632500007505000000020000,NEOGEO mini PAD Controller,a:b1,b:b0,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,start:b9,x:b2,y:b3,platform:Mac OS X,", "03000000921200004b46000003020000,NES 2-port Adapter,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,start:b11,platform:Mac OS X,", "030000001008000001e5000006010000,NEXT SNES Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,righttrigger:b6,start:b9,x:b3,y:b0,platform:Mac OS X,", "03000000d620000011a7000000020000,Nintendo Switch Core (Plus) Wired Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,", "03000000d620000011a7000010050000,Nintendo Switch PowerA Wired Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,", "030000007e0500000920000000000000,Nintendo Switch Pro Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Mac OS X,", "030000007e0500000920000001000000,Nintendo Switch Pro Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Mac OS X,", "03000000550900001472000025050000,NVIDIA Controller v01.04,a:b0,b:b1,back:b17,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b15,leftshoulder:b4,leftstick:b7,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a5,start:b6,x:b2,y:b3,platform:Mac OS X,", "030000006f0e00000901000002010000,PDP Versus Fighting Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Mac OS X,", "030000008f0e00000300000000000000,Piranha xtreme,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Mac OS X,", "030000004c050000da0c000000010000,Playstation Classic Controller,a:b2,b:b1,back:b8,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,start:b9,x:b3,y:b0,platform:Mac OS X,", "030000004c0500003713000000010000,PlayStation Vita,a:b1,b:b2,back:b8,dpdown:b13,dpleft:b15,dpright:b14,dpup:b12,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,rightx:a3,righty:a4,start:b9,x:b0,y:b3,platform:Mac OS X,", "03000000d62000006dca000000010000,PowerA Pro Ex,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,", "03000000100800000300000006010000,PS2 Adapter,a:b2,b:b1,back:b8,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a4,righty:a3,start:b9,x:b3,y:b0,platform:Mac OS X,", "030000004c0500006802000000000000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Mac OS X,", "030000004c0500006802000000010000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Mac OS X,", "030000004c050000a00b000000010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,", "030000004c050000c405000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,", "030000004c050000c405000000010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,", "030000004c050000cc09000000010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,", "050000004c050000e60c000000010000,PS5 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,", "030000008916000000fd000000000000,Razer Onza TE,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,", "03000000321500000204000000010000,Razer Panthera (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,", "03000000321500000104000000010000,Razer Panthera (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,", "03000000321500000010000000010000,Razer RAIJU,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,", "03000000321500000507000001010000,Razer Raiju Mobile,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b21,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,", "03000000321500000011000000010000,Razer Raion Fightpad for PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,", "03000000321500000009000000020000,Razer Serval,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Mac OS X,", "030000003215000000090000163a0000,Razer Serval,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Mac OS X,", "0300000032150000030a000000000000,Razer Wildcat,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,", "03000000790000001100000000000000,Retrolink Classic Controller,a:b2,b:b1,back:b8,leftshoulder:b4,leftx:a3,lefty:a4,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Mac OS X,", "03000000790000001100000006010000,Retrolink SNES Controller,a:b2,b:b1,back:b8,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-a4,leftshoulder:b4,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Mac OS X,", "030000006b140000010d000000010000,Revolution Pro Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,", "030000006b140000130d000000010000,Revolution Pro Controller 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,", "03000000c6240000fefa000000000000,Rock Candy Gamepad for PS3,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,", "03000000730700000401000000010000,Sanwa PlayOnline Mobile,a:b0,b:b1,back:b2,leftx:a0,lefty:a1,start:b3,platform:Mac OS X,", "03000000811700007e05000000000000,Sega Saturn,a:b2,b:b4,dpdown:b16,dpleft:b15,dpright:b14,dpup:b17,leftshoulder:b8,lefttrigger:a5,leftx:a0,lefty:a2,rightshoulder:b9,righttrigger:a4,start:b13,x:b0,y:b6,platform:Mac OS X,", "03000000b40400000a01000000000000,Sega Saturn USB Gamepad,a:b0,b:b1,back:b5,guide:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b8,x:b3,y:b4,platform:Mac OS X,", "030000003512000021ab000000000000,SFC30 Joystick,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Mac OS X,", "0300000000f00000f100000000000000,SNES RetroPort,a:b2,b:b3,back:b4,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b5,rightshoulder:b7,start:b6,x:b0,y:b1,platform:Mac OS X,", "030000004c050000e60c000000010000,Sony DualSense,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,", "030000004c050000cc09000000000000,Sony DualShock 4 V2,a:b1,b:b2,back:b13,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,", "030000004c050000a00b000000000000,Sony DualShock 4 Wireless Adaptor,a:b1,b:b2,back:b13,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,", "03000000d11800000094000000010000,Stadia Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Mac OS X,", "030000005e0400008e02000001000000,Steam Virtual Gamepad,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,", "03000000110100002014000000000000,SteelSeries Nimbus,a:b0,b:b1,dpdown:b9,dpleft:b11,dpright:b10,dpup:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b12,x:b2,y:b3,platform:Mac OS X,", "03000000110100002014000001000000,SteelSeries Nimbus,a:b0,b:b1,dpdown:b9,dpleft:b11,dpright:b10,dpup:b8,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3~,x:b2,y:b3,platform:Mac OS X,", "03000000381000002014000001000000,SteelSeries Nimbus,a:b0,b:b1,dpdown:b9,dpleft:b11,dpright:b10,dpup:b8,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3~,x:b2,y:b3,platform:Mac OS X,", "050000004e696d6275732b0000000000,SteelSeries Nimbus Plus,a:b0,b:b1,back:b15,dpdown:b11,dpleft:b13,dpright:b12,dpup:b10,guide:b16,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3~,start:b14,x:b2,y:b3,platform:Mac OS X,", "03000000110100001714000000000000,SteelSeries Stratus XL,a:b0,b:b1,dpdown:b9,dpleft:b11,dpright:b10,dpup:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3~,start:b12,x:b2,y:b3,platform:Mac OS X,", "03000000110100001714000020010000,SteelSeries Stratus XL,a:b0,b:b1,dpdown:b9,dpleft:b11,dpright:b10,dpup:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3~,start:b12,x:b2,y:b3,platform:Mac OS X,", "03000000457500002211000000010000,SZMY-POWER PC Gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,", "030000004f04000015b3000000000000,Thrustmaster Dual Analog 3.2,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,platform:Mac OS X,", "030000004f0400000ed0000000020000,ThrustMaster eSwap PRO Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,", "030000004f04000000b3000000000000,Thrustmaster Firestorm Dual Power,a:b0,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b11,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,rightx:a2,righty:a3,start:b10,x:b1,y:b3,platform:Mac OS X,", "03000000bd12000015d0000000000000,Tomee SNES USB Controller,a:b2,b:b1,back:b8,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Mac OS X,", "03000000bd12000015d0000000010000,Tomee SNES USB Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Mac OS X,", "03000000100800000100000000000000,Twin USB Joystick,a:b4,b:b2,back:b16,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b12,leftstick:b20,lefttrigger:b8,leftx:a0,lefty:a2,rightshoulder:b14,rightstick:b22,righttrigger:b10,rightx:a6,righty:a4,start:b18,x:b6,y:b0,platform:Mac OS X,", "030000006f0e00000302000025040000,Victrix Pro Fight Stick for PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Mac OS X,", "030000006f0e00000702000003060000,Victrix Pro Fight Stick for PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Mac OS X,", "03000000791d00000103000009010000,Wii Classic Controller,a:b2,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,guide:b10,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Mac OS X,", "050000005769696d6f74652028303000,Wii Remote,a:b4,b:b5,back:b7,dpdown:b3,dpleft:b0,dpright:b1,dpup:b2,guide:b8,leftshoulder:b11,lefttrigger:b12,leftx:a0,lefty:a1,start:b6,x:b10,y:b9,platform:Mac OS X,", "050000005769696d6f74652028313800,Wii U Pro Controller,a:b16,b:b15,back:b7,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b8,leftshoulder:b19,leftstick:b23,lefttrigger:b21,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b24,righttrigger:b22,rightx:a2,righty:a3,start:b6,x:b18,y:b17,platform:Mac OS X,", "030000005e0400008e02000000000000,X360 Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,", "030000006f0e00000104000000000000,Xbox 360 Wired Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,", "03000000c6240000045d000000000000,Xbox 360 Wired Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,", "030000005e0400000a0b000000000000,Xbox Adaptive Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,", "030000005e040000050b000003090000,Xbox Elite Wireless Controller Series 2,a:b0,b:b1,back:b31,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b53,leftshoulder:b6,leftstick:b13,lefttrigger:a6,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,", "03000000c62400003a54000000000000,Xbox One PowerA Wired Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,", "030000005e040000d102000000000000,Xbox One Wired Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,", "030000005e040000dd02000000000000,Xbox One Wired Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,", "030000005e040000e302000000000000,Xbox One Wired Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,", "030000005e040000130b000001050000,Xbox Series Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,", "030000005e040000130b000005050000,Xbox Series Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,", "030000005e040000e002000000000000,Xbox Wireless Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Mac OS X,", "030000005e040000e002000003090000,Xbox Wireless Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Mac OS X,", "030000005e040000ea02000000000000,Xbox Wireless Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,", "030000005e040000fd02000003090000,Xbox Wireless Controller,a:b0,b:b1,back:b16,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b15,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,", "03000000172700004431000029010000,XiaoMi Game Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b15,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a6,rightx:a2,righty:a5,start:b11,x:b3,y:b4,platform:Mac OS X,", "03000000120c0000100e000000010000,ZEROPLUS P4 Gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,", "03000000120c0000101e000000010000,ZEROPLUS P4 Wired Gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,", #endif // GLFW_BUILD_COCOA_MAPPINGS #if defined(GLFW_BUILD_LINUX_MAPPINGS) "03000000c82d00000090000011010000,8BitDo FC30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,", "05000000c82d00001038000000010000,8Bitdo FC30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,", "05000000c82d00005106000000010000,8BitDo M30,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b8,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,start:b11,x:b3,y:b4,platform:Linux,", "03000000c82d00001590000011010000,8BitDo N30 Pro 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,", "05000000c82d00006528000000010000,8BitDo N30 Pro 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,", "03000000c82d00000310000011010000,8BitDo NES30,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b7,lefttrigger:b6,rightshoulder:b9,righttrigger:b8,start:b11,x:b3,y:b4,platform:Linux,", "05000000c82d00008010000000010000,8BitDo NES30,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b7,lefttrigger:b6,rightshoulder:b9,righttrigger:b8,start:b11,x:b3,y:b4,platform:Linux,", "03000000022000000090000011010000,8Bitdo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,", "05000000203800000900000000010000,8Bitdo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,", "05000000c82d00002038000000010000,8Bitdo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,", "03000000c82d00000190000011010000,8Bitdo NES30 Pro 8Bitdo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,", "05000000c82d00000060000000010000,8BitDo SF30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,", "05000000c82d00000061000000010000,8Bitdo SF30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,", "03000000c82d000021ab000010010000,8BitDo SFC30,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Linux,", "030000003512000012ab000010010000,8Bitdo SFC30 GamePad,a:b2,b:b1,back:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b7,x:b3,y:b0,platform:Linux,", "05000000102800000900000000010000,8Bitdo SFC30 GamePad,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Linux,", "05000000c82d00003028000000010000,8Bitdo SFC30 GamePad,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Linux,", "03000000c82d00000160000000000000,8BitDo SN30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Linux,", "03000000c82d00000160000011010000,8BitDo SN30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,", "03000000c82d00000161000000000000,8BitDo SN30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Linux,", "03000000c82d00001290000011010000,8BitDo SN30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Linux,", "05000000c82d00000161000000010000,8BitDo SN30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,", "05000000c82d00006228000000010000,8BitDo SN30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,", "03000000c82d00000260000011010000,8BitDo SN30 Pro+,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,", "05000000c82d00000261000000010000,8BitDo SN30 Pro+,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,", "05000000202800000900000000010000,8BitDo SNES30 Gamepad,a:b1,b:b0,back:b10,dpdown:b122,dpleft:b119,dpright:b120,dpup:b117,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Linux,", "03000000c82d00000031000011010000,8BitDo Wireless Adapter (DInput),a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,", "030000005e0400008e02000020010000,8BitDo Wireless Adapter (XInput),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", "03000000c82d00001890000011010000,8BitDo Zero 2,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Linux,", "05000000c82d00003032000000010000,8BitDo Zero 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,", "050000005e040000e002000030110000,8BitDo Zero 2 (XInput),a:b0,b:b1,back:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b7,x:b2,y:b3,platform:Linux,", "05000000a00500003232000001000000,8Bitdo Zero GamePad,a:b0,b:b1,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Linux,", "05000000a00500003232000008010000,8Bitdo Zero GamePad,a:b0,b:b1,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Linux,", "03000000c01100000355000011010000,ACRUX USB GAME PAD,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", "030000006f0e00001302000000010000,Afterglow,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", "030000006f0e00003901000020060000,Afterglow Controller for Xbox One,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", "030000006f0e00003901000000430000,Afterglow Prismatic Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", "030000006f0e00003901000013020000,Afterglow Prismatic Wired Controller 048-007-NA,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", "03000000100000008200000011010000,Akishop Customs PS360+ v1.66,a:b1,b:b2,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux,", "030000007c1800000006000010010000,Alienware Dual Compatible Game Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b3,platform:Linux,", "05000000491900000204000021000000,Amazon Fire Game Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b17,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b12,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,", "03000000491900001904000011010000,Amazon Luna Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b9,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b7,x:b2,y:b3,platform:Linux,", "05000000710100001904000000010000,Amazon Luna Controller,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b11,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Linux,", "03000000790000003018000011010000,Arcade Fightstick F300,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux,", "03000000a30c00002700000011010000,Astro City Mini,a:b2,b:b1,back:b8,leftx:a0,lefty:a1,rightshoulder:b4,righttrigger:b5,start:b9,x:b3,y:b0,platform:Linux,", "03000000a30c00002800000011010000,Astro City Mini,a:b2,b:b1,back:b8,leftx:a0,lefty:a1,rightshoulder:b4,righttrigger:b5,start:b9,x:b3,y:b0,platform:Linux,", "05000000050b00000045000031000000,ASUS Gamepad,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b6,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b10,x:b2,y:b3,platform:Linux,", "05000000050b00000045000040000000,ASUS Gamepad,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b6,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b10,x:b2,y:b3,platform:Linux,", "03000000503200000110000000000000,Atari Classic Controller,a:b0,back:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b4,start:b3,x:b1,platform:Linux,", "05000000503200000110000000000000,Atari Classic Controller,a:b0,back:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b4,start:b3,x:b1,platform:Linux,", "03000000503200000210000000000000,Atari Game Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b2,platform:Linux,", "05000000503200000210000000000000,Atari Game Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b2,platform:Linux,", "03000000120c00000500000010010000,AxisPad,a:b2,b:b3,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a3,righty:a2,start:b11,x:b0,y:b1,platform:Linux,", "03000000ef0500000300000000010000,AxisPad,a:b2,b:b3,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a3,righty:a2,start:b11,x:b0,y:b1,platform:Linux,", "03000000c62400001b89000011010000,BDA MOGA XP5-X Plus,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,", "03000000d62000002a79000011010000,BDA PS4 Fightpad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,", "03000000c21100000791000011010000,Be1 GC101 Controller 1.03 mode,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,", "03000000c31100000791000011010000,Be1 GC101 GAMEPAD 1.03 mode,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,", "030000005e0400008e02000003030000,Be1 GC101 Xbox 360 Controller mode,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", "05000000bc2000000055000001000000,BETOP AX1 BFM,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,", "03000000666600006706000000010000,boom PSX to PC Converter,a:b2,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,leftshoulder:b6,leftstick:b9,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b10,righttrigger:b5,rightx:a2,righty:a3,start:b11,x:b3,y:b0,platform:Linux,", "03000000120c0000200e000011010000,Brook Mars,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,", "03000000120c0000210e000011010000,Brook Mars,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", "03000000120c0000f70e000011010000,Brook Universal Fighting Board,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,rightshoulder:b5,rightstick:b11,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux,", "03000000ffff0000ffff000000010000,Chinese-made Xbox Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b2,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b4,platform:Linux,", "03000000e82000006058000001010000,Cideko AK08b,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,", "030000000b0400003365000000010000,Competition Pro,a:b0,b:b1,back:b2,leftx:a0,lefty:a1,start:b3,platform:Linux,", "03000000260900008888000000010000,Cyber Gadget GameCube Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:a5,rightx:a2,righty:a3~,start:b7,x:b2,y:b3,platform:Linux,", "03000000a306000022f6000011010000,Cyborg V.3 Rumble Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:+a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:-a3,rightx:a2,righty:a4,start:b9,x:b0,y:b3,platform:Linux,", "03000000b40400000a01000000010000,CYPRESS USB Gamepad,a:b0,b:b1,back:b5,guide:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b8,x:b3,y:b4,platform:Linux,", "03000000790000000600000010010000,DragonRise Inc. Generic USB Joystick,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b3,y:b0,platform:Linux,", "030000004f04000004b3000010010000,Dual Power 2,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,platform:Linux,", "030000006f0e00003001000001010000,EA Sports PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", "03000000341a000005f7000010010000,GameCube {HuiJia USB box},a:b1,b:b2,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b0,y:b3,platform:Linux,", "03000000bc2000000055000011010000,GameSir G3w,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,", "0500000047532047616d657061640000,GameStop Gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,", "030000006f0e00000104000000010000,Gamestop Logic3 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", "030000008f0e00000800000010010000,Gasia Co. Ltd PS(R) Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,", "030000006f0e00001304000000010000,Generic X-Box pad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", "03000000451300000010000010010000,Genius Maxfire Grandias 12,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,", "03000000f0250000c183000010010000,Goodbetterbest Ltd USB Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", "0300000079000000d418000000010000,GPD Win 2 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", "030000007d0400000540000000010000,Gravis Eliminator GamePad Pro,a:b1,b:b2,back:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux,", "03000000280400000140000000010000,Gravis GamePad Pro USB ,a:b1,b:b2,back:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux,", "030000008f0e00000610000000010000,GreenAsia Electronics 4Axes 12Keys GamePad ,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b9,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b10,righttrigger:b5,rightx:a3,righty:a2,start:b11,x:b3,y:b0,platform:Linux,", "030000008f0e00001200000010010000,GreenAsia Inc. USB Joystick,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b2,y:b3,platform:Linux,", "0500000047532067616d657061640000,GS gamepad,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,", "03000000f0250000c383000010010000,GT VX2,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,", "06000000adde0000efbe000002010000,Hidromancer Game Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", "03000000d81400000862000011010000,HitBox (PS3/PC) Analog Mode,a:b1,b:b2,back:b8,guide:b9,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b12,x:b0,y:b3,platform:Linux,", "03000000c9110000f055000011010000,HJC Game GAMEPAD,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,", "03000000632500002605000010010000,HJD-X,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,", "030000000d0f00000d00000000010000,hori,a:b0,b:b6,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b3,leftx:b4,lefty:b5,rightshoulder:b7,start:b9,x:b1,y:b2,platform:Linux,", "030000000d0f00001000000011010000,HORI CO. LTD. FIGHTING STICK 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux,", "030000000d0f0000c100000011010000,HORI CO. LTD. HORIPAD S,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b13,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", "030000000d0f00006a00000011010000,HORI CO. LTD. Real Arcade Pro.4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,", "030000000d0f00006b00000011010000,HORI CO. LTD. Real Arcade Pro.4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", "030000000d0f00002200000011010000,HORI CO. LTD. REAL ARCADE Pro.V3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux,", "030000000d0f00008500000010010000,HORI Fighting Commander,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", "030000000d0f00008600000002010000,Hori Fighting Commander,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,", "030000000d0f00005f00000011010000,Hori Fighting Commander 4 (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", "030000000d0f00005e00000011010000,Hori Fighting Commander 4 (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,", "03000000ad1b000001f5000033050000,Hori Pad EX Turbo 2,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", "030000000d0f00009200000011010000,Hori Pokken Tournament DX Pro Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux,", "030000000d0f0000aa00000011010000,HORI Real Arcade Pro,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,", "030000000d0f0000d800000072056800,HORI Real Arcade Pro S,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Linux,", "030000000d0f00001600000000010000,Hori Real Arcade Pro.EX-SE (Xbox 360),a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b2,y:b3,platform:Linux,", "030000000d0f00006e00000011010000,HORIPAD 4 (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", "030000000d0f00006600000011010000,HORIPAD 4 (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,", "030000000d0f0000ee00000011010000,HORIPAD mini4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,", "030000000d0f00006700000001010000,HORIPAD ONE,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", "030000008f0e00001330000010010000,HuiJia SNES Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b9,x:b3,y:b0,platform:Linux,", "03000000242e00008816000001010000,Hyperkin X91,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", "03000000830500006020000010010000,iBuffalo SNES Controller,a:b1,b:b0,back:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b7,x:b3,y:b2,platform:Linux,", "050000006964726f69643a636f6e0000,idroid:con,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", "03000000b50700001503000010010000,impact,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Linux,", "03000000d80400008200000003000000,IMS PCU#0 Gamepad Interface,a:b1,b:b0,back:b4,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,start:b5,x:b3,y:b2,platform:Linux,", "03000000fd0500000030000000010000,InterAct GoPad I-73000 (Fighting Game Layout),a:b3,b:b4,back:b6,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:b5,start:b7,x:b0,y:b1,platform:Linux,", "0500000049190000020400001b010000,Ipega PG-9069 - Bluetooth Gamepad,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b161,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,", "03000000632500007505000011010000,Ipega PG-9099 - Bluetooth Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,", "030000006e0500000320000010010000,JC-U3613M - DirectInput Mode,a:b2,b:b3,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3,start:b11,x:b0,y:b1,platform:Linux,", "03000000300f00001001000010010000,Jess Tech Dual Analog Rumble Pad,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Linux,", "03000000300f00000b01000010010000,Jess Tech GGE909 PC Recoil Pad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Linux,", "03000000ba2200002010000001010000,Jess Technology USB Game Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Linux,", "030000007e0500000620000001000000,Joy-Con (L),+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b0,b:b1,back:b13,leftshoulder:b4,leftstick:b10,rightshoulder:b5,start:b8,x:b2,y:b3,platform:Linux,", "050000007e0500000620000001000000,Joy-Con (L),+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b0,b:b1,back:b13,leftshoulder:b4,leftstick:b10,rightshoulder:b5,start:b8,x:b2,y:b3,platform:Linux,", "030000007e0500000720000001000000,Joy-Con (R),+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b0,b:b1,back:b12,leftshoulder:b4,leftstick:b11,rightshoulder:b5,start:b9,x:b2,y:b3,platform:Linux,", "050000007e0500000720000001000000,Joy-Con (R),+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b0,b:b1,back:b12,leftshoulder:b4,leftstick:b11,rightshoulder:b5,start:b9,x:b2,y:b3,platform:Linux,", "03000000bd12000003c0000010010000,Joypad Alpha Shock,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", "03000000242f00002d00000011010000,JYS Wireless Adapter,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,", "03000000242f00008a00000011010000,JYS Wireless Adapter,a:b1,b:b4,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b0,y:b3,platform:Linux,", "030000006f0e00000103000000020000,Logic3 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", "030000006d040000d1ca000000000000,Logitech ChillStream,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", "030000006d04000019c2000010010000,Logitech Cordless RumblePad 2,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", "030000006d04000016c2000010010000,Logitech Dual Action,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", "030000006d04000016c2000011010000,Logitech Dual Action,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", "030000006d0400001dc2000014400000,Logitech F310 Gamepad (XInput),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", "030000006d0400001ec2000019200000,Logitech F510 Gamepad (XInput),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", "030000006d0400001ec2000020200000,Logitech F510 Gamepad (XInput),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", "030000006d04000019c2000011010000,Logitech F710 Gamepad (DInput),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", "030000006d0400001fc2000005030000,Logitech F710 Gamepad (XInput),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", "030000006d0400000ac2000010010000,Logitech Inc. WingMan RumblePad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b2,rightx:a3,righty:a4,x:b3,y:b4,platform:Linux,", "030000006d04000018c2000010010000,Logitech RumblePad 2,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", "030000006d04000011c2000010010000,Logitech WingMan Cordless RumblePad,a:b0,b:b1,back:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b6,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b10,rightx:a3,righty:a4,start:b8,x:b3,y:b4,platform:Linux,", "050000004d4f435554452d3035305800,M54-PC,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,", "05000000380700006652000025010000,Mad Catz C.T.R.L.R ,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", "03000000380700005032000011010000,Mad Catz FightPad PRO (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", "03000000380700005082000011010000,Mad Catz FightPad PRO (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,", "03000000ad1b00002ef0000090040000,Mad Catz Fightpad SFxT,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,start:b7,x:b2,y:b3,platform:Linux,", "03000000380700008034000011010000,Mad Catz fightstick (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", "03000000380700008084000011010000,Mad Catz fightstick (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,", "03000000380700008433000011010000,Mad Catz FightStick TE S+ (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", "03000000380700008483000011010000,Mad Catz FightStick TE S+ (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,", "03000000380700001647000010040000,Mad Catz Wired Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", "03000000380700003847000090040000,Mad Catz Wired Xbox 360 Controller (SFIV),a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,", "03000000ad1b000016f0000090040000,Mad Catz Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", "03000000380700001888000010010000,MadCatz PC USB Wired Stick 8818,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", "03000000380700003888000010010000,MadCatz PC USB Wired Stick 8838,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:a0,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", "03000000242f0000f700000001010000,Magic-S Pro,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", "03000000120c00000500000000010000,Manta Dualshock 2,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b2,y:b3,platform:Linux,", "03000000790000004418000010010000,Mayflash GameCube Controller,a:b1,b:b0,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b2,y:b3,platform:Linux,", "03000000790000004318000010010000,Mayflash GameCube Controller Adapter,a:b1,b:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b0,y:b3,platform:Linux,", "03000000242f00007300000011010000,Mayflash Magic NS,a:b1,b:b4,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b0,y:b3,platform:Linux,", "0300000079000000d218000011010000,Mayflash Magic NS,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", "03000000d620000010a7000011010000,Mayflash Magic NS,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", "0300000025090000e803000001010000,Mayflash Wii Classic Controller,a:b1,b:b0,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:a4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:a5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b2,platform:Linux,", "03000000780000000600000010010000,Microntek USB Joystick,a:b2,b:b1,back:b8,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,start:b9,x:b3,y:b0,platform:Linux,", "030000005e0400000e00000000010000,Microsoft SideWinder,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,rightshoulder:b7,start:b8,x:b3,y:b4,platform:Linux,", "030000005e0400008e02000004010000,Microsoft X-Box 360 pad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", "030000005e0400008e02000062230000,Microsoft X-Box 360 pad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", "050000005e040000050b000003090000,Microsoft X-Box One Elite 2 pad,a:b0,b:b1,back:b17,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a6,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,", "030000005e040000e302000003020000,Microsoft X-Box One Elite pad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", "030000005e040000d102000001010000,Microsoft X-Box One pad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", "030000005e040000dd02000003020000,Microsoft X-Box One pad (Firmware 2015),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", "030000005e040000d102000003020000,Microsoft X-Box One pad v2,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", "030000005e0400008502000000010000,Microsoft X-Box pad (Japan),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b2,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b4,platform:Linux,", "030000005e0400008902000021010000,Microsoft X-Box pad v2 (US),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b2,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b4,platform:Linux,", "030000005e040000000b000008040000,Microsoft Xbox One Elite 2 pad - Wired,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", "030000005e040000ea02000008040000,Microsoft Xbox One S pad - Wired,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", "03000000c62400001a53000000010000,Mini PE,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", "03000000030000000300000002000000,Miroof,a:b1,b:b0,back:b6,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b7,x:b3,y:b2,platform:Linux,", "05000000d6200000e589000001000000,Moga 2 HID,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Linux,", "05000000d6200000ad0d000001000000,Moga Pro,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Linux,", "05000000d62000007162000001000000,Moga Pro 2 HID,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Linux,", "03000000c62400002b89000011010000,MOGA XP5-A Plus,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,", "05000000c62400002a89000000010000,MOGA XP5-A Plus,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b22,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,", "05000000c62400001a89000000010000,MOGA XP5-X Plus,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,", "03000000250900006688000000010000,MP-8866 Super Dual Box,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Linux,", "030000006b140000010c000010010000,NACON GC-400ES,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,", "030000000d0f00000900000010010000,Natec Genesis P44,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", "03000000790000004518000010010000,NEXILUX GAMECUBE Controller Adapter,a:b1,b:b0,x:b2,y:b3,start:b9,rightshoulder:b7,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a5,righty:a2,lefttrigger:a3,righttrigger:a4,platform:Linux,", "030000001008000001e5000010010000,NEXT SNES Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,righttrigger:b6,start:b9,x:b3,y:b0,platform:Linux,", "060000007e0500003713000000000000,Nintendo 3DS,a:b0,b:b1,back:b8,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,rightx:a2,righty:a3,start:b9,x:b3,y:b2,platform:Linux,", "060000007e0500000820000000000000,Nintendo Combined Joy-Cons (joycond),a:b0,b:b1,back:b9,dpdown:b15,dpleft:b16,dpright:b17,dpup:b14,guide:b11,leftshoulder:b5,leftstick:b12,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b13,righttrigger:b8,rightx:a2,righty:a3,start:b10,x:b3,y:b2,platform:Linux,", "030000007e0500003703000000016800,Nintendo GameCube Controller,a:b0,b:b2,dpdown:b6,dpleft:b4,dpright:b5,dpup:b7,lefttrigger:a4,leftx:a0,lefty:a1~,rightshoulder:b9,righttrigger:a5,rightx:a2,righty:a3~,start:b8,x:b1,y:b3,platform:Linux,", "03000000790000004618000010010000,Nintendo GameCube Controller Adapter,a:b1,b:b0,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,rightx:a5~,righty:a2~,start:b9,x:b2,y:b3,platform:Linux,", "050000007e0500000620000001800000,Nintendo Switch Left Joy-Con,a:b9,b:b8,back:b5,leftshoulder:b2,leftstick:b6,leftx:a1,lefty:a0~,rightshoulder:b4,start:b0,x:b7,y:b10,platform:Linux,", "030000007e0500000920000011810000,Nintendo Switch Pro Controller,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b11,leftshoulder:b5,leftstick:b12,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b13,righttrigger:b8,rightx:a2,righty:a3,start:b10,x:b3,y:b2,platform:Linux,", "050000007e0500000920000001000000,Nintendo Switch Pro Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,", "050000007e0500000920000001800000,Nintendo Switch Pro Controller,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b11,leftshoulder:b5,leftstick:b12,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b13,righttrigger:b8,rightx:a2,righty:a3,start:b10,x:b3,y:b2,platform:Linux,", "050000007e0500000720000001800000,Nintendo Switch Right Joy-Con,a:b1,b:b2,back:b9,leftshoulder:b4,leftstick:b10,leftx:a1~,lefty:a0~,rightshoulder:b6,start:b8,x:b0,y:b3,platform:Linux,", "050000007e0500001720000001000000,Nintendo Switch SNES Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,rightshoulder:b5,start:b9,x:b2,y:b3,platform:Linux,", "050000007e0500003003000001000000,Nintendo Wii Remote Pro Controller,a:b0,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b16,dpup:b13,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b2,platform:Linux,", "05000000010000000100000003000000,Nintendo Wiimote,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,", "030000000d0500000308000010010000,Nostromo n45 Dual Analog Gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b4,leftstick:b12,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b10,x:b2,y:b3,platform:Linux,", "03000000550900001072000011010000,NVIDIA Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b13,leftshoulder:b4,leftstick:b8,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Linux,", "03000000550900001472000011010000,NVIDIA Controller v01.04,a:b0,b:b1,back:b14,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b16,leftshoulder:b4,leftstick:b7,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a5,start:b6,x:b2,y:b3,platform:Linux,", "05000000550900001472000001000000,NVIDIA Controller v01.04,a:b0,b:b1,back:b14,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b16,leftshoulder:b4,leftstick:b7,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a5,start:b6,x:b2,y:b3,platform:Linux,", "03000000451300000830000010010000,NYKO CORE,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,", "19000000010000000100000001010000,odroidgo2_joypad,a:b1,b:b0,dpdown:b7,dpleft:b8,dpright:b9,dpup:b6,guide:b10,leftshoulder:b4,leftstick:b12,lefttrigger:b11,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b13,righttrigger:b14,start:b15,x:b2,y:b3,platform:Linux,", "19000000010000000200000011000000,odroidgo2_joypad_v11,a:b1,b:b0,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,guide:b12,leftshoulder:b4,leftstick:b14,lefttrigger:b13,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b15,righttrigger:b16,start:b17,x:b2,y:b3,platform:Linux,", "030000005e0400000202000000010000,Old Xbox pad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b2,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b4,platform:Linux,", "03000000c0160000dc27000001010000,OnyxSoft Dual JoyDivision,a:b0,b:b1,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b6,x:b2,y:b3,platform:Linux,", "05000000362800000100000002010000,OUYA Game Controller,a:b0,b:b3,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,guide:b14,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,x:b1,y:b2,platform:Linux,", "05000000362800000100000003010000,OUYA Game Controller,a:b0,b:b3,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,guide:b14,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,x:b1,y:b2,platform:Linux,", "03000000830500005020000010010000,Padix Co. Ltd. Rockfire PSX/USB Bridge,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3,start:b11,x:b2,y:b3,platform:Linux,", "03000000790000001c18000011010000,PC Game Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,", "03000000ff1100003133000010010000,PC Game Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,", "030000006f0e0000b802000001010000,PDP AFTERGLOW Wired Xbox One Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", "030000006f0e0000b802000013020000,PDP AFTERGLOW Wired Xbox One Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", "030000006f0e00006401000001010000,PDP Battlefield One,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", "030000006f0e00008001000011010000,PDP CO. LTD. Faceoff Wired Pro Controller for Nintendo Switch,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", "030000006f0e00003101000000010000,PDP EA Sports Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", "030000006f0e0000c802000012010000,PDP Kingdom Hearts Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", "030000006f0e00008701000011010000,PDP Rock Candy Wired Controller for Nintendo Switch,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b13,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,", "030000006f0e00000901000011010000,PDP Versus Fighting Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux,", "030000006f0e0000a802000023020000,PDP Wired Controller for Xbox One,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,", "030000006f0e00008501000011010000,PDP Wired Fight Pad Pro for Nintendo Switch,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,", "0500000049190000030400001b010000,PG-9099,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,", "05000000491900000204000000000000,PG-9118,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,", "030000004c050000da0c000011010000,Playstation Controller,a:b2,b:b1,back:b8,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,start:b9,x:b3,y:b0,platform:Linux,", "030000004c0500003713000011010000,PlayStation Vita,a:b1,b:b2,back:b8,dpdown:b13,dpleft:b15,dpright:b14,dpup:b12,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,rightx:a3,righty:a4,start:b9,x:b0,y:b3,platform:Linux,", "03000000c62400000053000000010000,PowerA,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", "03000000c62400003a54000001010000,PowerA 1428124-01,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", "03000000d62000006dca000011010000,PowerA Pro Ex,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", "03000000d62000000228000001010000,PowerA Wired Controller for Xbox One,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", "03000000c62400001a58000001010000,PowerA Xbox One Cabled,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", "03000000c62400001a54000001010000,PowerA Xbox One Mini Wired Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", "030000006d040000d2ca000011010000,Precision Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", "03000000ff1100004133000010010000,PS2 Controller,a:b2,b:b1,back:b8,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,start:b9,x:b3,y:b0,platform:Linux,", "03000000341a00003608000011010000,PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", "030000004c0500006802000010010000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Linux,", "030000004c0500006802000010810000,PS3 Controller,a:b0,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b16,dpup:b13,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux,", "030000004c0500006802000011010000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Linux,", "030000004c0500006802000011810000,PS3 Controller,a:b0,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b16,dpup:b13,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux,", "030000006f0e00001402000011010000,PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", "030000008f0e00000300000010010000,PS3 Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,", "050000004c0500006802000000000000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Linux,", "050000004c0500006802000000010000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:a12,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:a13,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Linux,", "050000004c0500006802000000800000,PS3 Controller,a:b0,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b16,dpup:b13,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux,", "050000004c0500006802000000810000,PS3 Controller,a:b0,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b16,dpup:b13,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux,", "05000000504c415953544154494f4e00,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Linux,", "060000004c0500006802000000010000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Linux,", "030000004c050000a00b000011010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,", "030000004c050000a00b000011810000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux,", "030000004c050000c405000011010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,", "030000004c050000c405000011810000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux,", "030000004c050000cc09000000010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,", "030000004c050000cc09000011010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,", "030000004c050000cc09000011810000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux,", "03000000c01100000140000011010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,", "050000004c050000c405000000010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,", "050000004c050000c405000000810000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux,", "050000004c050000c405000001800000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux,", "050000004c050000cc09000000010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,", "050000004c050000cc09000000810000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux,", "050000004c050000cc09000001800000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux,", "030000004c050000e60c000011010000,PS5 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,", "050000004c050000e60c000000010000,PS5 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,", "03000000ff000000cb01000010010000,PSP,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b7,x:b2,y:b3,platform:Linux,", "03000000300f00001211000011010000,QanBa Arcade JoyStick,a:b2,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b5,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b6,start:b9,x:b1,y:b3,platform:Linux,", "030000009b2800004200000001010000,Raphnet Technologies Dual NES to USB v2.0,a:b0,b:b1,back:b2,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,start:b3,platform:Linux,", "030000009b2800003200000001010000,Raphnet Technologies GC/N64 to USB v3.4,a:b0,b:b7,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:b5,rightx:a3,righty:a4,start:b3,x:b1,y:b8,platform:Linux,", "030000009b2800006000000001010000,Raphnet Technologies GC/N64 to USB v3.6,a:b0,b:b7,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:b5,rightx:a3,righty:a4,start:b3,x:b1,y:b8,platform:Linux,", "030000009b2800000300000001010000,raphnet.net 4nes4snes v1.5,a:b0,b:b4,back:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b1,y:b5,platform:Linux,", "030000008916000001fd000024010000,Razer Onza Classic Edition,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", "030000008916000000fd000024010000,Razer Onza Tournament Edition,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", "03000000321500000204000011010000,Razer Panthera (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", "03000000321500000104000011010000,Razer Panthera (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,", "03000000321500000810000011010000,Razer Panthera Evo Arcade Stick for PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b13,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,", "03000000321500000010000011010000,Razer RAIJU,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,", "03000000321500000507000000010000,Razer Raiju Mobile,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b21,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,", "03000000321500000011000011010000,Razer Raion Fightpad for PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,", "030000008916000000fe000024010000,Razer Sabertooth,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", "03000000c6240000045d000024010000,Razer Sabertooth,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", "03000000c6240000045d000025010000,Razer Sabertooth,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", "03000000321500000009000011010000,Razer Serval,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Linux,", "050000003215000000090000163a0000,Razer Serval,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Linux,", "0300000032150000030a000001010000,Razer Wildcat,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", "03000000790000001100000010010000,Retrolink SNES Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Linux,", "0300000081170000990a000001010000,Retronic Adapter,a:b0,leftx:a0,lefty:a1,platform:Linux,", "0300000000f000000300000000010000,RetroPad,a:b1,b:b5,back:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b0,y:b4,platform:Linux,", "030000006b140000010d000011010000,Revolution Pro Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,", "030000006b140000130d000011010000,Revolution Pro Controller 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,", "030000006f0e00001f01000000010000,Rock Candy,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", "030000006f0e00001e01000011010000,Rock Candy PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", "030000006f0e00004601000001010000,Rock Candy Xbox One Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", "03000000a306000023f6000011010000,Saitek Cyborg V.1 Game Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a4,start:b9,x:b0,y:b3,platform:Linux,", "03000000a30600001005000000010000,Saitek P150,a:b0,b:b1,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b7,lefttrigger:b6,rightshoulder:b2,righttrigger:b5,x:b3,y:b4,platform:Linux,", "03000000a30600000701000000010000,Saitek P220,a:b2,b:b3,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,lefttrigger:b7,rightshoulder:b4,righttrigger:b5,x:b0,y:b1,platform:Linux,", "03000000a30600000cff000010010000,Saitek P2500 Force Rumble Pad,a:b2,b:b3,back:b11,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a3,righty:a2,start:b10,x:b0,y:b1,platform:Linux,", "03000000a30600000c04000011010000,Saitek P2900 Wireless Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b12,x:b0,y:b3,platform:Linux,", "03000000300f00001201000010010000,Saitek P380,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Linux,", "03000000a30600000901000000010000,Saitek P880,a:b2,b:b3,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a3,righty:a2,x:b0,y:b1,platform:Linux,", "03000000a30600000b04000000010000,Saitek P990 Dual Analog Pad,a:b1,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b8,x:b0,y:b3,platform:Linux,", "03000000a306000018f5000010010000,Saitek PLC Saitek P3200 Rumble Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b0,y:b3,platform:Linux,", "03000000a306000020f6000011010000,Saitek PS2700 Rumble Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a4,start:b9,x:b0,y:b3,platform:Linux,", "03000000d81d00000e00000010010000,Savior,a:b0,b:b1,back:b8,leftshoulder:b6,leftstick:b10,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b2,rightstick:b11,righttrigger:b3,start:b9,x:b4,y:b5,platform:Linux,", "03000000f025000021c1000010010000,ShanWan Gioteck PS3 Wired Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,", "03000000632500007505000010010000,SHANWAN PS3/PC Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,", "03000000bc2000000055000010010000,ShanWan PS3/PC Wired GamePad,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,", "030000005f140000c501000010010000,SHANWAN Trust Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,", "03000000632500002305000010010000,ShanWan USB Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,", "03000000341a00000908000010010000,SL-6566,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,", "030000004c050000e60c000011810000,Sony DualSense,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux,", "050000004c050000e60c000000810000,Sony DualSense ,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux,", "03000000250900000500000000010000,Sony PS2 pad with SmartJoy adapter,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Linux,", "030000005e0400008e02000073050000,Speedlink TORID Wireless Gamepad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", "030000005e0400008e02000020200000,SpeedLink XEOX Pro Analog Gamepad pad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", "03000000d11800000094000011010000,Stadia Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Linux,", "03000000de2800000112000001000000,Steam Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,paddle1:b11,paddle2:b10,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,platform:Linux,", "03000000de2800000211000001000000,Steam Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,paddle1:b11,paddle2:b10,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,platform:Linux,", "03000000de2800000211000011010000,Steam Controller,a:b2,b:b3,back:b10,dpdown:b18,dpleft:b19,dpright:b20,dpup:b17,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,paddle1:b15,paddle2:b16,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b5,platform:Linux,", "03000000de2800004211000001000000,Steam Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,paddle1:b11,paddle2:b10,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,platform:Linux,", "03000000de2800004211000011010000,Steam Controller,a:b2,b:b3,back:b10,dpdown:b18,dpleft:b19,dpright:b20,dpup:b17,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,paddle1:b15,paddle2:b16,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b5,platform:Linux,", "03000000de280000fc11000001000000,Steam Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", "05000000de2800000212000001000000,Steam Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,paddle1:b11,paddle2:b10,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,platform:Linux,", "05000000de2800000511000001000000,Steam Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,paddle1:b11,paddle2:b10,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,platform:Linux,", "05000000de2800000611000001000000,Steam Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,paddle1:b11,paddle2:b10,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,platform:Linux,", "03000000de280000ff11000001000000,Steam Virtual Gamepad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", "03000000381000003014000075010000,SteelSeries Stratus Duo,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", "03000000381000003114000075010000,SteelSeries Stratus Duo,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", "0500000011010000311400001b010000,SteelSeries Stratus Duo,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b32,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,", "05000000110100001914000009010000,SteelSeries Stratus XL,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,", "03000000ad1b000038f0000090040000,Street Fighter IV FightStick TE,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", "030000003b07000004a1000000010000,Suncom SFX Plus for USB,a:b0,b:b2,back:b7,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b9,righttrigger:b5,start:b8,x:b1,y:b3,platform:Linux,", "03000000666600000488000000010000,Super Joy Box 5 Pro,a:b2,b:b1,back:b9,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Linux,", "0300000000f00000f100000000010000,Super RetroPort,a:b1,b:b5,back:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b0,y:b4,platform:Linux,", "03000000457500002211000010010000,SZMY-POWER CO. LTD. GAMEPAD,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,", "030000008f0e00000d31000010010000,SZMY-POWER CO. LTD. GAMEPAD 3 TURBO,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", "030000008f0e00001431000010010000,SZMY-POWER CO. LTD. PS3 gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", "030000004f04000020b3000010010000,Thrustmaster 2 in 1 DT,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,platform:Linux,", "030000004f04000015b3000010010000,Thrustmaster Dual Analog 4,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,platform:Linux,", "030000004f04000023b3000000010000,Thrustmaster Dual Trigger 3-in-1,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,", "030000004f0400000ed0000011010000,ThrustMaster eSwap PRO Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,", "03000000b50700000399000000010000,Thrustmaster Firestorm Digital 2,a:b2,b:b4,back:b11,leftshoulder:b6,leftstick:b10,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b8,rightstick:b0,righttrigger:b9,start:b1,x:b3,y:b5,platform:Linux,", "030000004f04000003b3000010010000,Thrustmaster Firestorm Dual Analog 2,a:b0,b:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b9,rightx:a2,righty:a3,x:b1,y:b3,platform:Linux,", "030000004f04000000b3000010010000,Thrustmaster Firestorm Dual Power,a:b0,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b11,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b10,x:b1,y:b3,platform:Linux,", "030000004f04000026b3000002040000,Thrustmaster Gamepad GP XID,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", "03000000c6240000025b000002020000,Thrustmaster GPX Gamepad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", "030000004f04000008d0000000010000,Thrustmaster Run N Drive Wireless,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,", "030000004f04000009d0000000010000,Thrustmaster Run N Drive Wireless PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", "030000004f04000007d0000000010000,Thrustmaster T Mini Wireless,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,", "030000004f04000012b3000010010000,Thrustmaster vibrating gamepad,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,platform:Linux,", "03000000bd12000015d0000010010000,Tomee SNES USB Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Linux,", "03000000d814000007cd000011010000,Toodles 2008 Chimp PC/PS3,a:b0,b:b1,back:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b9,x:b3,y:b2,platform:Linux,", "030000005e0400008e02000070050000,Torid,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", "03000000c01100000591000011010000,Torid,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,", "03000000100800000100000010010000,Twin USB PS2 Adapter,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Linux,", "03000000100800000300000010010000,USB Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Linux,", "03000000790000000600000007010000,USB gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b3,y:b0,platform:Linux,", "03000000790000001100000000010000,USB Gamepad1,a:b2,b:b1,back:b8,dpdown:a0,dpleft:a1,dpright:a2,dpup:a4,start:b9,platform:Linux,", "030000006f0e00000302000011010000,Victrix Pro Fight Stick for PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux,", "030000006f0e00000702000011010000,Victrix Pro Fight Stick for PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux,", "05000000ac0500003232000001000000,VR-BOX,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b2,y:b3,platform:Linux,", "03000000791d00000103000010010000,Wii Classic Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,", "050000000d0f0000f600000001000000,Wireless HORIPAD Switch Pro Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,", "030000005e0400008e02000010010000,X360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", "030000005e0400008e02000014010000,X360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", "030000005e0400001907000000010000,X360 Wireless Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", "030000005e0400009102000007010000,X360 Wireless Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", "030000005e040000a102000000010000,X360 Wireless Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", "030000005e040000a102000007010000,X360 Wireless Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", "0000000058626f782033363020576900,Xbox 360 Wireless Controller,a:b0,b:b1,back:b14,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,guide:b7,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b6,x:b2,y:b3,platform:Linux,", "030000005e040000a102000014010000,Xbox 360 Wireless Receiver (XBOX),a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", "0000000058626f782047616d65706100,Xbox Gamepad (userspace driver),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Linux,", "030000005e040000d102000002010000,Xbox One Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", "050000005e040000fd02000030110000,Xbox One Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", "050000005e040000050b000002090000,Xbox One Elite Series 2,a:b0,b:b1,back:b136,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a6,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,", "030000005e040000ea02000000000000,Xbox One Wireless Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", "050000005e040000e002000003090000,Xbox One Wireless Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", "050000005e040000fd02000003090000,Xbox One Wireless Controller,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b16,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,", "030000005e040000ea02000001030000,Xbox One Wireless Controller (Model 1708),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", "030000005e040000120b000001050000,Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", "030000005e040000130b000005050000,Xbox Series Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,", "050000005e040000130b000001050000,Xbox Series Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,", "050000005e040000130b000005050000,Xbox Series Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,", "030000005e040000120b000005050000,XBox Series pad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", "030000005e0400008e02000000010000,xbox360 Wireless EasySMX,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,", "03000000450c00002043000010010000,XEOX Gamepad SL-6556-BK,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,", "03000000ac0500005b05000010010000,Xiaoji Gamesir-G3w,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,", "05000000172700004431000029010000,XiaoMi Game Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b20,leftshoulder:b6,leftstick:b13,lefttrigger:a7,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a6,rightx:a2,righty:a5,start:b11,x:b3,y:b4,platform:Linux,", "03000000c0160000e105000001010000,Xin-Mo Xin-Mo Dual Arcade,a:b4,b:b3,back:b6,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b9,leftshoulder:b2,leftx:a0,lefty:a1,rightshoulder:b5,start:b7,x:b1,y:b0,platform:Linux,", "03000000120c0000100e000011010000,ZEROPLUS P4 Gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,", "03000000120c0000101e000011010000,ZEROPLUS P4 Wired Gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,", #endif // GLFW_BUILD_LINUX_MAPPINGS }; #endif #include #include #include #include #include // Internal key state used for sticky keys #define _GLFW_STICK 3 // Internal constants for gamepad mapping source types #define _GLFW_JOYSTICK_AXIS 1 #define _GLFW_JOYSTICK_BUTTON 2 #define _GLFW_JOYSTICK_HATBIT 3 // Finds a mapping based on joystick GUID // static _GLFWmapping* findMapping(const char* guid) { int i; for (i = 0; i < _glfw.mappingCount; i++) { if (strcmp(_glfw.mappings[i].guid, guid) == 0) return _glfw.mappings + i; } return NULL; } // Checks whether a gamepad mapping element is present in the hardware // static GLFWbool isValidElementForJoystick(const _GLFWmapelement* e, const _GLFWjoystick* js) { if (e->type == _GLFW_JOYSTICK_HATBIT && (e->index >> 4) >= js->hatCount) return GLFW_FALSE; else if (e->type == _GLFW_JOYSTICK_BUTTON && e->index >= js->buttonCount) return GLFW_FALSE; else if (e->type == _GLFW_JOYSTICK_AXIS && e->index >= js->axisCount) return GLFW_FALSE; return GLFW_TRUE; } // Finds a mapping based on joystick GUID and verifies element indices // static _GLFWmapping* findValidMapping(const _GLFWjoystick* js) { _GLFWmapping* mapping = findMapping(js->guid); if (mapping) { int i; for (i = 0; i <= GLFW_GAMEPAD_BUTTON_LAST; i++) { if (!isValidElementForJoystick(mapping->buttons + i, js)) return NULL; } for (i = 0; i <= GLFW_GAMEPAD_AXIS_LAST; i++) { if (!isValidElementForJoystick(mapping->axes + i, js)) return NULL; } } return mapping; } // Parses an SDL_GameControllerDB line and adds it to the mapping list // static GLFWbool parseMapping(_GLFWmapping* mapping, const char* string) { const char* c = string; size_t i, length; struct { const char* name; _GLFWmapelement* element; } fields[] = { { "platform", NULL }, { "a", mapping->buttons + GLFW_GAMEPAD_BUTTON_A }, { "b", mapping->buttons + GLFW_GAMEPAD_BUTTON_B }, { "x", mapping->buttons + GLFW_GAMEPAD_BUTTON_X }, { "y", mapping->buttons + GLFW_GAMEPAD_BUTTON_Y }, { "back", mapping->buttons + GLFW_GAMEPAD_BUTTON_BACK }, { "start", mapping->buttons + GLFW_GAMEPAD_BUTTON_START }, { "guide", mapping->buttons + GLFW_GAMEPAD_BUTTON_GUIDE }, { "leftshoulder", mapping->buttons + GLFW_GAMEPAD_BUTTON_LEFT_BUMPER }, { "rightshoulder", mapping->buttons + GLFW_GAMEPAD_BUTTON_RIGHT_BUMPER }, { "leftstick", mapping->buttons + GLFW_GAMEPAD_BUTTON_LEFT_THUMB }, { "rightstick", mapping->buttons + GLFW_GAMEPAD_BUTTON_RIGHT_THUMB }, { "dpup", mapping->buttons + GLFW_GAMEPAD_BUTTON_DPAD_UP }, { "dpright", mapping->buttons + GLFW_GAMEPAD_BUTTON_DPAD_RIGHT }, { "dpdown", mapping->buttons + GLFW_GAMEPAD_BUTTON_DPAD_DOWN }, { "dpleft", mapping->buttons + GLFW_GAMEPAD_BUTTON_DPAD_LEFT }, { "lefttrigger", mapping->axes + GLFW_GAMEPAD_AXIS_LEFT_TRIGGER }, { "righttrigger", mapping->axes + GLFW_GAMEPAD_AXIS_RIGHT_TRIGGER }, { "leftx", mapping->axes + GLFW_GAMEPAD_AXIS_LEFT_X }, { "lefty", mapping->axes + GLFW_GAMEPAD_AXIS_LEFT_Y }, { "rightx", mapping->axes + GLFW_GAMEPAD_AXIS_RIGHT_X }, { "righty", mapping->axes + GLFW_GAMEPAD_AXIS_RIGHT_Y } }; length = strcspn(c, ","); if (length != 32 || c[length] != ',') { _glfwInputError(GLFW_INVALID_VALUE, NULL); return GLFW_FALSE; } memcpy(mapping->guid, c, length); c += length + 1; length = strcspn(c, ","); if (length >= sizeof(mapping->name) || c[length] != ',') { _glfwInputError(GLFW_INVALID_VALUE, NULL); return GLFW_FALSE; } memcpy(mapping->name, c, length); c += length + 1; while (*c) { // TODO: Implement output modifiers if (*c == '+' || *c == '-') return GLFW_FALSE; for (i = 0; i < sizeof(fields) / sizeof(fields[0]); i++) { length = strlen(fields[i].name); if (strncmp(c, fields[i].name, length) != 0 || c[length] != ':') continue; c += length + 1; if (fields[i].element) { _GLFWmapelement* e = fields[i].element; int8_t minimum = -1; int8_t maximum = 1; if (*c == '+') { minimum = 0; c += 1; } else if (*c == '-') { maximum = 0; c += 1; } if (*c == 'a') e->type = _GLFW_JOYSTICK_AXIS; else if (*c == 'b') e->type = _GLFW_JOYSTICK_BUTTON; else if (*c == 'h') e->type = _GLFW_JOYSTICK_HATBIT; else break; if (e->type == _GLFW_JOYSTICK_HATBIT) { const unsigned long hat = strtoul(c + 1, (char**) &c, 10); const unsigned long bit = strtoul(c + 1, (char**) &c, 10); e->index = (uint8_t) ((hat << 4) | bit); } else e->index = (uint8_t) strtoul(c + 1, (char**) &c, 10); if (e->type == _GLFW_JOYSTICK_AXIS) { e->axisScale = 2 / (maximum - minimum); e->axisOffset = -(maximum + minimum); if (*c == '~') { e->axisScale = -e->axisScale; e->axisOffset = -e->axisOffset; } } } else { length = strlen(_GLFW_PLATFORM_MAPPING_NAME); if (strncmp(c, _GLFW_PLATFORM_MAPPING_NAME, length) != 0) return GLFW_FALSE; } break; } c += strcspn(c, ","); c += strspn(c, ","); } for (i = 0; i < 32; i++) { if (mapping->guid[i] >= 'A' && mapping->guid[i] <= 'F') mapping->guid[i] += 'a' - 'A'; } _glfwPlatformUpdateGamepadGUID(mapping->guid); return GLFW_TRUE; } ////////////////////////////////////////////////////////////////////////// ////// GLFW event API ////// ////////////////////////////////////////////////////////////////////////// // Notifies shared code of a physical key event // void _glfwInputKey(_GLFWwindow* window, int key, int scancode, int action, int mods) { if (key >= 0 && key <= GLFW_KEY_LAST) { GLFWbool repeated = GLFW_FALSE; if (action == GLFW_RELEASE && window->keys[key] == GLFW_RELEASE) return; if (action == GLFW_PRESS && window->keys[key] == GLFW_PRESS) repeated = GLFW_TRUE; if (action == GLFW_RELEASE && window->stickyKeys) window->keys[key] = _GLFW_STICK; else window->keys[key] = (char) action; if (repeated) action = GLFW_REPEAT; } if (!window->lockKeyMods) mods &= ~(GLFW_MOD_CAPS_LOCK | GLFW_MOD_NUM_LOCK); if (window->callbacks.key) window->callbacks.key((GLFWwindow*) window, key, scancode, action, mods); } // Notifies shared code of a Unicode codepoint input event // The 'plain' parameter determines whether to emit a regular character event // void _glfwInputChar(_GLFWwindow* window, uint32_t codepoint, int mods, GLFWbool plain) { if (codepoint < 32 || (codepoint > 126 && codepoint < 160)) return; if (!window->lockKeyMods) mods &= ~(GLFW_MOD_CAPS_LOCK | GLFW_MOD_NUM_LOCK); if (window->callbacks.charmods) window->callbacks.charmods((GLFWwindow*) window, codepoint, mods); if (plain) { if (window->callbacks.character) window->callbacks.character((GLFWwindow*) window, codepoint); } } // Notifies shared code of a scroll event // void _glfwInputScroll(_GLFWwindow* window, double xoffset, double yoffset) { if (window->callbacks.scroll) window->callbacks.scroll((GLFWwindow*) window, xoffset, yoffset); } // Notifies shared code of a mouse button click event // void _glfwInputMouseClick(_GLFWwindow* window, int button, int action, int mods) { if (button < 0 || button > GLFW_MOUSE_BUTTON_LAST) return; if (!window->lockKeyMods) mods &= ~(GLFW_MOD_CAPS_LOCK | GLFW_MOD_NUM_LOCK); if (action == GLFW_RELEASE && window->stickyMouseButtons) window->mouseButtons[button] = _GLFW_STICK; else window->mouseButtons[button] = (char) action; if (window->callbacks.mouseButton) window->callbacks.mouseButton((GLFWwindow*) window, button, action, mods); } // Notifies shared code of a cursor motion event // The position is specified in content area relative screen coordinates // void _glfwInputCursorPos(_GLFWwindow* window, double xpos, double ypos) { if (window->virtualCursorPosX == xpos && window->virtualCursorPosY == ypos) return; window->virtualCursorPosX = xpos; window->virtualCursorPosY = ypos; if (window->callbacks.cursorPos) window->callbacks.cursorPos((GLFWwindow*) window, xpos, ypos); } // Notifies shared code of a cursor enter/leave event // void _glfwInputCursorEnter(_GLFWwindow* window, GLFWbool entered) { if (window->callbacks.cursorEnter) window->callbacks.cursorEnter((GLFWwindow*) window, entered); } // Notifies shared code of files or directories dropped on a window // void _glfwInputDrop(_GLFWwindow* window, int count, const char** paths) { if (window->callbacks.drop) window->callbacks.drop((GLFWwindow*) window, count, paths); } // Notifies shared code of a joystick connection or disconnection // void _glfwInputJoystick(_GLFWjoystick* js, int event) { const int jid = (int) (js - _glfw.joysticks); if (_glfw.callbacks.joystick) _glfw.callbacks.joystick(jid, event); } // Notifies shared code of the new value of a joystick axis // void _glfwInputJoystickAxis(_GLFWjoystick* js, int axis, float value) { js->axes[axis] = value; } // Notifies shared code of the new value of a joystick button // void _glfwInputJoystickButton(_GLFWjoystick* js, int button, char value) { js->buttons[button] = value; } // Notifies shared code of the new value of a joystick hat // void _glfwInputJoystickHat(_GLFWjoystick* js, int hat, char value) { const int base = js->buttonCount + hat * 4; js->buttons[base + 0] = (value & 0x01) ? GLFW_PRESS : GLFW_RELEASE; js->buttons[base + 1] = (value & 0x02) ? GLFW_PRESS : GLFW_RELEASE; js->buttons[base + 2] = (value & 0x04) ? GLFW_PRESS : GLFW_RELEASE; js->buttons[base + 3] = (value & 0x08) ? GLFW_PRESS : GLFW_RELEASE; js->hats[hat] = value; } ////////////////////////////////////////////////////////////////////////// ////// GLFW internal API ////// ////////////////////////////////////////////////////////////////////////// // Adds the built-in set of gamepad mappings // void _glfwInitGamepadMappings(void) { int jid; size_t i; const size_t count = sizeof(_glfwDefaultMappings) / sizeof(char*); _glfw.mappings = calloc(count, sizeof(_GLFWmapping)); for (i = 0; i < count; i++) { if (parseMapping(&_glfw.mappings[_glfw.mappingCount], _glfwDefaultMappings[i])) _glfw.mappingCount++; } for (jid = 0; jid <= GLFW_JOYSTICK_LAST; jid++) { _GLFWjoystick* js = _glfw.joysticks + jid; if (js->present) js->mapping = findValidMapping(js); } } // Returns an available joystick object with arrays and name allocated // _GLFWjoystick* _glfwAllocJoystick(const char* name, const char* guid, int axisCount, int buttonCount, int hatCount) { int jid; _GLFWjoystick* js; for (jid = 0; jid <= GLFW_JOYSTICK_LAST; jid++) { if (!_glfw.joysticks[jid].present) break; } if (jid > GLFW_JOYSTICK_LAST) return NULL; js = _glfw.joysticks + jid; js->present = GLFW_TRUE; js->axes = calloc(axisCount, sizeof(float)); js->buttons = calloc(buttonCount + (size_t) hatCount * 4, 1); js->hats = calloc(hatCount, 1); js->axisCount = axisCount; js->buttonCount = buttonCount; js->hatCount = hatCount; strncpy(js->name, name, sizeof(js->name) - 1); strncpy(js->guid, guid, sizeof(js->guid) - 1); js->mapping = findValidMapping(js); return js; } // Frees arrays and name and flags the joystick object as unused // void _glfwFreeJoystick(_GLFWjoystick* js) { free(js->axes); free(js->buttons); free(js->hats); memset(js, 0, sizeof(_GLFWjoystick)); } // Center the cursor in the content area of the specified window // void _glfwCenterCursorInContentArea(_GLFWwindow* window) { int width, height; _glfwPlatformGetWindowSize(window, &width, &height); _glfwPlatformSetCursorPos(window, width / 2.0, height / 2.0); } ////////////////////////////////////////////////////////////////////////// ////// GLFW public API ////// ////////////////////////////////////////////////////////////////////////// GLFWAPI int glfwGetInputMode(GLFWwindow* handle, int mode) { _GLFWwindow* window = (_GLFWwindow*) handle; assert(window != NULL); _GLFW_REQUIRE_INIT_OR_RETURN(0); switch (mode) { case GLFW_CURSOR: return window->cursorMode; case GLFW_STICKY_KEYS: return window->stickyKeys; case GLFW_STICKY_MOUSE_BUTTONS: return window->stickyMouseButtons; case GLFW_LOCK_KEY_MODS: return window->lockKeyMods; case GLFW_RAW_MOUSE_MOTION: return window->rawMouseMotion; } _glfwInputError(GLFW_INVALID_ENUM, "Invalid input mode 0x%08X", mode); return 0; } GLFWAPI void glfwSetInputMode(GLFWwindow* handle, int mode, int value) { _GLFWwindow* window = (_GLFWwindow*) handle; assert(window != NULL); _GLFW_REQUIRE_INIT(); if (mode == GLFW_CURSOR) { if (value != GLFW_CURSOR_NORMAL && value != GLFW_CURSOR_HIDDEN && value != GLFW_CURSOR_DISABLED) { _glfwInputError(GLFW_INVALID_ENUM, "Invalid cursor mode 0x%08X", value); return; } if (window->cursorMode == value) return; window->cursorMode = value; _glfwPlatformGetCursorPos(window, &window->virtualCursorPosX, &window->virtualCursorPosY); _glfwPlatformSetCursorMode(window, value); } else if (mode == GLFW_STICKY_KEYS) { value = value ? GLFW_TRUE : GLFW_FALSE; if (window->stickyKeys == value) return; if (!value) { int i; // Release all sticky keys for (i = 0; i <= GLFW_KEY_LAST; i++) { if (window->keys[i] == _GLFW_STICK) window->keys[i] = GLFW_RELEASE; } } window->stickyKeys = value; } else if (mode == GLFW_STICKY_MOUSE_BUTTONS) { value = value ? GLFW_TRUE : GLFW_FALSE; if (window->stickyMouseButtons == value) return; if (!value) { int i; // Release all sticky mouse buttons for (i = 0; i <= GLFW_MOUSE_BUTTON_LAST; i++) { if (window->mouseButtons[i] == _GLFW_STICK) window->mouseButtons[i] = GLFW_RELEASE; } } window->stickyMouseButtons = value; } else if (mode == GLFW_LOCK_KEY_MODS) { window->lockKeyMods = value ? GLFW_TRUE : GLFW_FALSE; } else if (mode == GLFW_RAW_MOUSE_MOTION) { if (!_glfwPlatformRawMouseMotionSupported()) { _glfwInputError(GLFW_PLATFORM_ERROR, "Raw mouse motion is not supported on this system"); return; } value = value ? GLFW_TRUE : GLFW_FALSE; if (window->rawMouseMotion == value) return; window->rawMouseMotion = value; _glfwPlatformSetRawMouseMotion(window, value); } else _glfwInputError(GLFW_INVALID_ENUM, "Invalid input mode 0x%08X", mode); } GLFWAPI int glfwRawMouseMotionSupported(void) { _GLFW_REQUIRE_INIT_OR_RETURN(GLFW_FALSE); return _glfwPlatformRawMouseMotionSupported(); } GLFWAPI const char * glfwGetKeys(GLFWwindow* handle) //< @r-lyeh { _GLFWwindow* window = (_GLFWwindow*) handle; assert(window != NULL); _GLFW_REQUIRE_INIT_OR_RETURN(NULL); return window->keys; } GLFWAPI const char* glfwGetKeyName(int key, int scancode) { _GLFW_REQUIRE_INIT_OR_RETURN(NULL); if (key != GLFW_KEY_UNKNOWN) { if (key != GLFW_KEY_KP_EQUAL && (key < GLFW_KEY_KP_0 || key > GLFW_KEY_KP_ADD) && (key < GLFW_KEY_APOSTROPHE || key > GLFW_KEY_WORLD_2)) { return NULL; } scancode = _glfwPlatformGetKeyScancode(key); } return _glfwPlatformGetScancodeName(scancode); } GLFWAPI int glfwGetKeyScancode(int key) { _GLFW_REQUIRE_INIT_OR_RETURN(-1); if (key < GLFW_KEY_SPACE || key > GLFW_KEY_LAST) { _glfwInputError(GLFW_INVALID_ENUM, "Invalid key %i", key); return GLFW_RELEASE; } return _glfwPlatformGetKeyScancode(key); } GLFWAPI int glfwGetKey(GLFWwindow* handle, int key) { _GLFWwindow* window = (_GLFWwindow*) handle; assert(window != NULL); _GLFW_REQUIRE_INIT_OR_RETURN(GLFW_RELEASE); if (key < GLFW_KEY_SPACE || key > GLFW_KEY_LAST) { _glfwInputError(GLFW_INVALID_ENUM, "Invalid key %i", key); return GLFW_RELEASE; } if (window->keys[key] == _GLFW_STICK) { // Sticky mode: release key now window->keys[key] = GLFW_RELEASE; return GLFW_PRESS; } return (int) window->keys[key]; } GLFWAPI int glfwGetMouseButton(GLFWwindow* handle, int button) { _GLFWwindow* window = (_GLFWwindow*) handle; assert(window != NULL); _GLFW_REQUIRE_INIT_OR_RETURN(GLFW_RELEASE); if (button < GLFW_MOUSE_BUTTON_1 || button > GLFW_MOUSE_BUTTON_LAST) { _glfwInputError(GLFW_INVALID_ENUM, "Invalid mouse button %i", button); return GLFW_RELEASE; } if (window->mouseButtons[button] == _GLFW_STICK) { // Sticky mode: release mouse button now window->mouseButtons[button] = GLFW_RELEASE; return GLFW_PRESS; } return (int) window->mouseButtons[button]; } GLFWAPI void glfwGetCursorPos(GLFWwindow* handle, double* xpos, double* ypos) { _GLFWwindow* window = (_GLFWwindow*) handle; assert(window != NULL); if (xpos) *xpos = 0; if (ypos) *ypos = 0; _GLFW_REQUIRE_INIT(); if (window->cursorMode == GLFW_CURSOR_DISABLED) { if (xpos) *xpos = window->virtualCursorPosX; if (ypos) *ypos = window->virtualCursorPosY; } else _glfwPlatformGetCursorPos(window, xpos, ypos); } GLFWAPI void glfwSetCursorPos(GLFWwindow* handle, double xpos, double ypos) { _GLFWwindow* window = (_GLFWwindow*) handle; assert(window != NULL); _GLFW_REQUIRE_INIT(); if (xpos != xpos || xpos < -DBL_MAX || xpos > DBL_MAX || ypos != ypos || ypos < -DBL_MAX || ypos > DBL_MAX) { _glfwInputError(GLFW_INVALID_VALUE, "Invalid cursor position %f %f", xpos, ypos); return; } if (!_glfwPlatformWindowFocused(window)) return; if (window->cursorMode == GLFW_CURSOR_DISABLED) { // Only update the accumulated position if the cursor is disabled window->virtualCursorPosX = xpos; window->virtualCursorPosY = ypos; } else { // Update system cursor position _glfwPlatformSetCursorPos(window, xpos, ypos); } } GLFWAPI GLFWcursor* glfwCreateCursor(const GLFWimage* image, int xhot, int yhot) { _GLFWcursor* cursor; assert(image != NULL); _GLFW_REQUIRE_INIT_OR_RETURN(NULL); cursor = calloc(1, sizeof(_GLFWcursor)); cursor->next = _glfw.cursorListHead; _glfw.cursorListHead = cursor; if (!_glfwPlatformCreateCursor(cursor, image, xhot, yhot)) { glfwDestroyCursor((GLFWcursor*) cursor); return NULL; } return (GLFWcursor*) cursor; } GLFWAPI GLFWcursor* glfwCreateStandardCursor(int shape) { _GLFWcursor* cursor; _GLFW_REQUIRE_INIT_OR_RETURN(NULL); if (shape != GLFW_ARROW_CURSOR && shape != GLFW_IBEAM_CURSOR && shape != GLFW_CROSSHAIR_CURSOR && shape != GLFW_HAND_CURSOR && shape != GLFW_HRESIZE_CURSOR && shape != GLFW_VRESIZE_CURSOR) { _glfwInputError(GLFW_INVALID_ENUM, "Invalid standard cursor 0x%08X", shape); return NULL; } cursor = calloc(1, sizeof(_GLFWcursor)); cursor->next = _glfw.cursorListHead; _glfw.cursorListHead = cursor; if (!_glfwPlatformCreateStandardCursor(cursor, shape)) { glfwDestroyCursor((GLFWcursor*) cursor); return NULL; } return (GLFWcursor*) cursor; } GLFWAPI void glfwDestroyCursor(GLFWcursor* handle) { _GLFWcursor* cursor = (_GLFWcursor*) handle; _GLFW_REQUIRE_INIT(); if (cursor == NULL) return; // Make sure the cursor is not being used by any window { _GLFWwindow* window; for (window = _glfw.windowListHead; window; window = window->next) { if (window->cursor == cursor) glfwSetCursor((GLFWwindow*) window, NULL); } } _glfwPlatformDestroyCursor(cursor); // Unlink cursor from global linked list { _GLFWcursor** prev = &_glfw.cursorListHead; while (*prev != cursor) prev = &((*prev)->next); *prev = cursor->next; } free(cursor); } GLFWAPI void glfwSetCursor(GLFWwindow* windowHandle, GLFWcursor* cursorHandle) { _GLFWwindow* window = (_GLFWwindow*) windowHandle; _GLFWcursor* cursor = (_GLFWcursor*) cursorHandle; assert(window != NULL); _GLFW_REQUIRE_INIT(); window->cursor = cursor; _glfwPlatformSetCursor(window, cursor); } GLFWAPI GLFWkeyfun glfwSetKeyCallback(GLFWwindow* handle, GLFWkeyfun cbfun) { _GLFWwindow* window = (_GLFWwindow*) handle; assert(window != NULL); _GLFW_REQUIRE_INIT_OR_RETURN(NULL); _GLFW_SWAP_POINTERS(window->callbacks.key, cbfun); return cbfun; } GLFWAPI GLFWcharfun glfwSetCharCallback(GLFWwindow* handle, GLFWcharfun cbfun) { _GLFWwindow* window = (_GLFWwindow*) handle; assert(window != NULL); _GLFW_REQUIRE_INIT_OR_RETURN(NULL); _GLFW_SWAP_POINTERS(window->callbacks.character, cbfun); return cbfun; } GLFWAPI GLFWcharmodsfun glfwSetCharModsCallback(GLFWwindow* handle, GLFWcharmodsfun cbfun) { _GLFWwindow* window = (_GLFWwindow*) handle; assert(window != NULL); _GLFW_REQUIRE_INIT_OR_RETURN(NULL); _GLFW_SWAP_POINTERS(window->callbacks.charmods, cbfun); return cbfun; } GLFWAPI GLFWmousebuttonfun glfwSetMouseButtonCallback(GLFWwindow* handle, GLFWmousebuttonfun cbfun) { _GLFWwindow* window = (_GLFWwindow*) handle; assert(window != NULL); _GLFW_REQUIRE_INIT_OR_RETURN(NULL); _GLFW_SWAP_POINTERS(window->callbacks.mouseButton, cbfun); return cbfun; } GLFWAPI GLFWcursorposfun glfwSetCursorPosCallback(GLFWwindow* handle, GLFWcursorposfun cbfun) { _GLFWwindow* window = (_GLFWwindow*) handle; assert(window != NULL); _GLFW_REQUIRE_INIT_OR_RETURN(NULL); _GLFW_SWAP_POINTERS(window->callbacks.cursorPos, cbfun); return cbfun; } GLFWAPI GLFWcursorenterfun glfwSetCursorEnterCallback(GLFWwindow* handle, GLFWcursorenterfun cbfun) { _GLFWwindow* window = (_GLFWwindow*) handle; assert(window != NULL); _GLFW_REQUIRE_INIT_OR_RETURN(NULL); _GLFW_SWAP_POINTERS(window->callbacks.cursorEnter, cbfun); return cbfun; } GLFWAPI GLFWscrollfun glfwSetScrollCallback(GLFWwindow* handle, GLFWscrollfun cbfun) { _GLFWwindow* window = (_GLFWwindow*) handle; assert(window != NULL); _GLFW_REQUIRE_INIT_OR_RETURN(NULL); _GLFW_SWAP_POINTERS(window->callbacks.scroll, cbfun); return cbfun; } GLFWAPI GLFWdropfun glfwSetDropCallback(GLFWwindow* handle, GLFWdropfun cbfun) { _GLFWwindow* window = (_GLFWwindow*) handle; assert(window != NULL); _GLFW_REQUIRE_INIT_OR_RETURN(NULL); _GLFW_SWAP_POINTERS(window->callbacks.drop, cbfun); return cbfun; } GLFWAPI int glfwJoystickPresent(int jid) { _GLFWjoystick* js; assert(jid >= GLFW_JOYSTICK_1); assert(jid <= GLFW_JOYSTICK_LAST); _GLFW_REQUIRE_INIT_OR_RETURN(GLFW_FALSE); if (jid < 0 || jid > GLFW_JOYSTICK_LAST) { _glfwInputError(GLFW_INVALID_ENUM, "Invalid joystick ID %i", jid); return GLFW_FALSE; } js = _glfw.joysticks + jid; if (!js->present) return GLFW_FALSE; return _glfwPlatformPollJoystick(js, _GLFW_POLL_PRESENCE); } GLFWAPI const float* glfwGetJoystickAxes(int jid, int* count) { _GLFWjoystick* js; assert(jid >= GLFW_JOYSTICK_1); assert(jid <= GLFW_JOYSTICK_LAST); assert(count != NULL); *count = 0; _GLFW_REQUIRE_INIT_OR_RETURN(NULL); if (jid < 0 || jid > GLFW_JOYSTICK_LAST) { _glfwInputError(GLFW_INVALID_ENUM, "Invalid joystick ID %i", jid); return NULL; } js = _glfw.joysticks + jid; if (!js->present) return NULL; if (!_glfwPlatformPollJoystick(js, _GLFW_POLL_AXES)) return NULL; *count = js->axisCount; return js->axes; } GLFWAPI const unsigned char* glfwGetJoystickButtons(int jid, int* count) { _GLFWjoystick* js; assert(jid >= GLFW_JOYSTICK_1); assert(jid <= GLFW_JOYSTICK_LAST); assert(count != NULL); *count = 0; _GLFW_REQUIRE_INIT_OR_RETURN(NULL); if (jid < 0 || jid > GLFW_JOYSTICK_LAST) { _glfwInputError(GLFW_INVALID_ENUM, "Invalid joystick ID %i", jid); return NULL; } js = _glfw.joysticks + jid; if (!js->present) return NULL; if (!_glfwPlatformPollJoystick(js, _GLFW_POLL_BUTTONS)) return NULL; if (_glfw.hints.init.hatButtons) *count = js->buttonCount + js->hatCount * 4; else *count = js->buttonCount; return js->buttons; } GLFWAPI const unsigned char* glfwGetJoystickHats(int jid, int* count) { _GLFWjoystick* js; assert(jid >= GLFW_JOYSTICK_1); assert(jid <= GLFW_JOYSTICK_LAST); assert(count != NULL); *count = 0; _GLFW_REQUIRE_INIT_OR_RETURN(NULL); if (jid < 0 || jid > GLFW_JOYSTICK_LAST) { _glfwInputError(GLFW_INVALID_ENUM, "Invalid joystick ID %i", jid); return NULL; } js = _glfw.joysticks + jid; if (!js->present) return NULL; if (!_glfwPlatformPollJoystick(js, _GLFW_POLL_BUTTONS)) return NULL; *count = js->hatCount; return js->hats; } GLFWAPI const char* glfwGetJoystickName(int jid) { _GLFWjoystick* js; assert(jid >= GLFW_JOYSTICK_1); assert(jid <= GLFW_JOYSTICK_LAST); _GLFW_REQUIRE_INIT_OR_RETURN(NULL); if (jid < 0 || jid > GLFW_JOYSTICK_LAST) { _glfwInputError(GLFW_INVALID_ENUM, "Invalid joystick ID %i", jid); return NULL; } js = _glfw.joysticks + jid; if (!js->present) return NULL; if (!_glfwPlatformPollJoystick(js, _GLFW_POLL_PRESENCE)) return NULL; return js->name; } GLFWAPI const char* glfwGetJoystickGUID(int jid) { _GLFWjoystick* js; assert(jid >= GLFW_JOYSTICK_1); assert(jid <= GLFW_JOYSTICK_LAST); _GLFW_REQUIRE_INIT_OR_RETURN(NULL); if (jid < 0 || jid > GLFW_JOYSTICK_LAST) { _glfwInputError(GLFW_INVALID_ENUM, "Invalid joystick ID %i", jid); return NULL; } js = _glfw.joysticks + jid; if (!js->present) return NULL; if (!_glfwPlatformPollJoystick(js, _GLFW_POLL_PRESENCE)) return NULL; return js->guid; } GLFWAPI void glfwSetJoystickUserPointer(int jid, void* pointer) { _GLFWjoystick* js; assert(jid >= GLFW_JOYSTICK_1); assert(jid <= GLFW_JOYSTICK_LAST); _GLFW_REQUIRE_INIT(); js = _glfw.joysticks + jid; if (!js->present) return; js->userPointer = pointer; } GLFWAPI void* glfwGetJoystickUserPointer(int jid) { _GLFWjoystick* js; assert(jid >= GLFW_JOYSTICK_1); assert(jid <= GLFW_JOYSTICK_LAST); _GLFW_REQUIRE_INIT_OR_RETURN(NULL); js = _glfw.joysticks + jid; if (!js->present) return NULL; return js->userPointer; } GLFWAPI GLFWjoystickfun glfwSetJoystickCallback(GLFWjoystickfun cbfun) { _GLFW_REQUIRE_INIT_OR_RETURN(NULL); _GLFW_SWAP_POINTERS(_glfw.callbacks.joystick, cbfun); return cbfun; } GLFWAPI int glfwUpdateGamepadMappings(const char* string) { int jid; const char* c = string; assert(string != NULL); _GLFW_REQUIRE_INIT_OR_RETURN(GLFW_FALSE); while (*c) { if ((*c >= '0' && *c <= '9') || (*c >= 'a' && *c <= 'f') || (*c >= 'A' && *c <= 'F')) { char line[1024]; const size_t length = strcspn(c, "\r\n"); if (length < sizeof(line)) { _GLFWmapping mapping = {{0}}; memcpy(line, c, length); line[length] = '\0'; if (parseMapping(&mapping, line)) { _GLFWmapping* previous = findMapping(mapping.guid); if (previous) *previous = mapping; else { _glfw.mappingCount++; _glfw.mappings = realloc(_glfw.mappings, sizeof(_GLFWmapping) * _glfw.mappingCount); _glfw.mappings[_glfw.mappingCount - 1] = mapping; } } } c += length; } else { c += strcspn(c, "\r\n"); c += strspn(c, "\r\n"); } } for (jid = 0; jid <= GLFW_JOYSTICK_LAST; jid++) { _GLFWjoystick* js = _glfw.joysticks + jid; if (js->present) js->mapping = findValidMapping(js); } return GLFW_TRUE; } GLFWAPI int glfwJoystickIsGamepad(int jid) { _GLFWjoystick* js; assert(jid >= GLFW_JOYSTICK_1); assert(jid <= GLFW_JOYSTICK_LAST); _GLFW_REQUIRE_INIT_OR_RETURN(GLFW_FALSE); if (jid < 0 || jid > GLFW_JOYSTICK_LAST) { _glfwInputError(GLFW_INVALID_ENUM, "Invalid joystick ID %i", jid); return GLFW_FALSE; } js = _glfw.joysticks + jid; if (!js->present) return GLFW_FALSE; if (!_glfwPlatformPollJoystick(js, _GLFW_POLL_PRESENCE)) return GLFW_FALSE; return js->mapping != NULL; } GLFWAPI const char* glfwGetGamepadName(int jid) { _GLFWjoystick* js; assert(jid >= GLFW_JOYSTICK_1); assert(jid <= GLFW_JOYSTICK_LAST); _GLFW_REQUIRE_INIT_OR_RETURN(NULL); if (jid < 0 || jid > GLFW_JOYSTICK_LAST) { _glfwInputError(GLFW_INVALID_ENUM, "Invalid joystick ID %i", jid); return NULL; } js = _glfw.joysticks + jid; if (!js->present) return NULL; if (!_glfwPlatformPollJoystick(js, _GLFW_POLL_PRESENCE)) return NULL; if (!js->mapping) return NULL; return js->mapping->name; } GLFWAPI int glfwGetGamepadState(int jid, GLFWgamepadstate* state) { int i; _GLFWjoystick* js; assert(jid >= GLFW_JOYSTICK_1); assert(jid <= GLFW_JOYSTICK_LAST); assert(state != NULL); memset(state, 0, sizeof(GLFWgamepadstate)); _GLFW_REQUIRE_INIT_OR_RETURN(GLFW_FALSE); if (jid < 0 || jid > GLFW_JOYSTICK_LAST) { _glfwInputError(GLFW_INVALID_ENUM, "Invalid joystick ID %i", jid); return GLFW_FALSE; } js = _glfw.joysticks + jid; if (!js->present) return GLFW_FALSE; if (!_glfwPlatformPollJoystick(js, _GLFW_POLL_ALL)) return GLFW_FALSE; if (!js->mapping) return GLFW_FALSE; for (i = 0; i <= GLFW_GAMEPAD_BUTTON_LAST; i++) { const _GLFWmapelement* e = js->mapping->buttons + i; if (e->type == _GLFW_JOYSTICK_AXIS) { const float value = js->axes[e->index] * e->axisScale + e->axisOffset; // HACK: This should be baked into the value transform // TODO: Bake into transform when implementing output modifiers if (e->axisOffset < 0 || (e->axisOffset == 0 && e->axisScale > 0)) { if (value >= 0.f) state->buttons[i] = GLFW_PRESS; } else { if (value <= 0.f) state->buttons[i] = GLFW_PRESS; } } else if (e->type == _GLFW_JOYSTICK_HATBIT) { const unsigned int hat = e->index >> 4; const unsigned int bit = e->index & 0xf; if (js->hats[hat] & bit) state->buttons[i] = GLFW_PRESS; } else if (e->type == _GLFW_JOYSTICK_BUTTON) state->buttons[i] = js->buttons[e->index]; } for (i = 0; i <= GLFW_GAMEPAD_AXIS_LAST; i++) { const _GLFWmapelement* e = js->mapping->axes + i; if (e->type == _GLFW_JOYSTICK_AXIS) { const float value = js->axes[e->index] * e->axisScale + e->axisOffset; state->axes[i] = _glfw_fminf(_glfw_fmaxf(value, -1.f), 1.f); } else if (e->type == _GLFW_JOYSTICK_HATBIT) { const unsigned int hat = e->index >> 4; const unsigned int bit = e->index & 0xf; if (js->hats[hat] & bit) state->axes[i] = 1.f; else state->axes[i] = -1.f; } else if (e->type == _GLFW_JOYSTICK_BUTTON) state->axes[i] = js->buttons[e->index] * 2.f - 1.f; } return GLFW_TRUE; } GLFWAPI void glfwSetClipboardString(GLFWwindow* handle, const char* string) { assert(string != NULL); _GLFW_REQUIRE_INIT(); _glfwPlatformSetClipboardString(string); } GLFWAPI const char* glfwGetClipboardString(GLFWwindow* handle) { _GLFW_REQUIRE_INIT_OR_RETURN(NULL); return _glfwPlatformGetClipboardString(); } GLFWAPI double glfwGetTime(void) { _GLFW_REQUIRE_INIT_OR_RETURN(0.0); return (double) (_glfwPlatformGetTimerValue() - _glfw.timer.offset) / _glfwPlatformGetTimerFrequency(); } GLFWAPI void glfwSetTime(double time) { _GLFW_REQUIRE_INIT(); if (time != time || time < 0.0 || time > 18446744073.0) { _glfwInputError(GLFW_INVALID_VALUE, "Invalid time %f", time); return; } _glfw.timer.offset = _glfwPlatformGetTimerValue() - (uint64_t) (time * _glfwPlatformGetTimerFrequency()); } GLFWAPI uint64_t glfwGetTimerValue(void) { _GLFW_REQUIRE_INIT_OR_RETURN(0); return _glfwPlatformGetTimerValue(); } GLFWAPI uint64_t glfwGetTimerFrequency(void) { _GLFW_REQUIRE_INIT_OR_RETURN(0); return _glfwPlatformGetTimerFrequency(); } #endif #ifndef HEADER_GUARD_MONITOR_C #define HEADER_GUARD_MONITOR_C //======================================================================== // GLFW 3.3.7 - www.glfw.org //------------------------------------------------------------------------ // Copyright (c) 2002-2006 Marcus Geelnard // Copyright (c) 2006-2019 Camilla Löwy // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would // be appreciated but is not required. // // 2. Altered source versions must be plainly marked as such, and must not // be misrepresented as being the original software. // // 3. This notice may not be removed or altered from any source // distribution. // //======================================================================== // Please use C89 style variable declarations in this file because VS 2010 //======================================================================== #include #include #include #include #include #include // Lexically compare video modes, used by qsort // static int compareVideoModes(const void* fp, const void* sp) { const GLFWvidmode* fm = fp; const GLFWvidmode* sm = sp; const int fbpp = fm->redBits + fm->greenBits + fm->blueBits; const int sbpp = sm->redBits + sm->greenBits + sm->blueBits; const int farea = fm->width * fm->height; const int sarea = sm->width * sm->height; // First sort on color bits per pixel if (fbpp != sbpp) return fbpp - sbpp; // Then sort on screen area if (farea != sarea) return farea - sarea; // Then sort on width if (fm->width != sm->width) return fm->width - sm->width; // Lastly sort on refresh rate return fm->refreshRate - sm->refreshRate; } // Retrieves the available modes for the specified monitor // static GLFWbool refreshVideoModes(_GLFWmonitor* monitor) { int modeCount; GLFWvidmode* modes; if (monitor->modes) return GLFW_TRUE; modes = _glfwPlatformGetVideoModes(monitor, &modeCount); if (!modes) return GLFW_FALSE; qsort(modes, modeCount, sizeof(GLFWvidmode), compareVideoModes); free(monitor->modes); monitor->modes = modes; monitor->modeCount = modeCount; return GLFW_TRUE; } ////////////////////////////////////////////////////////////////////////// ////// GLFW event API ////// ////////////////////////////////////////////////////////////////////////// // Notifies shared code of a monitor connection or disconnection // void _glfwInputMonitor(_GLFWmonitor* monitor, int action, int placement) { if (action == GLFW_CONNECTED) { _glfw.monitorCount++; _glfw.monitors = realloc(_glfw.monitors, sizeof(_GLFWmonitor*) * _glfw.monitorCount); if (placement == _GLFW_INSERT_FIRST) { memmove(_glfw.monitors + 1, _glfw.monitors, ((size_t) _glfw.monitorCount - 1) * sizeof(_GLFWmonitor*)); _glfw.monitors[0] = monitor; } else _glfw.monitors[_glfw.monitorCount - 1] = monitor; } else if (action == GLFW_DISCONNECTED) { int i; _GLFWwindow* window; for (window = _glfw.windowListHead; window; window = window->next) { if (window->monitor == monitor) { int width, height, xoff, yoff; _glfwPlatformGetWindowSize(window, &width, &height); _glfwPlatformSetWindowMonitor(window, NULL, 0, 0, width, height, 0); _glfwPlatformGetWindowFrameSize(window, &xoff, &yoff, NULL, NULL); _glfwPlatformSetWindowPos(window, xoff, yoff); } } for (i = 0; i < _glfw.monitorCount; i++) { if (_glfw.monitors[i] == monitor) { _glfw.monitorCount--; memmove(_glfw.monitors + i, _glfw.monitors + i + 1, ((size_t) _glfw.monitorCount - i) * sizeof(_GLFWmonitor*)); break; } } } if (_glfw.callbacks.monitor) _glfw.callbacks.monitor((GLFWmonitor*) monitor, action); if (action == GLFW_DISCONNECTED) _glfwFreeMonitor(monitor); } // Notifies shared code that a full screen window has acquired or released // a monitor // void _glfwInputMonitorWindow(_GLFWmonitor* monitor, _GLFWwindow* window) { monitor->window = window; } ////////////////////////////////////////////////////////////////////////// ////// GLFW internal API ////// ////////////////////////////////////////////////////////////////////////// // Allocates and returns a monitor object with the specified name and dimensions // _GLFWmonitor* _glfwAllocMonitor(const char* name, int widthMM, int heightMM) { _GLFWmonitor* monitor = calloc(1, sizeof(_GLFWmonitor)); monitor->widthMM = widthMM; monitor->heightMM = heightMM; strncpy(monitor->name, name, sizeof(monitor->name) - 1); return monitor; } // Frees a monitor object and any data associated with it // void _glfwFreeMonitor(_GLFWmonitor* monitor) { if (monitor == NULL) return; _glfwPlatformFreeMonitor(monitor); _glfwFreeGammaArrays(&monitor->originalRamp); _glfwFreeGammaArrays(&monitor->currentRamp); free(monitor->modes); free(monitor); } // Allocates red, green and blue value arrays of the specified size // void _glfwAllocGammaArrays(GLFWgammaramp* ramp, unsigned int size) { ramp->red = calloc(size, sizeof(unsigned short)); ramp->green = calloc(size, sizeof(unsigned short)); ramp->blue = calloc(size, sizeof(unsigned short)); ramp->size = size; } // Frees the red, green and blue value arrays and clears the struct // void _glfwFreeGammaArrays(GLFWgammaramp* ramp) { free(ramp->red); free(ramp->green); free(ramp->blue); memset(ramp, 0, sizeof(GLFWgammaramp)); } // Chooses the video mode most closely matching the desired one // const GLFWvidmode* _glfwChooseVideoMode(_GLFWmonitor* monitor, const GLFWvidmode* desired) { int i; unsigned int sizeDiff, leastSizeDiff = UINT_MAX; unsigned int rateDiff, leastRateDiff = UINT_MAX; unsigned int colorDiff, leastColorDiff = UINT_MAX; const GLFWvidmode* current; const GLFWvidmode* closest = NULL; if (!refreshVideoModes(monitor)) return NULL; for (i = 0; i < monitor->modeCount; i++) { current = monitor->modes + i; colorDiff = 0; if (desired->redBits != GLFW_DONT_CARE) colorDiff += abs(current->redBits - desired->redBits); if (desired->greenBits != GLFW_DONT_CARE) colorDiff += abs(current->greenBits - desired->greenBits); if (desired->blueBits != GLFW_DONT_CARE) colorDiff += abs(current->blueBits - desired->blueBits); sizeDiff = abs((current->width - desired->width) * (current->width - desired->width) + (current->height - desired->height) * (current->height - desired->height)); if (desired->refreshRate != GLFW_DONT_CARE) rateDiff = abs(current->refreshRate - desired->refreshRate); else rateDiff = UINT_MAX - current->refreshRate; if ((colorDiff < leastColorDiff) || (colorDiff == leastColorDiff && sizeDiff < leastSizeDiff) || (colorDiff == leastColorDiff && sizeDiff == leastSizeDiff && rateDiff < leastRateDiff)) { closest = current; leastSizeDiff = sizeDiff; leastRateDiff = rateDiff; leastColorDiff = colorDiff; } } return closest; } // Performs lexical comparison between two @ref GLFWvidmode structures // int _glfwCompareVideoModes(const GLFWvidmode* fm, const GLFWvidmode* sm) { return compareVideoModes(fm, sm); } // Splits a color depth into red, green and blue bit depths // void _glfwSplitBPP(int bpp, int* red, int* green, int* blue) { int delta; // We assume that by 32 the user really meant 24 if (bpp == 32) bpp = 24; // Convert "bits per pixel" to red, green & blue sizes *red = *green = *blue = bpp / 3; delta = bpp - (*red * 3); if (delta >= 1) *green = *green + 1; if (delta == 2) *red = *red + 1; } ////////////////////////////////////////////////////////////////////////// ////// GLFW public API ////// ////////////////////////////////////////////////////////////////////////// GLFWAPI GLFWmonitor** glfwGetMonitors(int* count) { assert(count != NULL); *count = 0; _GLFW_REQUIRE_INIT_OR_RETURN(NULL); *count = _glfw.monitorCount; return (GLFWmonitor**) _glfw.monitors; } GLFWAPI GLFWmonitor* glfwGetPrimaryMonitor(void) { _GLFW_REQUIRE_INIT_OR_RETURN(NULL); if (!_glfw.monitorCount) return NULL; return (GLFWmonitor*) _glfw.monitors[0]; } GLFWAPI void glfwGetMonitorPos(GLFWmonitor* handle, int* xpos, int* ypos) { _GLFWmonitor* monitor = (_GLFWmonitor*) handle; assert(monitor != NULL); if (xpos) *xpos = 0; if (ypos) *ypos = 0; _GLFW_REQUIRE_INIT(); _glfwPlatformGetMonitorPos(monitor, xpos, ypos); } GLFWAPI void glfwGetMonitorWorkarea(GLFWmonitor* handle, int* xpos, int* ypos, int* width, int* height) { _GLFWmonitor* monitor = (_GLFWmonitor*) handle; assert(monitor != NULL); if (xpos) *xpos = 0; if (ypos) *ypos = 0; if (width) *width = 0; if (height) *height = 0; _GLFW_REQUIRE_INIT(); _glfwPlatformGetMonitorWorkarea(monitor, xpos, ypos, width, height); } GLFWAPI void glfwGetMonitorPhysicalSize(GLFWmonitor* handle, int* widthMM, int* heightMM) { _GLFWmonitor* monitor = (_GLFWmonitor*) handle; assert(monitor != NULL); if (widthMM) *widthMM = 0; if (heightMM) *heightMM = 0; _GLFW_REQUIRE_INIT(); if (widthMM) *widthMM = monitor->widthMM; if (heightMM) *heightMM = monitor->heightMM; } GLFWAPI void glfwGetMonitorContentScale(GLFWmonitor* handle, float* xscale, float* yscale) { _GLFWmonitor* monitor = (_GLFWmonitor*) handle; assert(monitor != NULL); if (xscale) *xscale = 0.f; if (yscale) *yscale = 0.f; _GLFW_REQUIRE_INIT(); _glfwPlatformGetMonitorContentScale(monitor, xscale, yscale); } GLFWAPI const char* glfwGetMonitorName(GLFWmonitor* handle) { _GLFWmonitor* monitor = (_GLFWmonitor*) handle; assert(monitor != NULL); _GLFW_REQUIRE_INIT_OR_RETURN(NULL); return monitor->name; } GLFWAPI void glfwSetMonitorUserPointer(GLFWmonitor* handle, void* pointer) { _GLFWmonitor* monitor = (_GLFWmonitor*) handle; assert(monitor != NULL); _GLFW_REQUIRE_INIT(); monitor->userPointer = pointer; } GLFWAPI void* glfwGetMonitorUserPointer(GLFWmonitor* handle) { _GLFWmonitor* monitor = (_GLFWmonitor*) handle; assert(monitor != NULL); _GLFW_REQUIRE_INIT_OR_RETURN(NULL); return monitor->userPointer; } GLFWAPI GLFWmonitorfun glfwSetMonitorCallback(GLFWmonitorfun cbfun) { _GLFW_REQUIRE_INIT_OR_RETURN(NULL); _GLFW_SWAP_POINTERS(_glfw.callbacks.monitor, cbfun); return cbfun; } GLFWAPI const GLFWvidmode* glfwGetVideoModes(GLFWmonitor* handle, int* count) { _GLFWmonitor* monitor = (_GLFWmonitor*) handle; assert(monitor != NULL); assert(count != NULL); *count = 0; _GLFW_REQUIRE_INIT_OR_RETURN(NULL); if (!refreshVideoModes(monitor)) return NULL; *count = monitor->modeCount; return monitor->modes; } GLFWAPI const GLFWvidmode* glfwGetVideoMode(GLFWmonitor* handle) { _GLFWmonitor* monitor = (_GLFWmonitor*) handle; assert(monitor != NULL); _GLFW_REQUIRE_INIT_OR_RETURN(NULL); _glfwPlatformGetVideoMode(monitor, &monitor->currentMode); return &monitor->currentMode; } GLFWAPI void glfwSetGamma(GLFWmonitor* handle, float gamma) { unsigned int i; unsigned short* values; GLFWgammaramp ramp; const GLFWgammaramp* original; assert(handle != NULL); assert(gamma > 0.f); assert(gamma <= FLT_MAX); _GLFW_REQUIRE_INIT(); if (gamma != gamma || gamma <= 0.f || gamma > FLT_MAX) { _glfwInputError(GLFW_INVALID_VALUE, "Invalid gamma value %f", gamma); return; } original = glfwGetGammaRamp(handle); if (!original) return; values = calloc(original->size, sizeof(unsigned short)); for (i = 0; i < original->size; i++) { float value; // Calculate intensity value = i / (float) (original->size - 1); // Apply gamma curve value = powf(value, 1.f / gamma) * 65535.f + 0.5f; // Clamp to value range value = _glfw_fminf(value, 65535.f); values[i] = (unsigned short) value; } ramp.red = values; ramp.green = values; ramp.blue = values; ramp.size = original->size; glfwSetGammaRamp(handle, &ramp); free(values); } GLFWAPI const GLFWgammaramp* glfwGetGammaRamp(GLFWmonitor* handle) { _GLFWmonitor* monitor = (_GLFWmonitor*) handle; assert(monitor != NULL); _GLFW_REQUIRE_INIT_OR_RETURN(NULL); _glfwFreeGammaArrays(&monitor->currentRamp); if (!_glfwPlatformGetGammaRamp(monitor, &monitor->currentRamp)) return NULL; return &monitor->currentRamp; } GLFWAPI void glfwSetGammaRamp(GLFWmonitor* handle, const GLFWgammaramp* ramp) { _GLFWmonitor* monitor = (_GLFWmonitor*) handle; assert(monitor != NULL); assert(ramp != NULL); assert(ramp->size > 0); assert(ramp->red != NULL); assert(ramp->green != NULL); assert(ramp->blue != NULL); if (ramp->size <= 0) { _glfwInputError(GLFW_INVALID_VALUE, "Invalid gamma ramp size %i", ramp->size); return; } _GLFW_REQUIRE_INIT(); if (!monitor->originalRamp.size) { if (!_glfwPlatformGetGammaRamp(monitor, &monitor->originalRamp)) return; } _glfwPlatformSetGammaRamp(monitor, ramp); } #endif #ifndef HEADER_GUARD_VULKAN_C #define HEADER_GUARD_VULKAN_C //======================================================================== // GLFW 3.3.7 - www.glfw.org //------------------------------------------------------------------------ // Copyright (c) 2002-2006 Marcus Geelnard // Copyright (c) 2006-2018 Camilla Löwy // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would // be appreciated but is not required. // // 2. Altered source versions must be plainly marked as such, and must not // be misrepresented as being the original software. // // 3. This notice may not be removed or altered from any source // distribution. // //======================================================================== // Please use C89 style variable declarations in this file because VS 2010 //======================================================================== #include #include #include #define _GLFW_FIND_LOADER 1 #define _GLFW_REQUIRE_LOADER 2 ////////////////////////////////////////////////////////////////////////// ////// GLFW internal API ////// ////////////////////////////////////////////////////////////////////////// GLFWbool _glfwInitVulkan(int mode) { VkResult err; VkExtensionProperties* ep; uint32_t i, count; if (_glfw.vk.available) return GLFW_TRUE; #if !defined(_GLFW_VULKAN_STATIC) #if defined(_GLFW_VULKAN_LIBRARY) _glfw.vk.handle = _glfw_dlopen(_GLFW_VULKAN_LIBRARY); #elif defined(_GLFW_WIN32) _glfw.vk.handle = _glfw_dlopen("vulkan-1.dll"); #elif defined(_GLFW_COCOA) _glfw.vk.handle = _glfw_dlopen("libvulkan.1.dylib"); if (!_glfw.vk.handle) _glfw.vk.handle = _glfwLoadLocalVulkanLoaderNS(); #elif defined(__OpenBSD__) || defined(__NetBSD__) _glfw.vk.handle = _glfw_dlopen("libvulkan.so"); #else _glfw.vk.handle = _glfw_dlopen("libvulkan.so.1"); #endif if (!_glfw.vk.handle) { if (mode == _GLFW_REQUIRE_LOADER) _glfwInputError(GLFW_API_UNAVAILABLE, "Vulkan: Loader not found"); return GLFW_FALSE; } _glfw.vk.GetInstanceProcAddr = (PFN_vkGetInstanceProcAddr) _glfw_dlsym(_glfw.vk.handle, "vkGetInstanceProcAddr"); if (!_glfw.vk.GetInstanceProcAddr) { _glfwInputError(GLFW_API_UNAVAILABLE, "Vulkan: Loader does not export vkGetInstanceProcAddr"); _glfwTerminateVulkan(); return GLFW_FALSE; } _glfw.vk.EnumerateInstanceExtensionProperties = (PFN_vkEnumerateInstanceExtensionProperties) vkGetInstanceProcAddr(NULL, "vkEnumerateInstanceExtensionProperties"); if (!_glfw.vk.EnumerateInstanceExtensionProperties) { _glfwInputError(GLFW_API_UNAVAILABLE, "Vulkan: Failed to retrieve vkEnumerateInstanceExtensionProperties"); _glfwTerminateVulkan(); return GLFW_FALSE; } #endif // _GLFW_VULKAN_STATIC err = vkEnumerateInstanceExtensionProperties(NULL, &count, NULL); if (err) { // NOTE: This happens on systems with a loader but without any Vulkan ICD if (mode == _GLFW_REQUIRE_LOADER) { _glfwInputError(GLFW_API_UNAVAILABLE, "Vulkan: Failed to query instance extension count: %s", _glfwGetVulkanResultString(err)); } _glfwTerminateVulkan(); return GLFW_FALSE; } ep = calloc(count, sizeof(VkExtensionProperties)); err = vkEnumerateInstanceExtensionProperties(NULL, &count, ep); if (err) { _glfwInputError(GLFW_API_UNAVAILABLE, "Vulkan: Failed to query instance extensions: %s", _glfwGetVulkanResultString(err)); free(ep); _glfwTerminateVulkan(); return GLFW_FALSE; } for (i = 0; i < count; i++) { if (strcmp(ep[i].extensionName, "VK_KHR_surface") == 0) _glfw.vk.KHR_surface = GLFW_TRUE; #if defined(_GLFW_WIN32) else if (strcmp(ep[i].extensionName, "VK_KHR_win32_surface") == 0) _glfw.vk.KHR_win32_surface = GLFW_TRUE; #elif defined(_GLFW_COCOA) else if (strcmp(ep[i].extensionName, "VK_MVK_macos_surface") == 0) _glfw.vk.MVK_macos_surface = GLFW_TRUE; else if (strcmp(ep[i].extensionName, "VK_EXT_metal_surface") == 0) _glfw.vk.EXT_metal_surface = GLFW_TRUE; #elif defined(_GLFW_X11) else if (strcmp(ep[i].extensionName, "VK_KHR_xlib_surface") == 0) _glfw.vk.KHR_xlib_surface = GLFW_TRUE; else if (strcmp(ep[i].extensionName, "VK_KHR_xcb_surface") == 0) _glfw.vk.KHR_xcb_surface = GLFW_TRUE; #elif defined(_GLFW_WAYLAND) else if (strcmp(ep[i].extensionName, "VK_KHR_wayland_surface") == 0) _glfw.vk.KHR_wayland_surface = GLFW_TRUE; #endif } free(ep); _glfw.vk.available = GLFW_TRUE; _glfwPlatformGetRequiredInstanceExtensions(_glfw.vk.extensions); return GLFW_TRUE; } void _glfwTerminateVulkan(void) { #if !defined(_GLFW_VULKAN_STATIC) if (_glfw.vk.handle) _glfw_dlclose(_glfw.vk.handle); #endif } const char* _glfwGetVulkanResultString(VkResult result) { switch (result) { case VK_SUCCESS: return "Success"; case VK_NOT_READY: return "A fence or query has not yet completed"; case VK_TIMEOUT: return "A wait operation has not completed in the specified time"; case VK_EVENT_SET: return "An event is signaled"; case VK_EVENT_RESET: return "An event is unsignaled"; case VK_INCOMPLETE: return "A return array was too small for the result"; case VK_ERROR_OUT_OF_HOST_MEMORY: return "A host memory allocation has failed"; case VK_ERROR_OUT_OF_DEVICE_MEMORY: return "A device memory allocation has failed"; case VK_ERROR_INITIALIZATION_FAILED: return "Initialization of an object could not be completed for implementation-specific reasons"; case VK_ERROR_DEVICE_LOST: return "The logical or physical device has been lost"; case VK_ERROR_MEMORY_MAP_FAILED: return "Mapping of a memory object has failed"; case VK_ERROR_LAYER_NOT_PRESENT: return "A requested layer is not present or could not be loaded"; case VK_ERROR_EXTENSION_NOT_PRESENT: return "A requested extension is not supported"; case VK_ERROR_FEATURE_NOT_PRESENT: return "A requested feature is not supported"; case VK_ERROR_INCOMPATIBLE_DRIVER: return "The requested version of Vulkan is not supported by the driver or is otherwise incompatible"; case VK_ERROR_TOO_MANY_OBJECTS: return "Too many objects of the type have already been created"; case VK_ERROR_FORMAT_NOT_SUPPORTED: return "A requested format is not supported on this device"; case VK_ERROR_SURFACE_LOST_KHR: return "A surface is no longer available"; case VK_SUBOPTIMAL_KHR: return "A swapchain no longer matches the surface properties exactly, but can still be used"; case VK_ERROR_OUT_OF_DATE_KHR: return "A surface has changed in such a way that it is no longer compatible with the swapchain"; case VK_ERROR_INCOMPATIBLE_DISPLAY_KHR: return "The display used by a swapchain does not use the same presentable image layout"; case VK_ERROR_NATIVE_WINDOW_IN_USE_KHR: return "The requested window is already connected to a VkSurfaceKHR, or to some other non-Vulkan API"; case VK_ERROR_VALIDATION_FAILED_EXT: return "A validation layer found an error"; default: return "ERROR: UNKNOWN VULKAN ERROR"; } } ////////////////////////////////////////////////////////////////////////// ////// GLFW public API ////// ////////////////////////////////////////////////////////////////////////// GLFWAPI int glfwVulkanSupported(void) { _GLFW_REQUIRE_INIT_OR_RETURN(GLFW_FALSE); return _glfwInitVulkan(_GLFW_FIND_LOADER); } GLFWAPI const char** glfwGetRequiredInstanceExtensions(uint32_t* count) { assert(count != NULL); *count = 0; _GLFW_REQUIRE_INIT_OR_RETURN(NULL); if (!_glfwInitVulkan(_GLFW_REQUIRE_LOADER)) return NULL; if (!_glfw.vk.extensions[0]) return NULL; *count = 2; return (const char**) _glfw.vk.extensions; } GLFWAPI GLFWvkproc glfwGetInstanceProcAddress(VkInstance instance, const char* procname) { GLFWvkproc proc; assert(procname != NULL); _GLFW_REQUIRE_INIT_OR_RETURN(NULL); if (!_glfwInitVulkan(_GLFW_REQUIRE_LOADER)) return NULL; proc = (GLFWvkproc) vkGetInstanceProcAddr(instance, procname); #if defined(_GLFW_VULKAN_STATIC) if (!proc) { if (strcmp(procname, "vkGetInstanceProcAddr") == 0) return (GLFWvkproc) vkGetInstanceProcAddr; } #else if (!proc) proc = (GLFWvkproc) _glfw_dlsym(_glfw.vk.handle, procname); #endif return proc; } GLFWAPI int glfwGetPhysicalDevicePresentationSupport(VkInstance instance, VkPhysicalDevice device, uint32_t queuefamily) { assert(instance != VK_NULL_HANDLE); assert(device != VK_NULL_HANDLE); _GLFW_REQUIRE_INIT_OR_RETURN(GLFW_FALSE); if (!_glfwInitVulkan(_GLFW_REQUIRE_LOADER)) return GLFW_FALSE; if (!_glfw.vk.extensions[0]) { _glfwInputError(GLFW_API_UNAVAILABLE, "Vulkan: Window surface creation extensions not found"); return GLFW_FALSE; } return _glfwPlatformGetPhysicalDevicePresentationSupport(instance, device, queuefamily); } GLFWAPI VkResult glfwCreateWindowSurface(VkInstance instance, GLFWwindow* handle, const VkAllocationCallbacks* allocator, VkSurfaceKHR* surface) { _GLFWwindow* window = (_GLFWwindow*) handle; assert(instance != VK_NULL_HANDLE); assert(window != NULL); assert(surface != NULL); *surface = VK_NULL_HANDLE; _GLFW_REQUIRE_INIT_OR_RETURN(VK_ERROR_INITIALIZATION_FAILED); if (!_glfwInitVulkan(_GLFW_REQUIRE_LOADER)) return VK_ERROR_INITIALIZATION_FAILED; if (!_glfw.vk.extensions[0]) { _glfwInputError(GLFW_API_UNAVAILABLE, "Vulkan: Window surface creation extensions not found"); return VK_ERROR_EXTENSION_NOT_PRESENT; } if (window->context.client != GLFW_NO_API) { _glfwInputError(GLFW_INVALID_VALUE, "Vulkan: Window surface creation requires the window to have the client API set to GLFW_NO_API"); return VK_ERROR_NATIVE_WINDOW_IN_USE_KHR; } return _glfwPlatformCreateWindowSurface(instance, window, allocator, surface); } #endif #ifndef HEADER_GUARD_WINDOW_C #define HEADER_GUARD_WINDOW_C //======================================================================== // GLFW 3.3.7 - www.glfw.org //------------------------------------------------------------------------ // Copyright (c) 2002-2006 Marcus Geelnard // Copyright (c) 2006-2019 Camilla Löwy // Copyright (c) 2012 Torsten Walluhn // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would // be appreciated but is not required. // // 2. Altered source versions must be plainly marked as such, and must not // be misrepresented as being the original software. // // 3. This notice may not be removed or altered from any source // distribution. // //======================================================================== // Please use C89 style variable declarations in this file because VS 2010 //======================================================================== #include #include #include #include ////////////////////////////////////////////////////////////////////////// ////// GLFW event API ////// ////////////////////////////////////////////////////////////////////////// // Notifies shared code that a window has lost or received input focus // void _glfwInputWindowFocus(_GLFWwindow* window, GLFWbool focused) { if (window->callbacks.focus) window->callbacks.focus((GLFWwindow*) window, focused); if (!focused) { int key, button; for (key = 0; key <= GLFW_KEY_LAST; key++) { if (window->keys[key] == GLFW_PRESS) { const int scancode = _glfwPlatformGetKeyScancode(key); _glfwInputKey(window, key, scancode, GLFW_RELEASE, 0); } } for (button = 0; button <= GLFW_MOUSE_BUTTON_LAST; button++) { if (window->mouseButtons[button] == GLFW_PRESS) _glfwInputMouseClick(window, button, GLFW_RELEASE, 0); } } } // Notifies shared code that a window has moved // The position is specified in content area relative screen coordinates // void _glfwInputWindowPos(_GLFWwindow* window, int x, int y) { if (window->callbacks.pos) window->callbacks.pos((GLFWwindow*) window, x, y); } // Notifies shared code that a window has been resized // The size is specified in screen coordinates // void _glfwInputWindowSize(_GLFWwindow* window, int width, int height) { if (window->callbacks.size) window->callbacks.size((GLFWwindow*) window, width, height); } // Notifies shared code that a window has been iconified or restored // void _glfwInputWindowIconify(_GLFWwindow* window, GLFWbool iconified) { if (window->callbacks.iconify) window->callbacks.iconify((GLFWwindow*) window, iconified); } // Notifies shared code that a window has been maximized or restored // void _glfwInputWindowMaximize(_GLFWwindow* window, GLFWbool maximized) { if (window->callbacks.maximize) window->callbacks.maximize((GLFWwindow*) window, maximized); } // Notifies shared code that a window framebuffer has been resized // The size is specified in pixels // void _glfwInputFramebufferSize(_GLFWwindow* window, int width, int height) { if (window->callbacks.fbsize) window->callbacks.fbsize((GLFWwindow*) window, width, height); } // Notifies shared code that a window content scale has changed // The scale is specified as the ratio between the current and default DPI // void _glfwInputWindowContentScale(_GLFWwindow* window, float xscale, float yscale) { if (window->callbacks.scale) window->callbacks.scale((GLFWwindow*) window, xscale, yscale); } // Notifies shared code that the window contents needs updating // void _glfwInputWindowDamage(_GLFWwindow* window) { if (window->callbacks.refresh) window->callbacks.refresh((GLFWwindow*) window); } // Notifies shared code that the user wishes to close a window // void _glfwInputWindowCloseRequest(_GLFWwindow* window) { window->shouldClose = GLFW_TRUE; if (window->callbacks.close) window->callbacks.close((GLFWwindow*) window); } // Notifies shared code that a window has changed its desired monitor // void _glfwInputWindowMonitor(_GLFWwindow* window, _GLFWmonitor* monitor) { window->monitor = monitor; } ////////////////////////////////////////////////////////////////////////// ////// GLFW public API ////// ////////////////////////////////////////////////////////////////////////// GLFWAPI GLFWwindow* glfwCreateWindow(int width, int height, const char* title, GLFWmonitor* monitor, GLFWwindow* share) { _GLFWfbconfig fbconfig; _GLFWctxconfig ctxconfig; _GLFWwndconfig wndconfig; _GLFWwindow* window; assert(title != NULL); assert(width >= 0); assert(height >= 0); _GLFW_REQUIRE_INIT_OR_RETURN(NULL); if (width <= 0 || height <= 0) { _glfwInputError(GLFW_INVALID_VALUE, "Invalid window size %ix%i", width, height); return NULL; } fbconfig = _glfw.hints.framebuffer; ctxconfig = _glfw.hints.context; wndconfig = _glfw.hints.window; wndconfig.width = width; wndconfig.height = height; wndconfig.title = title; ctxconfig.share = (_GLFWwindow*) share; if (!_glfwIsValidContextConfig(&ctxconfig)) return NULL; window = calloc(1, sizeof(_GLFWwindow)); window->next = _glfw.windowListHead; _glfw.windowListHead = window; window->videoMode.width = width; window->videoMode.height = height; window->videoMode.redBits = fbconfig.redBits; window->videoMode.greenBits = fbconfig.greenBits; window->videoMode.blueBits = fbconfig.blueBits; window->videoMode.refreshRate = _glfw.hints.refreshRate; window->monitor = (_GLFWmonitor*) monitor; window->resizable = wndconfig.resizable; window->decorated = wndconfig.decorated; window->autoIconify = wndconfig.autoIconify; window->floating = wndconfig.floating; window->focusOnShow = wndconfig.focusOnShow; window->cursorMode = GLFW_CURSOR_NORMAL; window->doublebuffer = fbconfig.doublebuffer; window->minwidth = GLFW_DONT_CARE; window->minheight = GLFW_DONT_CARE; window->maxwidth = GLFW_DONT_CARE; window->maxheight = GLFW_DONT_CARE; window->numer = GLFW_DONT_CARE; window->denom = GLFW_DONT_CARE; // Open the actual window and create its context if (!_glfwPlatformCreateWindow(window, &wndconfig, &ctxconfig, &fbconfig)) { glfwDestroyWindow((GLFWwindow*) window); return NULL; } if (ctxconfig.client != GLFW_NO_API) { if (!_glfwRefreshContextAttribs(window, &ctxconfig)) { glfwDestroyWindow((GLFWwindow*) window); return NULL; } } if (window->monitor) { if (wndconfig.centerCursor) _glfwCenterCursorInContentArea(window); } else { if (wndconfig.visible) { _glfwPlatformShowWindow(window); if (wndconfig.focused) _glfwPlatformFocusWindow(window); } } return (GLFWwindow*) window; } void glfwDefaultWindowHints(void) { _GLFW_REQUIRE_INIT(); // The default is OpenGL with minimum version 1.0 memset(&_glfw.hints.context, 0, sizeof(_glfw.hints.context)); _glfw.hints.context.client = GLFW_OPENGL_API; _glfw.hints.context.source = GLFW_NATIVE_CONTEXT_API; _glfw.hints.context.major = 1; _glfw.hints.context.minor = 0; // The default is a focused, visible, resizable window with decorations memset(&_glfw.hints.window, 0, sizeof(_glfw.hints.window)); _glfw.hints.window.resizable = GLFW_TRUE; _glfw.hints.window.visible = GLFW_TRUE; _glfw.hints.window.decorated = GLFW_TRUE; _glfw.hints.window.focused = GLFW_TRUE; _glfw.hints.window.autoIconify = GLFW_TRUE; _glfw.hints.window.centerCursor = GLFW_TRUE; _glfw.hints.window.focusOnShow = GLFW_TRUE; // The default is 24 bits of color, 24 bits of depth and 8 bits of stencil, // double buffered memset(&_glfw.hints.framebuffer, 0, sizeof(_glfw.hints.framebuffer)); _glfw.hints.framebuffer.redBits = 8; _glfw.hints.framebuffer.greenBits = 8; _glfw.hints.framebuffer.blueBits = 8; _glfw.hints.framebuffer.alphaBits = 8; _glfw.hints.framebuffer.depthBits = 24; _glfw.hints.framebuffer.stencilBits = 8; _glfw.hints.framebuffer.doublebuffer = GLFW_TRUE; // The default is to select the highest available refresh rate _glfw.hints.refreshRate = GLFW_DONT_CARE; // The default is to use full Retina resolution framebuffers _glfw.hints.window.ns.retina = GLFW_TRUE; } GLFWAPI void glfwWindowHint(int hint, int value) { _GLFW_REQUIRE_INIT(); switch (hint) { case GLFW_RED_BITS: _glfw.hints.framebuffer.redBits = value; return; case GLFW_GREEN_BITS: _glfw.hints.framebuffer.greenBits = value; return; case GLFW_BLUE_BITS: _glfw.hints.framebuffer.blueBits = value; return; case GLFW_ALPHA_BITS: _glfw.hints.framebuffer.alphaBits = value; return; case GLFW_DEPTH_BITS: _glfw.hints.framebuffer.depthBits = value; return; case GLFW_STENCIL_BITS: _glfw.hints.framebuffer.stencilBits = value; return; case GLFW_ACCUM_RED_BITS: _glfw.hints.framebuffer.accumRedBits = value; return; case GLFW_ACCUM_GREEN_BITS: _glfw.hints.framebuffer.accumGreenBits = value; return; case GLFW_ACCUM_BLUE_BITS: _glfw.hints.framebuffer.accumBlueBits = value; return; case GLFW_ACCUM_ALPHA_BITS: _glfw.hints.framebuffer.accumAlphaBits = value; return; case GLFW_AUX_BUFFERS: _glfw.hints.framebuffer.auxBuffers = value; return; case GLFW_STEREO: _glfw.hints.framebuffer.stereo = value ? GLFW_TRUE : GLFW_FALSE; return; case GLFW_DOUBLEBUFFER: _glfw.hints.framebuffer.doublebuffer = value ? GLFW_TRUE : GLFW_FALSE; return; case GLFW_TRANSPARENT_FRAMEBUFFER: _glfw.hints.framebuffer.transparent = value ? GLFW_TRUE : GLFW_FALSE; return; case GLFW_SAMPLES: _glfw.hints.framebuffer.samples = value; return; case GLFW_SRGB_CAPABLE: _glfw.hints.framebuffer.sRGB = value ? GLFW_TRUE : GLFW_FALSE; return; case GLFW_RESIZABLE: _glfw.hints.window.resizable = value ? GLFW_TRUE : GLFW_FALSE; return; case GLFW_DECORATED: _glfw.hints.window.decorated = value ? GLFW_TRUE : GLFW_FALSE; return; case GLFW_FOCUSED: _glfw.hints.window.focused = value ? GLFW_TRUE : GLFW_FALSE; return; case GLFW_AUTO_ICONIFY: _glfw.hints.window.autoIconify = value ? GLFW_TRUE : GLFW_FALSE; return; case GLFW_FLOATING: _glfw.hints.window.floating = value ? GLFW_TRUE : GLFW_FALSE; return; case GLFW_MAXIMIZED: _glfw.hints.window.maximized = value ? GLFW_TRUE : GLFW_FALSE; return; case GLFW_VISIBLE: _glfw.hints.window.visible = value ? GLFW_TRUE : GLFW_FALSE; return; case GLFW_COCOA_RETINA_FRAMEBUFFER: _glfw.hints.window.ns.retina = value ? GLFW_TRUE : GLFW_FALSE; return; case GLFW_COCOA_GRAPHICS_SWITCHING: _glfw.hints.context.nsgl.offline = value ? GLFW_TRUE : GLFW_FALSE; return; case GLFW_SCALE_TO_MONITOR: _glfw.hints.window.scaleToMonitor = value ? GLFW_TRUE : GLFW_FALSE; return; case GLFW_CENTER_CURSOR: _glfw.hints.window.centerCursor = value ? GLFW_TRUE : GLFW_FALSE; return; case GLFW_FOCUS_ON_SHOW: _glfw.hints.window.focusOnShow = value ? GLFW_TRUE : GLFW_FALSE; return; case GLFW_CLIENT_API: _glfw.hints.context.client = value; return; case GLFW_CONTEXT_CREATION_API: _glfw.hints.context.source = value; return; case GLFW_CONTEXT_VERSION_MAJOR: _glfw.hints.context.major = value; return; case GLFW_CONTEXT_VERSION_MINOR: _glfw.hints.context.minor = value; return; case GLFW_CONTEXT_ROBUSTNESS: _glfw.hints.context.robustness = value; return; case GLFW_OPENGL_FORWARD_COMPAT: _glfw.hints.context.forward = value ? GLFW_TRUE : GLFW_FALSE; return; case GLFW_OPENGL_DEBUG_CONTEXT: _glfw.hints.context.debug = value ? GLFW_TRUE : GLFW_FALSE; return; case GLFW_CONTEXT_NO_ERROR: _glfw.hints.context.noerror = value ? GLFW_TRUE : GLFW_FALSE; return; case GLFW_OPENGL_PROFILE: _glfw.hints.context.profile = value; return; case GLFW_CONTEXT_RELEASE_BEHAVIOR: _glfw.hints.context.release = value; return; case GLFW_REFRESH_RATE: _glfw.hints.refreshRate = value; return; } _glfwInputError(GLFW_INVALID_ENUM, "Invalid window hint 0x%08X", hint); } GLFWAPI void glfwWindowHintString(int hint, const char* value) { assert(value != NULL); _GLFW_REQUIRE_INIT(); switch (hint) { case GLFW_COCOA_FRAME_NAME: strncpy(_glfw.hints.window.ns.frameName, value, sizeof(_glfw.hints.window.ns.frameName) - 1); return; case GLFW_X11_CLASS_NAME: strncpy(_glfw.hints.window.x11.className, value, sizeof(_glfw.hints.window.x11.className) - 1); return; case GLFW_X11_INSTANCE_NAME: strncpy(_glfw.hints.window.x11.instanceName, value, sizeof(_glfw.hints.window.x11.instanceName) - 1); return; } _glfwInputError(GLFW_INVALID_ENUM, "Invalid window hint string 0x%08X", hint); } GLFWAPI void glfwDestroyWindow(GLFWwindow* handle) { _GLFWwindow* window = (_GLFWwindow*) handle; _GLFW_REQUIRE_INIT(); // Allow closing of NULL (to match the behavior of free) if (window == NULL) return; // Clear all callbacks to avoid exposing a half torn-down window object memset(&window->callbacks, 0, sizeof(window->callbacks)); // The window's context must not be current on another thread when the // window is destroyed if (window == _glfwPlatformGetTls(&_glfw.contextSlot)) glfwMakeContextCurrent(NULL); _glfwPlatformDestroyWindow(window); // Unlink window from global linked list { _GLFWwindow** prev = &_glfw.windowListHead; while (*prev != window) prev = &((*prev)->next); *prev = window->next; } free(window); } GLFWAPI int glfwWindowShouldClose(GLFWwindow* handle) { _GLFWwindow* window = (_GLFWwindow*) handle; assert(window != NULL); _GLFW_REQUIRE_INIT_OR_RETURN(0); return window->shouldClose; } GLFWAPI void glfwSetWindowShouldClose(GLFWwindow* handle, int value) { _GLFWwindow* window = (_GLFWwindow*) handle; assert(window != NULL); _GLFW_REQUIRE_INIT(); window->shouldClose = value; } GLFWAPI void glfwSetWindowTitle(GLFWwindow* handle, const char* title) { _GLFWwindow* window = (_GLFWwindow*) handle; assert(window != NULL); assert(title != NULL); _GLFW_REQUIRE_INIT(); _glfwPlatformSetWindowTitle(window, title); } GLFWAPI void glfwSetWindowIcon(GLFWwindow* handle, int count, const GLFWimage* images) { _GLFWwindow* window = (_GLFWwindow*) handle; assert(window != NULL); assert(count >= 0); assert(count == 0 || images != NULL); _GLFW_REQUIRE_INIT(); _glfwPlatformSetWindowIcon(window, count, images); } GLFWAPI void glfwGetWindowPos(GLFWwindow* handle, int* xpos, int* ypos) { _GLFWwindow* window = (_GLFWwindow*) handle; assert(window != NULL); if (xpos) *xpos = 0; if (ypos) *ypos = 0; _GLFW_REQUIRE_INIT(); _glfwPlatformGetWindowPos(window, xpos, ypos); } GLFWAPI void glfwSetWindowPos(GLFWwindow* handle, int xpos, int ypos) { _GLFWwindow* window = (_GLFWwindow*) handle; assert(window != NULL); _GLFW_REQUIRE_INIT(); if (window->monitor) return; _glfwPlatformSetWindowPos(window, xpos, ypos); } GLFWAPI void glfwGetWindowSize(GLFWwindow* handle, int* width, int* height) { _GLFWwindow* window = (_GLFWwindow*) handle; assert(window != NULL); if (width) *width = 0; if (height) *height = 0; _GLFW_REQUIRE_INIT(); _glfwPlatformGetWindowSize(window, width, height); } GLFWAPI void glfwSetWindowSize(GLFWwindow* handle, int width, int height) { _GLFWwindow* window = (_GLFWwindow*) handle; assert(window != NULL); assert(width >= 0); assert(height >= 0); _GLFW_REQUIRE_INIT(); window->videoMode.width = width; window->videoMode.height = height; _glfwPlatformSetWindowSize(window, width, height); } GLFWAPI void glfwSetWindowSizeLimits(GLFWwindow* handle, int minwidth, int minheight, int maxwidth, int maxheight) { _GLFWwindow* window = (_GLFWwindow*) handle; assert(window != NULL); _GLFW_REQUIRE_INIT(); if (minwidth != GLFW_DONT_CARE && minheight != GLFW_DONT_CARE) { if (minwidth < 0 || minheight < 0) { _glfwInputError(GLFW_INVALID_VALUE, "Invalid window minimum size %ix%i", minwidth, minheight); return; } } if (maxwidth != GLFW_DONT_CARE && maxheight != GLFW_DONT_CARE) { if (maxwidth < 0 || maxheight < 0 || maxwidth < minwidth || maxheight < minheight) { _glfwInputError(GLFW_INVALID_VALUE, "Invalid window maximum size %ix%i", maxwidth, maxheight); return; } } window->minwidth = minwidth; window->minheight = minheight; window->maxwidth = maxwidth; window->maxheight = maxheight; if (window->monitor || !window->resizable) return; _glfwPlatformSetWindowSizeLimits(window, minwidth, minheight, maxwidth, maxheight); } GLFWAPI void glfwSetWindowAspectRatio(GLFWwindow* handle, int numer, int denom) { _GLFWwindow* window = (_GLFWwindow*) handle; assert(window != NULL); assert(numer != 0); assert(denom != 0); _GLFW_REQUIRE_INIT(); if (numer != GLFW_DONT_CARE && denom != GLFW_DONT_CARE) { if (numer <= 0 || denom <= 0) { _glfwInputError(GLFW_INVALID_VALUE, "Invalid window aspect ratio %i:%i", numer, denom); return; } } window->numer = numer; window->denom = denom; if (window->monitor || !window->resizable) return; _glfwPlatformSetWindowAspectRatio(window, numer, denom); } GLFWAPI void glfwGetFramebufferSize(GLFWwindow* handle, int* width, int* height) { _GLFWwindow* window = (_GLFWwindow*) handle; assert(window != NULL); if (width) *width = 0; if (height) *height = 0; _GLFW_REQUIRE_INIT(); _glfwPlatformGetFramebufferSize(window, width, height); } GLFWAPI void glfwGetWindowFrameSize(GLFWwindow* handle, int* left, int* top, int* right, int* bottom) { _GLFWwindow* window = (_GLFWwindow*) handle; assert(window != NULL); if (left) *left = 0; if (top) *top = 0; if (right) *right = 0; if (bottom) *bottom = 0; _GLFW_REQUIRE_INIT(); _glfwPlatformGetWindowFrameSize(window, left, top, right, bottom); } GLFWAPI void glfwGetWindowContentScale(GLFWwindow* handle, float* xscale, float* yscale) { _GLFWwindow* window = (_GLFWwindow*) handle; assert(window != NULL); if (xscale) *xscale = 0.f; if (yscale) *yscale = 0.f; _GLFW_REQUIRE_INIT(); _glfwPlatformGetWindowContentScale(window, xscale, yscale); } GLFWAPI float glfwGetWindowOpacity(GLFWwindow* handle) { _GLFWwindow* window = (_GLFWwindow*) handle; assert(window != NULL); _GLFW_REQUIRE_INIT_OR_RETURN(1.f); return _glfwPlatformGetWindowOpacity(window); } GLFWAPI void glfwSetWindowOpacity(GLFWwindow* handle, float opacity) { _GLFWwindow* window = (_GLFWwindow*) handle; assert(window != NULL); assert(opacity == opacity); assert(opacity >= 0.f); assert(opacity <= 1.f); _GLFW_REQUIRE_INIT(); if (opacity != opacity || opacity < 0.f || opacity > 1.f) { _glfwInputError(GLFW_INVALID_VALUE, "Invalid window opacity %f", opacity); return; } _glfwPlatformSetWindowOpacity(window, opacity); } GLFWAPI void glfwIconifyWindow(GLFWwindow* handle) { _GLFWwindow* window = (_GLFWwindow*) handle; assert(window != NULL); _GLFW_REQUIRE_INIT(); _glfwPlatformIconifyWindow(window); } GLFWAPI void glfwRestoreWindow(GLFWwindow* handle) { _GLFWwindow* window = (_GLFWwindow*) handle; assert(window != NULL); _GLFW_REQUIRE_INIT(); _glfwPlatformRestoreWindow(window); } GLFWAPI void glfwMaximizeWindow(GLFWwindow* handle) { _GLFWwindow* window = (_GLFWwindow*) handle; assert(window != NULL); _GLFW_REQUIRE_INIT(); if (window->monitor) return; _glfwPlatformMaximizeWindow(window); } GLFWAPI void glfwShowWindow(GLFWwindow* handle) { _GLFWwindow* window = (_GLFWwindow*) handle; assert(window != NULL); _GLFW_REQUIRE_INIT(); if (window->monitor) return; _glfwPlatformShowWindow(window); if (window->focusOnShow) _glfwPlatformFocusWindow(window); } GLFWAPI void glfwRequestWindowAttention(GLFWwindow* handle) { _GLFWwindow* window = (_GLFWwindow*) handle; assert(window != NULL); _GLFW_REQUIRE_INIT(); _glfwPlatformRequestWindowAttention(window); } GLFWAPI void glfwHideWindow(GLFWwindow* handle) { _GLFWwindow* window = (_GLFWwindow*) handle; assert(window != NULL); _GLFW_REQUIRE_INIT(); if (window->monitor) return; _glfwPlatformHideWindow(window); } GLFWAPI void glfwFocusWindow(GLFWwindow* handle) { _GLFWwindow* window = (_GLFWwindow*) handle; assert(window != NULL); _GLFW_REQUIRE_INIT(); _glfwPlatformFocusWindow(window); } GLFWAPI int glfwGetWindowAttrib(GLFWwindow* handle, int attrib) { _GLFWwindow* window = (_GLFWwindow*) handle; assert(window != NULL); _GLFW_REQUIRE_INIT_OR_RETURN(0); switch (attrib) { case GLFW_FOCUSED: return _glfwPlatformWindowFocused(window); case GLFW_ICONIFIED: return _glfwPlatformWindowIconified(window); case GLFW_VISIBLE: return _glfwPlatformWindowVisible(window); case GLFW_MAXIMIZED: return _glfwPlatformWindowMaximized(window); case GLFW_HOVERED: return _glfwPlatformWindowHovered(window); case GLFW_FOCUS_ON_SHOW: return window->focusOnShow; case GLFW_TRANSPARENT_FRAMEBUFFER: return _glfwPlatformFramebufferTransparent(window); case GLFW_RESIZABLE: return window->resizable; case GLFW_DECORATED: return window->decorated; case GLFW_FLOATING: return window->floating; case GLFW_AUTO_ICONIFY: return window->autoIconify; case GLFW_CLIENT_API: return window->context.client; case GLFW_CONTEXT_CREATION_API: return window->context.source; case GLFW_CONTEXT_VERSION_MAJOR: return window->context.major; case GLFW_CONTEXT_VERSION_MINOR: return window->context.minor; case GLFW_CONTEXT_REVISION: return window->context.revision; case GLFW_CONTEXT_ROBUSTNESS: return window->context.robustness; case GLFW_OPENGL_FORWARD_COMPAT: return window->context.forward; case GLFW_OPENGL_DEBUG_CONTEXT: return window->context.debug; case GLFW_OPENGL_PROFILE: return window->context.profile; case GLFW_CONTEXT_RELEASE_BEHAVIOR: return window->context.release; case GLFW_CONTEXT_NO_ERROR: return window->context.noerror; } _glfwInputError(GLFW_INVALID_ENUM, "Invalid window attribute 0x%08X", attrib); return 0; } GLFWAPI void glfwSetWindowAttrib(GLFWwindow* handle, int attrib, int value) { _GLFWwindow* window = (_GLFWwindow*) handle; assert(window != NULL); _GLFW_REQUIRE_INIT(); value = value ? GLFW_TRUE : GLFW_FALSE; if (attrib == GLFW_AUTO_ICONIFY) window->autoIconify = value; else if (attrib == GLFW_RESIZABLE) { if (window->resizable == value) return; window->resizable = value; if (!window->monitor) _glfwPlatformSetWindowResizable(window, value); } else if (attrib == GLFW_DECORATED) { if (window->decorated == value) return; window->decorated = value; if (!window->monitor) _glfwPlatformSetWindowDecorated(window, value); } else if (attrib == GLFW_FLOATING) { if (window->floating == value) return; window->floating = value; if (!window->monitor) _glfwPlatformSetWindowFloating(window, value); } else if (attrib == GLFW_FOCUS_ON_SHOW) window->focusOnShow = value; else _glfwInputError(GLFW_INVALID_ENUM, "Invalid window attribute 0x%08X", attrib); } GLFWAPI GLFWmonitor* glfwGetWindowMonitor(GLFWwindow* handle) { _GLFWwindow* window = (_GLFWwindow*) handle; assert(window != NULL); _GLFW_REQUIRE_INIT_OR_RETURN(NULL); return (GLFWmonitor*) window->monitor; } GLFWAPI void glfwSetWindowMonitor(GLFWwindow* wh, GLFWmonitor* mh, int xpos, int ypos, int width, int height, int refreshRate) { _GLFWwindow* window = (_GLFWwindow*) wh; _GLFWmonitor* monitor = (_GLFWmonitor*) mh; assert(window != NULL); assert(width >= 0); assert(height >= 0); _GLFW_REQUIRE_INIT(); if (width <= 0 || height <= 0) { _glfwInputError(GLFW_INVALID_VALUE, "Invalid window size %ix%i", width, height); return; } if (refreshRate < 0 && refreshRate != GLFW_DONT_CARE) { _glfwInputError(GLFW_INVALID_VALUE, "Invalid refresh rate %i", refreshRate); return; } window->videoMode.width = width; window->videoMode.height = height; window->videoMode.refreshRate = refreshRate; _glfwPlatformSetWindowMonitor(window, monitor, xpos, ypos, width, height, refreshRate); } GLFWAPI void glfwSetWindowUserPointer(GLFWwindow* handle, void* pointer) { _GLFWwindow* window = (_GLFWwindow*) handle; assert(window != NULL); _GLFW_REQUIRE_INIT(); window->userPointer = pointer; } GLFWAPI void* glfwGetWindowUserPointer(GLFWwindow* handle) { _GLFWwindow* window = (_GLFWwindow*) handle; assert(window != NULL); _GLFW_REQUIRE_INIT_OR_RETURN(NULL); return window->userPointer; } GLFWAPI GLFWwindowposfun glfwSetWindowPosCallback(GLFWwindow* handle, GLFWwindowposfun cbfun) { _GLFWwindow* window = (_GLFWwindow*) handle; assert(window != NULL); _GLFW_REQUIRE_INIT_OR_RETURN(NULL); _GLFW_SWAP_POINTERS(window->callbacks.pos, cbfun); return cbfun; } GLFWAPI GLFWwindowsizefun glfwSetWindowSizeCallback(GLFWwindow* handle, GLFWwindowsizefun cbfun) { _GLFWwindow* window = (_GLFWwindow*) handle; assert(window != NULL); _GLFW_REQUIRE_INIT_OR_RETURN(NULL); _GLFW_SWAP_POINTERS(window->callbacks.size, cbfun); return cbfun; } GLFWAPI GLFWwindowclosefun glfwSetWindowCloseCallback(GLFWwindow* handle, GLFWwindowclosefun cbfun) { _GLFWwindow* window = (_GLFWwindow*) handle; assert(window != NULL); _GLFW_REQUIRE_INIT_OR_RETURN(NULL); _GLFW_SWAP_POINTERS(window->callbacks.close, cbfun); return cbfun; } GLFWAPI GLFWwindowrefreshfun glfwSetWindowRefreshCallback(GLFWwindow* handle, GLFWwindowrefreshfun cbfun) { _GLFWwindow* window = (_GLFWwindow*) handle; assert(window != NULL); _GLFW_REQUIRE_INIT_OR_RETURN(NULL); _GLFW_SWAP_POINTERS(window->callbacks.refresh, cbfun); return cbfun; } GLFWAPI GLFWwindowfocusfun glfwSetWindowFocusCallback(GLFWwindow* handle, GLFWwindowfocusfun cbfun) { _GLFWwindow* window = (_GLFWwindow*) handle; assert(window != NULL); _GLFW_REQUIRE_INIT_OR_RETURN(NULL); _GLFW_SWAP_POINTERS(window->callbacks.focus, cbfun); return cbfun; } GLFWAPI GLFWwindowiconifyfun glfwSetWindowIconifyCallback(GLFWwindow* handle, GLFWwindowiconifyfun cbfun) { _GLFWwindow* window = (_GLFWwindow*) handle; assert(window != NULL); _GLFW_REQUIRE_INIT_OR_RETURN(NULL); _GLFW_SWAP_POINTERS(window->callbacks.iconify, cbfun); return cbfun; } GLFWAPI GLFWwindowmaximizefun glfwSetWindowMaximizeCallback(GLFWwindow* handle, GLFWwindowmaximizefun cbfun) { _GLFWwindow* window = (_GLFWwindow*) handle; assert(window != NULL); _GLFW_REQUIRE_INIT_OR_RETURN(NULL); _GLFW_SWAP_POINTERS(window->callbacks.maximize, cbfun); return cbfun; } GLFWAPI GLFWframebuffersizefun glfwSetFramebufferSizeCallback(GLFWwindow* handle, GLFWframebuffersizefun cbfun) { _GLFWwindow* window = (_GLFWwindow*) handle; assert(window != NULL); _GLFW_REQUIRE_INIT_OR_RETURN(NULL); _GLFW_SWAP_POINTERS(window->callbacks.fbsize, cbfun); return cbfun; } GLFWAPI GLFWwindowcontentscalefun glfwSetWindowContentScaleCallback(GLFWwindow* handle, GLFWwindowcontentscalefun cbfun) { _GLFWwindow* window = (_GLFWwindow*) handle; assert(window != NULL); _GLFW_REQUIRE_INIT_OR_RETURN(NULL); _GLFW_SWAP_POINTERS(window->callbacks.scale, cbfun); return cbfun; } GLFWAPI void glfwPollEvents(void) { _GLFW_REQUIRE_INIT(); _glfwPlatformPollEvents(); } GLFWAPI void glfwWaitEvents(void) { _GLFW_REQUIRE_INIT(); _glfwPlatformWaitEvents(); } GLFWAPI void glfwWaitEventsTimeout(double timeout) { _GLFW_REQUIRE_INIT(); assert(timeout == timeout); assert(timeout >= 0.0); assert(timeout <= DBL_MAX); if (timeout != timeout || timeout < 0.0 || timeout > DBL_MAX) { _glfwInputError(GLFW_INVALID_VALUE, "Invalid time %f", timeout); return; } _glfwPlatformWaitEventsTimeout(timeout); } GLFWAPI void glfwPostEmptyEvent(void) { _GLFW_REQUIRE_INIT(); _glfwPlatformPostEmptyEvent(); } #endif #ifdef _GLFW_WIN32 #ifdef _MSC_VER #define _CRT_SECURE_NO_WARNINGS #endif #ifndef _GLFW_USE_HYBRID_HPG #define _GLFW_USE_HYBRID_HPG 1 #endif #ifndef _UNICODE //< @r-lyeh: add guard #define _UNICODE #endif #ifdef MINGW #define UNICODE #define WINVER 0x0501 #endif #ifndef HEADER_GUARD_WIN32_INIT_C #define HEADER_GUARD_WIN32_INIT_C //======================================================================== // GLFW 3.3.7 Win32 - www.glfw.org //------------------------------------------------------------------------ // Copyright (c) 2002-2006 Marcus Geelnard // Copyright (c) 2006-2019 Camilla Löwy // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would // be appreciated but is not required. // // 2. Altered source versions must be plainly marked as such, and must not // be misrepresented as being the original software. // // 3. This notice may not be removed or altered from any source // distribution. // //======================================================================== // Please use C89 style variable declarations in this file because VS 2010 //======================================================================== #include #include static const GUID _glfw_GUID_DEVINTERFACE_HID = {0x4d1e55b2,0xf16f,0x11cf,{0x88,0xcb,0x00,0x11,0x11,0x00,0x00,0x30}}; #define GUID_DEVINTERFACE_HID _glfw_GUID_DEVINTERFACE_HID #if defined(_GLFW_USE_HYBRID_HPG) || defined(_GLFW_USE_OPTIMUS_HPG) #if defined(_GLFW_BUILD_DLL) #pragma message("These symbols must be exported by the executable and have no effect in a DLL") #endif // Executables (but not DLLs) exporting this symbol with this value will be // automatically directed to the high-performance GPU on Nvidia Optimus systems // with up-to-date drivers // __declspec(dllexport) DWORD NvOptimusEnablement = 1; // Executables (but not DLLs) exporting this symbol with this value will be // automatically directed to the high-performance GPU on AMD PowerXpress systems // with up-to-date drivers // __declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1; #endif // _GLFW_USE_HYBRID_HPG #if defined(_GLFW_BUILD_DLL) // GLFW DLL entry point // BOOL WINAPI DllMain(HINSTANCE instance, DWORD reason, LPVOID reserved) { return TRUE; } #endif // _GLFW_BUILD_DLL // Load necessary libraries (DLLs) // static GLFWbool loadLibraries(void) { _glfw.win32.user32.instance = LoadLibraryA("user32.dll"); if (!_glfw.win32.user32.instance) { _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, "Win32: Failed to load user32.dll"); return GLFW_FALSE; } _glfw.win32.user32.SetProcessDPIAware_ = (PFN_SetProcessDPIAware) GetProcAddress(_glfw.win32.user32.instance, "SetProcessDPIAware"); _glfw.win32.user32.ChangeWindowMessageFilterEx_ = (PFN_ChangeWindowMessageFilterEx) GetProcAddress(_glfw.win32.user32.instance, "ChangeWindowMessageFilterEx"); _glfw.win32.user32.EnableNonClientDpiScaling_ = (PFN_EnableNonClientDpiScaling) GetProcAddress(_glfw.win32.user32.instance, "EnableNonClientDpiScaling"); _glfw.win32.user32.SetProcessDpiAwarenessContext_ = (PFN_SetProcessDpiAwarenessContext) GetProcAddress(_glfw.win32.user32.instance, "SetProcessDpiAwarenessContext"); _glfw.win32.user32.GetDpiForWindow_ = (PFN_GetDpiForWindow) GetProcAddress(_glfw.win32.user32.instance, "GetDpiForWindow"); _glfw.win32.user32.AdjustWindowRectExForDpi_ = (PFN_AdjustWindowRectExForDpi) GetProcAddress(_glfw.win32.user32.instance, "AdjustWindowRectExForDpi"); _glfw.win32.user32.GetSystemMetricsForDpi_ = (PFN_GetSystemMetricsForDpi) GetProcAddress(_glfw.win32.user32.instance, "GetSystemMetricsForDpi"); _glfw.win32.dinput8.instance = LoadLibraryA("dinput8.dll"); if (_glfw.win32.dinput8.instance) { _glfw.win32.dinput8.Create = (PFN_DirectInput8Create) GetProcAddress(_glfw.win32.dinput8.instance, "DirectInput8Create"); } { int i; const char* names[] = { "xinput1_4.dll", "xinput1_3.dll", "xinput9_1_0.dll", "xinput1_2.dll", "xinput1_1.dll", NULL }; for (i = 0; names[i]; i++) { _glfw.win32.xinput.instance = LoadLibraryA(names[i]); if (_glfw.win32.xinput.instance) { _glfw.win32.xinput.GetCapabilities = (PFN_XInputGetCapabilities) GetProcAddress(_glfw.win32.xinput.instance, "XInputGetCapabilities"); _glfw.win32.xinput.GetState = (PFN_XInputGetState) GetProcAddress(_glfw.win32.xinput.instance, "XInputGetState"); break; } } } _glfw.win32.dwmapi.instance = LoadLibraryA("dwmapi.dll"); if (_glfw.win32.dwmapi.instance) { _glfw.win32.dwmapi.IsCompositionEnabled = (PFN_DwmIsCompositionEnabled) GetProcAddress(_glfw.win32.dwmapi.instance, "DwmIsCompositionEnabled"); _glfw.win32.dwmapi.Flush = (PFN_DwmFlush) GetProcAddress(_glfw.win32.dwmapi.instance, "DwmFlush"); _glfw.win32.dwmapi.EnableBlurBehindWindow = (PFN_DwmEnableBlurBehindWindow) GetProcAddress(_glfw.win32.dwmapi.instance, "DwmEnableBlurBehindWindow"); _glfw.win32.dwmapi.GetColorizationColor = (PFN_DwmGetColorizationColor) GetProcAddress(_glfw.win32.dwmapi.instance, "DwmGetColorizationColor"); } _glfw.win32.shcore.instance = LoadLibraryA("shcore.dll"); if (_glfw.win32.shcore.instance) { _glfw.win32.shcore.SetProcessDpiAwareness_ = (PFN_SetProcessDpiAwareness) GetProcAddress(_glfw.win32.shcore.instance, "SetProcessDpiAwareness"); _glfw.win32.shcore.GetDpiForMonitor_ = (PFN_GetDpiForMonitor) GetProcAddress(_glfw.win32.shcore.instance, "GetDpiForMonitor"); } _glfw.win32.ntdll.instance = LoadLibraryA("ntdll.dll"); if (_glfw.win32.ntdll.instance) { _glfw.win32.ntdll.RtlVerifyVersionInfo_ = (PFN_RtlVerifyVersionInfo) GetProcAddress(_glfw.win32.ntdll.instance, "RtlVerifyVersionInfo"); } return GLFW_TRUE; } // Unload used libraries (DLLs) // static void freeLibraries(void) { if (_glfw.win32.xinput.instance) FreeLibrary(_glfw.win32.xinput.instance); if (_glfw.win32.dinput8.instance) FreeLibrary(_glfw.win32.dinput8.instance); if (_glfw.win32.user32.instance) FreeLibrary(_glfw.win32.user32.instance); if (_glfw.win32.dwmapi.instance) FreeLibrary(_glfw.win32.dwmapi.instance); if (_glfw.win32.shcore.instance) FreeLibrary(_glfw.win32.shcore.instance); if (_glfw.win32.ntdll.instance) FreeLibrary(_glfw.win32.ntdll.instance); } // Create key code translation tables // static void createKeyTables(void) { int scancode; memset(_glfw.win32.keycodes, -1, sizeof(_glfw.win32.keycodes)); memset(_glfw.win32.scancodes, -1, sizeof(_glfw.win32.scancodes)); _glfw.win32.keycodes[0x00B] = GLFW_KEY_0; _glfw.win32.keycodes[0x002] = GLFW_KEY_1; _glfw.win32.keycodes[0x003] = GLFW_KEY_2; _glfw.win32.keycodes[0x004] = GLFW_KEY_3; _glfw.win32.keycodes[0x005] = GLFW_KEY_4; _glfw.win32.keycodes[0x006] = GLFW_KEY_5; _glfw.win32.keycodes[0x007] = GLFW_KEY_6; _glfw.win32.keycodes[0x008] = GLFW_KEY_7; _glfw.win32.keycodes[0x009] = GLFW_KEY_8; _glfw.win32.keycodes[0x00A] = GLFW_KEY_9; _glfw.win32.keycodes[0x01E] = GLFW_KEY_A; _glfw.win32.keycodes[0x030] = GLFW_KEY_B; _glfw.win32.keycodes[0x02E] = GLFW_KEY_C; _glfw.win32.keycodes[0x020] = GLFW_KEY_D; _glfw.win32.keycodes[0x012] = GLFW_KEY_E; _glfw.win32.keycodes[0x021] = GLFW_KEY_F; _glfw.win32.keycodes[0x022] = GLFW_KEY_G; _glfw.win32.keycodes[0x023] = GLFW_KEY_H; _glfw.win32.keycodes[0x017] = GLFW_KEY_I; _glfw.win32.keycodes[0x024] = GLFW_KEY_J; _glfw.win32.keycodes[0x025] = GLFW_KEY_K; _glfw.win32.keycodes[0x026] = GLFW_KEY_L; _glfw.win32.keycodes[0x032] = GLFW_KEY_M; _glfw.win32.keycodes[0x031] = GLFW_KEY_N; _glfw.win32.keycodes[0x018] = GLFW_KEY_O; _glfw.win32.keycodes[0x019] = GLFW_KEY_P; _glfw.win32.keycodes[0x010] = GLFW_KEY_Q; _glfw.win32.keycodes[0x013] = GLFW_KEY_R; _glfw.win32.keycodes[0x01F] = GLFW_KEY_S; _glfw.win32.keycodes[0x014] = GLFW_KEY_T; _glfw.win32.keycodes[0x016] = GLFW_KEY_U; _glfw.win32.keycodes[0x02F] = GLFW_KEY_V; _glfw.win32.keycodes[0x011] = GLFW_KEY_W; _glfw.win32.keycodes[0x02D] = GLFW_KEY_X; _glfw.win32.keycodes[0x015] = GLFW_KEY_Y; _glfw.win32.keycodes[0x02C] = GLFW_KEY_Z; _glfw.win32.keycodes[0x028] = GLFW_KEY_APOSTROPHE; _glfw.win32.keycodes[0x02B] = GLFW_KEY_BACKSLASH; _glfw.win32.keycodes[0x033] = GLFW_KEY_COMMA; _glfw.win32.keycodes[0x00D] = GLFW_KEY_EQUAL; _glfw.win32.keycodes[0x029] = GLFW_KEY_GRAVE_ACCENT; _glfw.win32.keycodes[0x01A] = GLFW_KEY_LEFT_BRACKET; _glfw.win32.keycodes[0x00C] = GLFW_KEY_MINUS; _glfw.win32.keycodes[0x034] = GLFW_KEY_PERIOD; _glfw.win32.keycodes[0x01B] = GLFW_KEY_RIGHT_BRACKET; _glfw.win32.keycodes[0x027] = GLFW_KEY_SEMICOLON; _glfw.win32.keycodes[0x035] = GLFW_KEY_SLASH; _glfw.win32.keycodes[0x056] = GLFW_KEY_WORLD_2; _glfw.win32.keycodes[0x00E] = GLFW_KEY_BACKSPACE; _glfw.win32.keycodes[0x153] = GLFW_KEY_DELETE; _glfw.win32.keycodes[0x14F] = GLFW_KEY_END; _glfw.win32.keycodes[0x01C] = GLFW_KEY_ENTER; _glfw.win32.keycodes[0x001] = GLFW_KEY_ESCAPE; _glfw.win32.keycodes[0x147] = GLFW_KEY_HOME; _glfw.win32.keycodes[0x152] = GLFW_KEY_INSERT; _glfw.win32.keycodes[0x15D] = GLFW_KEY_MENU; _glfw.win32.keycodes[0x151] = GLFW_KEY_PAGE_DOWN; _glfw.win32.keycodes[0x149] = GLFW_KEY_PAGE_UP; _glfw.win32.keycodes[0x045] = GLFW_KEY_PAUSE; _glfw.win32.keycodes[0x146] = GLFW_KEY_PAUSE; _glfw.win32.keycodes[0x039] = GLFW_KEY_SPACE; _glfw.win32.keycodes[0x00F] = GLFW_KEY_TAB; _glfw.win32.keycodes[0x03A] = GLFW_KEY_CAPS_LOCK; _glfw.win32.keycodes[0x145] = GLFW_KEY_NUM_LOCK; _glfw.win32.keycodes[0x046] = GLFW_KEY_SCROLL_LOCK; _glfw.win32.keycodes[0x03B] = GLFW_KEY_F1; _glfw.win32.keycodes[0x03C] = GLFW_KEY_F2; _glfw.win32.keycodes[0x03D] = GLFW_KEY_F3; _glfw.win32.keycodes[0x03E] = GLFW_KEY_F4; _glfw.win32.keycodes[0x03F] = GLFW_KEY_F5; _glfw.win32.keycodes[0x040] = GLFW_KEY_F6; _glfw.win32.keycodes[0x041] = GLFW_KEY_F7; _glfw.win32.keycodes[0x042] = GLFW_KEY_F8; _glfw.win32.keycodes[0x043] = GLFW_KEY_F9; _glfw.win32.keycodes[0x044] = GLFW_KEY_F10; _glfw.win32.keycodes[0x057] = GLFW_KEY_F11; _glfw.win32.keycodes[0x058] = GLFW_KEY_F12; _glfw.win32.keycodes[0x064] = GLFW_KEY_F13; _glfw.win32.keycodes[0x065] = GLFW_KEY_F14; _glfw.win32.keycodes[0x066] = GLFW_KEY_F15; _glfw.win32.keycodes[0x067] = GLFW_KEY_F16; _glfw.win32.keycodes[0x068] = GLFW_KEY_F17; _glfw.win32.keycodes[0x069] = GLFW_KEY_F18; _glfw.win32.keycodes[0x06A] = GLFW_KEY_F19; _glfw.win32.keycodes[0x06B] = GLFW_KEY_F20; _glfw.win32.keycodes[0x06C] = GLFW_KEY_F21; _glfw.win32.keycodes[0x06D] = GLFW_KEY_F22; _glfw.win32.keycodes[0x06E] = GLFW_KEY_F23; _glfw.win32.keycodes[0x076] = GLFW_KEY_F24; _glfw.win32.keycodes[0x038] = GLFW_KEY_LEFT_ALT; _glfw.win32.keycodes[0x01D] = GLFW_KEY_LEFT_CONTROL; _glfw.win32.keycodes[0x02A] = GLFW_KEY_LEFT_SHIFT; _glfw.win32.keycodes[0x15B] = GLFW_KEY_LEFT_SUPER; _glfw.win32.keycodes[0x137] = GLFW_KEY_PRINT_SCREEN; _glfw.win32.keycodes[0x138] = GLFW_KEY_RIGHT_ALT; _glfw.win32.keycodes[0x11D] = GLFW_KEY_RIGHT_CONTROL; _glfw.win32.keycodes[0x036] = GLFW_KEY_RIGHT_SHIFT; _glfw.win32.keycodes[0x15C] = GLFW_KEY_RIGHT_SUPER; _glfw.win32.keycodes[0x150] = GLFW_KEY_DOWN; _glfw.win32.keycodes[0x14B] = GLFW_KEY_LEFT; _glfw.win32.keycodes[0x14D] = GLFW_KEY_RIGHT; _glfw.win32.keycodes[0x148] = GLFW_KEY_UP; _glfw.win32.keycodes[0x052] = GLFW_KEY_KP_0; _glfw.win32.keycodes[0x04F] = GLFW_KEY_KP_1; _glfw.win32.keycodes[0x050] = GLFW_KEY_KP_2; _glfw.win32.keycodes[0x051] = GLFW_KEY_KP_3; _glfw.win32.keycodes[0x04B] = GLFW_KEY_KP_4; _glfw.win32.keycodes[0x04C] = GLFW_KEY_KP_5; _glfw.win32.keycodes[0x04D] = GLFW_KEY_KP_6; _glfw.win32.keycodes[0x047] = GLFW_KEY_KP_7; _glfw.win32.keycodes[0x048] = GLFW_KEY_KP_8; _glfw.win32.keycodes[0x049] = GLFW_KEY_KP_9; _glfw.win32.keycodes[0x04E] = GLFW_KEY_KP_ADD; _glfw.win32.keycodes[0x053] = GLFW_KEY_KP_DECIMAL; _glfw.win32.keycodes[0x135] = GLFW_KEY_KP_DIVIDE; _glfw.win32.keycodes[0x11C] = GLFW_KEY_KP_ENTER; _glfw.win32.keycodes[0x059] = GLFW_KEY_KP_EQUAL; _glfw.win32.keycodes[0x037] = GLFW_KEY_KP_MULTIPLY; _glfw.win32.keycodes[0x04A] = GLFW_KEY_KP_SUBTRACT; for (scancode = 0; scancode < 512; scancode++) { if (_glfw.win32.keycodes[scancode] > 0) _glfw.win32.scancodes[_glfw.win32.keycodes[scancode]] = scancode; } } // Creates a dummy window for behind-the-scenes work // static GLFWbool createHelperWindow(void) { MSG msg; _glfw.win32.helperWindowHandle = CreateWindowExW(WS_EX_OVERLAPPEDWINDOW, _GLFW_WNDCLASSNAME, L"GLFW message window", WS_CLIPSIBLINGS | WS_CLIPCHILDREN, 0, 0, 1, 1, NULL, NULL, GetModuleHandleW(NULL), NULL); if (!_glfw.win32.helperWindowHandle) { _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, "Win32: Failed to create helper window"); return GLFW_FALSE; } // HACK: The command to the first ShowWindow call is ignored if the parent // process passed along a STARTUPINFO, so clear that with a no-op call ShowWindow(_glfw.win32.helperWindowHandle, SW_HIDE); // Register for HID device notifications { DEV_BROADCAST_DEVICEINTERFACE_W dbi; ZeroMemory(&dbi, sizeof(dbi)); dbi.dbcc_size = sizeof(dbi); dbi.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE; dbi.dbcc_classguid = GUID_DEVINTERFACE_HID; _glfw.win32.deviceNotificationHandle = RegisterDeviceNotificationW(_glfw.win32.helperWindowHandle, (DEV_BROADCAST_HDR*) &dbi, DEVICE_NOTIFY_WINDOW_HANDLE); } while (PeekMessageW(&msg, _glfw.win32.helperWindowHandle, 0, 0, PM_REMOVE)) { TranslateMessage(&msg); DispatchMessageW(&msg); } return GLFW_TRUE; } ////////////////////////////////////////////////////////////////////////// ////// GLFW internal API ////// ////////////////////////////////////////////////////////////////////////// // Returns a wide string version of the specified UTF-8 string // WCHAR* _glfwCreateWideStringFromUTF8Win32(const char* source) { WCHAR* target; int count; count = MultiByteToWideChar(CP_UTF8, 0, source, -1, NULL, 0); if (!count) { _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, "Win32: Failed to convert string from UTF-8"); return NULL; } target = calloc(count, sizeof(WCHAR)); if (!MultiByteToWideChar(CP_UTF8, 0, source, -1, target, count)) { _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, "Win32: Failed to convert string from UTF-8"); free(target); return NULL; } return target; } // Returns a UTF-8 string version of the specified wide string // char* _glfwCreateUTF8FromWideStringWin32(const WCHAR* source) { char* target; int size; size = WideCharToMultiByte(CP_UTF8, 0, source, -1, NULL, 0, NULL, NULL); if (!size) { _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, "Win32: Failed to convert string to UTF-8"); return NULL; } target = calloc(size, 1); if (!WideCharToMultiByte(CP_UTF8, 0, source, -1, target, size, NULL, NULL)) { _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, "Win32: Failed to convert string to UTF-8"); free(target); return NULL; } return target; } // Reports the specified error, appending information about the last Win32 error // void _glfwInputErrorWin32(int error, const char* description) { WCHAR buffer[_GLFW_MESSAGE_SIZE] = L""; char message[_GLFW_MESSAGE_SIZE] = ""; FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_MAX_WIDTH_MASK, NULL, GetLastError() & 0xffff, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), buffer, sizeof(buffer) / sizeof(WCHAR), NULL); WideCharToMultiByte(CP_UTF8, 0, buffer, -1, message, sizeof(message), NULL, NULL); _glfwInputError(error, "%s: %s", description, message); } // Updates key names according to the current keyboard layout // void _glfwUpdateKeyNamesWin32(void) { int key; BYTE state[256] = {0}; memset(_glfw.win32.keynames, 0, sizeof(_glfw.win32.keynames)); for (key = GLFW_KEY_SPACE; key <= GLFW_KEY_LAST; key++) { UINT vk; int scancode, length; WCHAR chars[16]; scancode = _glfw.win32.scancodes[key]; if (scancode == -1) continue; if (key >= GLFW_KEY_KP_0 && key <= GLFW_KEY_KP_ADD) { const UINT vks[] = { VK_NUMPAD0, VK_NUMPAD1, VK_NUMPAD2, VK_NUMPAD3, VK_NUMPAD4, VK_NUMPAD5, VK_NUMPAD6, VK_NUMPAD7, VK_NUMPAD8, VK_NUMPAD9, VK_DECIMAL, VK_DIVIDE, VK_MULTIPLY, VK_SUBTRACT, VK_ADD }; vk = vks[key - GLFW_KEY_KP_0]; } else vk = MapVirtualKeyW(scancode, MAPVK_VSC_TO_VK); length = ToUnicode(vk, scancode, state, chars, sizeof(chars) / sizeof(WCHAR), 0); if (length == -1) { length = ToUnicode(vk, scancode, state, chars, sizeof(chars) / sizeof(WCHAR), 0); } if (length < 1) continue; WideCharToMultiByte(CP_UTF8, 0, chars, 1, _glfw.win32.keynames[key], sizeof(_glfw.win32.keynames[key]), NULL, NULL); } } // Replacement for IsWindowsVersionOrGreater, as we cannot rely on the // application having a correct embedded manifest // BOOL _glfwIsWindowsVersionOrGreaterWin32(WORD major, WORD minor, WORD sp) { OSVERSIONINFOEXW osvi = { sizeof(osvi), major, minor, 0, 0, {0}, sp }; DWORD mask = VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR; ULONGLONG cond = VerSetConditionMask(0, VER_MAJORVERSION, VER_GREATER_EQUAL); cond = VerSetConditionMask(cond, VER_MINORVERSION, VER_GREATER_EQUAL); cond = VerSetConditionMask(cond, VER_SERVICEPACKMAJOR, VER_GREATER_EQUAL); // HACK: Use RtlVerifyVersionInfo instead of VerifyVersionInfoW as the // latter lies unless the user knew to embed a non-default manifest // announcing support for Windows 10 via supportedOS GUID return RtlVerifyVersionInfo(&osvi, mask, cond) == 0; } // Checks whether we are on at least the specified build of Windows 10 // BOOL _glfwIsWindows10BuildOrGreaterWin32(WORD build) { OSVERSIONINFOEXW osvi = { sizeof(osvi), 10, 0, build }; DWORD mask = VER_MAJORVERSION | VER_MINORVERSION | VER_BUILDNUMBER; ULONGLONG cond = VerSetConditionMask(0, VER_MAJORVERSION, VER_GREATER_EQUAL); cond = VerSetConditionMask(cond, VER_MINORVERSION, VER_GREATER_EQUAL); cond = VerSetConditionMask(cond, VER_BUILDNUMBER, VER_GREATER_EQUAL); // HACK: Use RtlVerifyVersionInfo instead of VerifyVersionInfoW as the // latter lies unless the user knew to embed a non-default manifest // announcing support for Windows 10 via supportedOS GUID return RtlVerifyVersionInfo(&osvi, mask, cond) == 0; } ////////////////////////////////////////////////////////////////////////// ////// GLFW platform API ////// ////////////////////////////////////////////////////////////////////////// int _glfwPlatformInit(void) { // To make SetForegroundWindow work as we want, we need to fiddle // with the FOREGROUNDLOCKTIMEOUT system setting (we do this as early // as possible in the hope of still being the foreground process) SystemParametersInfoW(SPI_GETFOREGROUNDLOCKTIMEOUT, 0, &_glfw.win32.foregroundLockTimeout, 0); SystemParametersInfoW(SPI_SETFOREGROUNDLOCKTIMEOUT, 0, UIntToPtr(0), SPIF_SENDCHANGE); if (!loadLibraries()) return GLFW_FALSE; createKeyTables(); _glfwUpdateKeyNamesWin32(); if (_glfwIsWindows10CreatorsUpdateOrGreaterWin32()) SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2); else if (IsWindows8Point1OrGreater()) SetProcessDpiAwareness(PROCESS_PER_MONITOR_DPI_AWARE); else if (IsWindowsVistaOrGreater()) SetProcessDPIAware(); if (!_glfwRegisterWindowClassWin32()) return GLFW_FALSE; if (!createHelperWindow()) return GLFW_FALSE; _glfwInitTimerWin32(); _glfwInitJoysticksWin32(); _glfwPollMonitorsWin32(); return GLFW_TRUE; } void _glfwPlatformTerminate(void) { if (_glfw.win32.deviceNotificationHandle) UnregisterDeviceNotification(_glfw.win32.deviceNotificationHandle); if (_glfw.win32.helperWindowHandle) DestroyWindow(_glfw.win32.helperWindowHandle); _glfwUnregisterWindowClassWin32(); // Restore previous foreground lock timeout system setting SystemParametersInfoW(SPI_SETFOREGROUNDLOCKTIMEOUT, 0, UIntToPtr(_glfw.win32.foregroundLockTimeout), SPIF_SENDCHANGE); free(_glfw.win32.clipboardString); free(_glfw.win32.rawInput); _glfwTerminateWGL(); _glfwTerminateEGL(); _glfwTerminateJoysticksWin32(); freeLibraries(); } const char* _glfwPlatformGetVersionString(void) { return _GLFW_VERSION_NUMBER " Win32 WGL EGL OSMesa" #if defined(__MINGW32__) " MinGW" #elif defined(_MSC_VER) " VisualC" #endif #if defined(_GLFW_USE_HYBRID_HPG) || defined(_GLFW_USE_OPTIMUS_HPG) " hybrid-GPU" #endif #if defined(_GLFW_BUILD_DLL) " DLL" #endif ; } #endif #ifndef HEADER_GUARD_WIN32_JOYSTICK_C #define HEADER_GUARD_WIN32_JOYSTICK_C //======================================================================== // GLFW 3.3.7 Win32 - www.glfw.org //------------------------------------------------------------------------ // Copyright (c) 2002-2006 Marcus Geelnard // Copyright (c) 2006-2019 Camilla Löwy // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would // be appreciated but is not required. // // 2. Altered source versions must be plainly marked as such, and must not // be misrepresented as being the original software. // // 3. This notice may not be removed or altered from any source // distribution. // //======================================================================== // Please use C89 style variable declarations in this file because VS 2010 //======================================================================== #include #include #define _GLFW_TYPE_AXIS 0 #define _GLFW_TYPE_SLIDER 1 #define _GLFW_TYPE_BUTTON 2 #define _GLFW_TYPE_POV 3 // Data produced with DirectInput device object enumeration // typedef struct _GLFWobjenumWin32 { IDirectInputDevice8W* device; _GLFWjoyobjectWin32* objects; int objectCount; int axisCount; int sliderCount; int buttonCount; int povCount; } _GLFWobjenumWin32; // Define local copies of the necessary GUIDs // static const GUID _glfw_IID_IDirectInput8W = {0xbf798031,0x483a,0x4da2,{0xaa,0x99,0x5d,0x64,0xed,0x36,0x97,0x00}}; static const GUID _glfw_GUID_XAxis = {0xa36d02e0,0xc9f3,0x11cf,{0xbf,0xc7,0x44,0x45,0x53,0x54,0x00,0x00}}; static const GUID _glfw_GUID_YAxis = {0xa36d02e1,0xc9f3,0x11cf,{0xbf,0xc7,0x44,0x45,0x53,0x54,0x00,0x00}}; static const GUID _glfw_GUID_ZAxis = {0xa36d02e2,0xc9f3,0x11cf,{0xbf,0xc7,0x44,0x45,0x53,0x54,0x00,0x00}}; static const GUID _glfw_GUID_RxAxis = {0xa36d02f4,0xc9f3,0x11cf,{0xbf,0xc7,0x44,0x45,0x53,0x54,0x00,0x00}}; static const GUID _glfw_GUID_RyAxis = {0xa36d02f5,0xc9f3,0x11cf,{0xbf,0xc7,0x44,0x45,0x53,0x54,0x00,0x00}}; static const GUID _glfw_GUID_RzAxis = {0xa36d02e3,0xc9f3,0x11cf,{0xbf,0xc7,0x44,0x45,0x53,0x54,0x00,0x00}}; static const GUID _glfw_GUID_Slider = {0xa36d02e4,0xc9f3,0x11cf,{0xbf,0xc7,0x44,0x45,0x53,0x54,0x00,0x00}}; static const GUID _glfw_GUID_POV = {0xa36d02f2,0xc9f3,0x11cf,{0xbf,0xc7,0x44,0x45,0x53,0x54,0x00,0x00}}; #define IID_IDirectInput8W _glfw_IID_IDirectInput8W #define GUID_XAxis _glfw_GUID_XAxis #define GUID_YAxis _glfw_GUID_YAxis #define GUID_ZAxis _glfw_GUID_ZAxis #define GUID_RxAxis _glfw_GUID_RxAxis #define GUID_RyAxis _glfw_GUID_RyAxis #define GUID_RzAxis _glfw_GUID_RzAxis #define GUID_Slider _glfw_GUID_Slider #define GUID_POV _glfw_GUID_POV // Object data array for our clone of c_dfDIJoystick // Generated with https://github.com/elmindreda/c_dfDIJoystick2 // static DIOBJECTDATAFORMAT _glfwObjectDataFormats[] = { { &GUID_XAxis,DIJOFS_X,DIDFT_AXIS|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,DIDOI_ASPECTPOSITION }, { &GUID_YAxis,DIJOFS_Y,DIDFT_AXIS|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,DIDOI_ASPECTPOSITION }, { &GUID_ZAxis,DIJOFS_Z,DIDFT_AXIS|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,DIDOI_ASPECTPOSITION }, { &GUID_RxAxis,DIJOFS_RX,DIDFT_AXIS|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,DIDOI_ASPECTPOSITION }, { &GUID_RyAxis,DIJOFS_RY,DIDFT_AXIS|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,DIDOI_ASPECTPOSITION }, { &GUID_RzAxis,DIJOFS_RZ,DIDFT_AXIS|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,DIDOI_ASPECTPOSITION }, { &GUID_Slider,DIJOFS_SLIDER(0),DIDFT_AXIS|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,DIDOI_ASPECTPOSITION }, { &GUID_Slider,DIJOFS_SLIDER(1),DIDFT_AXIS|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,DIDOI_ASPECTPOSITION }, { &GUID_POV,DIJOFS_POV(0),DIDFT_POV|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, { &GUID_POV,DIJOFS_POV(1),DIDFT_POV|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, { &GUID_POV,DIJOFS_POV(2),DIDFT_POV|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, { &GUID_POV,DIJOFS_POV(3),DIDFT_POV|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, { NULL,DIJOFS_BUTTON(0),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, { NULL,DIJOFS_BUTTON(1),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, { NULL,DIJOFS_BUTTON(2),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, { NULL,DIJOFS_BUTTON(3),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, { NULL,DIJOFS_BUTTON(4),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, { NULL,DIJOFS_BUTTON(5),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, { NULL,DIJOFS_BUTTON(6),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, { NULL,DIJOFS_BUTTON(7),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, { NULL,DIJOFS_BUTTON(8),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, { NULL,DIJOFS_BUTTON(9),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, { NULL,DIJOFS_BUTTON(10),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, { NULL,DIJOFS_BUTTON(11),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, { NULL,DIJOFS_BUTTON(12),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, { NULL,DIJOFS_BUTTON(13),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, { NULL,DIJOFS_BUTTON(14),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, { NULL,DIJOFS_BUTTON(15),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, { NULL,DIJOFS_BUTTON(16),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, { NULL,DIJOFS_BUTTON(17),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, { NULL,DIJOFS_BUTTON(18),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, { NULL,DIJOFS_BUTTON(19),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, { NULL,DIJOFS_BUTTON(20),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, { NULL,DIJOFS_BUTTON(21),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, { NULL,DIJOFS_BUTTON(22),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, { NULL,DIJOFS_BUTTON(23),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, { NULL,DIJOFS_BUTTON(24),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, { NULL,DIJOFS_BUTTON(25),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, { NULL,DIJOFS_BUTTON(26),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, { NULL,DIJOFS_BUTTON(27),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, { NULL,DIJOFS_BUTTON(28),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, { NULL,DIJOFS_BUTTON(29),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, { NULL,DIJOFS_BUTTON(30),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, { NULL,DIJOFS_BUTTON(31),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, }; // Our clone of c_dfDIJoystick // static const DIDATAFORMAT _glfwDataFormat = { sizeof(DIDATAFORMAT), sizeof(DIOBJECTDATAFORMAT), DIDFT_ABSAXIS, sizeof(DIJOYSTATE), sizeof(_glfwObjectDataFormats) / sizeof(DIOBJECTDATAFORMAT), _glfwObjectDataFormats }; // Returns a description fitting the specified XInput capabilities // static const char* getDeviceDescription(const XINPUT_CAPABILITIES* xic) { switch (xic->SubType) { case XINPUT_DEVSUBTYPE_WHEEL: return "XInput Wheel"; case XINPUT_DEVSUBTYPE_ARCADE_STICK: return "XInput Arcade Stick"; case XINPUT_DEVSUBTYPE_FLIGHT_STICK: return "XInput Flight Stick"; case XINPUT_DEVSUBTYPE_DANCE_PAD: return "XInput Dance Pad"; case XINPUT_DEVSUBTYPE_GUITAR: return "XInput Guitar"; case XINPUT_DEVSUBTYPE_DRUM_KIT: return "XInput Drum Kit"; case XINPUT_DEVSUBTYPE_GAMEPAD: { if (xic->Flags & XINPUT_CAPS_WIRELESS) return "Wireless Xbox Controller"; else return "Xbox Controller"; } } return "Unknown XInput Device"; } // Lexically compare device objects // static int compareJoystickObjects(const void* first, const void* second) { const _GLFWjoyobjectWin32* fo = first; const _GLFWjoyobjectWin32* so = second; if (fo->type != so->type) return fo->type - so->type; return fo->offset - so->offset; } // Checks whether the specified device supports XInput // Technique from FDInputJoystickManager::IsXInputDeviceFast in ZDoom // static GLFWbool supportsXInput(const GUID* guid) { UINT i, count = 0; RAWINPUTDEVICELIST* ridl; GLFWbool result = GLFW_FALSE; if (GetRawInputDeviceList(NULL, &count, sizeof(RAWINPUTDEVICELIST)) != 0) return GLFW_FALSE; ridl = calloc(count, sizeof(RAWINPUTDEVICELIST)); if (GetRawInputDeviceList(ridl, &count, sizeof(RAWINPUTDEVICELIST)) == (UINT) -1) { free(ridl); return GLFW_FALSE; } for (i = 0; i < count; i++) { RID_DEVICE_INFO rdi; char name[256]; UINT size; if (ridl[i].dwType != RIM_TYPEHID) continue; ZeroMemory(&rdi, sizeof(rdi)); rdi.cbSize = sizeof(rdi); size = sizeof(rdi); if ((INT) GetRawInputDeviceInfoA(ridl[i].hDevice, RIDI_DEVICEINFO, &rdi, &size) == -1) { continue; } if (MAKELONG(rdi.hid.dwVendorId, rdi.hid.dwProductId) != (LONG) guid->Data1) continue; memset(name, 0, sizeof(name)); size = sizeof(name); if ((INT) GetRawInputDeviceInfoA(ridl[i].hDevice, RIDI_DEVICENAME, name, &size) == -1) { break; } name[sizeof(name) - 1] = '\0'; if (strstr(name, "IG_")) { result = GLFW_TRUE; break; } } free(ridl); return result; } // Frees all resources associated with the specified joystick // static void closeJoystick(_GLFWjoystick* js) { if (js->win32.device) { IDirectInputDevice8_Unacquire(js->win32.device); IDirectInputDevice8_Release(js->win32.device); } free(js->win32.objects); _glfwFreeJoystick(js); _glfwInputJoystick(js, GLFW_DISCONNECTED); } // DirectInput device object enumeration callback // Insights gleaned from SDL // static BOOL CALLBACK deviceObjectCallback(const DIDEVICEOBJECTINSTANCEW* doi, void* user) { _GLFWobjenumWin32* data = user; _GLFWjoyobjectWin32* object = data->objects + data->objectCount; if (DIDFT_GETTYPE(doi->dwType) & DIDFT_AXIS) { DIPROPRANGE dipr; if (memcmp(&doi->guidType, &GUID_Slider, sizeof(GUID)) == 0) object->offset = DIJOFS_SLIDER(data->sliderCount); else if (memcmp(&doi->guidType, &GUID_XAxis, sizeof(GUID)) == 0) object->offset = DIJOFS_X; else if (memcmp(&doi->guidType, &GUID_YAxis, sizeof(GUID)) == 0) object->offset = DIJOFS_Y; else if (memcmp(&doi->guidType, &GUID_ZAxis, sizeof(GUID)) == 0) object->offset = DIJOFS_Z; else if (memcmp(&doi->guidType, &GUID_RxAxis, sizeof(GUID)) == 0) object->offset = DIJOFS_RX; else if (memcmp(&doi->guidType, &GUID_RyAxis, sizeof(GUID)) == 0) object->offset = DIJOFS_RY; else if (memcmp(&doi->guidType, &GUID_RzAxis, sizeof(GUID)) == 0) object->offset = DIJOFS_RZ; else return DIENUM_CONTINUE; ZeroMemory(&dipr, sizeof(dipr)); dipr.diph.dwSize = sizeof(dipr); dipr.diph.dwHeaderSize = sizeof(dipr.diph); dipr.diph.dwObj = doi->dwType; dipr.diph.dwHow = DIPH_BYID; dipr.lMin = -32768; dipr.lMax = 32767; if (FAILED(IDirectInputDevice8_SetProperty(data->device, DIPROP_RANGE, &dipr.diph))) { return DIENUM_CONTINUE; } if (memcmp(&doi->guidType, &GUID_Slider, sizeof(GUID)) == 0) { object->type = _GLFW_TYPE_SLIDER; data->sliderCount++; } else { object->type = _GLFW_TYPE_AXIS; data->axisCount++; } } else if (DIDFT_GETTYPE(doi->dwType) & DIDFT_BUTTON) { object->offset = DIJOFS_BUTTON(data->buttonCount); object->type = _GLFW_TYPE_BUTTON; data->buttonCount++; } else if (DIDFT_GETTYPE(doi->dwType) & DIDFT_POV) { object->offset = DIJOFS_POV(data->povCount); object->type = _GLFW_TYPE_POV; data->povCount++; } data->objectCount++; return DIENUM_CONTINUE; } // DirectInput device enumeration callback // static BOOL CALLBACK deviceCallback(const DIDEVICEINSTANCE* di, void* user) { int jid = 0; DIDEVCAPS dc; DIPROPDWORD dipd; IDirectInputDevice8W* device; //< @r-lyeh +W (tcc) _GLFWobjenumWin32 data; _GLFWjoystick* js; char guid[33]; char name[256]; for (jid = 0; jid <= GLFW_JOYSTICK_LAST; jid++) { js = _glfw.joysticks + jid; if (js->present) { if (memcmp(&js->win32.guid, &di->guidInstance, sizeof(GUID)) == 0) return DIENUM_CONTINUE; } } if (supportsXInput(&di->guidProduct)) return DIENUM_CONTINUE; if (FAILED(IDirectInput8_CreateDevice(_glfw.win32.dinput8.api, &di->guidInstance, &device, NULL))) { _glfwInputError(GLFW_PLATFORM_ERROR, "Win32: Failed to create device"); return DIENUM_CONTINUE; } if (FAILED(IDirectInputDevice8_SetDataFormat(device, &_glfwDataFormat))) { _glfwInputError(GLFW_PLATFORM_ERROR, "Win32: Failed to set device data format"); IDirectInputDevice8_Release(device); return DIENUM_CONTINUE; } ZeroMemory(&dc, sizeof(dc)); dc.dwSize = sizeof(dc); if (FAILED(IDirectInputDevice8_GetCapabilities(device, &dc))) { _glfwInputError(GLFW_PLATFORM_ERROR, "Win32: Failed to query device capabilities"); IDirectInputDevice8_Release(device); return DIENUM_CONTINUE; } ZeroMemory(&dipd, sizeof(dipd)); dipd.diph.dwSize = sizeof(dipd); dipd.diph.dwHeaderSize = sizeof(dipd.diph); dipd.diph.dwHow = DIPH_DEVICE; dipd.dwData = DIPROPAXISMODE_ABS; if (FAILED(IDirectInputDevice8_SetProperty(device, DIPROP_AXISMODE, &dipd.diph))) { _glfwInputError(GLFW_PLATFORM_ERROR, "Win32: Failed to set device axis mode"); IDirectInputDevice8_Release(device); return DIENUM_CONTINUE; } memset(&data, 0, sizeof(data)); data.device = device; data.objects = calloc(dc.dwAxes + (size_t) dc.dwButtons + dc.dwPOVs, sizeof(_GLFWjoyobjectWin32)); if (FAILED(IDirectInputDevice8_EnumObjects(device, deviceObjectCallback, &data, DIDFT_AXIS | DIDFT_BUTTON | DIDFT_POV))) { _glfwInputError(GLFW_PLATFORM_ERROR, "Win32: Failed to enumerate device objects"); IDirectInputDevice8_Release(device); free(data.objects); return DIENUM_CONTINUE; } qsort(data.objects, data.objectCount, sizeof(_GLFWjoyobjectWin32), compareJoystickObjects); if (!WideCharToMultiByte(CP_UTF8, 0, di->tszInstanceName, -1, name, sizeof(name), NULL, NULL)) { _glfwInputError(GLFW_PLATFORM_ERROR, "Win32: Failed to convert joystick name to UTF-8"); IDirectInputDevice8_Release(device); free(data.objects); return DIENUM_STOP; } // Generate a joystick GUID that matches the SDL 2.0.5+ one if (memcmp(&di->guidProduct.Data4[2], "PIDVID", 6) == 0) { sprintf(guid, "03000000%02x%02x0000%02x%02x000000000000", (uint8_t) di->guidProduct.Data1, (uint8_t) (di->guidProduct.Data1 >> 8), (uint8_t) (di->guidProduct.Data1 >> 16), (uint8_t) (di->guidProduct.Data1 >> 24)); } else { sprintf(guid, "05000000%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x00", name[0], name[1], name[2], name[3], name[4], name[5], name[6], name[7], name[8], name[9], name[10]); } js = _glfwAllocJoystick(name, guid, data.axisCount + data.sliderCount, data.buttonCount, data.povCount); if (!js) { IDirectInputDevice8_Release(device); free(data.objects); return DIENUM_STOP; } js->win32.device = device; js->win32.guid = di->guidInstance; js->win32.objects = data.objects; js->win32.objectCount = data.objectCount; _glfwInputJoystick(js, GLFW_CONNECTED); return DIENUM_CONTINUE; } ////////////////////////////////////////////////////////////////////////// ////// GLFW internal API ////// ////////////////////////////////////////////////////////////////////////// // Initialize joystick interface // void _glfwInitJoysticksWin32(void) { if (_glfw.win32.dinput8.instance) { if (FAILED(DirectInput8Create(GetModuleHandleW(NULL), DIRECTINPUT_VERSION, &IID_IDirectInput8W, (void**) &_glfw.win32.dinput8.api, NULL))) { _glfwInputError(GLFW_PLATFORM_ERROR, "Win32: Failed to create interface"); } } _glfwDetectJoystickConnectionWin32(); } // Close all opened joystick handles // void _glfwTerminateJoysticksWin32(void) { int jid; for (jid = GLFW_JOYSTICK_1; jid <= GLFW_JOYSTICK_LAST; jid++) closeJoystick(_glfw.joysticks + jid); if (_glfw.win32.dinput8.api) IDirectInput8_Release(_glfw.win32.dinput8.api); } // Checks for new joysticks after DBT_DEVICEARRIVAL // void _glfwDetectJoystickConnectionWin32(void) { if (_glfw.win32.xinput.instance) { DWORD index; for (index = 0; index < XUSER_MAX_COUNT; index++) { int jid; char guid[33]; XINPUT_CAPABILITIES xic; _GLFWjoystick* js; for (jid = 0; jid <= GLFW_JOYSTICK_LAST; jid++) { if (_glfw.joysticks[jid].present && _glfw.joysticks[jid].win32.device == NULL && _glfw.joysticks[jid].win32.index == index) { break; } } if (jid <= GLFW_JOYSTICK_LAST) continue; if (XInputGetCapabilities(index, 0, &xic) != ERROR_SUCCESS) continue; // Generate a joystick GUID that matches the SDL 2.0.5+ one sprintf(guid, "78696e707574%02x000000000000000000", xic.SubType & 0xff); js = _glfwAllocJoystick(getDeviceDescription(&xic), guid, 6, 10, 1); if (!js) continue; js->win32.index = index; _glfwInputJoystick(js, GLFW_CONNECTED); } } if (_glfw.win32.dinput8.api) { if (FAILED(IDirectInput8_EnumDevices(_glfw.win32.dinput8.api, DI8DEVCLASS_GAMECTRL, deviceCallback, NULL, DIEDFL_ALLDEVICES))) { _glfwInputError(GLFW_PLATFORM_ERROR, "Failed to enumerate DirectInput8 devices"); return; } } } // Checks for joystick disconnection after DBT_DEVICEREMOVECOMPLETE // void _glfwDetectJoystickDisconnectionWin32(void) { int jid; for (jid = 0; jid <= GLFW_JOYSTICK_LAST; jid++) { _GLFWjoystick* js = _glfw.joysticks + jid; if (js->present) _glfwPlatformPollJoystick(js, _GLFW_POLL_PRESENCE); } } ////////////////////////////////////////////////////////////////////////// ////// GLFW platform API ////// ////////////////////////////////////////////////////////////////////////// int _glfwPlatformPollJoystick(_GLFWjoystick* js, int mode) { if (js->win32.device) { int i, ai = 0, bi = 0, pi = 0; HRESULT result; DIJOYSTATE state; IDirectInputDevice8_Poll(js->win32.device); result = IDirectInputDevice8_GetDeviceState(js->win32.device, sizeof(state), &state); if (result == DIERR_NOTACQUIRED || result == DIERR_INPUTLOST) { IDirectInputDevice8_Acquire(js->win32.device); IDirectInputDevice8_Poll(js->win32.device); result = IDirectInputDevice8_GetDeviceState(js->win32.device, sizeof(state), &state); } if (FAILED(result)) { closeJoystick(js); return GLFW_FALSE; } if (mode == _GLFW_POLL_PRESENCE) return GLFW_TRUE; for (i = 0; i < js->win32.objectCount; i++) { const void* data = (char*) &state + js->win32.objects[i].offset; switch (js->win32.objects[i].type) { case _GLFW_TYPE_AXIS: case _GLFW_TYPE_SLIDER: { const float value = (*((LONG*) data) + 0.5f) / 32767.5f; _glfwInputJoystickAxis(js, ai, value); ai++; break; } case _GLFW_TYPE_BUTTON: { const char value = (*((BYTE*) data) & 0x80) != 0; _glfwInputJoystickButton(js, bi, value); bi++; break; } case _GLFW_TYPE_POV: { const int states[9] = { GLFW_HAT_UP, GLFW_HAT_RIGHT_UP, GLFW_HAT_RIGHT, GLFW_HAT_RIGHT_DOWN, GLFW_HAT_DOWN, GLFW_HAT_LEFT_DOWN, GLFW_HAT_LEFT, GLFW_HAT_LEFT_UP, GLFW_HAT_CENTERED }; // Screams of horror are appropriate at this point int stateIndex = LOWORD(*(DWORD*) data) / (45 * DI_DEGREES); if (stateIndex < 0 || stateIndex > 8) stateIndex = 8; _glfwInputJoystickHat(js, pi, states[stateIndex]); pi++; break; } } } } else { int i, dpad = 0; DWORD result; XINPUT_STATE xis; const WORD buttons[10] = { XINPUT_GAMEPAD_A, XINPUT_GAMEPAD_B, XINPUT_GAMEPAD_X, XINPUT_GAMEPAD_Y, XINPUT_GAMEPAD_LEFT_SHOULDER, XINPUT_GAMEPAD_RIGHT_SHOULDER, XINPUT_GAMEPAD_BACK, XINPUT_GAMEPAD_START, XINPUT_GAMEPAD_LEFT_THUMB, XINPUT_GAMEPAD_RIGHT_THUMB }; result = XInputGetState(js->win32.index, &xis); if (result != ERROR_SUCCESS) { if (result == ERROR_DEVICE_NOT_CONNECTED) closeJoystick(js); return GLFW_FALSE; } if (mode == _GLFW_POLL_PRESENCE) return GLFW_TRUE; _glfwInputJoystickAxis(js, 0, (xis.Gamepad.sThumbLX + 0.5f) / 32767.5f); _glfwInputJoystickAxis(js, 1, -(xis.Gamepad.sThumbLY + 0.5f) / 32767.5f); _glfwInputJoystickAxis(js, 2, (xis.Gamepad.sThumbRX + 0.5f) / 32767.5f); _glfwInputJoystickAxis(js, 3, -(xis.Gamepad.sThumbRY + 0.5f) / 32767.5f); _glfwInputJoystickAxis(js, 4, xis.Gamepad.bLeftTrigger / 127.5f - 1.f); _glfwInputJoystickAxis(js, 5, xis.Gamepad.bRightTrigger / 127.5f - 1.f); for (i = 0; i < 10; i++) { const char value = (xis.Gamepad.wButtons & buttons[i]) ? 1 : 0; _glfwInputJoystickButton(js, i, value); } if (xis.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_UP) dpad |= GLFW_HAT_UP; if (xis.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_RIGHT) dpad |= GLFW_HAT_RIGHT; if (xis.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_DOWN) dpad |= GLFW_HAT_DOWN; if (xis.Gamepad.wButtons & XINPUT_GAMEPAD_DPAD_LEFT) dpad |= GLFW_HAT_LEFT; _glfwInputJoystickHat(js, 0, dpad); } return GLFW_TRUE; } void _glfwPlatformUpdateGamepadGUID(char* guid) { if (strcmp(guid + 20, "504944564944") == 0) { char original[33]; strncpy(original, guid, sizeof(original) - 1); sprintf(guid, "03000000%.4s0000%.4s000000000000", original, original + 4); } } #endif #ifndef HEADER_GUARD_WIN32_MONITOR_C #define HEADER_GUARD_WIN32_MONITOR_C //======================================================================== // GLFW 3.3.7 Win32 - www.glfw.org //------------------------------------------------------------------------ // Copyright (c) 2002-2006 Marcus Geelnard // Copyright (c) 2006-2019 Camilla Löwy // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would // be appreciated but is not required. // // 2. Altered source versions must be plainly marked as such, and must not // be misrepresented as being the original software. // // 3. This notice may not be removed or altered from any source // distribution. // //======================================================================== // Please use C89 style variable declarations in this file because VS 2010 //======================================================================== #include #include #include #include #include // Callback for EnumDisplayMonitors in createMonitor // static BOOL CALLBACK monitorCallback(HMONITOR handle, HDC dc, RECT* rect, LPARAM data) { #ifdef __TINYC__ //< @r-lyeh MONITORINFOEXW mi = { sizeof(MONITORINFOEXW) }; //< @r-lyeh #else //< @r-lyeh MONITORINFOEXW mi; ZeroMemory(&mi, sizeof(mi)); mi.cbSize = sizeof(mi); #endif //< @r-lyeh if (GetMonitorInfoW(handle, (MONITORINFO*) &mi)) { _GLFWmonitor* monitor = (_GLFWmonitor*) data; if (wcscmp(mi.szDevice, monitor->win32.adapterName) == 0) monitor->win32.handle = handle; } return TRUE; } // Create monitor from an adapter and (optionally) a display // static _GLFWmonitor* createMonitor(DISPLAY_DEVICEW* adapter, DISPLAY_DEVICEW* display) { _GLFWmonitor* monitor; int widthMM, heightMM; char* name; HDC dc; DEVMODEW dm; RECT rect; if (display) name = _glfwCreateUTF8FromWideStringWin32(display->DeviceString); else name = _glfwCreateUTF8FromWideStringWin32(adapter->DeviceString); if (!name) return NULL; ZeroMemory(&dm, sizeof(dm)); dm.dmSize = sizeof(dm); EnumDisplaySettingsW(adapter->DeviceName, ENUM_CURRENT_SETTINGS, &dm); dc = CreateDCW(L"DISPLAY", adapter->DeviceName, NULL, NULL); if (IsWindows8Point1OrGreater()) { widthMM = GetDeviceCaps(dc, HORZSIZE); heightMM = GetDeviceCaps(dc, VERTSIZE); } else { widthMM = (int) (dm.dmPelsWidth * 25.4f / GetDeviceCaps(dc, LOGPIXELSX)); heightMM = (int) (dm.dmPelsHeight * 25.4f / GetDeviceCaps(dc, LOGPIXELSY)); } DeleteDC(dc); monitor = _glfwAllocMonitor(name, widthMM, heightMM); free(name); if (adapter->StateFlags & DISPLAY_DEVICE_MODESPRUNED) monitor->win32.modesPruned = GLFW_TRUE; wcscpy(monitor->win32.adapterName, adapter->DeviceName); WideCharToMultiByte(CP_UTF8, 0, adapter->DeviceName, -1, monitor->win32.publicAdapterName, sizeof(monitor->win32.publicAdapterName), NULL, NULL); if (display) { wcscpy(monitor->win32.displayName, display->DeviceName); WideCharToMultiByte(CP_UTF8, 0, display->DeviceName, -1, monitor->win32.publicDisplayName, sizeof(monitor->win32.publicDisplayName), NULL, NULL); } rect.left = dm.dmPosition.x; rect.top = dm.dmPosition.y; rect.right = dm.dmPosition.x + dm.dmPelsWidth; rect.bottom = dm.dmPosition.y + dm.dmPelsHeight; EnumDisplayMonitors(NULL, &rect, monitorCallback, (LPARAM) monitor); return monitor; } ////////////////////////////////////////////////////////////////////////// ////// GLFW internal API ////// ////////////////////////////////////////////////////////////////////////// // Poll for changes in the set of connected monitors // void _glfwPollMonitorsWin32(void) { int i, disconnectedCount; _GLFWmonitor** disconnected = NULL; DWORD adapterIndex, displayIndex; DISPLAY_DEVICEW adapter, display; _GLFWmonitor* monitor; disconnectedCount = _glfw.monitorCount; if (disconnectedCount) { disconnected = calloc(_glfw.monitorCount, sizeof(_GLFWmonitor*)); memcpy(disconnected, _glfw.monitors, _glfw.monitorCount * sizeof(_GLFWmonitor*)); } for (adapterIndex = 0; ; adapterIndex++) { int type = _GLFW_INSERT_LAST; ZeroMemory(&adapter, sizeof(adapter)); adapter.cb = sizeof(adapter); if (!EnumDisplayDevicesW(NULL, adapterIndex, &adapter, 0)) break; if (!(adapter.StateFlags & DISPLAY_DEVICE_ACTIVE)) continue; if (adapter.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE) type = _GLFW_INSERT_FIRST; for (displayIndex = 0; ; displayIndex++) { ZeroMemory(&display, sizeof(display)); display.cb = sizeof(display); if (!EnumDisplayDevicesW(adapter.DeviceName, displayIndex, &display, 0)) break; if (!(display.StateFlags & DISPLAY_DEVICE_ACTIVE)) continue; for (i = 0; i < disconnectedCount; i++) { if (disconnected[i] && wcscmp(disconnected[i]->win32.displayName, display.DeviceName) == 0) { disconnected[i] = NULL; // handle may have changed, update EnumDisplayMonitors(NULL, NULL, monitorCallback, (LPARAM) _glfw.monitors[i]); break; } } if (i < disconnectedCount) continue; monitor = createMonitor(&adapter, &display); if (!monitor) { free(disconnected); return; } _glfwInputMonitor(monitor, GLFW_CONNECTED, type); type = _GLFW_INSERT_LAST; } // HACK: If an active adapter does not have any display devices // (as sometimes happens), add it directly as a monitor if (displayIndex == 0) { for (i = 0; i < disconnectedCount; i++) { if (disconnected[i] && wcscmp(disconnected[i]->win32.adapterName, adapter.DeviceName) == 0) { disconnected[i] = NULL; break; } } if (i < disconnectedCount) continue; monitor = createMonitor(&adapter, NULL); if (!monitor) { free(disconnected); return; } _glfwInputMonitor(monitor, GLFW_CONNECTED, type); } } for (i = 0; i < disconnectedCount; i++) { if (disconnected[i]) _glfwInputMonitor(disconnected[i], GLFW_DISCONNECTED, 0); } free(disconnected); } // Change the current video mode // void _glfwSetVideoModeWin32(_GLFWmonitor* monitor, const GLFWvidmode* desired) { GLFWvidmode current; const GLFWvidmode* best; DEVMODEW dm; LONG result; best = _glfwChooseVideoMode(monitor, desired); _glfwPlatformGetVideoMode(monitor, ¤t); if (_glfwCompareVideoModes(¤t, best) == 0) return; ZeroMemory(&dm, sizeof(dm)); dm.dmSize = sizeof(dm); dm.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT | DM_BITSPERPEL | DM_DISPLAYFREQUENCY; dm.dmPelsWidth = best->width; dm.dmPelsHeight = best->height; dm.dmBitsPerPel = best->redBits + best->greenBits + best->blueBits; dm.dmDisplayFrequency = best->refreshRate; if (dm.dmBitsPerPel < 15 || dm.dmBitsPerPel >= 24) dm.dmBitsPerPel = 32; result = ChangeDisplaySettingsExW(monitor->win32.adapterName, &dm, NULL, CDS_FULLSCREEN, NULL); if (result == DISP_CHANGE_SUCCESSFUL) monitor->win32.modeChanged = GLFW_TRUE; else { const char* description = "Unknown error"; if (result == DISP_CHANGE_BADDUALVIEW) description = "The system uses DualView"; else if (result == DISP_CHANGE_BADFLAGS) description = "Invalid flags"; else if (result == DISP_CHANGE_BADMODE) description = "Graphics mode not supported"; else if (result == DISP_CHANGE_BADPARAM) description = "Invalid parameter"; else if (result == DISP_CHANGE_FAILED) description = "Graphics mode failed"; else if (result == DISP_CHANGE_NOTUPDATED) description = "Failed to write to registry"; else if (result == DISP_CHANGE_RESTART) description = "Computer restart required"; _glfwInputError(GLFW_PLATFORM_ERROR, "Win32: Failed to set video mode: %s", description); } } // Restore the previously saved (original) video mode // void _glfwRestoreVideoModeWin32(_GLFWmonitor* monitor) { if (monitor->win32.modeChanged) { ChangeDisplaySettingsExW(monitor->win32.adapterName, NULL, NULL, CDS_FULLSCREEN, NULL); monitor->win32.modeChanged = GLFW_FALSE; } } void _glfwGetMonitorContentScaleWin32(HMONITOR handle, float* xscale, float* yscale) { UINT xdpi, ydpi; if (xscale) *xscale = 0.f; if (yscale) *yscale = 0.f; if (IsWindows8Point1OrGreater()) { if (GetDpiForMonitor(handle, MDT_EFFECTIVE_DPI, &xdpi, &ydpi) != S_OK) { _glfwInputError(GLFW_PLATFORM_ERROR, "Win32: Failed to query monitor DPI"); return; } } else { const HDC dc = GetDC(NULL); xdpi = GetDeviceCaps(dc, LOGPIXELSX); ydpi = GetDeviceCaps(dc, LOGPIXELSY); ReleaseDC(NULL, dc); } if (xscale) *xscale = xdpi / (float) USER_DEFAULT_SCREEN_DPI; if (yscale) *yscale = ydpi / (float) USER_DEFAULT_SCREEN_DPI; } ////////////////////////////////////////////////////////////////////////// ////// GLFW platform API ////// ////////////////////////////////////////////////////////////////////////// void _glfwPlatformFreeMonitor(_GLFWmonitor* monitor) { } void _glfwPlatformGetMonitorPos(_GLFWmonitor* monitor, int* xpos, int* ypos) { DEVMODEW dm; ZeroMemory(&dm, sizeof(dm)); dm.dmSize = sizeof(dm); EnumDisplaySettingsExW(monitor->win32.adapterName, ENUM_CURRENT_SETTINGS, &dm, EDS_ROTATEDMODE); if (xpos) *xpos = dm.dmPosition.x; if (ypos) *ypos = dm.dmPosition.y; } void _glfwPlatformGetMonitorContentScale(_GLFWmonitor* monitor, float* xscale, float* yscale) { _glfwGetMonitorContentScaleWin32(monitor->win32.handle, xscale, yscale); } void _glfwPlatformGetMonitorWorkarea(_GLFWmonitor* monitor, int* xpos, int* ypos, int* width, int* height) { MONITORINFO mi = { sizeof(mi) }; GetMonitorInfoW(monitor->win32.handle, &mi); if (xpos) *xpos = mi.rcWork.left; if (ypos) *ypos = mi.rcWork.top; if (width) *width = mi.rcWork.right - mi.rcWork.left; if (height) *height = mi.rcWork.bottom - mi.rcWork.top; } GLFWvidmode* _glfwPlatformGetVideoModes(_GLFWmonitor* monitor, int* count) { int modeIndex = 0, size = 0; GLFWvidmode* result = NULL; *count = 0; for (;;) { int i; GLFWvidmode mode; DEVMODEW dm; ZeroMemory(&dm, sizeof(dm)); dm.dmSize = sizeof(dm); if (!EnumDisplaySettingsW(monitor->win32.adapterName, modeIndex, &dm)) break; modeIndex++; // Skip modes with less than 15 BPP if (dm.dmBitsPerPel < 15) continue; mode.width = dm.dmPelsWidth; mode.height = dm.dmPelsHeight; mode.refreshRate = dm.dmDisplayFrequency; _glfwSplitBPP(dm.dmBitsPerPel, &mode.redBits, &mode.greenBits, &mode.blueBits); for (i = 0; i < *count; i++) { if (_glfwCompareVideoModes(result + i, &mode) == 0) break; } // Skip duplicate modes if (i < *count) continue; if (monitor->win32.modesPruned) { // Skip modes not supported by the connected displays if (ChangeDisplaySettingsExW(monitor->win32.adapterName, &dm, NULL, CDS_TEST, NULL) != DISP_CHANGE_SUCCESSFUL) { continue; } } if (*count == size) { size += 128; result = (GLFWvidmode*) realloc(result, size * sizeof(GLFWvidmode)); } (*count)++; result[*count - 1] = mode; } if (!*count) { // HACK: Report the current mode if no valid modes were found result = calloc(1, sizeof(GLFWvidmode)); _glfwPlatformGetVideoMode(monitor, result); *count = 1; } return result; } void _glfwPlatformGetVideoMode(_GLFWmonitor* monitor, GLFWvidmode* mode) { DEVMODEW dm; ZeroMemory(&dm, sizeof(dm)); dm.dmSize = sizeof(dm); EnumDisplaySettingsW(monitor->win32.adapterName, ENUM_CURRENT_SETTINGS, &dm); mode->width = dm.dmPelsWidth; mode->height = dm.dmPelsHeight; mode->refreshRate = dm.dmDisplayFrequency; _glfwSplitBPP(dm.dmBitsPerPel, &mode->redBits, &mode->greenBits, &mode->blueBits); } GLFWbool _glfwPlatformGetGammaRamp(_GLFWmonitor* monitor, GLFWgammaramp* ramp) { HDC dc; WORD values[3][256]; dc = CreateDCW(L"DISPLAY", monitor->win32.adapterName, NULL, NULL); GetDeviceGammaRamp(dc, values); DeleteDC(dc); _glfwAllocGammaArrays(ramp, 256); memcpy(ramp->red, values[0], sizeof(values[0])); memcpy(ramp->green, values[1], sizeof(values[1])); memcpy(ramp->blue, values[2], sizeof(values[2])); return GLFW_TRUE; } void _glfwPlatformSetGammaRamp(_GLFWmonitor* monitor, const GLFWgammaramp* ramp) { HDC dc; WORD values[3][256]; if (ramp->size != 256) { _glfwInputError(GLFW_PLATFORM_ERROR, "Win32: Gamma ramp size must be 256"); return; } memcpy(values[0], ramp->red, sizeof(values[0])); memcpy(values[1], ramp->green, sizeof(values[1])); memcpy(values[2], ramp->blue, sizeof(values[2])); dc = CreateDCW(L"DISPLAY", monitor->win32.adapterName, NULL, NULL); SetDeviceGammaRamp(dc, values); DeleteDC(dc); } ////////////////////////////////////////////////////////////////////////// ////// GLFW native API ////// ////////////////////////////////////////////////////////////////////////// GLFWAPI const char* glfwGetWin32Adapter(GLFWmonitor* handle) { _GLFWmonitor* monitor = (_GLFWmonitor*) handle; _GLFW_REQUIRE_INIT_OR_RETURN(NULL); return monitor->win32.publicAdapterName; } GLFWAPI const char* glfwGetWin32Monitor(GLFWmonitor* handle) { _GLFWmonitor* monitor = (_GLFWmonitor*) handle; _GLFW_REQUIRE_INIT_OR_RETURN(NULL); return monitor->win32.publicDisplayName; } #endif #ifndef HEADER_GUARD_WIN32_TIME_C #define HEADER_GUARD_WIN32_TIME_C //======================================================================== // GLFW 3.3.7 Win32 - www.glfw.org //------------------------------------------------------------------------ // Copyright (c) 2002-2006 Marcus Geelnard // Copyright (c) 2006-2017 Camilla Löwy // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would // be appreciated but is not required. // // 2. Altered source versions must be plainly marked as such, and must not // be misrepresented as being the original software. // // 3. This notice may not be removed or altered from any source // distribution. // //======================================================================== // Please use C89 style variable declarations in this file because VS 2010 //======================================================================== ////////////////////////////////////////////////////////////////////////// ////// GLFW internal API ////// ////////////////////////////////////////////////////////////////////////// // Initialise timer // void _glfwInitTimerWin32(void) { QueryPerformanceFrequency((LARGE_INTEGER*) &_glfw.timer.win32.frequency); } ////////////////////////////////////////////////////////////////////////// ////// GLFW platform API ////// ////////////////////////////////////////////////////////////////////////// uint64_t _glfwPlatformGetTimerValue(void) { uint64_t value; QueryPerformanceCounter((LARGE_INTEGER*) &value); return value; } uint64_t _glfwPlatformGetTimerFrequency(void) { return _glfw.timer.win32.frequency; } #endif #ifndef HEADER_GUARD_WIN32_THREAD_C #define HEADER_GUARD_WIN32_THREAD_C //======================================================================== // GLFW 3.3.7 Win32 - www.glfw.org //------------------------------------------------------------------------ // Copyright (c) 2002-2006 Marcus Geelnard // Copyright (c) 2006-2017 Camilla Löwy // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would // be appreciated but is not required. // // 2. Altered source versions must be plainly marked as such, and must not // be misrepresented as being the original software. // // 3. This notice may not be removed or altered from any source // distribution. // //======================================================================== // Please use C89 style variable declarations in this file because VS 2010 //======================================================================== #include ////////////////////////////////////////////////////////////////////////// ////// GLFW platform API ////// ////////////////////////////////////////////////////////////////////////// GLFWbool _glfwPlatformCreateTls(_GLFWtls* tls) { assert(tls->win32.allocated == GLFW_FALSE); tls->win32.index = TlsAlloc(); if (tls->win32.index == TLS_OUT_OF_INDEXES) { _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, "Win32: Failed to allocate TLS index"); return GLFW_FALSE; } tls->win32.allocated = GLFW_TRUE; return GLFW_TRUE; } void _glfwPlatformDestroyTls(_GLFWtls* tls) { if (tls->win32.allocated) TlsFree(tls->win32.index); memset(tls, 0, sizeof(_GLFWtls)); } void* _glfwPlatformGetTls(_GLFWtls* tls) { assert(tls->win32.allocated == GLFW_TRUE); return TlsGetValue(tls->win32.index); } void _glfwPlatformSetTls(_GLFWtls* tls, void* value) { assert(tls->win32.allocated == GLFW_TRUE); TlsSetValue(tls->win32.index, value); } GLFWbool _glfwPlatformCreateMutex(_GLFWmutex* mutex) { assert(mutex->win32.allocated == GLFW_FALSE); InitializeCriticalSection(&mutex->win32.section); return mutex->win32.allocated = GLFW_TRUE; } void _glfwPlatformDestroyMutex(_GLFWmutex* mutex) { if (mutex->win32.allocated) DeleteCriticalSection(&mutex->win32.section); memset(mutex, 0, sizeof(_GLFWmutex)); } void _glfwPlatformLockMutex(_GLFWmutex* mutex) { assert(mutex->win32.allocated == GLFW_TRUE); EnterCriticalSection(&mutex->win32.section); } void _glfwPlatformUnlockMutex(_GLFWmutex* mutex) { assert(mutex->win32.allocated == GLFW_TRUE); LeaveCriticalSection(&mutex->win32.section); } #endif #ifndef HEADER_GUARD_WIN32_WINDOW_C #define HEADER_GUARD_WIN32_WINDOW_C //======================================================================== // GLFW 3.3.7 Win32 - www.glfw.org //------------------------------------------------------------------------ // Copyright (c) 2002-2006 Marcus Geelnard // Copyright (c) 2006-2019 Camilla Löwy // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would // be appreciated but is not required. // // 2. Altered source versions must be plainly marked as such, and must not // be misrepresented as being the original software. // // 3. This notice may not be removed or altered from any source // distribution. // //======================================================================== // Please use C89 style variable declarations in this file because VS 2010 //======================================================================== #include #include #include #include #include #include // Returns the window style for the specified window // static DWORD getWindowStyle(const _GLFWwindow* window) { DWORD style = WS_CLIPSIBLINGS | WS_CLIPCHILDREN; if (window->monitor) style |= WS_POPUP; else { style |= WS_SYSMENU | WS_MINIMIZEBOX; if (window->decorated) { style |= WS_CAPTION; if (window->resizable) style |= WS_MAXIMIZEBOX | WS_THICKFRAME; } else style |= WS_POPUP; } return style; } // Returns the extended window style for the specified window // static DWORD getWindowExStyle(const _GLFWwindow* window) { DWORD style = WS_EX_APPWINDOW; if (window->monitor || window->floating) style |= WS_EX_TOPMOST; return style; } // Returns the image whose area most closely matches the desired one // static const GLFWimage* chooseImage(int count, const GLFWimage* images, int width, int height) { int i, leastDiff = INT_MAX; const GLFWimage* closest = NULL; for (i = 0; i < count; i++) { const int currDiff = abs(images[i].width * images[i].height - width * height); if (currDiff < leastDiff) { closest = images + i; leastDiff = currDiff; } } return closest; } // Creates an RGBA icon or cursor // static HICON createIcon(const GLFWimage* image, int xhot, int yhot, GLFWbool icon) { int i; HDC dc; HICON handle; HBITMAP color, mask; BITMAPV5HEADER bi; ICONINFO ii; unsigned char* target = NULL; unsigned char* source = image->pixels; ZeroMemory(&bi, sizeof(bi)); bi.bV5Size = sizeof(bi); bi.bV5Width = image->width; bi.bV5Height = -image->height; bi.bV5Planes = 1; bi.bV5BitCount = 32; bi.bV5Compression = BI_BITFIELDS; bi.bV5RedMask = 0x00ff0000; bi.bV5GreenMask = 0x0000ff00; bi.bV5BlueMask = 0x000000ff; bi.bV5AlphaMask = 0xff000000; dc = GetDC(NULL); color = CreateDIBSection(dc, (BITMAPINFO*) &bi, DIB_RGB_COLORS, (void**) &target, NULL, (DWORD) 0); ReleaseDC(NULL, dc); if (!color) { _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, "Win32: Failed to create RGBA bitmap"); return NULL; } mask = CreateBitmap(image->width, image->height, 1, 1, NULL); if (!mask) { _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, "Win32: Failed to create mask bitmap"); DeleteObject(color); return NULL; } for (i = 0; i < image->width * image->height; i++) { target[0] = source[2]; target[1] = source[1]; target[2] = source[0]; target[3] = source[3]; target += 4; source += 4; } ZeroMemory(&ii, sizeof(ii)); ii.fIcon = icon; ii.xHotspot = xhot; ii.yHotspot = yhot; ii.hbmMask = mask; ii.hbmColor = color; handle = CreateIconIndirect(&ii); DeleteObject(color); DeleteObject(mask); if (!handle) { if (icon) { _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, "Win32: Failed to create icon"); } else { _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, "Win32: Failed to create cursor"); } } return handle; } // Translate content area size to full window size according to styles and DPI // static void getFullWindowSize(DWORD style, DWORD exStyle, int contentWidth, int contentHeight, int* fullWidth, int* fullHeight, UINT dpi) { RECT rect = { 0, 0, contentWidth, contentHeight }; if (_glfwIsWindows10AnniversaryUpdateOrGreaterWin32()) AdjustWindowRectExForDpi(&rect, style, FALSE, exStyle, dpi); else AdjustWindowRectEx(&rect, style, FALSE, exStyle); *fullWidth = rect.right - rect.left; *fullHeight = rect.bottom - rect.top; } // Enforce the content area aspect ratio based on which edge is being dragged // static void applyAspectRatio(_GLFWwindow* window, int edge, RECT* area) { int xoff, yoff; UINT dpi = USER_DEFAULT_SCREEN_DPI; const float ratio = (float) window->numer / (float) window->denom; if (_glfwIsWindows10AnniversaryUpdateOrGreaterWin32()) dpi = GetDpiForWindow(window->win32.handle); getFullWindowSize(getWindowStyle(window), getWindowExStyle(window), 0, 0, &xoff, &yoff, dpi); if (edge == WMSZ_LEFT || edge == WMSZ_BOTTOMLEFT || edge == WMSZ_RIGHT || edge == WMSZ_BOTTOMRIGHT) { area->bottom = area->top + yoff + (int) ((area->right - area->left - xoff) / ratio); } else if (edge == WMSZ_TOPLEFT || edge == WMSZ_TOPRIGHT) { area->top = area->bottom - yoff - (int) ((area->right - area->left - xoff) / ratio); } else if (edge == WMSZ_TOP || edge == WMSZ_BOTTOM) { area->right = area->left + xoff + (int) ((area->bottom - area->top - yoff) * ratio); } } // Updates the cursor image according to its cursor mode // static void updateCursorImage(_GLFWwindow* window) { if (window->cursorMode == GLFW_CURSOR_NORMAL) { if (window->cursor) SetCursor(window->cursor->win32.handle); else SetCursor(LoadCursorW(NULL, IDC_ARROW)); } else SetCursor(NULL); } // Updates the cursor clip rect // static void updateClipRect(_GLFWwindow* window) { if (window) { RECT clipRect; GetClientRect(window->win32.handle, &clipRect); ClientToScreen(window->win32.handle, (POINT*) &clipRect.left); ClientToScreen(window->win32.handle, (POINT*) &clipRect.right); ClipCursor(&clipRect); } else ClipCursor(NULL); } // Enables WM_INPUT messages for the mouse for the specified window // static void enableRawMouseMotion(_GLFWwindow* window) { const RAWINPUTDEVICE rid = { 0x01, 0x02, 0, window->win32.handle }; if (!RegisterRawInputDevices(&rid, 1, sizeof(rid))) { _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, "Win32: Failed to register raw input device"); } } // Disables WM_INPUT messages for the mouse // static void disableRawMouseMotion(_GLFWwindow* window) { const RAWINPUTDEVICE rid = { 0x01, 0x02, RIDEV_REMOVE, NULL }; if (!RegisterRawInputDevices(&rid, 1, sizeof(rid))) { _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, "Win32: Failed to remove raw input device"); } } // Apply disabled cursor mode to a focused window // static void disableCursor(_GLFWwindow* window) { _glfw.win32.disabledCursorWindow = window; _glfwPlatformGetCursorPos(window, &_glfw.win32.restoreCursorPosX, &_glfw.win32.restoreCursorPosY); updateCursorImage(window); _glfwCenterCursorInContentArea(window); updateClipRect(window); if (window->rawMouseMotion) enableRawMouseMotion(window); } // Exit disabled cursor mode for the specified window // static void enableCursor(_GLFWwindow* window) { if (window->rawMouseMotion) disableRawMouseMotion(window); _glfw.win32.disabledCursorWindow = NULL; updateClipRect(NULL); _glfwPlatformSetCursorPos(window, _glfw.win32.restoreCursorPosX, _glfw.win32.restoreCursorPosY); updateCursorImage(window); } // Returns whether the cursor is in the content area of the specified window // static GLFWbool cursorInContentArea(_GLFWwindow* window) { RECT area; POINT pos; if (!GetCursorPos(&pos)) return GLFW_FALSE; if (WindowFromPoint(pos) != window->win32.handle) return GLFW_FALSE; GetClientRect(window->win32.handle, &area); ClientToScreen(window->win32.handle, (POINT*) &area.left); ClientToScreen(window->win32.handle, (POINT*) &area.right); return PtInRect(&area, pos); } // Update native window styles to match attributes // static void updateWindowStyles(const _GLFWwindow* window) { RECT rect; DWORD style = GetWindowLongW(window->win32.handle, GWL_STYLE); style &= ~(WS_OVERLAPPEDWINDOW | WS_POPUP); style |= getWindowStyle(window); GetClientRect(window->win32.handle, &rect); if (_glfwIsWindows10AnniversaryUpdateOrGreaterWin32()) { AdjustWindowRectExForDpi(&rect, style, FALSE, getWindowExStyle(window), GetDpiForWindow(window->win32.handle)); } else AdjustWindowRectEx(&rect, style, FALSE, getWindowExStyle(window)); ClientToScreen(window->win32.handle, (POINT*) &rect.left); ClientToScreen(window->win32.handle, (POINT*) &rect.right); SetWindowLongW(window->win32.handle, GWL_STYLE, style); SetWindowPos(window->win32.handle, HWND_TOP, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, SWP_FRAMECHANGED | SWP_NOACTIVATE | SWP_NOZORDER); } // Update window framebuffer transparency // static void updateFramebufferTransparency(const _GLFWwindow* window) { BOOL composition, opaque; DWORD color; if (!IsWindowsVistaOrGreater()) return; if (FAILED(DwmIsCompositionEnabled(&composition)) || !composition) return; if (IsWindows8OrGreater() || (SUCCEEDED(DwmGetColorizationColor(&color, &opaque)) && !opaque)) { HRGN region = CreateRectRgn(0, 0, -1, -1); DWM_BLURBEHIND bb = {0}; bb.dwFlags = DWM_BB_ENABLE | DWM_BB_BLURREGION; bb.hRgnBlur = region; bb.fEnable = TRUE; DwmEnableBlurBehindWindow(window->win32.handle, &bb); DeleteObject(region); } else { // HACK: Disable framebuffer transparency on Windows 7 when the // colorization color is opaque, because otherwise the window // contents is blended additively with the previous frame instead // of replacing it DWM_BLURBEHIND bb = {0}; bb.dwFlags = DWM_BB_ENABLE; DwmEnableBlurBehindWindow(window->win32.handle, &bb); } } // Retrieves and translates modifier keys // static int getKeyMods(void) { int mods = 0; if (GetKeyState(VK_SHIFT) & 0x8000) mods |= GLFW_MOD_SHIFT; if (GetKeyState(VK_CONTROL) & 0x8000) mods |= GLFW_MOD_CONTROL; if (GetKeyState(VK_MENU) & 0x8000) mods |= GLFW_MOD_ALT; if ((GetKeyState(VK_LWIN) | GetKeyState(VK_RWIN)) & 0x8000) mods |= GLFW_MOD_SUPER; if (GetKeyState(VK_CAPITAL) & 1) mods |= GLFW_MOD_CAPS_LOCK; if (GetKeyState(VK_NUMLOCK) & 1) mods |= GLFW_MOD_NUM_LOCK; return mods; } static void fitToMonitor(_GLFWwindow* window) { MONITORINFO mi = { sizeof(mi) }; GetMonitorInfoW(window->monitor->win32.handle, &mi); SetWindowPos(window->win32.handle, HWND_TOPMOST, mi.rcMonitor.left, mi.rcMonitor.top, mi.rcMonitor.right - mi.rcMonitor.left, mi.rcMonitor.bottom - mi.rcMonitor.top, SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOCOPYBITS); } // Make the specified window and its video mode active on its monitor // static void acquireMonitor(_GLFWwindow* window) { if (!_glfw.win32.acquiredMonitorCount) { SetThreadExecutionState(ES_CONTINUOUS | ES_DISPLAY_REQUIRED); // HACK: When mouse trails are enabled the cursor becomes invisible when // the OpenGL ICD switches to page flipping if (IsWindowsXPOrGreater()) { SystemParametersInfoW(SPI_GETMOUSETRAILS, 0, &_glfw.win32.mouseTrailSize, 0); SystemParametersInfoW(SPI_SETMOUSETRAILS, 0, 0, 0); } } if (!window->monitor->window) _glfw.win32.acquiredMonitorCount++; _glfwSetVideoModeWin32(window->monitor, &window->videoMode); _glfwInputMonitorWindow(window->monitor, window); } // Remove the window and restore the original video mode // static void releaseMonitor(_GLFWwindow* window) { if (window->monitor->window != window) return; _glfw.win32.acquiredMonitorCount--; if (!_glfw.win32.acquiredMonitorCount) { SetThreadExecutionState(ES_CONTINUOUS); // HACK: Restore mouse trail length saved in acquireMonitor if (IsWindowsXPOrGreater()) SystemParametersInfoW(SPI_SETMOUSETRAILS, _glfw.win32.mouseTrailSize, 0, 0); } _glfwInputMonitorWindow(window->monitor, NULL); _glfwRestoreVideoModeWin32(window->monitor); } // Manually maximize the window, for when SW_MAXIMIZE cannot be used // static void maximizeWindowManually(_GLFWwindow* window) { RECT rect; DWORD style; MONITORINFO mi = { sizeof(mi) }; GetMonitorInfoW(MonitorFromWindow(window->win32.handle, MONITOR_DEFAULTTONEAREST), &mi); rect = mi.rcWork; if (window->maxwidth != GLFW_DONT_CARE && window->maxheight != GLFW_DONT_CARE) { if (rect.right - rect.left > window->maxwidth) rect.right = rect.left + window->maxwidth; if (rect.bottom - rect.top > window->maxheight) rect.bottom = rect.top + window->maxheight; } style = GetWindowLongW(window->win32.handle, GWL_STYLE); style |= WS_MAXIMIZE; SetWindowLongW(window->win32.handle, GWL_STYLE, style); if (window->decorated) { const DWORD exStyle = GetWindowLongW(window->win32.handle, GWL_EXSTYLE); if (_glfwIsWindows10AnniversaryUpdateOrGreaterWin32()) { const UINT dpi = GetDpiForWindow(window->win32.handle); AdjustWindowRectExForDpi(&rect, style, FALSE, exStyle, dpi); OffsetRect(&rect, 0, GetSystemMetricsForDpi(SM_CYCAPTION, dpi)); } else { AdjustWindowRectEx(&rect, style, FALSE, exStyle); OffsetRect(&rect, 0, GetSystemMetrics(SM_CYCAPTION)); } if (rect.bottom > mi.rcWork.bottom) rect.bottom = mi.rcWork.bottom; } SetWindowPos(window->win32.handle, HWND_TOP, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED); } // Window callback function (handles window messages) // static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { _GLFWwindow* window = GetPropW(hWnd, L"GLFW"); if (!window) { // This is the message handling for the hidden helper window // and for a regular window during its initial creation switch (uMsg) { case WM_NCCREATE: { if (_glfwIsWindows10AnniversaryUpdateOrGreaterWin32()) { const CREATESTRUCTW* cs = (const CREATESTRUCTW*) lParam; const _GLFWwndconfig* wndconfig = cs->lpCreateParams; // On per-monitor DPI aware V1 systems, only enable // non-client scaling for windows that scale the client area // We need WM_GETDPISCALEDSIZE from V2 to keep the client // area static when the non-client area is scaled if (wndconfig && wndconfig->scaleToMonitor) EnableNonClientDpiScaling(hWnd); } break; } case WM_DISPLAYCHANGE: _glfwPollMonitorsWin32(); break; case WM_DEVICECHANGE: { if (wParam == DBT_DEVICEARRIVAL) { DEV_BROADCAST_HDR* dbh = (DEV_BROADCAST_HDR*) lParam; if (dbh && dbh->dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE) _glfwDetectJoystickConnectionWin32(); } else if (wParam == DBT_DEVICEREMOVECOMPLETE) { DEV_BROADCAST_HDR* dbh = (DEV_BROADCAST_HDR*) lParam; if (dbh && dbh->dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE) _glfwDetectJoystickDisconnectionWin32(); } break; } } return DefWindowProcW(hWnd, uMsg, wParam, lParam); } switch (uMsg) { case WM_MOUSEACTIVATE: { // HACK: Postpone cursor disabling when the window was activated by // clicking a caption button if (HIWORD(lParam) == WM_LBUTTONDOWN) { if (LOWORD(lParam) != HTCLIENT) window->win32.frameAction = GLFW_TRUE; } break; } case WM_CAPTURECHANGED: { // HACK: Disable the cursor once the caption button action has been // completed or cancelled if (lParam == 0 && window->win32.frameAction) { if (window->cursorMode == GLFW_CURSOR_DISABLED) disableCursor(window); window->win32.frameAction = GLFW_FALSE; } break; } case WM_SETFOCUS: { _glfwInputWindowFocus(window, GLFW_TRUE); // HACK: Do not disable cursor while the user is interacting with // a caption button if (window->win32.frameAction) break; if (window->cursorMode == GLFW_CURSOR_DISABLED) disableCursor(window); return 0; } case WM_KILLFOCUS: { if (window->cursorMode == GLFW_CURSOR_DISABLED) enableCursor(window); if (window->monitor && window->autoIconify) _glfwPlatformIconifyWindow(window); _glfwInputWindowFocus(window, GLFW_FALSE); return 0; } case WM_SYSCOMMAND: { switch (wParam & 0xfff0) { case SC_SCREENSAVE: case SC_MONITORPOWER: { if (window->monitor) { // We are running in full screen mode, so disallow // screen saver and screen blanking return 0; } else break; } // User trying to access application menu using ALT? case SC_KEYMENU: return 0; } break; } case WM_CLOSE: { _glfwInputWindowCloseRequest(window); return 0; } case WM_INPUTLANGCHANGE: { _glfwUpdateKeyNamesWin32(); break; } case WM_CHAR: case WM_SYSCHAR: { if (wParam >= 0xd800 && wParam <= 0xdbff) window->win32.highSurrogate = (WCHAR) wParam; else { uint32_t codepoint = 0; if (wParam >= 0xdc00 && wParam <= 0xdfff) { if (window->win32.highSurrogate) { codepoint += (window->win32.highSurrogate - 0xd800) << 10; codepoint += (WCHAR) wParam - 0xdc00; codepoint += 0x10000; } } else codepoint = (WCHAR) wParam; window->win32.highSurrogate = 0; _glfwInputChar(window, codepoint, getKeyMods(), uMsg != WM_SYSCHAR); } return 0; } case WM_UNICHAR: { if (wParam == UNICODE_NOCHAR) { // WM_UNICHAR is not sent by Windows, but is sent by some // third-party input method engine // Returning TRUE here announces support for this message return TRUE; } _glfwInputChar(window, (uint32_t) wParam, getKeyMods(), GLFW_TRUE); return 0; } case WM_KEYDOWN: case WM_SYSKEYDOWN: case WM_KEYUP: case WM_SYSKEYUP: { int key, scancode; const int action = (HIWORD(lParam) & KF_UP) ? GLFW_RELEASE : GLFW_PRESS; const int mods = getKeyMods(); scancode = (HIWORD(lParam) & (KF_EXTENDED | 0xff)); if (!scancode) { // NOTE: Some synthetic key messages have a scancode of zero // HACK: Map the virtual key back to a usable scancode scancode = MapVirtualKeyW((UINT) wParam, MAPVK_VK_TO_VSC); } key = _glfw.win32.keycodes[scancode]; // The Ctrl keys require special handling if (wParam == VK_CONTROL) { if (HIWORD(lParam) & KF_EXTENDED) { // Right side keys have the extended key bit set key = GLFW_KEY_RIGHT_CONTROL; } else { // NOTE: Alt Gr sends Left Ctrl followed by Right Alt // HACK: We only want one event for Alt Gr, so if we detect // this sequence we discard this Left Ctrl message now // and later report Right Alt normally MSG next; const DWORD time = GetMessageTime(); if (PeekMessageW(&next, NULL, 0, 0, PM_NOREMOVE)) { if (next.message == WM_KEYDOWN || next.message == WM_SYSKEYDOWN || next.message == WM_KEYUP || next.message == WM_SYSKEYUP) { if (next.wParam == VK_MENU && (HIWORD(next.lParam) & KF_EXTENDED) && next.time == time) { // Next message is Right Alt down so discard this break; } } } // This is a regular Left Ctrl message key = GLFW_KEY_LEFT_CONTROL; } } else if (wParam == VK_PROCESSKEY) { // IME notifies that keys have been filtered by setting the // virtual key-code to VK_PROCESSKEY break; } if (action == GLFW_RELEASE && wParam == VK_SHIFT) { // HACK: Release both Shift keys on Shift up event, as when both // are pressed the first release does not emit any event // NOTE: The other half of this is in _glfwPlatformPollEvents _glfwInputKey(window, GLFW_KEY_LEFT_SHIFT, scancode, action, mods); _glfwInputKey(window, GLFW_KEY_RIGHT_SHIFT, scancode, action, mods); } else if (wParam == VK_SNAPSHOT) { // HACK: Key down is not reported for the Print Screen key _glfwInputKey(window, key, scancode, GLFW_PRESS, mods); _glfwInputKey(window, key, scancode, GLFW_RELEASE, mods); } else _glfwInputKey(window, key, scancode, action, mods); break; } case WM_LBUTTONDOWN: case WM_RBUTTONDOWN: case WM_MBUTTONDOWN: case WM_XBUTTONDOWN: case WM_LBUTTONUP: case WM_RBUTTONUP: case WM_MBUTTONUP: case WM_XBUTTONUP: { int i, button, action; if (uMsg == WM_LBUTTONDOWN || uMsg == WM_LBUTTONUP) button = GLFW_MOUSE_BUTTON_LEFT; else if (uMsg == WM_RBUTTONDOWN || uMsg == WM_RBUTTONUP) button = GLFW_MOUSE_BUTTON_RIGHT; else if (uMsg == WM_MBUTTONDOWN || uMsg == WM_MBUTTONUP) button = GLFW_MOUSE_BUTTON_MIDDLE; else if (GET_XBUTTON_WPARAM(wParam) == XBUTTON1) button = GLFW_MOUSE_BUTTON_4; else button = GLFW_MOUSE_BUTTON_5; if (uMsg == WM_LBUTTONDOWN || uMsg == WM_RBUTTONDOWN || uMsg == WM_MBUTTONDOWN || uMsg == WM_XBUTTONDOWN) { action = GLFW_PRESS; } else action = GLFW_RELEASE; for (i = 0; i <= GLFW_MOUSE_BUTTON_LAST; i++) { if (window->mouseButtons[i] == GLFW_PRESS) break; } if (i > GLFW_MOUSE_BUTTON_LAST) SetCapture(hWnd); _glfwInputMouseClick(window, button, action, getKeyMods()); for (i = 0; i <= GLFW_MOUSE_BUTTON_LAST; i++) { if (window->mouseButtons[i] == GLFW_PRESS) break; } if (i > GLFW_MOUSE_BUTTON_LAST) ReleaseCapture(); if (uMsg == WM_XBUTTONDOWN || uMsg == WM_XBUTTONUP) return TRUE; return 0; } case WM_MOUSEMOVE: { const int x = GET_X_LPARAM(lParam); const int y = GET_Y_LPARAM(lParam); if (!window->win32.cursorTracked) { TRACKMOUSEEVENT tme; ZeroMemory(&tme, sizeof(tme)); tme.cbSize = sizeof(tme); tme.dwFlags = TME_LEAVE; tme.hwndTrack = window->win32.handle; TrackMouseEvent(&tme); window->win32.cursorTracked = GLFW_TRUE; _glfwInputCursorEnter(window, GLFW_TRUE); } if (window->cursorMode == GLFW_CURSOR_DISABLED) { const int dx = x - window->win32.lastCursorPosX; const int dy = y - window->win32.lastCursorPosY; if (_glfw.win32.disabledCursorWindow != window) break; if (window->rawMouseMotion) break; _glfwInputCursorPos(window, window->virtualCursorPosX + dx, window->virtualCursorPosY + dy); } else _glfwInputCursorPos(window, x, y); window->win32.lastCursorPosX = x; window->win32.lastCursorPosY = y; return 0; } case WM_INPUT: { UINT size = 0; HRAWINPUT ri = (HRAWINPUT) lParam; RAWINPUT* data = NULL; int dx, dy; if (_glfw.win32.disabledCursorWindow != window) break; if (!window->rawMouseMotion) break; GetRawInputData(ri, RID_INPUT, NULL, &size, sizeof(RAWINPUTHEADER)); if (size > (UINT) _glfw.win32.rawInputSize) { free(_glfw.win32.rawInput); _glfw.win32.rawInput = calloc(size, 1); _glfw.win32.rawInputSize = size; } size = _glfw.win32.rawInputSize; if (GetRawInputData(ri, RID_INPUT, _glfw.win32.rawInput, &size, sizeof(RAWINPUTHEADER)) == (UINT) -1) { _glfwInputError(GLFW_PLATFORM_ERROR, "Win32: Failed to retrieve raw input data"); break; } data = _glfw.win32.rawInput; if (data->data.mouse.usFlags & MOUSE_MOVE_ABSOLUTE) { dx = data->data.mouse.lLastX - window->win32.lastCursorPosX; dy = data->data.mouse.lLastY - window->win32.lastCursorPosY; } else { dx = data->data.mouse.lLastX; dy = data->data.mouse.lLastY; } _glfwInputCursorPos(window, window->virtualCursorPosX + dx, window->virtualCursorPosY + dy); window->win32.lastCursorPosX += dx; window->win32.lastCursorPosY += dy; break; } case WM_MOUSELEAVE: { window->win32.cursorTracked = GLFW_FALSE; _glfwInputCursorEnter(window, GLFW_FALSE); return 0; } case WM_MOUSEWHEEL: { _glfwInputScroll(window, 0.0, (SHORT) HIWORD(wParam) / (double) WHEEL_DELTA); return 0; } case WM_MOUSEHWHEEL: { // This message is only sent on Windows Vista and later // NOTE: The X-axis is inverted for consistency with macOS and X11 _glfwInputScroll(window, -((SHORT) HIWORD(wParam) / (double) WHEEL_DELTA), 0.0); return 0; } case WM_ENTERSIZEMOVE: case WM_ENTERMENULOOP: { if (window->win32.frameAction) break; // HACK: Enable the cursor while the user is moving or // resizing the window or using the window menu if (window->cursorMode == GLFW_CURSOR_DISABLED) enableCursor(window); break; } case WM_EXITSIZEMOVE: case WM_EXITMENULOOP: { if (window->win32.frameAction) break; // HACK: Disable the cursor once the user is done moving or // resizing the window or using the menu if (window->cursorMode == GLFW_CURSOR_DISABLED) disableCursor(window); break; } case WM_SIZE: { const int width = LOWORD(lParam); const int height = HIWORD(lParam); const GLFWbool iconified = wParam == SIZE_MINIMIZED; const GLFWbool maximized = wParam == SIZE_MAXIMIZED || (window->win32.maximized && wParam != SIZE_RESTORED); if (_glfw.win32.disabledCursorWindow == window) updateClipRect(window); if (window->win32.iconified != iconified) _glfwInputWindowIconify(window, iconified); if (window->win32.maximized != maximized) _glfwInputWindowMaximize(window, maximized); if (width != window->win32.width || height != window->win32.height) { window->win32.width = width; window->win32.height = height; _glfwInputFramebufferSize(window, width, height); _glfwInputWindowSize(window, width, height); } if (window->monitor && window->win32.iconified != iconified) { if (iconified) releaseMonitor(window); else { acquireMonitor(window); fitToMonitor(window); } } window->win32.iconified = iconified; window->win32.maximized = maximized; return 0; } case WM_MOVE: { if (_glfw.win32.disabledCursorWindow == window) updateClipRect(window); // NOTE: This cannot use LOWORD/HIWORD recommended by MSDN, as // those macros do not handle negative window positions correctly _glfwInputWindowPos(window, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)); return 0; } case WM_SIZING: { if (window->numer == GLFW_DONT_CARE || window->denom == GLFW_DONT_CARE) { break; } applyAspectRatio(window, (int) wParam, (RECT*) lParam); return TRUE; } case WM_GETMINMAXINFO: { int xoff, yoff; UINT dpi = USER_DEFAULT_SCREEN_DPI; MINMAXINFO* mmi = (MINMAXINFO*) lParam; if (window->monitor) break; if (_glfwIsWindows10AnniversaryUpdateOrGreaterWin32()) dpi = GetDpiForWindow(window->win32.handle); getFullWindowSize(getWindowStyle(window), getWindowExStyle(window), 0, 0, &xoff, &yoff, dpi); if (window->minwidth != GLFW_DONT_CARE && window->minheight != GLFW_DONT_CARE) { mmi->ptMinTrackSize.x = window->minwidth + xoff; mmi->ptMinTrackSize.y = window->minheight + yoff; } if (window->maxwidth != GLFW_DONT_CARE && window->maxheight != GLFW_DONT_CARE) { mmi->ptMaxTrackSize.x = window->maxwidth + xoff; mmi->ptMaxTrackSize.y = window->maxheight + yoff; } if (!window->decorated) { MONITORINFO mi; const HMONITOR mh = MonitorFromWindow(window->win32.handle, MONITOR_DEFAULTTONEAREST); ZeroMemory(&mi, sizeof(mi)); mi.cbSize = sizeof(mi); GetMonitorInfoW(mh, &mi); mmi->ptMaxPosition.x = mi.rcWork.left - mi.rcMonitor.left; mmi->ptMaxPosition.y = mi.rcWork.top - mi.rcMonitor.top; mmi->ptMaxSize.x = mi.rcWork.right - mi.rcWork.left; mmi->ptMaxSize.y = mi.rcWork.bottom - mi.rcWork.top; } return 0; } case WM_PAINT: { _glfwInputWindowDamage(window); break; } case WM_ERASEBKGND: { return TRUE; } case WM_NCACTIVATE: case WM_NCPAINT: { // Prevent title bar from being drawn after restoring a minimized // undecorated window if (!window->decorated) return TRUE; break; } case WM_DWMCOMPOSITIONCHANGED: case WM_DWMCOLORIZATIONCOLORCHANGED: { if (window->win32.transparent) updateFramebufferTransparency(window); return 0; } case WM_GETDPISCALEDSIZE: { if (window->win32.scaleToMonitor) break; // Adjust the window size to keep the content area size constant if (_glfwIsWindows10CreatorsUpdateOrGreaterWin32()) { RECT source = {0}, target = {0}; SIZE* size = (SIZE*) lParam; AdjustWindowRectExForDpi(&source, getWindowStyle(window), FALSE, getWindowExStyle(window), GetDpiForWindow(window->win32.handle)); AdjustWindowRectExForDpi(&target, getWindowStyle(window), FALSE, getWindowExStyle(window), LOWORD(wParam)); size->cx += (target.right - target.left) - (source.right - source.left); size->cy += (target.bottom - target.top) - (source.bottom - source.top); return TRUE; } break; } case WM_DPICHANGED: { const float xscale = HIWORD(wParam) / (float) USER_DEFAULT_SCREEN_DPI; const float yscale = LOWORD(wParam) / (float) USER_DEFAULT_SCREEN_DPI; // Resize windowed mode windows that either permit rescaling or that // need it to compensate for non-client area scaling if (!window->monitor && (window->win32.scaleToMonitor || _glfwIsWindows10CreatorsUpdateOrGreaterWin32())) { RECT* suggested = (RECT*) lParam; SetWindowPos(window->win32.handle, HWND_TOP, suggested->left, suggested->top, suggested->right - suggested->left, suggested->bottom - suggested->top, SWP_NOACTIVATE | SWP_NOZORDER); } _glfwInputWindowContentScale(window, xscale, yscale); break; } case WM_SETCURSOR: { if (LOWORD(lParam) == HTCLIENT) { updateCursorImage(window); return TRUE; } break; } case WM_DROPFILES: { HDROP drop = (HDROP) wParam; POINT pt; int i; const int count = DragQueryFileW(drop, 0xffffffff, NULL, 0); char** paths = calloc(count, sizeof(char*)); // Move the mouse to the position of the drop DragQueryPoint(drop, &pt); _glfwInputCursorPos(window, pt.x, pt.y); for (i = 0; i < count; i++) { const UINT length = DragQueryFileW(drop, i, NULL, 0); WCHAR* buffer = calloc((size_t) length + 1, sizeof(WCHAR)); DragQueryFileW(drop, i, buffer, length + 1); paths[i] = _glfwCreateUTF8FromWideStringWin32(buffer); free(buffer); } _glfwInputDrop(window, count, (const char**) paths); for (i = 0; i < count; i++) free(paths[i]); free(paths); DragFinish(drop); return 0; } } return DefWindowProcW(hWnd, uMsg, wParam, lParam); } // Creates the GLFW window // static int createNativeWindow(_GLFWwindow* window, const _GLFWwndconfig* wndconfig, const _GLFWfbconfig* fbconfig) { int xpos, ypos, fullWidth, fullHeight; WCHAR* wideTitle; DWORD style = getWindowStyle(window); DWORD exStyle = getWindowExStyle(window); if (window->monitor) { GLFWvidmode mode; // NOTE: This window placement is temporary and approximate, as the // correct position and size cannot be known until the monitor // video mode has been picked in _glfwSetVideoModeWin32 _glfwPlatformGetMonitorPos(window->monitor, &xpos, &ypos); _glfwPlatformGetVideoMode(window->monitor, &mode); fullWidth = mode.width; fullHeight = mode.height; } else { xpos = CW_USEDEFAULT; ypos = CW_USEDEFAULT; window->win32.maximized = wndconfig->maximized; if (wndconfig->maximized) style |= WS_MAXIMIZE; getFullWindowSize(style, exStyle, wndconfig->width, wndconfig->height, &fullWidth, &fullHeight, USER_DEFAULT_SCREEN_DPI); } wideTitle = _glfwCreateWideStringFromUTF8Win32(wndconfig->title); if (!wideTitle) return GLFW_FALSE; window->win32.handle = CreateWindowExW(exStyle, _GLFW_WNDCLASSNAME, wideTitle, style, xpos, ypos, fullWidth, fullHeight, NULL, // No parent window NULL, // No window menu GetModuleHandleW(NULL), (LPVOID) wndconfig); free(wideTitle); if (!window->win32.handle) { _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, "Win32: Failed to create window"); return GLFW_FALSE; } SetPropW(window->win32.handle, L"GLFW", window); if (IsWindows7OrGreater()) { ChangeWindowMessageFilterEx(window->win32.handle, WM_DROPFILES, MSGFLT_ALLOW, NULL); ChangeWindowMessageFilterEx(window->win32.handle, WM_COPYDATA, MSGFLT_ALLOW, NULL); ChangeWindowMessageFilterEx(window->win32.handle, WM_COPYGLOBALDATA, MSGFLT_ALLOW, NULL); } window->win32.scaleToMonitor = wndconfig->scaleToMonitor; if (!window->monitor) { RECT rect = { 0, 0, wndconfig->width, wndconfig->height }; WINDOWPLACEMENT wp = { sizeof(wp) }; const HMONITOR mh = MonitorFromWindow(window->win32.handle, MONITOR_DEFAULTTONEAREST); // Adjust window rect to account for DPI scaling of the window frame and // (if enabled) DPI scaling of the content area // This cannot be done until we know what monitor the window was placed on // Only update the restored window rect as the window may be maximized if (wndconfig->scaleToMonitor) { float xscale, yscale; _glfwGetMonitorContentScaleWin32(mh, &xscale, &yscale); if (xscale > 0.f && yscale > 0.f) { rect.right = (int) (rect.right * xscale); rect.bottom = (int) (rect.bottom * yscale); } } if (_glfwIsWindows10AnniversaryUpdateOrGreaterWin32()) { AdjustWindowRectExForDpi(&rect, style, FALSE, exStyle, GetDpiForWindow(window->win32.handle)); } else AdjustWindowRectEx(&rect, style, FALSE, exStyle); GetWindowPlacement(window->win32.handle, &wp); OffsetRect(&rect, wp.rcNormalPosition.left - rect.left, wp.rcNormalPosition.top - rect.top); wp.rcNormalPosition = rect; wp.showCmd = SW_HIDE; SetWindowPlacement(window->win32.handle, &wp); // Adjust rect of maximized undecorated window, because by default Windows will // make such a window cover the whole monitor instead of its workarea if (wndconfig->maximized && !wndconfig->decorated) { MONITORINFO mi = { sizeof(mi) }; GetMonitorInfoW(mh, &mi); SetWindowPos(window->win32.handle, HWND_TOP, mi.rcWork.left, mi.rcWork.top, mi.rcWork.right - mi.rcWork.left, mi.rcWork.bottom - mi.rcWork.top, SWP_NOACTIVATE | SWP_NOZORDER); } } DragAcceptFiles(window->win32.handle, TRUE); if (fbconfig->transparent) { updateFramebufferTransparency(window); window->win32.transparent = GLFW_TRUE; } _glfwPlatformGetWindowSize(window, &window->win32.width, &window->win32.height); return GLFW_TRUE; } ////////////////////////////////////////////////////////////////////////// ////// GLFW internal API ////// ////////////////////////////////////////////////////////////////////////// // Registers the GLFW window class // GLFWbool _glfwRegisterWindowClassWin32(void) { WNDCLASSEXW wc; ZeroMemory(&wc, sizeof(wc)); wc.cbSize = sizeof(wc); wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC; wc.lpfnWndProc = (WNDPROC) windowProc; wc.hInstance = GetModuleHandleW(NULL); wc.hCursor = LoadCursorW(NULL, IDC_ARROW); wc.lpszClassName = _GLFW_WNDCLASSNAME; // Load user-provided icon if available wc.hIcon = LoadImageW(GetModuleHandleW(NULL), L"GLFW_ICON", IMAGE_ICON, 0, 0, LR_DEFAULTSIZE | LR_SHARED); if (!wc.hIcon) { // No user-provided icon found, load default icon wc.hIcon = LoadImageW(NULL, IDI_APPLICATION, IMAGE_ICON, 0, 0, LR_DEFAULTSIZE | LR_SHARED); } if (!RegisterClassExW(&wc)) { _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, "Win32: Failed to register window class"); return GLFW_FALSE; } return GLFW_TRUE; } // Unregisters the GLFW window class // void _glfwUnregisterWindowClassWin32(void) { UnregisterClassW(_GLFW_WNDCLASSNAME, GetModuleHandleW(NULL)); } ////////////////////////////////////////////////////////////////////////// ////// GLFW platform API ////// ////////////////////////////////////////////////////////////////////////// int _glfwPlatformCreateWindow(_GLFWwindow* window, const _GLFWwndconfig* wndconfig, const _GLFWctxconfig* ctxconfig, const _GLFWfbconfig* fbconfig) { if (!createNativeWindow(window, wndconfig, fbconfig)) return GLFW_FALSE; if (ctxconfig->client != GLFW_NO_API) { if (ctxconfig->source == GLFW_NATIVE_CONTEXT_API) { if (!_glfwInitWGL()) return GLFW_FALSE; if (!_glfwCreateContextWGL(window, ctxconfig, fbconfig)) return GLFW_FALSE; } else if (ctxconfig->source == GLFW_EGL_CONTEXT_API) { if (!_glfwInitEGL()) return GLFW_FALSE; if (!_glfwCreateContextEGL(window, ctxconfig, fbconfig)) return GLFW_FALSE; } else if (ctxconfig->source == GLFW_OSMESA_CONTEXT_API) { if (!_glfwInitOSMesa()) return GLFW_FALSE; if (!_glfwCreateContextOSMesa(window, ctxconfig, fbconfig)) return GLFW_FALSE; } } if (window->monitor) { _glfwPlatformShowWindow(window); _glfwPlatformFocusWindow(window); acquireMonitor(window); fitToMonitor(window); } return GLFW_TRUE; } void _glfwPlatformDestroyWindow(_GLFWwindow* window) { if (window->monitor) releaseMonitor(window); if (window->context.destroy) window->context.destroy(window); if (_glfw.win32.disabledCursorWindow == window) _glfw.win32.disabledCursorWindow = NULL; if (window->win32.handle) { RemovePropW(window->win32.handle, L"GLFW"); DestroyWindow(window->win32.handle); window->win32.handle = NULL; } if (window->win32.bigIcon) DestroyIcon(window->win32.bigIcon); if (window->win32.smallIcon) DestroyIcon(window->win32.smallIcon); } void _glfwPlatformSetWindowTitle(_GLFWwindow* window, const char* title) { WCHAR* wideTitle = _glfwCreateWideStringFromUTF8Win32(title); if (!wideTitle) return; SetWindowTextW(window->win32.handle, wideTitle); free(wideTitle); } void _glfwPlatformSetWindowIcon(_GLFWwindow* window, int count, const GLFWimage* images) { HICON bigIcon = NULL, smallIcon = NULL; if (count) { const GLFWimage* bigImage = chooseImage(count, images, GetSystemMetrics(SM_CXICON), GetSystemMetrics(SM_CYICON)); const GLFWimage* smallImage = chooseImage(count, images, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON)); bigIcon = createIcon(bigImage, 0, 0, GLFW_TRUE); smallIcon = createIcon(smallImage, 0, 0, GLFW_TRUE); } else { bigIcon = (HICON) GetClassLongPtrW(window->win32.handle, GCLP_HICON); smallIcon = (HICON) GetClassLongPtrW(window->win32.handle, GCLP_HICONSM); } SendMessageW(window->win32.handle, WM_SETICON, ICON_BIG, (LPARAM) bigIcon); SendMessageW(window->win32.handle, WM_SETICON, ICON_SMALL, (LPARAM) smallIcon); if (window->win32.bigIcon) DestroyIcon(window->win32.bigIcon); if (window->win32.smallIcon) DestroyIcon(window->win32.smallIcon); if (count) { window->win32.bigIcon = bigIcon; window->win32.smallIcon = smallIcon; } } void _glfwPlatformGetWindowPos(_GLFWwindow* window, int* xpos, int* ypos) { POINT pos = { 0, 0 }; ClientToScreen(window->win32.handle, &pos); if (xpos) *xpos = pos.x; if (ypos) *ypos = pos.y; } void _glfwPlatformSetWindowPos(_GLFWwindow* window, int xpos, int ypos) { RECT rect = { xpos, ypos, xpos, ypos }; if (_glfwIsWindows10AnniversaryUpdateOrGreaterWin32()) { AdjustWindowRectExForDpi(&rect, getWindowStyle(window), FALSE, getWindowExStyle(window), GetDpiForWindow(window->win32.handle)); } else { AdjustWindowRectEx(&rect, getWindowStyle(window), FALSE, getWindowExStyle(window)); } SetWindowPos(window->win32.handle, NULL, rect.left, rect.top, 0, 0, SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOSIZE); } void _glfwPlatformGetWindowSize(_GLFWwindow* window, int* width, int* height) { RECT area; GetClientRect(window->win32.handle, &area); if (width) *width = area.right; if (height) *height = area.bottom; } void _glfwPlatformSetWindowSize(_GLFWwindow* window, int width, int height) { if (window->monitor) { if (window->monitor->window == window) { acquireMonitor(window); fitToMonitor(window); } } else { RECT rect = { 0, 0, width, height }; if (_glfwIsWindows10AnniversaryUpdateOrGreaterWin32()) { AdjustWindowRectExForDpi(&rect, getWindowStyle(window), FALSE, getWindowExStyle(window), GetDpiForWindow(window->win32.handle)); } else { AdjustWindowRectEx(&rect, getWindowStyle(window), FALSE, getWindowExStyle(window)); } SetWindowPos(window->win32.handle, HWND_TOP, 0, 0, rect.right - rect.left, rect.bottom - rect.top, SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOMOVE | SWP_NOZORDER); } } void _glfwPlatformSetWindowSizeLimits(_GLFWwindow* window, int minwidth, int minheight, int maxwidth, int maxheight) { RECT area; if ((minwidth == GLFW_DONT_CARE || minheight == GLFW_DONT_CARE) && (maxwidth == GLFW_DONT_CARE || maxheight == GLFW_DONT_CARE)) { return; } GetWindowRect(window->win32.handle, &area); MoveWindow(window->win32.handle, area.left, area.top, area.right - area.left, area.bottom - area.top, TRUE); } void _glfwPlatformSetWindowAspectRatio(_GLFWwindow* window, int numer, int denom) { RECT area; if (numer == GLFW_DONT_CARE || denom == GLFW_DONT_CARE) return; GetWindowRect(window->win32.handle, &area); applyAspectRatio(window, WMSZ_BOTTOMRIGHT, &area); MoveWindow(window->win32.handle, area.left, area.top, area.right - area.left, area.bottom - area.top, TRUE); } void _glfwPlatformGetFramebufferSize(_GLFWwindow* window, int* width, int* height) { _glfwPlatformGetWindowSize(window, width, height); } void _glfwPlatformGetWindowFrameSize(_GLFWwindow* window, int* left, int* top, int* right, int* bottom) { RECT rect; int width, height; _glfwPlatformGetWindowSize(window, &width, &height); SetRect(&rect, 0, 0, width, height); if (_glfwIsWindows10AnniversaryUpdateOrGreaterWin32()) { AdjustWindowRectExForDpi(&rect, getWindowStyle(window), FALSE, getWindowExStyle(window), GetDpiForWindow(window->win32.handle)); } else { AdjustWindowRectEx(&rect, getWindowStyle(window), FALSE, getWindowExStyle(window)); } if (left) *left = -rect.left; if (top) *top = -rect.top; if (right) *right = rect.right - width; if (bottom) *bottom = rect.bottom - height; } void _glfwPlatformGetWindowContentScale(_GLFWwindow* window, float* xscale, float* yscale) { const HANDLE handle = MonitorFromWindow(window->win32.handle, MONITOR_DEFAULTTONEAREST); _glfwGetMonitorContentScaleWin32(handle, xscale, yscale); } void _glfwPlatformIconifyWindow(_GLFWwindow* window) { ShowWindow(window->win32.handle, SW_MINIMIZE); } void _glfwPlatformRestoreWindow(_GLFWwindow* window) { ShowWindow(window->win32.handle, SW_RESTORE); } void _glfwPlatformMaximizeWindow(_GLFWwindow* window) { if (IsWindowVisible(window->win32.handle)) ShowWindow(window->win32.handle, SW_MAXIMIZE); else maximizeWindowManually(window); } void _glfwPlatformShowWindow(_GLFWwindow* window) { ShowWindow(window->win32.handle, SW_SHOWNA); } void _glfwPlatformHideWindow(_GLFWwindow* window) { ShowWindow(window->win32.handle, SW_HIDE); } void _glfwPlatformRequestWindowAttention(_GLFWwindow* window) { FlashWindow(window->win32.handle, TRUE); } void _glfwPlatformFocusWindow(_GLFWwindow* window) { BringWindowToTop(window->win32.handle); SetForegroundWindow(window->win32.handle); SetFocus(window->win32.handle); } void _glfwPlatformSetWindowMonitor(_GLFWwindow* window, _GLFWmonitor* monitor, int xpos, int ypos, int width, int height, int refreshRate) { if (window->monitor == monitor) { if (monitor) { if (monitor->window == window) { acquireMonitor(window); fitToMonitor(window); } } else { RECT rect = { xpos, ypos, xpos + width, ypos + height }; if (_glfwIsWindows10AnniversaryUpdateOrGreaterWin32()) { AdjustWindowRectExForDpi(&rect, getWindowStyle(window), FALSE, getWindowExStyle(window), GetDpiForWindow(window->win32.handle)); } else { AdjustWindowRectEx(&rect, getWindowStyle(window), FALSE, getWindowExStyle(window)); } SetWindowPos(window->win32.handle, HWND_TOP, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, SWP_NOCOPYBITS | SWP_NOACTIVATE | SWP_NOZORDER); } return; } if (window->monitor) releaseMonitor(window); _glfwInputWindowMonitor(window, monitor); if (window->monitor) { MONITORINFO mi = { sizeof(mi) }; UINT flags = SWP_SHOWWINDOW | SWP_NOACTIVATE | SWP_NOCOPYBITS; if (window->decorated) { DWORD style = GetWindowLongW(window->win32.handle, GWL_STYLE); style &= ~WS_OVERLAPPEDWINDOW; style |= getWindowStyle(window); SetWindowLongW(window->win32.handle, GWL_STYLE, style); flags |= SWP_FRAMECHANGED; } acquireMonitor(window); GetMonitorInfoW(window->monitor->win32.handle, &mi); SetWindowPos(window->win32.handle, HWND_TOPMOST, mi.rcMonitor.left, mi.rcMonitor.top, mi.rcMonitor.right - mi.rcMonitor.left, mi.rcMonitor.bottom - mi.rcMonitor.top, flags); } else { HWND after; RECT rect = { xpos, ypos, xpos + width, ypos + height }; DWORD style = GetWindowLongW(window->win32.handle, GWL_STYLE); UINT flags = SWP_NOACTIVATE | SWP_NOCOPYBITS; if (window->decorated) { style &= ~WS_POPUP; style |= getWindowStyle(window); SetWindowLongW(window->win32.handle, GWL_STYLE, style); flags |= SWP_FRAMECHANGED; } if (window->floating) after = HWND_TOPMOST; else after = HWND_NOTOPMOST; if (_glfwIsWindows10AnniversaryUpdateOrGreaterWin32()) { AdjustWindowRectExForDpi(&rect, getWindowStyle(window), FALSE, getWindowExStyle(window), GetDpiForWindow(window->win32.handle)); } else { AdjustWindowRectEx(&rect, getWindowStyle(window), FALSE, getWindowExStyle(window)); } SetWindowPos(window->win32.handle, after, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, flags); } } int _glfwPlatformWindowFocused(_GLFWwindow* window) { return window->win32.handle == GetActiveWindow(); } int _glfwPlatformWindowIconified(_GLFWwindow* window) { return IsIconic(window->win32.handle); } int _glfwPlatformWindowVisible(_GLFWwindow* window) { return IsWindowVisible(window->win32.handle); } int _glfwPlatformWindowMaximized(_GLFWwindow* window) { return IsZoomed(window->win32.handle); } int _glfwPlatformWindowHovered(_GLFWwindow* window) { return cursorInContentArea(window); } int _glfwPlatformFramebufferTransparent(_GLFWwindow* window) { BOOL composition, opaque; DWORD color; if (!window->win32.transparent) return GLFW_FALSE; if (!IsWindowsVistaOrGreater()) return GLFW_FALSE; if (FAILED(DwmIsCompositionEnabled(&composition)) || !composition) return GLFW_FALSE; if (!IsWindows8OrGreater()) { // HACK: Disable framebuffer transparency on Windows 7 when the // colorization color is opaque, because otherwise the window // contents is blended additively with the previous frame instead // of replacing it if (FAILED(DwmGetColorizationColor(&color, &opaque)) || opaque) return GLFW_FALSE; } return GLFW_TRUE; } void _glfwPlatformSetWindowResizable(_GLFWwindow* window, GLFWbool enabled) { updateWindowStyles(window); } void _glfwPlatformSetWindowDecorated(_GLFWwindow* window, GLFWbool enabled) { updateWindowStyles(window); } void _glfwPlatformSetWindowFloating(_GLFWwindow* window, GLFWbool enabled) { const HWND after = enabled ? HWND_TOPMOST : HWND_NOTOPMOST; SetWindowPos(window->win32.handle, after, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE); } float _glfwPlatformGetWindowOpacity(_GLFWwindow* window) { BYTE alpha; DWORD flags; if ((GetWindowLongW(window->win32.handle, GWL_EXSTYLE) & WS_EX_LAYERED) && GetLayeredWindowAttributes(window->win32.handle, NULL, &alpha, &flags)) { if (flags & LWA_ALPHA) return alpha / 255.f; } return 1.f; } void _glfwPlatformSetWindowOpacity(_GLFWwindow* window, float opacity) { if (opacity < 1.f) { const BYTE alpha = (BYTE) (255 * opacity); DWORD style = GetWindowLongW(window->win32.handle, GWL_EXSTYLE); style |= WS_EX_LAYERED; SetWindowLongW(window->win32.handle, GWL_EXSTYLE, style); SetLayeredWindowAttributes(window->win32.handle, 0, alpha, LWA_ALPHA); } else { DWORD style = GetWindowLongW(window->win32.handle, GWL_EXSTYLE); style &= ~WS_EX_LAYERED; SetWindowLongW(window->win32.handle, GWL_EXSTYLE, style); } } void _glfwPlatformSetRawMouseMotion(_GLFWwindow *window, GLFWbool enabled) { if (_glfw.win32.disabledCursorWindow != window) return; if (enabled) enableRawMouseMotion(window); else disableRawMouseMotion(window); } GLFWbool _glfwPlatformRawMouseMotionSupported(void) { return GLFW_TRUE; } void _glfwPlatformPollEvents(void) { MSG msg; HWND handle; _GLFWwindow* window; while (PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE)) { if (msg.message == WM_QUIT) { // NOTE: While GLFW does not itself post WM_QUIT, other processes // may post it to this one, for example Task Manager // HACK: Treat WM_QUIT as a close on all windows window = _glfw.windowListHead; while (window) { _glfwInputWindowCloseRequest(window); window = window->next; } } else { TranslateMessage(&msg); DispatchMessageW(&msg); } } // HACK: Release modifier keys that the system did not emit KEYUP for // NOTE: Shift keys on Windows tend to "stick" when both are pressed as // no key up message is generated by the first key release // NOTE: Windows key is not reported as released by the Win+V hotkey // Other Win hotkeys are handled implicitly by _glfwInputWindowFocus // because they change the input focus // NOTE: The other half of this is in the WM_*KEY* handler in windowProc handle = GetActiveWindow(); if (handle) { window = GetPropW(handle, L"GLFW"); if (window) { int i; const int keys[4][2] = { { VK_LSHIFT, GLFW_KEY_LEFT_SHIFT }, { VK_RSHIFT, GLFW_KEY_RIGHT_SHIFT }, { VK_LWIN, GLFW_KEY_LEFT_SUPER }, { VK_RWIN, GLFW_KEY_RIGHT_SUPER } }; for (i = 0; i < 4; i++) { const int vk = keys[i][0]; const int key = keys[i][1]; const int scancode = _glfw.win32.scancodes[key]; if ((GetKeyState(vk) & 0x8000)) continue; if (window->keys[key] != GLFW_PRESS) continue; _glfwInputKey(window, key, scancode, GLFW_RELEASE, getKeyMods()); } } } window = _glfw.win32.disabledCursorWindow; if (window) { int width, height; _glfwPlatformGetWindowSize(window, &width, &height); // NOTE: Re-center the cursor only if it has moved since the last call, // to avoid breaking glfwWaitEvents with WM_MOUSEMOVE if (window->win32.lastCursorPosX != width / 2 || window->win32.lastCursorPosY != height / 2) { _glfwPlatformSetCursorPos(window, width / 2, height / 2); } } } void _glfwPlatformWaitEvents(void) { WaitMessage(); _glfwPlatformPollEvents(); } void _glfwPlatformWaitEventsTimeout(double timeout) { MsgWaitForMultipleObjects(0, NULL, FALSE, (DWORD) (timeout * 1e3), QS_ALLEVENTS); _glfwPlatformPollEvents(); } void _glfwPlatformPostEmptyEvent(void) { PostMessageW(_glfw.win32.helperWindowHandle, WM_NULL, 0, 0); } void _glfwPlatformGetCursorPos(_GLFWwindow* window, double* xpos, double* ypos) { POINT pos; if (GetCursorPos(&pos)) { ScreenToClient(window->win32.handle, &pos); if (xpos) *xpos = pos.x; if (ypos) *ypos = pos.y; } } void _glfwPlatformSetCursorPos(_GLFWwindow* window, double xpos, double ypos) { POINT pos = { (int) xpos, (int) ypos }; // Store the new position so it can be recognized later window->win32.lastCursorPosX = pos.x; window->win32.lastCursorPosY = pos.y; ClientToScreen(window->win32.handle, &pos); SetCursorPos(pos.x, pos.y); } void _glfwPlatformSetCursorMode(_GLFWwindow* window, int mode) { if (mode == GLFW_CURSOR_DISABLED) { if (_glfwPlatformWindowFocused(window)) disableCursor(window); } else if (_glfw.win32.disabledCursorWindow == window) enableCursor(window); else if (cursorInContentArea(window)) updateCursorImage(window); } const char* _glfwPlatformGetScancodeName(int scancode) { if (scancode < 0 || scancode > (KF_EXTENDED | 0xff) || _glfw.win32.keycodes[scancode] == GLFW_KEY_UNKNOWN) { _glfwInputError(GLFW_INVALID_VALUE, "Invalid scancode %i", scancode); return NULL; } return _glfw.win32.keynames[_glfw.win32.keycodes[scancode]]; } int _glfwPlatformGetKeyScancode(int key) { return _glfw.win32.scancodes[key]; } int _glfwPlatformCreateCursor(_GLFWcursor* cursor, const GLFWimage* image, int xhot, int yhot) { cursor->win32.handle = (HCURSOR) createIcon(image, xhot, yhot, GLFW_FALSE); if (!cursor->win32.handle) return GLFW_FALSE; return GLFW_TRUE; } int _glfwPlatformCreateStandardCursor(_GLFWcursor* cursor, int shape) { int id = 0; if (shape == GLFW_ARROW_CURSOR) id = OCR_NORMAL; else if (shape == GLFW_IBEAM_CURSOR) id = OCR_IBEAM; else if (shape == GLFW_CROSSHAIR_CURSOR) id = OCR_CROSS; else if (shape == GLFW_HAND_CURSOR) id = OCR_HAND; else if (shape == GLFW_HRESIZE_CURSOR) id = OCR_SIZEWE; else if (shape == GLFW_VRESIZE_CURSOR) id = OCR_SIZENS; else return GLFW_FALSE; cursor->win32.handle = LoadImageW(NULL, MAKEINTRESOURCEW(id), IMAGE_CURSOR, 0, 0, LR_DEFAULTSIZE | LR_SHARED); if (!cursor->win32.handle) { _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, "Win32: Failed to create standard cursor"); return GLFW_FALSE; } return GLFW_TRUE; } void _glfwPlatformDestroyCursor(_GLFWcursor* cursor) { if (cursor->win32.handle) DestroyIcon((HICON) cursor->win32.handle); } void _glfwPlatformSetCursor(_GLFWwindow* window, _GLFWcursor* cursor) { if (cursorInContentArea(window)) updateCursorImage(window); } void _glfwPlatformSetClipboardString(const char* string) { int characterCount; HANDLE object; WCHAR* buffer; characterCount = MultiByteToWideChar(CP_UTF8, 0, string, -1, NULL, 0); if (!characterCount) return; object = GlobalAlloc(GMEM_MOVEABLE, characterCount * sizeof(WCHAR)); if (!object) { _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, "Win32: Failed to allocate global handle for clipboard"); return; } buffer = GlobalLock(object); if (!buffer) { _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, "Win32: Failed to lock global handle"); GlobalFree(object); return; } MultiByteToWideChar(CP_UTF8, 0, string, -1, buffer, characterCount); GlobalUnlock(object); if (!OpenClipboard(_glfw.win32.helperWindowHandle)) { _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, "Win32: Failed to open clipboard"); GlobalFree(object); return; } EmptyClipboard(); SetClipboardData(CF_UNICODETEXT, object); CloseClipboard(); } const char* _glfwPlatformGetClipboardString(void) { HANDLE object; WCHAR* buffer; if (!OpenClipboard(_glfw.win32.helperWindowHandle)) { _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, "Win32: Failed to open clipboard"); return NULL; } object = GetClipboardData(CF_UNICODETEXT); if (!object) { _glfwInputErrorWin32(GLFW_FORMAT_UNAVAILABLE, "Win32: Failed to convert clipboard to string"); CloseClipboard(); return NULL; } buffer = GlobalLock(object); if (!buffer) { _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, "Win32: Failed to lock global handle"); CloseClipboard(); return NULL; } free(_glfw.win32.clipboardString); _glfw.win32.clipboardString = _glfwCreateUTF8FromWideStringWin32(buffer); GlobalUnlock(object); CloseClipboard(); return _glfw.win32.clipboardString; } void _glfwPlatformGetRequiredInstanceExtensions(char** extensions) { if (!_glfw.vk.KHR_surface || !_glfw.vk.KHR_win32_surface) return; extensions[0] = "VK_KHR_surface"; extensions[1] = "VK_KHR_win32_surface"; } int _glfwPlatformGetPhysicalDevicePresentationSupport(VkInstance instance, VkPhysicalDevice device, uint32_t queuefamily) { PFN_vkGetPhysicalDeviceWin32PresentationSupportKHR vkGetPhysicalDeviceWin32PresentationSupportKHR = (PFN_vkGetPhysicalDeviceWin32PresentationSupportKHR) vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceWin32PresentationSupportKHR"); if (!vkGetPhysicalDeviceWin32PresentationSupportKHR) { _glfwInputError(GLFW_API_UNAVAILABLE, "Win32: Vulkan instance missing VK_KHR_win32_surface extension"); return GLFW_FALSE; } return vkGetPhysicalDeviceWin32PresentationSupportKHR(device, queuefamily); } VkResult _glfwPlatformCreateWindowSurface(VkInstance instance, _GLFWwindow* window, const VkAllocationCallbacks* allocator, VkSurfaceKHR* surface) { VkResult err; VkWin32SurfaceCreateInfoKHR sci; PFN_vkCreateWin32SurfaceKHR vkCreateWin32SurfaceKHR; vkCreateWin32SurfaceKHR = (PFN_vkCreateWin32SurfaceKHR) vkGetInstanceProcAddr(instance, "vkCreateWin32SurfaceKHR"); if (!vkCreateWin32SurfaceKHR) { _glfwInputError(GLFW_API_UNAVAILABLE, "Win32: Vulkan instance missing VK_KHR_win32_surface extension"); return VK_ERROR_EXTENSION_NOT_PRESENT; } memset(&sci, 0, sizeof(sci)); sci.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR; sci.hinstance = GetModuleHandleW(NULL); sci.hwnd = window->win32.handle; err = vkCreateWin32SurfaceKHR(instance, &sci, allocator, surface); if (err) { _glfwInputError(GLFW_PLATFORM_ERROR, "Win32: Failed to create Vulkan surface: %s", _glfwGetVulkanResultString(err)); } return err; } ////////////////////////////////////////////////////////////////////////// ////// GLFW native API ////// ////////////////////////////////////////////////////////////////////////// GLFWAPI HWND glfwGetWin32Window(GLFWwindow* handle) { _GLFWwindow* window = (_GLFWwindow*) handle; _GLFW_REQUIRE_INIT_OR_RETURN(NULL); return window->win32.handle; } #endif #ifndef HEADER_GUARD_WGL_CONTEXT_C #define HEADER_GUARD_WGL_CONTEXT_C //======================================================================== // GLFW 3.3.7 WGL - www.glfw.org //------------------------------------------------------------------------ // Copyright (c) 2002-2006 Marcus Geelnard // Copyright (c) 2006-2019 Camilla Löwy // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would // be appreciated but is not required. // // 2. Altered source versions must be plainly marked as such, and must not // be misrepresented as being the original software. // // 3. This notice may not be removed or altered from any source // distribution. // //======================================================================== // Please use C89 style variable declarations in this file because VS 2010 //======================================================================== #include #include #include // Return the value corresponding to the specified attribute // static int findPixelFormatAttribValue(const int* attribs, int attribCount, const int* values, int attrib) { int i; for (i = 0; i < attribCount; i++) { if (attribs[i] == attrib) return values[i]; } _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, "WGL: Unknown pixel format attribute requested"); return 0; } #define addAttrib(a) \ { \ assert((size_t) attribCount < sizeof(attribs) / sizeof(attribs[0])); \ attribs[attribCount++] = a; \ } #define findAttribValue(a) \ findPixelFormatAttribValue(attribs, attribCount, values, a) // Return a list of available and usable framebuffer configs // static int choosePixelFormat(_GLFWwindow* window, const _GLFWctxconfig* ctxconfig, const _GLFWfbconfig* fbconfig) { _GLFWfbconfig* usableConfigs; const _GLFWfbconfig* closest; int i, pixelFormat, nativeCount, usableCount = 0, attribCount = 0; int attribs[40]; int values[sizeof(attribs) / sizeof(attribs[0])]; if (_glfw.wgl.ARB_pixel_format) { const int attrib = WGL_NUMBER_PIXEL_FORMATS_ARB; if (!wglGetPixelFormatAttribivARB(window->context.wgl.dc, 1, 0, 1, &attrib, &nativeCount)) { _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, "WGL: Failed to retrieve pixel format attribute"); return 0; } addAttrib(WGL_SUPPORT_OPENGL_ARB); addAttrib(WGL_DRAW_TO_WINDOW_ARB); addAttrib(WGL_PIXEL_TYPE_ARB); addAttrib(WGL_ACCELERATION_ARB); addAttrib(WGL_RED_BITS_ARB); addAttrib(WGL_RED_SHIFT_ARB); addAttrib(WGL_GREEN_BITS_ARB); addAttrib(WGL_GREEN_SHIFT_ARB); addAttrib(WGL_BLUE_BITS_ARB); addAttrib(WGL_BLUE_SHIFT_ARB); addAttrib(WGL_ALPHA_BITS_ARB); addAttrib(WGL_ALPHA_SHIFT_ARB); addAttrib(WGL_DEPTH_BITS_ARB); addAttrib(WGL_STENCIL_BITS_ARB); addAttrib(WGL_ACCUM_BITS_ARB); addAttrib(WGL_ACCUM_RED_BITS_ARB); addAttrib(WGL_ACCUM_GREEN_BITS_ARB); addAttrib(WGL_ACCUM_BLUE_BITS_ARB); addAttrib(WGL_ACCUM_ALPHA_BITS_ARB); addAttrib(WGL_AUX_BUFFERS_ARB); addAttrib(WGL_STEREO_ARB); addAttrib(WGL_DOUBLE_BUFFER_ARB); if (_glfw.wgl.ARB_multisample) addAttrib(WGL_SAMPLES_ARB); if (ctxconfig->client == GLFW_OPENGL_API) { if (_glfw.wgl.ARB_framebuffer_sRGB || _glfw.wgl.EXT_framebuffer_sRGB) addAttrib(WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB); } else { if (_glfw.wgl.EXT_colorspace) addAttrib(WGL_COLORSPACE_EXT); } } else { nativeCount = DescribePixelFormat(window->context.wgl.dc, 1, sizeof(PIXELFORMATDESCRIPTOR), NULL); } usableConfigs = calloc(nativeCount, sizeof(_GLFWfbconfig)); for (i = 0; i < nativeCount; i++) { _GLFWfbconfig* u = usableConfigs + usableCount; pixelFormat = i + 1; if (_glfw.wgl.ARB_pixel_format) { // Get pixel format attributes through "modern" extension if (!wglGetPixelFormatAttribivARB(window->context.wgl.dc, pixelFormat, 0, attribCount, attribs, values)) { _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, "WGL: Failed to retrieve pixel format attributes"); free(usableConfigs); return 0; } if (!findAttribValue(WGL_SUPPORT_OPENGL_ARB) || !findAttribValue(WGL_DRAW_TO_WINDOW_ARB)) { continue; } if (findAttribValue(WGL_PIXEL_TYPE_ARB) != WGL_TYPE_RGBA_ARB) continue; if (findAttribValue(WGL_ACCELERATION_ARB) == WGL_NO_ACCELERATION_ARB) continue; if (findAttribValue(WGL_DOUBLE_BUFFER_ARB) != fbconfig->doublebuffer) continue; u->redBits = findAttribValue(WGL_RED_BITS_ARB); u->greenBits = findAttribValue(WGL_GREEN_BITS_ARB); u->blueBits = findAttribValue(WGL_BLUE_BITS_ARB); u->alphaBits = findAttribValue(WGL_ALPHA_BITS_ARB); u->depthBits = findAttribValue(WGL_DEPTH_BITS_ARB); u->stencilBits = findAttribValue(WGL_STENCIL_BITS_ARB); u->accumRedBits = findAttribValue(WGL_ACCUM_RED_BITS_ARB); u->accumGreenBits = findAttribValue(WGL_ACCUM_GREEN_BITS_ARB); u->accumBlueBits = findAttribValue(WGL_ACCUM_BLUE_BITS_ARB); u->accumAlphaBits = findAttribValue(WGL_ACCUM_ALPHA_BITS_ARB); u->auxBuffers = findAttribValue(WGL_AUX_BUFFERS_ARB); if (findAttribValue(WGL_STEREO_ARB)) u->stereo = GLFW_TRUE; if (_glfw.wgl.ARB_multisample) u->samples = findAttribValue(WGL_SAMPLES_ARB); if (ctxconfig->client == GLFW_OPENGL_API) { if (_glfw.wgl.ARB_framebuffer_sRGB || _glfw.wgl.EXT_framebuffer_sRGB) { if (findAttribValue(WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB)) u->sRGB = GLFW_TRUE; } } else { if (_glfw.wgl.EXT_colorspace) { if (findAttribValue(WGL_COLORSPACE_EXT) == WGL_COLORSPACE_SRGB_EXT) u->sRGB = GLFW_TRUE; } } } else { // Get pixel format attributes through legacy PFDs PIXELFORMATDESCRIPTOR pfd; if (!DescribePixelFormat(window->context.wgl.dc, pixelFormat, sizeof(PIXELFORMATDESCRIPTOR), &pfd)) { _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, "WGL: Failed to describe pixel format"); free(usableConfigs); return 0; } if (!(pfd.dwFlags & PFD_DRAW_TO_WINDOW) || !(pfd.dwFlags & PFD_SUPPORT_OPENGL)) { continue; } if (!(pfd.dwFlags & PFD_GENERIC_ACCELERATED) && (pfd.dwFlags & PFD_GENERIC_FORMAT)) { continue; } if (pfd.iPixelType != PFD_TYPE_RGBA) continue; if (!!(pfd.dwFlags & PFD_DOUBLEBUFFER) != fbconfig->doublebuffer) continue; u->redBits = pfd.cRedBits; u->greenBits = pfd.cGreenBits; u->blueBits = pfd.cBlueBits; u->alphaBits = pfd.cAlphaBits; u->depthBits = pfd.cDepthBits; u->stencilBits = pfd.cStencilBits; u->accumRedBits = pfd.cAccumRedBits; u->accumGreenBits = pfd.cAccumGreenBits; u->accumBlueBits = pfd.cAccumBlueBits; u->accumAlphaBits = pfd.cAccumAlphaBits; u->auxBuffers = pfd.cAuxBuffers; if (pfd.dwFlags & PFD_STEREO) u->stereo = GLFW_TRUE; } u->handle = pixelFormat; usableCount++; } if (!usableCount) { _glfwInputError(GLFW_API_UNAVAILABLE, "WGL: The driver does not appear to support OpenGL"); free(usableConfigs); return 0; } closest = _glfwChooseFBConfig(fbconfig, usableConfigs, usableCount); if (!closest) { _glfwInputError(GLFW_FORMAT_UNAVAILABLE, "WGL: Failed to find a suitable pixel format"); free(usableConfigs); return 0; } pixelFormat = (int) closest->handle; free(usableConfigs); return pixelFormat; } #undef addAttrib #undef findAttribValue static void makeContextCurrentWGL(_GLFWwindow* window) { if (window) { if (wglMakeCurrent(window->context.wgl.dc, window->context.wgl.handle)) _glfwPlatformSetTls(&_glfw.contextSlot, window); else { _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, "WGL: Failed to make context current"); _glfwPlatformSetTls(&_glfw.contextSlot, NULL); } } else { if (!wglMakeCurrent(NULL, NULL)) { _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, "WGL: Failed to clear current context"); } _glfwPlatformSetTls(&_glfw.contextSlot, NULL); } } static void swapBuffersWGL(_GLFWwindow* window) { if (!window->monitor) { if (IsWindowsVistaOrGreater()) { // DWM Composition is always enabled on Win8+ BOOL enabled = IsWindows8OrGreater(); // HACK: Use DwmFlush when desktop composition is enabled if (enabled || (SUCCEEDED(DwmIsCompositionEnabled(&enabled)) && enabled)) { int count = abs(window->context.wgl.interval); while (count--) DwmFlush(); } } } SwapBuffers(window->context.wgl.dc); } static void swapIntervalWGL(int interval) { _GLFWwindow* window = _glfwPlatformGetTls(&_glfw.contextSlot); window->context.wgl.interval = interval; if (!window->monitor) { if (IsWindowsVistaOrGreater()) { // DWM Composition is always enabled on Win8+ BOOL enabled = IsWindows8OrGreater(); // HACK: Disable WGL swap interval when desktop composition is enabled to // avoid interfering with DWM vsync if (enabled || (SUCCEEDED(DwmIsCompositionEnabled(&enabled)) && enabled)) interval = 0; } } if (_glfw.wgl.EXT_swap_control) wglSwapIntervalEXT(interval); } static int extensionSupportedWGL(const char* extension) { const char* extensions = NULL; if (_glfw.wgl.GetExtensionsStringARB) extensions = wglGetExtensionsStringARB(wglGetCurrentDC()); else if (_glfw.wgl.GetExtensionsStringEXT) extensions = wglGetExtensionsStringEXT(); if (!extensions) return GLFW_FALSE; return _glfwStringInExtensionString(extension, extensions); } static GLFWglproc getProcAddressWGL(const char* procname) { const GLFWglproc proc = (GLFWglproc) wglGetProcAddress(procname); if (proc) return proc; return (GLFWglproc) GetProcAddress(_glfw.wgl.instance, procname); } static void destroyContextWGL(_GLFWwindow* window) { if (window->context.wgl.handle) { wglDeleteContext(window->context.wgl.handle); window->context.wgl.handle = NULL; } } ////////////////////////////////////////////////////////////////////////// ////// GLFW internal API ////// ////////////////////////////////////////////////////////////////////////// // Initialize WGL // GLFWbool _glfwInitWGL(void) { PIXELFORMATDESCRIPTOR pfd; HGLRC prc, rc; HDC pdc, dc; if (_glfw.wgl.instance) return GLFW_TRUE; _glfw.wgl.instance = LoadLibraryA("opengl32.dll"); if (!_glfw.wgl.instance) { _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, "WGL: Failed to load opengl32.dll"); return GLFW_FALSE; } _glfw.wgl.CreateContext = (PFN_wglCreateContext) GetProcAddress(_glfw.wgl.instance, "wglCreateContext"); _glfw.wgl.DeleteContext = (PFN_wglDeleteContext) GetProcAddress(_glfw.wgl.instance, "wglDeleteContext"); _glfw.wgl.GetProcAddress = (PFN_wglGetProcAddress) GetProcAddress(_glfw.wgl.instance, "wglGetProcAddress"); _glfw.wgl.GetCurrentDC = (PFN_wglGetCurrentDC) GetProcAddress(_glfw.wgl.instance, "wglGetCurrentDC"); _glfw.wgl.GetCurrentContext = (PFN_wglGetCurrentContext) GetProcAddress(_glfw.wgl.instance, "wglGetCurrentContext"); _glfw.wgl.MakeCurrent = (PFN_wglMakeCurrent) GetProcAddress(_glfw.wgl.instance, "wglMakeCurrent"); _glfw.wgl.ShareLists = (PFN_wglShareLists) GetProcAddress(_glfw.wgl.instance, "wglShareLists"); // NOTE: A dummy context has to be created for opengl32.dll to load the // OpenGL ICD, from which we can then query WGL extensions // NOTE: This code will accept the Microsoft GDI ICD; accelerated context // creation failure occurs during manual pixel format enumeration dc = GetDC(_glfw.win32.helperWindowHandle); ZeroMemory(&pfd, sizeof(pfd)); pfd.nSize = sizeof(pfd); pfd.nVersion = 1; pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER; pfd.iPixelType = PFD_TYPE_RGBA; pfd.cColorBits = 24; if (!SetPixelFormat(dc, ChoosePixelFormat(dc, &pfd), &pfd)) { _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, "WGL: Failed to set pixel format for dummy context"); return GLFW_FALSE; } rc = wglCreateContext(dc); if (!rc) { _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, "WGL: Failed to create dummy context"); return GLFW_FALSE; } pdc = wglGetCurrentDC(); prc = wglGetCurrentContext(); if (!wglMakeCurrent(dc, rc)) { _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, "WGL: Failed to make dummy context current"); wglMakeCurrent(pdc, prc); wglDeleteContext(rc); return GLFW_FALSE; } // NOTE: Functions must be loaded first as they're needed to retrieve the // extension string that tells us whether the functions are supported _glfw.wgl.GetExtensionsStringEXT = (PFNWGLGETEXTENSIONSSTRINGEXTPROC) wglGetProcAddress("wglGetExtensionsStringEXT"); _glfw.wgl.GetExtensionsStringARB = (PFNWGLGETEXTENSIONSSTRINGARBPROC) wglGetProcAddress("wglGetExtensionsStringARB"); _glfw.wgl.CreateContextAttribsARB = (PFNWGLCREATECONTEXTATTRIBSARBPROC) wglGetProcAddress("wglCreateContextAttribsARB"); _glfw.wgl.SwapIntervalEXT = (PFNWGLSWAPINTERVALEXTPROC) wglGetProcAddress("wglSwapIntervalEXT"); _glfw.wgl.GetPixelFormatAttribivARB = (PFNWGLGETPIXELFORMATATTRIBIVARBPROC) wglGetProcAddress("wglGetPixelFormatAttribivARB"); // NOTE: WGL_ARB_extensions_string and WGL_EXT_extensions_string are not // checked below as we are already using them _glfw.wgl.ARB_multisample = extensionSupportedWGL("WGL_ARB_multisample"); _glfw.wgl.ARB_framebuffer_sRGB = extensionSupportedWGL("WGL_ARB_framebuffer_sRGB"); _glfw.wgl.EXT_framebuffer_sRGB = extensionSupportedWGL("WGL_EXT_framebuffer_sRGB"); _glfw.wgl.ARB_create_context = extensionSupportedWGL("WGL_ARB_create_context"); _glfw.wgl.ARB_create_context_profile = extensionSupportedWGL("WGL_ARB_create_context_profile"); _glfw.wgl.EXT_create_context_es2_profile = extensionSupportedWGL("WGL_EXT_create_context_es2_profile"); _glfw.wgl.ARB_create_context_robustness = extensionSupportedWGL("WGL_ARB_create_context_robustness"); _glfw.wgl.ARB_create_context_no_error = extensionSupportedWGL("WGL_ARB_create_context_no_error"); _glfw.wgl.EXT_swap_control = extensionSupportedWGL("WGL_EXT_swap_control"); _glfw.wgl.EXT_colorspace = extensionSupportedWGL("WGL_EXT_colorspace"); _glfw.wgl.ARB_pixel_format = extensionSupportedWGL("WGL_ARB_pixel_format"); _glfw.wgl.ARB_context_flush_control = extensionSupportedWGL("WGL_ARB_context_flush_control"); wglMakeCurrent(pdc, prc); wglDeleteContext(rc); return GLFW_TRUE; } // Terminate WGL // void _glfwTerminateWGL(void) { if (_glfw.wgl.instance) FreeLibrary(_glfw.wgl.instance); } #define setAttrib(a, v) \ { \ assert(((size_t) index + 1) < sizeof(attribs) / sizeof(attribs[0])); \ attribs[index++] = a; \ attribs[index++] = v; \ } // Create the OpenGL or OpenGL ES context // GLFWbool _glfwCreateContextWGL(_GLFWwindow* window, const _GLFWctxconfig* ctxconfig, const _GLFWfbconfig* fbconfig) { int attribs[40]; int pixelFormat; PIXELFORMATDESCRIPTOR pfd; HGLRC share = NULL; if (ctxconfig->share) share = ctxconfig->share->context.wgl.handle; window->context.wgl.dc = GetDC(window->win32.handle); if (!window->context.wgl.dc) { _glfwInputError(GLFW_PLATFORM_ERROR, "WGL: Failed to retrieve DC for window"); return GLFW_FALSE; } pixelFormat = choosePixelFormat(window, ctxconfig, fbconfig); if (!pixelFormat) return GLFW_FALSE; if (!DescribePixelFormat(window->context.wgl.dc, pixelFormat, sizeof(pfd), &pfd)) { _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, "WGL: Failed to retrieve PFD for selected pixel format"); return GLFW_FALSE; } if (!SetPixelFormat(window->context.wgl.dc, pixelFormat, &pfd)) { _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, "WGL: Failed to set selected pixel format"); return GLFW_FALSE; } if (ctxconfig->client == GLFW_OPENGL_API) { if (ctxconfig->forward) { if (!_glfw.wgl.ARB_create_context) { _glfwInputError(GLFW_VERSION_UNAVAILABLE, "WGL: A forward compatible OpenGL context requested but WGL_ARB_create_context is unavailable"); return GLFW_FALSE; } } if (ctxconfig->profile) { if (!_glfw.wgl.ARB_create_context_profile) { _glfwInputError(GLFW_VERSION_UNAVAILABLE, "WGL: OpenGL profile requested but WGL_ARB_create_context_profile is unavailable"); return GLFW_FALSE; } } } else { if (!_glfw.wgl.ARB_create_context || !_glfw.wgl.ARB_create_context_profile || !_glfw.wgl.EXT_create_context_es2_profile) { _glfwInputError(GLFW_API_UNAVAILABLE, "WGL: OpenGL ES requested but WGL_ARB_create_context_es2_profile is unavailable"); return GLFW_FALSE; } } if (_glfw.wgl.ARB_create_context) { int index = 0, mask = 0, flags = 0; if (ctxconfig->client == GLFW_OPENGL_API) { if (ctxconfig->forward) flags |= WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB; if (ctxconfig->profile == GLFW_OPENGL_CORE_PROFILE) mask |= WGL_CONTEXT_CORE_PROFILE_BIT_ARB; else if (ctxconfig->profile == GLFW_OPENGL_COMPAT_PROFILE) mask |= WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB; } else mask |= WGL_CONTEXT_ES2_PROFILE_BIT_EXT; if (ctxconfig->debug) flags |= WGL_CONTEXT_DEBUG_BIT_ARB; if (ctxconfig->robustness) { if (_glfw.wgl.ARB_create_context_robustness) { if (ctxconfig->robustness == GLFW_NO_RESET_NOTIFICATION) { setAttrib(WGL_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB, WGL_NO_RESET_NOTIFICATION_ARB); } else if (ctxconfig->robustness == GLFW_LOSE_CONTEXT_ON_RESET) { setAttrib(WGL_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB, WGL_LOSE_CONTEXT_ON_RESET_ARB); } flags |= WGL_CONTEXT_ROBUST_ACCESS_BIT_ARB; } } if (ctxconfig->release) { if (_glfw.wgl.ARB_context_flush_control) { if (ctxconfig->release == GLFW_RELEASE_BEHAVIOR_NONE) { setAttrib(WGL_CONTEXT_RELEASE_BEHAVIOR_ARB, WGL_CONTEXT_RELEASE_BEHAVIOR_NONE_ARB); } else if (ctxconfig->release == GLFW_RELEASE_BEHAVIOR_FLUSH) { setAttrib(WGL_CONTEXT_RELEASE_BEHAVIOR_ARB, WGL_CONTEXT_RELEASE_BEHAVIOR_FLUSH_ARB); } } } if (ctxconfig->noerror) { if (_glfw.wgl.ARB_create_context_no_error) setAttrib(WGL_CONTEXT_OPENGL_NO_ERROR_ARB, GLFW_TRUE); } // NOTE: Only request an explicitly versioned context when necessary, as // explicitly requesting version 1.0 does not always return the // highest version supported by the driver if (ctxconfig->major != 1 || ctxconfig->minor != 0) { setAttrib(WGL_CONTEXT_MAJOR_VERSION_ARB, ctxconfig->major); setAttrib(WGL_CONTEXT_MINOR_VERSION_ARB, ctxconfig->minor); } if (flags) setAttrib(WGL_CONTEXT_FLAGS_ARB, flags); if (mask) setAttrib(WGL_CONTEXT_PROFILE_MASK_ARB, mask); setAttrib(0, 0); window->context.wgl.handle = wglCreateContextAttribsARB(window->context.wgl.dc, share, attribs); if (!window->context.wgl.handle) { const DWORD error = GetLastError(); if (error == (0xc0070000 | ERROR_INVALID_VERSION_ARB)) { if (ctxconfig->client == GLFW_OPENGL_API) { _glfwInputError(GLFW_VERSION_UNAVAILABLE, "WGL: Driver does not support OpenGL version %i.%i", ctxconfig->major, ctxconfig->minor); } else { _glfwInputError(GLFW_VERSION_UNAVAILABLE, "WGL: Driver does not support OpenGL ES version %i.%i", ctxconfig->major, ctxconfig->minor); } } else if (error == (0xc0070000 | ERROR_INVALID_PROFILE_ARB)) { _glfwInputError(GLFW_VERSION_UNAVAILABLE, "WGL: Driver does not support the requested OpenGL profile"); } else if (error == (0xc0070000 | ERROR_INCOMPATIBLE_DEVICE_CONTEXTS_ARB)) { _glfwInputError(GLFW_INVALID_VALUE, "WGL: The share context is not compatible with the requested context"); } else { if (ctxconfig->client == GLFW_OPENGL_API) { _glfwInputError(GLFW_VERSION_UNAVAILABLE, "WGL: Failed to create OpenGL context"); } else { _glfwInputError(GLFW_VERSION_UNAVAILABLE, "WGL: Failed to create OpenGL ES context"); } } return GLFW_FALSE; } } else { window->context.wgl.handle = wglCreateContext(window->context.wgl.dc); if (!window->context.wgl.handle) { _glfwInputErrorWin32(GLFW_VERSION_UNAVAILABLE, "WGL: Failed to create OpenGL context"); return GLFW_FALSE; } if (share) { if (!wglShareLists(share, window->context.wgl.handle)) { _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, "WGL: Failed to enable sharing with specified OpenGL context"); return GLFW_FALSE; } } } window->context.makeCurrent = makeContextCurrentWGL; window->context.swapBuffers = swapBuffersWGL; window->context.swapInterval = swapIntervalWGL; window->context.extensionSupported = extensionSupportedWGL; window->context.getProcAddress = getProcAddressWGL; window->context.destroy = destroyContextWGL; return GLFW_TRUE; } #undef setAttrib ////////////////////////////////////////////////////////////////////////// ////// GLFW native API ////// ////////////////////////////////////////////////////////////////////////// GLFWAPI HGLRC glfwGetWGLContext(GLFWwindow* handle) { _GLFWwindow* window = (_GLFWwindow*) handle; _GLFW_REQUIRE_INIT_OR_RETURN(NULL); if (window->context.source != GLFW_NATIVE_CONTEXT_API) { _glfwInputError(GLFW_NO_WINDOW_CONTEXT, NULL); return NULL; } return window->context.wgl.handle; } #endif #endif #ifdef _GLFW_OSMESA #ifndef HEADER_GUARD_NULL_INIT_C #define HEADER_GUARD_NULL_INIT_C //======================================================================== // GLFW 3.3.7 - www.glfw.org //------------------------------------------------------------------------ // Copyright (c) 2016 Google Inc. // Copyright (c) 2016-2017 Camilla Löwy // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would // be appreciated but is not required. // // 2. Altered source versions must be plainly marked as such, and must not // be misrepresented as being the original software. // // 3. This notice may not be removed or altered from any source // distribution. // //======================================================================== // It is fine to use C99 in this file because it will not be built with VS //======================================================================== ////////////////////////////////////////////////////////////////////////// ////// GLFW platform API ////// ////////////////////////////////////////////////////////////////////////// int _glfwPlatformInit(void) { _glfwInitTimerPOSIX(); return GLFW_TRUE; } void _glfwPlatformTerminate(void) { _glfwTerminateOSMesa(); } const char* _glfwPlatformGetVersionString(void) { return _GLFW_VERSION_NUMBER " null OSMesa"; } #endif #ifndef HEADER_GUARD_NULL_MONITOR_C #define HEADER_GUARD_NULL_MONITOR_C //======================================================================== // GLFW 3.3.7 - www.glfw.org //------------------------------------------------------------------------ // Copyright (c) 2016 Google Inc. // Copyright (c) 2016-2019 Camilla Löwy // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would // be appreciated but is not required. // // 2. Altered source versions must be plainly marked as such, and must not // be misrepresented as being the original software. // // 3. This notice may not be removed or altered from any source // distribution. // //======================================================================== // It is fine to use C99 in this file because it will not be built with VS //======================================================================== ////////////////////////////////////////////////////////////////////////// ////// GLFW platform API ////// ////////////////////////////////////////////////////////////////////////// void _glfwPlatformFreeMonitor(_GLFWmonitor* monitor) { } void _glfwPlatformGetMonitorPos(_GLFWmonitor* monitor, int* xpos, int* ypos) { } void _glfwPlatformGetMonitorContentScale(_GLFWmonitor* monitor, float* xscale, float* yscale) { if (xscale) *xscale = 1.f; if (yscale) *yscale = 1.f; } void _glfwPlatformGetMonitorWorkarea(_GLFWmonitor* monitor, int* xpos, int* ypos, int* width, int* height) { } GLFWvidmode* _glfwPlatformGetVideoModes(_GLFWmonitor* monitor, int* found) { return NULL; } void _glfwPlatformGetVideoMode(_GLFWmonitor* monitor, GLFWvidmode* mode) { } GLFWbool _glfwPlatformGetGammaRamp(_GLFWmonitor* monitor, GLFWgammaramp* ramp) { return GLFW_FALSE; } void _glfwPlatformSetGammaRamp(_GLFWmonitor* monitor, const GLFWgammaramp* ramp) { } #endif #ifndef HEADER_GUARD_NULL_WINDOW_C #define HEADER_GUARD_NULL_WINDOW_C //======================================================================== // GLFW 3.3.7 - www.glfw.org //------------------------------------------------------------------------ // Copyright (c) 2016 Google Inc. // Copyright (c) 2016-2019 Camilla Löwy // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would // be appreciated but is not required. // // 2. Altered source versions must be plainly marked as such, and must not // be misrepresented as being the original software. // // 3. This notice may not be removed or altered from any source // distribution. // //======================================================================== // It is fine to use C99 in this file because it will not be built with VS //======================================================================== static int createNativeWindow(_GLFWwindow* window, const _GLFWwndconfig* wndconfig) { window->null.width = wndconfig->width; window->null.height = wndconfig->height; return GLFW_TRUE; } ////////////////////////////////////////////////////////////////////////// ////// GLFW platform API ////// ////////////////////////////////////////////////////////////////////////// int _glfwPlatformCreateWindow(_GLFWwindow* window, const _GLFWwndconfig* wndconfig, const _GLFWctxconfig* ctxconfig, const _GLFWfbconfig* fbconfig) { if (!createNativeWindow(window, wndconfig)) return GLFW_FALSE; if (ctxconfig->client != GLFW_NO_API) { if (ctxconfig->source == GLFW_NATIVE_CONTEXT_API || ctxconfig->source == GLFW_OSMESA_CONTEXT_API) { if (!_glfwInitOSMesa()) return GLFW_FALSE; if (!_glfwCreateContextOSMesa(window, ctxconfig, fbconfig)) return GLFW_FALSE; } else { _glfwInputError(GLFW_API_UNAVAILABLE, "Null: EGL not available"); return GLFW_FALSE; } } return GLFW_TRUE; } void _glfwPlatformDestroyWindow(_GLFWwindow* window) { if (window->context.destroy) window->context.destroy(window); } void _glfwPlatformSetWindowTitle(_GLFWwindow* window, const char* title) { } void _glfwPlatformSetWindowIcon(_GLFWwindow* window, int count, const GLFWimage* images) { } void _glfwPlatformSetWindowMonitor(_GLFWwindow* window, _GLFWmonitor* monitor, int xpos, int ypos, int width, int height, int refreshRate) { } void _glfwPlatformGetWindowPos(_GLFWwindow* window, int* xpos, int* ypos) { } void _glfwPlatformSetWindowPos(_GLFWwindow* window, int xpos, int ypos) { } void _glfwPlatformGetWindowSize(_GLFWwindow* window, int* width, int* height) { if (width) *width = window->null.width; if (height) *height = window->null.height; } void _glfwPlatformSetWindowSize(_GLFWwindow* window, int width, int height) { window->null.width = width; window->null.height = height; } void _glfwPlatformSetWindowSizeLimits(_GLFWwindow* window, int minwidth, int minheight, int maxwidth, int maxheight) { } void _glfwPlatformSetWindowAspectRatio(_GLFWwindow* window, int n, int d) { } void _glfwPlatformGetFramebufferSize(_GLFWwindow* window, int* width, int* height) { if (width) *width = window->null.width; if (height) *height = window->null.height; } void _glfwPlatformGetWindowFrameSize(_GLFWwindow* window, int* left, int* top, int* right, int* bottom) { } void _glfwPlatformGetWindowContentScale(_GLFWwindow* window, float* xscale, float* yscale) { if (xscale) *xscale = 1.f; if (yscale) *yscale = 1.f; } void _glfwPlatformIconifyWindow(_GLFWwindow* window) { } void _glfwPlatformRestoreWindow(_GLFWwindow* window) { } void _glfwPlatformMaximizeWindow(_GLFWwindow* window) { } int _glfwPlatformWindowMaximized(_GLFWwindow* window) { return GLFW_FALSE; } int _glfwPlatformWindowHovered(_GLFWwindow* window) { return GLFW_FALSE; } int _glfwPlatformFramebufferTransparent(_GLFWwindow* window) { return GLFW_FALSE; } void _glfwPlatformSetWindowResizable(_GLFWwindow* window, GLFWbool enabled) { } void _glfwPlatformSetWindowDecorated(_GLFWwindow* window, GLFWbool enabled) { } void _glfwPlatformSetWindowFloating(_GLFWwindow* window, GLFWbool enabled) { } float _glfwPlatformGetWindowOpacity(_GLFWwindow* window) { return 1.f; } void _glfwPlatformSetWindowOpacity(_GLFWwindow* window, float opacity) { } void _glfwPlatformSetRawMouseMotion(_GLFWwindow *window, GLFWbool enabled) { } GLFWbool _glfwPlatformRawMouseMotionSupported(void) { return GLFW_FALSE; } void _glfwPlatformShowWindow(_GLFWwindow* window) { } void _glfwPlatformRequestWindowAttention(_GLFWwindow* window) { } void _glfwPlatformUnhideWindow(_GLFWwindow* window) { } void _glfwPlatformHideWindow(_GLFWwindow* window) { } void _glfwPlatformFocusWindow(_GLFWwindow* window) { } int _glfwPlatformWindowFocused(_GLFWwindow* window) { return GLFW_FALSE; } int _glfwPlatformWindowIconified(_GLFWwindow* window) { return GLFW_FALSE; } int _glfwPlatformWindowVisible(_GLFWwindow* window) { return GLFW_FALSE; } void _glfwPlatformPollEvents(void) { } void _glfwPlatformWaitEvents(void) { } void _glfwPlatformWaitEventsTimeout(double timeout) { } void _glfwPlatformPostEmptyEvent(void) { } void _glfwPlatformGetCursorPos(_GLFWwindow* window, double* xpos, double* ypos) { } void _glfwPlatformSetCursorPos(_GLFWwindow* window, double x, double y) { } void _glfwPlatformSetCursorMode(_GLFWwindow* window, int mode) { } int _glfwPlatformCreateCursor(_GLFWcursor* cursor, const GLFWimage* image, int xhot, int yhot) { return GLFW_TRUE; } int _glfwPlatformCreateStandardCursor(_GLFWcursor* cursor, int shape) { return GLFW_TRUE; } void _glfwPlatformDestroyCursor(_GLFWcursor* cursor) { } void _glfwPlatformSetCursor(_GLFWwindow* window, _GLFWcursor* cursor) { } void _glfwPlatformSetClipboardString(const char* string) { } const char* _glfwPlatformGetClipboardString(void) { return NULL; } const char* _glfwPlatformGetScancodeName(int scancode) { return ""; } int _glfwPlatformGetKeyScancode(int key) { return -1; } void _glfwPlatformGetRequiredInstanceExtensions(char** extensions) { } int _glfwPlatformGetPhysicalDevicePresentationSupport(VkInstance instance, VkPhysicalDevice device, uint32_t queuefamily) { return GLFW_FALSE; } VkResult _glfwPlatformCreateWindowSurface(VkInstance instance, _GLFWwindow* window, const VkAllocationCallbacks* allocator, VkSurfaceKHR* surface) { // This seems like the most appropriate error to return here return VK_ERROR_INITIALIZATION_FAILED; } #endif #ifndef HEADER_GUARD_NULL_JOYSTICK_C #define HEADER_GUARD_NULL_JOYSTICK_C //======================================================================== // GLFW 3.3.7 - www.glfw.org //------------------------------------------------------------------------ // Copyright (c) 2016-2017 Camilla Löwy // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would // be appreciated but is not required. // // 2. Altered source versions must be plainly marked as such, and must not // be misrepresented as being the original software. // // 3. This notice may not be removed or altered from any source // distribution. // //======================================================================== // It is fine to use C99 in this file because it will not be built with VS //======================================================================== ////////////////////////////////////////////////////////////////////////// ////// GLFW platform API ////// ////////////////////////////////////////////////////////////////////////// int _glfwPlatformPollJoystick(_GLFWjoystick* js, int mode) { return GLFW_FALSE; } void _glfwPlatformUpdateGamepadGUID(char* guid) { } #endif #endif #ifdef _GLFW_X11 #ifndef HEADER_GUARD_X11_INIT_C #define HEADER_GUARD_X11_INIT_C //======================================================================== // GLFW 3.3.7 X11 - www.glfw.org //------------------------------------------------------------------------ // Copyright (c) 2002-2006 Marcus Geelnard // Copyright (c) 2006-2019 Camilla Löwy // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would // be appreciated but is not required. // // 2. Altered source versions must be plainly marked as such, and must not // be misrepresented as being the original software. // // 3. This notice may not be removed or altered from any source // distribution. // //======================================================================== // It is fine to use C99 in this file because it will not be built with VS //======================================================================== #include #include #include #include #include #include #include #include #include // Translate the X11 KeySyms for a key to a GLFW key code // NOTE: This is only used as a fallback, in case the XKB method fails // It is layout-dependent and will fail partially on most non-US layouts // static int translateKeySyms(const KeySym* keysyms, int width) { if (width > 1) { switch (keysyms[1]) { case XK_KP_0: return GLFW_KEY_KP_0; case XK_KP_1: return GLFW_KEY_KP_1; case XK_KP_2: return GLFW_KEY_KP_2; case XK_KP_3: return GLFW_KEY_KP_3; case XK_KP_4: return GLFW_KEY_KP_4; case XK_KP_5: return GLFW_KEY_KP_5; case XK_KP_6: return GLFW_KEY_KP_6; case XK_KP_7: return GLFW_KEY_KP_7; case XK_KP_8: return GLFW_KEY_KP_8; case XK_KP_9: return GLFW_KEY_KP_9; case XK_KP_Separator: case XK_KP_Decimal: return GLFW_KEY_KP_DECIMAL; case XK_KP_Equal: return GLFW_KEY_KP_EQUAL; case XK_KP_Enter: return GLFW_KEY_KP_ENTER; default: break; } } switch (keysyms[0]) { case XK_Escape: return GLFW_KEY_ESCAPE; case XK_Tab: return GLFW_KEY_TAB; case XK_Shift_L: return GLFW_KEY_LEFT_SHIFT; case XK_Shift_R: return GLFW_KEY_RIGHT_SHIFT; case XK_Control_L: return GLFW_KEY_LEFT_CONTROL; case XK_Control_R: return GLFW_KEY_RIGHT_CONTROL; case XK_Meta_L: case XK_Alt_L: return GLFW_KEY_LEFT_ALT; case XK_Mode_switch: // Mapped to Alt_R on many keyboards case XK_ISO_Level3_Shift: // AltGr on at least some machines case XK_Meta_R: case XK_Alt_R: return GLFW_KEY_RIGHT_ALT; case XK_Super_L: return GLFW_KEY_LEFT_SUPER; case XK_Super_R: return GLFW_KEY_RIGHT_SUPER; case XK_Menu: return GLFW_KEY_MENU; case XK_Num_Lock: return GLFW_KEY_NUM_LOCK; case XK_Caps_Lock: return GLFW_KEY_CAPS_LOCK; case XK_Print: return GLFW_KEY_PRINT_SCREEN; case XK_Scroll_Lock: return GLFW_KEY_SCROLL_LOCK; case XK_Pause: return GLFW_KEY_PAUSE; case XK_Delete: return GLFW_KEY_DELETE; case XK_BackSpace: return GLFW_KEY_BACKSPACE; case XK_Return: return GLFW_KEY_ENTER; case XK_Home: return GLFW_KEY_HOME; case XK_End: return GLFW_KEY_END; case XK_Page_Up: return GLFW_KEY_PAGE_UP; case XK_Page_Down: return GLFW_KEY_PAGE_DOWN; case XK_Insert: return GLFW_KEY_INSERT; case XK_Left: return GLFW_KEY_LEFT; case XK_Right: return GLFW_KEY_RIGHT; case XK_Down: return GLFW_KEY_DOWN; case XK_Up: return GLFW_KEY_UP; case XK_F1: return GLFW_KEY_F1; case XK_F2: return GLFW_KEY_F2; case XK_F3: return GLFW_KEY_F3; case XK_F4: return GLFW_KEY_F4; case XK_F5: return GLFW_KEY_F5; case XK_F6: return GLFW_KEY_F6; case XK_F7: return GLFW_KEY_F7; case XK_F8: return GLFW_KEY_F8; case XK_F9: return GLFW_KEY_F9; case XK_F10: return GLFW_KEY_F10; case XK_F11: return GLFW_KEY_F11; case XK_F12: return GLFW_KEY_F12; case XK_F13: return GLFW_KEY_F13; case XK_F14: return GLFW_KEY_F14; case XK_F15: return GLFW_KEY_F15; case XK_F16: return GLFW_KEY_F16; case XK_F17: return GLFW_KEY_F17; case XK_F18: return GLFW_KEY_F18; case XK_F19: return GLFW_KEY_F19; case XK_F20: return GLFW_KEY_F20; case XK_F21: return GLFW_KEY_F21; case XK_F22: return GLFW_KEY_F22; case XK_F23: return GLFW_KEY_F23; case XK_F24: return GLFW_KEY_F24; case XK_F25: return GLFW_KEY_F25; // Numeric keypad case XK_KP_Divide: return GLFW_KEY_KP_DIVIDE; case XK_KP_Multiply: return GLFW_KEY_KP_MULTIPLY; case XK_KP_Subtract: return GLFW_KEY_KP_SUBTRACT; case XK_KP_Add: return GLFW_KEY_KP_ADD; // These should have been detected in secondary keysym test above! case XK_KP_Insert: return GLFW_KEY_KP_0; case XK_KP_End: return GLFW_KEY_KP_1; case XK_KP_Down: return GLFW_KEY_KP_2; case XK_KP_Page_Down: return GLFW_KEY_KP_3; case XK_KP_Left: return GLFW_KEY_KP_4; case XK_KP_Right: return GLFW_KEY_KP_6; case XK_KP_Home: return GLFW_KEY_KP_7; case XK_KP_Up: return GLFW_KEY_KP_8; case XK_KP_Page_Up: return GLFW_KEY_KP_9; case XK_KP_Delete: return GLFW_KEY_KP_DECIMAL; case XK_KP_Equal: return GLFW_KEY_KP_EQUAL; case XK_KP_Enter: return GLFW_KEY_KP_ENTER; // Last resort: Check for printable keys (should not happen if the XKB // extension is available). This will give a layout dependent mapping // (which is wrong, and we may miss some keys, especially on non-US // keyboards), but it's better than nothing... case XK_a: return GLFW_KEY_A; case XK_b: return GLFW_KEY_B; case XK_c: return GLFW_KEY_C; case XK_d: return GLFW_KEY_D; case XK_e: return GLFW_KEY_E; case XK_f: return GLFW_KEY_F; case XK_g: return GLFW_KEY_G; case XK_h: return GLFW_KEY_H; case XK_i: return GLFW_KEY_I; case XK_j: return GLFW_KEY_J; case XK_k: return GLFW_KEY_K; case XK_l: return GLFW_KEY_L; case XK_m: return GLFW_KEY_M; case XK_n: return GLFW_KEY_N; case XK_o: return GLFW_KEY_O; case XK_p: return GLFW_KEY_P; case XK_q: return GLFW_KEY_Q; case XK_r: return GLFW_KEY_R; case XK_s: return GLFW_KEY_S; case XK_t: return GLFW_KEY_T; case XK_u: return GLFW_KEY_U; case XK_v: return GLFW_KEY_V; case XK_w: return GLFW_KEY_W; case XK_x: return GLFW_KEY_X; case XK_y: return GLFW_KEY_Y; case XK_z: return GLFW_KEY_Z; case XK_1: return GLFW_KEY_1; case XK_2: return GLFW_KEY_2; case XK_3: return GLFW_KEY_3; case XK_4: return GLFW_KEY_4; case XK_5: return GLFW_KEY_5; case XK_6: return GLFW_KEY_6; case XK_7: return GLFW_KEY_7; case XK_8: return GLFW_KEY_8; case XK_9: return GLFW_KEY_9; case XK_0: return GLFW_KEY_0; case XK_space: return GLFW_KEY_SPACE; case XK_minus: return GLFW_KEY_MINUS; case XK_equal: return GLFW_KEY_EQUAL; case XK_bracketleft: return GLFW_KEY_LEFT_BRACKET; case XK_bracketright: return GLFW_KEY_RIGHT_BRACKET; case XK_backslash: return GLFW_KEY_BACKSLASH; case XK_semicolon: return GLFW_KEY_SEMICOLON; case XK_apostrophe: return GLFW_KEY_APOSTROPHE; case XK_grave: return GLFW_KEY_GRAVE_ACCENT; case XK_comma: return GLFW_KEY_COMMA; case XK_period: return GLFW_KEY_PERIOD; case XK_slash: return GLFW_KEY_SLASH; case XK_less: return GLFW_KEY_WORLD_1; // At least in some layouts... default: break; } // No matching translation was found return GLFW_KEY_UNKNOWN; } // Create key code translation tables // static void createKeyTables(void) { int scancode, scancodeMin, scancodeMax; memset(_glfw.x11.keycodes, -1, sizeof(_glfw.x11.keycodes)); memset(_glfw.x11.scancodes, -1, sizeof(_glfw.x11.scancodes)); if (_glfw.x11.xkb.available) { // Use XKB to determine physical key locations independently of the // current keyboard layout XkbDescPtr desc = XkbGetMap(_glfw.x11.display, 0, XkbUseCoreKbd); XkbGetNames(_glfw.x11.display, XkbKeyNamesMask | XkbKeyAliasesMask, desc); scancodeMin = desc->min_key_code; scancodeMax = desc->max_key_code; const struct { int key; char* name; } keymap[] = { { GLFW_KEY_GRAVE_ACCENT, "TLDE" }, { GLFW_KEY_1, "AE01" }, { GLFW_KEY_2, "AE02" }, { GLFW_KEY_3, "AE03" }, { GLFW_KEY_4, "AE04" }, { GLFW_KEY_5, "AE05" }, { GLFW_KEY_6, "AE06" }, { GLFW_KEY_7, "AE07" }, { GLFW_KEY_8, "AE08" }, { GLFW_KEY_9, "AE09" }, { GLFW_KEY_0, "AE10" }, { GLFW_KEY_MINUS, "AE11" }, { GLFW_KEY_EQUAL, "AE12" }, { GLFW_KEY_Q, "AD01" }, { GLFW_KEY_W, "AD02" }, { GLFW_KEY_E, "AD03" }, { GLFW_KEY_R, "AD04" }, { GLFW_KEY_T, "AD05" }, { GLFW_KEY_Y, "AD06" }, { GLFW_KEY_U, "AD07" }, { GLFW_KEY_I, "AD08" }, { GLFW_KEY_O, "AD09" }, { GLFW_KEY_P, "AD10" }, { GLFW_KEY_LEFT_BRACKET, "AD11" }, { GLFW_KEY_RIGHT_BRACKET, "AD12" }, { GLFW_KEY_A, "AC01" }, { GLFW_KEY_S, "AC02" }, { GLFW_KEY_D, "AC03" }, { GLFW_KEY_F, "AC04" }, { GLFW_KEY_G, "AC05" }, { GLFW_KEY_H, "AC06" }, { GLFW_KEY_J, "AC07" }, { GLFW_KEY_K, "AC08" }, { GLFW_KEY_L, "AC09" }, { GLFW_KEY_SEMICOLON, "AC10" }, { GLFW_KEY_APOSTROPHE, "AC11" }, { GLFW_KEY_Z, "AB01" }, { GLFW_KEY_X, "AB02" }, { GLFW_KEY_C, "AB03" }, { GLFW_KEY_V, "AB04" }, { GLFW_KEY_B, "AB05" }, { GLFW_KEY_N, "AB06" }, { GLFW_KEY_M, "AB07" }, { GLFW_KEY_COMMA, "AB08" }, { GLFW_KEY_PERIOD, "AB09" }, { GLFW_KEY_SLASH, "AB10" }, { GLFW_KEY_BACKSLASH, "BKSL" }, { GLFW_KEY_WORLD_1, "LSGT" }, { GLFW_KEY_SPACE, "SPCE" }, { GLFW_KEY_ESCAPE, "ESC" }, { GLFW_KEY_ENTER, "RTRN" }, { GLFW_KEY_TAB, "TAB" }, { GLFW_KEY_BACKSPACE, "BKSP" }, { GLFW_KEY_INSERT, "INS" }, { GLFW_KEY_DELETE, "DELE" }, { GLFW_KEY_RIGHT, "RGHT" }, { GLFW_KEY_LEFT, "LEFT" }, { GLFW_KEY_DOWN, "DOWN" }, { GLFW_KEY_UP, "UP" }, { GLFW_KEY_PAGE_UP, "PGUP" }, { GLFW_KEY_PAGE_DOWN, "PGDN" }, { GLFW_KEY_HOME, "HOME" }, { GLFW_KEY_END, "END" }, { GLFW_KEY_CAPS_LOCK, "CAPS" }, { GLFW_KEY_SCROLL_LOCK, "SCLK" }, { GLFW_KEY_NUM_LOCK, "NMLK" }, { GLFW_KEY_PRINT_SCREEN, "PRSC" }, { GLFW_KEY_PAUSE, "PAUS" }, { GLFW_KEY_F1, "FK01" }, { GLFW_KEY_F2, "FK02" }, { GLFW_KEY_F3, "FK03" }, { GLFW_KEY_F4, "FK04" }, { GLFW_KEY_F5, "FK05" }, { GLFW_KEY_F6, "FK06" }, { GLFW_KEY_F7, "FK07" }, { GLFW_KEY_F8, "FK08" }, { GLFW_KEY_F9, "FK09" }, { GLFW_KEY_F10, "FK10" }, { GLFW_KEY_F11, "FK11" }, { GLFW_KEY_F12, "FK12" }, { GLFW_KEY_F13, "FK13" }, { GLFW_KEY_F14, "FK14" }, { GLFW_KEY_F15, "FK15" }, { GLFW_KEY_F16, "FK16" }, { GLFW_KEY_F17, "FK17" }, { GLFW_KEY_F18, "FK18" }, { GLFW_KEY_F19, "FK19" }, { GLFW_KEY_F20, "FK20" }, { GLFW_KEY_F21, "FK21" }, { GLFW_KEY_F22, "FK22" }, { GLFW_KEY_F23, "FK23" }, { GLFW_KEY_F24, "FK24" }, { GLFW_KEY_F25, "FK25" }, { GLFW_KEY_KP_0, "KP0" }, { GLFW_KEY_KP_1, "KP1" }, { GLFW_KEY_KP_2, "KP2" }, { GLFW_KEY_KP_3, "KP3" }, { GLFW_KEY_KP_4, "KP4" }, { GLFW_KEY_KP_5, "KP5" }, { GLFW_KEY_KP_6, "KP6" }, { GLFW_KEY_KP_7, "KP7" }, { GLFW_KEY_KP_8, "KP8" }, { GLFW_KEY_KP_9, "KP9" }, { GLFW_KEY_KP_DECIMAL, "KPDL" }, { GLFW_KEY_KP_DIVIDE, "KPDV" }, { GLFW_KEY_KP_MULTIPLY, "KPMU" }, { GLFW_KEY_KP_SUBTRACT, "KPSU" }, { GLFW_KEY_KP_ADD, "KPAD" }, { GLFW_KEY_KP_ENTER, "KPEN" }, { GLFW_KEY_KP_EQUAL, "KPEQ" }, { GLFW_KEY_LEFT_SHIFT, "LFSH" }, { GLFW_KEY_LEFT_CONTROL, "LCTL" }, { GLFW_KEY_LEFT_ALT, "LALT" }, { GLFW_KEY_LEFT_SUPER, "LWIN" }, { GLFW_KEY_RIGHT_SHIFT, "RTSH" }, { GLFW_KEY_RIGHT_CONTROL, "RCTL" }, { GLFW_KEY_RIGHT_ALT, "RALT" }, { GLFW_KEY_RIGHT_ALT, "LVL3" }, { GLFW_KEY_RIGHT_ALT, "MDSW" }, { GLFW_KEY_RIGHT_SUPER, "RWIN" }, { GLFW_KEY_MENU, "MENU" } }; // Find the X11 key code -> GLFW key code mapping for (scancode = scancodeMin; scancode <= scancodeMax; scancode++) { int key = GLFW_KEY_UNKNOWN; // Map the key name to a GLFW key code. Note: We use the US // keyboard layout. Because function keys aren't mapped correctly // when using traditional KeySym translations, they are mapped // here instead. for (int i = 0; i < sizeof(keymap) / sizeof(keymap[0]); i++) { if (strncmp(desc->names->keys[scancode].name, keymap[i].name, XkbKeyNameLength) == 0) { key = keymap[i].key; break; } } // Fall back to key aliases in case the key name did not match for (int i = 0; i < desc->names->num_key_aliases; i++) { if (key != GLFW_KEY_UNKNOWN) break; if (strncmp(desc->names->key_aliases[i].real, desc->names->keys[scancode].name, XkbKeyNameLength) != 0) { continue; } for (int j = 0; j < sizeof(keymap) / sizeof(keymap[0]); j++) { if (strncmp(desc->names->key_aliases[i].alias, keymap[j].name, XkbKeyNameLength) == 0) { key = keymap[j].key; break; } } } _glfw.x11.keycodes[scancode] = key; } XkbFreeNames(desc, XkbKeyNamesMask, True); XkbFreeKeyboard(desc, 0, True); } else XDisplayKeycodes(_glfw.x11.display, &scancodeMin, &scancodeMax); int width; KeySym* keysyms = XGetKeyboardMapping(_glfw.x11.display, scancodeMin, scancodeMax - scancodeMin + 1, &width); for (scancode = scancodeMin; scancode <= scancodeMax; scancode++) { // Translate the un-translated key codes using traditional X11 KeySym // lookups if (_glfw.x11.keycodes[scancode] < 0) { const size_t base = (scancode - scancodeMin) * width; _glfw.x11.keycodes[scancode] = translateKeySyms(&keysyms[base], width); } // Store the reverse translation for faster key name lookup if (_glfw.x11.keycodes[scancode] > 0) _glfw.x11.scancodes[_glfw.x11.keycodes[scancode]] = scancode; } XFree(keysyms); } // Check whether the IM has a usable style // static GLFWbool hasUsableInputMethodStyle(void) { GLFWbool found = GLFW_FALSE; XIMStyles* styles = NULL; if (XGetIMValues(_glfw.x11.im, XNQueryInputStyle, &styles, NULL) != NULL) return GLFW_FALSE; for (unsigned int i = 0; i < styles->count_styles; i++) { if (styles->supported_styles[i] == (XIMPreeditNothing | XIMStatusNothing)) { found = GLFW_TRUE; break; } } XFree(styles); return found; } // Check whether the specified atom is supported // static Atom getAtomIfSupported(Atom* supportedAtoms, unsigned long atomCount, const char* atomName) { const Atom atom = XInternAtom(_glfw.x11.display, atomName, False); for (unsigned long i = 0; i < atomCount; i++) { if (supportedAtoms[i] == atom) return atom; } return None; } // Check whether the running window manager is EWMH-compliant // static void detectEWMH(void) { // First we read the _NET_SUPPORTING_WM_CHECK property on the root window Window* windowFromRoot = NULL; if (!_glfwGetWindowPropertyX11(_glfw.x11.root, _glfw.x11.NET_SUPPORTING_WM_CHECK, XA_WINDOW, (unsigned char**) &windowFromRoot)) { return; } _glfwGrabErrorHandlerX11(); // If it exists, it should be the XID of a top-level window // Then we look for the same property on that window Window* windowFromChild = NULL; if (!_glfwGetWindowPropertyX11(*windowFromRoot, _glfw.x11.NET_SUPPORTING_WM_CHECK, XA_WINDOW, (unsigned char**) &windowFromChild)) { XFree(windowFromRoot); return; } _glfwReleaseErrorHandlerX11(); // If the property exists, it should contain the XID of the window if (*windowFromRoot != *windowFromChild) { XFree(windowFromRoot); XFree(windowFromChild); return; } XFree(windowFromRoot); XFree(windowFromChild); // We are now fairly sure that an EWMH-compliant WM is currently running // We can now start querying the WM about what features it supports by // looking in the _NET_SUPPORTED property on the root window // It should contain a list of supported EWMH protocol and state atoms Atom* supportedAtoms = NULL; const unsigned long atomCount = _glfwGetWindowPropertyX11(_glfw.x11.root, _glfw.x11.NET_SUPPORTED, XA_ATOM, (unsigned char**) &supportedAtoms); // See which of the atoms we support that are supported by the WM _glfw.x11.NET_WM_STATE = getAtomIfSupported(supportedAtoms, atomCount, "_NET_WM_STATE"); _glfw.x11.NET_WM_STATE_ABOVE = getAtomIfSupported(supportedAtoms, atomCount, "_NET_WM_STATE_ABOVE"); _glfw.x11.NET_WM_STATE_FULLSCREEN = getAtomIfSupported(supportedAtoms, atomCount, "_NET_WM_STATE_FULLSCREEN"); _glfw.x11.NET_WM_STATE_MAXIMIZED_VERT = getAtomIfSupported(supportedAtoms, atomCount, "_NET_WM_STATE_MAXIMIZED_VERT"); _glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ = getAtomIfSupported(supportedAtoms, atomCount, "_NET_WM_STATE_MAXIMIZED_HORZ"); _glfw.x11.NET_WM_STATE_DEMANDS_ATTENTION = getAtomIfSupported(supportedAtoms, atomCount, "_NET_WM_STATE_DEMANDS_ATTENTION"); _glfw.x11.NET_WM_FULLSCREEN_MONITORS = getAtomIfSupported(supportedAtoms, atomCount, "_NET_WM_FULLSCREEN_MONITORS"); _glfw.x11.NET_WM_WINDOW_TYPE = getAtomIfSupported(supportedAtoms, atomCount, "_NET_WM_WINDOW_TYPE"); _glfw.x11.NET_WM_WINDOW_TYPE_NORMAL = getAtomIfSupported(supportedAtoms, atomCount, "_NET_WM_WINDOW_TYPE_NORMAL"); _glfw.x11.NET_WORKAREA = getAtomIfSupported(supportedAtoms, atomCount, "_NET_WORKAREA"); _glfw.x11.NET_CURRENT_DESKTOP = getAtomIfSupported(supportedAtoms, atomCount, "_NET_CURRENT_DESKTOP"); _glfw.x11.NET_ACTIVE_WINDOW = getAtomIfSupported(supportedAtoms, atomCount, "_NET_ACTIVE_WINDOW"); _glfw.x11.NET_FRAME_EXTENTS = getAtomIfSupported(supportedAtoms, atomCount, "_NET_FRAME_EXTENTS"); _glfw.x11.NET_REQUEST_FRAME_EXTENTS = getAtomIfSupported(supportedAtoms, atomCount, "_NET_REQUEST_FRAME_EXTENTS"); if (supportedAtoms) XFree(supportedAtoms); } // Look for and initialize supported X11 extensions // static GLFWbool initExtensions(void) { #if defined(__OpenBSD__) || defined(__NetBSD__) _glfw.x11.vidmode.handle = _glfw_dlopen("libXxf86vm.so"); #else _glfw.x11.vidmode.handle = _glfw_dlopen("libXxf86vm.so.1"); #endif if (_glfw.x11.vidmode.handle) { _glfw.x11.vidmode.QueryExtension = (PFN_XF86VidModeQueryExtension) _glfw_dlsym(_glfw.x11.vidmode.handle, "XF86VidModeQueryExtension"); _glfw.x11.vidmode.GetGammaRamp = (PFN_XF86VidModeGetGammaRamp) _glfw_dlsym(_glfw.x11.vidmode.handle, "XF86VidModeGetGammaRamp"); _glfw.x11.vidmode.SetGammaRamp = (PFN_XF86VidModeSetGammaRamp) _glfw_dlsym(_glfw.x11.vidmode.handle, "XF86VidModeSetGammaRamp"); _glfw.x11.vidmode.GetGammaRampSize = (PFN_XF86VidModeGetGammaRampSize) _glfw_dlsym(_glfw.x11.vidmode.handle, "XF86VidModeGetGammaRampSize"); _glfw.x11.vidmode.available = XF86VidModeQueryExtension(_glfw.x11.display, &_glfw.x11.vidmode.eventBase, &_glfw.x11.vidmode.errorBase); } #if defined(__CYGWIN__) _glfw.x11.xi.handle = _glfw_dlopen("libXi-6.so"); #elif defined(__OpenBSD__) || defined(__NetBSD__) _glfw.x11.xi.handle = _glfw_dlopen("libXi.so"); #else _glfw.x11.xi.handle = _glfw_dlopen("libXi.so.6"); #endif if (_glfw.x11.xi.handle) { _glfw.x11.xi.QueryVersion = (PFN_XIQueryVersion) _glfw_dlsym(_glfw.x11.xi.handle, "XIQueryVersion"); _glfw.x11.xi.SelectEvents = (PFN_XISelectEvents) _glfw_dlsym(_glfw.x11.xi.handle, "XISelectEvents"); if (XQueryExtension(_glfw.x11.display, "XInputExtension", &_glfw.x11.xi.majorOpcode, &_glfw.x11.xi.eventBase, &_glfw.x11.xi.errorBase)) { _glfw.x11.xi.major = 2; _glfw.x11.xi.minor = 0; if (XIQueryVersion(_glfw.x11.display, &_glfw.x11.xi.major, &_glfw.x11.xi.minor) == Success) { _glfw.x11.xi.available = GLFW_TRUE; } } } #if defined(__CYGWIN__) _glfw.x11.randr.handle = _glfw_dlopen("libXrandr-2.so"); #elif defined(__OpenBSD__) || defined(__NetBSD__) _glfw.x11.randr.handle = _glfw_dlopen("libXrandr.so"); #else _glfw.x11.randr.handle = _glfw_dlopen("libXrandr.so.2"); #endif if (_glfw.x11.randr.handle) { _glfw.x11.randr.AllocGamma = (PFN_XRRAllocGamma) _glfw_dlsym(_glfw.x11.randr.handle, "XRRAllocGamma"); _glfw.x11.randr.FreeGamma = (PFN_XRRFreeGamma) _glfw_dlsym(_glfw.x11.randr.handle, "XRRFreeGamma"); _glfw.x11.randr.FreeCrtcInfo = (PFN_XRRFreeCrtcInfo) _glfw_dlsym(_glfw.x11.randr.handle, "XRRFreeCrtcInfo"); _glfw.x11.randr.FreeGamma = (PFN_XRRFreeGamma) _glfw_dlsym(_glfw.x11.randr.handle, "XRRFreeGamma"); _glfw.x11.randr.FreeOutputInfo = (PFN_XRRFreeOutputInfo) _glfw_dlsym(_glfw.x11.randr.handle, "XRRFreeOutputInfo"); _glfw.x11.randr.FreeScreenResources = (PFN_XRRFreeScreenResources) _glfw_dlsym(_glfw.x11.randr.handle, "XRRFreeScreenResources"); _glfw.x11.randr.GetCrtcGamma = (PFN_XRRGetCrtcGamma) _glfw_dlsym(_glfw.x11.randr.handle, "XRRGetCrtcGamma"); _glfw.x11.randr.GetCrtcGammaSize = (PFN_XRRGetCrtcGammaSize) _glfw_dlsym(_glfw.x11.randr.handle, "XRRGetCrtcGammaSize"); _glfw.x11.randr.GetCrtcInfo = (PFN_XRRGetCrtcInfo) _glfw_dlsym(_glfw.x11.randr.handle, "XRRGetCrtcInfo"); _glfw.x11.randr.GetOutputInfo = (PFN_XRRGetOutputInfo) _glfw_dlsym(_glfw.x11.randr.handle, "XRRGetOutputInfo"); _glfw.x11.randr.GetOutputPrimary = (PFN_XRRGetOutputPrimary) _glfw_dlsym(_glfw.x11.randr.handle, "XRRGetOutputPrimary"); _glfw.x11.randr.GetScreenResourcesCurrent = (PFN_XRRGetScreenResourcesCurrent) _glfw_dlsym(_glfw.x11.randr.handle, "XRRGetScreenResourcesCurrent"); _glfw.x11.randr.QueryExtension = (PFN_XRRQueryExtension) _glfw_dlsym(_glfw.x11.randr.handle, "XRRQueryExtension"); _glfw.x11.randr.QueryVersion = (PFN_XRRQueryVersion) _glfw_dlsym(_glfw.x11.randr.handle, "XRRQueryVersion"); _glfw.x11.randr.SelectInput = (PFN_XRRSelectInput) _glfw_dlsym(_glfw.x11.randr.handle, "XRRSelectInput"); _glfw.x11.randr.SetCrtcConfig = (PFN_XRRSetCrtcConfig) _glfw_dlsym(_glfw.x11.randr.handle, "XRRSetCrtcConfig"); _glfw.x11.randr.SetCrtcGamma = (PFN_XRRSetCrtcGamma) _glfw_dlsym(_glfw.x11.randr.handle, "XRRSetCrtcGamma"); _glfw.x11.randr.UpdateConfiguration = (PFN_XRRUpdateConfiguration) _glfw_dlsym(_glfw.x11.randr.handle, "XRRUpdateConfiguration"); if (XRRQueryExtension(_glfw.x11.display, &_glfw.x11.randr.eventBase, &_glfw.x11.randr.errorBase)) { if (XRRQueryVersion(_glfw.x11.display, &_glfw.x11.randr.major, &_glfw.x11.randr.minor)) { // The GLFW RandR path requires at least version 1.3 if (_glfw.x11.randr.major > 1 || _glfw.x11.randr.minor >= 3) _glfw.x11.randr.available = GLFW_TRUE; } else { _glfwInputError(GLFW_PLATFORM_ERROR, "X11: Failed to query RandR version"); } } } if (_glfw.x11.randr.available) { XRRScreenResources* sr = XRRGetScreenResourcesCurrent(_glfw.x11.display, _glfw.x11.root); if (!sr->ncrtc || !XRRGetCrtcGammaSize(_glfw.x11.display, sr->crtcs[0])) { // This is likely an older Nvidia driver with broken gamma support // Flag it as useless and fall back to xf86vm gamma, if available _glfw.x11.randr.gammaBroken = GLFW_TRUE; } if (!sr->ncrtc) { // A system without CRTCs is likely a system with broken RandR // Disable the RandR monitor path and fall back to core functions _glfw.x11.randr.monitorBroken = GLFW_TRUE; } XRRFreeScreenResources(sr); } if (_glfw.x11.randr.available && !_glfw.x11.randr.monitorBroken) { XRRSelectInput(_glfw.x11.display, _glfw.x11.root, RROutputChangeNotifyMask); } #if defined(__CYGWIN__) _glfw.x11.xcursor.handle = _glfw_dlopen("libXcursor-1.so"); #elif defined(__OpenBSD__) || defined(__NetBSD__) _glfw.x11.xcursor.handle = _glfw_dlopen("libXcursor.so"); #else _glfw.x11.xcursor.handle = _glfw_dlopen("libXcursor.so.1"); #endif if (_glfw.x11.xcursor.handle) { _glfw.x11.xcursor.ImageCreate = (PFN_XcursorImageCreate) _glfw_dlsym(_glfw.x11.xcursor.handle, "XcursorImageCreate"); _glfw.x11.xcursor.ImageDestroy = (PFN_XcursorImageDestroy) _glfw_dlsym(_glfw.x11.xcursor.handle, "XcursorImageDestroy"); _glfw.x11.xcursor.ImageLoadCursor = (PFN_XcursorImageLoadCursor) _glfw_dlsym(_glfw.x11.xcursor.handle, "XcursorImageLoadCursor"); } #if defined(__CYGWIN__) _glfw.x11.xinerama.handle = _glfw_dlopen("libXinerama-1.so"); #elif defined(__OpenBSD__) || defined(__NetBSD__) _glfw.x11.xinerama.handle = _glfw_dlopen("libXinerama.so"); #else _glfw.x11.xinerama.handle = _glfw_dlopen("libXinerama.so.1"); #endif if (_glfw.x11.xinerama.handle) { _glfw.x11.xinerama.IsActive = (PFN_XineramaIsActive) _glfw_dlsym(_glfw.x11.xinerama.handle, "XineramaIsActive"); _glfw.x11.xinerama.QueryExtension = (PFN_XineramaQueryExtension) _glfw_dlsym(_glfw.x11.xinerama.handle, "XineramaQueryExtension"); _glfw.x11.xinerama.QueryScreens = (PFN_XineramaQueryScreens) _glfw_dlsym(_glfw.x11.xinerama.handle, "XineramaQueryScreens"); if (XineramaQueryExtension(_glfw.x11.display, &_glfw.x11.xinerama.major, &_glfw.x11.xinerama.minor)) { if (XineramaIsActive(_glfw.x11.display)) _glfw.x11.xinerama.available = GLFW_TRUE; } } _glfw.x11.xkb.major = 1; _glfw.x11.xkb.minor = 0; _glfw.x11.xkb.available = XkbQueryExtension(_glfw.x11.display, &_glfw.x11.xkb.majorOpcode, &_glfw.x11.xkb.eventBase, &_glfw.x11.xkb.errorBase, &_glfw.x11.xkb.major, &_glfw.x11.xkb.minor); if (_glfw.x11.xkb.available) { Bool supported; if (XkbSetDetectableAutoRepeat(_glfw.x11.display, True, &supported)) { if (supported) _glfw.x11.xkb.detectable = GLFW_TRUE; } XkbStateRec state; if (XkbGetState(_glfw.x11.display, XkbUseCoreKbd, &state) == Success) _glfw.x11.xkb.group = (unsigned int)state.group; XkbSelectEventDetails(_glfw.x11.display, XkbUseCoreKbd, XkbStateNotify, XkbGroupStateMask, XkbGroupStateMask); } #if defined(__CYGWIN__) _glfw.x11.x11xcb.handle = _glfw_dlopen("libX11-xcb-1.so"); #elif defined(__OpenBSD__) || defined(__NetBSD__) _glfw.x11.x11xcb.handle = _glfw_dlopen("libX11-xcb.so"); #else _glfw.x11.x11xcb.handle = _glfw_dlopen("libX11-xcb.so.1"); #endif if (_glfw.x11.x11xcb.handle) { _glfw.x11.x11xcb.GetXCBConnection = (PFN_XGetXCBConnection) _glfw_dlsym(_glfw.x11.x11xcb.handle, "XGetXCBConnection"); } #if defined(__CYGWIN__) _glfw.x11.xrender.handle = _glfw_dlopen("libXrender-1.so"); #elif defined(__OpenBSD__) || defined(__NetBSD__) _glfw.x11.xrender.handle = _glfw_dlopen("libXrender.so"); #else _glfw.x11.xrender.handle = _glfw_dlopen("libXrender.so.1"); #endif if (_glfw.x11.xrender.handle) { _glfw.x11.xrender.QueryExtension = (PFN_XRenderQueryExtension) _glfw_dlsym(_glfw.x11.xrender.handle, "XRenderQueryExtension"); _glfw.x11.xrender.QueryVersion = (PFN_XRenderQueryVersion) _glfw_dlsym(_glfw.x11.xrender.handle, "XRenderQueryVersion"); _glfw.x11.xrender.FindVisualFormat = (PFN_XRenderFindVisualFormat) _glfw_dlsym(_glfw.x11.xrender.handle, "XRenderFindVisualFormat"); if (XRenderQueryExtension(_glfw.x11.display, &_glfw.x11.xrender.errorBase, &_glfw.x11.xrender.eventBase)) { if (XRenderQueryVersion(_glfw.x11.display, &_glfw.x11.xrender.major, &_glfw.x11.xrender.minor)) { _glfw.x11.xrender.available = GLFW_TRUE; } } } // Update the key code LUT // FIXME: We should listen to XkbMapNotify events to track changes to // the keyboard mapping. createKeyTables(); // String format atoms _glfw.x11.NULL_ = XInternAtom(_glfw.x11.display, "NULL", False); _glfw.x11.UTF8_STRING = XInternAtom(_glfw.x11.display, "UTF8_STRING", False); _glfw.x11.ATOM_PAIR = XInternAtom(_glfw.x11.display, "ATOM_PAIR", False); // Custom selection property atom _glfw.x11.GLFW_SELECTION = XInternAtom(_glfw.x11.display, "GLFW_SELECTION", False); // ICCCM standard clipboard atoms _glfw.x11.TARGETS = XInternAtom(_glfw.x11.display, "TARGETS", False); _glfw.x11.MULTIPLE = XInternAtom(_glfw.x11.display, "MULTIPLE", False); _glfw.x11.PRIMARY = XInternAtom(_glfw.x11.display, "PRIMARY", False); _glfw.x11.INCR = XInternAtom(_glfw.x11.display, "INCR", False); _glfw.x11.CLIPBOARD = XInternAtom(_glfw.x11.display, "CLIPBOARD", False); // Clipboard manager atoms _glfw.x11.CLIPBOARD_MANAGER = XInternAtom(_glfw.x11.display, "CLIPBOARD_MANAGER", False); _glfw.x11.SAVE_TARGETS = XInternAtom(_glfw.x11.display, "SAVE_TARGETS", False); // Xdnd (drag and drop) atoms _glfw.x11.XdndAware = XInternAtom(_glfw.x11.display, "XdndAware", False); _glfw.x11.XdndEnter = XInternAtom(_glfw.x11.display, "XdndEnter", False); _glfw.x11.XdndPosition = XInternAtom(_glfw.x11.display, "XdndPosition", False); _glfw.x11.XdndStatus = XInternAtom(_glfw.x11.display, "XdndStatus", False); _glfw.x11.XdndActionCopy = XInternAtom(_glfw.x11.display, "XdndActionCopy", False); _glfw.x11.XdndDrop = XInternAtom(_glfw.x11.display, "XdndDrop", False); _glfw.x11.XdndFinished = XInternAtom(_glfw.x11.display, "XdndFinished", False); _glfw.x11.XdndSelection = XInternAtom(_glfw.x11.display, "XdndSelection", False); _glfw.x11.XdndTypeList = XInternAtom(_glfw.x11.display, "XdndTypeList", False); _glfw.x11.text_uri_list = XInternAtom(_glfw.x11.display, "text/uri-list", False); // ICCCM, EWMH and Motif window property atoms // These can be set safely even without WM support // The EWMH atoms that require WM support are handled in detectEWMH _glfw.x11.WM_PROTOCOLS = XInternAtom(_glfw.x11.display, "WM_PROTOCOLS", False); _glfw.x11.WM_STATE = XInternAtom(_glfw.x11.display, "WM_STATE", False); _glfw.x11.WM_DELETE_WINDOW = XInternAtom(_glfw.x11.display, "WM_DELETE_WINDOW", False); _glfw.x11.NET_SUPPORTED = XInternAtom(_glfw.x11.display, "_NET_SUPPORTED", False); _glfw.x11.NET_SUPPORTING_WM_CHECK = XInternAtom(_glfw.x11.display, "_NET_SUPPORTING_WM_CHECK", False); _glfw.x11.NET_WM_ICON = XInternAtom(_glfw.x11.display, "_NET_WM_ICON", False); _glfw.x11.NET_WM_PING = XInternAtom(_glfw.x11.display, "_NET_WM_PING", False); _glfw.x11.NET_WM_PID = XInternAtom(_glfw.x11.display, "_NET_WM_PID", False); _glfw.x11.NET_WM_NAME = XInternAtom(_glfw.x11.display, "_NET_WM_NAME", False); _glfw.x11.NET_WM_ICON_NAME = XInternAtom(_glfw.x11.display, "_NET_WM_ICON_NAME", False); _glfw.x11.NET_WM_BYPASS_COMPOSITOR = XInternAtom(_glfw.x11.display, "_NET_WM_BYPASS_COMPOSITOR", False); _glfw.x11.NET_WM_WINDOW_OPACITY = XInternAtom(_glfw.x11.display, "_NET_WM_WINDOW_OPACITY", False); _glfw.x11.MOTIF_WM_HINTS = XInternAtom(_glfw.x11.display, "_MOTIF_WM_HINTS", False); // The compositing manager selection name contains the screen number { char name[32]; snprintf(name, sizeof(name), "_NET_WM_CM_S%u", _glfw.x11.screen); _glfw.x11.NET_WM_CM_Sx = XInternAtom(_glfw.x11.display, name, False); } // Detect whether an EWMH-conformant window manager is running detectEWMH(); return GLFW_TRUE; } // Retrieve system content scale via folklore heuristics // static void getSystemContentScale(float* xscale, float* yscale) { // Start by assuming the default X11 DPI // NOTE: Some desktop environments (KDE) may remove the Xft.dpi field when it // would be set to 96, so assume that is the case if we cannot find it float xdpi = 96.f, ydpi = 96.f; // NOTE: Basing the scale on Xft.dpi where available should provide the most // consistent user experience (matches Qt, Gtk, etc), although not // always the most accurate one char* rms = XResourceManagerString(_glfw.x11.display); if (rms) { XrmDatabase db = XrmGetStringDatabase(rms); if (db) { XrmValue value; char* type = NULL; if (XrmGetResource(db, "Xft.dpi", "Xft.Dpi", &type, &value)) { if (type && strcmp(type, "String") == 0) xdpi = ydpi = atof(value.addr); } XrmDestroyDatabase(db); } } *xscale = xdpi / 96.f; *yscale = ydpi / 96.f; } // Create a blank cursor for hidden and disabled cursor modes // static Cursor createHiddenCursor(void) { unsigned char pixels[16 * 16 * 4] = { 0 }; GLFWimage image = { 16, 16, pixels }; return _glfwCreateCursorX11(&image, 0, 0); } // Create a helper window for IPC // static Window createHelperWindow(void) { XSetWindowAttributes wa; wa.event_mask = PropertyChangeMask; return XCreateWindow(_glfw.x11.display, _glfw.x11.root, 0, 0, 1, 1, 0, 0, InputOnly, DefaultVisual(_glfw.x11.display, _glfw.x11.screen), CWEventMask, &wa); } // Create the pipe for empty events without assumuing the OS has pipe2(2) // static GLFWbool createEmptyEventPipe(void) { if (pipe(_glfw.x11.emptyEventPipe) != 0) { _glfwInputError(GLFW_PLATFORM_ERROR, "X11: Failed to create empty event pipe: %s", strerror(errno)); return GLFW_FALSE; } for (int i = 0; i < 2; i++) { const int sf = fcntl(_glfw.x11.emptyEventPipe[i], F_GETFL, 0); const int df = fcntl(_glfw.x11.emptyEventPipe[i], F_GETFD, 0); if (sf == -1 || df == -1 || fcntl(_glfw.x11.emptyEventPipe[i], F_SETFL, sf | O_NONBLOCK) == -1 || fcntl(_glfw.x11.emptyEventPipe[i], F_SETFD, df | FD_CLOEXEC) == -1) { _glfwInputError(GLFW_PLATFORM_ERROR, "X11: Failed to set flags for empty event pipe: %s", strerror(errno)); return GLFW_FALSE; } } return GLFW_TRUE; } // X error handler // static int errorHandler(Display *display, XErrorEvent* event) { if (_glfw.x11.display != display) return 0; _glfw.x11.errorCode = event->error_code; return 0; } ////////////////////////////////////////////////////////////////////////// ////// GLFW internal API ////// ////////////////////////////////////////////////////////////////////////// // Sets the X error handler callback // void _glfwGrabErrorHandlerX11(void) { _glfw.x11.errorCode = Success; XSetErrorHandler(errorHandler); } // Clears the X error handler callback // void _glfwReleaseErrorHandlerX11(void) { // Synchronize to make sure all commands are processed XSync(_glfw.x11.display, False); XSetErrorHandler(NULL); } // Reports the specified error, appending information about the last X error // void _glfwInputErrorX11(int error, const char* message) { char buffer[_GLFW_MESSAGE_SIZE]; XGetErrorText(_glfw.x11.display, _glfw.x11.errorCode, buffer, sizeof(buffer)); _glfwInputError(error, "%s: %s", message, buffer); } // Creates a native cursor object from the specified image and hotspot // Cursor _glfwCreateCursorX11(const GLFWimage* image, int xhot, int yhot) { int i; Cursor cursor; if (!_glfw.x11.xcursor.handle) return None; XcursorImage* native = XcursorImageCreate(image->width, image->height); if (native == NULL) return None; native->xhot = xhot; native->yhot = yhot; unsigned char* source = (unsigned char*) image->pixels; XcursorPixel* target = native->pixels; for (i = 0; i < image->width * image->height; i++, target++, source += 4) { unsigned int alpha = source[3]; *target = (alpha << 24) | ((unsigned char) ((source[0] * alpha) / 255) << 16) | ((unsigned char) ((source[1] * alpha) / 255) << 8) | ((unsigned char) ((source[2] * alpha) / 255) << 0); } cursor = XcursorImageLoadCursor(_glfw.x11.display, native); XcursorImageDestroy(native); return cursor; } ////////////////////////////////////////////////////////////////////////// ////// GLFW platform API ////// ////////////////////////////////////////////////////////////////////////// int _glfwPlatformInit(void) { // HACK: If the application has left the locale as "C" then both wide // character text input and explicit UTF-8 input via XIM will break // This sets the CTYPE part of the current locale from the environment // in the hope that it is set to something more sane than "C" if (strcmp(setlocale(LC_CTYPE, NULL), "C") == 0) setlocale(LC_CTYPE, ""); XInitThreads(); XrmInitialize(); _glfw.x11.display = XOpenDisplay(NULL); if (!_glfw.x11.display) { const char* display = getenv("DISPLAY"); if (display) { _glfwInputError(GLFW_PLATFORM_ERROR, "X11: Failed to open display %s", display); } else { _glfwInputError(GLFW_PLATFORM_ERROR, "X11: The DISPLAY environment variable is missing"); } return GLFW_FALSE; } _glfw.x11.screen = DefaultScreen(_glfw.x11.display); _glfw.x11.root = RootWindow(_glfw.x11.display, _glfw.x11.screen); _glfw.x11.context = XUniqueContext(); getSystemContentScale(&_glfw.x11.contentScaleX, &_glfw.x11.contentScaleY); if (!createEmptyEventPipe()) return GLFW_FALSE; if (!initExtensions()) return GLFW_FALSE; _glfw.x11.helperWindowHandle = createHelperWindow(); _glfw.x11.hiddenCursorHandle = createHiddenCursor(); if (XSupportsLocale()) { XSetLocaleModifiers(""); _glfw.x11.im = XOpenIM(_glfw.x11.display, 0, NULL, NULL); if (_glfw.x11.im) { if (!hasUsableInputMethodStyle()) { XCloseIM(_glfw.x11.im); _glfw.x11.im = NULL; } } } #if defined(__linux__) if (!_glfwInitJoysticksLinux()) return GLFW_FALSE; #endif _glfwInitTimerPOSIX(); _glfwPollMonitorsX11(); return GLFW_TRUE; } void _glfwPlatformTerminate(void) { if (_glfw.x11.helperWindowHandle) { if (XGetSelectionOwner(_glfw.x11.display, _glfw.x11.CLIPBOARD) == _glfw.x11.helperWindowHandle) { _glfwPushSelectionToManagerX11(); } XDestroyWindow(_glfw.x11.display, _glfw.x11.helperWindowHandle); _glfw.x11.helperWindowHandle = None; } if (_glfw.x11.hiddenCursorHandle) { XFreeCursor(_glfw.x11.display, _glfw.x11.hiddenCursorHandle); _glfw.x11.hiddenCursorHandle = (Cursor) 0; } free(_glfw.x11.primarySelectionString); free(_glfw.x11.clipboardString); if (_glfw.x11.im) { XCloseIM(_glfw.x11.im); _glfw.x11.im = NULL; } if (_glfw.x11.display) { XCloseDisplay(_glfw.x11.display); _glfw.x11.display = NULL; } if (_glfw.x11.x11xcb.handle) { _glfw_dlclose(_glfw.x11.x11xcb.handle); _glfw.x11.x11xcb.handle = NULL; } if (_glfw.x11.xcursor.handle) { _glfw_dlclose(_glfw.x11.xcursor.handle); _glfw.x11.xcursor.handle = NULL; } if (_glfw.x11.randr.handle) { _glfw_dlclose(_glfw.x11.randr.handle); _glfw.x11.randr.handle = NULL; } if (_glfw.x11.xinerama.handle) { _glfw_dlclose(_glfw.x11.xinerama.handle); _glfw.x11.xinerama.handle = NULL; } if (_glfw.x11.xrender.handle) { _glfw_dlclose(_glfw.x11.xrender.handle); _glfw.x11.xrender.handle = NULL; } if (_glfw.x11.vidmode.handle) { _glfw_dlclose(_glfw.x11.vidmode.handle); _glfw.x11.vidmode.handle = NULL; } if (_glfw.x11.xi.handle) { _glfw_dlclose(_glfw.x11.xi.handle); _glfw.x11.xi.handle = NULL; } // NOTE: These need to be unloaded after XCloseDisplay, as they register // cleanup callbacks that get called by that function _glfwTerminateEGL(); _glfwTerminateGLX(); #if defined(__linux__) _glfwTerminateJoysticksLinux(); #endif if (_glfw.x11.emptyEventPipe[0] || _glfw.x11.emptyEventPipe[1]) { close(_glfw.x11.emptyEventPipe[0]); close(_glfw.x11.emptyEventPipe[1]); } } const char* _glfwPlatformGetVersionString(void) { return _GLFW_VERSION_NUMBER " X11 GLX EGL OSMesa" #if defined(_POSIX_TIMERS) && defined(_POSIX_MONOTONIC_CLOCK) " clock_gettime" #else " gettimeofday" #endif #if defined(__linux__) " evdev" #endif #if defined(_GLFW_BUILD_DLL) " shared" #endif ; } #endif #ifndef HEADER_GUARD_X11_MONITOR_C #define HEADER_GUARD_X11_MONITOR_C //======================================================================== // GLFW 3.3.7 X11 - www.glfw.org //------------------------------------------------------------------------ // Copyright (c) 2002-2006 Marcus Geelnard // Copyright (c) 2006-2019 Camilla Löwy // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would // be appreciated but is not required. // // 2. Altered source versions must be plainly marked as such, and must not // be misrepresented as being the original software. // // 3. This notice may not be removed or altered from any source // distribution. // //======================================================================== // It is fine to use C99 in this file because it will not be built with VS //======================================================================== #include #include #include #include // Check whether the display mode should be included in enumeration // static GLFWbool modeIsGood(const XRRModeInfo* mi) { return (mi->modeFlags & RR_Interlace) == 0; } // Calculates the refresh rate, in Hz, from the specified RandR mode info // static int calculateRefreshRate(const XRRModeInfo* mi) { if (mi->hTotal && mi->vTotal) return (int) round((double) mi->dotClock / ((double) mi->hTotal * (double) mi->vTotal)); else return 0; } // Returns the mode info for a RandR mode XID // static const XRRModeInfo* getModeInfo(const XRRScreenResources* sr, RRMode id) { for (int i = 0; i < sr->nmode; i++) { if (sr->modes[i].id == id) return sr->modes + i; } return NULL; } // Convert RandR mode info to GLFW video mode // static GLFWvidmode vidmodeFromModeInfo(const XRRModeInfo* mi, const XRRCrtcInfo* ci) { GLFWvidmode mode; if (ci->rotation == RR_Rotate_90 || ci->rotation == RR_Rotate_270) { mode.width = mi->height; mode.height = mi->width; } else { mode.width = mi->width; mode.height = mi->height; } mode.refreshRate = calculateRefreshRate(mi); _glfwSplitBPP(DefaultDepth(_glfw.x11.display, _glfw.x11.screen), &mode.redBits, &mode.greenBits, &mode.blueBits); return mode; } ////////////////////////////////////////////////////////////////////////// ////// GLFW internal API ////// ////////////////////////////////////////////////////////////////////////// // Poll for changes in the set of connected monitors // void _glfwPollMonitorsX11(void) { if (_glfw.x11.randr.available && !_glfw.x11.randr.monitorBroken) { int disconnectedCount, screenCount = 0; _GLFWmonitor** disconnected = NULL; XineramaScreenInfo* screens = NULL; XRRScreenResources* sr = XRRGetScreenResourcesCurrent(_glfw.x11.display, _glfw.x11.root); RROutput primary = XRRGetOutputPrimary(_glfw.x11.display, _glfw.x11.root); if (_glfw.x11.xinerama.available) screens = XineramaQueryScreens(_glfw.x11.display, &screenCount); disconnectedCount = _glfw.monitorCount; if (disconnectedCount) { disconnected = calloc(_glfw.monitorCount, sizeof(_GLFWmonitor*)); memcpy(disconnected, _glfw.monitors, _glfw.monitorCount * sizeof(_GLFWmonitor*)); } for (int i = 0; i < sr->noutput; i++) { int j, type, widthMM, heightMM; XRROutputInfo* oi = XRRGetOutputInfo(_glfw.x11.display, sr, sr->outputs[i]); if (oi->connection != RR_Connected || oi->crtc == None) { XRRFreeOutputInfo(oi); continue; } for (j = 0; j < disconnectedCount; j++) { if (disconnected[j] && disconnected[j]->x11.output == sr->outputs[i]) { disconnected[j] = NULL; break; } } if (j < disconnectedCount) { XRRFreeOutputInfo(oi); continue; } XRRCrtcInfo* ci = XRRGetCrtcInfo(_glfw.x11.display, sr, oi->crtc); if (ci->rotation == RR_Rotate_90 || ci->rotation == RR_Rotate_270) { widthMM = oi->mm_height; heightMM = oi->mm_width; } else { widthMM = oi->mm_width; heightMM = oi->mm_height; } if (widthMM <= 0 || heightMM <= 0) { // HACK: If RandR does not provide a physical size, assume the // X11 default 96 DPI and calculate from the CRTC viewport // NOTE: These members are affected by rotation, unlike the mode // info and output info members widthMM = (int) (ci->width * 25.4f / 96.f); heightMM = (int) (ci->height * 25.4f / 96.f); } _GLFWmonitor* monitor = _glfwAllocMonitor(oi->name, widthMM, heightMM); monitor->x11.output = sr->outputs[i]; monitor->x11.crtc = oi->crtc; for (j = 0; j < screenCount; j++) { if (screens[j].x_org == ci->x && screens[j].y_org == ci->y && screens[j].width == ci->width && screens[j].height == ci->height) { monitor->x11.index = j; break; } } if (monitor->x11.output == primary) type = _GLFW_INSERT_FIRST; else type = _GLFW_INSERT_LAST; _glfwInputMonitor(monitor, GLFW_CONNECTED, type); XRRFreeOutputInfo(oi); XRRFreeCrtcInfo(ci); } XRRFreeScreenResources(sr); if (screens) XFree(screens); for (int i = 0; i < disconnectedCount; i++) { if (disconnected[i]) _glfwInputMonitor(disconnected[i], GLFW_DISCONNECTED, 0); } free(disconnected); } else { const int widthMM = DisplayWidthMM(_glfw.x11.display, _glfw.x11.screen); const int heightMM = DisplayHeightMM(_glfw.x11.display, _glfw.x11.screen); _glfwInputMonitor(_glfwAllocMonitor("Display", widthMM, heightMM), GLFW_CONNECTED, _GLFW_INSERT_FIRST); } } // Set the current video mode for the specified monitor // void _glfwSetVideoModeX11(_GLFWmonitor* monitor, const GLFWvidmode* desired) { if (_glfw.x11.randr.available && !_glfw.x11.randr.monitorBroken) { GLFWvidmode current; RRMode native = None; const GLFWvidmode* best = _glfwChooseVideoMode(monitor, desired); _glfwPlatformGetVideoMode(monitor, ¤t); if (_glfwCompareVideoModes(¤t, best) == 0) return; XRRScreenResources* sr = XRRGetScreenResourcesCurrent(_glfw.x11.display, _glfw.x11.root); XRRCrtcInfo* ci = XRRGetCrtcInfo(_glfw.x11.display, sr, monitor->x11.crtc); XRROutputInfo* oi = XRRGetOutputInfo(_glfw.x11.display, sr, monitor->x11.output); for (int i = 0; i < oi->nmode; i++) { const XRRModeInfo* mi = getModeInfo(sr, oi->modes[i]); if (!modeIsGood(mi)) continue; const GLFWvidmode mode = vidmodeFromModeInfo(mi, ci); if (_glfwCompareVideoModes(best, &mode) == 0) { native = mi->id; break; } } if (native) { if (monitor->x11.oldMode == None) monitor->x11.oldMode = ci->mode; XRRSetCrtcConfig(_glfw.x11.display, sr, monitor->x11.crtc, CurrentTime, ci->x, ci->y, native, ci->rotation, ci->outputs, ci->noutput); } XRRFreeOutputInfo(oi); XRRFreeCrtcInfo(ci); XRRFreeScreenResources(sr); } } // Restore the saved (original) video mode for the specified monitor // void _glfwRestoreVideoModeX11(_GLFWmonitor* monitor) { if (_glfw.x11.randr.available && !_glfw.x11.randr.monitorBroken) { if (monitor->x11.oldMode == None) return; XRRScreenResources* sr = XRRGetScreenResourcesCurrent(_glfw.x11.display, _glfw.x11.root); XRRCrtcInfo* ci = XRRGetCrtcInfo(_glfw.x11.display, sr, monitor->x11.crtc); XRRSetCrtcConfig(_glfw.x11.display, sr, monitor->x11.crtc, CurrentTime, ci->x, ci->y, monitor->x11.oldMode, ci->rotation, ci->outputs, ci->noutput); XRRFreeCrtcInfo(ci); XRRFreeScreenResources(sr); monitor->x11.oldMode = None; } } ////////////////////////////////////////////////////////////////////////// ////// GLFW platform API ////// ////////////////////////////////////////////////////////////////////////// void _glfwPlatformFreeMonitor(_GLFWmonitor* monitor) { } void _glfwPlatformGetMonitorPos(_GLFWmonitor* monitor, int* xpos, int* ypos) { if (_glfw.x11.randr.available && !_glfw.x11.randr.monitorBroken) { XRRScreenResources* sr = XRRGetScreenResourcesCurrent(_glfw.x11.display, _glfw.x11.root); XRRCrtcInfo* ci = XRRGetCrtcInfo(_glfw.x11.display, sr, monitor->x11.crtc); if (ci) { if (xpos) *xpos = ci->x; if (ypos) *ypos = ci->y; XRRFreeCrtcInfo(ci); } XRRFreeScreenResources(sr); } } void _glfwPlatformGetMonitorContentScale(_GLFWmonitor* monitor, float* xscale, float* yscale) { if (xscale) *xscale = _glfw.x11.contentScaleX; if (yscale) *yscale = _glfw.x11.contentScaleY; } void _glfwPlatformGetMonitorWorkarea(_GLFWmonitor* monitor, int* xpos, int* ypos, int* width, int* height) { int areaX = 0, areaY = 0, areaWidth = 0, areaHeight = 0; if (_glfw.x11.randr.available && !_glfw.x11.randr.monitorBroken) { XRRScreenResources* sr = XRRGetScreenResourcesCurrent(_glfw.x11.display, _glfw.x11.root); XRRCrtcInfo* ci = XRRGetCrtcInfo(_glfw.x11.display, sr, monitor->x11.crtc); areaX = ci->x; areaY = ci->y; const XRRModeInfo* mi = getModeInfo(sr, ci->mode); if (ci->rotation == RR_Rotate_90 || ci->rotation == RR_Rotate_270) { areaWidth = mi->height; areaHeight = mi->width; } else { areaWidth = mi->width; areaHeight = mi->height; } XRRFreeCrtcInfo(ci); XRRFreeScreenResources(sr); } else { areaWidth = DisplayWidth(_glfw.x11.display, _glfw.x11.screen); areaHeight = DisplayHeight(_glfw.x11.display, _glfw.x11.screen); } if (_glfw.x11.NET_WORKAREA && _glfw.x11.NET_CURRENT_DESKTOP) { Atom* extents = NULL; Atom* desktop = NULL; const unsigned long extentCount = _glfwGetWindowPropertyX11(_glfw.x11.root, _glfw.x11.NET_WORKAREA, XA_CARDINAL, (unsigned char**) &extents); if (_glfwGetWindowPropertyX11(_glfw.x11.root, _glfw.x11.NET_CURRENT_DESKTOP, XA_CARDINAL, (unsigned char**) &desktop) > 0) { if (extentCount >= 4 && *desktop < extentCount / 4) { const int globalX = extents[*desktop * 4 + 0]; const int globalY = extents[*desktop * 4 + 1]; const int globalWidth = extents[*desktop * 4 + 2]; const int globalHeight = extents[*desktop * 4 + 3]; if (areaX < globalX) { areaWidth -= globalX - areaX; areaX = globalX; } if (areaY < globalY) { areaHeight -= globalY - areaY; areaY = globalY; } if (areaX + areaWidth > globalX + globalWidth) areaWidth = globalX - areaX + globalWidth; if (areaY + areaHeight > globalY + globalHeight) areaHeight = globalY - areaY + globalHeight; } } if (extents) XFree(extents); if (desktop) XFree(desktop); } if (xpos) *xpos = areaX; if (ypos) *ypos = areaY; if (width) *width = areaWidth; if (height) *height = areaHeight; } GLFWvidmode* _glfwPlatformGetVideoModes(_GLFWmonitor* monitor, int* count) { GLFWvidmode* result; *count = 0; if (_glfw.x11.randr.available && !_glfw.x11.randr.monitorBroken) { XRRScreenResources* sr = XRRGetScreenResourcesCurrent(_glfw.x11.display, _glfw.x11.root); XRRCrtcInfo* ci = XRRGetCrtcInfo(_glfw.x11.display, sr, monitor->x11.crtc); XRROutputInfo* oi = XRRGetOutputInfo(_glfw.x11.display, sr, monitor->x11.output); result = calloc(oi->nmode, sizeof(GLFWvidmode)); for (int i = 0; i < oi->nmode; i++) { const XRRModeInfo* mi = getModeInfo(sr, oi->modes[i]); if (!modeIsGood(mi)) continue; const GLFWvidmode mode = vidmodeFromModeInfo(mi, ci); int j; for (j = 0; j < *count; j++) { if (_glfwCompareVideoModes(result + j, &mode) == 0) break; } // Skip duplicate modes if (j < *count) continue; (*count)++; result[*count - 1] = mode; } XRRFreeOutputInfo(oi); XRRFreeCrtcInfo(ci); XRRFreeScreenResources(sr); } else { *count = 1; result = calloc(1, sizeof(GLFWvidmode)); _glfwPlatformGetVideoMode(monitor, result); } return result; } void _glfwPlatformGetVideoMode(_GLFWmonitor* monitor, GLFWvidmode* mode) { if (_glfw.x11.randr.available && !_glfw.x11.randr.monitorBroken) { XRRScreenResources* sr = XRRGetScreenResourcesCurrent(_glfw.x11.display, _glfw.x11.root); XRRCrtcInfo* ci = XRRGetCrtcInfo(_glfw.x11.display, sr, monitor->x11.crtc); if (ci) { const XRRModeInfo* mi = getModeInfo(sr, ci->mode); if (mi) // mi can be NULL if the monitor has been disconnected *mode = vidmodeFromModeInfo(mi, ci); XRRFreeCrtcInfo(ci); } XRRFreeScreenResources(sr); } else { mode->width = DisplayWidth(_glfw.x11.display, _glfw.x11.screen); mode->height = DisplayHeight(_glfw.x11.display, _glfw.x11.screen); mode->refreshRate = 0; _glfwSplitBPP(DefaultDepth(_glfw.x11.display, _glfw.x11.screen), &mode->redBits, &mode->greenBits, &mode->blueBits); } } GLFWbool _glfwPlatformGetGammaRamp(_GLFWmonitor* monitor, GLFWgammaramp* ramp) { if (_glfw.x11.randr.available && !_glfw.x11.randr.gammaBroken) { const size_t size = XRRGetCrtcGammaSize(_glfw.x11.display, monitor->x11.crtc); XRRCrtcGamma* gamma = XRRGetCrtcGamma(_glfw.x11.display, monitor->x11.crtc); _glfwAllocGammaArrays(ramp, size); memcpy(ramp->red, gamma->red, size * sizeof(unsigned short)); memcpy(ramp->green, gamma->green, size * sizeof(unsigned short)); memcpy(ramp->blue, gamma->blue, size * sizeof(unsigned short)); XRRFreeGamma(gamma); return GLFW_TRUE; } else if (_glfw.x11.vidmode.available) { int size; XF86VidModeGetGammaRampSize(_glfw.x11.display, _glfw.x11.screen, &size); _glfwAllocGammaArrays(ramp, size); XF86VidModeGetGammaRamp(_glfw.x11.display, _glfw.x11.screen, ramp->size, ramp->red, ramp->green, ramp->blue); return GLFW_TRUE; } else { _glfwInputError(GLFW_PLATFORM_ERROR, "X11: Gamma ramp access not supported by server"); return GLFW_FALSE; } } void _glfwPlatformSetGammaRamp(_GLFWmonitor* monitor, const GLFWgammaramp* ramp) { if (_glfw.x11.randr.available && !_glfw.x11.randr.gammaBroken) { if (XRRGetCrtcGammaSize(_glfw.x11.display, monitor->x11.crtc) != ramp->size) { _glfwInputError(GLFW_PLATFORM_ERROR, "X11: Gamma ramp size must match current ramp size"); return; } XRRCrtcGamma* gamma = XRRAllocGamma(ramp->size); memcpy(gamma->red, ramp->red, ramp->size * sizeof(unsigned short)); memcpy(gamma->green, ramp->green, ramp->size * sizeof(unsigned short)); memcpy(gamma->blue, ramp->blue, ramp->size * sizeof(unsigned short)); XRRSetCrtcGamma(_glfw.x11.display, monitor->x11.crtc, gamma); XRRFreeGamma(gamma); } else if (_glfw.x11.vidmode.available) { XF86VidModeSetGammaRamp(_glfw.x11.display, _glfw.x11.screen, ramp->size, (unsigned short*) ramp->red, (unsigned short*) ramp->green, (unsigned short*) ramp->blue); } else { _glfwInputError(GLFW_PLATFORM_ERROR, "X11: Gamma ramp access not supported by server"); } } ////////////////////////////////////////////////////////////////////////// ////// GLFW native API ////// ////////////////////////////////////////////////////////////////////////// GLFWAPI RRCrtc glfwGetX11Adapter(GLFWmonitor* handle) { _GLFWmonitor* monitor = (_GLFWmonitor*) handle; _GLFW_REQUIRE_INIT_OR_RETURN(None); return monitor->x11.crtc; } GLFWAPI RROutput glfwGetX11Monitor(GLFWmonitor* handle) { _GLFWmonitor* monitor = (_GLFWmonitor*) handle; _GLFW_REQUIRE_INIT_OR_RETURN(None); return monitor->x11.output; } #endif #ifndef HEADER_GUARD_X11_WINDOW_C #define HEADER_GUARD_X11_WINDOW_C //======================================================================== // GLFW 3.3.7 X11 - www.glfw.org //------------------------------------------------------------------------ // Copyright (c) 2002-2006 Marcus Geelnard // Copyright (c) 2006-2019 Camilla Löwy // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would // be appreciated but is not required. // // 2. Altered source versions must be plainly marked as such, and must not // be misrepresented as being the original software. // // 3. This notice may not be removed or altered from any source // distribution. // //======================================================================== // It is fine to use C99 in this file because it will not be built with VS //======================================================================== #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include // Action for EWMH client messages #define _NET_WM_STATE_REMOVE 0 #define _NET_WM_STATE_ADD 1 #define _NET_WM_STATE_TOGGLE 2 // Additional mouse button names for XButtonEvent #define Button6 6 #define Button7 7 // Motif WM hints flags #define MWM_HINTS_DECORATIONS 2 #define MWM_DECOR_ALL 1 #define _GLFW_XDND_VERSION 5 // Wait for data to arrive on any of the specified file descriptors // static GLFWbool waitForData(struct pollfd* fds, nfds_t count, double* timeout) { for (;;) { if (timeout) { const uint64_t base = _glfwPlatformGetTimerValue(); #if defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__CYGWIN__) const time_t seconds = (time_t) *timeout; const long nanoseconds = (long) ((*timeout - seconds) * 1e9); const struct timespec ts = { seconds, nanoseconds }; const int result = ppoll(fds, count, &ts, NULL); #elif defined(__NetBSD__) const time_t seconds = (time_t) *timeout; const long nanoseconds = (long) ((*timeout - seconds) * 1e9); const struct timespec ts = { seconds, nanoseconds }; const int result = pollts(fds, count, &ts, NULL); #else const int milliseconds = (int) (*timeout * 1e3); const int result = poll(fds, count, milliseconds); #endif const int error = errno; // clock_gettime may overwrite our error *timeout -= (_glfwPlatformGetTimerValue() - base) / (double) _glfwPlatformGetTimerFrequency(); if (result > 0) return GLFW_TRUE; else if (result == -1 && error != EINTR && error != EAGAIN) return GLFW_FALSE; else if (*timeout <= 0.0) return GLFW_FALSE; } else { const int result = poll(fds, count, -1); if (result > 0) return GLFW_TRUE; else if (result == -1 && errno != EINTR && errno != EAGAIN) return GLFW_FALSE; } } } // Wait for event data to arrive on the X11 display socket // This avoids blocking other threads via the per-display Xlib lock that also // covers GLX functions // static GLFWbool waitForX11Event(double* timeout) { struct pollfd fd = { ConnectionNumber(_glfw.x11.display), POLLIN }; while (!XPending(_glfw.x11.display)) { if (!waitForData(&fd, 1, timeout)) return GLFW_FALSE; } return GLFW_TRUE; } // Wait for event data to arrive on any event file descriptor // This avoids blocking other threads via the per-display Xlib lock that also // covers GLX functions // static GLFWbool waitForAnyEvent(double* timeout) { nfds_t count = 2; struct pollfd fds[3] = { { ConnectionNumber(_glfw.x11.display), POLLIN }, { _glfw.x11.emptyEventPipe[0], POLLIN } }; #if defined(__linux__) if (_glfw.linjs.inotify > 0) fds[count++] = (struct pollfd) { _glfw.linjs.inotify, POLLIN }; #endif while (!XPending(_glfw.x11.display)) { if (!waitForData(fds, count, timeout)) return GLFW_FALSE; for (int i = 1; i < count; i++) { if (fds[i].revents & POLLIN) return GLFW_TRUE; } } return GLFW_TRUE; } // Writes a byte to the empty event pipe // static void writeEmptyEvent(void) { for (;;) { const char byte = 0; const int result = write(_glfw.x11.emptyEventPipe[1], &byte, 1); if (result == 1 || (result == -1 && errno != EINTR)) break; } } // Drains available data from the empty event pipe // static void drainEmptyEvents(void) { for (;;) { char dummy[64]; const int result = read(_glfw.x11.emptyEventPipe[0], dummy, sizeof(dummy)); if (result == -1 && errno != EINTR) break; } } // Waits until a VisibilityNotify event arrives for the specified window or the // timeout period elapses (ICCCM section 4.2.2) // static GLFWbool waitForVisibilityNotify(_GLFWwindow* window) { XEvent dummy; double timeout = 0.1; while (!XCheckTypedWindowEvent(_glfw.x11.display, window->x11.handle, VisibilityNotify, &dummy)) { if (!waitForX11Event(&timeout)) return GLFW_FALSE; } return GLFW_TRUE; } // Returns whether the window is iconified // static int getWindowState(_GLFWwindow* window) { int result = WithdrawnState; struct { CARD32 state; Window icon; } *state = NULL; if (_glfwGetWindowPropertyX11(window->x11.handle, _glfw.x11.WM_STATE, _glfw.x11.WM_STATE, (unsigned char**) &state) >= 2) { result = state->state; } if (state) XFree(state); return result; } // Returns whether the event is a selection event // static Bool isSelectionEvent(Display* display, XEvent* event, XPointer pointer) { if (event->xany.window != _glfw.x11.helperWindowHandle) return False; return event->type == SelectionRequest || event->type == SelectionNotify || event->type == SelectionClear; } // Returns whether it is a _NET_FRAME_EXTENTS event for the specified window // static Bool isFrameExtentsEvent(Display* display, XEvent* event, XPointer pointer) { _GLFWwindow* window = (_GLFWwindow*) pointer; return event->type == PropertyNotify && event->xproperty.state == PropertyNewValue && event->xproperty.window == window->x11.handle && event->xproperty.atom == _glfw.x11.NET_FRAME_EXTENTS; } // Returns whether it is a property event for the specified selection transfer // static Bool isSelPropNewValueNotify(Display* display, XEvent* event, XPointer pointer) { XEvent* notification = (XEvent*) pointer; return event->type == PropertyNotify && event->xproperty.state == PropertyNewValue && event->xproperty.window == notification->xselection.requestor && event->xproperty.atom == notification->xselection.property; } // Translates an X event modifier state mask // static int translateState(int state) { int mods = 0; if (state & ShiftMask) mods |= GLFW_MOD_SHIFT; if (state & ControlMask) mods |= GLFW_MOD_CONTROL; if (state & Mod1Mask) mods |= GLFW_MOD_ALT; if (state & Mod4Mask) mods |= GLFW_MOD_SUPER; if (state & LockMask) mods |= GLFW_MOD_CAPS_LOCK; if (state & Mod2Mask) mods |= GLFW_MOD_NUM_LOCK; return mods; } // Translates an X11 key code to a GLFW key token // static int translateKey(int scancode) { // Use the pre-filled LUT (see createKeyTables() in x11_init.c) if (scancode < 0 || scancode > 255) return GLFW_KEY_UNKNOWN; return _glfw.x11.keycodes[scancode]; } // Sends an EWMH or ICCCM event to the window manager // static void sendEventToWM(_GLFWwindow* window, Atom type, long a, long b, long c, long d, long e) { XEvent event = { ClientMessage }; event.xclient.window = window->x11.handle; event.xclient.format = 32; // Data is 32-bit longs event.xclient.message_type = type; event.xclient.data.l[0] = a; event.xclient.data.l[1] = b; event.xclient.data.l[2] = c; event.xclient.data.l[3] = d; event.xclient.data.l[4] = e; XSendEvent(_glfw.x11.display, _glfw.x11.root, False, SubstructureNotifyMask | SubstructureRedirectMask, &event); } // Updates the normal hints according to the window settings // static void updateNormalHints(_GLFWwindow* window, int width, int height) { XSizeHints* hints = XAllocSizeHints(); if (!window->monitor) { if (window->resizable) { if (window->minwidth != GLFW_DONT_CARE && window->minheight != GLFW_DONT_CARE) { hints->flags |= PMinSize; hints->min_width = window->minwidth; hints->min_height = window->minheight; } if (window->maxwidth != GLFW_DONT_CARE && window->maxheight != GLFW_DONT_CARE) { hints->flags |= PMaxSize; hints->max_width = window->maxwidth; hints->max_height = window->maxheight; } if (window->numer != GLFW_DONT_CARE && window->denom != GLFW_DONT_CARE) { hints->flags |= PAspect; hints->min_aspect.x = hints->max_aspect.x = window->numer; hints->min_aspect.y = hints->max_aspect.y = window->denom; } } else { hints->flags |= (PMinSize | PMaxSize); hints->min_width = hints->max_width = width; hints->min_height = hints->max_height = height; } } hints->flags |= PWinGravity; hints->win_gravity = StaticGravity; XSetWMNormalHints(_glfw.x11.display, window->x11.handle, hints); XFree(hints); } // Updates the full screen status of the window // static void updateWindowMode(_GLFWwindow* window) { if (window->monitor) { if (_glfw.x11.xinerama.available && _glfw.x11.NET_WM_FULLSCREEN_MONITORS) { sendEventToWM(window, _glfw.x11.NET_WM_FULLSCREEN_MONITORS, window->monitor->x11.index, window->monitor->x11.index, window->monitor->x11.index, window->monitor->x11.index, 0); } if (_glfw.x11.NET_WM_STATE && _glfw.x11.NET_WM_STATE_FULLSCREEN) { sendEventToWM(window, _glfw.x11.NET_WM_STATE, _NET_WM_STATE_ADD, _glfw.x11.NET_WM_STATE_FULLSCREEN, 0, 1, 0); } else { // This is the butcher's way of removing window decorations // Setting the override-redirect attribute on a window makes the // window manager ignore the window completely (ICCCM, section 4) // The good thing is that this makes undecorated full screen windows // easy to do; the bad thing is that we have to do everything // manually and some things (like iconify/restore) won't work at // all, as those are tasks usually performed by the window manager XSetWindowAttributes attributes; attributes.override_redirect = True; XChangeWindowAttributes(_glfw.x11.display, window->x11.handle, CWOverrideRedirect, &attributes); window->x11.overrideRedirect = GLFW_TRUE; } // Enable compositor bypass if (!window->x11.transparent) { const unsigned long value = 1; XChangeProperty(_glfw.x11.display, window->x11.handle, _glfw.x11.NET_WM_BYPASS_COMPOSITOR, XA_CARDINAL, 32, PropModeReplace, (unsigned char*) &value, 1); } } else { if (_glfw.x11.xinerama.available && _glfw.x11.NET_WM_FULLSCREEN_MONITORS) { XDeleteProperty(_glfw.x11.display, window->x11.handle, _glfw.x11.NET_WM_FULLSCREEN_MONITORS); } if (_glfw.x11.NET_WM_STATE && _glfw.x11.NET_WM_STATE_FULLSCREEN) { sendEventToWM(window, _glfw.x11.NET_WM_STATE, _NET_WM_STATE_REMOVE, _glfw.x11.NET_WM_STATE_FULLSCREEN, 0, 1, 0); } else { XSetWindowAttributes attributes; attributes.override_redirect = False; XChangeWindowAttributes(_glfw.x11.display, window->x11.handle, CWOverrideRedirect, &attributes); window->x11.overrideRedirect = GLFW_FALSE; } // Disable compositor bypass if (!window->x11.transparent) { XDeleteProperty(_glfw.x11.display, window->x11.handle, _glfw.x11.NET_WM_BYPASS_COMPOSITOR); } } } // Splits and translates a text/uri-list into separate file paths // NOTE: This function destroys the provided string // static char** parseUriList(char* text, int* count) { const char* prefix = "file://"; char** paths = NULL; char* line; *count = 0; while ((line = strtok(text, "\r\n"))) { text = NULL; if (line[0] == '#') continue; if (strncmp(line, prefix, strlen(prefix)) == 0) { line += strlen(prefix); // TODO: Validate hostname while (*line != '/') line++; } (*count)++; char* path = calloc(strlen(line) + 1, 1); paths = realloc(paths, *count * sizeof(char*)); paths[*count - 1] = path; while (*line) { if (line[0] == '%' && line[1] && line[2]) { const char digits[3] = { line[1], line[2], '\0' }; *path = strtol(digits, NULL, 16); line += 2; } else *path = *line; path++; line++; } } return paths; } // Decode a Unicode code point from a UTF-8 stream // Based on cutef8 by Jeff Bezanson (Public Domain) // #if defined(X_HAVE_UTF8_STRING) static uint32_t decodeUTF8(const char** s) { uint32_t codepoint = 0, count = 0; static const uint32_t offsets[] = { 0x00000000u, 0x00003080u, 0x000e2080u, 0x03c82080u, 0xfa082080u, 0x82082080u }; do { codepoint = (codepoint << 6) + (unsigned char) **s; (*s)++; count++; } while ((**s & 0xc0) == 0x80); assert(count <= 6); return codepoint - offsets[count - 1]; } #endif /*X_HAVE_UTF8_STRING*/ // Convert the specified Latin-1 string to UTF-8 // static char* convertLatin1toUTF8(const char* source) { size_t size = 1; const char* sp; for (sp = source; *sp; sp++) size += (*sp & 0x80) ? 2 : 1; char* target = calloc(size, 1); char* tp = target; for (sp = source; *sp; sp++) tp += _glfwEncodeUTF8(tp, *sp); return target; } // Updates the cursor image according to its cursor mode // static void updateCursorImage(_GLFWwindow* window) { if (window->cursorMode == GLFW_CURSOR_NORMAL) { if (window->cursor) { XDefineCursor(_glfw.x11.display, window->x11.handle, window->cursor->x11.handle); } else XUndefineCursor(_glfw.x11.display, window->x11.handle); } else { XDefineCursor(_glfw.x11.display, window->x11.handle, _glfw.x11.hiddenCursorHandle); } } // Enable XI2 raw mouse motion events // static void enableRawMouseMotion(_GLFWwindow* window) { XIEventMask em; unsigned char mask[XIMaskLen(XI_RawMotion)] = { 0 }; em.deviceid = XIAllMasterDevices; em.mask_len = sizeof(mask); em.mask = mask; XISetMask(mask, XI_RawMotion); XISelectEvents(_glfw.x11.display, _glfw.x11.root, &em, 1); } // Disable XI2 raw mouse motion events // static void disableRawMouseMotion(_GLFWwindow* window) { XIEventMask em; unsigned char mask[] = { 0 }; em.deviceid = XIAllMasterDevices; em.mask_len = sizeof(mask); em.mask = mask; XISelectEvents(_glfw.x11.display, _glfw.x11.root, &em, 1); } // Apply disabled cursor mode to a focused window // static void disableCursor(_GLFWwindow* window) { if (window->rawMouseMotion) enableRawMouseMotion(window); _glfw.x11.disabledCursorWindow = window; _glfwPlatformGetCursorPos(window, &_glfw.x11.restoreCursorPosX, &_glfw.x11.restoreCursorPosY); updateCursorImage(window); _glfwCenterCursorInContentArea(window); XGrabPointer(_glfw.x11.display, window->x11.handle, True, ButtonPressMask | ButtonReleaseMask | PointerMotionMask, GrabModeAsync, GrabModeAsync, window->x11.handle, _glfw.x11.hiddenCursorHandle, CurrentTime); } // Exit disabled cursor mode for the specified window // static void enableCursor(_GLFWwindow* window) { if (window->rawMouseMotion) disableRawMouseMotion(window); _glfw.x11.disabledCursorWindow = NULL; XUngrabPointer(_glfw.x11.display, CurrentTime); _glfwPlatformSetCursorPos(window, _glfw.x11.restoreCursorPosX, _glfw.x11.restoreCursorPosY); updateCursorImage(window); } // Create the X11 window (and its colormap) // static GLFWbool createNativeWindow(_GLFWwindow* window, const _GLFWwndconfig* wndconfig, Visual* visual, int depth) { int width = wndconfig->width; int height = wndconfig->height; if (wndconfig->scaleToMonitor) { width *= _glfw.x11.contentScaleX; height *= _glfw.x11.contentScaleY; } // Create a colormap based on the visual used by the current context window->x11.colormap = XCreateColormap(_glfw.x11.display, _glfw.x11.root, visual, AllocNone); window->x11.transparent = _glfwIsVisualTransparentX11(visual); XSetWindowAttributes wa = { 0 }; wa.colormap = window->x11.colormap; wa.event_mask = StructureNotifyMask | KeyPressMask | KeyReleaseMask | PointerMotionMask | ButtonPressMask | ButtonReleaseMask | ExposureMask | FocusChangeMask | VisibilityChangeMask | EnterWindowMask | LeaveWindowMask | PropertyChangeMask; _glfwGrabErrorHandlerX11(); window->x11.parent = _glfw.x11.root; window->x11.handle = XCreateWindow(_glfw.x11.display, _glfw.x11.root, 0, 0, // Position width, height, 0, // Border width depth, // Color depth InputOutput, visual, CWBorderPixel | CWColormap | CWEventMask, &wa); _glfwReleaseErrorHandlerX11(); if (!window->x11.handle) { _glfwInputErrorX11(GLFW_PLATFORM_ERROR, "X11: Failed to create window"); return GLFW_FALSE; } XSaveContext(_glfw.x11.display, window->x11.handle, _glfw.x11.context, (XPointer) window); if (!wndconfig->decorated) _glfwPlatformSetWindowDecorated(window, GLFW_FALSE); if (_glfw.x11.NET_WM_STATE && !window->monitor) { Atom states[3]; int count = 0; if (wndconfig->floating) { if (_glfw.x11.NET_WM_STATE_ABOVE) states[count++] = _glfw.x11.NET_WM_STATE_ABOVE; } if (wndconfig->maximized) { if (_glfw.x11.NET_WM_STATE_MAXIMIZED_VERT && _glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ) { states[count++] = _glfw.x11.NET_WM_STATE_MAXIMIZED_VERT; states[count++] = _glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ; window->x11.maximized = GLFW_TRUE; } } if (count) { XChangeProperty(_glfw.x11.display, window->x11.handle, _glfw.x11.NET_WM_STATE, XA_ATOM, 32, PropModeReplace, (unsigned char*) states, count); } } // Declare the WM protocols supported by GLFW { Atom protocols[] = { _glfw.x11.WM_DELETE_WINDOW, _glfw.x11.NET_WM_PING }; XSetWMProtocols(_glfw.x11.display, window->x11.handle, protocols, sizeof(protocols) / sizeof(Atom)); } // Declare our PID { const long pid = getpid(); XChangeProperty(_glfw.x11.display, window->x11.handle, _glfw.x11.NET_WM_PID, XA_CARDINAL, 32, PropModeReplace, (unsigned char*) &pid, 1); } if (_glfw.x11.NET_WM_WINDOW_TYPE && _glfw.x11.NET_WM_WINDOW_TYPE_NORMAL) { Atom type = _glfw.x11.NET_WM_WINDOW_TYPE_NORMAL; XChangeProperty(_glfw.x11.display, window->x11.handle, _glfw.x11.NET_WM_WINDOW_TYPE, XA_ATOM, 32, PropModeReplace, (unsigned char*) &type, 1); } // Set ICCCM WM_HINTS property { XWMHints* hints = XAllocWMHints(); if (!hints) { _glfwInputError(GLFW_OUT_OF_MEMORY, "X11: Failed to allocate WM hints"); return GLFW_FALSE; } hints->flags = StateHint; hints->initial_state = NormalState; XSetWMHints(_glfw.x11.display, window->x11.handle, hints); XFree(hints); } updateNormalHints(window, width, height); // Set ICCCM WM_CLASS property { XClassHint* hint = XAllocClassHint(); if (strlen(wndconfig->x11.instanceName) && strlen(wndconfig->x11.className)) { hint->res_name = (char*) wndconfig->x11.instanceName; hint->res_class = (char*) wndconfig->x11.className; } else { const char* resourceName = getenv("RESOURCE_NAME"); if (resourceName && strlen(resourceName)) hint->res_name = (char*) resourceName; else if (strlen(wndconfig->title)) hint->res_name = (char*) wndconfig->title; else hint->res_name = (char*) "glfw-application"; if (strlen(wndconfig->title)) hint->res_class = (char*) wndconfig->title; else hint->res_class = (char*) "GLFW-Application"; } XSetClassHint(_glfw.x11.display, window->x11.handle, hint); XFree(hint); } // Announce support for Xdnd (drag and drop) { const Atom version = _GLFW_XDND_VERSION; XChangeProperty(_glfw.x11.display, window->x11.handle, _glfw.x11.XdndAware, XA_ATOM, 32, PropModeReplace, (unsigned char*) &version, 1); } _glfwPlatformSetWindowTitle(window, wndconfig->title); if (_glfw.x11.im) { window->x11.ic = XCreateIC(_glfw.x11.im, XNInputStyle, XIMPreeditNothing | XIMStatusNothing, XNClientWindow, window->x11.handle, XNFocusWindow, window->x11.handle, NULL); } if (window->x11.ic) { unsigned long filter = 0; if (XGetICValues(window->x11.ic, XNFilterEvents, &filter, NULL) == NULL) XSelectInput(_glfw.x11.display, window->x11.handle, wa.event_mask | filter); } _glfwPlatformGetWindowPos(window, &window->x11.xpos, &window->x11.ypos); _glfwPlatformGetWindowSize(window, &window->x11.width, &window->x11.height); return GLFW_TRUE; } // Set the specified property to the selection converted to the requested target // static Atom writeTargetToProperty(const XSelectionRequestEvent* request) { int i; char* selectionString = NULL; const Atom formats[] = { _glfw.x11.UTF8_STRING, XA_STRING }; const int formatCount = sizeof(formats) / sizeof(formats[0]); if (request->selection == _glfw.x11.PRIMARY) selectionString = _glfw.x11.primarySelectionString; else selectionString = _glfw.x11.clipboardString; if (request->property == None) { // The requester is a legacy client (ICCCM section 2.2) // We don't support legacy clients, so fail here return None; } if (request->target == _glfw.x11.TARGETS) { // The list of supported targets was requested const Atom targets[] = { _glfw.x11.TARGETS, _glfw.x11.MULTIPLE, _glfw.x11.UTF8_STRING, XA_STRING }; XChangeProperty(_glfw.x11.display, request->requestor, request->property, XA_ATOM, 32, PropModeReplace, (unsigned char*) targets, sizeof(targets) / sizeof(targets[0])); return request->property; } if (request->target == _glfw.x11.MULTIPLE) { // Multiple conversions were requested Atom* targets; unsigned long i, count; count = _glfwGetWindowPropertyX11(request->requestor, request->property, _glfw.x11.ATOM_PAIR, (unsigned char**) &targets); for (i = 0; i < count; i += 2) { int j; for (j = 0; j < formatCount; j++) { if (targets[i] == formats[j]) break; } if (j < formatCount) { XChangeProperty(_glfw.x11.display, request->requestor, targets[i + 1], targets[i], 8, PropModeReplace, (unsigned char *) selectionString, strlen(selectionString)); } else targets[i + 1] = None; } XChangeProperty(_glfw.x11.display, request->requestor, request->property, _glfw.x11.ATOM_PAIR, 32, PropModeReplace, (unsigned char*) targets, count); XFree(targets); return request->property; } if (request->target == _glfw.x11.SAVE_TARGETS) { // The request is a check whether we support SAVE_TARGETS // It should be handled as a no-op side effect target XChangeProperty(_glfw.x11.display, request->requestor, request->property, _glfw.x11.NULL_, 32, PropModeReplace, NULL, 0); return request->property; } // Conversion to a data target was requested for (i = 0; i < formatCount; i++) { if (request->target == formats[i]) { // The requested target is one we support XChangeProperty(_glfw.x11.display, request->requestor, request->property, request->target, 8, PropModeReplace, (unsigned char *) selectionString, strlen(selectionString)); return request->property; } } // The requested target is not supported return None; } static void handleSelectionClear(XEvent* event) { if (event->xselectionclear.selection == _glfw.x11.PRIMARY) { free(_glfw.x11.primarySelectionString); _glfw.x11.primarySelectionString = NULL; } else { free(_glfw.x11.clipboardString); _glfw.x11.clipboardString = NULL; } } static void handleSelectionRequest(XEvent* event) { const XSelectionRequestEvent* request = &event->xselectionrequest; XEvent reply = { SelectionNotify }; reply.xselection.property = writeTargetToProperty(request); reply.xselection.display = request->display; reply.xselection.requestor = request->requestor; reply.xselection.selection = request->selection; reply.xselection.target = request->target; reply.xselection.time = request->time; XSendEvent(_glfw.x11.display, request->requestor, False, 0, &reply); } static const char* getSelectionString(Atom selection) { char** selectionString = NULL; const Atom targets[] = { _glfw.x11.UTF8_STRING, XA_STRING }; const size_t targetCount = sizeof(targets) / sizeof(targets[0]); if (selection == _glfw.x11.PRIMARY) selectionString = &_glfw.x11.primarySelectionString; else selectionString = &_glfw.x11.clipboardString; if (XGetSelectionOwner(_glfw.x11.display, selection) == _glfw.x11.helperWindowHandle) { // Instead of doing a large number of X round-trips just to put this // string into a window property and then read it back, just return it return *selectionString; } free(*selectionString); *selectionString = NULL; for (size_t i = 0; i < targetCount; i++) { char* data; Atom actualType; int actualFormat; unsigned long itemCount, bytesAfter; XEvent notification, dummy; XConvertSelection(_glfw.x11.display, selection, targets[i], _glfw.x11.GLFW_SELECTION, _glfw.x11.helperWindowHandle, CurrentTime); while (!XCheckTypedWindowEvent(_glfw.x11.display, _glfw.x11.helperWindowHandle, SelectionNotify, ¬ification)) { waitForX11Event(NULL); } if (notification.xselection.property == None) continue; XCheckIfEvent(_glfw.x11.display, &dummy, isSelPropNewValueNotify, (XPointer) ¬ification); XGetWindowProperty(_glfw.x11.display, notification.xselection.requestor, notification.xselection.property, 0, LONG_MAX, True, AnyPropertyType, &actualType, &actualFormat, &itemCount, &bytesAfter, (unsigned char**) &data); if (actualType == _glfw.x11.INCR) { size_t size = 1; char* string = NULL; for (;;) { while (!XCheckIfEvent(_glfw.x11.display, &dummy, isSelPropNewValueNotify, (XPointer) ¬ification)) { waitForX11Event(NULL); } XFree(data); XGetWindowProperty(_glfw.x11.display, notification.xselection.requestor, notification.xselection.property, 0, LONG_MAX, True, AnyPropertyType, &actualType, &actualFormat, &itemCount, &bytesAfter, (unsigned char**) &data); if (itemCount) { size += itemCount; string = realloc(string, size); string[size - itemCount - 1] = '\0'; strcat(string, data); } if (!itemCount) { if (targets[i] == XA_STRING) { *selectionString = convertLatin1toUTF8(string); free(string); } else *selectionString = string; break; } } } else if (actualType == targets[i]) { if (targets[i] == XA_STRING) *selectionString = convertLatin1toUTF8(data); else *selectionString = _glfw_strdup(data); } XFree(data); if (*selectionString) break; } if (!*selectionString) { _glfwInputError(GLFW_FORMAT_UNAVAILABLE, "X11: Failed to convert selection to string"); } return *selectionString; } // Make the specified window and its video mode active on its monitor // static void acquireMonitor(_GLFWwindow* window) { if (_glfw.x11.saver.count == 0) { // Remember old screen saver settings XGetScreenSaver(_glfw.x11.display, &_glfw.x11.saver.timeout, &_glfw.x11.saver.interval, &_glfw.x11.saver.blanking, &_glfw.x11.saver.exposure); // Disable screen saver XSetScreenSaver(_glfw.x11.display, 0, 0, DontPreferBlanking, DefaultExposures); } if (!window->monitor->window) _glfw.x11.saver.count++; _glfwSetVideoModeX11(window->monitor, &window->videoMode); if (window->x11.overrideRedirect) { int xpos, ypos; GLFWvidmode mode; // Manually position the window over its monitor _glfwPlatformGetMonitorPos(window->monitor, &xpos, &ypos); _glfwPlatformGetVideoMode(window->monitor, &mode); XMoveResizeWindow(_glfw.x11.display, window->x11.handle, xpos, ypos, mode.width, mode.height); } _glfwInputMonitorWindow(window->monitor, window); } // Remove the window and restore the original video mode // static void releaseMonitor(_GLFWwindow* window) { if (window->monitor->window != window) return; _glfwInputMonitorWindow(window->monitor, NULL); _glfwRestoreVideoModeX11(window->monitor); _glfw.x11.saver.count--; if (_glfw.x11.saver.count == 0) { // Restore old screen saver settings XSetScreenSaver(_glfw.x11.display, _glfw.x11.saver.timeout, _glfw.x11.saver.interval, _glfw.x11.saver.blanking, _glfw.x11.saver.exposure); } } // Process the specified X event // static void processEvent(XEvent *event) { int keycode = 0; Bool filtered = False; // HACK: Save scancode as some IMs clear the field in XFilterEvent if (event->type == KeyPress || event->type == KeyRelease) keycode = event->xkey.keycode; if (_glfw.x11.im) filtered = XFilterEvent(event, None); if (_glfw.x11.randr.available) { if (event->type == _glfw.x11.randr.eventBase + RRNotify) { XRRUpdateConfiguration(event); _glfwPollMonitorsX11(); return; } } if (_glfw.x11.xkb.available) { if (event->type == _glfw.x11.xkb.eventBase + XkbEventCode) { if (((XkbEvent*) event)->any.xkb_type == XkbStateNotify && (((XkbEvent*) event)->state.changed & XkbGroupStateMask)) { _glfw.x11.xkb.group = ((XkbEvent*) event)->state.group; } return; } } if (event->type == GenericEvent) { if (_glfw.x11.xi.available) { _GLFWwindow* window = _glfw.x11.disabledCursorWindow; if (window && window->rawMouseMotion && event->xcookie.extension == _glfw.x11.xi.majorOpcode && XGetEventData(_glfw.x11.display, &event->xcookie) && event->xcookie.evtype == XI_RawMotion) { XIRawEvent* re = event->xcookie.data; if (re->valuators.mask_len) { const double* values = re->raw_values; double xpos = window->virtualCursorPosX; double ypos = window->virtualCursorPosY; if (XIMaskIsSet(re->valuators.mask, 0)) { xpos += *values; values++; } if (XIMaskIsSet(re->valuators.mask, 1)) ypos += *values; _glfwInputCursorPos(window, xpos, ypos); } } XFreeEventData(_glfw.x11.display, &event->xcookie); } return; } if (event->type == SelectionClear) { handleSelectionClear(event); return; } else if (event->type == SelectionRequest) { handleSelectionRequest(event); return; } _GLFWwindow* window = NULL; if (XFindContext(_glfw.x11.display, event->xany.window, _glfw.x11.context, (XPointer*) &window) != 0) { // This is an event for a window that has already been destroyed return; } switch (event->type) { case ReparentNotify: { window->x11.parent = event->xreparent.parent; return; } case KeyPress: { const int key = translateKey(keycode); const int mods = translateState(event->xkey.state); const int plain = !(mods & (GLFW_MOD_CONTROL | GLFW_MOD_ALT)); if (window->x11.ic) { // HACK: Do not report the key press events duplicated by XIM // Duplicate key releases are filtered out implicitly by // the GLFW key repeat logic in _glfwInputKey // A timestamp per key is used to handle simultaneous keys // NOTE: Always allow the first event for each key through // (the server never sends a timestamp of zero) // NOTE: Timestamp difference is compared to handle wrap-around Time diff = event->xkey.time - window->x11.keyPressTimes[keycode]; if (diff == event->xkey.time || (diff > 0 && diff < ((Time)1 << 31))) { if (keycode) _glfwInputKey(window, key, keycode, GLFW_PRESS, mods); window->x11.keyPressTimes[keycode] = event->xkey.time; } if (!filtered) { int count; Status status; #if defined(X_HAVE_UTF8_STRING) char buffer[100]; char* chars = buffer; count = Xutf8LookupString(window->x11.ic, &event->xkey, buffer, sizeof(buffer) - 1, NULL, &status); if (status == XBufferOverflow) { chars = calloc(count + 1, 1); count = Xutf8LookupString(window->x11.ic, &event->xkey, chars, count, NULL, &status); } if (status == XLookupChars || status == XLookupBoth) { const char* c = chars; chars[count] = '\0'; while (c - chars < count) _glfwInputChar(window, decodeUTF8(&c), mods, plain); } #else /*X_HAVE_UTF8_STRING*/ wchar_t buffer[16]; wchar_t* chars = buffer; count = XwcLookupString(window->x11.ic, &event->xkey, buffer, sizeof(buffer) / sizeof(wchar_t), NULL, &status); if (status == XBufferOverflow) { chars = calloc(count, sizeof(wchar_t)); count = XwcLookupString(window->x11.ic, &event->xkey, chars, count, NULL, &status); } if (status == XLookupChars || status == XLookupBoth) { int i; for (i = 0; i < count; i++) _glfwInputChar(window, chars[i], mods, plain); } #endif /*X_HAVE_UTF8_STRING*/ if (chars != buffer) free(chars); } } else { KeySym keysym; XLookupString(&event->xkey, NULL, 0, &keysym, NULL); _glfwInputKey(window, key, keycode, GLFW_PRESS, mods); const uint32_t codepoint = _glfwKeySym2Unicode(keysym); if (codepoint != GLFW_INVALID_CODEPOINT) _glfwInputChar(window, codepoint, mods, plain); } return; } case KeyRelease: { const int key = translateKey(keycode); const int mods = translateState(event->xkey.state); if (!_glfw.x11.xkb.detectable) { // HACK: Key repeat events will arrive as KeyRelease/KeyPress // pairs with similar or identical time stamps // The key repeat logic in _glfwInputKey expects only key // presses to repeat, so detect and discard release events if (XEventsQueued(_glfw.x11.display, QueuedAfterReading)) { XEvent next; XPeekEvent(_glfw.x11.display, &next); if (next.type == KeyPress && next.xkey.window == event->xkey.window && next.xkey.keycode == keycode) { // HACK: The time of repeat events sometimes doesn't // match that of the press event, so add an // epsilon // Toshiyuki Takahashi can press a button // 16 times per second so it's fairly safe to // assume that no human is pressing the key 50 // times per second (value is ms) if ((next.xkey.time - event->xkey.time) < 20) { // This is very likely a server-generated key repeat // event, so ignore it return; } } } } _glfwInputKey(window, key, keycode, GLFW_RELEASE, mods); return; } case ButtonPress: { const int mods = translateState(event->xbutton.state); if (event->xbutton.button == Button1) _glfwInputMouseClick(window, GLFW_MOUSE_BUTTON_LEFT, GLFW_PRESS, mods); else if (event->xbutton.button == Button2) _glfwInputMouseClick(window, GLFW_MOUSE_BUTTON_MIDDLE, GLFW_PRESS, mods); else if (event->xbutton.button == Button3) _glfwInputMouseClick(window, GLFW_MOUSE_BUTTON_RIGHT, GLFW_PRESS, mods); // Modern X provides scroll events as mouse button presses else if (event->xbutton.button == Button4) _glfwInputScroll(window, 0.0, 1.0); else if (event->xbutton.button == Button5) _glfwInputScroll(window, 0.0, -1.0); else if (event->xbutton.button == Button6) _glfwInputScroll(window, 1.0, 0.0); else if (event->xbutton.button == Button7) _glfwInputScroll(window, -1.0, 0.0); else { // Additional buttons after 7 are treated as regular buttons // We subtract 4 to fill the gap left by scroll input above _glfwInputMouseClick(window, event->xbutton.button - Button1 - 4, GLFW_PRESS, mods); } return; } case ButtonRelease: { const int mods = translateState(event->xbutton.state); if (event->xbutton.button == Button1) { _glfwInputMouseClick(window, GLFW_MOUSE_BUTTON_LEFT, GLFW_RELEASE, mods); } else if (event->xbutton.button == Button2) { _glfwInputMouseClick(window, GLFW_MOUSE_BUTTON_MIDDLE, GLFW_RELEASE, mods); } else if (event->xbutton.button == Button3) { _glfwInputMouseClick(window, GLFW_MOUSE_BUTTON_RIGHT, GLFW_RELEASE, mods); } else if (event->xbutton.button > Button7) { // Additional buttons after 7 are treated as regular buttons // We subtract 4 to fill the gap left by scroll input above _glfwInputMouseClick(window, event->xbutton.button - Button1 - 4, GLFW_RELEASE, mods); } return; } case EnterNotify: { // XEnterWindowEvent is XCrossingEvent const int x = event->xcrossing.x; const int y = event->xcrossing.y; // HACK: This is a workaround for WMs (KWM, Fluxbox) that otherwise // ignore the defined cursor for hidden cursor mode if (window->cursorMode == GLFW_CURSOR_HIDDEN) updateCursorImage(window); _glfwInputCursorEnter(window, GLFW_TRUE); _glfwInputCursorPos(window, x, y); window->x11.lastCursorPosX = x; window->x11.lastCursorPosY = y; return; } case LeaveNotify: { _glfwInputCursorEnter(window, GLFW_FALSE); return; } case MotionNotify: { const int x = event->xmotion.x; const int y = event->xmotion.y; if (x != window->x11.warpCursorPosX || y != window->x11.warpCursorPosY) { // The cursor was moved by something other than GLFW if (window->cursorMode == GLFW_CURSOR_DISABLED) { if (_glfw.x11.disabledCursorWindow != window) return; if (window->rawMouseMotion) return; const int dx = x - window->x11.lastCursorPosX; const int dy = y - window->x11.lastCursorPosY; _glfwInputCursorPos(window, window->virtualCursorPosX + dx, window->virtualCursorPosY + dy); } else _glfwInputCursorPos(window, x, y); } window->x11.lastCursorPosX = x; window->x11.lastCursorPosY = y; return; } case ConfigureNotify: { if (event->xconfigure.width != window->x11.width || event->xconfigure.height != window->x11.height) { _glfwInputFramebufferSize(window, event->xconfigure.width, event->xconfigure.height); _glfwInputWindowSize(window, event->xconfigure.width, event->xconfigure.height); window->x11.width = event->xconfigure.width; window->x11.height = event->xconfigure.height; } int xpos = event->xconfigure.x; int ypos = event->xconfigure.y; // NOTE: ConfigureNotify events from the server are in local // coordinates, so if we are reparented we need to translate // the position into root (screen) coordinates if (!event->xany.send_event && window->x11.parent != _glfw.x11.root) { _glfwGrabErrorHandlerX11(); Window dummy; XTranslateCoordinates(_glfw.x11.display, window->x11.parent, _glfw.x11.root, xpos, ypos, &xpos, &ypos, &dummy); _glfwReleaseErrorHandlerX11(); if (_glfw.x11.errorCode == BadWindow) return; } if (xpos != window->x11.xpos || ypos != window->x11.ypos) { _glfwInputWindowPos(window, xpos, ypos); window->x11.xpos = xpos; window->x11.ypos = ypos; } return; } case ClientMessage: { // Custom client message, probably from the window manager if (filtered) return; if (event->xclient.message_type == None) return; if (event->xclient.message_type == _glfw.x11.WM_PROTOCOLS) { const Atom protocol = event->xclient.data.l[0]; if (protocol == None) return; if (protocol == _glfw.x11.WM_DELETE_WINDOW) { // The window manager was asked to close the window, for // example by the user pressing a 'close' window decoration // button _glfwInputWindowCloseRequest(window); } else if (protocol == _glfw.x11.NET_WM_PING) { // The window manager is pinging the application to ensure // it's still responding to events XEvent reply = *event; reply.xclient.window = _glfw.x11.root; XSendEvent(_glfw.x11.display, _glfw.x11.root, False, SubstructureNotifyMask | SubstructureRedirectMask, &reply); } } else if (event->xclient.message_type == _glfw.x11.XdndEnter) { // A drag operation has entered the window unsigned long i, count; Atom* formats = NULL; const GLFWbool list = event->xclient.data.l[1] & 1; _glfw.x11.xdnd.source = event->xclient.data.l[0]; _glfw.x11.xdnd.version = event->xclient.data.l[1] >> 24; _glfw.x11.xdnd.format = None; if (_glfw.x11.xdnd.version > _GLFW_XDND_VERSION) return; if (list) { count = _glfwGetWindowPropertyX11(_glfw.x11.xdnd.source, _glfw.x11.XdndTypeList, XA_ATOM, (unsigned char**) &formats); } else { count = 3; formats = (Atom*) event->xclient.data.l + 2; } for (i = 0; i < count; i++) { if (formats[i] == _glfw.x11.text_uri_list) { _glfw.x11.xdnd.format = _glfw.x11.text_uri_list; break; } } if (list && formats) XFree(formats); } else if (event->xclient.message_type == _glfw.x11.XdndDrop) { // The drag operation has finished by dropping on the window Time time = CurrentTime; if (_glfw.x11.xdnd.version > _GLFW_XDND_VERSION) return; if (_glfw.x11.xdnd.format) { if (_glfw.x11.xdnd.version >= 1) time = event->xclient.data.l[2]; // Request the chosen format from the source window XConvertSelection(_glfw.x11.display, _glfw.x11.XdndSelection, _glfw.x11.xdnd.format, _glfw.x11.XdndSelection, window->x11.handle, time); } else if (_glfw.x11.xdnd.version >= 2) { XEvent reply = { ClientMessage }; reply.xclient.window = _glfw.x11.xdnd.source; reply.xclient.message_type = _glfw.x11.XdndFinished; reply.xclient.format = 32; reply.xclient.data.l[0] = window->x11.handle; reply.xclient.data.l[1] = 0; // The drag was rejected reply.xclient.data.l[2] = None; XSendEvent(_glfw.x11.display, _glfw.x11.xdnd.source, False, NoEventMask, &reply); XFlush(_glfw.x11.display); } } else if (event->xclient.message_type == _glfw.x11.XdndPosition) { // The drag operation has moved over the window const int xabs = (event->xclient.data.l[2] >> 16) & 0xffff; const int yabs = (event->xclient.data.l[2]) & 0xffff; Window dummy; int xpos, ypos; if (_glfw.x11.xdnd.version > _GLFW_XDND_VERSION) return; XTranslateCoordinates(_glfw.x11.display, _glfw.x11.root, window->x11.handle, xabs, yabs, &xpos, &ypos, &dummy); _glfwInputCursorPos(window, xpos, ypos); XEvent reply = { ClientMessage }; reply.xclient.window = _glfw.x11.xdnd.source; reply.xclient.message_type = _glfw.x11.XdndStatus; reply.xclient.format = 32; reply.xclient.data.l[0] = window->x11.handle; reply.xclient.data.l[2] = 0; // Specify an empty rectangle reply.xclient.data.l[3] = 0; if (_glfw.x11.xdnd.format) { // Reply that we are ready to copy the dragged data reply.xclient.data.l[1] = 1; // Accept with no rectangle if (_glfw.x11.xdnd.version >= 2) reply.xclient.data.l[4] = _glfw.x11.XdndActionCopy; } XSendEvent(_glfw.x11.display, _glfw.x11.xdnd.source, False, NoEventMask, &reply); XFlush(_glfw.x11.display); } return; } case SelectionNotify: { if (event->xselection.property == _glfw.x11.XdndSelection) { // The converted data from the drag operation has arrived char* data; const unsigned long result = _glfwGetWindowPropertyX11(event->xselection.requestor, event->xselection.property, event->xselection.target, (unsigned char**) &data); if (result) { int i, count; char** paths = parseUriList(data, &count); _glfwInputDrop(window, count, (const char**) paths); for (i = 0; i < count; i++) free(paths[i]); free(paths); } if (data) XFree(data); if (_glfw.x11.xdnd.version >= 2) { XEvent reply = { ClientMessage }; reply.xclient.window = _glfw.x11.xdnd.source; reply.xclient.message_type = _glfw.x11.XdndFinished; reply.xclient.format = 32; reply.xclient.data.l[0] = window->x11.handle; reply.xclient.data.l[1] = result; reply.xclient.data.l[2] = _glfw.x11.XdndActionCopy; XSendEvent(_glfw.x11.display, _glfw.x11.xdnd.source, False, NoEventMask, &reply); XFlush(_glfw.x11.display); } } return; } case FocusIn: { if (event->xfocus.mode == NotifyGrab || event->xfocus.mode == NotifyUngrab) { // Ignore focus events from popup indicator windows, window menu // key chords and window dragging return; } if (window->cursorMode == GLFW_CURSOR_DISABLED) disableCursor(window); if (window->x11.ic) XSetICFocus(window->x11.ic); _glfwInputWindowFocus(window, GLFW_TRUE); return; } case FocusOut: { if (event->xfocus.mode == NotifyGrab || event->xfocus.mode == NotifyUngrab) { // Ignore focus events from popup indicator windows, window menu // key chords and window dragging return; } if (window->cursorMode == GLFW_CURSOR_DISABLED) enableCursor(window); if (window->x11.ic) XUnsetICFocus(window->x11.ic); if (window->monitor && window->autoIconify) _glfwPlatformIconifyWindow(window); _glfwInputWindowFocus(window, GLFW_FALSE); return; } case Expose: { _glfwInputWindowDamage(window); return; } case PropertyNotify: { if (event->xproperty.state != PropertyNewValue) return; if (event->xproperty.atom == _glfw.x11.WM_STATE) { const int state = getWindowState(window); if (state != IconicState && state != NormalState) return; const GLFWbool iconified = (state == IconicState); if (window->x11.iconified != iconified) { if (window->monitor) { if (iconified) releaseMonitor(window); else acquireMonitor(window); } window->x11.iconified = iconified; _glfwInputWindowIconify(window, iconified); } } else if (event->xproperty.atom == _glfw.x11.NET_WM_STATE) { const GLFWbool maximized = _glfwPlatformWindowMaximized(window); if (window->x11.maximized != maximized) { window->x11.maximized = maximized; _glfwInputWindowMaximize(window, maximized); } } return; } case DestroyNotify: return; } } ////////////////////////////////////////////////////////////////////////// ////// GLFW internal API ////// ////////////////////////////////////////////////////////////////////////// // Retrieve a single window property of the specified type // Inspired by fghGetWindowProperty from freeglut // unsigned long _glfwGetWindowPropertyX11(Window window, Atom property, Atom type, unsigned char** value) { Atom actualType; int actualFormat; unsigned long itemCount, bytesAfter; XGetWindowProperty(_glfw.x11.display, window, property, 0, LONG_MAX, False, type, &actualType, &actualFormat, &itemCount, &bytesAfter, value); return itemCount; } GLFWbool _glfwIsVisualTransparentX11(Visual* visual) { if (!_glfw.x11.xrender.available) return GLFW_FALSE; XRenderPictFormat* pf = XRenderFindVisualFormat(_glfw.x11.display, visual); return pf && pf->direct.alphaMask; } // Push contents of our selection to clipboard manager // void _glfwPushSelectionToManagerX11(void) { XConvertSelection(_glfw.x11.display, _glfw.x11.CLIPBOARD_MANAGER, _glfw.x11.SAVE_TARGETS, None, _glfw.x11.helperWindowHandle, CurrentTime); for (;;) { XEvent event; while (XCheckIfEvent(_glfw.x11.display, &event, isSelectionEvent, NULL)) { switch (event.type) { case SelectionRequest: handleSelectionRequest(&event); break; case SelectionClear: handleSelectionClear(&event); break; case SelectionNotify: { if (event.xselection.target == _glfw.x11.SAVE_TARGETS) { // This means one of two things; either the selection // was not owned, which means there is no clipboard // manager, or the transfer to the clipboard manager has // completed // In either case, it means we are done here return; } break; } } } waitForX11Event(NULL); } } ////////////////////////////////////////////////////////////////////////// ////// GLFW platform API ////// ////////////////////////////////////////////////////////////////////////// int _glfwPlatformCreateWindow(_GLFWwindow* window, const _GLFWwndconfig* wndconfig, const _GLFWctxconfig* ctxconfig, const _GLFWfbconfig* fbconfig) { Visual* visual = NULL; int depth; if (ctxconfig->client != GLFW_NO_API) { if (ctxconfig->source == GLFW_NATIVE_CONTEXT_API) { if (!_glfwInitGLX()) return GLFW_FALSE; if (!_glfwChooseVisualGLX(wndconfig, ctxconfig, fbconfig, &visual, &depth)) return GLFW_FALSE; } else if (ctxconfig->source == GLFW_EGL_CONTEXT_API) { if (!_glfwInitEGL()) return GLFW_FALSE; if (!_glfwChooseVisualEGL(wndconfig, ctxconfig, fbconfig, &visual, &depth)) return GLFW_FALSE; } else if (ctxconfig->source == GLFW_OSMESA_CONTEXT_API) { if (!_glfwInitOSMesa()) return GLFW_FALSE; } } if (!visual) { visual = DefaultVisual(_glfw.x11.display, _glfw.x11.screen); depth = DefaultDepth(_glfw.x11.display, _glfw.x11.screen); } if (!createNativeWindow(window, wndconfig, visual, depth)) return GLFW_FALSE; if (ctxconfig->client != GLFW_NO_API) { if (ctxconfig->source == GLFW_NATIVE_CONTEXT_API) { if (!_glfwCreateContextGLX(window, ctxconfig, fbconfig)) return GLFW_FALSE; } else if (ctxconfig->source == GLFW_EGL_CONTEXT_API) { if (!_glfwCreateContextEGL(window, ctxconfig, fbconfig)) return GLFW_FALSE; } else if (ctxconfig->source == GLFW_OSMESA_CONTEXT_API) { if (!_glfwCreateContextOSMesa(window, ctxconfig, fbconfig)) return GLFW_FALSE; } } if (window->monitor) { _glfwPlatformShowWindow(window); updateWindowMode(window); acquireMonitor(window); } XFlush(_glfw.x11.display); return GLFW_TRUE; } void _glfwPlatformDestroyWindow(_GLFWwindow* window) { if (_glfw.x11.disabledCursorWindow == window) _glfw.x11.disabledCursorWindow = NULL; if (window->monitor) releaseMonitor(window); if (window->x11.ic) { XDestroyIC(window->x11.ic); window->x11.ic = NULL; } if (window->context.destroy) window->context.destroy(window); if (window->x11.handle) { XDeleteContext(_glfw.x11.display, window->x11.handle, _glfw.x11.context); XUnmapWindow(_glfw.x11.display, window->x11.handle); XDestroyWindow(_glfw.x11.display, window->x11.handle); window->x11.handle = (Window) 0; } if (window->x11.colormap) { XFreeColormap(_glfw.x11.display, window->x11.colormap); window->x11.colormap = (Colormap) 0; } XFlush(_glfw.x11.display); } void _glfwPlatformSetWindowTitle(_GLFWwindow* window, const char* title) { #if defined(X_HAVE_UTF8_STRING) Xutf8SetWMProperties(_glfw.x11.display, window->x11.handle, title, title, NULL, 0, NULL, NULL, NULL); #else // This may be a slightly better fallback than using XStoreName and // XSetIconName, which always store their arguments using STRING XmbSetWMProperties(_glfw.x11.display, window->x11.handle, title, title, NULL, 0, NULL, NULL, NULL); #endif XChangeProperty(_glfw.x11.display, window->x11.handle, _glfw.x11.NET_WM_NAME, _glfw.x11.UTF8_STRING, 8, PropModeReplace, (unsigned char*) title, strlen(title)); XChangeProperty(_glfw.x11.display, window->x11.handle, _glfw.x11.NET_WM_ICON_NAME, _glfw.x11.UTF8_STRING, 8, PropModeReplace, (unsigned char*) title, strlen(title)); XFlush(_glfw.x11.display); } void _glfwPlatformSetWindowIcon(_GLFWwindow* window, int count, const GLFWimage* images) { if (count) { int i, j, longCount = 0; for (i = 0; i < count; i++) longCount += 2 + images[i].width * images[i].height; unsigned long* icon = calloc(longCount, sizeof(unsigned long)); unsigned long* target = icon; for (i = 0; i < count; i++) { *target++ = images[i].width; *target++ = images[i].height; for (j = 0; j < images[i].width * images[i].height; j++) { *target++ = (((unsigned long) images[i].pixels[j * 4 + 0]) << 16) | (((unsigned long) images[i].pixels[j * 4 + 1]) << 8) | (((unsigned long) images[i].pixels[j * 4 + 2]) << 0) | (((unsigned long) images[i].pixels[j * 4 + 3]) << 24); } } // NOTE: XChangeProperty expects 32-bit values like the image data above to be // placed in the 32 least significant bits of individual longs. This is // true even if long is 64-bit and a WM protocol calls for "packed" data. // This is because of a historical mistake that then became part of the Xlib // ABI. Xlib will pack these values into a regular array of 32-bit values // before sending it over the wire. XChangeProperty(_glfw.x11.display, window->x11.handle, _glfw.x11.NET_WM_ICON, XA_CARDINAL, 32, PropModeReplace, (unsigned char*) icon, longCount); free(icon); } else { XDeleteProperty(_glfw.x11.display, window->x11.handle, _glfw.x11.NET_WM_ICON); } XFlush(_glfw.x11.display); } void _glfwPlatformGetWindowPos(_GLFWwindow* window, int* xpos, int* ypos) { Window dummy; int x, y; XTranslateCoordinates(_glfw.x11.display, window->x11.handle, _glfw.x11.root, 0, 0, &x, &y, &dummy); if (xpos) *xpos = x; if (ypos) *ypos = y; } void _glfwPlatformSetWindowPos(_GLFWwindow* window, int xpos, int ypos) { // HACK: Explicitly setting PPosition to any value causes some WMs, notably // Compiz and Metacity, to honor the position of unmapped windows if (!_glfwPlatformWindowVisible(window)) { long supplied; XSizeHints* hints = XAllocSizeHints(); if (XGetWMNormalHints(_glfw.x11.display, window->x11.handle, hints, &supplied)) { hints->flags |= PPosition; hints->x = hints->y = 0; XSetWMNormalHints(_glfw.x11.display, window->x11.handle, hints); } XFree(hints); } XMoveWindow(_glfw.x11.display, window->x11.handle, xpos, ypos); XFlush(_glfw.x11.display); } void _glfwPlatformGetWindowSize(_GLFWwindow* window, int* width, int* height) { XWindowAttributes attribs; XGetWindowAttributes(_glfw.x11.display, window->x11.handle, &attribs); if (width) *width = attribs.width; if (height) *height = attribs.height; } void _glfwPlatformSetWindowSize(_GLFWwindow* window, int width, int height) { if (window->monitor) { if (window->monitor->window == window) acquireMonitor(window); } else { if (!window->resizable) updateNormalHints(window, width, height); XResizeWindow(_glfw.x11.display, window->x11.handle, width, height); } XFlush(_glfw.x11.display); } void _glfwPlatformSetWindowSizeLimits(_GLFWwindow* window, int minwidth, int minheight, int maxwidth, int maxheight) { int width, height; _glfwPlatformGetWindowSize(window, &width, &height); updateNormalHints(window, width, height); XFlush(_glfw.x11.display); } void _glfwPlatformSetWindowAspectRatio(_GLFWwindow* window, int numer, int denom) { int width, height; _glfwPlatformGetWindowSize(window, &width, &height); updateNormalHints(window, width, height); XFlush(_glfw.x11.display); } void _glfwPlatformGetFramebufferSize(_GLFWwindow* window, int* width, int* height) { _glfwPlatformGetWindowSize(window, width, height); } void _glfwPlatformGetWindowFrameSize(_GLFWwindow* window, int* left, int* top, int* right, int* bottom) { long* extents = NULL; if (window->monitor || !window->decorated) return; if (_glfw.x11.NET_FRAME_EXTENTS == None) return; if (!_glfwPlatformWindowVisible(window) && _glfw.x11.NET_REQUEST_FRAME_EXTENTS) { XEvent event; double timeout = 0.5; // Ensure _NET_FRAME_EXTENTS is set, allowing glfwGetWindowFrameSize to // function before the window is mapped sendEventToWM(window, _glfw.x11.NET_REQUEST_FRAME_EXTENTS, 0, 0, 0, 0, 0); // HACK: Use a timeout because earlier versions of some window managers // (at least Unity, Fluxbox and Xfwm) failed to send the reply // They have been fixed but broken versions are still in the wild // If you are affected by this and your window manager is NOT // listed above, PLEASE report it to their and our issue trackers while (!XCheckIfEvent(_glfw.x11.display, &event, isFrameExtentsEvent, (XPointer) window)) { if (!waitForX11Event(&timeout)) { _glfwInputError(GLFW_PLATFORM_ERROR, "X11: The window manager has a broken _NET_REQUEST_FRAME_EXTENTS implementation; please report this issue"); return; } } } if (_glfwGetWindowPropertyX11(window->x11.handle, _glfw.x11.NET_FRAME_EXTENTS, XA_CARDINAL, (unsigned char**) &extents) == 4) { if (left) *left = extents[0]; if (top) *top = extents[2]; if (right) *right = extents[1]; if (bottom) *bottom = extents[3]; } if (extents) XFree(extents); } void _glfwPlatformGetWindowContentScale(_GLFWwindow* window, float* xscale, float* yscale) { if (xscale) *xscale = _glfw.x11.contentScaleX; if (yscale) *yscale = _glfw.x11.contentScaleY; } void _glfwPlatformIconifyWindow(_GLFWwindow* window) { if (window->x11.overrideRedirect) { // Override-redirect windows cannot be iconified or restored, as those // tasks are performed by the window manager _glfwInputError(GLFW_PLATFORM_ERROR, "X11: Iconification of full screen windows requires a WM that supports EWMH full screen"); return; } XIconifyWindow(_glfw.x11.display, window->x11.handle, _glfw.x11.screen); XFlush(_glfw.x11.display); } void _glfwPlatformRestoreWindow(_GLFWwindow* window) { if (window->x11.overrideRedirect) { // Override-redirect windows cannot be iconified or restored, as those // tasks are performed by the window manager _glfwInputError(GLFW_PLATFORM_ERROR, "X11: Iconification of full screen windows requires a WM that supports EWMH full screen"); return; } if (_glfwPlatformWindowIconified(window)) { XMapWindow(_glfw.x11.display, window->x11.handle); waitForVisibilityNotify(window); } else if (_glfwPlatformWindowVisible(window)) { if (_glfw.x11.NET_WM_STATE && _glfw.x11.NET_WM_STATE_MAXIMIZED_VERT && _glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ) { sendEventToWM(window, _glfw.x11.NET_WM_STATE, _NET_WM_STATE_REMOVE, _glfw.x11.NET_WM_STATE_MAXIMIZED_VERT, _glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ, 1, 0); } } XFlush(_glfw.x11.display); } void _glfwPlatformMaximizeWindow(_GLFWwindow* window) { if (!_glfw.x11.NET_WM_STATE || !_glfw.x11.NET_WM_STATE_MAXIMIZED_VERT || !_glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ) { return; } if (_glfwPlatformWindowVisible(window)) { sendEventToWM(window, _glfw.x11.NET_WM_STATE, _NET_WM_STATE_ADD, _glfw.x11.NET_WM_STATE_MAXIMIZED_VERT, _glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ, 1, 0); } else { Atom* states = NULL; unsigned long count = _glfwGetWindowPropertyX11(window->x11.handle, _glfw.x11.NET_WM_STATE, XA_ATOM, (unsigned char**) &states); // NOTE: We don't check for failure as this property may not exist yet // and that's fine (and we'll create it implicitly with append) Atom missing[2] = { _glfw.x11.NET_WM_STATE_MAXIMIZED_VERT, _glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ }; unsigned long missingCount = 2; for (unsigned long i = 0; i < count; i++) { for (unsigned long j = 0; j < missingCount; j++) { if (states[i] == missing[j]) { missing[j] = missing[missingCount - 1]; missingCount--; } } } if (states) XFree(states); if (!missingCount) return; XChangeProperty(_glfw.x11.display, window->x11.handle, _glfw.x11.NET_WM_STATE, XA_ATOM, 32, PropModeAppend, (unsigned char*) missing, missingCount); } XFlush(_glfw.x11.display); } void _glfwPlatformShowWindow(_GLFWwindow* window) { if (_glfwPlatformWindowVisible(window)) return; XMapWindow(_glfw.x11.display, window->x11.handle); waitForVisibilityNotify(window); } void _glfwPlatformHideWindow(_GLFWwindow* window) { XUnmapWindow(_glfw.x11.display, window->x11.handle); XFlush(_glfw.x11.display); } void _glfwPlatformRequestWindowAttention(_GLFWwindow* window) { if (!_glfw.x11.NET_WM_STATE || !_glfw.x11.NET_WM_STATE_DEMANDS_ATTENTION) return; sendEventToWM(window, _glfw.x11.NET_WM_STATE, _NET_WM_STATE_ADD, _glfw.x11.NET_WM_STATE_DEMANDS_ATTENTION, 0, 1, 0); } void _glfwPlatformFocusWindow(_GLFWwindow* window) { if (_glfw.x11.NET_ACTIVE_WINDOW) sendEventToWM(window, _glfw.x11.NET_ACTIVE_WINDOW, 1, 0, 0, 0, 0); else if (_glfwPlatformWindowVisible(window)) { XRaiseWindow(_glfw.x11.display, window->x11.handle); XSetInputFocus(_glfw.x11.display, window->x11.handle, RevertToParent, CurrentTime); } XFlush(_glfw.x11.display); } void _glfwPlatformSetWindowMonitor(_GLFWwindow* window, _GLFWmonitor* monitor, int xpos, int ypos, int width, int height, int refreshRate) { if (window->monitor == monitor) { if (monitor) { if (monitor->window == window) acquireMonitor(window); } else { if (!window->resizable) updateNormalHints(window, width, height); XMoveResizeWindow(_glfw.x11.display, window->x11.handle, xpos, ypos, width, height); } XFlush(_glfw.x11.display); return; } if (window->monitor) { _glfwPlatformSetWindowDecorated(window, window->decorated); _glfwPlatformSetWindowFloating(window, window->floating); releaseMonitor(window); } _glfwInputWindowMonitor(window, monitor); updateNormalHints(window, width, height); if (window->monitor) { if (!_glfwPlatformWindowVisible(window)) { XMapRaised(_glfw.x11.display, window->x11.handle); waitForVisibilityNotify(window); } updateWindowMode(window); acquireMonitor(window); } else { updateWindowMode(window); XMoveResizeWindow(_glfw.x11.display, window->x11.handle, xpos, ypos, width, height); } XFlush(_glfw.x11.display); } int _glfwPlatformWindowFocused(_GLFWwindow* window) { Window focused; int state; XGetInputFocus(_glfw.x11.display, &focused, &state); return window->x11.handle == focused; } int _glfwPlatformWindowIconified(_GLFWwindow* window) { return getWindowState(window) == IconicState; } int _glfwPlatformWindowVisible(_GLFWwindow* window) { XWindowAttributes wa; XGetWindowAttributes(_glfw.x11.display, window->x11.handle, &wa); return wa.map_state == IsViewable; } int _glfwPlatformWindowMaximized(_GLFWwindow* window) { Atom* states; unsigned long i; GLFWbool maximized = GLFW_FALSE; if (!_glfw.x11.NET_WM_STATE || !_glfw.x11.NET_WM_STATE_MAXIMIZED_VERT || !_glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ) { return maximized; } const unsigned long count = _glfwGetWindowPropertyX11(window->x11.handle, _glfw.x11.NET_WM_STATE, XA_ATOM, (unsigned char**) &states); for (i = 0; i < count; i++) { if (states[i] == _glfw.x11.NET_WM_STATE_MAXIMIZED_VERT || states[i] == _glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ) { maximized = GLFW_TRUE; break; } } if (states) XFree(states); return maximized; } int _glfwPlatformWindowHovered(_GLFWwindow* window) { Window w = _glfw.x11.root; while (w) { Window root; int rootX, rootY, childX, childY; unsigned int mask; _glfwGrabErrorHandlerX11(); const Bool result = XQueryPointer(_glfw.x11.display, w, &root, &w, &rootX, &rootY, &childX, &childY, &mask); _glfwReleaseErrorHandlerX11(); if (_glfw.x11.errorCode == BadWindow) w = _glfw.x11.root; else if (!result) return GLFW_FALSE; else if (w == window->x11.handle) return GLFW_TRUE; } return GLFW_FALSE; } int _glfwPlatformFramebufferTransparent(_GLFWwindow* window) { if (!window->x11.transparent) return GLFW_FALSE; return XGetSelectionOwner(_glfw.x11.display, _glfw.x11.NET_WM_CM_Sx) != None; } void _glfwPlatformSetWindowResizable(_GLFWwindow* window, GLFWbool enabled) { int width, height; _glfwPlatformGetWindowSize(window, &width, &height); updateNormalHints(window, width, height); } void _glfwPlatformSetWindowDecorated(_GLFWwindow* window, GLFWbool enabled) { struct { unsigned long flags; unsigned long functions; unsigned long decorations; long input_mode; unsigned long status; } hints = {0}; hints.flags = MWM_HINTS_DECORATIONS; hints.decorations = enabled ? MWM_DECOR_ALL : 0; XChangeProperty(_glfw.x11.display, window->x11.handle, _glfw.x11.MOTIF_WM_HINTS, _glfw.x11.MOTIF_WM_HINTS, 32, PropModeReplace, (unsigned char*) &hints, sizeof(hints) / sizeof(long)); } void _glfwPlatformSetWindowFloating(_GLFWwindow* window, GLFWbool enabled) { if (!_glfw.x11.NET_WM_STATE || !_glfw.x11.NET_WM_STATE_ABOVE) return; if (_glfwPlatformWindowVisible(window)) { const long action = enabled ? _NET_WM_STATE_ADD : _NET_WM_STATE_REMOVE; sendEventToWM(window, _glfw.x11.NET_WM_STATE, action, _glfw.x11.NET_WM_STATE_ABOVE, 0, 1, 0); } else { Atom* states = NULL; unsigned long i, count; count = _glfwGetWindowPropertyX11(window->x11.handle, _glfw.x11.NET_WM_STATE, XA_ATOM, (unsigned char**) &states); // NOTE: We don't check for failure as this property may not exist yet // and that's fine (and we'll create it implicitly with append) if (enabled) { for (i = 0; i < count; i++) { if (states[i] == _glfw.x11.NET_WM_STATE_ABOVE) break; } if (i == count) { XChangeProperty(_glfw.x11.display, window->x11.handle, _glfw.x11.NET_WM_STATE, XA_ATOM, 32, PropModeAppend, (unsigned char*) &_glfw.x11.NET_WM_STATE_ABOVE, 1); } } else if (states) { for (i = 0; i < count; i++) { if (states[i] == _glfw.x11.NET_WM_STATE_ABOVE) break; } if (i < count) { states[i] = states[count - 1]; count--; XChangeProperty(_glfw.x11.display, window->x11.handle, _glfw.x11.NET_WM_STATE, XA_ATOM, 32, PropModeReplace, (unsigned char*) states, count); } } if (states) XFree(states); } XFlush(_glfw.x11.display); } float _glfwPlatformGetWindowOpacity(_GLFWwindow* window) { float opacity = 1.f; if (XGetSelectionOwner(_glfw.x11.display, _glfw.x11.NET_WM_CM_Sx)) { CARD32* value = NULL; if (_glfwGetWindowPropertyX11(window->x11.handle, _glfw.x11.NET_WM_WINDOW_OPACITY, XA_CARDINAL, (unsigned char**) &value)) { opacity = (float) (*value / (double) 0xffffffffu); } if (value) XFree(value); } return opacity; } void _glfwPlatformSetWindowOpacity(_GLFWwindow* window, float opacity) { const CARD32 value = (CARD32) (0xffffffffu * (double) opacity); XChangeProperty(_glfw.x11.display, window->x11.handle, _glfw.x11.NET_WM_WINDOW_OPACITY, XA_CARDINAL, 32, PropModeReplace, (unsigned char*) &value, 1); } void _glfwPlatformSetRawMouseMotion(_GLFWwindow *window, GLFWbool enabled) { if (!_glfw.x11.xi.available) return; if (_glfw.x11.disabledCursorWindow != window) return; if (enabled) enableRawMouseMotion(window); else disableRawMouseMotion(window); } GLFWbool _glfwPlatformRawMouseMotionSupported(void) { return _glfw.x11.xi.available; } void _glfwPlatformPollEvents(void) { drainEmptyEvents(); #if defined(__linux__) _glfwDetectJoystickConnectionLinux(); #endif XPending(_glfw.x11.display); while (XQLength(_glfw.x11.display)) { XEvent event; XNextEvent(_glfw.x11.display, &event); processEvent(&event); } _GLFWwindow* window = _glfw.x11.disabledCursorWindow; if (window) { int width, height; _glfwPlatformGetWindowSize(window, &width, &height); // NOTE: Re-center the cursor only if it has moved since the last call, // to avoid breaking glfwWaitEvents with MotionNotify if (window->x11.lastCursorPosX != width / 2 || window->x11.lastCursorPosY != height / 2) { _glfwPlatformSetCursorPos(window, width / 2, height / 2); } } XFlush(_glfw.x11.display); } void _glfwPlatformWaitEvents(void) { waitForAnyEvent(NULL); _glfwPlatformPollEvents(); } void _glfwPlatformWaitEventsTimeout(double timeout) { waitForAnyEvent(&timeout); _glfwPlatformPollEvents(); } void _glfwPlatformPostEmptyEvent(void) { writeEmptyEvent(); } void _glfwPlatformGetCursorPos(_GLFWwindow* window, double* xpos, double* ypos) { Window root, child; int rootX, rootY, childX, childY; unsigned int mask; XQueryPointer(_glfw.x11.display, window->x11.handle, &root, &child, &rootX, &rootY, &childX, &childY, &mask); if (xpos) *xpos = childX; if (ypos) *ypos = childY; } void _glfwPlatformSetCursorPos(_GLFWwindow* window, double x, double y) { // Store the new position so it can be recognized later window->x11.warpCursorPosX = (int) x; window->x11.warpCursorPosY = (int) y; XWarpPointer(_glfw.x11.display, None, window->x11.handle, 0,0,0,0, (int) x, (int) y); XFlush(_glfw.x11.display); } void _glfwPlatformSetCursorMode(_GLFWwindow* window, int mode) { if (mode == GLFW_CURSOR_DISABLED) { if (_glfwPlatformWindowFocused(window)) disableCursor(window); } else if (_glfw.x11.disabledCursorWindow == window) enableCursor(window); else updateCursorImage(window); XFlush(_glfw.x11.display); } const char* _glfwPlatformGetScancodeName(int scancode) { if (!_glfw.x11.xkb.available) return NULL; if (scancode < 0 || scancode > 0xff || _glfw.x11.keycodes[scancode] == GLFW_KEY_UNKNOWN) { _glfwInputError(GLFW_INVALID_VALUE, "Invalid scancode %i", scancode); return NULL; } const int key = _glfw.x11.keycodes[scancode]; const KeySym keysym = XkbKeycodeToKeysym(_glfw.x11.display, scancode, _glfw.x11.xkb.group, 0); if (keysym == NoSymbol) return NULL; const uint32_t codepoint = _glfwKeySym2Unicode(keysym); if (codepoint == GLFW_INVALID_CODEPOINT) return NULL; const size_t count = _glfwEncodeUTF8(_glfw.x11.keynames[key], codepoint); if (count == 0) return NULL; _glfw.x11.keynames[key][count] = '\0'; return _glfw.x11.keynames[key]; } int _glfwPlatformGetKeyScancode(int key) { return _glfw.x11.scancodes[key]; } int _glfwPlatformCreateCursor(_GLFWcursor* cursor, const GLFWimage* image, int xhot, int yhot) { cursor->x11.handle = _glfwCreateCursorX11(image, xhot, yhot); if (!cursor->x11.handle) return GLFW_FALSE; return GLFW_TRUE; } int _glfwPlatformCreateStandardCursor(_GLFWcursor* cursor, int shape) { int native = 0; if (shape == GLFW_ARROW_CURSOR) native = XC_left_ptr; else if (shape == GLFW_IBEAM_CURSOR) native = XC_xterm; else if (shape == GLFW_CROSSHAIR_CURSOR) native = XC_crosshair; else if (shape == GLFW_HAND_CURSOR) native = XC_hand2; else if (shape == GLFW_HRESIZE_CURSOR) native = XC_sb_h_double_arrow; else if (shape == GLFW_VRESIZE_CURSOR) native = XC_sb_v_double_arrow; else return GLFW_FALSE; cursor->x11.handle = XCreateFontCursor(_glfw.x11.display, native); if (!cursor->x11.handle) { _glfwInputError(GLFW_PLATFORM_ERROR, "X11: Failed to create standard cursor"); return GLFW_FALSE; } return GLFW_TRUE; } void _glfwPlatformDestroyCursor(_GLFWcursor* cursor) { if (cursor->x11.handle) XFreeCursor(_glfw.x11.display, cursor->x11.handle); } void _glfwPlatformSetCursor(_GLFWwindow* window, _GLFWcursor* cursor) { if (window->cursorMode == GLFW_CURSOR_NORMAL) { updateCursorImage(window); XFlush(_glfw.x11.display); } } void _glfwPlatformSetClipboardString(const char* string) { char* copy = _glfw_strdup(string); free(_glfw.x11.clipboardString); _glfw.x11.clipboardString = copy; XSetSelectionOwner(_glfw.x11.display, _glfw.x11.CLIPBOARD, _glfw.x11.helperWindowHandle, CurrentTime); if (XGetSelectionOwner(_glfw.x11.display, _glfw.x11.CLIPBOARD) != _glfw.x11.helperWindowHandle) { _glfwInputError(GLFW_PLATFORM_ERROR, "X11: Failed to become owner of clipboard selection"); } } const char* _glfwPlatformGetClipboardString(void) { return getSelectionString(_glfw.x11.CLIPBOARD); } void _glfwPlatformGetRequiredInstanceExtensions(char** extensions) { if (!_glfw.vk.KHR_surface) return; if (!_glfw.vk.KHR_xcb_surface || !_glfw.x11.x11xcb.handle) { if (!_glfw.vk.KHR_xlib_surface) return; } extensions[0] = "VK_KHR_surface"; // NOTE: VK_KHR_xcb_surface is preferred due to some early ICDs exposing but // not correctly implementing VK_KHR_xlib_surface if (_glfw.vk.KHR_xcb_surface && _glfw.x11.x11xcb.handle) extensions[1] = "VK_KHR_xcb_surface"; else extensions[1] = "VK_KHR_xlib_surface"; } int _glfwPlatformGetPhysicalDevicePresentationSupport(VkInstance instance, VkPhysicalDevice device, uint32_t queuefamily) { VisualID visualID = XVisualIDFromVisual(DefaultVisual(_glfw.x11.display, _glfw.x11.screen)); if (_glfw.vk.KHR_xcb_surface && _glfw.x11.x11xcb.handle) { PFN_vkGetPhysicalDeviceXcbPresentationSupportKHR vkGetPhysicalDeviceXcbPresentationSupportKHR = (PFN_vkGetPhysicalDeviceXcbPresentationSupportKHR) vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceXcbPresentationSupportKHR"); if (!vkGetPhysicalDeviceXcbPresentationSupportKHR) { _glfwInputError(GLFW_API_UNAVAILABLE, "X11: Vulkan instance missing VK_KHR_xcb_surface extension"); return GLFW_FALSE; } xcb_connection_t* connection = XGetXCBConnection(_glfw.x11.display); if (!connection) { _glfwInputError(GLFW_PLATFORM_ERROR, "X11: Failed to retrieve XCB connection"); return GLFW_FALSE; } return vkGetPhysicalDeviceXcbPresentationSupportKHR(device, queuefamily, connection, visualID); } else { PFN_vkGetPhysicalDeviceXlibPresentationSupportKHR vkGetPhysicalDeviceXlibPresentationSupportKHR = (PFN_vkGetPhysicalDeviceXlibPresentationSupportKHR) vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceXlibPresentationSupportKHR"); if (!vkGetPhysicalDeviceXlibPresentationSupportKHR) { _glfwInputError(GLFW_API_UNAVAILABLE, "X11: Vulkan instance missing VK_KHR_xlib_surface extension"); return GLFW_FALSE; } return vkGetPhysicalDeviceXlibPresentationSupportKHR(device, queuefamily, _glfw.x11.display, visualID); } } VkResult _glfwPlatformCreateWindowSurface(VkInstance instance, _GLFWwindow* window, const VkAllocationCallbacks* allocator, VkSurfaceKHR* surface) { if (_glfw.vk.KHR_xcb_surface && _glfw.x11.x11xcb.handle) { VkResult err; VkXcbSurfaceCreateInfoKHR sci; PFN_vkCreateXcbSurfaceKHR vkCreateXcbSurfaceKHR; xcb_connection_t* connection = XGetXCBConnection(_glfw.x11.display); if (!connection) { _glfwInputError(GLFW_PLATFORM_ERROR, "X11: Failed to retrieve XCB connection"); return VK_ERROR_EXTENSION_NOT_PRESENT; } vkCreateXcbSurfaceKHR = (PFN_vkCreateXcbSurfaceKHR) vkGetInstanceProcAddr(instance, "vkCreateXcbSurfaceKHR"); if (!vkCreateXcbSurfaceKHR) { _glfwInputError(GLFW_API_UNAVAILABLE, "X11: Vulkan instance missing VK_KHR_xcb_surface extension"); return VK_ERROR_EXTENSION_NOT_PRESENT; } memset(&sci, 0, sizeof(sci)); sci.sType = VK_STRUCTURE_TYPE_XCB_SURFACE_CREATE_INFO_KHR; sci.connection = connection; sci.window = window->x11.handle; err = vkCreateXcbSurfaceKHR(instance, &sci, allocator, surface); if (err) { _glfwInputError(GLFW_PLATFORM_ERROR, "X11: Failed to create Vulkan XCB surface: %s", _glfwGetVulkanResultString(err)); } return err; } else { VkResult err; VkXlibSurfaceCreateInfoKHR sci; PFN_vkCreateXlibSurfaceKHR vkCreateXlibSurfaceKHR; vkCreateXlibSurfaceKHR = (PFN_vkCreateXlibSurfaceKHR) vkGetInstanceProcAddr(instance, "vkCreateXlibSurfaceKHR"); if (!vkCreateXlibSurfaceKHR) { _glfwInputError(GLFW_API_UNAVAILABLE, "X11: Vulkan instance missing VK_KHR_xlib_surface extension"); return VK_ERROR_EXTENSION_NOT_PRESENT; } memset(&sci, 0, sizeof(sci)); sci.sType = VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR; sci.dpy = _glfw.x11.display; sci.window = window->x11.handle; err = vkCreateXlibSurfaceKHR(instance, &sci, allocator, surface); if (err) { _glfwInputError(GLFW_PLATFORM_ERROR, "X11: Failed to create Vulkan X11 surface: %s", _glfwGetVulkanResultString(err)); } return err; } } ////////////////////////////////////////////////////////////////////////// ////// GLFW native API ////// ////////////////////////////////////////////////////////////////////////// GLFWAPI Display* glfwGetX11Display(void) { _GLFW_REQUIRE_INIT_OR_RETURN(NULL); return _glfw.x11.display; } GLFWAPI Window glfwGetX11Window(GLFWwindow* handle) { _GLFWwindow* window = (_GLFWwindow*) handle; _GLFW_REQUIRE_INIT_OR_RETURN(None); return window->x11.handle; } GLFWAPI void glfwSetX11SelectionString(const char* string) { _GLFW_REQUIRE_INIT(); free(_glfw.x11.primarySelectionString); _glfw.x11.primarySelectionString = _glfw_strdup(string); XSetSelectionOwner(_glfw.x11.display, _glfw.x11.PRIMARY, _glfw.x11.helperWindowHandle, CurrentTime); if (XGetSelectionOwner(_glfw.x11.display, _glfw.x11.PRIMARY) != _glfw.x11.helperWindowHandle) { _glfwInputError(GLFW_PLATFORM_ERROR, "X11: Failed to become owner of primary selection"); } } GLFWAPI const char* glfwGetX11SelectionString(void) { _GLFW_REQUIRE_INIT_OR_RETURN(NULL); return getSelectionString(_glfw.x11.PRIMARY); } #endif #ifndef HEADER_GUARD_GLX_CONTEXT_C #define HEADER_GUARD_GLX_CONTEXT_C //======================================================================== // GLFW 3.3.7 GLX - www.glfw.org //------------------------------------------------------------------------ // Copyright (c) 2002-2006 Marcus Geelnard // Copyright (c) 2006-2019 Camilla Löwy // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would // be appreciated but is not required. // // 2. Altered source versions must be plainly marked as such, and must not // be misrepresented as being the original software. // // 3. This notice may not be removed or altered from any source // distribution. // //======================================================================== // It is fine to use C99 in this file because it will not be built with VS //======================================================================== #include #include #include #ifndef GLXBadProfileARB #define GLXBadProfileARB 13 #endif // Returns the specified attribute of the specified GLXFBConfig // static int getGLXFBConfigAttrib(GLXFBConfig fbconfig, int attrib) { int value; glXGetFBConfigAttrib(_glfw.x11.display, fbconfig, attrib, &value); return value; } // Return the GLXFBConfig most closely matching the specified hints // static GLFWbool chooseGLXFBConfig(const _GLFWfbconfig* desired, GLXFBConfig* result) { GLXFBConfig* nativeConfigs; _GLFWfbconfig* usableConfigs; const _GLFWfbconfig* closest; int i, nativeCount, usableCount; const char* vendor; GLFWbool trustWindowBit = GLFW_TRUE; // HACK: This is a (hopefully temporary) workaround for Chromium // (VirtualBox GL) not setting the window bit on any GLXFBConfigs vendor = glXGetClientString(_glfw.x11.display, GLX_VENDOR); if (vendor && strcmp(vendor, "Chromium") == 0) trustWindowBit = GLFW_FALSE; nativeConfigs = glXGetFBConfigs(_glfw.x11.display, _glfw.x11.screen, &nativeCount); if (!nativeConfigs || !nativeCount) { _glfwInputError(GLFW_API_UNAVAILABLE, "GLX: No GLXFBConfigs returned"); return GLFW_FALSE; } usableConfigs = calloc(nativeCount, sizeof(_GLFWfbconfig)); usableCount = 0; for (i = 0; i < nativeCount; i++) { const GLXFBConfig n = nativeConfigs[i]; _GLFWfbconfig* u = usableConfigs + usableCount; // Only consider RGBA GLXFBConfigs if (!(getGLXFBConfigAttrib(n, GLX_RENDER_TYPE) & GLX_RGBA_BIT)) continue; // Only consider window GLXFBConfigs if (!(getGLXFBConfigAttrib(n, GLX_DRAWABLE_TYPE) & GLX_WINDOW_BIT)) { if (trustWindowBit) continue; } if (getGLXFBConfigAttrib(n, GLX_DOUBLEBUFFER) != desired->doublebuffer) continue; if (desired->transparent) { XVisualInfo* vi = glXGetVisualFromFBConfig(_glfw.x11.display, n); if (vi) { u->transparent = _glfwIsVisualTransparentX11(vi->visual); XFree(vi); } } u->redBits = getGLXFBConfigAttrib(n, GLX_RED_SIZE); u->greenBits = getGLXFBConfigAttrib(n, GLX_GREEN_SIZE); u->blueBits = getGLXFBConfigAttrib(n, GLX_BLUE_SIZE); u->alphaBits = getGLXFBConfigAttrib(n, GLX_ALPHA_SIZE); u->depthBits = getGLXFBConfigAttrib(n, GLX_DEPTH_SIZE); u->stencilBits = getGLXFBConfigAttrib(n, GLX_STENCIL_SIZE); u->accumRedBits = getGLXFBConfigAttrib(n, GLX_ACCUM_RED_SIZE); u->accumGreenBits = getGLXFBConfigAttrib(n, GLX_ACCUM_GREEN_SIZE); u->accumBlueBits = getGLXFBConfigAttrib(n, GLX_ACCUM_BLUE_SIZE); u->accumAlphaBits = getGLXFBConfigAttrib(n, GLX_ACCUM_ALPHA_SIZE); u->auxBuffers = getGLXFBConfigAttrib(n, GLX_AUX_BUFFERS); if (getGLXFBConfigAttrib(n, GLX_STEREO)) u->stereo = GLFW_TRUE; if (_glfw.glx.ARB_multisample) u->samples = getGLXFBConfigAttrib(n, GLX_SAMPLES); if (_glfw.glx.ARB_framebuffer_sRGB || _glfw.glx.EXT_framebuffer_sRGB) u->sRGB = getGLXFBConfigAttrib(n, GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB); u->handle = (uintptr_t) n; usableCount++; } closest = _glfwChooseFBConfig(desired, usableConfigs, usableCount); if (closest) *result = (GLXFBConfig) closest->handle; XFree(nativeConfigs); free(usableConfigs); return closest != NULL; } // Create the OpenGL context using legacy API // static GLXContext createLegacyContextGLX(_GLFWwindow* window, GLXFBConfig fbconfig, GLXContext share) { return glXCreateNewContext(_glfw.x11.display, fbconfig, GLX_RGBA_TYPE, share, True); } static void makeContextCurrentGLX(_GLFWwindow* window) { if (window) { if (!glXMakeCurrent(_glfw.x11.display, window->context.glx.window, window->context.glx.handle)) { _glfwInputError(GLFW_PLATFORM_ERROR, "GLX: Failed to make context current"); return; } } else { if (!glXMakeCurrent(_glfw.x11.display, None, NULL)) { _glfwInputError(GLFW_PLATFORM_ERROR, "GLX: Failed to clear current context"); return; } } _glfwPlatformSetTls(&_glfw.contextSlot, window); } static void swapBuffersGLX(_GLFWwindow* window) { glXSwapBuffers(_glfw.x11.display, window->context.glx.window); } static void swapIntervalGLX(int interval) { _GLFWwindow* window = _glfwPlatformGetTls(&_glfw.contextSlot); if (_glfw.glx.EXT_swap_control) { _glfw.glx.SwapIntervalEXT(_glfw.x11.display, window->context.glx.window, interval); } else if (_glfw.glx.MESA_swap_control) _glfw.glx.SwapIntervalMESA(interval); else if (_glfw.glx.SGI_swap_control) { if (interval > 0) _glfw.glx.SwapIntervalSGI(interval); } } static int extensionSupportedGLX(const char* extension) { const char* extensions = glXQueryExtensionsString(_glfw.x11.display, _glfw.x11.screen); if (extensions) { if (_glfwStringInExtensionString(extension, extensions)) return GLFW_TRUE; } return GLFW_FALSE; } static GLFWglproc getProcAddressGLX(const char* procname) { if (_glfw.glx.GetProcAddress) return _glfw.glx.GetProcAddress((const GLubyte*) procname); else if (_glfw.glx.GetProcAddressARB) return _glfw.glx.GetProcAddressARB((const GLubyte*) procname); else return _glfw_dlsym(_glfw.glx.handle, procname); } static void destroyContextGLX(_GLFWwindow* window) { if (window->context.glx.window) { glXDestroyWindow(_glfw.x11.display, window->context.glx.window); window->context.glx.window = None; } if (window->context.glx.handle) { glXDestroyContext(_glfw.x11.display, window->context.glx.handle); window->context.glx.handle = NULL; } } ////////////////////////////////////////////////////////////////////////// ////// GLFW internal API ////// ////////////////////////////////////////////////////////////////////////// // Initialize GLX // GLFWbool _glfwInitGLX(void) { int i; const char* sonames[] = { #if defined(_GLFW_GLX_LIBRARY) _GLFW_GLX_LIBRARY, #elif defined(__CYGWIN__) "libGL-1.so", #elif defined(__OpenBSD__) || defined(__NetBSD__) "libGL.so", #else "libGL.so.1", "libGL.so", #endif NULL }; if (_glfw.glx.handle) return GLFW_TRUE; for (i = 0; sonames[i]; i++) { _glfw.glx.handle = _glfw_dlopen(sonames[i]); if (_glfw.glx.handle) break; } if (!_glfw.glx.handle) { _glfwInputError(GLFW_API_UNAVAILABLE, "GLX: Failed to load GLX"); return GLFW_FALSE; } _glfw.glx.GetFBConfigs = _glfw_dlsym(_glfw.glx.handle, "glXGetFBConfigs"); _glfw.glx.GetFBConfigAttrib = _glfw_dlsym(_glfw.glx.handle, "glXGetFBConfigAttrib"); _glfw.glx.GetClientString = _glfw_dlsym(_glfw.glx.handle, "glXGetClientString"); _glfw.glx.QueryExtension = _glfw_dlsym(_glfw.glx.handle, "glXQueryExtension"); _glfw.glx.QueryVersion = _glfw_dlsym(_glfw.glx.handle, "glXQueryVersion"); _glfw.glx.DestroyContext = _glfw_dlsym(_glfw.glx.handle, "glXDestroyContext"); _glfw.glx.MakeCurrent = _glfw_dlsym(_glfw.glx.handle, "glXMakeCurrent"); _glfw.glx.SwapBuffers = _glfw_dlsym(_glfw.glx.handle, "glXSwapBuffers"); _glfw.glx.QueryExtensionsString = _glfw_dlsym(_glfw.glx.handle, "glXQueryExtensionsString"); _glfw.glx.CreateNewContext = _glfw_dlsym(_glfw.glx.handle, "glXCreateNewContext"); _glfw.glx.CreateWindow = _glfw_dlsym(_glfw.glx.handle, "glXCreateWindow"); _glfw.glx.DestroyWindow = _glfw_dlsym(_glfw.glx.handle, "glXDestroyWindow"); _glfw.glx.GetVisualFromFBConfig = _glfw_dlsym(_glfw.glx.handle, "glXGetVisualFromFBConfig"); if (!_glfw.glx.GetFBConfigs || !_glfw.glx.GetFBConfigAttrib || !_glfw.glx.GetClientString || !_glfw.glx.QueryExtension || !_glfw.glx.QueryVersion || !_glfw.glx.DestroyContext || !_glfw.glx.MakeCurrent || !_glfw.glx.SwapBuffers || !_glfw.glx.QueryExtensionsString || !_glfw.glx.CreateNewContext || !_glfw.glx.CreateWindow || !_glfw.glx.DestroyWindow || !_glfw.glx.GetVisualFromFBConfig) { _glfwInputError(GLFW_PLATFORM_ERROR, "GLX: Failed to load required entry points"); return GLFW_FALSE; } // NOTE: Unlike GLX 1.3 entry points these are not required to be present _glfw.glx.GetProcAddress = (PFNGLXGETPROCADDRESSPROC) _glfw_dlsym(_glfw.glx.handle, "glXGetProcAddress"); _glfw.glx.GetProcAddressARB = (PFNGLXGETPROCADDRESSPROC) _glfw_dlsym(_glfw.glx.handle, "glXGetProcAddressARB"); if (!glXQueryExtension(_glfw.x11.display, &_glfw.glx.errorBase, &_glfw.glx.eventBase)) { _glfwInputError(GLFW_API_UNAVAILABLE, "GLX: GLX extension not found"); return GLFW_FALSE; } if (!glXQueryVersion(_glfw.x11.display, &_glfw.glx.major, &_glfw.glx.minor)) { _glfwInputError(GLFW_API_UNAVAILABLE, "GLX: Failed to query GLX version"); return GLFW_FALSE; } if (_glfw.glx.major == 1 && _glfw.glx.minor < 3) { _glfwInputError(GLFW_API_UNAVAILABLE, "GLX: GLX version 1.3 is required"); return GLFW_FALSE; } if (extensionSupportedGLX("GLX_EXT_swap_control")) { _glfw.glx.SwapIntervalEXT = (PFNGLXSWAPINTERVALEXTPROC) getProcAddressGLX("glXSwapIntervalEXT"); if (_glfw.glx.SwapIntervalEXT) _glfw.glx.EXT_swap_control = GLFW_TRUE; } if (extensionSupportedGLX("GLX_SGI_swap_control")) { _glfw.glx.SwapIntervalSGI = (PFNGLXSWAPINTERVALSGIPROC) getProcAddressGLX("glXSwapIntervalSGI"); if (_glfw.glx.SwapIntervalSGI) _glfw.glx.SGI_swap_control = GLFW_TRUE; } if (extensionSupportedGLX("GLX_MESA_swap_control")) { _glfw.glx.SwapIntervalMESA = (PFNGLXSWAPINTERVALMESAPROC) getProcAddressGLX("glXSwapIntervalMESA"); if (_glfw.glx.SwapIntervalMESA) _glfw.glx.MESA_swap_control = GLFW_TRUE; } if (extensionSupportedGLX("GLX_ARB_multisample")) _glfw.glx.ARB_multisample = GLFW_TRUE; if (extensionSupportedGLX("GLX_ARB_framebuffer_sRGB")) _glfw.glx.ARB_framebuffer_sRGB = GLFW_TRUE; if (extensionSupportedGLX("GLX_EXT_framebuffer_sRGB")) _glfw.glx.EXT_framebuffer_sRGB = GLFW_TRUE; if (extensionSupportedGLX("GLX_ARB_create_context")) { _glfw.glx.CreateContextAttribsARB = (PFNGLXCREATECONTEXTATTRIBSARBPROC) getProcAddressGLX("glXCreateContextAttribsARB"); if (_glfw.glx.CreateContextAttribsARB) _glfw.glx.ARB_create_context = GLFW_TRUE; } if (extensionSupportedGLX("GLX_ARB_create_context_robustness")) _glfw.glx.ARB_create_context_robustness = GLFW_TRUE; if (extensionSupportedGLX("GLX_ARB_create_context_profile")) _glfw.glx.ARB_create_context_profile = GLFW_TRUE; if (extensionSupportedGLX("GLX_EXT_create_context_es2_profile")) _glfw.glx.EXT_create_context_es2_profile = GLFW_TRUE; if (extensionSupportedGLX("GLX_ARB_create_context_no_error")) _glfw.glx.ARB_create_context_no_error = GLFW_TRUE; if (extensionSupportedGLX("GLX_ARB_context_flush_control")) _glfw.glx.ARB_context_flush_control = GLFW_TRUE; return GLFW_TRUE; } // Terminate GLX // void _glfwTerminateGLX(void) { // NOTE: This function must not call any X11 functions, as it is called // after XCloseDisplay (see _glfwPlatformTerminate for details) if (_glfw.glx.handle) { _glfw_dlclose(_glfw.glx.handle); _glfw.glx.handle = NULL; } } #define setAttrib(a, v) \ { \ assert(((size_t) index + 1) < sizeof(attribs) / sizeof(attribs[0])); \ attribs[index++] = a; \ attribs[index++] = v; \ } // Create the OpenGL or OpenGL ES context // GLFWbool _glfwCreateContextGLX(_GLFWwindow* window, const _GLFWctxconfig* ctxconfig, const _GLFWfbconfig* fbconfig) { int attribs[40]; GLXFBConfig native = NULL; GLXContext share = NULL; if (ctxconfig->share) share = ctxconfig->share->context.glx.handle; if (!chooseGLXFBConfig(fbconfig, &native)) { _glfwInputError(GLFW_FORMAT_UNAVAILABLE, "GLX: Failed to find a suitable GLXFBConfig"); return GLFW_FALSE; } if (ctxconfig->client == GLFW_OPENGL_ES_API) { if (!_glfw.glx.ARB_create_context || !_glfw.glx.ARB_create_context_profile || !_glfw.glx.EXT_create_context_es2_profile) { _glfwInputError(GLFW_API_UNAVAILABLE, "GLX: OpenGL ES requested but GLX_EXT_create_context_es2_profile is unavailable"); return GLFW_FALSE; } } if (ctxconfig->forward) { if (!_glfw.glx.ARB_create_context) { _glfwInputError(GLFW_VERSION_UNAVAILABLE, "GLX: Forward compatibility requested but GLX_ARB_create_context_profile is unavailable"); return GLFW_FALSE; } } if (ctxconfig->profile) { if (!_glfw.glx.ARB_create_context || !_glfw.glx.ARB_create_context_profile) { _glfwInputError(GLFW_VERSION_UNAVAILABLE, "GLX: An OpenGL profile requested but GLX_ARB_create_context_profile is unavailable"); return GLFW_FALSE; } } _glfwGrabErrorHandlerX11(); if (_glfw.glx.ARB_create_context) { int index = 0, mask = 0, flags = 0; if (ctxconfig->client == GLFW_OPENGL_API) { if (ctxconfig->forward) flags |= GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB; if (ctxconfig->profile == GLFW_OPENGL_CORE_PROFILE) mask |= GLX_CONTEXT_CORE_PROFILE_BIT_ARB; else if (ctxconfig->profile == GLFW_OPENGL_COMPAT_PROFILE) mask |= GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB; } else mask |= GLX_CONTEXT_ES2_PROFILE_BIT_EXT; if (ctxconfig->debug) flags |= GLX_CONTEXT_DEBUG_BIT_ARB; if (ctxconfig->robustness) { if (_glfw.glx.ARB_create_context_robustness) { if (ctxconfig->robustness == GLFW_NO_RESET_NOTIFICATION) { setAttrib(GLX_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB, GLX_NO_RESET_NOTIFICATION_ARB); } else if (ctxconfig->robustness == GLFW_LOSE_CONTEXT_ON_RESET) { setAttrib(GLX_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB, GLX_LOSE_CONTEXT_ON_RESET_ARB); } flags |= GLX_CONTEXT_ROBUST_ACCESS_BIT_ARB; } } if (ctxconfig->release) { if (_glfw.glx.ARB_context_flush_control) { if (ctxconfig->release == GLFW_RELEASE_BEHAVIOR_NONE) { setAttrib(GLX_CONTEXT_RELEASE_BEHAVIOR_ARB, GLX_CONTEXT_RELEASE_BEHAVIOR_NONE_ARB); } else if (ctxconfig->release == GLFW_RELEASE_BEHAVIOR_FLUSH) { setAttrib(GLX_CONTEXT_RELEASE_BEHAVIOR_ARB, GLX_CONTEXT_RELEASE_BEHAVIOR_FLUSH_ARB); } } } if (ctxconfig->noerror) { if (_glfw.glx.ARB_create_context_no_error) setAttrib(GLX_CONTEXT_OPENGL_NO_ERROR_ARB, GLFW_TRUE); } // NOTE: Only request an explicitly versioned context when necessary, as // explicitly requesting version 1.0 does not always return the // highest version supported by the driver if (ctxconfig->major != 1 || ctxconfig->minor != 0) { setAttrib(GLX_CONTEXT_MAJOR_VERSION_ARB, ctxconfig->major); setAttrib(GLX_CONTEXT_MINOR_VERSION_ARB, ctxconfig->minor); } if (mask) setAttrib(GLX_CONTEXT_PROFILE_MASK_ARB, mask); if (flags) setAttrib(GLX_CONTEXT_FLAGS_ARB, flags); setAttrib(None, None); window->context.glx.handle = _glfw.glx.CreateContextAttribsARB(_glfw.x11.display, native, share, True, attribs); // HACK: This is a fallback for broken versions of the Mesa // implementation of GLX_ARB_create_context_profile that fail // default 1.0 context creation with a GLXBadProfileARB error in // violation of the extension spec if (!window->context.glx.handle) { if (_glfw.x11.errorCode == _glfw.glx.errorBase + GLXBadProfileARB && ctxconfig->client == GLFW_OPENGL_API && ctxconfig->profile == GLFW_OPENGL_ANY_PROFILE && ctxconfig->forward == GLFW_FALSE) { window->context.glx.handle = createLegacyContextGLX(window, native, share); } } } else { window->context.glx.handle = createLegacyContextGLX(window, native, share); } _glfwReleaseErrorHandlerX11(); if (!window->context.glx.handle) { _glfwInputErrorX11(GLFW_VERSION_UNAVAILABLE, "GLX: Failed to create context"); return GLFW_FALSE; } window->context.glx.window = glXCreateWindow(_glfw.x11.display, native, window->x11.handle, NULL); if (!window->context.glx.window) { _glfwInputError(GLFW_PLATFORM_ERROR, "GLX: Failed to create window"); return GLFW_FALSE; } window->context.makeCurrent = makeContextCurrentGLX; window->context.swapBuffers = swapBuffersGLX; window->context.swapInterval = swapIntervalGLX; window->context.extensionSupported = extensionSupportedGLX; window->context.getProcAddress = getProcAddressGLX; window->context.destroy = destroyContextGLX; return GLFW_TRUE; } #undef setAttrib // Returns the Visual and depth of the chosen GLXFBConfig // GLFWbool _glfwChooseVisualGLX(const _GLFWwndconfig* wndconfig, const _GLFWctxconfig* ctxconfig, const _GLFWfbconfig* fbconfig, Visual** visual, int* depth) { GLXFBConfig native; XVisualInfo* result; if (!chooseGLXFBConfig(fbconfig, &native)) { _glfwInputError(GLFW_FORMAT_UNAVAILABLE, "GLX: Failed to find a suitable GLXFBConfig"); return GLFW_FALSE; } result = glXGetVisualFromFBConfig(_glfw.x11.display, native); if (!result) { _glfwInputError(GLFW_PLATFORM_ERROR, "GLX: Failed to retrieve Visual for GLXFBConfig"); return GLFW_FALSE; } *visual = result->visual; *depth = result->depth; XFree(result); return GLFW_TRUE; } ////////////////////////////////////////////////////////////////////////// ////// GLFW native API ////// ////////////////////////////////////////////////////////////////////////// GLFWAPI GLXContext glfwGetGLXContext(GLFWwindow* handle) { _GLFWwindow* window = (_GLFWwindow*) handle; _GLFW_REQUIRE_INIT_OR_RETURN(NULL); if (window->context.source != GLFW_NATIVE_CONTEXT_API) { _glfwInputError(GLFW_NO_WINDOW_CONTEXT, NULL); return NULL; } return window->context.glx.handle; } GLFWAPI GLXWindow glfwGetGLXWindow(GLFWwindow* handle) { _GLFWwindow* window = (_GLFWwindow*) handle; _GLFW_REQUIRE_INIT_OR_RETURN(None); if (window->context.source != GLFW_NATIVE_CONTEXT_API) { _glfwInputError(GLFW_NO_WINDOW_CONTEXT, NULL); return None; } return window->context.glx.window; } #endif #endif #ifdef _GLFW_WAYLAND #ifndef HEADER_GUARD_WL_INIT_C #define HEADER_GUARD_WL_INIT_C //======================================================================== // GLFW 3.3.7 Wayland - www.glfw.org //------------------------------------------------------------------------ // Copyright (c) 2014 Jonas Ã…dahl // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would // be appreciated but is not required. // // 2. Altered source versions must be plainly marked as such, and must not // be misrepresented as being the original software. // // 3. This notice may not be removed or altered from any source // distribution. // //======================================================================== // It is fine to use C99 in this file because it will not be built with VS //======================================================================== #ifndef _POSIX_C_SOURCE //< @r-lyeh: add guard #define _POSIX_C_SOURCE 200809L #endif #include #include #include #include #include #include #include #include #include #include #include static inline int min(int n1, int n2) { return n1 < n2 ? n1 : n2; } static _GLFWwindow* findWindowFromDecorationSurface(struct wl_surface* surface, int* which) { int focus; _GLFWwindow* window = _glfw.windowListHead; if (!which) which = &focus; while (window) { if (surface == window->wl.decorations.top.surface) { *which = topDecoration; break; } if (surface == window->wl.decorations.left.surface) { *which = leftDecoration; break; } if (surface == window->wl.decorations.right.surface) { *which = rightDecoration; break; } if (surface == window->wl.decorations.bottom.surface) { *which = bottomDecoration; break; } window = window->next; } return window; } static void pointerHandleEnter(void* data, struct wl_pointer* pointer, uint32_t serial, struct wl_surface* surface, wl_fixed_t sx, wl_fixed_t sy) { // Happens in the case we just destroyed the surface. if (!surface) return; int focus = 0; _GLFWwindow* window = wl_surface_get_user_data(surface); if (!window) { window = findWindowFromDecorationSurface(surface, &focus); if (!window) return; } window->wl.decorations.focus = focus; _glfw.wl.serial = serial; _glfw.wl.pointerEnterSerial = serial; _glfw.wl.pointerFocus = window; window->wl.hovered = GLFW_TRUE; _glfwPlatformSetCursor(window, window->wl.currentCursor); _glfwInputCursorEnter(window, GLFW_TRUE); } static void pointerHandleLeave(void* data, struct wl_pointer* pointer, uint32_t serial, struct wl_surface* surface) { _GLFWwindow* window = _glfw.wl.pointerFocus; if (!window) return; window->wl.hovered = GLFW_FALSE; _glfw.wl.serial = serial; _glfw.wl.pointerFocus = NULL; _glfwInputCursorEnter(window, GLFW_FALSE); _glfw.wl.cursorPreviousName = NULL; } static void setCursor(_GLFWwindow* window, const char* name) { struct wl_buffer* buffer; struct wl_cursor* cursor; struct wl_cursor_image* image; struct wl_surface* surface = _glfw.wl.cursorSurface; struct wl_cursor_theme* theme = _glfw.wl.cursorTheme; int scale = 1; if (window->wl.scale > 1 && _glfw.wl.cursorThemeHiDPI) { // We only support up to scale=2 for now, since libwayland-cursor // requires us to load a different theme for each size. scale = 2; theme = _glfw.wl.cursorThemeHiDPI; } cursor = wl_cursor_theme_get_cursor(theme, name); if (!cursor) { _glfwInputError(GLFW_PLATFORM_ERROR, "Wayland: Standard cursor not found"); return; } // TODO: handle animated cursors too. image = cursor->images[0]; if (!image) return; buffer = wl_cursor_image_get_buffer(image); if (!buffer) return; wl_pointer_set_cursor(_glfw.wl.pointer, _glfw.wl.pointerEnterSerial, surface, image->hotspot_x / scale, image->hotspot_y / scale); wl_surface_set_buffer_scale(surface, scale); wl_surface_attach(surface, buffer, 0, 0); wl_surface_damage(surface, 0, 0, image->width, image->height); wl_surface_commit(surface); _glfw.wl.cursorPreviousName = name; } static void pointerHandleMotion(void* data, struct wl_pointer* pointer, uint32_t time, wl_fixed_t sx, wl_fixed_t sy) { _GLFWwindow* window = _glfw.wl.pointerFocus; const char* cursorName = NULL; double x, y; if (!window) return; if (window->cursorMode == GLFW_CURSOR_DISABLED) return; x = wl_fixed_to_double(sx); y = wl_fixed_to_double(sy); window->wl.cursorPosX = x; window->wl.cursorPosY = y; switch (window->wl.decorations.focus) { case mainWindow: _glfwInputCursorPos(window, x, y); _glfw.wl.cursorPreviousName = NULL; return; case topDecoration: if (y < _GLFW_DECORATION_WIDTH) cursorName = "n-resize"; else cursorName = "left_ptr"; break; case leftDecoration: if (y < _GLFW_DECORATION_WIDTH) cursorName = "nw-resize"; else cursorName = "w-resize"; break; case rightDecoration: if (y < _GLFW_DECORATION_WIDTH) cursorName = "ne-resize"; else cursorName = "e-resize"; break; case bottomDecoration: if (x < _GLFW_DECORATION_WIDTH) cursorName = "sw-resize"; else if (x > window->wl.width + _GLFW_DECORATION_WIDTH) cursorName = "se-resize"; else cursorName = "s-resize"; break; default: assert(0); } if (_glfw.wl.cursorPreviousName != cursorName) setCursor(window, cursorName); } static void pointerHandleButton(void* data, struct wl_pointer* pointer, uint32_t serial, uint32_t time, uint32_t button, uint32_t state) { _GLFWwindow* window = _glfw.wl.pointerFocus; int glfwButton; // Both xdg-shell and wl_shell use the same values. uint32_t edges = WL_SHELL_SURFACE_RESIZE_NONE; if (!window) return; if (button == BTN_LEFT) { switch (window->wl.decorations.focus) { case mainWindow: break; case topDecoration: if (window->wl.cursorPosY < _GLFW_DECORATION_WIDTH) edges = WL_SHELL_SURFACE_RESIZE_TOP; else { if (window->wl.xdg.toplevel) xdg_toplevel_move(window->wl.xdg.toplevel, _glfw.wl.seat, serial); else wl_shell_surface_move(window->wl.shellSurface, _glfw.wl.seat, serial); } break; case leftDecoration: if (window->wl.cursorPosY < _GLFW_DECORATION_WIDTH) edges = WL_SHELL_SURFACE_RESIZE_TOP_LEFT; else edges = WL_SHELL_SURFACE_RESIZE_LEFT; break; case rightDecoration: if (window->wl.cursorPosY < _GLFW_DECORATION_WIDTH) edges = WL_SHELL_SURFACE_RESIZE_TOP_RIGHT; else edges = WL_SHELL_SURFACE_RESIZE_RIGHT; break; case bottomDecoration: if (window->wl.cursorPosX < _GLFW_DECORATION_WIDTH) edges = WL_SHELL_SURFACE_RESIZE_BOTTOM_LEFT; else if (window->wl.cursorPosX > window->wl.width + _GLFW_DECORATION_WIDTH) edges = WL_SHELL_SURFACE_RESIZE_BOTTOM_RIGHT; else edges = WL_SHELL_SURFACE_RESIZE_BOTTOM; break; default: assert(0); } if (edges != WL_SHELL_SURFACE_RESIZE_NONE) { if (window->wl.xdg.toplevel) xdg_toplevel_resize(window->wl.xdg.toplevel, _glfw.wl.seat, serial, edges); else wl_shell_surface_resize(window->wl.shellSurface, _glfw.wl.seat, serial, edges); return; } } else if (button == BTN_RIGHT) { if (window->wl.decorations.focus != mainWindow && window->wl.xdg.toplevel) { xdg_toplevel_show_window_menu(window->wl.xdg.toplevel, _glfw.wl.seat, serial, window->wl.cursorPosX, window->wl.cursorPosY); return; } } // Don’t pass the button to the user if it was related to a decoration. if (window->wl.decorations.focus != mainWindow) return; _glfw.wl.serial = serial; /* Makes left, right and middle 0, 1 and 2. Overall order follows evdev * codes. */ glfwButton = button - BTN_LEFT; _glfwInputMouseClick(window, glfwButton, state == WL_POINTER_BUTTON_STATE_PRESSED ? GLFW_PRESS : GLFW_RELEASE, _glfw.wl.xkb.modifiers); } static void pointerHandleAxis(void* data, struct wl_pointer* pointer, uint32_t time, uint32_t axis, wl_fixed_t value) { _GLFWwindow* window = _glfw.wl.pointerFocus; double x = 0.0, y = 0.0; // Wayland scroll events are in pointer motion coordinate space (think two // finger scroll). The factor 10 is commonly used to convert to "scroll // step means 1.0. const double scrollFactor = 1.0 / 10.0; if (!window) return; assert(axis == WL_POINTER_AXIS_HORIZONTAL_SCROLL || axis == WL_POINTER_AXIS_VERTICAL_SCROLL); if (axis == WL_POINTER_AXIS_HORIZONTAL_SCROLL) x = -wl_fixed_to_double(value) * scrollFactor; else if (axis == WL_POINTER_AXIS_VERTICAL_SCROLL) y = -wl_fixed_to_double(value) * scrollFactor; _glfwInputScroll(window, x, y); } static const struct wl_pointer_listener pointerListener = { pointerHandleEnter, pointerHandleLeave, pointerHandleMotion, pointerHandleButton, pointerHandleAxis, }; static void keyboardHandleKeymap(void* data, struct wl_keyboard* keyboard, uint32_t format, int fd, uint32_t size) { struct xkb_keymap* keymap; struct xkb_state* state; struct xkb_compose_table* composeTable; struct xkb_compose_state* composeState; char* mapStr; const char* locale; if (format != WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1) { close(fd); return; } mapStr = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0); if (mapStr == MAP_FAILED) { close(fd); return; } keymap = xkb_keymap_new_from_string(_glfw.wl.xkb.context, mapStr, XKB_KEYMAP_FORMAT_TEXT_V1, 0); munmap(mapStr, size); close(fd); if (!keymap) { _glfwInputError(GLFW_PLATFORM_ERROR, "Wayland: Failed to compile keymap"); return; } state = xkb_state_new(keymap); if (!state) { _glfwInputError(GLFW_PLATFORM_ERROR, "Wayland: Failed to create XKB state"); xkb_keymap_unref(keymap); return; } // Look up the preferred locale, falling back to "C" as default. locale = getenv("LC_ALL"); if (!locale) locale = getenv("LC_CTYPE"); if (!locale) locale = getenv("LANG"); if (!locale) locale = "C"; composeTable = xkb_compose_table_new_from_locale(_glfw.wl.xkb.context, locale, XKB_COMPOSE_COMPILE_NO_FLAGS); if (composeTable) { composeState = xkb_compose_state_new(composeTable, XKB_COMPOSE_STATE_NO_FLAGS); xkb_compose_table_unref(composeTable); if (composeState) _glfw.wl.xkb.composeState = composeState; else _glfwInputError(GLFW_PLATFORM_ERROR, "Wayland: Failed to create XKB compose state"); } else { _glfwInputError(GLFW_PLATFORM_ERROR, "Wayland: Failed to create XKB compose table"); } xkb_keymap_unref(_glfw.wl.xkb.keymap); xkb_state_unref(_glfw.wl.xkb.state); _glfw.wl.xkb.keymap = keymap; _glfw.wl.xkb.state = state; _glfw.wl.xkb.controlMask = 1 << xkb_keymap_mod_get_index(_glfw.wl.xkb.keymap, "Control"); _glfw.wl.xkb.altMask = 1 << xkb_keymap_mod_get_index(_glfw.wl.xkb.keymap, "Mod1"); _glfw.wl.xkb.shiftMask = 1 << xkb_keymap_mod_get_index(_glfw.wl.xkb.keymap, "Shift"); _glfw.wl.xkb.superMask = 1 << xkb_keymap_mod_get_index(_glfw.wl.xkb.keymap, "Mod4"); _glfw.wl.xkb.capsLockMask = 1 << xkb_keymap_mod_get_index(_glfw.wl.xkb.keymap, "Lock"); _glfw.wl.xkb.numLockMask = 1 << xkb_keymap_mod_get_index(_glfw.wl.xkb.keymap, "Mod2"); } static void keyboardHandleEnter(void* data, struct wl_keyboard* keyboard, uint32_t serial, struct wl_surface* surface, struct wl_array* keys) { // Happens in the case we just destroyed the surface. if (!surface) return; _GLFWwindow* window = wl_surface_get_user_data(surface); if (!window) { window = findWindowFromDecorationSurface(surface, NULL); if (!window) return; } _glfw.wl.serial = serial; _glfw.wl.keyboardFocus = window; _glfwInputWindowFocus(window, GLFW_TRUE); } static void keyboardHandleLeave(void* data, struct wl_keyboard* keyboard, uint32_t serial, struct wl_surface* surface) { _GLFWwindow* window = _glfw.wl.keyboardFocus; if (!window) return; struct itimerspec timer = {}; timerfd_settime(_glfw.wl.timerfd, 0, &timer, NULL); _glfw.wl.serial = serial; _glfw.wl.keyboardFocus = NULL; _glfwInputWindowFocus(window, GLFW_FALSE); } static int translateKey(uint32_t scancode) { if (scancode < sizeof(_glfw.wl.keycodes) / sizeof(_glfw.wl.keycodes[0])) return _glfw.wl.keycodes[scancode]; return GLFW_KEY_UNKNOWN; } static xkb_keysym_t composeSymbol(xkb_keysym_t sym) { if (sym == XKB_KEY_NoSymbol || !_glfw.wl.xkb.composeState) return sym; if (xkb_compose_state_feed(_glfw.wl.xkb.composeState, sym) != XKB_COMPOSE_FEED_ACCEPTED) return sym; switch (xkb_compose_state_get_status(_glfw.wl.xkb.composeState)) { case XKB_COMPOSE_COMPOSED: return xkb_compose_state_get_one_sym(_glfw.wl.xkb.composeState); case XKB_COMPOSE_COMPOSING: case XKB_COMPOSE_CANCELLED: return XKB_KEY_NoSymbol; case XKB_COMPOSE_NOTHING: default: return sym; } } GLFWbool _glfwInputTextWayland(_GLFWwindow* window, uint32_t scancode) { const xkb_keysym_t* keysyms; const xkb_keycode_t keycode = scancode + 8; if (xkb_state_key_get_syms(_glfw.wl.xkb.state, keycode, &keysyms) == 1) { const xkb_keysym_t keysym = composeSymbol(keysyms[0]); const uint32_t codepoint = _glfwKeySym2Unicode(keysym); if (codepoint != GLFW_INVALID_CODEPOINT) { const int mods = _glfw.wl.xkb.modifiers; const int plain = !(mods & (GLFW_MOD_CONTROL | GLFW_MOD_ALT)); _glfwInputChar(window, codepoint, mods, plain); } } return xkb_keymap_key_repeats(_glfw.wl.xkb.keymap, keycode); } static void keyboardHandleKey(void* data, struct wl_keyboard* keyboard, uint32_t serial, uint32_t time, uint32_t scancode, uint32_t state) { _GLFWwindow* window = _glfw.wl.keyboardFocus; if (!window) return; const int key = translateKey(scancode); const int action = state == WL_KEYBOARD_KEY_STATE_PRESSED ? GLFW_PRESS : GLFW_RELEASE; _glfw.wl.serial = serial; _glfwInputKey(window, key, scancode, action, _glfw.wl.xkb.modifiers); struct itimerspec timer = {}; if (action == GLFW_PRESS) { const GLFWbool shouldRepeat = _glfwInputTextWayland(window, scancode); if (shouldRepeat && _glfw.wl.keyboardRepeatRate > 0) { _glfw.wl.keyboardLastKey = key; _glfw.wl.keyboardLastScancode = scancode; if (_glfw.wl.keyboardRepeatRate > 1) timer.it_interval.tv_nsec = 1000000000 / _glfw.wl.keyboardRepeatRate; else timer.it_interval.tv_sec = 1; timer.it_value.tv_sec = _glfw.wl.keyboardRepeatDelay / 1000; timer.it_value.tv_nsec = (_glfw.wl.keyboardRepeatDelay % 1000) * 1000000; } } timerfd_settime(_glfw.wl.timerfd, 0, &timer, NULL); } static void keyboardHandleModifiers(void* data, struct wl_keyboard* keyboard, uint32_t serial, uint32_t modsDepressed, uint32_t modsLatched, uint32_t modsLocked, uint32_t group) { _glfw.wl.serial = serial; if (!_glfw.wl.xkb.keymap) return; xkb_state_update_mask(_glfw.wl.xkb.state, modsDepressed, modsLatched, modsLocked, 0, 0, group); const xkb_mod_mask_t mask = xkb_state_serialize_mods(_glfw.wl.xkb.state, XKB_STATE_MODS_DEPRESSED | XKB_STATE_LAYOUT_DEPRESSED | XKB_STATE_MODS_LATCHED | XKB_STATE_LAYOUT_LATCHED); unsigned int mods = 0; if (mask & _glfw.wl.xkb.controlMask) mods |= GLFW_MOD_CONTROL; if (mask & _glfw.wl.xkb.altMask) mods |= GLFW_MOD_ALT; if (mask & _glfw.wl.xkb.shiftMask) mods |= GLFW_MOD_SHIFT; if (mask & _glfw.wl.xkb.superMask) mods |= GLFW_MOD_SUPER; if (mask & _glfw.wl.xkb.capsLockMask) mods |= GLFW_MOD_CAPS_LOCK; if (mask & _glfw.wl.xkb.numLockMask) mods |= GLFW_MOD_NUM_LOCK; _glfw.wl.xkb.modifiers = mods; } #ifdef WL_KEYBOARD_REPEAT_INFO_SINCE_VERSION static void keyboardHandleRepeatInfo(void* data, struct wl_keyboard* keyboard, int32_t rate, int32_t delay) { if (keyboard != _glfw.wl.keyboard) return; _glfw.wl.keyboardRepeatRate = rate; _glfw.wl.keyboardRepeatDelay = delay; } #endif static const struct wl_keyboard_listener keyboardListener = { keyboardHandleKeymap, keyboardHandleEnter, keyboardHandleLeave, keyboardHandleKey, keyboardHandleModifiers, #ifdef WL_KEYBOARD_REPEAT_INFO_SINCE_VERSION keyboardHandleRepeatInfo, #endif }; static void seatHandleCapabilities(void* data, struct wl_seat* seat, enum wl_seat_capability caps) { if ((caps & WL_SEAT_CAPABILITY_POINTER) && !_glfw.wl.pointer) { _glfw.wl.pointer = wl_seat_get_pointer(seat); wl_pointer_add_listener(_glfw.wl.pointer, &pointerListener, NULL); } else if (!(caps & WL_SEAT_CAPABILITY_POINTER) && _glfw.wl.pointer) { wl_pointer_destroy(_glfw.wl.pointer); _glfw.wl.pointer = NULL; } if ((caps & WL_SEAT_CAPABILITY_KEYBOARD) && !_glfw.wl.keyboard) { _glfw.wl.keyboard = wl_seat_get_keyboard(seat); wl_keyboard_add_listener(_glfw.wl.keyboard, &keyboardListener, NULL); } else if (!(caps & WL_SEAT_CAPABILITY_KEYBOARD) && _glfw.wl.keyboard) { wl_keyboard_destroy(_glfw.wl.keyboard); _glfw.wl.keyboard = NULL; } } static void seatHandleName(void* data, struct wl_seat* seat, const char* name) { } static const struct wl_seat_listener seatListener = { seatHandleCapabilities, seatHandleName, }; static void dataOfferHandleOffer(void* data, struct wl_data_offer* dataOffer, const char* mimeType) { } static const struct wl_data_offer_listener dataOfferListener = { dataOfferHandleOffer, }; static void dataDeviceHandleDataOffer(void* data, struct wl_data_device* dataDevice, struct wl_data_offer* id) { if (_glfw.wl.dataOffer) wl_data_offer_destroy(_glfw.wl.dataOffer); _glfw.wl.dataOffer = id; wl_data_offer_add_listener(_glfw.wl.dataOffer, &dataOfferListener, NULL); } static void dataDeviceHandleEnter(void* data, struct wl_data_device* dataDevice, uint32_t serial, struct wl_surface *surface, wl_fixed_t x, wl_fixed_t y, struct wl_data_offer *id) { } static void dataDeviceHandleLeave(void* data, struct wl_data_device* dataDevice) { } static void dataDeviceHandleMotion(void* data, struct wl_data_device* dataDevice, uint32_t time, wl_fixed_t x, wl_fixed_t y) { } static void dataDeviceHandleDrop(void* data, struct wl_data_device* dataDevice) { } static void dataDeviceHandleSelection(void* data, struct wl_data_device* dataDevice, struct wl_data_offer* id) { } static const struct wl_data_device_listener dataDeviceListener = { dataDeviceHandleDataOffer, dataDeviceHandleEnter, dataDeviceHandleLeave, dataDeviceHandleMotion, dataDeviceHandleDrop, dataDeviceHandleSelection, }; static void wmBaseHandlePing(void* data, struct xdg_wm_base* wmBase, uint32_t serial) { xdg_wm_base_pong(wmBase, serial); } static const struct xdg_wm_base_listener wmBaseListener = { wmBaseHandlePing }; static void registryHandleGlobal(void* data, struct wl_registry* registry, uint32_t name, const char* interface, uint32_t version) { if (strcmp(interface, "wl_compositor") == 0) { _glfw.wl.compositorVersion = min(3, version); _glfw.wl.compositor = wl_registry_bind(registry, name, &wl_compositor_interface, _glfw.wl.compositorVersion); } else if (strcmp(interface, "wl_subcompositor") == 0) { _glfw.wl.subcompositor = wl_registry_bind(registry, name, &wl_subcompositor_interface, 1); } else if (strcmp(interface, "wl_shm") == 0) { _glfw.wl.shm = wl_registry_bind(registry, name, &wl_shm_interface, 1); } else if (strcmp(interface, "wl_shell") == 0) { _glfw.wl.shell = wl_registry_bind(registry, name, &wl_shell_interface, 1); } else if (strcmp(interface, "wl_output") == 0) { _glfwAddOutputWayland(name, version); } else if (strcmp(interface, "wl_seat") == 0) { if (!_glfw.wl.seat) { _glfw.wl.seatVersion = min(4, version); _glfw.wl.seat = wl_registry_bind(registry, name, &wl_seat_interface, _glfw.wl.seatVersion); wl_seat_add_listener(_glfw.wl.seat, &seatListener, NULL); } } else if (strcmp(interface, "wl_data_device_manager") == 0) { if (!_glfw.wl.dataDeviceManager) { _glfw.wl.dataDeviceManager = wl_registry_bind(registry, name, &wl_data_device_manager_interface, 1); } } else if (strcmp(interface, "xdg_wm_base") == 0) { _glfw.wl.wmBase = wl_registry_bind(registry, name, &xdg_wm_base_interface, 1); xdg_wm_base_add_listener(_glfw.wl.wmBase, &wmBaseListener, NULL); } else if (strcmp(interface, "zxdg_decoration_manager_v1") == 0) { _glfw.wl.decorationManager = wl_registry_bind(registry, name, &zxdg_decoration_manager_v1_interface, 1); } else if (strcmp(interface, "wp_viewporter") == 0) { _glfw.wl.viewporter = wl_registry_bind(registry, name, &wp_viewporter_interface, 1); } else if (strcmp(interface, "zwp_relative_pointer_manager_v1") == 0) { _glfw.wl.relativePointerManager = wl_registry_bind(registry, name, &zwp_relative_pointer_manager_v1_interface, 1); } else if (strcmp(interface, "zwp_pointer_constraints_v1") == 0) { _glfw.wl.pointerConstraints = wl_registry_bind(registry, name, &zwp_pointer_constraints_v1_interface, 1); } else if (strcmp(interface, "zwp_idle_inhibit_manager_v1") == 0) { _glfw.wl.idleInhibitManager = wl_registry_bind(registry, name, &zwp_idle_inhibit_manager_v1_interface, 1); } } static void registryHandleGlobalRemove(void *data, struct wl_registry *registry, uint32_t name) { int i; _GLFWmonitor* monitor; for (i = 0; i < _glfw.monitorCount; ++i) { monitor = _glfw.monitors[i]; if (monitor->wl.name == name) { _glfwInputMonitor(monitor, GLFW_DISCONNECTED, 0); return; } } } static const struct wl_registry_listener registryListener = { registryHandleGlobal, registryHandleGlobalRemove }; // Create key code translation tables // static void createKeyTables(void) { int scancode; memset(_glfw.wl.keycodes, -1, sizeof(_glfw.wl.keycodes)); memset(_glfw.wl.scancodes, -1, sizeof(_glfw.wl.scancodes)); _glfw.wl.keycodes[KEY_GRAVE] = GLFW_KEY_GRAVE_ACCENT; _glfw.wl.keycodes[KEY_1] = GLFW_KEY_1; _glfw.wl.keycodes[KEY_2] = GLFW_KEY_2; _glfw.wl.keycodes[KEY_3] = GLFW_KEY_3; _glfw.wl.keycodes[KEY_4] = GLFW_KEY_4; _glfw.wl.keycodes[KEY_5] = GLFW_KEY_5; _glfw.wl.keycodes[KEY_6] = GLFW_KEY_6; _glfw.wl.keycodes[KEY_7] = GLFW_KEY_7; _glfw.wl.keycodes[KEY_8] = GLFW_KEY_8; _glfw.wl.keycodes[KEY_9] = GLFW_KEY_9; _glfw.wl.keycodes[KEY_0] = GLFW_KEY_0; _glfw.wl.keycodes[KEY_SPACE] = GLFW_KEY_SPACE; _glfw.wl.keycodes[KEY_MINUS] = GLFW_KEY_MINUS; _glfw.wl.keycodes[KEY_EQUAL] = GLFW_KEY_EQUAL; _glfw.wl.keycodes[KEY_Q] = GLFW_KEY_Q; _glfw.wl.keycodes[KEY_W] = GLFW_KEY_W; _glfw.wl.keycodes[KEY_E] = GLFW_KEY_E; _glfw.wl.keycodes[KEY_R] = GLFW_KEY_R; _glfw.wl.keycodes[KEY_T] = GLFW_KEY_T; _glfw.wl.keycodes[KEY_Y] = GLFW_KEY_Y; _glfw.wl.keycodes[KEY_U] = GLFW_KEY_U; _glfw.wl.keycodes[KEY_I] = GLFW_KEY_I; _glfw.wl.keycodes[KEY_O] = GLFW_KEY_O; _glfw.wl.keycodes[KEY_P] = GLFW_KEY_P; _glfw.wl.keycodes[KEY_LEFTBRACE] = GLFW_KEY_LEFT_BRACKET; _glfw.wl.keycodes[KEY_RIGHTBRACE] = GLFW_KEY_RIGHT_BRACKET; _glfw.wl.keycodes[KEY_A] = GLFW_KEY_A; _glfw.wl.keycodes[KEY_S] = GLFW_KEY_S; _glfw.wl.keycodes[KEY_D] = GLFW_KEY_D; _glfw.wl.keycodes[KEY_F] = GLFW_KEY_F; _glfw.wl.keycodes[KEY_G] = GLFW_KEY_G; _glfw.wl.keycodes[KEY_H] = GLFW_KEY_H; _glfw.wl.keycodes[KEY_J] = GLFW_KEY_J; _glfw.wl.keycodes[KEY_K] = GLFW_KEY_K; _glfw.wl.keycodes[KEY_L] = GLFW_KEY_L; _glfw.wl.keycodes[KEY_SEMICOLON] = GLFW_KEY_SEMICOLON; _glfw.wl.keycodes[KEY_APOSTROPHE] = GLFW_KEY_APOSTROPHE; _glfw.wl.keycodes[KEY_Z] = GLFW_KEY_Z; _glfw.wl.keycodes[KEY_X] = GLFW_KEY_X; _glfw.wl.keycodes[KEY_C] = GLFW_KEY_C; _glfw.wl.keycodes[KEY_V] = GLFW_KEY_V; _glfw.wl.keycodes[KEY_B] = GLFW_KEY_B; _glfw.wl.keycodes[KEY_N] = GLFW_KEY_N; _glfw.wl.keycodes[KEY_M] = GLFW_KEY_M; _glfw.wl.keycodes[KEY_COMMA] = GLFW_KEY_COMMA; _glfw.wl.keycodes[KEY_DOT] = GLFW_KEY_PERIOD; _glfw.wl.keycodes[KEY_SLASH] = GLFW_KEY_SLASH; _glfw.wl.keycodes[KEY_BACKSLASH] = GLFW_KEY_BACKSLASH; _glfw.wl.keycodes[KEY_ESC] = GLFW_KEY_ESCAPE; _glfw.wl.keycodes[KEY_TAB] = GLFW_KEY_TAB; _glfw.wl.keycodes[KEY_LEFTSHIFT] = GLFW_KEY_LEFT_SHIFT; _glfw.wl.keycodes[KEY_RIGHTSHIFT] = GLFW_KEY_RIGHT_SHIFT; _glfw.wl.keycodes[KEY_LEFTCTRL] = GLFW_KEY_LEFT_CONTROL; _glfw.wl.keycodes[KEY_RIGHTCTRL] = GLFW_KEY_RIGHT_CONTROL; _glfw.wl.keycodes[KEY_LEFTALT] = GLFW_KEY_LEFT_ALT; _glfw.wl.keycodes[KEY_RIGHTALT] = GLFW_KEY_RIGHT_ALT; _glfw.wl.keycodes[KEY_LEFTMETA] = GLFW_KEY_LEFT_SUPER; _glfw.wl.keycodes[KEY_RIGHTMETA] = GLFW_KEY_RIGHT_SUPER; _glfw.wl.keycodes[KEY_COMPOSE] = GLFW_KEY_MENU; _glfw.wl.keycodes[KEY_NUMLOCK] = GLFW_KEY_NUM_LOCK; _glfw.wl.keycodes[KEY_CAPSLOCK] = GLFW_KEY_CAPS_LOCK; _glfw.wl.keycodes[KEY_PRINT] = GLFW_KEY_PRINT_SCREEN; _glfw.wl.keycodes[KEY_SCROLLLOCK] = GLFW_KEY_SCROLL_LOCK; _glfw.wl.keycodes[KEY_PAUSE] = GLFW_KEY_PAUSE; _glfw.wl.keycodes[KEY_DELETE] = GLFW_KEY_DELETE; _glfw.wl.keycodes[KEY_BACKSPACE] = GLFW_KEY_BACKSPACE; _glfw.wl.keycodes[KEY_ENTER] = GLFW_KEY_ENTER; _glfw.wl.keycodes[KEY_HOME] = GLFW_KEY_HOME; _glfw.wl.keycodes[KEY_END] = GLFW_KEY_END; _glfw.wl.keycodes[KEY_PAGEUP] = GLFW_KEY_PAGE_UP; _glfw.wl.keycodes[KEY_PAGEDOWN] = GLFW_KEY_PAGE_DOWN; _glfw.wl.keycodes[KEY_INSERT] = GLFW_KEY_INSERT; _glfw.wl.keycodes[KEY_LEFT] = GLFW_KEY_LEFT; _glfw.wl.keycodes[KEY_RIGHT] = GLFW_KEY_RIGHT; _glfw.wl.keycodes[KEY_DOWN] = GLFW_KEY_DOWN; _glfw.wl.keycodes[KEY_UP] = GLFW_KEY_UP; _glfw.wl.keycodes[KEY_F1] = GLFW_KEY_F1; _glfw.wl.keycodes[KEY_F2] = GLFW_KEY_F2; _glfw.wl.keycodes[KEY_F3] = GLFW_KEY_F3; _glfw.wl.keycodes[KEY_F4] = GLFW_KEY_F4; _glfw.wl.keycodes[KEY_F5] = GLFW_KEY_F5; _glfw.wl.keycodes[KEY_F6] = GLFW_KEY_F6; _glfw.wl.keycodes[KEY_F7] = GLFW_KEY_F7; _glfw.wl.keycodes[KEY_F8] = GLFW_KEY_F8; _glfw.wl.keycodes[KEY_F9] = GLFW_KEY_F9; _glfw.wl.keycodes[KEY_F10] = GLFW_KEY_F10; _glfw.wl.keycodes[KEY_F11] = GLFW_KEY_F11; _glfw.wl.keycodes[KEY_F12] = GLFW_KEY_F12; _glfw.wl.keycodes[KEY_F13] = GLFW_KEY_F13; _glfw.wl.keycodes[KEY_F14] = GLFW_KEY_F14; _glfw.wl.keycodes[KEY_F15] = GLFW_KEY_F15; _glfw.wl.keycodes[KEY_F16] = GLFW_KEY_F16; _glfw.wl.keycodes[KEY_F17] = GLFW_KEY_F17; _glfw.wl.keycodes[KEY_F18] = GLFW_KEY_F18; _glfw.wl.keycodes[KEY_F19] = GLFW_KEY_F19; _glfw.wl.keycodes[KEY_F20] = GLFW_KEY_F20; _glfw.wl.keycodes[KEY_F21] = GLFW_KEY_F21; _glfw.wl.keycodes[KEY_F22] = GLFW_KEY_F22; _glfw.wl.keycodes[KEY_F23] = GLFW_KEY_F23; _glfw.wl.keycodes[KEY_F24] = GLFW_KEY_F24; _glfw.wl.keycodes[KEY_KPSLASH] = GLFW_KEY_KP_DIVIDE; _glfw.wl.keycodes[KEY_KPASTERISK] = GLFW_KEY_KP_MULTIPLY; _glfw.wl.keycodes[KEY_KPMINUS] = GLFW_KEY_KP_SUBTRACT; _glfw.wl.keycodes[KEY_KPPLUS] = GLFW_KEY_KP_ADD; _glfw.wl.keycodes[KEY_KP0] = GLFW_KEY_KP_0; _glfw.wl.keycodes[KEY_KP1] = GLFW_KEY_KP_1; _glfw.wl.keycodes[KEY_KP2] = GLFW_KEY_KP_2; _glfw.wl.keycodes[KEY_KP3] = GLFW_KEY_KP_3; _glfw.wl.keycodes[KEY_KP4] = GLFW_KEY_KP_4; _glfw.wl.keycodes[KEY_KP5] = GLFW_KEY_KP_5; _glfw.wl.keycodes[KEY_KP6] = GLFW_KEY_KP_6; _glfw.wl.keycodes[KEY_KP7] = GLFW_KEY_KP_7; _glfw.wl.keycodes[KEY_KP8] = GLFW_KEY_KP_8; _glfw.wl.keycodes[KEY_KP9] = GLFW_KEY_KP_9; _glfw.wl.keycodes[KEY_KPDOT] = GLFW_KEY_KP_DECIMAL; _glfw.wl.keycodes[KEY_KPEQUAL] = GLFW_KEY_KP_EQUAL; _glfw.wl.keycodes[KEY_KPENTER] = GLFW_KEY_KP_ENTER; _glfw.wl.keycodes[KEY_102ND] = GLFW_KEY_WORLD_2; for (scancode = 0; scancode < 256; scancode++) { if (_glfw.wl.keycodes[scancode] > 0) _glfw.wl.scancodes[_glfw.wl.keycodes[scancode]] = scancode; } } ////////////////////////////////////////////////////////////////////////// ////// GLFW platform API ////// ////////////////////////////////////////////////////////////////////////// int _glfwPlatformInit(void) { const char *cursorTheme; const char *cursorSizeStr; char *cursorSizeEnd; long cursorSizeLong; int cursorSize; _glfw.wl.cursor.handle = _glfw_dlopen("libwayland-cursor.so.0"); if (!_glfw.wl.cursor.handle) { _glfwInputError(GLFW_PLATFORM_ERROR, "Wayland: Failed to open libwayland-cursor"); return GLFW_FALSE; } _glfw.wl.cursor.theme_load = (PFN_wl_cursor_theme_load) _glfw_dlsym(_glfw.wl.cursor.handle, "wl_cursor_theme_load"); _glfw.wl.cursor.theme_destroy = (PFN_wl_cursor_theme_destroy) _glfw_dlsym(_glfw.wl.cursor.handle, "wl_cursor_theme_destroy"); _glfw.wl.cursor.theme_get_cursor = (PFN_wl_cursor_theme_get_cursor) _glfw_dlsym(_glfw.wl.cursor.handle, "wl_cursor_theme_get_cursor"); _glfw.wl.cursor.image_get_buffer = (PFN_wl_cursor_image_get_buffer) _glfw_dlsym(_glfw.wl.cursor.handle, "wl_cursor_image_get_buffer"); _glfw.wl.egl.handle = _glfw_dlopen("libwayland-egl.so.1"); if (!_glfw.wl.egl.handle) { _glfwInputError(GLFW_PLATFORM_ERROR, "Wayland: Failed to open libwayland-egl"); return GLFW_FALSE; } _glfw.wl.egl.window_create = (PFN_wl_egl_window_create) _glfw_dlsym(_glfw.wl.egl.handle, "wl_egl_window_create"); _glfw.wl.egl.window_destroy = (PFN_wl_egl_window_destroy) _glfw_dlsym(_glfw.wl.egl.handle, "wl_egl_window_destroy"); _glfw.wl.egl.window_resize = (PFN_wl_egl_window_resize) _glfw_dlsym(_glfw.wl.egl.handle, "wl_egl_window_resize"); _glfw.wl.xkb.handle = _glfw_dlopen("libxkbcommon.so.0"); if (!_glfw.wl.xkb.handle) { _glfwInputError(GLFW_PLATFORM_ERROR, "Wayland: Failed to open libxkbcommon"); return GLFW_FALSE; } _glfw.wl.xkb.context_new = (PFN_xkb_context_new) _glfw_dlsym(_glfw.wl.xkb.handle, "xkb_context_new"); _glfw.wl.xkb.context_unref = (PFN_xkb_context_unref) _glfw_dlsym(_glfw.wl.xkb.handle, "xkb_context_unref"); _glfw.wl.xkb.keymap_new_from_string = (PFN_xkb_keymap_new_from_string) _glfw_dlsym(_glfw.wl.xkb.handle, "xkb_keymap_new_from_string"); _glfw.wl.xkb.keymap_unref = (PFN_xkb_keymap_unref) _glfw_dlsym(_glfw.wl.xkb.handle, "xkb_keymap_unref"); _glfw.wl.xkb.keymap_mod_get_index = (PFN_xkb_keymap_mod_get_index) _glfw_dlsym(_glfw.wl.xkb.handle, "xkb_keymap_mod_get_index"); _glfw.wl.xkb.keymap_key_repeats = (PFN_xkb_keymap_key_repeats) _glfw_dlsym(_glfw.wl.xkb.handle, "xkb_keymap_key_repeats"); _glfw.wl.xkb.keymap_key_get_syms_by_level = (PFN_xkb_keymap_key_get_syms_by_level) _glfw_dlsym(_glfw.wl.xkb.handle, "xkb_keymap_key_get_syms_by_level"); _glfw.wl.xkb.state_new = (PFN_xkb_state_new) _glfw_dlsym(_glfw.wl.xkb.handle, "xkb_state_new"); _glfw.wl.xkb.state_unref = (PFN_xkb_state_unref) _glfw_dlsym(_glfw.wl.xkb.handle, "xkb_state_unref"); _glfw.wl.xkb.state_key_get_syms = (PFN_xkb_state_key_get_syms) _glfw_dlsym(_glfw.wl.xkb.handle, "xkb_state_key_get_syms"); _glfw.wl.xkb.state_update_mask = (PFN_xkb_state_update_mask) _glfw_dlsym(_glfw.wl.xkb.handle, "xkb_state_update_mask"); _glfw.wl.xkb.state_serialize_mods = (PFN_xkb_state_serialize_mods) _glfw_dlsym(_glfw.wl.xkb.handle, "xkb_state_serialize_mods"); _glfw.wl.xkb.state_key_get_layout = (PFN_xkb_state_key_get_layout) _glfw_dlsym(_glfw.wl.xkb.handle, "xkb_state_key_get_layout"); _glfw.wl.xkb.compose_table_new_from_locale = (PFN_xkb_compose_table_new_from_locale) _glfw_dlsym(_glfw.wl.xkb.handle, "xkb_compose_table_new_from_locale"); _glfw.wl.xkb.compose_table_unref = (PFN_xkb_compose_table_unref) _glfw_dlsym(_glfw.wl.xkb.handle, "xkb_compose_table_unref"); _glfw.wl.xkb.compose_state_new = (PFN_xkb_compose_state_new) _glfw_dlsym(_glfw.wl.xkb.handle, "xkb_compose_state_new"); _glfw.wl.xkb.compose_state_unref = (PFN_xkb_compose_state_unref) _glfw_dlsym(_glfw.wl.xkb.handle, "xkb_compose_state_unref"); _glfw.wl.xkb.compose_state_feed = (PFN_xkb_compose_state_feed) _glfw_dlsym(_glfw.wl.xkb.handle, "xkb_compose_state_feed"); _glfw.wl.xkb.compose_state_get_status = (PFN_xkb_compose_state_get_status) _glfw_dlsym(_glfw.wl.xkb.handle, "xkb_compose_state_get_status"); _glfw.wl.xkb.compose_state_get_one_sym = (PFN_xkb_compose_state_get_one_sym) _glfw_dlsym(_glfw.wl.xkb.handle, "xkb_compose_state_get_one_sym"); _glfw.wl.display = wl_display_connect(NULL); if (!_glfw.wl.display) { _glfwInputError(GLFW_PLATFORM_ERROR, "Wayland: Failed to connect to display"); return GLFW_FALSE; } _glfw.wl.registry = wl_display_get_registry(_glfw.wl.display); wl_registry_add_listener(_glfw.wl.registry, ®istryListener, NULL); createKeyTables(); _glfw.wl.xkb.context = xkb_context_new(0); if (!_glfw.wl.xkb.context) { _glfwInputError(GLFW_PLATFORM_ERROR, "Wayland: Failed to initialize xkb context"); return GLFW_FALSE; } // Sync so we got all registry objects wl_display_roundtrip(_glfw.wl.display); // Sync so we got all initial output events wl_display_roundtrip(_glfw.wl.display); #ifdef __linux__ if (!_glfwInitJoysticksLinux()) return GLFW_FALSE; #endif _glfwInitTimerPOSIX(); _glfw.wl.timerfd = -1; if (_glfw.wl.seatVersion >= 4) _glfw.wl.timerfd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC | TFD_NONBLOCK); if (_glfw.wl.pointer && _glfw.wl.shm) { cursorTheme = getenv("XCURSOR_THEME"); cursorSizeStr = getenv("XCURSOR_SIZE"); cursorSize = 32; if (cursorSizeStr) { errno = 0; cursorSizeLong = strtol(cursorSizeStr, &cursorSizeEnd, 10); if (!*cursorSizeEnd && !errno && cursorSizeLong > 0 && cursorSizeLong <= INT_MAX) cursorSize = (int)cursorSizeLong; } _glfw.wl.cursorTheme = wl_cursor_theme_load(cursorTheme, cursorSize, _glfw.wl.shm); if (!_glfw.wl.cursorTheme) { _glfwInputError(GLFW_PLATFORM_ERROR, "Wayland: Unable to load default cursor theme"); return GLFW_FALSE; } // If this happens to be NULL, we just fallback to the scale=1 version. _glfw.wl.cursorThemeHiDPI = wl_cursor_theme_load(cursorTheme, 2 * cursorSize, _glfw.wl.shm); _glfw.wl.cursorSurface = wl_compositor_create_surface(_glfw.wl.compositor); _glfw.wl.cursorTimerfd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC | TFD_NONBLOCK); } if (_glfw.wl.seat && _glfw.wl.dataDeviceManager) { _glfw.wl.dataDevice = wl_data_device_manager_get_data_device(_glfw.wl.dataDeviceManager, _glfw.wl.seat); wl_data_device_add_listener(_glfw.wl.dataDevice, &dataDeviceListener, NULL); _glfw.wl.clipboardSize = 4096; _glfw.wl.clipboardString = calloc(_glfw.wl.clipboardSize, 1); if (!_glfw.wl.clipboardString) { _glfwInputError(GLFW_OUT_OF_MEMORY, "Wayland: Unable to allocate clipboard memory"); return GLFW_FALSE; } } return GLFW_TRUE; } void _glfwPlatformTerminate(void) { #ifdef __linux__ _glfwTerminateJoysticksLinux(); #endif _glfwTerminateEGL(); if (_glfw.wl.egl.handle) { _glfw_dlclose(_glfw.wl.egl.handle); _glfw.wl.egl.handle = NULL; } if (_glfw.wl.xkb.composeState) xkb_compose_state_unref(_glfw.wl.xkb.composeState); if (_glfw.wl.xkb.keymap) xkb_keymap_unref(_glfw.wl.xkb.keymap); if (_glfw.wl.xkb.state) xkb_state_unref(_glfw.wl.xkb.state); if (_glfw.wl.xkb.context) xkb_context_unref(_glfw.wl.xkb.context); if (_glfw.wl.xkb.handle) { _glfw_dlclose(_glfw.wl.xkb.handle); _glfw.wl.xkb.handle = NULL; } if (_glfw.wl.cursorTheme) wl_cursor_theme_destroy(_glfw.wl.cursorTheme); if (_glfw.wl.cursorThemeHiDPI) wl_cursor_theme_destroy(_glfw.wl.cursorThemeHiDPI); if (_glfw.wl.cursor.handle) { _glfw_dlclose(_glfw.wl.cursor.handle); _glfw.wl.cursor.handle = NULL; } if (_glfw.wl.cursorSurface) wl_surface_destroy(_glfw.wl.cursorSurface); if (_glfw.wl.subcompositor) wl_subcompositor_destroy(_glfw.wl.subcompositor); if (_glfw.wl.compositor) wl_compositor_destroy(_glfw.wl.compositor); if (_glfw.wl.shm) wl_shm_destroy(_glfw.wl.shm); if (_glfw.wl.shell) wl_shell_destroy(_glfw.wl.shell); if (_glfw.wl.viewporter) wp_viewporter_destroy(_glfw.wl.viewporter); if (_glfw.wl.decorationManager) zxdg_decoration_manager_v1_destroy(_glfw.wl.decorationManager); if (_glfw.wl.wmBase) xdg_wm_base_destroy(_glfw.wl.wmBase); if (_glfw.wl.dataSource) wl_data_source_destroy(_glfw.wl.dataSource); if (_glfw.wl.dataDevice) wl_data_device_destroy(_glfw.wl.dataDevice); if (_glfw.wl.dataOffer) wl_data_offer_destroy(_glfw.wl.dataOffer); if (_glfw.wl.dataDeviceManager) wl_data_device_manager_destroy(_glfw.wl.dataDeviceManager); if (_glfw.wl.pointer) wl_pointer_destroy(_glfw.wl.pointer); if (_glfw.wl.keyboard) wl_keyboard_destroy(_glfw.wl.keyboard); if (_glfw.wl.seat) wl_seat_destroy(_glfw.wl.seat); if (_glfw.wl.relativePointerManager) zwp_relative_pointer_manager_v1_destroy(_glfw.wl.relativePointerManager); if (_glfw.wl.pointerConstraints) zwp_pointer_constraints_v1_destroy(_glfw.wl.pointerConstraints); if (_glfw.wl.idleInhibitManager) zwp_idle_inhibit_manager_v1_destroy(_glfw.wl.idleInhibitManager); if (_glfw.wl.registry) wl_registry_destroy(_glfw.wl.registry); if (_glfw.wl.display) { wl_display_flush(_glfw.wl.display); wl_display_disconnect(_glfw.wl.display); } if (_glfw.wl.timerfd >= 0) close(_glfw.wl.timerfd); if (_glfw.wl.cursorTimerfd >= 0) close(_glfw.wl.cursorTimerfd); free(_glfw.wl.clipboardString); free(_glfw.wl.clipboardSendString); } const char* _glfwPlatformGetVersionString(void) { return _GLFW_VERSION_NUMBER " Wayland EGL OSMesa" #if defined(_POSIX_TIMERS) && defined(_POSIX_MONOTONIC_CLOCK) " clock_gettime" #else " gettimeofday" #endif " evdev" #if defined(_GLFW_BUILD_DLL) " shared" #endif ; } #endif #ifndef HEADER_GUARD_WL_MONITOR_C #define HEADER_GUARD_WL_MONITOR_C //======================================================================== // GLFW 3.3.7 Wayland - www.glfw.org //------------------------------------------------------------------------ // Copyright (c) 2014 Jonas Ã…dahl // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would // be appreciated but is not required. // // 2. Altered source versions must be plainly marked as such, and must not // be misrepresented as being the original software. // // 3. This notice may not be removed or altered from any source // distribution. // //======================================================================== // It is fine to use C99 in this file because it will not be built with VS //======================================================================== #include #include #include #include #include static void outputHandleGeometry(void* data, struct wl_output* output, int32_t x, int32_t y, int32_t physicalWidth, int32_t physicalHeight, int32_t subpixel, const char* make, const char* model, int32_t transform) { struct _GLFWmonitor *monitor = data; monitor->wl.x = x; monitor->wl.y = y; monitor->widthMM = physicalWidth; monitor->heightMM = physicalHeight; snprintf(monitor->name, sizeof(monitor->name), "%s %s", make, model); } static void outputHandleMode(void* data, struct wl_output* output, uint32_t flags, int32_t width, int32_t height, int32_t refresh) { struct _GLFWmonitor *monitor = data; GLFWvidmode mode; mode.width = width; mode.height = height; mode.redBits = 8; mode.greenBits = 8; mode.blueBits = 8; mode.refreshRate = (int) round(refresh / 1000.0); monitor->modeCount++; monitor->modes = realloc(monitor->modes, monitor->modeCount * sizeof(GLFWvidmode)); monitor->modes[monitor->modeCount - 1] = mode; if (flags & WL_OUTPUT_MODE_CURRENT) monitor->wl.currentMode = monitor->modeCount - 1; } static void outputHandleDone(void* data, struct wl_output* output) { struct _GLFWmonitor *monitor = data; if (monitor->widthMM <= 0 || monitor->heightMM <= 0) { // If Wayland does not provide a physical size, assume the default 96 DPI const GLFWvidmode* mode = &monitor->modes[monitor->wl.currentMode]; monitor->widthMM = (int) (mode->width * 25.4f / 96.f); monitor->heightMM = (int) (mode->height * 25.4f / 96.f); } _glfwInputMonitor(monitor, GLFW_CONNECTED, _GLFW_INSERT_LAST); } static void outputHandleScale(void* data, struct wl_output* output, int32_t factor) { struct _GLFWmonitor *monitor = data; monitor->wl.scale = factor; } static const struct wl_output_listener outputListener = { outputHandleGeometry, outputHandleMode, outputHandleDone, outputHandleScale, }; ////////////////////////////////////////////////////////////////////////// ////// GLFW internal API ////// ////////////////////////////////////////////////////////////////////////// void _glfwAddOutputWayland(uint32_t name, uint32_t version) { _GLFWmonitor *monitor; struct wl_output *output; if (version < 2) { _glfwInputError(GLFW_PLATFORM_ERROR, "Wayland: Unsupported output interface version"); return; } // The actual name of this output will be set in the geometry handler. monitor = _glfwAllocMonitor("", 0, 0); output = wl_registry_bind(_glfw.wl.registry, name, &wl_output_interface, 2); if (!output) { _glfwFreeMonitor(monitor); return; } monitor->wl.scale = 1; monitor->wl.output = output; monitor->wl.name = name; wl_output_add_listener(output, &outputListener, monitor); } ////////////////////////////////////////////////////////////////////////// ////// GLFW platform API ////// ////////////////////////////////////////////////////////////////////////// void _glfwPlatformFreeMonitor(_GLFWmonitor* monitor) { if (monitor->wl.output) wl_output_destroy(monitor->wl.output); } void _glfwPlatformGetMonitorPos(_GLFWmonitor* monitor, int* xpos, int* ypos) { if (xpos) *xpos = monitor->wl.x; if (ypos) *ypos = monitor->wl.y; } void _glfwPlatformGetMonitorContentScale(_GLFWmonitor* monitor, float* xscale, float* yscale) { if (xscale) *xscale = (float) monitor->wl.scale; if (yscale) *yscale = (float) monitor->wl.scale; } void _glfwPlatformGetMonitorWorkarea(_GLFWmonitor* monitor, int* xpos, int* ypos, int* width, int* height) { if (xpos) *xpos = monitor->wl.x; if (ypos) *ypos = monitor->wl.y; if (width) *width = monitor->modes[monitor->wl.currentMode].width; if (height) *height = monitor->modes[monitor->wl.currentMode].height; } GLFWvidmode* _glfwPlatformGetVideoModes(_GLFWmonitor* monitor, int* found) { *found = monitor->modeCount; return monitor->modes; } void _glfwPlatformGetVideoMode(_GLFWmonitor* monitor, GLFWvidmode* mode) { *mode = monitor->modes[monitor->wl.currentMode]; } GLFWbool _glfwPlatformGetGammaRamp(_GLFWmonitor* monitor, GLFWgammaramp* ramp) { _glfwInputError(GLFW_PLATFORM_ERROR, "Wayland: Gamma ramp access is not available"); return GLFW_FALSE; } void _glfwPlatformSetGammaRamp(_GLFWmonitor* monitor, const GLFWgammaramp* ramp) { _glfwInputError(GLFW_PLATFORM_ERROR, "Wayland: Gamma ramp access is not available"); } ////////////////////////////////////////////////////////////////////////// ////// GLFW native API ////// ////////////////////////////////////////////////////////////////////////// GLFWAPI struct wl_output* glfwGetWaylandMonitor(GLFWmonitor* handle) { _GLFWmonitor* monitor = (_GLFWmonitor*) handle; _GLFW_REQUIRE_INIT_OR_RETURN(NULL); return monitor->wl.output; } #endif #ifndef HEADER_GUARD_WL_WINDOW_C #define HEADER_GUARD_WL_WINDOW_C //======================================================================== // GLFW 3.3.7 Wayland - www.glfw.org //------------------------------------------------------------------------ // Copyright (c) 2014 Jonas Ã…dahl // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would // be appreciated but is not required. // // 2. Altered source versions must be plainly marked as such, and must not // be misrepresented as being the original software. // // 3. This notice may not be removed or altered from any source // distribution. // //======================================================================== // It is fine to use C99 in this file because it will not be built with VS //======================================================================== #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include static void shellSurfaceHandlePing(void* data, struct wl_shell_surface* shellSurface, uint32_t serial) { wl_shell_surface_pong(shellSurface, serial); } static void shellSurfaceHandleConfigure(void* data, struct wl_shell_surface* shellSurface, uint32_t edges, int32_t width, int32_t height) { _GLFWwindow* window = data; float aspectRatio; float targetRatio; if (!window->monitor) { if (_glfw.wl.viewporter && window->decorated) { width -= _GLFW_DECORATION_HORIZONTAL; height -= _GLFW_DECORATION_VERTICAL; } if (width < 1) width = 1; if (height < 1) height = 1; if (window->numer != GLFW_DONT_CARE && window->denom != GLFW_DONT_CARE) { aspectRatio = (float)width / (float)height; targetRatio = (float)window->numer / (float)window->denom; if (aspectRatio < targetRatio) height = width / targetRatio; else if (aspectRatio > targetRatio) width = height * targetRatio; } if (window->minwidth != GLFW_DONT_CARE && width < window->minwidth) width = window->minwidth; else if (window->maxwidth != GLFW_DONT_CARE && width > window->maxwidth) width = window->maxwidth; if (window->minheight != GLFW_DONT_CARE && height < window->minheight) height = window->minheight; else if (window->maxheight != GLFW_DONT_CARE && height > window->maxheight) height = window->maxheight; } _glfwInputWindowSize(window, width, height); _glfwPlatformSetWindowSize(window, width, height); _glfwInputWindowDamage(window); } static void shellSurfaceHandlePopupDone(void* data, struct wl_shell_surface* shellSurface) { } static const struct wl_shell_surface_listener shellSurfaceListener = { shellSurfaceHandlePing, shellSurfaceHandleConfigure, shellSurfaceHandlePopupDone }; static int createTmpfileCloexec(char* tmpname) { int fd; fd = mkostemp(tmpname, O_CLOEXEC); if (fd >= 0) unlink(tmpname); return fd; } /* * Create a new, unique, anonymous file of the given size, and * return the file descriptor for it. The file descriptor is set * CLOEXEC. The file is immediately suitable for mmap()'ing * the given size at offset zero. * * The file should not have a permanent backing store like a disk, * but may have if XDG_RUNTIME_DIR is not properly implemented in OS. * * The file name is deleted from the file system. * * The file is suitable for buffer sharing between processes by * transmitting the file descriptor over Unix sockets using the * SCM_RIGHTS methods. * * posix_fallocate() is used to guarantee that disk space is available * for the file at the given size. If disk space is insufficient, errno * is set to ENOSPC. If posix_fallocate() is not supported, program may * receive SIGBUS on accessing mmap()'ed file contents instead. */ static int createAnonymousFile(off_t size) { static const char template[] = "/glfw-shared-XXXXXX"; const char* path; char* name; int fd; int ret; #ifdef HAVE_MEMFD_CREATE fd = memfd_create("glfw-shared", MFD_CLOEXEC | MFD_ALLOW_SEALING); if (fd >= 0) { // We can add this seal before calling posix_fallocate(), as the file // is currently zero-sized anyway. // // There is also no need to check for the return value, we couldn’t do // anything with it anyway. fcntl(fd, F_ADD_SEALS, F_SEAL_SHRINK | F_SEAL_SEAL); } else #elif defined(SHM_ANON) fd = shm_open(SHM_ANON, O_RDWR | O_CLOEXEC, 0600); if (fd < 0) #endif { path = getenv("XDG_RUNTIME_DIR"); if (!path) { errno = ENOENT; return -1; } name = calloc(strlen(path) + sizeof(template), 1); strcpy(name, path); strcat(name, template); fd = createTmpfileCloexec(name); free(name); if (fd < 0) return -1; } #if defined(SHM_ANON) // posix_fallocate does not work on SHM descriptors ret = ftruncate(fd, size); #else ret = posix_fallocate(fd, 0, size); #endif if (ret != 0) { close(fd); errno = ret; return -1; } return fd; } static struct wl_buffer* createShmBuffer(const GLFWimage* image) { struct wl_shm_pool* pool; struct wl_buffer* buffer; int stride = image->width * 4; int length = image->width * image->height * 4; void* data; int fd, i; fd = createAnonymousFile(length); if (fd < 0) { _glfwInputError(GLFW_PLATFORM_ERROR, "Wayland: Creating a buffer file for %d B failed: %s", length, strerror(errno)); return NULL; } data = mmap(NULL, length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); if (data == MAP_FAILED) { _glfwInputError(GLFW_PLATFORM_ERROR, "Wayland: mmap failed: %s", strerror(errno)); close(fd); return NULL; } pool = wl_shm_create_pool(_glfw.wl.shm, fd, length); close(fd); unsigned char* source = (unsigned char*) image->pixels; unsigned char* target = data; for (i = 0; i < image->width * image->height; i++, source += 4) { unsigned int alpha = source[3]; *target++ = (unsigned char) ((source[2] * alpha) / 255); *target++ = (unsigned char) ((source[1] * alpha) / 255); *target++ = (unsigned char) ((source[0] * alpha) / 255); *target++ = (unsigned char) alpha; } buffer = wl_shm_pool_create_buffer(pool, 0, image->width, image->height, stride, WL_SHM_FORMAT_ARGB8888); munmap(data, length); wl_shm_pool_destroy(pool); return buffer; } // Wait for data to arrive on any of the specified file descriptors // static GLFWbool waitForData(struct pollfd* fds, nfds_t count, double* timeout) { for (;;) { if (timeout) { const uint64_t base = _glfwPlatformGetTimerValue(); #if defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__CYGWIN__) const time_t seconds = (time_t) *timeout; const long nanoseconds = (long) ((*timeout - seconds) * 1e9); const struct timespec ts = { seconds, nanoseconds }; const int result = ppoll(fds, count, &ts, NULL); #elif defined(__NetBSD__) const time_t seconds = (time_t) *timeout; const long nanoseconds = (long) ((*timeout - seconds) * 1e9); const struct timespec ts = { seconds, nanoseconds }; const int result = pollts(fds, count, &ts, NULL); #else const int milliseconds = (int) (*timeout * 1e3); const int result = poll(fds, count, milliseconds); #endif const int error = errno; // clock_gettime may overwrite our error *timeout -= (_glfwPlatformGetTimerValue() - base) / (double) _glfwPlatformGetTimerFrequency(); if (result > 0) return GLFW_TRUE; else if (result == -1 && error != EINTR && error != EAGAIN) return GLFW_FALSE; else if (*timeout <= 0.0) return GLFW_FALSE; } else { const int result = poll(fds, count, -1); if (result > 0) return GLFW_TRUE; else if (result == -1 && errno != EINTR && errno != EAGAIN) return GLFW_FALSE; } } } static void createDecoration(_GLFWdecorationWayland* decoration, struct wl_surface* parent, struct wl_buffer* buffer, GLFWbool opaque, int x, int y, int width, int height) { struct wl_region* region; decoration->surface = wl_compositor_create_surface(_glfw.wl.compositor); decoration->subsurface = wl_subcompositor_get_subsurface(_glfw.wl.subcompositor, decoration->surface, parent); wl_subsurface_set_position(decoration->subsurface, x, y); decoration->viewport = wp_viewporter_get_viewport(_glfw.wl.viewporter, decoration->surface); wp_viewport_set_destination(decoration->viewport, width, height); wl_surface_attach(decoration->surface, buffer, 0, 0); if (opaque) { region = wl_compositor_create_region(_glfw.wl.compositor); wl_region_add(region, 0, 0, width, height); wl_surface_set_opaque_region(decoration->surface, region); wl_surface_commit(decoration->surface); wl_region_destroy(region); } else wl_surface_commit(decoration->surface); } static void createDecorations(_GLFWwindow* window) { unsigned char data[] = { 224, 224, 224, 255 }; const GLFWimage image = { 1, 1, data }; GLFWbool opaque = (data[3] == 255); if (!_glfw.wl.viewporter || !window->decorated || window->wl.decorations.serverSide) return; if (!window->wl.decorations.buffer) window->wl.decorations.buffer = createShmBuffer(&image); if (!window->wl.decorations.buffer) return; createDecoration(&window->wl.decorations.top, window->wl.surface, window->wl.decorations.buffer, opaque, 0, -_GLFW_DECORATION_TOP, window->wl.width, _GLFW_DECORATION_TOP); createDecoration(&window->wl.decorations.left, window->wl.surface, window->wl.decorations.buffer, opaque, -_GLFW_DECORATION_WIDTH, -_GLFW_DECORATION_TOP, _GLFW_DECORATION_WIDTH, window->wl.height + _GLFW_DECORATION_TOP); createDecoration(&window->wl.decorations.right, window->wl.surface, window->wl.decorations.buffer, opaque, window->wl.width, -_GLFW_DECORATION_TOP, _GLFW_DECORATION_WIDTH, window->wl.height + _GLFW_DECORATION_TOP); createDecoration(&window->wl.decorations.bottom, window->wl.surface, window->wl.decorations.buffer, opaque, -_GLFW_DECORATION_WIDTH, window->wl.height, window->wl.width + _GLFW_DECORATION_HORIZONTAL, _GLFW_DECORATION_WIDTH); } static void destroyDecoration(_GLFWdecorationWayland* decoration) { if (decoration->subsurface) wl_subsurface_destroy(decoration->subsurface); if (decoration->surface) wl_surface_destroy(decoration->surface); if (decoration->viewport) wp_viewport_destroy(decoration->viewport); decoration->surface = NULL; decoration->subsurface = NULL; decoration->viewport = NULL; } static void destroyDecorations(_GLFWwindow* window) { destroyDecoration(&window->wl.decorations.top); destroyDecoration(&window->wl.decorations.left); destroyDecoration(&window->wl.decorations.right); destroyDecoration(&window->wl.decorations.bottom); } static void xdgDecorationHandleConfigure(void* data, struct zxdg_toplevel_decoration_v1* decoration, uint32_t mode) { _GLFWwindow* window = data; window->wl.decorations.serverSide = (mode == ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE); if (!window->wl.decorations.serverSide) createDecorations(window); } static const struct zxdg_toplevel_decoration_v1_listener xdgDecorationListener = { xdgDecorationHandleConfigure, }; // Makes the surface considered as XRGB instead of ARGB. static void setOpaqueRegion(_GLFWwindow* window) { struct wl_region* region; region = wl_compositor_create_region(_glfw.wl.compositor); if (!region) return; wl_region_add(region, 0, 0, window->wl.width, window->wl.height); wl_surface_set_opaque_region(window->wl.surface, region); wl_surface_commit(window->wl.surface); wl_region_destroy(region); } static void resizeWindow(_GLFWwindow* window) { int scale = window->wl.scale; int scaledWidth = window->wl.width * scale; int scaledHeight = window->wl.height * scale; wl_egl_window_resize(window->wl.native, scaledWidth, scaledHeight, 0, 0); if (!window->wl.transparent) setOpaqueRegion(window); _glfwInputFramebufferSize(window, scaledWidth, scaledHeight); _glfwInputWindowContentScale(window, scale, scale); if (!window->wl.decorations.top.surface) return; // Top decoration. wp_viewport_set_destination(window->wl.decorations.top.viewport, window->wl.width, _GLFW_DECORATION_TOP); wl_surface_commit(window->wl.decorations.top.surface); // Left decoration. wp_viewport_set_destination(window->wl.decorations.left.viewport, _GLFW_DECORATION_WIDTH, window->wl.height + _GLFW_DECORATION_TOP); wl_surface_commit(window->wl.decorations.left.surface); // Right decoration. wl_subsurface_set_position(window->wl.decorations.right.subsurface, window->wl.width, -_GLFW_DECORATION_TOP); wp_viewport_set_destination(window->wl.decorations.right.viewport, _GLFW_DECORATION_WIDTH, window->wl.height + _GLFW_DECORATION_TOP); wl_surface_commit(window->wl.decorations.right.surface); // Bottom decoration. wl_subsurface_set_position(window->wl.decorations.bottom.subsurface, -_GLFW_DECORATION_WIDTH, window->wl.height); wp_viewport_set_destination(window->wl.decorations.bottom.viewport, window->wl.width + _GLFW_DECORATION_HORIZONTAL, _GLFW_DECORATION_WIDTH); wl_surface_commit(window->wl.decorations.bottom.surface); } static void checkScaleChange(_GLFWwindow* window) { // Check if we will be able to set the buffer scale or not. if (_glfw.wl.compositorVersion < 3) return; // Get the scale factor from the highest scale monitor. int maxScale = 1; for (int i = 0; i < window->wl.monitorsCount; i++) { const int scale = window->wl.monitors[i]->wl.scale; if (maxScale < scale) maxScale = scale; } // Only change the framebuffer size if the scale changed. if (window->wl.scale != maxScale) { window->wl.scale = maxScale; wl_surface_set_buffer_scale(window->wl.surface, maxScale); resizeWindow(window); } } static void surfaceHandleEnter(void *data, struct wl_surface *surface, struct wl_output *output) { _GLFWwindow* window = data; _GLFWmonitor* monitor = wl_output_get_user_data(output); if (window->wl.monitorsCount + 1 > window->wl.monitorsSize) { ++window->wl.monitorsSize; window->wl.monitors = realloc(window->wl.monitors, window->wl.monitorsSize * sizeof(_GLFWmonitor*)); } window->wl.monitors[window->wl.monitorsCount++] = monitor; checkScaleChange(window); } static void surfaceHandleLeave(void *data, struct wl_surface *surface, struct wl_output *output) { _GLFWwindow* window = data; _GLFWmonitor* monitor = wl_output_get_user_data(output); GLFWbool found; int i; for (i = 0, found = GLFW_FALSE; i < window->wl.monitorsCount - 1; ++i) { if (monitor == window->wl.monitors[i]) found = GLFW_TRUE; if (found) window->wl.monitors[i] = window->wl.monitors[i + 1]; } window->wl.monitors[--window->wl.monitorsCount] = NULL; checkScaleChange(window); } static const struct wl_surface_listener surfaceListener = { surfaceHandleEnter, surfaceHandleLeave }; static void setIdleInhibitor(_GLFWwindow* window, GLFWbool enable) { if (enable && !window->wl.idleInhibitor && _glfw.wl.idleInhibitManager) { window->wl.idleInhibitor = zwp_idle_inhibit_manager_v1_create_inhibitor( _glfw.wl.idleInhibitManager, window->wl.surface); if (!window->wl.idleInhibitor) _glfwInputError(GLFW_PLATFORM_ERROR, "Wayland: Idle inhibitor creation failed"); } else if (!enable && window->wl.idleInhibitor) { zwp_idle_inhibitor_v1_destroy(window->wl.idleInhibitor); window->wl.idleInhibitor = NULL; } } static void setFullscreen(_GLFWwindow* window, _GLFWmonitor* monitor, int refreshRate) { if (window->wl.xdg.toplevel) { xdg_toplevel_set_fullscreen( window->wl.xdg.toplevel, monitor->wl.output); } else if (window->wl.shellSurface) { wl_shell_surface_set_fullscreen( window->wl.shellSurface, WL_SHELL_SURFACE_FULLSCREEN_METHOD_DEFAULT, refreshRate * 1000, // Convert Hz to mHz. monitor->wl.output); } setIdleInhibitor(window, GLFW_TRUE); if (!window->wl.decorations.serverSide) destroyDecorations(window); } static GLFWbool createShellSurface(_GLFWwindow* window) { if (!_glfw.wl.shell) { _glfwInputError(GLFW_PLATFORM_ERROR, "Wayland: wl_shell protocol not available"); return GLFW_FALSE; } window->wl.shellSurface = wl_shell_get_shell_surface(_glfw.wl.shell, window->wl.surface); if (!window->wl.shellSurface) { _glfwInputError(GLFW_PLATFORM_ERROR, "Wayland: Shell surface creation failed"); return GLFW_FALSE; } wl_shell_surface_add_listener(window->wl.shellSurface, &shellSurfaceListener, window); if (window->wl.title) wl_shell_surface_set_title(window->wl.shellSurface, window->wl.title); if (window->monitor) { setFullscreen(window, window->monitor, 0); } else if (window->wl.maximized) { wl_shell_surface_set_maximized(window->wl.shellSurface, NULL); setIdleInhibitor(window, GLFW_FALSE); createDecorations(window); } else { wl_shell_surface_set_toplevel(window->wl.shellSurface); setIdleInhibitor(window, GLFW_FALSE); createDecorations(window); } wl_surface_commit(window->wl.surface); return GLFW_TRUE; } static void xdgToplevelHandleConfigure(void* data, struct xdg_toplevel* toplevel, int32_t width, int32_t height, struct wl_array* states) { _GLFWwindow* window = data; float aspectRatio; float targetRatio; uint32_t* state; GLFWbool maximized = GLFW_FALSE; GLFWbool fullscreen = GLFW_FALSE; GLFWbool activated = GLFW_FALSE; wl_array_for_each(state, states) { switch (*state) { case XDG_TOPLEVEL_STATE_MAXIMIZED: maximized = GLFW_TRUE; break; case XDG_TOPLEVEL_STATE_FULLSCREEN: fullscreen = GLFW_TRUE; break; case XDG_TOPLEVEL_STATE_RESIZING: break; case XDG_TOPLEVEL_STATE_ACTIVATED: activated = GLFW_TRUE; break; } } if (width != 0 && height != 0) { if (!maximized && !fullscreen) { if (window->numer != GLFW_DONT_CARE && window->denom != GLFW_DONT_CARE) { aspectRatio = (float)width / (float)height; targetRatio = (float)window->numer / (float)window->denom; if (aspectRatio < targetRatio) height = width / targetRatio; else if (aspectRatio > targetRatio) width = height * targetRatio; } } _glfwInputWindowSize(window, width, height); _glfwPlatformSetWindowSize(window, width, height); _glfwInputWindowDamage(window); } if (window->wl.wasFullscreen && window->autoIconify) { if (!activated || !fullscreen) { _glfwPlatformIconifyWindow(window); window->wl.wasFullscreen = GLFW_FALSE; } } if (fullscreen && activated) window->wl.wasFullscreen = GLFW_TRUE; } static void xdgToplevelHandleClose(void* data, struct xdg_toplevel* toplevel) { _GLFWwindow* window = data; _glfwInputWindowCloseRequest(window); } static const struct xdg_toplevel_listener xdgToplevelListener = { xdgToplevelHandleConfigure, xdgToplevelHandleClose }; static void xdgSurfaceHandleConfigure(void* data, struct xdg_surface* surface, uint32_t serial) { xdg_surface_ack_configure(surface, serial); } static const struct xdg_surface_listener xdgSurfaceListener = { xdgSurfaceHandleConfigure }; static void setXdgDecorations(_GLFWwindow* window) { if (_glfw.wl.decorationManager) { window->wl.xdg.decoration = zxdg_decoration_manager_v1_get_toplevel_decoration( _glfw.wl.decorationManager, window->wl.xdg.toplevel); zxdg_toplevel_decoration_v1_add_listener(window->wl.xdg.decoration, &xdgDecorationListener, window); zxdg_toplevel_decoration_v1_set_mode( window->wl.xdg.decoration, ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE); } else { window->wl.decorations.serverSide = GLFW_FALSE; createDecorations(window); } } static GLFWbool createXdgSurface(_GLFWwindow* window) { window->wl.xdg.surface = xdg_wm_base_get_xdg_surface(_glfw.wl.wmBase, window->wl.surface); if (!window->wl.xdg.surface) { _glfwInputError(GLFW_PLATFORM_ERROR, "Wayland: xdg-surface creation failed"); return GLFW_FALSE; } xdg_surface_add_listener(window->wl.xdg.surface, &xdgSurfaceListener, window); window->wl.xdg.toplevel = xdg_surface_get_toplevel(window->wl.xdg.surface); if (!window->wl.xdg.toplevel) { _glfwInputError(GLFW_PLATFORM_ERROR, "Wayland: xdg-toplevel creation failed"); return GLFW_FALSE; } xdg_toplevel_add_listener(window->wl.xdg.toplevel, &xdgToplevelListener, window); if (window->wl.title) xdg_toplevel_set_title(window->wl.xdg.toplevel, window->wl.title); if (window->minwidth != GLFW_DONT_CARE && window->minheight != GLFW_DONT_CARE) xdg_toplevel_set_min_size(window->wl.xdg.toplevel, window->minwidth, window->minheight); if (window->maxwidth != GLFW_DONT_CARE && window->maxheight != GLFW_DONT_CARE) xdg_toplevel_set_max_size(window->wl.xdg.toplevel, window->maxwidth, window->maxheight); if (window->monitor) { xdg_toplevel_set_fullscreen(window->wl.xdg.toplevel, window->monitor->wl.output); setIdleInhibitor(window, GLFW_TRUE); } else if (window->wl.maximized) { xdg_toplevel_set_maximized(window->wl.xdg.toplevel); setIdleInhibitor(window, GLFW_FALSE); setXdgDecorations(window); } else { setIdleInhibitor(window, GLFW_FALSE); setXdgDecorations(window); } wl_surface_commit(window->wl.surface); wl_display_roundtrip(_glfw.wl.display); return GLFW_TRUE; } static GLFWbool createSurface(_GLFWwindow* window, const _GLFWwndconfig* wndconfig, const _GLFWfbconfig* fbconfig) { window->wl.surface = wl_compositor_create_surface(_glfw.wl.compositor); if (!window->wl.surface) return GLFW_FALSE; wl_surface_add_listener(window->wl.surface, &surfaceListener, window); wl_surface_set_user_data(window->wl.surface, window); window->wl.native = wl_egl_window_create(window->wl.surface, wndconfig->width, wndconfig->height); if (!window->wl.native) return GLFW_FALSE; window->wl.width = wndconfig->width; window->wl.height = wndconfig->height; window->wl.scale = 1; window->wl.title = _glfw_strdup(wndconfig->title); window->wl.transparent = fbconfig->transparent; if (!window->wl.transparent) setOpaqueRegion(window); if (window->monitor || wndconfig->visible) { if (!createXdgSurface(window)) return GLFW_FALSE; window->wl.visible = GLFW_TRUE; } return GLFW_TRUE; } static void setCursorImage(_GLFWwindow* window, _GLFWcursorWayland* cursorWayland) { struct itimerspec timer = {}; struct wl_cursor* wlCursor = cursorWayland->cursor; struct wl_cursor_image* image; struct wl_buffer* buffer; struct wl_surface* surface = _glfw.wl.cursorSurface; int scale = 1; if (!wlCursor) buffer = cursorWayland->buffer; else { if (window->wl.scale > 1 && cursorWayland->cursorHiDPI) { wlCursor = cursorWayland->cursorHiDPI; scale = 2; } image = wlCursor->images[cursorWayland->currentImage]; buffer = wl_cursor_image_get_buffer(image); if (!buffer) return; timer.it_value.tv_sec = image->delay / 1000; timer.it_value.tv_nsec = (image->delay % 1000) * 1000000; timerfd_settime(_glfw.wl.cursorTimerfd, 0, &timer, NULL); cursorWayland->width = image->width; cursorWayland->height = image->height; cursorWayland->xhot = image->hotspot_x; cursorWayland->yhot = image->hotspot_y; } wl_pointer_set_cursor(_glfw.wl.pointer, _glfw.wl.pointerEnterSerial, surface, cursorWayland->xhot / scale, cursorWayland->yhot / scale); wl_surface_set_buffer_scale(surface, scale); wl_surface_attach(surface, buffer, 0, 0); wl_surface_damage(surface, 0, 0, cursorWayland->width, cursorWayland->height); wl_surface_commit(surface); } static void incrementCursorImage(_GLFWwindow* window) { _GLFWcursor* cursor; if (!window || window->wl.decorations.focus != mainWindow) return; cursor = window->wl.currentCursor; if (cursor && cursor->wl.cursor) { cursor->wl.currentImage += 1; cursor->wl.currentImage %= cursor->wl.cursor->image_count; setCursorImage(window, &cursor->wl); } } static GLFWbool flushDisplay(void) { while (wl_display_flush(_glfw.wl.display) == -1) { if (errno != EAGAIN) return GLFW_FALSE; struct pollfd fd = { wl_display_get_fd(_glfw.wl.display), POLLOUT }; while (poll(&fd, 1, -1) == -1) { if (errno != EINTR && errno != EAGAIN) return GLFW_FALSE; } } return GLFW_TRUE; } static void handleEvents(double* timeout) { GLFWbool event = GLFW_FALSE; struct pollfd fds[] = { { wl_display_get_fd(_glfw.wl.display), POLLIN }, { _glfw.wl.timerfd, POLLIN }, { _glfw.wl.cursorTimerfd, POLLIN }, }; while (!event) { while (wl_display_prepare_read(_glfw.wl.display) != 0) wl_display_dispatch_pending(_glfw.wl.display); // If an error other than EAGAIN happens, we have likely been disconnected // from the Wayland session; try to handle that the best we can. if (!flushDisplay()) { wl_display_cancel_read(_glfw.wl.display); _GLFWwindow* window = _glfw.windowListHead; while (window) { _glfwInputWindowCloseRequest(window); window = window->next; } return; } if (!waitForData(fds, 3, timeout)) { wl_display_cancel_read(_glfw.wl.display); return; } if (fds[0].revents & POLLIN) { wl_display_read_events(_glfw.wl.display); if (wl_display_dispatch_pending(_glfw.wl.display) > 0) event = GLFW_TRUE; } else wl_display_cancel_read(_glfw.wl.display); if (fds[1].revents & POLLIN) { uint64_t repeats; if (read(_glfw.wl.timerfd, &repeats, sizeof(repeats)) == 8) { for (uint64_t i = 0; i < repeats; i++) { _glfwInputKey(_glfw.wl.keyboardFocus, _glfw.wl.keyboardLastKey, _glfw.wl.keyboardLastScancode, GLFW_PRESS, _glfw.wl.xkb.modifiers); _glfwInputTextWayland(_glfw.wl.keyboardFocus, _glfw.wl.keyboardLastScancode); } event = GLFW_TRUE; } } if (fds[2].revents & POLLIN) { uint64_t repeats; if (read(_glfw.wl.cursorTimerfd, &repeats, sizeof(repeats)) == 8) { incrementCursorImage(_glfw.wl.pointerFocus); event = GLFW_TRUE; } } } } // Translates a GLFW standard cursor to a theme cursor name // static char *translateCursorShape(int shape) { switch (shape) { case GLFW_ARROW_CURSOR: return "left_ptr"; case GLFW_IBEAM_CURSOR: return "xterm"; case GLFW_CROSSHAIR_CURSOR: return "crosshair"; case GLFW_HAND_CURSOR: return "hand2"; case GLFW_HRESIZE_CURSOR: return "sb_h_double_arrow"; case GLFW_VRESIZE_CURSOR: return "sb_v_double_arrow"; } return NULL; } ////////////////////////////////////////////////////////////////////////// ////// GLFW platform API ////// ////////////////////////////////////////////////////////////////////////// int _glfwPlatformCreateWindow(_GLFWwindow* window, const _GLFWwndconfig* wndconfig, const _GLFWctxconfig* ctxconfig, const _GLFWfbconfig* fbconfig) { if (!createSurface(window, wndconfig, fbconfig)) return GLFW_FALSE; if (ctxconfig->client != GLFW_NO_API) { if (ctxconfig->source == GLFW_EGL_CONTEXT_API || ctxconfig->source == GLFW_NATIVE_CONTEXT_API) { if (!_glfwInitEGL()) return GLFW_FALSE; if (!_glfwCreateContextEGL(window, ctxconfig, fbconfig)) return GLFW_FALSE; } else if (ctxconfig->source == GLFW_OSMESA_CONTEXT_API) { if (!_glfwInitOSMesa()) return GLFW_FALSE; if (!_glfwCreateContextOSMesa(window, ctxconfig, fbconfig)) return GLFW_FALSE; } } return GLFW_TRUE; } void _glfwPlatformDestroyWindow(_GLFWwindow* window) { if (window == _glfw.wl.pointerFocus) { _glfw.wl.pointerFocus = NULL; _glfwInputCursorEnter(window, GLFW_FALSE); } if (window == _glfw.wl.keyboardFocus) { _glfw.wl.keyboardFocus = NULL; _glfwInputWindowFocus(window, GLFW_FALSE); } if (window->wl.idleInhibitor) zwp_idle_inhibitor_v1_destroy(window->wl.idleInhibitor); if (window->context.destroy) window->context.destroy(window); destroyDecorations(window); if (window->wl.xdg.decoration) zxdg_toplevel_decoration_v1_destroy(window->wl.xdg.decoration); if (window->wl.decorations.buffer) wl_buffer_destroy(window->wl.decorations.buffer); if (window->wl.native) wl_egl_window_destroy(window->wl.native); if (window->wl.shellSurface) wl_shell_surface_destroy(window->wl.shellSurface); if (window->wl.xdg.toplevel) xdg_toplevel_destroy(window->wl.xdg.toplevel); if (window->wl.xdg.surface) xdg_surface_destroy(window->wl.xdg.surface); if (window->wl.surface) wl_surface_destroy(window->wl.surface); free(window->wl.title); free(window->wl.monitors); } void _glfwPlatformSetWindowTitle(_GLFWwindow* window, const char* title) { if (window->wl.title) free(window->wl.title); window->wl.title = _glfw_strdup(title); if (window->wl.xdg.toplevel) xdg_toplevel_set_title(window->wl.xdg.toplevel, title); else if (window->wl.shellSurface) wl_shell_surface_set_title(window->wl.shellSurface, title); } void _glfwPlatformSetWindowIcon(_GLFWwindow* window, int count, const GLFWimage* images) { _glfwInputError(GLFW_PLATFORM_ERROR, "Wayland: Setting window icon not supported"); } void _glfwPlatformGetWindowPos(_GLFWwindow* window, int* xpos, int* ypos) { // A Wayland client is not aware of its position, so just warn and leave it // as (0, 0) _glfwInputError(GLFW_PLATFORM_ERROR, "Wayland: Window position retrieval not supported"); } void _glfwPlatformSetWindowPos(_GLFWwindow* window, int xpos, int ypos) { // A Wayland client can not set its position, so just warn _glfwInputError(GLFW_PLATFORM_ERROR, "Wayland: Window position setting not supported"); } void _glfwPlatformGetWindowSize(_GLFWwindow* window, int* width, int* height) { if (width) *width = window->wl.width; if (height) *height = window->wl.height; } void _glfwPlatformSetWindowSize(_GLFWwindow* window, int width, int height) { window->wl.width = width; window->wl.height = height; resizeWindow(window); } void _glfwPlatformSetWindowSizeLimits(_GLFWwindow* window, int minwidth, int minheight, int maxwidth, int maxheight) { if (_glfw.wl.wmBase) { if (window->wl.xdg.toplevel) { if (minwidth == GLFW_DONT_CARE || minheight == GLFW_DONT_CARE) minwidth = minheight = 0; if (maxwidth == GLFW_DONT_CARE || maxheight == GLFW_DONT_CARE) maxwidth = maxheight = 0; xdg_toplevel_set_min_size(window->wl.xdg.toplevel, minwidth, minheight); xdg_toplevel_set_max_size(window->wl.xdg.toplevel, maxwidth, maxheight); wl_surface_commit(window->wl.surface); } } else { // TODO: find out how to trigger a resize. // The actual limits are checked in the wl_shell_surface::configure handler. } } void _glfwPlatformSetWindowAspectRatio(_GLFWwindow* window, int numer, int denom) { // TODO: find out how to trigger a resize. // The actual limits are checked in the wl_shell_surface::configure handler. } void _glfwPlatformGetFramebufferSize(_GLFWwindow* window, int* width, int* height) { _glfwPlatformGetWindowSize(window, width, height); if (width) *width *= window->wl.scale; if (height) *height *= window->wl.scale; } void _glfwPlatformGetWindowFrameSize(_GLFWwindow* window, int* left, int* top, int* right, int* bottom) { if (window->decorated && !window->monitor && !window->wl.decorations.serverSide) { if (top) *top = _GLFW_DECORATION_TOP; if (left) *left = _GLFW_DECORATION_WIDTH; if (right) *right = _GLFW_DECORATION_WIDTH; if (bottom) *bottom = _GLFW_DECORATION_WIDTH; } } void _glfwPlatformGetWindowContentScale(_GLFWwindow* window, float* xscale, float* yscale) { if (xscale) *xscale = (float) window->wl.scale; if (yscale) *yscale = (float) window->wl.scale; } void _glfwPlatformIconifyWindow(_GLFWwindow* window) { if (_glfw.wl.wmBase) { if (window->wl.xdg.toplevel) xdg_toplevel_set_minimized(window->wl.xdg.toplevel); } else { _glfwInputError(GLFW_PLATFORM_ERROR, "Wayland: Iconify window not supported on wl_shell"); } } void _glfwPlatformRestoreWindow(_GLFWwindow* window) { if (window->wl.xdg.toplevel) { if (window->monitor) xdg_toplevel_unset_fullscreen(window->wl.xdg.toplevel); if (window->wl.maximized) xdg_toplevel_unset_maximized(window->wl.xdg.toplevel); // There is no way to unset minimized, or even to know if we are // minimized, so there is nothing to do here. } else if (window->wl.shellSurface) { if (window->monitor || window->wl.maximized) wl_shell_surface_set_toplevel(window->wl.shellSurface); } _glfwInputWindowMonitor(window, NULL); window->wl.maximized = GLFW_FALSE; } void _glfwPlatformMaximizeWindow(_GLFWwindow* window) { if (window->wl.xdg.toplevel) { xdg_toplevel_set_maximized(window->wl.xdg.toplevel); } else if (window->wl.shellSurface) { // Let the compositor select the best output. wl_shell_surface_set_maximized(window->wl.shellSurface, NULL); } window->wl.maximized = GLFW_TRUE; } void _glfwPlatformShowWindow(_GLFWwindow* window) { if (!window->wl.visible) { // NOTE: The XDG/shell surface is created here so command-line applications // with off-screen windows do not appear in for example the Unity dock if (_glfw.wl.wmBase) { if (!window->wl.xdg.toplevel) createXdgSurface(window); } else if (!window->wl.shellSurface) createShellSurface(window); window->wl.visible = GLFW_TRUE; _glfwInputWindowDamage(window); } } void _glfwPlatformHideWindow(_GLFWwindow* window) { if (window->wl.visible) { window->wl.visible = GLFW_FALSE; wl_surface_attach(window->wl.surface, NULL, 0, 0); wl_surface_commit(window->wl.surface); } } void _glfwPlatformRequestWindowAttention(_GLFWwindow* window) { // TODO _glfwInputError(GLFW_PLATFORM_ERROR, "Wayland: Window attention request not implemented yet"); } void _glfwPlatformFocusWindow(_GLFWwindow* window) { _glfwInputError(GLFW_PLATFORM_ERROR, "Wayland: Focusing a window requires user interaction"); } void _glfwPlatformSetWindowMonitor(_GLFWwindow* window, _GLFWmonitor* monitor, int xpos, int ypos, int width, int height, int refreshRate) { if (monitor) { setFullscreen(window, monitor, refreshRate); } else { if (window->wl.xdg.toplevel) xdg_toplevel_unset_fullscreen(window->wl.xdg.toplevel); else if (window->wl.shellSurface) wl_shell_surface_set_toplevel(window->wl.shellSurface); setIdleInhibitor(window, GLFW_FALSE); if (!_glfw.wl.decorationManager) createDecorations(window); } _glfwInputWindowMonitor(window, monitor); } int _glfwPlatformWindowFocused(_GLFWwindow* window) { return _glfw.wl.keyboardFocus == window; } int _glfwPlatformWindowIconified(_GLFWwindow* window) { // wl_shell doesn't have any iconified concept, and xdg-shell doesn’t give // any way to request whether a surface is iconified. return GLFW_FALSE; } int _glfwPlatformWindowVisible(_GLFWwindow* window) { return window->wl.visible; } int _glfwPlatformWindowMaximized(_GLFWwindow* window) { return window->wl.maximized; } int _glfwPlatformWindowHovered(_GLFWwindow* window) { return window->wl.hovered; } int _glfwPlatformFramebufferTransparent(_GLFWwindow* window) { return window->wl.transparent; } void _glfwPlatformSetWindowResizable(_GLFWwindow* window, GLFWbool enabled) { // TODO _glfwInputError(GLFW_PLATFORM_ERROR, "Wayland: Window attribute setting not implemented yet"); } void _glfwPlatformSetWindowDecorated(_GLFWwindow* window, GLFWbool enabled) { if (!window->monitor) { if (enabled) createDecorations(window); else destroyDecorations(window); } } void _glfwPlatformSetWindowFloating(_GLFWwindow* window, GLFWbool enabled) { // TODO _glfwInputError(GLFW_PLATFORM_ERROR, "Wayland: Window attribute setting not implemented yet"); } float _glfwPlatformGetWindowOpacity(_GLFWwindow* window) { return 1.f; } void _glfwPlatformSetWindowOpacity(_GLFWwindow* window, float opacity) { } void _glfwPlatformSetRawMouseMotion(_GLFWwindow *window, GLFWbool enabled) { // This is handled in relativePointerHandleRelativeMotion } GLFWbool _glfwPlatformRawMouseMotionSupported(void) { return GLFW_TRUE; } void _glfwPlatformPollEvents(void) { double timeout = 0.0; handleEvents(&timeout); } void _glfwPlatformWaitEvents(void) { handleEvents(NULL); } void _glfwPlatformWaitEventsTimeout(double timeout) { handleEvents(&timeout); } void _glfwPlatformPostEmptyEvent(void) { wl_display_sync(_glfw.wl.display); flushDisplay(); } void _glfwPlatformGetCursorPos(_GLFWwindow* window, double* xpos, double* ypos) { if (xpos) *xpos = window->wl.cursorPosX; if (ypos) *ypos = window->wl.cursorPosY; } static GLFWbool isPointerLocked(_GLFWwindow* window); void _glfwPlatformSetCursorPos(_GLFWwindow* window, double x, double y) { if (isPointerLocked(window)) { zwp_locked_pointer_v1_set_cursor_position_hint( window->wl.pointerLock.lockedPointer, wl_fixed_from_double(x), wl_fixed_from_double(y)); wl_surface_commit(window->wl.surface); } } void _glfwPlatformSetCursorMode(_GLFWwindow* window, int mode) { _glfwPlatformSetCursor(window, window->wl.currentCursor); } const char* _glfwPlatformGetScancodeName(int scancode) { if (scancode < 0 || scancode > 255 || _glfw.wl.keycodes[scancode] == GLFW_KEY_UNKNOWN) { _glfwInputError(GLFW_INVALID_VALUE, "Wayland: Invalid scancode %i", scancode); return NULL; } const int key = _glfw.wl.keycodes[scancode]; const xkb_keycode_t keycode = scancode + 8; const xkb_layout_index_t layout = xkb_state_key_get_layout(_glfw.wl.xkb.state, keycode); if (layout == XKB_LAYOUT_INVALID) { _glfwInputError(GLFW_PLATFORM_ERROR, "Wayland: Failed to retrieve layout for key name"); return NULL; } const xkb_keysym_t* keysyms = NULL; xkb_keymap_key_get_syms_by_level(_glfw.wl.xkb.keymap, keycode, layout, 0, &keysyms); if (keysyms == NULL) { _glfwInputError(GLFW_PLATFORM_ERROR, "Wayland: Failed to retrieve keysym for key name"); return NULL; } const uint32_t codepoint = _glfwKeySym2Unicode(keysyms[0]); if (codepoint == GLFW_INVALID_CODEPOINT) { _glfwInputError(GLFW_PLATFORM_ERROR, "Wayland: Failed to retrieve codepoint for key name"); return NULL; } const size_t count = _glfwEncodeUTF8(_glfw.wl.keynames[key], codepoint); if (count == 0) { _glfwInputError(GLFW_PLATFORM_ERROR, "Wayland: Failed to encode codepoint for key name"); return NULL; } _glfw.wl.keynames[key][count] = '\0'; return _glfw.wl.keynames[key]; } int _glfwPlatformGetKeyScancode(int key) { return _glfw.wl.scancodes[key]; } int _glfwPlatformCreateCursor(_GLFWcursor* cursor, const GLFWimage* image, int xhot, int yhot) { cursor->wl.buffer = createShmBuffer(image); if (!cursor->wl.buffer) return GLFW_FALSE; cursor->wl.width = image->width; cursor->wl.height = image->height; cursor->wl.xhot = xhot; cursor->wl.yhot = yhot; return GLFW_TRUE; } int _glfwPlatformCreateStandardCursor(_GLFWcursor* cursor, int shape) { struct wl_cursor* standardCursor; standardCursor = wl_cursor_theme_get_cursor(_glfw.wl.cursorTheme, translateCursorShape(shape)); if (!standardCursor) { _glfwInputError(GLFW_PLATFORM_ERROR, "Wayland: Standard cursor \"%s\" not found", translateCursorShape(shape)); return GLFW_FALSE; } cursor->wl.cursor = standardCursor; cursor->wl.currentImage = 0; if (_glfw.wl.cursorThemeHiDPI) { standardCursor = wl_cursor_theme_get_cursor(_glfw.wl.cursorThemeHiDPI, translateCursorShape(shape)); cursor->wl.cursorHiDPI = standardCursor; } return GLFW_TRUE; } void _glfwPlatformDestroyCursor(_GLFWcursor* cursor) { // If it's a standard cursor we don't need to do anything here if (cursor->wl.cursor) return; if (cursor->wl.buffer) wl_buffer_destroy(cursor->wl.buffer); } static void relativePointerHandleRelativeMotion(void* data, struct zwp_relative_pointer_v1* pointer, uint32_t timeHi, uint32_t timeLo, wl_fixed_t dx, wl_fixed_t dy, wl_fixed_t dxUnaccel, wl_fixed_t dyUnaccel) { _GLFWwindow* window = data; double xpos = window->virtualCursorPosX; double ypos = window->virtualCursorPosY; if (window->cursorMode != GLFW_CURSOR_DISABLED) return; if (window->rawMouseMotion) { xpos += wl_fixed_to_double(dxUnaccel); ypos += wl_fixed_to_double(dyUnaccel); } else { xpos += wl_fixed_to_double(dx); ypos += wl_fixed_to_double(dy); } _glfwInputCursorPos(window, xpos, ypos); } static const struct zwp_relative_pointer_v1_listener relativePointerListener = { relativePointerHandleRelativeMotion }; static void lockedPointerHandleLocked(void* data, struct zwp_locked_pointer_v1* lockedPointer) { } static void unlockPointer(_GLFWwindow* window) { struct zwp_relative_pointer_v1* relativePointer = window->wl.pointerLock.relativePointer; struct zwp_locked_pointer_v1* lockedPointer = window->wl.pointerLock.lockedPointer; zwp_relative_pointer_v1_destroy(relativePointer); zwp_locked_pointer_v1_destroy(lockedPointer); window->wl.pointerLock.relativePointer = NULL; window->wl.pointerLock.lockedPointer = NULL; } static void lockPointer(_GLFWwindow* window); static void lockedPointerHandleUnlocked(void* data, struct zwp_locked_pointer_v1* lockedPointer) { } static const struct zwp_locked_pointer_v1_listener lockedPointerListener = { lockedPointerHandleLocked, lockedPointerHandleUnlocked }; static void lockPointer(_GLFWwindow* window) { struct zwp_relative_pointer_v1* relativePointer; struct zwp_locked_pointer_v1* lockedPointer; if (!_glfw.wl.relativePointerManager) { _glfwInputError(GLFW_PLATFORM_ERROR, "Wayland: no relative pointer manager"); return; } relativePointer = zwp_relative_pointer_manager_v1_get_relative_pointer( _glfw.wl.relativePointerManager, _glfw.wl.pointer); zwp_relative_pointer_v1_add_listener(relativePointer, &relativePointerListener, window); lockedPointer = zwp_pointer_constraints_v1_lock_pointer( _glfw.wl.pointerConstraints, window->wl.surface, _glfw.wl.pointer, NULL, ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_PERSISTENT); zwp_locked_pointer_v1_add_listener(lockedPointer, &lockedPointerListener, window); window->wl.pointerLock.relativePointer = relativePointer; window->wl.pointerLock.lockedPointer = lockedPointer; wl_pointer_set_cursor(_glfw.wl.pointer, _glfw.wl.pointerEnterSerial, NULL, 0, 0); } static GLFWbool isPointerLocked(_GLFWwindow* window) { return window->wl.pointerLock.lockedPointer != NULL; } void _glfwPlatformSetCursor(_GLFWwindow* window, _GLFWcursor* cursor) { struct wl_cursor* defaultCursor; struct wl_cursor* defaultCursorHiDPI = NULL; if (!_glfw.wl.pointer) return; window->wl.currentCursor = cursor; // If we're not in the correct window just save the cursor // the next time the pointer enters the window the cursor will change if (window != _glfw.wl.pointerFocus || window->wl.decorations.focus != mainWindow) return; // Unlock possible pointer lock if no longer disabled. if (window->cursorMode != GLFW_CURSOR_DISABLED && isPointerLocked(window)) unlockPointer(window); if (window->cursorMode == GLFW_CURSOR_NORMAL) { if (cursor) setCursorImage(window, &cursor->wl); else { defaultCursor = wl_cursor_theme_get_cursor(_glfw.wl.cursorTheme, "left_ptr"); if (!defaultCursor) { _glfwInputError(GLFW_PLATFORM_ERROR, "Wayland: Standard cursor not found"); return; } if (_glfw.wl.cursorThemeHiDPI) defaultCursorHiDPI = wl_cursor_theme_get_cursor(_glfw.wl.cursorThemeHiDPI, "left_ptr"); _GLFWcursorWayland cursorWayland = { defaultCursor, defaultCursorHiDPI, NULL, 0, 0, 0, 0, 0 }; setCursorImage(window, &cursorWayland); } } else if (window->cursorMode == GLFW_CURSOR_DISABLED) { if (!isPointerLocked(window)) lockPointer(window); } else if (window->cursorMode == GLFW_CURSOR_HIDDEN) { wl_pointer_set_cursor(_glfw.wl.pointer, _glfw.wl.pointerEnterSerial, NULL, 0, 0); } } static void dataSourceHandleTarget(void* data, struct wl_data_source* dataSource, const char* mimeType) { if (_glfw.wl.dataSource != dataSource) { _glfwInputError(GLFW_PLATFORM_ERROR, "Wayland: Unknown clipboard data source"); return; } } static void dataSourceHandleSend(void* data, struct wl_data_source* dataSource, const char* mimeType, int fd) { const char* string = _glfw.wl.clipboardSendString; size_t len = _glfw.wl.clipboardSendSize; int ret; if (_glfw.wl.dataSource != dataSource) { _glfwInputError(GLFW_PLATFORM_ERROR, "Wayland: Unknown clipboard data source"); return; } if (!string) { _glfwInputError(GLFW_PLATFORM_ERROR, "Wayland: Copy requested from an invalid string"); return; } if (strcmp(mimeType, "text/plain;charset=utf-8") != 0) { _glfwInputError(GLFW_PLATFORM_ERROR, "Wayland: Wrong MIME type asked from clipboard"); close(fd); return; } while (len > 0) { ret = write(fd, string, len); if (ret == -1 && errno == EINTR) continue; if (ret == -1) { // TODO: also report errno maybe. _glfwInputError(GLFW_PLATFORM_ERROR, "Wayland: Error while writing the clipboard"); close(fd); return; } len -= ret; } close(fd); } static void dataSourceHandleCancelled(void* data, struct wl_data_source* dataSource) { wl_data_source_destroy(dataSource); if (_glfw.wl.dataSource != dataSource) { _glfwInputError(GLFW_PLATFORM_ERROR, "Wayland: Unknown clipboard data source"); return; } _glfw.wl.dataSource = NULL; } static const struct wl_data_source_listener dataSourceListener = { dataSourceHandleTarget, dataSourceHandleSend, dataSourceHandleCancelled, }; void _glfwPlatformSetClipboardString(const char* string) { if (_glfw.wl.dataSource) { wl_data_source_destroy(_glfw.wl.dataSource); _glfw.wl.dataSource = NULL; } if (_glfw.wl.clipboardSendString) { free(_glfw.wl.clipboardSendString); _glfw.wl.clipboardSendString = NULL; } _glfw.wl.clipboardSendString = strdup(string); if (!_glfw.wl.clipboardSendString) { _glfwInputError(GLFW_PLATFORM_ERROR, "Wayland: Impossible to allocate clipboard string"); return; } _glfw.wl.clipboardSendSize = strlen(string); _glfw.wl.dataSource = wl_data_device_manager_create_data_source(_glfw.wl.dataDeviceManager); if (!_glfw.wl.dataSource) { _glfwInputError(GLFW_PLATFORM_ERROR, "Wayland: Impossible to create clipboard source"); free(_glfw.wl.clipboardSendString); return; } wl_data_source_add_listener(_glfw.wl.dataSource, &dataSourceListener, NULL); wl_data_source_offer(_glfw.wl.dataSource, "text/plain;charset=utf-8"); wl_data_device_set_selection(_glfw.wl.dataDevice, _glfw.wl.dataSource, _glfw.wl.serial); } static GLFWbool growClipboardString(void) { char* clipboard = _glfw.wl.clipboardString; clipboard = realloc(clipboard, _glfw.wl.clipboardSize * 2); if (!clipboard) { _glfwInputError(GLFW_OUT_OF_MEMORY, "Wayland: Impossible to grow clipboard string"); return GLFW_FALSE; } _glfw.wl.clipboardString = clipboard; _glfw.wl.clipboardSize = _glfw.wl.clipboardSize * 2; return GLFW_TRUE; } const char* _glfwPlatformGetClipboardString(void) { int fds[2]; int ret; size_t len = 0; if (!_glfw.wl.dataOffer) { _glfwInputError(GLFW_FORMAT_UNAVAILABLE, "No clipboard data has been sent yet"); return NULL; } ret = pipe2(fds, O_CLOEXEC); if (ret < 0) { // TODO: also report errno maybe? _glfwInputError(GLFW_PLATFORM_ERROR, "Wayland: Impossible to create clipboard pipe fds"); return NULL; } wl_data_offer_receive(_glfw.wl.dataOffer, "text/plain;charset=utf-8", fds[1]); close(fds[1]); // XXX: this is a huge hack, this function shouldn’t be synchronous! handleEvents(NULL); for (;;) { // Grow the clipboard if we need to paste something bigger, there is no // shrink operation yet. if (len + 4096 > _glfw.wl.clipboardSize) { if (!growClipboardString()) { close(fds[0]); return NULL; } } // Then read from the fd to the clipboard, handling all known errors. ret = read(fds[0], _glfw.wl.clipboardString + len, 4096); if (ret == 0) break; if (ret == -1 && errno == EINTR) continue; if (ret == -1) { // TODO: also report errno maybe. _glfwInputError(GLFW_PLATFORM_ERROR, "Wayland: Impossible to read from clipboard fd"); close(fds[0]); return NULL; } len += ret; } close(fds[0]); if (len + 1 > _glfw.wl.clipboardSize) { if (!growClipboardString()) return NULL; } _glfw.wl.clipboardString[len] = '\0'; return _glfw.wl.clipboardString; } void _glfwPlatformGetRequiredInstanceExtensions(char** extensions) { if (!_glfw.vk.KHR_surface || !_glfw.vk.KHR_wayland_surface) return; extensions[0] = "VK_KHR_surface"; extensions[1] = "VK_KHR_wayland_surface"; } int _glfwPlatformGetPhysicalDevicePresentationSupport(VkInstance instance, VkPhysicalDevice device, uint32_t queuefamily) { PFN_vkGetPhysicalDeviceWaylandPresentationSupportKHR vkGetPhysicalDeviceWaylandPresentationSupportKHR = (PFN_vkGetPhysicalDeviceWaylandPresentationSupportKHR) vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceWaylandPresentationSupportKHR"); if (!vkGetPhysicalDeviceWaylandPresentationSupportKHR) { _glfwInputError(GLFW_API_UNAVAILABLE, "Wayland: Vulkan instance missing VK_KHR_wayland_surface extension"); return VK_NULL_HANDLE; } return vkGetPhysicalDeviceWaylandPresentationSupportKHR(device, queuefamily, _glfw.wl.display); } VkResult _glfwPlatformCreateWindowSurface(VkInstance instance, _GLFWwindow* window, const VkAllocationCallbacks* allocator, VkSurfaceKHR* surface) { VkResult err; VkWaylandSurfaceCreateInfoKHR sci; PFN_vkCreateWaylandSurfaceKHR vkCreateWaylandSurfaceKHR; vkCreateWaylandSurfaceKHR = (PFN_vkCreateWaylandSurfaceKHR) vkGetInstanceProcAddr(instance, "vkCreateWaylandSurfaceKHR"); if (!vkCreateWaylandSurfaceKHR) { _glfwInputError(GLFW_API_UNAVAILABLE, "Wayland: Vulkan instance missing VK_KHR_wayland_surface extension"); return VK_ERROR_EXTENSION_NOT_PRESENT; } memset(&sci, 0, sizeof(sci)); sci.sType = VK_STRUCTURE_TYPE_WAYLAND_SURFACE_CREATE_INFO_KHR; sci.display = _glfw.wl.display; sci.surface = window->wl.surface; err = vkCreateWaylandSurfaceKHR(instance, &sci, allocator, surface); if (err) { _glfwInputError(GLFW_PLATFORM_ERROR, "Wayland: Failed to create Vulkan surface: %s", _glfwGetVulkanResultString(err)); } return err; } ////////////////////////////////////////////////////////////////////////// ////// GLFW native API ////// ////////////////////////////////////////////////////////////////////////// GLFWAPI struct wl_display* glfwGetWaylandDisplay(void) { _GLFW_REQUIRE_INIT_OR_RETURN(NULL); return _glfw.wl.display; } GLFWAPI struct wl_surface* glfwGetWaylandWindow(GLFWwindow* handle) { _GLFWwindow* window = (_GLFWwindow*) handle; _GLFW_REQUIRE_INIT_OR_RETURN(NULL); return window->wl.surface; } #endif #endif #ifdef _GLFW_COCOA #ifndef HEADER_GUARD_COCOA_INIT_M #define HEADER_GUARD_COCOA_INIT_M //======================================================================== // GLFW 3.3.7 macOS - www.glfw.org //------------------------------------------------------------------------ // Copyright (c) 2009-2019 Camilla Löwy // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would // be appreciated but is not required. // // 2. Altered source versions must be plainly marked as such, and must not // be misrepresented as being the original software. // // 3. This notice may not be removed or altered from any source // distribution. // //======================================================================== // It is fine to use C99 in this file because it will not be built with VS //======================================================================== #include // For MAXPATHLEN // Needed for _NSGetProgname #include // Change to our application bundle's resources directory, if present // static void changeToResourcesDirectory(void) { char resourcesPath[MAXPATHLEN]; CFBundleRef bundle = CFBundleGetMainBundle(); if (!bundle) return; CFURLRef resourcesURL = CFBundleCopyResourcesDirectoryURL(bundle); CFStringRef last = CFURLCopyLastPathComponent(resourcesURL); if (CFStringCompare(CFSTR("Resources"), last, 0) != kCFCompareEqualTo) { CFRelease(last); CFRelease(resourcesURL); return; } CFRelease(last); if (!CFURLGetFileSystemRepresentation(resourcesURL, true, (UInt8*) resourcesPath, MAXPATHLEN)) { CFRelease(resourcesURL); return; } CFRelease(resourcesURL); chdir(resourcesPath); } // Set up the menu bar (manually) // This is nasty, nasty stuff -- calls to undocumented semi-private APIs that // could go away at any moment, lots of stuff that really should be // localize(d|able), etc. Add a nib to save us this horror. // static void createMenuBar(void) { size_t i; NSString* appName = nil; NSDictionary* bundleInfo = [[NSBundle mainBundle] infoDictionary]; NSString* nameKeys[] = { @"CFBundleDisplayName", @"CFBundleName", @"CFBundleExecutable", }; // Try to figure out what the calling application is called for (i = 0; i < sizeof(nameKeys) / sizeof(nameKeys[0]); i++) { id name = bundleInfo[nameKeys[i]]; if (name && [name isKindOfClass:[NSString class]] && ![name isEqualToString:@""]) { appName = name; break; } } if (!appName) { char** progname = _NSGetProgname(); if (progname && *progname) appName = @(*progname); else appName = @"GLFW Application"; } NSMenu* bar = [[NSMenu alloc] init]; [NSApp setMainMenu:bar]; NSMenuItem* appMenuItem = [bar addItemWithTitle:@"" action:NULL keyEquivalent:@""]; NSMenu* appMenu = [[NSMenu alloc] init]; [appMenuItem setSubmenu:appMenu]; [appMenu addItemWithTitle:[NSString stringWithFormat:@"About %@", appName] action:@selector(orderFrontStandardAboutPanel:) keyEquivalent:@""]; [appMenu addItem:[NSMenuItem separatorItem]]; NSMenu* servicesMenu = [[NSMenu alloc] init]; [NSApp setServicesMenu:servicesMenu]; [[appMenu addItemWithTitle:@"Services" action:NULL keyEquivalent:@""] setSubmenu:servicesMenu]; [servicesMenu release]; [appMenu addItem:[NSMenuItem separatorItem]]; [appMenu addItemWithTitle:[NSString stringWithFormat:@"Hide %@", appName] action:@selector(hide:) keyEquivalent:@"h"]; [[appMenu addItemWithTitle:@"Hide Others" action:@selector(hideOtherApplications:) keyEquivalent:@"h"] setKeyEquivalentModifierMask:NSEventModifierFlagOption | NSEventModifierFlagCommand]; [appMenu addItemWithTitle:@"Show All" action:@selector(unhideAllApplications:) keyEquivalent:@""]; [appMenu addItem:[NSMenuItem separatorItem]]; [appMenu addItemWithTitle:[NSString stringWithFormat:@"Quit %@", appName] action:@selector(terminate:) keyEquivalent:@"q"]; NSMenuItem* windowMenuItem = [bar addItemWithTitle:@"" action:NULL keyEquivalent:@""]; [bar release]; NSMenu* windowMenu = [[NSMenu alloc] initWithTitle:@"Window"]; [NSApp setWindowsMenu:windowMenu]; [windowMenuItem setSubmenu:windowMenu]; [windowMenu addItemWithTitle:@"Minimize" action:@selector(performMiniaturize:) keyEquivalent:@"m"]; [windowMenu addItemWithTitle:@"Zoom" action:@selector(performZoom:) keyEquivalent:@""]; [windowMenu addItem:[NSMenuItem separatorItem]]; [windowMenu addItemWithTitle:@"Bring All to Front" action:@selector(arrangeInFront:) keyEquivalent:@""]; // TODO: Make this appear at the bottom of the menu (for consistency) [windowMenu addItem:[NSMenuItem separatorItem]]; [[windowMenu addItemWithTitle:@"Enter Full Screen" action:@selector(toggleFullScreen:) keyEquivalent:@"f"] setKeyEquivalentModifierMask:NSEventModifierFlagControl | NSEventModifierFlagCommand]; // Prior to Snow Leopard, we need to use this oddly-named semi-private API // to get the application menu working properly. SEL setAppleMenuSelector = NSSelectorFromString(@"setAppleMenu:"); [NSApp performSelector:setAppleMenuSelector withObject:appMenu]; } // Create key code translation tables // static void createKeyTables(void) { int scancode; memset(_glfw.ns.keycodes, -1, sizeof(_glfw.ns.keycodes)); memset(_glfw.ns.scancodes, -1, sizeof(_glfw.ns.scancodes)); _glfw.ns.keycodes[0x1D] = GLFW_KEY_0; _glfw.ns.keycodes[0x12] = GLFW_KEY_1; _glfw.ns.keycodes[0x13] = GLFW_KEY_2; _glfw.ns.keycodes[0x14] = GLFW_KEY_3; _glfw.ns.keycodes[0x15] = GLFW_KEY_4; _glfw.ns.keycodes[0x17] = GLFW_KEY_5; _glfw.ns.keycodes[0x16] = GLFW_KEY_6; _glfw.ns.keycodes[0x1A] = GLFW_KEY_7; _glfw.ns.keycodes[0x1C] = GLFW_KEY_8; _glfw.ns.keycodes[0x19] = GLFW_KEY_9; _glfw.ns.keycodes[0x00] = GLFW_KEY_A; _glfw.ns.keycodes[0x0B] = GLFW_KEY_B; _glfw.ns.keycodes[0x08] = GLFW_KEY_C; _glfw.ns.keycodes[0x02] = GLFW_KEY_D; _glfw.ns.keycodes[0x0E] = GLFW_KEY_E; _glfw.ns.keycodes[0x03] = GLFW_KEY_F; _glfw.ns.keycodes[0x05] = GLFW_KEY_G; _glfw.ns.keycodes[0x04] = GLFW_KEY_H; _glfw.ns.keycodes[0x22] = GLFW_KEY_I; _glfw.ns.keycodes[0x26] = GLFW_KEY_J; _glfw.ns.keycodes[0x28] = GLFW_KEY_K; _glfw.ns.keycodes[0x25] = GLFW_KEY_L; _glfw.ns.keycodes[0x2E] = GLFW_KEY_M; _glfw.ns.keycodes[0x2D] = GLFW_KEY_N; _glfw.ns.keycodes[0x1F] = GLFW_KEY_O; _glfw.ns.keycodes[0x23] = GLFW_KEY_P; _glfw.ns.keycodes[0x0C] = GLFW_KEY_Q; _glfw.ns.keycodes[0x0F] = GLFW_KEY_R; _glfw.ns.keycodes[0x01] = GLFW_KEY_S; _glfw.ns.keycodes[0x11] = GLFW_KEY_T; _glfw.ns.keycodes[0x20] = GLFW_KEY_U; _glfw.ns.keycodes[0x09] = GLFW_KEY_V; _glfw.ns.keycodes[0x0D] = GLFW_KEY_W; _glfw.ns.keycodes[0x07] = GLFW_KEY_X; _glfw.ns.keycodes[0x10] = GLFW_KEY_Y; _glfw.ns.keycodes[0x06] = GLFW_KEY_Z; _glfw.ns.keycodes[0x27] = GLFW_KEY_APOSTROPHE; _glfw.ns.keycodes[0x2A] = GLFW_KEY_BACKSLASH; _glfw.ns.keycodes[0x2B] = GLFW_KEY_COMMA; _glfw.ns.keycodes[0x18] = GLFW_KEY_EQUAL; _glfw.ns.keycodes[0x32] = GLFW_KEY_GRAVE_ACCENT; _glfw.ns.keycodes[0x21] = GLFW_KEY_LEFT_BRACKET; _glfw.ns.keycodes[0x1B] = GLFW_KEY_MINUS; _glfw.ns.keycodes[0x2F] = GLFW_KEY_PERIOD; _glfw.ns.keycodes[0x1E] = GLFW_KEY_RIGHT_BRACKET; _glfw.ns.keycodes[0x29] = GLFW_KEY_SEMICOLON; _glfw.ns.keycodes[0x2C] = GLFW_KEY_SLASH; _glfw.ns.keycodes[0x0A] = GLFW_KEY_WORLD_1; _glfw.ns.keycodes[0x33] = GLFW_KEY_BACKSPACE; _glfw.ns.keycodes[0x39] = GLFW_KEY_CAPS_LOCK; _glfw.ns.keycodes[0x75] = GLFW_KEY_DELETE; _glfw.ns.keycodes[0x7D] = GLFW_KEY_DOWN; _glfw.ns.keycodes[0x77] = GLFW_KEY_END; _glfw.ns.keycodes[0x24] = GLFW_KEY_ENTER; _glfw.ns.keycodes[0x35] = GLFW_KEY_ESCAPE; _glfw.ns.keycodes[0x7A] = GLFW_KEY_F1; _glfw.ns.keycodes[0x78] = GLFW_KEY_F2; _glfw.ns.keycodes[0x63] = GLFW_KEY_F3; _glfw.ns.keycodes[0x76] = GLFW_KEY_F4; _glfw.ns.keycodes[0x60] = GLFW_KEY_F5; _glfw.ns.keycodes[0x61] = GLFW_KEY_F6; _glfw.ns.keycodes[0x62] = GLFW_KEY_F7; _glfw.ns.keycodes[0x64] = GLFW_KEY_F8; _glfw.ns.keycodes[0x65] = GLFW_KEY_F9; _glfw.ns.keycodes[0x6D] = GLFW_KEY_F10; _glfw.ns.keycodes[0x67] = GLFW_KEY_F11; _glfw.ns.keycodes[0x6F] = GLFW_KEY_F12; _glfw.ns.keycodes[0x69] = GLFW_KEY_F13; _glfw.ns.keycodes[0x6B] = GLFW_KEY_F14; _glfw.ns.keycodes[0x71] = GLFW_KEY_F15; _glfw.ns.keycodes[0x6A] = GLFW_KEY_F16; _glfw.ns.keycodes[0x40] = GLFW_KEY_F17; _glfw.ns.keycodes[0x4F] = GLFW_KEY_F18; _glfw.ns.keycodes[0x50] = GLFW_KEY_F19; _glfw.ns.keycodes[0x5A] = GLFW_KEY_F20; _glfw.ns.keycodes[0x73] = GLFW_KEY_HOME; _glfw.ns.keycodes[0x72] = GLFW_KEY_INSERT; _glfw.ns.keycodes[0x7B] = GLFW_KEY_LEFT; _glfw.ns.keycodes[0x3A] = GLFW_KEY_LEFT_ALT; _glfw.ns.keycodes[0x3B] = GLFW_KEY_LEFT_CONTROL; _glfw.ns.keycodes[0x38] = GLFW_KEY_LEFT_SHIFT; _glfw.ns.keycodes[0x37] = GLFW_KEY_LEFT_SUPER; _glfw.ns.keycodes[0x6E] = GLFW_KEY_MENU; _glfw.ns.keycodes[0x47] = GLFW_KEY_NUM_LOCK; _glfw.ns.keycodes[0x79] = GLFW_KEY_PAGE_DOWN; _glfw.ns.keycodes[0x74] = GLFW_KEY_PAGE_UP; _glfw.ns.keycodes[0x7C] = GLFW_KEY_RIGHT; _glfw.ns.keycodes[0x3D] = GLFW_KEY_RIGHT_ALT; _glfw.ns.keycodes[0x3E] = GLFW_KEY_RIGHT_CONTROL; _glfw.ns.keycodes[0x3C] = GLFW_KEY_RIGHT_SHIFT; _glfw.ns.keycodes[0x36] = GLFW_KEY_RIGHT_SUPER; _glfw.ns.keycodes[0x31] = GLFW_KEY_SPACE; _glfw.ns.keycodes[0x30] = GLFW_KEY_TAB; _glfw.ns.keycodes[0x7E] = GLFW_KEY_UP; _glfw.ns.keycodes[0x52] = GLFW_KEY_KP_0; _glfw.ns.keycodes[0x53] = GLFW_KEY_KP_1; _glfw.ns.keycodes[0x54] = GLFW_KEY_KP_2; _glfw.ns.keycodes[0x55] = GLFW_KEY_KP_3; _glfw.ns.keycodes[0x56] = GLFW_KEY_KP_4; _glfw.ns.keycodes[0x57] = GLFW_KEY_KP_5; _glfw.ns.keycodes[0x58] = GLFW_KEY_KP_6; _glfw.ns.keycodes[0x59] = GLFW_KEY_KP_7; _glfw.ns.keycodes[0x5B] = GLFW_KEY_KP_8; _glfw.ns.keycodes[0x5C] = GLFW_KEY_KP_9; _glfw.ns.keycodes[0x45] = GLFW_KEY_KP_ADD; _glfw.ns.keycodes[0x41] = GLFW_KEY_KP_DECIMAL; _glfw.ns.keycodes[0x4B] = GLFW_KEY_KP_DIVIDE; _glfw.ns.keycodes[0x4C] = GLFW_KEY_KP_ENTER; _glfw.ns.keycodes[0x51] = GLFW_KEY_KP_EQUAL; _glfw.ns.keycodes[0x43] = GLFW_KEY_KP_MULTIPLY; _glfw.ns.keycodes[0x4E] = GLFW_KEY_KP_SUBTRACT; for (scancode = 0; scancode < 256; scancode++) { // Store the reverse translation for faster key name lookup if (_glfw.ns.keycodes[scancode] >= 0) _glfw.ns.scancodes[_glfw.ns.keycodes[scancode]] = scancode; } } // Retrieve Unicode data for the current keyboard layout // static GLFWbool updateUnicodeDataNS(void) { if (_glfw.ns.inputSource) { CFRelease(_glfw.ns.inputSource); _glfw.ns.inputSource = NULL; _glfw.ns.unicodeData = nil; } _glfw.ns.inputSource = TISCopyCurrentKeyboardLayoutInputSource(); if (!_glfw.ns.inputSource) { _glfwInputError(GLFW_PLATFORM_ERROR, "Cocoa: Failed to retrieve keyboard layout input source"); return GLFW_FALSE; } _glfw.ns.unicodeData = TISGetInputSourceProperty(_glfw.ns.inputSource, kTISPropertyUnicodeKeyLayoutData); if (!_glfw.ns.unicodeData) { _glfwInputError(GLFW_PLATFORM_ERROR, "Cocoa: Failed to retrieve keyboard layout Unicode data"); return GLFW_FALSE; } return GLFW_TRUE; } // Load HIToolbox.framework and the TIS symbols we need from it // static GLFWbool initializeTIS(void) { // This works only because Cocoa has already loaded it properly _glfw.ns.tis.bundle = CFBundleGetBundleWithIdentifier(CFSTR("com.apple.HIToolbox")); if (!_glfw.ns.tis.bundle) { _glfwInputError(GLFW_PLATFORM_ERROR, "Cocoa: Failed to load HIToolbox.framework"); return GLFW_FALSE; } CFStringRef* kPropertyUnicodeKeyLayoutData = CFBundleGetDataPointerForName(_glfw.ns.tis.bundle, CFSTR("kTISPropertyUnicodeKeyLayoutData")); _glfw.ns.tis.CopyCurrentKeyboardLayoutInputSource = CFBundleGetFunctionPointerForName(_glfw.ns.tis.bundle, CFSTR("TISCopyCurrentKeyboardLayoutInputSource")); _glfw.ns.tis.GetInputSourceProperty = CFBundleGetFunctionPointerForName(_glfw.ns.tis.bundle, CFSTR("TISGetInputSourceProperty")); _glfw.ns.tis.GetKbdType = CFBundleGetFunctionPointerForName(_glfw.ns.tis.bundle, CFSTR("LMGetKbdType")); if (!kPropertyUnicodeKeyLayoutData || !TISCopyCurrentKeyboardLayoutInputSource || !TISGetInputSourceProperty || !LMGetKbdType) { _glfwInputError(GLFW_PLATFORM_ERROR, "Cocoa: Failed to load TIS API symbols"); return GLFW_FALSE; } _glfw.ns.tis.kPropertyUnicodeKeyLayoutData = *kPropertyUnicodeKeyLayoutData; return updateUnicodeDataNS(); } @interface GLFWHelper : NSObject @end @implementation GLFWHelper - (void)selectedKeyboardInputSourceChanged:(NSObject* )object { updateUnicodeDataNS(); } - (void)doNothing:(id)object { } @end // GLFWHelper @interface GLFWApplicationDelegate : NSObject @end @implementation GLFWApplicationDelegate - (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender { _GLFWwindow* window; for (window = _glfw.windowListHead; window; window = window->next) _glfwInputWindowCloseRequest(window); return NSTerminateCancel; } - (void)applicationDidChangeScreenParameters:(NSNotification *) notification { _GLFWwindow* window; for (window = _glfw.windowListHead; window; window = window->next) { if (window->context.client != GLFW_NO_API) [window->context.nsgl.object update]; } _glfwPollMonitorsNS(); } - (void)applicationWillFinishLaunching:(NSNotification *)notification { if (_glfw.hints.init.ns.menubar) { // Menu bar setup must go between sharedApplication and finishLaunching // in order to properly emulate the behavior of NSApplicationMain if ([[NSBundle mainBundle] pathForResource:@"MainMenu" ofType:@"nib"]) { [[NSBundle mainBundle] loadNibNamed:@"MainMenu" owner:NSApp topLevelObjects:&_glfw.ns.nibObjects]; } else createMenuBar(); } } - (void)applicationDidFinishLaunching:(NSNotification *)notification { _glfw.ns.finishedLaunching = GLFW_TRUE; _glfwPlatformPostEmptyEvent(); // In case we are unbundled, make us a proper UI application if (_glfw.hints.init.ns.menubar) [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular]; [NSApp stop:nil]; } - (void)applicationDidHide:(NSNotification *)notification { int i; for (i = 0; i < _glfw.monitorCount; i++) _glfwRestoreVideoModeNS(_glfw.monitors[i]); } @end // GLFWApplicationDelegate ////////////////////////////////////////////////////////////////////////// ////// GLFW internal API ////// ////////////////////////////////////////////////////////////////////////// void* _glfwLoadLocalVulkanLoaderNS(void) { CFBundleRef bundle = CFBundleGetMainBundle(); if (!bundle) return NULL; CFURLRef url = CFBundleCopyAuxiliaryExecutableURL(bundle, CFSTR("libvulkan.1.dylib")); if (!url) return NULL; char path[PATH_MAX]; void* handle = NULL; if (CFURLGetFileSystemRepresentation(url, true, (UInt8*) path, sizeof(path) - 1)) handle = _glfw_dlopen(path); CFRelease(url); return handle; } ////////////////////////////////////////////////////////////////////////// ////// GLFW platform API ////// ////////////////////////////////////////////////////////////////////////// int _glfwPlatformInit(void) { @autoreleasepool { _glfw.ns.helper = [[GLFWHelper alloc] init]; [NSThread detachNewThreadSelector:@selector(doNothing:) toTarget:_glfw.ns.helper withObject:nil]; if (NSApp) _glfw.ns.finishedLaunching = GLFW_TRUE; [NSApplication sharedApplication]; _glfw.ns.delegate = [[GLFWApplicationDelegate alloc] init]; if (_glfw.ns.delegate == nil) { _glfwInputError(GLFW_PLATFORM_ERROR, "Cocoa: Failed to create application delegate"); return GLFW_FALSE; } [NSApp setDelegate:_glfw.ns.delegate]; NSEvent* (^block)(NSEvent*) = ^ NSEvent* (NSEvent* event) { if ([event modifierFlags] & NSEventModifierFlagCommand) [[NSApp keyWindow] sendEvent:event]; return event; }; _glfw.ns.keyUpMonitor = [NSEvent addLocalMonitorForEventsMatchingMask:NSEventMaskKeyUp handler:block]; if (_glfw.hints.init.ns.chdir) changeToResourcesDirectory(); // Press and Hold prevents some keys from emitting repeated characters NSDictionary* defaults = @{@"ApplePressAndHoldEnabled":@NO}; [[NSUserDefaults standardUserDefaults] registerDefaults:defaults]; [[NSNotificationCenter defaultCenter] addObserver:_glfw.ns.helper selector:@selector(selectedKeyboardInputSourceChanged:) name:NSTextInputContextKeyboardSelectionDidChangeNotification object:nil]; createKeyTables(); _glfw.ns.eventSource = CGEventSourceCreate(kCGEventSourceStateHIDSystemState); if (!_glfw.ns.eventSource) return GLFW_FALSE; CGEventSourceSetLocalEventsSuppressionInterval(_glfw.ns.eventSource, 0.0); if (!initializeTIS()) return GLFW_FALSE; _glfwInitTimerNS(); _glfwInitJoysticksNS(); _glfwPollMonitorsNS(); return GLFW_TRUE; } // autoreleasepool } void _glfwPlatformTerminate(void) { @autoreleasepool { if (_glfw.ns.inputSource) { CFRelease(_glfw.ns.inputSource); _glfw.ns.inputSource = NULL; _glfw.ns.unicodeData = nil; } if (_glfw.ns.eventSource) { CFRelease(_glfw.ns.eventSource); _glfw.ns.eventSource = NULL; } if (_glfw.ns.delegate) { [NSApp setDelegate:nil]; [_glfw.ns.delegate release]; _glfw.ns.delegate = nil; } if (_glfw.ns.helper) { [[NSNotificationCenter defaultCenter] removeObserver:_glfw.ns.helper name:NSTextInputContextKeyboardSelectionDidChangeNotification object:nil]; [[NSNotificationCenter defaultCenter] removeObserver:_glfw.ns.helper]; [_glfw.ns.helper release]; _glfw.ns.helper = nil; } if (_glfw.ns.keyUpMonitor) [NSEvent removeMonitor:_glfw.ns.keyUpMonitor]; free(_glfw.ns.clipboardString); _glfwTerminateNSGL(); _glfwTerminateJoysticksNS(); } // autoreleasepool } const char* _glfwPlatformGetVersionString(void) { return _GLFW_VERSION_NUMBER " Cocoa NSGL EGL OSMesa" #if defined(_GLFW_BUILD_DLL) " dynamic" #endif ; } #endif #ifndef HEADER_GUARD_NSGL_CONTEXT_M #define HEADER_GUARD_NSGL_CONTEXT_M //======================================================================== // GLFW 3.3.7 macOS - www.glfw.org //------------------------------------------------------------------------ // Copyright (c) 2009-2019 Camilla Löwy // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would // be appreciated but is not required. // // 2. Altered source versions must be plainly marked as such, and must not // be misrepresented as being the original software. // // 3. This notice may not be removed or altered from any source // distribution. // //======================================================================== // It is fine to use C99 in this file because it will not be built with VS //======================================================================== #include #include static void makeContextCurrentNSGL(_GLFWwindow* window) { @autoreleasepool { if (window) [window->context.nsgl.object makeCurrentContext]; else [NSOpenGLContext clearCurrentContext]; _glfwPlatformSetTls(&_glfw.contextSlot, window); } // autoreleasepool } static void swapBuffersNSGL(_GLFWwindow* window) { @autoreleasepool { // HACK: Simulate vsync with usleep as NSGL swap interval does not apply to // windows with a non-visible occlusion state if (window->ns.occluded) { int interval = 0; [window->context.nsgl.object getValues:&interval forParameter:NSOpenGLContextParameterSwapInterval]; if (interval > 0) { const double framerate = 60.0; const uint64_t frequency = _glfwPlatformGetTimerFrequency(); const uint64_t value = _glfwPlatformGetTimerValue(); const double elapsed = value / (double) frequency; const double period = 1.0 / framerate; const double delay = period - fmod(elapsed, period); usleep(floorl(delay * 1e6)); } } [window->context.nsgl.object flushBuffer]; } // autoreleasepool } static void swapIntervalNSGL(int interval) { @autoreleasepool { _GLFWwindow* window = _glfwPlatformGetTls(&_glfw.contextSlot); if (window) { [window->context.nsgl.object setValues:&interval forParameter:NSOpenGLContextParameterSwapInterval]; } } // autoreleasepool } static int extensionSupportedNSGL(const char* extension) { // There are no NSGL extensions return GLFW_FALSE; } static GLFWglproc getProcAddressNSGL(const char* procname) { CFStringRef symbolName = CFStringCreateWithCString(kCFAllocatorDefault, procname, kCFStringEncodingASCII); GLFWglproc symbol = CFBundleGetFunctionPointerForName(_glfw.nsgl.framework, symbolName); CFRelease(symbolName); return symbol; } static void destroyContextNSGL(_GLFWwindow* window) { @autoreleasepool { [window->context.nsgl.pixelFormat release]; window->context.nsgl.pixelFormat = nil; [window->context.nsgl.object release]; window->context.nsgl.object = nil; } // autoreleasepool } ////////////////////////////////////////////////////////////////////////// ////// GLFW internal API ////// ////////////////////////////////////////////////////////////////////////// // Initialize OpenGL support // GLFWbool _glfwInitNSGL(void) { if (_glfw.nsgl.framework) return GLFW_TRUE; _glfw.nsgl.framework = CFBundleGetBundleWithIdentifier(CFSTR("com.apple.opengl")); if (_glfw.nsgl.framework == NULL) { _glfwInputError(GLFW_API_UNAVAILABLE, "NSGL: Failed to locate OpenGL framework"); return GLFW_FALSE; } return GLFW_TRUE; } // Terminate OpenGL support // void _glfwTerminateNSGL(void) { } // Create the OpenGL context // GLFWbool _glfwCreateContextNSGL(_GLFWwindow* window, const _GLFWctxconfig* ctxconfig, const _GLFWfbconfig* fbconfig) { if (ctxconfig->client == GLFW_OPENGL_ES_API) { _glfwInputError(GLFW_API_UNAVAILABLE, "NSGL: OpenGL ES is not available on macOS"); return GLFW_FALSE; } if (ctxconfig->major > 2) { if (ctxconfig->major == 3 && ctxconfig->minor < 2) { _glfwInputError(GLFW_VERSION_UNAVAILABLE, "NSGL: The targeted version of macOS does not support OpenGL 3.0 or 3.1 but may support 3.2 and above"); return GLFW_FALSE; } if (!ctxconfig->forward || ctxconfig->profile != GLFW_OPENGL_CORE_PROFILE) { _glfwInputError(GLFW_VERSION_UNAVAILABLE, "NSGL: The targeted version of macOS only supports forward-compatible core profile contexts for OpenGL 3.2 and above"); return GLFW_FALSE; } } // Context robustness modes (GL_KHR_robustness) are not yet supported by // macOS but are not a hard constraint, so ignore and continue // Context release behaviors (GL_KHR_context_flush_control) are not yet // supported by macOS but are not a hard constraint, so ignore and continue // Debug contexts (GL_KHR_debug) are not yet supported by macOS but are not // a hard constraint, so ignore and continue // No-error contexts (GL_KHR_no_error) are not yet supported by macOS but // are not a hard constraint, so ignore and continue #define addAttrib(a) \ { \ assert((size_t) index < sizeof(attribs) / sizeof(attribs[0])); \ attribs[index++] = a; \ } #define setAttrib(a, v) { addAttrib(a); addAttrib(v); } NSOpenGLPixelFormatAttribute attribs[40]; int index = 0; addAttrib(NSOpenGLPFAAccelerated); addAttrib(NSOpenGLPFAClosestPolicy); if (ctxconfig->nsgl.offline) { addAttrib(NSOpenGLPFAAllowOfflineRenderers); // NOTE: This replaces the NSSupportsAutomaticGraphicsSwitching key in // Info.plist for unbundled applications // HACK: This assumes that NSOpenGLPixelFormat will remain // a straightforward wrapper of its CGL counterpart addAttrib(kCGLPFASupportsAutomaticGraphicsSwitching); } #if MAC_OS_X_VERSION_MAX_ALLOWED >= 101000 if (ctxconfig->major >= 4) { setAttrib(NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersion4_1Core); } else #endif /*MAC_OS_X_VERSION_MAX_ALLOWED*/ if (ctxconfig->major >= 3) { setAttrib(NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersion3_2Core); } if (ctxconfig->major <= 2) { if (fbconfig->auxBuffers != GLFW_DONT_CARE) setAttrib(NSOpenGLPFAAuxBuffers, fbconfig->auxBuffers); if (fbconfig->accumRedBits != GLFW_DONT_CARE && fbconfig->accumGreenBits != GLFW_DONT_CARE && fbconfig->accumBlueBits != GLFW_DONT_CARE && fbconfig->accumAlphaBits != GLFW_DONT_CARE) { const int accumBits = fbconfig->accumRedBits + fbconfig->accumGreenBits + fbconfig->accumBlueBits + fbconfig->accumAlphaBits; setAttrib(NSOpenGLPFAAccumSize, accumBits); } } if (fbconfig->redBits != GLFW_DONT_CARE && fbconfig->greenBits != GLFW_DONT_CARE && fbconfig->blueBits != GLFW_DONT_CARE) { int colorBits = fbconfig->redBits + fbconfig->greenBits + fbconfig->blueBits; // macOS needs non-zero color size, so set reasonable values if (colorBits == 0) colorBits = 24; else if (colorBits < 15) colorBits = 15; setAttrib(NSOpenGLPFAColorSize, colorBits); } if (fbconfig->alphaBits != GLFW_DONT_CARE) setAttrib(NSOpenGLPFAAlphaSize, fbconfig->alphaBits); if (fbconfig->depthBits != GLFW_DONT_CARE) setAttrib(NSOpenGLPFADepthSize, fbconfig->depthBits); if (fbconfig->stencilBits != GLFW_DONT_CARE) setAttrib(NSOpenGLPFAStencilSize, fbconfig->stencilBits); if (fbconfig->stereo) { #if MAC_OS_X_VERSION_MAX_ALLOWED >= 101200 _glfwInputError(GLFW_FORMAT_UNAVAILABLE, "NSGL: Stereo rendering is deprecated"); return GLFW_FALSE; #else addAttrib(NSOpenGLPFAStereo); #endif } if (fbconfig->doublebuffer) addAttrib(NSOpenGLPFADoubleBuffer); if (fbconfig->samples != GLFW_DONT_CARE) { if (fbconfig->samples == 0) { setAttrib(NSOpenGLPFASampleBuffers, 0); } else { setAttrib(NSOpenGLPFASampleBuffers, 1); setAttrib(NSOpenGLPFASamples, fbconfig->samples); } } // NOTE: All NSOpenGLPixelFormats on the relevant cards support sRGB // framebuffer, so there's no need (and no way) to request it addAttrib(0); #undef addAttrib #undef setAttrib window->context.nsgl.pixelFormat = [[NSOpenGLPixelFormat alloc] initWithAttributes:attribs]; if (window->context.nsgl.pixelFormat == nil) { _glfwInputError(GLFW_FORMAT_UNAVAILABLE, "NSGL: Failed to find a suitable pixel format"); return GLFW_FALSE; } NSOpenGLContext* share = nil; if (ctxconfig->share) share = ctxconfig->share->context.nsgl.object; window->context.nsgl.object = [[NSOpenGLContext alloc] initWithFormat:window->context.nsgl.pixelFormat shareContext:share]; if (window->context.nsgl.object == nil) { _glfwInputError(GLFW_VERSION_UNAVAILABLE, "NSGL: Failed to create OpenGL context"); return GLFW_FALSE; } if (fbconfig->transparent) { GLint opaque = 0; [window->context.nsgl.object setValues:&opaque forParameter:NSOpenGLContextParameterSurfaceOpacity]; } [window->ns.view setWantsBestResolutionOpenGLSurface:window->ns.retina]; [window->context.nsgl.object setView:window->ns.view]; window->context.makeCurrent = makeContextCurrentNSGL; window->context.swapBuffers = swapBuffersNSGL; window->context.swapInterval = swapIntervalNSGL; window->context.extensionSupported = extensionSupportedNSGL; window->context.getProcAddress = getProcAddressNSGL; window->context.destroy = destroyContextNSGL; return GLFW_TRUE; } ////////////////////////////////////////////////////////////////////////// ////// GLFW native API ////// ////////////////////////////////////////////////////////////////////////// GLFWAPI id glfwGetNSGLContext(GLFWwindow* handle) { _GLFWwindow* window = (_GLFWwindow*) handle; _GLFW_REQUIRE_INIT_OR_RETURN(nil); if (window->context.source != GLFW_NATIVE_CONTEXT_API) { _glfwInputError(GLFW_NO_WINDOW_CONTEXT, NULL); return nil; } return window->context.nsgl.object; } #endif #ifndef HEADER_GUARD_COCOA_JOYSTICK_M #define HEADER_GUARD_COCOA_JOYSTICK_M //======================================================================== // GLFW 3.3.7 Cocoa - www.glfw.org //------------------------------------------------------------------------ // Copyright (c) 2009-2019 Camilla Löwy // Copyright (c) 2012 Torsten Walluhn // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would // be appreciated but is not required. // // 2. Altered source versions must be plainly marked as such, and must not // be misrepresented as being the original software. // // 3. This notice may not be removed or altered from any source // distribution. // //======================================================================== // It is fine to use C99 in this file because it will not be built with VS //======================================================================== #include #include #include #include #include #include #include // Joystick element information // typedef struct _GLFWjoyelementNS { IOHIDElementRef native; uint32_t usage; int index; long minimum; long maximum; } _GLFWjoyelementNS; // Returns the value of the specified element of the specified joystick // static long getElementValue(_GLFWjoystick* js, _GLFWjoyelementNS* element) { IOHIDValueRef valueRef; long value = 0; if (js->ns.device) { if (IOHIDDeviceGetValue(js->ns.device, element->native, &valueRef) == kIOReturnSuccess) { value = IOHIDValueGetIntegerValue(valueRef); } } return value; } // Comparison function for matching the SDL element order // static CFComparisonResult compareElements(const void* fp, const void* sp, void* user) { const _GLFWjoyelementNS* fe = fp; const _GLFWjoyelementNS* se = sp; if (fe->usage < se->usage) return kCFCompareLessThan; if (fe->usage > se->usage) return kCFCompareGreaterThan; if (fe->index < se->index) return kCFCompareLessThan; if (fe->index > se->index) return kCFCompareGreaterThan; return kCFCompareEqualTo; } // Removes the specified joystick // static void closeJoystick(_GLFWjoystick* js) { int i; if (!js->present) return; for (i = 0; i < CFArrayGetCount(js->ns.axes); i++) free((void*) CFArrayGetValueAtIndex(js->ns.axes, i)); CFRelease(js->ns.axes); for (i = 0; i < CFArrayGetCount(js->ns.buttons); i++) free((void*) CFArrayGetValueAtIndex(js->ns.buttons, i)); CFRelease(js->ns.buttons); for (i = 0; i < CFArrayGetCount(js->ns.hats); i++) free((void*) CFArrayGetValueAtIndex(js->ns.hats, i)); CFRelease(js->ns.hats); _glfwFreeJoystick(js); _glfwInputJoystick(js, GLFW_DISCONNECTED); } // Callback for user-initiated joystick addition // static void matchCallback(void* context, IOReturn result, void* sender, IOHIDDeviceRef device) { int jid; char name[256]; char guid[33]; CFIndex i; CFTypeRef property; uint32_t vendor = 0, product = 0, version = 0; _GLFWjoystick* js; CFMutableArrayRef axes, buttons, hats; for (jid = 0; jid <= GLFW_JOYSTICK_LAST; jid++) { if (_glfw.joysticks[jid].ns.device == device) return; } axes = CFArrayCreateMutable(NULL, 0, NULL); buttons = CFArrayCreateMutable(NULL, 0, NULL); hats = CFArrayCreateMutable(NULL, 0, NULL); property = IOHIDDeviceGetProperty(device, CFSTR(kIOHIDProductKey)); if (property) { CFStringGetCString(property, name, sizeof(name), kCFStringEncodingUTF8); } else strncpy(name, "Unknown", sizeof(name)); property = IOHIDDeviceGetProperty(device, CFSTR(kIOHIDVendorIDKey)); if (property) CFNumberGetValue(property, kCFNumberSInt32Type, &vendor); property = IOHIDDeviceGetProperty(device, CFSTR(kIOHIDProductIDKey)); if (property) CFNumberGetValue(property, kCFNumberSInt32Type, &product); property = IOHIDDeviceGetProperty(device, CFSTR(kIOHIDVersionNumberKey)); if (property) CFNumberGetValue(property, kCFNumberSInt32Type, &version); // Generate a joystick GUID that matches the SDL 2.0.5+ one if (vendor && product) { sprintf(guid, "03000000%02x%02x0000%02x%02x0000%02x%02x0000", (uint8_t) vendor, (uint8_t) (vendor >> 8), (uint8_t) product, (uint8_t) (product >> 8), (uint8_t) version, (uint8_t) (version >> 8)); } else { sprintf(guid, "05000000%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x00", name[0], name[1], name[2], name[3], name[4], name[5], name[6], name[7], name[8], name[9], name[10]); } CFArrayRef elements = IOHIDDeviceCopyMatchingElements(device, NULL, kIOHIDOptionsTypeNone); for (i = 0; i < CFArrayGetCount(elements); i++) { IOHIDElementRef native = (IOHIDElementRef) CFArrayGetValueAtIndex(elements, i); if (CFGetTypeID(native) != IOHIDElementGetTypeID()) continue; const IOHIDElementType type = IOHIDElementGetType(native); if ((type != kIOHIDElementTypeInput_Axis) && (type != kIOHIDElementTypeInput_Button) && (type != kIOHIDElementTypeInput_Misc)) { continue; } CFMutableArrayRef target = NULL; const uint32_t usage = IOHIDElementGetUsage(native); const uint32_t page = IOHIDElementGetUsagePage(native); if (page == kHIDPage_GenericDesktop) { switch (usage) { case kHIDUsage_GD_X: case kHIDUsage_GD_Y: case kHIDUsage_GD_Z: case kHIDUsage_GD_Rx: case kHIDUsage_GD_Ry: case kHIDUsage_GD_Rz: case kHIDUsage_GD_Slider: case kHIDUsage_GD_Dial: case kHIDUsage_GD_Wheel: target = axes; break; case kHIDUsage_GD_Hatswitch: target = hats; break; case kHIDUsage_GD_DPadUp: case kHIDUsage_GD_DPadRight: case kHIDUsage_GD_DPadDown: case kHIDUsage_GD_DPadLeft: case kHIDUsage_GD_SystemMainMenu: case kHIDUsage_GD_Select: case kHIDUsage_GD_Start: target = buttons; break; } } else if (page == kHIDPage_Simulation) { switch (usage) { case kHIDUsage_Sim_Accelerator: case kHIDUsage_Sim_Brake: case kHIDUsage_Sim_Throttle: case kHIDUsage_Sim_Rudder: case kHIDUsage_Sim_Steering: target = axes; break; } } else if (page == kHIDPage_Button || page == kHIDPage_Consumer) target = buttons; if (target) { _GLFWjoyelementNS* element = calloc(1, sizeof(_GLFWjoyelementNS)); element->native = native; element->usage = usage; element->index = (int) CFArrayGetCount(target); element->minimum = IOHIDElementGetLogicalMin(native); element->maximum = IOHIDElementGetLogicalMax(native); CFArrayAppendValue(target, element); } } CFRelease(elements); CFArraySortValues(axes, CFRangeMake(0, CFArrayGetCount(axes)), compareElements, NULL); CFArraySortValues(buttons, CFRangeMake(0, CFArrayGetCount(buttons)), compareElements, NULL); CFArraySortValues(hats, CFRangeMake(0, CFArrayGetCount(hats)), compareElements, NULL); js = _glfwAllocJoystick(name, guid, (int) CFArrayGetCount(axes), (int) CFArrayGetCount(buttons), (int) CFArrayGetCount(hats)); js->ns.device = device; js->ns.axes = axes; js->ns.buttons = buttons; js->ns.hats = hats; _glfwInputJoystick(js, GLFW_CONNECTED); } // Callback for user-initiated joystick removal // static void removeCallback(void* context, IOReturn result, void* sender, IOHIDDeviceRef device) { int jid; for (jid = 0; jid <= GLFW_JOYSTICK_LAST; jid++) { if (_glfw.joysticks[jid].ns.device == device) { closeJoystick(_glfw.joysticks + jid); break; } } } ////////////////////////////////////////////////////////////////////////// ////// GLFW internal API ////// ////////////////////////////////////////////////////////////////////////// // Initialize joystick interface // void _glfwInitJoysticksNS(void) { CFMutableArrayRef matching; const long usages[] = { kHIDUsage_GD_Joystick, kHIDUsage_GD_GamePad, kHIDUsage_GD_MultiAxisController }; _glfw.ns.hidManager = IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDOptionsTypeNone); matching = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); if (!matching) { _glfwInputError(GLFW_PLATFORM_ERROR, "Cocoa: Failed to create array"); return; } for (size_t i = 0; i < sizeof(usages) / sizeof(long); i++) { const long page = kHIDPage_GenericDesktop; CFMutableDictionaryRef dict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); if (!dict) continue; CFNumberRef pageRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberLongType, &page); CFNumberRef usageRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberLongType, &usages[i]); if (pageRef && usageRef) { CFDictionarySetValue(dict, CFSTR(kIOHIDDeviceUsagePageKey), pageRef); CFDictionarySetValue(dict, CFSTR(kIOHIDDeviceUsageKey), usageRef); CFArrayAppendValue(matching, dict); } if (pageRef) CFRelease(pageRef); if (usageRef) CFRelease(usageRef); CFRelease(dict); } IOHIDManagerSetDeviceMatchingMultiple(_glfw.ns.hidManager, matching); CFRelease(matching); IOHIDManagerRegisterDeviceMatchingCallback(_glfw.ns.hidManager, &matchCallback, NULL); IOHIDManagerRegisterDeviceRemovalCallback(_glfw.ns.hidManager, &removeCallback, NULL); IOHIDManagerScheduleWithRunLoop(_glfw.ns.hidManager, CFRunLoopGetMain(), kCFRunLoopDefaultMode); IOHIDManagerOpen(_glfw.ns.hidManager, kIOHIDOptionsTypeNone); // Execute the run loop once in order to register any initially-attached // joysticks CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0, false); } // Close all opened joystick handles // void _glfwTerminateJoysticksNS(void) { int jid; for (jid = 0; jid <= GLFW_JOYSTICK_LAST; jid++) closeJoystick(_glfw.joysticks + jid); CFRelease(_glfw.ns.hidManager); _glfw.ns.hidManager = NULL; } ////////////////////////////////////////////////////////////////////////// ////// GLFW platform API ////// ////////////////////////////////////////////////////////////////////////// int _glfwPlatformPollJoystick(_GLFWjoystick* js, int mode) { if (mode & _GLFW_POLL_AXES) { CFIndex i; for (i = 0; i < CFArrayGetCount(js->ns.axes); i++) { _GLFWjoyelementNS* axis = (_GLFWjoyelementNS*) CFArrayGetValueAtIndex(js->ns.axes, i); const long raw = getElementValue(js, axis); // Perform auto calibration if (raw < axis->minimum) axis->minimum = raw; if (raw > axis->maximum) axis->maximum = raw; const long size = axis->maximum - axis->minimum; if (size == 0) _glfwInputJoystickAxis(js, (int) i, 0.f); else { const float value = (2.f * (raw - axis->minimum) / size) - 1.f; _glfwInputJoystickAxis(js, (int) i, value); } } } if (mode & _GLFW_POLL_BUTTONS) { CFIndex i; for (i = 0; i < CFArrayGetCount(js->ns.buttons); i++) { _GLFWjoyelementNS* button = (_GLFWjoyelementNS*) CFArrayGetValueAtIndex(js->ns.buttons, i); const char value = getElementValue(js, button) - button->minimum; const int state = (value > 0) ? GLFW_PRESS : GLFW_RELEASE; _glfwInputJoystickButton(js, (int) i, state); } for (i = 0; i < CFArrayGetCount(js->ns.hats); i++) { const int states[9] = { GLFW_HAT_UP, GLFW_HAT_RIGHT_UP, GLFW_HAT_RIGHT, GLFW_HAT_RIGHT_DOWN, GLFW_HAT_DOWN, GLFW_HAT_LEFT_DOWN, GLFW_HAT_LEFT, GLFW_HAT_LEFT_UP, GLFW_HAT_CENTERED }; _GLFWjoyelementNS* hat = (_GLFWjoyelementNS*) CFArrayGetValueAtIndex(js->ns.hats, i); long state = getElementValue(js, hat) - hat->minimum; if (state < 0 || state > 8) state = 8; _glfwInputJoystickHat(js, (int) i, states[state]); } } return js->present; } void _glfwPlatformUpdateGamepadGUID(char* guid) { if ((strncmp(guid + 4, "000000000000", 12) == 0) && (strncmp(guid + 20, "000000000000", 12) == 0)) { char original[33]; strncpy(original, guid, sizeof(original) - 1); sprintf(guid, "03000000%.4s0000%.4s000000000000", original, original + 16); } } #endif #ifndef HEADER_GUARD_COCOA_MONITOR_M #define HEADER_GUARD_COCOA_MONITOR_M //======================================================================== // GLFW 3.3.7 macOS - www.glfw.org //------------------------------------------------------------------------ // Copyright (c) 2002-2006 Marcus Geelnard // Copyright (c) 2006-2019 Camilla Löwy // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would // be appreciated but is not required. // // 2. Altered source versions must be plainly marked as such, and must not // be misrepresented as being the original software. // // 3. This notice may not be removed or altered from any source // distribution. // //======================================================================== // It is fine to use C99 in this file because it will not be built with VS //======================================================================== #include #include #include #include #include // Get the name of the specified display, or NULL // static char* getMonitorName(CGDirectDisplayID displayID, NSScreen* screen) { // IOKit doesn't work on Apple Silicon anymore // Luckily, 10.15 introduced -[NSScreen localizedName]. // Use it if available, and fall back to IOKit otherwise. if (screen) { if ([screen respondsToSelector:@selector(localizedName)]) { NSString* name = [screen valueForKey:@"localizedName"]; if (name) return _glfw_strdup([name UTF8String]); } } io_iterator_t it; io_service_t service; CFDictionaryRef info; if (IOServiceGetMatchingServices(MACH_PORT_NULL, IOServiceMatching("IODisplayConnect"), &it) != 0) { // This may happen if a desktop Mac is running headless return _glfw_strdup("Display"); } while ((service = IOIteratorNext(it)) != 0) { info = IODisplayCreateInfoDictionary(service, kIODisplayOnlyPreferredName); CFNumberRef vendorIDRef = CFDictionaryGetValue(info, CFSTR(kDisplayVendorID)); CFNumberRef productIDRef = CFDictionaryGetValue(info, CFSTR(kDisplayProductID)); if (!vendorIDRef || !productIDRef) { CFRelease(info); continue; } unsigned int vendorID, productID; CFNumberGetValue(vendorIDRef, kCFNumberIntType, &vendorID); CFNumberGetValue(productIDRef, kCFNumberIntType, &productID); if (CGDisplayVendorNumber(displayID) == vendorID && CGDisplayModelNumber(displayID) == productID) { // Info dictionary is used and freed below break; } CFRelease(info); } IOObjectRelease(it); if (!service) { _glfwInputError(GLFW_PLATFORM_ERROR, "Cocoa: Failed to find service port for display"); return _glfw_strdup("Display"); } CFDictionaryRef names = CFDictionaryGetValue(info, CFSTR(kDisplayProductName)); CFStringRef nameRef; if (!names || !CFDictionaryGetValueIfPresent(names, CFSTR("en_US"), (const void**) &nameRef)) { // This may happen if a desktop Mac is running headless CFRelease(info); return _glfw_strdup("Display"); } const CFIndex size = CFStringGetMaximumSizeForEncoding(CFStringGetLength(nameRef), kCFStringEncodingUTF8); char* name = calloc(size + 1, 1); CFStringGetCString(nameRef, name, size, kCFStringEncodingUTF8); CFRelease(info); return name; } // Check whether the display mode should be included in enumeration // static GLFWbool modeIsGood(CGDisplayModeRef mode) { uint32_t flags = CGDisplayModeGetIOFlags(mode); if (!(flags & kDisplayModeValidFlag) || !(flags & kDisplayModeSafeFlag)) return GLFW_FALSE; if (flags & kDisplayModeInterlacedFlag) return GLFW_FALSE; if (flags & kDisplayModeStretchedFlag) return GLFW_FALSE; #if MAC_OS_X_VERSION_MAX_ALLOWED <= 101100 CFStringRef format = CGDisplayModeCopyPixelEncoding(mode); if (CFStringCompare(format, CFSTR(IO16BitDirectPixels), 0) && CFStringCompare(format, CFSTR(IO32BitDirectPixels), 0)) { CFRelease(format); return GLFW_FALSE; } CFRelease(format); #endif /* MAC_OS_X_VERSION_MAX_ALLOWED */ return GLFW_TRUE; } // Convert Core Graphics display mode to GLFW video mode // static GLFWvidmode vidmodeFromCGDisplayMode(CGDisplayModeRef mode, double fallbackRefreshRate) { GLFWvidmode result; result.width = (int) CGDisplayModeGetWidth(mode); result.height = (int) CGDisplayModeGetHeight(mode); result.refreshRate = (int) round(CGDisplayModeGetRefreshRate(mode)); if (result.refreshRate == 0) result.refreshRate = (int) round(fallbackRefreshRate); #if MAC_OS_X_VERSION_MAX_ALLOWED <= 101100 CFStringRef format = CGDisplayModeCopyPixelEncoding(mode); if (CFStringCompare(format, CFSTR(IO16BitDirectPixels), 0) == 0) { result.redBits = 5; result.greenBits = 5; result.blueBits = 5; } else #endif /* MAC_OS_X_VERSION_MAX_ALLOWED */ { result.redBits = 8; result.greenBits = 8; result.blueBits = 8; } #if MAC_OS_X_VERSION_MAX_ALLOWED <= 101100 CFRelease(format); #endif /* MAC_OS_X_VERSION_MAX_ALLOWED */ return result; } // Starts reservation for display fading // static CGDisplayFadeReservationToken beginFadeReservation(void) { CGDisplayFadeReservationToken token = kCGDisplayFadeReservationInvalidToken; if (CGAcquireDisplayFadeReservation(5, &token) == kCGErrorSuccess) { CGDisplayFade(token, 0.3, kCGDisplayBlendNormal, kCGDisplayBlendSolidColor, 0.0, 0.0, 0.0, TRUE); } return token; } // Ends reservation for display fading // static void endFadeReservation(CGDisplayFadeReservationToken token) { if (token != kCGDisplayFadeReservationInvalidToken) { CGDisplayFade(token, 0.5, kCGDisplayBlendSolidColor, kCGDisplayBlendNormal, 0.0, 0.0, 0.0, FALSE); CGReleaseDisplayFadeReservation(token); } } // Returns the display refresh rate queried from the I/O registry // static double getFallbackRefreshRate(CGDirectDisplayID displayID) { double refreshRate = 60.0; io_iterator_t it; io_service_t service; if (IOServiceGetMatchingServices(MACH_PORT_NULL, IOServiceMatching("IOFramebuffer"), &it) != 0) { return refreshRate; } while ((service = IOIteratorNext(it)) != 0) { const CFNumberRef indexRef = IORegistryEntryCreateCFProperty(service, CFSTR("IOFramebufferOpenGLIndex"), kCFAllocatorDefault, kNilOptions); if (!indexRef) continue; uint32_t index = 0; CFNumberGetValue(indexRef, kCFNumberIntType, &index); CFRelease(indexRef); if (CGOpenGLDisplayMaskToDisplayID(1 << index) != displayID) continue; const CFNumberRef clockRef = IORegistryEntryCreateCFProperty(service, CFSTR("IOFBCurrentPixelClock"), kCFAllocatorDefault, kNilOptions); const CFNumberRef countRef = IORegistryEntryCreateCFProperty(service, CFSTR("IOFBCurrentPixelCount"), kCFAllocatorDefault, kNilOptions); uint32_t clock = 0, count = 0; if (clockRef) { CFNumberGetValue(clockRef, kCFNumberIntType, &clock); CFRelease(clockRef); } if (countRef) { CFNumberGetValue(countRef, kCFNumberIntType, &count); CFRelease(countRef); } if (clock > 0 && count > 0) refreshRate = clock / (double) count; break; } IOObjectRelease(it); return refreshRate; } ////////////////////////////////////////////////////////////////////////// ////// GLFW internal API ////// ////////////////////////////////////////////////////////////////////////// // Poll for changes in the set of connected monitors // void _glfwPollMonitorsNS(void) { uint32_t displayCount; CGGetOnlineDisplayList(0, NULL, &displayCount); CGDirectDisplayID* displays = calloc(displayCount, sizeof(CGDirectDisplayID)); CGGetOnlineDisplayList(displayCount, displays, &displayCount); for (int i = 0; i < _glfw.monitorCount; i++) _glfw.monitors[i]->ns.screen = nil; _GLFWmonitor** disconnected = NULL; uint32_t disconnectedCount = _glfw.monitorCount; if (disconnectedCount) { disconnected = calloc(_glfw.monitorCount, sizeof(_GLFWmonitor*)); memcpy(disconnected, _glfw.monitors, _glfw.monitorCount * sizeof(_GLFWmonitor*)); } for (uint32_t i = 0; i < displayCount; i++) { if (CGDisplayIsAsleep(displays[i])) continue; const uint32_t unitNumber = CGDisplayUnitNumber(displays[i]); NSScreen* screen = nil; for (screen in [NSScreen screens]) { NSNumber* screenNumber = [screen deviceDescription][@"NSScreenNumber"]; // HACK: Compare unit numbers instead of display IDs to work around // display replacement on machines with automatic graphics // switching if (CGDisplayUnitNumber([screenNumber unsignedIntValue]) == unitNumber) break; } // HACK: Compare unit numbers instead of display IDs to work around // display replacement on machines with automatic graphics // switching uint32_t j; for (j = 0; j < disconnectedCount; j++) { if (disconnected[j] && disconnected[j]->ns.unitNumber == unitNumber) { disconnected[j]->ns.screen = screen; disconnected[j] = NULL; break; } } if (j < disconnectedCount) continue; const CGSize size = CGDisplayScreenSize(displays[i]); char* name = getMonitorName(displays[i], screen); if (!name) continue; _GLFWmonitor* monitor = _glfwAllocMonitor(name, size.width, size.height); monitor->ns.displayID = displays[i]; monitor->ns.unitNumber = unitNumber; monitor->ns.screen = screen; free(name); CGDisplayModeRef mode = CGDisplayCopyDisplayMode(displays[i]); if (CGDisplayModeGetRefreshRate(mode) == 0.0) monitor->ns.fallbackRefreshRate = getFallbackRefreshRate(displays[i]); CGDisplayModeRelease(mode); _glfwInputMonitor(monitor, GLFW_CONNECTED, _GLFW_INSERT_LAST); } for (uint32_t i = 0; i < disconnectedCount; i++) { if (disconnected[i]) _glfwInputMonitor(disconnected[i], GLFW_DISCONNECTED, 0); } free(disconnected); free(displays); } // Change the current video mode // void _glfwSetVideoModeNS(_GLFWmonitor* monitor, const GLFWvidmode* desired) { GLFWvidmode current; _glfwPlatformGetVideoMode(monitor, ¤t); const GLFWvidmode* best = _glfwChooseVideoMode(monitor, desired); if (_glfwCompareVideoModes(¤t, best) == 0) return; CFArrayRef modes = CGDisplayCopyAllDisplayModes(monitor->ns.displayID, NULL); const CFIndex count = CFArrayGetCount(modes); CGDisplayModeRef native = NULL; for (CFIndex i = 0; i < count; i++) { CGDisplayModeRef dm = (CGDisplayModeRef) CFArrayGetValueAtIndex(modes, i); if (!modeIsGood(dm)) continue; const GLFWvidmode mode = vidmodeFromCGDisplayMode(dm, monitor->ns.fallbackRefreshRate); if (_glfwCompareVideoModes(best, &mode) == 0) { native = dm; break; } } if (native) { if (monitor->ns.previousMode == NULL) monitor->ns.previousMode = CGDisplayCopyDisplayMode(monitor->ns.displayID); CGDisplayFadeReservationToken token = beginFadeReservation(); CGDisplaySetDisplayMode(monitor->ns.displayID, native, NULL); endFadeReservation(token); } CFRelease(modes); } // Restore the previously saved (original) video mode // void _glfwRestoreVideoModeNS(_GLFWmonitor* monitor) { if (monitor->ns.previousMode) { CGDisplayFadeReservationToken token = beginFadeReservation(); CGDisplaySetDisplayMode(monitor->ns.displayID, monitor->ns.previousMode, NULL); endFadeReservation(token); CGDisplayModeRelease(monitor->ns.previousMode); monitor->ns.previousMode = NULL; } } ////////////////////////////////////////////////////////////////////////// ////// GLFW platform API ////// ////////////////////////////////////////////////////////////////////////// void _glfwPlatformFreeMonitor(_GLFWmonitor* monitor) { } void _glfwPlatformGetMonitorPos(_GLFWmonitor* monitor, int* xpos, int* ypos) { @autoreleasepool { const CGRect bounds = CGDisplayBounds(monitor->ns.displayID); if (xpos) *xpos = (int) bounds.origin.x; if (ypos) *ypos = (int) bounds.origin.y; } // autoreleasepool } void _glfwPlatformGetMonitorContentScale(_GLFWmonitor* monitor, float* xscale, float* yscale) { @autoreleasepool { if (!monitor->ns.screen) { _glfwInputError(GLFW_PLATFORM_ERROR, "Cocoa: Cannot query content scale without screen"); } const NSRect points = [monitor->ns.screen frame]; const NSRect pixels = [monitor->ns.screen convertRectToBacking:points]; if (xscale) *xscale = (float) (pixels.size.width / points.size.width); if (yscale) *yscale = (float) (pixels.size.height / points.size.height); } // autoreleasepool } void _glfwPlatformGetMonitorWorkarea(_GLFWmonitor* monitor, int* xpos, int* ypos, int* width, int* height) { @autoreleasepool { if (!monitor->ns.screen) { _glfwInputError(GLFW_PLATFORM_ERROR, "Cocoa: Cannot query workarea without screen"); } const NSRect frameRect = [monitor->ns.screen visibleFrame]; if (xpos) *xpos = frameRect.origin.x; if (ypos) *ypos = _glfwTransformYNS(frameRect.origin.y + frameRect.size.height - 1); if (width) *width = frameRect.size.width; if (height) *height = frameRect.size.height; } // autoreleasepool } GLFWvidmode* _glfwPlatformGetVideoModes(_GLFWmonitor* monitor, int* count) { @autoreleasepool { *count = 0; CFArrayRef modes = CGDisplayCopyAllDisplayModes(monitor->ns.displayID, NULL); const CFIndex found = CFArrayGetCount(modes); GLFWvidmode* result = calloc(found, sizeof(GLFWvidmode)); for (CFIndex i = 0; i < found; i++) { CGDisplayModeRef dm = (CGDisplayModeRef) CFArrayGetValueAtIndex(modes, i); if (!modeIsGood(dm)) continue; const GLFWvidmode mode = vidmodeFromCGDisplayMode(dm, monitor->ns.fallbackRefreshRate); CFIndex j; for (j = 0; j < *count; j++) { if (_glfwCompareVideoModes(result + j, &mode) == 0) break; } // Skip duplicate modes if (j < *count) continue; (*count)++; result[*count - 1] = mode; } CFRelease(modes); return result; } // autoreleasepool } void _glfwPlatformGetVideoMode(_GLFWmonitor* monitor, GLFWvidmode *mode) { @autoreleasepool { CGDisplayModeRef native = CGDisplayCopyDisplayMode(monitor->ns.displayID); *mode = vidmodeFromCGDisplayMode(native, monitor->ns.fallbackRefreshRate); CGDisplayModeRelease(native); } // autoreleasepool } GLFWbool _glfwPlatformGetGammaRamp(_GLFWmonitor* monitor, GLFWgammaramp* ramp) { @autoreleasepool { uint32_t size = CGDisplayGammaTableCapacity(monitor->ns.displayID); CGGammaValue* values = calloc(size * 3, sizeof(CGGammaValue)); CGGetDisplayTransferByTable(monitor->ns.displayID, size, values, values + size, values + size * 2, &size); _glfwAllocGammaArrays(ramp, size); for (uint32_t i = 0; i < size; i++) { ramp->red[i] = (unsigned short) (values[i] * 65535); ramp->green[i] = (unsigned short) (values[i + size] * 65535); ramp->blue[i] = (unsigned short) (values[i + size * 2] * 65535); } free(values); return GLFW_TRUE; } // autoreleasepool } void _glfwPlatformSetGammaRamp(_GLFWmonitor* monitor, const GLFWgammaramp* ramp) { @autoreleasepool { CGGammaValue* values = calloc(ramp->size * 3, sizeof(CGGammaValue)); for (unsigned int i = 0; i < ramp->size; i++) { values[i] = ramp->red[i] / 65535.f; values[i + ramp->size] = ramp->green[i] / 65535.f; values[i + ramp->size * 2] = ramp->blue[i] / 65535.f; } CGSetDisplayTransferByTable(monitor->ns.displayID, ramp->size, values, values + ramp->size, values + ramp->size * 2); free(values); } // autoreleasepool } ////////////////////////////////////////////////////////////////////////// ////// GLFW native API ////// ////////////////////////////////////////////////////////////////////////// GLFWAPI CGDirectDisplayID glfwGetCocoaMonitor(GLFWmonitor* handle) { _GLFWmonitor* monitor = (_GLFWmonitor*) handle; _GLFW_REQUIRE_INIT_OR_RETURN(kCGNullDirectDisplay); return monitor->ns.displayID; } #endif #ifndef HEADER_GUARD_COCOA_WINDOW_M #define HEADER_GUARD_COCOA_WINDOW_M //======================================================================== // GLFW 3.3.7 macOS - www.glfw.org //------------------------------------------------------------------------ // Copyright (c) 2009-2019 Camilla Löwy // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would // be appreciated but is not required. // // 2. Altered source versions must be plainly marked as such, and must not // be misrepresented as being the original software. // // 3. This notice may not be removed or altered from any source // distribution. // //======================================================================== // It is fine to use C99 in this file because it will not be built with VS //======================================================================== #include #include // Returns the style mask corresponding to the window settings // static NSUInteger getStyleMask(_GLFWwindow* window) { NSUInteger styleMask = NSWindowStyleMaskMiniaturizable; if (window->monitor || !window->decorated) styleMask |= NSWindowStyleMaskBorderless; else { styleMask |= NSWindowStyleMaskTitled | NSWindowStyleMaskClosable; if (window->resizable) styleMask |= NSWindowStyleMaskResizable; } return styleMask; } // Returns whether the cursor is in the content area of the specified window // static GLFWbool cursorInContentArea(_GLFWwindow* window) { const NSPoint pos = [window->ns.object mouseLocationOutsideOfEventStream]; return [window->ns.view mouse:pos inRect:[window->ns.view frame]]; } // Hides the cursor if not already hidden // static void hideCursor(_GLFWwindow* window) { if (!_glfw.ns.cursorHidden) { [NSCursor hide]; _glfw.ns.cursorHidden = GLFW_TRUE; } } // Shows the cursor if not already shown // static void showCursor(_GLFWwindow* window) { if (_glfw.ns.cursorHidden) { [NSCursor unhide]; _glfw.ns.cursorHidden = GLFW_FALSE; } } // Updates the cursor image according to its cursor mode // static void updateCursorImage(_GLFWwindow* window) { if (window->cursorMode == GLFW_CURSOR_NORMAL) { showCursor(window); if (window->cursor) [(NSCursor*) window->cursor->ns.object set]; else [[NSCursor arrowCursor] set]; } else hideCursor(window); } // Apply chosen cursor mode to a focused window // static void updateCursorMode(_GLFWwindow* window) { if (window->cursorMode == GLFW_CURSOR_DISABLED) { _glfw.ns.disabledCursorWindow = window; _glfwPlatformGetCursorPos(window, &_glfw.ns.restoreCursorPosX, &_glfw.ns.restoreCursorPosY); _glfwCenterCursorInContentArea(window); CGAssociateMouseAndMouseCursorPosition(false); } else if (_glfw.ns.disabledCursorWindow == window) { _glfw.ns.disabledCursorWindow = NULL; _glfwPlatformSetCursorPos(window, _glfw.ns.restoreCursorPosX, _glfw.ns.restoreCursorPosY); // NOTE: The matching CGAssociateMouseAndMouseCursorPosition call is // made in _glfwPlatformSetCursorPos as part of a workaround } if (cursorInContentArea(window)) updateCursorImage(window); } // Make the specified window and its video mode active on its monitor // static void acquireMonitor(_GLFWwindow* window) { _glfwSetVideoModeNS(window->monitor, &window->videoMode); const CGRect bounds = CGDisplayBounds(window->monitor->ns.displayID); const NSRect frame = NSMakeRect(bounds.origin.x, _glfwTransformYNS(bounds.origin.y + bounds.size.height - 1), bounds.size.width, bounds.size.height); [window->ns.object setFrame:frame display:YES]; _glfwInputMonitorWindow(window->monitor, window); } // Remove the window and restore the original video mode // static void releaseMonitor(_GLFWwindow* window) { if (window->monitor->window != window) return; _glfwInputMonitorWindow(window->monitor, NULL); _glfwRestoreVideoModeNS(window->monitor); } // Translates macOS key modifiers into GLFW ones // static int translateFlags(NSUInteger flags) { int mods = 0; if (flags & NSEventModifierFlagShift) mods |= GLFW_MOD_SHIFT; if (flags & NSEventModifierFlagControl) mods |= GLFW_MOD_CONTROL; if (flags & NSEventModifierFlagOption) mods |= GLFW_MOD_ALT; if (flags & NSEventModifierFlagCommand) mods |= GLFW_MOD_SUPER; if (flags & NSEventModifierFlagCapsLock) mods |= GLFW_MOD_CAPS_LOCK; return mods; } // Translates a macOS keycode to a GLFW keycode // static int translateKey(unsigned int key) { if (key >= sizeof(_glfw.ns.keycodes) / sizeof(_glfw.ns.keycodes[0])) return GLFW_KEY_UNKNOWN; return _glfw.ns.keycodes[key]; } // Translate a GLFW keycode to a Cocoa modifier flag // static NSUInteger translateKeyToModifierFlag(int key) { switch (key) { case GLFW_KEY_LEFT_SHIFT: case GLFW_KEY_RIGHT_SHIFT: return NSEventModifierFlagShift; case GLFW_KEY_LEFT_CONTROL: case GLFW_KEY_RIGHT_CONTROL: return NSEventModifierFlagControl; case GLFW_KEY_LEFT_ALT: case GLFW_KEY_RIGHT_ALT: return NSEventModifierFlagOption; case GLFW_KEY_LEFT_SUPER: case GLFW_KEY_RIGHT_SUPER: return NSEventModifierFlagCommand; case GLFW_KEY_CAPS_LOCK: return NSEventModifierFlagCapsLock; } return 0; } // Defines a constant for empty ranges in NSTextInputClient // static const NSRange kEmptyRange = { NSNotFound, 0 }; //------------------------------------------------------------------------ // Delegate for window related notifications //------------------------------------------------------------------------ @interface GLFWWindowDelegate : NSObject { _GLFWwindow* window; } - (instancetype)initWithGlfwWindow:(_GLFWwindow *)initWindow; @end @implementation GLFWWindowDelegate - (instancetype)initWithGlfwWindow:(_GLFWwindow *)initWindow { self = [super init]; if (self != nil) window = initWindow; return self; } - (BOOL)windowShouldClose:(id)sender { _glfwInputWindowCloseRequest(window); return NO; } - (void)windowDidResize:(NSNotification *)notification { if (window->context.source == GLFW_NATIVE_CONTEXT_API) [window->context.nsgl.object update]; if (_glfw.ns.disabledCursorWindow == window) _glfwCenterCursorInContentArea(window); const int maximized = [window->ns.object isZoomed]; if (window->ns.maximized != maximized) { window->ns.maximized = maximized; _glfwInputWindowMaximize(window, maximized); } const NSRect contentRect = [window->ns.view frame]; const NSRect fbRect = [window->ns.view convertRectToBacking:contentRect]; if (fbRect.size.width != window->ns.fbWidth || fbRect.size.height != window->ns.fbHeight) { window->ns.fbWidth = fbRect.size.width; window->ns.fbHeight = fbRect.size.height; _glfwInputFramebufferSize(window, fbRect.size.width, fbRect.size.height); } if (contentRect.size.width != window->ns.width || contentRect.size.height != window->ns.height) { window->ns.width = contentRect.size.width; window->ns.height = contentRect.size.height; _glfwInputWindowSize(window, contentRect.size.width, contentRect.size.height); } } - (void)windowDidMove:(NSNotification *)notification { if (window->context.source == GLFW_NATIVE_CONTEXT_API) [window->context.nsgl.object update]; if (_glfw.ns.disabledCursorWindow == window) _glfwCenterCursorInContentArea(window); int x, y; _glfwPlatformGetWindowPos(window, &x, &y); _glfwInputWindowPos(window, x, y); } - (void)windowDidMiniaturize:(NSNotification *)notification { if (window->monitor) releaseMonitor(window); _glfwInputWindowIconify(window, GLFW_TRUE); } - (void)windowDidDeminiaturize:(NSNotification *)notification { if (window->monitor) acquireMonitor(window); _glfwInputWindowIconify(window, GLFW_FALSE); } - (void)windowDidBecomeKey:(NSNotification *)notification { if (_glfw.ns.disabledCursorWindow == window) _glfwCenterCursorInContentArea(window); _glfwInputWindowFocus(window, GLFW_TRUE); updateCursorMode(window); } - (void)windowDidResignKey:(NSNotification *)notification { if (window->monitor && window->autoIconify) _glfwPlatformIconifyWindow(window); _glfwInputWindowFocus(window, GLFW_FALSE); } - (void)windowDidChangeOcclusionState:(NSNotification* )notification { if ([window->ns.object occlusionState] & NSWindowOcclusionStateVisible) window->ns.occluded = GLFW_FALSE; else window->ns.occluded = GLFW_TRUE; } @end //------------------------------------------------------------------------ // Content view class for the GLFW window //------------------------------------------------------------------------ @interface GLFWContentView : NSView { _GLFWwindow* window; NSTrackingArea* trackingArea; NSMutableAttributedString* markedText; } - (instancetype)initWithGlfwWindow:(_GLFWwindow *)initWindow; @end @implementation GLFWContentView - (instancetype)initWithGlfwWindow:(_GLFWwindow *)initWindow { self = [super init]; if (self != nil) { window = initWindow; trackingArea = nil; markedText = [[NSMutableAttributedString alloc] init]; [self updateTrackingAreas]; [self registerForDraggedTypes:@[NSPasteboardTypeURL]]; } return self; } - (void)dealloc { [trackingArea release]; [markedText release]; [super dealloc]; } - (BOOL)isOpaque { return [window->ns.object isOpaque]; } - (BOOL)canBecomeKeyView { return YES; } - (BOOL)acceptsFirstResponder { return YES; } - (BOOL)wantsUpdateLayer { return YES; } - (void)updateLayer { if (window->context.source == GLFW_NATIVE_CONTEXT_API) [window->context.nsgl.object update]; _glfwInputWindowDamage(window); } - (void)cursorUpdate:(NSEvent *)event { updateCursorImage(window); } - (BOOL)acceptsFirstMouse:(NSEvent *)event { return YES; } - (void)mouseDown:(NSEvent *)event { _glfwInputMouseClick(window, GLFW_MOUSE_BUTTON_LEFT, GLFW_PRESS, translateFlags([event modifierFlags])); } - (void)mouseDragged:(NSEvent *)event { [self mouseMoved:event]; } - (void)mouseUp:(NSEvent *)event { _glfwInputMouseClick(window, GLFW_MOUSE_BUTTON_LEFT, GLFW_RELEASE, translateFlags([event modifierFlags])); } - (void)mouseMoved:(NSEvent *)event { if (window->cursorMode == GLFW_CURSOR_DISABLED) { const double dx = [event deltaX] - window->ns.cursorWarpDeltaX; const double dy = [event deltaY] - window->ns.cursorWarpDeltaY; _glfwInputCursorPos(window, window->virtualCursorPosX + dx, window->virtualCursorPosY + dy); } else { const NSRect contentRect = [window->ns.view frame]; // NOTE: The returned location uses base 0,1 not 0,0 const NSPoint pos = [event locationInWindow]; _glfwInputCursorPos(window, pos.x, contentRect.size.height - pos.y); } window->ns.cursorWarpDeltaX = 0; window->ns.cursorWarpDeltaY = 0; } - (void)rightMouseDown:(NSEvent *)event { _glfwInputMouseClick(window, GLFW_MOUSE_BUTTON_RIGHT, GLFW_PRESS, translateFlags([event modifierFlags])); } - (void)rightMouseDragged:(NSEvent *)event { [self mouseMoved:event]; } - (void)rightMouseUp:(NSEvent *)event { _glfwInputMouseClick(window, GLFW_MOUSE_BUTTON_RIGHT, GLFW_RELEASE, translateFlags([event modifierFlags])); } - (void)otherMouseDown:(NSEvent *)event { _glfwInputMouseClick(window, (int) [event buttonNumber], GLFW_PRESS, translateFlags([event modifierFlags])); } - (void)otherMouseDragged:(NSEvent *)event { [self mouseMoved:event]; } - (void)otherMouseUp:(NSEvent *)event { _glfwInputMouseClick(window, (int) [event buttonNumber], GLFW_RELEASE, translateFlags([event modifierFlags])); } - (void)mouseExited:(NSEvent *)event { if (window->cursorMode == GLFW_CURSOR_HIDDEN) showCursor(window); _glfwInputCursorEnter(window, GLFW_FALSE); } - (void)mouseEntered:(NSEvent *)event { if (window->cursorMode == GLFW_CURSOR_HIDDEN) hideCursor(window); _glfwInputCursorEnter(window, GLFW_TRUE); } - (void)viewDidChangeBackingProperties { const NSRect contentRect = [window->ns.view frame]; const NSRect fbRect = [window->ns.view convertRectToBacking:contentRect]; const float xscale = fbRect.size.width / contentRect.size.width; const float yscale = fbRect.size.height / contentRect.size.height; if (xscale != window->ns.xscale || yscale != window->ns.yscale) { if (window->ns.retina && window->ns.layer) [window->ns.layer setContentsScale:[window->ns.object backingScaleFactor]]; window->ns.xscale = xscale; window->ns.yscale = yscale; _glfwInputWindowContentScale(window, xscale, yscale); } if (fbRect.size.width != window->ns.fbWidth || fbRect.size.height != window->ns.fbHeight) { window->ns.fbWidth = fbRect.size.width; window->ns.fbHeight = fbRect.size.height; _glfwInputFramebufferSize(window, fbRect.size.width, fbRect.size.height); } } - (void)drawRect:(NSRect)rect { _glfwInputWindowDamage(window); } - (void)updateTrackingAreas { if (trackingArea != nil) { [self removeTrackingArea:trackingArea]; [trackingArea release]; } const NSTrackingAreaOptions options = NSTrackingMouseEnteredAndExited | NSTrackingActiveInKeyWindow | NSTrackingEnabledDuringMouseDrag | NSTrackingCursorUpdate | NSTrackingInVisibleRect | NSTrackingAssumeInside; trackingArea = [[NSTrackingArea alloc] initWithRect:[self bounds] options:options owner:self userInfo:nil]; [self addTrackingArea:trackingArea]; [super updateTrackingAreas]; } - (void)keyDown:(NSEvent *)event { const int key = translateKey([event keyCode]); const int mods = translateFlags([event modifierFlags]); _glfwInputKey(window, key, [event keyCode], GLFW_PRESS, mods); [self interpretKeyEvents:@[event]]; } - (void)flagsChanged:(NSEvent *)event { int action; const unsigned int modifierFlags = [event modifierFlags] & NSEventModifierFlagDeviceIndependentFlagsMask; const int key = translateKey([event keyCode]); const int mods = translateFlags(modifierFlags); const NSUInteger keyFlag = translateKeyToModifierFlag(key); if (keyFlag & modifierFlags) { if (window->keys[key] == GLFW_PRESS) action = GLFW_RELEASE; else action = GLFW_PRESS; } else action = GLFW_RELEASE; _glfwInputKey(window, key, [event keyCode], action, mods); } - (void)keyUp:(NSEvent *)event { const int key = translateKey([event keyCode]); const int mods = translateFlags([event modifierFlags]); _glfwInputKey(window, key, [event keyCode], GLFW_RELEASE, mods); } - (void)scrollWheel:(NSEvent *)event { double deltaX = [event scrollingDeltaX]; double deltaY = [event scrollingDeltaY]; if ([event hasPreciseScrollingDeltas]) { deltaX *= 0.1; deltaY *= 0.1; } if (fabs(deltaX) > 0.0 || fabs(deltaY) > 0.0) _glfwInputScroll(window, deltaX, deltaY); } - (NSDragOperation)draggingEntered:(id )sender { // HACK: We don't know what to say here because we don't know what the // application wants to do with the paths return NSDragOperationGeneric; } - (BOOL)performDragOperation:(id )sender { const NSRect contentRect = [window->ns.view frame]; // NOTE: The returned location uses base 0,1 not 0,0 const NSPoint pos = [sender draggingLocation]; _glfwInputCursorPos(window, pos.x, contentRect.size.height - pos.y); NSPasteboard* pasteboard = [sender draggingPasteboard]; NSDictionary* options = @{NSPasteboardURLReadingFileURLsOnlyKey:@YES}; NSArray* urls = [pasteboard readObjectsForClasses:@[[NSURL class]] options:options]; const NSUInteger count = [urls count]; if (count) { char** paths = calloc(count, sizeof(char*)); for (NSUInteger i = 0; i < count; i++) paths[i] = _glfw_strdup([urls[i] fileSystemRepresentation]); _glfwInputDrop(window, (int) count, (const char**) paths); for (NSUInteger i = 0; i < count; i++) free(paths[i]); free(paths); } return YES; } - (BOOL)hasMarkedText { return [markedText length] > 0; } - (NSRange)markedRange { if ([markedText length] > 0) return NSMakeRange(0, [markedText length] - 1); else return kEmptyRange; } - (NSRange)selectedRange { return kEmptyRange; } - (void)setMarkedText:(id)string selectedRange:(NSRange)selectedRange replacementRange:(NSRange)replacementRange { [markedText release]; if ([string isKindOfClass:[NSAttributedString class]]) markedText = [[NSMutableAttributedString alloc] initWithAttributedString:string]; else markedText = [[NSMutableAttributedString alloc] initWithString:string]; } - (void)unmarkText { [[markedText mutableString] setString:@""]; } - (NSArray*)validAttributesForMarkedText { return [NSArray array]; } - (NSAttributedString*)attributedSubstringForProposedRange:(NSRange)range actualRange:(NSRangePointer)actualRange { return nil; } - (NSUInteger)characterIndexForPoint:(NSPoint)point { return 0; } - (NSRect)firstRectForCharacterRange:(NSRange)range actualRange:(NSRangePointer)actualRange { const NSRect frame = [window->ns.view frame]; return NSMakeRect(frame.origin.x, frame.origin.y, 0.0, 0.0); } - (void)insertText:(id)string replacementRange:(NSRange)replacementRange { NSString* characters; NSEvent* event = [NSApp currentEvent]; const int mods = translateFlags([event modifierFlags]); const int plain = !(mods & GLFW_MOD_SUPER); if ([string isKindOfClass:[NSAttributedString class]]) characters = [string string]; else characters = (NSString*) string; NSRange range = NSMakeRange(0, [characters length]); while (range.length) { uint32_t codepoint = 0; if ([characters getBytes:&codepoint maxLength:sizeof(codepoint) usedLength:NULL encoding:NSUTF32StringEncoding options:0 range:range remainingRange:&range]) { if (codepoint >= 0xf700 && codepoint <= 0xf7ff) continue; _glfwInputChar(window, codepoint, mods, plain); } } } - (void)doCommandBySelector:(SEL)selector { } @end //------------------------------------------------------------------------ // GLFW window class //------------------------------------------------------------------------ @interface GLFWWindow : NSWindow {} @end @implementation GLFWWindow - (BOOL)canBecomeKeyWindow { // Required for NSWindowStyleMaskBorderless windows return YES; } - (BOOL)canBecomeMainWindow { return YES; } @end // Create the Cocoa window // static GLFWbool createNativeWindow(_GLFWwindow* window, const _GLFWwndconfig* wndconfig, const _GLFWfbconfig* fbconfig) { window->ns.delegate = [[GLFWWindowDelegate alloc] initWithGlfwWindow:window]; if (window->ns.delegate == nil) { _glfwInputError(GLFW_PLATFORM_ERROR, "Cocoa: Failed to create window delegate"); return GLFW_FALSE; } NSRect contentRect; if (window->monitor) { GLFWvidmode mode; int xpos, ypos; _glfwPlatformGetVideoMode(window->monitor, &mode); _glfwPlatformGetMonitorPos(window->monitor, &xpos, &ypos); contentRect = NSMakeRect(xpos, ypos, mode.width, mode.height); } else contentRect = NSMakeRect(0, 0, wndconfig->width, wndconfig->height); window->ns.object = [[GLFWWindow alloc] initWithContentRect:contentRect styleMask:getStyleMask(window) backing:NSBackingStoreBuffered defer:NO]; if (window->ns.object == nil) { _glfwInputError(GLFW_PLATFORM_ERROR, "Cocoa: Failed to create window"); return GLFW_FALSE; } if (window->monitor) [window->ns.object setLevel:NSMainMenuWindowLevel + 1]; else { [(NSWindow*) window->ns.object center]; _glfw.ns.cascadePoint = NSPointToCGPoint([window->ns.object cascadeTopLeftFromPoint: NSPointFromCGPoint(_glfw.ns.cascadePoint)]); if (wndconfig->resizable) { const NSWindowCollectionBehavior behavior = NSWindowCollectionBehaviorFullScreenPrimary | NSWindowCollectionBehaviorManaged; [window->ns.object setCollectionBehavior:behavior]; } if (wndconfig->floating) [window->ns.object setLevel:NSFloatingWindowLevel]; if (wndconfig->maximized) [window->ns.object zoom:nil]; } if (strlen(wndconfig->ns.frameName)) [window->ns.object setFrameAutosaveName:@(wndconfig->ns.frameName)]; window->ns.view = [[GLFWContentView alloc] initWithGlfwWindow:window]; window->ns.retina = wndconfig->ns.retina; if (fbconfig->transparent) { [window->ns.object setOpaque:NO]; [window->ns.object setHasShadow:NO]; [window->ns.object setBackgroundColor:[NSColor clearColor]]; } [window->ns.object setContentView:window->ns.view]; [window->ns.object makeFirstResponder:window->ns.view]; [window->ns.object setTitle:@(wndconfig->title)]; [window->ns.object setDelegate:window->ns.delegate]; [window->ns.object setAcceptsMouseMovedEvents:YES]; [window->ns.object setRestorable:NO]; #if MAC_OS_X_VERSION_MAX_ALLOWED >= 101200 if ([window->ns.object respondsToSelector:@selector(setTabbingMode:)]) [window->ns.object setTabbingMode:NSWindowTabbingModeDisallowed]; #endif _glfwPlatformGetWindowSize(window, &window->ns.width, &window->ns.height); _glfwPlatformGetFramebufferSize(window, &window->ns.fbWidth, &window->ns.fbHeight); return GLFW_TRUE; } ////////////////////////////////////////////////////////////////////////// ////// GLFW internal API ////// ////////////////////////////////////////////////////////////////////////// // Transforms a y-coordinate between the CG display and NS screen spaces // float _glfwTransformYNS(float y) { return CGDisplayBounds(CGMainDisplayID()).size.height - y - 1; } ////////////////////////////////////////////////////////////////////////// ////// GLFW platform API ////// ////////////////////////////////////////////////////////////////////////// int _glfwPlatformCreateWindow(_GLFWwindow* window, const _GLFWwndconfig* wndconfig, const _GLFWctxconfig* ctxconfig, const _GLFWfbconfig* fbconfig) { @autoreleasepool { if (!_glfw.ns.finishedLaunching) [NSApp run]; if (!createNativeWindow(window, wndconfig, fbconfig)) return GLFW_FALSE; if (ctxconfig->client != GLFW_NO_API) { if (ctxconfig->source == GLFW_NATIVE_CONTEXT_API) { if (!_glfwInitNSGL()) return GLFW_FALSE; if (!_glfwCreateContextNSGL(window, ctxconfig, fbconfig)) return GLFW_FALSE; } else if (ctxconfig->source == GLFW_EGL_CONTEXT_API) { // EGL implementation on macOS use CALayer* EGLNativeWindowType so we // need to get the layer for EGL window surface creation. [window->ns.view setWantsLayer:YES]; window->ns.layer = [window->ns.view layer]; if (!_glfwInitEGL()) return GLFW_FALSE; if (!_glfwCreateContextEGL(window, ctxconfig, fbconfig)) return GLFW_FALSE; } else if (ctxconfig->source == GLFW_OSMESA_CONTEXT_API) { if (!_glfwInitOSMesa()) return GLFW_FALSE; if (!_glfwCreateContextOSMesa(window, ctxconfig, fbconfig)) return GLFW_FALSE; } } if (window->monitor) { _glfwPlatformShowWindow(window); _glfwPlatformFocusWindow(window); acquireMonitor(window); } return GLFW_TRUE; } // autoreleasepool } void _glfwPlatformDestroyWindow(_GLFWwindow* window) { @autoreleasepool { if (_glfw.ns.disabledCursorWindow == window) _glfw.ns.disabledCursorWindow = NULL; [window->ns.object orderOut:nil]; if (window->monitor) releaseMonitor(window); if (window->context.destroy) window->context.destroy(window); [window->ns.object setDelegate:nil]; [window->ns.delegate release]; window->ns.delegate = nil; [window->ns.view release]; window->ns.view = nil; [window->ns.object close]; window->ns.object = nil; // HACK: Allow Cocoa to catch up before returning _glfwPlatformPollEvents(); } // autoreleasepool } void _glfwPlatformSetWindowTitle(_GLFWwindow* window, const char* title) { @autoreleasepool { NSString* string = @(title); [window->ns.object setTitle:string]; // HACK: Set the miniwindow title explicitly as setTitle: doesn't update it // if the window lacks NSWindowStyleMaskTitled [window->ns.object setMiniwindowTitle:string]; } // autoreleasepool } void _glfwPlatformSetWindowIcon(_GLFWwindow* window, int count, const GLFWimage* images) { // Regular windows do not have icons } void _glfwPlatformGetWindowPos(_GLFWwindow* window, int* xpos, int* ypos) { @autoreleasepool { const NSRect contentRect = [window->ns.object contentRectForFrameRect:[window->ns.object frame]]; if (xpos) *xpos = contentRect.origin.x; if (ypos) *ypos = _glfwTransformYNS(contentRect.origin.y + contentRect.size.height - 1); } // autoreleasepool } void _glfwPlatformSetWindowPos(_GLFWwindow* window, int x, int y) { @autoreleasepool { const NSRect contentRect = [window->ns.view frame]; const NSRect dummyRect = NSMakeRect(x, _glfwTransformYNS(y + contentRect.size.height - 1), 0, 0); const NSRect frameRect = [window->ns.object frameRectForContentRect:dummyRect]; [window->ns.object setFrameOrigin:frameRect.origin]; } // autoreleasepool } void _glfwPlatformGetWindowSize(_GLFWwindow* window, int* width, int* height) { @autoreleasepool { const NSRect contentRect = [window->ns.view frame]; if (width) *width = contentRect.size.width; if (height) *height = contentRect.size.height; } // autoreleasepool } void _glfwPlatformSetWindowSize(_GLFWwindow* window, int width, int height) { @autoreleasepool { if (window->monitor) { if (window->monitor->window == window) acquireMonitor(window); } else { NSRect contentRect = [window->ns.object contentRectForFrameRect:[window->ns.object frame]]; contentRect.origin.y += contentRect.size.height - height; contentRect.size = NSMakeSize(width, height); [window->ns.object setFrame:[window->ns.object frameRectForContentRect:contentRect] display:YES]; } } // autoreleasepool } void _glfwPlatformSetWindowSizeLimits(_GLFWwindow* window, int minwidth, int minheight, int maxwidth, int maxheight) { @autoreleasepool { if (minwidth == GLFW_DONT_CARE || minheight == GLFW_DONT_CARE) [window->ns.object setContentMinSize:NSMakeSize(0, 0)]; else [window->ns.object setContentMinSize:NSMakeSize(minwidth, minheight)]; if (maxwidth == GLFW_DONT_CARE || maxheight == GLFW_DONT_CARE) [window->ns.object setContentMaxSize:NSMakeSize(DBL_MAX, DBL_MAX)]; else [window->ns.object setContentMaxSize:NSMakeSize(maxwidth, maxheight)]; } // autoreleasepool } void _glfwPlatformSetWindowAspectRatio(_GLFWwindow* window, int numer, int denom) { @autoreleasepool { if (numer == GLFW_DONT_CARE || denom == GLFW_DONT_CARE) [window->ns.object setResizeIncrements:NSMakeSize(1.0, 1.0)]; else [window->ns.object setContentAspectRatio:NSMakeSize(numer, denom)]; } // autoreleasepool } void _glfwPlatformGetFramebufferSize(_GLFWwindow* window, int* width, int* height) { @autoreleasepool { const NSRect contentRect = [window->ns.view frame]; const NSRect fbRect = [window->ns.view convertRectToBacking:contentRect]; if (width) *width = (int) fbRect.size.width; if (height) *height = (int) fbRect.size.height; } // autoreleasepool } void _glfwPlatformGetWindowFrameSize(_GLFWwindow* window, int* left, int* top, int* right, int* bottom) { @autoreleasepool { const NSRect contentRect = [window->ns.view frame]; const NSRect frameRect = [window->ns.object frameRectForContentRect:contentRect]; if (left) *left = contentRect.origin.x - frameRect.origin.x; if (top) *top = frameRect.origin.y + frameRect.size.height - contentRect.origin.y - contentRect.size.height; if (right) *right = frameRect.origin.x + frameRect.size.width - contentRect.origin.x - contentRect.size.width; if (bottom) *bottom = contentRect.origin.y - frameRect.origin.y; } // autoreleasepool } void _glfwPlatformGetWindowContentScale(_GLFWwindow* window, float* xscale, float* yscale) { @autoreleasepool { const NSRect points = [window->ns.view frame]; const NSRect pixels = [window->ns.view convertRectToBacking:points]; if (xscale) *xscale = (float) (pixels.size.width / points.size.width); if (yscale) *yscale = (float) (pixels.size.height / points.size.height); } // autoreleasepool } void _glfwPlatformIconifyWindow(_GLFWwindow* window) { @autoreleasepool { [window->ns.object miniaturize:nil]; } // autoreleasepool } void _glfwPlatformRestoreWindow(_GLFWwindow* window) { @autoreleasepool { if ([window->ns.object isMiniaturized]) [window->ns.object deminiaturize:nil]; else if ([window->ns.object isZoomed]) [window->ns.object zoom:nil]; } // autoreleasepool } void _glfwPlatformMaximizeWindow(_GLFWwindow* window) { @autoreleasepool { if (![window->ns.object isZoomed]) [window->ns.object zoom:nil]; } // autoreleasepool } void _glfwPlatformShowWindow(_GLFWwindow* window) { @autoreleasepool { [window->ns.object orderFront:nil]; } // autoreleasepool } void _glfwPlatformHideWindow(_GLFWwindow* window) { @autoreleasepool { [window->ns.object orderOut:nil]; } // autoreleasepool } void _glfwPlatformRequestWindowAttention(_GLFWwindow* window) { @autoreleasepool { [NSApp requestUserAttention:NSInformationalRequest]; } // autoreleasepool } void _glfwPlatformFocusWindow(_GLFWwindow* window) { @autoreleasepool { // Make us the active application // HACK: This is here to prevent applications using only hidden windows from // being activated, but should probably not be done every time any // window is shown [NSApp activateIgnoringOtherApps:YES]; [window->ns.object makeKeyAndOrderFront:nil]; } // autoreleasepool } void _glfwPlatformSetWindowMonitor(_GLFWwindow* window, _GLFWmonitor* monitor, int xpos, int ypos, int width, int height, int refreshRate) { @autoreleasepool { if (window->monitor == monitor) { if (monitor) { if (monitor->window == window) acquireMonitor(window); } else { const NSRect contentRect = NSMakeRect(xpos, _glfwTransformYNS(ypos + height - 1), width, height); const NSRect frameRect = [window->ns.object frameRectForContentRect:contentRect styleMask:getStyleMask(window)]; [window->ns.object setFrame:frameRect display:YES]; } return; } if (window->monitor) releaseMonitor(window); _glfwInputWindowMonitor(window, monitor); // HACK: Allow the state cached in Cocoa to catch up to reality // TODO: Solve this in a less terrible way _glfwPlatformPollEvents(); const NSUInteger styleMask = getStyleMask(window); [window->ns.object setStyleMask:styleMask]; // HACK: Changing the style mask can cause the first responder to be cleared [window->ns.object makeFirstResponder:window->ns.view]; if (window->monitor) { [window->ns.object setLevel:NSMainMenuWindowLevel + 1]; [window->ns.object setHasShadow:NO]; acquireMonitor(window); } else { NSRect contentRect = NSMakeRect(xpos, _glfwTransformYNS(ypos + height - 1), width, height); NSRect frameRect = [window->ns.object frameRectForContentRect:contentRect styleMask:styleMask]; [window->ns.object setFrame:frameRect display:YES]; if (window->numer != GLFW_DONT_CARE && window->denom != GLFW_DONT_CARE) { [window->ns.object setContentAspectRatio:NSMakeSize(window->numer, window->denom)]; } if (window->minwidth != GLFW_DONT_CARE && window->minheight != GLFW_DONT_CARE) { [window->ns.object setContentMinSize:NSMakeSize(window->minwidth, window->minheight)]; } if (window->maxwidth != GLFW_DONT_CARE && window->maxheight != GLFW_DONT_CARE) { [window->ns.object setContentMaxSize:NSMakeSize(window->maxwidth, window->maxheight)]; } if (window->floating) [window->ns.object setLevel:NSFloatingWindowLevel]; else [window->ns.object setLevel:NSNormalWindowLevel]; [window->ns.object setHasShadow:YES]; // HACK: Clearing NSWindowStyleMaskTitled resets and disables the window // title property but the miniwindow title property is unaffected [window->ns.object setTitle:[window->ns.object miniwindowTitle]]; } } // autoreleasepool } int _glfwPlatformWindowFocused(_GLFWwindow* window) { @autoreleasepool { return [window->ns.object isKeyWindow]; } // autoreleasepool } int _glfwPlatformWindowIconified(_GLFWwindow* window) { @autoreleasepool { return [window->ns.object isMiniaturized]; } // autoreleasepool } int _glfwPlatformWindowVisible(_GLFWwindow* window) { @autoreleasepool { return [window->ns.object isVisible]; } // autoreleasepool } int _glfwPlatformWindowMaximized(_GLFWwindow* window) { @autoreleasepool { return [window->ns.object isZoomed]; } // autoreleasepool } int _glfwPlatformWindowHovered(_GLFWwindow* window) { @autoreleasepool { const NSPoint point = [NSEvent mouseLocation]; if ([NSWindow windowNumberAtPoint:point belowWindowWithWindowNumber:0] != [window->ns.object windowNumber]) { return GLFW_FALSE; } return NSMouseInRect(point, [window->ns.object convertRectToScreen:[window->ns.view frame]], NO); } // autoreleasepool } int _glfwPlatformFramebufferTransparent(_GLFWwindow* window) { @autoreleasepool { return ![window->ns.object isOpaque] && ![window->ns.view isOpaque]; } // autoreleasepool } void _glfwPlatformSetWindowResizable(_GLFWwindow* window, GLFWbool enabled) { @autoreleasepool { [window->ns.object setStyleMask:getStyleMask(window)]; } // autoreleasepool } void _glfwPlatformSetWindowDecorated(_GLFWwindow* window, GLFWbool enabled) { @autoreleasepool { [window->ns.object setStyleMask:getStyleMask(window)]; [window->ns.object makeFirstResponder:window->ns.view]; } // autoreleasepool } void _glfwPlatformSetWindowFloating(_GLFWwindow* window, GLFWbool enabled) { @autoreleasepool { if (enabled) [window->ns.object setLevel:NSFloatingWindowLevel]; else [window->ns.object setLevel:NSNormalWindowLevel]; } // autoreleasepool } float _glfwPlatformGetWindowOpacity(_GLFWwindow* window) { @autoreleasepool { return (float) [window->ns.object alphaValue]; } // autoreleasepool } void _glfwPlatformSetWindowOpacity(_GLFWwindow* window, float opacity) { @autoreleasepool { [window->ns.object setAlphaValue:opacity]; } // autoreleasepool } void _glfwPlatformSetRawMouseMotion(_GLFWwindow *window, GLFWbool enabled) { } GLFWbool _glfwPlatformRawMouseMotionSupported(void) { return GLFW_FALSE; } void _glfwPlatformPollEvents(void) { @autoreleasepool { if (!_glfw.ns.finishedLaunching) [NSApp run]; for (;;) { NSEvent* event = [NSApp nextEventMatchingMask:NSEventMaskAny untilDate:[NSDate distantPast] inMode:NSDefaultRunLoopMode dequeue:YES]; if (event == nil) break; [NSApp sendEvent:event]; } } // autoreleasepool } void _glfwPlatformWaitEvents(void) { @autoreleasepool { if (!_glfw.ns.finishedLaunching) [NSApp run]; // I wanted to pass NO to dequeue:, and rely on PollEvents to // dequeue and send. For reasons not at all clear to me, passing // NO to dequeue: causes this method never to return. NSEvent *event = [NSApp nextEventMatchingMask:NSEventMaskAny untilDate:[NSDate distantFuture] inMode:NSDefaultRunLoopMode dequeue:YES]; [NSApp sendEvent:event]; _glfwPlatformPollEvents(); } // autoreleasepool } void _glfwPlatformWaitEventsTimeout(double timeout) { @autoreleasepool { if (!_glfw.ns.finishedLaunching) [NSApp run]; NSDate* date = [NSDate dateWithTimeIntervalSinceNow:timeout]; NSEvent* event = [NSApp nextEventMatchingMask:NSEventMaskAny untilDate:date inMode:NSDefaultRunLoopMode dequeue:YES]; if (event) [NSApp sendEvent:event]; _glfwPlatformPollEvents(); } // autoreleasepool } void _glfwPlatformPostEmptyEvent(void) { @autoreleasepool { if (!_glfw.ns.finishedLaunching) [NSApp run]; NSEvent* event = [NSEvent otherEventWithType:NSEventTypeApplicationDefined location:NSMakePoint(0, 0) modifierFlags:0 timestamp:0 windowNumber:0 context:nil subtype:0 data1:0 data2:0]; [NSApp postEvent:event atStart:YES]; } // autoreleasepool } void _glfwPlatformGetCursorPos(_GLFWwindow* window, double* xpos, double* ypos) { @autoreleasepool { const NSRect contentRect = [window->ns.view frame]; // NOTE: The returned location uses base 0,1 not 0,0 const NSPoint pos = [window->ns.object mouseLocationOutsideOfEventStream]; if (xpos) *xpos = pos.x; if (ypos) *ypos = contentRect.size.height - pos.y; } // autoreleasepool } void _glfwPlatformSetCursorPos(_GLFWwindow* window, double x, double y) { @autoreleasepool { updateCursorImage(window); const NSRect contentRect = [window->ns.view frame]; // NOTE: The returned location uses base 0,1 not 0,0 const NSPoint pos = [window->ns.object mouseLocationOutsideOfEventStream]; window->ns.cursorWarpDeltaX += x - pos.x; window->ns.cursorWarpDeltaY += y - contentRect.size.height + pos.y; if (window->monitor) { CGDisplayMoveCursorToPoint(window->monitor->ns.displayID, CGPointMake(x, y)); } else { const NSRect localRect = NSMakeRect(x, contentRect.size.height - y - 1, 0, 0); const NSRect globalRect = [window->ns.object convertRectToScreen:localRect]; const NSPoint globalPoint = globalRect.origin; CGWarpMouseCursorPosition(CGPointMake(globalPoint.x, _glfwTransformYNS(globalPoint.y))); } // HACK: Calling this right after setting the cursor position prevents macOS // from freezing the cursor for a fraction of a second afterwards if (window->cursorMode != GLFW_CURSOR_DISABLED) CGAssociateMouseAndMouseCursorPosition(true); } // autoreleasepool } void _glfwPlatformSetCursorMode(_GLFWwindow* window, int mode) { @autoreleasepool { if (_glfwPlatformWindowFocused(window)) updateCursorMode(window); } // autoreleasepool } const char* _glfwPlatformGetScancodeName(int scancode) { @autoreleasepool { if (scancode < 0 || scancode > 0xff || _glfw.ns.keycodes[scancode] == GLFW_KEY_UNKNOWN) { _glfwInputError(GLFW_INVALID_VALUE, "Invalid scancode %i", scancode); return NULL; } const int key = _glfw.ns.keycodes[scancode]; UInt32 deadKeyState = 0; UniChar characters[4]; UniCharCount characterCount = 0; if (UCKeyTranslate([(NSData*) _glfw.ns.unicodeData bytes], scancode, kUCKeyActionDisplay, 0, LMGetKbdType(), kUCKeyTranslateNoDeadKeysBit, &deadKeyState, sizeof(characters) / sizeof(characters[0]), &characterCount, characters) != noErr) { return NULL; } if (!characterCount) return NULL; CFStringRef string = CFStringCreateWithCharactersNoCopy(kCFAllocatorDefault, characters, characterCount, kCFAllocatorNull); CFStringGetCString(string, _glfw.ns.keynames[key], sizeof(_glfw.ns.keynames[key]), kCFStringEncodingUTF8); CFRelease(string); return _glfw.ns.keynames[key]; } // autoreleasepool } int _glfwPlatformGetKeyScancode(int key) { return _glfw.ns.scancodes[key]; } int _glfwPlatformCreateCursor(_GLFWcursor* cursor, const GLFWimage* image, int xhot, int yhot) { @autoreleasepool { NSImage* native; NSBitmapImageRep* rep; rep = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:NULL pixelsWide:image->width pixelsHigh:image->height bitsPerSample:8 samplesPerPixel:4 hasAlpha:YES isPlanar:NO colorSpaceName:NSCalibratedRGBColorSpace bitmapFormat:NSBitmapFormatAlphaNonpremultiplied bytesPerRow:image->width * 4 bitsPerPixel:32]; if (rep == nil) return GLFW_FALSE; memcpy([rep bitmapData], image->pixels, image->width * image->height * 4); native = [[NSImage alloc] initWithSize:NSMakeSize(image->width, image->height)]; [native addRepresentation:rep]; cursor->ns.object = [[NSCursor alloc] initWithImage:native hotSpot:NSMakePoint(xhot, yhot)]; [native release]; [rep release]; if (cursor->ns.object == nil) return GLFW_FALSE; return GLFW_TRUE; } // autoreleasepool } int _glfwPlatformCreateStandardCursor(_GLFWcursor* cursor, int shape) { @autoreleasepool { if (shape == GLFW_ARROW_CURSOR) cursor->ns.object = [NSCursor arrowCursor]; else if (shape == GLFW_IBEAM_CURSOR) cursor->ns.object = [NSCursor IBeamCursor]; else if (shape == GLFW_CROSSHAIR_CURSOR) cursor->ns.object = [NSCursor crosshairCursor]; else if (shape == GLFW_HAND_CURSOR) cursor->ns.object = [NSCursor pointingHandCursor]; else if (shape == GLFW_HRESIZE_CURSOR) cursor->ns.object = [NSCursor resizeLeftRightCursor]; else if (shape == GLFW_VRESIZE_CURSOR) cursor->ns.object = [NSCursor resizeUpDownCursor]; if (!cursor->ns.object) { _glfwInputError(GLFW_PLATFORM_ERROR, "Cocoa: Failed to retrieve standard cursor"); return GLFW_FALSE; } [cursor->ns.object retain]; return GLFW_TRUE; } // autoreleasepool } void _glfwPlatformDestroyCursor(_GLFWcursor* cursor) { @autoreleasepool { if (cursor->ns.object) [(NSCursor*) cursor->ns.object release]; } // autoreleasepool } void _glfwPlatformSetCursor(_GLFWwindow* window, _GLFWcursor* cursor) { @autoreleasepool { if (cursorInContentArea(window)) updateCursorImage(window); } // autoreleasepool } void _glfwPlatformSetClipboardString(const char* string) { @autoreleasepool { NSPasteboard* pasteboard = [NSPasteboard generalPasteboard]; [pasteboard declareTypes:@[NSPasteboardTypeString] owner:nil]; [pasteboard setString:@(string) forType:NSPasteboardTypeString]; } // autoreleasepool } const char* _glfwPlatformGetClipboardString(void) { @autoreleasepool { NSPasteboard* pasteboard = [NSPasteboard generalPasteboard]; if (![[pasteboard types] containsObject:NSPasteboardTypeString]) { _glfwInputError(GLFW_FORMAT_UNAVAILABLE, "Cocoa: Failed to retrieve string from pasteboard"); return NULL; } NSString* object = [pasteboard stringForType:NSPasteboardTypeString]; if (!object) { _glfwInputError(GLFW_PLATFORM_ERROR, "Cocoa: Failed to retrieve object from pasteboard"); return NULL; } free(_glfw.ns.clipboardString); _glfw.ns.clipboardString = _glfw_strdup([object UTF8String]); return _glfw.ns.clipboardString; } // autoreleasepool } void _glfwPlatformGetRequiredInstanceExtensions(char** extensions) { if (_glfw.vk.KHR_surface && _glfw.vk.EXT_metal_surface) { extensions[0] = "VK_KHR_surface"; extensions[1] = "VK_EXT_metal_surface"; } else if (_glfw.vk.KHR_surface && _glfw.vk.MVK_macos_surface) { extensions[0] = "VK_KHR_surface"; extensions[1] = "VK_MVK_macos_surface"; } } int _glfwPlatformGetPhysicalDevicePresentationSupport(VkInstance instance, VkPhysicalDevice device, uint32_t queuefamily) { return GLFW_TRUE; } VkResult _glfwPlatformCreateWindowSurface(VkInstance instance, _GLFWwindow* window, const VkAllocationCallbacks* allocator, VkSurfaceKHR* surface) { @autoreleasepool { #if MAC_OS_X_VERSION_MAX_ALLOWED >= 101100 // HACK: Dynamically load Core Animation to avoid adding an extra // dependency for the majority who don't use MoltenVK NSBundle* bundle = [NSBundle bundleWithPath:@"/System/Library/Frameworks/QuartzCore.framework"]; if (!bundle) { _glfwInputError(GLFW_PLATFORM_ERROR, "Cocoa: Failed to find QuartzCore.framework"); return VK_ERROR_EXTENSION_NOT_PRESENT; } // NOTE: Create the layer here as makeBackingLayer should not return nil window->ns.layer = [[bundle classNamed:@"CAMetalLayer"] layer]; if (!window->ns.layer) { _glfwInputError(GLFW_PLATFORM_ERROR, "Cocoa: Failed to create layer for view"); return VK_ERROR_EXTENSION_NOT_PRESENT; } if (window->ns.retina) [window->ns.layer setContentsScale:[window->ns.object backingScaleFactor]]; [window->ns.view setLayer:window->ns.layer]; [window->ns.view setWantsLayer:YES]; VkResult err; if (_glfw.vk.EXT_metal_surface) { VkMetalSurfaceCreateInfoEXT sci; PFN_vkCreateMetalSurfaceEXT vkCreateMetalSurfaceEXT; vkCreateMetalSurfaceEXT = (PFN_vkCreateMetalSurfaceEXT) vkGetInstanceProcAddr(instance, "vkCreateMetalSurfaceEXT"); if (!vkCreateMetalSurfaceEXT) { _glfwInputError(GLFW_API_UNAVAILABLE, "Cocoa: Vulkan instance missing VK_EXT_metal_surface extension"); return VK_ERROR_EXTENSION_NOT_PRESENT; } memset(&sci, 0, sizeof(sci)); sci.sType = VK_STRUCTURE_TYPE_METAL_SURFACE_CREATE_INFO_EXT; sci.pLayer = window->ns.layer; err = vkCreateMetalSurfaceEXT(instance, &sci, allocator, surface); } else { VkMacOSSurfaceCreateInfoMVK sci; PFN_vkCreateMacOSSurfaceMVK vkCreateMacOSSurfaceMVK; vkCreateMacOSSurfaceMVK = (PFN_vkCreateMacOSSurfaceMVK) vkGetInstanceProcAddr(instance, "vkCreateMacOSSurfaceMVK"); if (!vkCreateMacOSSurfaceMVK) { _glfwInputError(GLFW_API_UNAVAILABLE, "Cocoa: Vulkan instance missing VK_MVK_macos_surface extension"); return VK_ERROR_EXTENSION_NOT_PRESENT; } memset(&sci, 0, sizeof(sci)); sci.sType = VK_STRUCTURE_TYPE_MACOS_SURFACE_CREATE_INFO_MVK; sci.pView = window->ns.view; err = vkCreateMacOSSurfaceMVK(instance, &sci, allocator, surface); } if (err) { _glfwInputError(GLFW_PLATFORM_ERROR, "Cocoa: Failed to create Vulkan surface: %s", _glfwGetVulkanResultString(err)); } return err; #else return VK_ERROR_EXTENSION_NOT_PRESENT; #endif } // autoreleasepool } ////////////////////////////////////////////////////////////////////////// ////// GLFW native API ////// ////////////////////////////////////////////////////////////////////////// GLFWAPI id glfwGetCocoaWindow(GLFWwindow* handle) { _GLFWwindow* window = (_GLFWwindow*) handle; _GLFW_REQUIRE_INIT_OR_RETURN(nil); return window->ns.object; } #endif #ifndef HEADER_GUARD_COCOA_TIME_C #define HEADER_GUARD_COCOA_TIME_C //======================================================================== // GLFW 3.3.7 macOS - www.glfw.org //------------------------------------------------------------------------ // Copyright (c) 2009-2016 Camilla Löwy // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would // be appreciated but is not required. // // 2. Altered source versions must be plainly marked as such, and must not // be misrepresented as being the original software. // // 3. This notice may not be removed or altered from any source // distribution. // //======================================================================== // It is fine to use C99 in this file because it will not be built with VS //======================================================================== #include ////////////////////////////////////////////////////////////////////////// ////// GLFW internal API ////// ////////////////////////////////////////////////////////////////////////// // Initialise timer // void _glfwInitTimerNS(void) { mach_timebase_info_data_t info; mach_timebase_info(&info); _glfw.timer.ns.frequency = (info.denom * 1e9) / info.numer; } ////////////////////////////////////////////////////////////////////////// ////// GLFW platform API ////// ////////////////////////////////////////////////////////////////////////// uint64_t _glfwPlatformGetTimerValue(void) { return mach_absolute_time(); } uint64_t _glfwPlatformGetTimerFrequency(void) { return _glfw.timer.ns.frequency; } #endif #endif #if !defined _GLFW_COCOA && !defined _GLFW_WIN32 #ifndef HEADER_GUARD_POSIX_TIME_C #define HEADER_GUARD_POSIX_TIME_C //======================================================================== // GLFW 3.3.7 POSIX - www.glfw.org //------------------------------------------------------------------------ // Copyright (c) 2002-2006 Marcus Geelnard // Copyright (c) 2006-2017 Camilla Löwy // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would // be appreciated but is not required. // // 2. Altered source versions must be plainly marked as such, and must not // be misrepresented as being the original software. // // 3. This notice may not be removed or altered from any source // distribution. // //======================================================================== // It is fine to use C99 in this file because it will not be built with VS //======================================================================== #include #include ////////////////////////////////////////////////////////////////////////// ////// GLFW internal API ////// ////////////////////////////////////////////////////////////////////////// // Initialise timer // void _glfwInitTimerPOSIX(void) { #if defined(CLOCK_MONOTONIC) struct timespec ts; if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) { _glfw.timer.posix.monotonic = GLFW_TRUE; _glfw.timer.posix.frequency = 1000000000; } else #endif { _glfw.timer.posix.monotonic = GLFW_FALSE; _glfw.timer.posix.frequency = 1000000; } } ////////////////////////////////////////////////////////////////////////// ////// GLFW platform API ////// ////////////////////////////////////////////////////////////////////////// uint64_t _glfwPlatformGetTimerValue(void) { #if defined(CLOCK_MONOTONIC) if (_glfw.timer.posix.monotonic) { struct timespec ts; clock_gettime(CLOCK_MONOTONIC, &ts); return (uint64_t) ts.tv_sec * (uint64_t) 1000000000 + (uint64_t) ts.tv_nsec; } else #endif { struct timeval tv; gettimeofday(&tv, NULL); return (uint64_t) tv.tv_sec * (uint64_t) 1000000 + (uint64_t) tv.tv_usec; } } uint64_t _glfwPlatformGetTimerFrequency(void) { return _glfw.timer.posix.frequency; } #endif #endif #if !defined _GLFW_WIN32 #ifndef HEADER_GUARD_POSIX_THREAD_C #define HEADER_GUARD_POSIX_THREAD_C //======================================================================== // GLFW 3.3.7 POSIX - www.glfw.org //------------------------------------------------------------------------ // Copyright (c) 2002-2006 Marcus Geelnard // Copyright (c) 2006-2017 Camilla Löwy // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would // be appreciated but is not required. // // 2. Altered source versions must be plainly marked as such, and must not // be misrepresented as being the original software. // // 3. This notice may not be removed or altered from any source // distribution. // //======================================================================== // It is fine to use C99 in this file because it will not be built with VS //======================================================================== #include #include ////////////////////////////////////////////////////////////////////////// ////// GLFW platform API ////// ////////////////////////////////////////////////////////////////////////// GLFWbool _glfwPlatformCreateTls(_GLFWtls* tls) { assert(tls->posix.allocated == GLFW_FALSE); if (pthread_key_create(&tls->posix.key, NULL) != 0) { _glfwInputError(GLFW_PLATFORM_ERROR, "POSIX: Failed to create context TLS"); return GLFW_FALSE; } tls->posix.allocated = GLFW_TRUE; return GLFW_TRUE; } void _glfwPlatformDestroyTls(_GLFWtls* tls) { if (tls->posix.allocated) pthread_key_delete(tls->posix.key); memset(tls, 0, sizeof(_GLFWtls)); } void* _glfwPlatformGetTls(_GLFWtls* tls) { assert(tls->posix.allocated == GLFW_TRUE); return pthread_getspecific(tls->posix.key); } void _glfwPlatformSetTls(_GLFWtls* tls, void* value) { assert(tls->posix.allocated == GLFW_TRUE); pthread_setspecific(tls->posix.key, value); } GLFWbool _glfwPlatformCreateMutex(_GLFWmutex* mutex) { assert(mutex->posix.allocated == GLFW_FALSE); if (pthread_mutex_init(&mutex->posix.handle, NULL) != 0) { _glfwInputError(GLFW_PLATFORM_ERROR, "POSIX: Failed to create mutex"); return GLFW_FALSE; } return mutex->posix.allocated = GLFW_TRUE; } void _glfwPlatformDestroyMutex(_GLFWmutex* mutex) { if (mutex->posix.allocated) pthread_mutex_destroy(&mutex->posix.handle); memset(mutex, 0, sizeof(_GLFWmutex)); } void _glfwPlatformLockMutex(_GLFWmutex* mutex) { assert(mutex->posix.allocated == GLFW_TRUE); pthread_mutex_lock(&mutex->posix.handle); } void _glfwPlatformUnlockMutex(_GLFWmutex* mutex) { assert(mutex->posix.allocated == GLFW_TRUE); pthread_mutex_unlock(&mutex->posix.handle); } #endif #endif #if !defined _GLFW_COCOA && !defined _GLFW_WIN32 && !defined _GLFW_OSMESA #ifndef HEADER_GUARD_LINUX_JOYSTICK_C #define HEADER_GUARD_LINUX_JOYSTICK_C //======================================================================== // GLFW 3.3.7 Linux - www.glfw.org //------------------------------------------------------------------------ // Copyright (c) 2002-2006 Marcus Geelnard // Copyright (c) 2006-2017 Camilla Löwy // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would // be appreciated but is not required. // // 2. Altered source versions must be plainly marked as such, and must not // be misrepresented as being the original software. // // 3. This notice may not be removed or altered from any source // distribution. // //======================================================================== // It is fine to use C99 in this file because it will not be built with VS //======================================================================== #include #include #include #include #include #include #include #include #include #include #ifndef SYN_DROPPED // < v2.6.39 kernel headers // Workaround for CentOS-6, which is supported till 2020-11-30, but still on v2.6.32 #define SYN_DROPPED 3 #endif // Apply an EV_KEY event to the specified joystick // static void handleKeyEvent(_GLFWjoystick* js, int code, int value) { _glfwInputJoystickButton(js, js->linjs.keyMap[code - BTN_MISC], value ? GLFW_PRESS : GLFW_RELEASE); } // Apply an EV_ABS event to the specified joystick // static void handleAbsEvent(_GLFWjoystick* js, int code, int value) { const int index = js->linjs.absMap[code]; if (code >= ABS_HAT0X && code <= ABS_HAT3Y) { static const char stateMap[3][3] = { { GLFW_HAT_CENTERED, GLFW_HAT_UP, GLFW_HAT_DOWN }, { GLFW_HAT_LEFT, GLFW_HAT_LEFT_UP, GLFW_HAT_LEFT_DOWN }, { GLFW_HAT_RIGHT, GLFW_HAT_RIGHT_UP, GLFW_HAT_RIGHT_DOWN }, }; const int hat = (code - ABS_HAT0X) / 2; const int axis = (code - ABS_HAT0X) % 2; int* state = js->linjs.hats[hat]; // NOTE: Looking at several input drivers, it seems all hat events use // -1 for left / up, 0 for centered and 1 for right / down if (value == 0) state[axis] = 0; else if (value < 0) state[axis] = 1; else if (value > 0) state[axis] = 2; _glfwInputJoystickHat(js, index, stateMap[state[0]][state[1]]); } else { const struct input_absinfo* info = &js->linjs.absInfo[code]; float normalized = value; const int range = info->maximum - info->minimum; if (range) { // Normalize to 0.0 -> 1.0 normalized = (normalized - info->minimum) / range; // Normalize to -1.0 -> 1.0 normalized = normalized * 2.0f - 1.0f; } _glfwInputJoystickAxis(js, index, normalized); } } // Poll state of absolute axes // static void pollAbsState(_GLFWjoystick* js) { for (int code = 0; code < ABS_CNT; code++) { if (js->linjs.absMap[code] < 0) continue; struct input_absinfo* info = &js->linjs.absInfo[code]; if (ioctl(js->linjs.fd, EVIOCGABS(code), info) < 0) continue; handleAbsEvent(js, code, info->value); } } #define isBitSet(bit, arr) (arr[(bit) / 8] & (1 << ((bit) % 8))) // Attempt to open the specified joystick device // static GLFWbool openJoystickDevice(const char* path) { for (int jid = 0; jid <= GLFW_JOYSTICK_LAST; jid++) { if (!_glfw.joysticks[jid].present) continue; if (strcmp(_glfw.joysticks[jid].linjs.path, path) == 0) return GLFW_FALSE; } _GLFWjoystickLinux linjs = {0}; linjs.fd = open(path, O_RDONLY | O_NONBLOCK); if (linjs.fd == -1) return GLFW_FALSE; char evBits[(EV_CNT + 7) / 8] = {0}; char keyBits[(KEY_CNT + 7) / 8] = {0}; char absBits[(ABS_CNT + 7) / 8] = {0}; struct input_id id; if (ioctl(linjs.fd, EVIOCGBIT(0, sizeof(evBits)), evBits) < 0 || ioctl(linjs.fd, EVIOCGBIT(EV_KEY, sizeof(keyBits)), keyBits) < 0 || ioctl(linjs.fd, EVIOCGBIT(EV_ABS, sizeof(absBits)), absBits) < 0 || ioctl(linjs.fd, EVIOCGID, &id) < 0) { _glfwInputError(GLFW_PLATFORM_ERROR, "Linux: Failed to query input device: %s", strerror(errno)); close(linjs.fd); return GLFW_FALSE; } // Ensure this device supports the events expected of a joystick if (!isBitSet(EV_KEY, evBits) || !isBitSet(EV_ABS, evBits)) { close(linjs.fd); return GLFW_FALSE; } char name[256] = ""; if (ioctl(linjs.fd, EVIOCGNAME(sizeof(name)), name) < 0) strncpy(name, "Unknown", sizeof(name)); char guid[33] = ""; // Generate a joystick GUID that matches the SDL 2.0.5+ one if (id.vendor && id.product && id.version) { sprintf(guid, "%02x%02x0000%02x%02x0000%02x%02x0000%02x%02x0000", id.bustype & 0xff, id.bustype >> 8, id.vendor & 0xff, id.vendor >> 8, id.product & 0xff, id.product >> 8, id.version & 0xff, id.version >> 8); } else { sprintf(guid, "%02x%02x0000%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x00", id.bustype & 0xff, id.bustype >> 8, name[0], name[1], name[2], name[3], name[4], name[5], name[6], name[7], name[8], name[9], name[10]); } int axisCount = 0, buttonCount = 0, hatCount = 0; for (int code = BTN_MISC; code < KEY_CNT; code++) { if (!isBitSet(code, keyBits)) continue; linjs.keyMap[code - BTN_MISC] = buttonCount; buttonCount++; } for (int code = 0; code < ABS_CNT; code++) { linjs.absMap[code] = -1; if (!isBitSet(code, absBits)) continue; if (code >= ABS_HAT0X && code <= ABS_HAT3Y) { linjs.absMap[code] = hatCount; hatCount++; // Skip the Y axis code++; } else { if (ioctl(linjs.fd, EVIOCGABS(code), &linjs.absInfo[code]) < 0) continue; linjs.absMap[code] = axisCount; axisCount++; } } _GLFWjoystick* js = _glfwAllocJoystick(name, guid, axisCount, buttonCount, hatCount); if (!js) { close(linjs.fd); return GLFW_FALSE; } strncpy(linjs.path, path, sizeof(linjs.path) - 1); memcpy(&js->linjs, &linjs, sizeof(linjs)); pollAbsState(js); _glfwInputJoystick(js, GLFW_CONNECTED); return GLFW_TRUE; } #undef isBitSet // Frees all resources associated with the specified joystick // static void closeJoystick(_GLFWjoystick* js) { close(js->linjs.fd); _glfwFreeJoystick(js); _glfwInputJoystick(js, GLFW_DISCONNECTED); } // Lexically compare joysticks by name; used by qsort // static int compareJoysticks(const void* fp, const void* sp) { const _GLFWjoystick* fj = fp; const _GLFWjoystick* sj = sp; return strcmp(fj->linjs.path, sj->linjs.path); } ////////////////////////////////////////////////////////////////////////// ////// GLFW internal API ////// ////////////////////////////////////////////////////////////////////////// // Initialize joystick interface // GLFWbool _glfwInitJoysticksLinux(void) { const char* dirname = "/dev/input"; _glfw.linjs.inotify = inotify_init1(IN_NONBLOCK | IN_CLOEXEC); if (_glfw.linjs.inotify > 0) { // HACK: Register for IN_ATTRIB to get notified when udev is done // This works well in practice but the true way is libudev _glfw.linjs.watch = inotify_add_watch(_glfw.linjs.inotify, dirname, IN_CREATE | IN_ATTRIB | IN_DELETE); } // Continue without device connection notifications if inotify fails if (regcomp(&_glfw.linjs.regex, "^event[0-9]\\+$", 0) != 0) { _glfwInputError(GLFW_PLATFORM_ERROR, "Linux: Failed to compile regex"); return GLFW_FALSE; } int count = 0; DIR* dir = opendir(dirname); if (dir) { struct dirent* entry; while ((entry = readdir(dir))) { regmatch_t match; if (regexec(&_glfw.linjs.regex, entry->d_name, 1, &match, 0) != 0) continue; char path[PATH_MAX]; snprintf(path, sizeof(path), "%s/%s", dirname, entry->d_name); if (openJoystickDevice(path)) count++; } closedir(dir); } // Continue with no joysticks if enumeration fails qsort(_glfw.joysticks, count, sizeof(_GLFWjoystick), compareJoysticks); return GLFW_TRUE; } // Close all opened joystick handles // void _glfwTerminateJoysticksLinux(void) { int jid; for (jid = 0; jid <= GLFW_JOYSTICK_LAST; jid++) { _GLFWjoystick* js = _glfw.joysticks + jid; if (js->present) closeJoystick(js); } regfree(&_glfw.linjs.regex); if (_glfw.linjs.inotify > 0) { if (_glfw.linjs.watch > 0) inotify_rm_watch(_glfw.linjs.inotify, _glfw.linjs.watch); close(_glfw.linjs.inotify); } } void _glfwDetectJoystickConnectionLinux(void) { if (_glfw.linjs.inotify <= 0) return; ssize_t offset = 0; char buffer[16384]; const ssize_t size = read(_glfw.linjs.inotify, buffer, sizeof(buffer)); while (size > offset) { regmatch_t match; const struct inotify_event* e = (struct inotify_event*) (buffer + offset); offset += sizeof(struct inotify_event) + e->len; if (regexec(&_glfw.linjs.regex, e->name, 1, &match, 0) != 0) continue; char path[PATH_MAX]; snprintf(path, sizeof(path), "/dev/input/%s", e->name); if (e->mask & (IN_CREATE | IN_ATTRIB)) openJoystickDevice(path); else if (e->mask & IN_DELETE) { for (int jid = 0; jid <= GLFW_JOYSTICK_LAST; jid++) { if (strcmp(_glfw.joysticks[jid].linjs.path, path) == 0) { closeJoystick(_glfw.joysticks + jid); break; } } } } } ////////////////////////////////////////////////////////////////////////// ////// GLFW platform API ////// ////////////////////////////////////////////////////////////////////////// int _glfwPlatformPollJoystick(_GLFWjoystick* js, int mode) { // Read all queued events (non-blocking) for (;;) { struct input_event e; errno = 0; if (read(js->linjs.fd, &e, sizeof(e)) < 0) { // Reset the joystick slot if the device was disconnected if (errno == ENODEV) closeJoystick(js); break; } if (e.type == EV_SYN) { if (e.code == SYN_DROPPED) _glfw.linjs.dropped = GLFW_TRUE; else if (e.code == SYN_REPORT) { _glfw.linjs.dropped = GLFW_FALSE; pollAbsState(js); } } if (_glfw.linjs.dropped) continue; if (e.type == EV_KEY) handleKeyEvent(js, e.code, e.value); else if (e.type == EV_ABS) handleAbsEvent(js, e.code, e.value); } return js->present; } void _glfwPlatformUpdateGamepadGUID(char* guid) { } #endif #ifndef HEADER_GUARD_XKB_UNICODE_C #define HEADER_GUARD_XKB_UNICODE_C //======================================================================== // GLFW 3.3.7 X11 - www.glfw.org //------------------------------------------------------------------------ // Copyright (c) 2002-2006 Marcus Geelnard // Copyright (c) 2006-2017 Camilla Löwy // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would // be appreciated but is not required. // // 2. Altered source versions must be plainly marked as such, and must not // be misrepresented as being the original software. // // 3. This notice may not be removed or altered from any source // distribution. // //======================================================================== // It is fine to use C99 in this file because it will not be built with VS //======================================================================== /* * Marcus: This code was originally written by Markus G. Kuhn. * I have made some slight changes (trimmed it down a bit from >60 KB to * 20 KB), but the functionality is the same. */ /* * This module converts keysym values into the corresponding ISO 10646 * (UCS, Unicode) values. * * The array keysymtab[] contains pairs of X11 keysym values for graphical * characters and the corresponding Unicode value. The function * _glfwKeySym2Unicode() maps a keysym onto a Unicode value using a binary * search, therefore keysymtab[] must remain SORTED by keysym value. * * We allow to represent any UCS character in the range U-00000000 to * U-00FFFFFF by a keysym value in the range 0x01000000 to 0x01ffffff. * This admittedly does not cover the entire 31-bit space of UCS, but * it does cover all of the characters up to U-10FFFF, which can be * represented by UTF-16, and more, and it is very unlikely that higher * UCS codes will ever be assigned by ISO. So to get Unicode character * U+ABCD you can directly use keysym 0x0100abcd. * * Original author: Markus G. Kuhn , University of * Cambridge, April 2001 * * Special thanks to Richard Verhoeven for preparing * an initial draft of the mapping table. * */ //************************************************************************ //**** KeySym to Unicode mapping table **** //************************************************************************ static const struct codepair { unsigned short keysym; unsigned short ucs; } keysymtab[] = { { 0x01a1, 0x0104 }, { 0x01a2, 0x02d8 }, { 0x01a3, 0x0141 }, { 0x01a5, 0x013d }, { 0x01a6, 0x015a }, { 0x01a9, 0x0160 }, { 0x01aa, 0x015e }, { 0x01ab, 0x0164 }, { 0x01ac, 0x0179 }, { 0x01ae, 0x017d }, { 0x01af, 0x017b }, { 0x01b1, 0x0105 }, { 0x01b2, 0x02db }, { 0x01b3, 0x0142 }, { 0x01b5, 0x013e }, { 0x01b6, 0x015b }, { 0x01b7, 0x02c7 }, { 0x01b9, 0x0161 }, { 0x01ba, 0x015f }, { 0x01bb, 0x0165 }, { 0x01bc, 0x017a }, { 0x01bd, 0x02dd }, { 0x01be, 0x017e }, { 0x01bf, 0x017c }, { 0x01c0, 0x0154 }, { 0x01c3, 0x0102 }, { 0x01c5, 0x0139 }, { 0x01c6, 0x0106 }, { 0x01c8, 0x010c }, { 0x01ca, 0x0118 }, { 0x01cc, 0x011a }, { 0x01cf, 0x010e }, { 0x01d0, 0x0110 }, { 0x01d1, 0x0143 }, { 0x01d2, 0x0147 }, { 0x01d5, 0x0150 }, { 0x01d8, 0x0158 }, { 0x01d9, 0x016e }, { 0x01db, 0x0170 }, { 0x01de, 0x0162 }, { 0x01e0, 0x0155 }, { 0x01e3, 0x0103 }, { 0x01e5, 0x013a }, { 0x01e6, 0x0107 }, { 0x01e8, 0x010d }, { 0x01ea, 0x0119 }, { 0x01ec, 0x011b }, { 0x01ef, 0x010f }, { 0x01f0, 0x0111 }, { 0x01f1, 0x0144 }, { 0x01f2, 0x0148 }, { 0x01f5, 0x0151 }, { 0x01f8, 0x0159 }, { 0x01f9, 0x016f }, { 0x01fb, 0x0171 }, { 0x01fe, 0x0163 }, { 0x01ff, 0x02d9 }, { 0x02a1, 0x0126 }, { 0x02a6, 0x0124 }, { 0x02a9, 0x0130 }, { 0x02ab, 0x011e }, { 0x02ac, 0x0134 }, { 0x02b1, 0x0127 }, { 0x02b6, 0x0125 }, { 0x02b9, 0x0131 }, { 0x02bb, 0x011f }, { 0x02bc, 0x0135 }, { 0x02c5, 0x010a }, { 0x02c6, 0x0108 }, { 0x02d5, 0x0120 }, { 0x02d8, 0x011c }, { 0x02dd, 0x016c }, { 0x02de, 0x015c }, { 0x02e5, 0x010b }, { 0x02e6, 0x0109 }, { 0x02f5, 0x0121 }, { 0x02f8, 0x011d }, { 0x02fd, 0x016d }, { 0x02fe, 0x015d }, { 0x03a2, 0x0138 }, { 0x03a3, 0x0156 }, { 0x03a5, 0x0128 }, { 0x03a6, 0x013b }, { 0x03aa, 0x0112 }, { 0x03ab, 0x0122 }, { 0x03ac, 0x0166 }, { 0x03b3, 0x0157 }, { 0x03b5, 0x0129 }, { 0x03b6, 0x013c }, { 0x03ba, 0x0113 }, { 0x03bb, 0x0123 }, { 0x03bc, 0x0167 }, { 0x03bd, 0x014a }, { 0x03bf, 0x014b }, { 0x03c0, 0x0100 }, { 0x03c7, 0x012e }, { 0x03cc, 0x0116 }, { 0x03cf, 0x012a }, { 0x03d1, 0x0145 }, { 0x03d2, 0x014c }, { 0x03d3, 0x0136 }, { 0x03d9, 0x0172 }, { 0x03dd, 0x0168 }, { 0x03de, 0x016a }, { 0x03e0, 0x0101 }, { 0x03e7, 0x012f }, { 0x03ec, 0x0117 }, { 0x03ef, 0x012b }, { 0x03f1, 0x0146 }, { 0x03f2, 0x014d }, { 0x03f3, 0x0137 }, { 0x03f9, 0x0173 }, { 0x03fd, 0x0169 }, { 0x03fe, 0x016b }, { 0x047e, 0x203e }, { 0x04a1, 0x3002 }, { 0x04a2, 0x300c }, { 0x04a3, 0x300d }, { 0x04a4, 0x3001 }, { 0x04a5, 0x30fb }, { 0x04a6, 0x30f2 }, { 0x04a7, 0x30a1 }, { 0x04a8, 0x30a3 }, { 0x04a9, 0x30a5 }, { 0x04aa, 0x30a7 }, { 0x04ab, 0x30a9 }, { 0x04ac, 0x30e3 }, { 0x04ad, 0x30e5 }, { 0x04ae, 0x30e7 }, { 0x04af, 0x30c3 }, { 0x04b0, 0x30fc }, { 0x04b1, 0x30a2 }, { 0x04b2, 0x30a4 }, { 0x04b3, 0x30a6 }, { 0x04b4, 0x30a8 }, { 0x04b5, 0x30aa }, { 0x04b6, 0x30ab }, { 0x04b7, 0x30ad }, { 0x04b8, 0x30af }, { 0x04b9, 0x30b1 }, { 0x04ba, 0x30b3 }, { 0x04bb, 0x30b5 }, { 0x04bc, 0x30b7 }, { 0x04bd, 0x30b9 }, { 0x04be, 0x30bb }, { 0x04bf, 0x30bd }, { 0x04c0, 0x30bf }, { 0x04c1, 0x30c1 }, { 0x04c2, 0x30c4 }, { 0x04c3, 0x30c6 }, { 0x04c4, 0x30c8 }, { 0x04c5, 0x30ca }, { 0x04c6, 0x30cb }, { 0x04c7, 0x30cc }, { 0x04c8, 0x30cd }, { 0x04c9, 0x30ce }, { 0x04ca, 0x30cf }, { 0x04cb, 0x30d2 }, { 0x04cc, 0x30d5 }, { 0x04cd, 0x30d8 }, { 0x04ce, 0x30db }, { 0x04cf, 0x30de }, { 0x04d0, 0x30df }, { 0x04d1, 0x30e0 }, { 0x04d2, 0x30e1 }, { 0x04d3, 0x30e2 }, { 0x04d4, 0x30e4 }, { 0x04d5, 0x30e6 }, { 0x04d6, 0x30e8 }, { 0x04d7, 0x30e9 }, { 0x04d8, 0x30ea }, { 0x04d9, 0x30eb }, { 0x04da, 0x30ec }, { 0x04db, 0x30ed }, { 0x04dc, 0x30ef }, { 0x04dd, 0x30f3 }, { 0x04de, 0x309b }, { 0x04df, 0x309c }, { 0x05ac, 0x060c }, { 0x05bb, 0x061b }, { 0x05bf, 0x061f }, { 0x05c1, 0x0621 }, { 0x05c2, 0x0622 }, { 0x05c3, 0x0623 }, { 0x05c4, 0x0624 }, { 0x05c5, 0x0625 }, { 0x05c6, 0x0626 }, { 0x05c7, 0x0627 }, { 0x05c8, 0x0628 }, { 0x05c9, 0x0629 }, { 0x05ca, 0x062a }, { 0x05cb, 0x062b }, { 0x05cc, 0x062c }, { 0x05cd, 0x062d }, { 0x05ce, 0x062e }, { 0x05cf, 0x062f }, { 0x05d0, 0x0630 }, { 0x05d1, 0x0631 }, { 0x05d2, 0x0632 }, { 0x05d3, 0x0633 }, { 0x05d4, 0x0634 }, { 0x05d5, 0x0635 }, { 0x05d6, 0x0636 }, { 0x05d7, 0x0637 }, { 0x05d8, 0x0638 }, { 0x05d9, 0x0639 }, { 0x05da, 0x063a }, { 0x05e0, 0x0640 }, { 0x05e1, 0x0641 }, { 0x05e2, 0x0642 }, { 0x05e3, 0x0643 }, { 0x05e4, 0x0644 }, { 0x05e5, 0x0645 }, { 0x05e6, 0x0646 }, { 0x05e7, 0x0647 }, { 0x05e8, 0x0648 }, { 0x05e9, 0x0649 }, { 0x05ea, 0x064a }, { 0x05eb, 0x064b }, { 0x05ec, 0x064c }, { 0x05ed, 0x064d }, { 0x05ee, 0x064e }, { 0x05ef, 0x064f }, { 0x05f0, 0x0650 }, { 0x05f1, 0x0651 }, { 0x05f2, 0x0652 }, { 0x06a1, 0x0452 }, { 0x06a2, 0x0453 }, { 0x06a3, 0x0451 }, { 0x06a4, 0x0454 }, { 0x06a5, 0x0455 }, { 0x06a6, 0x0456 }, { 0x06a7, 0x0457 }, { 0x06a8, 0x0458 }, { 0x06a9, 0x0459 }, { 0x06aa, 0x045a }, { 0x06ab, 0x045b }, { 0x06ac, 0x045c }, { 0x06ae, 0x045e }, { 0x06af, 0x045f }, { 0x06b0, 0x2116 }, { 0x06b1, 0x0402 }, { 0x06b2, 0x0403 }, { 0x06b3, 0x0401 }, { 0x06b4, 0x0404 }, { 0x06b5, 0x0405 }, { 0x06b6, 0x0406 }, { 0x06b7, 0x0407 }, { 0x06b8, 0x0408 }, { 0x06b9, 0x0409 }, { 0x06ba, 0x040a }, { 0x06bb, 0x040b }, { 0x06bc, 0x040c }, { 0x06be, 0x040e }, { 0x06bf, 0x040f }, { 0x06c0, 0x044e }, { 0x06c1, 0x0430 }, { 0x06c2, 0x0431 }, { 0x06c3, 0x0446 }, { 0x06c4, 0x0434 }, { 0x06c5, 0x0435 }, { 0x06c6, 0x0444 }, { 0x06c7, 0x0433 }, { 0x06c8, 0x0445 }, { 0x06c9, 0x0438 }, { 0x06ca, 0x0439 }, { 0x06cb, 0x043a }, { 0x06cc, 0x043b }, { 0x06cd, 0x043c }, { 0x06ce, 0x043d }, { 0x06cf, 0x043e }, { 0x06d0, 0x043f }, { 0x06d1, 0x044f }, { 0x06d2, 0x0440 }, { 0x06d3, 0x0441 }, { 0x06d4, 0x0442 }, { 0x06d5, 0x0443 }, { 0x06d6, 0x0436 }, { 0x06d7, 0x0432 }, { 0x06d8, 0x044c }, { 0x06d9, 0x044b }, { 0x06da, 0x0437 }, { 0x06db, 0x0448 }, { 0x06dc, 0x044d }, { 0x06dd, 0x0449 }, { 0x06de, 0x0447 }, { 0x06df, 0x044a }, { 0x06e0, 0x042e }, { 0x06e1, 0x0410 }, { 0x06e2, 0x0411 }, { 0x06e3, 0x0426 }, { 0x06e4, 0x0414 }, { 0x06e5, 0x0415 }, { 0x06e6, 0x0424 }, { 0x06e7, 0x0413 }, { 0x06e8, 0x0425 }, { 0x06e9, 0x0418 }, { 0x06ea, 0x0419 }, { 0x06eb, 0x041a }, { 0x06ec, 0x041b }, { 0x06ed, 0x041c }, { 0x06ee, 0x041d }, { 0x06ef, 0x041e }, { 0x06f0, 0x041f }, { 0x06f1, 0x042f }, { 0x06f2, 0x0420 }, { 0x06f3, 0x0421 }, { 0x06f4, 0x0422 }, { 0x06f5, 0x0423 }, { 0x06f6, 0x0416 }, { 0x06f7, 0x0412 }, { 0x06f8, 0x042c }, { 0x06f9, 0x042b }, { 0x06fa, 0x0417 }, { 0x06fb, 0x0428 }, { 0x06fc, 0x042d }, { 0x06fd, 0x0429 }, { 0x06fe, 0x0427 }, { 0x06ff, 0x042a }, { 0x07a1, 0x0386 }, { 0x07a2, 0x0388 }, { 0x07a3, 0x0389 }, { 0x07a4, 0x038a }, { 0x07a5, 0x03aa }, { 0x07a7, 0x038c }, { 0x07a8, 0x038e }, { 0x07a9, 0x03ab }, { 0x07ab, 0x038f }, { 0x07ae, 0x0385 }, { 0x07af, 0x2015 }, { 0x07b1, 0x03ac }, { 0x07b2, 0x03ad }, { 0x07b3, 0x03ae }, { 0x07b4, 0x03af }, { 0x07b5, 0x03ca }, { 0x07b6, 0x0390 }, { 0x07b7, 0x03cc }, { 0x07b8, 0x03cd }, { 0x07b9, 0x03cb }, { 0x07ba, 0x03b0 }, { 0x07bb, 0x03ce }, { 0x07c1, 0x0391 }, { 0x07c2, 0x0392 }, { 0x07c3, 0x0393 }, { 0x07c4, 0x0394 }, { 0x07c5, 0x0395 }, { 0x07c6, 0x0396 }, { 0x07c7, 0x0397 }, { 0x07c8, 0x0398 }, { 0x07c9, 0x0399 }, { 0x07ca, 0x039a }, { 0x07cb, 0x039b }, { 0x07cc, 0x039c }, { 0x07cd, 0x039d }, { 0x07ce, 0x039e }, { 0x07cf, 0x039f }, { 0x07d0, 0x03a0 }, { 0x07d1, 0x03a1 }, { 0x07d2, 0x03a3 }, { 0x07d4, 0x03a4 }, { 0x07d5, 0x03a5 }, { 0x07d6, 0x03a6 }, { 0x07d7, 0x03a7 }, { 0x07d8, 0x03a8 }, { 0x07d9, 0x03a9 }, { 0x07e1, 0x03b1 }, { 0x07e2, 0x03b2 }, { 0x07e3, 0x03b3 }, { 0x07e4, 0x03b4 }, { 0x07e5, 0x03b5 }, { 0x07e6, 0x03b6 }, { 0x07e7, 0x03b7 }, { 0x07e8, 0x03b8 }, { 0x07e9, 0x03b9 }, { 0x07ea, 0x03ba }, { 0x07eb, 0x03bb }, { 0x07ec, 0x03bc }, { 0x07ed, 0x03bd }, { 0x07ee, 0x03be }, { 0x07ef, 0x03bf }, { 0x07f0, 0x03c0 }, { 0x07f1, 0x03c1 }, { 0x07f2, 0x03c3 }, { 0x07f3, 0x03c2 }, { 0x07f4, 0x03c4 }, { 0x07f5, 0x03c5 }, { 0x07f6, 0x03c6 }, { 0x07f7, 0x03c7 }, { 0x07f8, 0x03c8 }, { 0x07f9, 0x03c9 }, { 0x08a1, 0x23b7 }, { 0x08a2, 0x250c }, { 0x08a3, 0x2500 }, { 0x08a4, 0x2320 }, { 0x08a5, 0x2321 }, { 0x08a6, 0x2502 }, { 0x08a7, 0x23a1 }, { 0x08a8, 0x23a3 }, { 0x08a9, 0x23a4 }, { 0x08aa, 0x23a6 }, { 0x08ab, 0x239b }, { 0x08ac, 0x239d }, { 0x08ad, 0x239e }, { 0x08ae, 0x23a0 }, { 0x08af, 0x23a8 }, { 0x08b0, 0x23ac }, { 0x08bc, 0x2264 }, { 0x08bd, 0x2260 }, { 0x08be, 0x2265 }, { 0x08bf, 0x222b }, { 0x08c0, 0x2234 }, { 0x08c1, 0x221d }, { 0x08c2, 0x221e }, { 0x08c5, 0x2207 }, { 0x08c8, 0x223c }, { 0x08c9, 0x2243 }, { 0x08cd, 0x21d4 }, { 0x08ce, 0x21d2 }, { 0x08cf, 0x2261 }, { 0x08d6, 0x221a }, { 0x08da, 0x2282 }, { 0x08db, 0x2283 }, { 0x08dc, 0x2229 }, { 0x08dd, 0x222a }, { 0x08de, 0x2227 }, { 0x08df, 0x2228 }, { 0x08ef, 0x2202 }, { 0x08f6, 0x0192 }, { 0x08fb, 0x2190 }, { 0x08fc, 0x2191 }, { 0x08fd, 0x2192 }, { 0x08fe, 0x2193 }, { 0x09e0, 0x25c6 }, { 0x09e1, 0x2592 }, { 0x09e2, 0x2409 }, { 0x09e3, 0x240c }, { 0x09e4, 0x240d }, { 0x09e5, 0x240a }, { 0x09e8, 0x2424 }, { 0x09e9, 0x240b }, { 0x09ea, 0x2518 }, { 0x09eb, 0x2510 }, { 0x09ec, 0x250c }, { 0x09ed, 0x2514 }, { 0x09ee, 0x253c }, { 0x09ef, 0x23ba }, { 0x09f0, 0x23bb }, { 0x09f1, 0x2500 }, { 0x09f2, 0x23bc }, { 0x09f3, 0x23bd }, { 0x09f4, 0x251c }, { 0x09f5, 0x2524 }, { 0x09f6, 0x2534 }, { 0x09f7, 0x252c }, { 0x09f8, 0x2502 }, { 0x0aa1, 0x2003 }, { 0x0aa2, 0x2002 }, { 0x0aa3, 0x2004 }, { 0x0aa4, 0x2005 }, { 0x0aa5, 0x2007 }, { 0x0aa6, 0x2008 }, { 0x0aa7, 0x2009 }, { 0x0aa8, 0x200a }, { 0x0aa9, 0x2014 }, { 0x0aaa, 0x2013 }, { 0x0aae, 0x2026 }, { 0x0aaf, 0x2025 }, { 0x0ab0, 0x2153 }, { 0x0ab1, 0x2154 }, { 0x0ab2, 0x2155 }, { 0x0ab3, 0x2156 }, { 0x0ab4, 0x2157 }, { 0x0ab5, 0x2158 }, { 0x0ab6, 0x2159 }, { 0x0ab7, 0x215a }, { 0x0ab8, 0x2105 }, { 0x0abb, 0x2012 }, { 0x0abc, 0x2329 }, { 0x0abe, 0x232a }, { 0x0ac3, 0x215b }, { 0x0ac4, 0x215c }, { 0x0ac5, 0x215d }, { 0x0ac6, 0x215e }, { 0x0ac9, 0x2122 }, { 0x0aca, 0x2613 }, { 0x0acc, 0x25c1 }, { 0x0acd, 0x25b7 }, { 0x0ace, 0x25cb }, { 0x0acf, 0x25af }, { 0x0ad0, 0x2018 }, { 0x0ad1, 0x2019 }, { 0x0ad2, 0x201c }, { 0x0ad3, 0x201d }, { 0x0ad4, 0x211e }, { 0x0ad6, 0x2032 }, { 0x0ad7, 0x2033 }, { 0x0ad9, 0x271d }, { 0x0adb, 0x25ac }, { 0x0adc, 0x25c0 }, { 0x0add, 0x25b6 }, { 0x0ade, 0x25cf }, { 0x0adf, 0x25ae }, { 0x0ae0, 0x25e6 }, { 0x0ae1, 0x25ab }, { 0x0ae2, 0x25ad }, { 0x0ae3, 0x25b3 }, { 0x0ae4, 0x25bd }, { 0x0ae5, 0x2606 }, { 0x0ae6, 0x2022 }, { 0x0ae7, 0x25aa }, { 0x0ae8, 0x25b2 }, { 0x0ae9, 0x25bc }, { 0x0aea, 0x261c }, { 0x0aeb, 0x261e }, { 0x0aec, 0x2663 }, { 0x0aed, 0x2666 }, { 0x0aee, 0x2665 }, { 0x0af0, 0x2720 }, { 0x0af1, 0x2020 }, { 0x0af2, 0x2021 }, { 0x0af3, 0x2713 }, { 0x0af4, 0x2717 }, { 0x0af5, 0x266f }, { 0x0af6, 0x266d }, { 0x0af7, 0x2642 }, { 0x0af8, 0x2640 }, { 0x0af9, 0x260e }, { 0x0afa, 0x2315 }, { 0x0afb, 0x2117 }, { 0x0afc, 0x2038 }, { 0x0afd, 0x201a }, { 0x0afe, 0x201e }, { 0x0ba3, 0x003c }, { 0x0ba6, 0x003e }, { 0x0ba8, 0x2228 }, { 0x0ba9, 0x2227 }, { 0x0bc0, 0x00af }, { 0x0bc2, 0x22a5 }, { 0x0bc3, 0x2229 }, { 0x0bc4, 0x230a }, { 0x0bc6, 0x005f }, { 0x0bca, 0x2218 }, { 0x0bcc, 0x2395 }, { 0x0bce, 0x22a4 }, { 0x0bcf, 0x25cb }, { 0x0bd3, 0x2308 }, { 0x0bd6, 0x222a }, { 0x0bd8, 0x2283 }, { 0x0bda, 0x2282 }, { 0x0bdc, 0x22a2 }, { 0x0bfc, 0x22a3 }, { 0x0cdf, 0x2017 }, { 0x0ce0, 0x05d0 }, { 0x0ce1, 0x05d1 }, { 0x0ce2, 0x05d2 }, { 0x0ce3, 0x05d3 }, { 0x0ce4, 0x05d4 }, { 0x0ce5, 0x05d5 }, { 0x0ce6, 0x05d6 }, { 0x0ce7, 0x05d7 }, { 0x0ce8, 0x05d8 }, { 0x0ce9, 0x05d9 }, { 0x0cea, 0x05da }, { 0x0ceb, 0x05db }, { 0x0cec, 0x05dc }, { 0x0ced, 0x05dd }, { 0x0cee, 0x05de }, { 0x0cef, 0x05df }, { 0x0cf0, 0x05e0 }, { 0x0cf1, 0x05e1 }, { 0x0cf2, 0x05e2 }, { 0x0cf3, 0x05e3 }, { 0x0cf4, 0x05e4 }, { 0x0cf5, 0x05e5 }, { 0x0cf6, 0x05e6 }, { 0x0cf7, 0x05e7 }, { 0x0cf8, 0x05e8 }, { 0x0cf9, 0x05e9 }, { 0x0cfa, 0x05ea }, { 0x0da1, 0x0e01 }, { 0x0da2, 0x0e02 }, { 0x0da3, 0x0e03 }, { 0x0da4, 0x0e04 }, { 0x0da5, 0x0e05 }, { 0x0da6, 0x0e06 }, { 0x0da7, 0x0e07 }, { 0x0da8, 0x0e08 }, { 0x0da9, 0x0e09 }, { 0x0daa, 0x0e0a }, { 0x0dab, 0x0e0b }, { 0x0dac, 0x0e0c }, { 0x0dad, 0x0e0d }, { 0x0dae, 0x0e0e }, { 0x0daf, 0x0e0f }, { 0x0db0, 0x0e10 }, { 0x0db1, 0x0e11 }, { 0x0db2, 0x0e12 }, { 0x0db3, 0x0e13 }, { 0x0db4, 0x0e14 }, { 0x0db5, 0x0e15 }, { 0x0db6, 0x0e16 }, { 0x0db7, 0x0e17 }, { 0x0db8, 0x0e18 }, { 0x0db9, 0x0e19 }, { 0x0dba, 0x0e1a }, { 0x0dbb, 0x0e1b }, { 0x0dbc, 0x0e1c }, { 0x0dbd, 0x0e1d }, { 0x0dbe, 0x0e1e }, { 0x0dbf, 0x0e1f }, { 0x0dc0, 0x0e20 }, { 0x0dc1, 0x0e21 }, { 0x0dc2, 0x0e22 }, { 0x0dc3, 0x0e23 }, { 0x0dc4, 0x0e24 }, { 0x0dc5, 0x0e25 }, { 0x0dc6, 0x0e26 }, { 0x0dc7, 0x0e27 }, { 0x0dc8, 0x0e28 }, { 0x0dc9, 0x0e29 }, { 0x0dca, 0x0e2a }, { 0x0dcb, 0x0e2b }, { 0x0dcc, 0x0e2c }, { 0x0dcd, 0x0e2d }, { 0x0dce, 0x0e2e }, { 0x0dcf, 0x0e2f }, { 0x0dd0, 0x0e30 }, { 0x0dd1, 0x0e31 }, { 0x0dd2, 0x0e32 }, { 0x0dd3, 0x0e33 }, { 0x0dd4, 0x0e34 }, { 0x0dd5, 0x0e35 }, { 0x0dd6, 0x0e36 }, { 0x0dd7, 0x0e37 }, { 0x0dd8, 0x0e38 }, { 0x0dd9, 0x0e39 }, { 0x0dda, 0x0e3a }, { 0x0ddf, 0x0e3f }, { 0x0de0, 0x0e40 }, { 0x0de1, 0x0e41 }, { 0x0de2, 0x0e42 }, { 0x0de3, 0x0e43 }, { 0x0de4, 0x0e44 }, { 0x0de5, 0x0e45 }, { 0x0de6, 0x0e46 }, { 0x0de7, 0x0e47 }, { 0x0de8, 0x0e48 }, { 0x0de9, 0x0e49 }, { 0x0dea, 0x0e4a }, { 0x0deb, 0x0e4b }, { 0x0dec, 0x0e4c }, { 0x0ded, 0x0e4d }, { 0x0df0, 0x0e50 }, { 0x0df1, 0x0e51 }, { 0x0df2, 0x0e52 }, { 0x0df3, 0x0e53 }, { 0x0df4, 0x0e54 }, { 0x0df5, 0x0e55 }, { 0x0df6, 0x0e56 }, { 0x0df7, 0x0e57 }, { 0x0df8, 0x0e58 }, { 0x0df9, 0x0e59 }, { 0x0ea1, 0x3131 }, { 0x0ea2, 0x3132 }, { 0x0ea3, 0x3133 }, { 0x0ea4, 0x3134 }, { 0x0ea5, 0x3135 }, { 0x0ea6, 0x3136 }, { 0x0ea7, 0x3137 }, { 0x0ea8, 0x3138 }, { 0x0ea9, 0x3139 }, { 0x0eaa, 0x313a }, { 0x0eab, 0x313b }, { 0x0eac, 0x313c }, { 0x0ead, 0x313d }, { 0x0eae, 0x313e }, { 0x0eaf, 0x313f }, { 0x0eb0, 0x3140 }, { 0x0eb1, 0x3141 }, { 0x0eb2, 0x3142 }, { 0x0eb3, 0x3143 }, { 0x0eb4, 0x3144 }, { 0x0eb5, 0x3145 }, { 0x0eb6, 0x3146 }, { 0x0eb7, 0x3147 }, { 0x0eb8, 0x3148 }, { 0x0eb9, 0x3149 }, { 0x0eba, 0x314a }, { 0x0ebb, 0x314b }, { 0x0ebc, 0x314c }, { 0x0ebd, 0x314d }, { 0x0ebe, 0x314e }, { 0x0ebf, 0x314f }, { 0x0ec0, 0x3150 }, { 0x0ec1, 0x3151 }, { 0x0ec2, 0x3152 }, { 0x0ec3, 0x3153 }, { 0x0ec4, 0x3154 }, { 0x0ec5, 0x3155 }, { 0x0ec6, 0x3156 }, { 0x0ec7, 0x3157 }, { 0x0ec8, 0x3158 }, { 0x0ec9, 0x3159 }, { 0x0eca, 0x315a }, { 0x0ecb, 0x315b }, { 0x0ecc, 0x315c }, { 0x0ecd, 0x315d }, { 0x0ece, 0x315e }, { 0x0ecf, 0x315f }, { 0x0ed0, 0x3160 }, { 0x0ed1, 0x3161 }, { 0x0ed2, 0x3162 }, { 0x0ed3, 0x3163 }, { 0x0ed4, 0x11a8 }, { 0x0ed5, 0x11a9 }, { 0x0ed6, 0x11aa }, { 0x0ed7, 0x11ab }, { 0x0ed8, 0x11ac }, { 0x0ed9, 0x11ad }, { 0x0eda, 0x11ae }, { 0x0edb, 0x11af }, { 0x0edc, 0x11b0 }, { 0x0edd, 0x11b1 }, { 0x0ede, 0x11b2 }, { 0x0edf, 0x11b3 }, { 0x0ee0, 0x11b4 }, { 0x0ee1, 0x11b5 }, { 0x0ee2, 0x11b6 }, { 0x0ee3, 0x11b7 }, { 0x0ee4, 0x11b8 }, { 0x0ee5, 0x11b9 }, { 0x0ee6, 0x11ba }, { 0x0ee7, 0x11bb }, { 0x0ee8, 0x11bc }, { 0x0ee9, 0x11bd }, { 0x0eea, 0x11be }, { 0x0eeb, 0x11bf }, { 0x0eec, 0x11c0 }, { 0x0eed, 0x11c1 }, { 0x0eee, 0x11c2 }, { 0x0eef, 0x316d }, { 0x0ef0, 0x3171 }, { 0x0ef1, 0x3178 }, { 0x0ef2, 0x317f }, { 0x0ef3, 0x3181 }, { 0x0ef4, 0x3184 }, { 0x0ef5, 0x3186 }, { 0x0ef6, 0x318d }, { 0x0ef7, 0x318e }, { 0x0ef8, 0x11eb }, { 0x0ef9, 0x11f0 }, { 0x0efa, 0x11f9 }, { 0x0eff, 0x20a9 }, { 0x13a4, 0x20ac }, { 0x13bc, 0x0152 }, { 0x13bd, 0x0153 }, { 0x13be, 0x0178 }, { 0x20ac, 0x20ac }, { 0xfe50, '`' }, { 0xfe51, 0x00b4 }, { 0xfe52, '^' }, { 0xfe53, '~' }, { 0xfe54, 0x00af }, { 0xfe55, 0x02d8 }, { 0xfe56, 0x02d9 }, { 0xfe57, 0x00a8 }, { 0xfe58, 0x02da }, { 0xfe59, 0x02dd }, { 0xfe5a, 0x02c7 }, { 0xfe5b, 0x00b8 }, { 0xfe5c, 0x02db }, { 0xfe5d, 0x037a }, { 0xfe5e, 0x309b }, { 0xfe5f, 0x309c }, { 0xfe63, '/' }, { 0xfe64, 0x02bc }, { 0xfe65, 0x02bd }, { 0xfe66, 0x02f5 }, { 0xfe67, 0x02f3 }, { 0xfe68, 0x02cd }, { 0xfe69, 0xa788 }, { 0xfe6a, 0x02f7 }, { 0xfe6e, ',' }, { 0xfe6f, 0x00a4 }, { 0xfe80, 'a' }, // XK_dead_a { 0xfe81, 'A' }, // XK_dead_A { 0xfe82, 'e' }, // XK_dead_e { 0xfe83, 'E' }, // XK_dead_E { 0xfe84, 'i' }, // XK_dead_i { 0xfe85, 'I' }, // XK_dead_I { 0xfe86, 'o' }, // XK_dead_o { 0xfe87, 'O' }, // XK_dead_O { 0xfe88, 'u' }, // XK_dead_u { 0xfe89, 'U' }, // XK_dead_U { 0xfe8a, 0x0259 }, { 0xfe8b, 0x018f }, { 0xfe8c, 0x00b5 }, { 0xfe90, '_' }, { 0xfe91, 0x02c8 }, { 0xfe92, 0x02cc }, { 0xff80 /*XKB_KEY_KP_Space*/, ' ' }, { 0xff95 /*XKB_KEY_KP_7*/, 0x0037 }, { 0xff96 /*XKB_KEY_KP_4*/, 0x0034 }, { 0xff97 /*XKB_KEY_KP_8*/, 0x0038 }, { 0xff98 /*XKB_KEY_KP_6*/, 0x0036 }, { 0xff99 /*XKB_KEY_KP_2*/, 0x0032 }, { 0xff9a /*XKB_KEY_KP_9*/, 0x0039 }, { 0xff9b /*XKB_KEY_KP_3*/, 0x0033 }, { 0xff9c /*XKB_KEY_KP_1*/, 0x0031 }, { 0xff9d /*XKB_KEY_KP_5*/, 0x0035 }, { 0xff9e /*XKB_KEY_KP_0*/, 0x0030 }, { 0xffaa /*XKB_KEY_KP_Multiply*/, '*' }, { 0xffab /*XKB_KEY_KP_Add*/, '+' }, { 0xffac /*XKB_KEY_KP_Separator*/, ',' }, { 0xffad /*XKB_KEY_KP_Subtract*/, '-' }, { 0xffae /*XKB_KEY_KP_Decimal*/, '.' }, { 0xffaf /*XKB_KEY_KP_Divide*/, '/' }, { 0xffb0 /*XKB_KEY_KP_0*/, 0x0030 }, { 0xffb1 /*XKB_KEY_KP_1*/, 0x0031 }, { 0xffb2 /*XKB_KEY_KP_2*/, 0x0032 }, { 0xffb3 /*XKB_KEY_KP_3*/, 0x0033 }, { 0xffb4 /*XKB_KEY_KP_4*/, 0x0034 }, { 0xffb5 /*XKB_KEY_KP_5*/, 0x0035 }, { 0xffb6 /*XKB_KEY_KP_6*/, 0x0036 }, { 0xffb7 /*XKB_KEY_KP_7*/, 0x0037 }, { 0xffb8 /*XKB_KEY_KP_8*/, 0x0038 }, { 0xffb9 /*XKB_KEY_KP_9*/, 0x0039 }, { 0xffbd /*XKB_KEY_KP_Equal*/, '=' } }; ////////////////////////////////////////////////////////////////////////// ////// GLFW internal API ////// ////////////////////////////////////////////////////////////////////////// // Convert XKB KeySym to Unicode // uint32_t _glfwKeySym2Unicode(unsigned int keysym) { int min = 0; int max = sizeof(keysymtab) / sizeof(struct codepair) - 1; int mid; // First check for Latin-1 characters (1:1 mapping) if ((keysym >= 0x0020 && keysym <= 0x007e) || (keysym >= 0x00a0 && keysym <= 0x00ff)) { return keysym; } // Also check for directly encoded 24-bit UCS characters if ((keysym & 0xff000000) == 0x01000000) return keysym & 0x00ffffff; // Binary search in table while (max >= min) { mid = (min + max) / 2; if (keysymtab[mid].keysym < keysym) min = mid + 1; else if (keysymtab[mid].keysym > keysym) max = mid - 1; else return keysymtab[mid].ucs; } // No matching Unicode value found return GLFW_INVALID_CODEPOINT; } #endif #endif #endif #endif /* __EMSCRIPTEN__ */ #line 0 #undef timeGetTime //--- #line 1 "3rd_swrap.h" // https://github.com/BareRose/swrap/blob/master/swrap.h /* swrap - Portable, protocol-agnostic TCP and UDP socket wrapper, primarily designed for client-server models in applications such as games To the extent possible under law, the author(s) have dedicated all copyright and related and neighboring rights to this software to the public domain worldwide. This software is distributed without any warranty. You should have received a copy of the CC0 Public Domain Dedication along with this software. If not, see . */ /* swrap supports the following three configurations: #define SWRAP_EXTERN Default, should be used when using swrap in multiple compilation units within the same project. #define SWRAP_IMPLEMENTATION Must be defined in exactly one source file within a project for swrap to be found by the linker. #define SWRAP_STATIC Defines all swrap functions as static, useful if swrap is only used in a single compilation unit. */ //include only once #ifndef SWRAP_H #define SWRAP_H //process configuration #ifdef SWRAP_STATIC #define SWRAP_IMPLEMENTATION #define SWDEF static #else //SWRAP_EXTERN #define SWDEF extern #endif //constants #define SWRAP_TCP 0 #define SWRAP_UDP 1 #define SWRAP_BIND 0 #define SWRAP_CONNECT 1 #define SWRAP_DEFAULT 0x00 #define SWRAP_NOBLOCK 0x01 #define SWRAP_NODELAY 0x02 //structs struct swrap_addr { char data[128]; //enough space to hold any kind of address }; //function declarations SWDEF int swrapInit(); SWDEF int swrapSocket(int, int, char, const char*, const char*); SWDEF void swrapClose(int); SWDEF void swrapTerminate(); SWDEF int swrapListen(int, int); SWDEF int swrapAccept(int, struct swrap_addr*); SWDEF int swrapAddress(int, struct swrap_addr*); SWDEF int swrapAddressInfo(struct swrap_addr*, char*, size_t, char*, size_t); SWDEF int swrapSend(int, const char*, size_t); SWDEF int swrapReceive(int, char*, size_t); SWDEF int swrapSendTo(int, struct swrap_addr*, const char*, size_t); SWDEF int swrapReceiveFrom(int, struct swrap_addr*, char*, size_t); SWDEF int swrapSelect(int, double); SWDEF int swrapMultiSelect(int*, size_t, double); //implementation section #ifdef SWRAP_IMPLEMENTATION //includes #ifdef _WIN32 //windows #include #else //unix #include #include #include #include #include #endif #include //NULL #include //INT_MAX on emscripten //< @r-lyeh: added //general functions SWDEF int swrapInit () { //initializes socket functionality, returns 0 on success #ifdef _WIN32 WSADATA WsaData; return (WSAStartup(MAKEWORD(2,2), &WsaData) != NO_ERROR); #else return 0; #endif } SWDEF int swrapSocket (int prot, int mode, char flags, const char* host, const char* serv) { //protocol-agnostically creates a new socket configured according to the given parameters //sockets have to be created and bound/connected all at once to allow for protocol-agnosticity //int: Protocol of the socket, either SWRAP_TCP or SWRAP_UDP for TCP or UDP respectively // SWRAP_TCP: TCP protocol connection-oriented reliable delivery, see swrapListen/Accept // SWRAP_UDP: UDP protocol connectionless unreliable, SWRAP_CONNECT just assigns correspondent //int: Mode of the socket // SWRAP_BIND: Bind to given address (or all interfaces if NULL) and port, e.g. for a server // SWRAP_CONNECT: Connect to given address (localhost if NULL) and port, e.g. for a client //char: Configuration flags, either SWRAP_DEFAULT or a bitwise combination of flags // SWRAP_NOBLOCK: Sets the socket to be non-blocking, default is blocking // SWRAP_NODELAY: Disables Nagle's for TCP sockets, default is enabled //char*: Host/address as a string, can be IPv4, IPv6, etc... //char*: Service/port as a string, e.g. "1728" or "http" //returns socket handle, or -1 on failure struct addrinfo* result, hint = { (mode == SWRAP_BIND) ? AI_PASSIVE : 0, //ai_flags AF_UNSPEC, //ai_family (prot == SWRAP_TCP) ? SOCK_STREAM : SOCK_DGRAM, //ai_socktype 0, 0, NULL, NULL, NULL}; //get address info if (getaddrinfo(host, serv, &hint, &result)) return -1; //create socket #ifdef _WIN32 SOCKET wsck = socket(result->ai_family, result->ai_socktype, result->ai_protocol); if (wsck == INVALID_SOCKET) return -1; //reject socket handle outside int range if (wsck > INT_MAX) { closesocket(wsck); return -1; } //convert to int int sock = wsck; #else int sock = socket(result->ai_family, result->ai_socktype, result->ai_protocol); if (sock == -1) return -1; #endif //make sure IPV6_ONLY is disabled if (result->ai_family == AF_INET6) { int no = 0; setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, (void*)&no, sizeof(no)); } //set TCP_NODELAY if applicable if (prot == SWRAP_TCP) { int nodelay = (flags&SWRAP_NODELAY); setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (void*)&nodelay, sizeof(nodelay)); } //bind if applicable if ((mode == SWRAP_BIND)&&(bind(sock, result->ai_addr, result->ai_addrlen))) { swrapClose(sock); return -1; } //set non-blocking if needed if (flags&SWRAP_NOBLOCK) { #ifdef _WIN32 DWORD no_block = 1; if (ioctlsocket(sock, FIONBIO, &no_block)) { swrapClose(sock); return -1; } #else if (fcntl(sock, F_SETFL, O_NONBLOCK, 1) == -1) { swrapClose(sock); return -1; } #endif } //connect if applicable (return only relevant if blocking) if ((mode == SWRAP_CONNECT)&&(connect(sock, result->ai_addr, result->ai_addrlen))&&(!(flags&SWRAP_NOBLOCK))) { swrapClose(sock); return -1; } //free address info freeaddrinfo(result); //return socket handle return sock; } SWDEF void swrapClose (int sock) { //closes the given socket #ifdef _WIN32 closesocket(sock); #else close(sock); #endif } SWDEF void swrapTerminate () { //terminates socket functionality #ifdef _WIN32 WSACleanup(); #endif } //connection functions SWDEF int swrapListen (int sock, int blog) { //configures the given socket (must be SWRAP_TCP + SWRAP_BIND) to listen for new connections with given maximum backlog //returns 0 on success, non-zero on failure return listen(sock, blog); } SWDEF int swrapAccept (int sock, struct swrap_addr* addr) { //uses the given socket (must be swrapListen) to accept a new incoming connection, optionally returning its address //returns a socket handle for the new connection, or -1 on failure (e.g. if there are no new connections) #ifdef _WIN32 int addr_size = sizeof(struct swrap_addr); SOCKET wsck = accept(sock, (struct sockaddr*)addr, (addr) ? &addr_size : NULL); if (wsck == INVALID_SOCKET) return -1; //reject socket handle outside int range if (wsck > INT_MAX) { closesocket(wsck); return -1; } //return new socket return wsck; #else socklen_t addr_size = sizeof(struct swrap_addr); return accept(sock, (struct sockaddr*)addr, (addr) ? &addr_size : NULL); #endif } //address functions SWDEF int swrapAddress (int sock, struct swrap_addr* addr) { //writes the address the given socket is bound to into given address pointer, useful when automatically assigning port //returns 0 on success, non-zero on failure #ifdef _WIN32 int addr_size = sizeof(struct swrap_addr); #else socklen_t addr_size = sizeof(struct swrap_addr); #endif return getsockname(sock, (struct sockaddr*)addr, &addr_size); } SWDEF int swrapAddressInfo (struct swrap_addr* addr, char* host, size_t host_size, char* serv, size_t serv_size) { //writes the host/address and service/port of given address into given buffers (pointer + size), either buffer may be NULL //returns 0 on success, non-zero on failure return getnameinfo((struct sockaddr*)addr, sizeof(struct swrap_addr), host, host_size, serv, serv_size, 0); } //send/receive functions SWDEF int swrapSend (int sock, const char* data, size_t data_size) { //uses the given socket (either SWRAP_CONNECT or returned by swrapAccept) to send given data (pointer + size) //at most INT_MAX bytes of data will be sent, data sizes greater than that are clamped to INT_MAX //returns how much data was actually sent (may be less than data size), or -1 on failure return send(sock, data, (data_size > INT_MAX) ? INT_MAX : data_size, 0); } SWDEF int swrapReceive (int sock, char* data, size_t data_size) { //receives data using given socket (either SWRAP_CONNECT or returned by swrapAccept) into given buffer (pointer + size) //at most INT_MAX bytes of data will be received, buffer sizes greater than INT_MAX have no additional benefit //returns the number of bytes received, 0 on orderly shutdown, or -1 on failure (e.g. no data to receive) return recv(sock, data, (data_size > INT_MAX) ? INT_MAX : data_size, 0); } SWDEF int swrapSendTo (int sock, struct swrap_addr* addr, const char* data, size_t data_size) { //uses the given socket to send given data (pointer + size) to the given swrap_addr (e.g. from swrapReceiveFrom) //at most INT_MAX bytes of data will be sent, data sizes greater than that are clamped to INT_MAX //returns how much data was actually sent (may be less than data size), or -1 on failure return sendto(sock, data, (data_size > INT_MAX) ? INT_MAX : data_size, 0, (struct sockaddr*)addr, sizeof(struct swrap_addr)); } SWDEF int swrapReceiveFrom (int sock, struct swrap_addr* addr, char* data, size_t data_size) { //receives data using given socket into given buffer (pointer + size), optionally returning sender's address //at most INT_MAX bytes of data will be received, buffer sizes greater than INT_MAX have no additional benefit //returns the number of bytes received, 0 on orderly shutdown, or -1 on failure (e.g. no data to receive) #ifdef _WIN32 int addr_size = sizeof(struct swrap_addr); #else socklen_t addr_size = sizeof(struct swrap_addr); #endif return recvfrom(sock, data, (data_size > INT_MAX) ? INT_MAX : data_size, 0, (struct sockaddr*)addr, &addr_size); } //select functions SWDEF int swrapSelect (int sock, double timeout) { //waits either until given socket has new data to receive or given time (in seconds) has passed //if given socket is -1 an empty select will be performed, which is just a sub-second sleep //returns 1 if new data is available, 0 if timeout was reached, and -1 on error fd_set set; struct timeval time; //fd set FD_ZERO(&set); if (sock > -1) FD_SET(sock, &set); //timeout time.tv_sec = timeout; time.tv_usec = (timeout - time.tv_sec)*1000000.0; //return return select(sock+1, &set, NULL, NULL, &time); } SWDEF int swrapMultiSelect (int* socks, size_t socks_size, double timeout) { //waits either until a socket in given list has new data to receive or given time (in seconds) has passed //if the given list length is 0 an empty select will be performed, which is just a sub-second sleep //returns 1 or more if new data is available, 0 if timeout was reached, and -1 on error fd_set set; struct timeval time; int sock_max = -1; //fd set FD_ZERO(&set); for (size_t i = 0; i < socks_size; i++) { if (socks[i] > sock_max) sock_max = socks[i]; if (socks[i] > -1) FD_SET(socks[i], &set); } //timeout time.tv_sec = timeout; time.tv_usec = (timeout - time.tv_sec)*1000000.0; //return return select(sock_max+1, &set, NULL, NULL, &time); } #endif //SWRAP_IMPLEMENTATION #endif //SWRAP_H #line 0 //--- #line 1 "3rd_jo_mp1.h" /* public domain Simple, Minimalistic MPEG Layer 1 decoder - http://jonolick.com * * Revision History: * 1.00 (2014-26-1) Initial release. * * Basic usage: * int hz, channels, outputSize; * short *output; * jo_read_mp1(input, inputSize, output, outputSize, hz, channels); * // Do something with the data here * free(output); * * */ #ifndef JO_INCLUDE_MP1_H #define JO_INCLUDE_MP1_H #include extern bool jo_read_mp1(const void *input, int inputSize, short **output, int *outputSize, int *hz, int *channels); #endif // JO_INCLUDE_MP1_H #ifndef JO_MP1_HEADER_FILE_ONLY #if defined(_MSC_VER) && _MSC_VER >= 0x1400 #define _CRT_SECURE_NO_WARNINGS // suppress warnings about fopen() #endif #include #include #include static const double s_jo_multTbl[64] = { 2.000000,1.587401,1.259921,1.000000,0.793701,0.629961,0.500000,0.396850,0.314980,0.250000,0.198425,0.157490,0.125000,0.099213,0.078745,0.062500, 0.049606,0.039373,0.031250,0.024803,0.019686,0.015625,0.012402,0.009843,0.007812,0.006201,0.004922,0.003906,0.003100,0.002461,0.001953,0.001550, 0.001230,0.000977,0.000775,0.000615,0.000488,0.000388,0.000308,0.000244,0.000194,0.000154,0.000122,0.000097,0.000077,0.000061,0.000048,0.000038, 0.000031,0.000024,0.000019,0.000015,0.000012,0.000010,0.000008,0.000006,0.000005,0.000004,0.000003,0.000002,0.000002,0.000002,0.000001,1e-20 }; // l = i - 256; // s = (i & 0x40) ? 1 : -1; // windowTbl[(i/16)|((i%16)<<5)] = s * 20 * exp(-(l/112)*-(l/112)) * sin(l * M_PI*2 / 112) / l; static const double s_jo_windowTbl[512] = { -0.000000,-0.000443,0.003250,-0.007004,0.031082,-0.078629,0.100311,-0.572037,1.144989,0.572037,0.100311,0.078629,0.031082,0.007004,0.003250,0.000443, -0.000015,-0.000473,0.003326,-0.007919,0.030518,-0.084183,0.090927,-0.600220,1.144287,0.543823,0.108856,0.073059,0.031479,0.006119,0.003174,0.000397, -0.000015,-0.000534,0.003387,-0.008865,0.029785,-0.089706,0.080688,-0.628296,1.142212,0.515610,0.116577,0.067520,0.031738,0.005295,0.003082,0.000366, -0.000015,-0.000580,0.003433,-0.009842,0.028885,-0.095169,0.069595,-0.656219,1.138763,0.487473,0.123474,0.061996,0.031845,0.004486,0.002991,0.000320, -0.000015,-0.000626,0.003464,-0.010849,0.027802,-0.100540,0.057617,-0.683914,1.133926,0.459473,0.129578,0.056534,0.031815,0.003723,0.002899,0.000290, -0.000015,-0.000687,0.003479,-0.011887,0.026535,-0.105820,0.044785,-0.711319,1.127747,0.431656,0.134888,0.051132,0.031662,0.003006,0.002792,0.000259, -0.000015,-0.000748,0.003479,-0.012939,0.025085,-0.110947,0.031082,-0.738373,1.120224,0.404083,0.139450,0.045837,0.031387,0.002335,0.002686,0.000244, -0.000031,-0.000809,0.003464,-0.014023,0.023422,-0.115921,0.016510,-0.765030,1.111374,0.376801,0.143265,0.040634,0.031006,0.001694,0.002579,0.000214, -0.000031,-0.000885,0.003418,-0.015121,0.021576,-0.120697,0.001068,-0.791214,1.101212,0.349869,0.146362,0.035553,0.030533,0.001099,0.002457,0.000198, -0.000031,-0.000961,0.003372,-0.016235,0.019531,-0.125259,-0.015228,-0.816864,1.089783,0.323318,0.148773,0.030609,0.029938,0.000549,0.002350,0.000168, -0.000031,-0.001038,0.003281,-0.017349,0.017258,-0.129562,-0.032379,-0.841949,1.077118,0.297211,0.150497,0.025818,0.029282,0.000031,0.002243,0.000153, -0.000046,-0.001114,0.003174,-0.018463,0.014801,-0.133591,-0.050354,-0.866364,1.063217,0.271591,0.151596,0.021179,0.028534,-0.000443,0.002121,0.000137, -0.000046,-0.001205,0.003052,-0.019577,0.012115,-0.137299,-0.069168,-0.890091,1.048157,0.246506,0.152069,0.016708,0.027725,-0.000870,0.002014,0.000122, -0.000061,-0.001297,0.002884,-0.020691,0.009232,-0.140671,-0.088776,-0.913055,1.031937,0.221985,0.151962,0.012421,0.026840,-0.001266,0.001907,0.000107, -0.000061,-0.001389,0.002701,-0.021790,0.006134,-0.143677,-0.109161,-0.935196,1.014618,0.198059,0.151306,0.008316,0.025909,-0.001617,0.001785,0.000107, -0.000076,-0.001480,0.002487,-0.022858,0.002823,-0.146255,-0.130310,-0.956482,0.996246,0.174789,0.150116,0.004395,0.024933,-0.001938,0.001694,0.000092, -0.000076,-0.001587,0.002228,-0.023911,-0.000687,-0.148422,-0.152206,-0.976852,0.976852,0.152206,0.148422,0.000687,0.023911,-0.002228,0.001587,0.000076, -0.000092,-0.001694,0.001938,-0.024933,-0.004395,-0.150116,-0.174789,-0.996246,0.956482,0.130310,0.146255,-0.002823,0.022858,-0.002487,0.001480,0.000076, -0.000107,-0.001785,0.001617,-0.025909,-0.008316,-0.151306,-0.198059,-1.014618,0.935196,0.109161,0.143677,-0.006134,0.021790,-0.002701,0.001389,0.000061, -0.000107,-0.001907,0.001266,-0.026840,-0.012421,-0.151962,-0.221985,-1.031937,0.913055,0.088776,0.140671,-0.009232,0.020691,-0.002884,0.001297,0.000061, -0.000122,-0.002014,0.000870,-0.027725,-0.016708,-0.152069,-0.246506,-1.048157,0.890091,0.069168,0.137299,-0.012115,0.019577,-0.003052,0.001205,0.000046, -0.000137,-0.002121,0.000443,-0.028534,-0.021179,-0.151596,-0.271591,-1.063217,0.866364,0.050354,0.133591,-0.014801,0.018463,-0.003174,0.001114,0.000046, -0.000153,-0.002243,-0.000031,-0.029282,-0.025818,-0.150497,-0.297211,-1.077118,0.841949,0.032379,0.129562,-0.017258,0.017349,-0.003281,0.001038,0.000031, -0.000168,-0.002350,-0.000549,-0.029938,-0.030609,-0.148773,-0.323318,-1.089783,0.816864,0.015228,0.125259,-0.019531,0.016235,-0.003372,0.000961,0.000031, -0.000198,-0.002457,-0.001099,-0.030533,-0.035553,-0.146362,-0.349869,-1.101212,0.791214,-0.001068,0.120697,-0.021576,0.015121,-0.003418,0.000885,0.000031, -0.000214,-0.002579,-0.001694,-0.031006,-0.040634,-0.143265,-0.376801,-1.111374,0.765030,-0.016510,0.115921,-0.023422,0.014023,-0.003464,0.000809,0.000031, -0.000244,-0.002686,-0.002335,-0.031387,-0.045837,-0.139450,-0.404083,-1.120224,0.738373,-0.031082,0.110947,-0.025085,0.012939,-0.003479,0.000748,0.000015, -0.000259,-0.002792,-0.003006,-0.031662,-0.051132,-0.134888,-0.431656,-1.127747,0.711319,-0.044785,0.105820,-0.026535,0.011887,-0.003479,0.000687,0.000015, -0.000290,-0.002899,-0.003723,-0.031815,-0.056534,-0.129578,-0.459473,-1.133926,0.683914,-0.057617,0.100540,-0.027802,0.010849,-0.003464,0.000626,0.000015, -0.000320,-0.002991,-0.004486,-0.031845,-0.061996,-0.123474,-0.487473,-1.138763,0.656219,-0.069595,0.095169,-0.028885,0.009842,-0.003433,0.000580,0.000015, -0.000366,-0.003082,-0.005295,-0.031738,-0.067520,-0.116577,-0.515610,-1.142212,0.628296,-0.080688,0.089706,-0.029785,0.008865,-0.003387,0.000534,0.000015, -0.000397,-0.003174,-0.006119,-0.031479,-0.073059,-0.108856,-0.543823,-1.144287,0.600220,-0.090927,0.084183,-0.030518,0.007919,-0.003326,0.000473,0.000015, }; // filterTbl[i][j] = cos(((M_PI/64*i+M_PI/4)*(2*j+1))); static const double s_jo_filterTbl[64][32] = { {0.707107,-0.707107,-0.707107,0.707107,0.707107,-0.707107,-0.707107,0.707107,0.707107,-0.707107,-0.707107,0.707107,0.707107,-0.707107,-0.707107,0.707107, 0.707107,-0.707107,-0.707107,0.707107,0.707107,-0.707107,-0.707107,0.707107,0.707107,-0.707107,-0.707107,0.707107,0.707107,-0.707107,-0.707107,0.707107}, {0.671559,-0.803208,-0.514103,0.903989,0.336890,-0.970031,-0.146730,0.998795,-0.049068,-0.989177,0.242980,0.941544,-0.427555,-0.857729,0.595699,0.740951, -0.740951,-0.595699,0.857729,0.427555,-0.941544,-0.242980,0.989177,0.049068,-0.998795,0.146730,0.970031,-0.336890,-0.903989,0.514103,0.803208,-0.671559}, {0.634393,-0.881921,-0.290285,0.995185,-0.098017,-0.956940,0.471397,0.773010,-0.773010,-0.471397,0.956940,0.098017,-0.995185,0.290285,0.881921,-0.634393, -0.634393,0.881921,0.290285,-0.995185,0.098017,0.956940,-0.471397,-0.773010,0.773010,0.471397,-0.956940,-0.098017,0.995185,-0.290285,-0.881921,0.634393}, {0.595699,-0.941544,-0.049068,0.970031,-0.514103,-0.671559,0.903989,0.146730,-0.989177,0.427555,0.740951,-0.857729,-0.242980,0.998795,-0.336890,-0.803208, 0.803208,0.336890,-0.998795,0.242980,0.857729,-0.740951,-0.427555,0.989177,-0.146730,-0.903989,0.671559,0.514103,-0.970031,0.049068,0.941544,-0.595699}, {0.555570,-0.980785,0.195090,0.831470,-0.831470,-0.195090,0.980785,-0.555570,-0.555570,0.980785,-0.195090,-0.831470,0.831470,0.195090,-0.980785,0.555570, 0.555570,-0.980785,0.195090,0.831470,-0.831470,-0.195090,0.980785,-0.555570,-0.555570,0.980785,-0.195090,-0.831470,0.831470,0.195090,-0.980785,0.555570}, {0.514103,-0.998795,0.427555,0.595699,-0.989177,0.336890,0.671559,-0.970031,0.242980,0.740951,-0.941544,0.146730,0.803208,-0.903989,0.049068,0.857729, -0.857729,-0.049068,0.903989,-0.803208,-0.146730,0.941544,-0.740951,-0.242980,0.970031,-0.671559,-0.336890,0.989177,-0.595699,-0.427555,0.998795,-0.514103}, {0.471397,-0.995185,0.634393,0.290285,-0.956940,0.773010,0.098017,-0.881921,0.881921,-0.098017,-0.773010,0.956940,-0.290285,-0.634393,0.995185,-0.471397, -0.471397,0.995185,-0.634393,-0.290285,0.956940,-0.773010,-0.098017,0.881921,-0.881921,0.098017,0.773010,-0.956940,0.290285,0.634393,-0.995185,0.471397}, {0.427555,-0.970031,0.803208,-0.049068,-0.740951,0.989177,-0.514103,-0.336890,0.941544,-0.857729,0.146730,0.671559,-0.998795,0.595699,0.242980,-0.903989, 0.903989,-0.242980,-0.595699,0.998795,-0.671559,-0.146730,0.857729,-0.941544,0.336890,0.514103,-0.989177,0.740951,0.049068,-0.803208,0.970031,-0.427555}, {0.382683,-0.923880,0.923880,-0.382683,-0.382683,0.923880,-0.923880,0.382683,0.382683,-0.923880,0.923880,-0.382683,-0.382683,0.923880,-0.923880,0.382683, 0.382683,-0.923880,0.923880,-0.382683,-0.382683,0.923880,-0.923880,0.382683,0.382683,-0.923880,0.923880,-0.382683,-0.382683,0.923880,-0.923880,0.382683}, {0.336890,-0.857729,0.989177,-0.671559,0.049068,0.595699,-0.970031,0.903989,-0.427555,-0.242980,0.803208,-0.998795,0.740951,-0.146730,-0.514103,0.941544, -0.941544,0.514103,0.146730,-0.740951,0.998795,-0.803208,0.242980,0.427555,-0.903989,0.970031,-0.595699,-0.049068,0.671559,-0.989177,0.857729,-0.336890}, {0.290285,-0.773010,0.995185,-0.881921,0.471397,0.098017,-0.634393,0.956940,-0.956940,0.634393,-0.098017,-0.471397,0.881921,-0.995185,0.773010,-0.290285, -0.290285,0.773010,-0.995185,0.881921,-0.471397,-0.098017,0.634393,-0.956940,0.956940,-0.634393,0.098017,0.471397,-0.881921,0.995185,-0.773010,0.290285}, {0.242980,-0.671559,0.941544,-0.989177,0.803208,-0.427555,-0.049068,0.514103,-0.857729,0.998795,-0.903989,0.595699,-0.146730,-0.336890,0.740951,-0.970031, 0.970031,-0.740951,0.336890,0.146730,-0.595699,0.903989,-0.998795,0.857729,-0.514103,0.049068,0.427555,-0.803208,0.989177,-0.941544,0.671559,-0.242980}, {0.195090,-0.555570,0.831470,-0.980785,0.980785,-0.831470,0.555570,-0.195090,-0.195090,0.555570,-0.831470,0.980785,-0.980785,0.831470,-0.555570,0.195090, 0.195090,-0.555570,0.831470,-0.980785,0.980785,-0.831470,0.555570,-0.195090,-0.195090,0.555570,-0.831470,0.980785,-0.980785,0.831470,-0.555570,0.195090}, {0.146730,-0.427555,0.671559,-0.857729,0.970031,-0.998795,0.941544,-0.803208,0.595699,-0.336890,0.049068,0.242980,-0.514103,0.740951,-0.903989,0.989177, -0.989177,0.903989,-0.740951,0.514103,-0.242980,-0.049068,0.336890,-0.595699,0.803208,-0.941544,0.998795,-0.970031,0.857729,-0.671559,0.427555,-0.146730}, {0.098017,-0.290285,0.471397,-0.634393,0.773010,-0.881921,0.956940,-0.995185,0.995185,-0.956940,0.881921,-0.773010,0.634393,-0.471397,0.290285,-0.098017, -0.098017,0.290285,-0.471397,0.634393,-0.773010,0.881921,-0.956940,0.995185,-0.995185,0.956940,-0.881921,0.773010,-0.634393,0.471397,-0.290285,0.098017}, {0.049068,-0.146730,0.242980,-0.336890,0.427555,-0.514103,0.595699,-0.671559,0.740951,-0.803208,0.857729,-0.903989,0.941544,-0.970031,0.989177,-0.998795, 0.998795,-0.989177,0.970031,-0.941544,0.903989,-0.857729,0.803208,-0.740951,0.671559,-0.595699,0.514103,-0.427555,0.336890,-0.242980,0.146730,-0.049068}, {0.000000,-0.000000,0.000000,-0.000000,0.000000,-0.000000,-0.000000,-0.000000,-0.000000,-0.000000,-0.000000,-0.000000,-0.000000,-0.000000,-0.000000,-0.000000, 0.000000,-0.000000,0.000000,-0.000000,0.000000,-0.000000,0.000000,0.000000,0.000000,-0.000000,0.000000,0.000000,0.000000,-0.000000,0.000000,0.000000}, {-0.049068,0.146730,-0.242980,0.336890,-0.427555,0.514103,-0.595699,0.671559,-0.740951,0.803208,-0.857729,0.903989,-0.941544,0.970031,-0.989177,0.998795, -0.998795,0.989177,-0.970031,0.941544,-0.903989,0.857729,-0.803208,0.740951,-0.671559,0.595699,-0.514103,0.427555,-0.336890,0.242980,-0.146730,0.049068}, {-0.098017,0.290285,-0.471397,0.634393,-0.773010,0.881921,-0.956940,0.995185,-0.995185,0.956940,-0.881921,0.773010,-0.634393,0.471397,-0.290285,0.098017, 0.098017,-0.290285,0.471397,-0.634393,0.773010,-0.881921,0.956940,-0.995185,0.995185,-0.956940,0.881921,-0.773010,0.634393,-0.471397,0.290285,-0.098017}, {-0.146730,0.427555,-0.671559,0.857729,-0.970031,0.998795,-0.941544,0.803208,-0.595699,0.336890,-0.049068,-0.242980,0.514103,-0.740951,0.903989,-0.989177, 0.989177,-0.903989,0.740951,-0.514103,0.242980,0.049068,-0.336890,0.595699,-0.803208,0.941544,-0.998795,0.970031,-0.857729,0.671559,-0.427555,0.146730}, {-0.195090,0.555570,-0.831470,0.980785,-0.980785,0.831470,-0.555570,0.195090,0.195090,-0.555570,0.831470,-0.980785,0.980785,-0.831470,0.555570,-0.195090, -0.195090,0.555570,-0.831470,0.980785,-0.980785,0.831470,-0.555570,0.195090,0.195090,-0.555570,0.831470,-0.980785,0.980785,-0.831470,0.555570,-0.195090}, {-0.242980,0.671559,-0.941544,0.989177,-0.803208,0.427555,0.049068,-0.514103,0.857729,-0.998795,0.903989,-0.595699,0.146730,0.336890,-0.740951,0.970031, -0.970031,0.740951,-0.336890,-0.146730,0.595699,-0.903989,0.998795,-0.857729,0.514103,-0.049068,-0.427555,0.803208,-0.989177,0.941544,-0.671559,0.242980}, {-0.290285,0.773010,-0.995185,0.881921,-0.471397,-0.098017,0.634393,-0.956940,0.956940,-0.634393,0.098017,0.471397,-0.881921,0.995185,-0.773010,0.290285, 0.290285,-0.773010,0.995185,-0.881921,0.471397,0.098017,-0.634393,0.956940,-0.956940,0.634393,-0.098017,-0.471397,0.881921,-0.995185,0.773010,-0.290285}, {-0.336890,0.857729,-0.989177,0.671559,-0.049068,-0.595699,0.970031,-0.903989,0.427555,0.242980,-0.803208,0.998795,-0.740951,0.146730,0.514103,-0.941544, 0.941544,-0.514103,-0.146730,0.740951,-0.998795,0.803208,-0.242980,-0.427555,0.903989,-0.970031,0.595699,0.049068,-0.671559,0.989177,-0.857729,0.336890}, {-0.382683,0.923880,-0.923880,0.382683,0.382683,-0.923880,0.923880,-0.382683,-0.382683,0.923880,-0.923880,0.382683,0.382683,-0.923880,0.923880,-0.382683, -0.382683,0.923880,-0.923880,0.382683,0.382683,-0.923880,0.923880,-0.382683,-0.382683,0.923880,-0.923880,0.382683,0.382683,-0.923880,0.923880,-0.382683}, {-0.427555,0.970031,-0.803208,0.049068,0.740951,-0.989177,0.514103,0.336890,-0.941544,0.857729,-0.146730,-0.671559,0.998795,-0.595699,-0.242980,0.903989, -0.903989,0.242980,0.595699,-0.998795,0.671559,0.146730,-0.857729,0.941544,-0.336890,-0.514103,0.989177,-0.740951,-0.049068,0.803208,-0.970031,0.427555}, {-0.471397,0.995185,-0.634393,-0.290285,0.956940,-0.773010,-0.098017,0.881921,-0.881921,0.098017,0.773010,-0.956940,0.290285,0.634393,-0.995185,0.471397, 0.471397,-0.995185,0.634393,0.290285,-0.956940,0.773010,0.098017,-0.881921,0.881921,-0.098017,-0.773010,0.956940,-0.290285,-0.634393,0.995185,-0.471397}, {-0.514103,0.998795,-0.427555,-0.595699,0.989177,-0.336890,-0.671559,0.970031,-0.242980,-0.740951,0.941544,-0.146730,-0.803208,0.903989,-0.049068,-0.857729, 0.857729,0.049068,-0.903989,0.803208,0.146730,-0.941544,0.740951,0.242980,-0.970031,0.671559,0.336890,-0.989177,0.595699,0.427555,-0.998795,0.514103}, {-0.555570,0.980785,-0.195090,-0.831470,0.831470,0.195090,-0.980785,0.555570,0.555570,-0.980785,0.195090,0.831470,-0.831470,-0.195090,0.980785,-0.555570, -0.555570,0.980785,-0.195090,-0.831470,0.831470,0.195090,-0.980785,0.555570,0.555570,-0.980785,0.195090,0.831470,-0.831470,-0.195090,0.980785,-0.555570}, {-0.595699,0.941544,0.049068,-0.970031,0.514103,0.671559,-0.903989,-0.146730,0.989177,-0.427555,-0.740951,0.857729,0.242980,-0.998795,0.336890,0.803208, -0.803208,-0.336890,0.998795,-0.242980,-0.857729,0.740951,0.427555,-0.989177,0.146730,0.903989,-0.671559,-0.514103,0.970031,-0.049068,-0.941544,0.595699}, {-0.634393,0.881921,0.290285,-0.995185,0.098017,0.956940,-0.471397,-0.773010,0.773010,0.471397,-0.956940,-0.098017,0.995185,-0.290285,-0.881921,0.634393, 0.634393,-0.881921,-0.290285,0.995185,-0.098017,-0.956940,0.471397,0.773010,-0.773010,-0.471397,0.956940,0.098017,-0.995185,0.290285,0.881921,-0.634393}, {-0.671559,0.803208,0.514103,-0.903989,-0.336890,0.970031,0.146730,-0.998795,0.049068,0.989177,-0.242980,-0.941544,0.427555,0.857729,-0.595699,-0.740951, 0.740951,0.595699,-0.857729,-0.427555,0.941544,0.242980,-0.989177,-0.049068,0.998795,-0.146730,-0.970031,0.336890,0.903989,-0.514103,-0.803208,0.671559}, {-0.707107,0.707107,0.707107,-0.707107,-0.707107,0.707107,0.707107,-0.707107,-0.707107,0.707107,0.707107,-0.707107,-0.707107,0.707107,0.707107,-0.707107, -0.707107,0.707107,0.707107,-0.707107,-0.707107,0.707107,0.707107,-0.707107,-0.707107,0.707107,0.707107,-0.707107,-0.707107,0.707107,0.707107,-0.707107}, {-0.740951,0.595699,0.857729,-0.427555,-0.941544,0.242980,0.989177,-0.049068,-0.998795,-0.146730,0.970031,0.336890,-0.903989,-0.514103,0.803208,0.671559, -0.671559,-0.803208,0.514103,0.903989,-0.336890,-0.970031,0.146730,0.998795,0.049068,-0.989177,-0.242980,0.941544,0.427555,-0.857729,-0.595699,0.740951}, {-0.773010,0.471397,0.956940,-0.098017,-0.995185,-0.290285,0.881921,0.634393,-0.634393,-0.881921,0.290285,0.995185,0.098017,-0.956940,-0.471397,0.773010, 0.773010,-0.471397,-0.956940,0.098017,0.995185,0.290285,-0.881921,-0.634393,0.634393,0.881921,-0.290285,-0.995185,-0.098017,0.956940,0.471397,-0.773010}, {-0.803208,0.336890,0.998795,0.242980,-0.857729,-0.740951,0.427555,0.989177,0.146730,-0.903989,-0.671559,0.514103,0.970031,0.049068,-0.941544,-0.595699, 0.595699,0.941544,-0.049068,-0.970031,-0.514103,0.671559,0.903989,-0.146730,-0.989177,-0.427555,0.740951,0.857729,-0.242980,-0.998795,-0.336890,0.803208}, {-0.831470,0.195090,0.980785,0.555570,-0.555570,-0.980785,-0.195090,0.831470,0.831470,-0.195090,-0.980785,-0.555570,0.555570,0.980785,0.195090,-0.831470, -0.831470,0.195090,0.980785,0.555570,-0.555570,-0.980785,-0.195090,0.831470,0.831470,-0.195090,-0.980785,-0.555570,0.555570,0.980785,0.195090,-0.831470}, {-0.857729,0.049068,0.903989,0.803208,-0.146730,-0.941544,-0.740951,0.242980,0.970031,0.671559,-0.336890,-0.989177,-0.595699,0.427555,0.998795,0.514103, -0.514103,-0.998795,-0.427555,0.595699,0.989177,0.336890,-0.671559,-0.970031,-0.242980,0.740951,0.941544,0.146730,-0.803208,-0.903989,-0.049068,0.857729}, {-0.881921,-0.098017,0.773010,0.956940,0.290285,-0.634393,-0.995185,-0.471397,0.471397,0.995185,0.634393,-0.290285,-0.956940,-0.773010,0.098017,0.881921, 0.881921,0.098017,-0.773010,-0.956940,-0.290285,0.634393,0.995185,0.471397,-0.471397,-0.995185,-0.634393,0.290285,0.956940,0.773010,-0.098017,-0.881921}, {-0.903989,-0.242980,0.595699,0.998795,0.671559,-0.146730,-0.857729,-0.941544,-0.336890,0.514103,0.989177,0.740951,-0.049068,-0.803208,-0.970031,-0.427555, 0.427555,0.970031,0.803208,0.049068,-0.740951,-0.989177,-0.514103,0.336890,0.941544,0.857729,0.146730,-0.671559,-0.998795,-0.595699,0.242980,0.903989}, {-0.923880,-0.382683,0.382683,0.923880,0.923880,0.382683,-0.382683,-0.923880,-0.923880,-0.382683,0.382683,0.923880,0.923880,0.382683,-0.382683,-0.923880, -0.923880,-0.382683,0.382683,0.923880,0.923880,0.382683,-0.382683,-0.923880,-0.923880,-0.382683,0.382683,0.923880,0.923880,0.382683,-0.382683,-0.923880}, {-0.941544,-0.514103,0.146730,0.740951,0.998795,0.803208,0.242980,-0.427555,-0.903989,-0.970031,-0.595699,0.049068,0.671559,0.989177,0.857729,0.336890, -0.336890,-0.857729,-0.989177,-0.671559,-0.049068,0.595699,0.970031,0.903989,0.427555,-0.242980,-0.803208,-0.998795,-0.740951,-0.146730,0.514103,0.941544}, {-0.956940,-0.634393,-0.098017,0.471397,0.881921,0.995185,0.773010,0.290285,-0.290285,-0.773010,-0.995185,-0.881921,-0.471397,0.098017,0.634393,0.956940, 0.956940,0.634393,0.098017,-0.471397,-0.881921,-0.995185,-0.773010,-0.290285,0.290285,0.773010,0.995185,0.881921,0.471397,-0.098017,-0.634393,-0.956940}, {-0.970031,-0.740951,-0.336890,0.146730,0.595699,0.903989,0.998795,0.857729,0.514103,0.049068,-0.427555,-0.803208,-0.989177,-0.941544,-0.671559,-0.242980, 0.242980,0.671559,0.941544,0.989177,0.803208,0.427555,-0.049068,-0.514103,-0.857729,-0.998795,-0.903989,-0.595699,-0.146730,0.336890,0.740951,0.970031}, {-0.980785,-0.831470,-0.555570,-0.195090,0.195090,0.555570,0.831470,0.980785,0.980785,0.831470,0.555570,0.195090,-0.195090,-0.555570,-0.831470,-0.980785, -0.980785,-0.831470,-0.555570,-0.195090,0.195090,0.555570,0.831470,0.980785,0.980785,0.831470,0.555570,0.195090,-0.195090,-0.555570,-0.831470,-0.980785}, {-0.989177,-0.903989,-0.740951,-0.514103,-0.242980,0.049068,0.336890,0.595699,0.803208,0.941544,0.998795,0.970031,0.857729,0.671559,0.427555,0.146730, -0.146730,-0.427555,-0.671559,-0.857729,-0.970031,-0.998795,-0.941544,-0.803208,-0.595699,-0.336890,-0.049068,0.242980,0.514103,0.740951,0.903989,0.989177}, {-0.995185,-0.956940,-0.881921,-0.773010,-0.634393,-0.471397,-0.290285,-0.098017,0.098017,0.290285,0.471397,0.634393,0.773010,0.881921,0.956940,0.995185, 0.995185,0.956940,0.881921,0.773010,0.634393,0.471397,0.290285,0.098017,-0.098017,-0.290285,-0.471397,-0.634393,-0.773010,-0.881921,-0.956940,-0.995185}, {-0.998795,-0.989177,-0.970031,-0.941544,-0.903989,-0.857729,-0.803208,-0.740951,-0.671559,-0.595699,-0.514103,-0.427555,-0.336890,-0.242980,-0.146730,-0.049068, 0.049068,0.146730,0.242980,0.336890,0.427555,0.514103,0.595699,0.671559,0.740951,0.803208,0.857729,0.903989,0.941544,0.970031,0.989177,0.998795}, {-1.000000,-1.000000,-1.000000,-1.000000,-1.000000,-1.000000,-1.000000,-1.000000,-1.000000,-1.000000,-1.000000,-1.000000,-1.000000,-1.000000,-1.000000,-1.000000, -1.000000,-1.000000,-1.000000,-1.000000,-1.000000,-1.000000,-1.000000,-1.000000,-1.000000,-1.000000,-1.000000,-1.000000,-1.000000,-1.000000,-1.000000,-1.000000}, {-0.998795,-0.989177,-0.970031,-0.941544,-0.903989,-0.857729,-0.803208,-0.740951,-0.671559,-0.595699,-0.514103,-0.427555,-0.336890,-0.242980,-0.146730,-0.049068, 0.049068,0.146730,0.242980,0.336890,0.427555,0.514103,0.595699,0.671559,0.740951,0.803208,0.857729,0.903989,0.941544,0.970031,0.989177,0.998795}, {-0.995185,-0.956940,-0.881921,-0.773010,-0.634393,-0.471397,-0.290285,-0.098017,0.098017,0.290285,0.471397,0.634393,0.773010,0.881921,0.956940,0.995185, 0.995185,0.956940,0.881921,0.773010,0.634393,0.471397,0.290285,0.098017,-0.098017,-0.290285,-0.471397,-0.634393,-0.773010,-0.881921,-0.956940,-0.995185}, {-0.989177,-0.903989,-0.740951,-0.514103,-0.242980,0.049068,0.336890,0.595699,0.803208,0.941544,0.998795,0.970031,0.857729,0.671559,0.427555,0.146730, -0.146730,-0.427555,-0.671559,-0.857729,-0.970031,-0.998795,-0.941544,-0.803208,-0.595699,-0.336890,-0.049068,0.242980,0.514103,0.740951,0.903989,0.989177}, {-0.980785,-0.831470,-0.555570,-0.195090,0.195090,0.555570,0.831470,0.980785,0.980785,0.831470,0.555570,0.195090,-0.195090,-0.555570,-0.831470,-0.980785, -0.980785,-0.831470,-0.555570,-0.195090,0.195090,0.555570,0.831470,0.980785,0.980785,0.831470,0.555570,0.195090,-0.195090,-0.555570,-0.831470,-0.980785}, {-0.970031,-0.740951,-0.336890,0.146730,0.595699,0.903989,0.998795,0.857729,0.514103,0.049068,-0.427555,-0.803208,-0.989177,-0.941544,-0.671559,-0.242980, 0.242980,0.671559,0.941544,0.989177,0.803208,0.427555,-0.049068,-0.514103,-0.857729,-0.998795,-0.903989,-0.595699,-0.146730,0.336890,0.740951,0.970031}, {-0.956940,-0.634393,-0.098017,0.471397,0.881921,0.995185,0.773010,0.290285,-0.290285,-0.773010,-0.995185,-0.881921,-0.471397,0.098017,0.634393,0.956940, 0.956940,0.634393,0.098017,-0.471397,-0.881921,-0.995185,-0.773010,-0.290285,0.290285,0.773010,0.995185,0.881921,0.471397,-0.098017,-0.634393,-0.956940}, {-0.941544,-0.514103,0.146730,0.740951,0.998795,0.803208,0.242980,-0.427555,-0.903989,-0.970031,-0.595699,0.049068,0.671559,0.989177,0.857729,0.336890, -0.336890,-0.857729,-0.989177,-0.671559,-0.049068,0.595699,0.970031,0.903989,0.427555,-0.242980,-0.803208,-0.998795,-0.740951,-0.146730,0.514103,0.941544}, {-0.923880,-0.382683,0.382683,0.923880,0.923880,0.382683,-0.382683,-0.923880,-0.923880,-0.382683,0.382683,0.923880,0.923880,0.382683,-0.382683,-0.923880, -0.923880,-0.382683,0.382683,0.923880,0.923880,0.382683,-0.382683,-0.923880,-0.923880,-0.382683,0.382683,0.923880,0.923880,0.382683,-0.382683,-0.923880}, {-0.903989,-0.242980,0.595699,0.998795,0.671559,-0.146730,-0.857729,-0.941544,-0.336890,0.514103,0.989177,0.740951,-0.049068,-0.803208,-0.970031,-0.427555, 0.427555,0.970031,0.803208,0.049068,-0.740951,-0.989177,-0.514103,0.336890,0.941544,0.857729,0.146730,-0.671559,-0.998795,-0.595699,0.242980,0.903989}, {-0.881921,-0.098017,0.773010,0.956940,0.290285,-0.634393,-0.995185,-0.471397,0.471397,0.995185,0.634393,-0.290285,-0.956940,-0.773010,0.098017,0.881921, 0.881921,0.098017,-0.773010,-0.956940,-0.290285,0.634393,0.995185,0.471397,-0.471397,-0.995185,-0.634393,0.290285,0.956940,0.773010,-0.098017,-0.881921}, {-0.857729,0.049068,0.903989,0.803208,-0.146730,-0.941544,-0.740951,0.242980,0.970031,0.671559,-0.336890,-0.989177,-0.595699,0.427555,0.998795,0.514103, -0.514103,-0.998795,-0.427555,0.595699,0.989177,0.336890,-0.671559,-0.970031,-0.242980,0.740951,0.941544,0.146730,-0.803208,-0.903989,-0.049068,0.857729}, {-0.831470,0.195090,0.980785,0.555570,-0.555570,-0.980785,-0.195090,0.831470,0.831470,-0.195090,-0.980785,-0.555570,0.555570,0.980785,0.195090,-0.831470, -0.831470,0.195090,0.980785,0.555570,-0.555570,-0.980785,-0.195090,0.831470,0.831470,-0.195090,-0.980785,-0.555570,0.555570,0.980785,0.195090,-0.831470}, {-0.803208,0.336890,0.998795,0.242980,-0.857729,-0.740951,0.427555,0.989177,0.146730,-0.903989,-0.671559,0.514103,0.970031,0.049068,-0.941544,-0.595699, 0.595699,0.941544,-0.049068,-0.970031,-0.514103,0.671559,0.903989,-0.146730,-0.989177,-0.427555,0.740951,0.857729,-0.242980,-0.998795,-0.336890,0.803208}, {-0.773010,0.471397,0.956940,-0.098017,-0.995185,-0.290285,0.881921,0.634393,-0.634393,-0.881921,0.290285,0.995185,0.098017,-0.956940,-0.471397,0.773010, 0.773010,-0.471397,-0.956940,0.098017,0.995185,0.290285,-0.881921,-0.634393,0.634393,0.881921,-0.290285,-0.995185,-0.098017,0.956940,0.471397,-0.773010}, {-0.740951,0.595699,0.857729,-0.427555,-0.941544,0.242980,0.989177,-0.049068,-0.998795,-0.146730,0.970031,0.336890,-0.903989,-0.514103,0.803208,0.671559, -0.671559,-0.803208,0.514103,0.903989,-0.336890,-0.970031,0.146730,0.998795,0.049068,-0.989177,-0.242980,0.941544,0.427555,-0.857729,-0.595699,0.740951} }; // up to 32-bits static unsigned jo_readBits(const unsigned char *data, int *at, int num) { unsigned r = 0; // read partial starting bits int sc = (8 - (*at & 7)) & 7; sc = sc > num ? num : sc; if(sc) { r = (data[*at/8] >> (8 - (*at&7) - sc)) & ((1<=8) { r <<= 8; r |= data[*at/8]; *at += 8; num -= 8; } // read partial ending bits if(num) { r <<= num; r |= (data[*at/8] >> (8 - num)) & ((1<= inputSize * 8 - 32) { break; } unsigned header = jo_readBits(data, &at, 32); //printf("header: %x.%x/%x: %08x\n", (at-32)/8, (at-32)&7, inputSize, header); // sync = 0xFFF // ID = 1 // layer = 3 (layer 1) if ((header & 0xFFFE0000) != 0xFFFE0000) { return false; } static const int bitrateTbl[16] = { 0,32,40,48,56,64,80,96,112,128,160,192,224,256,320,-1 }; int kbps = bitrateTbl[(header >> 12) & 15]; if (kbps < 0) { return false; } static const int hzTbl[4] = { 44100, 48000, 32000, 0 }; hz = hzTbl[(header >> 10) & 3]; if (!hz) { return false; } // mode 0 = stereo // mode 1 = joint stereo // mode 2 = dual channel (no idea what this does TODO) // mode 3 = mono int mode = (header >> 6) & 3; int modeExt = (header >> 4) & 3; channels = mode == 3 ? 1 : 2; const int bound = mode == 1 ? (modeExt + 1) * 4 : 32; bool errorProtection = ((header >> 16) & 1) ^ 1; //< @r-lyeh extra parens if (errorProtection) { at += 16; // skip the CRC. } // Read bit allocations int bitAlloc[32][2] = { 0 }; for (int i = 0; i < bound; ++i) { for (int ch = 0; ch < channels; ++ch) { bitAlloc[i][ch] = jo_readBits(data, &at, 4); } } for (int i = bound; i < 32; ++i) { bitAlloc[i][1] = bitAlloc[i][0] = jo_readBits(data, &at, 4); } // Read scale indexes int scaleIdx[32][2]; for (int i = 0; i < 32; ++i) { for (int ch = 0; ch < channels; ++ch) { scaleIdx[i][ch] = bitAlloc[i][ch] ? jo_readBits(data, &at, 6) : 63; } } // Read & compute output samples short pcm[12][2][32]; for (int s = 0; s < 12; ++s) { // Read normalized, quantized band samples int samples[32][2] = { 0 }; for (int i = 0; i < bound; ++i) { for (int ch = 0; ch < channels; ++ch) { if (bitAlloc[i][ch]) { samples[i][ch] = jo_readBits(data, &at, bitAlloc[i][ch] + 1); } } } for (int i = bound; i < 32; ++i) { if (bitAlloc[i][0]) { samples[i][1] = samples[i][0] = jo_readBits(data, &at, bitAlloc[i][0] + 1); } } // Compute bands: Dequantize & Denormalize double bandTbl[2][32] = { 0 }; for (int i = 0; i < 32; ++i) { for (int ch = 0; ch < channels; ++ch) { int b = bitAlloc[i][ch]; if (b++) { int samp = samples[i][ch]; double f = ((samp >> (b - 1)) & 1) ? 0 : -1; f += (samp & ((1 << (b - 1)) - 1)) / (double)(1 << (b - 1)); f = (f + 1.0 / (1 << (b - 1))) * (1 << b) / ((1 << b) - 1.0); f *= s_jo_multTbl[scaleIdx[i][ch]]; bandTbl[ch][i] = f; } } } // Convert subbands to PCM for (int ch = 0; ch < channels; ++ch) { bufOffset[ch] = (bufOffset[ch] + 0x3C0) & 0x3ff; double *bufOffsetPtr = buf[ch] + bufOffset[ch]; const double *f = s_jo_filterTbl[0]; for (int i = 0; i < 64; ++i) { double sum = 0; for (int j = 0; j < 32; ++j) { sum += *f++ * bandTbl[ch][j]; } bufOffsetPtr[i] = sum; } const double *w = s_jo_windowTbl; for (int i = 0; i < 32; ++i) { double sum = 0; for (int j = 0; j < 16; ++j) { int k = i | (j + (j + 1 & -2)) << 5; sum += *w++ * buf[ch][(k + bufOffset[ch]) & 0x3ff]; } int ss = (int)(sum * 0x8000); ss = ss > SHRT_MAX ? SHRT_MAX : ss < SHRT_MIN ? SHRT_MIN : ss; pcm[s][ch][i] = ss; } } } if (at > inputSize * 8) { printf("file corruption?\n"); return false; } if (outputMax == 0) { // estimate total number of samples (may be totally wrong, but its better than nothing) at = (at + 7)&-8; outputMax = inputSize / (at / 8) * 384 * channels * sizeof(*output); output = (short*)REALLOC(output, outputMax); } if (outputSize * sizeof(*output) + 384 * channels * sizeof(*output) > outputMax) { outputMax += 384 * channels * sizeof(*output); output = (short*)REALLOC(output, outputMax); } for (int i = 0; i < 12; ++i) { for (int j = 0; j < 32; ++j) { for (int k = 0; k < channels; ++k) { output[outputSize++] = pcm[i][k][j]; } } } } *outputSize_ = outputSize; *hz_ = hz; *channels_ = channels; *output_ = output; return outputSize && hz && channels && output; } #endif // JO_MP1_HEADER_FILE_ONLY #line 0 #define get_bits stb_vorbis_get_bits #define error stb_vorbis_error #line 1 "3rd_stb_vorbis.h" // Ogg Vorbis audio decoder - v1.19 - public domain // http://nothings.org/stb_vorbis/ // // Original version written by Sean Barrett in 2007. // // Originally sponsored by RAD Game Tools. Seeking implementation // sponsored by Phillip Bennefall, Marc Andersen, Aaron Baker, // Elias Software, Aras Pranckevicius, and Sean Barrett. // // LICENSE // // See end of file for license information. // // Limitations: // // - floor 0 not supported (used in old ogg vorbis files pre-2004) // - lossless sample-truncation at beginning ignored // - cannot concatenate multiple vorbis streams // - sample positions are 32-bit, limiting seekable 192Khz // files to around 6 hours (Ogg supports 64-bit) // // Feature contributors: // Dougall Johnson (sample-exact seeking) // // Bugfix/warning contributors: // Terje Mathisen Niklas Frykholm Andy Hill // Casey Muratori John Bolton Gargaj // Laurent Gomila Marc LeBlanc Ronny Chevalier // Bernhard Wodo Evan Balster github:alxprd // Tom Beaumont Ingo Leitgeb Nicolas Guillemot // Phillip Bennefall Rohit Thiago Goulart // github:manxorist saga musix github:infatum // Timur Gagiev Maxwell Koo Peter Waller // github:audinowho Dougall Johnson // // Partial history: // 1.19 - 2020-02-05 - warnings // 1.18 - 2020-02-02 - fix seek bugs; parse header comments; misc warnings etc. // 1.17 - 2019-07-08 - fix CVE-2019-13217..CVE-2019-13223 (by ForAllSecure) // 1.16 - 2019-03-04 - fix warnings // 1.15 - 2019-02-07 - explicit failure if Ogg Skeleton data is found // 1.14 - 2018-02-11 - delete bogus dealloca usage // 1.13 - 2018-01-29 - fix truncation of last frame (hopefully) // 1.12 - 2017-11-21 - limit residue begin/end to blocksize/2 to avoid large temp allocs in bad/corrupt files // 1.11 - 2017-07-23 - fix MinGW compilation // 1.10 - 2017-03-03 - more robust seeking; fix negative ilog(); clear error in open_memory // 1.09 - 2016-04-04 - back out 'truncation of last frame' fix from previous version // 1.08 - 2016-04-02 - warnings; setup memory leaks; truncation of last frame // 1.07 - 2015-01-16 - fixes for crashes on invalid files; warning fixes; const // 1.06 - 2015-08-31 - full, correct support for seeking API (Dougall Johnson) // some crash fixes when out of memory or with corrupt files // fix some inappropriately signed shifts // 1.05 - 2015-04-19 - don't define __forceinline if it's redundant // 1.04 - 2014-08-27 - fix missing const-correct case in API // 1.03 - 2014-08-07 - warning fixes // 1.02 - 2014-07-09 - declare qsort comparison as explicitly _cdecl in Windows // 1.01 - 2014-06-18 - fix stb_vorbis_get_samples_float (interleaved was correct) // 1.0 - 2014-05-26 - fix memory leaks; fix warnings; fix bugs in >2-channel; // (API change) report sample rate for decode-full-file funcs // // See end of file for full version history. ////////////////////////////////////////////////////////////////////////////// // // HEADER BEGINS HERE // #ifndef STB_VORBIS_INCLUDE_STB_VORBIS_H #define STB_VORBIS_INCLUDE_STB_VORBIS_H #if defined(STB_VORBIS_NO_CRT) && !defined(STB_VORBIS_NO_STDIO) #define STB_VORBIS_NO_STDIO 1 #endif #ifndef STB_VORBIS_NO_STDIO #include #endif #ifdef __cplusplus extern "C" { #endif /////////// THREAD SAFETY // Individual stb_vorbis* handles are not thread-safe; you cannot decode from // them from multiple threads at the same time. However, you can have multiple // stb_vorbis* handles and decode from them independently in multiple thrads. /////////// MEMORY ALLOCATION // normally stb_vorbis uses malloc() to allocate memory at startup, // and alloca() to allocate temporary memory during a frame on the // stack. (Memory consumption will depend on the amount of setup // data in the file and how you set the compile flags for speed // vs. size. In my test files the maximal-size usage is ~150KB.) // // You can modify the wrapper functions in the source (setup_malloc, // setup_temp_malloc, temp_malloc) to change this behavior, or you // can use a simpler allocation model: you pass in a buffer from // which stb_vorbis will allocate _all_ its memory (including the // temp memory). "open" may fail with a VORBIS_outofmem if you // do not pass in enough data; there is no way to determine how // much you do need except to succeed (at which point you can // query get_info to find the exact amount required. yes I know // this is lame). // // If you pass in a non-NULL buffer of the type below, allocation // will occur from it as described above. Otherwise just pass NULL // to use malloc()/alloca() typedef struct { char *alloc_buffer; int alloc_buffer_length_in_bytes; } stb_vorbis_alloc; /////////// FUNCTIONS USEABLE WITH ALL INPUT MODES typedef struct stb_vorbis stb_vorbis; typedef struct { unsigned int sample_rate; int channels; unsigned int setup_memory_required; unsigned int setup_temp_memory_required; unsigned int temp_memory_required; int max_frame_size; } stb_vorbis_info; typedef struct { char *vendor; int comment_list_length; char **comment_list; } stb_vorbis_comment; // get general information about the file extern stb_vorbis_info stb_vorbis_get_info(stb_vorbis *f); // get ogg comments extern stb_vorbis_comment stb_vorbis_get_comment(stb_vorbis *f); // get the last error detected (clears it, too) extern int stb_vorbis_get_error(stb_vorbis *f); // close an ogg vorbis file and free all memory in use extern void stb_vorbis_close(stb_vorbis *f); // this function returns the offset (in samples) from the beginning of the // file that will be returned by the next decode, if it is known, or -1 // otherwise. after a flush_pushdata() call, this may take a while before // it becomes valid again. // NOT WORKING YET after a seek with PULLDATA API extern int stb_vorbis_get_sample_offset(stb_vorbis *f); // returns the current seek point within the file, or offset from the beginning // of the memory buffer. In pushdata mode it returns 0. extern unsigned int stb_vorbis_get_file_offset(stb_vorbis *f); /////////// PUSHDATA API #ifndef STB_VORBIS_NO_PUSHDATA_API // this API allows you to get blocks of data from any source and hand // them to stb_vorbis. you have to buffer them; stb_vorbis will tell // you how much it used, and you have to give it the rest next time; // and stb_vorbis may not have enough data to work with and you will // need to give it the same data again PLUS more. Note that the Vorbis // specification does not bound the size of an individual frame. extern stb_vorbis *stb_vorbis_open_pushdata( const unsigned char * datablock, int datablock_length_in_bytes, int *datablock_memory_consumed_in_bytes, int *error, const stb_vorbis_alloc *alloc_buffer); // create a vorbis decoder by passing in the initial data block containing // the ogg&vorbis headers (you don't need to do parse them, just provide // the first N bytes of the file--you're told if it's not enough, see below) // on success, returns an stb_vorbis *, does not set error, returns the amount of // data parsed/consumed on this call in *datablock_memory_consumed_in_bytes; // on failure, returns NULL on error and sets *error, does not change *datablock_memory_consumed // if returns NULL and *error is VORBIS_need_more_data, then the input block was // incomplete and you need to pass in a larger block from the start of the file extern int stb_vorbis_decode_frame_pushdata( stb_vorbis *f, const unsigned char *datablock, int datablock_length_in_bytes, int *channels, // place to write number of float * buffers float ***output, // place to write float ** array of float * buffers int *samples // place to write number of output samples ); // decode a frame of audio sample data if possible from the passed-in data block // // return value: number of bytes we used from datablock // // possible cases: // 0 bytes used, 0 samples output (need more data) // N bytes used, 0 samples output (resynching the stream, keep going) // N bytes used, M samples output (one frame of data) // note that after opening a file, you will ALWAYS get one N-bytes,0-sample // frame, because Vorbis always "discards" the first frame. // // Note that on resynch, stb_vorbis will rarely consume all of the buffer, // instead only datablock_length_in_bytes-3 or less. This is because it wants // to avoid missing parts of a page header if they cross a datablock boundary, // without writing state-machiney code to record a partial detection. // // The number of channels returned are stored in *channels (which can be // NULL--it is always the same as the number of channels reported by // get_info). *output will contain an array of float* buffers, one per // channel. In other words, (*output)[0][0] contains the first sample from // the first channel, and (*output)[1][0] contains the first sample from // the second channel. extern void stb_vorbis_flush_pushdata(stb_vorbis *f); // inform stb_vorbis that your next datablock will not be contiguous with // previous ones (e.g. you've seeked in the data); future attempts to decode // frames will cause stb_vorbis to resynchronize (as noted above), and // once it sees a valid Ogg page (typically 4-8KB, as large as 64KB), it // will begin decoding the _next_ frame. // // if you want to seek using pushdata, you need to seek in your file, then // call stb_vorbis_flush_pushdata(), then start calling decoding, then once // decoding is returning you data, call stb_vorbis_get_sample_offset, and // if you don't like the result, seek your file again and repeat. #endif ////////// PULLING INPUT API #ifndef STB_VORBIS_NO_PULLDATA_API // This API assumes stb_vorbis is allowed to pull data from a source-- // either a block of memory containing the _entire_ vorbis stream, or a // FILE * that you or it create, or possibly some other reading mechanism // if you go modify the source to replace the FILE * case with some kind // of callback to your code. (But if you don't support seeking, you may // just want to go ahead and use pushdata.) #if !defined(STB_VORBIS_NO_STDIO) && !defined(STB_VORBIS_NO_INTEGER_CONVERSION) extern int stb_vorbis_decode_filename(const char *filename, int *channels, int *sample_rate, short **output); #endif #if !defined(STB_VORBIS_NO_INTEGER_CONVERSION) extern int stb_vorbis_decode_memory(const unsigned char *mem, int len, int *channels, int *sample_rate, short **output); #endif // decode an entire file and output the data interleaved into a malloc()ed // buffer stored in *output. The return value is the number of samples // decoded, or -1 if the file could not be opened or was not an ogg vorbis file. // When you're done with it, just free() the pointer returned in *output. extern stb_vorbis * stb_vorbis_open_memory(const unsigned char *data, int len, int *error, const stb_vorbis_alloc *alloc_buffer); // create an ogg vorbis decoder from an ogg vorbis stream in memory (note // this must be the entire stream!). on failure, returns NULL and sets *error #ifndef STB_VORBIS_NO_STDIO extern stb_vorbis * stb_vorbis_open_filename(const char *filename, int *error, const stb_vorbis_alloc *alloc_buffer); // create an ogg vorbis decoder from a filename via fopen(). on failure, // returns NULL and sets *error (possibly to VORBIS_file_open_failure). extern stb_vorbis * stb_vorbis_open_file(FILE *f, int close_handle_on_close, int *error, const stb_vorbis_alloc *alloc_buffer); // create an ogg vorbis decoder from an open FILE *, looking for a stream at // the _current_ seek point (ftell). on failure, returns NULL and sets *error. // note that stb_vorbis must "own" this stream; if you seek it in between // calls to stb_vorbis, it will become confused. Moreover, if you attempt to // perform stb_vorbis_seek_*() operations on this file, it will assume it // owns the _entire_ rest of the file after the start point. Use the next // function, stb_vorbis_open_file_section(), to limit it. extern stb_vorbis * stb_vorbis_open_file_section(FILE *f, int close_handle_on_close, int *error, const stb_vorbis_alloc *alloc_buffer, unsigned int len); // create an ogg vorbis decoder from an open FILE *, looking for a stream at // the _current_ seek point (ftell); the stream will be of length 'len' bytes. // on failure, returns NULL and sets *error. note that stb_vorbis must "own" // this stream; if you seek it in between calls to stb_vorbis, it will become // confused. #endif extern int stb_vorbis_seek_frame(stb_vorbis *f, unsigned int sample_number); extern int stb_vorbis_seek(stb_vorbis *f, unsigned int sample_number); // these functions seek in the Vorbis file to (approximately) 'sample_number'. // after calling seek_frame(), the next call to get_frame_*() will include // the specified sample. after calling stb_vorbis_seek(), the next call to // stb_vorbis_get_samples_* will start with the specified sample. If you // do not need to seek to EXACTLY the target sample when using get_samples_*, // you can also use seek_frame(). extern int stb_vorbis_seek_start(stb_vorbis *f); // this function is equivalent to stb_vorbis_seek(f,0) extern unsigned int stb_vorbis_stream_length_in_samples(stb_vorbis *f); extern float stb_vorbis_stream_length_in_seconds(stb_vorbis *f); // these functions return the total length of the vorbis stream extern int stb_vorbis_get_frame_float(stb_vorbis *f, int *channels, float ***output); // decode the next frame and return the number of samples. the number of // channels returned are stored in *channels (which can be NULL--it is always // the same as the number of channels reported by get_info). *output will // contain an array of float* buffers, one per channel. These outputs will // be overwritten on the next call to stb_vorbis_get_frame_*. // // You generally should not intermix calls to stb_vorbis_get_frame_*() // and stb_vorbis_get_samples_*(), since the latter calls the former. #ifndef STB_VORBIS_NO_INTEGER_CONVERSION extern int stb_vorbis_get_frame_short_interleaved(stb_vorbis *f, int num_c, short *buffer, int num_shorts); extern int stb_vorbis_get_frame_short (stb_vorbis *f, int num_c, short **buffer, int num_samples); #endif // decode the next frame and return the number of *samples* per channel. // Note that for interleaved data, you pass in the number of shorts (the // size of your array), but the return value is the number of samples per // channel, not the total number of samples. // // The data is coerced to the number of channels you request according to the // channel coercion rules (see below). You must pass in the size of your // buffer(s) so that stb_vorbis will not overwrite the end of the buffer. // The maximum buffer size needed can be gotten from get_info(); however, // the Vorbis I specification implies an absolute maximum of 4096 samples // per channel. // Channel coercion rules: // Let M be the number of channels requested, and N the number of channels present, // and Cn be the nth channel; let stereo L be the sum of all L and center channels, // and stereo R be the sum of all R and center channels (channel assignment from the // vorbis spec). // M N output // 1 k sum(Ck) for all k // 2 * stereo L, stereo R // k l k > l, the first l channels, then 0s // k l k <= l, the first k channels // Note that this is not _good_ surround etc. mixing at all! It's just so // you get something useful. extern int stb_vorbis_get_samples_float_interleaved(stb_vorbis *f, int channels, float *buffer, int num_floats); extern int stb_vorbis_get_samples_float(stb_vorbis *f, int channels, float **buffer, int num_samples); // gets num_samples samples, not necessarily on a frame boundary--this requires // buffering so you have to supply the buffers. DOES NOT APPLY THE COERCION RULES. // Returns the number of samples stored per channel; it may be less than requested // at the end of the file. If there are no more samples in the file, returns 0. #ifndef STB_VORBIS_NO_INTEGER_CONVERSION extern int stb_vorbis_get_samples_short_interleaved(stb_vorbis *f, int channels, short *buffer, int num_shorts); extern int stb_vorbis_get_samples_short(stb_vorbis *f, int channels, short **buffer, int num_samples); #endif // gets num_samples samples, not necessarily on a frame boundary--this requires // buffering so you have to supply the buffers. Applies the coercion rules above // to produce 'channels' channels. Returns the number of samples stored per channel; // it may be less than requested at the end of the file. If there are no more // samples in the file, returns 0. #endif //////// ERROR CODES enum STBVorbisError { VORBIS__no_error, VORBIS_need_more_data=1, // not a real error VORBIS_invalid_api_mixing, // can't mix API modes VORBIS_outofmem, // not enough memory VORBIS_feature_not_supported, // uses floor 0 VORBIS_too_many_channels, // STB_VORBIS_MAX_CHANNELS is too small VORBIS_file_open_failure, // fopen() failed VORBIS_seek_without_length, // can't seek in unknown-length file VORBIS_unexpected_eof=10, // file is truncated? VORBIS_seek_invalid, // seek past EOF // decoding errors (corrupt/invalid stream) -- you probably // don't care about the exact details of these // vorbis errors: VORBIS_invalid_setup=20, VORBIS_invalid_stream, // ogg errors: VORBIS_missing_capture_pattern=30, VORBIS_invalid_stream_structure_version, VORBIS_continued_packet_flag_invalid, VORBIS_incorrect_stream_serial_number, VORBIS_invalid_first_page, VORBIS_bad_packet_type, VORBIS_cant_find_last_page, VORBIS_seek_failed, VORBIS_ogg_skeleton_not_supported }; #ifdef __cplusplus } #endif #endif // STB_VORBIS_INCLUDE_STB_VORBIS_H // // HEADER ENDS HERE // ////////////////////////////////////////////////////////////////////////////// #ifndef STB_VORBIS_HEADER_ONLY // global configuration settings (e.g. set these in the project/makefile), // or just set them in this file at the top (although ideally the first few // should be visible when the header file is compiled too, although it's not // crucial) // STB_VORBIS_NO_PUSHDATA_API // does not compile the code for the various stb_vorbis_*_pushdata() // functions // #define STB_VORBIS_NO_PUSHDATA_API // STB_VORBIS_NO_PULLDATA_API // does not compile the code for the non-pushdata APIs // #define STB_VORBIS_NO_PULLDATA_API // STB_VORBIS_NO_STDIO // does not compile the code for the APIs that use FILE *s internally // or externally (implied by STB_VORBIS_NO_PULLDATA_API) // #define STB_VORBIS_NO_STDIO // STB_VORBIS_NO_INTEGER_CONVERSION // does not compile the code for converting audio sample data from // float to integer (implied by STB_VORBIS_NO_PULLDATA_API) // #define STB_VORBIS_NO_INTEGER_CONVERSION // STB_VORBIS_NO_FAST_SCALED_FLOAT // does not use a fast float-to-int trick to accelerate float-to-int on // most platforms which requires endianness be defined correctly. //#define STB_VORBIS_NO_FAST_SCALED_FLOAT // STB_VORBIS_MAX_CHANNELS [number] // globally define this to the maximum number of channels you need. // The spec does not put a restriction on channels except that // the count is stored in a byte, so 255 is the hard limit. // Reducing this saves about 16 bytes per value, so using 16 saves // (255-16)*16 or around 4KB. Plus anything other memory usage // I forgot to account for. Can probably go as low as 8 (7.1 audio), // 6 (5.1 audio), or 2 (stereo only). #ifndef STB_VORBIS_MAX_CHANNELS #define STB_VORBIS_MAX_CHANNELS 16 // enough for anyone? #endif // STB_VORBIS_PUSHDATA_CRC_COUNT [number] // after a flush_pushdata(), stb_vorbis begins scanning for the // next valid page, without backtracking. when it finds something // that looks like a page, it streams through it and verifies its // CRC32. Should that validation fail, it keeps scanning. But it's // possible that _while_ streaming through to check the CRC32 of // one candidate page, it sees another candidate page. This #define // determines how many "overlapping" candidate pages it can search // at once. Note that "real" pages are typically ~4KB to ~8KB, whereas // garbage pages could be as big as 64KB, but probably average ~16KB. // So don't hose ourselves by scanning an apparent 64KB page and // missing a ton of real ones in the interim; so minimum of 2 #ifndef STB_VORBIS_PUSHDATA_CRC_COUNT #define STB_VORBIS_PUSHDATA_CRC_COUNT 4 #endif // STB_VORBIS_FAST_HUFFMAN_LENGTH [number] // sets the log size of the huffman-acceleration table. Maximum // supported value is 24. with larger numbers, more decodings are O(1), // but the table size is larger so worse cache missing, so you'll have // to probe (and try multiple ogg vorbis files) to find the sweet spot. #ifndef STB_VORBIS_FAST_HUFFMAN_LENGTH #define STB_VORBIS_FAST_HUFFMAN_LENGTH 10 #endif // STB_VORBIS_FAST_BINARY_LENGTH [number] // sets the log size of the binary-search acceleration table. this // is used in similar fashion to the fast-huffman size to set initial // parameters for the binary search // STB_VORBIS_FAST_HUFFMAN_INT // The fast huffman tables are much more efficient if they can be // stored as 16-bit results instead of 32-bit results. This restricts // the codebooks to having only 65535 possible outcomes, though. // (At least, accelerated by the huffman table.) #ifndef STB_VORBIS_FAST_HUFFMAN_INT #define STB_VORBIS_FAST_HUFFMAN_SHORT #endif // STB_VORBIS_NO_HUFFMAN_BINARY_SEARCH // If the 'fast huffman' search doesn't succeed, then stb_vorbis falls // back on binary searching for the correct one. This requires storing // extra tables with the huffman codes in sorted order. Defining this // symbol trades off space for speed by forcing a linear search in the // non-fast case, except for "sparse" codebooks. // #define STB_VORBIS_NO_HUFFMAN_BINARY_SEARCH // STB_VORBIS_DIVIDES_IN_RESIDUE // stb_vorbis precomputes the result of the scalar residue decoding // that would otherwise require a divide per chunk. you can trade off // space for time by defining this symbol. // #define STB_VORBIS_DIVIDES_IN_RESIDUE // STB_VORBIS_DIVIDES_IN_CODEBOOK // vorbis VQ codebooks can be encoded two ways: with every case explicitly // stored, or with all elements being chosen from a small range of values, // and all values possible in all elements. By default, stb_vorbis expands // this latter kind out to look like the former kind for ease of decoding, // because otherwise an integer divide-per-vector-element is required to // unpack the index. If you define STB_VORBIS_DIVIDES_IN_CODEBOOK, you can // trade off storage for speed. //#define STB_VORBIS_DIVIDES_IN_CODEBOOK #ifdef STB_VORBIS_CODEBOOK_SHORTS #error "STB_VORBIS_CODEBOOK_SHORTS is no longer supported as it produced incorrect results for some input formats" #endif // STB_VORBIS_DIVIDE_TABLE // this replaces small integer divides in the floor decode loop with // table lookups. made less than 1% difference, so disabled by default. // STB_VORBIS_NO_INLINE_DECODE // disables the inlining of the scalar codebook fast-huffman decode. // might save a little codespace; useful for debugging // #define STB_VORBIS_NO_INLINE_DECODE // STB_VORBIS_NO_DEFER_FLOOR // Normally we only decode the floor without synthesizing the actual // full curve. We can instead synthesize the curve immediately. This // requires more memory and is very likely slower, so I don't think // you'd ever want to do it except for debugging. // #define STB_VORBIS_NO_DEFER_FLOOR ////////////////////////////////////////////////////////////////////////////// #ifdef STB_VORBIS_NO_PULLDATA_API #define STB_VORBIS_NO_INTEGER_CONVERSION #define STB_VORBIS_NO_STDIO #endif #if defined(STB_VORBIS_NO_CRT) && !defined(STB_VORBIS_NO_STDIO) #define STB_VORBIS_NO_STDIO 1 #endif #ifndef STB_VORBIS_NO_INTEGER_CONVERSION #ifndef STB_VORBIS_NO_FAST_SCALED_FLOAT // only need endianness for fast-float-to-int, which we don't // use for pushdata #ifndef STB_VORBIS_BIG_ENDIAN #define STB_VORBIS_ENDIAN 0 #else #define STB_VORBIS_ENDIAN 1 #endif #endif #endif #ifndef STB_VORBIS_NO_STDIO #include #endif #ifndef STB_VORBIS_NO_CRT #include #include #include #include // find definition of alloca if it's not in stdlib.h: #if defined(_MSC_VER) || defined(__MINGW32__) #include #endif #if defined(__linux__) || defined(__linux) || defined(__EMSCRIPTEN__) #include #endif #else // STB_VORBIS_NO_CRT #define NULL 0 #define malloc(s) 0 #define free(s) ((void) 0) #define realloc(s) 0 #endif // STB_VORBIS_NO_CRT #include #ifdef __MINGW32__ // eff you mingw: // "fixed": // http://sourceforge.net/p/mingw-w64/mailman/message/32882927/ // "no that broke the build, reverted, who cares about C": // http://sourceforge.net/p/mingw-w64/mailman/message/32890381/ #ifdef __forceinline #undef __forceinline #endif #define __forceinline #ifndef alloca #define alloca __builtin_alloca #endif #elif !defined(_MSC_VER) #if __GNUC__ #define __forceinline inline #else #define __forceinline #endif #endif #if STB_VORBIS_MAX_CHANNELS > 256 #error "Value of STB_VORBIS_MAX_CHANNELS outside of allowed range" #endif #if STB_VORBIS_FAST_HUFFMAN_LENGTH > 24 #error "Value of STB_VORBIS_FAST_HUFFMAN_LENGTH outside of allowed range" #endif #if 0 #include #define CHECK(f) _CrtIsValidHeapPointer(f->channel_buffers[1]) #else #define CHECK(f) ((void) 0) #endif #define MAX_BLOCKSIZE_LOG 13 // from specification #define MAX_BLOCKSIZE (1 << MAX_BLOCKSIZE_LOG) typedef unsigned char uint8; typedef signed char int8; typedef unsigned short uint16; typedef signed short int16; typedef unsigned int uint32; typedef signed int int32; #ifndef TRUE #define TRUE 1 #define FALSE 0 #endif typedef float codetype; // @NOTE // // Some arrays below are tagged "//varies", which means it's actually // a variable-sized piece of data, but rather than malloc I assume it's // small enough it's better to just allocate it all together with the // main thing // // Most of the variables are specified with the smallest size I could pack // them into. It might give better performance to make them all full-sized // integers. It should be safe to freely rearrange the structures or change // the sizes larger--nothing relies on silently truncating etc., nor the // order of variables. #define FAST_HUFFMAN_TABLE_SIZE (1 << STB_VORBIS_FAST_HUFFMAN_LENGTH) #define FAST_HUFFMAN_TABLE_MASK (FAST_HUFFMAN_TABLE_SIZE - 1) typedef struct { int dimensions, entries; uint8 *codeword_lengths; float minimum_value; float delta_value; uint8 value_bits; uint8 lookup_type; uint8 sequence_p; uint8 sparse; uint32 lookup_values; codetype *multiplicands; uint32 *codewords; #ifdef STB_VORBIS_FAST_HUFFMAN_SHORT int16 fast_huffman[FAST_HUFFMAN_TABLE_SIZE]; #else int32 fast_huffman[FAST_HUFFMAN_TABLE_SIZE]; #endif uint32 *sorted_codewords; int *sorted_values; int sorted_entries; } Codebook; typedef struct { uint8 order; uint16 rate; uint16 bark_map_size; uint8 amplitude_bits; uint8 amplitude_offset; uint8 number_of_books; uint8 book_list[16]; // varies } Floor0; typedef struct { uint8 partitions; uint8 partition_class_list[32]; // varies uint8 class_dimensions[16]; // varies uint8 class_subclasses[16]; // varies uint8 class_masterbooks[16]; // varies int16 subclass_books[16][8]; // varies uint16 Xlist[31*8+2]; // varies uint8 sorted_order[31*8+2]; uint8 neighbors[31*8+2][2]; uint8 floor1_multiplier; uint8 rangebits; int values; } Floor1; typedef union { Floor0 floor0; Floor1 floor1; } Floor; typedef struct { uint32 begin, end; uint32 part_size; uint8 classifications; uint8 classbook; uint8 **classdata; int16 (*residue_books)[8]; } Residue; typedef struct { uint8 magnitude; uint8 angle; uint8 mux; } MappingChannel; typedef struct { uint16 coupling_steps; MappingChannel *chan; uint8 submaps; uint8 submap_floor[15]; // varies uint8 submap_residue[15]; // varies } Mapping; typedef struct { uint8 blockflag; uint8 mapping; uint16 windowtype; uint16 transformtype; } Mode; typedef struct { uint32 goal_crc; // expected crc if match int bytes_left; // bytes left in packet uint32 crc_so_far; // running crc int bytes_done; // bytes processed in _current_ chunk uint32 sample_loc; // granule pos encoded in page } CRCscan; typedef struct { uint32 page_start, page_end; uint32 last_decoded_sample; } ProbedPage; struct stb_vorbis { // user-accessible info unsigned int sample_rate; int channels; unsigned int setup_memory_required; unsigned int temp_memory_required; unsigned int setup_temp_memory_required; char *vendor; int comment_list_length; char **comment_list; // input config #ifndef STB_VORBIS_NO_STDIO FILE *f; uint32 f_start; int close_on_free; #endif uint8 *stream; uint8 *stream_start; uint8 *stream_end; uint32 stream_len; uint8 push_mode; // the page to seek to when seeking to start, may be zero uint32 first_audio_page_offset; // p_first is the page on which the first audio packet ends // (but not necessarily the page on which it starts) ProbedPage p_first, p_last; // memory management stb_vorbis_alloc alloc; int setup_offset; int temp_offset; // run-time results int eof; enum STBVorbisError error; // user-useful data // header info int blocksize[2]; int blocksize_0, blocksize_1; int codebook_count; Codebook *codebooks; int floor_count; uint16 floor_types[64]; // varies Floor *floor_config; int residue_count; uint16 residue_types[64]; // varies Residue *residue_config; int mapping_count; Mapping *mapping; int mode_count; Mode mode_config[64]; // varies uint32 total_samples; // decode buffer float *channel_buffers[STB_VORBIS_MAX_CHANNELS]; float *outputs [STB_VORBIS_MAX_CHANNELS]; float *previous_window[STB_VORBIS_MAX_CHANNELS]; int previous_length; #ifndef STB_VORBIS_NO_DEFER_FLOOR int16 *finalY[STB_VORBIS_MAX_CHANNELS]; #else float *floor_buffers[STB_VORBIS_MAX_CHANNELS]; #endif uint32 current_loc; // sample location of next frame to decode int current_loc_valid; // per-blocksize precomputed data // twiddle factors float *A[2],*B[2],*C[2]; float *window[2]; uint16 *bit_reverse[2]; // current page/packet/segment streaming info uint32 serial; // stream serial number for verification int last_page; int segment_count; uint8 segments[255]; uint8 page_flag; uint8 bytes_in_seg; uint8 first_decode; int next_seg; int last_seg; // flag that we're on the last segment int last_seg_which; // what was the segment number of the last seg? uint32 acc; int valid_bits; int packet_bytes; int end_seg_with_known_loc; uint32 known_loc_for_packet; int discard_samples_deferred; uint32 samples_output; // push mode scanning int page_crc_tests; // only in push_mode: number of tests active; -1 if not searching #ifndef STB_VORBIS_NO_PUSHDATA_API CRCscan scan[STB_VORBIS_PUSHDATA_CRC_COUNT]; #endif // sample-access int channel_buffer_start; int channel_buffer_end; }; #if defined(STB_VORBIS_NO_PUSHDATA_API) #define IS_PUSH_MODE(f) FALSE #elif defined(STB_VORBIS_NO_PULLDATA_API) #define IS_PUSH_MODE(f) TRUE #else #define IS_PUSH_MODE(f) ((f)->push_mode) #endif typedef struct stb_vorbis vorb; static int error(vorb *f, enum STBVorbisError e) { f->error = e; if (!f->eof && e != VORBIS_need_more_data) { f->error=e; // breakpoint for debugging } return 0; } // these functions are used for allocating temporary memory // while decoding. if you can afford the stack space, use // alloca(); otherwise, provide a temp buffer and it will // allocate out of those. #define array_size_required(count,size) (count*(sizeof(void *)+(size))) #define temp_alloc(f,size) (f->alloc.alloc_buffer ? setup_temp_malloc(f,size) : alloca(size)) #define temp_free(f,p) (void)0 #define temp_alloc_save(f) ((f)->temp_offset) #define temp_alloc_restore(f,p) ((f)->temp_offset = (p)) #define temp_block_array(f,count,size) make_block_array(temp_alloc(f,array_size_required(count,size)), count, size) // given a sufficiently large block of memory, make an array of pointers to subblocks of it static void *make_block_array(void *mem, int count, int size) { int i; void ** p = (void **) mem; char *q = (char *) (p + count); for (i=0; i < count; ++i) { p[i] = q; q += size; } return p; } static void *setup_malloc(vorb *f, int sz) { sz = (sz+7) & ~7; // round up to nearest 8 for alignment of future allocs. f->setup_memory_required += sz; if (f->alloc.alloc_buffer) { void *p = (char *) f->alloc.alloc_buffer + f->setup_offset; if (f->setup_offset + sz > f->temp_offset) return NULL; f->setup_offset += sz; return p; } return sz ? malloc(sz) : NULL; } static void setup_free(vorb *f, void *p) { if (f->alloc.alloc_buffer) return; // do nothing; setup mem is a stack free(p); } static void *setup_temp_malloc(vorb *f, int sz) { sz = (sz+7) & ~7; // round up to nearest 8 for alignment of future allocs. if (f->alloc.alloc_buffer) { if (f->temp_offset - sz < f->setup_offset) return NULL; f->temp_offset -= sz; return (char *) f->alloc.alloc_buffer + f->temp_offset; } return malloc(sz); } static void setup_temp_free(vorb *f, void *p, int sz) { if (f->alloc.alloc_buffer) { f->temp_offset += (sz+3)&~3; return; } free(p); } #define CRC32_POLY 0x04c11db7 // from spec static uint32 crc_table[256]; static void crc32_init(void) { int i,j; uint32 s; for(i=0; i < 256; i++) { for (s=(uint32) i << 24, j=0; j < 8; ++j) s = (s << 1) ^ (s >= (1U<<31) ? CRC32_POLY : 0); crc_table[i] = s; } } static __forceinline uint32 crc32_update(uint32 crc, uint8 byte) { return (crc << 8) ^ crc_table[byte ^ (crc >> 24)]; } // used in setup, and for huffman that doesn't go fast path static unsigned int bit_reverse(unsigned int n) { n = ((n & 0xAAAAAAAA) >> 1) | ((n & 0x55555555) << 1); n = ((n & 0xCCCCCCCC) >> 2) | ((n & 0x33333333) << 2); n = ((n & 0xF0F0F0F0) >> 4) | ((n & 0x0F0F0F0F) << 4); n = ((n & 0xFF00FF00) >> 8) | ((n & 0x00FF00FF) << 8); return (n >> 16) | (n << 16); } static float square(float x) { return x*x; } // this is a weird definition of log2() for which log2(1) = 1, log2(2) = 2, log2(4) = 3 // as required by the specification. fast(?) implementation from stb.h // @OPTIMIZE: called multiple times per-packet with "constants"; move to setup static int ilog(int32 n) { static signed char log2_4[16] = { 0,1,2,2,3,3,3,3,4,4,4,4,4,4,4,4 }; if (n < 0) return 0; // signed n returns 0 // 2 compares if n < 16, 3 compares otherwise (4 if signed or n > 1<<29) if (n < (1 << 14)) if (n < (1 << 4)) return 0 + log2_4[n ]; else if (n < (1 << 9)) return 5 + log2_4[n >> 5]; else return 10 + log2_4[n >> 10]; else if (n < (1 << 24)) if (n < (1 << 19)) return 15 + log2_4[n >> 15]; else return 20 + log2_4[n >> 20]; else if (n < (1 << 29)) return 25 + log2_4[n >> 25]; else return 30 + log2_4[n >> 30]; } #ifndef M_PI #define M_PI 3.14159265358979323846264f // from CRC #endif // code length assigned to a value with no huffman encoding #define NO_CODE 255 /////////////////////// LEAF SETUP FUNCTIONS ////////////////////////// // // these functions are only called at setup, and only a few times // per file static float float32_unpack(uint32 x) { // from the specification uint32 mantissa = x & 0x1fffff; uint32 sign = x & 0x80000000; uint32 exp = (x & 0x7fe00000) >> 21; double res = sign ? -(double)mantissa : (double)mantissa; return (float) ldexp((float)res, exp-788); } // zlib & jpeg huffman tables assume that the output symbols // can either be arbitrarily arranged, or have monotonically // increasing frequencies--they rely on the lengths being sorted; // this makes for a very simple generation algorithm. // vorbis allows a huffman table with non-sorted lengths. This // requires a more sophisticated construction, since symbols in // order do not map to huffman codes "in order". static void add_entry(Codebook *c, uint32 huff_code, int symbol, int count, int len, uint32 *values) { if (!c->sparse) { c->codewords [symbol] = huff_code; } else { c->codewords [count] = huff_code; c->codeword_lengths[count] = len; values [count] = symbol; } } static int compute_codewords(Codebook *c, uint8 *len, int n, uint32 *values) { int i,k,m=0; uint32 available[32]; memset(available, 0, sizeof(available)); // find the first entry for (k=0; k < n; ++k) if (len[k] < NO_CODE) break; if (k == n) { assert(c->sorted_entries == 0); return TRUE; } // add to the list add_entry(c, 0, k, m++, len[k], values); // add all available leaves for (i=1; i <= len[k]; ++i) available[i] = 1U << (32-i); // note that the above code treats the first case specially, // but it's really the same as the following code, so they // could probably be combined (except the initial code is 0, // and I use 0 in available[] to mean 'empty') for (i=k+1; i < n; ++i) { uint32 res; int z = len[i], y; if (z == NO_CODE) continue; // find lowest available leaf (should always be earliest, // which is what the specification calls for) // note that this property, and the fact we can never have // more than one free leaf at a given level, isn't totally // trivial to prove, but it seems true and the assert never // fires, so! while (z > 0 && !available[z]) --z; if (z == 0) { return FALSE; } res = available[z]; assert(z >= 0 && z < 32); available[z] = 0; add_entry(c, bit_reverse(res), i, m++, len[i], values); // propagate availability up the tree if (z != len[i]) { assert(len[i] >= 0 && len[i] < 32); for (y=len[i]; y > z; --y) { assert(available[y] == 0); available[y] = res + (1 << (32-y)); } } } return TRUE; } // accelerated huffman table allows fast O(1) match of all symbols // of length <= STB_VORBIS_FAST_HUFFMAN_LENGTH static void compute_accelerated_huffman(Codebook *c) { int i, len; for (i=0; i < FAST_HUFFMAN_TABLE_SIZE; ++i) c->fast_huffman[i] = -1; len = c->sparse ? c->sorted_entries : c->entries; #ifdef STB_VORBIS_FAST_HUFFMAN_SHORT if (len > 32767) len = 32767; // largest possible value we can encode! #endif for (i=0; i < len; ++i) { if (c->codeword_lengths[i] <= STB_VORBIS_FAST_HUFFMAN_LENGTH) { uint32 z = c->sparse ? bit_reverse(c->sorted_codewords[i]) : c->codewords[i]; // set table entries for all bit combinations in the higher bits while (z < FAST_HUFFMAN_TABLE_SIZE) { c->fast_huffman[z] = i; z += 1 << c->codeword_lengths[i]; } } } } #ifdef _MSC_VER #define STBV_CDECL __cdecl #else #define STBV_CDECL #endif static int STBV_CDECL uint32_compare(const void *p, const void *q) { uint32 x = * (uint32 *) p; uint32 y = * (uint32 *) q; return x < y ? -1 : x > y; } static int include_in_sort(Codebook *c, uint8 len) { if (c->sparse) { assert(len != NO_CODE); return TRUE; } if (len == NO_CODE) return FALSE; if (len > STB_VORBIS_FAST_HUFFMAN_LENGTH) return TRUE; return FALSE; } // if the fast table above doesn't work, we want to binary // search them... need to reverse the bits static void compute_sorted_huffman(Codebook *c, uint8 *lengths, uint32 *values) { int i, len; // build a list of all the entries // OPTIMIZATION: don't include the short ones, since they'll be caught by FAST_HUFFMAN. // this is kind of a frivolous optimization--I don't see any performance improvement, // but it's like 4 extra lines of code, so. if (!c->sparse) { int k = 0; for (i=0; i < c->entries; ++i) if (include_in_sort(c, lengths[i])) c->sorted_codewords[k++] = bit_reverse(c->codewords[i]); assert(k == c->sorted_entries); } else { for (i=0; i < c->sorted_entries; ++i) c->sorted_codewords[i] = bit_reverse(c->codewords[i]); } qsort(c->sorted_codewords, c->sorted_entries, sizeof(c->sorted_codewords[0]), uint32_compare); c->sorted_codewords[c->sorted_entries] = 0xffffffff; len = c->sparse ? c->sorted_entries : c->entries; // now we need to indicate how they correspond; we could either // #1: sort a different data structure that says who they correspond to // #2: for each sorted entry, search the original list to find who corresponds // #3: for each original entry, find the sorted entry // #1 requires extra storage, #2 is slow, #3 can use binary search! for (i=0; i < len; ++i) { int huff_len = c->sparse ? lengths[values[i]] : lengths[i]; if (include_in_sort(c,huff_len)) { uint32 code = bit_reverse(c->codewords[i]); int x=0, n=c->sorted_entries; while (n > 1) { // invariant: sc[x] <= code < sc[x+n] int m = x + (n >> 1); if (c->sorted_codewords[m] <= code) { x = m; n -= (n>>1); } else { n >>= 1; } } assert(c->sorted_codewords[x] == code); if (c->sparse) { c->sorted_values[x] = values[i]; c->codeword_lengths[x] = huff_len; } else { c->sorted_values[x] = i; } } } } // only run while parsing the header (3 times) static int vorbis_validate(uint8 *data) { static uint8 vorbis[6] = { 'v', 'o', 'r', 'b', 'i', 's' }; return memcmp(data, vorbis, 6) == 0; } // called from setup only, once per code book // (formula implied by specification) static int lookup1_values(int entries, int dim) { int r = (int) floor(exp((float) log((float) entries) / dim)); if ((int) floor(pow((float) r+1, dim)) <= entries) // (int) cast for MinGW warning; ++r; // floor() to avoid _ftol() when non-CRT if (pow((float) r+1, dim) <= entries) return -1; if ((int) floor(pow((float) r, dim)) > entries) return -1; return r; } // called twice per file static void compute_twiddle_factors(int n, float *A, float *B, float *C) { int n4 = n >> 2, n8 = n >> 3; int k,k2; for (k=k2=0; k < n4; ++k,k2+=2) { A[k2 ] = (float) cos(4*k*M_PI/n); A[k2+1] = (float) -sin(4*k*M_PI/n); B[k2 ] = (float) cos((k2+1)*M_PI/n/2) * 0.5f; B[k2+1] = (float) sin((k2+1)*M_PI/n/2) * 0.5f; } for (k=k2=0; k < n8; ++k,k2+=2) { C[k2 ] = (float) cos(2*(k2+1)*M_PI/n); C[k2+1] = (float) -sin(2*(k2+1)*M_PI/n); } } static void compute_window(int n, float *window) { int n2 = n >> 1, i; for (i=0; i < n2; ++i) window[i] = (float) sin(0.5 * M_PI * square((float) sin((i - 0 + 0.5) / n2 * 0.5 * M_PI))); } static void compute_bitreverse(int n, uint16 *rev) { int ld = ilog(n) - 1; // ilog is off-by-one from normal definitions int i, n8 = n >> 3; for (i=0; i < n8; ++i) rev[i] = (bit_reverse(i) >> (32-ld+3)) << 2; } static int init_blocksize(vorb *f, int b, int n) { int n2 = n >> 1, n4 = n >> 2, n8 = n >> 3; f->A[b] = (float *) setup_malloc(f, sizeof(float) * n2); f->B[b] = (float *) setup_malloc(f, sizeof(float) * n2); f->C[b] = (float *) setup_malloc(f, sizeof(float) * n4); if (!f->A[b] || !f->B[b] || !f->C[b]) return error(f, VORBIS_outofmem); compute_twiddle_factors(n, f->A[b], f->B[b], f->C[b]); f->window[b] = (float *) setup_malloc(f, sizeof(float) * n2); if (!f->window[b]) return error(f, VORBIS_outofmem); compute_window(n, f->window[b]); f->bit_reverse[b] = (uint16 *) setup_malloc(f, sizeof(uint16) * n8); if (!f->bit_reverse[b]) return error(f, VORBIS_outofmem); compute_bitreverse(n, f->bit_reverse[b]); return TRUE; } static void neighbors(uint16 *x, int n, int *plow, int *phigh) { int low = -1; int high = 65536; int i; for (i=0; i < n; ++i) { if (x[i] > low && x[i] < x[n]) { *plow = i; low = x[i]; } if (x[i] < high && x[i] > x[n]) { *phigh = i; high = x[i]; } } } // this has been repurposed so y is now the original index instead of y typedef struct { uint16 x,id; } stbv__floor_ordering; static int STBV_CDECL point_compare(const void *p, const void *q) { stbv__floor_ordering *a = (stbv__floor_ordering *) p; stbv__floor_ordering *b = (stbv__floor_ordering *) q; return a->x < b->x ? -1 : a->x > b->x; } // /////////////////////// END LEAF SETUP FUNCTIONS ////////////////////////// #if defined(STB_VORBIS_NO_STDIO) #define USE_MEMORY(z) TRUE #else #define USE_MEMORY(z) ((z)->stream) #endif static uint8 get8(vorb *z) { if (USE_MEMORY(z)) { if (z->stream >= z->stream_end) { z->eof = TRUE; return 0; } return *z->stream++; } #ifndef STB_VORBIS_NO_STDIO { int c = fgetc(z->f); if (c == EOF) { z->eof = TRUE; return 0; } return c; } #endif } static uint32 get32(vorb *f) { uint32 x; x = get8(f); x += get8(f) << 8; x += get8(f) << 16; x += (uint32) get8(f) << 24; return x; } static int getn(vorb *z, uint8 *data, int n) { if (USE_MEMORY(z)) { if (z->stream+n > z->stream_end) { z->eof = 1; return 0; } memcpy(data, z->stream, n); z->stream += n; return 1; } #ifndef STB_VORBIS_NO_STDIO if (fread(data, n, 1, z->f) == 1) return 1; else { z->eof = 1; return 0; } #endif } static void skip(vorb *z, int n) { if (USE_MEMORY(z)) { z->stream += n; if (z->stream >= z->stream_end) z->eof = 1; return; } #ifndef STB_VORBIS_NO_STDIO { long x = ftell(z->f); fseek(z->f, x+n, SEEK_SET); } #endif } static int set_file_offset(stb_vorbis *f, unsigned int loc) { #ifndef STB_VORBIS_NO_PUSHDATA_API if (f->push_mode) return 0; #endif f->eof = 0; if (USE_MEMORY(f)) { if (f->stream_start + loc >= f->stream_end || f->stream_start + loc < f->stream_start) { f->stream = f->stream_end; f->eof = 1; return 0; } else { f->stream = f->stream_start + loc; return 1; } } #ifndef STB_VORBIS_NO_STDIO if (loc + f->f_start < loc || loc >= 0x80000000) { loc = 0x7fffffff; f->eof = 1; } else { loc += f->f_start; } if (!fseek(f->f, loc, SEEK_SET)) return 1; f->eof = 1; fseek(f->f, f->f_start, SEEK_END); return 0; #endif } static uint8 ogg_page_header[4] = { 0x4f, 0x67, 0x67, 0x53 }; static int capture_pattern(vorb *f) { if (0x4f != get8(f)) return FALSE; if (0x67 != get8(f)) return FALSE; if (0x67 != get8(f)) return FALSE; if (0x53 != get8(f)) return FALSE; return TRUE; } #define PAGEFLAG_continued_packet 1 #define PAGEFLAG_first_page 2 #define PAGEFLAG_last_page 4 static int start_page_no_capturepattern(vorb *f) { uint32 loc0,loc1,n; if (f->first_decode && !IS_PUSH_MODE(f)) { f->p_first.page_start = stb_vorbis_get_file_offset(f) - 4; } // stream structure version if (0 != get8(f)) return error(f, VORBIS_invalid_stream_structure_version); // header flag f->page_flag = get8(f); // absolute granule position loc0 = get32(f); loc1 = get32(f); // @TODO: validate loc0,loc1 as valid positions? // stream serial number -- vorbis doesn't interleave, so discard get32(f); //if (f->serial != get32(f)) return error(f, VORBIS_incorrect_stream_serial_number); // page sequence number n = get32(f); f->last_page = n; // CRC32 get32(f); // page_segments f->segment_count = get8(f); if (!getn(f, f->segments, f->segment_count)) return error(f, VORBIS_unexpected_eof); // assume we _don't_ know any the sample position of any segments f->end_seg_with_known_loc = -2; if (loc0 != ~0U || loc1 != ~0U) { int i; // determine which packet is the last one that will complete for (i=f->segment_count-1; i >= 0; --i) if (f->segments[i] < 255) break; // 'i' is now the index of the _last_ segment of a packet that ends if (i >= 0) { f->end_seg_with_known_loc = i; f->known_loc_for_packet = loc0; } } if (f->first_decode) { int i,len; len = 0; for (i=0; i < f->segment_count; ++i) len += f->segments[i]; len += 27 + f->segment_count; f->p_first.page_end = f->p_first.page_start + len; f->p_first.last_decoded_sample = loc0; } f->next_seg = 0; return TRUE; } static int start_page(vorb *f) { if (!capture_pattern(f)) return error(f, VORBIS_missing_capture_pattern); return start_page_no_capturepattern(f); } static int start_packet(vorb *f) { while (f->next_seg == -1) { if (!start_page(f)) return FALSE; if (f->page_flag & PAGEFLAG_continued_packet) return error(f, VORBIS_continued_packet_flag_invalid); } f->last_seg = FALSE; f->valid_bits = 0; f->packet_bytes = 0; f->bytes_in_seg = 0; // f->next_seg is now valid return TRUE; } static int maybe_start_packet(vorb *f) { if (f->next_seg == -1) { int x = get8(f); if (f->eof) return FALSE; // EOF at page boundary is not an error! if (0x4f != x ) return error(f, VORBIS_missing_capture_pattern); if (0x67 != get8(f)) return error(f, VORBIS_missing_capture_pattern); if (0x67 != get8(f)) return error(f, VORBIS_missing_capture_pattern); if (0x53 != get8(f)) return error(f, VORBIS_missing_capture_pattern); if (!start_page_no_capturepattern(f)) return FALSE; if (f->page_flag & PAGEFLAG_continued_packet) { // set up enough state that we can read this packet if we want, // e.g. during recovery f->last_seg = FALSE; f->bytes_in_seg = 0; return error(f, VORBIS_continued_packet_flag_invalid); } } return start_packet(f); } static int next_segment(vorb *f) { int len; if (f->last_seg) return 0; if (f->next_seg == -1) { f->last_seg_which = f->segment_count-1; // in case start_page fails if (!start_page(f)) { f->last_seg = 1; return 0; } if (!(f->page_flag & PAGEFLAG_continued_packet)) return error(f, VORBIS_continued_packet_flag_invalid); } len = f->segments[f->next_seg++]; if (len < 255) { f->last_seg = TRUE; f->last_seg_which = f->next_seg-1; } if (f->next_seg >= f->segment_count) f->next_seg = -1; assert(f->bytes_in_seg == 0); f->bytes_in_seg = len; return len; } #define EOP (-1) #define INVALID_BITS (-1) static int get8_packet_raw(vorb *f) { if (!f->bytes_in_seg) { // CLANG! if (f->last_seg) return EOP; else if (!next_segment(f)) return EOP; } assert(f->bytes_in_seg > 0); --f->bytes_in_seg; ++f->packet_bytes; return get8(f); } static int get8_packet(vorb *f) { int x = get8_packet_raw(f); f->valid_bits = 0; return x; } static int get32_packet(vorb *f) { uint32 x; x = get8_packet(f); x += get8_packet(f) << 8; x += get8_packet(f) << 16; x += (uint32) get8_packet(f) << 24; return x; } static void flush_packet(vorb *f) { while (get8_packet_raw(f) != EOP); } // @OPTIMIZE: this is the secondary bit decoder, so it's probably not as important // as the huffman decoder? static uint32 get_bits(vorb *f, int n) { uint32 z; if (f->valid_bits < 0) return 0; if (f->valid_bits < n) { if (n > 24) { // the accumulator technique below would not work correctly in this case z = get_bits(f, 24); z += get_bits(f, n-24) << 24; return z; } if (f->valid_bits == 0) f->acc = 0; while (f->valid_bits < n) { int z = get8_packet_raw(f); if (z == EOP) { f->valid_bits = INVALID_BITS; return 0; } f->acc += z << f->valid_bits; f->valid_bits += 8; } } if (f->valid_bits < 0) return 0; z = f->acc & ((1 << n)-1); f->acc >>= n; f->valid_bits -= n; return z; } // @OPTIMIZE: primary accumulator for huffman // expand the buffer to as many bits as possible without reading off end of packet // it might be nice to allow f->valid_bits and f->acc to be stored in registers, // e.g. cache them locally and decode locally static __forceinline void prep_huffman(vorb *f) { if (f->valid_bits <= 24) { if (f->valid_bits == 0) f->acc = 0; do { int z; if (f->last_seg && !f->bytes_in_seg) return; z = get8_packet_raw(f); if (z == EOP) return; f->acc += (unsigned) z << f->valid_bits; f->valid_bits += 8; } while (f->valid_bits <= 24); } } enum { VORBIS_packet_id = 1, VORBIS_packet_comment = 3, VORBIS_packet_setup = 5 }; static int codebook_decode_scalar_raw(vorb *f, Codebook *c) { int i; prep_huffman(f); if (c->codewords == NULL && c->sorted_codewords == NULL) return -1; // cases to use binary search: sorted_codewords && !c->codewords // sorted_codewords && c->entries > 8 if (c->entries > 8 ? c->sorted_codewords!=NULL : !c->codewords) { // binary search uint32 code = bit_reverse(f->acc); int x=0, n=c->sorted_entries, len; while (n > 1) { // invariant: sc[x] <= code < sc[x+n] int m = x + (n >> 1); if (c->sorted_codewords[m] <= code) { x = m; n -= (n>>1); } else { n >>= 1; } } // x is now the sorted index if (!c->sparse) x = c->sorted_values[x]; // x is now sorted index if sparse, or symbol otherwise len = c->codeword_lengths[x]; if (f->valid_bits >= len) { f->acc >>= len; f->valid_bits -= len; return x; } f->valid_bits = 0; return -1; } // if small, linear search assert(!c->sparse); for (i=0; i < c->entries; ++i) { if (c->codeword_lengths[i] == NO_CODE) continue; if (c->codewords[i] == (f->acc & ((1 << c->codeword_lengths[i])-1))) { if (f->valid_bits >= c->codeword_lengths[i]) { f->acc >>= c->codeword_lengths[i]; f->valid_bits -= c->codeword_lengths[i]; return i; } f->valid_bits = 0; return -1; } } error(f, VORBIS_invalid_stream); f->valid_bits = 0; return -1; } #ifndef STB_VORBIS_NO_INLINE_DECODE #define DECODE_RAW(var, f,c) \ if (f->valid_bits < STB_VORBIS_FAST_HUFFMAN_LENGTH) \ prep_huffman(f); \ var = f->acc & FAST_HUFFMAN_TABLE_MASK; \ var = c->fast_huffman[var]; \ if (var >= 0) { \ int n = c->codeword_lengths[var]; \ f->acc >>= n; \ f->valid_bits -= n; \ if (f->valid_bits < 0) { f->valid_bits = 0; var = -1; } \ } else { \ var = codebook_decode_scalar_raw(f,c); \ } #else static int codebook_decode_scalar(vorb *f, Codebook *c) { int i; if (f->valid_bits < STB_VORBIS_FAST_HUFFMAN_LENGTH) prep_huffman(f); // fast huffman table lookup i = f->acc & FAST_HUFFMAN_TABLE_MASK; i = c->fast_huffman[i]; if (i >= 0) { f->acc >>= c->codeword_lengths[i]; f->valid_bits -= c->codeword_lengths[i]; if (f->valid_bits < 0) { f->valid_bits = 0; return -1; } return i; } return codebook_decode_scalar_raw(f,c); } #define DECODE_RAW(var,f,c) var = codebook_decode_scalar(f,c); #endif #define DECODE(var,f,c) \ DECODE_RAW(var,f,c) \ if (c->sparse) var = c->sorted_values[var]; #ifndef STB_VORBIS_DIVIDES_IN_CODEBOOK #define DECODE_VQ(var,f,c) DECODE_RAW(var,f,c) #else #define DECODE_VQ(var,f,c) DECODE(var,f,c) #endif // CODEBOOK_ELEMENT_FAST is an optimization for the CODEBOOK_FLOATS case // where we avoid one addition #define CODEBOOK_ELEMENT(c,off) (c->multiplicands[off]) #define CODEBOOK_ELEMENT_FAST(c,off) (c->multiplicands[off]) #define CODEBOOK_ELEMENT_BASE(c) (0) static int codebook_decode_start(vorb *f, Codebook *c) { int z = -1; // type 0 is only legal in a scalar context if (c->lookup_type == 0) error(f, VORBIS_invalid_stream); else { DECODE_VQ(z,f,c); if (c->sparse) assert(z < c->sorted_entries); if (z < 0) { // check for EOP if (!f->bytes_in_seg) if (f->last_seg) return z; error(f, VORBIS_invalid_stream); } } return z; } static int codebook_decode(vorb *f, Codebook *c, float *output, int len) { int i,z = codebook_decode_start(f,c); if (z < 0) return FALSE; if (len > c->dimensions) len = c->dimensions; #ifdef STB_VORBIS_DIVIDES_IN_CODEBOOK if (c->lookup_type == 1) { float last = CODEBOOK_ELEMENT_BASE(c); int div = 1; for (i=0; i < len; ++i) { int off = (z / div) % c->lookup_values; float val = CODEBOOK_ELEMENT_FAST(c,off) + last; output[i] += val; if (c->sequence_p) last = val + c->minimum_value; div *= c->lookup_values; } return TRUE; } #endif z *= c->dimensions; if (c->sequence_p) { float last = CODEBOOK_ELEMENT_BASE(c); for (i=0; i < len; ++i) { float val = CODEBOOK_ELEMENT_FAST(c,z+i) + last; output[i] += val; last = val + c->minimum_value; } } else { float last = CODEBOOK_ELEMENT_BASE(c); for (i=0; i < len; ++i) { output[i] += CODEBOOK_ELEMENT_FAST(c,z+i) + last; } } return TRUE; } static int codebook_decode_step(vorb *f, Codebook *c, float *output, int len, int step) { int i,z = codebook_decode_start(f,c); float last = CODEBOOK_ELEMENT_BASE(c); if (z < 0) return FALSE; if (len > c->dimensions) len = c->dimensions; #ifdef STB_VORBIS_DIVIDES_IN_CODEBOOK if (c->lookup_type == 1) { int div = 1; for (i=0; i < len; ++i) { int off = (z / div) % c->lookup_values; float val = CODEBOOK_ELEMENT_FAST(c,off) + last; output[i*step] += val; if (c->sequence_p) last = val; div *= c->lookup_values; } return TRUE; } #endif z *= c->dimensions; for (i=0; i < len; ++i) { float val = CODEBOOK_ELEMENT_FAST(c,z+i) + last; output[i*step] += val; if (c->sequence_p) last = val; } return TRUE; } static int codebook_decode_deinterleave_repeat(vorb *f, Codebook *c, float **outputs, int ch, int *c_inter_p, int *p_inter_p, int len, int total_decode) { int c_inter = *c_inter_p; int p_inter = *p_inter_p; int i,z, effective = c->dimensions; // type 0 is only legal in a scalar context if (c->lookup_type == 0) return error(f, VORBIS_invalid_stream); while (total_decode > 0) { float last = CODEBOOK_ELEMENT_BASE(c); DECODE_VQ(z,f,c); #ifndef STB_VORBIS_DIVIDES_IN_CODEBOOK assert(!c->sparse || z < c->sorted_entries); #endif if (z < 0) { if (!f->bytes_in_seg) if (f->last_seg) return FALSE; return error(f, VORBIS_invalid_stream); } // if this will take us off the end of the buffers, stop short! // we check by computing the length of the virtual interleaved // buffer (len*ch), our current offset within it (p_inter*ch)+(c_inter), // and the length we'll be using (effective) if (c_inter + p_inter*ch + effective > len * ch) { effective = len*ch - (p_inter*ch - c_inter); } #ifdef STB_VORBIS_DIVIDES_IN_CODEBOOK if (c->lookup_type == 1) { int div = 1; for (i=0; i < effective; ++i) { int off = (z / div) % c->lookup_values; float val = CODEBOOK_ELEMENT_FAST(c,off) + last; if (outputs[c_inter]) outputs[c_inter][p_inter] += val; if (++c_inter == ch) { c_inter = 0; ++p_inter; } if (c->sequence_p) last = val; div *= c->lookup_values; } } else #endif { z *= c->dimensions; if (c->sequence_p) { for (i=0; i < effective; ++i) { float val = CODEBOOK_ELEMENT_FAST(c,z+i) + last; if (outputs[c_inter]) outputs[c_inter][p_inter] += val; if (++c_inter == ch) { c_inter = 0; ++p_inter; } last = val; } } else { for (i=0; i < effective; ++i) { float val = CODEBOOK_ELEMENT_FAST(c,z+i) + last; if (outputs[c_inter]) outputs[c_inter][p_inter] += val; if (++c_inter == ch) { c_inter = 0; ++p_inter; } } } } total_decode -= effective; } *c_inter_p = c_inter; *p_inter_p = p_inter; return TRUE; } static int predict_point(int x, int x0, int x1, int y0, int y1) { int dy = y1 - y0; int adx = x1 - x0; // @OPTIMIZE: force int division to round in the right direction... is this necessary on x86? int err = abs(dy) * (x - x0); int off = err / adx; return dy < 0 ? y0 - off : y0 + off; } // the following table is block-copied from the specification static float inverse_db_table[256] = { 1.0649863e-07f, 1.1341951e-07f, 1.2079015e-07f, 1.2863978e-07f, 1.3699951e-07f, 1.4590251e-07f, 1.5538408e-07f, 1.6548181e-07f, 1.7623575e-07f, 1.8768855e-07f, 1.9988561e-07f, 2.1287530e-07f, 2.2670913e-07f, 2.4144197e-07f, 2.5713223e-07f, 2.7384213e-07f, 2.9163793e-07f, 3.1059021e-07f, 3.3077411e-07f, 3.5226968e-07f, 3.7516214e-07f, 3.9954229e-07f, 4.2550680e-07f, 4.5315863e-07f, 4.8260743e-07f, 5.1396998e-07f, 5.4737065e-07f, 5.8294187e-07f, 6.2082472e-07f, 6.6116941e-07f, 7.0413592e-07f, 7.4989464e-07f, 7.9862701e-07f, 8.5052630e-07f, 9.0579828e-07f, 9.6466216e-07f, 1.0273513e-06f, 1.0941144e-06f, 1.1652161e-06f, 1.2409384e-06f, 1.3215816e-06f, 1.4074654e-06f, 1.4989305e-06f, 1.5963394e-06f, 1.7000785e-06f, 1.8105592e-06f, 1.9282195e-06f, 2.0535261e-06f, 2.1869758e-06f, 2.3290978e-06f, 2.4804557e-06f, 2.6416497e-06f, 2.8133190e-06f, 2.9961443e-06f, 3.1908506e-06f, 3.3982101e-06f, 3.6190449e-06f, 3.8542308e-06f, 4.1047004e-06f, 4.3714470e-06f, 4.6555282e-06f, 4.9580707e-06f, 5.2802740e-06f, 5.6234160e-06f, 5.9888572e-06f, 6.3780469e-06f, 6.7925283e-06f, 7.2339451e-06f, 7.7040476e-06f, 8.2047000e-06f, 8.7378876e-06f, 9.3057248e-06f, 9.9104632e-06f, 1.0554501e-05f, 1.1240392e-05f, 1.1970856e-05f, 1.2748789e-05f, 1.3577278e-05f, 1.4459606e-05f, 1.5399272e-05f, 1.6400004e-05f, 1.7465768e-05f, 1.8600792e-05f, 1.9809576e-05f, 2.1096914e-05f, 2.2467911e-05f, 2.3928002e-05f, 2.5482978e-05f, 2.7139006e-05f, 2.8902651e-05f, 3.0780908e-05f, 3.2781225e-05f, 3.4911534e-05f, 3.7180282e-05f, 3.9596466e-05f, 4.2169667e-05f, 4.4910090e-05f, 4.7828601e-05f, 5.0936773e-05f, 5.4246931e-05f, 5.7772202e-05f, 6.1526565e-05f, 6.5524908e-05f, 6.9783085e-05f, 7.4317983e-05f, 7.9147585e-05f, 8.4291040e-05f, 8.9768747e-05f, 9.5602426e-05f, 0.00010181521f, 0.00010843174f, 0.00011547824f, 0.00012298267f, 0.00013097477f, 0.00013948625f, 0.00014855085f, 0.00015820453f, 0.00016848555f, 0.00017943469f, 0.00019109536f, 0.00020351382f, 0.00021673929f, 0.00023082423f, 0.00024582449f, 0.00026179955f, 0.00027881276f, 0.00029693158f, 0.00031622787f, 0.00033677814f, 0.00035866388f, 0.00038197188f, 0.00040679456f, 0.00043323036f, 0.00046138411f, 0.00049136745f, 0.00052329927f, 0.00055730621f, 0.00059352311f, 0.00063209358f, 0.00067317058f, 0.00071691700f, 0.00076350630f, 0.00081312324f, 0.00086596457f, 0.00092223983f, 0.00098217216f, 0.0010459992f, 0.0011139742f, 0.0011863665f, 0.0012634633f, 0.0013455702f, 0.0014330129f, 0.0015261382f, 0.0016253153f, 0.0017309374f, 0.0018434235f, 0.0019632195f, 0.0020908006f, 0.0022266726f, 0.0023713743f, 0.0025254795f, 0.0026895994f, 0.0028643847f, 0.0030505286f, 0.0032487691f, 0.0034598925f, 0.0036847358f, 0.0039241906f, 0.0041792066f, 0.0044507950f, 0.0047400328f, 0.0050480668f, 0.0053761186f, 0.0057254891f, 0.0060975636f, 0.0064938176f, 0.0069158225f, 0.0073652516f, 0.0078438871f, 0.0083536271f, 0.0088964928f, 0.009474637f, 0.010090352f, 0.010746080f, 0.011444421f, 0.012188144f, 0.012980198f, 0.013823725f, 0.014722068f, 0.015678791f, 0.016697687f, 0.017782797f, 0.018938423f, 0.020169149f, 0.021479854f, 0.022875735f, 0.024362330f, 0.025945531f, 0.027631618f, 0.029427276f, 0.031339626f, 0.033376252f, 0.035545228f, 0.037855157f, 0.040315199f, 0.042935108f, 0.045725273f, 0.048696758f, 0.051861348f, 0.055231591f, 0.058820850f, 0.062643361f, 0.066714279f, 0.071049749f, 0.075666962f, 0.080584227f, 0.085821044f, 0.091398179f, 0.097337747f, 0.10366330f, 0.11039993f, 0.11757434f, 0.12521498f, 0.13335215f, 0.14201813f, 0.15124727f, 0.16107617f, 0.17154380f, 0.18269168f, 0.19456402f, 0.20720788f, 0.22067342f, 0.23501402f, 0.25028656f, 0.26655159f, 0.28387361f, 0.30232132f, 0.32196786f, 0.34289114f, 0.36517414f, 0.38890521f, 0.41417847f, 0.44109412f, 0.46975890f, 0.50028648f, 0.53279791f, 0.56742212f, 0.60429640f, 0.64356699f, 0.68538959f, 0.72993007f, 0.77736504f, 0.82788260f, 0.88168307f, 0.9389798f, 1.0f }; // @OPTIMIZE: if you want to replace this bresenham line-drawing routine, // note that you must produce bit-identical output to decode correctly; // this specific sequence of operations is specified in the spec (it's // drawing integer-quantized frequency-space lines that the encoder // expects to be exactly the same) // ... also, isn't the whole point of Bresenham's algorithm to NOT // have to divide in the setup? sigh. #ifndef STB_VORBIS_NO_DEFER_FLOOR #define LINE_OP(a,b) a *= b #else #define LINE_OP(a,b) a = b #endif #ifdef STB_VORBIS_DIVIDE_TABLE #define DIVTAB_NUMER 32 #define DIVTAB_DENOM 64 int8 integer_divide_table[DIVTAB_NUMER][DIVTAB_DENOM]; // 2KB #endif static __forceinline void draw_line(float *output, int x0, int y0, int x1, int y1, int n) { int dy = y1 - y0; int adx = x1 - x0; int ady = abs(dy); int base; int x=x0,y=y0; int err = 0; int sy; #ifdef STB_VORBIS_DIVIDE_TABLE if (adx < DIVTAB_DENOM && ady < DIVTAB_NUMER) { if (dy < 0) { base = -integer_divide_table[ady][adx]; sy = base-1; } else { base = integer_divide_table[ady][adx]; sy = base+1; } } else { base = dy / adx; if (dy < 0) sy = base - 1; else sy = base+1; } #else base = dy / adx; if (dy < 0) sy = base - 1; else sy = base+1; #endif ady -= abs(base) * adx; if (x1 > n) x1 = n; if (x < x1) { LINE_OP(output[x], inverse_db_table[y&255]); for (++x; x < x1; ++x) { err += ady; if (err >= adx) { err -= adx; y += sy; } else y += base; LINE_OP(output[x], inverse_db_table[y&255]); } } } static int residue_decode(vorb *f, Codebook *book, float *target, int offset, int n, int rtype) { int k; if (rtype == 0) { int step = n / book->dimensions; for (k=0; k < step; ++k) if (!codebook_decode_step(f, book, target+offset+k, n-offset-k, step)) return FALSE; } else { for (k=0; k < n; ) { if (!codebook_decode(f, book, target+offset, n-k)) return FALSE; k += book->dimensions; offset += book->dimensions; } } return TRUE; } // n is 1/2 of the blocksize -- // specification: "Correct per-vector decode length is [n]/2" static void decode_residue(vorb *f, float *residue_buffers[], int ch, int n, int rn, uint8 *do_not_decode) { int i,j,pass; Residue *r = f->residue_config + rn; int rtype = f->residue_types[rn]; int c = r->classbook; int classwords = f->codebooks[c].dimensions; unsigned int actual_size = rtype == 2 ? n*2 : n; unsigned int limit_r_begin = (r->begin < actual_size ? r->begin : actual_size); unsigned int limit_r_end = (r->end < actual_size ? r->end : actual_size); int n_read = limit_r_end - limit_r_begin; int part_read = n_read / r->part_size; int temp_alloc_point = temp_alloc_save(f); #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE uint8 ***part_classdata = (uint8 ***) temp_block_array(f,f->channels, part_read * sizeof(**part_classdata)); #else int **classifications = (int **) temp_block_array(f,f->channels, part_read * sizeof(**classifications)); #endif CHECK(f); for (i=0; i < ch; ++i) if (!do_not_decode[i]) memset(residue_buffers[i], 0, sizeof(float) * n); if (rtype == 2 && ch != 1) { for (j=0; j < ch; ++j) if (!do_not_decode[j]) break; if (j == ch) goto done; for (pass=0; pass < 8; ++pass) { int pcount = 0, class_set = 0; if (ch == 2) { while (pcount < part_read) { int z = r->begin + pcount*r->part_size; int c_inter = (z & 1), p_inter = z>>1; if (pass == 0) { Codebook *c = f->codebooks+r->classbook; int q; DECODE(q,f,c); if (q == EOP) goto done; #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE part_classdata[0][class_set] = r->classdata[q]; #else for (i=classwords-1; i >= 0; --i) { classifications[0][i+pcount] = q % r->classifications; q /= r->classifications; } #endif } for (i=0; i < classwords && pcount < part_read; ++i, ++pcount) { int z = r->begin + pcount*r->part_size; #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE int c = part_classdata[0][class_set][i]; #else int c = classifications[0][pcount]; #endif int b = r->residue_books[c][pass]; if (b >= 0) { Codebook *book = f->codebooks + b; #ifdef STB_VORBIS_DIVIDES_IN_CODEBOOK if (!codebook_decode_deinterleave_repeat(f, book, residue_buffers, ch, &c_inter, &p_inter, n, r->part_size)) goto done; #else // saves 1% if (!codebook_decode_deinterleave_repeat(f, book, residue_buffers, ch, &c_inter, &p_inter, n, r->part_size)) goto done; #endif } else { z += r->part_size; c_inter = z & 1; p_inter = z >> 1; } } #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE ++class_set; #endif } } else if (ch > 2) { while (pcount < part_read) { int z = r->begin + pcount*r->part_size; int c_inter = z % ch, p_inter = z/ch; if (pass == 0) { Codebook *c = f->codebooks+r->classbook; int q; DECODE(q,f,c); if (q == EOP) goto done; #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE part_classdata[0][class_set] = r->classdata[q]; #else for (i=classwords-1; i >= 0; --i) { classifications[0][i+pcount] = q % r->classifications; q /= r->classifications; } #endif } for (i=0; i < classwords && pcount < part_read; ++i, ++pcount) { int z = r->begin + pcount*r->part_size; #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE int c = part_classdata[0][class_set][i]; #else int c = classifications[0][pcount]; #endif int b = r->residue_books[c][pass]; if (b >= 0) { Codebook *book = f->codebooks + b; if (!codebook_decode_deinterleave_repeat(f, book, residue_buffers, ch, &c_inter, &p_inter, n, r->part_size)) goto done; } else { z += r->part_size; c_inter = z % ch; p_inter = z / ch; } } #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE ++class_set; #endif } } } goto done; } CHECK(f); for (pass=0; pass < 8; ++pass) { int pcount = 0, class_set=0; while (pcount < part_read) { if (pass == 0) { for (j=0; j < ch; ++j) { if (!do_not_decode[j]) { Codebook *c = f->codebooks+r->classbook; int temp; DECODE(temp,f,c); if (temp == EOP) goto done; #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE part_classdata[j][class_set] = r->classdata[temp]; #else for (i=classwords-1; i >= 0; --i) { classifications[j][i+pcount] = temp % r->classifications; temp /= r->classifications; } #endif } } } for (i=0; i < classwords && pcount < part_read; ++i, ++pcount) { for (j=0; j < ch; ++j) { if (!do_not_decode[j]) { #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE int c = part_classdata[j][class_set][i]; #else int c = classifications[j][pcount]; #endif int b = r->residue_books[c][pass]; if (b >= 0) { float *target = residue_buffers[j]; int offset = r->begin + pcount * r->part_size; int n = r->part_size; Codebook *book = f->codebooks + b; if (!residue_decode(f, book, target, offset, n, rtype)) goto done; } } } } #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE ++class_set; #endif } } done: CHECK(f); #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE temp_free(f,part_classdata); #else temp_free(f,classifications); #endif temp_alloc_restore(f,temp_alloc_point); } #if 0 // slow way for debugging void inverse_mdct_slow(float *buffer, int n) { int i,j; int n2 = n >> 1; float *x = (float *) malloc(sizeof(*x) * n2); memcpy(x, buffer, sizeof(*x) * n2); for (i=0; i < n; ++i) { float acc = 0; for (j=0; j < n2; ++j) // formula from paper: //acc += n/4.0f * x[j] * (float) cos(M_PI / 2 / n * (2 * i + 1 + n/2.0)*(2*j+1)); // formula from wikipedia //acc += 2.0f / n2 * x[j] * (float) cos(M_PI/n2 * (i + 0.5 + n2/2)*(j + 0.5)); // these are equivalent, except the formula from the paper inverts the multiplier! // however, what actually works is NO MULTIPLIER!?! //acc += 64 * 2.0f / n2 * x[j] * (float) cos(M_PI/n2 * (i + 0.5 + n2/2)*(j + 0.5)); acc += x[j] * (float) cos(M_PI / 2 / n * (2 * i + 1 + n/2.0)*(2*j+1)); buffer[i] = acc; } free(x); } #elif 0 // same as above, but just barely able to run in real time on modern machines void inverse_mdct_slow(float *buffer, int n, vorb *f, int blocktype) { float mcos[16384]; int i,j; int n2 = n >> 1, nmask = (n << 2) -1; float *x = (float *) malloc(sizeof(*x) * n2); memcpy(x, buffer, sizeof(*x) * n2); for (i=0; i < 4*n; ++i) mcos[i] = (float) cos(M_PI / 2 * i / n); for (i=0; i < n; ++i) { float acc = 0; for (j=0; j < n2; ++j) acc += x[j] * mcos[(2 * i + 1 + n2)*(2*j+1) & nmask]; buffer[i] = acc; } free(x); } #elif 0 // transform to use a slow dct-iv; this is STILL basically trivial, // but only requires half as many ops void dct_iv_slow(float *buffer, int n) { float mcos[16384]; float x[2048]; int i,j; int n2 = n >> 1, nmask = (n << 3) - 1; memcpy(x, buffer, sizeof(*x) * n); for (i=0; i < 8*n; ++i) mcos[i] = (float) cos(M_PI / 4 * i / n); for (i=0; i < n; ++i) { float acc = 0; for (j=0; j < n; ++j) acc += x[j] * mcos[((2 * i + 1)*(2*j+1)) & nmask]; buffer[i] = acc; } } void inverse_mdct_slow(float *buffer, int n, vorb *f, int blocktype) { int i, n4 = n >> 2, n2 = n >> 1, n3_4 = n - n4; float temp[4096]; memcpy(temp, buffer, n2 * sizeof(float)); dct_iv_slow(temp, n2); // returns -c'-d, a-b' for (i=0; i < n4 ; ++i) buffer[i] = temp[i+n4]; // a-b' for ( ; i < n3_4; ++i) buffer[i] = -temp[n3_4 - i - 1]; // b-a', c+d' for ( ; i < n ; ++i) buffer[i] = -temp[i - n3_4]; // c'+d } #endif #ifndef LIBVORBIS_MDCT #define LIBVORBIS_MDCT 0 #endif #if LIBVORBIS_MDCT // directly call the vorbis MDCT using an interface documented // by Jeff Roberts... useful for performance comparison typedef struct { int n; int log2n; float *trig; int *bitrev; float scale; } mdct_lookup; extern void mdct_init(mdct_lookup *lookup, int n); extern void mdct_clear(mdct_lookup *l); extern void mdct_backward(mdct_lookup *init, float *in, float *out); mdct_lookup M1,M2; void inverse_mdct(float *buffer, int n, vorb *f, int blocktype) { mdct_lookup *M; if (M1.n == n) M = &M1; else if (M2.n == n) M = &M2; else if (M1.n == 0) { mdct_init(&M1, n); M = &M1; } else { if (M2.n) __asm int 3; mdct_init(&M2, n); M = &M2; } mdct_backward(M, buffer, buffer); } #endif // the following were split out into separate functions while optimizing; // they could be pushed back up but eh. __forceinline showed no change; // they're probably already being inlined. static void imdct_step3_iter0_loop(int n, float *e, int i_off, int k_off, float *A) { float *ee0 = e + i_off; float *ee2 = ee0 + k_off; int i; assert((n & 3) == 0); for (i=(n>>2); i > 0; --i) { float k00_20, k01_21; k00_20 = ee0[ 0] - ee2[ 0]; k01_21 = ee0[-1] - ee2[-1]; ee0[ 0] += ee2[ 0];//ee0[ 0] = ee0[ 0] + ee2[ 0]; ee0[-1] += ee2[-1];//ee0[-1] = ee0[-1] + ee2[-1]; ee2[ 0] = k00_20 * A[0] - k01_21 * A[1]; ee2[-1] = k01_21 * A[0] + k00_20 * A[1]; A += 8; k00_20 = ee0[-2] - ee2[-2]; k01_21 = ee0[-3] - ee2[-3]; ee0[-2] += ee2[-2];//ee0[-2] = ee0[-2] + ee2[-2]; ee0[-3] += ee2[-3];//ee0[-3] = ee0[-3] + ee2[-3]; ee2[-2] = k00_20 * A[0] - k01_21 * A[1]; ee2[-3] = k01_21 * A[0] + k00_20 * A[1]; A += 8; k00_20 = ee0[-4] - ee2[-4]; k01_21 = ee0[-5] - ee2[-5]; ee0[-4] += ee2[-4];//ee0[-4] = ee0[-4] + ee2[-4]; ee0[-5] += ee2[-5];//ee0[-5] = ee0[-5] + ee2[-5]; ee2[-4] = k00_20 * A[0] - k01_21 * A[1]; ee2[-5] = k01_21 * A[0] + k00_20 * A[1]; A += 8; k00_20 = ee0[-6] - ee2[-6]; k01_21 = ee0[-7] - ee2[-7]; ee0[-6] += ee2[-6];//ee0[-6] = ee0[-6] + ee2[-6]; ee0[-7] += ee2[-7];//ee0[-7] = ee0[-7] + ee2[-7]; ee2[-6] = k00_20 * A[0] - k01_21 * A[1]; ee2[-7] = k01_21 * A[0] + k00_20 * A[1]; A += 8; ee0 -= 8; ee2 -= 8; } } static void imdct_step3_inner_r_loop(int lim, float *e, int d0, int k_off, float *A, int k1) { int i; float k00_20, k01_21; float *e0 = e + d0; float *e2 = e0 + k_off; for (i=lim >> 2; i > 0; --i) { k00_20 = e0[-0] - e2[-0]; k01_21 = e0[-1] - e2[-1]; e0[-0] += e2[-0];//e0[-0] = e0[-0] + e2[-0]; e0[-1] += e2[-1];//e0[-1] = e0[-1] + e2[-1]; e2[-0] = (k00_20)*A[0] - (k01_21) * A[1]; e2[-1] = (k01_21)*A[0] + (k00_20) * A[1]; A += k1; k00_20 = e0[-2] - e2[-2]; k01_21 = e0[-3] - e2[-3]; e0[-2] += e2[-2];//e0[-2] = e0[-2] + e2[-2]; e0[-3] += e2[-3];//e0[-3] = e0[-3] + e2[-3]; e2[-2] = (k00_20)*A[0] - (k01_21) * A[1]; e2[-3] = (k01_21)*A[0] + (k00_20) * A[1]; A += k1; k00_20 = e0[-4] - e2[-4]; k01_21 = e0[-5] - e2[-5]; e0[-4] += e2[-4];//e0[-4] = e0[-4] + e2[-4]; e0[-5] += e2[-5];//e0[-5] = e0[-5] + e2[-5]; e2[-4] = (k00_20)*A[0] - (k01_21) * A[1]; e2[-5] = (k01_21)*A[0] + (k00_20) * A[1]; A += k1; k00_20 = e0[-6] - e2[-6]; k01_21 = e0[-7] - e2[-7]; e0[-6] += e2[-6];//e0[-6] = e0[-6] + e2[-6]; e0[-7] += e2[-7];//e0[-7] = e0[-7] + e2[-7]; e2[-6] = (k00_20)*A[0] - (k01_21) * A[1]; e2[-7] = (k01_21)*A[0] + (k00_20) * A[1]; e0 -= 8; e2 -= 8; A += k1; } } static void imdct_step3_inner_s_loop(int n, float *e, int i_off, int k_off, float *A, int a_off, int k0) { int i; float A0 = A[0]; float A1 = A[0+1]; float A2 = A[0+a_off]; float A3 = A[0+a_off+1]; float A4 = A[0+a_off*2+0]; float A5 = A[0+a_off*2+1]; float A6 = A[0+a_off*3+0]; float A7 = A[0+a_off*3+1]; float k00,k11; float *ee0 = e +i_off; float *ee2 = ee0+k_off; for (i=n; i > 0; --i) { k00 = ee0[ 0] - ee2[ 0]; k11 = ee0[-1] - ee2[-1]; ee0[ 0] = ee0[ 0] + ee2[ 0]; ee0[-1] = ee0[-1] + ee2[-1]; ee2[ 0] = (k00) * A0 - (k11) * A1; ee2[-1] = (k11) * A0 + (k00) * A1; k00 = ee0[-2] - ee2[-2]; k11 = ee0[-3] - ee2[-3]; ee0[-2] = ee0[-2] + ee2[-2]; ee0[-3] = ee0[-3] + ee2[-3]; ee2[-2] = (k00) * A2 - (k11) * A3; ee2[-3] = (k11) * A2 + (k00) * A3; k00 = ee0[-4] - ee2[-4]; k11 = ee0[-5] - ee2[-5]; ee0[-4] = ee0[-4] + ee2[-4]; ee0[-5] = ee0[-5] + ee2[-5]; ee2[-4] = (k00) * A4 - (k11) * A5; ee2[-5] = (k11) * A4 + (k00) * A5; k00 = ee0[-6] - ee2[-6]; k11 = ee0[-7] - ee2[-7]; ee0[-6] = ee0[-6] + ee2[-6]; ee0[-7] = ee0[-7] + ee2[-7]; ee2[-6] = (k00) * A6 - (k11) * A7; ee2[-7] = (k11) * A6 + (k00) * A7; ee0 -= k0; ee2 -= k0; } } static __forceinline void iter_54(float *z) { float k00,k11,k22,k33; float y0,y1,y2,y3; k00 = z[ 0] - z[-4]; y0 = z[ 0] + z[-4]; y2 = z[-2] + z[-6]; k22 = z[-2] - z[-6]; z[-0] = y0 + y2; // z0 + z4 + z2 + z6 z[-2] = y0 - y2; // z0 + z4 - z2 - z6 // done with y0,y2 k33 = z[-3] - z[-7]; z[-4] = k00 + k33; // z0 - z4 + z3 - z7 z[-6] = k00 - k33; // z0 - z4 - z3 + z7 // done with k33 k11 = z[-1] - z[-5]; y1 = z[-1] + z[-5]; y3 = z[-3] + z[-7]; z[-1] = y1 + y3; // z1 + z5 + z3 + z7 z[-3] = y1 - y3; // z1 + z5 - z3 - z7 z[-5] = k11 - k22; // z1 - z5 + z2 - z6 z[-7] = k11 + k22; // z1 - z5 - z2 + z6 } static void imdct_step3_inner_s_loop_ld654(int n, float *e, int i_off, float *A, int base_n) { int a_off = base_n >> 3; float A2 = A[0+a_off]; float *z = e + i_off; float *base = z - 16 * n; while (z > base) { float k00,k11; k00 = z[-0] - z[-8]; k11 = z[-1] - z[-9]; z[-0] = z[-0] + z[-8]; z[-1] = z[-1] + z[-9]; z[-8] = k00; z[-9] = k11 ; k00 = z[ -2] - z[-10]; k11 = z[ -3] - z[-11]; z[ -2] = z[ -2] + z[-10]; z[ -3] = z[ -3] + z[-11]; z[-10] = (k00+k11) * A2; z[-11] = (k11-k00) * A2; k00 = z[-12] - z[ -4]; // reverse to avoid a unary negation k11 = z[ -5] - z[-13]; z[ -4] = z[ -4] + z[-12]; z[ -5] = z[ -5] + z[-13]; z[-12] = k11; z[-13] = k00; k00 = z[-14] - z[ -6]; // reverse to avoid a unary negation k11 = z[ -7] - z[-15]; z[ -6] = z[ -6] + z[-14]; z[ -7] = z[ -7] + z[-15]; z[-14] = (k00+k11) * A2; z[-15] = (k00-k11) * A2; iter_54(z); iter_54(z-8); z -= 16; } } static void inverse_mdct(float *buffer, int n, vorb *f, int blocktype) { int n2 = n >> 1, n4 = n >> 2, n8 = n >> 3, l; int ld; // @OPTIMIZE: reduce register pressure by using fewer variables? int save_point = temp_alloc_save(f); float *buf2 = (float *) temp_alloc(f, n2 * sizeof(*buf2)); float *u=NULL,*v=NULL; // twiddle factors float *A = f->A[blocktype]; // IMDCT algorithm from "The use of multirate filter banks for coding of high quality digital audio" // See notes about bugs in that paper in less-optimal implementation 'inverse_mdct_old' after this function. // kernel from paper // merged: // copy and reflect spectral data // step 0 // note that it turns out that the items added together during // this step are, in fact, being added to themselves (as reflected // by step 0). inexplicable inefficiency! this became obvious // once I combined the passes. // so there's a missing 'times 2' here (for adding X to itself). // this propagates through linearly to the end, where the numbers // are 1/2 too small, and need to be compensated for. { float *d,*e, *AA, *e_stop; d = &buf2[n2-2]; AA = A; e = &buffer[0]; e_stop = &buffer[n2]; while (e != e_stop) { d[1] = (e[0] * AA[0] - e[2]*AA[1]); d[0] = (e[0] * AA[1] + e[2]*AA[0]); d -= 2; AA += 2; e += 4; } e = &buffer[n2-3]; while (d >= buf2) { d[1] = (-e[2] * AA[0] - -e[0]*AA[1]); d[0] = (-e[2] * AA[1] + -e[0]*AA[0]); d -= 2; AA += 2; e -= 4; } } // now we use symbolic names for these, so that we can // possibly swap their meaning as we change which operations // are in place u = buffer; v = buf2; // step 2 (paper output is w, now u) // this could be in place, but the data ends up in the wrong // place... _somebody_'s got to swap it, so this is nominated { float *AA = &A[n2-8]; float *d0,*d1, *e0, *e1; e0 = &v[n4]; e1 = &v[0]; d0 = &u[n4]; d1 = &u[0]; while (AA >= A) { float v40_20, v41_21; v41_21 = e0[1] - e1[1]; v40_20 = e0[0] - e1[0]; d0[1] = e0[1] + e1[1]; d0[0] = e0[0] + e1[0]; d1[1] = v41_21*AA[4] - v40_20*AA[5]; d1[0] = v40_20*AA[4] + v41_21*AA[5]; v41_21 = e0[3] - e1[3]; v40_20 = e0[2] - e1[2]; d0[3] = e0[3] + e1[3]; d0[2] = e0[2] + e1[2]; d1[3] = v41_21*AA[0] - v40_20*AA[1]; d1[2] = v40_20*AA[0] + v41_21*AA[1]; AA -= 8; d0 += 4; d1 += 4; e0 += 4; e1 += 4; } } // step 3 ld = ilog(n) - 1; // ilog is off-by-one from normal definitions // optimized step 3: // the original step3 loop can be nested r inside s or s inside r; // it's written originally as s inside r, but this is dumb when r // iterates many times, and s few. So I have two copies of it and // switch between them halfway. // this is iteration 0 of step 3 imdct_step3_iter0_loop(n >> 4, u, n2-1-n4*0, -(n >> 3), A); imdct_step3_iter0_loop(n >> 4, u, n2-1-n4*1, -(n >> 3), A); // this is iteration 1 of step 3 imdct_step3_inner_r_loop(n >> 5, u, n2-1 - n8*0, -(n >> 4), A, 16); imdct_step3_inner_r_loop(n >> 5, u, n2-1 - n8*1, -(n >> 4), A, 16); imdct_step3_inner_r_loop(n >> 5, u, n2-1 - n8*2, -(n >> 4), A, 16); imdct_step3_inner_r_loop(n >> 5, u, n2-1 - n8*3, -(n >> 4), A, 16); l=2; for (; l < (ld-3)>>1; ++l) { int k0 = n >> (l+2), k0_2 = k0>>1; int lim = 1 << (l+1); int i; for (i=0; i < lim; ++i) imdct_step3_inner_r_loop(n >> (l+4), u, n2-1 - k0*i, -k0_2, A, 1 << (l+3)); } for (; l < ld-6; ++l) { int k0 = n >> (l+2), k1 = 1 << (l+3), k0_2 = k0>>1; int rlim = n >> (l+6), r; int lim = 1 << (l+1); int i_off; float *A0 = A; i_off = n2-1; for (r=rlim; r > 0; --r) { imdct_step3_inner_s_loop(lim, u, i_off, -k0_2, A0, k1, k0); A0 += k1*4; i_off -= 8; } } // iterations with count: // ld-6,-5,-4 all interleaved together // the big win comes from getting rid of needless flops // due to the constants on pass 5 & 4 being all 1 and 0; // combining them to be simultaneous to improve cache made little difference imdct_step3_inner_s_loop_ld654(n >> 5, u, n2-1, A, n); // output is u // step 4, 5, and 6 // cannot be in-place because of step 5 { uint16 *bitrev = f->bit_reverse[blocktype]; // weirdly, I'd have thought reading sequentially and writing // erratically would have been better than vice-versa, but in // fact that's not what my testing showed. (That is, with // j = bitreverse(i), do you read i and write j, or read j and write i.) float *d0 = &v[n4-4]; float *d1 = &v[n2-4]; while (d0 >= v) { int k4; k4 = bitrev[0]; d1[3] = u[k4+0]; d1[2] = u[k4+1]; d0[3] = u[k4+2]; d0[2] = u[k4+3]; k4 = bitrev[1]; d1[1] = u[k4+0]; d1[0] = u[k4+1]; d0[1] = u[k4+2]; d0[0] = u[k4+3]; d0 -= 4; d1 -= 4; bitrev += 2; } } // (paper output is u, now v) // data must be in buf2 assert(v == buf2); // step 7 (paper output is v, now v) // this is now in place { float *C = f->C[blocktype]; float *d, *e; d = v; e = v + n2 - 4; while (d < e) { float a02,a11,b0,b1,b2,b3; a02 = d[0] - e[2]; a11 = d[1] + e[3]; b0 = C[1]*a02 + C[0]*a11; b1 = C[1]*a11 - C[0]*a02; b2 = d[0] + e[ 2]; b3 = d[1] - e[ 3]; d[0] = b2 + b0; d[1] = b3 + b1; e[2] = b2 - b0; e[3] = b1 - b3; a02 = d[2] - e[0]; a11 = d[3] + e[1]; b0 = C[3]*a02 + C[2]*a11; b1 = C[3]*a11 - C[2]*a02; b2 = d[2] + e[ 0]; b3 = d[3] - e[ 1]; d[2] = b2 + b0; d[3] = b3 + b1; e[0] = b2 - b0; e[1] = b1 - b3; C += 4; d += 4; e -= 4; } } // data must be in buf2 // step 8+decode (paper output is X, now buffer) // this generates pairs of data a la 8 and pushes them directly through // the decode kernel (pushing rather than pulling) to avoid having // to make another pass later // this cannot POSSIBLY be in place, so we refer to the buffers directly { float *d0,*d1,*d2,*d3; float *B = f->B[blocktype] + n2 - 8; float *e = buf2 + n2 - 8; d0 = &buffer[0]; d1 = &buffer[n2-4]; d2 = &buffer[n2]; d3 = &buffer[n-4]; while (e >= v) { float p0,p1,p2,p3; p3 = e[6]*B[7] - e[7]*B[6]; p2 = -e[6]*B[6] - e[7]*B[7]; d0[0] = p3; d1[3] = - p3; d2[0] = p2; d3[3] = p2; p1 = e[4]*B[5] - e[5]*B[4]; p0 = -e[4]*B[4] - e[5]*B[5]; d0[1] = p1; d1[2] = - p1; d2[1] = p0; d3[2] = p0; p3 = e[2]*B[3] - e[3]*B[2]; p2 = -e[2]*B[2] - e[3]*B[3]; d0[2] = p3; d1[1] = - p3; d2[2] = p2; d3[1] = p2; p1 = e[0]*B[1] - e[1]*B[0]; p0 = -e[0]*B[0] - e[1]*B[1]; d0[3] = p1; d1[0] = - p1; d2[3] = p0; d3[0] = p0; B -= 8; e -= 8; d0 += 4; d2 += 4; d1 -= 4; d3 -= 4; } } temp_free(f,buf2); temp_alloc_restore(f,save_point); } #if 0 // this is the original version of the above code, if you want to optimize it from scratch void inverse_mdct_naive(float *buffer, int n) { float s; float A[1 << 12], B[1 << 12], C[1 << 11]; int i,k,k2,k4, n2 = n >> 1, n4 = n >> 2, n8 = n >> 3, l; int n3_4 = n - n4, ld; // how can they claim this only uses N words?! // oh, because they're only used sparsely, whoops float u[1 << 13], X[1 << 13], v[1 << 13], w[1 << 13]; // set up twiddle factors for (k=k2=0; k < n4; ++k,k2+=2) { A[k2 ] = (float) cos(4*k*M_PI/n); A[k2+1] = (float) -sin(4*k*M_PI/n); B[k2 ] = (float) cos((k2+1)*M_PI/n/2); B[k2+1] = (float) sin((k2+1)*M_PI/n/2); } for (k=k2=0; k < n8; ++k,k2+=2) { C[k2 ] = (float) cos(2*(k2+1)*M_PI/n); C[k2+1] = (float) -sin(2*(k2+1)*M_PI/n); } // IMDCT algorithm from "The use of multirate filter banks for coding of high quality digital audio" // Note there are bugs in that pseudocode, presumably due to them attempting // to rename the arrays nicely rather than representing the way their actual // implementation bounces buffers back and forth. As a result, even in the // "some formulars corrected" version, a direct implementation fails. These // are noted below as "paper bug". // copy and reflect spectral data for (k=0; k < n2; ++k) u[k] = buffer[k]; for ( ; k < n ; ++k) u[k] = -buffer[n - k - 1]; // kernel from paper // step 1 for (k=k2=k4=0; k < n4; k+=1, k2+=2, k4+=4) { v[n-k4-1] = (u[k4] - u[n-k4-1]) * A[k2] - (u[k4+2] - u[n-k4-3])*A[k2+1]; v[n-k4-3] = (u[k4] - u[n-k4-1]) * A[k2+1] + (u[k4+2] - u[n-k4-3])*A[k2]; } // step 2 for (k=k4=0; k < n8; k+=1, k4+=4) { w[n2+3+k4] = v[n2+3+k4] + v[k4+3]; w[n2+1+k4] = v[n2+1+k4] + v[k4+1]; w[k4+3] = (v[n2+3+k4] - v[k4+3])*A[n2-4-k4] - (v[n2+1+k4]-v[k4+1])*A[n2-3-k4]; w[k4+1] = (v[n2+1+k4] - v[k4+1])*A[n2-4-k4] + (v[n2+3+k4]-v[k4+3])*A[n2-3-k4]; } // step 3 ld = ilog(n) - 1; // ilog is off-by-one from normal definitions for (l=0; l < ld-3; ++l) { int k0 = n >> (l+2), k1 = 1 << (l+3); int rlim = n >> (l+4), r4, r; int s2lim = 1 << (l+2), s2; for (r=r4=0; r < rlim; r4+=4,++r) { for (s2=0; s2 < s2lim; s2+=2) { u[n-1-k0*s2-r4] = w[n-1-k0*s2-r4] + w[n-1-k0*(s2+1)-r4]; u[n-3-k0*s2-r4] = w[n-3-k0*s2-r4] + w[n-3-k0*(s2+1)-r4]; u[n-1-k0*(s2+1)-r4] = (w[n-1-k0*s2-r4] - w[n-1-k0*(s2+1)-r4]) * A[r*k1] - (w[n-3-k0*s2-r4] - w[n-3-k0*(s2+1)-r4]) * A[r*k1+1]; u[n-3-k0*(s2+1)-r4] = (w[n-3-k0*s2-r4] - w[n-3-k0*(s2+1)-r4]) * A[r*k1] + (w[n-1-k0*s2-r4] - w[n-1-k0*(s2+1)-r4]) * A[r*k1+1]; } } if (l+1 < ld-3) { // paper bug: ping-ponging of u&w here is omitted memcpy(w, u, sizeof(u)); } } // step 4 for (i=0; i < n8; ++i) { int j = bit_reverse(i) >> (32-ld+3); assert(j < n8); if (i == j) { // paper bug: original code probably swapped in place; if copying, // need to directly copy in this case int i8 = i << 3; v[i8+1] = u[i8+1]; v[i8+3] = u[i8+3]; v[i8+5] = u[i8+5]; v[i8+7] = u[i8+7]; } else if (i < j) { int i8 = i << 3, j8 = j << 3; v[j8+1] = u[i8+1], v[i8+1] = u[j8 + 1]; v[j8+3] = u[i8+3], v[i8+3] = u[j8 + 3]; v[j8+5] = u[i8+5], v[i8+5] = u[j8 + 5]; v[j8+7] = u[i8+7], v[i8+7] = u[j8 + 7]; } } // step 5 for (k=0; k < n2; ++k) { w[k] = v[k*2+1]; } // step 6 for (k=k2=k4=0; k < n8; ++k, k2 += 2, k4 += 4) { u[n-1-k2] = w[k4]; u[n-2-k2] = w[k4+1]; u[n3_4 - 1 - k2] = w[k4+2]; u[n3_4 - 2 - k2] = w[k4+3]; } // step 7 for (k=k2=0; k < n8; ++k, k2 += 2) { v[n2 + k2 ] = ( u[n2 + k2] + u[n-2-k2] + C[k2+1]*(u[n2+k2]-u[n-2-k2]) + C[k2]*(u[n2+k2+1]+u[n-2-k2+1]))/2; v[n-2 - k2] = ( u[n2 + k2] + u[n-2-k2] - C[k2+1]*(u[n2+k2]-u[n-2-k2]) - C[k2]*(u[n2+k2+1]+u[n-2-k2+1]))/2; v[n2+1+ k2] = ( u[n2+1+k2] - u[n-1-k2] + C[k2+1]*(u[n2+1+k2]+u[n-1-k2]) - C[k2]*(u[n2+k2]-u[n-2-k2]))/2; v[n-1 - k2] = (-u[n2+1+k2] + u[n-1-k2] + C[k2+1]*(u[n2+1+k2]+u[n-1-k2]) - C[k2]*(u[n2+k2]-u[n-2-k2]))/2; } // step 8 for (k=k2=0; k < n4; ++k,k2 += 2) { X[k] = v[k2+n2]*B[k2 ] + v[k2+1+n2]*B[k2+1]; X[n2-1-k] = v[k2+n2]*B[k2+1] - v[k2+1+n2]*B[k2 ]; } // decode kernel to output // determined the following value experimentally // (by first figuring out what made inverse_mdct_slow work); then matching that here // (probably vorbis encoder premultiplies by n or n/2, to save it on the decoder?) s = 0.5; // theoretically would be n4 // [[[ note! the s value of 0.5 is compensated for by the B[] in the current code, // so it needs to use the "old" B values to behave correctly, or else // set s to 1.0 ]]] for (i=0; i < n4 ; ++i) buffer[i] = s * X[i+n4]; for ( ; i < n3_4; ++i) buffer[i] = -s * X[n3_4 - i - 1]; for ( ; i < n ; ++i) buffer[i] = -s * X[i - n3_4]; } #endif static float *get_window(vorb *f, int len) { len <<= 1; if (len == f->blocksize_0) return f->window[0]; if (len == f->blocksize_1) return f->window[1]; return NULL; } #ifndef STB_VORBIS_NO_DEFER_FLOOR typedef int16 YTYPE; #else typedef int YTYPE; #endif static int do_floor(vorb *f, Mapping *map, int i, int n, float *target, YTYPE *finalY, uint8 *step2_flag) { int n2 = n >> 1; int s = map->chan[i].mux, floor; floor = map->submap_floor[s]; if (f->floor_types[floor] == 0) { return error(f, VORBIS_invalid_stream); } else { Floor1 *g = &f->floor_config[floor].floor1; int j,q; int lx = 0, ly = finalY[0] * g->floor1_multiplier; for (q=1; q < g->values; ++q) { j = g->sorted_order[q]; #ifndef STB_VORBIS_NO_DEFER_FLOOR if (finalY[j] >= 0) #else if (step2_flag[j]) #endif { int hy = finalY[j] * g->floor1_multiplier; int hx = g->Xlist[j]; if (lx != hx) draw_line(target, lx,ly, hx,hy, n2); CHECK(f); lx = hx, ly = hy; } } if (lx < n2) { // optimization of: draw_line(target, lx,ly, n,ly, n2); for (j=lx; j < n2; ++j) LINE_OP(target[j], inverse_db_table[ly]); CHECK(f); } } return TRUE; } // The meaning of "left" and "right" // // For a given frame: // we compute samples from 0..n // window_center is n/2 // we'll window and mix the samples from left_start to left_end with data from the previous frame // all of the samples from left_end to right_start can be output without mixing; however, // this interval is 0-length except when transitioning between short and long frames // all of the samples from right_start to right_end need to be mixed with the next frame, // which we don't have, so those get saved in a buffer // frame N's right_end-right_start, the number of samples to mix with the next frame, // has to be the same as frame N+1's left_end-left_start (which they are by // construction) static int vorbis_decode_initial(vorb *f, int *p_left_start, int *p_left_end, int *p_right_start, int *p_right_end, int *mode) { Mode *m; int i, n, prev, next, window_center; f->channel_buffer_start = f->channel_buffer_end = 0; retry: if (f->eof) return FALSE; if (!maybe_start_packet(f)) return FALSE; // check packet type if (get_bits(f,1) != 0) { if (IS_PUSH_MODE(f)) return error(f,VORBIS_bad_packet_type); while (EOP != get8_packet(f)); goto retry; } if (f->alloc.alloc_buffer) assert(f->alloc.alloc_buffer_length_in_bytes == f->temp_offset); i = get_bits(f, ilog(f->mode_count-1)); if (i == EOP) return FALSE; if (i >= f->mode_count) return FALSE; *mode = i; m = f->mode_config + i; if (m->blockflag) { n = f->blocksize_1; prev = get_bits(f,1); next = get_bits(f,1); } else { prev = next = 0; n = f->blocksize_0; } // WINDOWING window_center = n >> 1; if (m->blockflag && !prev) { *p_left_start = (n - f->blocksize_0) >> 2; *p_left_end = (n + f->blocksize_0) >> 2; } else { *p_left_start = 0; *p_left_end = window_center; } if (m->blockflag && !next) { *p_right_start = (n*3 - f->blocksize_0) >> 2; *p_right_end = (n*3 + f->blocksize_0) >> 2; } else { *p_right_start = window_center; *p_right_end = n; } return TRUE; } static int vorbis_decode_packet_rest(vorb *f, int *len, Mode *m, int left_start, int left_end, int right_start, int right_end, int *p_left) { Mapping *map; int i,j,k,n,n2; int zero_channel[256]; int really_zero_channel[256]; // WINDOWING n = f->blocksize[m->blockflag]; map = &f->mapping[m->mapping]; // FLOORS n2 = n >> 1; CHECK(f); for (i=0; i < f->channels; ++i) { int s = map->chan[i].mux, floor; zero_channel[i] = FALSE; floor = map->submap_floor[s]; if (f->floor_types[floor] == 0) { return error(f, VORBIS_invalid_stream); } else { Floor1 *g = &f->floor_config[floor].floor1; if (get_bits(f, 1)) { short *finalY; uint8 step2_flag[256]; static int range_list[4] = { 256, 128, 86, 64 }; int range = range_list[g->floor1_multiplier-1]; int offset = 2; finalY = f->finalY[i]; finalY[0] = get_bits(f, ilog(range)-1); finalY[1] = get_bits(f, ilog(range)-1); for (j=0; j < g->partitions; ++j) { int pclass = g->partition_class_list[j]; int cdim = g->class_dimensions[pclass]; int cbits = g->class_subclasses[pclass]; int csub = (1 << cbits)-1; int cval = 0; if (cbits) { Codebook *c = f->codebooks + g->class_masterbooks[pclass]; DECODE(cval,f,c); } for (k=0; k < cdim; ++k) { int book = g->subclass_books[pclass][cval & csub]; cval = cval >> cbits; if (book >= 0) { int temp; Codebook *c = f->codebooks + book; DECODE(temp,f,c); finalY[offset++] = temp; } else finalY[offset++] = 0; } } if (f->valid_bits == INVALID_BITS) goto error; // behavior according to spec step2_flag[0] = step2_flag[1] = 1; for (j=2; j < g->values; ++j) { int low, high, pred, highroom, lowroom, room, val; low = g->neighbors[j][0]; high = g->neighbors[j][1]; //neighbors(g->Xlist, j, &low, &high); pred = predict_point(g->Xlist[j], g->Xlist[low], g->Xlist[high], finalY[low], finalY[high]); val = finalY[j]; highroom = range - pred; lowroom = pred; if (highroom < lowroom) room = highroom * 2; else room = lowroom * 2; if (val) { step2_flag[low] = step2_flag[high] = 1; step2_flag[j] = 1; if (val >= room) if (highroom > lowroom) finalY[j] = val - lowroom + pred; else finalY[j] = pred - val + highroom - 1; else if (val & 1) finalY[j] = pred - ((val+1)>>1); else finalY[j] = pred + (val>>1); } else { step2_flag[j] = 0; finalY[j] = pred; } } #ifdef STB_VORBIS_NO_DEFER_FLOOR do_floor(f, map, i, n, f->floor_buffers[i], finalY, step2_flag); #else // defer final floor computation until _after_ residue for (j=0; j < g->values; ++j) { if (!step2_flag[j]) finalY[j] = -1; } #endif } else { error: zero_channel[i] = TRUE; } // So we just defer everything else to later // at this point we've decoded the floor into buffer } } CHECK(f); // at this point we've decoded all floors if (f->alloc.alloc_buffer) assert(f->alloc.alloc_buffer_length_in_bytes == f->temp_offset); // re-enable coupled channels if necessary memcpy(really_zero_channel, zero_channel, sizeof(really_zero_channel[0]) * f->channels); for (i=0; i < map->coupling_steps; ++i) if (!zero_channel[map->chan[i].magnitude] || !zero_channel[map->chan[i].angle]) { zero_channel[map->chan[i].magnitude] = zero_channel[map->chan[i].angle] = FALSE; } CHECK(f); // RESIDUE DECODE for (i=0; i < map->submaps; ++i) { float *residue_buffers[STB_VORBIS_MAX_CHANNELS]; int r; uint8 do_not_decode[256]; int ch = 0; for (j=0; j < f->channels; ++j) { if (map->chan[j].mux == i) { if (zero_channel[j]) { do_not_decode[ch] = TRUE; residue_buffers[ch] = NULL; } else { do_not_decode[ch] = FALSE; residue_buffers[ch] = f->channel_buffers[j]; } ++ch; } } r = map->submap_residue[i]; decode_residue(f, residue_buffers, ch, n2, r, do_not_decode); } if (f->alloc.alloc_buffer) assert(f->alloc.alloc_buffer_length_in_bytes == f->temp_offset); CHECK(f); // INVERSE COUPLING for (i = map->coupling_steps-1; i >= 0; --i) { int n2 = n >> 1; float *m = f->channel_buffers[map->chan[i].magnitude]; float *a = f->channel_buffers[map->chan[i].angle ]; for (j=0; j < n2; ++j) { float a2,m2; if (m[j] > 0) if (a[j] > 0) m2 = m[j], a2 = m[j] - a[j]; else a2 = m[j], m2 = m[j] + a[j]; else if (a[j] > 0) m2 = m[j], a2 = m[j] + a[j]; else a2 = m[j], m2 = m[j] - a[j]; m[j] = m2; a[j] = a2; } } CHECK(f); // finish decoding the floors #ifndef STB_VORBIS_NO_DEFER_FLOOR for (i=0; i < f->channels; ++i) { if (really_zero_channel[i]) { memset(f->channel_buffers[i], 0, sizeof(*f->channel_buffers[i]) * n2); } else { do_floor(f, map, i, n, f->channel_buffers[i], f->finalY[i], NULL); } } #else for (i=0; i < f->channels; ++i) { if (really_zero_channel[i]) { memset(f->channel_buffers[i], 0, sizeof(*f->channel_buffers[i]) * n2); } else { for (j=0; j < n2; ++j) f->channel_buffers[i][j] *= f->floor_buffers[i][j]; } } #endif // INVERSE MDCT CHECK(f); for (i=0; i < f->channels; ++i) inverse_mdct(f->channel_buffers[i], n, f, m->blockflag); CHECK(f); // this shouldn't be necessary, unless we exited on an error // and want to flush to get to the next packet flush_packet(f); if (f->first_decode) { // assume we start so first non-discarded sample is sample 0 // this isn't to spec, but spec would require us to read ahead // and decode the size of all current frames--could be done, // but presumably it's not a commonly used feature f->current_loc = -n2; // start of first frame is positioned for discard // we might have to discard samples "from" the next frame too, // if we're lapping a large block then a small at the start? f->discard_samples_deferred = n - right_end; f->current_loc_valid = TRUE; f->first_decode = FALSE; } else if (f->discard_samples_deferred) { if (f->discard_samples_deferred >= right_start - left_start) { f->discard_samples_deferred -= (right_start - left_start); left_start = right_start; *p_left = left_start; } else { left_start += f->discard_samples_deferred; *p_left = left_start; f->discard_samples_deferred = 0; } } else if (f->previous_length == 0 && f->current_loc_valid) { // we're recovering from a seek... that means we're going to discard // the samples from this packet even though we know our position from // the last page header, so we need to update the position based on // the discarded samples here // but wait, the code below is going to add this in itself even // on a discard, so we don't need to do it here... } // check if we have ogg information about the sample # for this packet if (f->last_seg_which == f->end_seg_with_known_loc) { // if we have a valid current loc, and this is final: if (f->current_loc_valid && (f->page_flag & PAGEFLAG_last_page)) { uint32 current_end = f->known_loc_for_packet; // then let's infer the size of the (probably) short final frame if (current_end < f->current_loc + (right_end-left_start)) { if (current_end < f->current_loc) { // negative truncation, that's impossible! *len = 0; } else { *len = current_end - f->current_loc; } *len += left_start; // this doesn't seem right, but has no ill effect on my test files if (*len > right_end) *len = right_end; // this should never happen f->current_loc += *len; return TRUE; } } // otherwise, just set our sample loc // guess that the ogg granule pos refers to the _middle_ of the // last frame? // set f->current_loc to the position of left_start f->current_loc = f->known_loc_for_packet - (n2-left_start); f->current_loc_valid = TRUE; } if (f->current_loc_valid) f->current_loc += (right_start - left_start); if (f->alloc.alloc_buffer) assert(f->alloc.alloc_buffer_length_in_bytes == f->temp_offset); *len = right_end; // ignore samples after the window goes to 0 CHECK(f); return TRUE; } static int vorbis_decode_packet(vorb *f, int *len, int *p_left, int *p_right) { int mode, left_end, right_end; if (!vorbis_decode_initial(f, p_left, &left_end, p_right, &right_end, &mode)) return 0; return vorbis_decode_packet_rest(f, len, f->mode_config + mode, *p_left, left_end, *p_right, right_end, p_left); } static int vorbis_finish_frame(stb_vorbis *f, int len, int left, int right) { int prev,i,j; // we use right&left (the start of the right- and left-window sin()-regions) // to determine how much to return, rather than inferring from the rules // (same result, clearer code); 'left' indicates where our sin() window // starts, therefore where the previous window's right edge starts, and // therefore where to start mixing from the previous buffer. 'right' // indicates where our sin() ending-window starts, therefore that's where // we start saving, and where our returned-data ends. // mixin from previous window if (f->previous_length) { int i,j, n = f->previous_length; float *w = get_window(f, n); if (w == NULL) return 0; for (i=0; i < f->channels; ++i) { for (j=0; j < n; ++j) f->channel_buffers[i][left+j] = f->channel_buffers[i][left+j]*w[ j] + f->previous_window[i][ j]*w[n-1-j]; } } prev = f->previous_length; // last half of this data becomes previous window f->previous_length = len - right; // @OPTIMIZE: could avoid this copy by double-buffering the // output (flipping previous_window with channel_buffers), but // then previous_window would have to be 2x as large, and // channel_buffers couldn't be temp mem (although they're NOT // currently temp mem, they could be (unless we want to level // performance by spreading out the computation)) for (i=0; i < f->channels; ++i) for (j=0; right+j < len; ++j) f->previous_window[i][j] = f->channel_buffers[i][right+j]; if (!prev) // there was no previous packet, so this data isn't valid... // this isn't entirely true, only the would-have-overlapped data // isn't valid, but this seems to be what the spec requires return 0; // truncate a short frame if (len < right) right = len; f->samples_output += right-left; return right - left; } static int vorbis_pump_first_frame(stb_vorbis *f) { int len, right, left, res; res = vorbis_decode_packet(f, &len, &left, &right); if (res) vorbis_finish_frame(f, len, left, right); return res; } #ifndef STB_VORBIS_NO_PUSHDATA_API static int is_whole_packet_present(stb_vorbis *f) { // make sure that we have the packet available before continuing... // this requires a full ogg parse, but we know we can fetch from f->stream // instead of coding this out explicitly, we could save the current read state, // read the next packet with get8() until end-of-packet, check f->eof, then // reset the state? but that would be slower, esp. since we'd have over 256 bytes // of state to restore (primarily the page segment table) int s = f->next_seg, first = TRUE; uint8 *p = f->stream; if (s != -1) { // if we're not starting the packet with a 'continue on next page' flag for (; s < f->segment_count; ++s) { p += f->segments[s]; if (f->segments[s] < 255) // stop at first short segment break; } // either this continues, or it ends it... if (s == f->segment_count) s = -1; // set 'crosses page' flag if (p > f->stream_end) return error(f, VORBIS_need_more_data); first = FALSE; } for (; s == -1;) { uint8 *q; int n; // check that we have the page header ready if (p + 26 >= f->stream_end) return error(f, VORBIS_need_more_data); // validate the page if (memcmp(p, ogg_page_header, 4)) return error(f, VORBIS_invalid_stream); if (p[4] != 0) return error(f, VORBIS_invalid_stream); if (first) { // the first segment must NOT have 'continued_packet', later ones MUST if (f->previous_length) if ((p[5] & PAGEFLAG_continued_packet)) return error(f, VORBIS_invalid_stream); // if no previous length, we're resynching, so we can come in on a continued-packet, // which we'll just drop } else { if (!(p[5] & PAGEFLAG_continued_packet)) return error(f, VORBIS_invalid_stream); } n = p[26]; // segment counts q = p+27; // q points to segment table p = q + n; // advance past header // make sure we've read the segment table if (p > f->stream_end) return error(f, VORBIS_need_more_data); for (s=0; s < n; ++s) { p += q[s]; if (q[s] < 255) break; } if (s == n) s = -1; // set 'crosses page' flag if (p > f->stream_end) return error(f, VORBIS_need_more_data); first = FALSE; } return TRUE; } #endif // !STB_VORBIS_NO_PUSHDATA_API static int start_decoder(vorb *f) { uint8 header[6], x,y; int len,i,j,k, max_submaps = 0; int longest_floorlist=0; // first page, first packet f->first_decode = TRUE; if (!start_page(f)) return FALSE; // validate page flag if (!(f->page_flag & PAGEFLAG_first_page)) return error(f, VORBIS_invalid_first_page); if (f->page_flag & PAGEFLAG_last_page) return error(f, VORBIS_invalid_first_page); if (f->page_flag & PAGEFLAG_continued_packet) return error(f, VORBIS_invalid_first_page); // check for expected packet length if (f->segment_count != 1) return error(f, VORBIS_invalid_first_page); if (f->segments[0] != 30) { // check for the Ogg skeleton fishead identifying header to refine our error if (f->segments[0] == 64 && getn(f, header, 6) && header[0] == 'f' && header[1] == 'i' && header[2] == 's' && header[3] == 'h' && header[4] == 'e' && header[5] == 'a' && get8(f) == 'd' && get8(f) == '\0') return error(f, VORBIS_ogg_skeleton_not_supported); else return error(f, VORBIS_invalid_first_page); } // read packet // check packet header if (get8(f) != VORBIS_packet_id) return error(f, VORBIS_invalid_first_page); if (!getn(f, header, 6)) return error(f, VORBIS_unexpected_eof); if (!vorbis_validate(header)) return error(f, VORBIS_invalid_first_page); // vorbis_version if (get32(f) != 0) return error(f, VORBIS_invalid_first_page); f->channels = get8(f); if (!f->channels) return error(f, VORBIS_invalid_first_page); if (f->channels > STB_VORBIS_MAX_CHANNELS) return error(f, VORBIS_too_many_channels); f->sample_rate = get32(f); if (!f->sample_rate) return error(f, VORBIS_invalid_first_page); get32(f); // bitrate_maximum get32(f); // bitrate_nominal get32(f); // bitrate_minimum x = get8(f); { int log0,log1; log0 = x & 15; log1 = x >> 4; f->blocksize_0 = 1 << log0; f->blocksize_1 = 1 << log1; if (log0 < 6 || log0 > 13) return error(f, VORBIS_invalid_setup); if (log1 < 6 || log1 > 13) return error(f, VORBIS_invalid_setup); if (log0 > log1) return error(f, VORBIS_invalid_setup); } // framing_flag x = get8(f); if (!(x & 1)) return error(f, VORBIS_invalid_first_page); // second packet! if (!start_page(f)) return FALSE; if (!start_packet(f)) return FALSE; if (!next_segment(f)) return FALSE; if (get8_packet(f) != VORBIS_packet_comment) return error(f, VORBIS_invalid_setup); for (i=0; i < 6; ++i) header[i] = get8_packet(f); if (!vorbis_validate(header)) return error(f, VORBIS_invalid_setup); //file vendor len = get32_packet(f); f->vendor = (char*)setup_malloc(f, sizeof(char) * (len+1)); for(i=0; i < len; ++i) { f->vendor[i] = get8_packet(f); } f->vendor[len] = (char)'\0'; //user comments f->comment_list_length = get32_packet(f); f->comment_list = (char**)setup_malloc(f, sizeof(char*) * (f->comment_list_length)); for(i=0; i < f->comment_list_length; ++i) { len = get32_packet(f); f->comment_list[i] = (char*)setup_malloc(f, sizeof(char) * (len+1)); for(j=0; j < len; ++j) { f->comment_list[i][j] = get8_packet(f); } f->comment_list[i][len] = (char)'\0'; } // framing_flag x = get8_packet(f); if (!(x & 1)) return error(f, VORBIS_invalid_setup); skip(f, f->bytes_in_seg); f->bytes_in_seg = 0; do { len = next_segment(f); skip(f, len); f->bytes_in_seg = 0; } while (len); // third packet! if (!start_packet(f)) return FALSE; #ifndef STB_VORBIS_NO_PUSHDATA_API if (IS_PUSH_MODE(f)) { if (!is_whole_packet_present(f)) { // convert error in ogg header to write type if (f->error == VORBIS_invalid_stream) f->error = VORBIS_invalid_setup; return FALSE; } } #endif crc32_init(); // always init it, to avoid multithread race conditions if (get8_packet(f) != VORBIS_packet_setup) return error(f, VORBIS_invalid_setup); for (i=0; i < 6; ++i) header[i] = get8_packet(f); if (!vorbis_validate(header)) return error(f, VORBIS_invalid_setup); // codebooks f->codebook_count = get_bits(f,8) + 1; f->codebooks = (Codebook *) setup_malloc(f, sizeof(*f->codebooks) * f->codebook_count); if (f->codebooks == NULL) return error(f, VORBIS_outofmem); memset(f->codebooks, 0, sizeof(*f->codebooks) * f->codebook_count); for (i=0; i < f->codebook_count; ++i) { uint32 *values; int ordered, sorted_count; int total=0; uint8 *lengths; Codebook *c = f->codebooks+i; CHECK(f); x = get_bits(f, 8); if (x != 0x42) return error(f, VORBIS_invalid_setup); x = get_bits(f, 8); if (x != 0x43) return error(f, VORBIS_invalid_setup); x = get_bits(f, 8); if (x != 0x56) return error(f, VORBIS_invalid_setup); x = get_bits(f, 8); c->dimensions = (get_bits(f, 8)<<8) + x; x = get_bits(f, 8); y = get_bits(f, 8); c->entries = (get_bits(f, 8)<<16) + (y<<8) + x; ordered = get_bits(f,1); c->sparse = ordered ? 0 : get_bits(f,1); if (c->dimensions == 0 && c->entries != 0) return error(f, VORBIS_invalid_setup); if (c->sparse) lengths = (uint8 *) setup_temp_malloc(f, c->entries); else lengths = c->codeword_lengths = (uint8 *) setup_malloc(f, c->entries); if (!lengths) return error(f, VORBIS_outofmem); if (ordered) { int current_entry = 0; int current_length = get_bits(f,5) + 1; while (current_entry < c->entries) { int limit = c->entries - current_entry; int n = get_bits(f, ilog(limit)); if (current_length >= 32) return error(f, VORBIS_invalid_setup); if (current_entry + n > (int) c->entries) { return error(f, VORBIS_invalid_setup); } memset(lengths + current_entry, current_length, n); current_entry += n; ++current_length; } } else { for (j=0; j < c->entries; ++j) { int present = c->sparse ? get_bits(f,1) : 1; if (present) { lengths[j] = get_bits(f, 5) + 1; ++total; if (lengths[j] == 32) return error(f, VORBIS_invalid_setup); } else { lengths[j] = NO_CODE; } } } if (c->sparse && total >= c->entries >> 2) { // convert sparse items to non-sparse! if (c->entries > (int) f->setup_temp_memory_required) f->setup_temp_memory_required = c->entries; c->codeword_lengths = (uint8 *) setup_malloc(f, c->entries); if (c->codeword_lengths == NULL) return error(f, VORBIS_outofmem); memcpy(c->codeword_lengths, lengths, c->entries); setup_temp_free(f, lengths, c->entries); // note this is only safe if there have been no intervening temp mallocs! lengths = c->codeword_lengths; c->sparse = 0; } // compute the size of the sorted tables if (c->sparse) { sorted_count = total; } else { sorted_count = 0; #ifndef STB_VORBIS_NO_HUFFMAN_BINARY_SEARCH for (j=0; j < c->entries; ++j) if (lengths[j] > STB_VORBIS_FAST_HUFFMAN_LENGTH && lengths[j] != NO_CODE) ++sorted_count; #endif } c->sorted_entries = sorted_count; values = NULL; CHECK(f); if (!c->sparse) { c->codewords = (uint32 *) setup_malloc(f, sizeof(c->codewords[0]) * c->entries); if (!c->codewords) return error(f, VORBIS_outofmem); } else { unsigned int size; if (c->sorted_entries) { c->codeword_lengths = (uint8 *) setup_malloc(f, c->sorted_entries); if (!c->codeword_lengths) return error(f, VORBIS_outofmem); c->codewords = (uint32 *) setup_temp_malloc(f, sizeof(*c->codewords) * c->sorted_entries); if (!c->codewords) return error(f, VORBIS_outofmem); values = (uint32 *) setup_temp_malloc(f, sizeof(*values) * c->sorted_entries); if (!values) return error(f, VORBIS_outofmem); } size = c->entries + (sizeof(*c->codewords) + sizeof(*values)) * c->sorted_entries; if (size > f->setup_temp_memory_required) f->setup_temp_memory_required = size; } if (!compute_codewords(c, lengths, c->entries, values)) { if (c->sparse) setup_temp_free(f, values, 0); return error(f, VORBIS_invalid_setup); } if (c->sorted_entries) { // allocate an extra slot for sentinels c->sorted_codewords = (uint32 *) setup_malloc(f, sizeof(*c->sorted_codewords) * (c->sorted_entries+1)); if (c->sorted_codewords == NULL) return error(f, VORBIS_outofmem); // allocate an extra slot at the front so that c->sorted_values[-1] is defined // so that we can catch that case without an extra if c->sorted_values = ( int *) setup_malloc(f, sizeof(*c->sorted_values ) * (c->sorted_entries+1)); if (c->sorted_values == NULL) return error(f, VORBIS_outofmem); ++c->sorted_values; c->sorted_values[-1] = -1; compute_sorted_huffman(c, lengths, values); } if (c->sparse) { setup_temp_free(f, values, sizeof(*values)*c->sorted_entries); setup_temp_free(f, c->codewords, sizeof(*c->codewords)*c->sorted_entries); setup_temp_free(f, lengths, c->entries); c->codewords = NULL; } compute_accelerated_huffman(c); CHECK(f); c->lookup_type = get_bits(f, 4); if (c->lookup_type > 2) return error(f, VORBIS_invalid_setup); if (c->lookup_type > 0) { uint16 *mults; c->minimum_value = float32_unpack(get_bits(f, 32)); c->delta_value = float32_unpack(get_bits(f, 32)); c->value_bits = get_bits(f, 4)+1; c->sequence_p = get_bits(f,1); if (c->lookup_type == 1) { int values = lookup1_values(c->entries, c->dimensions); if (values < 0) return error(f, VORBIS_invalid_setup); c->lookup_values = (uint32) values; } else { c->lookup_values = c->entries * c->dimensions; } if (c->lookup_values == 0) return error(f, VORBIS_invalid_setup); mults = (uint16 *) setup_temp_malloc(f, sizeof(mults[0]) * c->lookup_values); if (mults == NULL) return error(f, VORBIS_outofmem); for (j=0; j < (int) c->lookup_values; ++j) { int q = get_bits(f, c->value_bits); if (q == EOP) { setup_temp_free(f,mults,sizeof(mults[0])*c->lookup_values); return error(f, VORBIS_invalid_setup); } mults[j] = q; } #ifndef STB_VORBIS_DIVIDES_IN_CODEBOOK if (c->lookup_type == 1) { int len, sparse = c->sparse; float last=0; // pre-expand the lookup1-style multiplicands, to avoid a divide in the inner loop if (sparse) { if (c->sorted_entries == 0) goto skip; c->multiplicands = (codetype *) setup_malloc(f, sizeof(c->multiplicands[0]) * c->sorted_entries * c->dimensions); } else c->multiplicands = (codetype *) setup_malloc(f, sizeof(c->multiplicands[0]) * c->entries * c->dimensions); if (c->multiplicands == NULL) { setup_temp_free(f,mults,sizeof(mults[0])*c->lookup_values); return error(f, VORBIS_outofmem); } len = sparse ? c->sorted_entries : c->entries; for (j=0; j < len; ++j) { unsigned int z = sparse ? c->sorted_values[j] : j; unsigned int div=1; for (k=0; k < c->dimensions; ++k) { int off = (z / div) % c->lookup_values; float val = mults[off]; val = mults[off]*c->delta_value + c->minimum_value + last; c->multiplicands[j*c->dimensions + k] = val; if (c->sequence_p) last = val; if (k+1 < c->dimensions) { if (div > UINT_MAX / (unsigned int) c->lookup_values) { setup_temp_free(f, mults,sizeof(mults[0])*c->lookup_values); return error(f, VORBIS_invalid_setup); } div *= c->lookup_values; } } } c->lookup_type = 2; } else #endif { float last=0; CHECK(f); c->multiplicands = (codetype *) setup_malloc(f, sizeof(c->multiplicands[0]) * c->lookup_values); if (c->multiplicands == NULL) { setup_temp_free(f, mults,sizeof(mults[0])*c->lookup_values); return error(f, VORBIS_outofmem); } for (j=0; j < (int) c->lookup_values; ++j) { float val = mults[j] * c->delta_value + c->minimum_value + last; c->multiplicands[j] = val; if (c->sequence_p) last = val; } } #ifndef STB_VORBIS_DIVIDES_IN_CODEBOOK skip:; #endif setup_temp_free(f, mults, sizeof(mults[0])*c->lookup_values); CHECK(f); } CHECK(f); } // time domain transfers (notused) x = get_bits(f, 6) + 1; for (i=0; i < x; ++i) { uint32 z = get_bits(f, 16); if (z != 0) return error(f, VORBIS_invalid_setup); } // Floors f->floor_count = get_bits(f, 6)+1; f->floor_config = (Floor *) setup_malloc(f, f->floor_count * sizeof(*f->floor_config)); if (f->floor_config == NULL) return error(f, VORBIS_outofmem); for (i=0; i < f->floor_count; ++i) { f->floor_types[i] = get_bits(f, 16); if (f->floor_types[i] > 1) return error(f, VORBIS_invalid_setup); if (f->floor_types[i] == 0) { Floor0 *g = &f->floor_config[i].floor0; g->order = get_bits(f,8); g->rate = get_bits(f,16); g->bark_map_size = get_bits(f,16); g->amplitude_bits = get_bits(f,6); g->amplitude_offset = get_bits(f,8); g->number_of_books = get_bits(f,4) + 1; for (j=0; j < g->number_of_books; ++j) g->book_list[j] = get_bits(f,8); return error(f, VORBIS_feature_not_supported); } else { stbv__floor_ordering p[31*8+2]; Floor1 *g = &f->floor_config[i].floor1; int max_class = -1; g->partitions = get_bits(f, 5); for (j=0; j < g->partitions; ++j) { g->partition_class_list[j] = get_bits(f, 4); if (g->partition_class_list[j] > max_class) max_class = g->partition_class_list[j]; } for (j=0; j <= max_class; ++j) { g->class_dimensions[j] = get_bits(f, 3)+1; g->class_subclasses[j] = get_bits(f, 2); if (g->class_subclasses[j]) { g->class_masterbooks[j] = get_bits(f, 8); if (g->class_masterbooks[j] >= f->codebook_count) return error(f, VORBIS_invalid_setup); } for (k=0; k < 1 << g->class_subclasses[j]; ++k) { g->subclass_books[j][k] = get_bits(f,8)-1; if (g->subclass_books[j][k] >= f->codebook_count) return error(f, VORBIS_invalid_setup); } } g->floor1_multiplier = get_bits(f,2)+1; g->rangebits = get_bits(f,4); g->Xlist[0] = 0; g->Xlist[1] = 1 << g->rangebits; g->values = 2; for (j=0; j < g->partitions; ++j) { int c = g->partition_class_list[j]; for (k=0; k < g->class_dimensions[c]; ++k) { g->Xlist[g->values] = get_bits(f, g->rangebits); ++g->values; } } // precompute the sorting for (j=0; j < g->values; ++j) { p[j].x = g->Xlist[j]; p[j].id = j; } qsort(p, g->values, sizeof(p[0]), point_compare); for (j=0; j < g->values-1; ++j) if (p[j].x == p[j+1].x) return error(f, VORBIS_invalid_setup); for (j=0; j < g->values; ++j) g->sorted_order[j] = (uint8) p[j].id; // precompute the neighbors for (j=2; j < g->values; ++j) { int low = 0,hi = 0; neighbors(g->Xlist, j, &low,&hi); g->neighbors[j][0] = low; g->neighbors[j][1] = hi; } if (g->values > longest_floorlist) longest_floorlist = g->values; } } // Residue f->residue_count = get_bits(f, 6)+1; f->residue_config = (Residue *) setup_malloc(f, f->residue_count * sizeof(f->residue_config[0])); if (f->residue_config == NULL) return error(f, VORBIS_outofmem); memset(f->residue_config, 0, f->residue_count * sizeof(f->residue_config[0])); for (i=0; i < f->residue_count; ++i) { uint8 residue_cascade[64]; Residue *r = f->residue_config+i; f->residue_types[i] = get_bits(f, 16); if (f->residue_types[i] > 2) return error(f, VORBIS_invalid_setup); r->begin = get_bits(f, 24); r->end = get_bits(f, 24); if (r->end < r->begin) return error(f, VORBIS_invalid_setup); r->part_size = get_bits(f,24)+1; r->classifications = get_bits(f,6)+1; r->classbook = get_bits(f,8); if (r->classbook >= f->codebook_count) return error(f, VORBIS_invalid_setup); for (j=0; j < r->classifications; ++j) { uint8 high_bits=0; uint8 low_bits=get_bits(f,3); if (get_bits(f,1)) high_bits = get_bits(f,5); residue_cascade[j] = high_bits*8 + low_bits; } r->residue_books = (short (*)[8]) setup_malloc(f, sizeof(r->residue_books[0]) * r->classifications); if (r->residue_books == NULL) return error(f, VORBIS_outofmem); for (j=0; j < r->classifications; ++j) { for (k=0; k < 8; ++k) { if (residue_cascade[j] & (1 << k)) { r->residue_books[j][k] = get_bits(f, 8); if (r->residue_books[j][k] >= f->codebook_count) return error(f, VORBIS_invalid_setup); } else { r->residue_books[j][k] = -1; } } } // precompute the classifications[] array to avoid inner-loop mod/divide // call it 'classdata' since we already have r->classifications r->classdata = (uint8 **) setup_malloc(f, sizeof(*r->classdata) * f->codebooks[r->classbook].entries); if (!r->classdata) return error(f, VORBIS_outofmem); memset(r->classdata, 0, sizeof(*r->classdata) * f->codebooks[r->classbook].entries); for (j=0; j < f->codebooks[r->classbook].entries; ++j) { int classwords = f->codebooks[r->classbook].dimensions; int temp = j; r->classdata[j] = (uint8 *) setup_malloc(f, sizeof(r->classdata[j][0]) * classwords); if (r->classdata[j] == NULL) return error(f, VORBIS_outofmem); for (k=classwords-1; k >= 0; --k) { r->classdata[j][k] = temp % r->classifications; temp /= r->classifications; } } } f->mapping_count = get_bits(f,6)+1; f->mapping = (Mapping *) setup_malloc(f, f->mapping_count * sizeof(*f->mapping)); if (f->mapping == NULL) return error(f, VORBIS_outofmem); memset(f->mapping, 0, f->mapping_count * sizeof(*f->mapping)); for (i=0; i < f->mapping_count; ++i) { Mapping *m = f->mapping + i; int mapping_type = get_bits(f,16); if (mapping_type != 0) return error(f, VORBIS_invalid_setup); m->chan = (MappingChannel *) setup_malloc(f, f->channels * sizeof(*m->chan)); if (m->chan == NULL) return error(f, VORBIS_outofmem); if (get_bits(f,1)) m->submaps = get_bits(f,4)+1; else m->submaps = 1; if (m->submaps > max_submaps) max_submaps = m->submaps; if (get_bits(f,1)) { m->coupling_steps = get_bits(f,8)+1; if (m->coupling_steps > f->channels) return error(f, VORBIS_invalid_setup); for (k=0; k < m->coupling_steps; ++k) { m->chan[k].magnitude = get_bits(f, ilog(f->channels-1)); m->chan[k].angle = get_bits(f, ilog(f->channels-1)); if (m->chan[k].magnitude >= f->channels) return error(f, VORBIS_invalid_setup); if (m->chan[k].angle >= f->channels) return error(f, VORBIS_invalid_setup); if (m->chan[k].magnitude == m->chan[k].angle) return error(f, VORBIS_invalid_setup); } } else m->coupling_steps = 0; // reserved field if (get_bits(f,2)) return error(f, VORBIS_invalid_setup); if (m->submaps > 1) { for (j=0; j < f->channels; ++j) { m->chan[j].mux = get_bits(f, 4); if (m->chan[j].mux >= m->submaps) return error(f, VORBIS_invalid_setup); } } else // @SPECIFICATION: this case is missing from the spec for (j=0; j < f->channels; ++j) m->chan[j].mux = 0; for (j=0; j < m->submaps; ++j) { get_bits(f,8); // discard m->submap_floor[j] = get_bits(f,8); m->submap_residue[j] = get_bits(f,8); if (m->submap_floor[j] >= f->floor_count) return error(f, VORBIS_invalid_setup); if (m->submap_residue[j] >= f->residue_count) return error(f, VORBIS_invalid_setup); } } // Modes f->mode_count = get_bits(f, 6)+1; for (i=0; i < f->mode_count; ++i) { Mode *m = f->mode_config+i; m->blockflag = get_bits(f,1); m->windowtype = get_bits(f,16); m->transformtype = get_bits(f,16); m->mapping = get_bits(f,8); if (m->windowtype != 0) return error(f, VORBIS_invalid_setup); if (m->transformtype != 0) return error(f, VORBIS_invalid_setup); if (m->mapping >= f->mapping_count) return error(f, VORBIS_invalid_setup); } flush_packet(f); f->previous_length = 0; for (i=0; i < f->channels; ++i) { f->channel_buffers[i] = (float *) setup_malloc(f, sizeof(float) * f->blocksize_1); f->previous_window[i] = (float *) setup_malloc(f, sizeof(float) * f->blocksize_1/2); f->finalY[i] = (int16 *) setup_malloc(f, sizeof(int16) * longest_floorlist); if (f->channel_buffers[i] == NULL || f->previous_window[i] == NULL || f->finalY[i] == NULL) return error(f, VORBIS_outofmem); memset(f->channel_buffers[i], 0, sizeof(float) * f->blocksize_1); #ifdef STB_VORBIS_NO_DEFER_FLOOR f->floor_buffers[i] = (float *) setup_malloc(f, sizeof(float) * f->blocksize_1/2); if (f->floor_buffers[i] == NULL) return error(f, VORBIS_outofmem); #endif } if (!init_blocksize(f, 0, f->blocksize_0)) return FALSE; if (!init_blocksize(f, 1, f->blocksize_1)) return FALSE; f->blocksize[0] = f->blocksize_0; f->blocksize[1] = f->blocksize_1; #ifdef STB_VORBIS_DIVIDE_TABLE if (integer_divide_table[1][1]==0) for (i=0; i < DIVTAB_NUMER; ++i) for (j=1; j < DIVTAB_DENOM; ++j) integer_divide_table[i][j] = i / j; #endif // compute how much temporary memory is needed // 1. { uint32 imdct_mem = (f->blocksize_1 * sizeof(float) >> 1); uint32 classify_mem; int i,max_part_read=0; for (i=0; i < f->residue_count; ++i) { Residue *r = f->residue_config + i; unsigned int actual_size = f->blocksize_1 / 2; unsigned int limit_r_begin = r->begin < actual_size ? r->begin : actual_size; unsigned int limit_r_end = r->end < actual_size ? r->end : actual_size; int n_read = limit_r_end - limit_r_begin; int part_read = n_read / r->part_size; if (part_read > max_part_read) max_part_read = part_read; } #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE classify_mem = f->channels * (sizeof(void*) + max_part_read * sizeof(uint8 *)); #else classify_mem = f->channels * (sizeof(void*) + max_part_read * sizeof(int *)); #endif // maximum reasonable partition size is f->blocksize_1 f->temp_memory_required = classify_mem; if (imdct_mem > f->temp_memory_required) f->temp_memory_required = imdct_mem; } if (f->alloc.alloc_buffer) { assert(f->temp_offset == f->alloc.alloc_buffer_length_in_bytes); // check if there's enough temp memory so we don't error later if (f->setup_offset + sizeof(*f) + f->temp_memory_required > (unsigned) f->temp_offset) return error(f, VORBIS_outofmem); } // @TODO: stb_vorbis_seek_start expects first_audio_page_offset to point to a page // without PAGEFLAG_continued_packet, so this either points to the first page, or // the page after the end of the headers. It might be cleaner to point to a page // in the middle of the headers, when that's the page where the first audio packet // starts, but we'd have to also correctly skip the end of any continued packet in // stb_vorbis_seek_start. if (f->next_seg == -1) { f->first_audio_page_offset = stb_vorbis_get_file_offset(f); } else { f->first_audio_page_offset = 0; } return TRUE; } static void vorbis_deinit(stb_vorbis *p) { int i,j; setup_free(p, p->vendor); for (i=0; i < p->comment_list_length; ++i) { setup_free(p, p->comment_list[i]); } setup_free(p, p->comment_list); if (p->residue_config) { for (i=0; i < p->residue_count; ++i) { Residue *r = p->residue_config+i; if (r->classdata) { for (j=0; j < p->codebooks[r->classbook].entries; ++j) setup_free(p, r->classdata[j]); setup_free(p, r->classdata); } setup_free(p, r->residue_books); } } if (p->codebooks) { CHECK(p); for (i=0; i < p->codebook_count; ++i) { Codebook *c = p->codebooks + i; setup_free(p, c->codeword_lengths); setup_free(p, c->multiplicands); setup_free(p, c->codewords); setup_free(p, c->sorted_codewords); // c->sorted_values[-1] is the first entry in the array setup_free(p, c->sorted_values ? c->sorted_values-1 : NULL); } setup_free(p, p->codebooks); } setup_free(p, p->floor_config); setup_free(p, p->residue_config); if (p->mapping) { for (i=0; i < p->mapping_count; ++i) setup_free(p, p->mapping[i].chan); setup_free(p, p->mapping); } CHECK(p); for (i=0; i < p->channels && i < STB_VORBIS_MAX_CHANNELS; ++i) { setup_free(p, p->channel_buffers[i]); setup_free(p, p->previous_window[i]); #ifdef STB_VORBIS_NO_DEFER_FLOOR setup_free(p, p->floor_buffers[i]); #endif setup_free(p, p->finalY[i]); } for (i=0; i < 2; ++i) { setup_free(p, p->A[i]); setup_free(p, p->B[i]); setup_free(p, p->C[i]); setup_free(p, p->window[i]); setup_free(p, p->bit_reverse[i]); } #ifndef STB_VORBIS_NO_STDIO if (p->close_on_free) fclose(p->f); #endif } void stb_vorbis_close(stb_vorbis *p) { if (p == NULL) return; vorbis_deinit(p); setup_free(p,p); } static void vorbis_init(stb_vorbis *p, const stb_vorbis_alloc *z) { memset(p, 0, sizeof(*p)); // NULL out all malloc'd pointers to start if (z) { p->alloc = *z; p->alloc.alloc_buffer_length_in_bytes = (p->alloc.alloc_buffer_length_in_bytes+3) & ~3; p->temp_offset = p->alloc.alloc_buffer_length_in_bytes; } p->eof = 0; p->error = VORBIS__no_error; p->stream = NULL; p->codebooks = NULL; p->page_crc_tests = -1; #ifndef STB_VORBIS_NO_STDIO p->close_on_free = FALSE; p->f = NULL; #endif } int stb_vorbis_get_sample_offset(stb_vorbis *f) { if (f->current_loc_valid) return f->current_loc; else return -1; } stb_vorbis_info stb_vorbis_get_info(stb_vorbis *f) { stb_vorbis_info d; d.channels = f->channels; d.sample_rate = f->sample_rate; d.setup_memory_required = f->setup_memory_required; d.setup_temp_memory_required = f->setup_temp_memory_required; d.temp_memory_required = f->temp_memory_required; d.max_frame_size = f->blocksize_1 >> 1; return d; } stb_vorbis_comment stb_vorbis_get_comment(stb_vorbis *f) { stb_vorbis_comment d; d.vendor = f->vendor; d.comment_list_length = f->comment_list_length; d.comment_list = f->comment_list; return d; } int stb_vorbis_get_error(stb_vorbis *f) { int e = f->error; f->error = VORBIS__no_error; return e; } static stb_vorbis * vorbis_alloc(stb_vorbis *f) { stb_vorbis *p = (stb_vorbis *) setup_malloc(f, sizeof(*p)); return p; } #ifndef STB_VORBIS_NO_PUSHDATA_API void stb_vorbis_flush_pushdata(stb_vorbis *f) { f->previous_length = 0; f->page_crc_tests = 0; f->discard_samples_deferred = 0; f->current_loc_valid = FALSE; f->first_decode = FALSE; f->samples_output = 0; f->channel_buffer_start = 0; f->channel_buffer_end = 0; } static int vorbis_search_for_page_pushdata(vorb *f, uint8 *data, int data_len) { int i,n; for (i=0; i < f->page_crc_tests; ++i) f->scan[i].bytes_done = 0; // if we have room for more scans, search for them first, because // they may cause us to stop early if their header is incomplete if (f->page_crc_tests < STB_VORBIS_PUSHDATA_CRC_COUNT) { if (data_len < 4) return 0; data_len -= 3; // need to look for 4-byte sequence, so don't miss // one that straddles a boundary for (i=0; i < data_len; ++i) { if (data[i] == 0x4f) { if (0==memcmp(data+i, ogg_page_header, 4)) { int j,len; uint32 crc; // make sure we have the whole page header if (i+26 >= data_len || i+27+data[i+26] >= data_len) { // only read up to this page start, so hopefully we'll // have the whole page header start next time data_len = i; break; } // ok, we have it all; compute the length of the page len = 27 + data[i+26]; for (j=0; j < data[i+26]; ++j) len += data[i+27+j]; // scan everything up to the embedded crc (which we must 0) crc = 0; for (j=0; j < 22; ++j) crc = crc32_update(crc, data[i+j]); // now process 4 0-bytes for ( ; j < 26; ++j) crc = crc32_update(crc, 0); // len is the total number of bytes we need to scan n = f->page_crc_tests++; f->scan[n].bytes_left = len-j; f->scan[n].crc_so_far = crc; f->scan[n].goal_crc = data[i+22] + (data[i+23] << 8) + (data[i+24]<<16) + (data[i+25]<<24); // if the last frame on a page is continued to the next, then // we can't recover the sample_loc immediately if (data[i+27+data[i+26]-1] == 255) f->scan[n].sample_loc = ~0; else f->scan[n].sample_loc = data[i+6] + (data[i+7] << 8) + (data[i+ 8]<<16) + (data[i+ 9]<<24); f->scan[n].bytes_done = i+j; if (f->page_crc_tests == STB_VORBIS_PUSHDATA_CRC_COUNT) break; // keep going if we still have room for more } } } } for (i=0; i < f->page_crc_tests;) { uint32 crc; int j; int n = f->scan[i].bytes_done; int m = f->scan[i].bytes_left; if (m > data_len - n) m = data_len - n; // m is the bytes to scan in the current chunk crc = f->scan[i].crc_so_far; for (j=0; j < m; ++j) crc = crc32_update(crc, data[n+j]); f->scan[i].bytes_left -= m; f->scan[i].crc_so_far = crc; if (f->scan[i].bytes_left == 0) { // does it match? if (f->scan[i].crc_so_far == f->scan[i].goal_crc) { // Houston, we have page data_len = n+m; // consumption amount is wherever that scan ended f->page_crc_tests = -1; // drop out of page scan mode f->previous_length = 0; // decode-but-don't-output one frame f->next_seg = -1; // start a new page f->current_loc = f->scan[i].sample_loc; // set the current sample location // to the amount we'd have decoded had we decoded this page f->current_loc_valid = f->current_loc != ~0U; return data_len; } // delete entry f->scan[i] = f->scan[--f->page_crc_tests]; } else { ++i; } } return data_len; } // return value: number of bytes we used int stb_vorbis_decode_frame_pushdata( stb_vorbis *f, // the file we're decoding const uint8 *data, int data_len, // the memory available for decoding int *channels, // place to write number of float * buffers float ***output, // place to write float ** array of float * buffers int *samples // place to write number of output samples ) { int i; int len,right,left; if (!IS_PUSH_MODE(f)) return error(f, VORBIS_invalid_api_mixing); if (f->page_crc_tests >= 0) { *samples = 0; return vorbis_search_for_page_pushdata(f, (uint8 *) data, data_len); } f->stream = (uint8 *) data; f->stream_end = (uint8 *) data + data_len; f->error = VORBIS__no_error; // check that we have the entire packet in memory if (!is_whole_packet_present(f)) { *samples = 0; return 0; } if (!vorbis_decode_packet(f, &len, &left, &right)) { // save the actual error we encountered enum STBVorbisError error = f->error; if (error == VORBIS_bad_packet_type) { // flush and resynch f->error = VORBIS__no_error; while (get8_packet(f) != EOP) if (f->eof) break; *samples = 0; return (int) (f->stream - data); } if (error == VORBIS_continued_packet_flag_invalid) { if (f->previous_length == 0) { // we may be resynching, in which case it's ok to hit one // of these; just discard the packet f->error = VORBIS__no_error; while (get8_packet(f) != EOP) if (f->eof) break; *samples = 0; return (int) (f->stream - data); } } // if we get an error while parsing, what to do? // well, it DEFINITELY won't work to continue from where we are! stb_vorbis_flush_pushdata(f); // restore the error that actually made us bail f->error = error; *samples = 0; return 1; } // success! len = vorbis_finish_frame(f, len, left, right); for (i=0; i < f->channels; ++i) f->outputs[i] = f->channel_buffers[i] + left; if (channels) *channels = f->channels; *samples = len; *output = f->outputs; return (int) (f->stream - data); } stb_vorbis *stb_vorbis_open_pushdata( const unsigned char *data, int data_len, // the memory available for decoding int *data_used, // only defined if result is not NULL int *error, const stb_vorbis_alloc *alloc) { stb_vorbis *f, p; vorbis_init(&p, alloc); p.stream = (uint8 *) data; p.stream_end = (uint8 *) data + data_len; p.push_mode = TRUE; if (!start_decoder(&p)) { if (p.eof) *error = VORBIS_need_more_data; else *error = p.error; return NULL; } f = vorbis_alloc(&p); if (f) { *f = p; *data_used = (int) (f->stream - data); *error = 0; return f; } else { vorbis_deinit(&p); return NULL; } } #endif // STB_VORBIS_NO_PUSHDATA_API unsigned int stb_vorbis_get_file_offset(stb_vorbis *f) { #ifndef STB_VORBIS_NO_PUSHDATA_API if (f->push_mode) return 0; #endif if (USE_MEMORY(f)) return (unsigned int) (f->stream - f->stream_start); #ifndef STB_VORBIS_NO_STDIO return (unsigned int) (ftell(f->f) - f->f_start); #endif } #ifndef STB_VORBIS_NO_PULLDATA_API // // DATA-PULLING API // static uint32 vorbis_find_page(stb_vorbis *f, uint32 *end, uint32 *last) { for(;;) { int n; if (f->eof) return 0; n = get8(f); if (n == 0x4f) { // page header candidate unsigned int retry_loc = stb_vorbis_get_file_offset(f); int i; // check if we're off the end of a file_section stream if (retry_loc - 25 > f->stream_len) return 0; // check the rest of the header for (i=1; i < 4; ++i) if (get8(f) != ogg_page_header[i]) break; if (f->eof) return 0; if (i == 4) { uint8 header[27]; uint32 i, crc, goal, len; for (i=0; i < 4; ++i) header[i] = ogg_page_header[i]; for (; i < 27; ++i) header[i] = get8(f); if (f->eof) return 0; if (header[4] != 0) goto invalid; goal = header[22] + (header[23] << 8) + (header[24]<<16) + (header[25]<<24); for (i=22; i < 26; ++i) header[i] = 0; crc = 0; for (i=0; i < 27; ++i) crc = crc32_update(crc, header[i]); len = 0; for (i=0; i < header[26]; ++i) { int s = get8(f); crc = crc32_update(crc, s); len += s; } if (len && f->eof) return 0; for (i=0; i < len; ++i) crc = crc32_update(crc, get8(f)); // finished parsing probable page if (crc == goal) { // we could now check that it's either got the last // page flag set, OR it's followed by the capture // pattern, but I guess TECHNICALLY you could have // a file with garbage between each ogg page and recover // from it automatically? So even though that paranoia // might decrease the chance of an invalid decode by // another 2^32, not worth it since it would hose those // invalid-but-useful files? if (end) *end = stb_vorbis_get_file_offset(f); if (last) { if (header[5] & 0x04) *last = 1; else *last = 0; } set_file_offset(f, retry_loc-1); return 1; } } invalid: // not a valid page, so rewind and look for next one set_file_offset(f, retry_loc); } } } #define SAMPLE_unknown 0xffffffff // seeking is implemented with a binary search, which narrows down the range to // 64K, before using a linear search (because finding the synchronization // pattern can be expensive, and the chance we'd find the end page again is // relatively high for small ranges) // // two initial interpolation-style probes are used at the start of the search // to try to bound either side of the binary search sensibly, while still // working in O(log n) time if they fail. static int get_seek_page_info(stb_vorbis *f, ProbedPage *z) { uint8 header[27], lacing[255]; int i,len; // record where the page starts z->page_start = stb_vorbis_get_file_offset(f); // parse the header getn(f, header, 27); if (header[0] != 'O' || header[1] != 'g' || header[2] != 'g' || header[3] != 'S') return 0; getn(f, lacing, header[26]); // determine the length of the payload len = 0; for (i=0; i < header[26]; ++i) len += lacing[i]; // this implies where the page ends z->page_end = z->page_start + 27 + header[26] + len; // read the last-decoded sample out of the data z->last_decoded_sample = header[6] + (header[7] << 8) + (header[8] << 16) + (header[9] << 24); // restore file state to where we were set_file_offset(f, z->page_start); return 1; } // rarely used function to seek back to the preceding page while finding the // start of a packet static int go_to_page_before(stb_vorbis *f, unsigned int limit_offset) { unsigned int previous_safe, end; // now we want to seek back 64K from the limit if (limit_offset >= 65536 && limit_offset-65536 >= f->first_audio_page_offset) previous_safe = limit_offset - 65536; else previous_safe = f->first_audio_page_offset; set_file_offset(f, previous_safe); while (vorbis_find_page(f, &end, NULL)) { if (end >= limit_offset && stb_vorbis_get_file_offset(f) < limit_offset) return 1; set_file_offset(f, end); } return 0; } // implements the search logic for finding a page and starting decoding. if // the function succeeds, current_loc_valid will be true and current_loc will // be less than or equal to the provided sample number (the closer the // better). static int seek_to_sample_coarse(stb_vorbis *f, uint32 sample_number) { ProbedPage left, right, mid; int i, start_seg_with_known_loc, end_pos, page_start; uint32 delta, stream_length, padding, last_sample_limit; double offset = 0.0, bytes_per_sample = 0.0; int probe = 0; // find the last page and validate the target sample stream_length = stb_vorbis_stream_length_in_samples(f); if (stream_length == 0) return error(f, VORBIS_seek_without_length); if (sample_number > stream_length) return error(f, VORBIS_seek_invalid); // this is the maximum difference between the window-center (which is the // actual granule position value), and the right-start (which the spec // indicates should be the granule position (give or take one)). padding = ((f->blocksize_1 - f->blocksize_0) >> 2); if (sample_number < padding) last_sample_limit = 0; else last_sample_limit = sample_number - padding; left = f->p_first; while (left.last_decoded_sample == ~0U) { // (untested) the first page does not have a 'last_decoded_sample' set_file_offset(f, left.page_end); if (!get_seek_page_info(f, &left)) goto error; } right = f->p_last; assert(right.last_decoded_sample != ~0U); // starting from the start is handled differently if (last_sample_limit <= left.last_decoded_sample) { if (stb_vorbis_seek_start(f)) { if (f->current_loc > sample_number) return error(f, VORBIS_seek_failed); return 1; } return 0; } while (left.page_end != right.page_start) { assert(left.page_end < right.page_start); // search range in bytes delta = right.page_start - left.page_end; if (delta <= 65536) { // there's only 64K left to search - handle it linearly set_file_offset(f, left.page_end); } else { if (probe < 2) { if (probe == 0) { // first probe (interpolate) double data_bytes = right.page_end - left.page_start; bytes_per_sample = data_bytes / right.last_decoded_sample; offset = left.page_start + bytes_per_sample * (last_sample_limit - left.last_decoded_sample); } else { // second probe (try to bound the other side) double error = ((double) last_sample_limit - mid.last_decoded_sample) * bytes_per_sample; if (error >= 0 && error < 8000) error = 8000; if (error < 0 && error > -8000) error = -8000; offset += error * 2; } // ensure the offset is valid if (offset < left.page_end) offset = left.page_end; if (offset > right.page_start - 65536) offset = right.page_start - 65536; set_file_offset(f, (unsigned int) offset); } else { // binary search for large ranges (offset by 32K to ensure // we don't hit the right page) set_file_offset(f, left.page_end + (delta / 2) - 32768); } if (!vorbis_find_page(f, NULL, NULL)) goto error; } for (;;) { if (!get_seek_page_info(f, &mid)) goto error; if (mid.last_decoded_sample != ~0U) break; // (untested) no frames end on this page set_file_offset(f, mid.page_end); assert(mid.page_start < right.page_start); } // if we've just found the last page again then we're in a tricky file, // and we're close enough (if it wasn't an interpolation probe). if (mid.page_start == right.page_start) { if (probe >= 2 || delta <= 65536) break; } else { if (last_sample_limit < mid.last_decoded_sample) right = mid; else left = mid; } ++probe; } // seek back to start of the last packet page_start = left.page_start; set_file_offset(f, page_start); if (!start_page(f)) return error(f, VORBIS_seek_failed); end_pos = f->end_seg_with_known_loc; assert(end_pos >= 0); for (;;) { for (i = end_pos; i > 0; --i) if (f->segments[i-1] != 255) break; start_seg_with_known_loc = i; if (start_seg_with_known_loc > 0 || !(f->page_flag & PAGEFLAG_continued_packet)) break; // (untested) the final packet begins on an earlier page if (!go_to_page_before(f, page_start)) goto error; page_start = stb_vorbis_get_file_offset(f); if (!start_page(f)) goto error; end_pos = f->segment_count - 1; } // prepare to start decoding f->current_loc_valid = FALSE; f->last_seg = FALSE; f->valid_bits = 0; f->packet_bytes = 0; f->bytes_in_seg = 0; f->previous_length = 0; f->next_seg = start_seg_with_known_loc; for (i = 0; i < start_seg_with_known_loc; i++) skip(f, f->segments[i]); // start decoding (optimizable - this frame is generally discarded) if (!vorbis_pump_first_frame(f)) return 0; if (f->current_loc > sample_number) return error(f, VORBIS_seek_failed); return 1; error: // try to restore the file to a valid state stb_vorbis_seek_start(f); return error(f, VORBIS_seek_failed); } // the same as vorbis_decode_initial, but without advancing static int peek_decode_initial(vorb *f, int *p_left_start, int *p_left_end, int *p_right_start, int *p_right_end, int *mode) { int bits_read, bytes_read; if (!vorbis_decode_initial(f, p_left_start, p_left_end, p_right_start, p_right_end, mode)) return 0; // either 1 or 2 bytes were read, figure out which so we can rewind bits_read = 1 + ilog(f->mode_count-1); if (f->mode_config[*mode].blockflag) bits_read += 2; bytes_read = (bits_read + 7) / 8; f->bytes_in_seg += bytes_read; f->packet_bytes -= bytes_read; skip(f, -bytes_read); if (f->next_seg == -1) f->next_seg = f->segment_count - 1; else f->next_seg--; f->valid_bits = 0; return 1; } int stb_vorbis_seek_frame(stb_vorbis *f, unsigned int sample_number) { uint32 max_frame_samples; if (IS_PUSH_MODE(f)) return error(f, VORBIS_invalid_api_mixing); // fast page-level search if (!seek_to_sample_coarse(f, sample_number)) return 0; assert(f->current_loc_valid); assert(f->current_loc <= sample_number); // linear search for the relevant packet max_frame_samples = (f->blocksize_1*3 - f->blocksize_0) >> 2; while (f->current_loc < sample_number) { int left_start, left_end, right_start, right_end, mode, frame_samples; if (!peek_decode_initial(f, &left_start, &left_end, &right_start, &right_end, &mode)) return error(f, VORBIS_seek_failed); // calculate the number of samples returned by the next frame frame_samples = right_start - left_start; if (f->current_loc + frame_samples > sample_number) { return 1; // the next frame will contain the sample } else if (f->current_loc + frame_samples + max_frame_samples > sample_number) { // there's a chance the frame after this could contain the sample vorbis_pump_first_frame(f); } else { // this frame is too early to be relevant f->current_loc += frame_samples; f->previous_length = 0; maybe_start_packet(f); flush_packet(f); } } // the next frame should start with the sample if (f->current_loc != sample_number) return error(f, VORBIS_seek_failed); return 1; } int stb_vorbis_seek(stb_vorbis *f, unsigned int sample_number) { if (!stb_vorbis_seek_frame(f, sample_number)) return 0; if (sample_number != f->current_loc) { int n; uint32 frame_start = f->current_loc; stb_vorbis_get_frame_float(f, &n, NULL); assert(sample_number > frame_start); assert(f->channel_buffer_start + (int) (sample_number-frame_start) <= f->channel_buffer_end); f->channel_buffer_start += (sample_number - frame_start); } return 1; } int stb_vorbis_seek_start(stb_vorbis *f) { if (IS_PUSH_MODE(f)) { return error(f, VORBIS_invalid_api_mixing); } set_file_offset(f, f->first_audio_page_offset); f->previous_length = 0; f->first_decode = TRUE; f->next_seg = -1; return vorbis_pump_first_frame(f); } unsigned int stb_vorbis_stream_length_in_samples(stb_vorbis *f) { unsigned int restore_offset, previous_safe; unsigned int end, last_page_loc; if (IS_PUSH_MODE(f)) return error(f, VORBIS_invalid_api_mixing); if (!f->total_samples) { unsigned int last; uint32 lo,hi; char header[6]; // first, store the current decode position so we can restore it restore_offset = stb_vorbis_get_file_offset(f); // now we want to seek back 64K from the end (the last page must // be at most a little less than 64K, but let's allow a little slop) if (f->stream_len >= 65536 && f->stream_len-65536 >= f->first_audio_page_offset) previous_safe = f->stream_len - 65536; else previous_safe = f->first_audio_page_offset; set_file_offset(f, previous_safe); // previous_safe is now our candidate 'earliest known place that seeking // to will lead to the final page' if (!vorbis_find_page(f, &end, &last)) { // if we can't find a page, we're hosed! f->error = VORBIS_cant_find_last_page; f->total_samples = 0xffffffff; goto done; } // check if there are more pages last_page_loc = stb_vorbis_get_file_offset(f); // stop when the last_page flag is set, not when we reach eof; // this allows us to stop short of a 'file_section' end without // explicitly checking the length of the section while (!last) { set_file_offset(f, end); if (!vorbis_find_page(f, &end, &last)) { // the last page we found didn't have the 'last page' flag // set. whoops! break; } previous_safe = last_page_loc+1; last_page_loc = stb_vorbis_get_file_offset(f); } set_file_offset(f, last_page_loc); // parse the header getn(f, (unsigned char *)header, 6); // extract the absolute granule position lo = get32(f); hi = get32(f); if (lo == 0xffffffff && hi == 0xffffffff) { f->error = VORBIS_cant_find_last_page; f->total_samples = SAMPLE_unknown; goto done; } if (hi) lo = 0xfffffffe; // saturate f->total_samples = lo; f->p_last.page_start = last_page_loc; f->p_last.page_end = end; f->p_last.last_decoded_sample = lo; done: set_file_offset(f, restore_offset); } return f->total_samples == SAMPLE_unknown ? 0 : f->total_samples; } float stb_vorbis_stream_length_in_seconds(stb_vorbis *f) { return stb_vorbis_stream_length_in_samples(f) / (float) f->sample_rate; } int stb_vorbis_get_frame_float(stb_vorbis *f, int *channels, float ***output) { int len, right,left,i; if (IS_PUSH_MODE(f)) return error(f, VORBIS_invalid_api_mixing); if (!vorbis_decode_packet(f, &len, &left, &right)) { f->channel_buffer_start = f->channel_buffer_end = 0; return 0; } len = vorbis_finish_frame(f, len, left, right); for (i=0; i < f->channels; ++i) f->outputs[i] = f->channel_buffers[i] + left; f->channel_buffer_start = left; f->channel_buffer_end = left+len; if (channels) *channels = f->channels; if (output) *output = f->outputs; return len; } #ifndef STB_VORBIS_NO_STDIO stb_vorbis * stb_vorbis_open_file_section(FILE *file, int close_on_free, int *error, const stb_vorbis_alloc *alloc, unsigned int length) { stb_vorbis *f, p; vorbis_init(&p, alloc); p.f = file; p.f_start = (uint32) ftell(file); p.stream_len = length; p.close_on_free = close_on_free; if (start_decoder(&p)) { f = vorbis_alloc(&p); if (f) { *f = p; vorbis_pump_first_frame(f); return f; } } if (error) *error = p.error; vorbis_deinit(&p); return NULL; } stb_vorbis * stb_vorbis_open_file(FILE *file, int close_on_free, int *error, const stb_vorbis_alloc *alloc) { unsigned int len, start; start = (unsigned int) ftell(file); fseek(file, 0, SEEK_END); len = (unsigned int) (ftell(file) - start); fseek(file, start, SEEK_SET); return stb_vorbis_open_file_section(file, close_on_free, error, alloc, len); } stb_vorbis * stb_vorbis_open_filename(const char *filename, int *error, const stb_vorbis_alloc *alloc) { FILE *f; #if defined(_WIN32) && defined(__STDC_WANT_SECURE_LIB__) if (0 != fopen_s(&f, filename, "rb")) f = NULL; #else f = fopen(filename, "rb"); #endif if (f) return stb_vorbis_open_file(f, TRUE, error, alloc); if (error) *error = VORBIS_file_open_failure; return NULL; } #endif // STB_VORBIS_NO_STDIO stb_vorbis * stb_vorbis_open_memory(const unsigned char *data, int len, int *error, const stb_vorbis_alloc *alloc) { stb_vorbis *f, p; if (data == NULL) return NULL; vorbis_init(&p, alloc); p.stream = (uint8 *) data; p.stream_end = (uint8 *) data + len; p.stream_start = (uint8 *) p.stream; p.stream_len = len; p.push_mode = FALSE; if (start_decoder(&p)) { f = vorbis_alloc(&p); if (f) { *f = p; vorbis_pump_first_frame(f); if (error) *error = VORBIS__no_error; return f; } } if (error) *error = p.error; vorbis_deinit(&p); return NULL; } #ifndef STB_VORBIS_NO_INTEGER_CONVERSION #define PLAYBACK_MONO 1 #define PLAYBACK_LEFT 2 #define PLAYBACK_RIGHT 4 #define L (PLAYBACK_LEFT | PLAYBACK_MONO) #define C (PLAYBACK_LEFT | PLAYBACK_RIGHT | PLAYBACK_MONO) #define R (PLAYBACK_RIGHT | PLAYBACK_MONO) static int8 channel_position[7][6] = { { 0 }, { C }, { L, R }, { L, C, R }, { L, R, L, R }, { L, C, R, L, R }, { L, C, R, L, R, C }, }; #ifndef STB_VORBIS_NO_FAST_SCALED_FLOAT typedef union { float f; int i; } float_conv; typedef char stb_vorbis_float_size_test[sizeof(float)==4 && sizeof(int) == 4]; #define FASTDEF(x) float_conv x // add (1<<23) to convert to int, then divide by 2^SHIFT, then add 0.5/2^SHIFT to round #define MAGIC(SHIFT) (1.5f * (1 << (23-SHIFT)) + 0.5f/(1 << SHIFT)) #define ADDEND(SHIFT) (((150-SHIFT) << 23) + (1 << 22)) #define FAST_SCALED_FLOAT_TO_INT(temp,x,s) (temp.f = (x) + MAGIC(s), temp.i - ADDEND(s)) #define check_endianness() #else #define FAST_SCALED_FLOAT_TO_INT(temp,x,s) ((int) ((x) * (1 << (s)))) #define check_endianness() #define FASTDEF(x) #endif static void copy_samples(short *dest, float *src, int len) { int i; check_endianness(); for (i=0; i < len; ++i) { FASTDEF(temp); int v = FAST_SCALED_FLOAT_TO_INT(temp, src[i],15); if ((unsigned int) (v + 32768) > 65535) v = v < 0 ? -32768 : 32767; dest[i] = v; } } static void compute_samples(int mask, short *output, int num_c, float **data, int d_offset, int len) { #define BUFFER_SIZE 32 float buffer[BUFFER_SIZE]; int i,j,o,n = BUFFER_SIZE; check_endianness(); for (o = 0; o < len; o += BUFFER_SIZE) { memset(buffer, 0, sizeof(buffer)); if (o + n > len) n = len - o; for (j=0; j < num_c; ++j) { if (channel_position[num_c][j] & mask) { for (i=0; i < n; ++i) buffer[i] += data[j][d_offset+o+i]; } } for (i=0; i < n; ++i) { FASTDEF(temp); int v = FAST_SCALED_FLOAT_TO_INT(temp,buffer[i],15); if ((unsigned int) (v + 32768) > 65535) v = v < 0 ? -32768 : 32767; output[o+i] = v; } } } static void compute_stereo_samples(short *output, int num_c, float **data, int d_offset, int len) { #define BUFFER_SIZE 32 float buffer[BUFFER_SIZE]; int i,j,o,n = BUFFER_SIZE >> 1; // o is the offset in the source data check_endianness(); for (o = 0; o < len; o += BUFFER_SIZE >> 1) { // o2 is the offset in the output data int o2 = o << 1; memset(buffer, 0, sizeof(buffer)); if (o + n > len) n = len - o; for (j=0; j < num_c; ++j) { int m = channel_position[num_c][j] & (PLAYBACK_LEFT | PLAYBACK_RIGHT); if (m == (PLAYBACK_LEFT | PLAYBACK_RIGHT)) { for (i=0; i < n; ++i) { buffer[i*2+0] += data[j][d_offset+o+i]; buffer[i*2+1] += data[j][d_offset+o+i]; } } else if (m == PLAYBACK_LEFT) { for (i=0; i < n; ++i) { buffer[i*2+0] += data[j][d_offset+o+i]; } } else if (m == PLAYBACK_RIGHT) { for (i=0; i < n; ++i) { buffer[i*2+1] += data[j][d_offset+o+i]; } } } for (i=0; i < (n<<1); ++i) { FASTDEF(temp); int v = FAST_SCALED_FLOAT_TO_INT(temp,buffer[i],15); if ((unsigned int) (v + 32768) > 65535) v = v < 0 ? -32768 : 32767; output[o2+i] = v; } } } static void convert_samples_short(int buf_c, short **buffer, int b_offset, int data_c, float **data, int d_offset, int samples) { int i; if (buf_c != data_c && buf_c <= 2 && data_c <= 6) { static int channel_selector[3][2] = { {0}, {PLAYBACK_MONO}, {PLAYBACK_LEFT, PLAYBACK_RIGHT} }; for (i=0; i < buf_c; ++i) compute_samples(channel_selector[buf_c][i], buffer[i]+b_offset, data_c, data, d_offset, samples); } else { int limit = buf_c < data_c ? buf_c : data_c; for (i=0; i < limit; ++i) copy_samples(buffer[i]+b_offset, data[i]+d_offset, samples); for ( ; i < buf_c; ++i) memset(buffer[i]+b_offset, 0, sizeof(short) * samples); } } int stb_vorbis_get_frame_short(stb_vorbis *f, int num_c, short **buffer, int num_samples) { float **output = NULL; int len = stb_vorbis_get_frame_float(f, NULL, &output); if (len > num_samples) len = num_samples; if (len) convert_samples_short(num_c, buffer, 0, f->channels, output, 0, len); return len; } static void convert_channels_short_interleaved(int buf_c, short *buffer, int data_c, float **data, int d_offset, int len) { int i; check_endianness(); if (buf_c != data_c && buf_c <= 2 && data_c <= 6) { assert(buf_c == 2); for (i=0; i < buf_c; ++i) compute_stereo_samples(buffer, data_c, data, d_offset, len); } else { int limit = buf_c < data_c ? buf_c : data_c; int j; for (j=0; j < len; ++j) { for (i=0; i < limit; ++i) { FASTDEF(temp); float f = data[i][d_offset+j]; int v = FAST_SCALED_FLOAT_TO_INT(temp, f,15);//data[i][d_offset+j],15); if ((unsigned int) (v + 32768) > 65535) v = v < 0 ? -32768 : 32767; *buffer++ = v; } for ( ; i < buf_c; ++i) *buffer++ = 0; } } } int stb_vorbis_get_frame_short_interleaved(stb_vorbis *f, int num_c, short *buffer, int num_shorts) { float **output; int len; if (num_c == 1) return stb_vorbis_get_frame_short(f,num_c,&buffer, num_shorts); len = stb_vorbis_get_frame_float(f, NULL, &output); if (len) { if (len*num_c > num_shorts) len = num_shorts / num_c; convert_channels_short_interleaved(num_c, buffer, f->channels, output, 0, len); } return len; } int stb_vorbis_get_samples_short_interleaved(stb_vorbis *f, int channels, short *buffer, int num_shorts) { float **outputs; int len = num_shorts / channels; int n=0; int z = f->channels; if (z > channels) z = channels; while (n < len) { int k = f->channel_buffer_end - f->channel_buffer_start; if (n+k >= len) k = len - n; if (k) convert_channels_short_interleaved(channels, buffer, f->channels, f->channel_buffers, f->channel_buffer_start, k); buffer += k*channels; n += k; f->channel_buffer_start += k; if (n == len) break; if (!stb_vorbis_get_frame_float(f, NULL, &outputs)) break; } return n; } int stb_vorbis_get_samples_short(stb_vorbis *f, int channels, short **buffer, int len) { float **outputs; int n=0; int z = f->channels; if (z > channels) z = channels; while (n < len) { int k = f->channel_buffer_end - f->channel_buffer_start; if (n+k >= len) k = len - n; if (k) convert_samples_short(channels, buffer, n, f->channels, f->channel_buffers, f->channel_buffer_start, k); n += k; f->channel_buffer_start += k; if (n == len) break; if (!stb_vorbis_get_frame_float(f, NULL, &outputs)) break; } return n; } #ifndef STB_VORBIS_NO_STDIO int stb_vorbis_decode_filename(const char *filename, int *channels, int *sample_rate, short **output) { int data_len, offset, total, limit, error; short *data; stb_vorbis *v = stb_vorbis_open_filename(filename, &error, NULL); if (v == NULL) return -1; limit = v->channels * 4096; *channels = v->channels; if (sample_rate) *sample_rate = v->sample_rate; offset = data_len = 0; total = limit; data = (short *) malloc(total * sizeof(*data)); if (data == NULL) { stb_vorbis_close(v); return -2; } for (;;) { int n = stb_vorbis_get_frame_short_interleaved(v, v->channels, data+offset, total-offset); if (n == 0) break; data_len += n; offset += n * v->channels; if (offset + limit > total) { short *data2; total *= 2; data2 = (short *) realloc(data, total * sizeof(*data)); if (data2 == NULL) { free(data); stb_vorbis_close(v); return -2; } data = data2; } } *output = data; stb_vorbis_close(v); return data_len; } #endif // NO_STDIO int stb_vorbis_decode_memory(const uint8 *mem, int len, int *channels, int *sample_rate, short **output) { int data_len, offset, total, limit, error; short *data; stb_vorbis *v = stb_vorbis_open_memory(mem, len, &error, NULL); if (v == NULL) return -1; limit = v->channels * 4096; *channels = v->channels; if (sample_rate) *sample_rate = v->sample_rate; offset = data_len = 0; total = limit; data = (short *) malloc(total * sizeof(*data)); if (data == NULL) { stb_vorbis_close(v); return -2; } for (;;) { int n = stb_vorbis_get_frame_short_interleaved(v, v->channels, data+offset, total-offset); if (n == 0) break; data_len += n; offset += n * v->channels; if (offset + limit > total) { short *data2; total *= 2; data2 = (short *) realloc(data, total * sizeof(*data)); if (data2 == NULL) { free(data); stb_vorbis_close(v); return -2; } data = data2; } } *output = data; stb_vorbis_close(v); return data_len; } #endif // STB_VORBIS_NO_INTEGER_CONVERSION int stb_vorbis_get_samples_float_interleaved(stb_vorbis *f, int channels, float *buffer, int num_floats) { float **outputs; int len = num_floats / channels; int n=0; int z = f->channels; if (z > channels) z = channels; while (n < len) { int i,j; int k = f->channel_buffer_end - f->channel_buffer_start; if (n+k >= len) k = len - n; for (j=0; j < k; ++j) { for (i=0; i < z; ++i) *buffer++ = f->channel_buffers[i][f->channel_buffer_start+j]; for ( ; i < channels; ++i) *buffer++ = 0; } n += k; f->channel_buffer_start += k; if (n == len) break; if (!stb_vorbis_get_frame_float(f, NULL, &outputs)) break; } return n; } int stb_vorbis_get_samples_float(stb_vorbis *f, int channels, float **buffer, int num_samples) { float **outputs; int n=0; int z = f->channels; if (z > channels) z = channels; while (n < num_samples) { int i; int k = f->channel_buffer_end - f->channel_buffer_start; if (n+k >= num_samples) k = num_samples - n; if (k) { for (i=0; i < z; ++i) memcpy(buffer[i]+n, f->channel_buffers[i]+f->channel_buffer_start, sizeof(float)*k); for ( ; i < channels; ++i) memset(buffer[i]+n, 0, sizeof(float) * k); } n += k; f->channel_buffer_start += k; if (n == num_samples) break; if (!stb_vorbis_get_frame_float(f, NULL, &outputs)) break; } return n; } #endif // STB_VORBIS_NO_PULLDATA_API /* Version history 1.17 - 2019-07-08 - fix CVE-2019-13217, -13218, -13219, -13220, -13221, -13222, -13223 found with Mayhem by ForAllSecure 1.16 - 2019-03-04 - fix warnings 1.15 - 2019-02-07 - explicit failure if Ogg Skeleton data is found 1.14 - 2018-02-11 - delete bogus dealloca usage 1.13 - 2018-01-29 - fix truncation of last frame (hopefully) 1.12 - 2017-11-21 - limit residue begin/end to blocksize/2 to avoid large temp allocs in bad/corrupt files 1.11 - 2017-07-23 - fix MinGW compilation 1.10 - 2017-03-03 - more robust seeking; fix negative ilog(); clear error in open_memory 1.09 - 2016-04-04 - back out 'avoid discarding last frame' fix from previous version 1.08 - 2016-04-02 - fixed multiple warnings; fix setup memory leaks; avoid discarding last frame of audio data 1.07 - 2015-01-16 - fixed some warnings, fix mingw, const-correct API some more crash fixes when out of memory or with corrupt files 1.06 - 2015-08-31 - full, correct support for seeking API (Dougall Johnson) some crash fixes when out of memory or with corrupt files 1.05 - 2015-04-19 - don't define __forceinline if it's redundant 1.04 - 2014-08-27 - fix missing const-correct case in API 1.03 - 2014-08-07 - Warning fixes 1.02 - 2014-07-09 - Declare qsort compare function _cdecl on windows 1.01 - 2014-06-18 - fix stb_vorbis_get_samples_float 1.0 - 2014-05-26 - fix memory leaks; fix warnings; fix bugs in multichannel (API change) report sample rate for decode-full-file funcs 0.99996 - bracket #include for macintosh compilation by Laurent Gomila 0.99995 - use union instead of pointer-cast for fast-float-to-int to avoid alias-optimization problem 0.99994 - change fast-float-to-int to work in single-precision FPU mode, remove endian-dependence 0.99993 - remove assert that fired on legal files with empty tables 0.99992 - rewind-to-start 0.99991 - bugfix to stb_vorbis_get_samples_short by Bernhard Wodo 0.9999 - (should have been 0.99990) fix no-CRT support, compiling as C++ 0.9998 - add a full-decode function with a memory source 0.9997 - fix a bug in the read-from-FILE case in 0.9996 addition 0.9996 - query length of vorbis stream in samples/seconds 0.9995 - bugfix to another optimization that only happened in certain files 0.9994 - bugfix to one of the optimizations that caused significant (but inaudible?) errors 0.9993 - performance improvements; runs in 99% to 104% of time of reference implementation 0.9992 - performance improvement of IMDCT; now performs close to reference implementation 0.9991 - performance improvement of IMDCT 0.999 - (should have been 0.9990) performance improvement of IMDCT 0.998 - no-CRT support from Casey Muratori 0.997 - bugfixes for bugs found by Terje Mathisen 0.996 - bugfix: fast-huffman decode initialized incorrectly for sparse codebooks; fixing gives 10% speedup - found by Terje Mathisen 0.995 - bugfix: fix to 'effective' overrun detection - found by Terje Mathisen 0.994 - bugfix: garbage decode on final VQ symbol of a non-multiple - found by Terje Mathisen 0.993 - bugfix: pushdata API required 1 extra byte for empty page (failed to consume final page if empty) - found by Terje Mathisen 0.992 - fixes for MinGW warning 0.991 - turn fast-float-conversion on by default 0.990 - fix push-mode seek recovery if you seek into the headers 0.98b - fix to bad release of 0.98 0.98 - fix push-mode seek recovery; robustify float-to-int and support non-fast mode 0.97 - builds under c++ (typecasting, don't use 'class' keyword) 0.96 - somehow MY 0.95 was right, but the web one was wrong, so here's my 0.95 rereleased as 0.96, fixes a typo in the clamping code 0.95 - clamping code for 16-bit functions 0.94 - not publically released 0.93 - fixed all-zero-floor case (was decoding garbage) 0.92 - fixed a memory leak 0.91 - conditional compiles to omit parts of the API and the infrastructure to support them: STB_VORBIS_NO_PULLDATA_API, STB_VORBIS_NO_PUSHDATA_API, STB_VORBIS_NO_STDIO, STB_VORBIS_NO_INTEGER_CONVERSION 0.90 - first public release */ #endif // STB_VORBIS_HEADER_ONLY /* ------------------------------------------------------------------------------ This software is available under 2 licenses -- choose whichever you prefer. ------------------------------------------------------------------------------ ALTERNATIVE A - MIT License Copyright (c) 2017 Sean Barrett 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. ------------------------------------------------------------------------------ ALTERNATIVE B - Public Domain (www.unlicense.org) This is free and unencumbered software released into the public domain. Anyone is free to copy, modify, publish, use, compile, sell, or distribute this software, either in source code form or as a compiled binary, for any purpose, commercial or non-commercial, and by any means. In jurisdictions that recognize copyright laws, the author or authors of this software dedicate any and all copyright interest in the software to the public domain. We make this dedication for the benefit of the public at large and to the detriment of our heirs and successors. We intend this dedication to be an overt act of relinquishment in perpetuity of all present and future rights to this software under copyright law. 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 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. ------------------------------------------------------------------------------ */ #line 0 #undef error #undef DEBUG #line 1 "3rd_sts_mixer.h" /////////////////////////////////////////////////////////////////////////////// // sts_mixer.h - v0.02 // written 2016 by Sebastian Steinhauer // // LICENSE // Public domain. See "unlicense" statement at the end of this file. // // ABOUT // A simple stereo audio mixer which is capable of mixing samples and audio streams. // Samples can be played with different gain, pitch and panning. // Streams can be played with different gain. // This library has no malloc/free. All structs have to be "prepared" by the user. So you can enroll your own memory management. // You have to implement/provide a real audio-backend to hear something from the speakers. // A good starting point would be SDL2 where you can use an audio callback to feed the audio device. // // USAGE // Please note that most audio systems will run in a separate thread. So you have to take care about locking before modifying the sts_mixer_t state. // See the example at the end of the file. // // VERSION HISTORY // 0.02 (2022-05-10) allow voice queueing in same channel. ie, chain another sample on same voice channel after current sample playback is done (@r-lyeh) // 0.01 (2016-05-01) initial version // #ifndef __INCLUDED__STS_MIXER_H__ #define __INCLUDED__STS_MIXER_H__ // The number of concurrent voices (channels) which are used to mix the audio. // If you need more, use a higher number by setting #define STS_MIXER_VOICE n before including this header. #ifndef STS_MIXER_VOICES #define STS_MIXER_VOICES 32 #endif // STS_MIXER_VOICES // Defines the various audio formats. Note that they are all on system endianess. enum { STS_MIXER_SAMPLE_FORMAT_NONE, // no format STS_MIXER_SAMPLE_FORMAT_8, // signed 8-bit STS_MIXER_SAMPLE_FORMAT_16, // signed 16-bit STS_MIXER_SAMPLE_FORMAT_32, // signed 32-bit STS_MIXER_SAMPLE_FORMAT_FLOAT // floats }; //////////////////////////////////////////////////////////////////////////////// // // SAMPLES // // A sample is a *MONO* piece of audio which is loaded fully to memory. // It can be played with various gains, pitches and pannings. // typedef struct { unsigned int length; // length in samples (so 1024 samples of STS_MIXER_SAMPLE_FORMAT_16 would be 2048 bytes) unsigned int frequency; // frequency of this sample (e.g. 44100, 22000 ...) int audio_format; // one of STS_MIXER_SAMPLE_FORMAT_* void* data; // pointer to the sample data, sts_mixer makes no copy, so you have to keep them in memory void* next; // next sample in chain (if any) //< @r-lyeh } sts_mixer_sample_t; //////////////////////////////////////////////////////////////////////////////// // // STREAMS // // A stream is *STEREO* audio which will be decoded/loaded as needed. // It can be played with various gains. No panning or pitching. // // The callback which will be called when the stream needs more data. typedef void (*sts_mixer_stream_callback)(sts_mixer_sample_t* sample, void* userdata); typedef struct { void* userdata; // a userdata pointer which will passed to the callback sts_mixer_stream_callback callback; // this callback will be called when the stream needs more data sts_mixer_sample_t sample; // the current stream "sample" which holds the current piece of audio } sts_mixer_stream_t; //////////////////////////////////////////////////////////////////////////////// // // VOICES // // A voice is an audio source which will be used during mixing. // It can play nothing, a sample or a stream. // Most of those fields are considered "private" and you should not play around with those. // typedef struct { int state; sts_mixer_sample_t* sample; sts_mixer_stream_t* stream; float position; float gain; float pitch; float pan; } sts_mixer_voice_t; //////////////////////////////////////////////////////////////////////////////// // // MIXER // // The mixer state. // typedef struct { float gain; // the global gain (you can change it if you want to change to overall volume) unsigned int frequency; // the frequency for the output of mixed audio data int audio_format; // the audio format for the output of mixed audio data sts_mixer_voice_t voices[STS_MIXER_VOICES]; // holding all audio voices for this state } sts_mixer_t; //////////////////////////////////////////////////////////////////////////////// // // API // // "Initializes" a new sts_mixer state. void sts_mixer_init(sts_mixer_t* mixer, unsigned int frequency, int audio_format); // "Shutdown" the mixer state. It will simply reset all fields. void sts_mixer_shutdown(sts_mixer_t* mixer); // Return the number of active voices. Active voices are voices that play either a stream or a sample. int sts_mixer_get_active_voices(sts_mixer_t* mixer); // Play the given sample with the gain, pitch and panning. // Panning can be something between -1.0f (fully left) ... +1.0f (fully right) // Please note that pitch will be clamped so it cannot reach 0.0f (would be useless). // Returns the number of the voice where this sample will be played or -1 if no voice was free. int sts_mixer_play_sample(sts_mixer_t* mixer, sts_mixer_sample_t* sample, float gain, float pitch, float pan); // Plays the given stream with the gain. // Returns the number of the voice where this stream will be played or -1 if no voice was free. int sts_mixer_play_stream(sts_mixer_t* mixer, sts_mixer_stream_t* stream, float gain); // Stops voice with the given voice no. You can pass the returned number of sts_mixer_play_sample / sts_mixer_play_stream here. void sts_mixer_stop_voice(sts_mixer_t* mixer, int voice); // Stops all voices playing the given sample. Useful when you want to delete the sample and make sure it is not used anymore. void sts_mixer_stop_sample(sts_mixer_t* mixer, sts_mixer_sample_t* sample); // Stops all voices playing the given stream. Useful when you want to delete the stream and make sure it is not used anymore. void sts_mixer_stop_stream(sts_mixer_t* mixer, sts_mixer_stream_t* stream); // The mixing function. You should call the function if you need to pass more audio data to the audio device. // Typically this function is called in a separate thread or something like that. // It will write audio data in the specified format and frequency of the mixer state. void sts_mixer_mix_audio(sts_mixer_t* mixer, void* output, unsigned int samples); #endif // __INCLUDED__STS_MIXER_H__ /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// //// //// IMPLEMENTATION //// //// #ifdef STS_MIXER_IMPLEMENTATION enum { STS_MIXER_VOICE_STOPPED, STS_MIXER_VOICE_PLAYING, STS_MIXER_VOICE_STREAMING }; static float sts_mixer__clamp(const float value, const float min, const float max) { if (value < min) return min; else if (value > max) return max; else return value; } static float sts_mixer__clamp_sample(const float sample) { if (sample < -1.0f) return -1.0f; else if (sample > 1.0f) return 1.0f; else return sample; } static float sts_mixer__get_sample(sts_mixer_sample_t* sample, unsigned int position) { switch (sample->audio_format) { case STS_MIXER_SAMPLE_FORMAT_8: return (float)((char*)sample->data)[position] / 127.0f; case STS_MIXER_SAMPLE_FORMAT_16: return (float)((short*)sample->data)[position] / 32767.0f; case STS_MIXER_SAMPLE_FORMAT_32: return (float)((int*)sample->data)[position] / 2147483647.0f; case STS_MIXER_SAMPLE_FORMAT_FLOAT: return ((float*)sample->data)[position]; default: return 0.0f; } } static void sts_mixer__reset_voice(sts_mixer_t* mixer, const int i) { sts_mixer_voice_t* voice = &mixer->voices[i]; voice->state = STS_MIXER_VOICE_STOPPED; voice->sample = 0; voice->stream = 0; voice->position = voice->gain = voice->pitch = voice->pan = 0.0f; } static int sts_mixer__find_free_voice(sts_mixer_t* mixer) { int i; for (i = 0; i < STS_MIXER_VOICES; ++i) { if (mixer->voices[i].state == STS_MIXER_VOICE_STOPPED) return i; } return -1; } void sts_mixer_init(sts_mixer_t* mixer, unsigned int frequency, int audio_format) { int i; for (i = 0; i < STS_MIXER_VOICES; ++i) sts_mixer__reset_voice(mixer, i); mixer->frequency = frequency; mixer->gain = 1.0f; mixer->audio_format = audio_format; } void sts_mixer_shutdown(sts_mixer_t* mixer) { sts_mixer_init(mixer, 0, 0); } int sts_mixer_get_active_voices(sts_mixer_t* mixer) { int i, active; for (i = 0, active = 0; i < STS_MIXER_VOICES; ++i) { if (mixer->voices[i].state != STS_MIXER_VOICE_STOPPED) ++active; } return active; } int sts_mixer_play_sample(sts_mixer_t* mixer, sts_mixer_sample_t* sample, float gain, float pitch, float pan) { int i; sts_mixer_voice_t* voice; i = sts_mixer__find_free_voice(mixer); if (i >= 0) { voice = &mixer->voices[i]; voice->gain = gain; voice->pitch = sts_mixer__clamp(pitch, 0.1f, 10.0f); voice->pan = sts_mixer__clamp(pan * 0.5f, -0.5f, 0.5f); voice->position = 0.0f; voice->sample = sample; voice->stream = 0; voice->state = STS_MIXER_VOICE_PLAYING; } return i; } int sts_mixer_play_stream(sts_mixer_t* mixer, sts_mixer_stream_t* stream, float gain) { int i; sts_mixer_voice_t* voice; i = sts_mixer__find_free_voice(mixer); if (i >= 0) { voice = &mixer->voices[i]; voice->gain = gain; voice->position = 0.0f; voice->sample = 0; voice->stream = stream; voice->state = STS_MIXER_VOICE_STREAMING; } return i; } void sts_mixer_stop_voice(sts_mixer_t* mixer, int voice) { if (voice >= 0 && voice < STS_MIXER_VOICES) sts_mixer__reset_voice(mixer, voice); } void sts_mixer_stop_sample(sts_mixer_t* mixer, sts_mixer_sample_t* sample) { int i; for (i = 0; i < STS_MIXER_VOICES; ++i) { if (mixer->voices[i].sample == sample) sts_mixer__reset_voice(mixer, i); } } void sts_mixer_stop_stream(sts_mixer_t* mixer, sts_mixer_stream_t* stream) { int i; for (i = 0; i < STS_MIXER_VOICES; ++i) { if (mixer->voices[i].stream == stream) sts_mixer__reset_voice(mixer, i); } } void sts_mixer_mix_audio(sts_mixer_t* mixer, void* output, unsigned int samples) { sts_mixer_voice_t* voice; unsigned int i, position; float left, right, advance, sample; char* out_8 = (char*)output; short* out_16 = (short*)output; int* out_32 = (int*)output; float* out_float = (float*)output; // mix all voices advance = 1.0f / (float)mixer->frequency; for (; samples > 0; --samples) { left = right = 0.0f; for (i = 0; i < STS_MIXER_VOICES; ++i) { voice = &mixer->voices[i]; if (voice->state == STS_MIXER_VOICE_PLAYING) { position = (int)voice->position; if (position < voice->sample->length) { sample = sts_mixer__clamp_sample(sts_mixer__get_sample(voice->sample, position) * voice->gain); left += sts_mixer__clamp_sample(sample * (0.5f - voice->pan)); right += sts_mixer__clamp_sample(sample * (0.5f + voice->pan)); voice->position += (float)voice->sample->frequency * advance * voice->pitch; } else if( voice->sample->next ) { //< @r-lyeh *voice->sample = *(sts_mixer_sample_t*)voice->sample->next; //< @r-lyeh voice->position = 0; //< @r-lyeh } else sts_mixer__reset_voice(mixer, i); } else if (voice->state == STS_MIXER_VOICE_STREAMING) { position = ((int)voice->position) * 2; if (position >= voice->stream->sample.length) { // buffer empty...refill voice->stream->callback(&voice->stream->sample, voice->stream->userdata); voice->position = 0.0f; position = 0; } left += sts_mixer__clamp_sample(sts_mixer__get_sample(&voice->stream->sample, position) * voice->gain); right += sts_mixer__clamp_sample(sts_mixer__get_sample(&voice->stream->sample, position + 1) * voice->gain); voice->position += (float)voice->stream->sample.frequency * advance; } } // write to buffer. float _g = mixer->gain; //< @r-lyeh: added master gain float _127 = 127.0f * _g; //< @r-lyeh: added master gain float _32767 = 32767.0f * _g; //< @r-lyeh: added master gain float _2147483647 = 2147483647.0f * _g; //< @r-lyeh: added master gain left = sts_mixer__clamp_sample(left); right = sts_mixer__clamp_sample(right); switch (mixer->audio_format) { case STS_MIXER_SAMPLE_FORMAT_8: *out_8++ = (char)(left * _127); //< @r-lyeh: added master gain *out_8++ = (char)(right * _127); //< @r-lyeh: added master gain break; case STS_MIXER_SAMPLE_FORMAT_16: *out_16++ = (short)(left * _32767); //< @r-lyeh: added master gain *out_16++ = (short)(right * _32767); //< @r-lyeh: added master gain break; case STS_MIXER_SAMPLE_FORMAT_32: *out_32++ = (int)(left * _2147483647); //< @r-lyeh: added master gain *out_32++ = (int)(right * _2147483647); //< @r-lyeh: added master gain break; case STS_MIXER_SAMPLE_FORMAT_FLOAT: *out_float++ = left * _g; //< @r-lyeh: added master gain *out_float++ = right * _g; //< @r-lyeh: added master gain break; } } } #endif // STS_MIXER_IMPLEMENTATION //////////////////////////////////////////////////////////////////////////////// // EXAMPLE // This is a very simple example loading a stream and a sample using // dr_flac.h (https://github.com/mackron/dr_libs) and SDL2. You can of course also use stb_vorbis or something similar :) // Please note how the audio thread of SDL2 will be locked when the mixer state get's modified. This is important! // Also there's no error checking in the entire example code, so beware. // #if 0 #include "SDL.h" #define DR_FLAC_IMPLEMENTATION #include "dr_flac.h" #define STS_MIXER_IMPLEMENTATION #include "sts_mixer.h" SDL_AudioDeviceID audio_device = 0; sts_mixer_t mixer; // encapsulate drflac and some buffer with the sts_mixer_stream_t typedef struct { drflac* flac; // FLAC decoder state sts_mixer_stream_t stream; // mixer stream int32_t data[4096*2]; // static sample buffer } mystream_t; // SDL2 audio callback static void audio_callback(void* userdata, Uint8* stream, int len) { (void)(userdata); sts_mixer_mix_audio(&mixer, stream, len / (sizeof(int) * 2)); } // load a sample static void load_sample(sts_mixer_sample_t* sample, const char *filename) { drflac* flac = drflac_open_file(filename); sample->frequency = flac->sampleRate; sample->audio_format = STS_MIXER_SAMPLE_FORMAT_32; sample->length = flac->totalSampleCount; sample->data = malloc(sample->length * sizeof(int32_t)); drflac_read_s32(flac, sample->length, (int32_t*)sample->data); drflac_close(flac); } // the callback to refill the stream data static void refill_stream(sts_mixer_sample_t* sample, void* userdata) { mystream_t* stream = (mystream_t*)userdata; if (drflac_read_s32(stream->flac, sample->length, stream->data) < sample->length) drflac_seek_to_sample(stream->flac, 0); } // load a stream static void load_stream(mystream_t* stream, const char *filename) { stream->flac = drflac_open_file(filename); stream->stream.userdata = stream; stream->stream.callback = refill_stream; stream->stream.sample.frequency = stream->flac->sampleRate; stream->stream.sample.audio_format = STS_MIXER_SAMPLE_FORMAT_32; stream->stream.sample.length = 4096*2; stream->stream.sample.data = stream->data; refill_stream(&stream->stream.sample, stream); } // helper to get random [0.0f..1.0f values static float randf() { return (float)(rand()) / (float)RAND_MAX; } int main(int argc, char *argv[]) { SDL_AudioSpec want, have; sts_mixer_sample_t sample; mystream_t stream; (void)(argc); (void)(argv); // init SDL2 + audio want.format = AUDIO_S32SYS; want.freq = 44100; want.channels = 2; want.userdata = NULL; want.samples = 4096; want.callback = audio_callback; SDL_Init(SDL_INIT_AUDIO); audio_device = SDL_OpenAudioDevice(NULL, 0, &want, &have, 0); // init sts_mixer and load things sts_mixer_init(&mixer, 44100, STS_MIXER_SAMPLE_FORMAT_32); load_sample(&sample, "effect.flac"); load_stream(&stream, "music.flac"); // play the stream sts_mixer_play_stream(&mixer, &stream.stream, 0.7f); // start audio processing and do a loop for audio effects SDL_PauseAudioDevice(audio_device, 0); for (;;) { // !!!IMPORTANT!!! lock the audio thread before modifying data in the sts_mixer !!! SDL_LockAudioDevice(audio_device); // play a sample with random gain, pitch and panning sts_mixer_play_sample(&mixer, &sample, randf(), 0.5f + randf(), -1.0f + randf() * 2.0f); // unlock audio thread again SDL_UnlockAudioDevice(audio_device); // wait ... SDL_Delay(76); } SDL_PauseAudioDevice(audio_device, 1); SDL_CloseAudioDevice(audio_device); SDL_Quit(); return 0; } #endif // 0 /* This is free and unencumbered software released into the public domain. Anyone is free to copy, modify, publish, use, compile, sell, or distribute this software, either in source code form or as a compiled binary, for any purpose, commercial or non-commercial, and by any means. In jurisdictions that recognize copyright laws, the author or authors of this software dedicate any and all copyright interest in the software to the public domain. We make this dedication for the benefit of the public at large and to the detriment of our heirs and successors. We intend this dedication to be an overt act of relinquishment in perpetuity of all present and future rights to this software under copyright law. 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 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. For more information, please refer to */ #line 0 #line 1 "3rd_miniaudio.h" /* Audio playback and capture library. Choice of public domain or MIT-0. See license statements at the end of this file. miniaudio - v0.11.11 - 2022-11-04 David Reid - mackron@gmail.com Website: https://miniaud.io Documentation: https://miniaud.io/docs GitHub: https://github.com/mackron/miniaudio */ /* 1. Introduction =============== miniaudio is a single file library for audio playback and capture. To use it, do the following in one .c file: ```c #define MINIAUDIO_IMPLEMENTATION #include "miniaudio.h" ``` You can do `#include "miniaudio.h"` in other parts of the program just like any other header. miniaudio includes both low level and high level APIs. The low level API is good for those who want to do all of their mixing themselves and only require a light weight interface to the underlying audio device. The high level API is good for those who have complex mixing and effect requirements. In miniaudio, objects are transparent structures. Unlike many other libraries, there are no handles to opaque objects which means you need to allocate memory for objects yourself. In the examples presented in this documentation you will often see objects declared on the stack. You need to be careful when translating these examples to your own code so that you don't accidentally declare your objects on the stack and then cause them to become invalid once the function returns. In addition, you must ensure the memory address of your objects remain the same throughout their lifetime. You therefore cannot be making copies of your objects. A config/init pattern is used throughout the entire library. The idea is that you set up a config object and pass that into the initialization routine. The advantage to this system is that the config object can be initialized with logical defaults and new properties added to it without breaking the API. The config object can be allocated on the stack and does not need to be maintained after initialization of the corresponding object. 1.1. Low Level API ------------------ The low level API gives you access to the raw audio data of an audio device. It supports playback, capture, full-duplex and loopback (WASAPI only). You can enumerate over devices to determine which physical device(s) you want to connect to. The low level API uses the concept of a "device" as the abstraction for physical devices. The idea is that you choose a physical device to emit or capture audio from, and then move data to/from the device when miniaudio tells you to. Data is delivered to and from devices asynchronously via a callback which you specify when initializing the device. When initializing the device you first need to configure it. The device configuration allows you to specify things like the format of the data delivered via the callback, the size of the internal buffer and the ID of the device you want to emit or capture audio from. Once you have the device configuration set up you can initialize the device. When initializing a device you need to allocate memory for the device object beforehand. This gives the application complete control over how the memory is allocated. In the example below we initialize a playback device on the stack, but you could allocate it on the heap if that suits your situation better. ```c void data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount) { // In playback mode copy data to pOutput. In capture mode read data from pInput. In full-duplex mode, both // pOutput and pInput will be valid and you can move data from pInput into pOutput. Never process more than // frameCount frames. } int main() { ma_device_config config = ma_device_config_init(ma_device_type_playback); config.playback.format = ma_format_f32; // Set to ma_format_unknown to use the device's native format. config.playback.channels = 2; // Set to 0 to use the device's native channel count. config.sampleRate = 48000; // Set to 0 to use the device's native sample rate. config.dataCallback = data_callback; // This function will be called when miniaudio needs more data. config.pUserData = pMyCustomData; // Can be accessed from the device object (device.pUserData). ma_device device; if (ma_device_init(NULL, &config, &device) != MA_SUCCESS) { return -1; // Failed to initialize the device. } ma_device_start(&device); // The device is sleeping by default so you'll need to start it manually. // Do something here. Probably your program's main loop. ma_device_uninit(&device); // This will stop the device so no need to do that manually. return 0; } ``` In the example above, `data_callback()` is where audio data is written and read from the device. The idea is in playback mode you cause sound to be emitted from the speakers by writing audio data to the output buffer (`pOutput` in the example). In capture mode you read data from the input buffer (`pInput`) to extract sound captured by the microphone. The `frameCount` parameter tells you how many frames can be written to the output buffer and read from the input buffer. A "frame" is one sample for each channel. For example, in a stereo stream (2 channels), one frame is 2 samples: one for the left, one for the right. The channel count is defined by the device config. The size in bytes of an individual sample is defined by the sample format which is also specified in the device config. Multi-channel audio data is always interleaved, which means the samples for each frame are stored next to each other in memory. For example, in a stereo stream the first pair of samples will be the left and right samples for the first frame, the second pair of samples will be the left and right samples for the second frame, etc. The configuration of the device is defined by the `ma_device_config` structure. The config object is always initialized with `ma_device_config_init()`. It's important to always initialize the config with this function as it initializes it with logical defaults and ensures your program doesn't break when new members are added to the `ma_device_config` structure. The example above uses a fairly simple and standard device configuration. The call to `ma_device_config_init()` takes a single parameter, which is whether or not the device is a playback, capture, duplex or loopback device (loopback devices are not supported on all backends). The `config.playback.format` member sets the sample format which can be one of the following (all formats are native-endian): +---------------+----------------------------------------+---------------------------+ | Symbol | Description | Range | +---------------+----------------------------------------+---------------------------+ | ma_format_f32 | 32-bit floating point | [-1, 1] | | ma_format_s16 | 16-bit signed integer | [-32768, 32767] | | ma_format_s24 | 24-bit signed integer (tightly packed) | [-8388608, 8388607] | | ma_format_s32 | 32-bit signed integer | [-2147483648, 2147483647] | | ma_format_u8 | 8-bit unsigned integer | [0, 255] | +---------------+----------------------------------------+---------------------------+ The `config.playback.channels` member sets the number of channels to use with the device. The channel count cannot exceed MA_MAX_CHANNELS. The `config.sampleRate` member sets the sample rate (which must be the same for both playback and capture in full-duplex configurations). This is usually set to 44100 or 48000, but can be set to anything. It's recommended to keep this between 8000 and 384000, however. Note that leaving the format, channel count and/or sample rate at their default values will result in the internal device's native configuration being used which is useful if you want to avoid the overhead of miniaudio's automatic data conversion. In addition to the sample format, channel count and sample rate, the data callback and user data pointer are also set via the config. The user data pointer is not passed into the callback as a parameter, but is instead set to the `pUserData` member of `ma_device` which you can access directly since all miniaudio structures are transparent. Initializing the device is done with `ma_device_init()`. This will return a result code telling you what went wrong, if anything. On success it will return `MA_SUCCESS`. After initialization is complete the device will be in a stopped state. To start it, use `ma_device_start()`. Uninitializing the device will stop it, which is what the example above does, but you can also stop the device with `ma_device_stop()`. To resume the device simply call `ma_device_start()` again. Note that it's important to never stop or start the device from inside the callback. This will result in a deadlock. Instead you set a variable or signal an event indicating that the device needs to stop and handle it in a different thread. The following APIs must never be called inside the callback: ```c ma_device_init() ma_device_init_ex() ma_device_uninit() ma_device_start() ma_device_stop() ``` You must never try uninitializing and reinitializing a device inside the callback. You must also never try to stop and start it from inside the callback. There are a few other things you shouldn't do in the callback depending on your requirements, however this isn't so much a thread-safety thing, but rather a real-time processing thing which is beyond the scope of this introduction. The example above demonstrates the initialization of a playback device, but it works exactly the same for capture. All you need to do is change the device type from `ma_device_type_playback` to `ma_device_type_capture` when setting up the config, like so: ```c ma_device_config config = ma_device_config_init(ma_device_type_capture); config.capture.format = MY_FORMAT; config.capture.channels = MY_CHANNEL_COUNT; ``` In the data callback you just read from the input buffer (`pInput` in the example above) and leave the output buffer alone (it will be set to NULL when the device type is set to `ma_device_type_capture`). These are the available device types and how you should handle the buffers in the callback: +-------------------------+--------------------------------------------------------+ | Device Type | Callback Behavior | +-------------------------+--------------------------------------------------------+ | ma_device_type_playback | Write to output buffer, leave input buffer untouched. | | ma_device_type_capture | Read from input buffer, leave output buffer untouched. | | ma_device_type_duplex | Read from input buffer, write to output buffer. | | ma_device_type_loopback | Read from input buffer, leave output buffer untouched. | +-------------------------+--------------------------------------------------------+ You will notice in the example above that the sample format and channel count is specified separately for playback and capture. This is to support different data formats between the playback and capture devices in a full-duplex system. An example may be that you want to capture audio data as a monaural stream (one channel), but output sound to a stereo speaker system. Note that if you use different formats between playback and capture in a full-duplex configuration you will need to convert the data yourself. There are functions available to help you do this which will be explained later. The example above did not specify a physical device to connect to which means it will use the operating system's default device. If you have multiple physical devices connected and you want to use a specific one you will need to specify the device ID in the configuration, like so: ```c config.playback.pDeviceID = pMyPlaybackDeviceID; // Only if requesting a playback or duplex device. config.capture.pDeviceID = pMyCaptureDeviceID; // Only if requesting a capture, duplex or loopback device. ``` To retrieve the device ID you will need to perform device enumeration, however this requires the use of a new concept called the "context". Conceptually speaking the context sits above the device. There is one context to many devices. The purpose of the context is to represent the backend at a more global level and to perform operations outside the scope of an individual device. Mainly it is used for performing run-time linking against backend libraries, initializing backends and enumerating devices. The example below shows how to enumerate devices. ```c ma_context context; if (ma_context_init(NULL, 0, NULL, &context) != MA_SUCCESS) { // Error. } ma_device_info* pPlaybackInfos; ma_uint32 playbackCount; ma_device_info* pCaptureInfos; ma_uint32 captureCount; if (ma_context_get_devices(&context, &pPlaybackInfos, &playbackCount, &pCaptureInfos, &captureCount) != MA_SUCCESS) { // Error. } // Loop over each device info and do something with it. Here we just print the name with their index. You may want // to give the user the opportunity to choose which device they'd prefer. for (ma_uint32 iDevice = 0; iDevice < playbackCount; iDevice += 1) { printf("%d - %s\n", iDevice, pPlaybackInfos[iDevice].name); } ma_device_config config = ma_device_config_init(ma_device_type_playback); config.playback.pDeviceID = &pPlaybackInfos[chosenPlaybackDeviceIndex].id; config.playback.format = MY_FORMAT; config.playback.channels = MY_CHANNEL_COUNT; config.sampleRate = MY_SAMPLE_RATE; config.dataCallback = data_callback; config.pUserData = pMyCustomData; ma_device device; if (ma_device_init(&context, &config, &device) != MA_SUCCESS) { // Error } ... ma_device_uninit(&device); ma_context_uninit(&context); ``` The first thing we do in this example is initialize a `ma_context` object with `ma_context_init()`. The first parameter is a pointer to a list of `ma_backend` values which are used to override the default backend priorities. When this is NULL, as in this example, miniaudio's default priorities are used. The second parameter is the number of backends listed in the array pointed to by the first parameter. The third parameter is a pointer to a `ma_context_config` object which can be NULL, in which case defaults are used. The context configuration is used for setting the logging callback, custom memory allocation callbacks, user-defined data and some backend-specific configurations. Once the context has been initialized you can enumerate devices. In the example above we use the simpler `ma_context_get_devices()`, however you can also use a callback for handling devices by using `ma_context_enumerate_devices()`. When using `ma_context_get_devices()` you provide a pointer to a pointer that will, upon output, be set to a pointer to a buffer containing a list of `ma_device_info` structures. You also provide a pointer to an unsigned integer that will receive the number of items in the returned buffer. Do not free the returned buffers as their memory is managed internally by miniaudio. The `ma_device_info` structure contains an `id` member which is the ID you pass to the device config. It also contains the name of the device which is useful for presenting a list of devices to the user via the UI. When creating your own context you will want to pass it to `ma_device_init()` when initializing the device. Passing in NULL, like we do in the first example, will result in miniaudio creating the context for you, which you don't want to do since you've already created a context. Note that internally the context is only tracked by it's pointer which means you must not change the location of the `ma_context` object. If this is an issue, consider using `malloc()` to allocate memory for the context. 1.2. High Level API ------------------- The high level API consists of three main parts: * Resource management for loading and streaming sounds. * A node graph for advanced mixing and effect processing. * A high level "engine" that wraps around the resource manager and node graph. The resource manager (`ma_resource_manager`) is used for loading sounds. It supports loading sounds fully into memory and also streaming. It will also deal with reference counting for you which avoids the same sound being loaded multiple times. The node graph is used for mixing and effect processing. The idea is that you connect a number of nodes into the graph by connecting each node's outputs to another node's inputs. Each node can implement it's own effect. By chaining nodes together, advanced mixing and effect processing can be achieved. The engine encapsulates both the resource manager and the node graph to create a simple, easy to use high level API. The resource manager and node graph APIs are covered in more later sections of this manual. The code below shows how you can initialize an engine using it's default configuration. ```c ma_result result; ma_engine engine; result = ma_engine_init(NULL, &engine); if (result != MA_SUCCESS) { return result; // Failed to initialize the engine. } ``` This creates an engine instance which will initialize a device internally which you can access with `ma_engine_get_device()`. It will also initialize a resource manager for you which can be accessed with `ma_engine_get_resource_manager()`. The engine itself is a node graph (`ma_node_graph`) which means you can pass a pointer to the engine object into any of the `ma_node_graph` APIs (with a cast). Alternatively, you can use `ma_engine_get_node_graph()` instead of a cast. Note that all objects in miniaudio, including the `ma_engine` object in the example above, are transparent structures. There are no handles to opaque structures in miniaudio which means you need to be mindful of how you declare them. In the example above we are declaring it on the stack, but this will result in the struct being invalidated once the function encapsulating it returns. If allocating the engine on the heap is more appropriate, you can easily do so with a standard call to `malloc()` or whatever heap allocation routine you like: ```c ma_engine* pEngine = malloc(sizeof(*pEngine)); ``` The `ma_engine` API uses the same config/init pattern used all throughout miniaudio. To configure an engine, you can fill out a `ma_engine_config` object and pass it into the first parameter of `ma_engine_init()`: ```c ma_result result; ma_engine engine; ma_engine_config engineConfig; engineConfig = ma_engine_config_init(); engineConfig.pResourceManager = &myCustomResourceManager; // <-- Initialized as some earlier stage. result = ma_engine_init(&engineConfig, &engine); if (result != MA_SUCCESS) { return result; } ``` This creates an engine instance using a custom config. In this particular example it's showing how you can specify a custom resource manager rather than having the engine initialize one internally. This is particularly useful if you want to have multiple engine's share the same resource manager. The engine must be uninitialized with `ma_engine_uninit()` when it's no longer needed. By default the engine will be started, but nothing will be playing because no sounds have been initialized. The easiest but least flexible way of playing a sound is like so: ```c ma_engine_play_sound(&engine, "my_sound.wav", NULL); ``` This plays what miniaudio calls an "inline" sound. It plays the sound once, and then puts the internal sound up for recycling. The last parameter is used to specify which sound group the sound should be associated with which will be explained later. This particular way of playing a sound is simple, but lacks flexibility and features. A more flexible way of playing a sound is to first initialize a sound: ```c ma_result result; ma_sound sound; result = ma_sound_init_from_file(&engine, "my_sound.wav", 0, NULL, NULL, &sound); if (result != MA_SUCCESS) { return result; } ma_sound_start(&sound); ``` This returns a `ma_sound` object which represents a single instance of the specified sound file. If you want to play the same file multiple times simultaneously, you need to create one sound for each instance. Sounds should be uninitialized with `ma_sound_uninit()`. Sounds are not started by default. Start a sound with `ma_sound_start()` and stop it with `ma_sound_stop()`. When a sound is stopped, it is not rewound to the start. Use `ma_sound_seek_to_pcm_frame(&sound, 0)` to seek back to the start of a sound. By default, starting and stopping sounds happens immediately, but sometimes it might be convenient to schedule the sound the be started and/or stopped at a specific time. This can be done with the following functions: ```c ma_sound_set_start_time_in_pcm_frames() ma_sound_set_start_time_in_milliseconds() ma_sound_set_stop_time_in_pcm_frames() ma_sound_set_stop_time_in_milliseconds() ``` The start/stop time needs to be specified based on the absolute timer which is controlled by the engine. The current global time time in PCM frames can be retrieved with `ma_engine_get_time()`. The engine's global time can be changed with `ma_engine_set_time()` for synchronization purposes if required. Note that scheduling a start time still requires an explicit call to `ma_sound_start()` before anything will play: ```c ma_sound_set_start_time_in_pcm_frames(&sound, ma_engine_get_time(&engine) + (ma_engine_get_sample_rate(&engine) * 2); ma_sound_start(&sound); ``` The third parameter of `ma_sound_init_from_file()` is a set of flags that control how the sound be loaded and a few options on which features should be enabled for that sound. By default, the sound is synchronously loaded fully into memory straight from the file system without any kind of decoding. If you want to decode the sound before storing it in memory, you need to specify the `MA_SOUND_FLAG_DECODE` flag. This is useful if you want to incur the cost of decoding at an earlier stage, such as a loading stage. Without this option, decoding will happen dynamically at mixing time which might be too expensive on the audio thread. If you want to load the sound asynchronously, you can specify the `MA_SOUND_FLAG_ASYNC` flag. This will result in `ma_sound_init_from_file()` returning quickly, but the sound will not start playing until the sound has had some audio decoded. The fourth parameter is a pointer to sound group. A sound group is used as a mechanism to organise sounds into groups which have their own effect processing and volume control. An example is a game which might have separate groups for sfx, voice and music. Each of these groups have their own independent volume control. Use `ma_sound_group_init()` or `ma_sound_group_init_ex()` to initialize a sound group. Sounds and sound groups are nodes in the engine's node graph and can be plugged into any `ma_node` API. This makes it possible to connect sounds and sound groups to effect nodes to produce complex effect chains. A sound can have it's volume changed with `ma_sound_set_volume()`. If you prefer decibel volume control you can use `ma_volume_db_to_linear()` to convert from decibel representation to linear. Panning and pitching is supported with `ma_sound_set_pan()` and `ma_sound_set_pitch()`. If you know a sound will never have it's pitch changed with `ma_sound_set_pitch()` or via the doppler effect, you can specify the `MA_SOUND_FLAG_NO_PITCH` flag when initializing the sound for an optimization. By default, sounds and sound groups have spatialization enabled. If you don't ever want to spatialize your sounds, initialize the sound with the `MA_SOUND_FLAG_NO_SPATIALIZATION` flag. The spatialization model is fairly simple and is roughly on feature parity with OpenAL. HRTF and environmental occlusion are not currently supported, but planned for the future. The supported features include: * Sound and listener positioning and orientation with cones * Attenuation models: none, inverse, linear and exponential * Doppler effect Sounds can be faded in and out with `ma_sound_set_fade_in_pcm_frames()`. To check if a sound is currently playing, you can use `ma_sound_is_playing()`. To check if a sound is at the end, use `ma_sound_at_end()`. Looping of a sound can be controlled with `ma_sound_set_looping()`. Use `ma_sound_is_looping()` to check whether or not the sound is looping. 2. Building =========== miniaudio should work cleanly out of the box without the need to download or install any dependencies. See below for platform-specific details. 2.1. Windows ------------ The Windows build should compile cleanly on all popular compilers without the need to configure any include paths nor link to any libraries. The UWP build may require linking to mmdevapi.lib if you get errors about an unresolved external symbol for `ActivateAudioInterfaceAsync()`. 2.2. macOS and iOS ------------------ The macOS build should compile cleanly without the need to download any dependencies nor link to any libraries or frameworks. The iOS build needs to be compiled as Objective-C and will need to link the relevant frameworks but should compile cleanly out of the box with Xcode. Compiling through the command line requires linking to `-lpthread` and `-lm`. Due to the way miniaudio links to frameworks at runtime, your application may not pass Apple's notarization process. To fix this there are two options. The first is to use the `MA_NO_RUNTIME_LINKING` option, like so: ```c #ifdef __APPLE__ #define MA_NO_RUNTIME_LINKING #endif #define MINIAUDIO_IMPLEMENTATION #include "miniaudio.h" ``` This will require linking with `-framework CoreFoundation -framework CoreAudio -framework AudioUnit`. Alternatively, if you would rather keep using runtime linking you can add the following to your entitlements.xcent file: ``` com.apple.security.cs.allow-dyld-environment-variables com.apple.security.cs.allow-unsigned-executable-memory ``` See this discussion for more info: https://github.com/mackron/miniaudio/issues/203. 2.3. Linux ---------- The Linux build only requires linking to `-ldl`, `-lpthread` and `-lm`. You do not need any development packages. You may need to link with `-latomic` if you're compiling for 32-bit ARM. 2.4. BSD -------- The BSD build only requires linking to `-lpthread` and `-lm`. NetBSD uses audio(4), OpenBSD uses sndio and FreeBSD uses OSS. You may need to link with `-latomic` if you're compiling for 32-bit ARM. 2.5. Android ------------ AAudio is the highest priority backend on Android. This should work out of the box without needing any kind of compiler configuration. Support for AAudio starts with Android 8 which means older versions will fall back to OpenSL|ES which requires API level 16+. There have been reports that the OpenSL|ES backend fails to initialize on some Android based devices due to `dlopen()` failing to open "libOpenSLES.so". If this happens on your platform you'll need to disable run-time linking with `MA_NO_RUNTIME_LINKING` and link with -lOpenSLES. 2.6. Emscripten --------------- The Emscripten build emits Web Audio JavaScript directly and should compile cleanly out of the box. You cannot use `-std=c*` compiler flags, nor `-ansi`. 2.7. Build Options ------------------ `#define` these options before including miniaudio.h. +----------------------------------+--------------------------------------------------------------------+ | Option | Description | +----------------------------------+--------------------------------------------------------------------+ | MA_NO_WASAPI | Disables the WASAPI backend. | +----------------------------------+--------------------------------------------------------------------+ | MA_NO_DSOUND | Disables the DirectSound backend. | +----------------------------------+--------------------------------------------------------------------+ | MA_NO_WINMM | Disables the WinMM backend. | +----------------------------------+--------------------------------------------------------------------+ | MA_NO_ALSA | Disables the ALSA backend. | +----------------------------------+--------------------------------------------------------------------+ | MA_NO_PULSEAUDIO | Disables the PulseAudio backend. | +----------------------------------+--------------------------------------------------------------------+ | MA_NO_JACK | Disables the JACK backend. | +----------------------------------+--------------------------------------------------------------------+ | MA_NO_COREAUDIO | Disables the Core Audio backend. | +----------------------------------+--------------------------------------------------------------------+ | MA_NO_SNDIO | Disables the sndio backend. | +----------------------------------+--------------------------------------------------------------------+ | MA_NO_AUDIO4 | Disables the audio(4) backend. | +----------------------------------+--------------------------------------------------------------------+ | MA_NO_OSS | Disables the OSS backend. | +----------------------------------+--------------------------------------------------------------------+ | MA_NO_AAUDIO | Disables the AAudio backend. | +----------------------------------+--------------------------------------------------------------------+ | MA_NO_OPENSL | Disables the OpenSL|ES backend. | +----------------------------------+--------------------------------------------------------------------+ | MA_NO_WEBAUDIO | Disables the Web Audio backend. | +----------------------------------+--------------------------------------------------------------------+ | MA_NO_NULL | Disables the null backend. | +----------------------------------+--------------------------------------------------------------------+ | MA_ENABLE_ONLY_SPECIFIC_BACKENDS | Disables all backends by default and requires `MA_ENABLE_*` to | | | enable specific backends. | +----------------------------------+--------------------------------------------------------------------+ | MA_ENABLE_WASAPI | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to | | | enable the WASAPI backend. | +----------------------------------+--------------------------------------------------------------------+ | MA_ENABLE_DSOUND | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to | | | enable the DirectSound backend. | +----------------------------------+--------------------------------------------------------------------+ | MA_ENABLE_WINMM | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to | | | enable the WinMM backend. | +----------------------------------+--------------------------------------------------------------------+ | MA_ENABLE_ALSA | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to | | | enable the ALSA backend. | +----------------------------------+--------------------------------------------------------------------+ | MA_ENABLE_PULSEAUDIO | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to | | | enable the PulseAudio backend. | +----------------------------------+--------------------------------------------------------------------+ | MA_ENABLE_JACK | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to | | | enable the JACK backend. | +----------------------------------+--------------------------------------------------------------------+ | MA_ENABLE_COREAUDIO | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to | | | enable the Core Audio backend. | +----------------------------------+--------------------------------------------------------------------+ | MA_ENABLE_SNDIO | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to | | | enable the sndio backend. | +----------------------------------+--------------------------------------------------------------------+ | MA_ENABLE_AUDIO4 | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to | | | enable the audio(4) backend. | +----------------------------------+--------------------------------------------------------------------+ | MA_ENABLE_OSS | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to | | | enable the OSS backend. | +----------------------------------+--------------------------------------------------------------------+ | MA_ENABLE_AAUDIO | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to | | | enable the AAudio backend. | +----------------------------------+--------------------------------------------------------------------+ | MA_ENABLE_OPENSL | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to | | | enable the OpenSL|ES backend. | +----------------------------------+--------------------------------------------------------------------+ | MA_ENABLE_WEBAUDIO | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to | | | enable the Web Audio backend. | +----------------------------------+--------------------------------------------------------------------+ | MA_ENABLE_NULL | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to | | | enable the null backend. | +----------------------------------+--------------------------------------------------------------------+ | MA_NO_DECODING | Disables decoding APIs. | +----------------------------------+--------------------------------------------------------------------+ | MA_NO_ENCODING | Disables encoding APIs. | +----------------------------------+--------------------------------------------------------------------+ | MA_NO_WAV | Disables the built-in WAV decoder and encoder. | +----------------------------------+--------------------------------------------------------------------+ | MA_NO_FLAC | Disables the built-in FLAC decoder. | +----------------------------------+--------------------------------------------------------------------+ | MA_NO_MP3 | Disables the built-in MP3 decoder. | +----------------------------------+--------------------------------------------------------------------+ | MA_NO_DEVICE_IO | Disables playback and recording. This will disable `ma_context` | | | and `ma_device` APIs. This is useful if you only want to use | | | miniaudio's data conversion and/or decoding APIs. | +----------------------------------+--------------------------------------------------------------------+ | MA_NO_RESOURCE_MANAGER | Disables the resource manager. When using the engine this will | | | also disable the following functions: | | | | | | ``` | | | ma_sound_init_from_file() | | | ma_sound_init_from_file_w() | | | ma_sound_init_copy() | | | ma_engine_play_sound_ex() | | | ma_engine_play_sound() | | | ``` | | | | | | The only way to initialize a `ma_sound` object is to initialize it | | | from a data source. | +----------------------------------+--------------------------------------------------------------------+ | MA_NO_NODE_GRAPH | Disables the node graph API. This will also disable the engine API | | | because it depends on the node graph. | +----------------------------------+--------------------------------------------------------------------+ | MA_NO_ENGINE | Disables the engine API. | +----------------------------------+--------------------------------------------------------------------+ | MA_NO_THREADING | Disables the `ma_thread`, `ma_mutex`, `ma_semaphore` and | | | `ma_event` APIs. This option is useful if you only need to use | | | miniaudio for data conversion, decoding and/or encoding. Some | | | families of APIs require threading which means the following | | | options must also be set: | | | | | | ``` | | | MA_NO_DEVICE_IO | | | ``` | +----------------------------------+--------------------------------------------------------------------+ | MA_NO_GENERATION | Disables generation APIs such a `ma_waveform` and `ma_noise`. | +----------------------------------+--------------------------------------------------------------------+ | MA_NO_SSE2 | Disables SSE2 optimizations. | +----------------------------------+--------------------------------------------------------------------+ | MA_NO_AVX2 | Disables AVX2 optimizations. | +----------------------------------+--------------------------------------------------------------------+ | MA_NO_NEON | Disables NEON optimizations. | +----------------------------------+--------------------------------------------------------------------+ | MA_NO_RUNTIME_LINKING | Disables runtime linking. This is useful for passing Apple's | | | notarization process. When enabling this, you may need to avoid | | | using `-std=c89` or `-std=c99` on Linux builds or else you may end | | | up with compilation errors due to conflicts with `timespec` and | | | `timeval` data types. | | | | | | You may need to enable this if your target platform does not allow | | | runtime linking via `dlopen()`. | +----------------------------------+--------------------------------------------------------------------+ | MA_DEBUG_OUTPUT | Enable `printf()` output of debug logs (`MA_LOG_LEVEL_DEBUG`). | +----------------------------------+--------------------------------------------------------------------+ | MA_COINIT_VALUE | Windows only. The value to pass to internal calls to | | | `CoInitializeEx()`. Defaults to `COINIT_MULTITHREADED`. | +----------------------------------+--------------------------------------------------------------------+ | MA_API | Controls how public APIs should be decorated. Default is `extern`. | +----------------------------------+--------------------------------------------------------------------+ 3. Definitions ============== This section defines common terms used throughout miniaudio. Unfortunately there is often ambiguity in the use of terms throughout the audio space, so this section is intended to clarify how miniaudio uses each term. 3.1. Sample ----------- A sample is a single unit of audio data. If the sample format is f32, then one sample is one 32-bit floating point number. 3.2. Frame / PCM Frame ---------------------- A frame is a group of samples equal to the number of channels. For a stereo stream a frame is 2 samples, a mono frame is 1 sample, a 5.1 surround sound frame is 6 samples, etc. The terms "frame" and "PCM frame" are the same thing in miniaudio. Note that this is different to a compressed frame. If ever miniaudio needs to refer to a compressed frame, such as a FLAC frame, it will always clarify what it's referring to with something like "FLAC frame". 3.3. Channel ------------ A stream of monaural audio that is emitted from an individual speaker in a speaker system, or received from an individual microphone in a microphone system. A stereo stream has two channels (a left channel, and a right channel), a 5.1 surround sound system has 6 channels, etc. Some audio systems refer to a channel as a complex audio stream that's mixed with other channels to produce the final mix - this is completely different to miniaudio's use of the term "channel" and should not be confused. 3.4. Sample Rate ---------------- The sample rate in miniaudio is always expressed in Hz, such as 44100, 48000, etc. It's the number of PCM frames that are processed per second. 3.5. Formats ------------ Throughout miniaudio you will see references to different sample formats: +---------------+----------------------------------------+---------------------------+ | Symbol | Description | Range | +---------------+----------------------------------------+---------------------------+ | ma_format_f32 | 32-bit floating point | [-1, 1] | | ma_format_s16 | 16-bit signed integer | [-32768, 32767] | | ma_format_s24 | 24-bit signed integer (tightly packed) | [-8388608, 8388607] | | ma_format_s32 | 32-bit signed integer | [-2147483648, 2147483647] | | ma_format_u8 | 8-bit unsigned integer | [0, 255] | +---------------+----------------------------------------+---------------------------+ All formats are native-endian. 4. Data Sources =============== The data source abstraction in miniaudio is used for retrieving audio data from some source. A few examples include `ma_decoder`, `ma_noise` and `ma_waveform`. You will need to be familiar with data sources in order to make sense of some of the higher level concepts in miniaudio. The `ma_data_source` API is a generic interface for reading from a data source. Any object that implements the data source interface can be plugged into any `ma_data_source` function. To read data from a data source: ```c ma_result result; ma_uint64 framesRead; result = ma_data_source_read_pcm_frames(pDataSource, pFramesOut, frameCount, &framesRead, loop); if (result != MA_SUCCESS) { return result; // Failed to read data from the data source. } ``` If you don't need the number of frames that were successfully read you can pass in `NULL` to the `pFramesRead` parameter. If this returns a value less than the number of frames requested it means the end of the file has been reached. `MA_AT_END` will be returned only when the number of frames read is 0. When calling any data source function, with the exception of `ma_data_source_init()` and `ma_data_source_uninit()`, you can pass in any object that implements a data source. For example, you could plug in a decoder like so: ```c ma_result result; ma_uint64 framesRead; ma_decoder decoder; // <-- This would be initialized with `ma_decoder_init_*()`. result = ma_data_source_read_pcm_frames(&decoder, pFramesOut, frameCount, &framesRead, loop); if (result != MA_SUCCESS) { return result; // Failed to read data from the decoder. } ``` If you want to seek forward you can pass in `NULL` to the `pFramesOut` parameter. Alternatively you can use `ma_data_source_seek_pcm_frames()`. To seek to a specific PCM frame: ```c result = ma_data_source_seek_to_pcm_frame(pDataSource, frameIndex); if (result != MA_SUCCESS) { return result; // Failed to seek to PCM frame. } ``` You can retrieve the total length of a data source in PCM frames, but note that some data sources may not have the notion of a length, such as noise and waveforms, and others may just not have a way of determining the length such as some decoders. To retrieve the length: ```c ma_uint64 length; result = ma_data_source_get_length_in_pcm_frames(pDataSource, &length); if (result != MA_SUCCESS) { return result; // Failed to retrieve the length. } ``` Care should be taken when retrieving the length of a data source where the underlying decoder is pulling data from a data stream with an undefined length, such as internet radio or some kind of broadcast. If you do this, `ma_data_source_get_length_in_pcm_frames()` may never return. The current position of the cursor in PCM frames can also be retrieved: ```c ma_uint64 cursor; result = ma_data_source_get_cursor_in_pcm_frames(pDataSource, &cursor); if (result != MA_SUCCESS) { return result; // Failed to retrieve the cursor. } ``` You will often need to know the data format that will be returned after reading. This can be retrieved like so: ```c ma_format format; ma_uint32 channels; ma_uint32 sampleRate; ma_channel channelMap[MA_MAX_CHANNELS]; result = ma_data_source_get_data_format(pDataSource, &format, &channels, &sampleRate, channelMap, MA_MAX_CHANNELS); if (result != MA_SUCCESS) { return result; // Failed to retrieve data format. } ``` If you do not need a specific data format property, just pass in NULL to the respective parameter. There may be cases where you want to implement something like a sound bank where you only want to read data within a certain range of the underlying data. To do this you can use a range: ```c result = ma_data_source_set_range_in_pcm_frames(pDataSource, rangeBegInFrames, rangeEndInFrames); if (result != MA_SUCCESS) { return result; // Failed to set the range. } ``` This is useful if you have a sound bank where many sounds are stored in the same file and you want the data source to only play one of those sub-sounds. Custom loop points can also be used with data sources. By default, data sources will loop after they reach the end of the data source, but if you need to loop at a specific location, you can do the following: ```c result = ma_data_set_loop_point_in_pcm_frames(pDataSource, loopBegInFrames, loopEndInFrames); if (result != MA_SUCCESS) { return result; // Failed to set the loop point. } ``` The loop point is relative to the current range. It's sometimes useful to chain data sources together so that a seamless transition can be achieved. To do this, you can use chaining: ```c ma_decoder decoder1; ma_decoder decoder2; // ... initialize decoders with ma_decoder_init_*() ... result = ma_data_source_set_next(&decoder1, &decoder2); if (result != MA_SUCCESS) { return result; // Failed to set the next data source. } result = ma_data_source_read_pcm_frames(&decoder1, pFramesOut, frameCount, pFramesRead, MA_FALSE); if (result != MA_SUCCESS) { return result; // Failed to read from the decoder. } ``` In the example above we're using decoders. When reading from a chain, you always want to read from the top level data source in the chain. In the example above, `decoder1` is the top level data source in the chain. When `decoder1` reaches the end, `decoder2` will start seamlessly without any gaps. Note that the `loop` parameter is set to false in the example above. When this is set to true, only the current data source will be looped. You can loop the entire chain by linking in a loop like so: ```c ma_data_source_set_next(&decoder1, &decoder2); // decoder1 -> decoder2 ma_data_source_set_next(&decoder2, &decoder1); // decoder2 -> decoder1 (loop back to the start). ``` Note that setting up chaining is not thread safe, so care needs to be taken if you're dynamically changing links while the audio thread is in the middle of reading. Do not use `ma_decoder_seek_to_pcm_frame()` as a means to reuse a data source to play multiple instances of the same sound simultaneously. Instead, initialize multiple data sources for each instance. This can be extremely inefficient depending on the data source and can result in glitching due to subtle changes to the state of internal filters. 4.1. Custom Data Sources ------------------------ You can implement a custom data source by implementing the functions in `ma_data_source_vtable`. Your custom object must have `ma_data_source_base` as it's first member: ```c struct my_data_source { ma_data_source_base base; ... }; ``` In your initialization routine, you need to call `ma_data_source_init()` in order to set up the base object (`ma_data_source_base`): ```c static ma_result my_data_source_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) { // Read data here. Output in the same format returned by my_data_source_get_data_format(). } static ma_result my_data_source_seek(ma_data_source* pDataSource, ma_uint64 frameIndex) { // Seek to a specific PCM frame here. Return MA_NOT_IMPLEMENTED if seeking is not supported. } static ma_result my_data_source_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) { // Return the format of the data here. } static ma_result my_data_source_get_cursor(ma_data_source* pDataSource, ma_uint64* pCursor) { // Retrieve the current position of the cursor here. Return MA_NOT_IMPLEMENTED and set *pCursor to 0 if there is no notion of a cursor. } static ma_result my_data_source_get_length(ma_data_source* pDataSource, ma_uint64* pLength) { // Retrieve the length in PCM frames here. Return MA_NOT_IMPLEMENTED and set *pLength to 0 if there is no notion of a length or if the length is unknown. } static g_my_data_source_vtable = { my_data_source_read, my_data_source_seek, my_data_source_get_data_format, my_data_source_get_cursor, my_data_source_get_length }; ma_result my_data_source_init(my_data_source* pMyDataSource) { ma_result result; ma_data_source_config baseConfig; baseConfig = ma_data_source_config_init(); baseConfig.vtable = &g_my_data_source_vtable; result = ma_data_source_init(&baseConfig, &pMyDataSource->base); if (result != MA_SUCCESS) { return result; } // ... do the initialization of your custom data source here ... return MA_SUCCESS; } void my_data_source_uninit(my_data_source* pMyDataSource) { // ... do the uninitialization of your custom data source here ... // You must uninitialize the base data source. ma_data_source_uninit(&pMyDataSource->base); } ``` Note that `ma_data_source_init()` and `ma_data_source_uninit()` are never called directly outside of the custom data source. It's up to the custom data source itself to call these within their own init/uninit functions. 5. Engine ========= The `ma_engine` API is a high level API for managing and mixing sounds and effect processing. The `ma_engine` object encapsulates a resource manager and a node graph, both of which will be explained in more detail later. Sounds are called `ma_sound` and are created from an engine. Sounds can be associated with a mixing group called `ma_sound_group` which are also created from the engine. Both `ma_sound` and `ma_sound_group` objects are nodes within the engine's node graph. When the engine is initialized, it will normally create a device internally. If you would rather manage the device yourself, you can do so and just pass a pointer to it via the engine config when you initialize the engine. You can also just use the engine without a device, which again can be configured via the engine config. The most basic way to initialize the engine is with a default config, like so: ```c ma_result result; ma_engine engine; result = ma_engine_init(NULL, &engine); if (result != MA_SUCCESS) { return result; // Failed to initialize the engine. } ``` This will result in the engine initializing a playback device using the operating system's default device. This will be sufficient for many use cases, but if you need more flexibility you'll want to configure the engine with an engine config: ```c ma_result result; ma_engine engine; ma_engine_config engineConfig; engineConfig = ma_engine_config_init(); engineConfig.pPlaybackDevice = &myDevice; result = ma_engine_init(&engineConfig, &engine); if (result != MA_SUCCESS) { return result; // Failed to initialize the engine. } ``` In the example above we're passing in a pre-initialized device. Since the caller is the one in control of the device's data callback, it's their responsibility to manually call `ma_engine_read_pcm_frames()` from inside their data callback: ```c void playback_data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount) { ma_engine_read_pcm_frames(&g_Engine, pOutput, frameCount, NULL); } ``` You can also use the engine independent of a device entirely: ```c ma_result result; ma_engine engine; ma_engine_config engineConfig; engineConfig = ma_engine_config_init(); engineConfig.noDevice = MA_TRUE; engineConfig.channels = 2; // Must be set when not using a device. engineConfig.sampleRate = 48000; // Must be set when not using a device. result = ma_engine_init(&engineConfig, &engine); if (result != MA_SUCCESS) { return result; // Failed to initialize the engine. } ``` Note that when you're not using a device, you must set the channel count and sample rate in the config or else miniaudio won't know what to use (miniaudio will use the device to determine this normally). When not using a device, you need to use `ma_engine_read_pcm_frames()` to process audio data from the engine. This kind of setup is useful if you want to do something like offline processing. When a sound is loaded it goes through a resource manager. By default the engine will initialize a resource manager internally, but you can also specify a pre-initialized resource manager: ```c ma_result result; ma_engine engine1; ma_engine engine2; ma_engine_config engineConfig; engineConfig = ma_engine_config_init(); engineConfig.pResourceManager = &myResourceManager; ma_engine_init(&engineConfig, &engine1); ma_engine_init(&engineConfig, &engine2); ``` In this example we are initializing two engines, both of which are sharing the same resource manager. This is especially useful for saving memory when loading the same file across multiple engines. If you were not to use a shared resource manager, each engine instance would use their own which would result in any sounds that are used between both engine's being loaded twice. By using a shared resource manager, it would only be loaded once. Using multiple engine's is useful when you need to output to multiple playback devices, such as in a local multiplayer game where each player is using their own set of headphones. By default an engine will be in a started state. To make it so the engine is not automatically started you can configure it as such: ```c engineConfig.noAutoStart = MA_TRUE; // The engine will need to be started manually. ma_engine_start(&engine); // Later on the engine can be stopped with ma_engine_stop(). ma_engine_stop(&engine); ``` The concept of starting or stopping an engine is only relevant when using the engine with a device. Attempting to start or stop an engine that is not associated with a device will result in `MA_INVALID_OPERATION`. The master volume of the engine can be controlled with `ma_engine_set_volume()` which takes a linear scale, with 0 resulting in silence and anything above 1 resulting in amplification. If you prefer decibel based volume control, use `ma_volume_db_to_linear()` to convert from dB to linear. When a sound is spatialized, it is done so relative to a listener. An engine can be configured to have multiple listeners which can be configured via the config: ```c engineConfig.listenerCount = 2; ``` The maximum number of listeners is restricted to `MA_ENGINE_MAX_LISTENERS`. By default, when a sound is spatialized, it will be done so relative to the closest listener. You can also pin a sound to a specific listener which will be explained later. Listener's have a position, direction, cone, and velocity (for doppler effect). A listener is referenced by an index, the meaning of which is up to the caller (the index is 0 based and cannot go beyond the listener count, minus 1). The position, direction and velocity are all specified in absolute terms: ```c ma_engine_listener_set_position(&engine, listenerIndex, worldPosX, worldPosY, worldPosZ); ``` The direction of the listener represents it's forward vector. The listener's up vector can also be specified and defaults to +1 on the Y axis. ```c ma_engine_listener_set_direction(&engine, listenerIndex, forwardX, forwardY, forwardZ); ma_engine_listener_set_world_up(&engine, listenerIndex, 0, 1, 0); ``` The engine supports directional attenuation. The listener can have a cone the controls how sound is attenuated based on the listener's direction. When a sound is between the inner and outer cones, it will be attenuated between 1 and the cone's outer gain: ```c ma_engine_listener_set_cone(&engine, listenerIndex, innerAngleInRadians, outerAngleInRadians, outerGain); ``` When a sound is inside the inner code, no directional attenuation is applied. When the sound is outside of the outer cone, the attenuation will be set to `outerGain` in the example above. When the sound is in between the inner and outer cones, the attenuation will be interpolated between 1 and the outer gain. The engine's coordinate system follows the OpenGL coordinate system where positive X points right, positive Y points up and negative Z points forward. The simplest and least flexible way to play a sound is like so: ```c ma_engine_play_sound(&engine, "my_sound.wav", pGroup); ``` This is a "fire and forget" style of function. The engine will manage the `ma_sound` object internally. When the sound finishes playing, it'll be put up for recycling. For more flexibility you'll want to initialize a sound object: ```c ma_sound sound; result = ma_sound_init_from_file(&engine, "my_sound.wav", flags, pGroup, NULL, &sound); if (result != MA_SUCCESS) { return result; // Failed to load sound. } ``` Sounds need to be uninitialized with `ma_sound_uninit()`. The example above loads a sound from a file. If the resource manager has been disabled you will not be able to use this function and instead you'll need to initialize a sound directly from a data source: ```c ma_sound sound; result = ma_sound_init_from_data_source(&engine, &dataSource, flags, pGroup, &sound); if (result != MA_SUCCESS) { return result; } ``` Each `ma_sound` object represents a single instance of the sound. If you want to play the same sound multiple times at the same time, you need to initialize a separate `ma_sound` object. For the most flexibility when initializing sounds, use `ma_sound_init_ex()`. This uses miniaudio's standard config/init pattern: ```c ma_sound sound; ma_sound_config soundConfig; soundConfig = ma_sound_config_init(); soundConfig.pFilePath = NULL; // Set this to load from a file path. soundConfig.pDataSource = NULL; // Set this to initialize from an existing data source. soundConfig.pInitialAttachment = &someNodeInTheNodeGraph; soundConfig.initialAttachmentInputBusIndex = 0; soundConfig.channelsIn = 1; soundConfig.channelsOut = 0; // Set to 0 to use the engine's native channel count. result = ma_sound_init_ex(&soundConfig, &sound); if (result != MA_SUCCESS) { return result; } ``` In the example above, the sound is being initialized without a file nor a data source. This is valid, in which case the sound acts as a node in the middle of the node graph. This means you can connect other sounds to this sound and allow it to act like a sound group. Indeed, this is exactly what a `ma_sound_group` is. When loading a sound, you specify a set of flags that control how the sound is loaded and what features are enabled for that sound. When no flags are set, the sound will be fully loaded into memory in exactly the same format as how it's stored on the file system. The resource manager will allocate a block of memory and then load the file directly into it. When reading audio data, it will be decoded dynamically on the fly. In order to save processing time on the audio thread, it might be beneficial to pre-decode the sound. You can do this with the `MA_SOUND_FLAG_DECODE` flag: ```c ma_sound_init_from_file(&engine, "my_sound.wav", MA_SOUND_FLAG_DECODE, pGroup, NULL, &sound); ``` By default, sounds will be loaded synchronously, meaning `ma_sound_init_*()` will not return until the sound has been fully loaded. If this is prohibitive you can instead load sounds asynchronously by specificying the `MA_SOUND_FLAG_ASYNC` flag: ```c ma_sound_init_from_file(&engine, "my_sound.wav", MA_SOUND_FLAG_DECODE | MA_SOUND_FLAG_ASYNC, pGroup, NULL, &sound); ``` This will result in `ma_sound_init_*()` returning quickly, but the sound won't yet have been fully loaded. When you start the sound, it won't output anything until some sound is available. The sound will start outputting audio before the sound has been fully decoded when the `MA_SOUND_FLAG_DECODE` is specified. If you need to wait for an asynchronously loaded sound to be fully loaded, you can use a fence. A fence in miniaudio is a simple synchronization mechanism which simply blocks until it's internal counter hit's zero. You can specify a fence like so: ```c ma_result result; ma_fence fence; ma_sound sounds[4]; result = ma_fence_init(&fence); if (result != MA_SUCCES) { return result; } // Load some sounds asynchronously. for (int iSound = 0; iSound < 4; iSound += 1) { ma_sound_init_from_file(&engine, mySoundFilesPaths[iSound], MA_SOUND_FLAG_DECODE | MA_SOUND_FLAG_ASYNC, pGroup, &fence, &sounds[iSound]); } // ... do some other stuff here in the mean time ... // Wait for all sounds to finish loading. ma_fence_wait(&fence); ``` If loading the entire sound into memory is prohibitive, you can also configure the engine to stream the audio data: ```c ma_sound_init_from_file(&engine, "my_sound.wav", MA_SOUND_FLAG_STREAM, pGroup, NULL, &sound); ``` When streaming sounds, 2 seconds worth of audio data is stored in memory. Although it should work fine, it's inefficient to use streaming for short sounds. Streaming is useful for things like music tracks in games. When loading a sound from a file path, the engine will reference count the file to prevent it from being loaded if it's already in memory. When you uninitialize a sound, the reference count will be decremented, and if it hits zero, the sound will be unloaded from memory. This reference counting system is not used for streams. The engine will use a 64-bit hash of the file name when comparing file paths which means there's a small chance you might encounter a name collision. If this is an issue, you'll need to use a different name for one of the colliding file paths, or just not load from files and instead load from a data source. When you initialize a sound, if you specify a sound group the sound will be attached to that group automatically. If you set it to NULL, it will be automatically attached to the engine's endpoint. If you would instead rather leave the sound unattached by default, you can can specify the `MA_SOUND_FLAG_NO_DEFAULT_ATTACHMENT` flag. This is useful if you want to set up a complex node graph. Sounds are not started by default. To start a sound, use `ma_sound_start()`. Stop a sound with `ma_sound_stop()`. Sounds can have their volume controlled with `ma_sound_set_volume()` in the same way as the engine's master volume. Sounds support stereo panning and pitching. Set the pan with `ma_sound_set_pan()`. Setting the pan to 0 will result in an unpanned sound. Setting it to -1 will shift everything to the left, whereas +1 will shift it to the right. The pitch can be controlled with `ma_sound_set_pitch()`. A larger value will result in a higher pitch. The pitch must be greater than 0. The engine supports 3D spatialization of sounds. By default sounds will have spatialization enabled, but if a sound does not need to be spatialized it's best to disable it. There are two ways to disable spatialization of a sound: ```c // Disable spatialization at initialization time via a flag: ma_sound_init_from_file(&engine, "my_sound.wav", MA_SOUND_FLAG_NO_SPATIALIZATION, NULL, NULL, &sound); // Dynamically disable or enable spatialization post-initialization: ma_sound_set_spatialization_enabled(&sound, isSpatializationEnabled); ``` By default sounds will be spatialized based on the closest listener. If a sound should always be spatialized relative to a specific listener it can be pinned to one: ```c ma_sound_set_pinned_listener_index(&sound, listenerIndex); ``` Like listeners, sounds have a position. By default, the position of a sound is in absolute space, but it can be changed to be relative to a listener: ```c ma_sound_set_positioning(&sound, ma_positioning_relative); ``` Note that relative positioning of a sound only makes sense if there is either only one listener, or the sound is pinned to a specific listener. To set the position of a sound: ```c ma_sound_set_position(&sound, posX, posY, posZ); ``` The direction works the same way as a listener and represents the sound's forward direction: ```c ma_sound_set_direction(&sound, forwardX, forwardY, forwardZ); ``` Sound's also have a cone for controlling directional attenuation. This works exactly the same as listeners: ```c ma_sound_set_cone(&sound, innerAngleInRadians, outerAngleInRadians, outerGain); ``` The velocity of a sound is used for doppler effect and can be set as such: ```c ma_sound_set_velocity(&sound, velocityX, velocityY, velocityZ); ``` The engine supports different attenuation models which can be configured on a per-sound basis. By default the attenuation model is set to `ma_attenuation_model_inverse` which is the equivalent to OpenAL's `AL_INVERSE_DISTANCE_CLAMPED`. Configure the attenuation model like so: ```c ma_sound_set_attenuation_model(&sound, ma_attenuation_model_inverse); ``` The supported attenuation models include the following: +----------------------------------+----------------------------------------------+ | ma_attenuation_model_none | No distance attenuation. | +----------------------------------+----------------------------------------------+ | ma_attenuation_model_inverse | Equivalent to `AL_INVERSE_DISTANCE_CLAMPED`. | +----------------------------------+----------------------------------------------+ | ma_attenuation_model_linear | Linear attenuation. | +----------------------------------+----------------------------------------------+ | ma_attenuation_model_exponential | Exponential attenuation. | +----------------------------------+----------------------------------------------+ To control how quickly a sound rolls off as it moves away from the listener, you need to configure the rolloff: ```c ma_sound_set_rolloff(&sound, rolloff); ``` You can control the minimum and maximum gain to apply from spatialization: ```c ma_sound_set_min_gain(&sound, minGain); ma_sound_set_max_gain(&sound, maxGain); ``` Likewise, in the calculation of attenuation, you can control the minimum and maximum distances for the attenuation calculation. This is useful if you want to ensure sounds don't drop below a certain volume after the listener moves further away and to have sounds play a maximum volume when the listener is within a certain distance: ```c ma_sound_set_min_distance(&sound, minDistance); ma_sound_set_max_distance(&sound, maxDistance); ``` The engine's spatialization system supports doppler effect. The doppler factor can be configure on a per-sound basis like so: ```c ma_sound_set_doppler_factor(&sound, dopplerFactor); ``` You can fade sounds in and out with `ma_sound_set_fade_in_pcm_frames()` and `ma_sound_set_fade_in_milliseconds()`. Set the volume to -1 to use the current volume as the starting volume: ```c // Fade in over 1 second. ma_sound_set_fade_in_milliseconds(&sound, 0, 1, 1000); // ... sometime later ... // Fade out over 1 second, starting from the current volume. ma_sound_set_fade_in_milliseconds(&sound, -1, 0, 1000); ``` By default sounds will start immediately, but sometimes for timing and synchronization purposes it can be useful to schedule a sound to start or stop: ```c // Start the sound in 1 second from now. ma_sound_set_start_time_in_pcm_frames(&sound, ma_engine_get_time(&engine) + (ma_engine_get_sample_rate(&engine) * 1)); // Stop the sound in 2 seconds from now. ma_sound_set_stop_time_in_pcm_frames(&sound, ma_engine_get_time(&engine) + (ma_engine_get_sample_rate(&engine) * 2)); ``` Note that scheduling a start time still requires an explicit call to `ma_sound_start()` before anything will play. The time is specified in global time which is controlled by the engine. You can get the engine's current time with `ma_engine_get_time()`. The engine's global time is incremented automatically as audio data is read, but it can be reset with `ma_engine_set_time()` in case it needs to be resynchronized for some reason. To determine whether or not a sound is currently playing, use `ma_sound_is_playing()`. This will take the scheduled start and stop times into account. Whether or not a sound should loop can be controlled with `ma_sound_set_looping()`. Sounds will not be looping by default. Use `ma_sound_is_looping()` to determine whether or not a sound is looping. Use `ma_sound_at_end()` to determine whether or not a sound is currently at the end. For a looping sound this should never return true. Internally a sound wraps around a data source. Some APIs exist to control the underlying data source, mainly for convenience: ```c ma_sound_seek_to_pcm_frame(&sound, frameIndex); ma_sound_get_data_format(&sound, &format, &channels, &sampleRate, pChannelMap, channelMapCapacity); ma_sound_get_cursor_in_pcm_frames(&sound, &cursor); ma_sound_get_length_in_pcm_frames(&sound, &length); ``` Sound groups have the same API as sounds, only they are called `ma_sound_group`, and since they do not have any notion of a data source, anything relating to a data source is unavailable. Internally, sound data is loaded via the `ma_decoder` API which means by default it only supports file formats that have built-in support in miniaudio. You can extend this to support any kind of file format through the use of custom decoders. To do this you'll need to use a self-managed resource manager and configure it appropriately. See the "Resource Management" section below for details on how to set this up. 6. Resource Management ====================== Many programs will want to manage sound resources for things such as reference counting and streaming. This is supported by miniaudio via the `ma_resource_manager` API. The resource manager is mainly responsible for the following: * Loading of sound files into memory with reference counting. * Streaming of sound data. When loading a sound file, the resource manager will give you back a `ma_data_source` compatible object called `ma_resource_manager_data_source`. This object can be passed into any `ma_data_source` API which is how you can read and seek audio data. When loading a sound file, you specify whether or not you want the sound to be fully loaded into memory (and optionally pre-decoded) or streamed. When loading into memory, you can also specify whether or not you want the data to be loaded asynchronously. The example below is how you can initialize a resource manager using it's default configuration: ```c ma_resource_manager_config config; ma_resource_manager resourceManager; config = ma_resource_manager_config_init(); result = ma_resource_manager_init(&config, &resourceManager); if (result != MA_SUCCESS) { ma_device_uninit(&device); printf("Failed to initialize the resource manager."); return -1; } ``` You can configure the format, channels and sample rate of the decoded audio data. By default it will use the file's native data format, but you can configure it to use a consistent format. This is useful for offloading the cost of data conversion to load time rather than dynamically converting at mixing time. To do this, you configure the decoded format, channels and sample rate like the code below: ```c config = ma_resource_manager_config_init(); config.decodedFormat = device.playback.format; config.decodedChannels = device.playback.channels; config.decodedSampleRate = device.sampleRate; ``` In the code above, the resource manager will be configured so that any decoded audio data will be pre-converted at load time to the device's native data format. If instead you used defaults and the data format of the file did not match the device's data format, you would need to convert the data at mixing time which may be prohibitive in high-performance and large scale scenarios like games. Internally the resource manager uses the `ma_decoder` API to load sounds. This means by default it only supports decoders that are built into miniaudio. It's possible to support additional encoding formats through the use of custom decoders. To do so, pass in your `ma_decoding_backend_vtable` vtables into the resource manager config: ```c ma_decoding_backend_vtable* pCustomBackendVTables[] = { &g_ma_decoding_backend_vtable_libvorbis, &g_ma_decoding_backend_vtable_libopus }; ... resourceManagerConfig.ppCustomDecodingBackendVTables = pCustomBackendVTables; resourceManagerConfig.customDecodingBackendCount = sizeof(pCustomBackendVTables) / sizeof(pCustomBackendVTables[0]); resourceManagerConfig.pCustomDecodingBackendUserData = NULL; ``` This system can allow you to support any kind of file format. See the "Decoding" section for details on how to implement custom decoders. The miniaudio repository includes examples for Opus via libopus and libopusfile and Vorbis via libvorbis and libvorbisfile. Asynchronicity is achieved via a job system. When an operation needs to be performed, such as the decoding of a page, a job will be posted to a queue which will then be processed by a job thread. By default there will be only one job thread running, but this can be configured, like so: ```c config = ma_resource_manager_config_init(); config.jobThreadCount = MY_JOB_THREAD_COUNT; ``` By default job threads are managed internally by the resource manager, however you can also self manage your job threads if, for example, you want to integrate the job processing into your existing job infrastructure, or if you simply don't like the way the resource manager does it. To do this, just set the job thread count to 0 and process jobs manually. To process jobs, you first need to retrieve a job using `ma_resource_manager_next_job()` and then process it using `ma_job_process()`: ```c config = ma_resource_manager_config_init(); config.jobThreadCount = 0; // Don't manage any job threads internally. config.flags = MA_RESOURCE_MANAGER_FLAG_NON_BLOCKING; // Optional. Makes `ma_resource_manager_next_job()` non-blocking. // ... Initialize your custom job threads ... void my_custom_job_thread(...) { for (;;) { ma_job job; ma_result result = ma_resource_manager_next_job(pMyResourceManager, &job); if (result != MA_SUCCESS) { if (result == MA_NO_DATA_AVAILABLE) { // No jobs are available. Keep going. Will only get this if the resource manager was initialized // with MA_RESOURCE_MANAGER_FLAG_NON_BLOCKING. continue; } else if (result == MA_CANCELLED) { // MA_JOB_TYPE_QUIT was posted. Exit. break; } else { // Some other error occurred. break; } } ma_job_process(&job); } } ``` In the example above, the `MA_JOB_TYPE_QUIT` event is the used as the termination indicator, but you can use whatever you would like to terminate the thread. The call to `ma_resource_manager_next_job()` is blocking by default, but can be configured to be non-blocking by initializing the resource manager with the `MA_RESOURCE_MANAGER_FLAG_NON_BLOCKING` configuration flag. Note that the `MA_JOB_TYPE_QUIT` will never be removed from the job queue. This is to give every thread the opportunity to catch the event and terminate naturally. When loading a file, it's sometimes convenient to be able to customize how files are opened and read instead of using standard `fopen()`, `fclose()`, etc. which is what miniaudio will use by default. This can be done by setting `pVFS` member of the resource manager's config: ```c // Initialize your custom VFS object. See documentation for VFS for information on how to do this. my_custom_vfs vfs = my_custom_vfs_init(); config = ma_resource_manager_config_init(); config.pVFS = &vfs; ``` This is particularly useful in programs like games where you want to read straight from an archive rather than the normal file system. If you do not specify a custom VFS, the resource manager will use the operating system's normal file operations. To load a sound file and create a data source, call `ma_resource_manager_data_source_init()`. When loading a sound you need to specify the file path and options for how the sounds should be loaded. By default a sound will be loaded synchronously. The returned data source is owned by the caller which means the caller is responsible for the allocation and freeing of the data source. Below is an example for initializing a data source: ```c ma_resource_manager_data_source dataSource; ma_result result = ma_resource_manager_data_source_init(pResourceManager, pFilePath, flags, &dataSource); if (result != MA_SUCCESS) { // Error. } // ... // A ma_resource_manager_data_source object is compatible with the `ma_data_source` API. To read data, just call // the `ma_data_source_read_pcm_frames()` like you would with any normal data source. result = ma_data_source_read_pcm_frames(&dataSource, pDecodedData, frameCount, &framesRead); if (result != MA_SUCCESS) { // Failed to read PCM frames. } // ... ma_resource_manager_data_source_uninit(pResourceManager, &dataSource); ``` The `flags` parameter specifies how you want to perform loading of the sound file. It can be a combination of the following flags: ``` MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_DECODE MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT ``` When no flags are specified (set to 0), the sound will be fully loaded into memory, but not decoded, meaning the raw file data will be stored in memory, and then dynamically decoded when `ma_data_source_read_pcm_frames()` is called. To instead decode the audio data before storing it in memory, use the `MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_DECODE` flag. By default, the sound file will be loaded synchronously, meaning `ma_resource_manager_data_source_init()` will only return after the entire file has been loaded. This is good for simplicity, but can be prohibitively slow. You can instead load the sound asynchronously using the `MA_RESOURCE_MANAGER_DATA_SOURCE_ASYNC` flag. This will result in `ma_resource_manager_data_source_init()` returning quickly, but no data will be returned by `ma_data_source_read_pcm_frames()` until some data is available. When no data is available because the asynchronous decoding hasn't caught up, `MA_BUSY` will be returned by `ma_data_source_read_pcm_frames()`. For large sounds, it's often prohibitive to store the entire file in memory. To mitigate this, you can instead stream audio data which you can do by specifying the `MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM` flag. When streaming, data will be decoded in 1 second pages. When a new page needs to be decoded, a job will be posted to the job queue and then subsequently processed in a job thread. For in-memory sounds, reference counting is used to ensure the data is loaded only once. This means multiple calls to `ma_resource_manager_data_source_init()` with the same file path will result in the file data only being loaded once. Each call to `ma_resource_manager_data_source_init()` must be matched up with a call to `ma_resource_manager_data_source_uninit()`. Sometimes it can be useful for a program to register self-managed raw audio data and associate it with a file path. Use the `ma_resource_manager_register_*()` and `ma_resource_manager_unregister_*()` APIs to do this. `ma_resource_manager_register_decoded_data()` is used to associate a pointer to raw, self-managed decoded audio data in the specified data format with the specified name. Likewise, `ma_resource_manager_register_encoded_data()` is used to associate a pointer to raw self-managed encoded audio data (the raw file data) with the specified name. Note that these names need not be actual file paths. When `ma_resource_manager_data_source_init()` is called (without the `MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM` flag), the resource manager will look for these explicitly registered data buffers and, if found, will use it as the backing data for the data source. Note that the resource manager does *not* make a copy of this data so it is up to the caller to ensure the pointer stays valid for it's lifetime. Use `ma_resource_manager_unregister_data()` to unregister the self-managed data. You can also use `ma_resource_manager_register_file()` and `ma_resource_manager_unregister_file()` to register and unregister a file. It does not make sense to use the `MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM` flag with a self-managed data pointer. 6.1. Asynchronous Loading and Synchronization --------------------------------------------- When loading asynchronously, it can be useful to poll whether or not loading has finished. Use `ma_resource_manager_data_source_result()` to determine this. For in-memory sounds, this will return `MA_SUCCESS` when the file has been *entirely* decoded. If the sound is still being decoded, `MA_BUSY` will be returned. Otherwise, some other error code will be returned if the sound failed to load. For streaming data sources, `MA_SUCCESS` will be returned when the first page has been decoded and the sound is ready to be played. If the first page is still being decoded, `MA_BUSY` will be returned. Otherwise, some other error code will be returned if the sound failed to load. In addition to polling, you can also use a simple synchronization object called a "fence" to wait for asynchronously loaded sounds to finish. This is called `ma_fence`. The advantage to using a fence is that it can be used to wait for a group of sounds to finish loading rather than waiting for sounds on an individual basis. There are two stages to loading a sound: * Initialization of the internal decoder; and * Completion of decoding of the file (the file is fully decoded) You can specify separate fences for each of the different stages. Waiting for the initialization of the internal decoder is important for when you need to know the sample format, channels and sample rate of the file. The example below shows how you could use a fence when loading a number of sounds: ```c // This fence will be released when all sounds are finished loading entirely. ma_fence fence; ma_fence_init(&fence); // This will be passed into the initialization routine for each sound. ma_resource_manager_pipeline_notifications notifications = ma_resource_manager_pipeline_notifications_init(); notifications.done.pFence = &fence; // Now load a bunch of sounds: for (iSound = 0; iSound < soundCount; iSound += 1) { ma_resource_manager_data_source_init(pResourceManager, pSoundFilePaths[iSound], flags, ¬ifications, &pSoundSources[iSound]); } // ... DO SOMETHING ELSE WHILE SOUNDS ARE LOADING ... // Wait for loading of sounds to finish. ma_fence_wait(&fence); ``` In the example above we used a fence for waiting until the entire file has been fully decoded. If you only need to wait for the initialization of the internal decoder to complete, you can use the `init` member of the `ma_resource_manager_pipeline_notifications` object: ```c notifications.init.pFence = &fence; ``` If a fence is not appropriate for your situation, you can instead use a callback that is fired on an individual sound basis. This is done in a very similar way to fences: ```c typedef struct { ma_async_notification_callbacks cb; void* pMyData; } my_notification; void my_notification_callback(ma_async_notification* pNotification) { my_notification* pMyNotification = (my_notification*)pNotification; // Do something in response to the sound finishing loading. } ... my_notification myCallback; myCallback.cb.onSignal = my_notification_callback; myCallback.pMyData = pMyData; ma_resource_manager_pipeline_notifications notifications = ma_resource_manager_pipeline_notifications_init(); notifications.done.pNotification = &myCallback; ma_resource_manager_data_source_init(pResourceManager, "my_sound.wav", flags, ¬ifications, &mySound); ``` In the example above we just extend the `ma_async_notification_callbacks` object and pass an instantiation into the `ma_resource_manager_pipeline_notifications` in the same way as we did with the fence, only we set `pNotification` instead of `pFence`. You can set both of these at the same time and they should both work as expected. If using the `pNotification` system, you need to ensure your `ma_async_notification_callbacks` object stays valid. 6.2. Resource Manager Implementation Details -------------------------------------------- Resources are managed in two main ways: * By storing the entire sound inside an in-memory buffer (referred to as a data buffer) * By streaming audio data on the fly (referred to as a data stream) A resource managed data source (`ma_resource_manager_data_source`) encapsulates a data buffer or data stream, depending on whether or not the data source was initialized with the `MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM` flag. If so, it will make use of a `ma_resource_manager_data_stream` object. Otherwise it will use a `ma_resource_manager_data_buffer` object. Both of these objects are data sources which means they can be used with any `ma_data_source_*()` API. Another major feature of the resource manager is the ability to asynchronously decode audio files. This relieves the audio thread of time-consuming decoding which can negatively affect scalability due to the audio thread needing to complete it's work extremely quickly to avoid glitching. Asynchronous decoding is achieved through a job system. There is a central multi-producer, multi-consumer, fixed-capacity job queue. When some asynchronous work needs to be done, a job is posted to the queue which is then read by a job thread. The number of job threads can be configured for improved scalability, and job threads can all run in parallel without needing to worry about the order of execution (how this is achieved is explained below). When a sound is being loaded asynchronously, playback can begin before the sound has been fully decoded. This enables the application to start playback of the sound quickly, while at the same time allowing to resource manager to keep loading in the background. Since there may be less threads than the number of sounds being loaded at a given time, a simple scheduling system is used to keep decoding time balanced and fair. The resource manager solves this by splitting decoding into chunks called pages. By default, each page is 1 second long. When a page has been decoded, a new job will be posted to start decoding the next page. By dividing up decoding into pages, an individual sound shouldn't ever delay every other sound from having their first page decoded. Of course, when loading many sounds at the same time, there will always be an amount of time required to process jobs in the queue so in heavy load situations there will still be some delay. To determine if a data source is ready to have some frames read, use `ma_resource_manager_data_source_get_available_frames()`. This will return the number of frames available starting from the current position. 6.2.1. Job Queue ---------------- The resource manager uses a job queue which is multi-producer, multi-consumer, and fixed-capacity. This job queue is not currently lock-free, and instead uses a spinlock to achieve thread-safety. Only a fixed number of jobs can be allocated and inserted into the queue which is done through a lock-free data structure for allocating an index into a fixed sized array, with reference counting for mitigation of the ABA problem. The reference count is 32-bit. For many types of jobs it's important that they execute in a specific order. In these cases, jobs are executed serially. For the resource manager, serial execution of jobs is only required on a per-object basis (per data buffer or per data stream). Each of these objects stores an execution counter. When a job is posted it is associated with an execution counter. When the job is processed, it checks if the execution counter of the job equals the execution counter of the owning object and if so, processes the job. If the counters are not equal, the job will be posted back onto the job queue for later processing. When the job finishes processing the execution order of the main object is incremented. This system means the no matter how many job threads are executing, decoding of an individual sound will always get processed serially. The advantage to having multiple threads comes into play when loading multiple sounds at the same time. The resource manager's job queue is not 100% lock-free and will use a spinlock to achieve thread-safety for a very small section of code. This is only relevant when the resource manager uses more than one job thread. If only using a single job thread, which is the default, the lock should never actually wait in practice. The amount of time spent locking should be quite short, but it's something to be aware of for those who have pedantic lock-free requirements and need to use more than one job thread. There are plans to remove this lock in a future version. In addition, posting a job will release a semaphore, which on Win32 is implemented with `ReleaseSemaphore` and on POSIX platforms via a condition variable: ```c pthread_mutex_lock(&pSemaphore->lock); { pSemaphore->value += 1; pthread_cond_signal(&pSemaphore->cond); } pthread_mutex_unlock(&pSemaphore->lock); ``` Again, this is relevant for those with strict lock-free requirements in the audio thread. To avoid this, you can use non-blocking mode (via the `MA_JOB_QUEUE_FLAG_NON_BLOCKING` flag) and implement your own job processing routine (see the "Resource Manager" section above for details on how to do this). 6.2.2. Data Buffers ------------------- When the `MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM` flag is excluded at initialization time, the resource manager will try to load the data into an in-memory data buffer. Before doing so, however, it will first check if the specified file is already loaded. If so, it will increment a reference counter and just use the already loaded data. This saves both time and memory. When the data buffer is uninitialized, the reference counter will be decremented. If the counter hits zero, the file will be unloaded. This is a detail to keep in mind because it could result in excessive loading and unloading of a sound. For example, the following sequence will result in a file be loaded twice, once after the other: ```c ma_resource_manager_data_source_init(pResourceManager, "my_file", ..., &myDataBuffer0); // Refcount = 1. Initial load. ma_resource_manager_data_source_uninit(pResourceManager, &myDataBuffer0); // Refcount = 0. Unloaded. ma_resource_manager_data_source_init(pResourceManager, "my_file", ..., &myDataBuffer1); // Refcount = 1. Reloaded because previous uninit() unloaded it. ma_resource_manager_data_source_uninit(pResourceManager, &myDataBuffer1); // Refcount = 0. Unloaded. ``` A binary search tree (BST) is used for storing data buffers as it has good balance between efficiency and simplicity. The key of the BST is a 64-bit hash of the file path that was passed into `ma_resource_manager_data_source_init()`. The advantage of using a hash is that it saves memory over storing the entire path, has faster comparisons, and results in a mostly balanced BST due to the random nature of the hash. The disadvantages are that file names are case-sensitive and there's a small chance of name collisions. If case-sensitivity is an issue, you should normalize your file names to upper- or lower-case before initializing your data sources. If name collisions become an issue, you'll need to change the name of one of the colliding names or just not use the resource manager. When a sound file has not already been loaded and the `MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC` flag is excluded, the file will be decoded synchronously by the calling thread. There are two options for controlling how the audio is stored in the data buffer - encoded or decoded. When the `MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_DECODE` option is excluded, the raw file data will be stored in memory. Otherwise the sound will be decoded before storing it in memory. Synchronous loading is a very simple and standard process of simply adding an item to the BST, allocating a block of memory and then decoding (if `MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_DECODE` is specified). When the `MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC` flag is specified, loading of the data buffer is done asynchronously. In this case, a job is posted to the queue to start loading and then the function immediately returns, setting an internal result code to `MA_BUSY`. This result code is returned when the program calls `ma_resource_manager_data_source_result()`. When decoding has fully completed `MA_SUCCESS` will be returned. This can be used to know if loading has fully completed. When loading asynchronously, a single job is posted to the queue of the type `MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_BUFFER_NODE`. This involves making a copy of the file path and associating it with job. When the job is processed by the job thread, it will first load the file using the VFS associated with the resource manager. When using a custom VFS, it's important that it be completely thread-safe because it will be used from one or more job threads at the same time. Individual files should only ever be accessed by one thread at a time, however. After opening the file via the VFS, the job will determine whether or not the file is being decoded. If not, it simply allocates a block of memory and loads the raw file contents into it and returns. On the other hand, when the file is being decoded, it will first allocate a decoder on the heap and initialize it. Then it will check if the length of the file is known. If so it will allocate a block of memory to store the decoded output and initialize it to silence. If the size is unknown, it will allocate room for one page. After memory has been allocated, the first page will be decoded. If the sound is shorter than a page, the result code will be set to `MA_SUCCESS` and the completion event will be signalled and loading is now complete. If, however, there is more to decode, a job with the code `MA_JOB_TYPE_RESOURCE_MANAGER_PAGE_DATA_BUFFER_NODE` is posted. This job will decode the next page and perform the same process if it reaches the end. If there is more to decode, the job will post another `MA_JOB_TYPE_RESOURCE_MANAGER_PAGE_DATA_BUFFER_NODE` job which will keep on happening until the sound has been fully decoded. For sounds of an unknown length, each page will be linked together as a linked list. Internally this is implemented via the `ma_paged_audio_buffer` object. 6.2.3. Data Streams ------------------- Data streams only ever store two pages worth of data for each instance. They are most useful for large sounds like music tracks in games that would consume too much memory if fully decoded in memory. After every frame from a page has been read, a job will be posted to load the next page which is done from the VFS. For data streams, the `MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC` flag will determine whether or not initialization of the data source waits until the two pages have been decoded. When unset, `ma_resource_manager_data_source_init()` will wait until the two pages have been loaded, otherwise it will return immediately. When frames are read from a data stream using `ma_resource_manager_data_source_read_pcm_frames()`, `MA_BUSY` will be returned if there are no frames available. If there are some frames available, but less than the number requested, `MA_SUCCESS` will be returned, but the actual number of frames read will be less than the number requested. Due to the asynchronous nature of data streams, seeking is also asynchronous. If the data stream is in the middle of a seek, `MA_BUSY` will be returned when trying to read frames. When `ma_resource_manager_data_source_read_pcm_frames()` results in a page getting fully consumed a job is posted to load the next page. This will be posted from the same thread that called `ma_resource_manager_data_source_read_pcm_frames()`. Data streams are uninitialized by posting a job to the queue, but the function won't return until that job has been processed. The reason for this is that the caller owns the data stream object and therefore miniaudio needs to ensure everything completes before handing back control to the caller. Also, if the data stream is uninitialized while pages are in the middle of decoding, they must complete before destroying any underlying object and the job system handles this cleanly. Note that when a new page needs to be loaded, a job will be posted to the resource manager's job thread from the audio thread. You must keep in mind the details mentioned in the "Job Queue" section above regarding locking when posting an event if you require a strictly lock-free audio thread. 7. Node Graph ============= miniaudio's routing infrastructure follows a node graph paradigm. The idea is that you create a node whose outputs are attached to inputs of another node, thereby creating a graph. There are different types of nodes, with each node in the graph processing input data to produce output, which is then fed through the chain. Each node in the graph can apply their own custom effects. At the start of the graph will usually be one or more data source nodes which have no inputs and instead pull their data from a data source. At the end of the graph is an endpoint which represents the end of the chain and is where the final output is ultimately extracted from. Each node has a number of input buses and a number of output buses. An output bus from a node is attached to an input bus of another. Multiple nodes can connect their output buses to another node's input bus, in which case their outputs will be mixed before processing by the node. Below is a diagram that illustrates a hypothetical node graph setup: ``` >>>>>>>>>>>>>>>>>>>>>>>>>>>>>> Data flows left to right >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> +---------------+ +-----------------+ | Data Source 1 =----+ +----------+ +----= Low Pass Filter =----+ +---------------+ | | =----+ +-----------------+ | +----------+ +----= Splitter | +----= ENDPOINT | +---------------+ | | =----+ +-----------------+ | +----------+ | Data Source 2 =----+ +----------+ +----= Echo / Delay =----+ +---------------+ +-----------------+ ``` In the above graph, it starts with two data sources whose outputs are attached to the input of a splitter node. It's at this point that the two data sources are mixed. After mixing, the splitter performs it's processing routine and produces two outputs which is simply a duplication of the input stream. One output is attached to a low pass filter, whereas the other output is attached to a echo/delay. The outputs of the the low pass filter and the echo are attached to the endpoint, and since they're both connected to the same input bus, they'll be mixed. Each input bus must be configured to accept the same number of channels, but the number of channels used by input buses can be different to the number of channels for output buses in which case miniaudio will automatically convert the input data to the output channel count before processing. The number of channels of an output bus of one node must match the channel count of the input bus it's attached to. The channel counts cannot be changed after the node has been initialized. If you attempt to attach an output bus to an input bus with a different channel count, attachment will fail. To use a node graph, you first need to initialize a `ma_node_graph` object. This is essentially a container around the entire graph. The `ma_node_graph` object is required for some thread-safety issues which will be explained later. A `ma_node_graph` object is initialized using miniaudio's standard config/init system: ```c ma_node_graph_config nodeGraphConfig = ma_node_graph_config_init(myChannelCount); result = ma_node_graph_init(&nodeGraphConfig, NULL, &nodeGraph); // Second parameter is a pointer to allocation callbacks. if (result != MA_SUCCESS) { // Failed to initialize node graph. } ``` When you initialize the node graph, you're specifying the channel count of the endpoint. The endpoint is a special node which has one input bus and one output bus, both of which have the same channel count, which is specified in the config. Any nodes that connect directly to the endpoint must be configured such that their output buses have the same channel count. When you read audio data from the node graph, it'll have the channel count you specified in the config. To read data from the graph: ```c ma_uint32 framesRead; result = ma_node_graph_read_pcm_frames(&nodeGraph, pFramesOut, frameCount, &framesRead); if (result != MA_SUCCESS) { // Failed to read data from the node graph. } ``` When you read audio data, miniaudio starts at the node graph's endpoint node which then pulls in data from it's input attachments, which in turn recusively pull in data from their inputs, and so on. At the start of the graph there will be some kind of data source node which will have zero inputs and will instead read directly from a data source. The base nodes don't literally need to read from a `ma_data_source` object, but they will always have some kind of underlying object that sources some kind of audio. The `ma_data_source_node` node can be used to read from a `ma_data_source`. Data is always in floating-point format and in the number of channels you specified when the graph was initialized. The sample rate is defined by the underlying data sources. It's up to you to ensure they use a consistent and appropraite sample rate. The `ma_node` API is designed to allow custom nodes to be implemented with relative ease, but miniaudio includes a few stock nodes for common functionality. This is how you would initialize a node which reads directly from a data source (`ma_data_source_node`) which is an example of one of the stock nodes that comes with miniaudio: ```c ma_data_source_node_config config = ma_data_source_node_config_init(pMyDataSource); ma_data_source_node dataSourceNode; result = ma_data_source_node_init(&nodeGraph, &config, NULL, &dataSourceNode); if (result != MA_SUCCESS) { // Failed to create data source node. } ``` The data source node will use the output channel count to determine the channel count of the output bus. There will be 1 output bus and 0 input buses (data will be drawn directly from the data source). The data source must output to floating-point (`ma_format_f32`) or else an error will be returned from `ma_data_source_node_init()`. By default the node will not be attached to the graph. To do so, use `ma_node_attach_output_bus()`: ```c result = ma_node_attach_output_bus(&dataSourceNode, 0, ma_node_graph_get_endpoint(&nodeGraph), 0); if (result != MA_SUCCESS) { // Failed to attach node. } ``` The code above connects the data source node directly to the endpoint. Since the data source node has only a single output bus, the index will always be 0. Likewise, the endpoint only has a single input bus which means the input bus index will also always be 0. To detach a specific output bus, use `ma_node_detach_output_bus()`. To detach all output buses, use `ma_node_detach_all_output_buses()`. If you want to just move the output bus from one attachment to another, you do not need to detach first. You can just call `ma_node_attach_output_bus()` and it'll deal with it for you. Less frequently you may want to create a specialized node. This will be a node where you implement your own processing callback to apply a custom effect of some kind. This is similar to initalizing one of the stock node types, only this time you need to specify a pointer to a vtable containing a pointer to the processing function and the number of input and output buses. Example: ```c static void my_custom_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) { // Do some processing of ppFramesIn (one stream of audio data per input bus) const float* pFramesIn_0 = ppFramesIn[0]; // Input bus @ index 0. const float* pFramesIn_1 = ppFramesIn[1]; // Input bus @ index 1. float* pFramesOut_0 = ppFramesOut[0]; // Output bus @ index 0. // Do some processing. On input, `pFrameCountIn` will be the number of input frames in each // buffer in `ppFramesIn` and `pFrameCountOut` will be the capacity of each of the buffers // in `ppFramesOut`. On output, `pFrameCountIn` should be set to the number of input frames // your node consumed and `pFrameCountOut` should be set the number of output frames that // were produced. // // You should process as many frames as you can. If your effect consumes input frames at the // same rate as output frames (always the case, unless you're doing resampling), you need // only look at `ppFramesOut` and process that exact number of frames. If you're doing // resampling, you'll need to be sure to set both `pFrameCountIn` and `pFrameCountOut` // properly. } static ma_node_vtable my_custom_node_vtable = { my_custom_node_process_pcm_frames, // The function that will be called to process your custom node. This is where you'd implement your effect processing. NULL, // Optional. A callback for calculating the number of input frames that are required to process a specified number of output frames. 2, // 2 input buses. 1, // 1 output bus. 0 // Default flags. }; ... // Each bus needs to have a channel count specified. To do this you need to specify the channel // counts in an array and then pass that into the node config. ma_uint32 inputChannels[2]; // Equal in size to the number of input channels specified in the vtable. ma_uint32 outputChannels[1]; // Equal in size to the number of output channels specicied in the vtable. inputChannels[0] = channelsIn; inputChannels[1] = channelsIn; outputChannels[0] = channelsOut; ma_node_config nodeConfig = ma_node_config_init(); nodeConfig.vtable = &my_custom_node_vtable; nodeConfig.pInputChannels = inputChannels; nodeConfig.pOutputChannels = outputChannels; ma_node_base node; result = ma_node_init(&nodeGraph, &nodeConfig, NULL, &node); if (result != MA_SUCCESS) { // Failed to initialize node. } ``` When initializing a custom node, as in the code above, you'll normally just place your vtable in static space. The number of input and output buses are specified as part of the vtable. If you need a variable number of buses on a per-node bases, the vtable should have the relevant bus count set to `MA_NODE_BUS_COUNT_UNKNOWN`. In this case, the bus count should be set in the node config: ```c static ma_node_vtable my_custom_node_vtable = { my_custom_node_process_pcm_frames, // The function that will be called process your custom node. This is where you'd implement your effect processing. NULL, // Optional. A callback for calculating the number of input frames that are required to process a specified number of output frames. MA_NODE_BUS_COUNT_UNKNOWN, // The number of input buses is determined on a per-node basis. 1, // 1 output bus. 0 // Default flags. }; ... ma_node_config nodeConfig = ma_node_config_init(); nodeConfig.vtable = &my_custom_node_vtable; nodeConfig.inputBusCount = myBusCount; // <-- Since the vtable specifies MA_NODE_BUS_COUNT_UNKNOWN, the input bus count should be set here. nodeConfig.pInputChannels = inputChannels; // <-- Make sure there are nodeConfig.inputBusCount elements in this array. nodeConfig.pOutputChannels = outputChannels; // <-- The vtable specifies 1 output bus, so there must be 1 element in this array. ``` In the above example it's important to never set the `inputBusCount` and `outputBusCount` members to anything other than their defaults if the vtable specifies an explicit count. They can only be set if the vtable specifies MA_NODE_BUS_COUNT_UNKNOWN in the relevant bus count. Most often you'll want to create a structure to encapsulate your node with some extra data. You need to make sure the `ma_node_base` object is your first member of the structure: ```c typedef struct { ma_node_base base; // <-- Make sure this is always the first member. float someCustomData; } my_custom_node; ``` By doing this, your object will be compatible with all `ma_node` APIs and you can attach it to the graph just like any other node. In the custom processing callback (`my_custom_node_process_pcm_frames()` in the example above), the number of channels for each bus is what was specified by the config when the node was initialized with `ma_node_init()`. In addition, all attachments to each of the input buses will have been pre-mixed by miniaudio. The config allows you to specify different channel counts for each individual input and output bus. It's up to the effect to handle it appropriate, and if it can't, return an error in it's initialization routine. Custom nodes can be assigned some flags to describe their behaviour. These are set via the vtable and include the following: +-----------------------------------------+---------------------------------------------------+ | Flag Name | Description | +-----------------------------------------+---------------------------------------------------+ | MA_NODE_FLAG_PASSTHROUGH | Useful for nodes that do not do any kind of audio | | | processing, but are instead used for tracking | | | time, handling events, etc. Also used by the | | | internal endpoint node. It reads directly from | | | the input bus to the output bus. Nodes with this | | | flag must have exactly 1 input bus and 1 output | | | bus, and both buses must have the same channel | | | counts. | +-----------------------------------------+---------------------------------------------------+ | MA_NODE_FLAG_CONTINUOUS_PROCESSING | Causes the processing callback to be called even | | | when no data is available to be read from input | | | attachments. This is useful for effects like | | | echos where there will be a tail of audio data | | | that still needs to be processed even when the | | | original data sources have reached their ends. | +-----------------------------------------+---------------------------------------------------+ | MA_NODE_FLAG_ALLOW_NULL_INPUT | Used in conjunction with | | | `MA_NODE_FLAG_CONTINUOUS_PROCESSING`. When this | | | is set, the `ppFramesIn` parameter of the | | | processing callback will be set to NULL when | | | there are no input frames are available. When | | | this is unset, silence will be posted to the | | | processing callback. | +-----------------------------------------+---------------------------------------------------+ | MA_NODE_FLAG_DIFFERENT_PROCESSING_RATES | Used to tell miniaudio that input and output | | | frames are processed at different rates. You | | | should set this for any nodes that perform | | | resampling. | +-----------------------------------------+---------------------------------------------------+ | MA_NODE_FLAG_SILENT_OUTPUT | Used to tell miniaudio that a node produces only | | | silent output. This is useful for nodes where you | | | don't want the output to contribute to the final | | | mix. An example might be if you want split your | | | stream and have one branch be output to a file. | | | When using this flag, you should avoid writing to | | | the output buffer of the node's processing | | | callback because miniaudio will ignore it anyway. | +-----------------------------------------+---------------------------------------------------+ If you need to make a copy of an audio stream for effect processing you can use a splitter node called `ma_splitter_node`. This takes has 1 input bus and splits the stream into 2 output buses. You can use it like this: ```c ma_splitter_node_config splitterNodeConfig = ma_splitter_node_config_init(channels); ma_splitter_node splitterNode; result = ma_splitter_node_init(&nodeGraph, &splitterNodeConfig, NULL, &splitterNode); if (result != MA_SUCCESS) { // Failed to create node. } // Attach your output buses to two different input buses (can be on two different nodes). ma_node_attach_output_bus(&splitterNode, 0, ma_node_graph_get_endpoint(&nodeGraph), 0); // Attach directly to the endpoint. ma_node_attach_output_bus(&splitterNode, 1, &myEffectNode, 0); // Attach to input bus 0 of some effect node. ``` The volume of an output bus can be configured on a per-bus basis: ```c ma_node_set_output_bus_volume(&splitterNode, 0, 0.5f); ma_node_set_output_bus_volume(&splitterNode, 1, 0.5f); ``` In the code above we're using the splitter node from before and changing the volume of each of the copied streams. You can start and stop a node with the following: ```c ma_node_set_state(&splitterNode, ma_node_state_started); // The default state. ma_node_set_state(&splitterNode, ma_node_state_stopped); ``` By default the node is in a started state, but since it won't be connected to anything won't actually be invoked by the node graph until it's connected. When you stop a node, data will not be read from any of it's input connections. You can use this property to stop a group of sounds atomically. You can configure the initial state of a node in it's config: ```c nodeConfig.initialState = ma_node_state_stopped; ``` Note that for the stock specialized nodes, all of their configs will have a `nodeConfig` member which is the config to use with the base node. This is where the initial state can be configured for specialized nodes: ```c dataSourceNodeConfig.nodeConfig.initialState = ma_node_state_stopped; ``` When using a specialized node like `ma_data_source_node` or `ma_splitter_node`, be sure to not modify the `vtable` member of the `nodeConfig` object. 7.1. Timing ----------- The node graph supports starting and stopping nodes at scheduled times. This is especially useful for data source nodes where you want to get the node set up, but only start playback at a specific time. There are two clocks: local and global. A local clock is per-node, whereas the global clock is per graph. Scheduling starts and stops can only be done based on the global clock because the local clock will not be running while the node is stopped. The global clocks advances whenever `ma_node_graph_read_pcm_frames()` is called. On the other hand, the local clock only advances when the node's processing callback is fired, and is advanced based on the output frame count. To retrieve the global time, use `ma_node_graph_get_time()`. The global time can be set with `ma_node_graph_set_time()` which might be useful if you want to do seeking on a global timeline. Getting and setting the local time is similar. Use `ma_node_get_time()` to retrieve the local time, and `ma_node_set_time()` to set the local time. The global and local times will be advanced by the audio thread, so care should be taken to avoid data races. Ideally you should avoid calling these outside of the node processing callbacks which are always run on the audio thread. There is basic support for scheduling the starting and stopping of nodes. You can only schedule one start and one stop at a time. This is mainly intended for putting nodes into a started or stopped state in a frame-exact manner. Without this mechanism, starting and stopping of a node is limited to the resolution of a call to `ma_node_graph_read_pcm_frames()` which would typically be in blocks of several milliseconds. The following APIs can be used for scheduling node states: ```c ma_node_set_state_time() ma_node_get_state_time() ``` The time is absolute and must be based on the global clock. An example is below: ```c ma_node_set_state_time(&myNode, ma_node_state_started, sampleRate*1); // Delay starting to 1 second. ma_node_set_state_time(&myNode, ma_node_state_stopped, sampleRate*5); // Delay stopping to 5 seconds. ``` An example for changing the state using a relative time. ```c ma_node_set_state_time(&myNode, ma_node_state_started, sampleRate*1 + ma_node_graph_get_time(&myNodeGraph)); ma_node_set_state_time(&myNode, ma_node_state_stopped, sampleRate*5 + ma_node_graph_get_time(&myNodeGraph)); ``` Note that due to the nature of multi-threading the times may not be 100% exact. If this is an issue, consider scheduling state changes from within a processing callback. An idea might be to have some kind of passthrough trigger node that is used specifically for tracking time and handling events. 7.2. Thread Safety and Locking ------------------------------ When processing audio, it's ideal not to have any kind of locking in the audio thread. Since it's expected that `ma_node_graph_read_pcm_frames()` would be run on the audio thread, it does so without the use of any locks. This section discusses the implementation used by miniaudio and goes over some of the compromises employed by miniaudio to achieve this goal. Note that the current implementation may not be ideal - feedback and critiques are most welcome. The node graph API is not *entirely* lock-free. Only `ma_node_graph_read_pcm_frames()` is expected to be lock-free. Attachment, detachment and uninitialization of nodes use locks to simplify the implementation, but are crafted in a way such that such locking is not required when reading audio data from the graph. Locking in these areas are achieved by means of spinlocks. The main complication with keeping `ma_node_graph_read_pcm_frames()` lock-free stems from the fact that a node can be uninitialized, and it's memory potentially freed, while in the middle of being processed on the audio thread. There are times when the audio thread will be referencing a node, which means the uninitialization process of a node needs to make sure it delays returning until the audio thread is finished so that control is not handed back to the caller thereby giving them a chance to free the node's memory. When the audio thread is processing a node, it does so by reading from each of the output buses of the node. In order for a node to process data for one of it's output buses, it needs to read from each of it's input buses, and so on an so forth. It follows that once all output buses of a node are detached, the node as a whole will be disconnected and no further processing will occur unless it's output buses are reattached, which won't be happening when the node is being uninitialized. By having `ma_node_detach_output_bus()` wait until the audio thread is finished with it, we can simplify a few things, at the expense of making `ma_node_detach_output_bus()` a bit slower. By doing this, the implementation of `ma_node_uninit()` becomes trivial - just detach all output nodes, followed by each of the attachments to each of it's input nodes, and then do any final clean up. With the above design, the worst-case scenario is `ma_node_detach_output_bus()` taking as long as it takes to process the output bus being detached. This will happen if it's called at just the wrong moment where the audio thread has just iterated it and has just started processing. The caller of `ma_node_detach_output_bus()` will stall until the audio thread is finished, which includes the cost of recursively processing it's inputs. This is the biggest compromise made with the approach taken by miniaudio for it's lock-free processing system. The cost of detaching nodes earlier in the pipeline (data sources, for example) will be cheaper than the cost of detaching higher level nodes, such as some kind of final post-processing endpoint. If you need to do mass detachments, detach starting from the lowest level nodes and work your way towards the final endpoint node (but don't try detaching the node graph's endpoint). If the audio thread is not running, detachment will be fast and detachment in any order will be the same. The reason nodes need to wait for their input attachments to complete is due to the potential for desyncs between data sources. If the node was to terminate processing mid way through processing it's inputs, there's a chance that some of the underlying data sources will have been read, but then others not. That will then result in a potential desynchronization when detaching and reattaching higher-level nodes. A possible solution to this is to have an option when detaching to terminate processing before processing all input attachments which should be fairly simple. Another compromise, albeit less significant, is locking when attaching and detaching nodes. This locking is achieved by means of a spinlock in order to reduce memory overhead. A lock is present for each input bus and output bus. When an output bus is connected to an input bus, both the output bus and input bus is locked. This locking is specifically for attaching and detaching across different threads and does not affect `ma_node_graph_read_pcm_frames()` in any way. The locking and unlocking is mostly self-explanatory, but a slightly less intuitive aspect comes into it when considering that iterating over attachments must not break as a result of attaching or detaching a node while iteration is occuring. Attaching and detaching are both quite simple. When an output bus of a node is attached to an input bus of another node, it's added to a linked list. Basically, an input bus is a linked list, where each item in the list is and output bus. We have some intentional (and convenient) restrictions on what can done with the linked list in order to simplify the implementation. First of all, whenever something needs to iterate over the list, it must do so in a forward direction. Backwards iteration is not supported. Also, items can only be added to the start of the list. The linked list is a doubly-linked list where each item in the list (an output bus) holds a pointer to the next item in the list, and another to the previous item. A pointer to the previous item is only required for fast detachment of the node - it is never used in iteration. This is an important property because it means from the perspective of iteration, attaching and detaching of an item can be done with a single atomic assignment. This is exploited by both the attachment and detachment process. When attaching the node, the first thing that is done is the setting of the local "next" and "previous" pointers of the node. After that, the item is "attached" to the list by simply performing an atomic exchange with the head pointer. After that, the node is "attached" to the list from the perspective of iteration. Even though the "previous" pointer of the next item hasn't yet been set, from the perspective of iteration it's been attached because iteration will only be happening in a forward direction which means the "previous" pointer won't actually ever get used. The same general process applies to detachment. See `ma_node_attach_output_bus()` and `ma_node_detach_output_bus()` for the implementation of this mechanism. 8. Decoding =========== The `ma_decoder` API is used for reading audio files. Decoders are completely decoupled from devices and can be used independently. The following formats are supported: +---------+------------------+----------+ | Format | Decoding Backend | Built-In | +---------+------------------+----------+ | WAV | dr_wav | Yes | | MP3 | dr_mp3 | Yes | | FLAC | dr_flac | Yes | | Vorbis | stb_vorbis | No | +---------+------------------+----------+ Vorbis is supported via stb_vorbis which can be enabled by including the header section before the implementation of miniaudio, like the following: ```c #define STB_VORBIS_HEADER_ONLY #include "extras/stb_vorbis.c" // Enables Vorbis decoding. #define MINIAUDIO_IMPLEMENTATION #include "miniaudio.h" // The stb_vorbis implementation must come after the implementation of miniaudio. #undef STB_VORBIS_HEADER_ONLY #include "extras/stb_vorbis.c" ``` A copy of stb_vorbis is included in the "extras" folder in the miniaudio repository (https://github.com/mackron/miniaudio). Built-in decoders are amalgamated into the implementation section of miniaudio. You can disable the built-in decoders by specifying one or more of the following options before the miniaudio implementation: ```c #define MA_NO_WAV #define MA_NO_MP3 #define MA_NO_FLAC ``` Disabling built-in decoding libraries is useful if you use these libraries independantly of the `ma_decoder` API. A decoder can be initialized from a file with `ma_decoder_init_file()`, a block of memory with `ma_decoder_init_memory()`, or from data delivered via callbacks with `ma_decoder_init()`. Here is an example for loading a decoder from a file: ```c ma_decoder decoder; ma_result result = ma_decoder_init_file("MySong.mp3", NULL, &decoder); if (result != MA_SUCCESS) { return false; // An error occurred. } ... ma_decoder_uninit(&decoder); ``` When initializing a decoder, you can optionally pass in a pointer to a `ma_decoder_config` object (the `NULL` argument in the example above) which allows you to configure the output format, channel count, sample rate and channel map: ```c ma_decoder_config config = ma_decoder_config_init(ma_format_f32, 2, 48000); ``` When passing in `NULL` for decoder config in `ma_decoder_init*()`, the output format will be the same as that defined by the decoding backend. Data is read from the decoder as PCM frames. This will output the number of PCM frames actually read. If this is less than the requested number of PCM frames it means you've reached the end. The return value will be `MA_AT_END` if no samples have been read and the end has been reached. ```c ma_result result = ma_decoder_read_pcm_frames(pDecoder, pFrames, framesToRead, &framesRead); if (framesRead < framesToRead) { // Reached the end. } ``` You can also seek to a specific frame like so: ```c ma_result result = ma_decoder_seek_to_pcm_frame(pDecoder, targetFrame); if (result != MA_SUCCESS) { return false; // An error occurred. } ``` If you want to loop back to the start, you can simply seek back to the first PCM frame: ```c ma_decoder_seek_to_pcm_frame(pDecoder, 0); ``` When loading a decoder, miniaudio uses a trial and error technique to find the appropriate decoding backend. This can be unnecessarily inefficient if the type is already known. In this case you can use `encodingFormat` variable in the device config to specify a specific encoding format you want to decode: ```c decoderConfig.encodingFormat = ma_encoding_format_wav; ``` See the `ma_encoding_format` enum for possible encoding formats. The `ma_decoder_init_file()` API will try using the file extension to determine which decoding backend to prefer. 8.1. Custom Decoders -------------------- It's possible to implement a custom decoder and plug it into miniaudio. This is extremely useful when you want to use the `ma_decoder` API, but need to support an encoding format that's not one of the stock formats supported by miniaudio. This can be put to particularly good use when using the `ma_engine` and/or `ma_resource_manager` APIs because they use `ma_decoder` internally. If, for example, you wanted to support Opus, you can do so with a custom decoder (there if a reference Opus decoder in the "extras" folder of the miniaudio repository which uses libopus + libopusfile). A custom decoder must implement a data source. A vtable called `ma_decoding_backend_vtable` needs to be implemented which is then passed into the decoder config: ```c ma_decoding_backend_vtable* pCustomBackendVTables[] = { &g_ma_decoding_backend_vtable_libvorbis, &g_ma_decoding_backend_vtable_libopus }; ... decoderConfig = ma_decoder_config_init_default(); decoderConfig.pCustomBackendUserData = NULL; decoderConfig.ppCustomBackendVTables = pCustomBackendVTables; decoderConfig.customBackendCount = sizeof(pCustomBackendVTables) / sizeof(pCustomBackendVTables[0]); ``` The `ma_decoding_backend_vtable` vtable has the following functions: ``` onInit onInitFile onInitFileW onInitMemory onUninit ``` There are only two functions that must be implemented - `onInit` and `onUninit`. The other functions can be implemented for a small optimization for loading from a file path or memory. If these are not specified, miniaudio will deal with it for you via a generic implementation. When you initialize a custom data source (by implementing the `onInit` function in the vtable) you will need to output a pointer to a `ma_data_source` which implements your custom decoder. See the section about data sources for details on how to implemen this. Alternatively, see the "custom_decoders" example in the miniaudio repository. The `onInit` function takes a pointer to some callbacks for the purpose of reading raw audio data from some abitrary source. You'll use these functions to read from the raw data and perform the decoding. When you call them, you will pass in the `pReadSeekTellUserData` pointer to the relevant parameter. The `pConfig` parameter in `onInit` can be used to configure the backend if appropriate. It's only used as a hint and can be ignored. However, if any of the properties are relevant to your decoder, an optimal implementation will handle the relevant properties appropriately. If memory allocation is required, it should be done so via the specified allocation callbacks if possible (the `pAllocationCallbacks` parameter). If an error occurs when initializing the decoder, you should leave `ppBackend` unset, or set to NULL, and make sure everything is cleaned up appropriately and an appropriate result code returned. When multiple custom backends are specified, miniaudio will cycle through the vtables in the order they're listed in the array that's passed into the decoder config so it's important that your initialization routine is clean. When a decoder is uninitialized, the `onUninit` callback will be fired which will give you an opportunity to clean up and internal data. 9. Encoding =========== The `ma_encoding` API is used for writing audio files. The only supported output format is WAV which is achieved via dr_wav which is amalgamated into the implementation section of miniaudio. This can be disabled by specifying the following option before the implementation of miniaudio: ```c #define MA_NO_WAV ``` An encoder can be initialized to write to a file with `ma_encoder_init_file()` or from data delivered via callbacks with `ma_encoder_init()`. Below is an example for initializing an encoder to output to a file. ```c ma_encoder_config config = ma_encoder_config_init(ma_encoding_format_wav, FORMAT, CHANNELS, SAMPLE_RATE); ma_encoder encoder; ma_result result = ma_encoder_init_file("my_file.wav", &config, &encoder); if (result != MA_SUCCESS) { // Error } ... ma_encoder_uninit(&encoder); ``` When initializing an encoder you must specify a config which is initialized with `ma_encoder_config_init()`. Here you must specify the file type, the output sample format, output channel count and output sample rate. The following file types are supported: +------------------------+-------------+ | Enum | Description | +------------------------+-------------+ | ma_encoding_format_wav | WAV | +------------------------+-------------+ If the format, channel count or sample rate is not supported by the output file type an error will be returned. The encoder will not perform data conversion so you will need to convert it before outputting any audio data. To output audio data, use `ma_encoder_write_pcm_frames()`, like in the example below: ```c framesWritten = ma_encoder_write_pcm_frames(&encoder, pPCMFramesToWrite, framesToWrite); ``` Encoders must be uninitialized with `ma_encoder_uninit()`. 10. Data Conversion =================== A data conversion API is included with miniaudio which supports the majority of data conversion requirements. This supports conversion between sample formats, channel counts (with channel mapping) and sample rates. 10.1. Sample Format Conversion ------------------------------ Conversion between sample formats is achieved with the `ma_pcm_*_to_*()`, `ma_pcm_convert()` and `ma_convert_pcm_frames_format()` APIs. Use `ma_pcm_*_to_*()` to convert between two specific formats. Use `ma_pcm_convert()` to convert based on a `ma_format` variable. Use `ma_convert_pcm_frames_format()` to convert PCM frames where you want to specify the frame count and channel count as a variable instead of the total sample count. 10.1.1. Dithering ----------------- Dithering can be set using the ditherMode parameter. The different dithering modes include the following, in order of efficiency: +-----------+--------------------------+ | Type | Enum Token | +-----------+--------------------------+ | None | ma_dither_mode_none | | Rectangle | ma_dither_mode_rectangle | | Triangle | ma_dither_mode_triangle | +-----------+--------------------------+ Note that even if the dither mode is set to something other than `ma_dither_mode_none`, it will be ignored for conversions where dithering is not needed. Dithering is available for the following conversions: ``` s16 -> u8 s24 -> u8 s32 -> u8 f32 -> u8 s24 -> s16 s32 -> s16 f32 -> s16 ``` Note that it is not an error to pass something other than ma_dither_mode_none for conversions where dither is not used. It will just be ignored. 10.2. Channel Conversion ------------------------ Channel conversion is used for channel rearrangement and conversion from one channel count to another. The `ma_channel_converter` API is used for channel conversion. Below is an example of initializing a simple channel converter which converts from mono to stereo. ```c ma_channel_converter_config config = ma_channel_converter_config_init( ma_format, // Sample format 1, // Input channels NULL, // Input channel map 2, // Output channels NULL, // Output channel map ma_channel_mix_mode_default); // The mixing algorithm to use when combining channels. result = ma_channel_converter_init(&config, NULL, &converter); if (result != MA_SUCCESS) { // Error. } ``` To perform the conversion simply call `ma_channel_converter_process_pcm_frames()` like so: ```c ma_result result = ma_channel_converter_process_pcm_frames(&converter, pFramesOut, pFramesIn, frameCount); if (result != MA_SUCCESS) { // Error. } ``` It is up to the caller to ensure the output buffer is large enough to accomodate the new PCM frames. Input and output PCM frames are always interleaved. Deinterleaved layouts are not supported. 10.2.1. Channel Mapping ----------------------- In addition to converting from one channel count to another, like the example above, the channel converter can also be used to rearrange channels. When initializing the channel converter, you can optionally pass in channel maps for both the input and output frames. If the channel counts are the same, and each channel map contains the same channel positions with the exception that they're in a different order, a simple shuffling of the channels will be performed. If, however, there is not a 1:1 mapping of channel positions, or the channel counts differ, the input channels will be mixed based on a mixing mode which is specified when initializing the `ma_channel_converter_config` object. When converting from mono to multi-channel, the mono channel is simply copied to each output channel. When going the other way around, the audio of each output channel is simply averaged and copied to the mono channel. In more complicated cases blending is used. The `ma_channel_mix_mode_simple` mode will drop excess channels and silence extra channels. For example, converting from 4 to 2 channels, the 3rd and 4th channels will be dropped, whereas converting from 2 to 4 channels will put silence into the 3rd and 4th channels. The `ma_channel_mix_mode_rectangle` mode uses spacial locality based on a rectangle to compute a simple distribution between input and output. Imagine sitting in the middle of a room, with speakers on the walls representing channel positions. The `MA_CHANNEL_FRONT_LEFT` position can be thought of as being in the corner of the front and left walls. Finally, the `ma_channel_mix_mode_custom_weights` mode can be used to use custom user-defined weights. Custom weights can be passed in as the last parameter of `ma_channel_converter_config_init()`. Predefined channel maps can be retrieved with `ma_channel_map_init_standard()`. This takes a `ma_standard_channel_map` enum as it's first parameter, which can be one of the following: +-----------------------------------+-----------------------------------------------------------+ | Name | Description | +-----------------------------------+-----------------------------------------------------------+ | ma_standard_channel_map_default | Default channel map used by miniaudio. See below. | | ma_standard_channel_map_microsoft | Channel map used by Microsoft's bitfield channel maps. | | ma_standard_channel_map_alsa | Default ALSA channel map. | | ma_standard_channel_map_rfc3551 | RFC 3551. Based on AIFF. | | ma_standard_channel_map_flac | FLAC channel map. | | ma_standard_channel_map_vorbis | Vorbis channel map. | | ma_standard_channel_map_sound4 | FreeBSD's sound(4). | | ma_standard_channel_map_sndio | sndio channel map. http://www.sndio.org/tips.html. | | ma_standard_channel_map_webaudio | https://webaudio.github.io/web-audio-api/#ChannelOrdering | +-----------------------------------+-----------------------------------------------------------+ Below are the channel maps used by default in miniaudio (`ma_standard_channel_map_default`): +---------------+---------------------------------+ | Channel Count | Mapping | +---------------+---------------------------------+ | 1 (Mono) | 0: MA_CHANNEL_MONO | +---------------+---------------------------------+ | 2 (Stereo) | 0: MA_CHANNEL_FRONT_LEFT
| | | 1: MA_CHANNEL_FRONT_RIGHT | +---------------+---------------------------------+ | 3 | 0: MA_CHANNEL_FRONT_LEFT
| | | 1: MA_CHANNEL_FRONT_RIGHT
| | | 2: MA_CHANNEL_FRONT_CENTER | +---------------+---------------------------------+ | 4 (Surround) | 0: MA_CHANNEL_FRONT_LEFT
| | | 1: MA_CHANNEL_FRONT_RIGHT
| | | 2: MA_CHANNEL_FRONT_CENTER
| | | 3: MA_CHANNEL_BACK_CENTER | +---------------+---------------------------------+ | 5 | 0: MA_CHANNEL_FRONT_LEFT
| | | 1: MA_CHANNEL_FRONT_RIGHT
| | | 2: MA_CHANNEL_FRONT_CENTER
| | | 3: MA_CHANNEL_BACK_LEFT
| | | 4: MA_CHANNEL_BACK_RIGHT | +---------------+---------------------------------+ | 6 (5.1) | 0: MA_CHANNEL_FRONT_LEFT
| | | 1: MA_CHANNEL_FRONT_RIGHT
| | | 2: MA_CHANNEL_FRONT_CENTER
| | | 3: MA_CHANNEL_LFE
| | | 4: MA_CHANNEL_SIDE_LEFT
| | | 5: MA_CHANNEL_SIDE_RIGHT | +---------------+---------------------------------+ | 7 | 0: MA_CHANNEL_FRONT_LEFT
| | | 1: MA_CHANNEL_FRONT_RIGHT
| | | 2: MA_CHANNEL_FRONT_CENTER
| | | 3: MA_CHANNEL_LFE
| | | 4: MA_CHANNEL_BACK_CENTER
| | | 4: MA_CHANNEL_SIDE_LEFT
| | | 5: MA_CHANNEL_SIDE_RIGHT | +---------------+---------------------------------+ | 8 (7.1) | 0: MA_CHANNEL_FRONT_LEFT
| | | 1: MA_CHANNEL_FRONT_RIGHT
| | | 2: MA_CHANNEL_FRONT_CENTER
| | | 3: MA_CHANNEL_LFE
| | | 4: MA_CHANNEL_BACK_LEFT
| | | 5: MA_CHANNEL_BACK_RIGHT
| | | 6: MA_CHANNEL_SIDE_LEFT
| | | 7: MA_CHANNEL_SIDE_RIGHT | +---------------+---------------------------------+ | Other | All channels set to 0. This | | | is equivalent to the same | | | mapping as the device. | +---------------+---------------------------------+ 10.3. Resampling ---------------- Resampling is achieved with the `ma_resampler` object. To create a resampler object, do something like the following: ```c ma_resampler_config config = ma_resampler_config_init( ma_format_s16, channels, sampleRateIn, sampleRateOut, ma_resample_algorithm_linear); ma_resampler resampler; ma_result result = ma_resampler_init(&config, &resampler); if (result != MA_SUCCESS) { // An error occurred... } ``` Do the following to uninitialize the resampler: ```c ma_resampler_uninit(&resampler); ``` The following example shows how data can be processed ```c ma_uint64 frameCountIn = 1000; ma_uint64 frameCountOut = 2000; ma_result result = ma_resampler_process_pcm_frames(&resampler, pFramesIn, &frameCountIn, pFramesOut, &frameCountOut); if (result != MA_SUCCESS) { // An error occurred... } // At this point, frameCountIn contains the number of input frames that were consumed and frameCountOut contains the // number of output frames written. ``` To initialize the resampler you first need to set up a config (`ma_resampler_config`) with `ma_resampler_config_init()`. You need to specify the sample format you want to use, the number of channels, the input and output sample rate, and the algorithm. The sample format can be either `ma_format_s16` or `ma_format_f32`. If you need a different format you will need to perform pre- and post-conversions yourself where necessary. Note that the format is the same for both input and output. The format cannot be changed after initialization. The resampler supports multiple channels and is always interleaved (both input and output). The channel count cannot be changed after initialization. The sample rates can be anything other than zero, and are always specified in hertz. They should be set to something like 44100, etc. The sample rate is the only configuration property that can be changed after initialization. The miniaudio resampler has built-in support for the following algorithms: +-----------+------------------------------+ | Algorithm | Enum Token | +-----------+------------------------------+ | Linear | ma_resample_algorithm_linear | | Custom | ma_resample_algorithm_custom | +-----------+------------------------------+ The algorithm cannot be changed after initialization. Processing always happens on a per PCM frame basis and always assumes interleaved input and output. De-interleaved processing is not supported. To process frames, use `ma_resampler_process_pcm_frames()`. On input, this function takes the number of output frames you can fit in the output buffer and the number of input frames contained in the input buffer. On output these variables contain the number of output frames that were written to the output buffer and the number of input frames that were consumed in the process. You can pass in NULL for the input buffer in which case it will be treated as an infinitely large buffer of zeros. The output buffer can also be NULL, in which case the processing will be treated as seek. The sample rate can be changed dynamically on the fly. You can change this with explicit sample rates with `ma_resampler_set_rate()` and also with a decimal ratio with `ma_resampler_set_rate_ratio()`. The ratio is in/out. Sometimes it's useful to know exactly how many input frames will be required to output a specific number of frames. You can calculate this with `ma_resampler_get_required_input_frame_count()`. Likewise, it's sometimes useful to know exactly how many frames would be output given a certain number of input frames. You can do this with `ma_resampler_get_expected_output_frame_count()`. Due to the nature of how resampling works, the resampler introduces some latency. This can be retrieved in terms of both the input rate and the output rate with `ma_resampler_get_input_latency()` and `ma_resampler_get_output_latency()`. 10.3.1. Resampling Algorithms ----------------------------- The choice of resampling algorithm depends on your situation and requirements. 10.3.1.1. Linear Resampling --------------------------- The linear resampler is the fastest, but comes at the expense of poorer quality. There is, however, some control over the quality of the linear resampler which may make it a suitable option depending on your requirements. The linear resampler performs low-pass filtering before or after downsampling or upsampling, depending on the sample rates you're converting between. When decreasing the sample rate, the low-pass filter will be applied before downsampling. When increasing the rate it will be performed after upsampling. By default a fourth order low-pass filter will be applied. This can be configured via the `lpfOrder` configuration variable. Setting this to 0 will disable filtering. The low-pass filter has a cutoff frequency which defaults to half the sample rate of the lowest of the input and output sample rates (Nyquist Frequency). The API for the linear resampler is the same as the main resampler API, only it's called `ma_linear_resampler`. 10.3.2. Custom Resamplers ------------------------- You can implement a custom resampler by using the `ma_resample_algorithm_custom` resampling algorithm and setting a vtable in the resampler config: ```c ma_resampler_config config = ma_resampler_config_init(..., ma_resample_algorithm_custom); config.pBackendVTable = &g_customResamplerVTable; ``` Custom resamplers are useful if the stock algorithms are not appropriate for your use case. You need to implement the required functions in `ma_resampling_backend_vtable`. Note that not all functions in the vtable need to be implemented, but if it's possible to implement, they should be. You can use the `ma_linear_resampler` object for an example on how to implement the vtable. The `onGetHeapSize` callback is used to calculate the size of any internal heap allocation the custom resampler will need to make given the supplied config. When you initialize the resampler via the `onInit` callback, you'll be given a pointer to a heap allocation which is where you should store the heap allocated data. You should not free this data in `onUninit` because miniaudio will manage it for you. The `onProcess` callback is where the actual resampling takes place. On input, `pFrameCountIn` points to a variable containing the number of frames in the `pFramesIn` buffer and `pFrameCountOut` points to a variable containing the capacity in frames of the `pFramesOut` buffer. On output, `pFrameCountIn` should be set to the number of input frames that were fully consumed, whereas `pFrameCountOut` should be set to the number of frames that were written to `pFramesOut`. The `onSetRate` callback is optional and is used for dynamically changing the sample rate. If dynamic rate changes are not supported, you can set this callback to NULL. The `onGetInputLatency` and `onGetOutputLatency` functions are used for retrieving the latency in input and output rates respectively. These can be NULL in which case latency calculations will be assumed to be NULL. The `onGetRequiredInputFrameCount` callback is used to give miniaudio a hint as to how many input frames are required to be available to produce the given number of output frames. Likewise, the `onGetExpectedOutputFrameCount` callback is used to determine how many output frames will be produced given the specified number of input frames. miniaudio will use these as a hint, but they are optional and can be set to NULL if you're unable to implement them. 10.4. General Data Conversion ----------------------------- The `ma_data_converter` API can be used to wrap sample format conversion, channel conversion and resampling into one operation. This is what miniaudio uses internally to convert between the format requested when the device was initialized and the format of the backend's native device. The API for general data conversion is very similar to the resampling API. Create a `ma_data_converter` object like this: ```c ma_data_converter_config config = ma_data_converter_config_init( inputFormat, outputFormat, inputChannels, outputChannels, inputSampleRate, outputSampleRate ); ma_data_converter converter; ma_result result = ma_data_converter_init(&config, NULL, &converter); if (result != MA_SUCCESS) { // An error occurred... } ``` In the example above we use `ma_data_converter_config_init()` to initialize the config, however there's many more properties that can be configured, such as channel maps and resampling quality. Something like the following may be more suitable depending on your requirements: ```c ma_data_converter_config config = ma_data_converter_config_init_default(); config.formatIn = inputFormat; config.formatOut = outputFormat; config.channelsIn = inputChannels; config.channelsOut = outputChannels; config.sampleRateIn = inputSampleRate; config.sampleRateOut = outputSampleRate; ma_channel_map_init_standard(ma_standard_channel_map_flac, config.channelMapIn, sizeof(config.channelMapIn)/sizeof(config.channelMapIn[0]), config.channelCountIn); config.resampling.linear.lpfOrder = MA_MAX_FILTER_ORDER; ``` Do the following to uninitialize the data converter: ```c ma_data_converter_uninit(&converter, NULL); ``` The following example shows how data can be processed ```c ma_uint64 frameCountIn = 1000; ma_uint64 frameCountOut = 2000; ma_result result = ma_data_converter_process_pcm_frames(&converter, pFramesIn, &frameCountIn, pFramesOut, &frameCountOut); if (result != MA_SUCCESS) { // An error occurred... } // At this point, frameCountIn contains the number of input frames that were consumed and frameCountOut contains the number // of output frames written. ``` The data converter supports multiple channels and is always interleaved (both input and output). The channel count cannot be changed after initialization. Sample rates can be anything other than zero, and are always specified in hertz. They should be set to something like 44100, etc. The sample rate is the only configuration property that can be changed after initialization, but only if the `resampling.allowDynamicSampleRate` member of `ma_data_converter_config` is set to `MA_TRUE`. To change the sample rate, use `ma_data_converter_set_rate()` or `ma_data_converter_set_rate_ratio()`. The ratio must be in/out. The resampling algorithm cannot be changed after initialization. Processing always happens on a per PCM frame basis and always assumes interleaved input and output. De-interleaved processing is not supported. To process frames, use `ma_data_converter_process_pcm_frames()`. On input, this function takes the number of output frames you can fit in the output buffer and the number of input frames contained in the input buffer. On output these variables contain the number of output frames that were written to the output buffer and the number of input frames that were consumed in the process. You can pass in NULL for the input buffer in which case it will be treated as an infinitely large buffer of zeros. The output buffer can also be NULL, in which case the processing will be treated as seek. Sometimes it's useful to know exactly how many input frames will be required to output a specific number of frames. You can calculate this with `ma_data_converter_get_required_input_frame_count()`. Likewise, it's sometimes useful to know exactly how many frames would be output given a certain number of input frames. You can do this with `ma_data_converter_get_expected_output_frame_count()`. Due to the nature of how resampling works, the data converter introduces some latency if resampling is required. This can be retrieved in terms of both the input rate and the output rate with `ma_data_converter_get_input_latency()` and `ma_data_converter_get_output_latency()`. 11. Filtering ============= 11.1. Biquad Filtering ---------------------- Biquad filtering is achieved with the `ma_biquad` API. Example: ```c ma_biquad_config config = ma_biquad_config_init(ma_format_f32, channels, b0, b1, b2, a0, a1, a2); ma_result result = ma_biquad_init(&config, &biquad); if (result != MA_SUCCESS) { // Error. } ... ma_biquad_process_pcm_frames(&biquad, pFramesOut, pFramesIn, frameCount); ``` Biquad filtering is implemented using transposed direct form 2. The numerator coefficients are b0, b1 and b2, and the denominator coefficients are a0, a1 and a2. The a0 coefficient is required and coefficients must not be pre-normalized. Supported formats are `ma_format_s16` and `ma_format_f32`. If you need to use a different format you need to convert it yourself beforehand. When using `ma_format_s16` the biquad filter will use fixed point arithmetic. When using `ma_format_f32`, floating point arithmetic will be used. Input and output frames are always interleaved. Filtering can be applied in-place by passing in the same pointer for both the input and output buffers, like so: ```c ma_biquad_process_pcm_frames(&biquad, pMyData, pMyData, frameCount); ``` If you need to change the values of the coefficients, but maintain the values in the registers you can do so with `ma_biquad_reinit()`. This is useful if you need to change the properties of the filter while keeping the values of registers valid to avoid glitching. Do not use `ma_biquad_init()` for this as it will do a full initialization which involves clearing the registers to 0. Note that changing the format or channel count after initialization is invalid and will result in an error. 11.2. Low-Pass Filtering ------------------------ Low-pass filtering is achieved with the following APIs: +---------+------------------------------------------+ | API | Description | +---------+------------------------------------------+ | ma_lpf1 | First order low-pass filter | | ma_lpf2 | Second order low-pass filter | | ma_lpf | High order low-pass filter (Butterworth) | +---------+------------------------------------------+ Low-pass filter example: ```c ma_lpf_config config = ma_lpf_config_init(ma_format_f32, channels, sampleRate, cutoffFrequency, order); ma_result result = ma_lpf_init(&config, &lpf); if (result != MA_SUCCESS) { // Error. } ... ma_lpf_process_pcm_frames(&lpf, pFramesOut, pFramesIn, frameCount); ``` Supported formats are `ma_format_s16` and` ma_format_f32`. If you need to use a different format you need to convert it yourself beforehand. Input and output frames are always interleaved. Filtering can be applied in-place by passing in the same pointer for both the input and output buffers, like so: ```c ma_lpf_process_pcm_frames(&lpf, pMyData, pMyData, frameCount); ``` The maximum filter order is limited to `MA_MAX_FILTER_ORDER` which is set to 8. If you need more, you can chain first and second order filters together. ```c for (iFilter = 0; iFilter < filterCount; iFilter += 1) { ma_lpf2_process_pcm_frames(&lpf2[iFilter], pMyData, pMyData, frameCount); } ``` If you need to change the configuration of the filter, but need to maintain the state of internal registers you can do so with `ma_lpf_reinit()`. This may be useful if you need to change the sample rate and/or cutoff frequency dynamically while maintaing smooth transitions. Note that changing the format or channel count after initialization is invalid and will result in an error. The `ma_lpf` object supports a configurable order, but if you only need a first order filter you may want to consider using `ma_lpf1`. Likewise, if you only need a second order filter you can use `ma_lpf2`. The advantage of this is that they're lighter weight and a bit more efficient. If an even filter order is specified, a series of second order filters will be processed in a chain. If an odd filter order is specified, a first order filter will be applied, followed by a series of second order filters in a chain. 11.3. High-Pass Filtering ------------------------- High-pass filtering is achieved with the following APIs: +---------+-------------------------------------------+ | API | Description | +---------+-------------------------------------------+ | ma_hpf1 | First order high-pass filter | | ma_hpf2 | Second order high-pass filter | | ma_hpf | High order high-pass filter (Butterworth) | +---------+-------------------------------------------+ High-pass filters work exactly the same as low-pass filters, only the APIs are called `ma_hpf1`, `ma_hpf2` and `ma_hpf`. See example code for low-pass filters for example usage. 11.4. Band-Pass Filtering ------------------------- Band-pass filtering is achieved with the following APIs: +---------+-------------------------------+ | API | Description | +---------+-------------------------------+ | ma_bpf2 | Second order band-pass filter | | ma_bpf | High order band-pass filter | +---------+-------------------------------+ Band-pass filters work exactly the same as low-pass filters, only the APIs are called `ma_bpf2` and `ma_hpf`. See example code for low-pass filters for example usage. Note that the order for band-pass filters must be an even number which means there is no first order band-pass filter, unlike low-pass and high-pass filters. 11.5. Notch Filtering --------------------- Notch filtering is achieved with the following APIs: +-----------+------------------------------------------+ | API | Description | +-----------+------------------------------------------+ | ma_notch2 | Second order notching filter | +-----------+------------------------------------------+ 11.6. Peaking EQ Filtering ------------------------- Peaking filtering is achieved with the following APIs: +----------+------------------------------------------+ | API | Description | +----------+------------------------------------------+ | ma_peak2 | Second order peaking filter | +----------+------------------------------------------+ 11.7. Low Shelf Filtering ------------------------- Low shelf filtering is achieved with the following APIs: +-------------+------------------------------------------+ | API | Description | +-------------+------------------------------------------+ | ma_loshelf2 | Second order low shelf filter | +-------------+------------------------------------------+ Where a high-pass filter is used to eliminate lower frequencies, a low shelf filter can be used to just turn them down rather than eliminate them entirely. 11.8. High Shelf Filtering -------------------------- High shelf filtering is achieved with the following APIs: +-------------+------------------------------------------+ | API | Description | +-------------+------------------------------------------+ | ma_hishelf2 | Second order high shelf filter | +-------------+------------------------------------------+ The high shelf filter has the same API as the low shelf filter, only you would use `ma_hishelf` instead of `ma_loshelf`. Where a low shelf filter is used to adjust the volume of low frequencies, the high shelf filter does the same thing for high frequencies. 12. Waveform and Noise Generation ================================= 12.1. Waveforms --------------- miniaudio supports generation of sine, square, triangle and sawtooth waveforms. This is achieved with the `ma_waveform` API. Example: ```c ma_waveform_config config = ma_waveform_config_init( FORMAT, CHANNELS, SAMPLE_RATE, ma_waveform_type_sine, amplitude, frequency); ma_waveform waveform; ma_result result = ma_waveform_init(&config, &waveform); if (result != MA_SUCCESS) { // Error. } ... ma_waveform_read_pcm_frames(&waveform, pOutput, frameCount); ``` The amplitude, frequency, type, and sample rate can be changed dynamically with `ma_waveform_set_amplitude()`, `ma_waveform_set_frequency()`, `ma_waveform_set_type()`, and `ma_waveform_set_sample_rate()` respectively. You can invert the waveform by setting the amplitude to a negative value. You can use this to control whether or not a sawtooth has a positive or negative ramp, for example. Below are the supported waveform types: +---------------------------+ | Enum Name | +---------------------------+ | ma_waveform_type_sine | | ma_waveform_type_square | | ma_waveform_type_triangle | | ma_waveform_type_sawtooth | +---------------------------+ 12.2. Noise ----------- miniaudio supports generation of white, pink and Brownian noise via the `ma_noise` API. Example: ```c ma_noise_config config = ma_noise_config_init( FORMAT, CHANNELS, ma_noise_type_white, SEED, amplitude); ma_noise noise; ma_result result = ma_noise_init(&config, &noise); if (result != MA_SUCCESS) { // Error. } ... ma_noise_read_pcm_frames(&noise, pOutput, frameCount); ``` The noise API uses simple LCG random number generation. It supports a custom seed which is useful for things like automated testing requiring reproducibility. Setting the seed to zero will default to `MA_DEFAULT_LCG_SEED`. The amplitude, seed, and type can be changed dynamically with `ma_noise_set_amplitude()`, `ma_noise_set_seed()`, and `ma_noise_set_type()` respectively. By default, the noise API will use different values for different channels. So, for example, the left side in a stereo stream will be different to the right side. To instead have each channel use the same random value, set the `duplicateChannels` member of the noise config to true, like so: ```c config.duplicateChannels = MA_TRUE; ``` Below are the supported noise types. +------------------------+ | Enum Name | +------------------------+ | ma_noise_type_white | | ma_noise_type_pink | | ma_noise_type_brownian | +------------------------+ 13. Audio Buffers ================= miniaudio supports reading from a buffer of raw audio data via the `ma_audio_buffer` API. This can read from memory that's managed by the application, but can also handle the memory management for you internally. Memory management is flexible and should support most use cases. Audio buffers are initialised using the standard configuration system used everywhere in miniaudio: ```c ma_audio_buffer_config config = ma_audio_buffer_config_init( format, channels, sizeInFrames, pExistingData, &allocationCallbacks); ma_audio_buffer buffer; result = ma_audio_buffer_init(&config, &buffer); if (result != MA_SUCCESS) { // Error. } ... ma_audio_buffer_uninit(&buffer); ``` In the example above, the memory pointed to by `pExistingData` will *not* be copied and is how an application can do self-managed memory allocation. If you would rather make a copy of the data, use `ma_audio_buffer_init_copy()`. To uninitialize the buffer, use `ma_audio_buffer_uninit()`. Sometimes it can be convenient to allocate the memory for the `ma_audio_buffer` structure and the raw audio data in a contiguous block of memory. That is, the raw audio data will be located immediately after the `ma_audio_buffer` structure. To do this, use `ma_audio_buffer_alloc_and_init()`: ```c ma_audio_buffer_config config = ma_audio_buffer_config_init( format, channels, sizeInFrames, pExistingData, &allocationCallbacks); ma_audio_buffer* pBuffer result = ma_audio_buffer_alloc_and_init(&config, &pBuffer); if (result != MA_SUCCESS) { // Error } ... ma_audio_buffer_uninit_and_free(&buffer); ``` If you initialize the buffer with `ma_audio_buffer_alloc_and_init()` you should uninitialize it with `ma_audio_buffer_uninit_and_free()`. In the example above, the memory pointed to by `pExistingData` will be copied into the buffer, which is contrary to the behavior of `ma_audio_buffer_init()`. An audio buffer has a playback cursor just like a decoder. As you read frames from the buffer, the cursor moves forward. The last parameter (`loop`) can be used to determine if the buffer should loop. The return value is the number of frames actually read. If this is less than the number of frames requested it means the end has been reached. This should never happen if the `loop` parameter is set to true. If you want to manually loop back to the start, you can do so with with `ma_audio_buffer_seek_to_pcm_frame(pAudioBuffer, 0)`. Below is an example for reading data from an audio buffer. ```c ma_uint64 framesRead = ma_audio_buffer_read_pcm_frames(pAudioBuffer, pFramesOut, desiredFrameCount, isLooping); if (framesRead < desiredFrameCount) { // If not looping, this means the end has been reached. This should never happen in looping mode with valid input. } ``` Sometimes you may want to avoid the cost of data movement between the internal buffer and the output buffer. Instead you can use memory mapping to retrieve a pointer to a segment of data: ```c void* pMappedFrames; ma_uint64 frameCount = frameCountToTryMapping; ma_result result = ma_audio_buffer_map(pAudioBuffer, &pMappedFrames, &frameCount); if (result == MA_SUCCESS) { // Map was successful. The value in frameCount will be how many frames were _actually_ mapped, which may be // less due to the end of the buffer being reached. ma_copy_pcm_frames(pFramesOut, pMappedFrames, frameCount, pAudioBuffer->format, pAudioBuffer->channels); // You must unmap the buffer. ma_audio_buffer_unmap(pAudioBuffer, frameCount); } ``` When you use memory mapping, the read cursor is increment by the frame count passed in to `ma_audio_buffer_unmap()`. If you decide not to process every frame you can pass in a value smaller than the value returned by `ma_audio_buffer_map()`. The disadvantage to using memory mapping is that it does not handle looping for you. You can determine if the buffer is at the end for the purpose of looping with `ma_audio_buffer_at_end()` or by inspecting the return value of `ma_audio_buffer_unmap()` and checking if it equals `MA_AT_END`. You should not treat `MA_AT_END` as an error when returned by `ma_audio_buffer_unmap()`. 14. Ring Buffers ================ miniaudio supports lock free (single producer, single consumer) ring buffers which are exposed via the `ma_rb` and `ma_pcm_rb` APIs. The `ma_rb` API operates on bytes, whereas the `ma_pcm_rb` operates on PCM frames. They are otherwise identical as `ma_pcm_rb` is just a wrapper around `ma_rb`. Unlike most other APIs in miniaudio, ring buffers support both interleaved and deinterleaved streams. The caller can also allocate their own backing memory for the ring buffer to use internally for added flexibility. Otherwise the ring buffer will manage it's internal memory for you. The examples below use the PCM frame variant of the ring buffer since that's most likely the one you will want to use. To initialize a ring buffer, do something like the following: ```c ma_pcm_rb rb; ma_result result = ma_pcm_rb_init(FORMAT, CHANNELS, BUFFER_SIZE_IN_FRAMES, NULL, NULL, &rb); if (result != MA_SUCCESS) { // Error } ``` The `ma_pcm_rb_init()` function takes the sample format and channel count as parameters because it's the PCM varient of the ring buffer API. For the regular ring buffer that operates on bytes you would call `ma_rb_init()` which leaves these out and just takes the size of the buffer in bytes instead of frames. The fourth parameter is an optional pre-allocated buffer and the fifth parameter is a pointer to a `ma_allocation_callbacks` structure for custom memory allocation routines. Passing in `NULL` for this results in `MA_MALLOC()` and `MA_FREE()` being used. Use `ma_pcm_rb_init_ex()` if you need a deinterleaved buffer. The data for each sub-buffer is offset from each other based on the stride. To manage your sub-buffers you can use `ma_pcm_rb_get_subbuffer_stride()`, `ma_pcm_rb_get_subbuffer_offset()` and `ma_pcm_rb_get_subbuffer_ptr()`. Use `ma_pcm_rb_acquire_read()` and `ma_pcm_rb_acquire_write()` to retrieve a pointer to a section of the ring buffer. You specify the number of frames you need, and on output it will set to what was actually acquired. If the read or write pointer is positioned such that the number of frames requested will require a loop, it will be clamped to the end of the buffer. Therefore, the number of frames you're given may be less than the number you requested. After calling `ma_pcm_rb_acquire_read()` or `ma_pcm_rb_acquire_write()`, you do your work on the buffer and then "commit" it with `ma_pcm_rb_commit_read()` or `ma_pcm_rb_commit_write()`. This is where the read/write pointers are updated. When you commit you need to pass in the buffer that was returned by the earlier call to `ma_pcm_rb_acquire_read()` or `ma_pcm_rb_acquire_write()` and is only used for validation. The number of frames passed to `ma_pcm_rb_commit_read()` and `ma_pcm_rb_commit_write()` is what's used to increment the pointers, and can be less that what was originally requested. If you want to correct for drift between the write pointer and the read pointer you can use a combination of `ma_pcm_rb_pointer_distance()`, `ma_pcm_rb_seek_read()` and `ma_pcm_rb_seek_write()`. Note that you can only move the pointers forward, and you should only move the read pointer forward via the consumer thread, and the write pointer forward by the producer thread. If there is too much space between the pointers, move the read pointer forward. If there is too little space between the pointers, move the write pointer forward. You can use a ring buffer at the byte level instead of the PCM frame level by using the `ma_rb` API. This is exactly the same, only you will use the `ma_rb` functions instead of `ma_pcm_rb` and instead of frame counts you will pass around byte counts. The maximum size of the buffer in bytes is `0x7FFFFFFF-(MA_SIMD_ALIGNMENT-1)` due to the most significant bit being used to encode a loop flag and the internally managed buffers always being aligned to `MA_SIMD_ALIGNMENT`. Note that the ring buffer is only thread safe when used by a single consumer thread and single producer thread. 15. Backends ============ The following backends are supported by miniaudio. These are listed in order of default priority. When no backend is specified when initializing a context or device, miniaudio will attempt to use each of these backends in the order listed in the table below. Note that backends that are not usable by the build target will not be included in the build. For example, ALSA, which is specific to Linux, will not be included in the Windows build. +-------------+-----------------------+--------------------------------------------------------+ | Name | Enum Name | Supported Operating Systems | +-------------+-----------------------+--------------------------------------------------------+ | WASAPI | ma_backend_wasapi | Windows Vista+ | | DirectSound | ma_backend_dsound | Windows XP+ | | WinMM | ma_backend_winmm | Windows XP+ (may work on older versions, but untested) | | Core Audio | ma_backend_coreaudio | macOS, iOS | | sndio | ma_backend_sndio | OpenBSD | | audio(4) | ma_backend_audio4 | NetBSD, OpenBSD | | OSS | ma_backend_oss | FreeBSD | | PulseAudio | ma_backend_pulseaudio | Cross Platform (disabled on Windows, BSD and Android) | | ALSA | ma_backend_alsa | Linux | | JACK | ma_backend_jack | Cross Platform (disabled on BSD and Android) | | AAudio | ma_backend_aaudio | Android 8+ | | OpenSL ES | ma_backend_opensl | Android (API level 16+) | | Web Audio | ma_backend_webaudio | Web (via Emscripten) | | Custom | ma_backend_custom | Cross Platform | | Null | ma_backend_null | Cross Platform (not used on Web) | +-------------+-----------------------+--------------------------------------------------------+ Some backends have some nuance details you may want to be aware of. 15.1. WASAPI ------------ - Low-latency shared mode will be disabled when using an application-defined sample rate which is different to the device's native sample rate. To work around this, set `wasapi.noAutoConvertSRC` to true in the device config. This is due to IAudioClient3_InitializeSharedAudioStream() failing when the `AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM` flag is specified. Setting wasapi.noAutoConvertSRC will result in miniaudio's internal resampler being used instead which will in turn enable the use of low-latency shared mode. 15.2. PulseAudio ---------------- - If you experience bad glitching/noise on Arch Linux, consider this fix from the Arch wiki: https://wiki.archlinux.org/index.php/PulseAudio/Troubleshooting#Glitches,_skips_or_crackling. Alternatively, consider using a different backend such as ALSA. 15.3. Android ------------- - To capture audio on Android, remember to add the RECORD_AUDIO permission to your manifest: `` - With OpenSL|ES, only a single ma_context can be active at any given time. This is due to a limitation with OpenSL|ES. - With AAudio, only default devices are enumerated. This is due to AAudio not having an enumeration API (devices are enumerated through Java). You can however perform your own device enumeration through Java and then set the ID in the ma_device_id structure (ma_device_id.aaudio) and pass it to ma_device_init(). - The backend API will perform resampling where possible. The reason for this as opposed to using miniaudio's built-in resampler is to take advantage of any potential device-specific optimizations the driver may implement. 15.4. UWP --------- - UWP only supports default playback and capture devices. - UWP requires the Microphone capability to be enabled in the application's manifest (Package.appxmanifest): ``` ... ``` 15.5. Web Audio / Emscripten ---------------------------- - You cannot use `-std=c*` compiler flags, nor `-ansi`. This only applies to the Emscripten build. - The first time a context is initialized it will create a global object called "miniaudio" whose primary purpose is to act as a factory for device objects. - Currently the Web Audio backend uses ScriptProcessorNode's, but this may need to change later as they've been deprecated. - Google has implemented a policy in their browsers that prevent automatic media output without first receiving some kind of user input. The following web page has additional details: https://developers.google.com/web/updates/2017/09/autoplay-policy-changes. Starting the device may fail if you try to start playback without first handling some kind of user input. 16. Optimization Tips ===================== 16.1. High Level API -------------------- - If a sound does not require doppler or pitch shifting, consider disabling pitching by initializing the sound with the `MA_SOUND_FLAG_NO_PITCH` flag. - If a sound does not require spatialization, disable it by initialzing the sound with the `MA_SOUND_FLAG_NO_SPATIALIZATION` flag. It can be renabled again post-initialization with `ma_sound_set_spatialization_enabled()`. 17. Miscellaneous Notes ======================= - Automatic stream routing is enabled on a per-backend basis. Support is explicitly enabled for WASAPI and Core Audio, however other backends such as PulseAudio may naturally support it, though not all have been tested. - The contents of the output buffer passed into the data callback will always be pre-initialized to silence unless the `noPreSilencedOutputBuffer` config variable in `ma_device_config` is set to true, in which case it'll be undefined which will require you to write something to the entire buffer. - By default miniaudio will automatically clip samples. This only applies when the playback sample format is configured as `ma_format_f32`. If you are doing clipping yourself, you can disable this overhead by setting `noClip` to true in the device config. - Note that GCC and Clang requires `-msse2`, `-mavx2`, etc. for SIMD optimizations. - The sndio backend is currently only enabled on OpenBSD builds. - The audio(4) backend is supported on OpenBSD, but you may need to disable sndiod before you can use it. - When compiling with VC6 and earlier, decoding is restricted to files less than 2GB in size. This is due to 64-bit file APIs not being available. */ #ifndef miniaudio_h #define miniaudio_h #ifdef __cplusplus extern "C" { #endif #define MA_STRINGIFY(x) #x #define MA_XSTRINGIFY(x) MA_STRINGIFY(x) #define MA_VERSION_MAJOR 0 #define MA_VERSION_MINOR 11 #define MA_VERSION_REVISION 11 #define MA_VERSION_STRING MA_XSTRINGIFY(MA_VERSION_MAJOR) "." MA_XSTRINGIFY(MA_VERSION_MINOR) "." MA_XSTRINGIFY(MA_VERSION_REVISION) #if defined(_MSC_VER) && !defined(__clang__) #pragma warning(push) #pragma warning(disable:4201) /* nonstandard extension used: nameless struct/union */ #pragma warning(disable:4214) /* nonstandard extension used: bit field types other than int */ #pragma warning(disable:4324) /* structure was padded due to alignment specifier */ #elif defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wpedantic" /* For ISO C99 doesn't support unnamed structs/unions [-Wpedantic] */ #if defined(__clang__) #pragma GCC diagnostic ignored "-Wc11-extensions" /* anonymous unions are a C11 extension */ #endif #endif #if defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) && !defined(__ILP32__)) || defined(_M_X64) || defined(__ia64) || defined(_M_IA64) || defined(__aarch64__) || defined(_M_ARM64) || defined(__powerpc64__) #define MA_SIZEOF_PTR 8 #else #define MA_SIZEOF_PTR 4 #endif #include /* For size_t. */ /* Sized types. */ #if defined(MA_USE_STDINT) #include typedef int8_t ma_int8; typedef uint8_t ma_uint8; typedef int16_t ma_int16; typedef uint16_t ma_uint16; typedef int32_t ma_int32; typedef uint32_t ma_uint32; typedef int64_t ma_int64; typedef uint64_t ma_uint64; #else typedef signed char ma_int8; typedef unsigned char ma_uint8; typedef signed short ma_int16; typedef unsigned short ma_uint16; typedef signed int ma_int32; typedef unsigned int ma_uint32; #if defined(_MSC_VER) && !defined(__clang__) typedef signed __int64 ma_int64; typedef unsigned __int64 ma_uint64; #else #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wlong-long" #if defined(__clang__) #pragma GCC diagnostic ignored "-Wc++11-long-long" #endif #endif typedef signed long long ma_int64; typedef unsigned long long ma_uint64; #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) #pragma GCC diagnostic pop #endif #endif #endif /* MA_USE_STDINT */ #if MA_SIZEOF_PTR == 8 typedef ma_uint64 ma_uintptr; #else typedef ma_uint32 ma_uintptr; #endif typedef ma_uint8 ma_bool8; typedef ma_uint32 ma_bool32; #define MA_TRUE 1 #define MA_FALSE 0 typedef void* ma_handle; typedef void* ma_ptr; typedef void (* ma_proc)(void); #if defined(_MSC_VER) && !defined(_WCHAR_T_DEFINED) typedef ma_uint16 wchar_t; #endif /* Define NULL for some compilers. */ #ifndef NULL #define NULL 0 #endif #if defined(SIZE_MAX) #define MA_SIZE_MAX SIZE_MAX #else #define MA_SIZE_MAX 0xFFFFFFFF /* When SIZE_MAX is not defined by the standard library just default to the maximum 32-bit unsigned integer. */ #endif /* Platform/backend detection. */ #ifdef _WIN32 #define MA_WIN32 #if defined(MA_FORCE_UWP) || (defined(WINAPI_FAMILY) && ((defined(WINAPI_FAMILY_PC_APP) && WINAPI_FAMILY == WINAPI_FAMILY_PC_APP) || (defined(WINAPI_FAMILY_PHONE_APP) && WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP))) #define MA_WIN32_UWP #elif defined(WINAPI_FAMILY) && (defined(WINAPI_FAMILY_GAMES) && WINAPI_FAMILY == WINAPI_FAMILY_GAMES) #define MA_WIN32_GDK #else #define MA_WIN32_DESKTOP #endif #else #define MA_POSIX /* Use the MA_NO_PTHREAD_IN_HEADER option at your own risk. This is intentionally undocumented. You can use this to avoid including pthread.h in the header section. The downside is that it results in some fixed sized structures being declared for the various types that are used in miniaudio. The risk here is that these types might be too small for a given platform. This risk is yours to take and no support will be offered if you enable this option. */ #ifndef MA_NO_PTHREAD_IN_HEADER #include /* Unfortunate #include, but needed for pthread_t, pthread_mutex_t and pthread_cond_t types. */ typedef pthread_t ma_pthread_t; typedef pthread_mutex_t ma_pthread_mutex_t; typedef pthread_cond_t ma_pthread_cond_t; #else typedef ma_uintptr ma_pthread_t; typedef union ma_pthread_mutex_t { char __data[40]; ma_uint64 __alignment; } ma_pthread_mutex_t; typedef union ma_pthread_cond_t { char __data[48]; ma_uint64 __alignment; } ma_pthread_cond_t; #endif #ifdef __unix__ #define MA_UNIX #if defined(__DragonFly__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) #define MA_BSD #endif #endif #ifdef __linux__ #define MA_LINUX #endif #ifdef __APPLE__ #define MA_APPLE #endif #ifdef __ANDROID__ #define MA_ANDROID #endif #ifdef __EMSCRIPTEN__ #define MA_EMSCRIPTEN #endif #endif #ifdef _MSC_VER #define MA_INLINE __forceinline #elif defined(__GNUC__) /* I've had a bug report where GCC is emitting warnings about functions possibly not being inlineable. This warning happens when the __attribute__((always_inline)) attribute is defined without an "inline" statement. I think therefore there must be some case where "__inline__" is not always defined, thus the compiler emitting these warnings. When using -std=c89 or -ansi on the command line, we cannot use the "inline" keyword and instead need to use "__inline__". In an attempt to work around this issue I am using "__inline__" only when we're compiling in strict ANSI mode. */ #if defined(__STRICT_ANSI__) #define MA_GNUC_INLINE_HINT __inline__ #else #define MA_GNUC_INLINE_HINT inline #endif #if (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 2)) || defined(__clang__) #define MA_INLINE MA_GNUC_INLINE_HINT __attribute__((always_inline)) #else #define MA_INLINE MA_GNUC_INLINE_HINT #endif #elif defined(__WATCOMC__) #define MA_INLINE __inline #else #define MA_INLINE #endif #if !defined(MA_API) #if defined(MA_DLL) #if defined(_WIN32) #define MA_DLL_IMPORT __declspec(dllimport) #define MA_DLL_EXPORT __declspec(dllexport) #define MA_DLL_PRIVATE static #else #if defined(__GNUC__) && __GNUC__ >= 4 #define MA_DLL_IMPORT __attribute__((visibility("default"))) #define MA_DLL_EXPORT __attribute__((visibility("default"))) #define MA_DLL_PRIVATE __attribute__((visibility("hidden"))) #else #define MA_DLL_IMPORT #define MA_DLL_EXPORT #define MA_DLL_PRIVATE static #endif #endif #if defined(MINIAUDIO_IMPLEMENTATION) || defined(MA_IMPLEMENTATION) #define MA_API MA_DLL_EXPORT #else #define MA_API MA_DLL_IMPORT #endif #define MA_PRIVATE MA_DLL_PRIVATE #else #define MA_API extern #define MA_PRIVATE static #endif #endif /* SIMD alignment in bytes. Currently set to 32 bytes in preparation for future AVX optimizations. */ #define MA_SIMD_ALIGNMENT 32 /* Logging Levels ============== Log levels are only used to give logging callbacks some context as to the severity of a log message so they can do filtering. All log levels will be posted to registered logging callbacks. If you don't want to output a certain log level you can discriminate against the log level in the callback. MA_LOG_LEVEL_DEBUG Used for debugging. Useful for debug and test builds, but should be disabled in release builds. MA_LOG_LEVEL_INFO Informational logging. Useful for debugging. This will never be called from within the data callback. MA_LOG_LEVEL_WARNING Warnings. You should enable this in you development builds and action them when encounted. These logs usually indicate a potential problem or misconfiguration, but still allow you to keep running. This will never be called from within the data callback. MA_LOG_LEVEL_ERROR Error logging. This will be fired when an operation fails and is subsequently aborted. This can be fired from within the data callback, in which case the device will be stopped. You should always have this log level enabled. */ typedef enum { MA_LOG_LEVEL_DEBUG = 4, MA_LOG_LEVEL_INFO = 3, MA_LOG_LEVEL_WARNING = 2, MA_LOG_LEVEL_ERROR = 1 } ma_log_level; /* Variables needing to be accessed atomically should be declared with this macro for two reasons: 1) It allows people who read the code to identify a variable as such; and 2) It forces alignment on platforms where it's required or optimal. Note that for x86/64, alignment is not strictly necessary, but does have some performance implications. Where supported by the compiler, alignment will be used, but otherwise if the CPU architecture does not require it, it will simply leave it unaligned. This is the case with old versions of Visual Studio, which I've confirmed with at least VC6. */ #if !defined(_MSC_VER) && defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) #include #define MA_ATOMIC(alignment, type) alignas(alignment) type #else #if defined(__GNUC__) /* GCC-style compilers. */ #define MA_ATOMIC(alignment, type) type __attribute__((aligned(alignment))) #elif defined(_MSC_VER) && _MSC_VER > 1200 /* 1200 = VC6. Alignment not supported, but not necessary because x86 is the only supported target. */ /* MSVC. */ #define MA_ATOMIC(alignment, type) __declspec(align(alignment)) type #else /* Other compilers. */ #define MA_ATOMIC(alignment, type) type #endif #endif typedef struct ma_context ma_context; typedef struct ma_device ma_device; typedef ma_uint8 ma_channel; typedef enum { MA_CHANNEL_NONE = 0, MA_CHANNEL_MONO = 1, MA_CHANNEL_FRONT_LEFT = 2, MA_CHANNEL_FRONT_RIGHT = 3, MA_CHANNEL_FRONT_CENTER = 4, MA_CHANNEL_LFE = 5, MA_CHANNEL_BACK_LEFT = 6, MA_CHANNEL_BACK_RIGHT = 7, MA_CHANNEL_FRONT_LEFT_CENTER = 8, MA_CHANNEL_FRONT_RIGHT_CENTER = 9, MA_CHANNEL_BACK_CENTER = 10, MA_CHANNEL_SIDE_LEFT = 11, MA_CHANNEL_SIDE_RIGHT = 12, MA_CHANNEL_TOP_CENTER = 13, MA_CHANNEL_TOP_FRONT_LEFT = 14, MA_CHANNEL_TOP_FRONT_CENTER = 15, MA_CHANNEL_TOP_FRONT_RIGHT = 16, MA_CHANNEL_TOP_BACK_LEFT = 17, MA_CHANNEL_TOP_BACK_CENTER = 18, MA_CHANNEL_TOP_BACK_RIGHT = 19, MA_CHANNEL_AUX_0 = 20, MA_CHANNEL_AUX_1 = 21, MA_CHANNEL_AUX_2 = 22, MA_CHANNEL_AUX_3 = 23, MA_CHANNEL_AUX_4 = 24, MA_CHANNEL_AUX_5 = 25, MA_CHANNEL_AUX_6 = 26, MA_CHANNEL_AUX_7 = 27, MA_CHANNEL_AUX_8 = 28, MA_CHANNEL_AUX_9 = 29, MA_CHANNEL_AUX_10 = 30, MA_CHANNEL_AUX_11 = 31, MA_CHANNEL_AUX_12 = 32, MA_CHANNEL_AUX_13 = 33, MA_CHANNEL_AUX_14 = 34, MA_CHANNEL_AUX_15 = 35, MA_CHANNEL_AUX_16 = 36, MA_CHANNEL_AUX_17 = 37, MA_CHANNEL_AUX_18 = 38, MA_CHANNEL_AUX_19 = 39, MA_CHANNEL_AUX_20 = 40, MA_CHANNEL_AUX_21 = 41, MA_CHANNEL_AUX_22 = 42, MA_CHANNEL_AUX_23 = 43, MA_CHANNEL_AUX_24 = 44, MA_CHANNEL_AUX_25 = 45, MA_CHANNEL_AUX_26 = 46, MA_CHANNEL_AUX_27 = 47, MA_CHANNEL_AUX_28 = 48, MA_CHANNEL_AUX_29 = 49, MA_CHANNEL_AUX_30 = 50, MA_CHANNEL_AUX_31 = 51, MA_CHANNEL_LEFT = MA_CHANNEL_FRONT_LEFT, MA_CHANNEL_RIGHT = MA_CHANNEL_FRONT_RIGHT, MA_CHANNEL_POSITION_COUNT = (MA_CHANNEL_AUX_31 + 1) } _ma_channel_position; /* Do not use `_ma_channel_position` directly. Use `ma_channel` instead. */ typedef enum { MA_SUCCESS = 0, MA_ERROR = -1, /* A generic error. */ MA_INVALID_ARGS = -2, MA_INVALID_OPERATION = -3, MA_OUT_OF_MEMORY = -4, MA_OUT_OF_RANGE = -5, MA_ACCESS_DENIED = -6, MA_DOES_NOT_EXIST = -7, MA_ALREADY_EXISTS = -8, MA_TOO_MANY_OPEN_FILES = -9, MA_INVALID_FILE = -10, MA_TOO_BIG = -11, MA_PATH_TOO_LONG = -12, MA_NAME_TOO_LONG = -13, MA_NOT_DIRECTORY = -14, MA_IS_DIRECTORY = -15, MA_DIRECTORY_NOT_EMPTY = -16, MA_AT_END = -17, MA_NO_SPACE = -18, MA_BUSY = -19, MA_IO_ERROR = -20, MA_INTERRUPT = -21, MA_UNAVAILABLE = -22, MA_ALREADY_IN_USE = -23, MA_BAD_ADDRESS = -24, MA_BAD_SEEK = -25, MA_BAD_PIPE = -26, MA_DEADLOCK = -27, MA_TOO_MANY_LINKS = -28, MA_NOT_IMPLEMENTED = -29, MA_NO_MESSAGE = -30, MA_BAD_MESSAGE = -31, MA_NO_DATA_AVAILABLE = -32, MA_INVALID_DATA = -33, MA_TIMEOUT = -34, MA_NO_NETWORK = -35, MA_NOT_UNIQUE = -36, MA_NOT_SOCKET = -37, MA_NO_ADDRESS = -38, MA_BAD_PROTOCOL = -39, MA_PROTOCOL_UNAVAILABLE = -40, MA_PROTOCOL_NOT_SUPPORTED = -41, MA_PROTOCOL_FAMILY_NOT_SUPPORTED = -42, MA_ADDRESS_FAMILY_NOT_SUPPORTED = -43, MA_SOCKET_NOT_SUPPORTED = -44, MA_CONNECTION_RESET = -45, MA_ALREADY_CONNECTED = -46, MA_NOT_CONNECTED = -47, MA_CONNECTION_REFUSED = -48, MA_NO_HOST = -49, MA_IN_PROGRESS = -50, MA_CANCELLED = -51, MA_MEMORY_ALREADY_MAPPED = -52, /* General miniaudio-specific errors. */ MA_FORMAT_NOT_SUPPORTED = -100, MA_DEVICE_TYPE_NOT_SUPPORTED = -101, MA_SHARE_MODE_NOT_SUPPORTED = -102, MA_NO_BACKEND = -103, MA_NO_DEVICE = -104, MA_API_NOT_FOUND = -105, MA_INVALID_DEVICE_CONFIG = -106, MA_LOOP = -107, /* State errors. */ MA_DEVICE_NOT_INITIALIZED = -200, MA_DEVICE_ALREADY_INITIALIZED = -201, MA_DEVICE_NOT_STARTED = -202, MA_DEVICE_NOT_STOPPED = -203, /* Operation errors. */ MA_FAILED_TO_INIT_BACKEND = -300, MA_FAILED_TO_OPEN_BACKEND_DEVICE = -301, MA_FAILED_TO_START_BACKEND_DEVICE = -302, MA_FAILED_TO_STOP_BACKEND_DEVICE = -303 } ma_result; #define MA_MIN_CHANNELS 1 #ifndef MA_MAX_CHANNELS #define MA_MAX_CHANNELS 254 #endif #ifndef MA_MAX_FILTER_ORDER #define MA_MAX_FILTER_ORDER 8 #endif typedef enum { ma_stream_format_pcm = 0 } ma_stream_format; typedef enum { ma_stream_layout_interleaved = 0, ma_stream_layout_deinterleaved } ma_stream_layout; typedef enum { ma_dither_mode_none = 0, ma_dither_mode_rectangle, ma_dither_mode_triangle } ma_dither_mode; typedef enum { /* I like to keep these explicitly defined because they're used as a key into a lookup table. When items are added to this, make sure there are no gaps and that they're added to the lookup table in ma_get_bytes_per_sample(). */ ma_format_unknown = 0, /* Mainly used for indicating an error, but also used as the default for the output format for decoders. */ ma_format_u8 = 1, ma_format_s16 = 2, /* Seems to be the most widely supported format. */ ma_format_s24 = 3, /* Tightly packed. 3 bytes per sample. */ ma_format_s32 = 4, ma_format_f32 = 5, ma_format_count } ma_format; typedef enum { /* Standard rates need to be in priority order. */ ma_standard_sample_rate_48000 = 48000, /* Most common */ ma_standard_sample_rate_44100 = 44100, ma_standard_sample_rate_32000 = 32000, /* Lows */ ma_standard_sample_rate_24000 = 24000, ma_standard_sample_rate_22050 = 22050, ma_standard_sample_rate_88200 = 88200, /* Highs */ ma_standard_sample_rate_96000 = 96000, ma_standard_sample_rate_176400 = 176400, ma_standard_sample_rate_192000 = 192000, ma_standard_sample_rate_16000 = 16000, /* Extreme lows */ ma_standard_sample_rate_11025 = 11250, ma_standard_sample_rate_8000 = 8000, ma_standard_sample_rate_352800 = 352800, /* Extreme highs */ ma_standard_sample_rate_384000 = 384000, ma_standard_sample_rate_min = ma_standard_sample_rate_8000, ma_standard_sample_rate_max = ma_standard_sample_rate_384000, ma_standard_sample_rate_count = 14 /* Need to maintain the count manually. Make sure this is updated if items are added to enum. */ } ma_standard_sample_rate; typedef enum { ma_channel_mix_mode_rectangular = 0, /* Simple averaging based on the plane(s) the channel is sitting on. */ ma_channel_mix_mode_simple, /* Drop excess channels; zeroed out extra channels. */ ma_channel_mix_mode_custom_weights, /* Use custom weights specified in ma_channel_converter_config. */ ma_channel_mix_mode_default = ma_channel_mix_mode_rectangular } ma_channel_mix_mode; typedef enum { ma_standard_channel_map_microsoft, ma_standard_channel_map_alsa, ma_standard_channel_map_rfc3551, /* Based off AIFF. */ ma_standard_channel_map_flac, ma_standard_channel_map_vorbis, ma_standard_channel_map_sound4, /* FreeBSD's sound(4). */ ma_standard_channel_map_sndio, /* www.sndio.org/tips.html */ ma_standard_channel_map_webaudio = ma_standard_channel_map_flac, /* https://webaudio.github.io/web-audio-api/#ChannelOrdering. Only 1, 2, 4 and 6 channels are defined, but can fill in the gaps with logical assumptions. */ ma_standard_channel_map_default = ma_standard_channel_map_microsoft } ma_standard_channel_map; typedef enum { ma_performance_profile_low_latency = 0, ma_performance_profile_conservative } ma_performance_profile; typedef struct { void* pUserData; void* (* onMalloc)(size_t sz, void* pUserData); void* (* onRealloc)(void* p, size_t sz, void* pUserData); void (* onFree)(void* p, void* pUserData); } ma_allocation_callbacks; typedef struct { ma_int32 state; } ma_lcg; /* Spinlocks are 32-bit for compatibility reasons. */ typedef ma_uint32 ma_spinlock; #ifndef MA_NO_THREADING /* Thread priorities should be ordered such that the default priority of the worker thread is 0. */ typedef enum { ma_thread_priority_idle = -5, ma_thread_priority_lowest = -4, ma_thread_priority_low = -3, ma_thread_priority_normal = -2, ma_thread_priority_high = -1, ma_thread_priority_highest = 0, ma_thread_priority_realtime = 1, ma_thread_priority_default = 0 } ma_thread_priority; #if defined(MA_WIN32) typedef ma_handle ma_thread; #endif #if defined(MA_POSIX) typedef ma_pthread_t ma_thread; #endif #if defined(MA_WIN32) typedef ma_handle ma_mutex; #endif #if defined(MA_POSIX) typedef ma_pthread_mutex_t ma_mutex; #endif #if defined(MA_WIN32) typedef ma_handle ma_event; #endif #if defined(MA_POSIX) typedef struct { ma_uint32 value; ma_pthread_mutex_t lock; ma_pthread_cond_t cond; } ma_event; #endif /* MA_POSIX */ #if defined(MA_WIN32) typedef ma_handle ma_semaphore; #endif #if defined(MA_POSIX) typedef struct { int value; ma_pthread_mutex_t lock; ma_pthread_cond_t cond; } ma_semaphore; #endif /* MA_POSIX */ #else /* MA_NO_THREADING is set which means threading is disabled. Threading is required by some API families. If any of these are enabled we need to throw an error. */ #ifndef MA_NO_DEVICE_IO #error "MA_NO_THREADING cannot be used without MA_NO_DEVICE_IO"; #endif #endif /* MA_NO_THREADING */ /* Retrieves the version of miniaudio as separated integers. Each component can be NULL if it's not required. */ MA_API void ma_version(ma_uint32* pMajor, ma_uint32* pMinor, ma_uint32* pRevision); /* Retrieves the version of miniaudio as a string which can be useful for logging purposes. */ MA_API const char* ma_version_string(void); /************************************************************************************************************************************************************** Logging **************************************************************************************************************************************************************/ #include /* For va_list. */ #if defined(__has_attribute) #if __has_attribute(format) #define MA_ATTRIBUTE_FORMAT(fmt, va) __attribute__((format(printf, fmt, va))) #endif #endif #ifndef MA_ATTRIBUTE_FORMAT #define MA_ATTRIBUTE_FORMAT(fmt,va) #endif #ifndef MA_MAX_LOG_CALLBACKS #define MA_MAX_LOG_CALLBACKS 4 #endif /* The callback for handling log messages. Parameters ---------- pUserData (in) The user data pointer that was passed into ma_log_register_callback(). logLevel (in) The log level. This can be one of the following: +----------------------+ | Log Level | +----------------------+ | MA_LOG_LEVEL_DEBUG | | MA_LOG_LEVEL_INFO | | MA_LOG_LEVEL_WARNING | | MA_LOG_LEVEL_ERROR | +----------------------+ pMessage (in) The log message. Remarks ------- Do not modify the state of the device from inside the callback. */ typedef void (* ma_log_callback_proc)(void* pUserData, ma_uint32 level, const char* pMessage); typedef struct { ma_log_callback_proc onLog; void* pUserData; } ma_log_callback; MA_API ma_log_callback ma_log_callback_init(ma_log_callback_proc onLog, void* pUserData); typedef struct { ma_log_callback callbacks[MA_MAX_LOG_CALLBACKS]; ma_uint32 callbackCount; ma_allocation_callbacks allocationCallbacks; /* Need to store these persistently because ma_log_postv() might need to allocate a buffer on the heap. */ #ifndef MA_NO_THREADING ma_mutex lock; /* For thread safety just to make it easier and safer for the logging implementation. */ #endif } ma_log; MA_API ma_result ma_log_init(const ma_allocation_callbacks* pAllocationCallbacks, ma_log* pLog); MA_API void ma_log_uninit(ma_log* pLog); MA_API ma_result ma_log_register_callback(ma_log* pLog, ma_log_callback callback); MA_API ma_result ma_log_unregister_callback(ma_log* pLog, ma_log_callback callback); MA_API ma_result ma_log_post(ma_log* pLog, ma_uint32 level, const char* pMessage); MA_API ma_result ma_log_postv(ma_log* pLog, ma_uint32 level, const char* pFormat, va_list args); MA_API ma_result ma_log_postf(ma_log* pLog, ma_uint32 level, const char* pFormat, ...) MA_ATTRIBUTE_FORMAT(3, 4); /************************************************************************************************************************************************************** Biquad Filtering **************************************************************************************************************************************************************/ typedef union { float f32; ma_int32 s32; } ma_biquad_coefficient; typedef struct { ma_format format; ma_uint32 channels; double b0; double b1; double b2; double a0; double a1; double a2; } ma_biquad_config; MA_API ma_biquad_config ma_biquad_config_init(ma_format format, ma_uint32 channels, double b0, double b1, double b2, double a0, double a1, double a2); typedef struct { ma_format format; ma_uint32 channels; ma_biquad_coefficient b0; ma_biquad_coefficient b1; ma_biquad_coefficient b2; ma_biquad_coefficient a1; ma_biquad_coefficient a2; ma_biquad_coefficient* pR1; ma_biquad_coefficient* pR2; /* Memory management. */ void* _pHeap; ma_bool32 _ownsHeap; } ma_biquad; MA_API ma_result ma_biquad_get_heap_size(const ma_biquad_config* pConfig, size_t* pHeapSizeInBytes); MA_API ma_result ma_biquad_init_preallocated(const ma_biquad_config* pConfig, void* pHeap, ma_biquad* pBQ); MA_API ma_result ma_biquad_init(const ma_biquad_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_biquad* pBQ); MA_API void ma_biquad_uninit(ma_biquad* pBQ, const ma_allocation_callbacks* pAllocationCallbacks); MA_API ma_result ma_biquad_reinit(const ma_biquad_config* pConfig, ma_biquad* pBQ); MA_API ma_result ma_biquad_clear_cache(ma_biquad* pBQ); MA_API ma_result ma_biquad_process_pcm_frames(ma_biquad* pBQ, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); MA_API ma_uint32 ma_biquad_get_latency(const ma_biquad* pBQ); /************************************************************************************************************************************************************** Low-Pass Filtering **************************************************************************************************************************************************************/ typedef struct { ma_format format; ma_uint32 channels; ma_uint32 sampleRate; double cutoffFrequency; double q; } ma_lpf1_config, ma_lpf2_config; MA_API ma_lpf1_config ma_lpf1_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency); MA_API ma_lpf2_config ma_lpf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, double q); typedef struct { ma_format format; ma_uint32 channels; ma_biquad_coefficient a; ma_biquad_coefficient* pR1; /* Memory management. */ void* _pHeap; ma_bool32 _ownsHeap; } ma_lpf1; MA_API ma_result ma_lpf1_get_heap_size(const ma_lpf1_config* pConfig, size_t* pHeapSizeInBytes); MA_API ma_result ma_lpf1_init_preallocated(const ma_lpf1_config* pConfig, void* pHeap, ma_lpf1* pLPF); MA_API ma_result ma_lpf1_init(const ma_lpf1_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_lpf1* pLPF); MA_API void ma_lpf1_uninit(ma_lpf1* pLPF, const ma_allocation_callbacks* pAllocationCallbacks); MA_API ma_result ma_lpf1_reinit(const ma_lpf1_config* pConfig, ma_lpf1* pLPF); MA_API ma_result ma_lpf1_clear_cache(ma_lpf1* pLPF); MA_API ma_result ma_lpf1_process_pcm_frames(ma_lpf1* pLPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); MA_API ma_uint32 ma_lpf1_get_latency(const ma_lpf1* pLPF); typedef struct { ma_biquad bq; /* The second order low-pass filter is implemented as a biquad filter. */ } ma_lpf2; MA_API ma_result ma_lpf2_get_heap_size(const ma_lpf2_config* pConfig, size_t* pHeapSizeInBytes); MA_API ma_result ma_lpf2_init_preallocated(const ma_lpf2_config* pConfig, void* pHeap, ma_lpf2* pHPF); MA_API ma_result ma_lpf2_init(const ma_lpf2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_lpf2* pLPF); MA_API void ma_lpf2_uninit(ma_lpf2* pLPF, const ma_allocation_callbacks* pAllocationCallbacks); MA_API ma_result ma_lpf2_reinit(const ma_lpf2_config* pConfig, ma_lpf2* pLPF); MA_API ma_result ma_lpf2_clear_cache(ma_lpf2* pLPF); MA_API ma_result ma_lpf2_process_pcm_frames(ma_lpf2* pLPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); MA_API ma_uint32 ma_lpf2_get_latency(const ma_lpf2* pLPF); typedef struct { ma_format format; ma_uint32 channels; ma_uint32 sampleRate; double cutoffFrequency; ma_uint32 order; /* If set to 0, will be treated as a passthrough (no filtering will be applied). */ } ma_lpf_config; MA_API ma_lpf_config ma_lpf_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order); typedef struct { ma_format format; ma_uint32 channels; ma_uint32 sampleRate; ma_uint32 lpf1Count; ma_uint32 lpf2Count; ma_lpf1* pLPF1; ma_lpf2* pLPF2; /* Memory management. */ void* _pHeap; ma_bool32 _ownsHeap; } ma_lpf; MA_API ma_result ma_lpf_get_heap_size(const ma_lpf_config* pConfig, size_t* pHeapSizeInBytes); MA_API ma_result ma_lpf_init_preallocated(const ma_lpf_config* pConfig, void* pHeap, ma_lpf* pLPF); MA_API ma_result ma_lpf_init(const ma_lpf_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_lpf* pLPF); MA_API void ma_lpf_uninit(ma_lpf* pLPF, const ma_allocation_callbacks* pAllocationCallbacks); MA_API ma_result ma_lpf_reinit(const ma_lpf_config* pConfig, ma_lpf* pLPF); MA_API ma_result ma_lpf_clear_cache(ma_lpf* pLPF); MA_API ma_result ma_lpf_process_pcm_frames(ma_lpf* pLPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); MA_API ma_uint32 ma_lpf_get_latency(const ma_lpf* pLPF); /************************************************************************************************************************************************************** High-Pass Filtering **************************************************************************************************************************************************************/ typedef struct { ma_format format; ma_uint32 channels; ma_uint32 sampleRate; double cutoffFrequency; double q; } ma_hpf1_config, ma_hpf2_config; MA_API ma_hpf1_config ma_hpf1_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency); MA_API ma_hpf2_config ma_hpf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, double q); typedef struct { ma_format format; ma_uint32 channels; ma_biquad_coefficient a; ma_biquad_coefficient* pR1; /* Memory management. */ void* _pHeap; ma_bool32 _ownsHeap; } ma_hpf1; MA_API ma_result ma_hpf1_get_heap_size(const ma_hpf1_config* pConfig, size_t* pHeapSizeInBytes); MA_API ma_result ma_hpf1_init_preallocated(const ma_hpf1_config* pConfig, void* pHeap, ma_hpf1* pLPF); MA_API ma_result ma_hpf1_init(const ma_hpf1_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hpf1* pHPF); MA_API void ma_hpf1_uninit(ma_hpf1* pHPF, const ma_allocation_callbacks* pAllocationCallbacks); MA_API ma_result ma_hpf1_reinit(const ma_hpf1_config* pConfig, ma_hpf1* pHPF); MA_API ma_result ma_hpf1_process_pcm_frames(ma_hpf1* pHPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); MA_API ma_uint32 ma_hpf1_get_latency(const ma_hpf1* pHPF); typedef struct { ma_biquad bq; /* The second order high-pass filter is implemented as a biquad filter. */ } ma_hpf2; MA_API ma_result ma_hpf2_get_heap_size(const ma_hpf2_config* pConfig, size_t* pHeapSizeInBytes); MA_API ma_result ma_hpf2_init_preallocated(const ma_hpf2_config* pConfig, void* pHeap, ma_hpf2* pHPF); MA_API ma_result ma_hpf2_init(const ma_hpf2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hpf2* pHPF); MA_API void ma_hpf2_uninit(ma_hpf2* pHPF, const ma_allocation_callbacks* pAllocationCallbacks); MA_API ma_result ma_hpf2_reinit(const ma_hpf2_config* pConfig, ma_hpf2* pHPF); MA_API ma_result ma_hpf2_process_pcm_frames(ma_hpf2* pHPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); MA_API ma_uint32 ma_hpf2_get_latency(const ma_hpf2* pHPF); typedef struct { ma_format format; ma_uint32 channels; ma_uint32 sampleRate; double cutoffFrequency; ma_uint32 order; /* If set to 0, will be treated as a passthrough (no filtering will be applied). */ } ma_hpf_config; MA_API ma_hpf_config ma_hpf_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order); typedef struct { ma_format format; ma_uint32 channels; ma_uint32 sampleRate; ma_uint32 hpf1Count; ma_uint32 hpf2Count; ma_hpf1* pHPF1; ma_hpf2* pHPF2; /* Memory management. */ void* _pHeap; ma_bool32 _ownsHeap; } ma_hpf; MA_API ma_result ma_hpf_get_heap_size(const ma_hpf_config* pConfig, size_t* pHeapSizeInBytes); MA_API ma_result ma_hpf_init_preallocated(const ma_hpf_config* pConfig, void* pHeap, ma_hpf* pLPF); MA_API ma_result ma_hpf_init(const ma_hpf_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hpf* pHPF); MA_API void ma_hpf_uninit(ma_hpf* pHPF, const ma_allocation_callbacks* pAllocationCallbacks); MA_API ma_result ma_hpf_reinit(const ma_hpf_config* pConfig, ma_hpf* pHPF); MA_API ma_result ma_hpf_process_pcm_frames(ma_hpf* pHPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); MA_API ma_uint32 ma_hpf_get_latency(const ma_hpf* pHPF); /************************************************************************************************************************************************************** Band-Pass Filtering **************************************************************************************************************************************************************/ typedef struct { ma_format format; ma_uint32 channels; ma_uint32 sampleRate; double cutoffFrequency; double q; } ma_bpf2_config; MA_API ma_bpf2_config ma_bpf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, double q); typedef struct { ma_biquad bq; /* The second order band-pass filter is implemented as a biquad filter. */ } ma_bpf2; MA_API ma_result ma_bpf2_get_heap_size(const ma_bpf2_config* pConfig, size_t* pHeapSizeInBytes); MA_API ma_result ma_bpf2_init_preallocated(const ma_bpf2_config* pConfig, void* pHeap, ma_bpf2* pBPF); MA_API ma_result ma_bpf2_init(const ma_bpf2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_bpf2* pBPF); MA_API void ma_bpf2_uninit(ma_bpf2* pBPF, const ma_allocation_callbacks* pAllocationCallbacks); MA_API ma_result ma_bpf2_reinit(const ma_bpf2_config* pConfig, ma_bpf2* pBPF); MA_API ma_result ma_bpf2_process_pcm_frames(ma_bpf2* pBPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); MA_API ma_uint32 ma_bpf2_get_latency(const ma_bpf2* pBPF); typedef struct { ma_format format; ma_uint32 channels; ma_uint32 sampleRate; double cutoffFrequency; ma_uint32 order; /* If set to 0, will be treated as a passthrough (no filtering will be applied). */ } ma_bpf_config; MA_API ma_bpf_config ma_bpf_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order); typedef struct { ma_format format; ma_uint32 channels; ma_uint32 bpf2Count; ma_bpf2* pBPF2; /* Memory management. */ void* _pHeap; ma_bool32 _ownsHeap; } ma_bpf; MA_API ma_result ma_bpf_get_heap_size(const ma_bpf_config* pConfig, size_t* pHeapSizeInBytes); MA_API ma_result ma_bpf_init_preallocated(const ma_bpf_config* pConfig, void* pHeap, ma_bpf* pBPF); MA_API ma_result ma_bpf_init(const ma_bpf_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_bpf* pBPF); MA_API void ma_bpf_uninit(ma_bpf* pBPF, const ma_allocation_callbacks* pAllocationCallbacks); MA_API ma_result ma_bpf_reinit(const ma_bpf_config* pConfig, ma_bpf* pBPF); MA_API ma_result ma_bpf_process_pcm_frames(ma_bpf* pBPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); MA_API ma_uint32 ma_bpf_get_latency(const ma_bpf* pBPF); /************************************************************************************************************************************************************** Notching Filter **************************************************************************************************************************************************************/ typedef struct { ma_format format; ma_uint32 channels; ma_uint32 sampleRate; double q; double frequency; } ma_notch2_config, ma_notch_config; MA_API ma_notch2_config ma_notch2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double q, double frequency); typedef struct { ma_biquad bq; } ma_notch2; MA_API ma_result ma_notch2_get_heap_size(const ma_notch2_config* pConfig, size_t* pHeapSizeInBytes); MA_API ma_result ma_notch2_init_preallocated(const ma_notch2_config* pConfig, void* pHeap, ma_notch2* pFilter); MA_API ma_result ma_notch2_init(const ma_notch2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_notch2* pFilter); MA_API void ma_notch2_uninit(ma_notch2* pFilter, const ma_allocation_callbacks* pAllocationCallbacks); MA_API ma_result ma_notch2_reinit(const ma_notch2_config* pConfig, ma_notch2* pFilter); MA_API ma_result ma_notch2_process_pcm_frames(ma_notch2* pFilter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); MA_API ma_uint32 ma_notch2_get_latency(const ma_notch2* pFilter); /************************************************************************************************************************************************************** Peaking EQ Filter **************************************************************************************************************************************************************/ typedef struct { ma_format format; ma_uint32 channels; ma_uint32 sampleRate; double gainDB; double q; double frequency; } ma_peak2_config, ma_peak_config; MA_API ma_peak2_config ma_peak2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double q, double frequency); typedef struct { ma_biquad bq; } ma_peak2; MA_API ma_result ma_peak2_get_heap_size(const ma_peak2_config* pConfig, size_t* pHeapSizeInBytes); MA_API ma_result ma_peak2_init_preallocated(const ma_peak2_config* pConfig, void* pHeap, ma_peak2* pFilter); MA_API ma_result ma_peak2_init(const ma_peak2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_peak2* pFilter); MA_API void ma_peak2_uninit(ma_peak2* pFilter, const ma_allocation_callbacks* pAllocationCallbacks); MA_API ma_result ma_peak2_reinit(const ma_peak2_config* pConfig, ma_peak2* pFilter); MA_API ma_result ma_peak2_process_pcm_frames(ma_peak2* pFilter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); MA_API ma_uint32 ma_peak2_get_latency(const ma_peak2* pFilter); /************************************************************************************************************************************************************** Low Shelf Filter **************************************************************************************************************************************************************/ typedef struct { ma_format format; ma_uint32 channels; ma_uint32 sampleRate; double gainDB; double shelfSlope; double frequency; } ma_loshelf2_config, ma_loshelf_config; MA_API ma_loshelf2_config ma_loshelf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double shelfSlope, double frequency); typedef struct { ma_biquad bq; } ma_loshelf2; MA_API ma_result ma_loshelf2_get_heap_size(const ma_loshelf2_config* pConfig, size_t* pHeapSizeInBytes); MA_API ma_result ma_loshelf2_init_preallocated(const ma_loshelf2_config* pConfig, void* pHeap, ma_loshelf2* pFilter); MA_API ma_result ma_loshelf2_init(const ma_loshelf2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_loshelf2* pFilter); MA_API void ma_loshelf2_uninit(ma_loshelf2* pFilter, const ma_allocation_callbacks* pAllocationCallbacks); MA_API ma_result ma_loshelf2_reinit(const ma_loshelf2_config* pConfig, ma_loshelf2* pFilter); MA_API ma_result ma_loshelf2_process_pcm_frames(ma_loshelf2* pFilter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); MA_API ma_uint32 ma_loshelf2_get_latency(const ma_loshelf2* pFilter); /************************************************************************************************************************************************************** High Shelf Filter **************************************************************************************************************************************************************/ typedef struct { ma_format format; ma_uint32 channels; ma_uint32 sampleRate; double gainDB; double shelfSlope; double frequency; } ma_hishelf2_config, ma_hishelf_config; MA_API ma_hishelf2_config ma_hishelf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double shelfSlope, double frequency); typedef struct { ma_biquad bq; } ma_hishelf2; MA_API ma_result ma_hishelf2_get_heap_size(const ma_hishelf2_config* pConfig, size_t* pHeapSizeInBytes); MA_API ma_result ma_hishelf2_init_preallocated(const ma_hishelf2_config* pConfig, void* pHeap, ma_hishelf2* pFilter); MA_API ma_result ma_hishelf2_init(const ma_hishelf2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hishelf2* pFilter); MA_API void ma_hishelf2_uninit(ma_hishelf2* pFilter, const ma_allocation_callbacks* pAllocationCallbacks); MA_API ma_result ma_hishelf2_reinit(const ma_hishelf2_config* pConfig, ma_hishelf2* pFilter); MA_API ma_result ma_hishelf2_process_pcm_frames(ma_hishelf2* pFilter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); MA_API ma_uint32 ma_hishelf2_get_latency(const ma_hishelf2* pFilter); /* Delay */ typedef struct { ma_uint32 channels; ma_uint32 sampleRate; ma_uint32 delayInFrames; ma_bool32 delayStart; /* Set to true to delay the start of the output; false otherwise. */ float wet; /* 0..1. Default = 1. */ float dry; /* 0..1. Default = 1. */ float decay; /* 0..1. Default = 0 (no feedback). Feedback decay. Use this for echo. */ } ma_delay_config; MA_API ma_delay_config ma_delay_config_init(ma_uint32 channels, ma_uint32 sampleRate, ma_uint32 delayInFrames, float decay); typedef struct { ma_delay_config config; ma_uint32 cursor; /* Feedback is written to this cursor. Always equal or in front of the read cursor. */ ma_uint32 bufferSizeInFrames; float* pBuffer; } ma_delay; MA_API ma_result ma_delay_init(const ma_delay_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_delay* pDelay); MA_API void ma_delay_uninit(ma_delay* pDelay, const ma_allocation_callbacks* pAllocationCallbacks); MA_API ma_result ma_delay_process_pcm_frames(ma_delay* pDelay, void* pFramesOut, const void* pFramesIn, ma_uint32 frameCount); MA_API void ma_delay_set_wet(ma_delay* pDelay, float value); MA_API float ma_delay_get_wet(const ma_delay* pDelay); MA_API void ma_delay_set_dry(ma_delay* pDelay, float value); MA_API float ma_delay_get_dry(const ma_delay* pDelay); MA_API void ma_delay_set_decay(ma_delay* pDelay, float value); MA_API float ma_delay_get_decay(const ma_delay* pDelay); /* Gainer for smooth volume changes. */ typedef struct { ma_uint32 channels; ma_uint32 smoothTimeInFrames; } ma_gainer_config; MA_API ma_gainer_config ma_gainer_config_init(ma_uint32 channels, ma_uint32 smoothTimeInFrames); typedef struct { ma_gainer_config config; ma_uint32 t; float* pOldGains; float* pNewGains; /* Memory management. */ void* _pHeap; ma_bool32 _ownsHeap; } ma_gainer; MA_API ma_result ma_gainer_get_heap_size(const ma_gainer_config* pConfig, size_t* pHeapSizeInBytes); MA_API ma_result ma_gainer_init_preallocated(const ma_gainer_config* pConfig, void* pHeap, ma_gainer* pGainer); MA_API ma_result ma_gainer_init(const ma_gainer_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_gainer* pGainer); MA_API void ma_gainer_uninit(ma_gainer* pGainer, const ma_allocation_callbacks* pAllocationCallbacks); MA_API ma_result ma_gainer_process_pcm_frames(ma_gainer* pGainer, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); MA_API ma_result ma_gainer_set_gain(ma_gainer* pGainer, float newGain); MA_API ma_result ma_gainer_set_gains(ma_gainer* pGainer, float* pNewGains); /* Stereo panner. */ typedef enum { ma_pan_mode_balance = 0, /* Does not blend one side with the other. Technically just a balance. Compatible with other popular audio engines and therefore the default. */ ma_pan_mode_pan /* A true pan. The sound from one side will "move" to the other side and blend with it. */ } ma_pan_mode; typedef struct { ma_format format; ma_uint32 channels; ma_pan_mode mode; float pan; } ma_panner_config; MA_API ma_panner_config ma_panner_config_init(ma_format format, ma_uint32 channels); typedef struct { ma_format format; ma_uint32 channels; ma_pan_mode mode; float pan; /* -1..1 where 0 is no pan, -1 is left side, +1 is right side. Defaults to 0. */ } ma_panner; MA_API ma_result ma_panner_init(const ma_panner_config* pConfig, ma_panner* pPanner); MA_API ma_result ma_panner_process_pcm_frames(ma_panner* pPanner, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); MA_API void ma_panner_set_mode(ma_panner* pPanner, ma_pan_mode mode); MA_API ma_pan_mode ma_panner_get_mode(const ma_panner* pPanner); MA_API void ma_panner_set_pan(ma_panner* pPanner, float pan); MA_API float ma_panner_get_pan(const ma_panner* pPanner); /* Fader. */ typedef struct { ma_format format; ma_uint32 channels; ma_uint32 sampleRate; } ma_fader_config; MA_API ma_fader_config ma_fader_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate); typedef struct { ma_fader_config config; float volumeBeg; /* If volumeBeg and volumeEnd is equal to 1, no fading happens (ma_fader_process_pcm_frames() will run as a passthrough). */ float volumeEnd; ma_uint64 lengthInFrames; /* The total length of the fade. */ ma_uint64 cursorInFrames; /* The current time in frames. Incremented by ma_fader_process_pcm_frames(). */ } ma_fader; MA_API ma_result ma_fader_init(const ma_fader_config* pConfig, ma_fader* pFader); MA_API ma_result ma_fader_process_pcm_frames(ma_fader* pFader, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); MA_API void ma_fader_get_data_format(const ma_fader* pFader, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate); MA_API void ma_fader_set_fade(ma_fader* pFader, float volumeBeg, float volumeEnd, ma_uint64 lengthInFrames); MA_API float ma_fader_get_current_volume(ma_fader* pFader); /* Spatializer. */ typedef struct { float x; float y; float z; } ma_vec3f; typedef enum { ma_attenuation_model_none, /* No distance attenuation and no spatialization. */ ma_attenuation_model_inverse, /* Equivalent to OpenAL's AL_INVERSE_DISTANCE_CLAMPED. */ ma_attenuation_model_linear, /* Linear attenuation. Equivalent to OpenAL's AL_LINEAR_DISTANCE_CLAMPED. */ ma_attenuation_model_exponential /* Exponential attenuation. Equivalent to OpenAL's AL_EXPONENT_DISTANCE_CLAMPED. */ } ma_attenuation_model; typedef enum { ma_positioning_absolute, ma_positioning_relative } ma_positioning; typedef enum { ma_handedness_right, ma_handedness_left } ma_handedness; typedef struct { ma_uint32 channelsOut; ma_channel* pChannelMapOut; ma_handedness handedness; /* Defaults to right. Forward is -1 on the Z axis. In a left handed system, forward is +1 on the Z axis. */ float coneInnerAngleInRadians; float coneOuterAngleInRadians; float coneOuterGain; float speedOfSound; ma_vec3f worldUp; } ma_spatializer_listener_config; MA_API ma_spatializer_listener_config ma_spatializer_listener_config_init(ma_uint32 channelsOut); typedef struct { ma_spatializer_listener_config config; ma_vec3f position; /* The absolute position of the listener. */ ma_vec3f direction; /* The direction the listener is facing. The world up vector is config.worldUp. */ ma_vec3f velocity; ma_bool32 isEnabled; /* Memory management. */ ma_bool32 _ownsHeap; void* _pHeap; } ma_spatializer_listener; MA_API ma_result ma_spatializer_listener_get_heap_size(const ma_spatializer_listener_config* pConfig, size_t* pHeapSizeInBytes); MA_API ma_result ma_spatializer_listener_init_preallocated(const ma_spatializer_listener_config* pConfig, void* pHeap, ma_spatializer_listener* pListener); MA_API ma_result ma_spatializer_listener_init(const ma_spatializer_listener_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_spatializer_listener* pListener); MA_API void ma_spatializer_listener_uninit(ma_spatializer_listener* pListener, const ma_allocation_callbacks* pAllocationCallbacks); MA_API ma_channel* ma_spatializer_listener_get_channel_map(ma_spatializer_listener* pListener); MA_API void ma_spatializer_listener_set_cone(ma_spatializer_listener* pListener, float innerAngleInRadians, float outerAngleInRadians, float outerGain); MA_API void ma_spatializer_listener_get_cone(const ma_spatializer_listener* pListener, float* pInnerAngleInRadians, float* pOuterAngleInRadians, float* pOuterGain); MA_API void ma_spatializer_listener_set_position(ma_spatializer_listener* pListener, float x, float y, float z); MA_API ma_vec3f ma_spatializer_listener_get_position(const ma_spatializer_listener* pListener); MA_API void ma_spatializer_listener_set_direction(ma_spatializer_listener* pListener, float x, float y, float z); MA_API ma_vec3f ma_spatializer_listener_get_direction(const ma_spatializer_listener* pListener); MA_API void ma_spatializer_listener_set_velocity(ma_spatializer_listener* pListener, float x, float y, float z); MA_API ma_vec3f ma_spatializer_listener_get_velocity(const ma_spatializer_listener* pListener); MA_API void ma_spatializer_listener_set_speed_of_sound(ma_spatializer_listener* pListener, float speedOfSound); MA_API float ma_spatializer_listener_get_speed_of_sound(const ma_spatializer_listener* pListener); MA_API void ma_spatializer_listener_set_world_up(ma_spatializer_listener* pListener, float x, float y, float z); MA_API ma_vec3f ma_spatializer_listener_get_world_up(const ma_spatializer_listener* pListener); MA_API void ma_spatializer_listener_set_enabled(ma_spatializer_listener* pListener, ma_bool32 isEnabled); MA_API ma_bool32 ma_spatializer_listener_is_enabled(const ma_spatializer_listener* pListener); typedef struct { ma_uint32 channelsIn; ma_uint32 channelsOut; ma_channel* pChannelMapIn; ma_attenuation_model attenuationModel; ma_positioning positioning; ma_handedness handedness; /* Defaults to right. Forward is -1 on the Z axis. In a left handed system, forward is +1 on the Z axis. */ float minGain; float maxGain; float minDistance; float maxDistance; float rolloff; float coneInnerAngleInRadians; float coneOuterAngleInRadians; float coneOuterGain; float dopplerFactor; /* Set to 0 to disable doppler effect. */ float directionalAttenuationFactor; /* Set to 0 to disable directional attenuation. */ ma_uint32 gainSmoothTimeInFrames; /* When the gain of a channel changes during spatialization, the transition will be linearly interpolated over this number of frames. */ } ma_spatializer_config; MA_API ma_spatializer_config ma_spatializer_config_init(ma_uint32 channelsIn, ma_uint32 channelsOut); typedef struct { ma_uint32 channelsIn; ma_uint32 channelsOut; ma_channel* pChannelMapIn; ma_attenuation_model attenuationModel; ma_positioning positioning; ma_handedness handedness; /* Defaults to right. Forward is -1 on the Z axis. In a left handed system, forward is +1 on the Z axis. */ float minGain; float maxGain; float minDistance; float maxDistance; float rolloff; float coneInnerAngleInRadians; float coneOuterAngleInRadians; float coneOuterGain; float dopplerFactor; /* Set to 0 to disable doppler effect. */ float directionalAttenuationFactor; /* Set to 0 to disable directional attenuation. */ ma_uint32 gainSmoothTimeInFrames; /* When the gain of a channel changes during spatialization, the transition will be linearly interpolated over this number of frames. */ ma_vec3f position; ma_vec3f direction; ma_vec3f velocity; /* For doppler effect. */ float dopplerPitch; /* Will be updated by ma_spatializer_process_pcm_frames() and can be used by higher level functions to apply a pitch shift for doppler effect. */ ma_gainer gainer; /* For smooth gain transitions. */ float* pNewChannelGainsOut; /* An offset of _pHeap. Used by ma_spatializer_process_pcm_frames() to store new channel gains. The number of elements in this array is equal to config.channelsOut. */ /* Memory management. */ void* _pHeap; ma_bool32 _ownsHeap; } ma_spatializer; MA_API ma_result ma_spatializer_get_heap_size(const ma_spatializer_config* pConfig, size_t* pHeapSizeInBytes); MA_API ma_result ma_spatializer_init_preallocated(const ma_spatializer_config* pConfig, void* pHeap, ma_spatializer* pSpatializer); MA_API ma_result ma_spatializer_init(const ma_spatializer_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_spatializer* pSpatializer); MA_API void ma_spatializer_uninit(ma_spatializer* pSpatializer, const ma_allocation_callbacks* pAllocationCallbacks); MA_API ma_result ma_spatializer_process_pcm_frames(ma_spatializer* pSpatializer, ma_spatializer_listener* pListener, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); MA_API ma_uint32 ma_spatializer_get_input_channels(const ma_spatializer* pSpatializer); MA_API ma_uint32 ma_spatializer_get_output_channels(const ma_spatializer* pSpatializer); MA_API void ma_spatializer_set_attenuation_model(ma_spatializer* pSpatializer, ma_attenuation_model attenuationModel); MA_API ma_attenuation_model ma_spatializer_get_attenuation_model(const ma_spatializer* pSpatializer); MA_API void ma_spatializer_set_positioning(ma_spatializer* pSpatializer, ma_positioning positioning); MA_API ma_positioning ma_spatializer_get_positioning(const ma_spatializer* pSpatializer); MA_API void ma_spatializer_set_rolloff(ma_spatializer* pSpatializer, float rolloff); MA_API float ma_spatializer_get_rolloff(const ma_spatializer* pSpatializer); MA_API void ma_spatializer_set_min_gain(ma_spatializer* pSpatializer, float minGain); MA_API float ma_spatializer_get_min_gain(const ma_spatializer* pSpatializer); MA_API void ma_spatializer_set_max_gain(ma_spatializer* pSpatializer, float maxGain); MA_API float ma_spatializer_get_max_gain(const ma_spatializer* pSpatializer); MA_API void ma_spatializer_set_min_distance(ma_spatializer* pSpatializer, float minDistance); MA_API float ma_spatializer_get_min_distance(const ma_spatializer* pSpatializer); MA_API void ma_spatializer_set_max_distance(ma_spatializer* pSpatializer, float maxDistance); MA_API float ma_spatializer_get_max_distance(const ma_spatializer* pSpatializer); MA_API void ma_spatializer_set_cone(ma_spatializer* pSpatializer, float innerAngleInRadians, float outerAngleInRadians, float outerGain); MA_API void ma_spatializer_get_cone(const ma_spatializer* pSpatializer, float* pInnerAngleInRadians, float* pOuterAngleInRadians, float* pOuterGain); MA_API void ma_spatializer_set_doppler_factor(ma_spatializer* pSpatializer, float dopplerFactor); MA_API float ma_spatializer_get_doppler_factor(const ma_spatializer* pSpatializer); MA_API void ma_spatializer_set_directional_attenuation_factor(ma_spatializer* pSpatializer, float directionalAttenuationFactor); MA_API float ma_spatializer_get_directional_attenuation_factor(const ma_spatializer* pSpatializer); MA_API void ma_spatializer_set_position(ma_spatializer* pSpatializer, float x, float y, float z); MA_API ma_vec3f ma_spatializer_get_position(const ma_spatializer* pSpatializer); MA_API void ma_spatializer_set_direction(ma_spatializer* pSpatializer, float x, float y, float z); MA_API ma_vec3f ma_spatializer_get_direction(const ma_spatializer* pSpatializer); MA_API void ma_spatializer_set_velocity(ma_spatializer* pSpatializer, float x, float y, float z); MA_API ma_vec3f ma_spatializer_get_velocity(const ma_spatializer* pSpatializer); MA_API void ma_spatializer_get_relative_position_and_direction(const ma_spatializer* pSpatializer, const ma_spatializer_listener* pListener, ma_vec3f* pRelativePos, ma_vec3f* pRelativeDir); /************************************************************************************************************************************************************ ************************************************************************************************************************************************************* DATA CONVERSION =============== This section contains the APIs for data conversion. You will find everything here for channel mapping, sample format conversion, resampling, etc. ************************************************************************************************************************************************************* ************************************************************************************************************************************************************/ /************************************************************************************************************************************************************** Resampling **************************************************************************************************************************************************************/ typedef struct { ma_format format; ma_uint32 channels; ma_uint32 sampleRateIn; ma_uint32 sampleRateOut; ma_uint32 lpfOrder; /* The low-pass filter order. Setting this to 0 will disable low-pass filtering. */ double lpfNyquistFactor; /* 0..1. Defaults to 1. 1 = Half the sampling frequency (Nyquist Frequency), 0.5 = Quarter the sampling frequency (half Nyquest Frequency), etc. */ } ma_linear_resampler_config; MA_API ma_linear_resampler_config ma_linear_resampler_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut); typedef struct { ma_linear_resampler_config config; ma_uint32 inAdvanceInt; ma_uint32 inAdvanceFrac; ma_uint32 inTimeInt; ma_uint32 inTimeFrac; union { float* f32; ma_int16* s16; } x0; /* The previous input frame. */ union { float* f32; ma_int16* s16; } x1; /* The next input frame. */ ma_lpf lpf; /* Memory management. */ void* _pHeap; ma_bool32 _ownsHeap; } ma_linear_resampler; MA_API ma_result ma_linear_resampler_get_heap_size(const ma_linear_resampler_config* pConfig, size_t* pHeapSizeInBytes); MA_API ma_result ma_linear_resampler_init_preallocated(const ma_linear_resampler_config* pConfig, void* pHeap, ma_linear_resampler* pResampler); MA_API ma_result ma_linear_resampler_init(const ma_linear_resampler_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_linear_resampler* pResampler); MA_API void ma_linear_resampler_uninit(ma_linear_resampler* pResampler, const ma_allocation_callbacks* pAllocationCallbacks); MA_API ma_result ma_linear_resampler_process_pcm_frames(ma_linear_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut); MA_API ma_result ma_linear_resampler_set_rate(ma_linear_resampler* pResampler, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut); MA_API ma_result ma_linear_resampler_set_rate_ratio(ma_linear_resampler* pResampler, float ratioInOut); MA_API ma_uint64 ma_linear_resampler_get_input_latency(const ma_linear_resampler* pResampler); MA_API ma_uint64 ma_linear_resampler_get_output_latency(const ma_linear_resampler* pResampler); MA_API ma_result ma_linear_resampler_get_required_input_frame_count(const ma_linear_resampler* pResampler, ma_uint64 outputFrameCount, ma_uint64* pInputFrameCount); MA_API ma_result ma_linear_resampler_get_expected_output_frame_count(const ma_linear_resampler* pResampler, ma_uint64 inputFrameCount, ma_uint64* pOutputFrameCount); MA_API ma_result ma_linear_resampler_reset(ma_linear_resampler* pResampler); typedef struct ma_resampler_config ma_resampler_config; typedef void ma_resampling_backend; typedef struct { ma_result (* onGetHeapSize )(void* pUserData, const ma_resampler_config* pConfig, size_t* pHeapSizeInBytes); ma_result (* onInit )(void* pUserData, const ma_resampler_config* pConfig, void* pHeap, ma_resampling_backend** ppBackend); void (* onUninit )(void* pUserData, ma_resampling_backend* pBackend, const ma_allocation_callbacks* pAllocationCallbacks); ma_result (* onProcess )(void* pUserData, ma_resampling_backend* pBackend, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut); ma_result (* onSetRate )(void* pUserData, ma_resampling_backend* pBackend, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut); /* Optional. Rate changes will be disabled. */ ma_uint64 (* onGetInputLatency )(void* pUserData, const ma_resampling_backend* pBackend); /* Optional. Latency will be reported as 0. */ ma_uint64 (* onGetOutputLatency )(void* pUserData, const ma_resampling_backend* pBackend); /* Optional. Latency will be reported as 0. */ ma_result (* onGetRequiredInputFrameCount )(void* pUserData, const ma_resampling_backend* pBackend, ma_uint64 outputFrameCount, ma_uint64* pInputFrameCount); /* Optional. Latency mitigation will be disabled. */ ma_result (* onGetExpectedOutputFrameCount)(void* pUserData, const ma_resampling_backend* pBackend, ma_uint64 inputFrameCount, ma_uint64* pOutputFrameCount); /* Optional. Latency mitigation will be disabled. */ ma_result (* onReset )(void* pUserData, ma_resampling_backend* pBackend); } ma_resampling_backend_vtable; typedef enum { ma_resample_algorithm_linear = 0, /* Fastest, lowest quality. Optional low-pass filtering. Default. */ ma_resample_algorithm_custom, } ma_resample_algorithm; struct ma_resampler_config { ma_format format; /* Must be either ma_format_f32 or ma_format_s16. */ ma_uint32 channels; ma_uint32 sampleRateIn; ma_uint32 sampleRateOut; ma_resample_algorithm algorithm; /* When set to ma_resample_algorithm_custom, pBackendVTable will be used. */ ma_resampling_backend_vtable* pBackendVTable; void* pBackendUserData; struct { ma_uint32 lpfOrder; } linear; }; MA_API ma_resampler_config ma_resampler_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut, ma_resample_algorithm algorithm); typedef struct { ma_resampling_backend* pBackend; ma_resampling_backend_vtable* pBackendVTable; void* pBackendUserData; ma_format format; ma_uint32 channels; ma_uint32 sampleRateIn; ma_uint32 sampleRateOut; union { ma_linear_resampler linear; } state; /* State for stock resamplers so we can avoid a malloc. For stock resamplers, pBackend will point here. */ /* Memory management. */ void* _pHeap; ma_bool32 _ownsHeap; } ma_resampler; MA_API ma_result ma_resampler_get_heap_size(const ma_resampler_config* pConfig, size_t* pHeapSizeInBytes); MA_API ma_result ma_resampler_init_preallocated(const ma_resampler_config* pConfig, void* pHeap, ma_resampler* pResampler); /* Initializes a new resampler object from a config. */ MA_API ma_result ma_resampler_init(const ma_resampler_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_resampler* pResampler); /* Uninitializes a resampler. */ MA_API void ma_resampler_uninit(ma_resampler* pResampler, const ma_allocation_callbacks* pAllocationCallbacks); /* Converts the given input data. Both the input and output frames must be in the format specified in the config when the resampler was initilized. On input, [pFrameCountOut] contains the number of output frames to process. On output it contains the number of output frames that were actually processed, which may be less than the requested amount which will happen if there's not enough input data. You can use ma_resampler_get_expected_output_frame_count() to know how many output frames will be processed for a given number of input frames. On input, [pFrameCountIn] contains the number of input frames contained in [pFramesIn]. On output it contains the number of whole input frames that were actually processed. You can use ma_resampler_get_required_input_frame_count() to know how many input frames you should provide for a given number of output frames. [pFramesIn] can be NULL, in which case zeroes will be used instead. If [pFramesOut] is NULL, a seek is performed. In this case, if [pFrameCountOut] is not NULL it will seek by the specified number of output frames. Otherwise, if [pFramesCountOut] is NULL and [pFrameCountIn] is not NULL, it will seek by the specified number of input frames. When seeking, [pFramesIn] is allowed to NULL, in which case the internal timing state will be updated, but no input will be processed. In this case, any internal filter state will be updated as if zeroes were passed in. It is an error for [pFramesOut] to be non-NULL and [pFrameCountOut] to be NULL. It is an error for both [pFrameCountOut] and [pFrameCountIn] to be NULL. */ MA_API ma_result ma_resampler_process_pcm_frames(ma_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut); /* Sets the input and output sample sample rate. */ MA_API ma_result ma_resampler_set_rate(ma_resampler* pResampler, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut); /* Sets the input and output sample rate as a ratio. The ration is in/out. */ MA_API ma_result ma_resampler_set_rate_ratio(ma_resampler* pResampler, float ratio); /* Retrieves the latency introduced by the resampler in input frames. */ MA_API ma_uint64 ma_resampler_get_input_latency(const ma_resampler* pResampler); /* Retrieves the latency introduced by the resampler in output frames. */ MA_API ma_uint64 ma_resampler_get_output_latency(const ma_resampler* pResampler); /* Calculates the number of whole input frames that would need to be read from the client in order to output the specified number of output frames. The returned value does not include cached input frames. It only returns the number of extra frames that would need to be read from the input buffer in order to output the specified number of output frames. */ MA_API ma_result ma_resampler_get_required_input_frame_count(const ma_resampler* pResampler, ma_uint64 outputFrameCount, ma_uint64* pInputFrameCount); /* Calculates the number of whole output frames that would be output after fully reading and consuming the specified number of input frames. */ MA_API ma_result ma_resampler_get_expected_output_frame_count(const ma_resampler* pResampler, ma_uint64 inputFrameCount, ma_uint64* pOutputFrameCount); /* Resets the resampler's timer and clears it's internal cache. */ MA_API ma_result ma_resampler_reset(ma_resampler* pResampler); /************************************************************************************************************************************************************** Channel Conversion **************************************************************************************************************************************************************/ typedef enum { ma_channel_conversion_path_unknown, ma_channel_conversion_path_passthrough, ma_channel_conversion_path_mono_out, /* Converting to mono. */ ma_channel_conversion_path_mono_in, /* Converting from mono. */ ma_channel_conversion_path_shuffle, /* Simple shuffle. Will use this when all channels are present in both input and output channel maps, but just in a different order. */ ma_channel_conversion_path_weights /* Blended based on weights. */ } ma_channel_conversion_path; typedef enum { ma_mono_expansion_mode_duplicate = 0, /* The default. */ ma_mono_expansion_mode_average, /* Average the mono channel across all channels. */ ma_mono_expansion_mode_stereo_only, /* Duplicate to the left and right channels only and ignore the others. */ ma_mono_expansion_mode_default = ma_mono_expansion_mode_duplicate } ma_mono_expansion_mode; typedef struct { ma_format format; ma_uint32 channelsIn; ma_uint32 channelsOut; const ma_channel* pChannelMapIn; const ma_channel* pChannelMapOut; ma_channel_mix_mode mixingMode; ma_bool32 calculateLFEFromSpatialChannels; /* When an output LFE channel is present, but no input LFE, set to true to set the output LFE to the average of all spatial channels (LR, FR, etc.). Ignored when an input LFE is present. */ float** ppWeights; /* [in][out]. Only used when mixingMode is set to ma_channel_mix_mode_custom_weights. */ } ma_channel_converter_config; MA_API ma_channel_converter_config ma_channel_converter_config_init(ma_format format, ma_uint32 channelsIn, const ma_channel* pChannelMapIn, ma_uint32 channelsOut, const ma_channel* pChannelMapOut, ma_channel_mix_mode mixingMode); typedef struct { ma_format format; ma_uint32 channelsIn; ma_uint32 channelsOut; ma_channel_mix_mode mixingMode; ma_channel_conversion_path conversionPath; ma_channel* pChannelMapIn; ma_channel* pChannelMapOut; ma_uint8* pShuffleTable; /* Indexed by output channel index. */ union { float** f32; ma_int32** s16; } weights; /* [in][out] */ /* Memory management. */ void* _pHeap; ma_bool32 _ownsHeap; } ma_channel_converter; MA_API ma_result ma_channel_converter_get_heap_size(const ma_channel_converter_config* pConfig, size_t* pHeapSizeInBytes); MA_API ma_result ma_channel_converter_init_preallocated(const ma_channel_converter_config* pConfig, void* pHeap, ma_channel_converter* pConverter); MA_API ma_result ma_channel_converter_init(const ma_channel_converter_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_channel_converter* pConverter); MA_API void ma_channel_converter_uninit(ma_channel_converter* pConverter, const ma_allocation_callbacks* pAllocationCallbacks); MA_API ma_result ma_channel_converter_process_pcm_frames(ma_channel_converter* pConverter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); MA_API ma_result ma_channel_converter_get_input_channel_map(const ma_channel_converter* pConverter, ma_channel* pChannelMap, size_t channelMapCap); MA_API ma_result ma_channel_converter_get_output_channel_map(const ma_channel_converter* pConverter, ma_channel* pChannelMap, size_t channelMapCap); /************************************************************************************************************************************************************** Data Conversion **************************************************************************************************************************************************************/ typedef struct { ma_format formatIn; ma_format formatOut; ma_uint32 channelsIn; ma_uint32 channelsOut; ma_uint32 sampleRateIn; ma_uint32 sampleRateOut; ma_channel* pChannelMapIn; ma_channel* pChannelMapOut; ma_dither_mode ditherMode; ma_channel_mix_mode channelMixMode; ma_bool32 calculateLFEFromSpatialChannels; /* When an output LFE channel is present, but no input LFE, set to true to set the output LFE to the average of all spatial channels (LR, FR, etc.). Ignored when an input LFE is present. */ float** ppChannelWeights; /* [in][out]. Only used when mixingMode is set to ma_channel_mix_mode_custom_weights. */ ma_bool32 allowDynamicSampleRate; ma_resampler_config resampling; } ma_data_converter_config; MA_API ma_data_converter_config ma_data_converter_config_init_default(void); MA_API ma_data_converter_config ma_data_converter_config_init(ma_format formatIn, ma_format formatOut, ma_uint32 channelsIn, ma_uint32 channelsOut, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut); typedef enum { ma_data_converter_execution_path_passthrough, /* No conversion. */ ma_data_converter_execution_path_format_only, /* Only format conversion. */ ma_data_converter_execution_path_channels_only, /* Only channel conversion. */ ma_data_converter_execution_path_resample_only, /* Only resampling. */ ma_data_converter_execution_path_resample_first, /* All conversions, but resample as the first step. */ ma_data_converter_execution_path_channels_first /* All conversions, but channels as the first step. */ } ma_data_converter_execution_path; typedef struct { ma_format formatIn; ma_format formatOut; ma_uint32 channelsIn; ma_uint32 channelsOut; ma_uint32 sampleRateIn; ma_uint32 sampleRateOut; ma_dither_mode ditherMode; ma_data_converter_execution_path executionPath; /* The execution path the data converter will follow when processing. */ ma_channel_converter channelConverter; ma_resampler resampler; ma_bool8 hasPreFormatConversion; ma_bool8 hasPostFormatConversion; ma_bool8 hasChannelConverter; ma_bool8 hasResampler; ma_bool8 isPassthrough; /* Memory management. */ ma_bool8 _ownsHeap; void* _pHeap; } ma_data_converter; MA_API ma_result ma_data_converter_get_heap_size(const ma_data_converter_config* pConfig, size_t* pHeapSizeInBytes); MA_API ma_result ma_data_converter_init_preallocated(const ma_data_converter_config* pConfig, void* pHeap, ma_data_converter* pConverter); MA_API ma_result ma_data_converter_init(const ma_data_converter_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_converter* pConverter); MA_API void ma_data_converter_uninit(ma_data_converter* pConverter, const ma_allocation_callbacks* pAllocationCallbacks); MA_API ma_result ma_data_converter_process_pcm_frames(ma_data_converter* pConverter, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut); MA_API ma_result ma_data_converter_set_rate(ma_data_converter* pConverter, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut); MA_API ma_result ma_data_converter_set_rate_ratio(ma_data_converter* pConverter, float ratioInOut); MA_API ma_uint64 ma_data_converter_get_input_latency(const ma_data_converter* pConverter); MA_API ma_uint64 ma_data_converter_get_output_latency(const ma_data_converter* pConverter); MA_API ma_result ma_data_converter_get_required_input_frame_count(const ma_data_converter* pConverter, ma_uint64 outputFrameCount, ma_uint64* pInputFrameCount); MA_API ma_result ma_data_converter_get_expected_output_frame_count(const ma_data_converter* pConverter, ma_uint64 inputFrameCount, ma_uint64* pOutputFrameCount); MA_API ma_result ma_data_converter_get_input_channel_map(const ma_data_converter* pConverter, ma_channel* pChannelMap, size_t channelMapCap); MA_API ma_result ma_data_converter_get_output_channel_map(const ma_data_converter* pConverter, ma_channel* pChannelMap, size_t channelMapCap); MA_API ma_result ma_data_converter_reset(ma_data_converter* pConverter); /************************************************************************************************************************************************************ Format Conversion ************************************************************************************************************************************************************/ MA_API void ma_pcm_u8_to_s16(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); MA_API void ma_pcm_u8_to_s24(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); MA_API void ma_pcm_u8_to_s32(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); MA_API void ma_pcm_u8_to_f32(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); MA_API void ma_pcm_s16_to_u8(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); MA_API void ma_pcm_s16_to_s24(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); MA_API void ma_pcm_s16_to_s32(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); MA_API void ma_pcm_s16_to_f32(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); MA_API void ma_pcm_s24_to_u8(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); MA_API void ma_pcm_s24_to_s16(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); MA_API void ma_pcm_s24_to_s32(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); MA_API void ma_pcm_s24_to_f32(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); MA_API void ma_pcm_s32_to_u8(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); MA_API void ma_pcm_s32_to_s16(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); MA_API void ma_pcm_s32_to_s24(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); MA_API void ma_pcm_s32_to_f32(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); MA_API void ma_pcm_f32_to_u8(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); MA_API void ma_pcm_f32_to_s16(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); MA_API void ma_pcm_f32_to_s24(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); MA_API void ma_pcm_f32_to_s32(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode); MA_API void ma_pcm_convert(void* pOut, ma_format formatOut, const void* pIn, ma_format formatIn, ma_uint64 sampleCount, ma_dither_mode ditherMode); MA_API void ma_convert_pcm_frames_format(void* pOut, ma_format formatOut, const void* pIn, ma_format formatIn, ma_uint64 frameCount, ma_uint32 channels, ma_dither_mode ditherMode); /* Deinterleaves an interleaved buffer. */ MA_API void ma_deinterleave_pcm_frames(ma_format format, ma_uint32 channels, ma_uint64 frameCount, const void* pInterleavedPCMFrames, void** ppDeinterleavedPCMFrames); /* Interleaves a group of deinterleaved buffers. */ MA_API void ma_interleave_pcm_frames(ma_format format, ma_uint32 channels, ma_uint64 frameCount, const void** ppDeinterleavedPCMFrames, void* pInterleavedPCMFrames); /************************************************************************************************************************************************************ Channel Maps ************************************************************************************************************************************************************/ /* This is used in the shuffle table to indicate that the channel index is undefined and should be ignored. */ #define MA_CHANNEL_INDEX_NULL 255 /* Retrieves the channel position of the specified channel in the given channel map. The pChannelMap parameter can be null, in which case miniaudio's default channel map will be assumed. */ MA_API ma_channel ma_channel_map_get_channel(const ma_channel* pChannelMap, ma_uint32 channelCount, ma_uint32 channelIndex); /* Initializes a blank channel map. When a blank channel map is specified anywhere it indicates that the native channel map should be used. */ MA_API void ma_channel_map_init_blank(ma_channel* pChannelMap, ma_uint32 channels); /* Helper for retrieving a standard channel map. The output channel map buffer must have a capacity of at least `channelMapCap`. */ MA_API void ma_channel_map_init_standard(ma_standard_channel_map standardChannelMap, ma_channel* pChannelMap, size_t channelMapCap, ma_uint32 channels); /* Copies a channel map. Both input and output channel map buffers must have a capacity of at at least `channels`. */ MA_API void ma_channel_map_copy(ma_channel* pOut, const ma_channel* pIn, ma_uint32 channels); /* Copies a channel map if one is specified, otherwise copies the default channel map. The output buffer must have a capacity of at least `channels`. If not NULL, the input channel map must also have a capacity of at least `channels`. */ MA_API void ma_channel_map_copy_or_default(ma_channel* pOut, size_t channelMapCapOut, const ma_channel* pIn, ma_uint32 channels); /* Determines whether or not a channel map is valid. A blank channel map is valid (all channels set to MA_CHANNEL_NONE). The way a blank channel map is handled is context specific, but is usually treated as a passthrough. Invalid channel maps: - A channel map with no channels - A channel map with more than one channel and a mono channel The channel map buffer must have a capacity of at least `channels`. */ MA_API ma_bool32 ma_channel_map_is_valid(const ma_channel* pChannelMap, ma_uint32 channels); /* Helper for comparing two channel maps for equality. This assumes the channel count is the same between the two. Both channels map buffers must have a capacity of at least `channels`. */ MA_API ma_bool32 ma_channel_map_is_equal(const ma_channel* pChannelMapA, const ma_channel* pChannelMapB, ma_uint32 channels); /* Helper for determining if a channel map is blank (all channels set to MA_CHANNEL_NONE). The channel map buffer must have a capacity of at least `channels`. */ MA_API ma_bool32 ma_channel_map_is_blank(const ma_channel* pChannelMap, ma_uint32 channels); /* Helper for determining whether or not a channel is present in the given channel map. The channel map buffer must have a capacity of at least `channels`. */ MA_API ma_bool32 ma_channel_map_contains_channel_position(ma_uint32 channels, const ma_channel* pChannelMap, ma_channel channelPosition); /* Find a channel position in the given channel map. Returns MA_TRUE if the channel is found; MA_FALSE otherwise. The index of the channel is output to `pChannelIndex`. The channel map buffer must have a capacity of at least `channels`. */ MA_API ma_bool32 ma_channel_map_find_channel_position(ma_uint32 channels, const ma_channel* pChannelMap, ma_channel channelPosition, ma_uint32* pChannelIndex); /* Generates a string representing the given channel map. This is for printing and debugging purposes, not serialization/deserialization. Returns the length of the string, not including the null terminator. */ MA_API size_t ma_channel_map_to_string(const ma_channel* pChannelMap, ma_uint32 channels, char* pBufferOut, size_t bufferCap); /* Retrieves a human readable version of a channel position. */ MA_API const char* ma_channel_position_to_string(ma_channel channel); /************************************************************************************************************************************************************ Conversion Helpers ************************************************************************************************************************************************************/ /* High-level helper for doing a full format conversion in one go. Returns the number of output frames. Call this with pOut set to NULL to determine the required size of the output buffer. frameCountOut should be set to the capacity of pOut. If pOut is NULL, frameCountOut is ignored. A return value of 0 indicates an error. This function is useful for one-off bulk conversions, but if you're streaming data you should use the ma_data_converter APIs instead. */ MA_API ma_uint64 ma_convert_frames(void* pOut, ma_uint64 frameCountOut, ma_format formatOut, ma_uint32 channelsOut, ma_uint32 sampleRateOut, const void* pIn, ma_uint64 frameCountIn, ma_format formatIn, ma_uint32 channelsIn, ma_uint32 sampleRateIn); MA_API ma_uint64 ma_convert_frames_ex(void* pOut, ma_uint64 frameCountOut, const void* pIn, ma_uint64 frameCountIn, const ma_data_converter_config* pConfig); /************************************************************************************************************************************************************ Ring Buffer ************************************************************************************************************************************************************/ typedef struct { void* pBuffer; ma_uint32 subbufferSizeInBytes; ma_uint32 subbufferCount; ma_uint32 subbufferStrideInBytes; MA_ATOMIC(4, ma_uint32) encodedReadOffset; /* Most significant bit is the loop flag. Lower 31 bits contains the actual offset in bytes. Must be used atomically. */ MA_ATOMIC(4, ma_uint32) encodedWriteOffset; /* Most significant bit is the loop flag. Lower 31 bits contains the actual offset in bytes. Must be used atomically. */ ma_bool8 ownsBuffer; /* Used to know whether or not miniaudio is responsible for free()-ing the buffer. */ ma_bool8 clearOnWriteAcquire; /* When set, clears the acquired write buffer before returning from ma_rb_acquire_write(). */ ma_allocation_callbacks allocationCallbacks; } ma_rb; MA_API ma_result ma_rb_init_ex(size_t subbufferSizeInBytes, size_t subbufferCount, size_t subbufferStrideInBytes, void* pOptionalPreallocatedBuffer, const ma_allocation_callbacks* pAllocationCallbacks, ma_rb* pRB); MA_API ma_result ma_rb_init(size_t bufferSizeInBytes, void* pOptionalPreallocatedBuffer, const ma_allocation_callbacks* pAllocationCallbacks, ma_rb* pRB); MA_API void ma_rb_uninit(ma_rb* pRB); MA_API void ma_rb_reset(ma_rb* pRB); MA_API ma_result ma_rb_acquire_read(ma_rb* pRB, size_t* pSizeInBytes, void** ppBufferOut); MA_API ma_result ma_rb_commit_read(ma_rb* pRB, size_t sizeInBytes); MA_API ma_result ma_rb_acquire_write(ma_rb* pRB, size_t* pSizeInBytes, void** ppBufferOut); MA_API ma_result ma_rb_commit_write(ma_rb* pRB, size_t sizeInBytes); MA_API ma_result ma_rb_seek_read(ma_rb* pRB, size_t offsetInBytes); MA_API ma_result ma_rb_seek_write(ma_rb* pRB, size_t offsetInBytes); MA_API ma_int32 ma_rb_pointer_distance(ma_rb* pRB); /* Returns the distance between the write pointer and the read pointer. Should never be negative for a correct program. Will return the number of bytes that can be read before the read pointer hits the write pointer. */ MA_API ma_uint32 ma_rb_available_read(ma_rb* pRB); MA_API ma_uint32 ma_rb_available_write(ma_rb* pRB); MA_API size_t ma_rb_get_subbuffer_size(ma_rb* pRB); MA_API size_t ma_rb_get_subbuffer_stride(ma_rb* pRB); MA_API size_t ma_rb_get_subbuffer_offset(ma_rb* pRB, size_t subbufferIndex); MA_API void* ma_rb_get_subbuffer_ptr(ma_rb* pRB, size_t subbufferIndex, void* pBuffer); typedef struct { ma_rb rb; ma_format format; ma_uint32 channels; } ma_pcm_rb; MA_API ma_result ma_pcm_rb_init_ex(ma_format format, ma_uint32 channels, ma_uint32 subbufferSizeInFrames, ma_uint32 subbufferCount, ma_uint32 subbufferStrideInFrames, void* pOptionalPreallocatedBuffer, const ma_allocation_callbacks* pAllocationCallbacks, ma_pcm_rb* pRB); MA_API ma_result ma_pcm_rb_init(ma_format format, ma_uint32 channels, ma_uint32 bufferSizeInFrames, void* pOptionalPreallocatedBuffer, const ma_allocation_callbacks* pAllocationCallbacks, ma_pcm_rb* pRB); MA_API void ma_pcm_rb_uninit(ma_pcm_rb* pRB); MA_API void ma_pcm_rb_reset(ma_pcm_rb* pRB); MA_API ma_result ma_pcm_rb_acquire_read(ma_pcm_rb* pRB, ma_uint32* pSizeInFrames, void** ppBufferOut); MA_API ma_result ma_pcm_rb_commit_read(ma_pcm_rb* pRB, ma_uint32 sizeInFrames); MA_API ma_result ma_pcm_rb_acquire_write(ma_pcm_rb* pRB, ma_uint32* pSizeInFrames, void** ppBufferOut); MA_API ma_result ma_pcm_rb_commit_write(ma_pcm_rb* pRB, ma_uint32 sizeInFrames); MA_API ma_result ma_pcm_rb_seek_read(ma_pcm_rb* pRB, ma_uint32 offsetInFrames); MA_API ma_result ma_pcm_rb_seek_write(ma_pcm_rb* pRB, ma_uint32 offsetInFrames); MA_API ma_int32 ma_pcm_rb_pointer_distance(ma_pcm_rb* pRB); /* Return value is in frames. */ MA_API ma_uint32 ma_pcm_rb_available_read(ma_pcm_rb* pRB); MA_API ma_uint32 ma_pcm_rb_available_write(ma_pcm_rb* pRB); MA_API ma_uint32 ma_pcm_rb_get_subbuffer_size(ma_pcm_rb* pRB); MA_API ma_uint32 ma_pcm_rb_get_subbuffer_stride(ma_pcm_rb* pRB); MA_API ma_uint32 ma_pcm_rb_get_subbuffer_offset(ma_pcm_rb* pRB, ma_uint32 subbufferIndex); MA_API void* ma_pcm_rb_get_subbuffer_ptr(ma_pcm_rb* pRB, ma_uint32 subbufferIndex, void* pBuffer); /* The idea of the duplex ring buffer is to act as the intermediary buffer when running two asynchronous devices in a duplex set up. The capture device writes to it, and then a playback device reads from it. At the moment this is just a simple naive implementation, but in the future I want to implement some dynamic resampling to seamlessly handle desyncs. Note that the API is work in progress and may change at any time in any version. The size of the buffer is based on the capture side since that's what'll be written to the buffer. It is based on the capture period size in frames. The internal sample rate of the capture device is also needed in order to calculate the size. */ typedef struct { ma_pcm_rb rb; } ma_duplex_rb; MA_API ma_result ma_duplex_rb_init(ma_format captureFormat, ma_uint32 captureChannels, ma_uint32 sampleRate, ma_uint32 captureInternalSampleRate, ma_uint32 captureInternalPeriodSizeInFrames, const ma_allocation_callbacks* pAllocationCallbacks, ma_duplex_rb* pRB); MA_API ma_result ma_duplex_rb_uninit(ma_duplex_rb* pRB); /************************************************************************************************************************************************************ Miscellaneous Helpers ************************************************************************************************************************************************************/ /* Retrieves a human readable description of the given result code. */ MA_API const char* ma_result_description(ma_result result); /* malloc() */ MA_API void* ma_malloc(size_t sz, const ma_allocation_callbacks* pAllocationCallbacks); /* calloc() */ MA_API void* ma_calloc(size_t sz, const ma_allocation_callbacks* pAllocationCallbacks); /* realloc() */ MA_API void* ma_realloc(void* p, size_t sz, const ma_allocation_callbacks* pAllocationCallbacks); /* free() */ MA_API void ma_free(void* p, const ma_allocation_callbacks* pAllocationCallbacks); /* Performs an aligned malloc, with the assumption that the alignment is a power of 2. */ MA_API void* ma_aligned_malloc(size_t sz, size_t alignment, const ma_allocation_callbacks* pAllocationCallbacks); /* Free's an aligned malloc'd buffer. */ MA_API void ma_aligned_free(void* p, const ma_allocation_callbacks* pAllocationCallbacks); /* Retrieves a friendly name for a format. */ MA_API const char* ma_get_format_name(ma_format format); /* Blends two frames in floating point format. */ MA_API void ma_blend_f32(float* pOut, float* pInA, float* pInB, float factor, ma_uint32 channels); /* Retrieves the size of a sample in bytes for the given format. This API is efficient and is implemented using a lookup table. Thread Safety: SAFE This API is pure. */ MA_API ma_uint32 ma_get_bytes_per_sample(ma_format format); static MA_INLINE ma_uint32 ma_get_bytes_per_frame(ma_format format, ma_uint32 channels) { return ma_get_bytes_per_sample(format) * channels; } /* Converts a log level to a string. */ MA_API const char* ma_log_level_to_string(ma_uint32 logLevel); /************************************************************************************************************************************************************ Synchronization ************************************************************************************************************************************************************/ /* Locks a spinlock. */ MA_API ma_result ma_spinlock_lock(volatile ma_spinlock* pSpinlock); /* Locks a spinlock, but does not yield() when looping. */ MA_API ma_result ma_spinlock_lock_noyield(volatile ma_spinlock* pSpinlock); /* Unlocks a spinlock. */ MA_API ma_result ma_spinlock_unlock(volatile ma_spinlock* pSpinlock); #ifndef MA_NO_THREADING /* Creates a mutex. A mutex must be created from a valid context. A mutex is initially unlocked. */ MA_API ma_result ma_mutex_init(ma_mutex* pMutex); /* Deletes a mutex. */ MA_API void ma_mutex_uninit(ma_mutex* pMutex); /* Locks a mutex with an infinite timeout. */ MA_API void ma_mutex_lock(ma_mutex* pMutex); /* Unlocks a mutex. */ MA_API void ma_mutex_unlock(ma_mutex* pMutex); /* Initializes an auto-reset event. */ MA_API ma_result ma_event_init(ma_event* pEvent); /* Uninitializes an auto-reset event. */ MA_API void ma_event_uninit(ma_event* pEvent); /* Waits for the specified auto-reset event to become signalled. */ MA_API ma_result ma_event_wait(ma_event* pEvent); /* Signals the specified auto-reset event. */ MA_API ma_result ma_event_signal(ma_event* pEvent); #endif /* MA_NO_THREADING */ /* Fence ===== This locks while the counter is larger than 0. Counter can be incremented and decremented by any thread, but care needs to be taken when waiting. It is possible for one thread to acquire the fence just as another thread returns from ma_fence_wait(). The idea behind a fence is to allow you to wait for a group of operations to complete. When an operation starts, the counter is incremented which locks the fence. When the operation completes, the fence will be released which decrements the counter. ma_fence_wait() will block until the counter hits zero. If threading is disabled, ma_fence_wait() will spin on the counter. */ typedef struct { #ifndef MA_NO_THREADING ma_event e; #endif ma_uint32 counter; } ma_fence; MA_API ma_result ma_fence_init(ma_fence* pFence); MA_API void ma_fence_uninit(ma_fence* pFence); MA_API ma_result ma_fence_acquire(ma_fence* pFence); /* Increment counter. */ MA_API ma_result ma_fence_release(ma_fence* pFence); /* Decrement counter. */ MA_API ma_result ma_fence_wait(ma_fence* pFence); /* Wait for counter to reach 0. */ /* Notification callback for asynchronous operations. */ typedef void ma_async_notification; typedef struct { void (* onSignal)(ma_async_notification* pNotification); } ma_async_notification_callbacks; MA_API ma_result ma_async_notification_signal(ma_async_notification* pNotification); /* Simple polling notification. This just sets a variable when the notification has been signalled which is then polled with ma_async_notification_poll_is_signalled() */ typedef struct { ma_async_notification_callbacks cb; ma_bool32 signalled; } ma_async_notification_poll; MA_API ma_result ma_async_notification_poll_init(ma_async_notification_poll* pNotificationPoll); MA_API ma_bool32 ma_async_notification_poll_is_signalled(const ma_async_notification_poll* pNotificationPoll); /* Event Notification This uses an ma_event. If threading is disabled (MA_NO_THREADING), initialization will fail. */ typedef struct { ma_async_notification_callbacks cb; #ifndef MA_NO_THREADING ma_event e; #endif } ma_async_notification_event; MA_API ma_result ma_async_notification_event_init(ma_async_notification_event* pNotificationEvent); MA_API ma_result ma_async_notification_event_uninit(ma_async_notification_event* pNotificationEvent); MA_API ma_result ma_async_notification_event_wait(ma_async_notification_event* pNotificationEvent); MA_API ma_result ma_async_notification_event_signal(ma_async_notification_event* pNotificationEvent); /************************************************************************************************************************************************************ Job Queue ************************************************************************************************************************************************************/ /* Slot Allocator -------------- The idea of the slot allocator is for it to be used in conjunction with a fixed sized buffer. You use the slot allocator to allocator an index that can be used as the insertion point for an object. Slots are reference counted to help mitigate the ABA problem in the lock-free queue we use for tracking jobs. The slot index is stored in the low 32 bits. The reference counter is stored in the high 32 bits: +-----------------+-----------------+ | 32 Bits | 32 Bits | +-----------------+-----------------+ | Reference Count | Slot Index | +-----------------+-----------------+ */ typedef struct { ma_uint32 capacity; /* The number of slots to make available. */ } ma_slot_allocator_config; MA_API ma_slot_allocator_config ma_slot_allocator_config_init(ma_uint32 capacity); typedef struct { MA_ATOMIC(4, ma_uint32) bitfield; /* Must be used atomically because the allocation and freeing routines need to make copies of this which must never be optimized away by the compiler. */ } ma_slot_allocator_group; typedef struct { ma_slot_allocator_group* pGroups; /* Slots are grouped in chunks of 32. */ ma_uint32* pSlots; /* 32 bits for reference counting for ABA mitigation. */ ma_uint32 count; /* Allocation count. */ ma_uint32 capacity; /* Memory management. */ ma_bool32 _ownsHeap; void* _pHeap; } ma_slot_allocator; MA_API ma_result ma_slot_allocator_get_heap_size(const ma_slot_allocator_config* pConfig, size_t* pHeapSizeInBytes); MA_API ma_result ma_slot_allocator_init_preallocated(const ma_slot_allocator_config* pConfig, void* pHeap, ma_slot_allocator* pAllocator); MA_API ma_result ma_slot_allocator_init(const ma_slot_allocator_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_slot_allocator* pAllocator); MA_API void ma_slot_allocator_uninit(ma_slot_allocator* pAllocator, const ma_allocation_callbacks* pAllocationCallbacks); MA_API ma_result ma_slot_allocator_alloc(ma_slot_allocator* pAllocator, ma_uint64* pSlot); MA_API ma_result ma_slot_allocator_free(ma_slot_allocator* pAllocator, ma_uint64 slot); typedef struct ma_job ma_job; /* Callback for processing a job. Each job type will have their own processing callback which will be called by ma_job_process(). */ typedef ma_result (* ma_job_proc)(ma_job* pJob); /* When a job type is added here an callback needs to be added go "g_jobVTable" in the implementation section. */ typedef enum { /* Miscellaneous. */ MA_JOB_TYPE_QUIT = 0, MA_JOB_TYPE_CUSTOM, /* Resource Manager. */ MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_BUFFER_NODE, MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_BUFFER_NODE, MA_JOB_TYPE_RESOURCE_MANAGER_PAGE_DATA_BUFFER_NODE, MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_BUFFER, MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_BUFFER, MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_STREAM, MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_STREAM, MA_JOB_TYPE_RESOURCE_MANAGER_PAGE_DATA_STREAM, MA_JOB_TYPE_RESOURCE_MANAGER_SEEK_DATA_STREAM, /* Device. */ MA_JOB_TYPE_DEVICE_AAUDIO_REROUTE, /* Count. Must always be last. */ MA_JOB_TYPE_COUNT } ma_job_type; struct ma_job { union { struct { ma_uint16 code; /* Job type. */ ma_uint16 slot; /* Index into a ma_slot_allocator. */ ma_uint32 refcount; } breakup; ma_uint64 allocation; } toc; /* 8 bytes. We encode the job code into the slot allocation data to save space. */ MA_ATOMIC(8, ma_uint64) next; /* refcount + slot for the next item. Does not include the job code. */ ma_uint32 order; /* Execution order. Used to create a data dependency and ensure a job is executed in order. Usage is contextual depending on the job type. */ union { /* Miscellaneous. */ struct { ma_job_proc proc; ma_uintptr data0; ma_uintptr data1; } custom; /* Resource Manager */ union { struct { /*ma_resource_manager**/ void* pResourceManager; /*ma_resource_manager_data_buffer_node**/ void* pDataBufferNode; char* pFilePath; wchar_t* pFilePathW; ma_uint32 flags; /* Resource manager data source flags that were used when initializing the data buffer. */ ma_async_notification* pInitNotification; /* Signalled when the data buffer has been initialized and the format/channels/rate can be retrieved. */ ma_async_notification* pDoneNotification; /* Signalled when the data buffer has been fully decoded. Will be passed through to MA_JOB_TYPE_RESOURCE_MANAGER_PAGE_DATA_BUFFER_NODE when decoding. */ ma_fence* pInitFence; /* Released when initialization of the decoder is complete. */ ma_fence* pDoneFence; /* Released if initialization of the decoder fails. Passed through to PAGE_DATA_BUFFER_NODE untouched if init is successful. */ } loadDataBufferNode; struct { /*ma_resource_manager**/ void* pResourceManager; /*ma_resource_manager_data_buffer_node**/ void* pDataBufferNode; ma_async_notification* pDoneNotification; ma_fence* pDoneFence; } freeDataBufferNode; struct { /*ma_resource_manager**/ void* pResourceManager; /*ma_resource_manager_data_buffer_node**/ void* pDataBufferNode; /*ma_decoder**/ void* pDecoder; ma_async_notification* pDoneNotification; /* Signalled when the data buffer has been fully decoded. */ ma_fence* pDoneFence; /* Passed through from LOAD_DATA_BUFFER_NODE and released when the data buffer completes decoding or an error occurs. */ } pageDataBufferNode; struct { /*ma_resource_manager_data_buffer**/ void* pDataBuffer; ma_async_notification* pInitNotification; /* Signalled when the data buffer has been initialized and the format/channels/rate can be retrieved. */ ma_async_notification* pDoneNotification; /* Signalled when the data buffer has been fully decoded. */ ma_fence* pInitFence; /* Released when the data buffer has been initialized and the format/channels/rate can be retrieved. */ ma_fence* pDoneFence; /* Released when the data buffer has been fully decoded. */ ma_uint64 rangeBegInPCMFrames; ma_uint64 rangeEndInPCMFrames; ma_uint64 loopPointBegInPCMFrames; ma_uint64 loopPointEndInPCMFrames; ma_uint32 isLooping; } loadDataBuffer; struct { /*ma_resource_manager_data_buffer**/ void* pDataBuffer; ma_async_notification* pDoneNotification; ma_fence* pDoneFence; } freeDataBuffer; struct { /*ma_resource_manager_data_stream**/ void* pDataStream; char* pFilePath; /* Allocated when the job is posted, freed by the job thread after loading. */ wchar_t* pFilePathW; /* ^ As above ^. Only used if pFilePath is NULL. */ ma_uint64 initialSeekPoint; ma_async_notification* pInitNotification; /* Signalled after the first two pages have been decoded and frames can be read from the stream. */ ma_fence* pInitFence; } loadDataStream; struct { /*ma_resource_manager_data_stream**/ void* pDataStream; ma_async_notification* pDoneNotification; ma_fence* pDoneFence; } freeDataStream; struct { /*ma_resource_manager_data_stream**/ void* pDataStream; ma_uint32 pageIndex; /* The index of the page to decode into. */ } pageDataStream; struct { /*ma_resource_manager_data_stream**/ void* pDataStream; ma_uint64 frameIndex; } seekDataStream; } resourceManager; /* Device. */ union { union { struct { /*ma_device**/ void* pDevice; /*ma_device_type*/ ma_uint32 deviceType; } reroute; } aaudio; } device; } data; }; MA_API ma_job ma_job_init(ma_uint16 code); MA_API ma_result ma_job_process(ma_job* pJob); /* When set, ma_job_queue_next() will not wait and no semaphore will be signaled in ma_job_queue_post(). ma_job_queue_next() will return MA_NO_DATA_AVAILABLE if nothing is available. This flag should always be used for platforms that do not support multithreading. */ typedef enum { MA_JOB_QUEUE_FLAG_NON_BLOCKING = 0x00000001 } ma_job_queue_flags; typedef struct { ma_uint32 flags; ma_uint32 capacity; /* The maximum number of jobs that can fit in the queue at a time. */ } ma_job_queue_config; MA_API ma_job_queue_config ma_job_queue_config_init(ma_uint32 flags, ma_uint32 capacity); typedef struct { ma_uint32 flags; /* Flags passed in at initialization time. */ ma_uint32 capacity; /* The maximum number of jobs that can fit in the queue at a time. Set by the config. */ MA_ATOMIC(8, ma_uint64) head; /* The first item in the list. Required for removing from the top of the list. */ MA_ATOMIC(8, ma_uint64) tail; /* The last item in the list. Required for appending to the end of the list. */ #ifndef MA_NO_THREADING ma_semaphore sem; /* Only used when MA_JOB_QUEUE_FLAG_NON_BLOCKING is unset. */ #endif ma_slot_allocator allocator; ma_job* pJobs; #ifndef MA_USE_EXPERIMENTAL_LOCK_FREE_JOB_QUEUE ma_spinlock lock; #endif /* Memory management. */ void* _pHeap; ma_bool32 _ownsHeap; } ma_job_queue; MA_API ma_result ma_job_queue_get_heap_size(const ma_job_queue_config* pConfig, size_t* pHeapSizeInBytes); MA_API ma_result ma_job_queue_init_preallocated(const ma_job_queue_config* pConfig, void* pHeap, ma_job_queue* pQueue); MA_API ma_result ma_job_queue_init(const ma_job_queue_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_job_queue* pQueue); MA_API void ma_job_queue_uninit(ma_job_queue* pQueue, const ma_allocation_callbacks* pAllocationCallbacks); MA_API ma_result ma_job_queue_post(ma_job_queue* pQueue, const ma_job* pJob); MA_API ma_result ma_job_queue_next(ma_job_queue* pQueue, ma_job* pJob); /* Returns MA_CANCELLED if the next job is a quit job. */ /************************************************************************************************************************************************************ ************************************************************************************************************************************************************* DEVICE I/O ========== This section contains the APIs for device playback and capture. Here is where you'll find ma_device_init(), etc. ************************************************************************************************************************************************************* ************************************************************************************************************************************************************/ #ifndef MA_NO_DEVICE_IO /* Some backends are only supported on certain platforms. */ #if defined(MA_WIN32) #define MA_SUPPORT_WASAPI #if defined(MA_WIN32_DESKTOP) /* DirectSound and WinMM backends are only supported on desktops. */ #define MA_SUPPORT_DSOUND #define MA_SUPPORT_WINMM #define MA_SUPPORT_JACK /* JACK is technically supported on Windows, but I don't know how many people use it in practice... */ #endif #endif #if defined(MA_UNIX) #if defined(MA_LINUX) #if !defined(MA_ANDROID) /* ALSA is not supported on Android. */ #define MA_SUPPORT_ALSA #endif #endif #if !defined(MA_BSD) && !defined(MA_ANDROID) && !defined(MA_EMSCRIPTEN) #define MA_SUPPORT_PULSEAUDIO #define MA_SUPPORT_JACK #endif #if defined(__OpenBSD__) /* <-- Change this to "#if defined(MA_BSD)" to enable sndio on all BSD flavors. */ #define MA_SUPPORT_SNDIO /* sndio is only supported on OpenBSD for now. May be expanded later if there's demand. */ #endif #if defined(__NetBSD__) || defined(__OpenBSD__) #define MA_SUPPORT_AUDIO4 /* Only support audio(4) on platforms with known support. */ #endif #if defined(__FreeBSD__) || defined(__DragonFly__) #define MA_SUPPORT_OSS /* Only support OSS on specific platforms with known support. */ #endif #endif #if defined(MA_ANDROID) #define MA_SUPPORT_AAUDIO #define MA_SUPPORT_OPENSL #endif #if defined(MA_APPLE) #define MA_SUPPORT_COREAUDIO #endif #if defined(MA_EMSCRIPTEN) #define MA_SUPPORT_WEBAUDIO #endif /* All platforms should support custom backends. */ #define MA_SUPPORT_CUSTOM /* Explicitly disable the Null backend for Emscripten because it uses a background thread which is not properly supported right now. */ #if !defined(MA_EMSCRIPTEN) #define MA_SUPPORT_NULL #endif #if defined(MA_SUPPORT_WASAPI) && !defined(MA_NO_WASAPI) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_WASAPI)) #define MA_HAS_WASAPI #endif #if defined(MA_SUPPORT_DSOUND) && !defined(MA_NO_DSOUND) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_DSOUND)) #define MA_HAS_DSOUND #endif #if defined(MA_SUPPORT_WINMM) && !defined(MA_NO_WINMM) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_WINMM)) #define MA_HAS_WINMM #endif #if defined(MA_SUPPORT_ALSA) && !defined(MA_NO_ALSA) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_ALSA)) #define MA_HAS_ALSA #endif #if defined(MA_SUPPORT_PULSEAUDIO) && !defined(MA_NO_PULSEAUDIO) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_PULSEAUDIO)) #define MA_HAS_PULSEAUDIO #endif #if defined(MA_SUPPORT_JACK) && !defined(MA_NO_JACK) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_JACK)) #define MA_HAS_JACK #endif #if defined(MA_SUPPORT_COREAUDIO) && !defined(MA_NO_COREAUDIO) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_COREAUDIO)) #define MA_HAS_COREAUDIO #endif #if defined(MA_SUPPORT_SNDIO) && !defined(MA_NO_SNDIO) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_SNDIO)) #define MA_HAS_SNDIO #endif #if defined(MA_SUPPORT_AUDIO4) && !defined(MA_NO_AUDIO4) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_AUDIO4)) #define MA_HAS_AUDIO4 #endif #if defined(MA_SUPPORT_OSS) && !defined(MA_NO_OSS) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_OSS)) #define MA_HAS_OSS #endif #if defined(MA_SUPPORT_AAUDIO) && !defined(MA_NO_AAUDIO) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_AAUDIO)) #define MA_HAS_AAUDIO #endif #if defined(MA_SUPPORT_OPENSL) && !defined(MA_NO_OPENSL) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_OPENSL)) #define MA_HAS_OPENSL #endif #if defined(MA_SUPPORT_WEBAUDIO) && !defined(MA_NO_WEBAUDIO) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_WEBAUDIO)) #define MA_HAS_WEBAUDIO #endif #if defined(MA_SUPPORT_CUSTOM) && !defined(MA_NO_CUSTOM) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_CUSTOM)) #define MA_HAS_CUSTOM #endif #if defined(MA_SUPPORT_NULL) && !defined(MA_NO_NULL) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_NULL)) #define MA_HAS_NULL #endif typedef enum { ma_device_state_uninitialized = 0, ma_device_state_stopped = 1, /* The device's default state after initialization. */ ma_device_state_started = 2, /* The device is started and is requesting and/or delivering audio data. */ ma_device_state_starting = 3, /* Transitioning from a stopped state to started. */ ma_device_state_stopping = 4 /* Transitioning from a started state to stopped. */ } ma_device_state; #ifdef MA_SUPPORT_WASAPI /* We need a IMMNotificationClient object for WASAPI. */ typedef struct { void* lpVtbl; ma_uint32 counter; ma_device* pDevice; } ma_IMMNotificationClient; #endif /* Backend enums must be in priority order. */ typedef enum { ma_backend_wasapi, ma_backend_dsound, ma_backend_winmm, ma_backend_coreaudio, ma_backend_sndio, ma_backend_audio4, ma_backend_oss, ma_backend_pulseaudio, ma_backend_alsa, ma_backend_jack, ma_backend_aaudio, ma_backend_opensl, ma_backend_webaudio, ma_backend_custom, /* <-- Custom backend, with callbacks defined by the context config. */ ma_backend_null /* <-- Must always be the last item. Lowest priority, and used as the terminator for backend enumeration. */ } ma_backend; #define MA_BACKEND_COUNT (ma_backend_null+1) /* Device job thread. This is used by backends that require asynchronous processing of certain operations. It is not used by all backends. The device job thread is made up of a thread and a job queue. You can post a job to the thread with ma_device_job_thread_post(). The thread will do the processing of the job. */ typedef struct { ma_bool32 noThread; /* Set this to true if you want to process jobs yourself. */ ma_uint32 jobQueueCapacity; ma_uint32 jobQueueFlags; } ma_device_job_thread_config; MA_API ma_device_job_thread_config ma_device_job_thread_config_init(void); typedef struct { ma_thread thread; ma_job_queue jobQueue; ma_bool32 _hasThread; } ma_device_job_thread; MA_API ma_result ma_device_job_thread_init(const ma_device_job_thread_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_device_job_thread* pJobThread); MA_API void ma_device_job_thread_uninit(ma_device_job_thread* pJobThread, const ma_allocation_callbacks* pAllocationCallbacks); MA_API ma_result ma_device_job_thread_post(ma_device_job_thread* pJobThread, const ma_job* pJob); MA_API ma_result ma_device_job_thread_next(ma_device_job_thread* pJobThread, ma_job* pJob); /* Device notification types. */ typedef enum { ma_device_notification_type_started, ma_device_notification_type_stopped, ma_device_notification_type_rerouted, ma_device_notification_type_interruption_began, ma_device_notification_type_interruption_ended } ma_device_notification_type; typedef struct { ma_device* pDevice; ma_device_notification_type type; union { struct { int _unused; } started; struct { int _unused; } stopped; struct { int _unused; } rerouted; struct { int _unused; } interruption; } data; } ma_device_notification; /* The notification callback for when the application should be notified of a change to the device. This callback is used for notifying the application of changes such as when the device has started, stopped, rerouted or an interruption has occurred. Note that not all backends will post all notification types. For example, some backends will perform automatic stream routing without any kind of notification to the host program which means miniaudio will never know about it and will never be able to fire the rerouted notification. You should keep this in mind when designing your program. The stopped notification will *not* get fired when a device is rerouted. Parameters ---------- pNotification (in) A pointer to a structure containing information about the event. Use the `pDevice` member of this object to retrieve the relevant device. The `type` member can be used to discriminate against each of the notification types. Remarks ------- Do not restart or uninitialize the device from the callback. Not all notifications will be triggered by all backends, however the started and stopped events should be reliable for all backends. Some backends do not have a good way to detect device stoppages due to unplugging the device which may result in the stopped callback not getting fired. This has been observed with at least one BSD variant. The rerouted notification is fired *after* the reroute has occurred. The stopped notification will *not* get fired when a device is rerouted. The following backends are known to do automatic stream rerouting, but do not have a way to be notified of the change: * DirectSound The interruption notifications are used on mobile platforms for detecting when audio is interrupted due to things like an incoming phone call. Currently this is only implemented on iOS. None of the Android backends will report this notification. */ typedef void (* ma_device_notification_proc)(const ma_device_notification* pNotification); /* The callback for processing audio data from the device. The data callback is fired by miniaudio whenever the device needs to have more data delivered to a playback device, or when a capture device has some data available. This is called as soon as the backend asks for more data which means it may be called with inconsistent frame counts. You cannot assume the callback will be fired with a consistent frame count. Parameters ---------- pDevice (in) A pointer to the relevant device. pOutput (out) A pointer to the output buffer that will receive audio data that will later be played back through the speakers. This will be non-null for a playback or full-duplex device and null for a capture and loopback device. pInput (in) A pointer to the buffer containing input data from a recording device. This will be non-null for a capture, full-duplex or loopback device and null for a playback device. frameCount (in) The number of PCM frames to process. Note that this will not necessarily be equal to what you requested when you initialized the device. The `periodSizeInFrames` and `periodSizeInMilliseconds` members of the device config are just hints, and are not necessarily exactly what you'll get. You must not assume this will always be the same value each time the callback is fired. Remarks ------- You cannot stop and start the device from inside the callback or else you'll get a deadlock. You must also not uninitialize the device from inside the callback. The following APIs cannot be called from inside the callback: ma_device_init() ma_device_init_ex() ma_device_uninit() ma_device_start() ma_device_stop() The proper way to stop the device is to call `ma_device_stop()` from a different thread, normally the main application thread. */ typedef void (* ma_device_data_proc)(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount); /* DEPRECATED. Use ma_device_notification_proc instead. The callback for when the device has been stopped. This will be called when the device is stopped explicitly with `ma_device_stop()` and also called implicitly when the device is stopped through external forces such as being unplugged or an internal error occuring. Parameters ---------- pDevice (in) A pointer to the device that has just stopped. Remarks ------- Do not restart or uninitialize the device from the callback. */ typedef void (* ma_stop_proc)(ma_device* pDevice); /* DEPRECATED. Use ma_device_notification_proc instead. */ typedef enum { ma_device_type_playback = 1, ma_device_type_capture = 2, ma_device_type_duplex = ma_device_type_playback | ma_device_type_capture, /* 3 */ ma_device_type_loopback = 4 } ma_device_type; typedef enum { ma_share_mode_shared = 0, ma_share_mode_exclusive } ma_share_mode; /* iOS/tvOS/watchOS session categories. */ typedef enum { ma_ios_session_category_default = 0, /* AVAudioSessionCategoryPlayAndRecord. */ ma_ios_session_category_none, /* Leave the session category unchanged. */ ma_ios_session_category_ambient, /* AVAudioSessionCategoryAmbient */ ma_ios_session_category_solo_ambient, /* AVAudioSessionCategorySoloAmbient */ ma_ios_session_category_playback, /* AVAudioSessionCategoryPlayback */ ma_ios_session_category_record, /* AVAudioSessionCategoryRecord */ ma_ios_session_category_play_and_record, /* AVAudioSessionCategoryPlayAndRecord */ ma_ios_session_category_multi_route /* AVAudioSessionCategoryMultiRoute */ } ma_ios_session_category; /* iOS/tvOS/watchOS session category options */ typedef enum { ma_ios_session_category_option_mix_with_others = 0x01, /* AVAudioSessionCategoryOptionMixWithOthers */ ma_ios_session_category_option_duck_others = 0x02, /* AVAudioSessionCategoryOptionDuckOthers */ ma_ios_session_category_option_allow_bluetooth = 0x04, /* AVAudioSessionCategoryOptionAllowBluetooth */ ma_ios_session_category_option_default_to_speaker = 0x08, /* AVAudioSessionCategoryOptionDefaultToSpeaker */ ma_ios_session_category_option_interrupt_spoken_audio_and_mix_with_others = 0x11, /* AVAudioSessionCategoryOptionInterruptSpokenAudioAndMixWithOthers */ ma_ios_session_category_option_allow_bluetooth_a2dp = 0x20, /* AVAudioSessionCategoryOptionAllowBluetoothA2DP */ ma_ios_session_category_option_allow_air_play = 0x40, /* AVAudioSessionCategoryOptionAllowAirPlay */ } ma_ios_session_category_option; /* OpenSL stream types. */ typedef enum { ma_opensl_stream_type_default = 0, /* Leaves the stream type unset. */ ma_opensl_stream_type_voice, /* SL_ANDROID_STREAM_VOICE */ ma_opensl_stream_type_system, /* SL_ANDROID_STREAM_SYSTEM */ ma_opensl_stream_type_ring, /* SL_ANDROID_STREAM_RING */ ma_opensl_stream_type_media, /* SL_ANDROID_STREAM_MEDIA */ ma_opensl_stream_type_alarm, /* SL_ANDROID_STREAM_ALARM */ ma_opensl_stream_type_notification /* SL_ANDROID_STREAM_NOTIFICATION */ } ma_opensl_stream_type; /* OpenSL recording presets. */ typedef enum { ma_opensl_recording_preset_default = 0, /* Leaves the input preset unset. */ ma_opensl_recording_preset_generic, /* SL_ANDROID_RECORDING_PRESET_GENERIC */ ma_opensl_recording_preset_camcorder, /* SL_ANDROID_RECORDING_PRESET_CAMCORDER */ ma_opensl_recording_preset_voice_recognition, /* SL_ANDROID_RECORDING_PRESET_VOICE_RECOGNITION */ ma_opensl_recording_preset_voice_communication, /* SL_ANDROID_RECORDING_PRESET_VOICE_COMMUNICATION */ ma_opensl_recording_preset_voice_unprocessed /* SL_ANDROID_RECORDING_PRESET_UNPROCESSED */ } ma_opensl_recording_preset; /* WASAPI audio thread priority characteristics. */ typedef enum { ma_wasapi_usage_default = 0, ma_wasapi_usage_games, ma_wasapi_usage_pro_audio, } ma_wasapi_usage; /* AAudio usage types. */ typedef enum { ma_aaudio_usage_default = 0, /* Leaves the usage type unset. */ ma_aaudio_usage_media, /* AAUDIO_USAGE_MEDIA */ ma_aaudio_usage_voice_communication, /* AAUDIO_USAGE_VOICE_COMMUNICATION */ ma_aaudio_usage_voice_communication_signalling, /* AAUDIO_USAGE_VOICE_COMMUNICATION_SIGNALLING */ ma_aaudio_usage_alarm, /* AAUDIO_USAGE_ALARM */ ma_aaudio_usage_notification, /* AAUDIO_USAGE_NOTIFICATION */ ma_aaudio_usage_notification_ringtone, /* AAUDIO_USAGE_NOTIFICATION_RINGTONE */ ma_aaudio_usage_notification_event, /* AAUDIO_USAGE_NOTIFICATION_EVENT */ ma_aaudio_usage_assistance_accessibility, /* AAUDIO_USAGE_ASSISTANCE_ACCESSIBILITY */ ma_aaudio_usage_assistance_navigation_guidance, /* AAUDIO_USAGE_ASSISTANCE_NAVIGATION_GUIDANCE */ ma_aaudio_usage_assistance_sonification, /* AAUDIO_USAGE_ASSISTANCE_SONIFICATION */ ma_aaudio_usage_game, /* AAUDIO_USAGE_GAME */ ma_aaudio_usage_assitant, /* AAUDIO_USAGE_ASSISTANT */ ma_aaudio_usage_emergency, /* AAUDIO_SYSTEM_USAGE_EMERGENCY */ ma_aaudio_usage_safety, /* AAUDIO_SYSTEM_USAGE_SAFETY */ ma_aaudio_usage_vehicle_status, /* AAUDIO_SYSTEM_USAGE_VEHICLE_STATUS */ ma_aaudio_usage_announcement /* AAUDIO_SYSTEM_USAGE_ANNOUNCEMENT */ } ma_aaudio_usage; /* AAudio content types. */ typedef enum { ma_aaudio_content_type_default = 0, /* Leaves the content type unset. */ ma_aaudio_content_type_speech, /* AAUDIO_CONTENT_TYPE_SPEECH */ ma_aaudio_content_type_music, /* AAUDIO_CONTENT_TYPE_MUSIC */ ma_aaudio_content_type_movie, /* AAUDIO_CONTENT_TYPE_MOVIE */ ma_aaudio_content_type_sonification /* AAUDIO_CONTENT_TYPE_SONIFICATION */ } ma_aaudio_content_type; /* AAudio input presets. */ typedef enum { ma_aaudio_input_preset_default = 0, /* Leaves the input preset unset. */ ma_aaudio_input_preset_generic, /* AAUDIO_INPUT_PRESET_GENERIC */ ma_aaudio_input_preset_camcorder, /* AAUDIO_INPUT_PRESET_CAMCORDER */ ma_aaudio_input_preset_voice_recognition, /* AAUDIO_INPUT_PRESET_VOICE_RECOGNITION */ ma_aaudio_input_preset_voice_communication, /* AAUDIO_INPUT_PRESET_VOICE_COMMUNICATION */ ma_aaudio_input_preset_unprocessed, /* AAUDIO_INPUT_PRESET_UNPROCESSED */ ma_aaudio_input_preset_voice_performance /* AAUDIO_INPUT_PRESET_VOICE_PERFORMANCE */ } ma_aaudio_input_preset; typedef union { ma_int64 counter; double counterD; } ma_timer; typedef union { wchar_t wasapi[64]; /* WASAPI uses a wchar_t string for identification. */ ma_uint8 dsound[16]; /* DirectSound uses a GUID for identification. */ /*UINT_PTR*/ ma_uint32 winmm; /* When creating a device, WinMM expects a Win32 UINT_PTR for device identification. In practice it's actually just a UINT. */ char alsa[256]; /* ALSA uses a name string for identification. */ char pulse[256]; /* PulseAudio uses a name string for identification. */ int jack; /* JACK always uses default devices. */ char coreaudio[256]; /* Core Audio uses a string for identification. */ char sndio[256]; /* "snd/0", etc. */ char audio4[256]; /* "/dev/audio", etc. */ char oss[64]; /* "dev/dsp0", etc. "dev/dsp" for the default device. */ ma_int32 aaudio; /* AAudio uses a 32-bit integer for identification. */ ma_uint32 opensl; /* OpenSL|ES uses a 32-bit unsigned integer for identification. */ char webaudio[32]; /* Web Audio always uses default devices for now, but if this changes it'll be a GUID. */ union { int i; char s[256]; void* p; } custom; /* The custom backend could be anything. Give them a few options. */ int nullbackend; /* The null backend uses an integer for device IDs. */ } ma_device_id; typedef struct ma_context_config ma_context_config; typedef struct ma_device_config ma_device_config; typedef struct ma_backend_callbacks ma_backend_callbacks; #define MA_DATA_FORMAT_FLAG_EXCLUSIVE_MODE (1U << 1) /* If set, this is supported in exclusive mode. Otherwise not natively supported by exclusive mode. */ #ifndef MA_MAX_DEVICE_NAME_LENGTH #define MA_MAX_DEVICE_NAME_LENGTH 255 #endif typedef struct { /* Basic info. This is the only information guaranteed to be filled in during device enumeration. */ ma_device_id id; char name[MA_MAX_DEVICE_NAME_LENGTH + 1]; /* +1 for null terminator. */ ma_bool32 isDefault; ma_uint32 nativeDataFormatCount; struct { ma_format format; /* Sample format. If set to ma_format_unknown, all sample formats are supported. */ ma_uint32 channels; /* If set to 0, all channels are supported. */ ma_uint32 sampleRate; /* If set to 0, all sample rates are supported. */ ma_uint32 flags; /* A combination of MA_DATA_FORMAT_FLAG_* flags. */ } nativeDataFormats[/*ma_format_count * ma_standard_sample_rate_count * MA_MAX_CHANNELS*/ 64]; /* Not sure how big to make this. There can be *many* permutations for virtual devices which can support anything. */ } ma_device_info; struct ma_device_config { ma_device_type deviceType; ma_uint32 sampleRate; ma_uint32 periodSizeInFrames; ma_uint32 periodSizeInMilliseconds; ma_uint32 periods; ma_performance_profile performanceProfile; ma_bool8 noPreSilencedOutputBuffer; /* When set to true, the contents of the output buffer passed into the data callback will be left undefined rather than initialized to silence. */ ma_bool8 noClip; /* When set to true, the contents of the output buffer passed into the data callback will be clipped after returning. Only applies when the playback sample format is f32. */ ma_bool8 noDisableDenormals; /* Do not disable denormals when firing the data callback. */ ma_bool8 noFixedSizedCallback; /* Disables strict fixed-sized data callbacks. Setting this to true will result in the period size being treated only as a hint to the backend. This is an optimization for those who don't need fixed sized callbacks. */ ma_device_data_proc dataCallback; ma_device_notification_proc notificationCallback; ma_stop_proc stopCallback; void* pUserData; ma_resampler_config resampling; struct { const ma_device_id* pDeviceID; ma_format format; ma_uint32 channels; ma_channel* pChannelMap; ma_channel_mix_mode channelMixMode; ma_bool32 calculateLFEFromSpatialChannels; /* When an output LFE channel is present, but no input LFE, set to true to set the output LFE to the average of all spatial channels (LR, FR, etc.). Ignored when an input LFE is present. */ ma_share_mode shareMode; } playback; struct { const ma_device_id* pDeviceID; ma_format format; ma_uint32 channels; ma_channel* pChannelMap; ma_channel_mix_mode channelMixMode; ma_bool32 calculateLFEFromSpatialChannels; /* When an output LFE channel is present, but no input LFE, set to true to set the output LFE to the average of all spatial channels (LR, FR, etc.). Ignored when an input LFE is present. */ ma_share_mode shareMode; } capture; struct { ma_wasapi_usage usage; /* When configured, uses Avrt APIs to set the thread characteristics. */ ma_bool8 noAutoConvertSRC; /* When set to true, disables the use of AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM. */ ma_bool8 noDefaultQualitySRC; /* When set to true, disables the use of AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY. */ ma_bool8 noAutoStreamRouting; /* Disables automatic stream routing. */ ma_bool8 noHardwareOffloading; /* Disables WASAPI's hardware offloading feature. */ ma_uint32 loopbackProcessID; /* The process ID to include or exclude for loopback mode. Set to 0 to capture audio from all processes. Ignored when an explicit device ID is specified. */ ma_bool8 loopbackProcessExclude; /* When set to true, excludes the process specified by loopbackProcessID. By default, the process will be included. */ } wasapi; struct { ma_bool32 noMMap; /* Disables MMap mode. */ ma_bool32 noAutoFormat; /* Opens the ALSA device with SND_PCM_NO_AUTO_FORMAT. */ ma_bool32 noAutoChannels; /* Opens the ALSA device with SND_PCM_NO_AUTO_CHANNELS. */ ma_bool32 noAutoResample; /* Opens the ALSA device with SND_PCM_NO_AUTO_RESAMPLE. */ } alsa; struct { const char* pStreamNamePlayback; const char* pStreamNameCapture; } pulse; struct { ma_bool32 allowNominalSampleRateChange; /* Desktop only. When enabled, allows changing of the sample rate at the operating system level. */ } coreaudio; struct { ma_opensl_stream_type streamType; ma_opensl_recording_preset recordingPreset; } opensl; struct { ma_aaudio_usage usage; ma_aaudio_content_type contentType; ma_aaudio_input_preset inputPreset; ma_bool32 noAutoStartAfterReroute; } aaudio; }; /* The callback for handling device enumeration. This is fired from `ma_context_enumerated_devices()`. Parameters ---------- pContext (in) A pointer to the context performing the enumeration. deviceType (in) The type of the device being enumerated. This will always be either `ma_device_type_playback` or `ma_device_type_capture`. pInfo (in) A pointer to a `ma_device_info` containing the ID and name of the enumerated device. Note that this will not include detailed information about the device, only basic information (ID and name). The reason for this is that it would otherwise require opening the backend device to probe for the information which is too inefficient. pUserData (in) The user data pointer passed into `ma_context_enumerate_devices()`. */ typedef ma_bool32 (* ma_enum_devices_callback_proc)(ma_context* pContext, ma_device_type deviceType, const ma_device_info* pInfo, void* pUserData); /* Describes some basic details about a playback or capture device. */ typedef struct { const ma_device_id* pDeviceID; ma_share_mode shareMode; ma_format format; ma_uint32 channels; ma_uint32 sampleRate; ma_channel channelMap[MA_MAX_CHANNELS]; ma_uint32 periodSizeInFrames; ma_uint32 periodSizeInMilliseconds; ma_uint32 periodCount; } ma_device_descriptor; /* These are the callbacks required to be implemented for a backend. These callbacks are grouped into two parts: context and device. There is one context to many devices. A device is created from a context. The general flow goes like this: 1) A context is created with `onContextInit()` 1a) Available devices can be enumerated with `onContextEnumerateDevices()` if required. 1b) Detailed information about a device can be queried with `onContextGetDeviceInfo()` if required. 2) A device is created from the context that was created in the first step using `onDeviceInit()`, and optionally a device ID that was selected from device enumeration via `onContextEnumerateDevices()`. 3) A device is started or stopped with `onDeviceStart()` / `onDeviceStop()` 4) Data is delivered to and from the device by the backend. This is always done based on the native format returned by the prior call to `onDeviceInit()`. Conversion between the device's native format and the format requested by the application will be handled by miniaudio internally. Initialization of the context is quite simple. You need to do any necessary initialization of internal objects and then output the callbacks defined in this structure. Once the context has been initialized you can initialize a device. Before doing so, however, the application may want to know which physical devices are available. This is where `onContextEnumerateDevices()` comes in. This is fairly simple. For each device, fire the given callback with, at a minimum, the basic information filled out in `ma_device_info`. When the callback returns `MA_FALSE`, enumeration needs to stop and the `onContextEnumerateDevices()` function returns with a success code. Detailed device information can be retrieved from a device ID using `onContextGetDeviceInfo()`. This takes as input the device type and ID, and on output returns detailed information about the device in `ma_device_info`. The `onContextGetDeviceInfo()` callback must handle the case when the device ID is NULL, in which case information about the default device needs to be retrieved. Once the context has been created and the device ID retrieved (if using anything other than the default device), the device can be created. This is a little bit more complicated than initialization of the context due to it's more complicated configuration. When initializing a device, a duplex device may be requested. This means a separate data format needs to be specified for both playback and capture. On input, the data format is set to what the application wants. On output it's set to the native format which should match as closely as possible to the requested format. The conversion between the format requested by the application and the device's native format will be handled internally by miniaudio. On input, if the sample format is set to `ma_format_unknown`, the backend is free to use whatever sample format it desires, so long as it's supported by miniaudio. When the channel count is set to 0, the backend should use the device's native channel count. The same applies for sample rate. For the channel map, the default should be used when `ma_channel_map_is_blank()` returns true (all channels set to `MA_CHANNEL_NONE`). On input, the `periodSizeInFrames` or `periodSizeInMilliseconds` option should always be set. The backend should inspect both of these variables. If `periodSizeInFrames` is set, it should take priority, otherwise it needs to be derived from the period size in milliseconds (`periodSizeInMilliseconds`) and the sample rate, keeping in mind that the sample rate may be 0, in which case the sample rate will need to be determined before calculating the period size in frames. On output, all members of the `ma_device_descriptor` object should be set to a valid value, except for `periodSizeInMilliseconds` which is optional (`periodSizeInFrames` *must* be set). Starting and stopping of the device is done with `onDeviceStart()` and `onDeviceStop()` and should be self-explanatory. If the backend uses asynchronous reading and writing, `onDeviceStart()` and `onDeviceStop()` should always be implemented. The handling of data delivery between the application and the device is the most complicated part of the process. To make this a bit easier, some helper callbacks are available. If the backend uses a blocking read/write style of API, the `onDeviceRead()` and `onDeviceWrite()` callbacks can optionally be implemented. These are blocking and work just like reading and writing from a file. If the backend uses a callback for data delivery, that callback must call `ma_device_handle_backend_data_callback()` from within it's callback. This allows miniaudio to then process any necessary data conversion and then pass it to the miniaudio data callback. If the backend requires absolute flexibility with it's data delivery, it can optionally implement the `onDeviceDataLoop()` callback which will allow it to implement the logic that will run on the audio thread. This is much more advanced and is completely optional. The audio thread should run data delivery logic in a loop while `ma_device_get_state() == ma_device_state_started` and no errors have been encounted. Do not start or stop the device here. That will be handled from outside the `onDeviceDataLoop()` callback. The invocation of the `onDeviceDataLoop()` callback will be handled by miniaudio. When you start the device, miniaudio will fire this callback. When the device is stopped, the `ma_device_get_state() == ma_device_state_started` condition will fail and the loop will be terminated which will then fall through to the part that stops the device. For an example on how to implement the `onDeviceDataLoop()` callback, look at `ma_device_audio_thread__default_read_write()`. Implement the `onDeviceDataLoopWakeup()` callback if you need a mechanism to wake up the audio thread. If the backend supports an optimized retrieval of device information from an initialized `ma_device` object, it should implement the `onDeviceGetInfo()` callback. This is optional, in which case it will fall back to `onContextGetDeviceInfo()` which is less efficient. */ struct ma_backend_callbacks { ma_result (* onContextInit)(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks); ma_result (* onContextUninit)(ma_context* pContext); ma_result (* onContextEnumerateDevices)(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData); ma_result (* onContextGetDeviceInfo)(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo); ma_result (* onDeviceInit)(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture); ma_result (* onDeviceUninit)(ma_device* pDevice); ma_result (* onDeviceStart)(ma_device* pDevice); ma_result (* onDeviceStop)(ma_device* pDevice); ma_result (* onDeviceRead)(ma_device* pDevice, void* pFrames, ma_uint32 frameCount, ma_uint32* pFramesRead); ma_result (* onDeviceWrite)(ma_device* pDevice, const void* pFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten); ma_result (* onDeviceDataLoop)(ma_device* pDevice); ma_result (* onDeviceDataLoopWakeup)(ma_device* pDevice); ma_result (* onDeviceGetInfo)(ma_device* pDevice, ma_device_type type, ma_device_info* pDeviceInfo); }; struct ma_context_config { ma_log* pLog; ma_thread_priority threadPriority; size_t threadStackSize; void* pUserData; ma_allocation_callbacks allocationCallbacks; struct { ma_bool32 useVerboseDeviceEnumeration; } alsa; struct { const char* pApplicationName; const char* pServerName; ma_bool32 tryAutoSpawn; /* Enables autospawning of the PulseAudio daemon if necessary. */ } pulse; struct { ma_ios_session_category sessionCategory; ma_uint32 sessionCategoryOptions; ma_bool32 noAudioSessionActivate; /* iOS only. When set to true, does not perform an explicit [[AVAudioSession sharedInstace] setActive:true] on initialization. */ ma_bool32 noAudioSessionDeactivate; /* iOS only. When set to true, does not perform an explicit [[AVAudioSession sharedInstace] setActive:false] on uninitialization. */ } coreaudio; struct { const char* pClientName; ma_bool32 tryStartServer; } jack; ma_backend_callbacks custom; }; /* WASAPI specific structure for some commands which must run on a common thread due to bugs in WASAPI. */ typedef struct { int code; ma_event* pEvent; /* This will be signalled when the event is complete. */ union { struct { int _unused; } quit; struct { ma_device_type deviceType; void* pAudioClient; void** ppAudioClientService; ma_result* pResult; /* The result from creating the audio client service. */ } createAudioClient; struct { ma_device* pDevice; ma_device_type deviceType; } releaseAudioClient; } data; } ma_context_command__wasapi; struct ma_context { ma_backend_callbacks callbacks; ma_backend backend; /* DirectSound, ALSA, etc. */ ma_log* pLog; ma_log log; /* Only used if the log is owned by the context. The pLog member will be set to &log in this case. */ ma_thread_priority threadPriority; size_t threadStackSize; void* pUserData; ma_allocation_callbacks allocationCallbacks; ma_mutex deviceEnumLock; /* Used to make ma_context_get_devices() thread safe. */ ma_mutex deviceInfoLock; /* Used to make ma_context_get_device_info() thread safe. */ ma_uint32 deviceInfoCapacity; /* Total capacity of pDeviceInfos. */ ma_uint32 playbackDeviceInfoCount; ma_uint32 captureDeviceInfoCount; ma_device_info* pDeviceInfos; /* Playback devices first, then capture. */ union { #ifdef MA_SUPPORT_WASAPI struct { ma_thread commandThread; ma_mutex commandLock; ma_semaphore commandSem; ma_uint32 commandIndex; ma_uint32 commandCount; ma_context_command__wasapi commands[4]; ma_handle hAvrt; ma_proc AvSetMmThreadCharacteristicsW; ma_proc AvRevertMmThreadcharacteristics; ma_handle hMMDevapi; ma_proc ActivateAudioInterfaceAsync; } wasapi; #endif #ifdef MA_SUPPORT_DSOUND struct { ma_handle hDSoundDLL; ma_proc DirectSoundCreate; ma_proc DirectSoundEnumerateA; ma_proc DirectSoundCaptureCreate; ma_proc DirectSoundCaptureEnumerateA; } dsound; #endif #ifdef MA_SUPPORT_WINMM struct { ma_handle hWinMM; ma_proc waveOutGetNumDevs; ma_proc waveOutGetDevCapsA; ma_proc waveOutOpen; ma_proc waveOutClose; ma_proc waveOutPrepareHeader; ma_proc waveOutUnprepareHeader; ma_proc waveOutWrite; ma_proc waveOutReset; ma_proc waveInGetNumDevs; ma_proc waveInGetDevCapsA; ma_proc waveInOpen; ma_proc waveInClose; ma_proc waveInPrepareHeader; ma_proc waveInUnprepareHeader; ma_proc waveInAddBuffer; ma_proc waveInStart; ma_proc waveInReset; } winmm; #endif #ifdef MA_SUPPORT_ALSA struct { ma_handle asoundSO; ma_proc snd_pcm_open; ma_proc snd_pcm_close; ma_proc snd_pcm_hw_params_sizeof; ma_proc snd_pcm_hw_params_any; ma_proc snd_pcm_hw_params_set_format; ma_proc snd_pcm_hw_params_set_format_first; ma_proc snd_pcm_hw_params_get_format_mask; ma_proc snd_pcm_hw_params_set_channels; ma_proc snd_pcm_hw_params_set_channels_near; ma_proc snd_pcm_hw_params_set_channels_minmax; ma_proc snd_pcm_hw_params_set_rate_resample; ma_proc snd_pcm_hw_params_set_rate; ma_proc snd_pcm_hw_params_set_rate_near; ma_proc snd_pcm_hw_params_set_buffer_size_near; ma_proc snd_pcm_hw_params_set_periods_near; ma_proc snd_pcm_hw_params_set_access; ma_proc snd_pcm_hw_params_get_format; ma_proc snd_pcm_hw_params_get_channels; ma_proc snd_pcm_hw_params_get_channels_min; ma_proc snd_pcm_hw_params_get_channels_max; ma_proc snd_pcm_hw_params_get_rate; ma_proc snd_pcm_hw_params_get_rate_min; ma_proc snd_pcm_hw_params_get_rate_max; ma_proc snd_pcm_hw_params_get_buffer_size; ma_proc snd_pcm_hw_params_get_periods; ma_proc snd_pcm_hw_params_get_access; ma_proc snd_pcm_hw_params_test_format; ma_proc snd_pcm_hw_params_test_channels; ma_proc snd_pcm_hw_params_test_rate; ma_proc snd_pcm_hw_params; ma_proc snd_pcm_sw_params_sizeof; ma_proc snd_pcm_sw_params_current; ma_proc snd_pcm_sw_params_get_boundary; ma_proc snd_pcm_sw_params_set_avail_min; ma_proc snd_pcm_sw_params_set_start_threshold; ma_proc snd_pcm_sw_params_set_stop_threshold; ma_proc snd_pcm_sw_params; ma_proc snd_pcm_format_mask_sizeof; ma_proc snd_pcm_format_mask_test; ma_proc snd_pcm_get_chmap; ma_proc snd_pcm_state; ma_proc snd_pcm_prepare; ma_proc snd_pcm_start; ma_proc snd_pcm_drop; ma_proc snd_pcm_drain; ma_proc snd_pcm_reset; ma_proc snd_device_name_hint; ma_proc snd_device_name_get_hint; ma_proc snd_card_get_index; ma_proc snd_device_name_free_hint; ma_proc snd_pcm_mmap_begin; ma_proc snd_pcm_mmap_commit; ma_proc snd_pcm_recover; ma_proc snd_pcm_readi; ma_proc snd_pcm_writei; ma_proc snd_pcm_avail; ma_proc snd_pcm_avail_update; ma_proc snd_pcm_wait; ma_proc snd_pcm_nonblock; ma_proc snd_pcm_info; ma_proc snd_pcm_info_sizeof; ma_proc snd_pcm_info_get_name; ma_proc snd_pcm_poll_descriptors; ma_proc snd_pcm_poll_descriptors_count; ma_proc snd_pcm_poll_descriptors_revents; ma_proc snd_config_update_free_global; ma_mutex internalDeviceEnumLock; ma_bool32 useVerboseDeviceEnumeration; } alsa; #endif #ifdef MA_SUPPORT_PULSEAUDIO struct { ma_handle pulseSO; ma_proc pa_mainloop_new; ma_proc pa_mainloop_free; ma_proc pa_mainloop_quit; ma_proc pa_mainloop_get_api; ma_proc pa_mainloop_iterate; ma_proc pa_mainloop_wakeup; ma_proc pa_threaded_mainloop_new; ma_proc pa_threaded_mainloop_free; ma_proc pa_threaded_mainloop_start; ma_proc pa_threaded_mainloop_stop; ma_proc pa_threaded_mainloop_lock; ma_proc pa_threaded_mainloop_unlock; ma_proc pa_threaded_mainloop_wait; ma_proc pa_threaded_mainloop_signal; ma_proc pa_threaded_mainloop_accept; ma_proc pa_threaded_mainloop_get_retval; ma_proc pa_threaded_mainloop_get_api; ma_proc pa_threaded_mainloop_in_thread; ma_proc pa_threaded_mainloop_set_name; ma_proc pa_context_new; ma_proc pa_context_unref; ma_proc pa_context_connect; ma_proc pa_context_disconnect; ma_proc pa_context_set_state_callback; ma_proc pa_context_get_state; ma_proc pa_context_get_sink_info_list; ma_proc pa_context_get_source_info_list; ma_proc pa_context_get_sink_info_by_name; ma_proc pa_context_get_source_info_by_name; ma_proc pa_operation_unref; ma_proc pa_operation_get_state; ma_proc pa_channel_map_init_extend; ma_proc pa_channel_map_valid; ma_proc pa_channel_map_compatible; ma_proc pa_stream_new; ma_proc pa_stream_unref; ma_proc pa_stream_connect_playback; ma_proc pa_stream_connect_record; ma_proc pa_stream_disconnect; ma_proc pa_stream_get_state; ma_proc pa_stream_get_sample_spec; ma_proc pa_stream_get_channel_map; ma_proc pa_stream_get_buffer_attr; ma_proc pa_stream_set_buffer_attr; ma_proc pa_stream_get_device_name; ma_proc pa_stream_set_write_callback; ma_proc pa_stream_set_read_callback; ma_proc pa_stream_set_suspended_callback; ma_proc pa_stream_set_moved_callback; ma_proc pa_stream_is_suspended; ma_proc pa_stream_flush; ma_proc pa_stream_drain; ma_proc pa_stream_is_corked; ma_proc pa_stream_cork; ma_proc pa_stream_trigger; ma_proc pa_stream_begin_write; ma_proc pa_stream_write; ma_proc pa_stream_peek; ma_proc pa_stream_drop; ma_proc pa_stream_writable_size; ma_proc pa_stream_readable_size; /*pa_mainloop**/ ma_ptr pMainLoop; /*pa_context**/ ma_ptr pPulseContext; char* pApplicationName; /* Set when the context is initialized. Used by devices for their local pa_context objects. */ char* pServerName; /* Set when the context is initialized. Used by devices for their local pa_context objects. */ } pulse; #endif #ifdef MA_SUPPORT_JACK struct { ma_handle jackSO; ma_proc jack_client_open; ma_proc jack_client_close; ma_proc jack_client_name_size; ma_proc jack_set_process_callback; ma_proc jack_set_buffer_size_callback; ma_proc jack_on_shutdown; ma_proc jack_get_sample_rate; ma_proc jack_get_buffer_size; ma_proc jack_get_ports; ma_proc jack_activate; ma_proc jack_deactivate; ma_proc jack_connect; ma_proc jack_port_register; ma_proc jack_port_name; ma_proc jack_port_get_buffer; ma_proc jack_free; char* pClientName; ma_bool32 tryStartServer; } jack; #endif #ifdef MA_SUPPORT_COREAUDIO struct { ma_handle hCoreFoundation; ma_proc CFStringGetCString; ma_proc CFRelease; ma_handle hCoreAudio; ma_proc AudioObjectGetPropertyData; ma_proc AudioObjectGetPropertyDataSize; ma_proc AudioObjectSetPropertyData; ma_proc AudioObjectAddPropertyListener; ma_proc AudioObjectRemovePropertyListener; ma_handle hAudioUnit; /* Could possibly be set to AudioToolbox on later versions of macOS. */ ma_proc AudioComponentFindNext; ma_proc AudioComponentInstanceDispose; ma_proc AudioComponentInstanceNew; ma_proc AudioOutputUnitStart; ma_proc AudioOutputUnitStop; ma_proc AudioUnitAddPropertyListener; ma_proc AudioUnitGetPropertyInfo; ma_proc AudioUnitGetProperty; ma_proc AudioUnitSetProperty; ma_proc AudioUnitInitialize; ma_proc AudioUnitRender; /*AudioComponent*/ ma_ptr component; ma_bool32 noAudioSessionDeactivate; /* For tracking whether or not the iOS audio session should be explicitly deactivated. Set from the config in ma_context_init__coreaudio(). */ } coreaudio; #endif #ifdef MA_SUPPORT_SNDIO struct { ma_handle sndioSO; ma_proc sio_open; ma_proc sio_close; ma_proc sio_setpar; ma_proc sio_getpar; ma_proc sio_getcap; ma_proc sio_start; ma_proc sio_stop; ma_proc sio_read; ma_proc sio_write; ma_proc sio_onmove; ma_proc sio_nfds; ma_proc sio_pollfd; ma_proc sio_revents; ma_proc sio_eof; ma_proc sio_setvol; ma_proc sio_onvol; ma_proc sio_initpar; } sndio; #endif #ifdef MA_SUPPORT_AUDIO4 struct { int _unused; } audio4; #endif #ifdef MA_SUPPORT_OSS struct { int versionMajor; int versionMinor; } oss; #endif #ifdef MA_SUPPORT_AAUDIO struct { ma_handle hAAudio; /* libaaudio.so */ ma_proc AAudio_createStreamBuilder; ma_proc AAudioStreamBuilder_delete; ma_proc AAudioStreamBuilder_setDeviceId; ma_proc AAudioStreamBuilder_setDirection; ma_proc AAudioStreamBuilder_setSharingMode; ma_proc AAudioStreamBuilder_setFormat; ma_proc AAudioStreamBuilder_setChannelCount; ma_proc AAudioStreamBuilder_setSampleRate; ma_proc AAudioStreamBuilder_setBufferCapacityInFrames; ma_proc AAudioStreamBuilder_setFramesPerDataCallback; ma_proc AAudioStreamBuilder_setDataCallback; ma_proc AAudioStreamBuilder_setErrorCallback; ma_proc AAudioStreamBuilder_setPerformanceMode; ma_proc AAudioStreamBuilder_setUsage; ma_proc AAudioStreamBuilder_setContentType; ma_proc AAudioStreamBuilder_setInputPreset; ma_proc AAudioStreamBuilder_openStream; ma_proc AAudioStream_close; ma_proc AAudioStream_getState; ma_proc AAudioStream_waitForStateChange; ma_proc AAudioStream_getFormat; ma_proc AAudioStream_getChannelCount; ma_proc AAudioStream_getSampleRate; ma_proc AAudioStream_getBufferCapacityInFrames; ma_proc AAudioStream_getFramesPerDataCallback; ma_proc AAudioStream_getFramesPerBurst; ma_proc AAudioStream_requestStart; ma_proc AAudioStream_requestStop; ma_device_job_thread jobThread; /* For processing operations outside of the error callback, specifically device disconnections and rerouting. */ } aaudio; #endif #ifdef MA_SUPPORT_OPENSL struct { ma_handle libOpenSLES; ma_handle SL_IID_ENGINE; ma_handle SL_IID_AUDIOIODEVICECAPABILITIES; ma_handle SL_IID_ANDROIDSIMPLEBUFFERQUEUE; ma_handle SL_IID_RECORD; ma_handle SL_IID_PLAY; ma_handle SL_IID_OUTPUTMIX; ma_handle SL_IID_ANDROIDCONFIGURATION; ma_proc slCreateEngine; } opensl; #endif #ifdef MA_SUPPORT_WEBAUDIO struct { int _unused; } webaudio; #endif #ifdef MA_SUPPORT_NULL struct { int _unused; } null_backend; #endif }; union { #ifdef MA_WIN32 struct { /*HMODULE*/ ma_handle hOle32DLL; ma_proc CoInitializeEx; ma_proc CoUninitialize; ma_proc CoCreateInstance; ma_proc CoTaskMemFree; ma_proc PropVariantClear; ma_proc StringFromGUID2; /*HMODULE*/ ma_handle hUser32DLL; ma_proc GetForegroundWindow; ma_proc GetDesktopWindow; /*HMODULE*/ ma_handle hAdvapi32DLL; ma_proc RegOpenKeyExA; ma_proc RegCloseKey; ma_proc RegQueryValueExA; } win32; #endif #ifdef MA_POSIX struct { ma_handle pthreadSO; ma_proc pthread_create; ma_proc pthread_join; ma_proc pthread_mutex_init; ma_proc pthread_mutex_destroy; ma_proc pthread_mutex_lock; ma_proc pthread_mutex_unlock; ma_proc pthread_cond_init; ma_proc pthread_cond_destroy; ma_proc pthread_cond_wait; ma_proc pthread_cond_signal; ma_proc pthread_attr_init; ma_proc pthread_attr_destroy; ma_proc pthread_attr_setschedpolicy; ma_proc pthread_attr_getschedparam; ma_proc pthread_attr_setschedparam; } posix; #endif int _unused; }; }; struct ma_device { ma_context* pContext; ma_device_type type; ma_uint32 sampleRate; MA_ATOMIC(4, ma_device_state) state; /* The state of the device is variable and can change at any time on any thread. Must be used atomically. */ ma_device_data_proc onData; /* Set once at initialization time and should not be changed after. */ ma_device_notification_proc onNotification; /* Set once at initialization time and should not be changed after. */ ma_stop_proc onStop; /* DEPRECATED. Use the notification callback instead. Set once at initialization time and should not be changed after. */ void* pUserData; /* Application defined data. */ ma_mutex startStopLock; ma_event wakeupEvent; ma_event startEvent; ma_event stopEvent; ma_thread thread; ma_result workResult; /* This is set by the worker thread after it's finished doing a job. */ ma_bool8 isOwnerOfContext; /* When set to true, uninitializing the device will also uninitialize the context. Set to true when NULL is passed into ma_device_init(). */ ma_bool8 noPreSilencedOutputBuffer; ma_bool8 noClip; ma_bool8 noDisableDenormals; ma_bool8 noFixedSizedCallback; MA_ATOMIC(4, float) masterVolumeFactor; /* Linear 0..1. Can be read and written simultaneously by different threads. Must be used atomically. */ ma_duplex_rb duplexRB; /* Intermediary buffer for duplex device on asynchronous backends. */ struct { ma_resample_algorithm algorithm; ma_resampling_backend_vtable* pBackendVTable; void* pBackendUserData; struct { ma_uint32 lpfOrder; } linear; } resampling; struct { ma_device_id* pID; /* Set to NULL if using default ID, otherwise set to the address of "id". */ ma_device_id id; /* If using an explicit device, will be set to a copy of the ID used for initialization. Otherwise cleared to 0. */ char name[MA_MAX_DEVICE_NAME_LENGTH + 1]; /* Maybe temporary. Likely to be replaced with a query API. */ ma_share_mode shareMode; /* Set to whatever was passed in when the device was initialized. */ ma_format format; ma_uint32 channels; ma_channel channelMap[MA_MAX_CHANNELS]; ma_format internalFormat; ma_uint32 internalChannels; ma_uint32 internalSampleRate; ma_channel internalChannelMap[MA_MAX_CHANNELS]; ma_uint32 internalPeriodSizeInFrames; ma_uint32 internalPeriods; ma_channel_mix_mode channelMixMode; ma_bool32 calculateLFEFromSpatialChannels; ma_data_converter converter; void* pIntermediaryBuffer; /* For implementing fixed sized buffer callbacks. Will be null if using variable sized callbacks. */ ma_uint32 intermediaryBufferCap; ma_uint32 intermediaryBufferLen; /* How many valid frames are sitting in the intermediary buffer. */ void* pInputCache; /* In external format. Can be null. */ ma_uint64 inputCacheCap; ma_uint64 inputCacheConsumed; ma_uint64 inputCacheRemaining; } playback; struct { ma_device_id* pID; /* Set to NULL if using default ID, otherwise set to the address of "id". */ ma_device_id id; /* If using an explicit device, will be set to a copy of the ID used for initialization. Otherwise cleared to 0. */ char name[MA_MAX_DEVICE_NAME_LENGTH + 1]; /* Maybe temporary. Likely to be replaced with a query API. */ ma_share_mode shareMode; /* Set to whatever was passed in when the device was initialized. */ ma_format format; ma_uint32 channels; ma_channel channelMap[MA_MAX_CHANNELS]; ma_format internalFormat; ma_uint32 internalChannels; ma_uint32 internalSampleRate; ma_channel internalChannelMap[MA_MAX_CHANNELS]; ma_uint32 internalPeriodSizeInFrames; ma_uint32 internalPeriods; ma_channel_mix_mode channelMixMode; ma_bool32 calculateLFEFromSpatialChannels; ma_data_converter converter; void* pIntermediaryBuffer; /* For implementing fixed sized buffer callbacks. Will be null if using variable sized callbacks. */ ma_uint32 intermediaryBufferCap; ma_uint32 intermediaryBufferLen; /* How many valid frames are sitting in the intermediary buffer. */ } capture; union { #ifdef MA_SUPPORT_WASAPI struct { /*IAudioClient**/ ma_ptr pAudioClientPlayback; /*IAudioClient**/ ma_ptr pAudioClientCapture; /*IAudioRenderClient**/ ma_ptr pRenderClient; /*IAudioCaptureClient**/ ma_ptr pCaptureClient; /*IMMDeviceEnumerator**/ ma_ptr pDeviceEnumerator; /* Used for IMMNotificationClient notifications. Required for detecting default device changes. */ ma_IMMNotificationClient notificationClient; /*HANDLE*/ ma_handle hEventPlayback; /* Auto reset. Initialized to signaled. */ /*HANDLE*/ ma_handle hEventCapture; /* Auto reset. Initialized to unsignaled. */ ma_uint32 actualBufferSizeInFramesPlayback; /* Value from GetBufferSize(). internalPeriodSizeInFrames is not set to the _actual_ buffer size when low-latency shared mode is being used due to the way the IAudioClient3 API works. */ ma_uint32 actualBufferSizeInFramesCapture; ma_uint32 originalPeriodSizeInFrames; ma_uint32 originalPeriodSizeInMilliseconds; ma_uint32 originalPeriods; ma_performance_profile originalPerformanceProfile; ma_uint32 periodSizeInFramesPlayback; ma_uint32 periodSizeInFramesCapture; void* pMappedBufferCapture; ma_uint32 mappedBufferCaptureCap; ma_uint32 mappedBufferCaptureLen; void* pMappedBufferPlayback; ma_uint32 mappedBufferPlaybackCap; ma_uint32 mappedBufferPlaybackLen; MA_ATOMIC(4, ma_bool32) isStartedCapture; /* Can be read and written simultaneously across different threads. Must be used atomically, and must be 32-bit. */ MA_ATOMIC(4, ma_bool32) isStartedPlayback; /* Can be read and written simultaneously across different threads. Must be used atomically, and must be 32-bit. */ ma_uint32 loopbackProcessID; ma_bool8 loopbackProcessExclude; ma_bool8 noAutoConvertSRC; /* When set to true, disables the use of AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM. */ ma_bool8 noDefaultQualitySRC; /* When set to true, disables the use of AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY. */ ma_bool8 noHardwareOffloading; ma_bool8 allowCaptureAutoStreamRouting; ma_bool8 allowPlaybackAutoStreamRouting; ma_bool8 isDetachedPlayback; ma_bool8 isDetachedCapture; ma_wasapi_usage usage; void *hAvrtHandle; } wasapi; #endif #ifdef MA_SUPPORT_DSOUND struct { /*LPDIRECTSOUND*/ ma_ptr pPlayback; /*LPDIRECTSOUNDBUFFER*/ ma_ptr pPlaybackPrimaryBuffer; /*LPDIRECTSOUNDBUFFER*/ ma_ptr pPlaybackBuffer; /*LPDIRECTSOUNDCAPTURE*/ ma_ptr pCapture; /*LPDIRECTSOUNDCAPTUREBUFFER*/ ma_ptr pCaptureBuffer; } dsound; #endif #ifdef MA_SUPPORT_WINMM struct { /*HWAVEOUT*/ ma_handle hDevicePlayback; /*HWAVEIN*/ ma_handle hDeviceCapture; /*HANDLE*/ ma_handle hEventPlayback; /*HANDLE*/ ma_handle hEventCapture; ma_uint32 fragmentSizeInFrames; ma_uint32 iNextHeaderPlayback; /* [0,periods). Used as an index into pWAVEHDRPlayback. */ ma_uint32 iNextHeaderCapture; /* [0,periods). Used as an index into pWAVEHDRCapture. */ ma_uint32 headerFramesConsumedPlayback; /* The number of PCM frames consumed in the buffer in pWAVEHEADER[iNextHeader]. */ ma_uint32 headerFramesConsumedCapture; /* ^^^ */ /*WAVEHDR**/ ma_uint8* pWAVEHDRPlayback; /* One instantiation for each period. */ /*WAVEHDR**/ ma_uint8* pWAVEHDRCapture; /* One instantiation for each period. */ ma_uint8* pIntermediaryBufferPlayback; ma_uint8* pIntermediaryBufferCapture; ma_uint8* _pHeapData; /* Used internally and is used for the heap allocated data for the intermediary buffer and the WAVEHDR structures. */ } winmm; #endif #ifdef MA_SUPPORT_ALSA struct { /*snd_pcm_t**/ ma_ptr pPCMPlayback; /*snd_pcm_t**/ ma_ptr pPCMCapture; /*struct pollfd**/ void* pPollDescriptorsPlayback; /*struct pollfd**/ void* pPollDescriptorsCapture; int pollDescriptorCountPlayback; int pollDescriptorCountCapture; int wakeupfdPlayback; /* eventfd for waking up from poll() when the playback device is stopped. */ int wakeupfdCapture; /* eventfd for waking up from poll() when the capture device is stopped. */ ma_bool8 isUsingMMapPlayback; ma_bool8 isUsingMMapCapture; } alsa; #endif #ifdef MA_SUPPORT_PULSEAUDIO struct { /*pa_mainloop**/ ma_ptr pMainLoop; /*pa_context**/ ma_ptr pPulseContext; /*pa_stream**/ ma_ptr pStreamPlayback; /*pa_stream**/ ma_ptr pStreamCapture; } pulse; #endif #ifdef MA_SUPPORT_JACK struct { /*jack_client_t**/ ma_ptr pClient; /*jack_port_t**/ ma_ptr* ppPortsPlayback; /*jack_port_t**/ ma_ptr* ppPortsCapture; float* pIntermediaryBufferPlayback; /* Typed as a float because JACK is always floating point. */ float* pIntermediaryBufferCapture; } jack; #endif #ifdef MA_SUPPORT_COREAUDIO struct { ma_uint32 deviceObjectIDPlayback; ma_uint32 deviceObjectIDCapture; /*AudioUnit*/ ma_ptr audioUnitPlayback; /*AudioUnit*/ ma_ptr audioUnitCapture; /*AudioBufferList**/ ma_ptr pAudioBufferList; /* Only used for input devices. */ ma_uint32 audioBufferCapInFrames; /* Only used for input devices. The capacity in frames of each buffer in pAudioBufferList. */ ma_event stopEvent; ma_uint32 originalPeriodSizeInFrames; ma_uint32 originalPeriodSizeInMilliseconds; ma_uint32 originalPeriods; ma_performance_profile originalPerformanceProfile; ma_bool32 isDefaultPlaybackDevice; ma_bool32 isDefaultCaptureDevice; ma_bool32 isSwitchingPlaybackDevice; /* <-- Set to true when the default device has changed and miniaudio is in the process of switching. */ ma_bool32 isSwitchingCaptureDevice; /* <-- Set to true when the default device has changed and miniaudio is in the process of switching. */ void* pNotificationHandler; /* Only used on mobile platforms. Obj-C object for handling route changes. */ } coreaudio; #endif #ifdef MA_SUPPORT_SNDIO struct { ma_ptr handlePlayback; ma_ptr handleCapture; ma_bool32 isStartedPlayback; ma_bool32 isStartedCapture; } sndio; #endif #ifdef MA_SUPPORT_AUDIO4 struct { int fdPlayback; int fdCapture; } audio4; #endif #ifdef MA_SUPPORT_OSS struct { int fdPlayback; int fdCapture; } oss; #endif #ifdef MA_SUPPORT_AAUDIO struct { /*AAudioStream**/ ma_ptr pStreamPlayback; /*AAudioStream**/ ma_ptr pStreamCapture; ma_aaudio_usage usage; ma_aaudio_content_type contentType; ma_aaudio_input_preset inputPreset; ma_bool32 noAutoStartAfterReroute; } aaudio; #endif #ifdef MA_SUPPORT_OPENSL struct { /*SLObjectItf*/ ma_ptr pOutputMixObj; /*SLOutputMixItf*/ ma_ptr pOutputMix; /*SLObjectItf*/ ma_ptr pAudioPlayerObj; /*SLPlayItf*/ ma_ptr pAudioPlayer; /*SLObjectItf*/ ma_ptr pAudioRecorderObj; /*SLRecordItf*/ ma_ptr pAudioRecorder; /*SLAndroidSimpleBufferQueueItf*/ ma_ptr pBufferQueuePlayback; /*SLAndroidSimpleBufferQueueItf*/ ma_ptr pBufferQueueCapture; ma_bool32 isDrainingCapture; ma_bool32 isDrainingPlayback; ma_uint32 currentBufferIndexPlayback; ma_uint32 currentBufferIndexCapture; ma_uint8* pBufferPlayback; /* This is malloc()'d and is used for storing audio data. Typed as ma_uint8 for easy offsetting. */ ma_uint8* pBufferCapture; } opensl; #endif #ifdef MA_SUPPORT_WEBAUDIO struct { int indexPlayback; /* We use a factory on the JavaScript side to manage devices and use an index for JS/C interop. */ int indexCapture; } webaudio; #endif #ifdef MA_SUPPORT_NULL struct { ma_thread deviceThread; ma_event operationEvent; ma_event operationCompletionEvent; ma_semaphore operationSemaphore; ma_uint32 operation; ma_result operationResult; ma_timer timer; double priorRunTime; ma_uint32 currentPeriodFramesRemainingPlayback; ma_uint32 currentPeriodFramesRemainingCapture; ma_uint64 lastProcessedFramePlayback; ma_uint64 lastProcessedFrameCapture; MA_ATOMIC(4, ma_bool32) isStarted; /* Read and written by multiple threads. Must be used atomically, and must be 32-bit for compiler compatibility. */ } null_device; #endif }; }; #if defined(_MSC_VER) && !defined(__clang__) #pragma warning(pop) #elif defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))) #pragma GCC diagnostic pop /* For ISO C99 doesn't support unnamed structs/unions [-Wpedantic] */ #endif /* Initializes a `ma_context_config` object. Return Value ------------ A `ma_context_config` initialized to defaults. Remarks ------- You must always use this to initialize the default state of the `ma_context_config` object. Not using this will result in your program breaking when miniaudio is updated and new members are added to `ma_context_config`. It also sets logical defaults. You can override members of the returned object by changing it's members directly. See Also -------- ma_context_init() */ MA_API ma_context_config ma_context_config_init(void); /* Initializes a context. The context is used for selecting and initializing an appropriate backend and to represent the backend at a more global level than that of an individual device. There is one context to many devices, and a device is created from a context. A context is required to enumerate devices. Parameters ---------- backends (in, optional) A list of backends to try initializing, in priority order. Can be NULL, in which case it uses default priority order. backendCount (in, optional) The number of items in `backend`. Ignored if `backend` is NULL. pConfig (in, optional) The context configuration. pContext (in) A pointer to the context object being initialized. Return Value ------------ MA_SUCCESS if successful; any other error code otherwise. Thread Safety ------------- Unsafe. Do not call this function across multiple threads as some backends read and write to global state. Remarks ------- When `backends` is NULL, the default priority order will be used. Below is a list of backends in priority order: |-------------|-----------------------|--------------------------------------------------------| | Name | Enum Name | Supported Operating Systems | |-------------|-----------------------|--------------------------------------------------------| | WASAPI | ma_backend_wasapi | Windows Vista+ | | DirectSound | ma_backend_dsound | Windows XP+ | | WinMM | ma_backend_winmm | Windows XP+ (may work on older versions, but untested) | | Core Audio | ma_backend_coreaudio | macOS, iOS | | ALSA | ma_backend_alsa | Linux | | PulseAudio | ma_backend_pulseaudio | Cross Platform (disabled on Windows, BSD and Android) | | JACK | ma_backend_jack | Cross Platform (disabled on BSD and Android) | | sndio | ma_backend_sndio | OpenBSD | | audio(4) | ma_backend_audio4 | NetBSD, OpenBSD | | OSS | ma_backend_oss | FreeBSD | | AAudio | ma_backend_aaudio | Android 8+ | | OpenSL|ES | ma_backend_opensl | Android (API level 16+) | | Web Audio | ma_backend_webaudio | Web (via Emscripten) | | Null | ma_backend_null | Cross Platform (not used on Web) | |-------------|-----------------------|--------------------------------------------------------| The context can be configured via the `pConfig` argument. The config object is initialized with `ma_context_config_init()`. Individual configuration settings can then be set directly on the structure. Below are the members of the `ma_context_config` object. pLog A pointer to the `ma_log` to post log messages to. Can be NULL if the application does not require logging. See the `ma_log` API for details on how to use the logging system. threadPriority The desired priority to use for the audio thread. Allowable values include the following: |--------------------------------------| | Thread Priority | |--------------------------------------| | ma_thread_priority_idle | | ma_thread_priority_lowest | | ma_thread_priority_low | | ma_thread_priority_normal | | ma_thread_priority_high | | ma_thread_priority_highest (default) | | ma_thread_priority_realtime | | ma_thread_priority_default | |--------------------------------------| threadStackSize The desired size of the stack for the audio thread. Defaults to the operating system's default. pUserData A pointer to application-defined data. This can be accessed from the context object directly such as `context.pUserData`. allocationCallbacks Structure containing custom allocation callbacks. Leaving this at defaults will cause it to use MA_MALLOC, MA_REALLOC and MA_FREE. These allocation callbacks will be used for anything tied to the context, including devices. alsa.useVerboseDeviceEnumeration ALSA will typically enumerate many different devices which can be intrusive and not user-friendly. To combat this, miniaudio will enumerate only unique card/device pairs by default. The problem with this is that you lose a bit of flexibility and control. Setting alsa.useVerboseDeviceEnumeration makes it so the ALSA backend includes all devices. Defaults to false. pulse.pApplicationName PulseAudio only. The application name to use when initializing the PulseAudio context with `pa_context_new()`. pulse.pServerName PulseAudio only. The name of the server to connect to with `pa_context_connect()`. pulse.tryAutoSpawn PulseAudio only. Whether or not to try automatically starting the PulseAudio daemon. Defaults to false. If you set this to true, keep in mind that miniaudio uses a trial and error method to find the most appropriate backend, and this will result in the PulseAudio daemon starting which may be intrusive for the end user. coreaudio.sessionCategory iOS only. The session category to use for the shared AudioSession instance. Below is a list of allowable values and their Core Audio equivalents. |-----------------------------------------|-------------------------------------| | miniaudio Token | Core Audio Token | |-----------------------------------------|-------------------------------------| | ma_ios_session_category_ambient | AVAudioSessionCategoryAmbient | | ma_ios_session_category_solo_ambient | AVAudioSessionCategorySoloAmbient | | ma_ios_session_category_playback | AVAudioSessionCategoryPlayback | | ma_ios_session_category_record | AVAudioSessionCategoryRecord | | ma_ios_session_category_play_and_record | AVAudioSessionCategoryPlayAndRecord | | ma_ios_session_category_multi_route | AVAudioSessionCategoryMultiRoute | | ma_ios_session_category_none | AVAudioSessionCategoryAmbient | | ma_ios_session_category_default | AVAudioSessionCategoryAmbient | |-----------------------------------------|-------------------------------------| coreaudio.sessionCategoryOptions iOS only. Session category options to use with the shared AudioSession instance. Below is a list of allowable values and their Core Audio equivalents. |---------------------------------------------------------------------------|------------------------------------------------------------------| | miniaudio Token | Core Audio Token | |---------------------------------------------------------------------------|------------------------------------------------------------------| | ma_ios_session_category_option_mix_with_others | AVAudioSessionCategoryOptionMixWithOthers | | ma_ios_session_category_option_duck_others | AVAudioSessionCategoryOptionDuckOthers | | ma_ios_session_category_option_allow_bluetooth | AVAudioSessionCategoryOptionAllowBluetooth | | ma_ios_session_category_option_default_to_speaker | AVAudioSessionCategoryOptionDefaultToSpeaker | | ma_ios_session_category_option_interrupt_spoken_audio_and_mix_with_others | AVAudioSessionCategoryOptionInterruptSpokenAudioAndMixWithOthers | | ma_ios_session_category_option_allow_bluetooth_a2dp | AVAudioSessionCategoryOptionAllowBluetoothA2DP | | ma_ios_session_category_option_allow_air_play | AVAudioSessionCategoryOptionAllowAirPlay | |---------------------------------------------------------------------------|------------------------------------------------------------------| coreaudio.noAudioSessionActivate iOS only. When set to true, does not perform an explicit [[AVAudioSession sharedInstace] setActive:true] on initialization. coreaudio.noAudioSessionDeactivate iOS only. When set to true, does not perform an explicit [[AVAudioSession sharedInstace] setActive:false] on uninitialization. jack.pClientName The name of the client to pass to `jack_client_open()`. jack.tryStartServer Whether or not to try auto-starting the JACK server. Defaults to false. It is recommended that only a single context is active at any given time because it's a bulky data structure which performs run-time linking for the relevant backends every time it's initialized. The location of the context cannot change throughout it's lifetime. Consider allocating the `ma_context` object with `malloc()` if this is an issue. The reason for this is that a pointer to the context is stored in the `ma_device` structure. Example 1 - Default Initialization ---------------------------------- The example below shows how to initialize the context using the default configuration. ```c ma_context context; ma_result result = ma_context_init(NULL, 0, NULL, &context); if (result != MA_SUCCESS) { // Error. } ``` Example 2 - Custom Configuration -------------------------------- The example below shows how to initialize the context using custom backend priorities and a custom configuration. In this hypothetical example, the program wants to prioritize ALSA over PulseAudio on Linux. They also want to avoid using the WinMM backend on Windows because it's latency is too high. They also want an error to be returned if no valid backend is available which they achieve by excluding the Null backend. For the configuration, the program wants to capture any log messages so they can, for example, route it to a log file and user interface. ```c ma_backend backends[] = { ma_backend_alsa, ma_backend_pulseaudio, ma_backend_wasapi, ma_backend_dsound }; ma_log log; ma_log_init(&log); ma_log_register_callback(&log, ma_log_callback_init(my_log_callbac, pMyLogUserData)); ma_context_config config = ma_context_config_init(); config.pLog = &log; // Specify a custom log object in the config so any logs that are posted from ma_context_init() are captured. ma_context context; ma_result result = ma_context_init(backends, sizeof(backends)/sizeof(backends[0]), &config, &context); if (result != MA_SUCCESS) { // Error. if (result == MA_NO_BACKEND) { // Couldn't find an appropriate backend. } } // You could also attach a log callback post-initialization: ma_log_register_callback(ma_context_get_log(&context), ma_log_callback_init(my_log_callback, pMyLogUserData)); ``` See Also -------- ma_context_config_init() ma_context_uninit() */ MA_API ma_result ma_context_init(const ma_backend backends[], ma_uint32 backendCount, const ma_context_config* pConfig, ma_context* pContext); /* Uninitializes a context. Return Value ------------ MA_SUCCESS if successful; any other error code otherwise. Thread Safety ------------- Unsafe. Do not call this function across multiple threads as some backends read and write to global state. Remarks ------- Results are undefined if you call this while any device created by this context is still active. See Also -------- ma_context_init() */ MA_API ma_result ma_context_uninit(ma_context* pContext); /* Retrieves the size of the ma_context object. This is mainly for the purpose of bindings to know how much memory to allocate. */ MA_API size_t ma_context_sizeof(void); /* Retrieves a pointer to the log object associated with this context. Remarks ------- Pass the returned pointer to `ma_log_post()`, `ma_log_postv()` or `ma_log_postf()` to post a log message. You can attach your own logging callback to the log with `ma_log_register_callback()` Return Value ------------ A pointer to the `ma_log` object that the context uses to post log messages. If some error occurs, NULL will be returned. */ MA_API ma_log* ma_context_get_log(ma_context* pContext); /* Enumerates over every device (both playback and capture). This is a lower-level enumeration function to the easier to use `ma_context_get_devices()`. Use `ma_context_enumerate_devices()` if you would rather not incur an internal heap allocation, or it simply suits your code better. Note that this only retrieves the ID and name/description of the device. The reason for only retrieving basic information is that it would otherwise require opening the backend device in order to probe it for more detailed information which can be inefficient. Consider using `ma_context_get_device_info()` for this, but don't call it from within the enumeration callback. Returning false from the callback will stop enumeration. Returning true will continue enumeration. Parameters ---------- pContext (in) A pointer to the context performing the enumeration. callback (in) The callback to fire for each enumerated device. pUserData (in) A pointer to application-defined data passed to the callback. Return Value ------------ MA_SUCCESS if successful; any other error code otherwise. Thread Safety ------------- Safe. This is guarded using a simple mutex lock. Remarks ------- Do _not_ assume the first enumerated device of a given type is the default device. Some backends and platforms may only support default playback and capture devices. In general, you should not do anything complicated from within the callback. In particular, do not try initializing a device from within the callback. Also, do not try to call `ma_context_get_device_info()` from within the callback. Consider using `ma_context_get_devices()` for a simpler and safer API, albeit at the expense of an internal heap allocation. Example 1 - Simple Enumeration ------------------------------ ma_bool32 ma_device_enum_callback(ma_context* pContext, ma_device_type deviceType, const ma_device_info* pInfo, void* pUserData) { printf("Device Name: %s\n", pInfo->name); return MA_TRUE; } ma_result result = ma_context_enumerate_devices(&context, my_device_enum_callback, pMyUserData); if (result != MA_SUCCESS) { // Error. } See Also -------- ma_context_get_devices() */ MA_API ma_result ma_context_enumerate_devices(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData); /* Retrieves basic information about every active playback and/or capture device. This function will allocate memory internally for the device lists and return a pointer to them through the `ppPlaybackDeviceInfos` and `ppCaptureDeviceInfos` parameters. If you do not want to incur the overhead of these allocations consider using `ma_context_enumerate_devices()` which will instead use a callback. Parameters ---------- pContext (in) A pointer to the context performing the enumeration. ppPlaybackDeviceInfos (out) A pointer to a pointer that will receive the address of a buffer containing the list of `ma_device_info` structures for playback devices. pPlaybackDeviceCount (out) A pointer to an unsigned integer that will receive the number of playback devices. ppCaptureDeviceInfos (out) A pointer to a pointer that will receive the address of a buffer containing the list of `ma_device_info` structures for capture devices. pCaptureDeviceCount (out) A pointer to an unsigned integer that will receive the number of capture devices. Return Value ------------ MA_SUCCESS if successful; any other error code otherwise. Thread Safety ------------- Unsafe. Since each call to this function invalidates the pointers from the previous call, you should not be calling this simultaneously across multiple threads. Instead, you need to make a copy of the returned data with your own higher level synchronization. Remarks ------- It is _not_ safe to assume the first device in the list is the default device. You can pass in NULL for the playback or capture lists in which case they'll be ignored. The returned pointers will become invalid upon the next call this this function, or when the context is uninitialized. Do not free the returned pointers. See Also -------- ma_context_get_devices() */ MA_API ma_result ma_context_get_devices(ma_context* pContext, ma_device_info** ppPlaybackDeviceInfos, ma_uint32* pPlaybackDeviceCount, ma_device_info** ppCaptureDeviceInfos, ma_uint32* pCaptureDeviceCount); /* Retrieves information about a device of the given type, with the specified ID and share mode. Parameters ---------- pContext (in) A pointer to the context performing the query. deviceType (in) The type of the device being queried. Must be either `ma_device_type_playback` or `ma_device_type_capture`. pDeviceID (in) The ID of the device being queried. pDeviceInfo (out) A pointer to the `ma_device_info` structure that will receive the device information. Return Value ------------ MA_SUCCESS if successful; any other error code otherwise. Thread Safety ------------- Safe. This is guarded using a simple mutex lock. Remarks ------- Do _not_ call this from within the `ma_context_enumerate_devices()` callback. It's possible for a device to have different information and capabilities depending on whether or not it's opened in shared or exclusive mode. For example, in shared mode, WASAPI always uses floating point samples for mixing, but in exclusive mode it can be anything. Therefore, this function allows you to specify which share mode you want information for. Note that not all backends and devices support shared or exclusive mode, in which case this function will fail if the requested share mode is unsupported. This leaves pDeviceInfo unmodified in the result of an error. */ MA_API ma_result ma_context_get_device_info(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo); /* Determines if the given context supports loopback mode. Parameters ---------- pContext (in) A pointer to the context getting queried. Return Value ------------ MA_TRUE if the context supports loopback mode; MA_FALSE otherwise. */ MA_API ma_bool32 ma_context_is_loopback_supported(ma_context* pContext); /* Initializes a device config with default settings. Parameters ---------- deviceType (in) The type of the device this config is being initialized for. This must set to one of the following: |-------------------------| | Device Type | |-------------------------| | ma_device_type_playback | | ma_device_type_capture | | ma_device_type_duplex | | ma_device_type_loopback | |-------------------------| Return Value ------------ A new device config object with default settings. You will typically want to adjust the config after this function returns. See remarks. Thread Safety ------------- Safe. Callback Safety --------------- Safe, but don't try initializing a device in a callback. Remarks ------- The returned config will be initialized to defaults. You will normally want to customize a few variables before initializing the device. See Example 1 for a typical configuration which sets the sample format, channel count, sample rate, data callback and user data. These are usually things you will want to change before initializing the device. See `ma_device_init()` for details on specific configuration options. Example 1 - Simple Configuration -------------------------------- The example below is what a program will typically want to configure for each device at a minimum. Notice how `ma_device_config_init()` is called first, and then the returned object is modified directly. This is important because it ensures that your program continues to work as new configuration options are added to the `ma_device_config` structure. ```c ma_device_config config = ma_device_config_init(ma_device_type_playback); config.playback.format = ma_format_f32; config.playback.channels = 2; config.sampleRate = 48000; config.dataCallback = ma_data_callback; config.pUserData = pMyUserData; ``` See Also -------- ma_device_init() ma_device_init_ex() */ MA_API ma_device_config ma_device_config_init(ma_device_type deviceType); /* Initializes a device. A device represents a physical audio device. The idea is you send or receive audio data from the device to either play it back through a speaker, or capture it from a microphone. Whether or not you should send or receive data from the device (or both) depends on the type of device you are initializing which can be playback, capture, full-duplex or loopback. (Note that loopback mode is only supported on select backends.) Sending and receiving audio data to and from the device is done via a callback which is fired by miniaudio at periodic time intervals. The frequency at which data is delivered to and from a device depends on the size of it's period. The size of the period can be defined in terms of PCM frames or milliseconds, whichever is more convenient. Generally speaking, the smaller the period, the lower the latency at the expense of higher CPU usage and increased risk of glitching due to the more frequent and granular data deliver intervals. The size of a period will depend on your requirements, but miniaudio's defaults should work fine for most scenarios. If you're building a game you should leave this fairly small, whereas if you're building a simple media player you can make it larger. Note that the period size you request is actually just a hint - miniaudio will tell the backend what you want, but the backend is ultimately responsible for what it gives you. You cannot assume you will get exactly what you ask for. When delivering data to and from a device you need to make sure it's in the correct format which you can set through the device configuration. You just set the format that you want to use and miniaudio will perform all of the necessary conversion for you internally. When delivering data to and from the callback you can assume the format is the same as what you requested when you initialized the device. See Remarks for more details on miniaudio's data conversion pipeline. Parameters ---------- pContext (in, optional) A pointer to the context that owns the device. This can be null, in which case it creates a default context internally. pConfig (in) A pointer to the device configuration. Cannot be null. See remarks for details. pDevice (out) A pointer to the device object being initialized. Return Value ------------ MA_SUCCESS if successful; any other error code otherwise. Thread Safety ------------- Unsafe. It is not safe to call this function simultaneously for different devices because some backends depend on and mutate global state. The same applies to calling this at the same time as `ma_device_uninit()`. Callback Safety --------------- Unsafe. It is not safe to call this inside any callback. Remarks ------- Setting `pContext` to NULL will result in miniaudio creating a default context internally and is equivalent to passing in a context initialized like so: ```c ma_context_init(NULL, 0, NULL, &context); ``` Do not set `pContext` to NULL if you are needing to open multiple devices. You can, however, use NULL when initializing the first device, and then use device.pContext for the initialization of other devices. The device can be configured via the `pConfig` argument. The config object is initialized with `ma_device_config_init()`. Individual configuration settings can then be set directly on the structure. Below are the members of the `ma_device_config` object. deviceType Must be `ma_device_type_playback`, `ma_device_type_capture`, `ma_device_type_duplex` of `ma_device_type_loopback`. sampleRate The sample rate, in hertz. The most common sample rates are 48000 and 44100. Setting this to 0 will use the device's native sample rate. periodSizeInFrames The desired size of a period in PCM frames. If this is 0, `periodSizeInMilliseconds` will be used instead. If both are 0 the default buffer size will be used depending on the selected performance profile. This value affects latency. See below for details. periodSizeInMilliseconds The desired size of a period in milliseconds. If this is 0, `periodSizeInFrames` will be used instead. If both are 0 the default buffer size will be used depending on the selected performance profile. The value affects latency. See below for details. periods The number of periods making up the device's entire buffer. The total buffer size is `periodSizeInFrames` or `periodSizeInMilliseconds` multiplied by this value. This is just a hint as backends will be the ones who ultimately decide how your periods will be configured. performanceProfile A hint to miniaudio as to the performance requirements of your program. Can be either `ma_performance_profile_low_latency` (default) or `ma_performance_profile_conservative`. This mainly affects the size of default buffers and can usually be left at it's default value. noPreSilencedOutputBuffer When set to true, the contents of the output buffer passed into the data callback will be left undefined. When set to false (default), the contents of the output buffer will be cleared the zero. You can use this to avoid the overhead of zeroing out the buffer if you can guarantee that your data callback will write to every sample in the output buffer, or if you are doing your own clearing. noClip When set to true, the contents of the output buffer passed into the data callback will be clipped after returning. When set to false (default), the contents of the output buffer are left alone after returning and it will be left up to the backend itself to decide whether or not the clip. This only applies when the playback sample format is f32. noDisableDenormals By default, miniaudio will disable denormals when the data callback is called. Setting this to true will prevent the disabling of denormals. noFixedSizedCallback Allows miniaudio to fire the data callback with any frame count. When this is set to false (the default), the data callback will be fired with a consistent frame count as specified by `periodSizeInFrames` or `periodSizeInMilliseconds`. When set to true, miniaudio will fire the callback with whatever the backend requests, which could be anything. dataCallback The callback to fire whenever data is ready to be delivered to or from the device. notificationCallback The callback to fire when something has changed with the device, such as whether or not it has been started or stopped. pUserData The user data pointer to use with the device. You can access this directly from the device object like `device.pUserData`. resampling.algorithm The resampling algorithm to use when miniaudio needs to perform resampling between the rate specified by `sampleRate` and the device's native rate. The default value is `ma_resample_algorithm_linear`, and the quality can be configured with `resampling.linear.lpfOrder`. resampling.pBackendVTable A pointer to an optional vtable that can be used for plugging in a custom resampler. resampling.pBackendUserData A pointer that will passed to callbacks in pBackendVTable. resampling.linear.lpfOrder The linear resampler applies a low-pass filter as part of it's procesing for anti-aliasing. This setting controls the order of the filter. The higher the value, the better the quality, in general. Setting this to 0 will disable low-pass filtering altogether. The maximum value is `MA_MAX_FILTER_ORDER`. The default value is `min(4, MA_MAX_FILTER_ORDER)`. playback.pDeviceID A pointer to a `ma_device_id` structure containing the ID of the playback device to initialize. Setting this NULL (default) will use the system's default playback device. Retrieve the device ID from the `ma_device_info` structure, which can be retrieved using device enumeration. playback.format The sample format to use for playback. When set to `ma_format_unknown` the device's native format will be used. This can be retrieved after initialization from the device object directly with `device.playback.format`. playback.channels The number of channels to use for playback. When set to 0 the device's native channel count will be used. This can be retrieved after initialization from the device object directly with `device.playback.channels`. playback.pChannelMap The channel map to use for playback. When left empty, the device's native channel map will be used. This can be retrieved after initialization from the device object direct with `device.playback.pChannelMap`. When set, the buffer should contain `channels` items. playback.shareMode The preferred share mode to use for playback. Can be either `ma_share_mode_shared` (default) or `ma_share_mode_exclusive`. Note that if you specify exclusive mode, but it's not supported by the backend, initialization will fail. You can then fall back to shared mode if desired by changing this to ma_share_mode_shared and reinitializing. capture.pDeviceID A pointer to a `ma_device_id` structure containing the ID of the capture device to initialize. Setting this NULL (default) will use the system's default capture device. Retrieve the device ID from the `ma_device_info` structure, which can be retrieved using device enumeration. capture.format The sample format to use for capture. When set to `ma_format_unknown` the device's native format will be used. This can be retrieved after initialization from the device object directly with `device.capture.format`. capture.channels The number of channels to use for capture. When set to 0 the device's native channel count will be used. This can be retrieved after initialization from the device object directly with `device.capture.channels`. capture.pChannelMap The channel map to use for capture. When left empty, the device's native channel map will be used. This can be retrieved after initialization from the device object direct with `device.capture.pChannelMap`. When set, the buffer should contain `channels` items. capture.shareMode The preferred share mode to use for capture. Can be either `ma_share_mode_shared` (default) or `ma_share_mode_exclusive`. Note that if you specify exclusive mode, but it's not supported by the backend, initialization will fail. You can then fall back to shared mode if desired by changing this to ma_share_mode_shared and reinitializing. wasapi.noAutoConvertSRC WASAPI only. When set to true, disables WASAPI's automatic resampling and forces the use of miniaudio's resampler. Defaults to false. wasapi.noDefaultQualitySRC WASAPI only. Only used when `wasapi.noAutoConvertSRC` is set to false. When set to true, disables the use of `AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY`. You should usually leave this set to false, which is the default. wasapi.noAutoStreamRouting WASAPI only. When set to true, disables automatic stream routing on the WASAPI backend. Defaults to false. wasapi.noHardwareOffloading WASAPI only. When set to true, disables the use of WASAPI's hardware offloading feature. Defaults to false. alsa.noMMap ALSA only. When set to true, disables MMap mode. Defaults to false. alsa.noAutoFormat ALSA only. When set to true, disables ALSA's automatic format conversion by including the SND_PCM_NO_AUTO_FORMAT flag. Defaults to false. alsa.noAutoChannels ALSA only. When set to true, disables ALSA's automatic channel conversion by including the SND_PCM_NO_AUTO_CHANNELS flag. Defaults to false. alsa.noAutoResample ALSA only. When set to true, disables ALSA's automatic resampling by including the SND_PCM_NO_AUTO_RESAMPLE flag. Defaults to false. pulse.pStreamNamePlayback PulseAudio only. Sets the stream name for playback. pulse.pStreamNameCapture PulseAudio only. Sets the stream name for capture. coreaudio.allowNominalSampleRateChange Core Audio only. Desktop only. When enabled, allows the sample rate of the device to be changed at the operating system level. This is disabled by default in order to prevent intrusive changes to the user's system. This is useful if you want to use a sample rate that is known to be natively supported by the hardware thereby avoiding the cost of resampling. When set to true, miniaudio will find the closest match between the sample rate requested in the device config and the sample rates natively supported by the hardware. When set to false, the sample rate currently set by the operating system will always be used. opensl.streamType OpenSL only. Explicitly sets the stream type. If left unset (`ma_opensl_stream_type_default`), the stream type will be left unset. Think of this as the type of audio you're playing. opensl.recordingPreset OpenSL only. Explicitly sets the type of recording your program will be doing. When left unset, the recording preset will be left unchanged. aaudio.usage AAudio only. Explicitly sets the nature of the audio the program will be consuming. When left unset, the usage will be left unchanged. aaudio.contentType AAudio only. Sets the content type. When left unset, the content type will be left unchanged. aaudio.inputPreset AAudio only. Explicitly sets the type of recording your program will be doing. When left unset, the input preset will be left unchanged. aaudio.noAutoStartAfterReroute AAudio only. Controls whether or not the device should be automatically restarted after a stream reroute. When set to false (default) the device will be restarted automatically; otherwise the device will be stopped. Once initialized, the device's config is immutable. If you need to change the config you will need to initialize a new device. After initializing the device it will be in a stopped state. To start it, use `ma_device_start()`. If both `periodSizeInFrames` and `periodSizeInMilliseconds` are set to zero, it will default to `MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_LOW_LATENCY` or `MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_CONSERVATIVE`, depending on whether or not `performanceProfile` is set to `ma_performance_profile_low_latency` or `ma_performance_profile_conservative`. If you request exclusive mode and the backend does not support it an error will be returned. For robustness, you may want to first try initializing the device in exclusive mode, and then fall back to shared mode if required. Alternatively you can just request shared mode (the default if you leave it unset in the config) which is the most reliable option. Some backends do not have a practical way of choosing whether or not the device should be exclusive or not (ALSA, for example) in which case it just acts as a hint. Unless you have special requirements you should try avoiding exclusive mode as it's intrusive to the user. Starting with Windows 10, miniaudio will use low-latency shared mode where possible which may make exclusive mode unnecessary. When sending or receiving data to/from a device, miniaudio will internally perform a format conversion to convert between the format specified by the config and the format used internally by the backend. If you pass in 0 for the sample format, channel count, sample rate _and_ channel map, data transmission will run on an optimized pass-through fast path. You can retrieve the format, channel count and sample rate by inspecting the `playback/capture.format`, `playback/capture.channels` and `sampleRate` members of the device object. When compiling for UWP you must ensure you call this function on the main UI thread because the operating system may need to present the user with a message asking for permissions. Please refer to the official documentation for ActivateAudioInterfaceAsync() for more information. ALSA Specific: When initializing the default device, requesting shared mode will try using the "dmix" device for playback and the "dsnoop" device for capture. If these fail it will try falling back to the "hw" device. Example 1 - Simple Initialization --------------------------------- This example shows how to initialize a simple playback device using a standard configuration. If you are just needing to do simple playback from the default playback device this is usually all you need. ```c ma_device_config config = ma_device_config_init(ma_device_type_playback); config.playback.format = ma_format_f32; config.playback.channels = 2; config.sampleRate = 48000; config.dataCallback = ma_data_callback; config.pMyUserData = pMyUserData; ma_device device; ma_result result = ma_device_init(NULL, &config, &device); if (result != MA_SUCCESS) { // Error } ``` Example 2 - Advanced Initialization ----------------------------------- This example shows how you might do some more advanced initialization. In this hypothetical example we want to control the latency by setting the buffer size and period count. We also want to allow the user to be able to choose which device to output from which means we need a context so we can perform device enumeration. ```c ma_context context; ma_result result = ma_context_init(NULL, 0, NULL, &context); if (result != MA_SUCCESS) { // Error } ma_device_info* pPlaybackDeviceInfos; ma_uint32 playbackDeviceCount; result = ma_context_get_devices(&context, &pPlaybackDeviceInfos, &playbackDeviceCount, NULL, NULL); if (result != MA_SUCCESS) { // Error } // ... choose a device from pPlaybackDeviceInfos ... ma_device_config config = ma_device_config_init(ma_device_type_playback); config.playback.pDeviceID = pMyChosenDeviceID; // <-- Get this from the `id` member of one of the `ma_device_info` objects returned by ma_context_get_devices(). config.playback.format = ma_format_f32; config.playback.channels = 2; config.sampleRate = 48000; config.dataCallback = ma_data_callback; config.pUserData = pMyUserData; config.periodSizeInMilliseconds = 10; config.periods = 3; ma_device device; result = ma_device_init(&context, &config, &device); if (result != MA_SUCCESS) { // Error } ``` See Also -------- ma_device_config_init() ma_device_uninit() ma_device_start() ma_context_init() ma_context_get_devices() ma_context_enumerate_devices() */ MA_API ma_result ma_device_init(ma_context* pContext, const ma_device_config* pConfig, ma_device* pDevice); /* Initializes a device without a context, with extra parameters for controlling the configuration of the internal self-managed context. This is the same as `ma_device_init()`, only instead of a context being passed in, the parameters from `ma_context_init()` are passed in instead. This function allows you to configure the internally created context. Parameters ---------- backends (in, optional) A list of backends to try initializing, in priority order. Can be NULL, in which case it uses default priority order. backendCount (in, optional) The number of items in `backend`. Ignored if `backend` is NULL. pContextConfig (in, optional) The context configuration. pConfig (in) A pointer to the device configuration. Cannot be null. See remarks for details. pDevice (out) A pointer to the device object being initialized. Return Value ------------ MA_SUCCESS if successful; any other error code otherwise. Thread Safety ------------- Unsafe. It is not safe to call this function simultaneously for different devices because some backends depend on and mutate global state. The same applies to calling this at the same time as `ma_device_uninit()`. Callback Safety --------------- Unsafe. It is not safe to call this inside any callback. Remarks ------- You only need to use this function if you want to configure the context differently to it's defaults. You should never use this function if you want to manage your own context. See the documentation for `ma_context_init()` for information on the different context configuration options. See Also -------- ma_device_init() ma_device_uninit() ma_device_config_init() ma_context_init() */ MA_API ma_result ma_device_init_ex(const ma_backend backends[], ma_uint32 backendCount, const ma_context_config* pContextConfig, const ma_device_config* pConfig, ma_device* pDevice); /* Uninitializes a device. This will explicitly stop the device. You do not need to call `ma_device_stop()` beforehand, but it's harmless if you do. Parameters ---------- pDevice (in) A pointer to the device to stop. Return Value ------------ Nothing Thread Safety ------------- Unsafe. As soon as this API is called the device should be considered undefined. Callback Safety --------------- Unsafe. It is not safe to call this inside any callback. Doing this will result in a deadlock. See Also -------- ma_device_init() ma_device_stop() */ MA_API void ma_device_uninit(ma_device* pDevice); /* Retrieves a pointer to the context that owns the given device. */ MA_API ma_context* ma_device_get_context(ma_device* pDevice); /* Helper function for retrieving the log object associated with the context that owns this device. */ MA_API ma_log* ma_device_get_log(ma_device* pDevice); /* Retrieves information about the device. Parameters ---------- pDevice (in) A pointer to the device whose information is being retrieved. type (in) The device type. This parameter is required for duplex devices. When retrieving device information, you are doing so for an individual playback or capture device. pDeviceInfo (out) A pointer to the `ma_device_info` that will receive the device information. Return Value ------------ MA_SUCCESS if successful; any other error code otherwise. Thread Safety ------------- Unsafe. This should be considered unsafe because it may be calling into the backend which may or may not be safe. Callback Safety --------------- Unsafe. You should avoid calling this in the data callback because it may call into the backend which may or may not be safe. */ MA_API ma_result ma_device_get_info(ma_device* pDevice, ma_device_type type, ma_device_info* pDeviceInfo); /* Retrieves the name of the device. Parameters ---------- pDevice (in) A pointer to the device whose information is being retrieved. type (in) The device type. This parameter is required for duplex devices. When retrieving device information, you are doing so for an individual playback or capture device. pName (out) A pointer to the buffer that will receive the name. nameCap (in) The capacity of the output buffer, including space for the null terminator. pLengthNotIncludingNullTerminator (out, optional) A pointer to the variable that will receive the length of the name, not including the null terminator. Return Value ------------ MA_SUCCESS if successful; any other error code otherwise. Thread Safety ------------- Unsafe. This should be considered unsafe because it may be calling into the backend which may or may not be safe. Callback Safety --------------- Unsafe. You should avoid calling this in the data callback because it may call into the backend which may or may not be safe. Remarks ------- If the name does not fully fit into the output buffer, it'll be truncated. You can pass in NULL to `pName` if you want to first get the length of the name for the purpose of memory allocation of the output buffer. Allocating a buffer of size `MA_MAX_DEVICE_NAME_LENGTH + 1` should be enough for most cases and will avoid the need for the inefficiency of calling this function twice. This is implemented in terms of `ma_device_get_info()`. */ MA_API ma_result ma_device_get_name(ma_device* pDevice, ma_device_type type, char* pName, size_t nameCap, size_t* pLengthNotIncludingNullTerminator); /* Starts the device. For playback devices this begins playback. For capture devices it begins recording. Use `ma_device_stop()` to stop the device. Parameters ---------- pDevice (in) A pointer to the device to start. Return Value ------------ MA_SUCCESS if successful; any other error code otherwise. Thread Safety ------------- Safe. It's safe to call this from any thread with the exception of the callback thread. Callback Safety --------------- Unsafe. It is not safe to call this inside any callback. Remarks ------- For a playback device, this will retrieve an initial chunk of audio data from the client before returning. The reason for this is to ensure there is valid audio data in the buffer, which needs to be done before the device begins playback. This API waits until the backend device has been started for real by the worker thread. It also waits on a mutex for thread-safety. Do not call this in any callback. See Also -------- ma_device_stop() */ MA_API ma_result ma_device_start(ma_device* pDevice); /* Stops the device. For playback devices this stops playback. For capture devices it stops recording. Use `ma_device_start()` to start the device again. Parameters ---------- pDevice (in) A pointer to the device to stop. Return Value ------------ MA_SUCCESS if successful; any other error code otherwise. Thread Safety ------------- Safe. It's safe to call this from any thread with the exception of the callback thread. Callback Safety --------------- Unsafe. It is not safe to call this inside any callback. Doing this will result in a deadlock. Remarks ------- This API needs to wait on the worker thread to stop the backend device properly before returning. It also waits on a mutex for thread-safety. In addition, some backends need to wait for the device to finish playback/recording of the current fragment which can take some time (usually proportionate to the buffer size that was specified at initialization time). Backends are required to either pause the stream in-place or drain the buffer if pausing is not possible. The reason for this is that stopping the device and the resuming it with ma_device_start() (which you might do when your program loses focus) may result in a situation where those samples are never output to the speakers or received from the microphone which can in turn result in de-syncs. Do not call this in any callback. This will be called implicitly by `ma_device_uninit()`. See Also -------- ma_device_start() */ MA_API ma_result ma_device_stop(ma_device* pDevice); /* Determines whether or not the device is started. Parameters ---------- pDevice (in) A pointer to the device whose start state is being retrieved. Return Value ------------ True if the device is started, false otherwise. Thread Safety ------------- Safe. If another thread calls `ma_device_start()` or `ma_device_stop()` at this same time as this function is called, there's a very small chance the return value will be out of sync. Callback Safety --------------- Safe. This is implemented as a simple accessor. See Also -------- ma_device_start() ma_device_stop() */ MA_API ma_bool32 ma_device_is_started(const ma_device* pDevice); /* Retrieves the state of the device. Parameters ---------- pDevice (in) A pointer to the device whose state is being retrieved. Return Value ------------ The current state of the device. The return value will be one of the following: +-------------------------------+------------------------------------------------------------------------------+ | ma_device_state_uninitialized | Will only be returned if the device is in the middle of initialization. | +-------------------------------+------------------------------------------------------------------------------+ | ma_device_state_stopped | The device is stopped. The initial state of the device after initialization. | +-------------------------------+------------------------------------------------------------------------------+ | ma_device_state_started | The device started and requesting and/or delivering audio data. | +-------------------------------+------------------------------------------------------------------------------+ | ma_device_state_starting | The device is in the process of starting. | +-------------------------------+------------------------------------------------------------------------------+ | ma_device_state_stopping | The device is in the process of stopping. | +-------------------------------+------------------------------------------------------------------------------+ Thread Safety ------------- Safe. This is implemented as a simple accessor. Note that if the device is started or stopped at the same time as this function is called, there's a possibility the return value could be out of sync. See remarks. Callback Safety --------------- Safe. This is implemented as a simple accessor. Remarks ------- The general flow of a devices state goes like this: ``` ma_device_init() -> ma_device_state_uninitialized -> ma_device_state_stopped ma_device_start() -> ma_device_state_starting -> ma_device_state_started ma_device_stop() -> ma_device_state_stopping -> ma_device_state_stopped ``` When the state of the device is changed with `ma_device_start()` or `ma_device_stop()` at this same time as this function is called, the value returned by this function could potentially be out of sync. If this is significant to your program you need to implement your own synchronization. */ MA_API ma_device_state ma_device_get_state(const ma_device* pDevice); /* Performs post backend initialization routines for setting up internal data conversion. This should be called whenever the backend is initialized. The only time this should be called from outside of miniaudio is if you're implementing a custom backend, and you would only do it if you are reinitializing the backend due to rerouting or reinitializing for some reason. Parameters ---------- pDevice [in] A pointer to the device. deviceType [in] The type of the device that was just reinitialized. pPlaybackDescriptor [in] The descriptor of the playback device containing the internal data format and buffer sizes. pPlaybackDescriptor [in] The descriptor of the capture device containing the internal data format and buffer sizes. Return Value ------------ MA_SUCCESS if successful; any other error otherwise. Thread Safety ------------- Unsafe. This will be reinitializing internal data converters which may be in use by another thread. Callback Safety --------------- Unsafe. This will be reinitializing internal data converters which may be in use by the callback. Remarks ------- For a duplex device, you can call this for only one side of the system. This is why the deviceType is specified as a parameter rather than deriving it from the device. You do not need to call this manually unless you are doing a custom backend, in which case you need only do it if you're manually performing rerouting or reinitialization. */ MA_API ma_result ma_device_post_init(ma_device* pDevice, ma_device_type deviceType, const ma_device_descriptor* pPlaybackDescriptor, const ma_device_descriptor* pCaptureDescriptor); /* Sets the master volume factor for the device. The volume factor must be between 0 (silence) and 1 (full volume). Use `ma_device_set_master_volume_db()` to use decibel notation, where 0 is full volume and values less than 0 decreases the volume. Parameters ---------- pDevice (in) A pointer to the device whose volume is being set. volume (in) The new volume factor. Must be >= 0. Return Value ------------ MA_SUCCESS if the volume was set successfully. MA_INVALID_ARGS if pDevice is NULL. MA_INVALID_ARGS if volume is negative. Thread Safety ------------- Safe. This just sets a local member of the device object. Callback Safety --------------- Safe. If you set the volume in the data callback, that data written to the output buffer will have the new volume applied. Remarks ------- This applies the volume factor across all channels. This does not change the operating system's volume. It only affects the volume for the given `ma_device` object's audio stream. See Also -------- ma_device_get_master_volume() ma_device_set_master_volume_db() ma_device_get_master_volume_db() */ MA_API ma_result ma_device_set_master_volume(ma_device* pDevice, float volume); /* Retrieves the master volume factor for the device. Parameters ---------- pDevice (in) A pointer to the device whose volume factor is being retrieved. pVolume (in) A pointer to the variable that will receive the volume factor. The returned value will be in the range of [0, 1]. Return Value ------------ MA_SUCCESS if successful. MA_INVALID_ARGS if pDevice is NULL. MA_INVALID_ARGS if pVolume is NULL. Thread Safety ------------- Safe. This just a simple member retrieval. Callback Safety --------------- Safe. Remarks ------- If an error occurs, `*pVolume` will be set to 0. See Also -------- ma_device_set_master_volume() ma_device_set_master_volume_gain_db() ma_device_get_master_volume_gain_db() */ MA_API ma_result ma_device_get_master_volume(ma_device* pDevice, float* pVolume); /* Sets the master volume for the device as gain in decibels. A gain of 0 is full volume, whereas a gain of < 0 will decrease the volume. Parameters ---------- pDevice (in) A pointer to the device whose gain is being set. gainDB (in) The new volume as gain in decibels. Must be less than or equal to 0, where 0 is full volume and anything less than 0 decreases the volume. Return Value ------------ MA_SUCCESS if the volume was set successfully. MA_INVALID_ARGS if pDevice is NULL. MA_INVALID_ARGS if the gain is > 0. Thread Safety ------------- Safe. This just sets a local member of the device object. Callback Safety --------------- Safe. If you set the volume in the data callback, that data written to the output buffer will have the new volume applied. Remarks ------- This applies the gain across all channels. This does not change the operating system's volume. It only affects the volume for the given `ma_device` object's audio stream. See Also -------- ma_device_get_master_volume_gain_db() ma_device_set_master_volume() ma_device_get_master_volume() */ MA_API ma_result ma_device_set_master_volume_db(ma_device* pDevice, float gainDB); /* Retrieves the master gain in decibels. Parameters ---------- pDevice (in) A pointer to the device whose gain is being retrieved. pGainDB (in) A pointer to the variable that will receive the gain in decibels. The returned value will be <= 0. Return Value ------------ MA_SUCCESS if successful. MA_INVALID_ARGS if pDevice is NULL. MA_INVALID_ARGS if pGainDB is NULL. Thread Safety ------------- Safe. This just a simple member retrieval. Callback Safety --------------- Safe. Remarks ------- If an error occurs, `*pGainDB` will be set to 0. See Also -------- ma_device_set_master_volume_db() ma_device_set_master_volume() ma_device_get_master_volume() */ MA_API ma_result ma_device_get_master_volume_db(ma_device* pDevice, float* pGainDB); /* Called from the data callback of asynchronous backends to allow miniaudio to process the data and fire the miniaudio data callback. Parameters ---------- pDevice (in) A pointer to device whose processing the data callback. pOutput (out) A pointer to the buffer that will receive the output PCM frame data. On a playback device this must not be NULL. On a duplex device this can be NULL, in which case pInput must not be NULL. pInput (in) A pointer to the buffer containing input PCM frame data. On a capture device this must not be NULL. On a duplex device this can be NULL, in which case `pOutput` must not be NULL. frameCount (in) The number of frames being processed. Return Value ------------ MA_SUCCESS if successful; any other result code otherwise. Thread Safety ------------- This function should only ever be called from the internal data callback of the backend. It is safe to call this simultaneously between a playback and capture device in duplex setups. Callback Safety --------------- Do not call this from the miniaudio data callback. It should only ever be called from the internal data callback of the backend. Remarks ------- If both `pOutput` and `pInput` are NULL, and error will be returned. In duplex scenarios, both `pOutput` and `pInput` can be non-NULL, in which case `pInput` will be processed first, followed by `pOutput`. If you are implementing a custom backend, and that backend uses a callback for data delivery, you'll need to call this from inside that callback. */ MA_API ma_result ma_device_handle_backend_data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount); /* Calculates an appropriate buffer size from a descriptor, native sample rate and performance profile. This function is used by backends for helping determine an appropriately sized buffer to use with the device depending on the values of `periodSizeInFrames` and `periodSizeInMilliseconds` in the `pDescriptor` object. Since buffer size calculations based on time depends on the sample rate, a best guess at the device's native sample rate is also required which is where `nativeSampleRate` comes in. In addition, the performance profile is also needed for cases where both the period size in frames and milliseconds are both zero. Parameters ---------- pDescriptor (in) A pointer to device descriptor whose `periodSizeInFrames` and `periodSizeInMilliseconds` members will be used for the calculation of the buffer size. nativeSampleRate (in) The device's native sample rate. This is only ever used when the `periodSizeInFrames` member of `pDescriptor` is zero. In this case, `periodSizeInMilliseconds` will be used instead, in which case a sample rate is required to convert to a size in frames. performanceProfile (in) When both the `periodSizeInFrames` and `periodSizeInMilliseconds` members of `pDescriptor` are zero, miniaudio will fall back to a buffer size based on the performance profile. The profile to use for this calculation is determine by this parameter. Return Value ------------ The calculated buffer size in frames. Thread Safety ------------- This is safe so long as nothing modifies `pDescriptor` at the same time. However, this function should only ever be called from within the backend's device initialization routine and therefore shouldn't have any multithreading concerns. Callback Safety --------------- This is safe to call within the data callback, but there is no reason to ever do this. Remarks ------- If `nativeSampleRate` is zero, this function will fall back to `pDescriptor->sampleRate`. If that is also zero, `MA_DEFAULT_SAMPLE_RATE` will be used instead. */ MA_API ma_uint32 ma_calculate_buffer_size_in_frames_from_descriptor(const ma_device_descriptor* pDescriptor, ma_uint32 nativeSampleRate, ma_performance_profile performanceProfile); /* Retrieves a friendly name for a backend. */ MA_API const char* ma_get_backend_name(ma_backend backend); /* Determines whether or not the given backend is available by the compilation environment. */ MA_API ma_bool32 ma_is_backend_enabled(ma_backend backend); /* Retrieves compile-time enabled backends. Parameters ---------- pBackends (out, optional) A pointer to the buffer that will receive the enabled backends. Set to NULL to retrieve the backend count. Setting the capacity of the buffer to `MA_BUFFER_COUNT` will guarantee it's large enough for all backends. backendCap (in) The capacity of the `pBackends` buffer. pBackendCount (out) A pointer to the variable that will receive the enabled backend count. Return Value ------------ MA_SUCCESS if successful. MA_INVALID_ARGS if `pBackendCount` is NULL. MA_NO_SPACE if the capacity of `pBackends` is not large enough. If `MA_NO_SPACE` is returned, the `pBackends` buffer will be filled with `*pBackendCount` values. Thread Safety ------------- Safe. Callback Safety --------------- Safe. Remarks ------- If you want to retrieve the number of backends so you can determine the capacity of `pBackends` buffer, you can call this function with `pBackends` set to NULL. This will also enumerate the null backend. If you don't want to include this you need to check for `ma_backend_null` when you enumerate over the returned backends and handle it appropriately. Alternatively, you can disable it at compile time with `MA_NO_NULL`. The returned backends are determined based on compile time settings, not the platform it's currently running on. For example, PulseAudio will be returned if it was enabled at compile time, even when the user doesn't actually have PulseAudio installed. Example 1 --------- The example below retrieves the enabled backend count using a fixed sized buffer allocated on the stack. The buffer is given a capacity of `MA_BACKEND_COUNT` which will guarantee it'll be large enough to store all available backends. Since `MA_BACKEND_COUNT` is always a relatively small value, this should be suitable for most scenarios. ``` ma_backend enabledBackends[MA_BACKEND_COUNT]; size_t enabledBackendCount; result = ma_get_enabled_backends(enabledBackends, MA_BACKEND_COUNT, &enabledBackendCount); if (result != MA_SUCCESS) { // Failed to retrieve enabled backends. Should never happen in this example since all inputs are valid. } ``` See Also -------- ma_is_backend_enabled() */ MA_API ma_result ma_get_enabled_backends(ma_backend* pBackends, size_t backendCap, size_t* pBackendCount); /* Determines whether or not loopback mode is support by a backend. */ MA_API ma_bool32 ma_is_loopback_supported(ma_backend backend); #endif /* MA_NO_DEVICE_IO */ /************************************************************************************************************************************************************ Utiltities ************************************************************************************************************************************************************/ /* Calculates a buffer size in milliseconds from the specified number of frames and sample rate. */ MA_API ma_uint32 ma_calculate_buffer_size_in_milliseconds_from_frames(ma_uint32 bufferSizeInFrames, ma_uint32 sampleRate); /* Calculates a buffer size in frames from the specified number of milliseconds and sample rate. */ MA_API ma_uint32 ma_calculate_buffer_size_in_frames_from_milliseconds(ma_uint32 bufferSizeInMilliseconds, ma_uint32 sampleRate); /* Copies PCM frames from one buffer to another. */ MA_API void ma_copy_pcm_frames(void* dst, const void* src, ma_uint64 frameCount, ma_format format, ma_uint32 channels); /* Copies silent frames into the given buffer. Remarks ------- For all formats except `ma_format_u8`, the output buffer will be filled with 0. For `ma_format_u8` it will be filled with 128. The reason for this is that it makes more sense for the purpose of mixing to initialize it to the center point. */ MA_API void ma_silence_pcm_frames(void* p, ma_uint64 frameCount, ma_format format, ma_uint32 channels); /* Offsets a pointer by the specified number of PCM frames. */ MA_API void* ma_offset_pcm_frames_ptr(void* p, ma_uint64 offsetInFrames, ma_format format, ma_uint32 channels); MA_API const void* ma_offset_pcm_frames_const_ptr(const void* p, ma_uint64 offsetInFrames, ma_format format, ma_uint32 channels); static MA_INLINE float* ma_offset_pcm_frames_ptr_f32(float* p, ma_uint64 offsetInFrames, ma_uint32 channels) { return (float*)ma_offset_pcm_frames_ptr((void*)p, offsetInFrames, ma_format_f32, channels); } static MA_INLINE const float* ma_offset_pcm_frames_const_ptr_f32(const float* p, ma_uint64 offsetInFrames, ma_uint32 channels) { return (const float*)ma_offset_pcm_frames_const_ptr((const void*)p, offsetInFrames, ma_format_f32, channels); } /* Clips samples. */ MA_API void ma_clip_samples_u8(ma_uint8* pDst, const ma_int16* pSrc, ma_uint64 count); MA_API void ma_clip_samples_s16(ma_int16* pDst, const ma_int32* pSrc, ma_uint64 count); MA_API void ma_clip_samples_s24(ma_uint8* pDst, const ma_int64* pSrc, ma_uint64 count); MA_API void ma_clip_samples_s32(ma_int32* pDst, const ma_int64* pSrc, ma_uint64 count); MA_API void ma_clip_samples_f32(float* pDst, const float* pSrc, ma_uint64 count); MA_API void ma_clip_pcm_frames(void* pDst, const void* pSrc, ma_uint64 frameCount, ma_format format, ma_uint32 channels); /* Helper for applying a volume factor to samples. Note that the source and destination buffers can be the same, in which case it'll perform the operation in-place. */ MA_API void ma_copy_and_apply_volume_factor_u8(ma_uint8* pSamplesOut, const ma_uint8* pSamplesIn, ma_uint64 sampleCount, float factor); MA_API void ma_copy_and_apply_volume_factor_s16(ma_int16* pSamplesOut, const ma_int16* pSamplesIn, ma_uint64 sampleCount, float factor); MA_API void ma_copy_and_apply_volume_factor_s24(void* pSamplesOut, const void* pSamplesIn, ma_uint64 sampleCount, float factor); MA_API void ma_copy_and_apply_volume_factor_s32(ma_int32* pSamplesOut, const ma_int32* pSamplesIn, ma_uint64 sampleCount, float factor); MA_API void ma_copy_and_apply_volume_factor_f32(float* pSamplesOut, const float* pSamplesIn, ma_uint64 sampleCount, float factor); MA_API void ma_apply_volume_factor_u8(ma_uint8* pSamples, ma_uint64 sampleCount, float factor); MA_API void ma_apply_volume_factor_s16(ma_int16* pSamples, ma_uint64 sampleCount, float factor); MA_API void ma_apply_volume_factor_s24(void* pSamples, ma_uint64 sampleCount, float factor); MA_API void ma_apply_volume_factor_s32(ma_int32* pSamples, ma_uint64 sampleCount, float factor); MA_API void ma_apply_volume_factor_f32(float* pSamples, ma_uint64 sampleCount, float factor); MA_API void ma_copy_and_apply_volume_factor_pcm_frames_u8(ma_uint8* pFramesOut, const ma_uint8* pFramesIn, ma_uint64 frameCount, ma_uint32 channels, float factor); MA_API void ma_copy_and_apply_volume_factor_pcm_frames_s16(ma_int16* pFramesOut, const ma_int16* pFramesIn, ma_uint64 frameCount, ma_uint32 channels, float factor); MA_API void ma_copy_and_apply_volume_factor_pcm_frames_s24(void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount, ma_uint32 channels, float factor); MA_API void ma_copy_and_apply_volume_factor_pcm_frames_s32(ma_int32* pFramesOut, const ma_int32* pFramesIn, ma_uint64 frameCount, ma_uint32 channels, float factor); MA_API void ma_copy_and_apply_volume_factor_pcm_frames_f32(float* pFramesOut, const float* pFramesIn, ma_uint64 frameCount, ma_uint32 channels, float factor); MA_API void ma_copy_and_apply_volume_factor_pcm_frames(void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount, ma_format format, ma_uint32 channels, float factor); MA_API void ma_apply_volume_factor_pcm_frames_u8(ma_uint8* pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor); MA_API void ma_apply_volume_factor_pcm_frames_s16(ma_int16* pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor); MA_API void ma_apply_volume_factor_pcm_frames_s24(void* pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor); MA_API void ma_apply_volume_factor_pcm_frames_s32(ma_int32* pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor); MA_API void ma_apply_volume_factor_pcm_frames_f32(float* pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor); MA_API void ma_apply_volume_factor_pcm_frames(void* pFrames, ma_uint64 frameCount, ma_format format, ma_uint32 channels, float factor); MA_API void ma_copy_and_apply_volume_factor_per_channel_f32(float* pFramesOut, const float* pFramesIn, ma_uint64 frameCount, ma_uint32 channels, float* pChannelGains); MA_API void ma_copy_and_apply_volume_and_clip_samples_u8(ma_uint8* pDst, const ma_int16* pSrc, ma_uint64 count, float volume); MA_API void ma_copy_and_apply_volume_and_clip_samples_s16(ma_int16* pDst, const ma_int32* pSrc, ma_uint64 count, float volume); MA_API void ma_copy_and_apply_volume_and_clip_samples_s24(ma_uint8* pDst, const ma_int64* pSrc, ma_uint64 count, float volume); MA_API void ma_copy_and_apply_volume_and_clip_samples_s32(ma_int32* pDst, const ma_int64* pSrc, ma_uint64 count, float volume); MA_API void ma_copy_and_apply_volume_and_clip_samples_f32(float* pDst, const float* pSrc, ma_uint64 count, float volume); MA_API void ma_copy_and_apply_volume_and_clip_pcm_frames(void* pDst, const void* pSrc, ma_uint64 frameCount, ma_format format, ma_uint32 channels, float volume); /* Helper for converting a linear factor to gain in decibels. */ MA_API float ma_volume_linear_to_db(float factor); /* Helper for converting gain in decibels to a linear factor. */ MA_API float ma_volume_db_to_linear(float gain); /************************************************************************************************** Data Source **************************************************************************************************/ typedef void ma_data_source; #define MA_DATA_SOURCE_SELF_MANAGED_RANGE_AND_LOOP_POINT 0x00000001 typedef struct { ma_result (* onRead)(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); ma_result (* onSeek)(ma_data_source* pDataSource, ma_uint64 frameIndex); ma_result (* onGetDataFormat)(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap); ma_result (* onGetCursor)(ma_data_source* pDataSource, ma_uint64* pCursor); ma_result (* onGetLength)(ma_data_source* pDataSource, ma_uint64* pLength); ma_result (* onSetLooping)(ma_data_source* pDataSource, ma_bool32 isLooping); ma_uint32 flags; } ma_data_source_vtable; typedef ma_data_source* (* ma_data_source_get_next_proc)(ma_data_source* pDataSource); typedef struct { const ma_data_source_vtable* vtable; } ma_data_source_config; MA_API ma_data_source_config ma_data_source_config_init(void); typedef struct { const ma_data_source_vtable* vtable; ma_uint64 rangeBegInFrames; ma_uint64 rangeEndInFrames; /* Set to -1 for unranged (default). */ ma_uint64 loopBegInFrames; /* Relative to rangeBegInFrames. */ ma_uint64 loopEndInFrames; /* Relative to rangeBegInFrames. Set to -1 for the end of the range. */ ma_data_source* pCurrent; /* When non-NULL, the data source being initialized will act as a proxy and will route all operations to pCurrent. Used in conjunction with pNext/onGetNext for seamless chaining. */ ma_data_source* pNext; /* When set to NULL, onGetNext will be used. */ ma_data_source_get_next_proc onGetNext; /* Will be used when pNext is NULL. If both are NULL, no next will be used. */ MA_ATOMIC(4, ma_bool32) isLooping; } ma_data_source_base; MA_API ma_result ma_data_source_init(const ma_data_source_config* pConfig, ma_data_source* pDataSource); MA_API void ma_data_source_uninit(ma_data_source* pDataSource); MA_API ma_result ma_data_source_read_pcm_frames(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); /* Must support pFramesOut = NULL in which case a forward seek should be performed. */ MA_API ma_result ma_data_source_seek_pcm_frames(ma_data_source* pDataSource, ma_uint64 frameCount, ma_uint64* pFramesSeeked); /* Can only seek forward. Equivalent to ma_data_source_read_pcm_frames(pDataSource, NULL, frameCount, &framesRead); */ MA_API ma_result ma_data_source_seek_to_pcm_frame(ma_data_source* pDataSource, ma_uint64 frameIndex); MA_API ma_result ma_data_source_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap); MA_API ma_result ma_data_source_get_cursor_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pCursor); MA_API ma_result ma_data_source_get_length_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pLength); /* Returns MA_NOT_IMPLEMENTED if the length is unknown or cannot be determined. Decoders can return this. */ MA_API ma_result ma_data_source_get_cursor_in_seconds(ma_data_source* pDataSource, float* pCursor); MA_API ma_result ma_data_source_get_length_in_seconds(ma_data_source* pDataSource, float* pLength); MA_API ma_result ma_data_source_set_looping(ma_data_source* pDataSource, ma_bool32 isLooping); MA_API ma_bool32 ma_data_source_is_looping(const ma_data_source* pDataSource); MA_API ma_result ma_data_source_set_range_in_pcm_frames(ma_data_source* pDataSource, ma_uint64 rangeBegInFrames, ma_uint64 rangeEndInFrames); MA_API void ma_data_source_get_range_in_pcm_frames(const ma_data_source* pDataSource, ma_uint64* pRangeBegInFrames, ma_uint64* pRangeEndInFrames); MA_API ma_result ma_data_source_set_loop_point_in_pcm_frames(ma_data_source* pDataSource, ma_uint64 loopBegInFrames, ma_uint64 loopEndInFrames); MA_API void ma_data_source_get_loop_point_in_pcm_frames(const ma_data_source* pDataSource, ma_uint64* pLoopBegInFrames, ma_uint64* pLoopEndInFrames); MA_API ma_result ma_data_source_set_current(ma_data_source* pDataSource, ma_data_source* pCurrentDataSource); MA_API ma_data_source* ma_data_source_get_current(const ma_data_source* pDataSource); MA_API ma_result ma_data_source_set_next(ma_data_source* pDataSource, ma_data_source* pNextDataSource); MA_API ma_data_source* ma_data_source_get_next(const ma_data_source* pDataSource); MA_API ma_result ma_data_source_set_next_callback(ma_data_source* pDataSource, ma_data_source_get_next_proc onGetNext); MA_API ma_data_source_get_next_proc ma_data_source_get_next_callback(const ma_data_source* pDataSource); typedef struct { ma_data_source_base ds; ma_format format; ma_uint32 channels; ma_uint32 sampleRate; ma_uint64 cursor; ma_uint64 sizeInFrames; const void* pData; } ma_audio_buffer_ref; MA_API ma_result ma_audio_buffer_ref_init(ma_format format, ma_uint32 channels, const void* pData, ma_uint64 sizeInFrames, ma_audio_buffer_ref* pAudioBufferRef); MA_API void ma_audio_buffer_ref_uninit(ma_audio_buffer_ref* pAudioBufferRef); MA_API ma_result ma_audio_buffer_ref_set_data(ma_audio_buffer_ref* pAudioBufferRef, const void* pData, ma_uint64 sizeInFrames); MA_API ma_uint64 ma_audio_buffer_ref_read_pcm_frames(ma_audio_buffer_ref* pAudioBufferRef, void* pFramesOut, ma_uint64 frameCount, ma_bool32 loop); MA_API ma_result ma_audio_buffer_ref_seek_to_pcm_frame(ma_audio_buffer_ref* pAudioBufferRef, ma_uint64 frameIndex); MA_API ma_result ma_audio_buffer_ref_map(ma_audio_buffer_ref* pAudioBufferRef, void** ppFramesOut, ma_uint64* pFrameCount); MA_API ma_result ma_audio_buffer_ref_unmap(ma_audio_buffer_ref* pAudioBufferRef, ma_uint64 frameCount); /* Returns MA_AT_END if the end has been reached. This should be considered successful. */ MA_API ma_bool32 ma_audio_buffer_ref_at_end(const ma_audio_buffer_ref* pAudioBufferRef); MA_API ma_result ma_audio_buffer_ref_get_cursor_in_pcm_frames(const ma_audio_buffer_ref* pAudioBufferRef, ma_uint64* pCursor); MA_API ma_result ma_audio_buffer_ref_get_length_in_pcm_frames(const ma_audio_buffer_ref* pAudioBufferRef, ma_uint64* pLength); MA_API ma_result ma_audio_buffer_ref_get_available_frames(const ma_audio_buffer_ref* pAudioBufferRef, ma_uint64* pAvailableFrames); typedef struct { ma_format format; ma_uint32 channels; ma_uint32 sampleRate; ma_uint64 sizeInFrames; const void* pData; /* If set to NULL, will allocate a block of memory for you. */ ma_allocation_callbacks allocationCallbacks; } ma_audio_buffer_config; MA_API ma_audio_buffer_config ma_audio_buffer_config_init(ma_format format, ma_uint32 channels, ma_uint64 sizeInFrames, const void* pData, const ma_allocation_callbacks* pAllocationCallbacks); typedef struct { ma_audio_buffer_ref ref; ma_allocation_callbacks allocationCallbacks; ma_bool32 ownsData; /* Used to control whether or not miniaudio owns the data buffer. If set to true, pData will be freed in ma_audio_buffer_uninit(). */ ma_uint8 _pExtraData[1]; /* For allocating a buffer with the memory located directly after the other memory of the structure. */ } ma_audio_buffer; MA_API ma_result ma_audio_buffer_init(const ma_audio_buffer_config* pConfig, ma_audio_buffer* pAudioBuffer); MA_API ma_result ma_audio_buffer_init_copy(const ma_audio_buffer_config* pConfig, ma_audio_buffer* pAudioBuffer); MA_API ma_result ma_audio_buffer_alloc_and_init(const ma_audio_buffer_config* pConfig, ma_audio_buffer** ppAudioBuffer); /* Always copies the data. Doesn't make sense to use this otherwise. Use ma_audio_buffer_uninit_and_free() to uninit. */ MA_API void ma_audio_buffer_uninit(ma_audio_buffer* pAudioBuffer); MA_API void ma_audio_buffer_uninit_and_free(ma_audio_buffer* pAudioBuffer); MA_API ma_uint64 ma_audio_buffer_read_pcm_frames(ma_audio_buffer* pAudioBuffer, void* pFramesOut, ma_uint64 frameCount, ma_bool32 loop); MA_API ma_result ma_audio_buffer_seek_to_pcm_frame(ma_audio_buffer* pAudioBuffer, ma_uint64 frameIndex); MA_API ma_result ma_audio_buffer_map(ma_audio_buffer* pAudioBuffer, void** ppFramesOut, ma_uint64* pFrameCount); MA_API ma_result ma_audio_buffer_unmap(ma_audio_buffer* pAudioBuffer, ma_uint64 frameCount); /* Returns MA_AT_END if the end has been reached. This should be considered successful. */ MA_API ma_bool32 ma_audio_buffer_at_end(const ma_audio_buffer* pAudioBuffer); MA_API ma_result ma_audio_buffer_get_cursor_in_pcm_frames(const ma_audio_buffer* pAudioBuffer, ma_uint64* pCursor); MA_API ma_result ma_audio_buffer_get_length_in_pcm_frames(const ma_audio_buffer* pAudioBuffer, ma_uint64* pLength); MA_API ma_result ma_audio_buffer_get_available_frames(const ma_audio_buffer* pAudioBuffer, ma_uint64* pAvailableFrames); /* Paged Audio Buffer ================== A paged audio buffer is made up of a linked list of pages. It's expandable, but not shrinkable. It can be used for cases where audio data is streamed in asynchronously while allowing data to be read at the same time. This is lock-free, but not 100% thread safe. You can append a page and read from the buffer across simultaneously across different threads, however only one thread at a time can append, and only one thread at a time can read and seek. */ typedef struct ma_paged_audio_buffer_page ma_paged_audio_buffer_page; struct ma_paged_audio_buffer_page { MA_ATOMIC(MA_SIZEOF_PTR, ma_paged_audio_buffer_page*) pNext; ma_uint64 sizeInFrames; ma_uint8 pAudioData[1]; }; typedef struct { ma_format format; ma_uint32 channels; ma_paged_audio_buffer_page head; /* Dummy head for the lock-free algorithm. Always has a size of 0. */ MA_ATOMIC(MA_SIZEOF_PTR, ma_paged_audio_buffer_page*) pTail; /* Never null. Initially set to &head. */ } ma_paged_audio_buffer_data; MA_API ma_result ma_paged_audio_buffer_data_init(ma_format format, ma_uint32 channels, ma_paged_audio_buffer_data* pData); MA_API void ma_paged_audio_buffer_data_uninit(ma_paged_audio_buffer_data* pData, const ma_allocation_callbacks* pAllocationCallbacks); MA_API ma_paged_audio_buffer_page* ma_paged_audio_buffer_data_get_head(ma_paged_audio_buffer_data* pData); MA_API ma_paged_audio_buffer_page* ma_paged_audio_buffer_data_get_tail(ma_paged_audio_buffer_data* pData); MA_API ma_result ma_paged_audio_buffer_data_get_length_in_pcm_frames(ma_paged_audio_buffer_data* pData, ma_uint64* pLength); MA_API ma_result ma_paged_audio_buffer_data_allocate_page(ma_paged_audio_buffer_data* pData, ma_uint64 pageSizeInFrames, const void* pInitialData, const ma_allocation_callbacks* pAllocationCallbacks, ma_paged_audio_buffer_page** ppPage); MA_API ma_result ma_paged_audio_buffer_data_free_page(ma_paged_audio_buffer_data* pData, ma_paged_audio_buffer_page* pPage, const ma_allocation_callbacks* pAllocationCallbacks); MA_API ma_result ma_paged_audio_buffer_data_append_page(ma_paged_audio_buffer_data* pData, ma_paged_audio_buffer_page* pPage); MA_API ma_result ma_paged_audio_buffer_data_allocate_and_append_page(ma_paged_audio_buffer_data* pData, ma_uint32 pageSizeInFrames, const void* pInitialData, const ma_allocation_callbacks* pAllocationCallbacks); typedef struct { ma_paged_audio_buffer_data* pData; /* Must not be null. */ } ma_paged_audio_buffer_config; MA_API ma_paged_audio_buffer_config ma_paged_audio_buffer_config_init(ma_paged_audio_buffer_data* pData); typedef struct { ma_data_source_base ds; ma_paged_audio_buffer_data* pData; /* Audio data is read from here. Cannot be null. */ ma_paged_audio_buffer_page* pCurrent; ma_uint64 relativeCursor; /* Relative to the current page. */ ma_uint64 absoluteCursor; } ma_paged_audio_buffer; MA_API ma_result ma_paged_audio_buffer_init(const ma_paged_audio_buffer_config* pConfig, ma_paged_audio_buffer* pPagedAudioBuffer); MA_API void ma_paged_audio_buffer_uninit(ma_paged_audio_buffer* pPagedAudioBuffer); MA_API ma_result ma_paged_audio_buffer_read_pcm_frames(ma_paged_audio_buffer* pPagedAudioBuffer, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); /* Returns MA_AT_END if no more pages available. */ MA_API ma_result ma_paged_audio_buffer_seek_to_pcm_frame(ma_paged_audio_buffer* pPagedAudioBuffer, ma_uint64 frameIndex); MA_API ma_result ma_paged_audio_buffer_get_cursor_in_pcm_frames(ma_paged_audio_buffer* pPagedAudioBuffer, ma_uint64* pCursor); MA_API ma_result ma_paged_audio_buffer_get_length_in_pcm_frames(ma_paged_audio_buffer* pPagedAudioBuffer, ma_uint64* pLength); /************************************************************************************************************************************************************ VFS === The VFS object (virtual file system) is what's used to customize file access. This is useful in cases where stdio FILE* based APIs may not be entirely appropriate for a given situation. ************************************************************************************************************************************************************/ typedef void ma_vfs; typedef ma_handle ma_vfs_file; typedef enum { MA_OPEN_MODE_READ = 0x00000001, MA_OPEN_MODE_WRITE = 0x00000002 } ma_open_mode_flags; typedef enum { ma_seek_origin_start, ma_seek_origin_current, ma_seek_origin_end /* Not used by decoders. */ } ma_seek_origin; typedef struct { ma_uint64 sizeInBytes; } ma_file_info; typedef struct { ma_result (* onOpen) (ma_vfs* pVFS, const char* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile); ma_result (* onOpenW)(ma_vfs* pVFS, const wchar_t* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile); ma_result (* onClose)(ma_vfs* pVFS, ma_vfs_file file); ma_result (* onRead) (ma_vfs* pVFS, ma_vfs_file file, void* pDst, size_t sizeInBytes, size_t* pBytesRead); ma_result (* onWrite)(ma_vfs* pVFS, ma_vfs_file file, const void* pSrc, size_t sizeInBytes, size_t* pBytesWritten); ma_result (* onSeek) (ma_vfs* pVFS, ma_vfs_file file, ma_int64 offset, ma_seek_origin origin); ma_result (* onTell) (ma_vfs* pVFS, ma_vfs_file file, ma_int64* pCursor); ma_result (* onInfo) (ma_vfs* pVFS, ma_vfs_file file, ma_file_info* pInfo); } ma_vfs_callbacks; MA_API ma_result ma_vfs_open(ma_vfs* pVFS, const char* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile); MA_API ma_result ma_vfs_open_w(ma_vfs* pVFS, const wchar_t* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile); MA_API ma_result ma_vfs_close(ma_vfs* pVFS, ma_vfs_file file); MA_API ma_result ma_vfs_read(ma_vfs* pVFS, ma_vfs_file file, void* pDst, size_t sizeInBytes, size_t* pBytesRead); MA_API ma_result ma_vfs_write(ma_vfs* pVFS, ma_vfs_file file, const void* pSrc, size_t sizeInBytes, size_t* pBytesWritten); MA_API ma_result ma_vfs_seek(ma_vfs* pVFS, ma_vfs_file file, ma_int64 offset, ma_seek_origin origin); MA_API ma_result ma_vfs_tell(ma_vfs* pVFS, ma_vfs_file file, ma_int64* pCursor); MA_API ma_result ma_vfs_info(ma_vfs* pVFS, ma_vfs_file file, ma_file_info* pInfo); MA_API ma_result ma_vfs_open_and_read_file(ma_vfs* pVFS, const char* pFilePath, void** ppData, size_t* pSize, const ma_allocation_callbacks* pAllocationCallbacks); typedef struct { ma_vfs_callbacks cb; ma_allocation_callbacks allocationCallbacks; /* Only used for the wchar_t version of open() on non-Windows platforms. */ } ma_default_vfs; MA_API ma_result ma_default_vfs_init(ma_default_vfs* pVFS, const ma_allocation_callbacks* pAllocationCallbacks); typedef ma_result (* ma_read_proc)(void* pUserData, void* pBufferOut, size_t bytesToRead, size_t* pBytesRead); typedef ma_result (* ma_seek_proc)(void* pUserData, ma_int64 offset, ma_seek_origin origin); typedef ma_result (* ma_tell_proc)(void* pUserData, ma_int64* pCursor); #if !defined(MA_NO_DECODING) || !defined(MA_NO_ENCODING) typedef enum { ma_encoding_format_unknown = 0, ma_encoding_format_wav, ma_encoding_format_flac, ma_encoding_format_mp3, ma_encoding_format_vorbis } ma_encoding_format; #endif /************************************************************************************************************************************************************ Decoding ======== Decoders are independent of the main device API. Decoding APIs can be called freely inside the device's data callback, but they are not thread safe unless you do your own synchronization. ************************************************************************************************************************************************************/ #ifndef MA_NO_DECODING typedef struct ma_decoder ma_decoder; typedef struct { ma_format preferredFormat; ma_uint32 seekPointCount; /* Set to > 0 to generate a seektable if the decoding backend supports it. */ } ma_decoding_backend_config; MA_API ma_decoding_backend_config ma_decoding_backend_config_init(ma_format preferredFormat, ma_uint32 seekPointCount); typedef struct { ma_result (* onInit )(void* pUserData, ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend); ma_result (* onInitFile )(void* pUserData, const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend); /* Optional. */ ma_result (* onInitFileW )(void* pUserData, const wchar_t* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend); /* Optional. */ ma_result (* onInitMemory)(void* pUserData, const void* pData, size_t dataSize, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend); /* Optional. */ void (* onUninit )(void* pUserData, ma_data_source* pBackend, const ma_allocation_callbacks* pAllocationCallbacks); } ma_decoding_backend_vtable; typedef ma_result (* ma_decoder_read_proc)(ma_decoder* pDecoder, void* pBufferOut, size_t bytesToRead, size_t* pBytesRead); /* Returns the number of bytes read. */ typedef ma_result (* ma_decoder_seek_proc)(ma_decoder* pDecoder, ma_int64 byteOffset, ma_seek_origin origin); typedef ma_result (* ma_decoder_tell_proc)(ma_decoder* pDecoder, ma_int64* pCursor); typedef struct { ma_format format; /* Set to 0 or ma_format_unknown to use the stream's internal format. */ ma_uint32 channels; /* Set to 0 to use the stream's internal channels. */ ma_uint32 sampleRate; /* Set to 0 to use the stream's internal sample rate. */ ma_channel* pChannelMap; ma_channel_mix_mode channelMixMode; ma_dither_mode ditherMode; ma_resampler_config resampling; ma_allocation_callbacks allocationCallbacks; ma_encoding_format encodingFormat; ma_uint32 seekPointCount; /* When set to > 0, specifies the number of seek points to use for the generation of a seek table. Not all decoding backends support this. */ ma_decoding_backend_vtable** ppCustomBackendVTables; ma_uint32 customBackendCount; void* pCustomBackendUserData; } ma_decoder_config; struct ma_decoder { ma_data_source_base ds; ma_data_source* pBackend; /* The decoding backend we'll be pulling data from. */ const ma_decoding_backend_vtable* pBackendVTable; /* The vtable for the decoding backend. This needs to be stored so we can access the onUninit() callback. */ void* pBackendUserData; ma_decoder_read_proc onRead; ma_decoder_seek_proc onSeek; ma_decoder_tell_proc onTell; void* pUserData; ma_uint64 readPointerInPCMFrames; /* In output sample rate. Used for keeping track of how many frames are available for decoding. */ ma_format outputFormat; ma_uint32 outputChannels; ma_uint32 outputSampleRate; ma_data_converter converter; /* Data conversion is achieved by running frames through this. */ void* pInputCache; /* In input format. Can be null if it's not needed. */ ma_uint64 inputCacheCap; /* The capacity of the input cache. */ ma_uint64 inputCacheConsumed; /* The number of frames that have been consumed in the cache. Used for determining the next valid frame. */ ma_uint64 inputCacheRemaining; /* The number of valid frames remaining in the cahce. */ ma_allocation_callbacks allocationCallbacks; union { struct { ma_vfs* pVFS; ma_vfs_file file; } vfs; struct { const ma_uint8* pData; size_t dataSize; size_t currentReadPos; } memory; /* Only used for decoders that were opened against a block of memory. */ } data; }; MA_API ma_decoder_config ma_decoder_config_init(ma_format outputFormat, ma_uint32 outputChannels, ma_uint32 outputSampleRate); MA_API ma_decoder_config ma_decoder_config_init_default(void); MA_API ma_result ma_decoder_init(ma_decoder_read_proc onRead, ma_decoder_seek_proc onSeek, void* pUserData, const ma_decoder_config* pConfig, ma_decoder* pDecoder); MA_API ma_result ma_decoder_init_memory(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder); MA_API ma_result ma_decoder_init_vfs(ma_vfs* pVFS, const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder); MA_API ma_result ma_decoder_init_vfs_w(ma_vfs* pVFS, const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder); MA_API ma_result ma_decoder_init_file(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder); MA_API ma_result ma_decoder_init_file_w(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder); /* Uninitializes a decoder. */ MA_API ma_result ma_decoder_uninit(ma_decoder* pDecoder); /* Reads PCM frames from the given decoder. This is not thread safe without your own synchronization. */ MA_API ma_result ma_decoder_read_pcm_frames(ma_decoder* pDecoder, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); /* Seeks to a PCM frame based on it's absolute index. This is not thread safe without your own synchronization. */ MA_API ma_result ma_decoder_seek_to_pcm_frame(ma_decoder* pDecoder, ma_uint64 frameIndex); /* Retrieves the decoder's output data format. */ MA_API ma_result ma_decoder_get_data_format(ma_decoder* pDecoder, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap); /* Retrieves the current position of the read cursor in PCM frames. */ MA_API ma_result ma_decoder_get_cursor_in_pcm_frames(ma_decoder* pDecoder, ma_uint64* pCursor); /* Retrieves the length of the decoder in PCM frames. Do not call this on streams of an undefined length, such as internet radio. If the length is unknown or an error occurs, 0 will be returned. This will always return 0 for Vorbis decoders. This is due to a limitation with stb_vorbis in push mode which is what miniaudio uses internally. For MP3's, this will decode the entire file. Do not call this in time critical scenarios. This function is not thread safe without your own synchronization. */ MA_API ma_result ma_decoder_get_length_in_pcm_frames(ma_decoder* pDecoder, ma_uint64* pLength); /* Retrieves the number of frames that can be read before reaching the end. This calls `ma_decoder_get_length_in_pcm_frames()` so you need to be aware of the rules for that function, in particular ensuring you do not call it on streams of an undefined length, such as internet radio. If the total length of the decoder cannot be retrieved, such as with Vorbis decoders, `MA_NOT_IMPLEMENTED` will be returned. */ MA_API ma_result ma_decoder_get_available_frames(ma_decoder* pDecoder, ma_uint64* pAvailableFrames); /* Helper for opening and decoding a file into a heap allocated block of memory. Free the returned pointer with ma_free(). On input, pConfig should be set to what you want. On output it will be set to what you got. */ MA_API ma_result ma_decode_from_vfs(ma_vfs* pVFS, const char* pFilePath, ma_decoder_config* pConfig, ma_uint64* pFrameCountOut, void** ppPCMFramesOut); MA_API ma_result ma_decode_file(const char* pFilePath, ma_decoder_config* pConfig, ma_uint64* pFrameCountOut, void** ppPCMFramesOut); MA_API ma_result ma_decode_memory(const void* pData, size_t dataSize, ma_decoder_config* pConfig, ma_uint64* pFrameCountOut, void** ppPCMFramesOut); #endif /* MA_NO_DECODING */ /************************************************************************************************************************************************************ Encoding ======== Encoders do not perform any format conversion for you. If your target format does not support the format, and error will be returned. ************************************************************************************************************************************************************/ #ifndef MA_NO_ENCODING typedef struct ma_encoder ma_encoder; typedef ma_result (* ma_encoder_write_proc) (ma_encoder* pEncoder, const void* pBufferIn, size_t bytesToWrite, size_t* pBytesWritten); typedef ma_result (* ma_encoder_seek_proc) (ma_encoder* pEncoder, ma_int64 offset, ma_seek_origin origin); typedef ma_result (* ma_encoder_init_proc) (ma_encoder* pEncoder); typedef void (* ma_encoder_uninit_proc) (ma_encoder* pEncoder); typedef ma_result (* ma_encoder_write_pcm_frames_proc)(ma_encoder* pEncoder, const void* pFramesIn, ma_uint64 frameCount, ma_uint64* pFramesWritten); typedef struct { ma_encoding_format encodingFormat; ma_format format; ma_uint32 channels; ma_uint32 sampleRate; ma_allocation_callbacks allocationCallbacks; } ma_encoder_config; MA_API ma_encoder_config ma_encoder_config_init(ma_encoding_format encodingFormat, ma_format format, ma_uint32 channels, ma_uint32 sampleRate); struct ma_encoder { ma_encoder_config config; ma_encoder_write_proc onWrite; ma_encoder_seek_proc onSeek; ma_encoder_init_proc onInit; ma_encoder_uninit_proc onUninit; ma_encoder_write_pcm_frames_proc onWritePCMFrames; void* pUserData; void* pInternalEncoder; /* <-- The drwav/drflac/stb_vorbis/etc. objects. */ union { struct { ma_vfs* pVFS; ma_vfs_file file; } vfs; } data; }; MA_API ma_result ma_encoder_init(ma_encoder_write_proc onWrite, ma_encoder_seek_proc onSeek, void* pUserData, const ma_encoder_config* pConfig, ma_encoder* pEncoder); MA_API ma_result ma_encoder_init_vfs(ma_vfs* pVFS, const char* pFilePath, const ma_encoder_config* pConfig, ma_encoder* pEncoder); MA_API ma_result ma_encoder_init_vfs_w(ma_vfs* pVFS, const wchar_t* pFilePath, const ma_encoder_config* pConfig, ma_encoder* pEncoder); MA_API ma_result ma_encoder_init_file(const char* pFilePath, const ma_encoder_config* pConfig, ma_encoder* pEncoder); MA_API ma_result ma_encoder_init_file_w(const wchar_t* pFilePath, const ma_encoder_config* pConfig, ma_encoder* pEncoder); MA_API void ma_encoder_uninit(ma_encoder* pEncoder); MA_API ma_result ma_encoder_write_pcm_frames(ma_encoder* pEncoder, const void* pFramesIn, ma_uint64 frameCount, ma_uint64* pFramesWritten); #endif /* MA_NO_ENCODING */ /************************************************************************************************************************************************************ Generation ************************************************************************************************************************************************************/ #ifndef MA_NO_GENERATION typedef enum { ma_waveform_type_sine, ma_waveform_type_square, ma_waveform_type_triangle, ma_waveform_type_sawtooth } ma_waveform_type; typedef struct { ma_format format; ma_uint32 channels; ma_uint32 sampleRate; ma_waveform_type type; double amplitude; double frequency; } ma_waveform_config; MA_API ma_waveform_config ma_waveform_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, ma_waveform_type type, double amplitude, double frequency); typedef struct { ma_data_source_base ds; ma_waveform_config config; double advance; double time; } ma_waveform; MA_API ma_result ma_waveform_init(const ma_waveform_config* pConfig, ma_waveform* pWaveform); MA_API void ma_waveform_uninit(ma_waveform* pWaveform); MA_API ma_result ma_waveform_read_pcm_frames(ma_waveform* pWaveform, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); MA_API ma_result ma_waveform_seek_to_pcm_frame(ma_waveform* pWaveform, ma_uint64 frameIndex); MA_API ma_result ma_waveform_set_amplitude(ma_waveform* pWaveform, double amplitude); MA_API ma_result ma_waveform_set_frequency(ma_waveform* pWaveform, double frequency); MA_API ma_result ma_waveform_set_type(ma_waveform* pWaveform, ma_waveform_type type); MA_API ma_result ma_waveform_set_sample_rate(ma_waveform* pWaveform, ma_uint32 sampleRate); typedef enum { ma_noise_type_white, ma_noise_type_pink, ma_noise_type_brownian } ma_noise_type; typedef struct { ma_format format; ma_uint32 channels; ma_noise_type type; ma_int32 seed; double amplitude; ma_bool32 duplicateChannels; } ma_noise_config; MA_API ma_noise_config ma_noise_config_init(ma_format format, ma_uint32 channels, ma_noise_type type, ma_int32 seed, double amplitude); typedef struct { ma_data_source_vtable ds; ma_noise_config config; ma_lcg lcg; union { struct { double** bin; double* accumulation; ma_uint32* counter; } pink; struct { double* accumulation; } brownian; } state; /* Memory management. */ void* _pHeap; ma_bool32 _ownsHeap; } ma_noise; MA_API ma_result ma_noise_get_heap_size(const ma_noise_config* pConfig, size_t* pHeapSizeInBytes); MA_API ma_result ma_noise_init_preallocated(const ma_noise_config* pConfig, void* pHeap, ma_noise* pNoise); MA_API ma_result ma_noise_init(const ma_noise_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_noise* pNoise); MA_API void ma_noise_uninit(ma_noise* pNoise, const ma_allocation_callbacks* pAllocationCallbacks); MA_API ma_result ma_noise_read_pcm_frames(ma_noise* pNoise, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); MA_API ma_result ma_noise_set_amplitude(ma_noise* pNoise, double amplitude); MA_API ma_result ma_noise_set_seed(ma_noise* pNoise, ma_int32 seed); MA_API ma_result ma_noise_set_type(ma_noise* pNoise, ma_noise_type type); #endif /* MA_NO_GENERATION */ /************************************************************************************************************************************************************ Resource Manager ************************************************************************************************************************************************************/ /* The resource manager cannot be enabled if there is no decoder. */ #if !defined(MA_NO_RESOURCE_MANAGER) && defined(MA_NO_DECODING) #define MA_NO_RESOURCE_MANAGER #endif #ifndef MA_NO_RESOURCE_MANAGER typedef struct ma_resource_manager ma_resource_manager; typedef struct ma_resource_manager_data_buffer_node ma_resource_manager_data_buffer_node; typedef struct ma_resource_manager_data_buffer ma_resource_manager_data_buffer; typedef struct ma_resource_manager_data_stream ma_resource_manager_data_stream; typedef struct ma_resource_manager_data_source ma_resource_manager_data_source; typedef enum { MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM = 0x00000001, /* When set, does not load the entire data source in memory. Disk I/O will happen on job threads. */ MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_DECODE = 0x00000002, /* Decode data before storing in memory. When set, decoding is done at the resource manager level rather than the mixing thread. Results in faster mixing, but higher memory usage. */ MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC = 0x00000004, /* When set, the resource manager will load the data source asynchronously. */ MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT = 0x00000008, /* When set, waits for initialization of the underlying data source before returning from ma_resource_manager_data_source_init(). */ MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_UNKNOWN_LENGTH = 0x00000010 /* Gives the resource manager a hint that the length of the data source is unknown and calling `ma_data_source_get_length_in_pcm_frames()` should be avoided. */ } ma_resource_manager_data_source_flags; /* Pipeline notifications used by the resource manager. Made up of both an async notification and a fence, both of which are optional. */ typedef struct { ma_async_notification* pNotification; ma_fence* pFence; } ma_resource_manager_pipeline_stage_notification; typedef struct { ma_resource_manager_pipeline_stage_notification init; /* Initialization of the decoder. */ ma_resource_manager_pipeline_stage_notification done; /* Decoding fully completed. */ } ma_resource_manager_pipeline_notifications; MA_API ma_resource_manager_pipeline_notifications ma_resource_manager_pipeline_notifications_init(void); /* BEGIN BACKWARDS COMPATIBILITY */ /* TODO: Remove this block in version 0.12. */ #if 1 #define ma_resource_manager_job ma_job #define ma_resource_manager_job_init ma_job_init #define MA_JOB_TYPE_RESOURCE_MANAGER_QUEUE_FLAG_NON_BLOCKING MA_JOB_QUEUE_FLAG_NON_BLOCKING #define ma_resource_manager_job_queue_config ma_job_queue_config #define ma_resource_manager_job_queue_config_init ma_job_queue_config_init #define ma_resource_manager_job_queue ma_job_queue #define ma_resource_manager_job_queue_get_heap_size ma_job_queue_get_heap_size #define ma_resource_manager_job_queue_init_preallocated ma_job_queue_init_preallocated #define ma_resource_manager_job_queue_init ma_job_queue_init #define ma_resource_manager_job_queue_uninit ma_job_queue_uninit #define ma_resource_manager_job_queue_post ma_job_queue_post #define ma_resource_manager_job_queue_next ma_job_queue_next #endif /* END BACKWARDS COMPATIBILITY */ /* Maximum job thread count will be restricted to this, but this may be removed later and replaced with a heap allocation thereby removing any limitation. */ #ifndef MA_RESOURCE_MANAGER_MAX_JOB_THREAD_COUNT #define MA_RESOURCE_MANAGER_MAX_JOB_THREAD_COUNT 64 #endif typedef enum { /* Indicates ma_resource_manager_next_job() should not block. Only valid when the job thread count is 0. */ MA_RESOURCE_MANAGER_FLAG_NON_BLOCKING = 0x00000001, /* Disables any kind of multithreading. Implicitly enables MA_RESOURCE_MANAGER_FLAG_NON_BLOCKING. */ MA_RESOURCE_MANAGER_FLAG_NO_THREADING = 0x00000002 } ma_resource_manager_flags; typedef struct { const char* pFilePath; const wchar_t* pFilePathW; const ma_resource_manager_pipeline_notifications* pNotifications; ma_uint64 initialSeekPointInPCMFrames; ma_uint64 rangeBegInPCMFrames; ma_uint64 rangeEndInPCMFrames; ma_uint64 loopPointBegInPCMFrames; ma_uint64 loopPointEndInPCMFrames; ma_bool32 isLooping; ma_uint32 flags; } ma_resource_manager_data_source_config; MA_API ma_resource_manager_data_source_config ma_resource_manager_data_source_config_init(void); typedef enum { ma_resource_manager_data_supply_type_unknown = 0, /* Used for determining whether or the data supply has been initialized. */ ma_resource_manager_data_supply_type_encoded, /* Data supply is an encoded buffer. Connector is ma_decoder. */ ma_resource_manager_data_supply_type_decoded, /* Data supply is a decoded buffer. Connector is ma_audio_buffer. */ ma_resource_manager_data_supply_type_decoded_paged /* Data supply is a linked list of decoded buffers. Connector is ma_paged_audio_buffer. */ } ma_resource_manager_data_supply_type; typedef struct { MA_ATOMIC(4, ma_resource_manager_data_supply_type) type; /* Read and written from different threads so needs to be accessed atomically. */ union { struct { const void* pData; size_t sizeInBytes; } encoded; struct { const void* pData; ma_uint64 totalFrameCount; ma_uint64 decodedFrameCount; ma_format format; ma_uint32 channels; ma_uint32 sampleRate; } decoded; struct { ma_paged_audio_buffer_data data; ma_uint64 decodedFrameCount; ma_uint32 sampleRate; } decodedPaged; } backend; } ma_resource_manager_data_supply; struct ma_resource_manager_data_buffer_node { ma_uint32 hashedName32; /* The hashed name. This is the key. */ ma_uint32 refCount; MA_ATOMIC(4, ma_result) result; /* Result from asynchronous loading. When loading set to MA_BUSY. When fully loaded set to MA_SUCCESS. When deleting set to MA_UNAVAILABLE. */ MA_ATOMIC(4, ma_uint32) executionCounter; /* For allocating execution orders for jobs. */ MA_ATOMIC(4, ma_uint32) executionPointer; /* For managing the order of execution for asynchronous jobs relating to this object. Incremented as jobs complete processing. */ ma_bool32 isDataOwnedByResourceManager; /* Set to true when the underlying data buffer was allocated the resource manager. Set to false if it is owned by the application (via ma_resource_manager_register_*()). */ ma_resource_manager_data_supply data; ma_resource_manager_data_buffer_node* pParent; ma_resource_manager_data_buffer_node* pChildLo; ma_resource_manager_data_buffer_node* pChildHi; }; struct ma_resource_manager_data_buffer { ma_data_source_base ds; /* Base data source. A data buffer is a data source. */ ma_resource_manager* pResourceManager; /* A pointer to the resource manager that owns this buffer. */ ma_resource_manager_data_buffer_node* pNode; /* The data node. This is reference counted and is what supplies the data. */ ma_uint32 flags; /* The flags that were passed used to initialize the buffer. */ MA_ATOMIC(4, ma_uint32) executionCounter; /* For allocating execution orders for jobs. */ MA_ATOMIC(4, ma_uint32) executionPointer; /* For managing the order of execution for asynchronous jobs relating to this object. Incremented as jobs complete processing. */ ma_uint64 seekTargetInPCMFrames; /* Only updated by the public API. Never written nor read from the job thread. */ ma_bool32 seekToCursorOnNextRead; /* On the next read we need to seek to the frame cursor. */ MA_ATOMIC(4, ma_result) result; /* Keeps track of a result of decoding. Set to MA_BUSY while the buffer is still loading. Set to MA_SUCCESS when loading is finished successfully. Otherwise set to some other code. */ MA_ATOMIC(4, ma_bool32) isLooping; /* Can be read and written by different threads at the same time. Must be used atomically. */ ma_bool32 isConnectorInitialized; /* Used for asynchronous loading to ensure we don't try to initialize the connector multiple times while waiting for the node to fully load. */ union { ma_decoder decoder; /* Supply type is ma_resource_manager_data_supply_type_encoded */ ma_audio_buffer buffer; /* Supply type is ma_resource_manager_data_supply_type_decoded */ ma_paged_audio_buffer pagedBuffer; /* Supply type is ma_resource_manager_data_supply_type_decoded_paged */ } connector; /* Connects this object to the node's data supply. */ }; struct ma_resource_manager_data_stream { ma_data_source_base ds; /* Base data source. A data stream is a data source. */ ma_resource_manager* pResourceManager; /* A pointer to the resource manager that owns this data stream. */ ma_uint32 flags; /* The flags that were passed used to initialize the stream. */ ma_decoder decoder; /* Used for filling pages with data. This is only ever accessed by the job thread. The public API should never touch this. */ ma_bool32 isDecoderInitialized; /* Required for determining whether or not the decoder should be uninitialized in MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_STREAM. */ ma_uint64 totalLengthInPCMFrames; /* This is calculated when first loaded by the MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_STREAM. */ ma_uint32 relativeCursor; /* The playback cursor, relative to the current page. Only ever accessed by the public API. Never accessed by the job thread. */ MA_ATOMIC(8, ma_uint64) absoluteCursor; /* The playback cursor, in absolute position starting from the start of the file. */ ma_uint32 currentPageIndex; /* Toggles between 0 and 1. Index 0 is the first half of pPageData. Index 1 is the second half. Only ever accessed by the public API. Never accessed by the job thread. */ MA_ATOMIC(4, ma_uint32) executionCounter; /* For allocating execution orders for jobs. */ MA_ATOMIC(4, ma_uint32) executionPointer; /* For managing the order of execution for asynchronous jobs relating to this object. Incremented as jobs complete processing. */ /* Written by the public API, read by the job thread. */ MA_ATOMIC(4, ma_bool32) isLooping; /* Whether or not the stream is looping. It's important to set the looping flag at the data stream level for smooth loop transitions. */ /* Written by the job thread, read by the public API. */ void* pPageData; /* Buffer containing the decoded data of each page. Allocated once at initialization time. */ MA_ATOMIC(4, ma_uint32) pageFrameCount[2]; /* The number of valid PCM frames in each page. Used to determine the last valid frame. */ /* Written and read by both the public API and the job thread. These must be atomic. */ MA_ATOMIC(4, ma_result) result; /* Result from asynchronous loading. When loading set to MA_BUSY. When initialized set to MA_SUCCESS. When deleting set to MA_UNAVAILABLE. If an error occurs when loading, set to an error code. */ MA_ATOMIC(4, ma_bool32) isDecoderAtEnd; /* Whether or not the decoder has reached the end. */ MA_ATOMIC(4, ma_bool32) isPageValid[2]; /* Booleans to indicate whether or not a page is valid. Set to false by the public API, set to true by the job thread. Set to false as the pages are consumed, true when they are filled. */ MA_ATOMIC(4, ma_bool32) seekCounter; /* When 0, no seeking is being performed. When > 0, a seek is being performed and reading should be delayed with MA_BUSY. */ }; struct ma_resource_manager_data_source { union { ma_resource_manager_data_buffer buffer; ma_resource_manager_data_stream stream; } backend; /* Must be the first item because we need the first item to be the data source callbacks for the buffer or stream. */ ma_uint32 flags; /* The flags that were passed in to ma_resource_manager_data_source_init(). */ MA_ATOMIC(4, ma_uint32) executionCounter; /* For allocating execution orders for jobs. */ MA_ATOMIC(4, ma_uint32) executionPointer; /* For managing the order of execution for asynchronous jobs relating to this object. Incremented as jobs complete processing. */ }; typedef struct { ma_allocation_callbacks allocationCallbacks; ma_log* pLog; ma_format decodedFormat; /* The decoded format to use. Set to ma_format_unknown (default) to use the file's native format. */ ma_uint32 decodedChannels; /* The decoded channel count to use. Set to 0 (default) to use the file's native channel count. */ ma_uint32 decodedSampleRate; /* the decoded sample rate to use. Set to 0 (default) to use the file's native sample rate. */ ma_uint32 jobThreadCount; /* Set to 0 if you want to self-manage your job threads. Defaults to 1. */ ma_uint32 jobQueueCapacity; /* The maximum number of jobs that can fit in the queue at a time. Defaults to MA_JOB_TYPE_RESOURCE_MANAGER_QUEUE_CAPACITY. Cannot be zero. */ ma_uint32 flags; ma_vfs* pVFS; /* Can be NULL in which case defaults will be used. */ ma_decoding_backend_vtable** ppCustomDecodingBackendVTables; ma_uint32 customDecodingBackendCount; void* pCustomDecodingBackendUserData; } ma_resource_manager_config; MA_API ma_resource_manager_config ma_resource_manager_config_init(void); struct ma_resource_manager { ma_resource_manager_config config; ma_resource_manager_data_buffer_node* pRootDataBufferNode; /* The root buffer in the binary tree. */ #ifndef MA_NO_THREADING ma_mutex dataBufferBSTLock; /* For synchronizing access to the data buffer binary tree. */ ma_thread jobThreads[MA_RESOURCE_MANAGER_MAX_JOB_THREAD_COUNT]; /* The threads for executing jobs. */ #endif ma_job_queue jobQueue; /* Multi-consumer, multi-producer job queue for managing jobs for asynchronous decoding and streaming. */ ma_default_vfs defaultVFS; /* Only used if a custom VFS is not specified. */ ma_log log; /* Only used if no log was specified in the config. */ }; /* Init. */ MA_API ma_result ma_resource_manager_init(const ma_resource_manager_config* pConfig, ma_resource_manager* pResourceManager); MA_API void ma_resource_manager_uninit(ma_resource_manager* pResourceManager); MA_API ma_log* ma_resource_manager_get_log(ma_resource_manager* pResourceManager); /* Registration. */ MA_API ma_result ma_resource_manager_register_file(ma_resource_manager* pResourceManager, const char* pFilePath, ma_uint32 flags); MA_API ma_result ma_resource_manager_register_file_w(ma_resource_manager* pResourceManager, const wchar_t* pFilePath, ma_uint32 flags); MA_API ma_result ma_resource_manager_register_decoded_data(ma_resource_manager* pResourceManager, const char* pName, const void* pData, ma_uint64 frameCount, ma_format format, ma_uint32 channels, ma_uint32 sampleRate); /* Does not copy. Increments the reference count if already exists and returns MA_SUCCESS. */ MA_API ma_result ma_resource_manager_register_decoded_data_w(ma_resource_manager* pResourceManager, const wchar_t* pName, const void* pData, ma_uint64 frameCount, ma_format format, ma_uint32 channels, ma_uint32 sampleRate); MA_API ma_result ma_resource_manager_register_encoded_data(ma_resource_manager* pResourceManager, const char* pName, const void* pData, size_t sizeInBytes); /* Does not copy. Increments the reference count if already exists and returns MA_SUCCESS. */ MA_API ma_result ma_resource_manager_register_encoded_data_w(ma_resource_manager* pResourceManager, const wchar_t* pName, const void* pData, size_t sizeInBytes); MA_API ma_result ma_resource_manager_unregister_file(ma_resource_manager* pResourceManager, const char* pFilePath); MA_API ma_result ma_resource_manager_unregister_file_w(ma_resource_manager* pResourceManager, const wchar_t* pFilePath); MA_API ma_result ma_resource_manager_unregister_data(ma_resource_manager* pResourceManager, const char* pName); MA_API ma_result ma_resource_manager_unregister_data_w(ma_resource_manager* pResourceManager, const wchar_t* pName); /* Data Buffers. */ MA_API ma_result ma_resource_manager_data_buffer_init_ex(ma_resource_manager* pResourceManager, const ma_resource_manager_data_source_config* pConfig, ma_resource_manager_data_buffer* pDataBuffer); MA_API ma_result ma_resource_manager_data_buffer_init(ma_resource_manager* pResourceManager, const char* pFilePath, ma_uint32 flags, const ma_resource_manager_pipeline_notifications* pNotifications, ma_resource_manager_data_buffer* pDataBuffer); MA_API ma_result ma_resource_manager_data_buffer_init_w(ma_resource_manager* pResourceManager, const wchar_t* pFilePath, ma_uint32 flags, const ma_resource_manager_pipeline_notifications* pNotifications, ma_resource_manager_data_buffer* pDataBuffer); MA_API ma_result ma_resource_manager_data_buffer_init_copy(ma_resource_manager* pResourceManager, const ma_resource_manager_data_buffer* pExistingDataBuffer, ma_resource_manager_data_buffer* pDataBuffer); MA_API ma_result ma_resource_manager_data_buffer_uninit(ma_resource_manager_data_buffer* pDataBuffer); MA_API ma_result ma_resource_manager_data_buffer_read_pcm_frames(ma_resource_manager_data_buffer* pDataBuffer, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); MA_API ma_result ma_resource_manager_data_buffer_seek_to_pcm_frame(ma_resource_manager_data_buffer* pDataBuffer, ma_uint64 frameIndex); MA_API ma_result ma_resource_manager_data_buffer_get_data_format(ma_resource_manager_data_buffer* pDataBuffer, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap); MA_API ma_result ma_resource_manager_data_buffer_get_cursor_in_pcm_frames(ma_resource_manager_data_buffer* pDataBuffer, ma_uint64* pCursor); MA_API ma_result ma_resource_manager_data_buffer_get_length_in_pcm_frames(ma_resource_manager_data_buffer* pDataBuffer, ma_uint64* pLength); MA_API ma_result ma_resource_manager_data_buffer_result(const ma_resource_manager_data_buffer* pDataBuffer); MA_API ma_result ma_resource_manager_data_buffer_set_looping(ma_resource_manager_data_buffer* pDataBuffer, ma_bool32 isLooping); MA_API ma_bool32 ma_resource_manager_data_buffer_is_looping(const ma_resource_manager_data_buffer* pDataBuffer); MA_API ma_result ma_resource_manager_data_buffer_get_available_frames(ma_resource_manager_data_buffer* pDataBuffer, ma_uint64* pAvailableFrames); /* Data Streams. */ MA_API ma_result ma_resource_manager_data_stream_init_ex(ma_resource_manager* pResourceManager, const ma_resource_manager_data_source_config* pConfig, ma_resource_manager_data_stream* pDataStream); MA_API ma_result ma_resource_manager_data_stream_init(ma_resource_manager* pResourceManager, const char* pFilePath, ma_uint32 flags, const ma_resource_manager_pipeline_notifications* pNotifications, ma_resource_manager_data_stream* pDataStream); MA_API ma_result ma_resource_manager_data_stream_init_w(ma_resource_manager* pResourceManager, const wchar_t* pFilePath, ma_uint32 flags, const ma_resource_manager_pipeline_notifications* pNotifications, ma_resource_manager_data_stream* pDataStream); MA_API ma_result ma_resource_manager_data_stream_uninit(ma_resource_manager_data_stream* pDataStream); MA_API ma_result ma_resource_manager_data_stream_read_pcm_frames(ma_resource_manager_data_stream* pDataStream, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); MA_API ma_result ma_resource_manager_data_stream_seek_to_pcm_frame(ma_resource_manager_data_stream* pDataStream, ma_uint64 frameIndex); MA_API ma_result ma_resource_manager_data_stream_get_data_format(ma_resource_manager_data_stream* pDataStream, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap); MA_API ma_result ma_resource_manager_data_stream_get_cursor_in_pcm_frames(ma_resource_manager_data_stream* pDataStream, ma_uint64* pCursor); MA_API ma_result ma_resource_manager_data_stream_get_length_in_pcm_frames(ma_resource_manager_data_stream* pDataStream, ma_uint64* pLength); MA_API ma_result ma_resource_manager_data_stream_result(const ma_resource_manager_data_stream* pDataStream); MA_API ma_result ma_resource_manager_data_stream_set_looping(ma_resource_manager_data_stream* pDataStream, ma_bool32 isLooping); MA_API ma_bool32 ma_resource_manager_data_stream_is_looping(const ma_resource_manager_data_stream* pDataStream); MA_API ma_result ma_resource_manager_data_stream_get_available_frames(ma_resource_manager_data_stream* pDataStream, ma_uint64* pAvailableFrames); /* Data Sources. */ MA_API ma_result ma_resource_manager_data_source_init_ex(ma_resource_manager* pResourceManager, const ma_resource_manager_data_source_config* pConfig, ma_resource_manager_data_source* pDataSource); MA_API ma_result ma_resource_manager_data_source_init(ma_resource_manager* pResourceManager, const char* pName, ma_uint32 flags, const ma_resource_manager_pipeline_notifications* pNotifications, ma_resource_manager_data_source* pDataSource); MA_API ma_result ma_resource_manager_data_source_init_w(ma_resource_manager* pResourceManager, const wchar_t* pName, ma_uint32 flags, const ma_resource_manager_pipeline_notifications* pNotifications, ma_resource_manager_data_source* pDataSource); MA_API ma_result ma_resource_manager_data_source_init_copy(ma_resource_manager* pResourceManager, const ma_resource_manager_data_source* pExistingDataSource, ma_resource_manager_data_source* pDataSource); MA_API ma_result ma_resource_manager_data_source_uninit(ma_resource_manager_data_source* pDataSource); MA_API ma_result ma_resource_manager_data_source_read_pcm_frames(ma_resource_manager_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); MA_API ma_result ma_resource_manager_data_source_seek_to_pcm_frame(ma_resource_manager_data_source* pDataSource, ma_uint64 frameIndex); MA_API ma_result ma_resource_manager_data_source_get_data_format(ma_resource_manager_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap); MA_API ma_result ma_resource_manager_data_source_get_cursor_in_pcm_frames(ma_resource_manager_data_source* pDataSource, ma_uint64* pCursor); MA_API ma_result ma_resource_manager_data_source_get_length_in_pcm_frames(ma_resource_manager_data_source* pDataSource, ma_uint64* pLength); MA_API ma_result ma_resource_manager_data_source_result(const ma_resource_manager_data_source* pDataSource); MA_API ma_result ma_resource_manager_data_source_set_looping(ma_resource_manager_data_source* pDataSource, ma_bool32 isLooping); MA_API ma_bool32 ma_resource_manager_data_source_is_looping(const ma_resource_manager_data_source* pDataSource); MA_API ma_result ma_resource_manager_data_source_get_available_frames(ma_resource_manager_data_source* pDataSource, ma_uint64* pAvailableFrames); /* Job management. */ MA_API ma_result ma_resource_manager_post_job(ma_resource_manager* pResourceManager, const ma_job* pJob); MA_API ma_result ma_resource_manager_post_job_quit(ma_resource_manager* pResourceManager); /* Helper for posting a quit job. */ MA_API ma_result ma_resource_manager_next_job(ma_resource_manager* pResourceManager, ma_job* pJob); MA_API ma_result ma_resource_manager_process_job(ma_resource_manager* pResourceManager, ma_job* pJob); /* DEPRECATED. Use ma_job_process(). Will be removed in version 0.12. */ MA_API ma_result ma_resource_manager_process_next_job(ma_resource_manager* pResourceManager); /* Returns MA_CANCELLED if a MA_JOB_TYPE_QUIT job is found. In non-blocking mode, returns MA_NO_DATA_AVAILABLE if no jobs are available. */ #endif /* MA_NO_RESOURCE_MANAGER */ /************************************************************************************************************************************************************ Node Graph ************************************************************************************************************************************************************/ #ifndef MA_NO_NODE_GRAPH /* Must never exceed 254. */ #ifndef MA_MAX_NODE_BUS_COUNT #define MA_MAX_NODE_BUS_COUNT 254 #endif /* Used internally by miniaudio for memory management. Must never exceed MA_MAX_NODE_BUS_COUNT. */ #ifndef MA_MAX_NODE_LOCAL_BUS_COUNT #define MA_MAX_NODE_LOCAL_BUS_COUNT 2 #endif /* Use this when the bus count is determined by the node instance rather than the vtable. */ #define MA_NODE_BUS_COUNT_UNKNOWN 255 typedef struct ma_node_graph ma_node_graph; typedef void ma_node; /* Node flags. */ typedef enum { MA_NODE_FLAG_PASSTHROUGH = 0x00000001, MA_NODE_FLAG_CONTINUOUS_PROCESSING = 0x00000002, MA_NODE_FLAG_ALLOW_NULL_INPUT = 0x00000004, MA_NODE_FLAG_DIFFERENT_PROCESSING_RATES = 0x00000008, MA_NODE_FLAG_SILENT_OUTPUT = 0x00000010 } ma_node_flags; /* The playback state of a node. Either started or stopped. */ typedef enum { ma_node_state_started = 0, ma_node_state_stopped = 1 } ma_node_state; typedef struct { /* Extended processing callback. This callback is used for effects that process input and output at different rates (i.e. they perform resampling). This is similar to the simple version, only they take two seperate frame counts: one for input, and one for output. On input, `pFrameCountOut` is equal to the capacity of the output buffer for each bus, whereas `pFrameCountIn` will be equal to the number of PCM frames in each of the buffers in `ppFramesIn`. On output, set `pFrameCountOut` to the number of PCM frames that were actually output and set `pFrameCountIn` to the number of input frames that were consumed. */ void (* onProcess)(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut); /* A callback for retrieving the number of a input frames that are required to output the specified number of output frames. You would only want to implement this when the node performs resampling. This is optional, even for nodes that perform resampling, but it does offer a small reduction in latency as it allows miniaudio to calculate the exact number of input frames to read at a time instead of having to estimate. */ ma_result (* onGetRequiredInputFrameCount)(ma_node* pNode, ma_uint32 outputFrameCount, ma_uint32* pInputFrameCount); /* The number of input buses. This is how many sub-buffers will be contained in the `ppFramesIn` parameters of the callbacks above. */ ma_uint8 inputBusCount; /* The number of output buses. This is how many sub-buffers will be contained in the `ppFramesOut` parameters of the callbacks above. */ ma_uint8 outputBusCount; /* Flags describing characteristics of the node. This is currently just a placeholder for some ideas for later on. */ ma_uint32 flags; } ma_node_vtable; typedef struct { const ma_node_vtable* vtable; /* Should never be null. Initialization of the node will fail if so. */ ma_node_state initialState; /* Defaults to ma_node_state_started. */ ma_uint32 inputBusCount; /* Only used if the vtable specifies an input bus count of `MA_NODE_BUS_COUNT_UNKNOWN`, otherwise must be set to `MA_NODE_BUS_COUNT_UNKNOWN` (default). */ ma_uint32 outputBusCount; /* Only used if the vtable specifies an output bus count of `MA_NODE_BUS_COUNT_UNKNOWN`, otherwise be set to `MA_NODE_BUS_COUNT_UNKNOWN` (default). */ const ma_uint32* pInputChannels; /* The number of elements are determined by the input bus count as determined by the vtable, or `inputBusCount` if the vtable specifies `MA_NODE_BUS_COUNT_UNKNOWN`. */ const ma_uint32* pOutputChannels; /* The number of elements are determined by the output bus count as determined by the vtable, or `outputBusCount` if the vtable specifies `MA_NODE_BUS_COUNT_UNKNOWN`. */ } ma_node_config; MA_API ma_node_config ma_node_config_init(void); /* A node has multiple output buses. An output bus is attached to an input bus as an item in a linked list. Think of the input bus as a linked list, with the output bus being an item in that list. */ typedef struct ma_node_output_bus ma_node_output_bus; struct ma_node_output_bus { /* Immutable. */ ma_node* pNode; /* The node that owns this output bus. The input node. Will be null for dummy head and tail nodes. */ ma_uint8 outputBusIndex; /* The index of the output bus on pNode that this output bus represents. */ ma_uint8 channels; /* The number of channels in the audio stream for this bus. */ /* Mutable via multiple threads. Must be used atomically. The weird ordering here is for packing reasons. */ MA_ATOMIC(1, ma_uint8) inputNodeInputBusIndex; /* The index of the input bus on the input. Required for detaching. */ MA_ATOMIC(4, ma_uint32) flags; /* Some state flags for tracking the read state of the output buffer. A combination of MA_NODE_OUTPUT_BUS_FLAG_*. */ MA_ATOMIC(4, ma_uint32) refCount; /* Reference count for some thread-safety when detaching. */ MA_ATOMIC(4, ma_bool32) isAttached; /* This is used to prevent iteration of nodes that are in the middle of being detached. Used for thread safety. */ MA_ATOMIC(4, ma_spinlock) lock; /* Unfortunate lock, but significantly simplifies the implementation. Required for thread-safe attaching and detaching. */ MA_ATOMIC(4, float) volume; /* Linear. */ MA_ATOMIC(MA_SIZEOF_PTR, ma_node_output_bus*) pNext; /* If null, it's the tail node or detached. */ MA_ATOMIC(MA_SIZEOF_PTR, ma_node_output_bus*) pPrev; /* If null, it's the head node or detached. */ MA_ATOMIC(MA_SIZEOF_PTR, ma_node*) pInputNode; /* The node that this output bus is attached to. Required for detaching. */ }; /* A node has multiple input buses. The output buses of a node are connecting to the input busses of another. An input bus is essentially just a linked list of output buses. */ typedef struct ma_node_input_bus ma_node_input_bus; struct ma_node_input_bus { /* Mutable via multiple threads. */ ma_node_output_bus head; /* Dummy head node for simplifying some lock-free thread-safety stuff. */ MA_ATOMIC(4, ma_uint32) nextCounter; /* This is used to determine whether or not the input bus is finding the next node in the list. Used for thread safety when detaching output buses. */ MA_ATOMIC(4, ma_spinlock) lock; /* Unfortunate lock, but significantly simplifies the implementation. Required for thread-safe attaching and detaching. */ /* Set once at startup. */ ma_uint8 channels; /* The number of channels in the audio stream for this bus. */ }; typedef struct ma_node_base ma_node_base; struct ma_node_base { /* These variables are set once at startup. */ ma_node_graph* pNodeGraph; /* The graph this node belongs to. */ const ma_node_vtable* vtable; float* pCachedData; /* Allocated on the heap. Fixed size. Needs to be stored on the heap because reading from output buses is done in separate function calls. */ ma_uint16 cachedDataCapInFramesPerBus; /* The capacity of the input data cache in frames, per bus. */ /* These variables are read and written only from the audio thread. */ ma_uint16 cachedFrameCountOut; ma_uint16 cachedFrameCountIn; ma_uint16 consumedFrameCountIn; /* These variables are read and written between different threads. */ MA_ATOMIC(4, ma_node_state) state; /* When set to stopped, nothing will be read, regardless of the times in stateTimes. */ MA_ATOMIC(8, ma_uint64) stateTimes[2]; /* Indexed by ma_node_state. Specifies the time based on the global clock that a node should be considered to be in the relevant state. */ MA_ATOMIC(8, ma_uint64) localTime; /* The node's local clock. This is just a running sum of the number of output frames that have been processed. Can be modified by any thread with `ma_node_set_time()`. */ ma_uint32 inputBusCount; ma_uint32 outputBusCount; ma_node_input_bus* pInputBuses; ma_node_output_bus* pOutputBuses; /* Memory management. */ ma_node_input_bus _inputBuses[MA_MAX_NODE_LOCAL_BUS_COUNT]; ma_node_output_bus _outputBuses[MA_MAX_NODE_LOCAL_BUS_COUNT]; void* _pHeap; /* A heap allocation for internal use only. pInputBuses and/or pOutputBuses will point to this if the bus count exceeds MA_MAX_NODE_LOCAL_BUS_COUNT. */ ma_bool32 _ownsHeap; /* If set to true, the node owns the heap allocation and _pHeap will be freed in ma_node_uninit(). */ }; MA_API ma_result ma_node_get_heap_size(ma_node_graph* pNodeGraph, const ma_node_config* pConfig, size_t* pHeapSizeInBytes); MA_API ma_result ma_node_init_preallocated(ma_node_graph* pNodeGraph, const ma_node_config* pConfig, void* pHeap, ma_node* pNode); MA_API ma_result ma_node_init(ma_node_graph* pNodeGraph, const ma_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_node* pNode); MA_API void ma_node_uninit(ma_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks); MA_API ma_node_graph* ma_node_get_node_graph(const ma_node* pNode); MA_API ma_uint32 ma_node_get_input_bus_count(const ma_node* pNode); MA_API ma_uint32 ma_node_get_output_bus_count(const ma_node* pNode); MA_API ma_uint32 ma_node_get_input_channels(const ma_node* pNode, ma_uint32 inputBusIndex); MA_API ma_uint32 ma_node_get_output_channels(const ma_node* pNode, ma_uint32 outputBusIndex); MA_API ma_result ma_node_attach_output_bus(ma_node* pNode, ma_uint32 outputBusIndex, ma_node* pOtherNode, ma_uint32 otherNodeInputBusIndex); MA_API ma_result ma_node_detach_output_bus(ma_node* pNode, ma_uint32 outputBusIndex); MA_API ma_result ma_node_detach_all_output_buses(ma_node* pNode); MA_API ma_result ma_node_set_output_bus_volume(ma_node* pNode, ma_uint32 outputBusIndex, float volume); MA_API float ma_node_get_output_bus_volume(const ma_node* pNode, ma_uint32 outputBusIndex); MA_API ma_result ma_node_set_state(ma_node* pNode, ma_node_state state); MA_API ma_node_state ma_node_get_state(const ma_node* pNode); MA_API ma_result ma_node_set_state_time(ma_node* pNode, ma_node_state state, ma_uint64 globalTime); MA_API ma_uint64 ma_node_get_state_time(const ma_node* pNode, ma_node_state state); MA_API ma_node_state ma_node_get_state_by_time(const ma_node* pNode, ma_uint64 globalTime); MA_API ma_node_state ma_node_get_state_by_time_range(const ma_node* pNode, ma_uint64 globalTimeBeg, ma_uint64 globalTimeEnd); MA_API ma_uint64 ma_node_get_time(const ma_node* pNode); MA_API ma_result ma_node_set_time(ma_node* pNode, ma_uint64 localTime); typedef struct { ma_uint32 channels; ma_uint16 nodeCacheCapInFrames; } ma_node_graph_config; MA_API ma_node_graph_config ma_node_graph_config_init(ma_uint32 channels); struct ma_node_graph { /* Immutable. */ ma_node_base base; /* The node graph itself is a node so it can be connected as an input to different node graph. This has zero inputs and calls ma_node_graph_read_pcm_frames() to generate it's output. */ ma_node_base endpoint; /* Special node that all nodes eventually connect to. Data is read from this node in ma_node_graph_read_pcm_frames(). */ ma_uint16 nodeCacheCapInFrames; /* Read and written by multiple threads. */ MA_ATOMIC(4, ma_bool32) isReading; }; MA_API ma_result ma_node_graph_init(const ma_node_graph_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_node_graph* pNodeGraph); MA_API void ma_node_graph_uninit(ma_node_graph* pNodeGraph, const ma_allocation_callbacks* pAllocationCallbacks); MA_API ma_node* ma_node_graph_get_endpoint(ma_node_graph* pNodeGraph); MA_API ma_result ma_node_graph_read_pcm_frames(ma_node_graph* pNodeGraph, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); MA_API ma_uint32 ma_node_graph_get_channels(const ma_node_graph* pNodeGraph); MA_API ma_uint64 ma_node_graph_get_time(const ma_node_graph* pNodeGraph); MA_API ma_result ma_node_graph_set_time(ma_node_graph* pNodeGraph, ma_uint64 globalTime); /* Data source node. 0 input buses, 1 output bus. Used for reading from a data source. */ typedef struct { ma_node_config nodeConfig; ma_data_source* pDataSource; } ma_data_source_node_config; MA_API ma_data_source_node_config ma_data_source_node_config_init(ma_data_source* pDataSource); typedef struct { ma_node_base base; ma_data_source* pDataSource; } ma_data_source_node; MA_API ma_result ma_data_source_node_init(ma_node_graph* pNodeGraph, const ma_data_source_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source_node* pDataSourceNode); MA_API void ma_data_source_node_uninit(ma_data_source_node* pDataSourceNode, const ma_allocation_callbacks* pAllocationCallbacks); MA_API ma_result ma_data_source_node_set_looping(ma_data_source_node* pDataSourceNode, ma_bool32 isLooping); MA_API ma_bool32 ma_data_source_node_is_looping(ma_data_source_node* pDataSourceNode); /* Splitter Node. 1 input, 2 outputs. Used for splitting/copying a stream so it can be as input into two separate output nodes. */ typedef struct { ma_node_config nodeConfig; ma_uint32 channels; ma_uint32 outputBusCount; } ma_splitter_node_config; MA_API ma_splitter_node_config ma_splitter_node_config_init(ma_uint32 channels); typedef struct { ma_node_base base; } ma_splitter_node; MA_API ma_result ma_splitter_node_init(ma_node_graph* pNodeGraph, const ma_splitter_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_splitter_node* pSplitterNode); MA_API void ma_splitter_node_uninit(ma_splitter_node* pSplitterNode, const ma_allocation_callbacks* pAllocationCallbacks); /* Biquad Node */ typedef struct { ma_node_config nodeConfig; ma_biquad_config biquad; } ma_biquad_node_config; MA_API ma_biquad_node_config ma_biquad_node_config_init(ma_uint32 channels, float b0, float b1, float b2, float a0, float a1, float a2); typedef struct { ma_node_base baseNode; ma_biquad biquad; } ma_biquad_node; MA_API ma_result ma_biquad_node_init(ma_node_graph* pNodeGraph, const ma_biquad_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_biquad_node* pNode); MA_API ma_result ma_biquad_node_reinit(const ma_biquad_config* pConfig, ma_biquad_node* pNode); MA_API void ma_biquad_node_uninit(ma_biquad_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks); /* Low Pass Filter Node */ typedef struct { ma_node_config nodeConfig; ma_lpf_config lpf; } ma_lpf_node_config; MA_API ma_lpf_node_config ma_lpf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order); typedef struct { ma_node_base baseNode; ma_lpf lpf; } ma_lpf_node; MA_API ma_result ma_lpf_node_init(ma_node_graph* pNodeGraph, const ma_lpf_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_lpf_node* pNode); MA_API ma_result ma_lpf_node_reinit(const ma_lpf_config* pConfig, ma_lpf_node* pNode); MA_API void ma_lpf_node_uninit(ma_lpf_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks); /* High Pass Filter Node */ typedef struct { ma_node_config nodeConfig; ma_hpf_config hpf; } ma_hpf_node_config; MA_API ma_hpf_node_config ma_hpf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order); typedef struct { ma_node_base baseNode; ma_hpf hpf; } ma_hpf_node; MA_API ma_result ma_hpf_node_init(ma_node_graph* pNodeGraph, const ma_hpf_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hpf_node* pNode); MA_API ma_result ma_hpf_node_reinit(const ma_hpf_config* pConfig, ma_hpf_node* pNode); MA_API void ma_hpf_node_uninit(ma_hpf_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks); /* Band Pass Filter Node */ typedef struct { ma_node_config nodeConfig; ma_bpf_config bpf; } ma_bpf_node_config; MA_API ma_bpf_node_config ma_bpf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order); typedef struct { ma_node_base baseNode; ma_bpf bpf; } ma_bpf_node; MA_API ma_result ma_bpf_node_init(ma_node_graph* pNodeGraph, const ma_bpf_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_bpf_node* pNode); MA_API ma_result ma_bpf_node_reinit(const ma_bpf_config* pConfig, ma_bpf_node* pNode); MA_API void ma_bpf_node_uninit(ma_bpf_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks); /* Notching Filter Node */ typedef struct { ma_node_config nodeConfig; ma_notch_config notch; } ma_notch_node_config; MA_API ma_notch_node_config ma_notch_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double q, double frequency); typedef struct { ma_node_base baseNode; ma_notch2 notch; } ma_notch_node; MA_API ma_result ma_notch_node_init(ma_node_graph* pNodeGraph, const ma_notch_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_notch_node* pNode); MA_API ma_result ma_notch_node_reinit(const ma_notch_config* pConfig, ma_notch_node* pNode); MA_API void ma_notch_node_uninit(ma_notch_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks); /* Peaking Filter Node */ typedef struct { ma_node_config nodeConfig; ma_peak_config peak; } ma_peak_node_config; MA_API ma_peak_node_config ma_peak_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double q, double frequency); typedef struct { ma_node_base baseNode; ma_peak2 peak; } ma_peak_node; MA_API ma_result ma_peak_node_init(ma_node_graph* pNodeGraph, const ma_peak_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_peak_node* pNode); MA_API ma_result ma_peak_node_reinit(const ma_peak_config* pConfig, ma_peak_node* pNode); MA_API void ma_peak_node_uninit(ma_peak_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks); /* Low Shelf Filter Node */ typedef struct { ma_node_config nodeConfig; ma_loshelf_config loshelf; } ma_loshelf_node_config; MA_API ma_loshelf_node_config ma_loshelf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double q, double frequency); typedef struct { ma_node_base baseNode; ma_loshelf2 loshelf; } ma_loshelf_node; MA_API ma_result ma_loshelf_node_init(ma_node_graph* pNodeGraph, const ma_loshelf_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_loshelf_node* pNode); MA_API ma_result ma_loshelf_node_reinit(const ma_loshelf_config* pConfig, ma_loshelf_node* pNode); MA_API void ma_loshelf_node_uninit(ma_loshelf_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks); /* High Shelf Filter Node */ typedef struct { ma_node_config nodeConfig; ma_hishelf_config hishelf; } ma_hishelf_node_config; MA_API ma_hishelf_node_config ma_hishelf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double q, double frequency); typedef struct { ma_node_base baseNode; ma_hishelf2 hishelf; } ma_hishelf_node; MA_API ma_result ma_hishelf_node_init(ma_node_graph* pNodeGraph, const ma_hishelf_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hishelf_node* pNode); MA_API ma_result ma_hishelf_node_reinit(const ma_hishelf_config* pConfig, ma_hishelf_node* pNode); MA_API void ma_hishelf_node_uninit(ma_hishelf_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks); typedef struct { ma_node_config nodeConfig; ma_delay_config delay; } ma_delay_node_config; MA_API ma_delay_node_config ma_delay_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, ma_uint32 delayInFrames, float decay); typedef struct { ma_node_base baseNode; ma_delay delay; } ma_delay_node; MA_API ma_result ma_delay_node_init(ma_node_graph* pNodeGraph, const ma_delay_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_delay_node* pDelayNode); MA_API void ma_delay_node_uninit(ma_delay_node* pDelayNode, const ma_allocation_callbacks* pAllocationCallbacks); MA_API void ma_delay_node_set_wet(ma_delay_node* pDelayNode, float value); MA_API float ma_delay_node_get_wet(const ma_delay_node* pDelayNode); MA_API void ma_delay_node_set_dry(ma_delay_node* pDelayNode, float value); MA_API float ma_delay_node_get_dry(const ma_delay_node* pDelayNode); MA_API void ma_delay_node_set_decay(ma_delay_node* pDelayNode, float value); MA_API float ma_delay_node_get_decay(const ma_delay_node* pDelayNode); #endif /* MA_NO_NODE_GRAPH */ /* SECTION: miniaudio_engine.h */ /************************************************************************************************************************************************************ Engine ************************************************************************************************************************************************************/ #if !defined(MA_NO_ENGINE) && !defined(MA_NO_NODE_GRAPH) typedef struct ma_engine ma_engine; typedef struct ma_sound ma_sound; /* Sound flags. */ typedef enum { MA_SOUND_FLAG_STREAM = 0x00000001, /* MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM */ MA_SOUND_FLAG_DECODE = 0x00000002, /* MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_DECODE */ MA_SOUND_FLAG_ASYNC = 0x00000004, /* MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC */ MA_SOUND_FLAG_WAIT_INIT = 0x00000008, /* MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT */ MA_SOUND_FLAG_NO_DEFAULT_ATTACHMENT = 0x00000010, /* Do not attach to the endpoint by default. Useful for when setting up nodes in a complex graph system. */ MA_SOUND_FLAG_NO_PITCH = 0x00000020, /* Disable pitch shifting with ma_sound_set_pitch() and ma_sound_group_set_pitch(). This is an optimization. */ MA_SOUND_FLAG_NO_SPATIALIZATION = 0x00000040 /* Disable spatialization. */ } ma_sound_flags; #ifndef MA_ENGINE_MAX_LISTENERS #define MA_ENGINE_MAX_LISTENERS 4 #endif #define MA_LISTENER_INDEX_CLOSEST ((ma_uint8)-1) typedef enum { ma_engine_node_type_sound, ma_engine_node_type_group } ma_engine_node_type; typedef struct { ma_engine* pEngine; ma_engine_node_type type; ma_uint32 channelsIn; ma_uint32 channelsOut; ma_uint32 sampleRate; /* Only used when the type is set to ma_engine_node_type_sound. */ ma_mono_expansion_mode monoExpansionMode; ma_bool8 isPitchDisabled; /* Pitching can be explicitly disable with MA_SOUND_FLAG_NO_PITCH to optimize processing. */ ma_bool8 isSpatializationDisabled; /* Spatialization can be explicitly disabled with MA_SOUND_FLAG_NO_SPATIALIZATION. */ ma_uint8 pinnedListenerIndex; /* The index of the listener this node should always use for spatialization. If set to MA_LISTENER_INDEX_CLOSEST the engine will use the closest listener. */ } ma_engine_node_config; MA_API ma_engine_node_config ma_engine_node_config_init(ma_engine* pEngine, ma_engine_node_type type, ma_uint32 flags); /* Base node object for both ma_sound and ma_sound_group. */ typedef struct { ma_node_base baseNode; /* Must be the first member for compatiblity with the ma_node API. */ ma_engine* pEngine; /* A pointer to the engine. Set based on the value from the config. */ ma_uint32 sampleRate; /* The sample rate of the input data. For sounds backed by a data source, this will be the data source's sample rate. Otherwise it'll be the engine's sample rate. */ ma_mono_expansion_mode monoExpansionMode; ma_fader fader; ma_linear_resampler resampler; /* For pitch shift. */ ma_spatializer spatializer; ma_panner panner; MA_ATOMIC(4, float) pitch; float oldPitch; /* For determining whether or not the resampler needs to be updated to reflect the new pitch. The resampler will be updated on the mixing thread. */ float oldDopplerPitch; /* For determining whether or not the resampler needs to be updated to take a new doppler pitch into account. */ MA_ATOMIC(4, ma_bool32) isPitchDisabled; /* When set to true, pitching will be disabled which will allow the resampler to be bypassed to save some computation. */ MA_ATOMIC(4, ma_bool32) isSpatializationDisabled; /* Set to false by default. When set to false, will not have spatialisation applied. */ MA_ATOMIC(4, ma_uint32) pinnedListenerIndex; /* The index of the listener this node should always use for spatialization. If set to MA_LISTENER_INDEX_CLOSEST the engine will use the closest listener. */ /* Memory management. */ ma_bool8 _ownsHeap; void* _pHeap; } ma_engine_node; MA_API ma_result ma_engine_node_get_heap_size(const ma_engine_node_config* pConfig, size_t* pHeapSizeInBytes); MA_API ma_result ma_engine_node_init_preallocated(const ma_engine_node_config* pConfig, void* pHeap, ma_engine_node* pEngineNode); MA_API ma_result ma_engine_node_init(const ma_engine_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_engine_node* pEngineNode); MA_API void ma_engine_node_uninit(ma_engine_node* pEngineNode, const ma_allocation_callbacks* pAllocationCallbacks); #define MA_SOUND_SOURCE_CHANNEL_COUNT 0xFFFFFFFF typedef struct { const char* pFilePath; /* Set this to load from the resource manager. */ const wchar_t* pFilePathW; /* Set this to load from the resource manager. */ ma_data_source* pDataSource; /* Set this to load from an existing data source. */ ma_node* pInitialAttachment; /* If set, the sound will be attached to an input of this node. This can be set to a ma_sound. If set to NULL, the sound will be attached directly to the endpoint unless MA_SOUND_FLAG_NO_DEFAULT_ATTACHMENT is set in `flags`. */ ma_uint32 initialAttachmentInputBusIndex; /* The index of the input bus of pInitialAttachment to attach the sound to. */ ma_uint32 channelsIn; /* Ignored if using a data source as input (the data source's channel count will be used always). Otherwise, setting to 0 will cause the engine's channel count to be used. */ ma_uint32 channelsOut; /* Set this to 0 (default) to use the engine's channel count. Set to MA_SOUND_SOURCE_CHANNEL_COUNT to use the data source's channel count (only used if using a data source as input). */ ma_mono_expansion_mode monoExpansionMode; /* Controls how the mono channel should be expanded to other channels when spatialization is disabled on a sound. */ ma_uint32 flags; /* A combination of MA_SOUND_FLAG_* flags. */ ma_uint64 initialSeekPointInPCMFrames; /* Initializes the sound such that it's seeked to this location by default. */ ma_uint64 rangeBegInPCMFrames; ma_uint64 rangeEndInPCMFrames; ma_uint64 loopPointBegInPCMFrames; ma_uint64 loopPointEndInPCMFrames; ma_bool32 isLooping; ma_fence* pDoneFence; /* Released when the resource manager has finished decoding the entire sound. Not used with streams. */ } ma_sound_config; MA_API ma_sound_config ma_sound_config_init(void); /* Deprecated. Will be removed in version 0.12. Use ma_sound_config_2() instead. */ MA_API ma_sound_config ma_sound_config_init_2(ma_engine* pEngine); /* Will be renamed to ma_sound_config_init() in version 0.12. */ struct ma_sound { ma_engine_node engineNode; /* Must be the first member for compatibility with the ma_node API. */ ma_data_source* pDataSource; MA_ATOMIC(8, ma_uint64) seekTarget; /* The PCM frame index to seek to in the mixing thread. Set to (~(ma_uint64)0) to not perform any seeking. */ MA_ATOMIC(4, ma_bool32) atEnd; ma_bool8 ownsDataSource; /* We're declaring a resource manager data source object here to save us a malloc when loading a sound via the resource manager, which I *think* will be the most common scenario. */ #ifndef MA_NO_RESOURCE_MANAGER ma_resource_manager_data_source* pResourceManagerDataSource; #endif }; /* Structure specifically for sounds played with ma_engine_play_sound(). Making this a separate structure to reduce overhead. */ typedef struct ma_sound_inlined ma_sound_inlined; struct ma_sound_inlined { ma_sound sound; ma_sound_inlined* pNext; ma_sound_inlined* pPrev; }; /* A sound group is just a sound. */ typedef ma_sound_config ma_sound_group_config; typedef ma_sound ma_sound_group; MA_API ma_sound_group_config ma_sound_group_config_init(void); /* Deprecated. Will be removed in version 0.12. Use ma_sound_config_2() instead. */ MA_API ma_sound_group_config ma_sound_group_config_init_2(ma_engine* pEngine); /* Will be renamed to ma_sound_config_init() in version 0.12. */ typedef struct { #if !defined(MA_NO_RESOURCE_MANAGER) ma_resource_manager* pResourceManager; /* Can be null in which case a resource manager will be created for you. */ #endif #if !defined(MA_NO_DEVICE_IO) ma_context* pContext; ma_device* pDevice; /* If set, the caller is responsible for calling ma_engine_data_callback() in the device's data callback. */ ma_device_id* pPlaybackDeviceID; /* The ID of the playback device to use with the default listener. */ ma_device_notification_proc notificationCallback; #endif ma_log* pLog; /* When set to NULL, will use the context's log. */ ma_uint32 listenerCount; /* Must be between 1 and MA_ENGINE_MAX_LISTENERS. */ ma_uint32 channels; /* The number of channels to use when mixing and spatializing. When set to 0, will use the native channel count of the device. */ ma_uint32 sampleRate; /* The sample rate. When set to 0 will use the native channel count of the device. */ ma_uint32 periodSizeInFrames; /* If set to something other than 0, updates will always be exactly this size. The underlying device may be a different size, but from the perspective of the mixer that won't matter.*/ ma_uint32 periodSizeInMilliseconds; /* Used if periodSizeInFrames is unset. */ ma_uint32 gainSmoothTimeInFrames; /* The number of frames to interpolate the gain of spatialized sounds across. If set to 0, will use gainSmoothTimeInMilliseconds. */ ma_uint32 gainSmoothTimeInMilliseconds; /* When set to 0, gainSmoothTimeInFrames will be used. If both are set to 0, a default value will be used. */ ma_allocation_callbacks allocationCallbacks; ma_bool32 noAutoStart; /* When set to true, requires an explicit call to ma_engine_start(). This is false by default, meaning the engine will be started automatically in ma_engine_init(). */ ma_bool32 noDevice; /* When set to true, don't create a default device. ma_engine_read_pcm_frames() can be called manually to read data. */ ma_mono_expansion_mode monoExpansionMode; /* Controls how the mono channel should be expanded to other channels when spatialization is disabled on a sound. */ ma_vfs* pResourceManagerVFS; /* A pointer to a pre-allocated VFS object to use with the resource manager. This is ignored if pResourceManager is not NULL. */ } ma_engine_config; MA_API ma_engine_config ma_engine_config_init(void); struct ma_engine { ma_node_graph nodeGraph; /* An engine is a node graph. It should be able to be plugged into any ma_node_graph API (with a cast) which means this must be the first member of this struct. */ #if !defined(MA_NO_RESOURCE_MANAGER) ma_resource_manager* pResourceManager; #endif #if !defined(MA_NO_DEVICE_IO) ma_device* pDevice; /* Optionally set via the config, otherwise allocated by the engine in ma_engine_init(). */ #endif ma_log* pLog; ma_uint32 sampleRate; ma_uint32 listenerCount; ma_spatializer_listener listeners[MA_ENGINE_MAX_LISTENERS]; ma_allocation_callbacks allocationCallbacks; ma_bool8 ownsResourceManager; ma_bool8 ownsDevice; ma_spinlock inlinedSoundLock; /* For synchronizing access so the inlined sound list. */ ma_sound_inlined* pInlinedSoundHead; /* The first inlined sound. Inlined sounds are tracked in a linked list. */ MA_ATOMIC(4, ma_uint32) inlinedSoundCount; /* The total number of allocated inlined sound objects. Used for debugging. */ ma_uint32 gainSmoothTimeInFrames; /* The number of frames to interpolate the gain of spatialized sounds across. */ ma_mono_expansion_mode monoExpansionMode; }; MA_API ma_result ma_engine_init(const ma_engine_config* pConfig, ma_engine* pEngine); MA_API void ma_engine_uninit(ma_engine* pEngine); MA_API ma_result ma_engine_read_pcm_frames(ma_engine* pEngine, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); MA_API ma_node_graph* ma_engine_get_node_graph(ma_engine* pEngine); #if !defined(MA_NO_RESOURCE_MANAGER) MA_API ma_resource_manager* ma_engine_get_resource_manager(ma_engine* pEngine); #endif MA_API ma_device* ma_engine_get_device(ma_engine* pEngine); MA_API ma_log* ma_engine_get_log(ma_engine* pEngine); MA_API ma_node* ma_engine_get_endpoint(ma_engine* pEngine); MA_API ma_uint64 ma_engine_get_time(const ma_engine* pEngine); MA_API ma_result ma_engine_set_time(ma_engine* pEngine, ma_uint64 globalTime); MA_API ma_uint32 ma_engine_get_channels(const ma_engine* pEngine); MA_API ma_uint32 ma_engine_get_sample_rate(const ma_engine* pEngine); MA_API ma_result ma_engine_start(ma_engine* pEngine); MA_API ma_result ma_engine_stop(ma_engine* pEngine); MA_API ma_result ma_engine_set_volume(ma_engine* pEngine, float volume); MA_API ma_result ma_engine_set_gain_db(ma_engine* pEngine, float gainDB); MA_API ma_uint32 ma_engine_get_listener_count(const ma_engine* pEngine); MA_API ma_uint32 ma_engine_find_closest_listener(const ma_engine* pEngine, float absolutePosX, float absolutePosY, float absolutePosZ); MA_API void ma_engine_listener_set_position(ma_engine* pEngine, ma_uint32 listenerIndex, float x, float y, float z); MA_API ma_vec3f ma_engine_listener_get_position(const ma_engine* pEngine, ma_uint32 listenerIndex); MA_API void ma_engine_listener_set_direction(ma_engine* pEngine, ma_uint32 listenerIndex, float x, float y, float z); MA_API ma_vec3f ma_engine_listener_get_direction(const ma_engine* pEngine, ma_uint32 listenerIndex); MA_API void ma_engine_listener_set_velocity(ma_engine* pEngine, ma_uint32 listenerIndex, float x, float y, float z); MA_API ma_vec3f ma_engine_listener_get_velocity(const ma_engine* pEngine, ma_uint32 listenerIndex); MA_API void ma_engine_listener_set_cone(ma_engine* pEngine, ma_uint32 listenerIndex, float innerAngleInRadians, float outerAngleInRadians, float outerGain); MA_API void ma_engine_listener_get_cone(const ma_engine* pEngine, ma_uint32 listenerIndex, float* pInnerAngleInRadians, float* pOuterAngleInRadians, float* pOuterGain); MA_API void ma_engine_listener_set_world_up(ma_engine* pEngine, ma_uint32 listenerIndex, float x, float y, float z); MA_API ma_vec3f ma_engine_listener_get_world_up(const ma_engine* pEngine, ma_uint32 listenerIndex); MA_API void ma_engine_listener_set_enabled(ma_engine* pEngine, ma_uint32 listenerIndex, ma_bool32 isEnabled); MA_API ma_bool32 ma_engine_listener_is_enabled(const ma_engine* pEngine, ma_uint32 listenerIndex); #ifndef MA_NO_RESOURCE_MANAGER MA_API ma_result ma_engine_play_sound_ex(ma_engine* pEngine, const char* pFilePath, ma_node* pNode, ma_uint32 nodeInputBusIndex); MA_API ma_result ma_engine_play_sound(ma_engine* pEngine, const char* pFilePath, ma_sound_group* pGroup); /* Fire and forget. */ #endif #ifndef MA_NO_RESOURCE_MANAGER MA_API ma_result ma_sound_init_from_file(ma_engine* pEngine, const char* pFilePath, ma_uint32 flags, ma_sound_group* pGroup, ma_fence* pDoneFence, ma_sound* pSound); MA_API ma_result ma_sound_init_from_file_w(ma_engine* pEngine, const wchar_t* pFilePath, ma_uint32 flags, ma_sound_group* pGroup, ma_fence* pDoneFence, ma_sound* pSound); MA_API ma_result ma_sound_init_copy(ma_engine* pEngine, const ma_sound* pExistingSound, ma_uint32 flags, ma_sound_group* pGroup, ma_sound* pSound); #endif MA_API ma_result ma_sound_init_from_data_source(ma_engine* pEngine, ma_data_source* pDataSource, ma_uint32 flags, ma_sound_group* pGroup, ma_sound* pSound); MA_API ma_result ma_sound_init_ex(ma_engine* pEngine, const ma_sound_config* pConfig, ma_sound* pSound); MA_API void ma_sound_uninit(ma_sound* pSound); MA_API ma_engine* ma_sound_get_engine(const ma_sound* pSound); MA_API ma_data_source* ma_sound_get_data_source(const ma_sound* pSound); MA_API ma_result ma_sound_start(ma_sound* pSound); MA_API ma_result ma_sound_stop(ma_sound* pSound); MA_API void ma_sound_set_volume(ma_sound* pSound, float volume); MA_API float ma_sound_get_volume(const ma_sound* pSound); MA_API void ma_sound_set_pan(ma_sound* pSound, float pan); MA_API float ma_sound_get_pan(const ma_sound* pSound); MA_API void ma_sound_set_pan_mode(ma_sound* pSound, ma_pan_mode panMode); MA_API ma_pan_mode ma_sound_get_pan_mode(const ma_sound* pSound); MA_API void ma_sound_set_pitch(ma_sound* pSound, float pitch); MA_API float ma_sound_get_pitch(const ma_sound* pSound); MA_API void ma_sound_set_spatialization_enabled(ma_sound* pSound, ma_bool32 enabled); MA_API ma_bool32 ma_sound_is_spatialization_enabled(const ma_sound* pSound); MA_API void ma_sound_set_pinned_listener_index(ma_sound* pSound, ma_uint32 listenerIndex); MA_API ma_uint32 ma_sound_get_pinned_listener_index(const ma_sound* pSound); MA_API ma_uint32 ma_sound_get_listener_index(const ma_sound* pSound); MA_API ma_vec3f ma_sound_get_direction_to_listener(const ma_sound* pSound); MA_API void ma_sound_set_position(ma_sound* pSound, float x, float y, float z); MA_API ma_vec3f ma_sound_get_position(const ma_sound* pSound); MA_API void ma_sound_set_direction(ma_sound* pSound, float x, float y, float z); MA_API ma_vec3f ma_sound_get_direction(const ma_sound* pSound); MA_API void ma_sound_set_velocity(ma_sound* pSound, float x, float y, float z); MA_API ma_vec3f ma_sound_get_velocity(const ma_sound* pSound); MA_API void ma_sound_set_attenuation_model(ma_sound* pSound, ma_attenuation_model attenuationModel); MA_API ma_attenuation_model ma_sound_get_attenuation_model(const ma_sound* pSound); MA_API void ma_sound_set_positioning(ma_sound* pSound, ma_positioning positioning); MA_API ma_positioning ma_sound_get_positioning(const ma_sound* pSound); MA_API void ma_sound_set_rolloff(ma_sound* pSound, float rolloff); MA_API float ma_sound_get_rolloff(const ma_sound* pSound); MA_API void ma_sound_set_min_gain(ma_sound* pSound, float minGain); MA_API float ma_sound_get_min_gain(const ma_sound* pSound); MA_API void ma_sound_set_max_gain(ma_sound* pSound, float maxGain); MA_API float ma_sound_get_max_gain(const ma_sound* pSound); MA_API void ma_sound_set_min_distance(ma_sound* pSound, float minDistance); MA_API float ma_sound_get_min_distance(const ma_sound* pSound); MA_API void ma_sound_set_max_distance(ma_sound* pSound, float maxDistance); MA_API float ma_sound_get_max_distance(const ma_sound* pSound); MA_API void ma_sound_set_cone(ma_sound* pSound, float innerAngleInRadians, float outerAngleInRadians, float outerGain); MA_API void ma_sound_get_cone(const ma_sound* pSound, float* pInnerAngleInRadians, float* pOuterAngleInRadians, float* pOuterGain); MA_API void ma_sound_set_doppler_factor(ma_sound* pSound, float dopplerFactor); MA_API float ma_sound_get_doppler_factor(const ma_sound* pSound); MA_API void ma_sound_set_directional_attenuation_factor(ma_sound* pSound, float directionalAttenuationFactor); MA_API float ma_sound_get_directional_attenuation_factor(const ma_sound* pSound); MA_API void ma_sound_set_fade_in_pcm_frames(ma_sound* pSound, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInFrames); MA_API void ma_sound_set_fade_in_milliseconds(ma_sound* pSound, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInMilliseconds); MA_API float ma_sound_get_current_fade_volume(ma_sound* pSound); MA_API void ma_sound_set_start_time_in_pcm_frames(ma_sound* pSound, ma_uint64 absoluteGlobalTimeInFrames); MA_API void ma_sound_set_start_time_in_milliseconds(ma_sound* pSound, ma_uint64 absoluteGlobalTimeInMilliseconds); MA_API void ma_sound_set_stop_time_in_pcm_frames(ma_sound* pSound, ma_uint64 absoluteGlobalTimeInFrames); MA_API void ma_sound_set_stop_time_in_milliseconds(ma_sound* pSound, ma_uint64 absoluteGlobalTimeInMilliseconds); MA_API ma_bool32 ma_sound_is_playing(const ma_sound* pSound); MA_API ma_uint64 ma_sound_get_time_in_pcm_frames(const ma_sound* pSound); MA_API void ma_sound_set_looping(ma_sound* pSound, ma_bool32 isLooping); MA_API ma_bool32 ma_sound_is_looping(const ma_sound* pSound); MA_API ma_bool32 ma_sound_at_end(const ma_sound* pSound); MA_API ma_result ma_sound_seek_to_pcm_frame(ma_sound* pSound, ma_uint64 frameIndex); /* Just a wrapper around ma_data_source_seek_to_pcm_frame(). */ MA_API ma_result ma_sound_get_data_format(ma_sound* pSound, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap); MA_API ma_result ma_sound_get_cursor_in_pcm_frames(ma_sound* pSound, ma_uint64* pCursor); MA_API ma_result ma_sound_get_length_in_pcm_frames(ma_sound* pSound, ma_uint64* pLength); MA_API ma_result ma_sound_get_cursor_in_seconds(ma_sound* pSound, float* pCursor); MA_API ma_result ma_sound_get_length_in_seconds(ma_sound* pSound, float* pLength); MA_API ma_result ma_sound_group_init(ma_engine* pEngine, ma_uint32 flags, ma_sound_group* pParentGroup, ma_sound_group* pGroup); MA_API ma_result ma_sound_group_init_ex(ma_engine* pEngine, const ma_sound_group_config* pConfig, ma_sound_group* pGroup); MA_API void ma_sound_group_uninit(ma_sound_group* pGroup); MA_API ma_engine* ma_sound_group_get_engine(const ma_sound_group* pGroup); MA_API ma_result ma_sound_group_start(ma_sound_group* pGroup); MA_API ma_result ma_sound_group_stop(ma_sound_group* pGroup); MA_API void ma_sound_group_set_volume(ma_sound_group* pGroup, float volume); MA_API float ma_sound_group_get_volume(const ma_sound_group* pGroup); MA_API void ma_sound_group_set_pan(ma_sound_group* pGroup, float pan); MA_API float ma_sound_group_get_pan(const ma_sound_group* pGroup); MA_API void ma_sound_group_set_pan_mode(ma_sound_group* pGroup, ma_pan_mode panMode); MA_API ma_pan_mode ma_sound_group_get_pan_mode(const ma_sound_group* pGroup); MA_API void ma_sound_group_set_pitch(ma_sound_group* pGroup, float pitch); MA_API float ma_sound_group_get_pitch(const ma_sound_group* pGroup); MA_API void ma_sound_group_set_spatialization_enabled(ma_sound_group* pGroup, ma_bool32 enabled); MA_API ma_bool32 ma_sound_group_is_spatialization_enabled(const ma_sound_group* pGroup); MA_API void ma_sound_group_set_pinned_listener_index(ma_sound_group* pGroup, ma_uint32 listenerIndex); MA_API ma_uint32 ma_sound_group_get_pinned_listener_index(const ma_sound_group* pGroup); MA_API ma_uint32 ma_sound_group_get_listener_index(const ma_sound_group* pGroup); MA_API ma_vec3f ma_sound_group_get_direction_to_listener(const ma_sound_group* pGroup); MA_API void ma_sound_group_set_position(ma_sound_group* pGroup, float x, float y, float z); MA_API ma_vec3f ma_sound_group_get_position(const ma_sound_group* pGroup); MA_API void ma_sound_group_set_direction(ma_sound_group* pGroup, float x, float y, float z); MA_API ma_vec3f ma_sound_group_get_direction(const ma_sound_group* pGroup); MA_API void ma_sound_group_set_velocity(ma_sound_group* pGroup, float x, float y, float z); MA_API ma_vec3f ma_sound_group_get_velocity(const ma_sound_group* pGroup); MA_API void ma_sound_group_set_attenuation_model(ma_sound_group* pGroup, ma_attenuation_model attenuationModel); MA_API ma_attenuation_model ma_sound_group_get_attenuation_model(const ma_sound_group* pGroup); MA_API void ma_sound_group_set_positioning(ma_sound_group* pGroup, ma_positioning positioning); MA_API ma_positioning ma_sound_group_get_positioning(const ma_sound_group* pGroup); MA_API void ma_sound_group_set_rolloff(ma_sound_group* pGroup, float rolloff); MA_API float ma_sound_group_get_rolloff(const ma_sound_group* pGroup); MA_API void ma_sound_group_set_min_gain(ma_sound_group* pGroup, float minGain); MA_API float ma_sound_group_get_min_gain(const ma_sound_group* pGroup); MA_API void ma_sound_group_set_max_gain(ma_sound_group* pGroup, float maxGain); MA_API float ma_sound_group_get_max_gain(const ma_sound_group* pGroup); MA_API void ma_sound_group_set_min_distance(ma_sound_group* pGroup, float minDistance); MA_API float ma_sound_group_get_min_distance(const ma_sound_group* pGroup); MA_API void ma_sound_group_set_max_distance(ma_sound_group* pGroup, float maxDistance); MA_API float ma_sound_group_get_max_distance(const ma_sound_group* pGroup); MA_API void ma_sound_group_set_cone(ma_sound_group* pGroup, float innerAngleInRadians, float outerAngleInRadians, float outerGain); MA_API void ma_sound_group_get_cone(const ma_sound_group* pGroup, float* pInnerAngleInRadians, float* pOuterAngleInRadians, float* pOuterGain); MA_API void ma_sound_group_set_doppler_factor(ma_sound_group* pGroup, float dopplerFactor); MA_API float ma_sound_group_get_doppler_factor(const ma_sound_group* pGroup); MA_API void ma_sound_group_set_directional_attenuation_factor(ma_sound_group* pGroup, float directionalAttenuationFactor); MA_API float ma_sound_group_get_directional_attenuation_factor(const ma_sound_group* pGroup); MA_API void ma_sound_group_set_fade_in_pcm_frames(ma_sound_group* pGroup, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInFrames); MA_API void ma_sound_group_set_fade_in_milliseconds(ma_sound_group* pGroup, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInMilliseconds); MA_API float ma_sound_group_get_current_fade_volume(ma_sound_group* pGroup); MA_API void ma_sound_group_set_start_time_in_pcm_frames(ma_sound_group* pGroup, ma_uint64 absoluteGlobalTimeInFrames); MA_API void ma_sound_group_set_start_time_in_milliseconds(ma_sound_group* pGroup, ma_uint64 absoluteGlobalTimeInMilliseconds); MA_API void ma_sound_group_set_stop_time_in_pcm_frames(ma_sound_group* pGroup, ma_uint64 absoluteGlobalTimeInFrames); MA_API void ma_sound_group_set_stop_time_in_milliseconds(ma_sound_group* pGroup, ma_uint64 absoluteGlobalTimeInMilliseconds); MA_API ma_bool32 ma_sound_group_is_playing(const ma_sound_group* pGroup); MA_API ma_uint64 ma_sound_group_get_time_in_pcm_frames(const ma_sound_group* pGroup); #endif /* MA_NO_ENGINE */ /* END SECTION: miniaudio_engine.h */ #ifdef __cplusplus } #endif #endif /* miniaudio_h */ /* This is for preventing greying out of the implementation section. */ #if defined(Q_CREATOR_RUN) || defined(__INTELLISENSE__) || defined(__CDT_PARSER__) #define MINIAUDIO_IMPLEMENTATION #endif /************************************************************************************************************************************************************ ************************************************************************************************************************************************************* IMPLEMENTATION ************************************************************************************************************************************************************* ************************************************************************************************************************************************************/ #if defined(MINIAUDIO_IMPLEMENTATION) || defined(MA_IMPLEMENTATION) #ifndef miniaudio_c #define miniaudio_c #include #include /* For INT_MAX */ #include /* sin(), etc. */ #include #include #if !defined(_MSC_VER) && !defined(__DMC__) #include /* For strcasecmp(). */ #include /* For wcslen(), wcsrtombs() */ #endif #ifdef _MSC_VER #include /* For _controlfp_s constants */ #endif #ifdef MA_WIN32 #include #else #include /* For malloc(), free(), wcstombs(). */ #include /* For memset() */ #include #include /* select() (used for ma_sleep()). */ #include #endif #include /* For fstat(), etc. */ #ifdef MA_EMSCRIPTEN #include #endif #if !defined(MA_64BIT) && !defined(MA_32BIT) #ifdef _WIN32 #ifdef _WIN64 #define MA_64BIT #else #define MA_32BIT #endif #endif #endif #if !defined(MA_64BIT) && !defined(MA_32BIT) #ifdef __GNUC__ #ifdef __LP64__ #define MA_64BIT #else #define MA_32BIT #endif #endif #endif #if !defined(MA_64BIT) && !defined(MA_32BIT) #include #if INTPTR_MAX == INT64_MAX #define MA_64BIT #else #define MA_32BIT #endif #endif /* Architecture Detection */ #if defined(__x86_64__) || defined(_M_X64) #define MA_X64 #elif defined(__i386) || defined(_M_IX86) #define MA_X86 #elif defined(__arm__) || defined(_M_ARM) || defined(__arm64) || defined(__arm64__) || defined(__aarch64__) || defined(_M_ARM64) #define MA_ARM #endif /* Intrinsics Support */ #if defined(MA_X64) || defined(MA_X86) #if defined(_MSC_VER) && !defined(__clang__) /* MSVC. */ #if _MSC_VER >= 1400 && !defined(MA_NO_SSE2) /* 2005 */ #define MA_SUPPORT_SSE2 #endif /*#if _MSC_VER >= 1600 && !defined(MA_NO_AVX)*/ /* 2010 */ /* #define MA_SUPPORT_AVX*/ /*#endif*/ #if _MSC_VER >= 1700 && !defined(MA_NO_AVX2) /* 2012 */ #define MA_SUPPORT_AVX2 #endif #else /* Assume GNUC-style. */ #if defined(__SSE2__) && !defined(MA_NO_SSE2) #define MA_SUPPORT_SSE2 #endif /*#if defined(__AVX__) && !defined(MA_NO_AVX)*/ /* #define MA_SUPPORT_AVX*/ /*#endif*/ #if defined(__AVX2__) && !defined(MA_NO_AVX2) #define MA_SUPPORT_AVX2 #endif #endif /* If at this point we still haven't determined compiler support for the intrinsics just fall back to __has_include. */ #if !defined(__GNUC__) && !defined(__clang__) && defined(__has_include) #if !defined(MA_SUPPORT_SSE2) && !defined(MA_NO_SSE2) && __has_include() #define MA_SUPPORT_SSE2 #endif /*#if !defined(MA_SUPPORT_AVX) && !defined(MA_NO_AVX) && __has_include()*/ /* #define MA_SUPPORT_AVX*/ /*#endif*/ #if !defined(MA_SUPPORT_AVX2) && !defined(MA_NO_AVX2) && __has_include() #define MA_SUPPORT_AVX2 #endif #endif #if defined(MA_SUPPORT_AVX2) || defined(MA_SUPPORT_AVX) #include #elif defined(MA_SUPPORT_SSE2) #include #endif #endif #if defined(MA_ARM) #if !defined(MA_NO_NEON) && (defined(__ARM_NEON) || defined(__aarch64__) || defined(_M_ARM64)) #define MA_SUPPORT_NEON #include #endif #endif /* Begin globally disabled warnings. */ #if defined(_MSC_VER) #pragma warning(push) #pragma warning(disable:4752) /* found Intel(R) Advanced Vector Extensions; consider using /arch:AVX */ #pragma warning(disable:4049) /* compiler limit : terminating line number emission */ #endif #if defined(MA_X64) || defined(MA_X86) #if defined(_MSC_VER) && !defined(__clang__) #if _MSC_VER >= 1400 #include static MA_INLINE void ma_cpuid(int info[4], int fid) { __cpuid(info, fid); } #else #define MA_NO_CPUID #endif #if _MSC_VER >= 1600 && (defined(_MSC_FULL_VER) && _MSC_FULL_VER >= 160040219) static MA_INLINE unsigned __int64 ma_xgetbv(int reg) { return _xgetbv(reg); } #else #define MA_NO_XGETBV #endif #elif (defined(__GNUC__) || defined(__clang__)) && !defined(MA_ANDROID) static MA_INLINE void ma_cpuid(int info[4], int fid) { /* It looks like the -fPIC option uses the ebx register which GCC complains about. We can work around this by just using a different register, the specific register of which I'm letting the compiler decide on. The "k" prefix is used to specify a 32-bit register. The {...} syntax is for supporting different assembly dialects. What's basically happening is that we're saving and restoring the ebx register manually. */ #if defined(DRFLAC_X86) && defined(__PIC__) __asm__ __volatile__ ( "xchg{l} {%%}ebx, %k1;" "cpuid;" "xchg{l} {%%}ebx, %k1;" : "=a"(info[0]), "=&r"(info[1]), "=c"(info[2]), "=d"(info[3]) : "a"(fid), "c"(0) ); #else __asm__ __volatile__ ( "cpuid" : "=a"(info[0]), "=b"(info[1]), "=c"(info[2]), "=d"(info[3]) : "a"(fid), "c"(0) ); #endif } static MA_INLINE ma_uint64 ma_xgetbv(int reg) { unsigned int hi; unsigned int lo; __asm__ __volatile__ ( "xgetbv" : "=a"(lo), "=d"(hi) : "c"(reg) ); return ((ma_uint64)hi << 32) | (ma_uint64)lo; } #else #define MA_NO_CPUID #define MA_NO_XGETBV #endif #else #define MA_NO_CPUID #define MA_NO_XGETBV #endif static MA_INLINE ma_bool32 ma_has_sse2(void) { #if defined(MA_SUPPORT_SSE2) #if (defined(MA_X64) || defined(MA_X86)) && !defined(MA_NO_SSE2) #if defined(MA_X64) return MA_TRUE; /* 64-bit targets always support SSE2. */ #elif (defined(_M_IX86_FP) && _M_IX86_FP == 2) || defined(__SSE2__) return MA_TRUE; /* If the compiler is allowed to freely generate SSE2 code we can assume support. */ #else #if defined(MA_NO_CPUID) return MA_FALSE; #else int info[4]; ma_cpuid(info, 1); return (info[3] & (1 << 26)) != 0; #endif #endif #else return MA_FALSE; /* SSE2 is only supported on x86 and x64 architectures. */ #endif #else return MA_FALSE; /* No compiler support. */ #endif } #if 0 static MA_INLINE ma_bool32 ma_has_avx() { #if defined(MA_SUPPORT_AVX) #if (defined(MA_X64) || defined(MA_X86)) && !defined(MA_NO_AVX) #if defined(_AVX_) || defined(__AVX__) return MA_TRUE; /* If the compiler is allowed to freely generate AVX code we can assume support. */ #else /* AVX requires both CPU and OS support. */ #if defined(MA_NO_CPUID) || defined(MA_NO_XGETBV) return MA_FALSE; #else int info[4]; ma_cpuid(info, 1); if (((info[2] & (1 << 27)) != 0) && ((info[2] & (1 << 28)) != 0)) { ma_uint64 xrc = ma_xgetbv(0); if ((xrc & 0x06) == 0x06) { return MA_TRUE; } else { return MA_FALSE; } } else { return MA_FALSE; } #endif #endif #else return MA_FALSE; /* AVX is only supported on x86 and x64 architectures. */ #endif #else return MA_FALSE; /* No compiler support. */ #endif } #endif static MA_INLINE ma_bool32 ma_has_avx2(void) { #if defined(MA_SUPPORT_AVX2) #if (defined(MA_X64) || defined(MA_X86)) && !defined(MA_NO_AVX2) #if defined(_AVX2_) || defined(__AVX2__) return MA_TRUE; /* If the compiler is allowed to freely generate AVX2 code we can assume support. */ #else /* AVX2 requires both CPU and OS support. */ #if defined(MA_NO_CPUID) || defined(MA_NO_XGETBV) return MA_FALSE; #else int info1[4]; int info7[4]; ma_cpuid(info1, 1); ma_cpuid(info7, 7); if (((info1[2] & (1 << 27)) != 0) && ((info7[1] & (1 << 5)) != 0)) { ma_uint64 xrc = ma_xgetbv(0); if ((xrc & 0x06) == 0x06) { return MA_TRUE; } else { return MA_FALSE; } } else { return MA_FALSE; } #endif #endif #else return MA_FALSE; /* AVX2 is only supported on x86 and x64 architectures. */ #endif #else return MA_FALSE; /* No compiler support. */ #endif } static MA_INLINE ma_bool32 ma_has_neon(void) { #if defined(MA_SUPPORT_NEON) #if defined(MA_ARM) && !defined(MA_NO_NEON) #if (defined(__ARM_NEON) || defined(__aarch64__) || defined(_M_ARM64)) return MA_TRUE; /* If the compiler is allowed to freely generate NEON code we can assume support. */ #else /* TODO: Runtime check. */ return MA_FALSE; #endif #else return MA_FALSE; /* NEON is only supported on ARM architectures. */ #endif #else return MA_FALSE; /* No compiler support. */ #endif } #define MA_SIMD_NONE 0 #define MA_SIMD_SSE2 1 #define MA_SIMD_AVX2 2 #define MA_SIMD_NEON 3 #ifndef MA_PREFERRED_SIMD # if defined(MA_SUPPORT_SSE2) && defined(MA_PREFER_SSE2) #define MA_PREFERRED_SIMD MA_SIMD_SSE2 #elif defined(MA_SUPPORT_AVX2) && defined(MA_PREFER_AVX2) #define MA_PREFERRED_SIMD MA_SIMD_AVX2 #elif defined(MA_SUPPORT_NEON) && defined(MA_PREFER_NEON) #define MA_PREFERRED_SIMD MA_SIMD_NEON #else #define MA_PREFERRED_SIMD MA_SIMD_NONE #endif #endif #if defined(__has_builtin) #define MA_COMPILER_HAS_BUILTIN(x) __has_builtin(x) #else #define MA_COMPILER_HAS_BUILTIN(x) 0 #endif #ifndef MA_ASSUME #if MA_COMPILER_HAS_BUILTIN(__builtin_assume) #define MA_ASSUME(x) __builtin_assume(x) #elif MA_COMPILER_HAS_BUILTIN(__builtin_unreachable) #define MA_ASSUME(x) do { if (!(x)) __builtin_unreachable(); } while (0) #elif defined(_MSC_VER) #define MA_ASSUME(x) __assume(x) #else #define MA_ASSUME(x) (void)(x) #endif #endif #ifndef MA_RESTRICT #if defined(__clang__) || defined(__GNUC__) || defined(_MSC_VER) #define MA_RESTRICT __restrict #else #define MA_RESTRICT #endif #endif #if defined(_MSC_VER) && _MSC_VER >= 1400 #define MA_HAS_BYTESWAP16_INTRINSIC #define MA_HAS_BYTESWAP32_INTRINSIC #define MA_HAS_BYTESWAP64_INTRINSIC #elif defined(__clang__) #if MA_COMPILER_HAS_BUILTIN(__builtin_bswap16) #define MA_HAS_BYTESWAP16_INTRINSIC #endif #if MA_COMPILER_HAS_BUILTIN(__builtin_bswap32) #define MA_HAS_BYTESWAP32_INTRINSIC #endif #if MA_COMPILER_HAS_BUILTIN(__builtin_bswap64) #define MA_HAS_BYTESWAP64_INTRINSIC #endif #elif defined(__GNUC__) #if ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3)) #define MA_HAS_BYTESWAP32_INTRINSIC #define MA_HAS_BYTESWAP64_INTRINSIC #endif #if ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)) #define MA_HAS_BYTESWAP16_INTRINSIC #endif #endif static MA_INLINE ma_bool32 ma_is_little_endian(void) { #if defined(MA_X86) || defined(MA_X64) return MA_TRUE; #else int n = 1; return (*(char*)&n) == 1; #endif } static MA_INLINE ma_bool32 ma_is_big_endian(void) { return !ma_is_little_endian(); } static MA_INLINE ma_uint32 ma_swap_endian_uint32(ma_uint32 n) { #ifdef MA_HAS_BYTESWAP32_INTRINSIC #if defined(_MSC_VER) return _byteswap_ulong(n); #elif defined(__GNUC__) || defined(__clang__) #if defined(MA_ARM) && (defined(__ARM_ARCH) && __ARM_ARCH >= 6) && !defined(MA_64BIT) /* <-- 64-bit inline assembly has not been tested, so disabling for now. */ /* Inline assembly optimized implementation for ARM. In my testing, GCC does not generate optimized code with __builtin_bswap32(). */ ma_uint32 r; __asm__ __volatile__ ( #if defined(MA_64BIT) "rev %w[out], %w[in]" : [out]"=r"(r) : [in]"r"(n) /* <-- This is untested. If someone in the community could test this, that would be appreciated! */ #else "rev %[out], %[in]" : [out]"=r"(r) : [in]"r"(n) #endif ); return r; #else return __builtin_bswap32(n); #endif #else #error "This compiler does not support the byte swap intrinsic." #endif #else return ((n & 0xFF000000) >> 24) | ((n & 0x00FF0000) >> 8) | ((n & 0x0000FF00) << 8) | ((n & 0x000000FF) << 24); #endif } #if !defined(MA_EMSCRIPTEN) #ifdef MA_WIN32 static void ma_sleep__win32(ma_uint32 milliseconds) { Sleep((DWORD)milliseconds); } #endif #ifdef MA_POSIX static void ma_sleep__posix(ma_uint32 milliseconds) { #ifdef MA_EMSCRIPTEN (void)milliseconds; MA_ASSERT(MA_FALSE); /* The Emscripten build should never sleep. */ #else #if defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 199309L struct timespec ts; ts.tv_sec = milliseconds / 1000; ts.tv_nsec = milliseconds % 1000 * 1000000; nanosleep(&ts, NULL); #else struct timeval tv; tv.tv_sec = milliseconds / 1000; tv.tv_usec = milliseconds % 1000 * 1000; select(0, NULL, NULL, NULL, &tv); #endif #endif } #endif static MA_INLINE void ma_sleep(ma_uint32 milliseconds) { #ifdef MA_WIN32 ma_sleep__win32(milliseconds); #endif #ifdef MA_POSIX ma_sleep__posix(milliseconds); #endif } #endif static MA_INLINE void ma_yield() { #if defined(__i386) || defined(_M_IX86) || defined(__x86_64__) || defined(_M_X64) /* x86/x64 */ #if (defined(_MSC_VER) || defined(__WATCOMC__) || defined(__DMC__)) && !defined(__clang__) #if _MSC_VER >= 1400 _mm_pause(); #else #if defined(__DMC__) /* Digital Mars does not recognize the PAUSE opcode. Fall back to NOP. */ __asm nop; #else __asm pause; #endif #endif #else __asm__ __volatile__ ("pause"); #endif #elif (defined(__arm__) && defined(__ARM_ARCH) && __ARM_ARCH >= 7) || defined(_M_ARM64) || (defined(_M_ARM) && _M_ARM >= 7) || defined(__ARM_ARCH_6K__) || defined(__ARM_ARCH_6T2__) /* ARM */ #if defined(_MSC_VER) /* Apparently there is a __yield() intrinsic that's compatible with ARM, but I cannot find documentation for it nor can I find where it's declared. */ __yield(); #else __asm__ __volatile__ ("yield"); /* ARMv6K/ARMv6T2 and above. */ #endif #else /* Unknown or unsupported architecture. No-op. */ #endif } #define MA_MM_DENORMALS_ZERO_MASK 0x0040 #define MA_MM_FLUSH_ZERO_MASK 0x8000 static MA_INLINE unsigned int ma_disable_denormals() { unsigned int prevState; #if defined(_MSC_VER) { /* Older versions of Visual Studio don't support the "safe" versions of _controlfp_s(). I don't know which version of Visual Studio first added support for _controlfp_s(), but I do know that VC6 lacks support. _MSC_VER = 1200 is VC6, but if you get compilation errors on older versions of Visual Studio, let me know and I'll make the necessary adjustment. */ #if _MSC_VER <= 1200 { prevState = _statusfp(); _controlfp(prevState | _DN_FLUSH, _MCW_DN); } #else { unsigned int unused; _controlfp_s(&prevState, 0, 0); _controlfp_s(&unused, prevState | _DN_FLUSH, _MCW_DN); } #endif } #elif defined(MA_X86) || defined(MA_X64) { #if defined(__SSE2__) && !(defined(__TINYC__) || defined(__WATCOMC__)) /* <-- Add compilers that lack support for _mm_getcsr() and _mm_setcsr() to this list. */ { prevState = _mm_getcsr(); _mm_setcsr(prevState | MA_MM_DENORMALS_ZERO_MASK | MA_MM_FLUSH_ZERO_MASK); } #else { /* x88/64, but no support for _mm_getcsr()/_mm_setcsr(). May need to fall back to inlined assembly here. */ prevState = 0; } #endif } #else { /* Unknown or unsupported architecture. No-op. */ prevState = 0; } #endif return prevState; } static MA_INLINE void ma_restore_denormals(unsigned int prevState) { #if defined(_MSC_VER) { /* Older versions of Visual Studio do not support _controlfp_s(). See ma_disable_denormals(). */ #if _MSC_VER <= 1200 { _controlfp(prevState, _MCW_DN); } #else { unsigned int unused; _controlfp_s(&unused, prevState, _MCW_DN); } #endif } #elif defined(MA_X86) || defined(MA_X64) { #if defined(__SSE2__) && !(defined(__TINYC__) || defined(__WATCOMC__)) /* <-- Add compilers that lack support for _mm_getcsr() and _mm_setcsr() to this list. */ { _mm_setcsr(prevState); } #else { /* x88/64, but no support for _mm_getcsr()/_mm_setcsr(). May need to fall back to inlined assembly here. */ (void)prevState; } #endif } #else { /* Unknown or unsupported architecture. No-op. */ (void)prevState; } #endif } #ifndef MA_COINIT_VALUE #define MA_COINIT_VALUE 0 /* 0 = COINIT_MULTITHREADED */ #endif #ifndef MA_FLT_MAX #ifdef FLT_MAX #define MA_FLT_MAX FLT_MAX #else #define MA_FLT_MAX 3.402823466e+38F #endif #endif #ifndef MA_PI #define MA_PI 3.14159265358979323846264f #endif #ifndef MA_PI_D #define MA_PI_D 3.14159265358979323846264 #endif #ifndef MA_TAU #define MA_TAU 6.28318530717958647693f #endif #ifndef MA_TAU_D #define MA_TAU_D 6.28318530717958647693 #endif /* The default format when ma_format_unknown (0) is requested when initializing a device. */ #ifndef MA_DEFAULT_FORMAT #define MA_DEFAULT_FORMAT ma_format_f32 #endif /* The default channel count to use when 0 is used when initializing a device. */ #ifndef MA_DEFAULT_CHANNELS #define MA_DEFAULT_CHANNELS 2 #endif /* The default sample rate to use when 0 is used when initializing a device. */ #ifndef MA_DEFAULT_SAMPLE_RATE #define MA_DEFAULT_SAMPLE_RATE 48000 #endif /* Default periods when none is specified in ma_device_init(). More periods means more work on the CPU. */ #ifndef MA_DEFAULT_PERIODS #define MA_DEFAULT_PERIODS 3 #endif /* The default period size in milliseconds for low latency mode. */ #ifndef MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_LOW_LATENCY #define MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_LOW_LATENCY 10 #endif /* The default buffer size in milliseconds for conservative mode. */ #ifndef MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_CONSERVATIVE #define MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_CONSERVATIVE 100 #endif /* The default LPF filter order for linear resampling. Note that this is clamped to MA_MAX_FILTER_ORDER. */ #ifndef MA_DEFAULT_RESAMPLER_LPF_ORDER #if MA_MAX_FILTER_ORDER >= 4 #define MA_DEFAULT_RESAMPLER_LPF_ORDER 4 #else #define MA_DEFAULT_RESAMPLER_LPF_ORDER MA_MAX_FILTER_ORDER #endif #endif #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wunused-variable" #endif /* Standard sample rates, in order of priority. */ static ma_uint32 g_maStandardSampleRatePriorities[] = { (ma_uint32)ma_standard_sample_rate_48000, (ma_uint32)ma_standard_sample_rate_44100, (ma_uint32)ma_standard_sample_rate_32000, (ma_uint32)ma_standard_sample_rate_24000, (ma_uint32)ma_standard_sample_rate_22050, (ma_uint32)ma_standard_sample_rate_88200, (ma_uint32)ma_standard_sample_rate_96000, (ma_uint32)ma_standard_sample_rate_176400, (ma_uint32)ma_standard_sample_rate_192000, (ma_uint32)ma_standard_sample_rate_16000, (ma_uint32)ma_standard_sample_rate_11025, (ma_uint32)ma_standard_sample_rate_8000, (ma_uint32)ma_standard_sample_rate_352800, (ma_uint32)ma_standard_sample_rate_384000 }; static MA_INLINE ma_bool32 ma_is_standard_sample_rate(ma_uint32 sampleRate) { ma_uint32 iSampleRate; for (iSampleRate = 0; iSampleRate < sizeof(g_maStandardSampleRatePriorities) / sizeof(g_maStandardSampleRatePriorities[0]); iSampleRate += 1) { if (g_maStandardSampleRatePriorities[iSampleRate] == sampleRate) { return MA_TRUE; } } /* Getting here means the sample rate is not supported. */ return MA_FALSE; } static ma_format g_maFormatPriorities[] = { ma_format_s16, /* Most common */ ma_format_f32, /*ma_format_s24_32,*/ /* Clean alignment */ ma_format_s32, ma_format_s24, /* Unclean alignment */ ma_format_u8 /* Low quality */ }; #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) #pragma GCC diagnostic pop #endif MA_API void ma_version(ma_uint32* pMajor, ma_uint32* pMinor, ma_uint32* pRevision) { if (pMajor) { *pMajor = MA_VERSION_MAJOR; } if (pMinor) { *pMinor = MA_VERSION_MINOR; } if (pRevision) { *pRevision = MA_VERSION_REVISION; } } MA_API const char* ma_version_string(void) { return MA_VERSION_STRING; } /****************************************************************************** Standard Library Stuff ******************************************************************************/ #ifndef MA_MALLOC #ifdef MA_WIN32 #define MA_MALLOC(sz) HeapAlloc(GetProcessHeap(), 0, (sz)) #else #define MA_MALLOC(sz) malloc((sz)) #endif #endif #ifndef MA_REALLOC #ifdef MA_WIN32 #define MA_REALLOC(p, sz) (((sz) > 0) ? ((p) ? HeapReAlloc(GetProcessHeap(), 0, (p), (sz)) : HeapAlloc(GetProcessHeap(), 0, (sz))) : ((VOID*)(size_t)(HeapFree(GetProcessHeap(), 0, (p)) & 0))) #else #define MA_REALLOC(p, sz) realloc((p), (sz)) #endif #endif #ifndef MA_FREE #ifdef MA_WIN32 #define MA_FREE(p) HeapFree(GetProcessHeap(), 0, (p)) #else #define MA_FREE(p) free((p)) #endif #endif static MA_INLINE void ma_zero_memory_default(void* p, size_t sz) { #ifdef MA_WIN32 ZeroMemory(p, sz); #else if (sz > 0) { memset(p, 0, sz); } #endif } #ifndef MA_ZERO_MEMORY #define MA_ZERO_MEMORY(p, sz) ma_zero_memory_default((p), (sz)) #endif #ifndef MA_COPY_MEMORY #ifdef MA_WIN32 #define MA_COPY_MEMORY(dst, src, sz) CopyMemory((dst), (src), (sz)) #else #define MA_COPY_MEMORY(dst, src, sz) memcpy((dst), (src), (sz)) #endif #endif #ifndef MA_MOVE_MEMORY #ifdef MA_WIN32 #define MA_MOVE_MEMORY(dst, src, sz) MoveMemory((dst), (src), (sz)) #else #define MA_MOVE_MEMORY(dst, src, sz) memmove((dst), (src), (sz)) #endif #endif #ifndef MA_ASSERT #ifdef MA_WIN32 #define MA_ASSERT(condition) assert(condition) #else #define MA_ASSERT(condition) assert(condition) #endif #endif #define MA_ZERO_OBJECT(p) MA_ZERO_MEMORY((p), sizeof(*(p))) #define ma_countof(x) (sizeof(x) / sizeof(x[0])) #define ma_max(x, y) (((x) > (y)) ? (x) : (y)) #define ma_min(x, y) (((x) < (y)) ? (x) : (y)) #define ma_abs(x) (((x) > 0) ? (x) : -(x)) #define ma_clamp(x, lo, hi) (ma_max(lo, ma_min(x, hi))) #define ma_offset_ptr(p, offset) (((ma_uint8*)(p)) + (offset)) #define ma_align(x, a) ((x + (a-1)) & ~(a-1)) #define ma_align_64(x) ma_align(x, 8) #define ma_buffer_frame_capacity(buffer, channels, format) (sizeof(buffer) / ma_get_bytes_per_sample(format) / (channels)) static MA_INLINE double ma_sind(double x) { /* TODO: Implement custom sin(x). */ return sin(x); } static MA_INLINE double ma_expd(double x) { /* TODO: Implement custom exp(x). */ return exp(x); } static MA_INLINE double ma_logd(double x) { /* TODO: Implement custom log(x). */ return log(x); } static MA_INLINE double ma_powd(double x, double y) { /* TODO: Implement custom pow(x, y). */ return pow(x, y); } static MA_INLINE double ma_sqrtd(double x) { /* TODO: Implement custom sqrt(x). */ return sqrt(x); } static MA_INLINE float ma_sinf(float x) { return (float)ma_sind((float)x); } static MA_INLINE double ma_cosd(double x) { return ma_sind((MA_PI_D*0.5) - x); } static MA_INLINE float ma_cosf(float x) { return (float)ma_cosd((float)x); } static MA_INLINE double ma_log10d(double x) { return ma_logd(x) * 0.43429448190325182765; } static MA_INLINE float ma_powf(float x, float y) { return (float)ma_powd((double)x, (double)y); } static MA_INLINE float ma_log10f(float x) { return (float)ma_log10d((double)x); } static MA_INLINE double ma_degrees_to_radians(double degrees) { return degrees * 0.01745329252; } static MA_INLINE double ma_radians_to_degrees(double radians) { return radians * 57.295779512896; } static MA_INLINE float ma_degrees_to_radians_f(float degrees) { return degrees * 0.01745329252f; } static MA_INLINE float ma_radians_to_degrees_f(float radians) { return radians * 57.295779512896f; } /* Return Values: 0: Success 22: EINVAL 34: ERANGE Not using symbolic constants for errors because I want to avoid #including errno.h */ MA_API int ma_strcpy_s(char* dst, size_t dstSizeInBytes, const char* src) { size_t i; if (dst == 0) { return 22; } if (dstSizeInBytes == 0) { return 34; } if (src == 0) { dst[0] = '\0'; return 22; } for (i = 0; i < dstSizeInBytes && src[i] != '\0'; ++i) { dst[i] = src[i]; } if (i < dstSizeInBytes) { dst[i] = '\0'; return 0; } dst[0] = '\0'; return 34; } MA_API int ma_wcscpy_s(wchar_t* dst, size_t dstCap, const wchar_t* src) { size_t i; if (dst == 0) { return 22; } if (dstCap == 0) { return 34; } if (src == 0) { dst[0] = '\0'; return 22; } for (i = 0; i < dstCap && src[i] != '\0'; ++i) { dst[i] = src[i]; } if (i < dstCap) { dst[i] = '\0'; return 0; } dst[0] = '\0'; return 34; } MA_API int ma_strncpy_s(char* dst, size_t dstSizeInBytes, const char* src, size_t count) { size_t maxcount; size_t i; if (dst == 0) { return 22; } if (dstSizeInBytes == 0) { return 34; } if (src == 0) { dst[0] = '\0'; return 22; } maxcount = count; if (count == ((size_t)-1) || count >= dstSizeInBytes) { /* -1 = _TRUNCATE */ maxcount = dstSizeInBytes - 1; } for (i = 0; i < maxcount && src[i] != '\0'; ++i) { dst[i] = src[i]; } if (src[i] == '\0' || i == count || count == ((size_t)-1)) { dst[i] = '\0'; return 0; } dst[0] = '\0'; return 34; } MA_API int ma_strcat_s(char* dst, size_t dstSizeInBytes, const char* src) { char* dstorig; if (dst == 0) { return 22; } if (dstSizeInBytes == 0) { return 34; } if (src == 0) { dst[0] = '\0'; return 22; } dstorig = dst; while (dstSizeInBytes > 0 && dst[0] != '\0') { dst += 1; dstSizeInBytes -= 1; } if (dstSizeInBytes == 0) { return 22; /* Unterminated. */ } while (dstSizeInBytes > 0 && src[0] != '\0') { *dst++ = *src++; dstSizeInBytes -= 1; } if (dstSizeInBytes > 0) { dst[0] = '\0'; } else { dstorig[0] = '\0'; return 34; } return 0; } MA_API int ma_strncat_s(char* dst, size_t dstSizeInBytes, const char* src, size_t count) { char* dstorig; if (dst == 0) { return 22; } if (dstSizeInBytes == 0) { return 34; } if (src == 0) { return 22; } dstorig = dst; while (dstSizeInBytes > 0 && dst[0] != '\0') { dst += 1; dstSizeInBytes -= 1; } if (dstSizeInBytes == 0) { return 22; /* Unterminated. */ } if (count == ((size_t)-1)) { /* _TRUNCATE */ count = dstSizeInBytes - 1; } while (dstSizeInBytes > 0 && src[0] != '\0' && count > 0) { *dst++ = *src++; dstSizeInBytes -= 1; count -= 1; } if (dstSizeInBytes > 0) { dst[0] = '\0'; } else { dstorig[0] = '\0'; return 34; } return 0; } MA_API int ma_itoa_s(int value, char* dst, size_t dstSizeInBytes, int radix) { int sign; unsigned int valueU; char* dstEnd; if (dst == NULL || dstSizeInBytes == 0) { return 22; } if (radix < 2 || radix > 36) { dst[0] = '\0'; return 22; } sign = (value < 0 && radix == 10) ? -1 : 1; /* The negative sign is only used when the base is 10. */ if (value < 0) { valueU = -value; } else { valueU = value; } dstEnd = dst; do { int remainder = valueU % radix; if (remainder > 9) { *dstEnd = (char)((remainder - 10) + 'a'); } else { *dstEnd = (char)(remainder + '0'); } dstEnd += 1; dstSizeInBytes -= 1; valueU /= radix; } while (dstSizeInBytes > 0 && valueU > 0); if (dstSizeInBytes == 0) { dst[0] = '\0'; return 22; /* Ran out of room in the output buffer. */ } if (sign < 0) { *dstEnd++ = '-'; dstSizeInBytes -= 1; } if (dstSizeInBytes == 0) { dst[0] = '\0'; return 22; /* Ran out of room in the output buffer. */ } *dstEnd = '\0'; /* At this point the string will be reversed. */ dstEnd -= 1; while (dst < dstEnd) { char temp = *dst; *dst = *dstEnd; *dstEnd = temp; dst += 1; dstEnd -= 1; } return 0; } MA_API int ma_strcmp(const char* str1, const char* str2) { if (str1 == str2) return 0; /* These checks differ from the standard implementation. It's not important, but I prefer it just for sanity. */ if (str1 == NULL) return -1; if (str2 == NULL) return 1; for (;;) { if (str1[0] == '\0') { break; } if (str1[0] != str2[0]) { break; } str1 += 1; str2 += 1; } return ((unsigned char*)str1)[0] - ((unsigned char*)str2)[0]; } MA_API int ma_strappend(char* dst, size_t dstSize, const char* srcA, const char* srcB) { int result; result = ma_strncpy_s(dst, dstSize, srcA, (size_t)-1); if (result != 0) { return result; } result = ma_strncat_s(dst, dstSize, srcB, (size_t)-1); if (result != 0) { return result; } return result; } MA_API char* ma_copy_string(const char* src, const ma_allocation_callbacks* pAllocationCallbacks) { size_t sz; char* dst; if (src == NULL) { return NULL; } sz = strlen(src)+1; dst = (char*)ma_malloc(sz, pAllocationCallbacks); if (dst == NULL) { return NULL; } ma_strcpy_s(dst, sz, src); return dst; } MA_API wchar_t* ma_copy_string_w(const wchar_t* src, const ma_allocation_callbacks* pAllocationCallbacks) { size_t sz = wcslen(src)+1; wchar_t* dst = (wchar_t*)ma_malloc(sz * sizeof(*dst), pAllocationCallbacks); if (dst == NULL) { return NULL; } ma_wcscpy_s(dst, sz, src); return dst; } #include static ma_result ma_result_from_errno(int e) { switch (e) { case 0: return MA_SUCCESS; #ifdef EPERM case EPERM: return MA_INVALID_OPERATION; #endif #ifdef ENOENT case ENOENT: return MA_DOES_NOT_EXIST; #endif #ifdef ESRCH case ESRCH: return MA_DOES_NOT_EXIST; #endif #ifdef EINTR case EINTR: return MA_INTERRUPT; #endif #ifdef EIO case EIO: return MA_IO_ERROR; #endif #ifdef ENXIO case ENXIO: return MA_DOES_NOT_EXIST; #endif #ifdef E2BIG case E2BIG: return MA_INVALID_ARGS; #endif #ifdef ENOEXEC case ENOEXEC: return MA_INVALID_FILE; #endif #ifdef EBADF case EBADF: return MA_INVALID_FILE; #endif #ifdef ECHILD case ECHILD: return MA_ERROR; #endif #ifdef EAGAIN case EAGAIN: return MA_UNAVAILABLE; #endif #ifdef ENOMEM case ENOMEM: return MA_OUT_OF_MEMORY; #endif #ifdef EACCES case EACCES: return MA_ACCESS_DENIED; #endif #ifdef EFAULT case EFAULT: return MA_BAD_ADDRESS; #endif #ifdef ENOTBLK case ENOTBLK: return MA_ERROR; #endif #ifdef EBUSY case EBUSY: return MA_BUSY; #endif #ifdef EEXIST case EEXIST: return MA_ALREADY_EXISTS; #endif #ifdef EXDEV case EXDEV: return MA_ERROR; #endif #ifdef ENODEV case ENODEV: return MA_DOES_NOT_EXIST; #endif #ifdef ENOTDIR case ENOTDIR: return MA_NOT_DIRECTORY; #endif #ifdef EISDIR case EISDIR: return MA_IS_DIRECTORY; #endif #ifdef EINVAL case EINVAL: return MA_INVALID_ARGS; #endif #ifdef ENFILE case ENFILE: return MA_TOO_MANY_OPEN_FILES; #endif #ifdef EMFILE case EMFILE: return MA_TOO_MANY_OPEN_FILES; #endif #ifdef ENOTTY case ENOTTY: return MA_INVALID_OPERATION; #endif #ifdef ETXTBSY case ETXTBSY: return MA_BUSY; #endif #ifdef EFBIG case EFBIG: return MA_TOO_BIG; #endif #ifdef ENOSPC case ENOSPC: return MA_NO_SPACE; #endif #ifdef ESPIPE case ESPIPE: return MA_BAD_SEEK; #endif #ifdef EROFS case EROFS: return MA_ACCESS_DENIED; #endif #ifdef EMLINK case EMLINK: return MA_TOO_MANY_LINKS; #endif #ifdef EPIPE case EPIPE: return MA_BAD_PIPE; #endif #ifdef EDOM case EDOM: return MA_OUT_OF_RANGE; #endif #ifdef ERANGE case ERANGE: return MA_OUT_OF_RANGE; #endif #ifdef EDEADLK case EDEADLK: return MA_DEADLOCK; #endif #ifdef ENAMETOOLONG case ENAMETOOLONG: return MA_PATH_TOO_LONG; #endif #ifdef ENOLCK case ENOLCK: return MA_ERROR; #endif #ifdef ENOSYS case ENOSYS: return MA_NOT_IMPLEMENTED; #endif #ifdef ENOTEMPTY case ENOTEMPTY: return MA_DIRECTORY_NOT_EMPTY; #endif #ifdef ELOOP case ELOOP: return MA_TOO_MANY_LINKS; #endif #ifdef ENOMSG case ENOMSG: return MA_NO_MESSAGE; #endif #ifdef EIDRM case EIDRM: return MA_ERROR; #endif #ifdef ECHRNG case ECHRNG: return MA_ERROR; #endif #ifdef EL2NSYNC case EL2NSYNC: return MA_ERROR; #endif #ifdef EL3HLT case EL3HLT: return MA_ERROR; #endif #ifdef EL3RST case EL3RST: return MA_ERROR; #endif #ifdef ELNRNG case ELNRNG: return MA_OUT_OF_RANGE; #endif #ifdef EUNATCH case EUNATCH: return MA_ERROR; #endif #ifdef ENOCSI case ENOCSI: return MA_ERROR; #endif #ifdef EL2HLT case EL2HLT: return MA_ERROR; #endif #ifdef EBADE case EBADE: return MA_ERROR; #endif #ifdef EBADR case EBADR: return MA_ERROR; #endif #ifdef EXFULL case EXFULL: return MA_ERROR; #endif #ifdef ENOANO case ENOANO: return MA_ERROR; #endif #ifdef EBADRQC case EBADRQC: return MA_ERROR; #endif #ifdef EBADSLT case EBADSLT: return MA_ERROR; #endif #ifdef EBFONT case EBFONT: return MA_INVALID_FILE; #endif #ifdef ENOSTR case ENOSTR: return MA_ERROR; #endif #ifdef ENODATA case ENODATA: return MA_NO_DATA_AVAILABLE; #endif #ifdef ETIME case ETIME: return MA_TIMEOUT; #endif #ifdef ENOSR case ENOSR: return MA_NO_DATA_AVAILABLE; #endif #ifdef ENONET case ENONET: return MA_NO_NETWORK; #endif #ifdef ENOPKG case ENOPKG: return MA_ERROR; #endif #ifdef EREMOTE case EREMOTE: return MA_ERROR; #endif #ifdef ENOLINK case ENOLINK: return MA_ERROR; #endif #ifdef EADV case EADV: return MA_ERROR; #endif #ifdef ESRMNT case ESRMNT: return MA_ERROR; #endif #ifdef ECOMM case ECOMM: return MA_ERROR; #endif #ifdef EPROTO case EPROTO: return MA_ERROR; #endif #ifdef EMULTIHOP case EMULTIHOP: return MA_ERROR; #endif #ifdef EDOTDOT case EDOTDOT: return MA_ERROR; #endif #ifdef EBADMSG case EBADMSG: return MA_BAD_MESSAGE; #endif #ifdef EOVERFLOW case EOVERFLOW: return MA_TOO_BIG; #endif #ifdef ENOTUNIQ case ENOTUNIQ: return MA_NOT_UNIQUE; #endif #ifdef EBADFD case EBADFD: return MA_ERROR; #endif #ifdef EREMCHG case EREMCHG: return MA_ERROR; #endif #ifdef ELIBACC case ELIBACC: return MA_ACCESS_DENIED; #endif #ifdef ELIBBAD case ELIBBAD: return MA_INVALID_FILE; #endif #ifdef ELIBSCN case ELIBSCN: return MA_INVALID_FILE; #endif #ifdef ELIBMAX case ELIBMAX: return MA_ERROR; #endif #ifdef ELIBEXEC case ELIBEXEC: return MA_ERROR; #endif #ifdef EILSEQ case EILSEQ: return MA_INVALID_DATA; #endif #ifdef ERESTART case ERESTART: return MA_ERROR; #endif #ifdef ESTRPIPE case ESTRPIPE: return MA_ERROR; #endif #ifdef EUSERS case EUSERS: return MA_ERROR; #endif #ifdef ENOTSOCK case ENOTSOCK: return MA_NOT_SOCKET; #endif #ifdef EDESTADDRREQ case EDESTADDRREQ: return MA_NO_ADDRESS; #endif #ifdef EMSGSIZE case EMSGSIZE: return MA_TOO_BIG; #endif #ifdef EPROTOTYPE case EPROTOTYPE: return MA_BAD_PROTOCOL; #endif #ifdef ENOPROTOOPT case ENOPROTOOPT: return MA_PROTOCOL_UNAVAILABLE; #endif #ifdef EPROTONOSUPPORT case EPROTONOSUPPORT: return MA_PROTOCOL_NOT_SUPPORTED; #endif #ifdef ESOCKTNOSUPPORT case ESOCKTNOSUPPORT: return MA_SOCKET_NOT_SUPPORTED; #endif #ifdef EOPNOTSUPP case EOPNOTSUPP: return MA_INVALID_OPERATION; #endif #ifdef EPFNOSUPPORT case EPFNOSUPPORT: return MA_PROTOCOL_FAMILY_NOT_SUPPORTED; #endif #ifdef EAFNOSUPPORT case EAFNOSUPPORT: return MA_ADDRESS_FAMILY_NOT_SUPPORTED; #endif #ifdef EADDRINUSE case EADDRINUSE: return MA_ALREADY_IN_USE; #endif #ifdef EADDRNOTAVAIL case EADDRNOTAVAIL: return MA_ERROR; #endif #ifdef ENETDOWN case ENETDOWN: return MA_NO_NETWORK; #endif #ifdef ENETUNREACH case ENETUNREACH: return MA_NO_NETWORK; #endif #ifdef ENETRESET case ENETRESET: return MA_NO_NETWORK; #endif #ifdef ECONNABORTED case ECONNABORTED: return MA_NO_NETWORK; #endif #ifdef ECONNRESET case ECONNRESET: return MA_CONNECTION_RESET; #endif #ifdef ENOBUFS case ENOBUFS: return MA_NO_SPACE; #endif #ifdef EISCONN case EISCONN: return MA_ALREADY_CONNECTED; #endif #ifdef ENOTCONN case ENOTCONN: return MA_NOT_CONNECTED; #endif #ifdef ESHUTDOWN case ESHUTDOWN: return MA_ERROR; #endif #ifdef ETOOMANYREFS case ETOOMANYREFS: return MA_ERROR; #endif #ifdef ETIMEDOUT case ETIMEDOUT: return MA_TIMEOUT; #endif #ifdef ECONNREFUSED case ECONNREFUSED: return MA_CONNECTION_REFUSED; #endif #ifdef EHOSTDOWN case EHOSTDOWN: return MA_NO_HOST; #endif #ifdef EHOSTUNREACH case EHOSTUNREACH: return MA_NO_HOST; #endif #ifdef EALREADY case EALREADY: return MA_IN_PROGRESS; #endif #ifdef EINPROGRESS case EINPROGRESS: return MA_IN_PROGRESS; #endif #ifdef ESTALE case ESTALE: return MA_INVALID_FILE; #endif #ifdef EUCLEAN case EUCLEAN: return MA_ERROR; #endif #ifdef ENOTNAM case ENOTNAM: return MA_ERROR; #endif #ifdef ENAVAIL case ENAVAIL: return MA_ERROR; #endif #ifdef EISNAM case EISNAM: return MA_ERROR; #endif #ifdef EREMOTEIO case EREMOTEIO: return MA_IO_ERROR; #endif #ifdef EDQUOT case EDQUOT: return MA_NO_SPACE; #endif #ifdef ENOMEDIUM case ENOMEDIUM: return MA_DOES_NOT_EXIST; #endif #ifdef EMEDIUMTYPE case EMEDIUMTYPE: return MA_ERROR; #endif #ifdef ECANCELED case ECANCELED: return MA_CANCELLED; #endif #ifdef ENOKEY case ENOKEY: return MA_ERROR; #endif #ifdef EKEYEXPIRED case EKEYEXPIRED: return MA_ERROR; #endif #ifdef EKEYREVOKED case EKEYREVOKED: return MA_ERROR; #endif #ifdef EKEYREJECTED case EKEYREJECTED: return MA_ERROR; #endif #ifdef EOWNERDEAD case EOWNERDEAD: return MA_ERROR; #endif #ifdef ENOTRECOVERABLE case ENOTRECOVERABLE: return MA_ERROR; #endif #ifdef ERFKILL case ERFKILL: return MA_ERROR; #endif #ifdef EHWPOISON case EHWPOISON: return MA_ERROR; #endif default: return MA_ERROR; } } MA_API ma_result ma_fopen(FILE** ppFile, const char* pFilePath, const char* pOpenMode) { #if defined(_MSC_VER) && _MSC_VER >= 1400 errno_t err; #endif if (ppFile != NULL) { *ppFile = NULL; /* Safety. */ } if (pFilePath == NULL || pOpenMode == NULL || ppFile == NULL) { return MA_INVALID_ARGS; } #if defined(_MSC_VER) && _MSC_VER >= 1400 err = fopen_s(ppFile, pFilePath, pOpenMode); if (err != 0) { return ma_result_from_errno(err); } #else #if defined(_WIN32) || defined(__APPLE__) *ppFile = fopen(pFilePath, pOpenMode); #else #if defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS == 64 && defined(_LARGEFILE64_SOURCE) *ppFile = fopen64(pFilePath, pOpenMode); #else *ppFile = fopen(pFilePath, pOpenMode); #endif #endif if (*ppFile == NULL) { ma_result result = ma_result_from_errno(errno); if (result == MA_SUCCESS) { result = MA_ERROR; /* Just a safety check to make sure we never ever return success when pFile == NULL. */ } return result; } #endif return MA_SUCCESS; } /* _wfopen() isn't always available in all compilation environments. * Windows only. * MSVC seems to support it universally as far back as VC6 from what I can tell (haven't checked further back). * MinGW-64 (both 32- and 64-bit) seems to support it. * MinGW wraps it in !defined(__STRICT_ANSI__). * OpenWatcom wraps it in !defined(_NO_EXT_KEYS). This can be reviewed as compatibility issues arise. The preference is to use _wfopen_s() and _wfopen() as opposed to the wcsrtombs() fallback, so if you notice your compiler not detecting this properly I'm happy to look at adding support. */ #if defined(_WIN32) #if defined(_MSC_VER) || defined(__MINGW64__) || (!defined(__STRICT_ANSI__) && !defined(_NO_EXT_KEYS)) #define MA_HAS_WFOPEN #endif #endif MA_API ma_result ma_wfopen(FILE** ppFile, const wchar_t* pFilePath, const wchar_t* pOpenMode, const ma_allocation_callbacks* pAllocationCallbacks) { if (ppFile != NULL) { *ppFile = NULL; /* Safety. */ } if (pFilePath == NULL || pOpenMode == NULL || ppFile == NULL) { return MA_INVALID_ARGS; } #if defined(MA_HAS_WFOPEN) { /* Use _wfopen() on Windows. */ #if defined(_MSC_VER) && _MSC_VER >= 1400 errno_t err = _wfopen_s(ppFile, pFilePath, pOpenMode); if (err != 0) { return ma_result_from_errno(err); } #else *ppFile = _wfopen(pFilePath, pOpenMode); if (*ppFile == NULL) { return ma_result_from_errno(errno); } #endif (void)pAllocationCallbacks; } #else /* Use fopen() on anything other than Windows. Requires a conversion. This is annoying because fopen() is locale specific. The only real way I can think of to do this is with wcsrtombs(). Note that wcstombs() is apparently not thread-safe because it uses a static global mbstate_t object for maintaining state. I've checked this with -std=c89 and it works, but if somebody get's a compiler error I'll look into improving compatibility. */ { mbstate_t mbs; size_t lenMB; const wchar_t* pFilePathTemp = pFilePath; char* pFilePathMB = NULL; char pOpenModeMB[32] = {0}; /* Get the length first. */ MA_ZERO_OBJECT(&mbs); lenMB = wcsrtombs(NULL, &pFilePathTemp, 0, &mbs); if (lenMB == (size_t)-1) { return ma_result_from_errno(errno); } pFilePathMB = (char*)ma_malloc(lenMB + 1, pAllocationCallbacks); if (pFilePathMB == NULL) { return MA_OUT_OF_MEMORY; } pFilePathTemp = pFilePath; MA_ZERO_OBJECT(&mbs); wcsrtombs(pFilePathMB, &pFilePathTemp, lenMB + 1, &mbs); /* The open mode should always consist of ASCII characters so we should be able to do a trivial conversion. */ { size_t i = 0; for (;;) { if (pOpenMode[i] == 0) { pOpenModeMB[i] = '\0'; break; } pOpenModeMB[i] = (char)pOpenMode[i]; i += 1; } } *ppFile = fopen(pFilePathMB, pOpenModeMB); ma_free(pFilePathMB, pAllocationCallbacks); } if (*ppFile == NULL) { return MA_ERROR; } #endif return MA_SUCCESS; } static MA_INLINE void ma_copy_memory_64(void* dst, const void* src, ma_uint64 sizeInBytes) { #if 0xFFFFFFFFFFFFFFFF <= MA_SIZE_MAX MA_COPY_MEMORY(dst, src, (size_t)sizeInBytes); #else while (sizeInBytes > 0) { ma_uint64 bytesToCopyNow = sizeInBytes; if (bytesToCopyNow > MA_SIZE_MAX) { bytesToCopyNow = MA_SIZE_MAX; } MA_COPY_MEMORY(dst, src, (size_t)bytesToCopyNow); /* Safe cast to size_t. */ sizeInBytes -= bytesToCopyNow; dst = ( void*)(( ma_uint8*)dst + bytesToCopyNow); src = (const void*)((const ma_uint8*)src + bytesToCopyNow); } #endif } static MA_INLINE void ma_zero_memory_64(void* dst, ma_uint64 sizeInBytes) { #if 0xFFFFFFFFFFFFFFFF <= MA_SIZE_MAX MA_ZERO_MEMORY(dst, (size_t)sizeInBytes); #else while (sizeInBytes > 0) { ma_uint64 bytesToZeroNow = sizeInBytes; if (bytesToZeroNow > MA_SIZE_MAX) { bytesToZeroNow = MA_SIZE_MAX; } MA_ZERO_MEMORY(dst, (size_t)bytesToZeroNow); /* Safe cast to size_t. */ sizeInBytes -= bytesToZeroNow; dst = (void*)((ma_uint8*)dst + bytesToZeroNow); } #endif } /* Thanks to good old Bit Twiddling Hacks for this one: http://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2 */ static MA_INLINE unsigned int ma_next_power_of_2(unsigned int x) { x--; x |= x >> 1; x |= x >> 2; x |= x >> 4; x |= x >> 8; x |= x >> 16; x++; return x; } static MA_INLINE unsigned int ma_prev_power_of_2(unsigned int x) { return ma_next_power_of_2(x) >> 1; } static MA_INLINE unsigned int ma_round_to_power_of_2(unsigned int x) { unsigned int prev = ma_prev_power_of_2(x); unsigned int next = ma_next_power_of_2(x); if ((next - x) > (x - prev)) { return prev; } else { return next; } } static MA_INLINE unsigned int ma_count_set_bits(unsigned int x) { unsigned int count = 0; while (x != 0) { if (x & 1) { count += 1; } x = x >> 1; } return count; } /************************************************************************************************************************************************************** Allocation Callbacks **************************************************************************************************************************************************************/ static void* ma__malloc_default(size_t sz, void* pUserData) { (void)pUserData; return MA_MALLOC(sz); } static void* ma__realloc_default(void* p, size_t sz, void* pUserData) { (void)pUserData; return MA_REALLOC(p, sz); } static void ma__free_default(void* p, void* pUserData) { (void)pUserData; MA_FREE(p); } static ma_allocation_callbacks ma_allocation_callbacks_init_default(void) { ma_allocation_callbacks callbacks; callbacks.pUserData = NULL; callbacks.onMalloc = ma__malloc_default; callbacks.onRealloc = ma__realloc_default; callbacks.onFree = ma__free_default; return callbacks; } static ma_result ma_allocation_callbacks_init_copy(ma_allocation_callbacks* pDst, const ma_allocation_callbacks* pSrc) { if (pDst == NULL) { return MA_INVALID_ARGS; } if (pSrc == NULL) { *pDst = ma_allocation_callbacks_init_default(); } else { if (pSrc->pUserData == NULL && pSrc->onFree == NULL && pSrc->onMalloc == NULL && pSrc->onRealloc == NULL) { *pDst = ma_allocation_callbacks_init_default(); } else { if (pSrc->onFree == NULL || (pSrc->onMalloc == NULL && pSrc->onRealloc == NULL)) { return MA_INVALID_ARGS; /* Invalid allocation callbacks. */ } else { *pDst = *pSrc; } } } return MA_SUCCESS; } /************************************************************************************************************************************************************** Logging **************************************************************************************************************************************************************/ MA_API const char* ma_log_level_to_string(ma_uint32 logLevel) { switch (logLevel) { case MA_LOG_LEVEL_DEBUG: return "DEBUG"; case MA_LOG_LEVEL_INFO: return "INFO"; case MA_LOG_LEVEL_WARNING: return "WARNING"; case MA_LOG_LEVEL_ERROR: return "ERROR"; default: return "ERROR"; } } #if defined(MA_DEBUG_OUTPUT) /* Customize this to use a specific tag in __android_log_print() for debug output messages. */ #ifndef MA_ANDROID_LOG_TAG #define MA_ANDROID_LOG_TAG "miniaudio" #endif void ma_log_callback_debug(void* pUserData, ma_uint32 level, const char* pMessage) { (void)pUserData; /* Special handling for some platforms. */ #if defined(MA_ANDROID) { /* Android. */ __android_log_print(ANDROID_LOG_DEBUG, MA_ANDROID_LOG_TAG, "%s: %s", ma_log_level_to_string(level), pMessage); } #else { /* Everything else. */ printf("%s: %s", ma_log_level_to_string(level), pMessage); } #endif } #endif MA_API ma_log_callback ma_log_callback_init(ma_log_callback_proc onLog, void* pUserData) { ma_log_callback callback; MA_ZERO_OBJECT(&callback); callback.onLog = onLog; callback.pUserData = pUserData; return callback; } MA_API ma_result ma_log_init(const ma_allocation_callbacks* pAllocationCallbacks, ma_log* pLog) { if (pLog == NULL) { return MA_INVALID_ARGS; } MA_ZERO_OBJECT(pLog); ma_allocation_callbacks_init_copy(&pLog->allocationCallbacks, pAllocationCallbacks); /* We need a mutex for thread safety. */ #ifndef MA_NO_THREADING { ma_result result = ma_mutex_init(&pLog->lock); if (result != MA_SUCCESS) { return result; } } #endif /* If we're using debug output, enable it. */ #if defined(MA_DEBUG_OUTPUT) { ma_log_register_callback(pLog, ma_log_callback_init(ma_log_callback_debug, NULL)); /* Doesn't really matter if this fails. */ } #endif return MA_SUCCESS; } MA_API void ma_log_uninit(ma_log* pLog) { if (pLog == NULL) { return; } #ifndef MA_NO_THREADING ma_mutex_uninit(&pLog->lock); #endif } static void ma_log_lock(ma_log* pLog) { #ifndef MA_NO_THREADING ma_mutex_lock(&pLog->lock); #else (void)pLog; #endif } static void ma_log_unlock(ma_log* pLog) { #ifndef MA_NO_THREADING ma_mutex_unlock(&pLog->lock); #else (void)pLog; #endif } MA_API ma_result ma_log_register_callback(ma_log* pLog, ma_log_callback callback) { ma_result result = MA_SUCCESS; if (pLog == NULL || callback.onLog == NULL) { return MA_INVALID_ARGS; } ma_log_lock(pLog); { if (pLog->callbackCount == ma_countof(pLog->callbacks)) { result = MA_OUT_OF_MEMORY; /* Reached the maximum allowed log callbacks. */ } else { pLog->callbacks[pLog->callbackCount] = callback; pLog->callbackCount += 1; } } ma_log_unlock(pLog); return result; } MA_API ma_result ma_log_unregister_callback(ma_log* pLog, ma_log_callback callback) { if (pLog == NULL) { return MA_INVALID_ARGS; } ma_log_lock(pLog); { ma_uint32 iLog; for (iLog = 0; iLog < pLog->callbackCount; ) { if (pLog->callbacks[iLog].onLog == callback.onLog) { /* Found. Move everything down a slot. */ ma_uint32 jLog; for (jLog = iLog; jLog < pLog->callbackCount-1; jLog += 1) { pLog->callbacks[jLog] = pLog->callbacks[jLog + 1]; } pLog->callbackCount -= 1; } else { /* Not found. */ iLog += 1; } } } ma_log_unlock(pLog); return MA_SUCCESS; } MA_API ma_result ma_log_post(ma_log* pLog, ma_uint32 level, const char* pMessage) { if (pLog == NULL || pMessage == NULL) { return MA_INVALID_ARGS; } ma_log_lock(pLog); { ma_uint32 iLog; for (iLog = 0; iLog < pLog->callbackCount; iLog += 1) { if (pLog->callbacks[iLog].onLog) { pLog->callbacks[iLog].onLog(pLog->callbacks[iLog].pUserData, level, pMessage); } } } ma_log_unlock(pLog); return MA_SUCCESS; } /* We need to emulate _vscprintf() for the VC6 build. This can be more efficient, but since it's only VC6, and it's just a logging function, I'm happy to keep this simple. In the VC6 build we can implement this in terms of _vsnprintf(). */ #if defined(_MSC_VER) && _MSC_VER < 1900 static int ma_vscprintf(const ma_allocation_callbacks* pAllocationCallbacks, const char* format, va_list args) { #if _MSC_VER > 1200 return _vscprintf(format, args); #else int result; char* pTempBuffer = NULL; size_t tempBufferCap = 1024; if (format == NULL) { errno = EINVAL; return -1; } for (;;) { char* pNewTempBuffer = (char*)ma_realloc(pTempBuffer, tempBufferCap, pAllocationCallbacks); if (pNewTempBuffer == NULL) { ma_free(pTempBuffer, pAllocationCallbacks); errno = ENOMEM; return -1; /* Out of memory. */ } pTempBuffer = pNewTempBuffer; result = _vsnprintf(pTempBuffer, tempBufferCap, format, args); ma_free(pTempBuffer, NULL); if (result != -1) { break; /* Got it. */ } /* Buffer wasn't big enough. Ideally it'd be nice to use an error code to know the reason for sure, but this is reliable enough. */ tempBufferCap *= 2; } return result; #endif } #endif MA_API ma_result ma_log_postv(ma_log* pLog, ma_uint32 level, const char* pFormat, va_list args) { if (pLog == NULL || pFormat == NULL) { return MA_INVALID_ARGS; } #if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || ((!defined(_MSC_VER) || _MSC_VER >= 1900) && !defined(__STRICT_ANSI__) && !defined(_NO_EXT_KEYS)) || (defined(__cplusplus) && __cplusplus >= 201103L) { ma_result result; int length; char pFormattedMessageStack[1024]; char* pFormattedMessageHeap = NULL; /* First try formatting into our fixed sized stack allocated buffer. If this is too small we'll fallback to a heap allocation. */ length = vsnprintf(pFormattedMessageStack, sizeof(pFormattedMessageStack), pFormat, args); if (length < 0) { return MA_INVALID_OPERATION; /* An error occured when trying to convert the buffer. */ } if ((size_t)length < sizeof(pFormattedMessageStack)) { /* The string was written to the stack. */ result = ma_log_post(pLog, level, pFormattedMessageStack); } else { /* The stack buffer was too small, try the heap. */ pFormattedMessageHeap = (char*)ma_malloc(length + 1, &pLog->allocationCallbacks); if (pFormattedMessageHeap == NULL) { return MA_OUT_OF_MEMORY; } length = vsnprintf(pFormattedMessageHeap, length + 1, pFormat, args); if (length < 0) { ma_free(pFormattedMessageHeap, &pLog->allocationCallbacks); return MA_INVALID_OPERATION; } result = ma_log_post(pLog, level, pFormattedMessageHeap); ma_free(pFormattedMessageHeap, &pLog->allocationCallbacks); } return result; } #else { /* Without snprintf() we need to first measure the string and then heap allocate it. I'm only aware of Visual Studio having support for this without snprintf(), so we'll need to restrict this branch to Visual Studio. For other compilers we need to just not support formatted logging because I don't want the security risk of overflowing a fixed sized stack allocated buffer. */ #if defined(_MSC_VER) && _MSC_VER >= 1200 /* 1200 = VC6 */ { ma_result result; int formattedLen; char* pFormattedMessage = NULL; va_list args2; #if _MSC_VER >= 1800 { va_copy(args2, args); } #else { args2 = args; } #endif formattedLen = ma_vscprintf(&pLog->allocationCallbacks, pFormat, args2); va_end(args2); if (formattedLen <= 0) { return MA_INVALID_OPERATION; } pFormattedMessage = (char*)ma_malloc(formattedLen + 1, &pLog->allocationCallbacks); if (pFormattedMessage == NULL) { return MA_OUT_OF_MEMORY; } /* We'll get errors on newer versions of Visual Studio if we try to use vsprintf(). */ #if _MSC_VER >= 1400 /* 1400 = Visual Studio 2005 */ { vsprintf_s(pFormattedMessage, formattedLen + 1, pFormat, args); } #else { vsprintf(pFormattedMessage, pFormat, args); } #endif result = ma_log_post(pLog, level, pFormattedMessage); ma_free(pFormattedMessage, &pLog->allocationCallbacks); return result; } #else { /* Can't do anything because we don't have a safe way of to emulate vsnprintf() without a manual solution. */ (void)level; (void)args; return MA_INVALID_OPERATION; } #endif } #endif } MA_API ma_result ma_log_postf(ma_log* pLog, ma_uint32 level, const char* pFormat, ...) { ma_result result; va_list args; if (pLog == NULL || pFormat == NULL) { return MA_INVALID_ARGS; } va_start(args, pFormat); { result = ma_log_postv(pLog, level, pFormat, args); } va_end(args); return result; } static MA_INLINE ma_uint8 ma_clip_u8(ma_int32 x) { return (ma_uint8)(ma_clamp(x, -128, 127) + 128); } static MA_INLINE ma_int16 ma_clip_s16(ma_int32 x) { return (ma_int16)ma_clamp(x, -32768, 32767); } static MA_INLINE ma_int64 ma_clip_s24(ma_int64 x) { return (ma_int64)ma_clamp(x, -8388608, 8388607); } static MA_INLINE ma_int32 ma_clip_s32(ma_int64 x) { /* This dance is to silence warnings with -std=c89. A good compiler should be able to optimize this away. */ ma_int64 clipMin; ma_int64 clipMax; clipMin = -((ma_int64)2147483647 + 1); clipMax = (ma_int64)2147483647; return (ma_int32)ma_clamp(x, clipMin, clipMax); } static MA_INLINE float ma_clip_f32(float x) { if (x < -1) return -1; if (x > +1) return +1; return x; } static MA_INLINE float ma_mix_f32(float x, float y, float a) { return x*(1-a) + y*a; } static MA_INLINE float ma_mix_f32_fast(float x, float y, float a) { float r0 = (y - x); float r1 = r0*a; return x + r1; /*return x + (y - x)*a;*/ } #if defined(MA_SUPPORT_SSE2) static MA_INLINE __m128 ma_mix_f32_fast__sse2(__m128 x, __m128 y, __m128 a) { return _mm_add_ps(x, _mm_mul_ps(_mm_sub_ps(y, x), a)); } #endif #if defined(MA_SUPPORT_AVX2) static MA_INLINE __m256 ma_mix_f32_fast__avx2(__m256 x, __m256 y, __m256 a) { return _mm256_add_ps(x, _mm256_mul_ps(_mm256_sub_ps(y, x), a)); } #endif #if defined(MA_SUPPORT_NEON) static MA_INLINE float32x4_t ma_mix_f32_fast__neon(float32x4_t x, float32x4_t y, float32x4_t a) { return vaddq_f32(x, vmulq_f32(vsubq_f32(y, x), a)); } #endif static MA_INLINE double ma_mix_f64(double x, double y, double a) { return x*(1-a) + y*a; } static MA_INLINE double ma_mix_f64_fast(double x, double y, double a) { return x + (y - x)*a; } static MA_INLINE float ma_scale_to_range_f32(float x, float lo, float hi) { return lo + x*(hi-lo); } /* Greatest common factor using Euclid's algorithm iteratively. */ static MA_INLINE ma_uint32 ma_gcf_u32(ma_uint32 a, ma_uint32 b) { for (;;) { if (b == 0) { break; } else { ma_uint32 t = a; a = b; b = t % a; } } return a; } static ma_uint32 ma_ffs_32(ma_uint32 x) { ma_uint32 i; /* Just a naive implementation just to get things working for now. Will optimize this later. */ for (i = 0; i < 32; i += 1) { if ((x & (1 << i)) != 0) { return i; } } return i; } static MA_INLINE ma_int16 ma_float_to_fixed_16(float x) { return (ma_int16)(x * (1 << 8)); } /* Random Number Generation miniaudio uses the LCG random number generation algorithm. This is good enough for audio. Note that miniaudio's global LCG implementation uses global state which is _not_ thread-local. When this is called across multiple threads, results will be unpredictable. However, it won't crash and results will still be random enough for miniaudio's purposes. */ #ifndef MA_DEFAULT_LCG_SEED #define MA_DEFAULT_LCG_SEED 4321 #endif #define MA_LCG_M 2147483647 #define MA_LCG_A 48271 #define MA_LCG_C 0 static ma_lcg g_maLCG = {MA_DEFAULT_LCG_SEED}; /* Non-zero initial seed. Use ma_seed() to use an explicit seed. */ static MA_INLINE void ma_lcg_seed(ma_lcg* pLCG, ma_int32 seed) { MA_ASSERT(pLCG != NULL); pLCG->state = seed; } static MA_INLINE ma_int32 ma_lcg_rand_s32(ma_lcg* pLCG) { pLCG->state = (MA_LCG_A * pLCG->state + MA_LCG_C) % MA_LCG_M; return pLCG->state; } static MA_INLINE ma_uint32 ma_lcg_rand_u32(ma_lcg* pLCG) { return (ma_uint32)ma_lcg_rand_s32(pLCG); } static MA_INLINE ma_int16 ma_lcg_rand_s16(ma_lcg* pLCG) { return (ma_int16)(ma_lcg_rand_s32(pLCG) & 0xFFFF); } static MA_INLINE double ma_lcg_rand_f64(ma_lcg* pLCG) { return ma_lcg_rand_s32(pLCG) / (double)0x7FFFFFFF; } static MA_INLINE float ma_lcg_rand_f32(ma_lcg* pLCG) { return (float)ma_lcg_rand_f64(pLCG); } static MA_INLINE float ma_lcg_rand_range_f32(ma_lcg* pLCG, float lo, float hi) { return ma_scale_to_range_f32(ma_lcg_rand_f32(pLCG), lo, hi); } static MA_INLINE ma_int32 ma_lcg_rand_range_s32(ma_lcg* pLCG, ma_int32 lo, ma_int32 hi) { if (lo == hi) { return lo; } return lo + ma_lcg_rand_u32(pLCG) / (0xFFFFFFFF / (hi - lo + 1) + 1); } static MA_INLINE void ma_seed(ma_int32 seed) { ma_lcg_seed(&g_maLCG, seed); } static MA_INLINE ma_int32 ma_rand_s32(void) { return ma_lcg_rand_s32(&g_maLCG); } static MA_INLINE ma_uint32 ma_rand_u32(void) { return ma_lcg_rand_u32(&g_maLCG); } static MA_INLINE double ma_rand_f64(void) { return ma_lcg_rand_f64(&g_maLCG); } static MA_INLINE float ma_rand_f32(void) { return ma_lcg_rand_f32(&g_maLCG); } static MA_INLINE float ma_rand_range_f32(float lo, float hi) { return ma_lcg_rand_range_f32(&g_maLCG, lo, hi); } static MA_INLINE ma_int32 ma_rand_range_s32(ma_int32 lo, ma_int32 hi) { return ma_lcg_rand_range_s32(&g_maLCG, lo, hi); } static MA_INLINE float ma_dither_f32_rectangle(float ditherMin, float ditherMax) { return ma_rand_range_f32(ditherMin, ditherMax); } static MA_INLINE float ma_dither_f32_triangle(float ditherMin, float ditherMax) { float a = ma_rand_range_f32(ditherMin, 0); float b = ma_rand_range_f32(0, ditherMax); return a + b; } static MA_INLINE float ma_dither_f32(ma_dither_mode ditherMode, float ditherMin, float ditherMax) { if (ditherMode == ma_dither_mode_rectangle) { return ma_dither_f32_rectangle(ditherMin, ditherMax); } if (ditherMode == ma_dither_mode_triangle) { return ma_dither_f32_triangle(ditherMin, ditherMax); } return 0; } static MA_INLINE ma_int32 ma_dither_s32(ma_dither_mode ditherMode, ma_int32 ditherMin, ma_int32 ditherMax) { if (ditherMode == ma_dither_mode_rectangle) { ma_int32 a = ma_rand_range_s32(ditherMin, ditherMax); return a; } if (ditherMode == ma_dither_mode_triangle) { ma_int32 a = ma_rand_range_s32(ditherMin, 0); ma_int32 b = ma_rand_range_s32(0, ditherMax); return a + b; } return 0; } /************************************************************************************************************************************************************** Atomics **************************************************************************************************************************************************************/ /* c89atomic.h begin */ #ifndef c89atomic_h #define c89atomic_h #if defined(__cplusplus) extern "C" { #endif typedef signed char c89atomic_int8; typedef unsigned char c89atomic_uint8; typedef signed short c89atomic_int16; typedef unsigned short c89atomic_uint16; typedef signed int c89atomic_int32; typedef unsigned int c89atomic_uint32; #if defined(_MSC_VER) && !defined(__clang__) typedef signed __int64 c89atomic_int64; typedef unsigned __int64 c89atomic_uint64; #else #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wlong-long" #if defined(__clang__) #pragma GCC diagnostic ignored "-Wc++11-long-long" #endif #endif typedef signed long long c89atomic_int64; typedef unsigned long long c89atomic_uint64; #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) #pragma GCC diagnostic pop #endif #endif typedef int c89atomic_memory_order; typedef unsigned char c89atomic_bool; #if !defined(C89ATOMIC_64BIT) && !defined(C89ATOMIC_32BIT) #ifdef _WIN32 #ifdef _WIN64 #define C89ATOMIC_64BIT #else #define C89ATOMIC_32BIT #endif #endif #endif #if !defined(C89ATOMIC_64BIT) && !defined(C89ATOMIC_32BIT) #ifdef __GNUC__ #ifdef __LP64__ #define C89ATOMIC_64BIT #else #define C89ATOMIC_32BIT #endif #endif #endif #if !defined(C89ATOMIC_64BIT) && !defined(C89ATOMIC_32BIT) #include #if INTPTR_MAX == INT64_MAX #define C89ATOMIC_64BIT #else #define C89ATOMIC_32BIT #endif #endif #if defined(__x86_64__) || defined(_M_X64) #define C89ATOMIC_X64 #elif defined(__i386) || defined(_M_IX86) #define C89ATOMIC_X86 #elif defined(__arm__) || defined(_M_ARM) || defined(__arm64) || defined(__arm64__) || defined(__aarch64__) || defined(_M_ARM64) #define C89ATOMIC_ARM #endif #if defined(_MSC_VER) #define C89ATOMIC_INLINE __forceinline #elif defined(__GNUC__) #if defined(__STRICT_ANSI__) #define C89ATOMIC_INLINE __inline__ __attribute__((always_inline)) #else #define C89ATOMIC_INLINE inline __attribute__((always_inline)) #endif #elif defined(__WATCOMC__) || defined(__DMC__) #define C89ATOMIC_INLINE __inline #else #define C89ATOMIC_INLINE #endif #define C89ATOMIC_HAS_8 #define C89ATOMIC_HAS_16 #define C89ATOMIC_HAS_32 #define C89ATOMIC_HAS_64 #if (defined(_MSC_VER) ) || defined(__WATCOMC__) || defined(__DMC__) #define c89atomic_memory_order_relaxed 0 #define c89atomic_memory_order_consume 1 #define c89atomic_memory_order_acquire 2 #define c89atomic_memory_order_release 3 #define c89atomic_memory_order_acq_rel 4 #define c89atomic_memory_order_seq_cst 5 #if _MSC_VER < 1600 && defined(C89ATOMIC_X86) #define C89ATOMIC_MSVC_USE_INLINED_ASSEMBLY #endif #if _MSC_VER < 1600 #undef C89ATOMIC_HAS_8 #undef C89ATOMIC_HAS_16 #endif #if !defined(C89ATOMIC_MSVC_USE_INLINED_ASSEMBLY) #include #endif #if defined(C89ATOMIC_MSVC_USE_INLINED_ASSEMBLY) #if defined(C89ATOMIC_HAS_8) static C89ATOMIC_INLINE c89atomic_uint8 __stdcall c89atomic_compare_and_swap_8(volatile c89atomic_uint8* dst, c89atomic_uint8 expected, c89atomic_uint8 desired) { c89atomic_uint8 result = 0; __asm { mov ecx, dst mov al, expected mov dl, desired lock cmpxchg [ecx], dl mov result, al } return result; } #endif #if defined(C89ATOMIC_HAS_16) static C89ATOMIC_INLINE c89atomic_uint16 __stdcall c89atomic_compare_and_swap_16(volatile c89atomic_uint16* dst, c89atomic_uint16 expected, c89atomic_uint16 desired) { c89atomic_uint16 result = 0; __asm { mov ecx, dst mov ax, expected mov dx, desired lock cmpxchg [ecx], dx mov result, ax } return result; } #endif #if defined(C89ATOMIC_HAS_32) static C89ATOMIC_INLINE c89atomic_uint32 __stdcall c89atomic_compare_and_swap_32(volatile c89atomic_uint32* dst, c89atomic_uint32 expected, c89atomic_uint32 desired) { c89atomic_uint32 result = 0; __asm { mov ecx, dst mov eax, expected mov edx, desired lock cmpxchg [ecx], edx mov result, eax } return result; } #endif #if defined(C89ATOMIC_HAS_64) static C89ATOMIC_INLINE c89atomic_uint64 __stdcall c89atomic_compare_and_swap_64(volatile c89atomic_uint64* dst, c89atomic_uint64 expected, c89atomic_uint64 desired) { c89atomic_uint32 resultEAX = 0; c89atomic_uint32 resultEDX = 0; __asm { mov esi, dst mov eax, dword ptr expected mov edx, dword ptr expected + 4 mov ebx, dword ptr desired mov ecx, dword ptr desired + 4 lock cmpxchg8b qword ptr [esi] mov resultEAX, eax mov resultEDX, edx } return ((c89atomic_uint64)resultEDX << 32) | resultEAX; } #endif #else #if defined(C89ATOMIC_HAS_8) #define c89atomic_compare_and_swap_8( dst, expected, desired) (c89atomic_uint8 )_InterlockedCompareExchange8((volatile char*)dst, (char)desired, (char)expected) #endif #if defined(C89ATOMIC_HAS_16) #define c89atomic_compare_and_swap_16(dst, expected, desired) (c89atomic_uint16)_InterlockedCompareExchange16((volatile short*)dst, (short)desired, (short)expected) #endif #if defined(C89ATOMIC_HAS_32) #define c89atomic_compare_and_swap_32(dst, expected, desired) (c89atomic_uint32)_InterlockedCompareExchange((volatile long*)dst, (long)desired, (long)expected) #endif #if defined(C89ATOMIC_HAS_64) #define c89atomic_compare_and_swap_64(dst, expected, desired) (c89atomic_uint64)_InterlockedCompareExchange64((volatile c89atomic_int64*)dst, (c89atomic_int64)desired, (c89atomic_int64)expected) #endif #endif #if defined(C89ATOMIC_MSVC_USE_INLINED_ASSEMBLY) #if defined(C89ATOMIC_HAS_8) static C89ATOMIC_INLINE c89atomic_uint8 __stdcall c89atomic_exchange_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order) { c89atomic_uint8 result = 0; (void)order; __asm { mov ecx, dst mov al, src lock xchg [ecx], al mov result, al } return result; } #endif #if defined(C89ATOMIC_HAS_16) static C89ATOMIC_INLINE c89atomic_uint16 __stdcall c89atomic_exchange_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order) { c89atomic_uint16 result = 0; (void)order; __asm { mov ecx, dst mov ax, src lock xchg [ecx], ax mov result, ax } return result; } #endif #if defined(C89ATOMIC_HAS_32) static C89ATOMIC_INLINE c89atomic_uint32 __stdcall c89atomic_exchange_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order) { c89atomic_uint32 result = 0; (void)order; __asm { mov ecx, dst mov eax, src lock xchg [ecx], eax mov result, eax } return result; } #endif #else #if defined(C89ATOMIC_HAS_8) static C89ATOMIC_INLINE c89atomic_uint8 __stdcall c89atomic_exchange_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order) { (void)order; return (c89atomic_uint8)_InterlockedExchange8((volatile char*)dst, (char)src); } #endif #if defined(C89ATOMIC_HAS_16) static C89ATOMIC_INLINE c89atomic_uint16 __stdcall c89atomic_exchange_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order) { (void)order; return (c89atomic_uint16)_InterlockedExchange16((volatile short*)dst, (short)src); } #endif #if defined(C89ATOMIC_HAS_32) static C89ATOMIC_INLINE c89atomic_uint32 __stdcall c89atomic_exchange_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order) { (void)order; return (c89atomic_uint32)_InterlockedExchange((volatile long*)dst, (long)src); } #endif #if defined(C89ATOMIC_HAS_64) && defined(C89ATOMIC_64BIT) static C89ATOMIC_INLINE c89atomic_uint64 __stdcall c89atomic_exchange_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order) { (void)order; return (c89atomic_uint64)_InterlockedExchange64((volatile long long*)dst, (long long)src); } #else #endif #endif #if defined(C89ATOMIC_HAS_64) && !defined(C89ATOMIC_64BIT) static C89ATOMIC_INLINE c89atomic_uint64 __stdcall c89atomic_exchange_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order) { c89atomic_uint64 oldValue; do { oldValue = *dst; } while (c89atomic_compare_and_swap_64(dst, oldValue, src) != oldValue); (void)order; return oldValue; } #endif #if defined(C89ATOMIC_MSVC_USE_INLINED_ASSEMBLY) #if defined(C89ATOMIC_HAS_8) static C89ATOMIC_INLINE c89atomic_uint8 __stdcall c89atomic_fetch_add_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order) { c89atomic_uint8 result = 0; (void)order; __asm { mov ecx, dst mov al, src lock xadd [ecx], al mov result, al } return result; } #endif #if defined(C89ATOMIC_HAS_16) static C89ATOMIC_INLINE c89atomic_uint16 __stdcall c89atomic_fetch_add_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order) { c89atomic_uint16 result = 0; (void)order; __asm { mov ecx, dst mov ax, src lock xadd [ecx], ax mov result, ax } return result; } #endif #if defined(C89ATOMIC_HAS_32) static C89ATOMIC_INLINE c89atomic_uint32 __stdcall c89atomic_fetch_add_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order) { c89atomic_uint32 result = 0; (void)order; __asm { mov ecx, dst mov eax, src lock xadd [ecx], eax mov result, eax } return result; } #endif #else #if defined(C89ATOMIC_HAS_8) static C89ATOMIC_INLINE c89atomic_uint8 __stdcall c89atomic_fetch_add_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order) { (void)order; return (c89atomic_uint8)_InterlockedExchangeAdd8((volatile char*)dst, (char)src); } #endif #if defined(C89ATOMIC_HAS_16) static C89ATOMIC_INLINE c89atomic_uint16 __stdcall c89atomic_fetch_add_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order) { (void)order; return (c89atomic_uint16)_InterlockedExchangeAdd16((volatile short*)dst, (short)src); } #endif #if defined(C89ATOMIC_HAS_32) static C89ATOMIC_INLINE c89atomic_uint32 __stdcall c89atomic_fetch_add_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order) { (void)order; return (c89atomic_uint32)_InterlockedExchangeAdd((volatile long*)dst, (long)src); } #endif #if defined(C89ATOMIC_HAS_64) && defined(C89ATOMIC_64BIT) static C89ATOMIC_INLINE c89atomic_uint64 __stdcall c89atomic_fetch_add_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order) { (void)order; return (c89atomic_uint64)_InterlockedExchangeAdd64((volatile long long*)dst, (long long)src); } #else #endif #endif #if defined(C89ATOMIC_HAS_64) && !defined(C89ATOMIC_64BIT) static C89ATOMIC_INLINE c89atomic_uint64 __stdcall c89atomic_fetch_add_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order) { c89atomic_uint64 oldValue; c89atomic_uint64 newValue; do { oldValue = *dst; newValue = oldValue + src; } while (c89atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); (void)order; return oldValue; } #endif #if defined(C89ATOMIC_MSVC_USE_INLINED_ASSEMBLY) static C89ATOMIC_INLINE void __stdcall c89atomic_thread_fence(c89atomic_memory_order order) { (void)order; __asm { lock add [esp], 0 } } #else #if defined(C89ATOMIC_X64) #define c89atomic_thread_fence(order) __faststorefence(), (void)order #else static C89ATOMIC_INLINE void c89atomic_thread_fence(c89atomic_memory_order order) { volatile c89atomic_uint32 barrier = 0; c89atomic_fetch_add_explicit_32(&barrier, 0, order); } #endif #endif #define c89atomic_compiler_fence() c89atomic_thread_fence(c89atomic_memory_order_seq_cst) #define c89atomic_signal_fence(order) c89atomic_thread_fence(order) #if defined(C89ATOMIC_HAS_8) static C89ATOMIC_INLINE c89atomic_uint8 c89atomic_load_explicit_8(volatile const c89atomic_uint8* ptr, c89atomic_memory_order order) { (void)order; return c89atomic_compare_and_swap_8((volatile c89atomic_uint8*)ptr, 0, 0); } #endif #if defined(C89ATOMIC_HAS_16) static C89ATOMIC_INLINE c89atomic_uint16 c89atomic_load_explicit_16(volatile const c89atomic_uint16* ptr, c89atomic_memory_order order) { (void)order; return c89atomic_compare_and_swap_16((volatile c89atomic_uint16*)ptr, 0, 0); } #endif #if defined(C89ATOMIC_HAS_32) static C89ATOMIC_INLINE c89atomic_uint32 c89atomic_load_explicit_32(volatile const c89atomic_uint32* ptr, c89atomic_memory_order order) { (void)order; return c89atomic_compare_and_swap_32((volatile c89atomic_uint32*)ptr, 0, 0); } #endif #if defined(C89ATOMIC_HAS_64) static C89ATOMIC_INLINE c89atomic_uint64 c89atomic_load_explicit_64(volatile const c89atomic_uint64* ptr, c89atomic_memory_order order) { (void)order; return c89atomic_compare_and_swap_64((volatile c89atomic_uint64*)ptr, 0, 0); } #endif #if defined(C89ATOMIC_HAS_8) #define c89atomic_store_explicit_8( dst, src, order) (void)c89atomic_exchange_explicit_8 (dst, src, order) #endif #if defined(C89ATOMIC_HAS_16) #define c89atomic_store_explicit_16(dst, src, order) (void)c89atomic_exchange_explicit_16(dst, src, order) #endif #if defined(C89ATOMIC_HAS_32) #define c89atomic_store_explicit_32(dst, src, order) (void)c89atomic_exchange_explicit_32(dst, src, order) #endif #if defined(C89ATOMIC_HAS_64) #define c89atomic_store_explicit_64(dst, src, order) (void)c89atomic_exchange_explicit_64(dst, src, order) #endif #if defined(C89ATOMIC_HAS_8) static C89ATOMIC_INLINE c89atomic_uint8 __stdcall c89atomic_fetch_sub_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order) { c89atomic_uint8 oldValue; c89atomic_uint8 newValue; do { oldValue = *dst; newValue = (c89atomic_uint8)(oldValue - src); } while (c89atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue); (void)order; return oldValue; } #endif #if defined(C89ATOMIC_HAS_16) static C89ATOMIC_INLINE c89atomic_uint16 __stdcall c89atomic_fetch_sub_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order) { c89atomic_uint16 oldValue; c89atomic_uint16 newValue; do { oldValue = *dst; newValue = (c89atomic_uint16)(oldValue - src); } while (c89atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue); (void)order; return oldValue; } #endif #if defined(C89ATOMIC_HAS_32) static C89ATOMIC_INLINE c89atomic_uint32 __stdcall c89atomic_fetch_sub_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order) { c89atomic_uint32 oldValue; c89atomic_uint32 newValue; do { oldValue = *dst; newValue = oldValue - src; } while (c89atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue); (void)order; return oldValue; } #endif #if defined(C89ATOMIC_HAS_64) static C89ATOMIC_INLINE c89atomic_uint64 __stdcall c89atomic_fetch_sub_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order) { c89atomic_uint64 oldValue; c89atomic_uint64 newValue; do { oldValue = *dst; newValue = oldValue - src; } while (c89atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); (void)order; return oldValue; } #endif #if defined(C89ATOMIC_HAS_8) static C89ATOMIC_INLINE c89atomic_uint8 __stdcall c89atomic_fetch_and_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order) { c89atomic_uint8 oldValue; c89atomic_uint8 newValue; do { oldValue = *dst; newValue = (c89atomic_uint8)(oldValue & src); } while (c89atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue); (void)order; return oldValue; } #endif #if defined(C89ATOMIC_HAS_16) static C89ATOMIC_INLINE c89atomic_uint16 __stdcall c89atomic_fetch_and_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order) { c89atomic_uint16 oldValue; c89atomic_uint16 newValue; do { oldValue = *dst; newValue = (c89atomic_uint16)(oldValue & src); } while (c89atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue); (void)order; return oldValue; } #endif #if defined(C89ATOMIC_HAS_32) static C89ATOMIC_INLINE c89atomic_uint32 __stdcall c89atomic_fetch_and_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order) { c89atomic_uint32 oldValue; c89atomic_uint32 newValue; do { oldValue = *dst; newValue = oldValue & src; } while (c89atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue); (void)order; return oldValue; } #endif #if defined(C89ATOMIC_HAS_64) static C89ATOMIC_INLINE c89atomic_uint64 __stdcall c89atomic_fetch_and_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order) { c89atomic_uint64 oldValue; c89atomic_uint64 newValue; do { oldValue = *dst; newValue = oldValue & src; } while (c89atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); (void)order; return oldValue; } #endif #if defined(C89ATOMIC_HAS_8) static C89ATOMIC_INLINE c89atomic_uint8 __stdcall c89atomic_fetch_xor_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order) { c89atomic_uint8 oldValue; c89atomic_uint8 newValue; do { oldValue = *dst; newValue = (c89atomic_uint8)(oldValue ^ src); } while (c89atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue); (void)order; return oldValue; } #endif #if defined(C89ATOMIC_HAS_16) static C89ATOMIC_INLINE c89atomic_uint16 __stdcall c89atomic_fetch_xor_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order) { c89atomic_uint16 oldValue; c89atomic_uint16 newValue; do { oldValue = *dst; newValue = (c89atomic_uint16)(oldValue ^ src); } while (c89atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue); (void)order; return oldValue; } #endif #if defined(C89ATOMIC_HAS_32) static C89ATOMIC_INLINE c89atomic_uint32 __stdcall c89atomic_fetch_xor_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order) { c89atomic_uint32 oldValue; c89atomic_uint32 newValue; do { oldValue = *dst; newValue = oldValue ^ src; } while (c89atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue); (void)order; return oldValue; } #endif #if defined(C89ATOMIC_HAS_64) static C89ATOMIC_INLINE c89atomic_uint64 __stdcall c89atomic_fetch_xor_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order) { c89atomic_uint64 oldValue; c89atomic_uint64 newValue; do { oldValue = *dst; newValue = oldValue ^ src; } while (c89atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); (void)order; return oldValue; } #endif #if defined(C89ATOMIC_HAS_8) static C89ATOMIC_INLINE c89atomic_uint8 __stdcall c89atomic_fetch_or_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order) { c89atomic_uint8 oldValue; c89atomic_uint8 newValue; do { oldValue = *dst; newValue = (c89atomic_uint8)(oldValue | src); } while (c89atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue); (void)order; return oldValue; } #endif #if defined(C89ATOMIC_HAS_16) static C89ATOMIC_INLINE c89atomic_uint16 __stdcall c89atomic_fetch_or_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order) { c89atomic_uint16 oldValue; c89atomic_uint16 newValue; do { oldValue = *dst; newValue = (c89atomic_uint16)(oldValue | src); } while (c89atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue); (void)order; return oldValue; } #endif #if defined(C89ATOMIC_HAS_32) static C89ATOMIC_INLINE c89atomic_uint32 __stdcall c89atomic_fetch_or_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order) { c89atomic_uint32 oldValue; c89atomic_uint32 newValue; do { oldValue = *dst; newValue = oldValue | src; } while (c89atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue); (void)order; return oldValue; } #endif #if defined(C89ATOMIC_HAS_64) static C89ATOMIC_INLINE c89atomic_uint64 __stdcall c89atomic_fetch_or_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order) { c89atomic_uint64 oldValue; c89atomic_uint64 newValue; do { oldValue = *dst; newValue = oldValue | src; } while (c89atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); (void)order; return oldValue; } #endif #if defined(C89ATOMIC_HAS_8) #define c89atomic_test_and_set_explicit_8( dst, order) c89atomic_exchange_explicit_8 (dst, 1, order) #endif #if defined(C89ATOMIC_HAS_16) #define c89atomic_test_and_set_explicit_16(dst, order) c89atomic_exchange_explicit_16(dst, 1, order) #endif #if defined(C89ATOMIC_HAS_32) #define c89atomic_test_and_set_explicit_32(dst, order) c89atomic_exchange_explicit_32(dst, 1, order) #endif #if defined(C89ATOMIC_HAS_64) #define c89atomic_test_and_set_explicit_64(dst, order) c89atomic_exchange_explicit_64(dst, 1, order) #endif #if defined(C89ATOMIC_HAS_8) #define c89atomic_clear_explicit_8( dst, order) c89atomic_store_explicit_8 (dst, 0, order) #endif #if defined(C89ATOMIC_HAS_16) #define c89atomic_clear_explicit_16(dst, order) c89atomic_store_explicit_16(dst, 0, order) #endif #if defined(C89ATOMIC_HAS_32) #define c89atomic_clear_explicit_32(dst, order) c89atomic_store_explicit_32(dst, 0, order) #endif #if defined(C89ATOMIC_HAS_64) #define c89atomic_clear_explicit_64(dst, order) c89atomic_store_explicit_64(dst, 0, order) #endif #if defined(C89ATOMIC_HAS_8) typedef c89atomic_uint8 c89atomic_flag; #define c89atomic_flag_test_and_set_explicit(ptr, order) (c89atomic_bool)c89atomic_test_and_set_explicit_8(ptr, order) #define c89atomic_flag_clear_explicit(ptr, order) c89atomic_clear_explicit_8(ptr, order) #define c89atoimc_flag_load_explicit(ptr, order) c89atomic_load_explicit_8(ptr, order) #else typedef c89atomic_uint32 c89atomic_flag; #define c89atomic_flag_test_and_set_explicit(ptr, order) (c89atomic_bool)c89atomic_test_and_set_explicit_32(ptr, order) #define c89atomic_flag_clear_explicit(ptr, order) c89atomic_clear_explicit_32(ptr, order) #define c89atoimc_flag_load_explicit(ptr, order) c89atomic_load_explicit_32(ptr, order) #endif #elif defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7))) #define C89ATOMIC_HAS_NATIVE_COMPARE_EXCHANGE #define C89ATOMIC_HAS_NATIVE_IS_LOCK_FREE #define c89atomic_memory_order_relaxed __ATOMIC_RELAXED #define c89atomic_memory_order_consume __ATOMIC_CONSUME #define c89atomic_memory_order_acquire __ATOMIC_ACQUIRE #define c89atomic_memory_order_release __ATOMIC_RELEASE #define c89atomic_memory_order_acq_rel __ATOMIC_ACQ_REL #define c89atomic_memory_order_seq_cst __ATOMIC_SEQ_CST #define c89atomic_compiler_fence() __asm__ __volatile__("":::"memory") #define c89atomic_thread_fence(order) __atomic_thread_fence(order) #define c89atomic_signal_fence(order) __atomic_signal_fence(order) #define c89atomic_is_lock_free_8(ptr) __atomic_is_lock_free(1, ptr) #define c89atomic_is_lock_free_16(ptr) __atomic_is_lock_free(2, ptr) #define c89atomic_is_lock_free_32(ptr) __atomic_is_lock_free(4, ptr) #define c89atomic_is_lock_free_64(ptr) __atomic_is_lock_free(8, ptr) #define c89atomic_test_and_set_explicit_8( dst, order) __atomic_exchange_n(dst, 1, order) #define c89atomic_test_and_set_explicit_16(dst, order) __atomic_exchange_n(dst, 1, order) #define c89atomic_test_and_set_explicit_32(dst, order) __atomic_exchange_n(dst, 1, order) #define c89atomic_test_and_set_explicit_64(dst, order) __atomic_exchange_n(dst, 1, order) #define c89atomic_clear_explicit_8( dst, order) __atomic_store_n(dst, 0, order) #define c89atomic_clear_explicit_16(dst, order) __atomic_store_n(dst, 0, order) #define c89atomic_clear_explicit_32(dst, order) __atomic_store_n(dst, 0, order) #define c89atomic_clear_explicit_64(dst, order) __atomic_store_n(dst, 0, order) #define c89atomic_store_explicit_8( dst, src, order) __atomic_store_n(dst, src, order) #define c89atomic_store_explicit_16(dst, src, order) __atomic_store_n(dst, src, order) #define c89atomic_store_explicit_32(dst, src, order) __atomic_store_n(dst, src, order) #define c89atomic_store_explicit_64(dst, src, order) __atomic_store_n(dst, src, order) #define c89atomic_load_explicit_8( dst, order) __atomic_load_n(dst, order) #define c89atomic_load_explicit_16(dst, order) __atomic_load_n(dst, order) #define c89atomic_load_explicit_32(dst, order) __atomic_load_n(dst, order) #define c89atomic_load_explicit_64(dst, order) __atomic_load_n(dst, order) #define c89atomic_exchange_explicit_8( dst, src, order) __atomic_exchange_n(dst, src, order) #define c89atomic_exchange_explicit_16(dst, src, order) __atomic_exchange_n(dst, src, order) #define c89atomic_exchange_explicit_32(dst, src, order) __atomic_exchange_n(dst, src, order) #define c89atomic_exchange_explicit_64(dst, src, order) __atomic_exchange_n(dst, src, order) #define c89atomic_compare_exchange_strong_explicit_8( dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 0, successOrder, failureOrder) #define c89atomic_compare_exchange_strong_explicit_16(dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 0, successOrder, failureOrder) #define c89atomic_compare_exchange_strong_explicit_32(dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 0, successOrder, failureOrder) #define c89atomic_compare_exchange_strong_explicit_64(dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 0, successOrder, failureOrder) #define c89atomic_compare_exchange_weak_explicit_8( dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 1, successOrder, failureOrder) #define c89atomic_compare_exchange_weak_explicit_16(dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 1, successOrder, failureOrder) #define c89atomic_compare_exchange_weak_explicit_32(dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 1, successOrder, failureOrder) #define c89atomic_compare_exchange_weak_explicit_64(dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 1, successOrder, failureOrder) #define c89atomic_fetch_add_explicit_8( dst, src, order) __atomic_fetch_add(dst, src, order) #define c89atomic_fetch_add_explicit_16(dst, src, order) __atomic_fetch_add(dst, src, order) #define c89atomic_fetch_add_explicit_32(dst, src, order) __atomic_fetch_add(dst, src, order) #define c89atomic_fetch_add_explicit_64(dst, src, order) __atomic_fetch_add(dst, src, order) #define c89atomic_fetch_sub_explicit_8( dst, src, order) __atomic_fetch_sub(dst, src, order) #define c89atomic_fetch_sub_explicit_16(dst, src, order) __atomic_fetch_sub(dst, src, order) #define c89atomic_fetch_sub_explicit_32(dst, src, order) __atomic_fetch_sub(dst, src, order) #define c89atomic_fetch_sub_explicit_64(dst, src, order) __atomic_fetch_sub(dst, src, order) #define c89atomic_fetch_or_explicit_8( dst, src, order) __atomic_fetch_or(dst, src, order) #define c89atomic_fetch_or_explicit_16(dst, src, order) __atomic_fetch_or(dst, src, order) #define c89atomic_fetch_or_explicit_32(dst, src, order) __atomic_fetch_or(dst, src, order) #define c89atomic_fetch_or_explicit_64(dst, src, order) __atomic_fetch_or(dst, src, order) #define c89atomic_fetch_xor_explicit_8( dst, src, order) __atomic_fetch_xor(dst, src, order) #define c89atomic_fetch_xor_explicit_16(dst, src, order) __atomic_fetch_xor(dst, src, order) #define c89atomic_fetch_xor_explicit_32(dst, src, order) __atomic_fetch_xor(dst, src, order) #define c89atomic_fetch_xor_explicit_64(dst, src, order) __atomic_fetch_xor(dst, src, order) #define c89atomic_fetch_and_explicit_8( dst, src, order) __atomic_fetch_and(dst, src, order) #define c89atomic_fetch_and_explicit_16(dst, src, order) __atomic_fetch_and(dst, src, order) #define c89atomic_fetch_and_explicit_32(dst, src, order) __atomic_fetch_and(dst, src, order) #define c89atomic_fetch_and_explicit_64(dst, src, order) __atomic_fetch_and(dst, src, order) #define c89atomic_compare_and_swap_8 (dst, expected, desired) __sync_val_compare_and_swap(dst, expected, desired) #define c89atomic_compare_and_swap_16(dst, expected, desired) __sync_val_compare_and_swap(dst, expected, desired) #define c89atomic_compare_and_swap_32(dst, expected, desired) __sync_val_compare_and_swap(dst, expected, desired) #define c89atomic_compare_and_swap_64(dst, expected, desired) __sync_val_compare_and_swap(dst, expected, desired) typedef c89atomic_uint8 c89atomic_flag; #define c89atomic_flag_test_and_set_explicit(dst, order) (c89atomic_bool)__atomic_test_and_set(dst, order) #define c89atomic_flag_clear_explicit(dst, order) __atomic_clear(dst, order) #define c89atoimc_flag_load_explicit(ptr, order) c89atomic_load_explicit_8(ptr, order) #else #define c89atomic_memory_order_relaxed 1 #define c89atomic_memory_order_consume 2 #define c89atomic_memory_order_acquire 3 #define c89atomic_memory_order_release 4 #define c89atomic_memory_order_acq_rel 5 #define c89atomic_memory_order_seq_cst 6 #define c89atomic_compiler_fence() __asm__ __volatile__("":::"memory") #if defined(__GNUC__) #define c89atomic_thread_fence(order) __sync_synchronize(), (void)order static C89ATOMIC_INLINE c89atomic_uint8 c89atomic_exchange_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order) { if (order > c89atomic_memory_order_acquire) { __sync_synchronize(); } return __sync_lock_test_and_set(dst, src); } static C89ATOMIC_INLINE c89atomic_uint16 c89atomic_exchange_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order) { c89atomic_uint16 oldValue; do { oldValue = *dst; } while (__sync_val_compare_and_swap(dst, oldValue, src) != oldValue); (void)order; return oldValue; } static C89ATOMIC_INLINE c89atomic_uint32 c89atomic_exchange_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order) { c89atomic_uint32 oldValue; do { oldValue = *dst; } while (__sync_val_compare_and_swap(dst, oldValue, src) != oldValue); (void)order; return oldValue; } static C89ATOMIC_INLINE c89atomic_uint64 c89atomic_exchange_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order) { c89atomic_uint64 oldValue; do { oldValue = *dst; } while (__sync_val_compare_and_swap(dst, oldValue, src) != oldValue); (void)order; return oldValue; } static C89ATOMIC_INLINE c89atomic_uint8 c89atomic_fetch_add_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order) { (void)order; return __sync_fetch_and_add(dst, src); } static C89ATOMIC_INLINE c89atomic_uint16 c89atomic_fetch_add_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order) { (void)order; return __sync_fetch_and_add(dst, src); } static C89ATOMIC_INLINE c89atomic_uint32 c89atomic_fetch_add_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order) { (void)order; return __sync_fetch_and_add(dst, src); } static C89ATOMIC_INLINE c89atomic_uint64 c89atomic_fetch_add_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order) { (void)order; return __sync_fetch_and_add(dst, src); } static C89ATOMIC_INLINE c89atomic_uint8 c89atomic_fetch_sub_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order) { (void)order; return __sync_fetch_and_sub(dst, src); } static C89ATOMIC_INLINE c89atomic_uint16 c89atomic_fetch_sub_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order) { (void)order; return __sync_fetch_and_sub(dst, src); } static C89ATOMIC_INLINE c89atomic_uint32 c89atomic_fetch_sub_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order) { (void)order; return __sync_fetch_and_sub(dst, src); } static C89ATOMIC_INLINE c89atomic_uint64 c89atomic_fetch_sub_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order) { (void)order; return __sync_fetch_and_sub(dst, src); } static C89ATOMIC_INLINE c89atomic_uint8 c89atomic_fetch_or_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order) { (void)order; return __sync_fetch_and_or(dst, src); } static C89ATOMIC_INLINE c89atomic_uint16 c89atomic_fetch_or_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order) { (void)order; return __sync_fetch_and_or(dst, src); } static C89ATOMIC_INLINE c89atomic_uint32 c89atomic_fetch_or_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order) { (void)order; return __sync_fetch_and_or(dst, src); } static C89ATOMIC_INLINE c89atomic_uint64 c89atomic_fetch_or_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order) { (void)order; return __sync_fetch_and_or(dst, src); } static C89ATOMIC_INLINE c89atomic_uint8 c89atomic_fetch_xor_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order) { (void)order; return __sync_fetch_and_xor(dst, src); } static C89ATOMIC_INLINE c89atomic_uint16 c89atomic_fetch_xor_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order) { (void)order; return __sync_fetch_and_xor(dst, src); } static C89ATOMIC_INLINE c89atomic_uint32 c89atomic_fetch_xor_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order) { (void)order; return __sync_fetch_and_xor(dst, src); } static C89ATOMIC_INLINE c89atomic_uint64 c89atomic_fetch_xor_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order) { (void)order; return __sync_fetch_and_xor(dst, src); } static C89ATOMIC_INLINE c89atomic_uint8 c89atomic_fetch_and_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order) { (void)order; return __sync_fetch_and_and(dst, src); } static C89ATOMIC_INLINE c89atomic_uint16 c89atomic_fetch_and_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order) { (void)order; return __sync_fetch_and_and(dst, src); } static C89ATOMIC_INLINE c89atomic_uint32 c89atomic_fetch_and_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order) { (void)order; return __sync_fetch_and_and(dst, src); } static C89ATOMIC_INLINE c89atomic_uint64 c89atomic_fetch_and_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order) { (void)order; return __sync_fetch_and_and(dst, src); } #define c89atomic_compare_and_swap_8( dst, expected, desired) __sync_val_compare_and_swap(dst, expected, desired) #define c89atomic_compare_and_swap_16(dst, expected, desired) __sync_val_compare_and_swap(dst, expected, desired) #define c89atomic_compare_and_swap_32(dst, expected, desired) __sync_val_compare_and_swap(dst, expected, desired) #define c89atomic_compare_and_swap_64(dst, expected, desired) __sync_val_compare_and_swap(dst, expected, desired) #else #if defined(C89ATOMIC_X86) #define c89atomic_thread_fence(order) __asm__ __volatile__("lock; addl $0, (%%esp)" ::: "memory", "cc") #elif defined(C89ATOMIC_X64) #define c89atomic_thread_fence(order) __asm__ __volatile__("lock; addq $0, (%%rsp)" ::: "memory", "cc") #else #error Unsupported architecture. Please submit a feature request. #endif static C89ATOMIC_INLINE c89atomic_uint8 c89atomic_compare_and_swap_8(volatile c89atomic_uint8* dst, c89atomic_uint8 expected, c89atomic_uint8 desired) { c89atomic_uint8 result; #if defined(C89ATOMIC_X86) || defined(C89ATOMIC_X64) __asm__ __volatile__("lock; cmpxchg %3, %0" : "+m"(*dst), "=a"(result) : "a"(expected), "d"(desired) : "cc"); #else #error Unsupported architecture. Please submit a feature request. #endif return result; } static C89ATOMIC_INLINE c89atomic_uint16 c89atomic_compare_and_swap_16(volatile c89atomic_uint16* dst, c89atomic_uint16 expected, c89atomic_uint16 desired) { c89atomic_uint16 result; #if defined(C89ATOMIC_X86) || defined(C89ATOMIC_X64) __asm__ __volatile__("lock; cmpxchg %3, %0" : "+m"(*dst), "=a"(result) : "a"(expected), "d"(desired) : "cc"); #else #error Unsupported architecture. Please submit a feature request. #endif return result; } static C89ATOMIC_INLINE c89atomic_uint32 c89atomic_compare_and_swap_32(volatile c89atomic_uint32* dst, c89atomic_uint32 expected, c89atomic_uint32 desired) { c89atomic_uint32 result; #if defined(C89ATOMIC_X86) || defined(C89ATOMIC_X64) __asm__ __volatile__("lock; cmpxchg %3, %0" : "+m"(*dst), "=a"(result) : "a"(expected), "d"(desired) : "cc"); #else #error Unsupported architecture. Please submit a feature request. #endif return result; } static C89ATOMIC_INLINE c89atomic_uint64 c89atomic_compare_and_swap_64(volatile c89atomic_uint64* dst, c89atomic_uint64 expected, c89atomic_uint64 desired) { volatile c89atomic_uint64 result; #if defined(C89ATOMIC_X86) c89atomic_uint32 resultEAX; c89atomic_uint32 resultEDX; __asm__ __volatile__("push %%ebx; xchg %5, %%ebx; lock; cmpxchg8b %0; pop %%ebx" : "+m"(*dst), "=a"(resultEAX), "=d"(resultEDX) : "a"(expected & 0xFFFFFFFF), "d"(expected >> 32), "r"(desired & 0xFFFFFFFF), "c"(desired >> 32) : "cc"); result = ((c89atomic_uint64)resultEDX << 32) | resultEAX; #elif defined(C89ATOMIC_X64) __asm__ __volatile__("lock; cmpxchg %3, %0" : "+m"(*dst), "=a"(result) : "a"(expected), "d"(desired) : "cc"); #else #error Unsupported architecture. Please submit a feature request. #endif return result; } static C89ATOMIC_INLINE c89atomic_uint8 c89atomic_exchange_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order) { c89atomic_uint8 result = 0; (void)order; #if defined(C89ATOMIC_X86) || defined(C89ATOMIC_X64) __asm__ __volatile__("lock; xchg %1, %0" : "+m"(*dst), "=a"(result) : "a"(src)); #else #error Unsupported architecture. Please submit a feature request. #endif return result; } static C89ATOMIC_INLINE c89atomic_uint16 c89atomic_exchange_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order) { c89atomic_uint16 result = 0; (void)order; #if defined(C89ATOMIC_X86) || defined(C89ATOMIC_X64) __asm__ __volatile__("lock; xchg %1, %0" : "+m"(*dst), "=a"(result) : "a"(src)); #else #error Unsupported architecture. Please submit a feature request. #endif return result; } static C89ATOMIC_INLINE c89atomic_uint32 c89atomic_exchange_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order) { c89atomic_uint32 result; (void)order; #if defined(C89ATOMIC_X86) || defined(C89ATOMIC_X64) __asm__ __volatile__("lock; xchg %1, %0" : "+m"(*dst), "=a"(result) : "a"(src)); #else #error Unsupported architecture. Please submit a feature request. #endif return result; } static C89ATOMIC_INLINE c89atomic_uint64 c89atomic_exchange_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order) { c89atomic_uint64 result; (void)order; #if defined(C89ATOMIC_X86) do { result = *dst; } while (c89atomic_compare_and_swap_64(dst, result, src) != result); #elif defined(C89ATOMIC_X64) __asm__ __volatile__("lock; xchg %1, %0" : "+m"(*dst), "=a"(result) : "a"(src)); #else #error Unsupported architecture. Please submit a feature request. #endif return result; } static C89ATOMIC_INLINE c89atomic_uint8 c89atomic_fetch_add_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order) { c89atomic_uint8 result; (void)order; #if defined(C89ATOMIC_X86) || defined(C89ATOMIC_X64) __asm__ __volatile__("lock; xadd %1, %0" : "+m"(*dst), "=a"(result) : "a"(src) : "cc"); #else #error Unsupported architecture. Please submit a feature request. #endif return result; } static C89ATOMIC_INLINE c89atomic_uint16 c89atomic_fetch_add_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order) { c89atomic_uint16 result; (void)order; #if defined(C89ATOMIC_X86) || defined(C89ATOMIC_X64) __asm__ __volatile__("lock; xadd %1, %0" : "+m"(*dst), "=a"(result) : "a"(src) : "cc"); #else #error Unsupported architecture. Please submit a feature request. #endif return result; } static C89ATOMIC_INLINE c89atomic_uint32 c89atomic_fetch_add_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order) { c89atomic_uint32 result; (void)order; #if defined(C89ATOMIC_X86) || defined(C89ATOMIC_X64) __asm__ __volatile__("lock; xadd %1, %0" : "+m"(*dst), "=a"(result) : "a"(src) : "cc"); #else #error Unsupported architecture. Please submit a feature request. #endif return result; } static C89ATOMIC_INLINE c89atomic_uint64 c89atomic_fetch_add_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order) { #if defined(C89ATOMIC_X86) c89atomic_uint64 oldValue; c89atomic_uint64 newValue; (void)order; do { oldValue = *dst; newValue = oldValue + src; } while (c89atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); return oldValue; #elif defined(C89ATOMIC_X64) c89atomic_uint64 result; (void)order; __asm__ __volatile__("lock; xadd %1, %0" : "+m"(*dst), "=a"(result) : "a"(src) : "cc"); return result; #endif } static C89ATOMIC_INLINE c89atomic_uint8 c89atomic_fetch_sub_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order) { c89atomic_uint8 oldValue; c89atomic_uint8 newValue; do { oldValue = *dst; newValue = (c89atomic_uint8)(oldValue - src); } while (c89atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue); (void)order; return oldValue; } static C89ATOMIC_INLINE c89atomic_uint16 c89atomic_fetch_sub_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order) { c89atomic_uint16 oldValue; c89atomic_uint16 newValue; do { oldValue = *dst; newValue = (c89atomic_uint16)(oldValue - src); } while (c89atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue); (void)order; return oldValue; } static C89ATOMIC_INLINE c89atomic_uint32 c89atomic_fetch_sub_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order) { c89atomic_uint32 oldValue; c89atomic_uint32 newValue; do { oldValue = *dst; newValue = oldValue - src; } while (c89atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue); (void)order; return oldValue; } static C89ATOMIC_INLINE c89atomic_uint64 c89atomic_fetch_sub_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order) { c89atomic_uint64 oldValue; c89atomic_uint64 newValue; do { oldValue = *dst; newValue = oldValue - src; } while (c89atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); (void)order; return oldValue; } static C89ATOMIC_INLINE c89atomic_uint8 c89atomic_fetch_and_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order) { c89atomic_uint8 oldValue; c89atomic_uint8 newValue; do { oldValue = *dst; newValue = (c89atomic_uint8)(oldValue & src); } while (c89atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue); (void)order; return oldValue; } static C89ATOMIC_INLINE c89atomic_uint16 c89atomic_fetch_and_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order) { c89atomic_uint16 oldValue; c89atomic_uint16 newValue; do { oldValue = *dst; newValue = (c89atomic_uint16)(oldValue & src); } while (c89atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue); (void)order; return oldValue; } static C89ATOMIC_INLINE c89atomic_uint32 c89atomic_fetch_and_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order) { c89atomic_uint32 oldValue; c89atomic_uint32 newValue; do { oldValue = *dst; newValue = oldValue & src; } while (c89atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue); (void)order; return oldValue; } static C89ATOMIC_INLINE c89atomic_uint64 c89atomic_fetch_and_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order) { c89atomic_uint64 oldValue; c89atomic_uint64 newValue; do { oldValue = *dst; newValue = oldValue & src; } while (c89atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); (void)order; return oldValue; } static C89ATOMIC_INLINE c89atomic_uint8 c89atomic_fetch_xor_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order) { c89atomic_uint8 oldValue; c89atomic_uint8 newValue; do { oldValue = *dst; newValue = (c89atomic_uint8)(oldValue ^ src); } while (c89atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue); (void)order; return oldValue; } static C89ATOMIC_INLINE c89atomic_uint16 c89atomic_fetch_xor_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order) { c89atomic_uint16 oldValue; c89atomic_uint16 newValue; do { oldValue = *dst; newValue = (c89atomic_uint16)(oldValue ^ src); } while (c89atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue); (void)order; return oldValue; } static C89ATOMIC_INLINE c89atomic_uint32 c89atomic_fetch_xor_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order) { c89atomic_uint32 oldValue; c89atomic_uint32 newValue; do { oldValue = *dst; newValue = oldValue ^ src; } while (c89atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue); (void)order; return oldValue; } static C89ATOMIC_INLINE c89atomic_uint64 c89atomic_fetch_xor_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order) { c89atomic_uint64 oldValue; c89atomic_uint64 newValue; do { oldValue = *dst; newValue = oldValue ^ src; } while (c89atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); (void)order; return oldValue; } static C89ATOMIC_INLINE c89atomic_uint8 c89atomic_fetch_or_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order) { c89atomic_uint8 oldValue; c89atomic_uint8 newValue; do { oldValue = *dst; newValue = (c89atomic_uint8)(oldValue | src); } while (c89atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue); (void)order; return oldValue; } static C89ATOMIC_INLINE c89atomic_uint16 c89atomic_fetch_or_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order) { c89atomic_uint16 oldValue; c89atomic_uint16 newValue; do { oldValue = *dst; newValue = (c89atomic_uint16)(oldValue | src); } while (c89atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue); (void)order; return oldValue; } static C89ATOMIC_INLINE c89atomic_uint32 c89atomic_fetch_or_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order) { c89atomic_uint32 oldValue; c89atomic_uint32 newValue; do { oldValue = *dst; newValue = oldValue | src; } while (c89atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue); (void)order; return oldValue; } static C89ATOMIC_INLINE c89atomic_uint64 c89atomic_fetch_or_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order) { c89atomic_uint64 oldValue; c89atomic_uint64 newValue; do { oldValue = *dst; newValue = oldValue | src; } while (c89atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); (void)order; return oldValue; } #endif #define c89atomic_signal_fence(order) c89atomic_thread_fence(order) static C89ATOMIC_INLINE c89atomic_uint8 c89atomic_load_explicit_8(volatile const c89atomic_uint8* ptr, c89atomic_memory_order order) { (void)order; return c89atomic_compare_and_swap_8((c89atomic_uint8*)ptr, 0, 0); } static C89ATOMIC_INLINE c89atomic_uint16 c89atomic_load_explicit_16(volatile const c89atomic_uint16* ptr, c89atomic_memory_order order) { (void)order; return c89atomic_compare_and_swap_16((c89atomic_uint16*)ptr, 0, 0); } static C89ATOMIC_INLINE c89atomic_uint32 c89atomic_load_explicit_32(volatile const c89atomic_uint32* ptr, c89atomic_memory_order order) { (void)order; return c89atomic_compare_and_swap_32((c89atomic_uint32*)ptr, 0, 0); } static C89ATOMIC_INLINE c89atomic_uint64 c89atomic_load_explicit_64(volatile const c89atomic_uint64* ptr, c89atomic_memory_order order) { (void)order; return c89atomic_compare_and_swap_64((c89atomic_uint64*)ptr, 0, 0); } #define c89atomic_store_explicit_8( dst, src, order) (void)c89atomic_exchange_explicit_8 (dst, src, order) #define c89atomic_store_explicit_16(dst, src, order) (void)c89atomic_exchange_explicit_16(dst, src, order) #define c89atomic_store_explicit_32(dst, src, order) (void)c89atomic_exchange_explicit_32(dst, src, order) #define c89atomic_store_explicit_64(dst, src, order) (void)c89atomic_exchange_explicit_64(dst, src, order) #define c89atomic_test_and_set_explicit_8( dst, order) c89atomic_exchange_explicit_8 (dst, 1, order) #define c89atomic_test_and_set_explicit_16(dst, order) c89atomic_exchange_explicit_16(dst, 1, order) #define c89atomic_test_and_set_explicit_32(dst, order) c89atomic_exchange_explicit_32(dst, 1, order) #define c89atomic_test_and_set_explicit_64(dst, order) c89atomic_exchange_explicit_64(dst, 1, order) #define c89atomic_clear_explicit_8( dst, order) c89atomic_store_explicit_8 (dst, 0, order) #define c89atomic_clear_explicit_16(dst, order) c89atomic_store_explicit_16(dst, 0, order) #define c89atomic_clear_explicit_32(dst, order) c89atomic_store_explicit_32(dst, 0, order) #define c89atomic_clear_explicit_64(dst, order) c89atomic_store_explicit_64(dst, 0, order) typedef c89atomic_uint8 c89atomic_flag; #define c89atomic_flag_test_and_set_explicit(ptr, order) (c89atomic_bool)c89atomic_test_and_set_explicit_8(ptr, order) #define c89atomic_flag_clear_explicit(ptr, order) c89atomic_clear_explicit_8(ptr, order) #define c89atoimc_flag_load_explicit(ptr, order) c89atomic_load_explicit_8(ptr, order) #endif #if !defined(C89ATOMIC_HAS_NATIVE_COMPARE_EXCHANGE) #if defined(C89ATOMIC_HAS_8) c89atomic_bool c89atomic_compare_exchange_strong_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8* expected, c89atomic_uint8 desired, c89atomic_memory_order successOrder, c89atomic_memory_order failureOrder) { c89atomic_uint8 expectedValue; c89atomic_uint8 result; (void)successOrder; (void)failureOrder; expectedValue = c89atomic_load_explicit_8(expected, c89atomic_memory_order_seq_cst); result = c89atomic_compare_and_swap_8(dst, expectedValue, desired); if (result == expectedValue) { return 1; } else { c89atomic_store_explicit_8(expected, result, failureOrder); return 0; } } #endif #if defined(C89ATOMIC_HAS_16) c89atomic_bool c89atomic_compare_exchange_strong_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16* expected, c89atomic_uint16 desired, c89atomic_memory_order successOrder, c89atomic_memory_order failureOrder) { c89atomic_uint16 expectedValue; c89atomic_uint16 result; (void)successOrder; (void)failureOrder; expectedValue = c89atomic_load_explicit_16(expected, c89atomic_memory_order_seq_cst); result = c89atomic_compare_and_swap_16(dst, expectedValue, desired); if (result == expectedValue) { return 1; } else { c89atomic_store_explicit_16(expected, result, failureOrder); return 0; } } #endif #if defined(C89ATOMIC_HAS_32) c89atomic_bool c89atomic_compare_exchange_strong_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32* expected, c89atomic_uint32 desired, c89atomic_memory_order successOrder, c89atomic_memory_order failureOrder) { c89atomic_uint32 expectedValue; c89atomic_uint32 result; (void)successOrder; (void)failureOrder; expectedValue = c89atomic_load_explicit_32(expected, c89atomic_memory_order_seq_cst); result = c89atomic_compare_and_swap_32(dst, expectedValue, desired); if (result == expectedValue) { return 1; } else { c89atomic_store_explicit_32(expected, result, failureOrder); return 0; } } #endif #if defined(C89ATOMIC_HAS_64) c89atomic_bool c89atomic_compare_exchange_strong_explicit_64(volatile c89atomic_uint64* dst, volatile c89atomic_uint64* expected, c89atomic_uint64 desired, c89atomic_memory_order successOrder, c89atomic_memory_order failureOrder) { c89atomic_uint64 expectedValue; c89atomic_uint64 result; (void)successOrder; (void)failureOrder; expectedValue = c89atomic_load_explicit_64(expected, c89atomic_memory_order_seq_cst); result = c89atomic_compare_and_swap_64(dst, expectedValue, desired); if (result == expectedValue) { return 1; } else { c89atomic_store_explicit_64(expected, result, failureOrder); return 0; } } #endif #define c89atomic_compare_exchange_weak_explicit_8( dst, expected, desired, successOrder, failureOrder) c89atomic_compare_exchange_strong_explicit_8 (dst, expected, desired, successOrder, failureOrder) #define c89atomic_compare_exchange_weak_explicit_16(dst, expected, desired, successOrder, failureOrder) c89atomic_compare_exchange_strong_explicit_16(dst, expected, desired, successOrder, failureOrder) #define c89atomic_compare_exchange_weak_explicit_32(dst, expected, desired, successOrder, failureOrder) c89atomic_compare_exchange_strong_explicit_32(dst, expected, desired, successOrder, failureOrder) #define c89atomic_compare_exchange_weak_explicit_64(dst, expected, desired, successOrder, failureOrder) c89atomic_compare_exchange_strong_explicit_64(dst, expected, desired, successOrder, failureOrder) #endif #if !defined(C89ATOMIC_HAS_NATIVE_IS_LOCK_FREE) static C89ATOMIC_INLINE c89atomic_bool c89atomic_is_lock_free_8(volatile void* ptr) { (void)ptr; return 1; } static C89ATOMIC_INLINE c89atomic_bool c89atomic_is_lock_free_16(volatile void* ptr) { (void)ptr; return 1; } static C89ATOMIC_INLINE c89atomic_bool c89atomic_is_lock_free_32(volatile void* ptr) { (void)ptr; return 1; } static C89ATOMIC_INLINE c89atomic_bool c89atomic_is_lock_free_64(volatile void* ptr) { (void)ptr; #if defined(C89ATOMIC_64BIT) return 1; #else #if defined(C89ATOMIC_X86) || defined(C89ATOMIC_X64) return 1; #else return 0; #endif #endif } #endif #if defined(C89ATOMIC_64BIT) static C89ATOMIC_INLINE c89atomic_bool c89atomic_is_lock_free_ptr(volatile void** ptr) { return c89atomic_is_lock_free_64((volatile c89atomic_uint64*)ptr); } static C89ATOMIC_INLINE void* c89atomic_load_explicit_ptr(volatile void** ptr, c89atomic_memory_order order) { return (void*)c89atomic_load_explicit_64((volatile c89atomic_uint64*)ptr, order); } static C89ATOMIC_INLINE void c89atomic_store_explicit_ptr(volatile void** dst, void* src, c89atomic_memory_order order) { c89atomic_store_explicit_64((volatile c89atomic_uint64*)dst, (c89atomic_uint64)src, order); } static C89ATOMIC_INLINE void* c89atomic_exchange_explicit_ptr(volatile void** dst, void* src, c89atomic_memory_order order) { return (void*)c89atomic_exchange_explicit_64((volatile c89atomic_uint64*)dst, (c89atomic_uint64)src, order); } static C89ATOMIC_INLINE c89atomic_bool c89atomic_compare_exchange_strong_explicit_ptr(volatile void** dst, void** expected, void* desired, c89atomic_memory_order successOrder, c89atomic_memory_order failureOrder) { return c89atomic_compare_exchange_strong_explicit_64((volatile c89atomic_uint64*)dst, (c89atomic_uint64*)expected, (c89atomic_uint64)desired, successOrder, failureOrder); } static C89ATOMIC_INLINE c89atomic_bool c89atomic_compare_exchange_weak_explicit_ptr(volatile void** dst, void** expected, void* desired, c89atomic_memory_order successOrder, c89atomic_memory_order failureOrder) { return c89atomic_compare_exchange_weak_explicit_64((volatile c89atomic_uint64*)dst, (c89atomic_uint64*)expected, (c89atomic_uint64)desired, successOrder, failureOrder); } static C89ATOMIC_INLINE void* c89atomic_compare_and_swap_ptr(volatile void** dst, void* expected, void* desired) { return (void*)c89atomic_compare_and_swap_64((volatile c89atomic_uint64*)dst, (c89atomic_uint64)expected, (c89atomic_uint64)desired); } #elif defined(C89ATOMIC_32BIT) static C89ATOMIC_INLINE c89atomic_bool c89atomic_is_lock_free_ptr(volatile void** ptr) { return c89atomic_is_lock_free_32((volatile c89atomic_uint32*)ptr); } static C89ATOMIC_INLINE void* c89atomic_load_explicit_ptr(volatile void** ptr, c89atomic_memory_order order) { return (void*)c89atomic_load_explicit_32((volatile c89atomic_uint32*)ptr, order); } static C89ATOMIC_INLINE void c89atomic_store_explicit_ptr(volatile void** dst, void* src, c89atomic_memory_order order) { c89atomic_store_explicit_32((volatile c89atomic_uint32*)dst, (c89atomic_uint32)src, order); } static C89ATOMIC_INLINE void* c89atomic_exchange_explicit_ptr(volatile void** dst, void* src, c89atomic_memory_order order) { return (void*)c89atomic_exchange_explicit_32((volatile c89atomic_uint32*)dst, (c89atomic_uint32)src, order); } static C89ATOMIC_INLINE c89atomic_bool c89atomic_compare_exchange_strong_explicit_ptr(volatile void** dst, void** expected, void* desired, c89atomic_memory_order successOrder, c89atomic_memory_order failureOrder) { return c89atomic_compare_exchange_strong_explicit_32((volatile c89atomic_uint32*)dst, (c89atomic_uint32*)expected, (c89atomic_uint32)desired, successOrder, failureOrder); } static C89ATOMIC_INLINE c89atomic_bool c89atomic_compare_exchange_weak_explicit_ptr(volatile void** dst, void** expected, void* desired, c89atomic_memory_order successOrder, c89atomic_memory_order failureOrder) { return c89atomic_compare_exchange_weak_explicit_32((volatile c89atomic_uint32*)dst, (c89atomic_uint32*)expected, (c89atomic_uint32)desired, successOrder, failureOrder); } static C89ATOMIC_INLINE void* c89atomic_compare_and_swap_ptr(volatile void** dst, void* expected, void* desired) { return (void*)c89atomic_compare_and_swap_32((volatile c89atomic_uint32*)dst, (c89atomic_uint32)expected, (c89atomic_uint32)desired); } #else #error Unsupported architecture. #endif #define c89atomic_flag_test_and_set(ptr) c89atomic_flag_test_and_set_explicit(ptr, c89atomic_memory_order_seq_cst) #define c89atomic_flag_clear(ptr) c89atomic_flag_clear_explicit(ptr, c89atomic_memory_order_seq_cst) #define c89atomic_store_ptr(dst, src) c89atomic_store_explicit_ptr((volatile void**)dst, (void*)src, c89atomic_memory_order_seq_cst) #define c89atomic_load_ptr(ptr) c89atomic_load_explicit_ptr((volatile void**)ptr, c89atomic_memory_order_seq_cst) #define c89atomic_exchange_ptr(dst, src) c89atomic_exchange_explicit_ptr((volatile void**)dst, (void*)src, c89atomic_memory_order_seq_cst) #define c89atomic_compare_exchange_strong_ptr(dst, expected, desired) c89atomic_compare_exchange_strong_explicit_ptr((volatile void**)dst, (void**)expected, (void*)desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst) #define c89atomic_compare_exchange_weak_ptr(dst, expected, desired) c89atomic_compare_exchange_weak_explicit_ptr((volatile void**)dst, (void**)expected, (void*)desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst) #define c89atomic_test_and_set_8( ptr) c89atomic_test_and_set_explicit_8( ptr, c89atomic_memory_order_seq_cst) #define c89atomic_test_and_set_16(ptr) c89atomic_test_and_set_explicit_16(ptr, c89atomic_memory_order_seq_cst) #define c89atomic_test_and_set_32(ptr) c89atomic_test_and_set_explicit_32(ptr, c89atomic_memory_order_seq_cst) #define c89atomic_test_and_set_64(ptr) c89atomic_test_and_set_explicit_64(ptr, c89atomic_memory_order_seq_cst) #define c89atomic_clear_8( ptr) c89atomic_clear_explicit_8( ptr, c89atomic_memory_order_seq_cst) #define c89atomic_clear_16(ptr) c89atomic_clear_explicit_16(ptr, c89atomic_memory_order_seq_cst) #define c89atomic_clear_32(ptr) c89atomic_clear_explicit_32(ptr, c89atomic_memory_order_seq_cst) #define c89atomic_clear_64(ptr) c89atomic_clear_explicit_64(ptr, c89atomic_memory_order_seq_cst) #define c89atomic_store_8( dst, src) c89atomic_store_explicit_8( dst, src, c89atomic_memory_order_seq_cst) #define c89atomic_store_16(dst, src) c89atomic_store_explicit_16(dst, src, c89atomic_memory_order_seq_cst) #define c89atomic_store_32(dst, src) c89atomic_store_explicit_32(dst, src, c89atomic_memory_order_seq_cst) #define c89atomic_store_64(dst, src) c89atomic_store_explicit_64(dst, src, c89atomic_memory_order_seq_cst) #define c89atomic_load_8( ptr) c89atomic_load_explicit_8( ptr, c89atomic_memory_order_seq_cst) #define c89atomic_load_16(ptr) c89atomic_load_explicit_16(ptr, c89atomic_memory_order_seq_cst) #define c89atomic_load_32(ptr) c89atomic_load_explicit_32(ptr, c89atomic_memory_order_seq_cst) #define c89atomic_load_64(ptr) c89atomic_load_explicit_64(ptr, c89atomic_memory_order_seq_cst) #define c89atomic_exchange_8( dst, src) c89atomic_exchange_explicit_8( dst, src, c89atomic_memory_order_seq_cst) #define c89atomic_exchange_16(dst, src) c89atomic_exchange_explicit_16(dst, src, c89atomic_memory_order_seq_cst) #define c89atomic_exchange_32(dst, src) c89atomic_exchange_explicit_32(dst, src, c89atomic_memory_order_seq_cst) #define c89atomic_exchange_64(dst, src) c89atomic_exchange_explicit_64(dst, src, c89atomic_memory_order_seq_cst) #define c89atomic_compare_exchange_strong_8( dst, expected, desired) c89atomic_compare_exchange_strong_explicit_8( dst, expected, desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst) #define c89atomic_compare_exchange_strong_16(dst, expected, desired) c89atomic_compare_exchange_strong_explicit_16(dst, expected, desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst) #define c89atomic_compare_exchange_strong_32(dst, expected, desired) c89atomic_compare_exchange_strong_explicit_32(dst, expected, desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst) #define c89atomic_compare_exchange_strong_64(dst, expected, desired) c89atomic_compare_exchange_strong_explicit_64(dst, expected, desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst) #define c89atomic_compare_exchange_weak_8( dst, expected, desired) c89atomic_compare_exchange_weak_explicit_8( dst, expected, desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst) #define c89atomic_compare_exchange_weak_16( dst, expected, desired) c89atomic_compare_exchange_weak_explicit_16(dst, expected, desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst) #define c89atomic_compare_exchange_weak_32( dst, expected, desired) c89atomic_compare_exchange_weak_explicit_32(dst, expected, desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst) #define c89atomic_compare_exchange_weak_64( dst, expected, desired) c89atomic_compare_exchange_weak_explicit_64(dst, expected, desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst) #define c89atomic_fetch_add_8( dst, src) c89atomic_fetch_add_explicit_8( dst, src, c89atomic_memory_order_seq_cst) #define c89atomic_fetch_add_16(dst, src) c89atomic_fetch_add_explicit_16(dst, src, c89atomic_memory_order_seq_cst) #define c89atomic_fetch_add_32(dst, src) c89atomic_fetch_add_explicit_32(dst, src, c89atomic_memory_order_seq_cst) #define c89atomic_fetch_add_64(dst, src) c89atomic_fetch_add_explicit_64(dst, src, c89atomic_memory_order_seq_cst) #define c89atomic_fetch_sub_8( dst, src) c89atomic_fetch_sub_explicit_8( dst, src, c89atomic_memory_order_seq_cst) #define c89atomic_fetch_sub_16(dst, src) c89atomic_fetch_sub_explicit_16(dst, src, c89atomic_memory_order_seq_cst) #define c89atomic_fetch_sub_32(dst, src) c89atomic_fetch_sub_explicit_32(dst, src, c89atomic_memory_order_seq_cst) #define c89atomic_fetch_sub_64(dst, src) c89atomic_fetch_sub_explicit_64(dst, src, c89atomic_memory_order_seq_cst) #define c89atomic_fetch_or_8( dst, src) c89atomic_fetch_or_explicit_8( dst, src, c89atomic_memory_order_seq_cst) #define c89atomic_fetch_or_16(dst, src) c89atomic_fetch_or_explicit_16(dst, src, c89atomic_memory_order_seq_cst) #define c89atomic_fetch_or_32(dst, src) c89atomic_fetch_or_explicit_32(dst, src, c89atomic_memory_order_seq_cst) #define c89atomic_fetch_or_64(dst, src) c89atomic_fetch_or_explicit_64(dst, src, c89atomic_memory_order_seq_cst) #define c89atomic_fetch_xor_8( dst, src) c89atomic_fetch_xor_explicit_8( dst, src, c89atomic_memory_order_seq_cst) #define c89atomic_fetch_xor_16(dst, src) c89atomic_fetch_xor_explicit_16(dst, src, c89atomic_memory_order_seq_cst) #define c89atomic_fetch_xor_32(dst, src) c89atomic_fetch_xor_explicit_32(dst, src, c89atomic_memory_order_seq_cst) #define c89atomic_fetch_xor_64(dst, src) c89atomic_fetch_xor_explicit_64(dst, src, c89atomic_memory_order_seq_cst) #define c89atomic_fetch_and_8( dst, src) c89atomic_fetch_and_explicit_8 (dst, src, c89atomic_memory_order_seq_cst) #define c89atomic_fetch_and_16(dst, src) c89atomic_fetch_and_explicit_16(dst, src, c89atomic_memory_order_seq_cst) #define c89atomic_fetch_and_32(dst, src) c89atomic_fetch_and_explicit_32(dst, src, c89atomic_memory_order_seq_cst) #define c89atomic_fetch_and_64(dst, src) c89atomic_fetch_and_explicit_64(dst, src, c89atomic_memory_order_seq_cst) #define c89atomic_test_and_set_explicit_i8( ptr, order) (c89atomic_int8 )c89atomic_test_and_set_explicit_8( (c89atomic_uint8* )ptr, order) #define c89atomic_test_and_set_explicit_i16(ptr, order) (c89atomic_int16)c89atomic_test_and_set_explicit_16((c89atomic_uint16*)ptr, order) #define c89atomic_test_and_set_explicit_i32(ptr, order) (c89atomic_int32)c89atomic_test_and_set_explicit_32((c89atomic_uint32*)ptr, order) #define c89atomic_test_and_set_explicit_i64(ptr, order) (c89atomic_int64)c89atomic_test_and_set_explicit_64((c89atomic_uint64*)ptr, order) #define c89atomic_clear_explicit_i8( ptr, order) c89atomic_clear_explicit_8( (c89atomic_uint8* )ptr, order) #define c89atomic_clear_explicit_i16(ptr, order) c89atomic_clear_explicit_16((c89atomic_uint16*)ptr, order) #define c89atomic_clear_explicit_i32(ptr, order) c89atomic_clear_explicit_32((c89atomic_uint32*)ptr, order) #define c89atomic_clear_explicit_i64(ptr, order) c89atomic_clear_explicit_64((c89atomic_uint64*)ptr, order) #define c89atomic_store_explicit_i8( dst, src, order) (c89atomic_int8 )c89atomic_store_explicit_8( (c89atomic_uint8* )dst, (c89atomic_uint8 )src, order) #define c89atomic_store_explicit_i16(dst, src, order) (c89atomic_int16)c89atomic_store_explicit_16((c89atomic_uint16*)dst, (c89atomic_uint16)src, order) #define c89atomic_store_explicit_i32(dst, src, order) (c89atomic_int32)c89atomic_store_explicit_32((c89atomic_uint32*)dst, (c89atomic_uint32)src, order) #define c89atomic_store_explicit_i64(dst, src, order) (c89atomic_int64)c89atomic_store_explicit_64((c89atomic_uint64*)dst, (c89atomic_uint64)src, order) #define c89atomic_load_explicit_i8( ptr, order) (c89atomic_int8 )c89atomic_load_explicit_8( (c89atomic_uint8* )ptr, order) #define c89atomic_load_explicit_i16(ptr, order) (c89atomic_int16)c89atomic_load_explicit_16((c89atomic_uint16*)ptr, order) #define c89atomic_load_explicit_i32(ptr, order) (c89atomic_int32)c89atomic_load_explicit_32((c89atomic_uint32*)ptr, order) #define c89atomic_load_explicit_i64(ptr, order) (c89atomic_int64)c89atomic_load_explicit_64((c89atomic_uint64*)ptr, order) #define c89atomic_exchange_explicit_i8( dst, src, order) (c89atomic_int8 )c89atomic_exchange_explicit_8 ((c89atomic_uint8* )dst, (c89atomic_uint8 )src, order) #define c89atomic_exchange_explicit_i16(dst, src, order) (c89atomic_int16)c89atomic_exchange_explicit_16((c89atomic_uint16*)dst, (c89atomic_uint16)src, order) #define c89atomic_exchange_explicit_i32(dst, src, order) (c89atomic_int32)c89atomic_exchange_explicit_32((c89atomic_uint32*)dst, (c89atomic_uint32)src, order) #define c89atomic_exchange_explicit_i64(dst, src, order) (c89atomic_int64)c89atomic_exchange_explicit_64((c89atomic_uint64*)dst, (c89atomic_uint64)src, order) #define c89atomic_compare_exchange_strong_explicit_i8( dst, expected, desired, successOrder, failureOrder) c89atomic_compare_exchange_strong_explicit_8( (c89atomic_uint8* )dst, (c89atomic_uint8* )expected, (c89atomic_uint8 )desired, successOrder, failureOrder) #define c89atomic_compare_exchange_strong_explicit_i16(dst, expected, desired, successOrder, failureOrder) c89atomic_compare_exchange_strong_explicit_16((c89atomic_uint16*)dst, (c89atomic_uint16*)expected, (c89atomic_uint16)desired, successOrder, failureOrder) #define c89atomic_compare_exchange_strong_explicit_i32(dst, expected, desired, successOrder, failureOrder) c89atomic_compare_exchange_strong_explicit_32((c89atomic_uint32*)dst, (c89atomic_uint32*)expected, (c89atomic_uint32)desired, successOrder, failureOrder) #define c89atomic_compare_exchange_strong_explicit_i64(dst, expected, desired, successOrder, failureOrder) c89atomic_compare_exchange_strong_explicit_64((c89atomic_uint64*)dst, (c89atomic_uint64*)expected, (c89atomic_uint64)desired, successOrder, failureOrder) #define c89atomic_compare_exchange_weak_explicit_i8( dst, expected, desired, successOrder, failureOrder) c89atomic_compare_exchange_weak_explicit_8( (c89atomic_uint8* )dst, (c89atomic_uint8* )expected, (c89atomic_uint8 )desired, successOrder, failureOrder) #define c89atomic_compare_exchange_weak_explicit_i16(dst, expected, desired, successOrder, failureOrder) c89atomic_compare_exchange_weak_explicit_16((c89atomic_uint16*)dst, (c89atomic_uint16*)expected, (c89atomic_uint16)desired, successOrder, failureOrder) #define c89atomic_compare_exchange_weak_explicit_i32(dst, expected, desired, successOrder, failureOrder) c89atomic_compare_exchange_weak_explicit_32((c89atomic_uint32*)dst, (c89atomic_uint32*)expected, (c89atomic_uint32)desired, successOrder, failureOrder) #define c89atomic_compare_exchange_weak_explicit_i64(dst, expected, desired, successOrder, failureOrder) c89atomic_compare_exchange_weak_explicit_64((c89atomic_uint64*)dst, (c89atomic_uint64*)expected, (c89atomic_uint64)desired, successOrder, failureOrder) #define c89atomic_fetch_add_explicit_i8( dst, src, order) (c89atomic_int8 )c89atomic_fetch_add_explicit_8( (c89atomic_uint8* )dst, (c89atomic_uint8 )src, order) #define c89atomic_fetch_add_explicit_i16(dst, src, order) (c89atomic_int16)c89atomic_fetch_add_explicit_16((c89atomic_uint16*)dst, (c89atomic_uint16)src, order) #define c89atomic_fetch_add_explicit_i32(dst, src, order) (c89atomic_int32)c89atomic_fetch_add_explicit_32((c89atomic_uint32*)dst, (c89atomic_uint32)src, order) #define c89atomic_fetch_add_explicit_i64(dst, src, order) (c89atomic_int64)c89atomic_fetch_add_explicit_64((c89atomic_uint64*)dst, (c89atomic_uint64)src, order) #define c89atomic_fetch_sub_explicit_i8( dst, src, order) (c89atomic_int8 )c89atomic_fetch_sub_explicit_8( (c89atomic_uint8* )dst, (c89atomic_uint8 )src, order) #define c89atomic_fetch_sub_explicit_i16(dst, src, order) (c89atomic_int16)c89atomic_fetch_sub_explicit_16((c89atomic_uint16*)dst, (c89atomic_uint16)src, order) #define c89atomic_fetch_sub_explicit_i32(dst, src, order) (c89atomic_int32)c89atomic_fetch_sub_explicit_32((c89atomic_uint32*)dst, (c89atomic_uint32)src, order) #define c89atomic_fetch_sub_explicit_i64(dst, src, order) (c89atomic_int64)c89atomic_fetch_sub_explicit_64((c89atomic_uint64*)dst, (c89atomic_uint64)src, order) #define c89atomic_fetch_or_explicit_i8( dst, src, order) (c89atomic_int8 )c89atomic_fetch_or_explicit_8( (c89atomic_uint8* )dst, (c89atomic_uint8 )src, order) #define c89atomic_fetch_or_explicit_i16(dst, src, order) (c89atomic_int16)c89atomic_fetch_or_explicit_16((c89atomic_uint16*)dst, (c89atomic_uint16)src, order) #define c89atomic_fetch_or_explicit_i32(dst, src, order) (c89atomic_int32)c89atomic_fetch_or_explicit_32((c89atomic_uint32*)dst, (c89atomic_uint32)src, order) #define c89atomic_fetch_or_explicit_i64(dst, src, order) (c89atomic_int64)c89atomic_fetch_or_explicit_64((c89atomic_uint64*)dst, (c89atomic_uint64)src, order) #define c89atomic_fetch_xor_explicit_i8( dst, src, order) (c89atomic_int8 )c89atomic_fetch_xor_explicit_8( (c89atomic_uint8* )dst, (c89atomic_uint8 )src, order) #define c89atomic_fetch_xor_explicit_i16(dst, src, order) (c89atomic_int16)c89atomic_fetch_xor_explicit_16((c89atomic_uint16*)dst, (c89atomic_uint16)src, order) #define c89atomic_fetch_xor_explicit_i32(dst, src, order) (c89atomic_int32)c89atomic_fetch_xor_explicit_32((c89atomic_uint32*)dst, (c89atomic_uint32)src, order) #define c89atomic_fetch_xor_explicit_i64(dst, src, order) (c89atomic_int64)c89atomic_fetch_xor_explicit_64((c89atomic_uint64*)dst, (c89atomic_uint64)src, order) #define c89atomic_fetch_and_explicit_i8( dst, src, order) (c89atomic_int8 )c89atomic_fetch_and_explicit_8( (c89atomic_uint8* )dst, (c89atomic_uint8 )src, order) #define c89atomic_fetch_and_explicit_i16(dst, src, order) (c89atomic_int16)c89atomic_fetch_and_explicit_16((c89atomic_uint16*)dst, (c89atomic_uint16)src, order) #define c89atomic_fetch_and_explicit_i32(dst, src, order) (c89atomic_int32)c89atomic_fetch_and_explicit_32((c89atomic_uint32*)dst, (c89atomic_uint32)src, order) #define c89atomic_fetch_and_explicit_i64(dst, src, order) (c89atomic_int64)c89atomic_fetch_and_explicit_64((c89atomic_uint64*)dst, (c89atomic_uint64)src, order) #define c89atomic_test_and_set_i8( ptr) c89atomic_test_and_set_explicit_i8( ptr, c89atomic_memory_order_seq_cst) #define c89atomic_test_and_set_i16(ptr) c89atomic_test_and_set_explicit_i16(ptr, c89atomic_memory_order_seq_cst) #define c89atomic_test_and_set_i32(ptr) c89atomic_test_and_set_explicit_i32(ptr, c89atomic_memory_order_seq_cst) #define c89atomic_test_and_set_i64(ptr) c89atomic_test_and_set_explicit_i64(ptr, c89atomic_memory_order_seq_cst) #define c89atomic_clear_i8( ptr) c89atomic_clear_explicit_i8( ptr, c89atomic_memory_order_seq_cst) #define c89atomic_clear_i16(ptr) c89atomic_clear_explicit_i16(ptr, c89atomic_memory_order_seq_cst) #define c89atomic_clear_i32(ptr) c89atomic_clear_explicit_i32(ptr, c89atomic_memory_order_seq_cst) #define c89atomic_clear_i64(ptr) c89atomic_clear_explicit_i64(ptr, c89atomic_memory_order_seq_cst) #define c89atomic_store_i8( dst, src) c89atomic_store_explicit_i8( dst, src, c89atomic_memory_order_seq_cst) #define c89atomic_store_i16(dst, src) c89atomic_store_explicit_i16(dst, src, c89atomic_memory_order_seq_cst) #define c89atomic_store_i32(dst, src) c89atomic_store_explicit_i32(dst, src, c89atomic_memory_order_seq_cst) #define c89atomic_store_i64(dst, src) c89atomic_store_explicit_i64(dst, src, c89atomic_memory_order_seq_cst) #define c89atomic_load_i8( ptr) c89atomic_load_explicit_i8( ptr, c89atomic_memory_order_seq_cst) #define c89atomic_load_i16(ptr) c89atomic_load_explicit_i16(ptr, c89atomic_memory_order_seq_cst) #define c89atomic_load_i32(ptr) c89atomic_load_explicit_i32(ptr, c89atomic_memory_order_seq_cst) #define c89atomic_load_i64(ptr) c89atomic_load_explicit_i64(ptr, c89atomic_memory_order_seq_cst) #define c89atomic_exchange_i8( dst, src) c89atomic_exchange_explicit_i8( dst, src, c89atomic_memory_order_seq_cst) #define c89atomic_exchange_i16(dst, src) c89atomic_exchange_explicit_i16(dst, src, c89atomic_memory_order_seq_cst) #define c89atomic_exchange_i32(dst, src) c89atomic_exchange_explicit_i32(dst, src, c89atomic_memory_order_seq_cst) #define c89atomic_exchange_i64(dst, src) c89atomic_exchange_explicit_i64(dst, src, c89atomic_memory_order_seq_cst) #define c89atomic_compare_exchange_strong_i8( dst, expected, desired) c89atomic_compare_exchange_strong_explicit_i8( dst, expected, desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst) #define c89atomic_compare_exchange_strong_i16(dst, expected, desired) c89atomic_compare_exchange_strong_explicit_i16(dst, expected, desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst) #define c89atomic_compare_exchange_strong_i32(dst, expected, desired) c89atomic_compare_exchange_strong_explicit_i32(dst, expected, desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst) #define c89atomic_compare_exchange_strong_i64(dst, expected, desired) c89atomic_compare_exchange_strong_explicit_i64(dst, expected, desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst) #define c89atomic_compare_exchange_weak_i8( dst, expected, desired) c89atomic_compare_exchange_weak_explicit_i8( dst, expected, desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst) #define c89atomic_compare_exchange_weak_i16(dst, expected, desired) c89atomic_compare_exchange_weak_explicit_i16(dst, expected, desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst) #define c89atomic_compare_exchange_weak_i32(dst, expected, desired) c89atomic_compare_exchange_weak_explicit_i32(dst, expected, desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst) #define c89atomic_compare_exchange_weak_i64(dst, expected, desired) c89atomic_compare_exchange_weak_explicit_i64(dst, expected, desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst) #define c89atomic_fetch_add_i8( dst, src) c89atomic_fetch_add_explicit_i8( dst, src, c89atomic_memory_order_seq_cst) #define c89atomic_fetch_add_i16(dst, src) c89atomic_fetch_add_explicit_i16(dst, src, c89atomic_memory_order_seq_cst) #define c89atomic_fetch_add_i32(dst, src) c89atomic_fetch_add_explicit_i32(dst, src, c89atomic_memory_order_seq_cst) #define c89atomic_fetch_add_i64(dst, src) c89atomic_fetch_add_explicit_i64(dst, src, c89atomic_memory_order_seq_cst) #define c89atomic_fetch_sub_i8( dst, src) c89atomic_fetch_sub_explicit_i8( dst, src, c89atomic_memory_order_seq_cst) #define c89atomic_fetch_sub_i16(dst, src) c89atomic_fetch_sub_explicit_i16(dst, src, c89atomic_memory_order_seq_cst) #define c89atomic_fetch_sub_i32(dst, src) c89atomic_fetch_sub_explicit_i32(dst, src, c89atomic_memory_order_seq_cst) #define c89atomic_fetch_sub_i64(dst, src) c89atomic_fetch_sub_explicit_i64(dst, src, c89atomic_memory_order_seq_cst) #define c89atomic_fetch_or_i8( dst, src) c89atomic_fetch_or_explicit_i8( dst, src, c89atomic_memory_order_seq_cst) #define c89atomic_fetch_or_i16(dst, src) c89atomic_fetch_or_explicit_i16(dst, src, c89atomic_memory_order_seq_cst) #define c89atomic_fetch_or_i32(dst, src) c89atomic_fetch_or_explicit_i32(dst, src, c89atomic_memory_order_seq_cst) #define c89atomic_fetch_or_i64(dst, src) c89atomic_fetch_or_explicit_i64(dst, src, c89atomic_memory_order_seq_cst) #define c89atomic_fetch_xor_i8( dst, src) c89atomic_fetch_xor_explicit_i8( dst, src, c89atomic_memory_order_seq_cst) #define c89atomic_fetch_xor_i16(dst, src) c89atomic_fetch_xor_explicit_i16(dst, src, c89atomic_memory_order_seq_cst) #define c89atomic_fetch_xor_i32(dst, src) c89atomic_fetch_xor_explicit_i32(dst, src, c89atomic_memory_order_seq_cst) #define c89atomic_fetch_xor_i64(dst, src) c89atomic_fetch_xor_explicit_i64(dst, src, c89atomic_memory_order_seq_cst) #define c89atomic_fetch_and_i8( dst, src) c89atomic_fetch_and_explicit_i8( dst, src, c89atomic_memory_order_seq_cst) #define c89atomic_fetch_and_i16(dst, src) c89atomic_fetch_and_explicit_i16(dst, src, c89atomic_memory_order_seq_cst) #define c89atomic_fetch_and_i32(dst, src) c89atomic_fetch_and_explicit_i32(dst, src, c89atomic_memory_order_seq_cst) #define c89atomic_fetch_and_i64(dst, src) c89atomic_fetch_and_explicit_i64(dst, src, c89atomic_memory_order_seq_cst) #define c89atomic_compare_and_swap_i8( dst, expected, dedsired) (c89atomic_int8 )c89atomic_compare_and_swap_8( (c89atomic_uint8* )dst, (c89atomic_uint8 )expected, (c89atomic_uint8 )dedsired) #define c89atomic_compare_and_swap_i16(dst, expected, dedsired) (c89atomic_int16)c89atomic_compare_and_swap_16((c89atomic_uint16*)dst, (c89atomic_uint16)expected, (c89atomic_uint16)dedsired) #define c89atomic_compare_and_swap_i32(dst, expected, dedsired) (c89atomic_int32)c89atomic_compare_and_swap_32((c89atomic_uint32*)dst, (c89atomic_uint32)expected, (c89atomic_uint32)dedsired) #define c89atomic_compare_and_swap_i64(dst, expected, dedsired) (c89atomic_int64)c89atomic_compare_and_swap_64((c89atomic_uint64*)dst, (c89atomic_uint64)expected, (c89atomic_uint64)dedsired) typedef union { c89atomic_uint32 i; float f; } c89atomic_if32; typedef union { c89atomic_uint64 i; double f; } c89atomic_if64; #define c89atomic_clear_explicit_f32(ptr, order) c89atomic_clear_explicit_32((c89atomic_uint32*)ptr, order) #define c89atomic_clear_explicit_f64(ptr, order) c89atomic_clear_explicit_64((c89atomic_uint64*)ptr, order) static C89ATOMIC_INLINE void c89atomic_store_explicit_f32(volatile float* dst, float src, c89atomic_memory_order order) { c89atomic_if32 x; x.f = src; c89atomic_store_explicit_32((volatile c89atomic_uint32*)dst, x.i, order); } static C89ATOMIC_INLINE void c89atomic_store_explicit_f64(volatile double* dst, double src, c89atomic_memory_order order) { c89atomic_if64 x; x.f = src; c89atomic_store_explicit_64((volatile c89atomic_uint64*)dst, x.i, order); } static C89ATOMIC_INLINE float c89atomic_load_explicit_f32(volatile const float* ptr, c89atomic_memory_order order) { c89atomic_if32 r; r.i = c89atomic_load_explicit_32((volatile const c89atomic_uint32*)ptr, order); return r.f; } static C89ATOMIC_INLINE double c89atomic_load_explicit_f64(volatile const double* ptr, c89atomic_memory_order order) { c89atomic_if64 r; r.i = c89atomic_load_explicit_64((volatile const c89atomic_uint64*)ptr, order); return r.f; } static C89ATOMIC_INLINE float c89atomic_exchange_explicit_f32(volatile float* dst, float src, c89atomic_memory_order order) { c89atomic_if32 r; c89atomic_if32 x; x.f = src; r.i = c89atomic_exchange_explicit_32((volatile c89atomic_uint32*)dst, x.i, order); return r.f; } static C89ATOMIC_INLINE double c89atomic_exchange_explicit_f64(volatile double* dst, double src, c89atomic_memory_order order) { c89atomic_if64 r; c89atomic_if64 x; x.f = src; r.i = c89atomic_exchange_explicit_64((volatile c89atomic_uint64*)dst, x.i, order); return r.f; } #define c89atomic_clear_f32(ptr) (float )c89atomic_clear_explicit_f32(ptr, c89atomic_memory_order_seq_cst) #define c89atomic_clear_f64(ptr) (double)c89atomic_clear_explicit_f64(ptr, c89atomic_memory_order_seq_cst) #define c89atomic_store_f32(dst, src) c89atomic_store_explicit_f32(dst, src, c89atomic_memory_order_seq_cst) #define c89atomic_store_f64(dst, src) c89atomic_store_explicit_f64(dst, src, c89atomic_memory_order_seq_cst) #define c89atomic_load_f32(ptr) (float )c89atomic_load_explicit_f32(ptr, c89atomic_memory_order_seq_cst) #define c89atomic_load_f64(ptr) (double)c89atomic_load_explicit_f64(ptr, c89atomic_memory_order_seq_cst) #define c89atomic_exchange_f32(dst, src) (float )c89atomic_exchange_explicit_f32(dst, src, c89atomic_memory_order_seq_cst) #define c89atomic_exchange_f64(dst, src) (double)c89atomic_exchange_explicit_f64(dst, src, c89atomic_memory_order_seq_cst) typedef c89atomic_flag c89atomic_spinlock; static C89ATOMIC_INLINE void c89atomic_spinlock_lock(volatile c89atomic_spinlock* pSpinlock) { for (;;) { if (c89atomic_flag_test_and_set_explicit(pSpinlock, c89atomic_memory_order_acquire) == 0) { break; } while (c89atoimc_flag_load_explicit(pSpinlock, c89atomic_memory_order_relaxed) == 1) { } } } static C89ATOMIC_INLINE void c89atomic_spinlock_unlock(volatile c89atomic_spinlock* pSpinlock) { c89atomic_flag_clear_explicit(pSpinlock, c89atomic_memory_order_release); } #if defined(__cplusplus) } #endif #endif /* c89atomic.h end */ MA_API ma_uint64 ma_calculate_frame_count_after_resampling(ma_uint32 sampleRateOut, ma_uint32 sampleRateIn, ma_uint64 frameCountIn) { /* This is based on the calculation in ma_linear_resampler_get_expected_output_frame_count(). */ ma_uint64 outputFrameCount; ma_uint64 preliminaryInputFrameCountFromFrac; ma_uint64 preliminaryInputFrameCount; if (sampleRateIn == 0 || sampleRateOut == 0 || frameCountIn == 0) { return 0; } if (sampleRateOut == sampleRateIn) { return frameCountIn; } outputFrameCount = (frameCountIn * sampleRateOut) / sampleRateIn; preliminaryInputFrameCountFromFrac = (outputFrameCount * (sampleRateIn / sampleRateOut)) / sampleRateOut; preliminaryInputFrameCount = (outputFrameCount * (sampleRateIn % sampleRateOut)) + preliminaryInputFrameCountFromFrac; if (preliminaryInputFrameCount <= frameCountIn) { outputFrameCount += 1; } return outputFrameCount; } #ifndef MA_DATA_CONVERTER_STACK_BUFFER_SIZE #define MA_DATA_CONVERTER_STACK_BUFFER_SIZE 4096 #endif #if defined(MA_WIN32) static ma_result ma_result_from_GetLastError(DWORD error) { switch (error) { case ERROR_SUCCESS: return MA_SUCCESS; case ERROR_PATH_NOT_FOUND: return MA_DOES_NOT_EXIST; case ERROR_TOO_MANY_OPEN_FILES: return MA_TOO_MANY_OPEN_FILES; case ERROR_NOT_ENOUGH_MEMORY: return MA_OUT_OF_MEMORY; case ERROR_DISK_FULL: return MA_NO_SPACE; case ERROR_HANDLE_EOF: return MA_AT_END; case ERROR_NEGATIVE_SEEK: return MA_BAD_SEEK; case ERROR_INVALID_PARAMETER: return MA_INVALID_ARGS; case ERROR_ACCESS_DENIED: return MA_ACCESS_DENIED; case ERROR_SEM_TIMEOUT: return MA_TIMEOUT; case ERROR_FILE_NOT_FOUND: return MA_DOES_NOT_EXIST; default: break; } return MA_ERROR; } #endif /* MA_WIN32 */ /******************************************************************************* Threading *******************************************************************************/ static MA_INLINE ma_result ma_spinlock_lock_ex(volatile ma_spinlock* pSpinlock, ma_bool32 yield) { if (pSpinlock == NULL) { return MA_INVALID_ARGS; } for (;;) { if (c89atomic_exchange_explicit_32(pSpinlock, 1, c89atomic_memory_order_acquire) == 0) { break; } while (c89atomic_load_explicit_32(pSpinlock, c89atomic_memory_order_relaxed) == 1) { if (yield) { ma_yield(); } } } return MA_SUCCESS; } MA_API ma_result ma_spinlock_lock(volatile ma_spinlock* pSpinlock) { return ma_spinlock_lock_ex(pSpinlock, MA_TRUE); } MA_API ma_result ma_spinlock_lock_noyield(volatile ma_spinlock* pSpinlock) { return ma_spinlock_lock_ex(pSpinlock, MA_FALSE); } MA_API ma_result ma_spinlock_unlock(volatile ma_spinlock* pSpinlock) { if (pSpinlock == NULL) { return MA_INVALID_ARGS; } c89atomic_store_explicit_32(pSpinlock, 0, c89atomic_memory_order_release); return MA_SUCCESS; } #ifndef MA_NO_THREADING #ifdef MA_WIN32 #define MA_THREADCALL WINAPI typedef unsigned long ma_thread_result; #else #define MA_THREADCALL typedef void* ma_thread_result; #endif typedef ma_thread_result (MA_THREADCALL * ma_thread_entry_proc)(void* pData); #ifdef MA_WIN32 static int ma_thread_priority_to_win32(ma_thread_priority priority) { switch (priority) { case ma_thread_priority_idle: return THREAD_PRIORITY_IDLE; case ma_thread_priority_lowest: return THREAD_PRIORITY_LOWEST; case ma_thread_priority_low: return THREAD_PRIORITY_BELOW_NORMAL; case ma_thread_priority_normal: return THREAD_PRIORITY_NORMAL; case ma_thread_priority_high: return THREAD_PRIORITY_ABOVE_NORMAL; case ma_thread_priority_highest: return THREAD_PRIORITY_HIGHEST; case ma_thread_priority_realtime: return THREAD_PRIORITY_TIME_CRITICAL; default: return THREAD_PRIORITY_NORMAL; } } static ma_result ma_thread_create__win32(ma_thread* pThread, ma_thread_priority priority, size_t stackSize, ma_thread_entry_proc entryProc, void* pData) { *pThread = CreateThread(NULL, stackSize, entryProc, pData, 0, NULL); if (*pThread == NULL) { return ma_result_from_GetLastError(GetLastError()); } SetThreadPriority((HANDLE)*pThread, ma_thread_priority_to_win32(priority)); return MA_SUCCESS; } static void ma_thread_wait__win32(ma_thread* pThread) { WaitForSingleObject((HANDLE)*pThread, INFINITE); CloseHandle((HANDLE)*pThread); } static ma_result ma_mutex_init__win32(ma_mutex* pMutex) { *pMutex = CreateEventW(NULL, FALSE, TRUE, NULL); if (*pMutex == NULL) { return ma_result_from_GetLastError(GetLastError()); } return MA_SUCCESS; } static void ma_mutex_uninit__win32(ma_mutex* pMutex) { CloseHandle((HANDLE)*pMutex); } static void ma_mutex_lock__win32(ma_mutex* pMutex) { WaitForSingleObject((HANDLE)*pMutex, INFINITE); } static void ma_mutex_unlock__win32(ma_mutex* pMutex) { SetEvent((HANDLE)*pMutex); } static ma_result ma_event_init__win32(ma_event* pEvent) { *pEvent = CreateEventW(NULL, FALSE, FALSE, NULL); if (*pEvent == NULL) { return ma_result_from_GetLastError(GetLastError()); } return MA_SUCCESS; } static void ma_event_uninit__win32(ma_event* pEvent) { CloseHandle((HANDLE)*pEvent); } static ma_result ma_event_wait__win32(ma_event* pEvent) { DWORD result = WaitForSingleObject((HANDLE)*pEvent, INFINITE); if (result == WAIT_OBJECT_0) { return MA_SUCCESS; } if (result == WAIT_TIMEOUT) { return MA_TIMEOUT; } return ma_result_from_GetLastError(GetLastError()); } static ma_result ma_event_signal__win32(ma_event* pEvent) { BOOL result = SetEvent((HANDLE)*pEvent); if (result == 0) { return ma_result_from_GetLastError(GetLastError()); } return MA_SUCCESS; } static ma_result ma_semaphore_init__win32(int initialValue, ma_semaphore* pSemaphore) { *pSemaphore = CreateSemaphoreW(NULL, (LONG)initialValue, LONG_MAX, NULL); if (*pSemaphore == NULL) { return ma_result_from_GetLastError(GetLastError()); } return MA_SUCCESS; } static void ma_semaphore_uninit__win32(ma_semaphore* pSemaphore) { CloseHandle((HANDLE)*pSemaphore); } static ma_result ma_semaphore_wait__win32(ma_semaphore* pSemaphore) { DWORD result = WaitForSingleObject((HANDLE)*pSemaphore, INFINITE); if (result == WAIT_OBJECT_0) { return MA_SUCCESS; } if (result == WAIT_TIMEOUT) { return MA_TIMEOUT; } return ma_result_from_GetLastError(GetLastError()); } static ma_result ma_semaphore_release__win32(ma_semaphore* pSemaphore) { BOOL result = ReleaseSemaphore((HANDLE)*pSemaphore, 1, NULL); if (result == 0) { return ma_result_from_GetLastError(GetLastError()); } return MA_SUCCESS; } #endif #ifdef MA_POSIX static ma_result ma_thread_create__posix(ma_thread* pThread, ma_thread_priority priority, size_t stackSize, ma_thread_entry_proc entryProc, void* pData) { int result; pthread_attr_t* pAttr = NULL; #if !defined(__EMSCRIPTEN__) /* Try setting the thread priority. It's not critical if anything fails here. */ pthread_attr_t attr; if (pthread_attr_init(&attr) == 0) { int scheduler = -1; if (priority == ma_thread_priority_idle) { #ifdef SCHED_IDLE if (pthread_attr_setschedpolicy(&attr, SCHED_IDLE) == 0) { scheduler = SCHED_IDLE; } #endif } else if (priority == ma_thread_priority_realtime) { #ifdef SCHED_FIFO if (pthread_attr_setschedpolicy(&attr, SCHED_FIFO) == 0) { scheduler = SCHED_FIFO; } #endif #ifdef MA_LINUX } else { scheduler = sched_getscheduler(0); #endif } if (stackSize > 0) { pthread_attr_setstacksize(&attr, stackSize); } if (scheduler != -1) { int priorityMin = sched_get_priority_min(scheduler); int priorityMax = sched_get_priority_max(scheduler); int priorityStep = (priorityMax - priorityMin) / 7; /* 7 = number of priorities supported by miniaudio. */ struct sched_param sched; if (pthread_attr_getschedparam(&attr, &sched) == 0) { if (priority == ma_thread_priority_idle) { sched.sched_priority = priorityMin; } else if (priority == ma_thread_priority_realtime) { sched.sched_priority = priorityMax; } else { sched.sched_priority += ((int)priority + 5) * priorityStep; /* +5 because the lowest priority is -5. */ if (sched.sched_priority < priorityMin) { sched.sched_priority = priorityMin; } if (sched.sched_priority > priorityMax) { sched.sched_priority = priorityMax; } } if (pthread_attr_setschedparam(&attr, &sched) == 0) { pAttr = &attr; } } } } #else /* It's the emscripten build. We'll have a few unused parameters. */ (void)priority; (void)stackSize; #endif result = pthread_create((pthread_t*)pThread, pAttr, entryProc, pData); /* The thread attributes object is no longer required. */ if (pAttr != NULL) { pthread_attr_destroy(pAttr); } if (result != 0) { return ma_result_from_errno(result); } return MA_SUCCESS; } static void ma_thread_wait__posix(ma_thread* pThread) { pthread_join((pthread_t)*pThread, NULL); } static ma_result ma_mutex_init__posix(ma_mutex* pMutex) { int result = pthread_mutex_init((pthread_mutex_t*)pMutex, NULL); if (result != 0) { return ma_result_from_errno(result); } return MA_SUCCESS; } static void ma_mutex_uninit__posix(ma_mutex* pMutex) { pthread_mutex_destroy((pthread_mutex_t*)pMutex); } static void ma_mutex_lock__posix(ma_mutex* pMutex) { pthread_mutex_lock((pthread_mutex_t*)pMutex); } static void ma_mutex_unlock__posix(ma_mutex* pMutex) { pthread_mutex_unlock((pthread_mutex_t*)pMutex); } static ma_result ma_event_init__posix(ma_event* pEvent) { int result; result = pthread_mutex_init((pthread_mutex_t*)&pEvent->lock, NULL); if (result != 0) { return ma_result_from_errno(result); } result = pthread_cond_init((pthread_cond_t*)&pEvent->cond, NULL); if (result != 0) { pthread_mutex_destroy((pthread_mutex_t*)&pEvent->lock); return ma_result_from_errno(result); } pEvent->value = 0; return MA_SUCCESS; } static void ma_event_uninit__posix(ma_event* pEvent) { pthread_cond_destroy((pthread_cond_t*)&pEvent->cond); pthread_mutex_destroy((pthread_mutex_t*)&pEvent->lock); } static ma_result ma_event_wait__posix(ma_event* pEvent) { pthread_mutex_lock((pthread_mutex_t*)&pEvent->lock); { while (pEvent->value == 0) { pthread_cond_wait((pthread_cond_t*)&pEvent->cond, (pthread_mutex_t*)&pEvent->lock); } pEvent->value = 0; /* Auto-reset. */ } pthread_mutex_unlock((pthread_mutex_t*)&pEvent->lock); return MA_SUCCESS; } static ma_result ma_event_signal__posix(ma_event* pEvent) { pthread_mutex_lock((pthread_mutex_t*)&pEvent->lock); { pEvent->value = 1; pthread_cond_signal((pthread_cond_t*)&pEvent->cond); } pthread_mutex_unlock((pthread_mutex_t*)&pEvent->lock); return MA_SUCCESS; } static ma_result ma_semaphore_init__posix(int initialValue, ma_semaphore* pSemaphore) { int result; if (pSemaphore == NULL) { return MA_INVALID_ARGS; } pSemaphore->value = initialValue; result = pthread_mutex_init((pthread_mutex_t*)&pSemaphore->lock, NULL); if (result != 0) { return ma_result_from_errno(result); /* Failed to create mutex. */ } result = pthread_cond_init((pthread_cond_t*)&pSemaphore->cond, NULL); if (result != 0) { pthread_mutex_destroy((pthread_mutex_t*)&pSemaphore->lock); return ma_result_from_errno(result); /* Failed to create condition variable. */ } return MA_SUCCESS; } static void ma_semaphore_uninit__posix(ma_semaphore* pSemaphore) { if (pSemaphore == NULL) { return; } pthread_cond_destroy((pthread_cond_t*)&pSemaphore->cond); pthread_mutex_destroy((pthread_mutex_t*)&pSemaphore->lock); } static ma_result ma_semaphore_wait__posix(ma_semaphore* pSemaphore) { if (pSemaphore == NULL) { return MA_INVALID_ARGS; } pthread_mutex_lock((pthread_mutex_t*)&pSemaphore->lock); { /* We need to wait on a condition variable before escaping. We can't return from this function until the semaphore has been signaled. */ while (pSemaphore->value == 0) { pthread_cond_wait((pthread_cond_t*)&pSemaphore->cond, (pthread_mutex_t*)&pSemaphore->lock); } pSemaphore->value -= 1; } pthread_mutex_unlock((pthread_mutex_t*)&pSemaphore->lock); return MA_SUCCESS; } static ma_result ma_semaphore_release__posix(ma_semaphore* pSemaphore) { if (pSemaphore == NULL) { return MA_INVALID_ARGS; } pthread_mutex_lock((pthread_mutex_t*)&pSemaphore->lock); { pSemaphore->value += 1; pthread_cond_signal((pthread_cond_t*)&pSemaphore->cond); } pthread_mutex_unlock((pthread_mutex_t*)&pSemaphore->lock); return MA_SUCCESS; } #endif typedef struct { ma_thread_entry_proc entryProc; void* pData; ma_allocation_callbacks allocationCallbacks; } ma_thread_proxy_data; static ma_thread_result MA_THREADCALL ma_thread_entry_proxy(void* pData) { ma_thread_proxy_data* pProxyData = (ma_thread_proxy_data*)pData; ma_thread_entry_proc entryProc; void* pEntryProcData; ma_thread_result result; #if defined(MA_ON_THREAD_ENTRY) MA_ON_THREAD_ENTRY #endif entryProc = pProxyData->entryProc; pEntryProcData = pProxyData->pData; /* Free the proxy data before getting into the real thread entry proc. */ ma_free(pProxyData, &pProxyData->allocationCallbacks); result = entryProc(pEntryProcData); #if defined(MA_ON_THREAD_EXIT) MA_ON_THREAD_EXIT #endif return result; } static ma_result ma_thread_create(ma_thread* pThread, ma_thread_priority priority, size_t stackSize, ma_thread_entry_proc entryProc, void* pData, const ma_allocation_callbacks* pAllocationCallbacks) { ma_result result; ma_thread_proxy_data* pProxyData; if (pThread == NULL || entryProc == NULL) { return MA_INVALID_ARGS; } pProxyData = (ma_thread_proxy_data*)ma_malloc(sizeof(*pProxyData), pAllocationCallbacks); /* Will be freed by the proxy entry proc. */ if (pProxyData == NULL) { return MA_OUT_OF_MEMORY; } pProxyData->entryProc = entryProc; pProxyData->pData = pData; ma_allocation_callbacks_init_copy(&pProxyData->allocationCallbacks, pAllocationCallbacks); #ifdef MA_WIN32 result = ma_thread_create__win32(pThread, priority, stackSize, ma_thread_entry_proxy, pProxyData); #endif #ifdef MA_POSIX result = ma_thread_create__posix(pThread, priority, stackSize, ma_thread_entry_proxy, pProxyData); #endif if (result != MA_SUCCESS) { ma_free(pProxyData, pAllocationCallbacks); return result; } return MA_SUCCESS; } static void ma_thread_wait(ma_thread* pThread) { if (pThread == NULL) { return; } #ifdef MA_WIN32 ma_thread_wait__win32(pThread); #endif #ifdef MA_POSIX ma_thread_wait__posix(pThread); #endif } MA_API ma_result ma_mutex_init(ma_mutex* pMutex) { if (pMutex == NULL) { MA_ASSERT(MA_FALSE); /* Fire an assert so the caller is aware of this bug. */ return MA_INVALID_ARGS; } #ifdef MA_WIN32 return ma_mutex_init__win32(pMutex); #endif #ifdef MA_POSIX return ma_mutex_init__posix(pMutex); #endif } MA_API void ma_mutex_uninit(ma_mutex* pMutex) { if (pMutex == NULL) { return; } #ifdef MA_WIN32 ma_mutex_uninit__win32(pMutex); #endif #ifdef MA_POSIX ma_mutex_uninit__posix(pMutex); #endif } MA_API void ma_mutex_lock(ma_mutex* pMutex) { if (pMutex == NULL) { MA_ASSERT(MA_FALSE); /* Fire an assert so the caller is aware of this bug. */ return; } #ifdef MA_WIN32 ma_mutex_lock__win32(pMutex); #endif #ifdef MA_POSIX ma_mutex_lock__posix(pMutex); #endif } MA_API void ma_mutex_unlock(ma_mutex* pMutex) { if (pMutex == NULL) { MA_ASSERT(MA_FALSE); /* Fire an assert so the caller is aware of this bug. */ return; } #ifdef MA_WIN32 ma_mutex_unlock__win32(pMutex); #endif #ifdef MA_POSIX ma_mutex_unlock__posix(pMutex); #endif } MA_API ma_result ma_event_init(ma_event* pEvent) { if (pEvent == NULL) { MA_ASSERT(MA_FALSE); /* Fire an assert so the caller is aware of this bug. */ return MA_INVALID_ARGS; } #ifdef MA_WIN32 return ma_event_init__win32(pEvent); #endif #ifdef MA_POSIX return ma_event_init__posix(pEvent); #endif } #if 0 static ma_result ma_event_alloc_and_init(ma_event** ppEvent, ma_allocation_callbacks* pAllocationCallbacks) { ma_result result; ma_event* pEvent; if (ppEvent == NULL) { return MA_INVALID_ARGS; } *ppEvent = NULL; pEvent = ma_malloc(sizeof(*pEvent), pAllocationCallbacks); if (pEvent == NULL) { return MA_OUT_OF_MEMORY; } result = ma_event_init(pEvent); if (result != MA_SUCCESS) { ma_free(pEvent, pAllocationCallbacks); return result; } *ppEvent = pEvent; return result; } #endif MA_API void ma_event_uninit(ma_event* pEvent) { if (pEvent == NULL) { return; } #ifdef MA_WIN32 ma_event_uninit__win32(pEvent); #endif #ifdef MA_POSIX ma_event_uninit__posix(pEvent); #endif } #if 0 static void ma_event_uninit_and_free(ma_event* pEvent, ma_allocation_callbacks* pAllocationCallbacks) { if (pEvent == NULL) { return; } ma_event_uninit(pEvent); ma_free(pEvent, pAllocationCallbacks); } #endif MA_API ma_result ma_event_wait(ma_event* pEvent) { if (pEvent == NULL) { MA_ASSERT(MA_FALSE); /* Fire an assert to the caller is aware of this bug. */ return MA_INVALID_ARGS; } #ifdef MA_WIN32 return ma_event_wait__win32(pEvent); #endif #ifdef MA_POSIX return ma_event_wait__posix(pEvent); #endif } MA_API ma_result ma_event_signal(ma_event* pEvent) { if (pEvent == NULL) { MA_ASSERT(MA_FALSE); /* Fire an assert to the caller is aware of this bug. */ return MA_INVALID_ARGS; } #ifdef MA_WIN32 return ma_event_signal__win32(pEvent); #endif #ifdef MA_POSIX return ma_event_signal__posix(pEvent); #endif } MA_API ma_result ma_semaphore_init(int initialValue, ma_semaphore* pSemaphore) { if (pSemaphore == NULL) { MA_ASSERT(MA_FALSE); /* Fire an assert so the caller is aware of this bug. */ return MA_INVALID_ARGS; } #ifdef MA_WIN32 return ma_semaphore_init__win32(initialValue, pSemaphore); #endif #ifdef MA_POSIX return ma_semaphore_init__posix(initialValue, pSemaphore); #endif } MA_API void ma_semaphore_uninit(ma_semaphore* pSemaphore) { if (pSemaphore == NULL) { MA_ASSERT(MA_FALSE); /* Fire an assert so the caller is aware of this bug. */ return; } #ifdef MA_WIN32 ma_semaphore_uninit__win32(pSemaphore); #endif #ifdef MA_POSIX ma_semaphore_uninit__posix(pSemaphore); #endif } MA_API ma_result ma_semaphore_wait(ma_semaphore* pSemaphore) { if (pSemaphore == NULL) { MA_ASSERT(MA_FALSE); /* Fire an assert so the caller is aware of this bug. */ return MA_INVALID_ARGS; } #ifdef MA_WIN32 return ma_semaphore_wait__win32(pSemaphore); #endif #ifdef MA_POSIX return ma_semaphore_wait__posix(pSemaphore); #endif } MA_API ma_result ma_semaphore_release(ma_semaphore* pSemaphore) { if (pSemaphore == NULL) { MA_ASSERT(MA_FALSE); /* Fire an assert so the caller is aware of this bug. */ return MA_INVALID_ARGS; } #ifdef MA_WIN32 return ma_semaphore_release__win32(pSemaphore); #endif #ifdef MA_POSIX return ma_semaphore_release__posix(pSemaphore); #endif } #else /* MA_NO_THREADING is set which means threading is disabled. Threading is required by some API families. If any of these are enabled we need to throw an error. */ #ifndef MA_NO_DEVICE_IO #error "MA_NO_THREADING cannot be used without MA_NO_DEVICE_IO"; #endif #endif /* MA_NO_THREADING */ #define MA_FENCE_COUNTER_MAX 0x7FFFFFFF MA_API ma_result ma_fence_init(ma_fence* pFence) { if (pFence == NULL) { return MA_INVALID_ARGS; } MA_ZERO_OBJECT(pFence); pFence->counter = 0; #ifndef MA_NO_THREADING { ma_result result; result = ma_event_init(&pFence->e); if (result != MA_SUCCESS) { return result; } } #endif return MA_SUCCESS; } MA_API void ma_fence_uninit(ma_fence* pFence) { if (pFence == NULL) { return; } #ifndef MA_NO_THREADING { ma_event_uninit(&pFence->e); } #endif MA_ZERO_OBJECT(pFence); } MA_API ma_result ma_fence_acquire(ma_fence* pFence) { if (pFence == NULL) { return MA_INVALID_ARGS; } for (;;) { ma_uint32 oldCounter = c89atomic_load_32(&pFence->counter); ma_uint32 newCounter = oldCounter + 1; /* Make sure we're not about to exceed our maximum value. */ if (newCounter > MA_FENCE_COUNTER_MAX) { MA_ASSERT(MA_FALSE); return MA_OUT_OF_RANGE; } if (c89atomic_compare_exchange_weak_32(&pFence->counter, &oldCounter, newCounter)) { return MA_SUCCESS; } else { if (oldCounter == MA_FENCE_COUNTER_MAX) { MA_ASSERT(MA_FALSE); return MA_OUT_OF_RANGE; /* The other thread took the last available slot. Abort. */ } } } /* Should never get here. */ /*return MA_SUCCESS;*/ } MA_API ma_result ma_fence_release(ma_fence* pFence) { if (pFence == NULL) { return MA_INVALID_ARGS; } for (;;) { ma_uint32 oldCounter = c89atomic_load_32(&pFence->counter); ma_uint32 newCounter = oldCounter - 1; if (oldCounter == 0) { MA_ASSERT(MA_FALSE); return MA_INVALID_OPERATION; /* Acquire/release mismatch. */ } if (c89atomic_compare_exchange_weak_32(&pFence->counter, &oldCounter, newCounter)) { #ifndef MA_NO_THREADING { if (newCounter == 0) { ma_event_signal(&pFence->e); /* <-- ma_fence_wait() will be waiting on this. */ } } #endif return MA_SUCCESS; } else { if (oldCounter == 0) { MA_ASSERT(MA_FALSE); return MA_INVALID_OPERATION; /* Another thread has taken the 0 slot. Acquire/release mismatch. */ } } } /* Should never get here. */ /*return MA_SUCCESS;*/ } MA_API ma_result ma_fence_wait(ma_fence* pFence) { if (pFence == NULL) { return MA_INVALID_ARGS; } for (;;) { ma_uint32 counter; counter = c89atomic_load_32(&pFence->counter); if (counter == 0) { /* Counter has hit zero. By the time we get here some other thread may have acquired the fence again, but that is where the caller needs to take care with how they se the fence. */ return MA_SUCCESS; } /* Getting here means the counter is > 0. We'll need to wait for something to happen. */ #ifndef MA_NO_THREADING { ma_result result; result = ma_event_wait(&pFence->e); if (result != MA_SUCCESS) { return result; } } #endif } /* Should never get here. */ /*return MA_INVALID_OPERATION;*/ } MA_API ma_result ma_async_notification_signal(ma_async_notification* pNotification) { ma_async_notification_callbacks* pNotificationCallbacks = (ma_async_notification_callbacks*)pNotification; if (pNotification == NULL) { return MA_INVALID_ARGS; } if (pNotificationCallbacks->onSignal == NULL) { return MA_NOT_IMPLEMENTED; } pNotificationCallbacks->onSignal(pNotification); return MA_INVALID_ARGS; } static void ma_async_notification_poll__on_signal(ma_async_notification* pNotification) { ((ma_async_notification_poll*)pNotification)->signalled = MA_TRUE; } MA_API ma_result ma_async_notification_poll_init(ma_async_notification_poll* pNotificationPoll) { if (pNotificationPoll == NULL) { return MA_INVALID_ARGS; } pNotificationPoll->cb.onSignal = ma_async_notification_poll__on_signal; pNotificationPoll->signalled = MA_FALSE; return MA_SUCCESS; } MA_API ma_bool32 ma_async_notification_poll_is_signalled(const ma_async_notification_poll* pNotificationPoll) { if (pNotificationPoll == NULL) { return MA_FALSE; } return pNotificationPoll->signalled; } static void ma_async_notification_event__on_signal(ma_async_notification* pNotification) { ma_async_notification_event_signal((ma_async_notification_event*)pNotification); } MA_API ma_result ma_async_notification_event_init(ma_async_notification_event* pNotificationEvent) { if (pNotificationEvent == NULL) { return MA_INVALID_ARGS; } pNotificationEvent->cb.onSignal = ma_async_notification_event__on_signal; #ifndef MA_NO_THREADING { ma_result result; result = ma_event_init(&pNotificationEvent->e); if (result != MA_SUCCESS) { return result; } return MA_SUCCESS; } #else { return MA_NOT_IMPLEMENTED; /* Threading is disabled. */ } #endif } MA_API ma_result ma_async_notification_event_uninit(ma_async_notification_event* pNotificationEvent) { if (pNotificationEvent == NULL) { return MA_INVALID_ARGS; } #ifndef MA_NO_THREADING { ma_event_uninit(&pNotificationEvent->e); return MA_SUCCESS; } #else { return MA_NOT_IMPLEMENTED; /* Threading is disabled. */ } #endif } MA_API ma_result ma_async_notification_event_wait(ma_async_notification_event* pNotificationEvent) { if (pNotificationEvent == NULL) { return MA_INVALID_ARGS; } #ifndef MA_NO_THREADING { return ma_event_wait(&pNotificationEvent->e); } #else { return MA_NOT_IMPLEMENTED; /* Threading is disabled. */ } #endif } MA_API ma_result ma_async_notification_event_signal(ma_async_notification_event* pNotificationEvent) { if (pNotificationEvent == NULL) { return MA_INVALID_ARGS; } #ifndef MA_NO_THREADING { return ma_event_signal(&pNotificationEvent->e); } #else { return MA_NOT_IMPLEMENTED; /* Threading is disabled. */ } #endif } /************************************************************************************************************************************************************ Job Queue ************************************************************************************************************************************************************/ MA_API ma_slot_allocator_config ma_slot_allocator_config_init(ma_uint32 capacity) { ma_slot_allocator_config config; MA_ZERO_OBJECT(&config); config.capacity = capacity; return config; } static MA_INLINE ma_uint32 ma_slot_allocator_calculate_group_capacity(ma_uint32 slotCapacity) { ma_uint32 cap = slotCapacity / 32; if ((slotCapacity % 32) != 0) { cap += 1; } return cap; } static MA_INLINE ma_uint32 ma_slot_allocator_group_capacity(const ma_slot_allocator* pAllocator) { return ma_slot_allocator_calculate_group_capacity(pAllocator->capacity); } typedef struct { size_t sizeInBytes; size_t groupsOffset; size_t slotsOffset; } ma_slot_allocator_heap_layout; static ma_result ma_slot_allocator_get_heap_layout(const ma_slot_allocator_config* pConfig, ma_slot_allocator_heap_layout* pHeapLayout) { MA_ASSERT(pHeapLayout != NULL); MA_ZERO_OBJECT(pHeapLayout); if (pConfig == NULL) { return MA_INVALID_ARGS; } if (pConfig->capacity == 0) { return MA_INVALID_ARGS; } pHeapLayout->sizeInBytes = 0; /* Groups. */ pHeapLayout->groupsOffset = pHeapLayout->sizeInBytes; pHeapLayout->sizeInBytes += ma_align_64(ma_slot_allocator_calculate_group_capacity(pConfig->capacity) * sizeof(ma_slot_allocator_group)); /* Slots. */ pHeapLayout->slotsOffset = pHeapLayout->sizeInBytes; pHeapLayout->sizeInBytes += ma_align_64(pConfig->capacity * sizeof(ma_uint32)); return MA_SUCCESS; } MA_API ma_result ma_slot_allocator_get_heap_size(const ma_slot_allocator_config* pConfig, size_t* pHeapSizeInBytes) { ma_result result; ma_slot_allocator_heap_layout layout; if (pHeapSizeInBytes == NULL) { return MA_INVALID_ARGS; } *pHeapSizeInBytes = 0; result = ma_slot_allocator_get_heap_layout(pConfig, &layout); if (result != MA_SUCCESS) { return result; } *pHeapSizeInBytes = layout.sizeInBytes; return result; } MA_API ma_result ma_slot_allocator_init_preallocated(const ma_slot_allocator_config* pConfig, void* pHeap, ma_slot_allocator* pAllocator) { ma_result result; ma_slot_allocator_heap_layout heapLayout; if (pAllocator == NULL) { return MA_INVALID_ARGS; } MA_ZERO_OBJECT(pAllocator); if (pHeap == NULL) { return MA_INVALID_ARGS; } result = ma_slot_allocator_get_heap_layout(pConfig, &heapLayout); if (result != MA_SUCCESS) { return result; } pAllocator->_pHeap = pHeap; MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes); pAllocator->pGroups = (ma_slot_allocator_group*)ma_offset_ptr(pHeap, heapLayout.groupsOffset); pAllocator->pSlots = (ma_uint32*)ma_offset_ptr(pHeap, heapLayout.slotsOffset); pAllocator->capacity = pConfig->capacity; return MA_SUCCESS; } MA_API ma_result ma_slot_allocator_init(const ma_slot_allocator_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_slot_allocator* pAllocator) { ma_result result; size_t heapSizeInBytes; void* pHeap; result = ma_slot_allocator_get_heap_size(pConfig, &heapSizeInBytes); if (result != MA_SUCCESS) { return result; /* Failed to retrieve the size of the heap allocation. */ } if (heapSizeInBytes > 0) { pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); if (pHeap == NULL) { return MA_OUT_OF_MEMORY; } } else { pHeap = NULL; } result = ma_slot_allocator_init_preallocated(pConfig, pHeap, pAllocator); if (result != MA_SUCCESS) { ma_free(pHeap, pAllocationCallbacks); return result; } pAllocator->_ownsHeap = MA_TRUE; return MA_SUCCESS; } MA_API void ma_slot_allocator_uninit(ma_slot_allocator* pAllocator, const ma_allocation_callbacks* pAllocationCallbacks) { if (pAllocator == NULL) { return; } if (pAllocator->_ownsHeap) { ma_free(pAllocator->_pHeap, pAllocationCallbacks); } } MA_API ma_result ma_slot_allocator_alloc(ma_slot_allocator* pAllocator, ma_uint64* pSlot) { ma_uint32 iAttempt; const ma_uint32 maxAttempts = 2; /* The number of iterations to perform until returning MA_OUT_OF_MEMORY if no slots can be found. */ if (pAllocator == NULL || pSlot == NULL) { return MA_INVALID_ARGS; } for (iAttempt = 0; iAttempt < maxAttempts; iAttempt += 1) { /* We need to acquire a suitable bitfield first. This is a bitfield that's got an available slot within it. */ ma_uint32 iGroup; for (iGroup = 0; iGroup < ma_slot_allocator_group_capacity(pAllocator); iGroup += 1) { /* CAS */ for (;;) { ma_uint32 oldBitfield; ma_uint32 newBitfield; ma_uint32 bitOffset; oldBitfield = c89atomic_load_32(&pAllocator->pGroups[iGroup].bitfield); /* <-- This copy must happen. The compiler must not optimize this away. */ /* Fast check to see if anything is available. */ if (oldBitfield == 0xFFFFFFFF) { break; /* No available bits in this bitfield. */ } bitOffset = ma_ffs_32(~oldBitfield); MA_ASSERT(bitOffset < 32); newBitfield = oldBitfield | (1 << bitOffset); if (c89atomic_compare_and_swap_32(&pAllocator->pGroups[iGroup].bitfield, oldBitfield, newBitfield) == oldBitfield) { ma_uint32 slotIndex; /* Increment the counter as soon as possible to have other threads report out-of-memory sooner than later. */ c89atomic_fetch_add_32(&pAllocator->count, 1); /* The slot index is required for constructing the output value. */ slotIndex = (iGroup << 5) + bitOffset; /* iGroup << 5 = iGroup * 32 */ if (slotIndex >= pAllocator->capacity) { return MA_OUT_OF_MEMORY; } /* Increment the reference count before constructing the output value. */ pAllocator->pSlots[slotIndex] += 1; /* Construct the output value. */ *pSlot = (((ma_uint64)pAllocator->pSlots[slotIndex] << 32) | slotIndex); return MA_SUCCESS; } } } /* We weren't able to find a slot. If it's because we've reached our capacity we need to return MA_OUT_OF_MEMORY. Otherwise we need to do another iteration and try again. */ if (pAllocator->count < pAllocator->capacity) { ma_yield(); } else { return MA_OUT_OF_MEMORY; } } /* We couldn't find a slot within the maximum number of attempts. */ return MA_OUT_OF_MEMORY; } MA_API ma_result ma_slot_allocator_free(ma_slot_allocator* pAllocator, ma_uint64 slot) { ma_uint32 iGroup; ma_uint32 iBit; if (pAllocator == NULL) { return MA_INVALID_ARGS; } iGroup = (ma_uint32)((slot & 0xFFFFFFFF) >> 5); /* slot / 32 */ iBit = (ma_uint32)((slot & 0xFFFFFFFF) & 31); /* slot % 32 */ if (iGroup >= ma_slot_allocator_group_capacity(pAllocator)) { return MA_INVALID_ARGS; } MA_ASSERT(iBit < 32); /* This must be true due to the logic we used to actually calculate it. */ while (c89atomic_load_32(&pAllocator->count) > 0) { /* CAS */ ma_uint32 oldBitfield; ma_uint32 newBitfield; oldBitfield = c89atomic_load_32(&pAllocator->pGroups[iGroup].bitfield); /* <-- This copy must happen. The compiler must not optimize this away. */ newBitfield = oldBitfield & ~(1 << iBit); /* Debugging for checking for double-frees. */ #if defined(MA_DEBUG_OUTPUT) { if ((oldBitfield & (1 << iBit)) == 0) { MA_ASSERT(MA_FALSE); /* Double free detected.*/ } } #endif if (c89atomic_compare_and_swap_32(&pAllocator->pGroups[iGroup].bitfield, oldBitfield, newBitfield) == oldBitfield) { c89atomic_fetch_sub_32(&pAllocator->count, 1); return MA_SUCCESS; } } /* Getting here means there are no allocations available for freeing. */ return MA_INVALID_OPERATION; } #define MA_JOB_ID_NONE ~((ma_uint64)0) #define MA_JOB_SLOT_NONE (ma_uint16)(~0) static MA_INLINE ma_uint32 ma_job_extract_refcount(ma_uint64 toc) { return (ma_uint32)(toc >> 32); } static MA_INLINE ma_uint16 ma_job_extract_slot(ma_uint64 toc) { return (ma_uint16)(toc & 0x0000FFFF); } static MA_INLINE ma_uint16 ma_job_extract_code(ma_uint64 toc) { return (ma_uint16)((toc & 0xFFFF0000) >> 16); } static MA_INLINE ma_uint64 ma_job_toc_to_allocation(ma_uint64 toc) { return ((ma_uint64)ma_job_extract_refcount(toc) << 32) | (ma_uint64)ma_job_extract_slot(toc); } static MA_INLINE ma_uint64 ma_job_set_refcount(ma_uint64 toc, ma_uint32 refcount) { /* Clear the reference count first. */ toc = toc & ~((ma_uint64)0xFFFFFFFF << 32); toc = toc | ((ma_uint64)refcount << 32); return toc; } MA_API ma_job ma_job_init(ma_uint16 code) { ma_job job; MA_ZERO_OBJECT(&job); job.toc.breakup.code = code; job.toc.breakup.slot = MA_JOB_SLOT_NONE; /* Temp value. Will be allocated when posted to a queue. */ job.next = MA_JOB_ID_NONE; return job; } static ma_result ma_job_process__noop(ma_job* pJob); static ma_result ma_job_process__quit(ma_job* pJob); static ma_result ma_job_process__custom(ma_job* pJob); static ma_result ma_job_process__resource_manager__load_data_buffer_node(ma_job* pJob); static ma_result ma_job_process__resource_manager__free_data_buffer_node(ma_job* pJob); static ma_result ma_job_process__resource_manager__page_data_buffer_node(ma_job* pJob); static ma_result ma_job_process__resource_manager__load_data_buffer(ma_job* pJob); static ma_result ma_job_process__resource_manager__free_data_buffer(ma_job* pJob); static ma_result ma_job_process__resource_manager__load_data_stream(ma_job* pJob); static ma_result ma_job_process__resource_manager__free_data_stream(ma_job* pJob); static ma_result ma_job_process__resource_manager__page_data_stream(ma_job* pJob); static ma_result ma_job_process__resource_manager__seek_data_stream(ma_job* pJob); #if !defined(MA_NO_DEVICE_IO) static ma_result ma_job_process__device__aaudio_reroute(ma_job* pJob); #endif static ma_job_proc g_jobVTable[MA_JOB_TYPE_COUNT] = { /* Miscellaneous. */ ma_job_process__quit, /* MA_JOB_TYPE_QUIT */ ma_job_process__custom, /* MA_JOB_TYPE_CUSTOM */ /* Resource Manager. */ ma_job_process__resource_manager__load_data_buffer_node, /* MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_BUFFER_NODE */ ma_job_process__resource_manager__free_data_buffer_node, /* MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_BUFFER_NODE */ ma_job_process__resource_manager__page_data_buffer_node, /* MA_JOB_TYPE_RESOURCE_MANAGER_PAGE_DATA_BUFFER_NODE */ ma_job_process__resource_manager__load_data_buffer, /* MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_BUFFER */ ma_job_process__resource_manager__free_data_buffer, /* MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_BUFFER */ ma_job_process__resource_manager__load_data_stream, /* MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_STREAM */ ma_job_process__resource_manager__free_data_stream, /* MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_STREAM */ ma_job_process__resource_manager__page_data_stream, /* MA_JOB_TYPE_RESOURCE_MANAGER_PAGE_DATA_STREAM */ ma_job_process__resource_manager__seek_data_stream, /* MA_JOB_TYPE_RESOURCE_MANAGER_SEEK_DATA_STREAM */ /* Device. */ #if !defined(MA_NO_DEVICE_IO) ma_job_process__device__aaudio_reroute /*MA_JOB_TYPE_DEVICE_AAUDIO_REROUTE*/ #endif }; MA_API ma_result ma_job_process(ma_job* pJob) { if (pJob == NULL) { return MA_INVALID_ARGS; } if (pJob->toc.breakup.code > MA_JOB_TYPE_COUNT) { return MA_INVALID_OPERATION; } return g_jobVTable[pJob->toc.breakup.code](pJob); } static ma_result ma_job_process__noop(ma_job* pJob) { MA_ASSERT(pJob != NULL); /* No-op. */ (void)pJob; return MA_SUCCESS; } static ma_result ma_job_process__quit(ma_job* pJob) { return ma_job_process__noop(pJob); } static ma_result ma_job_process__custom(ma_job* pJob) { MA_ASSERT(pJob != NULL); /* No-op if there's no callback. */ if (pJob->data.custom.proc == NULL) { return MA_SUCCESS; } return pJob->data.custom.proc(pJob); } MA_API ma_job_queue_config ma_job_queue_config_init(ma_uint32 flags, ma_uint32 capacity) { ma_job_queue_config config; config.flags = flags; config.capacity = capacity; return config; } typedef struct { size_t sizeInBytes; size_t allocatorOffset; size_t jobsOffset; } ma_job_queue_heap_layout; static ma_result ma_job_queue_get_heap_layout(const ma_job_queue_config* pConfig, ma_job_queue_heap_layout* pHeapLayout) { ma_result result; MA_ASSERT(pHeapLayout != NULL); MA_ZERO_OBJECT(pHeapLayout); if (pConfig == NULL) { return MA_INVALID_ARGS; } if (pConfig->capacity == 0) { return MA_INVALID_ARGS; } pHeapLayout->sizeInBytes = 0; /* Allocator. */ { ma_slot_allocator_config allocatorConfig; size_t allocatorHeapSizeInBytes; allocatorConfig = ma_slot_allocator_config_init(pConfig->capacity); result = ma_slot_allocator_get_heap_size(&allocatorConfig, &allocatorHeapSizeInBytes); if (result != MA_SUCCESS) { return result; } pHeapLayout->allocatorOffset = pHeapLayout->sizeInBytes; pHeapLayout->sizeInBytes += allocatorHeapSizeInBytes; } /* Jobs. */ pHeapLayout->jobsOffset = pHeapLayout->sizeInBytes; pHeapLayout->sizeInBytes += ma_align_64(pConfig->capacity * sizeof(ma_job)); return MA_SUCCESS; } MA_API ma_result ma_job_queue_get_heap_size(const ma_job_queue_config* pConfig, size_t* pHeapSizeInBytes) { ma_result result; ma_job_queue_heap_layout layout; if (pHeapSizeInBytes == NULL) { return MA_INVALID_ARGS; } *pHeapSizeInBytes = 0; result = ma_job_queue_get_heap_layout(pConfig, &layout); if (result != MA_SUCCESS) { return result; } *pHeapSizeInBytes = layout.sizeInBytes; return MA_SUCCESS; } MA_API ma_result ma_job_queue_init_preallocated(const ma_job_queue_config* pConfig, void* pHeap, ma_job_queue* pQueue) { ma_result result; ma_job_queue_heap_layout heapLayout; ma_slot_allocator_config allocatorConfig; if (pQueue == NULL) { return MA_INVALID_ARGS; } MA_ZERO_OBJECT(pQueue); result = ma_job_queue_get_heap_layout(pConfig, &heapLayout); if (result != MA_SUCCESS) { return result; } pQueue->_pHeap = pHeap; MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes); pQueue->flags = pConfig->flags; pQueue->capacity = pConfig->capacity; pQueue->pJobs = (ma_job*)ma_offset_ptr(pHeap, heapLayout.jobsOffset); allocatorConfig = ma_slot_allocator_config_init(pConfig->capacity); result = ma_slot_allocator_init_preallocated(&allocatorConfig, ma_offset_ptr(pHeap, heapLayout.allocatorOffset), &pQueue->allocator); if (result != MA_SUCCESS) { return result; } /* We need a semaphore if we're running in non-blocking mode. If threading is disabled we need to return an error. */ if ((pQueue->flags & MA_JOB_QUEUE_FLAG_NON_BLOCKING) == 0) { #ifndef MA_NO_THREADING { ma_semaphore_init(0, &pQueue->sem); } #else { /* Threading is disabled and we've requested non-blocking mode. */ return MA_INVALID_OPERATION; } #endif } /* Our queue needs to be initialized with a free standing node. This should always be slot 0. Required for the lock free algorithm. The first job in the queue is just a dummy item for giving us the first item in the list which is stored in the "next" member. */ ma_slot_allocator_alloc(&pQueue->allocator, &pQueue->head); /* Will never fail. */ pQueue->pJobs[ma_job_extract_slot(pQueue->head)].next = MA_JOB_ID_NONE; pQueue->tail = pQueue->head; return MA_SUCCESS; } MA_API ma_result ma_job_queue_init(const ma_job_queue_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_job_queue* pQueue) { ma_result result; size_t heapSizeInBytes; void* pHeap; result = ma_job_queue_get_heap_size(pConfig, &heapSizeInBytes); if (result != MA_SUCCESS) { return result; } if (heapSizeInBytes > 0) { pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); if (pHeap == NULL) { return MA_OUT_OF_MEMORY; } } else { pHeap = NULL; } result = ma_job_queue_init_preallocated(pConfig, pHeap, pQueue); if (result != MA_SUCCESS) { ma_free(pHeap, pAllocationCallbacks); return result; } pQueue->_ownsHeap = MA_TRUE; return MA_SUCCESS; } MA_API void ma_job_queue_uninit(ma_job_queue* pQueue, const ma_allocation_callbacks* pAllocationCallbacks) { if (pQueue == NULL) { return; } /* All we need to do is uninitialize the semaphore. */ if ((pQueue->flags & MA_JOB_QUEUE_FLAG_NON_BLOCKING) == 0) { #ifndef MA_NO_THREADING { ma_semaphore_uninit(&pQueue->sem); } #else { MA_ASSERT(MA_FALSE); /* Should never get here. Should have been checked at initialization time. */ } #endif } ma_slot_allocator_uninit(&pQueue->allocator, pAllocationCallbacks); if (pQueue->_ownsHeap) { ma_free(pQueue->_pHeap, pAllocationCallbacks); } } static ma_bool32 ma_job_queue_cas(volatile ma_uint64* dst, ma_uint64 expected, ma_uint64 desired) { /* The new counter is taken from the expected value. */ return c89atomic_compare_and_swap_64(dst, expected, ma_job_set_refcount(desired, ma_job_extract_refcount(expected) + 1)) == expected; } MA_API ma_result ma_job_queue_post(ma_job_queue* pQueue, const ma_job* pJob) { /* Lock free queue implementation based on the paper by Michael and Scott: Nonblocking Algorithms and Preemption-Safe Locking on Multiprogrammed Shared Memory Multiprocessors */ ma_result result; ma_uint64 slot; ma_uint64 tail; ma_uint64 next; if (pQueue == NULL || pJob == NULL) { return MA_INVALID_ARGS; } /* We need a new slot. */ result = ma_slot_allocator_alloc(&pQueue->allocator, &slot); if (result != MA_SUCCESS) { return result; /* Probably ran out of slots. If so, MA_OUT_OF_MEMORY will be returned. */ } /* At this point we should have a slot to place the job. */ MA_ASSERT(ma_job_extract_slot(slot) < pQueue->capacity); /* We need to put the job into memory before we do anything. */ pQueue->pJobs[ma_job_extract_slot(slot)] = *pJob; pQueue->pJobs[ma_job_extract_slot(slot)].toc.allocation = slot; /* This will overwrite the job code. */ pQueue->pJobs[ma_job_extract_slot(slot)].toc.breakup.code = pJob->toc.breakup.code; /* The job code needs to be applied again because the line above overwrote it. */ pQueue->pJobs[ma_job_extract_slot(slot)].next = MA_JOB_ID_NONE; /* Reset for safety. */ #ifndef MA_USE_EXPERIMENTAL_LOCK_FREE_JOB_QUEUE ma_spinlock_lock(&pQueue->lock); #endif { /* The job is stored in memory so now we need to add it to our linked list. We only ever add items to the end of the list. */ for (;;) { tail = c89atomic_load_64(&pQueue->tail); next = c89atomic_load_64(&pQueue->pJobs[ma_job_extract_slot(tail)].next); if (ma_job_toc_to_allocation(tail) == ma_job_toc_to_allocation(c89atomic_load_64(&pQueue->tail))) { if (ma_job_extract_slot(next) == 0xFFFF) { if (ma_job_queue_cas(&pQueue->pJobs[ma_job_extract_slot(tail)].next, next, slot)) { break; } } else { ma_job_queue_cas(&pQueue->tail, tail, ma_job_extract_slot(next)); } } } ma_job_queue_cas(&pQueue->tail, tail, slot); } #ifndef MA_USE_EXPERIMENTAL_LOCK_FREE_JOB_QUEUE ma_spinlock_unlock(&pQueue->lock); #endif /* Signal the semaphore as the last step if we're using synchronous mode. */ if ((pQueue->flags & MA_JOB_QUEUE_FLAG_NON_BLOCKING) == 0) { #ifndef MA_NO_THREADING { ma_semaphore_release(&pQueue->sem); } #else { MA_ASSERT(MA_FALSE); /* Should never get here. Should have been checked at initialization time. */ } #endif } return MA_SUCCESS; } MA_API ma_result ma_job_queue_next(ma_job_queue* pQueue, ma_job* pJob) { ma_uint64 head; ma_uint64 tail; ma_uint64 next; if (pQueue == NULL || pJob == NULL) { return MA_INVALID_ARGS; } /* If we're running in synchronous mode we'll need to wait on a semaphore. */ if ((pQueue->flags & MA_JOB_QUEUE_FLAG_NON_BLOCKING) == 0) { #ifndef MA_NO_THREADING { ma_semaphore_wait(&pQueue->sem); } #else { MA_ASSERT(MA_FALSE); /* Should never get here. Should have been checked at initialization time. */ } #endif } #ifndef MA_USE_EXPERIMENTAL_LOCK_FREE_JOB_QUEUE ma_spinlock_lock(&pQueue->lock); #endif { /* BUG: In lock-free mode, multiple threads can be in this section of code. The "head" variable in the loop below is stored. One thread can fall through to the freeing of this item while another is still using "head" for the retrieval of the "next" variable. The slot allocator might need to make use of some reference counting to ensure it's only truely freed when there are no more references to the item. This must be fixed before removing these locks. */ /* Now we need to remove the root item from the list. */ for (;;) { head = c89atomic_load_64(&pQueue->head); tail = c89atomic_load_64(&pQueue->tail); next = c89atomic_load_64(&pQueue->pJobs[ma_job_extract_slot(head)].next); if (ma_job_toc_to_allocation(head) == ma_job_toc_to_allocation(c89atomic_load_64(&pQueue->head))) { if (ma_job_extract_slot(head) == ma_job_extract_slot(tail)) { if (ma_job_extract_slot(next) == 0xFFFF) { #ifndef MA_USE_EXPERIMENTAL_LOCK_FREE_JOB_QUEUE ma_spinlock_unlock(&pQueue->lock); #endif return MA_NO_DATA_AVAILABLE; } ma_job_queue_cas(&pQueue->tail, tail, ma_job_extract_slot(next)); } else { *pJob = pQueue->pJobs[ma_job_extract_slot(next)]; if (ma_job_queue_cas(&pQueue->head, head, ma_job_extract_slot(next))) { break; } } } } } #ifndef MA_USE_EXPERIMENTAL_LOCK_FREE_JOB_QUEUE ma_spinlock_unlock(&pQueue->lock); #endif ma_slot_allocator_free(&pQueue->allocator, head); /* If it's a quit job make sure it's put back on the queue to ensure other threads have an opportunity to detect it and terminate naturally. We could instead just leave it on the queue, but that would involve fiddling with the lock-free code above and I want to keep that as simple as possible. */ if (pJob->toc.breakup.code == MA_JOB_TYPE_QUIT) { ma_job_queue_post(pQueue, pJob); return MA_CANCELLED; /* Return a cancelled status just in case the thread is checking return codes and not properly checking for a quit job. */ } return MA_SUCCESS; } /************************************************************************************************************************************************************ ************************************************************************************************************************************************************* DEVICE I/O ========== ************************************************************************************************************************************************************* ************************************************************************************************************************************************************/ #ifndef MA_NO_DEVICE_IO #ifdef MA_WIN32 #include #include #include #endif #if defined(MA_APPLE) && (__MAC_OS_X_VERSION_MIN_REQUIRED < 101200) #include /* For mach_absolute_time() */ #endif #ifdef MA_ANDROID #include #endif #ifdef MA_POSIX #include #include #include #endif /* Unfortunately using runtime linking for pthreads causes problems. This has occurred for me when testing on FreeBSD. When using runtime linking, deadlocks can occur (for me it happens when loading data from fread()). It turns out that doing compile-time linking fixes this. I'm not sure why this happens, but the safest way I can think of to fix this is to simply disable runtime linking by default. To enable runtime linking, #define this before the implementation of this file. I am not officially supporting this, but I'm leaving it here in case it's useful for somebody, somewhere. */ /*#define MA_USE_RUNTIME_LINKING_FOR_PTHREAD*/ /* Disable run-time linking on certain backends. */ #ifndef MA_NO_RUNTIME_LINKING #if defined(MA_EMSCRIPTEN) #define MA_NO_RUNTIME_LINKING #endif #endif MA_API void ma_device_info_add_native_data_format(ma_device_info* pDeviceInfo, ma_format format, ma_uint32 channels, ma_uint32 sampleRate, ma_uint32 flags) { if (pDeviceInfo == NULL) { return; } if (pDeviceInfo->nativeDataFormatCount < ma_countof(pDeviceInfo->nativeDataFormats)) { pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].format = format; pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].channels = channels; pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].sampleRate = sampleRate; pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].flags = flags; pDeviceInfo->nativeDataFormatCount += 1; } } MA_API const char* ma_get_backend_name(ma_backend backend) { switch (backend) { case ma_backend_wasapi: return "WASAPI"; case ma_backend_dsound: return "DirectSound"; case ma_backend_winmm: return "WinMM"; case ma_backend_coreaudio: return "Core Audio"; case ma_backend_sndio: return "sndio"; case ma_backend_audio4: return "audio(4)"; case ma_backend_oss: return "OSS"; case ma_backend_pulseaudio: return "PulseAudio"; case ma_backend_alsa: return "ALSA"; case ma_backend_jack: return "JACK"; case ma_backend_aaudio: return "AAudio"; case ma_backend_opensl: return "OpenSL|ES"; case ma_backend_webaudio: return "Web Audio"; case ma_backend_custom: return "Custom"; case ma_backend_null: return "Null"; default: return "Unknown"; } } MA_API ma_bool32 ma_is_backend_enabled(ma_backend backend) { /* This looks a little bit gross, but we want all backends to be included in the switch to avoid warnings on some compilers about some enums not being handled by the switch statement. */ switch (backend) { case ma_backend_wasapi: #if defined(MA_HAS_WASAPI) return MA_TRUE; #else return MA_FALSE; #endif case ma_backend_dsound: #if defined(MA_HAS_DSOUND) return MA_TRUE; #else return MA_FALSE; #endif case ma_backend_winmm: #if defined(MA_HAS_WINMM) return MA_TRUE; #else return MA_FALSE; #endif case ma_backend_coreaudio: #if defined(MA_HAS_COREAUDIO) return MA_TRUE; #else return MA_FALSE; #endif case ma_backend_sndio: #if defined(MA_HAS_SNDIO) return MA_TRUE; #else return MA_FALSE; #endif case ma_backend_audio4: #if defined(MA_HAS_AUDIO4) return MA_TRUE; #else return MA_FALSE; #endif case ma_backend_oss: #if defined(MA_HAS_OSS) return MA_TRUE; #else return MA_FALSE; #endif case ma_backend_pulseaudio: #if defined(MA_HAS_PULSEAUDIO) return MA_TRUE; #else return MA_FALSE; #endif case ma_backend_alsa: #if defined(MA_HAS_ALSA) return MA_TRUE; #else return MA_FALSE; #endif case ma_backend_jack: #if defined(MA_HAS_JACK) return MA_TRUE; #else return MA_FALSE; #endif case ma_backend_aaudio: #if defined(MA_HAS_AAUDIO) #if defined(MA_ANDROID) { char sdkVersion[PROP_VALUE_MAX + 1] = {0, }; if (__system_property_get("ro.build.version.sdk", sdkVersion)) { if (atoi(sdkVersion) >= 26) { return MA_TRUE; } else { return MA_FALSE; } } else { return MA_FALSE; } } #else return MA_FALSE; #endif #else return MA_FALSE; #endif case ma_backend_opensl: #if defined(MA_HAS_OPENSL) #if defined(MA_ANDROID) { char sdkVersion[PROP_VALUE_MAX + 1] = {0, }; if (__system_property_get("ro.build.version.sdk", sdkVersion)) { if (atoi(sdkVersion) >= 9) { return MA_TRUE; } else { return MA_FALSE; } } else { return MA_FALSE; } } #else return MA_TRUE; #endif #else return MA_FALSE; #endif case ma_backend_webaudio: #if defined(MA_HAS_WEBAUDIO) return MA_TRUE; #else return MA_FALSE; #endif case ma_backend_custom: #if defined(MA_HAS_CUSTOM) return MA_TRUE; #else return MA_FALSE; #endif case ma_backend_null: #if defined(MA_HAS_NULL) return MA_TRUE; #else return MA_FALSE; #endif default: return MA_FALSE; } } MA_API ma_result ma_get_enabled_backends(ma_backend* pBackends, size_t backendCap, size_t* pBackendCount) { size_t backendCount; size_t iBackend; ma_result result = MA_SUCCESS; if (pBackendCount == NULL) { return MA_INVALID_ARGS; } backendCount = 0; for (iBackend = 0; iBackend <= ma_backend_null; iBackend += 1) { ma_backend backend = (ma_backend)iBackend; if (ma_is_backend_enabled(backend)) { /* The backend is enabled. Try adding it to the list. If there's no room, MA_NO_SPACE needs to be returned. */ if (backendCount == backendCap) { result = MA_NO_SPACE; break; } else { pBackends[backendCount] = backend; backendCount += 1; } } } if (pBackendCount != NULL) { *pBackendCount = backendCount; } return result; } MA_API ma_bool32 ma_is_loopback_supported(ma_backend backend) { switch (backend) { case ma_backend_wasapi: return MA_TRUE; case ma_backend_dsound: return MA_FALSE; case ma_backend_winmm: return MA_FALSE; case ma_backend_coreaudio: return MA_FALSE; case ma_backend_sndio: return MA_FALSE; case ma_backend_audio4: return MA_FALSE; case ma_backend_oss: return MA_FALSE; case ma_backend_pulseaudio: return MA_FALSE; case ma_backend_alsa: return MA_FALSE; case ma_backend_jack: return MA_FALSE; case ma_backend_aaudio: return MA_FALSE; case ma_backend_opensl: return MA_FALSE; case ma_backend_webaudio: return MA_FALSE; case ma_backend_custom: return MA_FALSE; /* <-- Will depend on the implementation of the backend. */ case ma_backend_null: return MA_FALSE; default: return MA_FALSE; } } #ifdef MA_WIN32 /* WASAPI error codes. */ #define MA_AUDCLNT_E_NOT_INITIALIZED ((HRESULT)0x88890001) #define MA_AUDCLNT_E_ALREADY_INITIALIZED ((HRESULT)0x88890002) #define MA_AUDCLNT_E_WRONG_ENDPOINT_TYPE ((HRESULT)0x88890003) #define MA_AUDCLNT_E_DEVICE_INVALIDATED ((HRESULT)0x88890004) #define MA_AUDCLNT_E_NOT_STOPPED ((HRESULT)0x88890005) #define MA_AUDCLNT_E_BUFFER_TOO_LARGE ((HRESULT)0x88890006) #define MA_AUDCLNT_E_OUT_OF_ORDER ((HRESULT)0x88890007) #define MA_AUDCLNT_E_UNSUPPORTED_FORMAT ((HRESULT)0x88890008) #define MA_AUDCLNT_E_INVALID_SIZE ((HRESULT)0x88890009) #define MA_AUDCLNT_E_DEVICE_IN_USE ((HRESULT)0x8889000A) #define MA_AUDCLNT_E_BUFFER_OPERATION_PENDING ((HRESULT)0x8889000B) #define MA_AUDCLNT_E_THREAD_NOT_REGISTERED ((HRESULT)0x8889000C) #define MA_AUDCLNT_E_NO_SINGLE_PROCESS ((HRESULT)0x8889000D) #define MA_AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED ((HRESULT)0x8889000E) #define MA_AUDCLNT_E_ENDPOINT_CREATE_FAILED ((HRESULT)0x8889000F) #define MA_AUDCLNT_E_SERVICE_NOT_RUNNING ((HRESULT)0x88890010) #define MA_AUDCLNT_E_EVENTHANDLE_NOT_EXPECTED ((HRESULT)0x88890011) #define MA_AUDCLNT_E_EXCLUSIVE_MODE_ONLY ((HRESULT)0x88890012) #define MA_AUDCLNT_E_BUFDURATION_PERIOD_NOT_EQUAL ((HRESULT)0x88890013) #define MA_AUDCLNT_E_EVENTHANDLE_NOT_SET ((HRESULT)0x88890014) #define MA_AUDCLNT_E_INCORRECT_BUFFER_SIZE ((HRESULT)0x88890015) #define MA_AUDCLNT_E_BUFFER_SIZE_ERROR ((HRESULT)0x88890016) #define MA_AUDCLNT_E_CPUUSAGE_EXCEEDED ((HRESULT)0x88890017) #define MA_AUDCLNT_E_BUFFER_ERROR ((HRESULT)0x88890018) #define MA_AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED ((HRESULT)0x88890019) #define MA_AUDCLNT_E_INVALID_DEVICE_PERIOD ((HRESULT)0x88890020) #define MA_AUDCLNT_E_INVALID_STREAM_FLAG ((HRESULT)0x88890021) #define MA_AUDCLNT_E_ENDPOINT_OFFLOAD_NOT_CAPABLE ((HRESULT)0x88890022) #define MA_AUDCLNT_E_OUT_OF_OFFLOAD_RESOURCES ((HRESULT)0x88890023) #define MA_AUDCLNT_E_OFFLOAD_MODE_ONLY ((HRESULT)0x88890024) #define MA_AUDCLNT_E_NONOFFLOAD_MODE_ONLY ((HRESULT)0x88890025) #define MA_AUDCLNT_E_RESOURCES_INVALIDATED ((HRESULT)0x88890026) #define MA_AUDCLNT_E_RAW_MODE_UNSUPPORTED ((HRESULT)0x88890027) #define MA_AUDCLNT_E_ENGINE_PERIODICITY_LOCKED ((HRESULT)0x88890028) #define MA_AUDCLNT_E_ENGINE_FORMAT_LOCKED ((HRESULT)0x88890029) #define MA_AUDCLNT_E_HEADTRACKING_ENABLED ((HRESULT)0x88890030) #define MA_AUDCLNT_E_HEADTRACKING_UNSUPPORTED ((HRESULT)0x88890040) #define MA_AUDCLNT_S_BUFFER_EMPTY ((HRESULT)0x08890001) #define MA_AUDCLNT_S_THREAD_ALREADY_REGISTERED ((HRESULT)0x08890002) #define MA_AUDCLNT_S_POSITION_STALLED ((HRESULT)0x08890003) #define MA_DS_OK ((HRESULT)0) #define MA_DS_NO_VIRTUALIZATION ((HRESULT)0x0878000A) #define MA_DSERR_ALLOCATED ((HRESULT)0x8878000A) #define MA_DSERR_CONTROLUNAVAIL ((HRESULT)0x8878001E) #define MA_DSERR_INVALIDPARAM ((HRESULT)0x80070057) /*E_INVALIDARG*/ #define MA_DSERR_INVALIDCALL ((HRESULT)0x88780032) #define MA_DSERR_GENERIC ((HRESULT)0x80004005) /*E_FAIL*/ #define MA_DSERR_PRIOLEVELNEEDED ((HRESULT)0x88780046) #define MA_DSERR_OUTOFMEMORY ((HRESULT)0x8007000E) /*E_OUTOFMEMORY*/ #define MA_DSERR_BADFORMAT ((HRESULT)0x88780064) #define MA_DSERR_UNSUPPORTED ((HRESULT)0x80004001) /*E_NOTIMPL*/ #define MA_DSERR_NODRIVER ((HRESULT)0x88780078) #define MA_DSERR_ALREADYINITIALIZED ((HRESULT)0x88780082) #define MA_DSERR_NOAGGREGATION ((HRESULT)0x80040110) /*CLASS_E_NOAGGREGATION*/ #define MA_DSERR_BUFFERLOST ((HRESULT)0x88780096) #define MA_DSERR_OTHERAPPHASPRIO ((HRESULT)0x887800A0) #define MA_DSERR_UNINITIALIZED ((HRESULT)0x887800AA) #define MA_DSERR_NOINTERFACE ((HRESULT)0x80004002) /*E_NOINTERFACE*/ #define MA_DSERR_ACCESSDENIED ((HRESULT)0x80070005) /*E_ACCESSDENIED*/ #define MA_DSERR_BUFFERTOOSMALL ((HRESULT)0x887800B4) #define MA_DSERR_DS8_REQUIRED ((HRESULT)0x887800BE) #define MA_DSERR_SENDLOOP ((HRESULT)0x887800C8) #define MA_DSERR_BADSENDBUFFERGUID ((HRESULT)0x887800D2) #define MA_DSERR_OBJECTNOTFOUND ((HRESULT)0x88781161) #define MA_DSERR_FXUNAVAILABLE ((HRESULT)0x887800DC) static ma_result ma_result_from_HRESULT(HRESULT hr) { switch (hr) { case NOERROR: return MA_SUCCESS; /*case S_OK: return MA_SUCCESS;*/ case E_POINTER: return MA_INVALID_ARGS; case E_UNEXPECTED: return MA_ERROR; case E_NOTIMPL: return MA_NOT_IMPLEMENTED; case E_OUTOFMEMORY: return MA_OUT_OF_MEMORY; case E_INVALIDARG: return MA_INVALID_ARGS; case E_NOINTERFACE: return MA_API_NOT_FOUND; case E_HANDLE: return MA_INVALID_ARGS; case E_ABORT: return MA_ERROR; case E_FAIL: return MA_ERROR; case E_ACCESSDENIED: return MA_ACCESS_DENIED; /* WASAPI */ case MA_AUDCLNT_E_NOT_INITIALIZED: return MA_DEVICE_NOT_INITIALIZED; case MA_AUDCLNT_E_ALREADY_INITIALIZED: return MA_DEVICE_ALREADY_INITIALIZED; case MA_AUDCLNT_E_WRONG_ENDPOINT_TYPE: return MA_INVALID_ARGS; case MA_AUDCLNT_E_DEVICE_INVALIDATED: return MA_UNAVAILABLE; case MA_AUDCLNT_E_NOT_STOPPED: return MA_DEVICE_NOT_STOPPED; case MA_AUDCLNT_E_BUFFER_TOO_LARGE: return MA_TOO_BIG; case MA_AUDCLNT_E_OUT_OF_ORDER: return MA_INVALID_OPERATION; case MA_AUDCLNT_E_UNSUPPORTED_FORMAT: return MA_FORMAT_NOT_SUPPORTED; case MA_AUDCLNT_E_INVALID_SIZE: return MA_INVALID_ARGS; case MA_AUDCLNT_E_DEVICE_IN_USE: return MA_BUSY; case MA_AUDCLNT_E_BUFFER_OPERATION_PENDING: return MA_INVALID_OPERATION; case MA_AUDCLNT_E_THREAD_NOT_REGISTERED: return MA_DOES_NOT_EXIST; case MA_AUDCLNT_E_NO_SINGLE_PROCESS: return MA_INVALID_OPERATION; case MA_AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED: return MA_SHARE_MODE_NOT_SUPPORTED; case MA_AUDCLNT_E_ENDPOINT_CREATE_FAILED: return MA_FAILED_TO_OPEN_BACKEND_DEVICE; case MA_AUDCLNT_E_SERVICE_NOT_RUNNING: return MA_NOT_CONNECTED; case MA_AUDCLNT_E_EVENTHANDLE_NOT_EXPECTED: return MA_INVALID_ARGS; case MA_AUDCLNT_E_EXCLUSIVE_MODE_ONLY: return MA_SHARE_MODE_NOT_SUPPORTED; case MA_AUDCLNT_E_BUFDURATION_PERIOD_NOT_EQUAL: return MA_INVALID_ARGS; case MA_AUDCLNT_E_EVENTHANDLE_NOT_SET: return MA_INVALID_ARGS; case MA_AUDCLNT_E_INCORRECT_BUFFER_SIZE: return MA_INVALID_ARGS; case MA_AUDCLNT_E_BUFFER_SIZE_ERROR: return MA_INVALID_ARGS; case MA_AUDCLNT_E_CPUUSAGE_EXCEEDED: return MA_ERROR; case MA_AUDCLNT_E_BUFFER_ERROR: return MA_ERROR; case MA_AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED: return MA_INVALID_ARGS; case MA_AUDCLNT_E_INVALID_DEVICE_PERIOD: return MA_INVALID_ARGS; case MA_AUDCLNT_E_INVALID_STREAM_FLAG: return MA_INVALID_ARGS; case MA_AUDCLNT_E_ENDPOINT_OFFLOAD_NOT_CAPABLE: return MA_INVALID_OPERATION; case MA_AUDCLNT_E_OUT_OF_OFFLOAD_RESOURCES: return MA_OUT_OF_MEMORY; case MA_AUDCLNT_E_OFFLOAD_MODE_ONLY: return MA_INVALID_OPERATION; case MA_AUDCLNT_E_NONOFFLOAD_MODE_ONLY: return MA_INVALID_OPERATION; case MA_AUDCLNT_E_RESOURCES_INVALIDATED: return MA_INVALID_DATA; case MA_AUDCLNT_E_RAW_MODE_UNSUPPORTED: return MA_INVALID_OPERATION; case MA_AUDCLNT_E_ENGINE_PERIODICITY_LOCKED: return MA_INVALID_OPERATION; case MA_AUDCLNT_E_ENGINE_FORMAT_LOCKED: return MA_INVALID_OPERATION; case MA_AUDCLNT_E_HEADTRACKING_ENABLED: return MA_INVALID_OPERATION; case MA_AUDCLNT_E_HEADTRACKING_UNSUPPORTED: return MA_INVALID_OPERATION; case MA_AUDCLNT_S_BUFFER_EMPTY: return MA_NO_SPACE; case MA_AUDCLNT_S_THREAD_ALREADY_REGISTERED: return MA_ALREADY_EXISTS; case MA_AUDCLNT_S_POSITION_STALLED: return MA_ERROR; /* DirectSound */ /*case MA_DS_OK: return MA_SUCCESS;*/ /* S_OK */ case MA_DS_NO_VIRTUALIZATION: return MA_SUCCESS; case MA_DSERR_ALLOCATED: return MA_ALREADY_IN_USE; case MA_DSERR_CONTROLUNAVAIL: return MA_INVALID_OPERATION; /*case MA_DSERR_INVALIDPARAM: return MA_INVALID_ARGS;*/ /* E_INVALIDARG */ case MA_DSERR_INVALIDCALL: return MA_INVALID_OPERATION; /*case MA_DSERR_GENERIC: return MA_ERROR;*/ /* E_FAIL */ case MA_DSERR_PRIOLEVELNEEDED: return MA_INVALID_OPERATION; /*case MA_DSERR_OUTOFMEMORY: return MA_OUT_OF_MEMORY;*/ /* E_OUTOFMEMORY */ case MA_DSERR_BADFORMAT: return MA_FORMAT_NOT_SUPPORTED; /*case MA_DSERR_UNSUPPORTED: return MA_NOT_IMPLEMENTED;*/ /* E_NOTIMPL */ case MA_DSERR_NODRIVER: return MA_FAILED_TO_INIT_BACKEND; case MA_DSERR_ALREADYINITIALIZED: return MA_DEVICE_ALREADY_INITIALIZED; case MA_DSERR_NOAGGREGATION: return MA_ERROR; case MA_DSERR_BUFFERLOST: return MA_UNAVAILABLE; case MA_DSERR_OTHERAPPHASPRIO: return MA_ACCESS_DENIED; case MA_DSERR_UNINITIALIZED: return MA_DEVICE_NOT_INITIALIZED; /*case MA_DSERR_NOINTERFACE: return MA_API_NOT_FOUND;*/ /* E_NOINTERFACE */ /*case MA_DSERR_ACCESSDENIED: return MA_ACCESS_DENIED;*/ /* E_ACCESSDENIED */ case MA_DSERR_BUFFERTOOSMALL: return MA_NO_SPACE; case MA_DSERR_DS8_REQUIRED: return MA_INVALID_OPERATION; case MA_DSERR_SENDLOOP: return MA_DEADLOCK; case MA_DSERR_BADSENDBUFFERGUID: return MA_INVALID_ARGS; case MA_DSERR_OBJECTNOTFOUND: return MA_NO_DEVICE; case MA_DSERR_FXUNAVAILABLE: return MA_UNAVAILABLE; default: return MA_ERROR; } } typedef HRESULT (WINAPI * MA_PFN_CoInitializeEx)(LPVOID pvReserved, DWORD dwCoInit); typedef void (WINAPI * MA_PFN_CoUninitialize)(void); typedef HRESULT (WINAPI * MA_PFN_CoCreateInstance)(REFCLSID rclsid, LPUNKNOWN pUnkOuter, DWORD dwClsContext, REFIID riid, LPVOID *ppv); typedef void (WINAPI * MA_PFN_CoTaskMemFree)(LPVOID pv); typedef HRESULT (WINAPI * MA_PFN_PropVariantClear)(PROPVARIANT *pvar); typedef int (WINAPI * MA_PFN_StringFromGUID2)(const GUID* const rguid, LPOLESTR lpsz, int cchMax); typedef HWND (WINAPI * MA_PFN_GetForegroundWindow)(void); typedef HWND (WINAPI * MA_PFN_GetDesktopWindow)(void); #if defined(MA_WIN32_DESKTOP) /* Microsoft documents these APIs as returning LSTATUS, but the Win32 API shipping with some compilers do not define it. It's just a LONG. */ typedef LONG (WINAPI * MA_PFN_RegOpenKeyExA)(HKEY hKey, LPCSTR lpSubKey, DWORD ulOptions, REGSAM samDesired, PHKEY phkResult); typedef LONG (WINAPI * MA_PFN_RegCloseKey)(HKEY hKey); typedef LONG (WINAPI * MA_PFN_RegQueryValueExA)(HKEY hKey, LPCSTR lpValueName, LPDWORD lpReserved, LPDWORD lpType, LPBYTE lpData, LPDWORD lpcbData); #endif /* MA_WIN32_DESKTOP */ #endif /* MA_WIN32 */ #define MA_DEFAULT_PLAYBACK_DEVICE_NAME "Default Playback Device" #define MA_DEFAULT_CAPTURE_DEVICE_NAME "Default Capture Device" /******************************************************************************* Timing *******************************************************************************/ #ifdef MA_WIN32 static LARGE_INTEGER g_ma_TimerFrequency; /* <-- Initialized to zero since it's static. */ void ma_timer_init(ma_timer* pTimer) { LARGE_INTEGER counter; if (g_ma_TimerFrequency.QuadPart == 0) { QueryPerformanceFrequency(&g_ma_TimerFrequency); } QueryPerformanceCounter(&counter); pTimer->counter = counter.QuadPart; } double ma_timer_get_time_in_seconds(ma_timer* pTimer) { LARGE_INTEGER counter; if (!QueryPerformanceCounter(&counter)) { return 0; } return (double)(counter.QuadPart - pTimer->counter) / g_ma_TimerFrequency.QuadPart; } #elif defined(MA_APPLE) && (__MAC_OS_X_VERSION_MIN_REQUIRED < 101200) static ma_uint64 g_ma_TimerFrequency = 0; static void ma_timer_init(ma_timer* pTimer) { mach_timebase_info_data_t baseTime; mach_timebase_info(&baseTime); g_ma_TimerFrequency = (baseTime.denom * 1e9) / baseTime.numer; pTimer->counter = mach_absolute_time(); } static double ma_timer_get_time_in_seconds(ma_timer* pTimer) { ma_uint64 newTimeCounter = mach_absolute_time(); ma_uint64 oldTimeCounter = pTimer->counter; return (newTimeCounter - oldTimeCounter) / g_ma_TimerFrequency; } #elif defined(MA_EMSCRIPTEN) static MA_INLINE void ma_timer_init(ma_timer* pTimer) { pTimer->counterD = emscripten_get_now(); } static MA_INLINE double ma_timer_get_time_in_seconds(ma_timer* pTimer) { return (emscripten_get_now() - pTimer->counterD) / 1000; /* Emscripten is in milliseconds. */ } #else #if defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 199309L #if defined(CLOCK_MONOTONIC) #define MA_CLOCK_ID CLOCK_MONOTONIC #else #define MA_CLOCK_ID CLOCK_REALTIME #endif static void ma_timer_init(ma_timer* pTimer) { struct timespec newTime; clock_gettime(MA_CLOCK_ID, &newTime); pTimer->counter = (newTime.tv_sec * 1000000000) + newTime.tv_nsec; } static double ma_timer_get_time_in_seconds(ma_timer* pTimer) { ma_uint64 newTimeCounter; ma_uint64 oldTimeCounter; struct timespec newTime; clock_gettime(MA_CLOCK_ID, &newTime); newTimeCounter = (newTime.tv_sec * 1000000000) + newTime.tv_nsec; oldTimeCounter = pTimer->counter; return (newTimeCounter - oldTimeCounter) / 1000000000.0; } #else static void ma_timer_init(ma_timer* pTimer) { struct timeval newTime; gettimeofday(&newTime, NULL); pTimer->counter = (newTime.tv_sec * 1000000) + newTime.tv_usec; } static double ma_timer_get_time_in_seconds(ma_timer* pTimer) { ma_uint64 newTimeCounter; ma_uint64 oldTimeCounter; struct timeval newTime; gettimeofday(&newTime, NULL); newTimeCounter = (newTime.tv_sec * 1000000) + newTime.tv_usec; oldTimeCounter = pTimer->counter; return (newTimeCounter - oldTimeCounter) / 1000000.0; } #endif #endif /******************************************************************************* Dynamic Linking *******************************************************************************/ MA_API ma_handle ma_dlopen(ma_context* pContext, const char* filename) { ma_handle handle; ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "Loading library: %s\n", filename); #ifdef _WIN32 /* From MSDN: Desktop applications cannot use LoadPackagedLibrary; if a desktop application calls this function it fails with APPMODEL_ERROR_NO_PACKAGE.*/ #if !defined(WINAPI_FAMILY) || (defined(WINAPI_FAMILY) && (defined(WINAPI_FAMILY_DESKTOP_APP) && WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP)) handle = (ma_handle)LoadLibraryA(filename); #else /* *sigh* It appears there is no ANSI version of LoadPackagedLibrary()... */ WCHAR filenameW[4096]; if (MultiByteToWideChar(CP_UTF8, 0, filename, -1, filenameW, sizeof(filenameW)) == 0) { handle = NULL; } else { handle = (ma_handle)LoadPackagedLibrary(filenameW, 0); } #endif #else handle = (ma_handle)dlopen(filename, RTLD_NOW); #endif /* I'm not considering failure to load a library an error nor a warning because seamlessly falling through to a lower-priority backend is a deliberate design choice. Instead I'm logging it as an informational message. */ if (handle == NULL) { ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_INFO, "Failed to load library: %s\n", filename); } (void)pContext; /* It's possible for pContext to be unused. */ return handle; } MA_API void ma_dlclose(ma_context* pContext, ma_handle handle) { #ifdef _WIN32 FreeLibrary((HMODULE)handle); #else dlclose((void*)handle); #endif (void)pContext; } MA_API ma_proc ma_dlsym(ma_context* pContext, ma_handle handle, const char* symbol) { ma_proc proc; ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "Loading symbol: %s\n", symbol); #ifdef _WIN32 proc = (ma_proc)GetProcAddress((HMODULE)handle, symbol); #else #if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wpedantic" #endif proc = (ma_proc)dlsym((void*)handle, symbol); #if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)) #pragma GCC diagnostic pop #endif #endif if (proc == NULL) { ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_WARNING, "Failed to load symbol: %s\n", symbol); } (void)pContext; /* It's possible for pContext to be unused. */ return proc; } #if 0 static ma_uint32 ma_get_closest_standard_sample_rate(ma_uint32 sampleRateIn) { ma_uint32 closestRate = 0; ma_uint32 closestDiff = 0xFFFFFFFF; size_t iStandardRate; for (iStandardRate = 0; iStandardRate < ma_countof(g_maStandardSampleRatePriorities); ++iStandardRate) { ma_uint32 standardRate = g_maStandardSampleRatePriorities[iStandardRate]; ma_uint32 diff; if (sampleRateIn > standardRate) { diff = sampleRateIn - standardRate; } else { diff = standardRate - sampleRateIn; } if (diff == 0) { return standardRate; /* The input sample rate is a standard rate. */ } if (closestDiff > diff) { closestDiff = diff; closestRate = standardRate; } } return closestRate; } #endif static MA_INLINE unsigned int ma_device_disable_denormals(ma_device* pDevice) { MA_ASSERT(pDevice != NULL); if (!pDevice->noDisableDenormals) { return ma_disable_denormals(); } else { return 0; } } static MA_INLINE void ma_device_restore_denormals(ma_device* pDevice, unsigned int prevState) { MA_ASSERT(pDevice != NULL); if (!pDevice->noDisableDenormals) { ma_restore_denormals(prevState); } else { /* Do nothing. */ (void)prevState; } } static ma_device_notification ma_device_notification_init(ma_device* pDevice, ma_device_notification_type type) { ma_device_notification notification; MA_ZERO_OBJECT(¬ification); notification.pDevice = pDevice; notification.type = type; return notification; } static void ma_device__on_notification(ma_device_notification notification) { MA_ASSERT(notification.pDevice != NULL); if (notification.pDevice->onNotification != NULL) { notification.pDevice->onNotification(¬ification); } /* TEMP FOR COMPATIBILITY: If it's a stopped notification, fire the onStop callback as well. This is only for backwards compatibility and will be removed. */ if (notification.pDevice->onStop != NULL && notification.type == ma_device_notification_type_stopped) { notification.pDevice->onStop(notification.pDevice); } } void ma_device__on_notification_started(ma_device* pDevice) { ma_device__on_notification(ma_device_notification_init(pDevice, ma_device_notification_type_started)); } void ma_device__on_notification_stopped(ma_device* pDevice) { ma_device__on_notification(ma_device_notification_init(pDevice, ma_device_notification_type_stopped)); } void ma_device__on_notification_rerouted(ma_device* pDevice) { ma_device__on_notification(ma_device_notification_init(pDevice, ma_device_notification_type_rerouted)); } void ma_device__on_notification_interruption_began(ma_device* pDevice) { ma_device__on_notification(ma_device_notification_init(pDevice, ma_device_notification_type_interruption_began)); } void ma_device__on_notification_interruption_ended(ma_device* pDevice) { ma_device__on_notification(ma_device_notification_init(pDevice, ma_device_notification_type_interruption_ended)); } static void ma_device__on_data_inner(ma_device* pDevice, void* pFramesOut, const void* pFramesIn, ma_uint32 frameCount) { MA_ASSERT(pDevice != NULL); MA_ASSERT(pDevice->onData != NULL); if (!pDevice->noPreSilencedOutputBuffer && pFramesOut != NULL) { ma_silence_pcm_frames(pFramesOut, frameCount, pDevice->playback.format, pDevice->playback.channels); } pDevice->onData(pDevice, pFramesOut, pFramesIn, frameCount); } static void ma_device__on_data(ma_device* pDevice, void* pFramesOut, const void* pFramesIn, ma_uint32 frameCount) { MA_ASSERT(pDevice != NULL); /* Don't read more data from the client if we're in the process of stopping. */ if (ma_device_get_state(pDevice) == ma_device_state_stopping) { return; } if (pDevice->noFixedSizedCallback) { /* Fast path. Not using a fixed sized callback. Process directly from the specified buffers. */ ma_device__on_data_inner(pDevice, pFramesOut, pFramesIn, frameCount); } else { /* Slow path. Using a fixed sized callback. Need to use the intermediary buffer. */ ma_uint32 totalFramesProcessed = 0; while (totalFramesProcessed < frameCount) { ma_uint32 totalFramesRemaining = frameCount - totalFramesProcessed; ma_uint32 framesToProcessThisIteration = 0; if (pFramesIn != NULL) { /* Capturing. Write to the intermediary buffer. If there's no room, fire the callback to empty it. */ if (pDevice->capture.intermediaryBufferLen < pDevice->capture.intermediaryBufferCap) { /* There's some room left in the intermediary buffer. Write to it without firing the callback. */ framesToProcessThisIteration = totalFramesRemaining; if (framesToProcessThisIteration > pDevice->capture.intermediaryBufferCap - pDevice->capture.intermediaryBufferLen) { framesToProcessThisIteration = pDevice->capture.intermediaryBufferCap - pDevice->capture.intermediaryBufferLen; } ma_copy_pcm_frames( ma_offset_pcm_frames_ptr(pDevice->capture.pIntermediaryBuffer, pDevice->capture.intermediaryBufferLen, pDevice->capture.format, pDevice->capture.channels), ma_offset_pcm_frames_const_ptr(pFramesIn, totalFramesProcessed, pDevice->capture.format, pDevice->capture.channels), framesToProcessThisIteration, pDevice->capture.format, pDevice->capture.channels); pDevice->capture.intermediaryBufferLen += framesToProcessThisIteration; } if (pDevice->capture.intermediaryBufferLen == pDevice->capture.intermediaryBufferCap) { /* No room left in the intermediary buffer. Fire the data callback. */ if (pDevice->type == ma_device_type_duplex) { /* We'll do the duplex data callback later after we've processed the playback data. */ } else { ma_device__on_data_inner(pDevice, NULL, pDevice->capture.pIntermediaryBuffer, pDevice->capture.intermediaryBufferCap); /* The intermediary buffer has just been drained. */ pDevice->capture.intermediaryBufferLen = 0; } } } if (pFramesOut != NULL) { /* Playing back. Read from the intermediary buffer. If there's nothing in it, fire the callback to fill it. */ if (pDevice->playback.intermediaryBufferLen > 0) { /* There's some content in the intermediary buffer. Read from that without firing the callback. */ if (pDevice->type == ma_device_type_duplex) { /* The frames processed this iteration for a duplex device will always be based on the capture side. Leave it unmodified. */ } else { framesToProcessThisIteration = totalFramesRemaining; if (framesToProcessThisIteration > pDevice->playback.intermediaryBufferLen) { framesToProcessThisIteration = pDevice->playback.intermediaryBufferLen; } } ma_copy_pcm_frames( ma_offset_pcm_frames_ptr(pFramesOut, totalFramesProcessed, pDevice->playback.format, pDevice->playback.channels), ma_offset_pcm_frames_ptr(pDevice->playback.pIntermediaryBuffer, pDevice->playback.intermediaryBufferCap - pDevice->playback.intermediaryBufferLen, pDevice->playback.format, pDevice->playback.channels), framesToProcessThisIteration, pDevice->playback.format, pDevice->playback.channels); pDevice->playback.intermediaryBufferLen -= framesToProcessThisIteration; } if (pDevice->playback.intermediaryBufferLen == 0) { /* There's nothing in the intermediary buffer. Fire the data callback to fill it. */ if (pDevice->type == ma_device_type_duplex) { /* In duplex mode, the data callback will be fired later. Nothing to do here. */ } else { ma_device__on_data_inner(pDevice, pDevice->playback.pIntermediaryBuffer, NULL, pDevice->playback.intermediaryBufferCap); /* The intermediary buffer has just been filled. */ pDevice->playback.intermediaryBufferLen = pDevice->playback.intermediaryBufferCap; } } } /* If we're in duplex mode we might need to do a refill of the data. */ if (pDevice->type == ma_device_type_duplex) { if (pDevice->capture.intermediaryBufferLen == pDevice->capture.intermediaryBufferCap) { ma_device__on_data_inner(pDevice, pDevice->playback.pIntermediaryBuffer, pDevice->capture.pIntermediaryBuffer, pDevice->capture.intermediaryBufferCap); pDevice->playback.intermediaryBufferLen = pDevice->playback.intermediaryBufferCap; /* The playback buffer will have just been filled. */ pDevice->capture.intermediaryBufferLen = 0; /* The intermediary buffer has just been drained. */ } } /* Make sure this is only incremented once in the duplex case. */ totalFramesProcessed += framesToProcessThisIteration; } } } static void ma_device__handle_data_callback(ma_device* pDevice, void* pFramesOut, const void* pFramesIn, ma_uint32 frameCount) { float masterVolumeFactor; ma_device_get_master_volume(pDevice, &masterVolumeFactor); /* Use ma_device_get_master_volume() to ensure the volume is loaded atomically. */ if (pDevice->onData) { unsigned int prevDenormalState = ma_device_disable_denormals(pDevice); { /* Volume control of input makes things a bit awkward because the input buffer is read-only. We'll need to use a temp buffer and loop in this case. */ if (pFramesIn != NULL && masterVolumeFactor < 1) { ma_uint8 tempFramesIn[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; ma_uint32 bpfCapture = ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels); ma_uint32 bpfPlayback = ma_get_bytes_per_frame(pDevice->playback.format, pDevice->playback.channels); ma_uint32 totalFramesProcessed = 0; while (totalFramesProcessed < frameCount) { ma_uint32 framesToProcessThisIteration = frameCount - totalFramesProcessed; if (framesToProcessThisIteration > sizeof(tempFramesIn)/bpfCapture) { framesToProcessThisIteration = sizeof(tempFramesIn)/bpfCapture; } ma_copy_and_apply_volume_factor_pcm_frames(tempFramesIn, ma_offset_ptr(pFramesIn, totalFramesProcessed*bpfCapture), framesToProcessThisIteration, pDevice->capture.format, pDevice->capture.channels, masterVolumeFactor); ma_device__on_data(pDevice, ma_offset_ptr(pFramesOut, totalFramesProcessed*bpfPlayback), tempFramesIn, framesToProcessThisIteration); totalFramesProcessed += framesToProcessThisIteration; } } else { ma_device__on_data(pDevice, pFramesOut, pFramesIn, frameCount); } /* Volume control and clipping for playback devices. */ if (pFramesOut != NULL) { if (masterVolumeFactor < 1) { if (pFramesIn == NULL) { /* <-- In full-duplex situations, the volume will have been applied to the input samples before the data callback. Applying it again post-callback will incorrectly compound it. */ ma_apply_volume_factor_pcm_frames(pFramesOut, frameCount, pDevice->playback.format, pDevice->playback.channels, masterVolumeFactor); } } if (!pDevice->noClip && pDevice->playback.format == ma_format_f32) { ma_clip_samples_f32((float*)pFramesOut, (const float*)pFramesOut, frameCount * pDevice->playback.channels); /* Intentionally specifying the same pointer for both input and output for in-place processing. */ } } } ma_device_restore_denormals(pDevice, prevDenormalState); } } /* A helper function for reading sample data from the client. */ static void ma_device__read_frames_from_client(ma_device* pDevice, ma_uint32 frameCount, void* pFramesOut) { MA_ASSERT(pDevice != NULL); MA_ASSERT(frameCount > 0); MA_ASSERT(pFramesOut != NULL); if (pDevice->playback.converter.isPassthrough) { ma_device__handle_data_callback(pDevice, pFramesOut, NULL, frameCount); } else { ma_result result; ma_uint64 totalFramesReadOut; void* pRunningFramesOut; totalFramesReadOut = 0; pRunningFramesOut = pFramesOut; /* We run slightly different logic depending on whether or not we're using a heap-allocated buffer for caching input data. This will be the case if the data converter does not have the ability to retrieve the required input frame count for a given output frame count. */ if (pDevice->playback.pInputCache != NULL) { while (totalFramesReadOut < frameCount) { ma_uint64 framesToReadThisIterationIn; ma_uint64 framesToReadThisIterationOut; /* If there's any data available in the cache, that needs to get processed first. */ if (pDevice->playback.inputCacheRemaining > 0) { framesToReadThisIterationOut = (frameCount - totalFramesReadOut); framesToReadThisIterationIn = framesToReadThisIterationOut; if (framesToReadThisIterationIn > pDevice->playback.inputCacheRemaining) { framesToReadThisIterationIn = pDevice->playback.inputCacheRemaining; } result = ma_data_converter_process_pcm_frames(&pDevice->playback.converter, ma_offset_pcm_frames_ptr(pDevice->playback.pInputCache, pDevice->playback.inputCacheConsumed, pDevice->playback.format, pDevice->playback.channels), &framesToReadThisIterationIn, pRunningFramesOut, &framesToReadThisIterationOut); if (result != MA_SUCCESS) { break; } pDevice->playback.inputCacheConsumed += framesToReadThisIterationIn; pDevice->playback.inputCacheRemaining -= framesToReadThisIterationIn; totalFramesReadOut += framesToReadThisIterationOut; pRunningFramesOut = ma_offset_ptr(pRunningFramesOut, framesToReadThisIterationOut * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels)); if (framesToReadThisIterationIn == 0 && framesToReadThisIterationOut == 0) { break; /* We're done. */ } } /* Getting here means there's no data in the cache and we need to fill it up with data from the client. */ if (pDevice->playback.inputCacheRemaining == 0) { ma_device__handle_data_callback(pDevice, pDevice->playback.pInputCache, NULL, (ma_uint32)pDevice->playback.inputCacheCap); pDevice->playback.inputCacheConsumed = 0; pDevice->playback.inputCacheRemaining = pDevice->playback.inputCacheCap; } } } else { while (totalFramesReadOut < frameCount) { ma_uint8 pIntermediaryBuffer[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; /* In client format. */ ma_uint64 intermediaryBufferCap = sizeof(pIntermediaryBuffer) / ma_get_bytes_per_frame(pDevice->playback.format, pDevice->playback.channels); ma_uint64 framesToReadThisIterationIn; ma_uint64 framesReadThisIterationIn; ma_uint64 framesToReadThisIterationOut; ma_uint64 framesReadThisIterationOut; ma_uint64 requiredInputFrameCount; framesToReadThisIterationOut = (frameCount - totalFramesReadOut); framesToReadThisIterationIn = framesToReadThisIterationOut; if (framesToReadThisIterationIn > intermediaryBufferCap) { framesToReadThisIterationIn = intermediaryBufferCap; } ma_data_converter_get_required_input_frame_count(&pDevice->playback.converter, framesToReadThisIterationOut, &requiredInputFrameCount); if (framesToReadThisIterationIn > requiredInputFrameCount) { framesToReadThisIterationIn = requiredInputFrameCount; } if (framesToReadThisIterationIn > 0) { ma_device__handle_data_callback(pDevice, pIntermediaryBuffer, NULL, (ma_uint32)framesToReadThisIterationIn); } /* At this point we have our decoded data in input format and now we need to convert to output format. Note that even if we didn't read any input frames, we still want to try processing frames because there may some output frames generated from cached input data. */ framesReadThisIterationIn = framesToReadThisIterationIn; framesReadThisIterationOut = framesToReadThisIterationOut; result = ma_data_converter_process_pcm_frames(&pDevice->playback.converter, pIntermediaryBuffer, &framesReadThisIterationIn, pRunningFramesOut, &framesReadThisIterationOut); if (result != MA_SUCCESS) { break; } totalFramesReadOut += framesReadThisIterationOut; pRunningFramesOut = ma_offset_ptr(pRunningFramesOut, framesReadThisIterationOut * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels)); if (framesReadThisIterationIn == 0 && framesReadThisIterationOut == 0) { break; /* We're done. */ } } } } } /* A helper for sending sample data to the client. */ static void ma_device__send_frames_to_client(ma_device* pDevice, ma_uint32 frameCountInDeviceFormat, const void* pFramesInDeviceFormat) { MA_ASSERT(pDevice != NULL); MA_ASSERT(frameCountInDeviceFormat > 0); MA_ASSERT(pFramesInDeviceFormat != NULL); if (pDevice->capture.converter.isPassthrough) { ma_device__handle_data_callback(pDevice, NULL, pFramesInDeviceFormat, frameCountInDeviceFormat); } else { ma_result result; ma_uint8 pFramesInClientFormat[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; ma_uint64 framesInClientFormatCap = sizeof(pFramesInClientFormat) / ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels); ma_uint64 totalDeviceFramesProcessed = 0; ma_uint64 totalClientFramesProcessed = 0; const void* pRunningFramesInDeviceFormat = pFramesInDeviceFormat; /* We just keep going until we've exhaused all of our input frames and cannot generate any more output frames. */ for (;;) { ma_uint64 deviceFramesProcessedThisIteration; ma_uint64 clientFramesProcessedThisIteration; deviceFramesProcessedThisIteration = (frameCountInDeviceFormat - totalDeviceFramesProcessed); clientFramesProcessedThisIteration = framesInClientFormatCap; result = ma_data_converter_process_pcm_frames(&pDevice->capture.converter, pRunningFramesInDeviceFormat, &deviceFramesProcessedThisIteration, pFramesInClientFormat, &clientFramesProcessedThisIteration); if (result != MA_SUCCESS) { break; } if (clientFramesProcessedThisIteration > 0) { ma_device__handle_data_callback(pDevice, NULL, pFramesInClientFormat, (ma_uint32)clientFramesProcessedThisIteration); /* Safe cast. */ } pRunningFramesInDeviceFormat = ma_offset_ptr(pRunningFramesInDeviceFormat, deviceFramesProcessedThisIteration * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels)); totalDeviceFramesProcessed += deviceFramesProcessedThisIteration; totalClientFramesProcessed += clientFramesProcessedThisIteration; /* This is just to silence a warning. I might want to use this variable later so leaving in place for now. */ (void)totalClientFramesProcessed; if (deviceFramesProcessedThisIteration == 0 && clientFramesProcessedThisIteration == 0) { break; /* We're done. */ } } } } static ma_result ma_device__handle_duplex_callback_capture(ma_device* pDevice, ma_uint32 frameCountInDeviceFormat, const void* pFramesInDeviceFormat, ma_pcm_rb* pRB) { ma_result result; ma_uint32 totalDeviceFramesProcessed = 0; const void* pRunningFramesInDeviceFormat = pFramesInDeviceFormat; MA_ASSERT(pDevice != NULL); MA_ASSERT(frameCountInDeviceFormat > 0); MA_ASSERT(pFramesInDeviceFormat != NULL); MA_ASSERT(pRB != NULL); /* Write to the ring buffer. The ring buffer is in the client format which means we need to convert. */ for (;;) { ma_uint32 framesToProcessInDeviceFormat = (frameCountInDeviceFormat - totalDeviceFramesProcessed); ma_uint32 framesToProcessInClientFormat = MA_DATA_CONVERTER_STACK_BUFFER_SIZE / ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels); ma_uint64 framesProcessedInDeviceFormat; ma_uint64 framesProcessedInClientFormat; void* pFramesInClientFormat; result = ma_pcm_rb_acquire_write(pRB, &framesToProcessInClientFormat, &pFramesInClientFormat); if (result != MA_SUCCESS) { ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "Failed to acquire capture PCM frames from ring buffer."); break; } if (framesToProcessInClientFormat == 0) { if (ma_pcm_rb_pointer_distance(pRB) == (ma_int32)ma_pcm_rb_get_subbuffer_size(pRB)) { break; /* Overrun. Not enough room in the ring buffer for input frame. Excess frames are dropped. */ } } /* Convert. */ framesProcessedInDeviceFormat = framesToProcessInDeviceFormat; framesProcessedInClientFormat = framesToProcessInClientFormat; result = ma_data_converter_process_pcm_frames(&pDevice->capture.converter, pRunningFramesInDeviceFormat, &framesProcessedInDeviceFormat, pFramesInClientFormat, &framesProcessedInClientFormat); if (result != MA_SUCCESS) { break; } result = ma_pcm_rb_commit_write(pRB, (ma_uint32)framesProcessedInClientFormat); /* Safe cast. */ if (result != MA_SUCCESS) { ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "Failed to commit capture PCM frames to ring buffer."); break; } pRunningFramesInDeviceFormat = ma_offset_ptr(pRunningFramesInDeviceFormat, framesProcessedInDeviceFormat * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels)); totalDeviceFramesProcessed += (ma_uint32)framesProcessedInDeviceFormat; /* Safe cast. */ /* We're done when we're unable to process any client nor device frames. */ if (framesProcessedInClientFormat == 0 && framesProcessedInDeviceFormat == 0) { break; /* Done. */ } } return MA_SUCCESS; } static ma_result ma_device__handle_duplex_callback_playback(ma_device* pDevice, ma_uint32 frameCount, void* pFramesInInternalFormat, ma_pcm_rb* pRB) { ma_result result; ma_uint8 silentInputFrames[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; ma_uint32 totalFramesReadOut = 0; MA_ASSERT(pDevice != NULL); MA_ASSERT(frameCount > 0); MA_ASSERT(pFramesInInternalFormat != NULL); MA_ASSERT(pRB != NULL); MA_ASSERT(pDevice->playback.pInputCache != NULL); /* Sitting in the ring buffer should be captured data from the capture callback in external format. If there's not enough data in there for the whole frameCount frames we just use silence instead for the input data. */ MA_ZERO_MEMORY(silentInputFrames, sizeof(silentInputFrames)); while (totalFramesReadOut < frameCount && ma_device_is_started(pDevice)) { /* We should have a buffer allocated on the heap. Any playback frames still sitting in there need to be sent to the internal device before we process any more data from the client. */ if (pDevice->playback.inputCacheRemaining > 0) { ma_uint64 framesConvertedIn = pDevice->playback.inputCacheRemaining; ma_uint64 framesConvertedOut = (frameCount - totalFramesReadOut); ma_data_converter_process_pcm_frames(&pDevice->playback.converter, ma_offset_pcm_frames_ptr(pDevice->playback.pInputCache, pDevice->playback.inputCacheConsumed, pDevice->playback.format, pDevice->playback.channels), &framesConvertedIn, pFramesInInternalFormat, &framesConvertedOut); pDevice->playback.inputCacheConsumed += framesConvertedIn; pDevice->playback.inputCacheRemaining -= framesConvertedIn; totalFramesReadOut += (ma_uint32)framesConvertedOut; /* Safe cast. */ pFramesInInternalFormat = ma_offset_ptr(pFramesInInternalFormat, framesConvertedOut * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels)); } /* If there's no more data in the cache we'll need to fill it with some. */ if (totalFramesReadOut < frameCount && pDevice->playback.inputCacheRemaining == 0) { ma_uint32 inputFrameCount; void* pInputFrames; inputFrameCount = (ma_uint32)pDevice->playback.inputCacheCap; result = ma_pcm_rb_acquire_read(pRB, &inputFrameCount, &pInputFrames); if (result == MA_SUCCESS) { if (inputFrameCount > 0) { ma_device__handle_data_callback(pDevice, pDevice->playback.pInputCache, pInputFrames, inputFrameCount); } else { if (ma_pcm_rb_pointer_distance(pRB) == 0) { break; /* Underrun. */ } } } else { /* No capture data available. Feed in silence. */ inputFrameCount = (ma_uint32)ma_min(pDevice->playback.inputCacheCap, sizeof(silentInputFrames) / ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels)); ma_device__handle_data_callback(pDevice, pDevice->playback.pInputCache, silentInputFrames, inputFrameCount); } pDevice->playback.inputCacheConsumed = 0; pDevice->playback.inputCacheRemaining = inputFrameCount; result = ma_pcm_rb_commit_read(pRB, inputFrameCount); if (result != MA_SUCCESS) { return result; /* Should never happen. */ } } } return MA_SUCCESS; } /* A helper for changing the state of the device. */ static MA_INLINE void ma_device__set_state(ma_device* pDevice, ma_device_state newState) { c89atomic_exchange_i32((ma_int32*)&pDevice->state, (ma_int32)newState); } #ifdef MA_WIN32 GUID MA_GUID_KSDATAFORMAT_SUBTYPE_PCM = {0x00000001, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}}; GUID MA_GUID_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT = {0x00000003, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}}; /*GUID MA_GUID_KSDATAFORMAT_SUBTYPE_ALAW = {0x00000006, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}};*/ /*GUID MA_GUID_KSDATAFORMAT_SUBTYPE_MULAW = {0x00000007, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}};*/ #endif MA_API ma_uint32 ma_get_format_priority_index(ma_format format) /* Lower = better. */ { ma_uint32 i; for (i = 0; i < ma_countof(g_maFormatPriorities); ++i) { if (g_maFormatPriorities[i] == format) { return i; } } /* Getting here means the format could not be found or is equal to ma_format_unknown. */ return (ma_uint32)-1; } static ma_result ma_device__post_init_setup(ma_device* pDevice, ma_device_type deviceType); static ma_bool32 ma_device_descriptor_is_valid(const ma_device_descriptor* pDeviceDescriptor) { if (pDeviceDescriptor == NULL) { return MA_FALSE; } if (pDeviceDescriptor->format == ma_format_unknown) { return MA_FALSE; } if (pDeviceDescriptor->channels == 0 || pDeviceDescriptor->channels > MA_MAX_CHANNELS) { return MA_FALSE; } if (pDeviceDescriptor->sampleRate == 0) { return MA_FALSE; } return MA_TRUE; } static ma_result ma_device_audio_thread__default_read_write(ma_device* pDevice) { ma_result result = MA_SUCCESS; ma_bool32 exitLoop = MA_FALSE; ma_uint8 capturedDeviceData[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; ma_uint8 playbackDeviceData[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; ma_uint32 capturedDeviceDataCapInFrames = 0; ma_uint32 playbackDeviceDataCapInFrames = 0; MA_ASSERT(pDevice != NULL); /* Just some quick validation on the device type and the available callbacks. */ if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex || pDevice->type == ma_device_type_loopback) { if (pDevice->pContext->callbacks.onDeviceRead == NULL) { return MA_NOT_IMPLEMENTED; } capturedDeviceDataCapInFrames = sizeof(capturedDeviceData) / ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels); } if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { if (pDevice->pContext->callbacks.onDeviceWrite == NULL) { return MA_NOT_IMPLEMENTED; } playbackDeviceDataCapInFrames = sizeof(playbackDeviceData) / ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels); } /* NOTE: The device was started outside of this function, in the worker thread. */ while (ma_device_get_state(pDevice) == ma_device_state_started && !exitLoop) { switch (pDevice->type) { case ma_device_type_duplex: { /* The process is: onDeviceRead() -> convert -> callback -> convert -> onDeviceWrite() */ ma_uint32 totalCapturedDeviceFramesProcessed = 0; ma_uint32 capturedDevicePeriodSizeInFrames = ma_min(pDevice->capture.internalPeriodSizeInFrames, pDevice->playback.internalPeriodSizeInFrames); while (totalCapturedDeviceFramesProcessed < capturedDevicePeriodSizeInFrames) { ma_uint32 capturedDeviceFramesRemaining; ma_uint32 capturedDeviceFramesProcessed; ma_uint32 capturedDeviceFramesToProcess; ma_uint32 capturedDeviceFramesToTryProcessing = capturedDevicePeriodSizeInFrames - totalCapturedDeviceFramesProcessed; if (capturedDeviceFramesToTryProcessing > capturedDeviceDataCapInFrames) { capturedDeviceFramesToTryProcessing = capturedDeviceDataCapInFrames; } result = pDevice->pContext->callbacks.onDeviceRead(pDevice, capturedDeviceData, capturedDeviceFramesToTryProcessing, &capturedDeviceFramesToProcess); if (result != MA_SUCCESS) { exitLoop = MA_TRUE; break; } capturedDeviceFramesRemaining = capturedDeviceFramesToProcess; capturedDeviceFramesProcessed = 0; /* At this point we have our captured data in device format and we now need to convert it to client format. */ for (;;) { ma_uint8 capturedClientData[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; ma_uint8 playbackClientData[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; ma_uint32 capturedClientDataCapInFrames = sizeof(capturedClientData) / ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels); ma_uint32 playbackClientDataCapInFrames = sizeof(playbackClientData) / ma_get_bytes_per_frame(pDevice->playback.format, pDevice->playback.channels); ma_uint64 capturedClientFramesToProcessThisIteration = ma_min(capturedClientDataCapInFrames, playbackClientDataCapInFrames); ma_uint64 capturedDeviceFramesToProcessThisIteration = capturedDeviceFramesRemaining; ma_uint8* pRunningCapturedDeviceFrames = ma_offset_ptr(capturedDeviceData, capturedDeviceFramesProcessed * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels)); /* Convert capture data from device format to client format. */ result = ma_data_converter_process_pcm_frames(&pDevice->capture.converter, pRunningCapturedDeviceFrames, &capturedDeviceFramesToProcessThisIteration, capturedClientData, &capturedClientFramesToProcessThisIteration); if (result != MA_SUCCESS) { break; } /* If we weren't able to generate any output frames it must mean we've exhaused all of our input. The only time this would not be the case is if capturedClientData was too small which should never be the case when it's of the size MA_DATA_CONVERTER_STACK_BUFFER_SIZE. */ if (capturedClientFramesToProcessThisIteration == 0) { break; } ma_device__handle_data_callback(pDevice, playbackClientData, capturedClientData, (ma_uint32)capturedClientFramesToProcessThisIteration); /* Safe cast .*/ capturedDeviceFramesProcessed += (ma_uint32)capturedDeviceFramesToProcessThisIteration; /* Safe cast. */ capturedDeviceFramesRemaining -= (ma_uint32)capturedDeviceFramesToProcessThisIteration; /* Safe cast. */ /* At this point the playbackClientData buffer should be holding data that needs to be written to the device. */ for (;;) { ma_uint64 convertedClientFrameCount = capturedClientFramesToProcessThisIteration; ma_uint64 convertedDeviceFrameCount = playbackDeviceDataCapInFrames; result = ma_data_converter_process_pcm_frames(&pDevice->playback.converter, playbackClientData, &convertedClientFrameCount, playbackDeviceData, &convertedDeviceFrameCount); if (result != MA_SUCCESS) { break; } result = pDevice->pContext->callbacks.onDeviceWrite(pDevice, playbackDeviceData, (ma_uint32)convertedDeviceFrameCount, NULL); /* Safe cast. */ if (result != MA_SUCCESS) { exitLoop = MA_TRUE; break; } capturedClientFramesToProcessThisIteration -= (ma_uint32)convertedClientFrameCount; /* Safe cast. */ if (capturedClientFramesToProcessThisIteration == 0) { break; } } /* In case an error happened from ma_device_write__null()... */ if (result != MA_SUCCESS) { exitLoop = MA_TRUE; break; } } /* Make sure we don't get stuck in the inner loop. */ if (capturedDeviceFramesProcessed == 0) { break; } totalCapturedDeviceFramesProcessed += capturedDeviceFramesProcessed; } } break; case ma_device_type_capture: case ma_device_type_loopback: { ma_uint32 periodSizeInFrames = pDevice->capture.internalPeriodSizeInFrames; ma_uint32 framesReadThisPeriod = 0; while (framesReadThisPeriod < periodSizeInFrames) { ma_uint32 framesRemainingInPeriod = periodSizeInFrames - framesReadThisPeriod; ma_uint32 framesProcessed; ma_uint32 framesToReadThisIteration = framesRemainingInPeriod; if (framesToReadThisIteration > capturedDeviceDataCapInFrames) { framesToReadThisIteration = capturedDeviceDataCapInFrames; } result = pDevice->pContext->callbacks.onDeviceRead(pDevice, capturedDeviceData, framesToReadThisIteration, &framesProcessed); if (result != MA_SUCCESS) { exitLoop = MA_TRUE; break; } /* Make sure we don't get stuck in the inner loop. */ if (framesProcessed == 0) { break; } ma_device__send_frames_to_client(pDevice, framesProcessed, capturedDeviceData); framesReadThisPeriod += framesProcessed; } } break; case ma_device_type_playback: { /* We write in chunks of the period size, but use a stack allocated buffer for the intermediary. */ ma_uint32 periodSizeInFrames = pDevice->playback.internalPeriodSizeInFrames; ma_uint32 framesWrittenThisPeriod = 0; while (framesWrittenThisPeriod < periodSizeInFrames) { ma_uint32 framesRemainingInPeriod = periodSizeInFrames - framesWrittenThisPeriod; ma_uint32 framesProcessed; ma_uint32 framesToWriteThisIteration = framesRemainingInPeriod; if (framesToWriteThisIteration > playbackDeviceDataCapInFrames) { framesToWriteThisIteration = playbackDeviceDataCapInFrames; } ma_device__read_frames_from_client(pDevice, framesToWriteThisIteration, playbackDeviceData); result = pDevice->pContext->callbacks.onDeviceWrite(pDevice, playbackDeviceData, framesToWriteThisIteration, &framesProcessed); if (result != MA_SUCCESS) { exitLoop = MA_TRUE; break; } /* Make sure we don't get stuck in the inner loop. */ if (framesProcessed == 0) { break; } framesWrittenThisPeriod += framesProcessed; } } break; /* Should never get here. */ default: break; } } return result; } /******************************************************************************* Null Backend *******************************************************************************/ #ifdef MA_HAS_NULL #define MA_DEVICE_OP_NONE__NULL 0 #define MA_DEVICE_OP_START__NULL 1 #define MA_DEVICE_OP_SUSPEND__NULL 2 #define MA_DEVICE_OP_KILL__NULL 3 static ma_thread_result MA_THREADCALL ma_device_thread__null(void* pData) { ma_device* pDevice = (ma_device*)pData; MA_ASSERT(pDevice != NULL); for (;;) { /* Keep the thread alive until the device is uninitialized. */ ma_uint32 operation; /* Wait for an operation to be requested. */ ma_event_wait(&pDevice->null_device.operationEvent); /* At this point an event should have been triggered. */ operation = pDevice->null_device.operation; /* Starting the device needs to put the thread into a loop. */ if (operation == MA_DEVICE_OP_START__NULL) { /* Reset the timer just in case. */ ma_timer_init(&pDevice->null_device.timer); /* Getting here means a suspend or kill operation has been requested. */ pDevice->null_device.operationResult = MA_SUCCESS; ma_event_signal(&pDevice->null_device.operationCompletionEvent); ma_semaphore_release(&pDevice->null_device.operationSemaphore); continue; } /* Suspending the device means we need to stop the timer and just continue the loop. */ if (operation == MA_DEVICE_OP_SUSPEND__NULL) { /* We need to add the current run time to the prior run time, then reset the timer. */ pDevice->null_device.priorRunTime += ma_timer_get_time_in_seconds(&pDevice->null_device.timer); ma_timer_init(&pDevice->null_device.timer); /* We're done. */ pDevice->null_device.operationResult = MA_SUCCESS; ma_event_signal(&pDevice->null_device.operationCompletionEvent); ma_semaphore_release(&pDevice->null_device.operationSemaphore); continue; } /* Killing the device means we need to get out of this loop so that this thread can terminate. */ if (operation == MA_DEVICE_OP_KILL__NULL) { pDevice->null_device.operationResult = MA_SUCCESS; ma_event_signal(&pDevice->null_device.operationCompletionEvent); ma_semaphore_release(&pDevice->null_device.operationSemaphore); break; } /* Getting a signal on a "none" operation probably means an error. Return invalid operation. */ if (operation == MA_DEVICE_OP_NONE__NULL) { MA_ASSERT(MA_FALSE); /* <-- Trigger this in debug mode to ensure developers are aware they're doing something wrong (or there's a bug in a miniaudio). */ pDevice->null_device.operationResult = MA_INVALID_OPERATION; ma_event_signal(&pDevice->null_device.operationCompletionEvent); ma_semaphore_release(&pDevice->null_device.operationSemaphore); continue; /* Continue the loop. Don't terminate. */ } } return (ma_thread_result)0; } static ma_result ma_device_do_operation__null(ma_device* pDevice, ma_uint32 operation) { ma_result result; /* TODO: Need to review this and consider just using mutual exclusion. I think the original motivation for this was to just post the event to a queue and return immediately, but that has since changed and now this function is synchronous. I think this can be simplified to just use a mutex. */ /* The first thing to do is wait for an operation slot to become available. We only have a single slot for this, but we could extend this later to support queing of operations. */ result = ma_semaphore_wait(&pDevice->null_device.operationSemaphore); if (result != MA_SUCCESS) { return result; /* Failed to wait for the event. */ } /* When we get here it means the background thread is not referencing the operation code and it can be changed. After changing this we need to signal an event to the worker thread to let it know that it can start work. */ pDevice->null_device.operation = operation; /* Once the operation code has been set, the worker thread can start work. */ if (ma_event_signal(&pDevice->null_device.operationEvent) != MA_SUCCESS) { return MA_ERROR; } /* We want everything to be synchronous so we're going to wait for the worker thread to complete it's operation. */ if (ma_event_wait(&pDevice->null_device.operationCompletionEvent) != MA_SUCCESS) { return MA_ERROR; } return pDevice->null_device.operationResult; } static ma_uint64 ma_device_get_total_run_time_in_frames__null(ma_device* pDevice) { ma_uint32 internalSampleRate; if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { internalSampleRate = pDevice->capture.internalSampleRate; } else { internalSampleRate = pDevice->playback.internalSampleRate; } return (ma_uint64)((pDevice->null_device.priorRunTime + ma_timer_get_time_in_seconds(&pDevice->null_device.timer)) * internalSampleRate); } static ma_result ma_context_enumerate_devices__null(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData) { ma_bool32 cbResult = MA_TRUE; MA_ASSERT(pContext != NULL); MA_ASSERT(callback != NULL); /* Playback. */ if (cbResult) { ma_device_info deviceInfo; MA_ZERO_OBJECT(&deviceInfo); ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), "NULL Playback Device", (size_t)-1); deviceInfo.isDefault = MA_TRUE; /* Only one playback and capture device for the null backend, so might as well mark as default. */ cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData); } /* Capture. */ if (cbResult) { ma_device_info deviceInfo; MA_ZERO_OBJECT(&deviceInfo); ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), "NULL Capture Device", (size_t)-1); deviceInfo.isDefault = MA_TRUE; /* Only one playback and capture device for the null backend, so might as well mark as default. */ cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData); } (void)cbResult; /* Silence a static analysis warning. */ return MA_SUCCESS; } static ma_result ma_context_get_device_info__null(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo) { MA_ASSERT(pContext != NULL); if (pDeviceID != NULL && pDeviceID->nullbackend != 0) { return MA_NO_DEVICE; /* Don't know the device. */ } /* Name / Description */ if (deviceType == ma_device_type_playback) { ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), "NULL Playback Device", (size_t)-1); } else { ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), "NULL Capture Device", (size_t)-1); } pDeviceInfo->isDefault = MA_TRUE; /* Only one playback and capture device for the null backend, so might as well mark as default. */ /* Support everything on the null backend. */ pDeviceInfo->nativeDataFormats[0].format = ma_format_unknown; pDeviceInfo->nativeDataFormats[0].channels = 0; pDeviceInfo->nativeDataFormats[0].sampleRate = 0; pDeviceInfo->nativeDataFormats[0].flags = 0; pDeviceInfo->nativeDataFormatCount = 1; (void)pContext; return MA_SUCCESS; } static ma_result ma_device_uninit__null(ma_device* pDevice) { MA_ASSERT(pDevice != NULL); /* Keep it clean and wait for the device thread to finish before returning. */ ma_device_do_operation__null(pDevice, MA_DEVICE_OP_KILL__NULL); /* Wait for the thread to finish before continuing. */ ma_thread_wait(&pDevice->null_device.deviceThread); /* At this point the loop in the device thread is as good as terminated so we can uninitialize our events. */ ma_semaphore_uninit(&pDevice->null_device.operationSemaphore); ma_event_uninit(&pDevice->null_device.operationCompletionEvent); ma_event_uninit(&pDevice->null_device.operationEvent); return MA_SUCCESS; } static ma_result ma_device_init__null(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture) { ma_result result; MA_ASSERT(pDevice != NULL); MA_ZERO_OBJECT(&pDevice->null_device); if (pConfig->deviceType == ma_device_type_loopback) { return MA_DEVICE_TYPE_NOT_SUPPORTED; } /* The null backend supports everything exactly as we specify it. */ if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { pDescriptorCapture->format = (pDescriptorCapture->format != ma_format_unknown) ? pDescriptorCapture->format : MA_DEFAULT_FORMAT; pDescriptorCapture->channels = (pDescriptorCapture->channels != 0) ? pDescriptorCapture->channels : MA_DEFAULT_CHANNELS; pDescriptorCapture->sampleRate = (pDescriptorCapture->sampleRate != 0) ? pDescriptorCapture->sampleRate : MA_DEFAULT_SAMPLE_RATE; if (pDescriptorCapture->channelMap[0] == MA_CHANNEL_NONE) { ma_channel_map_init_standard(ma_standard_channel_map_default, pDescriptorCapture->channelMap, ma_countof(pDescriptorCapture->channelMap), pDescriptorCapture->channels); } pDescriptorCapture->periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptorCapture, pDescriptorCapture->sampleRate, pConfig->performanceProfile); } if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { pDescriptorPlayback->format = (pDescriptorPlayback->format != ma_format_unknown) ? pDescriptorPlayback->format : MA_DEFAULT_FORMAT; pDescriptorPlayback->channels = (pDescriptorPlayback->channels != 0) ? pDescriptorPlayback->channels : MA_DEFAULT_CHANNELS; pDescriptorPlayback->sampleRate = (pDescriptorPlayback->sampleRate != 0) ? pDescriptorPlayback->sampleRate : MA_DEFAULT_SAMPLE_RATE; if (pDescriptorPlayback->channelMap[0] == MA_CHANNEL_NONE) { ma_channel_map_init_standard(ma_standard_channel_map_default, pDescriptorPlayback->channelMap, ma_countof(pDescriptorCapture->channelMap), pDescriptorPlayback->channels); } pDescriptorPlayback->periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptorPlayback, pDescriptorPlayback->sampleRate, pConfig->performanceProfile); } /* In order to get timing right, we need to create a thread that does nothing but keeps track of the timer. This timer is started when the first period is "written" to it, and then stopped in ma_device_stop__null(). */ result = ma_event_init(&pDevice->null_device.operationEvent); if (result != MA_SUCCESS) { return result; } result = ma_event_init(&pDevice->null_device.operationCompletionEvent); if (result != MA_SUCCESS) { return result; } result = ma_semaphore_init(1, &pDevice->null_device.operationSemaphore); /* <-- It's important that the initial value is set to 1. */ if (result != MA_SUCCESS) { return result; } result = ma_thread_create(&pDevice->null_device.deviceThread, pDevice->pContext->threadPriority, 0, ma_device_thread__null, pDevice, &pDevice->pContext->allocationCallbacks); if (result != MA_SUCCESS) { return result; } return MA_SUCCESS; } static ma_result ma_device_start__null(ma_device* pDevice) { MA_ASSERT(pDevice != NULL); ma_device_do_operation__null(pDevice, MA_DEVICE_OP_START__NULL); c89atomic_exchange_32(&pDevice->null_device.isStarted, MA_TRUE); return MA_SUCCESS; } static ma_result ma_device_stop__null(ma_device* pDevice) { MA_ASSERT(pDevice != NULL); ma_device_do_operation__null(pDevice, MA_DEVICE_OP_SUSPEND__NULL); c89atomic_exchange_32(&pDevice->null_device.isStarted, MA_FALSE); return MA_SUCCESS; } static ma_result ma_device_write__null(ma_device* pDevice, const void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten) { ma_result result = MA_SUCCESS; ma_uint32 totalPCMFramesProcessed; ma_bool32 wasStartedOnEntry; if (pFramesWritten != NULL) { *pFramesWritten = 0; } wasStartedOnEntry = c89atomic_load_32(&pDevice->null_device.isStarted); /* Keep going until everything has been read. */ totalPCMFramesProcessed = 0; while (totalPCMFramesProcessed < frameCount) { ma_uint64 targetFrame; /* If there are any frames remaining in the current period, consume those first. */ if (pDevice->null_device.currentPeriodFramesRemainingPlayback > 0) { ma_uint32 framesRemaining = (frameCount - totalPCMFramesProcessed); ma_uint32 framesToProcess = pDevice->null_device.currentPeriodFramesRemainingPlayback; if (framesToProcess > framesRemaining) { framesToProcess = framesRemaining; } /* We don't actually do anything with pPCMFrames, so just mark it as unused to prevent a warning. */ (void)pPCMFrames; pDevice->null_device.currentPeriodFramesRemainingPlayback -= framesToProcess; totalPCMFramesProcessed += framesToProcess; } /* If we've consumed the current period we'll need to mark it as such an ensure the device is started if it's not already. */ if (pDevice->null_device.currentPeriodFramesRemainingPlayback == 0) { pDevice->null_device.currentPeriodFramesRemainingPlayback = 0; if (!c89atomic_load_32(&pDevice->null_device.isStarted) && !wasStartedOnEntry) { result = ma_device_start__null(pDevice); if (result != MA_SUCCESS) { break; } } } /* If we've consumed the whole buffer we can return now. */ MA_ASSERT(totalPCMFramesProcessed <= frameCount); if (totalPCMFramesProcessed == frameCount) { break; } /* Getting here means we've still got more frames to consume, we but need to wait for it to become available. */ targetFrame = pDevice->null_device.lastProcessedFramePlayback; for (;;) { ma_uint64 currentFrame; /* Stop waiting if the device has been stopped. */ if (!c89atomic_load_32(&pDevice->null_device.isStarted)) { break; } currentFrame = ma_device_get_total_run_time_in_frames__null(pDevice); if (currentFrame >= targetFrame) { break; } /* Getting here means we haven't yet reached the target sample, so continue waiting. */ ma_sleep(10); } pDevice->null_device.lastProcessedFramePlayback += pDevice->playback.internalPeriodSizeInFrames; pDevice->null_device.currentPeriodFramesRemainingPlayback = pDevice->playback.internalPeriodSizeInFrames; } if (pFramesWritten != NULL) { *pFramesWritten = totalPCMFramesProcessed; } return result; } static ma_result ma_device_read__null(ma_device* pDevice, void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesRead) { ma_result result = MA_SUCCESS; ma_uint32 totalPCMFramesProcessed; if (pFramesRead != NULL) { *pFramesRead = 0; } /* Keep going until everything has been read. */ totalPCMFramesProcessed = 0; while (totalPCMFramesProcessed < frameCount) { ma_uint64 targetFrame; /* If there are any frames remaining in the current period, consume those first. */ if (pDevice->null_device.currentPeriodFramesRemainingCapture > 0) { ma_uint32 bpf = ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels); ma_uint32 framesRemaining = (frameCount - totalPCMFramesProcessed); ma_uint32 framesToProcess = pDevice->null_device.currentPeriodFramesRemainingCapture; if (framesToProcess > framesRemaining) { framesToProcess = framesRemaining; } /* We need to ensure the output buffer is zeroed. */ MA_ZERO_MEMORY(ma_offset_ptr(pPCMFrames, totalPCMFramesProcessed*bpf), framesToProcess*bpf); pDevice->null_device.currentPeriodFramesRemainingCapture -= framesToProcess; totalPCMFramesProcessed += framesToProcess; } /* If we've consumed the current period we'll need to mark it as such an ensure the device is started if it's not already. */ if (pDevice->null_device.currentPeriodFramesRemainingCapture == 0) { pDevice->null_device.currentPeriodFramesRemainingCapture = 0; } /* If we've consumed the whole buffer we can return now. */ MA_ASSERT(totalPCMFramesProcessed <= frameCount); if (totalPCMFramesProcessed == frameCount) { break; } /* Getting here means we've still got more frames to consume, we but need to wait for it to become available. */ targetFrame = pDevice->null_device.lastProcessedFrameCapture + pDevice->capture.internalPeriodSizeInFrames; for (;;) { ma_uint64 currentFrame; /* Stop waiting if the device has been stopped. */ if (!c89atomic_load_32(&pDevice->null_device.isStarted)) { break; } currentFrame = ma_device_get_total_run_time_in_frames__null(pDevice); if (currentFrame >= targetFrame) { break; } /* Getting here means we haven't yet reached the target sample, so continue waiting. */ ma_sleep(10); } pDevice->null_device.lastProcessedFrameCapture += pDevice->capture.internalPeriodSizeInFrames; pDevice->null_device.currentPeriodFramesRemainingCapture = pDevice->capture.internalPeriodSizeInFrames; } if (pFramesRead != NULL) { *pFramesRead = totalPCMFramesProcessed; } return result; } static ma_result ma_context_uninit__null(ma_context* pContext) { MA_ASSERT(pContext != NULL); MA_ASSERT(pContext->backend == ma_backend_null); (void)pContext; return MA_SUCCESS; } static ma_result ma_context_init__null(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks) { MA_ASSERT(pContext != NULL); (void)pConfig; (void)pContext; pCallbacks->onContextInit = ma_context_init__null; pCallbacks->onContextUninit = ma_context_uninit__null; pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__null; pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__null; pCallbacks->onDeviceInit = ma_device_init__null; pCallbacks->onDeviceUninit = ma_device_uninit__null; pCallbacks->onDeviceStart = ma_device_start__null; pCallbacks->onDeviceStop = ma_device_stop__null; pCallbacks->onDeviceRead = ma_device_read__null; pCallbacks->onDeviceWrite = ma_device_write__null; pCallbacks->onDeviceDataLoop = NULL; /* Our backend is asynchronous with a blocking read-write API which means we can get miniaudio to deal with the audio thread. */ /* The null backend always works. */ return MA_SUCCESS; } #endif /******************************************************************************* WIN32 COMMON *******************************************************************************/ #if defined(MA_WIN32) #if defined(MA_WIN32_DESKTOP) #define ma_CoInitializeEx(pContext, pvReserved, dwCoInit) ((MA_PFN_CoInitializeEx)pContext->win32.CoInitializeEx)(pvReserved, dwCoInit) #define ma_CoUninitialize(pContext) ((MA_PFN_CoUninitialize)pContext->win32.CoUninitialize)() #define ma_CoCreateInstance(pContext, rclsid, pUnkOuter, dwClsContext, riid, ppv) ((MA_PFN_CoCreateInstance)pContext->win32.CoCreateInstance)(rclsid, pUnkOuter, dwClsContext, riid, ppv) #define ma_CoTaskMemFree(pContext, pv) ((MA_PFN_CoTaskMemFree)pContext->win32.CoTaskMemFree)(pv) #define ma_PropVariantClear(pContext, pvar) ((MA_PFN_PropVariantClear)pContext->win32.PropVariantClear)(pvar) #else #define ma_CoInitializeEx(pContext, pvReserved, dwCoInit) CoInitializeEx(pvReserved, dwCoInit) #define ma_CoUninitialize(pContext) CoUninitialize() #define ma_CoCreateInstance(pContext, rclsid, pUnkOuter, dwClsContext, riid, ppv) CoCreateInstance(rclsid, pUnkOuter, dwClsContext, riid, ppv) #define ma_CoTaskMemFree(pContext, pv) CoTaskMemFree(pv) #define ma_PropVariantClear(pContext, pvar) PropVariantClear(pvar) #endif #if !defined(MAXULONG_PTR) && !defined(__WATCOMC__) typedef size_t DWORD_PTR; #endif #if !defined(WAVE_FORMAT_44M08) #define WAVE_FORMAT_44M08 0x00000100 #define WAVE_FORMAT_44S08 0x00000200 #define WAVE_FORMAT_44M16 0x00000400 #define WAVE_FORMAT_44S16 0x00000800 #define WAVE_FORMAT_48M08 0x00001000 #define WAVE_FORMAT_48S08 0x00002000 #define WAVE_FORMAT_48M16 0x00004000 #define WAVE_FORMAT_48S16 0x00008000 #define WAVE_FORMAT_96M08 0x00010000 #define WAVE_FORMAT_96S08 0x00020000 #define WAVE_FORMAT_96M16 0x00040000 #define WAVE_FORMAT_96S16 0x00080000 #endif #ifndef SPEAKER_FRONT_LEFT #define SPEAKER_FRONT_LEFT 0x1 #define SPEAKER_FRONT_RIGHT 0x2 #define SPEAKER_FRONT_CENTER 0x4 #define SPEAKER_LOW_FREQUENCY 0x8 #define SPEAKER_BACK_LEFT 0x10 #define SPEAKER_BACK_RIGHT 0x20 #define SPEAKER_FRONT_LEFT_OF_CENTER 0x40 #define SPEAKER_FRONT_RIGHT_OF_CENTER 0x80 #define SPEAKER_BACK_CENTER 0x100 #define SPEAKER_SIDE_LEFT 0x200 #define SPEAKER_SIDE_RIGHT 0x400 #define SPEAKER_TOP_CENTER 0x800 #define SPEAKER_TOP_FRONT_LEFT 0x1000 #define SPEAKER_TOP_FRONT_CENTER 0x2000 #define SPEAKER_TOP_FRONT_RIGHT 0x4000 #define SPEAKER_TOP_BACK_LEFT 0x8000 #define SPEAKER_TOP_BACK_CENTER 0x10000 #define SPEAKER_TOP_BACK_RIGHT 0x20000 #endif /* The SDK that comes with old versions of MSVC (VC6, for example) does not appear to define WAVEFORMATEXTENSIBLE. We define our own implementation in this case. */ #if (defined(_MSC_VER) && !defined(_WAVEFORMATEXTENSIBLE_)) || defined(__DMC__) typedef struct { WAVEFORMATEX Format; union { WORD wValidBitsPerSample; WORD wSamplesPerBlock; WORD wReserved; } Samples; DWORD dwChannelMask; GUID SubFormat; } WAVEFORMATEXTENSIBLE; #endif #ifndef WAVE_FORMAT_EXTENSIBLE #define WAVE_FORMAT_EXTENSIBLE 0xFFFE #endif #ifndef WAVE_FORMAT_IEEE_FLOAT #define WAVE_FORMAT_IEEE_FLOAT 0x0003 #endif /* Converts an individual Win32-style channel identifier (SPEAKER_FRONT_LEFT, etc.) to miniaudio. */ static ma_uint8 ma_channel_id_to_ma__win32(DWORD id) { switch (id) { case SPEAKER_FRONT_LEFT: return MA_CHANNEL_FRONT_LEFT; case SPEAKER_FRONT_RIGHT: return MA_CHANNEL_FRONT_RIGHT; case SPEAKER_FRONT_CENTER: return MA_CHANNEL_FRONT_CENTER; case SPEAKER_LOW_FREQUENCY: return MA_CHANNEL_LFE; case SPEAKER_BACK_LEFT: return MA_CHANNEL_BACK_LEFT; case SPEAKER_BACK_RIGHT: return MA_CHANNEL_BACK_RIGHT; case SPEAKER_FRONT_LEFT_OF_CENTER: return MA_CHANNEL_FRONT_LEFT_CENTER; case SPEAKER_FRONT_RIGHT_OF_CENTER: return MA_CHANNEL_FRONT_RIGHT_CENTER; case SPEAKER_BACK_CENTER: return MA_CHANNEL_BACK_CENTER; case SPEAKER_SIDE_LEFT: return MA_CHANNEL_SIDE_LEFT; case SPEAKER_SIDE_RIGHT: return MA_CHANNEL_SIDE_RIGHT; case SPEAKER_TOP_CENTER: return MA_CHANNEL_TOP_CENTER; case SPEAKER_TOP_FRONT_LEFT: return MA_CHANNEL_TOP_FRONT_LEFT; case SPEAKER_TOP_FRONT_CENTER: return MA_CHANNEL_TOP_FRONT_CENTER; case SPEAKER_TOP_FRONT_RIGHT: return MA_CHANNEL_TOP_FRONT_RIGHT; case SPEAKER_TOP_BACK_LEFT: return MA_CHANNEL_TOP_BACK_LEFT; case SPEAKER_TOP_BACK_CENTER: return MA_CHANNEL_TOP_BACK_CENTER; case SPEAKER_TOP_BACK_RIGHT: return MA_CHANNEL_TOP_BACK_RIGHT; default: return 0; } } /* Converts an individual miniaudio channel identifier (MA_CHANNEL_FRONT_LEFT, etc.) to Win32-style. */ static DWORD ma_channel_id_to_win32(DWORD id) { switch (id) { case MA_CHANNEL_MONO: return SPEAKER_FRONT_CENTER; case MA_CHANNEL_FRONT_LEFT: return SPEAKER_FRONT_LEFT; case MA_CHANNEL_FRONT_RIGHT: return SPEAKER_FRONT_RIGHT; case MA_CHANNEL_FRONT_CENTER: return SPEAKER_FRONT_CENTER; case MA_CHANNEL_LFE: return SPEAKER_LOW_FREQUENCY; case MA_CHANNEL_BACK_LEFT: return SPEAKER_BACK_LEFT; case MA_CHANNEL_BACK_RIGHT: return SPEAKER_BACK_RIGHT; case MA_CHANNEL_FRONT_LEFT_CENTER: return SPEAKER_FRONT_LEFT_OF_CENTER; case MA_CHANNEL_FRONT_RIGHT_CENTER: return SPEAKER_FRONT_RIGHT_OF_CENTER; case MA_CHANNEL_BACK_CENTER: return SPEAKER_BACK_CENTER; case MA_CHANNEL_SIDE_LEFT: return SPEAKER_SIDE_LEFT; case MA_CHANNEL_SIDE_RIGHT: return SPEAKER_SIDE_RIGHT; case MA_CHANNEL_TOP_CENTER: return SPEAKER_TOP_CENTER; case MA_CHANNEL_TOP_FRONT_LEFT: return SPEAKER_TOP_FRONT_LEFT; case MA_CHANNEL_TOP_FRONT_CENTER: return SPEAKER_TOP_FRONT_CENTER; case MA_CHANNEL_TOP_FRONT_RIGHT: return SPEAKER_TOP_FRONT_RIGHT; case MA_CHANNEL_TOP_BACK_LEFT: return SPEAKER_TOP_BACK_LEFT; case MA_CHANNEL_TOP_BACK_CENTER: return SPEAKER_TOP_BACK_CENTER; case MA_CHANNEL_TOP_BACK_RIGHT: return SPEAKER_TOP_BACK_RIGHT; default: return 0; } } /* Converts a channel mapping to a Win32-style channel mask. */ static DWORD ma_channel_map_to_channel_mask__win32(const ma_channel* pChannelMap, ma_uint32 channels) { DWORD dwChannelMask = 0; ma_uint32 iChannel; for (iChannel = 0; iChannel < channels; ++iChannel) { dwChannelMask |= ma_channel_id_to_win32(pChannelMap[iChannel]); } return dwChannelMask; } /* Converts a Win32-style channel mask to a miniaudio channel map. */ static void ma_channel_mask_to_channel_map__win32(DWORD dwChannelMask, ma_uint32 channels, ma_channel* pChannelMap) { /* If the channel mask is set to 0, just assume a default Win32 channel map. */ if (dwChannelMask == 0) { ma_channel_map_init_standard(ma_standard_channel_map_microsoft, pChannelMap, channels, channels); } else { if (channels == 1 && (dwChannelMask & SPEAKER_FRONT_CENTER) != 0) { pChannelMap[0] = MA_CHANNEL_MONO; } else { /* Just iterate over each bit. */ ma_uint32 iChannel = 0; ma_uint32 iBit; for (iBit = 0; iBit < 32 && iChannel < channels; ++iBit) { DWORD bitValue = (dwChannelMask & (1UL << iBit)); if (bitValue != 0) { /* The bit is set. */ pChannelMap[iChannel] = ma_channel_id_to_ma__win32(bitValue); iChannel += 1; } } } } } #ifdef __cplusplus static ma_bool32 ma_is_guid_equal(const void* a, const void* b) { return IsEqualGUID(*(const GUID*)a, *(const GUID*)b); } #else #define ma_is_guid_equal(a, b) IsEqualGUID((const GUID*)a, (const GUID*)b) #endif static MA_INLINE ma_bool32 ma_is_guid_null(const void* guid) { static GUID nullguid = {0x00000000, 0x0000, 0x0000, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; return ma_is_guid_equal(guid, &nullguid); } static ma_format ma_format_from_WAVEFORMATEX(const WAVEFORMATEX* pWF) { MA_ASSERT(pWF != NULL); if (pWF->wFormatTag == WAVE_FORMAT_EXTENSIBLE) { const WAVEFORMATEXTENSIBLE* pWFEX = (const WAVEFORMATEXTENSIBLE*)pWF; if (ma_is_guid_equal(&pWFEX->SubFormat, &MA_GUID_KSDATAFORMAT_SUBTYPE_PCM)) { if (pWFEX->Samples.wValidBitsPerSample == 32) { return ma_format_s32; } if (pWFEX->Samples.wValidBitsPerSample == 24) { if (pWFEX->Format.wBitsPerSample == 32) { return ma_format_s32; } if (pWFEX->Format.wBitsPerSample == 24) { return ma_format_s24; } } if (pWFEX->Samples.wValidBitsPerSample == 16) { return ma_format_s16; } if (pWFEX->Samples.wValidBitsPerSample == 8) { return ma_format_u8; } } if (ma_is_guid_equal(&pWFEX->SubFormat, &MA_GUID_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT)) { if (pWFEX->Samples.wValidBitsPerSample == 32) { return ma_format_f32; } /* if (pWFEX->Samples.wValidBitsPerSample == 64) { return ma_format_f64; } */ } } else { if (pWF->wFormatTag == WAVE_FORMAT_PCM) { if (pWF->wBitsPerSample == 32) { return ma_format_s32; } if (pWF->wBitsPerSample == 24) { return ma_format_s24; } if (pWF->wBitsPerSample == 16) { return ma_format_s16; } if (pWF->wBitsPerSample == 8) { return ma_format_u8; } } if (pWF->wFormatTag == WAVE_FORMAT_IEEE_FLOAT) { if (pWF->wBitsPerSample == 32) { return ma_format_f32; } if (pWF->wBitsPerSample == 64) { /*return ma_format_f64;*/ } } } return ma_format_unknown; } #endif /******************************************************************************* WASAPI Backend *******************************************************************************/ #ifdef MA_HAS_WASAPI #if 0 #if defined(_MSC_VER) #pragma warning(push) #pragma warning(disable:4091) /* 'typedef ': ignored on left of '' when no variable is declared */ #endif #include #include #if defined(_MSC_VER) #pragma warning(pop) #endif #endif /* 0 */ static ma_result ma_device_reroute__wasapi(ma_device* pDevice, ma_device_type deviceType); /* Some compilers don't define VerifyVersionInfoW. Need to write this ourselves. */ #define MA_WIN32_WINNT_VISTA 0x0600 #define MA_VER_MINORVERSION 0x01 #define MA_VER_MAJORVERSION 0x02 #define MA_VER_SERVICEPACKMAJOR 0x20 #define MA_VER_GREATER_EQUAL 0x03 typedef struct { DWORD dwOSVersionInfoSize; DWORD dwMajorVersion; DWORD dwMinorVersion; DWORD dwBuildNumber; DWORD dwPlatformId; WCHAR szCSDVersion[128]; WORD wServicePackMajor; WORD wServicePackMinor; WORD wSuiteMask; BYTE wProductType; BYTE wReserved; } ma_OSVERSIONINFOEXW; typedef BOOL (WINAPI * ma_PFNVerifyVersionInfoW) (ma_OSVERSIONINFOEXW* lpVersionInfo, DWORD dwTypeMask, DWORDLONG dwlConditionMask); typedef ULONGLONG (WINAPI * ma_PFNVerSetConditionMask)(ULONGLONG dwlConditionMask, DWORD dwTypeBitMask, BYTE dwConditionMask); #ifndef PROPERTYKEY_DEFINED #define PROPERTYKEY_DEFINED #ifndef __WATCOMC__ typedef struct { GUID fmtid; DWORD pid; } PROPERTYKEY; #endif #endif /* Some compilers don't define PropVariantInit(). We just do this ourselves since it's just a memset(). */ static MA_INLINE void ma_PropVariantInit(PROPVARIANT* pProp) { MA_ZERO_OBJECT(pProp); } static const PROPERTYKEY MA_PKEY_Device_FriendlyName = {{0xA45C254E, 0xDF1C, 0x4EFD, {0x80, 0x20, 0x67, 0xD1, 0x46, 0xA8, 0x50, 0xE0}}, 14}; static const PROPERTYKEY MA_PKEY_AudioEngine_DeviceFormat = {{0xF19F064D, 0x82C, 0x4E27, {0xBC, 0x73, 0x68, 0x82, 0xA1, 0xBB, 0x8E, 0x4C}}, 0}; static const IID MA_IID_IUnknown = {0x00000000, 0x0000, 0x0000, {0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46}}; /* 00000000-0000-0000-C000-000000000046 */ #if !defined(MA_WIN32_DESKTOP) && !defined(MA_WIN32_GDK) static const IID MA_IID_IAgileObject = {0x94EA2B94, 0xE9CC, 0x49E0, {0xC0, 0xFF, 0xEE, 0x64, 0xCA, 0x8F, 0x5B, 0x90}}; /* 94EA2B94-E9CC-49E0-C0FF-EE64CA8F5B90 */ #endif static const IID MA_IID_IAudioClient = {0x1CB9AD4C, 0xDBFA, 0x4C32, {0xB1, 0x78, 0xC2, 0xF5, 0x68, 0xA7, 0x03, 0xB2}}; /* 1CB9AD4C-DBFA-4C32-B178-C2F568A703B2 = __uuidof(IAudioClient) */ static const IID MA_IID_IAudioClient2 = {0x726778CD, 0xF60A, 0x4EDA, {0x82, 0xDE, 0xE4, 0x76, 0x10, 0xCD, 0x78, 0xAA}}; /* 726778CD-F60A-4EDA-82DE-E47610CD78AA = __uuidof(IAudioClient2) */ static const IID MA_IID_IAudioClient3 = {0x7ED4EE07, 0x8E67, 0x4CD4, {0x8C, 0x1A, 0x2B, 0x7A, 0x59, 0x87, 0xAD, 0x42}}; /* 7ED4EE07-8E67-4CD4-8C1A-2B7A5987AD42 = __uuidof(IAudioClient3) */ static const IID MA_IID_IAudioRenderClient = {0xF294ACFC, 0x3146, 0x4483, {0xA7, 0xBF, 0xAD, 0xDC, 0xA7, 0xC2, 0x60, 0xE2}}; /* F294ACFC-3146-4483-A7BF-ADDCA7C260E2 = __uuidof(IAudioRenderClient) */ static const IID MA_IID_IAudioCaptureClient = {0xC8ADBD64, 0xE71E, 0x48A0, {0xA4, 0xDE, 0x18, 0x5C, 0x39, 0x5C, 0xD3, 0x17}}; /* C8ADBD64-E71E-48A0-A4DE-185C395CD317 = __uuidof(IAudioCaptureClient) */ static const IID MA_IID_IMMNotificationClient = {0x7991EEC9, 0x7E89, 0x4D85, {0x83, 0x90, 0x6C, 0x70, 0x3C, 0xEC, 0x60, 0xC0}}; /* 7991EEC9-7E89-4D85-8390-6C703CEC60C0 = __uuidof(IMMNotificationClient) */ #if !defined(MA_WIN32_DESKTOP) && !defined(MA_WIN32_GDK) static const IID MA_IID_DEVINTERFACE_AUDIO_RENDER = {0xE6327CAD, 0xDCEC, 0x4949, {0xAE, 0x8A, 0x99, 0x1E, 0x97, 0x6A, 0x79, 0xD2}}; /* E6327CAD-DCEC-4949-AE8A-991E976A79D2 */ static const IID MA_IID_DEVINTERFACE_AUDIO_CAPTURE = {0x2EEF81BE, 0x33FA, 0x4800, {0x96, 0x70, 0x1C, 0xD4, 0x74, 0x97, 0x2C, 0x3F}}; /* 2EEF81BE-33FA-4800-9670-1CD474972C3F */ static const IID MA_IID_IActivateAudioInterfaceCompletionHandler = {0x41D949AB, 0x9862, 0x444A, {0x80, 0xF6, 0xC2, 0x61, 0x33, 0x4D, 0xA5, 0xEB}}; /* 41D949AB-9862-444A-80F6-C261334DA5EB */ #endif static const IID MA_CLSID_MMDeviceEnumerator_Instance = {0xBCDE0395, 0xE52F, 0x467C, {0x8E, 0x3D, 0xC4, 0x57, 0x92, 0x91, 0x69, 0x2E}}; /* BCDE0395-E52F-467C-8E3D-C4579291692E = __uuidof(MMDeviceEnumerator) */ static const IID MA_IID_IMMDeviceEnumerator_Instance = {0xA95664D2, 0x9614, 0x4F35, {0xA7, 0x46, 0xDE, 0x8D, 0xB6, 0x36, 0x17, 0xE6}}; /* A95664D2-9614-4F35-A746-DE8DB63617E6 = __uuidof(IMMDeviceEnumerator) */ #ifdef __cplusplus #define MA_CLSID_MMDeviceEnumerator MA_CLSID_MMDeviceEnumerator_Instance #define MA_IID_IMMDeviceEnumerator MA_IID_IMMDeviceEnumerator_Instance #else #define MA_CLSID_MMDeviceEnumerator &MA_CLSID_MMDeviceEnumerator_Instance #define MA_IID_IMMDeviceEnumerator &MA_IID_IMMDeviceEnumerator_Instance #endif typedef struct ma_IUnknown ma_IUnknown; #if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) #define MA_MM_DEVICE_STATE_ACTIVE 1 #define MA_MM_DEVICE_STATE_DISABLED 2 #define MA_MM_DEVICE_STATE_NOTPRESENT 4 #define MA_MM_DEVICE_STATE_UNPLUGGED 8 typedef struct ma_IMMDeviceEnumerator ma_IMMDeviceEnumerator; typedef struct ma_IMMDeviceCollection ma_IMMDeviceCollection; typedef struct ma_IMMDevice ma_IMMDevice; #else typedef struct ma_IActivateAudioInterfaceCompletionHandler ma_IActivateAudioInterfaceCompletionHandler; typedef struct ma_IActivateAudioInterfaceAsyncOperation ma_IActivateAudioInterfaceAsyncOperation; #endif typedef struct ma_IPropertyStore ma_IPropertyStore; typedef struct ma_IAudioClient ma_IAudioClient; typedef struct ma_IAudioClient2 ma_IAudioClient2; typedef struct ma_IAudioClient3 ma_IAudioClient3; typedef struct ma_IAudioRenderClient ma_IAudioRenderClient; typedef struct ma_IAudioCaptureClient ma_IAudioCaptureClient; typedef ma_int64 MA_REFERENCE_TIME; #define MA_AUDCLNT_STREAMFLAGS_CROSSPROCESS 0x00010000 #define MA_AUDCLNT_STREAMFLAGS_LOOPBACK 0x00020000 #define MA_AUDCLNT_STREAMFLAGS_EVENTCALLBACK 0x00040000 #define MA_AUDCLNT_STREAMFLAGS_NOPERSIST 0x00080000 #define MA_AUDCLNT_STREAMFLAGS_RATEADJUST 0x00100000 #define MA_AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY 0x08000000 #define MA_AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM 0x80000000 #define MA_AUDCLNT_SESSIONFLAGS_EXPIREWHENUNOWNED 0x10000000 #define MA_AUDCLNT_SESSIONFLAGS_DISPLAY_HIDE 0x20000000 #define MA_AUDCLNT_SESSIONFLAGS_DISPLAY_HIDEWHENEXPIRED 0x40000000 /* Buffer flags. */ #define MA_AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY 1 #define MA_AUDCLNT_BUFFERFLAGS_SILENT 2 #define MA_AUDCLNT_BUFFERFLAGS_TIMESTAMP_ERROR 4 typedef enum { ma_eRender = 0, ma_eCapture = 1, ma_eAll = 2 } ma_EDataFlow; typedef enum { ma_eConsole = 0, ma_eMultimedia = 1, ma_eCommunications = 2 } ma_ERole; typedef enum { MA_AUDCLNT_SHAREMODE_SHARED, MA_AUDCLNT_SHAREMODE_EXCLUSIVE } MA_AUDCLNT_SHAREMODE; typedef enum { MA_AudioCategory_Other = 0 /* <-- miniaudio is only caring about Other. */ } MA_AUDIO_STREAM_CATEGORY; typedef struct { ma_uint32 cbSize; BOOL bIsOffload; MA_AUDIO_STREAM_CATEGORY eCategory; } ma_AudioClientProperties; /* IUnknown */ typedef struct { /* IUnknown */ HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IUnknown* pThis, const IID* const riid, void** ppObject); ULONG (STDMETHODCALLTYPE * AddRef) (ma_IUnknown* pThis); ULONG (STDMETHODCALLTYPE * Release) (ma_IUnknown* pThis); } ma_IUnknownVtbl; struct ma_IUnknown { ma_IUnknownVtbl* lpVtbl; }; static MA_INLINE HRESULT ma_IUnknown_QueryInterface(ma_IUnknown* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } static MA_INLINE ULONG ma_IUnknown_AddRef(ma_IUnknown* pThis) { return pThis->lpVtbl->AddRef(pThis); } static MA_INLINE ULONG ma_IUnknown_Release(ma_IUnknown* pThis) { return pThis->lpVtbl->Release(pThis); } #if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) /* IMMNotificationClient */ typedef struct { /* IUnknown */ HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IMMNotificationClient* pThis, const IID* const riid, void** ppObject); ULONG (STDMETHODCALLTYPE * AddRef) (ma_IMMNotificationClient* pThis); ULONG (STDMETHODCALLTYPE * Release) (ma_IMMNotificationClient* pThis); /* IMMNotificationClient */ HRESULT (STDMETHODCALLTYPE * OnDeviceStateChanged) (ma_IMMNotificationClient* pThis, LPCWSTR pDeviceID, DWORD dwNewState); HRESULT (STDMETHODCALLTYPE * OnDeviceAdded) (ma_IMMNotificationClient* pThis, LPCWSTR pDeviceID); HRESULT (STDMETHODCALLTYPE * OnDeviceRemoved) (ma_IMMNotificationClient* pThis, LPCWSTR pDeviceID); HRESULT (STDMETHODCALLTYPE * OnDefaultDeviceChanged)(ma_IMMNotificationClient* pThis, ma_EDataFlow dataFlow, ma_ERole role, LPCWSTR pDefaultDeviceID); HRESULT (STDMETHODCALLTYPE * OnPropertyValueChanged)(ma_IMMNotificationClient* pThis, LPCWSTR pDeviceID, const PROPERTYKEY key); } ma_IMMNotificationClientVtbl; /* IMMDeviceEnumerator */ typedef struct { /* IUnknown */ HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IMMDeviceEnumerator* pThis, const IID* const riid, void** ppObject); ULONG (STDMETHODCALLTYPE * AddRef) (ma_IMMDeviceEnumerator* pThis); ULONG (STDMETHODCALLTYPE * Release) (ma_IMMDeviceEnumerator* pThis); /* IMMDeviceEnumerator */ HRESULT (STDMETHODCALLTYPE * EnumAudioEndpoints) (ma_IMMDeviceEnumerator* pThis, ma_EDataFlow dataFlow, DWORD dwStateMask, ma_IMMDeviceCollection** ppDevices); HRESULT (STDMETHODCALLTYPE * GetDefaultAudioEndpoint) (ma_IMMDeviceEnumerator* pThis, ma_EDataFlow dataFlow, ma_ERole role, ma_IMMDevice** ppEndpoint); HRESULT (STDMETHODCALLTYPE * GetDevice) (ma_IMMDeviceEnumerator* pThis, LPCWSTR pID, ma_IMMDevice** ppDevice); HRESULT (STDMETHODCALLTYPE * RegisterEndpointNotificationCallback) (ma_IMMDeviceEnumerator* pThis, ma_IMMNotificationClient* pClient); HRESULT (STDMETHODCALLTYPE * UnregisterEndpointNotificationCallback)(ma_IMMDeviceEnumerator* pThis, ma_IMMNotificationClient* pClient); } ma_IMMDeviceEnumeratorVtbl; struct ma_IMMDeviceEnumerator { ma_IMMDeviceEnumeratorVtbl* lpVtbl; }; static MA_INLINE HRESULT ma_IMMDeviceEnumerator_QueryInterface(ma_IMMDeviceEnumerator* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } static MA_INLINE ULONG ma_IMMDeviceEnumerator_AddRef(ma_IMMDeviceEnumerator* pThis) { return pThis->lpVtbl->AddRef(pThis); } static MA_INLINE ULONG ma_IMMDeviceEnumerator_Release(ma_IMMDeviceEnumerator* pThis) { return pThis->lpVtbl->Release(pThis); } static MA_INLINE HRESULT ma_IMMDeviceEnumerator_EnumAudioEndpoints(ma_IMMDeviceEnumerator* pThis, ma_EDataFlow dataFlow, DWORD dwStateMask, ma_IMMDeviceCollection** ppDevices) { return pThis->lpVtbl->EnumAudioEndpoints(pThis, dataFlow, dwStateMask, ppDevices); } static MA_INLINE HRESULT ma_IMMDeviceEnumerator_GetDefaultAudioEndpoint(ma_IMMDeviceEnumerator* pThis, ma_EDataFlow dataFlow, ma_ERole role, ma_IMMDevice** ppEndpoint) { return pThis->lpVtbl->GetDefaultAudioEndpoint(pThis, dataFlow, role, ppEndpoint); } static MA_INLINE HRESULT ma_IMMDeviceEnumerator_GetDevice(ma_IMMDeviceEnumerator* pThis, LPCWSTR pID, ma_IMMDevice** ppDevice) { return pThis->lpVtbl->GetDevice(pThis, pID, ppDevice); } static MA_INLINE HRESULT ma_IMMDeviceEnumerator_RegisterEndpointNotificationCallback(ma_IMMDeviceEnumerator* pThis, ma_IMMNotificationClient* pClient) { return pThis->lpVtbl->RegisterEndpointNotificationCallback(pThis, pClient); } static MA_INLINE HRESULT ma_IMMDeviceEnumerator_UnregisterEndpointNotificationCallback(ma_IMMDeviceEnumerator* pThis, ma_IMMNotificationClient* pClient) { return pThis->lpVtbl->UnregisterEndpointNotificationCallback(pThis, pClient); } /* IMMDeviceCollection */ typedef struct { /* IUnknown */ HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IMMDeviceCollection* pThis, const IID* const riid, void** ppObject); ULONG (STDMETHODCALLTYPE * AddRef) (ma_IMMDeviceCollection* pThis); ULONG (STDMETHODCALLTYPE * Release) (ma_IMMDeviceCollection* pThis); /* IMMDeviceCollection */ HRESULT (STDMETHODCALLTYPE * GetCount)(ma_IMMDeviceCollection* pThis, UINT* pDevices); HRESULT (STDMETHODCALLTYPE * Item) (ma_IMMDeviceCollection* pThis, UINT nDevice, ma_IMMDevice** ppDevice); } ma_IMMDeviceCollectionVtbl; struct ma_IMMDeviceCollection { ma_IMMDeviceCollectionVtbl* lpVtbl; }; static MA_INLINE HRESULT ma_IMMDeviceCollection_QueryInterface(ma_IMMDeviceCollection* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } static MA_INLINE ULONG ma_IMMDeviceCollection_AddRef(ma_IMMDeviceCollection* pThis) { return pThis->lpVtbl->AddRef(pThis); } static MA_INLINE ULONG ma_IMMDeviceCollection_Release(ma_IMMDeviceCollection* pThis) { return pThis->lpVtbl->Release(pThis); } static MA_INLINE HRESULT ma_IMMDeviceCollection_GetCount(ma_IMMDeviceCollection* pThis, UINT* pDevices) { return pThis->lpVtbl->GetCount(pThis, pDevices); } static MA_INLINE HRESULT ma_IMMDeviceCollection_Item(ma_IMMDeviceCollection* pThis, UINT nDevice, ma_IMMDevice** ppDevice) { return pThis->lpVtbl->Item(pThis, nDevice, ppDevice); } /* IMMDevice */ typedef struct { /* IUnknown */ HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IMMDevice* pThis, const IID* const riid, void** ppObject); ULONG (STDMETHODCALLTYPE * AddRef) (ma_IMMDevice* pThis); ULONG (STDMETHODCALLTYPE * Release) (ma_IMMDevice* pThis); /* IMMDevice */ HRESULT (STDMETHODCALLTYPE * Activate) (ma_IMMDevice* pThis, const IID* const iid, DWORD dwClsCtx, PROPVARIANT* pActivationParams, void** ppInterface); HRESULT (STDMETHODCALLTYPE * OpenPropertyStore)(ma_IMMDevice* pThis, DWORD stgmAccess, ma_IPropertyStore** ppProperties); HRESULT (STDMETHODCALLTYPE * GetId) (ma_IMMDevice* pThis, LPWSTR *pID); HRESULT (STDMETHODCALLTYPE * GetState) (ma_IMMDevice* pThis, DWORD *pState); } ma_IMMDeviceVtbl; struct ma_IMMDevice { ma_IMMDeviceVtbl* lpVtbl; }; static MA_INLINE HRESULT ma_IMMDevice_QueryInterface(ma_IMMDevice* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } static MA_INLINE ULONG ma_IMMDevice_AddRef(ma_IMMDevice* pThis) { return pThis->lpVtbl->AddRef(pThis); } static MA_INLINE ULONG ma_IMMDevice_Release(ma_IMMDevice* pThis) { return pThis->lpVtbl->Release(pThis); } static MA_INLINE HRESULT ma_IMMDevice_Activate(ma_IMMDevice* pThis, const IID* const iid, DWORD dwClsCtx, PROPVARIANT* pActivationParams, void** ppInterface) { return pThis->lpVtbl->Activate(pThis, iid, dwClsCtx, pActivationParams, ppInterface); } static MA_INLINE HRESULT ma_IMMDevice_OpenPropertyStore(ma_IMMDevice* pThis, DWORD stgmAccess, ma_IPropertyStore** ppProperties) { return pThis->lpVtbl->OpenPropertyStore(pThis, stgmAccess, ppProperties); } static MA_INLINE HRESULT ma_IMMDevice_GetId(ma_IMMDevice* pThis, LPWSTR *pID) { return pThis->lpVtbl->GetId(pThis, pID); } static MA_INLINE HRESULT ma_IMMDevice_GetState(ma_IMMDevice* pThis, DWORD *pState) { return pThis->lpVtbl->GetState(pThis, pState); } #else /* IActivateAudioInterfaceAsyncOperation */ typedef struct { /* IUnknown */ HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IActivateAudioInterfaceAsyncOperation* pThis, const IID* const riid, void** ppObject); ULONG (STDMETHODCALLTYPE * AddRef) (ma_IActivateAudioInterfaceAsyncOperation* pThis); ULONG (STDMETHODCALLTYPE * Release) (ma_IActivateAudioInterfaceAsyncOperation* pThis); /* IActivateAudioInterfaceAsyncOperation */ HRESULT (STDMETHODCALLTYPE * GetActivateResult)(ma_IActivateAudioInterfaceAsyncOperation* pThis, HRESULT *pActivateResult, ma_IUnknown** ppActivatedInterface); } ma_IActivateAudioInterfaceAsyncOperationVtbl; struct ma_IActivateAudioInterfaceAsyncOperation { ma_IActivateAudioInterfaceAsyncOperationVtbl* lpVtbl; }; static MA_INLINE HRESULT ma_IActivateAudioInterfaceAsyncOperation_QueryInterface(ma_IActivateAudioInterfaceAsyncOperation* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } static MA_INLINE ULONG ma_IActivateAudioInterfaceAsyncOperation_AddRef(ma_IActivateAudioInterfaceAsyncOperation* pThis) { return pThis->lpVtbl->AddRef(pThis); } static MA_INLINE ULONG ma_IActivateAudioInterfaceAsyncOperation_Release(ma_IActivateAudioInterfaceAsyncOperation* pThis) { return pThis->lpVtbl->Release(pThis); } static MA_INLINE HRESULT ma_IActivateAudioInterfaceAsyncOperation_GetActivateResult(ma_IActivateAudioInterfaceAsyncOperation* pThis, HRESULT *pActivateResult, ma_IUnknown** ppActivatedInterface) { return pThis->lpVtbl->GetActivateResult(pThis, pActivateResult, ppActivatedInterface); } #endif /* IPropertyStore */ typedef struct { /* IUnknown */ HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IPropertyStore* pThis, const IID* const riid, void** ppObject); ULONG (STDMETHODCALLTYPE * AddRef) (ma_IPropertyStore* pThis); ULONG (STDMETHODCALLTYPE * Release) (ma_IPropertyStore* pThis); /* IPropertyStore */ HRESULT (STDMETHODCALLTYPE * GetCount)(ma_IPropertyStore* pThis, DWORD* pPropCount); HRESULT (STDMETHODCALLTYPE * GetAt) (ma_IPropertyStore* pThis, DWORD propIndex, PROPERTYKEY* pPropKey); HRESULT (STDMETHODCALLTYPE * GetValue)(ma_IPropertyStore* pThis, const PROPERTYKEY* const pKey, PROPVARIANT* pPropVar); HRESULT (STDMETHODCALLTYPE * SetValue)(ma_IPropertyStore* pThis, const PROPERTYKEY* const pKey, const PROPVARIANT* const pPropVar); HRESULT (STDMETHODCALLTYPE * Commit) (ma_IPropertyStore* pThis); } ma_IPropertyStoreVtbl; struct ma_IPropertyStore { ma_IPropertyStoreVtbl* lpVtbl; }; static MA_INLINE HRESULT ma_IPropertyStore_QueryInterface(ma_IPropertyStore* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } static MA_INLINE ULONG ma_IPropertyStore_AddRef(ma_IPropertyStore* pThis) { return pThis->lpVtbl->AddRef(pThis); } static MA_INLINE ULONG ma_IPropertyStore_Release(ma_IPropertyStore* pThis) { return pThis->lpVtbl->Release(pThis); } static MA_INLINE HRESULT ma_IPropertyStore_GetCount(ma_IPropertyStore* pThis, DWORD* pPropCount) { return pThis->lpVtbl->GetCount(pThis, pPropCount); } static MA_INLINE HRESULT ma_IPropertyStore_GetAt(ma_IPropertyStore* pThis, DWORD propIndex, PROPERTYKEY* pPropKey) { return pThis->lpVtbl->GetAt(pThis, propIndex, pPropKey); } static MA_INLINE HRESULT ma_IPropertyStore_GetValue(ma_IPropertyStore* pThis, const PROPERTYKEY* const pKey, PROPVARIANT* pPropVar) { return pThis->lpVtbl->GetValue(pThis, pKey, pPropVar); } static MA_INLINE HRESULT ma_IPropertyStore_SetValue(ma_IPropertyStore* pThis, const PROPERTYKEY* const pKey, const PROPVARIANT* const pPropVar) { return pThis->lpVtbl->SetValue(pThis, pKey, pPropVar); } static MA_INLINE HRESULT ma_IPropertyStore_Commit(ma_IPropertyStore* pThis) { return pThis->lpVtbl->Commit(pThis); } /* IAudioClient */ typedef struct { /* IUnknown */ HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IAudioClient* pThis, const IID* const riid, void** ppObject); ULONG (STDMETHODCALLTYPE * AddRef) (ma_IAudioClient* pThis); ULONG (STDMETHODCALLTYPE * Release) (ma_IAudioClient* pThis); /* IAudioClient */ HRESULT (STDMETHODCALLTYPE * Initialize) (ma_IAudioClient* pThis, MA_AUDCLNT_SHAREMODE shareMode, DWORD streamFlags, MA_REFERENCE_TIME bufferDuration, MA_REFERENCE_TIME periodicity, const WAVEFORMATEX* pFormat, const GUID* pAudioSessionGuid); HRESULT (STDMETHODCALLTYPE * GetBufferSize) (ma_IAudioClient* pThis, ma_uint32* pNumBufferFrames); HRESULT (STDMETHODCALLTYPE * GetStreamLatency) (ma_IAudioClient* pThis, MA_REFERENCE_TIME* pLatency); HRESULT (STDMETHODCALLTYPE * GetCurrentPadding)(ma_IAudioClient* pThis, ma_uint32* pNumPaddingFrames); HRESULT (STDMETHODCALLTYPE * IsFormatSupported)(ma_IAudioClient* pThis, MA_AUDCLNT_SHAREMODE shareMode, const WAVEFORMATEX* pFormat, WAVEFORMATEX** ppClosestMatch); HRESULT (STDMETHODCALLTYPE * GetMixFormat) (ma_IAudioClient* pThis, WAVEFORMATEX** ppDeviceFormat); HRESULT (STDMETHODCALLTYPE * GetDevicePeriod) (ma_IAudioClient* pThis, MA_REFERENCE_TIME* pDefaultDevicePeriod, MA_REFERENCE_TIME* pMinimumDevicePeriod); HRESULT (STDMETHODCALLTYPE * Start) (ma_IAudioClient* pThis); HRESULT (STDMETHODCALLTYPE * Stop) (ma_IAudioClient* pThis); HRESULT (STDMETHODCALLTYPE * Reset) (ma_IAudioClient* pThis); HRESULT (STDMETHODCALLTYPE * SetEventHandle) (ma_IAudioClient* pThis, HANDLE eventHandle); HRESULT (STDMETHODCALLTYPE * GetService) (ma_IAudioClient* pThis, const IID* const riid, void** pp); } ma_IAudioClientVtbl; struct ma_IAudioClient { ma_IAudioClientVtbl* lpVtbl; }; static MA_INLINE HRESULT ma_IAudioClient_QueryInterface(ma_IAudioClient* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } static MA_INLINE ULONG ma_IAudioClient_AddRef(ma_IAudioClient* pThis) { return pThis->lpVtbl->AddRef(pThis); } static MA_INLINE ULONG ma_IAudioClient_Release(ma_IAudioClient* pThis) { return pThis->lpVtbl->Release(pThis); } static MA_INLINE HRESULT ma_IAudioClient_Initialize(ma_IAudioClient* pThis, MA_AUDCLNT_SHAREMODE shareMode, DWORD streamFlags, MA_REFERENCE_TIME bufferDuration, MA_REFERENCE_TIME periodicity, const WAVEFORMATEX* pFormat, const GUID* pAudioSessionGuid) { return pThis->lpVtbl->Initialize(pThis, shareMode, streamFlags, bufferDuration, periodicity, pFormat, pAudioSessionGuid); } static MA_INLINE HRESULT ma_IAudioClient_GetBufferSize(ma_IAudioClient* pThis, ma_uint32* pNumBufferFrames) { return pThis->lpVtbl->GetBufferSize(pThis, pNumBufferFrames); } static MA_INLINE HRESULT ma_IAudioClient_GetStreamLatency(ma_IAudioClient* pThis, MA_REFERENCE_TIME* pLatency) { return pThis->lpVtbl->GetStreamLatency(pThis, pLatency); } static MA_INLINE HRESULT ma_IAudioClient_GetCurrentPadding(ma_IAudioClient* pThis, ma_uint32* pNumPaddingFrames) { return pThis->lpVtbl->GetCurrentPadding(pThis, pNumPaddingFrames); } static MA_INLINE HRESULT ma_IAudioClient_IsFormatSupported(ma_IAudioClient* pThis, MA_AUDCLNT_SHAREMODE shareMode, const WAVEFORMATEX* pFormat, WAVEFORMATEX** ppClosestMatch) { return pThis->lpVtbl->IsFormatSupported(pThis, shareMode, pFormat, ppClosestMatch); } static MA_INLINE HRESULT ma_IAudioClient_GetMixFormat(ma_IAudioClient* pThis, WAVEFORMATEX** ppDeviceFormat) { return pThis->lpVtbl->GetMixFormat(pThis, ppDeviceFormat); } static MA_INLINE HRESULT ma_IAudioClient_GetDevicePeriod(ma_IAudioClient* pThis, MA_REFERENCE_TIME* pDefaultDevicePeriod, MA_REFERENCE_TIME* pMinimumDevicePeriod) { return pThis->lpVtbl->GetDevicePeriod(pThis, pDefaultDevicePeriod, pMinimumDevicePeriod); } static MA_INLINE HRESULT ma_IAudioClient_Start(ma_IAudioClient* pThis) { return pThis->lpVtbl->Start(pThis); } static MA_INLINE HRESULT ma_IAudioClient_Stop(ma_IAudioClient* pThis) { return pThis->lpVtbl->Stop(pThis); } static MA_INLINE HRESULT ma_IAudioClient_Reset(ma_IAudioClient* pThis) { return pThis->lpVtbl->Reset(pThis); } static MA_INLINE HRESULT ma_IAudioClient_SetEventHandle(ma_IAudioClient* pThis, HANDLE eventHandle) { return pThis->lpVtbl->SetEventHandle(pThis, eventHandle); } static MA_INLINE HRESULT ma_IAudioClient_GetService(ma_IAudioClient* pThis, const IID* const riid, void** pp) { return pThis->lpVtbl->GetService(pThis, riid, pp); } /* IAudioClient2 */ typedef struct { /* IUnknown */ HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IAudioClient2* pThis, const IID* const riid, void** ppObject); ULONG (STDMETHODCALLTYPE * AddRef) (ma_IAudioClient2* pThis); ULONG (STDMETHODCALLTYPE * Release) (ma_IAudioClient2* pThis); /* IAudioClient */ HRESULT (STDMETHODCALLTYPE * Initialize) (ma_IAudioClient2* pThis, MA_AUDCLNT_SHAREMODE shareMode, DWORD streamFlags, MA_REFERENCE_TIME bufferDuration, MA_REFERENCE_TIME periodicity, const WAVEFORMATEX* pFormat, const GUID* pAudioSessionGuid); HRESULT (STDMETHODCALLTYPE * GetBufferSize) (ma_IAudioClient2* pThis, ma_uint32* pNumBufferFrames); HRESULT (STDMETHODCALLTYPE * GetStreamLatency) (ma_IAudioClient2* pThis, MA_REFERENCE_TIME* pLatency); HRESULT (STDMETHODCALLTYPE * GetCurrentPadding)(ma_IAudioClient2* pThis, ma_uint32* pNumPaddingFrames); HRESULT (STDMETHODCALLTYPE * IsFormatSupported)(ma_IAudioClient2* pThis, MA_AUDCLNT_SHAREMODE shareMode, const WAVEFORMATEX* pFormat, WAVEFORMATEX** ppClosestMatch); HRESULT (STDMETHODCALLTYPE * GetMixFormat) (ma_IAudioClient2* pThis, WAVEFORMATEX** ppDeviceFormat); HRESULT (STDMETHODCALLTYPE * GetDevicePeriod) (ma_IAudioClient2* pThis, MA_REFERENCE_TIME* pDefaultDevicePeriod, MA_REFERENCE_TIME* pMinimumDevicePeriod); HRESULT (STDMETHODCALLTYPE * Start) (ma_IAudioClient2* pThis); HRESULT (STDMETHODCALLTYPE * Stop) (ma_IAudioClient2* pThis); HRESULT (STDMETHODCALLTYPE * Reset) (ma_IAudioClient2* pThis); HRESULT (STDMETHODCALLTYPE * SetEventHandle) (ma_IAudioClient2* pThis, HANDLE eventHandle); HRESULT (STDMETHODCALLTYPE * GetService) (ma_IAudioClient2* pThis, const IID* const riid, void** pp); /* IAudioClient2 */ HRESULT (STDMETHODCALLTYPE * IsOffloadCapable) (ma_IAudioClient2* pThis, MA_AUDIO_STREAM_CATEGORY category, BOOL* pOffloadCapable); HRESULT (STDMETHODCALLTYPE * SetClientProperties)(ma_IAudioClient2* pThis, const ma_AudioClientProperties* pProperties); HRESULT (STDMETHODCALLTYPE * GetBufferSizeLimits)(ma_IAudioClient2* pThis, const WAVEFORMATEX* pFormat, BOOL eventDriven, MA_REFERENCE_TIME* pMinBufferDuration, MA_REFERENCE_TIME* pMaxBufferDuration); } ma_IAudioClient2Vtbl; struct ma_IAudioClient2 { ma_IAudioClient2Vtbl* lpVtbl; }; static MA_INLINE HRESULT ma_IAudioClient2_QueryInterface(ma_IAudioClient2* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } static MA_INLINE ULONG ma_IAudioClient2_AddRef(ma_IAudioClient2* pThis) { return pThis->lpVtbl->AddRef(pThis); } static MA_INLINE ULONG ma_IAudioClient2_Release(ma_IAudioClient2* pThis) { return pThis->lpVtbl->Release(pThis); } static MA_INLINE HRESULT ma_IAudioClient2_Initialize(ma_IAudioClient2* pThis, MA_AUDCLNT_SHAREMODE shareMode, DWORD streamFlags, MA_REFERENCE_TIME bufferDuration, MA_REFERENCE_TIME periodicity, const WAVEFORMATEX* pFormat, const GUID* pAudioSessionGuid) { return pThis->lpVtbl->Initialize(pThis, shareMode, streamFlags, bufferDuration, periodicity, pFormat, pAudioSessionGuid); } static MA_INLINE HRESULT ma_IAudioClient2_GetBufferSize(ma_IAudioClient2* pThis, ma_uint32* pNumBufferFrames) { return pThis->lpVtbl->GetBufferSize(pThis, pNumBufferFrames); } static MA_INLINE HRESULT ma_IAudioClient2_GetStreamLatency(ma_IAudioClient2* pThis, MA_REFERENCE_TIME* pLatency) { return pThis->lpVtbl->GetStreamLatency(pThis, pLatency); } static MA_INLINE HRESULT ma_IAudioClient2_GetCurrentPadding(ma_IAudioClient2* pThis, ma_uint32* pNumPaddingFrames) { return pThis->lpVtbl->GetCurrentPadding(pThis, pNumPaddingFrames); } static MA_INLINE HRESULT ma_IAudioClient2_IsFormatSupported(ma_IAudioClient2* pThis, MA_AUDCLNT_SHAREMODE shareMode, const WAVEFORMATEX* pFormat, WAVEFORMATEX** ppClosestMatch) { return pThis->lpVtbl->IsFormatSupported(pThis, shareMode, pFormat, ppClosestMatch); } static MA_INLINE HRESULT ma_IAudioClient2_GetMixFormat(ma_IAudioClient2* pThis, WAVEFORMATEX** ppDeviceFormat) { return pThis->lpVtbl->GetMixFormat(pThis, ppDeviceFormat); } static MA_INLINE HRESULT ma_IAudioClient2_GetDevicePeriod(ma_IAudioClient2* pThis, MA_REFERENCE_TIME* pDefaultDevicePeriod, MA_REFERENCE_TIME* pMinimumDevicePeriod) { return pThis->lpVtbl->GetDevicePeriod(pThis, pDefaultDevicePeriod, pMinimumDevicePeriod); } static MA_INLINE HRESULT ma_IAudioClient2_Start(ma_IAudioClient2* pThis) { return pThis->lpVtbl->Start(pThis); } static MA_INLINE HRESULT ma_IAudioClient2_Stop(ma_IAudioClient2* pThis) { return pThis->lpVtbl->Stop(pThis); } static MA_INLINE HRESULT ma_IAudioClient2_Reset(ma_IAudioClient2* pThis) { return pThis->lpVtbl->Reset(pThis); } static MA_INLINE HRESULT ma_IAudioClient2_SetEventHandle(ma_IAudioClient2* pThis, HANDLE eventHandle) { return pThis->lpVtbl->SetEventHandle(pThis, eventHandle); } static MA_INLINE HRESULT ma_IAudioClient2_GetService(ma_IAudioClient2* pThis, const IID* const riid, void** pp) { return pThis->lpVtbl->GetService(pThis, riid, pp); } static MA_INLINE HRESULT ma_IAudioClient2_IsOffloadCapable(ma_IAudioClient2* pThis, MA_AUDIO_STREAM_CATEGORY category, BOOL* pOffloadCapable) { return pThis->lpVtbl->IsOffloadCapable(pThis, category, pOffloadCapable); } static MA_INLINE HRESULT ma_IAudioClient2_SetClientProperties(ma_IAudioClient2* pThis, const ma_AudioClientProperties* pProperties) { return pThis->lpVtbl->SetClientProperties(pThis, pProperties); } static MA_INLINE HRESULT ma_IAudioClient2_GetBufferSizeLimits(ma_IAudioClient2* pThis, const WAVEFORMATEX* pFormat, BOOL eventDriven, MA_REFERENCE_TIME* pMinBufferDuration, MA_REFERENCE_TIME* pMaxBufferDuration) { return pThis->lpVtbl->GetBufferSizeLimits(pThis, pFormat, eventDriven, pMinBufferDuration, pMaxBufferDuration); } /* IAudioClient3 */ typedef struct { /* IUnknown */ HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IAudioClient3* pThis, const IID* const riid, void** ppObject); ULONG (STDMETHODCALLTYPE * AddRef) (ma_IAudioClient3* pThis); ULONG (STDMETHODCALLTYPE * Release) (ma_IAudioClient3* pThis); /* IAudioClient */ HRESULT (STDMETHODCALLTYPE * Initialize) (ma_IAudioClient3* pThis, MA_AUDCLNT_SHAREMODE shareMode, DWORD streamFlags, MA_REFERENCE_TIME bufferDuration, MA_REFERENCE_TIME periodicity, const WAVEFORMATEX* pFormat, const GUID* pAudioSessionGuid); HRESULT (STDMETHODCALLTYPE * GetBufferSize) (ma_IAudioClient3* pThis, ma_uint32* pNumBufferFrames); HRESULT (STDMETHODCALLTYPE * GetStreamLatency) (ma_IAudioClient3* pThis, MA_REFERENCE_TIME* pLatency); HRESULT (STDMETHODCALLTYPE * GetCurrentPadding)(ma_IAudioClient3* pThis, ma_uint32* pNumPaddingFrames); HRESULT (STDMETHODCALLTYPE * IsFormatSupported)(ma_IAudioClient3* pThis, MA_AUDCLNT_SHAREMODE shareMode, const WAVEFORMATEX* pFormat, WAVEFORMATEX** ppClosestMatch); HRESULT (STDMETHODCALLTYPE * GetMixFormat) (ma_IAudioClient3* pThis, WAVEFORMATEX** ppDeviceFormat); HRESULT (STDMETHODCALLTYPE * GetDevicePeriod) (ma_IAudioClient3* pThis, MA_REFERENCE_TIME* pDefaultDevicePeriod, MA_REFERENCE_TIME* pMinimumDevicePeriod); HRESULT (STDMETHODCALLTYPE * Start) (ma_IAudioClient3* pThis); HRESULT (STDMETHODCALLTYPE * Stop) (ma_IAudioClient3* pThis); HRESULT (STDMETHODCALLTYPE * Reset) (ma_IAudioClient3* pThis); HRESULT (STDMETHODCALLTYPE * SetEventHandle) (ma_IAudioClient3* pThis, HANDLE eventHandle); HRESULT (STDMETHODCALLTYPE * GetService) (ma_IAudioClient3* pThis, const IID* const riid, void** pp); /* IAudioClient2 */ HRESULT (STDMETHODCALLTYPE * IsOffloadCapable) (ma_IAudioClient3* pThis, MA_AUDIO_STREAM_CATEGORY category, BOOL* pOffloadCapable); HRESULT (STDMETHODCALLTYPE * SetClientProperties)(ma_IAudioClient3* pThis, const ma_AudioClientProperties* pProperties); HRESULT (STDMETHODCALLTYPE * GetBufferSizeLimits)(ma_IAudioClient3* pThis, const WAVEFORMATEX* pFormat, BOOL eventDriven, MA_REFERENCE_TIME* pMinBufferDuration, MA_REFERENCE_TIME* pMaxBufferDuration); /* IAudioClient3 */ HRESULT (STDMETHODCALLTYPE * GetSharedModeEnginePeriod) (ma_IAudioClient3* pThis, const WAVEFORMATEX* pFormat, ma_uint32* pDefaultPeriodInFrames, ma_uint32* pFundamentalPeriodInFrames, ma_uint32* pMinPeriodInFrames, ma_uint32* pMaxPeriodInFrames); HRESULT (STDMETHODCALLTYPE * GetCurrentSharedModeEnginePeriod)(ma_IAudioClient3* pThis, WAVEFORMATEX** ppFormat, ma_uint32* pCurrentPeriodInFrames); HRESULT (STDMETHODCALLTYPE * InitializeSharedAudioStream) (ma_IAudioClient3* pThis, DWORD streamFlags, ma_uint32 periodInFrames, const WAVEFORMATEX* pFormat, const GUID* pAudioSessionGuid); } ma_IAudioClient3Vtbl; struct ma_IAudioClient3 { ma_IAudioClient3Vtbl* lpVtbl; }; static MA_INLINE HRESULT ma_IAudioClient3_QueryInterface(ma_IAudioClient3* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } static MA_INLINE ULONG ma_IAudioClient3_AddRef(ma_IAudioClient3* pThis) { return pThis->lpVtbl->AddRef(pThis); } static MA_INLINE ULONG ma_IAudioClient3_Release(ma_IAudioClient3* pThis) { return pThis->lpVtbl->Release(pThis); } static MA_INLINE HRESULT ma_IAudioClient3_Initialize(ma_IAudioClient3* pThis, MA_AUDCLNT_SHAREMODE shareMode, DWORD streamFlags, MA_REFERENCE_TIME bufferDuration, MA_REFERENCE_TIME periodicity, const WAVEFORMATEX* pFormat, const GUID* pAudioSessionGuid) { return pThis->lpVtbl->Initialize(pThis, shareMode, streamFlags, bufferDuration, periodicity, pFormat, pAudioSessionGuid); } static MA_INLINE HRESULT ma_IAudioClient3_GetBufferSize(ma_IAudioClient3* pThis, ma_uint32* pNumBufferFrames) { return pThis->lpVtbl->GetBufferSize(pThis, pNumBufferFrames); } static MA_INLINE HRESULT ma_IAudioClient3_GetStreamLatency(ma_IAudioClient3* pThis, MA_REFERENCE_TIME* pLatency) { return pThis->lpVtbl->GetStreamLatency(pThis, pLatency); } static MA_INLINE HRESULT ma_IAudioClient3_GetCurrentPadding(ma_IAudioClient3* pThis, ma_uint32* pNumPaddingFrames) { return pThis->lpVtbl->GetCurrentPadding(pThis, pNumPaddingFrames); } static MA_INLINE HRESULT ma_IAudioClient3_IsFormatSupported(ma_IAudioClient3* pThis, MA_AUDCLNT_SHAREMODE shareMode, const WAVEFORMATEX* pFormat, WAVEFORMATEX** ppClosestMatch) { return pThis->lpVtbl->IsFormatSupported(pThis, shareMode, pFormat, ppClosestMatch); } static MA_INLINE HRESULT ma_IAudioClient3_GetMixFormat(ma_IAudioClient3* pThis, WAVEFORMATEX** ppDeviceFormat) { return pThis->lpVtbl->GetMixFormat(pThis, ppDeviceFormat); } static MA_INLINE HRESULT ma_IAudioClient3_GetDevicePeriod(ma_IAudioClient3* pThis, MA_REFERENCE_TIME* pDefaultDevicePeriod, MA_REFERENCE_TIME* pMinimumDevicePeriod) { return pThis->lpVtbl->GetDevicePeriod(pThis, pDefaultDevicePeriod, pMinimumDevicePeriod); } static MA_INLINE HRESULT ma_IAudioClient3_Start(ma_IAudioClient3* pThis) { return pThis->lpVtbl->Start(pThis); } static MA_INLINE HRESULT ma_IAudioClient3_Stop(ma_IAudioClient3* pThis) { return pThis->lpVtbl->Stop(pThis); } static MA_INLINE HRESULT ma_IAudioClient3_Reset(ma_IAudioClient3* pThis) { return pThis->lpVtbl->Reset(pThis); } static MA_INLINE HRESULT ma_IAudioClient3_SetEventHandle(ma_IAudioClient3* pThis, HANDLE eventHandle) { return pThis->lpVtbl->SetEventHandle(pThis, eventHandle); } static MA_INLINE HRESULT ma_IAudioClient3_GetService(ma_IAudioClient3* pThis, const IID* const riid, void** pp) { return pThis->lpVtbl->GetService(pThis, riid, pp); } static MA_INLINE HRESULT ma_IAudioClient3_IsOffloadCapable(ma_IAudioClient3* pThis, MA_AUDIO_STREAM_CATEGORY category, BOOL* pOffloadCapable) { return pThis->lpVtbl->IsOffloadCapable(pThis, category, pOffloadCapable); } static MA_INLINE HRESULT ma_IAudioClient3_SetClientProperties(ma_IAudioClient3* pThis, const ma_AudioClientProperties* pProperties) { return pThis->lpVtbl->SetClientProperties(pThis, pProperties); } static MA_INLINE HRESULT ma_IAudioClient3_GetBufferSizeLimits(ma_IAudioClient3* pThis, const WAVEFORMATEX* pFormat, BOOL eventDriven, MA_REFERENCE_TIME* pMinBufferDuration, MA_REFERENCE_TIME* pMaxBufferDuration) { return pThis->lpVtbl->GetBufferSizeLimits(pThis, pFormat, eventDriven, pMinBufferDuration, pMaxBufferDuration); } static MA_INLINE HRESULT ma_IAudioClient3_GetSharedModeEnginePeriod(ma_IAudioClient3* pThis, const WAVEFORMATEX* pFormat, ma_uint32* pDefaultPeriodInFrames, ma_uint32* pFundamentalPeriodInFrames, ma_uint32* pMinPeriodInFrames, ma_uint32* pMaxPeriodInFrames) { return pThis->lpVtbl->GetSharedModeEnginePeriod(pThis, pFormat, pDefaultPeriodInFrames, pFundamentalPeriodInFrames, pMinPeriodInFrames, pMaxPeriodInFrames); } static MA_INLINE HRESULT ma_IAudioClient3_GetCurrentSharedModeEnginePeriod(ma_IAudioClient3* pThis, WAVEFORMATEX** ppFormat, ma_uint32* pCurrentPeriodInFrames) { return pThis->lpVtbl->GetCurrentSharedModeEnginePeriod(pThis, ppFormat, pCurrentPeriodInFrames); } static MA_INLINE HRESULT ma_IAudioClient3_InitializeSharedAudioStream(ma_IAudioClient3* pThis, DWORD streamFlags, ma_uint32 periodInFrames, const WAVEFORMATEX* pFormat, const GUID* pAudioSessionGUID) { return pThis->lpVtbl->InitializeSharedAudioStream(pThis, streamFlags, periodInFrames, pFormat, pAudioSessionGUID); } /* IAudioRenderClient */ typedef struct { /* IUnknown */ HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IAudioRenderClient* pThis, const IID* const riid, void** ppObject); ULONG (STDMETHODCALLTYPE * AddRef) (ma_IAudioRenderClient* pThis); ULONG (STDMETHODCALLTYPE * Release) (ma_IAudioRenderClient* pThis); /* IAudioRenderClient */ HRESULT (STDMETHODCALLTYPE * GetBuffer) (ma_IAudioRenderClient* pThis, ma_uint32 numFramesRequested, BYTE** ppData); HRESULT (STDMETHODCALLTYPE * ReleaseBuffer)(ma_IAudioRenderClient* pThis, ma_uint32 numFramesWritten, DWORD dwFlags); } ma_IAudioRenderClientVtbl; struct ma_IAudioRenderClient { ma_IAudioRenderClientVtbl* lpVtbl; }; static MA_INLINE HRESULT ma_IAudioRenderClient_QueryInterface(ma_IAudioRenderClient* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } static MA_INLINE ULONG ma_IAudioRenderClient_AddRef(ma_IAudioRenderClient* pThis) { return pThis->lpVtbl->AddRef(pThis); } static MA_INLINE ULONG ma_IAudioRenderClient_Release(ma_IAudioRenderClient* pThis) { return pThis->lpVtbl->Release(pThis); } static MA_INLINE HRESULT ma_IAudioRenderClient_GetBuffer(ma_IAudioRenderClient* pThis, ma_uint32 numFramesRequested, BYTE** ppData) { return pThis->lpVtbl->GetBuffer(pThis, numFramesRequested, ppData); } static MA_INLINE HRESULT ma_IAudioRenderClient_ReleaseBuffer(ma_IAudioRenderClient* pThis, ma_uint32 numFramesWritten, DWORD dwFlags) { return pThis->lpVtbl->ReleaseBuffer(pThis, numFramesWritten, dwFlags); } /* IAudioCaptureClient */ typedef struct { /* IUnknown */ HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IAudioCaptureClient* pThis, const IID* const riid, void** ppObject); ULONG (STDMETHODCALLTYPE * AddRef) (ma_IAudioCaptureClient* pThis); ULONG (STDMETHODCALLTYPE * Release) (ma_IAudioCaptureClient* pThis); /* IAudioRenderClient */ HRESULT (STDMETHODCALLTYPE * GetBuffer) (ma_IAudioCaptureClient* pThis, BYTE** ppData, ma_uint32* pNumFramesToRead, DWORD* pFlags, ma_uint64* pDevicePosition, ma_uint64* pQPCPosition); HRESULT (STDMETHODCALLTYPE * ReleaseBuffer) (ma_IAudioCaptureClient* pThis, ma_uint32 numFramesRead); HRESULT (STDMETHODCALLTYPE * GetNextPacketSize)(ma_IAudioCaptureClient* pThis, ma_uint32* pNumFramesInNextPacket); } ma_IAudioCaptureClientVtbl; struct ma_IAudioCaptureClient { ma_IAudioCaptureClientVtbl* lpVtbl; }; static MA_INLINE HRESULT ma_IAudioCaptureClient_QueryInterface(ma_IAudioCaptureClient* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } static MA_INLINE ULONG ma_IAudioCaptureClient_AddRef(ma_IAudioCaptureClient* pThis) { return pThis->lpVtbl->AddRef(pThis); } static MA_INLINE ULONG ma_IAudioCaptureClient_Release(ma_IAudioCaptureClient* pThis) { return pThis->lpVtbl->Release(pThis); } static MA_INLINE HRESULT ma_IAudioCaptureClient_GetBuffer(ma_IAudioCaptureClient* pThis, BYTE** ppData, ma_uint32* pNumFramesToRead, DWORD* pFlags, ma_uint64* pDevicePosition, ma_uint64* pQPCPosition) { return pThis->lpVtbl->GetBuffer(pThis, ppData, pNumFramesToRead, pFlags, pDevicePosition, pQPCPosition); } static MA_INLINE HRESULT ma_IAudioCaptureClient_ReleaseBuffer(ma_IAudioCaptureClient* pThis, ma_uint32 numFramesRead) { return pThis->lpVtbl->ReleaseBuffer(pThis, numFramesRead); } static MA_INLINE HRESULT ma_IAudioCaptureClient_GetNextPacketSize(ma_IAudioCaptureClient* pThis, ma_uint32* pNumFramesInNextPacket) { return pThis->lpVtbl->GetNextPacketSize(pThis, pNumFramesInNextPacket); } #if defined(MA_WIN32_UWP) /* mmdevapi Functions */ typedef HRESULT (WINAPI * MA_PFN_ActivateAudioInterfaceAsync)(LPCWSTR deviceInterfacePath, const IID* riid, PROPVARIANT *activationParams, ma_IActivateAudioInterfaceCompletionHandler *completionHandler, ma_IActivateAudioInterfaceAsyncOperation **activationOperation); #endif /* Avrt Functions */ typedef HANDLE (WINAPI * MA_PFN_AvSetMmThreadCharacteristicsW)(LPCWSTR TaskName, LPDWORD TaskIndex); typedef BOOL (WINAPI * MA_PFN_AvRevertMmThreadCharacteristics)(HANDLE AvrtHandle); #if !defined(MA_WIN32_DESKTOP) && !defined(MA_WIN32_GDK) typedef struct ma_completion_handler_uwp ma_completion_handler_uwp; typedef struct { /* IUnknown */ HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_completion_handler_uwp* pThis, const IID* const riid, void** ppObject); ULONG (STDMETHODCALLTYPE * AddRef) (ma_completion_handler_uwp* pThis); ULONG (STDMETHODCALLTYPE * Release) (ma_completion_handler_uwp* pThis); /* IActivateAudioInterfaceCompletionHandler */ HRESULT (STDMETHODCALLTYPE * ActivateCompleted)(ma_completion_handler_uwp* pThis, ma_IActivateAudioInterfaceAsyncOperation* pActivateOperation); } ma_completion_handler_uwp_vtbl; struct ma_completion_handler_uwp { ma_completion_handler_uwp_vtbl* lpVtbl; MA_ATOMIC(4, ma_uint32) counter; HANDLE hEvent; }; static HRESULT STDMETHODCALLTYPE ma_completion_handler_uwp_QueryInterface(ma_completion_handler_uwp* pThis, const IID* const riid, void** ppObject) { /* We need to "implement" IAgileObject which is just an indicator that's used internally by WASAPI for some multithreading management. To "implement" this, we just make sure we return pThis when the IAgileObject is requested. */ if (!ma_is_guid_equal(riid, &MA_IID_IUnknown) && !ma_is_guid_equal(riid, &MA_IID_IActivateAudioInterfaceCompletionHandler) && !ma_is_guid_equal(riid, &MA_IID_IAgileObject)) { *ppObject = NULL; return E_NOINTERFACE; } /* Getting here means the IID is IUnknown or IMMNotificationClient. */ *ppObject = (void*)pThis; ((ma_completion_handler_uwp_vtbl*)pThis->lpVtbl)->AddRef(pThis); return S_OK; } static ULONG STDMETHODCALLTYPE ma_completion_handler_uwp_AddRef(ma_completion_handler_uwp* pThis) { return (ULONG)c89atomic_fetch_add_32(&pThis->counter, 1) + 1; } static ULONG STDMETHODCALLTYPE ma_completion_handler_uwp_Release(ma_completion_handler_uwp* pThis) { ma_uint32 newRefCount = c89atomic_fetch_sub_32(&pThis->counter, 1) - 1; if (newRefCount == 0) { return 0; /* We don't free anything here because we never allocate the object on the heap. */ } return (ULONG)newRefCount; } static HRESULT STDMETHODCALLTYPE ma_completion_handler_uwp_ActivateCompleted(ma_completion_handler_uwp* pThis, ma_IActivateAudioInterfaceAsyncOperation* pActivateOperation) { (void)pActivateOperation; SetEvent(pThis->hEvent); return S_OK; } static ma_completion_handler_uwp_vtbl g_maCompletionHandlerVtblInstance = { ma_completion_handler_uwp_QueryInterface, ma_completion_handler_uwp_AddRef, ma_completion_handler_uwp_Release, ma_completion_handler_uwp_ActivateCompleted }; static ma_result ma_completion_handler_uwp_init(ma_completion_handler_uwp* pHandler) { MA_ASSERT(pHandler != NULL); MA_ZERO_OBJECT(pHandler); pHandler->lpVtbl = &g_maCompletionHandlerVtblInstance; pHandler->counter = 1; pHandler->hEvent = CreateEventW(NULL, FALSE, FALSE, NULL); if (pHandler->hEvent == NULL) { return ma_result_from_GetLastError(GetLastError()); } return MA_SUCCESS; } static void ma_completion_handler_uwp_uninit(ma_completion_handler_uwp* pHandler) { if (pHandler->hEvent != NULL) { CloseHandle(pHandler->hEvent); } } static void ma_completion_handler_uwp_wait(ma_completion_handler_uwp* pHandler) { WaitForSingleObject(pHandler->hEvent, INFINITE); } #endif /* !MA_WIN32_DESKTOP */ /* We need a virtual table for our notification client object that's used for detecting changes to the default device. */ #if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) static HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_QueryInterface(ma_IMMNotificationClient* pThis, const IID* const riid, void** ppObject) { /* We care about two interfaces - IUnknown and IMMNotificationClient. If the requested IID is something else we just return E_NOINTERFACE. Otherwise we need to increment the reference counter and return S_OK. */ if (!ma_is_guid_equal(riid, &MA_IID_IUnknown) && !ma_is_guid_equal(riid, &MA_IID_IMMNotificationClient)) { *ppObject = NULL; return E_NOINTERFACE; } /* Getting here means the IID is IUnknown or IMMNotificationClient. */ *ppObject = (void*)pThis; ((ma_IMMNotificationClientVtbl*)pThis->lpVtbl)->AddRef(pThis); return S_OK; } static ULONG STDMETHODCALLTYPE ma_IMMNotificationClient_AddRef(ma_IMMNotificationClient* pThis) { return (ULONG)c89atomic_fetch_add_32(&pThis->counter, 1) + 1; } static ULONG STDMETHODCALLTYPE ma_IMMNotificationClient_Release(ma_IMMNotificationClient* pThis) { ma_uint32 newRefCount = c89atomic_fetch_sub_32(&pThis->counter, 1) - 1; if (newRefCount == 0) { return 0; /* We don't free anything here because we never allocate the object on the heap. */ } return (ULONG)newRefCount; } static HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_OnDeviceStateChanged(ma_IMMNotificationClient* pThis, LPCWSTR pDeviceID, DWORD dwNewState) { ma_bool32 isThisDevice = MA_FALSE; ma_bool32 isCapture = MA_FALSE; ma_bool32 isPlayback = MA_FALSE; #ifdef MA_DEBUG_OUTPUT /*ma_log_postf(ma_device_get_log(pThis->pDevice), MA_LOG_LEVEL_DEBUG, "IMMNotificationClient_OnDeviceStateChanged(pDeviceID=%S, dwNewState=%u)\n", (pDeviceID != NULL) ? pDeviceID : L"(NULL)", (unsigned int)dwNewState);*/ #endif /* There have been reports of a hang when a playback device is disconnected. The idea with this code is to explicitly stop the device if we detect that the device is disabled or has been unplugged. */ if (pThis->pDevice->wasapi.allowCaptureAutoStreamRouting && (pThis->pDevice->type == ma_device_type_capture || pThis->pDevice->type == ma_device_type_duplex || pThis->pDevice->type == ma_device_type_loopback)) { isCapture = MA_TRUE; if (wcscmp(pThis->pDevice->capture.id.wasapi, pDeviceID) == 0) { isThisDevice = MA_TRUE; } } if (pThis->pDevice->wasapi.allowPlaybackAutoStreamRouting && (pThis->pDevice->type == ma_device_type_playback || pThis->pDevice->type == ma_device_type_duplex)) { isPlayback = MA_TRUE; if (wcscmp(pThis->pDevice->playback.id.wasapi, pDeviceID) == 0) { isThisDevice = MA_TRUE; } } /* If the device ID matches our device we need to mark our device as detached and stop it. When a device is added in OnDeviceAdded(), we'll restart it. We only mark it as detached if the device was started at the time of being removed. */ if (isThisDevice) { if ((dwNewState & MA_MM_DEVICE_STATE_ACTIVE) == 0) { /* Unplugged or otherwise unavailable. Mark as detached if we were in a playing state. We'll use this to determine whether or not we need to automatically start the device when it's plugged back in again. */ if (ma_device_get_state(pThis->pDevice) == ma_device_state_started) { if (isPlayback) { pThis->pDevice->wasapi.isDetachedPlayback = MA_TRUE; } if (isCapture) { pThis->pDevice->wasapi.isDetachedCapture = MA_TRUE; } ma_device_stop(pThis->pDevice); } } if ((dwNewState & MA_MM_DEVICE_STATE_ACTIVE) != 0) { /* The device was activated. If we were detached, we need to start it again. */ ma_bool8 tryRestartingDevice = MA_FALSE; if (isPlayback) { if (pThis->pDevice->wasapi.isDetachedPlayback) { pThis->pDevice->wasapi.isDetachedPlayback = MA_FALSE; ma_device_reroute__wasapi(pThis->pDevice, ma_device_type_playback); tryRestartingDevice = MA_TRUE; } } if (isCapture) { if (pThis->pDevice->wasapi.isDetachedCapture) { pThis->pDevice->wasapi.isDetachedCapture = MA_FALSE; ma_device_reroute__wasapi(pThis->pDevice, (pThis->pDevice->type == ma_device_type_loopback) ? ma_device_type_loopback : ma_device_type_capture); tryRestartingDevice = MA_TRUE; } } if (tryRestartingDevice) { if (pThis->pDevice->wasapi.isDetachedPlayback == MA_FALSE && pThis->pDevice->wasapi.isDetachedCapture == MA_FALSE) { ma_device_start(pThis->pDevice); } } } } return S_OK; } static HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_OnDeviceAdded(ma_IMMNotificationClient* pThis, LPCWSTR pDeviceID) { #ifdef MA_DEBUG_OUTPUT /*ma_log_postf(ma_device_get_log(pThis->pDevice), MA_LOG_LEVEL_DEBUG, "IMMNotificationClient_OnDeviceAdded(pDeviceID=%S)\n", (pDeviceID != NULL) ? pDeviceID : L"(NULL)");*/ #endif /* We don't need to worry about this event for our purposes. */ (void)pThis; (void)pDeviceID; return S_OK; } static HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_OnDeviceRemoved(ma_IMMNotificationClient* pThis, LPCWSTR pDeviceID) { #ifdef MA_DEBUG_OUTPUT /*ma_log_postf(ma_device_get_log(pThis->pDevice), MA_LOG_LEVEL_DEBUG, "IMMNotificationClient_OnDeviceRemoved(pDeviceID=%S)\n", (pDeviceID != NULL) ? pDeviceID : L"(NULL)");*/ #endif /* We don't need to worry about this event for our purposes. */ (void)pThis; (void)pDeviceID; return S_OK; } static HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_OnDefaultDeviceChanged(ma_IMMNotificationClient* pThis, ma_EDataFlow dataFlow, ma_ERole role, LPCWSTR pDefaultDeviceID) { #ifdef MA_DEBUG_OUTPUT /*ma_log_postf(ma_device_get_log(pThis->pDevice), MA_LOG_LEVEL_DEBUG, "IMMNotificationClient_OnDefaultDeviceChanged(dataFlow=%d, role=%d, pDefaultDeviceID=%S)\n", dataFlow, role, (pDefaultDeviceID != NULL) ? pDefaultDeviceID : L"(NULL)");*/ #endif /* We only ever use the eConsole role in miniaudio. */ if (role != ma_eConsole) { ma_log_postf(ma_device_get_log(pThis->pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Stream rerouting: role != eConsole\n"); return S_OK; } /* We only care about devices with the same data flow and role as the current device. */ if ((pThis->pDevice->type == ma_device_type_playback && dataFlow != ma_eRender) || (pThis->pDevice->type == ma_device_type_capture && dataFlow != ma_eCapture)) { ma_log_postf(ma_device_get_log(pThis->pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Stream rerouting abandoned because dataFlow does match device type.\n"); return S_OK; } /* Don't do automatic stream routing if we're not allowed. */ if ((dataFlow == ma_eRender && pThis->pDevice->wasapi.allowPlaybackAutoStreamRouting == MA_FALSE) || (dataFlow == ma_eCapture && pThis->pDevice->wasapi.allowCaptureAutoStreamRouting == MA_FALSE)) { ma_log_postf(ma_device_get_log(pThis->pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Stream rerouting abandoned because automatic stream routing has been disabled by the device config.\n"); return S_OK; } /* Not currently supporting automatic stream routing in exclusive mode. This is not working correctly on my machine due to AUDCLNT_E_DEVICE_IN_USE errors when reinitializing the device. If this is a bug in miniaudio, we can try re-enabling this once it's fixed. */ if ((dataFlow == ma_eRender && pThis->pDevice->playback.shareMode == ma_share_mode_exclusive) || (dataFlow == ma_eCapture && pThis->pDevice->capture.shareMode == ma_share_mode_exclusive)) { ma_log_postf(ma_device_get_log(pThis->pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Stream rerouting abandoned because the device shared mode is exclusive.\n"); return S_OK; } /* Second attempt at device rerouting. We're going to retrieve the device's state at the time of the route change. We're then going to stop the device, reinitialize the device, and then start it again if the state before stopping was ma_device_state_started. */ { ma_uint32 previousState = ma_device_get_state(pThis->pDevice); ma_bool8 restartDevice = MA_FALSE; if (previousState == ma_device_state_started) { ma_device_stop(pThis->pDevice); restartDevice = MA_TRUE; } if (pDefaultDeviceID != NULL) { /* <-- The input device ID will be null if there's no other device available. */ if (dataFlow == ma_eRender) { ma_device_reroute__wasapi(pThis->pDevice, ma_device_type_playback); if (pThis->pDevice->wasapi.isDetachedPlayback) { pThis->pDevice->wasapi.isDetachedPlayback = MA_FALSE; if (pThis->pDevice->type == ma_device_type_duplex && pThis->pDevice->wasapi.isDetachedCapture) { restartDevice = MA_FALSE; /* It's a duplex device and the capture side is detached. We cannot be restarting the device just yet. */ } else { restartDevice = MA_TRUE; /* It's not a duplex device, or the capture side is also attached so we can go ahead and restart the device. */ } } } else { ma_device_reroute__wasapi(pThis->pDevice, (pThis->pDevice->type == ma_device_type_loopback) ? ma_device_type_loopback : ma_device_type_capture); if (pThis->pDevice->wasapi.isDetachedCapture) { pThis->pDevice->wasapi.isDetachedCapture = MA_FALSE; if (pThis->pDevice->type == ma_device_type_duplex && pThis->pDevice->wasapi.isDetachedPlayback) { restartDevice = MA_FALSE; /* It's a duplex device and the playback side is detached. We cannot be restarting the device just yet. */ } else { restartDevice = MA_TRUE; /* It's not a duplex device, or the playback side is also attached so we can go ahead and restart the device. */ } } } if (restartDevice) { ma_device_start(pThis->pDevice); } } } return S_OK; } static HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_OnPropertyValueChanged(ma_IMMNotificationClient* pThis, LPCWSTR pDeviceID, const PROPERTYKEY key) { #ifdef MA_DEBUG_OUTPUT /*ma_log_postf(ma_device_get_log(pThis->pDevice), MA_LOG_LEVEL_DEBUG, "IMMNotificationClient_OnPropertyValueChanged(pDeviceID=%S)\n", (pDeviceID != NULL) ? pDeviceID : L"(NULL)");*/ #endif (void)pThis; (void)pDeviceID; (void)key; return S_OK; } static ma_IMMNotificationClientVtbl g_maNotificationCientVtbl = { ma_IMMNotificationClient_QueryInterface, ma_IMMNotificationClient_AddRef, ma_IMMNotificationClient_Release, ma_IMMNotificationClient_OnDeviceStateChanged, ma_IMMNotificationClient_OnDeviceAdded, ma_IMMNotificationClient_OnDeviceRemoved, ma_IMMNotificationClient_OnDefaultDeviceChanged, ma_IMMNotificationClient_OnPropertyValueChanged }; #endif /* MA_WIN32_DESKTOP */ static LPCWSTR ma_to_usage_string__wasapi(ma_wasapi_usage usage) { switch (usage) { case ma_wasapi_usage_default: return NULL; case ma_wasapi_usage_games: return L"Games"; case ma_wasapi_usage_pro_audio: return L"Pro Audio"; default: break; } return NULL; } #if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) typedef ma_IMMDevice ma_WASAPIDeviceInterface; #else typedef ma_IUnknown ma_WASAPIDeviceInterface; #endif #define MA_CONTEXT_COMMAND_QUIT__WASAPI 1 #define MA_CONTEXT_COMMAND_CREATE_IAUDIOCLIENT__WASAPI 2 #define MA_CONTEXT_COMMAND_RELEASE_IAUDIOCLIENT__WASAPI 3 static ma_context_command__wasapi ma_context_init_command__wasapi(int code) { ma_context_command__wasapi cmd; MA_ZERO_OBJECT(&cmd); cmd.code = code; return cmd; } static ma_result ma_context_post_command__wasapi(ma_context* pContext, const ma_context_command__wasapi* pCmd) { /* For now we are doing everything synchronously, but I might relax this later if the need arises. */ ma_result result; ma_bool32 isUsingLocalEvent = MA_FALSE; ma_event localEvent; MA_ASSERT(pContext != NULL); MA_ASSERT(pCmd != NULL); if (pCmd->pEvent == NULL) { isUsingLocalEvent = MA_TRUE; result = ma_event_init(&localEvent); if (result != MA_SUCCESS) { return result; /* Failed to create the event for this command. */ } } /* Here is where we add the command to the list. If there's not enough room we'll spin until there is. */ ma_mutex_lock(&pContext->wasapi.commandLock); { ma_uint32 index; /* Spin until we've got some space available. */ while (pContext->wasapi.commandCount == ma_countof(pContext->wasapi.commands)) { ma_yield(); } /* Space is now available. Can safely add to the list. */ index = (pContext->wasapi.commandIndex + pContext->wasapi.commandCount) % ma_countof(pContext->wasapi.commands); pContext->wasapi.commands[index] = *pCmd; pContext->wasapi.commands[index].pEvent = &localEvent; pContext->wasapi.commandCount += 1; /* Now that the command has been added, release the semaphore so ma_context_next_command__wasapi() can return. */ ma_semaphore_release(&pContext->wasapi.commandSem); } ma_mutex_unlock(&pContext->wasapi.commandLock); if (isUsingLocalEvent) { ma_event_wait(&localEvent); ma_event_uninit(&localEvent); } return MA_SUCCESS; } static ma_result ma_context_next_command__wasapi(ma_context* pContext, ma_context_command__wasapi* pCmd) { ma_result result = MA_SUCCESS; MA_ASSERT(pContext != NULL); MA_ASSERT(pCmd != NULL); result = ma_semaphore_wait(&pContext->wasapi.commandSem); if (result == MA_SUCCESS) { ma_mutex_lock(&pContext->wasapi.commandLock); { *pCmd = pContext->wasapi.commands[pContext->wasapi.commandIndex]; pContext->wasapi.commandIndex = (pContext->wasapi.commandIndex + 1) % ma_countof(pContext->wasapi.commands); pContext->wasapi.commandCount -= 1; } ma_mutex_unlock(&pContext->wasapi.commandLock); } return result; } static ma_thread_result MA_THREADCALL ma_context_command_thread__wasapi(void* pUserData) { ma_result result; ma_context* pContext = (ma_context*)pUserData; MA_ASSERT(pContext != NULL); for (;;) { ma_context_command__wasapi cmd; result = ma_context_next_command__wasapi(pContext, &cmd); if (result != MA_SUCCESS) { break; } switch (cmd.code) { case MA_CONTEXT_COMMAND_QUIT__WASAPI: { /* Do nothing. Handled after the switch. */ } break; case MA_CONTEXT_COMMAND_CREATE_IAUDIOCLIENT__WASAPI: { if (cmd.data.createAudioClient.deviceType == ma_device_type_playback) { *cmd.data.createAudioClient.pResult = ma_result_from_HRESULT(ma_IAudioClient_GetService((ma_IAudioClient*)cmd.data.createAudioClient.pAudioClient, &MA_IID_IAudioRenderClient, cmd.data.createAudioClient.ppAudioClientService)); } else { *cmd.data.createAudioClient.pResult = ma_result_from_HRESULT(ma_IAudioClient_GetService((ma_IAudioClient*)cmd.data.createAudioClient.pAudioClient, &MA_IID_IAudioCaptureClient, cmd.data.createAudioClient.ppAudioClientService)); } } break; case MA_CONTEXT_COMMAND_RELEASE_IAUDIOCLIENT__WASAPI: { if (cmd.data.releaseAudioClient.deviceType == ma_device_type_playback) { if (cmd.data.releaseAudioClient.pDevice->wasapi.pAudioClientPlayback != NULL) { ma_IAudioClient_Release((ma_IAudioClient*)cmd.data.releaseAudioClient.pDevice->wasapi.pAudioClientPlayback); cmd.data.releaseAudioClient.pDevice->wasapi.pAudioClientPlayback = NULL; } } if (cmd.data.releaseAudioClient.deviceType == ma_device_type_capture) { if (cmd.data.releaseAudioClient.pDevice->wasapi.pAudioClientCapture != NULL) { ma_IAudioClient_Release((ma_IAudioClient*)cmd.data.releaseAudioClient.pDevice->wasapi.pAudioClientCapture); cmd.data.releaseAudioClient.pDevice->wasapi.pAudioClientCapture = NULL; } } } break; default: { /* Unknown command. Ignore it, but trigger an assert in debug mode so we're aware of it. */ MA_ASSERT(MA_FALSE); } break; } if (cmd.pEvent != NULL) { ma_event_signal(cmd.pEvent); } if (cmd.code == MA_CONTEXT_COMMAND_QUIT__WASAPI) { break; /* Received a quit message. Get out of here. */ } } return (ma_thread_result)0; } static ma_result ma_device_create_IAudioClient_service__wasapi(ma_context* pContext, ma_device_type deviceType, ma_IAudioClient* pAudioClient, void** ppAudioClientService) { ma_result result; ma_result cmdResult; ma_context_command__wasapi cmd = ma_context_init_command__wasapi(MA_CONTEXT_COMMAND_CREATE_IAUDIOCLIENT__WASAPI); cmd.data.createAudioClient.deviceType = deviceType; cmd.data.createAudioClient.pAudioClient = (void*)pAudioClient; cmd.data.createAudioClient.ppAudioClientService = ppAudioClientService; cmd.data.createAudioClient.pResult = &cmdResult; /* Declared locally, but won't be dereferenced after this function returns since execution of the command will wait here. */ result = ma_context_post_command__wasapi(pContext, &cmd); /* This will not return until the command has actually been run. */ if (result != MA_SUCCESS) { return result; } return *cmd.data.createAudioClient.pResult; } #if 0 /* Not used at the moment, but leaving here for future use. */ static ma_result ma_device_release_IAudioClient_service__wasapi(ma_device* pDevice, ma_device_type deviceType) { ma_result result; ma_context_command__wasapi cmd = ma_context_init_command__wasapi(MA_CONTEXT_COMMAND_RELEASE_IAUDIOCLIENT__WASAPI); cmd.data.releaseAudioClient.pDevice = pDevice; cmd.data.releaseAudioClient.deviceType = deviceType; result = ma_context_post_command__wasapi(pDevice->pContext, &cmd); /* This will not return until the command has actually been run. */ if (result != MA_SUCCESS) { return result; } return MA_SUCCESS; } #endif static void ma_add_native_data_format_to_device_info_from_WAVEFORMATEX(const WAVEFORMATEX* pWF, ma_share_mode shareMode, ma_device_info* pInfo) { MA_ASSERT(pWF != NULL); MA_ASSERT(pInfo != NULL); if (pInfo->nativeDataFormatCount >= ma_countof(pInfo->nativeDataFormats)) { return; /* Too many data formats. Need to ignore this one. Don't think this should ever happen with WASAPI. */ } pInfo->nativeDataFormats[pInfo->nativeDataFormatCount].format = ma_format_from_WAVEFORMATEX(pWF); pInfo->nativeDataFormats[pInfo->nativeDataFormatCount].channels = pWF->nChannels; pInfo->nativeDataFormats[pInfo->nativeDataFormatCount].sampleRate = pWF->nSamplesPerSec; pInfo->nativeDataFormats[pInfo->nativeDataFormatCount].flags = (shareMode == ma_share_mode_exclusive) ? MA_DATA_FORMAT_FLAG_EXCLUSIVE_MODE : 0; pInfo->nativeDataFormatCount += 1; } static ma_result ma_context_get_device_info_from_IAudioClient__wasapi(ma_context* pContext, /*ma_IMMDevice**/void* pMMDevice, ma_IAudioClient* pAudioClient, ma_device_info* pInfo) { HRESULT hr; WAVEFORMATEX* pWF = NULL; MA_ASSERT(pAudioClient != NULL); MA_ASSERT(pInfo != NULL); /* Shared Mode. We use GetMixFormat() here. */ hr = ma_IAudioClient_GetMixFormat((ma_IAudioClient*)pAudioClient, (WAVEFORMATEX**)&pWF); if (SUCCEEDED(hr)) { ma_add_native_data_format_to_device_info_from_WAVEFORMATEX(pWF, ma_share_mode_shared, pInfo); } else { ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to retrieve mix format for device info retrieval."); return ma_result_from_HRESULT(hr); } /* Exlcusive Mode. We repeatedly call IsFormatSupported() here. This is not currently supported on UWP. Failure to retrieve the exclusive mode format is not considered an error, so from here on out, MA_SUCCESS is guaranteed to be returned. */ #if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) { ma_IPropertyStore *pProperties; /* The first thing to do is get the format from PKEY_AudioEngine_DeviceFormat. This should give us a channel count we assume is correct which will simplify our searching. */ hr = ma_IMMDevice_OpenPropertyStore((ma_IMMDevice*)pMMDevice, STGM_READ, &pProperties); if (SUCCEEDED(hr)) { PROPVARIANT var; ma_PropVariantInit(&var); hr = ma_IPropertyStore_GetValue(pProperties, &MA_PKEY_AudioEngine_DeviceFormat, &var); if (SUCCEEDED(hr)) { pWF = (WAVEFORMATEX*)var.blob.pBlobData; /* In my testing, the format returned by PKEY_AudioEngine_DeviceFormat is suitable for exclusive mode so we check this format first. If this fails, fall back to a search. */ hr = ma_IAudioClient_IsFormatSupported((ma_IAudioClient*)pAudioClient, MA_AUDCLNT_SHAREMODE_EXCLUSIVE, pWF, NULL); if (SUCCEEDED(hr)) { /* The format returned by PKEY_AudioEngine_DeviceFormat is supported. */ ma_add_native_data_format_to_device_info_from_WAVEFORMATEX(pWF, ma_share_mode_exclusive, pInfo); } else { /* The format returned by PKEY_AudioEngine_DeviceFormat is not supported, so fall back to a search. We assume the channel count returned by MA_PKEY_AudioEngine_DeviceFormat is valid and correct. For simplicity we're only returning one format. */ ma_uint32 channels = pWF->nChannels; ma_channel defaultChannelMap[MA_MAX_CHANNELS]; WAVEFORMATEXTENSIBLE wf; ma_bool32 found; ma_uint32 iFormat; /* Make sure we don't overflow the channel map. */ if (channels > MA_MAX_CHANNELS) { channels = MA_MAX_CHANNELS; } ma_channel_map_init_standard(ma_standard_channel_map_microsoft, defaultChannelMap, ma_countof(defaultChannelMap), channels); MA_ZERO_OBJECT(&wf); wf.Format.cbSize = sizeof(wf); wf.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE; wf.Format.nChannels = (WORD)channels; wf.dwChannelMask = ma_channel_map_to_channel_mask__win32(defaultChannelMap, channels); found = MA_FALSE; for (iFormat = 0; iFormat < ma_countof(g_maFormatPriorities); ++iFormat) { ma_format format = g_maFormatPriorities[iFormat]; ma_uint32 iSampleRate; wf.Format.wBitsPerSample = (WORD)(ma_get_bytes_per_sample(format)*8); wf.Format.nBlockAlign = (WORD)(wf.Format.nChannels * wf.Format.wBitsPerSample / 8); wf.Format.nAvgBytesPerSec = wf.Format.nBlockAlign * wf.Format.nSamplesPerSec; wf.Samples.wValidBitsPerSample = /*(format == ma_format_s24_32) ? 24 :*/ wf.Format.wBitsPerSample; if (format == ma_format_f32) { wf.SubFormat = MA_GUID_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT; } else { wf.SubFormat = MA_GUID_KSDATAFORMAT_SUBTYPE_PCM; } for (iSampleRate = 0; iSampleRate < ma_countof(g_maStandardSampleRatePriorities); ++iSampleRate) { wf.Format.nSamplesPerSec = g_maStandardSampleRatePriorities[iSampleRate]; hr = ma_IAudioClient_IsFormatSupported((ma_IAudioClient*)pAudioClient, MA_AUDCLNT_SHAREMODE_EXCLUSIVE, (WAVEFORMATEX*)&wf, NULL); if (SUCCEEDED(hr)) { ma_add_native_data_format_to_device_info_from_WAVEFORMATEX((WAVEFORMATEX*)&wf, ma_share_mode_exclusive, pInfo); found = MA_TRUE; break; } } if (found) { break; } } ma_PropVariantClear(pContext, &var); if (!found) { ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_WARNING, "[WASAPI] Failed to find suitable device format for device info retrieval."); } } } else { ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_WARNING, "[WASAPI] Failed to retrieve device format for device info retrieval."); } ma_IPropertyStore_Release(pProperties); } else { ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_WARNING, "[WASAPI] Failed to open property store for device info retrieval."); } } #else { (void)pMMDevice; /* Unused. */ } #endif return MA_SUCCESS; } #if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) static ma_EDataFlow ma_device_type_to_EDataFlow(ma_device_type deviceType) { if (deviceType == ma_device_type_playback) { return ma_eRender; } else if (deviceType == ma_device_type_capture) { return ma_eCapture; } else { MA_ASSERT(MA_FALSE); return ma_eRender; /* Should never hit this. */ } } static ma_result ma_context_create_IMMDeviceEnumerator__wasapi(ma_context* pContext, ma_IMMDeviceEnumerator** ppDeviceEnumerator) { HRESULT hr; ma_IMMDeviceEnumerator* pDeviceEnumerator; MA_ASSERT(pContext != NULL); MA_ASSERT(ppDeviceEnumerator != NULL); *ppDeviceEnumerator = NULL; /* Safety. */ hr = ma_CoCreateInstance(pContext, MA_CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, MA_IID_IMMDeviceEnumerator, (void**)&pDeviceEnumerator); if (FAILED(hr)) { ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create device enumerator."); return ma_result_from_HRESULT(hr); } *ppDeviceEnumerator = pDeviceEnumerator; return MA_SUCCESS; } static LPWSTR ma_context_get_default_device_id_from_IMMDeviceEnumerator__wasapi(ma_context* pContext, ma_IMMDeviceEnumerator* pDeviceEnumerator, ma_device_type deviceType) { HRESULT hr; ma_IMMDevice* pMMDefaultDevice = NULL; LPWSTR pDefaultDeviceID = NULL; ma_EDataFlow dataFlow; ma_ERole role; MA_ASSERT(pContext != NULL); MA_ASSERT(pDeviceEnumerator != NULL); (void)pContext; /* Grab the EDataFlow type from the device type. */ dataFlow = ma_device_type_to_EDataFlow(deviceType); /* The role is always eConsole, but we may make this configurable later. */ role = ma_eConsole; hr = ma_IMMDeviceEnumerator_GetDefaultAudioEndpoint(pDeviceEnumerator, dataFlow, role, &pMMDefaultDevice); if (FAILED(hr)) { return NULL; } hr = ma_IMMDevice_GetId(pMMDefaultDevice, &pDefaultDeviceID); ma_IMMDevice_Release(pMMDefaultDevice); pMMDefaultDevice = NULL; if (FAILED(hr)) { return NULL; } return pDefaultDeviceID; } static LPWSTR ma_context_get_default_device_id__wasapi(ma_context* pContext, ma_device_type deviceType) /* Free the returned pointer with ma_CoTaskMemFree() */ { ma_result result; ma_IMMDeviceEnumerator* pDeviceEnumerator; LPWSTR pDefaultDeviceID = NULL; MA_ASSERT(pContext != NULL); result = ma_context_create_IMMDeviceEnumerator__wasapi(pContext, &pDeviceEnumerator); if (result != MA_SUCCESS) { return NULL; } pDefaultDeviceID = ma_context_get_default_device_id_from_IMMDeviceEnumerator__wasapi(pContext, pDeviceEnumerator, deviceType); ma_IMMDeviceEnumerator_Release(pDeviceEnumerator); return pDefaultDeviceID; } static ma_result ma_context_get_MMDevice__wasapi(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_IMMDevice** ppMMDevice) { ma_IMMDeviceEnumerator* pDeviceEnumerator; HRESULT hr; MA_ASSERT(pContext != NULL); MA_ASSERT(ppMMDevice != NULL); hr = ma_CoCreateInstance(pContext, MA_CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, MA_IID_IMMDeviceEnumerator, (void**)&pDeviceEnumerator); if (FAILED(hr)) { ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create IMMDeviceEnumerator.\n"); return ma_result_from_HRESULT(hr); } if (pDeviceID == NULL) { hr = ma_IMMDeviceEnumerator_GetDefaultAudioEndpoint(pDeviceEnumerator, (deviceType == ma_device_type_capture) ? ma_eCapture : ma_eRender, ma_eConsole, ppMMDevice); } else { hr = ma_IMMDeviceEnumerator_GetDevice(pDeviceEnumerator, pDeviceID->wasapi, ppMMDevice); } ma_IMMDeviceEnumerator_Release(pDeviceEnumerator); if (FAILED(hr)) { ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to retrieve IMMDevice.\n"); return ma_result_from_HRESULT(hr); } return MA_SUCCESS; } static ma_result ma_context_get_device_id_from_MMDevice__wasapi(ma_context* pContext, ma_IMMDevice* pMMDevice, ma_device_id* pDeviceID) { LPWSTR pDeviceIDString; HRESULT hr; MA_ASSERT(pDeviceID != NULL); hr = ma_IMMDevice_GetId(pMMDevice, &pDeviceIDString); if (SUCCEEDED(hr)) { size_t idlen = wcslen(pDeviceIDString); if (idlen+1 > ma_countof(pDeviceID->wasapi)) { ma_CoTaskMemFree(pContext, pDeviceIDString); MA_ASSERT(MA_FALSE); /* NOTE: If this is triggered, please report it. It means the format of the ID must haved change and is too long to fit in our fixed sized buffer. */ return MA_ERROR; } MA_COPY_MEMORY(pDeviceID->wasapi, pDeviceIDString, idlen * sizeof(wchar_t)); pDeviceID->wasapi[idlen] = '\0'; ma_CoTaskMemFree(pContext, pDeviceIDString); return MA_SUCCESS; } return MA_ERROR; } static ma_result ma_context_get_device_info_from_MMDevice__wasapi(ma_context* pContext, ma_IMMDevice* pMMDevice, LPWSTR pDefaultDeviceID, ma_bool32 onlySimpleInfo, ma_device_info* pInfo) { ma_result result; HRESULT hr; MA_ASSERT(pContext != NULL); MA_ASSERT(pMMDevice != NULL); MA_ASSERT(pInfo != NULL); /* ID. */ result = ma_context_get_device_id_from_MMDevice__wasapi(pContext, pMMDevice, &pInfo->id); if (result == MA_SUCCESS) { if (pDefaultDeviceID != NULL) { if (wcscmp(pInfo->id.wasapi, pDefaultDeviceID) == 0) { pInfo->isDefault = MA_TRUE; } } } /* Description / Friendly Name */ { ma_IPropertyStore *pProperties; hr = ma_IMMDevice_OpenPropertyStore(pMMDevice, STGM_READ, &pProperties); if (SUCCEEDED(hr)) { PROPVARIANT var; ma_PropVariantInit(&var); hr = ma_IPropertyStore_GetValue(pProperties, &MA_PKEY_Device_FriendlyName, &var); if (SUCCEEDED(hr)) { WideCharToMultiByte(CP_UTF8, 0, var.pwszVal, -1, pInfo->name, sizeof(pInfo->name), 0, FALSE); ma_PropVariantClear(pContext, &var); } ma_IPropertyStore_Release(pProperties); } } /* Format */ if (!onlySimpleInfo) { ma_IAudioClient* pAudioClient; hr = ma_IMMDevice_Activate(pMMDevice, &MA_IID_IAudioClient, CLSCTX_ALL, NULL, (void**)&pAudioClient); if (SUCCEEDED(hr)) { result = ma_context_get_device_info_from_IAudioClient__wasapi(pContext, pMMDevice, pAudioClient, pInfo); ma_IAudioClient_Release(pAudioClient); return result; } else { ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to activate audio client for device info retrieval."); return ma_result_from_HRESULT(hr); } } return MA_SUCCESS; } static ma_result ma_context_enumerate_devices_by_type__wasapi(ma_context* pContext, ma_IMMDeviceEnumerator* pDeviceEnumerator, ma_device_type deviceType, ma_enum_devices_callback_proc callback, void* pUserData) { ma_result result = MA_SUCCESS; UINT deviceCount; HRESULT hr; ma_uint32 iDevice; LPWSTR pDefaultDeviceID = NULL; ma_IMMDeviceCollection* pDeviceCollection = NULL; MA_ASSERT(pContext != NULL); MA_ASSERT(callback != NULL); /* Grab the default device. We use this to know whether or not flag the returned device info as being the default. */ pDefaultDeviceID = ma_context_get_default_device_id_from_IMMDeviceEnumerator__wasapi(pContext, pDeviceEnumerator, deviceType); /* We need to enumerate the devices which returns a device collection. */ hr = ma_IMMDeviceEnumerator_EnumAudioEndpoints(pDeviceEnumerator, ma_device_type_to_EDataFlow(deviceType), MA_MM_DEVICE_STATE_ACTIVE, &pDeviceCollection); if (SUCCEEDED(hr)) { hr = ma_IMMDeviceCollection_GetCount(pDeviceCollection, &deviceCount); if (FAILED(hr)) { ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to get device count.\n"); result = ma_result_from_HRESULT(hr); goto done; } for (iDevice = 0; iDevice < deviceCount; ++iDevice) { ma_device_info deviceInfo; ma_IMMDevice* pMMDevice; MA_ZERO_OBJECT(&deviceInfo); hr = ma_IMMDeviceCollection_Item(pDeviceCollection, iDevice, &pMMDevice); if (SUCCEEDED(hr)) { result = ma_context_get_device_info_from_MMDevice__wasapi(pContext, pMMDevice, pDefaultDeviceID, MA_TRUE, &deviceInfo); /* MA_TRUE = onlySimpleInfo. */ ma_IMMDevice_Release(pMMDevice); if (result == MA_SUCCESS) { ma_bool32 cbResult = callback(pContext, deviceType, &deviceInfo, pUserData); if (cbResult == MA_FALSE) { break; } } } } } done: if (pDefaultDeviceID != NULL) { ma_CoTaskMemFree(pContext, pDefaultDeviceID); pDefaultDeviceID = NULL; } if (pDeviceCollection != NULL) { ma_IMMDeviceCollection_Release(pDeviceCollection); pDeviceCollection = NULL; } return result; } static ma_result ma_context_get_IAudioClient_Desktop__wasapi(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, PROPVARIANT* pActivationParams, ma_IAudioClient** ppAudioClient, ma_IMMDevice** ppMMDevice) { ma_result result; HRESULT hr; MA_ASSERT(pContext != NULL); MA_ASSERT(ppAudioClient != NULL); MA_ASSERT(ppMMDevice != NULL); result = ma_context_get_MMDevice__wasapi(pContext, deviceType, pDeviceID, ppMMDevice); if (result != MA_SUCCESS) { return result; } hr = ma_IMMDevice_Activate(*ppMMDevice, &MA_IID_IAudioClient, CLSCTX_ALL, pActivationParams, (void**)ppAudioClient); if (FAILED(hr)) { return ma_result_from_HRESULT(hr); } return MA_SUCCESS; } #else static ma_result ma_context_get_IAudioClient_UWP__wasapi(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, PROPVARIANT* pActivationParams, ma_IAudioClient** ppAudioClient, ma_IUnknown** ppActivatedInterface) { ma_IActivateAudioInterfaceAsyncOperation *pAsyncOp = NULL; ma_completion_handler_uwp completionHandler; IID iid; LPOLESTR iidStr; HRESULT hr; ma_result result; HRESULT activateResult; ma_IUnknown* pActivatedInterface; MA_ASSERT(pContext != NULL); MA_ASSERT(ppAudioClient != NULL); if (pDeviceID != NULL) { iidStr = (LPOLESTR)pDeviceID->wasapi; } else { if (deviceType == ma_device_type_capture) { iid = MA_IID_DEVINTERFACE_AUDIO_CAPTURE; } else { iid = MA_IID_DEVINTERFACE_AUDIO_RENDER; } #if defined(__cplusplus) hr = StringFromIID(iid, &iidStr); #else hr = StringFromIID(&iid, &iidStr); #endif if (FAILED(hr)) { ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to convert device IID to string for ActivateAudioInterfaceAsync(). Out of memory.\n"); return ma_result_from_HRESULT(hr); } } result = ma_completion_handler_uwp_init(&completionHandler); if (result != MA_SUCCESS) { ma_CoTaskMemFree(pContext, iidStr); ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create event for waiting for ActivateAudioInterfaceAsync().\n"); return result; } hr = ((MA_PFN_ActivateAudioInterfaceAsync)pContext->wasapi.ActivateAudioInterfaceAsync)(iidStr, &MA_IID_IAudioClient, pActivationParams, (ma_IActivateAudioInterfaceCompletionHandler*)&completionHandler, (ma_IActivateAudioInterfaceAsyncOperation**)&pAsyncOp); if (FAILED(hr)) { ma_completion_handler_uwp_uninit(&completionHandler); ma_CoTaskMemFree(pContext, iidStr); ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] ActivateAudioInterfaceAsync() failed.\n"); return ma_result_from_HRESULT(hr); } if (pDeviceID == NULL) { ma_CoTaskMemFree(pContext, iidStr); } /* Wait for the async operation for finish. */ ma_completion_handler_uwp_wait(&completionHandler); ma_completion_handler_uwp_uninit(&completionHandler); hr = ma_IActivateAudioInterfaceAsyncOperation_GetActivateResult(pAsyncOp, &activateResult, &pActivatedInterface); ma_IActivateAudioInterfaceAsyncOperation_Release(pAsyncOp); if (FAILED(hr) || FAILED(activateResult)) { ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to activate device.\n"); return FAILED(hr) ? ma_result_from_HRESULT(hr) : ma_result_from_HRESULT(activateResult); } /* Here is where we grab the IAudioClient interface. */ hr = ma_IUnknown_QueryInterface(pActivatedInterface, &MA_IID_IAudioClient, (void**)ppAudioClient); if (FAILED(hr)) { ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to query IAudioClient interface.\n"); return ma_result_from_HRESULT(hr); } if (ppActivatedInterface) { *ppActivatedInterface = pActivatedInterface; } else { ma_IUnknown_Release(pActivatedInterface); } return MA_SUCCESS; } #endif /* https://docs.microsoft.com/en-us/windows/win32/api/audioclientactivationparams/ne-audioclientactivationparams-audioclient_activation_type */ typedef enum { MA_AUDIOCLIENT_ACTIVATION_TYPE_DEFAULT, MA_AUDIOCLIENT_ACTIVATION_TYPE_PROCESS_LOOPBACK } MA_AUDIOCLIENT_ACTIVATION_TYPE; /* https://docs.microsoft.com/en-us/windows/win32/api/audioclientactivationparams/ne-audioclientactivationparams-process_loopback_mode */ typedef enum { MA_PROCESS_LOOPBACK_MODE_INCLUDE_TARGET_PROCESS_TREE, MA_PROCESS_LOOPBACK_MODE_EXCLUDE_TARGET_PROCESS_TREE } MA_PROCESS_LOOPBACK_MODE; /* https://docs.microsoft.com/en-us/windows/win32/api/audioclientactivationparams/ns-audioclientactivationparams-audioclient_process_loopback_params */ typedef struct { DWORD TargetProcessId; MA_PROCESS_LOOPBACK_MODE ProcessLoopbackMode; } MA_AUDIOCLIENT_PROCESS_LOOPBACK_PARAMS; #if defined(_MSC_VER) && !defined(__clang__) #pragma warning(push) #pragma warning(disable:4201) /* nonstandard extension used: nameless struct/union */ #elif defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wpedantic" /* For ISO C99 doesn't support unnamed structs/unions [-Wpedantic] */ #if defined(__clang__) #pragma GCC diagnostic ignored "-Wc11-extensions" /* anonymous unions are a C11 extension */ #endif #endif /* https://docs.microsoft.com/en-us/windows/win32/api/audioclientactivationparams/ns-audioclientactivationparams-audioclient_activation_params */ typedef struct { MA_AUDIOCLIENT_ACTIVATION_TYPE ActivationType; union { MA_AUDIOCLIENT_PROCESS_LOOPBACK_PARAMS ProcessLoopbackParams; }; } MA_AUDIOCLIENT_ACTIVATION_PARAMS; #if defined(_MSC_VER) && !defined(__clang__) #pragma warning(pop) #elif defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))) #pragma GCC diagnostic pop #endif #define MA_VIRTUAL_AUDIO_DEVICE_PROCESS_LOOPBACK L"VAD\\Process_Loopback" static ma_result ma_context_get_IAudioClient__wasapi(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_uint32 loopbackProcessID, ma_bool32 loopbackProcessExclude, ma_IAudioClient** ppAudioClient, ma_WASAPIDeviceInterface** ppDeviceInterface) { ma_result result; ma_bool32 usingProcessLoopback = MA_FALSE; MA_AUDIOCLIENT_ACTIVATION_PARAMS audioclientActivationParams; PROPVARIANT activationParams; PROPVARIANT* pActivationParams = NULL; ma_device_id virtualDeviceID; /* Activation parameters specific to loopback mode. Note that process-specific loopback will only work when a default device ID is specified. */ if (deviceType == ma_device_type_loopback && loopbackProcessID != 0 && pDeviceID == NULL) { usingProcessLoopback = MA_TRUE; } if (usingProcessLoopback) { MA_ZERO_OBJECT(&audioclientActivationParams); audioclientActivationParams.ActivationType = MA_AUDIOCLIENT_ACTIVATION_TYPE_PROCESS_LOOPBACK; audioclientActivationParams.ProcessLoopbackParams.ProcessLoopbackMode = (loopbackProcessExclude) ? MA_PROCESS_LOOPBACK_MODE_EXCLUDE_TARGET_PROCESS_TREE : MA_PROCESS_LOOPBACK_MODE_INCLUDE_TARGET_PROCESS_TREE; audioclientActivationParams.ProcessLoopbackParams.TargetProcessId = (DWORD)loopbackProcessID; ma_PropVariantInit(&activationParams); activationParams.vt = VT_BLOB; activationParams.blob.cbSize = sizeof(audioclientActivationParams); activationParams.blob.pBlobData = (BYTE*)&audioclientActivationParams; pActivationParams = &activationParams; /* When requesting a specific device ID we need to use a special device ID. */ // MA_COPY_MEMORY(virtualDeviceID.wasapi, MA_VIRTUAL_AUDIO_DEVICE_PROCESS_LOOPBACK, (wcslen(MA_VIRTUAL_AUDIO_DEVICE_PROCESS_LOOPBACK) + 1) * sizeof(wchar_t)); /* +1 for the null terminator. */ memcpy(virtualDeviceID.wasapi, L"VAD\\Process_Loopback", (wcslen(L"VAD\\Process_Loopback") + 1) * sizeof(wchar_t)); /* +1 for the null terminator. */ //< @r-lyeh rewrite so it fixes `error C2143: syntax error: missing ')' before 'string'` (vs2022) pDeviceID = &virtualDeviceID; } else { pActivationParams = NULL; /* No activation parameters required. */ } #if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) result = ma_context_get_IAudioClient_Desktop__wasapi(pContext, deviceType, pDeviceID, pActivationParams, ppAudioClient, ppDeviceInterface); #else result = ma_context_get_IAudioClient_UWP__wasapi(pContext, deviceType, pDeviceID, pActivationParams, ppAudioClient, ppDeviceInterface); #endif /* If loopback mode was requested with a process ID and initialization failed, it could be because it's trying to run on an older version of Windows where it's not supported. We need to let the caller know about this with a log message. */ if (result != MA_SUCCESS) { if (usingProcessLoopback) { ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Loopback mode requested to %s process ID %u, but initialization failed. Support for this feature begins with Windows 10 Build 20348. Confirm your version of Windows or consider not using process-specific loopback.\n", (loopbackProcessExclude) ? "exclude" : "include", loopbackProcessID); } } return result; } static ma_result ma_context_enumerate_devices__wasapi(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData) { /* Different enumeration for desktop and UWP. */ #if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) /* Desktop */ HRESULT hr; ma_IMMDeviceEnumerator* pDeviceEnumerator; hr = ma_CoCreateInstance(pContext, MA_CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, MA_IID_IMMDeviceEnumerator, (void**)&pDeviceEnumerator); if (FAILED(hr)) { ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create device enumerator."); return ma_result_from_HRESULT(hr); } ma_context_enumerate_devices_by_type__wasapi(pContext, pDeviceEnumerator, ma_device_type_playback, callback, pUserData); ma_context_enumerate_devices_by_type__wasapi(pContext, pDeviceEnumerator, ma_device_type_capture, callback, pUserData); ma_IMMDeviceEnumerator_Release(pDeviceEnumerator); #else /* UWP The MMDevice API is only supported on desktop applications. For now, while I'm still figuring out how to properly enumerate over devices without using MMDevice, I'm restricting devices to defaults. Hint: DeviceInformation::FindAllAsync() with DeviceClass.AudioCapture/AudioRender. https://blogs.windows.com/buildingapps/2014/05/15/real-time-audio-in-windows-store-and-windows-phone-apps/ */ if (callback) { ma_bool32 cbResult = MA_TRUE; /* Playback. */ if (cbResult) { ma_device_info deviceInfo; MA_ZERO_OBJECT(&deviceInfo); ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); deviceInfo.isDefault = MA_TRUE; cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData); } /* Capture. */ if (cbResult) { ma_device_info deviceInfo; MA_ZERO_OBJECT(&deviceInfo); ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); deviceInfo.isDefault = MA_TRUE; cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData); } } #endif return MA_SUCCESS; } static ma_result ma_context_get_device_info__wasapi(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo) { #if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) ma_result result; ma_IMMDevice* pMMDevice = NULL; LPWSTR pDefaultDeviceID = NULL; result = ma_context_get_MMDevice__wasapi(pContext, deviceType, pDeviceID, &pMMDevice); if (result != MA_SUCCESS) { return result; } /* We need the default device ID so we can set the isDefault flag in the device info. */ pDefaultDeviceID = ma_context_get_default_device_id__wasapi(pContext, deviceType); result = ma_context_get_device_info_from_MMDevice__wasapi(pContext, pMMDevice, pDefaultDeviceID, MA_FALSE, pDeviceInfo); /* MA_FALSE = !onlySimpleInfo. */ if (pDefaultDeviceID != NULL) { ma_CoTaskMemFree(pContext, pDefaultDeviceID); pDefaultDeviceID = NULL; } ma_IMMDevice_Release(pMMDevice); return result; #else ma_IAudioClient* pAudioClient; ma_result result; /* UWP currently only uses default devices. */ if (deviceType == ma_device_type_playback) { ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); } else { ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); } result = ma_context_get_IAudioClient_UWP__wasapi(pContext, deviceType, pDeviceID, NULL, &pAudioClient, NULL); if (result != MA_SUCCESS) { return result; } result = ma_context_get_device_info_from_IAudioClient__wasapi(pContext, NULL, pAudioClient, pDeviceInfo); pDeviceInfo->isDefault = MA_TRUE; /* UWP only supports default devices. */ ma_IAudioClient_Release(pAudioClient); return result; #endif } static ma_result ma_device_uninit__wasapi(ma_device* pDevice) { MA_ASSERT(pDevice != NULL); #if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) if (pDevice->wasapi.pDeviceEnumerator) { ((ma_IMMDeviceEnumerator*)pDevice->wasapi.pDeviceEnumerator)->lpVtbl->UnregisterEndpointNotificationCallback((ma_IMMDeviceEnumerator*)pDevice->wasapi.pDeviceEnumerator, &pDevice->wasapi.notificationClient); ma_IMMDeviceEnumerator_Release((ma_IMMDeviceEnumerator*)pDevice->wasapi.pDeviceEnumerator); } #endif if (pDevice->wasapi.pRenderClient) { if (pDevice->wasapi.pMappedBufferPlayback != NULL) { ma_IAudioRenderClient_ReleaseBuffer((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient, pDevice->wasapi.mappedBufferPlaybackCap, 0); pDevice->wasapi.pMappedBufferPlayback = NULL; pDevice->wasapi.mappedBufferPlaybackCap = 0; pDevice->wasapi.mappedBufferPlaybackLen = 0; } ma_IAudioRenderClient_Release((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient); } if (pDevice->wasapi.pCaptureClient) { if (pDevice->wasapi.pMappedBufferCapture != NULL) { ma_IAudioCaptureClient_ReleaseBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, pDevice->wasapi.mappedBufferCaptureCap); pDevice->wasapi.pMappedBufferCapture = NULL; pDevice->wasapi.mappedBufferCaptureCap = 0; pDevice->wasapi.mappedBufferCaptureLen = 0; } ma_IAudioCaptureClient_Release((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient); } if (pDevice->wasapi.pAudioClientPlayback) { ma_IAudioClient_Release((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback); } if (pDevice->wasapi.pAudioClientCapture) { ma_IAudioClient_Release((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture); } if (pDevice->wasapi.hEventPlayback) { CloseHandle(pDevice->wasapi.hEventPlayback); } if (pDevice->wasapi.hEventCapture) { CloseHandle(pDevice->wasapi.hEventCapture); } return MA_SUCCESS; } typedef struct { /* Input. */ ma_format formatIn; ma_uint32 channelsIn; ma_uint32 sampleRateIn; ma_channel channelMapIn[MA_MAX_CHANNELS]; ma_uint32 periodSizeInFramesIn; ma_uint32 periodSizeInMillisecondsIn; ma_uint32 periodsIn; ma_share_mode shareMode; ma_performance_profile performanceProfile; ma_bool32 noAutoConvertSRC; ma_bool32 noDefaultQualitySRC; ma_bool32 noHardwareOffloading; ma_uint32 loopbackProcessID; ma_bool32 loopbackProcessExclude; /* Output. */ ma_IAudioClient* pAudioClient; ma_IAudioRenderClient* pRenderClient; ma_IAudioCaptureClient* pCaptureClient; ma_format formatOut; ma_uint32 channelsOut; ma_uint32 sampleRateOut; ma_channel channelMapOut[MA_MAX_CHANNELS]; ma_uint32 periodSizeInFramesOut; ma_uint32 periodsOut; ma_bool32 usingAudioClient3; char deviceName[256]; ma_device_id id; } ma_device_init_internal_data__wasapi; static ma_result ma_device_init_internal__wasapi(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_init_internal_data__wasapi* pData) { HRESULT hr; ma_result result = MA_SUCCESS; const char* errorMsg = ""; MA_AUDCLNT_SHAREMODE shareMode = MA_AUDCLNT_SHAREMODE_SHARED; DWORD streamFlags = 0; MA_REFERENCE_TIME periodDurationInMicroseconds; ma_bool32 wasInitializedUsingIAudioClient3 = MA_FALSE; WAVEFORMATEXTENSIBLE wf; ma_WASAPIDeviceInterface* pDeviceInterface = NULL; ma_IAudioClient2* pAudioClient2; ma_uint32 nativeSampleRate; MA_ASSERT(pContext != NULL); MA_ASSERT(pData != NULL); /* This function is only used to initialize one device type: either playback, capture or loopback. Never full-duplex. */ if (deviceType == ma_device_type_duplex) { return MA_INVALID_ARGS; } pData->pAudioClient = NULL; pData->pRenderClient = NULL; pData->pCaptureClient = NULL; streamFlags = MA_AUDCLNT_STREAMFLAGS_EVENTCALLBACK; if (!pData->noAutoConvertSRC && pData->sampleRateIn != 0 && pData->shareMode != ma_share_mode_exclusive) { /* <-- Exclusive streams must use the native sample rate. */ streamFlags |= MA_AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM; } if (!pData->noDefaultQualitySRC && pData->sampleRateIn != 0 && (streamFlags & MA_AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM) != 0) { streamFlags |= MA_AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY; } if (deviceType == ma_device_type_loopback) { streamFlags |= MA_AUDCLNT_STREAMFLAGS_LOOPBACK; } result = ma_context_get_IAudioClient__wasapi(pContext, deviceType, pDeviceID, pData->loopbackProcessID, pData->loopbackProcessExclude, &pData->pAudioClient, &pDeviceInterface); if (result != MA_SUCCESS) { goto done; } MA_ZERO_OBJECT(&wf); /* Try enabling hardware offloading. */ if (!pData->noHardwareOffloading) { hr = ma_IAudioClient_QueryInterface(pData->pAudioClient, &MA_IID_IAudioClient2, (void**)&pAudioClient2); if (SUCCEEDED(hr)) { BOOL isHardwareOffloadingSupported = 0; hr = ma_IAudioClient2_IsOffloadCapable(pAudioClient2, MA_AudioCategory_Other, &isHardwareOffloadingSupported); if (SUCCEEDED(hr) && isHardwareOffloadingSupported) { ma_AudioClientProperties clientProperties; MA_ZERO_OBJECT(&clientProperties); clientProperties.cbSize = sizeof(clientProperties); clientProperties.bIsOffload = 1; clientProperties.eCategory = MA_AudioCategory_Other; ma_IAudioClient2_SetClientProperties(pAudioClient2, &clientProperties); } pAudioClient2->lpVtbl->Release(pAudioClient2); } } /* Here is where we try to determine the best format to use with the device. If the client if wanting exclusive mode, first try finding the best format for that. If this fails, fall back to shared mode. */ result = MA_FORMAT_NOT_SUPPORTED; if (pData->shareMode == ma_share_mode_exclusive) { #if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) /* In exclusive mode on desktop we always use the backend's native format. */ ma_IPropertyStore* pStore = NULL; hr = ma_IMMDevice_OpenPropertyStore(pDeviceInterface, STGM_READ, &pStore); if (SUCCEEDED(hr)) { PROPVARIANT prop; ma_PropVariantInit(&prop); hr = ma_IPropertyStore_GetValue(pStore, &MA_PKEY_AudioEngine_DeviceFormat, &prop); if (SUCCEEDED(hr)) { WAVEFORMATEX* pActualFormat = (WAVEFORMATEX*)prop.blob.pBlobData; hr = ma_IAudioClient_IsFormatSupported((ma_IAudioClient*)pData->pAudioClient, MA_AUDCLNT_SHAREMODE_EXCLUSIVE, pActualFormat, NULL); if (SUCCEEDED(hr)) { MA_COPY_MEMORY(&wf, pActualFormat, sizeof(WAVEFORMATEXTENSIBLE)); } ma_PropVariantClear(pContext, &prop); } ma_IPropertyStore_Release(pStore); } #else /* I do not know how to query the device's native format on UWP so for now I'm just disabling support for exclusive mode. The alternative is to enumerate over different formats and check IsFormatSupported() until you find one that works. TODO: Add support for exclusive mode to UWP. */ hr = S_FALSE; #endif if (hr == S_OK) { shareMode = MA_AUDCLNT_SHAREMODE_EXCLUSIVE; result = MA_SUCCESS; } else { result = MA_SHARE_MODE_NOT_SUPPORTED; } } else { /* In shared mode we are always using the format reported by the operating system. */ WAVEFORMATEXTENSIBLE* pNativeFormat = NULL; hr = ma_IAudioClient_GetMixFormat((ma_IAudioClient*)pData->pAudioClient, (WAVEFORMATEX**)&pNativeFormat); if (hr != S_OK) { result = MA_FORMAT_NOT_SUPPORTED; } else { MA_COPY_MEMORY(&wf, pNativeFormat, sizeof(wf)); result = MA_SUCCESS; } ma_CoTaskMemFree(pContext, pNativeFormat); shareMode = MA_AUDCLNT_SHAREMODE_SHARED; } /* Return an error if we still haven't found a format. */ if (result != MA_SUCCESS) { errorMsg = "[WASAPI] Failed to find best device mix format."; goto done; } /* Override the native sample rate with the one requested by the caller, but only if we're not using the default sample rate. We'll use WASAPI to perform the sample rate conversion. */ nativeSampleRate = wf.Format.nSamplesPerSec; if (streamFlags & MA_AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM) { wf.Format.nSamplesPerSec = (pData->sampleRateIn != 0) ? pData->sampleRateIn : MA_DEFAULT_SAMPLE_RATE; wf.Format.nAvgBytesPerSec = wf.Format.nSamplesPerSec * wf.Format.nBlockAlign; } pData->formatOut = ma_format_from_WAVEFORMATEX((WAVEFORMATEX*)&wf); if (pData->formatOut == ma_format_unknown) { /* The format isn't supported. This is almost certainly because the exclusive mode format isn't supported by miniaudio. We need to return MA_SHARE_MODE_NOT_SUPPORTED in this case so that the caller can detect it and fall back to shared mode if desired. We should never get here if shared mode was requested, but just for completeness we'll check for it and return MA_FORMAT_NOT_SUPPORTED. */ if (shareMode == MA_AUDCLNT_SHAREMODE_EXCLUSIVE) { result = MA_SHARE_MODE_NOT_SUPPORTED; } else { result = MA_FORMAT_NOT_SUPPORTED; } errorMsg = "[WASAPI] Native format not supported."; goto done; } pData->channelsOut = wf.Format.nChannels; pData->sampleRateOut = wf.Format.nSamplesPerSec; /* Get the internal channel map based on the channel mask. */ ma_channel_mask_to_channel_map__win32(wf.dwChannelMask, pData->channelsOut, pData->channelMapOut); /* Period size. */ pData->periodsOut = (pData->periodsIn != 0) ? pData->periodsIn : MA_DEFAULT_PERIODS; pData->periodSizeInFramesOut = pData->periodSizeInFramesIn; if (pData->periodSizeInFramesOut == 0) { if (pData->periodSizeInMillisecondsIn == 0) { if (pData->performanceProfile == ma_performance_profile_low_latency) { pData->periodSizeInFramesOut = ma_calculate_buffer_size_in_frames_from_milliseconds(MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_LOW_LATENCY, wf.Format.nSamplesPerSec); } else { pData->periodSizeInFramesOut = ma_calculate_buffer_size_in_frames_from_milliseconds(MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_CONSERVATIVE, wf.Format.nSamplesPerSec); } } else { pData->periodSizeInFramesOut = ma_calculate_buffer_size_in_frames_from_milliseconds(pData->periodSizeInMillisecondsIn, wf.Format.nSamplesPerSec); } } periodDurationInMicroseconds = ((ma_uint64)pData->periodSizeInFramesOut * 1000 * 1000) / wf.Format.nSamplesPerSec; /* Slightly different initialization for shared and exclusive modes. We try exclusive mode first, and if it fails, fall back to shared mode. */ if (shareMode == MA_AUDCLNT_SHAREMODE_EXCLUSIVE) { MA_REFERENCE_TIME bufferDuration = periodDurationInMicroseconds * pData->periodsOut * 10; /* If the periodicy is too small, Initialize() will fail with AUDCLNT_E_INVALID_DEVICE_PERIOD. In this case we should just keep increasing it and trying it again. */ hr = E_FAIL; for (;;) { hr = ma_IAudioClient_Initialize((ma_IAudioClient*)pData->pAudioClient, shareMode, streamFlags, bufferDuration, bufferDuration, (WAVEFORMATEX*)&wf, NULL); if (hr == MA_AUDCLNT_E_INVALID_DEVICE_PERIOD) { if (bufferDuration > 500*10000) { break; } else { if (bufferDuration == 0) { /* <-- Just a sanity check to prevent an infinit loop. Should never happen, but it makes me feel better. */ break; } bufferDuration = bufferDuration * 2; continue; } } else { break; } } if (hr == MA_AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED) { ma_uint32 bufferSizeInFrames; hr = ma_IAudioClient_GetBufferSize((ma_IAudioClient*)pData->pAudioClient, &bufferSizeInFrames); if (SUCCEEDED(hr)) { bufferDuration = (MA_REFERENCE_TIME)((10000.0 * 1000 / wf.Format.nSamplesPerSec * bufferSizeInFrames) + 0.5); /* Unfortunately we need to release and re-acquire the audio client according to MSDN. Seems silly - why not just call IAudioClient_Initialize() again?! */ ma_IAudioClient_Release((ma_IAudioClient*)pData->pAudioClient); #if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) hr = ma_IMMDevice_Activate(pDeviceInterface, &MA_IID_IAudioClient, CLSCTX_ALL, NULL, (void**)&pData->pAudioClient); #else hr = ma_IUnknown_QueryInterface(pDeviceInterface, &MA_IID_IAudioClient, (void**)&pData->pAudioClient); #endif if (SUCCEEDED(hr)) { hr = ma_IAudioClient_Initialize((ma_IAudioClient*)pData->pAudioClient, shareMode, streamFlags, bufferDuration, bufferDuration, (WAVEFORMATEX*)&wf, NULL); } } } if (FAILED(hr)) { /* Failed to initialize in exclusive mode. Don't fall back to shared mode - instead tell the client about it. They can reinitialize in shared mode if they want. */ if (hr == E_ACCESSDENIED) { errorMsg = "[WASAPI] Failed to initialize device in exclusive mode. Access denied.", result = MA_ACCESS_DENIED; } else if (hr == MA_AUDCLNT_E_DEVICE_IN_USE) { errorMsg = "[WASAPI] Failed to initialize device in exclusive mode. Device in use.", result = MA_BUSY; } else { errorMsg = "[WASAPI] Failed to initialize device in exclusive mode."; result = ma_result_from_HRESULT(hr); } goto done; } } if (shareMode == MA_AUDCLNT_SHAREMODE_SHARED) { /* Low latency shared mode via IAudioClient3. NOTE ==== Contrary to the documentation on MSDN (https://docs.microsoft.com/en-us/windows/win32/api/audioclient/nf-audioclient-iaudioclient3-initializesharedaudiostream), the use of AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM and AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY with IAudioClient3_InitializeSharedAudioStream() absolutely does not work. Using any of these flags will result in HRESULT code 0x88890021. The other problem is that calling IAudioClient3_GetSharedModeEnginePeriod() with a sample rate different to that returned by IAudioClient_GetMixFormat() also results in an error. I'm therefore disabling low-latency shared mode with AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM. */ #ifndef MA_WASAPI_NO_LOW_LATENCY_SHARED_MODE { if ((streamFlags & MA_AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM) == 0 || nativeSampleRate == wf.Format.nSamplesPerSec) { ma_IAudioClient3* pAudioClient3 = NULL; hr = ma_IAudioClient_QueryInterface(pData->pAudioClient, &MA_IID_IAudioClient3, (void**)&pAudioClient3); if (SUCCEEDED(hr)) { ma_uint32 defaultPeriodInFrames; ma_uint32 fundamentalPeriodInFrames; ma_uint32 minPeriodInFrames; ma_uint32 maxPeriodInFrames; hr = ma_IAudioClient3_GetSharedModeEnginePeriod(pAudioClient3, (WAVEFORMATEX*)&wf, &defaultPeriodInFrames, &fundamentalPeriodInFrames, &minPeriodInFrames, &maxPeriodInFrames); if (SUCCEEDED(hr)) { ma_uint32 desiredPeriodInFrames = pData->periodSizeInFramesOut; ma_uint32 actualPeriodInFrames = desiredPeriodInFrames; /* Make sure the period size is a multiple of fundamentalPeriodInFrames. */ actualPeriodInFrames = actualPeriodInFrames / fundamentalPeriodInFrames; actualPeriodInFrames = actualPeriodInFrames * fundamentalPeriodInFrames; /* The period needs to be clamped between minPeriodInFrames and maxPeriodInFrames. */ actualPeriodInFrames = ma_clamp(actualPeriodInFrames, minPeriodInFrames, maxPeriodInFrames); ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "[WASAPI] Trying IAudioClient3_InitializeSharedAudioStream(actualPeriodInFrames=%d)\n", actualPeriodInFrames); ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, " defaultPeriodInFrames=%d\n", defaultPeriodInFrames); ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, " fundamentalPeriodInFrames=%d\n", fundamentalPeriodInFrames); ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, " minPeriodInFrames=%d\n", minPeriodInFrames); ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, " maxPeriodInFrames=%d\n", maxPeriodInFrames); /* If the client requested a largish buffer than we don't actually want to use low latency shared mode because it forces small buffers. */ if (actualPeriodInFrames >= desiredPeriodInFrames) { /* MA_AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM | MA_AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY must not be in the stream flags. If either of these are specified, IAudioClient3_InitializeSharedAudioStream() will fail. */ hr = ma_IAudioClient3_InitializeSharedAudioStream(pAudioClient3, streamFlags & ~(MA_AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM | MA_AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY), actualPeriodInFrames, (WAVEFORMATEX*)&wf, NULL); if (SUCCEEDED(hr)) { wasInitializedUsingIAudioClient3 = MA_TRUE; pData->periodSizeInFramesOut = actualPeriodInFrames; ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "[WASAPI] Using IAudioClient3\n"); ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, " periodSizeInFramesOut=%d\n", pData->periodSizeInFramesOut); } else { ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "[WASAPI] IAudioClient3_InitializeSharedAudioStream failed. Falling back to IAudioClient.\n"); } } else { ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "[WASAPI] Not using IAudioClient3 because the desired period size is larger than the maximum supported by IAudioClient3.\n"); } } else { ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "[WASAPI] IAudioClient3_GetSharedModeEnginePeriod failed. Falling back to IAudioClient.\n"); } ma_IAudioClient3_Release(pAudioClient3); pAudioClient3 = NULL; } } } #else { ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "[WASAPI] Not using IAudioClient3 because MA_WASAPI_NO_LOW_LATENCY_SHARED_MODE is enabled.\n"); } #endif /* If we don't have an IAudioClient3 then we need to use the normal initialization routine. */ if (!wasInitializedUsingIAudioClient3) { MA_REFERENCE_TIME bufferDuration = periodDurationInMicroseconds * pData->periodsOut * 10; /* <-- Multiply by 10 for microseconds to 100-nanoseconds. */ hr = ma_IAudioClient_Initialize((ma_IAudioClient*)pData->pAudioClient, shareMode, streamFlags, bufferDuration, 0, (WAVEFORMATEX*)&wf, NULL); if (FAILED(hr)) { if (hr == E_ACCESSDENIED) { errorMsg = "[WASAPI] Failed to initialize device. Access denied.", result = MA_ACCESS_DENIED; } else if (hr == MA_AUDCLNT_E_DEVICE_IN_USE) { errorMsg = "[WASAPI] Failed to initialize device. Device in use.", result = MA_BUSY; } else { errorMsg = "[WASAPI] Failed to initialize device.", result = ma_result_from_HRESULT(hr); } goto done; } } } if (!wasInitializedUsingIAudioClient3) { ma_uint32 bufferSizeInFrames; hr = ma_IAudioClient_GetBufferSize((ma_IAudioClient*)pData->pAudioClient, &bufferSizeInFrames); if (FAILED(hr)) { errorMsg = "[WASAPI] Failed to get audio client's actual buffer size.", result = ma_result_from_HRESULT(hr); goto done; } pData->periodSizeInFramesOut = bufferSizeInFrames / pData->periodsOut; } pData->usingAudioClient3 = wasInitializedUsingIAudioClient3; if (deviceType == ma_device_type_playback) { result = ma_device_create_IAudioClient_service__wasapi(pContext, deviceType, (ma_IAudioClient*)pData->pAudioClient, (void**)&pData->pRenderClient); } else { result = ma_device_create_IAudioClient_service__wasapi(pContext, deviceType, (ma_IAudioClient*)pData->pAudioClient, (void**)&pData->pCaptureClient); } /*if (FAILED(hr)) {*/ if (result != MA_SUCCESS) { errorMsg = "[WASAPI] Failed to get audio client service."; goto done; } /* Grab the name of the device. */ #if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) { ma_IPropertyStore *pProperties; hr = ma_IMMDevice_OpenPropertyStore(pDeviceInterface, STGM_READ, &pProperties); if (SUCCEEDED(hr)) { PROPVARIANT varName; ma_PropVariantInit(&varName); hr = ma_IPropertyStore_GetValue(pProperties, &MA_PKEY_Device_FriendlyName, &varName); if (SUCCEEDED(hr)) { WideCharToMultiByte(CP_UTF8, 0, varName.pwszVal, -1, pData->deviceName, sizeof(pData->deviceName), 0, FALSE); ma_PropVariantClear(pContext, &varName); } ma_IPropertyStore_Release(pProperties); } } #endif /* For the WASAPI backend we need to know the actual IDs of the device in order to do automatic stream routing so that IDs can be compared and we can determine which device has been detached and whether or not it matches with our ma_device. */ #if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) { /* Desktop */ ma_context_get_device_id_from_MMDevice__wasapi(pContext, pDeviceInterface, &pData->id); } #else { /* UWP */ /* TODO: Implement me. Need to figure out how to get the ID of the default device. */ } #endif done: /* Clean up. */ #if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) if (pDeviceInterface != NULL) { ma_IMMDevice_Release(pDeviceInterface); } #else if (pDeviceInterface != NULL) { ma_IUnknown_Release(pDeviceInterface); } #endif if (result != MA_SUCCESS) { if (pData->pRenderClient) { ma_IAudioRenderClient_Release((ma_IAudioRenderClient*)pData->pRenderClient); pData->pRenderClient = NULL; } if (pData->pCaptureClient) { ma_IAudioCaptureClient_Release((ma_IAudioCaptureClient*)pData->pCaptureClient); pData->pCaptureClient = NULL; } if (pData->pAudioClient) { ma_IAudioClient_Release((ma_IAudioClient*)pData->pAudioClient); pData->pAudioClient = NULL; } if (errorMsg != NULL && errorMsg[0] != '\0') { ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "%s\n", errorMsg); } return result; } else { return MA_SUCCESS; } } static ma_result ma_device_reinit__wasapi(ma_device* pDevice, ma_device_type deviceType) { ma_device_init_internal_data__wasapi data; ma_result result; MA_ASSERT(pDevice != NULL); /* We only re-initialize the playback or capture device. Never a full-duplex device. */ if (deviceType == ma_device_type_duplex) { return MA_INVALID_ARGS; } /* Before reinitializing the device we need to free the previous audio clients. There's a known memory leak here. We will be calling this from the routing change callback that is fired by WASAPI. If we attempt to release the IAudioClient we will deadlock. In my opinion this is a bug. I'm not sure what I need to do to handle this cleanly, but I think we'll probably need some system where we post an event, but delay the execution of it until the callback has returned. I'm not sure how to do this reliably, however. I have set up some infrastructure for a command thread which might be useful for this. */ if (deviceType == ma_device_type_capture || deviceType == ma_device_type_loopback) { if (pDevice->wasapi.pCaptureClient) { ma_IAudioCaptureClient_Release((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient); pDevice->wasapi.pCaptureClient = NULL; } if (pDevice->wasapi.pAudioClientCapture) { /*ma_device_release_IAudioClient_service__wasapi(pDevice, ma_device_type_capture);*/ pDevice->wasapi.pAudioClientCapture = NULL; } } if (deviceType == ma_device_type_playback) { if (pDevice->wasapi.pRenderClient) { ma_IAudioRenderClient_Release((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient); pDevice->wasapi.pRenderClient = NULL; } if (pDevice->wasapi.pAudioClientPlayback) { /*ma_device_release_IAudioClient_service__wasapi(pDevice, ma_device_type_playback);*/ pDevice->wasapi.pAudioClientPlayback = NULL; } } if (deviceType == ma_device_type_playback) { data.formatIn = pDevice->playback.format; data.channelsIn = pDevice->playback.channels; MA_COPY_MEMORY(data.channelMapIn, pDevice->playback.channelMap, sizeof(pDevice->playback.channelMap)); data.shareMode = pDevice->playback.shareMode; } else { data.formatIn = pDevice->capture.format; data.channelsIn = pDevice->capture.channels; MA_COPY_MEMORY(data.channelMapIn, pDevice->capture.channelMap, sizeof(pDevice->capture.channelMap)); data.shareMode = pDevice->capture.shareMode; } data.sampleRateIn = pDevice->sampleRate; data.periodSizeInFramesIn = pDevice->wasapi.originalPeriodSizeInFrames; data.periodSizeInMillisecondsIn = pDevice->wasapi.originalPeriodSizeInMilliseconds; data.periodsIn = pDevice->wasapi.originalPeriods; data.performanceProfile = pDevice->wasapi.originalPerformanceProfile; data.noAutoConvertSRC = pDevice->wasapi.noAutoConvertSRC; data.noDefaultQualitySRC = pDevice->wasapi.noDefaultQualitySRC; data.noHardwareOffloading = pDevice->wasapi.noHardwareOffloading; data.loopbackProcessID = pDevice->wasapi.loopbackProcessID; data.loopbackProcessExclude = pDevice->wasapi.loopbackProcessExclude; result = ma_device_init_internal__wasapi(pDevice->pContext, deviceType, NULL, &data); if (result != MA_SUCCESS) { return result; } /* At this point we have some new objects ready to go. We need to uninitialize the previous ones and then set the new ones. */ if (deviceType == ma_device_type_capture || deviceType == ma_device_type_loopback) { pDevice->wasapi.pAudioClientCapture = data.pAudioClient; pDevice->wasapi.pCaptureClient = data.pCaptureClient; pDevice->capture.internalFormat = data.formatOut; pDevice->capture.internalChannels = data.channelsOut; pDevice->capture.internalSampleRate = data.sampleRateOut; MA_COPY_MEMORY(pDevice->capture.internalChannelMap, data.channelMapOut, sizeof(data.channelMapOut)); pDevice->capture.internalPeriodSizeInFrames = data.periodSizeInFramesOut; pDevice->capture.internalPeriods = data.periodsOut; ma_strcpy_s(pDevice->capture.name, sizeof(pDevice->capture.name), data.deviceName); ma_IAudioClient_SetEventHandle((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture, pDevice->wasapi.hEventCapture); pDevice->wasapi.periodSizeInFramesCapture = data.periodSizeInFramesOut; ma_IAudioClient_GetBufferSize((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture, &pDevice->wasapi.actualBufferSizeInFramesCapture); /* We must always have a valid ID. */ ma_wcscpy_s(pDevice->capture.id.wasapi, sizeof(pDevice->capture.id.wasapi), data.id.wasapi); } if (deviceType == ma_device_type_playback) { pDevice->wasapi.pAudioClientPlayback = data.pAudioClient; pDevice->wasapi.pRenderClient = data.pRenderClient; pDevice->playback.internalFormat = data.formatOut; pDevice->playback.internalChannels = data.channelsOut; pDevice->playback.internalSampleRate = data.sampleRateOut; MA_COPY_MEMORY(pDevice->playback.internalChannelMap, data.channelMapOut, sizeof(data.channelMapOut)); pDevice->playback.internalPeriodSizeInFrames = data.periodSizeInFramesOut; pDevice->playback.internalPeriods = data.periodsOut; ma_strcpy_s(pDevice->playback.name, sizeof(pDevice->playback.name), data.deviceName); ma_IAudioClient_SetEventHandle((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback, pDevice->wasapi.hEventPlayback); pDevice->wasapi.periodSizeInFramesPlayback = data.periodSizeInFramesOut; ma_IAudioClient_GetBufferSize((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback, &pDevice->wasapi.actualBufferSizeInFramesPlayback); /* We must always have a valid ID because rerouting will look at it. */ ma_wcscpy_s(pDevice->playback.id.wasapi, sizeof(pDevice->playback.id.wasapi), data.id.wasapi); } return MA_SUCCESS; } static ma_result ma_device_init__wasapi(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture) { ma_result result = MA_SUCCESS; #if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) HRESULT hr; ma_IMMDeviceEnumerator* pDeviceEnumerator; #endif MA_ASSERT(pDevice != NULL); MA_ZERO_OBJECT(&pDevice->wasapi); pDevice->wasapi.usage = pConfig->wasapi.usage; pDevice->wasapi.noAutoConvertSRC = pConfig->wasapi.noAutoConvertSRC; pDevice->wasapi.noDefaultQualitySRC = pConfig->wasapi.noDefaultQualitySRC; pDevice->wasapi.noHardwareOffloading = pConfig->wasapi.noHardwareOffloading; pDevice->wasapi.loopbackProcessID = pConfig->wasapi.loopbackProcessID; pDevice->wasapi.loopbackProcessExclude = pConfig->wasapi.loopbackProcessExclude; /* Exclusive mode is not allowed with loopback. */ if (pConfig->deviceType == ma_device_type_loopback && pConfig->playback.shareMode == ma_share_mode_exclusive) { return MA_INVALID_DEVICE_CONFIG; } if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex || pConfig->deviceType == ma_device_type_loopback) { ma_device_init_internal_data__wasapi data; data.formatIn = pDescriptorCapture->format; data.channelsIn = pDescriptorCapture->channels; data.sampleRateIn = pDescriptorCapture->sampleRate; MA_COPY_MEMORY(data.channelMapIn, pDescriptorCapture->channelMap, sizeof(pDescriptorCapture->channelMap)); data.periodSizeInFramesIn = pDescriptorCapture->periodSizeInFrames; data.periodSizeInMillisecondsIn = pDescriptorCapture->periodSizeInMilliseconds; data.periodsIn = pDescriptorCapture->periodCount; data.shareMode = pDescriptorCapture->shareMode; data.performanceProfile = pConfig->performanceProfile; data.noAutoConvertSRC = pConfig->wasapi.noAutoConvertSRC; data.noDefaultQualitySRC = pConfig->wasapi.noDefaultQualitySRC; data.noHardwareOffloading = pConfig->wasapi.noHardwareOffloading; data.loopbackProcessID = pConfig->wasapi.loopbackProcessID; data.loopbackProcessExclude = pConfig->wasapi.loopbackProcessExclude; result = ma_device_init_internal__wasapi(pDevice->pContext, (pConfig->deviceType == ma_device_type_loopback) ? ma_device_type_loopback : ma_device_type_capture, pDescriptorCapture->pDeviceID, &data); if (result != MA_SUCCESS) { return result; } pDevice->wasapi.pAudioClientCapture = data.pAudioClient; pDevice->wasapi.pCaptureClient = data.pCaptureClient; pDevice->wasapi.originalPeriodSizeInMilliseconds = pDescriptorCapture->periodSizeInMilliseconds; pDevice->wasapi.originalPeriodSizeInFrames = pDescriptorCapture->periodSizeInFrames; pDevice->wasapi.originalPeriods = pDescriptorCapture->periodCount; pDevice->wasapi.originalPerformanceProfile = pConfig->performanceProfile; /* The event for capture needs to be manual reset for the same reason as playback. We keep the initial state set to unsignaled, however, because we want to block until we actually have something for the first call to ma_device_read(). */ pDevice->wasapi.hEventCapture = CreateEventW(NULL, FALSE, FALSE, NULL); /* Auto reset, unsignaled by default. */ if (pDevice->wasapi.hEventCapture == NULL) { result = ma_result_from_GetLastError(GetLastError()); if (pDevice->wasapi.pCaptureClient != NULL) { ma_IAudioCaptureClient_Release((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient); pDevice->wasapi.pCaptureClient = NULL; } if (pDevice->wasapi.pAudioClientCapture != NULL) { ma_IAudioClient_Release((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture); pDevice->wasapi.pAudioClientCapture = NULL; } ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create event for capture."); return result; } ma_IAudioClient_SetEventHandle((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture, pDevice->wasapi.hEventCapture); pDevice->wasapi.periodSizeInFramesCapture = data.periodSizeInFramesOut; ma_IAudioClient_GetBufferSize((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture, &pDevice->wasapi.actualBufferSizeInFramesCapture); /* We must always have a valid ID. */ ma_wcscpy_s(pDevice->capture.id.wasapi, sizeof(pDevice->capture.id.wasapi), data.id.wasapi); /* The descriptor needs to be updated with actual values. */ pDescriptorCapture->format = data.formatOut; pDescriptorCapture->channels = data.channelsOut; pDescriptorCapture->sampleRate = data.sampleRateOut; MA_COPY_MEMORY(pDescriptorCapture->channelMap, data.channelMapOut, sizeof(data.channelMapOut)); pDescriptorCapture->periodSizeInFrames = data.periodSizeInFramesOut; pDescriptorCapture->periodCount = data.periodsOut; } if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { ma_device_init_internal_data__wasapi data; data.formatIn = pDescriptorPlayback->format; data.channelsIn = pDescriptorPlayback->channels; data.sampleRateIn = pDescriptorPlayback->sampleRate; MA_COPY_MEMORY(data.channelMapIn, pDescriptorPlayback->channelMap, sizeof(pDescriptorPlayback->channelMap)); data.periodSizeInFramesIn = pDescriptorPlayback->periodSizeInFrames; data.periodSizeInMillisecondsIn = pDescriptorPlayback->periodSizeInMilliseconds; data.periodsIn = pDescriptorPlayback->periodCount; data.shareMode = pDescriptorPlayback->shareMode; data.performanceProfile = pConfig->performanceProfile; data.noAutoConvertSRC = pConfig->wasapi.noAutoConvertSRC; data.noDefaultQualitySRC = pConfig->wasapi.noDefaultQualitySRC; data.noHardwareOffloading = pConfig->wasapi.noHardwareOffloading; data.loopbackProcessID = pConfig->wasapi.loopbackProcessID; data.loopbackProcessExclude = pConfig->wasapi.loopbackProcessExclude; result = ma_device_init_internal__wasapi(pDevice->pContext, ma_device_type_playback, pDescriptorPlayback->pDeviceID, &data); if (result != MA_SUCCESS) { if (pConfig->deviceType == ma_device_type_duplex) { if (pDevice->wasapi.pCaptureClient != NULL) { ma_IAudioCaptureClient_Release((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient); pDevice->wasapi.pCaptureClient = NULL; } if (pDevice->wasapi.pAudioClientCapture != NULL) { ma_IAudioClient_Release((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture); pDevice->wasapi.pAudioClientCapture = NULL; } CloseHandle(pDevice->wasapi.hEventCapture); pDevice->wasapi.hEventCapture = NULL; } return result; } pDevice->wasapi.pAudioClientPlayback = data.pAudioClient; pDevice->wasapi.pRenderClient = data.pRenderClient; pDevice->wasapi.originalPeriodSizeInMilliseconds = pDescriptorPlayback->periodSizeInMilliseconds; pDevice->wasapi.originalPeriodSizeInFrames = pDescriptorPlayback->periodSizeInFrames; pDevice->wasapi.originalPeriods = pDescriptorPlayback->periodCount; pDevice->wasapi.originalPerformanceProfile = pConfig->performanceProfile; /* The event for playback is needs to be manual reset because we want to explicitly control the fact that it becomes signalled only after the whole available space has been filled, never before. The playback event also needs to be initially set to a signaled state so that the first call to ma_device_write() is able to get passed WaitForMultipleObjects(). */ pDevice->wasapi.hEventPlayback = CreateEventW(NULL, FALSE, TRUE, NULL); /* Auto reset, signaled by default. */ if (pDevice->wasapi.hEventPlayback == NULL) { result = ma_result_from_GetLastError(GetLastError()); if (pConfig->deviceType == ma_device_type_duplex) { if (pDevice->wasapi.pCaptureClient != NULL) { ma_IAudioCaptureClient_Release((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient); pDevice->wasapi.pCaptureClient = NULL; } if (pDevice->wasapi.pAudioClientCapture != NULL) { ma_IAudioClient_Release((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture); pDevice->wasapi.pAudioClientCapture = NULL; } CloseHandle(pDevice->wasapi.hEventCapture); pDevice->wasapi.hEventCapture = NULL; } if (pDevice->wasapi.pRenderClient != NULL) { ma_IAudioRenderClient_Release((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient); pDevice->wasapi.pRenderClient = NULL; } if (pDevice->wasapi.pAudioClientPlayback != NULL) { ma_IAudioClient_Release((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback); pDevice->wasapi.pAudioClientPlayback = NULL; } ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create event for playback."); return result; } ma_IAudioClient_SetEventHandle((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback, pDevice->wasapi.hEventPlayback); pDevice->wasapi.periodSizeInFramesPlayback = data.periodSizeInFramesOut; ma_IAudioClient_GetBufferSize((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback, &pDevice->wasapi.actualBufferSizeInFramesPlayback); /* We must always have a valid ID because rerouting will look at it. */ ma_wcscpy_s(pDevice->playback.id.wasapi, sizeof(pDevice->playback.id.wasapi), data.id.wasapi); /* The descriptor needs to be updated with actual values. */ pDescriptorPlayback->format = data.formatOut; pDescriptorPlayback->channels = data.channelsOut; pDescriptorPlayback->sampleRate = data.sampleRateOut; MA_COPY_MEMORY(pDescriptorPlayback->channelMap, data.channelMapOut, sizeof(data.channelMapOut)); pDescriptorPlayback->periodSizeInFrames = data.periodSizeInFramesOut; pDescriptorPlayback->periodCount = data.periodsOut; } /* We need to register a notification client to detect when the device has been disabled, unplugged or re-routed (when the default device changes). When we are connecting to the default device we want to do automatic stream routing when the device is disabled or unplugged. Otherwise we want to just stop the device outright and let the application handle it. */ #if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) if (pConfig->wasapi.noAutoStreamRouting == MA_FALSE) { if ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pConfig->capture.pDeviceID == NULL) { pDevice->wasapi.allowCaptureAutoStreamRouting = MA_TRUE; } if ((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pConfig->playback.pDeviceID == NULL) { pDevice->wasapi.allowPlaybackAutoStreamRouting = MA_TRUE; } } hr = ma_CoCreateInstance(pDevice->pContext, MA_CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, MA_IID_IMMDeviceEnumerator, (void**)&pDeviceEnumerator); if (FAILED(hr)) { ma_device_uninit__wasapi(pDevice); ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create device enumerator."); return ma_result_from_HRESULT(hr); } pDevice->wasapi.notificationClient.lpVtbl = (void*)&g_maNotificationCientVtbl; pDevice->wasapi.notificationClient.counter = 1; pDevice->wasapi.notificationClient.pDevice = pDevice; hr = pDeviceEnumerator->lpVtbl->RegisterEndpointNotificationCallback(pDeviceEnumerator, &pDevice->wasapi.notificationClient); if (SUCCEEDED(hr)) { pDevice->wasapi.pDeviceEnumerator = (ma_ptr)pDeviceEnumerator; } else { /* Not the end of the world if we fail to register the notification callback. We just won't support automatic stream routing. */ ma_IMMDeviceEnumerator_Release(pDeviceEnumerator); } #endif c89atomic_exchange_32(&pDevice->wasapi.isStartedCapture, MA_FALSE); c89atomic_exchange_32(&pDevice->wasapi.isStartedPlayback, MA_FALSE); return MA_SUCCESS; } static ma_result ma_device__get_available_frames__wasapi(ma_device* pDevice, ma_IAudioClient* pAudioClient, ma_uint32* pFrameCount) { ma_uint32 paddingFramesCount; HRESULT hr; ma_share_mode shareMode; MA_ASSERT(pDevice != NULL); MA_ASSERT(pFrameCount != NULL); *pFrameCount = 0; if ((ma_ptr)pAudioClient != pDevice->wasapi.pAudioClientPlayback && (ma_ptr)pAudioClient != pDevice->wasapi.pAudioClientCapture) { return MA_INVALID_OPERATION; } /* I've had a report that GetCurrentPadding() is returning a frame count of 0 which is preventing higher level function calls from doing anything because it thinks nothing is available. I have taken a look at the documentation and it looks like this is unnecessary in exclusive mode. From Microsoft's documentation: For an exclusive-mode rendering or capture stream that was initialized with the AUDCLNT_STREAMFLAGS_EVENTCALLBACK flag, the client typically has no use for the padding value reported by GetCurrentPadding. Instead, the client accesses an entire buffer during each processing pass. Considering this, I'm going to skip GetCurrentPadding() for exclusive mode and just report the entire buffer. This depends on the caller making sure they wait on the event handler. */ shareMode = ((ma_ptr)pAudioClient == pDevice->wasapi.pAudioClientPlayback) ? pDevice->playback.shareMode : pDevice->capture.shareMode; if (shareMode == ma_share_mode_shared) { /* Shared mode. */ hr = ma_IAudioClient_GetCurrentPadding(pAudioClient, &paddingFramesCount); if (FAILED(hr)) { return ma_result_from_HRESULT(hr); } if ((ma_ptr)pAudioClient == pDevice->wasapi.pAudioClientPlayback) { *pFrameCount = pDevice->wasapi.actualBufferSizeInFramesPlayback - paddingFramesCount; } else { *pFrameCount = paddingFramesCount; } } else { /* Exclusive mode. */ if ((ma_ptr)pAudioClient == pDevice->wasapi.pAudioClientPlayback) { *pFrameCount = pDevice->wasapi.actualBufferSizeInFramesPlayback; } else { *pFrameCount = pDevice->wasapi.actualBufferSizeInFramesCapture; } } return MA_SUCCESS; } static ma_result ma_device_reroute__wasapi(ma_device* pDevice, ma_device_type deviceType) { ma_result result; if (deviceType == ma_device_type_duplex) { return MA_INVALID_ARGS; } ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "=== CHANGING DEVICE ===\n"); result = ma_device_reinit__wasapi(pDevice, deviceType); if (result != MA_SUCCESS) { ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_WARNING, "[WASAPI] Reinitializing device after route change failed.\n"); return result; } ma_device__post_init_setup(pDevice, deviceType); ma_device__on_notification_rerouted(pDevice); return MA_SUCCESS; } static ma_result ma_device_start__wasapi(ma_device* pDevice) { HRESULT hr; MA_ASSERT(pDevice != NULL); if (pDevice->pContext->wasapi.hAvrt) { LPCWSTR pTaskName = ma_to_usage_string__wasapi(pDevice->wasapi.usage); if (pTaskName) { DWORD idx = 0; pDevice->wasapi.hAvrtHandle = ((MA_PFN_AvSetMmThreadCharacteristicsW)pDevice->pContext->wasapi.AvSetMmThreadCharacteristicsW)(pTaskName, &idx); } } if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex || pDevice->type == ma_device_type_loopback) { hr = ma_IAudioClient_Start((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture); if (FAILED(hr)) { ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to start internal capture device."); return ma_result_from_HRESULT(hr); } c89atomic_exchange_32(&pDevice->wasapi.isStartedCapture, MA_TRUE); } if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { hr = ma_IAudioClient_Start((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback); if (FAILED(hr)) { ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to start internal playback device."); return ma_result_from_HRESULT(hr); } c89atomic_exchange_32(&pDevice->wasapi.isStartedPlayback, MA_TRUE); } return MA_SUCCESS; } static ma_result ma_device_stop__wasapi(ma_device* pDevice) { ma_result result; HRESULT hr; MA_ASSERT(pDevice != NULL); if (pDevice->wasapi.hAvrtHandle) { ((MA_PFN_AvRevertMmThreadCharacteristics)pDevice->pContext->wasapi.AvRevertMmThreadcharacteristics)((HANDLE)pDevice->wasapi.hAvrtHandle); pDevice->wasapi.hAvrtHandle = NULL; } if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex || pDevice->type == ma_device_type_loopback) { hr = ma_IAudioClient_Stop((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture); if (FAILED(hr)) { ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to stop internal capture device."); return ma_result_from_HRESULT(hr); } /* The audio client needs to be reset otherwise restarting will fail. */ hr = ma_IAudioClient_Reset((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture); if (FAILED(hr)) { ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to reset internal capture device."); return ma_result_from_HRESULT(hr); } /* If we have a mapped buffer we need to release it. */ if (pDevice->wasapi.pMappedBufferCapture != NULL) { ma_IAudioCaptureClient_ReleaseBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, pDevice->wasapi.mappedBufferCaptureCap); pDevice->wasapi.pMappedBufferCapture = NULL; pDevice->wasapi.mappedBufferCaptureCap = 0; pDevice->wasapi.mappedBufferCaptureLen = 0; } c89atomic_exchange_32(&pDevice->wasapi.isStartedCapture, MA_FALSE); } if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { /* The buffer needs to be drained before stopping the device. Not doing this will result in the last few frames not getting output to the speakers. This is a problem for very short sounds because it'll result in a significant portion of it not getting played. */ if (c89atomic_load_32(&pDevice->wasapi.isStartedPlayback)) { /* We need to make sure we put a timeout here or else we'll risk getting stuck in a deadlock in some cases. */ DWORD waitTime = pDevice->wasapi.actualBufferSizeInFramesPlayback / pDevice->playback.internalSampleRate; if (pDevice->playback.shareMode == ma_share_mode_exclusive) { WaitForSingleObject(pDevice->wasapi.hEventPlayback, waitTime); } else { ma_uint32 prevFramesAvaialablePlayback = (ma_uint32)-1; ma_uint32 framesAvailablePlayback; for (;;) { result = ma_device__get_available_frames__wasapi(pDevice, (ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback, &framesAvailablePlayback); if (result != MA_SUCCESS) { break; } if (framesAvailablePlayback >= pDevice->wasapi.actualBufferSizeInFramesPlayback) { break; } /* Just a safety check to avoid an infinite loop. If this iteration results in a situation where the number of available frames has not changed, get out of the loop. I don't think this should ever happen, but I think it's nice to have just in case. */ if (framesAvailablePlayback == prevFramesAvaialablePlayback) { break; } prevFramesAvaialablePlayback = framesAvailablePlayback; WaitForSingleObject(pDevice->wasapi.hEventPlayback, waitTime * 1000); ResetEvent(pDevice->wasapi.hEventPlayback); /* Manual reset. */ } } } hr = ma_IAudioClient_Stop((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback); if (FAILED(hr)) { ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to stop internal playback device."); return ma_result_from_HRESULT(hr); } /* The audio client needs to be reset otherwise restarting will fail. */ hr = ma_IAudioClient_Reset((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback); if (FAILED(hr)) { ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to reset internal playback device."); return ma_result_from_HRESULT(hr); } if (pDevice->wasapi.pMappedBufferPlayback != NULL) { ma_IAudioRenderClient_ReleaseBuffer((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient, pDevice->wasapi.mappedBufferPlaybackCap, 0); pDevice->wasapi.pMappedBufferPlayback = NULL; pDevice->wasapi.mappedBufferPlaybackCap = 0; pDevice->wasapi.mappedBufferPlaybackLen = 0; } c89atomic_exchange_32(&pDevice->wasapi.isStartedPlayback, MA_FALSE); } return MA_SUCCESS; } #ifndef MA_WASAPI_WAIT_TIMEOUT_MILLISECONDS #define MA_WASAPI_WAIT_TIMEOUT_MILLISECONDS 5000 #endif static ma_result ma_device_read__wasapi(ma_device* pDevice, void* pFrames, ma_uint32 frameCount, ma_uint32* pFramesRead) { ma_result result = MA_SUCCESS; ma_uint32 totalFramesProcessed = 0; /* When reading, we need to get a buffer and process all of it before releasing it. Because the frame count (frameCount) can be different to the size of the buffer, we'll need to cache the pointer to the buffer. */ /* Keep running until we've processed the requested number of frames. */ while (ma_device_get_state(pDevice) == ma_device_state_started && totalFramesProcessed < frameCount) { ma_uint32 framesRemaining = frameCount - totalFramesProcessed; /* If we have a mapped data buffer, consume that first. */ if (pDevice->wasapi.pMappedBufferCapture != NULL) { /* We have a cached data pointer so consume that before grabbing another one from WASAPI. */ ma_uint32 framesToProcessNow = framesRemaining; if (framesToProcessNow > pDevice->wasapi.mappedBufferCaptureLen) { framesToProcessNow = pDevice->wasapi.mappedBufferCaptureLen; } /* Now just copy the data over to the output buffer. */ ma_copy_pcm_frames( ma_offset_pcm_frames_ptr(pFrames, totalFramesProcessed, pDevice->capture.internalFormat, pDevice->capture.internalChannels), ma_offset_pcm_frames_const_ptr(pDevice->wasapi.pMappedBufferCapture, pDevice->wasapi.mappedBufferCaptureCap - pDevice->wasapi.mappedBufferCaptureLen, pDevice->capture.internalFormat, pDevice->capture.internalChannels), framesToProcessNow, pDevice->capture.internalFormat, pDevice->capture.internalChannels ); totalFramesProcessed += framesToProcessNow; pDevice->wasapi.mappedBufferCaptureLen -= framesToProcessNow; /* If the data buffer has been fully consumed we need to release it. */ if (pDevice->wasapi.mappedBufferCaptureLen == 0) { ma_IAudioCaptureClient_ReleaseBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, pDevice->wasapi.mappedBufferCaptureCap); pDevice->wasapi.pMappedBufferCapture = NULL; pDevice->wasapi.mappedBufferCaptureCap = 0; } } else { /* We don't have any cached data pointer, so grab another one. */ HRESULT hr; DWORD flags = 0; /* First just ask WASAPI for a data buffer. If it's not available, we'll wait for more. */ hr = ma_IAudioCaptureClient_GetBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, (BYTE**)&pDevice->wasapi.pMappedBufferCapture, &pDevice->wasapi.mappedBufferCaptureCap, &flags, NULL, NULL); if (hr == S_OK) { /* We got a data buffer. Continue to the next loop iteration which will then read from the mapped pointer. */ pDevice->wasapi.mappedBufferCaptureLen = pDevice->wasapi.mappedBufferCaptureCap; /* There have been reports that indicate that at times the AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY is reported for every call to IAudioCaptureClient_GetBuffer() above which results in spamming of the debug messages below. To partially work around this, I'm only outputting these messages when MA_DEBUG_OUTPUT is explicitly defined. The better solution would be to figure out why the flag is always getting reported. */ #if defined(MA_DEBUG_OUTPUT) { if (flags != 0) { ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Capture Flags: %ld\n", flags); if ((flags & MA_AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY) != 0) { ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Data discontinuity (possible overrun). Attempting recovery. mappedBufferCaptureCap=%d\n", pDevice->wasapi.mappedBufferCaptureCap); } } } #endif /* Overrun detection. */ if ((flags & MA_AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY) != 0) { /* Glitched. Probably due to an overrun. */ /* If we got an overrun it probably means we're straddling the end of the buffer. In normal capture mode this is the fault of the client application because they're responsible for ensuring data is processed fast enough. In duplex mode, however, the processing of audio is tied to the playback device, so this can possibly be the result of a timing de-sync. In capture mode we're not going to do any kind of recovery because the real fix is for the client application to process faster. In duplex mode, we'll treat this as a desync and reset the buffers to prevent a never-ending sequence of glitches due to straddling the end of the buffer. */ if (pDevice->type == ma_device_type_duplex) { /* Experiment: If we empty out the *entire* buffer we may end up putting ourselves into an underrun position which isn't really any better than the overrun we're probably in right now. Instead we'll just empty out about half. */ ma_uint32 i; ma_uint32 periodCount = (pDevice->wasapi.actualBufferSizeInFramesCapture / pDevice->wasapi.periodSizeInFramesCapture); ma_uint32 iterationCount = periodCount / 2; if ((periodCount % 2) > 0) { iterationCount += 1; } for (i = 0; i < iterationCount; i += 1) { hr = ma_IAudioCaptureClient_ReleaseBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, pDevice->wasapi.mappedBufferCaptureCap); if (FAILED(hr)) { ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Data discontinuity recovery: IAudioCaptureClient_ReleaseBuffer() failed with %d.\n", (int)hr); //< @r-lyeh, silence clang-cl warning break; } flags = 0; hr = ma_IAudioCaptureClient_GetBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, (BYTE**)&pDevice->wasapi.pMappedBufferCapture, &pDevice->wasapi.mappedBufferCaptureCap, &flags, NULL, NULL); if (hr == MA_AUDCLNT_S_BUFFER_EMPTY || FAILED(hr)) { /* The buffer has been completely emptied or an error occurred. In this case we'll need to reset the state of the mapped buffer which will trigger the next iteration to get a fresh buffer from WASAPI. */ pDevice->wasapi.pMappedBufferCapture = NULL; pDevice->wasapi.mappedBufferCaptureCap = 0; pDevice->wasapi.mappedBufferCaptureLen = 0; if (hr == MA_AUDCLNT_S_BUFFER_EMPTY) { if ((flags & MA_AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY) != 0) { ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Data discontinuity recovery: Buffer emptied, and data discontinuity still reported.\n"); } else { ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Data discontinuity recovery: Buffer emptied.\n"); } } if (FAILED(hr)) { ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Data discontinuity recovery: IAudioCaptureClient_GetBuffer() failed with %d.\n", (int)hr); //< @r-lyeh, silence clang-cl warning } break; } } /* If at this point we have a valid buffer mapped, make sure the buffer length is set appropriately. */ if (pDevice->wasapi.pMappedBufferCapture != NULL) { pDevice->wasapi.mappedBufferCaptureLen = pDevice->wasapi.mappedBufferCaptureCap; } } } continue; } else { if (hr == MA_AUDCLNT_S_BUFFER_EMPTY || hr == MA_AUDCLNT_E_BUFFER_ERROR) { /* No data is available. We need to wait for more. There's two situations to consider here. The first is normal capture mode. If this times out it probably means the microphone isn't delivering data for whatever reason. In this case we'll just abort the read and return whatever we were able to get. The other situations is loopback mode, in which case a timeout probably just means the nothing is playing through the speakers. */ /* Experiment: Use a shorter timeout for loopback mode. */ DWORD timeoutInMilliseconds = MA_WASAPI_WAIT_TIMEOUT_MILLISECONDS; if (pDevice->type == ma_device_type_loopback) { timeoutInMilliseconds = 10; } if (WaitForSingleObject(pDevice->wasapi.hEventCapture, timeoutInMilliseconds) != WAIT_OBJECT_0) { if (pDevice->type == ma_device_type_loopback) { continue; /* Keep waiting in loopback mode. */ } else { result = MA_ERROR; break; /* Wait failed. */ } } /* At this point we should be able to loop back to the start of the loop and try retrieving a data buffer again. */ } else { /* An error occured and we need to abort. */ ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to retrieve internal buffer from capture device in preparation for reading from the device. HRESULT = %d. Stopping device.\n", (int)hr); result = ma_result_from_HRESULT(hr); break; } } } } /* If we were unable to process the entire requested frame count, but we still have a mapped buffer, there's a good chance either an error occurred or the device was stopped mid-read. In this case we'll need to make sure the buffer is released. */ if (totalFramesProcessed < frameCount && pDevice->wasapi.pMappedBufferCapture != NULL) { ma_IAudioCaptureClient_ReleaseBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, pDevice->wasapi.mappedBufferCaptureCap); pDevice->wasapi.pMappedBufferCapture = NULL; pDevice->wasapi.mappedBufferCaptureCap = 0; pDevice->wasapi.mappedBufferCaptureLen = 0; } if (pFramesRead != NULL) { *pFramesRead = totalFramesProcessed; } return result; } static ma_result ma_device_write__wasapi(ma_device* pDevice, const void* pFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten) { ma_result result = MA_SUCCESS; ma_uint32 totalFramesProcessed = 0; /* Keep writing to the device until it's stopped or we've consumed all of our input. */ while (ma_device_get_state(pDevice) == ma_device_state_started && totalFramesProcessed < frameCount) { ma_uint32 framesRemaining = frameCount - totalFramesProcessed; /* We're going to do this in a similar way to capture. We'll first check if the cached data pointer is valid, and if so, read from that. Otherwise We will call IAudioRenderClient_GetBuffer() with a requested buffer size equal to our actual period size. If it returns AUDCLNT_E_BUFFER_TOO_LARGE it means we need to wait for some data to become available. */ if (pDevice->wasapi.pMappedBufferPlayback != NULL) { /* We still have some space available in the mapped data buffer. Write to it. */ ma_uint32 framesToProcessNow = framesRemaining; if (framesToProcessNow > (pDevice->wasapi.mappedBufferPlaybackCap - pDevice->wasapi.mappedBufferPlaybackLen)) { framesToProcessNow = (pDevice->wasapi.mappedBufferPlaybackCap - pDevice->wasapi.mappedBufferPlaybackLen); } /* Now just copy the data over to the output buffer. */ ma_copy_pcm_frames( ma_offset_pcm_frames_ptr(pDevice->wasapi.pMappedBufferPlayback, pDevice->wasapi.mappedBufferPlaybackLen, pDevice->playback.internalFormat, pDevice->playback.internalChannels), ma_offset_pcm_frames_const_ptr(pFrames, totalFramesProcessed, pDevice->playback.internalFormat, pDevice->playback.internalChannels), framesToProcessNow, pDevice->playback.internalFormat, pDevice->playback.internalChannels ); totalFramesProcessed += framesToProcessNow; pDevice->wasapi.mappedBufferPlaybackLen += framesToProcessNow; /* If the data buffer has been fully consumed we need to release it. */ if (pDevice->wasapi.mappedBufferPlaybackLen == pDevice->wasapi.mappedBufferPlaybackCap) { ma_IAudioRenderClient_ReleaseBuffer((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient, pDevice->wasapi.mappedBufferPlaybackCap, 0); pDevice->wasapi.pMappedBufferPlayback = NULL; pDevice->wasapi.mappedBufferPlaybackCap = 0; pDevice->wasapi.mappedBufferPlaybackLen = 0; /* In exclusive mode we need to wait here. Exclusive mode is weird because GetBuffer() never seems to return AUDCLNT_E_BUFFER_TOO_LARGE, which is what we normally use to determine whether or not we need to wait for more data. */ if (pDevice->playback.shareMode == ma_share_mode_exclusive) { if (WaitForSingleObject(pDevice->wasapi.hEventPlayback, MA_WASAPI_WAIT_TIMEOUT_MILLISECONDS) != WAIT_OBJECT_0) { result = MA_ERROR; break; /* Wait failed. Probably timed out. */ } } } } else { /* We don't have a mapped data buffer so we'll need to get one. */ HRESULT hr; ma_uint32 bufferSizeInFrames; /* Special rules for exclusive mode. */ if (pDevice->playback.shareMode == ma_share_mode_exclusive) { bufferSizeInFrames = pDevice->wasapi.actualBufferSizeInFramesPlayback; } else { bufferSizeInFrames = pDevice->wasapi.periodSizeInFramesPlayback; } hr = ma_IAudioRenderClient_GetBuffer((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient, bufferSizeInFrames, (BYTE**)&pDevice->wasapi.pMappedBufferPlayback); if (hr == S_OK) { /* We have data available. */ pDevice->wasapi.mappedBufferPlaybackCap = bufferSizeInFrames; pDevice->wasapi.mappedBufferPlaybackLen = 0; } else { if (hr == MA_AUDCLNT_E_BUFFER_TOO_LARGE || hr == MA_AUDCLNT_E_BUFFER_ERROR) { /* Not enough data available. We need to wait for more. */ if (WaitForSingleObject(pDevice->wasapi.hEventPlayback, MA_WASAPI_WAIT_TIMEOUT_MILLISECONDS) != WAIT_OBJECT_0) { result = MA_ERROR; break; /* Wait failed. Probably timed out. */ } } else { /* Some error occurred. We'll need to abort. */ ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to retrieve internal buffer from playback device in preparation for writing to the device. HRESULT = %d. Stopping device.\n", (int)hr); result = ma_result_from_HRESULT(hr); break; } } } } if (pFramesWritten != NULL) { *pFramesWritten = totalFramesProcessed; } return result; } static ma_result ma_device_data_loop_wakeup__wasapi(ma_device* pDevice) { MA_ASSERT(pDevice != NULL); if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex || pDevice->type == ma_device_type_loopback) { SetEvent((HANDLE)pDevice->wasapi.hEventCapture); } if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { SetEvent((HANDLE)pDevice->wasapi.hEventPlayback); } return MA_SUCCESS; } static ma_result ma_context_uninit__wasapi(ma_context* pContext) { MA_ASSERT(pContext != NULL); MA_ASSERT(pContext->backend == ma_backend_wasapi); if (pContext->wasapi.commandThread != NULL) { ma_context_command__wasapi cmd = ma_context_init_command__wasapi(MA_CONTEXT_COMMAND_QUIT__WASAPI); ma_context_post_command__wasapi(pContext, &cmd); ma_thread_wait(&pContext->wasapi.commandThread); if (pContext->wasapi.hAvrt) { ma_dlclose(pContext, pContext->wasapi.hAvrt); pContext->wasapi.hAvrt = NULL; } #if defined(MA_WIN32_UWP) { if (pContext->wasapi.hMMDevapi) { ma_dlclose(pContext, pContext->wasapi.hMMDevapi); pContext->wasapi.hMMDevapi = NULL; } } #endif /* Only after the thread has been terminated can we uninitialize the sync objects for the command thread. */ ma_semaphore_uninit(&pContext->wasapi.commandSem); ma_mutex_uninit(&pContext->wasapi.commandLock); } return MA_SUCCESS; } static ma_result ma_context_init__wasapi(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks) { ma_result result = MA_SUCCESS; MA_ASSERT(pContext != NULL); (void)pConfig; #ifdef MA_WIN32_DESKTOP /* WASAPI is only supported in Vista SP1 and newer. The reason for SP1 and not the base version of Vista is that event-driven exclusive mode does not work until SP1. Unfortunately older compilers don't define these functions so we need to dynamically load them in order to avoid a link error. */ { ma_OSVERSIONINFOEXW osvi; ma_handle kernel32DLL; ma_PFNVerifyVersionInfoW _VerifyVersionInfoW; ma_PFNVerSetConditionMask _VerSetConditionMask; kernel32DLL = ma_dlopen(pContext, "kernel32.dll"); if (kernel32DLL == NULL) { return MA_NO_BACKEND; } _VerifyVersionInfoW = (ma_PFNVerifyVersionInfoW )ma_dlsym(pContext, kernel32DLL, "VerifyVersionInfoW"); _VerSetConditionMask = (ma_PFNVerSetConditionMask)ma_dlsym(pContext, kernel32DLL, "VerSetConditionMask"); if (_VerifyVersionInfoW == NULL || _VerSetConditionMask == NULL) { ma_dlclose(pContext, kernel32DLL); return MA_NO_BACKEND; } MA_ZERO_OBJECT(&osvi); osvi.dwOSVersionInfoSize = sizeof(osvi); osvi.dwMajorVersion = ((MA_WIN32_WINNT_VISTA >> 8) & 0xFF); osvi.dwMinorVersion = ((MA_WIN32_WINNT_VISTA >> 0) & 0xFF); osvi.wServicePackMajor = 1; if (_VerifyVersionInfoW(&osvi, MA_VER_MAJORVERSION | MA_VER_MINORVERSION | MA_VER_SERVICEPACKMAJOR, _VerSetConditionMask(_VerSetConditionMask(_VerSetConditionMask(0, MA_VER_MAJORVERSION, MA_VER_GREATER_EQUAL), MA_VER_MINORVERSION, MA_VER_GREATER_EQUAL), MA_VER_SERVICEPACKMAJOR, MA_VER_GREATER_EQUAL))) { result = MA_SUCCESS; } else { result = MA_NO_BACKEND; } ma_dlclose(pContext, kernel32DLL); } #endif if (result != MA_SUCCESS) { return result; } MA_ZERO_OBJECT(&pContext->wasapi); /* Annoyingly, WASAPI does not allow you to release an IAudioClient object from a different thread than the one that retrieved it with GetService(). This can result in a deadlock in two situations: 1) When calling ma_device_uninit() from a different thread to ma_device_init(); and 2) When uninitializing and reinitializing the internal IAudioClient object in response to automatic stream routing. We could define ma_device_uninit() such that it must be called on the same thread as ma_device_init(). We could also just not release the IAudioClient when performing automatic stream routing to avoid the deadlock. Neither of these are acceptable solutions in my view so we're going to have to work around this with a worker thread. This is not ideal, but I can't think of a better way to do this. More information about this can be found here: https://docs.microsoft.com/en-us/windows/win32/api/audioclient/nn-audioclient-iaudiorenderclient Note this section: When releasing an IAudioRenderClient interface instance, the client must call the interface's Release method from the same thread as the call to IAudioClient::GetService that created the object. */ { result = ma_mutex_init(&pContext->wasapi.commandLock); if (result != MA_SUCCESS) { return result; } result = ma_semaphore_init(0, &pContext->wasapi.commandSem); if (result != MA_SUCCESS) { ma_mutex_uninit(&pContext->wasapi.commandLock); return result; } result = ma_thread_create(&pContext->wasapi.commandThread, ma_thread_priority_normal, 0, ma_context_command_thread__wasapi, pContext, &pContext->allocationCallbacks); if (result != MA_SUCCESS) { ma_semaphore_uninit(&pContext->wasapi.commandSem); ma_mutex_uninit(&pContext->wasapi.commandLock); return result; } #if defined(MA_WIN32_UWP) { /* Link to mmdevapi so we can get access to ActivateAudioInterfaceAsync(). */ pContext->wasapi.hMMDevapi = ma_dlopen(pContext, "mmdevapi.dll"); if (pContext->wasapi.hMMDevapi) { pContext->wasapi.ActivateAudioInterfaceAsync = ma_dlsym(pContext, pContext->wasapi.hMMDevapi, "ActivateAudioInterfaceAsync"); if (pContext->wasapi.ActivateAudioInterfaceAsync == NULL) { ma_semaphore_uninit(&pContext->wasapi.commandSem); ma_mutex_uninit(&pContext->wasapi.commandLock); ma_dlclose(pContext, pContext->wasapi.hMMDevapi); return MA_NO_BACKEND; /* ActivateAudioInterfaceAsync() could not be loaded. */ } } else { ma_semaphore_uninit(&pContext->wasapi.commandSem); ma_mutex_uninit(&pContext->wasapi.commandLock); return MA_NO_BACKEND; /* Failed to load mmdevapi.dll which is required for ActivateAudioInterfaceAsync() */ } } #endif /* Optionally use the Avrt API to specify the audio thread's latency sensitivity requirements */ pContext->wasapi.hAvrt = ma_dlopen(pContext, "avrt.dll"); if (pContext->wasapi.hAvrt) { pContext->wasapi.AvSetMmThreadCharacteristicsW = ma_dlsym(pContext, pContext->wasapi.hAvrt, "AvSetMmThreadCharacteristicsW"); pContext->wasapi.AvRevertMmThreadcharacteristics = ma_dlsym(pContext, pContext->wasapi.hAvrt, "AvRevertMmThreadCharacteristics"); /* If either function could not be found, disable use of avrt entirely. */ if (!pContext->wasapi.AvSetMmThreadCharacteristicsW || !pContext->wasapi.AvRevertMmThreadcharacteristics) { pContext->wasapi.AvSetMmThreadCharacteristicsW = NULL; pContext->wasapi.AvRevertMmThreadcharacteristics = NULL; ma_dlclose(pContext, pContext->wasapi.hAvrt); pContext->wasapi.hAvrt = NULL; } } } pCallbacks->onContextInit = ma_context_init__wasapi; pCallbacks->onContextUninit = ma_context_uninit__wasapi; pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__wasapi; pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__wasapi; pCallbacks->onDeviceInit = ma_device_init__wasapi; pCallbacks->onDeviceUninit = ma_device_uninit__wasapi; pCallbacks->onDeviceStart = ma_device_start__wasapi; pCallbacks->onDeviceStop = ma_device_stop__wasapi; pCallbacks->onDeviceRead = ma_device_read__wasapi; pCallbacks->onDeviceWrite = ma_device_write__wasapi; pCallbacks->onDeviceDataLoop = NULL; pCallbacks->onDeviceDataLoopWakeup = ma_device_data_loop_wakeup__wasapi; return MA_SUCCESS; } #endif /****************************************************************************** DirectSound Backend ******************************************************************************/ #ifdef MA_HAS_DSOUND /*#include */ /*static const GUID MA_GUID_IID_DirectSoundNotify = {0xb0210783, 0x89cd, 0x11d0, {0xaf, 0x08, 0x00, 0xa0, 0xc9, 0x25, 0xcd, 0x16}};*/ /* miniaudio only uses priority or exclusive modes. */ #define MA_DSSCL_NORMAL 1 #define MA_DSSCL_PRIORITY 2 #define MA_DSSCL_EXCLUSIVE 3 #define MA_DSSCL_WRITEPRIMARY 4 #define MA_DSCAPS_PRIMARYMONO 0x00000001 #define MA_DSCAPS_PRIMARYSTEREO 0x00000002 #define MA_DSCAPS_PRIMARY8BIT 0x00000004 #define MA_DSCAPS_PRIMARY16BIT 0x00000008 #define MA_DSCAPS_CONTINUOUSRATE 0x00000010 #define MA_DSCAPS_EMULDRIVER 0x00000020 #define MA_DSCAPS_CERTIFIED 0x00000040 #define MA_DSCAPS_SECONDARYMONO 0x00000100 #define MA_DSCAPS_SECONDARYSTEREO 0x00000200 #define MA_DSCAPS_SECONDARY8BIT 0x00000400 #define MA_DSCAPS_SECONDARY16BIT 0x00000800 #define MA_DSBCAPS_PRIMARYBUFFER 0x00000001 #define MA_DSBCAPS_STATIC 0x00000002 #define MA_DSBCAPS_LOCHARDWARE 0x00000004 #define MA_DSBCAPS_LOCSOFTWARE 0x00000008 #define MA_DSBCAPS_CTRL3D 0x00000010 #define MA_DSBCAPS_CTRLFREQUENCY 0x00000020 #define MA_DSBCAPS_CTRLPAN 0x00000040 #define MA_DSBCAPS_CTRLVOLUME 0x00000080 #define MA_DSBCAPS_CTRLPOSITIONNOTIFY 0x00000100 #define MA_DSBCAPS_CTRLFX 0x00000200 #define MA_DSBCAPS_STICKYFOCUS 0x00004000 #define MA_DSBCAPS_GLOBALFOCUS 0x00008000 #define MA_DSBCAPS_GETCURRENTPOSITION2 0x00010000 #define MA_DSBCAPS_MUTE3DATMAXDISTANCE 0x00020000 #define MA_DSBCAPS_LOCDEFER 0x00040000 #define MA_DSBCAPS_TRUEPLAYPOSITION 0x00080000 #define MA_DSBPLAY_LOOPING 0x00000001 #define MA_DSBPLAY_LOCHARDWARE 0x00000002 #define MA_DSBPLAY_LOCSOFTWARE 0x00000004 #define MA_DSBPLAY_TERMINATEBY_TIME 0x00000008 #define MA_DSBPLAY_TERMINATEBY_DISTANCE 0x00000010 #define MA_DSBPLAY_TERMINATEBY_PRIORITY 0x00000020 #define MA_DSCBSTART_LOOPING 0x00000001 typedef struct { DWORD dwSize; DWORD dwFlags; DWORD dwBufferBytes; DWORD dwReserved; WAVEFORMATEX* lpwfxFormat; GUID guid3DAlgorithm; } MA_DSBUFFERDESC; typedef struct { DWORD dwSize; DWORD dwFlags; DWORD dwBufferBytes; DWORD dwReserved; WAVEFORMATEX* lpwfxFormat; DWORD dwFXCount; void* lpDSCFXDesc; /* <-- miniaudio doesn't use this, so set to void*. */ } MA_DSCBUFFERDESC; typedef struct { DWORD dwSize; DWORD dwFlags; DWORD dwMinSecondarySampleRate; DWORD dwMaxSecondarySampleRate; DWORD dwPrimaryBuffers; DWORD dwMaxHwMixingAllBuffers; DWORD dwMaxHwMixingStaticBuffers; DWORD dwMaxHwMixingStreamingBuffers; DWORD dwFreeHwMixingAllBuffers; DWORD dwFreeHwMixingStaticBuffers; DWORD dwFreeHwMixingStreamingBuffers; DWORD dwMaxHw3DAllBuffers; DWORD dwMaxHw3DStaticBuffers; DWORD dwMaxHw3DStreamingBuffers; DWORD dwFreeHw3DAllBuffers; DWORD dwFreeHw3DStaticBuffers; DWORD dwFreeHw3DStreamingBuffers; DWORD dwTotalHwMemBytes; DWORD dwFreeHwMemBytes; DWORD dwMaxContigFreeHwMemBytes; DWORD dwUnlockTransferRateHwBuffers; DWORD dwPlayCpuOverheadSwBuffers; DWORD dwReserved1; DWORD dwReserved2; } MA_DSCAPS; typedef struct { DWORD dwSize; DWORD dwFlags; DWORD dwBufferBytes; DWORD dwUnlockTransferRate; DWORD dwPlayCpuOverhead; } MA_DSBCAPS; typedef struct { DWORD dwSize; DWORD dwFlags; DWORD dwFormats; DWORD dwChannels; } MA_DSCCAPS; typedef struct { DWORD dwSize; DWORD dwFlags; DWORD dwBufferBytes; DWORD dwReserved; } MA_DSCBCAPS; typedef struct { DWORD dwOffset; HANDLE hEventNotify; } MA_DSBPOSITIONNOTIFY; typedef struct ma_IDirectSound ma_IDirectSound; typedef struct ma_IDirectSoundBuffer ma_IDirectSoundBuffer; typedef struct ma_IDirectSoundCapture ma_IDirectSoundCapture; typedef struct ma_IDirectSoundCaptureBuffer ma_IDirectSoundCaptureBuffer; typedef struct ma_IDirectSoundNotify ma_IDirectSoundNotify; /* COM objects. The way these work is that you have a vtable (a list of function pointers, kind of like how C++ works internally), and then you have a structure with a single member, which is a pointer to the vtable. The vtable is where the methods of the object are defined. Methods need to be in a specific order, and parent classes need to have their methods declared first. */ /* IDirectSound */ typedef struct { /* IUnknown */ HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IDirectSound* pThis, const IID* const riid, void** ppObject); ULONG (STDMETHODCALLTYPE * AddRef) (ma_IDirectSound* pThis); ULONG (STDMETHODCALLTYPE * Release) (ma_IDirectSound* pThis); /* IDirectSound */ HRESULT (STDMETHODCALLTYPE * CreateSoundBuffer) (ma_IDirectSound* pThis, const MA_DSBUFFERDESC* pDSBufferDesc, ma_IDirectSoundBuffer** ppDSBuffer, void* pUnkOuter); HRESULT (STDMETHODCALLTYPE * GetCaps) (ma_IDirectSound* pThis, MA_DSCAPS* pDSCaps); HRESULT (STDMETHODCALLTYPE * DuplicateSoundBuffer)(ma_IDirectSound* pThis, ma_IDirectSoundBuffer* pDSBufferOriginal, ma_IDirectSoundBuffer** ppDSBufferDuplicate); HRESULT (STDMETHODCALLTYPE * SetCooperativeLevel) (ma_IDirectSound* pThis, HWND hwnd, DWORD dwLevel); HRESULT (STDMETHODCALLTYPE * Compact) (ma_IDirectSound* pThis); HRESULT (STDMETHODCALLTYPE * GetSpeakerConfig) (ma_IDirectSound* pThis, DWORD* pSpeakerConfig); HRESULT (STDMETHODCALLTYPE * SetSpeakerConfig) (ma_IDirectSound* pThis, DWORD dwSpeakerConfig); HRESULT (STDMETHODCALLTYPE * Initialize) (ma_IDirectSound* pThis, const GUID* pGuidDevice); } ma_IDirectSoundVtbl; struct ma_IDirectSound { ma_IDirectSoundVtbl* lpVtbl; }; static MA_INLINE HRESULT ma_IDirectSound_QueryInterface(ma_IDirectSound* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } static MA_INLINE ULONG ma_IDirectSound_AddRef(ma_IDirectSound* pThis) { return pThis->lpVtbl->AddRef(pThis); } static MA_INLINE ULONG ma_IDirectSound_Release(ma_IDirectSound* pThis) { return pThis->lpVtbl->Release(pThis); } static MA_INLINE HRESULT ma_IDirectSound_CreateSoundBuffer(ma_IDirectSound* pThis, const MA_DSBUFFERDESC* pDSBufferDesc, ma_IDirectSoundBuffer** ppDSBuffer, void* pUnkOuter) { return pThis->lpVtbl->CreateSoundBuffer(pThis, pDSBufferDesc, ppDSBuffer, pUnkOuter); } static MA_INLINE HRESULT ma_IDirectSound_GetCaps(ma_IDirectSound* pThis, MA_DSCAPS* pDSCaps) { return pThis->lpVtbl->GetCaps(pThis, pDSCaps); } static MA_INLINE HRESULT ma_IDirectSound_DuplicateSoundBuffer(ma_IDirectSound* pThis, ma_IDirectSoundBuffer* pDSBufferOriginal, ma_IDirectSoundBuffer** ppDSBufferDuplicate) { return pThis->lpVtbl->DuplicateSoundBuffer(pThis, pDSBufferOriginal, ppDSBufferDuplicate); } static MA_INLINE HRESULT ma_IDirectSound_SetCooperativeLevel(ma_IDirectSound* pThis, HWND hwnd, DWORD dwLevel) { return pThis->lpVtbl->SetCooperativeLevel(pThis, hwnd, dwLevel); } static MA_INLINE HRESULT ma_IDirectSound_Compact(ma_IDirectSound* pThis) { return pThis->lpVtbl->Compact(pThis); } static MA_INLINE HRESULT ma_IDirectSound_GetSpeakerConfig(ma_IDirectSound* pThis, DWORD* pSpeakerConfig) { return pThis->lpVtbl->GetSpeakerConfig(pThis, pSpeakerConfig); } static MA_INLINE HRESULT ma_IDirectSound_SetSpeakerConfig(ma_IDirectSound* pThis, DWORD dwSpeakerConfig) { return pThis->lpVtbl->SetSpeakerConfig(pThis, dwSpeakerConfig); } static MA_INLINE HRESULT ma_IDirectSound_Initialize(ma_IDirectSound* pThis, const GUID* pGuidDevice) { return pThis->lpVtbl->Initialize(pThis, pGuidDevice); } /* IDirectSoundBuffer */ typedef struct { /* IUnknown */ HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IDirectSoundBuffer* pThis, const IID* const riid, void** ppObject); ULONG (STDMETHODCALLTYPE * AddRef) (ma_IDirectSoundBuffer* pThis); ULONG (STDMETHODCALLTYPE * Release) (ma_IDirectSoundBuffer* pThis); /* IDirectSoundBuffer */ HRESULT (STDMETHODCALLTYPE * GetCaps) (ma_IDirectSoundBuffer* pThis, MA_DSBCAPS* pDSBufferCaps); HRESULT (STDMETHODCALLTYPE * GetCurrentPosition)(ma_IDirectSoundBuffer* pThis, DWORD* pCurrentPlayCursor, DWORD* pCurrentWriteCursor); HRESULT (STDMETHODCALLTYPE * GetFormat) (ma_IDirectSoundBuffer* pThis, WAVEFORMATEX* pFormat, DWORD dwSizeAllocated, DWORD* pSizeWritten); HRESULT (STDMETHODCALLTYPE * GetVolume) (ma_IDirectSoundBuffer* pThis, LONG* pVolume); HRESULT (STDMETHODCALLTYPE * GetPan) (ma_IDirectSoundBuffer* pThis, LONG* pPan); HRESULT (STDMETHODCALLTYPE * GetFrequency) (ma_IDirectSoundBuffer* pThis, DWORD* pFrequency); HRESULT (STDMETHODCALLTYPE * GetStatus) (ma_IDirectSoundBuffer* pThis, DWORD* pStatus); HRESULT (STDMETHODCALLTYPE * Initialize) (ma_IDirectSoundBuffer* pThis, ma_IDirectSound* pDirectSound, const MA_DSBUFFERDESC* pDSBufferDesc); HRESULT (STDMETHODCALLTYPE * Lock) (ma_IDirectSoundBuffer* pThis, DWORD dwOffset, DWORD dwBytes, void** ppAudioPtr1, DWORD* pAudioBytes1, void** ppAudioPtr2, DWORD* pAudioBytes2, DWORD dwFlags); HRESULT (STDMETHODCALLTYPE * Play) (ma_IDirectSoundBuffer* pThis, DWORD dwReserved1, DWORD dwPriority, DWORD dwFlags); HRESULT (STDMETHODCALLTYPE * SetCurrentPosition)(ma_IDirectSoundBuffer* pThis, DWORD dwNewPosition); HRESULT (STDMETHODCALLTYPE * SetFormat) (ma_IDirectSoundBuffer* pThis, const WAVEFORMATEX* pFormat); HRESULT (STDMETHODCALLTYPE * SetVolume) (ma_IDirectSoundBuffer* pThis, LONG volume); HRESULT (STDMETHODCALLTYPE * SetPan) (ma_IDirectSoundBuffer* pThis, LONG pan); HRESULT (STDMETHODCALLTYPE * SetFrequency) (ma_IDirectSoundBuffer* pThis, DWORD dwFrequency); HRESULT (STDMETHODCALLTYPE * Stop) (ma_IDirectSoundBuffer* pThis); HRESULT (STDMETHODCALLTYPE * Unlock) (ma_IDirectSoundBuffer* pThis, void* pAudioPtr1, DWORD dwAudioBytes1, void* pAudioPtr2, DWORD dwAudioBytes2); HRESULT (STDMETHODCALLTYPE * Restore) (ma_IDirectSoundBuffer* pThis); } ma_IDirectSoundBufferVtbl; struct ma_IDirectSoundBuffer { ma_IDirectSoundBufferVtbl* lpVtbl; }; static MA_INLINE HRESULT ma_IDirectSoundBuffer_QueryInterface(ma_IDirectSoundBuffer* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } static MA_INLINE ULONG ma_IDirectSoundBuffer_AddRef(ma_IDirectSoundBuffer* pThis) { return pThis->lpVtbl->AddRef(pThis); } static MA_INLINE ULONG ma_IDirectSoundBuffer_Release(ma_IDirectSoundBuffer* pThis) { return pThis->lpVtbl->Release(pThis); } static MA_INLINE HRESULT ma_IDirectSoundBuffer_GetCaps(ma_IDirectSoundBuffer* pThis, MA_DSBCAPS* pDSBufferCaps) { return pThis->lpVtbl->GetCaps(pThis, pDSBufferCaps); } static MA_INLINE HRESULT ma_IDirectSoundBuffer_GetCurrentPosition(ma_IDirectSoundBuffer* pThis, DWORD* pCurrentPlayCursor, DWORD* pCurrentWriteCursor) { return pThis->lpVtbl->GetCurrentPosition(pThis, pCurrentPlayCursor, pCurrentWriteCursor); } static MA_INLINE HRESULT ma_IDirectSoundBuffer_GetFormat(ma_IDirectSoundBuffer* pThis, WAVEFORMATEX* pFormat, DWORD dwSizeAllocated, DWORD* pSizeWritten) { return pThis->lpVtbl->GetFormat(pThis, pFormat, dwSizeAllocated, pSizeWritten); } static MA_INLINE HRESULT ma_IDirectSoundBuffer_GetVolume(ma_IDirectSoundBuffer* pThis, LONG* pVolume) { return pThis->lpVtbl->GetVolume(pThis, pVolume); } static MA_INLINE HRESULT ma_IDirectSoundBuffer_GetPan(ma_IDirectSoundBuffer* pThis, LONG* pPan) { return pThis->lpVtbl->GetPan(pThis, pPan); } static MA_INLINE HRESULT ma_IDirectSoundBuffer_GetFrequency(ma_IDirectSoundBuffer* pThis, DWORD* pFrequency) { return pThis->lpVtbl->GetFrequency(pThis, pFrequency); } static MA_INLINE HRESULT ma_IDirectSoundBuffer_GetStatus(ma_IDirectSoundBuffer* pThis, DWORD* pStatus) { return pThis->lpVtbl->GetStatus(pThis, pStatus); } static MA_INLINE HRESULT ma_IDirectSoundBuffer_Initialize(ma_IDirectSoundBuffer* pThis, ma_IDirectSound* pDirectSound, const MA_DSBUFFERDESC* pDSBufferDesc) { return pThis->lpVtbl->Initialize(pThis, pDirectSound, pDSBufferDesc); } static MA_INLINE HRESULT ma_IDirectSoundBuffer_Lock(ma_IDirectSoundBuffer* pThis, DWORD dwOffset, DWORD dwBytes, void** ppAudioPtr1, DWORD* pAudioBytes1, void** ppAudioPtr2, DWORD* pAudioBytes2, DWORD dwFlags) { return pThis->lpVtbl->Lock(pThis, dwOffset, dwBytes, ppAudioPtr1, pAudioBytes1, ppAudioPtr2, pAudioBytes2, dwFlags); } static MA_INLINE HRESULT ma_IDirectSoundBuffer_Play(ma_IDirectSoundBuffer* pThis, DWORD dwReserved1, DWORD dwPriority, DWORD dwFlags) { return pThis->lpVtbl->Play(pThis, dwReserved1, dwPriority, dwFlags); } static MA_INLINE HRESULT ma_IDirectSoundBuffer_SetCurrentPosition(ma_IDirectSoundBuffer* pThis, DWORD dwNewPosition) { return pThis->lpVtbl->SetCurrentPosition(pThis, dwNewPosition); } static MA_INLINE HRESULT ma_IDirectSoundBuffer_SetFormat(ma_IDirectSoundBuffer* pThis, const WAVEFORMATEX* pFormat) { return pThis->lpVtbl->SetFormat(pThis, pFormat); } static MA_INLINE HRESULT ma_IDirectSoundBuffer_SetVolume(ma_IDirectSoundBuffer* pThis, LONG volume) { return pThis->lpVtbl->SetVolume(pThis, volume); } static MA_INLINE HRESULT ma_IDirectSoundBuffer_SetPan(ma_IDirectSoundBuffer* pThis, LONG pan) { return pThis->lpVtbl->SetPan(pThis, pan); } static MA_INLINE HRESULT ma_IDirectSoundBuffer_SetFrequency(ma_IDirectSoundBuffer* pThis, DWORD dwFrequency) { return pThis->lpVtbl->SetFrequency(pThis, dwFrequency); } static MA_INLINE HRESULT ma_IDirectSoundBuffer_Stop(ma_IDirectSoundBuffer* pThis) { return pThis->lpVtbl->Stop(pThis); } static MA_INLINE HRESULT ma_IDirectSoundBuffer_Unlock(ma_IDirectSoundBuffer* pThis, void* pAudioPtr1, DWORD dwAudioBytes1, void* pAudioPtr2, DWORD dwAudioBytes2) { return pThis->lpVtbl->Unlock(pThis, pAudioPtr1, dwAudioBytes1, pAudioPtr2, dwAudioBytes2); } static MA_INLINE HRESULT ma_IDirectSoundBuffer_Restore(ma_IDirectSoundBuffer* pThis) { return pThis->lpVtbl->Restore(pThis); } /* IDirectSoundCapture */ typedef struct { /* IUnknown */ HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IDirectSoundCapture* pThis, const IID* const riid, void** ppObject); ULONG (STDMETHODCALLTYPE * AddRef) (ma_IDirectSoundCapture* pThis); ULONG (STDMETHODCALLTYPE * Release) (ma_IDirectSoundCapture* pThis); /* IDirectSoundCapture */ HRESULT (STDMETHODCALLTYPE * CreateCaptureBuffer)(ma_IDirectSoundCapture* pThis, const MA_DSCBUFFERDESC* pDSCBufferDesc, ma_IDirectSoundCaptureBuffer** ppDSCBuffer, void* pUnkOuter); HRESULT (STDMETHODCALLTYPE * GetCaps) (ma_IDirectSoundCapture* pThis, MA_DSCCAPS* pDSCCaps); HRESULT (STDMETHODCALLTYPE * Initialize) (ma_IDirectSoundCapture* pThis, const GUID* pGuidDevice); } ma_IDirectSoundCaptureVtbl; struct ma_IDirectSoundCapture { ma_IDirectSoundCaptureVtbl* lpVtbl; }; static MA_INLINE HRESULT ma_IDirectSoundCapture_QueryInterface (ma_IDirectSoundCapture* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } static MA_INLINE ULONG ma_IDirectSoundCapture_AddRef (ma_IDirectSoundCapture* pThis) { return pThis->lpVtbl->AddRef(pThis); } static MA_INLINE ULONG ma_IDirectSoundCapture_Release (ma_IDirectSoundCapture* pThis) { return pThis->lpVtbl->Release(pThis); } static MA_INLINE HRESULT ma_IDirectSoundCapture_CreateCaptureBuffer(ma_IDirectSoundCapture* pThis, const MA_DSCBUFFERDESC* pDSCBufferDesc, ma_IDirectSoundCaptureBuffer** ppDSCBuffer, void* pUnkOuter) { return pThis->lpVtbl->CreateCaptureBuffer(pThis, pDSCBufferDesc, ppDSCBuffer, pUnkOuter); } static MA_INLINE HRESULT ma_IDirectSoundCapture_GetCaps (ma_IDirectSoundCapture* pThis, MA_DSCCAPS* pDSCCaps) { return pThis->lpVtbl->GetCaps(pThis, pDSCCaps); } static MA_INLINE HRESULT ma_IDirectSoundCapture_Initialize (ma_IDirectSoundCapture* pThis, const GUID* pGuidDevice) { return pThis->lpVtbl->Initialize(pThis, pGuidDevice); } /* IDirectSoundCaptureBuffer */ typedef struct { /* IUnknown */ HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IDirectSoundCaptureBuffer* pThis, const IID* const riid, void** ppObject); ULONG (STDMETHODCALLTYPE * AddRef) (ma_IDirectSoundCaptureBuffer* pThis); ULONG (STDMETHODCALLTYPE * Release) (ma_IDirectSoundCaptureBuffer* pThis); /* IDirectSoundCaptureBuffer */ HRESULT (STDMETHODCALLTYPE * GetCaps) (ma_IDirectSoundCaptureBuffer* pThis, MA_DSCBCAPS* pDSCBCaps); HRESULT (STDMETHODCALLTYPE * GetCurrentPosition)(ma_IDirectSoundCaptureBuffer* pThis, DWORD* pCapturePosition, DWORD* pReadPosition); HRESULT (STDMETHODCALLTYPE * GetFormat) (ma_IDirectSoundCaptureBuffer* pThis, WAVEFORMATEX* pFormat, DWORD dwSizeAllocated, DWORD* pSizeWritten); HRESULT (STDMETHODCALLTYPE * GetStatus) (ma_IDirectSoundCaptureBuffer* pThis, DWORD* pStatus); HRESULT (STDMETHODCALLTYPE * Initialize) (ma_IDirectSoundCaptureBuffer* pThis, ma_IDirectSoundCapture* pDirectSoundCapture, const MA_DSCBUFFERDESC* pDSCBufferDesc); HRESULT (STDMETHODCALLTYPE * Lock) (ma_IDirectSoundCaptureBuffer* pThis, DWORD dwOffset, DWORD dwBytes, void** ppAudioPtr1, DWORD* pAudioBytes1, void** ppAudioPtr2, DWORD* pAudioBytes2, DWORD dwFlags); HRESULT (STDMETHODCALLTYPE * Start) (ma_IDirectSoundCaptureBuffer* pThis, DWORD dwFlags); HRESULT (STDMETHODCALLTYPE * Stop) (ma_IDirectSoundCaptureBuffer* pThis); HRESULT (STDMETHODCALLTYPE * Unlock) (ma_IDirectSoundCaptureBuffer* pThis, void* pAudioPtr1, DWORD dwAudioBytes1, void* pAudioPtr2, DWORD dwAudioBytes2); } ma_IDirectSoundCaptureBufferVtbl; struct ma_IDirectSoundCaptureBuffer { ma_IDirectSoundCaptureBufferVtbl* lpVtbl; }; static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_QueryInterface(ma_IDirectSoundCaptureBuffer* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } static MA_INLINE ULONG ma_IDirectSoundCaptureBuffer_AddRef(ma_IDirectSoundCaptureBuffer* pThis) { return pThis->lpVtbl->AddRef(pThis); } static MA_INLINE ULONG ma_IDirectSoundCaptureBuffer_Release(ma_IDirectSoundCaptureBuffer* pThis) { return pThis->lpVtbl->Release(pThis); } static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_GetCaps(ma_IDirectSoundCaptureBuffer* pThis, MA_DSCBCAPS* pDSCBCaps) { return pThis->lpVtbl->GetCaps(pThis, pDSCBCaps); } static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_GetCurrentPosition(ma_IDirectSoundCaptureBuffer* pThis, DWORD* pCapturePosition, DWORD* pReadPosition) { return pThis->lpVtbl->GetCurrentPosition(pThis, pCapturePosition, pReadPosition); } static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_GetFormat(ma_IDirectSoundCaptureBuffer* pThis, WAVEFORMATEX* pFormat, DWORD dwSizeAllocated, DWORD* pSizeWritten) { return pThis->lpVtbl->GetFormat(pThis, pFormat, dwSizeAllocated, pSizeWritten); } static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_GetStatus(ma_IDirectSoundCaptureBuffer* pThis, DWORD* pStatus) { return pThis->lpVtbl->GetStatus(pThis, pStatus); } static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_Initialize(ma_IDirectSoundCaptureBuffer* pThis, ma_IDirectSoundCapture* pDirectSoundCapture, const MA_DSCBUFFERDESC* pDSCBufferDesc) { return pThis->lpVtbl->Initialize(pThis, pDirectSoundCapture, pDSCBufferDesc); } static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_Lock(ma_IDirectSoundCaptureBuffer* pThis, DWORD dwOffset, DWORD dwBytes, void** ppAudioPtr1, DWORD* pAudioBytes1, void** ppAudioPtr2, DWORD* pAudioBytes2, DWORD dwFlags) { return pThis->lpVtbl->Lock(pThis, dwOffset, dwBytes, ppAudioPtr1, pAudioBytes1, ppAudioPtr2, pAudioBytes2, dwFlags); } static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_Start(ma_IDirectSoundCaptureBuffer* pThis, DWORD dwFlags) { return pThis->lpVtbl->Start(pThis, dwFlags); } static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_Stop(ma_IDirectSoundCaptureBuffer* pThis) { return pThis->lpVtbl->Stop(pThis); } static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_Unlock(ma_IDirectSoundCaptureBuffer* pThis, void* pAudioPtr1, DWORD dwAudioBytes1, void* pAudioPtr2, DWORD dwAudioBytes2) { return pThis->lpVtbl->Unlock(pThis, pAudioPtr1, dwAudioBytes1, pAudioPtr2, dwAudioBytes2); } /* IDirectSoundNotify */ typedef struct { /* IUnknown */ HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IDirectSoundNotify* pThis, const IID* const riid, void** ppObject); ULONG (STDMETHODCALLTYPE * AddRef) (ma_IDirectSoundNotify* pThis); ULONG (STDMETHODCALLTYPE * Release) (ma_IDirectSoundNotify* pThis); /* IDirectSoundNotify */ HRESULT (STDMETHODCALLTYPE * SetNotificationPositions)(ma_IDirectSoundNotify* pThis, DWORD dwPositionNotifies, const MA_DSBPOSITIONNOTIFY* pPositionNotifies); } ma_IDirectSoundNotifyVtbl; struct ma_IDirectSoundNotify { ma_IDirectSoundNotifyVtbl* lpVtbl; }; static MA_INLINE HRESULT ma_IDirectSoundNotify_QueryInterface(ma_IDirectSoundNotify* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } static MA_INLINE ULONG ma_IDirectSoundNotify_AddRef(ma_IDirectSoundNotify* pThis) { return pThis->lpVtbl->AddRef(pThis); } static MA_INLINE ULONG ma_IDirectSoundNotify_Release(ma_IDirectSoundNotify* pThis) { return pThis->lpVtbl->Release(pThis); } static MA_INLINE HRESULT ma_IDirectSoundNotify_SetNotificationPositions(ma_IDirectSoundNotify* pThis, DWORD dwPositionNotifies, const MA_DSBPOSITIONNOTIFY* pPositionNotifies) { return pThis->lpVtbl->SetNotificationPositions(pThis, dwPositionNotifies, pPositionNotifies); } typedef BOOL (CALLBACK * ma_DSEnumCallbackAProc) (LPGUID pDeviceGUID, LPCSTR pDeviceDescription, LPCSTR pModule, LPVOID pContext); typedef HRESULT (WINAPI * ma_DirectSoundCreateProc) (const GUID* pcGuidDevice, ma_IDirectSound** ppDS8, LPUNKNOWN pUnkOuter); typedef HRESULT (WINAPI * ma_DirectSoundEnumerateAProc) (ma_DSEnumCallbackAProc pDSEnumCallback, LPVOID pContext); typedef HRESULT (WINAPI * ma_DirectSoundCaptureCreateProc) (const GUID* pcGuidDevice, ma_IDirectSoundCapture** ppDSC8, LPUNKNOWN pUnkOuter); typedef HRESULT (WINAPI * ma_DirectSoundCaptureEnumerateAProc)(ma_DSEnumCallbackAProc pDSEnumCallback, LPVOID pContext); static ma_uint32 ma_get_best_sample_rate_within_range(ma_uint32 sampleRateMin, ma_uint32 sampleRateMax) { /* Normalize the range in case we were given something stupid. */ if (sampleRateMin < (ma_uint32)ma_standard_sample_rate_min) { sampleRateMin = (ma_uint32)ma_standard_sample_rate_min; } if (sampleRateMax > (ma_uint32)ma_standard_sample_rate_max) { sampleRateMax = (ma_uint32)ma_standard_sample_rate_max; } if (sampleRateMin > sampleRateMax) { sampleRateMin = sampleRateMax; } if (sampleRateMin == sampleRateMax) { return sampleRateMax; } else { size_t iStandardRate; for (iStandardRate = 0; iStandardRate < ma_countof(g_maStandardSampleRatePriorities); ++iStandardRate) { ma_uint32 standardRate = g_maStandardSampleRatePriorities[iStandardRate]; if (standardRate >= sampleRateMin && standardRate <= sampleRateMax) { return standardRate; } } } /* Should never get here. */ MA_ASSERT(MA_FALSE); return 0; } /* Retrieves the channel count and channel map for the given speaker configuration. If the speaker configuration is unknown, the channel count and channel map will be left unmodified. */ static void ma_get_channels_from_speaker_config__dsound(DWORD speakerConfig, WORD* pChannelsOut, DWORD* pChannelMapOut) { WORD channels; DWORD channelMap; channels = 0; if (pChannelsOut != NULL) { channels = *pChannelsOut; } channelMap = 0; if (pChannelMapOut != NULL) { channelMap = *pChannelMapOut; } /* The speaker configuration is a combination of speaker config and speaker geometry. The lower 8 bits is what we care about. The upper 16 bits is for the geometry. */ switch ((BYTE)(speakerConfig)) { case 1 /*DSSPEAKER_HEADPHONE*/: channels = 2; channelMap = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT; break; case 2 /*DSSPEAKER_MONO*/: channels = 1; channelMap = SPEAKER_FRONT_CENTER; break; case 3 /*DSSPEAKER_QUAD*/: channels = 4; channelMap = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT; break; case 4 /*DSSPEAKER_STEREO*/: channels = 2; channelMap = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT; break; case 5 /*DSSPEAKER_SURROUND*/: channels = 4; channelMap = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_BACK_CENTER; break; case 6 /*DSSPEAKER_5POINT1_BACK*/ /*DSSPEAKER_5POINT1*/: channels = 6; channelMap = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT; break; case 7 /*DSSPEAKER_7POINT1_WIDE*/ /*DSSPEAKER_7POINT1*/: channels = 8; channelMap = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT | SPEAKER_FRONT_LEFT_OF_CENTER | SPEAKER_FRONT_RIGHT_OF_CENTER; break; case 8 /*DSSPEAKER_7POINT1_SURROUND*/: channels = 8; channelMap = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT | SPEAKER_SIDE_LEFT | SPEAKER_SIDE_RIGHT; break; case 9 /*DSSPEAKER_5POINT1_SURROUND*/: channels = 6; channelMap = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | SPEAKER_SIDE_LEFT | SPEAKER_SIDE_RIGHT; break; default: break; } if (pChannelsOut != NULL) { *pChannelsOut = channels; } if (pChannelMapOut != NULL) { *pChannelMapOut = channelMap; } } static ma_result ma_context_create_IDirectSound__dsound(ma_context* pContext, ma_share_mode shareMode, const ma_device_id* pDeviceID, ma_IDirectSound** ppDirectSound) { ma_IDirectSound* pDirectSound; HWND hWnd; HRESULT hr; MA_ASSERT(pContext != NULL); MA_ASSERT(ppDirectSound != NULL); *ppDirectSound = NULL; pDirectSound = NULL; if (FAILED(((ma_DirectSoundCreateProc)pContext->dsound.DirectSoundCreate)((pDeviceID == NULL) ? NULL : (const GUID*)pDeviceID->dsound, &pDirectSound, NULL))) { ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[DirectSound] DirectSoundCreate() failed for playback device."); return MA_FAILED_TO_OPEN_BACKEND_DEVICE; } /* The cooperative level must be set before doing anything else. */ hWnd = ((MA_PFN_GetForegroundWindow)pContext->win32.GetForegroundWindow)(); if (hWnd == NULL) { hWnd = ((MA_PFN_GetDesktopWindow)pContext->win32.GetDesktopWindow)(); } hr = ma_IDirectSound_SetCooperativeLevel(pDirectSound, hWnd, (shareMode == ma_share_mode_exclusive) ? MA_DSSCL_EXCLUSIVE : MA_DSSCL_PRIORITY); if (FAILED(hr)) { ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSound_SetCooperateiveLevel() failed for playback device."); return ma_result_from_HRESULT(hr); } *ppDirectSound = pDirectSound; return MA_SUCCESS; } static ma_result ma_context_create_IDirectSoundCapture__dsound(ma_context* pContext, ma_share_mode shareMode, const ma_device_id* pDeviceID, ma_IDirectSoundCapture** ppDirectSoundCapture) { ma_IDirectSoundCapture* pDirectSoundCapture; HRESULT hr; MA_ASSERT(pContext != NULL); MA_ASSERT(ppDirectSoundCapture != NULL); /* DirectSound does not support exclusive mode for capture. */ if (shareMode == ma_share_mode_exclusive) { return MA_SHARE_MODE_NOT_SUPPORTED; } *ppDirectSoundCapture = NULL; pDirectSoundCapture = NULL; hr = ((ma_DirectSoundCaptureCreateProc)pContext->dsound.DirectSoundCaptureCreate)((pDeviceID == NULL) ? NULL : (const GUID*)pDeviceID->dsound, &pDirectSoundCapture, NULL); if (FAILED(hr)) { ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[DirectSound] DirectSoundCaptureCreate() failed for capture device."); return ma_result_from_HRESULT(hr); } *ppDirectSoundCapture = pDirectSoundCapture; return MA_SUCCESS; } static ma_result ma_context_get_format_info_for_IDirectSoundCapture__dsound(ma_context* pContext, ma_IDirectSoundCapture* pDirectSoundCapture, WORD* pChannels, WORD* pBitsPerSample, DWORD* pSampleRate) { HRESULT hr; MA_DSCCAPS caps; WORD bitsPerSample; DWORD sampleRate; MA_ASSERT(pContext != NULL); MA_ASSERT(pDirectSoundCapture != NULL); if (pChannels) { *pChannels = 0; } if (pBitsPerSample) { *pBitsPerSample = 0; } if (pSampleRate) { *pSampleRate = 0; } MA_ZERO_OBJECT(&caps); caps.dwSize = sizeof(caps); hr = ma_IDirectSoundCapture_GetCaps(pDirectSoundCapture, &caps); if (FAILED(hr)) { ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundCapture_GetCaps() failed for capture device."); return ma_result_from_HRESULT(hr); } if (pChannels) { *pChannels = (WORD)caps.dwChannels; } /* The device can support multiple formats. We just go through the different formats in order of priority and pick the first one. This the same type of system as the WinMM backend. */ bitsPerSample = 16; sampleRate = 48000; if (caps.dwChannels == 1) { if ((caps.dwFormats & WAVE_FORMAT_48M16) != 0) { sampleRate = 48000; } else if ((caps.dwFormats & WAVE_FORMAT_44M16) != 0) { sampleRate = 44100; } else if ((caps.dwFormats & WAVE_FORMAT_2M16) != 0) { sampleRate = 22050; } else if ((caps.dwFormats & WAVE_FORMAT_1M16) != 0) { sampleRate = 11025; } else if ((caps.dwFormats & WAVE_FORMAT_96M16) != 0) { sampleRate = 96000; } else { bitsPerSample = 8; if ((caps.dwFormats & WAVE_FORMAT_48M08) != 0) { sampleRate = 48000; } else if ((caps.dwFormats & WAVE_FORMAT_44M08) != 0) { sampleRate = 44100; } else if ((caps.dwFormats & WAVE_FORMAT_2M08) != 0) { sampleRate = 22050; } else if ((caps.dwFormats & WAVE_FORMAT_1M08) != 0) { sampleRate = 11025; } else if ((caps.dwFormats & WAVE_FORMAT_96M08) != 0) { sampleRate = 96000; } else { bitsPerSample = 16; /* Didn't find it. Just fall back to 16-bit. */ } } } else if (caps.dwChannels == 2) { if ((caps.dwFormats & WAVE_FORMAT_48S16) != 0) { sampleRate = 48000; } else if ((caps.dwFormats & WAVE_FORMAT_44S16) != 0) { sampleRate = 44100; } else if ((caps.dwFormats & WAVE_FORMAT_2S16) != 0) { sampleRate = 22050; } else if ((caps.dwFormats & WAVE_FORMAT_1S16) != 0) { sampleRate = 11025; } else if ((caps.dwFormats & WAVE_FORMAT_96S16) != 0) { sampleRate = 96000; } else { bitsPerSample = 8; if ((caps.dwFormats & WAVE_FORMAT_48S08) != 0) { sampleRate = 48000; } else if ((caps.dwFormats & WAVE_FORMAT_44S08) != 0) { sampleRate = 44100; } else if ((caps.dwFormats & WAVE_FORMAT_2S08) != 0) { sampleRate = 22050; } else if ((caps.dwFormats & WAVE_FORMAT_1S08) != 0) { sampleRate = 11025; } else if ((caps.dwFormats & WAVE_FORMAT_96S08) != 0) { sampleRate = 96000; } else { bitsPerSample = 16; /* Didn't find it. Just fall back to 16-bit. */ } } } if (pBitsPerSample) { *pBitsPerSample = bitsPerSample; } if (pSampleRate) { *pSampleRate = sampleRate; } return MA_SUCCESS; } typedef struct { ma_context* pContext; ma_device_type deviceType; ma_enum_devices_callback_proc callback; void* pUserData; ma_bool32 terminated; } ma_context_enumerate_devices_callback_data__dsound; static BOOL CALLBACK ma_context_enumerate_devices_callback__dsound(LPGUID lpGuid, LPCSTR lpcstrDescription, LPCSTR lpcstrModule, LPVOID lpContext) { ma_context_enumerate_devices_callback_data__dsound* pData = (ma_context_enumerate_devices_callback_data__dsound*)lpContext; ma_device_info deviceInfo; (void)lpcstrModule; MA_ZERO_OBJECT(&deviceInfo); /* ID. */ if (lpGuid != NULL) { MA_COPY_MEMORY(deviceInfo.id.dsound, lpGuid, 16); } else { MA_ZERO_MEMORY(deviceInfo.id.dsound, 16); deviceInfo.isDefault = MA_TRUE; } /* Name / Description */ ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), lpcstrDescription, (size_t)-1); /* Call the callback function, but make sure we stop enumerating if the callee requested so. */ MA_ASSERT(pData != NULL); pData->terminated = !pData->callback(pData->pContext, pData->deviceType, &deviceInfo, pData->pUserData); if (pData->terminated) { return FALSE; /* Stop enumeration. */ } else { return TRUE; /* Continue enumeration. */ } } static ma_result ma_context_enumerate_devices__dsound(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData) { ma_context_enumerate_devices_callback_data__dsound data; MA_ASSERT(pContext != NULL); MA_ASSERT(callback != NULL); data.pContext = pContext; data.callback = callback; data.pUserData = pUserData; data.terminated = MA_FALSE; /* Playback. */ if (!data.terminated) { data.deviceType = ma_device_type_playback; ((ma_DirectSoundEnumerateAProc)pContext->dsound.DirectSoundEnumerateA)(ma_context_enumerate_devices_callback__dsound, &data); } /* Capture. */ if (!data.terminated) { data.deviceType = ma_device_type_capture; ((ma_DirectSoundCaptureEnumerateAProc)pContext->dsound.DirectSoundCaptureEnumerateA)(ma_context_enumerate_devices_callback__dsound, &data); } return MA_SUCCESS; } typedef struct { const ma_device_id* pDeviceID; ma_device_info* pDeviceInfo; ma_bool32 found; } ma_context_get_device_info_callback_data__dsound; static BOOL CALLBACK ma_context_get_device_info_callback__dsound(LPGUID lpGuid, LPCSTR lpcstrDescription, LPCSTR lpcstrModule, LPVOID lpContext) { ma_context_get_device_info_callback_data__dsound* pData = (ma_context_get_device_info_callback_data__dsound*)lpContext; MA_ASSERT(pData != NULL); if ((pData->pDeviceID == NULL || ma_is_guid_null(pData->pDeviceID->dsound)) && (lpGuid == NULL || ma_is_guid_null(lpGuid))) { /* Default device. */ ma_strncpy_s(pData->pDeviceInfo->name, sizeof(pData->pDeviceInfo->name), lpcstrDescription, (size_t)-1); pData->pDeviceInfo->isDefault = MA_TRUE; pData->found = MA_TRUE; return FALSE; /* Stop enumeration. */ } else { /* Not the default device. */ if (lpGuid != NULL && pData->pDeviceID != NULL) { if (memcmp(pData->pDeviceID->dsound, lpGuid, sizeof(pData->pDeviceID->dsound)) == 0) { ma_strncpy_s(pData->pDeviceInfo->name, sizeof(pData->pDeviceInfo->name), lpcstrDescription, (size_t)-1); pData->found = MA_TRUE; return FALSE; /* Stop enumeration. */ } } } (void)lpcstrModule; return TRUE; } static ma_result ma_context_get_device_info__dsound(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo) { ma_result result; HRESULT hr; if (pDeviceID != NULL) { ma_context_get_device_info_callback_data__dsound data; /* ID. */ MA_COPY_MEMORY(pDeviceInfo->id.dsound, pDeviceID->dsound, 16); /* Name / Description. This is retrieved by enumerating over each device until we find that one that matches the input ID. */ data.pDeviceID = pDeviceID; data.pDeviceInfo = pDeviceInfo; data.found = MA_FALSE; if (deviceType == ma_device_type_playback) { ((ma_DirectSoundEnumerateAProc)pContext->dsound.DirectSoundEnumerateA)(ma_context_get_device_info_callback__dsound, &data); } else { ((ma_DirectSoundCaptureEnumerateAProc)pContext->dsound.DirectSoundCaptureEnumerateA)(ma_context_get_device_info_callback__dsound, &data); } if (!data.found) { return MA_NO_DEVICE; } } else { /* I don't think there's a way to get the name of the default device with DirectSound. In this case we just need to use defaults. */ /* ID */ MA_ZERO_MEMORY(pDeviceInfo->id.dsound, 16); /* Name / Description */ if (deviceType == ma_device_type_playback) { ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); } else { ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); } pDeviceInfo->isDefault = MA_TRUE; } /* Retrieving detailed information is slightly different depending on the device type. */ if (deviceType == ma_device_type_playback) { /* Playback. */ ma_IDirectSound* pDirectSound; MA_DSCAPS caps; WORD channels; result = ma_context_create_IDirectSound__dsound(pContext, ma_share_mode_shared, pDeviceID, &pDirectSound); if (result != MA_SUCCESS) { return result; } MA_ZERO_OBJECT(&caps); caps.dwSize = sizeof(caps); hr = ma_IDirectSound_GetCaps(pDirectSound, &caps); if (FAILED(hr)) { ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSound_GetCaps() failed for playback device."); return ma_result_from_HRESULT(hr); } /* Channels. Only a single channel count is reported for DirectSound. */ if ((caps.dwFlags & MA_DSCAPS_PRIMARYSTEREO) != 0) { /* It supports at least stereo, but could support more. */ DWORD speakerConfig; channels = 2; /* Look at the speaker configuration to get a better idea on the channel count. */ hr = ma_IDirectSound_GetSpeakerConfig(pDirectSound, &speakerConfig); if (SUCCEEDED(hr)) { ma_get_channels_from_speaker_config__dsound(speakerConfig, &channels, NULL); } } else { /* It does not support stereo, which means we are stuck with mono. */ channels = 1; } /* In DirectSound, our native formats are centered around sample rates. All formats are supported, and we're only reporting a single channel count. However, DirectSound can report a range of supported sample rates. We're only going to include standard rates known by miniaudio in order to keep the size of this within reason. */ if ((caps.dwFlags & MA_DSCAPS_CONTINUOUSRATE) != 0) { /* Multiple sample rates are supported. We'll report in order of our preferred sample rates. */ size_t iStandardSampleRate; for (iStandardSampleRate = 0; iStandardSampleRate < ma_countof(g_maStandardSampleRatePriorities); iStandardSampleRate += 1) { ma_uint32 sampleRate = g_maStandardSampleRatePriorities[iStandardSampleRate]; if (sampleRate >= caps.dwMinSecondarySampleRate && sampleRate <= caps.dwMaxSecondarySampleRate) { pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].format = ma_format_unknown; pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].channels = channels; pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].sampleRate = sampleRate; pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].flags = 0; pDeviceInfo->nativeDataFormatCount += 1; } } } else { /* Only a single sample rate is supported. */ pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].format = ma_format_unknown; pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].channels = channels; pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].sampleRate = caps.dwMaxSecondarySampleRate; pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].flags = 0; pDeviceInfo->nativeDataFormatCount += 1; } ma_IDirectSound_Release(pDirectSound); } else { /* Capture. This is a little different to playback due to the say the supported formats are reported. Technically capture devices can support a number of different formats, but for simplicity and consistency with ma_device_init() I'm just reporting the best format. */ ma_IDirectSoundCapture* pDirectSoundCapture; WORD channels; WORD bitsPerSample; DWORD sampleRate; result = ma_context_create_IDirectSoundCapture__dsound(pContext, ma_share_mode_shared, pDeviceID, &pDirectSoundCapture); if (result != MA_SUCCESS) { return result; } result = ma_context_get_format_info_for_IDirectSoundCapture__dsound(pContext, pDirectSoundCapture, &channels, &bitsPerSample, &sampleRate); if (result != MA_SUCCESS) { ma_IDirectSoundCapture_Release(pDirectSoundCapture); return result; } ma_IDirectSoundCapture_Release(pDirectSoundCapture); /* The format is always an integer format and is based on the bits per sample. */ if (bitsPerSample == 8) { pDeviceInfo->nativeDataFormats[0].format = ma_format_u8; } else if (bitsPerSample == 16) { pDeviceInfo->nativeDataFormats[0].format = ma_format_s16; } else if (bitsPerSample == 24) { pDeviceInfo->nativeDataFormats[0].format = ma_format_s24; } else if (bitsPerSample == 32) { pDeviceInfo->nativeDataFormats[0].format = ma_format_s32; } else { return MA_FORMAT_NOT_SUPPORTED; } pDeviceInfo->nativeDataFormats[0].channels = channels; pDeviceInfo->nativeDataFormats[0].sampleRate = sampleRate; pDeviceInfo->nativeDataFormats[0].flags = 0; pDeviceInfo->nativeDataFormatCount = 1; } return MA_SUCCESS; } static ma_result ma_device_uninit__dsound(ma_device* pDevice) { MA_ASSERT(pDevice != NULL); if (pDevice->dsound.pCaptureBuffer != NULL) { ma_IDirectSoundCaptureBuffer_Release((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer); } if (pDevice->dsound.pCapture != NULL) { ma_IDirectSoundCapture_Release((ma_IDirectSoundCapture*)pDevice->dsound.pCapture); } if (pDevice->dsound.pPlaybackBuffer != NULL) { ma_IDirectSoundBuffer_Release((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer); } if (pDevice->dsound.pPlaybackPrimaryBuffer != NULL) { ma_IDirectSoundBuffer_Release((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackPrimaryBuffer); } if (pDevice->dsound.pPlayback != NULL) { ma_IDirectSound_Release((ma_IDirectSound*)pDevice->dsound.pPlayback); } return MA_SUCCESS; } static ma_result ma_config_to_WAVEFORMATEXTENSIBLE(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, const ma_channel* pChannelMap, WAVEFORMATEXTENSIBLE* pWF) { GUID subformat; if (format == ma_format_unknown) { format = MA_DEFAULT_FORMAT; } if (channels == 0) { channels = MA_DEFAULT_CHANNELS; } if (sampleRate == 0) { sampleRate = MA_DEFAULT_SAMPLE_RATE; } switch (format) { case ma_format_u8: case ma_format_s16: case ma_format_s24: /*case ma_format_s24_32:*/ case ma_format_s32: { subformat = MA_GUID_KSDATAFORMAT_SUBTYPE_PCM; } break; case ma_format_f32: { subformat = MA_GUID_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT; } break; default: return MA_FORMAT_NOT_SUPPORTED; } MA_ZERO_OBJECT(pWF); pWF->Format.cbSize = sizeof(*pWF); pWF->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE; pWF->Format.nChannels = (WORD)channels; pWF->Format.nSamplesPerSec = (DWORD)sampleRate; pWF->Format.wBitsPerSample = (WORD)(ma_get_bytes_per_sample(format)*8); pWF->Format.nBlockAlign = (WORD)(pWF->Format.nChannels * pWF->Format.wBitsPerSample / 8); pWF->Format.nAvgBytesPerSec = pWF->Format.nBlockAlign * pWF->Format.nSamplesPerSec; pWF->Samples.wValidBitsPerSample = pWF->Format.wBitsPerSample; pWF->dwChannelMask = ma_channel_map_to_channel_mask__win32(pChannelMap, channels); pWF->SubFormat = subformat; return MA_SUCCESS; } static ma_uint32 ma_calculate_period_size_in_frames_from_descriptor__dsound(const ma_device_descriptor* pDescriptor, ma_uint32 nativeSampleRate, ma_performance_profile performanceProfile) { /* DirectSound has a minimum period size of 20ms. In practice, this doesn't seem to be enough for reliable glitch-free processing so going to use 30ms instead. */ ma_uint32 minPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(30, nativeSampleRate); ma_uint32 periodSizeInFrames; periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptor, nativeSampleRate, performanceProfile); if (periodSizeInFrames < minPeriodSizeInFrames) { periodSizeInFrames = minPeriodSizeInFrames; } return periodSizeInFrames; } static ma_result ma_device_init__dsound(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture) { ma_result result; HRESULT hr; MA_ASSERT(pDevice != NULL); MA_ZERO_OBJECT(&pDevice->dsound); if (pConfig->deviceType == ma_device_type_loopback) { return MA_DEVICE_TYPE_NOT_SUPPORTED; } /* Unfortunately DirectSound uses different APIs and data structures for playback and catpure devices. We need to initialize the capture device first because we'll want to match it's buffer size and period count on the playback side if we're using full-duplex mode. */ if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { WAVEFORMATEXTENSIBLE wf; MA_DSCBUFFERDESC descDS; ma_uint32 periodSizeInFrames; ma_uint32 periodCount; char rawdata[1024]; /* <-- Ugly hack to avoid a malloc() due to a crappy DirectSound API. */ WAVEFORMATEXTENSIBLE* pActualFormat; result = ma_config_to_WAVEFORMATEXTENSIBLE(pDescriptorCapture->format, pDescriptorCapture->channels, pDescriptorCapture->sampleRate, pDescriptorCapture->channelMap, &wf); if (result != MA_SUCCESS) { return result; } result = ma_context_create_IDirectSoundCapture__dsound(pDevice->pContext, pDescriptorCapture->shareMode, pDescriptorCapture->pDeviceID, (ma_IDirectSoundCapture**)&pDevice->dsound.pCapture); if (result != MA_SUCCESS) { ma_device_uninit__dsound(pDevice); return result; } result = ma_context_get_format_info_for_IDirectSoundCapture__dsound(pDevice->pContext, (ma_IDirectSoundCapture*)pDevice->dsound.pCapture, &wf.Format.nChannels, &wf.Format.wBitsPerSample, &wf.Format.nSamplesPerSec); if (result != MA_SUCCESS) { ma_device_uninit__dsound(pDevice); return result; } wf.Format.nBlockAlign = (WORD)(wf.Format.nChannels * wf.Format.wBitsPerSample / 8); wf.Format.nAvgBytesPerSec = wf.Format.nBlockAlign * wf.Format.nSamplesPerSec; wf.Samples.wValidBitsPerSample = wf.Format.wBitsPerSample; wf.SubFormat = MA_GUID_KSDATAFORMAT_SUBTYPE_PCM; /* The size of the buffer must be a clean multiple of the period count. */ periodSizeInFrames = ma_calculate_period_size_in_frames_from_descriptor__dsound(pDescriptorCapture, wf.Format.nSamplesPerSec, pConfig->performanceProfile); periodCount = (pDescriptorCapture->periodCount > 0) ? pDescriptorCapture->periodCount : MA_DEFAULT_PERIODS; MA_ZERO_OBJECT(&descDS); descDS.dwSize = sizeof(descDS); descDS.dwFlags = 0; descDS.dwBufferBytes = periodSizeInFrames * periodCount * wf.Format.nBlockAlign; descDS.lpwfxFormat = (WAVEFORMATEX*)&wf; hr = ma_IDirectSoundCapture_CreateCaptureBuffer((ma_IDirectSoundCapture*)pDevice->dsound.pCapture, &descDS, (ma_IDirectSoundCaptureBuffer**)&pDevice->dsound.pCaptureBuffer, NULL); if (FAILED(hr)) { ma_device_uninit__dsound(pDevice); ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundCapture_CreateCaptureBuffer() failed for capture device."); return ma_result_from_HRESULT(hr); } /* Get the _actual_ properties of the buffer. */ pActualFormat = (WAVEFORMATEXTENSIBLE*)rawdata; hr = ma_IDirectSoundCaptureBuffer_GetFormat((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, (WAVEFORMATEX*)pActualFormat, sizeof(rawdata), NULL); if (FAILED(hr)) { ma_device_uninit__dsound(pDevice); ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to retrieve the actual format of the capture device's buffer."); return ma_result_from_HRESULT(hr); } /* We can now start setting the output data formats. */ pDescriptorCapture->format = ma_format_from_WAVEFORMATEX((WAVEFORMATEX*)pActualFormat); pDescriptorCapture->channels = pActualFormat->Format.nChannels; pDescriptorCapture->sampleRate = pActualFormat->Format.nSamplesPerSec; /* Get the native channel map based on the channel mask. */ if (pActualFormat->Format.wFormatTag == WAVE_FORMAT_EXTENSIBLE) { ma_channel_mask_to_channel_map__win32(pActualFormat->dwChannelMask, pDescriptorCapture->channels, pDescriptorCapture->channelMap); } else { ma_channel_mask_to_channel_map__win32(wf.dwChannelMask, pDescriptorCapture->channels, pDescriptorCapture->channelMap); } /* After getting the actual format the size of the buffer in frames may have actually changed. However, we want this to be as close to what the user has asked for as possible, so let's go ahead and release the old capture buffer and create a new one in this case. */ if (periodSizeInFrames != (descDS.dwBufferBytes / ma_get_bytes_per_frame(pDescriptorCapture->format, pDescriptorCapture->channels) / periodCount)) { descDS.dwBufferBytes = periodSizeInFrames * ma_get_bytes_per_frame(pDescriptorCapture->format, pDescriptorCapture->channels) * periodCount; ma_IDirectSoundCaptureBuffer_Release((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer); hr = ma_IDirectSoundCapture_CreateCaptureBuffer((ma_IDirectSoundCapture*)pDevice->dsound.pCapture, &descDS, (ma_IDirectSoundCaptureBuffer**)&pDevice->dsound.pCaptureBuffer, NULL); if (FAILED(hr)) { ma_device_uninit__dsound(pDevice); ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] Second attempt at IDirectSoundCapture_CreateCaptureBuffer() failed for capture device."); return ma_result_from_HRESULT(hr); } } /* DirectSound should give us a buffer exactly the size we asked for. */ pDescriptorCapture->periodSizeInFrames = periodSizeInFrames; pDescriptorCapture->periodCount = periodCount; } if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { WAVEFORMATEXTENSIBLE wf; MA_DSBUFFERDESC descDSPrimary; MA_DSCAPS caps; char rawdata[1024]; /* <-- Ugly hack to avoid a malloc() due to a crappy DirectSound API. */ WAVEFORMATEXTENSIBLE* pActualFormat; ma_uint32 periodSizeInFrames; ma_uint32 periodCount; MA_DSBUFFERDESC descDS; result = ma_config_to_WAVEFORMATEXTENSIBLE(pDescriptorPlayback->format, pDescriptorPlayback->channels, pDescriptorPlayback->sampleRate, pDescriptorPlayback->channelMap, &wf); if (result != MA_SUCCESS) { return result; } result = ma_context_create_IDirectSound__dsound(pDevice->pContext, pDescriptorPlayback->shareMode, pDescriptorPlayback->pDeviceID, (ma_IDirectSound**)&pDevice->dsound.pPlayback); if (result != MA_SUCCESS) { ma_device_uninit__dsound(pDevice); return result; } MA_ZERO_OBJECT(&descDSPrimary); descDSPrimary.dwSize = sizeof(MA_DSBUFFERDESC); descDSPrimary.dwFlags = MA_DSBCAPS_PRIMARYBUFFER | MA_DSBCAPS_CTRLVOLUME; hr = ma_IDirectSound_CreateSoundBuffer((ma_IDirectSound*)pDevice->dsound.pPlayback, &descDSPrimary, (ma_IDirectSoundBuffer**)&pDevice->dsound.pPlaybackPrimaryBuffer, NULL); if (FAILED(hr)) { ma_device_uninit__dsound(pDevice); ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSound_CreateSoundBuffer() failed for playback device's primary buffer."); return ma_result_from_HRESULT(hr); } /* We may want to make some adjustments to the format if we are using defaults. */ MA_ZERO_OBJECT(&caps); caps.dwSize = sizeof(caps); hr = ma_IDirectSound_GetCaps((ma_IDirectSound*)pDevice->dsound.pPlayback, &caps); if (FAILED(hr)) { ma_device_uninit__dsound(pDevice); ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSound_GetCaps() failed for playback device."); return ma_result_from_HRESULT(hr); } if (pDescriptorPlayback->channels == 0) { if ((caps.dwFlags & MA_DSCAPS_PRIMARYSTEREO) != 0) { DWORD speakerConfig; /* It supports at least stereo, but could support more. */ wf.Format.nChannels = 2; /* Look at the speaker configuration to get a better idea on the channel count. */ if (SUCCEEDED(ma_IDirectSound_GetSpeakerConfig((ma_IDirectSound*)pDevice->dsound.pPlayback, &speakerConfig))) { ma_get_channels_from_speaker_config__dsound(speakerConfig, &wf.Format.nChannels, &wf.dwChannelMask); } } else { /* It does not support stereo, which means we are stuck with mono. */ wf.Format.nChannels = 1; } } if (pDescriptorPlayback->sampleRate == 0) { /* We base the sample rate on the values returned by GetCaps(). */ if ((caps.dwFlags & MA_DSCAPS_CONTINUOUSRATE) != 0) { wf.Format.nSamplesPerSec = ma_get_best_sample_rate_within_range(caps.dwMinSecondarySampleRate, caps.dwMaxSecondarySampleRate); } else { wf.Format.nSamplesPerSec = caps.dwMaxSecondarySampleRate; } } wf.Format.nBlockAlign = (WORD)(wf.Format.nChannels * wf.Format.wBitsPerSample / 8); wf.Format.nAvgBytesPerSec = wf.Format.nBlockAlign * wf.Format.nSamplesPerSec; /* From MSDN: The method succeeds even if the hardware does not support the requested format; DirectSound sets the buffer to the closest supported format. To determine whether this has happened, an application can call the GetFormat method for the primary buffer and compare the result with the format that was requested with the SetFormat method. */ hr = ma_IDirectSoundBuffer_SetFormat((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackPrimaryBuffer, (WAVEFORMATEX*)&wf); if (FAILED(hr)) { ma_device_uninit__dsound(pDevice); ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to set format of playback device's primary buffer."); return ma_result_from_HRESULT(hr); } /* Get the _actual_ properties of the buffer. */ pActualFormat = (WAVEFORMATEXTENSIBLE*)rawdata; hr = ma_IDirectSoundBuffer_GetFormat((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackPrimaryBuffer, (WAVEFORMATEX*)pActualFormat, sizeof(rawdata), NULL); if (FAILED(hr)) { ma_device_uninit__dsound(pDevice); ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to retrieve the actual format of the playback device's primary buffer."); return ma_result_from_HRESULT(hr); } /* We now have enough information to start setting some output properties. */ pDescriptorPlayback->format = ma_format_from_WAVEFORMATEX((WAVEFORMATEX*)pActualFormat); pDescriptorPlayback->channels = pActualFormat->Format.nChannels; pDescriptorPlayback->sampleRate = pActualFormat->Format.nSamplesPerSec; /* Get the internal channel map based on the channel mask. */ if (pActualFormat->Format.wFormatTag == WAVE_FORMAT_EXTENSIBLE) { ma_channel_mask_to_channel_map__win32(pActualFormat->dwChannelMask, pDescriptorPlayback->channels, pDescriptorPlayback->channelMap); } else { ma_channel_mask_to_channel_map__win32(wf.dwChannelMask, pDescriptorPlayback->channels, pDescriptorPlayback->channelMap); } /* The size of the buffer must be a clean multiple of the period count. */ periodSizeInFrames = ma_calculate_period_size_in_frames_from_descriptor__dsound(pDescriptorPlayback, pDescriptorPlayback->sampleRate, pConfig->performanceProfile); periodCount = (pDescriptorPlayback->periodCount > 0) ? pDescriptorPlayback->periodCount : MA_DEFAULT_PERIODS; /* Meaning of dwFlags (from MSDN): DSBCAPS_CTRLPOSITIONNOTIFY The buffer has position notification capability. DSBCAPS_GLOBALFOCUS With this flag set, an application using DirectSound can continue to play its buffers if the user switches focus to another application, even if the new application uses DirectSound. DSBCAPS_GETCURRENTPOSITION2 In the first version of DirectSound, the play cursor was significantly ahead of the actual playing sound on emulated sound cards; it was directly behind the write cursor. Now, if the DSBCAPS_GETCURRENTPOSITION2 flag is specified, the application can get a more accurate play cursor. */ MA_ZERO_OBJECT(&descDS); descDS.dwSize = sizeof(descDS); descDS.dwFlags = MA_DSBCAPS_CTRLPOSITIONNOTIFY | MA_DSBCAPS_GLOBALFOCUS | MA_DSBCAPS_GETCURRENTPOSITION2; descDS.dwBufferBytes = periodSizeInFrames * periodCount * ma_get_bytes_per_frame(pDescriptorPlayback->format, pDescriptorPlayback->channels); descDS.lpwfxFormat = (WAVEFORMATEX*)&wf; hr = ma_IDirectSound_CreateSoundBuffer((ma_IDirectSound*)pDevice->dsound.pPlayback, &descDS, (ma_IDirectSoundBuffer**)&pDevice->dsound.pPlaybackBuffer, NULL); if (FAILED(hr)) { ma_device_uninit__dsound(pDevice); ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSound_CreateSoundBuffer() failed for playback device's secondary buffer."); return ma_result_from_HRESULT(hr); } /* DirectSound should give us a buffer exactly the size we asked for. */ pDescriptorPlayback->periodSizeInFrames = periodSizeInFrames; pDescriptorPlayback->periodCount = periodCount; } return MA_SUCCESS; } static ma_result ma_device_data_loop__dsound(ma_device* pDevice) { ma_result result = MA_SUCCESS; ma_uint32 bpfDeviceCapture = ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels); ma_uint32 bpfDevicePlayback = ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels); HRESULT hr; DWORD lockOffsetInBytesCapture; DWORD lockSizeInBytesCapture; DWORD mappedSizeInBytesCapture; DWORD mappedDeviceFramesProcessedCapture; void* pMappedDeviceBufferCapture; DWORD lockOffsetInBytesPlayback; DWORD lockSizeInBytesPlayback; DWORD mappedSizeInBytesPlayback; void* pMappedDeviceBufferPlayback; DWORD prevReadCursorInBytesCapture = 0; DWORD prevPlayCursorInBytesPlayback = 0; ma_bool32 physicalPlayCursorLoopFlagPlayback = 0; DWORD virtualWriteCursorInBytesPlayback = 0; ma_bool32 virtualWriteCursorLoopFlagPlayback = 0; ma_bool32 isPlaybackDeviceStarted = MA_FALSE; ma_uint32 framesWrittenToPlaybackDevice = 0; /* For knowing whether or not the playback device needs to be started. */ ma_uint32 waitTimeInMilliseconds = 1; MA_ASSERT(pDevice != NULL); /* The first thing to do is start the capture device. The playback device is only started after the first period is written. */ if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { hr = ma_IDirectSoundCaptureBuffer_Start((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, MA_DSCBSTART_LOOPING); if (FAILED(hr)) { ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundCaptureBuffer_Start() failed."); return ma_result_from_HRESULT(hr); } } while (ma_device_get_state(pDevice) == ma_device_state_started) { switch (pDevice->type) { case ma_device_type_duplex: { DWORD physicalCaptureCursorInBytes; DWORD physicalReadCursorInBytes; hr = ma_IDirectSoundCaptureBuffer_GetCurrentPosition((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, &physicalCaptureCursorInBytes, &physicalReadCursorInBytes); if (FAILED(hr)) { return ma_result_from_HRESULT(hr); } /* If nothing is available we just sleep for a bit and return from this iteration. */ if (physicalReadCursorInBytes == prevReadCursorInBytesCapture) { ma_sleep(waitTimeInMilliseconds); continue; /* Nothing is available in the capture buffer. */ } /* The current position has moved. We need to map all of the captured samples and write them to the playback device, making sure we don't return until every frame has been copied over. */ if (prevReadCursorInBytesCapture < physicalReadCursorInBytes) { /* The capture position has not looped. This is the simple case. */ lockOffsetInBytesCapture = prevReadCursorInBytesCapture; lockSizeInBytesCapture = (physicalReadCursorInBytes - prevReadCursorInBytesCapture); } else { /* The capture position has looped. This is the more complex case. Map to the end of the buffer. If this does not return anything, do it again from the start. */ if (prevReadCursorInBytesCapture < pDevice->capture.internalPeriodSizeInFrames*pDevice->capture.internalPeriods*bpfDeviceCapture) { /* Lock up to the end of the buffer. */ lockOffsetInBytesCapture = prevReadCursorInBytesCapture; lockSizeInBytesCapture = (pDevice->capture.internalPeriodSizeInFrames*pDevice->capture.internalPeriods*bpfDeviceCapture) - prevReadCursorInBytesCapture; } else { /* Lock starting from the start of the buffer. */ lockOffsetInBytesCapture = 0; lockSizeInBytesCapture = physicalReadCursorInBytes; } } if (lockSizeInBytesCapture == 0) { ma_sleep(waitTimeInMilliseconds); continue; /* Nothing is available in the capture buffer. */ } hr = ma_IDirectSoundCaptureBuffer_Lock((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, lockOffsetInBytesCapture, lockSizeInBytesCapture, &pMappedDeviceBufferCapture, &mappedSizeInBytesCapture, NULL, NULL, 0); if (FAILED(hr)) { ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to map buffer from capture device in preparation for writing to the device."); return ma_result_from_HRESULT(hr); } /* At this point we have some input data that we need to output. We do not return until every mapped frame of the input data is written to the playback device. */ mappedDeviceFramesProcessedCapture = 0; for (;;) { /* Keep writing to the playback device. */ ma_uint8 inputFramesInClientFormat[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; ma_uint32 inputFramesInClientFormatCap = sizeof(inputFramesInClientFormat) / ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels); ma_uint8 outputFramesInClientFormat[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; ma_uint32 outputFramesInClientFormatCap = sizeof(outputFramesInClientFormat) / ma_get_bytes_per_frame(pDevice->playback.format, pDevice->playback.channels); ma_uint32 outputFramesInClientFormatCount; ma_uint32 outputFramesInClientFormatConsumed = 0; ma_uint64 clientCapturedFramesToProcess = ma_min(inputFramesInClientFormatCap, outputFramesInClientFormatCap); ma_uint64 deviceCapturedFramesToProcess = (mappedSizeInBytesCapture / bpfDeviceCapture) - mappedDeviceFramesProcessedCapture; void* pRunningMappedDeviceBufferCapture = ma_offset_ptr(pMappedDeviceBufferCapture, mappedDeviceFramesProcessedCapture * bpfDeviceCapture); result = ma_data_converter_process_pcm_frames(&pDevice->capture.converter, pRunningMappedDeviceBufferCapture, &deviceCapturedFramesToProcess, inputFramesInClientFormat, &clientCapturedFramesToProcess); if (result != MA_SUCCESS) { break; } outputFramesInClientFormatCount = (ma_uint32)clientCapturedFramesToProcess; mappedDeviceFramesProcessedCapture += (ma_uint32)deviceCapturedFramesToProcess; ma_device__handle_data_callback(pDevice, outputFramesInClientFormat, inputFramesInClientFormat, (ma_uint32)clientCapturedFramesToProcess); /* At this point we have input and output data in client format. All we need to do now is convert it to the output device format. This may take a few passes. */ for (;;) { ma_uint32 framesWrittenThisIteration; DWORD physicalPlayCursorInBytes; DWORD physicalWriteCursorInBytes; DWORD availableBytesPlayback; DWORD silentPaddingInBytes = 0; /* <-- Must be initialized to 0. */ /* We need the physical play and write cursors. */ if (FAILED(ma_IDirectSoundBuffer_GetCurrentPosition((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, &physicalPlayCursorInBytes, &physicalWriteCursorInBytes))) { break; } if (physicalPlayCursorInBytes < prevPlayCursorInBytesPlayback) { physicalPlayCursorLoopFlagPlayback = !physicalPlayCursorLoopFlagPlayback; } prevPlayCursorInBytesPlayback = physicalPlayCursorInBytes; /* If there's any bytes available for writing we can do that now. The space between the virtual cursor position and play cursor. */ if (physicalPlayCursorLoopFlagPlayback == virtualWriteCursorLoopFlagPlayback) { /* Same loop iteration. The available bytes wraps all the way around from the virtual write cursor to the physical play cursor. */ if (physicalPlayCursorInBytes <= virtualWriteCursorInBytesPlayback) { availableBytesPlayback = (pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods*bpfDevicePlayback) - virtualWriteCursorInBytesPlayback; availableBytesPlayback += physicalPlayCursorInBytes; /* Wrap around. */ } else { /* This is an error. */ ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_WARNING, "[DirectSound] (Duplex/Playback): Play cursor has moved in front of the write cursor (same loop iteration). physicalPlayCursorInBytes=%ld, virtualWriteCursorInBytes=%ld.\n", physicalPlayCursorInBytes, virtualWriteCursorInBytesPlayback); availableBytesPlayback = 0; } } else { /* Different loop iterations. The available bytes only goes from the virtual write cursor to the physical play cursor. */ if (physicalPlayCursorInBytes >= virtualWriteCursorInBytesPlayback) { availableBytesPlayback = physicalPlayCursorInBytes - virtualWriteCursorInBytesPlayback; } else { /* This is an error. */ ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_WARNING, "[DirectSound] (Duplex/Playback): Write cursor has moved behind the play cursor (different loop iterations). physicalPlayCursorInBytes=%ld, virtualWriteCursorInBytes=%ld.\n", physicalPlayCursorInBytes, virtualWriteCursorInBytesPlayback); availableBytesPlayback = 0; } } /* If there's no room available for writing we need to wait for more. */ if (availableBytesPlayback == 0) { /* If we haven't started the device yet, this will never get beyond 0. In this case we need to get the device started. */ if (!isPlaybackDeviceStarted) { hr = ma_IDirectSoundBuffer_Play((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, 0, 0, MA_DSBPLAY_LOOPING); if (FAILED(hr)) { ma_IDirectSoundCaptureBuffer_Stop((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer); ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundBuffer_Play() failed."); return ma_result_from_HRESULT(hr); } isPlaybackDeviceStarted = MA_TRUE; } else { ma_sleep(waitTimeInMilliseconds); continue; } } /* Getting here means there room available somewhere. We limit this to either the end of the buffer or the physical play cursor, whichever is closest. */ lockOffsetInBytesPlayback = virtualWriteCursorInBytesPlayback; if (physicalPlayCursorLoopFlagPlayback == virtualWriteCursorLoopFlagPlayback) { /* Same loop iteration. Go up to the end of the buffer. */ lockSizeInBytesPlayback = (pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods*bpfDevicePlayback) - virtualWriteCursorInBytesPlayback; } else { /* Different loop iterations. Go up to the physical play cursor. */ lockSizeInBytesPlayback = physicalPlayCursorInBytes - virtualWriteCursorInBytesPlayback; } hr = ma_IDirectSoundBuffer_Lock((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, lockOffsetInBytesPlayback, lockSizeInBytesPlayback, &pMappedDeviceBufferPlayback, &mappedSizeInBytesPlayback, NULL, NULL, 0); if (FAILED(hr)) { ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to map buffer from playback device in preparation for writing to the device."); result = ma_result_from_HRESULT(hr); break; } /* Experiment: If the playback buffer is being starved, pad it with some silence to get it back in sync. This will cause a glitch, but it may prevent endless glitching due to it constantly running out of data. */ if (isPlaybackDeviceStarted) { DWORD bytesQueuedForPlayback = (pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods*bpfDevicePlayback) - availableBytesPlayback; if (bytesQueuedForPlayback < (pDevice->playback.internalPeriodSizeInFrames*bpfDevicePlayback)) { silentPaddingInBytes = (pDevice->playback.internalPeriodSizeInFrames*2*bpfDevicePlayback) - bytesQueuedForPlayback; if (silentPaddingInBytes > lockSizeInBytesPlayback) { silentPaddingInBytes = lockSizeInBytesPlayback; } ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_WARNING, "[DirectSound] (Duplex/Playback) Playback buffer starved. availableBytesPlayback=%ld, silentPaddingInBytes=%ld\n", availableBytesPlayback, silentPaddingInBytes); } } /* At this point we have a buffer for output. */ if (silentPaddingInBytes > 0) { MA_ZERO_MEMORY(pMappedDeviceBufferPlayback, silentPaddingInBytes); framesWrittenThisIteration = silentPaddingInBytes/bpfDevicePlayback; } else { ma_uint64 convertedFrameCountIn = (outputFramesInClientFormatCount - outputFramesInClientFormatConsumed); ma_uint64 convertedFrameCountOut = mappedSizeInBytesPlayback/bpfDevicePlayback; void* pConvertedFramesIn = ma_offset_ptr(outputFramesInClientFormat, outputFramesInClientFormatConsumed * bpfDevicePlayback); void* pConvertedFramesOut = pMappedDeviceBufferPlayback; result = ma_data_converter_process_pcm_frames(&pDevice->playback.converter, pConvertedFramesIn, &convertedFrameCountIn, pConvertedFramesOut, &convertedFrameCountOut); if (result != MA_SUCCESS) { break; } outputFramesInClientFormatConsumed += (ma_uint32)convertedFrameCountOut; framesWrittenThisIteration = (ma_uint32)convertedFrameCountOut; } hr = ma_IDirectSoundBuffer_Unlock((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, pMappedDeviceBufferPlayback, framesWrittenThisIteration*bpfDevicePlayback, NULL, 0); if (FAILED(hr)) { ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to unlock internal buffer from playback device after writing to the device."); result = ma_result_from_HRESULT(hr); break; } virtualWriteCursorInBytesPlayback += framesWrittenThisIteration*bpfDevicePlayback; if ((virtualWriteCursorInBytesPlayback/bpfDevicePlayback) == pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods) { virtualWriteCursorInBytesPlayback = 0; virtualWriteCursorLoopFlagPlayback = !virtualWriteCursorLoopFlagPlayback; } /* We may need to start the device. We want two full periods to be written before starting the playback device. Having an extra period adds a bit of a buffer to prevent the playback buffer from getting starved. */ framesWrittenToPlaybackDevice += framesWrittenThisIteration; if (!isPlaybackDeviceStarted && framesWrittenToPlaybackDevice >= (pDevice->playback.internalPeriodSizeInFrames*2)) { hr = ma_IDirectSoundBuffer_Play((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, 0, 0, MA_DSBPLAY_LOOPING); if (FAILED(hr)) { ma_IDirectSoundCaptureBuffer_Stop((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer); ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundBuffer_Play() failed."); return ma_result_from_HRESULT(hr); } isPlaybackDeviceStarted = MA_TRUE; } if (framesWrittenThisIteration < mappedSizeInBytesPlayback/bpfDevicePlayback) { break; /* We're finished with the output data.*/ } } if (clientCapturedFramesToProcess == 0) { break; /* We just consumed every input sample. */ } } /* At this point we're done with the mapped portion of the capture buffer. */ hr = ma_IDirectSoundCaptureBuffer_Unlock((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, pMappedDeviceBufferCapture, mappedSizeInBytesCapture, NULL, 0); if (FAILED(hr)) { ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to unlock internal buffer from capture device after reading from the device."); return ma_result_from_HRESULT(hr); } prevReadCursorInBytesCapture = (lockOffsetInBytesCapture + mappedSizeInBytesCapture); } break; case ma_device_type_capture: { DWORD physicalCaptureCursorInBytes; DWORD physicalReadCursorInBytes; hr = ma_IDirectSoundCaptureBuffer_GetCurrentPosition((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, &physicalCaptureCursorInBytes, &physicalReadCursorInBytes); if (FAILED(hr)) { return MA_ERROR; } /* If the previous capture position is the same as the current position we need to wait a bit longer. */ if (prevReadCursorInBytesCapture == physicalReadCursorInBytes) { ma_sleep(waitTimeInMilliseconds); continue; } /* Getting here means we have capture data available. */ if (prevReadCursorInBytesCapture < physicalReadCursorInBytes) { /* The capture position has not looped. This is the simple case. */ lockOffsetInBytesCapture = prevReadCursorInBytesCapture; lockSizeInBytesCapture = (physicalReadCursorInBytes - prevReadCursorInBytesCapture); } else { /* The capture position has looped. This is the more complex case. Map to the end of the buffer. If this does not return anything, do it again from the start. */ if (prevReadCursorInBytesCapture < pDevice->capture.internalPeriodSizeInFrames*pDevice->capture.internalPeriods*bpfDeviceCapture) { /* Lock up to the end of the buffer. */ lockOffsetInBytesCapture = prevReadCursorInBytesCapture; lockSizeInBytesCapture = (pDevice->capture.internalPeriodSizeInFrames*pDevice->capture.internalPeriods*bpfDeviceCapture) - prevReadCursorInBytesCapture; } else { /* Lock starting from the start of the buffer. */ lockOffsetInBytesCapture = 0; lockSizeInBytesCapture = physicalReadCursorInBytes; } } if (lockSizeInBytesCapture < pDevice->capture.internalPeriodSizeInFrames) { ma_sleep(waitTimeInMilliseconds); continue; /* Nothing is available in the capture buffer. */ } hr = ma_IDirectSoundCaptureBuffer_Lock((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, lockOffsetInBytesCapture, lockSizeInBytesCapture, &pMappedDeviceBufferCapture, &mappedSizeInBytesCapture, NULL, NULL, 0); if (FAILED(hr)) { ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to map buffer from capture device in preparation for writing to the device."); result = ma_result_from_HRESULT(hr); } if (lockSizeInBytesCapture != mappedSizeInBytesCapture) { ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[DirectSound] (Capture) lockSizeInBytesCapture=%ld != mappedSizeInBytesCapture=%ld\n", lockSizeInBytesCapture, mappedSizeInBytesCapture); } ma_device__send_frames_to_client(pDevice, mappedSizeInBytesCapture/bpfDeviceCapture, pMappedDeviceBufferCapture); hr = ma_IDirectSoundCaptureBuffer_Unlock((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, pMappedDeviceBufferCapture, mappedSizeInBytesCapture, NULL, 0); if (FAILED(hr)) { ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to unlock internal buffer from capture device after reading from the device."); return ma_result_from_HRESULT(hr); } prevReadCursorInBytesCapture = lockOffsetInBytesCapture + mappedSizeInBytesCapture; if (prevReadCursorInBytesCapture == (pDevice->capture.internalPeriodSizeInFrames*pDevice->capture.internalPeriods*bpfDeviceCapture)) { prevReadCursorInBytesCapture = 0; } } break; case ma_device_type_playback: { DWORD availableBytesPlayback; DWORD physicalPlayCursorInBytes; DWORD physicalWriteCursorInBytes; hr = ma_IDirectSoundBuffer_GetCurrentPosition((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, &physicalPlayCursorInBytes, &physicalWriteCursorInBytes); if (FAILED(hr)) { break; } if (physicalPlayCursorInBytes < prevPlayCursorInBytesPlayback) { physicalPlayCursorLoopFlagPlayback = !physicalPlayCursorLoopFlagPlayback; } prevPlayCursorInBytesPlayback = physicalPlayCursorInBytes; /* If there's any bytes available for writing we can do that now. The space between the virtual cursor position and play cursor. */ if (physicalPlayCursorLoopFlagPlayback == virtualWriteCursorLoopFlagPlayback) { /* Same loop iteration. The available bytes wraps all the way around from the virtual write cursor to the physical play cursor. */ if (physicalPlayCursorInBytes <= virtualWriteCursorInBytesPlayback) { availableBytesPlayback = (pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods*bpfDevicePlayback) - virtualWriteCursorInBytesPlayback; availableBytesPlayback += physicalPlayCursorInBytes; /* Wrap around. */ } else { /* This is an error. */ ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_WARNING, "[DirectSound] (Playback): Play cursor has moved in front of the write cursor (same loop iterations). physicalPlayCursorInBytes=%ld, virtualWriteCursorInBytes=%ld.\n", physicalPlayCursorInBytes, virtualWriteCursorInBytesPlayback); availableBytesPlayback = 0; } } else { /* Different loop iterations. The available bytes only goes from the virtual write cursor to the physical play cursor. */ if (physicalPlayCursorInBytes >= virtualWriteCursorInBytesPlayback) { availableBytesPlayback = physicalPlayCursorInBytes - virtualWriteCursorInBytesPlayback; } else { /* This is an error. */ ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_WARNING, "[DirectSound] (Playback): Write cursor has moved behind the play cursor (different loop iterations). physicalPlayCursorInBytes=%ld, virtualWriteCursorInBytes=%ld.\n", physicalPlayCursorInBytes, virtualWriteCursorInBytesPlayback); availableBytesPlayback = 0; } } /* If there's no room available for writing we need to wait for more. */ if (availableBytesPlayback < pDevice->playback.internalPeriodSizeInFrames) { /* If we haven't started the device yet, this will never get beyond 0. In this case we need to get the device started. */ if (availableBytesPlayback == 0 && !isPlaybackDeviceStarted) { hr = ma_IDirectSoundBuffer_Play((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, 0, 0, MA_DSBPLAY_LOOPING); if (FAILED(hr)) { ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundBuffer_Play() failed."); return ma_result_from_HRESULT(hr); } isPlaybackDeviceStarted = MA_TRUE; } else { ma_sleep(waitTimeInMilliseconds); continue; } } /* Getting here means there room available somewhere. We limit this to either the end of the buffer or the physical play cursor, whichever is closest. */ lockOffsetInBytesPlayback = virtualWriteCursorInBytesPlayback; if (physicalPlayCursorLoopFlagPlayback == virtualWriteCursorLoopFlagPlayback) { /* Same loop iteration. Go up to the end of the buffer. */ lockSizeInBytesPlayback = (pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods*bpfDevicePlayback) - virtualWriteCursorInBytesPlayback; } else { /* Different loop iterations. Go up to the physical play cursor. */ lockSizeInBytesPlayback = physicalPlayCursorInBytes - virtualWriteCursorInBytesPlayback; } hr = ma_IDirectSoundBuffer_Lock((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, lockOffsetInBytesPlayback, lockSizeInBytesPlayback, &pMappedDeviceBufferPlayback, &mappedSizeInBytesPlayback, NULL, NULL, 0); if (FAILED(hr)) { ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to map buffer from playback device in preparation for writing to the device."); result = ma_result_from_HRESULT(hr); break; } /* At this point we have a buffer for output. */ ma_device__read_frames_from_client(pDevice, (mappedSizeInBytesPlayback/bpfDevicePlayback), pMappedDeviceBufferPlayback); hr = ma_IDirectSoundBuffer_Unlock((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, pMappedDeviceBufferPlayback, mappedSizeInBytesPlayback, NULL, 0); if (FAILED(hr)) { ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to unlock internal buffer from playback device after writing to the device."); result = ma_result_from_HRESULT(hr); break; } virtualWriteCursorInBytesPlayback += mappedSizeInBytesPlayback; if (virtualWriteCursorInBytesPlayback == pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods*bpfDevicePlayback) { virtualWriteCursorInBytesPlayback = 0; virtualWriteCursorLoopFlagPlayback = !virtualWriteCursorLoopFlagPlayback; } /* We may need to start the device. We want two full periods to be written before starting the playback device. Having an extra period adds a bit of a buffer to prevent the playback buffer from getting starved. */ framesWrittenToPlaybackDevice += mappedSizeInBytesPlayback/bpfDevicePlayback; if (!isPlaybackDeviceStarted && framesWrittenToPlaybackDevice >= pDevice->playback.internalPeriodSizeInFrames) { hr = ma_IDirectSoundBuffer_Play((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, 0, 0, MA_DSBPLAY_LOOPING); if (FAILED(hr)) { ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundBuffer_Play() failed."); return ma_result_from_HRESULT(hr); } isPlaybackDeviceStarted = MA_TRUE; } } break; default: return MA_INVALID_ARGS; /* Invalid device type. */ } if (result != MA_SUCCESS) { return result; } } /* Getting here means the device is being stopped. */ if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { hr = ma_IDirectSoundCaptureBuffer_Stop((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer); if (FAILED(hr)) { ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundCaptureBuffer_Stop() failed."); return ma_result_from_HRESULT(hr); } } if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { /* The playback device should be drained before stopping. All we do is wait until the available bytes is equal to the size of the buffer. */ if (isPlaybackDeviceStarted) { for (;;) { DWORD availableBytesPlayback = 0; DWORD physicalPlayCursorInBytes; DWORD physicalWriteCursorInBytes; hr = ma_IDirectSoundBuffer_GetCurrentPosition((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, &physicalPlayCursorInBytes, &physicalWriteCursorInBytes); if (FAILED(hr)) { break; } if (physicalPlayCursorInBytes < prevPlayCursorInBytesPlayback) { physicalPlayCursorLoopFlagPlayback = !physicalPlayCursorLoopFlagPlayback; } prevPlayCursorInBytesPlayback = physicalPlayCursorInBytes; if (physicalPlayCursorLoopFlagPlayback == virtualWriteCursorLoopFlagPlayback) { /* Same loop iteration. The available bytes wraps all the way around from the virtual write cursor to the physical play cursor. */ if (physicalPlayCursorInBytes <= virtualWriteCursorInBytesPlayback) { availableBytesPlayback = (pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods*bpfDevicePlayback) - virtualWriteCursorInBytesPlayback; availableBytesPlayback += physicalPlayCursorInBytes; /* Wrap around. */ } else { break; } } else { /* Different loop iterations. The available bytes only goes from the virtual write cursor to the physical play cursor. */ if (physicalPlayCursorInBytes >= virtualWriteCursorInBytesPlayback) { availableBytesPlayback = physicalPlayCursorInBytes - virtualWriteCursorInBytesPlayback; } else { break; } } if (availableBytesPlayback >= (pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods*bpfDevicePlayback)) { break; } ma_sleep(waitTimeInMilliseconds); } } hr = ma_IDirectSoundBuffer_Stop((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer); if (FAILED(hr)) { ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundBuffer_Stop() failed."); return ma_result_from_HRESULT(hr); } ma_IDirectSoundBuffer_SetCurrentPosition((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, 0); } return MA_SUCCESS; } static ma_result ma_context_uninit__dsound(ma_context* pContext) { MA_ASSERT(pContext != NULL); MA_ASSERT(pContext->backend == ma_backend_dsound); ma_dlclose(pContext, pContext->dsound.hDSoundDLL); return MA_SUCCESS; } static ma_result ma_context_init__dsound(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks) { MA_ASSERT(pContext != NULL); (void)pConfig; pContext->dsound.hDSoundDLL = ma_dlopen(pContext, "dsound.dll"); if (pContext->dsound.hDSoundDLL == NULL) { return MA_API_NOT_FOUND; } pContext->dsound.DirectSoundCreate = ma_dlsym(pContext, pContext->dsound.hDSoundDLL, "DirectSoundCreate"); pContext->dsound.DirectSoundEnumerateA = ma_dlsym(pContext, pContext->dsound.hDSoundDLL, "DirectSoundEnumerateA"); pContext->dsound.DirectSoundCaptureCreate = ma_dlsym(pContext, pContext->dsound.hDSoundDLL, "DirectSoundCaptureCreate"); pContext->dsound.DirectSoundCaptureEnumerateA = ma_dlsym(pContext, pContext->dsound.hDSoundDLL, "DirectSoundCaptureEnumerateA"); pCallbacks->onContextInit = ma_context_init__dsound; pCallbacks->onContextUninit = ma_context_uninit__dsound; pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__dsound; pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__dsound; pCallbacks->onDeviceInit = ma_device_init__dsound; pCallbacks->onDeviceUninit = ma_device_uninit__dsound; pCallbacks->onDeviceStart = NULL; /* Not used. Started in onDeviceDataLoop. */ pCallbacks->onDeviceStop = NULL; /* Not used. Stopped in onDeviceDataLoop. */ pCallbacks->onDeviceRead = NULL; /* Not used. Data is read directly in onDeviceDataLoop. */ pCallbacks->onDeviceWrite = NULL; /* Not used. Data is written directly in onDeviceDataLoop. */ pCallbacks->onDeviceDataLoop = ma_device_data_loop__dsound; return MA_SUCCESS; } #endif /****************************************************************************** WinMM Backend ******************************************************************************/ #ifdef MA_HAS_WINMM /* Some older compilers don't have WAVEOUTCAPS2A and WAVEINCAPS2A, so we'll need to write this ourselves. These structures are exactly the same as the older ones but they have a few GUIDs for manufacturer/product/name identification. I'm keeping the names the same as the Win32 library for consistency, but namespaced to avoid naming conflicts with the Win32 version. */ typedef struct { WORD wMid; WORD wPid; MMVERSION vDriverVersion; CHAR szPname[MAXPNAMELEN]; DWORD dwFormats; WORD wChannels; WORD wReserved1; DWORD dwSupport; GUID ManufacturerGuid; GUID ProductGuid; GUID NameGuid; } MA_WAVEOUTCAPS2A; typedef struct { WORD wMid; WORD wPid; MMVERSION vDriverVersion; CHAR szPname[MAXPNAMELEN]; DWORD dwFormats; WORD wChannels; WORD wReserved1; GUID ManufacturerGuid; GUID ProductGuid; GUID NameGuid; } MA_WAVEINCAPS2A; typedef UINT (WINAPI * MA_PFN_waveOutGetNumDevs)(void); typedef MMRESULT (WINAPI * MA_PFN_waveOutGetDevCapsA)(ma_uintptr uDeviceID, LPWAVEOUTCAPSA pwoc, UINT cbwoc); typedef MMRESULT (WINAPI * MA_PFN_waveOutOpen)(LPHWAVEOUT phwo, UINT uDeviceID, LPCWAVEFORMATEX pwfx, DWORD_PTR dwCallback, DWORD_PTR dwInstance, DWORD fdwOpen); typedef MMRESULT (WINAPI * MA_PFN_waveOutClose)(HWAVEOUT hwo); typedef MMRESULT (WINAPI * MA_PFN_waveOutPrepareHeader)(HWAVEOUT hwo, LPWAVEHDR pwh, UINT cbwh); typedef MMRESULT (WINAPI * MA_PFN_waveOutUnprepareHeader)(HWAVEOUT hwo, LPWAVEHDR pwh, UINT cbwh); typedef MMRESULT (WINAPI * MA_PFN_waveOutWrite)(HWAVEOUT hwo, LPWAVEHDR pwh, UINT cbwh); typedef MMRESULT (WINAPI * MA_PFN_waveOutReset)(HWAVEOUT hwo); typedef UINT (WINAPI * MA_PFN_waveInGetNumDevs)(void); typedef MMRESULT (WINAPI * MA_PFN_waveInGetDevCapsA)(ma_uintptr uDeviceID, LPWAVEINCAPSA pwic, UINT cbwic); typedef MMRESULT (WINAPI * MA_PFN_waveInOpen)(LPHWAVEIN phwi, UINT uDeviceID, LPCWAVEFORMATEX pwfx, DWORD_PTR dwCallback, DWORD_PTR dwInstance, DWORD fdwOpen); typedef MMRESULT (WINAPI * MA_PFN_waveInClose)(HWAVEIN hwi); typedef MMRESULT (WINAPI * MA_PFN_waveInPrepareHeader)(HWAVEIN hwi, LPWAVEHDR pwh, UINT cbwh); typedef MMRESULT (WINAPI * MA_PFN_waveInUnprepareHeader)(HWAVEIN hwi, LPWAVEHDR pwh, UINT cbwh); typedef MMRESULT (WINAPI * MA_PFN_waveInAddBuffer)(HWAVEIN hwi, LPWAVEHDR pwh, UINT cbwh); typedef MMRESULT (WINAPI * MA_PFN_waveInStart)(HWAVEIN hwi); typedef MMRESULT (WINAPI * MA_PFN_waveInReset)(HWAVEIN hwi); static ma_result ma_result_from_MMRESULT(MMRESULT resultMM) { switch (resultMM) { case MMSYSERR_NOERROR: return MA_SUCCESS; case MMSYSERR_BADDEVICEID: return MA_INVALID_ARGS; case MMSYSERR_INVALHANDLE: return MA_INVALID_ARGS; case MMSYSERR_NOMEM: return MA_OUT_OF_MEMORY; case MMSYSERR_INVALFLAG: return MA_INVALID_ARGS; case MMSYSERR_INVALPARAM: return MA_INVALID_ARGS; case MMSYSERR_HANDLEBUSY: return MA_BUSY; case MMSYSERR_ERROR: return MA_ERROR; default: return MA_ERROR; } } static char* ma_find_last_character(char* str, char ch) { char* last; if (str == NULL) { return NULL; } last = NULL; while (*str != '\0') { if (*str == ch) { last = str; } str += 1; } return last; } static ma_uint32 ma_get_period_size_in_bytes(ma_uint32 periodSizeInFrames, ma_format format, ma_uint32 channels) { return periodSizeInFrames * ma_get_bytes_per_frame(format, channels); } /* Our own "WAVECAPS" structure that contains generic information shared between WAVEOUTCAPS2 and WAVEINCAPS2 so we can do things generically and typesafely. Names are being kept the same for consistency. */ typedef struct { CHAR szPname[MAXPNAMELEN]; DWORD dwFormats; WORD wChannels; GUID NameGuid; } MA_WAVECAPSA; static ma_result ma_get_best_info_from_formats_flags__winmm(DWORD dwFormats, WORD channels, WORD* pBitsPerSample, DWORD* pSampleRate) { WORD bitsPerSample = 0; DWORD sampleRate = 0; if (pBitsPerSample) { *pBitsPerSample = 0; } if (pSampleRate) { *pSampleRate = 0; } if (channels == 1) { bitsPerSample = 16; if ((dwFormats & WAVE_FORMAT_48M16) != 0) { sampleRate = 48000; } else if ((dwFormats & WAVE_FORMAT_44M16) != 0) { sampleRate = 44100; } else if ((dwFormats & WAVE_FORMAT_2M16) != 0) { sampleRate = 22050; } else if ((dwFormats & WAVE_FORMAT_1M16) != 0) { sampleRate = 11025; } else if ((dwFormats & WAVE_FORMAT_96M16) != 0) { sampleRate = 96000; } else { bitsPerSample = 8; if ((dwFormats & WAVE_FORMAT_48M08) != 0) { sampleRate = 48000; } else if ((dwFormats & WAVE_FORMAT_44M08) != 0) { sampleRate = 44100; } else if ((dwFormats & WAVE_FORMAT_2M08) != 0) { sampleRate = 22050; } else if ((dwFormats & WAVE_FORMAT_1M08) != 0) { sampleRate = 11025; } else if ((dwFormats & WAVE_FORMAT_96M08) != 0) { sampleRate = 96000; } else { return MA_FORMAT_NOT_SUPPORTED; } } } else { bitsPerSample = 16; if ((dwFormats & WAVE_FORMAT_48S16) != 0) { sampleRate = 48000; } else if ((dwFormats & WAVE_FORMAT_44S16) != 0) { sampleRate = 44100; } else if ((dwFormats & WAVE_FORMAT_2S16) != 0) { sampleRate = 22050; } else if ((dwFormats & WAVE_FORMAT_1S16) != 0) { sampleRate = 11025; } else if ((dwFormats & WAVE_FORMAT_96S16) != 0) { sampleRate = 96000; } else { bitsPerSample = 8; if ((dwFormats & WAVE_FORMAT_48S08) != 0) { sampleRate = 48000; } else if ((dwFormats & WAVE_FORMAT_44S08) != 0) { sampleRate = 44100; } else if ((dwFormats & WAVE_FORMAT_2S08) != 0) { sampleRate = 22050; } else if ((dwFormats & WAVE_FORMAT_1S08) != 0) { sampleRate = 11025; } else if ((dwFormats & WAVE_FORMAT_96S08) != 0) { sampleRate = 96000; } else { return MA_FORMAT_NOT_SUPPORTED; } } } if (pBitsPerSample) { *pBitsPerSample = bitsPerSample; } if (pSampleRate) { *pSampleRate = sampleRate; } return MA_SUCCESS; } static ma_result ma_formats_flags_to_WAVEFORMATEX__winmm(DWORD dwFormats, WORD channels, WAVEFORMATEX* pWF) { ma_result result; MA_ASSERT(pWF != NULL); MA_ZERO_OBJECT(pWF); pWF->cbSize = sizeof(*pWF); pWF->wFormatTag = WAVE_FORMAT_PCM; pWF->nChannels = (WORD)channels; if (pWF->nChannels > 2) { pWF->nChannels = 2; } result = ma_get_best_info_from_formats_flags__winmm(dwFormats, channels, &pWF->wBitsPerSample, &pWF->nSamplesPerSec); if (result != MA_SUCCESS) { return result; } pWF->nBlockAlign = (WORD)(pWF->nChannels * pWF->wBitsPerSample / 8); pWF->nAvgBytesPerSec = pWF->nBlockAlign * pWF->nSamplesPerSec; return MA_SUCCESS; } static ma_result ma_context_get_device_info_from_WAVECAPS(ma_context* pContext, MA_WAVECAPSA* pCaps, ma_device_info* pDeviceInfo) { WORD bitsPerSample; DWORD sampleRate; ma_result result; MA_ASSERT(pContext != NULL); MA_ASSERT(pCaps != NULL); MA_ASSERT(pDeviceInfo != NULL); /* Name / Description Unfortunately the name specified in WAVE(OUT/IN)CAPS2 is limited to 31 characters. This results in an unprofessional looking situation where the names of the devices are truncated. To help work around this, we need to look at the name GUID and try looking in the registry for the full name. If we can't find it there, we need to just fall back to the default name. */ /* Set the default to begin with. */ ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), pCaps->szPname, (size_t)-1); /* Now try the registry. There's a few things to consider here: - The name GUID can be null, in which we case we just need to stick to the original 31 characters. - If the name GUID is not present in the registry we'll also need to stick to the original 31 characters. - I like consistency, so I want the returned device names to be consistent with those returned by WASAPI and DirectSound. The problem, however is that WASAPI and DirectSound use " ()" format (such as "Speakers (High Definition Audio)"), but WinMM does not specificy the component name. From my admittedly limited testing, I've notice the component name seems to usually fit within the 31 characters of the fixed sized buffer, so what I'm going to do is parse that string for the component name, and then concatenate the name from the registry. */ if (!ma_is_guid_null(&pCaps->NameGuid)) { wchar_t guidStrW[256]; if (((MA_PFN_StringFromGUID2)pContext->win32.StringFromGUID2)(&pCaps->NameGuid, guidStrW, ma_countof(guidStrW)) > 0) { char guidStr[256]; char keyStr[1024]; HKEY hKey; WideCharToMultiByte(CP_UTF8, 0, guidStrW, -1, guidStr, sizeof(guidStr), 0, FALSE); ma_strcpy_s(keyStr, sizeof(keyStr), "SYSTEM\\CurrentControlSet\\Control\\MediaCategories\\"); ma_strcat_s(keyStr, sizeof(keyStr), guidStr); if (((MA_PFN_RegOpenKeyExA)pContext->win32.RegOpenKeyExA)(HKEY_LOCAL_MACHINE, keyStr, 0, KEY_READ, &hKey) == ERROR_SUCCESS) { BYTE nameFromReg[512]; DWORD nameFromRegSize = sizeof(nameFromReg); LONG resultWin32 = ((MA_PFN_RegQueryValueExA)pContext->win32.RegQueryValueExA)(hKey, "Name", 0, NULL, (LPBYTE)nameFromReg, (LPDWORD)&nameFromRegSize); ((MA_PFN_RegCloseKey)pContext->win32.RegCloseKey)(hKey); if (resultWin32 == ERROR_SUCCESS) { /* We have the value from the registry, so now we need to construct the name string. */ char name[1024]; if (ma_strcpy_s(name, sizeof(name), pDeviceInfo->name) == 0) { char* nameBeg = ma_find_last_character(name, '('); if (nameBeg != NULL) { size_t leadingLen = (nameBeg - name); ma_strncpy_s(nameBeg + 1, sizeof(name) - leadingLen, (const char*)nameFromReg, (size_t)-1); /* The closing ")", if it can fit. */ if (leadingLen + nameFromRegSize < sizeof(name)-1) { ma_strcat_s(name, sizeof(name), ")"); } ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), name, (size_t)-1); } } } } } } result = ma_get_best_info_from_formats_flags__winmm(pCaps->dwFormats, pCaps->wChannels, &bitsPerSample, &sampleRate); if (result != MA_SUCCESS) { return result; } if (bitsPerSample == 8) { pDeviceInfo->nativeDataFormats[0].format = ma_format_u8; } else if (bitsPerSample == 16) { pDeviceInfo->nativeDataFormats[0].format = ma_format_s16; } else if (bitsPerSample == 24) { pDeviceInfo->nativeDataFormats[0].format = ma_format_s24; } else if (bitsPerSample == 32) { pDeviceInfo->nativeDataFormats[0].format = ma_format_s32; } else { return MA_FORMAT_NOT_SUPPORTED; } pDeviceInfo->nativeDataFormats[0].channels = pCaps->wChannels; pDeviceInfo->nativeDataFormats[0].sampleRate = sampleRate; pDeviceInfo->nativeDataFormats[0].flags = 0; pDeviceInfo->nativeDataFormatCount = 1; return MA_SUCCESS; } static ma_result ma_context_get_device_info_from_WAVEOUTCAPS2(ma_context* pContext, MA_WAVEOUTCAPS2A* pCaps, ma_device_info* pDeviceInfo) { MA_WAVECAPSA caps; MA_ASSERT(pContext != NULL); MA_ASSERT(pCaps != NULL); MA_ASSERT(pDeviceInfo != NULL); MA_COPY_MEMORY(caps.szPname, pCaps->szPname, sizeof(caps.szPname)); caps.dwFormats = pCaps->dwFormats; caps.wChannels = pCaps->wChannels; caps.NameGuid = pCaps->NameGuid; return ma_context_get_device_info_from_WAVECAPS(pContext, &caps, pDeviceInfo); } static ma_result ma_context_get_device_info_from_WAVEINCAPS2(ma_context* pContext, MA_WAVEINCAPS2A* pCaps, ma_device_info* pDeviceInfo) { MA_WAVECAPSA caps; MA_ASSERT(pContext != NULL); MA_ASSERT(pCaps != NULL); MA_ASSERT(pDeviceInfo != NULL); MA_COPY_MEMORY(caps.szPname, pCaps->szPname, sizeof(caps.szPname)); caps.dwFormats = pCaps->dwFormats; caps.wChannels = pCaps->wChannels; caps.NameGuid = pCaps->NameGuid; return ma_context_get_device_info_from_WAVECAPS(pContext, &caps, pDeviceInfo); } static ma_result ma_context_enumerate_devices__winmm(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData) { UINT playbackDeviceCount; UINT captureDeviceCount; UINT iPlaybackDevice; UINT iCaptureDevice; MA_ASSERT(pContext != NULL); MA_ASSERT(callback != NULL); /* Playback. */ playbackDeviceCount = ((MA_PFN_waveOutGetNumDevs)pContext->winmm.waveOutGetNumDevs)(); for (iPlaybackDevice = 0; iPlaybackDevice < playbackDeviceCount; ++iPlaybackDevice) { MMRESULT result; MA_WAVEOUTCAPS2A caps; MA_ZERO_OBJECT(&caps); result = ((MA_PFN_waveOutGetDevCapsA)pContext->winmm.waveOutGetDevCapsA)(iPlaybackDevice, (WAVEOUTCAPSA*)&caps, sizeof(caps)); if (result == MMSYSERR_NOERROR) { ma_device_info deviceInfo; MA_ZERO_OBJECT(&deviceInfo); deviceInfo.id.winmm = iPlaybackDevice; /* The first enumerated device is the default device. */ if (iPlaybackDevice == 0) { deviceInfo.isDefault = MA_TRUE; } if (ma_context_get_device_info_from_WAVEOUTCAPS2(pContext, &caps, &deviceInfo) == MA_SUCCESS) { ma_bool32 cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData); if (cbResult == MA_FALSE) { return MA_SUCCESS; /* Enumeration was stopped. */ } } } } /* Capture. */ captureDeviceCount = ((MA_PFN_waveInGetNumDevs)pContext->winmm.waveInGetNumDevs)(); for (iCaptureDevice = 0; iCaptureDevice < captureDeviceCount; ++iCaptureDevice) { MMRESULT result; MA_WAVEINCAPS2A caps; MA_ZERO_OBJECT(&caps); result = ((MA_PFN_waveInGetDevCapsA)pContext->winmm.waveInGetDevCapsA)(iCaptureDevice, (WAVEINCAPSA*)&caps, sizeof(caps)); if (result == MMSYSERR_NOERROR) { ma_device_info deviceInfo; MA_ZERO_OBJECT(&deviceInfo); deviceInfo.id.winmm = iCaptureDevice; /* The first enumerated device is the default device. */ if (iCaptureDevice == 0) { deviceInfo.isDefault = MA_TRUE; } if (ma_context_get_device_info_from_WAVEINCAPS2(pContext, &caps, &deviceInfo) == MA_SUCCESS) { ma_bool32 cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData); if (cbResult == MA_FALSE) { return MA_SUCCESS; /* Enumeration was stopped. */ } } } } return MA_SUCCESS; } static ma_result ma_context_get_device_info__winmm(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo) { UINT winMMDeviceID; MA_ASSERT(pContext != NULL); winMMDeviceID = 0; if (pDeviceID != NULL) { winMMDeviceID = (UINT)pDeviceID->winmm; } pDeviceInfo->id.winmm = winMMDeviceID; /* The first ID is the default device. */ if (winMMDeviceID == 0) { pDeviceInfo->isDefault = MA_TRUE; } if (deviceType == ma_device_type_playback) { MMRESULT result; MA_WAVEOUTCAPS2A caps; MA_ZERO_OBJECT(&caps); result = ((MA_PFN_waveOutGetDevCapsA)pContext->winmm.waveOutGetDevCapsA)(winMMDeviceID, (WAVEOUTCAPSA*)&caps, sizeof(caps)); if (result == MMSYSERR_NOERROR) { return ma_context_get_device_info_from_WAVEOUTCAPS2(pContext, &caps, pDeviceInfo); } } else { MMRESULT result; MA_WAVEINCAPS2A caps; MA_ZERO_OBJECT(&caps); result = ((MA_PFN_waveInGetDevCapsA)pContext->winmm.waveInGetDevCapsA)(winMMDeviceID, (WAVEINCAPSA*)&caps, sizeof(caps)); if (result == MMSYSERR_NOERROR) { return ma_context_get_device_info_from_WAVEINCAPS2(pContext, &caps, pDeviceInfo); } } return MA_NO_DEVICE; } static ma_result ma_device_uninit__winmm(ma_device* pDevice) { MA_ASSERT(pDevice != NULL); if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { ((MA_PFN_waveInClose)pDevice->pContext->winmm.waveInClose)((HWAVEIN)pDevice->winmm.hDeviceCapture); CloseHandle((HANDLE)pDevice->winmm.hEventCapture); } if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { ((MA_PFN_waveOutReset)pDevice->pContext->winmm.waveOutReset)((HWAVEOUT)pDevice->winmm.hDevicePlayback); ((MA_PFN_waveOutClose)pDevice->pContext->winmm.waveOutClose)((HWAVEOUT)pDevice->winmm.hDevicePlayback); CloseHandle((HANDLE)pDevice->winmm.hEventPlayback); } ma_free(pDevice->winmm._pHeapData, &pDevice->pContext->allocationCallbacks); MA_ZERO_OBJECT(&pDevice->winmm); /* Safety. */ return MA_SUCCESS; } static ma_uint32 ma_calculate_period_size_in_frames_from_descriptor__winmm(const ma_device_descriptor* pDescriptor, ma_uint32 nativeSampleRate, ma_performance_profile performanceProfile) { /* WinMM has a minimum period size of 40ms. */ ma_uint32 minPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(40, nativeSampleRate); ma_uint32 periodSizeInFrames; periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptor, nativeSampleRate, performanceProfile); if (periodSizeInFrames < minPeriodSizeInFrames) { periodSizeInFrames = minPeriodSizeInFrames; } return periodSizeInFrames; } static ma_result ma_device_init__winmm(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture) { const char* errorMsg = ""; ma_result errorCode = MA_ERROR; ma_result result = MA_SUCCESS; ma_uint32 heapSize; UINT winMMDeviceIDPlayback = 0; UINT winMMDeviceIDCapture = 0; MA_ASSERT(pDevice != NULL); MA_ZERO_OBJECT(&pDevice->winmm); if (pConfig->deviceType == ma_device_type_loopback) { return MA_DEVICE_TYPE_NOT_SUPPORTED; } /* No exlusive mode with WinMM. */ if (((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pDescriptorPlayback->shareMode == ma_share_mode_exclusive) || ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pDescriptorCapture->shareMode == ma_share_mode_exclusive)) { return MA_SHARE_MODE_NOT_SUPPORTED; } if (pDescriptorPlayback->pDeviceID != NULL) { winMMDeviceIDPlayback = (UINT)pDescriptorPlayback->pDeviceID->winmm; } if (pDescriptorCapture->pDeviceID != NULL) { winMMDeviceIDCapture = (UINT)pDescriptorCapture->pDeviceID->winmm; } /* The capture device needs to be initialized first. */ if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { WAVEINCAPSA caps; WAVEFORMATEX wf; MMRESULT resultMM; /* We use an event to know when a new fragment needs to be enqueued. */ pDevice->winmm.hEventCapture = (ma_handle)CreateEventW(NULL, TRUE, TRUE, NULL); if (pDevice->winmm.hEventCapture == NULL) { errorMsg = "[WinMM] Failed to create event for fragment enqueing for the capture device.", errorCode = ma_result_from_GetLastError(GetLastError()); goto on_error; } /* The format should be based on the device's actual format. */ if (((MA_PFN_waveInGetDevCapsA)pDevice->pContext->winmm.waveInGetDevCapsA)(winMMDeviceIDCapture, &caps, sizeof(caps)) != MMSYSERR_NOERROR) { errorMsg = "[WinMM] Failed to retrieve internal device caps.", errorCode = MA_FORMAT_NOT_SUPPORTED; goto on_error; } result = ma_formats_flags_to_WAVEFORMATEX__winmm(caps.dwFormats, caps.wChannels, &wf); if (result != MA_SUCCESS) { errorMsg = "[WinMM] Could not find appropriate format for internal device.", errorCode = result; goto on_error; } resultMM = ((MA_PFN_waveInOpen)pDevice->pContext->winmm.waveInOpen)((LPHWAVEIN)&pDevice->winmm.hDeviceCapture, winMMDeviceIDCapture, &wf, (DWORD_PTR)pDevice->winmm.hEventCapture, (DWORD_PTR)pDevice, CALLBACK_EVENT | WAVE_ALLOWSYNC); if (resultMM != MMSYSERR_NOERROR) { errorMsg = "[WinMM] Failed to open capture device.", errorCode = MA_FAILED_TO_OPEN_BACKEND_DEVICE; goto on_error; } pDescriptorCapture->format = ma_format_from_WAVEFORMATEX(&wf); pDescriptorCapture->channels = wf.nChannels; pDescriptorCapture->sampleRate = wf.nSamplesPerSec; ma_channel_map_init_standard(ma_standard_channel_map_microsoft, pDescriptorCapture->channelMap, ma_countof(pDescriptorCapture->channelMap), pDescriptorCapture->channels); pDescriptorCapture->periodCount = pDescriptorCapture->periodCount; pDescriptorCapture->periodSizeInFrames = ma_calculate_period_size_in_frames_from_descriptor__winmm(pDescriptorCapture, pDescriptorCapture->sampleRate, pConfig->performanceProfile); } if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { WAVEOUTCAPSA caps; WAVEFORMATEX wf; MMRESULT resultMM; /* We use an event to know when a new fragment needs to be enqueued. */ pDevice->winmm.hEventPlayback = (ma_handle)CreateEventW(NULL, TRUE, TRUE, NULL); if (pDevice->winmm.hEventPlayback == NULL) { errorMsg = "[WinMM] Failed to create event for fragment enqueing for the playback device.", errorCode = ma_result_from_GetLastError(GetLastError()); goto on_error; } /* The format should be based on the device's actual format. */ if (((MA_PFN_waveOutGetDevCapsA)pDevice->pContext->winmm.waveOutGetDevCapsA)(winMMDeviceIDPlayback, &caps, sizeof(caps)) != MMSYSERR_NOERROR) { errorMsg = "[WinMM] Failed to retrieve internal device caps.", errorCode = MA_FORMAT_NOT_SUPPORTED; goto on_error; } result = ma_formats_flags_to_WAVEFORMATEX__winmm(caps.dwFormats, caps.wChannels, &wf); if (result != MA_SUCCESS) { errorMsg = "[WinMM] Could not find appropriate format for internal device.", errorCode = result; goto on_error; } resultMM = ((MA_PFN_waveOutOpen)pDevice->pContext->winmm.waveOutOpen)((LPHWAVEOUT)&pDevice->winmm.hDevicePlayback, winMMDeviceIDPlayback, &wf, (DWORD_PTR)pDevice->winmm.hEventPlayback, (DWORD_PTR)pDevice, CALLBACK_EVENT | WAVE_ALLOWSYNC); if (resultMM != MMSYSERR_NOERROR) { errorMsg = "[WinMM] Failed to open playback device.", errorCode = MA_FAILED_TO_OPEN_BACKEND_DEVICE; goto on_error; } pDescriptorPlayback->format = ma_format_from_WAVEFORMATEX(&wf); pDescriptorPlayback->channels = wf.nChannels; pDescriptorPlayback->sampleRate = wf.nSamplesPerSec; ma_channel_map_init_standard(ma_standard_channel_map_microsoft, pDescriptorPlayback->channelMap, ma_countof(pDescriptorPlayback->channelMap), pDescriptorPlayback->channels); pDescriptorPlayback->periodCount = pDescriptorPlayback->periodCount; pDescriptorPlayback->periodSizeInFrames = ma_calculate_period_size_in_frames_from_descriptor__winmm(pDescriptorPlayback, pDescriptorPlayback->sampleRate, pConfig->performanceProfile); } /* The heap allocated data is allocated like so: [Capture WAVEHDRs][Playback WAVEHDRs][Capture Intermediary Buffer][Playback Intermediary Buffer] */ heapSize = 0; if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { heapSize += sizeof(WAVEHDR)*pDescriptorCapture->periodCount + (pDescriptorCapture->periodSizeInFrames * pDescriptorCapture->periodCount * ma_get_bytes_per_frame(pDescriptorCapture->format, pDescriptorCapture->channels)); } if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { heapSize += sizeof(WAVEHDR)*pDescriptorPlayback->periodCount + (pDescriptorPlayback->periodSizeInFrames * pDescriptorPlayback->periodCount * ma_get_bytes_per_frame(pDescriptorPlayback->format, pDescriptorPlayback->channels)); } pDevice->winmm._pHeapData = (ma_uint8*)ma_calloc(heapSize, &pDevice->pContext->allocationCallbacks); if (pDevice->winmm._pHeapData == NULL) { errorMsg = "[WinMM] Failed to allocate memory for the intermediary buffer.", errorCode = MA_OUT_OF_MEMORY; goto on_error; } MA_ZERO_MEMORY(pDevice->winmm._pHeapData, heapSize); if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { ma_uint32 iPeriod; if (pConfig->deviceType == ma_device_type_capture) { pDevice->winmm.pWAVEHDRCapture = pDevice->winmm._pHeapData; pDevice->winmm.pIntermediaryBufferCapture = pDevice->winmm._pHeapData + (sizeof(WAVEHDR)*(pDescriptorCapture->periodCount)); } else { pDevice->winmm.pWAVEHDRCapture = pDevice->winmm._pHeapData; pDevice->winmm.pIntermediaryBufferCapture = pDevice->winmm._pHeapData + (sizeof(WAVEHDR)*(pDescriptorCapture->periodCount + pDescriptorPlayback->periodCount)); } /* Prepare headers. */ for (iPeriod = 0; iPeriod < pDescriptorCapture->periodCount; ++iPeriod) { ma_uint32 periodSizeInBytes = ma_get_period_size_in_bytes(pDescriptorCapture->periodSizeInFrames, pDescriptorCapture->format, pDescriptorCapture->channels); ((WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod].lpData = (LPSTR)(pDevice->winmm.pIntermediaryBufferCapture + (periodSizeInBytes*iPeriod)); ((WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod].dwBufferLength = periodSizeInBytes; ((WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod].dwFlags = 0L; ((WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod].dwLoops = 0L; ((MA_PFN_waveInPrepareHeader)pDevice->pContext->winmm.waveInPrepareHeader)((HWAVEIN)pDevice->winmm.hDeviceCapture, &((WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod], sizeof(WAVEHDR)); /* The user data of the WAVEHDR structure is a single flag the controls whether or not it is ready for writing. Consider it to be named "isLocked". A value of 0 means it's unlocked and available for writing. A value of 1 means it's locked. */ ((WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod].dwUser = 0; } } if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { ma_uint32 iPeriod; if (pConfig->deviceType == ma_device_type_playback) { pDevice->winmm.pWAVEHDRPlayback = pDevice->winmm._pHeapData; pDevice->winmm.pIntermediaryBufferPlayback = pDevice->winmm._pHeapData + (sizeof(WAVEHDR)*pDescriptorPlayback->periodCount); } else { pDevice->winmm.pWAVEHDRPlayback = pDevice->winmm._pHeapData + (sizeof(WAVEHDR)*(pDescriptorCapture->periodCount)); pDevice->winmm.pIntermediaryBufferPlayback = pDevice->winmm._pHeapData + (sizeof(WAVEHDR)*(pDescriptorCapture->periodCount + pDescriptorPlayback->periodCount)) + (pDescriptorCapture->periodSizeInFrames*pDescriptorCapture->periodCount*ma_get_bytes_per_frame(pDescriptorCapture->format, pDescriptorCapture->channels)); } /* Prepare headers. */ for (iPeriod = 0; iPeriod < pDescriptorPlayback->periodCount; ++iPeriod) { ma_uint32 periodSizeInBytes = ma_get_period_size_in_bytes(pDescriptorPlayback->periodSizeInFrames, pDescriptorPlayback->format, pDescriptorPlayback->channels); ((WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod].lpData = (LPSTR)(pDevice->winmm.pIntermediaryBufferPlayback + (periodSizeInBytes*iPeriod)); ((WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod].dwBufferLength = periodSizeInBytes; ((WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod].dwFlags = 0L; ((WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod].dwLoops = 0L; ((MA_PFN_waveOutPrepareHeader)pDevice->pContext->winmm.waveOutPrepareHeader)((HWAVEOUT)pDevice->winmm.hDevicePlayback, &((WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod], sizeof(WAVEHDR)); /* The user data of the WAVEHDR structure is a single flag the controls whether or not it is ready for writing. Consider it to be named "isLocked". A value of 0 means it's unlocked and available for writing. A value of 1 means it's locked. */ ((WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod].dwUser = 0; } } return MA_SUCCESS; on_error: if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { if (pDevice->winmm.pWAVEHDRCapture != NULL) { ma_uint32 iPeriod; for (iPeriod = 0; iPeriod < pDescriptorCapture->periodCount; ++iPeriod) { ((MA_PFN_waveInUnprepareHeader)pDevice->pContext->winmm.waveInUnprepareHeader)((HWAVEIN)pDevice->winmm.hDeviceCapture, &((WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod], sizeof(WAVEHDR)); } } ((MA_PFN_waveInClose)pDevice->pContext->winmm.waveInClose)((HWAVEIN)pDevice->winmm.hDeviceCapture); } if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { if (pDevice->winmm.pWAVEHDRCapture != NULL) { ma_uint32 iPeriod; for (iPeriod = 0; iPeriod < pDescriptorPlayback->periodCount; ++iPeriod) { ((MA_PFN_waveOutUnprepareHeader)pDevice->pContext->winmm.waveOutUnprepareHeader)((HWAVEOUT)pDevice->winmm.hDevicePlayback, &((WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod], sizeof(WAVEHDR)); } } ((MA_PFN_waveOutClose)pDevice->pContext->winmm.waveOutClose)((HWAVEOUT)pDevice->winmm.hDevicePlayback); } ma_free(pDevice->winmm._pHeapData, &pDevice->pContext->allocationCallbacks); if (errorMsg != NULL && errorMsg[0] != '\0') { ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "%s", errorMsg); } return errorCode; } static ma_result ma_device_start__winmm(ma_device* pDevice) { MA_ASSERT(pDevice != NULL); if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { MMRESULT resultMM; WAVEHDR* pWAVEHDR; ma_uint32 iPeriod; pWAVEHDR = (WAVEHDR*)pDevice->winmm.pWAVEHDRCapture; /* Make sure the event is reset to a non-signaled state to ensure we don't prematurely return from WaitForSingleObject(). */ ResetEvent((HANDLE)pDevice->winmm.hEventCapture); /* To start the device we attach all of the buffers and then start it. As the buffers are filled with data we will get notifications. */ for (iPeriod = 0; iPeriod < pDevice->capture.internalPeriods; ++iPeriod) { resultMM = ((MA_PFN_waveInAddBuffer)pDevice->pContext->winmm.waveInAddBuffer)((HWAVEIN)pDevice->winmm.hDeviceCapture, &((LPWAVEHDR)pDevice->winmm.pWAVEHDRCapture)[iPeriod], sizeof(WAVEHDR)); if (resultMM != MMSYSERR_NOERROR) { ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WinMM] Failed to attach input buffers to capture device in preparation for capture."); return ma_result_from_MMRESULT(resultMM); } /* Make sure all of the buffers start out locked. We don't want to access them until the backend tells us we can. */ pWAVEHDR[iPeriod].dwUser = 1; /* 1 = locked. */ } /* Capture devices need to be explicitly started, unlike playback devices. */ resultMM = ((MA_PFN_waveInStart)pDevice->pContext->winmm.waveInStart)((HWAVEIN)pDevice->winmm.hDeviceCapture); if (resultMM != MMSYSERR_NOERROR) { ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WinMM] Failed to start backend device."); return ma_result_from_MMRESULT(resultMM); } } if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { /* Don't need to do anything for playback. It'll be started automatically in ma_device_start__winmm(). */ } return MA_SUCCESS; } static ma_result ma_device_stop__winmm(ma_device* pDevice) { MMRESULT resultMM; MA_ASSERT(pDevice != NULL); if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { if (pDevice->winmm.hDeviceCapture == NULL) { return MA_INVALID_ARGS; } resultMM = ((MA_PFN_waveInReset)pDevice->pContext->winmm.waveInReset)((HWAVEIN)pDevice->winmm.hDeviceCapture); if (resultMM != MMSYSERR_NOERROR) { ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_WARNING, "[WinMM] WARNING: Failed to reset capture device."); } } if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { ma_uint32 iPeriod; WAVEHDR* pWAVEHDR; if (pDevice->winmm.hDevicePlayback == NULL) { return MA_INVALID_ARGS; } /* We need to drain the device. To do this we just loop over each header and if it's locked just wait for the event. */ pWAVEHDR = (WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback; for (iPeriod = 0; iPeriod < pDevice->playback.internalPeriods; iPeriod += 1) { if (pWAVEHDR[iPeriod].dwUser == 1) { /* 1 = locked. */ if (WaitForSingleObject((HANDLE)pDevice->winmm.hEventPlayback, INFINITE) != WAIT_OBJECT_0) { break; /* An error occurred so just abandon ship and stop the device without draining. */ } pWAVEHDR[iPeriod].dwUser = 0; } } resultMM = ((MA_PFN_waveOutReset)pDevice->pContext->winmm.waveOutReset)((HWAVEOUT)pDevice->winmm.hDevicePlayback); if (resultMM != MMSYSERR_NOERROR) { ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_WARNING, "[WinMM] WARNING: Failed to reset playback device."); } } return MA_SUCCESS; } static ma_result ma_device_write__winmm(ma_device* pDevice, const void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten) { ma_result result = MA_SUCCESS; MMRESULT resultMM; ma_uint32 totalFramesWritten; WAVEHDR* pWAVEHDR; MA_ASSERT(pDevice != NULL); MA_ASSERT(pPCMFrames != NULL); if (pFramesWritten != NULL) { *pFramesWritten = 0; } pWAVEHDR = (WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback; /* Keep processing as much data as possible. */ totalFramesWritten = 0; while (totalFramesWritten < frameCount) { /* If the current header has some space available we need to write part of it. */ if (pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwUser == 0) { /* 0 = unlocked. */ /* This header has room in it. We copy as much of it as we can. If we end up fully consuming the buffer we need to write it out and move on to the next iteration. */ ma_uint32 bpf = ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels); ma_uint32 framesRemainingInHeader = (pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwBufferLength/bpf) - pDevice->winmm.headerFramesConsumedPlayback; ma_uint32 framesToCopy = ma_min(framesRemainingInHeader, (frameCount - totalFramesWritten)); const void* pSrc = ma_offset_ptr(pPCMFrames, totalFramesWritten*bpf); void* pDst = ma_offset_ptr(pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].lpData, pDevice->winmm.headerFramesConsumedPlayback*bpf); MA_COPY_MEMORY(pDst, pSrc, framesToCopy*bpf); pDevice->winmm.headerFramesConsumedPlayback += framesToCopy; totalFramesWritten += framesToCopy; /* If we've consumed the buffer entirely we need to write it out to the device. */ if (pDevice->winmm.headerFramesConsumedPlayback == (pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwBufferLength/bpf)) { pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwUser = 1; /* 1 = locked. */ pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwFlags &= ~WHDR_DONE; /* <-- Need to make sure the WHDR_DONE flag is unset. */ /* Make sure the event is reset to a non-signaled state to ensure we don't prematurely return from WaitForSingleObject(). */ ResetEvent((HANDLE)pDevice->winmm.hEventPlayback); /* The device will be started here. */ resultMM = ((MA_PFN_waveOutWrite)pDevice->pContext->winmm.waveOutWrite)((HWAVEOUT)pDevice->winmm.hDevicePlayback, &pWAVEHDR[pDevice->winmm.iNextHeaderPlayback], sizeof(WAVEHDR)); if (resultMM != MMSYSERR_NOERROR) { result = ma_result_from_MMRESULT(resultMM); ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WinMM] waveOutWrite() failed."); break; } /* Make sure we move to the next header. */ pDevice->winmm.iNextHeaderPlayback = (pDevice->winmm.iNextHeaderPlayback + 1) % pDevice->playback.internalPeriods; pDevice->winmm.headerFramesConsumedPlayback = 0; } /* If at this point we have consumed the entire input buffer we can return. */ MA_ASSERT(totalFramesWritten <= frameCount); if (totalFramesWritten == frameCount) { break; } /* Getting here means there's more to process. */ continue; } /* Getting here means there isn't enough room in the buffer and we need to wait for one to become available. */ if (WaitForSingleObject((HANDLE)pDevice->winmm.hEventPlayback, INFINITE) != WAIT_OBJECT_0) { result = MA_ERROR; break; } /* Something happened. If the next buffer has been marked as done we need to reset a bit of state. */ if ((pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwFlags & WHDR_DONE) != 0) { pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwUser = 0; /* 0 = unlocked (make it available for writing). */ pDevice->winmm.headerFramesConsumedPlayback = 0; } /* If the device has been stopped we need to break. */ if (ma_device_get_state(pDevice) != ma_device_state_started) { break; } } if (pFramesWritten != NULL) { *pFramesWritten = totalFramesWritten; } return result; } static ma_result ma_device_read__winmm(ma_device* pDevice, void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesRead) { ma_result result = MA_SUCCESS; MMRESULT resultMM; ma_uint32 totalFramesRead; WAVEHDR* pWAVEHDR; MA_ASSERT(pDevice != NULL); MA_ASSERT(pPCMFrames != NULL); if (pFramesRead != NULL) { *pFramesRead = 0; } pWAVEHDR = (WAVEHDR*)pDevice->winmm.pWAVEHDRCapture; /* Keep processing as much data as possible. */ totalFramesRead = 0; while (totalFramesRead < frameCount) { /* If the current header has some space available we need to write part of it. */ if (pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwUser == 0) { /* 0 = unlocked. */ /* The buffer is available for reading. If we fully consume it we need to add it back to the buffer. */ ma_uint32 bpf = ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels); ma_uint32 framesRemainingInHeader = (pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwBufferLength/bpf) - pDevice->winmm.headerFramesConsumedCapture; ma_uint32 framesToCopy = ma_min(framesRemainingInHeader, (frameCount - totalFramesRead)); const void* pSrc = ma_offset_ptr(pWAVEHDR[pDevice->winmm.iNextHeaderCapture].lpData, pDevice->winmm.headerFramesConsumedCapture*bpf); void* pDst = ma_offset_ptr(pPCMFrames, totalFramesRead*bpf); MA_COPY_MEMORY(pDst, pSrc, framesToCopy*bpf); pDevice->winmm.headerFramesConsumedCapture += framesToCopy; totalFramesRead += framesToCopy; /* If we've consumed the buffer entirely we need to add it back to the device. */ if (pDevice->winmm.headerFramesConsumedCapture == (pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwBufferLength/bpf)) { pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwUser = 1; /* 1 = locked. */ pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwFlags &= ~WHDR_DONE; /* <-- Need to make sure the WHDR_DONE flag is unset. */ /* Make sure the event is reset to a non-signaled state to ensure we don't prematurely return from WaitForSingleObject(). */ ResetEvent((HANDLE)pDevice->winmm.hEventCapture); /* The device will be started here. */ resultMM = ((MA_PFN_waveInAddBuffer)pDevice->pContext->winmm.waveInAddBuffer)((HWAVEIN)pDevice->winmm.hDeviceCapture, &((LPWAVEHDR)pDevice->winmm.pWAVEHDRCapture)[pDevice->winmm.iNextHeaderCapture], sizeof(WAVEHDR)); if (resultMM != MMSYSERR_NOERROR) { result = ma_result_from_MMRESULT(resultMM); ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WinMM] waveInAddBuffer() failed."); break; } /* Make sure we move to the next header. */ pDevice->winmm.iNextHeaderCapture = (pDevice->winmm.iNextHeaderCapture + 1) % pDevice->capture.internalPeriods; pDevice->winmm.headerFramesConsumedCapture = 0; } /* If at this point we have filled the entire input buffer we can return. */ MA_ASSERT(totalFramesRead <= frameCount); if (totalFramesRead == frameCount) { break; } /* Getting here means there's more to process. */ continue; } /* Getting here means there isn't enough any data left to send to the client which means we need to wait for more. */ if (WaitForSingleObject((HANDLE)pDevice->winmm.hEventCapture, INFINITE) != WAIT_OBJECT_0) { result = MA_ERROR; break; } /* Something happened. If the next buffer has been marked as done we need to reset a bit of state. */ if ((pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwFlags & WHDR_DONE) != 0) { pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwUser = 0; /* 0 = unlocked (make it available for reading). */ pDevice->winmm.headerFramesConsumedCapture = 0; } /* If the device has been stopped we need to break. */ if (ma_device_get_state(pDevice) != ma_device_state_started) { break; } } if (pFramesRead != NULL) { *pFramesRead = totalFramesRead; } return result; } static ma_result ma_context_uninit__winmm(ma_context* pContext) { MA_ASSERT(pContext != NULL); MA_ASSERT(pContext->backend == ma_backend_winmm); ma_dlclose(pContext, pContext->winmm.hWinMM); return MA_SUCCESS; } static ma_result ma_context_init__winmm(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks) { MA_ASSERT(pContext != NULL); (void)pConfig; pContext->winmm.hWinMM = ma_dlopen(pContext, "winmm.dll"); if (pContext->winmm.hWinMM == NULL) { return MA_NO_BACKEND; } pContext->winmm.waveOutGetNumDevs = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveOutGetNumDevs"); pContext->winmm.waveOutGetDevCapsA = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveOutGetDevCapsA"); pContext->winmm.waveOutOpen = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveOutOpen"); pContext->winmm.waveOutClose = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveOutClose"); pContext->winmm.waveOutPrepareHeader = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveOutPrepareHeader"); pContext->winmm.waveOutUnprepareHeader = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveOutUnprepareHeader"); pContext->winmm.waveOutWrite = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveOutWrite"); pContext->winmm.waveOutReset = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveOutReset"); pContext->winmm.waveInGetNumDevs = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveInGetNumDevs"); pContext->winmm.waveInGetDevCapsA = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveInGetDevCapsA"); pContext->winmm.waveInOpen = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveInOpen"); pContext->winmm.waveInClose = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveInClose"); pContext->winmm.waveInPrepareHeader = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveInPrepareHeader"); pContext->winmm.waveInUnprepareHeader = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveInUnprepareHeader"); pContext->winmm.waveInAddBuffer = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveInAddBuffer"); pContext->winmm.waveInStart = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveInStart"); pContext->winmm.waveInReset = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveInReset"); pCallbacks->onContextInit = ma_context_init__winmm; pCallbacks->onContextUninit = ma_context_uninit__winmm; pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__winmm; pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__winmm; pCallbacks->onDeviceInit = ma_device_init__winmm; pCallbacks->onDeviceUninit = ma_device_uninit__winmm; pCallbacks->onDeviceStart = ma_device_start__winmm; pCallbacks->onDeviceStop = ma_device_stop__winmm; pCallbacks->onDeviceRead = ma_device_read__winmm; pCallbacks->onDeviceWrite = ma_device_write__winmm; pCallbacks->onDeviceDataLoop = NULL; /* This is a blocking read-write API, so this can be NULL since miniaudio will manage the audio thread for us. */ return MA_SUCCESS; } #endif /****************************************************************************** ALSA Backend ******************************************************************************/ #ifdef MA_HAS_ALSA #include /* poll(), struct pollfd */ #include /* eventfd() */ #ifdef MA_NO_RUNTIME_LINKING /* asoundlib.h marks some functions with "inline" which isn't always supported. Need to emulate it. */ #if !defined(__cplusplus) #if defined(__STRICT_ANSI__) #if !defined(inline) #define inline __inline__ __attribute__((always_inline)) #define MA_INLINE_DEFINED #endif #endif #endif #include #if defined(MA_INLINE_DEFINED) #undef inline #undef MA_INLINE_DEFINED #endif typedef snd_pcm_uframes_t ma_snd_pcm_uframes_t; typedef snd_pcm_sframes_t ma_snd_pcm_sframes_t; typedef snd_pcm_stream_t ma_snd_pcm_stream_t; typedef snd_pcm_format_t ma_snd_pcm_format_t; typedef snd_pcm_access_t ma_snd_pcm_access_t; typedef snd_pcm_t ma_snd_pcm_t; typedef snd_pcm_hw_params_t ma_snd_pcm_hw_params_t; typedef snd_pcm_sw_params_t ma_snd_pcm_sw_params_t; typedef snd_pcm_format_mask_t ma_snd_pcm_format_mask_t; typedef snd_pcm_info_t ma_snd_pcm_info_t; typedef snd_pcm_channel_area_t ma_snd_pcm_channel_area_t; typedef snd_pcm_chmap_t ma_snd_pcm_chmap_t; typedef snd_pcm_state_t ma_snd_pcm_state_t; /* snd_pcm_stream_t */ #define MA_SND_PCM_STREAM_PLAYBACK SND_PCM_STREAM_PLAYBACK #define MA_SND_PCM_STREAM_CAPTURE SND_PCM_STREAM_CAPTURE /* snd_pcm_format_t */ #define MA_SND_PCM_FORMAT_UNKNOWN SND_PCM_FORMAT_UNKNOWN #define MA_SND_PCM_FORMAT_U8 SND_PCM_FORMAT_U8 #define MA_SND_PCM_FORMAT_S16_LE SND_PCM_FORMAT_S16_LE #define MA_SND_PCM_FORMAT_S16_BE SND_PCM_FORMAT_S16_BE #define MA_SND_PCM_FORMAT_S24_LE SND_PCM_FORMAT_S24_LE #define MA_SND_PCM_FORMAT_S24_BE SND_PCM_FORMAT_S24_BE #define MA_SND_PCM_FORMAT_S32_LE SND_PCM_FORMAT_S32_LE #define MA_SND_PCM_FORMAT_S32_BE SND_PCM_FORMAT_S32_BE #define MA_SND_PCM_FORMAT_FLOAT_LE SND_PCM_FORMAT_FLOAT_LE #define MA_SND_PCM_FORMAT_FLOAT_BE SND_PCM_FORMAT_FLOAT_BE #define MA_SND_PCM_FORMAT_FLOAT64_LE SND_PCM_FORMAT_FLOAT64_LE #define MA_SND_PCM_FORMAT_FLOAT64_BE SND_PCM_FORMAT_FLOAT64_BE #define MA_SND_PCM_FORMAT_MU_LAW SND_PCM_FORMAT_MU_LAW #define MA_SND_PCM_FORMAT_A_LAW SND_PCM_FORMAT_A_LAW #define MA_SND_PCM_FORMAT_S24_3LE SND_PCM_FORMAT_S24_3LE #define MA_SND_PCM_FORMAT_S24_3BE SND_PCM_FORMAT_S24_3BE /* ma_snd_pcm_access_t */ #define MA_SND_PCM_ACCESS_MMAP_INTERLEAVED SND_PCM_ACCESS_MMAP_INTERLEAVED #define MA_SND_PCM_ACCESS_MMAP_NONINTERLEAVED SND_PCM_ACCESS_MMAP_NONINTERLEAVED #define MA_SND_PCM_ACCESS_MMAP_COMPLEX SND_PCM_ACCESS_MMAP_COMPLEX #define MA_SND_PCM_ACCESS_RW_INTERLEAVED SND_PCM_ACCESS_RW_INTERLEAVED #define MA_SND_PCM_ACCESS_RW_NONINTERLEAVED SND_PCM_ACCESS_RW_NONINTERLEAVED /* Channel positions. */ #define MA_SND_CHMAP_UNKNOWN SND_CHMAP_UNKNOWN #define MA_SND_CHMAP_NA SND_CHMAP_NA #define MA_SND_CHMAP_MONO SND_CHMAP_MONO #define MA_SND_CHMAP_FL SND_CHMAP_FL #define MA_SND_CHMAP_FR SND_CHMAP_FR #define MA_SND_CHMAP_RL SND_CHMAP_RL #define MA_SND_CHMAP_RR SND_CHMAP_RR #define MA_SND_CHMAP_FC SND_CHMAP_FC #define MA_SND_CHMAP_LFE SND_CHMAP_LFE #define MA_SND_CHMAP_SL SND_CHMAP_SL #define MA_SND_CHMAP_SR SND_CHMAP_SR #define MA_SND_CHMAP_RC SND_CHMAP_RC #define MA_SND_CHMAP_FLC SND_CHMAP_FLC #define MA_SND_CHMAP_FRC SND_CHMAP_FRC #define MA_SND_CHMAP_RLC SND_CHMAP_RLC #define MA_SND_CHMAP_RRC SND_CHMAP_RRC #define MA_SND_CHMAP_FLW SND_CHMAP_FLW #define MA_SND_CHMAP_FRW SND_CHMAP_FRW #define MA_SND_CHMAP_FLH SND_CHMAP_FLH #define MA_SND_CHMAP_FCH SND_CHMAP_FCH #define MA_SND_CHMAP_FRH SND_CHMAP_FRH #define MA_SND_CHMAP_TC SND_CHMAP_TC #define MA_SND_CHMAP_TFL SND_CHMAP_TFL #define MA_SND_CHMAP_TFR SND_CHMAP_TFR #define MA_SND_CHMAP_TFC SND_CHMAP_TFC #define MA_SND_CHMAP_TRL SND_CHMAP_TRL #define MA_SND_CHMAP_TRR SND_CHMAP_TRR #define MA_SND_CHMAP_TRC SND_CHMAP_TRC #define MA_SND_CHMAP_TFLC SND_CHMAP_TFLC #define MA_SND_CHMAP_TFRC SND_CHMAP_TFRC #define MA_SND_CHMAP_TSL SND_CHMAP_TSL #define MA_SND_CHMAP_TSR SND_CHMAP_TSR #define MA_SND_CHMAP_LLFE SND_CHMAP_LLFE #define MA_SND_CHMAP_RLFE SND_CHMAP_RLFE #define MA_SND_CHMAP_BC SND_CHMAP_BC #define MA_SND_CHMAP_BLC SND_CHMAP_BLC #define MA_SND_CHMAP_BRC SND_CHMAP_BRC /* Open mode flags. */ #define MA_SND_PCM_NO_AUTO_RESAMPLE SND_PCM_NO_AUTO_RESAMPLE #define MA_SND_PCM_NO_AUTO_CHANNELS SND_PCM_NO_AUTO_CHANNELS #define MA_SND_PCM_NO_AUTO_FORMAT SND_PCM_NO_AUTO_FORMAT #else #include /* For EPIPE, etc. */ typedef unsigned long ma_snd_pcm_uframes_t; typedef long ma_snd_pcm_sframes_t; typedef int ma_snd_pcm_stream_t; typedef int ma_snd_pcm_format_t; typedef int ma_snd_pcm_access_t; typedef int ma_snd_pcm_state_t; typedef struct ma_snd_pcm_t ma_snd_pcm_t; typedef struct ma_snd_pcm_hw_params_t ma_snd_pcm_hw_params_t; typedef struct ma_snd_pcm_sw_params_t ma_snd_pcm_sw_params_t; typedef struct ma_snd_pcm_format_mask_t ma_snd_pcm_format_mask_t; typedef struct ma_snd_pcm_info_t ma_snd_pcm_info_t; typedef struct { void* addr; unsigned int first; unsigned int step; } ma_snd_pcm_channel_area_t; typedef struct { unsigned int channels; unsigned int pos[1]; } ma_snd_pcm_chmap_t; /* snd_pcm_state_t */ #define MA_SND_PCM_STATE_OPEN 0 #define MA_SND_PCM_STATE_SETUP 1 #define MA_SND_PCM_STATE_PREPARED 2 #define MA_SND_PCM_STATE_RUNNING 3 #define MA_SND_PCM_STATE_XRUN 4 #define MA_SND_PCM_STATE_DRAINING 5 #define MA_SND_PCM_STATE_PAUSED 6 #define MA_SND_PCM_STATE_SUSPENDED 7 #define MA_SND_PCM_STATE_DISCONNECTED 8 /* snd_pcm_stream_t */ #define MA_SND_PCM_STREAM_PLAYBACK 0 #define MA_SND_PCM_STREAM_CAPTURE 1 /* snd_pcm_format_t */ #define MA_SND_PCM_FORMAT_UNKNOWN -1 #define MA_SND_PCM_FORMAT_U8 1 #define MA_SND_PCM_FORMAT_S16_LE 2 #define MA_SND_PCM_FORMAT_S16_BE 3 #define MA_SND_PCM_FORMAT_S24_LE 6 #define MA_SND_PCM_FORMAT_S24_BE 7 #define MA_SND_PCM_FORMAT_S32_LE 10 #define MA_SND_PCM_FORMAT_S32_BE 11 #define MA_SND_PCM_FORMAT_FLOAT_LE 14 #define MA_SND_PCM_FORMAT_FLOAT_BE 15 #define MA_SND_PCM_FORMAT_FLOAT64_LE 16 #define MA_SND_PCM_FORMAT_FLOAT64_BE 17 #define MA_SND_PCM_FORMAT_MU_LAW 20 #define MA_SND_PCM_FORMAT_A_LAW 21 #define MA_SND_PCM_FORMAT_S24_3LE 32 #define MA_SND_PCM_FORMAT_S24_3BE 33 /* snd_pcm_access_t */ #define MA_SND_PCM_ACCESS_MMAP_INTERLEAVED 0 #define MA_SND_PCM_ACCESS_MMAP_NONINTERLEAVED 1 #define MA_SND_PCM_ACCESS_MMAP_COMPLEX 2 #define MA_SND_PCM_ACCESS_RW_INTERLEAVED 3 #define MA_SND_PCM_ACCESS_RW_NONINTERLEAVED 4 /* Channel positions. */ #define MA_SND_CHMAP_UNKNOWN 0 #define MA_SND_CHMAP_NA 1 #define MA_SND_CHMAP_MONO 2 #define MA_SND_CHMAP_FL 3 #define MA_SND_CHMAP_FR 4 #define MA_SND_CHMAP_RL 5 #define MA_SND_CHMAP_RR 6 #define MA_SND_CHMAP_FC 7 #define MA_SND_CHMAP_LFE 8 #define MA_SND_CHMAP_SL 9 #define MA_SND_CHMAP_SR 10 #define MA_SND_CHMAP_RC 11 #define MA_SND_CHMAP_FLC 12 #define MA_SND_CHMAP_FRC 13 #define MA_SND_CHMAP_RLC 14 #define MA_SND_CHMAP_RRC 15 #define MA_SND_CHMAP_FLW 16 #define MA_SND_CHMAP_FRW 17 #define MA_SND_CHMAP_FLH 18 #define MA_SND_CHMAP_FCH 19 #define MA_SND_CHMAP_FRH 20 #define MA_SND_CHMAP_TC 21 #define MA_SND_CHMAP_TFL 22 #define MA_SND_CHMAP_TFR 23 #define MA_SND_CHMAP_TFC 24 #define MA_SND_CHMAP_TRL 25 #define MA_SND_CHMAP_TRR 26 #define MA_SND_CHMAP_TRC 27 #define MA_SND_CHMAP_TFLC 28 #define MA_SND_CHMAP_TFRC 29 #define MA_SND_CHMAP_TSL 30 #define MA_SND_CHMAP_TSR 31 #define MA_SND_CHMAP_LLFE 32 #define MA_SND_CHMAP_RLFE 33 #define MA_SND_CHMAP_BC 34 #define MA_SND_CHMAP_BLC 35 #define MA_SND_CHMAP_BRC 36 /* Open mode flags. */ #define MA_SND_PCM_NO_AUTO_RESAMPLE 0x00010000 #define MA_SND_PCM_NO_AUTO_CHANNELS 0x00020000 #define MA_SND_PCM_NO_AUTO_FORMAT 0x00040000 #endif typedef int (* ma_snd_pcm_open_proc) (ma_snd_pcm_t **pcm, const char *name, ma_snd_pcm_stream_t stream, int mode); typedef int (* ma_snd_pcm_close_proc) (ma_snd_pcm_t *pcm); typedef size_t (* ma_snd_pcm_hw_params_sizeof_proc) (void); typedef int (* ma_snd_pcm_hw_params_any_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params); typedef int (* ma_snd_pcm_hw_params_set_format_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, ma_snd_pcm_format_t val); typedef int (* ma_snd_pcm_hw_params_set_format_first_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, ma_snd_pcm_format_t *format); typedef void (* ma_snd_pcm_hw_params_get_format_mask_proc) (ma_snd_pcm_hw_params_t *params, ma_snd_pcm_format_mask_t *mask); typedef int (* ma_snd_pcm_hw_params_set_channels_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, unsigned int val); typedef int (* ma_snd_pcm_hw_params_set_channels_near_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, unsigned int *val); typedef int (* ma_snd_pcm_hw_params_set_channels_minmax_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, unsigned int *minimum, unsigned int *maximum); typedef int (* ma_snd_pcm_hw_params_set_rate_resample_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, unsigned int val); typedef int (* ma_snd_pcm_hw_params_set_rate_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, unsigned int val, int dir); typedef int (* ma_snd_pcm_hw_params_set_rate_near_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, unsigned int *val, int *dir); typedef int (* ma_snd_pcm_hw_params_set_buffer_size_near_proc)(ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, ma_snd_pcm_uframes_t *val); typedef int (* ma_snd_pcm_hw_params_set_periods_near_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, unsigned int *val, int *dir); typedef int (* ma_snd_pcm_hw_params_set_access_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, ma_snd_pcm_access_t _access); typedef int (* ma_snd_pcm_hw_params_get_format_proc) (const ma_snd_pcm_hw_params_t *params, ma_snd_pcm_format_t *format); typedef int (* ma_snd_pcm_hw_params_get_channels_proc) (const ma_snd_pcm_hw_params_t *params, unsigned int *val); typedef int (* ma_snd_pcm_hw_params_get_channels_min_proc) (const ma_snd_pcm_hw_params_t *params, unsigned int *val); typedef int (* ma_snd_pcm_hw_params_get_channels_max_proc) (const ma_snd_pcm_hw_params_t *params, unsigned int *val); typedef int (* ma_snd_pcm_hw_params_get_rate_proc) (const ma_snd_pcm_hw_params_t *params, unsigned int *rate, int *dir); typedef int (* ma_snd_pcm_hw_params_get_rate_min_proc) (const ma_snd_pcm_hw_params_t *params, unsigned int *rate, int *dir); typedef int (* ma_snd_pcm_hw_params_get_rate_max_proc) (const ma_snd_pcm_hw_params_t *params, unsigned int *rate, int *dir); typedef int (* ma_snd_pcm_hw_params_get_buffer_size_proc) (const ma_snd_pcm_hw_params_t *params, ma_snd_pcm_uframes_t *val); typedef int (* ma_snd_pcm_hw_params_get_periods_proc) (const ma_snd_pcm_hw_params_t *params, unsigned int *val, int *dir); typedef int (* ma_snd_pcm_hw_params_get_access_proc) (const ma_snd_pcm_hw_params_t *params, ma_snd_pcm_access_t *_access); typedef int (* ma_snd_pcm_hw_params_test_format_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, ma_snd_pcm_format_t val); typedef int (* ma_snd_pcm_hw_params_test_channels_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, unsigned int val); typedef int (* ma_snd_pcm_hw_params_test_rate_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, unsigned int val, int dir); typedef int (* ma_snd_pcm_hw_params_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params); typedef size_t (* ma_snd_pcm_sw_params_sizeof_proc) (void); typedef int (* ma_snd_pcm_sw_params_current_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_sw_params_t *params); typedef int (* ma_snd_pcm_sw_params_get_boundary_proc) (const ma_snd_pcm_sw_params_t *params, ma_snd_pcm_uframes_t* val); typedef int (* ma_snd_pcm_sw_params_set_avail_min_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_sw_params_t *params, ma_snd_pcm_uframes_t val); typedef int (* ma_snd_pcm_sw_params_set_start_threshold_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_sw_params_t *params, ma_snd_pcm_uframes_t val); typedef int (* ma_snd_pcm_sw_params_set_stop_threshold_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_sw_params_t *params, ma_snd_pcm_uframes_t val); typedef int (* ma_snd_pcm_sw_params_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_sw_params_t *params); typedef size_t (* ma_snd_pcm_format_mask_sizeof_proc) (void); typedef int (* ma_snd_pcm_format_mask_test_proc) (const ma_snd_pcm_format_mask_t *mask, ma_snd_pcm_format_t val); typedef ma_snd_pcm_chmap_t * (* ma_snd_pcm_get_chmap_proc) (ma_snd_pcm_t *pcm); typedef ma_snd_pcm_state_t (* ma_snd_pcm_state_proc) (ma_snd_pcm_t *pcm); typedef int (* ma_snd_pcm_prepare_proc) (ma_snd_pcm_t *pcm); typedef int (* ma_snd_pcm_start_proc) (ma_snd_pcm_t *pcm); typedef int (* ma_snd_pcm_drop_proc) (ma_snd_pcm_t *pcm); typedef int (* ma_snd_pcm_drain_proc) (ma_snd_pcm_t *pcm); typedef int (* ma_snd_pcm_reset_proc) (ma_snd_pcm_t *pcm); typedef int (* ma_snd_device_name_hint_proc) (int card, const char *iface, void ***hints); typedef char * (* ma_snd_device_name_get_hint_proc) (const void *hint, const char *id); typedef int (* ma_snd_card_get_index_proc) (const char *name); typedef int (* ma_snd_device_name_free_hint_proc) (void **hints); typedef int (* ma_snd_pcm_mmap_begin_proc) (ma_snd_pcm_t *pcm, const ma_snd_pcm_channel_area_t **areas, ma_snd_pcm_uframes_t *offset, ma_snd_pcm_uframes_t *frames); typedef ma_snd_pcm_sframes_t (* ma_snd_pcm_mmap_commit_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_uframes_t offset, ma_snd_pcm_uframes_t frames); typedef int (* ma_snd_pcm_recover_proc) (ma_snd_pcm_t *pcm, int err, int silent); typedef ma_snd_pcm_sframes_t (* ma_snd_pcm_readi_proc) (ma_snd_pcm_t *pcm, void *buffer, ma_snd_pcm_uframes_t size); typedef ma_snd_pcm_sframes_t (* ma_snd_pcm_writei_proc) (ma_snd_pcm_t *pcm, const void *buffer, ma_snd_pcm_uframes_t size); typedef ma_snd_pcm_sframes_t (* ma_snd_pcm_avail_proc) (ma_snd_pcm_t *pcm); typedef ma_snd_pcm_sframes_t (* ma_snd_pcm_avail_update_proc) (ma_snd_pcm_t *pcm); typedef int (* ma_snd_pcm_wait_proc) (ma_snd_pcm_t *pcm, int timeout); typedef int (* ma_snd_pcm_nonblock_proc) (ma_snd_pcm_t *pcm, int nonblock); typedef int (* ma_snd_pcm_info_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_info_t* info); typedef size_t (* ma_snd_pcm_info_sizeof_proc) (void); typedef const char* (* ma_snd_pcm_info_get_name_proc) (const ma_snd_pcm_info_t* info); typedef int (* ma_snd_pcm_poll_descriptors_proc) (ma_snd_pcm_t *pcm, struct pollfd *pfds, unsigned int space); typedef int (* ma_snd_pcm_poll_descriptors_count_proc) (ma_snd_pcm_t *pcm); typedef int (* ma_snd_pcm_poll_descriptors_revents_proc) (ma_snd_pcm_t *pcm, struct pollfd *pfds, unsigned int nfds, unsigned short *revents); typedef int (* ma_snd_config_update_free_global_proc) (void); /* This array specifies each of the common devices that can be used for both playback and capture. */ static const char* g_maCommonDeviceNamesALSA[] = { "default", "null", "pulse", "jack" }; /* This array allows us to blacklist specific playback devices. */ static const char* g_maBlacklistedPlaybackDeviceNamesALSA[] = { "" }; /* This array allows us to blacklist specific capture devices. */ static const char* g_maBlacklistedCaptureDeviceNamesALSA[] = { "" }; static ma_snd_pcm_format_t ma_convert_ma_format_to_alsa_format(ma_format format) { ma_snd_pcm_format_t ALSAFormats[] = { MA_SND_PCM_FORMAT_UNKNOWN, /* ma_format_unknown */ MA_SND_PCM_FORMAT_U8, /* ma_format_u8 */ MA_SND_PCM_FORMAT_S16_LE, /* ma_format_s16 */ MA_SND_PCM_FORMAT_S24_3LE, /* ma_format_s24 */ MA_SND_PCM_FORMAT_S32_LE, /* ma_format_s32 */ MA_SND_PCM_FORMAT_FLOAT_LE /* ma_format_f32 */ }; if (ma_is_big_endian()) { ALSAFormats[0] = MA_SND_PCM_FORMAT_UNKNOWN; ALSAFormats[1] = MA_SND_PCM_FORMAT_U8; ALSAFormats[2] = MA_SND_PCM_FORMAT_S16_BE; ALSAFormats[3] = MA_SND_PCM_FORMAT_S24_3BE; ALSAFormats[4] = MA_SND_PCM_FORMAT_S32_BE; ALSAFormats[5] = MA_SND_PCM_FORMAT_FLOAT_BE; } return ALSAFormats[format]; } static ma_format ma_format_from_alsa(ma_snd_pcm_format_t formatALSA) { if (ma_is_little_endian()) { switch (formatALSA) { case MA_SND_PCM_FORMAT_S16_LE: return ma_format_s16; case MA_SND_PCM_FORMAT_S24_3LE: return ma_format_s24; case MA_SND_PCM_FORMAT_S32_LE: return ma_format_s32; case MA_SND_PCM_FORMAT_FLOAT_LE: return ma_format_f32; default: break; } } else { switch (formatALSA) { case MA_SND_PCM_FORMAT_S16_BE: return ma_format_s16; case MA_SND_PCM_FORMAT_S24_3BE: return ma_format_s24; case MA_SND_PCM_FORMAT_S32_BE: return ma_format_s32; case MA_SND_PCM_FORMAT_FLOAT_BE: return ma_format_f32; default: break; } } /* Endian agnostic. */ switch (formatALSA) { case MA_SND_PCM_FORMAT_U8: return ma_format_u8; default: return ma_format_unknown; } } static ma_channel ma_convert_alsa_channel_position_to_ma_channel(unsigned int alsaChannelPos) { switch (alsaChannelPos) { case MA_SND_CHMAP_MONO: return MA_CHANNEL_MONO; case MA_SND_CHMAP_FL: return MA_CHANNEL_FRONT_LEFT; case MA_SND_CHMAP_FR: return MA_CHANNEL_FRONT_RIGHT; case MA_SND_CHMAP_RL: return MA_CHANNEL_BACK_LEFT; case MA_SND_CHMAP_RR: return MA_CHANNEL_BACK_RIGHT; case MA_SND_CHMAP_FC: return MA_CHANNEL_FRONT_CENTER; case MA_SND_CHMAP_LFE: return MA_CHANNEL_LFE; case MA_SND_CHMAP_SL: return MA_CHANNEL_SIDE_LEFT; case MA_SND_CHMAP_SR: return MA_CHANNEL_SIDE_RIGHT; case MA_SND_CHMAP_RC: return MA_CHANNEL_BACK_CENTER; case MA_SND_CHMAP_FLC: return MA_CHANNEL_FRONT_LEFT_CENTER; case MA_SND_CHMAP_FRC: return MA_CHANNEL_FRONT_RIGHT_CENTER; case MA_SND_CHMAP_RLC: return 0; case MA_SND_CHMAP_RRC: return 0; case MA_SND_CHMAP_FLW: return 0; case MA_SND_CHMAP_FRW: return 0; case MA_SND_CHMAP_FLH: return 0; case MA_SND_CHMAP_FCH: return 0; case MA_SND_CHMAP_FRH: return 0; case MA_SND_CHMAP_TC: return MA_CHANNEL_TOP_CENTER; case MA_SND_CHMAP_TFL: return MA_CHANNEL_TOP_FRONT_LEFT; case MA_SND_CHMAP_TFR: return MA_CHANNEL_TOP_FRONT_RIGHT; case MA_SND_CHMAP_TFC: return MA_CHANNEL_TOP_FRONT_CENTER; case MA_SND_CHMAP_TRL: return MA_CHANNEL_TOP_BACK_LEFT; case MA_SND_CHMAP_TRR: return MA_CHANNEL_TOP_BACK_RIGHT; case MA_SND_CHMAP_TRC: return MA_CHANNEL_TOP_BACK_CENTER; default: break; } return 0; } static ma_bool32 ma_is_common_device_name__alsa(const char* name) { size_t iName; for (iName = 0; iName < ma_countof(g_maCommonDeviceNamesALSA); ++iName) { if (ma_strcmp(name, g_maCommonDeviceNamesALSA[iName]) == 0) { return MA_TRUE; } } return MA_FALSE; } static ma_bool32 ma_is_playback_device_blacklisted__alsa(const char* name) { size_t iName; for (iName = 0; iName < ma_countof(g_maBlacklistedPlaybackDeviceNamesALSA); ++iName) { if (ma_strcmp(name, g_maBlacklistedPlaybackDeviceNamesALSA[iName]) == 0) { return MA_TRUE; } } return MA_FALSE; } static ma_bool32 ma_is_capture_device_blacklisted__alsa(const char* name) { size_t iName; for (iName = 0; iName < ma_countof(g_maBlacklistedCaptureDeviceNamesALSA); ++iName) { if (ma_strcmp(name, g_maBlacklistedCaptureDeviceNamesALSA[iName]) == 0) { return MA_TRUE; } } return MA_FALSE; } static ma_bool32 ma_is_device_blacklisted__alsa(ma_device_type deviceType, const char* name) { if (deviceType == ma_device_type_playback) { return ma_is_playback_device_blacklisted__alsa(name); } else { return ma_is_capture_device_blacklisted__alsa(name); } } static const char* ma_find_char(const char* str, char c, int* index) { int i = 0; for (;;) { if (str[i] == '\0') { if (index) *index = -1; return NULL; } if (str[i] == c) { if (index) *index = i; return str + i; } i += 1; } /* Should never get here, but treat it as though the character was not found to make me feel better inside. */ if (index) *index = -1; return NULL; } static ma_bool32 ma_is_device_name_in_hw_format__alsa(const char* hwid) { /* This function is just checking whether or not hwid is in "hw:%d,%d" format. */ int commaPos; const char* dev; int i; if (hwid == NULL) { return MA_FALSE; } if (hwid[0] != 'h' || hwid[1] != 'w' || hwid[2] != ':') { return MA_FALSE; } hwid += 3; dev = ma_find_char(hwid, ',', &commaPos); if (dev == NULL) { return MA_FALSE; } else { dev += 1; /* Skip past the ",". */ } /* Check if the part between the ":" and the "," contains only numbers. If not, return false. */ for (i = 0; i < commaPos; ++i) { if (hwid[i] < '0' || hwid[i] > '9') { return MA_FALSE; } } /* Check if everything after the "," is numeric. If not, return false. */ i = 0; while (dev[i] != '\0') { if (dev[i] < '0' || dev[i] > '9') { return MA_FALSE; } i += 1; } return MA_TRUE; } static int ma_convert_device_name_to_hw_format__alsa(ma_context* pContext, char* dst, size_t dstSize, const char* src) /* Returns 0 on success, non-0 on error. */ { /* src should look something like this: "hw:CARD=I82801AAICH,DEV=0" */ int colonPos; int commaPos; char card[256]; const char* dev; int cardIndex; if (dst == NULL) { return -1; } if (dstSize < 7) { return -1; /* Absolute minimum size of the output buffer is 7 bytes. */ } *dst = '\0'; /* Safety. */ if (src == NULL) { return -1; } /* If the input name is already in "hw:%d,%d" format, just return that verbatim. */ if (ma_is_device_name_in_hw_format__alsa(src)) { return ma_strcpy_s(dst, dstSize, src); } src = ma_find_char(src, ':', &colonPos); if (src == NULL) { return -1; /* Couldn't find a colon */ } dev = ma_find_char(src, ',', &commaPos); if (dev == NULL) { dev = "0"; ma_strncpy_s(card, sizeof(card), src+6, (size_t)-1); /* +6 = ":CARD=" */ } else { dev = dev + 5; /* +5 = ",DEV=" */ ma_strncpy_s(card, sizeof(card), src+6, commaPos-6); /* +6 = ":CARD=" */ } cardIndex = ((ma_snd_card_get_index_proc)pContext->alsa.snd_card_get_index)(card); if (cardIndex < 0) { return -2; /* Failed to retrieve the card index. */ } /* Construction. */ dst[0] = 'h'; dst[1] = 'w'; dst[2] = ':'; if (ma_itoa_s(cardIndex, dst+3, dstSize-3, 10) != 0) { return -3; } if (ma_strcat_s(dst, dstSize, ",") != 0) { return -3; } if (ma_strcat_s(dst, dstSize, dev) != 0) { return -3; } return 0; } static ma_bool32 ma_does_id_exist_in_list__alsa(ma_device_id* pUniqueIDs, ma_uint32 count, const char* pHWID) { ma_uint32 i; MA_ASSERT(pHWID != NULL); for (i = 0; i < count; ++i) { if (ma_strcmp(pUniqueIDs[i].alsa, pHWID) == 0) { return MA_TRUE; } } return MA_FALSE; } static ma_result ma_context_open_pcm__alsa(ma_context* pContext, ma_share_mode shareMode, ma_device_type deviceType, const ma_device_id* pDeviceID, int openMode, ma_snd_pcm_t** ppPCM) { ma_snd_pcm_t* pPCM; ma_snd_pcm_stream_t stream; MA_ASSERT(pContext != NULL); MA_ASSERT(ppPCM != NULL); *ppPCM = NULL; pPCM = NULL; stream = (deviceType == ma_device_type_playback) ? MA_SND_PCM_STREAM_PLAYBACK : MA_SND_PCM_STREAM_CAPTURE; if (pDeviceID == NULL) { ma_bool32 isDeviceOpen; size_t i; /* We're opening the default device. I don't know if trying anything other than "default" is necessary, but it makes me feel better to try as hard as we can get to get _something_ working. */ const char* defaultDeviceNames[] = { "default", NULL, NULL, NULL, NULL, NULL, NULL }; if (shareMode == ma_share_mode_exclusive) { defaultDeviceNames[1] = "hw"; defaultDeviceNames[2] = "hw:0"; defaultDeviceNames[3] = "hw:0,0"; } else { if (deviceType == ma_device_type_playback) { defaultDeviceNames[1] = "dmix"; defaultDeviceNames[2] = "dmix:0"; defaultDeviceNames[3] = "dmix:0,0"; } else { defaultDeviceNames[1] = "dsnoop"; defaultDeviceNames[2] = "dsnoop:0"; defaultDeviceNames[3] = "dsnoop:0,0"; } defaultDeviceNames[4] = "hw"; defaultDeviceNames[5] = "hw:0"; defaultDeviceNames[6] = "hw:0,0"; } isDeviceOpen = MA_FALSE; for (i = 0; i < ma_countof(defaultDeviceNames); ++i) { if (defaultDeviceNames[i] != NULL && defaultDeviceNames[i][0] != '\0') { if (((ma_snd_pcm_open_proc)pContext->alsa.snd_pcm_open)(&pPCM, defaultDeviceNames[i], stream, openMode) == 0) { isDeviceOpen = MA_TRUE; break; } } } if (!isDeviceOpen) { ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[ALSA] snd_pcm_open() failed when trying to open an appropriate default device."); return MA_FAILED_TO_OPEN_BACKEND_DEVICE; } } else { /* We're trying to open a specific device. There's a few things to consider here: miniaudio recongnizes a special format of device id that excludes the "hw", "dmix", etc. prefix. It looks like this: ":0,0", ":0,1", etc. When an ID of this format is specified, it indicates to miniaudio that it can try different combinations of plugins ("hw", "dmix", etc.) until it finds an appropriate one that works. This comes in very handy when trying to open a device in shared mode ("dmix"), vs exclusive mode ("hw"). */ /* May end up needing to make small adjustments to the ID, so make a copy. */ ma_device_id deviceID = *pDeviceID; int resultALSA = -ENODEV; if (deviceID.alsa[0] != ':') { /* The ID is not in ":0,0" format. Use the ID exactly as-is. */ resultALSA = ((ma_snd_pcm_open_proc)pContext->alsa.snd_pcm_open)(&pPCM, deviceID.alsa, stream, openMode); } else { char hwid[256]; /* The ID is in ":0,0" format. Try different plugins depending on the shared mode. */ if (deviceID.alsa[1] == '\0') { deviceID.alsa[0] = '\0'; /* An ID of ":" should be converted to "". */ } if (shareMode == ma_share_mode_shared) { if (deviceType == ma_device_type_playback) { ma_strcpy_s(hwid, sizeof(hwid), "dmix"); } else { ma_strcpy_s(hwid, sizeof(hwid), "dsnoop"); } if (ma_strcat_s(hwid, sizeof(hwid), deviceID.alsa) == 0) { resultALSA = ((ma_snd_pcm_open_proc)pContext->alsa.snd_pcm_open)(&pPCM, hwid, stream, openMode); } } /* If at this point we still don't have an open device it means we're either preferencing exclusive mode or opening with "dmix"/"dsnoop" failed. */ if (resultALSA != 0) { ma_strcpy_s(hwid, sizeof(hwid), "hw"); if (ma_strcat_s(hwid, sizeof(hwid), deviceID.alsa) == 0) { resultALSA = ((ma_snd_pcm_open_proc)pContext->alsa.snd_pcm_open)(&pPCM, hwid, stream, openMode); } } } if (resultALSA < 0) { ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[ALSA] snd_pcm_open() failed."); return ma_result_from_errno(-resultALSA); } } *ppPCM = pPCM; return MA_SUCCESS; } static ma_result ma_context_enumerate_devices__alsa(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData) { int resultALSA; ma_bool32 cbResult = MA_TRUE; char** ppDeviceHints; ma_device_id* pUniqueIDs = NULL; ma_uint32 uniqueIDCount = 0; char** ppNextDeviceHint; MA_ASSERT(pContext != NULL); MA_ASSERT(callback != NULL); ma_mutex_lock(&pContext->alsa.internalDeviceEnumLock); resultALSA = ((ma_snd_device_name_hint_proc)pContext->alsa.snd_device_name_hint)(-1, "pcm", (void***)&ppDeviceHints); if (resultALSA < 0) { ma_mutex_unlock(&pContext->alsa.internalDeviceEnumLock); return ma_result_from_errno(-resultALSA); } ppNextDeviceHint = ppDeviceHints; while (*ppNextDeviceHint != NULL) { char* NAME = ((ma_snd_device_name_get_hint_proc)pContext->alsa.snd_device_name_get_hint)(*ppNextDeviceHint, "NAME"); char* DESC = ((ma_snd_device_name_get_hint_proc)pContext->alsa.snd_device_name_get_hint)(*ppNextDeviceHint, "DESC"); char* IOID = ((ma_snd_device_name_get_hint_proc)pContext->alsa.snd_device_name_get_hint)(*ppNextDeviceHint, "IOID"); ma_device_type deviceType = ma_device_type_playback; ma_bool32 stopEnumeration = MA_FALSE; char hwid[sizeof(pUniqueIDs->alsa)]; ma_device_info deviceInfo; if ((IOID == NULL || ma_strcmp(IOID, "Output") == 0)) { deviceType = ma_device_type_playback; } if ((IOID != NULL && ma_strcmp(IOID, "Input" ) == 0)) { deviceType = ma_device_type_capture; } if (NAME != NULL) { if (pContext->alsa.useVerboseDeviceEnumeration) { /* Verbose mode. Use the name exactly as-is. */ ma_strncpy_s(hwid, sizeof(hwid), NAME, (size_t)-1); } else { /* Simplified mode. Use ":%d,%d" format. */ if (ma_convert_device_name_to_hw_format__alsa(pContext, hwid, sizeof(hwid), NAME) == 0) { /* At this point, hwid looks like "hw:0,0". In simplified enumeration mode, we actually want to strip off the plugin name so it looks like ":0,0". The reason for this is that this special format is detected at device initialization time and is used as an indicator to try and use the most appropriate plugin depending on the device type and sharing mode. */ char* dst = hwid; char* src = hwid+2; while ((*dst++ = *src++)); } else { /* Conversion to "hw:%d,%d" failed. Just use the name as-is. */ ma_strncpy_s(hwid, sizeof(hwid), NAME, (size_t)-1); } if (ma_does_id_exist_in_list__alsa(pUniqueIDs, uniqueIDCount, hwid)) { goto next_device; /* The device has already been enumerated. Move on to the next one. */ } else { /* The device has not yet been enumerated. Make sure it's added to our list so that it's not enumerated again. */ size_t newCapacity = sizeof(*pUniqueIDs) * (uniqueIDCount + 1); ma_device_id* pNewUniqueIDs = (ma_device_id*)ma_realloc(pUniqueIDs, newCapacity, &pContext->allocationCallbacks); if (pNewUniqueIDs == NULL) { goto next_device; /* Failed to allocate memory. */ } pUniqueIDs = pNewUniqueIDs; MA_COPY_MEMORY(pUniqueIDs[uniqueIDCount].alsa, hwid, sizeof(hwid)); uniqueIDCount += 1; } } } else { MA_ZERO_MEMORY(hwid, sizeof(hwid)); } MA_ZERO_OBJECT(&deviceInfo); ma_strncpy_s(deviceInfo.id.alsa, sizeof(deviceInfo.id.alsa), hwid, (size_t)-1); /* There's no good way to determine whether or not a device is the default on Linux. We're just going to do something simple and just use the name of "default" as the indicator. */ if (ma_strcmp(deviceInfo.id.alsa, "default") == 0) { deviceInfo.isDefault = MA_TRUE; } /* DESC is the friendly name. We treat this slightly differently depending on whether or not we are using verbose device enumeration. In verbose mode we want to take the entire description so that the end-user can distinguish between the subdevices of each card/dev pair. In simplified mode, however, we only want the first part of the description. The value in DESC seems to be split into two lines, with the first line being the name of the device and the second line being a description of the device. I don't like having the description be across two lines because it makes formatting ugly and annoying. I'm therefore deciding to put it all on a single line with the second line being put into parentheses. In simplified mode I'm just stripping the second line entirely. */ if (DESC != NULL) { int lfPos; const char* line2 = ma_find_char(DESC, '\n', &lfPos); if (line2 != NULL) { line2 += 1; /* Skip past the new-line character. */ if (pContext->alsa.useVerboseDeviceEnumeration) { /* Verbose mode. Put the second line in brackets. */ ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), DESC, lfPos); ma_strcat_s (deviceInfo.name, sizeof(deviceInfo.name), " ("); ma_strcat_s (deviceInfo.name, sizeof(deviceInfo.name), line2); ma_strcat_s (deviceInfo.name, sizeof(deviceInfo.name), ")"); } else { /* Simplified mode. Strip the second line entirely. */ ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), DESC, lfPos); } } else { /* There's no second line. Just copy the whole description. */ ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), DESC, (size_t)-1); } } if (!ma_is_device_blacklisted__alsa(deviceType, NAME)) { cbResult = callback(pContext, deviceType, &deviceInfo, pUserData); } /* Some devices are both playback and capture, but they are only enumerated by ALSA once. We need to fire the callback again for the other device type in this case. We do this for known devices and where the IOID hint is NULL, which means both Input and Output. */ if (cbResult) { if (ma_is_common_device_name__alsa(NAME) || IOID == NULL) { if (deviceType == ma_device_type_playback) { if (!ma_is_capture_device_blacklisted__alsa(NAME)) { cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData); } } else { if (!ma_is_playback_device_blacklisted__alsa(NAME)) { cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData); } } } } if (cbResult == MA_FALSE) { stopEnumeration = MA_TRUE; } next_device: free(NAME); free(DESC); free(IOID); ppNextDeviceHint += 1; /* We need to stop enumeration if the callback returned false. */ if (stopEnumeration) { break; } } ma_free(pUniqueIDs, &pContext->allocationCallbacks); ((ma_snd_device_name_free_hint_proc)pContext->alsa.snd_device_name_free_hint)((void**)ppDeviceHints); ma_mutex_unlock(&pContext->alsa.internalDeviceEnumLock); return MA_SUCCESS; } typedef struct { ma_device_type deviceType; const ma_device_id* pDeviceID; ma_share_mode shareMode; ma_device_info* pDeviceInfo; ma_bool32 foundDevice; } ma_context_get_device_info_enum_callback_data__alsa; static ma_bool32 ma_context_get_device_info_enum_callback__alsa(ma_context* pContext, ma_device_type deviceType, const ma_device_info* pDeviceInfo, void* pUserData) { ma_context_get_device_info_enum_callback_data__alsa* pData = (ma_context_get_device_info_enum_callback_data__alsa*)pUserData; MA_ASSERT(pData != NULL); (void)pContext; if (pData->pDeviceID == NULL && ma_strcmp(pDeviceInfo->id.alsa, "default") == 0) { ma_strncpy_s(pData->pDeviceInfo->name, sizeof(pData->pDeviceInfo->name), pDeviceInfo->name, (size_t)-1); pData->foundDevice = MA_TRUE; } else { if (pData->deviceType == deviceType && (pData->pDeviceID != NULL && ma_strcmp(pData->pDeviceID->alsa, pDeviceInfo->id.alsa) == 0)) { ma_strncpy_s(pData->pDeviceInfo->name, sizeof(pData->pDeviceInfo->name), pDeviceInfo->name, (size_t)-1); pData->foundDevice = MA_TRUE; } } /* Keep enumerating until we have found the device. */ return !pData->foundDevice; } static void ma_context_test_rate_and_add_native_data_format__alsa(ma_context* pContext, ma_snd_pcm_t* pPCM, ma_snd_pcm_hw_params_t* pHWParams, ma_format format, ma_uint32 channels, ma_uint32 sampleRate, ma_uint32 flags, ma_device_info* pDeviceInfo) { MA_ASSERT(pPCM != NULL); MA_ASSERT(pHWParams != NULL); MA_ASSERT(pDeviceInfo != NULL); if (pDeviceInfo->nativeDataFormatCount < ma_countof(pDeviceInfo->nativeDataFormats) && ((ma_snd_pcm_hw_params_test_rate_proc)pContext->alsa.snd_pcm_hw_params_test_rate)(pPCM, pHWParams, sampleRate, 0) == 0) { pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].format = format; pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].channels = channels; pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].sampleRate = sampleRate; pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].flags = flags; pDeviceInfo->nativeDataFormatCount += 1; } } static void ma_context_iterate_rates_and_add_native_data_format__alsa(ma_context* pContext, ma_snd_pcm_t* pPCM, ma_snd_pcm_hw_params_t* pHWParams, ma_format format, ma_uint32 channels, ma_uint32 flags, ma_device_info* pDeviceInfo) { ma_uint32 iSampleRate; unsigned int minSampleRate; unsigned int maxSampleRate; int sampleRateDir; /* Not used. Just passed into snd_pcm_hw_params_get_rate_min/max(). */ /* There could be a range. */ ((ma_snd_pcm_hw_params_get_rate_min_proc)pContext->alsa.snd_pcm_hw_params_get_rate_min)(pHWParams, &minSampleRate, &sampleRateDir); ((ma_snd_pcm_hw_params_get_rate_max_proc)pContext->alsa.snd_pcm_hw_params_get_rate_max)(pHWParams, &maxSampleRate, &sampleRateDir); /* Make sure our sample rates are clamped to sane values. Stupid devices like "pulse" will reports rates like "1" which is ridiculus. */ minSampleRate = ma_clamp(minSampleRate, (unsigned int)ma_standard_sample_rate_min, (unsigned int)ma_standard_sample_rate_max); maxSampleRate = ma_clamp(maxSampleRate, (unsigned int)ma_standard_sample_rate_min, (unsigned int)ma_standard_sample_rate_max); for (iSampleRate = 0; iSampleRate < ma_countof(g_maStandardSampleRatePriorities); iSampleRate += 1) { ma_uint32 standardSampleRate = g_maStandardSampleRatePriorities[iSampleRate]; if (standardSampleRate >= minSampleRate && standardSampleRate <= maxSampleRate) { ma_context_test_rate_and_add_native_data_format__alsa(pContext, pPCM, pHWParams, format, channels, standardSampleRate, flags, pDeviceInfo); } } /* Now make sure our min and max rates are included just in case they aren't in the range of our standard rates. */ if (!ma_is_standard_sample_rate(minSampleRate)) { ma_context_test_rate_and_add_native_data_format__alsa(pContext, pPCM, pHWParams, format, channels, minSampleRate, flags, pDeviceInfo); } if (!ma_is_standard_sample_rate(maxSampleRate) && maxSampleRate != minSampleRate) { ma_context_test_rate_and_add_native_data_format__alsa(pContext, pPCM, pHWParams, format, channels, maxSampleRate, flags, pDeviceInfo); } } static ma_result ma_context_get_device_info__alsa(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo) { ma_context_get_device_info_enum_callback_data__alsa data; ma_result result; int resultALSA; ma_snd_pcm_t* pPCM; ma_snd_pcm_hw_params_t* pHWParams; ma_uint32 iFormat; ma_uint32 iChannel; MA_ASSERT(pContext != NULL); /* We just enumerate to find basic information about the device. */ data.deviceType = deviceType; data.pDeviceID = pDeviceID; data.pDeviceInfo = pDeviceInfo; data.foundDevice = MA_FALSE; result = ma_context_enumerate_devices__alsa(pContext, ma_context_get_device_info_enum_callback__alsa, &data); if (result != MA_SUCCESS) { return result; } if (!data.foundDevice) { return MA_NO_DEVICE; } if (ma_strcmp(pDeviceInfo->id.alsa, "default") == 0) { pDeviceInfo->isDefault = MA_TRUE; } /* For detailed info we need to open the device. */ result = ma_context_open_pcm__alsa(pContext, ma_share_mode_shared, deviceType, pDeviceID, 0, &pPCM); if (result != MA_SUCCESS) { return result; } /* We need to initialize a HW parameters object in order to know what formats are supported. */ pHWParams = (ma_snd_pcm_hw_params_t*)ma_calloc(((ma_snd_pcm_hw_params_sizeof_proc)pContext->alsa.snd_pcm_hw_params_sizeof)(), &pContext->allocationCallbacks); if (pHWParams == NULL) { ((ma_snd_pcm_close_proc)pContext->alsa.snd_pcm_close)(pPCM); return MA_OUT_OF_MEMORY; } resultALSA = ((ma_snd_pcm_hw_params_any_proc)pContext->alsa.snd_pcm_hw_params_any)(pPCM, pHWParams); if (resultALSA < 0) { ma_free(pHWParams, &pContext->allocationCallbacks); ((ma_snd_pcm_close_proc)pContext->alsa.snd_pcm_close)(pPCM); ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to initialize hardware parameters. snd_pcm_hw_params_any() failed."); return ma_result_from_errno(-resultALSA); } /* Some ALSA devices can support many permutations of formats, channels and rates. We only support a fixed number of permutations which means we need to employ some strategies to ensure the best combinations are returned. An example is the "pulse" device which can do it's own data conversion in software and as a result can support any combination of format, channels and rate. We want to ensure the the first data formats are the best. We have a list of favored sample formats and sample rates, so these will be the basis of our iteration. */ /* Formats. We just iterate over our standard formats and test them, making sure we reset the configuration space each iteration. */ for (iFormat = 0; iFormat < ma_countof(g_maFormatPriorities); iFormat += 1) { ma_format format = g_maFormatPriorities[iFormat]; /* For each format we need to make sure we reset the configuration space so we don't return channel counts and rates that aren't compatible with a format. */ ((ma_snd_pcm_hw_params_any_proc)pContext->alsa.snd_pcm_hw_params_any)(pPCM, pHWParams); /* Test the format first. If this fails it means the format is not supported and we can skip it. */ if (((ma_snd_pcm_hw_params_test_format_proc)pContext->alsa.snd_pcm_hw_params_test_format)(pPCM, pHWParams, ma_convert_ma_format_to_alsa_format(format)) == 0) { /* The format is supported. */ unsigned int minChannels; unsigned int maxChannels; /* The configuration space needs to be restricted to this format so we can get an accurate picture of which sample rates and channel counts are support with this format. */ ((ma_snd_pcm_hw_params_set_format_proc)pContext->alsa.snd_pcm_hw_params_set_format)(pPCM, pHWParams, ma_convert_ma_format_to_alsa_format(format)); /* Now we need to check for supported channels. */ ((ma_snd_pcm_hw_params_get_channels_min_proc)pContext->alsa.snd_pcm_hw_params_get_channels_min)(pHWParams, &minChannels); ((ma_snd_pcm_hw_params_get_channels_max_proc)pContext->alsa.snd_pcm_hw_params_get_channels_max)(pHWParams, &maxChannels); if (minChannels > MA_MAX_CHANNELS) { continue; /* Too many channels. */ } if (maxChannels < MA_MIN_CHANNELS) { continue; /* Not enough channels. */ } /* Make sure the channel count is clamped. This is mainly intended for the max channels because some devices can report an unbound maximum. */ minChannels = ma_clamp(minChannels, MA_MIN_CHANNELS, MA_MAX_CHANNELS); maxChannels = ma_clamp(maxChannels, MA_MIN_CHANNELS, MA_MAX_CHANNELS); if (minChannels == MA_MIN_CHANNELS && maxChannels == MA_MAX_CHANNELS) { /* The device supports all channels. Don't iterate over every single one. Instead just set the channels to 0 which means all channels are supported. */ ma_context_iterate_rates_and_add_native_data_format__alsa(pContext, pPCM, pHWParams, format, 0, 0, pDeviceInfo); /* Intentionally setting the channel count to 0 as that means all channels are supported. */ } else { /* The device only supports a specific set of channels. We need to iterate over all of them. */ for (iChannel = minChannels; iChannel <= maxChannels; iChannel += 1) { /* Test the channel before applying it to the configuration space. */ unsigned int channels = iChannel; /* Make sure our channel range is reset before testing again or else we'll always fail the test. */ ((ma_snd_pcm_hw_params_any_proc)pContext->alsa.snd_pcm_hw_params_any)(pPCM, pHWParams); ((ma_snd_pcm_hw_params_set_format_proc)pContext->alsa.snd_pcm_hw_params_set_format)(pPCM, pHWParams, ma_convert_ma_format_to_alsa_format(format)); if (((ma_snd_pcm_hw_params_test_channels_proc)pContext->alsa.snd_pcm_hw_params_test_channels)(pPCM, pHWParams, channels) == 0) { /* The channel count is supported. */ /* The configuration space now needs to be restricted to the channel count before extracting the sample rate. */ ((ma_snd_pcm_hw_params_set_channels_proc)pContext->alsa.snd_pcm_hw_params_set_channels)(pPCM, pHWParams, channels); /* Only after the configuration space has been restricted to the specific channel count should we iterate over our sample rates. */ ma_context_iterate_rates_and_add_native_data_format__alsa(pContext, pPCM, pHWParams, format, channels, 0, pDeviceInfo); } else { /* The channel count is not supported. Skip. */ } } } } else { /* The format is not supported. Skip. */ } } ma_free(pHWParams, &pContext->allocationCallbacks); ((ma_snd_pcm_close_proc)pContext->alsa.snd_pcm_close)(pPCM); return MA_SUCCESS; } static ma_result ma_device_uninit__alsa(ma_device* pDevice) { MA_ASSERT(pDevice != NULL); if ((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture) { ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture); close(pDevice->alsa.wakeupfdCapture); ma_free(pDevice->alsa.pPollDescriptorsCapture, &pDevice->pContext->allocationCallbacks); } if ((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback) { ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback); close(pDevice->alsa.wakeupfdPlayback); ma_free(pDevice->alsa.pPollDescriptorsPlayback, &pDevice->pContext->allocationCallbacks); } return MA_SUCCESS; } static ma_result ma_device_init_by_type__alsa(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptor, ma_device_type deviceType) { ma_result result; int resultALSA; ma_snd_pcm_t* pPCM; ma_bool32 isUsingMMap; ma_snd_pcm_format_t formatALSA; ma_format internalFormat; ma_uint32 internalChannels; ma_uint32 internalSampleRate; ma_channel internalChannelMap[MA_MAX_CHANNELS]; ma_uint32 internalPeriodSizeInFrames; ma_uint32 internalPeriods; int openMode; ma_snd_pcm_hw_params_t* pHWParams; ma_snd_pcm_sw_params_t* pSWParams; ma_snd_pcm_uframes_t bufferBoundary; int pollDescriptorCount; struct pollfd* pPollDescriptors; int wakeupfd; MA_ASSERT(pConfig != NULL); MA_ASSERT(deviceType != ma_device_type_duplex); /* This function should only be called for playback _or_ capture, never duplex. */ MA_ASSERT(pDevice != NULL); formatALSA = ma_convert_ma_format_to_alsa_format(pDescriptor->format); openMode = 0; if (pConfig->alsa.noAutoResample) { openMode |= MA_SND_PCM_NO_AUTO_RESAMPLE; } if (pConfig->alsa.noAutoChannels) { openMode |= MA_SND_PCM_NO_AUTO_CHANNELS; } if (pConfig->alsa.noAutoFormat) { openMode |= MA_SND_PCM_NO_AUTO_FORMAT; } result = ma_context_open_pcm__alsa(pDevice->pContext, pDescriptor->shareMode, deviceType, pDescriptor->pDeviceID, openMode, &pPCM); if (result != MA_SUCCESS) { return result; } /* Hardware parameters. */ pHWParams = (ma_snd_pcm_hw_params_t*)ma_calloc(((ma_snd_pcm_hw_params_sizeof_proc)pDevice->pContext->alsa.snd_pcm_hw_params_sizeof)(), &pDevice->pContext->allocationCallbacks); if (pHWParams == NULL) { ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to allocate memory for hardware parameters."); return MA_OUT_OF_MEMORY; } resultALSA = ((ma_snd_pcm_hw_params_any_proc)pDevice->pContext->alsa.snd_pcm_hw_params_any)(pPCM, pHWParams); if (resultALSA < 0) { ma_free(pHWParams, &pDevice->pContext->allocationCallbacks); ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to initialize hardware parameters. snd_pcm_hw_params_any() failed."); return ma_result_from_errno(-resultALSA); } /* MMAP Mode. Try using interleaved MMAP access. If this fails, fall back to standard readi/writei. */ isUsingMMap = MA_FALSE; #if 0 /* NOTE: MMAP mode temporarily disabled. */ if (deviceType != ma_device_type_capture) { /* <-- Disabling MMAP mode for capture devices because I apparently do not have a device that supports it which means I can't test it... Contributions welcome. */ if (!pConfig->alsa.noMMap && ma_device__is_async(pDevice)) { if (((ma_snd_pcm_hw_params_set_access_proc)pDevice->pContext->alsa.snd_pcm_hw_params_set_access)(pPCM, pHWParams, MA_SND_PCM_ACCESS_MMAP_INTERLEAVED) == 0) { pDevice->alsa.isUsingMMap = MA_TRUE; } } } #endif if (!isUsingMMap) { resultALSA = ((ma_snd_pcm_hw_params_set_access_proc)pDevice->pContext->alsa.snd_pcm_hw_params_set_access)(pPCM, pHWParams, MA_SND_PCM_ACCESS_RW_INTERLEAVED); if (resultALSA < 0) { ma_free(pHWParams, &pDevice->pContext->allocationCallbacks); ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to set access mode to neither SND_PCM_ACCESS_MMAP_INTERLEAVED nor SND_PCM_ACCESS_RW_INTERLEAVED. snd_pcm_hw_params_set_access() failed."); return ma_result_from_errno(-resultALSA); } } /* Most important properties first. The documentation for OSS (yes, I know this is ALSA!) recommends format, channels, then sample rate. I can't find any documentation for ALSA specifically, so I'm going to copy the recommendation for OSS. */ /* Format. */ { /* At this point we should have a list of supported formats, so now we need to find the best one. We first check if the requested format is supported, and if so, use that one. If it's not supported, we just run though a list of formats and try to find the best one. */ if (formatALSA == MA_SND_PCM_FORMAT_UNKNOWN || ((ma_snd_pcm_hw_params_test_format_proc)pDevice->pContext->alsa.snd_pcm_hw_params_test_format)(pPCM, pHWParams, formatALSA) != 0) { /* We're either requesting the native format or the specified format is not supported. */ size_t iFormat; formatALSA = MA_SND_PCM_FORMAT_UNKNOWN; for (iFormat = 0; iFormat < ma_countof(g_maFormatPriorities); ++iFormat) { if (((ma_snd_pcm_hw_params_test_format_proc)pDevice->pContext->alsa.snd_pcm_hw_params_test_format)(pPCM, pHWParams, ma_convert_ma_format_to_alsa_format(g_maFormatPriorities[iFormat])) == 0) { formatALSA = ma_convert_ma_format_to_alsa_format(g_maFormatPriorities[iFormat]); break; } } if (formatALSA == MA_SND_PCM_FORMAT_UNKNOWN) { ma_free(pHWParams, &pDevice->pContext->allocationCallbacks); ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Format not supported. The device does not support any miniaudio formats."); return MA_FORMAT_NOT_SUPPORTED; } } resultALSA = ((ma_snd_pcm_hw_params_set_format_proc)pDevice->pContext->alsa.snd_pcm_hw_params_set_format)(pPCM, pHWParams, formatALSA); if (resultALSA < 0) { ma_free(pHWParams, &pDevice->pContext->allocationCallbacks); ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Format not supported. snd_pcm_hw_params_set_format() failed."); return ma_result_from_errno(-resultALSA); } internalFormat = ma_format_from_alsa(formatALSA); if (internalFormat == ma_format_unknown) { ma_free(pHWParams, &pDevice->pContext->allocationCallbacks); ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] The chosen format is not supported by miniaudio."); return MA_FORMAT_NOT_SUPPORTED; } } /* Channels. */ { unsigned int channels = pDescriptor->channels; if (channels == 0) { channels = MA_DEFAULT_CHANNELS; } resultALSA = ((ma_snd_pcm_hw_params_set_channels_near_proc)pDevice->pContext->alsa.snd_pcm_hw_params_set_channels_near)(pPCM, pHWParams, &channels); if (resultALSA < 0) { ma_free(pHWParams, &pDevice->pContext->allocationCallbacks); ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to set channel count. snd_pcm_hw_params_set_channels_near() failed."); return ma_result_from_errno(-resultALSA); } internalChannels = (ma_uint32)channels; } /* Sample Rate */ { unsigned int sampleRate; /* It appears there's either a bug in ALSA, a bug in some drivers, or I'm doing something silly; but having resampling enabled causes problems with some device configurations when used in conjunction with MMAP access mode. To fix this problem we need to disable resampling. To reproduce this problem, open the "plug:dmix" device, and set the sample rate to 44100. Internally, it looks like dmix uses a sample rate of 48000. The hardware parameters will get set correctly with no errors, but it looks like the 44100 -> 48000 resampling doesn't work properly - but only with MMAP access mode. You will notice skipping/crackling in the audio, and it'll run at a slightly faster rate. miniaudio has built-in support for sample rate conversion (albeit low quality at the moment), so disabling resampling should be fine for us. The only problem is that it won't be taking advantage of any kind of hardware-accelerated resampling and it won't be very good quality until I get a chance to improve the quality of miniaudio's software sample rate conversion. I don't currently know if the dmix plugin is the only one with this error. Indeed, this is the only one I've been able to reproduce this error with. In the future, we may want to restrict the disabling of resampling to only known bad plugins. */ ((ma_snd_pcm_hw_params_set_rate_resample_proc)pDevice->pContext->alsa.snd_pcm_hw_params_set_rate_resample)(pPCM, pHWParams, 0); sampleRate = pDescriptor->sampleRate; if (sampleRate == 0) { sampleRate = MA_DEFAULT_SAMPLE_RATE; } resultALSA = ((ma_snd_pcm_hw_params_set_rate_near_proc)pDevice->pContext->alsa.snd_pcm_hw_params_set_rate_near)(pPCM, pHWParams, &sampleRate, 0); if (resultALSA < 0) { ma_free(pHWParams, &pDevice->pContext->allocationCallbacks); ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Sample rate not supported. snd_pcm_hw_params_set_rate_near() failed."); return ma_result_from_errno(-resultALSA); } internalSampleRate = (ma_uint32)sampleRate; } /* Periods. */ { ma_uint32 periods = pDescriptor->periodCount; resultALSA = ((ma_snd_pcm_hw_params_set_periods_near_proc)pDevice->pContext->alsa.snd_pcm_hw_params_set_periods_near)(pPCM, pHWParams, &periods, NULL); if (resultALSA < 0) { ma_free(pHWParams, &pDevice->pContext->allocationCallbacks); ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to set period count. snd_pcm_hw_params_set_periods_near() failed."); return ma_result_from_errno(-resultALSA); } internalPeriods = periods; } /* Buffer Size */ { ma_snd_pcm_uframes_t actualBufferSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptor, internalSampleRate, pConfig->performanceProfile) * internalPeriods; resultALSA = ((ma_snd_pcm_hw_params_set_buffer_size_near_proc)pDevice->pContext->alsa.snd_pcm_hw_params_set_buffer_size_near)(pPCM, pHWParams, &actualBufferSizeInFrames); if (resultALSA < 0) { ma_free(pHWParams, &pDevice->pContext->allocationCallbacks); ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to set buffer size for device. snd_pcm_hw_params_set_buffer_size() failed."); return ma_result_from_errno(-resultALSA); } internalPeriodSizeInFrames = actualBufferSizeInFrames / internalPeriods; } /* Apply hardware parameters. */ resultALSA = ((ma_snd_pcm_hw_params_proc)pDevice->pContext->alsa.snd_pcm_hw_params)(pPCM, pHWParams); if (resultALSA < 0) { ma_free(pHWParams, &pDevice->pContext->allocationCallbacks); ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to set hardware parameters. snd_pcm_hw_params() failed."); return ma_result_from_errno(-resultALSA); } ma_free(pHWParams, &pDevice->pContext->allocationCallbacks); pHWParams = NULL; /* Software parameters. */ pSWParams = (ma_snd_pcm_sw_params_t*)ma_calloc(((ma_snd_pcm_sw_params_sizeof_proc)pDevice->pContext->alsa.snd_pcm_sw_params_sizeof)(), &pDevice->pContext->allocationCallbacks); if (pSWParams == NULL) { ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to allocate memory for software parameters."); return MA_OUT_OF_MEMORY; } resultALSA = ((ma_snd_pcm_sw_params_current_proc)pDevice->pContext->alsa.snd_pcm_sw_params_current)(pPCM, pSWParams); if (resultALSA < 0) { ma_free(pSWParams, &pDevice->pContext->allocationCallbacks); ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to initialize software parameters. snd_pcm_sw_params_current() failed."); return ma_result_from_errno(-resultALSA); } resultALSA = ((ma_snd_pcm_sw_params_set_avail_min_proc)pDevice->pContext->alsa.snd_pcm_sw_params_set_avail_min)(pPCM, pSWParams, ma_prev_power_of_2(internalPeriodSizeInFrames)); if (resultALSA < 0) { ma_free(pSWParams, &pDevice->pContext->allocationCallbacks); ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] snd_pcm_sw_params_set_avail_min() failed."); return ma_result_from_errno(-resultALSA); } resultALSA = ((ma_snd_pcm_sw_params_get_boundary_proc)pDevice->pContext->alsa.snd_pcm_sw_params_get_boundary)(pSWParams, &bufferBoundary); if (resultALSA < 0) { bufferBoundary = internalPeriodSizeInFrames * internalPeriods; } if (deviceType == ma_device_type_playback && !isUsingMMap) { /* Only playback devices in writei/readi mode need a start threshold. */ /* Subtle detail here with the start threshold. When in playback-only mode (no full-duplex) we can set the start threshold to the size of a period. But for full-duplex we need to set it such that it is at least two periods. */ resultALSA = ((ma_snd_pcm_sw_params_set_start_threshold_proc)pDevice->pContext->alsa.snd_pcm_sw_params_set_start_threshold)(pPCM, pSWParams, internalPeriodSizeInFrames*2); if (resultALSA < 0) { ma_free(pSWParams, &pDevice->pContext->allocationCallbacks); ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to set start threshold for playback device. snd_pcm_sw_params_set_start_threshold() failed."); return ma_result_from_errno(-resultALSA); } resultALSA = ((ma_snd_pcm_sw_params_set_stop_threshold_proc)pDevice->pContext->alsa.snd_pcm_sw_params_set_stop_threshold)(pPCM, pSWParams, bufferBoundary); if (resultALSA < 0) { /* Set to boundary to loop instead of stop in the event of an xrun. */ ma_free(pSWParams, &pDevice->pContext->allocationCallbacks); ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to set stop threshold for playback device. snd_pcm_sw_params_set_stop_threshold() failed."); return ma_result_from_errno(-resultALSA); } } resultALSA = ((ma_snd_pcm_sw_params_proc)pDevice->pContext->alsa.snd_pcm_sw_params)(pPCM, pSWParams); if (resultALSA < 0) { ma_free(pSWParams, &pDevice->pContext->allocationCallbacks); ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to set software parameters. snd_pcm_sw_params() failed."); return ma_result_from_errno(-resultALSA); } ma_free(pSWParams, &pDevice->pContext->allocationCallbacks); pSWParams = NULL; /* Grab the internal channel map. For now we're not going to bother trying to change the channel map and instead just do it ourselves. */ { ma_snd_pcm_chmap_t* pChmap = NULL; if (pDevice->pContext->alsa.snd_pcm_get_chmap != NULL) { pChmap = ((ma_snd_pcm_get_chmap_proc)pDevice->pContext->alsa.snd_pcm_get_chmap)(pPCM); } if (pChmap != NULL) { ma_uint32 iChannel; /* There are cases where the returned channel map can have a different channel count than was returned by snd_pcm_hw_params_set_channels_near(). */ if (pChmap->channels >= internalChannels) { /* Drop excess channels. */ for (iChannel = 0; iChannel < internalChannels; ++iChannel) { internalChannelMap[iChannel] = ma_convert_alsa_channel_position_to_ma_channel(pChmap->pos[iChannel]); } } else { ma_uint32 i; /* Excess channels use defaults. Do an initial fill with defaults, overwrite the first pChmap->channels, validate to ensure there are no duplicate channels. If validation fails, fall back to defaults. */ ma_bool32 isValid = MA_TRUE; /* Fill with defaults. */ ma_channel_map_init_standard(ma_standard_channel_map_alsa, internalChannelMap, ma_countof(internalChannelMap), internalChannels); /* Overwrite first pChmap->channels channels. */ for (iChannel = 0; iChannel < pChmap->channels; ++iChannel) { internalChannelMap[iChannel] = ma_convert_alsa_channel_position_to_ma_channel(pChmap->pos[iChannel]); } /* Validate. */ for (i = 0; i < internalChannels && isValid; ++i) { ma_uint32 j; for (j = i+1; j < internalChannels; ++j) { if (internalChannelMap[i] == internalChannelMap[j]) { isValid = MA_FALSE; break; } } } /* If our channel map is invalid, fall back to defaults. */ if (!isValid) { ma_channel_map_init_standard(ma_standard_channel_map_alsa, internalChannelMap, ma_countof(internalChannelMap), internalChannels); } } free(pChmap); pChmap = NULL; } else { /* Could not retrieve the channel map. Fall back to a hard-coded assumption. */ ma_channel_map_init_standard(ma_standard_channel_map_alsa, internalChannelMap, ma_countof(internalChannelMap), internalChannels); } } /* We need to retrieve the poll descriptors so we can use poll() to wait for data to become available for reading or writing. There's no well defined maximum for this so we're just going to allocate this on the heap. */ pollDescriptorCount = ((ma_snd_pcm_poll_descriptors_count_proc)pDevice->pContext->alsa.snd_pcm_poll_descriptors_count)(pPCM); if (pollDescriptorCount <= 0) { ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to retrieve poll descriptors count."); return MA_ERROR; } pPollDescriptors = (struct pollfd*)ma_malloc(sizeof(*pPollDescriptors) * (pollDescriptorCount + 1), &pDevice->pContext->allocationCallbacks); /* +1 because we want room for the wakeup descriptor. */ if (pPollDescriptors == NULL) { ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to allocate memory for poll descriptors."); return MA_OUT_OF_MEMORY; } /* We need an eventfd to wakeup from poll() and avoid a deadlock in situations where the driver never returns from writei() and readi(). This has been observed with the "pulse" device. */ wakeupfd = eventfd(0, 0); if (wakeupfd < 0) { ma_free(pPollDescriptors, &pDevice->pContext->allocationCallbacks); ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to create eventfd for poll wakeup."); return ma_result_from_errno(errno); } /* We'll place the wakeup fd at the start of the buffer. */ pPollDescriptors[0].fd = wakeupfd; pPollDescriptors[0].events = POLLIN; /* We only care about waiting to read from the wakeup file descriptor. */ pPollDescriptors[0].revents = 0; /* We can now extract the PCM poll descriptors which we place after the wakeup descriptor. */ pollDescriptorCount = ((ma_snd_pcm_poll_descriptors_proc)pDevice->pContext->alsa.snd_pcm_poll_descriptors)(pPCM, pPollDescriptors + 1, pollDescriptorCount); /* +1 because we want to place these descriptors after the wakeup descriptor. */ if (pollDescriptorCount <= 0) { close(wakeupfd); ma_free(pPollDescriptors, &pDevice->pContext->allocationCallbacks); ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to retrieve poll descriptors."); return MA_ERROR; } if (deviceType == ma_device_type_capture) { pDevice->alsa.pollDescriptorCountCapture = pollDescriptorCount; pDevice->alsa.pPollDescriptorsCapture = pPollDescriptors; pDevice->alsa.wakeupfdCapture = wakeupfd; } else { pDevice->alsa.pollDescriptorCountPlayback = pollDescriptorCount; pDevice->alsa.pPollDescriptorsPlayback = pPollDescriptors; pDevice->alsa.wakeupfdPlayback = wakeupfd; } /* We're done. Prepare the device. */ resultALSA = ((ma_snd_pcm_prepare_proc)pDevice->pContext->alsa.snd_pcm_prepare)(pPCM); if (resultALSA < 0) { close(wakeupfd); ma_free(pPollDescriptors, &pDevice->pContext->allocationCallbacks); ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM); ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to prepare device."); return ma_result_from_errno(-resultALSA); } if (deviceType == ma_device_type_capture) { pDevice->alsa.pPCMCapture = (ma_ptr)pPCM; pDevice->alsa.isUsingMMapCapture = isUsingMMap; } else { pDevice->alsa.pPCMPlayback = (ma_ptr)pPCM; pDevice->alsa.isUsingMMapPlayback = isUsingMMap; } pDescriptor->format = internalFormat; pDescriptor->channels = internalChannels; pDescriptor->sampleRate = internalSampleRate; ma_channel_map_copy(pDescriptor->channelMap, internalChannelMap, ma_min(internalChannels, MA_MAX_CHANNELS)); pDescriptor->periodSizeInFrames = internalPeriodSizeInFrames; pDescriptor->periodCount = internalPeriods; return MA_SUCCESS; } static ma_result ma_device_init__alsa(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture) { MA_ASSERT(pDevice != NULL); MA_ZERO_OBJECT(&pDevice->alsa); if (pConfig->deviceType == ma_device_type_loopback) { return MA_DEVICE_TYPE_NOT_SUPPORTED; } if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { ma_result result = ma_device_init_by_type__alsa(pDevice, pConfig, pDescriptorCapture, ma_device_type_capture); if (result != MA_SUCCESS) { return result; } } if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { ma_result result = ma_device_init_by_type__alsa(pDevice, pConfig, pDescriptorPlayback, ma_device_type_playback); if (result != MA_SUCCESS) { return result; } } return MA_SUCCESS; } static ma_result ma_device_start__alsa(ma_device* pDevice) { int resultALSA; if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { resultALSA = ((ma_snd_pcm_start_proc)pDevice->pContext->alsa.snd_pcm_start)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture); if (resultALSA < 0) { ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to start capture device."); return ma_result_from_errno(-resultALSA); } } if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { /* Don't need to do anything for playback because it'll be started automatically when enough data has been written. */ } return MA_SUCCESS; } static ma_result ma_device_stop__alsa(ma_device* pDevice) { if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Dropping capture device...\n"); ((ma_snd_pcm_drop_proc)pDevice->pContext->alsa.snd_pcm_drop)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture); ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Dropping capture device successful.\n"); /* We need to prepare the device again, otherwise we won't be able to restart the device. */ ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Preparing capture device...\n"); if (((ma_snd_pcm_prepare_proc)pDevice->pContext->alsa.snd_pcm_prepare)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture) < 0) { ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Preparing capture device failed.\n"); } else { ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Preparing capture device successful.\n"); } } if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Dropping playback device...\n"); ((ma_snd_pcm_drop_proc)pDevice->pContext->alsa.snd_pcm_drop)((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback); ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Dropping playback device successful.\n"); /* We need to prepare the device again, otherwise we won't be able to restart the device. */ ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Preparing playback device...\n"); if (((ma_snd_pcm_prepare_proc)pDevice->pContext->alsa.snd_pcm_prepare)((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback) < 0) { ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Preparing playback device failed.\n"); } else { ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Preparing playback device successful.\n"); } } return MA_SUCCESS; } static ma_result ma_device_wait__alsa(ma_device* pDevice, ma_snd_pcm_t* pPCM, struct pollfd* pPollDescriptors, int pollDescriptorCount, short requiredEvent) { for (;;) { unsigned short revents; int resultALSA; int resultPoll = poll(pPollDescriptors, pollDescriptorCount, -1); if (resultPoll < 0) { ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] poll() failed."); return ma_result_from_errno(errno); } /* Before checking the ALSA poll descriptor flag we need to check if the wakeup descriptor has had it's POLLIN flag set. If so, we need to actually read the data and then exit function. The wakeup descriptor will be the first item in the descriptors buffer. */ if ((pPollDescriptors[0].revents & POLLIN) != 0) { ma_uint64 t; int resultRead = read(pPollDescriptors[0].fd, &t, sizeof(t)); /* <-- Important that we read here so that the next write() does not block. */ if (resultRead < 0) { ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] read() failed."); return ma_result_from_errno(errno); } ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] POLLIN set for wakeupfd\n"); return MA_DEVICE_NOT_STARTED; } /* Getting here means that some data should be able to be read. We need to use ALSA to translate the revents flags for us. */ resultALSA = ((ma_snd_pcm_poll_descriptors_revents_proc)pDevice->pContext->alsa.snd_pcm_poll_descriptors_revents)(pPCM, pPollDescriptors + 1, pollDescriptorCount - 1, &revents); /* +1, -1 to ignore the wakeup descriptor. */ if (resultALSA < 0) { ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] snd_pcm_poll_descriptors_revents() failed."); return ma_result_from_errno(-resultALSA); } if ((revents & POLLERR) != 0) { ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] POLLERR detected."); return ma_result_from_errno(errno); } if ((revents & requiredEvent) == requiredEvent) { break; /* We're done. Data available for reading or writing. */ } } return MA_SUCCESS; } static ma_result ma_device_wait_read__alsa(ma_device* pDevice) { return ma_device_wait__alsa(pDevice, (ma_snd_pcm_t*)pDevice->alsa.pPCMCapture, (struct pollfd*)pDevice->alsa.pPollDescriptorsCapture, pDevice->alsa.pollDescriptorCountCapture + 1, POLLIN); /* +1 to account for the wakeup descriptor. */ } static ma_result ma_device_wait_write__alsa(ma_device* pDevice) { return ma_device_wait__alsa(pDevice, (ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback, (struct pollfd*)pDevice->alsa.pPollDescriptorsPlayback, pDevice->alsa.pollDescriptorCountPlayback + 1, POLLOUT); /* +1 to account for the wakeup descriptor. */ } static ma_result ma_device_read__alsa(ma_device* pDevice, void* pFramesOut, ma_uint32 frameCount, ma_uint32* pFramesRead) { ma_snd_pcm_sframes_t resultALSA = 0; MA_ASSERT(pDevice != NULL); MA_ASSERT(pFramesOut != NULL); if (pFramesRead != NULL) { *pFramesRead = 0; } while (ma_device_get_state(pDevice) == ma_device_state_started) { ma_result result; /* The first thing to do is wait for data to become available for reading. This will return an error code if the device has been stopped. */ result = ma_device_wait_read__alsa(pDevice); if (result != MA_SUCCESS) { return result; } /* Getting here means we should have data available. */ resultALSA = ((ma_snd_pcm_readi_proc)pDevice->pContext->alsa.snd_pcm_readi)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture, pFramesOut, frameCount); if (resultALSA >= 0) { break; /* Success. */ } else { if (resultALSA == -EAGAIN) { /*ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "EGAIN (read)\n");*/ continue; /* Try again. */ } else if (resultALSA == -EPIPE) { ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "EPIPE (read)\n"); /* Overrun. Recover and try again. If this fails we need to return an error. */ resultALSA = ((ma_snd_pcm_recover_proc)pDevice->pContext->alsa.snd_pcm_recover)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture, resultALSA, MA_TRUE); if (resultALSA < 0) { ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to recover device after overrun."); return ma_result_from_errno((int)-resultALSA); } resultALSA = ((ma_snd_pcm_start_proc)pDevice->pContext->alsa.snd_pcm_start)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture); if (resultALSA < 0) { ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to start device after underrun."); return ma_result_from_errno((int)-resultALSA); } continue; /* Try reading again. */ } } } if (pFramesRead != NULL) { *pFramesRead = resultALSA; } return MA_SUCCESS; } static ma_result ma_device_write__alsa(ma_device* pDevice, const void* pFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten) { ma_snd_pcm_sframes_t resultALSA = 0; MA_ASSERT(pDevice != NULL); MA_ASSERT(pFrames != NULL); if (pFramesWritten != NULL) { *pFramesWritten = 0; } while (ma_device_get_state(pDevice) == ma_device_state_started) { ma_result result; /* The first thing to do is wait for space to become available for writing. This will return an error code if the device has been stopped. */ result = ma_device_wait_write__alsa(pDevice); if (result != MA_SUCCESS) { return result; } resultALSA = ((ma_snd_pcm_writei_proc)pDevice->pContext->alsa.snd_pcm_writei)((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback, pFrames, frameCount); if (resultALSA >= 0) { break; /* Success. */ } else { if (resultALSA == -EAGAIN) { /*ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "EGAIN (write)\n");*/ continue; /* Try again. */ } else if (resultALSA == -EPIPE) { ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "EPIPE (write)\n"); /* Underrun. Recover and try again. If this fails we need to return an error. */ resultALSA = ((ma_snd_pcm_recover_proc)pDevice->pContext->alsa.snd_pcm_recover)((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback, resultALSA, MA_TRUE); /* MA_TRUE=silent (don't print anything on error). */ if (resultALSA < 0) { ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to recover device after underrun."); return ma_result_from_errno((int)-resultALSA); } /* In my testing I have had a situation where writei() does not automatically restart the device even though I've set it up as such in the software parameters. What will happen is writei() will block indefinitely even though the number of frames is well beyond the auto-start threshold. To work around this I've needed to add an explicit start here. Not sure if this is me just being stupid and not recovering the device properly, but this definitely feels like something isn't quite right here. */ resultALSA = ((ma_snd_pcm_start_proc)pDevice->pContext->alsa.snd_pcm_start)((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback); if (resultALSA < 0) { ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to start device after underrun."); return ma_result_from_errno((int)-resultALSA); } continue; /* Try writing again. */ } } } if (pFramesWritten != NULL) { *pFramesWritten = resultALSA; } return MA_SUCCESS; } static ma_result ma_device_data_loop_wakeup__alsa(ma_device* pDevice) { ma_uint64 t = 1; int resultWrite = 0; MA_ASSERT(pDevice != NULL); ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Waking up...\n"); /* Write to an eventfd to trigger a wakeup from poll() and abort any reading or writing. */ if (pDevice->alsa.pPollDescriptorsCapture != NULL) { resultWrite = write(pDevice->alsa.wakeupfdCapture, &t, sizeof(t)); } if (pDevice->alsa.pPollDescriptorsPlayback != NULL) { resultWrite = write(pDevice->alsa.wakeupfdPlayback, &t, sizeof(t)); } if (resultWrite < 0) { ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] write() failed.\n"); return ma_result_from_errno(errno); } ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Waking up completed successfully.\n"); return MA_SUCCESS; } static ma_result ma_context_uninit__alsa(ma_context* pContext) { MA_ASSERT(pContext != NULL); MA_ASSERT(pContext->backend == ma_backend_alsa); /* Clean up memory for memory leak checkers. */ ((ma_snd_config_update_free_global_proc)pContext->alsa.snd_config_update_free_global)(); #ifndef MA_NO_RUNTIME_LINKING ma_dlclose(pContext, pContext->alsa.asoundSO); #endif ma_mutex_uninit(&pContext->alsa.internalDeviceEnumLock); return MA_SUCCESS; } static ma_result ma_context_init__alsa(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks) { ma_result result; #ifndef MA_NO_RUNTIME_LINKING const char* libasoundNames[] = { "libasound.so.2", "libasound.so" }; size_t i; for (i = 0; i < ma_countof(libasoundNames); ++i) { pContext->alsa.asoundSO = ma_dlopen(pContext, libasoundNames[i]); if (pContext->alsa.asoundSO != NULL) { break; } } if (pContext->alsa.asoundSO == NULL) { ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "[ALSA] Failed to open shared object.\n"); return MA_NO_BACKEND; } pContext->alsa.snd_pcm_open = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_open"); pContext->alsa.snd_pcm_close = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_close"); pContext->alsa.snd_pcm_hw_params_sizeof = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_sizeof"); pContext->alsa.snd_pcm_hw_params_any = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_any"); pContext->alsa.snd_pcm_hw_params_set_format = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_set_format"); pContext->alsa.snd_pcm_hw_params_set_format_first = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_set_format_first"); pContext->alsa.snd_pcm_hw_params_get_format_mask = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_get_format_mask"); pContext->alsa.snd_pcm_hw_params_set_channels = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_set_channels"); pContext->alsa.snd_pcm_hw_params_set_channels_near = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_set_channels_near"); pContext->alsa.snd_pcm_hw_params_set_channels_minmax = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_set_channels_minmax"); pContext->alsa.snd_pcm_hw_params_set_rate_resample = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_set_rate_resample"); pContext->alsa.snd_pcm_hw_params_set_rate = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_set_rate"); pContext->alsa.snd_pcm_hw_params_set_rate_near = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_set_rate_near"); pContext->alsa.snd_pcm_hw_params_set_buffer_size_near = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_set_buffer_size_near"); pContext->alsa.snd_pcm_hw_params_set_periods_near = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_set_periods_near"); pContext->alsa.snd_pcm_hw_params_set_access = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_set_access"); pContext->alsa.snd_pcm_hw_params_get_format = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_get_format"); pContext->alsa.snd_pcm_hw_params_get_channels = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_get_channels"); pContext->alsa.snd_pcm_hw_params_get_channels_min = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_get_channels_min"); pContext->alsa.snd_pcm_hw_params_get_channels_max = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_get_channels_max"); pContext->alsa.snd_pcm_hw_params_get_rate = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_get_rate"); pContext->alsa.snd_pcm_hw_params_get_rate_min = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_get_rate_min"); pContext->alsa.snd_pcm_hw_params_get_rate_max = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_get_rate_max"); pContext->alsa.snd_pcm_hw_params_get_buffer_size = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_get_buffer_size"); pContext->alsa.snd_pcm_hw_params_get_periods = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_get_periods"); pContext->alsa.snd_pcm_hw_params_get_access = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_get_access"); pContext->alsa.snd_pcm_hw_params_test_format = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_test_format"); pContext->alsa.snd_pcm_hw_params_test_channels = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_test_channels"); pContext->alsa.snd_pcm_hw_params_test_rate = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_test_rate"); pContext->alsa.snd_pcm_hw_params = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params"); pContext->alsa.snd_pcm_sw_params_sizeof = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_sw_params_sizeof"); pContext->alsa.snd_pcm_sw_params_current = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_sw_params_current"); pContext->alsa.snd_pcm_sw_params_get_boundary = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_sw_params_get_boundary"); pContext->alsa.snd_pcm_sw_params_set_avail_min = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_sw_params_set_avail_min"); pContext->alsa.snd_pcm_sw_params_set_start_threshold = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_sw_params_set_start_threshold"); pContext->alsa.snd_pcm_sw_params_set_stop_threshold = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_sw_params_set_stop_threshold"); pContext->alsa.snd_pcm_sw_params = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_sw_params"); pContext->alsa.snd_pcm_format_mask_sizeof = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_format_mask_sizeof"); pContext->alsa.snd_pcm_format_mask_test = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_format_mask_test"); pContext->alsa.snd_pcm_get_chmap = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_get_chmap"); pContext->alsa.snd_pcm_state = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_state"); pContext->alsa.snd_pcm_prepare = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_prepare"); pContext->alsa.snd_pcm_start = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_start"); pContext->alsa.snd_pcm_drop = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_drop"); pContext->alsa.snd_pcm_drain = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_drain"); pContext->alsa.snd_pcm_reset = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_reset"); pContext->alsa.snd_device_name_hint = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_device_name_hint"); pContext->alsa.snd_device_name_get_hint = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_device_name_get_hint"); pContext->alsa.snd_card_get_index = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_card_get_index"); pContext->alsa.snd_device_name_free_hint = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_device_name_free_hint"); pContext->alsa.snd_pcm_mmap_begin = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_mmap_begin"); pContext->alsa.snd_pcm_mmap_commit = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_mmap_commit"); pContext->alsa.snd_pcm_recover = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_recover"); pContext->alsa.snd_pcm_readi = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_readi"); pContext->alsa.snd_pcm_writei = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_writei"); pContext->alsa.snd_pcm_avail = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_avail"); pContext->alsa.snd_pcm_avail_update = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_avail_update"); pContext->alsa.snd_pcm_wait = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_wait"); pContext->alsa.snd_pcm_nonblock = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_nonblock"); pContext->alsa.snd_pcm_info = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_info"); pContext->alsa.snd_pcm_info_sizeof = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_info_sizeof"); pContext->alsa.snd_pcm_info_get_name = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_info_get_name"); pContext->alsa.snd_pcm_poll_descriptors = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_poll_descriptors"); pContext->alsa.snd_pcm_poll_descriptors_count = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_poll_descriptors_count"); pContext->alsa.snd_pcm_poll_descriptors_revents = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_poll_descriptors_revents"); pContext->alsa.snd_config_update_free_global = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_config_update_free_global"); #else /* The system below is just for type safety. */ ma_snd_pcm_open_proc _snd_pcm_open = snd_pcm_open; ma_snd_pcm_close_proc _snd_pcm_close = snd_pcm_close; ma_snd_pcm_hw_params_sizeof_proc _snd_pcm_hw_params_sizeof = snd_pcm_hw_params_sizeof; ma_snd_pcm_hw_params_any_proc _snd_pcm_hw_params_any = snd_pcm_hw_params_any; ma_snd_pcm_hw_params_set_format_proc _snd_pcm_hw_params_set_format = snd_pcm_hw_params_set_format; ma_snd_pcm_hw_params_set_format_first_proc _snd_pcm_hw_params_set_format_first = snd_pcm_hw_params_set_format_first; ma_snd_pcm_hw_params_get_format_mask_proc _snd_pcm_hw_params_get_format_mask = snd_pcm_hw_params_get_format_mask; ma_snd_pcm_hw_params_set_channels_proc _snd_pcm_hw_params_set_channels = snd_pcm_hw_params_set_channels; ma_snd_pcm_hw_params_set_channels_near_proc _snd_pcm_hw_params_set_channels_near = snd_pcm_hw_params_set_channels_near; ma_snd_pcm_hw_params_set_rate_resample_proc _snd_pcm_hw_params_set_rate_resample = snd_pcm_hw_params_set_rate_resample; ma_snd_pcm_hw_params_set_rate_near _snd_pcm_hw_params_set_rate = snd_pcm_hw_params_set_rate; ma_snd_pcm_hw_params_set_rate_near_proc _snd_pcm_hw_params_set_rate_near = snd_pcm_hw_params_set_rate_near; ma_snd_pcm_hw_params_set_rate_minmax_proc _snd_pcm_hw_params_set_rate_minmax = snd_pcm_hw_params_set_rate_minmax; ma_snd_pcm_hw_params_set_buffer_size_near_proc _snd_pcm_hw_params_set_buffer_size_near = snd_pcm_hw_params_set_buffer_size_near; ma_snd_pcm_hw_params_set_periods_near_proc _snd_pcm_hw_params_set_periods_near = snd_pcm_hw_params_set_periods_near; ma_snd_pcm_hw_params_set_access_proc _snd_pcm_hw_params_set_access = snd_pcm_hw_params_set_access; ma_snd_pcm_hw_params_get_format_proc _snd_pcm_hw_params_get_format = snd_pcm_hw_params_get_format; ma_snd_pcm_hw_params_get_channels_proc _snd_pcm_hw_params_get_channels = snd_pcm_hw_params_get_channels; ma_snd_pcm_hw_params_get_channels_min_proc _snd_pcm_hw_params_get_channels_min = snd_pcm_hw_params_get_channels_min; ma_snd_pcm_hw_params_get_channels_max_proc _snd_pcm_hw_params_get_channels_max = snd_pcm_hw_params_get_channels_max; ma_snd_pcm_hw_params_get_rate_proc _snd_pcm_hw_params_get_rate = snd_pcm_hw_params_get_rate; ma_snd_pcm_hw_params_get_rate_min_proc _snd_pcm_hw_params_get_rate_min = snd_pcm_hw_params_get_rate_min; ma_snd_pcm_hw_params_get_rate_max_proc _snd_pcm_hw_params_get_rate_max = snd_pcm_hw_params_get_rate_max; ma_snd_pcm_hw_params_get_buffer_size_proc _snd_pcm_hw_params_get_buffer_size = snd_pcm_hw_params_get_buffer_size; ma_snd_pcm_hw_params_get_periods_proc _snd_pcm_hw_params_get_periods = snd_pcm_hw_params_get_periods; ma_snd_pcm_hw_params_get_access_proc _snd_pcm_hw_params_get_access = snd_pcm_hw_params_get_access; ma_snd_pcm_hw_params_test_format_proc _snd_pcm_hw_params_test_format = snd_pcm_hw_params_test_format; ma_snd_pcm_hw_params_test_channels_proc _snd_pcm_hw_params_test_channels = snd_pcm_hw_params_test_channels; ma_snd_pcm_hw_params_test_rate_proc _snd_pcm_hw_params_test_rate = snd_pcm_hw_params_test_rate; ma_snd_pcm_hw_params_proc _snd_pcm_hw_params = snd_pcm_hw_params; ma_snd_pcm_sw_params_sizeof_proc _snd_pcm_sw_params_sizeof = snd_pcm_sw_params_sizeof; ma_snd_pcm_sw_params_current_proc _snd_pcm_sw_params_current = snd_pcm_sw_params_current; ma_snd_pcm_sw_params_get_boundary_proc _snd_pcm_sw_params_get_boundary = snd_pcm_sw_params_get_boundary; ma_snd_pcm_sw_params_set_avail_min_proc _snd_pcm_sw_params_set_avail_min = snd_pcm_sw_params_set_avail_min; ma_snd_pcm_sw_params_set_start_threshold_proc _snd_pcm_sw_params_set_start_threshold = snd_pcm_sw_params_set_start_threshold; ma_snd_pcm_sw_params_set_stop_threshold_proc _snd_pcm_sw_params_set_stop_threshold = snd_pcm_sw_params_set_stop_threshold; ma_snd_pcm_sw_params_proc _snd_pcm_sw_params = snd_pcm_sw_params; ma_snd_pcm_format_mask_sizeof_proc _snd_pcm_format_mask_sizeof = snd_pcm_format_mask_sizeof; ma_snd_pcm_format_mask_test_proc _snd_pcm_format_mask_test = snd_pcm_format_mask_test; ma_snd_pcm_get_chmap_proc _snd_pcm_get_chmap = snd_pcm_get_chmap; ma_snd_pcm_state_proc _snd_pcm_state = snd_pcm_state; ma_snd_pcm_prepare_proc _snd_pcm_prepare = snd_pcm_prepare; ma_snd_pcm_start_proc _snd_pcm_start = snd_pcm_start; ma_snd_pcm_drop_proc _snd_pcm_drop = snd_pcm_drop; ma_snd_pcm_drain_proc _snd_pcm_drain = snd_pcm_drain; ma_snd_pcm_reset_proc _snd_pcm_reset = snd_pcm_reset; ma_snd_device_name_hint_proc _snd_device_name_hint = snd_device_name_hint; ma_snd_device_name_get_hint_proc _snd_device_name_get_hint = snd_device_name_get_hint; ma_snd_card_get_index_proc _snd_card_get_index = snd_card_get_index; ma_snd_device_name_free_hint_proc _snd_device_name_free_hint = snd_device_name_free_hint; ma_snd_pcm_mmap_begin_proc _snd_pcm_mmap_begin = snd_pcm_mmap_begin; ma_snd_pcm_mmap_commit_proc _snd_pcm_mmap_commit = snd_pcm_mmap_commit; ma_snd_pcm_recover_proc _snd_pcm_recover = snd_pcm_recover; ma_snd_pcm_readi_proc _snd_pcm_readi = snd_pcm_readi; ma_snd_pcm_writei_proc _snd_pcm_writei = snd_pcm_writei; ma_snd_pcm_avail_proc _snd_pcm_avail = snd_pcm_avail; ma_snd_pcm_avail_update_proc _snd_pcm_avail_update = snd_pcm_avail_update; ma_snd_pcm_wait_proc _snd_pcm_wait = snd_pcm_wait; ma_snd_pcm_nonblock_proc _snd_pcm_nonblock = snd_pcm_nonblock; ma_snd_pcm_info_proc _snd_pcm_info = snd_pcm_info; ma_snd_pcm_info_sizeof_proc _snd_pcm_info_sizeof = snd_pcm_info_sizeof; ma_snd_pcm_info_get_name_proc _snd_pcm_info_get_name = snd_pcm_info_get_name; ma_snd_pcm_poll_descriptors _snd_pcm_poll_descriptors = snd_pcm_poll_descriptors; ma_snd_pcm_poll_descriptors_count _snd_pcm_poll_descriptors_count = snd_pcm_poll_descriptors_count; ma_snd_pcm_poll_descriptors_revents _snd_pcm_poll_descriptors_revents = snd_pcm_poll_descriptors_revents; ma_snd_config_update_free_global_proc _snd_config_update_free_global = snd_config_update_free_global; pContext->alsa.snd_pcm_open = (ma_proc)_snd_pcm_open; pContext->alsa.snd_pcm_close = (ma_proc)_snd_pcm_close; pContext->alsa.snd_pcm_hw_params_sizeof = (ma_proc)_snd_pcm_hw_params_sizeof; pContext->alsa.snd_pcm_hw_params_any = (ma_proc)_snd_pcm_hw_params_any; pContext->alsa.snd_pcm_hw_params_set_format = (ma_proc)_snd_pcm_hw_params_set_format; pContext->alsa.snd_pcm_hw_params_set_format_first = (ma_proc)_snd_pcm_hw_params_set_format_first; pContext->alsa.snd_pcm_hw_params_get_format_mask = (ma_proc)_snd_pcm_hw_params_get_format_mask; pContext->alsa.snd_pcm_hw_params_set_channels = (ma_proc)_snd_pcm_hw_params_set_channels; pContext->alsa.snd_pcm_hw_params_set_channels_near = (ma_proc)_snd_pcm_hw_params_set_channels_near; pContext->alsa.snd_pcm_hw_params_set_channels_minmax = (ma_proc)_snd_pcm_hw_params_set_channels_minmax; pContext->alsa.snd_pcm_hw_params_set_rate_resample = (ma_proc)_snd_pcm_hw_params_set_rate_resample; pContext->alsa.snd_pcm_hw_params_set_rate = (ma_proc)_snd_pcm_hw_params_set_rate; pContext->alsa.snd_pcm_hw_params_set_rate_near = (ma_proc)_snd_pcm_hw_params_set_rate_near; pContext->alsa.snd_pcm_hw_params_set_buffer_size_near = (ma_proc)_snd_pcm_hw_params_set_buffer_size_near; pContext->alsa.snd_pcm_hw_params_set_periods_near = (ma_proc)_snd_pcm_hw_params_set_periods_near; pContext->alsa.snd_pcm_hw_params_set_access = (ma_proc)_snd_pcm_hw_params_set_access; pContext->alsa.snd_pcm_hw_params_get_format = (ma_proc)_snd_pcm_hw_params_get_format; pContext->alsa.snd_pcm_hw_params_get_channels = (ma_proc)_snd_pcm_hw_params_get_channels; pContext->alsa.snd_pcm_hw_params_get_channels_min = (ma_proc)_snd_pcm_hw_params_get_channels_min; pContext->alsa.snd_pcm_hw_params_get_channels_max = (ma_proc)_snd_pcm_hw_params_get_channels_max; pContext->alsa.snd_pcm_hw_params_get_rate = (ma_proc)_snd_pcm_hw_params_get_rate; pContext->alsa.snd_pcm_hw_params_get_rate_min = (ma_proc)_snd_pcm_hw_params_get_rate_min; pContext->alsa.snd_pcm_hw_params_get_rate_max = (ma_proc)_snd_pcm_hw_params_get_rate_max; pContext->alsa.snd_pcm_hw_params_get_buffer_size = (ma_proc)_snd_pcm_hw_params_get_buffer_size; pContext->alsa.snd_pcm_hw_params_get_periods = (ma_proc)_snd_pcm_hw_params_get_periods; pContext->alsa.snd_pcm_hw_params_get_access = (ma_proc)_snd_pcm_hw_params_get_access; pContext->alsa.snd_pcm_hw_params_test_format = (ma_proc)_snd_pcm_hw_params_test_format; pContext->alsa.snd_pcm_hw_params_test_channels = (ma_proc)_snd_pcm_hw_params_test_channels; pContext->alsa.snd_pcm_hw_params_test_rate = (ma_proc)_snd_pcm_hw_params_test_rate; pContext->alsa.snd_pcm_hw_params = (ma_proc)_snd_pcm_hw_params; pContext->alsa.snd_pcm_sw_params_sizeof = (ma_proc)_snd_pcm_sw_params_sizeof; pContext->alsa.snd_pcm_sw_params_current = (ma_proc)_snd_pcm_sw_params_current; pContext->alsa.snd_pcm_sw_params_get_boundary = (ma_proc)_snd_pcm_sw_params_get_boundary; pContext->alsa.snd_pcm_sw_params_set_avail_min = (ma_proc)_snd_pcm_sw_params_set_avail_min; pContext->alsa.snd_pcm_sw_params_set_start_threshold = (ma_proc)_snd_pcm_sw_params_set_start_threshold; pContext->alsa.snd_pcm_sw_params_set_stop_threshold = (ma_proc)_snd_pcm_sw_params_set_stop_threshold; pContext->alsa.snd_pcm_sw_params = (ma_proc)_snd_pcm_sw_params; pContext->alsa.snd_pcm_format_mask_sizeof = (ma_proc)_snd_pcm_format_mask_sizeof; pContext->alsa.snd_pcm_format_mask_test = (ma_proc)_snd_pcm_format_mask_test; pContext->alsa.snd_pcm_get_chmap = (ma_proc)_snd_pcm_get_chmap; pContext->alsa.snd_pcm_state = (ma_proc)_snd_pcm_state; pContext->alsa.snd_pcm_prepare = (ma_proc)_snd_pcm_prepare; pContext->alsa.snd_pcm_start = (ma_proc)_snd_pcm_start; pContext->alsa.snd_pcm_drop = (ma_proc)_snd_pcm_drop; pContext->alsa.snd_pcm_drain = (ma_proc)_snd_pcm_drain; pContext->alsa.snd_pcm_reset = (ma_proc)_snd_pcm_reset; pContext->alsa.snd_device_name_hint = (ma_proc)_snd_device_name_hint; pContext->alsa.snd_device_name_get_hint = (ma_proc)_snd_device_name_get_hint; pContext->alsa.snd_card_get_index = (ma_proc)_snd_card_get_index; pContext->alsa.snd_device_name_free_hint = (ma_proc)_snd_device_name_free_hint; pContext->alsa.snd_pcm_mmap_begin = (ma_proc)_snd_pcm_mmap_begin; pContext->alsa.snd_pcm_mmap_commit = (ma_proc)_snd_pcm_mmap_commit; pContext->alsa.snd_pcm_recover = (ma_proc)_snd_pcm_recover; pContext->alsa.snd_pcm_readi = (ma_proc)_snd_pcm_readi; pContext->alsa.snd_pcm_writei = (ma_proc)_snd_pcm_writei; pContext->alsa.snd_pcm_avail = (ma_proc)_snd_pcm_avail; pContext->alsa.snd_pcm_avail_update = (ma_proc)_snd_pcm_avail_update; pContext->alsa.snd_pcm_wait = (ma_proc)_snd_pcm_wait; pContext->alsa.snd_pcm_nonblock = (ma_proc)_snd_pcm_nonblock; pContext->alsa.snd_pcm_info = (ma_proc)_snd_pcm_info; pContext->alsa.snd_pcm_info_sizeof = (ma_proc)_snd_pcm_info_sizeof; pContext->alsa.snd_pcm_info_get_name = (ma_proc)_snd_pcm_info_get_name; pContext->alsa.snd_pcm_poll_descriptors = (ma_proc)_snd_pcm_poll_descriptors; pContext->alsa.snd_pcm_poll_descriptors_count = (ma_proc)_snd_pcm_poll_descriptors_count; pContext->alsa.snd_pcm_poll_descriptors_revents = (ma_proc)_snd_pcm_poll_descriptors_revents; pContext->alsa.snd_config_update_free_global = (ma_proc)_snd_config_update_free_global; #endif pContext->alsa.useVerboseDeviceEnumeration = pConfig->alsa.useVerboseDeviceEnumeration; result = ma_mutex_init(&pContext->alsa.internalDeviceEnumLock); if (result != MA_SUCCESS) { ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[ALSA] WARNING: Failed to initialize mutex for internal device enumeration."); return result; } pCallbacks->onContextInit = ma_context_init__alsa; pCallbacks->onContextUninit = ma_context_uninit__alsa; pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__alsa; pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__alsa; pCallbacks->onDeviceInit = ma_device_init__alsa; pCallbacks->onDeviceUninit = ma_device_uninit__alsa; pCallbacks->onDeviceStart = ma_device_start__alsa; pCallbacks->onDeviceStop = ma_device_stop__alsa; pCallbacks->onDeviceRead = ma_device_read__alsa; pCallbacks->onDeviceWrite = ma_device_write__alsa; pCallbacks->onDeviceDataLoop = NULL; pCallbacks->onDeviceDataLoopWakeup = ma_device_data_loop_wakeup__alsa; return MA_SUCCESS; } #endif /* ALSA */ /****************************************************************************** PulseAudio Backend ******************************************************************************/ #ifdef MA_HAS_PULSEAUDIO /* The PulseAudio API, along with Apple's Core Audio, is the worst of the maintream audio APIs. This is a brief description of what's going on in the PulseAudio backend. I apologize if this gets a bit ranty for your liking - you might want to skip this discussion. PulseAudio has something they call the "Simple API", which unfortunately isn't suitable for miniaudio. I've not seen anywhere where it allows you to enumerate over devices, nor does it seem to support the ability to stop and start streams. Looking at the documentation, it appears as though the stream is constantly running and you prevent sound from being emitted or captured by simply not calling the read or write functions. This is not a professional solution as it would be much better to *actually* stop the underlying stream. Perhaps the simple API has some smarts to do this automatically, but I'm not sure. Another limitation with the simple API is that it seems inefficient when you want to have multiple streams to a single context. For these reasons, miniaudio is not using the simple API. Since we're not using the simple API, we're left with the asynchronous API as our only other option. And boy, is this where it starts to get fun, and I don't mean that in a good way... The problems start with the very name of the API - "asynchronous". Yes, this is an asynchronous oriented API which means your commands don't immediately take effect. You instead need to issue your commands, and then wait for them to complete. The waiting mechanism is enabled through the use of a "main loop". In the asychronous API you cannot get away from the main loop, and the main loop is where almost all of PulseAudio's problems stem from. When you first initialize PulseAudio you need an object referred to as "main loop". You can implement this yourself by defining your own vtable, but it's much easier to just use one of the built-in main loop implementations. There's two generic implementations called pa_mainloop and pa_threaded_mainloop, and another implementation specific to GLib called pa_glib_mainloop. We're using pa_threaded_mainloop because it simplifies management of the worker thread. The idea of the main loop object is pretty self explanatory - you're supposed to use it to implement a worker thread which runs in a loop. The main loop is where operations are actually executed. To initialize the main loop, you just use `pa_threaded_mainloop_new()`. This is the first function you'll call. You can then get a pointer to the vtable with `pa_threaded_mainloop_get_api()` (the main loop vtable is called `pa_mainloop_api`). Again, you can bypass the threaded main loop object entirely and just implement `pa_mainloop_api` directly, but there's no need for it unless you're doing something extremely specialized such as if you want to integrate it into your application's existing main loop infrastructure. (EDIT 2021-01-26: miniaudio is no longer using `pa_threaded_mainloop` due to this issue: https://github.com/mackron/miniaudio/issues/262. It is now using `pa_mainloop` which turns out to be a simpler solution anyway. The rest of this rant still applies, however.) Once you have your main loop vtable (the `pa_mainloop_api` object) you can create the PulseAudio context. This is very similar to miniaudio's context and they map to each other quite well. You have one context to many streams, which is basically the same as miniaudio's one `ma_context` to many `ma_device`s. Here's where it starts to get annoying, however. When you first create the PulseAudio context, which is done with `pa_context_new()`, it's not actually connected to anything. When you connect, you call `pa_context_connect()`. However, if you remember, PulseAudio is an asynchronous API. That means you cannot just assume the context is connected after `pa_context_context()` has returned. You instead need to wait for it to connect. To do this, you need to either wait for a callback to get fired, which you can set with `pa_context_set_state_callback()`, or you can continuously poll the context's state. Either way, you need to run this in a loop. All objects from here out are created from the context, and, I believe, you can't be creating these objects until the context is connected. This waiting loop is therefore unavoidable. In order for the waiting to ever complete, however, the main loop needs to be running. Before attempting to connect the context, the main loop needs to be started with `pa_threaded_mainloop_start()`. The reason for this asynchronous design is to support cases where you're connecting to a remote server, say through a local network or an internet connection. However, the *VAST* majority of cases don't involve this at all - they just connect to a local "server" running on the host machine. The fact that this would be the default rather than making `pa_context_connect()` synchronous tends to boggle the mind. Once the context has been created and connected you can start creating a stream. A PulseAudio stream is analogous to miniaudio's device. The initialization of a stream is fairly standard - you configure some attributes (analogous to miniaudio's device config) and then call `pa_stream_new()` to actually create it. Here is where we start to get into "operations". When configuring the stream, you can get information about the source (such as sample format, sample rate, etc.), however it's not synchronous. Instead, a `pa_operation` object is returned from `pa_context_get_source_info_by_name()` (capture) or `pa_context_get_sink_info_by_name()` (playback). Then, you need to run a loop (again!) to wait for the operation to complete which you can determine via a callback or polling, just like we did with the context. Then, as an added bonus, you need to decrement the reference counter of the `pa_operation` object to ensure memory is cleaned up. All of that just to retrieve basic information about a device! Once the basic information about the device has been retrieved, miniaudio can now create the stream with `ma_stream_new()`. Like the context, this needs to be connected. But we need to be careful here, because we're now about to introduce one of the most horrific design choices in PulseAudio. PulseAudio allows you to specify a callback that is fired when data can be written to or read from a stream. The language is important here because PulseAudio takes it literally, specifically the "can be". You would think these callbacks would be appropriate as the place for writing and reading data to and from the stream, and that would be right, except when it's not. When you initialize the stream, you can set a flag that tells PulseAudio to not start the stream automatically. This is required because miniaudio does not auto-start devices straight after initialization - you need to call `ma_device_start()` manually. The problem is that even when this flag is specified, PulseAudio will immediately fire it's write or read callback. This is *technically* correct (based on the wording in the documentation) because indeed, data *can* be written at this point. The problem is that it's not *practical*. It makes sense that the write/read callback would be where a program will want to write or read data to or from the stream, but when it's called before the application has even requested that the stream be started, it's just not practical because the program probably isn't ready for any kind of data delivery at that point (it may still need to load files or whatnot). Instead, this callback should only be fired when the application requests the stream be started which is how it works with literally *every* other callback-based audio API. Since miniaudio forbids firing of the data callback until the device has been started (as it should be with *all* callback based APIs), logic needs to be added to ensure miniaudio doesn't just blindly fire the application-defined data callback from within the PulseAudio callback before the stream has actually been started. The device state is used for this - if the state is anything other than `ma_device_state_starting` or `ma_device_state_started`, the main data callback is not fired. This, unfortunately, is not the end of the problems with the PulseAudio write callback. Any normal callback based audio API will continuously fire the callback at regular intervals based on the size of the internal buffer. This will only ever be fired when the device is running, and will be fired regardless of whether or not the user actually wrote anything to the device/stream. This not the case in PulseAudio. In PulseAudio, the data callback will *only* be called if you wrote something to it previously. That means, if you don't call `pa_stream_write()`, the callback will not get fired. On the surface you wouldn't think this would matter because you should be always writing data, and if you don't have anything to write, just write silence. That's fine until you want to drain the stream. You see, if you're continuously writing data to the stream, the stream will never get drained! That means in order to drain the stream, you need to *not* write data to it! But remember, when you don't write data to the stream, the callback won't get fired again! Why is draining important? Because that's how we've defined stopping to work in miniaudio. In miniaudio, stopping the device requires it to be drained before returning from ma_device_stop(). So we've stopped the device, which requires us to drain, but draining requires us to *not* write data to the stream (or else it won't ever complete draining), but not writing to the stream means the callback won't get fired again! This becomes a problem when stopping and then restarting the device. When the device is stopped, it's drained, which requires us to *not* write anything to the stream. But then, since we didn't write anything to it, the write callback will *never* get called again if we just resume the stream naively. This means that starting the stream requires us to write data to the stream from outside the callback. This disconnect is something PulseAudio has got seriously wrong - there should only ever be a single source of data delivery, that being the callback. (I have tried using `pa_stream_flush()` to trigger the write callback to fire, but this just doesn't work for some reason.) Once you've created the stream, you need to connect it which involves the whole waiting procedure. This is the same process as the context, only this time you'll poll for the state with `pa_stream_get_status()`. The starting and stopping of a streaming is referred to as "corking" in PulseAudio. The analogy is corking a barrel. To start the stream, you uncork it, to stop it you cork it. Personally I think it's silly - why would you not just call it "starting" and "stopping" like any other normal audio API? Anyway, the act of corking is, you guessed it, asynchronous. This means you'll need our waiting loop as usual. Again, why this asynchronous design is the default is absolutely beyond me. Would it really be that hard to just make it run synchronously? Teardown is pretty simple (what?!). It's just a matter of calling the relevant `_unref()` function on each object in reverse order that they were initialized in. That's about it from the PulseAudio side. A bit ranty, I know, but they really need to fix that main loop and callback system. They're embarrassingly unpractical. The main loop thing is an easy fix - have synchronous versions of all APIs. If an application wants these to run asynchronously, they can execute them in a separate thread themselves. The desire to run these asynchronously is such a niche requirement - it makes no sense to make it the default. The stream write callback needs to be change, or an alternative provided, that is constantly fired, regardless of whether or not `pa_stream_write()` has been called, and it needs to take a pointer to a buffer as a parameter which the program just writes to directly rather than having to call `pa_stream_writable_size()` and `pa_stream_write()`. These changes alone will change PulseAudio from one of the worst audio APIs to one of the best. */ /* It is assumed pulseaudio.h is available when linking at compile time. When linking at compile time, we use the declarations in the header to check for type safety. We cannot do this when linking at run time because the header might not be available. */ #ifdef MA_NO_RUNTIME_LINKING /* pulseaudio.h marks some functions with "inline" which isn't always supported. Need to emulate it. */ #if !defined(__cplusplus) #if defined(__STRICT_ANSI__) #if !defined(inline) #define inline __inline__ __attribute__((always_inline)) #define MA_INLINE_DEFINED #endif #endif #endif #include #if defined(MA_INLINE_DEFINED) #undef inline #undef MA_INLINE_DEFINED #endif #define MA_PA_OK PA_OK #define MA_PA_ERR_ACCESS PA_ERR_ACCESS #define MA_PA_ERR_INVALID PA_ERR_INVALID #define MA_PA_ERR_NOENTITY PA_ERR_NOENTITY #define MA_PA_ERR_NOTSUPPORTED PA_ERR_NOTSUPPORTED #define MA_PA_CHANNELS_MAX PA_CHANNELS_MAX #define MA_PA_RATE_MAX PA_RATE_MAX typedef pa_context_flags_t ma_pa_context_flags_t; #define MA_PA_CONTEXT_NOFLAGS PA_CONTEXT_NOFLAGS #define MA_PA_CONTEXT_NOAUTOSPAWN PA_CONTEXT_NOAUTOSPAWN #define MA_PA_CONTEXT_NOFAIL PA_CONTEXT_NOFAIL typedef pa_stream_flags_t ma_pa_stream_flags_t; #define MA_PA_STREAM_NOFLAGS PA_STREAM_NOFLAGS #define MA_PA_STREAM_START_CORKED PA_STREAM_START_CORKED #define MA_PA_STREAM_INTERPOLATE_TIMING PA_STREAM_INTERPOLATE_TIMING #define MA_PA_STREAM_NOT_MONOTONIC PA_STREAM_NOT_MONOTONIC #define MA_PA_STREAM_AUTO_TIMING_UPDATE PA_STREAM_AUTO_TIMING_UPDATE #define MA_PA_STREAM_NO_REMAP_CHANNELS PA_STREAM_NO_REMAP_CHANNELS #define MA_PA_STREAM_NO_REMIX_CHANNELS PA_STREAM_NO_REMIX_CHANNELS #define MA_PA_STREAM_FIX_FORMAT PA_STREAM_FIX_FORMAT #define MA_PA_STREAM_FIX_RATE PA_STREAM_FIX_RATE #define MA_PA_STREAM_FIX_CHANNELS PA_STREAM_FIX_CHANNELS #define MA_PA_STREAM_DONT_MOVE PA_STREAM_DONT_MOVE #define MA_PA_STREAM_VARIABLE_RATE PA_STREAM_VARIABLE_RATE #define MA_PA_STREAM_PEAK_DETECT PA_STREAM_PEAK_DETECT #define MA_PA_STREAM_START_MUTED PA_STREAM_START_MUTED #define MA_PA_STREAM_ADJUST_LATENCY PA_STREAM_ADJUST_LATENCY #define MA_PA_STREAM_EARLY_REQUESTS PA_STREAM_EARLY_REQUESTS #define MA_PA_STREAM_DONT_INHIBIT_AUTO_SUSPEND PA_STREAM_DONT_INHIBIT_AUTO_SUSPEND #define MA_PA_STREAM_START_UNMUTED PA_STREAM_START_UNMUTED #define MA_PA_STREAM_FAIL_ON_SUSPEND PA_STREAM_FAIL_ON_SUSPEND #define MA_PA_STREAM_RELATIVE_VOLUME PA_STREAM_RELATIVE_VOLUME #define MA_PA_STREAM_PASSTHROUGH PA_STREAM_PASSTHROUGH typedef pa_sink_flags_t ma_pa_sink_flags_t; #define MA_PA_SINK_NOFLAGS PA_SINK_NOFLAGS #define MA_PA_SINK_HW_VOLUME_CTRL PA_SINK_HW_VOLUME_CTRL #define MA_PA_SINK_LATENCY PA_SINK_LATENCY #define MA_PA_SINK_HARDWARE PA_SINK_HARDWARE #define MA_PA_SINK_NETWORK PA_SINK_NETWORK #define MA_PA_SINK_HW_MUTE_CTRL PA_SINK_HW_MUTE_CTRL #define MA_PA_SINK_DECIBEL_VOLUME PA_SINK_DECIBEL_VOLUME #define MA_PA_SINK_FLAT_VOLUME PA_SINK_FLAT_VOLUME #define MA_PA_SINK_DYNAMIC_LATENCY PA_SINK_DYNAMIC_LATENCY #define MA_PA_SINK_SET_FORMATS PA_SINK_SET_FORMATS typedef pa_source_flags_t ma_pa_source_flags_t; #define MA_PA_SOURCE_NOFLAGS PA_SOURCE_NOFLAGS #define MA_PA_SOURCE_HW_VOLUME_CTRL PA_SOURCE_HW_VOLUME_CTRL #define MA_PA_SOURCE_LATENCY PA_SOURCE_LATENCY #define MA_PA_SOURCE_HARDWARE PA_SOURCE_HARDWARE #define MA_PA_SOURCE_NETWORK PA_SOURCE_NETWORK #define MA_PA_SOURCE_HW_MUTE_CTRL PA_SOURCE_HW_MUTE_CTRL #define MA_PA_SOURCE_DECIBEL_VOLUME PA_SOURCE_DECIBEL_VOLUME #define MA_PA_SOURCE_DYNAMIC_LATENCY PA_SOURCE_DYNAMIC_LATENCY #define MA_PA_SOURCE_FLAT_VOLUME PA_SOURCE_FLAT_VOLUME typedef pa_context_state_t ma_pa_context_state_t; #define MA_PA_CONTEXT_UNCONNECTED PA_CONTEXT_UNCONNECTED #define MA_PA_CONTEXT_CONNECTING PA_CONTEXT_CONNECTING #define MA_PA_CONTEXT_AUTHORIZING PA_CONTEXT_AUTHORIZING #define MA_PA_CONTEXT_SETTING_NAME PA_CONTEXT_SETTING_NAME #define MA_PA_CONTEXT_READY PA_CONTEXT_READY #define MA_PA_CONTEXT_FAILED PA_CONTEXT_FAILED #define MA_PA_CONTEXT_TERMINATED PA_CONTEXT_TERMINATED typedef pa_stream_state_t ma_pa_stream_state_t; #define MA_PA_STREAM_UNCONNECTED PA_STREAM_UNCONNECTED #define MA_PA_STREAM_CREATING PA_STREAM_CREATING #define MA_PA_STREAM_READY PA_STREAM_READY #define MA_PA_STREAM_FAILED PA_STREAM_FAILED #define MA_PA_STREAM_TERMINATED PA_STREAM_TERMINATED typedef pa_operation_state_t ma_pa_operation_state_t; #define MA_PA_OPERATION_RUNNING PA_OPERATION_RUNNING #define MA_PA_OPERATION_DONE PA_OPERATION_DONE #define MA_PA_OPERATION_CANCELLED PA_OPERATION_CANCELLED typedef pa_sink_state_t ma_pa_sink_state_t; #define MA_PA_SINK_INVALID_STATE PA_SINK_INVALID_STATE #define MA_PA_SINK_RUNNING PA_SINK_RUNNING #define MA_PA_SINK_IDLE PA_SINK_IDLE #define MA_PA_SINK_SUSPENDED PA_SINK_SUSPENDED typedef pa_source_state_t ma_pa_source_state_t; #define MA_PA_SOURCE_INVALID_STATE PA_SOURCE_INVALID_STATE #define MA_PA_SOURCE_RUNNING PA_SOURCE_RUNNING #define MA_PA_SOURCE_IDLE PA_SOURCE_IDLE #define MA_PA_SOURCE_SUSPENDED PA_SOURCE_SUSPENDED typedef pa_seek_mode_t ma_pa_seek_mode_t; #define MA_PA_SEEK_RELATIVE PA_SEEK_RELATIVE #define MA_PA_SEEK_ABSOLUTE PA_SEEK_ABSOLUTE #define MA_PA_SEEK_RELATIVE_ON_READ PA_SEEK_RELATIVE_ON_READ #define MA_PA_SEEK_RELATIVE_END PA_SEEK_RELATIVE_END typedef pa_channel_position_t ma_pa_channel_position_t; #define MA_PA_CHANNEL_POSITION_INVALID PA_CHANNEL_POSITION_INVALID #define MA_PA_CHANNEL_POSITION_MONO PA_CHANNEL_POSITION_MONO #define MA_PA_CHANNEL_POSITION_FRONT_LEFT PA_CHANNEL_POSITION_FRONT_LEFT #define MA_PA_CHANNEL_POSITION_FRONT_RIGHT PA_CHANNEL_POSITION_FRONT_RIGHT #define MA_PA_CHANNEL_POSITION_FRONT_CENTER PA_CHANNEL_POSITION_FRONT_CENTER #define MA_PA_CHANNEL_POSITION_REAR_CENTER PA_CHANNEL_POSITION_REAR_CENTER #define MA_PA_CHANNEL_POSITION_REAR_LEFT PA_CHANNEL_POSITION_REAR_LEFT #define MA_PA_CHANNEL_POSITION_REAR_RIGHT PA_CHANNEL_POSITION_REAR_RIGHT #define MA_PA_CHANNEL_POSITION_LFE PA_CHANNEL_POSITION_LFE #define MA_PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER #define MA_PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER #define MA_PA_CHANNEL_POSITION_SIDE_LEFT PA_CHANNEL_POSITION_SIDE_LEFT #define MA_PA_CHANNEL_POSITION_SIDE_RIGHT PA_CHANNEL_POSITION_SIDE_RIGHT #define MA_PA_CHANNEL_POSITION_AUX0 PA_CHANNEL_POSITION_AUX0 #define MA_PA_CHANNEL_POSITION_AUX1 PA_CHANNEL_POSITION_AUX1 #define MA_PA_CHANNEL_POSITION_AUX2 PA_CHANNEL_POSITION_AUX2 #define MA_PA_CHANNEL_POSITION_AUX3 PA_CHANNEL_POSITION_AUX3 #define MA_PA_CHANNEL_POSITION_AUX4 PA_CHANNEL_POSITION_AUX4 #define MA_PA_CHANNEL_POSITION_AUX5 PA_CHANNEL_POSITION_AUX5 #define MA_PA_CHANNEL_POSITION_AUX6 PA_CHANNEL_POSITION_AUX6 #define MA_PA_CHANNEL_POSITION_AUX7 PA_CHANNEL_POSITION_AUX7 #define MA_PA_CHANNEL_POSITION_AUX8 PA_CHANNEL_POSITION_AUX8 #define MA_PA_CHANNEL_POSITION_AUX9 PA_CHANNEL_POSITION_AUX9 #define MA_PA_CHANNEL_POSITION_AUX10 PA_CHANNEL_POSITION_AUX10 #define MA_PA_CHANNEL_POSITION_AUX11 PA_CHANNEL_POSITION_AUX11 #define MA_PA_CHANNEL_POSITION_AUX12 PA_CHANNEL_POSITION_AUX12 #define MA_PA_CHANNEL_POSITION_AUX13 PA_CHANNEL_POSITION_AUX13 #define MA_PA_CHANNEL_POSITION_AUX14 PA_CHANNEL_POSITION_AUX14 #define MA_PA_CHANNEL_POSITION_AUX15 PA_CHANNEL_POSITION_AUX15 #define MA_PA_CHANNEL_POSITION_AUX16 PA_CHANNEL_POSITION_AUX16 #define MA_PA_CHANNEL_POSITION_AUX17 PA_CHANNEL_POSITION_AUX17 #define MA_PA_CHANNEL_POSITION_AUX18 PA_CHANNEL_POSITION_AUX18 #define MA_PA_CHANNEL_POSITION_AUX19 PA_CHANNEL_POSITION_AUX19 #define MA_PA_CHANNEL_POSITION_AUX20 PA_CHANNEL_POSITION_AUX20 #define MA_PA_CHANNEL_POSITION_AUX21 PA_CHANNEL_POSITION_AUX21 #define MA_PA_CHANNEL_POSITION_AUX22 PA_CHANNEL_POSITION_AUX22 #define MA_PA_CHANNEL_POSITION_AUX23 PA_CHANNEL_POSITION_AUX23 #define MA_PA_CHANNEL_POSITION_AUX24 PA_CHANNEL_POSITION_AUX24 #define MA_PA_CHANNEL_POSITION_AUX25 PA_CHANNEL_POSITION_AUX25 #define MA_PA_CHANNEL_POSITION_AUX26 PA_CHANNEL_POSITION_AUX26 #define MA_PA_CHANNEL_POSITION_AUX27 PA_CHANNEL_POSITION_AUX27 #define MA_PA_CHANNEL_POSITION_AUX28 PA_CHANNEL_POSITION_AUX28 #define MA_PA_CHANNEL_POSITION_AUX29 PA_CHANNEL_POSITION_AUX29 #define MA_PA_CHANNEL_POSITION_AUX30 PA_CHANNEL_POSITION_AUX30 #define MA_PA_CHANNEL_POSITION_AUX31 PA_CHANNEL_POSITION_AUX31 #define MA_PA_CHANNEL_POSITION_TOP_CENTER PA_CHANNEL_POSITION_TOP_CENTER #define MA_PA_CHANNEL_POSITION_TOP_FRONT_LEFT PA_CHANNEL_POSITION_TOP_FRONT_LEFT #define MA_PA_CHANNEL_POSITION_TOP_FRONT_RIGHT PA_CHANNEL_POSITION_TOP_FRONT_RIGHT #define MA_PA_CHANNEL_POSITION_TOP_FRONT_CENTER PA_CHANNEL_POSITION_TOP_FRONT_CENTER #define MA_PA_CHANNEL_POSITION_TOP_REAR_LEFT PA_CHANNEL_POSITION_TOP_REAR_LEFT #define MA_PA_CHANNEL_POSITION_TOP_REAR_RIGHT PA_CHANNEL_POSITION_TOP_REAR_RIGHT #define MA_PA_CHANNEL_POSITION_TOP_REAR_CENTER PA_CHANNEL_POSITION_TOP_REAR_CENTER #define MA_PA_CHANNEL_POSITION_LEFT PA_CHANNEL_POSITION_LEFT #define MA_PA_CHANNEL_POSITION_RIGHT PA_CHANNEL_POSITION_RIGHT #define MA_PA_CHANNEL_POSITION_CENTER PA_CHANNEL_POSITION_CENTER #define MA_PA_CHANNEL_POSITION_SUBWOOFER PA_CHANNEL_POSITION_SUBWOOFER typedef pa_channel_map_def_t ma_pa_channel_map_def_t; #define MA_PA_CHANNEL_MAP_AIFF PA_CHANNEL_MAP_AIFF #define MA_PA_CHANNEL_MAP_ALSA PA_CHANNEL_MAP_ALSA #define MA_PA_CHANNEL_MAP_AUX PA_CHANNEL_MAP_AUX #define MA_PA_CHANNEL_MAP_WAVEEX PA_CHANNEL_MAP_WAVEEX #define MA_PA_CHANNEL_MAP_OSS PA_CHANNEL_MAP_OSS #define MA_PA_CHANNEL_MAP_DEFAULT PA_CHANNEL_MAP_DEFAULT typedef pa_sample_format_t ma_pa_sample_format_t; #define MA_PA_SAMPLE_INVALID PA_SAMPLE_INVALID #define MA_PA_SAMPLE_U8 PA_SAMPLE_U8 #define MA_PA_SAMPLE_ALAW PA_SAMPLE_ALAW #define MA_PA_SAMPLE_ULAW PA_SAMPLE_ULAW #define MA_PA_SAMPLE_S16LE PA_SAMPLE_S16LE #define MA_PA_SAMPLE_S16BE PA_SAMPLE_S16BE #define MA_PA_SAMPLE_FLOAT32LE PA_SAMPLE_FLOAT32LE #define MA_PA_SAMPLE_FLOAT32BE PA_SAMPLE_FLOAT32BE #define MA_PA_SAMPLE_S32LE PA_SAMPLE_S32LE #define MA_PA_SAMPLE_S32BE PA_SAMPLE_S32BE #define MA_PA_SAMPLE_S24LE PA_SAMPLE_S24LE #define MA_PA_SAMPLE_S24BE PA_SAMPLE_S24BE #define MA_PA_SAMPLE_S24_32LE PA_SAMPLE_S24_32LE #define MA_PA_SAMPLE_S24_32BE PA_SAMPLE_S24_32BE typedef pa_mainloop ma_pa_mainloop; typedef pa_threaded_mainloop ma_pa_threaded_mainloop; typedef pa_mainloop_api ma_pa_mainloop_api; typedef pa_context ma_pa_context; typedef pa_operation ma_pa_operation; typedef pa_stream ma_pa_stream; typedef pa_spawn_api ma_pa_spawn_api; typedef pa_buffer_attr ma_pa_buffer_attr; typedef pa_channel_map ma_pa_channel_map; typedef pa_cvolume ma_pa_cvolume; typedef pa_sample_spec ma_pa_sample_spec; typedef pa_sink_info ma_pa_sink_info; typedef pa_source_info ma_pa_source_info; typedef pa_context_notify_cb_t ma_pa_context_notify_cb_t; typedef pa_sink_info_cb_t ma_pa_sink_info_cb_t; typedef pa_source_info_cb_t ma_pa_source_info_cb_t; typedef pa_stream_success_cb_t ma_pa_stream_success_cb_t; typedef pa_stream_request_cb_t ma_pa_stream_request_cb_t; typedef pa_stream_notify_cb_t ma_pa_stream_notify_cb_t; typedef pa_free_cb_t ma_pa_free_cb_t; #else #define MA_PA_OK 0 #define MA_PA_ERR_ACCESS 1 #define MA_PA_ERR_INVALID 2 #define MA_PA_ERR_NOENTITY 5 #define MA_PA_ERR_NOTSUPPORTED 19 #define MA_PA_CHANNELS_MAX 32 #define MA_PA_RATE_MAX 384000 typedef int ma_pa_context_flags_t; #define MA_PA_CONTEXT_NOFLAGS 0x00000000 #define MA_PA_CONTEXT_NOAUTOSPAWN 0x00000001 #define MA_PA_CONTEXT_NOFAIL 0x00000002 typedef int ma_pa_stream_flags_t; #define MA_PA_STREAM_NOFLAGS 0x00000000 #define MA_PA_STREAM_START_CORKED 0x00000001 #define MA_PA_STREAM_INTERPOLATE_TIMING 0x00000002 #define MA_PA_STREAM_NOT_MONOTONIC 0x00000004 #define MA_PA_STREAM_AUTO_TIMING_UPDATE 0x00000008 #define MA_PA_STREAM_NO_REMAP_CHANNELS 0x00000010 #define MA_PA_STREAM_NO_REMIX_CHANNELS 0x00000020 #define MA_PA_STREAM_FIX_FORMAT 0x00000040 #define MA_PA_STREAM_FIX_RATE 0x00000080 #define MA_PA_STREAM_FIX_CHANNELS 0x00000100 #define MA_PA_STREAM_DONT_MOVE 0x00000200 #define MA_PA_STREAM_VARIABLE_RATE 0x00000400 #define MA_PA_STREAM_PEAK_DETECT 0x00000800 #define MA_PA_STREAM_START_MUTED 0x00001000 #define MA_PA_STREAM_ADJUST_LATENCY 0x00002000 #define MA_PA_STREAM_EARLY_REQUESTS 0x00004000 #define MA_PA_STREAM_DONT_INHIBIT_AUTO_SUSPEND 0x00008000 #define MA_PA_STREAM_START_UNMUTED 0x00010000 #define MA_PA_STREAM_FAIL_ON_SUSPEND 0x00020000 #define MA_PA_STREAM_RELATIVE_VOLUME 0x00040000 #define MA_PA_STREAM_PASSTHROUGH 0x00080000 typedef int ma_pa_sink_flags_t; #define MA_PA_SINK_NOFLAGS 0x00000000 #define MA_PA_SINK_HW_VOLUME_CTRL 0x00000001 #define MA_PA_SINK_LATENCY 0x00000002 #define MA_PA_SINK_HARDWARE 0x00000004 #define MA_PA_SINK_NETWORK 0x00000008 #define MA_PA_SINK_HW_MUTE_CTRL 0x00000010 #define MA_PA_SINK_DECIBEL_VOLUME 0x00000020 #define MA_PA_SINK_FLAT_VOLUME 0x00000040 #define MA_PA_SINK_DYNAMIC_LATENCY 0x00000080 #define MA_PA_SINK_SET_FORMATS 0x00000100 typedef int ma_pa_source_flags_t; #define MA_PA_SOURCE_NOFLAGS 0x00000000 #define MA_PA_SOURCE_HW_VOLUME_CTRL 0x00000001 #define MA_PA_SOURCE_LATENCY 0x00000002 #define MA_PA_SOURCE_HARDWARE 0x00000004 #define MA_PA_SOURCE_NETWORK 0x00000008 #define MA_PA_SOURCE_HW_MUTE_CTRL 0x00000010 #define MA_PA_SOURCE_DECIBEL_VOLUME 0x00000020 #define MA_PA_SOURCE_DYNAMIC_LATENCY 0x00000040 #define MA_PA_SOURCE_FLAT_VOLUME 0x00000080 typedef int ma_pa_context_state_t; #define MA_PA_CONTEXT_UNCONNECTED 0 #define MA_PA_CONTEXT_CONNECTING 1 #define MA_PA_CONTEXT_AUTHORIZING 2 #define MA_PA_CONTEXT_SETTING_NAME 3 #define MA_PA_CONTEXT_READY 4 #define MA_PA_CONTEXT_FAILED 5 #define MA_PA_CONTEXT_TERMINATED 6 typedef int ma_pa_stream_state_t; #define MA_PA_STREAM_UNCONNECTED 0 #define MA_PA_STREAM_CREATING 1 #define MA_PA_STREAM_READY 2 #define MA_PA_STREAM_FAILED 3 #define MA_PA_STREAM_TERMINATED 4 typedef int ma_pa_operation_state_t; #define MA_PA_OPERATION_RUNNING 0 #define MA_PA_OPERATION_DONE 1 #define MA_PA_OPERATION_CANCELLED 2 typedef int ma_pa_sink_state_t; #define MA_PA_SINK_INVALID_STATE -1 #define MA_PA_SINK_RUNNING 0 #define MA_PA_SINK_IDLE 1 #define MA_PA_SINK_SUSPENDED 2 typedef int ma_pa_source_state_t; #define MA_PA_SOURCE_INVALID_STATE -1 #define MA_PA_SOURCE_RUNNING 0 #define MA_PA_SOURCE_IDLE 1 #define MA_PA_SOURCE_SUSPENDED 2 typedef int ma_pa_seek_mode_t; #define MA_PA_SEEK_RELATIVE 0 #define MA_PA_SEEK_ABSOLUTE 1 #define MA_PA_SEEK_RELATIVE_ON_READ 2 #define MA_PA_SEEK_RELATIVE_END 3 typedef int ma_pa_channel_position_t; #define MA_PA_CHANNEL_POSITION_INVALID -1 #define MA_PA_CHANNEL_POSITION_MONO 0 #define MA_PA_CHANNEL_POSITION_FRONT_LEFT 1 #define MA_PA_CHANNEL_POSITION_FRONT_RIGHT 2 #define MA_PA_CHANNEL_POSITION_FRONT_CENTER 3 #define MA_PA_CHANNEL_POSITION_REAR_CENTER 4 #define MA_PA_CHANNEL_POSITION_REAR_LEFT 5 #define MA_PA_CHANNEL_POSITION_REAR_RIGHT 6 #define MA_PA_CHANNEL_POSITION_LFE 7 #define MA_PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER 8 #define MA_PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER 9 #define MA_PA_CHANNEL_POSITION_SIDE_LEFT 10 #define MA_PA_CHANNEL_POSITION_SIDE_RIGHT 11 #define MA_PA_CHANNEL_POSITION_AUX0 12 #define MA_PA_CHANNEL_POSITION_AUX1 13 #define MA_PA_CHANNEL_POSITION_AUX2 14 #define MA_PA_CHANNEL_POSITION_AUX3 15 #define MA_PA_CHANNEL_POSITION_AUX4 16 #define MA_PA_CHANNEL_POSITION_AUX5 17 #define MA_PA_CHANNEL_POSITION_AUX6 18 #define MA_PA_CHANNEL_POSITION_AUX7 19 #define MA_PA_CHANNEL_POSITION_AUX8 20 #define MA_PA_CHANNEL_POSITION_AUX9 21 #define MA_PA_CHANNEL_POSITION_AUX10 22 #define MA_PA_CHANNEL_POSITION_AUX11 23 #define MA_PA_CHANNEL_POSITION_AUX12 24 #define MA_PA_CHANNEL_POSITION_AUX13 25 #define MA_PA_CHANNEL_POSITION_AUX14 26 #define MA_PA_CHANNEL_POSITION_AUX15 27 #define MA_PA_CHANNEL_POSITION_AUX16 28 #define MA_PA_CHANNEL_POSITION_AUX17 29 #define MA_PA_CHANNEL_POSITION_AUX18 30 #define MA_PA_CHANNEL_POSITION_AUX19 31 #define MA_PA_CHANNEL_POSITION_AUX20 32 #define MA_PA_CHANNEL_POSITION_AUX21 33 #define MA_PA_CHANNEL_POSITION_AUX22 34 #define MA_PA_CHANNEL_POSITION_AUX23 35 #define MA_PA_CHANNEL_POSITION_AUX24 36 #define MA_PA_CHANNEL_POSITION_AUX25 37 #define MA_PA_CHANNEL_POSITION_AUX26 38 #define MA_PA_CHANNEL_POSITION_AUX27 39 #define MA_PA_CHANNEL_POSITION_AUX28 40 #define MA_PA_CHANNEL_POSITION_AUX29 41 #define MA_PA_CHANNEL_POSITION_AUX30 42 #define MA_PA_CHANNEL_POSITION_AUX31 43 #define MA_PA_CHANNEL_POSITION_TOP_CENTER 44 #define MA_PA_CHANNEL_POSITION_TOP_FRONT_LEFT 45 #define MA_PA_CHANNEL_POSITION_TOP_FRONT_RIGHT 46 #define MA_PA_CHANNEL_POSITION_TOP_FRONT_CENTER 47 #define MA_PA_CHANNEL_POSITION_TOP_REAR_LEFT 48 #define MA_PA_CHANNEL_POSITION_TOP_REAR_RIGHT 49 #define MA_PA_CHANNEL_POSITION_TOP_REAR_CENTER 50 #define MA_PA_CHANNEL_POSITION_LEFT MA_PA_CHANNEL_POSITION_FRONT_LEFT #define MA_PA_CHANNEL_POSITION_RIGHT MA_PA_CHANNEL_POSITION_FRONT_RIGHT #define MA_PA_CHANNEL_POSITION_CENTER MA_PA_CHANNEL_POSITION_FRONT_CENTER #define MA_PA_CHANNEL_POSITION_SUBWOOFER MA_PA_CHANNEL_POSITION_LFE typedef int ma_pa_channel_map_def_t; #define MA_PA_CHANNEL_MAP_AIFF 0 #define MA_PA_CHANNEL_MAP_ALSA 1 #define MA_PA_CHANNEL_MAP_AUX 2 #define MA_PA_CHANNEL_MAP_WAVEEX 3 #define MA_PA_CHANNEL_MAP_OSS 4 #define MA_PA_CHANNEL_MAP_DEFAULT MA_PA_CHANNEL_MAP_AIFF typedef int ma_pa_sample_format_t; #define MA_PA_SAMPLE_INVALID -1 #define MA_PA_SAMPLE_U8 0 #define MA_PA_SAMPLE_ALAW 1 #define MA_PA_SAMPLE_ULAW 2 #define MA_PA_SAMPLE_S16LE 3 #define MA_PA_SAMPLE_S16BE 4 #define MA_PA_SAMPLE_FLOAT32LE 5 #define MA_PA_SAMPLE_FLOAT32BE 6 #define MA_PA_SAMPLE_S32LE 7 #define MA_PA_SAMPLE_S32BE 8 #define MA_PA_SAMPLE_S24LE 9 #define MA_PA_SAMPLE_S24BE 10 #define MA_PA_SAMPLE_S24_32LE 11 #define MA_PA_SAMPLE_S24_32BE 12 typedef struct ma_pa_mainloop ma_pa_mainloop; typedef struct ma_pa_threaded_mainloop ma_pa_threaded_mainloop; typedef struct ma_pa_mainloop_api ma_pa_mainloop_api; typedef struct ma_pa_context ma_pa_context; typedef struct ma_pa_operation ma_pa_operation; typedef struct ma_pa_stream ma_pa_stream; typedef struct ma_pa_spawn_api ma_pa_spawn_api; typedef struct { ma_uint32 maxlength; ma_uint32 tlength; ma_uint32 prebuf; ma_uint32 minreq; ma_uint32 fragsize; } ma_pa_buffer_attr; typedef struct { ma_uint8 channels; ma_pa_channel_position_t map[MA_PA_CHANNELS_MAX]; } ma_pa_channel_map; typedef struct { ma_uint8 channels; ma_uint32 values[MA_PA_CHANNELS_MAX]; } ma_pa_cvolume; typedef struct { ma_pa_sample_format_t format; ma_uint32 rate; ma_uint8 channels; } ma_pa_sample_spec; typedef struct { const char* name; ma_uint32 index; const char* description; ma_pa_sample_spec sample_spec; ma_pa_channel_map channel_map; ma_uint32 owner_module; ma_pa_cvolume volume; int mute; ma_uint32 monitor_source; const char* monitor_source_name; ma_uint64 latency; const char* driver; ma_pa_sink_flags_t flags; void* proplist; ma_uint64 configured_latency; ma_uint32 base_volume; ma_pa_sink_state_t state; ma_uint32 n_volume_steps; ma_uint32 card; ma_uint32 n_ports; void** ports; void* active_port; ma_uint8 n_formats; void** formats; } ma_pa_sink_info; typedef struct { const char *name; ma_uint32 index; const char *description; ma_pa_sample_spec sample_spec; ma_pa_channel_map channel_map; ma_uint32 owner_module; ma_pa_cvolume volume; int mute; ma_uint32 monitor_of_sink; const char *monitor_of_sink_name; ma_uint64 latency; const char *driver; ma_pa_source_flags_t flags; void* proplist; ma_uint64 configured_latency; ma_uint32 base_volume; ma_pa_source_state_t state; ma_uint32 n_volume_steps; ma_uint32 card; ma_uint32 n_ports; void** ports; void* active_port; ma_uint8 n_formats; void** formats; } ma_pa_source_info; typedef void (* ma_pa_context_notify_cb_t)(ma_pa_context* c, void* userdata); typedef void (* ma_pa_sink_info_cb_t) (ma_pa_context* c, const ma_pa_sink_info* i, int eol, void* userdata); typedef void (* ma_pa_source_info_cb_t) (ma_pa_context* c, const ma_pa_source_info* i, int eol, void* userdata); typedef void (* ma_pa_stream_success_cb_t)(ma_pa_stream* s, int success, void* userdata); typedef void (* ma_pa_stream_request_cb_t)(ma_pa_stream* s, size_t nbytes, void* userdata); typedef void (* ma_pa_stream_notify_cb_t) (ma_pa_stream* s, void* userdata); typedef void (* ma_pa_free_cb_t) (void* p); #endif typedef ma_pa_mainloop* (* ma_pa_mainloop_new_proc) (void); typedef void (* ma_pa_mainloop_free_proc) (ma_pa_mainloop* m); typedef void (* ma_pa_mainloop_quit_proc) (ma_pa_mainloop* m, int retval); typedef ma_pa_mainloop_api* (* ma_pa_mainloop_get_api_proc) (ma_pa_mainloop* m); typedef int (* ma_pa_mainloop_iterate_proc) (ma_pa_mainloop* m, int block, int* retval); typedef void (* ma_pa_mainloop_wakeup_proc) (ma_pa_mainloop* m); typedef ma_pa_threaded_mainloop* (* ma_pa_threaded_mainloop_new_proc) (void); typedef void (* ma_pa_threaded_mainloop_free_proc) (ma_pa_threaded_mainloop* m); typedef int (* ma_pa_threaded_mainloop_start_proc) (ma_pa_threaded_mainloop* m); typedef void (* ma_pa_threaded_mainloop_stop_proc) (ma_pa_threaded_mainloop* m); typedef void (* ma_pa_threaded_mainloop_lock_proc) (ma_pa_threaded_mainloop* m); typedef void (* ma_pa_threaded_mainloop_unlock_proc) (ma_pa_threaded_mainloop* m); typedef void (* ma_pa_threaded_mainloop_wait_proc) (ma_pa_threaded_mainloop* m); typedef void (* ma_pa_threaded_mainloop_signal_proc) (ma_pa_threaded_mainloop* m, int wait_for_accept); typedef void (* ma_pa_threaded_mainloop_accept_proc) (ma_pa_threaded_mainloop* m); typedef int (* ma_pa_threaded_mainloop_get_retval_proc) (ma_pa_threaded_mainloop* m); typedef ma_pa_mainloop_api* (* ma_pa_threaded_mainloop_get_api_proc) (ma_pa_threaded_mainloop* m); typedef int (* ma_pa_threaded_mainloop_in_thread_proc) (ma_pa_threaded_mainloop* m); typedef void (* ma_pa_threaded_mainloop_set_name_proc) (ma_pa_threaded_mainloop* m, const char* name); typedef ma_pa_context* (* ma_pa_context_new_proc) (ma_pa_mainloop_api* mainloop, const char* name); typedef void (* ma_pa_context_unref_proc) (ma_pa_context* c); typedef int (* ma_pa_context_connect_proc) (ma_pa_context* c, const char* server, ma_pa_context_flags_t flags, const ma_pa_spawn_api* api); typedef void (* ma_pa_context_disconnect_proc) (ma_pa_context* c); typedef void (* ma_pa_context_set_state_callback_proc) (ma_pa_context* c, ma_pa_context_notify_cb_t cb, void* userdata); typedef ma_pa_context_state_t (* ma_pa_context_get_state_proc) (ma_pa_context* c); typedef ma_pa_operation* (* ma_pa_context_get_sink_info_list_proc) (ma_pa_context* c, ma_pa_sink_info_cb_t cb, void* userdata); typedef ma_pa_operation* (* ma_pa_context_get_source_info_list_proc) (ma_pa_context* c, ma_pa_source_info_cb_t cb, void* userdata); typedef ma_pa_operation* (* ma_pa_context_get_sink_info_by_name_proc) (ma_pa_context* c, const char* name, ma_pa_sink_info_cb_t cb, void* userdata); typedef ma_pa_operation* (* ma_pa_context_get_source_info_by_name_proc)(ma_pa_context* c, const char* name, ma_pa_source_info_cb_t cb, void* userdata); typedef void (* ma_pa_operation_unref_proc) (ma_pa_operation* o); typedef ma_pa_operation_state_t (* ma_pa_operation_get_state_proc) (ma_pa_operation* o); typedef ma_pa_channel_map* (* ma_pa_channel_map_init_extend_proc) (ma_pa_channel_map* m, unsigned channels, ma_pa_channel_map_def_t def); typedef int (* ma_pa_channel_map_valid_proc) (const ma_pa_channel_map* m); typedef int (* ma_pa_channel_map_compatible_proc) (const ma_pa_channel_map* m, const ma_pa_sample_spec* ss); typedef ma_pa_stream* (* ma_pa_stream_new_proc) (ma_pa_context* c, const char* name, const ma_pa_sample_spec* ss, const ma_pa_channel_map* map); typedef void (* ma_pa_stream_unref_proc) (ma_pa_stream* s); typedef int (* ma_pa_stream_connect_playback_proc) (ma_pa_stream* s, const char* dev, const ma_pa_buffer_attr* attr, ma_pa_stream_flags_t flags, const ma_pa_cvolume* volume, ma_pa_stream* sync_stream); typedef int (* ma_pa_stream_connect_record_proc) (ma_pa_stream* s, const char* dev, const ma_pa_buffer_attr* attr, ma_pa_stream_flags_t flags); typedef int (* ma_pa_stream_disconnect_proc) (ma_pa_stream* s); typedef ma_pa_stream_state_t (* ma_pa_stream_get_state_proc) (ma_pa_stream* s); typedef const ma_pa_sample_spec* (* ma_pa_stream_get_sample_spec_proc) (ma_pa_stream* s); typedef const ma_pa_channel_map* (* ma_pa_stream_get_channel_map_proc) (ma_pa_stream* s); typedef const ma_pa_buffer_attr* (* ma_pa_stream_get_buffer_attr_proc) (ma_pa_stream* s); typedef ma_pa_operation* (* ma_pa_stream_set_buffer_attr_proc) (ma_pa_stream* s, const ma_pa_buffer_attr* attr, ma_pa_stream_success_cb_t cb, void* userdata); typedef const char* (* ma_pa_stream_get_device_name_proc) (ma_pa_stream* s); typedef void (* ma_pa_stream_set_write_callback_proc) (ma_pa_stream* s, ma_pa_stream_request_cb_t cb, void* userdata); typedef void (* ma_pa_stream_set_read_callback_proc) (ma_pa_stream* s, ma_pa_stream_request_cb_t cb, void* userdata); typedef void (* ma_pa_stream_set_suspended_callback_proc) (ma_pa_stream* s, ma_pa_stream_notify_cb_t cb, void* userdata); typedef void (* ma_pa_stream_set_moved_callback_proc) (ma_pa_stream* s, ma_pa_stream_notify_cb_t cb, void* userdata); typedef int (* ma_pa_stream_is_suspended_proc) (const ma_pa_stream* s); typedef ma_pa_operation* (* ma_pa_stream_flush_proc) (ma_pa_stream* s, ma_pa_stream_success_cb_t cb, void* userdata); typedef ma_pa_operation* (* ma_pa_stream_drain_proc) (ma_pa_stream* s, ma_pa_stream_success_cb_t cb, void* userdata); typedef int (* ma_pa_stream_is_corked_proc) (ma_pa_stream* s); typedef ma_pa_operation* (* ma_pa_stream_cork_proc) (ma_pa_stream* s, int b, ma_pa_stream_success_cb_t cb, void* userdata); typedef ma_pa_operation* (* ma_pa_stream_trigger_proc) (ma_pa_stream* s, ma_pa_stream_success_cb_t cb, void* userdata); typedef int (* ma_pa_stream_begin_write_proc) (ma_pa_stream* s, void** data, size_t* nbytes); typedef int (* ma_pa_stream_write_proc) (ma_pa_stream* s, const void* data, size_t nbytes, ma_pa_free_cb_t free_cb, int64_t offset, ma_pa_seek_mode_t seek); typedef int (* ma_pa_stream_peek_proc) (ma_pa_stream* s, const void** data, size_t* nbytes); typedef int (* ma_pa_stream_drop_proc) (ma_pa_stream* s); typedef size_t (* ma_pa_stream_writable_size_proc) (ma_pa_stream* s); typedef size_t (* ma_pa_stream_readable_size_proc) (ma_pa_stream* s); typedef struct { ma_uint32 count; ma_uint32 capacity; ma_device_info* pInfo; } ma_pulse_device_enum_data; static ma_result ma_result_from_pulse(int result) { if (result < 0) { return MA_ERROR; } switch (result) { case MA_PA_OK: return MA_SUCCESS; case MA_PA_ERR_ACCESS: return MA_ACCESS_DENIED; case MA_PA_ERR_INVALID: return MA_INVALID_ARGS; case MA_PA_ERR_NOENTITY: return MA_NO_DEVICE; default: return MA_ERROR; } } #if 0 static ma_pa_sample_format_t ma_format_to_pulse(ma_format format) { if (ma_is_little_endian()) { switch (format) { case ma_format_s16: return MA_PA_SAMPLE_S16LE; case ma_format_s24: return MA_PA_SAMPLE_S24LE; case ma_format_s32: return MA_PA_SAMPLE_S32LE; case ma_format_f32: return MA_PA_SAMPLE_FLOAT32LE; default: break; } } else { switch (format) { case ma_format_s16: return MA_PA_SAMPLE_S16BE; case ma_format_s24: return MA_PA_SAMPLE_S24BE; case ma_format_s32: return MA_PA_SAMPLE_S32BE; case ma_format_f32: return MA_PA_SAMPLE_FLOAT32BE; default: break; } } /* Endian agnostic. */ switch (format) { case ma_format_u8: return MA_PA_SAMPLE_U8; default: return MA_PA_SAMPLE_INVALID; } } #endif static ma_format ma_format_from_pulse(ma_pa_sample_format_t format) { if (ma_is_little_endian()) { switch (format) { case MA_PA_SAMPLE_S16LE: return ma_format_s16; case MA_PA_SAMPLE_S24LE: return ma_format_s24; case MA_PA_SAMPLE_S32LE: return ma_format_s32; case MA_PA_SAMPLE_FLOAT32LE: return ma_format_f32; default: break; } } else { switch (format) { case MA_PA_SAMPLE_S16BE: return ma_format_s16; case MA_PA_SAMPLE_S24BE: return ma_format_s24; case MA_PA_SAMPLE_S32BE: return ma_format_s32; case MA_PA_SAMPLE_FLOAT32BE: return ma_format_f32; default: break; } } /* Endian agnostic. */ switch (format) { case MA_PA_SAMPLE_U8: return ma_format_u8; default: return ma_format_unknown; } } static ma_channel ma_channel_position_from_pulse(ma_pa_channel_position_t position) { switch (position) { case MA_PA_CHANNEL_POSITION_INVALID: return MA_CHANNEL_NONE; case MA_PA_CHANNEL_POSITION_MONO: return MA_CHANNEL_MONO; case MA_PA_CHANNEL_POSITION_FRONT_LEFT: return MA_CHANNEL_FRONT_LEFT; case MA_PA_CHANNEL_POSITION_FRONT_RIGHT: return MA_CHANNEL_FRONT_RIGHT; case MA_PA_CHANNEL_POSITION_FRONT_CENTER: return MA_CHANNEL_FRONT_CENTER; case MA_PA_CHANNEL_POSITION_REAR_CENTER: return MA_CHANNEL_BACK_CENTER; case MA_PA_CHANNEL_POSITION_REAR_LEFT: return MA_CHANNEL_BACK_LEFT; case MA_PA_CHANNEL_POSITION_REAR_RIGHT: return MA_CHANNEL_BACK_RIGHT; case MA_PA_CHANNEL_POSITION_LFE: return MA_CHANNEL_LFE; case MA_PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER: return MA_CHANNEL_FRONT_LEFT_CENTER; case MA_PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER: return MA_CHANNEL_FRONT_RIGHT_CENTER; case MA_PA_CHANNEL_POSITION_SIDE_LEFT: return MA_CHANNEL_SIDE_LEFT; case MA_PA_CHANNEL_POSITION_SIDE_RIGHT: return MA_CHANNEL_SIDE_RIGHT; case MA_PA_CHANNEL_POSITION_AUX0: return MA_CHANNEL_AUX_0; case MA_PA_CHANNEL_POSITION_AUX1: return MA_CHANNEL_AUX_1; case MA_PA_CHANNEL_POSITION_AUX2: return MA_CHANNEL_AUX_2; case MA_PA_CHANNEL_POSITION_AUX3: return MA_CHANNEL_AUX_3; case MA_PA_CHANNEL_POSITION_AUX4: return MA_CHANNEL_AUX_4; case MA_PA_CHANNEL_POSITION_AUX5: return MA_CHANNEL_AUX_5; case MA_PA_CHANNEL_POSITION_AUX6: return MA_CHANNEL_AUX_6; case MA_PA_CHANNEL_POSITION_AUX7: return MA_CHANNEL_AUX_7; case MA_PA_CHANNEL_POSITION_AUX8: return MA_CHANNEL_AUX_8; case MA_PA_CHANNEL_POSITION_AUX9: return MA_CHANNEL_AUX_9; case MA_PA_CHANNEL_POSITION_AUX10: return MA_CHANNEL_AUX_10; case MA_PA_CHANNEL_POSITION_AUX11: return MA_CHANNEL_AUX_11; case MA_PA_CHANNEL_POSITION_AUX12: return MA_CHANNEL_AUX_12; case MA_PA_CHANNEL_POSITION_AUX13: return MA_CHANNEL_AUX_13; case MA_PA_CHANNEL_POSITION_AUX14: return MA_CHANNEL_AUX_14; case MA_PA_CHANNEL_POSITION_AUX15: return MA_CHANNEL_AUX_15; case MA_PA_CHANNEL_POSITION_AUX16: return MA_CHANNEL_AUX_16; case MA_PA_CHANNEL_POSITION_AUX17: return MA_CHANNEL_AUX_17; case MA_PA_CHANNEL_POSITION_AUX18: return MA_CHANNEL_AUX_18; case MA_PA_CHANNEL_POSITION_AUX19: return MA_CHANNEL_AUX_19; case MA_PA_CHANNEL_POSITION_AUX20: return MA_CHANNEL_AUX_20; case MA_PA_CHANNEL_POSITION_AUX21: return MA_CHANNEL_AUX_21; case MA_PA_CHANNEL_POSITION_AUX22: return MA_CHANNEL_AUX_22; case MA_PA_CHANNEL_POSITION_AUX23: return MA_CHANNEL_AUX_23; case MA_PA_CHANNEL_POSITION_AUX24: return MA_CHANNEL_AUX_24; case MA_PA_CHANNEL_POSITION_AUX25: return MA_CHANNEL_AUX_25; case MA_PA_CHANNEL_POSITION_AUX26: return MA_CHANNEL_AUX_26; case MA_PA_CHANNEL_POSITION_AUX27: return MA_CHANNEL_AUX_27; case MA_PA_CHANNEL_POSITION_AUX28: return MA_CHANNEL_AUX_28; case MA_PA_CHANNEL_POSITION_AUX29: return MA_CHANNEL_AUX_29; case MA_PA_CHANNEL_POSITION_AUX30: return MA_CHANNEL_AUX_30; case MA_PA_CHANNEL_POSITION_AUX31: return MA_CHANNEL_AUX_31; case MA_PA_CHANNEL_POSITION_TOP_CENTER: return MA_CHANNEL_TOP_CENTER; case MA_PA_CHANNEL_POSITION_TOP_FRONT_LEFT: return MA_CHANNEL_TOP_FRONT_LEFT; case MA_PA_CHANNEL_POSITION_TOP_FRONT_RIGHT: return MA_CHANNEL_TOP_FRONT_RIGHT; case MA_PA_CHANNEL_POSITION_TOP_FRONT_CENTER: return MA_CHANNEL_TOP_FRONT_CENTER; case MA_PA_CHANNEL_POSITION_TOP_REAR_LEFT: return MA_CHANNEL_TOP_BACK_LEFT; case MA_PA_CHANNEL_POSITION_TOP_REAR_RIGHT: return MA_CHANNEL_TOP_BACK_RIGHT; case MA_PA_CHANNEL_POSITION_TOP_REAR_CENTER: return MA_CHANNEL_TOP_BACK_CENTER; default: return MA_CHANNEL_NONE; } } #if 0 static ma_pa_channel_position_t ma_channel_position_to_pulse(ma_channel position) { switch (position) { case MA_CHANNEL_NONE: return MA_PA_CHANNEL_POSITION_INVALID; case MA_CHANNEL_FRONT_LEFT: return MA_PA_CHANNEL_POSITION_FRONT_LEFT; case MA_CHANNEL_FRONT_RIGHT: return MA_PA_CHANNEL_POSITION_FRONT_RIGHT; case MA_CHANNEL_FRONT_CENTER: return MA_PA_CHANNEL_POSITION_FRONT_CENTER; case MA_CHANNEL_LFE: return MA_PA_CHANNEL_POSITION_LFE; case MA_CHANNEL_BACK_LEFT: return MA_PA_CHANNEL_POSITION_REAR_LEFT; case MA_CHANNEL_BACK_RIGHT: return MA_PA_CHANNEL_POSITION_REAR_RIGHT; case MA_CHANNEL_FRONT_LEFT_CENTER: return MA_PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER; case MA_CHANNEL_FRONT_RIGHT_CENTER: return MA_PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER; case MA_CHANNEL_BACK_CENTER: return MA_PA_CHANNEL_POSITION_REAR_CENTER; case MA_CHANNEL_SIDE_LEFT: return MA_PA_CHANNEL_POSITION_SIDE_LEFT; case MA_CHANNEL_SIDE_RIGHT: return MA_PA_CHANNEL_POSITION_SIDE_RIGHT; case MA_CHANNEL_TOP_CENTER: return MA_PA_CHANNEL_POSITION_TOP_CENTER; case MA_CHANNEL_TOP_FRONT_LEFT: return MA_PA_CHANNEL_POSITION_TOP_FRONT_LEFT; case MA_CHANNEL_TOP_FRONT_CENTER: return MA_PA_CHANNEL_POSITION_TOP_FRONT_CENTER; case MA_CHANNEL_TOP_FRONT_RIGHT: return MA_PA_CHANNEL_POSITION_TOP_FRONT_RIGHT; case MA_CHANNEL_TOP_BACK_LEFT: return MA_PA_CHANNEL_POSITION_TOP_REAR_LEFT; case MA_CHANNEL_TOP_BACK_CENTER: return MA_PA_CHANNEL_POSITION_TOP_REAR_CENTER; case MA_CHANNEL_TOP_BACK_RIGHT: return MA_PA_CHANNEL_POSITION_TOP_REAR_RIGHT; case MA_CHANNEL_19: return MA_PA_CHANNEL_POSITION_AUX18; case MA_CHANNEL_20: return MA_PA_CHANNEL_POSITION_AUX19; case MA_CHANNEL_21: return MA_PA_CHANNEL_POSITION_AUX20; case MA_CHANNEL_22: return MA_PA_CHANNEL_POSITION_AUX21; case MA_CHANNEL_23: return MA_PA_CHANNEL_POSITION_AUX22; case MA_CHANNEL_24: return MA_PA_CHANNEL_POSITION_AUX23; case MA_CHANNEL_25: return MA_PA_CHANNEL_POSITION_AUX24; case MA_CHANNEL_26: return MA_PA_CHANNEL_POSITION_AUX25; case MA_CHANNEL_27: return MA_PA_CHANNEL_POSITION_AUX26; case MA_CHANNEL_28: return MA_PA_CHANNEL_POSITION_AUX27; case MA_CHANNEL_29: return MA_PA_CHANNEL_POSITION_AUX28; case MA_CHANNEL_30: return MA_PA_CHANNEL_POSITION_AUX29; case MA_CHANNEL_31: return MA_PA_CHANNEL_POSITION_AUX30; case MA_CHANNEL_32: return MA_PA_CHANNEL_POSITION_AUX31; default: return (ma_pa_channel_position_t)position; } } #endif static ma_result ma_wait_for_operation__pulse(ma_context* pContext, ma_ptr pMainLoop, ma_pa_operation* pOP) { int resultPA; ma_pa_operation_state_t state; MA_ASSERT(pContext != NULL); MA_ASSERT(pOP != NULL); for (;;) { state = ((ma_pa_operation_get_state_proc)pContext->pulse.pa_operation_get_state)(pOP); if (state != MA_PA_OPERATION_RUNNING) { break; /* Done. */ } resultPA = ((ma_pa_mainloop_iterate_proc)pContext->pulse.pa_mainloop_iterate)((ma_pa_mainloop*)pMainLoop, 1, NULL); if (resultPA < 0) { return ma_result_from_pulse(resultPA); } } return MA_SUCCESS; } static ma_result ma_wait_for_operation_and_unref__pulse(ma_context* pContext, ma_ptr pMainLoop, ma_pa_operation* pOP) { ma_result result; if (pOP == NULL) { return MA_INVALID_ARGS; } result = ma_wait_for_operation__pulse(pContext, pMainLoop, pOP); ((ma_pa_operation_unref_proc)pContext->pulse.pa_operation_unref)(pOP); return result; } static ma_result ma_wait_for_pa_context_to_connect__pulse(ma_context* pContext, ma_ptr pMainLoop, ma_ptr pPulseContext) { int resultPA; ma_pa_context_state_t state; for (;;) { state = ((ma_pa_context_get_state_proc)pContext->pulse.pa_context_get_state)((ma_pa_context*)pPulseContext); if (state == MA_PA_CONTEXT_READY) { break; /* Done. */ } if (state == MA_PA_CONTEXT_FAILED || state == MA_PA_CONTEXT_TERMINATED) { ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[PulseAudio] An error occurred while connecting the PulseAudio context."); return MA_ERROR; } resultPA = ((ma_pa_mainloop_iterate_proc)pContext->pulse.pa_mainloop_iterate)((ma_pa_mainloop*)pMainLoop, 1, NULL); if (resultPA < 0) { return ma_result_from_pulse(resultPA); } } /* Should never get here. */ return MA_SUCCESS; } static ma_result ma_wait_for_pa_stream_to_connect__pulse(ma_context* pContext, ma_ptr pMainLoop, ma_ptr pStream) { int resultPA; ma_pa_stream_state_t state; for (;;) { state = ((ma_pa_stream_get_state_proc)pContext->pulse.pa_stream_get_state)((ma_pa_stream*)pStream); if (state == MA_PA_STREAM_READY) { break; /* Done. */ } if (state == MA_PA_STREAM_FAILED || state == MA_PA_STREAM_TERMINATED) { ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[PulseAudio] An error occurred while connecting the PulseAudio stream."); return MA_ERROR; } resultPA = ((ma_pa_mainloop_iterate_proc)pContext->pulse.pa_mainloop_iterate)((ma_pa_mainloop*)pMainLoop, 1, NULL); if (resultPA < 0) { return ma_result_from_pulse(resultPA); } } return MA_SUCCESS; } static ma_result ma_init_pa_mainloop_and_pa_context__pulse(ma_context* pContext, const char* pApplicationName, const char* pServerName, ma_bool32 tryAutoSpawn, ma_ptr* ppMainLoop, ma_ptr* ppPulseContext) { ma_result result; ma_ptr pMainLoop; ma_ptr pPulseContext; MA_ASSERT(ppMainLoop != NULL); MA_ASSERT(ppPulseContext != NULL); /* The PulseAudio context maps well to miniaudio's notion of a context. The pa_context object will be initialized as part of the ma_context. */ pMainLoop = ((ma_pa_mainloop_new_proc)pContext->pulse.pa_mainloop_new)(); if (pMainLoop == NULL) { ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to create mainloop."); return MA_FAILED_TO_INIT_BACKEND; } pPulseContext = ((ma_pa_context_new_proc)pContext->pulse.pa_context_new)(((ma_pa_mainloop_get_api_proc)pContext->pulse.pa_mainloop_get_api)((ma_pa_mainloop*)pMainLoop), pApplicationName); if (pPulseContext == NULL) { ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to create PulseAudio context."); ((ma_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)((ma_pa_mainloop*)(pMainLoop)); return MA_FAILED_TO_INIT_BACKEND; } /* Now we need to connect to the context. Everything is asynchronous so we need to wait for it to connect before returning. */ result = ma_result_from_pulse(((ma_pa_context_connect_proc)pContext->pulse.pa_context_connect)((ma_pa_context*)pPulseContext, pServerName, (tryAutoSpawn) ? 0 : MA_PA_CONTEXT_NOAUTOSPAWN, NULL)); if (result != MA_SUCCESS) { ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to connect PulseAudio context."); ((ma_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)((ma_pa_mainloop*)(pMainLoop)); return result; } /* Since ma_context_init() runs synchronously we need to wait for the PulseAudio context to connect before we return. */ result = ma_wait_for_pa_context_to_connect__pulse(pContext, pMainLoop, pPulseContext); if (result != MA_SUCCESS) { ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[PulseAudio] Waiting for connection failed."); ((ma_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)((ma_pa_mainloop*)(pMainLoop)); return result; } *ppMainLoop = pMainLoop; *ppPulseContext = pPulseContext; return MA_SUCCESS; } static void ma_device_sink_info_callback(ma_pa_context* pPulseContext, const ma_pa_sink_info* pInfo, int endOfList, void* pUserData) { ma_pa_sink_info* pInfoOut; if (endOfList > 0) { return; } /* There has been a report that indicates that pInfo can be null which results in a null pointer dereference below. We'll check for this for safety. */ if (pInfo == NULL) { return; } pInfoOut = (ma_pa_sink_info*)pUserData; MA_ASSERT(pInfoOut != NULL); *pInfoOut = *pInfo; (void)pPulseContext; /* Unused. */ } static void ma_device_source_info_callback(ma_pa_context* pPulseContext, const ma_pa_source_info* pInfo, int endOfList, void* pUserData) { ma_pa_source_info* pInfoOut; if (endOfList > 0) { return; } /* There has been a report that indicates that pInfo can be null which results in a null pointer dereference below. We'll check for this for safety. */ if (pInfo == NULL) { return; } pInfoOut = (ma_pa_source_info*)pUserData; MA_ASSERT(pInfoOut != NULL); *pInfoOut = *pInfo; (void)pPulseContext; /* Unused. */ } #if 0 static void ma_device_sink_name_callback(ma_pa_context* pPulseContext, const ma_pa_sink_info* pInfo, int endOfList, void* pUserData) { ma_device* pDevice; if (endOfList > 0) { return; } pDevice = (ma_device*)pUserData; MA_ASSERT(pDevice != NULL); ma_strncpy_s(pDevice->playback.name, sizeof(pDevice->playback.name), pInfo->description, (size_t)-1); (void)pPulseContext; /* Unused. */ } static void ma_device_source_name_callback(ma_pa_context* pPulseContext, const ma_pa_source_info* pInfo, int endOfList, void* pUserData) { ma_device* pDevice; if (endOfList > 0) { return; } pDevice = (ma_device*)pUserData; MA_ASSERT(pDevice != NULL); ma_strncpy_s(pDevice->capture.name, sizeof(pDevice->capture.name), pInfo->description, (size_t)-1); (void)pPulseContext; /* Unused. */ } #endif static ma_result ma_context_get_sink_info__pulse(ma_context* pContext, const char* pDeviceName, ma_pa_sink_info* pSinkInfo) { ma_pa_operation* pOP; pOP = ((ma_pa_context_get_sink_info_by_name_proc)pContext->pulse.pa_context_get_sink_info_by_name)((ma_pa_context*)pContext->pulse.pPulseContext, pDeviceName, ma_device_sink_info_callback, pSinkInfo); if (pOP == NULL) { return MA_ERROR; } return ma_wait_for_operation_and_unref__pulse(pContext, pContext->pulse.pMainLoop, pOP); } static ma_result ma_context_get_source_info__pulse(ma_context* pContext, const char* pDeviceName, ma_pa_source_info* pSourceInfo) { ma_pa_operation* pOP; pOP = ((ma_pa_context_get_source_info_by_name_proc)pContext->pulse.pa_context_get_source_info_by_name)((ma_pa_context*)pContext->pulse.pPulseContext, pDeviceName, ma_device_source_info_callback, pSourceInfo); if (pOP == NULL) { return MA_ERROR; } return ma_wait_for_operation_and_unref__pulse(pContext, pContext->pulse.pMainLoop, pOP); } static ma_result ma_context_get_default_device_index__pulse(ma_context* pContext, ma_device_type deviceType, ma_uint32* pIndex) { ma_result result; MA_ASSERT(pContext != NULL); MA_ASSERT(pIndex != NULL); if (pIndex != NULL) { *pIndex = (ma_uint32)-1; } if (deviceType == ma_device_type_playback) { ma_pa_sink_info sinkInfo; result = ma_context_get_sink_info__pulse(pContext, NULL, &sinkInfo); if (result != MA_SUCCESS) { return result; } if (pIndex != NULL) { *pIndex = sinkInfo.index; } } if (deviceType == ma_device_type_capture) { ma_pa_source_info sourceInfo; result = ma_context_get_source_info__pulse(pContext, NULL, &sourceInfo); if (result != MA_SUCCESS) { return result; } if (pIndex != NULL) { *pIndex = sourceInfo.index; } } return MA_SUCCESS; } typedef struct { ma_context* pContext; ma_enum_devices_callback_proc callback; void* pUserData; ma_bool32 isTerminated; ma_uint32 defaultDeviceIndexPlayback; ma_uint32 defaultDeviceIndexCapture; } ma_context_enumerate_devices_callback_data__pulse; static void ma_context_enumerate_devices_sink_callback__pulse(ma_pa_context* pPulseContext, const ma_pa_sink_info* pSinkInfo, int endOfList, void* pUserData) { ma_context_enumerate_devices_callback_data__pulse* pData = (ma_context_enumerate_devices_callback_data__pulse*)pUserData; ma_device_info deviceInfo; MA_ASSERT(pData != NULL); if (endOfList || pData->isTerminated) { return; } MA_ZERO_OBJECT(&deviceInfo); /* The name from PulseAudio is the ID for miniaudio. */ if (pSinkInfo->name != NULL) { ma_strncpy_s(deviceInfo.id.pulse, sizeof(deviceInfo.id.pulse), pSinkInfo->name, (size_t)-1); } /* The description from PulseAudio is the name for miniaudio. */ if (pSinkInfo->description != NULL) { ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), pSinkInfo->description, (size_t)-1); } if (pSinkInfo->index == pData->defaultDeviceIndexPlayback) { deviceInfo.isDefault = MA_TRUE; } pData->isTerminated = !pData->callback(pData->pContext, ma_device_type_playback, &deviceInfo, pData->pUserData); (void)pPulseContext; /* Unused. */ } static void ma_context_enumerate_devices_source_callback__pulse(ma_pa_context* pPulseContext, const ma_pa_source_info* pSourceInfo, int endOfList, void* pUserData) { ma_context_enumerate_devices_callback_data__pulse* pData = (ma_context_enumerate_devices_callback_data__pulse*)pUserData; ma_device_info deviceInfo; MA_ASSERT(pData != NULL); if (endOfList || pData->isTerminated) { return; } MA_ZERO_OBJECT(&deviceInfo); /* The name from PulseAudio is the ID for miniaudio. */ if (pSourceInfo->name != NULL) { ma_strncpy_s(deviceInfo.id.pulse, sizeof(deviceInfo.id.pulse), pSourceInfo->name, (size_t)-1); } /* The description from PulseAudio is the name for miniaudio. */ if (pSourceInfo->description != NULL) { ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), pSourceInfo->description, (size_t)-1); } if (pSourceInfo->index == pData->defaultDeviceIndexCapture) { deviceInfo.isDefault = MA_TRUE; } pData->isTerminated = !pData->callback(pData->pContext, ma_device_type_capture, &deviceInfo, pData->pUserData); (void)pPulseContext; /* Unused. */ } static ma_result ma_context_enumerate_devices__pulse(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData) { ma_result result = MA_SUCCESS; ma_context_enumerate_devices_callback_data__pulse callbackData; ma_pa_operation* pOP = NULL; MA_ASSERT(pContext != NULL); MA_ASSERT(callback != NULL); callbackData.pContext = pContext; callbackData.callback = callback; callbackData.pUserData = pUserData; callbackData.isTerminated = MA_FALSE; callbackData.defaultDeviceIndexPlayback = (ma_uint32)-1; callbackData.defaultDeviceIndexCapture = (ma_uint32)-1; /* We need to get the index of the default devices. */ ma_context_get_default_device_index__pulse(pContext, ma_device_type_playback, &callbackData.defaultDeviceIndexPlayback); ma_context_get_default_device_index__pulse(pContext, ma_device_type_capture, &callbackData.defaultDeviceIndexCapture); /* Playback. */ if (!callbackData.isTerminated) { pOP = ((ma_pa_context_get_sink_info_list_proc)pContext->pulse.pa_context_get_sink_info_list)((ma_pa_context*)(pContext->pulse.pPulseContext), ma_context_enumerate_devices_sink_callback__pulse, &callbackData); if (pOP == NULL) { result = MA_ERROR; goto done; } result = ma_wait_for_operation__pulse(pContext, pContext->pulse.pMainLoop, pOP); ((ma_pa_operation_unref_proc)pContext->pulse.pa_operation_unref)(pOP); if (result != MA_SUCCESS) { goto done; } } /* Capture. */ if (!callbackData.isTerminated) { pOP = ((ma_pa_context_get_source_info_list_proc)pContext->pulse.pa_context_get_source_info_list)((ma_pa_context*)(pContext->pulse.pPulseContext), ma_context_enumerate_devices_source_callback__pulse, &callbackData); if (pOP == NULL) { result = MA_ERROR; goto done; } result = ma_wait_for_operation__pulse(pContext, pContext->pulse.pMainLoop, pOP); ((ma_pa_operation_unref_proc)pContext->pulse.pa_operation_unref)(pOP); if (result != MA_SUCCESS) { goto done; } } done: return result; } typedef struct { ma_device_info* pDeviceInfo; ma_uint32 defaultDeviceIndex; ma_bool32 foundDevice; } ma_context_get_device_info_callback_data__pulse; static void ma_context_get_device_info_sink_callback__pulse(ma_pa_context* pPulseContext, const ma_pa_sink_info* pInfo, int endOfList, void* pUserData) { ma_context_get_device_info_callback_data__pulse* pData = (ma_context_get_device_info_callback_data__pulse*)pUserData; if (endOfList > 0) { return; } MA_ASSERT(pData != NULL); pData->foundDevice = MA_TRUE; if (pInfo->name != NULL) { ma_strncpy_s(pData->pDeviceInfo->id.pulse, sizeof(pData->pDeviceInfo->id.pulse), pInfo->name, (size_t)-1); } if (pInfo->description != NULL) { ma_strncpy_s(pData->pDeviceInfo->name, sizeof(pData->pDeviceInfo->name), pInfo->description, (size_t)-1); } /* We're just reporting a single data format here. I think technically PulseAudio might support all formats, but I don't trust that PulseAudio will do *anything* right, so I'm just going to report the "native" device format. */ pData->pDeviceInfo->nativeDataFormats[0].format = ma_format_from_pulse(pInfo->sample_spec.format); pData->pDeviceInfo->nativeDataFormats[0].channels = pInfo->sample_spec.channels; pData->pDeviceInfo->nativeDataFormats[0].sampleRate = pInfo->sample_spec.rate; pData->pDeviceInfo->nativeDataFormats[0].flags = 0; pData->pDeviceInfo->nativeDataFormatCount = 1; if (pData->defaultDeviceIndex == pInfo->index) { pData->pDeviceInfo->isDefault = MA_TRUE; } (void)pPulseContext; /* Unused. */ } static void ma_context_get_device_info_source_callback__pulse(ma_pa_context* pPulseContext, const ma_pa_source_info* pInfo, int endOfList, void* pUserData) { ma_context_get_device_info_callback_data__pulse* pData = (ma_context_get_device_info_callback_data__pulse*)pUserData; if (endOfList > 0) { return; } MA_ASSERT(pData != NULL); pData->foundDevice = MA_TRUE; if (pInfo->name != NULL) { ma_strncpy_s(pData->pDeviceInfo->id.pulse, sizeof(pData->pDeviceInfo->id.pulse), pInfo->name, (size_t)-1); } if (pInfo->description != NULL) { ma_strncpy_s(pData->pDeviceInfo->name, sizeof(pData->pDeviceInfo->name), pInfo->description, (size_t)-1); } /* We're just reporting a single data format here. I think technically PulseAudio might support all formats, but I don't trust that PulseAudio will do *anything* right, so I'm just going to report the "native" device format. */ pData->pDeviceInfo->nativeDataFormats[0].format = ma_format_from_pulse(pInfo->sample_spec.format); pData->pDeviceInfo->nativeDataFormats[0].channels = pInfo->sample_spec.channels; pData->pDeviceInfo->nativeDataFormats[0].sampleRate = pInfo->sample_spec.rate; pData->pDeviceInfo->nativeDataFormats[0].flags = 0; pData->pDeviceInfo->nativeDataFormatCount = 1; if (pData->defaultDeviceIndex == pInfo->index) { pData->pDeviceInfo->isDefault = MA_TRUE; } (void)pPulseContext; /* Unused. */ } static ma_result ma_context_get_device_info__pulse(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo) { ma_result result = MA_SUCCESS; ma_context_get_device_info_callback_data__pulse callbackData; ma_pa_operation* pOP = NULL; const char* pDeviceName = NULL; MA_ASSERT(pContext != NULL); callbackData.pDeviceInfo = pDeviceInfo; callbackData.foundDevice = MA_FALSE; if (pDeviceID != NULL) { pDeviceName = pDeviceID->pulse; } else { pDeviceName = NULL; } result = ma_context_get_default_device_index__pulse(pContext, deviceType, &callbackData.defaultDeviceIndex); if (deviceType == ma_device_type_playback) { pOP = ((ma_pa_context_get_sink_info_by_name_proc)pContext->pulse.pa_context_get_sink_info_by_name)((ma_pa_context*)(pContext->pulse.pPulseContext), pDeviceName, ma_context_get_device_info_sink_callback__pulse, &callbackData); } else { pOP = ((ma_pa_context_get_source_info_by_name_proc)pContext->pulse.pa_context_get_source_info_by_name)((ma_pa_context*)(pContext->pulse.pPulseContext), pDeviceName, ma_context_get_device_info_source_callback__pulse, &callbackData); } if (pOP != NULL) { ma_wait_for_operation_and_unref__pulse(pContext, pContext->pulse.pMainLoop, pOP); } else { result = MA_ERROR; goto done; } if (!callbackData.foundDevice) { result = MA_NO_DEVICE; goto done; } done: return result; } static ma_result ma_device_uninit__pulse(ma_device* pDevice) { ma_context* pContext; MA_ASSERT(pDevice != NULL); pContext = pDevice->pContext; MA_ASSERT(pContext != NULL); if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { ((ma_pa_stream_disconnect_proc)pContext->pulse.pa_stream_disconnect)((ma_pa_stream*)pDevice->pulse.pStreamCapture); ((ma_pa_stream_unref_proc)pContext->pulse.pa_stream_unref)((ma_pa_stream*)pDevice->pulse.pStreamCapture); } if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { ((ma_pa_stream_disconnect_proc)pContext->pulse.pa_stream_disconnect)((ma_pa_stream*)pDevice->pulse.pStreamPlayback); ((ma_pa_stream_unref_proc)pContext->pulse.pa_stream_unref)((ma_pa_stream*)pDevice->pulse.pStreamPlayback); } if (pDevice->type == ma_device_type_duplex) { ma_duplex_rb_uninit(&pDevice->duplexRB); } ((ma_pa_context_disconnect_proc)pContext->pulse.pa_context_disconnect)((ma_pa_context*)pDevice->pulse.pPulseContext); ((ma_pa_context_unref_proc)pContext->pulse.pa_context_unref)((ma_pa_context*)pDevice->pulse.pPulseContext); ((ma_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)((ma_pa_mainloop*)pDevice->pulse.pMainLoop); return MA_SUCCESS; } static ma_pa_buffer_attr ma_device__pa_buffer_attr_new(ma_uint32 periodSizeInFrames, ma_uint32 periods, const ma_pa_sample_spec* ss) { ma_pa_buffer_attr attr; attr.maxlength = periodSizeInFrames * periods * ma_get_bytes_per_frame(ma_format_from_pulse(ss->format), ss->channels); attr.tlength = attr.maxlength / periods; attr.prebuf = (ma_uint32)-1; attr.minreq = (ma_uint32)-1; attr.fragsize = attr.maxlength / periods; return attr; } static ma_pa_stream* ma_device__pa_stream_new__pulse(ma_device* pDevice, const char* pStreamName, const ma_pa_sample_spec* ss, const ma_pa_channel_map* cmap) { static int g_StreamCounter = 0; char actualStreamName[256]; if (pStreamName != NULL) { ma_strncpy_s(actualStreamName, sizeof(actualStreamName), pStreamName, (size_t)-1); } else { ma_strcpy_s(actualStreamName, sizeof(actualStreamName), "miniaudio:"); ma_itoa_s(g_StreamCounter, actualStreamName + 8, sizeof(actualStreamName)-8, 10); /* 8 = strlen("miniaudio:") */ } g_StreamCounter += 1; return ((ma_pa_stream_new_proc)pDevice->pContext->pulse.pa_stream_new)((ma_pa_context*)pDevice->pulse.pPulseContext, actualStreamName, ss, cmap); } static void ma_device_on_read__pulse(ma_pa_stream* pStream, size_t byteCount, void* pUserData) { ma_device* pDevice = (ma_device*)pUserData; ma_uint32 bpf; ma_uint32 deviceState; ma_uint64 frameCount; ma_uint64 framesProcessed; MA_ASSERT(pDevice != NULL); /* Don't do anything if the device isn't initialized yet. Yes, this can happen because PulseAudio can fire this callback before the stream has even started. Ridiculous. */ deviceState = ma_device_get_state(pDevice); if (deviceState != ma_device_state_starting && deviceState != ma_device_state_started) { return; } bpf = ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels); MA_ASSERT(bpf > 0); frameCount = byteCount / bpf; framesProcessed = 0; while (ma_device_get_state(pDevice) == ma_device_state_started && framesProcessed < frameCount) { const void* pMappedPCMFrames; size_t bytesMapped; ma_uint64 framesMapped; int pulseResult = ((ma_pa_stream_peek_proc)pDevice->pContext->pulse.pa_stream_peek)(pStream, &pMappedPCMFrames, &bytesMapped); if (pulseResult < 0) { break; /* Failed to map. Abort. */ } framesMapped = bytesMapped / bpf; if (framesMapped > 0) { if (pMappedPCMFrames != NULL) { ma_device_handle_backend_data_callback(pDevice, NULL, pMappedPCMFrames, framesMapped); } else { /* It's a hole. */ ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[PulseAudio] ma_device_on_read__pulse: Hole.\n"); } pulseResult = ((ma_pa_stream_drop_proc)pDevice->pContext->pulse.pa_stream_drop)(pStream); if (pulseResult < 0) { break; /* Failed to drop the buffer. */ } framesProcessed += framesMapped; } else { /* Nothing was mapped. Just abort. */ break; } } } static ma_result ma_device_write_to_stream__pulse(ma_device* pDevice, ma_pa_stream* pStream, ma_uint64* pFramesProcessed) { ma_result result = MA_SUCCESS; ma_uint64 framesProcessed = 0; size_t bytesMapped; ma_uint32 bpf; ma_uint32 deviceState; MA_ASSERT(pDevice != NULL); MA_ASSERT(pStream != NULL); bpf = ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels); MA_ASSERT(bpf > 0); deviceState = ma_device_get_state(pDevice); bytesMapped = ((ma_pa_stream_writable_size_proc)pDevice->pContext->pulse.pa_stream_writable_size)(pStream); if (bytesMapped != (size_t)-1) { if (bytesMapped > 0) { ma_uint64 framesMapped; void* pMappedPCMFrames; int pulseResult = ((ma_pa_stream_begin_write_proc)pDevice->pContext->pulse.pa_stream_begin_write)(pStream, &pMappedPCMFrames, &bytesMapped); if (pulseResult < 0) { result = ma_result_from_pulse(pulseResult); goto done; } framesMapped = bytesMapped / bpf; if (deviceState == ma_device_state_started || deviceState == ma_device_state_starting) { /* Check for starting state just in case this is being used to do the initial fill. */ ma_device_handle_backend_data_callback(pDevice, pMappedPCMFrames, NULL, framesMapped); } else { /* Device is not started. Write silence. */ ma_silence_pcm_frames(pMappedPCMFrames, framesMapped, pDevice->playback.format, pDevice->playback.channels); } pulseResult = ((ma_pa_stream_write_proc)pDevice->pContext->pulse.pa_stream_write)(pStream, pMappedPCMFrames, bytesMapped, NULL, 0, MA_PA_SEEK_RELATIVE); if (pulseResult < 0) { result = ma_result_from_pulse(pulseResult); goto done; /* Failed to write data to stream. */ } framesProcessed += framesMapped; } else { result = MA_SUCCESS; /* No data available for writing. */ goto done; } } else { result = MA_ERROR; /* Failed to retrieve the writable size. Abort. */ goto done; } done: if (pFramesProcessed != NULL) { *pFramesProcessed = framesProcessed; } return result; } static void ma_device_on_write__pulse(ma_pa_stream* pStream, size_t byteCount, void* pUserData) { ma_device* pDevice = (ma_device*)pUserData; ma_uint32 bpf; ma_uint64 frameCount; ma_uint64 framesProcessed; ma_uint32 deviceState; ma_result result; MA_ASSERT(pDevice != NULL); /* Don't do anything if the device isn't initialized yet. Yes, this can happen because PulseAudio can fire this callback before the stream has even started. Ridiculous. */ deviceState = ma_device_get_state(pDevice); if (deviceState != ma_device_state_starting && deviceState != ma_device_state_started) { return; } bpf = ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels); MA_ASSERT(bpf > 0); frameCount = byteCount / bpf; framesProcessed = 0; while (framesProcessed < frameCount) { ma_uint64 framesProcessedThisIteration; /* Don't keep trying to process frames if the device isn't started. */ deviceState = ma_device_get_state(pDevice); if (deviceState != ma_device_state_starting && deviceState != ma_device_state_started) { break; } result = ma_device_write_to_stream__pulse(pDevice, pStream, &framesProcessedThisIteration); if (result != MA_SUCCESS) { break; } framesProcessed += framesProcessedThisIteration; } } static void ma_device_on_suspended__pulse(ma_pa_stream* pStream, void* pUserData) { ma_device* pDevice = (ma_device*)pUserData; int suspended; (void)pStream; suspended = ((ma_pa_stream_is_suspended_proc)pDevice->pContext->pulse.pa_stream_is_suspended)(pStream); ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[Pulse] Device suspended state changed. pa_stream_is_suspended() returned %d.\n", suspended); if (suspended < 0) { return; } if (suspended == 1) { ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[Pulse] Device suspended state changed. Suspended.\n"); ma_device__on_notification_stopped(pDevice); } else { ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[Pulse] Device suspended state changed. Resumed.\n"); ma_device__on_notification_started(pDevice); } } static void ma_device_on_rerouted__pulse(ma_pa_stream* pStream, void* pUserData) { ma_device* pDevice = (ma_device*)pUserData; (void)pStream; (void)pUserData; ma_device__on_notification_rerouted(pDevice); } static ma_uint32 ma_calculate_period_size_in_frames_from_descriptor__pulse(const ma_device_descriptor* pDescriptor, ma_uint32 nativeSampleRate, ma_performance_profile performanceProfile) { /* There have been reports from users where buffers of < ~20ms result glitches when running through PipeWire. To work around this we're going to have to use a different default buffer size. */ const ma_uint32 defaultPeriodSizeInMilliseconds_LowLatency = 25; const ma_uint32 defaultPeriodSizeInMilliseconds_Conservative = MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_CONSERVATIVE; MA_ASSERT(nativeSampleRate != 0); if (pDescriptor->periodSizeInFrames == 0) { if (pDescriptor->periodSizeInMilliseconds == 0) { if (performanceProfile == ma_performance_profile_low_latency) { return ma_calculate_buffer_size_in_frames_from_milliseconds(defaultPeriodSizeInMilliseconds_LowLatency, nativeSampleRate); } else { return ma_calculate_buffer_size_in_frames_from_milliseconds(defaultPeriodSizeInMilliseconds_Conservative, nativeSampleRate); } } else { return ma_calculate_buffer_size_in_frames_from_milliseconds(pDescriptor->periodSizeInMilliseconds, nativeSampleRate); } } else { return pDescriptor->periodSizeInFrames; } } static ma_result ma_device_init__pulse(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture) { /* Notes for PulseAudio: - We're always using native format/channels/rate regardless of whether or not PulseAudio supports the format directly through their own data conversion system. I'm doing this to reduce as much variability from the PulseAudio side as possible because it's seems to be extremely unreliable at everything it does. - When both the period size in frames and milliseconds are 0, we default to miniaudio's default buffer sizes rather than leaving it up to PulseAudio because I don't trust PulseAudio to give us any kind of reasonable latency by default. - Do not ever, *ever* forget to use MA_PA_STREAM_ADJUST_LATENCY. If you don't specify this flag, capture mode will just not work properly until you open another PulseAudio app. */ ma_result result = MA_SUCCESS; int error = 0; const char* devPlayback = NULL; const char* devCapture = NULL; ma_format format = ma_format_unknown; ma_uint32 channels = 0; ma_uint32 sampleRate = 0; ma_pa_sink_info sinkInfo; ma_pa_source_info sourceInfo; ma_pa_sample_spec ss; ma_pa_channel_map cmap; ma_pa_buffer_attr attr; const ma_pa_sample_spec* pActualSS = NULL; const ma_pa_channel_map* pActualCMap = NULL; const ma_pa_buffer_attr* pActualAttr = NULL; ma_uint32 iChannel; ma_pa_stream_flags_t streamFlags; MA_ASSERT(pDevice != NULL); MA_ZERO_OBJECT(&pDevice->pulse); if (pConfig->deviceType == ma_device_type_loopback) { return MA_DEVICE_TYPE_NOT_SUPPORTED; } /* No exclusive mode with the PulseAudio backend. */ if (((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pConfig->playback.shareMode == ma_share_mode_exclusive) || ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pConfig->capture.shareMode == ma_share_mode_exclusive)) { return MA_SHARE_MODE_NOT_SUPPORTED; } if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { if (pDescriptorPlayback->pDeviceID != NULL) { devPlayback = pDescriptorPlayback->pDeviceID->pulse; } format = pDescriptorPlayback->format; channels = pDescriptorPlayback->channels; sampleRate = pDescriptorPlayback->sampleRate; } if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { if (pDescriptorCapture->pDeviceID != NULL) { devCapture = pDescriptorCapture->pDeviceID->pulse; } format = pDescriptorCapture->format; channels = pDescriptorCapture->channels; sampleRate = pDescriptorCapture->sampleRate; } result = ma_init_pa_mainloop_and_pa_context__pulse(pDevice->pContext, pDevice->pContext->pulse.pApplicationName, pDevice->pContext->pulse.pServerName, MA_FALSE, &pDevice->pulse.pMainLoop, &pDevice->pulse.pPulseContext); if (result != MA_SUCCESS) { ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to initialize PA mainloop and context for device.\n"); return result; } if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { result = ma_context_get_source_info__pulse(pDevice->pContext, devCapture, &sourceInfo); if (result != MA_SUCCESS) { ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to retrieve source info for capture device."); goto on_error0; } ss = sourceInfo.sample_spec; cmap = sourceInfo.channel_map; /* Use the requested sample rate if one was specified. */ if (pDescriptorCapture->sampleRate != 0) { ss.rate = pDescriptorCapture->sampleRate; } if (ma_format_from_pulse(ss.format) == ma_format_unknown) { if (ma_is_little_endian()) { ss.format = MA_PA_SAMPLE_FLOAT32LE; } else { ss.format = MA_PA_SAMPLE_FLOAT32BE; } ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] sample_spec.format not supported by miniaudio. Defaulting to PA_SAMPLE_FLOAT32.\n"); } if (ss.rate == 0) { ss.rate = MA_DEFAULT_SAMPLE_RATE; ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] sample_spec.rate = 0. Defaulting to %d.\n", ss.rate); } if (ss.channels == 0) { ss.channels = MA_DEFAULT_CHANNELS; ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] sample_spec.channels = 0. Defaulting to %d.\n", ss.channels); } /* We now have enough information to calculate our actual period size in frames. */ pDescriptorCapture->periodSizeInFrames = ma_calculate_period_size_in_frames_from_descriptor__pulse(pDescriptorCapture, ss.rate, pConfig->performanceProfile); attr = ma_device__pa_buffer_attr_new(pDescriptorCapture->periodSizeInFrames, pDescriptorCapture->periodCount, &ss); ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] Capture attr: maxlength=%d, tlength=%d, prebuf=%d, minreq=%d, fragsize=%d; periodSizeInFrames=%d\n", attr.maxlength, attr.tlength, attr.prebuf, attr.minreq, attr.fragsize, pDescriptorCapture->periodSizeInFrames); pDevice->pulse.pStreamCapture = ma_device__pa_stream_new__pulse(pDevice, pConfig->pulse.pStreamNameCapture, &ss, &cmap); if (pDevice->pulse.pStreamCapture == NULL) { ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to create PulseAudio capture stream.\n"); result = MA_ERROR; goto on_error0; } /* The callback needs to be set before connecting the stream. */ ((ma_pa_stream_set_read_callback_proc)pDevice->pContext->pulse.pa_stream_set_read_callback)((ma_pa_stream*)pDevice->pulse.pStreamCapture, ma_device_on_read__pulse, pDevice); /* State callback for checking when the device has been corked. */ ((ma_pa_stream_set_suspended_callback_proc)pDevice->pContext->pulse.pa_stream_set_suspended_callback)((ma_pa_stream*)pDevice->pulse.pStreamCapture, ma_device_on_suspended__pulse, pDevice); /* Rerouting notification. */ ((ma_pa_stream_set_moved_callback_proc)pDevice->pContext->pulse.pa_stream_set_moved_callback)((ma_pa_stream*)pDevice->pulse.pStreamCapture, ma_device_on_rerouted__pulse, pDevice); /* Connect after we've got all of our internal state set up. */ streamFlags = MA_PA_STREAM_START_CORKED | MA_PA_STREAM_ADJUST_LATENCY | MA_PA_STREAM_FIX_FORMAT | MA_PA_STREAM_FIX_RATE | MA_PA_STREAM_FIX_CHANNELS; if (devCapture != NULL) { streamFlags |= MA_PA_STREAM_DONT_MOVE; } error = ((ma_pa_stream_connect_record_proc)pDevice->pContext->pulse.pa_stream_connect_record)((ma_pa_stream*)pDevice->pulse.pStreamCapture, devCapture, &attr, streamFlags); if (error != MA_PA_OK) { ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to connect PulseAudio capture stream."); result = ma_result_from_pulse(error); goto on_error1; } result = ma_wait_for_pa_stream_to_connect__pulse(pDevice->pContext, pDevice->pulse.pMainLoop, (ma_pa_stream*)pDevice->pulse.pStreamCapture); if (result != MA_SUCCESS) { goto on_error2; } /* Internal format. */ pActualSS = ((ma_pa_stream_get_sample_spec_proc)pDevice->pContext->pulse.pa_stream_get_sample_spec)((ma_pa_stream*)pDevice->pulse.pStreamCapture); if (pActualSS != NULL) { ss = *pActualSS; ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] Capture sample spec: format=%s, channels=%d, rate=%d\n", ma_get_format_name(ma_format_from_pulse(ss.format)), ss.channels, ss.rate); } else { ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] Failed to retrieve capture sample spec.\n"); } pDescriptorCapture->format = ma_format_from_pulse(ss.format); pDescriptorCapture->channels = ss.channels; pDescriptorCapture->sampleRate = ss.rate; if (pDescriptorCapture->format == ma_format_unknown || pDescriptorCapture->channels == 0 || pDescriptorCapture->sampleRate == 0) { ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Capture sample spec is invalid. Device unusable by miniaudio. format=%s, channels=%d, sampleRate=%d.\n", ma_get_format_name(pDescriptorCapture->format), pDescriptorCapture->channels, pDescriptorCapture->sampleRate); result = MA_ERROR; goto on_error4; } /* Internal channel map. */ /* Bug in PipeWire. There have been reports that PipeWire is returning AUX channels when reporting the channel map. To somewhat workaround this, I'm hacking in a hard coded channel map for mono and stereo. In this case it should be safe to assume mono = MONO and stereo = LEFT/RIGHT. For all other channel counts we need to just put up with whatever PipeWire reports and hope it gets fixed sooner than later. I might remove this hack later. */ if (pDescriptorCapture->channels > 2) { pActualCMap = ((ma_pa_stream_get_channel_map_proc)pDevice->pContext->pulse.pa_stream_get_channel_map)((ma_pa_stream*)pDevice->pulse.pStreamCapture); if (pActualCMap != NULL) { cmap = *pActualCMap; } for (iChannel = 0; iChannel < pDescriptorCapture->channels; ++iChannel) { pDescriptorCapture->channelMap[iChannel] = ma_channel_position_from_pulse(cmap.map[iChannel]); } } else { /* Hack for mono and stereo. */ if (pDescriptorCapture->channels == 1) { pDescriptorCapture->channelMap[0] = MA_CHANNEL_MONO; } else if (pDescriptorCapture->channels == 2) { pDescriptorCapture->channelMap[0] = MA_CHANNEL_FRONT_LEFT; pDescriptorCapture->channelMap[1] = MA_CHANNEL_FRONT_RIGHT; } else { MA_ASSERT(MA_FALSE); /* Should never hit this. */ } } /* Buffer. */ pActualAttr = ((ma_pa_stream_get_buffer_attr_proc)pDevice->pContext->pulse.pa_stream_get_buffer_attr)((ma_pa_stream*)pDevice->pulse.pStreamCapture); if (pActualAttr != NULL) { attr = *pActualAttr; } if (attr.fragsize > 0) { pDescriptorCapture->periodCount = ma_max(attr.maxlength / attr.fragsize, 1); } else { pDescriptorCapture->periodCount = 1; } pDescriptorCapture->periodSizeInFrames = attr.maxlength / ma_get_bytes_per_frame(pDescriptorCapture->format, pDescriptorCapture->channels) / pDescriptorCapture->periodCount; ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] Capture actual attr: maxlength=%d, tlength=%d, prebuf=%d, minreq=%d, fragsize=%d; periodSizeInFrames=%d\n", attr.maxlength, attr.tlength, attr.prebuf, attr.minreq, attr.fragsize, pDescriptorCapture->periodSizeInFrames); } if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { result = ma_context_get_sink_info__pulse(pDevice->pContext, devPlayback, &sinkInfo); if (result != MA_SUCCESS) { ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to retrieve sink info for playback device.\n"); goto on_error2; } ss = sinkInfo.sample_spec; cmap = sinkInfo.channel_map; /* Use the requested sample rate if one was specified. */ if (pDescriptorPlayback->sampleRate != 0) { ss.rate = pDescriptorPlayback->sampleRate; } if (ma_format_from_pulse(ss.format) == ma_format_unknown) { if (ma_is_little_endian()) { ss.format = MA_PA_SAMPLE_FLOAT32LE; } else { ss.format = MA_PA_SAMPLE_FLOAT32BE; } ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] sample_spec.format not supported by miniaudio. Defaulting to PA_SAMPLE_FLOAT32.\n"); } if (ss.rate == 0) { ss.rate = MA_DEFAULT_SAMPLE_RATE; ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] sample_spec.rate = 0. Defaulting to %d.\n", ss.rate); } if (ss.channels == 0) { ss.channels = MA_DEFAULT_CHANNELS; ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] sample_spec.channels = 0. Defaulting to %d.\n", ss.channels); } /* We now have enough information to calculate the actual buffer size in frames. */ pDescriptorPlayback->periodSizeInFrames = ma_calculate_period_size_in_frames_from_descriptor__pulse(pDescriptorPlayback, ss.rate, pConfig->performanceProfile); attr = ma_device__pa_buffer_attr_new(pDescriptorPlayback->periodSizeInFrames, pDescriptorPlayback->periodCount, &ss); ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] Playback attr: maxlength=%d, tlength=%d, prebuf=%d, minreq=%d, fragsize=%d; periodSizeInFrames=%d\n", attr.maxlength, attr.tlength, attr.prebuf, attr.minreq, attr.fragsize, pDescriptorPlayback->periodSizeInFrames); pDevice->pulse.pStreamPlayback = ma_device__pa_stream_new__pulse(pDevice, pConfig->pulse.pStreamNamePlayback, &ss, &cmap); if (pDevice->pulse.pStreamPlayback == NULL) { ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to create PulseAudio playback stream.\n"); result = MA_ERROR; goto on_error2; } /* Note that this callback will be fired as soon as the stream is connected, even though it's started as corked. The callback needs to handle a device state of ma_device_state_uninitialized. */ ((ma_pa_stream_set_write_callback_proc)pDevice->pContext->pulse.pa_stream_set_write_callback)((ma_pa_stream*)pDevice->pulse.pStreamPlayback, ma_device_on_write__pulse, pDevice); /* State callback for checking when the device has been corked. */ ((ma_pa_stream_set_suspended_callback_proc)pDevice->pContext->pulse.pa_stream_set_suspended_callback)((ma_pa_stream*)pDevice->pulse.pStreamPlayback, ma_device_on_suspended__pulse, pDevice); /* Rerouting notification. */ ((ma_pa_stream_set_moved_callback_proc)pDevice->pContext->pulse.pa_stream_set_moved_callback)((ma_pa_stream*)pDevice->pulse.pStreamPlayback, ma_device_on_rerouted__pulse, pDevice); /* Connect after we've got all of our internal state set up. */ streamFlags = MA_PA_STREAM_START_CORKED | MA_PA_STREAM_ADJUST_LATENCY | MA_PA_STREAM_FIX_FORMAT | MA_PA_STREAM_FIX_RATE | MA_PA_STREAM_FIX_CHANNELS; if (devPlayback != NULL) { streamFlags |= MA_PA_STREAM_DONT_MOVE; } error = ((ma_pa_stream_connect_playback_proc)pDevice->pContext->pulse.pa_stream_connect_playback)((ma_pa_stream*)pDevice->pulse.pStreamPlayback, devPlayback, &attr, streamFlags, NULL, NULL); if (error != MA_PA_OK) { ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to connect PulseAudio playback stream."); result = ma_result_from_pulse(error); goto on_error3; } result = ma_wait_for_pa_stream_to_connect__pulse(pDevice->pContext, pDevice->pulse.pMainLoop, (ma_pa_stream*)pDevice->pulse.pStreamPlayback); if (result != MA_SUCCESS) { goto on_error3; } /* Internal format. */ pActualSS = ((ma_pa_stream_get_sample_spec_proc)pDevice->pContext->pulse.pa_stream_get_sample_spec)((ma_pa_stream*)pDevice->pulse.pStreamPlayback); if (pActualSS != NULL) { ss = *pActualSS; ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] Playback sample spec: format=%s, channels=%d, rate=%d\n", ma_get_format_name(ma_format_from_pulse(ss.format)), ss.channels, ss.rate); } else { ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] Failed to retrieve playback sample spec.\n"); } pDescriptorPlayback->format = ma_format_from_pulse(ss.format); pDescriptorPlayback->channels = ss.channels; pDescriptorPlayback->sampleRate = ss.rate; if (pDescriptorPlayback->format == ma_format_unknown || pDescriptorPlayback->channels == 0 || pDescriptorPlayback->sampleRate == 0) { ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Playback sample spec is invalid. Device unusable by miniaudio. format=%s, channels=%d, sampleRate=%d.\n", ma_get_format_name(pDescriptorPlayback->format), pDescriptorPlayback->channels, pDescriptorPlayback->sampleRate); result = MA_ERROR; goto on_error4; } /* Internal channel map. */ /* Bug in PipeWire. There have been reports that PipeWire is returning AUX channels when reporting the channel map. To somewhat workaround this, I'm hacking in a hard coded channel map for mono and stereo. In this case it should be safe to assume mono = MONO and stereo = LEFT/RIGHT. For all other channel counts we need to just put up with whatever PipeWire reports and hope it gets fixed sooner than later. I might remove this hack later. */ if (pDescriptorPlayback->channels > 2) { pActualCMap = ((ma_pa_stream_get_channel_map_proc)pDevice->pContext->pulse.pa_stream_get_channel_map)((ma_pa_stream*)pDevice->pulse.pStreamPlayback); if (pActualCMap != NULL) { cmap = *pActualCMap; } for (iChannel = 0; iChannel < pDescriptorPlayback->channels; ++iChannel) { pDescriptorPlayback->channelMap[iChannel] = ma_channel_position_from_pulse(cmap.map[iChannel]); } } else { /* Hack for mono and stereo. */ if (pDescriptorPlayback->channels == 1) { pDescriptorPlayback->channelMap[0] = MA_CHANNEL_MONO; } else if (pDescriptorPlayback->channels == 2) { pDescriptorPlayback->channelMap[0] = MA_CHANNEL_FRONT_LEFT; pDescriptorPlayback->channelMap[1] = MA_CHANNEL_FRONT_RIGHT; } else { MA_ASSERT(MA_FALSE); /* Should never hit this. */ } } /* Buffer. */ pActualAttr = ((ma_pa_stream_get_buffer_attr_proc)pDevice->pContext->pulse.pa_stream_get_buffer_attr)((ma_pa_stream*)pDevice->pulse.pStreamPlayback); if (pActualAttr != NULL) { attr = *pActualAttr; } if (attr.tlength > 0) { pDescriptorPlayback->periodCount = ma_max(attr.maxlength / attr.tlength, 1); } else { pDescriptorPlayback->periodCount = 1; } pDescriptorPlayback->periodSizeInFrames = attr.maxlength / ma_get_bytes_per_frame(pDescriptorPlayback->format, pDescriptorPlayback->channels) / pDescriptorPlayback->periodCount; ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] Playback actual attr: maxlength=%d, tlength=%d, prebuf=%d, minreq=%d, fragsize=%d; internalPeriodSizeInFrames=%d\n", attr.maxlength, attr.tlength, attr.prebuf, attr.minreq, attr.fragsize, pDescriptorPlayback->periodSizeInFrames); } /* We need a ring buffer for handling duplex mode. We can use the main duplex ring buffer in the main part of the ma_device struct. We cannot, however, depend on ma_device_init() initializing this for us later on because that will only do it if it's a fully asynchronous backend - i.e. the onDeviceDataLoop callback is NULL, which is not the case for PulseAudio. */ if (pConfig->deviceType == ma_device_type_duplex) { ma_format rbFormat = (format != ma_format_unknown) ? format : pDescriptorCapture->format; ma_uint32 rbChannels = (channels > 0) ? channels : pDescriptorCapture->channels; ma_uint32 rbSampleRate = (sampleRate > 0) ? sampleRate : pDescriptorCapture->sampleRate; result = ma_duplex_rb_init(rbFormat, rbChannels, rbSampleRate, pDescriptorCapture->sampleRate, pDescriptorCapture->periodSizeInFrames, &pDevice->pContext->allocationCallbacks, &pDevice->duplexRB); if (result != MA_SUCCESS) { ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to initialize ring buffer. %s.\n", ma_result_description(result)); goto on_error4; } } return MA_SUCCESS; on_error4: if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { ((ma_pa_stream_disconnect_proc)pDevice->pContext->pulse.pa_stream_disconnect)((ma_pa_stream*)pDevice->pulse.pStreamPlayback); } on_error3: if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { ((ma_pa_stream_unref_proc)pDevice->pContext->pulse.pa_stream_unref)((ma_pa_stream*)pDevice->pulse.pStreamPlayback); } on_error2: if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { ((ma_pa_stream_disconnect_proc)pDevice->pContext->pulse.pa_stream_disconnect)((ma_pa_stream*)pDevice->pulse.pStreamCapture); } on_error1: if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { ((ma_pa_stream_unref_proc)pDevice->pContext->pulse.pa_stream_unref)((ma_pa_stream*)pDevice->pulse.pStreamCapture); } on_error0: return result; } static void ma_pulse_operation_complete_callback(ma_pa_stream* pStream, int success, void* pUserData) { ma_bool32* pIsSuccessful = (ma_bool32*)pUserData; MA_ASSERT(pIsSuccessful != NULL); *pIsSuccessful = (ma_bool32)success; (void)pStream; /* Unused. */ } static ma_result ma_device__cork_stream__pulse(ma_device* pDevice, ma_device_type deviceType, int cork) { ma_context* pContext = pDevice->pContext; ma_bool32 wasSuccessful; ma_pa_stream* pStream; ma_pa_operation* pOP; ma_result result; /* This should not be called with a duplex device type. */ if (deviceType == ma_device_type_duplex) { return MA_INVALID_ARGS; } wasSuccessful = MA_FALSE; pStream = (ma_pa_stream*)((deviceType == ma_device_type_capture) ? pDevice->pulse.pStreamCapture : pDevice->pulse.pStreamPlayback); MA_ASSERT(pStream != NULL); pOP = ((ma_pa_stream_cork_proc)pContext->pulse.pa_stream_cork)(pStream, cork, ma_pulse_operation_complete_callback, &wasSuccessful); if (pOP == NULL) { ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to cork PulseAudio stream."); return MA_ERROR; } result = ma_wait_for_operation_and_unref__pulse(pDevice->pContext, pDevice->pulse.pMainLoop, pOP); if (result != MA_SUCCESS) { ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] An error occurred while waiting for the PulseAudio stream to cork."); return result; } if (!wasSuccessful) { ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to %s PulseAudio stream.", (cork) ? "stop" : "start"); return MA_ERROR; } return MA_SUCCESS; } static ma_result ma_device_start__pulse(ma_device* pDevice) { ma_result result; MA_ASSERT(pDevice != NULL); if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { result = ma_device__cork_stream__pulse(pDevice, ma_device_type_capture, 0); if (result != MA_SUCCESS) { return result; } } if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { /* We need to fill some data before uncorking. Not doing this will result in the write callback never getting fired. We're not going to abort if writing fails because I still want the device to get uncorked. */ ma_device_write_to_stream__pulse(pDevice, (ma_pa_stream*)(pDevice->pulse.pStreamPlayback), NULL); /* No need to check the result here. Always want to fall through an uncork.*/ result = ma_device__cork_stream__pulse(pDevice, ma_device_type_playback, 0); if (result != MA_SUCCESS) { return result; } } return MA_SUCCESS; } static ma_result ma_device_stop__pulse(ma_device* pDevice) { ma_result result; MA_ASSERT(pDevice != NULL); if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { result = ma_device__cork_stream__pulse(pDevice, ma_device_type_capture, 1); if (result != MA_SUCCESS) { return result; } } if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { /* Ideally we would drain the device here, but there's been cases where PulseAudio seems to be broken on some systems to the point where no audio processing seems to happen. When this happens, draining never completes and we get stuck here. For now I'm disabling draining of the device so we don't just freeze the application. */ #if 0 ma_pa_operation* pOP = ((ma_pa_stream_drain_proc)pDevice->pContext->pulse.pa_stream_drain)((ma_pa_stream*)pDevice->pulse.pStreamPlayback, ma_pulse_operation_complete_callback, &wasSuccessful); ma_wait_for_operation_and_unref__pulse(pDevice->pContext, pDevice->pulse.pMainLoop, pOP); #endif result = ma_device__cork_stream__pulse(pDevice, ma_device_type_playback, 1); if (result != MA_SUCCESS) { return result; } } return MA_SUCCESS; } static ma_result ma_device_data_loop__pulse(ma_device* pDevice) { int resultPA; MA_ASSERT(pDevice != NULL); /* NOTE: Don't start the device here. It'll be done at a higher level. */ /* All data is handled through callbacks. All we need to do is iterate over the main loop and let the callbacks deal with it. */ while (ma_device_get_state(pDevice) == ma_device_state_started) { resultPA = ((ma_pa_mainloop_iterate_proc)pDevice->pContext->pulse.pa_mainloop_iterate)((ma_pa_mainloop*)pDevice->pulse.pMainLoop, 1, NULL); if (resultPA < 0) { break; } } /* NOTE: Don't stop the device here. It'll be done at a higher level. */ return MA_SUCCESS; } static ma_result ma_device_data_loop_wakeup__pulse(ma_device* pDevice) { MA_ASSERT(pDevice != NULL); ((ma_pa_mainloop_wakeup_proc)pDevice->pContext->pulse.pa_mainloop_wakeup)((ma_pa_mainloop*)pDevice->pulse.pMainLoop); return MA_SUCCESS; } static ma_result ma_context_uninit__pulse(ma_context* pContext) { MA_ASSERT(pContext != NULL); MA_ASSERT(pContext->backend == ma_backend_pulseaudio); ((ma_pa_context_disconnect_proc)pContext->pulse.pa_context_disconnect)((ma_pa_context*)pContext->pulse.pPulseContext); ((ma_pa_context_unref_proc)pContext->pulse.pa_context_unref)((ma_pa_context*)pContext->pulse.pPulseContext); ((ma_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)((ma_pa_mainloop*)pContext->pulse.pMainLoop); ma_free(pContext->pulse.pServerName, &pContext->allocationCallbacks); ma_free(pContext->pulse.pApplicationName, &pContext->allocationCallbacks); #ifndef MA_NO_RUNTIME_LINKING ma_dlclose(pContext, pContext->pulse.pulseSO); #endif return MA_SUCCESS; } static ma_result ma_context_init__pulse(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks) { ma_result result; #ifndef MA_NO_RUNTIME_LINKING const char* libpulseNames[] = { "libpulse.so", "libpulse.so.0" }; size_t i; for (i = 0; i < ma_countof(libpulseNames); ++i) { pContext->pulse.pulseSO = ma_dlopen(pContext, libpulseNames[i]); if (pContext->pulse.pulseSO != NULL) { break; } } if (pContext->pulse.pulseSO == NULL) { return MA_NO_BACKEND; } pContext->pulse.pa_mainloop_new = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_mainloop_new"); pContext->pulse.pa_mainloop_free = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_mainloop_free"); pContext->pulse.pa_mainloop_quit = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_mainloop_quit"); pContext->pulse.pa_mainloop_get_api = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_mainloop_get_api"); pContext->pulse.pa_mainloop_iterate = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_mainloop_iterate"); pContext->pulse.pa_mainloop_wakeup = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_mainloop_wakeup"); pContext->pulse.pa_threaded_mainloop_new = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_threaded_mainloop_new"); pContext->pulse.pa_threaded_mainloop_free = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_threaded_mainloop_free"); pContext->pulse.pa_threaded_mainloop_start = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_threaded_mainloop_start"); pContext->pulse.pa_threaded_mainloop_stop = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_threaded_mainloop_stop"); pContext->pulse.pa_threaded_mainloop_lock = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_threaded_mainloop_lock"); pContext->pulse.pa_threaded_mainloop_unlock = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_threaded_mainloop_unlock"); pContext->pulse.pa_threaded_mainloop_wait = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_threaded_mainloop_wait"); pContext->pulse.pa_threaded_mainloop_signal = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_threaded_mainloop_signal"); pContext->pulse.pa_threaded_mainloop_accept = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_threaded_mainloop_accept"); pContext->pulse.pa_threaded_mainloop_get_retval = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_threaded_mainloop_get_retval"); pContext->pulse.pa_threaded_mainloop_get_api = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_threaded_mainloop_get_api"); pContext->pulse.pa_threaded_mainloop_in_thread = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_threaded_mainloop_in_thread"); pContext->pulse.pa_threaded_mainloop_set_name = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_threaded_mainloop_set_name"); pContext->pulse.pa_context_new = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_context_new"); pContext->pulse.pa_context_unref = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_context_unref"); pContext->pulse.pa_context_connect = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_context_connect"); pContext->pulse.pa_context_disconnect = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_context_disconnect"); pContext->pulse.pa_context_set_state_callback = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_context_set_state_callback"); pContext->pulse.pa_context_get_state = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_context_get_state"); pContext->pulse.pa_context_get_sink_info_list = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_context_get_sink_info_list"); pContext->pulse.pa_context_get_source_info_list = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_context_get_source_info_list"); pContext->pulse.pa_context_get_sink_info_by_name = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_context_get_sink_info_by_name"); pContext->pulse.pa_context_get_source_info_by_name = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_context_get_source_info_by_name"); pContext->pulse.pa_operation_unref = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_operation_unref"); pContext->pulse.pa_operation_get_state = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_operation_get_state"); pContext->pulse.pa_channel_map_init_extend = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_channel_map_init_extend"); pContext->pulse.pa_channel_map_valid = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_channel_map_valid"); pContext->pulse.pa_channel_map_compatible = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_channel_map_compatible"); pContext->pulse.pa_stream_new = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_new"); pContext->pulse.pa_stream_unref = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_unref"); pContext->pulse.pa_stream_connect_playback = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_connect_playback"); pContext->pulse.pa_stream_connect_record = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_connect_record"); pContext->pulse.pa_stream_disconnect = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_disconnect"); pContext->pulse.pa_stream_get_state = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_get_state"); pContext->pulse.pa_stream_get_sample_spec = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_get_sample_spec"); pContext->pulse.pa_stream_get_channel_map = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_get_channel_map"); pContext->pulse.pa_stream_get_buffer_attr = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_get_buffer_attr"); pContext->pulse.pa_stream_set_buffer_attr = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_set_buffer_attr"); pContext->pulse.pa_stream_get_device_name = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_get_device_name"); pContext->pulse.pa_stream_set_write_callback = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_set_write_callback"); pContext->pulse.pa_stream_set_read_callback = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_set_read_callback"); pContext->pulse.pa_stream_set_suspended_callback = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_set_suspended_callback"); pContext->pulse.pa_stream_set_moved_callback = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_set_moved_callback"); pContext->pulse.pa_stream_is_suspended = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_is_suspended"); pContext->pulse.pa_stream_flush = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_flush"); pContext->pulse.pa_stream_drain = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_drain"); pContext->pulse.pa_stream_is_corked = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_is_corked"); pContext->pulse.pa_stream_cork = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_cork"); pContext->pulse.pa_stream_trigger = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_trigger"); pContext->pulse.pa_stream_begin_write = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_begin_write"); pContext->pulse.pa_stream_write = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_write"); pContext->pulse.pa_stream_peek = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_peek"); pContext->pulse.pa_stream_drop = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_drop"); pContext->pulse.pa_stream_writable_size = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_writable_size"); pContext->pulse.pa_stream_readable_size = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_readable_size"); #else /* This strange assignment system is just for type safety. */ ma_pa_mainloop_new_proc _pa_mainloop_new = pa_mainloop_new; ma_pa_mainloop_free_proc _pa_mainloop_free = pa_mainloop_free; ma_pa_mainloop_quit_proc _pa_mainloop_quit = pa_mainloop_quit; ma_pa_mainloop_get_api_proc _pa_mainloop_get_api = pa_mainloop_get_api; ma_pa_mainloop_iterate_proc _pa_mainloop_iterate = pa_mainloop_iterate; ma_pa_mainloop_wakeup_proc _pa_mainloop_wakeup = pa_mainloop_wakeup; ma_pa_threaded_mainloop_new_proc _pa_threaded_mainloop_new = pa_threaded_mainloop_new; ma_pa_threaded_mainloop_free_proc _pa_threaded_mainloop_free = pa_threaded_mainloop_free; ma_pa_threaded_mainloop_start_proc _pa_threaded_mainloop_start = pa_threaded_mainloop_start; ma_pa_threaded_mainloop_stop_proc _pa_threaded_mainloop_stop = pa_threaded_mainloop_stop; ma_pa_threaded_mainloop_lock_proc _pa_threaded_mainloop_lock = pa_threaded_mainloop_lock; ma_pa_threaded_mainloop_unlock_proc _pa_threaded_mainloop_unlock = pa_threaded_mainloop_unlock; ma_pa_threaded_mainloop_wait_proc _pa_threaded_mainloop_wait = pa_threaded_mainloop_wait; ma_pa_threaded_mainloop_signal_proc _pa_threaded_mainloop_signal = pa_threaded_mainloop_signal; ma_pa_threaded_mainloop_accept_proc _pa_threaded_mainloop_accept = pa_threaded_mainloop_accept; ma_pa_threaded_mainloop_get_retval_proc _pa_threaded_mainloop_get_retval = pa_threaded_mainloop_get_retval; ma_pa_threaded_mainloop_get_api_proc _pa_threaded_mainloop_get_api = pa_threaded_mainloop_get_api; ma_pa_threaded_mainloop_in_thread_proc _pa_threaded_mainloop_in_thread = pa_threaded_mainloop_in_thread; ma_pa_threaded_mainloop_set_name_proc _pa_threaded_mainloop_set_name = pa_threaded_mainloop_set_name; ma_pa_context_new_proc _pa_context_new = pa_context_new; ma_pa_context_unref_proc _pa_context_unref = pa_context_unref; ma_pa_context_connect_proc _pa_context_connect = pa_context_connect; ma_pa_context_disconnect_proc _pa_context_disconnect = pa_context_disconnect; ma_pa_context_set_state_callback_proc _pa_context_set_state_callback = pa_context_set_state_callback; ma_pa_context_get_state_proc _pa_context_get_state = pa_context_get_state; ma_pa_context_get_sink_info_list_proc _pa_context_get_sink_info_list = pa_context_get_sink_info_list; ma_pa_context_get_source_info_list_proc _pa_context_get_source_info_list = pa_context_get_source_info_list; ma_pa_context_get_sink_info_by_name_proc _pa_context_get_sink_info_by_name = pa_context_get_sink_info_by_name; ma_pa_context_get_source_info_by_name_proc _pa_context_get_source_info_by_name= pa_context_get_source_info_by_name; ma_pa_operation_unref_proc _pa_operation_unref = pa_operation_unref; ma_pa_operation_get_state_proc _pa_operation_get_state = pa_operation_get_state; ma_pa_channel_map_init_extend_proc _pa_channel_map_init_extend = pa_channel_map_init_extend; ma_pa_channel_map_valid_proc _pa_channel_map_valid = pa_channel_map_valid; ma_pa_channel_map_compatible_proc _pa_channel_map_compatible = pa_channel_map_compatible; ma_pa_stream_new_proc _pa_stream_new = pa_stream_new; ma_pa_stream_unref_proc _pa_stream_unref = pa_stream_unref; ma_pa_stream_connect_playback_proc _pa_stream_connect_playback = pa_stream_connect_playback; ma_pa_stream_connect_record_proc _pa_stream_connect_record = pa_stream_connect_record; ma_pa_stream_disconnect_proc _pa_stream_disconnect = pa_stream_disconnect; ma_pa_stream_get_state_proc _pa_stream_get_state = pa_stream_get_state; ma_pa_stream_get_sample_spec_proc _pa_stream_get_sample_spec = pa_stream_get_sample_spec; ma_pa_stream_get_channel_map_proc _pa_stream_get_channel_map = pa_stream_get_channel_map; ma_pa_stream_get_buffer_attr_proc _pa_stream_get_buffer_attr = pa_stream_get_buffer_attr; ma_pa_stream_set_buffer_attr_proc _pa_stream_set_buffer_attr = pa_stream_set_buffer_attr; ma_pa_stream_get_device_name_proc _pa_stream_get_device_name = pa_stream_get_device_name; ma_pa_stream_set_write_callback_proc _pa_stream_set_write_callback = pa_stream_set_write_callback; ma_pa_stream_set_read_callback_proc _pa_stream_set_read_callback = pa_stream_set_read_callback; ma_pa_stream_set_suspended_callback_proc _pa_stream_set_suspended_callback = pa_stream_set_suspended_callback; ma_pa_stream_set_moved_callback_proc _pa_stream_set_moved_callback = pa_stream_set_moved_callback; ma_pa_stream_is_suspended_proc _pa_stream_is_suspended = pa_stream_is_suspended; ma_pa_stream_flush_proc _pa_stream_flush = pa_stream_flush; ma_pa_stream_drain_proc _pa_stream_drain = pa_stream_drain; ma_pa_stream_is_corked_proc _pa_stream_is_corked = pa_stream_is_corked; ma_pa_stream_cork_proc _pa_stream_cork = pa_stream_cork; ma_pa_stream_trigger_proc _pa_stream_trigger = pa_stream_trigger; ma_pa_stream_begin_write_proc _pa_stream_begin_write = pa_stream_begin_write; ma_pa_stream_write_proc _pa_stream_write = pa_stream_write; ma_pa_stream_peek_proc _pa_stream_peek = pa_stream_peek; ma_pa_stream_drop_proc _pa_stream_drop = pa_stream_drop; ma_pa_stream_writable_size_proc _pa_stream_writable_size = pa_stream_writable_size; ma_pa_stream_readable_size_proc _pa_stream_readable_size = pa_stream_readable_size; pContext->pulse.pa_mainloop_new = (ma_proc)_pa_mainloop_new; pContext->pulse.pa_mainloop_free = (ma_proc)_pa_mainloop_free; pContext->pulse.pa_mainloop_quit = (ma_proc)_pa_mainloop_quit; pContext->pulse.pa_mainloop_get_api = (ma_proc)_pa_mainloop_get_api; pContext->pulse.pa_mainloop_iterate = (ma_proc)_pa_mainloop_iterate; pContext->pulse.pa_mainloop_wakeup = (ma_proc)_pa_mainloop_wakeup; pContext->pulse.pa_threaded_mainloop_new = (ma_proc)_pa_threaded_mainloop_new; pContext->pulse.pa_threaded_mainloop_free = (ma_proc)_pa_threaded_mainloop_free; pContext->pulse.pa_threaded_mainloop_start = (ma_proc)_pa_threaded_mainloop_start; pContext->pulse.pa_threaded_mainloop_stop = (ma_proc)_pa_threaded_mainloop_stop; pContext->pulse.pa_threaded_mainloop_lock = (ma_proc)_pa_threaded_mainloop_lock; pContext->pulse.pa_threaded_mainloop_unlock = (ma_proc)_pa_threaded_mainloop_unlock; pContext->pulse.pa_threaded_mainloop_wait = (ma_proc)_pa_threaded_mainloop_wait; pContext->pulse.pa_threaded_mainloop_signal = (ma_proc)_pa_threaded_mainloop_signal; pContext->pulse.pa_threaded_mainloop_accept = (ma_proc)_pa_threaded_mainloop_accept; pContext->pulse.pa_threaded_mainloop_get_retval = (ma_proc)_pa_threaded_mainloop_get_retval; pContext->pulse.pa_threaded_mainloop_get_api = (ma_proc)_pa_threaded_mainloop_get_api; pContext->pulse.pa_threaded_mainloop_in_thread = (ma_proc)_pa_threaded_mainloop_in_thread; pContext->pulse.pa_threaded_mainloop_set_name = (ma_proc)_pa_threaded_mainloop_set_name; pContext->pulse.pa_context_new = (ma_proc)_pa_context_new; pContext->pulse.pa_context_unref = (ma_proc)_pa_context_unref; pContext->pulse.pa_context_connect = (ma_proc)_pa_context_connect; pContext->pulse.pa_context_disconnect = (ma_proc)_pa_context_disconnect; pContext->pulse.pa_context_set_state_callback = (ma_proc)_pa_context_set_state_callback; pContext->pulse.pa_context_get_state = (ma_proc)_pa_context_get_state; pContext->pulse.pa_context_get_sink_info_list = (ma_proc)_pa_context_get_sink_info_list; pContext->pulse.pa_context_get_source_info_list = (ma_proc)_pa_context_get_source_info_list; pContext->pulse.pa_context_get_sink_info_by_name = (ma_proc)_pa_context_get_sink_info_by_name; pContext->pulse.pa_context_get_source_info_by_name = (ma_proc)_pa_context_get_source_info_by_name; pContext->pulse.pa_operation_unref = (ma_proc)_pa_operation_unref; pContext->pulse.pa_operation_get_state = (ma_proc)_pa_operation_get_state; pContext->pulse.pa_channel_map_init_extend = (ma_proc)_pa_channel_map_init_extend; pContext->pulse.pa_channel_map_valid = (ma_proc)_pa_channel_map_valid; pContext->pulse.pa_channel_map_compatible = (ma_proc)_pa_channel_map_compatible; pContext->pulse.pa_stream_new = (ma_proc)_pa_stream_new; pContext->pulse.pa_stream_unref = (ma_proc)_pa_stream_unref; pContext->pulse.pa_stream_connect_playback = (ma_proc)_pa_stream_connect_playback; pContext->pulse.pa_stream_connect_record = (ma_proc)_pa_stream_connect_record; pContext->pulse.pa_stream_disconnect = (ma_proc)_pa_stream_disconnect; pContext->pulse.pa_stream_get_state = (ma_proc)_pa_stream_get_state; pContext->pulse.pa_stream_get_sample_spec = (ma_proc)_pa_stream_get_sample_spec; pContext->pulse.pa_stream_get_channel_map = (ma_proc)_pa_stream_get_channel_map; pContext->pulse.pa_stream_get_buffer_attr = (ma_proc)_pa_stream_get_buffer_attr; pContext->pulse.pa_stream_set_buffer_attr = (ma_proc)_pa_stream_set_buffer_attr; pContext->pulse.pa_stream_get_device_name = (ma_proc)_pa_stream_get_device_name; pContext->pulse.pa_stream_set_write_callback = (ma_proc)_pa_stream_set_write_callback; pContext->pulse.pa_stream_set_read_callback = (ma_proc)_pa_stream_set_read_callback; pContext->pulse.pa_stream_set_suspended_callback = (ma_proc)_pa_stream_set_suspended_callback; pContext->pulse.pa_stream_set_moved_callback = (ma_proc)_pa_stream_set_moved_callback; pContext->pulse.pa_stream_is_suspended = (ma_proc)_pa_stream_is_suspended; pContext->pulse.pa_stream_flush = (ma_proc)_pa_stream_flush; pContext->pulse.pa_stream_drain = (ma_proc)_pa_stream_drain; pContext->pulse.pa_stream_is_corked = (ma_proc)_pa_stream_is_corked; pContext->pulse.pa_stream_cork = (ma_proc)_pa_stream_cork; pContext->pulse.pa_stream_trigger = (ma_proc)_pa_stream_trigger; pContext->pulse.pa_stream_begin_write = (ma_proc)_pa_stream_begin_write; pContext->pulse.pa_stream_write = (ma_proc)_pa_stream_write; pContext->pulse.pa_stream_peek = (ma_proc)_pa_stream_peek; pContext->pulse.pa_stream_drop = (ma_proc)_pa_stream_drop; pContext->pulse.pa_stream_writable_size = (ma_proc)_pa_stream_writable_size; pContext->pulse.pa_stream_readable_size = (ma_proc)_pa_stream_readable_size; #endif /* We need to make a copy of the application and server names so we can pass them to the pa_context of each device. */ pContext->pulse.pApplicationName = ma_copy_string(pConfig->pulse.pApplicationName, &pContext->allocationCallbacks); if (pContext->pulse.pApplicationName == NULL && pConfig->pulse.pApplicationName != NULL) { return MA_OUT_OF_MEMORY; } pContext->pulse.pServerName = ma_copy_string(pConfig->pulse.pServerName, &pContext->allocationCallbacks); if (pContext->pulse.pServerName == NULL && pConfig->pulse.pServerName != NULL) { ma_free(pContext->pulse.pApplicationName, &pContext->allocationCallbacks); return MA_OUT_OF_MEMORY; } result = ma_init_pa_mainloop_and_pa_context__pulse(pContext, pConfig->pulse.pApplicationName, pConfig->pulse.pServerName, pConfig->pulse.tryAutoSpawn, &pContext->pulse.pMainLoop, &pContext->pulse.pPulseContext); if (result != MA_SUCCESS) { ma_free(pContext->pulse.pServerName, &pContext->allocationCallbacks); ma_free(pContext->pulse.pApplicationName, &pContext->allocationCallbacks); #ifndef MA_NO_RUNTIME_LINKING ma_dlclose(pContext, pContext->pulse.pulseSO); #endif return result; } /* With pa_mainloop we run a synchronous backend, but we implement our own main loop. */ pCallbacks->onContextInit = ma_context_init__pulse; pCallbacks->onContextUninit = ma_context_uninit__pulse; pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__pulse; pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__pulse; pCallbacks->onDeviceInit = ma_device_init__pulse; pCallbacks->onDeviceUninit = ma_device_uninit__pulse; pCallbacks->onDeviceStart = ma_device_start__pulse; pCallbacks->onDeviceStop = ma_device_stop__pulse; pCallbacks->onDeviceRead = NULL; /* Not used because we're implementing onDeviceDataLoop. */ pCallbacks->onDeviceWrite = NULL; /* Not used because we're implementing onDeviceDataLoop. */ pCallbacks->onDeviceDataLoop = ma_device_data_loop__pulse; pCallbacks->onDeviceDataLoopWakeup = ma_device_data_loop_wakeup__pulse; return MA_SUCCESS; } #endif /****************************************************************************** JACK Backend ******************************************************************************/ #ifdef MA_HAS_JACK /* It is assumed jack.h is available when compile-time linking is being used. */ #ifdef MA_NO_RUNTIME_LINKING #include typedef jack_nframes_t ma_jack_nframes_t; typedef jack_options_t ma_jack_options_t; typedef jack_status_t ma_jack_status_t; typedef jack_client_t ma_jack_client_t; typedef jack_port_t ma_jack_port_t; typedef JackProcessCallback ma_JackProcessCallback; typedef JackBufferSizeCallback ma_JackBufferSizeCallback; typedef JackShutdownCallback ma_JackShutdownCallback; #define MA_JACK_DEFAULT_AUDIO_TYPE JACK_DEFAULT_AUDIO_TYPE #define ma_JackNoStartServer JackNoStartServer #define ma_JackPortIsInput JackPortIsInput #define ma_JackPortIsOutput JackPortIsOutput #define ma_JackPortIsPhysical JackPortIsPhysical #else typedef ma_uint32 ma_jack_nframes_t; typedef int ma_jack_options_t; typedef int ma_jack_status_t; typedef struct ma_jack_client_t ma_jack_client_t; typedef struct ma_jack_port_t ma_jack_port_t; typedef int (* ma_JackProcessCallback) (ma_jack_nframes_t nframes, void* arg); typedef int (* ma_JackBufferSizeCallback)(ma_jack_nframes_t nframes, void* arg); typedef void (* ma_JackShutdownCallback) (void* arg); #define MA_JACK_DEFAULT_AUDIO_TYPE "32 bit float mono audio" #define ma_JackNoStartServer 1 #define ma_JackPortIsInput 1 #define ma_JackPortIsOutput 2 #define ma_JackPortIsPhysical 4 #endif typedef ma_jack_client_t* (* ma_jack_client_open_proc) (const char* client_name, ma_jack_options_t options, ma_jack_status_t* status, ...); typedef int (* ma_jack_client_close_proc) (ma_jack_client_t* client); typedef int (* ma_jack_client_name_size_proc) (void); typedef int (* ma_jack_set_process_callback_proc) (ma_jack_client_t* client, ma_JackProcessCallback process_callback, void* arg); typedef int (* ma_jack_set_buffer_size_callback_proc)(ma_jack_client_t* client, ma_JackBufferSizeCallback bufsize_callback, void* arg); typedef void (* ma_jack_on_shutdown_proc) (ma_jack_client_t* client, ma_JackShutdownCallback function, void* arg); typedef ma_jack_nframes_t (* ma_jack_get_sample_rate_proc) (ma_jack_client_t* client); typedef ma_jack_nframes_t (* ma_jack_get_buffer_size_proc) (ma_jack_client_t* client); typedef const char** (* ma_jack_get_ports_proc) (ma_jack_client_t* client, const char* port_name_pattern, const char* type_name_pattern, unsigned long flags); typedef int (* ma_jack_activate_proc) (ma_jack_client_t* client); typedef int (* ma_jack_deactivate_proc) (ma_jack_client_t* client); typedef int (* ma_jack_connect_proc) (ma_jack_client_t* client, const char* source_port, const char* destination_port); typedef ma_jack_port_t* (* ma_jack_port_register_proc) (ma_jack_client_t* client, const char* port_name, const char* port_type, unsigned long flags, unsigned long buffer_size); typedef const char* (* ma_jack_port_name_proc) (const ma_jack_port_t* port); typedef void* (* ma_jack_port_get_buffer_proc) (ma_jack_port_t* port, ma_jack_nframes_t nframes); typedef void (* ma_jack_free_proc) (void* ptr); static ma_result ma_context_open_client__jack(ma_context* pContext, ma_jack_client_t** ppClient) { size_t maxClientNameSize; char clientName[256]; ma_jack_status_t status; ma_jack_client_t* pClient; MA_ASSERT(pContext != NULL); MA_ASSERT(ppClient != NULL); if (ppClient) { *ppClient = NULL; } maxClientNameSize = ((ma_jack_client_name_size_proc)pContext->jack.jack_client_name_size)(); /* Includes null terminator. */ ma_strncpy_s(clientName, ma_min(sizeof(clientName), maxClientNameSize), (pContext->jack.pClientName != NULL) ? pContext->jack.pClientName : "miniaudio", (size_t)-1); pClient = ((ma_jack_client_open_proc)pContext->jack.jack_client_open)(clientName, (pContext->jack.tryStartServer) ? 0 : ma_JackNoStartServer, &status, NULL); if (pClient == NULL) { return MA_FAILED_TO_OPEN_BACKEND_DEVICE; } if (ppClient) { *ppClient = pClient; } return MA_SUCCESS; } static ma_result ma_context_enumerate_devices__jack(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData) { ma_bool32 cbResult = MA_TRUE; MA_ASSERT(pContext != NULL); MA_ASSERT(callback != NULL); /* Playback. */ if (cbResult) { ma_device_info deviceInfo; MA_ZERO_OBJECT(&deviceInfo); ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); deviceInfo.isDefault = MA_TRUE; /* JACK only uses default devices. */ cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData); } /* Capture. */ if (cbResult) { ma_device_info deviceInfo; MA_ZERO_OBJECT(&deviceInfo); ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); deviceInfo.isDefault = MA_TRUE; /* JACK only uses default devices. */ cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData); } (void)cbResult; /* For silencing a static analysis warning. */ return MA_SUCCESS; } static ma_result ma_context_get_device_info__jack(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo) { ma_jack_client_t* pClient; ma_result result; const char** ppPorts; MA_ASSERT(pContext != NULL); if (pDeviceID != NULL && pDeviceID->jack != 0) { return MA_NO_DEVICE; /* Don't know the device. */ } /* Name / Description */ if (deviceType == ma_device_type_playback) { ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); } else { ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); } /* Jack only uses default devices. */ pDeviceInfo->isDefault = MA_TRUE; /* Jack only supports f32 and has a specific channel count and sample rate. */ pDeviceInfo->nativeDataFormats[0].format = ma_format_f32; /* The channel count and sample rate can only be determined by opening the device. */ result = ma_context_open_client__jack(pContext, &pClient); if (result != MA_SUCCESS) { ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[JACK] Failed to open client."); return result; } pDeviceInfo->nativeDataFormats[0].sampleRate = ((ma_jack_get_sample_rate_proc)pContext->jack.jack_get_sample_rate)((ma_jack_client_t*)pClient); pDeviceInfo->nativeDataFormats[0].channels = 0; ppPorts = ((ma_jack_get_ports_proc)pContext->jack.jack_get_ports)((ma_jack_client_t*)pClient, NULL, MA_JACK_DEFAULT_AUDIO_TYPE, ma_JackPortIsPhysical | ((deviceType == ma_device_type_playback) ? ma_JackPortIsInput : ma_JackPortIsOutput)); if (ppPorts == NULL) { ((ma_jack_client_close_proc)pContext->jack.jack_client_close)((ma_jack_client_t*)pClient); ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[JACK] Failed to query physical ports."); return MA_FAILED_TO_OPEN_BACKEND_DEVICE; } while (ppPorts[pDeviceInfo->nativeDataFormats[0].channels] != NULL) { pDeviceInfo->nativeDataFormats[0].channels += 1; } pDeviceInfo->nativeDataFormats[0].flags = 0; pDeviceInfo->nativeDataFormatCount = 1; ((ma_jack_free_proc)pContext->jack.jack_free)((void*)ppPorts); ((ma_jack_client_close_proc)pContext->jack.jack_client_close)((ma_jack_client_t*)pClient); (void)pContext; return MA_SUCCESS; } static ma_result ma_device_uninit__jack(ma_device* pDevice) { ma_context* pContext; MA_ASSERT(pDevice != NULL); pContext = pDevice->pContext; MA_ASSERT(pContext != NULL); if (pDevice->jack.pClient != NULL) { ((ma_jack_client_close_proc)pContext->jack.jack_client_close)((ma_jack_client_t*)pDevice->jack.pClient); } if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { ma_free(pDevice->jack.pIntermediaryBufferCapture, &pDevice->pContext->allocationCallbacks); ma_free(pDevice->jack.ppPortsCapture, &pDevice->pContext->allocationCallbacks); } if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { ma_free(pDevice->jack.pIntermediaryBufferPlayback, &pDevice->pContext->allocationCallbacks); ma_free(pDevice->jack.ppPortsPlayback, &pDevice->pContext->allocationCallbacks); } return MA_SUCCESS; } static void ma_device__jack_shutdown_callback(void* pUserData) { /* JACK died. Stop the device. */ ma_device* pDevice = (ma_device*)pUserData; MA_ASSERT(pDevice != NULL); ma_device_stop(pDevice); } static int ma_device__jack_buffer_size_callback(ma_jack_nframes_t frameCount, void* pUserData) { ma_device* pDevice = (ma_device*)pUserData; MA_ASSERT(pDevice != NULL); if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { size_t newBufferSize = frameCount * (pDevice->capture.internalChannels * ma_get_bytes_per_sample(pDevice->capture.internalFormat)); float* pNewBuffer = (float*)ma_calloc(newBufferSize, &pDevice->pContext->allocationCallbacks); if (pNewBuffer == NULL) { return MA_OUT_OF_MEMORY; } ma_free(pDevice->jack.pIntermediaryBufferCapture, &pDevice->pContext->allocationCallbacks); pDevice->jack.pIntermediaryBufferCapture = pNewBuffer; pDevice->playback.internalPeriodSizeInFrames = frameCount; } if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { size_t newBufferSize = frameCount * (pDevice->playback.internalChannels * ma_get_bytes_per_sample(pDevice->playback.internalFormat)); float* pNewBuffer = (float*)ma_calloc(newBufferSize, &pDevice->pContext->allocationCallbacks); if (pNewBuffer == NULL) { return MA_OUT_OF_MEMORY; } ma_free(pDevice->jack.pIntermediaryBufferPlayback, &pDevice->pContext->allocationCallbacks); pDevice->jack.pIntermediaryBufferPlayback = pNewBuffer; pDevice->playback.internalPeriodSizeInFrames = frameCount; } return 0; } static int ma_device__jack_process_callback(ma_jack_nframes_t frameCount, void* pUserData) { ma_device* pDevice; ma_context* pContext; ma_uint32 iChannel; pDevice = (ma_device*)pUserData; MA_ASSERT(pDevice != NULL); pContext = pDevice->pContext; MA_ASSERT(pContext != NULL); if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { /* Channels need to be interleaved. */ for (iChannel = 0; iChannel < pDevice->capture.internalChannels; ++iChannel) { const float* pSrc = (const float*)((ma_jack_port_get_buffer_proc)pContext->jack.jack_port_get_buffer)((ma_jack_port_t*)pDevice->jack.ppPortsCapture[iChannel], frameCount); if (pSrc != NULL) { float* pDst = pDevice->jack.pIntermediaryBufferCapture + iChannel; ma_jack_nframes_t iFrame; for (iFrame = 0; iFrame < frameCount; ++iFrame) { *pDst = *pSrc; pDst += pDevice->capture.internalChannels; pSrc += 1; } } } ma_device_handle_backend_data_callback(pDevice, NULL, pDevice->jack.pIntermediaryBufferCapture, frameCount); } if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { ma_device_handle_backend_data_callback(pDevice, pDevice->jack.pIntermediaryBufferPlayback, NULL, frameCount); /* Channels need to be deinterleaved. */ for (iChannel = 0; iChannel < pDevice->playback.internalChannels; ++iChannel) { float* pDst = (float*)((ma_jack_port_get_buffer_proc)pContext->jack.jack_port_get_buffer)((ma_jack_port_t*)pDevice->jack.ppPortsPlayback[iChannel], frameCount); if (pDst != NULL) { const float* pSrc = pDevice->jack.pIntermediaryBufferPlayback + iChannel; ma_jack_nframes_t iFrame; for (iFrame = 0; iFrame < frameCount; ++iFrame) { *pDst = *pSrc; pDst += 1; pSrc += pDevice->playback.internalChannels; } } } } return 0; } static ma_result ma_device_init__jack(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture) { ma_result result; ma_uint32 periodSizeInFrames; MA_ASSERT(pConfig != NULL); MA_ASSERT(pDevice != NULL); if (pConfig->deviceType == ma_device_type_loopback) { ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Loopback mode not supported."); return MA_DEVICE_TYPE_NOT_SUPPORTED; } /* Only supporting default devices with JACK. */ if (((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pDescriptorPlayback->pDeviceID != NULL && pDescriptorPlayback->pDeviceID->jack != 0) || ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pDescriptorCapture->pDeviceID != NULL && pDescriptorCapture->pDeviceID->jack != 0)) { ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Only default devices are supported."); return MA_NO_DEVICE; } /* No exclusive mode with the JACK backend. */ if (((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pDescriptorPlayback->shareMode == ma_share_mode_exclusive) || ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pDescriptorCapture->shareMode == ma_share_mode_exclusive)) { ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Exclusive mode not supported."); return MA_SHARE_MODE_NOT_SUPPORTED; } /* Open the client. */ result = ma_context_open_client__jack(pDevice->pContext, (ma_jack_client_t**)&pDevice->jack.pClient); if (result != MA_SUCCESS) { ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to open client."); return result; } /* Callbacks. */ if (((ma_jack_set_process_callback_proc)pDevice->pContext->jack.jack_set_process_callback)((ma_jack_client_t*)pDevice->jack.pClient, ma_device__jack_process_callback, pDevice) != 0) { ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to set process callback."); return MA_FAILED_TO_OPEN_BACKEND_DEVICE; } if (((ma_jack_set_buffer_size_callback_proc)pDevice->pContext->jack.jack_set_buffer_size_callback)((ma_jack_client_t*)pDevice->jack.pClient, ma_device__jack_buffer_size_callback, pDevice) != 0) { ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to set buffer size callback."); return MA_FAILED_TO_OPEN_BACKEND_DEVICE; } ((ma_jack_on_shutdown_proc)pDevice->pContext->jack.jack_on_shutdown)((ma_jack_client_t*)pDevice->jack.pClient, ma_device__jack_shutdown_callback, pDevice); /* The buffer size in frames can change. */ periodSizeInFrames = ((ma_jack_get_buffer_size_proc)pDevice->pContext->jack.jack_get_buffer_size)((ma_jack_client_t*)pDevice->jack.pClient); if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { ma_uint32 iPort; const char** ppPorts; pDescriptorCapture->format = ma_format_f32; pDescriptorCapture->channels = 0; pDescriptorCapture->sampleRate = ((ma_jack_get_sample_rate_proc)pDevice->pContext->jack.jack_get_sample_rate)((ma_jack_client_t*)pDevice->jack.pClient); ma_channel_map_init_standard(ma_standard_channel_map_alsa, pDescriptorCapture->channelMap, ma_countof(pDescriptorCapture->channelMap), pDescriptorCapture->channels); ppPorts = ((ma_jack_get_ports_proc)pDevice->pContext->jack.jack_get_ports)((ma_jack_client_t*)pDevice->jack.pClient, NULL, MA_JACK_DEFAULT_AUDIO_TYPE, ma_JackPortIsPhysical | ma_JackPortIsOutput); if (ppPorts == NULL) { ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to query physical ports."); return MA_FAILED_TO_OPEN_BACKEND_DEVICE; } /* Need to count the number of ports first so we can allocate some memory. */ while (ppPorts[pDescriptorCapture->channels] != NULL) { pDescriptorCapture->channels += 1; } pDevice->jack.ppPortsCapture = (ma_ptr*)ma_malloc(sizeof(*pDevice->jack.ppPortsCapture) * pDescriptorCapture->channels, &pDevice->pContext->allocationCallbacks); if (pDevice->jack.ppPortsCapture == NULL) { return MA_OUT_OF_MEMORY; } for (iPort = 0; iPort < pDescriptorCapture->channels; iPort += 1) { char name[64]; ma_strcpy_s(name, sizeof(name), "capture"); ma_itoa_s((int)iPort, name+7, sizeof(name)-7, 10); /* 7 = length of "capture" */ pDevice->jack.ppPortsCapture[iPort] = ((ma_jack_port_register_proc)pDevice->pContext->jack.jack_port_register)((ma_jack_client_t*)pDevice->jack.pClient, name, MA_JACK_DEFAULT_AUDIO_TYPE, ma_JackPortIsInput, 0); if (pDevice->jack.ppPortsCapture[iPort] == NULL) { ((ma_jack_free_proc)pDevice->pContext->jack.jack_free)((void*)ppPorts); ma_device_uninit__jack(pDevice); ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to register ports."); return MA_FAILED_TO_OPEN_BACKEND_DEVICE; } } ((ma_jack_free_proc)pDevice->pContext->jack.jack_free)((void*)ppPorts); pDescriptorCapture->periodSizeInFrames = periodSizeInFrames; pDescriptorCapture->periodCount = 1; /* There's no notion of a period in JACK. Just set to 1. */ pDevice->jack.pIntermediaryBufferCapture = (float*)ma_calloc(pDescriptorCapture->periodSizeInFrames * ma_get_bytes_per_frame(pDescriptorCapture->format, pDescriptorCapture->channels), &pDevice->pContext->allocationCallbacks); if (pDevice->jack.pIntermediaryBufferCapture == NULL) { ma_device_uninit__jack(pDevice); return MA_OUT_OF_MEMORY; } } if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { ma_uint32 iPort; const char** ppPorts; pDescriptorPlayback->format = ma_format_f32; pDescriptorPlayback->channels = 0; pDescriptorPlayback->sampleRate = ((ma_jack_get_sample_rate_proc)pDevice->pContext->jack.jack_get_sample_rate)((ma_jack_client_t*)pDevice->jack.pClient); ma_channel_map_init_standard(ma_standard_channel_map_alsa, pDescriptorPlayback->channelMap, ma_countof(pDescriptorPlayback->channelMap), pDescriptorPlayback->channels); ppPorts = ((ma_jack_get_ports_proc)pDevice->pContext->jack.jack_get_ports)((ma_jack_client_t*)pDevice->jack.pClient, NULL, MA_JACK_DEFAULT_AUDIO_TYPE, ma_JackPortIsPhysical | ma_JackPortIsInput); if (ppPorts == NULL) { ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to query physical ports."); return MA_FAILED_TO_OPEN_BACKEND_DEVICE; } /* Need to count the number of ports first so we can allocate some memory. */ while (ppPorts[pDescriptorPlayback->channels] != NULL) { pDescriptorPlayback->channels += 1; } pDevice->jack.ppPortsPlayback = (ma_ptr*)ma_malloc(sizeof(*pDevice->jack.ppPortsPlayback) * pDescriptorPlayback->channels, &pDevice->pContext->allocationCallbacks); if (pDevice->jack.ppPortsPlayback == NULL) { ma_free(pDevice->jack.ppPortsCapture, &pDevice->pContext->allocationCallbacks); return MA_OUT_OF_MEMORY; } for (iPort = 0; iPort < pDescriptorPlayback->channels; iPort += 1) { char name[64]; ma_strcpy_s(name, sizeof(name), "playback"); ma_itoa_s((int)iPort, name+8, sizeof(name)-8, 10); /* 8 = length of "playback" */ pDevice->jack.ppPortsPlayback[iPort] = ((ma_jack_port_register_proc)pDevice->pContext->jack.jack_port_register)((ma_jack_client_t*)pDevice->jack.pClient, name, MA_JACK_DEFAULT_AUDIO_TYPE, ma_JackPortIsOutput, 0); if (pDevice->jack.ppPortsPlayback[iPort] == NULL) { ((ma_jack_free_proc)pDevice->pContext->jack.jack_free)((void*)ppPorts); ma_device_uninit__jack(pDevice); ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to register ports."); return MA_FAILED_TO_OPEN_BACKEND_DEVICE; } } ((ma_jack_free_proc)pDevice->pContext->jack.jack_free)((void*)ppPorts); pDescriptorPlayback->periodSizeInFrames = periodSizeInFrames; pDescriptorPlayback->periodCount = 1; /* There's no notion of a period in JACK. Just set to 1. */ pDevice->jack.pIntermediaryBufferPlayback = (float*)ma_calloc(pDescriptorPlayback->periodSizeInFrames * ma_get_bytes_per_frame(pDescriptorPlayback->format, pDescriptorPlayback->channels), &pDevice->pContext->allocationCallbacks); if (pDevice->jack.pIntermediaryBufferPlayback == NULL) { ma_device_uninit__jack(pDevice); return MA_OUT_OF_MEMORY; } } return MA_SUCCESS; } static ma_result ma_device_start__jack(ma_device* pDevice) { ma_context* pContext = pDevice->pContext; int resultJACK; size_t i; resultJACK = ((ma_jack_activate_proc)pContext->jack.jack_activate)((ma_jack_client_t*)pDevice->jack.pClient); if (resultJACK != 0) { ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to activate the JACK client."); return MA_FAILED_TO_START_BACKEND_DEVICE; } if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { const char** ppServerPorts = ((ma_jack_get_ports_proc)pContext->jack.jack_get_ports)((ma_jack_client_t*)pDevice->jack.pClient, NULL, MA_JACK_DEFAULT_AUDIO_TYPE, ma_JackPortIsPhysical | ma_JackPortIsOutput); if (ppServerPorts == NULL) { ((ma_jack_deactivate_proc)pContext->jack.jack_deactivate)((ma_jack_client_t*)pDevice->jack.pClient); ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to retrieve physical ports."); return MA_ERROR; } for (i = 0; ppServerPorts[i] != NULL; ++i) { const char* pServerPort = ppServerPorts[i]; const char* pClientPort = ((ma_jack_port_name_proc)pContext->jack.jack_port_name)((ma_jack_port_t*)pDevice->jack.ppPortsCapture[i]); resultJACK = ((ma_jack_connect_proc)pContext->jack.jack_connect)((ma_jack_client_t*)pDevice->jack.pClient, pServerPort, pClientPort); if (resultJACK != 0) { ((ma_jack_free_proc)pContext->jack.jack_free)((void*)ppServerPorts); ((ma_jack_deactivate_proc)pContext->jack.jack_deactivate)((ma_jack_client_t*)pDevice->jack.pClient); ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to connect ports."); return MA_ERROR; } } ((ma_jack_free_proc)pContext->jack.jack_free)((void*)ppServerPorts); } if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { const char** ppServerPorts = ((ma_jack_get_ports_proc)pContext->jack.jack_get_ports)((ma_jack_client_t*)pDevice->jack.pClient, NULL, MA_JACK_DEFAULT_AUDIO_TYPE, ma_JackPortIsPhysical | ma_JackPortIsInput); if (ppServerPorts == NULL) { ((ma_jack_deactivate_proc)pContext->jack.jack_deactivate)((ma_jack_client_t*)pDevice->jack.pClient); ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to retrieve physical ports."); return MA_ERROR; } for (i = 0; ppServerPorts[i] != NULL; ++i) { const char* pServerPort = ppServerPorts[i]; const char* pClientPort = ((ma_jack_port_name_proc)pContext->jack.jack_port_name)((ma_jack_port_t*)pDevice->jack.ppPortsPlayback[i]); resultJACK = ((ma_jack_connect_proc)pContext->jack.jack_connect)((ma_jack_client_t*)pDevice->jack.pClient, pClientPort, pServerPort); if (resultJACK != 0) { ((ma_jack_free_proc)pContext->jack.jack_free)((void*)ppServerPorts); ((ma_jack_deactivate_proc)pContext->jack.jack_deactivate)((ma_jack_client_t*)pDevice->jack.pClient); ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to connect ports."); return MA_ERROR; } } ((ma_jack_free_proc)pContext->jack.jack_free)((void*)ppServerPorts); } return MA_SUCCESS; } static ma_result ma_device_stop__jack(ma_device* pDevice) { ma_context* pContext = pDevice->pContext; if (((ma_jack_deactivate_proc)pContext->jack.jack_deactivate)((ma_jack_client_t*)pDevice->jack.pClient) != 0) { ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] An error occurred when deactivating the JACK client."); return MA_ERROR; } ma_device__on_notification_stopped(pDevice); return MA_SUCCESS; } static ma_result ma_context_uninit__jack(ma_context* pContext) { MA_ASSERT(pContext != NULL); MA_ASSERT(pContext->backend == ma_backend_jack); ma_free(pContext->jack.pClientName, &pContext->allocationCallbacks); pContext->jack.pClientName = NULL; #ifndef MA_NO_RUNTIME_LINKING ma_dlclose(pContext, pContext->jack.jackSO); #endif return MA_SUCCESS; } static ma_result ma_context_init__jack(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks) { #ifndef MA_NO_RUNTIME_LINKING const char* libjackNames[] = { #ifdef MA_WIN32 "libjack.dll", "libjack64.dll" #else "libjack.so", "libjack.so.0" #endif }; size_t i; for (i = 0; i < ma_countof(libjackNames); ++i) { pContext->jack.jackSO = ma_dlopen(pContext, libjackNames[i]); if (pContext->jack.jackSO != NULL) { break; } } if (pContext->jack.jackSO == NULL) { return MA_NO_BACKEND; } pContext->jack.jack_client_open = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_client_open"); pContext->jack.jack_client_close = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_client_close"); pContext->jack.jack_client_name_size = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_client_name_size"); pContext->jack.jack_set_process_callback = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_set_process_callback"); pContext->jack.jack_set_buffer_size_callback = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_set_buffer_size_callback"); pContext->jack.jack_on_shutdown = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_on_shutdown"); pContext->jack.jack_get_sample_rate = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_get_sample_rate"); pContext->jack.jack_get_buffer_size = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_get_buffer_size"); pContext->jack.jack_get_ports = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_get_ports"); pContext->jack.jack_activate = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_activate"); pContext->jack.jack_deactivate = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_deactivate"); pContext->jack.jack_connect = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_connect"); pContext->jack.jack_port_register = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_port_register"); pContext->jack.jack_port_name = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_port_name"); pContext->jack.jack_port_get_buffer = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_port_get_buffer"); pContext->jack.jack_free = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_free"); #else /* This strange assignment system is here just to ensure type safety of miniaudio's function pointer types. If anything differs slightly the compiler should throw a warning. */ ma_jack_client_open_proc _jack_client_open = jack_client_open; ma_jack_client_close_proc _jack_client_close = jack_client_close; ma_jack_client_name_size_proc _jack_client_name_size = jack_client_name_size; ma_jack_set_process_callback_proc _jack_set_process_callback = jack_set_process_callback; ma_jack_set_buffer_size_callback_proc _jack_set_buffer_size_callback = jack_set_buffer_size_callback; ma_jack_on_shutdown_proc _jack_on_shutdown = jack_on_shutdown; ma_jack_get_sample_rate_proc _jack_get_sample_rate = jack_get_sample_rate; ma_jack_get_buffer_size_proc _jack_get_buffer_size = jack_get_buffer_size; ma_jack_get_ports_proc _jack_get_ports = jack_get_ports; ma_jack_activate_proc _jack_activate = jack_activate; ma_jack_deactivate_proc _jack_deactivate = jack_deactivate; ma_jack_connect_proc _jack_connect = jack_connect; ma_jack_port_register_proc _jack_port_register = jack_port_register; ma_jack_port_name_proc _jack_port_name = jack_port_name; ma_jack_port_get_buffer_proc _jack_port_get_buffer = jack_port_get_buffer; ma_jack_free_proc _jack_free = jack_free; pContext->jack.jack_client_open = (ma_proc)_jack_client_open; pContext->jack.jack_client_close = (ma_proc)_jack_client_close; pContext->jack.jack_client_name_size = (ma_proc)_jack_client_name_size; pContext->jack.jack_set_process_callback = (ma_proc)_jack_set_process_callback; pContext->jack.jack_set_buffer_size_callback = (ma_proc)_jack_set_buffer_size_callback; pContext->jack.jack_on_shutdown = (ma_proc)_jack_on_shutdown; pContext->jack.jack_get_sample_rate = (ma_proc)_jack_get_sample_rate; pContext->jack.jack_get_buffer_size = (ma_proc)_jack_get_buffer_size; pContext->jack.jack_get_ports = (ma_proc)_jack_get_ports; pContext->jack.jack_activate = (ma_proc)_jack_activate; pContext->jack.jack_deactivate = (ma_proc)_jack_deactivate; pContext->jack.jack_connect = (ma_proc)_jack_connect; pContext->jack.jack_port_register = (ma_proc)_jack_port_register; pContext->jack.jack_port_name = (ma_proc)_jack_port_name; pContext->jack.jack_port_get_buffer = (ma_proc)_jack_port_get_buffer; pContext->jack.jack_free = (ma_proc)_jack_free; #endif if (pConfig->jack.pClientName != NULL) { pContext->jack.pClientName = ma_copy_string(pConfig->jack.pClientName, &pContext->allocationCallbacks); } pContext->jack.tryStartServer = pConfig->jack.tryStartServer; /* Getting here means the JACK library is installed, but it doesn't necessarily mean it's usable. We need to quickly test this by connecting a temporary client. */ { ma_jack_client_t* pDummyClient; ma_result result = ma_context_open_client__jack(pContext, &pDummyClient); if (result != MA_SUCCESS) { ma_free(pContext->jack.pClientName, &pContext->allocationCallbacks); #ifndef MA_NO_RUNTIME_LINKING ma_dlclose(pContext, pContext->jack.jackSO); #endif return MA_NO_BACKEND; } ((ma_jack_client_close_proc)pContext->jack.jack_client_close)((ma_jack_client_t*)pDummyClient); } pCallbacks->onContextInit = ma_context_init__jack; pCallbacks->onContextUninit = ma_context_uninit__jack; pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__jack; pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__jack; pCallbacks->onDeviceInit = ma_device_init__jack; pCallbacks->onDeviceUninit = ma_device_uninit__jack; pCallbacks->onDeviceStart = ma_device_start__jack; pCallbacks->onDeviceStop = ma_device_stop__jack; pCallbacks->onDeviceRead = NULL; /* Not used because JACK is asynchronous. */ pCallbacks->onDeviceWrite = NULL; /* Not used because JACK is asynchronous. */ pCallbacks->onDeviceDataLoop = NULL; /* Not used because JACK is asynchronous. */ return MA_SUCCESS; } #endif /* JACK */ /****************************************************************************** Core Audio Backend References ========== - Technical Note TN2091: Device input using the HAL Output Audio Unit https://developer.apple.com/library/archive/technotes/tn2091/_index.html ******************************************************************************/ #ifdef MA_HAS_COREAUDIO #include #if defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE == 1 #define MA_APPLE_MOBILE #if defined(TARGET_OS_TV) && TARGET_OS_TV == 1 #define MA_APPLE_TV #endif #if defined(TARGET_OS_WATCH) && TARGET_OS_WATCH == 1 #define MA_APPLE_WATCH #endif #if __has_feature(objc_arc) #define MA_BRIDGE_TRANSFER __bridge_transfer #define MA_BRIDGE_RETAINED __bridge_retained #else #define MA_BRIDGE_TRANSFER #define MA_BRIDGE_RETAINED #endif #else #define MA_APPLE_DESKTOP #endif #if defined(MA_APPLE_DESKTOP) #include #else #include #endif #include /* CoreFoundation */ typedef Boolean (* ma_CFStringGetCString_proc)(CFStringRef theString, char* buffer, CFIndex bufferSize, CFStringEncoding encoding); typedef void (* ma_CFRelease_proc)(CFTypeRef cf); /* CoreAudio */ #if defined(MA_APPLE_DESKTOP) typedef OSStatus (* ma_AudioObjectGetPropertyData_proc)(AudioObjectID inObjectID, const AudioObjectPropertyAddress* inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32* ioDataSize, void* outData); typedef OSStatus (* ma_AudioObjectGetPropertyDataSize_proc)(AudioObjectID inObjectID, const AudioObjectPropertyAddress* inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32* outDataSize); typedef OSStatus (* ma_AudioObjectSetPropertyData_proc)(AudioObjectID inObjectID, const AudioObjectPropertyAddress* inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32 inDataSize, const void* inData); typedef OSStatus (* ma_AudioObjectAddPropertyListener_proc)(AudioObjectID inObjectID, const AudioObjectPropertyAddress* inAddress, AudioObjectPropertyListenerProc inListener, void* inClientData); typedef OSStatus (* ma_AudioObjectRemovePropertyListener_proc)(AudioObjectID inObjectID, const AudioObjectPropertyAddress* inAddress, AudioObjectPropertyListenerProc inListener, void* inClientData); #endif /* AudioToolbox */ typedef AudioComponent (* ma_AudioComponentFindNext_proc)(AudioComponent inComponent, const AudioComponentDescription* inDesc); typedef OSStatus (* ma_AudioComponentInstanceDispose_proc)(AudioComponentInstance inInstance); typedef OSStatus (* ma_AudioComponentInstanceNew_proc)(AudioComponent inComponent, AudioComponentInstance* outInstance); typedef OSStatus (* ma_AudioOutputUnitStart_proc)(AudioUnit inUnit); typedef OSStatus (* ma_AudioOutputUnitStop_proc)(AudioUnit inUnit); typedef OSStatus (* ma_AudioUnitAddPropertyListener_proc)(AudioUnit inUnit, AudioUnitPropertyID inID, AudioUnitPropertyListenerProc inProc, void* inProcUserData); typedef OSStatus (* ma_AudioUnitGetPropertyInfo_proc)(AudioUnit inUnit, AudioUnitPropertyID inID, AudioUnitScope inScope, AudioUnitElement inElement, UInt32* outDataSize, Boolean* outWriteable); typedef OSStatus (* ma_AudioUnitGetProperty_proc)(AudioUnit inUnit, AudioUnitPropertyID inID, AudioUnitScope inScope, AudioUnitElement inElement, void* outData, UInt32* ioDataSize); typedef OSStatus (* ma_AudioUnitSetProperty_proc)(AudioUnit inUnit, AudioUnitPropertyID inID, AudioUnitScope inScope, AudioUnitElement inElement, const void* inData, UInt32 inDataSize); typedef OSStatus (* ma_AudioUnitInitialize_proc)(AudioUnit inUnit); typedef OSStatus (* ma_AudioUnitRender_proc)(AudioUnit inUnit, AudioUnitRenderActionFlags* ioActionFlags, const AudioTimeStamp* inTimeStamp, UInt32 inOutputBusNumber, UInt32 inNumberFrames, AudioBufferList* ioData); #define MA_COREAUDIO_OUTPUT_BUS 0 #define MA_COREAUDIO_INPUT_BUS 1 #if defined(MA_APPLE_DESKTOP) static ma_result ma_device_reinit_internal__coreaudio(ma_device* pDevice, ma_device_type deviceType, ma_bool32 disposePreviousAudioUnit); #endif /* Core Audio So far, Core Audio has been the worst backend to work with due to being both unintuitive and having almost no documentation apart from comments in the headers (which admittedly are quite good). For my own purposes, and for anybody out there whose needing to figure out how this darn thing works, I'm going to outline a few things here. Since miniaudio is a fairly low-level API, one of the things it needs is control over specific devices, and it needs to be able to identify whether or not it can be used as playback and/or capture. The AudioObject API is the only one I've seen that supports this level of detail. There was some public domain sample code I stumbled across that used the AudioComponent and AudioUnit APIs, but I couldn't see anything that gave low-level control over device selection and capabilities (the distinction between playback and capture in particular). Therefore, miniaudio is using the AudioObject API. Most (all?) functions in the AudioObject API take a AudioObjectID as it's input. This is the device identifier. When retrieving global information, such as the device list, you use kAudioObjectSystemObject. When retrieving device-specific data, you pass in the ID for that device. In order to retrieve device-specific IDs you need to enumerate over each of the devices. This is done using the AudioObjectGetPropertyDataSize() and AudioObjectGetPropertyData() APIs which seem to be the central APIs for retrieving information about the system and specific devices. To use the AudioObjectGetPropertyData() API you need to use the notion of a property address. A property address is a structure with three variables and is used to identify which property you are getting or setting. The first is the "selector" which is basically the specific property that you're wanting to retrieve or set. The second is the "scope", which is typically set to kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyScopeInput for input-specific properties and kAudioObjectPropertyScopeOutput for output-specific properties. The last is the "element" which is always set to kAudioObjectPropertyElementMain in miniaudio's case. I don't know of any cases where this would be set to anything different. Back to the earlier issue of device retrieval, you first use the AudioObjectGetPropertyDataSize() API to retrieve the size of the raw data which is just a list of AudioDeviceID's. You use the kAudioObjectSystemObject AudioObjectID, and a property address with the kAudioHardwarePropertyDevices selector and the kAudioObjectPropertyScopeGlobal scope. Once you have the size, allocate a block of memory of that size and then call AudioObjectGetPropertyData(). The data is just a list of AudioDeviceID's so just do "dataSize/sizeof(AudioDeviceID)" to know the device count. */ static ma_result ma_result_from_OSStatus(OSStatus status) { switch (status) { case noErr: return MA_SUCCESS; #if defined(MA_APPLE_DESKTOP) case kAudioHardwareNotRunningError: return MA_DEVICE_NOT_STARTED; case kAudioHardwareUnspecifiedError: return MA_ERROR; case kAudioHardwareUnknownPropertyError: return MA_INVALID_ARGS; case kAudioHardwareBadPropertySizeError: return MA_INVALID_OPERATION; case kAudioHardwareIllegalOperationError: return MA_INVALID_OPERATION; case kAudioHardwareBadObjectError: return MA_INVALID_ARGS; case kAudioHardwareBadDeviceError: return MA_INVALID_ARGS; case kAudioHardwareBadStreamError: return MA_INVALID_ARGS; case kAudioHardwareUnsupportedOperationError: return MA_INVALID_OPERATION; case kAudioDeviceUnsupportedFormatError: return MA_FORMAT_NOT_SUPPORTED; case kAudioDevicePermissionsError: return MA_ACCESS_DENIED; #endif default: return MA_ERROR; } } #if 0 static ma_channel ma_channel_from_AudioChannelBitmap(AudioChannelBitmap bit) { switch (bit) { case kAudioChannelBit_Left: return MA_CHANNEL_LEFT; case kAudioChannelBit_Right: return MA_CHANNEL_RIGHT; case kAudioChannelBit_Center: return MA_CHANNEL_FRONT_CENTER; case kAudioChannelBit_LFEScreen: return MA_CHANNEL_LFE; case kAudioChannelBit_LeftSurround: return MA_CHANNEL_BACK_LEFT; case kAudioChannelBit_RightSurround: return MA_CHANNEL_BACK_RIGHT; case kAudioChannelBit_LeftCenter: return MA_CHANNEL_FRONT_LEFT_CENTER; case kAudioChannelBit_RightCenter: return MA_CHANNEL_FRONT_RIGHT_CENTER; case kAudioChannelBit_CenterSurround: return MA_CHANNEL_BACK_CENTER; case kAudioChannelBit_LeftSurroundDirect: return MA_CHANNEL_SIDE_LEFT; case kAudioChannelBit_RightSurroundDirect: return MA_CHANNEL_SIDE_RIGHT; case kAudioChannelBit_TopCenterSurround: return MA_CHANNEL_TOP_CENTER; case kAudioChannelBit_VerticalHeightLeft: return MA_CHANNEL_TOP_FRONT_LEFT; case kAudioChannelBit_VerticalHeightCenter: return MA_CHANNEL_TOP_FRONT_CENTER; case kAudioChannelBit_VerticalHeightRight: return MA_CHANNEL_TOP_FRONT_RIGHT; case kAudioChannelBit_TopBackLeft: return MA_CHANNEL_TOP_BACK_LEFT; case kAudioChannelBit_TopBackCenter: return MA_CHANNEL_TOP_BACK_CENTER; case kAudioChannelBit_TopBackRight: return MA_CHANNEL_TOP_BACK_RIGHT; default: return MA_CHANNEL_NONE; } } #endif static ma_result ma_format_from_AudioStreamBasicDescription(const AudioStreamBasicDescription* pDescription, ma_format* pFormatOut) { MA_ASSERT(pDescription != NULL); MA_ASSERT(pFormatOut != NULL); *pFormatOut = ma_format_unknown; /* Safety. */ /* There's a few things miniaudio doesn't support. */ if (pDescription->mFormatID != kAudioFormatLinearPCM) { return MA_FORMAT_NOT_SUPPORTED; } /* We don't support any non-packed formats that are aligned high. */ if ((pDescription->mFormatFlags & kLinearPCMFormatFlagIsAlignedHigh) != 0) { return MA_FORMAT_NOT_SUPPORTED; } /* Only supporting native-endian. */ if ((ma_is_little_endian() && (pDescription->mFormatFlags & kAudioFormatFlagIsBigEndian) != 0) || (ma_is_big_endian() && (pDescription->mFormatFlags & kAudioFormatFlagIsBigEndian) == 0)) { return MA_FORMAT_NOT_SUPPORTED; } /* We are not currently supporting non-interleaved formats (this will be added in a future version of miniaudio). */ /*if ((pDescription->mFormatFlags & kAudioFormatFlagIsNonInterleaved) != 0) { return MA_FORMAT_NOT_SUPPORTED; }*/ if ((pDescription->mFormatFlags & kLinearPCMFormatFlagIsFloat) != 0) { if (pDescription->mBitsPerChannel == 32) { *pFormatOut = ma_format_f32; return MA_SUCCESS; } } else { if ((pDescription->mFormatFlags & kLinearPCMFormatFlagIsSignedInteger) != 0) { if (pDescription->mBitsPerChannel == 16) { *pFormatOut = ma_format_s16; return MA_SUCCESS; } else if (pDescription->mBitsPerChannel == 24) { if (pDescription->mBytesPerFrame == (pDescription->mBitsPerChannel/8 * pDescription->mChannelsPerFrame)) { *pFormatOut = ma_format_s24; return MA_SUCCESS; } else { if (pDescription->mBytesPerFrame/pDescription->mChannelsPerFrame == sizeof(ma_int32)) { /* TODO: Implement ma_format_s24_32. */ /**pFormatOut = ma_format_s24_32;*/ /*return MA_SUCCESS;*/ return MA_FORMAT_NOT_SUPPORTED; } } } else if (pDescription->mBitsPerChannel == 32) { *pFormatOut = ma_format_s32; return MA_SUCCESS; } } else { if (pDescription->mBitsPerChannel == 8) { *pFormatOut = ma_format_u8; return MA_SUCCESS; } } } /* Getting here means the format is not supported. */ return MA_FORMAT_NOT_SUPPORTED; } #if defined(MA_APPLE_DESKTOP) static ma_channel ma_channel_from_AudioChannelLabel(AudioChannelLabel label) { switch (label) { case kAudioChannelLabel_Unknown: return MA_CHANNEL_NONE; case kAudioChannelLabel_Unused: return MA_CHANNEL_NONE; case kAudioChannelLabel_UseCoordinates: return MA_CHANNEL_NONE; case kAudioChannelLabel_Left: return MA_CHANNEL_LEFT; case kAudioChannelLabel_Right: return MA_CHANNEL_RIGHT; case kAudioChannelLabel_Center: return MA_CHANNEL_FRONT_CENTER; case kAudioChannelLabel_LFEScreen: return MA_CHANNEL_LFE; case kAudioChannelLabel_LeftSurround: return MA_CHANNEL_BACK_LEFT; case kAudioChannelLabel_RightSurround: return MA_CHANNEL_BACK_RIGHT; case kAudioChannelLabel_LeftCenter: return MA_CHANNEL_FRONT_LEFT_CENTER; case kAudioChannelLabel_RightCenter: return MA_CHANNEL_FRONT_RIGHT_CENTER; case kAudioChannelLabel_CenterSurround: return MA_CHANNEL_BACK_CENTER; case kAudioChannelLabel_LeftSurroundDirect: return MA_CHANNEL_SIDE_LEFT; case kAudioChannelLabel_RightSurroundDirect: return MA_CHANNEL_SIDE_RIGHT; case kAudioChannelLabel_TopCenterSurround: return MA_CHANNEL_TOP_CENTER; case kAudioChannelLabel_VerticalHeightLeft: return MA_CHANNEL_TOP_FRONT_LEFT; case kAudioChannelLabel_VerticalHeightCenter: return MA_CHANNEL_TOP_FRONT_CENTER; case kAudioChannelLabel_VerticalHeightRight: return MA_CHANNEL_TOP_FRONT_RIGHT; case kAudioChannelLabel_TopBackLeft: return MA_CHANNEL_TOP_BACK_LEFT; case kAudioChannelLabel_TopBackCenter: return MA_CHANNEL_TOP_BACK_CENTER; case kAudioChannelLabel_TopBackRight: return MA_CHANNEL_TOP_BACK_RIGHT; case kAudioChannelLabel_RearSurroundLeft: return MA_CHANNEL_BACK_LEFT; case kAudioChannelLabel_RearSurroundRight: return MA_CHANNEL_BACK_RIGHT; case kAudioChannelLabel_LeftWide: return MA_CHANNEL_SIDE_LEFT; case kAudioChannelLabel_RightWide: return MA_CHANNEL_SIDE_RIGHT; case kAudioChannelLabel_LFE2: return MA_CHANNEL_LFE; case kAudioChannelLabel_LeftTotal: return MA_CHANNEL_LEFT; case kAudioChannelLabel_RightTotal: return MA_CHANNEL_RIGHT; case kAudioChannelLabel_HearingImpaired: return MA_CHANNEL_NONE; case kAudioChannelLabel_Narration: return MA_CHANNEL_MONO; case kAudioChannelLabel_Mono: return MA_CHANNEL_MONO; case kAudioChannelLabel_DialogCentricMix: return MA_CHANNEL_MONO; case kAudioChannelLabel_CenterSurroundDirect: return MA_CHANNEL_BACK_CENTER; case kAudioChannelLabel_Haptic: return MA_CHANNEL_NONE; case kAudioChannelLabel_Ambisonic_W: return MA_CHANNEL_NONE; case kAudioChannelLabel_Ambisonic_X: return MA_CHANNEL_NONE; case kAudioChannelLabel_Ambisonic_Y: return MA_CHANNEL_NONE; case kAudioChannelLabel_Ambisonic_Z: return MA_CHANNEL_NONE; case kAudioChannelLabel_MS_Mid: return MA_CHANNEL_LEFT; case kAudioChannelLabel_MS_Side: return MA_CHANNEL_RIGHT; case kAudioChannelLabel_XY_X: return MA_CHANNEL_LEFT; case kAudioChannelLabel_XY_Y: return MA_CHANNEL_RIGHT; case kAudioChannelLabel_HeadphonesLeft: return MA_CHANNEL_LEFT; case kAudioChannelLabel_HeadphonesRight: return MA_CHANNEL_RIGHT; case kAudioChannelLabel_ClickTrack: return MA_CHANNEL_NONE; case kAudioChannelLabel_ForeignLanguage: return MA_CHANNEL_NONE; case kAudioChannelLabel_Discrete: return MA_CHANNEL_NONE; case kAudioChannelLabel_Discrete_0: return MA_CHANNEL_AUX_0; case kAudioChannelLabel_Discrete_1: return MA_CHANNEL_AUX_1; case kAudioChannelLabel_Discrete_2: return MA_CHANNEL_AUX_2; case kAudioChannelLabel_Discrete_3: return MA_CHANNEL_AUX_3; case kAudioChannelLabel_Discrete_4: return MA_CHANNEL_AUX_4; case kAudioChannelLabel_Discrete_5: return MA_CHANNEL_AUX_5; case kAudioChannelLabel_Discrete_6: return MA_CHANNEL_AUX_6; case kAudioChannelLabel_Discrete_7: return MA_CHANNEL_AUX_7; case kAudioChannelLabel_Discrete_8: return MA_CHANNEL_AUX_8; case kAudioChannelLabel_Discrete_9: return MA_CHANNEL_AUX_9; case kAudioChannelLabel_Discrete_10: return MA_CHANNEL_AUX_10; case kAudioChannelLabel_Discrete_11: return MA_CHANNEL_AUX_11; case kAudioChannelLabel_Discrete_12: return MA_CHANNEL_AUX_12; case kAudioChannelLabel_Discrete_13: return MA_CHANNEL_AUX_13; case kAudioChannelLabel_Discrete_14: return MA_CHANNEL_AUX_14; case kAudioChannelLabel_Discrete_15: return MA_CHANNEL_AUX_15; case kAudioChannelLabel_Discrete_65535: return MA_CHANNEL_NONE; #if 0 /* Introduced in a later version of macOS. */ case kAudioChannelLabel_HOA_ACN: return MA_CHANNEL_NONE; case kAudioChannelLabel_HOA_ACN_0: return MA_CHANNEL_AUX_0; case kAudioChannelLabel_HOA_ACN_1: return MA_CHANNEL_AUX_1; case kAudioChannelLabel_HOA_ACN_2: return MA_CHANNEL_AUX_2; case kAudioChannelLabel_HOA_ACN_3: return MA_CHANNEL_AUX_3; case kAudioChannelLabel_HOA_ACN_4: return MA_CHANNEL_AUX_4; case kAudioChannelLabel_HOA_ACN_5: return MA_CHANNEL_AUX_5; case kAudioChannelLabel_HOA_ACN_6: return MA_CHANNEL_AUX_6; case kAudioChannelLabel_HOA_ACN_7: return MA_CHANNEL_AUX_7; case kAudioChannelLabel_HOA_ACN_8: return MA_CHANNEL_AUX_8; case kAudioChannelLabel_HOA_ACN_9: return MA_CHANNEL_AUX_9; case kAudioChannelLabel_HOA_ACN_10: return MA_CHANNEL_AUX_10; case kAudioChannelLabel_HOA_ACN_11: return MA_CHANNEL_AUX_11; case kAudioChannelLabel_HOA_ACN_12: return MA_CHANNEL_AUX_12; case kAudioChannelLabel_HOA_ACN_13: return MA_CHANNEL_AUX_13; case kAudioChannelLabel_HOA_ACN_14: return MA_CHANNEL_AUX_14; case kAudioChannelLabel_HOA_ACN_15: return MA_CHANNEL_AUX_15; case kAudioChannelLabel_HOA_ACN_65024: return MA_CHANNEL_NONE; #endif default: return MA_CHANNEL_NONE; } } static ma_result ma_get_channel_map_from_AudioChannelLayout(AudioChannelLayout* pChannelLayout, ma_channel* pChannelMap, size_t channelMapCap) { MA_ASSERT(pChannelLayout != NULL); if (pChannelLayout->mChannelLayoutTag == kAudioChannelLayoutTag_UseChannelDescriptions) { UInt32 iChannel; for (iChannel = 0; iChannel < pChannelLayout->mNumberChannelDescriptions && iChannel < channelMapCap; ++iChannel) { pChannelMap[iChannel] = ma_channel_from_AudioChannelLabel(pChannelLayout->mChannelDescriptions[iChannel].mChannelLabel); } } else #if 0 if (pChannelLayout->mChannelLayoutTag == kAudioChannelLayoutTag_UseChannelBitmap) { /* This is the same kind of system that's used by Windows audio APIs. */ UInt32 iChannel = 0; UInt32 iBit; AudioChannelBitmap bitmap = pChannelLayout->mChannelBitmap; for (iBit = 0; iBit < 32 && iChannel < channelMapCap; ++iBit) { AudioChannelBitmap bit = bitmap & (1 << iBit); if (bit != 0) { pChannelMap[iChannel++] = ma_channel_from_AudioChannelBit(bit); } } } else #endif { /* Need to use the tag to determine the channel map. For now I'm just assuming a default channel map, but later on this should be updated to determine the mapping based on the tag. */ UInt32 channelCount; /* Our channel map retrieval APIs below take 32-bit integers, so we'll want to clamp the channel map capacity. */ if (channelMapCap > 0xFFFFFFFF) { channelMapCap = 0xFFFFFFFF; } channelCount = ma_min(AudioChannelLayoutTag_GetNumberOfChannels(pChannelLayout->mChannelLayoutTag), (UInt32)channelMapCap); switch (pChannelLayout->mChannelLayoutTag) { case kAudioChannelLayoutTag_Mono: case kAudioChannelLayoutTag_Stereo: case kAudioChannelLayoutTag_StereoHeadphones: case kAudioChannelLayoutTag_MatrixStereo: case kAudioChannelLayoutTag_MidSide: case kAudioChannelLayoutTag_XY: case kAudioChannelLayoutTag_Binaural: case kAudioChannelLayoutTag_Ambisonic_B_Format: { ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, channelCount); } break; case kAudioChannelLayoutTag_Octagonal: { pChannelMap[7] = MA_CHANNEL_SIDE_RIGHT; pChannelMap[6] = MA_CHANNEL_SIDE_LEFT; } /* Intentional fallthrough. */ case kAudioChannelLayoutTag_Hexagonal: { pChannelMap[5] = MA_CHANNEL_BACK_CENTER; } /* Intentional fallthrough. */ case kAudioChannelLayoutTag_Pentagonal: { pChannelMap[4] = MA_CHANNEL_FRONT_CENTER; } /* Intentional fallghrough. */ case kAudioChannelLayoutTag_Quadraphonic: { pChannelMap[3] = MA_CHANNEL_BACK_RIGHT; pChannelMap[2] = MA_CHANNEL_BACK_LEFT; pChannelMap[1] = MA_CHANNEL_RIGHT; pChannelMap[0] = MA_CHANNEL_LEFT; } break; /* TODO: Add support for more tags here. */ default: { ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, channelCount); } break; } } return MA_SUCCESS; } #if (defined(MAC_OS_VERSION_12_0) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_VERSION_12_0) || \ (defined(__IPHONE_15_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_15_0) #define AUDIO_OBJECT_PROPERTY_ELEMENT kAudioObjectPropertyElementMain #else /* kAudioObjectPropertyElementMaster is deprecated. */ #define AUDIO_OBJECT_PROPERTY_ELEMENT kAudioObjectPropertyElementMaster #endif static ma_result ma_get_device_object_ids__coreaudio(ma_context* pContext, UInt32* pDeviceCount, AudioObjectID** ppDeviceObjectIDs) /* NOTE: Free the returned buffer with ma_free(). */ { AudioObjectPropertyAddress propAddressDevices; UInt32 deviceObjectsDataSize; OSStatus status; AudioObjectID* pDeviceObjectIDs; MA_ASSERT(pContext != NULL); MA_ASSERT(pDeviceCount != NULL); MA_ASSERT(ppDeviceObjectIDs != NULL); /* Safety. */ *pDeviceCount = 0; *ppDeviceObjectIDs = NULL; propAddressDevices.mSelector = kAudioHardwarePropertyDevices; propAddressDevices.mScope = kAudioObjectPropertyScopeGlobal; propAddressDevices.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT; status = ((ma_AudioObjectGetPropertyDataSize_proc)pContext->coreaudio.AudioObjectGetPropertyDataSize)(kAudioObjectSystemObject, &propAddressDevices, 0, NULL, &deviceObjectsDataSize); if (status != noErr) { return ma_result_from_OSStatus(status); } pDeviceObjectIDs = (AudioObjectID*)ma_malloc(deviceObjectsDataSize, &pContext->allocationCallbacks); if (pDeviceObjectIDs == NULL) { return MA_OUT_OF_MEMORY; } status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(kAudioObjectSystemObject, &propAddressDevices, 0, NULL, &deviceObjectsDataSize, pDeviceObjectIDs); if (status != noErr) { ma_free(pDeviceObjectIDs, &pContext->allocationCallbacks); return ma_result_from_OSStatus(status); } *pDeviceCount = deviceObjectsDataSize / sizeof(AudioObjectID); *ppDeviceObjectIDs = pDeviceObjectIDs; return MA_SUCCESS; } static ma_result ma_get_AudioObject_uid_as_CFStringRef(ma_context* pContext, AudioObjectID objectID, CFStringRef* pUID) { AudioObjectPropertyAddress propAddress; UInt32 dataSize; OSStatus status; MA_ASSERT(pContext != NULL); propAddress.mSelector = kAudioDevicePropertyDeviceUID; propAddress.mScope = kAudioObjectPropertyScopeGlobal; propAddress.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT; dataSize = sizeof(*pUID); status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(objectID, &propAddress, 0, NULL, &dataSize, pUID); if (status != noErr) { return ma_result_from_OSStatus(status); } return MA_SUCCESS; } static ma_result ma_get_AudioObject_uid(ma_context* pContext, AudioObjectID objectID, size_t bufferSize, char* bufferOut) { CFStringRef uid; ma_result result; MA_ASSERT(pContext != NULL); result = ma_get_AudioObject_uid_as_CFStringRef(pContext, objectID, &uid); if (result != MA_SUCCESS) { return result; } if (!((ma_CFStringGetCString_proc)pContext->coreaudio.CFStringGetCString)(uid, bufferOut, bufferSize, kCFStringEncodingUTF8)) { return MA_ERROR; } ((ma_CFRelease_proc)pContext->coreaudio.CFRelease)(uid); return MA_SUCCESS; } static ma_result ma_get_AudioObject_name(ma_context* pContext, AudioObjectID objectID, size_t bufferSize, char* bufferOut) { AudioObjectPropertyAddress propAddress; CFStringRef deviceName = NULL; UInt32 dataSize; OSStatus status; MA_ASSERT(pContext != NULL); propAddress.mSelector = kAudioDevicePropertyDeviceNameCFString; propAddress.mScope = kAudioObjectPropertyScopeGlobal; propAddress.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT; dataSize = sizeof(deviceName); status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(objectID, &propAddress, 0, NULL, &dataSize, &deviceName); if (status != noErr) { return ma_result_from_OSStatus(status); } if (!((ma_CFStringGetCString_proc)pContext->coreaudio.CFStringGetCString)(deviceName, bufferOut, bufferSize, kCFStringEncodingUTF8)) { return MA_ERROR; } ((ma_CFRelease_proc)pContext->coreaudio.CFRelease)(deviceName); return MA_SUCCESS; } static ma_bool32 ma_does_AudioObject_support_scope(ma_context* pContext, AudioObjectID deviceObjectID, AudioObjectPropertyScope scope) { AudioObjectPropertyAddress propAddress; UInt32 dataSize; OSStatus status; AudioBufferList* pBufferList; ma_bool32 isSupported; MA_ASSERT(pContext != NULL); /* To know whether or not a device is an input device we need ot look at the stream configuration. If it has an output channel it's a playback device. */ propAddress.mSelector = kAudioDevicePropertyStreamConfiguration; propAddress.mScope = scope; propAddress.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT; status = ((ma_AudioObjectGetPropertyDataSize_proc)pContext->coreaudio.AudioObjectGetPropertyDataSize)(deviceObjectID, &propAddress, 0, NULL, &dataSize); if (status != noErr) { return MA_FALSE; } pBufferList = (AudioBufferList*)ma_malloc(dataSize, &pContext->allocationCallbacks); if (pBufferList == NULL) { return MA_FALSE; /* Out of memory. */ } status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(deviceObjectID, &propAddress, 0, NULL, &dataSize, pBufferList); if (status != noErr) { ma_free(pBufferList, &pContext->allocationCallbacks); return MA_FALSE; } isSupported = MA_FALSE; if (pBufferList->mNumberBuffers > 0) { isSupported = MA_TRUE; } ma_free(pBufferList, &pContext->allocationCallbacks); return isSupported; } static ma_bool32 ma_does_AudioObject_support_playback(ma_context* pContext, AudioObjectID deviceObjectID) { return ma_does_AudioObject_support_scope(pContext, deviceObjectID, kAudioObjectPropertyScopeOutput); } static ma_bool32 ma_does_AudioObject_support_capture(ma_context* pContext, AudioObjectID deviceObjectID) { return ma_does_AudioObject_support_scope(pContext, deviceObjectID, kAudioObjectPropertyScopeInput); } static ma_result ma_get_AudioObject_stream_descriptions(ma_context* pContext, AudioObjectID deviceObjectID, ma_device_type deviceType, UInt32* pDescriptionCount, AudioStreamRangedDescription** ppDescriptions) /* NOTE: Free the returned pointer with ma_free(). */ { AudioObjectPropertyAddress propAddress; UInt32 dataSize; OSStatus status; AudioStreamRangedDescription* pDescriptions; MA_ASSERT(pContext != NULL); MA_ASSERT(pDescriptionCount != NULL); MA_ASSERT(ppDescriptions != NULL); /* TODO: Experiment with kAudioStreamPropertyAvailablePhysicalFormats instead of (or in addition to) kAudioStreamPropertyAvailableVirtualFormats. My MacBook Pro uses s24/32 format, however, which miniaudio does not currently support. */ propAddress.mSelector = kAudioStreamPropertyAvailableVirtualFormats; /*kAudioStreamPropertyAvailablePhysicalFormats;*/ propAddress.mScope = (deviceType == ma_device_type_playback) ? kAudioObjectPropertyScopeOutput : kAudioObjectPropertyScopeInput; propAddress.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT; status = ((ma_AudioObjectGetPropertyDataSize_proc)pContext->coreaudio.AudioObjectGetPropertyDataSize)(deviceObjectID, &propAddress, 0, NULL, &dataSize); if (status != noErr) { return ma_result_from_OSStatus(status); } pDescriptions = (AudioStreamRangedDescription*)ma_malloc(dataSize, &pContext->allocationCallbacks); if (pDescriptions == NULL) { return MA_OUT_OF_MEMORY; } status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(deviceObjectID, &propAddress, 0, NULL, &dataSize, pDescriptions); if (status != noErr) { ma_free(pDescriptions, &pContext->allocationCallbacks); return ma_result_from_OSStatus(status); } *pDescriptionCount = dataSize / sizeof(*pDescriptions); *ppDescriptions = pDescriptions; return MA_SUCCESS; } static ma_result ma_get_AudioObject_channel_layout(ma_context* pContext, AudioObjectID deviceObjectID, ma_device_type deviceType, AudioChannelLayout** ppChannelLayout) /* NOTE: Free the returned pointer with ma_free(). */ { AudioObjectPropertyAddress propAddress; UInt32 dataSize; OSStatus status; AudioChannelLayout* pChannelLayout; MA_ASSERT(pContext != NULL); MA_ASSERT(ppChannelLayout != NULL); *ppChannelLayout = NULL; /* Safety. */ propAddress.mSelector = kAudioDevicePropertyPreferredChannelLayout; propAddress.mScope = (deviceType == ma_device_type_playback) ? kAudioObjectPropertyScopeOutput : kAudioObjectPropertyScopeInput; propAddress.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT; status = ((ma_AudioObjectGetPropertyDataSize_proc)pContext->coreaudio.AudioObjectGetPropertyDataSize)(deviceObjectID, &propAddress, 0, NULL, &dataSize); if (status != noErr) { return ma_result_from_OSStatus(status); } pChannelLayout = (AudioChannelLayout*)ma_malloc(dataSize, &pContext->allocationCallbacks); if (pChannelLayout == NULL) { return MA_OUT_OF_MEMORY; } status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(deviceObjectID, &propAddress, 0, NULL, &dataSize, pChannelLayout); if (status != noErr) { ma_free(pChannelLayout, &pContext->allocationCallbacks); return ma_result_from_OSStatus(status); } *ppChannelLayout = pChannelLayout; return MA_SUCCESS; } static ma_result ma_get_AudioObject_channel_count(ma_context* pContext, AudioObjectID deviceObjectID, ma_device_type deviceType, ma_uint32* pChannelCount) { AudioChannelLayout* pChannelLayout; ma_result result; MA_ASSERT(pContext != NULL); MA_ASSERT(pChannelCount != NULL); *pChannelCount = 0; /* Safety. */ result = ma_get_AudioObject_channel_layout(pContext, deviceObjectID, deviceType, &pChannelLayout); if (result != MA_SUCCESS) { return result; } if (pChannelLayout->mChannelLayoutTag == kAudioChannelLayoutTag_UseChannelDescriptions) { *pChannelCount = pChannelLayout->mNumberChannelDescriptions; } else if (pChannelLayout->mChannelLayoutTag == kAudioChannelLayoutTag_UseChannelBitmap) { *pChannelCount = ma_count_set_bits(pChannelLayout->mChannelBitmap); } else { *pChannelCount = AudioChannelLayoutTag_GetNumberOfChannels(pChannelLayout->mChannelLayoutTag); } ma_free(pChannelLayout, &pContext->allocationCallbacks); return MA_SUCCESS; } #if 0 static ma_result ma_get_AudioObject_channel_map(ma_context* pContext, AudioObjectID deviceObjectID, ma_device_type deviceType, ma_channel* pChannelMap, size_t channelMapCap) { AudioChannelLayout* pChannelLayout; ma_result result; MA_ASSERT(pContext != NULL); result = ma_get_AudioObject_channel_layout(pContext, deviceObjectID, deviceType, &pChannelLayout); if (result != MA_SUCCESS) { return result; /* Rather than always failing here, would it be more robust to simply assume a default? */ } result = ma_get_channel_map_from_AudioChannelLayout(pChannelLayout, pChannelMap, channelMapCap); if (result != MA_SUCCESS) { ma_free(pChannelLayout, &pContext->allocationCallbacks); return result; } ma_free(pChannelLayout, &pContext->allocationCallbacks); return result; } #endif static ma_result ma_get_AudioObject_sample_rates(ma_context* pContext, AudioObjectID deviceObjectID, ma_device_type deviceType, UInt32* pSampleRateRangesCount, AudioValueRange** ppSampleRateRanges) /* NOTE: Free the returned pointer with ma_free(). */ { AudioObjectPropertyAddress propAddress; UInt32 dataSize; OSStatus status; AudioValueRange* pSampleRateRanges; MA_ASSERT(pContext != NULL); MA_ASSERT(pSampleRateRangesCount != NULL); MA_ASSERT(ppSampleRateRanges != NULL); /* Safety. */ *pSampleRateRangesCount = 0; *ppSampleRateRanges = NULL; propAddress.mSelector = kAudioDevicePropertyAvailableNominalSampleRates; propAddress.mScope = (deviceType == ma_device_type_playback) ? kAudioObjectPropertyScopeOutput : kAudioObjectPropertyScopeInput; propAddress.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT; status = ((ma_AudioObjectGetPropertyDataSize_proc)pContext->coreaudio.AudioObjectGetPropertyDataSize)(deviceObjectID, &propAddress, 0, NULL, &dataSize); if (status != noErr) { return ma_result_from_OSStatus(status); } pSampleRateRanges = (AudioValueRange*)ma_malloc(dataSize, &pContext->allocationCallbacks); if (pSampleRateRanges == NULL) { return MA_OUT_OF_MEMORY; } status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(deviceObjectID, &propAddress, 0, NULL, &dataSize, pSampleRateRanges); if (status != noErr) { ma_free(pSampleRateRanges, &pContext->allocationCallbacks); return ma_result_from_OSStatus(status); } *pSampleRateRangesCount = dataSize / sizeof(*pSampleRateRanges); *ppSampleRateRanges = pSampleRateRanges; return MA_SUCCESS; } #if 0 static ma_result ma_get_AudioObject_get_closest_sample_rate(ma_context* pContext, AudioObjectID deviceObjectID, ma_device_type deviceType, ma_uint32 sampleRateIn, ma_uint32* pSampleRateOut) { UInt32 sampleRateRangeCount; AudioValueRange* pSampleRateRanges; ma_result result; MA_ASSERT(pContext != NULL); MA_ASSERT(pSampleRateOut != NULL); *pSampleRateOut = 0; /* Safety. */ result = ma_get_AudioObject_sample_rates(pContext, deviceObjectID, deviceType, &sampleRateRangeCount, &pSampleRateRanges); if (result != MA_SUCCESS) { return result; } if (sampleRateRangeCount == 0) { ma_free(pSampleRateRanges, &pContext->allocationCallbacks); return MA_ERROR; /* Should never hit this case should we? */ } if (sampleRateIn == 0) { /* Search in order of miniaudio's preferred priority. */ UInt32 iMALSampleRate; for (iMALSampleRate = 0; iMALSampleRate < ma_countof(g_maStandardSampleRatePriorities); ++iMALSampleRate) { ma_uint32 malSampleRate = g_maStandardSampleRatePriorities[iMALSampleRate]; UInt32 iCASampleRate; for (iCASampleRate = 0; iCASampleRate < sampleRateRangeCount; ++iCASampleRate) { AudioValueRange caSampleRate = pSampleRateRanges[iCASampleRate]; if (caSampleRate.mMinimum <= malSampleRate && caSampleRate.mMaximum >= malSampleRate) { *pSampleRateOut = malSampleRate; ma_free(pSampleRateRanges, &pContext->allocationCallbacks); return MA_SUCCESS; } } } /* If we get here it means none of miniaudio's standard sample rates matched any of the supported sample rates from the device. In this case we just fall back to the first one reported by Core Audio. */ MA_ASSERT(sampleRateRangeCount > 0); *pSampleRateOut = pSampleRateRanges[0].mMinimum; ma_free(pSampleRateRanges, &pContext->allocationCallbacks); return MA_SUCCESS; } else { /* Find the closest match to this sample rate. */ UInt32 currentAbsoluteDifference = INT32_MAX; UInt32 iCurrentClosestRange = (UInt32)-1; UInt32 iRange; for (iRange = 0; iRange < sampleRateRangeCount; ++iRange) { if (pSampleRateRanges[iRange].mMinimum <= sampleRateIn && pSampleRateRanges[iRange].mMaximum >= sampleRateIn) { *pSampleRateOut = sampleRateIn; ma_free(pSampleRateRanges, &pContext->allocationCallbacks); return MA_SUCCESS; } else { UInt32 absoluteDifference; if (pSampleRateRanges[iRange].mMinimum > sampleRateIn) { absoluteDifference = pSampleRateRanges[iRange].mMinimum - sampleRateIn; } else { absoluteDifference = sampleRateIn - pSampleRateRanges[iRange].mMaximum; } if (currentAbsoluteDifference > absoluteDifference) { currentAbsoluteDifference = absoluteDifference; iCurrentClosestRange = iRange; } } } MA_ASSERT(iCurrentClosestRange != (UInt32)-1); *pSampleRateOut = pSampleRateRanges[iCurrentClosestRange].mMinimum; ma_free(pSampleRateRanges, &pContext->allocationCallbacks); return MA_SUCCESS; } /* Should never get here, but it would mean we weren't able to find any suitable sample rates. */ /*ma_free(pSampleRateRanges, &pContext->allocationCallbacks);*/ /*return MA_ERROR;*/ } #endif static ma_result ma_get_AudioObject_closest_buffer_size_in_frames(ma_context* pContext, AudioObjectID deviceObjectID, ma_device_type deviceType, ma_uint32 bufferSizeInFramesIn, ma_uint32* pBufferSizeInFramesOut) { AudioObjectPropertyAddress propAddress; AudioValueRange bufferSizeRange; UInt32 dataSize; OSStatus status; MA_ASSERT(pContext != NULL); MA_ASSERT(pBufferSizeInFramesOut != NULL); *pBufferSizeInFramesOut = 0; /* Safety. */ propAddress.mSelector = kAudioDevicePropertyBufferFrameSizeRange; propAddress.mScope = (deviceType == ma_device_type_playback) ? kAudioObjectPropertyScopeOutput : kAudioObjectPropertyScopeInput; propAddress.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT; dataSize = sizeof(bufferSizeRange); status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(deviceObjectID, &propAddress, 0, NULL, &dataSize, &bufferSizeRange); if (status != noErr) { return ma_result_from_OSStatus(status); } /* This is just a clamp. */ if (bufferSizeInFramesIn < bufferSizeRange.mMinimum) { *pBufferSizeInFramesOut = (ma_uint32)bufferSizeRange.mMinimum; } else if (bufferSizeInFramesIn > bufferSizeRange.mMaximum) { *pBufferSizeInFramesOut = (ma_uint32)bufferSizeRange.mMaximum; } else { *pBufferSizeInFramesOut = bufferSizeInFramesIn; } return MA_SUCCESS; } static ma_result ma_set_AudioObject_buffer_size_in_frames(ma_context* pContext, AudioObjectID deviceObjectID, ma_device_type deviceType, ma_uint32* pPeriodSizeInOut) { ma_result result; ma_uint32 chosenBufferSizeInFrames; AudioObjectPropertyAddress propAddress; UInt32 dataSize; OSStatus status; MA_ASSERT(pContext != NULL); result = ma_get_AudioObject_closest_buffer_size_in_frames(pContext, deviceObjectID, deviceType, *pPeriodSizeInOut, &chosenBufferSizeInFrames); if (result != MA_SUCCESS) { return result; } /* Try setting the size of the buffer... If this fails we just use whatever is currently set. */ propAddress.mSelector = kAudioDevicePropertyBufferFrameSize; propAddress.mScope = (deviceType == ma_device_type_playback) ? kAudioObjectPropertyScopeOutput : kAudioObjectPropertyScopeInput; propAddress.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT; ((ma_AudioObjectSetPropertyData_proc)pContext->coreaudio.AudioObjectSetPropertyData)(deviceObjectID, &propAddress, 0, NULL, sizeof(chosenBufferSizeInFrames), &chosenBufferSizeInFrames); /* Get the actual size of the buffer. */ dataSize = sizeof(*pPeriodSizeInOut); status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(deviceObjectID, &propAddress, 0, NULL, &dataSize, &chosenBufferSizeInFrames); if (status != noErr) { return ma_result_from_OSStatus(status); } *pPeriodSizeInOut = chosenBufferSizeInFrames; return MA_SUCCESS; } static ma_result ma_find_default_AudioObjectID(ma_context* pContext, ma_device_type deviceType, AudioObjectID* pDeviceObjectID) { AudioObjectPropertyAddress propAddressDefaultDevice; UInt32 defaultDeviceObjectIDSize = sizeof(AudioObjectID); AudioObjectID defaultDeviceObjectID; OSStatus status; MA_ASSERT(pContext != NULL); MA_ASSERT(pDeviceObjectID != NULL); /* Safety. */ *pDeviceObjectID = 0; propAddressDefaultDevice.mScope = kAudioObjectPropertyScopeGlobal; propAddressDefaultDevice.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT; if (deviceType == ma_device_type_playback) { propAddressDefaultDevice.mSelector = kAudioHardwarePropertyDefaultOutputDevice; } else { propAddressDefaultDevice.mSelector = kAudioHardwarePropertyDefaultInputDevice; } defaultDeviceObjectIDSize = sizeof(AudioObjectID); status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(kAudioObjectSystemObject, &propAddressDefaultDevice, 0, NULL, &defaultDeviceObjectIDSize, &defaultDeviceObjectID); if (status == noErr) { *pDeviceObjectID = defaultDeviceObjectID; return MA_SUCCESS; } /* If we get here it means we couldn't find the device. */ return MA_NO_DEVICE; } static ma_result ma_find_AudioObjectID(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, AudioObjectID* pDeviceObjectID) { MA_ASSERT(pContext != NULL); MA_ASSERT(pDeviceObjectID != NULL); /* Safety. */ *pDeviceObjectID = 0; if (pDeviceID == NULL) { /* Default device. */ return ma_find_default_AudioObjectID(pContext, deviceType, pDeviceObjectID); } else { /* Explicit device. */ UInt32 deviceCount; AudioObjectID* pDeviceObjectIDs; ma_result result; UInt32 iDevice; result = ma_get_device_object_ids__coreaudio(pContext, &deviceCount, &pDeviceObjectIDs); if (result != MA_SUCCESS) { return result; } for (iDevice = 0; iDevice < deviceCount; ++iDevice) { AudioObjectID deviceObjectID = pDeviceObjectIDs[iDevice]; char uid[256]; if (ma_get_AudioObject_uid(pContext, deviceObjectID, sizeof(uid), uid) != MA_SUCCESS) { continue; } if (deviceType == ma_device_type_playback) { if (ma_does_AudioObject_support_playback(pContext, deviceObjectID)) { if (strcmp(uid, pDeviceID->coreaudio) == 0) { *pDeviceObjectID = deviceObjectID; ma_free(pDeviceObjectIDs, &pContext->allocationCallbacks); return MA_SUCCESS; } } } else { if (ma_does_AudioObject_support_capture(pContext, deviceObjectID)) { if (strcmp(uid, pDeviceID->coreaudio) == 0) { *pDeviceObjectID = deviceObjectID; ma_free(pDeviceObjectIDs, &pContext->allocationCallbacks); return MA_SUCCESS; } } } } ma_free(pDeviceObjectIDs, &pContext->allocationCallbacks); } /* If we get here it means we couldn't find the device. */ return MA_NO_DEVICE; } static ma_result ma_find_best_format__coreaudio(ma_context* pContext, AudioObjectID deviceObjectID, ma_device_type deviceType, ma_format format, ma_uint32 channels, ma_uint32 sampleRate, const AudioStreamBasicDescription* pOrigFormat, AudioStreamBasicDescription* pFormat) { UInt32 deviceFormatDescriptionCount; AudioStreamRangedDescription* pDeviceFormatDescriptions; ma_result result; ma_uint32 desiredSampleRate; ma_uint32 desiredChannelCount; ma_format desiredFormat; AudioStreamBasicDescription bestDeviceFormatSoFar; ma_bool32 hasSupportedFormat; UInt32 iFormat; result = ma_get_AudioObject_stream_descriptions(pContext, deviceObjectID, deviceType, &deviceFormatDescriptionCount, &pDeviceFormatDescriptions); if (result != MA_SUCCESS) { return result; } desiredSampleRate = sampleRate; if (desiredSampleRate == 0) { desiredSampleRate = pOrigFormat->mSampleRate; } desiredChannelCount = channels; if (desiredChannelCount == 0) { desiredChannelCount = pOrigFormat->mChannelsPerFrame; } desiredFormat = format; if (desiredFormat == ma_format_unknown) { result = ma_format_from_AudioStreamBasicDescription(pOrigFormat, &desiredFormat); if (result != MA_SUCCESS || desiredFormat == ma_format_unknown) { desiredFormat = g_maFormatPriorities[0]; } } /* If we get here it means we don't have an exact match to what the client is asking for. We'll need to find the closest one. The next loop will check for formats that have the same sample rate to what we're asking for. If there is, we prefer that one in all cases. */ MA_ZERO_OBJECT(&bestDeviceFormatSoFar); hasSupportedFormat = MA_FALSE; for (iFormat = 0; iFormat < deviceFormatDescriptionCount; ++iFormat) { ma_format format; ma_result formatResult = ma_format_from_AudioStreamBasicDescription(&pDeviceFormatDescriptions[iFormat].mFormat, &format); if (formatResult == MA_SUCCESS && format != ma_format_unknown) { hasSupportedFormat = MA_TRUE; bestDeviceFormatSoFar = pDeviceFormatDescriptions[iFormat].mFormat; break; } } if (!hasSupportedFormat) { ma_free(pDeviceFormatDescriptions, &pContext->allocationCallbacks); return MA_FORMAT_NOT_SUPPORTED; } for (iFormat = 0; iFormat < deviceFormatDescriptionCount; ++iFormat) { AudioStreamBasicDescription thisDeviceFormat = pDeviceFormatDescriptions[iFormat].mFormat; ma_format thisSampleFormat; ma_result formatResult; ma_format bestSampleFormatSoFar; /* If the format is not supported by miniaudio we need to skip this one entirely. */ formatResult = ma_format_from_AudioStreamBasicDescription(&pDeviceFormatDescriptions[iFormat].mFormat, &thisSampleFormat); if (formatResult != MA_SUCCESS || thisSampleFormat == ma_format_unknown) { continue; /* The format is not supported by miniaudio. Skip. */ } ma_format_from_AudioStreamBasicDescription(&bestDeviceFormatSoFar, &bestSampleFormatSoFar); /* Getting here means the format is supported by miniaudio which makes this format a candidate. */ if (thisDeviceFormat.mSampleRate != desiredSampleRate) { /* The sample rate does not match, but this format could still be usable, although it's a very low priority. If the best format so far has an equal sample rate we can just ignore this one. */ if (bestDeviceFormatSoFar.mSampleRate == desiredSampleRate) { continue; /* The best sample rate so far has the same sample rate as what we requested which means it's still the best so far. Skip this format. */ } else { /* In this case, neither the best format so far nor this one have the same sample rate. Check the channel count next. */ if (thisDeviceFormat.mChannelsPerFrame != desiredChannelCount) { /* This format has a different sample rate _and_ a different channel count. */ if (bestDeviceFormatSoFar.mChannelsPerFrame == desiredChannelCount) { continue; /* No change to the best format. */ } else { /* Both this format and the best so far have different sample rates and different channel counts. Whichever has the best format is the new best. */ if (ma_get_format_priority_index(thisSampleFormat) < ma_get_format_priority_index(bestSampleFormatSoFar)) { bestDeviceFormatSoFar = thisDeviceFormat; continue; } else { continue; /* No change to the best format. */ } } } else { /* This format has a different sample rate but the desired channel count. */ if (bestDeviceFormatSoFar.mChannelsPerFrame == desiredChannelCount) { /* Both this format and the best so far have the desired channel count. Whichever has the best format is the new best. */ if (ma_get_format_priority_index(thisSampleFormat) < ma_get_format_priority_index(bestSampleFormatSoFar)) { bestDeviceFormatSoFar = thisDeviceFormat; continue; } else { continue; /* No change to the best format for now. */ } } else { /* This format has the desired channel count, but the best so far does not. We have a new best. */ bestDeviceFormatSoFar = thisDeviceFormat; continue; } } } } else { /* The sample rates match which makes this format a very high priority contender. If the best format so far has a different sample rate it needs to be replaced with this one. */ if (bestDeviceFormatSoFar.mSampleRate != desiredSampleRate) { bestDeviceFormatSoFar = thisDeviceFormat; continue; } else { /* In this case both this format and the best format so far have the same sample rate. Check the channel count next. */ if (thisDeviceFormat.mChannelsPerFrame == desiredChannelCount) { /* In this case this format has the same channel count as what the client is requesting. If the best format so far has a different count, this one becomes the new best. */ if (bestDeviceFormatSoFar.mChannelsPerFrame != desiredChannelCount) { bestDeviceFormatSoFar = thisDeviceFormat; continue; } else { /* In this case both this format and the best so far have the ideal sample rate and channel count. Check the format. */ if (thisSampleFormat == desiredFormat) { bestDeviceFormatSoFar = thisDeviceFormat; break; /* Found the exact match. */ } else { /* The formats are different. The new best format is the one with the highest priority format according to miniaudio. */ if (ma_get_format_priority_index(thisSampleFormat) < ma_get_format_priority_index(bestSampleFormatSoFar)) { bestDeviceFormatSoFar = thisDeviceFormat; continue; } else { continue; /* No change to the best format for now. */ } } } } else { /* In this case the channel count is different to what the client has requested. If the best so far has the same channel count as the requested count then it remains the best. */ if (bestDeviceFormatSoFar.mChannelsPerFrame == desiredChannelCount) { continue; } else { /* This is the case where both have the same sample rate (good) but different channel counts. Right now both have about the same priority, but we need to compare the format now. */ if (thisSampleFormat == bestSampleFormatSoFar) { if (ma_get_format_priority_index(thisSampleFormat) < ma_get_format_priority_index(bestSampleFormatSoFar)) { bestDeviceFormatSoFar = thisDeviceFormat; continue; } else { continue; /* No change to the best format for now. */ } } } } } } } *pFormat = bestDeviceFormatSoFar; ma_free(pDeviceFormatDescriptions, &pContext->allocationCallbacks); return MA_SUCCESS; } static ma_result ma_get_AudioUnit_channel_map(ma_context* pContext, AudioUnit audioUnit, ma_device_type deviceType, ma_channel* pChannelMap, size_t channelMapCap) { AudioUnitScope deviceScope; AudioUnitElement deviceBus; UInt32 channelLayoutSize; OSStatus status; AudioChannelLayout* pChannelLayout; ma_result result; MA_ASSERT(pContext != NULL); if (deviceType == ma_device_type_playback) { deviceScope = kAudioUnitScope_Input; deviceBus = MA_COREAUDIO_OUTPUT_BUS; } else { deviceScope = kAudioUnitScope_Output; deviceBus = MA_COREAUDIO_INPUT_BUS; } status = ((ma_AudioUnitGetPropertyInfo_proc)pContext->coreaudio.AudioUnitGetPropertyInfo)(audioUnit, kAudioUnitProperty_AudioChannelLayout, deviceScope, deviceBus, &channelLayoutSize, NULL); if (status != noErr) { return ma_result_from_OSStatus(status); } pChannelLayout = (AudioChannelLayout*)ma_malloc(channelLayoutSize, &pContext->allocationCallbacks); if (pChannelLayout == NULL) { return MA_OUT_OF_MEMORY; } status = ((ma_AudioUnitGetProperty_proc)pContext->coreaudio.AudioUnitGetProperty)(audioUnit, kAudioUnitProperty_AudioChannelLayout, deviceScope, deviceBus, pChannelLayout, &channelLayoutSize); if (status != noErr) { ma_free(pChannelLayout, &pContext->allocationCallbacks); return ma_result_from_OSStatus(status); } result = ma_get_channel_map_from_AudioChannelLayout(pChannelLayout, pChannelMap, channelMapCap); if (result != MA_SUCCESS) { ma_free(pChannelLayout, &pContext->allocationCallbacks); return result; } ma_free(pChannelLayout, &pContext->allocationCallbacks); return MA_SUCCESS; } #endif /* MA_APPLE_DESKTOP */ #if !defined(MA_APPLE_DESKTOP) static void ma_AVAudioSessionPortDescription_to_device_info(AVAudioSessionPortDescription* pPortDesc, ma_device_info* pInfo) { MA_ZERO_OBJECT(pInfo); ma_strncpy_s(pInfo->name, sizeof(pInfo->name), [pPortDesc.portName UTF8String], (size_t)-1); ma_strncpy_s(pInfo->id.coreaudio, sizeof(pInfo->id.coreaudio), [pPortDesc.UID UTF8String], (size_t)-1); } #endif static ma_result ma_context_enumerate_devices__coreaudio(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData) { #if defined(MA_APPLE_DESKTOP) UInt32 deviceCount; AudioObjectID* pDeviceObjectIDs; AudioObjectID defaultDeviceObjectIDPlayback; AudioObjectID defaultDeviceObjectIDCapture; ma_result result; UInt32 iDevice; ma_find_default_AudioObjectID(pContext, ma_device_type_playback, &defaultDeviceObjectIDPlayback); /* OK if this fails. */ ma_find_default_AudioObjectID(pContext, ma_device_type_capture, &defaultDeviceObjectIDCapture); /* OK if this fails. */ result = ma_get_device_object_ids__coreaudio(pContext, &deviceCount, &pDeviceObjectIDs); if (result != MA_SUCCESS) { return result; } for (iDevice = 0; iDevice < deviceCount; ++iDevice) { AudioObjectID deviceObjectID = pDeviceObjectIDs[iDevice]; ma_device_info info; MA_ZERO_OBJECT(&info); if (ma_get_AudioObject_uid(pContext, deviceObjectID, sizeof(info.id.coreaudio), info.id.coreaudio) != MA_SUCCESS) { continue; } if (ma_get_AudioObject_name(pContext, deviceObjectID, sizeof(info.name), info.name) != MA_SUCCESS) { continue; } if (ma_does_AudioObject_support_playback(pContext, deviceObjectID)) { if (deviceObjectID == defaultDeviceObjectIDPlayback) { info.isDefault = MA_TRUE; } if (!callback(pContext, ma_device_type_playback, &info, pUserData)) { break; } } if (ma_does_AudioObject_support_capture(pContext, deviceObjectID)) { if (deviceObjectID == defaultDeviceObjectIDCapture) { info.isDefault = MA_TRUE; } if (!callback(pContext, ma_device_type_capture, &info, pUserData)) { break; } } } ma_free(pDeviceObjectIDs, &pContext->allocationCallbacks); #else ma_device_info info; NSArray *pInputs = [[[AVAudioSession sharedInstance] currentRoute] inputs]; NSArray *pOutputs = [[[AVAudioSession sharedInstance] currentRoute] outputs]; for (AVAudioSessionPortDescription* pPortDesc in pOutputs) { ma_AVAudioSessionPortDescription_to_device_info(pPortDesc, &info); if (!callback(pContext, ma_device_type_playback, &info, pUserData)) { return MA_SUCCESS; } } for (AVAudioSessionPortDescription* pPortDesc in pInputs) { ma_AVAudioSessionPortDescription_to_device_info(pPortDesc, &info); if (!callback(pContext, ma_device_type_capture, &info, pUserData)) { return MA_SUCCESS; } } #endif return MA_SUCCESS; } static ma_result ma_context_get_device_info__coreaudio(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo) { ma_result result; MA_ASSERT(pContext != NULL); #if defined(MA_APPLE_DESKTOP) /* Desktop */ { AudioObjectID deviceObjectID; AudioObjectID defaultDeviceObjectID; UInt32 streamDescriptionCount; AudioStreamRangedDescription* pStreamDescriptions; UInt32 iStreamDescription; UInt32 sampleRateRangeCount; AudioValueRange* pSampleRateRanges; ma_find_default_AudioObjectID(pContext, deviceType, &defaultDeviceObjectID); /* OK if this fails. */ result = ma_find_AudioObjectID(pContext, deviceType, pDeviceID, &deviceObjectID); if (result != MA_SUCCESS) { return result; } result = ma_get_AudioObject_uid(pContext, deviceObjectID, sizeof(pDeviceInfo->id.coreaudio), pDeviceInfo->id.coreaudio); if (result != MA_SUCCESS) { return result; } result = ma_get_AudioObject_name(pContext, deviceObjectID, sizeof(pDeviceInfo->name), pDeviceInfo->name); if (result != MA_SUCCESS) { return result; } if (deviceObjectID == defaultDeviceObjectID) { pDeviceInfo->isDefault = MA_TRUE; } /* There could be a large number of permutations here. Fortunately there is only a single channel count being reported which reduces this quite a bit. For sample rates we're only reporting those that are one of miniaudio's recognized "standard" rates. If there are still more formats than can fit into our fixed sized array we'll just need to truncate them. This is unlikely and will probably only happen if some driver performs software data conversion and therefore reports every possible format and sample rate. */ pDeviceInfo->nativeDataFormatCount = 0; /* Formats. */ { ma_format uniqueFormats[ma_format_count]; ma_uint32 uniqueFormatCount = 0; ma_uint32 channels; /* Channels. */ result = ma_get_AudioObject_channel_count(pContext, deviceObjectID, deviceType, &channels); if (result != MA_SUCCESS) { return result; } /* Formats. */ result = ma_get_AudioObject_stream_descriptions(pContext, deviceObjectID, deviceType, &streamDescriptionCount, &pStreamDescriptions); if (result != MA_SUCCESS) { return result; } for (iStreamDescription = 0; iStreamDescription < streamDescriptionCount; ++iStreamDescription) { ma_format format; ma_bool32 hasFormatBeenHandled = MA_FALSE; ma_uint32 iOutputFormat; ma_uint32 iSampleRate; result = ma_format_from_AudioStreamBasicDescription(&pStreamDescriptions[iStreamDescription].mFormat, &format); if (result != MA_SUCCESS) { continue; } MA_ASSERT(format != ma_format_unknown); /* Make sure the format isn't already in the output list. */ for (iOutputFormat = 0; iOutputFormat < uniqueFormatCount; ++iOutputFormat) { if (uniqueFormats[iOutputFormat] == format) { hasFormatBeenHandled = MA_TRUE; break; } } /* If we've already handled this format just skip it. */ if (hasFormatBeenHandled) { continue; } uniqueFormats[uniqueFormatCount] = format; uniqueFormatCount += 1; /* Sample Rates */ result = ma_get_AudioObject_sample_rates(pContext, deviceObjectID, deviceType, &sampleRateRangeCount, &pSampleRateRanges); if (result != MA_SUCCESS) { return result; } /* Annoyingly Core Audio reports a sample rate range. We just get all the standard rates that are between this range. */ for (iSampleRate = 0; iSampleRate < sampleRateRangeCount; ++iSampleRate) { ma_uint32 iStandardSampleRate; for (iStandardSampleRate = 0; iStandardSampleRate < ma_countof(g_maStandardSampleRatePriorities); iStandardSampleRate += 1) { ma_uint32 standardSampleRate = g_maStandardSampleRatePriorities[iStandardSampleRate]; if (standardSampleRate >= pSampleRateRanges[iSampleRate].mMinimum && standardSampleRate <= pSampleRateRanges[iSampleRate].mMaximum) { /* We have a new data format. Add it to the list. */ pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].format = format; pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].channels = channels; pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].sampleRate = standardSampleRate; pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].flags = 0; pDeviceInfo->nativeDataFormatCount += 1; if (pDeviceInfo->nativeDataFormatCount >= ma_countof(pDeviceInfo->nativeDataFormats)) { break; /* No more room for any more formats. */ } } } } ma_free(pSampleRateRanges, &pContext->allocationCallbacks); if (pDeviceInfo->nativeDataFormatCount >= ma_countof(pDeviceInfo->nativeDataFormats)) { break; /* No more room for any more formats. */ } } ma_free(pStreamDescriptions, &pContext->allocationCallbacks); } } #else /* Mobile */ { AudioComponentDescription desc; AudioComponent component; AudioUnit audioUnit; OSStatus status; AudioUnitScope formatScope; AudioUnitElement formatElement; AudioStreamBasicDescription bestFormat; UInt32 propSize; /* We want to ensure we use a consistent device name to device enumeration. */ if (pDeviceID != NULL && pDeviceID->coreaudio[0] != '\0') { ma_bool32 found = MA_FALSE; if (deviceType == ma_device_type_playback) { NSArray *pOutputs = [[[AVAudioSession sharedInstance] currentRoute] outputs]; for (AVAudioSessionPortDescription* pPortDesc in pOutputs) { if (strcmp(pDeviceID->coreaudio, [pPortDesc.UID UTF8String]) == 0) { ma_AVAudioSessionPortDescription_to_device_info(pPortDesc, pDeviceInfo); found = MA_TRUE; break; } } } else { NSArray *pInputs = [[[AVAudioSession sharedInstance] currentRoute] inputs]; for (AVAudioSessionPortDescription* pPortDesc in pInputs) { if (strcmp(pDeviceID->coreaudio, [pPortDesc.UID UTF8String]) == 0) { ma_AVAudioSessionPortDescription_to_device_info(pPortDesc, pDeviceInfo); found = MA_TRUE; break; } } } if (!found) { return MA_DOES_NOT_EXIST; } } else { if (deviceType == ma_device_type_playback) { ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); } else { ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); } } /* Retrieving device information is more annoying on mobile than desktop. For simplicity I'm locking this down to whatever format is reported on a temporary I/O unit. The problem, however, is that this doesn't return a value for the sample rate which we need to retrieve from the AVAudioSession shared instance. */ desc.componentType = kAudioUnitType_Output; desc.componentSubType = kAudioUnitSubType_RemoteIO; desc.componentManufacturer = kAudioUnitManufacturer_Apple; desc.componentFlags = 0; desc.componentFlagsMask = 0; component = ((ma_AudioComponentFindNext_proc)pContext->coreaudio.AudioComponentFindNext)(NULL, &desc); if (component == NULL) { return MA_FAILED_TO_INIT_BACKEND; } status = ((ma_AudioComponentInstanceNew_proc)pContext->coreaudio.AudioComponentInstanceNew)(component, &audioUnit); if (status != noErr) { return ma_result_from_OSStatus(status); } formatScope = (deviceType == ma_device_type_playback) ? kAudioUnitScope_Input : kAudioUnitScope_Output; formatElement = (deviceType == ma_device_type_playback) ? MA_COREAUDIO_OUTPUT_BUS : MA_COREAUDIO_INPUT_BUS; propSize = sizeof(bestFormat); status = ((ma_AudioUnitGetProperty_proc)pContext->coreaudio.AudioUnitGetProperty)(audioUnit, kAudioUnitProperty_StreamFormat, formatScope, formatElement, &bestFormat, &propSize); if (status != noErr) { ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(audioUnit); return ma_result_from_OSStatus(status); } ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(audioUnit); audioUnit = NULL; /* Only a single format is being reported for iOS. */ pDeviceInfo->nativeDataFormatCount = 1; result = ma_format_from_AudioStreamBasicDescription(&bestFormat, &pDeviceInfo->nativeDataFormats[0].format); if (result != MA_SUCCESS) { return result; } pDeviceInfo->nativeDataFormats[0].channels = bestFormat.mChannelsPerFrame; /* It looks like Apple are wanting to push the whole AVAudioSession thing. Thus, we need to use that to determine device settings. To do this we just get the shared instance and inspect. */ @autoreleasepool { AVAudioSession* pAudioSession = [AVAudioSession sharedInstance]; MA_ASSERT(pAudioSession != NULL); pDeviceInfo->nativeDataFormats[0].sampleRate = (ma_uint32)pAudioSession.sampleRate; } } #endif (void)pDeviceInfo; /* Unused. */ return MA_SUCCESS; } static AudioBufferList* ma_allocate_AudioBufferList__coreaudio(ma_uint32 sizeInFrames, ma_format format, ma_uint32 channels, ma_stream_layout layout, const ma_allocation_callbacks* pAllocationCallbacks) { AudioBufferList* pBufferList; UInt32 audioBufferSizeInBytes; size_t allocationSize; MA_ASSERT(sizeInFrames > 0); MA_ASSERT(format != ma_format_unknown); MA_ASSERT(channels > 0); allocationSize = sizeof(AudioBufferList) - sizeof(AudioBuffer); /* Subtract sizeof(AudioBuffer) because that part is dynamically sized. */ if (layout == ma_stream_layout_interleaved) { /* Interleaved case. This is the simple case because we just have one buffer. */ allocationSize += sizeof(AudioBuffer) * 1; } else { /* Non-interleaved case. This is the more complex case because there's more than one buffer. */ allocationSize += sizeof(AudioBuffer) * channels; } allocationSize += sizeInFrames * ma_get_bytes_per_frame(format, channels); pBufferList = (AudioBufferList*)ma_malloc(allocationSize, pAllocationCallbacks); if (pBufferList == NULL) { return NULL; } audioBufferSizeInBytes = (UInt32)(sizeInFrames * ma_get_bytes_per_sample(format)); if (layout == ma_stream_layout_interleaved) { pBufferList->mNumberBuffers = 1; pBufferList->mBuffers[0].mNumberChannels = channels; pBufferList->mBuffers[0].mDataByteSize = audioBufferSizeInBytes * channels; pBufferList->mBuffers[0].mData = (ma_uint8*)pBufferList + sizeof(AudioBufferList); } else { ma_uint32 iBuffer; pBufferList->mNumberBuffers = channels; for (iBuffer = 0; iBuffer < pBufferList->mNumberBuffers; ++iBuffer) { pBufferList->mBuffers[iBuffer].mNumberChannels = 1; pBufferList->mBuffers[iBuffer].mDataByteSize = audioBufferSizeInBytes; pBufferList->mBuffers[iBuffer].mData = (ma_uint8*)pBufferList + ((sizeof(AudioBufferList) - sizeof(AudioBuffer)) + (sizeof(AudioBuffer) * channels)) + (audioBufferSizeInBytes * iBuffer); } } return pBufferList; } static ma_result ma_device_realloc_AudioBufferList__coreaudio(ma_device* pDevice, ma_uint32 sizeInFrames, ma_format format, ma_uint32 channels, ma_stream_layout layout) { MA_ASSERT(pDevice != NULL); MA_ASSERT(format != ma_format_unknown); MA_ASSERT(channels > 0); /* Only resize the buffer if necessary. */ if (pDevice->coreaudio.audioBufferCapInFrames < sizeInFrames) { AudioBufferList* pNewAudioBufferList; pNewAudioBufferList = ma_allocate_AudioBufferList__coreaudio(sizeInFrames, format, channels, layout, &pDevice->pContext->allocationCallbacks); if (pNewAudioBufferList == NULL) { return MA_OUT_OF_MEMORY; } /* At this point we'll have a new AudioBufferList and we can free the old one. */ ma_free(pDevice->coreaudio.pAudioBufferList, &pDevice->pContext->allocationCallbacks); pDevice->coreaudio.pAudioBufferList = pNewAudioBufferList; pDevice->coreaudio.audioBufferCapInFrames = sizeInFrames; } /* Getting here means the capacity of the audio is fine. */ return MA_SUCCESS; } static OSStatus ma_on_output__coreaudio(void* pUserData, AudioUnitRenderActionFlags* pActionFlags, const AudioTimeStamp* pTimeStamp, UInt32 busNumber, UInt32 frameCount, AudioBufferList* pBufferList) { ma_device* pDevice = (ma_device*)pUserData; ma_stream_layout layout; MA_ASSERT(pDevice != NULL); /*ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "INFO: Output Callback: busNumber=%d, frameCount=%d, mNumberBuffers=%d\n", (int)busNumber, (int)frameCount, (int)pBufferList->mNumberBuffers);*/ /* We need to check whether or not we are outputting interleaved or non-interleaved samples. The way we do this is slightly different for each type. */ layout = ma_stream_layout_interleaved; if (pBufferList->mBuffers[0].mNumberChannels != pDevice->playback.internalChannels) { layout = ma_stream_layout_deinterleaved; } if (layout == ma_stream_layout_interleaved) { /* For now we can assume everything is interleaved. */ UInt32 iBuffer; for (iBuffer = 0; iBuffer < pBufferList->mNumberBuffers; ++iBuffer) { if (pBufferList->mBuffers[iBuffer].mNumberChannels == pDevice->playback.internalChannels) { ma_uint32 frameCountForThisBuffer = pBufferList->mBuffers[iBuffer].mDataByteSize / ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels); if (frameCountForThisBuffer > 0) { ma_device_handle_backend_data_callback(pDevice, pBufferList->mBuffers[iBuffer].mData, NULL, frameCountForThisBuffer); } /*a_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, " frameCount=%d, mNumberChannels=%d, mDataByteSize=%d\n", (int)frameCount, (int)pBufferList->mBuffers[iBuffer].mNumberChannels, (int)pBufferList->mBuffers[iBuffer].mDataByteSize);*/ } else { /* This case is where the number of channels in the output buffer do not match our internal channels. It could mean that it's not interleaved, in which case we can't handle right now since miniaudio does not yet support non-interleaved streams. We just output silence here. */ MA_ZERO_MEMORY(pBufferList->mBuffers[iBuffer].mData, pBufferList->mBuffers[iBuffer].mDataByteSize); /*ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, " WARNING: Outputting silence. frameCount=%d, mNumberChannels=%d, mDataByteSize=%d\n", (int)frameCount, (int)pBufferList->mBuffers[iBuffer].mNumberChannels, (int)pBufferList->mBuffers[iBuffer].mDataByteSize);*/ } } } else { /* This is the deinterleaved case. We need to update each buffer in groups of internalChannels. This assumes each buffer is the same size. */ MA_ASSERT(pDevice->playback.internalChannels <= MA_MAX_CHANNELS); /* This should heve been validated at initialization time. */ /* For safety we'll check that the internal channels is a multiple of the buffer count. If it's not it means something very strange has happened and we're not going to support it. */ if ((pBufferList->mNumberBuffers % pDevice->playback.internalChannels) == 0) { ma_uint8 tempBuffer[4096]; UInt32 iBuffer; for (iBuffer = 0; iBuffer < pBufferList->mNumberBuffers; iBuffer += pDevice->playback.internalChannels) { ma_uint32 frameCountPerBuffer = pBufferList->mBuffers[iBuffer].mDataByteSize / ma_get_bytes_per_sample(pDevice->playback.internalFormat); ma_uint32 framesRemaining = frameCountPerBuffer; while (framesRemaining > 0) { void* ppDeinterleavedBuffers[MA_MAX_CHANNELS]; ma_uint32 iChannel; ma_uint32 framesToRead = sizeof(tempBuffer) / ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels); if (framesToRead > framesRemaining) { framesToRead = framesRemaining; } ma_device_handle_backend_data_callback(pDevice, tempBuffer, NULL, framesToRead); for (iChannel = 0; iChannel < pDevice->playback.internalChannels; ++iChannel) { ppDeinterleavedBuffers[iChannel] = (void*)ma_offset_ptr(pBufferList->mBuffers[iBuffer+iChannel].mData, (frameCountPerBuffer - framesRemaining) * ma_get_bytes_per_sample(pDevice->playback.internalFormat)); } ma_deinterleave_pcm_frames(pDevice->playback.internalFormat, pDevice->playback.internalChannels, framesToRead, tempBuffer, ppDeinterleavedBuffers); framesRemaining -= framesToRead; } } } } (void)pActionFlags; (void)pTimeStamp; (void)busNumber; (void)frameCount; return noErr; } static OSStatus ma_on_input__coreaudio(void* pUserData, AudioUnitRenderActionFlags* pActionFlags, const AudioTimeStamp* pTimeStamp, UInt32 busNumber, UInt32 frameCount, AudioBufferList* pUnusedBufferList) { ma_device* pDevice = (ma_device*)pUserData; AudioBufferList* pRenderedBufferList; ma_result result; ma_stream_layout layout; ma_uint32 iBuffer; OSStatus status; MA_ASSERT(pDevice != NULL); pRenderedBufferList = (AudioBufferList*)pDevice->coreaudio.pAudioBufferList; MA_ASSERT(pRenderedBufferList); /* We need to check whether or not we are outputting interleaved or non-interleaved samples. The way we do this is slightly different for each type. */ layout = ma_stream_layout_interleaved; if (pRenderedBufferList->mBuffers[0].mNumberChannels != pDevice->capture.internalChannels) { layout = ma_stream_layout_deinterleaved; } /*ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "INFO: Input Callback: busNumber=%d, frameCount=%d, mNumberBuffers=%d\n", (int)busNumber, (int)frameCount, (int)pRenderedBufferList->mNumberBuffers);*/ /* There has been a situation reported where frame count passed into this function is greater than the capacity of our capture buffer. There doesn't seem to be a reliable way to determine what the maximum frame count will be, so we need to instead resort to dynamically reallocating our buffer to ensure it's large enough to capture the number of frames requested by this callback. */ result = ma_device_realloc_AudioBufferList__coreaudio(pDevice, frameCount, pDevice->capture.internalFormat, pDevice->capture.internalChannels, layout); if (result != MA_SUCCESS) { ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "Failed to allocate AudioBufferList for capture.\n"); return noErr; } pRenderedBufferList = (AudioBufferList*)pDevice->coreaudio.pAudioBufferList; MA_ASSERT(pRenderedBufferList); /* When you call AudioUnitRender(), Core Audio tries to be helpful by setting the mDataByteSize to the number of bytes that were actually rendered. The problem with this is that the next call can fail with -50 due to the size no longer being set to the capacity of the buffer, but instead the size in bytes of the previous render. This will cause a problem when a future call to this callback specifies a larger number of frames. To work around this we need to explicitly set the size of each buffer to their respective size in bytes. */ for (iBuffer = 0; iBuffer < pRenderedBufferList->mNumberBuffers; ++iBuffer) { pRenderedBufferList->mBuffers[iBuffer].mDataByteSize = pDevice->coreaudio.audioBufferCapInFrames * ma_get_bytes_per_sample(pDevice->capture.internalFormat) * pRenderedBufferList->mBuffers[iBuffer].mNumberChannels; } status = ((ma_AudioUnitRender_proc)pDevice->pContext->coreaudio.AudioUnitRender)((AudioUnit)pDevice->coreaudio.audioUnitCapture, pActionFlags, pTimeStamp, busNumber, frameCount, pRenderedBufferList); if (status != noErr) { ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, " ERROR: AudioUnitRender() failed with %d.\n", (int)status); return status; } if (layout == ma_stream_layout_interleaved) { for (iBuffer = 0; iBuffer < pRenderedBufferList->mNumberBuffers; ++iBuffer) { if (pRenderedBufferList->mBuffers[iBuffer].mNumberChannels == pDevice->capture.internalChannels) { ma_device_handle_backend_data_callback(pDevice, NULL, pRenderedBufferList->mBuffers[iBuffer].mData, frameCount); /*ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, " mDataByteSize=%d.\n", (int)pRenderedBufferList->mBuffers[iBuffer].mDataByteSize);*/ } else { /* This case is where the number of channels in the output buffer do not match our internal channels. It could mean that it's not interleaved, in which case we can't handle right now since miniaudio does not yet support non-interleaved streams. */ ma_uint8 silentBuffer[4096]; ma_uint32 framesRemaining; MA_ZERO_MEMORY(silentBuffer, sizeof(silentBuffer)); framesRemaining = frameCount; while (framesRemaining > 0) { ma_uint32 framesToSend = sizeof(silentBuffer) / ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels); if (framesToSend > framesRemaining) { framesToSend = framesRemaining; } ma_device_handle_backend_data_callback(pDevice, NULL, silentBuffer, framesToSend); framesRemaining -= framesToSend; } /*ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, " WARNING: Outputting silence. frameCount=%d, mNumberChannels=%d, mDataByteSize=%d\n", (int)frameCount, (int)pRenderedBufferList->mBuffers[iBuffer].mNumberChannels, (int)pRenderedBufferList->mBuffers[iBuffer].mDataByteSize);*/ } } } else { /* This is the deinterleaved case. We need to interleave the audio data before sending it to the client. This assumes each buffer is the same size. */ MA_ASSERT(pDevice->capture.internalChannels <= MA_MAX_CHANNELS); /* This should have been validated at initialization time. */ /* For safety we'll check that the internal channels is a multiple of the buffer count. If it's not it means something very strange has happened and we're not going to support it. */ if ((pRenderedBufferList->mNumberBuffers % pDevice->capture.internalChannels) == 0) { ma_uint8 tempBuffer[4096]; for (iBuffer = 0; iBuffer < pRenderedBufferList->mNumberBuffers; iBuffer += pDevice->capture.internalChannels) { ma_uint32 framesRemaining = frameCount; while (framesRemaining > 0) { void* ppDeinterleavedBuffers[MA_MAX_CHANNELS]; ma_uint32 iChannel; ma_uint32 framesToSend = sizeof(tempBuffer) / ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels); if (framesToSend > framesRemaining) { framesToSend = framesRemaining; } for (iChannel = 0; iChannel < pDevice->capture.internalChannels; ++iChannel) { ppDeinterleavedBuffers[iChannel] = (void*)ma_offset_ptr(pRenderedBufferList->mBuffers[iBuffer+iChannel].mData, (frameCount - framesRemaining) * ma_get_bytes_per_sample(pDevice->capture.internalFormat)); } ma_interleave_pcm_frames(pDevice->capture.internalFormat, pDevice->capture.internalChannels, framesToSend, (const void**)ppDeinterleavedBuffers, tempBuffer); ma_device_handle_backend_data_callback(pDevice, NULL, tempBuffer, framesToSend); framesRemaining -= framesToSend; } } } } (void)pActionFlags; (void)pTimeStamp; (void)busNumber; (void)frameCount; (void)pUnusedBufferList; return noErr; } static void on_start_stop__coreaudio(void* pUserData, AudioUnit audioUnit, AudioUnitPropertyID propertyID, AudioUnitScope scope, AudioUnitElement element) { ma_device* pDevice = (ma_device*)pUserData; MA_ASSERT(pDevice != NULL); /* Don't do anything if it looks like we're just reinitializing due to a device switch. */ if (((audioUnit == pDevice->coreaudio.audioUnitPlayback) && pDevice->coreaudio.isSwitchingPlaybackDevice) || ((audioUnit == pDevice->coreaudio.audioUnitCapture) && pDevice->coreaudio.isSwitchingCaptureDevice)) { return; } /* There's been a report of a deadlock here when triggered by ma_device_uninit(). It looks like AudioUnitGetProprty (called below) and AudioComponentInstanceDispose (called in ma_device_uninit) can try waiting on the same lock. I'm going to try working around this by not calling any Core Audio APIs in the callback when the device has been stopped or uninitialized. */ if (ma_device_get_state(pDevice) == ma_device_state_uninitialized || ma_device_get_state(pDevice) == ma_device_state_stopping || ma_device_get_state(pDevice) == ma_device_state_stopped) { ma_device__on_notification_stopped(pDevice); } else { UInt32 isRunning; UInt32 isRunningSize = sizeof(isRunning); OSStatus status = ((ma_AudioUnitGetProperty_proc)pDevice->pContext->coreaudio.AudioUnitGetProperty)(audioUnit, kAudioOutputUnitProperty_IsRunning, scope, element, &isRunning, &isRunningSize); if (status != noErr) { goto done; /* Don't really know what to do in this case... just ignore it, I suppose... */ } if (!isRunning) { /* The stop event is a bit annoying in Core Audio because it will be called when we automatically switch the default device. Some scenarios to consider: 1) When the device is unplugged, this will be called _before_ the default device change notification. 2) When the device is changed via the default device change notification, this will be called _after_ the switch. For case #1, we just check if there's a new default device available. If so, we just ignore the stop event. For case #2 we check a flag. */ if (((audioUnit == pDevice->coreaudio.audioUnitPlayback) && pDevice->coreaudio.isDefaultPlaybackDevice) || ((audioUnit == pDevice->coreaudio.audioUnitCapture) && pDevice->coreaudio.isDefaultCaptureDevice)) { /* It looks like the device is switching through an external event, such as the user unplugging the device or changing the default device via the operating system's sound settings. If we're re-initializing the device, we just terminate because we want the stopping of the device to be seamless to the client (we don't want them receiving the stopped event and thinking that the device has stopped when it hasn't!). */ if (((audioUnit == pDevice->coreaudio.audioUnitPlayback) && pDevice->coreaudio.isSwitchingPlaybackDevice) || ((audioUnit == pDevice->coreaudio.audioUnitCapture) && pDevice->coreaudio.isSwitchingCaptureDevice)) { goto done; } /* Getting here means the device is not reinitializing which means it may have been unplugged. From what I can see, it looks like Core Audio will try switching to the new default device seamlessly. We need to somehow find a way to determine whether or not Core Audio will most likely be successful in switching to the new device. TODO: Try to predict if Core Audio will switch devices. If not, the stopped callback needs to be posted. */ goto done; } /* Getting here means we need to stop the device. */ ma_device__on_notification_stopped(pDevice); } } (void)propertyID; /* Unused. */ done: /* Always signal the stop event. It's possible for the "else" case to get hit which can happen during an interruption. */ ma_event_signal(&pDevice->coreaudio.stopEvent); } #if defined(MA_APPLE_DESKTOP) static ma_spinlock g_DeviceTrackingInitLock_CoreAudio = 0; /* A spinlock for mutal exclusion of the init/uninit of the global tracking data. Initialization to 0 is what we need. */ static ma_uint32 g_DeviceTrackingInitCounter_CoreAudio = 0; static ma_mutex g_DeviceTrackingMutex_CoreAudio; static ma_device** g_ppTrackedDevices_CoreAudio = NULL; static ma_uint32 g_TrackedDeviceCap_CoreAudio = 0; static ma_uint32 g_TrackedDeviceCount_CoreAudio = 0; static OSStatus ma_default_device_changed__coreaudio(AudioObjectID objectID, UInt32 addressCount, const AudioObjectPropertyAddress* pAddresses, void* pUserData) { ma_device_type deviceType; /* Not sure if I really need to check this, but it makes me feel better. */ if (addressCount == 0) { return noErr; } if (pAddresses[0].mSelector == kAudioHardwarePropertyDefaultOutputDevice) { deviceType = ma_device_type_playback; } else if (pAddresses[0].mSelector == kAudioHardwarePropertyDefaultInputDevice) { deviceType = ma_device_type_capture; } else { return noErr; /* Should never hit this. */ } ma_mutex_lock(&g_DeviceTrackingMutex_CoreAudio); { ma_uint32 iDevice; for (iDevice = 0; iDevice < g_TrackedDeviceCount_CoreAudio; iDevice += 1) { ma_result reinitResult; ma_device* pDevice; pDevice = g_ppTrackedDevices_CoreAudio[iDevice]; if (pDevice->type == deviceType || pDevice->type == ma_device_type_duplex) { if (deviceType == ma_device_type_playback) { pDevice->coreaudio.isSwitchingPlaybackDevice = MA_TRUE; reinitResult = ma_device_reinit_internal__coreaudio(pDevice, deviceType, MA_TRUE); pDevice->coreaudio.isSwitchingPlaybackDevice = MA_FALSE; } else { pDevice->coreaudio.isSwitchingCaptureDevice = MA_TRUE; reinitResult = ma_device_reinit_internal__coreaudio(pDevice, deviceType, MA_TRUE); pDevice->coreaudio.isSwitchingCaptureDevice = MA_FALSE; } if (reinitResult == MA_SUCCESS) { ma_device__post_init_setup(pDevice, deviceType); /* Restart the device if required. If this fails we need to stop the device entirely. */ if (ma_device_get_state(pDevice) == ma_device_state_started) { OSStatus status; if (deviceType == ma_device_type_playback) { status = ((ma_AudioOutputUnitStart_proc)pDevice->pContext->coreaudio.AudioOutputUnitStart)((AudioUnit)pDevice->coreaudio.audioUnitPlayback); if (status != noErr) { if (pDevice->type == ma_device_type_duplex) { ((ma_AudioOutputUnitStop_proc)pDevice->pContext->coreaudio.AudioOutputUnitStop)((AudioUnit)pDevice->coreaudio.audioUnitCapture); } ma_device__set_state(pDevice, ma_device_state_stopped); } } else if (deviceType == ma_device_type_capture) { status = ((ma_AudioOutputUnitStart_proc)pDevice->pContext->coreaudio.AudioOutputUnitStart)((AudioUnit)pDevice->coreaudio.audioUnitCapture); if (status != noErr) { if (pDevice->type == ma_device_type_duplex) { ((ma_AudioOutputUnitStop_proc)pDevice->pContext->coreaudio.AudioOutputUnitStop)((AudioUnit)pDevice->coreaudio.audioUnitPlayback); } ma_device__set_state(pDevice, ma_device_state_stopped); } } } ma_device__on_notification_rerouted(pDevice); } } } } ma_mutex_unlock(&g_DeviceTrackingMutex_CoreAudio); /* Unused parameters. */ (void)objectID; (void)pUserData; return noErr; } static ma_result ma_context__init_device_tracking__coreaudio(ma_context* pContext) { MA_ASSERT(pContext != NULL); ma_spinlock_lock(&g_DeviceTrackingInitLock_CoreAudio); { /* Don't do anything if we've already initializd device tracking. */ if (g_DeviceTrackingInitCounter_CoreAudio == 0) { AudioObjectPropertyAddress propAddress; propAddress.mScope = kAudioObjectPropertyScopeGlobal; propAddress.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT; ma_mutex_init(&g_DeviceTrackingMutex_CoreAudio); propAddress.mSelector = kAudioHardwarePropertyDefaultInputDevice; ((ma_AudioObjectAddPropertyListener_proc)pContext->coreaudio.AudioObjectAddPropertyListener)(kAudioObjectSystemObject, &propAddress, &ma_default_device_changed__coreaudio, NULL); propAddress.mSelector = kAudioHardwarePropertyDefaultOutputDevice; ((ma_AudioObjectAddPropertyListener_proc)pContext->coreaudio.AudioObjectAddPropertyListener)(kAudioObjectSystemObject, &propAddress, &ma_default_device_changed__coreaudio, NULL); } g_DeviceTrackingInitCounter_CoreAudio += 1; } ma_spinlock_unlock(&g_DeviceTrackingInitLock_CoreAudio); return MA_SUCCESS; } static ma_result ma_context__uninit_device_tracking__coreaudio(ma_context* pContext) { MA_ASSERT(pContext != NULL); ma_spinlock_lock(&g_DeviceTrackingInitLock_CoreAudio); { if (g_DeviceTrackingInitCounter_CoreAudio > 0) g_DeviceTrackingInitCounter_CoreAudio -= 1; if (g_DeviceTrackingInitCounter_CoreAudio == 0) { AudioObjectPropertyAddress propAddress; propAddress.mScope = kAudioObjectPropertyScopeGlobal; propAddress.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT; propAddress.mSelector = kAudioHardwarePropertyDefaultInputDevice; ((ma_AudioObjectRemovePropertyListener_proc)pContext->coreaudio.AudioObjectRemovePropertyListener)(kAudioObjectSystemObject, &propAddress, &ma_default_device_changed__coreaudio, NULL); propAddress.mSelector = kAudioHardwarePropertyDefaultOutputDevice; ((ma_AudioObjectRemovePropertyListener_proc)pContext->coreaudio.AudioObjectRemovePropertyListener)(kAudioObjectSystemObject, &propAddress, &ma_default_device_changed__coreaudio, NULL); /* At this point there should be no tracked devices. If not there's an error somewhere. */ if (g_ppTrackedDevices_CoreAudio != NULL) { ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_WARNING, "You have uninitialized all contexts while an associated device is still active."); ma_spinlock_unlock(&g_DeviceTrackingInitLock_CoreAudio); return MA_INVALID_OPERATION; } ma_mutex_uninit(&g_DeviceTrackingMutex_CoreAudio); } } ma_spinlock_unlock(&g_DeviceTrackingInitLock_CoreAudio); return MA_SUCCESS; } static ma_result ma_device__track__coreaudio(ma_device* pDevice) { MA_ASSERT(pDevice != NULL); ma_mutex_lock(&g_DeviceTrackingMutex_CoreAudio); { /* Allocate memory if required. */ if (g_TrackedDeviceCap_CoreAudio <= g_TrackedDeviceCount_CoreAudio) { ma_uint32 newCap; ma_device** ppNewDevices; newCap = g_TrackedDeviceCap_CoreAudio * 2; if (newCap == 0) { newCap = 1; } ppNewDevices = (ma_device**)ma_realloc(g_ppTrackedDevices_CoreAudio, sizeof(*g_ppTrackedDevices_CoreAudio)*newCap, &pDevice->pContext->allocationCallbacks); if (ppNewDevices == NULL) { ma_mutex_unlock(&g_DeviceTrackingMutex_CoreAudio); return MA_OUT_OF_MEMORY; } g_ppTrackedDevices_CoreAudio = ppNewDevices; g_TrackedDeviceCap_CoreAudio = newCap; } g_ppTrackedDevices_CoreAudio[g_TrackedDeviceCount_CoreAudio] = pDevice; g_TrackedDeviceCount_CoreAudio += 1; } ma_mutex_unlock(&g_DeviceTrackingMutex_CoreAudio); return MA_SUCCESS; } static ma_result ma_device__untrack__coreaudio(ma_device* pDevice) { MA_ASSERT(pDevice != NULL); ma_mutex_lock(&g_DeviceTrackingMutex_CoreAudio); { ma_uint32 iDevice; for (iDevice = 0; iDevice < g_TrackedDeviceCount_CoreAudio; iDevice += 1) { if (g_ppTrackedDevices_CoreAudio[iDevice] == pDevice) { /* We've found the device. We now need to remove it from the list. */ ma_uint32 jDevice; for (jDevice = iDevice; jDevice < g_TrackedDeviceCount_CoreAudio-1; jDevice += 1) { g_ppTrackedDevices_CoreAudio[jDevice] = g_ppTrackedDevices_CoreAudio[jDevice+1]; } g_TrackedDeviceCount_CoreAudio -= 1; /* If there's nothing else in the list we need to free memory. */ if (g_TrackedDeviceCount_CoreAudio == 0) { ma_free(g_ppTrackedDevices_CoreAudio, &pDevice->pContext->allocationCallbacks); g_ppTrackedDevices_CoreAudio = NULL; g_TrackedDeviceCap_CoreAudio = 0; } break; } } } ma_mutex_unlock(&g_DeviceTrackingMutex_CoreAudio); return MA_SUCCESS; } #endif #if defined(MA_APPLE_MOBILE) @interface ma_ios_notification_handler:NSObject { ma_device* m_pDevice; } @end @implementation ma_ios_notification_handler -(id)init:(ma_device*)pDevice { self = [super init]; m_pDevice = pDevice; /* For route changes. */ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handle_route_change:) name:AVAudioSessionRouteChangeNotification object:[AVAudioSession sharedInstance]]; /* For interruptions. */ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handle_interruption:) name:AVAudioSessionInterruptionNotification object:[AVAudioSession sharedInstance]]; return self; } -(void)dealloc { [self remove_handler]; #if defined(__has_feature) #if !__has_feature(objc_arc) [super dealloc]; #endif #endif } -(void)remove_handler { [[NSNotificationCenter defaultCenter] removeObserver:self name:AVAudioSessionRouteChangeNotification object:nil]; [[NSNotificationCenter defaultCenter] removeObserver:self name:AVAudioSessionInterruptionNotification object:nil]; } -(void)handle_interruption:(NSNotification*)pNotification { NSInteger type = [[[pNotification userInfo] objectForKey:AVAudioSessionInterruptionTypeKey] integerValue]; switch (type) { case AVAudioSessionInterruptionTypeBegan: { ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_INFO, "[Core Audio] Interruption: AVAudioSessionInterruptionTypeBegan\n"); /* Core Audio will have stopped the internal device automatically, but we need explicitly stop it at a higher level to ensure miniaudio-specific state is updated for consistency. */ ma_device_stop(m_pDevice); /* Fire the notification after the device has been stopped to ensure it's in the correct state when the notification handler is invoked. */ ma_device__on_notification_interruption_began(m_pDevice); } break; case AVAudioSessionInterruptionTypeEnded: { ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_INFO, "[Core Audio] Interruption: AVAudioSessionInterruptionTypeEnded\n"); ma_device__on_notification_interruption_ended(m_pDevice); } break; } } -(void)handle_route_change:(NSNotification*)pNotification { AVAudioSession* pSession = [AVAudioSession sharedInstance]; NSInteger reason = [[[pNotification userInfo] objectForKey:AVAudioSessionRouteChangeReasonKey] integerValue]; switch (reason) { case AVAudioSessionRouteChangeReasonOldDeviceUnavailable: { ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_INFO, "[Core Audio] Route Changed: AVAudioSessionRouteChangeReasonOldDeviceUnavailable\n"); } break; case AVAudioSessionRouteChangeReasonNewDeviceAvailable: { ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_INFO, "[Core Audio] Route Changed: AVAudioSessionRouteChangeReasonNewDeviceAvailable\n"); } break; case AVAudioSessionRouteChangeReasonNoSuitableRouteForCategory: { ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_INFO, "[Core Audio] Route Changed: AVAudioSessionRouteChangeReasonNoSuitableRouteForCategory\n"); } break; case AVAudioSessionRouteChangeReasonWakeFromSleep: { ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_INFO, "[Core Audio] Route Changed: AVAudioSessionRouteChangeReasonWakeFromSleep\n"); } break; case AVAudioSessionRouteChangeReasonOverride: { ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_INFO, "[Core Audio] Route Changed: AVAudioSessionRouteChangeReasonOverride\n"); } break; case AVAudioSessionRouteChangeReasonCategoryChange: { ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_INFO, "[Core Audio] Route Changed: AVAudioSessionRouteChangeReasonCategoryChange\n"); } break; case AVAudioSessionRouteChangeReasonUnknown: default: { ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_INFO, "[Core Audio] Route Changed: AVAudioSessionRouteChangeReasonUnknown\n"); } break; } ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_DEBUG, "[Core Audio] Changing Route. inputNumberChannels=%d; outputNumberOfChannels=%d\n", (int)pSession.inputNumberOfChannels, (int)pSession.outputNumberOfChannels); /* Let the application know about the route change. */ ma_device__on_notification_rerouted(m_pDevice); } @end #endif static ma_result ma_device_uninit__coreaudio(ma_device* pDevice) { MA_ASSERT(pDevice != NULL); MA_ASSERT(ma_device_get_state(pDevice) == ma_device_state_uninitialized); #if defined(MA_APPLE_DESKTOP) /* Make sure we're no longer tracking the device. It doesn't matter if we call this for a non-default device because it'll just gracefully ignore it. */ ma_device__untrack__coreaudio(pDevice); #endif #if defined(MA_APPLE_MOBILE) if (pDevice->coreaudio.pNotificationHandler != NULL) { ma_ios_notification_handler* pNotificationHandler = (MA_BRIDGE_TRANSFER ma_ios_notification_handler*)pDevice->coreaudio.pNotificationHandler; [pNotificationHandler remove_handler]; } #endif if (pDevice->coreaudio.audioUnitCapture != NULL) { ((ma_AudioComponentInstanceDispose_proc)pDevice->pContext->coreaudio.AudioComponentInstanceDispose)((AudioUnit)pDevice->coreaudio.audioUnitCapture); } if (pDevice->coreaudio.audioUnitPlayback != NULL) { ((ma_AudioComponentInstanceDispose_proc)pDevice->pContext->coreaudio.AudioComponentInstanceDispose)((AudioUnit)pDevice->coreaudio.audioUnitPlayback); } if (pDevice->coreaudio.pAudioBufferList) { ma_free(pDevice->coreaudio.pAudioBufferList, &pDevice->pContext->allocationCallbacks); } return MA_SUCCESS; } typedef struct { ma_bool32 allowNominalSampleRateChange; /* Input. */ ma_format formatIn; ma_uint32 channelsIn; ma_uint32 sampleRateIn; ma_channel channelMapIn[MA_MAX_CHANNELS]; ma_uint32 periodSizeInFramesIn; ma_uint32 periodSizeInMillisecondsIn; ma_uint32 periodsIn; ma_share_mode shareMode; ma_performance_profile performanceProfile; ma_bool32 registerStopEvent; /* Output. */ #if defined(MA_APPLE_DESKTOP) AudioObjectID deviceObjectID; #endif AudioComponent component; AudioUnit audioUnit; AudioBufferList* pAudioBufferList; /* Only used for input devices. */ ma_format formatOut; ma_uint32 channelsOut; ma_uint32 sampleRateOut; ma_channel channelMapOut[MA_MAX_CHANNELS]; ma_uint32 periodSizeInFramesOut; ma_uint32 periodsOut; char deviceName[256]; } ma_device_init_internal_data__coreaudio; static ma_result ma_device_init_internal__coreaudio(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_init_internal_data__coreaudio* pData, void* pDevice_DoNotReference) /* <-- pDevice is typed as void* intentionally so as to avoid accidentally referencing it. */ { ma_result result; OSStatus status; UInt32 enableIOFlag; AudioStreamBasicDescription bestFormat; UInt32 actualPeriodSizeInFrames; AURenderCallbackStruct callbackInfo; #if defined(MA_APPLE_DESKTOP) AudioObjectID deviceObjectID; #endif /* This API should only be used for a single device type: playback or capture. No full-duplex mode. */ if (deviceType == ma_device_type_duplex) { return MA_INVALID_ARGS; } MA_ASSERT(pContext != NULL); MA_ASSERT(deviceType == ma_device_type_playback || deviceType == ma_device_type_capture); #if defined(MA_APPLE_DESKTOP) pData->deviceObjectID = 0; #endif pData->component = NULL; pData->audioUnit = NULL; pData->pAudioBufferList = NULL; #if defined(MA_APPLE_DESKTOP) result = ma_find_AudioObjectID(pContext, deviceType, pDeviceID, &deviceObjectID); if (result != MA_SUCCESS) { return result; } pData->deviceObjectID = deviceObjectID; #endif /* Core audio doesn't really use the notion of a period so we can leave this unmodified, but not too over the top. */ pData->periodsOut = pData->periodsIn; if (pData->periodsOut == 0) { pData->periodsOut = MA_DEFAULT_PERIODS; } if (pData->periodsOut > 16) { pData->periodsOut = 16; } /* Audio unit. */ status = ((ma_AudioComponentInstanceNew_proc)pContext->coreaudio.AudioComponentInstanceNew)((AudioComponent)pContext->coreaudio.component, (AudioUnit*)&pData->audioUnit); if (status != noErr) { return ma_result_from_OSStatus(status); } /* The input/output buses need to be explicitly enabled and disabled. We set the flag based on the output unit first, then we just swap it for input. */ enableIOFlag = 1; if (deviceType == ma_device_type_capture) { enableIOFlag = 0; } status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output, MA_COREAUDIO_OUTPUT_BUS, &enableIOFlag, sizeof(enableIOFlag)); if (status != noErr) { ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); return ma_result_from_OSStatus(status); } enableIOFlag = (enableIOFlag == 0) ? 1 : 0; status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, MA_COREAUDIO_INPUT_BUS, &enableIOFlag, sizeof(enableIOFlag)); if (status != noErr) { ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); return ma_result_from_OSStatus(status); } /* Set the device to use with this audio unit. This is only used on desktop since we are using defaults on mobile. */ #if defined(MA_APPLE_DESKTOP) status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, 0, &deviceObjectID, sizeof(deviceObjectID)); if (status != noErr) { ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); return ma_result_from_OSStatus(result); } #else /* For some reason it looks like Apple is only allowing selection of the input device. There does not appear to be any way to change the default output route. I have no idea why this is like this, but for now we'll only be able to configure capture devices. */ if (pDeviceID != NULL) { if (deviceType == ma_device_type_capture) { ma_bool32 found = MA_FALSE; NSArray *pInputs = [[[AVAudioSession sharedInstance] currentRoute] inputs]; for (AVAudioSessionPortDescription* pPortDesc in pInputs) { if (strcmp(pDeviceID->coreaudio, [pPortDesc.UID UTF8String]) == 0) { [[AVAudioSession sharedInstance] setPreferredInput:pPortDesc error:nil]; found = MA_TRUE; break; } } if (found == MA_FALSE) { return MA_DOES_NOT_EXIST; } } } #endif /* Format. This is the hardest part of initialization because there's a few variables to take into account. 1) The format must be supported by the device. 2) The format must be supported miniaudio. 3) There's a priority that miniaudio prefers. Ideally we would like to use a format that's as close to the hardware as possible so we can get as close to a passthrough as possible. The most important property is the sample rate. miniaudio can do format conversion for any sample rate and channel count, but cannot do the same for the sample data format. If the sample data format is not supported by miniaudio it must be ignored completely. On mobile platforms this is a bit different. We just force the use of whatever the audio unit's current format is set to. */ { AudioStreamBasicDescription origFormat; UInt32 origFormatSize = sizeof(origFormat); AudioUnitScope formatScope = (deviceType == ma_device_type_playback) ? kAudioUnitScope_Input : kAudioUnitScope_Output; AudioUnitElement formatElement = (deviceType == ma_device_type_playback) ? MA_COREAUDIO_OUTPUT_BUS : MA_COREAUDIO_INPUT_BUS; if (deviceType == ma_device_type_playback) { status = ((ma_AudioUnitGetProperty_proc)pContext->coreaudio.AudioUnitGetProperty)(pData->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, MA_COREAUDIO_OUTPUT_BUS, &origFormat, &origFormatSize); } else { status = ((ma_AudioUnitGetProperty_proc)pContext->coreaudio.AudioUnitGetProperty)(pData->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, MA_COREAUDIO_INPUT_BUS, &origFormat, &origFormatSize); } if (status != noErr) { ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); return ma_result_from_OSStatus(status); } #if defined(MA_APPLE_DESKTOP) result = ma_find_best_format__coreaudio(pContext, deviceObjectID, deviceType, pData->formatIn, pData->channelsIn, pData->sampleRateIn, &origFormat, &bestFormat); if (result != MA_SUCCESS) { ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); return result; } /* Technical Note TN2091: Device input using the HAL Output Audio Unit https://developer.apple.com/library/archive/technotes/tn2091/_index.html This documentation says the following: The internal AudioConverter can handle any *simple* conversion. Typically, this means that a client can specify ANY variant of the PCM formats. Consequently, the device's sample rate should match the desired sample rate. If sample rate conversion is needed, it can be accomplished by buffering the input and converting the data on a separate thread with another AudioConverter. The important part here is the mention that it can handle *simple* conversions, which does *not* include sample rate. We therefore want to ensure the sample rate stays consistent. This document is specifically for input, but I'm going to play it safe and apply the same rule to output as well. I have tried going against the documentation by setting the sample rate anyway, but this just results in AudioUnitRender() returning a result code of -10863. I have also tried changing the format directly on the input scope on the input bus, but this just results in `ca_require: IsStreamFormatWritable(inScope, inElement) NotWritable` when trying to set the format. Something that does seem to work, however, has been setting the nominal sample rate on the deivce object. The problem with this, however, is that it actually changes the sample rate at the operating system level and not just the application. This could be intrusive to the user, however, so I don't think it's wise to make this the default. Instead I'm making this a configuration option. When the `coreaudio.allowNominalSampleRateChange` config option is set to true, changing the sample rate will be allowed. Otherwise it'll be fixed to the current sample rate. To check the system-defined sample rate, run the Audio MIDI Setup program that comes installed on macOS and observe how the sample rate changes as the sample rate is changed by miniaudio. */ if (pData->allowNominalSampleRateChange) { AudioValueRange sampleRateRange; AudioObjectPropertyAddress propAddress; sampleRateRange.mMinimum = bestFormat.mSampleRate; sampleRateRange.mMaximum = bestFormat.mSampleRate; propAddress.mSelector = kAudioDevicePropertyNominalSampleRate; propAddress.mScope = (deviceType == ma_device_type_playback) ? kAudioObjectPropertyScopeOutput : kAudioObjectPropertyScopeInput; propAddress.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT; status = ((ma_AudioObjectSetPropertyData_proc)pContext->coreaudio.AudioObjectSetPropertyData)(deviceObjectID, &propAddress, 0, NULL, sizeof(sampleRateRange), &sampleRateRange); if (status != noErr) { bestFormat.mSampleRate = origFormat.mSampleRate; } } else { bestFormat.mSampleRate = origFormat.mSampleRate; } status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioUnitProperty_StreamFormat, formatScope, formatElement, &bestFormat, sizeof(bestFormat)); if (status != noErr) { /* We failed to set the format, so fall back to the current format of the audio unit. */ bestFormat = origFormat; } #else bestFormat = origFormat; /* Sample rate is a little different here because for some reason kAudioUnitProperty_StreamFormat returns 0... Oh well. We need to instead try setting the sample rate to what the user has requested and then just see the results of it. Need to use some Objective-C here for this since it depends on Apple's AVAudioSession API. To do this we just get the shared AVAudioSession instance and then set it. Note that from what I can tell, it looks like the sample rate is shared between playback and capture for everything. */ @autoreleasepool { AVAudioSession* pAudioSession = [AVAudioSession sharedInstance]; MA_ASSERT(pAudioSession != NULL); [pAudioSession setPreferredSampleRate:(double)pData->sampleRateIn error:nil]; bestFormat.mSampleRate = pAudioSession.sampleRate; /* I've had a report that the channel count returned by AudioUnitGetProperty above is inconsistent with AVAudioSession outputNumberOfChannels. I'm going to try using the AVAudioSession values instead. */ if (deviceType == ma_device_type_playback) { bestFormat.mChannelsPerFrame = (UInt32)pAudioSession.outputNumberOfChannels; } if (deviceType == ma_device_type_capture) { bestFormat.mChannelsPerFrame = (UInt32)pAudioSession.inputNumberOfChannels; } } status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioUnitProperty_StreamFormat, formatScope, formatElement, &bestFormat, sizeof(bestFormat)); if (status != noErr) { ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); return ma_result_from_OSStatus(status); } #endif result = ma_format_from_AudioStreamBasicDescription(&bestFormat, &pData->formatOut); if (result != MA_SUCCESS) { ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); return result; } if (pData->formatOut == ma_format_unknown) { ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); return MA_FORMAT_NOT_SUPPORTED; } pData->channelsOut = bestFormat.mChannelsPerFrame; pData->sampleRateOut = bestFormat.mSampleRate; } /* Clamp the channel count for safety. */ if (pData->channelsOut > MA_MAX_CHANNELS) { pData->channelsOut = MA_MAX_CHANNELS; } /* Internal channel map. This is weird in my testing. If I use the AudioObject to get the channel map, the channel descriptions are set to "Unknown" for some reason. To work around this it looks like retrieving it from the AudioUnit will work. However, and this is where it gets weird, it doesn't seem to work with capture devices, nor at all on iOS... Therefore I'm going to fall back to a default assumption in these cases. */ #if defined(MA_APPLE_DESKTOP) result = ma_get_AudioUnit_channel_map(pContext, pData->audioUnit, deviceType, pData->channelMapOut, pData->channelsOut); if (result != MA_SUCCESS) { #if 0 /* Try falling back to the channel map from the AudioObject. */ result = ma_get_AudioObject_channel_map(pContext, deviceObjectID, deviceType, pData->channelMapOut, pData->channelsOut); if (result != MA_SUCCESS) { return result; } #else /* Fall back to default assumptions. */ ma_channel_map_init_standard(ma_standard_channel_map_default, pData->channelMapOut, ma_countof(pData->channelMapOut), pData->channelsOut); #endif } #else /* TODO: Figure out how to get the channel map using AVAudioSession. */ ma_channel_map_init_standard(ma_standard_channel_map_default, pData->channelMapOut, ma_countof(pData->channelMapOut), pData->channelsOut); #endif /* Buffer size. Not allowing this to be configurable on iOS. */ if (pData->periodSizeInFramesIn == 0) { if (pData->periodSizeInMillisecondsIn == 0) { if (pData->performanceProfile == ma_performance_profile_low_latency) { actualPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_LOW_LATENCY, pData->sampleRateOut); } else { actualPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_CONSERVATIVE, pData->sampleRateOut); } } else { actualPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(pData->periodSizeInMillisecondsIn, pData->sampleRateOut); } } else { actualPeriodSizeInFrames = pData->periodSizeInFramesIn; } #if defined(MA_APPLE_DESKTOP) result = ma_set_AudioObject_buffer_size_in_frames(pContext, deviceObjectID, deviceType, &actualPeriodSizeInFrames); if (result != MA_SUCCESS) { return result; } #else /* On iOS, the size of the IO buffer needs to be specified in seconds and is a floating point number. I don't trust any potential truncation errors due to converting from float to integer so I'm going to explicitly set the actual period size to the next power of 2. */ @autoreleasepool { AVAudioSession* pAudioSession = [AVAudioSession sharedInstance]; MA_ASSERT(pAudioSession != NULL); [pAudioSession setPreferredIOBufferDuration:((float)actualPeriodSizeInFrames / pAudioSession.sampleRate) error:nil]; actualPeriodSizeInFrames = ma_next_power_of_2((ma_uint32)(pAudioSession.IOBufferDuration * pAudioSession.sampleRate)); } #endif /* During testing I discovered that the buffer size can be too big. You'll get an error like this: kAudioUnitErr_TooManyFramesToProcess : inFramesToProcess=4096, mMaxFramesPerSlice=512 Note how inFramesToProcess is smaller than mMaxFramesPerSlice. To fix, we need to set kAudioUnitProperty_MaximumFramesPerSlice to that of the size of our buffer, or do it the other way around and set our buffer size to the kAudioUnitProperty_MaximumFramesPerSlice. */ status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, 0, &actualPeriodSizeInFrames, sizeof(actualPeriodSizeInFrames)); if (status != noErr) { ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); return ma_result_from_OSStatus(status); } pData->periodSizeInFramesOut = (ma_uint32)actualPeriodSizeInFrames; /* We need a buffer list if this is an input device. We render into this in the input callback. */ if (deviceType == ma_device_type_capture) { ma_bool32 isInterleaved = (bestFormat.mFormatFlags & kAudioFormatFlagIsNonInterleaved) == 0; AudioBufferList* pBufferList; pBufferList = ma_allocate_AudioBufferList__coreaudio(pData->periodSizeInFramesOut, pData->formatOut, pData->channelsOut, (isInterleaved) ? ma_stream_layout_interleaved : ma_stream_layout_deinterleaved, &pContext->allocationCallbacks); if (pBufferList == NULL) { ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); return MA_OUT_OF_MEMORY; } pData->pAudioBufferList = pBufferList; } /* Callbacks. */ callbackInfo.inputProcRefCon = pDevice_DoNotReference; if (deviceType == ma_device_type_playback) { callbackInfo.inputProc = ma_on_output__coreaudio; status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Global, 0, &callbackInfo, sizeof(callbackInfo)); if (status != noErr) { ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); return ma_result_from_OSStatus(status); } } else { callbackInfo.inputProc = ma_on_input__coreaudio; status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioOutputUnitProperty_SetInputCallback, kAudioUnitScope_Global, 0, &callbackInfo, sizeof(callbackInfo)); if (status != noErr) { ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); return ma_result_from_OSStatus(status); } } /* We need to listen for stop events. */ if (pData->registerStopEvent) { status = ((ma_AudioUnitAddPropertyListener_proc)pContext->coreaudio.AudioUnitAddPropertyListener)(pData->audioUnit, kAudioOutputUnitProperty_IsRunning, on_start_stop__coreaudio, pDevice_DoNotReference); if (status != noErr) { ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); return ma_result_from_OSStatus(status); } } /* Initialize the audio unit. */ status = ((ma_AudioUnitInitialize_proc)pContext->coreaudio.AudioUnitInitialize)(pData->audioUnit); if (status != noErr) { ma_free(pData->pAudioBufferList, &pContext->allocationCallbacks); pData->pAudioBufferList = NULL; ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit); return ma_result_from_OSStatus(status); } /* Grab the name. */ #if defined(MA_APPLE_DESKTOP) ma_get_AudioObject_name(pContext, deviceObjectID, sizeof(pData->deviceName), pData->deviceName); #else if (deviceType == ma_device_type_playback) { ma_strcpy_s(pData->deviceName, sizeof(pData->deviceName), MA_DEFAULT_PLAYBACK_DEVICE_NAME); } else { ma_strcpy_s(pData->deviceName, sizeof(pData->deviceName), MA_DEFAULT_CAPTURE_DEVICE_NAME); } #endif return result; } #if defined(MA_APPLE_DESKTOP) static ma_result ma_device_reinit_internal__coreaudio(ma_device* pDevice, ma_device_type deviceType, ma_bool32 disposePreviousAudioUnit) { ma_device_init_internal_data__coreaudio data; ma_result result; /* This should only be called for playback or capture, not duplex. */ if (deviceType == ma_device_type_duplex) { return MA_INVALID_ARGS; } data.allowNominalSampleRateChange = MA_FALSE; /* Don't change the nominal sample rate when switching devices. */ if (deviceType == ma_device_type_capture) { data.formatIn = pDevice->capture.format; data.channelsIn = pDevice->capture.channels; data.sampleRateIn = pDevice->sampleRate; MA_COPY_MEMORY(data.channelMapIn, pDevice->capture.channelMap, sizeof(pDevice->capture.channelMap)); data.shareMode = pDevice->capture.shareMode; data.performanceProfile = pDevice->coreaudio.originalPerformanceProfile; data.registerStopEvent = MA_TRUE; if (disposePreviousAudioUnit) { ((ma_AudioOutputUnitStop_proc)pDevice->pContext->coreaudio.AudioOutputUnitStop)((AudioUnit)pDevice->coreaudio.audioUnitCapture); ((ma_AudioComponentInstanceDispose_proc)pDevice->pContext->coreaudio.AudioComponentInstanceDispose)((AudioUnit)pDevice->coreaudio.audioUnitCapture); } if (pDevice->coreaudio.pAudioBufferList) { ma_free(pDevice->coreaudio.pAudioBufferList, &pDevice->pContext->allocationCallbacks); } } else if (deviceType == ma_device_type_playback) { data.formatIn = pDevice->playback.format; data.channelsIn = pDevice->playback.channels; data.sampleRateIn = pDevice->sampleRate; MA_COPY_MEMORY(data.channelMapIn, pDevice->playback.channelMap, sizeof(pDevice->playback.channelMap)); data.shareMode = pDevice->playback.shareMode; data.performanceProfile = pDevice->coreaudio.originalPerformanceProfile; data.registerStopEvent = (pDevice->type != ma_device_type_duplex); if (disposePreviousAudioUnit) { ((ma_AudioOutputUnitStop_proc)pDevice->pContext->coreaudio.AudioOutputUnitStop)((AudioUnit)pDevice->coreaudio.audioUnitPlayback); ((ma_AudioComponentInstanceDispose_proc)pDevice->pContext->coreaudio.AudioComponentInstanceDispose)((AudioUnit)pDevice->coreaudio.audioUnitPlayback); } } data.periodSizeInFramesIn = pDevice->coreaudio.originalPeriodSizeInFrames; data.periodSizeInMillisecondsIn = pDevice->coreaudio.originalPeriodSizeInMilliseconds; data.periodsIn = pDevice->coreaudio.originalPeriods; /* Need at least 3 periods for duplex. */ if (data.periodsIn < 3 && pDevice->type == ma_device_type_duplex) { data.periodsIn = 3; } result = ma_device_init_internal__coreaudio(pDevice->pContext, deviceType, NULL, &data, (void*)pDevice); if (result != MA_SUCCESS) { return result; } if (deviceType == ma_device_type_capture) { #if defined(MA_APPLE_DESKTOP) pDevice->coreaudio.deviceObjectIDCapture = (ma_uint32)data.deviceObjectID; ma_get_AudioObject_uid(pDevice->pContext, pDevice->coreaudio.deviceObjectIDCapture, sizeof(pDevice->capture.id.coreaudio), pDevice->capture.id.coreaudio); #endif pDevice->coreaudio.audioUnitCapture = (ma_ptr)data.audioUnit; pDevice->coreaudio.pAudioBufferList = (ma_ptr)data.pAudioBufferList; pDevice->coreaudio.audioBufferCapInFrames = data.periodSizeInFramesOut; pDevice->capture.internalFormat = data.formatOut; pDevice->capture.internalChannels = data.channelsOut; pDevice->capture.internalSampleRate = data.sampleRateOut; MA_COPY_MEMORY(pDevice->capture.internalChannelMap, data.channelMapOut, sizeof(data.channelMapOut)); pDevice->capture.internalPeriodSizeInFrames = data.periodSizeInFramesOut; pDevice->capture.internalPeriods = data.periodsOut; } else if (deviceType == ma_device_type_playback) { #if defined(MA_APPLE_DESKTOP) pDevice->coreaudio.deviceObjectIDPlayback = (ma_uint32)data.deviceObjectID; ma_get_AudioObject_uid(pDevice->pContext, pDevice->coreaudio.deviceObjectIDPlayback, sizeof(pDevice->playback.id.coreaudio), pDevice->playback.id.coreaudio); #endif pDevice->coreaudio.audioUnitPlayback = (ma_ptr)data.audioUnit; pDevice->playback.internalFormat = data.formatOut; pDevice->playback.internalChannels = data.channelsOut; pDevice->playback.internalSampleRate = data.sampleRateOut; MA_COPY_MEMORY(pDevice->playback.internalChannelMap, data.channelMapOut, sizeof(data.channelMapOut)); pDevice->playback.internalPeriodSizeInFrames = data.periodSizeInFramesOut; pDevice->playback.internalPeriods = data.periodsOut; } return MA_SUCCESS; } #endif /* MA_APPLE_DESKTOP */ static ma_result ma_device_init__coreaudio(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture) { ma_result result; MA_ASSERT(pDevice != NULL); MA_ASSERT(pConfig != NULL); if (pConfig->deviceType == ma_device_type_loopback) { return MA_DEVICE_TYPE_NOT_SUPPORTED; } /* No exclusive mode with the Core Audio backend for now. */ if (((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pDescriptorCapture->shareMode == ma_share_mode_exclusive) || ((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pDescriptorPlayback->shareMode == ma_share_mode_exclusive)) { return MA_SHARE_MODE_NOT_SUPPORTED; } /* Capture needs to be initialized first. */ if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { ma_device_init_internal_data__coreaudio data; data.allowNominalSampleRateChange = pConfig->coreaudio.allowNominalSampleRateChange; data.formatIn = pDescriptorCapture->format; data.channelsIn = pDescriptorCapture->channels; data.sampleRateIn = pDescriptorCapture->sampleRate; MA_COPY_MEMORY(data.channelMapIn, pDescriptorCapture->channelMap, sizeof(pDescriptorCapture->channelMap)); data.periodSizeInFramesIn = pDescriptorCapture->periodSizeInFrames; data.periodSizeInMillisecondsIn = pDescriptorCapture->periodSizeInMilliseconds; data.periodsIn = pDescriptorCapture->periodCount; data.shareMode = pDescriptorCapture->shareMode; data.performanceProfile = pConfig->performanceProfile; data.registerStopEvent = MA_TRUE; /* Need at least 3 periods for duplex. */ if (data.periodsIn < 3 && pConfig->deviceType == ma_device_type_duplex) { data.periodsIn = 3; } result = ma_device_init_internal__coreaudio(pDevice->pContext, ma_device_type_capture, pDescriptorCapture->pDeviceID, &data, (void*)pDevice); if (result != MA_SUCCESS) { return result; } pDevice->coreaudio.isDefaultCaptureDevice = (pConfig->capture.pDeviceID == NULL); #if defined(MA_APPLE_DESKTOP) pDevice->coreaudio.deviceObjectIDCapture = (ma_uint32)data.deviceObjectID; #endif pDevice->coreaudio.audioUnitCapture = (ma_ptr)data.audioUnit; pDevice->coreaudio.pAudioBufferList = (ma_ptr)data.pAudioBufferList; pDevice->coreaudio.audioBufferCapInFrames = data.periodSizeInFramesOut; pDevice->coreaudio.originalPeriodSizeInFrames = pDescriptorCapture->periodSizeInFrames; pDevice->coreaudio.originalPeriodSizeInMilliseconds = pDescriptorCapture->periodSizeInMilliseconds; pDevice->coreaudio.originalPeriods = pDescriptorCapture->periodCount; pDevice->coreaudio.originalPerformanceProfile = pConfig->performanceProfile; pDescriptorCapture->format = data.formatOut; pDescriptorCapture->channels = data.channelsOut; pDescriptorCapture->sampleRate = data.sampleRateOut; MA_COPY_MEMORY(pDescriptorCapture->channelMap, data.channelMapOut, sizeof(data.channelMapOut)); pDescriptorCapture->periodSizeInFrames = data.periodSizeInFramesOut; pDescriptorCapture->periodCount = data.periodsOut; #if defined(MA_APPLE_DESKTOP) ma_get_AudioObject_uid(pDevice->pContext, pDevice->coreaudio.deviceObjectIDCapture, sizeof(pDevice->capture.id.coreaudio), pDevice->capture.id.coreaudio); /* If we are using the default device we'll need to listen for changes to the system's default device so we can seemlessly switch the device in the background. */ if (pConfig->capture.pDeviceID == NULL) { ma_device__track__coreaudio(pDevice); } #endif } /* Playback. */ if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { ma_device_init_internal_data__coreaudio data; data.allowNominalSampleRateChange = pConfig->coreaudio.allowNominalSampleRateChange; data.formatIn = pDescriptorPlayback->format; data.channelsIn = pDescriptorPlayback->channels; data.sampleRateIn = pDescriptorPlayback->sampleRate; MA_COPY_MEMORY(data.channelMapIn, pDescriptorPlayback->channelMap, sizeof(pDescriptorPlayback->channelMap)); data.shareMode = pDescriptorPlayback->shareMode; data.performanceProfile = pConfig->performanceProfile; /* In full-duplex mode we want the playback buffer to be the same size as the capture buffer. */ if (pConfig->deviceType == ma_device_type_duplex) { data.periodSizeInFramesIn = pDescriptorCapture->periodSizeInFrames; data.periodsIn = pDescriptorCapture->periodCount; data.registerStopEvent = MA_FALSE; } else { data.periodSizeInFramesIn = pDescriptorPlayback->periodSizeInFrames; data.periodSizeInMillisecondsIn = pDescriptorPlayback->periodSizeInMilliseconds; data.periodsIn = pDescriptorPlayback->periodCount; data.registerStopEvent = MA_TRUE; } result = ma_device_init_internal__coreaudio(pDevice->pContext, ma_device_type_playback, pDescriptorPlayback->pDeviceID, &data, (void*)pDevice); if (result != MA_SUCCESS) { if (pConfig->deviceType == ma_device_type_duplex) { ((ma_AudioComponentInstanceDispose_proc)pDevice->pContext->coreaudio.AudioComponentInstanceDispose)((AudioUnit)pDevice->coreaudio.audioUnitCapture); if (pDevice->coreaudio.pAudioBufferList) { ma_free(pDevice->coreaudio.pAudioBufferList, &pDevice->pContext->allocationCallbacks); } } return result; } pDevice->coreaudio.isDefaultPlaybackDevice = (pConfig->playback.pDeviceID == NULL); #if defined(MA_APPLE_DESKTOP) pDevice->coreaudio.deviceObjectIDPlayback = (ma_uint32)data.deviceObjectID; #endif pDevice->coreaudio.audioUnitPlayback = (ma_ptr)data.audioUnit; pDevice->coreaudio.originalPeriodSizeInFrames = pDescriptorPlayback->periodSizeInFrames; pDevice->coreaudio.originalPeriodSizeInMilliseconds = pDescriptorPlayback->periodSizeInMilliseconds; pDevice->coreaudio.originalPeriods = pDescriptorPlayback->periodCount; pDevice->coreaudio.originalPerformanceProfile = pConfig->performanceProfile; pDescriptorPlayback->format = data.formatOut; pDescriptorPlayback->channels = data.channelsOut; pDescriptorPlayback->sampleRate = data.sampleRateOut; MA_COPY_MEMORY(pDescriptorPlayback->channelMap, data.channelMapOut, sizeof(data.channelMapOut)); pDescriptorPlayback->periodSizeInFrames = data.periodSizeInFramesOut; pDescriptorPlayback->periodCount = data.periodsOut; #if defined(MA_APPLE_DESKTOP) ma_get_AudioObject_uid(pDevice->pContext, pDevice->coreaudio.deviceObjectIDPlayback, sizeof(pDevice->playback.id.coreaudio), pDevice->playback.id.coreaudio); /* If we are using the default device we'll need to listen for changes to the system's default device so we can seemlessly switch the device in the background. */ if (pDescriptorPlayback->pDeviceID == NULL && (pConfig->deviceType != ma_device_type_duplex || pDescriptorCapture->pDeviceID != NULL)) { ma_device__track__coreaudio(pDevice); } #endif } /* When stopping the device, a callback is called on another thread. We need to wait for this callback before returning from ma_device_stop(). This event is used for this. */ ma_event_init(&pDevice->coreaudio.stopEvent); /* We need to detect when a route has changed so we can update the data conversion pipeline accordingly. This is done differently on non-Desktop Apple platforms. */ #if defined(MA_APPLE_MOBILE) pDevice->coreaudio.pNotificationHandler = (MA_BRIDGE_RETAINED void*)[[ma_ios_notification_handler alloc] init:pDevice]; #endif return MA_SUCCESS; } static ma_result ma_device_start__coreaudio(ma_device* pDevice) { MA_ASSERT(pDevice != NULL); if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { OSStatus status = ((ma_AudioOutputUnitStart_proc)pDevice->pContext->coreaudio.AudioOutputUnitStart)((AudioUnit)pDevice->coreaudio.audioUnitCapture); if (status != noErr) { return ma_result_from_OSStatus(status); } } if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { OSStatus status = ((ma_AudioOutputUnitStart_proc)pDevice->pContext->coreaudio.AudioOutputUnitStart)((AudioUnit)pDevice->coreaudio.audioUnitPlayback); if (status != noErr) { if (pDevice->type == ma_device_type_duplex) { ((ma_AudioOutputUnitStop_proc)pDevice->pContext->coreaudio.AudioOutputUnitStop)((AudioUnit)pDevice->coreaudio.audioUnitCapture); } return ma_result_from_OSStatus(status); } } return MA_SUCCESS; } static ma_result ma_device_stop__coreaudio(ma_device* pDevice) { MA_ASSERT(pDevice != NULL); /* It's not clear from the documentation whether or not AudioOutputUnitStop() actually drains the device or not. */ if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { OSStatus status = ((ma_AudioOutputUnitStop_proc)pDevice->pContext->coreaudio.AudioOutputUnitStop)((AudioUnit)pDevice->coreaudio.audioUnitCapture); if (status != noErr) { return ma_result_from_OSStatus(status); } } if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { OSStatus status = ((ma_AudioOutputUnitStop_proc)pDevice->pContext->coreaudio.AudioOutputUnitStop)((AudioUnit)pDevice->coreaudio.audioUnitPlayback); if (status != noErr) { return ma_result_from_OSStatus(status); } } /* We need to wait for the callback to finish before returning. */ ma_event_wait(&pDevice->coreaudio.stopEvent); return MA_SUCCESS; } static ma_result ma_context_uninit__coreaudio(ma_context* pContext) { MA_ASSERT(pContext != NULL); MA_ASSERT(pContext->backend == ma_backend_coreaudio); #if defined(MA_APPLE_MOBILE) if (!pContext->coreaudio.noAudioSessionDeactivate) { if (![[AVAudioSession sharedInstance] setActive:false error:nil]) { ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "Failed to deactivate audio session."); return MA_FAILED_TO_INIT_BACKEND; } } #endif #if !defined(MA_NO_RUNTIME_LINKING) && !defined(MA_APPLE_MOBILE) ma_dlclose(pContext, pContext->coreaudio.hAudioUnit); ma_dlclose(pContext, pContext->coreaudio.hCoreAudio); ma_dlclose(pContext, pContext->coreaudio.hCoreFoundation); #endif #if !defined(MA_APPLE_MOBILE) ma_context__uninit_device_tracking__coreaudio(pContext); #endif (void)pContext; return MA_SUCCESS; } #if defined(MA_APPLE_MOBILE) && defined(__IPHONE_12_0) static AVAudioSessionCategory ma_to_AVAudioSessionCategory(ma_ios_session_category category) { /* The "default" and "none" categories are treated different and should not be used as an input into this function. */ MA_ASSERT(category != ma_ios_session_category_default); MA_ASSERT(category != ma_ios_session_category_none); switch (category) { case ma_ios_session_category_ambient: return AVAudioSessionCategoryAmbient; case ma_ios_session_category_solo_ambient: return AVAudioSessionCategorySoloAmbient; case ma_ios_session_category_playback: return AVAudioSessionCategoryPlayback; case ma_ios_session_category_record: return AVAudioSessionCategoryRecord; case ma_ios_session_category_play_and_record: return AVAudioSessionCategoryPlayAndRecord; case ma_ios_session_category_multi_route: return AVAudioSessionCategoryMultiRoute; case ma_ios_session_category_none: return AVAudioSessionCategoryAmbient; case ma_ios_session_category_default: return AVAudioSessionCategoryAmbient; default: return AVAudioSessionCategoryAmbient; } } #endif static ma_result ma_context_init__coreaudio(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks) { #if !defined(MA_APPLE_MOBILE) ma_result result; #endif MA_ASSERT(pConfig != NULL); MA_ASSERT(pContext != NULL); #if defined(MA_APPLE_MOBILE) @autoreleasepool { AVAudioSession* pAudioSession = [AVAudioSession sharedInstance]; AVAudioSessionCategoryOptions options = pConfig->coreaudio.sessionCategoryOptions; MA_ASSERT(pAudioSession != NULL); if (pConfig->coreaudio.sessionCategory == ma_ios_session_category_default) { /* I'm going to use trial and error to determine our default session category. First we'll try PlayAndRecord. If that fails we'll try Playback and if that fails we'll try record. If all of these fail we'll just not set the category. */ #if !defined(MA_APPLE_TV) && !defined(MA_APPLE_WATCH) options |= AVAudioSessionCategoryOptionDefaultToSpeaker; #endif if ([pAudioSession setCategory: AVAudioSessionCategoryPlayAndRecord withOptions:options error:nil]) { /* Using PlayAndRecord */ } else if ([pAudioSession setCategory: AVAudioSessionCategoryPlayback withOptions:options error:nil]) { /* Using Playback */ } else if ([pAudioSession setCategory: AVAudioSessionCategoryRecord withOptions:options error:nil]) { /* Using Record */ } else { /* Leave as default? */ } } else { if (pConfig->coreaudio.sessionCategory != ma_ios_session_category_none) { #if defined(__IPHONE_12_0) if (![pAudioSession setCategory: ma_to_AVAudioSessionCategory(pConfig->coreaudio.sessionCategory) withOptions:options error:nil]) { return MA_INVALID_OPERATION; /* Failed to set session category. */ } #else /* Ignore the session category on version 11 and older, but post a warning. */ ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_WARNING, "Session category only supported in iOS 12 and newer."); #endif } } if (!pConfig->coreaudio.noAudioSessionActivate) { if (![pAudioSession setActive:true error:nil]) { ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "Failed to activate audio session."); return MA_FAILED_TO_INIT_BACKEND; } } } #endif #if !defined(MA_NO_RUNTIME_LINKING) && !defined(MA_APPLE_MOBILE) pContext->coreaudio.hCoreFoundation = ma_dlopen(pContext, "CoreFoundation.framework/CoreFoundation"); if (pContext->coreaudio.hCoreFoundation == NULL) { return MA_API_NOT_FOUND; } pContext->coreaudio.CFStringGetCString = ma_dlsym(pContext, pContext->coreaudio.hCoreFoundation, "CFStringGetCString"); pContext->coreaudio.CFRelease = ma_dlsym(pContext, pContext->coreaudio.hCoreFoundation, "CFRelease"); pContext->coreaudio.hCoreAudio = ma_dlopen(pContext, "CoreAudio.framework/CoreAudio"); if (pContext->coreaudio.hCoreAudio == NULL) { ma_dlclose(pContext, pContext->coreaudio.hCoreFoundation); return MA_API_NOT_FOUND; } pContext->coreaudio.AudioObjectGetPropertyData = ma_dlsym(pContext, pContext->coreaudio.hCoreAudio, "AudioObjectGetPropertyData"); pContext->coreaudio.AudioObjectGetPropertyDataSize = ma_dlsym(pContext, pContext->coreaudio.hCoreAudio, "AudioObjectGetPropertyDataSize"); pContext->coreaudio.AudioObjectSetPropertyData = ma_dlsym(pContext, pContext->coreaudio.hCoreAudio, "AudioObjectSetPropertyData"); pContext->coreaudio.AudioObjectAddPropertyListener = ma_dlsym(pContext, pContext->coreaudio.hCoreAudio, "AudioObjectAddPropertyListener"); pContext->coreaudio.AudioObjectRemovePropertyListener = ma_dlsym(pContext, pContext->coreaudio.hCoreAudio, "AudioObjectRemovePropertyListener"); /* It looks like Apple has moved some APIs from AudioUnit into AudioToolbox on more recent versions of macOS. They are still defined in AudioUnit, but just in case they decide to remove them from there entirely I'm going to implement a fallback. The way it'll work is that it'll first try AudioUnit, and if the required symbols are not present there we'll fall back to AudioToolbox. */ pContext->coreaudio.hAudioUnit = ma_dlopen(pContext, "AudioUnit.framework/AudioUnit"); if (pContext->coreaudio.hAudioUnit == NULL) { ma_dlclose(pContext, pContext->coreaudio.hCoreAudio); ma_dlclose(pContext, pContext->coreaudio.hCoreFoundation); return MA_API_NOT_FOUND; } if (ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioComponentFindNext") == NULL) { /* Couldn't find the required symbols in AudioUnit, so fall back to AudioToolbox. */ ma_dlclose(pContext, pContext->coreaudio.hAudioUnit); pContext->coreaudio.hAudioUnit = ma_dlopen(pContext, "AudioToolbox.framework/AudioToolbox"); if (pContext->coreaudio.hAudioUnit == NULL) { ma_dlclose(pContext, pContext->coreaudio.hCoreAudio); ma_dlclose(pContext, pContext->coreaudio.hCoreFoundation); return MA_API_NOT_FOUND; } } pContext->coreaudio.AudioComponentFindNext = ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioComponentFindNext"); pContext->coreaudio.AudioComponentInstanceDispose = ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioComponentInstanceDispose"); pContext->coreaudio.AudioComponentInstanceNew = ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioComponentInstanceNew"); pContext->coreaudio.AudioOutputUnitStart = ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioOutputUnitStart"); pContext->coreaudio.AudioOutputUnitStop = ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioOutputUnitStop"); pContext->coreaudio.AudioUnitAddPropertyListener = ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioUnitAddPropertyListener"); pContext->coreaudio.AudioUnitGetPropertyInfo = ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioUnitGetPropertyInfo"); pContext->coreaudio.AudioUnitGetProperty = ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioUnitGetProperty"); pContext->coreaudio.AudioUnitSetProperty = ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioUnitSetProperty"); pContext->coreaudio.AudioUnitInitialize = ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioUnitInitialize"); pContext->coreaudio.AudioUnitRender = ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioUnitRender"); #else pContext->coreaudio.CFStringGetCString = (ma_proc)CFStringGetCString; pContext->coreaudio.CFRelease = (ma_proc)CFRelease; #if defined(MA_APPLE_DESKTOP) pContext->coreaudio.AudioObjectGetPropertyData = (ma_proc)AudioObjectGetPropertyData; pContext->coreaudio.AudioObjectGetPropertyDataSize = (ma_proc)AudioObjectGetPropertyDataSize; pContext->coreaudio.AudioObjectSetPropertyData = (ma_proc)AudioObjectSetPropertyData; pContext->coreaudio.AudioObjectAddPropertyListener = (ma_proc)AudioObjectAddPropertyListener; pContext->coreaudio.AudioObjectRemovePropertyListener = (ma_proc)AudioObjectRemovePropertyListener; #endif pContext->coreaudio.AudioComponentFindNext = (ma_proc)AudioComponentFindNext; pContext->coreaudio.AudioComponentInstanceDispose = (ma_proc)AudioComponentInstanceDispose; pContext->coreaudio.AudioComponentInstanceNew = (ma_proc)AudioComponentInstanceNew; pContext->coreaudio.AudioOutputUnitStart = (ma_proc)AudioOutputUnitStart; pContext->coreaudio.AudioOutputUnitStop = (ma_proc)AudioOutputUnitStop; pContext->coreaudio.AudioUnitAddPropertyListener = (ma_proc)AudioUnitAddPropertyListener; pContext->coreaudio.AudioUnitGetPropertyInfo = (ma_proc)AudioUnitGetPropertyInfo; pContext->coreaudio.AudioUnitGetProperty = (ma_proc)AudioUnitGetProperty; pContext->coreaudio.AudioUnitSetProperty = (ma_proc)AudioUnitSetProperty; pContext->coreaudio.AudioUnitInitialize = (ma_proc)AudioUnitInitialize; pContext->coreaudio.AudioUnitRender = (ma_proc)AudioUnitRender; #endif /* Audio component. */ { AudioComponentDescription desc; desc.componentType = kAudioUnitType_Output; #if defined(MA_APPLE_DESKTOP) desc.componentSubType = kAudioUnitSubType_HALOutput; #else desc.componentSubType = kAudioUnitSubType_RemoteIO; #endif desc.componentManufacturer = kAudioUnitManufacturer_Apple; desc.componentFlags = 0; desc.componentFlagsMask = 0; pContext->coreaudio.component = ((ma_AudioComponentFindNext_proc)pContext->coreaudio.AudioComponentFindNext)(NULL, &desc); if (pContext->coreaudio.component == NULL) { #if !defined(MA_NO_RUNTIME_LINKING) && !defined(MA_APPLE_MOBILE) ma_dlclose(pContext, pContext->coreaudio.hAudioUnit); ma_dlclose(pContext, pContext->coreaudio.hCoreAudio); ma_dlclose(pContext, pContext->coreaudio.hCoreFoundation); #endif return MA_FAILED_TO_INIT_BACKEND; } } #if !defined(MA_APPLE_MOBILE) result = ma_context__init_device_tracking__coreaudio(pContext); if (result != MA_SUCCESS) { #if !defined(MA_NO_RUNTIME_LINKING) && !defined(MA_APPLE_MOBILE) ma_dlclose(pContext, pContext->coreaudio.hAudioUnit); ma_dlclose(pContext, pContext->coreaudio.hCoreAudio); ma_dlclose(pContext, pContext->coreaudio.hCoreFoundation); #endif return result; } #endif pContext->coreaudio.noAudioSessionDeactivate = pConfig->coreaudio.noAudioSessionDeactivate; pCallbacks->onContextInit = ma_context_init__coreaudio; pCallbacks->onContextUninit = ma_context_uninit__coreaudio; pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__coreaudio; pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__coreaudio; pCallbacks->onDeviceInit = ma_device_init__coreaudio; pCallbacks->onDeviceUninit = ma_device_uninit__coreaudio; pCallbacks->onDeviceStart = ma_device_start__coreaudio; pCallbacks->onDeviceStop = ma_device_stop__coreaudio; pCallbacks->onDeviceRead = NULL; pCallbacks->onDeviceWrite = NULL; pCallbacks->onDeviceDataLoop = NULL; return MA_SUCCESS; } #endif /* Core Audio */ /****************************************************************************** sndio Backend ******************************************************************************/ #ifdef MA_HAS_SNDIO #include /* Only supporting OpenBSD. This did not work very well at all on FreeBSD when I tried it. Not sure if this is due to miniaudio's implementation or if it's some kind of system configuration issue, but basically the default device just doesn't emit any sound, or at times you'll hear tiny pieces. I will consider enabling this when there's demand for it or if I can get it tested and debugged more thoroughly. */ #if 0 #if defined(__NetBSD__) || defined(__OpenBSD__) #include #endif #if defined(__FreeBSD__) || defined(__DragonFly__) #include #endif #endif #define MA_SIO_DEVANY "default" #define MA_SIO_PLAY 1 #define MA_SIO_REC 2 #define MA_SIO_NENC 8 #define MA_SIO_NCHAN 8 #define MA_SIO_NRATE 16 #define MA_SIO_NCONF 4 struct ma_sio_hdl; /* <-- Opaque */ struct ma_sio_par { unsigned int bits; unsigned int bps; unsigned int sig; unsigned int le; unsigned int msb; unsigned int rchan; unsigned int pchan; unsigned int rate; unsigned int bufsz; unsigned int xrun; unsigned int round; unsigned int appbufsz; int __pad[3]; unsigned int __magic; }; struct ma_sio_enc { unsigned int bits; unsigned int bps; unsigned int sig; unsigned int le; unsigned int msb; }; struct ma_sio_conf { unsigned int enc; unsigned int rchan; unsigned int pchan; unsigned int rate; }; struct ma_sio_cap { struct ma_sio_enc enc[MA_SIO_NENC]; unsigned int rchan[MA_SIO_NCHAN]; unsigned int pchan[MA_SIO_NCHAN]; unsigned int rate[MA_SIO_NRATE]; int __pad[7]; unsigned int nconf; struct ma_sio_conf confs[MA_SIO_NCONF]; }; typedef struct ma_sio_hdl* (* ma_sio_open_proc) (const char*, unsigned int, int); typedef void (* ma_sio_close_proc) (struct ma_sio_hdl*); typedef int (* ma_sio_setpar_proc) (struct ma_sio_hdl*, struct ma_sio_par*); typedef int (* ma_sio_getpar_proc) (struct ma_sio_hdl*, struct ma_sio_par*); typedef int (* ma_sio_getcap_proc) (struct ma_sio_hdl*, struct ma_sio_cap*); typedef size_t (* ma_sio_write_proc) (struct ma_sio_hdl*, const void*, size_t); typedef size_t (* ma_sio_read_proc) (struct ma_sio_hdl*, void*, size_t); typedef int (* ma_sio_start_proc) (struct ma_sio_hdl*); typedef int (* ma_sio_stop_proc) (struct ma_sio_hdl*); typedef int (* ma_sio_initpar_proc)(struct ma_sio_par*); static ma_uint32 ma_get_standard_sample_rate_priority_index__sndio(ma_uint32 sampleRate) /* Lower = higher priority */ { ma_uint32 i; for (i = 0; i < ma_countof(g_maStandardSampleRatePriorities); ++i) { if (g_maStandardSampleRatePriorities[i] == sampleRate) { return i; } } return (ma_uint32)-1; } static ma_format ma_format_from_sio_enc__sndio(unsigned int bits, unsigned int bps, unsigned int sig, unsigned int le, unsigned int msb) { /* We only support native-endian right now. */ if ((ma_is_little_endian() && le == 0) || (ma_is_big_endian() && le == 1)) { return ma_format_unknown; } if (bits == 8 && bps == 1 && sig == 0) { return ma_format_u8; } if (bits == 16 && bps == 2 && sig == 1) { return ma_format_s16; } if (bits == 24 && bps == 3 && sig == 1) { return ma_format_s24; } if (bits == 24 && bps == 4 && sig == 1 && msb == 0) { /*return ma_format_s24_32;*/ } if (bits == 32 && bps == 4 && sig == 1) { return ma_format_s32; } return ma_format_unknown; } static ma_format ma_find_best_format_from_sio_cap__sndio(struct ma_sio_cap* caps) { ma_format bestFormat; unsigned int iConfig; MA_ASSERT(caps != NULL); bestFormat = ma_format_unknown; for (iConfig = 0; iConfig < caps->nconf; iConfig += 1) { unsigned int iEncoding; for (iEncoding = 0; iEncoding < MA_SIO_NENC; iEncoding += 1) { unsigned int bits; unsigned int bps; unsigned int sig; unsigned int le; unsigned int msb; ma_format format; if ((caps->confs[iConfig].enc & (1UL << iEncoding)) == 0) { continue; } bits = caps->enc[iEncoding].bits; bps = caps->enc[iEncoding].bps; sig = caps->enc[iEncoding].sig; le = caps->enc[iEncoding].le; msb = caps->enc[iEncoding].msb; format = ma_format_from_sio_enc__sndio(bits, bps, sig, le, msb); if (format == ma_format_unknown) { continue; /* Format not supported. */ } if (bestFormat == ma_format_unknown) { bestFormat = format; } else { if (ma_get_format_priority_index(bestFormat) > ma_get_format_priority_index(format)) { /* <-- Lower = better. */ bestFormat = format; } } } } return bestFormat; } static ma_uint32 ma_find_best_channels_from_sio_cap__sndio(struct ma_sio_cap* caps, ma_device_type deviceType, ma_format requiredFormat) { ma_uint32 maxChannels; unsigned int iConfig; MA_ASSERT(caps != NULL); MA_ASSERT(requiredFormat != ma_format_unknown); /* Just pick whatever configuration has the most channels. */ maxChannels = 0; for (iConfig = 0; iConfig < caps->nconf; iConfig += 1) { /* The encoding should be of requiredFormat. */ unsigned int iEncoding; for (iEncoding = 0; iEncoding < MA_SIO_NENC; iEncoding += 1) { unsigned int iChannel; unsigned int bits; unsigned int bps; unsigned int sig; unsigned int le; unsigned int msb; ma_format format; if ((caps->confs[iConfig].enc & (1UL << iEncoding)) == 0) { continue; } bits = caps->enc[iEncoding].bits; bps = caps->enc[iEncoding].bps; sig = caps->enc[iEncoding].sig; le = caps->enc[iEncoding].le; msb = caps->enc[iEncoding].msb; format = ma_format_from_sio_enc__sndio(bits, bps, sig, le, msb); if (format != requiredFormat) { continue; } /* Getting here means the format is supported. Iterate over each channel count and grab the biggest one. */ for (iChannel = 0; iChannel < MA_SIO_NCHAN; iChannel += 1) { unsigned int chan = 0; unsigned int channels; if (deviceType == ma_device_type_playback) { chan = caps->confs[iConfig].pchan; } else { chan = caps->confs[iConfig].rchan; } if ((chan & (1UL << iChannel)) == 0) { continue; } if (deviceType == ma_device_type_playback) { channels = caps->pchan[iChannel]; } else { channels = caps->rchan[iChannel]; } if (maxChannels < channels) { maxChannels = channels; } } } } return maxChannels; } static ma_uint32 ma_find_best_sample_rate_from_sio_cap__sndio(struct ma_sio_cap* caps, ma_device_type deviceType, ma_format requiredFormat, ma_uint32 requiredChannels) { ma_uint32 firstSampleRate; ma_uint32 bestSampleRate; unsigned int iConfig; MA_ASSERT(caps != NULL); MA_ASSERT(requiredFormat != ma_format_unknown); MA_ASSERT(requiredChannels > 0); MA_ASSERT(requiredChannels <= MA_MAX_CHANNELS); firstSampleRate = 0; /* <-- If the device does not support a standard rate we'll fall back to the first one that's found. */ bestSampleRate = 0; for (iConfig = 0; iConfig < caps->nconf; iConfig += 1) { /* The encoding should be of requiredFormat. */ unsigned int iEncoding; for (iEncoding = 0; iEncoding < MA_SIO_NENC; iEncoding += 1) { unsigned int iChannel; unsigned int bits; unsigned int bps; unsigned int sig; unsigned int le; unsigned int msb; ma_format format; if ((caps->confs[iConfig].enc & (1UL << iEncoding)) == 0) { continue; } bits = caps->enc[iEncoding].bits; bps = caps->enc[iEncoding].bps; sig = caps->enc[iEncoding].sig; le = caps->enc[iEncoding].le; msb = caps->enc[iEncoding].msb; format = ma_format_from_sio_enc__sndio(bits, bps, sig, le, msb); if (format != requiredFormat) { continue; } /* Getting here means the format is supported. Iterate over each channel count and grab the biggest one. */ for (iChannel = 0; iChannel < MA_SIO_NCHAN; iChannel += 1) { unsigned int chan = 0; unsigned int channels; unsigned int iRate; if (deviceType == ma_device_type_playback) { chan = caps->confs[iConfig].pchan; } else { chan = caps->confs[iConfig].rchan; } if ((chan & (1UL << iChannel)) == 0) { continue; } if (deviceType == ma_device_type_playback) { channels = caps->pchan[iChannel]; } else { channels = caps->rchan[iChannel]; } if (channels != requiredChannels) { continue; } /* Getting here means we have found a compatible encoding/channel pair. */ for (iRate = 0; iRate < MA_SIO_NRATE; iRate += 1) { ma_uint32 rate = (ma_uint32)caps->rate[iRate]; ma_uint32 ratePriority; if (firstSampleRate == 0) { firstSampleRate = rate; } /* Disregard this rate if it's not a standard one. */ ratePriority = ma_get_standard_sample_rate_priority_index__sndio(rate); if (ratePriority == (ma_uint32)-1) { continue; } if (ma_get_standard_sample_rate_priority_index__sndio(bestSampleRate) > ratePriority) { /* Lower = better. */ bestSampleRate = rate; } } } } } /* If a standard sample rate was not found just fall back to the first one that was iterated. */ if (bestSampleRate == 0) { bestSampleRate = firstSampleRate; } return bestSampleRate; } static ma_result ma_context_enumerate_devices__sndio(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData) { ma_bool32 isTerminating = MA_FALSE; struct ma_sio_hdl* handle; MA_ASSERT(pContext != NULL); MA_ASSERT(callback != NULL); /* sndio doesn't seem to have a good device enumeration API, so I'm therefore only enumerating over default devices for now. */ /* Playback. */ if (!isTerminating) { handle = ((ma_sio_open_proc)pContext->sndio.sio_open)(MA_SIO_DEVANY, MA_SIO_PLAY, 0); if (handle != NULL) { /* Supports playback. */ ma_device_info deviceInfo; MA_ZERO_OBJECT(&deviceInfo); ma_strcpy_s(deviceInfo.id.sndio, sizeof(deviceInfo.id.sndio), MA_SIO_DEVANY); ma_strcpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME); isTerminating = !callback(pContext, ma_device_type_playback, &deviceInfo, pUserData); ((ma_sio_close_proc)pContext->sndio.sio_close)(handle); } } /* Capture. */ if (!isTerminating) { handle = ((ma_sio_open_proc)pContext->sndio.sio_open)(MA_SIO_DEVANY, MA_SIO_REC, 0); if (handle != NULL) { /* Supports capture. */ ma_device_info deviceInfo; MA_ZERO_OBJECT(&deviceInfo); ma_strcpy_s(deviceInfo.id.sndio, sizeof(deviceInfo.id.sndio), "default"); ma_strcpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_CAPTURE_DEVICE_NAME); isTerminating = !callback(pContext, ma_device_type_capture, &deviceInfo, pUserData); ((ma_sio_close_proc)pContext->sndio.sio_close)(handle); } } return MA_SUCCESS; } static ma_result ma_context_get_device_info__sndio(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo) { char devid[256]; struct ma_sio_hdl* handle; struct ma_sio_cap caps; unsigned int iConfig; MA_ASSERT(pContext != NULL); /* We need to open the device before we can get information about it. */ if (pDeviceID == NULL) { ma_strcpy_s(devid, sizeof(devid), MA_SIO_DEVANY); ma_strcpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), (deviceType == ma_device_type_playback) ? MA_DEFAULT_PLAYBACK_DEVICE_NAME : MA_DEFAULT_CAPTURE_DEVICE_NAME); } else { ma_strcpy_s(devid, sizeof(devid), pDeviceID->sndio); ma_strcpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), devid); } handle = ((ma_sio_open_proc)pContext->sndio.sio_open)(devid, (deviceType == ma_device_type_playback) ? MA_SIO_PLAY : MA_SIO_REC, 0); if (handle == NULL) { return MA_NO_DEVICE; } if (((ma_sio_getcap_proc)pContext->sndio.sio_getcap)(handle, &caps) == 0) { return MA_ERROR; } pDeviceInfo->nativeDataFormatCount = 0; for (iConfig = 0; iConfig < caps.nconf; iConfig += 1) { /* The main thing we care about is that the encoding is supported by miniaudio. If it is, we want to give preference to some formats over others. */ unsigned int iEncoding; unsigned int iChannel; unsigned int iRate; for (iEncoding = 0; iEncoding < MA_SIO_NENC; iEncoding += 1) { unsigned int bits; unsigned int bps; unsigned int sig; unsigned int le; unsigned int msb; ma_format format; if ((caps.confs[iConfig].enc & (1UL << iEncoding)) == 0) { continue; } bits = caps.enc[iEncoding].bits; bps = caps.enc[iEncoding].bps; sig = caps.enc[iEncoding].sig; le = caps.enc[iEncoding].le; msb = caps.enc[iEncoding].msb; format = ma_format_from_sio_enc__sndio(bits, bps, sig, le, msb); if (format == ma_format_unknown) { continue; /* Format not supported. */ } /* Channels. */ for (iChannel = 0; iChannel < MA_SIO_NCHAN; iChannel += 1) { unsigned int chan = 0; unsigned int channels; if (deviceType == ma_device_type_playback) { chan = caps.confs[iConfig].pchan; } else { chan = caps.confs[iConfig].rchan; } if ((chan & (1UL << iChannel)) == 0) { continue; } if (deviceType == ma_device_type_playback) { channels = caps.pchan[iChannel]; } else { channels = caps.rchan[iChannel]; } /* Sample Rates. */ for (iRate = 0; iRate < MA_SIO_NRATE; iRate += 1) { if ((caps.confs[iConfig].rate & (1UL << iRate)) != 0) { ma_device_info_add_native_data_format(pDeviceInfo, format, channels, caps.rate[iRate], 0); } } } } } ((ma_sio_close_proc)pContext->sndio.sio_close)(handle); return MA_SUCCESS; } static ma_result ma_device_uninit__sndio(ma_device* pDevice) { MA_ASSERT(pDevice != NULL); if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { ((ma_sio_close_proc)pDevice->pContext->sndio.sio_close)((struct ma_sio_hdl*)pDevice->sndio.handleCapture); } if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { ((ma_sio_close_proc)pDevice->pContext->sndio.sio_close)((struct ma_sio_hdl*)pDevice->sndio.handlePlayback); } return MA_SUCCESS; } static ma_result ma_device_init_handle__sndio(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptor, ma_device_type deviceType) { const char* pDeviceName; ma_ptr handle; int openFlags = 0; struct ma_sio_cap caps; struct ma_sio_par par; const ma_device_id* pDeviceID; ma_format format; ma_uint32 channels; ma_uint32 sampleRate; ma_format internalFormat; ma_uint32 internalChannels; ma_uint32 internalSampleRate; ma_uint32 internalPeriodSizeInFrames; ma_uint32 internalPeriods; MA_ASSERT(pConfig != NULL); MA_ASSERT(deviceType != ma_device_type_duplex); MA_ASSERT(pDevice != NULL); if (deviceType == ma_device_type_capture) { openFlags = MA_SIO_REC; } else { openFlags = MA_SIO_PLAY; } pDeviceID = pDescriptor->pDeviceID; format = pDescriptor->format; channels = pDescriptor->channels; sampleRate = pDescriptor->sampleRate; pDeviceName = MA_SIO_DEVANY; if (pDeviceID != NULL) { pDeviceName = pDeviceID->sndio; } handle = (ma_ptr)((ma_sio_open_proc)pDevice->pContext->sndio.sio_open)(pDeviceName, openFlags, 0); if (handle == NULL) { ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[sndio] Failed to open device."); return MA_FAILED_TO_OPEN_BACKEND_DEVICE; } /* We need to retrieve the device caps to determine the most appropriate format to use. */ if (((ma_sio_getcap_proc)pDevice->pContext->sndio.sio_getcap)((struct ma_sio_hdl*)handle, &caps) == 0) { ((ma_sio_close_proc)pDevice->pContext->sndio.sio_close)((struct ma_sio_hdl*)handle); ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[sndio] Failed to retrieve device caps."); return MA_ERROR; } /* Note: sndio reports a huge range of available channels. This is inconvenient for us because there's no real way, as far as I can tell, to get the _actual_ channel count of the device. I'm therefore restricting this to the requested channels, regardless of whether or not the default channel count is requested. For hardware devices, I'm suspecting only a single channel count will be reported and we can safely use the value returned by ma_find_best_channels_from_sio_cap__sndio(). */ if (deviceType == ma_device_type_capture) { if (format == ma_format_unknown) { format = ma_find_best_format_from_sio_cap__sndio(&caps); } if (channels == 0) { if (strlen(pDeviceName) > strlen("rsnd/") && strncmp(pDeviceName, "rsnd/", strlen("rsnd/")) == 0) { channels = ma_find_best_channels_from_sio_cap__sndio(&caps, deviceType, format); } else { channels = MA_DEFAULT_CHANNELS; } } } else { if (format == ma_format_unknown) { format = ma_find_best_format_from_sio_cap__sndio(&caps); } if (channels == 0) { if (strlen(pDeviceName) > strlen("rsnd/") && strncmp(pDeviceName, "rsnd/", strlen("rsnd/")) == 0) { channels = ma_find_best_channels_from_sio_cap__sndio(&caps, deviceType, format); } else { channels = MA_DEFAULT_CHANNELS; } } } if (sampleRate == 0) { sampleRate = ma_find_best_sample_rate_from_sio_cap__sndio(&caps, pConfig->deviceType, format, channels); } ((ma_sio_initpar_proc)pDevice->pContext->sndio.sio_initpar)(&par); par.msb = 0; par.le = ma_is_little_endian(); switch (format) { case ma_format_u8: { par.bits = 8; par.bps = 1; par.sig = 0; } break; case ma_format_s24: { par.bits = 24; par.bps = 3; par.sig = 1; } break; case ma_format_s32: { par.bits = 32; par.bps = 4; par.sig = 1; } break; case ma_format_s16: case ma_format_f32: case ma_format_unknown: default: { par.bits = 16; par.bps = 2; par.sig = 1; } break; } if (deviceType == ma_device_type_capture) { par.rchan = channels; } else { par.pchan = channels; } par.rate = sampleRate; internalPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptor, par.rate, pConfig->performanceProfile); par.round = internalPeriodSizeInFrames; par.appbufsz = par.round * pDescriptor->periodCount; if (((ma_sio_setpar_proc)pDevice->pContext->sndio.sio_setpar)((struct ma_sio_hdl*)handle, &par) == 0) { ((ma_sio_close_proc)pDevice->pContext->sndio.sio_close)((struct ma_sio_hdl*)handle); ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[sndio] Failed to set buffer size."); return MA_ERROR; } if (((ma_sio_getpar_proc)pDevice->pContext->sndio.sio_getpar)((struct ma_sio_hdl*)handle, &par) == 0) { ((ma_sio_close_proc)pDevice->pContext->sndio.sio_close)((struct ma_sio_hdl*)handle); ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[sndio] Failed to retrieve buffer size."); return MA_ERROR; } internalFormat = ma_format_from_sio_enc__sndio(par.bits, par.bps, par.sig, par.le, par.msb); internalChannels = (deviceType == ma_device_type_capture) ? par.rchan : par.pchan; internalSampleRate = par.rate; internalPeriods = par.appbufsz / par.round; internalPeriodSizeInFrames = par.round; if (deviceType == ma_device_type_capture) { pDevice->sndio.handleCapture = handle; } else { pDevice->sndio.handlePlayback = handle; } pDescriptor->format = internalFormat; pDescriptor->channels = internalChannels; pDescriptor->sampleRate = internalSampleRate; ma_channel_map_init_standard(ma_standard_channel_map_sndio, pDescriptor->channelMap, ma_countof(pDescriptor->channelMap), internalChannels); pDescriptor->periodSizeInFrames = internalPeriodSizeInFrames; pDescriptor->periodCount = internalPeriods; return MA_SUCCESS; } static ma_result ma_device_init__sndio(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture) { MA_ASSERT(pDevice != NULL); MA_ZERO_OBJECT(&pDevice->sndio); if (pConfig->deviceType == ma_device_type_loopback) { return MA_DEVICE_TYPE_NOT_SUPPORTED; } if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { ma_result result = ma_device_init_handle__sndio(pDevice, pConfig, pDescriptorCapture, ma_device_type_capture); if (result != MA_SUCCESS) { return result; } } if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { ma_result result = ma_device_init_handle__sndio(pDevice, pConfig, pDescriptorPlayback, ma_device_type_playback); if (result != MA_SUCCESS) { return result; } } return MA_SUCCESS; } static ma_result ma_device_start__sndio(ma_device* pDevice) { MA_ASSERT(pDevice != NULL); if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { ((ma_sio_start_proc)pDevice->pContext->sndio.sio_start)((struct ma_sio_hdl*)pDevice->sndio.handleCapture); } if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { ((ma_sio_start_proc)pDevice->pContext->sndio.sio_start)((struct ma_sio_hdl*)pDevice->sndio.handlePlayback); /* <-- Doesn't actually playback until data is written. */ } return MA_SUCCESS; } static ma_result ma_device_stop__sndio(ma_device* pDevice) { MA_ASSERT(pDevice != NULL); /* From the documentation: The sio_stop() function puts the audio subsystem in the same state as before sio_start() is called. It stops recording, drains the play buffer and then stops playback. If samples to play are queued but playback hasn't started yet then playback is forced immediately; playback will actually stop once the buffer is drained. In no case are samples in the play buffer discarded. Therefore, sio_stop() performs all of the necessary draining for us. */ if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { ((ma_sio_stop_proc)pDevice->pContext->sndio.sio_stop)((struct ma_sio_hdl*)pDevice->sndio.handleCapture); } if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { ((ma_sio_stop_proc)pDevice->pContext->sndio.sio_stop)((struct ma_sio_hdl*)pDevice->sndio.handlePlayback); } return MA_SUCCESS; } static ma_result ma_device_write__sndio(ma_device* pDevice, const void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten) { int result; if (pFramesWritten != NULL) { *pFramesWritten = 0; } result = ((ma_sio_write_proc)pDevice->pContext->sndio.sio_write)((struct ma_sio_hdl*)pDevice->sndio.handlePlayback, pPCMFrames, frameCount * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels)); if (result == 0) { ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[sndio] Failed to send data from the client to the device."); return MA_IO_ERROR; } if (pFramesWritten != NULL) { *pFramesWritten = frameCount; } return MA_SUCCESS; } static ma_result ma_device_read__sndio(ma_device* pDevice, void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesRead) { int result; if (pFramesRead != NULL) { *pFramesRead = 0; } result = ((ma_sio_read_proc)pDevice->pContext->sndio.sio_read)((struct ma_sio_hdl*)pDevice->sndio.handleCapture, pPCMFrames, frameCount * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels)); if (result == 0) { ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[sndio] Failed to read data from the device to be sent to the device."); return MA_IO_ERROR; } if (pFramesRead != NULL) { *pFramesRead = frameCount; } return MA_SUCCESS; } static ma_result ma_context_uninit__sndio(ma_context* pContext) { MA_ASSERT(pContext != NULL); MA_ASSERT(pContext->backend == ma_backend_sndio); (void)pContext; return MA_SUCCESS; } static ma_result ma_context_init__sndio(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks) { #ifndef MA_NO_RUNTIME_LINKING const char* libsndioNames[] = { "libsndio.so" }; size_t i; for (i = 0; i < ma_countof(libsndioNames); ++i) { pContext->sndio.sndioSO = ma_dlopen(pContext, libsndioNames[i]); if (pContext->sndio.sndioSO != NULL) { break; } } if (pContext->sndio.sndioSO == NULL) { return MA_NO_BACKEND; } pContext->sndio.sio_open = (ma_proc)ma_dlsym(pContext, pContext->sndio.sndioSO, "sio_open"); pContext->sndio.sio_close = (ma_proc)ma_dlsym(pContext, pContext->sndio.sndioSO, "sio_close"); pContext->sndio.sio_setpar = (ma_proc)ma_dlsym(pContext, pContext->sndio.sndioSO, "sio_setpar"); pContext->sndio.sio_getpar = (ma_proc)ma_dlsym(pContext, pContext->sndio.sndioSO, "sio_getpar"); pContext->sndio.sio_getcap = (ma_proc)ma_dlsym(pContext, pContext->sndio.sndioSO, "sio_getcap"); pContext->sndio.sio_write = (ma_proc)ma_dlsym(pContext, pContext->sndio.sndioSO, "sio_write"); pContext->sndio.sio_read = (ma_proc)ma_dlsym(pContext, pContext->sndio.sndioSO, "sio_read"); pContext->sndio.sio_start = (ma_proc)ma_dlsym(pContext, pContext->sndio.sndioSO, "sio_start"); pContext->sndio.sio_stop = (ma_proc)ma_dlsym(pContext, pContext->sndio.sndioSO, "sio_stop"); pContext->sndio.sio_initpar = (ma_proc)ma_dlsym(pContext, pContext->sndio.sndioSO, "sio_initpar"); #else pContext->sndio.sio_open = sio_open; pContext->sndio.sio_close = sio_close; pContext->sndio.sio_setpar = sio_setpar; pContext->sndio.sio_getpar = sio_getpar; pContext->sndio.sio_getcap = sio_getcap; pContext->sndio.sio_write = sio_write; pContext->sndio.sio_read = sio_read; pContext->sndio.sio_start = sio_start; pContext->sndio.sio_stop = sio_stop; pContext->sndio.sio_initpar = sio_initpar; #endif pCallbacks->onContextInit = ma_context_init__sndio; pCallbacks->onContextUninit = ma_context_uninit__sndio; pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__sndio; pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__sndio; pCallbacks->onDeviceInit = ma_device_init__sndio; pCallbacks->onDeviceUninit = ma_device_uninit__sndio; pCallbacks->onDeviceStart = ma_device_start__sndio; pCallbacks->onDeviceStop = ma_device_stop__sndio; pCallbacks->onDeviceRead = ma_device_read__sndio; pCallbacks->onDeviceWrite = ma_device_write__sndio; pCallbacks->onDeviceDataLoop = NULL; (void)pConfig; return MA_SUCCESS; } #endif /* sndio */ /****************************************************************************** audio(4) Backend ******************************************************************************/ #ifdef MA_HAS_AUDIO4 #include #include #include #include #include #include #include #if defined(__OpenBSD__) #include #if defined(OpenBSD) && OpenBSD >= 201709 #define MA_AUDIO4_USE_NEW_API #endif #endif static void ma_construct_device_id__audio4(char* id, size_t idSize, const char* base, int deviceIndex) { size_t baseLen; MA_ASSERT(id != NULL); MA_ASSERT(idSize > 0); MA_ASSERT(deviceIndex >= 0); baseLen = strlen(base); MA_ASSERT(idSize > baseLen); ma_strcpy_s(id, idSize, base); ma_itoa_s(deviceIndex, id+baseLen, idSize-baseLen, 10); } static ma_result ma_extract_device_index_from_id__audio4(const char* id, const char* base, int* pIndexOut) { size_t idLen; size_t baseLen; const char* deviceIndexStr; MA_ASSERT(id != NULL); MA_ASSERT(base != NULL); MA_ASSERT(pIndexOut != NULL); idLen = strlen(id); baseLen = strlen(base); if (idLen <= baseLen) { return MA_ERROR; /* Doesn't look like the id starts with the base. */ } if (strncmp(id, base, baseLen) != 0) { return MA_ERROR; /* ID does not begin with base. */ } deviceIndexStr = id + baseLen; if (deviceIndexStr[0] == '\0') { return MA_ERROR; /* No index specified in the ID. */ } if (pIndexOut) { *pIndexOut = atoi(deviceIndexStr); } return MA_SUCCESS; } #if !defined(MA_AUDIO4_USE_NEW_API) /* Old API */ static ma_format ma_format_from_encoding__audio4(unsigned int encoding, unsigned int precision) { if (precision == 8 && (encoding == AUDIO_ENCODING_ULINEAR || encoding == AUDIO_ENCODING_ULINEAR || encoding == AUDIO_ENCODING_ULINEAR_LE || encoding == AUDIO_ENCODING_ULINEAR_BE)) { return ma_format_u8; } else { if (ma_is_little_endian() && encoding == AUDIO_ENCODING_SLINEAR_LE) { if (precision == 16) { return ma_format_s16; } else if (precision == 24) { return ma_format_s24; } else if (precision == 32) { return ma_format_s32; } } else if (ma_is_big_endian() && encoding == AUDIO_ENCODING_SLINEAR_BE) { if (precision == 16) { return ma_format_s16; } else if (precision == 24) { return ma_format_s24; } else if (precision == 32) { return ma_format_s32; } } } return ma_format_unknown; /* Encoding not supported. */ } static void ma_encoding_from_format__audio4(ma_format format, unsigned int* pEncoding, unsigned int* pPrecision) { MA_ASSERT(pEncoding != NULL); MA_ASSERT(pPrecision != NULL); switch (format) { case ma_format_u8: { *pEncoding = AUDIO_ENCODING_ULINEAR; *pPrecision = 8; } break; case ma_format_s24: { *pEncoding = (ma_is_little_endian()) ? AUDIO_ENCODING_SLINEAR_LE : AUDIO_ENCODING_SLINEAR_BE; *pPrecision = 24; } break; case ma_format_s32: { *pEncoding = (ma_is_little_endian()) ? AUDIO_ENCODING_SLINEAR_LE : AUDIO_ENCODING_SLINEAR_BE; *pPrecision = 32; } break; case ma_format_s16: case ma_format_f32: case ma_format_unknown: default: { *pEncoding = (ma_is_little_endian()) ? AUDIO_ENCODING_SLINEAR_LE : AUDIO_ENCODING_SLINEAR_BE; *pPrecision = 16; } break; } } static ma_format ma_format_from_prinfo__audio4(struct audio_prinfo* prinfo) { return ma_format_from_encoding__audio4(prinfo->encoding, prinfo->precision); } static ma_format ma_best_format_from_fd__audio4(int fd, ma_format preferredFormat) { audio_encoding_t encoding; ma_uint32 iFormat; int counter = 0; /* First check to see if the preferred format is supported. */ if (preferredFormat != ma_format_unknown) { counter = 0; for (;;) { MA_ZERO_OBJECT(&encoding); encoding.index = counter; if (ioctl(fd, AUDIO_GETENC, &encoding) < 0) { break; } if (preferredFormat == ma_format_from_encoding__audio4(encoding.encoding, encoding.precision)) { return preferredFormat; /* Found the preferred format. */ } /* Getting here means this encoding does not match our preferred format so we need to more on to the next encoding. */ counter += 1; } } /* Getting here means our preferred format is not supported, so fall back to our standard priorities. */ for (iFormat = 0; iFormat < ma_countof(g_maFormatPriorities); iFormat += 1) { ma_format format = g_maFormatPriorities[iFormat]; counter = 0; for (;;) { MA_ZERO_OBJECT(&encoding); encoding.index = counter; if (ioctl(fd, AUDIO_GETENC, &encoding) < 0) { break; } if (format == ma_format_from_encoding__audio4(encoding.encoding, encoding.precision)) { return format; /* Found a workable format. */ } /* Getting here means this encoding does not match our preferred format so we need to more on to the next encoding. */ counter += 1; } } /* Getting here means not appropriate format was found. */ return ma_format_unknown; } #else static ma_format ma_format_from_swpar__audio4(struct audio_swpar* par) { if (par->bits == 8 && par->bps == 1 && par->sig == 0) { return ma_format_u8; } if (par->bits == 16 && par->bps == 2 && par->sig == 1 && par->le == ma_is_little_endian()) { return ma_format_s16; } if (par->bits == 24 && par->bps == 3 && par->sig == 1 && par->le == ma_is_little_endian()) { return ma_format_s24; } if (par->bits == 32 && par->bps == 4 && par->sig == 1 && par->le == ma_is_little_endian()) { return ma_format_f32; } /* Format not supported. */ return ma_format_unknown; } #endif static ma_result ma_context_get_device_info_from_fd__audio4(ma_context* pContext, ma_device_type deviceType, int fd, ma_device_info* pDeviceInfo) { audio_device_t fdDevice; MA_ASSERT(pContext != NULL); MA_ASSERT(fd >= 0); MA_ASSERT(pDeviceInfo != NULL); (void)pContext; (void)deviceType; if (ioctl(fd, AUDIO_GETDEV, &fdDevice) < 0) { return MA_ERROR; /* Failed to retrieve device info. */ } /* Name. */ ma_strcpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), fdDevice.name); #if !defined(MA_AUDIO4_USE_NEW_API) { audio_info_t fdInfo; int counter = 0; ma_uint32 channels; ma_uint32 sampleRate; if (ioctl(fd, AUDIO_GETINFO, &fdInfo) < 0) { return MA_ERROR; } if (deviceType == ma_device_type_playback) { channels = fdInfo.play.channels; sampleRate = fdInfo.play.sample_rate; } else { channels = fdInfo.record.channels; sampleRate = fdInfo.record.sample_rate; } /* Supported formats. We get this by looking at the encodings. */ pDeviceInfo->nativeDataFormatCount = 0; for (;;) { audio_encoding_t encoding; ma_format format; MA_ZERO_OBJECT(&encoding); encoding.index = counter; if (ioctl(fd, AUDIO_GETENC, &encoding) < 0) { break; } format = ma_format_from_encoding__audio4(encoding.encoding, encoding.precision); if (format != ma_format_unknown) { ma_device_info_add_native_data_format(pDeviceInfo, format, channels, sampleRate, 0); } counter += 1; } } #else { struct audio_swpar fdPar; ma_format format; ma_uint32 channels; ma_uint32 sampleRate; if (ioctl(fd, AUDIO_GETPAR, &fdPar) < 0) { return MA_ERROR; } format = ma_format_from_swpar__audio4(&fdPar); if (format == ma_format_unknown) { return MA_FORMAT_NOT_SUPPORTED; } if (deviceType == ma_device_type_playback) { channels = fdPar.pchan; } else { channels = fdPar.rchan; } sampleRate = fdPar.rate; pDeviceInfo->nativeDataFormatCount = 0; ma_device_info_add_native_data_format(pDeviceInfo, format, channels, sampleRate, 0); } #endif return MA_SUCCESS; } static ma_result ma_context_enumerate_devices__audio4(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData) { const int maxDevices = 64; char devpath[256]; int iDevice; MA_ASSERT(pContext != NULL); MA_ASSERT(callback != NULL); /* Every device will be named "/dev/audioN", with a "/dev/audioctlN" equivalent. We use the "/dev/audioctlN" version here since we can open it even when another process has control of the "/dev/audioN" device. */ for (iDevice = 0; iDevice < maxDevices; ++iDevice) { struct stat st; int fd; ma_bool32 isTerminating = MA_FALSE; ma_strcpy_s(devpath, sizeof(devpath), "/dev/audioctl"); ma_itoa_s(iDevice, devpath+strlen(devpath), sizeof(devpath)-strlen(devpath), 10); if (stat(devpath, &st) < 0) { break; } /* The device exists, but we need to check if it's usable as playback and/or capture. */ /* Playback. */ if (!isTerminating) { fd = open(devpath, O_RDONLY, 0); if (fd >= 0) { /* Supports playback. */ ma_device_info deviceInfo; MA_ZERO_OBJECT(&deviceInfo); ma_construct_device_id__audio4(deviceInfo.id.audio4, sizeof(deviceInfo.id.audio4), "/dev/audio", iDevice); if (ma_context_get_device_info_from_fd__audio4(pContext, ma_device_type_playback, fd, &deviceInfo) == MA_SUCCESS) { isTerminating = !callback(pContext, ma_device_type_playback, &deviceInfo, pUserData); } close(fd); } } /* Capture. */ if (!isTerminating) { fd = open(devpath, O_WRONLY, 0); if (fd >= 0) { /* Supports capture. */ ma_device_info deviceInfo; MA_ZERO_OBJECT(&deviceInfo); ma_construct_device_id__audio4(deviceInfo.id.audio4, sizeof(deviceInfo.id.audio4), "/dev/audio", iDevice); if (ma_context_get_device_info_from_fd__audio4(pContext, ma_device_type_capture, fd, &deviceInfo) == MA_SUCCESS) { isTerminating = !callback(pContext, ma_device_type_capture, &deviceInfo, pUserData); } close(fd); } } if (isTerminating) { break; } } return MA_SUCCESS; } static ma_result ma_context_get_device_info__audio4(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo) { int fd = -1; int deviceIndex = -1; char ctlid[256]; ma_result result; MA_ASSERT(pContext != NULL); /* We need to open the "/dev/audioctlN" device to get the info. To do this we need to extract the number from the device ID which will be in "/dev/audioN" format. */ if (pDeviceID == NULL) { /* Default device. */ ma_strcpy_s(ctlid, sizeof(ctlid), "/dev/audioctl"); } else { /* Specific device. We need to convert from "/dev/audioN" to "/dev/audioctlN". */ result = ma_extract_device_index_from_id__audio4(pDeviceID->audio4, "/dev/audio", &deviceIndex); if (result != MA_SUCCESS) { return result; } ma_construct_device_id__audio4(ctlid, sizeof(ctlid), "/dev/audioctl", deviceIndex); } fd = open(ctlid, (deviceType == ma_device_type_playback) ? O_WRONLY : O_RDONLY, 0); if (fd == -1) { return MA_NO_DEVICE; } if (deviceIndex == -1) { ma_strcpy_s(pDeviceInfo->id.audio4, sizeof(pDeviceInfo->id.audio4), "/dev/audio"); } else { ma_construct_device_id__audio4(pDeviceInfo->id.audio4, sizeof(pDeviceInfo->id.audio4), "/dev/audio", deviceIndex); } result = ma_context_get_device_info_from_fd__audio4(pContext, deviceType, fd, pDeviceInfo); close(fd); return result; } static ma_result ma_device_uninit__audio4(ma_device* pDevice) { MA_ASSERT(pDevice != NULL); if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { close(pDevice->audio4.fdCapture); } if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { close(pDevice->audio4.fdPlayback); } return MA_SUCCESS; } static ma_result ma_device_init_fd__audio4(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptor, ma_device_type deviceType) { const char* pDefaultDeviceNames[] = { "/dev/audio", "/dev/audio0" }; int fd; int fdFlags = 0; ma_format internalFormat; ma_uint32 internalChannels; ma_uint32 internalSampleRate; ma_uint32 internalPeriodSizeInFrames; ma_uint32 internalPeriods; MA_ASSERT(pConfig != NULL); MA_ASSERT(deviceType != ma_device_type_duplex); MA_ASSERT(pDevice != NULL); /* The first thing to do is open the file. */ if (deviceType == ma_device_type_capture) { fdFlags = O_RDONLY; } else { fdFlags = O_WRONLY; } /*fdFlags |= O_NONBLOCK;*/ if (pDescriptor->pDeviceID == NULL) { /* Default device. */ size_t iDevice; for (iDevice = 0; iDevice < ma_countof(pDefaultDeviceNames); ++iDevice) { fd = open(pDefaultDeviceNames[iDevice], fdFlags, 0); if (fd != -1) { break; } } } else { /* Specific device. */ fd = open(pDescriptor->pDeviceID->audio4, fdFlags, 0); } if (fd == -1) { ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] Failed to open device."); return ma_result_from_errno(errno); } #if !defined(MA_AUDIO4_USE_NEW_API) /* Old API */ { audio_info_t fdInfo; /* The documentation is a little bit unclear to me as to how it handles formats. It says the following: Regardless of formats supported by underlying driver, the audio driver accepts the following formats. By then the next sentence says this: `encoding` and `precision` are one of the values obtained by AUDIO_GETENC. It sounds like a direct contradiction to me. I'm going to play this safe any only use the best sample format returned by AUDIO_GETENC. If the requested format is supported we'll use that, but otherwise we'll just use our standard format priorities to pick an appropriate one. */ AUDIO_INITINFO(&fdInfo); /* We get the driver to do as much of the data conversion as possible. */ if (deviceType == ma_device_type_capture) { fdInfo.mode = AUMODE_RECORD; ma_encoding_from_format__audio4(ma_best_format_from_fd__audio4(fd, pDescriptor->format), &fdInfo.record.encoding, &fdInfo.record.precision); if (pDescriptor->channels != 0) { fdInfo.record.channels = ma_clamp(pDescriptor->channels, 1, 12); /* From the documentation: `channels` ranges from 1 to 12. */ } if (pDescriptor->sampleRate != 0) { fdInfo.record.sample_rate = ma_clamp(pDescriptor->sampleRate, 1000, 192000); /* From the documentation: `frequency` ranges from 1000Hz to 192000Hz. (They mean `sample_rate` instead of `frequency`.) */ } } else { fdInfo.mode = AUMODE_PLAY; ma_encoding_from_format__audio4(ma_best_format_from_fd__audio4(fd, pDescriptor->format), &fdInfo.play.encoding, &fdInfo.play.precision); if (pDescriptor->channels != 0) { fdInfo.play.channels = ma_clamp(pDescriptor->channels, 1, 12); /* From the documentation: `channels` ranges from 1 to 12. */ } if (pDescriptor->sampleRate != 0) { fdInfo.play.sample_rate = ma_clamp(pDescriptor->sampleRate, 1000, 192000); /* From the documentation: `frequency` ranges from 1000Hz to 192000Hz. (They mean `sample_rate` instead of `frequency`.) */ } } if (ioctl(fd, AUDIO_SETINFO, &fdInfo) < 0) { close(fd); ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] Failed to set device format. AUDIO_SETINFO failed."); return ma_result_from_errno(errno); } if (ioctl(fd, AUDIO_GETINFO, &fdInfo) < 0) { close(fd); ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] AUDIO_GETINFO failed."); return ma_result_from_errno(errno); } if (deviceType == ma_device_type_capture) { internalFormat = ma_format_from_prinfo__audio4(&fdInfo.record); internalChannels = fdInfo.record.channels; internalSampleRate = fdInfo.record.sample_rate; } else { internalFormat = ma_format_from_prinfo__audio4(&fdInfo.play); internalChannels = fdInfo.play.channels; internalSampleRate = fdInfo.play.sample_rate; } if (internalFormat == ma_format_unknown) { close(fd); ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] The device's internal device format is not supported by miniaudio. The device is unusable."); return MA_FORMAT_NOT_SUPPORTED; } /* Buffer. */ { ma_uint32 internalPeriodSizeInBytes; internalPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptor, internalSampleRate, pConfig->performanceProfile); internalPeriodSizeInBytes = internalPeriodSizeInFrames * ma_get_bytes_per_frame(internalFormat, internalChannels); if (internalPeriodSizeInBytes < 16) { internalPeriodSizeInBytes = 16; } internalPeriods = pDescriptor->periodCount; if (internalPeriods < 2) { internalPeriods = 2; } /* What miniaudio calls a period, audio4 calls a block. */ AUDIO_INITINFO(&fdInfo); fdInfo.hiwat = internalPeriods; fdInfo.lowat = internalPeriods-1; fdInfo.blocksize = internalPeriodSizeInBytes; if (ioctl(fd, AUDIO_SETINFO, &fdInfo) < 0) { close(fd); ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] Failed to set internal buffer size. AUDIO_SETINFO failed."); return ma_result_from_errno(errno); } internalPeriods = fdInfo.hiwat; internalPeriodSizeInFrames = fdInfo.blocksize / ma_get_bytes_per_frame(internalFormat, internalChannels); } } #else { struct audio_swpar fdPar; /* We need to retrieve the format of the device so we can know the channel count and sample rate. Then we can calculate the buffer size. */ if (ioctl(fd, AUDIO_GETPAR, &fdPar) < 0) { close(fd); ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] Failed to retrieve initial device parameters."); return ma_result_from_errno(errno); } internalFormat = ma_format_from_swpar__audio4(&fdPar); internalChannels = (deviceType == ma_device_type_capture) ? fdPar.rchan : fdPar.pchan; internalSampleRate = fdPar.rate; if (internalFormat == ma_format_unknown) { close(fd); ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] The device's internal device format is not supported by miniaudio. The device is unusable."); return MA_FORMAT_NOT_SUPPORTED; } /* Buffer. */ { ma_uint32 internalPeriodSizeInBytes; internalPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptor, internalSampleRate, pConfig->performanceProfile); /* What miniaudio calls a period, audio4 calls a block. */ internalPeriodSizeInBytes = internalPeriodSizeInFrames * ma_get_bytes_per_frame(internalFormat, internalChannels); if (internalPeriodSizeInBytes < 16) { internalPeriodSizeInBytes = 16; } fdPar.nblks = pDescriptor->periodCount; fdPar.round = internalPeriodSizeInBytes; if (ioctl(fd, AUDIO_SETPAR, &fdPar) < 0) { close(fd); ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] Failed to set device parameters."); return ma_result_from_errno(errno); } if (ioctl(fd, AUDIO_GETPAR, &fdPar) < 0) { close(fd); ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] Failed to retrieve actual device parameters."); return ma_result_from_errno(errno); } } internalFormat = ma_format_from_swpar__audio4(&fdPar); internalChannels = (deviceType == ma_device_type_capture) ? fdPar.rchan : fdPar.pchan; internalSampleRate = fdPar.rate; internalPeriods = fdPar.nblks; internalPeriodSizeInFrames = fdPar.round / ma_get_bytes_per_frame(internalFormat, internalChannels); } #endif if (internalFormat == ma_format_unknown) { close(fd); ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] The device's internal device format is not supported by miniaudio. The device is unusable."); return MA_FORMAT_NOT_SUPPORTED; } if (deviceType == ma_device_type_capture) { pDevice->audio4.fdCapture = fd; } else { pDevice->audio4.fdPlayback = fd; } pDescriptor->format = internalFormat; pDescriptor->channels = internalChannels; pDescriptor->sampleRate = internalSampleRate; ma_channel_map_init_standard(ma_standard_channel_map_sound4, pDescriptor->channelMap, ma_countof(pDescriptor->channelMap), internalChannels); pDescriptor->periodSizeInFrames = internalPeriodSizeInFrames; pDescriptor->periodCount = internalPeriods; return MA_SUCCESS; } static ma_result ma_device_init__audio4(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture) { MA_ASSERT(pDevice != NULL); MA_ZERO_OBJECT(&pDevice->audio4); if (pConfig->deviceType == ma_device_type_loopback) { return MA_DEVICE_TYPE_NOT_SUPPORTED; } pDevice->audio4.fdCapture = -1; pDevice->audio4.fdPlayback = -1; /* The version of the operating system dictates whether or not the device is exclusive or shared. NetBSD introduced in-kernel mixing which means it's shared. All other BSD flavours are exclusive as far as I'm aware. */ #if defined(__NetBSD_Version__) && __NetBSD_Version__ >= 800000000 /* NetBSD 8.0+ */ if (((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pDescriptorPlayback->shareMode == ma_share_mode_exclusive) || ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pDescriptorCapture->shareMode == ma_share_mode_exclusive)) { return MA_SHARE_MODE_NOT_SUPPORTED; } #else /* All other flavors. */ #endif if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { ma_result result = ma_device_init_fd__audio4(pDevice, pConfig, pDescriptorCapture, ma_device_type_capture); if (result != MA_SUCCESS) { return result; } } if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { ma_result result = ma_device_init_fd__audio4(pDevice, pConfig, pDescriptorPlayback, ma_device_type_playback); if (result != MA_SUCCESS) { if (pConfig->deviceType == ma_device_type_duplex) { close(pDevice->audio4.fdCapture); } return result; } } return MA_SUCCESS; } static ma_result ma_device_start__audio4(ma_device* pDevice) { MA_ASSERT(pDevice != NULL); if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { if (pDevice->audio4.fdCapture == -1) { return MA_INVALID_ARGS; } } if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { if (pDevice->audio4.fdPlayback == -1) { return MA_INVALID_ARGS; } } return MA_SUCCESS; } static ma_result ma_device_stop_fd__audio4(ma_device* pDevice, int fd) { if (fd == -1) { return MA_INVALID_ARGS; } #if !defined(MA_AUDIO4_USE_NEW_API) if (ioctl(fd, AUDIO_FLUSH, 0) < 0) { ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] Failed to stop device. AUDIO_FLUSH failed."); return ma_result_from_errno(errno); } #else if (ioctl(fd, AUDIO_STOP, 0) < 0) { ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] Failed to stop device. AUDIO_STOP failed."); return ma_result_from_errno(errno); } #endif return MA_SUCCESS; } static ma_result ma_device_stop__audio4(ma_device* pDevice) { MA_ASSERT(pDevice != NULL); if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { ma_result result; result = ma_device_stop_fd__audio4(pDevice, pDevice->audio4.fdCapture); if (result != MA_SUCCESS) { return result; } } if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { ma_result result; /* Drain the device first. If this fails we'll just need to flush without draining. Unfortunately draining isn't available on newer version of OpenBSD. */ #if !defined(MA_AUDIO4_USE_NEW_API) ioctl(pDevice->audio4.fdPlayback, AUDIO_DRAIN, 0); #endif /* Here is where the device is stopped immediately. */ result = ma_device_stop_fd__audio4(pDevice, pDevice->audio4.fdPlayback); if (result != MA_SUCCESS) { return result; } } return MA_SUCCESS; } static ma_result ma_device_write__audio4(ma_device* pDevice, const void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten) { int result; if (pFramesWritten != NULL) { *pFramesWritten = 0; } result = write(pDevice->audio4.fdPlayback, pPCMFrames, frameCount * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels)); if (result < 0) { ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] Failed to write data to the device."); return ma_result_from_errno(errno); } if (pFramesWritten != NULL) { *pFramesWritten = (ma_uint32)result / ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels); } return MA_SUCCESS; } static ma_result ma_device_read__audio4(ma_device* pDevice, void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesRead) { int result; if (pFramesRead != NULL) { *pFramesRead = 0; } result = read(pDevice->audio4.fdCapture, pPCMFrames, frameCount * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels)); if (result < 0) { ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] Failed to read data from the device."); return ma_result_from_errno(errno); } if (pFramesRead != NULL) { *pFramesRead = (ma_uint32)result / ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels); } return MA_SUCCESS; } static ma_result ma_context_uninit__audio4(ma_context* pContext) { MA_ASSERT(pContext != NULL); MA_ASSERT(pContext->backend == ma_backend_audio4); (void)pContext; return MA_SUCCESS; } static ma_result ma_context_init__audio4(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks) { MA_ASSERT(pContext != NULL); (void)pConfig; pCallbacks->onContextInit = ma_context_init__audio4; pCallbacks->onContextUninit = ma_context_uninit__audio4; pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__audio4; pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__audio4; pCallbacks->onDeviceInit = ma_device_init__audio4; pCallbacks->onDeviceUninit = ma_device_uninit__audio4; pCallbacks->onDeviceStart = ma_device_start__audio4; pCallbacks->onDeviceStop = ma_device_stop__audio4; pCallbacks->onDeviceRead = ma_device_read__audio4; pCallbacks->onDeviceWrite = ma_device_write__audio4; pCallbacks->onDeviceDataLoop = NULL; return MA_SUCCESS; } #endif /* audio4 */ /****************************************************************************** OSS Backend ******************************************************************************/ #ifdef MA_HAS_OSS #include #include #include #include #ifndef SNDCTL_DSP_HALT #define SNDCTL_DSP_HALT SNDCTL_DSP_RESET #endif #define MA_OSS_DEFAULT_DEVICE_NAME "/dev/dsp" static int ma_open_temp_device__oss() { /* The OSS sample code uses "/dev/mixer" as the device for getting system properties so I'm going to do the same. */ int fd = open("/dev/mixer", O_RDONLY, 0); if (fd >= 0) { return fd; } return -1; } static ma_result ma_context_open_device__oss(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_share_mode shareMode, int* pfd) { const char* deviceName; int flags; MA_ASSERT(pContext != NULL); MA_ASSERT(pfd != NULL); (void)pContext; *pfd = -1; /* This function should only be called for playback or capture, not duplex. */ if (deviceType == ma_device_type_duplex) { return MA_INVALID_ARGS; } deviceName = MA_OSS_DEFAULT_DEVICE_NAME; if (pDeviceID != NULL) { deviceName = pDeviceID->oss; } flags = (deviceType == ma_device_type_playback) ? O_WRONLY : O_RDONLY; if (shareMode == ma_share_mode_exclusive) { flags |= O_EXCL; } *pfd = open(deviceName, flags, 0); if (*pfd == -1) { return ma_result_from_errno(errno); } return MA_SUCCESS; } static ma_result ma_context_enumerate_devices__oss(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData) { int fd; oss_sysinfo si; int result; MA_ASSERT(pContext != NULL); MA_ASSERT(callback != NULL); fd = ma_open_temp_device__oss(); if (fd == -1) { ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[OSS] Failed to open a temporary device for retrieving system information used for device enumeration."); return MA_NO_BACKEND; } result = ioctl(fd, SNDCTL_SYSINFO, &si); if (result != -1) { int iAudioDevice; for (iAudioDevice = 0; iAudioDevice < si.numaudios; ++iAudioDevice) { oss_audioinfo ai; ai.dev = iAudioDevice; result = ioctl(fd, SNDCTL_AUDIOINFO, &ai); if (result != -1) { if (ai.devnode[0] != '\0') { /* <-- Can be blank, according to documentation. */ ma_device_info deviceInfo; ma_bool32 isTerminating = MA_FALSE; MA_ZERO_OBJECT(&deviceInfo); /* ID */ ma_strncpy_s(deviceInfo.id.oss, sizeof(deviceInfo.id.oss), ai.devnode, (size_t)-1); /* The human readable device name should be in the "ai.handle" variable, but it can sometimes be empty in which case we just fall back to "ai.name" which is less user friendly, but usually has a value. */ if (ai.handle[0] != '\0') { ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), ai.handle, (size_t)-1); } else { ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), ai.name, (size_t)-1); } /* The device can be both playback and capture. */ if (!isTerminating && (ai.caps & PCM_CAP_OUTPUT) != 0) { isTerminating = !callback(pContext, ma_device_type_playback, &deviceInfo, pUserData); } if (!isTerminating && (ai.caps & PCM_CAP_INPUT) != 0) { isTerminating = !callback(pContext, ma_device_type_capture, &deviceInfo, pUserData); } if (isTerminating) { break; } } } } } else { close(fd); ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[OSS] Failed to retrieve system information for device enumeration."); return MA_NO_BACKEND; } close(fd); return MA_SUCCESS; } static void ma_context_add_native_data_format__oss(ma_context* pContext, oss_audioinfo* pAudioInfo, ma_format format, ma_device_info* pDeviceInfo) { unsigned int minChannels; unsigned int maxChannels; unsigned int iRate; MA_ASSERT(pContext != NULL); MA_ASSERT(pAudioInfo != NULL); MA_ASSERT(pDeviceInfo != NULL); /* If we support all channels we just report 0. */ minChannels = ma_clamp(pAudioInfo->min_channels, MA_MIN_CHANNELS, MA_MAX_CHANNELS); maxChannels = ma_clamp(pAudioInfo->max_channels, MA_MIN_CHANNELS, MA_MAX_CHANNELS); /* OSS has this annoying thing where sample rates can be reported in two ways. We prefer explicitness, which OSS has in the form of nrates/rates, however there are times where nrates can be 0, in which case we'll need to use min_rate and max_rate and report only standard rates. */ if (pAudioInfo->nrates > 0) { for (iRate = 0; iRate < pAudioInfo->nrates; iRate += 1) { unsigned int rate = pAudioInfo->rates[iRate]; if (minChannels == MA_MIN_CHANNELS && maxChannels == MA_MAX_CHANNELS) { ma_device_info_add_native_data_format(pDeviceInfo, format, 0, rate, 0); /* Set the channel count to 0 to indicate that all channel counts are supported. */ } else { unsigned int iChannel; for (iChannel = minChannels; iChannel <= maxChannels; iChannel += 1) { ma_device_info_add_native_data_format(pDeviceInfo, format, iChannel, rate, 0); } } } } else { for (iRate = 0; iRate < ma_countof(g_maStandardSampleRatePriorities); iRate += 1) { ma_uint32 standardRate = g_maStandardSampleRatePriorities[iRate]; if (standardRate >= (ma_uint32)pAudioInfo->min_rate && standardRate <= (ma_uint32)pAudioInfo->max_rate) { if (minChannels == MA_MIN_CHANNELS && maxChannels == MA_MAX_CHANNELS) { ma_device_info_add_native_data_format(pDeviceInfo, format, 0, standardRate, 0); /* Set the channel count to 0 to indicate that all channel counts are supported. */ } else { unsigned int iChannel; for (iChannel = minChannels; iChannel <= maxChannels; iChannel += 1) { ma_device_info_add_native_data_format(pDeviceInfo, format, iChannel, standardRate, 0); } } } } } } static ma_result ma_context_get_device_info__oss(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo) { ma_bool32 foundDevice; int fdTemp; oss_sysinfo si; int result; MA_ASSERT(pContext != NULL); /* Handle the default device a little differently. */ if (pDeviceID == NULL) { if (deviceType == ma_device_type_playback) { ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); } else { ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); } return MA_SUCCESS; } /* If we get here it means we are _not_ using the default device. */ foundDevice = MA_FALSE; fdTemp = ma_open_temp_device__oss(); if (fdTemp == -1) { ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[OSS] Failed to open a temporary device for retrieving system information used for device enumeration."); return MA_NO_BACKEND; } result = ioctl(fdTemp, SNDCTL_SYSINFO, &si); if (result != -1) { int iAudioDevice; for (iAudioDevice = 0; iAudioDevice < si.numaudios; ++iAudioDevice) { oss_audioinfo ai; ai.dev = iAudioDevice; result = ioctl(fdTemp, SNDCTL_AUDIOINFO, &ai); if (result != -1) { if (ma_strcmp(ai.devnode, pDeviceID->oss) == 0) { /* It has the same name, so now just confirm the type. */ if ((deviceType == ma_device_type_playback && ((ai.caps & PCM_CAP_OUTPUT) != 0)) || (deviceType == ma_device_type_capture && ((ai.caps & PCM_CAP_INPUT) != 0))) { unsigned int formatMask; /* ID */ ma_strncpy_s(pDeviceInfo->id.oss, sizeof(pDeviceInfo->id.oss), ai.devnode, (size_t)-1); /* The human readable device name should be in the "ai.handle" variable, but it can sometimes be empty in which case we just fall back to "ai.name" which is less user friendly, but usually has a value. */ if (ai.handle[0] != '\0') { ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), ai.handle, (size_t)-1); } else { ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), ai.name, (size_t)-1); } pDeviceInfo->nativeDataFormatCount = 0; if (deviceType == ma_device_type_playback) { formatMask = ai.oformats; } else { formatMask = ai.iformats; } if (((formatMask & AFMT_S16_LE) != 0 && ma_is_little_endian()) || (AFMT_S16_BE && ma_is_big_endian())) { ma_context_add_native_data_format__oss(pContext, &ai, ma_format_s16, pDeviceInfo); } if (((formatMask & AFMT_S32_LE) != 0 && ma_is_little_endian()) || (AFMT_S32_BE && ma_is_big_endian())) { ma_context_add_native_data_format__oss(pContext, &ai, ma_format_s32, pDeviceInfo); } if ((formatMask & AFMT_U8) != 0) { ma_context_add_native_data_format__oss(pContext, &ai, ma_format_u8, pDeviceInfo); } foundDevice = MA_TRUE; break; } } } } } else { close(fdTemp); ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[OSS] Failed to retrieve system information for device enumeration."); return MA_NO_BACKEND; } close(fdTemp); if (!foundDevice) { return MA_NO_DEVICE; } return MA_SUCCESS; } static ma_result ma_device_uninit__oss(ma_device* pDevice) { MA_ASSERT(pDevice != NULL); if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { close(pDevice->oss.fdCapture); } if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { close(pDevice->oss.fdPlayback); } return MA_SUCCESS; } static int ma_format_to_oss(ma_format format) { int ossFormat = AFMT_U8; switch (format) { case ma_format_s16: ossFormat = (ma_is_little_endian()) ? AFMT_S16_LE : AFMT_S16_BE; break; case ma_format_s24: ossFormat = (ma_is_little_endian()) ? AFMT_S32_LE : AFMT_S32_BE; break; case ma_format_s32: ossFormat = (ma_is_little_endian()) ? AFMT_S32_LE : AFMT_S32_BE; break; case ma_format_f32: ossFormat = (ma_is_little_endian()) ? AFMT_S16_LE : AFMT_S16_BE; break; case ma_format_u8: default: ossFormat = AFMT_U8; break; } return ossFormat; } static ma_format ma_format_from_oss(int ossFormat) { if (ossFormat == AFMT_U8) { return ma_format_u8; } else { if (ma_is_little_endian()) { switch (ossFormat) { case AFMT_S16_LE: return ma_format_s16; case AFMT_S32_LE: return ma_format_s32; default: return ma_format_unknown; } } else { switch (ossFormat) { case AFMT_S16_BE: return ma_format_s16; case AFMT_S32_BE: return ma_format_s32; default: return ma_format_unknown; } } } return ma_format_unknown; } static ma_result ma_device_init_fd__oss(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptor, ma_device_type deviceType) { ma_result result; int ossResult; int fd; const ma_device_id* pDeviceID = NULL; ma_share_mode shareMode; int ossFormat; int ossChannels; int ossSampleRate; int ossFragment; MA_ASSERT(pDevice != NULL); MA_ASSERT(pConfig != NULL); MA_ASSERT(deviceType != ma_device_type_duplex); pDeviceID = pDescriptor->pDeviceID; shareMode = pDescriptor->shareMode; ossFormat = ma_format_to_oss((pDescriptor->format != ma_format_unknown) ? pDescriptor->format : ma_format_s16); /* Use s16 by default because OSS doesn't like floating point. */ ossChannels = (int)(pDescriptor->channels > 0) ? pDescriptor->channels : MA_DEFAULT_CHANNELS; ossSampleRate = (int)(pDescriptor->sampleRate > 0) ? pDescriptor->sampleRate : MA_DEFAULT_SAMPLE_RATE; result = ma_context_open_device__oss(pDevice->pContext, deviceType, pDeviceID, shareMode, &fd); if (result != MA_SUCCESS) { ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OSS] Failed to open device."); return result; } /* The OSS documantation is very clear about the order we should be initializing the device's properties: 1) Format 2) Channels 3) Sample rate. */ /* Format. */ ossResult = ioctl(fd, SNDCTL_DSP_SETFMT, &ossFormat); if (ossResult == -1) { close(fd); ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OSS] Failed to set format."); return ma_result_from_errno(errno); } /* Channels. */ ossResult = ioctl(fd, SNDCTL_DSP_CHANNELS, &ossChannels); if (ossResult == -1) { close(fd); ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OSS] Failed to set channel count."); return ma_result_from_errno(errno); } /* Sample Rate. */ ossResult = ioctl(fd, SNDCTL_DSP_SPEED, &ossSampleRate); if (ossResult == -1) { close(fd); ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OSS] Failed to set sample rate."); return ma_result_from_errno(errno); } /* Buffer. The documentation says that the fragment settings should be set as soon as possible, but I'm not sure if it should be done before or after format/channels/rate. OSS wants the fragment size in bytes and a power of 2. When setting, we specify the power, not the actual value. */ { ma_uint32 periodSizeInFrames; ma_uint32 periodSizeInBytes; ma_uint32 ossFragmentSizePower; periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptor, (ma_uint32)ossSampleRate, pConfig->performanceProfile); periodSizeInBytes = ma_round_to_power_of_2(periodSizeInFrames * ma_get_bytes_per_frame(ma_format_from_oss(ossFormat), ossChannels)); if (periodSizeInBytes < 16) { periodSizeInBytes = 16; } ossFragmentSizePower = 4; periodSizeInBytes >>= 4; while (periodSizeInBytes >>= 1) { ossFragmentSizePower += 1; } ossFragment = (int)((pConfig->periods << 16) | ossFragmentSizePower); ossResult = ioctl(fd, SNDCTL_DSP_SETFRAGMENT, &ossFragment); if (ossResult == -1) { close(fd); ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OSS] Failed to set fragment size and period count."); return ma_result_from_errno(errno); } } /* Internal settings. */ if (deviceType == ma_device_type_capture) { pDevice->oss.fdCapture = fd; } else { pDevice->oss.fdPlayback = fd; } pDescriptor->format = ma_format_from_oss(ossFormat); pDescriptor->channels = ossChannels; pDescriptor->sampleRate = ossSampleRate; ma_channel_map_init_standard(ma_standard_channel_map_sound4, pDescriptor->channelMap, ma_countof(pDescriptor->channelMap), pDescriptor->channels); pDescriptor->periodCount = (ma_uint32)(ossFragment >> 16); pDescriptor->periodSizeInFrames = (ma_uint32)(1 << (ossFragment & 0xFFFF)) / ma_get_bytes_per_frame(pDescriptor->format, pDescriptor->channels); if (pDescriptor->format == ma_format_unknown) { ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OSS] The device's internal format is not supported by miniaudio."); return MA_FORMAT_NOT_SUPPORTED; } return MA_SUCCESS; } static ma_result ma_device_init__oss(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture) { MA_ASSERT(pDevice != NULL); MA_ASSERT(pConfig != NULL); MA_ZERO_OBJECT(&pDevice->oss); if (pConfig->deviceType == ma_device_type_loopback) { return MA_DEVICE_TYPE_NOT_SUPPORTED; } if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { ma_result result = ma_device_init_fd__oss(pDevice, pConfig, pDescriptorCapture, ma_device_type_capture); if (result != MA_SUCCESS) { ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OSS] Failed to open device."); return result; } } if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { ma_result result = ma_device_init_fd__oss(pDevice, pConfig, pDescriptorPlayback, ma_device_type_playback); if (result != MA_SUCCESS) { ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OSS] Failed to open device."); return result; } } return MA_SUCCESS; } /* Note on Starting and Stopping ============================= In the past I was using SNDCTL_DSP_HALT to stop the device, however this results in issues when trying to resume the device again. If we use SNDCTL_DSP_HALT, the next write() or read() will fail. Instead what we need to do is just not write or read to and from the device when the device is not running. As a result, both the start and stop functions for OSS are just empty stubs. The starting and stopping logic is handled by ma_device_write__oss() and ma_device_read__oss(). These will check the device state, and if the device is stopped they will simply not do any kind of processing. The downside to this technique is that I've noticed a fairly lengthy delay in stopping the device, up to a second. This is on a virtual machine, and as such might just be due to the virtual drivers, but I'm not fully sure. I am not sure how to work around this problem so for the moment that's just how it's going to have to be. When starting the device, OSS will automatically start it when write() or read() is called. */ static ma_result ma_device_start__oss(ma_device* pDevice) { MA_ASSERT(pDevice != NULL); /* The device is automatically started with reading and writing. */ (void)pDevice; return MA_SUCCESS; } static ma_result ma_device_stop__oss(ma_device* pDevice) { MA_ASSERT(pDevice != NULL); /* See note above on why this is empty. */ (void)pDevice; return MA_SUCCESS; } static ma_result ma_device_write__oss(ma_device* pDevice, const void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten) { int resultOSS; ma_uint32 deviceState; if (pFramesWritten != NULL) { *pFramesWritten = 0; } /* Don't do any processing if the device is stopped. */ deviceState = ma_device_get_state(pDevice); if (deviceState != ma_device_state_started && deviceState != ma_device_state_starting) { return MA_SUCCESS; } resultOSS = write(pDevice->oss.fdPlayback, pPCMFrames, frameCount * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels)); if (resultOSS < 0) { ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OSS] Failed to send data from the client to the device."); return ma_result_from_errno(errno); } if (pFramesWritten != NULL) { *pFramesWritten = (ma_uint32)resultOSS / ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels); } return MA_SUCCESS; } static ma_result ma_device_read__oss(ma_device* pDevice, void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesRead) { int resultOSS; ma_uint32 deviceState; if (pFramesRead != NULL) { *pFramesRead = 0; } /* Don't do any processing if the device is stopped. */ deviceState = ma_device_get_state(pDevice); if (deviceState != ma_device_state_started && deviceState != ma_device_state_starting) { return MA_SUCCESS; } resultOSS = read(pDevice->oss.fdCapture, pPCMFrames, frameCount * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels)); if (resultOSS < 0) { ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OSS] Failed to read data from the device to be sent to the client."); return ma_result_from_errno(errno); } if (pFramesRead != NULL) { *pFramesRead = (ma_uint32)resultOSS / ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels); } return MA_SUCCESS; } static ma_result ma_context_uninit__oss(ma_context* pContext) { MA_ASSERT(pContext != NULL); MA_ASSERT(pContext->backend == ma_backend_oss); (void)pContext; return MA_SUCCESS; } static ma_result ma_context_init__oss(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks) { int fd; int ossVersion; int result; MA_ASSERT(pContext != NULL); (void)pConfig; /* Try opening a temporary device first so we can get version information. This is closed at the end. */ fd = ma_open_temp_device__oss(); if (fd == -1) { ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[OSS] Failed to open temporary device for retrieving system properties."); /* Looks liks OSS isn't installed, or there are no available devices. */ return MA_NO_BACKEND; } /* Grab the OSS version. */ ossVersion = 0; result = ioctl(fd, OSS_GETVERSION, &ossVersion); if (result == -1) { close(fd); ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[OSS] Failed to retrieve OSS version."); return MA_NO_BACKEND; } /* The file handle to temp device is no longer needed. Close ASAP. */ close(fd); pContext->oss.versionMajor = ((ossVersion & 0xFF0000) >> 16); pContext->oss.versionMinor = ((ossVersion & 0x00FF00) >> 8); pCallbacks->onContextInit = ma_context_init__oss; pCallbacks->onContextUninit = ma_context_uninit__oss; pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__oss; pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__oss; pCallbacks->onDeviceInit = ma_device_init__oss; pCallbacks->onDeviceUninit = ma_device_uninit__oss; pCallbacks->onDeviceStart = ma_device_start__oss; pCallbacks->onDeviceStop = ma_device_stop__oss; pCallbacks->onDeviceRead = ma_device_read__oss; pCallbacks->onDeviceWrite = ma_device_write__oss; pCallbacks->onDeviceDataLoop = NULL; return MA_SUCCESS; } #endif /* OSS */ /****************************************************************************** AAudio Backend ******************************************************************************/ #ifdef MA_HAS_AAUDIO /*#include */ typedef int32_t ma_aaudio_result_t; typedef int32_t ma_aaudio_direction_t; typedef int32_t ma_aaudio_sharing_mode_t; typedef int32_t ma_aaudio_format_t; typedef int32_t ma_aaudio_stream_state_t; typedef int32_t ma_aaudio_performance_mode_t; typedef int32_t ma_aaudio_usage_t; typedef int32_t ma_aaudio_content_type_t; typedef int32_t ma_aaudio_input_preset_t; typedef int32_t ma_aaudio_data_callback_result_t; typedef struct ma_AAudioStreamBuilder_t* ma_AAudioStreamBuilder; typedef struct ma_AAudioStream_t* ma_AAudioStream; #define MA_AAUDIO_UNSPECIFIED 0 /* Result codes. miniaudio only cares about the success code. */ #define MA_AAUDIO_OK 0 /* Directions. */ #define MA_AAUDIO_DIRECTION_OUTPUT 0 #define MA_AAUDIO_DIRECTION_INPUT 1 /* Sharing modes. */ #define MA_AAUDIO_SHARING_MODE_EXCLUSIVE 0 #define MA_AAUDIO_SHARING_MODE_SHARED 1 /* Formats. */ #define MA_AAUDIO_FORMAT_PCM_I16 1 #define MA_AAUDIO_FORMAT_PCM_FLOAT 2 /* Stream states. */ #define MA_AAUDIO_STREAM_STATE_UNINITIALIZED 0 #define MA_AAUDIO_STREAM_STATE_UNKNOWN 1 #define MA_AAUDIO_STREAM_STATE_OPEN 2 #define MA_AAUDIO_STREAM_STATE_STARTING 3 #define MA_AAUDIO_STREAM_STATE_STARTED 4 #define MA_AAUDIO_STREAM_STATE_PAUSING 5 #define MA_AAUDIO_STREAM_STATE_PAUSED 6 #define MA_AAUDIO_STREAM_STATE_FLUSHING 7 #define MA_AAUDIO_STREAM_STATE_FLUSHED 8 #define MA_AAUDIO_STREAM_STATE_STOPPING 9 #define MA_AAUDIO_STREAM_STATE_STOPPED 10 #define MA_AAUDIO_STREAM_STATE_CLOSING 11 #define MA_AAUDIO_STREAM_STATE_CLOSED 12 #define MA_AAUDIO_STREAM_STATE_DISCONNECTED 13 /* Performance modes. */ #define MA_AAUDIO_PERFORMANCE_MODE_NONE 10 #define MA_AAUDIO_PERFORMANCE_MODE_POWER_SAVING 11 #define MA_AAUDIO_PERFORMANCE_MODE_LOW_LATENCY 12 /* Usage types. */ #define MA_AAUDIO_USAGE_MEDIA 1 #define MA_AAUDIO_USAGE_VOICE_COMMUNICATION 2 #define MA_AAUDIO_USAGE_VOICE_COMMUNICATION_SIGNALLING 3 #define MA_AAUDIO_USAGE_ALARM 4 #define MA_AAUDIO_USAGE_NOTIFICATION 5 #define MA_AAUDIO_USAGE_NOTIFICATION_RINGTONE 6 #define MA_AAUDIO_USAGE_NOTIFICATION_EVENT 10 #define MA_AAUDIO_USAGE_ASSISTANCE_ACCESSIBILITY 11 #define MA_AAUDIO_USAGE_ASSISTANCE_NAVIGATION_GUIDANCE 12 #define MA_AAUDIO_USAGE_ASSISTANCE_SONIFICATION 13 #define MA_AAUDIO_USAGE_GAME 14 #define MA_AAUDIO_USAGE_ASSISTANT 16 #define MA_AAUDIO_SYSTEM_USAGE_EMERGENCY 1000 #define MA_AAUDIO_SYSTEM_USAGE_SAFETY 1001 #define MA_AAUDIO_SYSTEM_USAGE_VEHICLE_STATUS 1002 #define MA_AAUDIO_SYSTEM_USAGE_ANNOUNCEMENT 1003 /* Content types. */ #define MA_AAUDIO_CONTENT_TYPE_SPEECH 1 #define MA_AAUDIO_CONTENT_TYPE_MUSIC 2 #define MA_AAUDIO_CONTENT_TYPE_MOVIE 3 #define MA_AAUDIO_CONTENT_TYPE_SONIFICATION 4 /* Input presets. */ #define MA_AAUDIO_INPUT_PRESET_GENERIC 1 #define MA_AAUDIO_INPUT_PRESET_CAMCORDER 5 #define MA_AAUDIO_INPUT_PRESET_VOICE_RECOGNITION 6 #define MA_AAUDIO_INPUT_PRESET_VOICE_COMMUNICATION 7 #define MA_AAUDIO_INPUT_PRESET_UNPROCESSED 9 #define MA_AAUDIO_INPUT_PRESET_VOICE_PERFORMANCE 10 /* Callback results. */ #define MA_AAUDIO_CALLBACK_RESULT_CONTINUE 0 #define MA_AAUDIO_CALLBACK_RESULT_STOP 1 typedef ma_aaudio_data_callback_result_t (* ma_AAudioStream_dataCallback) (ma_AAudioStream* pStream, void* pUserData, void* pAudioData, int32_t numFrames); typedef void (* ma_AAudioStream_errorCallback)(ma_AAudioStream *pStream, void *pUserData, ma_aaudio_result_t error); typedef ma_aaudio_result_t (* MA_PFN_AAudio_createStreamBuilder) (ma_AAudioStreamBuilder** ppBuilder); typedef ma_aaudio_result_t (* MA_PFN_AAudioStreamBuilder_delete) (ma_AAudioStreamBuilder* pBuilder); typedef void (* MA_PFN_AAudioStreamBuilder_setDeviceId) (ma_AAudioStreamBuilder* pBuilder, int32_t deviceId); typedef void (* MA_PFN_AAudioStreamBuilder_setDirection) (ma_AAudioStreamBuilder* pBuilder, ma_aaudio_direction_t direction); typedef void (* MA_PFN_AAudioStreamBuilder_setSharingMode) (ma_AAudioStreamBuilder* pBuilder, ma_aaudio_sharing_mode_t sharingMode); typedef void (* MA_PFN_AAudioStreamBuilder_setFormat) (ma_AAudioStreamBuilder* pBuilder, ma_aaudio_format_t format); typedef void (* MA_PFN_AAudioStreamBuilder_setChannelCount) (ma_AAudioStreamBuilder* pBuilder, int32_t channelCount); typedef void (* MA_PFN_AAudioStreamBuilder_setSampleRate) (ma_AAudioStreamBuilder* pBuilder, int32_t sampleRate); typedef void (* MA_PFN_AAudioStreamBuilder_setBufferCapacityInFrames)(ma_AAudioStreamBuilder* pBuilder, int32_t numFrames); typedef void (* MA_PFN_AAudioStreamBuilder_setFramesPerDataCallback) (ma_AAudioStreamBuilder* pBuilder, int32_t numFrames); typedef void (* MA_PFN_AAudioStreamBuilder_setDataCallback) (ma_AAudioStreamBuilder* pBuilder, ma_AAudioStream_dataCallback callback, void* pUserData); typedef void (* MA_PFN_AAudioStreamBuilder_setErrorCallback) (ma_AAudioStreamBuilder* pBuilder, ma_AAudioStream_errorCallback callback, void* pUserData); typedef void (* MA_PFN_AAudioStreamBuilder_setPerformanceMode) (ma_AAudioStreamBuilder* pBuilder, ma_aaudio_performance_mode_t mode); typedef void (* MA_PFN_AAudioStreamBuilder_setUsage) (ma_AAudioStreamBuilder* pBuilder, ma_aaudio_usage_t contentType); typedef void (* MA_PFN_AAudioStreamBuilder_setContentType) (ma_AAudioStreamBuilder* pBuilder, ma_aaudio_content_type_t contentType); typedef void (* MA_PFN_AAudioStreamBuilder_setInputPreset) (ma_AAudioStreamBuilder* pBuilder, ma_aaudio_input_preset_t inputPreset); typedef ma_aaudio_result_t (* MA_PFN_AAudioStreamBuilder_openStream) (ma_AAudioStreamBuilder* pBuilder, ma_AAudioStream** ppStream); typedef ma_aaudio_result_t (* MA_PFN_AAudioStream_close) (ma_AAudioStream* pStream); typedef ma_aaudio_stream_state_t (* MA_PFN_AAudioStream_getState) (ma_AAudioStream* pStream); typedef ma_aaudio_result_t (* MA_PFN_AAudioStream_waitForStateChange) (ma_AAudioStream* pStream, ma_aaudio_stream_state_t inputState, ma_aaudio_stream_state_t* pNextState, int64_t timeoutInNanoseconds); typedef ma_aaudio_format_t (* MA_PFN_AAudioStream_getFormat) (ma_AAudioStream* pStream); typedef int32_t (* MA_PFN_AAudioStream_getChannelCount) (ma_AAudioStream* pStream); typedef int32_t (* MA_PFN_AAudioStream_getSampleRate) (ma_AAudioStream* pStream); typedef int32_t (* MA_PFN_AAudioStream_getBufferCapacityInFrames) (ma_AAudioStream* pStream); typedef int32_t (* MA_PFN_AAudioStream_getFramesPerDataCallback) (ma_AAudioStream* pStream); typedef int32_t (* MA_PFN_AAudioStream_getFramesPerBurst) (ma_AAudioStream* pStream); typedef ma_aaudio_result_t (* MA_PFN_AAudioStream_requestStart) (ma_AAudioStream* pStream); typedef ma_aaudio_result_t (* MA_PFN_AAudioStream_requestStop) (ma_AAudioStream* pStream); static ma_result ma_result_from_aaudio(ma_aaudio_result_t resultAA) { switch (resultAA) { case MA_AAUDIO_OK: return MA_SUCCESS; default: break; } return MA_ERROR; } static ma_aaudio_usage_t ma_to_usage__aaudio(ma_aaudio_usage usage) { switch (usage) { case ma_aaudio_usage_media: return MA_AAUDIO_USAGE_MEDIA; case ma_aaudio_usage_voice_communication: return MA_AAUDIO_USAGE_VOICE_COMMUNICATION; case ma_aaudio_usage_voice_communication_signalling: return MA_AAUDIO_USAGE_VOICE_COMMUNICATION_SIGNALLING; case ma_aaudio_usage_alarm: return MA_AAUDIO_USAGE_ALARM; case ma_aaudio_usage_notification: return MA_AAUDIO_USAGE_NOTIFICATION; case ma_aaudio_usage_notification_ringtone: return MA_AAUDIO_USAGE_NOTIFICATION_RINGTONE; case ma_aaudio_usage_notification_event: return MA_AAUDIO_USAGE_NOTIFICATION_EVENT; case ma_aaudio_usage_assistance_accessibility: return MA_AAUDIO_USAGE_ASSISTANCE_ACCESSIBILITY; case ma_aaudio_usage_assistance_navigation_guidance: return MA_AAUDIO_USAGE_ASSISTANCE_NAVIGATION_GUIDANCE; case ma_aaudio_usage_assistance_sonification: return MA_AAUDIO_USAGE_ASSISTANCE_SONIFICATION; case ma_aaudio_usage_game: return MA_AAUDIO_USAGE_GAME; case ma_aaudio_usage_assitant: return MA_AAUDIO_USAGE_ASSISTANT; case ma_aaudio_usage_emergency: return MA_AAUDIO_SYSTEM_USAGE_EMERGENCY; case ma_aaudio_usage_safety: return MA_AAUDIO_SYSTEM_USAGE_SAFETY; case ma_aaudio_usage_vehicle_status: return MA_AAUDIO_SYSTEM_USAGE_VEHICLE_STATUS; case ma_aaudio_usage_announcement: return MA_AAUDIO_SYSTEM_USAGE_ANNOUNCEMENT; default: break; } return MA_AAUDIO_USAGE_MEDIA; } static ma_aaudio_content_type_t ma_to_content_type__aaudio(ma_aaudio_content_type contentType) { switch (contentType) { case ma_aaudio_content_type_speech: return MA_AAUDIO_CONTENT_TYPE_SPEECH; case ma_aaudio_content_type_music: return MA_AAUDIO_CONTENT_TYPE_MUSIC; case ma_aaudio_content_type_movie: return MA_AAUDIO_CONTENT_TYPE_MOVIE; case ma_aaudio_content_type_sonification: return MA_AAUDIO_CONTENT_TYPE_SONIFICATION; default: break; } return MA_AAUDIO_CONTENT_TYPE_SPEECH; } static ma_aaudio_input_preset_t ma_to_input_preset__aaudio(ma_aaudio_input_preset inputPreset) { switch (inputPreset) { case ma_aaudio_input_preset_generic: return MA_AAUDIO_INPUT_PRESET_GENERIC; case ma_aaudio_input_preset_camcorder: return MA_AAUDIO_INPUT_PRESET_CAMCORDER; case ma_aaudio_input_preset_voice_recognition: return MA_AAUDIO_INPUT_PRESET_VOICE_RECOGNITION; case ma_aaudio_input_preset_voice_communication: return MA_AAUDIO_INPUT_PRESET_VOICE_COMMUNICATION; case ma_aaudio_input_preset_unprocessed: return MA_AAUDIO_INPUT_PRESET_UNPROCESSED; case ma_aaudio_input_preset_voice_performance: return MA_AAUDIO_INPUT_PRESET_VOICE_PERFORMANCE; default: break; } return MA_AAUDIO_INPUT_PRESET_GENERIC; } static void ma_stream_error_callback__aaudio(ma_AAudioStream* pStream, void* pUserData, ma_aaudio_result_t error) { ma_device* pDevice = (ma_device*)pUserData; MA_ASSERT(pDevice != NULL); (void)error; ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[AAudio] ERROR CALLBACK: error=%d, AAudioStream_getState()=%d\n", error, ((MA_PFN_AAudioStream_getState)pDevice->pContext->aaudio.AAudioStream_getState)(pStream)); /* From the documentation for AAudio, when a device is disconnected all we can do is stop it. However, we cannot stop it from the callback - we need to do it from another thread. Therefore we are going to use an event thread for the AAudio backend to do this cleanly and safely. */ if (((MA_PFN_AAudioStream_getState)pDevice->pContext->aaudio.AAudioStream_getState)(pStream) == MA_AAUDIO_STREAM_STATE_DISCONNECTED) { /* We need to post a job to the job thread for processing. This will reroute the device by reinitializing the stream. */ ma_result result; ma_job job = ma_job_init(MA_JOB_TYPE_DEVICE_AAUDIO_REROUTE); job.data.device.aaudio.reroute.pDevice = pDevice; if (pStream == pDevice->aaudio.pStreamCapture) { job.data.device.aaudio.reroute.deviceType = ma_device_type_capture; } else { job.data.device.aaudio.reroute.deviceType = ma_device_type_playback; } result = ma_device_job_thread_post(&pDevice->pContext->aaudio.jobThread, &job); if (result != MA_SUCCESS) { ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[AAudio] Device Disconnected. Failed to post job for rerouting.\n"); return; } } } static ma_aaudio_data_callback_result_t ma_stream_data_callback_capture__aaudio(ma_AAudioStream* pStream, void* pUserData, void* pAudioData, int32_t frameCount) { ma_device* pDevice = (ma_device*)pUserData; MA_ASSERT(pDevice != NULL); ma_device_handle_backend_data_callback(pDevice, NULL, pAudioData, frameCount); (void)pStream; return MA_AAUDIO_CALLBACK_RESULT_CONTINUE; } static ma_aaudio_data_callback_result_t ma_stream_data_callback_playback__aaudio(ma_AAudioStream* pStream, void* pUserData, void* pAudioData, int32_t frameCount) { ma_device* pDevice = (ma_device*)pUserData; MA_ASSERT(pDevice != NULL); ma_device_handle_backend_data_callback(pDevice, pAudioData, NULL, frameCount); (void)pStream; return MA_AAUDIO_CALLBACK_RESULT_CONTINUE; } static ma_result ma_create_and_configure_AAudioStreamBuilder__aaudio(ma_context* pContext, const ma_device_id* pDeviceID, ma_device_type deviceType, ma_share_mode shareMode, const ma_device_descriptor* pDescriptor, const ma_device_config* pConfig, ma_device* pDevice, ma_AAudioStreamBuilder** ppBuilder) { ma_AAudioStreamBuilder* pBuilder; ma_aaudio_result_t resultAA; ma_uint32 bufferCapacityInFrames; /* Safety. */ *ppBuilder = NULL; resultAA = ((MA_PFN_AAudio_createStreamBuilder)pContext->aaudio.AAudio_createStreamBuilder)(&pBuilder); if (resultAA != MA_AAUDIO_OK) { return ma_result_from_aaudio(resultAA); } if (pDeviceID != NULL) { ((MA_PFN_AAudioStreamBuilder_setDeviceId)pContext->aaudio.AAudioStreamBuilder_setDeviceId)(pBuilder, pDeviceID->aaudio); } ((MA_PFN_AAudioStreamBuilder_setDirection)pContext->aaudio.AAudioStreamBuilder_setDirection)(pBuilder, (deviceType == ma_device_type_playback) ? MA_AAUDIO_DIRECTION_OUTPUT : MA_AAUDIO_DIRECTION_INPUT); ((MA_PFN_AAudioStreamBuilder_setSharingMode)pContext->aaudio.AAudioStreamBuilder_setSharingMode)(pBuilder, (shareMode == ma_share_mode_shared) ? MA_AAUDIO_SHARING_MODE_SHARED : MA_AAUDIO_SHARING_MODE_EXCLUSIVE); /* If we have a device descriptor make sure we configure the stream builder to take our requested parameters. */ if (pDescriptor != NULL) { MA_ASSERT(pConfig != NULL); /* We must have a device config if we also have a descriptor. The config is required for AAudio specific configuration options. */ if (pDescriptor->sampleRate != 0) { ((MA_PFN_AAudioStreamBuilder_setSampleRate)pContext->aaudio.AAudioStreamBuilder_setSampleRate)(pBuilder, pDescriptor->sampleRate); } if (deviceType == ma_device_type_capture) { if (pDescriptor->channels != 0) { ((MA_PFN_AAudioStreamBuilder_setChannelCount)pContext->aaudio.AAudioStreamBuilder_setChannelCount)(pBuilder, pDescriptor->channels); } if (pDescriptor->format != ma_format_unknown) { ((MA_PFN_AAudioStreamBuilder_setFormat)pContext->aaudio.AAudioStreamBuilder_setFormat)(pBuilder, (pDescriptor->format == ma_format_s16) ? MA_AAUDIO_FORMAT_PCM_I16 : MA_AAUDIO_FORMAT_PCM_FLOAT); } } else { if (pDescriptor->channels != 0) { ((MA_PFN_AAudioStreamBuilder_setChannelCount)pContext->aaudio.AAudioStreamBuilder_setChannelCount)(pBuilder, pDescriptor->channels); } if (pDescriptor->format != ma_format_unknown) { ((MA_PFN_AAudioStreamBuilder_setFormat)pContext->aaudio.AAudioStreamBuilder_setFormat)(pBuilder, (pDescriptor->format == ma_format_s16) ? MA_AAUDIO_FORMAT_PCM_I16 : MA_AAUDIO_FORMAT_PCM_FLOAT); } } /* AAudio is annoying when it comes to it's buffer calculation stuff because it doesn't let you retrieve the actual sample rate until after you've opened the stream. But you need to configure the buffer capacity before you open the stream... :/ To solve, we're just going to assume MA_DEFAULT_SAMPLE_RATE (48000) and move on. */ bufferCapacityInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptor, pDescriptor->sampleRate, pConfig->performanceProfile) * pDescriptor->periodCount; ((MA_PFN_AAudioStreamBuilder_setBufferCapacityInFrames)pContext->aaudio.AAudioStreamBuilder_setBufferCapacityInFrames)(pBuilder, bufferCapacityInFrames); ((MA_PFN_AAudioStreamBuilder_setFramesPerDataCallback)pContext->aaudio.AAudioStreamBuilder_setFramesPerDataCallback)(pBuilder, bufferCapacityInFrames / pDescriptor->periodCount); if (deviceType == ma_device_type_capture) { if (pConfig->aaudio.inputPreset != ma_aaudio_input_preset_default && pContext->aaudio.AAudioStreamBuilder_setInputPreset != NULL) { ((MA_PFN_AAudioStreamBuilder_setInputPreset)pContext->aaudio.AAudioStreamBuilder_setInputPreset)(pBuilder, ma_to_input_preset__aaudio(pConfig->aaudio.inputPreset)); } ((MA_PFN_AAudioStreamBuilder_setDataCallback)pContext->aaudio.AAudioStreamBuilder_setDataCallback)(pBuilder, ma_stream_data_callback_capture__aaudio, (void*)pDevice); } else { if (pConfig->aaudio.usage != ma_aaudio_usage_default && pContext->aaudio.AAudioStreamBuilder_setUsage != NULL) { ((MA_PFN_AAudioStreamBuilder_setUsage)pContext->aaudio.AAudioStreamBuilder_setUsage)(pBuilder, ma_to_usage__aaudio(pConfig->aaudio.usage)); } if (pConfig->aaudio.contentType != ma_aaudio_content_type_default && pContext->aaudio.AAudioStreamBuilder_setContentType != NULL) { ((MA_PFN_AAudioStreamBuilder_setContentType)pContext->aaudio.AAudioStreamBuilder_setContentType)(pBuilder, ma_to_content_type__aaudio(pConfig->aaudio.contentType)); } ((MA_PFN_AAudioStreamBuilder_setDataCallback)pContext->aaudio.AAudioStreamBuilder_setDataCallback)(pBuilder, ma_stream_data_callback_playback__aaudio, (void*)pDevice); } /* Not sure how this affects things, but since there's a mapping between miniaudio's performance profiles and AAudio's performance modes, let go ahead and set it. */ ((MA_PFN_AAudioStreamBuilder_setPerformanceMode)pContext->aaudio.AAudioStreamBuilder_setPerformanceMode)(pBuilder, (pConfig->performanceProfile == ma_performance_profile_low_latency) ? MA_AAUDIO_PERFORMANCE_MODE_LOW_LATENCY : MA_AAUDIO_PERFORMANCE_MODE_NONE); /* We need to set an error callback to detect device changes. */ if (pDevice != NULL) { /* <-- pDevice should never be null if pDescriptor is not null, which is always the case if we hit this branch. Check anyway for safety. */ ((MA_PFN_AAudioStreamBuilder_setErrorCallback)pContext->aaudio.AAudioStreamBuilder_setErrorCallback)(pBuilder, ma_stream_error_callback__aaudio, (void*)pDevice); } } *ppBuilder = pBuilder; return MA_SUCCESS; } static ma_result ma_open_stream_and_close_builder__aaudio(ma_context* pContext, ma_AAudioStreamBuilder* pBuilder, ma_AAudioStream** ppStream) { ma_result result; result = ma_result_from_aaudio(((MA_PFN_AAudioStreamBuilder_openStream)pContext->aaudio.AAudioStreamBuilder_openStream)(pBuilder, ppStream)); ((MA_PFN_AAudioStreamBuilder_delete)pContext->aaudio.AAudioStreamBuilder_delete)(pBuilder); return result; } static ma_result ma_open_stream_basic__aaudio(ma_context* pContext, const ma_device_id* pDeviceID, ma_device_type deviceType, ma_share_mode shareMode, ma_AAudioStream** ppStream) { ma_result result; ma_AAudioStreamBuilder* pBuilder; *ppStream = NULL; result = ma_create_and_configure_AAudioStreamBuilder__aaudio(pContext, pDeviceID, deviceType, shareMode, NULL, NULL, NULL, &pBuilder); if (result != MA_SUCCESS) { return result; } return ma_open_stream_and_close_builder__aaudio(pContext, pBuilder, ppStream); } static ma_result ma_open_stream__aaudio(ma_device* pDevice, const ma_device_config* pConfig, ma_device_type deviceType, const ma_device_descriptor* pDescriptor, ma_AAudioStream** ppStream) { ma_result result; ma_AAudioStreamBuilder* pBuilder; MA_ASSERT(pDevice != NULL); MA_ASSERT(pDescriptor != NULL); MA_ASSERT(deviceType != ma_device_type_duplex); /* This function should not be called for a full-duplex device type. */ *ppStream = NULL; result = ma_create_and_configure_AAudioStreamBuilder__aaudio(pDevice->pContext, pDescriptor->pDeviceID, deviceType, pDescriptor->shareMode, pDescriptor, pConfig, pDevice, &pBuilder); if (result != MA_SUCCESS) { return result; } return ma_open_stream_and_close_builder__aaudio(pDevice->pContext, pBuilder, ppStream); } static ma_result ma_close_stream__aaudio(ma_context* pContext, ma_AAudioStream* pStream) { return ma_result_from_aaudio(((MA_PFN_AAudioStream_close)pContext->aaudio.AAudioStream_close)(pStream)); } static ma_bool32 ma_has_default_device__aaudio(ma_context* pContext, ma_device_type deviceType) { /* The only way to know this is to try creating a stream. */ ma_AAudioStream* pStream; ma_result result = ma_open_stream_basic__aaudio(pContext, NULL, deviceType, ma_share_mode_shared, &pStream); if (result != MA_SUCCESS) { return MA_FALSE; } ma_close_stream__aaudio(pContext, pStream); return MA_TRUE; } static ma_result ma_wait_for_simple_state_transition__aaudio(ma_context* pContext, ma_AAudioStream* pStream, ma_aaudio_stream_state_t oldState, ma_aaudio_stream_state_t newState) { ma_aaudio_stream_state_t actualNewState; ma_aaudio_result_t resultAA = ((MA_PFN_AAudioStream_waitForStateChange)pContext->aaudio.AAudioStream_waitForStateChange)(pStream, oldState, &actualNewState, 5000000000); /* 5 second timeout. */ if (resultAA != MA_AAUDIO_OK) { return ma_result_from_aaudio(resultAA); } if (newState != actualNewState) { return MA_ERROR; /* Failed to transition into the expected state. */ } return MA_SUCCESS; } static ma_result ma_context_enumerate_devices__aaudio(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData) { ma_bool32 cbResult = MA_TRUE; MA_ASSERT(pContext != NULL); MA_ASSERT(callback != NULL); /* Unfortunately AAudio does not have an enumeration API. Therefore I'm only going to report default devices, but only if it can instantiate a stream. */ /* Playback. */ if (cbResult) { ma_device_info deviceInfo; MA_ZERO_OBJECT(&deviceInfo); deviceInfo.id.aaudio = MA_AAUDIO_UNSPECIFIED; ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); if (ma_has_default_device__aaudio(pContext, ma_device_type_playback)) { cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData); } } /* Capture. */ if (cbResult) { ma_device_info deviceInfo; MA_ZERO_OBJECT(&deviceInfo); deviceInfo.id.aaudio = MA_AAUDIO_UNSPECIFIED; ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); if (ma_has_default_device__aaudio(pContext, ma_device_type_capture)) { cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData); } } return MA_SUCCESS; } static void ma_context_add_native_data_format_from_AAudioStream_ex__aaudio(ma_context* pContext, ma_AAudioStream* pStream, ma_format format, ma_uint32 flags, ma_device_info* pDeviceInfo) { MA_ASSERT(pContext != NULL); MA_ASSERT(pStream != NULL); MA_ASSERT(pDeviceInfo != NULL); pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].format = format; pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].channels = ((MA_PFN_AAudioStream_getChannelCount)pContext->aaudio.AAudioStream_getChannelCount)(pStream); pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].sampleRate = ((MA_PFN_AAudioStream_getSampleRate)pContext->aaudio.AAudioStream_getSampleRate)(pStream); pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].flags = flags; pDeviceInfo->nativeDataFormatCount += 1; } static void ma_context_add_native_data_format_from_AAudioStream__aaudio(ma_context* pContext, ma_AAudioStream* pStream, ma_uint32 flags, ma_device_info* pDeviceInfo) { /* AAudio supports s16 and f32. */ ma_context_add_native_data_format_from_AAudioStream_ex__aaudio(pContext, pStream, ma_format_f32, flags, pDeviceInfo); ma_context_add_native_data_format_from_AAudioStream_ex__aaudio(pContext, pStream, ma_format_s16, flags, pDeviceInfo); } static ma_result ma_context_get_device_info__aaudio(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo) { ma_AAudioStream* pStream; ma_result result; MA_ASSERT(pContext != NULL); /* ID */ if (pDeviceID != NULL) { pDeviceInfo->id.aaudio = pDeviceID->aaudio; } else { pDeviceInfo->id.aaudio = MA_AAUDIO_UNSPECIFIED; } /* Name */ if (deviceType == ma_device_type_playback) { ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); } else { ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); } pDeviceInfo->nativeDataFormatCount = 0; /* We'll need to open the device to get accurate sample rate and channel count information. */ result = ma_open_stream_basic__aaudio(pContext, pDeviceID, deviceType, ma_share_mode_shared, &pStream); if (result != MA_SUCCESS) { return result; } ma_context_add_native_data_format_from_AAudioStream__aaudio(pContext, pStream, 0, pDeviceInfo); ma_close_stream__aaudio(pContext, pStream); pStream = NULL; return MA_SUCCESS; } static ma_result ma_device_uninit__aaudio(ma_device* pDevice) { MA_ASSERT(pDevice != NULL); if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { ma_close_stream__aaudio(pDevice->pContext, (ma_AAudioStream*)pDevice->aaudio.pStreamCapture); pDevice->aaudio.pStreamCapture = NULL; } if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { ma_close_stream__aaudio(pDevice->pContext, (ma_AAudioStream*)pDevice->aaudio.pStreamPlayback); pDevice->aaudio.pStreamPlayback = NULL; } return MA_SUCCESS; } static ma_result ma_device_init_by_type__aaudio(ma_device* pDevice, const ma_device_config* pConfig, ma_device_type deviceType, ma_device_descriptor* pDescriptor, ma_AAudioStream** ppStream) { ma_result result; int32_t bufferCapacityInFrames; int32_t framesPerDataCallback; ma_AAudioStream* pStream; MA_ASSERT(pDevice != NULL); MA_ASSERT(pConfig != NULL); MA_ASSERT(pDescriptor != NULL); *ppStream = NULL; /* Safety. */ /* First step is to open the stream. From there we'll be able to extract the internal configuration. */ result = ma_open_stream__aaudio(pDevice, pConfig, deviceType, pDescriptor, &pStream); if (result != MA_SUCCESS) { return result; /* Failed to open the AAudio stream. */ } /* Now extract the internal configuration. */ pDescriptor->format = (((MA_PFN_AAudioStream_getFormat)pDevice->pContext->aaudio.AAudioStream_getFormat)(pStream) == MA_AAUDIO_FORMAT_PCM_I16) ? ma_format_s16 : ma_format_f32; pDescriptor->channels = ((MA_PFN_AAudioStream_getChannelCount)pDevice->pContext->aaudio.AAudioStream_getChannelCount)(pStream); pDescriptor->sampleRate = ((MA_PFN_AAudioStream_getSampleRate)pDevice->pContext->aaudio.AAudioStream_getSampleRate)(pStream); /* For the channel map we need to be sure we don't overflow any buffers. */ if (pDescriptor->channels <= MA_MAX_CHANNELS) { ma_channel_map_init_standard(ma_standard_channel_map_default, pDescriptor->channelMap, ma_countof(pDescriptor->channelMap), pDescriptor->channels); /* <-- Cannot find info on channel order, so assuming a default. */ } else { ma_channel_map_init_blank(pDescriptor->channelMap, MA_MAX_CHANNELS); /* Too many channels. Use a blank channel map. */ } bufferCapacityInFrames = ((MA_PFN_AAudioStream_getBufferCapacityInFrames)pDevice->pContext->aaudio.AAudioStream_getBufferCapacityInFrames)(pStream); framesPerDataCallback = ((MA_PFN_AAudioStream_getFramesPerDataCallback)pDevice->pContext->aaudio.AAudioStream_getFramesPerDataCallback)(pStream); if (framesPerDataCallback > 0) { pDescriptor->periodSizeInFrames = framesPerDataCallback; pDescriptor->periodCount = bufferCapacityInFrames / framesPerDataCallback; } else { pDescriptor->periodSizeInFrames = bufferCapacityInFrames; pDescriptor->periodCount = 1; } *ppStream = pStream; return MA_SUCCESS; } static ma_result ma_device_init__aaudio(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture) { ma_result result; MA_ASSERT(pDevice != NULL); if (pConfig->deviceType == ma_device_type_loopback) { return MA_DEVICE_TYPE_NOT_SUPPORTED; } pDevice->aaudio.usage = pConfig->aaudio.usage; pDevice->aaudio.contentType = pConfig->aaudio.contentType; pDevice->aaudio.inputPreset = pConfig->aaudio.inputPreset; pDevice->aaudio.noAutoStartAfterReroute = pConfig->aaudio.noAutoStartAfterReroute; if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { result = ma_device_init_by_type__aaudio(pDevice, pConfig, ma_device_type_capture, pDescriptorCapture, (ma_AAudioStream**)&pDevice->aaudio.pStreamCapture); if (result != MA_SUCCESS) { return result; } } if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { result = ma_device_init_by_type__aaudio(pDevice, pConfig, ma_device_type_playback, pDescriptorPlayback, (ma_AAudioStream**)&pDevice->aaudio.pStreamPlayback); if (result != MA_SUCCESS) { return result; } } return MA_SUCCESS; } static ma_result ma_device_start_stream__aaudio(ma_device* pDevice, ma_AAudioStream* pStream) { ma_aaudio_result_t resultAA; ma_aaudio_stream_state_t currentState; MA_ASSERT(pDevice != NULL); resultAA = ((MA_PFN_AAudioStream_requestStart)pDevice->pContext->aaudio.AAudioStream_requestStart)(pStream); if (resultAA != MA_AAUDIO_OK) { return ma_result_from_aaudio(resultAA); } /* Do we actually need to wait for the device to transition into it's started state? */ /* The device should be in either a starting or started state. If it's not set to started we need to wait for it to transition. It should go from starting to started. */ currentState = ((MA_PFN_AAudioStream_getState)pDevice->pContext->aaudio.AAudioStream_getState)(pStream); if (currentState != MA_AAUDIO_STREAM_STATE_STARTED) { ma_result result; if (currentState != MA_AAUDIO_STREAM_STATE_STARTING) { return MA_ERROR; /* Expecting the stream to be a starting or started state. */ } result = ma_wait_for_simple_state_transition__aaudio(pDevice->pContext, pStream, currentState, MA_AAUDIO_STREAM_STATE_STARTED); if (result != MA_SUCCESS) { return result; } } return MA_SUCCESS; } static ma_result ma_device_stop_stream__aaudio(ma_device* pDevice, ma_AAudioStream* pStream) { ma_aaudio_result_t resultAA; ma_aaudio_stream_state_t currentState; MA_ASSERT(pDevice != NULL); /* From the AAudio documentation: The stream will stop after all of the data currently buffered has been played. This maps with miniaudio's requirement that device's be drained which means we don't need to implement any draining logic. */ currentState = ((MA_PFN_AAudioStream_getState)pDevice->pContext->aaudio.AAudioStream_getState)(pStream); if (currentState == MA_AAUDIO_STREAM_STATE_DISCONNECTED) { return MA_SUCCESS; /* The device is disconnected. Don't try stopping it. */ } resultAA = ((MA_PFN_AAudioStream_requestStop)pDevice->pContext->aaudio.AAudioStream_requestStop)(pStream); if (resultAA != MA_AAUDIO_OK) { return ma_result_from_aaudio(resultAA); } /* The device should be in either a stopping or stopped state. If it's not set to started we need to wait for it to transition. It should go from stopping to stopped. */ currentState = ((MA_PFN_AAudioStream_getState)pDevice->pContext->aaudio.AAudioStream_getState)(pStream); if (currentState != MA_AAUDIO_STREAM_STATE_STOPPED) { ma_result result; if (currentState != MA_AAUDIO_STREAM_STATE_STOPPING) { return MA_ERROR; /* Expecting the stream to be a stopping or stopped state. */ } result = ma_wait_for_simple_state_transition__aaudio(pDevice->pContext, pStream, currentState, MA_AAUDIO_STREAM_STATE_STOPPED); if (result != MA_SUCCESS) { return result; } } return MA_SUCCESS; } static ma_result ma_device_start__aaudio(ma_device* pDevice) { MA_ASSERT(pDevice != NULL); if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { ma_result result = ma_device_start_stream__aaudio(pDevice, (ma_AAudioStream*)pDevice->aaudio.pStreamCapture); if (result != MA_SUCCESS) { return result; } } if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { ma_result result = ma_device_start_stream__aaudio(pDevice, (ma_AAudioStream*)pDevice->aaudio.pStreamPlayback); if (result != MA_SUCCESS) { if (pDevice->type == ma_device_type_duplex) { ma_device_stop_stream__aaudio(pDevice, (ma_AAudioStream*)pDevice->aaudio.pStreamCapture); } return result; } } return MA_SUCCESS; } static ma_result ma_device_stop__aaudio(ma_device* pDevice) { MA_ASSERT(pDevice != NULL); if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { ma_result result = ma_device_stop_stream__aaudio(pDevice, (ma_AAudioStream*)pDevice->aaudio.pStreamCapture); if (result != MA_SUCCESS) { return result; } } if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { ma_result result = ma_device_stop_stream__aaudio(pDevice, (ma_AAudioStream*)pDevice->aaudio.pStreamPlayback); if (result != MA_SUCCESS) { return result; } } ma_device__on_notification_stopped(pDevice); return MA_SUCCESS; } static ma_result ma_device_reinit__aaudio(ma_device* pDevice, ma_device_type deviceType) { ma_result result; MA_ASSERT(pDevice != NULL); /* The first thing to do is close the streams. */ if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex) { ma_close_stream__aaudio(pDevice->pContext, (ma_AAudioStream*)pDevice->aaudio.pStreamCapture); pDevice->aaudio.pStreamCapture = NULL; } if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) { ma_close_stream__aaudio(pDevice->pContext, (ma_AAudioStream*)pDevice->aaudio.pStreamPlayback); pDevice->aaudio.pStreamPlayback = NULL; } /* Now we need to reinitialize each streams. The hardest part with this is just filling output the config and descriptors. */ { ma_device_config deviceConfig; ma_device_descriptor descriptorPlayback; ma_device_descriptor descriptorCapture; deviceConfig = ma_device_config_init(deviceType); deviceConfig.playback.pDeviceID = NULL; /* Only doing rerouting with default devices. */ deviceConfig.playback.shareMode = pDevice->playback.shareMode; deviceConfig.playback.format = pDevice->playback.format; deviceConfig.playback.channels = pDevice->playback.channels; deviceConfig.capture.pDeviceID = NULL; /* Only doing rerouting with default devices. */ deviceConfig.capture.shareMode = pDevice->capture.shareMode; deviceConfig.capture.format = pDevice->capture.format; deviceConfig.capture.channels = pDevice->capture.channels; deviceConfig.sampleRate = pDevice->sampleRate; deviceConfig.aaudio.usage = pDevice->aaudio.usage; deviceConfig.aaudio.contentType = pDevice->aaudio.contentType; deviceConfig.aaudio.inputPreset = pDevice->aaudio.inputPreset; deviceConfig.aaudio.noAutoStartAfterReroute = pDevice->aaudio.noAutoStartAfterReroute; deviceConfig.periods = 1; /* Try to get an accurate period size. */ if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) { deviceConfig.periodSizeInFrames = pDevice->playback.internalPeriodSizeInFrames; } else { deviceConfig.periodSizeInFrames = pDevice->capture.internalPeriodSizeInFrames; } if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex || deviceType == ma_device_type_loopback) { descriptorCapture.pDeviceID = deviceConfig.capture.pDeviceID; descriptorCapture.shareMode = deviceConfig.capture.shareMode; descriptorCapture.format = deviceConfig.capture.format; descriptorCapture.channels = deviceConfig.capture.channels; descriptorCapture.sampleRate = deviceConfig.sampleRate; descriptorCapture.periodSizeInFrames = deviceConfig.periodSizeInFrames; descriptorCapture.periodCount = deviceConfig.periods; } if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) { descriptorPlayback.pDeviceID = deviceConfig.playback.pDeviceID; descriptorPlayback.shareMode = deviceConfig.playback.shareMode; descriptorPlayback.format = deviceConfig.playback.format; descriptorPlayback.channels = deviceConfig.playback.channels; descriptorPlayback.sampleRate = deviceConfig.sampleRate; descriptorPlayback.periodSizeInFrames = deviceConfig.periodSizeInFrames; descriptorPlayback.periodCount = deviceConfig.periods; } result = ma_device_init__aaudio(pDevice, &deviceConfig, &descriptorPlayback, &descriptorCapture); if (result != MA_SUCCESS) { return result; } result = ma_device_post_init(pDevice, deviceType, &descriptorPlayback, &descriptorCapture); if (result != MA_SUCCESS) { ma_device_uninit__aaudio(pDevice); return result; } /* We'll only ever do this in response to a reroute. */ ma_device__on_notification_rerouted(pDevice); /* If the device is started, start the streams. Maybe make this configurable? */ if (ma_device_get_state(pDevice) == ma_device_state_started) { if (pDevice->aaudio.noAutoStartAfterReroute == MA_FALSE) { ma_device_start__aaudio(pDevice); } else { ma_device_stop(pDevice); /* Do a full device stop so we set internal state correctly. */ } } return MA_SUCCESS; } } static ma_result ma_device_get_info__aaudio(ma_device* pDevice, ma_device_type type, ma_device_info* pDeviceInfo) { ma_AAudioStream* pStream = NULL; MA_ASSERT(pDevice != NULL); MA_ASSERT(type != ma_device_type_duplex); MA_ASSERT(pDeviceInfo != NULL); if (type == ma_device_type_playback) { pStream = (ma_AAudioStream*)pDevice->aaudio.pStreamCapture; pDeviceInfo->id.aaudio = pDevice->capture.id.aaudio; ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); /* Only supporting default devices. */ } if (type == ma_device_type_capture) { pStream = (ma_AAudioStream*)pDevice->aaudio.pStreamPlayback; pDeviceInfo->id.aaudio = pDevice->playback.id.aaudio; ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); /* Only supporting default devices. */ } /* Safety. Should never happen. */ if (pStream == NULL) { return MA_INVALID_OPERATION; } pDeviceInfo->nativeDataFormatCount = 0; ma_context_add_native_data_format_from_AAudioStream__aaudio(pDevice->pContext, pStream, 0, pDeviceInfo); return MA_SUCCESS; } static ma_result ma_context_uninit__aaudio(ma_context* pContext) { MA_ASSERT(pContext != NULL); MA_ASSERT(pContext->backend == ma_backend_aaudio); ma_device_job_thread_uninit(&pContext->aaudio.jobThread, &pContext->allocationCallbacks); ma_dlclose(pContext, pContext->aaudio.hAAudio); pContext->aaudio.hAAudio = NULL; return MA_SUCCESS; } static ma_result ma_context_init__aaudio(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks) { size_t i; const char* libNames[] = { "libaaudio.so" }; for (i = 0; i < ma_countof(libNames); ++i) { pContext->aaudio.hAAudio = ma_dlopen(pContext, libNames[i]); if (pContext->aaudio.hAAudio != NULL) { break; } } if (pContext->aaudio.hAAudio == NULL) { return MA_FAILED_TO_INIT_BACKEND; } pContext->aaudio.AAudio_createStreamBuilder = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudio_createStreamBuilder"); pContext->aaudio.AAudioStreamBuilder_delete = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_delete"); pContext->aaudio.AAudioStreamBuilder_setDeviceId = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setDeviceId"); pContext->aaudio.AAudioStreamBuilder_setDirection = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setDirection"); pContext->aaudio.AAudioStreamBuilder_setSharingMode = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setSharingMode"); pContext->aaudio.AAudioStreamBuilder_setFormat = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setFormat"); pContext->aaudio.AAudioStreamBuilder_setChannelCount = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setChannelCount"); pContext->aaudio.AAudioStreamBuilder_setSampleRate = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setSampleRate"); pContext->aaudio.AAudioStreamBuilder_setBufferCapacityInFrames = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setBufferCapacityInFrames"); pContext->aaudio.AAudioStreamBuilder_setFramesPerDataCallback = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setFramesPerDataCallback"); pContext->aaudio.AAudioStreamBuilder_setDataCallback = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setDataCallback"); pContext->aaudio.AAudioStreamBuilder_setErrorCallback = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setErrorCallback"); pContext->aaudio.AAudioStreamBuilder_setPerformanceMode = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setPerformanceMode"); pContext->aaudio.AAudioStreamBuilder_setUsage = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setUsage"); pContext->aaudio.AAudioStreamBuilder_setContentType = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setContentType"); pContext->aaudio.AAudioStreamBuilder_setInputPreset = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setInputPreset"); pContext->aaudio.AAudioStreamBuilder_openStream = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_openStream"); pContext->aaudio.AAudioStream_close = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_close"); pContext->aaudio.AAudioStream_getState = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_getState"); pContext->aaudio.AAudioStream_waitForStateChange = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_waitForStateChange"); pContext->aaudio.AAudioStream_getFormat = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_getFormat"); pContext->aaudio.AAudioStream_getChannelCount = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_getChannelCount"); pContext->aaudio.AAudioStream_getSampleRate = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_getSampleRate"); pContext->aaudio.AAudioStream_getBufferCapacityInFrames = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_getBufferCapacityInFrames"); pContext->aaudio.AAudioStream_getFramesPerDataCallback = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_getFramesPerDataCallback"); pContext->aaudio.AAudioStream_getFramesPerBurst = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_getFramesPerBurst"); pContext->aaudio.AAudioStream_requestStart = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_requestStart"); pContext->aaudio.AAudioStream_requestStop = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_requestStop"); pCallbacks->onContextInit = ma_context_init__aaudio; pCallbacks->onContextUninit = ma_context_uninit__aaudio; pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__aaudio; pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__aaudio; pCallbacks->onDeviceInit = ma_device_init__aaudio; pCallbacks->onDeviceUninit = ma_device_uninit__aaudio; pCallbacks->onDeviceStart = ma_device_start__aaudio; pCallbacks->onDeviceStop = ma_device_stop__aaudio; pCallbacks->onDeviceRead = NULL; /* Not used because AAudio is asynchronous. */ pCallbacks->onDeviceWrite = NULL; /* Not used because AAudio is asynchronous. */ pCallbacks->onDeviceDataLoop = NULL; /* Not used because AAudio is asynchronous. */ pCallbacks->onDeviceGetInfo = ma_device_get_info__aaudio; /* We need a job thread so we can deal with rerouting. */ { ma_result result; ma_device_job_thread_config jobThreadConfig; jobThreadConfig = ma_device_job_thread_config_init(); result = ma_device_job_thread_init(&jobThreadConfig, &pContext->allocationCallbacks, &pContext->aaudio.jobThread); if (result != MA_SUCCESS) { ma_dlclose(pContext, pContext->aaudio.hAAudio); pContext->aaudio.hAAudio = NULL; return result; } } (void)pConfig; return MA_SUCCESS; } static ma_result ma_job_process__device__aaudio_reroute(ma_job* pJob) { ma_device* pDevice; MA_ASSERT(pJob != NULL); pDevice = (ma_device*)pJob->data.device.aaudio.reroute.pDevice; MA_ASSERT(pDevice != NULL); /* Here is where we need to reroute the device. To do this we need to uninitialize the stream and reinitialize it. */ return ma_device_reinit__aaudio(pDevice, (ma_device_type)pJob->data.device.aaudio.reroute.deviceType); } #else /* Getting here means there is no AAudio backend so we need a no-op job implementation. */ static ma_result ma_job_process__device__aaudio_reroute(ma_job* pJob) { return ma_job_process__noop(pJob); } #endif /* AAudio */ /****************************************************************************** OpenSL|ES Backend ******************************************************************************/ #ifdef MA_HAS_OPENSL #include #ifdef MA_ANDROID #include #endif typedef SLresult (SLAPIENTRY * ma_slCreateEngine_proc)(SLObjectItf* pEngine, SLuint32 numOptions, SLEngineOption* pEngineOptions, SLuint32 numInterfaces, SLInterfaceID* pInterfaceIds, SLboolean* pInterfaceRequired); /* OpenSL|ES has one-per-application objects :( */ static SLObjectItf g_maEngineObjectSL = NULL; static SLEngineItf g_maEngineSL = NULL; static ma_uint32 g_maOpenSLInitCounter = 0; static ma_spinlock g_maOpenSLSpinlock = 0; /* For init/uninit. */ #define MA_OPENSL_OBJ(p) (*((SLObjectItf)(p))) #define MA_OPENSL_OUTPUTMIX(p) (*((SLOutputMixItf)(p))) #define MA_OPENSL_PLAY(p) (*((SLPlayItf)(p))) #define MA_OPENSL_RECORD(p) (*((SLRecordItf)(p))) #ifdef MA_ANDROID #define MA_OPENSL_BUFFERQUEUE(p) (*((SLAndroidSimpleBufferQueueItf)(p))) #else #define MA_OPENSL_BUFFERQUEUE(p) (*((SLBufferQueueItf)(p))) #endif static ma_result ma_result_from_OpenSL(SLuint32 result) { switch (result) { case SL_RESULT_SUCCESS: return MA_SUCCESS; case SL_RESULT_PRECONDITIONS_VIOLATED: return MA_ERROR; case SL_RESULT_PARAMETER_INVALID: return MA_INVALID_ARGS; case SL_RESULT_MEMORY_FAILURE: return MA_OUT_OF_MEMORY; case SL_RESULT_RESOURCE_ERROR: return MA_INVALID_DATA; case SL_RESULT_RESOURCE_LOST: return MA_ERROR; case SL_RESULT_IO_ERROR: return MA_IO_ERROR; case SL_RESULT_BUFFER_INSUFFICIENT: return MA_NO_SPACE; case SL_RESULT_CONTENT_CORRUPTED: return MA_INVALID_DATA; case SL_RESULT_CONTENT_UNSUPPORTED: return MA_FORMAT_NOT_SUPPORTED; case SL_RESULT_CONTENT_NOT_FOUND: return MA_ERROR; case SL_RESULT_PERMISSION_DENIED: return MA_ACCESS_DENIED; case SL_RESULT_FEATURE_UNSUPPORTED: return MA_NOT_IMPLEMENTED; case SL_RESULT_INTERNAL_ERROR: return MA_ERROR; case SL_RESULT_UNKNOWN_ERROR: return MA_ERROR; case SL_RESULT_OPERATION_ABORTED: return MA_ERROR; case SL_RESULT_CONTROL_LOST: return MA_ERROR; default: return MA_ERROR; } } /* Converts an individual OpenSL-style channel identifier (SL_SPEAKER_FRONT_LEFT, etc.) to miniaudio. */ static ma_uint8 ma_channel_id_to_ma__opensl(SLuint32 id) { switch (id) { case SL_SPEAKER_FRONT_LEFT: return MA_CHANNEL_FRONT_LEFT; case SL_SPEAKER_FRONT_RIGHT: return MA_CHANNEL_FRONT_RIGHT; case SL_SPEAKER_FRONT_CENTER: return MA_CHANNEL_FRONT_CENTER; case SL_SPEAKER_LOW_FREQUENCY: return MA_CHANNEL_LFE; case SL_SPEAKER_BACK_LEFT: return MA_CHANNEL_BACK_LEFT; case SL_SPEAKER_BACK_RIGHT: return MA_CHANNEL_BACK_RIGHT; case SL_SPEAKER_FRONT_LEFT_OF_CENTER: return MA_CHANNEL_FRONT_LEFT_CENTER; case SL_SPEAKER_FRONT_RIGHT_OF_CENTER: return MA_CHANNEL_FRONT_RIGHT_CENTER; case SL_SPEAKER_BACK_CENTER: return MA_CHANNEL_BACK_CENTER; case SL_SPEAKER_SIDE_LEFT: return MA_CHANNEL_SIDE_LEFT; case SL_SPEAKER_SIDE_RIGHT: return MA_CHANNEL_SIDE_RIGHT; case SL_SPEAKER_TOP_CENTER: return MA_CHANNEL_TOP_CENTER; case SL_SPEAKER_TOP_FRONT_LEFT: return MA_CHANNEL_TOP_FRONT_LEFT; case SL_SPEAKER_TOP_FRONT_CENTER: return MA_CHANNEL_TOP_FRONT_CENTER; case SL_SPEAKER_TOP_FRONT_RIGHT: return MA_CHANNEL_TOP_FRONT_RIGHT; case SL_SPEAKER_TOP_BACK_LEFT: return MA_CHANNEL_TOP_BACK_LEFT; case SL_SPEAKER_TOP_BACK_CENTER: return MA_CHANNEL_TOP_BACK_CENTER; case SL_SPEAKER_TOP_BACK_RIGHT: return MA_CHANNEL_TOP_BACK_RIGHT; default: return 0; } } /* Converts an individual miniaudio channel identifier (MA_CHANNEL_FRONT_LEFT, etc.) to OpenSL-style. */ static SLuint32 ma_channel_id_to_opensl(ma_uint8 id) { switch (id) { case MA_CHANNEL_MONO: return SL_SPEAKER_FRONT_CENTER; case MA_CHANNEL_FRONT_LEFT: return SL_SPEAKER_FRONT_LEFT; case MA_CHANNEL_FRONT_RIGHT: return SL_SPEAKER_FRONT_RIGHT; case MA_CHANNEL_FRONT_CENTER: return SL_SPEAKER_FRONT_CENTER; case MA_CHANNEL_LFE: return SL_SPEAKER_LOW_FREQUENCY; case MA_CHANNEL_BACK_LEFT: return SL_SPEAKER_BACK_LEFT; case MA_CHANNEL_BACK_RIGHT: return SL_SPEAKER_BACK_RIGHT; case MA_CHANNEL_FRONT_LEFT_CENTER: return SL_SPEAKER_FRONT_LEFT_OF_CENTER; case MA_CHANNEL_FRONT_RIGHT_CENTER: return SL_SPEAKER_FRONT_RIGHT_OF_CENTER; case MA_CHANNEL_BACK_CENTER: return SL_SPEAKER_BACK_CENTER; case MA_CHANNEL_SIDE_LEFT: return SL_SPEAKER_SIDE_LEFT; case MA_CHANNEL_SIDE_RIGHT: return SL_SPEAKER_SIDE_RIGHT; case MA_CHANNEL_TOP_CENTER: return SL_SPEAKER_TOP_CENTER; case MA_CHANNEL_TOP_FRONT_LEFT: return SL_SPEAKER_TOP_FRONT_LEFT; case MA_CHANNEL_TOP_FRONT_CENTER: return SL_SPEAKER_TOP_FRONT_CENTER; case MA_CHANNEL_TOP_FRONT_RIGHT: return SL_SPEAKER_TOP_FRONT_RIGHT; case MA_CHANNEL_TOP_BACK_LEFT: return SL_SPEAKER_TOP_BACK_LEFT; case MA_CHANNEL_TOP_BACK_CENTER: return SL_SPEAKER_TOP_BACK_CENTER; case MA_CHANNEL_TOP_BACK_RIGHT: return SL_SPEAKER_TOP_BACK_RIGHT; default: return 0; } } /* Converts a channel mapping to an OpenSL-style channel mask. */ static SLuint32 ma_channel_map_to_channel_mask__opensl(const ma_channel* pChannelMap, ma_uint32 channels) { SLuint32 channelMask = 0; ma_uint32 iChannel; for (iChannel = 0; iChannel < channels; ++iChannel) { channelMask |= ma_channel_id_to_opensl(pChannelMap[iChannel]); } return channelMask; } /* Converts an OpenSL-style channel mask to a miniaudio channel map. */ static void ma_channel_mask_to_channel_map__opensl(SLuint32 channelMask, ma_uint32 channels, ma_channel* pChannelMap) { if (channels == 1 && channelMask == 0) { pChannelMap[0] = MA_CHANNEL_MONO; } else if (channels == 2 && channelMask == 0) { pChannelMap[0] = MA_CHANNEL_FRONT_LEFT; pChannelMap[1] = MA_CHANNEL_FRONT_RIGHT; } else { if (channels == 1 && (channelMask & SL_SPEAKER_FRONT_CENTER) != 0) { pChannelMap[0] = MA_CHANNEL_MONO; } else { /* Just iterate over each bit. */ ma_uint32 iChannel = 0; ma_uint32 iBit; for (iBit = 0; iBit < 32 && iChannel < channels; ++iBit) { SLuint32 bitValue = (channelMask & (1UL << iBit)); if (bitValue != 0) { /* The bit is set. */ pChannelMap[iChannel] = ma_channel_id_to_ma__opensl(bitValue); iChannel += 1; } } } } } static SLuint32 ma_round_to_standard_sample_rate__opensl(SLuint32 samplesPerSec) { if (samplesPerSec <= SL_SAMPLINGRATE_8) { return SL_SAMPLINGRATE_8; } if (samplesPerSec <= SL_SAMPLINGRATE_11_025) { return SL_SAMPLINGRATE_11_025; } if (samplesPerSec <= SL_SAMPLINGRATE_12) { return SL_SAMPLINGRATE_12; } if (samplesPerSec <= SL_SAMPLINGRATE_16) { return SL_SAMPLINGRATE_16; } if (samplesPerSec <= SL_SAMPLINGRATE_22_05) { return SL_SAMPLINGRATE_22_05; } if (samplesPerSec <= SL_SAMPLINGRATE_24) { return SL_SAMPLINGRATE_24; } if (samplesPerSec <= SL_SAMPLINGRATE_32) { return SL_SAMPLINGRATE_32; } if (samplesPerSec <= SL_SAMPLINGRATE_44_1) { return SL_SAMPLINGRATE_44_1; } if (samplesPerSec <= SL_SAMPLINGRATE_48) { return SL_SAMPLINGRATE_48; } /* Android doesn't support more than 48000. */ #ifndef MA_ANDROID if (samplesPerSec <= SL_SAMPLINGRATE_64) { return SL_SAMPLINGRATE_64; } if (samplesPerSec <= SL_SAMPLINGRATE_88_2) { return SL_SAMPLINGRATE_88_2; } if (samplesPerSec <= SL_SAMPLINGRATE_96) { return SL_SAMPLINGRATE_96; } if (samplesPerSec <= SL_SAMPLINGRATE_192) { return SL_SAMPLINGRATE_192; } #endif return SL_SAMPLINGRATE_16; } static SLint32 ma_to_stream_type__opensl(ma_opensl_stream_type streamType) { switch (streamType) { case ma_opensl_stream_type_voice: return SL_ANDROID_STREAM_VOICE; case ma_opensl_stream_type_system: return SL_ANDROID_STREAM_SYSTEM; case ma_opensl_stream_type_ring: return SL_ANDROID_STREAM_RING; case ma_opensl_stream_type_media: return SL_ANDROID_STREAM_MEDIA; case ma_opensl_stream_type_alarm: return SL_ANDROID_STREAM_ALARM; case ma_opensl_stream_type_notification: return SL_ANDROID_STREAM_NOTIFICATION; default: break; } return SL_ANDROID_STREAM_VOICE; } static SLint32 ma_to_recording_preset__opensl(ma_opensl_recording_preset recordingPreset) { switch (recordingPreset) { case ma_opensl_recording_preset_generic: return SL_ANDROID_RECORDING_PRESET_GENERIC; case ma_opensl_recording_preset_camcorder: return SL_ANDROID_RECORDING_PRESET_CAMCORDER; case ma_opensl_recording_preset_voice_recognition: return SL_ANDROID_RECORDING_PRESET_VOICE_RECOGNITION; case ma_opensl_recording_preset_voice_communication: return SL_ANDROID_RECORDING_PRESET_VOICE_COMMUNICATION; case ma_opensl_recording_preset_voice_unprocessed: return SL_ANDROID_RECORDING_PRESET_UNPROCESSED; default: break; } return SL_ANDROID_RECORDING_PRESET_NONE; } static ma_result ma_context_enumerate_devices__opensl(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData) { ma_bool32 cbResult; MA_ASSERT(pContext != NULL); MA_ASSERT(callback != NULL); MA_ASSERT(g_maOpenSLInitCounter > 0); /* <-- If you trigger this it means you've either not initialized the context, or you've uninitialized it and then attempted to enumerate devices. */ if (g_maOpenSLInitCounter == 0) { return MA_INVALID_OPERATION; } /* TODO: Test Me. This is currently untested, so for now we are just returning default devices. */ #if 0 && !defined(MA_ANDROID) ma_bool32 isTerminated = MA_FALSE; SLuint32 pDeviceIDs[128]; SLint32 deviceCount = sizeof(pDeviceIDs) / sizeof(pDeviceIDs[0]); SLAudioIODeviceCapabilitiesItf deviceCaps; SLresult resultSL = (*g_maEngineObjectSL)->GetInterface(g_maEngineObjectSL, (SLInterfaceID)pContext->opensl.SL_IID_AUDIOIODEVICECAPABILITIES, &deviceCaps); if (resultSL != SL_RESULT_SUCCESS) { /* The interface may not be supported so just report a default device. */ goto return_default_device; } /* Playback */ if (!isTerminated) { resultSL = (*deviceCaps)->GetAvailableAudioOutputs(deviceCaps, &deviceCount, pDeviceIDs); if (resultSL != SL_RESULT_SUCCESS) { return ma_result_from_OpenSL(resultSL); } for (SLint32 iDevice = 0; iDevice < deviceCount; ++iDevice) { ma_device_info deviceInfo; MA_ZERO_OBJECT(&deviceInfo); deviceInfo.id.opensl = pDeviceIDs[iDevice]; SLAudioOutputDescriptor desc; resultSL = (*deviceCaps)->QueryAudioOutputCapabilities(deviceCaps, deviceInfo.id.opensl, &desc); if (resultSL == SL_RESULT_SUCCESS) { ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), (const char*)desc.pDeviceName, (size_t)-1); ma_bool32 cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData); if (cbResult == MA_FALSE) { isTerminated = MA_TRUE; break; } } } } /* Capture */ if (!isTerminated) { resultSL = (*deviceCaps)->GetAvailableAudioInputs(deviceCaps, &deviceCount, pDeviceIDs); if (resultSL != SL_RESULT_SUCCESS) { return ma_result_from_OpenSL(resultSL); } for (SLint32 iDevice = 0; iDevice < deviceCount; ++iDevice) { ma_device_info deviceInfo; MA_ZERO_OBJECT(&deviceInfo); deviceInfo.id.opensl = pDeviceIDs[iDevice]; SLAudioInputDescriptor desc; resultSL = (*deviceCaps)->QueryAudioInputCapabilities(deviceCaps, deviceInfo.id.opensl, &desc); if (resultSL == SL_RESULT_SUCCESS) { ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), (const char*)desc.deviceName, (size_t)-1); ma_bool32 cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData); if (cbResult == MA_FALSE) { isTerminated = MA_TRUE; break; } } } } return MA_SUCCESS; #else goto return_default_device; #endif return_default_device:; cbResult = MA_TRUE; /* Playback. */ if (cbResult) { ma_device_info deviceInfo; MA_ZERO_OBJECT(&deviceInfo); deviceInfo.id.opensl = SL_DEFAULTDEVICEID_AUDIOOUTPUT; ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData); } /* Capture. */ if (cbResult) { ma_device_info deviceInfo; MA_ZERO_OBJECT(&deviceInfo); deviceInfo.id.opensl = SL_DEFAULTDEVICEID_AUDIOINPUT; ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData); } return MA_SUCCESS; } static void ma_context_add_data_format_ex__opensl(ma_context* pContext, ma_format format, ma_uint32 channels, ma_uint32 sampleRate, ma_device_info* pDeviceInfo) { MA_ASSERT(pContext != NULL); MA_ASSERT(pDeviceInfo != NULL); pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].format = format; pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].channels = channels; pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].sampleRate = sampleRate; pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].flags = 0; pDeviceInfo->nativeDataFormatCount += 1; } static void ma_context_add_data_format__opensl(ma_context* pContext, ma_format format, ma_device_info* pDeviceInfo) { ma_uint32 minChannels = 1; ma_uint32 maxChannels = 2; ma_uint32 minSampleRate = (ma_uint32)ma_standard_sample_rate_8000; ma_uint32 maxSampleRate = (ma_uint32)ma_standard_sample_rate_48000; ma_uint32 iChannel; ma_uint32 iSampleRate; MA_ASSERT(pContext != NULL); MA_ASSERT(pDeviceInfo != NULL); /* Each sample format can support mono and stereo, and we'll support a small subset of standard rates (up to 48000). A better solution would be to somehow find a native sample rate. */ for (iChannel = minChannels; iChannel < maxChannels; iChannel += 1) { for (iSampleRate = 0; iSampleRate < ma_countof(g_maStandardSampleRatePriorities); iSampleRate += 1) { ma_uint32 standardSampleRate = g_maStandardSampleRatePriorities[iSampleRate]; if (standardSampleRate >= minSampleRate && standardSampleRate <= maxSampleRate) { ma_context_add_data_format_ex__opensl(pContext, format, iChannel, standardSampleRate, pDeviceInfo); } } } } static ma_result ma_context_get_device_info__opensl(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo) { MA_ASSERT(pContext != NULL); MA_ASSERT(g_maOpenSLInitCounter > 0); /* <-- If you trigger this it means you've either not initialized the context, or you've uninitialized it and then attempted to get device info. */ if (g_maOpenSLInitCounter == 0) { return MA_INVALID_OPERATION; } /* TODO: Test Me. This is currently untested, so for now we are just returning default devices. */ #if 0 && !defined(MA_ANDROID) SLAudioIODeviceCapabilitiesItf deviceCaps; SLresult resultSL = (*g_maEngineObjectSL)->GetInterface(g_maEngineObjectSL, (SLInterfaceID)pContext->opensl.SL_IID_AUDIOIODEVICECAPABILITIES, &deviceCaps); if (resultSL != SL_RESULT_SUCCESS) { /* The interface may not be supported so just report a default device. */ goto return_default_device; } if (deviceType == ma_device_type_playback) { SLAudioOutputDescriptor desc; resultSL = (*deviceCaps)->QueryAudioOutputCapabilities(deviceCaps, pDeviceID->opensl, &desc); if (resultSL != SL_RESULT_SUCCESS) { return ma_result_from_OpenSL(resultSL); } ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), (const char*)desc.pDeviceName, (size_t)-1); } else { SLAudioInputDescriptor desc; resultSL = (*deviceCaps)->QueryAudioInputCapabilities(deviceCaps, pDeviceID->opensl, &desc); if (resultSL != SL_RESULT_SUCCESS) { return ma_result_from_OpenSL(resultSL); } ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), (const char*)desc.deviceName, (size_t)-1); } goto return_detailed_info; #else goto return_default_device; #endif return_default_device: if (pDeviceID != NULL) { if ((deviceType == ma_device_type_playback && pDeviceID->opensl != SL_DEFAULTDEVICEID_AUDIOOUTPUT) || (deviceType == ma_device_type_capture && pDeviceID->opensl != SL_DEFAULTDEVICEID_AUDIOINPUT)) { return MA_NO_DEVICE; /* Don't know the device. */ } } /* ID and Name / Description */ if (deviceType == ma_device_type_playback) { pDeviceInfo->id.opensl = SL_DEFAULTDEVICEID_AUDIOOUTPUT; ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); } else { pDeviceInfo->id.opensl = SL_DEFAULTDEVICEID_AUDIOINPUT; ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); } pDeviceInfo->isDefault = MA_TRUE; goto return_detailed_info; return_detailed_info: /* For now we're just outputting a set of values that are supported by the API but not necessarily supported by the device natively. Later on we should work on this so that it more closely reflects the device's actual native format. */ pDeviceInfo->nativeDataFormatCount = 0; #if defined(MA_ANDROID) && __ANDROID_API__ >= 21 ma_context_add_data_format__opensl(pContext, ma_format_f32, pDeviceInfo); #endif ma_context_add_data_format__opensl(pContext, ma_format_s16, pDeviceInfo); ma_context_add_data_format__opensl(pContext, ma_format_u8, pDeviceInfo); return MA_SUCCESS; } #ifdef MA_ANDROID /*void ma_buffer_queue_callback_capture__opensl_android(SLAndroidSimpleBufferQueueItf pBufferQueue, SLuint32 eventFlags, const void* pBuffer, SLuint32 bufferSize, SLuint32 dataUsed, void* pContext)*/ static void ma_buffer_queue_callback_capture__opensl_android(SLAndroidSimpleBufferQueueItf pBufferQueue, void* pUserData) { ma_device* pDevice = (ma_device*)pUserData; size_t periodSizeInBytes; ma_uint8* pBuffer; SLresult resultSL; MA_ASSERT(pDevice != NULL); (void)pBufferQueue; /* For now, don't do anything unless the buffer was fully processed. From what I can tell, it looks like OpenSL|ES 1.1 improves on buffer queues to the point that we could much more intelligently handle this, but unfortunately it looks like Android is only supporting OpenSL|ES 1.0.1 for now :( */ /* Don't do anything if the device is not started. */ if (ma_device_get_state(pDevice) != ma_device_state_started) { return; } /* Don't do anything if the device is being drained. */ if (pDevice->opensl.isDrainingCapture) { return; } periodSizeInBytes = pDevice->capture.internalPeriodSizeInFrames * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels); pBuffer = pDevice->opensl.pBufferCapture + (pDevice->opensl.currentBufferIndexCapture * periodSizeInBytes); ma_device_handle_backend_data_callback(pDevice, NULL, pBuffer, pDevice->capture.internalPeriodSizeInFrames); resultSL = MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueueCapture)->Enqueue((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueueCapture, pBuffer, periodSizeInBytes); if (resultSL != SL_RESULT_SUCCESS) { return; } pDevice->opensl.currentBufferIndexCapture = (pDevice->opensl.currentBufferIndexCapture + 1) % pDevice->capture.internalPeriods; } static void ma_buffer_queue_callback_playback__opensl_android(SLAndroidSimpleBufferQueueItf pBufferQueue, void* pUserData) { ma_device* pDevice = (ma_device*)pUserData; size_t periodSizeInBytes; ma_uint8* pBuffer; SLresult resultSL; MA_ASSERT(pDevice != NULL); (void)pBufferQueue; /* Don't do anything if the device is not started. */ if (ma_device_get_state(pDevice) != ma_device_state_started) { return; } /* Don't do anything if the device is being drained. */ if (pDevice->opensl.isDrainingPlayback) { return; } periodSizeInBytes = pDevice->playback.internalPeriodSizeInFrames * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels); pBuffer = pDevice->opensl.pBufferPlayback + (pDevice->opensl.currentBufferIndexPlayback * periodSizeInBytes); ma_device_handle_backend_data_callback(pDevice, pBuffer, NULL, pDevice->playback.internalPeriodSizeInFrames); resultSL = MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueuePlayback)->Enqueue((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueuePlayback, pBuffer, periodSizeInBytes); if (resultSL != SL_RESULT_SUCCESS) { return; } pDevice->opensl.currentBufferIndexPlayback = (pDevice->opensl.currentBufferIndexPlayback + 1) % pDevice->playback.internalPeriods; } #endif static ma_result ma_device_uninit__opensl(ma_device* pDevice) { MA_ASSERT(pDevice != NULL); MA_ASSERT(g_maOpenSLInitCounter > 0); /* <-- If you trigger this it means you've either not initialized the context, or you've uninitialized it before uninitializing the device. */ if (g_maOpenSLInitCounter == 0) { return MA_INVALID_OPERATION; } if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { if (pDevice->opensl.pAudioRecorderObj) { MA_OPENSL_OBJ(pDevice->opensl.pAudioRecorderObj)->Destroy((SLObjectItf)pDevice->opensl.pAudioRecorderObj); } ma_free(pDevice->opensl.pBufferCapture, &pDevice->pContext->allocationCallbacks); } if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { if (pDevice->opensl.pAudioPlayerObj) { MA_OPENSL_OBJ(pDevice->opensl.pAudioPlayerObj)->Destroy((SLObjectItf)pDevice->opensl.pAudioPlayerObj); } if (pDevice->opensl.pOutputMixObj) { MA_OPENSL_OBJ(pDevice->opensl.pOutputMixObj)->Destroy((SLObjectItf)pDevice->opensl.pOutputMixObj); } ma_free(pDevice->opensl.pBufferPlayback, &pDevice->pContext->allocationCallbacks); } return MA_SUCCESS; } #if defined(MA_ANDROID) && __ANDROID_API__ >= 21 typedef SLAndroidDataFormat_PCM_EX ma_SLDataFormat_PCM; #else typedef SLDataFormat_PCM ma_SLDataFormat_PCM; #endif static ma_result ma_SLDataFormat_PCM_init__opensl(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, const ma_channel* channelMap, ma_SLDataFormat_PCM* pDataFormat) { /* We need to convert our format/channels/rate so that they aren't set to default. */ if (format == ma_format_unknown) { format = MA_DEFAULT_FORMAT; } if (channels == 0) { channels = MA_DEFAULT_CHANNELS; } if (sampleRate == 0) { sampleRate = MA_DEFAULT_SAMPLE_RATE; } #if defined(MA_ANDROID) && __ANDROID_API__ >= 21 if (format == ma_format_f32) { pDataFormat->formatType = SL_ANDROID_DATAFORMAT_PCM_EX; pDataFormat->representation = SL_ANDROID_PCM_REPRESENTATION_FLOAT; } else { pDataFormat->formatType = SL_DATAFORMAT_PCM; } #else pDataFormat->formatType = SL_DATAFORMAT_PCM; #endif pDataFormat->numChannels = channels; ((SLDataFormat_PCM*)pDataFormat)->samplesPerSec = ma_round_to_standard_sample_rate__opensl(sampleRate * 1000); /* In millihertz. Annoyingly, the sample rate variable is named differently between SLAndroidDataFormat_PCM_EX and SLDataFormat_PCM */ pDataFormat->bitsPerSample = ma_get_bytes_per_sample(format) * 8; pDataFormat->channelMask = ma_channel_map_to_channel_mask__opensl(channelMap, channels); pDataFormat->endianness = (ma_is_little_endian()) ? SL_BYTEORDER_LITTLEENDIAN : SL_BYTEORDER_BIGENDIAN; /* Android has a few restrictions on the format as documented here: https://developer.android.com/ndk/guides/audio/opensl-for-android.html - Only mono and stereo is supported. - Only u8 and s16 formats are supported. - Maximum sample rate of 48000. */ #ifdef MA_ANDROID if (pDataFormat->numChannels > 2) { pDataFormat->numChannels = 2; } #if __ANDROID_API__ >= 21 if (pDataFormat->formatType == SL_ANDROID_DATAFORMAT_PCM_EX) { /* It's floating point. */ MA_ASSERT(pDataFormat->representation == SL_ANDROID_PCM_REPRESENTATION_FLOAT); if (pDataFormat->bitsPerSample > 32) { pDataFormat->bitsPerSample = 32; } } else { if (pDataFormat->bitsPerSample > 16) { pDataFormat->bitsPerSample = 16; } } #else if (pDataFormat->bitsPerSample > 16) { pDataFormat->bitsPerSample = 16; } #endif if (((SLDataFormat_PCM*)pDataFormat)->samplesPerSec > SL_SAMPLINGRATE_48) { ((SLDataFormat_PCM*)pDataFormat)->samplesPerSec = SL_SAMPLINGRATE_48; } #endif pDataFormat->containerSize = pDataFormat->bitsPerSample; /* Always tightly packed for now. */ return MA_SUCCESS; } static ma_result ma_deconstruct_SLDataFormat_PCM__opensl(ma_SLDataFormat_PCM* pDataFormat, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) { ma_bool32 isFloatingPoint = MA_FALSE; #if defined(MA_ANDROID) && __ANDROID_API__ >= 21 if (pDataFormat->formatType == SL_ANDROID_DATAFORMAT_PCM_EX) { MA_ASSERT(pDataFormat->representation == SL_ANDROID_PCM_REPRESENTATION_FLOAT); isFloatingPoint = MA_TRUE; } #endif if (isFloatingPoint) { if (pDataFormat->bitsPerSample == 32) { *pFormat = ma_format_f32; } } else { if (pDataFormat->bitsPerSample == 8) { *pFormat = ma_format_u8; } else if (pDataFormat->bitsPerSample == 16) { *pFormat = ma_format_s16; } else if (pDataFormat->bitsPerSample == 24) { *pFormat = ma_format_s24; } else if (pDataFormat->bitsPerSample == 32) { *pFormat = ma_format_s32; } } *pChannels = pDataFormat->numChannels; *pSampleRate = ((SLDataFormat_PCM*)pDataFormat)->samplesPerSec / 1000; ma_channel_mask_to_channel_map__opensl(pDataFormat->channelMask, ma_min(pDataFormat->numChannels, channelMapCap), pChannelMap); return MA_SUCCESS; } static ma_result ma_device_init__opensl(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture) { #ifdef MA_ANDROID SLDataLocator_AndroidSimpleBufferQueue queue; SLresult resultSL; size_t bufferSizeInBytes; SLInterfaceID itfIDs[2]; const SLboolean itfIDsRequired[] = { SL_BOOLEAN_TRUE, /* SL_IID_ANDROIDSIMPLEBUFFERQUEUE */ SL_BOOLEAN_FALSE /* SL_IID_ANDROIDCONFIGURATION */ }; #endif MA_ASSERT(g_maOpenSLInitCounter > 0); /* <-- If you trigger this it means you've either not initialized the context, or you've uninitialized it and then attempted to initialize a new device. */ if (g_maOpenSLInitCounter == 0) { return MA_INVALID_OPERATION; } if (pConfig->deviceType == ma_device_type_loopback) { return MA_DEVICE_TYPE_NOT_SUPPORTED; } /* For now, only supporting Android implementations of OpenSL|ES since that's the only one I've been able to test with and I currently depend on Android-specific extensions (simple buffer queues). */ #ifdef MA_ANDROID itfIDs[0] = (SLInterfaceID)pDevice->pContext->opensl.SL_IID_ANDROIDSIMPLEBUFFERQUEUE; itfIDs[1] = (SLInterfaceID)pDevice->pContext->opensl.SL_IID_ANDROIDCONFIGURATION; /* No exclusive mode with OpenSL|ES. */ if (((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pDescriptorPlayback->shareMode == ma_share_mode_exclusive) || ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pDescriptorCapture->shareMode == ma_share_mode_exclusive)) { return MA_SHARE_MODE_NOT_SUPPORTED; } /* Now we can start initializing the device properly. */ MA_ASSERT(pDevice != NULL); MA_ZERO_OBJECT(&pDevice->opensl); queue.locatorType = SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE; if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { ma_SLDataFormat_PCM pcm; SLDataLocator_IODevice locatorDevice; SLDataSource source; SLDataSink sink; SLAndroidConfigurationItf pRecorderConfig; ma_SLDataFormat_PCM_init__opensl(pDescriptorCapture->format, pDescriptorCapture->channels, pDescriptorCapture->sampleRate, pDescriptorCapture->channelMap, &pcm); locatorDevice.locatorType = SL_DATALOCATOR_IODEVICE; locatorDevice.deviceType = SL_IODEVICE_AUDIOINPUT; locatorDevice.deviceID = SL_DEFAULTDEVICEID_AUDIOINPUT; /* Must always use the default device with Android. */ locatorDevice.device = NULL; source.pLocator = &locatorDevice; source.pFormat = NULL; queue.numBuffers = pDescriptorCapture->periodCount; sink.pLocator = &queue; sink.pFormat = (SLDataFormat_PCM*)&pcm; resultSL = (*g_maEngineSL)->CreateAudioRecorder(g_maEngineSL, (SLObjectItf*)&pDevice->opensl.pAudioRecorderObj, &source, &sink, ma_countof(itfIDs), itfIDs, itfIDsRequired); if (resultSL == SL_RESULT_CONTENT_UNSUPPORTED || resultSL == SL_RESULT_PARAMETER_INVALID) { /* Unsupported format. Fall back to something safer and try again. If this fails, just abort. */ pcm.formatType = SL_DATAFORMAT_PCM; pcm.numChannels = 1; ((SLDataFormat_PCM*)&pcm)->samplesPerSec = SL_SAMPLINGRATE_16; /* The name of the sample rate variable is different between SLAndroidDataFormat_PCM_EX and SLDataFormat_PCM. */ pcm.bitsPerSample = 16; pcm.containerSize = pcm.bitsPerSample; /* Always tightly packed for now. */ pcm.channelMask = 0; resultSL = (*g_maEngineSL)->CreateAudioRecorder(g_maEngineSL, (SLObjectItf*)&pDevice->opensl.pAudioRecorderObj, &source, &sink, ma_countof(itfIDs), itfIDs, itfIDsRequired); } if (resultSL != SL_RESULT_SUCCESS) { ma_device_uninit__opensl(pDevice); ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to create audio recorder."); return ma_result_from_OpenSL(resultSL); } /* Set the recording preset before realizing the player. */ if (pConfig->opensl.recordingPreset != ma_opensl_recording_preset_default) { resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioRecorderObj)->GetInterface((SLObjectItf)pDevice->opensl.pAudioRecorderObj, (SLInterfaceID)pDevice->pContext->opensl.SL_IID_ANDROIDCONFIGURATION, &pRecorderConfig); if (resultSL == SL_RESULT_SUCCESS) { SLint32 recordingPreset = ma_to_recording_preset__opensl(pConfig->opensl.recordingPreset); resultSL = (*pRecorderConfig)->SetConfiguration(pRecorderConfig, SL_ANDROID_KEY_RECORDING_PRESET, &recordingPreset, sizeof(SLint32)); if (resultSL != SL_RESULT_SUCCESS) { /* Failed to set the configuration. Just keep going. */ } } } resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioRecorderObj)->Realize((SLObjectItf)pDevice->opensl.pAudioRecorderObj, SL_BOOLEAN_FALSE); if (resultSL != SL_RESULT_SUCCESS) { ma_device_uninit__opensl(pDevice); ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to realize audio recorder."); return ma_result_from_OpenSL(resultSL); } resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioRecorderObj)->GetInterface((SLObjectItf)pDevice->opensl.pAudioRecorderObj, (SLInterfaceID)pDevice->pContext->opensl.SL_IID_RECORD, &pDevice->opensl.pAudioRecorder); if (resultSL != SL_RESULT_SUCCESS) { ma_device_uninit__opensl(pDevice); ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to retrieve SL_IID_RECORD interface."); return ma_result_from_OpenSL(resultSL); } resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioRecorderObj)->GetInterface((SLObjectItf)pDevice->opensl.pAudioRecorderObj, (SLInterfaceID)pDevice->pContext->opensl.SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &pDevice->opensl.pBufferQueueCapture); if (resultSL != SL_RESULT_SUCCESS) { ma_device_uninit__opensl(pDevice); ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to retrieve SL_IID_ANDROIDSIMPLEBUFFERQUEUE interface."); return ma_result_from_OpenSL(resultSL); } resultSL = MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueueCapture)->RegisterCallback((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueueCapture, ma_buffer_queue_callback_capture__opensl_android, pDevice); if (resultSL != SL_RESULT_SUCCESS) { ma_device_uninit__opensl(pDevice); ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to register buffer queue callback."); return ma_result_from_OpenSL(resultSL); } /* The internal format is determined by the "pcm" object. */ ma_deconstruct_SLDataFormat_PCM__opensl(&pcm, &pDescriptorCapture->format, &pDescriptorCapture->channels, &pDescriptorCapture->sampleRate, pDescriptorCapture->channelMap, ma_countof(pDescriptorCapture->channelMap)); /* Buffer. */ pDescriptorCapture->periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptorCapture, pDescriptorCapture->sampleRate, pConfig->performanceProfile); pDevice->opensl.currentBufferIndexCapture = 0; bufferSizeInBytes = pDescriptorCapture->periodSizeInFrames * ma_get_bytes_per_frame(pDescriptorCapture->format, pDescriptorCapture->channels) * pDescriptorCapture->periodCount; pDevice->opensl.pBufferCapture = (ma_uint8*)ma_calloc(bufferSizeInBytes, &pDevice->pContext->allocationCallbacks); if (pDevice->opensl.pBufferCapture == NULL) { ma_device_uninit__opensl(pDevice); ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to allocate memory for data buffer."); return MA_OUT_OF_MEMORY; } MA_ZERO_MEMORY(pDevice->opensl.pBufferCapture, bufferSizeInBytes); } if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { ma_SLDataFormat_PCM pcm; SLDataSource source; SLDataLocator_OutputMix outmixLocator; SLDataSink sink; SLAndroidConfigurationItf pPlayerConfig; ma_SLDataFormat_PCM_init__opensl(pDescriptorPlayback->format, pDescriptorPlayback->channels, pDescriptorPlayback->sampleRate, pDescriptorPlayback->channelMap, &pcm); resultSL = (*g_maEngineSL)->CreateOutputMix(g_maEngineSL, (SLObjectItf*)&pDevice->opensl.pOutputMixObj, 0, NULL, NULL); if (resultSL != SL_RESULT_SUCCESS) { ma_device_uninit__opensl(pDevice); ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to create output mix."); return ma_result_from_OpenSL(resultSL); } resultSL = MA_OPENSL_OBJ(pDevice->opensl.pOutputMixObj)->Realize((SLObjectItf)pDevice->opensl.pOutputMixObj, SL_BOOLEAN_FALSE); if (resultSL != SL_RESULT_SUCCESS) { ma_device_uninit__opensl(pDevice); ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to realize output mix object."); return ma_result_from_OpenSL(resultSL); } resultSL = MA_OPENSL_OBJ(pDevice->opensl.pOutputMixObj)->GetInterface((SLObjectItf)pDevice->opensl.pOutputMixObj, (SLInterfaceID)pDevice->pContext->opensl.SL_IID_OUTPUTMIX, &pDevice->opensl.pOutputMix); if (resultSL != SL_RESULT_SUCCESS) { ma_device_uninit__opensl(pDevice); ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to retrieve SL_IID_OUTPUTMIX interface."); return ma_result_from_OpenSL(resultSL); } /* Set the output device. */ if (pDescriptorPlayback->pDeviceID != NULL) { SLuint32 deviceID_OpenSL = pDescriptorPlayback->pDeviceID->opensl; MA_OPENSL_OUTPUTMIX(pDevice->opensl.pOutputMix)->ReRoute((SLOutputMixItf)pDevice->opensl.pOutputMix, 1, &deviceID_OpenSL); } queue.numBuffers = pDescriptorPlayback->periodCount; source.pLocator = &queue; source.pFormat = (SLDataFormat_PCM*)&pcm; outmixLocator.locatorType = SL_DATALOCATOR_OUTPUTMIX; outmixLocator.outputMix = (SLObjectItf)pDevice->opensl.pOutputMixObj; sink.pLocator = &outmixLocator; sink.pFormat = NULL; resultSL = (*g_maEngineSL)->CreateAudioPlayer(g_maEngineSL, (SLObjectItf*)&pDevice->opensl.pAudioPlayerObj, &source, &sink, ma_countof(itfIDs), itfIDs, itfIDsRequired); if (resultSL == SL_RESULT_CONTENT_UNSUPPORTED || resultSL == SL_RESULT_PARAMETER_INVALID) { /* Unsupported format. Fall back to something safer and try again. If this fails, just abort. */ pcm.formatType = SL_DATAFORMAT_PCM; pcm.numChannels = 2; ((SLDataFormat_PCM*)&pcm)->samplesPerSec = SL_SAMPLINGRATE_16; pcm.bitsPerSample = 16; pcm.containerSize = pcm.bitsPerSample; /* Always tightly packed for now. */ pcm.channelMask = SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT; resultSL = (*g_maEngineSL)->CreateAudioPlayer(g_maEngineSL, (SLObjectItf*)&pDevice->opensl.pAudioPlayerObj, &source, &sink, ma_countof(itfIDs), itfIDs, itfIDsRequired); } if (resultSL != SL_RESULT_SUCCESS) { ma_device_uninit__opensl(pDevice); ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to create audio player."); return ma_result_from_OpenSL(resultSL); } /* Set the stream type before realizing the player. */ if (pConfig->opensl.streamType != ma_opensl_stream_type_default) { resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioPlayerObj)->GetInterface((SLObjectItf)pDevice->opensl.pAudioPlayerObj, (SLInterfaceID)pDevice->pContext->opensl.SL_IID_ANDROIDCONFIGURATION, &pPlayerConfig); if (resultSL == SL_RESULT_SUCCESS) { SLint32 streamType = ma_to_stream_type__opensl(pConfig->opensl.streamType); resultSL = (*pPlayerConfig)->SetConfiguration(pPlayerConfig, SL_ANDROID_KEY_STREAM_TYPE, &streamType, sizeof(SLint32)); if (resultSL != SL_RESULT_SUCCESS) { /* Failed to set the configuration. Just keep going. */ } } } resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioPlayerObj)->Realize((SLObjectItf)pDevice->opensl.pAudioPlayerObj, SL_BOOLEAN_FALSE); if (resultSL != SL_RESULT_SUCCESS) { ma_device_uninit__opensl(pDevice); ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to realize audio player."); return ma_result_from_OpenSL(resultSL); } resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioPlayerObj)->GetInterface((SLObjectItf)pDevice->opensl.pAudioPlayerObj, (SLInterfaceID)pDevice->pContext->opensl.SL_IID_PLAY, &pDevice->opensl.pAudioPlayer); if (resultSL != SL_RESULT_SUCCESS) { ma_device_uninit__opensl(pDevice); ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to retrieve SL_IID_PLAY interface."); return ma_result_from_OpenSL(resultSL); } resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioPlayerObj)->GetInterface((SLObjectItf)pDevice->opensl.pAudioPlayerObj, (SLInterfaceID)pDevice->pContext->opensl.SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &pDevice->opensl.pBufferQueuePlayback); if (resultSL != SL_RESULT_SUCCESS) { ma_device_uninit__opensl(pDevice); ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to retrieve SL_IID_ANDROIDSIMPLEBUFFERQUEUE interface."); return ma_result_from_OpenSL(resultSL); } resultSL = MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueuePlayback)->RegisterCallback((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueuePlayback, ma_buffer_queue_callback_playback__opensl_android, pDevice); if (resultSL != SL_RESULT_SUCCESS) { ma_device_uninit__opensl(pDevice); ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to register buffer queue callback."); return ma_result_from_OpenSL(resultSL); } /* The internal format is determined by the "pcm" object. */ ma_deconstruct_SLDataFormat_PCM__opensl(&pcm, &pDescriptorPlayback->format, &pDescriptorPlayback->channels, &pDescriptorPlayback->sampleRate, pDescriptorPlayback->channelMap, ma_countof(pDescriptorPlayback->channelMap)); /* Buffer. */ pDescriptorPlayback->periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptorPlayback, pDescriptorPlayback->sampleRate, pConfig->performanceProfile); pDevice->opensl.currentBufferIndexPlayback = 0; bufferSizeInBytes = pDescriptorPlayback->periodSizeInFrames * ma_get_bytes_per_frame(pDescriptorPlayback->format, pDescriptorPlayback->channels) * pDescriptorPlayback->periodCount; pDevice->opensl.pBufferPlayback = (ma_uint8*)ma_calloc(bufferSizeInBytes, &pDevice->pContext->allocationCallbacks); if (pDevice->opensl.pBufferPlayback == NULL) { ma_device_uninit__opensl(pDevice); ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to allocate memory for data buffer."); return MA_OUT_OF_MEMORY; } MA_ZERO_MEMORY(pDevice->opensl.pBufferPlayback, bufferSizeInBytes); } return MA_SUCCESS; #else return MA_NO_BACKEND; /* Non-Android implementations are not supported. */ #endif } static ma_result ma_device_start__opensl(ma_device* pDevice) { SLresult resultSL; size_t periodSizeInBytes; ma_uint32 iPeriod; MA_ASSERT(pDevice != NULL); MA_ASSERT(g_maOpenSLInitCounter > 0); /* <-- If you trigger this it means you've either not initialized the context, or you've uninitialized it and then attempted to start the device. */ if (g_maOpenSLInitCounter == 0) { return MA_INVALID_OPERATION; } if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { resultSL = MA_OPENSL_RECORD(pDevice->opensl.pAudioRecorder)->SetRecordState((SLRecordItf)pDevice->opensl.pAudioRecorder, SL_RECORDSTATE_RECORDING); if (resultSL != SL_RESULT_SUCCESS) { ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to start internal capture device."); return ma_result_from_OpenSL(resultSL); } periodSizeInBytes = pDevice->capture.internalPeriodSizeInFrames * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels); for (iPeriod = 0; iPeriod < pDevice->capture.internalPeriods; ++iPeriod) { resultSL = MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueueCapture)->Enqueue((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueueCapture, pDevice->opensl.pBufferCapture + (periodSizeInBytes * iPeriod), periodSizeInBytes); if (resultSL != SL_RESULT_SUCCESS) { MA_OPENSL_RECORD(pDevice->opensl.pAudioRecorder)->SetRecordState((SLRecordItf)pDevice->opensl.pAudioRecorder, SL_RECORDSTATE_STOPPED); ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to enqueue buffer for capture device."); return ma_result_from_OpenSL(resultSL); } } } if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { resultSL = MA_OPENSL_PLAY(pDevice->opensl.pAudioPlayer)->SetPlayState((SLPlayItf)pDevice->opensl.pAudioPlayer, SL_PLAYSTATE_PLAYING); if (resultSL != SL_RESULT_SUCCESS) { ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to start internal playback device."); return ma_result_from_OpenSL(resultSL); } /* In playback mode (no duplex) we need to load some initial buffers. In duplex mode we need to enqueu silent buffers. */ if (pDevice->type == ma_device_type_duplex) { MA_ZERO_MEMORY(pDevice->opensl.pBufferPlayback, pDevice->playback.internalPeriodSizeInFrames * pDevice->playback.internalPeriods * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels)); } else { ma_device__read_frames_from_client(pDevice, pDevice->playback.internalPeriodSizeInFrames * pDevice->playback.internalPeriods, pDevice->opensl.pBufferPlayback); } periodSizeInBytes = pDevice->playback.internalPeriodSizeInFrames * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels); for (iPeriod = 0; iPeriod < pDevice->playback.internalPeriods; ++iPeriod) { resultSL = MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueuePlayback)->Enqueue((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueuePlayback, pDevice->opensl.pBufferPlayback + (periodSizeInBytes * iPeriod), periodSizeInBytes); if (resultSL != SL_RESULT_SUCCESS) { MA_OPENSL_PLAY(pDevice->opensl.pAudioPlayer)->SetPlayState((SLPlayItf)pDevice->opensl.pAudioPlayer, SL_PLAYSTATE_STOPPED); ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to enqueue buffer for playback device."); return ma_result_from_OpenSL(resultSL); } } } return MA_SUCCESS; } static ma_result ma_device_drain__opensl(ma_device* pDevice, ma_device_type deviceType) { SLAndroidSimpleBufferQueueItf pBufferQueue; MA_ASSERT(deviceType == ma_device_type_capture || deviceType == ma_device_type_playback); if (pDevice->type == ma_device_type_capture) { pBufferQueue = (SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueueCapture; pDevice->opensl.isDrainingCapture = MA_TRUE; } else { pBufferQueue = (SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueuePlayback; pDevice->opensl.isDrainingPlayback = MA_TRUE; } for (;;) { SLAndroidSimpleBufferQueueState state; MA_OPENSL_BUFFERQUEUE(pBufferQueue)->GetState(pBufferQueue, &state); if (state.count == 0) { break; } ma_sleep(10); } if (pDevice->type == ma_device_type_capture) { pDevice->opensl.isDrainingCapture = MA_FALSE; } else { pDevice->opensl.isDrainingPlayback = MA_FALSE; } return MA_SUCCESS; } static ma_result ma_device_stop__opensl(ma_device* pDevice) { SLresult resultSL; MA_ASSERT(pDevice != NULL); MA_ASSERT(g_maOpenSLInitCounter > 0); /* <-- If you trigger this it means you've either not initialized the context, or you've uninitialized it before stopping/uninitializing the device. */ if (g_maOpenSLInitCounter == 0) { return MA_INVALID_OPERATION; } if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { ma_device_drain__opensl(pDevice, ma_device_type_capture); resultSL = MA_OPENSL_RECORD(pDevice->opensl.pAudioRecorder)->SetRecordState((SLRecordItf)pDevice->opensl.pAudioRecorder, SL_RECORDSTATE_STOPPED); if (resultSL != SL_RESULT_SUCCESS) { ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to stop internal capture device."); return ma_result_from_OpenSL(resultSL); } MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueueCapture)->Clear((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueueCapture); } if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { ma_device_drain__opensl(pDevice, ma_device_type_playback); resultSL = MA_OPENSL_PLAY(pDevice->opensl.pAudioPlayer)->SetPlayState((SLPlayItf)pDevice->opensl.pAudioPlayer, SL_PLAYSTATE_STOPPED); if (resultSL != SL_RESULT_SUCCESS) { ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to stop internal playback device."); return ma_result_from_OpenSL(resultSL); } MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueuePlayback)->Clear((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueuePlayback); } /* Make sure the client is aware that the device has stopped. There may be an OpenSL|ES callback for this, but I haven't found it. */ ma_device__on_notification_stopped(pDevice); return MA_SUCCESS; } static ma_result ma_context_uninit__opensl(ma_context* pContext) { MA_ASSERT(pContext != NULL); MA_ASSERT(pContext->backend == ma_backend_opensl); (void)pContext; /* Uninit global data. */ ma_spinlock_lock(&g_maOpenSLSpinlock); { MA_ASSERT(g_maOpenSLInitCounter > 0); /* If you've triggered this, it means you have ma_context_init/uninit mismatch. Each successful call to ma_context_init() must be matched up with a call to ma_context_uninit(). */ g_maOpenSLInitCounter -= 1; if (g_maOpenSLInitCounter == 0) { (*g_maEngineObjectSL)->Destroy(g_maEngineObjectSL); } } ma_spinlock_unlock(&g_maOpenSLSpinlock); return MA_SUCCESS; } static ma_result ma_dlsym_SLInterfaceID__opensl(ma_context* pContext, const char* pName, ma_handle* pHandle) { /* We need to return an error if the symbol cannot be found. This is important because there have been reports that some symbols do not exist. */ ma_handle* p = (ma_handle*)ma_dlsym(pContext, pContext->opensl.libOpenSLES, pName); if (p == NULL) { ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_INFO, "[OpenSL] Cannot find symbol %s", pName); return MA_NO_BACKEND; } *pHandle = *p; return MA_SUCCESS; } static ma_result ma_context_init_engine_nolock__opensl(ma_context* pContext) { g_maOpenSLInitCounter += 1; if (g_maOpenSLInitCounter == 1) { SLresult resultSL; resultSL = ((ma_slCreateEngine_proc)pContext->opensl.slCreateEngine)(&g_maEngineObjectSL, 0, NULL, 0, NULL, NULL); if (resultSL != SL_RESULT_SUCCESS) { g_maOpenSLInitCounter -= 1; return ma_result_from_OpenSL(resultSL); } (*g_maEngineObjectSL)->Realize(g_maEngineObjectSL, SL_BOOLEAN_FALSE); resultSL = (*g_maEngineObjectSL)->GetInterface(g_maEngineObjectSL, (SLInterfaceID)pContext->opensl.SL_IID_ENGINE, &g_maEngineSL); if (resultSL != SL_RESULT_SUCCESS) { (*g_maEngineObjectSL)->Destroy(g_maEngineObjectSL); g_maOpenSLInitCounter -= 1; return ma_result_from_OpenSL(resultSL); } } return MA_SUCCESS; } static ma_result ma_context_init__opensl(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks) { ma_result result; #if !defined(MA_NO_RUNTIME_LINKING) size_t i; const char* libOpenSLESNames[] = { "libOpenSLES.so" }; #endif MA_ASSERT(pContext != NULL); (void)pConfig; #if !defined(MA_NO_RUNTIME_LINKING) /* Dynamically link against libOpenSLES.so. I have now had multiple reports that SL_IID_ANDROIDSIMPLEBUFFERQUEUE cannot be found. One report was happening at compile time and another at runtime. To try working around this, I'm going to link to libOpenSLES at runtime and extract the symbols rather than reference them directly. This should, hopefully, fix these issues as the compiler won't see any references to the symbols and will hopefully skip the checks. */ for (i = 0; i < ma_countof(libOpenSLESNames); i += 1) { pContext->opensl.libOpenSLES = ma_dlopen(pContext, libOpenSLESNames[i]); if (pContext->opensl.libOpenSLES != NULL) { break; } } if (pContext->opensl.libOpenSLES == NULL) { ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_INFO, "[OpenSL] Could not find libOpenSLES.so"); return MA_NO_BACKEND; } result = ma_dlsym_SLInterfaceID__opensl(pContext, "SL_IID_ENGINE", &pContext->opensl.SL_IID_ENGINE); if (result != MA_SUCCESS) { ma_dlclose(pContext, pContext->opensl.libOpenSLES); return result; } result = ma_dlsym_SLInterfaceID__opensl(pContext, "SL_IID_AUDIOIODEVICECAPABILITIES", &pContext->opensl.SL_IID_AUDIOIODEVICECAPABILITIES); if (result != MA_SUCCESS) { ma_dlclose(pContext, pContext->opensl.libOpenSLES); return result; } result = ma_dlsym_SLInterfaceID__opensl(pContext, "SL_IID_ANDROIDSIMPLEBUFFERQUEUE", &pContext->opensl.SL_IID_ANDROIDSIMPLEBUFFERQUEUE); if (result != MA_SUCCESS) { ma_dlclose(pContext, pContext->opensl.libOpenSLES); return result; } result = ma_dlsym_SLInterfaceID__opensl(pContext, "SL_IID_RECORD", &pContext->opensl.SL_IID_RECORD); if (result != MA_SUCCESS) { ma_dlclose(pContext, pContext->opensl.libOpenSLES); return result; } result = ma_dlsym_SLInterfaceID__opensl(pContext, "SL_IID_PLAY", &pContext->opensl.SL_IID_PLAY); if (result != MA_SUCCESS) { ma_dlclose(pContext, pContext->opensl.libOpenSLES); return result; } result = ma_dlsym_SLInterfaceID__opensl(pContext, "SL_IID_OUTPUTMIX", &pContext->opensl.SL_IID_OUTPUTMIX); if (result != MA_SUCCESS) { ma_dlclose(pContext, pContext->opensl.libOpenSLES); return result; } result = ma_dlsym_SLInterfaceID__opensl(pContext, "SL_IID_ANDROIDCONFIGURATION", &pContext->opensl.SL_IID_ANDROIDCONFIGURATION); if (result != MA_SUCCESS) { ma_dlclose(pContext, pContext->opensl.libOpenSLES); return result; } pContext->opensl.slCreateEngine = (ma_proc)ma_dlsym(pContext, pContext->opensl.libOpenSLES, "slCreateEngine"); if (pContext->opensl.slCreateEngine == NULL) { ma_dlclose(pContext, pContext->opensl.libOpenSLES); ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_INFO, "[OpenSL] Cannot find symbol slCreateEngine."); return MA_NO_BACKEND; } #else pContext->opensl.SL_IID_ENGINE = (ma_handle)SL_IID_ENGINE; pContext->opensl.SL_IID_AUDIOIODEVICECAPABILITIES = (ma_handle)SL_IID_AUDIOIODEVICECAPABILITIES; pContext->opensl.SL_IID_ANDROIDSIMPLEBUFFERQUEUE = (ma_handle)SL_IID_ANDROIDSIMPLEBUFFERQUEUE; pContext->opensl.SL_IID_RECORD = (ma_handle)SL_IID_RECORD; pContext->opensl.SL_IID_PLAY = (ma_handle)SL_IID_PLAY; pContext->opensl.SL_IID_OUTPUTMIX = (ma_handle)SL_IID_OUTPUTMIX; pContext->opensl.SL_IID_ANDROIDCONFIGURATION = (ma_handle)SL_IID_ANDROIDCONFIGURATION; pContext->opensl.slCreateEngine = (ma_proc)slCreateEngine; #endif /* Initialize global data first if applicable. */ ma_spinlock_lock(&g_maOpenSLSpinlock); { result = ma_context_init_engine_nolock__opensl(pContext); } ma_spinlock_unlock(&g_maOpenSLSpinlock); if (result != MA_SUCCESS) { ma_dlclose(pContext, pContext->opensl.libOpenSLES); ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_INFO, "[OpenSL] Failed to initialize OpenSL engine."); return result; } pCallbacks->onContextInit = ma_context_init__opensl; pCallbacks->onContextUninit = ma_context_uninit__opensl; pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__opensl; pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__opensl; pCallbacks->onDeviceInit = ma_device_init__opensl; pCallbacks->onDeviceUninit = ma_device_uninit__opensl; pCallbacks->onDeviceStart = ma_device_start__opensl; pCallbacks->onDeviceStop = ma_device_stop__opensl; pCallbacks->onDeviceRead = NULL; /* Not needed because OpenSL|ES is asynchronous. */ pCallbacks->onDeviceWrite = NULL; /* Not needed because OpenSL|ES is asynchronous. */ pCallbacks->onDeviceDataLoop = NULL; /* Not needed because OpenSL|ES is asynchronous. */ return MA_SUCCESS; } #endif /* OpenSL|ES */ /****************************************************************************** Web Audio Backend ******************************************************************************/ #ifdef MA_HAS_WEBAUDIO #include static ma_bool32 ma_is_capture_supported__webaudio() { return EM_ASM_INT({ return (navigator.mediaDevices !== undefined && navigator.mediaDevices.getUserMedia !== undefined); }, 0) != 0; /* Must pass in a dummy argument for C99 compatibility. */ } #ifdef __cplusplus extern "C" { #endif void EMSCRIPTEN_KEEPALIVE ma_device_process_pcm_frames_capture__webaudio(ma_device* pDevice, int frameCount, float* pFrames) { ma_device_handle_backend_data_callback(pDevice, NULL, pFrames, (ma_uint32)frameCount); } void EMSCRIPTEN_KEEPALIVE ma_device_process_pcm_frames_playback__webaudio(ma_device* pDevice, int frameCount, float* pFrames) { ma_device_handle_backend_data_callback(pDevice, pFrames, NULL, (ma_uint32)frameCount); } #ifdef __cplusplus } #endif static ma_result ma_context_enumerate_devices__webaudio(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData) { ma_bool32 cbResult = MA_TRUE; MA_ASSERT(pContext != NULL); MA_ASSERT(callback != NULL); /* Only supporting default devices for now. */ /* Playback. */ if (cbResult) { ma_device_info deviceInfo; MA_ZERO_OBJECT(&deviceInfo); ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); deviceInfo.isDefault = MA_TRUE; /* Only supporting default devices. */ cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData); } /* Capture. */ if (cbResult) { if (ma_is_capture_supported__webaudio()) { ma_device_info deviceInfo; MA_ZERO_OBJECT(&deviceInfo); ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); deviceInfo.isDefault = MA_TRUE; /* Only supporting default devices. */ cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData); } } return MA_SUCCESS; } static ma_result ma_context_get_device_info__webaudio(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo) { MA_ASSERT(pContext != NULL); if (deviceType == ma_device_type_capture && !ma_is_capture_supported__webaudio()) { return MA_NO_DEVICE; } MA_ZERO_MEMORY(pDeviceInfo->id.webaudio, sizeof(pDeviceInfo->id.webaudio)); /* Only supporting default devices for now. */ (void)pDeviceID; if (deviceType == ma_device_type_playback) { ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); } else { ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); } /* Only supporting default devices. */ pDeviceInfo->isDefault = MA_TRUE; /* Web Audio can support any number of channels and sample rates. It only supports f32 formats, however. */ pDeviceInfo->nativeDataFormats[0].flags = 0; pDeviceInfo->nativeDataFormats[0].format = ma_format_unknown; pDeviceInfo->nativeDataFormats[0].channels = 0; /* All channels are supported. */ pDeviceInfo->nativeDataFormats[0].sampleRate = EM_ASM_INT({ try { var temp = new (window.AudioContext || window.webkitAudioContext)(); var sampleRate = temp.sampleRate; temp.close(); return sampleRate; } catch(e) { return 0; } }, 0); /* Must pass in a dummy argument for C99 compatibility. */ if (pDeviceInfo->nativeDataFormats[0].sampleRate == 0) { return MA_NO_DEVICE; } pDeviceInfo->nativeDataFormatCount = 1; return MA_SUCCESS; } static void ma_device_uninit_by_index__webaudio(ma_device* pDevice, ma_device_type deviceType, int deviceIndex) { MA_ASSERT(pDevice != NULL); EM_ASM({ var device = miniaudio.get_device_by_index($0); /* Make sure all nodes are disconnected and marked for collection. */ if (device.scriptNode !== undefined) { device.scriptNode.onaudioprocess = function(e) {}; /* We want to reset the callback to ensure it doesn't get called after AudioContext.close() has returned. Shouldn't happen since we're disconnecting, but just to be safe... */ device.scriptNode.disconnect(); device.scriptNode = undefined; } if (device.streamNode !== undefined) { device.streamNode.disconnect(); device.streamNode = undefined; } /* Stop the device. I think there is a chance the callback could get fired after calling this, hence why we want to clear the callback before closing. */ device.webaudio.close(); device.webaudio = undefined; /* Can't forget to free the intermediary buffer. This is the buffer that's shared between JavaScript and C. */ if (device.intermediaryBuffer !== undefined) { Module._free(device.intermediaryBuffer); device.intermediaryBuffer = undefined; device.intermediaryBufferView = undefined; device.intermediaryBufferSizeInBytes = undefined; } /* Make sure the device is untracked so the slot can be reused later. */ miniaudio.untrack_device_by_index($0); }, deviceIndex, deviceType); } static ma_result ma_device_uninit__webaudio(ma_device* pDevice) { MA_ASSERT(pDevice != NULL); if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { ma_device_uninit_by_index__webaudio(pDevice, ma_device_type_capture, pDevice->webaudio.indexCapture); } if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { ma_device_uninit_by_index__webaudio(pDevice, ma_device_type_playback, pDevice->webaudio.indexPlayback); } return MA_SUCCESS; } static ma_uint32 ma_calculate_period_size_in_frames_from_descriptor__webaudio(const ma_device_descriptor* pDescriptor, ma_uint32 nativeSampleRate, ma_performance_profile performanceProfile) { /* There have been reports of the default buffer size being too small on some browsers. There have been reports of the default buffer size being too small on some browsers. If we're using default buffer size, we'll make sure the period size is a big biffer than our standard defaults. */ ma_uint32 periodSizeInFrames; if (pDescriptor->periodSizeInFrames == 0) { if (pDescriptor->periodSizeInMilliseconds == 0) { if (performanceProfile == ma_performance_profile_low_latency) { periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(33, nativeSampleRate); /* 1 frame @ 30 FPS */ } else { periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(333, nativeSampleRate); } } else { periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(pDescriptor->periodSizeInMilliseconds, nativeSampleRate); } } else { periodSizeInFrames = pDescriptor->periodSizeInFrames; } /* The size of the buffer must be a power of 2 and between 256 and 16384. */ if (periodSizeInFrames < 256) { periodSizeInFrames = 256; } else if (periodSizeInFrames > 16384) { periodSizeInFrames = 16384; } else { periodSizeInFrames = ma_next_power_of_2(periodSizeInFrames); } return periodSizeInFrames; } static ma_result ma_device_init_by_type__webaudio(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptor, ma_device_type deviceType) { int deviceIndex; ma_uint32 channels; ma_uint32 sampleRate; ma_uint32 periodSizeInFrames; MA_ASSERT(pDevice != NULL); MA_ASSERT(pConfig != NULL); MA_ASSERT(deviceType != ma_device_type_duplex); if (deviceType == ma_device_type_capture && !ma_is_capture_supported__webaudio()) { return MA_NO_DEVICE; } /* We're going to calculate some stuff in C just to simplify the JS code. */ channels = (pDescriptor->channels > 0) ? pDescriptor->channels : MA_DEFAULT_CHANNELS; sampleRate = (pDescriptor->sampleRate > 0) ? pDescriptor->sampleRate : MA_DEFAULT_SAMPLE_RATE; periodSizeInFrames = ma_calculate_period_size_in_frames_from_descriptor__webaudio(pDescriptor, sampleRate, pConfig->performanceProfile); ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "periodSizeInFrames = %d\n", (int)periodSizeInFrames); /* We create the device on the JavaScript side and reference it using an index. We use this to make it possible to reference the device between JavaScript and C. */ deviceIndex = EM_ASM_INT({ var channels = $0; var sampleRate = $1; var bufferSize = $2; /* In PCM frames. */ var isCapture = $3; var pDevice = $4; if (typeof(window.miniaudio) === 'undefined') { return -1; /* Context not initialized. */ } var device = {}; /* The AudioContext must be created in a suspended state. */ device.webaudio = new (window.AudioContext || window.webkitAudioContext)({sampleRate:sampleRate}); device.webaudio.suspend(); device.state = 1; /* ma_device_state_stopped */ /* We need an intermediary buffer which we use for JavaScript and C interop. This buffer stores interleaved f32 PCM data. Because it's passed between JavaScript and C it needs to be allocated and freed using Module._malloc() and Module._free(). */ device.intermediaryBufferSizeInBytes = channels * bufferSize * 4; device.intermediaryBuffer = Module._malloc(device.intermediaryBufferSizeInBytes); device.intermediaryBufferView = new Float32Array(Module.HEAPF32.buffer, device.intermediaryBuffer, device.intermediaryBufferSizeInBytes); /* Both playback and capture devices use a ScriptProcessorNode for performing per-sample operations. ScriptProcessorNode is actually deprecated so this is likely to be temporary. The way this works for playback is very simple. You just set a callback that's periodically fired, just like a normal audio callback function. But apparently this design is "flawed" and is now deprecated in favour of something called AudioWorklets which _forces_ you to load a _separate_ .js file at run time... nice... Hopefully ScriptProcessorNode will continue to work for years to come, but this may need to change to use AudioSourceBufferNode instead, which I think is what Emscripten uses for it's built-in SDL implementation. I'll be avoiding that insane AudioWorklet API like the plague... For capture it is a bit unintuitive. We use the ScriptProccessorNode _only_ to get the raw PCM data. It is connected to an AudioContext just like the playback case, however we just output silence to the AudioContext instead of passing any real data. It would make more sense to me to use the MediaRecorder API, but unfortunately you need to specify a MIME time (Opus, Vorbis, etc.) for the binary blob that's returned to the client, but I've been unable to figure out how to get this as raw PCM. The closest I can think is to use the MIME type for WAV files and just parse it, but I don't know how well this would work. Although ScriptProccessorNode is deprecated, in practice it seems to have pretty good browser support so I'm leaving it like this for now. If anyone knows how I could get raw PCM data using the MediaRecorder API please let me know! */ device.scriptNode = device.webaudio.createScriptProcessor(bufferSize, (isCapture) ? channels : 0, (isCapture) ? 0 : channels); if (isCapture) { device.scriptNode.onaudioprocess = function(e) { if (device.intermediaryBuffer === undefined) { return; /* This means the device has been uninitialized. */ } if (device.intermediaryBufferView.length == 0) { /* Recreate intermediaryBufferView when losing reference to the underlying buffer, probably due to emscripten resizing heap. */ device.intermediaryBufferView = new Float32Array(Module.HEAPF32.buffer, device.intermediaryBuffer, device.intermediaryBufferSizeInBytes); } /* Make sure silence it output to the AudioContext destination. Not doing this will cause sound to come out of the speakers! */ for (var iChannel = 0; iChannel < e.outputBuffer.numberOfChannels; ++iChannel) { e.outputBuffer.getChannelData(iChannel).fill(0.0); } /* There are some situations where we may want to send silence to the client. */ var sendSilence = false; if (device.streamNode === undefined) { sendSilence = true; } /* Sanity check. This will never happen, right? */ if (e.inputBuffer.numberOfChannels != channels) { console.log("Capture: Channel count mismatch. " + e.inputBufer.numberOfChannels + " != " + channels + ". Sending silence."); sendSilence = true; } /* This looped design guards against the situation where e.inputBuffer is a different size to the original buffer size. Should never happen in practice. */ var totalFramesProcessed = 0; while (totalFramesProcessed < e.inputBuffer.length) { var framesRemaining = e.inputBuffer.length - totalFramesProcessed; var framesToProcess = framesRemaining; if (framesToProcess > (device.intermediaryBufferSizeInBytes/channels/4)) { framesToProcess = (device.intermediaryBufferSizeInBytes/channels/4); } /* We need to do the reverse of the playback case. We need to interleave the input data and copy it into the intermediary buffer. Then we send it to the client. */ if (sendSilence) { device.intermediaryBufferView.fill(0.0); } else { for (var iFrame = 0; iFrame < framesToProcess; ++iFrame) { for (var iChannel = 0; iChannel < e.inputBuffer.numberOfChannels; ++iChannel) { device.intermediaryBufferView[iFrame*channels + iChannel] = e.inputBuffer.getChannelData(iChannel)[totalFramesProcessed + iFrame]; } } } /* Send data to the client from our intermediary buffer. */ _ma_device_process_pcm_frames_capture__webaudio(pDevice, framesToProcess, device.intermediaryBuffer); totalFramesProcessed += framesToProcess; } }; navigator.mediaDevices.getUserMedia({audio:true, video:false}) .then(function(stream) { device.streamNode = device.webaudio.createMediaStreamSource(stream); device.streamNode.connect(device.scriptNode); device.scriptNode.connect(device.webaudio.destination); }) .catch(function(error) { /* I think this should output silence... */ device.scriptNode.connect(device.webaudio.destination); }); } else { device.scriptNode.onaudioprocess = function(e) { if (device.intermediaryBuffer === undefined) { return; /* This means the device has been uninitialized. */ } if(device.intermediaryBufferView.length == 0) { /* Recreate intermediaryBufferView when losing reference to the underlying buffer, probably due to emscripten resizing heap. */ device.intermediaryBufferView = new Float32Array(Module.HEAPF32.buffer, device.intermediaryBuffer, device.intermediaryBufferSizeInBytes); } var outputSilence = false; /* Sanity check. This will never happen, right? */ if (e.outputBuffer.numberOfChannels != channels) { console.log("Playback: Channel count mismatch. " + e.outputBufer.numberOfChannels + " != " + channels + ". Outputting silence."); outputSilence = true; return; } /* This looped design guards against the situation where e.outputBuffer is a different size to the original buffer size. Should never happen in practice. */ var totalFramesProcessed = 0; while (totalFramesProcessed < e.outputBuffer.length) { var framesRemaining = e.outputBuffer.length - totalFramesProcessed; var framesToProcess = framesRemaining; if (framesToProcess > (device.intermediaryBufferSizeInBytes/channels/4)) { framesToProcess = (device.intermediaryBufferSizeInBytes/channels/4); } /* Read data from the client into our intermediary buffer. */ _ma_device_process_pcm_frames_playback__webaudio(pDevice, framesToProcess, device.intermediaryBuffer); /* At this point we'll have data in our intermediary buffer which we now need to deinterleave and copy over to the output buffers. */ if (outputSilence) { for (var iChannel = 0; iChannel < e.outputBuffer.numberOfChannels; ++iChannel) { e.outputBuffer.getChannelData(iChannel).fill(0.0); } } else { for (var iChannel = 0; iChannel < e.outputBuffer.numberOfChannels; ++iChannel) { var outputBuffer = e.outputBuffer.getChannelData(iChannel); var intermediaryBuffer = device.intermediaryBufferView; for (var iFrame = 0; iFrame < framesToProcess; ++iFrame) { outputBuffer[totalFramesProcessed + iFrame] = intermediaryBuffer[iFrame*channels + iChannel]; } } } totalFramesProcessed += framesToProcess; } }; device.scriptNode.connect(device.webaudio.destination); } return miniaudio.track_device(device); }, channels, sampleRate, periodSizeInFrames, deviceType == ma_device_type_capture, pDevice); if (deviceIndex < 0) { return MA_FAILED_TO_OPEN_BACKEND_DEVICE; } if (deviceType == ma_device_type_capture) { pDevice->webaudio.indexCapture = deviceIndex; } else { pDevice->webaudio.indexPlayback = deviceIndex; } pDescriptor->format = ma_format_f32; pDescriptor->channels = channels; ma_channel_map_init_standard(ma_standard_channel_map_webaudio, pDescriptor->channelMap, ma_countof(pDescriptor->channelMap), pDescriptor->channels); pDescriptor->sampleRate = EM_ASM_INT({ return miniaudio.get_device_by_index($0).webaudio.sampleRate; }, deviceIndex); pDescriptor->periodSizeInFrames = periodSizeInFrames; pDescriptor->periodCount = 1; return MA_SUCCESS; } static ma_result ma_device_init__webaudio(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture) { ma_result result; if (pConfig->deviceType == ma_device_type_loopback) { return MA_DEVICE_TYPE_NOT_SUPPORTED; } /* No exclusive mode with Web Audio. */ if (((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pDescriptorPlayback->shareMode == ma_share_mode_exclusive) || ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pDescriptorCapture->shareMode == ma_share_mode_exclusive)) { return MA_SHARE_MODE_NOT_SUPPORTED; } if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { result = ma_device_init_by_type__webaudio(pDevice, pConfig, pDescriptorCapture, ma_device_type_capture); if (result != MA_SUCCESS) { return result; } } if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { result = ma_device_init_by_type__webaudio(pDevice, pConfig, pDescriptorPlayback, ma_device_type_playback); if (result != MA_SUCCESS) { if (pConfig->deviceType == ma_device_type_duplex) { ma_device_uninit_by_index__webaudio(pDevice, ma_device_type_capture, pDevice->webaudio.indexCapture); } return result; } } return MA_SUCCESS; } static ma_result ma_device_start__webaudio(ma_device* pDevice) { MA_ASSERT(pDevice != NULL); if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { EM_ASM({ var device = miniaudio.get_device_by_index($0); device.webaudio.resume(); device.state = 2; /* ma_device_state_started */ }, pDevice->webaudio.indexCapture); } if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { EM_ASM({ var device = miniaudio.get_device_by_index($0); device.webaudio.resume(); device.state = 2; /* ma_device_state_started */ }, pDevice->webaudio.indexPlayback); } return MA_SUCCESS; } static ma_result ma_device_stop__webaudio(ma_device* pDevice) { MA_ASSERT(pDevice != NULL); /* From the WebAudio API documentation for AudioContext.suspend(): Suspends the progression of AudioContext's currentTime, allows any current context processing blocks that are already processed to be played to the destination, and then allows the system to release its claim on audio hardware. I read this to mean that "any current context processing blocks" are processed by suspend() - i.e. They they are drained. We therefore shouldn't need to do any kind of explicit draining. */ if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { EM_ASM({ var device = miniaudio.get_device_by_index($0); device.webaudio.suspend(); device.state = 1; /* ma_device_state_stopped */ }, pDevice->webaudio.indexCapture); } if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { EM_ASM({ var device = miniaudio.get_device_by_index($0); device.webaudio.suspend(); device.state = 1; /* ma_device_state_stopped */ }, pDevice->webaudio.indexPlayback); } ma_device__on_notification_stopped(pDevice); return MA_SUCCESS; } static ma_result ma_context_uninit__webaudio(ma_context* pContext) { MA_ASSERT(pContext != NULL); MA_ASSERT(pContext->backend == ma_backend_webaudio); (void)pContext; /* Unused. */ /* Remove the global miniaudio object from window if there are no more references to it. */ EM_ASM({ if (typeof(window.miniaudio) !== 'undefined') { window.miniaudio.referenceCount--; if (window.miniaudio.referenceCount === 0) { delete window.miniaudio; } } }); return MA_SUCCESS; } static ma_result ma_context_init__webaudio(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks) { int resultFromJS; MA_ASSERT(pContext != NULL); (void)pConfig; /* Unused. */ /* Here is where our global JavaScript object is initialized. */ resultFromJS = EM_ASM_INT({ if (typeof window === 'undefined' || (window.AudioContext || window.webkitAudioContext) === undefined) { return 0; /* Web Audio not supported. */ } if (typeof(window.miniaudio) === 'undefined') { window.miniaudio = { referenceCount: 0 }; miniaudio.devices = []; /* Device cache for mapping devices to indexes for JavaScript/C interop. */ miniaudio.track_device = function(device) { /* Try inserting into a free slot first. */ for (var iDevice = 0; iDevice < miniaudio.devices.length; ++iDevice) { if (miniaudio.devices[iDevice] == null) { miniaudio.devices[iDevice] = device; return iDevice; } } /* Getting here means there is no empty slots in the array so we just push to the end. */ miniaudio.devices.push(device); return miniaudio.devices.length - 1; }; miniaudio.untrack_device_by_index = function(deviceIndex) { /* We just set the device's slot to null. The slot will get reused in the next call to ma_track_device. */ miniaudio.devices[deviceIndex] = null; /* Trim the array if possible. */ while (miniaudio.devices.length > 0) { if (miniaudio.devices[miniaudio.devices.length-1] == null) { miniaudio.devices.pop(); } else { break; } } }; miniaudio.untrack_device = function(device) { for (var iDevice = 0; iDevice < miniaudio.devices.length; ++iDevice) { if (miniaudio.devices[iDevice] == device) { return miniaudio.untrack_device_by_index(iDevice); } } }; miniaudio.get_device_by_index = function(deviceIndex) { return miniaudio.devices[deviceIndex]; }; miniaudio.unlock_event_types = (function(){ return ['touchstart', 'touchend', 'click']; })(); miniaudio.unlock = function() { for(var i = 0; i < miniaudio.devices.length; ++i) { var device = miniaudio.devices[i]; if (device != null && device.webaudio != null && device.state === 2 /* ma_device_state_started */) { device.webaudio.resume(); } } miniaudio.unlock_event_types.map(function(event_type) { document.removeEventListener(event_type, miniaudio.unlock, true); }); }; miniaudio.unlock_event_types.map(function(event_type) { document.addEventListener(event_type, miniaudio.unlock, true); }); } window.miniaudio.referenceCount++; return 1; }, 0); /* Must pass in a dummy argument for C99 compatibility. */ if (resultFromJS != 1) { return MA_FAILED_TO_INIT_BACKEND; } pCallbacks->onContextInit = ma_context_init__webaudio; pCallbacks->onContextUninit = ma_context_uninit__webaudio; pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__webaudio; pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__webaudio; pCallbacks->onDeviceInit = ma_device_init__webaudio; pCallbacks->onDeviceUninit = ma_device_uninit__webaudio; pCallbacks->onDeviceStart = ma_device_start__webaudio; pCallbacks->onDeviceStop = ma_device_stop__webaudio; pCallbacks->onDeviceRead = NULL; /* Not needed because WebAudio is asynchronous. */ pCallbacks->onDeviceWrite = NULL; /* Not needed because WebAudio is asynchronous. */ pCallbacks->onDeviceDataLoop = NULL; /* Not needed because WebAudio is asynchronous. */ return MA_SUCCESS; } #endif /* Web Audio */ static ma_bool32 ma__is_channel_map_valid(const ma_channel* pChannelMap, ma_uint32 channels) { /* A blank channel map should be allowed, in which case it should use an appropriate default which will depend on context. */ if (pChannelMap != NULL && pChannelMap[0] != MA_CHANNEL_NONE) { ma_uint32 iChannel; if (channels == 0 || channels > MA_MAX_CHANNELS) { return MA_FALSE; /* Channel count out of range. */ } /* A channel cannot be present in the channel map more than once. */ for (iChannel = 0; iChannel < channels; ++iChannel) { ma_uint32 jChannel; for (jChannel = iChannel + 1; jChannel < channels; ++jChannel) { if (pChannelMap[iChannel] == pChannelMap[jChannel]) { return MA_FALSE; } } } } return MA_TRUE; } static ma_result ma_device__post_init_setup(ma_device* pDevice, ma_device_type deviceType) { ma_result result; MA_ASSERT(pDevice != NULL); if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex || deviceType == ma_device_type_loopback) { if (pDevice->capture.format == ma_format_unknown) { pDevice->capture.format = pDevice->capture.internalFormat; } if (pDevice->capture.channels == 0) { pDevice->capture.channels = pDevice->capture.internalChannels; } if (pDevice->capture.channelMap[0] == MA_CHANNEL_NONE) { MA_ASSERT(pDevice->capture.channels <= MA_MAX_CHANNELS); if (pDevice->capture.internalChannels == pDevice->capture.channels) { ma_channel_map_copy(pDevice->capture.channelMap, pDevice->capture.internalChannelMap, pDevice->capture.channels); } else { if (pDevice->capture.channelMixMode == ma_channel_mix_mode_simple) { ma_channel_map_init_blank(pDevice->capture.channelMap, pDevice->capture.channels); } else { ma_channel_map_init_standard(ma_standard_channel_map_default, pDevice->capture.channelMap, ma_countof(pDevice->capture.channelMap), pDevice->capture.channels); } } } } if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) { if (pDevice->playback.format == ma_format_unknown) { pDevice->playback.format = pDevice->playback.internalFormat; } if (pDevice->playback.channels == 0) { pDevice->playback.channels = pDevice->playback.internalChannels; } if (pDevice->playback.channelMap[0] == MA_CHANNEL_NONE) { MA_ASSERT(pDevice->playback.channels <= MA_MAX_CHANNELS); if (pDevice->playback.internalChannels == pDevice->playback.channels) { ma_channel_map_copy(pDevice->playback.channelMap, pDevice->playback.internalChannelMap, pDevice->playback.channels); } else { if (pDevice->playback.channelMixMode == ma_channel_mix_mode_simple) { ma_channel_map_init_blank(pDevice->playback.channelMap, pDevice->playback.channels); } else { ma_channel_map_init_standard(ma_standard_channel_map_default, pDevice->playback.channelMap, ma_countof(pDevice->playback.channelMap), pDevice->playback.channels); } } } } if (pDevice->sampleRate == 0) { if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex || deviceType == ma_device_type_loopback) { pDevice->sampleRate = pDevice->capture.internalSampleRate; } else { pDevice->sampleRate = pDevice->playback.internalSampleRate; } } /* Data converters. */ if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex || deviceType == ma_device_type_loopback) { /* Converting from internal device format to client format. */ ma_data_converter_config converterConfig = ma_data_converter_config_init_default(); converterConfig.formatIn = pDevice->capture.internalFormat; converterConfig.channelsIn = pDevice->capture.internalChannels; converterConfig.sampleRateIn = pDevice->capture.internalSampleRate; converterConfig.pChannelMapIn = pDevice->capture.internalChannelMap; converterConfig.formatOut = pDevice->capture.format; converterConfig.channelsOut = pDevice->capture.channels; converterConfig.sampleRateOut = pDevice->sampleRate; converterConfig.pChannelMapOut = pDevice->capture.channelMap; converterConfig.channelMixMode = pDevice->capture.channelMixMode; converterConfig.calculateLFEFromSpatialChannels = pDevice->capture.calculateLFEFromSpatialChannels; converterConfig.allowDynamicSampleRate = MA_FALSE; converterConfig.resampling.algorithm = pDevice->resampling.algorithm; converterConfig.resampling.linear.lpfOrder = pDevice->resampling.linear.lpfOrder; converterConfig.resampling.pBackendVTable = pDevice->resampling.pBackendVTable; converterConfig.resampling.pBackendUserData = pDevice->resampling.pBackendUserData; /* Make sure the old converter is uninitialized first. */ if (ma_device_get_state(pDevice) != ma_device_state_uninitialized) { ma_data_converter_uninit(&pDevice->capture.converter, &pDevice->pContext->allocationCallbacks); } result = ma_data_converter_init(&converterConfig, &pDevice->pContext->allocationCallbacks, &pDevice->capture.converter); if (result != MA_SUCCESS) { return result; } } if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) { /* Converting from client format to device format. */ ma_data_converter_config converterConfig = ma_data_converter_config_init_default(); converterConfig.formatIn = pDevice->playback.format; converterConfig.channelsIn = pDevice->playback.channels; converterConfig.sampleRateIn = pDevice->sampleRate; converterConfig.pChannelMapIn = pDevice->playback.channelMap; converterConfig.formatOut = pDevice->playback.internalFormat; converterConfig.channelsOut = pDevice->playback.internalChannels; converterConfig.sampleRateOut = pDevice->playback.internalSampleRate; converterConfig.pChannelMapOut = pDevice->playback.internalChannelMap; converterConfig.channelMixMode = pDevice->playback.channelMixMode; converterConfig.calculateLFEFromSpatialChannels = pDevice->playback.calculateLFEFromSpatialChannels; converterConfig.allowDynamicSampleRate = MA_FALSE; converterConfig.resampling.algorithm = pDevice->resampling.algorithm; converterConfig.resampling.linear.lpfOrder = pDevice->resampling.linear.lpfOrder; converterConfig.resampling.pBackendVTable = pDevice->resampling.pBackendVTable; converterConfig.resampling.pBackendUserData = pDevice->resampling.pBackendUserData; /* Make sure the old converter is uninitialized first. */ if (ma_device_get_state(pDevice) != ma_device_state_uninitialized) { ma_data_converter_uninit(&pDevice->playback.converter, &pDevice->pContext->allocationCallbacks); } result = ma_data_converter_init(&converterConfig, &pDevice->pContext->allocationCallbacks, &pDevice->playback.converter); if (result != MA_SUCCESS) { return result; } } /* In playback mode, if the data converter does not support retrieval of the required number of input frames given a number of output frames, we need to fall back to a heap-allocated cache. */ if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) { ma_uint64 unused; pDevice->playback.inputCacheConsumed = 0; pDevice->playback.inputCacheRemaining = 0; if (deviceType == ma_device_type_duplex || ma_data_converter_get_required_input_frame_count(&pDevice->playback.converter, 1, &unused) != MA_SUCCESS) { /* We need a heap allocated cache. We want to size this based on the period size. */ void* pNewInputCache; ma_uint64 newInputCacheCap; ma_uint64 newInputCacheSizeInBytes; newInputCacheCap = ma_calculate_frame_count_after_resampling(pDevice->playback.internalSampleRate, pDevice->sampleRate, pDevice->playback.internalPeriodSizeInFrames); newInputCacheSizeInBytes = newInputCacheCap * ma_get_bytes_per_frame(pDevice->playback.format, pDevice->playback.channels); if (newInputCacheSizeInBytes > MA_SIZE_MAX) { ma_free(pDevice->playback.pInputCache, &pDevice->pContext->allocationCallbacks); pDevice->playback.pInputCache = NULL; pDevice->playback.inputCacheCap = 0; return MA_OUT_OF_MEMORY; /* Allocation too big. Should never hit this, but makes the cast below safer for 32-bit builds. */ } pNewInputCache = ma_realloc(pDevice->playback.pInputCache, (size_t)newInputCacheSizeInBytes, &pDevice->pContext->allocationCallbacks); if (pNewInputCache == NULL) { ma_free(pDevice->playback.pInputCache, &pDevice->pContext->allocationCallbacks); pDevice->playback.pInputCache = NULL; pDevice->playback.inputCacheCap = 0; return MA_OUT_OF_MEMORY; } pDevice->playback.pInputCache = pNewInputCache; pDevice->playback.inputCacheCap = newInputCacheCap; } else { /* Heap allocation not required. Make sure we clear out the old cache just in case this function was called in response to a route change. */ ma_free(pDevice->playback.pInputCache, &pDevice->pContext->allocationCallbacks); pDevice->playback.pInputCache = NULL; pDevice->playback.inputCacheCap = 0; } } return MA_SUCCESS; } MA_API ma_result ma_device_post_init(ma_device* pDevice, ma_device_type deviceType, const ma_device_descriptor* pDescriptorPlayback, const ma_device_descriptor* pDescriptorCapture) { ma_result result; if (pDevice == NULL) { return MA_INVALID_ARGS; } /* Capture. */ if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex || deviceType == ma_device_type_loopback) { if (ma_device_descriptor_is_valid(pDescriptorCapture) == MA_FALSE) { return MA_INVALID_ARGS; } pDevice->capture.internalFormat = pDescriptorCapture->format; pDevice->capture.internalChannels = pDescriptorCapture->channels; pDevice->capture.internalSampleRate = pDescriptorCapture->sampleRate; MA_COPY_MEMORY(pDevice->capture.internalChannelMap, pDescriptorCapture->channelMap, sizeof(pDescriptorCapture->channelMap)); pDevice->capture.internalPeriodSizeInFrames = pDescriptorCapture->periodSizeInFrames; pDevice->capture.internalPeriods = pDescriptorCapture->periodCount; if (pDevice->capture.internalPeriodSizeInFrames == 0) { pDevice->capture.internalPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(pDescriptorCapture->periodSizeInMilliseconds, pDescriptorCapture->sampleRate); } } /* Playback. */ if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) { if (ma_device_descriptor_is_valid(pDescriptorPlayback) == MA_FALSE) { return MA_INVALID_ARGS; } pDevice->playback.internalFormat = pDescriptorPlayback->format; pDevice->playback.internalChannels = pDescriptorPlayback->channels; pDevice->playback.internalSampleRate = pDescriptorPlayback->sampleRate; MA_COPY_MEMORY(pDevice->playback.internalChannelMap, pDescriptorPlayback->channelMap, sizeof(pDescriptorPlayback->channelMap)); pDevice->playback.internalPeriodSizeInFrames = pDescriptorPlayback->periodSizeInFrames; pDevice->playback.internalPeriods = pDescriptorPlayback->periodCount; if (pDevice->playback.internalPeriodSizeInFrames == 0) { pDevice->playback.internalPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(pDescriptorPlayback->periodSizeInMilliseconds, pDescriptorPlayback->sampleRate); } } /* The name of the device can be retrieved from device info. This may be temporary and replaced with a `ma_device_get_info(pDevice, deviceType)` instead. For loopback devices, we need to retrieve the name of the playback device. */ { ma_device_info deviceInfo; if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex || deviceType == ma_device_type_loopback) { result = ma_device_get_info(pDevice, (deviceType == ma_device_type_loopback) ? ma_device_type_playback : ma_device_type_capture, &deviceInfo); if (result == MA_SUCCESS) { ma_strncpy_s(pDevice->capture.name, sizeof(pDevice->capture.name), deviceInfo.name, (size_t)-1); } else { /* We failed to retrieve the device info. Fall back to a default name. */ if (pDescriptorCapture->pDeviceID == NULL) { ma_strncpy_s(pDevice->capture.name, sizeof(pDevice->capture.name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); } else { ma_strncpy_s(pDevice->capture.name, sizeof(pDevice->capture.name), "Capture Device", (size_t)-1); } } } if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) { result = ma_device_get_info(pDevice, ma_device_type_playback, &deviceInfo); if (result == MA_SUCCESS) { ma_strncpy_s(pDevice->playback.name, sizeof(pDevice->playback.name), deviceInfo.name, (size_t)-1); } else { /* We failed to retrieve the device info. Fall back to a default name. */ if (pDescriptorPlayback->pDeviceID == NULL) { ma_strncpy_s(pDevice->playback.name, sizeof(pDevice->playback.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); } else { ma_strncpy_s(pDevice->playback.name, sizeof(pDevice->playback.name), "Playback Device", (size_t)-1); } } } } /* Update data conversion. */ return ma_device__post_init_setup(pDevice, deviceType); /* TODO: Should probably rename ma_device__post_init_setup() to something better. */ } static ma_thread_result MA_THREADCALL ma_worker_thread(void* pData) { ma_device* pDevice = (ma_device*)pData; MA_ASSERT(pDevice != NULL); #ifdef MA_WIN32 ma_CoInitializeEx(pDevice->pContext, NULL, MA_COINIT_VALUE); #endif /* When the device is being initialized it's initial state is set to ma_device_state_uninitialized. Before returning from ma_device_init(), the state needs to be set to something valid. In miniaudio the device's default state immediately after initialization is stopped, so therefore we need to mark the device as such. miniaudio will wait on the worker thread to signal an event to know when the worker thread is ready for action. */ ma_device__set_state(pDevice, ma_device_state_stopped); ma_event_signal(&pDevice->stopEvent); for (;;) { /* <-- This loop just keeps the thread alive. The main audio loop is inside. */ ma_result startResult; ma_result stopResult; /* <-- This will store the result from onDeviceStop(). If it returns an error, we don't fire the stopped notification callback. */ /* We wait on an event to know when something has requested that the device be started and the main loop entered. */ ma_event_wait(&pDevice->wakeupEvent); /* Default result code. */ pDevice->workResult = MA_SUCCESS; /* If the reason for the wake up is that we are terminating, just break from the loop. */ if (ma_device_get_state(pDevice) == ma_device_state_uninitialized) { break; } /* Getting to this point means the device is wanting to get started. The function that has requested that the device be started will be waiting on an event (pDevice->startEvent) which means we need to make sure we signal the event in both the success and error case. It's important that the state of the device is set _before_ signaling the event. */ MA_ASSERT(ma_device_get_state(pDevice) == ma_device_state_starting); /* If the device has a start callback, start it now. */ if (pDevice->pContext->callbacks.onDeviceStart != NULL) { startResult = pDevice->pContext->callbacks.onDeviceStart(pDevice); } else { startResult = MA_SUCCESS; } /* If starting was not successful we'll need to loop back to the start and wait for something to happen (pDevice->wakeupEvent). */ if (startResult != MA_SUCCESS) { pDevice->workResult = startResult; ma_event_signal(&pDevice->startEvent); /* <-- Always signal the start event so ma_device_start() can return as it'll be waiting on it. */ continue; } /* Make sure the state is set appropriately. */ ma_device__set_state(pDevice, ma_device_state_started); /* <-- Set this before signaling the event so that the state is always guaranteed to be good after ma_device_start() has returned. */ ma_event_signal(&pDevice->startEvent); ma_device__on_notification_started(pDevice); if (pDevice->pContext->callbacks.onDeviceDataLoop != NULL) { pDevice->pContext->callbacks.onDeviceDataLoop(pDevice); } else { /* The backend is not using a custom main loop implementation, so now fall back to the blocking read-write implementation. */ ma_device_audio_thread__default_read_write(pDevice); } /* Getting here means we have broken from the main loop which happens the application has requested that device be stopped. */ if (pDevice->pContext->callbacks.onDeviceStop != NULL) { stopResult = pDevice->pContext->callbacks.onDeviceStop(pDevice); } else { stopResult = MA_SUCCESS; /* No stop callback with the backend. Just assume successful. */ } /* After the device has stopped, make sure an event is posted. Don't post a stopped event if stopping failed. This can happen on some backends when the underlying stream has been stopped due to the device being physically unplugged or disabled via an OS setting. */ if (stopResult == MA_SUCCESS) { ma_device__on_notification_stopped(pDevice); } /* A function somewhere is waiting for the device to have stopped for real so we need to signal an event to allow it to continue. */ ma_device__set_state(pDevice, ma_device_state_stopped); ma_event_signal(&pDevice->stopEvent); } #ifdef MA_WIN32 ma_CoUninitialize(pDevice->pContext); #endif return (ma_thread_result)0; } /* Helper for determining whether or not the given device is initialized. */ static ma_bool32 ma_device__is_initialized(ma_device* pDevice) { if (pDevice == NULL) { return MA_FALSE; } return ma_device_get_state(pDevice) != ma_device_state_uninitialized; } #ifdef MA_WIN32 static ma_result ma_context_uninit_backend_apis__win32(ma_context* pContext) { /* For some reason UWP complains when CoUninitialize() is called. I'm just not going to call it on UWP. */ #ifdef MA_WIN32_DESKTOP ma_CoUninitialize(pContext); ma_dlclose(pContext, pContext->win32.hUser32DLL); ma_dlclose(pContext, pContext->win32.hOle32DLL); ma_dlclose(pContext, pContext->win32.hAdvapi32DLL); #else (void)pContext; #endif return MA_SUCCESS; } static ma_result ma_context_init_backend_apis__win32(ma_context* pContext) { #ifdef MA_WIN32_DESKTOP /* Ole32.dll */ pContext->win32.hOle32DLL = ma_dlopen(pContext, "ole32.dll"); if (pContext->win32.hOle32DLL == NULL) { return MA_FAILED_TO_INIT_BACKEND; } pContext->win32.CoInitializeEx = (ma_proc)ma_dlsym(pContext, pContext->win32.hOle32DLL, "CoInitializeEx"); pContext->win32.CoUninitialize = (ma_proc)ma_dlsym(pContext, pContext->win32.hOle32DLL, "CoUninitialize"); pContext->win32.CoCreateInstance = (ma_proc)ma_dlsym(pContext, pContext->win32.hOle32DLL, "CoCreateInstance"); pContext->win32.CoTaskMemFree = (ma_proc)ma_dlsym(pContext, pContext->win32.hOle32DLL, "CoTaskMemFree"); pContext->win32.PropVariantClear = (ma_proc)ma_dlsym(pContext, pContext->win32.hOle32DLL, "PropVariantClear"); pContext->win32.StringFromGUID2 = (ma_proc)ma_dlsym(pContext, pContext->win32.hOle32DLL, "StringFromGUID2"); /* User32.dll */ pContext->win32.hUser32DLL = ma_dlopen(pContext, "user32.dll"); if (pContext->win32.hUser32DLL == NULL) { return MA_FAILED_TO_INIT_BACKEND; } pContext->win32.GetForegroundWindow = (ma_proc)ma_dlsym(pContext, pContext->win32.hUser32DLL, "GetForegroundWindow"); pContext->win32.GetDesktopWindow = (ma_proc)ma_dlsym(pContext, pContext->win32.hUser32DLL, "GetDesktopWindow"); /* Advapi32.dll */ pContext->win32.hAdvapi32DLL = ma_dlopen(pContext, "advapi32.dll"); if (pContext->win32.hAdvapi32DLL == NULL) { return MA_FAILED_TO_INIT_BACKEND; } pContext->win32.RegOpenKeyExA = (ma_proc)ma_dlsym(pContext, pContext->win32.hAdvapi32DLL, "RegOpenKeyExA"); pContext->win32.RegCloseKey = (ma_proc)ma_dlsym(pContext, pContext->win32.hAdvapi32DLL, "RegCloseKey"); pContext->win32.RegQueryValueExA = (ma_proc)ma_dlsym(pContext, pContext->win32.hAdvapi32DLL, "RegQueryValueExA"); #else (void)pContext; /* Unused. */ #endif ma_CoInitializeEx(pContext, NULL, MA_COINIT_VALUE); return MA_SUCCESS; } #else static ma_result ma_context_uninit_backend_apis__nix(ma_context* pContext) { #if defined(MA_USE_RUNTIME_LINKING_FOR_PTHREAD) && !defined(MA_NO_RUNTIME_LINKING) ma_dlclose(pContext, pContext->posix.pthreadSO); #else (void)pContext; #endif return MA_SUCCESS; } static ma_result ma_context_init_backend_apis__nix(ma_context* pContext) { /* pthread */ #if defined(MA_USE_RUNTIME_LINKING_FOR_PTHREAD) && !defined(MA_NO_RUNTIME_LINKING) const char* libpthreadFileNames[] = { "libpthread.so", "libpthread.so.0", "libpthread.dylib" }; size_t i; for (i = 0; i < sizeof(libpthreadFileNames) / sizeof(libpthreadFileNames[0]); ++i) { pContext->posix.pthreadSO = ma_dlopen(pContext, libpthreadFileNames[i]); if (pContext->posix.pthreadSO != NULL) { break; } } if (pContext->posix.pthreadSO == NULL) { return MA_FAILED_TO_INIT_BACKEND; } pContext->posix.pthread_create = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_create"); pContext->posix.pthread_join = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_join"); pContext->posix.pthread_mutex_init = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_mutex_init"); pContext->posix.pthread_mutex_destroy = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_mutex_destroy"); pContext->posix.pthread_mutex_lock = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_mutex_lock"); pContext->posix.pthread_mutex_unlock = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_mutex_unlock"); pContext->posix.pthread_cond_init = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_cond_init"); pContext->posix.pthread_cond_destroy = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_cond_destroy"); pContext->posix.pthread_cond_wait = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_cond_wait"); pContext->posix.pthread_cond_signal = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_cond_signal"); pContext->posix.pthread_attr_init = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_attr_init"); pContext->posix.pthread_attr_destroy = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_attr_destroy"); pContext->posix.pthread_attr_setschedpolicy = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_attr_setschedpolicy"); pContext->posix.pthread_attr_getschedparam = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_attr_getschedparam"); pContext->posix.pthread_attr_setschedparam = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_attr_setschedparam"); #else pContext->posix.pthread_create = (ma_proc)pthread_create; pContext->posix.pthread_join = (ma_proc)pthread_join; pContext->posix.pthread_mutex_init = (ma_proc)pthread_mutex_init; pContext->posix.pthread_mutex_destroy = (ma_proc)pthread_mutex_destroy; pContext->posix.pthread_mutex_lock = (ma_proc)pthread_mutex_lock; pContext->posix.pthread_mutex_unlock = (ma_proc)pthread_mutex_unlock; pContext->posix.pthread_cond_init = (ma_proc)pthread_cond_init; pContext->posix.pthread_cond_destroy = (ma_proc)pthread_cond_destroy; pContext->posix.pthread_cond_wait = (ma_proc)pthread_cond_wait; pContext->posix.pthread_cond_signal = (ma_proc)pthread_cond_signal; pContext->posix.pthread_attr_init = (ma_proc)pthread_attr_init; pContext->posix.pthread_attr_destroy = (ma_proc)pthread_attr_destroy; #if !defined(__EMSCRIPTEN__) pContext->posix.pthread_attr_setschedpolicy = (ma_proc)pthread_attr_setschedpolicy; pContext->posix.pthread_attr_getschedparam = (ma_proc)pthread_attr_getschedparam; pContext->posix.pthread_attr_setschedparam = (ma_proc)pthread_attr_setschedparam; #endif #endif return MA_SUCCESS; } #endif static ma_result ma_context_init_backend_apis(ma_context* pContext) { ma_result result; #ifdef MA_WIN32 result = ma_context_init_backend_apis__win32(pContext); #else result = ma_context_init_backend_apis__nix(pContext); #endif return result; } static ma_result ma_context_uninit_backend_apis(ma_context* pContext) { ma_result result; #ifdef MA_WIN32 result = ma_context_uninit_backend_apis__win32(pContext); #else result = ma_context_uninit_backend_apis__nix(pContext); #endif return result; } static ma_bool32 ma_context_is_backend_asynchronous(ma_context* pContext) { MA_ASSERT(pContext != NULL); if (pContext->callbacks.onDeviceRead == NULL && pContext->callbacks.onDeviceWrite == NULL) { if (pContext->callbacks.onDeviceDataLoop == NULL) { return MA_TRUE; } else { return MA_FALSE; } } else { return MA_FALSE; } } /* The default capacity doesn't need to be too big. */ #ifndef MA_DEFAULT_DEVICE_JOB_QUEUE_CAPACITY #define MA_DEFAULT_DEVICE_JOB_QUEUE_CAPACITY 32 #endif MA_API ma_device_job_thread_config ma_device_job_thread_config_init(void) { ma_device_job_thread_config config; MA_ZERO_OBJECT(&config); config.noThread = MA_FALSE; config.jobQueueCapacity = MA_DEFAULT_DEVICE_JOB_QUEUE_CAPACITY; config.jobQueueFlags = 0; return config; } static ma_thread_result MA_THREADCALL ma_device_job_thread_entry(void* pUserData) { ma_device_job_thread* pJobThread = (ma_device_job_thread*)pUserData; MA_ASSERT(pJobThread != NULL); for (;;) { ma_result result; ma_job job; result = ma_device_job_thread_next(pJobThread, &job); if (result != MA_SUCCESS) { break; } if (job.toc.breakup.code == MA_JOB_TYPE_QUIT) { break; } ma_job_process(&job); } return (ma_thread_result)0; } MA_API ma_result ma_device_job_thread_init(const ma_device_job_thread_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_device_job_thread* pJobThread) { ma_result result; ma_job_queue_config jobQueueConfig; if (pJobThread == NULL) { return MA_INVALID_ARGS; } MA_ZERO_OBJECT(pJobThread); if (pConfig == NULL) { return MA_INVALID_ARGS; } /* Initialize the job queue before the thread to ensure it's in a valid state. */ jobQueueConfig = ma_job_queue_config_init(pConfig->jobQueueFlags, pConfig->jobQueueCapacity); result = ma_job_queue_init(&jobQueueConfig, pAllocationCallbacks, &pJobThread->jobQueue); if (result != MA_SUCCESS) { return result; /* Failed to initialize job queue. */ } /* The thread needs to be initialized after the job queue to ensure the thread doesn't try to access it prematurely. */ if (pConfig->noThread == MA_FALSE) { result = ma_thread_create(&pJobThread->thread, ma_thread_priority_normal, 0, ma_device_job_thread_entry, pJobThread, pAllocationCallbacks); if (result != MA_SUCCESS) { ma_job_queue_uninit(&pJobThread->jobQueue, pAllocationCallbacks); return result; /* Failed to create the job thread. */ } pJobThread->_hasThread = MA_TRUE; } else { pJobThread->_hasThread = MA_FALSE; } return MA_SUCCESS; } MA_API void ma_device_job_thread_uninit(ma_device_job_thread* pJobThread, const ma_allocation_callbacks* pAllocationCallbacks) { if (pJobThread == NULL) { return; } /* The first thing to do is post a quit message to the job queue. If we're using a thread we'll need to wait for it. */ { ma_job job = ma_job_init(MA_JOB_TYPE_QUIT); ma_device_job_thread_post(pJobThread, &job); } /* Wait for the thread to terminate naturally. */ if (pJobThread->_hasThread) { ma_thread_wait(&pJobThread->thread); } /* At this point the thread should be terminated so we can safely uninitialize the job queue. */ ma_job_queue_uninit(&pJobThread->jobQueue, pAllocationCallbacks); } MA_API ma_result ma_device_job_thread_post(ma_device_job_thread* pJobThread, const ma_job* pJob) { if (pJobThread == NULL || pJob == NULL) { return MA_INVALID_ARGS; } return ma_job_queue_post(&pJobThread->jobQueue, pJob); } MA_API ma_result ma_device_job_thread_next(ma_device_job_thread* pJobThread, ma_job* pJob) { if (pJob == NULL) { return MA_INVALID_ARGS; } MA_ZERO_OBJECT(pJob); if (pJobThread == NULL) { return MA_INVALID_ARGS; } return ma_job_queue_next(&pJobThread->jobQueue, pJob); } MA_API ma_context_config ma_context_config_init(void) { ma_context_config config; MA_ZERO_OBJECT(&config); return config; } MA_API ma_result ma_context_init(const ma_backend backends[], ma_uint32 backendCount, const ma_context_config* pConfig, ma_context* pContext) { ma_result result; ma_context_config defaultConfig; ma_backend defaultBackends[ma_backend_null+1]; ma_uint32 iBackend; ma_backend* pBackendsToIterate; ma_uint32 backendsToIterateCount; if (pContext == NULL) { return MA_INVALID_ARGS; } MA_ZERO_OBJECT(pContext); /* Always make sure the config is set first to ensure properties are available as soon as possible. */ if (pConfig == NULL) { defaultConfig = ma_context_config_init(); pConfig = &defaultConfig; } /* Allocation callbacks need to come first because they'll be passed around to other areas. */ result = ma_allocation_callbacks_init_copy(&pContext->allocationCallbacks, &pConfig->allocationCallbacks); if (result != MA_SUCCESS) { return result; } /* Get a lot set up first so we can start logging ASAP. */ if (pConfig->pLog != NULL) { pContext->pLog = pConfig->pLog; } else { result = ma_log_init(&pContext->allocationCallbacks, &pContext->log); if (result == MA_SUCCESS) { pContext->pLog = &pContext->log; } else { pContext->pLog = NULL; /* Logging is not available. */ } } pContext->threadPriority = pConfig->threadPriority; pContext->threadStackSize = pConfig->threadStackSize; pContext->pUserData = pConfig->pUserData; /* Backend APIs need to be initialized first. This is where external libraries will be loaded and linked. */ result = ma_context_init_backend_apis(pContext); if (result != MA_SUCCESS) { return result; } for (iBackend = 0; iBackend <= ma_backend_null; ++iBackend) { defaultBackends[iBackend] = (ma_backend)iBackend; } pBackendsToIterate = (ma_backend*)backends; backendsToIterateCount = backendCount; if (pBackendsToIterate == NULL) { pBackendsToIterate = (ma_backend*)defaultBackends; backendsToIterateCount = ma_countof(defaultBackends); } MA_ASSERT(pBackendsToIterate != NULL); for (iBackend = 0; iBackend < backendsToIterateCount; iBackend += 1) { ma_backend backend = pBackendsToIterate[iBackend]; /* Make sure all callbacks are reset so we don't accidentally drag in any from previously failed initialization attempts. */ MA_ZERO_OBJECT(&pContext->callbacks); /* These backends are using the new callback system. */ switch (backend) { #ifdef MA_HAS_WASAPI case ma_backend_wasapi: { pContext->callbacks.onContextInit = ma_context_init__wasapi; } break; #endif #ifdef MA_HAS_DSOUND case ma_backend_dsound: { pContext->callbacks.onContextInit = ma_context_init__dsound; } break; #endif #ifdef MA_HAS_WINMM case ma_backend_winmm: { pContext->callbacks.onContextInit = ma_context_init__winmm; } break; #endif #ifdef MA_HAS_COREAUDIO case ma_backend_coreaudio: { pContext->callbacks.onContextInit = ma_context_init__coreaudio; } break; #endif #ifdef MA_HAS_SNDIO case ma_backend_sndio: { pContext->callbacks.onContextInit = ma_context_init__sndio; } break; #endif #ifdef MA_HAS_AUDIO4 case ma_backend_audio4: { pContext->callbacks.onContextInit = ma_context_init__audio4; } break; #endif #ifdef MA_HAS_OSS case ma_backend_oss: { pContext->callbacks.onContextInit = ma_context_init__oss; } break; #endif #ifdef MA_HAS_PULSEAUDIO case ma_backend_pulseaudio: { pContext->callbacks.onContextInit = ma_context_init__pulse; } break; #endif #ifdef MA_HAS_ALSA case ma_backend_alsa: { pContext->callbacks.onContextInit = ma_context_init__alsa; } break; #endif #ifdef MA_HAS_JACK case ma_backend_jack: { pContext->callbacks.onContextInit = ma_context_init__jack; } break; #endif #ifdef MA_HAS_AAUDIO case ma_backend_aaudio: { if (ma_is_backend_enabled(backend)) { pContext->callbacks.onContextInit = ma_context_init__aaudio; } } break; #endif #ifdef MA_HAS_OPENSL case ma_backend_opensl: { if (ma_is_backend_enabled(backend)) { pContext->callbacks.onContextInit = ma_context_init__opensl; } } break; #endif #ifdef MA_HAS_WEBAUDIO case ma_backend_webaudio: { pContext->callbacks.onContextInit = ma_context_init__webaudio; } break; #endif #ifdef MA_HAS_CUSTOM case ma_backend_custom: { /* Slightly different logic for custom backends. Custom backends can optionally set all of their callbacks in the config. */ pContext->callbacks = pConfig->custom; } break; #endif #ifdef MA_HAS_NULL case ma_backend_null: { pContext->callbacks.onContextInit = ma_context_init__null; } break; #endif default: break; } if (pContext->callbacks.onContextInit != NULL) { ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "Attempting to initialize %s backend...\n", ma_get_backend_name(backend)); result = pContext->callbacks.onContextInit(pContext, pConfig, &pContext->callbacks); } else { result = MA_NO_BACKEND; } /* If this iteration was successful, return. */ if (result == MA_SUCCESS) { result = ma_mutex_init(&pContext->deviceEnumLock); if (result != MA_SUCCESS) { ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_WARNING, "Failed to initialize mutex for device enumeration. ma_context_get_devices() is not thread safe.\n"); } result = ma_mutex_init(&pContext->deviceInfoLock); if (result != MA_SUCCESS) { ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_WARNING, "Failed to initialize mutex for device info retrieval. ma_context_get_device_info() is not thread safe.\n"); } ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "System Architecture:\n"); ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, " Endian: %s\n", ma_is_little_endian() ? "LE" : "BE"); ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, " SSE2: %s\n", ma_has_sse2() ? "YES" : "NO"); ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, " AVX2: %s\n", ma_has_avx2() ? "YES" : "NO"); ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, " NEON: %s\n", ma_has_neon() ? "YES" : "NO"); pContext->backend = backend; return result; } else { ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "Failed to initialize %s backend.\n", ma_get_backend_name(backend)); } } /* If we get here it means an error occurred. */ MA_ZERO_OBJECT(pContext); /* Safety. */ return MA_NO_BACKEND; } MA_API ma_result ma_context_uninit(ma_context* pContext) { if (pContext == NULL) { return MA_INVALID_ARGS; } if (pContext->callbacks.onContextUninit != NULL) { pContext->callbacks.onContextUninit(pContext); } ma_mutex_uninit(&pContext->deviceEnumLock); ma_mutex_uninit(&pContext->deviceInfoLock); ma_free(pContext->pDeviceInfos, &pContext->allocationCallbacks); ma_context_uninit_backend_apis(pContext); if (pContext->pLog == &pContext->log) { ma_log_uninit(&pContext->log); } return MA_SUCCESS; } MA_API size_t ma_context_sizeof() { return sizeof(ma_context); } MA_API ma_log* ma_context_get_log(ma_context* pContext) { if (pContext == NULL) { return NULL; } return pContext->pLog; } MA_API ma_result ma_context_enumerate_devices(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData) { ma_result result; if (pContext == NULL || callback == NULL) { return MA_INVALID_ARGS; } if (pContext->callbacks.onContextEnumerateDevices == NULL) { return MA_INVALID_OPERATION; } ma_mutex_lock(&pContext->deviceEnumLock); { result = pContext->callbacks.onContextEnumerateDevices(pContext, callback, pUserData); } ma_mutex_unlock(&pContext->deviceEnumLock); return result; } static ma_bool32 ma_context_get_devices__enum_callback(ma_context* pContext, ma_device_type deviceType, const ma_device_info* pInfo, void* pUserData) { /* We need to insert the device info into our main internal buffer. Where it goes depends on the device type. If it's a capture device it's just appended to the end. If it's a playback device it's inserted just before the first capture device. */ /* First make sure we have room. Since the number of devices we add to the list is usually relatively small I've decided to use a simple fixed size increment for buffer expansion. */ const ma_uint32 bufferExpansionCount = 2; const ma_uint32 totalDeviceInfoCount = pContext->playbackDeviceInfoCount + pContext->captureDeviceInfoCount; if (totalDeviceInfoCount >= pContext->deviceInfoCapacity) { ma_uint32 newCapacity = pContext->deviceInfoCapacity + bufferExpansionCount; ma_device_info* pNewInfos = (ma_device_info*)ma_realloc(pContext->pDeviceInfos, sizeof(*pContext->pDeviceInfos)*newCapacity, &pContext->allocationCallbacks); if (pNewInfos == NULL) { return MA_FALSE; /* Out of memory. */ } pContext->pDeviceInfos = pNewInfos; pContext->deviceInfoCapacity = newCapacity; } if (deviceType == ma_device_type_playback) { /* Playback. Insert just before the first capture device. */ /* The first thing to do is move all of the capture devices down a slot. */ ma_uint32 iFirstCaptureDevice = pContext->playbackDeviceInfoCount; size_t iCaptureDevice; for (iCaptureDevice = totalDeviceInfoCount; iCaptureDevice > iFirstCaptureDevice; --iCaptureDevice) { pContext->pDeviceInfos[iCaptureDevice] = pContext->pDeviceInfos[iCaptureDevice-1]; } /* Now just insert where the first capture device was before moving it down a slot. */ pContext->pDeviceInfos[iFirstCaptureDevice] = *pInfo; pContext->playbackDeviceInfoCount += 1; } else { /* Capture. Insert at the end. */ pContext->pDeviceInfos[totalDeviceInfoCount] = *pInfo; pContext->captureDeviceInfoCount += 1; } (void)pUserData; return MA_TRUE; } MA_API ma_result ma_context_get_devices(ma_context* pContext, ma_device_info** ppPlaybackDeviceInfos, ma_uint32* pPlaybackDeviceCount, ma_device_info** ppCaptureDeviceInfos, ma_uint32* pCaptureDeviceCount) { ma_result result; /* Safety. */ if (ppPlaybackDeviceInfos != NULL) *ppPlaybackDeviceInfos = NULL; if (pPlaybackDeviceCount != NULL) *pPlaybackDeviceCount = 0; if (ppCaptureDeviceInfos != NULL) *ppCaptureDeviceInfos = NULL; if (pCaptureDeviceCount != NULL) *pCaptureDeviceCount = 0; if (pContext == NULL) { return MA_INVALID_ARGS; } if (pContext->callbacks.onContextEnumerateDevices == NULL) { return MA_INVALID_OPERATION; } /* Note that we don't use ma_context_enumerate_devices() here because we want to do locking at a higher level. */ ma_mutex_lock(&pContext->deviceEnumLock); { /* Reset everything first. */ pContext->playbackDeviceInfoCount = 0; pContext->captureDeviceInfoCount = 0; /* Now enumerate over available devices. */ result = pContext->callbacks.onContextEnumerateDevices(pContext, ma_context_get_devices__enum_callback, NULL); if (result == MA_SUCCESS) { /* Playback devices. */ if (ppPlaybackDeviceInfos != NULL) { *ppPlaybackDeviceInfos = pContext->pDeviceInfos; } if (pPlaybackDeviceCount != NULL) { *pPlaybackDeviceCount = pContext->playbackDeviceInfoCount; } /* Capture devices. */ if (ppCaptureDeviceInfos != NULL) { *ppCaptureDeviceInfos = pContext->pDeviceInfos; /* Capture devices come after playback devices. */ if (pContext->playbackDeviceInfoCount > 0) { /* Conditional, because NULL+0 is undefined behavior. */ *ppCaptureDeviceInfos += pContext->playbackDeviceInfoCount; } } if (pCaptureDeviceCount != NULL) { *pCaptureDeviceCount = pContext->captureDeviceInfoCount; } } } ma_mutex_unlock(&pContext->deviceEnumLock); return result; } MA_API ma_result ma_context_get_device_info(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo) { ma_result result; ma_device_info deviceInfo; /* NOTE: Do not clear pDeviceInfo on entry. The reason is the pDeviceID may actually point to pDeviceInfo->id which will break things. */ if (pContext == NULL || pDeviceInfo == NULL) { return MA_INVALID_ARGS; } MA_ZERO_OBJECT(&deviceInfo); /* Help the backend out by copying over the device ID if we have one. */ if (pDeviceID != NULL) { MA_COPY_MEMORY(&deviceInfo.id, pDeviceID, sizeof(*pDeviceID)); } if (pContext->callbacks.onContextGetDeviceInfo == NULL) { return MA_INVALID_OPERATION; } ma_mutex_lock(&pContext->deviceInfoLock); { result = pContext->callbacks.onContextGetDeviceInfo(pContext, deviceType, pDeviceID, &deviceInfo); } ma_mutex_unlock(&pContext->deviceInfoLock); *pDeviceInfo = deviceInfo; return result; } MA_API ma_bool32 ma_context_is_loopback_supported(ma_context* pContext) { if (pContext == NULL) { return MA_FALSE; } return ma_is_loopback_supported(pContext->backend); } MA_API ma_device_config ma_device_config_init(ma_device_type deviceType) { ma_device_config config; MA_ZERO_OBJECT(&config); config.deviceType = deviceType; config.resampling = ma_resampler_config_init(ma_format_unknown, 0, 0, 0, ma_resample_algorithm_linear); /* Format/channels/rate don't matter here. */ return config; } MA_API ma_result ma_device_init(ma_context* pContext, const ma_device_config* pConfig, ma_device* pDevice) { ma_result result; ma_device_descriptor descriptorPlayback; ma_device_descriptor descriptorCapture; /* The context can be null, in which case we self-manage it. */ if (pContext == NULL) { return ma_device_init_ex(NULL, 0, NULL, pConfig, pDevice); } if (pDevice == NULL) { return MA_INVALID_ARGS; } MA_ZERO_OBJECT(pDevice); if (pConfig == NULL) { return MA_INVALID_ARGS; } /* Check that we have our callbacks defined. */ if (pContext->callbacks.onDeviceInit == NULL) { return MA_INVALID_OPERATION; } /* Basic config validation. */ if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { if (pConfig->capture.channels > MA_MAX_CHANNELS) { return MA_INVALID_ARGS; } if (!ma__is_channel_map_valid(pConfig->capture.pChannelMap, pConfig->capture.channels)) { return MA_INVALID_ARGS; } } if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex || pConfig->deviceType == ma_device_type_loopback) { if (pConfig->playback.channels > MA_MAX_CHANNELS) { return MA_INVALID_ARGS; } if (!ma__is_channel_map_valid(pConfig->playback.pChannelMap, pConfig->playback.channels)) { return MA_INVALID_ARGS; } } pDevice->pContext = pContext; /* Set the user data and log callback ASAP to ensure it is available for the entire initialization process. */ pDevice->pUserData = pConfig->pUserData; pDevice->onData = pConfig->dataCallback; pDevice->onNotification = pConfig->notificationCallback; pDevice->onStop = pConfig->stopCallback; if (pConfig->playback.pDeviceID != NULL) { MA_COPY_MEMORY(&pDevice->playback.id, pConfig->playback.pDeviceID, sizeof(pDevice->playback.id)); pDevice->playback.pID = &pDevice->playback.id; } else { pDevice->playback.pID = NULL; } if (pConfig->capture.pDeviceID != NULL) { MA_COPY_MEMORY(&pDevice->capture.id, pConfig->capture.pDeviceID, sizeof(pDevice->capture.id)); pDevice->capture.pID = &pDevice->capture.id; } else { pDevice->capture.pID = NULL; } pDevice->noPreSilencedOutputBuffer = pConfig->noPreSilencedOutputBuffer; pDevice->noClip = pConfig->noClip; pDevice->noDisableDenormals = pConfig->noDisableDenormals; pDevice->noFixedSizedCallback = pConfig->noFixedSizedCallback; pDevice->masterVolumeFactor = 1; pDevice->type = pConfig->deviceType; pDevice->sampleRate = pConfig->sampleRate; pDevice->resampling.algorithm = pConfig->resampling.algorithm; pDevice->resampling.linear.lpfOrder = pConfig->resampling.linear.lpfOrder; pDevice->resampling.pBackendVTable = pConfig->resampling.pBackendVTable; pDevice->resampling.pBackendUserData = pConfig->resampling.pBackendUserData; pDevice->capture.shareMode = pConfig->capture.shareMode; pDevice->capture.format = pConfig->capture.format; pDevice->capture.channels = pConfig->capture.channels; ma_channel_map_copy_or_default(pDevice->capture.channelMap, ma_countof(pDevice->capture.channelMap), pConfig->capture.pChannelMap, pConfig->capture.channels); pDevice->capture.channelMixMode = pConfig->capture.channelMixMode; pDevice->capture.calculateLFEFromSpatialChannels = pConfig->capture.calculateLFEFromSpatialChannels; pDevice->playback.shareMode = pConfig->playback.shareMode; pDevice->playback.format = pConfig->playback.format; pDevice->playback.channels = pConfig->playback.channels; ma_channel_map_copy_or_default(pDevice->playback.channelMap, ma_countof(pDevice->playback.channelMap), pConfig->playback.pChannelMap, pConfig->playback.channels); pDevice->playback.channelMixMode = pConfig->playback.channelMixMode; pDevice->playback.calculateLFEFromSpatialChannels = pConfig->playback.calculateLFEFromSpatialChannels; result = ma_mutex_init(&pDevice->startStopLock); if (result != MA_SUCCESS) { return result; } /* When the device is started, the worker thread is the one that does the actual startup of the backend device. We use a semaphore to wait for the background thread to finish the work. The same applies for stopping the device. Each of these semaphores is released internally by the worker thread when the work is completed. The start semaphore is also used to wake up the worker thread. */ result = ma_event_init(&pDevice->wakeupEvent); if (result != MA_SUCCESS) { ma_mutex_uninit(&pDevice->startStopLock); return result; } result = ma_event_init(&pDevice->startEvent); if (result != MA_SUCCESS) { ma_event_uninit(&pDevice->wakeupEvent); ma_mutex_uninit(&pDevice->startStopLock); return result; } result = ma_event_init(&pDevice->stopEvent); if (result != MA_SUCCESS) { ma_event_uninit(&pDevice->startEvent); ma_event_uninit(&pDevice->wakeupEvent); ma_mutex_uninit(&pDevice->startStopLock); return result; } MA_ZERO_OBJECT(&descriptorPlayback); descriptorPlayback.pDeviceID = pConfig->playback.pDeviceID; descriptorPlayback.shareMode = pConfig->playback.shareMode; descriptorPlayback.format = pConfig->playback.format; descriptorPlayback.channels = pConfig->playback.channels; descriptorPlayback.sampleRate = pConfig->sampleRate; ma_channel_map_copy_or_default(descriptorPlayback.channelMap, ma_countof(descriptorPlayback.channelMap), pConfig->playback.pChannelMap, pConfig->playback.channels); descriptorPlayback.periodSizeInFrames = pConfig->periodSizeInFrames; descriptorPlayback.periodSizeInMilliseconds = pConfig->periodSizeInMilliseconds; descriptorPlayback.periodCount = pConfig->periods; if (descriptorPlayback.periodCount == 0) { descriptorPlayback.periodCount = MA_DEFAULT_PERIODS; } MA_ZERO_OBJECT(&descriptorCapture); descriptorCapture.pDeviceID = pConfig->capture.pDeviceID; descriptorCapture.shareMode = pConfig->capture.shareMode; descriptorCapture.format = pConfig->capture.format; descriptorCapture.channels = pConfig->capture.channels; descriptorCapture.sampleRate = pConfig->sampleRate; ma_channel_map_copy_or_default(descriptorCapture.channelMap, ma_countof(descriptorCapture.channelMap), pConfig->capture.pChannelMap, pConfig->capture.channels); descriptorCapture.periodSizeInFrames = pConfig->periodSizeInFrames; descriptorCapture.periodSizeInMilliseconds = pConfig->periodSizeInMilliseconds; descriptorCapture.periodCount = pConfig->periods; if (descriptorCapture.periodCount == 0) { descriptorCapture.periodCount = MA_DEFAULT_PERIODS; } result = pContext->callbacks.onDeviceInit(pDevice, pConfig, &descriptorPlayback, &descriptorCapture); if (result != MA_SUCCESS) { ma_event_uninit(&pDevice->startEvent); ma_event_uninit(&pDevice->wakeupEvent); ma_mutex_uninit(&pDevice->startStopLock); return result; } #if 0 /* On output the descriptors will contain the *actual* data format of the device. We need this to know how to convert the data between the requested format and the internal format. */ if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex || pConfig->deviceType == ma_device_type_loopback) { if (!ma_device_descriptor_is_valid(&descriptorCapture)) { ma_device_uninit(pDevice); return MA_INVALID_ARGS; } pDevice->capture.internalFormat = descriptorCapture.format; pDevice->capture.internalChannels = descriptorCapture.channels; pDevice->capture.internalSampleRate = descriptorCapture.sampleRate; ma_channel_map_copy(pDevice->capture.internalChannelMap, descriptorCapture.channelMap, descriptorCapture.channels); pDevice->capture.internalPeriodSizeInFrames = descriptorCapture.periodSizeInFrames; pDevice->capture.internalPeriods = descriptorCapture.periodCount; if (pDevice->capture.internalPeriodSizeInFrames == 0) { pDevice->capture.internalPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(descriptorCapture.periodSizeInMilliseconds, descriptorCapture.sampleRate); } } if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { if (!ma_device_descriptor_is_valid(&descriptorPlayback)) { ma_device_uninit(pDevice); return MA_INVALID_ARGS; } pDevice->playback.internalFormat = descriptorPlayback.format; pDevice->playback.internalChannels = descriptorPlayback.channels; pDevice->playback.internalSampleRate = descriptorPlayback.sampleRate; ma_channel_map_copy(pDevice->playback.internalChannelMap, descriptorPlayback.channelMap, descriptorPlayback.channels); pDevice->playback.internalPeriodSizeInFrames = descriptorPlayback.periodSizeInFrames; pDevice->playback.internalPeriods = descriptorPlayback.periodCount; if (pDevice->playback.internalPeriodSizeInFrames == 0) { pDevice->playback.internalPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(descriptorPlayback.periodSizeInMilliseconds, descriptorPlayback.sampleRate); } } /* The name of the device can be retrieved from device info. This may be temporary and replaced with a `ma_device_get_info(pDevice, deviceType)` instead. For loopback devices, we need to retrieve the name of the playback device. */ { ma_device_info deviceInfo; if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex || pConfig->deviceType == ma_device_type_loopback) { result = ma_device_get_info(pDevice, (pConfig->deviceType == ma_device_type_loopback) ? ma_device_type_playback : ma_device_type_capture, &deviceInfo); if (result == MA_SUCCESS) { ma_strncpy_s(pDevice->capture.name, sizeof(pDevice->capture.name), deviceInfo.name, (size_t)-1); } else { /* We failed to retrieve the device info. Fall back to a default name. */ if (descriptorCapture.pDeviceID == NULL) { ma_strncpy_s(pDevice->capture.name, sizeof(pDevice->capture.name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1); } else { ma_strncpy_s(pDevice->capture.name, sizeof(pDevice->capture.name), "Capture Device", (size_t)-1); } } } if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { result = ma_device_get_info(pDevice, ma_device_type_playback, &deviceInfo); if (result == MA_SUCCESS) { ma_strncpy_s(pDevice->playback.name, sizeof(pDevice->playback.name), deviceInfo.name, (size_t)-1); } else { /* We failed to retrieve the device info. Fall back to a default name. */ if (descriptorPlayback.pDeviceID == NULL) { ma_strncpy_s(pDevice->playback.name, sizeof(pDevice->playback.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1); } else { ma_strncpy_s(pDevice->playback.name, sizeof(pDevice->playback.name), "Playback Device", (size_t)-1); } } } } ma_device__post_init_setup(pDevice, pConfig->deviceType); #endif result = ma_device_post_init(pDevice, pConfig->deviceType, &descriptorPlayback, &descriptorCapture); if (result != MA_SUCCESS) { ma_device_uninit(pDevice); return result; } /* If we're using fixed sized callbacks we'll need to make use of an intermediary buffer. Needs to be done after post_init_setup() because we'll need access to the sample rate. */ if (pConfig->noFixedSizedCallback == MA_FALSE) { /* We're using a fixed sized data callback so we'll need an intermediary buffer. */ ma_uint32 intermediaryBufferCap = pConfig->periodSizeInFrames; if (intermediaryBufferCap == 0) { intermediaryBufferCap = ma_calculate_buffer_size_in_frames_from_milliseconds(pConfig->periodSizeInMilliseconds, pDevice->sampleRate); } if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex || pConfig->deviceType == ma_device_type_loopback) { ma_uint32 intermediaryBufferSizeInBytes; pDevice->capture.intermediaryBufferLen = 0; pDevice->capture.intermediaryBufferCap = intermediaryBufferCap; if (pDevice->capture.intermediaryBufferCap == 0) { pDevice->capture.intermediaryBufferCap = pDevice->capture.internalPeriodSizeInFrames; } intermediaryBufferSizeInBytes = pDevice->capture.intermediaryBufferCap * ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels); pDevice->capture.pIntermediaryBuffer = ma_malloc((size_t)intermediaryBufferSizeInBytes, &pContext->allocationCallbacks); if (pDevice->capture.pIntermediaryBuffer == NULL) { ma_device_uninit(pDevice); return MA_OUT_OF_MEMORY; } /* Silence the buffer for safety. */ ma_silence_pcm_frames(pDevice->capture.pIntermediaryBuffer, pDevice->capture.intermediaryBufferCap, pDevice->capture.format, pDevice->capture.channels); pDevice->capture.intermediaryBufferLen = pDevice->capture.intermediaryBufferCap; } if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { ma_uint64 intermediaryBufferSizeInBytes; pDevice->playback.intermediaryBufferLen = 0; if (pConfig->deviceType == ma_device_type_duplex) { pDevice->playback.intermediaryBufferCap = pDevice->capture.intermediaryBufferCap; /* In duplex mode, make sure the intermediary buffer is always the same size as the capture side. */ } else { pDevice->playback.intermediaryBufferCap = intermediaryBufferCap; if (pDevice->playback.intermediaryBufferCap == 0) { pDevice->playback.intermediaryBufferCap = pDevice->playback.internalPeriodSizeInFrames; } } intermediaryBufferSizeInBytes = pDevice->playback.intermediaryBufferCap * ma_get_bytes_per_frame(pDevice->playback.format, pDevice->playback.channels); pDevice->playback.pIntermediaryBuffer = ma_malloc((size_t)intermediaryBufferSizeInBytes, &pContext->allocationCallbacks); if (pDevice->playback.pIntermediaryBuffer == NULL) { ma_device_uninit(pDevice); return MA_OUT_OF_MEMORY; } /* Silence the buffer for safety. */ ma_silence_pcm_frames(pDevice->playback.pIntermediaryBuffer, pDevice->playback.intermediaryBufferCap, pDevice->playback.format, pDevice->playback.channels); pDevice->playback.intermediaryBufferLen = 0; } } else { /* Not using a fixed sized data callback so no need for an intermediary buffer. */ } /* Some backends don't require the worker thread. */ if (!ma_context_is_backend_asynchronous(pContext)) { /* The worker thread. */ result = ma_thread_create(&pDevice->thread, pContext->threadPriority, pContext->threadStackSize, ma_worker_thread, pDevice, &pContext->allocationCallbacks); if (result != MA_SUCCESS) { ma_device_uninit(pDevice); return result; } /* Wait for the worker thread to put the device into it's stopped state for real. */ ma_event_wait(&pDevice->stopEvent); MA_ASSERT(ma_device_get_state(pDevice) == ma_device_state_stopped); } else { /* If the backend is asynchronous and the device is duplex, we'll need an intermediary ring buffer. Note that this needs to be done after ma_device__post_init_setup(). */ if (ma_context_is_backend_asynchronous(pContext)) { if (pConfig->deviceType == ma_device_type_duplex) { result = ma_duplex_rb_init(pDevice->capture.format, pDevice->capture.channels, pDevice->sampleRate, pDevice->capture.internalSampleRate, pDevice->capture.internalPeriodSizeInFrames, &pDevice->pContext->allocationCallbacks, &pDevice->duplexRB); if (result != MA_SUCCESS) { ma_device_uninit(pDevice); return result; } } } ma_device__set_state(pDevice, ma_device_state_stopped); } /* Log device information. */ { ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[%s]\n", ma_get_backend_name(pDevice->pContext->backend)); if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex || pDevice->type == ma_device_type_loopback) { char name[MA_MAX_DEVICE_NAME_LENGTH + 1]; ma_device_get_name(pDevice, (pDevice->type == ma_device_type_loopback) ? ma_device_type_playback : ma_device_type_capture, name, sizeof(name), NULL); ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " %s (%s)\n", name, "Capture"); ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Format: %s -> %s\n", ma_get_format_name(pDevice->capture.internalFormat), ma_get_format_name(pDevice->capture.format)); ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Channels: %d -> %d\n", pDevice->capture.internalChannels, pDevice->capture.channels); ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Sample Rate: %d -> %d\n", pDevice->capture.internalSampleRate, pDevice->sampleRate); ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Buffer Size: %d*%d (%d)\n", pDevice->capture.internalPeriodSizeInFrames, pDevice->capture.internalPeriods, (pDevice->capture.internalPeriodSizeInFrames * pDevice->capture.internalPeriods)); ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Conversion:\n"); ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Pre Format Conversion: %s\n", pDevice->capture.converter.hasPreFormatConversion ? "YES" : "NO"); ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Post Format Conversion: %s\n", pDevice->capture.converter.hasPostFormatConversion ? "YES" : "NO"); ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Channel Routing: %s\n", pDevice->capture.converter.hasChannelConverter ? "YES" : "NO"); ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Resampling: %s\n", pDevice->capture.converter.hasResampler ? "YES" : "NO"); ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Passthrough: %s\n", pDevice->capture.converter.isPassthrough ? "YES" : "NO"); { char channelMapStr[1024]; ma_channel_map_to_string(pDevice->capture.internalChannelMap, pDevice->capture.internalChannels, channelMapStr, sizeof(channelMapStr)); ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Channel Map In: {%s}\n", channelMapStr); ma_channel_map_to_string(pDevice->capture.channelMap, pDevice->capture.channels, channelMapStr, sizeof(channelMapStr)); ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Channel Map Out: {%s}\n", channelMapStr); } } if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { char name[MA_MAX_DEVICE_NAME_LENGTH + 1]; ma_device_get_name(pDevice, ma_device_type_playback, name, sizeof(name), NULL); ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " %s (%s)\n", name, "Playback"); ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Format: %s -> %s\n", ma_get_format_name(pDevice->playback.format), ma_get_format_name(pDevice->playback.internalFormat)); ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Channels: %d -> %d\n", pDevice->playback.channels, pDevice->playback.internalChannels); ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Sample Rate: %d -> %d\n", pDevice->sampleRate, pDevice->playback.internalSampleRate); ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Buffer Size: %d*%d (%d)\n", pDevice->playback.internalPeriodSizeInFrames, pDevice->playback.internalPeriods, (pDevice->playback.internalPeriodSizeInFrames * pDevice->playback.internalPeriods)); ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Conversion:\n"); ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Pre Format Conversion: %s\n", pDevice->playback.converter.hasPreFormatConversion ? "YES" : "NO"); ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Post Format Conversion: %s\n", pDevice->playback.converter.hasPostFormatConversion ? "YES" : "NO"); ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Channel Routing: %s\n", pDevice->playback.converter.hasChannelConverter ? "YES" : "NO"); ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Resampling: %s\n", pDevice->playback.converter.hasResampler ? "YES" : "NO"); ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Passthrough: %s\n", pDevice->playback.converter.isPassthrough ? "YES" : "NO"); { char channelMapStr[1024]; ma_channel_map_to_string(pDevice->playback.channelMap, pDevice->playback.channels, channelMapStr, sizeof(channelMapStr)); ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Channel Map In: {%s}\n", channelMapStr); ma_channel_map_to_string(pDevice->playback.internalChannelMap, pDevice->playback.internalChannels, channelMapStr, sizeof(channelMapStr)); ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Channel Map Out: {%s}\n", channelMapStr); } } } MA_ASSERT(ma_device_get_state(pDevice) == ma_device_state_stopped); return MA_SUCCESS; } MA_API ma_result ma_device_init_ex(const ma_backend backends[], ma_uint32 backendCount, const ma_context_config* pContextConfig, const ma_device_config* pConfig, ma_device* pDevice) { ma_result result; ma_context* pContext; ma_backend defaultBackends[ma_backend_null+1]; ma_uint32 iBackend; ma_backend* pBackendsToIterate; ma_uint32 backendsToIterateCount; ma_allocation_callbacks allocationCallbacks; if (pConfig == NULL) { return MA_INVALID_ARGS; } if (pContextConfig != NULL) { result = ma_allocation_callbacks_init_copy(&allocationCallbacks, &pContextConfig->allocationCallbacks); if (result != MA_SUCCESS) { return result; } } else { allocationCallbacks = ma_allocation_callbacks_init_default(); } pContext = (ma_context*)ma_malloc(sizeof(*pContext), &allocationCallbacks); if (pContext == NULL) { return MA_OUT_OF_MEMORY; } for (iBackend = 0; iBackend <= ma_backend_null; ++iBackend) { defaultBackends[iBackend] = (ma_backend)iBackend; } pBackendsToIterate = (ma_backend*)backends; backendsToIterateCount = backendCount; if (pBackendsToIterate == NULL) { pBackendsToIterate = (ma_backend*)defaultBackends; backendsToIterateCount = ma_countof(defaultBackends); } result = MA_NO_BACKEND; for (iBackend = 0; iBackend < backendsToIterateCount; ++iBackend) { /* This is a hack for iOS. If the context config is null, there's a good chance the `ma_device_init(NULL, &deviceConfig, pDevice);` pattern is being used. In this case, set the session category based on the device type. */ #if defined(MA_APPLE_MOBILE) ma_context_config contextConfig; if (pContextConfig == NULL) { contextConfig = ma_context_config_init(); switch (pConfig->deviceType) { case ma_device_type_duplex: { contextConfig.coreaudio.sessionCategory = ma_ios_session_category_play_and_record; } break; case ma_device_type_capture: { contextConfig.coreaudio.sessionCategory = ma_ios_session_category_record; } break; case ma_device_type_playback: default: { contextConfig.coreaudio.sessionCategory = ma_ios_session_category_playback; } break; } pContextConfig = &contextConfig; } #endif result = ma_context_init(&pBackendsToIterate[iBackend], 1, pContextConfig, pContext); if (result == MA_SUCCESS) { result = ma_device_init(pContext, pConfig, pDevice); if (result == MA_SUCCESS) { break; /* Success. */ } else { ma_context_uninit(pContext); /* Failure. */ } } } if (result != MA_SUCCESS) { ma_free(pContext, &allocationCallbacks); return result; } pDevice->isOwnerOfContext = MA_TRUE; return result; } MA_API void ma_device_uninit(ma_device* pDevice) { if (!ma_device__is_initialized(pDevice)) { return; } /* Make sure the device is stopped first. The backends will probably handle this naturally, but I like to do it explicitly for my own sanity. */ if (ma_device_is_started(pDevice)) { ma_device_stop(pDevice); } /* Putting the device into an uninitialized state will make the worker thread return. */ ma_device__set_state(pDevice, ma_device_state_uninitialized); /* Wake up the worker thread and wait for it to properly terminate. */ if (!ma_context_is_backend_asynchronous(pDevice->pContext)) { ma_event_signal(&pDevice->wakeupEvent); ma_thread_wait(&pDevice->thread); } if (pDevice->pContext->callbacks.onDeviceUninit != NULL) { pDevice->pContext->callbacks.onDeviceUninit(pDevice); } ma_event_uninit(&pDevice->stopEvent); ma_event_uninit(&pDevice->startEvent); ma_event_uninit(&pDevice->wakeupEvent); ma_mutex_uninit(&pDevice->startStopLock); if (ma_context_is_backend_asynchronous(pDevice->pContext)) { if (pDevice->type == ma_device_type_duplex) { ma_duplex_rb_uninit(&pDevice->duplexRB); } } if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex || pDevice->type == ma_device_type_loopback) { ma_data_converter_uninit(&pDevice->capture.converter, &pDevice->pContext->allocationCallbacks); } if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { ma_data_converter_uninit(&pDevice->playback.converter, &pDevice->pContext->allocationCallbacks); } if (pDevice->playback.pInputCache != NULL) { ma_free(pDevice->playback.pInputCache, &pDevice->pContext->allocationCallbacks); } if (pDevice->capture.pIntermediaryBuffer != NULL) { ma_free(pDevice->capture.pIntermediaryBuffer, &pDevice->pContext->allocationCallbacks); } if (pDevice->playback.pIntermediaryBuffer != NULL) { ma_free(pDevice->playback.pIntermediaryBuffer, &pDevice->pContext->allocationCallbacks); } if (pDevice->isOwnerOfContext) { ma_allocation_callbacks allocationCallbacks = pDevice->pContext->allocationCallbacks; ma_context_uninit(pDevice->pContext); ma_free(pDevice->pContext, &allocationCallbacks); } MA_ZERO_OBJECT(pDevice); } MA_API ma_context* ma_device_get_context(ma_device* pDevice) { if (pDevice == NULL) { return NULL; } return pDevice->pContext; } MA_API ma_log* ma_device_get_log(ma_device* pDevice) { return ma_context_get_log(ma_device_get_context(pDevice)); } MA_API ma_result ma_device_get_info(ma_device* pDevice, ma_device_type type, ma_device_info* pDeviceInfo) { if (pDeviceInfo == NULL) { return MA_INVALID_ARGS; } MA_ZERO_OBJECT(pDeviceInfo); if (pDevice == NULL) { return MA_INVALID_ARGS; } /* If the onDeviceGetInfo() callback is set, use that. Otherwise we'll fall back to ma_context_get_device_info(). */ if (pDevice->pContext->callbacks.onDeviceGetInfo != NULL) { return pDevice->pContext->callbacks.onDeviceGetInfo(pDevice, type, pDeviceInfo); } /* Getting here means onDeviceGetInfo is not implemented so we need to fall back to an alternative. */ if (type == ma_device_type_playback) { return ma_context_get_device_info(pDevice->pContext, type, pDevice->playback.pID, pDeviceInfo); } else { return ma_context_get_device_info(pDevice->pContext, type, pDevice->capture.pID, pDeviceInfo); } } MA_API ma_result ma_device_get_name(ma_device* pDevice, ma_device_type type, char* pName, size_t nameCap, size_t* pLengthNotIncludingNullTerminator) { ma_result result; ma_device_info deviceInfo; if (pLengthNotIncludingNullTerminator != NULL) { *pLengthNotIncludingNullTerminator = 0; } if (pName != NULL && nameCap > 0) { pName[0] = '\0'; } result = ma_device_get_info(pDevice, type, &deviceInfo); if (result != MA_SUCCESS) { return result; } if (pName != NULL) { ma_strncpy_s(pName, nameCap, deviceInfo.name, (size_t)-1); /* For safety, make sure the length is based on the truncated output string rather than the source. Otherwise the caller might assume the output buffer contains more content than it actually does. */ if (pLengthNotIncludingNullTerminator != NULL) { *pLengthNotIncludingNullTerminator = strlen(pName); } } else { /* Name not specified. Just report the length of the source string. */ if (pLengthNotIncludingNullTerminator != NULL) { *pLengthNotIncludingNullTerminator = strlen(deviceInfo.name); } } return MA_SUCCESS; } MA_API ma_result ma_device_start(ma_device* pDevice) { ma_result result; if (pDevice == NULL) { return MA_INVALID_ARGS; } if (ma_device_get_state(pDevice) == ma_device_state_uninitialized) { return MA_INVALID_OPERATION; /* Not initialized. */ } if (ma_device_get_state(pDevice) == ma_device_state_started) { return MA_SUCCESS; /* Already started. */ } ma_mutex_lock(&pDevice->startStopLock); { /* Starting and stopping are wrapped in a mutex which means we can assert that the device is in a stopped or paused state. */ MA_ASSERT(ma_device_get_state(pDevice) == ma_device_state_stopped); ma_device__set_state(pDevice, ma_device_state_starting); /* Asynchronous backends need to be handled differently. */ if (ma_context_is_backend_asynchronous(pDevice->pContext)) { if (pDevice->pContext->callbacks.onDeviceStart != NULL) { result = pDevice->pContext->callbacks.onDeviceStart(pDevice); } else { result = MA_INVALID_OPERATION; } if (result == MA_SUCCESS) { ma_device__set_state(pDevice, ma_device_state_started); ma_device__on_notification_started(pDevice); } } else { /* Synchronous backends are started by signaling an event that's being waited on in the worker thread. We first wake up the thread and then wait for the start event. */ ma_event_signal(&pDevice->wakeupEvent); /* Wait for the worker thread to finish starting the device. Note that the worker thread will be the one who puts the device into the started state. Don't call ma_device__set_state() here. */ ma_event_wait(&pDevice->startEvent); result = pDevice->workResult; } /* We changed the state from stopped to started, so if we failed, make sure we put the state back to stopped. */ if (result != MA_SUCCESS) { ma_device__set_state(pDevice, ma_device_state_stopped); } } ma_mutex_unlock(&pDevice->startStopLock); return result; } MA_API ma_result ma_device_stop(ma_device* pDevice) { ma_result result; if (pDevice == NULL) { return MA_INVALID_ARGS; } if (ma_device_get_state(pDevice) == ma_device_state_uninitialized) { return MA_INVALID_OPERATION; /* Not initialized. */ } if (ma_device_get_state(pDevice) == ma_device_state_stopped) { return MA_SUCCESS; /* Already stopped. */ } ma_mutex_lock(&pDevice->startStopLock); { /* Starting and stopping are wrapped in a mutex which means we can assert that the device is in a started or paused state. */ MA_ASSERT(ma_device_get_state(pDevice) == ma_device_state_started); ma_device__set_state(pDevice, ma_device_state_stopping); /* Asynchronous backends need to be handled differently. */ if (ma_context_is_backend_asynchronous(pDevice->pContext)) { /* Asynchronous backends must have a stop operation. */ if (pDevice->pContext->callbacks.onDeviceStop != NULL) { result = pDevice->pContext->callbacks.onDeviceStop(pDevice); } else { result = MA_INVALID_OPERATION; } ma_device__set_state(pDevice, ma_device_state_stopped); } else { /* Synchronous backends. The stop callback is always called from the worker thread. Do not call the stop callback here. If the backend is implementing it's own audio thread loop we'll need to wake it up if required. Note that we need to make sure the state of the device is *not* playing right now, which it shouldn't be since we set it above. This is super important though, so I'm asserting it here as well for extra safety in case we accidentally change something later. */ MA_ASSERT(ma_device_get_state(pDevice) != ma_device_state_started); if (pDevice->pContext->callbacks.onDeviceDataLoopWakeup != NULL) { pDevice->pContext->callbacks.onDeviceDataLoopWakeup(pDevice); } /* We need to wait for the worker thread to become available for work before returning. Note that the worker thread will be the one who puts the device into the stopped state. Don't call ma_device__set_state() here. */ ma_event_wait(&pDevice->stopEvent); result = MA_SUCCESS; } /* This is a safety measure to ensure the internal buffer has been cleared so any leftover does not get played the next time the device starts. Ideally this should be drained by the backend first. */ pDevice->playback.intermediaryBufferLen = 0; pDevice->playback.inputCacheConsumed = 0; pDevice->playback.inputCacheRemaining = 0; } ma_mutex_unlock(&pDevice->startStopLock); return result; } MA_API ma_bool32 ma_device_is_started(const ma_device* pDevice) { return ma_device_get_state(pDevice) == ma_device_state_started; } MA_API ma_device_state ma_device_get_state(const ma_device* pDevice) { if (pDevice == NULL) { return ma_device_state_uninitialized; } return (ma_device_state)c89atomic_load_i32((ma_int32*)&pDevice->state); /* Naughty cast to get rid of a const warning. */ } MA_API ma_result ma_device_set_master_volume(ma_device* pDevice, float volume) { if (pDevice == NULL) { return MA_INVALID_ARGS; } if (volume < 0.0f) { return MA_INVALID_ARGS; } c89atomic_exchange_f32(&pDevice->masterVolumeFactor, volume); return MA_SUCCESS; } MA_API ma_result ma_device_get_master_volume(ma_device* pDevice, float* pVolume) { if (pVolume == NULL) { return MA_INVALID_ARGS; } if (pDevice == NULL) { *pVolume = 0; return MA_INVALID_ARGS; } *pVolume = c89atomic_load_f32(&pDevice->masterVolumeFactor); return MA_SUCCESS; } MA_API ma_result ma_device_set_master_volume_db(ma_device* pDevice, float gainDB) { if (gainDB > 0) { return MA_INVALID_ARGS; } return ma_device_set_master_volume(pDevice, ma_volume_db_to_linear(gainDB)); } MA_API ma_result ma_device_get_master_volume_db(ma_device* pDevice, float* pGainDB) { float factor; ma_result result; if (pGainDB == NULL) { return MA_INVALID_ARGS; } result = ma_device_get_master_volume(pDevice, &factor); if (result != MA_SUCCESS) { *pGainDB = 0; return result; } *pGainDB = ma_volume_linear_to_db(factor); return MA_SUCCESS; } MA_API ma_result ma_device_handle_backend_data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount) { if (pDevice == NULL) { return MA_INVALID_ARGS; } if (pOutput == NULL && pInput == NULL) { return MA_INVALID_ARGS; } if (pDevice->type == ma_device_type_duplex) { if (pInput != NULL) { ma_device__handle_duplex_callback_capture(pDevice, frameCount, pInput, &pDevice->duplexRB.rb); } if (pOutput != NULL) { ma_device__handle_duplex_callback_playback(pDevice, frameCount, pOutput, &pDevice->duplexRB.rb); } } else { if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_loopback) { if (pInput == NULL) { return MA_INVALID_ARGS; } ma_device__send_frames_to_client(pDevice, frameCount, pInput); } if (pDevice->type == ma_device_type_playback) { if (pOutput == NULL) { return MA_INVALID_ARGS; } ma_device__read_frames_from_client(pDevice, frameCount, pOutput); } } return MA_SUCCESS; } MA_API ma_uint32 ma_calculate_buffer_size_in_frames_from_descriptor(const ma_device_descriptor* pDescriptor, ma_uint32 nativeSampleRate, ma_performance_profile performanceProfile) { if (pDescriptor == NULL) { return 0; } /* We must have a non-0 native sample rate, but some backends don't allow retrieval of this at the time when the size of the buffer needs to be determined. In this case we need to just take a best guess and move on. We'll try using the sample rate in pDescriptor first. If that's not set we'll just fall back to MA_DEFAULT_SAMPLE_RATE. */ if (nativeSampleRate == 0) { nativeSampleRate = pDescriptor->sampleRate; } if (nativeSampleRate == 0) { nativeSampleRate = MA_DEFAULT_SAMPLE_RATE; } MA_ASSERT(nativeSampleRate != 0); if (pDescriptor->periodSizeInFrames == 0) { if (pDescriptor->periodSizeInMilliseconds == 0) { if (performanceProfile == ma_performance_profile_low_latency) { return ma_calculate_buffer_size_in_frames_from_milliseconds(MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_LOW_LATENCY, nativeSampleRate); } else { return ma_calculate_buffer_size_in_frames_from_milliseconds(MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_CONSERVATIVE, nativeSampleRate); } } else { return ma_calculate_buffer_size_in_frames_from_milliseconds(pDescriptor->periodSizeInMilliseconds, nativeSampleRate); } } else { return pDescriptor->periodSizeInFrames; } } #endif /* MA_NO_DEVICE_IO */ MA_API ma_uint32 ma_calculate_buffer_size_in_milliseconds_from_frames(ma_uint32 bufferSizeInFrames, ma_uint32 sampleRate) { /* Prevent a division by zero. */ if (sampleRate == 0) { return 0; } return bufferSizeInFrames*1000 / sampleRate; } MA_API ma_uint32 ma_calculate_buffer_size_in_frames_from_milliseconds(ma_uint32 bufferSizeInMilliseconds, ma_uint32 sampleRate) { /* Prevent a division by zero. */ if (sampleRate == 0) { return 0; } return bufferSizeInMilliseconds*sampleRate / 1000; } MA_API void ma_copy_pcm_frames(void* dst, const void* src, ma_uint64 frameCount, ma_format format, ma_uint32 channels) { if (dst == src) { return; /* No-op. */ } ma_copy_memory_64(dst, src, frameCount * ma_get_bytes_per_frame(format, channels)); } MA_API void ma_silence_pcm_frames(void* p, ma_uint64 frameCount, ma_format format, ma_uint32 channels) { if (format == ma_format_u8) { ma_uint64 sampleCount = frameCount * channels; ma_uint64 iSample; for (iSample = 0; iSample < sampleCount; iSample += 1) { ((ma_uint8*)p)[iSample] = 128; } } else { ma_zero_memory_64(p, frameCount * ma_get_bytes_per_frame(format, channels)); } } MA_API void* ma_offset_pcm_frames_ptr(void* p, ma_uint64 offsetInFrames, ma_format format, ma_uint32 channels) { return ma_offset_ptr(p, offsetInFrames * ma_get_bytes_per_frame(format, channels)); } MA_API const void* ma_offset_pcm_frames_const_ptr(const void* p, ma_uint64 offsetInFrames, ma_format format, ma_uint32 channels) { return ma_offset_ptr(p, offsetInFrames * ma_get_bytes_per_frame(format, channels)); } MA_API void ma_clip_samples_u8(ma_uint8* pDst, const ma_int16* pSrc, ma_uint64 count) { ma_uint64 iSample; MA_ASSERT(pDst != NULL); MA_ASSERT(pSrc != NULL); for (iSample = 0; iSample < count; iSample += 1) { pDst[iSample] = ma_clip_u8(pSrc[iSample]); } } MA_API void ma_clip_samples_s16(ma_int16* pDst, const ma_int32* pSrc, ma_uint64 count) { ma_uint64 iSample; MA_ASSERT(pDst != NULL); MA_ASSERT(pSrc != NULL); for (iSample = 0; iSample < count; iSample += 1) { pDst[iSample] = ma_clip_s16(pSrc[iSample]); } } MA_API void ma_clip_samples_s24(ma_uint8* pDst, const ma_int64* pSrc, ma_uint64 count) { ma_uint64 iSample; MA_ASSERT(pDst != NULL); MA_ASSERT(pSrc != NULL); for (iSample = 0; iSample < count; iSample += 1) { ma_int64 s = ma_clip_s24(pSrc[iSample]); pDst[iSample*3 + 0] = (ma_uint8)((s & 0x000000FF) >> 0); pDst[iSample*3 + 1] = (ma_uint8)((s & 0x0000FF00) >> 8); pDst[iSample*3 + 2] = (ma_uint8)((s & 0x00FF0000) >> 16); } } MA_API void ma_clip_samples_s32(ma_int32* pDst, const ma_int64* pSrc, ma_uint64 count) { ma_uint64 iSample; MA_ASSERT(pDst != NULL); MA_ASSERT(pSrc != NULL); for (iSample = 0; iSample < count; iSample += 1) { pDst[iSample] = ma_clip_s32(pSrc[iSample]); } } MA_API void ma_clip_samples_f32(float* pDst, const float* pSrc, ma_uint64 count) { ma_uint64 iSample; MA_ASSERT(pDst != NULL); MA_ASSERT(pSrc != NULL); for (iSample = 0; iSample < count; iSample += 1) { pDst[iSample] = ma_clip_f32(pSrc[iSample]); } } MA_API void ma_clip_pcm_frames(void* pDst, const void* pSrc, ma_uint64 frameCount, ma_format format, ma_uint32 channels) { ma_uint64 sampleCount; MA_ASSERT(pDst != NULL); MA_ASSERT(pSrc != NULL); sampleCount = frameCount * channels; switch (format) { case ma_format_u8: ma_clip_samples_u8( (ma_uint8*)pDst, (const ma_int16*)pSrc, sampleCount); break; case ma_format_s16: ma_clip_samples_s16((ma_int16*)pDst, (const ma_int32*)pSrc, sampleCount); break; case ma_format_s24: ma_clip_samples_s24((ma_uint8*)pDst, (const ma_int64*)pSrc, sampleCount); break; case ma_format_s32: ma_clip_samples_s32((ma_int32*)pDst, (const ma_int64*)pSrc, sampleCount); break; case ma_format_f32: ma_clip_samples_f32(( float*)pDst, (const float*)pSrc, sampleCount); break; /* Do nothing if we don't know the format. We're including these here to silence a compiler warning about enums not being handled by the switch. */ case ma_format_unknown: case ma_format_count: break; } } MA_API void ma_copy_and_apply_volume_factor_u8(ma_uint8* pSamplesOut, const ma_uint8* pSamplesIn, ma_uint64 sampleCount, float factor) { ma_uint64 iSample; if (pSamplesOut == NULL || pSamplesIn == NULL) { return; } for (iSample = 0; iSample < sampleCount; iSample += 1) { pSamplesOut[iSample] = (ma_uint8)(pSamplesIn[iSample] * factor); } } MA_API void ma_copy_and_apply_volume_factor_s16(ma_int16* pSamplesOut, const ma_int16* pSamplesIn, ma_uint64 sampleCount, float factor) { ma_uint64 iSample; if (pSamplesOut == NULL || pSamplesIn == NULL) { return; } for (iSample = 0; iSample < sampleCount; iSample += 1) { pSamplesOut[iSample] = (ma_int16)(pSamplesIn[iSample] * factor); } } MA_API void ma_copy_and_apply_volume_factor_s24(void* pSamplesOut, const void* pSamplesIn, ma_uint64 sampleCount, float factor) { ma_uint64 iSample; ma_uint8* pSamplesOut8; ma_uint8* pSamplesIn8; if (pSamplesOut == NULL || pSamplesIn == NULL) { return; } pSamplesOut8 = (ma_uint8*)pSamplesOut; pSamplesIn8 = (ma_uint8*)pSamplesIn; for (iSample = 0; iSample < sampleCount; iSample += 1) { ma_int32 sampleS32; sampleS32 = (ma_int32)(((ma_uint32)(pSamplesIn8[iSample*3+0]) << 8) | ((ma_uint32)(pSamplesIn8[iSample*3+1]) << 16) | ((ma_uint32)(pSamplesIn8[iSample*3+2])) << 24); sampleS32 = (ma_int32)(sampleS32 * factor); pSamplesOut8[iSample*3+0] = (ma_uint8)(((ma_uint32)sampleS32 & 0x0000FF00) >> 8); pSamplesOut8[iSample*3+1] = (ma_uint8)(((ma_uint32)sampleS32 & 0x00FF0000) >> 16); pSamplesOut8[iSample*3+2] = (ma_uint8)(((ma_uint32)sampleS32 & 0xFF000000) >> 24); } } MA_API void ma_copy_and_apply_volume_factor_s32(ma_int32* pSamplesOut, const ma_int32* pSamplesIn, ma_uint64 sampleCount, float factor) { ma_uint64 iSample; if (pSamplesOut == NULL || pSamplesIn == NULL) { return; } for (iSample = 0; iSample < sampleCount; iSample += 1) { pSamplesOut[iSample] = (ma_int32)(pSamplesIn[iSample] * factor); } } MA_API void ma_copy_and_apply_volume_factor_f32(float* pSamplesOut, const float* pSamplesIn, ma_uint64 sampleCount, float factor) { ma_uint64 iSample; if (pSamplesOut == NULL || pSamplesIn == NULL) { return; } if (factor == 1) { if (pSamplesOut == pSamplesIn) { /* In place. No-op. */ } else { /* Just a copy. */ for (iSample = 0; iSample < sampleCount; iSample += 1) { pSamplesOut[iSample] = pSamplesIn[iSample]; } } } else { for (iSample = 0; iSample < sampleCount; iSample += 1) { pSamplesOut[iSample] = pSamplesIn[iSample] * factor; } } } MA_API void ma_apply_volume_factor_u8(ma_uint8* pSamples, ma_uint64 sampleCount, float factor) { ma_copy_and_apply_volume_factor_u8(pSamples, pSamples, sampleCount, factor); } MA_API void ma_apply_volume_factor_s16(ma_int16* pSamples, ma_uint64 sampleCount, float factor) { ma_copy_and_apply_volume_factor_s16(pSamples, pSamples, sampleCount, factor); } MA_API void ma_apply_volume_factor_s24(void* pSamples, ma_uint64 sampleCount, float factor) { ma_copy_and_apply_volume_factor_s24(pSamples, pSamples, sampleCount, factor); } MA_API void ma_apply_volume_factor_s32(ma_int32* pSamples, ma_uint64 sampleCount, float factor) { ma_copy_and_apply_volume_factor_s32(pSamples, pSamples, sampleCount, factor); } MA_API void ma_apply_volume_factor_f32(float* pSamples, ma_uint64 sampleCount, float factor) { ma_copy_and_apply_volume_factor_f32(pSamples, pSamples, sampleCount, factor); } MA_API void ma_copy_and_apply_volume_factor_pcm_frames_u8(ma_uint8* pFramesOut, const ma_uint8* pFramesIn, ma_uint64 frameCount, ma_uint32 channels, float factor) { ma_copy_and_apply_volume_factor_u8(pFramesOut, pFramesIn, frameCount*channels, factor); } MA_API void ma_copy_and_apply_volume_factor_pcm_frames_s16(ma_int16* pFramesOut, const ma_int16* pFramesIn, ma_uint64 frameCount, ma_uint32 channels, float factor) { ma_copy_and_apply_volume_factor_s16(pFramesOut, pFramesIn, frameCount*channels, factor); } MA_API void ma_copy_and_apply_volume_factor_pcm_frames_s24(void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount, ma_uint32 channels, float factor) { ma_copy_and_apply_volume_factor_s24(pFramesOut, pFramesIn, frameCount*channels, factor); } MA_API void ma_copy_and_apply_volume_factor_pcm_frames_s32(ma_int32* pFramesOut, const ma_int32* pFramesIn, ma_uint64 frameCount, ma_uint32 channels, float factor) { ma_copy_and_apply_volume_factor_s32(pFramesOut, pFramesIn, frameCount*channels, factor); } MA_API void ma_copy_and_apply_volume_factor_pcm_frames_f32(float* pFramesOut, const float* pFramesIn, ma_uint64 frameCount, ma_uint32 channels, float factor) { ma_copy_and_apply_volume_factor_f32(pFramesOut, pFramesIn, frameCount*channels, factor); } MA_API void ma_copy_and_apply_volume_factor_pcm_frames(void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount, ma_format format, ma_uint32 channels, float factor) { switch (format) { case ma_format_u8: ma_copy_and_apply_volume_factor_pcm_frames_u8 ((ma_uint8*)pFramesOut, (const ma_uint8*)pFramesIn, frameCount, channels, factor); return; case ma_format_s16: ma_copy_and_apply_volume_factor_pcm_frames_s16((ma_int16*)pFramesOut, (const ma_int16*)pFramesIn, frameCount, channels, factor); return; case ma_format_s24: ma_copy_and_apply_volume_factor_pcm_frames_s24( pFramesOut, pFramesIn, frameCount, channels, factor); return; case ma_format_s32: ma_copy_and_apply_volume_factor_pcm_frames_s32((ma_int32*)pFramesOut, (const ma_int32*)pFramesIn, frameCount, channels, factor); return; case ma_format_f32: ma_copy_and_apply_volume_factor_pcm_frames_f32( (float*)pFramesOut, (const float*)pFramesIn, frameCount, channels, factor); return; default: return; /* Do nothing. */ } } MA_API void ma_apply_volume_factor_pcm_frames_u8(ma_uint8* pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor) { ma_copy_and_apply_volume_factor_pcm_frames_u8(pFrames, pFrames, frameCount, channels, factor); } MA_API void ma_apply_volume_factor_pcm_frames_s16(ma_int16* pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor) { ma_copy_and_apply_volume_factor_pcm_frames_s16(pFrames, pFrames, frameCount, channels, factor); } MA_API void ma_apply_volume_factor_pcm_frames_s24(void* pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor) { ma_copy_and_apply_volume_factor_pcm_frames_s24(pFrames, pFrames, frameCount, channels, factor); } MA_API void ma_apply_volume_factor_pcm_frames_s32(ma_int32* pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor) { ma_copy_and_apply_volume_factor_pcm_frames_s32(pFrames, pFrames, frameCount, channels, factor); } MA_API void ma_apply_volume_factor_pcm_frames_f32(float* pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor) { ma_copy_and_apply_volume_factor_pcm_frames_f32(pFrames, pFrames, frameCount, channels, factor); } MA_API void ma_apply_volume_factor_pcm_frames(void* pFramesOut, ma_uint64 frameCount, ma_format format, ma_uint32 channels, float factor) { ma_copy_and_apply_volume_factor_pcm_frames(pFramesOut, pFramesOut, frameCount, format, channels, factor); } MA_API void ma_copy_and_apply_volume_factor_per_channel_f32(float* pFramesOut, const float* pFramesIn, ma_uint64 frameCount, ma_uint32 channels, float* pChannelGains) { ma_uint64 iFrame; if (channels == 2) { /* TODO: Do an optimized implementation for stereo and mono. Can do a SIMD optimized implementation as well. */ } for (iFrame = 0; iFrame < frameCount; iFrame += 1) { ma_uint32 iChannel; for (iChannel = 0; iChannel < channels; iChannel += 1) { pFramesOut[iFrame * channels + iChannel] = pFramesIn[iFrame * channels + iChannel] * pChannelGains[iChannel]; } } } static MA_INLINE ma_int16 ma_apply_volume_unclipped_u8(ma_int16 x, ma_int16 volume) { return (ma_int16)(((ma_int32)x * (ma_int32)volume) >> 8); } static MA_INLINE ma_int32 ma_apply_volume_unclipped_s16(ma_int32 x, ma_int16 volume) { return (ma_int32)((x * volume) >> 8); } static MA_INLINE ma_int64 ma_apply_volume_unclipped_s24(ma_int64 x, ma_int16 volume) { return (ma_int64)((x * volume) >> 8); } static MA_INLINE ma_int64 ma_apply_volume_unclipped_s32(ma_int64 x, ma_int16 volume) { return (ma_int64)((x * volume) >> 8); } static MA_INLINE float ma_apply_volume_unclipped_f32(float x, float volume) { return x * volume; } MA_API void ma_copy_and_apply_volume_and_clip_samples_u8(ma_uint8* pDst, const ma_int16* pSrc, ma_uint64 count, float volume) { ma_uint64 iSample; ma_int16 volumeFixed; MA_ASSERT(pDst != NULL); MA_ASSERT(pSrc != NULL); volumeFixed = ma_float_to_fixed_16(volume); for (iSample = 0; iSample < count; iSample += 1) { pDst[iSample] = ma_clip_u8(ma_apply_volume_unclipped_u8(pSrc[iSample], volumeFixed)); } } MA_API void ma_copy_and_apply_volume_and_clip_samples_s16(ma_int16* pDst, const ma_int32* pSrc, ma_uint64 count, float volume) { ma_uint64 iSample; ma_int16 volumeFixed; MA_ASSERT(pDst != NULL); MA_ASSERT(pSrc != NULL); volumeFixed = ma_float_to_fixed_16(volume); for (iSample = 0; iSample < count; iSample += 1) { pDst[iSample] = ma_clip_s16(ma_apply_volume_unclipped_s16(pSrc[iSample], volumeFixed)); } } MA_API void ma_copy_and_apply_volume_and_clip_samples_s24(ma_uint8* pDst, const ma_int64* pSrc, ma_uint64 count, float volume) { ma_uint64 iSample; ma_int16 volumeFixed; MA_ASSERT(pDst != NULL); MA_ASSERT(pSrc != NULL); volumeFixed = ma_float_to_fixed_16(volume); for (iSample = 0; iSample < count; iSample += 1) { ma_int64 s = ma_clip_s24(ma_apply_volume_unclipped_s24(pSrc[iSample], volumeFixed)); pDst[iSample*3 + 0] = (ma_uint8)((s & 0x000000FF) >> 0); pDst[iSample*3 + 1] = (ma_uint8)((s & 0x0000FF00) >> 8); pDst[iSample*3 + 2] = (ma_uint8)((s & 0x00FF0000) >> 16); } } MA_API void ma_copy_and_apply_volume_and_clip_samples_s32(ma_int32* pDst, const ma_int64* pSrc, ma_uint64 count, float volume) { ma_uint64 iSample; ma_int16 volumeFixed; MA_ASSERT(pDst != NULL); MA_ASSERT(pSrc != NULL); volumeFixed = ma_float_to_fixed_16(volume); for (iSample = 0; iSample < count; iSample += 1) { pDst[iSample] = ma_clip_s32(ma_apply_volume_unclipped_s32(pSrc[iSample], volumeFixed)); } } MA_API void ma_copy_and_apply_volume_and_clip_samples_f32(float* pDst, const float* pSrc, ma_uint64 count, float volume) { ma_uint64 iSample; MA_ASSERT(pDst != NULL); MA_ASSERT(pSrc != NULL); /* For the f32 case we need to make sure this supports in-place processing where the input and output buffers are the same. */ for (iSample = 0; iSample < count; iSample += 1) { pDst[iSample] = ma_clip_f32(ma_apply_volume_unclipped_f32(pSrc[iSample], volume)); } } MA_API void ma_copy_and_apply_volume_and_clip_pcm_frames(void* pDst, const void* pSrc, ma_uint64 frameCount, ma_format format, ma_uint32 channels, float volume) { MA_ASSERT(pDst != NULL); MA_ASSERT(pSrc != NULL); if (volume == 1) { ma_clip_pcm_frames(pDst, pSrc, frameCount, format, channels); /* Optimized case for volume = 1. */ } else if (volume == 0) { ma_silence_pcm_frames(pDst, frameCount, format, channels); /* Optimized case for volume = 0. */ } else { ma_uint64 sampleCount = frameCount * channels; switch (format) { case ma_format_u8: ma_copy_and_apply_volume_and_clip_samples_u8( (ma_uint8*)pDst, (const ma_int16*)pSrc, sampleCount, volume); break; case ma_format_s16: ma_copy_and_apply_volume_and_clip_samples_s16((ma_int16*)pDst, (const ma_int32*)pSrc, sampleCount, volume); break; case ma_format_s24: ma_copy_and_apply_volume_and_clip_samples_s24((ma_uint8*)pDst, (const ma_int64*)pSrc, sampleCount, volume); break; case ma_format_s32: ma_copy_and_apply_volume_and_clip_samples_s32((ma_int32*)pDst, (const ma_int64*)pSrc, sampleCount, volume); break; case ma_format_f32: ma_copy_and_apply_volume_and_clip_samples_f32(( float*)pDst, (const float*)pSrc, sampleCount, volume); break; /* Do nothing if we don't know the format. We're including these here to silence a compiler warning about enums not being handled by the switch. */ case ma_format_unknown: case ma_format_count: break; } } } MA_API float ma_volume_linear_to_db(float factor) { return 20*ma_log10f(factor); } MA_API float ma_volume_db_to_linear(float gain) { return ma_powf(10, gain/20.0f); } /************************************************************************************************************************************************************** Format Conversion **************************************************************************************************************************************************************/ static MA_INLINE ma_int16 ma_pcm_sample_f32_to_s16(float x) { return (ma_int16)(x * 32767.0f); } static MA_INLINE ma_int16 ma_pcm_sample_u8_to_s16_no_scale(ma_uint8 x) { return (ma_int16)((ma_int16)x - 128); } static MA_INLINE ma_int64 ma_pcm_sample_s24_to_s32_no_scale(const ma_uint8* x) { return (ma_int64)(((ma_uint64)x[0] << 40) | ((ma_uint64)x[1] << 48) | ((ma_uint64)x[2] << 56)) >> 40; /* Make sure the sign bits are maintained. */ } static MA_INLINE void ma_pcm_sample_s32_to_s24_no_scale(ma_int64 x, ma_uint8* s24) { s24[0] = (ma_uint8)((x & 0x000000FF) >> 0); s24[1] = (ma_uint8)((x & 0x0000FF00) >> 8); s24[2] = (ma_uint8)((x & 0x00FF0000) >> 16); } /* u8 */ MA_API void ma_pcm_u8_to_u8(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) { (void)ditherMode; ma_copy_memory_64(dst, src, count * sizeof(ma_uint8)); } static MA_INLINE void ma_pcm_u8_to_s16__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) { ma_int16* dst_s16 = (ma_int16*)dst; const ma_uint8* src_u8 = (const ma_uint8*)src; ma_uint64 i; for (i = 0; i < count; i += 1) { ma_int16 x = src_u8[i]; x = (ma_int16)(x - 128); x = (ma_int16)(x << 8); dst_s16[i] = x; } (void)ditherMode; } static MA_INLINE void ma_pcm_u8_to_s16__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) { ma_pcm_u8_to_s16__reference(dst, src, count, ditherMode); } #if defined(MA_SUPPORT_SSE2) static MA_INLINE void ma_pcm_u8_to_s16__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) { ma_pcm_u8_to_s16__optimized(dst, src, count, ditherMode); } #endif #if defined(MA_SUPPORT_AVX2) static MA_INLINE void ma_pcm_u8_to_s16__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) { ma_pcm_u8_to_s16__optimized(dst, src, count, ditherMode); } #endif #if defined(MA_SUPPORT_NEON) static MA_INLINE void ma_pcm_u8_to_s16__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) { ma_pcm_u8_to_s16__optimized(dst, src, count, ditherMode); } #endif MA_API void ma_pcm_u8_to_s16(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) { #ifdef MA_USE_REFERENCE_CONVERSION_APIS ma_pcm_u8_to_s16__reference(dst, src, count, ditherMode); #else # if MA_PREFERRED_SIMD == MA_SIMD_AVX2 if (ma_has_avx2()) { ma_pcm_u8_to_s16__avx2(dst, src, count, ditherMode); } else #elif MA_PREFERRED_SIMD == MA_SIMD_SSE2 if (ma_has_sse2()) { ma_pcm_u8_to_s16__sse2(dst, src, count, ditherMode); } else #elif MA_PREFERRED_SIMD == MA_SIMD_NEON if (ma_has_neon()) { ma_pcm_u8_to_s16__neon(dst, src, count, ditherMode); } else #endif { ma_pcm_u8_to_s16__optimized(dst, src, count, ditherMode); } #endif } static MA_INLINE void ma_pcm_u8_to_s24__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) { ma_uint8* dst_s24 = (ma_uint8*)dst; const ma_uint8* src_u8 = (const ma_uint8*)src; ma_uint64 i; for (i = 0; i < count; i += 1) { ma_int16 x = src_u8[i]; x = (ma_int16)(x - 128); dst_s24[i*3+0] = 0; dst_s24[i*3+1] = 0; dst_s24[i*3+2] = (ma_uint8)((ma_int8)x); } (void)ditherMode; } static MA_INLINE void ma_pcm_u8_to_s24__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) { ma_pcm_u8_to_s24__reference(dst, src, count, ditherMode); } #if defined(MA_SUPPORT_SSE2) static MA_INLINE void ma_pcm_u8_to_s24__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) { ma_pcm_u8_to_s24__optimized(dst, src, count, ditherMode); } #endif #if defined(MA_SUPPORT_AVX2) static MA_INLINE void ma_pcm_u8_to_s24__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) { ma_pcm_u8_to_s24__optimized(dst, src, count, ditherMode); } #endif #if defined(MA_SUPPORT_NEON) static MA_INLINE void ma_pcm_u8_to_s24__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) { ma_pcm_u8_to_s24__optimized(dst, src, count, ditherMode); } #endif MA_API void ma_pcm_u8_to_s24(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) { #ifdef MA_USE_REFERENCE_CONVERSION_APIS ma_pcm_u8_to_s24__reference(dst, src, count, ditherMode); #else # if MA_PREFERRED_SIMD == MA_SIMD_AVX2 if (ma_has_avx2()) { ma_pcm_u8_to_s24__avx2(dst, src, count, ditherMode); } else #elif MA_PREFERRED_SIMD == MA_SIMD_SSE2 if (ma_has_sse2()) { ma_pcm_u8_to_s24__sse2(dst, src, count, ditherMode); } else #elif MA_PREFERRED_SIMD == MA_SIMD_NEON if (ma_has_neon()) { ma_pcm_u8_to_s24__neon(dst, src, count, ditherMode); } else #endif { ma_pcm_u8_to_s24__optimized(dst, src, count, ditherMode); } #endif } static MA_INLINE void ma_pcm_u8_to_s32__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) { ma_int32* dst_s32 = (ma_int32*)dst; const ma_uint8* src_u8 = (const ma_uint8*)src; ma_uint64 i; for (i = 0; i < count; i += 1) { ma_int32 x = src_u8[i]; x = x - 128; x = x << 24; dst_s32[i] = x; } (void)ditherMode; } static MA_INLINE void ma_pcm_u8_to_s32__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) { ma_pcm_u8_to_s32__reference(dst, src, count, ditherMode); } #if defined(MA_SUPPORT_SSE2) static MA_INLINE void ma_pcm_u8_to_s32__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) { ma_pcm_u8_to_s32__optimized(dst, src, count, ditherMode); } #endif #if defined(MA_SUPPORT_AVX2) static MA_INLINE void ma_pcm_u8_to_s32__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) { ma_pcm_u8_to_s32__optimized(dst, src, count, ditherMode); } #endif #if defined(MA_SUPPORT_NEON) static MA_INLINE void ma_pcm_u8_to_s32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) { ma_pcm_u8_to_s32__optimized(dst, src, count, ditherMode); } #endif MA_API void ma_pcm_u8_to_s32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) { #ifdef MA_USE_REFERENCE_CONVERSION_APIS ma_pcm_u8_to_s32__reference(dst, src, count, ditherMode); #else # if MA_PREFERRED_SIMD == MA_SIMD_AVX2 if (ma_has_avx2()) { ma_pcm_u8_to_s32__avx2(dst, src, count, ditherMode); } else #elif MA_PREFERRED_SIMD == MA_SIMD_SSE2 if (ma_has_sse2()) { ma_pcm_u8_to_s32__sse2(dst, src, count, ditherMode); } else #elif MA_PREFERRED_SIMD == MA_SIMD_NEON if (ma_has_neon()) { ma_pcm_u8_to_s32__neon(dst, src, count, ditherMode); } else #endif { ma_pcm_u8_to_s32__optimized(dst, src, count, ditherMode); } #endif } static MA_INLINE void ma_pcm_u8_to_f32__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) { float* dst_f32 = (float*)dst; const ma_uint8* src_u8 = (const ma_uint8*)src; ma_uint64 i; for (i = 0; i < count; i += 1) { float x = (float)src_u8[i]; x = x * 0.00784313725490196078f; /* 0..255 to 0..2 */ x = x - 1; /* 0..2 to -1..1 */ dst_f32[i] = x; } (void)ditherMode; } static MA_INLINE void ma_pcm_u8_to_f32__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) { ma_pcm_u8_to_f32__reference(dst, src, count, ditherMode); } #if defined(MA_SUPPORT_SSE2) static MA_INLINE void ma_pcm_u8_to_f32__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) { ma_pcm_u8_to_f32__optimized(dst, src, count, ditherMode); } #endif #if defined(MA_SUPPORT_AVX2) static MA_INLINE void ma_pcm_u8_to_f32__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) { ma_pcm_u8_to_f32__optimized(dst, src, count, ditherMode); } #endif #if defined(MA_SUPPORT_NEON) static MA_INLINE void ma_pcm_u8_to_f32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) { ma_pcm_u8_to_f32__optimized(dst, src, count, ditherMode); } #endif MA_API void ma_pcm_u8_to_f32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) { #ifdef MA_USE_REFERENCE_CONVERSION_APIS ma_pcm_u8_to_f32__reference(dst, src, count, ditherMode); #else # if MA_PREFERRED_SIMD == MA_SIMD_AVX2 if (ma_has_avx2()) { ma_pcm_u8_to_f32__avx2(dst, src, count, ditherMode); } else #elif MA_PREFERRED_SIMD == MA_SIMD_SSE2 if (ma_has_sse2()) { ma_pcm_u8_to_f32__sse2(dst, src, count, ditherMode); } else #elif MA_PREFERRED_SIMD == MA_SIMD_NEON if (ma_has_neon()) { ma_pcm_u8_to_f32__neon(dst, src, count, ditherMode); } else #endif { ma_pcm_u8_to_f32__optimized(dst, src, count, ditherMode); } #endif } #ifdef MA_USE_REFERENCE_CONVERSION_APIS static MA_INLINE void ma_pcm_interleave_u8__reference(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels) { ma_uint8* dst_u8 = (ma_uint8*)dst; const ma_uint8** src_u8 = (const ma_uint8**)src; ma_uint64 iFrame; for (iFrame = 0; iFrame < frameCount; iFrame += 1) { ma_uint32 iChannel; for (iChannel = 0; iChannel < channels; iChannel += 1) { dst_u8[iFrame*channels + iChannel] = src_u8[iChannel][iFrame]; } } } #else static MA_INLINE void ma_pcm_interleave_u8__optimized(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels) { ma_uint8* dst_u8 = (ma_uint8*)dst; const ma_uint8** src_u8 = (const ma_uint8**)src; if (channels == 1) { ma_copy_memory_64(dst, src[0], frameCount * sizeof(ma_uint8)); } else if (channels == 2) { ma_uint64 iFrame; for (iFrame = 0; iFrame < frameCount; iFrame += 1) { dst_u8[iFrame*2 + 0] = src_u8[0][iFrame]; dst_u8[iFrame*2 + 1] = src_u8[1][iFrame]; } } else { ma_uint64 iFrame; for (iFrame = 0; iFrame < frameCount; iFrame += 1) { ma_uint32 iChannel; for (iChannel = 0; iChannel < channels; iChannel += 1) { dst_u8[iFrame*channels + iChannel] = src_u8[iChannel][iFrame]; } } } } #endif MA_API void ma_pcm_interleave_u8(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels) { #ifdef MA_USE_REFERENCE_CONVERSION_APIS ma_pcm_interleave_u8__reference(dst, src, frameCount, channels); #else ma_pcm_interleave_u8__optimized(dst, src, frameCount, channels); #endif } static MA_INLINE void ma_pcm_deinterleave_u8__reference(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels) { ma_uint8** dst_u8 = (ma_uint8**)dst; const ma_uint8* src_u8 = (const ma_uint8*)src; ma_uint64 iFrame; for (iFrame = 0; iFrame < frameCount; iFrame += 1) { ma_uint32 iChannel; for (iChannel = 0; iChannel < channels; iChannel += 1) { dst_u8[iChannel][iFrame] = src_u8[iFrame*channels + iChannel]; } } } static MA_INLINE void ma_pcm_deinterleave_u8__optimized(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels) { ma_pcm_deinterleave_u8__reference(dst, src, frameCount, channels); } MA_API void ma_pcm_deinterleave_u8(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels) { #ifdef MA_USE_REFERENCE_CONVERSION_APIS ma_pcm_deinterleave_u8__reference(dst, src, frameCount, channels); #else ma_pcm_deinterleave_u8__optimized(dst, src, frameCount, channels); #endif } /* s16 */ static MA_INLINE void ma_pcm_s16_to_u8__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) { ma_uint8* dst_u8 = (ma_uint8*)dst; const ma_int16* src_s16 = (const ma_int16*)src; if (ditherMode == ma_dither_mode_none) { ma_uint64 i; for (i = 0; i < count; i += 1) { ma_int16 x = src_s16[i]; x = (ma_int16)(x >> 8); x = (ma_int16)(x + 128); dst_u8[i] = (ma_uint8)x; } } else { ma_uint64 i; for (i = 0; i < count; i += 1) { ma_int16 x = src_s16[i]; /* Dither. Don't overflow. */ ma_int32 dither = ma_dither_s32(ditherMode, -0x80, 0x7F); if ((x + dither) <= 0x7FFF) { x = (ma_int16)(x + dither); } else { x = 0x7FFF; } x = (ma_int16)(x >> 8); x = (ma_int16)(x + 128); dst_u8[i] = (ma_uint8)x; } } } static MA_INLINE void ma_pcm_s16_to_u8__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) { ma_pcm_s16_to_u8__reference(dst, src, count, ditherMode); } #if defined(MA_SUPPORT_SSE2) static MA_INLINE void ma_pcm_s16_to_u8__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) { ma_pcm_s16_to_u8__optimized(dst, src, count, ditherMode); } #endif #if defined(MA_SUPPORT_AVX2) static MA_INLINE void ma_pcm_s16_to_u8__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) { ma_pcm_s16_to_u8__optimized(dst, src, count, ditherMode); } #endif #if defined(MA_SUPPORT_NEON) static MA_INLINE void ma_pcm_s16_to_u8__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) { ma_pcm_s16_to_u8__optimized(dst, src, count, ditherMode); } #endif MA_API void ma_pcm_s16_to_u8(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) { #ifdef MA_USE_REFERENCE_CONVERSION_APIS ma_pcm_s16_to_u8__reference(dst, src, count, ditherMode); #else # if MA_PREFERRED_SIMD == MA_SIMD_AVX2 if (ma_has_avx2()) { ma_pcm_s16_to_u8__avx2(dst, src, count, ditherMode); } else #elif MA_PREFERRED_SIMD == MA_SIMD_SSE2 if (ma_has_sse2()) { ma_pcm_s16_to_u8__sse2(dst, src, count, ditherMode); } else #elif MA_PREFERRED_SIMD == MA_SIMD_NEON if (ma_has_neon()) { ma_pcm_s16_to_u8__neon(dst, src, count, ditherMode); } else #endif { ma_pcm_s16_to_u8__optimized(dst, src, count, ditherMode); } #endif } MA_API void ma_pcm_s16_to_s16(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) { (void)ditherMode; ma_copy_memory_64(dst, src, count * sizeof(ma_int16)); } static MA_INLINE void ma_pcm_s16_to_s24__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) { ma_uint8* dst_s24 = (ma_uint8*)dst; const ma_int16* src_s16 = (const ma_int16*)src; ma_uint64 i; for (i = 0; i < count; i += 1) { dst_s24[i*3+0] = 0; dst_s24[i*3+1] = (ma_uint8)(src_s16[i] & 0xFF); dst_s24[i*3+2] = (ma_uint8)(src_s16[i] >> 8); } (void)ditherMode; } static MA_INLINE void ma_pcm_s16_to_s24__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) { ma_pcm_s16_to_s24__reference(dst, src, count, ditherMode); } #if defined(MA_SUPPORT_SSE2) static MA_INLINE void ma_pcm_s16_to_s24__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) { ma_pcm_s16_to_s24__optimized(dst, src, count, ditherMode); } #endif #if defined(MA_SUPPORT_AVX2) static MA_INLINE void ma_pcm_s16_to_s24__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) { ma_pcm_s16_to_s24__optimized(dst, src, count, ditherMode); } #endif #if defined(MA_SUPPORT_NEON) static MA_INLINE void ma_pcm_s16_to_s24__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) { ma_pcm_s16_to_s24__optimized(dst, src, count, ditherMode); } #endif MA_API void ma_pcm_s16_to_s24(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) { #ifdef MA_USE_REFERENCE_CONVERSION_APIS ma_pcm_s16_to_s24__reference(dst, src, count, ditherMode); #else # if MA_PREFERRED_SIMD == MA_SIMD_AVX2 if (ma_has_avx2()) { ma_pcm_s16_to_s24__avx2(dst, src, count, ditherMode); } else #elif MA_PREFERRED_SIMD == MA_SIMD_SSE2 if (ma_has_sse2()) { ma_pcm_s16_to_s24__sse2(dst, src, count, ditherMode); } else #elif MA_PREFERRED_SIMD == MA_SIMD_NEON if (ma_has_neon()) { ma_pcm_s16_to_s24__neon(dst, src, count, ditherMode); } else #endif { ma_pcm_s16_to_s24__optimized(dst, src, count, ditherMode); } #endif } static MA_INLINE void ma_pcm_s16_to_s32__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) { ma_int32* dst_s32 = (ma_int32*)dst; const ma_int16* src_s16 = (const ma_int16*)src; ma_uint64 i; for (i = 0; i < count; i += 1) { dst_s32[i] = src_s16[i] << 16; } (void)ditherMode; } static MA_INLINE void ma_pcm_s16_to_s32__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) { ma_pcm_s16_to_s32__reference(dst, src, count, ditherMode); } #if defined(MA_SUPPORT_SSE2) static MA_INLINE void ma_pcm_s16_to_s32__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) { ma_pcm_s16_to_s32__optimized(dst, src, count, ditherMode); } #endif #if defined(MA_SUPPORT_AVX2) static MA_INLINE void ma_pcm_s16_to_s32__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) { ma_pcm_s16_to_s32__optimized(dst, src, count, ditherMode); } #endif #if defined(MA_SUPPORT_NEON) static MA_INLINE void ma_pcm_s16_to_s32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) { ma_pcm_s16_to_s32__optimized(dst, src, count, ditherMode); } #endif MA_API void ma_pcm_s16_to_s32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) { #ifdef MA_USE_REFERENCE_CONVERSION_APIS ma_pcm_s16_to_s32__reference(dst, src, count, ditherMode); #else # if MA_PREFERRED_SIMD == MA_SIMD_AVX2 if (ma_has_avx2()) { ma_pcm_s16_to_s32__avx2(dst, src, count, ditherMode); } else #elif MA_PREFERRED_SIMD == MA_SIMD_SSE2 if (ma_has_sse2()) { ma_pcm_s16_to_s32__sse2(dst, src, count, ditherMode); } else #elif MA_PREFERRED_SIMD == MA_SIMD_NEON if (ma_has_neon()) { ma_pcm_s16_to_s32__neon(dst, src, count, ditherMode); } else #endif { ma_pcm_s16_to_s32__optimized(dst, src, count, ditherMode); } #endif } static MA_INLINE void ma_pcm_s16_to_f32__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) { float* dst_f32 = (float*)dst; const ma_int16* src_s16 = (const ma_int16*)src; ma_uint64 i; for (i = 0; i < count; i += 1) { float x = (float)src_s16[i]; #if 0 /* The accurate way. */ x = x + 32768.0f; /* -32768..32767 to 0..65535 */ x = x * 0.00003051804379339284f; /* 0..65535 to 0..2 */ x = x - 1; /* 0..2 to -1..1 */ #else /* The fast way. */ x = x * 0.000030517578125f; /* -32768..32767 to -1..0.999969482421875 */ #endif dst_f32[i] = x; } (void)ditherMode; } static MA_INLINE void ma_pcm_s16_to_f32__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) { ma_pcm_s16_to_f32__reference(dst, src, count, ditherMode); } #if defined(MA_SUPPORT_SSE2) static MA_INLINE void ma_pcm_s16_to_f32__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) { ma_pcm_s16_to_f32__optimized(dst, src, count, ditherMode); } #endif #if defined(MA_SUPPORT_AVX2) static MA_INLINE void ma_pcm_s16_to_f32__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) { ma_pcm_s16_to_f32__optimized(dst, src, count, ditherMode); } #endif #if defined(MA_SUPPORT_NEON) static MA_INLINE void ma_pcm_s16_to_f32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) { ma_pcm_s16_to_f32__optimized(dst, src, count, ditherMode); } #endif MA_API void ma_pcm_s16_to_f32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) { #ifdef MA_USE_REFERENCE_CONVERSION_APIS ma_pcm_s16_to_f32__reference(dst, src, count, ditherMode); #else # if MA_PREFERRED_SIMD == MA_SIMD_AVX2 if (ma_has_avx2()) { ma_pcm_s16_to_f32__avx2(dst, src, count, ditherMode); } else #elif MA_PREFERRED_SIMD == MA_SIMD_SSE2 if (ma_has_sse2()) { ma_pcm_s16_to_f32__sse2(dst, src, count, ditherMode); } else #elif MA_PREFERRED_SIMD == MA_SIMD_NEON if (ma_has_neon()) { ma_pcm_s16_to_f32__neon(dst, src, count, ditherMode); } else #endif { ma_pcm_s16_to_f32__optimized(dst, src, count, ditherMode); } #endif } static MA_INLINE void ma_pcm_interleave_s16__reference(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels) { ma_int16* dst_s16 = (ma_int16*)dst; const ma_int16** src_s16 = (const ma_int16**)src; ma_uint64 iFrame; for (iFrame = 0; iFrame < frameCount; iFrame += 1) { ma_uint32 iChannel; for (iChannel = 0; iChannel < channels; iChannel += 1) { dst_s16[iFrame*channels + iChannel] = src_s16[iChannel][iFrame]; } } } static MA_INLINE void ma_pcm_interleave_s16__optimized(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels) { ma_pcm_interleave_s16__reference(dst, src, frameCount, channels); } MA_API void ma_pcm_interleave_s16(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels) { #ifdef MA_USE_REFERENCE_CONVERSION_APIS ma_pcm_interleave_s16__reference(dst, src, frameCount, channels); #else ma_pcm_interleave_s16__optimized(dst, src, frameCount, channels); #endif } static MA_INLINE void ma_pcm_deinterleave_s16__reference(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels) { ma_int16** dst_s16 = (ma_int16**)dst; const ma_int16* src_s16 = (const ma_int16*)src; ma_uint64 iFrame; for (iFrame = 0; iFrame < frameCount; iFrame += 1) { ma_uint32 iChannel; for (iChannel = 0; iChannel < channels; iChannel += 1) { dst_s16[iChannel][iFrame] = src_s16[iFrame*channels + iChannel]; } } } static MA_INLINE void ma_pcm_deinterleave_s16__optimized(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels) { ma_pcm_deinterleave_s16__reference(dst, src, frameCount, channels); } MA_API void ma_pcm_deinterleave_s16(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels) { #ifdef MA_USE_REFERENCE_CONVERSION_APIS ma_pcm_deinterleave_s16__reference(dst, src, frameCount, channels); #else ma_pcm_deinterleave_s16__optimized(dst, src, frameCount, channels); #endif } /* s24 */ static MA_INLINE void ma_pcm_s24_to_u8__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) { ma_uint8* dst_u8 = (ma_uint8*)dst; const ma_uint8* src_s24 = (const ma_uint8*)src; if (ditherMode == ma_dither_mode_none) { ma_uint64 i; for (i = 0; i < count; i += 1) { dst_u8[i] = (ma_uint8)((ma_int8)src_s24[i*3 + 2] + 128); } } else { ma_uint64 i; for (i = 0; i < count; i += 1) { ma_int32 x = (ma_int32)(((ma_uint32)(src_s24[i*3+0]) << 8) | ((ma_uint32)(src_s24[i*3+1]) << 16) | ((ma_uint32)(src_s24[i*3+2])) << 24); /* Dither. Don't overflow. */ ma_int32 dither = ma_dither_s32(ditherMode, -0x800000, 0x7FFFFF); if ((ma_int64)x + dither <= 0x7FFFFFFF) { x = x + dither; } else { x = 0x7FFFFFFF; } x = x >> 24; x = x + 128; dst_u8[i] = (ma_uint8)x; } } } static MA_INLINE void ma_pcm_s24_to_u8__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) { ma_pcm_s24_to_u8__reference(dst, src, count, ditherMode); } #if defined(MA_SUPPORT_SSE2) static MA_INLINE void ma_pcm_s24_to_u8__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) { ma_pcm_s24_to_u8__optimized(dst, src, count, ditherMode); } #endif #if defined(MA_SUPPORT_AVX2) static MA_INLINE void ma_pcm_s24_to_u8__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) { ma_pcm_s24_to_u8__optimized(dst, src, count, ditherMode); } #endif #if defined(MA_SUPPORT_NEON) static MA_INLINE void ma_pcm_s24_to_u8__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) { ma_pcm_s24_to_u8__optimized(dst, src, count, ditherMode); } #endif MA_API void ma_pcm_s24_to_u8(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) { #ifdef MA_USE_REFERENCE_CONVERSION_APIS ma_pcm_s24_to_u8__reference(dst, src, count, ditherMode); #else # if MA_PREFERRED_SIMD == MA_SIMD_AVX2 if (ma_has_avx2()) { ma_pcm_s24_to_u8__avx2(dst, src, count, ditherMode); } else #elif MA_PREFERRED_SIMD == MA_SIMD_SSE2 if (ma_has_sse2()) { ma_pcm_s24_to_u8__sse2(dst, src, count, ditherMode); } else #elif MA_PREFERRED_SIMD == MA_SIMD_NEON if (ma_has_neon()) { ma_pcm_s24_to_u8__neon(dst, src, count, ditherMode); } else #endif { ma_pcm_s24_to_u8__optimized(dst, src, count, ditherMode); } #endif } static MA_INLINE void ma_pcm_s24_to_s16__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) { ma_int16* dst_s16 = (ma_int16*)dst; const ma_uint8* src_s24 = (const ma_uint8*)src; if (ditherMode == ma_dither_mode_none) { ma_uint64 i; for (i = 0; i < count; i += 1) { ma_uint16 dst_lo = ((ma_uint16)src_s24[i*3 + 1]); ma_uint16 dst_hi = (ma_uint16)((ma_uint16)src_s24[i*3 + 2] << 8); dst_s16[i] = (ma_int16)(dst_lo | dst_hi); } } else { ma_uint64 i; for (i = 0; i < count; i += 1) { ma_int32 x = (ma_int32)(((ma_uint32)(src_s24[i*3+0]) << 8) | ((ma_uint32)(src_s24[i*3+1]) << 16) | ((ma_uint32)(src_s24[i*3+2])) << 24); /* Dither. Don't overflow. */ ma_int32 dither = ma_dither_s32(ditherMode, -0x8000, 0x7FFF); if ((ma_int64)x + dither <= 0x7FFFFFFF) { x = x + dither; } else { x = 0x7FFFFFFF; } x = x >> 16; dst_s16[i] = (ma_int16)x; } } } static MA_INLINE void ma_pcm_s24_to_s16__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) { ma_pcm_s24_to_s16__reference(dst, src, count, ditherMode); } #if defined(MA_SUPPORT_SSE2) static MA_INLINE void ma_pcm_s24_to_s16__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) { ma_pcm_s24_to_s16__optimized(dst, src, count, ditherMode); } #endif #if defined(MA_SUPPORT_AVX2) static MA_INLINE void ma_pcm_s24_to_s16__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) { ma_pcm_s24_to_s16__optimized(dst, src, count, ditherMode); } #endif #if defined(MA_SUPPORT_NEON) static MA_INLINE void ma_pcm_s24_to_s16__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) { ma_pcm_s24_to_s16__optimized(dst, src, count, ditherMode); } #endif MA_API void ma_pcm_s24_to_s16(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) { #ifdef MA_USE_REFERENCE_CONVERSION_APIS ma_pcm_s24_to_s16__reference(dst, src, count, ditherMode); #else # if MA_PREFERRED_SIMD == MA_SIMD_AVX2 if (ma_has_avx2()) { ma_pcm_s24_to_s16__avx2(dst, src, count, ditherMode); } else #elif MA_PREFERRED_SIMD == MA_SIMD_SSE2 if (ma_has_sse2()) { ma_pcm_s24_to_s16__sse2(dst, src, count, ditherMode); } else #elif MA_PREFERRED_SIMD == MA_SIMD_NEON if (ma_has_neon()) { ma_pcm_s24_to_s16__neon(dst, src, count, ditherMode); } else #endif { ma_pcm_s24_to_s16__optimized(dst, src, count, ditherMode); } #endif } MA_API void ma_pcm_s24_to_s24(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) { (void)ditherMode; ma_copy_memory_64(dst, src, count * 3); } static MA_INLINE void ma_pcm_s24_to_s32__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) { ma_int32* dst_s32 = (ma_int32*)dst; const ma_uint8* src_s24 = (const ma_uint8*)src; ma_uint64 i; for (i = 0; i < count; i += 1) { dst_s32[i] = (ma_int32)(((ma_uint32)(src_s24[i*3+0]) << 8) | ((ma_uint32)(src_s24[i*3+1]) << 16) | ((ma_uint32)(src_s24[i*3+2])) << 24); } (void)ditherMode; } static MA_INLINE void ma_pcm_s24_to_s32__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) { ma_pcm_s24_to_s32__reference(dst, src, count, ditherMode); } #if defined(MA_SUPPORT_SSE2) static MA_INLINE void ma_pcm_s24_to_s32__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) { ma_pcm_s24_to_s32__optimized(dst, src, count, ditherMode); } #endif #if defined(MA_SUPPORT_AVX2) static MA_INLINE void ma_pcm_s24_to_s32__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) { ma_pcm_s24_to_s32__optimized(dst, src, count, ditherMode); } #endif #if defined(MA_SUPPORT_NEON) static MA_INLINE void ma_pcm_s24_to_s32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) { ma_pcm_s24_to_s32__optimized(dst, src, count, ditherMode); } #endif MA_API void ma_pcm_s24_to_s32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) { #ifdef MA_USE_REFERENCE_CONVERSION_APIS ma_pcm_s24_to_s32__reference(dst, src, count, ditherMode); #else # if MA_PREFERRED_SIMD == MA_SIMD_AVX2 if (ma_has_avx2()) { ma_pcm_s24_to_s32__avx2(dst, src, count, ditherMode); } else #elif MA_PREFERRED_SIMD == MA_SIMD_SSE2 if (ma_has_sse2()) { ma_pcm_s24_to_s32__sse2(dst, src, count, ditherMode); } else #elif MA_PREFERRED_SIMD == MA_SIMD_NEON if (ma_has_neon()) { ma_pcm_s24_to_s32__neon(dst, src, count, ditherMode); } else #endif { ma_pcm_s24_to_s32__optimized(dst, src, count, ditherMode); } #endif } static MA_INLINE void ma_pcm_s24_to_f32__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) { float* dst_f32 = (float*)dst; const ma_uint8* src_s24 = (const ma_uint8*)src; ma_uint64 i; for (i = 0; i < count; i += 1) { float x = (float)(((ma_int32)(((ma_uint32)(src_s24[i*3+0]) << 8) | ((ma_uint32)(src_s24[i*3+1]) << 16) | ((ma_uint32)(src_s24[i*3+2])) << 24)) >> 8); #if 0 /* The accurate way. */ x = x + 8388608.0f; /* -8388608..8388607 to 0..16777215 */ x = x * 0.00000011920929665621f; /* 0..16777215 to 0..2 */ x = x - 1; /* 0..2 to -1..1 */ #else /* The fast way. */ x = x * 0.00000011920928955078125f; /* -8388608..8388607 to -1..0.999969482421875 */ #endif dst_f32[i] = x; } (void)ditherMode; } static MA_INLINE void ma_pcm_s24_to_f32__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) { ma_pcm_s24_to_f32__reference(dst, src, count, ditherMode); } #if defined(MA_SUPPORT_SSE2) static MA_INLINE void ma_pcm_s24_to_f32__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) { ma_pcm_s24_to_f32__optimized(dst, src, count, ditherMode); } #endif #if defined(MA_SUPPORT_AVX2) static MA_INLINE void ma_pcm_s24_to_f32__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) { ma_pcm_s24_to_f32__optimized(dst, src, count, ditherMode); } #endif #if defined(MA_SUPPORT_NEON) static MA_INLINE void ma_pcm_s24_to_f32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) { ma_pcm_s24_to_f32__optimized(dst, src, count, ditherMode); } #endif MA_API void ma_pcm_s24_to_f32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) { #ifdef MA_USE_REFERENCE_CONVERSION_APIS ma_pcm_s24_to_f32__reference(dst, src, count, ditherMode); #else # if MA_PREFERRED_SIMD == MA_SIMD_AVX2 if (ma_has_avx2()) { ma_pcm_s24_to_f32__avx2(dst, src, count, ditherMode); } else #elif MA_PREFERRED_SIMD == MA_SIMD_SSE2 if (ma_has_sse2()) { ma_pcm_s24_to_f32__sse2(dst, src, count, ditherMode); } else #elif MA_PREFERRED_SIMD == MA_SIMD_NEON if (ma_has_neon()) { ma_pcm_s24_to_f32__neon(dst, src, count, ditherMode); } else #endif { ma_pcm_s24_to_f32__optimized(dst, src, count, ditherMode); } #endif } static MA_INLINE void ma_pcm_interleave_s24__reference(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels) { ma_uint8* dst8 = (ma_uint8*)dst; const ma_uint8** src8 = (const ma_uint8**)src; ma_uint64 iFrame; for (iFrame = 0; iFrame < frameCount; iFrame += 1) { ma_uint32 iChannel; for (iChannel = 0; iChannel < channels; iChannel += 1) { dst8[iFrame*3*channels + iChannel*3 + 0] = src8[iChannel][iFrame*3 + 0]; dst8[iFrame*3*channels + iChannel*3 + 1] = src8[iChannel][iFrame*3 + 1]; dst8[iFrame*3*channels + iChannel*3 + 2] = src8[iChannel][iFrame*3 + 2]; } } } static MA_INLINE void ma_pcm_interleave_s24__optimized(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels) { ma_pcm_interleave_s24__reference(dst, src, frameCount, channels); } MA_API void ma_pcm_interleave_s24(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels) { #ifdef MA_USE_REFERENCE_CONVERSION_APIS ma_pcm_interleave_s24__reference(dst, src, frameCount, channels); #else ma_pcm_interleave_s24__optimized(dst, src, frameCount, channels); #endif } static MA_INLINE void ma_pcm_deinterleave_s24__reference(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels) { ma_uint8** dst8 = (ma_uint8**)dst; const ma_uint8* src8 = (const ma_uint8*)src; ma_uint32 iFrame; for (iFrame = 0; iFrame < frameCount; iFrame += 1) { ma_uint32 iChannel; for (iChannel = 0; iChannel < channels; iChannel += 1) { dst8[iChannel][iFrame*3 + 0] = src8[iFrame*3*channels + iChannel*3 + 0]; dst8[iChannel][iFrame*3 + 1] = src8[iFrame*3*channels + iChannel*3 + 1]; dst8[iChannel][iFrame*3 + 2] = src8[iFrame*3*channels + iChannel*3 + 2]; } } } static MA_INLINE void ma_pcm_deinterleave_s24__optimized(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels) { ma_pcm_deinterleave_s24__reference(dst, src, frameCount, channels); } MA_API void ma_pcm_deinterleave_s24(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels) { #ifdef MA_USE_REFERENCE_CONVERSION_APIS ma_pcm_deinterleave_s24__reference(dst, src, frameCount, channels); #else ma_pcm_deinterleave_s24__optimized(dst, src, frameCount, channels); #endif } /* s32 */ static MA_INLINE void ma_pcm_s32_to_u8__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) { ma_uint8* dst_u8 = (ma_uint8*)dst; const ma_int32* src_s32 = (const ma_int32*)src; if (ditherMode == ma_dither_mode_none) { ma_uint64 i; for (i = 0; i < count; i += 1) { ma_int32 x = src_s32[i]; x = x >> 24; x = x + 128; dst_u8[i] = (ma_uint8)x; } } else { ma_uint64 i; for (i = 0; i < count; i += 1) { ma_int32 x = src_s32[i]; /* Dither. Don't overflow. */ ma_int32 dither = ma_dither_s32(ditherMode, -0x800000, 0x7FFFFF); if ((ma_int64)x + dither <= 0x7FFFFFFF) { x = x + dither; } else { x = 0x7FFFFFFF; } x = x >> 24; x = x + 128; dst_u8[i] = (ma_uint8)x; } } } static MA_INLINE void ma_pcm_s32_to_u8__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) { ma_pcm_s32_to_u8__reference(dst, src, count, ditherMode); } #if defined(MA_SUPPORT_SSE2) static MA_INLINE void ma_pcm_s32_to_u8__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) { ma_pcm_s32_to_u8__optimized(dst, src, count, ditherMode); } #endif #if defined(MA_SUPPORT_AVX2) static MA_INLINE void ma_pcm_s32_to_u8__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) { ma_pcm_s32_to_u8__optimized(dst, src, count, ditherMode); } #endif #if defined(MA_SUPPORT_NEON) static MA_INLINE void ma_pcm_s32_to_u8__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) { ma_pcm_s32_to_u8__optimized(dst, src, count, ditherMode); } #endif MA_API void ma_pcm_s32_to_u8(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) { #ifdef MA_USE_REFERENCE_CONVERSION_APIS ma_pcm_s32_to_u8__reference(dst, src, count, ditherMode); #else # if MA_PREFERRED_SIMD == MA_SIMD_AVX2 if (ma_has_avx2()) { ma_pcm_s32_to_u8__avx2(dst, src, count, ditherMode); } else #elif MA_PREFERRED_SIMD == MA_SIMD_SSE2 if (ma_has_sse2()) { ma_pcm_s32_to_u8__sse2(dst, src, count, ditherMode); } else #elif MA_PREFERRED_SIMD == MA_SIMD_NEON if (ma_has_neon()) { ma_pcm_s32_to_u8__neon(dst, src, count, ditherMode); } else #endif { ma_pcm_s32_to_u8__optimized(dst, src, count, ditherMode); } #endif } static MA_INLINE void ma_pcm_s32_to_s16__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) { ma_int16* dst_s16 = (ma_int16*)dst; const ma_int32* src_s32 = (const ma_int32*)src; if (ditherMode == ma_dither_mode_none) { ma_uint64 i; for (i = 0; i < count; i += 1) { ma_int32 x = src_s32[i]; x = x >> 16; dst_s16[i] = (ma_int16)x; } } else { ma_uint64 i; for (i = 0; i < count; i += 1) { ma_int32 x = src_s32[i]; /* Dither. Don't overflow. */ ma_int32 dither = ma_dither_s32(ditherMode, -0x8000, 0x7FFF); if ((ma_int64)x + dither <= 0x7FFFFFFF) { x = x + dither; } else { x = 0x7FFFFFFF; } x = x >> 16; dst_s16[i] = (ma_int16)x; } } } static MA_INLINE void ma_pcm_s32_to_s16__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) { ma_pcm_s32_to_s16__reference(dst, src, count, ditherMode); } #if defined(MA_SUPPORT_SSE2) static MA_INLINE void ma_pcm_s32_to_s16__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) { ma_pcm_s32_to_s16__optimized(dst, src, count, ditherMode); } #endif #if defined(MA_SUPPORT_AVX2) static MA_INLINE void ma_pcm_s32_to_s16__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) { ma_pcm_s32_to_s16__optimized(dst, src, count, ditherMode); } #endif #if defined(MA_SUPPORT_NEON) static MA_INLINE void ma_pcm_s32_to_s16__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) { ma_pcm_s32_to_s16__optimized(dst, src, count, ditherMode); } #endif MA_API void ma_pcm_s32_to_s16(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) { #ifdef MA_USE_REFERENCE_CONVERSION_APIS ma_pcm_s32_to_s16__reference(dst, src, count, ditherMode); #else # if MA_PREFERRED_SIMD == MA_SIMD_AVX2 if (ma_has_avx2()) { ma_pcm_s32_to_s16__avx2(dst, src, count, ditherMode); } else #elif MA_PREFERRED_SIMD == MA_SIMD_SSE2 if (ma_has_sse2()) { ma_pcm_s32_to_s16__sse2(dst, src, count, ditherMode); } else #elif MA_PREFERRED_SIMD == MA_SIMD_NEON if (ma_has_neon()) { ma_pcm_s32_to_s16__neon(dst, src, count, ditherMode); } else #endif { ma_pcm_s32_to_s16__optimized(dst, src, count, ditherMode); } #endif } static MA_INLINE void ma_pcm_s32_to_s24__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) { ma_uint8* dst_s24 = (ma_uint8*)dst; const ma_int32* src_s32 = (const ma_int32*)src; ma_uint64 i; for (i = 0; i < count; i += 1) { ma_uint32 x = (ma_uint32)src_s32[i]; dst_s24[i*3+0] = (ma_uint8)((x & 0x0000FF00) >> 8); dst_s24[i*3+1] = (ma_uint8)((x & 0x00FF0000) >> 16); dst_s24[i*3+2] = (ma_uint8)((x & 0xFF000000) >> 24); } (void)ditherMode; /* No dithering for s32 -> s24. */ } static MA_INLINE void ma_pcm_s32_to_s24__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) { ma_pcm_s32_to_s24__reference(dst, src, count, ditherMode); } #if defined(MA_SUPPORT_SSE2) static MA_INLINE void ma_pcm_s32_to_s24__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) { ma_pcm_s32_to_s24__optimized(dst, src, count, ditherMode); } #endif #if defined(MA_SUPPORT_AVX2) static MA_INLINE void ma_pcm_s32_to_s24__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) { ma_pcm_s32_to_s24__optimized(dst, src, count, ditherMode); } #endif #if defined(MA_SUPPORT_NEON) static MA_INLINE void ma_pcm_s32_to_s24__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) { ma_pcm_s32_to_s24__optimized(dst, src, count, ditherMode); } #endif MA_API void ma_pcm_s32_to_s24(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) { #ifdef MA_USE_REFERENCE_CONVERSION_APIS ma_pcm_s32_to_s24__reference(dst, src, count, ditherMode); #else # if MA_PREFERRED_SIMD == MA_SIMD_AVX2 if (ma_has_avx2()) { ma_pcm_s32_to_s24__avx2(dst, src, count, ditherMode); } else #elif MA_PREFERRED_SIMD == MA_SIMD_SSE2 if (ma_has_sse2()) { ma_pcm_s32_to_s24__sse2(dst, src, count, ditherMode); } else #elif MA_PREFERRED_SIMD == MA_SIMD_NEON if (ma_has_neon()) { ma_pcm_s32_to_s24__neon(dst, src, count, ditherMode); } else #endif { ma_pcm_s32_to_s24__optimized(dst, src, count, ditherMode); } #endif } MA_API void ma_pcm_s32_to_s32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) { (void)ditherMode; ma_copy_memory_64(dst, src, count * sizeof(ma_int32)); } static MA_INLINE void ma_pcm_s32_to_f32__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) { float* dst_f32 = (float*)dst; const ma_int32* src_s32 = (const ma_int32*)src; ma_uint64 i; for (i = 0; i < count; i += 1) { double x = src_s32[i]; #if 0 x = x + 2147483648.0; x = x * 0.0000000004656612873077392578125; x = x - 1; #else x = x / 2147483648.0; #endif dst_f32[i] = (float)x; } (void)ditherMode; /* No dithering for s32 -> f32. */ } static MA_INLINE void ma_pcm_s32_to_f32__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) { ma_pcm_s32_to_f32__reference(dst, src, count, ditherMode); } #if defined(MA_SUPPORT_SSE2) static MA_INLINE void ma_pcm_s32_to_f32__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) { ma_pcm_s32_to_f32__optimized(dst, src, count, ditherMode); } #endif #if defined(MA_SUPPORT_AVX2) static MA_INLINE void ma_pcm_s32_to_f32__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) { ma_pcm_s32_to_f32__optimized(dst, src, count, ditherMode); } #endif #if defined(MA_SUPPORT_NEON) static MA_INLINE void ma_pcm_s32_to_f32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) { ma_pcm_s32_to_f32__optimized(dst, src, count, ditherMode); } #endif MA_API void ma_pcm_s32_to_f32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) { #ifdef MA_USE_REFERENCE_CONVERSION_APIS ma_pcm_s32_to_f32__reference(dst, src, count, ditherMode); #else # if MA_PREFERRED_SIMD == MA_SIMD_AVX2 if (ma_has_avx2()) { ma_pcm_s32_to_f32__avx2(dst, src, count, ditherMode); } else #elif MA_PREFERRED_SIMD == MA_SIMD_SSE2 if (ma_has_sse2()) { ma_pcm_s32_to_f32__sse2(dst, src, count, ditherMode); } else #elif MA_PREFERRED_SIMD == MA_SIMD_NEON if (ma_has_neon()) { ma_pcm_s32_to_f32__neon(dst, src, count, ditherMode); } else #endif { ma_pcm_s32_to_f32__optimized(dst, src, count, ditherMode); } #endif } static MA_INLINE void ma_pcm_interleave_s32__reference(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels) { ma_int32* dst_s32 = (ma_int32*)dst; const ma_int32** src_s32 = (const ma_int32**)src; ma_uint64 iFrame; for (iFrame = 0; iFrame < frameCount; iFrame += 1) { ma_uint32 iChannel; for (iChannel = 0; iChannel < channels; iChannel += 1) { dst_s32[iFrame*channels + iChannel] = src_s32[iChannel][iFrame]; } } } static MA_INLINE void ma_pcm_interleave_s32__optimized(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels) { ma_pcm_interleave_s32__reference(dst, src, frameCount, channels); } MA_API void ma_pcm_interleave_s32(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels) { #ifdef MA_USE_REFERENCE_CONVERSION_APIS ma_pcm_interleave_s32__reference(dst, src, frameCount, channels); #else ma_pcm_interleave_s32__optimized(dst, src, frameCount, channels); #endif } static MA_INLINE void ma_pcm_deinterleave_s32__reference(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels) { ma_int32** dst_s32 = (ma_int32**)dst; const ma_int32* src_s32 = (const ma_int32*)src; ma_uint64 iFrame; for (iFrame = 0; iFrame < frameCount; iFrame += 1) { ma_uint32 iChannel; for (iChannel = 0; iChannel < channels; iChannel += 1) { dst_s32[iChannel][iFrame] = src_s32[iFrame*channels + iChannel]; } } } static MA_INLINE void ma_pcm_deinterleave_s32__optimized(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels) { ma_pcm_deinterleave_s32__reference(dst, src, frameCount, channels); } MA_API void ma_pcm_deinterleave_s32(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels) { #ifdef MA_USE_REFERENCE_CONVERSION_APIS ma_pcm_deinterleave_s32__reference(dst, src, frameCount, channels); #else ma_pcm_deinterleave_s32__optimized(dst, src, frameCount, channels); #endif } /* f32 */ static MA_INLINE void ma_pcm_f32_to_u8__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) { ma_uint64 i; ma_uint8* dst_u8 = (ma_uint8*)dst; const float* src_f32 = (const float*)src; float ditherMin = 0; float ditherMax = 0; if (ditherMode != ma_dither_mode_none) { ditherMin = 1.0f / -128; ditherMax = 1.0f / 127; } for (i = 0; i < count; i += 1) { float x = src_f32[i]; x = x + ma_dither_f32(ditherMode, ditherMin, ditherMax); x = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); /* clip */ x = x + 1; /* -1..1 to 0..2 */ x = x * 127.5f; /* 0..2 to 0..255 */ dst_u8[i] = (ma_uint8)x; } } static MA_INLINE void ma_pcm_f32_to_u8__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) { ma_pcm_f32_to_u8__reference(dst, src, count, ditherMode); } #if defined(MA_SUPPORT_SSE2) static MA_INLINE void ma_pcm_f32_to_u8__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) { ma_pcm_f32_to_u8__optimized(dst, src, count, ditherMode); } #endif #if defined(MA_SUPPORT_AVX2) static MA_INLINE void ma_pcm_f32_to_u8__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) { ma_pcm_f32_to_u8__optimized(dst, src, count, ditherMode); } #endif #if defined(MA_SUPPORT_NEON) static MA_INLINE void ma_pcm_f32_to_u8__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) { ma_pcm_f32_to_u8__optimized(dst, src, count, ditherMode); } #endif MA_API void ma_pcm_f32_to_u8(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) { #ifdef MA_USE_REFERENCE_CONVERSION_APIS ma_pcm_f32_to_u8__reference(dst, src, count, ditherMode); #else # if MA_PREFERRED_SIMD == MA_SIMD_AVX2 if (ma_has_avx2()) { ma_pcm_f32_to_u8__avx2(dst, src, count, ditherMode); } else #elif MA_PREFERRED_SIMD == MA_SIMD_SSE2 if (ma_has_sse2()) { ma_pcm_f32_to_u8__sse2(dst, src, count, ditherMode); } else #elif MA_PREFERRED_SIMD == MA_SIMD_NEON if (ma_has_neon()) { ma_pcm_f32_to_u8__neon(dst, src, count, ditherMode); } else #endif { ma_pcm_f32_to_u8__optimized(dst, src, count, ditherMode); } #endif } #ifdef MA_USE_REFERENCE_CONVERSION_APIS static MA_INLINE void ma_pcm_f32_to_s16__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) { ma_uint64 i; ma_int16* dst_s16 = (ma_int16*)dst; const float* src_f32 = (const float*)src; float ditherMin = 0; float ditherMax = 0; if (ditherMode != ma_dither_mode_none) { ditherMin = 1.0f / -32768; ditherMax = 1.0f / 32767; } for (i = 0; i < count; i += 1) { float x = src_f32[i]; x = x + ma_dither_f32(ditherMode, ditherMin, ditherMax); x = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); /* clip */ #if 0 /* The accurate way. */ x = x + 1; /* -1..1 to 0..2 */ x = x * 32767.5f; /* 0..2 to 0..65535 */ x = x - 32768.0f; /* 0...65535 to -32768..32767 */ #else /* The fast way. */ x = x * 32767.0f; /* -1..1 to -32767..32767 */ #endif dst_s16[i] = (ma_int16)x; } } #else static MA_INLINE void ma_pcm_f32_to_s16__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) { ma_uint64 i; ma_uint64 i4; ma_uint64 count4; ma_int16* dst_s16 = (ma_int16*)dst; const float* src_f32 = (const float*)src; float ditherMin = 0; float ditherMax = 0; if (ditherMode != ma_dither_mode_none) { ditherMin = 1.0f / -32768; ditherMax = 1.0f / 32767; } /* Unrolled. */ i = 0; count4 = count >> 2; for (i4 = 0; i4 < count4; i4 += 1) { float d0 = ma_dither_f32(ditherMode, ditherMin, ditherMax); float d1 = ma_dither_f32(ditherMode, ditherMin, ditherMax); float d2 = ma_dither_f32(ditherMode, ditherMin, ditherMax); float d3 = ma_dither_f32(ditherMode, ditherMin, ditherMax); float x0 = src_f32[i+0]; float x1 = src_f32[i+1]; float x2 = src_f32[i+2]; float x3 = src_f32[i+3]; x0 = x0 + d0; x1 = x1 + d1; x2 = x2 + d2; x3 = x3 + d3; x0 = ((x0 < -1) ? -1 : ((x0 > 1) ? 1 : x0)); x1 = ((x1 < -1) ? -1 : ((x1 > 1) ? 1 : x1)); x2 = ((x2 < -1) ? -1 : ((x2 > 1) ? 1 : x2)); x3 = ((x3 < -1) ? -1 : ((x3 > 1) ? 1 : x3)); x0 = x0 * 32767.0f; x1 = x1 * 32767.0f; x2 = x2 * 32767.0f; x3 = x3 * 32767.0f; dst_s16[i+0] = (ma_int16)x0; dst_s16[i+1] = (ma_int16)x1; dst_s16[i+2] = (ma_int16)x2; dst_s16[i+3] = (ma_int16)x3; i += 4; } /* Leftover. */ for (; i < count; i += 1) { float x = src_f32[i]; x = x + ma_dither_f32(ditherMode, ditherMin, ditherMax); x = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); /* clip */ x = x * 32767.0f; /* -1..1 to -32767..32767 */ dst_s16[i] = (ma_int16)x; } } #if defined(MA_SUPPORT_SSE2) static MA_INLINE void ma_pcm_f32_to_s16__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) { ma_uint64 i; ma_uint64 i8; ma_uint64 count8; ma_int16* dst_s16; const float* src_f32; float ditherMin; float ditherMax; /* Both the input and output buffers need to be aligned to 16 bytes. */ if ((((ma_uintptr)dst & 15) != 0) || (((ma_uintptr)src & 15) != 0)) { ma_pcm_f32_to_s16__optimized(dst, src, count, ditherMode); return; } dst_s16 = (ma_int16*)dst; src_f32 = (const float*)src; ditherMin = 0; ditherMax = 0; if (ditherMode != ma_dither_mode_none) { ditherMin = 1.0f / -32768; ditherMax = 1.0f / 32767; } i = 0; /* SSE2. SSE allows us to output 8 s16's at a time which means our loop is unrolled 8 times. */ count8 = count >> 3; for (i8 = 0; i8 < count8; i8 += 1) { __m128 d0; __m128 d1; __m128 x0; __m128 x1; if (ditherMode == ma_dither_mode_none) { d0 = _mm_set1_ps(0); d1 = _mm_set1_ps(0); } else if (ditherMode == ma_dither_mode_rectangle) { d0 = _mm_set_ps( ma_dither_f32_rectangle(ditherMin, ditherMax), ma_dither_f32_rectangle(ditherMin, ditherMax), ma_dither_f32_rectangle(ditherMin, ditherMax), ma_dither_f32_rectangle(ditherMin, ditherMax) ); d1 = _mm_set_ps( ma_dither_f32_rectangle(ditherMin, ditherMax), ma_dither_f32_rectangle(ditherMin, ditherMax), ma_dither_f32_rectangle(ditherMin, ditherMax), ma_dither_f32_rectangle(ditherMin, ditherMax) ); } else { d0 = _mm_set_ps( ma_dither_f32_triangle(ditherMin, ditherMax), ma_dither_f32_triangle(ditherMin, ditherMax), ma_dither_f32_triangle(ditherMin, ditherMax), ma_dither_f32_triangle(ditherMin, ditherMax) ); d1 = _mm_set_ps( ma_dither_f32_triangle(ditherMin, ditherMax), ma_dither_f32_triangle(ditherMin, ditherMax), ma_dither_f32_triangle(ditherMin, ditherMax), ma_dither_f32_triangle(ditherMin, ditherMax) ); } x0 = *((__m128*)(src_f32 + i) + 0); x1 = *((__m128*)(src_f32 + i) + 1); x0 = _mm_add_ps(x0, d0); x1 = _mm_add_ps(x1, d1); x0 = _mm_mul_ps(x0, _mm_set1_ps(32767.0f)); x1 = _mm_mul_ps(x1, _mm_set1_ps(32767.0f)); _mm_stream_si128(((__m128i*)(dst_s16 + i)), _mm_packs_epi32(_mm_cvttps_epi32(x0), _mm_cvttps_epi32(x1))); i += 8; } /* Leftover. */ for (; i < count; i += 1) { float x = src_f32[i]; x = x + ma_dither_f32(ditherMode, ditherMin, ditherMax); x = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); /* clip */ x = x * 32767.0f; /* -1..1 to -32767..32767 */ dst_s16[i] = (ma_int16)x; } } #endif /* SSE2 */ #if defined(MA_SUPPORT_AVX2) static MA_INLINE void ma_pcm_f32_to_s16__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) { ma_uint64 i; ma_uint64 i16; ma_uint64 count16; ma_int16* dst_s16; const float* src_f32; float ditherMin; float ditherMax; /* Both the input and output buffers need to be aligned to 32 bytes. */ if ((((ma_uintptr)dst & 31) != 0) || (((ma_uintptr)src & 31) != 0)) { ma_pcm_f32_to_s16__optimized(dst, src, count, ditherMode); return; } dst_s16 = (ma_int16*)dst; src_f32 = (const float*)src; ditherMin = 0; ditherMax = 0; if (ditherMode != ma_dither_mode_none) { ditherMin = 1.0f / -32768; ditherMax = 1.0f / 32767; } i = 0; /* AVX2. AVX2 allows us to output 16 s16's at a time which means our loop is unrolled 16 times. */ count16 = count >> 4; for (i16 = 0; i16 < count16; i16 += 1) { __m256 d0; __m256 d1; __m256 x0; __m256 x1; __m256i i0; __m256i i1; __m256i p0; __m256i p1; __m256i r; if (ditherMode == ma_dither_mode_none) { d0 = _mm256_set1_ps(0); d1 = _mm256_set1_ps(0); } else if (ditherMode == ma_dither_mode_rectangle) { d0 = _mm256_set_ps( ma_dither_f32_rectangle(ditherMin, ditherMax), ma_dither_f32_rectangle(ditherMin, ditherMax), ma_dither_f32_rectangle(ditherMin, ditherMax), ma_dither_f32_rectangle(ditherMin, ditherMax), ma_dither_f32_rectangle(ditherMin, ditherMax), ma_dither_f32_rectangle(ditherMin, ditherMax), ma_dither_f32_rectangle(ditherMin, ditherMax), ma_dither_f32_rectangle(ditherMin, ditherMax) ); d1 = _mm256_set_ps( ma_dither_f32_rectangle(ditherMin, ditherMax), ma_dither_f32_rectangle(ditherMin, ditherMax), ma_dither_f32_rectangle(ditherMin, ditherMax), ma_dither_f32_rectangle(ditherMin, ditherMax), ma_dither_f32_rectangle(ditherMin, ditherMax), ma_dither_f32_rectangle(ditherMin, ditherMax), ma_dither_f32_rectangle(ditherMin, ditherMax), ma_dither_f32_rectangle(ditherMin, ditherMax) ); } else { d0 = _mm256_set_ps( ma_dither_f32_triangle(ditherMin, ditherMax), ma_dither_f32_triangle(ditherMin, ditherMax), ma_dither_f32_triangle(ditherMin, ditherMax), ma_dither_f32_triangle(ditherMin, ditherMax), ma_dither_f32_triangle(ditherMin, ditherMax), ma_dither_f32_triangle(ditherMin, ditherMax), ma_dither_f32_triangle(ditherMin, ditherMax), ma_dither_f32_triangle(ditherMin, ditherMax) ); d1 = _mm256_set_ps( ma_dither_f32_triangle(ditherMin, ditherMax), ma_dither_f32_triangle(ditherMin, ditherMax), ma_dither_f32_triangle(ditherMin, ditherMax), ma_dither_f32_triangle(ditherMin, ditherMax), ma_dither_f32_triangle(ditherMin, ditherMax), ma_dither_f32_triangle(ditherMin, ditherMax), ma_dither_f32_triangle(ditherMin, ditherMax), ma_dither_f32_triangle(ditherMin, ditherMax) ); } x0 = *((__m256*)(src_f32 + i) + 0); x1 = *((__m256*)(src_f32 + i) + 1); x0 = _mm256_add_ps(x0, d0); x1 = _mm256_add_ps(x1, d1); x0 = _mm256_mul_ps(x0, _mm256_set1_ps(32767.0f)); x1 = _mm256_mul_ps(x1, _mm256_set1_ps(32767.0f)); /* Computing the final result is a little more complicated for AVX2 than SSE2. */ i0 = _mm256_cvttps_epi32(x0); i1 = _mm256_cvttps_epi32(x1); p0 = _mm256_permute2x128_si256(i0, i1, 0 | 32); p1 = _mm256_permute2x128_si256(i0, i1, 1 | 48); r = _mm256_packs_epi32(p0, p1); _mm256_stream_si256(((__m256i*)(dst_s16 + i)), r); i += 16; } /* Leftover. */ for (; i < count; i += 1) { float x = src_f32[i]; x = x + ma_dither_f32(ditherMode, ditherMin, ditherMax); x = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); /* clip */ x = x * 32767.0f; /* -1..1 to -32767..32767 */ dst_s16[i] = (ma_int16)x; } } #endif /* AVX2 */ #if defined(MA_SUPPORT_NEON) static MA_INLINE void ma_pcm_f32_to_s16__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) { ma_uint64 i; ma_uint64 i8; ma_uint64 count8; ma_int16* dst_s16; const float* src_f32; float ditherMin; float ditherMax; if (!ma_has_neon()) { return ma_pcm_f32_to_s16__optimized(dst, src, count, ditherMode); } /* Both the input and output buffers need to be aligned to 16 bytes. */ if ((((ma_uintptr)dst & 15) != 0) || (((ma_uintptr)src & 15) != 0)) { ma_pcm_f32_to_s16__optimized(dst, src, count, ditherMode); return; } dst_s16 = (ma_int16*)dst; src_f32 = (const float*)src; ditherMin = 0; ditherMax = 0; if (ditherMode != ma_dither_mode_none) { ditherMin = 1.0f / -32768; ditherMax = 1.0f / 32767; } i = 0; /* NEON. NEON allows us to output 8 s16's at a time which means our loop is unrolled 8 times. */ count8 = count >> 3; for (i8 = 0; i8 < count8; i8 += 1) { float32x4_t d0; float32x4_t d1; float32x4_t x0; float32x4_t x1; int32x4_t i0; int32x4_t i1; if (ditherMode == ma_dither_mode_none) { d0 = vmovq_n_f32(0); d1 = vmovq_n_f32(0); } else if (ditherMode == ma_dither_mode_rectangle) { float d0v[4]; d0v[0] = ma_dither_f32_rectangle(ditherMin, ditherMax); d0v[1] = ma_dither_f32_rectangle(ditherMin, ditherMax); d0v[2] = ma_dither_f32_rectangle(ditherMin, ditherMax); d0v[3] = ma_dither_f32_rectangle(ditherMin, ditherMax); d0 = vld1q_f32(d0v); float d1v[4]; d1v[0] = ma_dither_f32_rectangle(ditherMin, ditherMax); d1v[1] = ma_dither_f32_rectangle(ditherMin, ditherMax); d1v[2] = ma_dither_f32_rectangle(ditherMin, ditherMax); d1v[3] = ma_dither_f32_rectangle(ditherMin, ditherMax); d1 = vld1q_f32(d1v); } else { float d0v[4]; d0v[0] = ma_dither_f32_triangle(ditherMin, ditherMax); d0v[1] = ma_dither_f32_triangle(ditherMin, ditherMax); d0v[2] = ma_dither_f32_triangle(ditherMin, ditherMax); d0v[3] = ma_dither_f32_triangle(ditherMin, ditherMax); d0 = vld1q_f32(d0v); float d1v[4]; d1v[0] = ma_dither_f32_triangle(ditherMin, ditherMax); d1v[1] = ma_dither_f32_triangle(ditherMin, ditherMax); d1v[2] = ma_dither_f32_triangle(ditherMin, ditherMax); d1v[3] = ma_dither_f32_triangle(ditherMin, ditherMax); d1 = vld1q_f32(d1v); } x0 = *((float32x4_t*)(src_f32 + i) + 0); x1 = *((float32x4_t*)(src_f32 + i) + 1); x0 = vaddq_f32(x0, d0); x1 = vaddq_f32(x1, d1); x0 = vmulq_n_f32(x0, 32767.0f); x1 = vmulq_n_f32(x1, 32767.0f); i0 = vcvtq_s32_f32(x0); i1 = vcvtq_s32_f32(x1); *((int16x8_t*)(dst_s16 + i)) = vcombine_s16(vqmovn_s32(i0), vqmovn_s32(i1)); i += 8; } /* Leftover. */ for (; i < count; i += 1) { float x = src_f32[i]; x = x + ma_dither_f32(ditherMode, ditherMin, ditherMax); x = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); /* clip */ x = x * 32767.0f; /* -1..1 to -32767..32767 */ dst_s16[i] = (ma_int16)x; } } #endif /* Neon */ #endif /* MA_USE_REFERENCE_CONVERSION_APIS */ MA_API void ma_pcm_f32_to_s16(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) { #ifdef MA_USE_REFERENCE_CONVERSION_APIS ma_pcm_f32_to_s16__reference(dst, src, count, ditherMode); #else # if MA_PREFERRED_SIMD == MA_SIMD_AVX2 if (ma_has_avx2()) { ma_pcm_f32_to_s16__avx2(dst, src, count, ditherMode); } else #elif MA_PREFERRED_SIMD == MA_SIMD_SSE2 if (ma_has_sse2()) { ma_pcm_f32_to_s16__sse2(dst, src, count, ditherMode); } else #elif MA_PREFERRED_SIMD == MA_SIMD_NEON if (ma_has_neon()) { ma_pcm_f32_to_s16__neon(dst, src, count, ditherMode); } else #endif { ma_pcm_f32_to_s16__optimized(dst, src, count, ditherMode); } #endif } static MA_INLINE void ma_pcm_f32_to_s24__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) { ma_uint8* dst_s24 = (ma_uint8*)dst; const float* src_f32 = (const float*)src; ma_uint64 i; for (i = 0; i < count; i += 1) { ma_int32 r; float x = src_f32[i]; x = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); /* clip */ #if 0 /* The accurate way. */ x = x + 1; /* -1..1 to 0..2 */ x = x * 8388607.5f; /* 0..2 to 0..16777215 */ x = x - 8388608.0f; /* 0..16777215 to -8388608..8388607 */ #else /* The fast way. */ x = x * 8388607.0f; /* -1..1 to -8388607..8388607 */ #endif r = (ma_int32)x; dst_s24[(i*3)+0] = (ma_uint8)((r & 0x0000FF) >> 0); dst_s24[(i*3)+1] = (ma_uint8)((r & 0x00FF00) >> 8); dst_s24[(i*3)+2] = (ma_uint8)((r & 0xFF0000) >> 16); } (void)ditherMode; /* No dithering for f32 -> s24. */ } static MA_INLINE void ma_pcm_f32_to_s24__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) { ma_pcm_f32_to_s24__reference(dst, src, count, ditherMode); } #if defined(MA_SUPPORT_SSE2) static MA_INLINE void ma_pcm_f32_to_s24__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) { ma_pcm_f32_to_s24__optimized(dst, src, count, ditherMode); } #endif #if defined(MA_SUPPORT_AVX2) static MA_INLINE void ma_pcm_f32_to_s24__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) { ma_pcm_f32_to_s24__optimized(dst, src, count, ditherMode); } #endif #if defined(MA_SUPPORT_NEON) static MA_INLINE void ma_pcm_f32_to_s24__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) { ma_pcm_f32_to_s24__optimized(dst, src, count, ditherMode); } #endif MA_API void ma_pcm_f32_to_s24(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) { #ifdef MA_USE_REFERENCE_CONVERSION_APIS ma_pcm_f32_to_s24__reference(dst, src, count, ditherMode); #else # if MA_PREFERRED_SIMD == MA_SIMD_AVX2 if (ma_has_avx2()) { ma_pcm_f32_to_s24__avx2(dst, src, count, ditherMode); } else #elif MA_PREFERRED_SIMD == MA_SIMD_SSE2 if (ma_has_sse2()) { ma_pcm_f32_to_s24__sse2(dst, src, count, ditherMode); } else #elif MA_PREFERRED_SIMD == MA_SIMD_NEON if (ma_has_neon()) { ma_pcm_f32_to_s24__neon(dst, src, count, ditherMode); } else #endif { ma_pcm_f32_to_s24__optimized(dst, src, count, ditherMode); } #endif } static MA_INLINE void ma_pcm_f32_to_s32__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) { ma_int32* dst_s32 = (ma_int32*)dst; const float* src_f32 = (const float*)src; ma_uint32 i; for (i = 0; i < count; i += 1) { double x = src_f32[i]; x = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); /* clip */ #if 0 /* The accurate way. */ x = x + 1; /* -1..1 to 0..2 */ x = x * 2147483647.5; /* 0..2 to 0..4294967295 */ x = x - 2147483648.0; /* 0...4294967295 to -2147483648..2147483647 */ #else /* The fast way. */ x = x * 2147483647.0; /* -1..1 to -2147483647..2147483647 */ #endif dst_s32[i] = (ma_int32)x; } (void)ditherMode; /* No dithering for f32 -> s32. */ } static MA_INLINE void ma_pcm_f32_to_s32__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) { ma_pcm_f32_to_s32__reference(dst, src, count, ditherMode); } #if defined(MA_SUPPORT_SSE2) static MA_INLINE void ma_pcm_f32_to_s32__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) { ma_pcm_f32_to_s32__optimized(dst, src, count, ditherMode); } #endif #if defined(MA_SUPPORT_AVX2) static MA_INLINE void ma_pcm_f32_to_s32__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) { ma_pcm_f32_to_s32__optimized(dst, src, count, ditherMode); } #endif #if defined(MA_SUPPORT_NEON) static MA_INLINE void ma_pcm_f32_to_s32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) { ma_pcm_f32_to_s32__optimized(dst, src, count, ditherMode); } #endif MA_API void ma_pcm_f32_to_s32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) { #ifdef MA_USE_REFERENCE_CONVERSION_APIS ma_pcm_f32_to_s32__reference(dst, src, count, ditherMode); #else # if MA_PREFERRED_SIMD == MA_SIMD_AVX2 if (ma_has_avx2()) { ma_pcm_f32_to_s32__avx2(dst, src, count, ditherMode); } else #elif MA_PREFERRED_SIMD == MA_SIMD_SSE2 if (ma_has_sse2()) { ma_pcm_f32_to_s32__sse2(dst, src, count, ditherMode); } else #elif MA_PREFERRED_SIMD == MA_SIMD_NEON if (ma_has_neon()) { ma_pcm_f32_to_s32__neon(dst, src, count, ditherMode); } else #endif { ma_pcm_f32_to_s32__optimized(dst, src, count, ditherMode); } #endif } MA_API void ma_pcm_f32_to_f32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode) { (void)ditherMode; ma_copy_memory_64(dst, src, count * sizeof(float)); } static void ma_pcm_interleave_f32__reference(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels) { float* dst_f32 = (float*)dst; const float** src_f32 = (const float**)src; ma_uint64 iFrame; for (iFrame = 0; iFrame < frameCount; iFrame += 1) { ma_uint32 iChannel; for (iChannel = 0; iChannel < channels; iChannel += 1) { dst_f32[iFrame*channels + iChannel] = src_f32[iChannel][iFrame]; } } } static void ma_pcm_interleave_f32__optimized(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels) { ma_pcm_interleave_f32__reference(dst, src, frameCount, channels); } MA_API void ma_pcm_interleave_f32(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels) { #ifdef MA_USE_REFERENCE_CONVERSION_APIS ma_pcm_interleave_f32__reference(dst, src, frameCount, channels); #else ma_pcm_interleave_f32__optimized(dst, src, frameCount, channels); #endif } static void ma_pcm_deinterleave_f32__reference(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels) { float** dst_f32 = (float**)dst; const float* src_f32 = (const float*)src; ma_uint64 iFrame; for (iFrame = 0; iFrame < frameCount; iFrame += 1) { ma_uint32 iChannel; for (iChannel = 0; iChannel < channels; iChannel += 1) { dst_f32[iChannel][iFrame] = src_f32[iFrame*channels + iChannel]; } } } static void ma_pcm_deinterleave_f32__optimized(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels) { ma_pcm_deinterleave_f32__reference(dst, src, frameCount, channels); } MA_API void ma_pcm_deinterleave_f32(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels) { #ifdef MA_USE_REFERENCE_CONVERSION_APIS ma_pcm_deinterleave_f32__reference(dst, src, frameCount, channels); #else ma_pcm_deinterleave_f32__optimized(dst, src, frameCount, channels); #endif } MA_API void ma_pcm_convert(void* pOut, ma_format formatOut, const void* pIn, ma_format formatIn, ma_uint64 sampleCount, ma_dither_mode ditherMode) { if (formatOut == formatIn) { ma_copy_memory_64(pOut, pIn, sampleCount * ma_get_bytes_per_sample(formatOut)); return; } switch (formatIn) { case ma_format_u8: { switch (formatOut) { case ma_format_s16: ma_pcm_u8_to_s16(pOut, pIn, sampleCount, ditherMode); return; case ma_format_s24: ma_pcm_u8_to_s24(pOut, pIn, sampleCount, ditherMode); return; case ma_format_s32: ma_pcm_u8_to_s32(pOut, pIn, sampleCount, ditherMode); return; case ma_format_f32: ma_pcm_u8_to_f32(pOut, pIn, sampleCount, ditherMode); return; default: break; } } break; case ma_format_s16: { switch (formatOut) { case ma_format_u8: ma_pcm_s16_to_u8( pOut, pIn, sampleCount, ditherMode); return; case ma_format_s24: ma_pcm_s16_to_s24(pOut, pIn, sampleCount, ditherMode); return; case ma_format_s32: ma_pcm_s16_to_s32(pOut, pIn, sampleCount, ditherMode); return; case ma_format_f32: ma_pcm_s16_to_f32(pOut, pIn, sampleCount, ditherMode); return; default: break; } } break; case ma_format_s24: { switch (formatOut) { case ma_format_u8: ma_pcm_s24_to_u8( pOut, pIn, sampleCount, ditherMode); return; case ma_format_s16: ma_pcm_s24_to_s16(pOut, pIn, sampleCount, ditherMode); return; case ma_format_s32: ma_pcm_s24_to_s32(pOut, pIn, sampleCount, ditherMode); return; case ma_format_f32: ma_pcm_s24_to_f32(pOut, pIn, sampleCount, ditherMode); return; default: break; } } break; case ma_format_s32: { switch (formatOut) { case ma_format_u8: ma_pcm_s32_to_u8( pOut, pIn, sampleCount, ditherMode); return; case ma_format_s16: ma_pcm_s32_to_s16(pOut, pIn, sampleCount, ditherMode); return; case ma_format_s24: ma_pcm_s32_to_s24(pOut, pIn, sampleCount, ditherMode); return; case ma_format_f32: ma_pcm_s32_to_f32(pOut, pIn, sampleCount, ditherMode); return; default: break; } } break; case ma_format_f32: { switch (formatOut) { case ma_format_u8: ma_pcm_f32_to_u8( pOut, pIn, sampleCount, ditherMode); return; case ma_format_s16: ma_pcm_f32_to_s16(pOut, pIn, sampleCount, ditherMode); return; case ma_format_s24: ma_pcm_f32_to_s24(pOut, pIn, sampleCount, ditherMode); return; case ma_format_s32: ma_pcm_f32_to_s32(pOut, pIn, sampleCount, ditherMode); return; default: break; } } break; default: break; } } MA_API void ma_convert_pcm_frames_format(void* pOut, ma_format formatOut, const void* pIn, ma_format formatIn, ma_uint64 frameCount, ma_uint32 channels, ma_dither_mode ditherMode) { ma_pcm_convert(pOut, formatOut, pIn, formatIn, frameCount * channels, ditherMode); } MA_API void ma_deinterleave_pcm_frames(ma_format format, ma_uint32 channels, ma_uint64 frameCount, const void* pInterleavedPCMFrames, void** ppDeinterleavedPCMFrames) { if (pInterleavedPCMFrames == NULL || ppDeinterleavedPCMFrames == NULL) { return; /* Invalid args. */ } /* For efficiency we do this per format. */ switch (format) { case ma_format_s16: { const ma_int16* pSrcS16 = (const ma_int16*)pInterleavedPCMFrames; ma_uint64 iPCMFrame; for (iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) { ma_uint32 iChannel; for (iChannel = 0; iChannel < channels; ++iChannel) { ma_int16* pDstS16 = (ma_int16*)ppDeinterleavedPCMFrames[iChannel]; pDstS16[iPCMFrame] = pSrcS16[iPCMFrame*channels+iChannel]; } } } break; case ma_format_f32: { const float* pSrcF32 = (const float*)pInterleavedPCMFrames; ma_uint64 iPCMFrame; for (iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) { ma_uint32 iChannel; for (iChannel = 0; iChannel < channels; ++iChannel) { float* pDstF32 = (float*)ppDeinterleavedPCMFrames[iChannel]; pDstF32[iPCMFrame] = pSrcF32[iPCMFrame*channels+iChannel]; } } } break; default: { ma_uint32 sampleSizeInBytes = ma_get_bytes_per_sample(format); ma_uint64 iPCMFrame; for (iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) { ma_uint32 iChannel; for (iChannel = 0; iChannel < channels; ++iChannel) { void* pDst = ma_offset_ptr(ppDeinterleavedPCMFrames[iChannel], iPCMFrame*sampleSizeInBytes); const void* pSrc = ma_offset_ptr(pInterleavedPCMFrames, (iPCMFrame*channels+iChannel)*sampleSizeInBytes); memcpy(pDst, pSrc, sampleSizeInBytes); } } } break; } } MA_API void ma_interleave_pcm_frames(ma_format format, ma_uint32 channels, ma_uint64 frameCount, const void** ppDeinterleavedPCMFrames, void* pInterleavedPCMFrames) { switch (format) { case ma_format_s16: { ma_int16* pDstS16 = (ma_int16*)pInterleavedPCMFrames; ma_uint64 iPCMFrame; for (iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) { ma_uint32 iChannel; for (iChannel = 0; iChannel < channels; ++iChannel) { const ma_int16* pSrcS16 = (const ma_int16*)ppDeinterleavedPCMFrames[iChannel]; pDstS16[iPCMFrame*channels+iChannel] = pSrcS16[iPCMFrame]; } } } break; case ma_format_f32: { float* pDstF32 = (float*)pInterleavedPCMFrames; ma_uint64 iPCMFrame; for (iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) { ma_uint32 iChannel; for (iChannel = 0; iChannel < channels; ++iChannel) { const float* pSrcF32 = (const float*)ppDeinterleavedPCMFrames[iChannel]; pDstF32[iPCMFrame*channels+iChannel] = pSrcF32[iPCMFrame]; } } } break; default: { ma_uint32 sampleSizeInBytes = ma_get_bytes_per_sample(format); ma_uint64 iPCMFrame; for (iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) { ma_uint32 iChannel; for (iChannel = 0; iChannel < channels; ++iChannel) { void* pDst = ma_offset_ptr(pInterleavedPCMFrames, (iPCMFrame*channels+iChannel)*sampleSizeInBytes); const void* pSrc = ma_offset_ptr(ppDeinterleavedPCMFrames[iChannel], iPCMFrame*sampleSizeInBytes); memcpy(pDst, pSrc, sampleSizeInBytes); } } } break; } } /************************************************************************************************************************************************************** Biquad Filter **************************************************************************************************************************************************************/ #ifndef MA_BIQUAD_FIXED_POINT_SHIFT #define MA_BIQUAD_FIXED_POINT_SHIFT 14 #endif static ma_int32 ma_biquad_float_to_fp(double x) { return (ma_int32)(x * (1 << MA_BIQUAD_FIXED_POINT_SHIFT)); } MA_API ma_biquad_config ma_biquad_config_init(ma_format format, ma_uint32 channels, double b0, double b1, double b2, double a0, double a1, double a2) { ma_biquad_config config; MA_ZERO_OBJECT(&config); config.format = format; config.channels = channels; config.b0 = b0; config.b1 = b1; config.b2 = b2; config.a0 = a0; config.a1 = a1; config.a2 = a2; return config; } typedef struct { size_t sizeInBytes; size_t r1Offset; size_t r2Offset; } ma_biquad_heap_layout; static ma_result ma_biquad_get_heap_layout(const ma_biquad_config* pConfig, ma_biquad_heap_layout* pHeapLayout) { MA_ASSERT(pHeapLayout != NULL); MA_ZERO_OBJECT(pHeapLayout); if (pConfig == NULL) { return MA_INVALID_ARGS; } if (pConfig->channels == 0) { return MA_INVALID_ARGS; } pHeapLayout->sizeInBytes = 0; /* R0 */ pHeapLayout->r1Offset = pHeapLayout->sizeInBytes; pHeapLayout->sizeInBytes += sizeof(ma_biquad_coefficient) * pConfig->channels; /* R1 */ pHeapLayout->r2Offset = pHeapLayout->sizeInBytes; pHeapLayout->sizeInBytes += sizeof(ma_biquad_coefficient) * pConfig->channels; /* Make sure allocation size is aligned. */ pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes); return MA_SUCCESS; } MA_API ma_result ma_biquad_get_heap_size(const ma_biquad_config* pConfig, size_t* pHeapSizeInBytes) { ma_result result; ma_biquad_heap_layout heapLayout; if (pHeapSizeInBytes == NULL) { return MA_INVALID_ARGS; } *pHeapSizeInBytes = 0; result = ma_biquad_get_heap_layout(pConfig, &heapLayout); if (result != MA_SUCCESS) { return result; } *pHeapSizeInBytes = heapLayout.sizeInBytes; return MA_SUCCESS; } MA_API ma_result ma_biquad_init_preallocated(const ma_biquad_config* pConfig, void* pHeap, ma_biquad* pBQ) { ma_result result; ma_biquad_heap_layout heapLayout; if (pBQ == NULL) { return MA_INVALID_ARGS; } MA_ZERO_OBJECT(pBQ); result = ma_biquad_get_heap_layout(pConfig, &heapLayout); if (result != MA_SUCCESS) { return result; } pBQ->_pHeap = pHeap; MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes); pBQ->pR1 = (ma_biquad_coefficient*)ma_offset_ptr(pHeap, heapLayout.r1Offset); pBQ->pR2 = (ma_biquad_coefficient*)ma_offset_ptr(pHeap, heapLayout.r2Offset); return ma_biquad_reinit(pConfig, pBQ); } MA_API ma_result ma_biquad_init(const ma_biquad_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_biquad* pBQ) { ma_result result; size_t heapSizeInBytes; void* pHeap; result = ma_biquad_get_heap_size(pConfig, &heapSizeInBytes); if (result != MA_SUCCESS) { return result; } if (heapSizeInBytes > 0) { pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); if (pHeap == NULL) { return MA_OUT_OF_MEMORY; } } else { pHeap = NULL; } result = ma_biquad_init_preallocated(pConfig, pHeap, pBQ); if (result != MA_SUCCESS) { ma_free(pHeap, pAllocationCallbacks); return result; } pBQ->_ownsHeap = MA_TRUE; return MA_SUCCESS; } MA_API void ma_biquad_uninit(ma_biquad* pBQ, const ma_allocation_callbacks* pAllocationCallbacks) { if (pBQ == NULL) { return; } if (pBQ->_ownsHeap) { ma_free(pBQ->_pHeap, pAllocationCallbacks); } } MA_API ma_result ma_biquad_reinit(const ma_biquad_config* pConfig, ma_biquad* pBQ) { if (pBQ == NULL || pConfig == NULL) { return MA_INVALID_ARGS; } if (pConfig->a0 == 0) { return MA_INVALID_ARGS; /* Division by zero. */ } /* Only supporting f32 and s16. */ if (pConfig->format != ma_format_f32 && pConfig->format != ma_format_s16) { return MA_INVALID_ARGS; } /* The format cannot be changed after initialization. */ if (pBQ->format != ma_format_unknown && pBQ->format != pConfig->format) { return MA_INVALID_OPERATION; } /* The channel count cannot be changed after initialization. */ if (pBQ->channels != 0 && pBQ->channels != pConfig->channels) { return MA_INVALID_OPERATION; } pBQ->format = pConfig->format; pBQ->channels = pConfig->channels; /* Normalize. */ if (pConfig->format == ma_format_f32) { pBQ->b0.f32 = (float)(pConfig->b0 / pConfig->a0); pBQ->b1.f32 = (float)(pConfig->b1 / pConfig->a0); pBQ->b2.f32 = (float)(pConfig->b2 / pConfig->a0); pBQ->a1.f32 = (float)(pConfig->a1 / pConfig->a0); pBQ->a2.f32 = (float)(pConfig->a2 / pConfig->a0); } else { pBQ->b0.s32 = ma_biquad_float_to_fp(pConfig->b0 / pConfig->a0); pBQ->b1.s32 = ma_biquad_float_to_fp(pConfig->b1 / pConfig->a0); pBQ->b2.s32 = ma_biquad_float_to_fp(pConfig->b2 / pConfig->a0); pBQ->a1.s32 = ma_biquad_float_to_fp(pConfig->a1 / pConfig->a0); pBQ->a2.s32 = ma_biquad_float_to_fp(pConfig->a2 / pConfig->a0); } return MA_SUCCESS; } MA_API ma_result ma_biquad_clear_cache(ma_biquad* pBQ) { if (pBQ == NULL) { return MA_INVALID_ARGS; } if (pBQ->format == ma_format_f32) { pBQ->pR1->f32 = 0; pBQ->pR2->f32 = 0; } else { pBQ->pR1->s32 = 0; pBQ->pR2->s32 = 0; } return MA_SUCCESS; } static MA_INLINE void ma_biquad_process_pcm_frame_f32__direct_form_2_transposed(ma_biquad* pBQ, float* pY, const float* pX) { ma_uint32 c; const ma_uint32 channels = pBQ->channels; const float b0 = pBQ->b0.f32; const float b1 = pBQ->b1.f32; const float b2 = pBQ->b2.f32; const float a1 = pBQ->a1.f32; const float a2 = pBQ->a2.f32; MA_ASSUME(channels > 0); for (c = 0; c < channels; c += 1) { float r1 = pBQ->pR1[c].f32; float r2 = pBQ->pR2[c].f32; float x = pX[c]; float y; y = b0*x + r1; r1 = b1*x - a1*y + r2; r2 = b2*x - a2*y; pY[c] = y; pBQ->pR1[c].f32 = r1; pBQ->pR2[c].f32 = r2; } } static MA_INLINE void ma_biquad_process_pcm_frame_f32(ma_biquad* pBQ, float* pY, const float* pX) { ma_biquad_process_pcm_frame_f32__direct_form_2_transposed(pBQ, pY, pX); } static MA_INLINE void ma_biquad_process_pcm_frame_s16__direct_form_2_transposed(ma_biquad* pBQ, ma_int16* pY, const ma_int16* pX) { ma_uint32 c; const ma_uint32 channels = pBQ->channels; const ma_int32 b0 = pBQ->b0.s32; const ma_int32 b1 = pBQ->b1.s32; const ma_int32 b2 = pBQ->b2.s32; const ma_int32 a1 = pBQ->a1.s32; const ma_int32 a2 = pBQ->a2.s32; MA_ASSUME(channels > 0); for (c = 0; c < channels; c += 1) { ma_int32 r1 = pBQ->pR1[c].s32; ma_int32 r2 = pBQ->pR2[c].s32; ma_int32 x = pX[c]; ma_int32 y; y = (b0*x + r1) >> MA_BIQUAD_FIXED_POINT_SHIFT; r1 = (b1*x - a1*y + r2); r2 = (b2*x - a2*y); pY[c] = (ma_int16)ma_clamp(y, -32768, 32767); pBQ->pR1[c].s32 = r1; pBQ->pR2[c].s32 = r2; } } static MA_INLINE void ma_biquad_process_pcm_frame_s16(ma_biquad* pBQ, ma_int16* pY, const ma_int16* pX) { ma_biquad_process_pcm_frame_s16__direct_form_2_transposed(pBQ, pY, pX); } MA_API ma_result ma_biquad_process_pcm_frames(ma_biquad* pBQ, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) { ma_uint32 n; if (pBQ == NULL || pFramesOut == NULL || pFramesIn == NULL) { return MA_INVALID_ARGS; } /* Note that the logic below needs to support in-place filtering. That is, it must support the case where pFramesOut and pFramesIn are the same. */ if (pBQ->format == ma_format_f32) { /* */ float* pY = ( float*)pFramesOut; const float* pX = (const float*)pFramesIn; for (n = 0; n < frameCount; n += 1) { ma_biquad_process_pcm_frame_f32__direct_form_2_transposed(pBQ, pY, pX); pY += pBQ->channels; pX += pBQ->channels; } } else if (pBQ->format == ma_format_s16) { /* */ ma_int16* pY = ( ma_int16*)pFramesOut; const ma_int16* pX = (const ma_int16*)pFramesIn; for (n = 0; n < frameCount; n += 1) { ma_biquad_process_pcm_frame_s16__direct_form_2_transposed(pBQ, pY, pX); pY += pBQ->channels; pX += pBQ->channels; } } else { MA_ASSERT(MA_FALSE); return MA_INVALID_ARGS; /* Format not supported. Should never hit this because it's checked in ma_biquad_init() and ma_biquad_reinit(). */ } return MA_SUCCESS; } MA_API ma_uint32 ma_biquad_get_latency(const ma_biquad* pBQ) { if (pBQ == NULL) { return 0; } return 2; } /************************************************************************************************************************************************************** Low-Pass Filter **************************************************************************************************************************************************************/ MA_API ma_lpf1_config ma_lpf1_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency) { ma_lpf1_config config; MA_ZERO_OBJECT(&config); config.format = format; config.channels = channels; config.sampleRate = sampleRate; config.cutoffFrequency = cutoffFrequency; config.q = 0.5; return config; } MA_API ma_lpf2_config ma_lpf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, double q) { ma_lpf2_config config; MA_ZERO_OBJECT(&config); config.format = format; config.channels = channels; config.sampleRate = sampleRate; config.cutoffFrequency = cutoffFrequency; config.q = q; /* Q cannot be 0 or else it'll result in a division by 0. In this case just default to 0.707107. */ if (config.q == 0) { config.q = 0.707107; } return config; } typedef struct { size_t sizeInBytes; size_t r1Offset; } ma_lpf1_heap_layout; static ma_result ma_lpf1_get_heap_layout(const ma_lpf1_config* pConfig, ma_lpf1_heap_layout* pHeapLayout) { MA_ASSERT(pHeapLayout != NULL); MA_ZERO_OBJECT(pHeapLayout); if (pConfig == NULL) { return MA_INVALID_ARGS; } if (pConfig->channels == 0) { return MA_INVALID_ARGS; } pHeapLayout->sizeInBytes = 0; /* R1 */ pHeapLayout->r1Offset = pHeapLayout->sizeInBytes; pHeapLayout->sizeInBytes += sizeof(ma_biquad_coefficient) * pConfig->channels; /* Make sure allocation size is aligned. */ pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes); return MA_SUCCESS; } MA_API ma_result ma_lpf1_get_heap_size(const ma_lpf1_config* pConfig, size_t* pHeapSizeInBytes) { ma_result result; ma_lpf1_heap_layout heapLayout; if (pHeapSizeInBytes == NULL) { return MA_INVALID_ARGS; } result = ma_lpf1_get_heap_layout(pConfig, &heapLayout); if (result != MA_SUCCESS) { return result; } *pHeapSizeInBytes = heapLayout.sizeInBytes; return MA_SUCCESS; } MA_API ma_result ma_lpf1_init_preallocated(const ma_lpf1_config* pConfig, void* pHeap, ma_lpf1* pLPF) { ma_result result; ma_lpf1_heap_layout heapLayout; if (pLPF == NULL) { return MA_INVALID_ARGS; } MA_ZERO_OBJECT(pLPF); result = ma_lpf1_get_heap_layout(pConfig, &heapLayout); if (result != MA_SUCCESS) { return result; } pLPF->_pHeap = pHeap; MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes); pLPF->pR1 = (ma_biquad_coefficient*)ma_offset_ptr(pHeap, heapLayout.r1Offset); return ma_lpf1_reinit(pConfig, pLPF); } MA_API ma_result ma_lpf1_init(const ma_lpf1_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_lpf1* pLPF) { ma_result result; size_t heapSizeInBytes; void* pHeap; result = ma_lpf1_get_heap_size(pConfig, &heapSizeInBytes); if (result != MA_SUCCESS) { return result; } if (heapSizeInBytes > 0) { pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); if (pHeap == NULL) { return MA_OUT_OF_MEMORY; } } else { pHeap = NULL; } result = ma_lpf1_init_preallocated(pConfig, pHeap, pLPF); if (result != MA_SUCCESS) { ma_free(pHeap, pAllocationCallbacks); return result; } pLPF->_ownsHeap = MA_TRUE; return MA_SUCCESS; } MA_API void ma_lpf1_uninit(ma_lpf1* pLPF, const ma_allocation_callbacks* pAllocationCallbacks) { if (pLPF == NULL) { return; } if (pLPF->_ownsHeap) { ma_free(pLPF->_pHeap, pAllocationCallbacks); } } MA_API ma_result ma_lpf1_reinit(const ma_lpf1_config* pConfig, ma_lpf1* pLPF) { double a; if (pLPF == NULL || pConfig == NULL) { return MA_INVALID_ARGS; } /* Only supporting f32 and s16. */ if (pConfig->format != ma_format_f32 && pConfig->format != ma_format_s16) { return MA_INVALID_ARGS; } /* The format cannot be changed after initialization. */ if (pLPF->format != ma_format_unknown && pLPF->format != pConfig->format) { return MA_INVALID_OPERATION; } /* The channel count cannot be changed after initialization. */ if (pLPF->channels != 0 && pLPF->channels != pConfig->channels) { return MA_INVALID_OPERATION; } pLPF->format = pConfig->format; pLPF->channels = pConfig->channels; a = ma_expd(-2 * MA_PI_D * pConfig->cutoffFrequency / pConfig->sampleRate); if (pConfig->format == ma_format_f32) { pLPF->a.f32 = (float)a; } else { pLPF->a.s32 = ma_biquad_float_to_fp(a); } return MA_SUCCESS; } MA_API ma_result ma_lpf1_clear_cache(ma_lpf1* pLPF) { if (pLPF == NULL) { return MA_INVALID_ARGS; } if (pLPF->format == ma_format_f32) { pLPF->a.f32 = 0; } else { pLPF->a.s32 = 0; } return MA_SUCCESS; } static MA_INLINE void ma_lpf1_process_pcm_frame_f32(ma_lpf1* pLPF, float* pY, const float* pX) { ma_uint32 c; const ma_uint32 channels = pLPF->channels; const float a = pLPF->a.f32; const float b = 1 - a; MA_ASSUME(channels > 0); for (c = 0; c < channels; c += 1) { float r1 = pLPF->pR1[c].f32; float x = pX[c]; float y; y = b*x + a*r1; pY[c] = y; pLPF->pR1[c].f32 = y; } } static MA_INLINE void ma_lpf1_process_pcm_frame_s16(ma_lpf1* pLPF, ma_int16* pY, const ma_int16* pX) { ma_uint32 c; const ma_uint32 channels = pLPF->channels; const ma_int32 a = pLPF->a.s32; const ma_int32 b = ((1 << MA_BIQUAD_FIXED_POINT_SHIFT) - a); MA_ASSUME(channels > 0); for (c = 0; c < channels; c += 1) { ma_int32 r1 = pLPF->pR1[c].s32; ma_int32 x = pX[c]; ma_int32 y; y = (b*x + a*r1) >> MA_BIQUAD_FIXED_POINT_SHIFT; pY[c] = (ma_int16)y; pLPF->pR1[c].s32 = (ma_int32)y; } } MA_API ma_result ma_lpf1_process_pcm_frames(ma_lpf1* pLPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) { ma_uint32 n; if (pLPF == NULL || pFramesOut == NULL || pFramesIn == NULL) { return MA_INVALID_ARGS; } /* Note that the logic below needs to support in-place filtering. That is, it must support the case where pFramesOut and pFramesIn are the same. */ if (pLPF->format == ma_format_f32) { /* */ float* pY = ( float*)pFramesOut; const float* pX = (const float*)pFramesIn; for (n = 0; n < frameCount; n += 1) { ma_lpf1_process_pcm_frame_f32(pLPF, pY, pX); pY += pLPF->channels; pX += pLPF->channels; } } else if (pLPF->format == ma_format_s16) { /* */ ma_int16* pY = ( ma_int16*)pFramesOut; const ma_int16* pX = (const ma_int16*)pFramesIn; for (n = 0; n < frameCount; n += 1) { ma_lpf1_process_pcm_frame_s16(pLPF, pY, pX); pY += pLPF->channels; pX += pLPF->channels; } } else { MA_ASSERT(MA_FALSE); return MA_INVALID_ARGS; /* Format not supported. Should never hit this because it's checked in ma_biquad_init() and ma_biquad_reinit(). */ } return MA_SUCCESS; } MA_API ma_uint32 ma_lpf1_get_latency(const ma_lpf1* pLPF) { if (pLPF == NULL) { return 0; } return 1; } static MA_INLINE ma_biquad_config ma_lpf2__get_biquad_config(const ma_lpf2_config* pConfig) { ma_biquad_config bqConfig; double q; double w; double s; double c; double a; MA_ASSERT(pConfig != NULL); q = pConfig->q; w = 2 * MA_PI_D * pConfig->cutoffFrequency / pConfig->sampleRate; s = ma_sind(w); c = ma_cosd(w); a = s / (2*q); bqConfig.b0 = (1 - c) / 2; bqConfig.b1 = 1 - c; bqConfig.b2 = (1 - c) / 2; bqConfig.a0 = 1 + a; bqConfig.a1 = -2 * c; bqConfig.a2 = 1 - a; bqConfig.format = pConfig->format; bqConfig.channels = pConfig->channels; return bqConfig; } MA_API ma_result ma_lpf2_get_heap_size(const ma_lpf2_config* pConfig, size_t* pHeapSizeInBytes) { ma_biquad_config bqConfig; bqConfig = ma_lpf2__get_biquad_config(pConfig); return ma_biquad_get_heap_size(&bqConfig, pHeapSizeInBytes); } MA_API ma_result ma_lpf2_init_preallocated(const ma_lpf2_config* pConfig, void* pHeap, ma_lpf2* pLPF) { ma_result result; ma_biquad_config bqConfig; if (pLPF == NULL) { return MA_INVALID_ARGS; } MA_ZERO_OBJECT(pLPF); if (pConfig == NULL) { return MA_INVALID_ARGS; } bqConfig = ma_lpf2__get_biquad_config(pConfig); result = ma_biquad_init_preallocated(&bqConfig, pHeap, &pLPF->bq); if (result != MA_SUCCESS) { return result; } return MA_SUCCESS; } MA_API ma_result ma_lpf2_init(const ma_lpf2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_lpf2* pLPF) { ma_result result; size_t heapSizeInBytes; void* pHeap; result = ma_lpf2_get_heap_size(pConfig, &heapSizeInBytes); if (result != MA_SUCCESS) { return result; } if (heapSizeInBytes > 0) { pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); if (pHeap == NULL) { return MA_OUT_OF_MEMORY; } } else { pHeap = NULL; } result = ma_lpf2_init_preallocated(pConfig, pHeap, pLPF); if (result != MA_SUCCESS) { ma_free(pHeap, pAllocationCallbacks); return result; } pLPF->bq._ownsHeap = MA_TRUE; /* <-- This will cause the biquad to take ownership of the heap and free it when it's uninitialized. */ return MA_SUCCESS; } MA_API void ma_lpf2_uninit(ma_lpf2* pLPF, const ma_allocation_callbacks* pAllocationCallbacks) { if (pLPF == NULL) { return; } ma_biquad_uninit(&pLPF->bq, pAllocationCallbacks); /* <-- This will free the heap allocation. */ } MA_API ma_result ma_lpf2_reinit(const ma_lpf2_config* pConfig, ma_lpf2* pLPF) { ma_result result; ma_biquad_config bqConfig; if (pLPF == NULL || pConfig == NULL) { return MA_INVALID_ARGS; } bqConfig = ma_lpf2__get_biquad_config(pConfig); result = ma_biquad_reinit(&bqConfig, &pLPF->bq); if (result != MA_SUCCESS) { return result; } return MA_SUCCESS; } MA_API ma_result ma_lpf2_clear_cache(ma_lpf2* pLPF) { if (pLPF == NULL) { return MA_INVALID_ARGS; } ma_biquad_clear_cache(&pLPF->bq); return MA_SUCCESS; } static MA_INLINE void ma_lpf2_process_pcm_frame_s16(ma_lpf2* pLPF, ma_int16* pFrameOut, const ma_int16* pFrameIn) { ma_biquad_process_pcm_frame_s16(&pLPF->bq, pFrameOut, pFrameIn); } static MA_INLINE void ma_lpf2_process_pcm_frame_f32(ma_lpf2* pLPF, float* pFrameOut, const float* pFrameIn) { ma_biquad_process_pcm_frame_f32(&pLPF->bq, pFrameOut, pFrameIn); } MA_API ma_result ma_lpf2_process_pcm_frames(ma_lpf2* pLPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) { if (pLPF == NULL) { return MA_INVALID_ARGS; } return ma_biquad_process_pcm_frames(&pLPF->bq, pFramesOut, pFramesIn, frameCount); } MA_API ma_uint32 ma_lpf2_get_latency(const ma_lpf2* pLPF) { if (pLPF == NULL) { return 0; } return ma_biquad_get_latency(&pLPF->bq); } MA_API ma_lpf_config ma_lpf_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order) { ma_lpf_config config; MA_ZERO_OBJECT(&config); config.format = format; config.channels = channels; config.sampleRate = sampleRate; config.cutoffFrequency = cutoffFrequency; config.order = ma_min(order, MA_MAX_FILTER_ORDER); return config; } typedef struct { size_t sizeInBytes; size_t lpf1Offset; size_t lpf2Offset; /* Offset of the first second order filter. Subsequent filters will come straight after, and will each have the same heap size. */ } ma_lpf_heap_layout; static void ma_lpf_calculate_sub_lpf_counts(ma_uint32 order, ma_uint32* pLPF1Count, ma_uint32* pLPF2Count) { MA_ASSERT(pLPF1Count != NULL); MA_ASSERT(pLPF2Count != NULL); *pLPF1Count = order % 2; *pLPF2Count = order / 2; } static ma_result ma_lpf_get_heap_layout(const ma_lpf_config* pConfig, ma_lpf_heap_layout* pHeapLayout) { ma_result result; ma_uint32 lpf1Count; ma_uint32 lpf2Count; ma_uint32 ilpf1; ma_uint32 ilpf2; MA_ASSERT(pHeapLayout != NULL); MA_ZERO_OBJECT(pHeapLayout); if (pConfig == NULL) { return MA_INVALID_ARGS; } if (pConfig->channels == 0) { return MA_INVALID_ARGS; } if (pConfig->order > MA_MAX_FILTER_ORDER) { return MA_INVALID_ARGS; } ma_lpf_calculate_sub_lpf_counts(pConfig->order, &lpf1Count, &lpf2Count); pHeapLayout->sizeInBytes = 0; /* LPF 1 */ pHeapLayout->lpf1Offset = pHeapLayout->sizeInBytes; for (ilpf1 = 0; ilpf1 < lpf1Count; ilpf1 += 1) { size_t lpf1HeapSizeInBytes; ma_lpf1_config lpf1Config = ma_lpf1_config_init(pConfig->format, pConfig->channels, pConfig->sampleRate, pConfig->cutoffFrequency); result = ma_lpf1_get_heap_size(&lpf1Config, &lpf1HeapSizeInBytes); if (result != MA_SUCCESS) { return result; } pHeapLayout->sizeInBytes += sizeof(ma_lpf1) + lpf1HeapSizeInBytes; } /* LPF 2*/ pHeapLayout->lpf2Offset = pHeapLayout->sizeInBytes; for (ilpf2 = 0; ilpf2 < lpf2Count; ilpf2 += 1) { size_t lpf2HeapSizeInBytes; ma_lpf2_config lpf2Config = ma_lpf2_config_init(pConfig->format, pConfig->channels, pConfig->sampleRate, pConfig->cutoffFrequency, 0.707107); /* <-- The "q" parameter does not matter for the purpose of calculating the heap size. */ result = ma_lpf2_get_heap_size(&lpf2Config, &lpf2HeapSizeInBytes); if (result != MA_SUCCESS) { return result; } pHeapLayout->sizeInBytes += sizeof(ma_lpf2) + lpf2HeapSizeInBytes; } /* Make sure allocation size is aligned. */ pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes); return MA_SUCCESS; } static ma_result ma_lpf_reinit__internal(const ma_lpf_config* pConfig, void* pHeap, ma_lpf* pLPF, ma_bool32 isNew) { ma_result result; ma_uint32 lpf1Count; ma_uint32 lpf2Count; ma_uint32 ilpf1; ma_uint32 ilpf2; ma_lpf_heap_layout heapLayout; /* Only used if isNew is true. */ if (pLPF == NULL || pConfig == NULL) { return MA_INVALID_ARGS; } /* Only supporting f32 and s16. */ if (pConfig->format != ma_format_f32 && pConfig->format != ma_format_s16) { return MA_INVALID_ARGS; } /* The format cannot be changed after initialization. */ if (pLPF->format != ma_format_unknown && pLPF->format != pConfig->format) { return MA_INVALID_OPERATION; } /* The channel count cannot be changed after initialization. */ if (pLPF->channels != 0 && pLPF->channels != pConfig->channels) { return MA_INVALID_OPERATION; } if (pConfig->order > MA_MAX_FILTER_ORDER) { return MA_INVALID_ARGS; } ma_lpf_calculate_sub_lpf_counts(pConfig->order, &lpf1Count, &lpf2Count); /* The filter order can't change between reinits. */ if (!isNew) { if (pLPF->lpf1Count != lpf1Count || pLPF->lpf2Count != lpf2Count) { return MA_INVALID_OPERATION; } } if (isNew) { result = ma_lpf_get_heap_layout(pConfig, &heapLayout); if (result != MA_SUCCESS) { return result; } pLPF->_pHeap = pHeap; MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes); pLPF->pLPF1 = (ma_lpf1*)ma_offset_ptr(pHeap, heapLayout.lpf1Offset); pLPF->pLPF2 = (ma_lpf2*)ma_offset_ptr(pHeap, heapLayout.lpf2Offset); } else { MA_ZERO_OBJECT(&heapLayout); /* To silence a compiler warning. */ } for (ilpf1 = 0; ilpf1 < lpf1Count; ilpf1 += 1) { ma_lpf1_config lpf1Config = ma_lpf1_config_init(pConfig->format, pConfig->channels, pConfig->sampleRate, pConfig->cutoffFrequency); if (isNew) { size_t lpf1HeapSizeInBytes; result = ma_lpf1_get_heap_size(&lpf1Config, &lpf1HeapSizeInBytes); if (result == MA_SUCCESS) { result = ma_lpf1_init_preallocated(&lpf1Config, ma_offset_ptr(pHeap, heapLayout.lpf1Offset + (sizeof(ma_lpf1) * lpf1Count) + (ilpf1 * lpf1HeapSizeInBytes)), &pLPF->pLPF1[ilpf1]); } } else { result = ma_lpf1_reinit(&lpf1Config, &pLPF->pLPF1[ilpf1]); } if (result != MA_SUCCESS) { ma_uint32 jlpf1; for (jlpf1 = 0; jlpf1 < ilpf1; jlpf1 += 1) { ma_lpf1_uninit(&pLPF->pLPF1[jlpf1], NULL); /* No need for allocation callbacks here since we used a preallocated heap allocation. */ } return result; } } for (ilpf2 = 0; ilpf2 < lpf2Count; ilpf2 += 1) { ma_lpf2_config lpf2Config; double q; double a; /* Tempting to use 0.707107, but won't result in a Butterworth filter if the order is > 2. */ if (lpf1Count == 1) { a = (1 + ilpf2*1) * (MA_PI_D/(pConfig->order*1)); /* Odd order. */ } else { a = (1 + ilpf2*2) * (MA_PI_D/(pConfig->order*2)); /* Even order. */ } q = 1 / (2*ma_cosd(a)); lpf2Config = ma_lpf2_config_init(pConfig->format, pConfig->channels, pConfig->sampleRate, pConfig->cutoffFrequency, q); if (isNew) { size_t lpf2HeapSizeInBytes; result = ma_lpf2_get_heap_size(&lpf2Config, &lpf2HeapSizeInBytes); if (result == MA_SUCCESS) { result = ma_lpf2_init_preallocated(&lpf2Config, ma_offset_ptr(pHeap, heapLayout.lpf2Offset + (sizeof(ma_lpf2) * lpf2Count) + (ilpf2 * lpf2HeapSizeInBytes)), &pLPF->pLPF2[ilpf2]); } } else { result = ma_lpf2_reinit(&lpf2Config, &pLPF->pLPF2[ilpf2]); } if (result != MA_SUCCESS) { ma_uint32 jlpf1; ma_uint32 jlpf2; for (jlpf1 = 0; jlpf1 < lpf1Count; jlpf1 += 1) { ma_lpf1_uninit(&pLPF->pLPF1[jlpf1], NULL); /* No need for allocation callbacks here since we used a preallocated heap allocation. */ } for (jlpf2 = 0; jlpf2 < ilpf2; jlpf2 += 1) { ma_lpf2_uninit(&pLPF->pLPF2[jlpf2], NULL); /* No need for allocation callbacks here since we used a preallocated heap allocation. */ } return result; } } pLPF->lpf1Count = lpf1Count; pLPF->lpf2Count = lpf2Count; pLPF->format = pConfig->format; pLPF->channels = pConfig->channels; pLPF->sampleRate = pConfig->sampleRate; return MA_SUCCESS; } MA_API ma_result ma_lpf_get_heap_size(const ma_lpf_config* pConfig, size_t* pHeapSizeInBytes) { ma_result result; ma_lpf_heap_layout heapLayout; if (pHeapSizeInBytes == NULL) { return MA_INVALID_ARGS; } *pHeapSizeInBytes = 0; result = ma_lpf_get_heap_layout(pConfig, &heapLayout); if (result != MA_SUCCESS) { return result; } *pHeapSizeInBytes = heapLayout.sizeInBytes; return result; } MA_API ma_result ma_lpf_init_preallocated(const ma_lpf_config* pConfig, void* pHeap, ma_lpf* pLPF) { if (pLPF == NULL) { return MA_INVALID_ARGS; } MA_ZERO_OBJECT(pLPF); return ma_lpf_reinit__internal(pConfig, pHeap, pLPF, /*isNew*/MA_TRUE); } MA_API ma_result ma_lpf_init(const ma_lpf_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_lpf* pLPF) { ma_result result; size_t heapSizeInBytes; void* pHeap; result = ma_lpf_get_heap_size(pConfig, &heapSizeInBytes); if (result != MA_SUCCESS) { return result; } if (heapSizeInBytes > 0) { pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); if (pHeap == NULL) { return MA_OUT_OF_MEMORY; } } else { pHeap = NULL; } result = ma_lpf_init_preallocated(pConfig, pHeap, pLPF); if (result != MA_SUCCESS) { ma_free(pHeap, pAllocationCallbacks); return result; } pLPF->_ownsHeap = MA_TRUE; return MA_SUCCESS; } MA_API void ma_lpf_uninit(ma_lpf* pLPF, const ma_allocation_callbacks* pAllocationCallbacks) { ma_uint32 ilpf1; ma_uint32 ilpf2; if (pLPF == NULL) { return; } for (ilpf1 = 0; ilpf1 < pLPF->lpf1Count; ilpf1 += 1) { ma_lpf1_uninit(&pLPF->pLPF1[ilpf1], pAllocationCallbacks); } for (ilpf2 = 0; ilpf2 < pLPF->lpf2Count; ilpf2 += 1) { ma_lpf2_uninit(&pLPF->pLPF2[ilpf2], pAllocationCallbacks); } if (pLPF->_ownsHeap) { ma_free(pLPF->_pHeap, pAllocationCallbacks); } } MA_API ma_result ma_lpf_reinit(const ma_lpf_config* pConfig, ma_lpf* pLPF) { return ma_lpf_reinit__internal(pConfig, NULL, pLPF, /*isNew*/MA_FALSE); } MA_API ma_result ma_lpf_clear_cache(ma_lpf* pLPF) { ma_uint32 ilpf1; ma_uint32 ilpf2; if (pLPF == NULL) { return MA_INVALID_ARGS; } for (ilpf1 = 0; ilpf1 < pLPF->lpf1Count; ilpf1 += 1) { ma_lpf1_clear_cache(&pLPF->pLPF1[ilpf1]); } for (ilpf2 = 0; ilpf2 < pLPF->lpf2Count; ilpf2 += 1) { ma_lpf2_clear_cache(&pLPF->pLPF2[ilpf2]); } return MA_SUCCESS; } static MA_INLINE void ma_lpf_process_pcm_frame_f32(ma_lpf* pLPF, float* pY, const void* pX) { ma_uint32 ilpf1; ma_uint32 ilpf2; MA_ASSERT(pLPF->format == ma_format_f32); MA_COPY_MEMORY(pY, pX, ma_get_bytes_per_frame(pLPF->format, pLPF->channels)); for (ilpf1 = 0; ilpf1 < pLPF->lpf1Count; ilpf1 += 1) { ma_lpf1_process_pcm_frame_f32(&pLPF->pLPF1[ilpf1], pY, pY); } for (ilpf2 = 0; ilpf2 < pLPF->lpf2Count; ilpf2 += 1) { ma_lpf2_process_pcm_frame_f32(&pLPF->pLPF2[ilpf2], pY, pY); } } static MA_INLINE void ma_lpf_process_pcm_frame_s16(ma_lpf* pLPF, ma_int16* pY, const ma_int16* pX) { ma_uint32 ilpf1; ma_uint32 ilpf2; MA_ASSERT(pLPF->format == ma_format_s16); MA_COPY_MEMORY(pY, pX, ma_get_bytes_per_frame(pLPF->format, pLPF->channels)); for (ilpf1 = 0; ilpf1 < pLPF->lpf1Count; ilpf1 += 1) { ma_lpf1_process_pcm_frame_s16(&pLPF->pLPF1[ilpf1], pY, pY); } for (ilpf2 = 0; ilpf2 < pLPF->lpf2Count; ilpf2 += 1) { ma_lpf2_process_pcm_frame_s16(&pLPF->pLPF2[ilpf2], pY, pY); } } MA_API ma_result ma_lpf_process_pcm_frames(ma_lpf* pLPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) { ma_result result; ma_uint32 ilpf1; ma_uint32 ilpf2; if (pLPF == NULL) { return MA_INVALID_ARGS; } /* Faster path for in-place. */ if (pFramesOut == pFramesIn) { for (ilpf1 = 0; ilpf1 < pLPF->lpf1Count; ilpf1 += 1) { result = ma_lpf1_process_pcm_frames(&pLPF->pLPF1[ilpf1], pFramesOut, pFramesOut, frameCount); if (result != MA_SUCCESS) { return result; } } for (ilpf2 = 0; ilpf2 < pLPF->lpf2Count; ilpf2 += 1) { result = ma_lpf2_process_pcm_frames(&pLPF->pLPF2[ilpf2], pFramesOut, pFramesOut, frameCount); if (result != MA_SUCCESS) { return result; } } } /* Slightly slower path for copying. */ if (pFramesOut != pFramesIn) { ma_uint32 iFrame; /* */ if (pLPF->format == ma_format_f32) { /* */ float* pFramesOutF32 = ( float*)pFramesOut; const float* pFramesInF32 = (const float*)pFramesIn; for (iFrame = 0; iFrame < frameCount; iFrame += 1) { ma_lpf_process_pcm_frame_f32(pLPF, pFramesOutF32, pFramesInF32); pFramesOutF32 += pLPF->channels; pFramesInF32 += pLPF->channels; } } else if (pLPF->format == ma_format_s16) { /* */ ma_int16* pFramesOutS16 = ( ma_int16*)pFramesOut; const ma_int16* pFramesInS16 = (const ma_int16*)pFramesIn; for (iFrame = 0; iFrame < frameCount; iFrame += 1) { ma_lpf_process_pcm_frame_s16(pLPF, pFramesOutS16, pFramesInS16); pFramesOutS16 += pLPF->channels; pFramesInS16 += pLPF->channels; } } else { MA_ASSERT(MA_FALSE); return MA_INVALID_OPERATION; /* Should never hit this. */ } } return MA_SUCCESS; } MA_API ma_uint32 ma_lpf_get_latency(const ma_lpf* pLPF) { if (pLPF == NULL) { return 0; } return pLPF->lpf2Count*2 + pLPF->lpf1Count; } /************************************************************************************************************************************************************** High-Pass Filtering **************************************************************************************************************************************************************/ MA_API ma_hpf1_config ma_hpf1_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency) { ma_hpf1_config config; MA_ZERO_OBJECT(&config); config.format = format; config.channels = channels; config.sampleRate = sampleRate; config.cutoffFrequency = cutoffFrequency; return config; } MA_API ma_hpf2_config ma_hpf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, double q) { ma_hpf2_config config; MA_ZERO_OBJECT(&config); config.format = format; config.channels = channels; config.sampleRate = sampleRate; config.cutoffFrequency = cutoffFrequency; config.q = q; /* Q cannot be 0 or else it'll result in a division by 0. In this case just default to 0.707107. */ if (config.q == 0) { config.q = 0.707107; } return config; } typedef struct { size_t sizeInBytes; size_t r1Offset; } ma_hpf1_heap_layout; static ma_result ma_hpf1_get_heap_layout(const ma_hpf1_config* pConfig, ma_hpf1_heap_layout* pHeapLayout) { MA_ASSERT(pHeapLayout != NULL); MA_ZERO_OBJECT(pHeapLayout); if (pConfig == NULL) { return MA_INVALID_ARGS; } if (pConfig->channels == 0) { return MA_INVALID_ARGS; } pHeapLayout->sizeInBytes = 0; /* R1 */ pHeapLayout->r1Offset = pHeapLayout->sizeInBytes; pHeapLayout->sizeInBytes += sizeof(ma_biquad_coefficient) * pConfig->channels; /* Make sure allocation size is aligned. */ pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes); return MA_SUCCESS; } MA_API ma_result ma_hpf1_get_heap_size(const ma_hpf1_config* pConfig, size_t* pHeapSizeInBytes) { ma_result result; ma_hpf1_heap_layout heapLayout; if (pHeapSizeInBytes == NULL) { return MA_INVALID_ARGS; } result = ma_hpf1_get_heap_layout(pConfig, &heapLayout); if (result != MA_SUCCESS) { return result; } *pHeapSizeInBytes = heapLayout.sizeInBytes; return MA_SUCCESS; } MA_API ma_result ma_hpf1_init_preallocated(const ma_hpf1_config* pConfig, void* pHeap, ma_hpf1* pLPF) { ma_result result; ma_hpf1_heap_layout heapLayout; if (pLPF == NULL) { return MA_INVALID_ARGS; } MA_ZERO_OBJECT(pLPF); result = ma_hpf1_get_heap_layout(pConfig, &heapLayout); if (result != MA_SUCCESS) { return result; } pLPF->_pHeap = pHeap; MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes); pLPF->pR1 = (ma_biquad_coefficient*)ma_offset_ptr(pHeap, heapLayout.r1Offset); return ma_hpf1_reinit(pConfig, pLPF); } MA_API ma_result ma_hpf1_init(const ma_hpf1_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hpf1* pLPF) { ma_result result; size_t heapSizeInBytes; void* pHeap; result = ma_hpf1_get_heap_size(pConfig, &heapSizeInBytes); if (result != MA_SUCCESS) { return result; } if (heapSizeInBytes > 0) { pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); if (pHeap == NULL) { return MA_OUT_OF_MEMORY; } } else { pHeap = NULL; } result = ma_hpf1_init_preallocated(pConfig, pHeap, pLPF); if (result != MA_SUCCESS) { ma_free(pHeap, pAllocationCallbacks); return result; } pLPF->_ownsHeap = MA_TRUE; return MA_SUCCESS; } MA_API void ma_hpf1_uninit(ma_hpf1* pHPF, const ma_allocation_callbacks* pAllocationCallbacks) { if (pHPF == NULL) { return; } if (pHPF->_ownsHeap) { ma_free(pHPF->_pHeap, pAllocationCallbacks); } } MA_API ma_result ma_hpf1_reinit(const ma_hpf1_config* pConfig, ma_hpf1* pHPF) { double a; if (pHPF == NULL || pConfig == NULL) { return MA_INVALID_ARGS; } /* Only supporting f32 and s16. */ if (pConfig->format != ma_format_f32 && pConfig->format != ma_format_s16) { return MA_INVALID_ARGS; } /* The format cannot be changed after initialization. */ if (pHPF->format != ma_format_unknown && pHPF->format != pConfig->format) { return MA_INVALID_OPERATION; } /* The channel count cannot be changed after initialization. */ if (pHPF->channels != 0 && pHPF->channels != pConfig->channels) { return MA_INVALID_OPERATION; } pHPF->format = pConfig->format; pHPF->channels = pConfig->channels; a = ma_expd(-2 * MA_PI_D * pConfig->cutoffFrequency / pConfig->sampleRate); if (pConfig->format == ma_format_f32) { pHPF->a.f32 = (float)a; } else { pHPF->a.s32 = ma_biquad_float_to_fp(a); } return MA_SUCCESS; } static MA_INLINE void ma_hpf1_process_pcm_frame_f32(ma_hpf1* pHPF, float* pY, const float* pX) { ma_uint32 c; const ma_uint32 channels = pHPF->channels; const float a = 1 - pHPF->a.f32; const float b = 1 - a; MA_ASSUME(channels > 0); for (c = 0; c < channels; c += 1) { float r1 = pHPF->pR1[c].f32; float x = pX[c]; float y; y = b*x - a*r1; pY[c] = y; pHPF->pR1[c].f32 = y; } } static MA_INLINE void ma_hpf1_process_pcm_frame_s16(ma_hpf1* pHPF, ma_int16* pY, const ma_int16* pX) { ma_uint32 c; const ma_uint32 channels = pHPF->channels; const ma_int32 a = ((1 << MA_BIQUAD_FIXED_POINT_SHIFT) - pHPF->a.s32); const ma_int32 b = ((1 << MA_BIQUAD_FIXED_POINT_SHIFT) - a); MA_ASSUME(channels > 0); for (c = 0; c < channels; c += 1) { ma_int32 r1 = pHPF->pR1[c].s32; ma_int32 x = pX[c]; ma_int32 y; y = (b*x - a*r1) >> MA_BIQUAD_FIXED_POINT_SHIFT; pY[c] = (ma_int16)y; pHPF->pR1[c].s32 = (ma_int32)y; } } MA_API ma_result ma_hpf1_process_pcm_frames(ma_hpf1* pHPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) { ma_uint32 n; if (pHPF == NULL || pFramesOut == NULL || pFramesIn == NULL) { return MA_INVALID_ARGS; } /* Note that the logic below needs to support in-place filtering. That is, it must support the case where pFramesOut and pFramesIn are the same. */ if (pHPF->format == ma_format_f32) { /* */ float* pY = ( float*)pFramesOut; const float* pX = (const float*)pFramesIn; for (n = 0; n < frameCount; n += 1) { ma_hpf1_process_pcm_frame_f32(pHPF, pY, pX); pY += pHPF->channels; pX += pHPF->channels; } } else if (pHPF->format == ma_format_s16) { /* */ ma_int16* pY = ( ma_int16*)pFramesOut; const ma_int16* pX = (const ma_int16*)pFramesIn; for (n = 0; n < frameCount; n += 1) { ma_hpf1_process_pcm_frame_s16(pHPF, pY, pX); pY += pHPF->channels; pX += pHPF->channels; } } else { MA_ASSERT(MA_FALSE); return MA_INVALID_ARGS; /* Format not supported. Should never hit this because it's checked in ma_biquad_init() and ma_biquad_reinit(). */ } return MA_SUCCESS; } MA_API ma_uint32 ma_hpf1_get_latency(const ma_hpf1* pHPF) { if (pHPF == NULL) { return 0; } return 1; } static MA_INLINE ma_biquad_config ma_hpf2__get_biquad_config(const ma_hpf2_config* pConfig) { ma_biquad_config bqConfig; double q; double w; double s; double c; double a; MA_ASSERT(pConfig != NULL); q = pConfig->q; w = 2 * MA_PI_D * pConfig->cutoffFrequency / pConfig->sampleRate; s = ma_sind(w); c = ma_cosd(w); a = s / (2*q); bqConfig.b0 = (1 + c) / 2; bqConfig.b1 = -(1 + c); bqConfig.b2 = (1 + c) / 2; bqConfig.a0 = 1 + a; bqConfig.a1 = -2 * c; bqConfig.a2 = 1 - a; bqConfig.format = pConfig->format; bqConfig.channels = pConfig->channels; return bqConfig; } MA_API ma_result ma_hpf2_get_heap_size(const ma_hpf2_config* pConfig, size_t* pHeapSizeInBytes) { ma_biquad_config bqConfig; bqConfig = ma_hpf2__get_biquad_config(pConfig); return ma_biquad_get_heap_size(&bqConfig, pHeapSizeInBytes); } MA_API ma_result ma_hpf2_init_preallocated(const ma_hpf2_config* pConfig, void* pHeap, ma_hpf2* pHPF) { ma_result result; ma_biquad_config bqConfig; if (pHPF == NULL) { return MA_INVALID_ARGS; } MA_ZERO_OBJECT(pHPF); if (pConfig == NULL) { return MA_INVALID_ARGS; } bqConfig = ma_hpf2__get_biquad_config(pConfig); result = ma_biquad_init_preallocated(&bqConfig, pHeap, &pHPF->bq); if (result != MA_SUCCESS) { return result; } return MA_SUCCESS; } MA_API ma_result ma_hpf2_init(const ma_hpf2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hpf2* pHPF) { ma_result result; size_t heapSizeInBytes; void* pHeap; result = ma_hpf2_get_heap_size(pConfig, &heapSizeInBytes); if (result != MA_SUCCESS) { return result; } if (heapSizeInBytes > 0) { pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); if (pHeap == NULL) { return MA_OUT_OF_MEMORY; } } else { pHeap = NULL; } result = ma_hpf2_init_preallocated(pConfig, pHeap, pHPF); if (result != MA_SUCCESS) { ma_free(pHeap, pAllocationCallbacks); return result; } pHPF->bq._ownsHeap = MA_TRUE; /* <-- This will cause the biquad to take ownership of the heap and free it when it's uninitialized. */ return MA_SUCCESS; } MA_API void ma_hpf2_uninit(ma_hpf2* pHPF, const ma_allocation_callbacks* pAllocationCallbacks) { if (pHPF == NULL) { return; } ma_biquad_uninit(&pHPF->bq, pAllocationCallbacks); /* <-- This will free the heap allocation. */ } MA_API ma_result ma_hpf2_reinit(const ma_hpf2_config* pConfig, ma_hpf2* pHPF) { ma_result result; ma_biquad_config bqConfig; if (pHPF == NULL || pConfig == NULL) { return MA_INVALID_ARGS; } bqConfig = ma_hpf2__get_biquad_config(pConfig); result = ma_biquad_reinit(&bqConfig, &pHPF->bq); if (result != MA_SUCCESS) { return result; } return MA_SUCCESS; } static MA_INLINE void ma_hpf2_process_pcm_frame_s16(ma_hpf2* pHPF, ma_int16* pFrameOut, const ma_int16* pFrameIn) { ma_biquad_process_pcm_frame_s16(&pHPF->bq, pFrameOut, pFrameIn); } static MA_INLINE void ma_hpf2_process_pcm_frame_f32(ma_hpf2* pHPF, float* pFrameOut, const float* pFrameIn) { ma_biquad_process_pcm_frame_f32(&pHPF->bq, pFrameOut, pFrameIn); } MA_API ma_result ma_hpf2_process_pcm_frames(ma_hpf2* pHPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) { if (pHPF == NULL) { return MA_INVALID_ARGS; } return ma_biquad_process_pcm_frames(&pHPF->bq, pFramesOut, pFramesIn, frameCount); } MA_API ma_uint32 ma_hpf2_get_latency(const ma_hpf2* pHPF) { if (pHPF == NULL) { return 0; } return ma_biquad_get_latency(&pHPF->bq); } MA_API ma_hpf_config ma_hpf_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order) { ma_hpf_config config; MA_ZERO_OBJECT(&config); config.format = format; config.channels = channels; config.sampleRate = sampleRate; config.cutoffFrequency = cutoffFrequency; config.order = ma_min(order, MA_MAX_FILTER_ORDER); return config; } typedef struct { size_t sizeInBytes; size_t hpf1Offset; size_t hpf2Offset; /* Offset of the first second order filter. Subsequent filters will come straight after, and will each have the same heap size. */ } ma_hpf_heap_layout; static void ma_hpf_calculate_sub_hpf_counts(ma_uint32 order, ma_uint32* pHPF1Count, ma_uint32* pHPF2Count) { MA_ASSERT(pHPF1Count != NULL); MA_ASSERT(pHPF2Count != NULL); *pHPF1Count = order % 2; *pHPF2Count = order / 2; } static ma_result ma_hpf_get_heap_layout(const ma_hpf_config* pConfig, ma_hpf_heap_layout* pHeapLayout) { ma_result result; ma_uint32 hpf1Count; ma_uint32 hpf2Count; ma_uint32 ihpf1; ma_uint32 ihpf2; MA_ASSERT(pHeapLayout != NULL); MA_ZERO_OBJECT(pHeapLayout); if (pConfig == NULL) { return MA_INVALID_ARGS; } if (pConfig->channels == 0) { return MA_INVALID_ARGS; } if (pConfig->order > MA_MAX_FILTER_ORDER) { return MA_INVALID_ARGS; } ma_hpf_calculate_sub_hpf_counts(pConfig->order, &hpf1Count, &hpf2Count); pHeapLayout->sizeInBytes = 0; /* HPF 1 */ pHeapLayout->hpf1Offset = pHeapLayout->sizeInBytes; for (ihpf1 = 0; ihpf1 < hpf1Count; ihpf1 += 1) { size_t hpf1HeapSizeInBytes; ma_hpf1_config hpf1Config = ma_hpf1_config_init(pConfig->format, pConfig->channels, pConfig->sampleRate, pConfig->cutoffFrequency); result = ma_hpf1_get_heap_size(&hpf1Config, &hpf1HeapSizeInBytes); if (result != MA_SUCCESS) { return result; } pHeapLayout->sizeInBytes += sizeof(ma_hpf1) + hpf1HeapSizeInBytes; } /* HPF 2*/ pHeapLayout->hpf2Offset = pHeapLayout->sizeInBytes; for (ihpf2 = 0; ihpf2 < hpf2Count; ihpf2 += 1) { size_t hpf2HeapSizeInBytes; ma_hpf2_config hpf2Config = ma_hpf2_config_init(pConfig->format, pConfig->channels, pConfig->sampleRate, pConfig->cutoffFrequency, 0.707107); /* <-- The "q" parameter does not matter for the purpose of calculating the heap size. */ result = ma_hpf2_get_heap_size(&hpf2Config, &hpf2HeapSizeInBytes); if (result != MA_SUCCESS) { return result; } pHeapLayout->sizeInBytes += sizeof(ma_hpf2) + hpf2HeapSizeInBytes; } /* Make sure allocation size is aligned. */ pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes); return MA_SUCCESS; } static ma_result ma_hpf_reinit__internal(const ma_hpf_config* pConfig, void* pHeap, ma_hpf* pHPF, ma_bool32 isNew) { ma_result result; ma_uint32 hpf1Count; ma_uint32 hpf2Count; ma_uint32 ihpf1; ma_uint32 ihpf2; ma_hpf_heap_layout heapLayout; /* Only used if isNew is true. */ if (pHPF == NULL || pConfig == NULL) { return MA_INVALID_ARGS; } /* Only supporting f32 and s16. */ if (pConfig->format != ma_format_f32 && pConfig->format != ma_format_s16) { return MA_INVALID_ARGS; } /* The format cannot be changed after initialization. */ if (pHPF->format != ma_format_unknown && pHPF->format != pConfig->format) { return MA_INVALID_OPERATION; } /* The channel count cannot be changed after initialization. */ if (pHPF->channels != 0 && pHPF->channels != pConfig->channels) { return MA_INVALID_OPERATION; } if (pConfig->order > MA_MAX_FILTER_ORDER) { return MA_INVALID_ARGS; } ma_hpf_calculate_sub_hpf_counts(pConfig->order, &hpf1Count, &hpf2Count); /* The filter order can't change between reinits. */ if (!isNew) { if (pHPF->hpf1Count != hpf1Count || pHPF->hpf2Count != hpf2Count) { return MA_INVALID_OPERATION; } } if (isNew) { result = ma_hpf_get_heap_layout(pConfig, &heapLayout); if (result != MA_SUCCESS) { return result; } pHPF->_pHeap = pHeap; MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes); pHPF->pHPF1 = (ma_hpf1*)ma_offset_ptr(pHeap, heapLayout.hpf1Offset); pHPF->pHPF2 = (ma_hpf2*)ma_offset_ptr(pHeap, heapLayout.hpf2Offset); } else { MA_ZERO_OBJECT(&heapLayout); /* To silence a compiler warning. */ } for (ihpf1 = 0; ihpf1 < hpf1Count; ihpf1 += 1) { ma_hpf1_config hpf1Config = ma_hpf1_config_init(pConfig->format, pConfig->channels, pConfig->sampleRate, pConfig->cutoffFrequency); if (isNew) { size_t hpf1HeapSizeInBytes; result = ma_hpf1_get_heap_size(&hpf1Config, &hpf1HeapSizeInBytes); if (result == MA_SUCCESS) { result = ma_hpf1_init_preallocated(&hpf1Config, ma_offset_ptr(pHeap, heapLayout.hpf1Offset + (sizeof(ma_hpf1) * hpf1Count) + (ihpf1 * hpf1HeapSizeInBytes)), &pHPF->pHPF1[ihpf1]); } } else { result = ma_hpf1_reinit(&hpf1Config, &pHPF->pHPF1[ihpf1]); } if (result != MA_SUCCESS) { ma_uint32 jhpf1; for (jhpf1 = 0; jhpf1 < ihpf1; jhpf1 += 1) { ma_hpf1_uninit(&pHPF->pHPF1[jhpf1], NULL); /* No need for allocation callbacks here since we used a preallocated heap allocation. */ } return result; } } for (ihpf2 = 0; ihpf2 < hpf2Count; ihpf2 += 1) { ma_hpf2_config hpf2Config; double q; double a; /* Tempting to use 0.707107, but won't result in a Butterworth filter if the order is > 2. */ if (hpf1Count == 1) { a = (1 + ihpf2*1) * (MA_PI_D/(pConfig->order*1)); /* Odd order. */ } else { a = (1 + ihpf2*2) * (MA_PI_D/(pConfig->order*2)); /* Even order. */ } q = 1 / (2*ma_cosd(a)); hpf2Config = ma_hpf2_config_init(pConfig->format, pConfig->channels, pConfig->sampleRate, pConfig->cutoffFrequency, q); if (isNew) { size_t hpf2HeapSizeInBytes; result = ma_hpf2_get_heap_size(&hpf2Config, &hpf2HeapSizeInBytes); if (result == MA_SUCCESS) { result = ma_hpf2_init_preallocated(&hpf2Config, ma_offset_ptr(pHeap, heapLayout.hpf2Offset + (sizeof(ma_hpf2) * hpf2Count) + (ihpf2 * hpf2HeapSizeInBytes)), &pHPF->pHPF2[ihpf2]); } } else { result = ma_hpf2_reinit(&hpf2Config, &pHPF->pHPF2[ihpf2]); } if (result != MA_SUCCESS) { ma_uint32 jhpf1; ma_uint32 jhpf2; for (jhpf1 = 0; jhpf1 < hpf1Count; jhpf1 += 1) { ma_hpf1_uninit(&pHPF->pHPF1[jhpf1], NULL); /* No need for allocation callbacks here since we used a preallocated heap allocation. */ } for (jhpf2 = 0; jhpf2 < ihpf2; jhpf2 += 1) { ma_hpf2_uninit(&pHPF->pHPF2[jhpf2], NULL); /* No need for allocation callbacks here since we used a preallocated heap allocation. */ } return result; } } pHPF->hpf1Count = hpf1Count; pHPF->hpf2Count = hpf2Count; pHPF->format = pConfig->format; pHPF->channels = pConfig->channels; pHPF->sampleRate = pConfig->sampleRate; return MA_SUCCESS; } MA_API ma_result ma_hpf_get_heap_size(const ma_hpf_config* pConfig, size_t* pHeapSizeInBytes) { ma_result result; ma_hpf_heap_layout heapLayout; if (pHeapSizeInBytes == NULL) { return MA_INVALID_ARGS; } *pHeapSizeInBytes = 0; result = ma_hpf_get_heap_layout(pConfig, &heapLayout); if (result != MA_SUCCESS) { return result; } *pHeapSizeInBytes = heapLayout.sizeInBytes; return result; } MA_API ma_result ma_hpf_init_preallocated(const ma_hpf_config* pConfig, void* pHeap, ma_hpf* pLPF) { if (pLPF == NULL) { return MA_INVALID_ARGS; } MA_ZERO_OBJECT(pLPF); return ma_hpf_reinit__internal(pConfig, pHeap, pLPF, /*isNew*/MA_TRUE); } MA_API ma_result ma_hpf_init(const ma_hpf_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hpf* pHPF) { ma_result result; size_t heapSizeInBytes; void* pHeap; result = ma_hpf_get_heap_size(pConfig, &heapSizeInBytes); if (result != MA_SUCCESS) { return result; } if (heapSizeInBytes > 0) { pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); if (pHeap == NULL) { return MA_OUT_OF_MEMORY; } } else { pHeap = NULL; } result = ma_hpf_init_preallocated(pConfig, pHeap, pHPF); if (result != MA_SUCCESS) { ma_free(pHeap, pAllocationCallbacks); return result; } pHPF->_ownsHeap = MA_TRUE; return MA_SUCCESS; } MA_API void ma_hpf_uninit(ma_hpf* pHPF, const ma_allocation_callbacks* pAllocationCallbacks) { ma_uint32 ihpf1; ma_uint32 ihpf2; if (pHPF == NULL) { return; } for (ihpf1 = 0; ihpf1 < pHPF->hpf1Count; ihpf1 += 1) { ma_hpf1_uninit(&pHPF->pHPF1[ihpf1], pAllocationCallbacks); } for (ihpf2 = 0; ihpf2 < pHPF->hpf2Count; ihpf2 += 1) { ma_hpf2_uninit(&pHPF->pHPF2[ihpf2], pAllocationCallbacks); } if (pHPF->_ownsHeap) { ma_free(pHPF->_pHeap, pAllocationCallbacks); } } MA_API ma_result ma_hpf_reinit(const ma_hpf_config* pConfig, ma_hpf* pHPF) { return ma_hpf_reinit__internal(pConfig, NULL, pHPF, /*isNew*/MA_FALSE); } MA_API ma_result ma_hpf_process_pcm_frames(ma_hpf* pHPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) { ma_result result; ma_uint32 ihpf1; ma_uint32 ihpf2; if (pHPF == NULL) { return MA_INVALID_ARGS; } /* Faster path for in-place. */ if (pFramesOut == pFramesIn) { for (ihpf1 = 0; ihpf1 < pHPF->hpf1Count; ihpf1 += 1) { result = ma_hpf1_process_pcm_frames(&pHPF->pHPF1[ihpf1], pFramesOut, pFramesOut, frameCount); if (result != MA_SUCCESS) { return result; } } for (ihpf2 = 0; ihpf2 < pHPF->hpf2Count; ihpf2 += 1) { result = ma_hpf2_process_pcm_frames(&pHPF->pHPF2[ihpf2], pFramesOut, pFramesOut, frameCount); if (result != MA_SUCCESS) { return result; } } } /* Slightly slower path for copying. */ if (pFramesOut != pFramesIn) { ma_uint32 iFrame; /* */ if (pHPF->format == ma_format_f32) { /* */ float* pFramesOutF32 = ( float*)pFramesOut; const float* pFramesInF32 = (const float*)pFramesIn; for (iFrame = 0; iFrame < frameCount; iFrame += 1) { MA_COPY_MEMORY(pFramesOutF32, pFramesInF32, ma_get_bytes_per_frame(pHPF->format, pHPF->channels)); for (ihpf1 = 0; ihpf1 < pHPF->hpf1Count; ihpf1 += 1) { ma_hpf1_process_pcm_frame_f32(&pHPF->pHPF1[ihpf1], pFramesOutF32, pFramesOutF32); } for (ihpf2 = 0; ihpf2 < pHPF->hpf2Count; ihpf2 += 1) { ma_hpf2_process_pcm_frame_f32(&pHPF->pHPF2[ihpf2], pFramesOutF32, pFramesOutF32); } pFramesOutF32 += pHPF->channels; pFramesInF32 += pHPF->channels; } } else if (pHPF->format == ma_format_s16) { /* */ ma_int16* pFramesOutS16 = ( ma_int16*)pFramesOut; const ma_int16* pFramesInS16 = (const ma_int16*)pFramesIn; for (iFrame = 0; iFrame < frameCount; iFrame += 1) { MA_COPY_MEMORY(pFramesOutS16, pFramesInS16, ma_get_bytes_per_frame(pHPF->format, pHPF->channels)); for (ihpf1 = 0; ihpf1 < pHPF->hpf1Count; ihpf1 += 1) { ma_hpf1_process_pcm_frame_s16(&pHPF->pHPF1[ihpf1], pFramesOutS16, pFramesOutS16); } for (ihpf2 = 0; ihpf2 < pHPF->hpf2Count; ihpf2 += 1) { ma_hpf2_process_pcm_frame_s16(&pHPF->pHPF2[ihpf2], pFramesOutS16, pFramesOutS16); } pFramesOutS16 += pHPF->channels; pFramesInS16 += pHPF->channels; } } else { MA_ASSERT(MA_FALSE); return MA_INVALID_OPERATION; /* Should never hit this. */ } } return MA_SUCCESS; } MA_API ma_uint32 ma_hpf_get_latency(const ma_hpf* pHPF) { if (pHPF == NULL) { return 0; } return pHPF->hpf2Count*2 + pHPF->hpf1Count; } /************************************************************************************************************************************************************** Band-Pass Filtering **************************************************************************************************************************************************************/ MA_API ma_bpf2_config ma_bpf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, double q) { ma_bpf2_config config; MA_ZERO_OBJECT(&config); config.format = format; config.channels = channels; config.sampleRate = sampleRate; config.cutoffFrequency = cutoffFrequency; config.q = q; /* Q cannot be 0 or else it'll result in a division by 0. In this case just default to 0.707107. */ if (config.q == 0) { config.q = 0.707107; } return config; } static MA_INLINE ma_biquad_config ma_bpf2__get_biquad_config(const ma_bpf2_config* pConfig) { ma_biquad_config bqConfig; double q; double w; double s; double c; double a; MA_ASSERT(pConfig != NULL); q = pConfig->q; w = 2 * MA_PI_D * pConfig->cutoffFrequency / pConfig->sampleRate; s = ma_sind(w); c = ma_cosd(w); a = s / (2*q); bqConfig.b0 = q * a; bqConfig.b1 = 0; bqConfig.b2 = -q * a; bqConfig.a0 = 1 + a; bqConfig.a1 = -2 * c; bqConfig.a2 = 1 - a; bqConfig.format = pConfig->format; bqConfig.channels = pConfig->channels; return bqConfig; } MA_API ma_result ma_bpf2_get_heap_size(const ma_bpf2_config* pConfig, size_t* pHeapSizeInBytes) { ma_biquad_config bqConfig; bqConfig = ma_bpf2__get_biquad_config(pConfig); return ma_biquad_get_heap_size(&bqConfig, pHeapSizeInBytes); } MA_API ma_result ma_bpf2_init_preallocated(const ma_bpf2_config* pConfig, void* pHeap, ma_bpf2* pBPF) { ma_result result; ma_biquad_config bqConfig; if (pBPF == NULL) { return MA_INVALID_ARGS; } MA_ZERO_OBJECT(pBPF); if (pConfig == NULL) { return MA_INVALID_ARGS; } bqConfig = ma_bpf2__get_biquad_config(pConfig); result = ma_biquad_init_preallocated(&bqConfig, pHeap, &pBPF->bq); if (result != MA_SUCCESS) { return result; } return MA_SUCCESS; } MA_API ma_result ma_bpf2_init(const ma_bpf2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_bpf2* pBPF) { ma_result result; size_t heapSizeInBytes; void* pHeap; result = ma_bpf2_get_heap_size(pConfig, &heapSizeInBytes); if (result != MA_SUCCESS) { return result; } if (heapSizeInBytes > 0) { pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); if (pHeap == NULL) { return MA_OUT_OF_MEMORY; } } else { pHeap = NULL; } result = ma_bpf2_init_preallocated(pConfig, pHeap, pBPF); if (result != MA_SUCCESS) { ma_free(pHeap, pAllocationCallbacks); return result; } pBPF->bq._ownsHeap = MA_TRUE; /* <-- This will cause the biquad to take ownership of the heap and free it when it's uninitialized. */ return MA_SUCCESS; } MA_API void ma_bpf2_uninit(ma_bpf2* pBPF, const ma_allocation_callbacks* pAllocationCallbacks) { if (pBPF == NULL) { return; } ma_biquad_uninit(&pBPF->bq, pAllocationCallbacks); /* <-- This will free the heap allocation. */ } MA_API ma_result ma_bpf2_reinit(const ma_bpf2_config* pConfig, ma_bpf2* pBPF) { ma_result result; ma_biquad_config bqConfig; if (pBPF == NULL || pConfig == NULL) { return MA_INVALID_ARGS; } bqConfig = ma_bpf2__get_biquad_config(pConfig); result = ma_biquad_reinit(&bqConfig, &pBPF->bq); if (result != MA_SUCCESS) { return result; } return MA_SUCCESS; } static MA_INLINE void ma_bpf2_process_pcm_frame_s16(ma_bpf2* pBPF, ma_int16* pFrameOut, const ma_int16* pFrameIn) { ma_biquad_process_pcm_frame_s16(&pBPF->bq, pFrameOut, pFrameIn); } static MA_INLINE void ma_bpf2_process_pcm_frame_f32(ma_bpf2* pBPF, float* pFrameOut, const float* pFrameIn) { ma_biquad_process_pcm_frame_f32(&pBPF->bq, pFrameOut, pFrameIn); } MA_API ma_result ma_bpf2_process_pcm_frames(ma_bpf2* pBPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) { if (pBPF == NULL) { return MA_INVALID_ARGS; } return ma_biquad_process_pcm_frames(&pBPF->bq, pFramesOut, pFramesIn, frameCount); } MA_API ma_uint32 ma_bpf2_get_latency(const ma_bpf2* pBPF) { if (pBPF == NULL) { return 0; } return ma_biquad_get_latency(&pBPF->bq); } MA_API ma_bpf_config ma_bpf_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order) { ma_bpf_config config; MA_ZERO_OBJECT(&config); config.format = format; config.channels = channels; config.sampleRate = sampleRate; config.cutoffFrequency = cutoffFrequency; config.order = ma_min(order, MA_MAX_FILTER_ORDER); return config; } typedef struct { size_t sizeInBytes; size_t bpf2Offset; } ma_bpf_heap_layout; static ma_result ma_bpf_get_heap_layout(const ma_bpf_config* pConfig, ma_bpf_heap_layout* pHeapLayout) { ma_result result; ma_uint32 bpf2Count; ma_uint32 ibpf2; MA_ASSERT(pHeapLayout != NULL); MA_ZERO_OBJECT(pHeapLayout); if (pConfig == NULL) { return MA_INVALID_ARGS; } if (pConfig->order > MA_MAX_FILTER_ORDER) { return MA_INVALID_ARGS; } /* We must have an even number of order. */ if ((pConfig->order & 0x1) != 0) { return MA_INVALID_ARGS; } bpf2Count = pConfig->channels / 2; pHeapLayout->sizeInBytes = 0; /* BPF 2 */ pHeapLayout->bpf2Offset = pHeapLayout->sizeInBytes; for (ibpf2 = 0; ibpf2 < bpf2Count; ibpf2 += 1) { size_t bpf2HeapSizeInBytes; ma_bpf2_config bpf2Config = ma_bpf2_config_init(pConfig->format, pConfig->channels, pConfig->sampleRate, pConfig->cutoffFrequency, 0.707107); /* <-- The "q" parameter does not matter for the purpose of calculating the heap size. */ result = ma_bpf2_get_heap_size(&bpf2Config, &bpf2HeapSizeInBytes); if (result != MA_SUCCESS) { return result; } pHeapLayout->sizeInBytes += sizeof(ma_bpf2) + bpf2HeapSizeInBytes; } /* Make sure allocation size is aligned. */ pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes); return MA_SUCCESS; } static ma_result ma_bpf_reinit__internal(const ma_bpf_config* pConfig, void* pHeap, ma_bpf* pBPF, ma_bool32 isNew) { ma_result result; ma_uint32 bpf2Count; ma_uint32 ibpf2; ma_bpf_heap_layout heapLayout; /* Only used if isNew is true. */ if (pBPF == NULL || pConfig == NULL) { return MA_INVALID_ARGS; } /* Only supporting f32 and s16. */ if (pConfig->format != ma_format_f32 && pConfig->format != ma_format_s16) { return MA_INVALID_ARGS; } /* The format cannot be changed after initialization. */ if (pBPF->format != ma_format_unknown && pBPF->format != pConfig->format) { return MA_INVALID_OPERATION; } /* The channel count cannot be changed after initialization. */ if (pBPF->channels != 0 && pBPF->channels != pConfig->channels) { return MA_INVALID_OPERATION; } if (pConfig->order > MA_MAX_FILTER_ORDER) { return MA_INVALID_ARGS; } /* We must have an even number of order. */ if ((pConfig->order & 0x1) != 0) { return MA_INVALID_ARGS; } bpf2Count = pConfig->order / 2; /* The filter order can't change between reinits. */ if (!isNew) { if (pBPF->bpf2Count != bpf2Count) { return MA_INVALID_OPERATION; } } if (isNew) { result = ma_bpf_get_heap_layout(pConfig, &heapLayout); if (result != MA_SUCCESS) { return result; } pBPF->_pHeap = pHeap; MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes); pBPF->pBPF2 = (ma_bpf2*)ma_offset_ptr(pHeap, heapLayout.bpf2Offset); } else { MA_ZERO_OBJECT(&heapLayout); } for (ibpf2 = 0; ibpf2 < bpf2Count; ibpf2 += 1) { ma_bpf2_config bpf2Config; double q; /* TODO: Calculate Q to make this a proper Butterworth filter. */ q = 0.707107; bpf2Config = ma_bpf2_config_init(pConfig->format, pConfig->channels, pConfig->sampleRate, pConfig->cutoffFrequency, q); if (isNew) { size_t bpf2HeapSizeInBytes; result = ma_bpf2_get_heap_size(&bpf2Config, &bpf2HeapSizeInBytes); if (result == MA_SUCCESS) { result = ma_bpf2_init_preallocated(&bpf2Config, ma_offset_ptr(pHeap, heapLayout.bpf2Offset + (sizeof(ma_bpf2) * bpf2Count) + (ibpf2 * bpf2HeapSizeInBytes)), &pBPF->pBPF2[ibpf2]); } } else { result = ma_bpf2_reinit(&bpf2Config, &pBPF->pBPF2[ibpf2]); } if (result != MA_SUCCESS) { return result; } } pBPF->bpf2Count = bpf2Count; pBPF->format = pConfig->format; pBPF->channels = pConfig->channels; return MA_SUCCESS; } MA_API ma_result ma_bpf_get_heap_size(const ma_bpf_config* pConfig, size_t* pHeapSizeInBytes) { ma_result result; ma_bpf_heap_layout heapLayout; if (pHeapSizeInBytes == NULL) { return MA_INVALID_ARGS; } *pHeapSizeInBytes = 0; result = ma_bpf_get_heap_layout(pConfig, &heapLayout); if (result != MA_SUCCESS) { return result; } *pHeapSizeInBytes = heapLayout.sizeInBytes; return MA_SUCCESS; } MA_API ma_result ma_bpf_init_preallocated(const ma_bpf_config* pConfig, void* pHeap, ma_bpf* pBPF) { if (pBPF == NULL) { return MA_INVALID_ARGS; } MA_ZERO_OBJECT(pBPF); return ma_bpf_reinit__internal(pConfig, pHeap, pBPF, /*isNew*/MA_TRUE); } MA_API ma_result ma_bpf_init(const ma_bpf_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_bpf* pBPF) { ma_result result; size_t heapSizeInBytes; void* pHeap; result = ma_bpf_get_heap_size(pConfig, &heapSizeInBytes); if (result != MA_SUCCESS) { return result; } if (heapSizeInBytes > 0) { pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); if (pHeap == NULL) { return MA_OUT_OF_MEMORY; } } else { pHeap = NULL; } result = ma_bpf_init_preallocated(pConfig, pHeap, pBPF); if (result != MA_SUCCESS) { ma_free(pHeap, pAllocationCallbacks); return result; } pBPF->_ownsHeap = MA_TRUE; return MA_SUCCESS; } MA_API void ma_bpf_uninit(ma_bpf* pBPF, const ma_allocation_callbacks* pAllocationCallbacks) { ma_uint32 ibpf2; if (pBPF == NULL) { return; } for (ibpf2 = 0; ibpf2 < pBPF->bpf2Count; ibpf2 += 1) { ma_bpf2_uninit(&pBPF->pBPF2[ibpf2], pAllocationCallbacks); } if (pBPF->_ownsHeap) { ma_free(pBPF->_pHeap, pAllocationCallbacks); } } MA_API ma_result ma_bpf_reinit(const ma_bpf_config* pConfig, ma_bpf* pBPF) { return ma_bpf_reinit__internal(pConfig, NULL, pBPF, /*isNew*/MA_FALSE); } MA_API ma_result ma_bpf_process_pcm_frames(ma_bpf* pBPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) { ma_result result; ma_uint32 ibpf2; if (pBPF == NULL) { return MA_INVALID_ARGS; } /* Faster path for in-place. */ if (pFramesOut == pFramesIn) { for (ibpf2 = 0; ibpf2 < pBPF->bpf2Count; ibpf2 += 1) { result = ma_bpf2_process_pcm_frames(&pBPF->pBPF2[ibpf2], pFramesOut, pFramesOut, frameCount); if (result != MA_SUCCESS) { return result; } } } /* Slightly slower path for copying. */ if (pFramesOut != pFramesIn) { ma_uint32 iFrame; /* */ if (pBPF->format == ma_format_f32) { /* */ float* pFramesOutF32 = ( float*)pFramesOut; const float* pFramesInF32 = (const float*)pFramesIn; for (iFrame = 0; iFrame < frameCount; iFrame += 1) { MA_COPY_MEMORY(pFramesOutF32, pFramesInF32, ma_get_bytes_per_frame(pBPF->format, pBPF->channels)); for (ibpf2 = 0; ibpf2 < pBPF->bpf2Count; ibpf2 += 1) { ma_bpf2_process_pcm_frame_f32(&pBPF->pBPF2[ibpf2], pFramesOutF32, pFramesOutF32); } pFramesOutF32 += pBPF->channels; pFramesInF32 += pBPF->channels; } } else if (pBPF->format == ma_format_s16) { /* */ ma_int16* pFramesOutS16 = ( ma_int16*)pFramesOut; const ma_int16* pFramesInS16 = (const ma_int16*)pFramesIn; for (iFrame = 0; iFrame < frameCount; iFrame += 1) { MA_COPY_MEMORY(pFramesOutS16, pFramesInS16, ma_get_bytes_per_frame(pBPF->format, pBPF->channels)); for (ibpf2 = 0; ibpf2 < pBPF->bpf2Count; ibpf2 += 1) { ma_bpf2_process_pcm_frame_s16(&pBPF->pBPF2[ibpf2], pFramesOutS16, pFramesOutS16); } pFramesOutS16 += pBPF->channels; pFramesInS16 += pBPF->channels; } } else { MA_ASSERT(MA_FALSE); return MA_INVALID_OPERATION; /* Should never hit this. */ } } return MA_SUCCESS; } MA_API ma_uint32 ma_bpf_get_latency(const ma_bpf* pBPF) { if (pBPF == NULL) { return 0; } return pBPF->bpf2Count*2; } /************************************************************************************************************************************************************** Notching Filter **************************************************************************************************************************************************************/ MA_API ma_notch2_config ma_notch2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double q, double frequency) { ma_notch2_config config; MA_ZERO_OBJECT(&config); config.format = format; config.channels = channels; config.sampleRate = sampleRate; config.q = q; config.frequency = frequency; if (config.q == 0) { config.q = 0.707107; } return config; } static MA_INLINE ma_biquad_config ma_notch2__get_biquad_config(const ma_notch2_config* pConfig) { ma_biquad_config bqConfig; double q; double w; double s; double c; double a; MA_ASSERT(pConfig != NULL); q = pConfig->q; w = 2 * MA_PI_D * pConfig->frequency / pConfig->sampleRate; s = ma_sind(w); c = ma_cosd(w); a = s / (2*q); bqConfig.b0 = 1; bqConfig.b1 = -2 * c; bqConfig.b2 = 1; bqConfig.a0 = 1 + a; bqConfig.a1 = -2 * c; bqConfig.a2 = 1 - a; bqConfig.format = pConfig->format; bqConfig.channels = pConfig->channels; return bqConfig; } MA_API ma_result ma_notch2_get_heap_size(const ma_notch2_config* pConfig, size_t* pHeapSizeInBytes) { ma_biquad_config bqConfig; bqConfig = ma_notch2__get_biquad_config(pConfig); return ma_biquad_get_heap_size(&bqConfig, pHeapSizeInBytes); } MA_API ma_result ma_notch2_init_preallocated(const ma_notch2_config* pConfig, void* pHeap, ma_notch2* pFilter) { ma_result result; ma_biquad_config bqConfig; if (pFilter == NULL) { return MA_INVALID_ARGS; } MA_ZERO_OBJECT(pFilter); if (pConfig == NULL) { return MA_INVALID_ARGS; } bqConfig = ma_notch2__get_biquad_config(pConfig); result = ma_biquad_init_preallocated(&bqConfig, pHeap, &pFilter->bq); if (result != MA_SUCCESS) { return result; } return MA_SUCCESS; } MA_API ma_result ma_notch2_init(const ma_notch2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_notch2* pFilter) { ma_result result; size_t heapSizeInBytes; void* pHeap; result = ma_notch2_get_heap_size(pConfig, &heapSizeInBytes); if (result != MA_SUCCESS) { return result; } if (heapSizeInBytes > 0) { pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); if (pHeap == NULL) { return MA_OUT_OF_MEMORY; } } else { pHeap = NULL; } result = ma_notch2_init_preallocated(pConfig, pHeap, pFilter); if (result != MA_SUCCESS) { ma_free(pHeap, pAllocationCallbacks); return result; } pFilter->bq._ownsHeap = MA_TRUE; /* <-- This will cause the biquad to take ownership of the heap and free it when it's uninitialized. */ return MA_SUCCESS; } MA_API void ma_notch2_uninit(ma_notch2* pFilter, const ma_allocation_callbacks* pAllocationCallbacks) { if (pFilter == NULL) { return; } ma_biquad_uninit(&pFilter->bq, pAllocationCallbacks); /* <-- This will free the heap allocation. */ } MA_API ma_result ma_notch2_reinit(const ma_notch2_config* pConfig, ma_notch2* pFilter) { ma_result result; ma_biquad_config bqConfig; if (pFilter == NULL || pConfig == NULL) { return MA_INVALID_ARGS; } bqConfig = ma_notch2__get_biquad_config(pConfig); result = ma_biquad_reinit(&bqConfig, &pFilter->bq); if (result != MA_SUCCESS) { return result; } return MA_SUCCESS; } static MA_INLINE void ma_notch2_process_pcm_frame_s16(ma_notch2* pFilter, ma_int16* pFrameOut, const ma_int16* pFrameIn) { ma_biquad_process_pcm_frame_s16(&pFilter->bq, pFrameOut, pFrameIn); } static MA_INLINE void ma_notch2_process_pcm_frame_f32(ma_notch2* pFilter, float* pFrameOut, const float* pFrameIn) { ma_biquad_process_pcm_frame_f32(&pFilter->bq, pFrameOut, pFrameIn); } MA_API ma_result ma_notch2_process_pcm_frames(ma_notch2* pFilter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) { if (pFilter == NULL) { return MA_INVALID_ARGS; } return ma_biquad_process_pcm_frames(&pFilter->bq, pFramesOut, pFramesIn, frameCount); } MA_API ma_uint32 ma_notch2_get_latency(const ma_notch2* pFilter) { if (pFilter == NULL) { return 0; } return ma_biquad_get_latency(&pFilter->bq); } /************************************************************************************************************************************************************** Peaking EQ Filter **************************************************************************************************************************************************************/ MA_API ma_peak2_config ma_peak2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double q, double frequency) { ma_peak2_config config; MA_ZERO_OBJECT(&config); config.format = format; config.channels = channels; config.sampleRate = sampleRate; config.gainDB = gainDB; config.q = q; config.frequency = frequency; if (config.q == 0) { config.q = 0.707107; } return config; } static MA_INLINE ma_biquad_config ma_peak2__get_biquad_config(const ma_peak2_config* pConfig) { ma_biquad_config bqConfig; double q; double w; double s; double c; double a; double A; MA_ASSERT(pConfig != NULL); q = pConfig->q; w = 2 * MA_PI_D * pConfig->frequency / pConfig->sampleRate; s = ma_sind(w); c = ma_cosd(w); a = s / (2*q); A = ma_powd(10, (pConfig->gainDB / 40)); bqConfig.b0 = 1 + (a * A); bqConfig.b1 = -2 * c; bqConfig.b2 = 1 - (a * A); bqConfig.a0 = 1 + (a / A); bqConfig.a1 = -2 * c; bqConfig.a2 = 1 - (a / A); bqConfig.format = pConfig->format; bqConfig.channels = pConfig->channels; return bqConfig; } MA_API ma_result ma_peak2_get_heap_size(const ma_peak2_config* pConfig, size_t* pHeapSizeInBytes) { ma_biquad_config bqConfig; bqConfig = ma_peak2__get_biquad_config(pConfig); return ma_biquad_get_heap_size(&bqConfig, pHeapSizeInBytes); } MA_API ma_result ma_peak2_init_preallocated(const ma_peak2_config* pConfig, void* pHeap, ma_peak2* pFilter) { ma_result result; ma_biquad_config bqConfig; if (pFilter == NULL) { return MA_INVALID_ARGS; } MA_ZERO_OBJECT(pFilter); if (pConfig == NULL) { return MA_INVALID_ARGS; } bqConfig = ma_peak2__get_biquad_config(pConfig); result = ma_biquad_init_preallocated(&bqConfig, pHeap, &pFilter->bq); if (result != MA_SUCCESS) { return result; } return MA_SUCCESS; } MA_API ma_result ma_peak2_init(const ma_peak2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_peak2* pFilter) { ma_result result; size_t heapSizeInBytes; void* pHeap; result = ma_peak2_get_heap_size(pConfig, &heapSizeInBytes); if (result != MA_SUCCESS) { return result; } if (heapSizeInBytes > 0) { pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); if (pHeap == NULL) { return MA_OUT_OF_MEMORY; } } else { pHeap = NULL; } result = ma_peak2_init_preallocated(pConfig, pHeap, pFilter); if (result != MA_SUCCESS) { ma_free(pHeap, pAllocationCallbacks); return result; } pFilter->bq._ownsHeap = MA_TRUE; /* <-- This will cause the biquad to take ownership of the heap and free it when it's uninitialized. */ return MA_SUCCESS; } MA_API void ma_peak2_uninit(ma_peak2* pFilter, const ma_allocation_callbacks* pAllocationCallbacks) { if (pFilter == NULL) { return; } ma_biquad_uninit(&pFilter->bq, pAllocationCallbacks); /* <-- This will free the heap allocation. */ } MA_API ma_result ma_peak2_reinit(const ma_peak2_config* pConfig, ma_peak2* pFilter) { ma_result result; ma_biquad_config bqConfig; if (pFilter == NULL || pConfig == NULL) { return MA_INVALID_ARGS; } bqConfig = ma_peak2__get_biquad_config(pConfig); result = ma_biquad_reinit(&bqConfig, &pFilter->bq); if (result != MA_SUCCESS) { return result; } return MA_SUCCESS; } static MA_INLINE void ma_peak2_process_pcm_frame_s16(ma_peak2* pFilter, ma_int16* pFrameOut, const ma_int16* pFrameIn) { ma_biquad_process_pcm_frame_s16(&pFilter->bq, pFrameOut, pFrameIn); } static MA_INLINE void ma_peak2_process_pcm_frame_f32(ma_peak2* pFilter, float* pFrameOut, const float* pFrameIn) { ma_biquad_process_pcm_frame_f32(&pFilter->bq, pFrameOut, pFrameIn); } MA_API ma_result ma_peak2_process_pcm_frames(ma_peak2* pFilter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) { if (pFilter == NULL) { return MA_INVALID_ARGS; } return ma_biquad_process_pcm_frames(&pFilter->bq, pFramesOut, pFramesIn, frameCount); } MA_API ma_uint32 ma_peak2_get_latency(const ma_peak2* pFilter) { if (pFilter == NULL) { return 0; } return ma_biquad_get_latency(&pFilter->bq); } /************************************************************************************************************************************************************** Low Shelf Filter **************************************************************************************************************************************************************/ MA_API ma_loshelf2_config ma_loshelf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double shelfSlope, double frequency) { ma_loshelf2_config config; MA_ZERO_OBJECT(&config); config.format = format; config.channels = channels; config.sampleRate = sampleRate; config.gainDB = gainDB; config.shelfSlope = shelfSlope; config.frequency = frequency; return config; } static MA_INLINE ma_biquad_config ma_loshelf2__get_biquad_config(const ma_loshelf2_config* pConfig) { ma_biquad_config bqConfig; double w; double s; double c; double A; double S; double a; double sqrtA; MA_ASSERT(pConfig != NULL); w = 2 * MA_PI_D * pConfig->frequency / pConfig->sampleRate; s = ma_sind(w); c = ma_cosd(w); A = ma_powd(10, (pConfig->gainDB / 40)); S = pConfig->shelfSlope; a = s/2 * ma_sqrtd((A + 1/A) * (1/S - 1) + 2); sqrtA = 2*ma_sqrtd(A)*a; bqConfig.b0 = A * ((A + 1) - (A - 1)*c + sqrtA); bqConfig.b1 = 2 * A * ((A - 1) - (A + 1)*c); bqConfig.b2 = A * ((A + 1) - (A - 1)*c - sqrtA); bqConfig.a0 = (A + 1) + (A - 1)*c + sqrtA; bqConfig.a1 = -2 * ((A - 1) + (A + 1)*c); bqConfig.a2 = (A + 1) + (A - 1)*c - sqrtA; bqConfig.format = pConfig->format; bqConfig.channels = pConfig->channels; return bqConfig; } MA_API ma_result ma_loshelf2_get_heap_size(const ma_loshelf2_config* pConfig, size_t* pHeapSizeInBytes) { ma_biquad_config bqConfig; bqConfig = ma_loshelf2__get_biquad_config(pConfig); return ma_biquad_get_heap_size(&bqConfig, pHeapSizeInBytes); } MA_API ma_result ma_loshelf2_init_preallocated(const ma_loshelf2_config* pConfig, void* pHeap, ma_loshelf2* pFilter) { ma_result result; ma_biquad_config bqConfig; if (pFilter == NULL) { return MA_INVALID_ARGS; } MA_ZERO_OBJECT(pFilter); if (pConfig == NULL) { return MA_INVALID_ARGS; } bqConfig = ma_loshelf2__get_biquad_config(pConfig); result = ma_biquad_init_preallocated(&bqConfig, pHeap, &pFilter->bq); if (result != MA_SUCCESS) { return result; } return MA_SUCCESS; } MA_API ma_result ma_loshelf2_init(const ma_loshelf2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_loshelf2* pFilter) { ma_result result; size_t heapSizeInBytes; void* pHeap; result = ma_loshelf2_get_heap_size(pConfig, &heapSizeInBytes); if (result != MA_SUCCESS) { return result; } if (heapSizeInBytes > 0) { pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); if (pHeap == NULL) { return MA_OUT_OF_MEMORY; } } else { pHeap = NULL; } result = ma_loshelf2_init_preallocated(pConfig, pHeap, pFilter); if (result != MA_SUCCESS) { ma_free(pHeap, pAllocationCallbacks); return result; } pFilter->bq._ownsHeap = MA_TRUE; /* <-- This will cause the biquad to take ownership of the heap and free it when it's uninitialized. */ return MA_SUCCESS; } MA_API void ma_loshelf2_uninit(ma_loshelf2* pFilter, const ma_allocation_callbacks* pAllocationCallbacks) { if (pFilter == NULL) { return; } ma_biquad_uninit(&pFilter->bq, pAllocationCallbacks); /* <-- This will free the heap allocation. */ } MA_API ma_result ma_loshelf2_reinit(const ma_loshelf2_config* pConfig, ma_loshelf2* pFilter) { ma_result result; ma_biquad_config bqConfig; if (pFilter == NULL || pConfig == NULL) { return MA_INVALID_ARGS; } bqConfig = ma_loshelf2__get_biquad_config(pConfig); result = ma_biquad_reinit(&bqConfig, &pFilter->bq); if (result != MA_SUCCESS) { return result; } return MA_SUCCESS; } static MA_INLINE void ma_loshelf2_process_pcm_frame_s16(ma_loshelf2* pFilter, ma_int16* pFrameOut, const ma_int16* pFrameIn) { ma_biquad_process_pcm_frame_s16(&pFilter->bq, pFrameOut, pFrameIn); } static MA_INLINE void ma_loshelf2_process_pcm_frame_f32(ma_loshelf2* pFilter, float* pFrameOut, const float* pFrameIn) { ma_biquad_process_pcm_frame_f32(&pFilter->bq, pFrameOut, pFrameIn); } MA_API ma_result ma_loshelf2_process_pcm_frames(ma_loshelf2* pFilter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) { if (pFilter == NULL) { return MA_INVALID_ARGS; } return ma_biquad_process_pcm_frames(&pFilter->bq, pFramesOut, pFramesIn, frameCount); } MA_API ma_uint32 ma_loshelf2_get_latency(const ma_loshelf2* pFilter) { if (pFilter == NULL) { return 0; } return ma_biquad_get_latency(&pFilter->bq); } /************************************************************************************************************************************************************** High Shelf Filter **************************************************************************************************************************************************************/ MA_API ma_hishelf2_config ma_hishelf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double shelfSlope, double frequency) { ma_hishelf2_config config; MA_ZERO_OBJECT(&config); config.format = format; config.channels = channels; config.sampleRate = sampleRate; config.gainDB = gainDB; config.shelfSlope = shelfSlope; config.frequency = frequency; return config; } static MA_INLINE ma_biquad_config ma_hishelf2__get_biquad_config(const ma_hishelf2_config* pConfig) { ma_biquad_config bqConfig; double w; double s; double c; double A; double S; double a; double sqrtA; MA_ASSERT(pConfig != NULL); w = 2 * MA_PI_D * pConfig->frequency / pConfig->sampleRate; s = ma_sind(w); c = ma_cosd(w); A = ma_powd(10, (pConfig->gainDB / 40)); S = pConfig->shelfSlope; a = s/2 * ma_sqrtd((A + 1/A) * (1/S - 1) + 2); sqrtA = 2*ma_sqrtd(A)*a; bqConfig.b0 = A * ((A + 1) + (A - 1)*c + sqrtA); bqConfig.b1 = -2 * A * ((A - 1) + (A + 1)*c); bqConfig.b2 = A * ((A + 1) + (A - 1)*c - sqrtA); bqConfig.a0 = (A + 1) - (A - 1)*c + sqrtA; bqConfig.a1 = 2 * ((A - 1) - (A + 1)*c); bqConfig.a2 = (A + 1) - (A - 1)*c - sqrtA; bqConfig.format = pConfig->format; bqConfig.channels = pConfig->channels; return bqConfig; } MA_API ma_result ma_hishelf2_get_heap_size(const ma_hishelf2_config* pConfig, size_t* pHeapSizeInBytes) { ma_biquad_config bqConfig; bqConfig = ma_hishelf2__get_biquad_config(pConfig); return ma_biquad_get_heap_size(&bqConfig, pHeapSizeInBytes); } MA_API ma_result ma_hishelf2_init_preallocated(const ma_hishelf2_config* pConfig, void* pHeap, ma_hishelf2* pFilter) { ma_result result; ma_biquad_config bqConfig; if (pFilter == NULL) { return MA_INVALID_ARGS; } MA_ZERO_OBJECT(pFilter); if (pConfig == NULL) { return MA_INVALID_ARGS; } bqConfig = ma_hishelf2__get_biquad_config(pConfig); result = ma_biquad_init_preallocated(&bqConfig, pHeap, &pFilter->bq); if (result != MA_SUCCESS) { return result; } return MA_SUCCESS; } MA_API ma_result ma_hishelf2_init(const ma_hishelf2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hishelf2* pFilter) { ma_result result; size_t heapSizeInBytes; void* pHeap; result = ma_hishelf2_get_heap_size(pConfig, &heapSizeInBytes); if (result != MA_SUCCESS) { return result; } if (heapSizeInBytes > 0) { pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); if (pHeap == NULL) { return MA_OUT_OF_MEMORY; } } else { pHeap = NULL; } result = ma_hishelf2_init_preallocated(pConfig, pHeap, pFilter); if (result != MA_SUCCESS) { ma_free(pHeap, pAllocationCallbacks); return result; } pFilter->bq._ownsHeap = MA_TRUE; /* <-- This will cause the biquad to take ownership of the heap and free it when it's uninitialized. */ return MA_SUCCESS; } MA_API void ma_hishelf2_uninit(ma_hishelf2* pFilter, const ma_allocation_callbacks* pAllocationCallbacks) { if (pFilter == NULL) { return; } ma_biquad_uninit(&pFilter->bq, pAllocationCallbacks); /* <-- This will free the heap allocation. */ } MA_API ma_result ma_hishelf2_reinit(const ma_hishelf2_config* pConfig, ma_hishelf2* pFilter) { ma_result result; ma_biquad_config bqConfig; if (pFilter == NULL || pConfig == NULL) { return MA_INVALID_ARGS; } bqConfig = ma_hishelf2__get_biquad_config(pConfig); result = ma_biquad_reinit(&bqConfig, &pFilter->bq); if (result != MA_SUCCESS) { return result; } return MA_SUCCESS; } static MA_INLINE void ma_hishelf2_process_pcm_frame_s16(ma_hishelf2* pFilter, ma_int16* pFrameOut, const ma_int16* pFrameIn) { ma_biquad_process_pcm_frame_s16(&pFilter->bq, pFrameOut, pFrameIn); } static MA_INLINE void ma_hishelf2_process_pcm_frame_f32(ma_hishelf2* pFilter, float* pFrameOut, const float* pFrameIn) { ma_biquad_process_pcm_frame_f32(&pFilter->bq, pFrameOut, pFrameIn); } MA_API ma_result ma_hishelf2_process_pcm_frames(ma_hishelf2* pFilter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) { if (pFilter == NULL) { return MA_INVALID_ARGS; } return ma_biquad_process_pcm_frames(&pFilter->bq, pFramesOut, pFramesIn, frameCount); } MA_API ma_uint32 ma_hishelf2_get_latency(const ma_hishelf2* pFilter) { if (pFilter == NULL) { return 0; } return ma_biquad_get_latency(&pFilter->bq); } /* Delay */ MA_API ma_delay_config ma_delay_config_init(ma_uint32 channels, ma_uint32 sampleRate, ma_uint32 delayInFrames, float decay) { ma_delay_config config; MA_ZERO_OBJECT(&config); config.channels = channels; config.sampleRate = sampleRate; config.delayInFrames = delayInFrames; config.delayStart = (decay == 0) ? MA_TRUE : MA_FALSE; /* Delay the start if it looks like we're not configuring an echo. */ config.wet = 1; config.dry = 1; config.decay = decay; return config; } MA_API ma_result ma_delay_init(const ma_delay_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_delay* pDelay) { if (pDelay == NULL) { return MA_INVALID_ARGS; } MA_ZERO_OBJECT(pDelay); if (pConfig == NULL) { return MA_INVALID_ARGS; } if (pConfig->decay < 0 || pConfig->decay > 1) { return MA_INVALID_ARGS; } pDelay->config = *pConfig; pDelay->bufferSizeInFrames = pConfig->delayInFrames; pDelay->cursor = 0; pDelay->pBuffer = (float*)ma_malloc((size_t)(pDelay->bufferSizeInFrames * ma_get_bytes_per_frame(ma_format_f32, pConfig->channels)), pAllocationCallbacks); if (pDelay->pBuffer == NULL) { return MA_OUT_OF_MEMORY; } ma_silence_pcm_frames(pDelay->pBuffer, pDelay->bufferSizeInFrames, ma_format_f32, pConfig->channels); return MA_SUCCESS; } MA_API void ma_delay_uninit(ma_delay* pDelay, const ma_allocation_callbacks* pAllocationCallbacks) { if (pDelay == NULL) { return; } ma_free(pDelay->pBuffer, pAllocationCallbacks); } MA_API ma_result ma_delay_process_pcm_frames(ma_delay* pDelay, void* pFramesOut, const void* pFramesIn, ma_uint32 frameCount) { ma_uint32 iFrame; ma_uint32 iChannel; float* pFramesOutF32 = (float*)pFramesOut; const float* pFramesInF32 = (const float*)pFramesIn; if (pDelay == NULL || pFramesOut == NULL || pFramesIn == NULL) { return MA_INVALID_ARGS; } for (iFrame = 0; iFrame < frameCount; iFrame += 1) { for (iChannel = 0; iChannel < pDelay->config.channels; iChannel += 1) { ma_uint32 iBuffer = (pDelay->cursor * pDelay->config.channels) + iChannel; if (pDelay->config.delayStart) { /* Delayed start. */ /* Read */ pFramesOutF32[iChannel] = pDelay->pBuffer[iBuffer] * pDelay->config.wet; /* Feedback */ pDelay->pBuffer[iBuffer] = (pDelay->pBuffer[iBuffer] * pDelay->config.decay) + (pFramesInF32[iChannel] * pDelay->config.dry); } else { /* Immediate start */ /* Feedback */ pDelay->pBuffer[iBuffer] = (pDelay->pBuffer[iBuffer] * pDelay->config.decay) + (pFramesInF32[iChannel] * pDelay->config.dry); /* Read */ pFramesOutF32[iChannel] = pDelay->pBuffer[iBuffer] * pDelay->config.wet; } } pDelay->cursor = (pDelay->cursor + 1) % pDelay->bufferSizeInFrames; pFramesOutF32 += pDelay->config.channels; pFramesInF32 += pDelay->config.channels; } return MA_SUCCESS; } MA_API void ma_delay_set_wet(ma_delay* pDelay, float value) { if (pDelay == NULL) { return; } pDelay->config.wet = value; } MA_API float ma_delay_get_wet(const ma_delay* pDelay) { if (pDelay == NULL) { return 0; } return pDelay->config.wet; } MA_API void ma_delay_set_dry(ma_delay* pDelay, float value) { if (pDelay == NULL) { return; } pDelay->config.dry = value; } MA_API float ma_delay_get_dry(const ma_delay* pDelay) { if (pDelay == NULL) { return 0; } return pDelay->config.dry; } MA_API void ma_delay_set_decay(ma_delay* pDelay, float value) { if (pDelay == NULL) { return; } pDelay->config.decay = value; } MA_API float ma_delay_get_decay(const ma_delay* pDelay) { if (pDelay == NULL) { return 0; } return pDelay->config.decay; } MA_API ma_gainer_config ma_gainer_config_init(ma_uint32 channels, ma_uint32 smoothTimeInFrames) { ma_gainer_config config; MA_ZERO_OBJECT(&config); config.channels = channels; config.smoothTimeInFrames = smoothTimeInFrames; return config; } typedef struct { size_t sizeInBytes; size_t oldGainsOffset; size_t newGainsOffset; } ma_gainer_heap_layout; static ma_result ma_gainer_get_heap_layout(const ma_gainer_config* pConfig, ma_gainer_heap_layout* pHeapLayout) { MA_ASSERT(pHeapLayout != NULL); MA_ZERO_OBJECT(pHeapLayout); if (pConfig == NULL) { return MA_INVALID_ARGS; } if (pConfig->channels == 0) { return MA_INVALID_ARGS; } pHeapLayout->sizeInBytes = 0; /* Old gains. */ pHeapLayout->oldGainsOffset = pHeapLayout->sizeInBytes; pHeapLayout->sizeInBytes += sizeof(float) * pConfig->channels; /* New gains. */ pHeapLayout->newGainsOffset = pHeapLayout->sizeInBytes; pHeapLayout->sizeInBytes += sizeof(float) * pConfig->channels; /* Alignment. */ pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes); return MA_SUCCESS; } MA_API ma_result ma_gainer_get_heap_size(const ma_gainer_config* pConfig, size_t* pHeapSizeInBytes) { ma_result result; ma_gainer_heap_layout heapLayout; if (pHeapSizeInBytes == NULL) { return MA_INVALID_ARGS; } *pHeapSizeInBytes = 0; result = ma_gainer_get_heap_layout(pConfig, &heapLayout); if (result != MA_SUCCESS) { return MA_INVALID_ARGS; } *pHeapSizeInBytes = heapLayout.sizeInBytes; return MA_SUCCESS; } MA_API ma_result ma_gainer_init_preallocated(const ma_gainer_config* pConfig, void* pHeap, ma_gainer* pGainer) { ma_result result; ma_gainer_heap_layout heapLayout; ma_uint32 iChannel; if (pGainer == NULL) { return MA_INVALID_ARGS; } MA_ZERO_OBJECT(pGainer); if (pConfig == NULL || pHeap == NULL) { return MA_INVALID_ARGS; } result = ma_gainer_get_heap_layout(pConfig, &heapLayout); if (result != MA_SUCCESS) { return result; } pGainer->_pHeap = pHeap; MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes); pGainer->pOldGains = (float*)ma_offset_ptr(pHeap, heapLayout.oldGainsOffset); pGainer->pNewGains = (float*)ma_offset_ptr(pHeap, heapLayout.newGainsOffset); pGainer->config = *pConfig; pGainer->t = (ma_uint32)-1; /* No interpolation by default. */ for (iChannel = 0; iChannel < pConfig->channels; iChannel += 1) { pGainer->pOldGains[iChannel] = 1; pGainer->pNewGains[iChannel] = 1; } return MA_SUCCESS; } MA_API ma_result ma_gainer_init(const ma_gainer_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_gainer* pGainer) { ma_result result; size_t heapSizeInBytes; void* pHeap; result = ma_gainer_get_heap_size(pConfig, &heapSizeInBytes); if (result != MA_SUCCESS) { return result; /* Failed to retrieve the size of the heap allocation. */ } if (heapSizeInBytes > 0) { pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); if (pHeap == NULL) { return MA_OUT_OF_MEMORY; } } else { pHeap = NULL; } result = ma_gainer_init_preallocated(pConfig, pHeap, pGainer); if (result != MA_SUCCESS) { ma_free(pHeap, pAllocationCallbacks); return result; } pGainer->_ownsHeap = MA_TRUE; return MA_SUCCESS; } MA_API void ma_gainer_uninit(ma_gainer* pGainer, const ma_allocation_callbacks* pAllocationCallbacks) { if (pGainer == NULL) { return; } if (pGainer->_ownsHeap) { ma_free(pGainer->_pHeap, pAllocationCallbacks); } } static float ma_gainer_calculate_current_gain(const ma_gainer* pGainer, ma_uint32 channel) { float a = (float)pGainer->t / pGainer->config.smoothTimeInFrames; return ma_mix_f32_fast(pGainer->pOldGains[channel], pGainer->pNewGains[channel], a); } MA_API ma_result ma_gainer_process_pcm_frames(ma_gainer* pGainer, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) { ma_uint64 iFrame; ma_uint32 iChannel; float* pFramesOutF32 = (float*)pFramesOut; const float* pFramesInF32 = (const float*)pFramesIn; if (pGainer == NULL) { return MA_INVALID_ARGS; } if (pGainer->t >= pGainer->config.smoothTimeInFrames) { /* Fast path. No gain calculation required. */ ma_copy_and_apply_volume_factor_per_channel_f32(pFramesOutF32, pFramesInF32, frameCount, pGainer->config.channels, pGainer->pNewGains); /* Now that some frames have been processed we need to make sure future changes to the gain are interpolated. */ if (pGainer->t == (ma_uint32)-1) { pGainer->t = pGainer->config.smoothTimeInFrames; } } else { /* Slow path. Need to interpolate the gain for each channel individually. */ /* We can allow the input and output buffers to be null in which case we'll just update the internal timer. */ if (pFramesOut != NULL && pFramesIn != NULL) { float a = (float)pGainer->t / pGainer->config.smoothTimeInFrames; float d = 1.0f / pGainer->config.smoothTimeInFrames; ma_uint32 channelCount = pGainer->config.channels; for (iFrame = 0; iFrame < frameCount; iFrame += 1) { for (iChannel = 0; iChannel < channelCount; iChannel += 1) { pFramesOutF32[iChannel] = pFramesInF32[iChannel] * ma_mix_f32_fast(pGainer->pOldGains[iChannel], pGainer->pNewGains[iChannel], a); } pFramesOutF32 += channelCount; pFramesInF32 += channelCount; a += d; if (a > 1) { a = 1; } } } pGainer->t = (ma_uint32)ma_min(pGainer->t + frameCount, pGainer->config.smoothTimeInFrames); #if 0 /* Reference implementation. */ for (iFrame = 0; iFrame < frameCount; iFrame += 1) { /* We can allow the input and output buffers to be null in which case we'll just update the internal timer. */ if (pFramesOut != NULL && pFramesIn != NULL) { for (iChannel = 0; iChannel < pGainer->config.channels; iChannel += 1) { pFramesOutF32[iFrame*pGainer->config.channels + iChannel] = pFramesInF32[iFrame*pGainer->config.channels + iChannel] * ma_gainer_calculate_current_gain(pGainer, iChannel); } } /* Move interpolation time forward, but don't go beyond our smoothing time. */ pGainer->t = ma_min(pGainer->t + 1, pGainer->config.smoothTimeInFrames); } #endif } return MA_SUCCESS; } static void ma_gainer_set_gain_by_index(ma_gainer* pGainer, float newGain, ma_uint32 iChannel) { pGainer->pOldGains[iChannel] = ma_gainer_calculate_current_gain(pGainer, iChannel); pGainer->pNewGains[iChannel] = newGain; } static void ma_gainer_reset_smoothing_time(ma_gainer* pGainer) { if (pGainer->t == (ma_uint32)-1) { pGainer->t = pGainer->config.smoothTimeInFrames; /* No smoothing required for initial gains setting. */ } else { pGainer->t = 0; } } MA_API ma_result ma_gainer_set_gain(ma_gainer* pGainer, float newGain) { ma_uint32 iChannel; if (pGainer == NULL) { return MA_INVALID_ARGS; } for (iChannel = 0; iChannel < pGainer->config.channels; iChannel += 1) { ma_gainer_set_gain_by_index(pGainer, newGain, iChannel); } /* The smoothing time needs to be reset to ensure we always interpolate by the configured smoothing time, but only if it's not the first setting. */ ma_gainer_reset_smoothing_time(pGainer); return MA_SUCCESS; } MA_API ma_result ma_gainer_set_gains(ma_gainer* pGainer, float* pNewGains) { ma_uint32 iChannel; if (pGainer == NULL || pNewGains == NULL) { return MA_INVALID_ARGS; } for (iChannel = 0; iChannel < pGainer->config.channels; iChannel += 1) { ma_gainer_set_gain_by_index(pGainer, pNewGains[iChannel], iChannel); } /* The smoothing time needs to be reset to ensure we always interpolate by the configured smoothing time, but only if it's not the first setting. */ ma_gainer_reset_smoothing_time(pGainer); return MA_SUCCESS; } MA_API ma_panner_config ma_panner_config_init(ma_format format, ma_uint32 channels) { ma_panner_config config; MA_ZERO_OBJECT(&config); config.format = format; config.channels = channels; config.mode = ma_pan_mode_balance; /* Set to balancing mode by default because it's consistent with other audio engines and most likely what the caller is expecting. */ config.pan = 0; return config; } MA_API ma_result ma_panner_init(const ma_panner_config* pConfig, ma_panner* pPanner) { if (pPanner == NULL) { return MA_INVALID_ARGS; } MA_ZERO_OBJECT(pPanner); if (pConfig == NULL) { return MA_INVALID_ARGS; } pPanner->format = pConfig->format; pPanner->channels = pConfig->channels; pPanner->mode = pConfig->mode; pPanner->pan = pConfig->pan; return MA_SUCCESS; } static void ma_stereo_balance_pcm_frames_f32(float* pFramesOut, const float* pFramesIn, ma_uint64 frameCount, float pan) { ma_uint64 iFrame; if (pan > 0) { float factor = 1.0f - pan; if (pFramesOut == pFramesIn) { for (iFrame = 0; iFrame < frameCount; iFrame += 1) { pFramesOut[iFrame*2 + 0] = pFramesIn[iFrame*2 + 0] * factor; } } else { for (iFrame = 0; iFrame < frameCount; iFrame += 1) { pFramesOut[iFrame*2 + 0] = pFramesIn[iFrame*2 + 0] * factor; pFramesOut[iFrame*2 + 1] = pFramesIn[iFrame*2 + 1]; } } } else { float factor = 1.0f + pan; if (pFramesOut == pFramesIn) { for (iFrame = 0; iFrame < frameCount; iFrame += 1) { pFramesOut[iFrame*2 + 1] = pFramesIn[iFrame*2 + 1] * factor; } } else { for (iFrame = 0; iFrame < frameCount; iFrame += 1) { pFramesOut[iFrame*2 + 0] = pFramesIn[iFrame*2 + 0]; pFramesOut[iFrame*2 + 1] = pFramesIn[iFrame*2 + 1] * factor; } } } } static void ma_stereo_balance_pcm_frames(void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount, ma_format format, float pan) { if (pan == 0) { /* Fast path. No panning required. */ if (pFramesOut == pFramesIn) { /* No-op */ } else { ma_copy_pcm_frames(pFramesOut, pFramesIn, frameCount, format, 2); } return; } switch (format) { case ma_format_f32: ma_stereo_balance_pcm_frames_f32((float*)pFramesOut, (float*)pFramesIn, frameCount, pan); break; /* Unknown format. Just copy. */ default: { ma_copy_pcm_frames(pFramesOut, pFramesIn, frameCount, format, 2); } break; } } static void ma_stereo_pan_pcm_frames_f32(float* pFramesOut, const float* pFramesIn, ma_uint64 frameCount, float pan) { ma_uint64 iFrame; if (pan > 0) { float factorL0 = 1.0f - pan; float factorL1 = 0.0f + pan; for (iFrame = 0; iFrame < frameCount; iFrame += 1) { float sample0 = (pFramesIn[iFrame*2 + 0] * factorL0); float sample1 = (pFramesIn[iFrame*2 + 0] * factorL1) + pFramesIn[iFrame*2 + 1]; pFramesOut[iFrame*2 + 0] = sample0; pFramesOut[iFrame*2 + 1] = sample1; } } else { float factorR0 = 0.0f - pan; float factorR1 = 1.0f + pan; for (iFrame = 0; iFrame < frameCount; iFrame += 1) { float sample0 = pFramesIn[iFrame*2 + 0] + (pFramesIn[iFrame*2 + 1] * factorR0); float sample1 = (pFramesIn[iFrame*2 + 1] * factorR1); pFramesOut[iFrame*2 + 0] = sample0; pFramesOut[iFrame*2 + 1] = sample1; } } } static void ma_stereo_pan_pcm_frames(void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount, ma_format format, float pan) { if (pan == 0) { /* Fast path. No panning required. */ if (pFramesOut == pFramesIn) { /* No-op */ } else { ma_copy_pcm_frames(pFramesOut, pFramesIn, frameCount, format, 2); } return; } switch (format) { case ma_format_f32: ma_stereo_pan_pcm_frames_f32((float*)pFramesOut, (float*)pFramesIn, frameCount, pan); break; /* Unknown format. Just copy. */ default: { ma_copy_pcm_frames(pFramesOut, pFramesIn, frameCount, format, 2); } break; } } MA_API ma_result ma_panner_process_pcm_frames(ma_panner* pPanner, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) { if (pPanner == NULL || pFramesOut == NULL || pFramesIn == NULL) { return MA_INVALID_ARGS; } if (pPanner->channels == 2) { /* Stereo case. For now assume channel 0 is left and channel right is 1, but should probably add support for a channel map. */ if (pPanner->mode == ma_pan_mode_balance) { ma_stereo_balance_pcm_frames(pFramesOut, pFramesIn, frameCount, pPanner->format, pPanner->pan); } else { ma_stereo_pan_pcm_frames(pFramesOut, pFramesIn, frameCount, pPanner->format, pPanner->pan); } } else { if (pPanner->channels == 1) { /* Panning has no effect on mono streams. */ ma_copy_pcm_frames(pFramesOut, pFramesIn, frameCount, pPanner->format, pPanner->channels); } else { /* For now we're not going to support non-stereo set ups. Not sure how I want to handle this case just yet. */ ma_copy_pcm_frames(pFramesOut, pFramesIn, frameCount, pPanner->format, pPanner->channels); } } return MA_SUCCESS; } MA_API void ma_panner_set_mode(ma_panner* pPanner, ma_pan_mode mode) { if (pPanner == NULL) { return; } pPanner->mode = mode; } MA_API ma_pan_mode ma_panner_get_mode(const ma_panner* pPanner) { if (pPanner == NULL) { return ma_pan_mode_balance; } return pPanner->mode; } MA_API void ma_panner_set_pan(ma_panner* pPanner, float pan) { if (pPanner == NULL) { return; } pPanner->pan = ma_clamp(pan, -1.0f, 1.0f); } MA_API float ma_panner_get_pan(const ma_panner* pPanner) { if (pPanner == NULL) { return 0; } return pPanner->pan; } MA_API ma_fader_config ma_fader_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate) { ma_fader_config config; MA_ZERO_OBJECT(&config); config.format = format; config.channels = channels; config.sampleRate = sampleRate; return config; } MA_API ma_result ma_fader_init(const ma_fader_config* pConfig, ma_fader* pFader) { if (pFader == NULL) { return MA_INVALID_ARGS; } MA_ZERO_OBJECT(pFader); if (pConfig == NULL) { return MA_INVALID_ARGS; } /* Only f32 is supported for now. */ if (pConfig->format != ma_format_f32) { return MA_INVALID_ARGS; } pFader->config = *pConfig; pFader->volumeBeg = 1; pFader->volumeEnd = 1; pFader->lengthInFrames = 0; pFader->cursorInFrames = 0; return MA_SUCCESS; } MA_API ma_result ma_fader_process_pcm_frames(ma_fader* pFader, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) { if (pFader == NULL) { return MA_INVALID_ARGS; } /* For now we need to clamp frameCount so that the cursor never overflows 32-bits. This is required for the conversion to a float which we use for the linear interpolation. This might be changed later. */ if (frameCount + pFader->cursorInFrames > UINT_MAX) { frameCount = UINT_MAX - pFader->cursorInFrames; } /* Optimized path if volumeBeg and volumeEnd are equal. */ if (pFader->volumeBeg == pFader->volumeEnd) { if (pFader->volumeBeg == 1) { /* Straight copy. */ ma_copy_pcm_frames(pFramesOut, pFramesIn, frameCount, pFader->config.format, pFader->config.channels); } else { /* Copy with volume. */ ma_copy_and_apply_volume_and_clip_pcm_frames(pFramesOut, pFramesIn, frameCount, pFader->config.format, pFader->config.channels, pFader->volumeEnd); } } else { /* Slower path. Volumes are different, so may need to do an interpolation. */ if (pFader->cursorInFrames >= pFader->lengthInFrames) { /* Fast path. We've gone past the end of the fade period so just apply the end volume to all samples. */ ma_copy_and_apply_volume_and_clip_pcm_frames(pFramesOut, pFramesIn, frameCount, pFader->config.format, pFader->config.channels, pFader->volumeEnd); } else { /* Slow path. This is where we do the actual fading. */ ma_uint64 iFrame; ma_uint32 iChannel; /* For now we only support f32. Support for other formats will be added later. */ if (pFader->config.format == ma_format_f32) { const float* pFramesInF32 = (const float*)pFramesIn; /* */ float* pFramesOutF32 = ( float*)pFramesOut; for (iFrame = 0; iFrame < frameCount; iFrame += 1) { float a = (ma_uint32)ma_min(pFader->cursorInFrames + iFrame, pFader->lengthInFrames) / (float)((ma_uint32)pFader->lengthInFrames); /* Safe cast due to the frameCount clamp at the top of this function. */ float volume = ma_mix_f32_fast(pFader->volumeBeg, pFader->volumeEnd, a); for (iChannel = 0; iChannel < pFader->config.channels; iChannel += 1) { pFramesOutF32[iFrame*pFader->config.channels + iChannel] = pFramesInF32[iFrame*pFader->config.channels + iChannel] * volume; } } } else { return MA_NOT_IMPLEMENTED; } } } pFader->cursorInFrames += frameCount; return MA_SUCCESS; } MA_API void ma_fader_get_data_format(const ma_fader* pFader, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate) { if (pFader == NULL) { return; } if (pFormat != NULL) { *pFormat = pFader->config.format; } if (pChannels != NULL) { *pChannels = pFader->config.channels; } if (pSampleRate != NULL) { *pSampleRate = pFader->config.sampleRate; } } MA_API void ma_fader_set_fade(ma_fader* pFader, float volumeBeg, float volumeEnd, ma_uint64 lengthInFrames) { if (pFader == NULL) { return; } /* If the volume is negative, use current volume. */ if (volumeBeg < 0) { volumeBeg = ma_fader_get_current_volume(pFader); } /* The length needs to be clamped to 32-bits due to how we convert it to a float for linear interpolation reasons. I might change this requirement later, but for now it's not important. */ if (lengthInFrames > UINT_MAX) { lengthInFrames = UINT_MAX; } pFader->volumeBeg = volumeBeg; pFader->volumeEnd = volumeEnd; pFader->lengthInFrames = lengthInFrames; pFader->cursorInFrames = 0; /* Reset cursor. */ } MA_API float ma_fader_get_current_volume(ma_fader* pFader) { if (pFader == NULL) { return 0.0f; } /* The current volume depends on the position of the cursor. */ if (pFader->cursorInFrames == 0) { return pFader->volumeBeg; } else if (pFader->cursorInFrames >= pFader->lengthInFrames) { return pFader->volumeEnd; } else { /* The cursor is somewhere inside the fading period. We can figure this out with a simple linear interpoluation between volumeBeg and volumeEnd based on our cursor position. */ return ma_mix_f32_fast(pFader->volumeBeg, pFader->volumeEnd, (ma_uint32)pFader->cursorInFrames / (float)((ma_uint32)pFader->lengthInFrames)); /* Safe cast to uint32 because we clamp it in ma_fader_process_pcm_frames(). */ } } MA_API ma_vec3f ma_vec3f_init_3f(float x, float y, float z) { ma_vec3f v; v.x = x; v.y = y; v.z = z; return v; } MA_API ma_vec3f ma_vec3f_sub(ma_vec3f a, ma_vec3f b) { return ma_vec3f_init_3f( a.x - b.x, a.y - b.y, a.z - b.z ); } MA_API ma_vec3f ma_vec3f_neg(ma_vec3f a) { return ma_vec3f_init_3f( -a.x, -a.y, -a.z ); } MA_API float ma_vec3f_dot(ma_vec3f a, ma_vec3f b) { return a.x*b.x + a.y*b.y + a.z*b.z; } MA_API float ma_vec3f_len2(ma_vec3f v) { return ma_vec3f_dot(v, v); } MA_API float ma_vec3f_len(ma_vec3f v) { return (float)ma_sqrtd(ma_vec3f_len2(v)); } MA_API float ma_vec3f_dist(ma_vec3f a, ma_vec3f b) { return ma_vec3f_len(ma_vec3f_sub(a, b)); } MA_API ma_vec3f ma_vec3f_normalize(ma_vec3f v) { float f; float l = ma_vec3f_len(v); if (l == 0) { return ma_vec3f_init_3f(0, 0, 0); } f = 1 / l; v.x *= f; v.y *= f; v.z *= f; return v; } MA_API ma_vec3f ma_vec3f_cross(ma_vec3f a, ma_vec3f b) { return ma_vec3f_init_3f( a.y*b.z - a.z*b.y, a.z*b.x - a.x*b.z, a.x*b.y - a.y*b.x ); } static void ma_channel_map_apply_f32(float* pFramesOut, const ma_channel* pChannelMapOut, ma_uint32 channelsOut, const float* pFramesIn, const ma_channel* pChannelMapIn, ma_uint32 channelsIn, ma_uint64 frameCount, ma_channel_mix_mode mode, ma_mono_expansion_mode monoExpansionMode); static ma_bool32 ma_is_spatial_channel_position(ma_channel channelPosition); #ifndef MA_DEFAULT_SPEED_OF_SOUND #define MA_DEFAULT_SPEED_OF_SOUND 343.3f #endif /* These vectors represent the direction that speakers are facing from the center point. They're used for panning in the spatializer. Must be normalized. */ static ma_vec3f g_maChannelDirections[MA_CHANNEL_POSITION_COUNT] = { { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_NONE */ { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_MONO */ {-0.7071f, 0.0f, -0.7071f }, /* MA_CHANNEL_FRONT_LEFT */ {+0.7071f, 0.0f, -0.7071f }, /* MA_CHANNEL_FRONT_RIGHT */ { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_FRONT_CENTER */ { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_LFE */ {-0.7071f, 0.0f, +0.7071f }, /* MA_CHANNEL_BACK_LEFT */ {+0.7071f, 0.0f, +0.7071f }, /* MA_CHANNEL_BACK_RIGHT */ {-0.3162f, 0.0f, -0.9487f }, /* MA_CHANNEL_FRONT_LEFT_CENTER */ {+0.3162f, 0.0f, -0.9487f }, /* MA_CHANNEL_FRONT_RIGHT_CENTER */ { 0.0f, 0.0f, +1.0f }, /* MA_CHANNEL_BACK_CENTER */ {-1.0f, 0.0f, 0.0f }, /* MA_CHANNEL_SIDE_LEFT */ {+1.0f, 0.0f, 0.0f }, /* MA_CHANNEL_SIDE_RIGHT */ { 0.0f, +1.0f, 0.0f }, /* MA_CHANNEL_TOP_CENTER */ {-0.5774f, +0.5774f, -0.5774f }, /* MA_CHANNEL_TOP_FRONT_LEFT */ { 0.0f, +0.7071f, -0.7071f }, /* MA_CHANNEL_TOP_FRONT_CENTER */ {+0.5774f, +0.5774f, -0.5774f }, /* MA_CHANNEL_TOP_FRONT_RIGHT */ {-0.5774f, +0.5774f, +0.5774f }, /* MA_CHANNEL_TOP_BACK_LEFT */ { 0.0f, +0.7071f, +0.7071f }, /* MA_CHANNEL_TOP_BACK_CENTER */ {+0.5774f, +0.5774f, +0.5774f }, /* MA_CHANNEL_TOP_BACK_RIGHT */ { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_0 */ { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_1 */ { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_2 */ { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_3 */ { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_4 */ { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_5 */ { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_6 */ { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_7 */ { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_8 */ { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_9 */ { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_10 */ { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_11 */ { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_12 */ { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_13 */ { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_14 */ { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_15 */ { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_16 */ { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_17 */ { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_18 */ { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_19 */ { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_20 */ { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_21 */ { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_22 */ { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_23 */ { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_24 */ { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_25 */ { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_26 */ { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_27 */ { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_28 */ { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_29 */ { 0.0f, 0.0f, -1.0f }, /* MA_CHANNEL_AUX_30 */ { 0.0f, 0.0f, -1.0f } /* MA_CHANNEL_AUX_31 */ }; static ma_vec3f ma_get_channel_direction(ma_channel channel) { if (channel >= MA_CHANNEL_POSITION_COUNT) { return ma_vec3f_init_3f(0, 0, -1); } else { return g_maChannelDirections[channel]; } } static float ma_attenuation_inverse(float distance, float minDistance, float maxDistance, float rolloff) { if (minDistance >= maxDistance) { return 1; /* To avoid division by zero. Do not attenuate. */ } return minDistance / (minDistance + rolloff * (ma_clamp(distance, minDistance, maxDistance) - minDistance)); } static float ma_attenuation_linear(float distance, float minDistance, float maxDistance, float rolloff) { if (minDistance >= maxDistance) { return 1; /* To avoid division by zero. Do not attenuate. */ } return 1 - rolloff * (ma_clamp(distance, minDistance, maxDistance) - minDistance) / (maxDistance - minDistance); } static float ma_attenuation_exponential(float distance, float minDistance, float maxDistance, float rolloff) { if (minDistance >= maxDistance) { return 1; /* To avoid division by zero. Do not attenuate. */ } return (float)ma_powd(ma_clamp(distance, minDistance, maxDistance) / minDistance, -rolloff); } /* Dopper Effect calculation taken from the OpenAL spec, with two main differences: 1) The source to listener vector will have already been calcualted at an earlier step so we can just use that directly. We need only the position of the source relative to the origin. 2) We don't scale by a frequency because we actually just want the ratio which we'll plug straight into the resampler directly. */ static float ma_doppler_pitch(ma_vec3f relativePosition, ma_vec3f sourceVelocity, ma_vec3f listenVelocity, float speedOfSound, float dopplerFactor) { float len; float vls; float vss; len = ma_vec3f_len(relativePosition); /* There's a case where the position of the source will be right on top of the listener in which case the length will be 0 and we'll end up with a division by zero. We can just return a ratio of 1.0 in this case. This is not considered in the OpenAL spec, but is necessary. */ if (len == 0) { return 1.0; } vls = ma_vec3f_dot(relativePosition, listenVelocity) / len; vss = ma_vec3f_dot(relativePosition, sourceVelocity) / len; vls = ma_min(vls, speedOfSound / dopplerFactor); vss = ma_min(vss, speedOfSound / dopplerFactor); return (speedOfSound - dopplerFactor*vls) / (speedOfSound - dopplerFactor*vss); } static void ma_get_default_channel_map_for_spatializer(ma_channel* pChannelMap, size_t channelMapCap, ma_uint32 channelCount) { /* Special case for stereo. Want to default the left and right speakers to side left and side right so that they're facing directly down the X axis rather than slightly forward. Not doing this will result in sounds being quieter when behind the listener. This might actually be good for some scenerios, but I don't think it's an appropriate default because it can be a bit unexpected. */ if (channelCount == 2) { pChannelMap[0] = MA_CHANNEL_SIDE_LEFT; pChannelMap[1] = MA_CHANNEL_SIDE_RIGHT; } else { ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, channelCount); } } MA_API ma_spatializer_listener_config ma_spatializer_listener_config_init(ma_uint32 channelsOut) { ma_spatializer_listener_config config; MA_ZERO_OBJECT(&config); config.channelsOut = channelsOut; config.pChannelMapOut = NULL; config.handedness = ma_handedness_right; config.worldUp = ma_vec3f_init_3f(0, 1, 0); config.coneInnerAngleInRadians = 6.283185f; /* 360 degrees. */ config.coneOuterAngleInRadians = 6.283185f; /* 360 degrees. */ config.coneOuterGain = 0; config.speedOfSound = 343.3f; /* Same as OpenAL. Used for doppler effect. */ return config; } typedef struct { size_t sizeInBytes; size_t channelMapOutOffset; } ma_spatializer_listener_heap_layout; static ma_result ma_spatializer_listener_get_heap_layout(const ma_spatializer_listener_config* pConfig, ma_spatializer_listener_heap_layout* pHeapLayout) { MA_ASSERT(pHeapLayout != NULL); MA_ZERO_OBJECT(pHeapLayout); if (pConfig == NULL) { return MA_INVALID_ARGS; } if (pConfig->channelsOut == 0) { return MA_INVALID_ARGS; } pHeapLayout->sizeInBytes = 0; /* Channel map. We always need this, even for passthroughs. */ pHeapLayout->channelMapOutOffset = pHeapLayout->sizeInBytes; pHeapLayout->sizeInBytes += ma_align_64(sizeof(*pConfig->pChannelMapOut) * pConfig->channelsOut); return MA_SUCCESS; } MA_API ma_result ma_spatializer_listener_get_heap_size(const ma_spatializer_listener_config* pConfig, size_t* pHeapSizeInBytes) { ma_result result; ma_spatializer_listener_heap_layout heapLayout; if (pHeapSizeInBytes == NULL) { return MA_INVALID_ARGS; } *pHeapSizeInBytes = 0; result = ma_spatializer_listener_get_heap_layout(pConfig, &heapLayout); if (result != MA_SUCCESS) { return result; } *pHeapSizeInBytes = heapLayout.sizeInBytes; return MA_SUCCESS; } MA_API ma_result ma_spatializer_listener_init_preallocated(const ma_spatializer_listener_config* pConfig, void* pHeap, ma_spatializer_listener* pListener) { ma_result result; ma_spatializer_listener_heap_layout heapLayout; if (pListener == NULL) { return MA_INVALID_ARGS; } MA_ZERO_OBJECT(pListener); result = ma_spatializer_listener_get_heap_layout(pConfig, &heapLayout); if (result != MA_SUCCESS) { return result; } pListener->_pHeap = pHeap; MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes); pListener->config = *pConfig; pListener->position = ma_vec3f_init_3f(0, 0, 0); pListener->direction = ma_vec3f_init_3f(0, 0, -1); pListener->velocity = ma_vec3f_init_3f(0, 0, 0); pListener->isEnabled = MA_TRUE; /* Swap the forward direction if we're left handed (it was initialized based on right handed). */ if (pListener->config.handedness == ma_handedness_left) { pListener->direction = ma_vec3f_neg(pListener->direction); } /* We must always have a valid channel map. */ pListener->config.pChannelMapOut = (ma_channel*)ma_offset_ptr(pHeap, heapLayout.channelMapOutOffset); /* Use a slightly different default channel map for stereo. */ if (pConfig->pChannelMapOut == NULL) { ma_get_default_channel_map_for_spatializer(pListener->config.pChannelMapOut, pConfig->channelsOut, pConfig->channelsOut); } else { ma_channel_map_copy_or_default(pListener->config.pChannelMapOut, pConfig->channelsOut, pConfig->pChannelMapOut, pConfig->channelsOut); } return MA_SUCCESS; } MA_API ma_result ma_spatializer_listener_init(const ma_spatializer_listener_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_spatializer_listener* pListener) { ma_result result; size_t heapSizeInBytes; void* pHeap; result = ma_spatializer_listener_get_heap_size(pConfig, &heapSizeInBytes); if (result != MA_SUCCESS) { return result; } if (heapSizeInBytes > 0) { pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); if (pHeap == NULL) { return MA_OUT_OF_MEMORY; } } else { pHeap = NULL; } result = ma_spatializer_listener_init_preallocated(pConfig, pHeap, pListener); if (result != MA_SUCCESS) { ma_free(pHeap, pAllocationCallbacks); return result; } pListener->_ownsHeap = MA_TRUE; return MA_SUCCESS; } MA_API void ma_spatializer_listener_uninit(ma_spatializer_listener* pListener, const ma_allocation_callbacks* pAllocationCallbacks) { if (pListener == NULL) { return; } if (pListener->_ownsHeap) { ma_free(pListener->_pHeap, pAllocationCallbacks); } } MA_API ma_channel* ma_spatializer_listener_get_channel_map(ma_spatializer_listener* pListener) { if (pListener == NULL) { return NULL; } return pListener->config.pChannelMapOut; } MA_API void ma_spatializer_listener_set_cone(ma_spatializer_listener* pListener, float innerAngleInRadians, float outerAngleInRadians, float outerGain) { if (pListener == NULL) { return; } pListener->config.coneInnerAngleInRadians = innerAngleInRadians; pListener->config.coneOuterAngleInRadians = outerAngleInRadians; pListener->config.coneOuterGain = outerGain; } MA_API void ma_spatializer_listener_get_cone(const ma_spatializer_listener* pListener, float* pInnerAngleInRadians, float* pOuterAngleInRadians, float* pOuterGain) { if (pListener == NULL) { return; } if (pInnerAngleInRadians != NULL) { *pInnerAngleInRadians = pListener->config.coneInnerAngleInRadians; } if (pOuterAngleInRadians != NULL) { *pOuterAngleInRadians = pListener->config.coneOuterAngleInRadians; } if (pOuterGain != NULL) { *pOuterGain = pListener->config.coneOuterGain; } } MA_API void ma_spatializer_listener_set_position(ma_spatializer_listener* pListener, float x, float y, float z) { if (pListener == NULL) { return; } pListener->position = ma_vec3f_init_3f(x, y, z); } MA_API ma_vec3f ma_spatializer_listener_get_position(const ma_spatializer_listener* pListener) { if (pListener == NULL) { return ma_vec3f_init_3f(0, 0, 0); } return pListener->position; } MA_API void ma_spatializer_listener_set_direction(ma_spatializer_listener* pListener, float x, float y, float z) { if (pListener == NULL) { return; } pListener->direction = ma_vec3f_init_3f(x, y, z); } MA_API ma_vec3f ma_spatializer_listener_get_direction(const ma_spatializer_listener* pListener) { if (pListener == NULL) { return ma_vec3f_init_3f(0, 0, -1); } return pListener->direction; } MA_API void ma_spatializer_listener_set_velocity(ma_spatializer_listener* pListener, float x, float y, float z) { if (pListener == NULL) { return; } pListener->velocity = ma_vec3f_init_3f(x, y, z); } MA_API ma_vec3f ma_spatializer_listener_get_velocity(const ma_spatializer_listener* pListener) { if (pListener == NULL) { return ma_vec3f_init_3f(0, 0, 0); } return pListener->velocity; } MA_API void ma_spatializer_listener_set_speed_of_sound(ma_spatializer_listener* pListener, float speedOfSound) { if (pListener == NULL) { return; } pListener->config.speedOfSound = speedOfSound; } MA_API float ma_spatializer_listener_get_speed_of_sound(const ma_spatializer_listener* pListener) { if (pListener == NULL) { return 0; } return pListener->config.speedOfSound; } MA_API void ma_spatializer_listener_set_world_up(ma_spatializer_listener* pListener, float x, float y, float z) { if (pListener == NULL) { return; } pListener->config.worldUp = ma_vec3f_init_3f(x, y, z); } MA_API ma_vec3f ma_spatializer_listener_get_world_up(const ma_spatializer_listener* pListener) { if (pListener == NULL) { return ma_vec3f_init_3f(0, 1, 0); } return pListener->config.worldUp; } MA_API void ma_spatializer_listener_set_enabled(ma_spatializer_listener* pListener, ma_bool32 isEnabled) { if (pListener == NULL) { return; } pListener->isEnabled = isEnabled; } MA_API ma_bool32 ma_spatializer_listener_is_enabled(const ma_spatializer_listener* pListener) { if (pListener == NULL) { return MA_FALSE; } return pListener->isEnabled; } MA_API ma_spatializer_config ma_spatializer_config_init(ma_uint32 channelsIn, ma_uint32 channelsOut) { ma_spatializer_config config; MA_ZERO_OBJECT(&config); config.channelsIn = channelsIn; config.channelsOut = channelsOut; config.pChannelMapIn = NULL; config.attenuationModel = ma_attenuation_model_inverse; config.positioning = ma_positioning_absolute; config.handedness = ma_handedness_right; config.minGain = 0; config.maxGain = 1; config.minDistance = 1; config.maxDistance = MA_FLT_MAX; config.rolloff = 1; config.coneInnerAngleInRadians = 6.283185f; /* 360 degrees. */ config.coneOuterAngleInRadians = 6.283185f; /* 360 degress. */ config.coneOuterGain = 0.0f; config.dopplerFactor = 1; config.directionalAttenuationFactor = 1; config.gainSmoothTimeInFrames = 360; /* 7.5ms @ 48K. */ return config; } static ma_gainer_config ma_spatializer_gainer_config_init(const ma_spatializer_config* pConfig) { MA_ASSERT(pConfig != NULL); return ma_gainer_config_init(pConfig->channelsOut, pConfig->gainSmoothTimeInFrames); } static ma_result ma_spatializer_validate_config(const ma_spatializer_config* pConfig) { MA_ASSERT(pConfig != NULL); if (pConfig->channelsIn == 0 || pConfig->channelsOut == 0) { return MA_INVALID_ARGS; } return MA_SUCCESS; } typedef struct { size_t sizeInBytes; size_t channelMapInOffset; size_t newChannelGainsOffset; size_t gainerOffset; } ma_spatializer_heap_layout; static ma_result ma_spatializer_get_heap_layout(const ma_spatializer_config* pConfig, ma_spatializer_heap_layout* pHeapLayout) { ma_result result; MA_ASSERT(pHeapLayout != NULL); MA_ZERO_OBJECT(pHeapLayout); if (pConfig == NULL) { return MA_INVALID_ARGS; } result = ma_spatializer_validate_config(pConfig); if (result != MA_SUCCESS) { return result; } pHeapLayout->sizeInBytes = 0; /* Channel map. */ pHeapLayout->channelMapInOffset = MA_SIZE_MAX; /* <-- MA_SIZE_MAX indicates no allocation necessary. */ if (pConfig->pChannelMapIn != NULL) { pHeapLayout->channelMapInOffset = pHeapLayout->sizeInBytes; pHeapLayout->sizeInBytes += ma_align_64(sizeof(*pConfig->pChannelMapIn) * pConfig->channelsIn); } /* New channel gains for output. */ pHeapLayout->newChannelGainsOffset = pHeapLayout->sizeInBytes; pHeapLayout->sizeInBytes += ma_align_64(sizeof(float) * pConfig->channelsOut); /* Gainer. */ { size_t gainerHeapSizeInBytes; ma_gainer_config gainerConfig; gainerConfig = ma_spatializer_gainer_config_init(pConfig); result = ma_gainer_get_heap_size(&gainerConfig, &gainerHeapSizeInBytes); if (result != MA_SUCCESS) { return result; } pHeapLayout->gainerOffset = pHeapLayout->sizeInBytes; pHeapLayout->sizeInBytes += ma_align_64(gainerHeapSizeInBytes); } return MA_SUCCESS; } MA_API ma_result ma_spatializer_get_heap_size(const ma_spatializer_config* pConfig, size_t* pHeapSizeInBytes) { ma_result result; ma_spatializer_heap_layout heapLayout; if (pHeapSizeInBytes == NULL) { return MA_INVALID_ARGS; } *pHeapSizeInBytes = 0; /* Safety. */ result = ma_spatializer_get_heap_layout(pConfig, &heapLayout); if (result != MA_SUCCESS) { return result; } *pHeapSizeInBytes = heapLayout.sizeInBytes; return MA_SUCCESS; } MA_API ma_result ma_spatializer_init_preallocated(const ma_spatializer_config* pConfig, void* pHeap, ma_spatializer* pSpatializer) { ma_result result; ma_spatializer_heap_layout heapLayout; ma_gainer_config gainerConfig; if (pSpatializer == NULL) { return MA_INVALID_ARGS; } MA_ZERO_OBJECT(pSpatializer); if (pConfig == NULL || pHeap == NULL) { return MA_INVALID_ARGS; } result = ma_spatializer_get_heap_layout(pConfig, &heapLayout); if (result != MA_SUCCESS) { return result; } pSpatializer->_pHeap = pHeap; MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes); pSpatializer->channelsIn = pConfig->channelsIn; pSpatializer->channelsOut = pConfig->channelsOut; pSpatializer->attenuationModel = pConfig->attenuationModel; pSpatializer->positioning = pConfig->positioning; pSpatializer->handedness = pConfig->handedness; pSpatializer->minGain = pConfig->minGain; pSpatializer->maxGain = pConfig->maxGain; pSpatializer->minDistance = pConfig->minDistance; pSpatializer->maxDistance = pConfig->maxDistance; pSpatializer->rolloff = pConfig->rolloff; pSpatializer->coneInnerAngleInRadians = pConfig->coneInnerAngleInRadians; pSpatializer->coneOuterAngleInRadians = pConfig->coneOuterAngleInRadians; pSpatializer->coneOuterGain = pConfig->coneOuterGain; pSpatializer->dopplerFactor = pConfig->dopplerFactor; pSpatializer->directionalAttenuationFactor = pConfig->directionalAttenuationFactor; pSpatializer->gainSmoothTimeInFrames = pConfig->gainSmoothTimeInFrames; pSpatializer->position = ma_vec3f_init_3f(0, 0, 0); pSpatializer->direction = ma_vec3f_init_3f(0, 0, -1); pSpatializer->velocity = ma_vec3f_init_3f(0, 0, 0); pSpatializer->dopplerPitch = 1; /* Swap the forward direction if we're left handed (it was initialized based on right handed). */ if (pSpatializer->handedness == ma_handedness_left) { pSpatializer->direction = ma_vec3f_neg(pSpatializer->direction); } /* Channel map. This will be on the heap. */ if (pConfig->pChannelMapIn != NULL) { pSpatializer->pChannelMapIn = (ma_channel*)ma_offset_ptr(pHeap, heapLayout.channelMapInOffset); ma_channel_map_copy_or_default(pSpatializer->pChannelMapIn, pSpatializer->channelsIn, pConfig->pChannelMapIn, pSpatializer->channelsIn); } /* New channel gains for output channels. */ pSpatializer->pNewChannelGainsOut = (float*)ma_offset_ptr(pHeap, heapLayout.newChannelGainsOffset); /* Gainer. */ gainerConfig = ma_spatializer_gainer_config_init(pConfig); result = ma_gainer_init_preallocated(&gainerConfig, ma_offset_ptr(pHeap, heapLayout.gainerOffset), &pSpatializer->gainer); if (result != MA_SUCCESS) { return result; /* Failed to initialize the gainer. */ } return MA_SUCCESS; } MA_API ma_result ma_spatializer_init(const ma_spatializer_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_spatializer* pSpatializer) { ma_result result; size_t heapSizeInBytes; void* pHeap; /* We'll need a heap allocation to retrieve the size. */ result = ma_spatializer_get_heap_size(pConfig, &heapSizeInBytes); if (result != MA_SUCCESS) { return result; } if (heapSizeInBytes > 0) { pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); if (pHeap == NULL) { return MA_OUT_OF_MEMORY; } } else { pHeap = NULL; } result = ma_spatializer_init_preallocated(pConfig, pHeap, pSpatializer); if (result != MA_SUCCESS) { ma_free(pHeap, pAllocationCallbacks); return result; } pSpatializer->_ownsHeap = MA_TRUE; return MA_SUCCESS; } MA_API void ma_spatializer_uninit(ma_spatializer* pSpatializer, const ma_allocation_callbacks* pAllocationCallbacks) { if (pSpatializer == NULL) { return; } ma_gainer_uninit(&pSpatializer->gainer, pAllocationCallbacks); if (pSpatializer->_ownsHeap) { ma_free(pSpatializer->_pHeap, pAllocationCallbacks); } } static float ma_calculate_angular_gain(ma_vec3f dirA, ma_vec3f dirB, float coneInnerAngleInRadians, float coneOuterAngleInRadians, float coneOuterGain) { /* Angular attenuation. Unlike distance gain, the math for this is not specified by the OpenAL spec so we'll just go ahead and figure this out for ourselves at the expense of possibly being inconsistent with other implementations. To do cone attenuation, I'm just using the same math that we'd use to implement a basic spotlight in OpenGL. We just need to get the direction from the source to the listener and then do a dot product against that and the direction of the spotlight. Then we just compare that dot product against the cosine of the inner and outer angles. If the dot product is greater than the the outer angle, we just use coneOuterGain. If it's less than the inner angle, we just use a gain of 1. Otherwise we linearly interpolate between 1 and coneOuterGain. */ if (coneInnerAngleInRadians < 6.283185f) { float angularGain = 1; float cutoffInner = (float)ma_cosd(coneInnerAngleInRadians*0.5f); float cutoffOuter = (float)ma_cosd(coneOuterAngleInRadians*0.5f); float d; d = ma_vec3f_dot(dirA, dirB); if (d > cutoffInner) { /* It's inside the inner angle. */ angularGain = 1; } else { /* It's outside the inner angle. */ if (d > cutoffOuter) { /* It's between the inner and outer angle. We need to linearly interpolate between 1 and coneOuterGain. */ angularGain = ma_mix_f32(coneOuterGain, 1, (d - cutoffOuter) / (cutoffInner - cutoffOuter)); } else { /* It's outside the outer angle. */ angularGain = coneOuterGain; } } /*printf("d = %f; cutoffInner = %f; cutoffOuter = %f; angularGain = %f\n", d, cutoffInner, cutoffOuter, angularGain);*/ return angularGain; } else { /* Inner angle is 360 degrees so no need to do any attenuation. */ return 1; } } MA_API ma_result ma_spatializer_process_pcm_frames(ma_spatializer* pSpatializer, ma_spatializer_listener* pListener, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) { ma_channel* pChannelMapIn = pSpatializer->pChannelMapIn; ma_channel* pChannelMapOut = pListener->config.pChannelMapOut; if (pSpatializer == NULL) { return MA_INVALID_ARGS; } /* If we're not spatializing we need to run an optimized path. */ if (c89atomic_load_i32(&pSpatializer->attenuationModel) == ma_attenuation_model_none) { if (ma_spatializer_listener_is_enabled(pListener)) { /* No attenuation is required, but we'll need to do some channel conversion. */ if (pSpatializer->channelsIn == pSpatializer->channelsOut) { ma_copy_pcm_frames(pFramesOut, pFramesIn, frameCount, ma_format_f32, pSpatializer->channelsIn); } else { ma_channel_map_apply_f32((float*)pFramesOut, pChannelMapOut, pSpatializer->channelsOut, (const float*)pFramesIn, pChannelMapIn, pSpatializer->channelsIn, frameCount, ma_channel_mix_mode_rectangular, ma_mono_expansion_mode_default); /* Safe casts to float* because f32 is the only supported format. */ } } else { /* The listener is disabled. Output silence. */ ma_silence_pcm_frames(pFramesOut, frameCount, ma_format_f32, pSpatializer->channelsOut); } /* We're not doing attenuation so don't bother with doppler for now. I'm not sure if this is the correct thinking so might need to review this later. */ pSpatializer->dopplerPitch = 1; } else { /* Let's first determine which listener the sound is closest to. Need to keep in mind that we might not have a world or any listeners, in which case we just spatializer based on the listener being positioned at the origin (0, 0, 0). */ ma_vec3f relativePosNormalized; ma_vec3f relativePos; /* The position relative to the listener. */ ma_vec3f relativeDir; /* The direction of the sound, relative to the listener. */ ma_vec3f listenerVel; /* The volocity of the listener. For doppler pitch calculation. */ float speedOfSound; float distance = 0; float gain = 1; ma_uint32 iChannel; const ma_uint32 channelsOut = pSpatializer->channelsOut; const ma_uint32 channelsIn = pSpatializer->channelsIn; float minDistance = ma_spatializer_get_min_distance(pSpatializer); float maxDistance = ma_spatializer_get_max_distance(pSpatializer); float rolloff = ma_spatializer_get_rolloff(pSpatializer); float dopplerFactor = ma_spatializer_get_doppler_factor(pSpatializer); /* We'll need the listener velocity for doppler pitch calculations. The speed of sound is defined by the listener, so we'll grab that here too. */ if (pListener != NULL) { listenerVel = pListener->velocity; speedOfSound = pListener->config.speedOfSound; } else { listenerVel = ma_vec3f_init_3f(0, 0, 0); speedOfSound = MA_DEFAULT_SPEED_OF_SOUND; } if (pListener == NULL || ma_spatializer_get_positioning(pSpatializer) == ma_positioning_relative) { /* There's no listener or we're using relative positioning. */ relativePos = pSpatializer->position; relativeDir = pSpatializer->direction; } else { /* We've found a listener and we're using absolute positioning. We need to transform the sound's position and direction so that it's relative to listener. Later on we'll use this for determining the factors to apply to each channel to apply the panning effect. */ ma_spatializer_get_relative_position_and_direction(pSpatializer, pListener, &relativePos, &relativeDir); } distance = ma_vec3f_len(relativePos); /* We've gathered the data, so now we can apply some spatialization. */ switch (ma_spatializer_get_attenuation_model(pSpatializer)) { case ma_attenuation_model_inverse: { gain = ma_attenuation_inverse(distance, minDistance, maxDistance, rolloff); } break; case ma_attenuation_model_linear: { gain = ma_attenuation_linear(distance, minDistance, maxDistance, rolloff); } break; case ma_attenuation_model_exponential: { gain = ma_attenuation_exponential(distance, minDistance, maxDistance, rolloff); } break; case ma_attenuation_model_none: default: { gain = 1; } break; } /* Normalize the position. */ if (distance > 0.001f) { float distanceInv = 1/distance; relativePosNormalized = relativePos; relativePosNormalized.x *= distanceInv; relativePosNormalized.y *= distanceInv; relativePosNormalized.z *= distanceInv; } else { distance = 0; relativePosNormalized = ma_vec3f_init_3f(0, 0, 0); } /* Angular attenuation. Unlike distance gain, the math for this is not specified by the OpenAL spec so we'll just go ahead and figure this out for ourselves at the expense of possibly being inconsistent with other implementations. To do cone attenuation, I'm just using the same math that we'd use to implement a basic spotlight in OpenGL. We just need to get the direction from the source to the listener and then do a dot product against that and the direction of the spotlight. Then we just compare that dot product against the cosine of the inner and outer angles. If the dot product is greater than the the outer angle, we just use coneOuterGain. If it's less than the inner angle, we just use a gain of 1. Otherwise we linearly interpolate between 1 and coneOuterGain. */ if (distance > 0) { /* Source anglular gain. */ float spatializerConeInnerAngle; float spatializerConeOuterAngle; float spatializerConeOuterGain; ma_spatializer_get_cone(pSpatializer, &spatializerConeInnerAngle, &spatializerConeOuterAngle, &spatializerConeOuterGain); gain *= ma_calculate_angular_gain(relativeDir, ma_vec3f_neg(relativePosNormalized), spatializerConeInnerAngle, spatializerConeOuterAngle, spatializerConeOuterGain); /* We're supporting angular gain on the listener as well for those who want to reduce the volume of sounds that are positioned behind the listener. On default settings, this will have no effect. */ if (pListener != NULL && pListener->config.coneInnerAngleInRadians < 6.283185f) { ma_vec3f listenerDirection; float listenerInnerAngle; float listenerOuterAngle; float listenerOuterGain; if (pListener->config.handedness == ma_handedness_right) { listenerDirection = ma_vec3f_init_3f(0, 0, -1); } else { listenerDirection = ma_vec3f_init_3f(0, 0, +1); } listenerInnerAngle = pListener->config.coneInnerAngleInRadians; listenerOuterAngle = pListener->config.coneOuterAngleInRadians; listenerOuterGain = pListener->config.coneOuterGain; gain *= ma_calculate_angular_gain(listenerDirection, relativePosNormalized, listenerInnerAngle, listenerOuterAngle, listenerOuterGain); } } else { /* The sound is right on top of the listener. Don't do any angular attenuation. */ } /* Clamp the gain. */ gain = ma_clamp(gain, ma_spatializer_get_min_gain(pSpatializer), ma_spatializer_get_max_gain(pSpatializer)); /* Panning. This is where we'll apply the gain and convert to the output channel count. We have an optimized path for when we're converting to a mono stream. In that case we don't really need to do any panning - we just apply the gain to the final output. */ /*printf("distance=%f; gain=%f\n", distance, gain);*/ /* We must have a valid channel map here to ensure we spatialize properly. */ MA_ASSERT(pChannelMapOut != NULL); /* We're not converting to mono so we'll want to apply some panning. This is where the feeling of something being to the left, right, infront or behind the listener is calculated. I'm just using a basic model here. Note that the code below is not based on any specific algorithm. I'm just implementing this off the top of my head and seeing how it goes. There might be better ways to do this. To determine the direction of the sound relative to a speaker I'm using dot products. Each speaker is given a direction. For example, the left channel in a stereo system will be -1 on the X axis and the right channel will be +1 on the X axis. A dot product is performed against the direction vector of the channel and the normalized position of the sound. */ for (iChannel = 0; iChannel < channelsOut; iChannel += 1) { pSpatializer->pNewChannelGainsOut[iChannel] = gain; } /* Convert to our output channel count. If the listener is disabled we just output silence here. We cannot ignore the whole section of code here because we need to update some internal spatialization state. */ if (ma_spatializer_listener_is_enabled(pListener)) { ma_channel_map_apply_f32((float*)pFramesOut, pChannelMapOut, channelsOut, (const float*)pFramesIn, pChannelMapIn, channelsIn, frameCount, ma_channel_mix_mode_rectangular, ma_mono_expansion_mode_default); } else { ma_silence_pcm_frames(pFramesOut, frameCount, ma_format_f32, pSpatializer->channelsOut); } /* Calculate our per-channel gains. We do this based on the normalized relative position of the sound and it's relation to the direction of the channel. */ if (distance > 0) { ma_vec3f unitPos = relativePos; float distanceInv = 1/distance; unitPos.x *= distanceInv; unitPos.y *= distanceInv; unitPos.z *= distanceInv; for (iChannel = 0; iChannel < channelsOut; iChannel += 1) { ma_channel channelOut; float d; float dMin; channelOut = ma_channel_map_get_channel(pChannelMapOut, channelsOut, iChannel); if (ma_is_spatial_channel_position(channelOut)) { d = ma_mix_f32_fast(1, ma_vec3f_dot(unitPos, ma_get_channel_direction(channelOut)), ma_spatializer_get_directional_attenuation_factor(pSpatializer)); } else { d = 1; /* It's not a spatial channel so there's no real notion of direction. */ } /* In my testing, if the panning effect is too aggressive it makes spatialization feel uncomfortable. The "dMin" variable below is used to control the aggressiveness of the panning effect. When set to 0, panning will be most extreme and any sounds that are positioned on the opposite side of the speaker will be completely silent from that speaker. Not only does this feel uncomfortable, it doesn't even remotely represent the real world at all because sounds that come from your right side are still clearly audible from your left side. Setting "dMin" to 1 will result in no panning at all, which is also not ideal. By setting it to something greater than 0, the spatialization effect becomes much less dramatic and a lot more bearable. Summary: 0 = more extreme panning; 1 = no panning. */ dMin = 0.2f; /* TODO: Consider making this configurable. */ /* At this point, "d" will be positive if the sound is on the same side as the channel and negative if it's on the opposite side. It will be in the range of -1..1. There's two ways I can think of to calculate a panning value. The first is to simply convert it to 0..1, however this has a problem which I'm not entirely happy with. Considering a stereo system, when a sound is positioned right in front of the listener it'll result in each speaker getting a gain of 0.5. I don't know if I like the idea of having a scaling factor of 0.5 being applied to a sound when it's sitting right in front of the listener. I would intuitively expect that to be played at full volume, or close to it. The second idea I think of is to only apply a reduction in gain when the sound is on the opposite side of the speaker. That is, reduce the gain only when the dot product is negative. The problem with this is that there will not be any attenuation as the sound sweeps around the 180 degrees where the dot product is positive. The idea with this option is that you leave the gain at 1 when the sound is being played on the same side as the speaker and then you just reduce the volume when the sound is on the other side. The summarize, I think the first option should give a better sense of spatialization, but the second option is better for preserving the sound's power. UPDATE: In my testing, I find the first option to sound better. You can feel the sense of space a bit better, but you can also hear the reduction in volume when it's right in front. */ #if 1 { /* Scale the dot product from -1..1 to 0..1. Will result in a sound directly in front losing power by being played at 0.5 gain. */ d = (d + 1) * 0.5f; /* -1..1 to 0..1 */ d = ma_max(d, dMin); pSpatializer->pNewChannelGainsOut[iChannel] *= d; } #else { /* Only reduce the volume of the sound if it's on the opposite side. This path keeps the volume more consistent, but comes at the expense of a worse sense of space and positioning. */ if (d < 0) { d += 1; /* Move into the positive range. */ d = ma_max(d, dMin); channelGainsOut[iChannel] *= d; } } #endif } } else { /* Assume the sound is right on top of us. Don't do any panning. */ } /* Now we need to apply the volume to each channel. This needs to run through the gainer to ensure we get a smooth volume transition. */ ma_gainer_set_gains(&pSpatializer->gainer, pSpatializer->pNewChannelGainsOut); ma_gainer_process_pcm_frames(&pSpatializer->gainer, pFramesOut, pFramesOut, frameCount); /* Before leaving we'll want to update our doppler pitch so that the caller can apply some pitch shifting if they desire. Note that we need to negate the relative position here because the doppler calculation needs to be source-to-listener, but ours is listener-to- source. */ if (dopplerFactor > 0) { pSpatializer->dopplerPitch = ma_doppler_pitch(ma_vec3f_sub(pListener->position, pSpatializer->position), pSpatializer->velocity, listenerVel, speedOfSound, dopplerFactor); } else { pSpatializer->dopplerPitch = 1; } } return MA_SUCCESS; } MA_API ma_uint32 ma_spatializer_get_input_channels(const ma_spatializer* pSpatializer) { if (pSpatializer == NULL) { return 0; } return pSpatializer->channelsIn; } MA_API ma_uint32 ma_spatializer_get_output_channels(const ma_spatializer* pSpatializer) { if (pSpatializer == NULL) { return 0; } return pSpatializer->channelsOut; } MA_API void ma_spatializer_set_attenuation_model(ma_spatializer* pSpatializer, ma_attenuation_model attenuationModel) { if (pSpatializer == NULL) { return; } c89atomic_exchange_i32(&pSpatializer->attenuationModel, attenuationModel); } MA_API ma_attenuation_model ma_spatializer_get_attenuation_model(const ma_spatializer* pSpatializer) { if (pSpatializer == NULL) { return ma_attenuation_model_none; } return (ma_attenuation_model)c89atomic_load_i32(&pSpatializer->attenuationModel); } MA_API void ma_spatializer_set_positioning(ma_spatializer* pSpatializer, ma_positioning positioning) { if (pSpatializer == NULL) { return; } c89atomic_exchange_i32(&pSpatializer->positioning, positioning); } MA_API ma_positioning ma_spatializer_get_positioning(const ma_spatializer* pSpatializer) { if (pSpatializer == NULL) { return ma_positioning_absolute; } return (ma_positioning)c89atomic_load_i32(&pSpatializer->positioning); } MA_API void ma_spatializer_set_rolloff(ma_spatializer* pSpatializer, float rolloff) { if (pSpatializer == NULL) { return; } c89atomic_exchange_f32(&pSpatializer->rolloff, rolloff); } MA_API float ma_spatializer_get_rolloff(const ma_spatializer* pSpatializer) { if (pSpatializer == NULL) { return 0; } return c89atomic_load_f32(&pSpatializer->rolloff); } MA_API void ma_spatializer_set_min_gain(ma_spatializer* pSpatializer, float minGain) { if (pSpatializer == NULL) { return; } c89atomic_exchange_f32(&pSpatializer->minGain, minGain); } MA_API float ma_spatializer_get_min_gain(const ma_spatializer* pSpatializer) { if (pSpatializer == NULL) { return 0; } return c89atomic_load_f32(&pSpatializer->minGain); } MA_API void ma_spatializer_set_max_gain(ma_spatializer* pSpatializer, float maxGain) { if (pSpatializer == NULL) { return; } c89atomic_exchange_f32(&pSpatializer->maxGain, maxGain); } MA_API float ma_spatializer_get_max_gain(const ma_spatializer* pSpatializer) { if (pSpatializer == NULL) { return 0; } return c89atomic_load_f32(&pSpatializer->maxGain); } MA_API void ma_spatializer_set_min_distance(ma_spatializer* pSpatializer, float minDistance) { if (pSpatializer == NULL) { return; } c89atomic_exchange_f32(&pSpatializer->minDistance, minDistance); } MA_API float ma_spatializer_get_min_distance(const ma_spatializer* pSpatializer) { if (pSpatializer == NULL) { return 0; } return c89atomic_load_f32(&pSpatializer->minDistance); } MA_API void ma_spatializer_set_max_distance(ma_spatializer* pSpatializer, float maxDistance) { if (pSpatializer == NULL) { return; } c89atomic_exchange_f32(&pSpatializer->maxDistance, maxDistance); } MA_API float ma_spatializer_get_max_distance(const ma_spatializer* pSpatializer) { if (pSpatializer == NULL) { return 0; } return c89atomic_load_f32(&pSpatializer->maxDistance); } MA_API void ma_spatializer_set_cone(ma_spatializer* pSpatializer, float innerAngleInRadians, float outerAngleInRadians, float outerGain) { if (pSpatializer == NULL) { return; } c89atomic_exchange_f32(&pSpatializer->coneInnerAngleInRadians, innerAngleInRadians); c89atomic_exchange_f32(&pSpatializer->coneOuterAngleInRadians, outerAngleInRadians); c89atomic_exchange_f32(&pSpatializer->coneOuterGain, outerGain); } MA_API void ma_spatializer_get_cone(const ma_spatializer* pSpatializer, float* pInnerAngleInRadians, float* pOuterAngleInRadians, float* pOuterGain) { if (pSpatializer == NULL) { return; } if (pInnerAngleInRadians != NULL) { *pInnerAngleInRadians = c89atomic_load_f32(&pSpatializer->coneInnerAngleInRadians); } if (pOuterAngleInRadians != NULL) { *pOuterAngleInRadians = c89atomic_load_f32(&pSpatializer->coneOuterAngleInRadians); } if (pOuterGain != NULL) { *pOuterGain = c89atomic_load_f32(&pSpatializer->coneOuterGain); } } MA_API void ma_spatializer_set_doppler_factor(ma_spatializer* pSpatializer, float dopplerFactor) { if (pSpatializer == NULL) { return; } c89atomic_exchange_f32(&pSpatializer->dopplerFactor, dopplerFactor); } MA_API float ma_spatializer_get_doppler_factor(const ma_spatializer* pSpatializer) { if (pSpatializer == NULL) { return 1; } return c89atomic_load_f32(&pSpatializer->dopplerFactor); } MA_API void ma_spatializer_set_directional_attenuation_factor(ma_spatializer* pSpatializer, float directionalAttenuationFactor) { if (pSpatializer == NULL) { return; } c89atomic_exchange_f32(&pSpatializer->directionalAttenuationFactor, directionalAttenuationFactor); } MA_API float ma_spatializer_get_directional_attenuation_factor(const ma_spatializer* pSpatializer) { if (pSpatializer == NULL) { return 1; } return c89atomic_load_f32(&pSpatializer->directionalAttenuationFactor); } MA_API void ma_spatializer_set_position(ma_spatializer* pSpatializer, float x, float y, float z) { if (pSpatializer == NULL) { return; } pSpatializer->position = ma_vec3f_init_3f(x, y, z); } MA_API ma_vec3f ma_spatializer_get_position(const ma_spatializer* pSpatializer) { if (pSpatializer == NULL) { return ma_vec3f_init_3f(0, 0, 0); } return pSpatializer->position; } MA_API void ma_spatializer_set_direction(ma_spatializer* pSpatializer, float x, float y, float z) { if (pSpatializer == NULL) { return; } pSpatializer->direction = ma_vec3f_init_3f(x, y, z); } MA_API ma_vec3f ma_spatializer_get_direction(const ma_spatializer* pSpatializer) { if (pSpatializer == NULL) { return ma_vec3f_init_3f(0, 0, -1); } return pSpatializer->direction; } MA_API void ma_spatializer_set_velocity(ma_spatializer* pSpatializer, float x, float y, float z) { if (pSpatializer == NULL) { return; } pSpatializer->velocity = ma_vec3f_init_3f(x, y, z); } MA_API ma_vec3f ma_spatializer_get_velocity(const ma_spatializer* pSpatializer) { if (pSpatializer == NULL) { return ma_vec3f_init_3f(0, 0, 0); } return pSpatializer->velocity; } MA_API void ma_spatializer_get_relative_position_and_direction(const ma_spatializer* pSpatializer, const ma_spatializer_listener* pListener, ma_vec3f* pRelativePos, ma_vec3f* pRelativeDir) { if (pRelativePos != NULL) { pRelativePos->x = 0; pRelativePos->y = 0; pRelativePos->z = 0; } if (pRelativeDir != NULL) { pRelativeDir->x = 0; pRelativeDir->y = 0; pRelativeDir->z = -1; } if (pSpatializer == NULL) { return; } if (pListener == NULL || ma_spatializer_get_positioning(pSpatializer) == ma_positioning_relative) { /* There's no listener or we're using relative positioning. */ if (pRelativePos != NULL) { *pRelativePos = pSpatializer->position; } if (pRelativeDir != NULL) { *pRelativeDir = pSpatializer->direction; } } else { ma_vec3f v; ma_vec3f axisX; ma_vec3f axisY; ma_vec3f axisZ; float m[4][4]; /* We need to calcualte the right vector from our forward and up vectors. This is done with a cross product. */ axisZ = ma_vec3f_normalize(pListener->direction); /* Normalization required here because we can't trust the caller. */ axisX = ma_vec3f_normalize(ma_vec3f_cross(axisZ, pListener->config.worldUp)); /* Normalization required here because the world up vector may not be perpendicular with the forward vector. */ /* The calculation of axisX above can result in a zero-length vector if the listener is looking straight up on the Y axis. We'll need to fall back to a +X in this case so that the calculations below don't fall apart. This is where a quaternion based listener and sound orientation would come in handy. */ if (ma_vec3f_len2(axisX) == 0) { axisX = ma_vec3f_init_3f(1, 0, 0); } axisY = ma_vec3f_cross(axisX, axisZ); /* No normalization is required here because axisX and axisZ are unit length and perpendicular. */ /* We need to swap the X axis if we're left handed because otherwise the cross product above will have resulted in it pointing in the wrong direction (right handed was assumed in the cross products above). */ if (pListener->config.handedness == ma_handedness_left) { axisX = ma_vec3f_neg(axisX); } /* Lookat. */ m[0][0] = axisX.x; m[1][0] = axisX.y; m[2][0] = axisX.z; m[3][0] = -ma_vec3f_dot(axisX, pListener->position); m[0][1] = axisY.x; m[1][1] = axisY.y; m[2][1] = axisY.z; m[3][1] = -ma_vec3f_dot(axisY, pListener->position); m[0][2] = -axisZ.x; m[1][2] = -axisZ.y; m[2][2] = -axisZ.z; m[3][2] = -ma_vec3f_dot(ma_vec3f_neg(axisZ), pListener->position); m[0][3] = 0; m[1][3] = 0; m[2][3] = 0; m[3][3] = 1; /* Multiply the lookat matrix by the spatializer position to transform it to listener space. This allows calculations to work based on the sound being relative to the origin which makes things simpler. */ if (pRelativePos != NULL) { v = pSpatializer->position; pRelativePos->x = m[0][0] * v.x + m[1][0] * v.y + m[2][0] * v.z + m[3][0] * 1; pRelativePos->y = m[0][1] * v.x + m[1][1] * v.y + m[2][1] * v.z + m[3][1] * 1; pRelativePos->z = m[0][2] * v.x + m[1][2] * v.y + m[2][2] * v.z + m[3][2] * 1; } /* The direction of the sound needs to also be transformed so that it's relative to the rotation of the listener. */ if (pRelativeDir != NULL) { v = pSpatializer->direction; pRelativeDir->x = m[0][0] * v.x + m[1][0] * v.y + m[2][0] * v.z; pRelativeDir->y = m[0][1] * v.x + m[1][1] * v.y + m[2][1] * v.z; pRelativeDir->z = m[0][2] * v.x + m[1][2] * v.y + m[2][2] * v.z; } } } /************************************************************************************************************************************************************** Resampling **************************************************************************************************************************************************************/ MA_API ma_linear_resampler_config ma_linear_resampler_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut) { ma_linear_resampler_config config; MA_ZERO_OBJECT(&config); config.format = format; config.channels = channels; config.sampleRateIn = sampleRateIn; config.sampleRateOut = sampleRateOut; config.lpfOrder = ma_min(MA_DEFAULT_RESAMPLER_LPF_ORDER, MA_MAX_FILTER_ORDER); config.lpfNyquistFactor = 1; return config; } typedef struct { size_t sizeInBytes; size_t x0Offset; size_t x1Offset; size_t lpfOffset; } ma_linear_resampler_heap_layout; static void ma_linear_resampler_adjust_timer_for_new_rate(ma_linear_resampler* pResampler, ma_uint32 oldSampleRateOut, ma_uint32 newSampleRateOut) { /* So what's happening here? Basically we need to adjust the fractional component of the time advance based on the new rate. The old time advance will be based on the old sample rate, but we are needing to adjust it to that it's based on the new sample rate. */ ma_uint32 oldRateTimeWhole = pResampler->inTimeFrac / oldSampleRateOut; /* <-- This should almost never be anything other than 0, but leaving it here to make this more general and robust just in case. */ ma_uint32 oldRateTimeFract = pResampler->inTimeFrac % oldSampleRateOut; pResampler->inTimeFrac = (oldRateTimeWhole * newSampleRateOut) + ((oldRateTimeFract * newSampleRateOut) / oldSampleRateOut); /* Make sure the fractional part is less than the output sample rate. */ pResampler->inTimeInt += pResampler->inTimeFrac / pResampler->config.sampleRateOut; pResampler->inTimeFrac = pResampler->inTimeFrac % pResampler->config.sampleRateOut; } static ma_result ma_linear_resampler_set_rate_internal(ma_linear_resampler* pResampler, void* pHeap, ma_linear_resampler_heap_layout* pHeapLayout, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut, ma_bool32 isResamplerAlreadyInitialized) { ma_result result; ma_uint32 gcf; ma_uint32 lpfSampleRate; double lpfCutoffFrequency; ma_lpf_config lpfConfig; ma_uint32 oldSampleRateOut; /* Required for adjusting time advance down the bottom. */ if (pResampler == NULL) { return MA_INVALID_ARGS; } if (sampleRateIn == 0 || sampleRateOut == 0) { return MA_INVALID_ARGS; } oldSampleRateOut = pResampler->config.sampleRateOut; pResampler->config.sampleRateIn = sampleRateIn; pResampler->config.sampleRateOut = sampleRateOut; /* Simplify the sample rate. */ gcf = ma_gcf_u32(pResampler->config.sampleRateIn, pResampler->config.sampleRateOut); pResampler->config.sampleRateIn /= gcf; pResampler->config.sampleRateOut /= gcf; /* Always initialize the low-pass filter, even when the order is 0. */ if (pResampler->config.lpfOrder > MA_MAX_FILTER_ORDER) { return MA_INVALID_ARGS; } lpfSampleRate = (ma_uint32)(ma_max(pResampler->config.sampleRateIn, pResampler->config.sampleRateOut)); lpfCutoffFrequency = ( double)(ma_min(pResampler->config.sampleRateIn, pResampler->config.sampleRateOut) * 0.5 * pResampler->config.lpfNyquistFactor); lpfConfig = ma_lpf_config_init(pResampler->config.format, pResampler->config.channels, lpfSampleRate, lpfCutoffFrequency, pResampler->config.lpfOrder); /* If the resampler is alreay initialized we don't want to do a fresh initialization of the low-pass filter because it will result in the cached frames getting cleared. Instead we re-initialize the filter which will maintain any cached frames. */ if (isResamplerAlreadyInitialized) { result = ma_lpf_reinit(&lpfConfig, &pResampler->lpf); } else { result = ma_lpf_init_preallocated(&lpfConfig, ma_offset_ptr(pHeap, pHeapLayout->lpfOffset), &pResampler->lpf); } if (result != MA_SUCCESS) { return result; } pResampler->inAdvanceInt = pResampler->config.sampleRateIn / pResampler->config.sampleRateOut; pResampler->inAdvanceFrac = pResampler->config.sampleRateIn % pResampler->config.sampleRateOut; /* Our timer was based on the old rate. We need to adjust it so that it's based on the new rate. */ ma_linear_resampler_adjust_timer_for_new_rate(pResampler, oldSampleRateOut, pResampler->config.sampleRateOut); return MA_SUCCESS; } static ma_result ma_linear_resampler_get_heap_layout(const ma_linear_resampler_config* pConfig, ma_linear_resampler_heap_layout* pHeapLayout) { MA_ASSERT(pHeapLayout != NULL); MA_ZERO_OBJECT(pHeapLayout); if (pConfig == NULL) { return MA_INVALID_ARGS; } if (pConfig->format != ma_format_f32 && pConfig->format != ma_format_s16) { return MA_INVALID_ARGS; } if (pConfig->channels == 0) { return MA_INVALID_ARGS; } pHeapLayout->sizeInBytes = 0; /* x0 */ pHeapLayout->x0Offset = pHeapLayout->sizeInBytes; if (pConfig->format == ma_format_f32) { pHeapLayout->sizeInBytes += sizeof(float) * pConfig->channels; } else { pHeapLayout->sizeInBytes += sizeof(ma_int16) * pConfig->channels; } /* x1 */ pHeapLayout->x1Offset = pHeapLayout->sizeInBytes; if (pConfig->format == ma_format_f32) { pHeapLayout->sizeInBytes += sizeof(float) * pConfig->channels; } else { pHeapLayout->sizeInBytes += sizeof(ma_int16) * pConfig->channels; } /* LPF */ pHeapLayout->lpfOffset = ma_align_64(pHeapLayout->sizeInBytes); { ma_result result; size_t lpfHeapSizeInBytes; ma_lpf_config lpfConfig = ma_lpf_config_init(pConfig->format, pConfig->channels, 1, 1, pConfig->lpfOrder); /* Sample rate and cutoff frequency do not matter. */ result = ma_lpf_get_heap_size(&lpfConfig, &lpfHeapSizeInBytes); if (result != MA_SUCCESS) { return result; } pHeapLayout->sizeInBytes += lpfHeapSizeInBytes; } /* Make sure allocation size is aligned. */ pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes); return MA_SUCCESS; } MA_API ma_result ma_linear_resampler_get_heap_size(const ma_linear_resampler_config* pConfig, size_t* pHeapSizeInBytes) { ma_result result; ma_linear_resampler_heap_layout heapLayout; if (pHeapSizeInBytes == NULL) { return MA_INVALID_ARGS; } *pHeapSizeInBytes = 0; result = ma_linear_resampler_get_heap_layout(pConfig, &heapLayout); if (result != MA_SUCCESS) { return result; } *pHeapSizeInBytes = heapLayout.sizeInBytes; return MA_SUCCESS; } MA_API ma_result ma_linear_resampler_init_preallocated(const ma_linear_resampler_config* pConfig, void* pHeap, ma_linear_resampler* pResampler) { ma_result result; ma_linear_resampler_heap_layout heapLayout; if (pResampler == NULL) { return MA_INVALID_ARGS; } MA_ZERO_OBJECT(pResampler); result = ma_linear_resampler_get_heap_layout(pConfig, &heapLayout); if (result != MA_SUCCESS) { return result; } pResampler->config = *pConfig; pResampler->_pHeap = pHeap; MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes); if (pConfig->format == ma_format_f32) { pResampler->x0.f32 = (float*)ma_offset_ptr(pHeap, heapLayout.x0Offset); pResampler->x1.f32 = (float*)ma_offset_ptr(pHeap, heapLayout.x1Offset); } else { pResampler->x0.s16 = (ma_int16*)ma_offset_ptr(pHeap, heapLayout.x0Offset); pResampler->x1.s16 = (ma_int16*)ma_offset_ptr(pHeap, heapLayout.x1Offset); } /* Setting the rate will set up the filter and time advances for us. */ result = ma_linear_resampler_set_rate_internal(pResampler, pHeap, &heapLayout, pConfig->sampleRateIn, pConfig->sampleRateOut, /* isResamplerAlreadyInitialized = */ MA_FALSE); if (result != MA_SUCCESS) { return result; } pResampler->inTimeInt = 1; /* Set this to one to force an input sample to always be loaded for the first output frame. */ pResampler->inTimeFrac = 0; return MA_SUCCESS; } MA_API ma_result ma_linear_resampler_init(const ma_linear_resampler_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_linear_resampler* pResampler) { ma_result result; size_t heapSizeInBytes; void* pHeap; result = ma_linear_resampler_get_heap_size(pConfig, &heapSizeInBytes); if (result != MA_SUCCESS) { return result; } if (heapSizeInBytes > 0) { pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); if (pHeap == NULL) { return MA_OUT_OF_MEMORY; } } else { pHeap = NULL; } result = ma_linear_resampler_init_preallocated(pConfig, pHeap, pResampler); if (result != MA_SUCCESS) { ma_free(pHeap, pAllocationCallbacks); return result; } pResampler->_ownsHeap = MA_TRUE; return MA_SUCCESS; } MA_API void ma_linear_resampler_uninit(ma_linear_resampler* pResampler, const ma_allocation_callbacks* pAllocationCallbacks) { if (pResampler == NULL) { return; } ma_lpf_uninit(&pResampler->lpf, pAllocationCallbacks); if (pResampler->_ownsHeap) { ma_free(pResampler->_pHeap, pAllocationCallbacks); } } static MA_INLINE ma_int16 ma_linear_resampler_mix_s16(ma_int16 x, ma_int16 y, ma_int32 a, const ma_int32 shift) { ma_int32 b; ma_int32 c; ma_int32 r; MA_ASSERT(a <= (1<> shift); } static void ma_linear_resampler_interpolate_frame_s16(ma_linear_resampler* pResampler, ma_int16* MA_RESTRICT pFrameOut) { ma_uint32 c; ma_uint32 a; const ma_uint32 channels = pResampler->config.channels; const ma_uint32 shift = 12; MA_ASSERT(pResampler != NULL); MA_ASSERT(pFrameOut != NULL); a = (pResampler->inTimeFrac << shift) / pResampler->config.sampleRateOut; MA_ASSUME(channels > 0); for (c = 0; c < channels; c += 1) { ma_int16 s = ma_linear_resampler_mix_s16(pResampler->x0.s16[c], pResampler->x1.s16[c], a, shift); pFrameOut[c] = s; } } static void ma_linear_resampler_interpolate_frame_f32(ma_linear_resampler* pResampler, float* MA_RESTRICT pFrameOut) { ma_uint32 c; float a; const ma_uint32 channels = pResampler->config.channels; MA_ASSERT(pResampler != NULL); MA_ASSERT(pFrameOut != NULL); a = (float)pResampler->inTimeFrac / pResampler->config.sampleRateOut; MA_ASSUME(channels > 0); for (c = 0; c < channels; c += 1) { float s = ma_mix_f32_fast(pResampler->x0.f32[c], pResampler->x1.f32[c], a); pFrameOut[c] = s; } } static ma_result ma_linear_resampler_process_pcm_frames_s16_downsample(ma_linear_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut) { const ma_int16* pFramesInS16; /* */ ma_int16* pFramesOutS16; ma_uint64 frameCountIn; ma_uint64 frameCountOut; ma_uint64 framesProcessedIn; ma_uint64 framesProcessedOut; MA_ASSERT(pResampler != NULL); MA_ASSERT(pFrameCountIn != NULL); MA_ASSERT(pFrameCountOut != NULL); pFramesInS16 = (const ma_int16*)pFramesIn; pFramesOutS16 = ( ma_int16*)pFramesOut; frameCountIn = *pFrameCountIn; frameCountOut = *pFrameCountOut; framesProcessedIn = 0; framesProcessedOut = 0; while (framesProcessedOut < frameCountOut) { /* Before interpolating we need to load the buffers. When doing this we need to ensure we run every input sample through the filter. */ while (pResampler->inTimeInt > 0 && frameCountIn > framesProcessedIn) { ma_uint32 iChannel; if (pFramesInS16 != NULL) { for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) { pResampler->x0.s16[iChannel] = pResampler->x1.s16[iChannel]; pResampler->x1.s16[iChannel] = pFramesInS16[iChannel]; } pFramesInS16 += pResampler->config.channels; } else { for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) { pResampler->x0.s16[iChannel] = pResampler->x1.s16[iChannel]; pResampler->x1.s16[iChannel] = 0; } } /* Filter. */ ma_lpf_process_pcm_frame_s16(&pResampler->lpf, pResampler->x1.s16, pResampler->x1.s16); framesProcessedIn += 1; pResampler->inTimeInt -= 1; } if (pResampler->inTimeInt > 0) { break; /* Ran out of input data. */ } /* Getting here means the frames have been loaded and filtered and we can generate the next output frame. */ if (pFramesOutS16 != NULL) { MA_ASSERT(pResampler->inTimeInt == 0); ma_linear_resampler_interpolate_frame_s16(pResampler, pFramesOutS16); pFramesOutS16 += pResampler->config.channels; } framesProcessedOut += 1; /* Advance time forward. */ pResampler->inTimeInt += pResampler->inAdvanceInt; pResampler->inTimeFrac += pResampler->inAdvanceFrac; if (pResampler->inTimeFrac >= pResampler->config.sampleRateOut) { pResampler->inTimeFrac -= pResampler->config.sampleRateOut; pResampler->inTimeInt += 1; } } *pFrameCountIn = framesProcessedIn; *pFrameCountOut = framesProcessedOut; return MA_SUCCESS; } static ma_result ma_linear_resampler_process_pcm_frames_s16_upsample(ma_linear_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut) { const ma_int16* pFramesInS16; /* */ ma_int16* pFramesOutS16; ma_uint64 frameCountIn; ma_uint64 frameCountOut; ma_uint64 framesProcessedIn; ma_uint64 framesProcessedOut; MA_ASSERT(pResampler != NULL); MA_ASSERT(pFrameCountIn != NULL); MA_ASSERT(pFrameCountOut != NULL); pFramesInS16 = (const ma_int16*)pFramesIn; pFramesOutS16 = ( ma_int16*)pFramesOut; frameCountIn = *pFrameCountIn; frameCountOut = *pFrameCountOut; framesProcessedIn = 0; framesProcessedOut = 0; while (framesProcessedOut < frameCountOut) { /* Before interpolating we need to load the buffers. */ while (pResampler->inTimeInt > 0 && frameCountIn > framesProcessedIn) { ma_uint32 iChannel; if (pFramesInS16 != NULL) { for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) { pResampler->x0.s16[iChannel] = pResampler->x1.s16[iChannel]; pResampler->x1.s16[iChannel] = pFramesInS16[iChannel]; } pFramesInS16 += pResampler->config.channels; } else { for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) { pResampler->x0.s16[iChannel] = pResampler->x1.s16[iChannel]; pResampler->x1.s16[iChannel] = 0; } } framesProcessedIn += 1; pResampler->inTimeInt -= 1; } if (pResampler->inTimeInt > 0) { break; /* Ran out of input data. */ } /* Getting here means the frames have been loaded and we can generate the next output frame. */ if (pFramesOutS16 != NULL) { MA_ASSERT(pResampler->inTimeInt == 0); ma_linear_resampler_interpolate_frame_s16(pResampler, pFramesOutS16); /* Filter. */ ma_lpf_process_pcm_frame_s16(&pResampler->lpf, pFramesOutS16, pFramesOutS16); pFramesOutS16 += pResampler->config.channels; } framesProcessedOut += 1; /* Advance time forward. */ pResampler->inTimeInt += pResampler->inAdvanceInt; pResampler->inTimeFrac += pResampler->inAdvanceFrac; if (pResampler->inTimeFrac >= pResampler->config.sampleRateOut) { pResampler->inTimeFrac -= pResampler->config.sampleRateOut; pResampler->inTimeInt += 1; } } *pFrameCountIn = framesProcessedIn; *pFrameCountOut = framesProcessedOut; return MA_SUCCESS; } static ma_result ma_linear_resampler_process_pcm_frames_s16(ma_linear_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut) { MA_ASSERT(pResampler != NULL); if (pResampler->config.sampleRateIn > pResampler->config.sampleRateOut) { return ma_linear_resampler_process_pcm_frames_s16_downsample(pResampler, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut); } else { return ma_linear_resampler_process_pcm_frames_s16_upsample(pResampler, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut); } } static ma_result ma_linear_resampler_process_pcm_frames_f32_downsample(ma_linear_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut) { const float* pFramesInF32; /* */ float* pFramesOutF32; ma_uint64 frameCountIn; ma_uint64 frameCountOut; ma_uint64 framesProcessedIn; ma_uint64 framesProcessedOut; MA_ASSERT(pResampler != NULL); MA_ASSERT(pFrameCountIn != NULL); MA_ASSERT(pFrameCountOut != NULL); pFramesInF32 = (const float*)pFramesIn; pFramesOutF32 = ( float*)pFramesOut; frameCountIn = *pFrameCountIn; frameCountOut = *pFrameCountOut; framesProcessedIn = 0; framesProcessedOut = 0; while (framesProcessedOut < frameCountOut) { /* Before interpolating we need to load the buffers. When doing this we need to ensure we run every input sample through the filter. */ while (pResampler->inTimeInt > 0 && frameCountIn > framesProcessedIn) { ma_uint32 iChannel; if (pFramesInF32 != NULL) { for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) { pResampler->x0.f32[iChannel] = pResampler->x1.f32[iChannel]; pResampler->x1.f32[iChannel] = pFramesInF32[iChannel]; } pFramesInF32 += pResampler->config.channels; } else { for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) { pResampler->x0.f32[iChannel] = pResampler->x1.f32[iChannel]; pResampler->x1.f32[iChannel] = 0; } } /* Filter. */ ma_lpf_process_pcm_frame_f32(&pResampler->lpf, pResampler->x1.f32, pResampler->x1.f32); framesProcessedIn += 1; pResampler->inTimeInt -= 1; } if (pResampler->inTimeInt > 0) { break; /* Ran out of input data. */ } /* Getting here means the frames have been loaded and filtered and we can generate the next output frame. */ if (pFramesOutF32 != NULL) { MA_ASSERT(pResampler->inTimeInt == 0); ma_linear_resampler_interpolate_frame_f32(pResampler, pFramesOutF32); pFramesOutF32 += pResampler->config.channels; } framesProcessedOut += 1; /* Advance time forward. */ pResampler->inTimeInt += pResampler->inAdvanceInt; pResampler->inTimeFrac += pResampler->inAdvanceFrac; if (pResampler->inTimeFrac >= pResampler->config.sampleRateOut) { pResampler->inTimeFrac -= pResampler->config.sampleRateOut; pResampler->inTimeInt += 1; } } *pFrameCountIn = framesProcessedIn; *pFrameCountOut = framesProcessedOut; return MA_SUCCESS; } static ma_result ma_linear_resampler_process_pcm_frames_f32_upsample(ma_linear_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut) { const float* pFramesInF32; /* */ float* pFramesOutF32; ma_uint64 frameCountIn; ma_uint64 frameCountOut; ma_uint64 framesProcessedIn; ma_uint64 framesProcessedOut; MA_ASSERT(pResampler != NULL); MA_ASSERT(pFrameCountIn != NULL); MA_ASSERT(pFrameCountOut != NULL); pFramesInF32 = (const float*)pFramesIn; pFramesOutF32 = ( float*)pFramesOut; frameCountIn = *pFrameCountIn; frameCountOut = *pFrameCountOut; framesProcessedIn = 0; framesProcessedOut = 0; while (framesProcessedOut < frameCountOut) { /* Before interpolating we need to load the buffers. */ while (pResampler->inTimeInt > 0 && frameCountIn > framesProcessedIn) { ma_uint32 iChannel; if (pFramesInF32 != NULL) { for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) { pResampler->x0.f32[iChannel] = pResampler->x1.f32[iChannel]; pResampler->x1.f32[iChannel] = pFramesInF32[iChannel]; } pFramesInF32 += pResampler->config.channels; } else { for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) { pResampler->x0.f32[iChannel] = pResampler->x1.f32[iChannel]; pResampler->x1.f32[iChannel] = 0; } } framesProcessedIn += 1; pResampler->inTimeInt -= 1; } if (pResampler->inTimeInt > 0) { break; /* Ran out of input data. */ } /* Getting here means the frames have been loaded and we can generate the next output frame. */ if (pFramesOutF32 != NULL) { MA_ASSERT(pResampler->inTimeInt == 0); ma_linear_resampler_interpolate_frame_f32(pResampler, pFramesOutF32); /* Filter. */ ma_lpf_process_pcm_frame_f32(&pResampler->lpf, pFramesOutF32, pFramesOutF32); pFramesOutF32 += pResampler->config.channels; } framesProcessedOut += 1; /* Advance time forward. */ pResampler->inTimeInt += pResampler->inAdvanceInt; pResampler->inTimeFrac += pResampler->inAdvanceFrac; if (pResampler->inTimeFrac >= pResampler->config.sampleRateOut) { pResampler->inTimeFrac -= pResampler->config.sampleRateOut; pResampler->inTimeInt += 1; } } *pFrameCountIn = framesProcessedIn; *pFrameCountOut = framesProcessedOut; return MA_SUCCESS; } static ma_result ma_linear_resampler_process_pcm_frames_f32(ma_linear_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut) { MA_ASSERT(pResampler != NULL); if (pResampler->config.sampleRateIn > pResampler->config.sampleRateOut) { return ma_linear_resampler_process_pcm_frames_f32_downsample(pResampler, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut); } else { return ma_linear_resampler_process_pcm_frames_f32_upsample(pResampler, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut); } } MA_API ma_result ma_linear_resampler_process_pcm_frames(ma_linear_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut) { if (pResampler == NULL) { return MA_INVALID_ARGS; } /* */ if (pResampler->config.format == ma_format_s16) { return ma_linear_resampler_process_pcm_frames_s16(pResampler, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut); } else if (pResampler->config.format == ma_format_f32) { return ma_linear_resampler_process_pcm_frames_f32(pResampler, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut); } else { /* Should never get here. Getting here means the format is not supported and you didn't check the return value of ma_linear_resampler_init(). */ MA_ASSERT(MA_FALSE); return MA_INVALID_ARGS; } } MA_API ma_result ma_linear_resampler_set_rate(ma_linear_resampler* pResampler, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut) { return ma_linear_resampler_set_rate_internal(pResampler, NULL, NULL, sampleRateIn, sampleRateOut, /* isResamplerAlreadyInitialized = */ MA_TRUE); } MA_API ma_result ma_linear_resampler_set_rate_ratio(ma_linear_resampler* pResampler, float ratioInOut) { ma_uint32 n; ma_uint32 d; if (pResampler == NULL) { return MA_INVALID_ARGS; } if (ratioInOut <= 0) { return MA_INVALID_ARGS; } d = 1000; n = (ma_uint32)(ratioInOut * d); if (n == 0) { return MA_INVALID_ARGS; /* Ratio too small. */ } MA_ASSERT(n != 0); return ma_linear_resampler_set_rate(pResampler, n, d); } MA_API ma_uint64 ma_linear_resampler_get_input_latency(const ma_linear_resampler* pResampler) { if (pResampler == NULL) { return 0; } return 1 + ma_lpf_get_latency(&pResampler->lpf); } MA_API ma_uint64 ma_linear_resampler_get_output_latency(const ma_linear_resampler* pResampler) { if (pResampler == NULL) { return 0; } return ma_linear_resampler_get_input_latency(pResampler) * pResampler->config.sampleRateOut / pResampler->config.sampleRateIn; } MA_API ma_result ma_linear_resampler_get_required_input_frame_count(const ma_linear_resampler* pResampler, ma_uint64 outputFrameCount, ma_uint64* pInputFrameCount) { ma_uint64 inputFrameCount; if (pInputFrameCount == NULL) { return MA_INVALID_ARGS; } *pInputFrameCount = 0; if (pResampler == NULL) { return MA_INVALID_ARGS; } if (outputFrameCount == 0) { return MA_SUCCESS; } /* Any whole input frames are consumed before the first output frame is generated. */ inputFrameCount = pResampler->inTimeInt; outputFrameCount -= 1; /* The rest of the output frames can be calculated in constant time. */ inputFrameCount += outputFrameCount * pResampler->inAdvanceInt; inputFrameCount += (pResampler->inTimeFrac + (outputFrameCount * pResampler->inAdvanceFrac)) / pResampler->config.sampleRateOut; *pInputFrameCount = inputFrameCount; return MA_SUCCESS; } MA_API ma_result ma_linear_resampler_get_expected_output_frame_count(const ma_linear_resampler* pResampler, ma_uint64 inputFrameCount, ma_uint64* pOutputFrameCount) { ma_uint64 outputFrameCount; ma_uint64 preliminaryInputFrameCountFromFrac; ma_uint64 preliminaryInputFrameCount; if (pOutputFrameCount == NULL) { return MA_INVALID_ARGS; } *pOutputFrameCount = 0; if (pResampler == NULL) { return MA_INVALID_ARGS; } /* The first step is to get a preliminary output frame count. This will either be exactly equal to what we need, or less by 1. We need to determine how many input frames will be consumed by this value. If it's greater than our original input frame count it means we won't be able to generate an extra frame because we will have run out of input data. Otherwise we will have enough input for the generation of an extra output frame. This add-by-one logic is necessary due to how the data loading logic works when processing frames. */ outputFrameCount = (inputFrameCount * pResampler->config.sampleRateOut) / pResampler->config.sampleRateIn; /* We need to determine how many *whole* input frames will have been processed to generate our preliminary output frame count. This is used in the logic below to determine whether or not we need to add an extra output frame. */ preliminaryInputFrameCountFromFrac = (pResampler->inTimeFrac + outputFrameCount*pResampler->inAdvanceFrac) / pResampler->config.sampleRateOut; preliminaryInputFrameCount = (pResampler->inTimeInt + outputFrameCount*pResampler->inAdvanceInt ) + preliminaryInputFrameCountFromFrac; /* If the total number of *whole* input frames that would be required to generate our preliminary output frame count is greather than the amount of whole input frames we have available as input we need to *not* add an extra output frame as there won't be enough data to actually process. Otherwise we need to add the extra output frame. */ if (preliminaryInputFrameCount <= inputFrameCount) { outputFrameCount += 1; } *pOutputFrameCount = outputFrameCount; return MA_SUCCESS; } MA_API ma_result ma_linear_resampler_reset(ma_linear_resampler* pResampler) { ma_uint32 iChannel; if (pResampler == NULL) { return MA_INVALID_ARGS; } /* Timers need to be cleared back to zero. */ pResampler->inTimeInt = 1; /* Set this to one to force an input sample to always be loaded for the first output frame. */ pResampler->inTimeFrac = 0; /* Cached samples need to be cleared. */ if (pResampler->config.format == ma_format_f32) { for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) { pResampler->x0.f32[iChannel] = 0; pResampler->x1.f32[iChannel] = 0; } } else { for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) { pResampler->x0.s16[iChannel] = 0; pResampler->x1.s16[iChannel] = 0; } } /* The low pass filter needs to have it's cache reset. */ ma_lpf_clear_cache(&pResampler->lpf); return MA_SUCCESS; } /* Linear resampler backend vtable. */ static ma_linear_resampler_config ma_resampling_backend_get_config__linear(const ma_resampler_config* pConfig) { ma_linear_resampler_config linearConfig; linearConfig = ma_linear_resampler_config_init(pConfig->format, pConfig->channels, pConfig->sampleRateIn, pConfig->sampleRateOut); linearConfig.lpfOrder = pConfig->linear.lpfOrder; return linearConfig; } static ma_result ma_resampling_backend_get_heap_size__linear(void* pUserData, const ma_resampler_config* pConfig, size_t* pHeapSizeInBytes) { ma_linear_resampler_config linearConfig; (void)pUserData; linearConfig = ma_resampling_backend_get_config__linear(pConfig); return ma_linear_resampler_get_heap_size(&linearConfig, pHeapSizeInBytes); } static ma_result ma_resampling_backend_init__linear(void* pUserData, const ma_resampler_config* pConfig, void* pHeap, ma_resampling_backend** ppBackend) { ma_resampler* pResampler = (ma_resampler*)pUserData; ma_result result; ma_linear_resampler_config linearConfig; (void)pUserData; linearConfig = ma_resampling_backend_get_config__linear(pConfig); result = ma_linear_resampler_init_preallocated(&linearConfig, pHeap, &pResampler->state.linear); if (result != MA_SUCCESS) { return result; } *ppBackend = &pResampler->state.linear; return MA_SUCCESS; } static void ma_resampling_backend_uninit__linear(void* pUserData, ma_resampling_backend* pBackend, const ma_allocation_callbacks* pAllocationCallbacks) { (void)pUserData; ma_linear_resampler_uninit((ma_linear_resampler*)pBackend, pAllocationCallbacks); } static ma_result ma_resampling_backend_process__linear(void* pUserData, ma_resampling_backend* pBackend, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut) { (void)pUserData; return ma_linear_resampler_process_pcm_frames((ma_linear_resampler*)pBackend, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut); } static ma_result ma_resampling_backend_set_rate__linear(void* pUserData, ma_resampling_backend* pBackend, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut) { (void)pUserData; return ma_linear_resampler_set_rate((ma_linear_resampler*)pBackend, sampleRateIn, sampleRateOut); } static ma_uint64 ma_resampling_backend_get_input_latency__linear(void* pUserData, const ma_resampling_backend* pBackend) { (void)pUserData; return ma_linear_resampler_get_input_latency((const ma_linear_resampler*)pBackend); } static ma_uint64 ma_resampling_backend_get_output_latency__linear(void* pUserData, const ma_resampling_backend* pBackend) { (void)pUserData; return ma_linear_resampler_get_output_latency((const ma_linear_resampler*)pBackend); } static ma_result ma_resampling_backend_get_required_input_frame_count__linear(void* pUserData, const ma_resampling_backend* pBackend, ma_uint64 outputFrameCount, ma_uint64* pInputFrameCount) { (void)pUserData; return ma_linear_resampler_get_required_input_frame_count((const ma_linear_resampler*)pBackend, outputFrameCount, pInputFrameCount); } static ma_result ma_resampling_backend_get_expected_output_frame_count__linear(void* pUserData, const ma_resampling_backend* pBackend, ma_uint64 inputFrameCount, ma_uint64* pOutputFrameCount) { (void)pUserData; return ma_linear_resampler_get_expected_output_frame_count((const ma_linear_resampler*)pBackend, inputFrameCount, pOutputFrameCount); } static ma_result ma_resampling_backend_reset__linear(void* pUserData, ma_resampling_backend* pBackend) { (void)pUserData; return ma_linear_resampler_reset((ma_linear_resampler*)pBackend); } static ma_resampling_backend_vtable g_ma_linear_resampler_vtable = { ma_resampling_backend_get_heap_size__linear, ma_resampling_backend_init__linear, ma_resampling_backend_uninit__linear, ma_resampling_backend_process__linear, ma_resampling_backend_set_rate__linear, ma_resampling_backend_get_input_latency__linear, ma_resampling_backend_get_output_latency__linear, ma_resampling_backend_get_required_input_frame_count__linear, ma_resampling_backend_get_expected_output_frame_count__linear, ma_resampling_backend_reset__linear }; MA_API ma_resampler_config ma_resampler_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut, ma_resample_algorithm algorithm) { ma_resampler_config config; MA_ZERO_OBJECT(&config); config.format = format; config.channels = channels; config.sampleRateIn = sampleRateIn; config.sampleRateOut = sampleRateOut; config.algorithm = algorithm; /* Linear. */ config.linear.lpfOrder = ma_min(MA_DEFAULT_RESAMPLER_LPF_ORDER, MA_MAX_FILTER_ORDER); return config; } static ma_result ma_resampler_get_vtable(const ma_resampler_config* pConfig, ma_resampler* pResampler, ma_resampling_backend_vtable** ppVTable, void** ppUserData) { MA_ASSERT(pConfig != NULL); MA_ASSERT(ppVTable != NULL); MA_ASSERT(ppUserData != NULL); /* Safety. */ *ppVTable = NULL; *ppUserData = NULL; switch (pConfig->algorithm) { case ma_resample_algorithm_linear: { *ppVTable = &g_ma_linear_resampler_vtable; *ppUserData = pResampler; } break; case ma_resample_algorithm_custom: { *ppVTable = pConfig->pBackendVTable; *ppUserData = pConfig->pBackendUserData; } break; default: return MA_INVALID_ARGS; } return MA_SUCCESS; } MA_API ma_result ma_resampler_get_heap_size(const ma_resampler_config* pConfig, size_t* pHeapSizeInBytes) { ma_result result; ma_resampling_backend_vtable* pVTable; void* pVTableUserData; if (pHeapSizeInBytes == NULL) { return MA_INVALID_ARGS; } *pHeapSizeInBytes = 0; if (pConfig == NULL) { return MA_INVALID_ARGS; } result = ma_resampler_get_vtable(pConfig, NULL, &pVTable, &pVTableUserData); if (result != MA_SUCCESS) { return result; } if (pVTable == NULL || pVTable->onGetHeapSize == NULL) { return MA_NOT_IMPLEMENTED; } result = pVTable->onGetHeapSize(pVTableUserData, pConfig, pHeapSizeInBytes); if (result != MA_SUCCESS) { return result; } return MA_SUCCESS; } MA_API ma_result ma_resampler_init_preallocated(const ma_resampler_config* pConfig, void* pHeap, ma_resampler* pResampler) { ma_result result; if (pResampler == NULL) { return MA_INVALID_ARGS; } MA_ZERO_OBJECT(pResampler); if (pConfig == NULL) { return MA_INVALID_ARGS; } pResampler->_pHeap = pHeap; pResampler->format = pConfig->format; pResampler->channels = pConfig->channels; pResampler->sampleRateIn = pConfig->sampleRateIn; pResampler->sampleRateOut = pConfig->sampleRateOut; result = ma_resampler_get_vtable(pConfig, pResampler, &pResampler->pBackendVTable, &pResampler->pBackendUserData); if (result != MA_SUCCESS) { return result; } if (pResampler->pBackendVTable == NULL || pResampler->pBackendVTable->onInit == NULL) { return MA_NOT_IMPLEMENTED; /* onInit not implemented. */ } result = pResampler->pBackendVTable->onInit(pResampler->pBackendUserData, pConfig, pHeap, &pResampler->pBackend); if (result != MA_SUCCESS) { return result; } return MA_SUCCESS; } MA_API ma_result ma_resampler_init(const ma_resampler_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_resampler* pResampler) { ma_result result; size_t heapSizeInBytes; void* pHeap; result = ma_resampler_get_heap_size(pConfig, &heapSizeInBytes); if (result != MA_SUCCESS) { return result; } if (heapSizeInBytes > 0) { pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); if (pHeap == NULL) { return MA_OUT_OF_MEMORY; } } else { pHeap = NULL; } result = ma_resampler_init_preallocated(pConfig, pHeap, pResampler); if (result != MA_SUCCESS) { ma_free(pHeap, pAllocationCallbacks); return result; } pResampler->_ownsHeap = MA_TRUE; return MA_SUCCESS; } MA_API void ma_resampler_uninit(ma_resampler* pResampler, const ma_allocation_callbacks* pAllocationCallbacks) { if (pResampler == NULL) { return; } if (pResampler->pBackendVTable == NULL || pResampler->pBackendVTable->onUninit == NULL) { return; } pResampler->pBackendVTable->onUninit(pResampler->pBackendUserData, pResampler->pBackend, pAllocationCallbacks); if (pResampler->_ownsHeap) { ma_free(pResampler->_pHeap, pAllocationCallbacks); } } MA_API ma_result ma_resampler_process_pcm_frames(ma_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut) { if (pResampler == NULL) { return MA_INVALID_ARGS; } if (pFrameCountOut == NULL && pFrameCountIn == NULL) { return MA_INVALID_ARGS; } if (pResampler->pBackendVTable == NULL || pResampler->pBackendVTable->onProcess == NULL) { return MA_NOT_IMPLEMENTED; } return pResampler->pBackendVTable->onProcess(pResampler->pBackendUserData, pResampler->pBackend, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut); } MA_API ma_result ma_resampler_set_rate(ma_resampler* pResampler, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut) { ma_result result; if (pResampler == NULL) { return MA_INVALID_ARGS; } if (sampleRateIn == 0 || sampleRateOut == 0) { return MA_INVALID_ARGS; } if (pResampler->pBackendVTable == NULL || pResampler->pBackendVTable->onSetRate == NULL) { return MA_NOT_IMPLEMENTED; } result = pResampler->pBackendVTable->onSetRate(pResampler->pBackendUserData, pResampler->pBackend, sampleRateIn, sampleRateOut); if (result != MA_SUCCESS) { return result; } pResampler->sampleRateIn = sampleRateIn; pResampler->sampleRateOut = sampleRateOut; return MA_SUCCESS; } MA_API ma_result ma_resampler_set_rate_ratio(ma_resampler* pResampler, float ratio) { ma_uint32 n; ma_uint32 d; if (pResampler == NULL) { return MA_INVALID_ARGS; } if (ratio <= 0) { return MA_INVALID_ARGS; } d = 1000; n = (ma_uint32)(ratio * d); if (n == 0) { return MA_INVALID_ARGS; /* Ratio too small. */ } MA_ASSERT(n != 0); return ma_resampler_set_rate(pResampler, n, d); } MA_API ma_uint64 ma_resampler_get_input_latency(const ma_resampler* pResampler) { if (pResampler == NULL) { return 0; } if (pResampler->pBackendVTable == NULL || pResampler->pBackendVTable->onGetInputLatency == NULL) { return 0; } return pResampler->pBackendVTable->onGetInputLatency(pResampler->pBackendUserData, pResampler->pBackend); } MA_API ma_uint64 ma_resampler_get_output_latency(const ma_resampler* pResampler) { if (pResampler == NULL) { return 0; } if (pResampler->pBackendVTable == NULL || pResampler->pBackendVTable->onGetOutputLatency == NULL) { return 0; } return pResampler->pBackendVTable->onGetOutputLatency(pResampler->pBackendUserData, pResampler->pBackend); } MA_API ma_result ma_resampler_get_required_input_frame_count(const ma_resampler* pResampler, ma_uint64 outputFrameCount, ma_uint64* pInputFrameCount) { if (pInputFrameCount == NULL) { return MA_INVALID_ARGS; } *pInputFrameCount = 0; if (pResampler == NULL) { return MA_INVALID_ARGS; } if (pResampler->pBackendVTable == NULL || pResampler->pBackendVTable->onGetRequiredInputFrameCount == NULL) { return MA_NOT_IMPLEMENTED; } return pResampler->pBackendVTable->onGetRequiredInputFrameCount(pResampler->pBackendUserData, pResampler->pBackend, outputFrameCount, pInputFrameCount); } MA_API ma_result ma_resampler_get_expected_output_frame_count(const ma_resampler* pResampler, ma_uint64 inputFrameCount, ma_uint64* pOutputFrameCount) { if (pOutputFrameCount == NULL) { return MA_INVALID_ARGS; } *pOutputFrameCount = 0; if (pResampler == NULL) { return MA_INVALID_ARGS; } if (pResampler->pBackendVTable == NULL || pResampler->pBackendVTable->onGetExpectedOutputFrameCount == NULL) { return MA_NOT_IMPLEMENTED; } return pResampler->pBackendVTable->onGetExpectedOutputFrameCount(pResampler->pBackendUserData, pResampler->pBackend, inputFrameCount, pOutputFrameCount); } MA_API ma_result ma_resampler_reset(ma_resampler* pResampler) { if (pResampler == NULL) { return MA_INVALID_ARGS; } if (pResampler->pBackendVTable == NULL || pResampler->pBackendVTable->onReset == NULL) { return MA_NOT_IMPLEMENTED; } return pResampler->pBackendVTable->onReset(pResampler->pBackendUserData, pResampler->pBackend); } /************************************************************************************************************************************************************** Channel Conversion **************************************************************************************************************************************************************/ #ifndef MA_CHANNEL_CONVERTER_FIXED_POINT_SHIFT #define MA_CHANNEL_CONVERTER_FIXED_POINT_SHIFT 12 #endif #define MA_PLANE_LEFT 0 #define MA_PLANE_RIGHT 1 #define MA_PLANE_FRONT 2 #define MA_PLANE_BACK 3 #define MA_PLANE_BOTTOM 4 #define MA_PLANE_TOP 5 static float g_maChannelPlaneRatios[MA_CHANNEL_POSITION_COUNT][6] = { { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_NONE */ { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_MONO */ { 0.5f, 0.0f, 0.5f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_FRONT_LEFT */ { 0.0f, 0.5f, 0.5f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_FRONT_RIGHT */ { 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_FRONT_CENTER */ { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_LFE */ { 0.5f, 0.0f, 0.0f, 0.5f, 0.0f, 0.0f}, /* MA_CHANNEL_BACK_LEFT */ { 0.0f, 0.5f, 0.0f, 0.5f, 0.0f, 0.0f}, /* MA_CHANNEL_BACK_RIGHT */ { 0.25f, 0.0f, 0.75f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_FRONT_LEFT_CENTER */ { 0.0f, 0.25f, 0.75f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_FRONT_RIGHT_CENTER */ { 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f}, /* MA_CHANNEL_BACK_CENTER */ { 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_SIDE_LEFT */ { 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_SIDE_RIGHT */ { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}, /* MA_CHANNEL_TOP_CENTER */ { 0.33f, 0.0f, 0.33f, 0.0f, 0.0f, 0.34f}, /* MA_CHANNEL_TOP_FRONT_LEFT */ { 0.0f, 0.0f, 0.5f, 0.0f, 0.0f, 0.5f}, /* MA_CHANNEL_TOP_FRONT_CENTER */ { 0.0f, 0.33f, 0.33f, 0.0f, 0.0f, 0.34f}, /* MA_CHANNEL_TOP_FRONT_RIGHT */ { 0.33f, 0.0f, 0.0f, 0.33f, 0.0f, 0.34f}, /* MA_CHANNEL_TOP_BACK_LEFT */ { 0.0f, 0.0f, 0.0f, 0.5f, 0.0f, 0.5f}, /* MA_CHANNEL_TOP_BACK_CENTER */ { 0.0f, 0.33f, 0.0f, 0.33f, 0.0f, 0.34f}, /* MA_CHANNEL_TOP_BACK_RIGHT */ { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_0 */ { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_1 */ { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_2 */ { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_3 */ { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_4 */ { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_5 */ { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_6 */ { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_7 */ { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_8 */ { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_9 */ { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_10 */ { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_11 */ { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_12 */ { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_13 */ { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_14 */ { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_15 */ { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_16 */ { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_17 */ { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_18 */ { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_19 */ { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_20 */ { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_21 */ { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_22 */ { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_23 */ { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_24 */ { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_25 */ { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_26 */ { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_27 */ { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_28 */ { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_29 */ { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_30 */ { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_31 */ }; static float ma_calculate_channel_position_rectangular_weight(ma_channel channelPositionA, ma_channel channelPositionB) { /* Imagine the following simplified example: You have a single input speaker which is the front/left speaker which you want to convert to the following output configuration: - front/left - side/left - back/left The front/left output is easy - it the same speaker position so it receives the full contribution of the front/left input. The amount of contribution to apply to the side/left and back/left speakers, however, is a bit more complicated. Imagine the front/left speaker as emitting audio from two planes - the front plane and the left plane. You can think of the front/left speaker emitting half of it's total volume from the front, and the other half from the left. Since part of it's volume is being emitted from the left side, and the side/left and back/left channels also emit audio from the left plane, one would expect that they would receive some amount of contribution from front/left speaker. The amount of contribution depends on how many planes are shared between the two speakers. Note that in the examples below I've added a top/front/left speaker as an example just to show how the math works across 3 spatial dimensions. The first thing to do is figure out how each speaker's volume is spread over each of plane: - front/left: 2 planes (front and left) = 1/2 = half it's total volume on each plane - side/left: 1 plane (left only) = 1/1 = entire volume from left plane - back/left: 2 planes (back and left) = 1/2 = half it's total volume on each plane - top/front/left: 3 planes (top, front and left) = 1/3 = one third it's total volume on each plane The amount of volume each channel contributes to each of it's planes is what controls how much it is willing to given and take to other channels on the same plane. The volume that is willing to the given by one channel is multiplied by the volume that is willing to be taken by the other to produce the final contribution. */ /* Contribution = Sum(Volume to Give * Volume to Take) */ float contribution = g_maChannelPlaneRatios[channelPositionA][0] * g_maChannelPlaneRatios[channelPositionB][0] + g_maChannelPlaneRatios[channelPositionA][1] * g_maChannelPlaneRatios[channelPositionB][1] + g_maChannelPlaneRatios[channelPositionA][2] * g_maChannelPlaneRatios[channelPositionB][2] + g_maChannelPlaneRatios[channelPositionA][3] * g_maChannelPlaneRatios[channelPositionB][3] + g_maChannelPlaneRatios[channelPositionA][4] * g_maChannelPlaneRatios[channelPositionB][4] + g_maChannelPlaneRatios[channelPositionA][5] * g_maChannelPlaneRatios[channelPositionB][5]; return contribution; } MA_API ma_channel_converter_config ma_channel_converter_config_init(ma_format format, ma_uint32 channelsIn, const ma_channel* pChannelMapIn, ma_uint32 channelsOut, const ma_channel* pChannelMapOut, ma_channel_mix_mode mixingMode) { ma_channel_converter_config config; MA_ZERO_OBJECT(&config); config.format = format; config.channelsIn = channelsIn; config.channelsOut = channelsOut; config.pChannelMapIn = pChannelMapIn; config.pChannelMapOut = pChannelMapOut; config.mixingMode = mixingMode; return config; } static ma_int32 ma_channel_converter_float_to_fixed(float x) { return (ma_int32)(x * (1< 0); for (iChannel = 0; iChannel < channels; ++iChannel) { if (ma_is_spatial_channel_position(ma_channel_map_get_channel(pChannelMap, channels, iChannel))) { spatialChannelCount++; } } return spatialChannelCount; } static ma_bool32 ma_is_spatial_channel_position(ma_channel channelPosition) { int i; if (channelPosition == MA_CHANNEL_NONE || channelPosition == MA_CHANNEL_MONO || channelPosition == MA_CHANNEL_LFE) { return MA_FALSE; } if (channelPosition >= MA_CHANNEL_AUX_0 && channelPosition <= MA_CHANNEL_AUX_31) { return MA_FALSE; } for (i = 0; i < 6; ++i) { /* Each side of a cube. */ if (g_maChannelPlaneRatios[channelPosition][i] != 0) { return MA_TRUE; } } return MA_FALSE; } static ma_bool32 ma_channel_map_is_passthrough(const ma_channel* pChannelMapIn, ma_uint32 channelsIn, const ma_channel* pChannelMapOut, ma_uint32 channelsOut) { if (channelsOut == channelsIn) { return ma_channel_map_is_equal(pChannelMapOut, pChannelMapIn, channelsOut); } else { return MA_FALSE; /* Channel counts differ, so cannot be a passthrough. */ } } static ma_channel_conversion_path ma_channel_map_get_conversion_path(const ma_channel* pChannelMapIn, ma_uint32 channelsIn, const ma_channel* pChannelMapOut, ma_uint32 channelsOut, ma_channel_mix_mode mode) { if (ma_channel_map_is_passthrough(pChannelMapIn, channelsIn, pChannelMapOut, channelsOut)) { return ma_channel_conversion_path_passthrough; } if (channelsOut == 1 && (pChannelMapOut == NULL || pChannelMapOut[0] == MA_CHANNEL_MONO)) { return ma_channel_conversion_path_mono_out; } if (channelsIn == 1 && (pChannelMapIn == NULL || pChannelMapIn[0] == MA_CHANNEL_MONO)) { return ma_channel_conversion_path_mono_in; } if (mode == ma_channel_mix_mode_custom_weights) { return ma_channel_conversion_path_weights; } /* We can use a simple shuffle if both channel maps have the same channel count and all channel positions are present in both. */ if (channelsIn == channelsOut) { ma_uint32 iChannelIn; ma_bool32 areAllChannelPositionsPresent = MA_TRUE; for (iChannelIn = 0; iChannelIn < channelsIn; ++iChannelIn) { ma_bool32 isInputChannelPositionInOutput = MA_FALSE; if (ma_channel_map_contains_channel_position(channelsOut, pChannelMapOut, ma_channel_map_get_channel(pChannelMapIn, channelsIn, iChannelIn))) { isInputChannelPositionInOutput = MA_TRUE; break; } if (!isInputChannelPositionInOutput) { areAllChannelPositionsPresent = MA_FALSE; break; } } if (areAllChannelPositionsPresent) { return ma_channel_conversion_path_shuffle; } } /* Getting here means we'll need to use weights. */ return ma_channel_conversion_path_weights; } static ma_result ma_channel_map_build_shuffle_table(const ma_channel* pChannelMapIn, ma_uint32 channelCountIn, const ma_channel* pChannelMapOut, ma_uint32 channelCountOut, ma_uint8* pShuffleTable) { ma_uint32 iChannelIn; ma_uint32 iChannelOut; if (pShuffleTable == NULL || channelCountIn == 0 || channelCountOut == 0) { return MA_INVALID_ARGS; } /* When building the shuffle table we just do a 1:1 mapping based on the first occurance of a channel. If the input channel has more than one occurance of a channel position, the second one will be ignored. */ for (iChannelOut = 0; iChannelOut < channelCountOut; iChannelOut += 1) { ma_channel channelOut; /* Default to MA_CHANNEL_INDEX_NULL so that if a mapping is not found it'll be set appropriately. */ pShuffleTable[iChannelOut] = MA_CHANNEL_INDEX_NULL; channelOut = ma_channel_map_get_channel(pChannelMapOut, channelCountOut, iChannelOut); for (iChannelIn = 0; iChannelIn < channelCountIn; iChannelIn += 1) { ma_channel channelIn; channelIn = ma_channel_map_get_channel(pChannelMapIn, channelCountIn, iChannelIn); if (channelOut == channelIn) { pShuffleTable[iChannelOut] = (ma_uint8)iChannelIn; break; } /* Getting here means the channels don't exactly match, but we are going to support some relaxed matching for practicality. If, for example, there are two stereo channel maps, but one uses front left/right and the other uses side left/right, it makes logical sense to just map these. The way we'll do it is we'll check if there is a logical corresponding mapping, and if so, apply it, but we will *not* break from the loop, thereby giving the loop a chance to find an exact match later which will take priority. */ switch (channelOut) { /* Left channels. */ case MA_CHANNEL_FRONT_LEFT: case MA_CHANNEL_SIDE_LEFT: { switch (channelIn) { case MA_CHANNEL_FRONT_LEFT: case MA_CHANNEL_SIDE_LEFT: { pShuffleTable[iChannelOut] = (ma_uint8)iChannelIn; } break; } } break; /* Right channels. */ case MA_CHANNEL_FRONT_RIGHT: case MA_CHANNEL_SIDE_RIGHT: { switch (channelIn) { case MA_CHANNEL_FRONT_RIGHT: case MA_CHANNEL_SIDE_RIGHT: { pShuffleTable[iChannelOut] = (ma_uint8)iChannelIn; } break; } } break; default: break; } } } return MA_SUCCESS; } static void ma_channel_map_apply_shuffle_table_u8(ma_uint8* pFramesOut, ma_uint32 channelsOut, const ma_uint8* pFramesIn, ma_uint32 channelsIn, ma_uint64 frameCount, const ma_uint8* pShuffleTable) { ma_uint64 iFrame; ma_uint32 iChannelOut; for (iFrame = 0; iFrame < frameCount; iFrame += 1) { for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) { ma_uint8 iChannelIn = pShuffleTable[iChannelOut]; if (iChannelIn < channelsIn) { /* For safety, and to deal with MA_CHANNEL_INDEX_NULL. */ pFramesOut[iChannelOut] = pFramesIn[iChannelIn]; } else { pFramesOut[iChannelOut] = 0; } } pFramesOut += channelsOut; pFramesIn += channelsIn; } } static void ma_channel_map_apply_shuffle_table_s16(ma_int16* pFramesOut, ma_uint32 channelsOut, const ma_int16* pFramesIn, ma_uint32 channelsIn, ma_uint64 frameCount, const ma_uint8* pShuffleTable) { ma_uint64 iFrame; ma_uint32 iChannelOut; for (iFrame = 0; iFrame < frameCount; iFrame += 1) { for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) { ma_uint8 iChannelIn = pShuffleTable[iChannelOut]; if (iChannelIn < channelsIn) { /* For safety, and to deal with MA_CHANNEL_INDEX_NULL. */ pFramesOut[iChannelOut] = pFramesIn[iChannelIn]; } else { pFramesOut[iChannelOut] = 0; } } pFramesOut += channelsOut; pFramesIn += channelsIn; } } static void ma_channel_map_apply_shuffle_table_s24(ma_uint8* pFramesOut, ma_uint32 channelsOut, const ma_uint8* pFramesIn, ma_uint32 channelsIn, ma_uint64 frameCount, const ma_uint8* pShuffleTable) { ma_uint64 iFrame; ma_uint32 iChannelOut; for (iFrame = 0; iFrame < frameCount; iFrame += 1) { for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) { ma_uint8 iChannelIn = pShuffleTable[iChannelOut]; if (iChannelIn < channelsIn) { /* For safety, and to deal with MA_CHANNEL_INDEX_NULL. */ pFramesOut[iChannelOut*3 + 0] = pFramesIn[iChannelIn*3 + 0]; pFramesOut[iChannelOut*3 + 1] = pFramesIn[iChannelIn*3 + 1]; pFramesOut[iChannelOut*3 + 2] = pFramesIn[iChannelIn*3 + 2]; } else { pFramesOut[iChannelOut*3 + 0] = 0; } pFramesOut[iChannelOut*3 + 1] = 0; } pFramesOut[iChannelOut*3 + 2] = 0; pFramesOut += channelsOut*3; pFramesIn += channelsIn*3; } } static void ma_channel_map_apply_shuffle_table_s32(ma_int32* pFramesOut, ma_uint32 channelsOut, const ma_int32* pFramesIn, ma_uint32 channelsIn, ma_uint64 frameCount, const ma_uint8* pShuffleTable) { ma_uint64 iFrame; ma_uint32 iChannelOut; for (iFrame = 0; iFrame < frameCount; iFrame += 1) { for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) { ma_uint8 iChannelIn = pShuffleTable[iChannelOut]; if (iChannelIn < channelsIn) { /* For safety, and to deal with MA_CHANNEL_INDEX_NULL. */ pFramesOut[iChannelOut] = pFramesIn[iChannelIn]; } else { pFramesOut[iChannelOut] = 0; } } pFramesOut += channelsOut; pFramesIn += channelsIn; } } static void ma_channel_map_apply_shuffle_table_f32(float* pFramesOut, ma_uint32 channelsOut, const float* pFramesIn, ma_uint32 channelsIn, ma_uint64 frameCount, const ma_uint8* pShuffleTable) { ma_uint64 iFrame; ma_uint32 iChannelOut; for (iFrame = 0; iFrame < frameCount; iFrame += 1) { for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) { ma_uint8 iChannelIn = pShuffleTable[iChannelOut]; if (iChannelIn < channelsIn) { /* For safety, and to deal with MA_CHANNEL_INDEX_NULL. */ pFramesOut[iChannelOut] = pFramesIn[iChannelIn]; } else { pFramesOut[iChannelOut] = 0; } } pFramesOut += channelsOut; pFramesIn += channelsIn; } } static ma_result ma_channel_map_apply_shuffle_table(void* pFramesOut, ma_uint32 channelsOut, const void* pFramesIn, ma_uint32 channelsIn, ma_uint64 frameCount, const ma_uint8* pShuffleTable, ma_format format) { if (pFramesOut == NULL || pFramesIn == NULL || channelsOut == 0 || pShuffleTable == NULL) { return MA_INVALID_ARGS; } switch (format) { case ma_format_u8: { ma_channel_map_apply_shuffle_table_u8((ma_uint8*)pFramesOut, channelsOut, (const ma_uint8*)pFramesIn, channelsIn, frameCount, pShuffleTable); } break; case ma_format_s16: { ma_channel_map_apply_shuffle_table_s16((ma_int16*)pFramesOut, channelsOut, (const ma_int16*)pFramesIn, channelsIn, frameCount, pShuffleTable); } break; case ma_format_s24: { ma_channel_map_apply_shuffle_table_s24((ma_uint8*)pFramesOut, channelsOut, (const ma_uint8*)pFramesIn, channelsIn, frameCount, pShuffleTable); } break; case ma_format_s32: { ma_channel_map_apply_shuffle_table_s32((ma_int32*)pFramesOut, channelsOut, (const ma_int32*)pFramesIn, channelsIn, frameCount, pShuffleTable); } break; case ma_format_f32: { ma_channel_map_apply_shuffle_table_f32((float*)pFramesOut, channelsOut, (const float*)pFramesIn, channelsIn, frameCount, pShuffleTable); } break; default: return MA_INVALID_ARGS; /* Unknown format. */ } return MA_SUCCESS; } static ma_result ma_channel_map_apply_mono_out_f32(float* pFramesOut, const float* pFramesIn, const ma_channel* pChannelMapIn, ma_uint32 channelsIn, ma_uint64 frameCount) { ma_uint64 iFrame; ma_uint32 iChannelIn; ma_uint32 accumulationCount; if (pFramesOut == NULL || pFramesIn == NULL || channelsIn == 0) { return MA_INVALID_ARGS; } /* In this case the output stream needs to be the average of all channels, ignoring NONE. */ /* A quick pre-processing step to get the accumulation counter since we're ignoring NONE channels. */ accumulationCount = 0; for (iChannelIn = 0; iChannelIn < channelsIn; iChannelIn += 1) { if (ma_channel_map_get_channel(pChannelMapIn, channelsIn, iChannelIn) != MA_CHANNEL_NONE) { accumulationCount += 1; } } if (accumulationCount > 0) { /* <-- Prevent a division by zero. */ for (iFrame = 0; iFrame < frameCount; iFrame += 1) { float accumulation = 0; for (iChannelIn = 0; iChannelIn < channelsIn; iChannelIn += 1) { ma_channel channelIn = ma_channel_map_get_channel(pChannelMapIn, channelsIn, iChannelIn); if (channelIn != MA_CHANNEL_NONE) { accumulation += pFramesIn[iChannelIn]; } } pFramesOut[0] = accumulation / accumulationCount; pFramesOut += 1; pFramesIn += channelsIn; } } else { ma_silence_pcm_frames(pFramesOut, frameCount, ma_format_f32, 1); } return MA_SUCCESS; } static ma_result ma_channel_map_apply_mono_in_f32(float* pFramesOut, const ma_channel* pChannelMapOut, ma_uint32 channelsOut, const float* pFramesIn, ma_uint64 frameCount, ma_mono_expansion_mode monoExpansionMode) { ma_uint64 iFrame; ma_uint32 iChannelOut; if (pFramesOut == NULL || channelsOut == 0 || pFramesIn == NULL) { return MA_INVALID_ARGS; } /* Note that the MA_CHANNEL_NONE channel must be ignored in all cases. */ switch (monoExpansionMode) { case ma_mono_expansion_mode_average: { float weight; ma_uint32 validChannelCount = 0; for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) { ma_channel channelOut = ma_channel_map_get_channel(pChannelMapOut, channelsOut, iChannelOut); if (channelOut != MA_CHANNEL_NONE) { validChannelCount += 1; } } weight = 1.0f / validChannelCount; for (iFrame = 0; iFrame < frameCount; iFrame += 1) { for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) { ma_channel channelOut = ma_channel_map_get_channel(pChannelMapOut, channelsOut, iChannelOut); if (channelOut != MA_CHANNEL_NONE) { pFramesOut[iChannelOut] = pFramesIn[0] * weight; } } pFramesOut += channelsOut; pFramesIn += 1; } } break; case ma_mono_expansion_mode_stereo_only: { if (channelsOut >= 2) { ma_uint32 iChannelLeft = (ma_uint32)-1; ma_uint32 iChannelRight = (ma_uint32)-1; /* We first need to find our stereo channels. We prefer front-left and front-right, but if they're not available, we'll also try side-left and side-right. If neither are available we'll fall through to the default case below. */ for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) { ma_channel channelOut = ma_channel_map_get_channel(pChannelMapOut, channelsOut, iChannelOut); if (channelOut == MA_CHANNEL_SIDE_LEFT) { iChannelLeft = iChannelOut; } if (channelOut == MA_CHANNEL_SIDE_RIGHT) { iChannelRight = iChannelOut; } } for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) { ma_channel channelOut = ma_channel_map_get_channel(pChannelMapOut, channelsOut, iChannelOut); if (channelOut == MA_CHANNEL_FRONT_LEFT) { iChannelLeft = iChannelOut; } if (channelOut == MA_CHANNEL_FRONT_RIGHT) { iChannelRight = iChannelOut; } } if (iChannelLeft != (ma_uint32)-1 && iChannelRight != (ma_uint32)-1) { /* We found our stereo channels so we can duplicate the signal across those channels. */ for (iFrame = 0; iFrame < frameCount; iFrame += 1) { for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) { ma_channel channelOut = ma_channel_map_get_channel(pChannelMapOut, channelsOut, iChannelOut); if (channelOut != MA_CHANNEL_NONE) { if (iChannelOut == iChannelLeft || iChannelOut == iChannelRight) { pFramesOut[iChannelOut] = pFramesIn[0]; } else { pFramesOut[iChannelOut] = 0.0f; } } } pFramesOut += channelsOut; pFramesIn += 1; } break; /* Get out of the switch. */ } else { /* Fallthrough. Does not have left and right channels. */ goto default_handler; } } else { /* Fallthrough. Does not have stereo channels. */ goto default_handler; } }; /* Fallthrough. See comments above. */ case ma_mono_expansion_mode_duplicate: default: { default_handler: { for (iFrame = 0; iFrame < frameCount; iFrame += 1) { for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) { ma_channel channelOut = ma_channel_map_get_channel(pChannelMapOut, channelsOut, iChannelOut); if (channelOut != MA_CHANNEL_NONE) { pFramesOut[iChannelOut] = pFramesIn[0]; } } pFramesOut += channelsOut; pFramesIn += 1; } } } break; } return MA_SUCCESS; } static void ma_channel_map_apply_f32(float* pFramesOut, const ma_channel* pChannelMapOut, ma_uint32 channelsOut, const float* pFramesIn, const ma_channel* pChannelMapIn, ma_uint32 channelsIn, ma_uint64 frameCount, ma_channel_mix_mode mode, ma_mono_expansion_mode monoExpansionMode) { ma_channel_conversion_path conversionPath = ma_channel_map_get_conversion_path(pChannelMapIn, channelsIn, pChannelMapOut, channelsOut, mode); /* Optimized Path: Passthrough */ if (conversionPath == ma_channel_conversion_path_passthrough) { ma_copy_pcm_frames(pFramesOut, pFramesIn, frameCount, ma_format_f32, channelsOut); return; } /* Special Path: Mono Output. */ if (conversionPath == ma_channel_conversion_path_mono_out) { ma_channel_map_apply_mono_out_f32(pFramesOut, pFramesIn, pChannelMapIn, channelsIn, frameCount); return; } /* Special Path: Mono Input. */ if (conversionPath == ma_channel_conversion_path_mono_in) { ma_channel_map_apply_mono_in_f32(pFramesOut, pChannelMapOut, channelsOut, pFramesIn, frameCount, monoExpansionMode); return; } /* Getting here means we aren't running on an optimized conversion path. */ if (channelsOut <= MA_MAX_CHANNELS) { ma_result result; if (mode == ma_channel_mix_mode_simple) { ma_channel shuffleTable[MA_MAX_CHANNELS]; result = ma_channel_map_build_shuffle_table(pChannelMapIn, channelsIn, pChannelMapOut, channelsOut, shuffleTable); if (result != MA_SUCCESS) { return; } result = ma_channel_map_apply_shuffle_table(pFramesOut, channelsOut, pFramesIn, channelsIn, frameCount, shuffleTable, ma_format_f32); if (result != MA_SUCCESS) { return; } } else { ma_uint32 iFrame; ma_uint32 iChannelOut; ma_uint32 iChannelIn; float weights[32][32]; /* Do not use MA_MAX_CHANNELS here! */ /* If we have a small enough number of channels, pre-compute the weights. Otherwise we'll just need to fall back to a slower path because otherwise we'll run out of stack space. */ if (channelsIn <= ma_countof(weights) && channelsOut <= ma_countof(weights)) { /* Pre-compute weights. */ for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) { ma_channel channelOut = ma_channel_map_get_channel(pChannelMapOut, channelsOut, iChannelOut); for (iChannelIn = 0; iChannelIn < channelsIn; iChannelIn += 1) { ma_channel channelIn = ma_channel_map_get_channel(pChannelMapIn, channelsIn, iChannelIn); weights[iChannelOut][iChannelIn] = ma_calculate_channel_position_rectangular_weight(channelOut, channelIn); } } for (iFrame = 0; iFrame < frameCount; iFrame += 1) { for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) { float accumulation = 0; for (iChannelIn = 0; iChannelIn < channelsIn; iChannelIn += 1) { accumulation += pFramesIn[iChannelIn] * weights[iChannelOut][iChannelIn]; } pFramesOut[iChannelOut] = accumulation; } pFramesOut += channelsOut; pFramesIn += channelsIn; } } else { /* Cannot pre-compute weights because not enough room in stack-allocated buffer. */ for (iFrame = 0; iFrame < frameCount; iFrame += 1) { for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) { float accumulation = 0; ma_channel channelOut = ma_channel_map_get_channel(pChannelMapOut, channelsOut, iChannelOut); for (iChannelIn = 0; iChannelIn < channelsIn; iChannelIn += 1) { ma_channel channelIn = ma_channel_map_get_channel(pChannelMapIn, channelsIn, iChannelIn); accumulation += pFramesIn[iChannelIn] * ma_calculate_channel_position_rectangular_weight(channelOut, channelIn); } pFramesOut[iChannelOut] = accumulation; } pFramesOut += channelsOut; pFramesIn += channelsIn; } } } } else { /* Fall back to silence. If you hit this, what are you doing with so many channels?! */ ma_silence_pcm_frames(pFramesOut, frameCount, ma_format_f32, channelsOut); } } typedef struct { size_t sizeInBytes; size_t channelMapInOffset; size_t channelMapOutOffset; size_t shuffleTableOffset; size_t weightsOffset; } ma_channel_converter_heap_layout; static ma_channel_conversion_path ma_channel_converter_config_get_conversion_path(const ma_channel_converter_config* pConfig) { return ma_channel_map_get_conversion_path(pConfig->pChannelMapIn, pConfig->channelsIn, pConfig->pChannelMapOut, pConfig->channelsOut, pConfig->mixingMode); } static ma_result ma_channel_converter_get_heap_layout(const ma_channel_converter_config* pConfig, ma_channel_converter_heap_layout* pHeapLayout) { ma_channel_conversion_path conversionPath; MA_ASSERT(pHeapLayout != NULL); if (pConfig == NULL) { return MA_INVALID_ARGS; } if (pConfig->channelsIn == 0 || pConfig->channelsOut == 0) { return MA_INVALID_ARGS; } if (!ma_channel_map_is_valid(pConfig->pChannelMapIn, pConfig->channelsIn)) { return MA_INVALID_ARGS; } if (!ma_channel_map_is_valid(pConfig->pChannelMapOut, pConfig->channelsOut)) { return MA_INVALID_ARGS; } pHeapLayout->sizeInBytes = 0; /* Input channel map. Only need to allocate this if we have an input channel map (otherwise default channel map is assumed). */ pHeapLayout->channelMapInOffset = pHeapLayout->sizeInBytes; if (pConfig->pChannelMapIn != NULL) { pHeapLayout->sizeInBytes += sizeof(ma_channel) * pConfig->channelsIn; } /* Output channel map. Only need to allocate this if we have an output channel map (otherwise default channel map is assumed). */ pHeapLayout->channelMapOutOffset = pHeapLayout->sizeInBytes; if (pConfig->pChannelMapOut != NULL) { pHeapLayout->sizeInBytes += sizeof(ma_channel) * pConfig->channelsOut; } /* Alignment for the next section. */ pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes); /* Whether or not we use weights of a shuffle table depends on the channel map themselves and the algorithm we've chosen. */ conversionPath = ma_channel_converter_config_get_conversion_path(pConfig); /* Shuffle table */ pHeapLayout->shuffleTableOffset = pHeapLayout->sizeInBytes; if (conversionPath == ma_channel_conversion_path_shuffle) { pHeapLayout->sizeInBytes += sizeof(ma_uint8) * pConfig->channelsOut; } /* Weights */ pHeapLayout->weightsOffset = pHeapLayout->sizeInBytes; if (conversionPath == ma_channel_conversion_path_weights) { pHeapLayout->sizeInBytes += sizeof(float*) * pConfig->channelsIn; pHeapLayout->sizeInBytes += sizeof(float ) * pConfig->channelsIn * pConfig->channelsOut; } /* Make sure allocation size is aligned. */ pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes); return MA_SUCCESS; } MA_API ma_result ma_channel_converter_get_heap_size(const ma_channel_converter_config* pConfig, size_t* pHeapSizeInBytes) { ma_result result; ma_channel_converter_heap_layout heapLayout; if (pHeapSizeInBytes == NULL) { return MA_INVALID_ARGS; } *pHeapSizeInBytes = 0; result = ma_channel_converter_get_heap_layout(pConfig, &heapLayout); if (result != MA_SUCCESS) { return result; } *pHeapSizeInBytes = heapLayout.sizeInBytes; return MA_SUCCESS; } MA_API ma_result ma_channel_converter_init_preallocated(const ma_channel_converter_config* pConfig, void* pHeap, ma_channel_converter* pConverter) { ma_result result; ma_channel_converter_heap_layout heapLayout; if (pConverter == NULL) { return MA_INVALID_ARGS; } MA_ZERO_OBJECT(pConverter); result = ma_channel_converter_get_heap_layout(pConfig, &heapLayout); if (result != MA_SUCCESS) { return result; } pConverter->_pHeap = pHeap; MA_ZERO_MEMORY(pConverter->_pHeap, heapLayout.sizeInBytes); pConverter->format = pConfig->format; pConverter->channelsIn = pConfig->channelsIn; pConverter->channelsOut = pConfig->channelsOut; pConverter->mixingMode = pConfig->mixingMode; if (pConfig->pChannelMapIn != NULL) { pConverter->pChannelMapIn = (ma_channel*)ma_offset_ptr(pHeap, heapLayout.channelMapInOffset); ma_channel_map_copy_or_default(pConverter->pChannelMapIn, pConfig->channelsIn, pConfig->pChannelMapIn, pConfig->channelsIn); } else { pConverter->pChannelMapIn = NULL; /* Use default channel map. */ } if (pConfig->pChannelMapOut != NULL) { pConverter->pChannelMapOut = (ma_channel*)ma_offset_ptr(pHeap, heapLayout.channelMapOutOffset); ma_channel_map_copy_or_default(pConverter->pChannelMapOut, pConfig->channelsOut, pConfig->pChannelMapOut, pConfig->channelsOut); } else { pConverter->pChannelMapOut = NULL; /* Use default channel map. */ } pConverter->conversionPath = ma_channel_converter_config_get_conversion_path(pConfig); if (pConverter->conversionPath == ma_channel_conversion_path_shuffle) { pConverter->pShuffleTable = (ma_uint8*)ma_offset_ptr(pHeap, heapLayout.shuffleTableOffset); ma_channel_map_build_shuffle_table(pConverter->pChannelMapIn, pConverter->channelsIn, pConverter->pChannelMapOut, pConverter->channelsOut, pConverter->pShuffleTable); } if (pConverter->conversionPath == ma_channel_conversion_path_weights) { ma_uint32 iChannelIn; ma_uint32 iChannelOut; if (pConverter->format == ma_format_f32) { pConverter->weights.f32 = (float** )ma_offset_ptr(pHeap, heapLayout.weightsOffset); for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; iChannelIn += 1) { pConverter->weights.f32[iChannelIn] = (float*)ma_offset_ptr(pHeap, heapLayout.weightsOffset + ((sizeof(float*) * pConverter->channelsIn) + (sizeof(float) * pConverter->channelsOut * iChannelIn))); } } else { pConverter->weights.s16 = (ma_int32**)ma_offset_ptr(pHeap, heapLayout.weightsOffset); for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; iChannelIn += 1) { pConverter->weights.s16[iChannelIn] = (ma_int32*)ma_offset_ptr(pHeap, heapLayout.weightsOffset + ((sizeof(ma_int32*) * pConverter->channelsIn) + (sizeof(ma_int32) * pConverter->channelsOut * iChannelIn))); } } /* Silence our weights by default. */ for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; iChannelIn += 1) { for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; iChannelOut += 1) { if (pConverter->format == ma_format_f32) { pConverter->weights.f32[iChannelIn][iChannelOut] = 0.0f; } else { pConverter->weights.s16[iChannelIn][iChannelOut] = 0; } } } /* We now need to fill out our weights table. This is determined by the mixing mode. */ /* In all cases we need to make sure all channels that are present in both channel maps have a 1:1 mapping. */ for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) { ma_channel channelPosIn = ma_channel_map_get_channel(pConverter->pChannelMapIn, pConverter->channelsIn, iChannelIn); for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) { ma_channel channelPosOut = ma_channel_map_get_channel(pConverter->pChannelMapOut, pConverter->channelsOut, iChannelOut); if (channelPosIn == channelPosOut) { float weight = 1; if (pConverter->format == ma_format_f32) { pConverter->weights.f32[iChannelIn][iChannelOut] = weight; } else { pConverter->weights.s16[iChannelIn][iChannelOut] = ma_channel_converter_float_to_fixed(weight); } } } } switch (pConverter->mixingMode) { case ma_channel_mix_mode_custom_weights: { if (pConfig->ppWeights == NULL) { return MA_INVALID_ARGS; /* Config specified a custom weights mixing mode, but no custom weights have been specified. */ } for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; iChannelIn += 1) { for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; iChannelOut += 1) { float weight = pConfig->ppWeights[iChannelIn][iChannelOut]; if (pConverter->format == ma_format_f32) { pConverter->weights.f32[iChannelIn][iChannelOut] = weight; } else { pConverter->weights.s16[iChannelIn][iChannelOut] = ma_channel_converter_float_to_fixed(weight); } } } } break; case ma_channel_mix_mode_simple: { /* In simple mode, only set weights for channels that have exactly matching types, leave the rest at zero. The 1:1 mappings have already been covered before this switch statement. */ } break; case ma_channel_mix_mode_rectangular: default: { /* Unmapped input channels. */ for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) { ma_channel channelPosIn = ma_channel_map_get_channel(pConverter->pChannelMapIn, pConverter->channelsIn, iChannelIn); if (ma_is_spatial_channel_position(channelPosIn)) { if (!ma_channel_map_contains_channel_position(pConverter->channelsOut, pConverter->pChannelMapOut, channelPosIn)) { for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) { ma_channel channelPosOut = ma_channel_map_get_channel(pConverter->pChannelMapOut, pConverter->channelsOut, iChannelOut); if (ma_is_spatial_channel_position(channelPosOut)) { float weight = 0; if (pConverter->mixingMode == ma_channel_mix_mode_rectangular) { weight = ma_calculate_channel_position_rectangular_weight(channelPosIn, channelPosOut); } /* Only apply the weight if we haven't already got some contribution from the respective channels. */ if (pConverter->format == ma_format_f32) { if (pConverter->weights.f32[iChannelIn][iChannelOut] == 0) { pConverter->weights.f32[iChannelIn][iChannelOut] = weight; } } else { if (pConverter->weights.s16[iChannelIn][iChannelOut] == 0) { pConverter->weights.s16[iChannelIn][iChannelOut] = ma_channel_converter_float_to_fixed(weight); } } } } } } } /* Unmapped output channels. */ for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) { ma_channel channelPosOut = ma_channel_map_get_channel(pConverter->pChannelMapOut, pConverter->channelsOut, iChannelOut); if (ma_is_spatial_channel_position(channelPosOut)) { if (!ma_channel_map_contains_channel_position(pConverter->channelsIn, pConverter->pChannelMapIn, channelPosOut)) { for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) { ma_channel channelPosIn = ma_channel_map_get_channel(pConverter->pChannelMapIn, pConverter->channelsIn, iChannelIn); if (ma_is_spatial_channel_position(channelPosIn)) { float weight = 0; if (pConverter->mixingMode == ma_channel_mix_mode_rectangular) { weight = ma_calculate_channel_position_rectangular_weight(channelPosIn, channelPosOut); } /* Only apply the weight if we haven't already got some contribution from the respective channels. */ if (pConverter->format == ma_format_f32) { if (pConverter->weights.f32[iChannelIn][iChannelOut] == 0) { pConverter->weights.f32[iChannelIn][iChannelOut] = weight; } } else { if (pConverter->weights.s16[iChannelIn][iChannelOut] == 0) { pConverter->weights.s16[iChannelIn][iChannelOut] = ma_channel_converter_float_to_fixed(weight); } } } } } } } /* If LFE is in the output channel map but was not present in the input channel map, configure its weight now */ if (pConfig->calculateLFEFromSpatialChannels) { if (!ma_channel_map_contains_channel_position(pConverter->channelsIn, pConverter->pChannelMapIn, MA_CHANNEL_LFE)) { ma_uint32 spatialChannelCount = ma_channel_map_get_spatial_channel_count(pConverter->pChannelMapIn, pConverter->channelsIn); ma_uint32 iChannelOutLFE; if (spatialChannelCount > 0 && ma_channel_map_find_channel_position(pConverter->channelsOut, pConverter->pChannelMapOut, MA_CHANNEL_LFE, &iChannelOutLFE)) { const float weightForLFE = 1.0f / spatialChannelCount; for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) { const ma_channel channelPosIn = ma_channel_map_get_channel(pConverter->pChannelMapIn, pConverter->channelsIn, iChannelIn); if (ma_is_spatial_channel_position(channelPosIn)) { if (pConverter->format == ma_format_f32) { if (pConverter->weights.f32[iChannelIn][iChannelOutLFE] == 0) { pConverter->weights.f32[iChannelIn][iChannelOutLFE] = weightForLFE; } } else { if (pConverter->weights.s16[iChannelIn][iChannelOutLFE] == 0) { pConverter->weights.s16[iChannelIn][iChannelOutLFE] = ma_channel_converter_float_to_fixed(weightForLFE); } } } } } } } } break; } } return MA_SUCCESS; } MA_API ma_result ma_channel_converter_init(const ma_channel_converter_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_channel_converter* pConverter) { ma_result result; size_t heapSizeInBytes; void* pHeap; result = ma_channel_converter_get_heap_size(pConfig, &heapSizeInBytes); if (result != MA_SUCCESS) { return result; } if (heapSizeInBytes > 0) { pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); if (pHeap == NULL) { return MA_OUT_OF_MEMORY; } } else { pHeap = NULL; } result = ma_channel_converter_init_preallocated(pConfig, pHeap, pConverter); if (result != MA_SUCCESS) { ma_free(pHeap, pAllocationCallbacks); return result; } pConverter->_ownsHeap = MA_TRUE; return MA_SUCCESS; } MA_API void ma_channel_converter_uninit(ma_channel_converter* pConverter, const ma_allocation_callbacks* pAllocationCallbacks) { if (pConverter == NULL) { return; } if (pConverter->_ownsHeap) { ma_free(pConverter->_pHeap, pAllocationCallbacks); } } static ma_result ma_channel_converter_process_pcm_frames__passthrough(ma_channel_converter* pConverter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) { MA_ASSERT(pConverter != NULL); MA_ASSERT(pFramesOut != NULL); MA_ASSERT(pFramesIn != NULL); ma_copy_memory_64(pFramesOut, pFramesIn, frameCount * ma_get_bytes_per_frame(pConverter->format, pConverter->channelsOut)); return MA_SUCCESS; } static ma_result ma_channel_converter_process_pcm_frames__shuffle(ma_channel_converter* pConverter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) { MA_ASSERT(pConverter != NULL); MA_ASSERT(pFramesOut != NULL); MA_ASSERT(pFramesIn != NULL); MA_ASSERT(pConverter->channelsIn == pConverter->channelsOut); return ma_channel_map_apply_shuffle_table(pFramesOut, pConverter->channelsOut, pFramesIn, pConverter->channelsIn, frameCount, pConverter->pShuffleTable, pConverter->format); } static ma_result ma_channel_converter_process_pcm_frames__mono_in(ma_channel_converter* pConverter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) { ma_uint64 iFrame; MA_ASSERT(pConverter != NULL); MA_ASSERT(pFramesOut != NULL); MA_ASSERT(pFramesIn != NULL); MA_ASSERT(pConverter->channelsIn == 1); switch (pConverter->format) { case ma_format_u8: { /* */ ma_uint8* pFramesOutU8 = ( ma_uint8*)pFramesOut; const ma_uint8* pFramesInU8 = (const ma_uint8*)pFramesIn; for (iFrame = 0; iFrame < frameCount; ++iFrame) { ma_uint32 iChannel; for (iChannel = 0; iChannel < pConverter->channelsOut; iChannel += 1) { pFramesOutU8[iFrame*pConverter->channelsOut + iChannel] = pFramesInU8[iFrame]; } } } break; case ma_format_s16: { /* */ ma_int16* pFramesOutS16 = ( ma_int16*)pFramesOut; const ma_int16* pFramesInS16 = (const ma_int16*)pFramesIn; if (pConverter->channelsOut == 2) { for (iFrame = 0; iFrame < frameCount; ++iFrame) { pFramesOutS16[iFrame*2 + 0] = pFramesInS16[iFrame]; pFramesOutS16[iFrame*2 + 1] = pFramesInS16[iFrame]; } } else { for (iFrame = 0; iFrame < frameCount; ++iFrame) { ma_uint32 iChannel; for (iChannel = 0; iChannel < pConverter->channelsOut; iChannel += 1) { pFramesOutS16[iFrame*pConverter->channelsOut + iChannel] = pFramesInS16[iFrame]; } } } } break; case ma_format_s24: { /* */ ma_uint8* pFramesOutS24 = ( ma_uint8*)pFramesOut; const ma_uint8* pFramesInS24 = (const ma_uint8*)pFramesIn; for (iFrame = 0; iFrame < frameCount; ++iFrame) { ma_uint32 iChannel; for (iChannel = 0; iChannel < pConverter->channelsOut; iChannel += 1) { ma_uint64 iSampleOut = iFrame*pConverter->channelsOut + iChannel; ma_uint64 iSampleIn = iFrame; pFramesOutS24[iSampleOut*3 + 0] = pFramesInS24[iSampleIn*3 + 0]; pFramesOutS24[iSampleOut*3 + 1] = pFramesInS24[iSampleIn*3 + 1]; pFramesOutS24[iSampleOut*3 + 2] = pFramesInS24[iSampleIn*3 + 2]; } } } break; case ma_format_s32: { /* */ ma_int32* pFramesOutS32 = ( ma_int32*)pFramesOut; const ma_int32* pFramesInS32 = (const ma_int32*)pFramesIn; for (iFrame = 0; iFrame < frameCount; ++iFrame) { ma_uint32 iChannel; for (iChannel = 0; iChannel < pConverter->channelsOut; iChannel += 1) { pFramesOutS32[iFrame*pConverter->channelsOut + iChannel] = pFramesInS32[iFrame]; } } } break; case ma_format_f32: { /* */ float* pFramesOutF32 = ( float*)pFramesOut; const float* pFramesInF32 = (const float*)pFramesIn; if (pConverter->channelsOut == 2) { for (iFrame = 0; iFrame < frameCount; ++iFrame) { pFramesOutF32[iFrame*2 + 0] = pFramesInF32[iFrame]; pFramesOutF32[iFrame*2 + 1] = pFramesInF32[iFrame]; } } else { for (iFrame = 0; iFrame < frameCount; ++iFrame) { ma_uint32 iChannel; for (iChannel = 0; iChannel < pConverter->channelsOut; iChannel += 1) { pFramesOutF32[iFrame*pConverter->channelsOut + iChannel] = pFramesInF32[iFrame]; } } } } break; default: return MA_INVALID_OPERATION; /* Unknown format. */ } return MA_SUCCESS; } static ma_result ma_channel_converter_process_pcm_frames__mono_out(ma_channel_converter* pConverter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) { ma_uint64 iFrame; ma_uint32 iChannel; MA_ASSERT(pConverter != NULL); MA_ASSERT(pFramesOut != NULL); MA_ASSERT(pFramesIn != NULL); MA_ASSERT(pConverter->channelsOut == 1); switch (pConverter->format) { case ma_format_u8: { /* */ ma_uint8* pFramesOutU8 = ( ma_uint8*)pFramesOut; const ma_uint8* pFramesInU8 = (const ma_uint8*)pFramesIn; for (iFrame = 0; iFrame < frameCount; ++iFrame) { ma_int32 t = 0; for (iChannel = 0; iChannel < pConverter->channelsIn; iChannel += 1) { t += ma_pcm_sample_u8_to_s16_no_scale(pFramesInU8[iFrame*pConverter->channelsIn + iChannel]); } pFramesOutU8[iFrame] = ma_clip_u8(t / pConverter->channelsOut); } } break; case ma_format_s16: { /* */ ma_int16* pFramesOutS16 = ( ma_int16*)pFramesOut; const ma_int16* pFramesInS16 = (const ma_int16*)pFramesIn; for (iFrame = 0; iFrame < frameCount; ++iFrame) { ma_int32 t = 0; for (iChannel = 0; iChannel < pConverter->channelsIn; iChannel += 1) { t += pFramesInS16[iFrame*pConverter->channelsIn + iChannel]; } pFramesOutS16[iFrame] = (ma_int16)(t / pConverter->channelsIn); } } break; case ma_format_s24: { /* */ ma_uint8* pFramesOutS24 = ( ma_uint8*)pFramesOut; const ma_uint8* pFramesInS24 = (const ma_uint8*)pFramesIn; for (iFrame = 0; iFrame < frameCount; ++iFrame) { ma_int64 t = 0; for (iChannel = 0; iChannel < pConverter->channelsIn; iChannel += 1) { t += ma_pcm_sample_s24_to_s32_no_scale(&pFramesInS24[(iFrame*pConverter->channelsIn + iChannel)*3]); } ma_pcm_sample_s32_to_s24_no_scale(t / pConverter->channelsIn, &pFramesOutS24[iFrame*3]); } } break; case ma_format_s32: { /* */ ma_int32* pFramesOutS32 = ( ma_int32*)pFramesOut; const ma_int32* pFramesInS32 = (const ma_int32*)pFramesIn; for (iFrame = 0; iFrame < frameCount; ++iFrame) { ma_int64 t = 0; for (iChannel = 0; iChannel < pConverter->channelsIn; iChannel += 1) { t += pFramesInS32[iFrame*pConverter->channelsIn + iChannel]; } pFramesOutS32[iFrame] = (ma_int32)(t / pConverter->channelsIn); } } break; case ma_format_f32: { /* */ float* pFramesOutF32 = ( float*)pFramesOut; const float* pFramesInF32 = (const float*)pFramesIn; for (iFrame = 0; iFrame < frameCount; ++iFrame) { float t = 0; for (iChannel = 0; iChannel < pConverter->channelsIn; iChannel += 1) { t += pFramesInF32[iFrame*pConverter->channelsIn + iChannel]; } pFramesOutF32[iFrame] = t / pConverter->channelsIn; } } break; default: return MA_INVALID_OPERATION; /* Unknown format. */ } return MA_SUCCESS; } static ma_result ma_channel_converter_process_pcm_frames__weights(ma_channel_converter* pConverter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) { ma_uint32 iFrame; ma_uint32 iChannelIn; ma_uint32 iChannelOut; MA_ASSERT(pConverter != NULL); MA_ASSERT(pFramesOut != NULL); MA_ASSERT(pFramesIn != NULL); /* This is the more complicated case. Each of the output channels is accumulated with 0 or more input channels. */ /* Clear. */ ma_zero_memory_64(pFramesOut, frameCount * ma_get_bytes_per_frame(pConverter->format, pConverter->channelsOut)); /* Accumulate. */ switch (pConverter->format) { case ma_format_u8: { /* */ ma_uint8* pFramesOutU8 = ( ma_uint8*)pFramesOut; const ma_uint8* pFramesInU8 = (const ma_uint8*)pFramesIn; for (iFrame = 0; iFrame < frameCount; iFrame += 1) { for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) { for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) { ma_int16 u8_O = ma_pcm_sample_u8_to_s16_no_scale(pFramesOutU8[iFrame*pConverter->channelsOut + iChannelOut]); ma_int16 u8_I = ma_pcm_sample_u8_to_s16_no_scale(pFramesInU8 [iFrame*pConverter->channelsIn + iChannelIn ]); ma_int32 s = (ma_int32)ma_clamp(u8_O + ((u8_I * pConverter->weights.s16[iChannelIn][iChannelOut]) >> MA_CHANNEL_CONVERTER_FIXED_POINT_SHIFT), -128, 127); pFramesOutU8[iFrame*pConverter->channelsOut + iChannelOut] = ma_clip_u8((ma_int16)s); } } } } break; case ma_format_s16: { /* */ ma_int16* pFramesOutS16 = ( ma_int16*)pFramesOut; const ma_int16* pFramesInS16 = (const ma_int16*)pFramesIn; for (iFrame = 0; iFrame < frameCount; iFrame += 1) { for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) { for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) { ma_int32 s = pFramesOutS16[iFrame*pConverter->channelsOut + iChannelOut]; s += (pFramesInS16[iFrame*pConverter->channelsIn + iChannelIn] * pConverter->weights.s16[iChannelIn][iChannelOut]) >> MA_CHANNEL_CONVERTER_FIXED_POINT_SHIFT; pFramesOutS16[iFrame*pConverter->channelsOut + iChannelOut] = (ma_int16)ma_clamp(s, -32768, 32767); } } } } break; case ma_format_s24: { /* */ ma_uint8* pFramesOutS24 = ( ma_uint8*)pFramesOut; const ma_uint8* pFramesInS24 = (const ma_uint8*)pFramesIn; for (iFrame = 0; iFrame < frameCount; iFrame += 1) { for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) { for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) { ma_int64 s24_O = ma_pcm_sample_s24_to_s32_no_scale(&pFramesOutS24[(iFrame*pConverter->channelsOut + iChannelOut)*3]); ma_int64 s24_I = ma_pcm_sample_s24_to_s32_no_scale(&pFramesInS24 [(iFrame*pConverter->channelsIn + iChannelIn )*3]); ma_int64 s24 = (ma_int32)ma_clamp(s24_O + ((s24_I * pConverter->weights.s16[iChannelIn][iChannelOut]) >> MA_CHANNEL_CONVERTER_FIXED_POINT_SHIFT), -8388608, 8388607); ma_pcm_sample_s32_to_s24_no_scale(s24, &pFramesOutS24[(iFrame*pConverter->channelsOut + iChannelOut)*3]); } } } } break; case ma_format_s32: { /* */ ma_int32* pFramesOutS32 = ( ma_int32*)pFramesOut; const ma_int32* pFramesInS32 = (const ma_int32*)pFramesIn; for (iFrame = 0; iFrame < frameCount; iFrame += 1) { for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) { for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) { ma_int64 s = pFramesOutS32[iFrame*pConverter->channelsOut + iChannelOut]; s += ((ma_int64)pFramesInS32[iFrame*pConverter->channelsIn + iChannelIn] * pConverter->weights.s16[iChannelIn][iChannelOut]) >> MA_CHANNEL_CONVERTER_FIXED_POINT_SHIFT; pFramesOutS32[iFrame*pConverter->channelsOut + iChannelOut] = ma_clip_s32(s); } } } } break; case ma_format_f32: { /* */ float* pFramesOutF32 = ( float*)pFramesOut; const float* pFramesInF32 = (const float*)pFramesIn; for (iFrame = 0; iFrame < frameCount; iFrame += 1) { for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) { for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) { pFramesOutF32[iFrame*pConverter->channelsOut + iChannelOut] += pFramesInF32[iFrame*pConverter->channelsIn + iChannelIn] * pConverter->weights.f32[iChannelIn][iChannelOut]; } } } } break; default: return MA_INVALID_OPERATION; /* Unknown format. */ } return MA_SUCCESS; } MA_API ma_result ma_channel_converter_process_pcm_frames(ma_channel_converter* pConverter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount) { if (pConverter == NULL) { return MA_INVALID_ARGS; } if (pFramesOut == NULL) { return MA_INVALID_ARGS; } if (pFramesIn == NULL) { ma_zero_memory_64(pFramesOut, frameCount * ma_get_bytes_per_frame(pConverter->format, pConverter->channelsOut)); return MA_SUCCESS; } switch (pConverter->conversionPath) { case ma_channel_conversion_path_passthrough: return ma_channel_converter_process_pcm_frames__passthrough(pConverter, pFramesOut, pFramesIn, frameCount); case ma_channel_conversion_path_mono_out: return ma_channel_converter_process_pcm_frames__mono_out(pConverter, pFramesOut, pFramesIn, frameCount); case ma_channel_conversion_path_mono_in: return ma_channel_converter_process_pcm_frames__mono_in(pConverter, pFramesOut, pFramesIn, frameCount); case ma_channel_conversion_path_shuffle: return ma_channel_converter_process_pcm_frames__shuffle(pConverter, pFramesOut, pFramesIn, frameCount); case ma_channel_conversion_path_weights: default: { return ma_channel_converter_process_pcm_frames__weights(pConverter, pFramesOut, pFramesIn, frameCount); } } } MA_API ma_result ma_channel_converter_get_input_channel_map(const ma_channel_converter* pConverter, ma_channel* pChannelMap, size_t channelMapCap) { if (pConverter == NULL || pChannelMap == NULL) { return MA_INVALID_ARGS; } ma_channel_map_copy_or_default(pChannelMap, channelMapCap, pConverter->pChannelMapIn, pConverter->channelsIn); return MA_SUCCESS; } MA_API ma_result ma_channel_converter_get_output_channel_map(const ma_channel_converter* pConverter, ma_channel* pChannelMap, size_t channelMapCap) { if (pConverter == NULL || pChannelMap == NULL) { return MA_INVALID_ARGS; } ma_channel_map_copy_or_default(pChannelMap, channelMapCap, pConverter->pChannelMapOut, pConverter->channelsOut); return MA_SUCCESS; } /************************************************************************************************************************************************************** Data Conversion **************************************************************************************************************************************************************/ MA_API ma_data_converter_config ma_data_converter_config_init_default() { ma_data_converter_config config; MA_ZERO_OBJECT(&config); config.ditherMode = ma_dither_mode_none; config.resampling.algorithm = ma_resample_algorithm_linear; config.allowDynamicSampleRate = MA_FALSE; /* Disable dynamic sample rates by default because dynamic rate adjustments should be quite rare and it allows an optimization for cases when the in and out sample rates are the same. */ /* Linear resampling defaults. */ config.resampling.linear.lpfOrder = 1; return config; } MA_API ma_data_converter_config ma_data_converter_config_init(ma_format formatIn, ma_format formatOut, ma_uint32 channelsIn, ma_uint32 channelsOut, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut) { ma_data_converter_config config = ma_data_converter_config_init_default(); config.formatIn = formatIn; config.formatOut = formatOut; config.channelsIn = channelsIn; config.channelsOut = channelsOut; config.sampleRateIn = sampleRateIn; config.sampleRateOut = sampleRateOut; return config; } typedef struct { size_t sizeInBytes; size_t channelConverterOffset; size_t resamplerOffset; } ma_data_converter_heap_layout; static ma_bool32 ma_data_converter_config_is_resampler_required(const ma_data_converter_config* pConfig) { MA_ASSERT(pConfig != NULL); return pConfig->allowDynamicSampleRate || pConfig->sampleRateIn != pConfig->sampleRateOut; } static ma_format ma_data_converter_config_get_mid_format(const ma_data_converter_config* pConfig) { MA_ASSERT(pConfig != NULL); /* We want to avoid as much data conversion as possible. The channel converter and linear resampler both support s16 and f32 natively. We need to decide on the format to use for this stage. We call this the mid format because it's used in the middle stage of the conversion pipeline. If the output format is either s16 or f32 we use that one. If that is not the case it will do the same thing for the input format. If it's neither we just use f32. If we are using a custom resampling backend, we can only guarantee that f32 will be supported so we'll be forced to use that if resampling is required. */ if (ma_data_converter_config_is_resampler_required(pConfig) && pConfig->resampling.algorithm != ma_resample_algorithm_linear) { return ma_format_f32; /* <-- Force f32 since that is the only one we can guarantee will be supported by the resampler. */ } else { /* */ if (pConfig->formatOut == ma_format_s16 || pConfig->formatOut == ma_format_f32) { return pConfig->formatOut; } else if (pConfig->formatIn == ma_format_s16 || pConfig->formatIn == ma_format_f32) { return pConfig->formatIn; } else { return ma_format_f32; } } } static ma_channel_converter_config ma_channel_converter_config_init_from_data_converter_config(const ma_data_converter_config* pConfig) { ma_channel_converter_config channelConverterConfig; MA_ASSERT(pConfig != NULL); channelConverterConfig = ma_channel_converter_config_init(ma_data_converter_config_get_mid_format(pConfig), pConfig->channelsIn, pConfig->pChannelMapIn, pConfig->channelsOut, pConfig->pChannelMapOut, pConfig->channelMixMode); channelConverterConfig.ppWeights = pConfig->ppChannelWeights; channelConverterConfig.calculateLFEFromSpatialChannels = pConfig->calculateLFEFromSpatialChannels; return channelConverterConfig; } static ma_resampler_config ma_resampler_config_init_from_data_converter_config(const ma_data_converter_config* pConfig) { ma_resampler_config resamplerConfig; ma_uint32 resamplerChannels; MA_ASSERT(pConfig != NULL); /* The resampler is the most expensive part of the conversion process, so we need to do it at the stage where the channel count is at it's lowest. */ if (pConfig->channelsIn < pConfig->channelsOut) { resamplerChannels = pConfig->channelsIn; } else { resamplerChannels = pConfig->channelsOut; } resamplerConfig = ma_resampler_config_init(ma_data_converter_config_get_mid_format(pConfig), resamplerChannels, pConfig->sampleRateIn, pConfig->sampleRateOut, pConfig->resampling.algorithm); resamplerConfig.linear = pConfig->resampling.linear; resamplerConfig.pBackendVTable = pConfig->resampling.pBackendVTable; resamplerConfig.pBackendUserData = pConfig->resampling.pBackendUserData; return resamplerConfig; } static ma_result ma_data_converter_get_heap_layout(const ma_data_converter_config* pConfig, ma_data_converter_heap_layout* pHeapLayout) { ma_result result; MA_ASSERT(pHeapLayout != NULL); MA_ZERO_OBJECT(pHeapLayout); if (pConfig == NULL) { return MA_INVALID_ARGS; } if (pConfig->channelsIn == 0 || pConfig->channelsOut == 0) { return MA_INVALID_ARGS; } pHeapLayout->sizeInBytes = 0; /* Channel converter. */ pHeapLayout->channelConverterOffset = pHeapLayout->sizeInBytes; { size_t heapSizeInBytes; ma_channel_converter_config channelConverterConfig = ma_channel_converter_config_init_from_data_converter_config(pConfig); result = ma_channel_converter_get_heap_size(&channelConverterConfig, &heapSizeInBytes); if (result != MA_SUCCESS) { return result; } pHeapLayout->sizeInBytes += heapSizeInBytes; } /* Resampler. */ pHeapLayout->resamplerOffset = pHeapLayout->sizeInBytes; if (ma_data_converter_config_is_resampler_required(pConfig)) { size_t heapSizeInBytes; ma_resampler_config resamplerConfig = ma_resampler_config_init_from_data_converter_config(pConfig); result = ma_resampler_get_heap_size(&resamplerConfig, &heapSizeInBytes); if (result != MA_SUCCESS) { return result; } pHeapLayout->sizeInBytes += heapSizeInBytes; } /* Make sure allocation size is aligned. */ pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes); return MA_SUCCESS; } MA_API ma_result ma_data_converter_get_heap_size(const ma_data_converter_config* pConfig, size_t* pHeapSizeInBytes) { ma_result result; ma_data_converter_heap_layout heapLayout; if (pHeapSizeInBytes == NULL) { return MA_INVALID_ARGS; } *pHeapSizeInBytes = 0; result = ma_data_converter_get_heap_layout(pConfig, &heapLayout); if (result != MA_SUCCESS) { return result; } *pHeapSizeInBytes = heapLayout.sizeInBytes; return MA_SUCCESS; } MA_API ma_result ma_data_converter_init_preallocated(const ma_data_converter_config* pConfig, void* pHeap, ma_data_converter* pConverter) { ma_result result; ma_data_converter_heap_layout heapLayout; ma_format midFormat; ma_bool32 isResamplingRequired; if (pConverter == NULL) { return MA_INVALID_ARGS; } MA_ZERO_OBJECT(pConverter); result = ma_data_converter_get_heap_layout(pConfig, &heapLayout); if (result != MA_SUCCESS) { return result; } pConverter->_pHeap = pHeap; MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes); pConverter->formatIn = pConfig->formatIn; pConverter->formatOut = pConfig->formatOut; pConverter->channelsIn = pConfig->channelsIn; pConverter->channelsOut = pConfig->channelsOut; pConverter->sampleRateIn = pConfig->sampleRateIn; pConverter->sampleRateOut = pConfig->sampleRateOut; pConverter->ditherMode = pConfig->ditherMode; /* Determine if resampling is required. We need to do this so we can determine an appropriate mid format to use. If resampling is required, the mid format must be ma_format_f32 since that is the only one that is guaranteed to supported by custom resampling backends. */ isResamplingRequired = ma_data_converter_config_is_resampler_required(pConfig); midFormat = ma_data_converter_config_get_mid_format(pConfig); /* Channel converter. We always initialize this, but we check if it configures itself as a passthrough to determine whether or not it's needed. */ { ma_channel_converter_config channelConverterConfig = ma_channel_converter_config_init_from_data_converter_config(pConfig); result = ma_channel_converter_init_preallocated(&channelConverterConfig, ma_offset_ptr(pHeap, heapLayout.channelConverterOffset), &pConverter->channelConverter); if (result != MA_SUCCESS) { return result; } /* If the channel converter is not a passthrough we need to enable it. Otherwise we can skip it. */ if (pConverter->channelConverter.conversionPath != ma_channel_conversion_path_passthrough) { pConverter->hasChannelConverter = MA_TRUE; } } /* Resampler. */ if (isResamplingRequired) { ma_resampler_config resamplerConfig = ma_resampler_config_init_from_data_converter_config(pConfig); result = ma_resampler_init_preallocated(&resamplerConfig, ma_offset_ptr(pHeap, heapLayout.resamplerOffset), &pConverter->resampler); if (result != MA_SUCCESS) { return result; } pConverter->hasResampler = MA_TRUE; } /* We can simplify pre- and post-format conversion if we have neither channel conversion nor resampling. */ if (pConverter->hasChannelConverter == MA_FALSE && pConverter->hasResampler == MA_FALSE) { /* We have neither channel conversion nor resampling so we'll only need one of pre- or post-format conversion, or none if the input and output formats are the same. */ if (pConverter->formatIn == pConverter->formatOut) { /* The formats are the same so we can just pass through. */ pConverter->hasPreFormatConversion = MA_FALSE; pConverter->hasPostFormatConversion = MA_FALSE; } else { /* The formats are different so we need to do either pre- or post-format conversion. It doesn't matter which. */ pConverter->hasPreFormatConversion = MA_FALSE; pConverter->hasPostFormatConversion = MA_TRUE; } } else { /* We have a channel converter and/or resampler so we'll need channel conversion based on the mid format. */ if (pConverter->formatIn != midFormat) { pConverter->hasPreFormatConversion = MA_TRUE; } if (pConverter->formatOut != midFormat) { pConverter->hasPostFormatConversion = MA_TRUE; } } /* We can enable passthrough optimizations if applicable. Note that we'll only be able to do this if the sample rate is static. */ if (pConverter->hasPreFormatConversion == MA_FALSE && pConverter->hasPostFormatConversion == MA_FALSE && pConverter->hasChannelConverter == MA_FALSE && pConverter->hasResampler == MA_FALSE) { pConverter->isPassthrough = MA_TRUE; } /* We now need to determine our execution path. */ if (pConverter->isPassthrough) { pConverter->executionPath = ma_data_converter_execution_path_passthrough; } else { if (pConverter->channelsIn < pConverter->channelsOut) { /* Do resampling first, if necessary. */ MA_ASSERT(pConverter->hasChannelConverter == MA_TRUE); if (pConverter->hasResampler) { pConverter->executionPath = ma_data_converter_execution_path_resample_first; } else { pConverter->executionPath = ma_data_converter_execution_path_channels_only; } } else { /* Do channel conversion first, if necessary. */ if (pConverter->hasChannelConverter) { if (pConverter->hasResampler) { pConverter->executionPath = ma_data_converter_execution_path_channels_first; } else { pConverter->executionPath = ma_data_converter_execution_path_channels_only; } } else { /* Channel routing not required. */ if (pConverter->hasResampler) { pConverter->executionPath = ma_data_converter_execution_path_resample_only; } else { pConverter->executionPath = ma_data_converter_execution_path_format_only; } } } } return MA_SUCCESS; } MA_API ma_result ma_data_converter_init(const ma_data_converter_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_converter* pConverter) { ma_result result; size_t heapSizeInBytes; void* pHeap; result = ma_data_converter_get_heap_size(pConfig, &heapSizeInBytes); if (result != MA_SUCCESS) { return result; } if (heapSizeInBytes > 0) { pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); if (pHeap == NULL) { return MA_OUT_OF_MEMORY; } } else { pHeap = NULL; } result = ma_data_converter_init_preallocated(pConfig, pHeap, pConverter); if (result != MA_SUCCESS) { ma_free(pHeap, pAllocationCallbacks); return result; } pConverter->_ownsHeap = MA_TRUE; return MA_SUCCESS; } MA_API void ma_data_converter_uninit(ma_data_converter* pConverter, const ma_allocation_callbacks* pAllocationCallbacks) { if (pConverter == NULL) { return; } if (pConverter->hasResampler) { ma_resampler_uninit(&pConverter->resampler, pAllocationCallbacks); } ma_channel_converter_uninit(&pConverter->channelConverter, pAllocationCallbacks); if (pConverter->_ownsHeap) { ma_free(pConverter->_pHeap, pAllocationCallbacks); } } static ma_result ma_data_converter_process_pcm_frames__passthrough(ma_data_converter* pConverter, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut) { ma_uint64 frameCountIn; ma_uint64 frameCountOut; ma_uint64 frameCount; MA_ASSERT(pConverter != NULL); frameCountIn = 0; if (pFrameCountIn != NULL) { frameCountIn = *pFrameCountIn; } frameCountOut = 0; if (pFrameCountOut != NULL) { frameCountOut = *pFrameCountOut; } frameCount = ma_min(frameCountIn, frameCountOut); if (pFramesOut != NULL) { if (pFramesIn != NULL) { ma_copy_memory_64(pFramesOut, pFramesIn, frameCount * ma_get_bytes_per_frame(pConverter->formatOut, pConverter->channelsOut)); } else { ma_zero_memory_64(pFramesOut, frameCount * ma_get_bytes_per_frame(pConverter->formatOut, pConverter->channelsOut)); } } if (pFrameCountIn != NULL) { *pFrameCountIn = frameCount; } if (pFrameCountOut != NULL) { *pFrameCountOut = frameCount; } return MA_SUCCESS; } static ma_result ma_data_converter_process_pcm_frames__format_only(ma_data_converter* pConverter, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut) { ma_uint64 frameCountIn; ma_uint64 frameCountOut; ma_uint64 frameCount; MA_ASSERT(pConverter != NULL); frameCountIn = 0; if (pFrameCountIn != NULL) { frameCountIn = *pFrameCountIn; } frameCountOut = 0; if (pFrameCountOut != NULL) { frameCountOut = *pFrameCountOut; } frameCount = ma_min(frameCountIn, frameCountOut); if (pFramesOut != NULL) { if (pFramesIn != NULL) { ma_convert_pcm_frames_format(pFramesOut, pConverter->formatOut, pFramesIn, pConverter->formatIn, frameCount, pConverter->channelsIn, pConverter->ditherMode); } else { ma_zero_memory_64(pFramesOut, frameCount * ma_get_bytes_per_frame(pConverter->formatOut, pConverter->channelsOut)); } } if (pFrameCountIn != NULL) { *pFrameCountIn = frameCount; } if (pFrameCountOut != NULL) { *pFrameCountOut = frameCount; } return MA_SUCCESS; } static ma_result ma_data_converter_process_pcm_frames__resample_with_format_conversion(ma_data_converter* pConverter, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut) { ma_result result = MA_SUCCESS; ma_uint64 frameCountIn; ma_uint64 frameCountOut; ma_uint64 framesProcessedIn; ma_uint64 framesProcessedOut; MA_ASSERT(pConverter != NULL); frameCountIn = 0; if (pFrameCountIn != NULL) { frameCountIn = *pFrameCountIn; } frameCountOut = 0; if (pFrameCountOut != NULL) { frameCountOut = *pFrameCountOut; } framesProcessedIn = 0; framesProcessedOut = 0; while (framesProcessedOut < frameCountOut) { ma_uint8 pTempBufferOut[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; const ma_uint32 tempBufferOutCap = sizeof(pTempBufferOut) / ma_get_bytes_per_frame(pConverter->resampler.format, pConverter->resampler.channels); const void* pFramesInThisIteration; /* */ void* pFramesOutThisIteration; ma_uint64 frameCountInThisIteration; ma_uint64 frameCountOutThisIteration; if (pFramesIn != NULL) { pFramesInThisIteration = ma_offset_ptr(pFramesIn, framesProcessedIn * ma_get_bytes_per_frame(pConverter->formatIn, pConverter->channelsIn)); } else { pFramesInThisIteration = NULL; } if (pFramesOut != NULL) { pFramesOutThisIteration = ma_offset_ptr(pFramesOut, framesProcessedOut * ma_get_bytes_per_frame(pConverter->formatOut, pConverter->channelsOut)); } else { pFramesOutThisIteration = NULL; } /* Do a pre format conversion if necessary. */ if (pConverter->hasPreFormatConversion) { ma_uint8 pTempBufferIn[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; const ma_uint32 tempBufferInCap = sizeof(pTempBufferIn) / ma_get_bytes_per_frame(pConverter->resampler.format, pConverter->resampler.channels); frameCountInThisIteration = (frameCountIn - framesProcessedIn); if (frameCountInThisIteration > tempBufferInCap) { frameCountInThisIteration = tempBufferInCap; } if (pConverter->hasPostFormatConversion) { if (frameCountInThisIteration > tempBufferOutCap) { frameCountInThisIteration = tempBufferOutCap; } } if (pFramesInThisIteration != NULL) { ma_convert_pcm_frames_format(pTempBufferIn, pConverter->resampler.format, pFramesInThisIteration, pConverter->formatIn, frameCountInThisIteration, pConverter->channelsIn, pConverter->ditherMode); } else { MA_ZERO_MEMORY(pTempBufferIn, sizeof(pTempBufferIn)); } frameCountOutThisIteration = (frameCountOut - framesProcessedOut); if (pConverter->hasPostFormatConversion) { /* Both input and output conversion required. Output to the temp buffer. */ if (frameCountOutThisIteration > tempBufferOutCap) { frameCountOutThisIteration = tempBufferOutCap; } result = ma_resampler_process_pcm_frames(&pConverter->resampler, pTempBufferIn, &frameCountInThisIteration, pTempBufferOut, &frameCountOutThisIteration); } else { /* Only pre-format required. Output straight to the output buffer. */ result = ma_resampler_process_pcm_frames(&pConverter->resampler, pTempBufferIn, &frameCountInThisIteration, pFramesOutThisIteration, &frameCountOutThisIteration); } if (result != MA_SUCCESS) { break; } } else { /* No pre-format required. Just read straight from the input buffer. */ MA_ASSERT(pConverter->hasPostFormatConversion == MA_TRUE); frameCountInThisIteration = (frameCountIn - framesProcessedIn); frameCountOutThisIteration = (frameCountOut - framesProcessedOut); if (frameCountOutThisIteration > tempBufferOutCap) { frameCountOutThisIteration = tempBufferOutCap; } result = ma_resampler_process_pcm_frames(&pConverter->resampler, pFramesInThisIteration, &frameCountInThisIteration, pTempBufferOut, &frameCountOutThisIteration); if (result != MA_SUCCESS) { break; } } /* If we are doing a post format conversion we need to do that now. */ if (pConverter->hasPostFormatConversion) { if (pFramesOutThisIteration != NULL) { ma_convert_pcm_frames_format(pFramesOutThisIteration, pConverter->formatOut, pTempBufferOut, pConverter->resampler.format, frameCountOutThisIteration, pConverter->resampler.channels, pConverter->ditherMode); } } framesProcessedIn += frameCountInThisIteration; framesProcessedOut += frameCountOutThisIteration; MA_ASSERT(framesProcessedIn <= frameCountIn); MA_ASSERT(framesProcessedOut <= frameCountOut); if (frameCountOutThisIteration == 0) { break; /* Consumed all of our input data. */ } } if (pFrameCountIn != NULL) { *pFrameCountIn = framesProcessedIn; } if (pFrameCountOut != NULL) { *pFrameCountOut = framesProcessedOut; } return result; } static ma_result ma_data_converter_process_pcm_frames__resample_only(ma_data_converter* pConverter, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut) { MA_ASSERT(pConverter != NULL); if (pConverter->hasPreFormatConversion == MA_FALSE && pConverter->hasPostFormatConversion == MA_FALSE) { /* Neither pre- nor post-format required. This is simple case where only resampling is required. */ return ma_resampler_process_pcm_frames(&pConverter->resampler, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut); } else { /* Format conversion required. */ return ma_data_converter_process_pcm_frames__resample_with_format_conversion(pConverter, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut); } } static ma_result ma_data_converter_process_pcm_frames__channels_only(ma_data_converter* pConverter, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut) { ma_result result; ma_uint64 frameCountIn; ma_uint64 frameCountOut; ma_uint64 frameCount; MA_ASSERT(pConverter != NULL); frameCountIn = 0; if (pFrameCountIn != NULL) { frameCountIn = *pFrameCountIn; } frameCountOut = 0; if (pFrameCountOut != NULL) { frameCountOut = *pFrameCountOut; } frameCount = ma_min(frameCountIn, frameCountOut); if (pConverter->hasPreFormatConversion == MA_FALSE && pConverter->hasPostFormatConversion == MA_FALSE) { /* No format conversion required. */ result = ma_channel_converter_process_pcm_frames(&pConverter->channelConverter, pFramesOut, pFramesIn, frameCount); if (result != MA_SUCCESS) { return result; } } else { /* Format conversion required. */ ma_uint64 framesProcessed = 0; while (framesProcessed < frameCount) { ma_uint8 pTempBufferOut[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; const ma_uint32 tempBufferOutCap = sizeof(pTempBufferOut) / ma_get_bytes_per_frame(pConverter->channelConverter.format, pConverter->channelConverter.channelsOut); const void* pFramesInThisIteration; /* */ void* pFramesOutThisIteration; ma_uint64 frameCountThisIteration; if (pFramesIn != NULL) { pFramesInThisIteration = ma_offset_ptr(pFramesIn, framesProcessed * ma_get_bytes_per_frame(pConverter->formatIn, pConverter->channelsIn)); } else { pFramesInThisIteration = NULL; } if (pFramesOut != NULL) { pFramesOutThisIteration = ma_offset_ptr(pFramesOut, framesProcessed * ma_get_bytes_per_frame(pConverter->formatOut, pConverter->channelsOut)); } else { pFramesOutThisIteration = NULL; } /* Do a pre format conversion if necessary. */ if (pConverter->hasPreFormatConversion) { ma_uint8 pTempBufferIn[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; const ma_uint32 tempBufferInCap = sizeof(pTempBufferIn) / ma_get_bytes_per_frame(pConverter->channelConverter.format, pConverter->channelConverter.channelsIn); frameCountThisIteration = (frameCount - framesProcessed); if (frameCountThisIteration > tempBufferInCap) { frameCountThisIteration = tempBufferInCap; } if (pConverter->hasPostFormatConversion) { if (frameCountThisIteration > tempBufferOutCap) { frameCountThisIteration = tempBufferOutCap; } } if (pFramesInThisIteration != NULL) { ma_convert_pcm_frames_format(pTempBufferIn, pConverter->channelConverter.format, pFramesInThisIteration, pConverter->formatIn, frameCountThisIteration, pConverter->channelsIn, pConverter->ditherMode); } else { MA_ZERO_MEMORY(pTempBufferIn, sizeof(pTempBufferIn)); } if (pConverter->hasPostFormatConversion) { /* Both input and output conversion required. Output to the temp buffer. */ result = ma_channel_converter_process_pcm_frames(&pConverter->channelConverter, pTempBufferOut, pTempBufferIn, frameCountThisIteration); } else { /* Only pre-format required. Output straight to the output buffer. */ result = ma_channel_converter_process_pcm_frames(&pConverter->channelConverter, pFramesOutThisIteration, pTempBufferIn, frameCountThisIteration); } if (result != MA_SUCCESS) { break; } } else { /* No pre-format required. Just read straight from the input buffer. */ MA_ASSERT(pConverter->hasPostFormatConversion == MA_TRUE); frameCountThisIteration = (frameCount - framesProcessed); if (frameCountThisIteration > tempBufferOutCap) { frameCountThisIteration = tempBufferOutCap; } result = ma_channel_converter_process_pcm_frames(&pConverter->channelConverter, pTempBufferOut, pFramesInThisIteration, frameCountThisIteration); if (result != MA_SUCCESS) { break; } } /* If we are doing a post format conversion we need to do that now. */ if (pConverter->hasPostFormatConversion) { if (pFramesOutThisIteration != NULL) { ma_convert_pcm_frames_format(pFramesOutThisIteration, pConverter->formatOut, pTempBufferOut, pConverter->channelConverter.format, frameCountThisIteration, pConverter->channelConverter.channelsOut, pConverter->ditherMode); } } framesProcessed += frameCountThisIteration; } } if (pFrameCountIn != NULL) { *pFrameCountIn = frameCount; } if (pFrameCountOut != NULL) { *pFrameCountOut = frameCount; } return MA_SUCCESS; } static ma_result ma_data_converter_process_pcm_frames__resample_first(ma_data_converter* pConverter, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut) { ma_result result; ma_uint64 frameCountIn; ma_uint64 frameCountOut; ma_uint64 framesProcessedIn; ma_uint64 framesProcessedOut; ma_uint8 pTempBufferIn[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; /* In resampler format. */ ma_uint64 tempBufferInCap; ma_uint8 pTempBufferMid[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; /* In resampler format, channel converter input format. */ ma_uint64 tempBufferMidCap; ma_uint8 pTempBufferOut[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; /* In channel converter output format. */ ma_uint64 tempBufferOutCap; MA_ASSERT(pConverter != NULL); MA_ASSERT(pConverter->resampler.format == pConverter->channelConverter.format); MA_ASSERT(pConverter->resampler.channels == pConverter->channelConverter.channelsIn); MA_ASSERT(pConverter->resampler.channels < pConverter->channelConverter.channelsOut); frameCountIn = 0; if (pFrameCountIn != NULL) { frameCountIn = *pFrameCountIn; } frameCountOut = 0; if (pFrameCountOut != NULL) { frameCountOut = *pFrameCountOut; } framesProcessedIn = 0; framesProcessedOut = 0; tempBufferInCap = sizeof(pTempBufferIn) / ma_get_bytes_per_frame(pConverter->resampler.format, pConverter->resampler.channels); tempBufferMidCap = sizeof(pTempBufferIn) / ma_get_bytes_per_frame(pConverter->resampler.format, pConverter->resampler.channels); tempBufferOutCap = sizeof(pTempBufferOut) / ma_get_bytes_per_frame(pConverter->channelConverter.format, pConverter->channelConverter.channelsOut); while (framesProcessedOut < frameCountOut) { ma_uint64 frameCountInThisIteration; ma_uint64 frameCountOutThisIteration; const void* pRunningFramesIn = NULL; void* pRunningFramesOut = NULL; const void* pResampleBufferIn; void* pChannelsBufferOut; if (pFramesIn != NULL) { pRunningFramesIn = ma_offset_ptr(pFramesIn, framesProcessedIn * ma_get_bytes_per_frame(pConverter->formatIn, pConverter->channelsIn)); } if (pFramesOut != NULL) { pRunningFramesOut = ma_offset_ptr(pFramesOut, framesProcessedOut * ma_get_bytes_per_frame(pConverter->formatOut, pConverter->channelsOut)); } /* Run input data through the resampler and output it to the temporary buffer. */ frameCountInThisIteration = (frameCountIn - framesProcessedIn); if (pConverter->hasPreFormatConversion) { if (frameCountInThisIteration > tempBufferInCap) { frameCountInThisIteration = tempBufferInCap; } } frameCountOutThisIteration = (frameCountOut - framesProcessedOut); if (frameCountOutThisIteration > tempBufferMidCap) { frameCountOutThisIteration = tempBufferMidCap; } /* We can't read more frames than can fit in the output buffer. */ if (pConverter->hasPostFormatConversion) { if (frameCountOutThisIteration > tempBufferOutCap) { frameCountOutThisIteration = tempBufferOutCap; } } /* We need to ensure we don't try to process too many input frames that we run out of room in the output buffer. If this happens we'll end up glitching. */ /* We need to try to predict how many input frames will be required for the resampler. If the resampler can tell us, we'll use that. Otherwise we'll need to make a best guess. The further off we are from this, the more wasted format conversions we'll end up doing. */ #if 1 { ma_uint64 requiredInputFrameCount; result = ma_resampler_get_required_input_frame_count(&pConverter->resampler, frameCountOutThisIteration, &requiredInputFrameCount); if (result != MA_SUCCESS) { /* Fall back to a best guess. */ requiredInputFrameCount = (frameCountOutThisIteration * pConverter->resampler.sampleRateIn) / pConverter->resampler.sampleRateOut; } if (frameCountInThisIteration > requiredInputFrameCount) { frameCountInThisIteration = requiredInputFrameCount; } } #endif if (pConverter->hasPreFormatConversion) { if (pFramesIn != NULL) { ma_convert_pcm_frames_format(pTempBufferIn, pConverter->resampler.format, pRunningFramesIn, pConverter->formatIn, frameCountInThisIteration, pConverter->channelsIn, pConverter->ditherMode); pResampleBufferIn = pTempBufferIn; } else { pResampleBufferIn = NULL; } } else { pResampleBufferIn = pRunningFramesIn; } result = ma_resampler_process_pcm_frames(&pConverter->resampler, pResampleBufferIn, &frameCountInThisIteration, pTempBufferMid, &frameCountOutThisIteration); if (result != MA_SUCCESS) { return result; } /* The input data has been resampled so now we need to run it through the channel converter. The input data is always contained in pTempBufferMid. We only need to do this part if we have an output buffer. */ if (pFramesOut != NULL) { if (pConverter->hasPostFormatConversion) { pChannelsBufferOut = pTempBufferOut; } else { pChannelsBufferOut = pRunningFramesOut; } result = ma_channel_converter_process_pcm_frames(&pConverter->channelConverter, pChannelsBufferOut, pTempBufferMid, frameCountOutThisIteration); if (result != MA_SUCCESS) { return result; } /* Finally we do post format conversion. */ if (pConverter->hasPostFormatConversion) { ma_convert_pcm_frames_format(pRunningFramesOut, pConverter->formatOut, pChannelsBufferOut, pConverter->channelConverter.format, frameCountOutThisIteration, pConverter->channelConverter.channelsOut, pConverter->ditherMode); } } framesProcessedIn += frameCountInThisIteration; framesProcessedOut += frameCountOutThisIteration; MA_ASSERT(framesProcessedIn <= frameCountIn); MA_ASSERT(framesProcessedOut <= frameCountOut); if (frameCountOutThisIteration == 0) { break; /* Consumed all of our input data. */ } } if (pFrameCountIn != NULL) { *pFrameCountIn = framesProcessedIn; } if (pFrameCountOut != NULL) { *pFrameCountOut = framesProcessedOut; } return MA_SUCCESS; } static ma_result ma_data_converter_process_pcm_frames__channels_first(ma_data_converter* pConverter, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut) { ma_result result; ma_uint64 frameCountIn; ma_uint64 frameCountOut; ma_uint64 framesProcessedIn; ma_uint64 framesProcessedOut; ma_uint8 pTempBufferIn[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; /* In resampler format. */ ma_uint64 tempBufferInCap; ma_uint8 pTempBufferMid[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; /* In resampler format, channel converter input format. */ ma_uint64 tempBufferMidCap; ma_uint8 pTempBufferOut[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; /* In channel converter output format. */ ma_uint64 tempBufferOutCap; MA_ASSERT(pConverter != NULL); MA_ASSERT(pConverter->resampler.format == pConverter->channelConverter.format); MA_ASSERT(pConverter->resampler.channels == pConverter->channelConverter.channelsOut); MA_ASSERT(pConverter->resampler.channels <= pConverter->channelConverter.channelsIn); frameCountIn = 0; if (pFrameCountIn != NULL) { frameCountIn = *pFrameCountIn; } frameCountOut = 0; if (pFrameCountOut != NULL) { frameCountOut = *pFrameCountOut; } framesProcessedIn = 0; framesProcessedOut = 0; tempBufferInCap = sizeof(pTempBufferIn) / ma_get_bytes_per_frame(pConverter->channelConverter.format, pConverter->channelConverter.channelsIn); tempBufferMidCap = sizeof(pTempBufferIn) / ma_get_bytes_per_frame(pConverter->channelConverter.format, pConverter->channelConverter.channelsOut); tempBufferOutCap = sizeof(pTempBufferOut) / ma_get_bytes_per_frame(pConverter->resampler.format, pConverter->resampler.channels); while (framesProcessedOut < frameCountOut) { ma_uint64 frameCountInThisIteration; ma_uint64 frameCountOutThisIteration; const void* pRunningFramesIn = NULL; void* pRunningFramesOut = NULL; const void* pChannelsBufferIn; void* pResampleBufferOut; if (pFramesIn != NULL) { pRunningFramesIn = ma_offset_ptr(pFramesIn, framesProcessedIn * ma_get_bytes_per_frame(pConverter->formatIn, pConverter->channelsIn)); } if (pFramesOut != NULL) { pRunningFramesOut = ma_offset_ptr(pFramesOut, framesProcessedOut * ma_get_bytes_per_frame(pConverter->formatOut, pConverter->channelsOut)); } /* Before doing any processing we need to determine how many frames we should try processing this iteration, for both input and output. The resampler requires us to perform format and channel conversion before passing any data into it. If we get our input count wrong, we'll end up peforming redundant pre-processing. This isn't the end of the world, but it does result in some inefficiencies proportionate to how far our estimates are off. If the resampler has a means to calculate exactly how much we'll need, we'll use that. Otherwise we'll make a best guess. In order to do this, we'll need to calculate the output frame count first. */ frameCountOutThisIteration = (frameCountOut - framesProcessedOut); if (frameCountOutThisIteration > tempBufferMidCap) { frameCountOutThisIteration = tempBufferMidCap; } if (pConverter->hasPostFormatConversion) { if (frameCountOutThisIteration > tempBufferOutCap) { frameCountOutThisIteration = tempBufferOutCap; } } /* Now that we have the output frame count we can determine the input frame count. */ frameCountInThisIteration = (frameCountIn - framesProcessedIn); if (pConverter->hasPreFormatConversion) { if (frameCountInThisIteration > tempBufferInCap) { frameCountInThisIteration = tempBufferInCap; } } if (frameCountInThisIteration > tempBufferMidCap) { frameCountInThisIteration = tempBufferMidCap; } #if 1 { ma_uint64 requiredInputFrameCount; result = ma_resampler_get_required_input_frame_count(&pConverter->resampler, frameCountOutThisIteration, &requiredInputFrameCount); if (result != MA_SUCCESS) { /* Fall back to a best guess. */ requiredInputFrameCount = (frameCountOutThisIteration * pConverter->resampler.sampleRateIn) / pConverter->resampler.sampleRateOut; } if (frameCountInThisIteration > requiredInputFrameCount) { frameCountInThisIteration = requiredInputFrameCount; } } #endif /* Pre format conversion. */ if (pConverter->hasPreFormatConversion) { if (pRunningFramesIn != NULL) { ma_convert_pcm_frames_format(pTempBufferIn, pConverter->channelConverter.format, pRunningFramesIn, pConverter->formatIn, frameCountInThisIteration, pConverter->channelsIn, pConverter->ditherMode); pChannelsBufferIn = pTempBufferIn; } else { pChannelsBufferIn = NULL; } } else { pChannelsBufferIn = pRunningFramesIn; } /* Channel conversion. */ result = ma_channel_converter_process_pcm_frames(&pConverter->channelConverter, pTempBufferMid, pChannelsBufferIn, frameCountInThisIteration); if (result != MA_SUCCESS) { return result; } /* Resampling. */ if (pConverter->hasPostFormatConversion) { pResampleBufferOut = pTempBufferOut; } else { pResampleBufferOut = pRunningFramesOut; } result = ma_resampler_process_pcm_frames(&pConverter->resampler, pTempBufferMid, &frameCountInThisIteration, pResampleBufferOut, &frameCountOutThisIteration); if (result != MA_SUCCESS) { return result; } /* Post format conversion. */ if (pConverter->hasPostFormatConversion) { if (pRunningFramesOut != NULL) { ma_convert_pcm_frames_format(pRunningFramesOut, pConverter->formatOut, pResampleBufferOut, pConverter->resampler.format, frameCountOutThisIteration, pConverter->channelsOut, pConverter->ditherMode); } } framesProcessedIn += frameCountInThisIteration; framesProcessedOut += frameCountOutThisIteration; MA_ASSERT(framesProcessedIn <= frameCountIn); MA_ASSERT(framesProcessedOut <= frameCountOut); if (frameCountOutThisIteration == 0) { break; /* Consumed all of our input data. */ } } if (pFrameCountIn != NULL) { *pFrameCountIn = framesProcessedIn; } if (pFrameCountOut != NULL) { *pFrameCountOut = framesProcessedOut; } return MA_SUCCESS; } MA_API ma_result ma_data_converter_process_pcm_frames(ma_data_converter* pConverter, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut) { if (pConverter == NULL) { return MA_INVALID_ARGS; } switch (pConverter->executionPath) { case ma_data_converter_execution_path_passthrough: return ma_data_converter_process_pcm_frames__passthrough(pConverter, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut); case ma_data_converter_execution_path_format_only: return ma_data_converter_process_pcm_frames__format_only(pConverter, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut); case ma_data_converter_execution_path_channels_only: return ma_data_converter_process_pcm_frames__channels_only(pConverter, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut); case ma_data_converter_execution_path_resample_only: return ma_data_converter_process_pcm_frames__resample_only(pConverter, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut); case ma_data_converter_execution_path_resample_first: return ma_data_converter_process_pcm_frames__resample_first(pConverter, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut); case ma_data_converter_execution_path_channels_first: return ma_data_converter_process_pcm_frames__channels_first(pConverter, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut); default: return MA_INVALID_OPERATION; /* Should never hit this. */ } } MA_API ma_result ma_data_converter_set_rate(ma_data_converter* pConverter, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut) { if (pConverter == NULL) { return MA_INVALID_ARGS; } if (pConverter->hasResampler == MA_FALSE) { return MA_INVALID_OPERATION; /* Dynamic resampling not enabled. */ } return ma_resampler_set_rate(&pConverter->resampler, sampleRateIn, sampleRateOut); } MA_API ma_result ma_data_converter_set_rate_ratio(ma_data_converter* pConverter, float ratioInOut) { if (pConverter == NULL) { return MA_INVALID_ARGS; } if (pConverter->hasResampler == MA_FALSE) { return MA_INVALID_OPERATION; /* Dynamic resampling not enabled. */ } return ma_resampler_set_rate_ratio(&pConverter->resampler, ratioInOut); } MA_API ma_uint64 ma_data_converter_get_input_latency(const ma_data_converter* pConverter) { if (pConverter == NULL) { return 0; } if (pConverter->hasResampler) { return ma_resampler_get_input_latency(&pConverter->resampler); } return 0; /* No latency without a resampler. */ } MA_API ma_uint64 ma_data_converter_get_output_latency(const ma_data_converter* pConverter) { if (pConverter == NULL) { return 0; } if (pConverter->hasResampler) { return ma_resampler_get_output_latency(&pConverter->resampler); } return 0; /* No latency without a resampler. */ } MA_API ma_result ma_data_converter_get_required_input_frame_count(const ma_data_converter* pConverter, ma_uint64 outputFrameCount, ma_uint64* pInputFrameCount) { if (pInputFrameCount == NULL) { return MA_INVALID_ARGS; } *pInputFrameCount = 0; if (pConverter == NULL) { return MA_INVALID_ARGS; } if (pConverter->hasResampler) { return ma_resampler_get_required_input_frame_count(&pConverter->resampler, outputFrameCount, pInputFrameCount); } else { *pInputFrameCount = outputFrameCount; /* 1:1 */ return MA_SUCCESS; } } MA_API ma_result ma_data_converter_get_expected_output_frame_count(const ma_data_converter* pConverter, ma_uint64 inputFrameCount, ma_uint64* pOutputFrameCount) { if (pOutputFrameCount == NULL) { return MA_INVALID_ARGS; } *pOutputFrameCount = 0; if (pConverter == NULL) { return MA_INVALID_ARGS; } if (pConverter->hasResampler) { return ma_resampler_get_expected_output_frame_count(&pConverter->resampler, inputFrameCount, pOutputFrameCount); } else { *pOutputFrameCount = inputFrameCount; /* 1:1 */ return MA_SUCCESS; } } MA_API ma_result ma_data_converter_get_input_channel_map(const ma_data_converter* pConverter, ma_channel* pChannelMap, size_t channelMapCap) { if (pConverter == NULL || pChannelMap == NULL) { return MA_INVALID_ARGS; } if (pConverter->hasChannelConverter) { ma_channel_converter_get_output_channel_map(&pConverter->channelConverter, pChannelMap, channelMapCap); } else { ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, pConverter->channelsOut); } return MA_SUCCESS; } MA_API ma_result ma_data_converter_get_output_channel_map(const ma_data_converter* pConverter, ma_channel* pChannelMap, size_t channelMapCap) { if (pConverter == NULL || pChannelMap == NULL) { return MA_INVALID_ARGS; } if (pConverter->hasChannelConverter) { ma_channel_converter_get_input_channel_map(&pConverter->channelConverter, pChannelMap, channelMapCap); } else { ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, pConverter->channelsIn); } return MA_SUCCESS; } MA_API ma_result ma_data_converter_reset(ma_data_converter* pConverter) { if (pConverter == NULL) { return MA_INVALID_ARGS; } /* There's nothing to do if we're not resampling. */ if (pConverter->hasResampler == MA_FALSE) { return MA_SUCCESS; } return ma_resampler_reset(&pConverter->resampler); } /************************************************************************************************************************************************************** Channel Maps **************************************************************************************************************************************************************/ static ma_channel ma_channel_map_init_standard_channel(ma_standard_channel_map standardChannelMap, ma_uint32 channelCount, ma_uint32 channelIndex); MA_API ma_channel ma_channel_map_get_channel(const ma_channel* pChannelMap, ma_uint32 channelCount, ma_uint32 channelIndex) { if (pChannelMap == NULL) { return ma_channel_map_init_standard_channel(ma_standard_channel_map_default, channelCount, channelIndex); } else { if (channelIndex >= channelCount) { return MA_CHANNEL_NONE; } return pChannelMap[channelIndex]; } } MA_API void ma_channel_map_init_blank(ma_channel* pChannelMap, ma_uint32 channels) { if (pChannelMap == NULL) { return; } MA_ZERO_MEMORY(pChannelMap, sizeof(*pChannelMap) * channels); } static ma_channel ma_channel_map_init_standard_channel_microsoft(ma_uint32 channelCount, ma_uint32 channelIndex) { if (channelCount == 0 || channelIndex >= channelCount) { return MA_CHANNEL_NONE; } /* This is the Microsoft channel map. Based off the speaker configurations mentioned here: https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/content/ksmedia/ns-ksmedia-ksaudio_channel_config */ switch (channelCount) { case 0: return MA_CHANNEL_NONE; case 1: { return MA_CHANNEL_MONO; } break; case 2: { switch (channelIndex) { case 0: return MA_CHANNEL_FRONT_LEFT; case 1: return MA_CHANNEL_FRONT_RIGHT; } } break; case 3: /* No defined, but best guess. */ { switch (channelIndex) { case 0: return MA_CHANNEL_FRONT_LEFT; case 1: return MA_CHANNEL_FRONT_RIGHT; case 2: return MA_CHANNEL_FRONT_CENTER; } } break; case 4: { switch (channelIndex) { #ifndef MA_USE_QUAD_MICROSOFT_CHANNEL_MAP /* Surround. Using the Surround profile has the advantage of the 3rd channel (MA_CHANNEL_FRONT_CENTER) mapping nicely with higher channel counts. */ case 0: return MA_CHANNEL_FRONT_LEFT; case 1: return MA_CHANNEL_FRONT_RIGHT; case 2: return MA_CHANNEL_FRONT_CENTER; case 3: return MA_CHANNEL_BACK_CENTER; #else /* Quad. */ case 0: return MA_CHANNEL_FRONT_LEFT; case 1: return MA_CHANNEL_FRONT_RIGHT; case 2: return MA_CHANNEL_BACK_LEFT; case 3: return MA_CHANNEL_BACK_RIGHT; #endif } } break; case 5: /* Not defined, but best guess. */ { switch (channelIndex) { case 0: return MA_CHANNEL_FRONT_LEFT; case 1: return MA_CHANNEL_FRONT_RIGHT; case 2: return MA_CHANNEL_FRONT_CENTER; case 3: return MA_CHANNEL_BACK_LEFT; case 4: return MA_CHANNEL_BACK_RIGHT; } } break; case 6: { switch (channelIndex) { case 0: return MA_CHANNEL_FRONT_LEFT; case 1: return MA_CHANNEL_FRONT_RIGHT; case 2: return MA_CHANNEL_FRONT_CENTER; case 3: return MA_CHANNEL_LFE; case 4: return MA_CHANNEL_SIDE_LEFT; case 5: return MA_CHANNEL_SIDE_RIGHT; } } break; case 7: /* Not defined, but best guess. */ { switch (channelIndex) { case 0: return MA_CHANNEL_FRONT_LEFT; case 1: return MA_CHANNEL_FRONT_RIGHT; case 2: return MA_CHANNEL_FRONT_CENTER; case 3: return MA_CHANNEL_LFE; case 4: return MA_CHANNEL_BACK_CENTER; case 5: return MA_CHANNEL_SIDE_LEFT; case 6: return MA_CHANNEL_SIDE_RIGHT; } } break; case 8: default: { switch (channelIndex) { case 0: return MA_CHANNEL_FRONT_LEFT; case 1: return MA_CHANNEL_FRONT_RIGHT; case 2: return MA_CHANNEL_FRONT_CENTER; case 3: return MA_CHANNEL_LFE; case 4: return MA_CHANNEL_BACK_LEFT; case 5: return MA_CHANNEL_BACK_RIGHT; case 6: return MA_CHANNEL_SIDE_LEFT; case 7: return MA_CHANNEL_SIDE_RIGHT; } } break; } if (channelCount > 8) { if (channelIndex < 32) { /* We have 32 AUX channels. */ return (ma_channel)(MA_CHANNEL_AUX_0 + (channelIndex - 8)); } } /* Getting here means we don't know how to map the channel position so just return MA_CHANNEL_NONE. */ return MA_CHANNEL_NONE; } static ma_channel ma_channel_map_init_standard_channel_alsa(ma_uint32 channelCount, ma_uint32 channelIndex) { switch (channelCount) { case 0: return MA_CHANNEL_NONE; case 1: { return MA_CHANNEL_MONO; } break; case 2: { switch (channelIndex) { case 0: return MA_CHANNEL_FRONT_LEFT; case 1: return MA_CHANNEL_FRONT_RIGHT; } } break; case 3: { switch (channelIndex) { case 0: return MA_CHANNEL_FRONT_LEFT; case 1: return MA_CHANNEL_FRONT_RIGHT; case 2: return MA_CHANNEL_FRONT_CENTER; } } break; case 4: { switch (channelIndex) { case 0: return MA_CHANNEL_FRONT_LEFT; case 1: return MA_CHANNEL_FRONT_RIGHT; case 2: return MA_CHANNEL_BACK_LEFT; case 3: return MA_CHANNEL_BACK_RIGHT; } } break; case 5: { switch (channelIndex) { case 0: return MA_CHANNEL_FRONT_LEFT; case 1: return MA_CHANNEL_FRONT_RIGHT; case 2: return MA_CHANNEL_BACK_LEFT; case 3: return MA_CHANNEL_BACK_RIGHT; case 4: return MA_CHANNEL_FRONT_CENTER; } } break; case 6: { switch (channelIndex) { case 0: return MA_CHANNEL_FRONT_LEFT; case 1: return MA_CHANNEL_FRONT_RIGHT; case 2: return MA_CHANNEL_BACK_LEFT; case 3: return MA_CHANNEL_BACK_RIGHT; case 4: return MA_CHANNEL_FRONT_CENTER; case 5: return MA_CHANNEL_LFE; } } break; case 7: { switch (channelIndex) { case 0: return MA_CHANNEL_FRONT_LEFT; case 1: return MA_CHANNEL_FRONT_RIGHT; case 2: return MA_CHANNEL_BACK_LEFT; case 3: return MA_CHANNEL_BACK_RIGHT; case 4: return MA_CHANNEL_FRONT_CENTER; case 5: return MA_CHANNEL_LFE; case 6: return MA_CHANNEL_BACK_CENTER; } } break; case 8: default: { switch (channelIndex) { case 0: return MA_CHANNEL_FRONT_LEFT; case 1: return MA_CHANNEL_FRONT_RIGHT; case 2: return MA_CHANNEL_BACK_LEFT; case 3: return MA_CHANNEL_BACK_RIGHT; case 4: return MA_CHANNEL_FRONT_CENTER; case 5: return MA_CHANNEL_LFE; case 6: return MA_CHANNEL_SIDE_LEFT; case 7: return MA_CHANNEL_SIDE_RIGHT; } } break; } if (channelCount > 8) { if (channelIndex < 32) { /* We have 32 AUX channels. */ return (ma_channel)(MA_CHANNEL_AUX_0 + (channelIndex - 8)); } } /* Getting here means we don't know how to map the channel position so just return MA_CHANNEL_NONE. */ return MA_CHANNEL_NONE; } static ma_channel ma_channel_map_init_standard_channel_rfc3551(ma_uint32 channelCount, ma_uint32 channelIndex) { switch (channelCount) { case 0: return MA_CHANNEL_NONE; case 1: { return MA_CHANNEL_MONO; } break; case 2: { switch (channelIndex) { case 0: return MA_CHANNEL_FRONT_LEFT; case 1: return MA_CHANNEL_FRONT_RIGHT; } } break; case 3: { switch (channelIndex) { case 0: return MA_CHANNEL_FRONT_LEFT; case 1: return MA_CHANNEL_FRONT_RIGHT; case 2: return MA_CHANNEL_FRONT_CENTER; } } break; case 4: { switch (channelIndex) { case 0: return MA_CHANNEL_FRONT_LEFT; case 2: return MA_CHANNEL_FRONT_CENTER; case 1: return MA_CHANNEL_FRONT_RIGHT; case 3: return MA_CHANNEL_BACK_CENTER; } } break; case 5: { switch (channelIndex) { case 0: return MA_CHANNEL_FRONT_LEFT; case 1: return MA_CHANNEL_FRONT_RIGHT; case 2: return MA_CHANNEL_FRONT_CENTER; case 3: return MA_CHANNEL_BACK_LEFT; case 4: return MA_CHANNEL_BACK_RIGHT; } } break; case 6: default: { switch (channelIndex) { case 0: return MA_CHANNEL_FRONT_LEFT; case 1: return MA_CHANNEL_SIDE_LEFT; case 2: return MA_CHANNEL_FRONT_CENTER; case 3: return MA_CHANNEL_FRONT_RIGHT; case 4: return MA_CHANNEL_SIDE_RIGHT; case 5: return MA_CHANNEL_BACK_CENTER; } } break; } if (channelCount > 6) { if (channelIndex < 32) { /* We have 32 AUX channels. */ return (ma_channel)(MA_CHANNEL_AUX_0 + (channelIndex - 6)); } } /* Getting here means we don't know how to map the channel position so just return MA_CHANNEL_NONE. */ return MA_CHANNEL_NONE; } static ma_channel ma_channel_map_init_standard_channel_flac(ma_uint32 channelCount, ma_uint32 channelIndex) { switch (channelCount) { case 0: return MA_CHANNEL_NONE; case 1: { return MA_CHANNEL_MONO; } break; case 2: { switch (channelIndex) { case 0: return MA_CHANNEL_FRONT_LEFT; case 1: return MA_CHANNEL_FRONT_RIGHT; } } break; case 3: { switch (channelIndex) { case 0: return MA_CHANNEL_FRONT_LEFT; case 1: return MA_CHANNEL_FRONT_RIGHT; case 2: return MA_CHANNEL_FRONT_CENTER; } } break; case 4: { switch (channelIndex) { case 0: return MA_CHANNEL_FRONT_LEFT; case 1: return MA_CHANNEL_FRONT_RIGHT; case 2: return MA_CHANNEL_BACK_LEFT; case 3: return MA_CHANNEL_BACK_RIGHT; } } break; case 5: { switch (channelIndex) { case 0: return MA_CHANNEL_FRONT_LEFT; case 1: return MA_CHANNEL_FRONT_RIGHT; case 2: return MA_CHANNEL_FRONT_CENTER; case 3: return MA_CHANNEL_BACK_LEFT; case 4: return MA_CHANNEL_BACK_RIGHT; } } break; case 6: { switch (channelIndex) { case 0: return MA_CHANNEL_FRONT_LEFT; case 1: return MA_CHANNEL_FRONT_RIGHT; case 2: return MA_CHANNEL_FRONT_CENTER; case 3: return MA_CHANNEL_LFE; case 4: return MA_CHANNEL_BACK_LEFT; case 5: return MA_CHANNEL_BACK_RIGHT; } } break; case 7: { switch (channelIndex) { case 0: return MA_CHANNEL_FRONT_LEFT; case 1: return MA_CHANNEL_FRONT_RIGHT; case 2: return MA_CHANNEL_FRONT_CENTER; case 3: return MA_CHANNEL_LFE; case 4: return MA_CHANNEL_BACK_CENTER; case 5: return MA_CHANNEL_SIDE_LEFT; case 6: return MA_CHANNEL_SIDE_RIGHT; } } break; case 8: default: { switch (channelIndex) { case 0: return MA_CHANNEL_FRONT_LEFT; case 1: return MA_CHANNEL_FRONT_RIGHT; case 2: return MA_CHANNEL_FRONT_CENTER; case 3: return MA_CHANNEL_LFE; case 4: return MA_CHANNEL_BACK_LEFT; case 5: return MA_CHANNEL_BACK_RIGHT; case 6: return MA_CHANNEL_SIDE_LEFT; case 7: return MA_CHANNEL_SIDE_RIGHT; } } break; } if (channelCount > 8) { if (channelIndex < 32) { /* We have 32 AUX channels. */ return (ma_channel)(MA_CHANNEL_AUX_0 + (channelIndex - 8)); } } /* Getting here means we don't know how to map the channel position so just return MA_CHANNEL_NONE. */ return MA_CHANNEL_NONE; } static ma_channel ma_channel_map_init_standard_channel_vorbis(ma_uint32 channelCount, ma_uint32 channelIndex) { switch (channelCount) { case 0: return MA_CHANNEL_NONE; case 1: { return MA_CHANNEL_MONO; } break; case 2: { switch (channelIndex) { case 0: return MA_CHANNEL_FRONT_LEFT; case 1: return MA_CHANNEL_FRONT_RIGHT; } } break; case 3: { switch (channelIndex) { case 0: return MA_CHANNEL_FRONT_LEFT; case 1: return MA_CHANNEL_FRONT_CENTER; case 2: return MA_CHANNEL_FRONT_RIGHT; } } break; case 4: { switch (channelIndex) { case 0: return MA_CHANNEL_FRONT_LEFT; case 1: return MA_CHANNEL_FRONT_RIGHT; case 2: return MA_CHANNEL_BACK_LEFT; case 3: return MA_CHANNEL_BACK_RIGHT; } } break; case 5: { switch (channelIndex) { case 0: return MA_CHANNEL_FRONT_LEFT; case 1: return MA_CHANNEL_FRONT_CENTER; case 2: return MA_CHANNEL_FRONT_RIGHT; case 3: return MA_CHANNEL_BACK_LEFT; case 4: return MA_CHANNEL_BACK_RIGHT; } } break; case 6: { switch (channelIndex) { case 0: return MA_CHANNEL_FRONT_LEFT; case 1: return MA_CHANNEL_FRONT_CENTER; case 2: return MA_CHANNEL_FRONT_RIGHT; case 3: return MA_CHANNEL_BACK_LEFT; case 4: return MA_CHANNEL_BACK_RIGHT; case 5: return MA_CHANNEL_LFE; } } break; case 7: { switch (channelIndex) { case 0: return MA_CHANNEL_FRONT_LEFT; case 1: return MA_CHANNEL_FRONT_CENTER; case 2: return MA_CHANNEL_FRONT_RIGHT; case 3: return MA_CHANNEL_SIDE_LEFT; case 4: return MA_CHANNEL_SIDE_RIGHT; case 5: return MA_CHANNEL_BACK_CENTER; case 6: return MA_CHANNEL_LFE; } } break; case 8: default: { switch (channelIndex) { case 0: return MA_CHANNEL_FRONT_LEFT; case 1: return MA_CHANNEL_FRONT_CENTER; case 2: return MA_CHANNEL_FRONT_RIGHT; case 3: return MA_CHANNEL_SIDE_LEFT; case 4: return MA_CHANNEL_SIDE_RIGHT; case 5: return MA_CHANNEL_BACK_LEFT; case 6: return MA_CHANNEL_BACK_RIGHT; case 7: return MA_CHANNEL_LFE; } } break; } if (channelCount > 8) { if (channelIndex < 32) { /* We have 32 AUX channels. */ return (ma_channel)(MA_CHANNEL_AUX_0 + (channelIndex - 8)); } } /* Getting here means we don't know how to map the channel position so just return MA_CHANNEL_NONE. */ return MA_CHANNEL_NONE; } static ma_channel ma_channel_map_init_standard_channel_sound4(ma_uint32 channelCount, ma_uint32 channelIndex) { switch (channelCount) { case 0: return MA_CHANNEL_NONE; case 1: { return MA_CHANNEL_MONO; } break; case 2: { switch (channelIndex) { case 0: return MA_CHANNEL_FRONT_LEFT; case 1: return MA_CHANNEL_FRONT_RIGHT; } } break; case 3: { switch (channelIndex) { case 0: return MA_CHANNEL_FRONT_LEFT; case 1: return MA_CHANNEL_FRONT_RIGHT; case 2: return MA_CHANNEL_FRONT_CENTER; } } break; case 4: { switch (channelIndex) { case 0: return MA_CHANNEL_FRONT_LEFT; case 1: return MA_CHANNEL_FRONT_RIGHT; case 2: return MA_CHANNEL_BACK_LEFT; case 3: return MA_CHANNEL_BACK_RIGHT; } } break; case 5: { switch (channelIndex) { case 0: return MA_CHANNEL_FRONT_LEFT; case 1: return MA_CHANNEL_FRONT_RIGHT; case 2: return MA_CHANNEL_FRONT_CENTER; case 3: return MA_CHANNEL_BACK_LEFT; case 4: return MA_CHANNEL_BACK_RIGHT; } } break; case 6: { switch (channelIndex) { case 0: return MA_CHANNEL_FRONT_LEFT; case 1: return MA_CHANNEL_FRONT_CENTER; case 2: return MA_CHANNEL_FRONT_RIGHT; case 3: return MA_CHANNEL_BACK_LEFT; case 4: return MA_CHANNEL_BACK_RIGHT; case 5: return MA_CHANNEL_LFE; } } break; case 7: { switch (channelIndex) { case 0: return MA_CHANNEL_FRONT_LEFT; case 1: return MA_CHANNEL_FRONT_CENTER; case 2: return MA_CHANNEL_FRONT_RIGHT; case 3: return MA_CHANNEL_SIDE_LEFT; case 4: return MA_CHANNEL_SIDE_RIGHT; case 5: return MA_CHANNEL_BACK_CENTER; case 6: return MA_CHANNEL_LFE; } } break; case 8: default: { switch (channelIndex) { case 0: return MA_CHANNEL_FRONT_LEFT; case 1: return MA_CHANNEL_FRONT_CENTER; case 2: return MA_CHANNEL_FRONT_RIGHT; case 3: return MA_CHANNEL_SIDE_LEFT; case 4: return MA_CHANNEL_SIDE_RIGHT; case 5: return MA_CHANNEL_BACK_LEFT; case 6: return MA_CHANNEL_BACK_RIGHT; case 7: return MA_CHANNEL_LFE; } } break; } if (channelCount > 8) { if (channelIndex < 32) { /* We have 32 AUX channels. */ return (ma_channel)(MA_CHANNEL_AUX_0 + (channelIndex - 8)); } } /* Getting here means we don't know how to map the channel position so just return MA_CHANNEL_NONE. */ return MA_CHANNEL_NONE; } static ma_channel ma_channel_map_init_standard_channel_sndio(ma_uint32 channelCount, ma_uint32 channelIndex) { switch (channelCount) { case 0: return MA_CHANNEL_NONE; case 1: { return MA_CHANNEL_MONO; } break; case 2: { switch (channelIndex) { case 0: return MA_CHANNEL_FRONT_LEFT; case 1: return MA_CHANNEL_FRONT_RIGHT; } } break; case 3: /* No defined, but best guess. */ { switch (channelIndex) { case 0: return MA_CHANNEL_FRONT_LEFT; case 1: return MA_CHANNEL_FRONT_RIGHT; case 2: return MA_CHANNEL_FRONT_CENTER; } } break; case 4: { switch (channelIndex) { case 0: return MA_CHANNEL_FRONT_LEFT; case 1: return MA_CHANNEL_FRONT_RIGHT; case 2: return MA_CHANNEL_BACK_LEFT; case 3: return MA_CHANNEL_BACK_RIGHT; } } break; case 5: /* Not defined, but best guess. */ { switch (channelIndex) { case 0: return MA_CHANNEL_FRONT_LEFT; case 1: return MA_CHANNEL_FRONT_RIGHT; case 2: return MA_CHANNEL_BACK_LEFT; case 3: return MA_CHANNEL_BACK_RIGHT; case 4: return MA_CHANNEL_FRONT_CENTER; } } break; case 6: default: { switch (channelIndex) { case 0: return MA_CHANNEL_FRONT_LEFT; case 1: return MA_CHANNEL_FRONT_RIGHT; case 2: return MA_CHANNEL_BACK_LEFT; case 3: return MA_CHANNEL_BACK_RIGHT; case 4: return MA_CHANNEL_FRONT_CENTER; case 5: return MA_CHANNEL_LFE; } } break; } if (channelCount > 6) { if (channelIndex < 32) { /* We have 32 AUX channels. */ return (ma_channel)(MA_CHANNEL_AUX_0 + (channelIndex - 6)); } } /* Getting here means we don't know how to map the channel position so just return MA_CHANNEL_NONE. */ return MA_CHANNEL_NONE; } static ma_channel ma_channel_map_init_standard_channel(ma_standard_channel_map standardChannelMap, ma_uint32 channelCount, ma_uint32 channelIndex) { if (channelCount == 0 || channelIndex >= channelCount) { return MA_CHANNEL_NONE; } switch (standardChannelMap) { case ma_standard_channel_map_alsa: { return ma_channel_map_init_standard_channel_alsa(channelCount, channelIndex); } break; case ma_standard_channel_map_rfc3551: { return ma_channel_map_init_standard_channel_rfc3551(channelCount, channelIndex); } break; case ma_standard_channel_map_flac: { return ma_channel_map_init_standard_channel_flac(channelCount, channelIndex); } break; case ma_standard_channel_map_vorbis: { return ma_channel_map_init_standard_channel_vorbis(channelCount, channelIndex); } break; case ma_standard_channel_map_sound4: { return ma_channel_map_init_standard_channel_sound4(channelCount, channelIndex); } break; case ma_standard_channel_map_sndio: { return ma_channel_map_init_standard_channel_sndio(channelCount, channelIndex); } break; case ma_standard_channel_map_microsoft: /* Also default. */ /*case ma_standard_channel_map_default;*/ default: { return ma_channel_map_init_standard_channel_microsoft(channelCount, channelIndex); } break; } } MA_API void ma_channel_map_init_standard(ma_standard_channel_map standardChannelMap, ma_channel* pChannelMap, size_t channelMapCap, ma_uint32 channels) { ma_uint32 iChannel; if (pChannelMap == NULL || channelMapCap == 0 || channels == 0) { return; } for (iChannel = 0; iChannel < channels; iChannel += 1) { if (channelMapCap == 0) { break; /* Ran out of room. */ } pChannelMap[0] = ma_channel_map_init_standard_channel(standardChannelMap, channels, iChannel); pChannelMap += 1; channelMapCap -= 1; } } MA_API void ma_channel_map_copy(ma_channel* pOut, const ma_channel* pIn, ma_uint32 channels) { if (pOut != NULL && pIn != NULL && channels > 0) { MA_COPY_MEMORY(pOut, pIn, sizeof(*pOut) * channels); } } MA_API void ma_channel_map_copy_or_default(ma_channel* pOut, size_t channelMapCapOut, const ma_channel* pIn, ma_uint32 channels) { if (pOut == NULL || channels == 0) { return; } if (pIn != NULL) { ma_channel_map_copy(pOut, pIn, channels); } else { ma_channel_map_init_standard(ma_standard_channel_map_default, pOut, channelMapCapOut, channels); } } MA_API ma_bool32 ma_channel_map_is_valid(const ma_channel* pChannelMap, ma_uint32 channels) { /* A channel count of 0 is invalid. */ if (channels == 0) { return MA_FALSE; } /* It does not make sense to have a mono channel when there is more than 1 channel. */ if (channels > 1) { ma_uint32 iChannel; for (iChannel = 0; iChannel < channels; ++iChannel) { if (ma_channel_map_get_channel(pChannelMap, channels, iChannel) == MA_CHANNEL_MONO) { return MA_FALSE; } } } return MA_TRUE; } MA_API ma_bool32 ma_channel_map_is_equal(const ma_channel* pChannelMapA, const ma_channel* pChannelMapB, ma_uint32 channels) { ma_uint32 iChannel; if (pChannelMapA == pChannelMapB) { return MA_TRUE; } for (iChannel = 0; iChannel < channels; ++iChannel) { if (ma_channel_map_get_channel(pChannelMapA, channels, iChannel) != ma_channel_map_get_channel(pChannelMapB, channels, iChannel)) { return MA_FALSE; } } return MA_TRUE; } MA_API ma_bool32 ma_channel_map_is_blank(const ma_channel* pChannelMap, ma_uint32 channels) { ma_uint32 iChannel; /* A null channel map is equivalent to the default channel map. */ if (pChannelMap == NULL) { return MA_FALSE; } for (iChannel = 0; iChannel < channels; ++iChannel) { if (pChannelMap[iChannel] != MA_CHANNEL_NONE) { return MA_FALSE; } } return MA_TRUE; } MA_API ma_bool32 ma_channel_map_contains_channel_position(ma_uint32 channels, const ma_channel* pChannelMap, ma_channel channelPosition) { return ma_channel_map_find_channel_position(channels, pChannelMap, channelPosition, NULL); } MA_API ma_bool32 ma_channel_map_find_channel_position(ma_uint32 channels, const ma_channel* pChannelMap, ma_channel channelPosition, ma_uint32* pChannelIndex) { ma_uint32 iChannel; if (pChannelIndex != NULL) { *pChannelIndex = (ma_uint32)-1; } for (iChannel = 0; iChannel < channels; ++iChannel) { if (ma_channel_map_get_channel(pChannelMap, channels, iChannel) == channelPosition) { if (pChannelIndex != NULL) { *pChannelIndex = iChannel; } return MA_TRUE; } } /* Getting here means the channel position was not found. */ return MA_FALSE; } MA_API size_t ma_channel_map_to_string(const ma_channel* pChannelMap, ma_uint32 channels, char* pBufferOut, size_t bufferCap) { size_t len; ma_uint32 iChannel; len = 0; for (iChannel = 0; iChannel < channels; iChannel += 1) { const char* pChannelStr = ma_channel_position_to_string(ma_channel_map_get_channel(pChannelMap, channels, iChannel)); size_t channelStrLen = strlen(pChannelStr); /* Append the string if necessary. */ if (pBufferOut != NULL && bufferCap > len + channelStrLen) { MA_COPY_MEMORY(pBufferOut + len, pChannelStr, channelStrLen); } len += channelStrLen; /* Append a space if it's not the last item. */ if (iChannel+1 < channels) { if (pBufferOut != NULL && bufferCap > len + 1) { pBufferOut[len] = ' '; } len += 1; } } /* Null terminate. Don't increment the length here. */ if (pBufferOut != NULL && bufferCap > len + 1) { pBufferOut[len] = '\0'; } return len; } MA_API const char* ma_channel_position_to_string(ma_channel channel) { switch (channel) { case MA_CHANNEL_NONE : return "CHANNEL_NONE"; case MA_CHANNEL_MONO : return "CHANNEL_MONO"; case MA_CHANNEL_FRONT_LEFT : return "CHANNEL_FRONT_LEFT"; case MA_CHANNEL_FRONT_RIGHT : return "CHANNEL_FRONT_RIGHT"; case MA_CHANNEL_FRONT_CENTER : return "CHANNEL_FRONT_CENTER"; case MA_CHANNEL_LFE : return "CHANNEL_LFE"; case MA_CHANNEL_BACK_LEFT : return "CHANNEL_BACK_LEFT"; case MA_CHANNEL_BACK_RIGHT : return "CHANNEL_BACK_RIGHT"; case MA_CHANNEL_FRONT_LEFT_CENTER : return "CHANNEL_FRONT_LEFT_CENTER "; case MA_CHANNEL_FRONT_RIGHT_CENTER: return "CHANNEL_FRONT_RIGHT_CENTER"; case MA_CHANNEL_BACK_CENTER : return "CHANNEL_BACK_CENTER"; case MA_CHANNEL_SIDE_LEFT : return "CHANNEL_SIDE_LEFT"; case MA_CHANNEL_SIDE_RIGHT : return "CHANNEL_SIDE_RIGHT"; case MA_CHANNEL_TOP_CENTER : return "CHANNEL_TOP_CENTER"; case MA_CHANNEL_TOP_FRONT_LEFT : return "CHANNEL_TOP_FRONT_LEFT"; case MA_CHANNEL_TOP_FRONT_CENTER : return "CHANNEL_TOP_FRONT_CENTER"; case MA_CHANNEL_TOP_FRONT_RIGHT : return "CHANNEL_TOP_FRONT_RIGHT"; case MA_CHANNEL_TOP_BACK_LEFT : return "CHANNEL_TOP_BACK_LEFT"; case MA_CHANNEL_TOP_BACK_CENTER : return "CHANNEL_TOP_BACK_CENTER"; case MA_CHANNEL_TOP_BACK_RIGHT : return "CHANNEL_TOP_BACK_RIGHT"; case MA_CHANNEL_AUX_0 : return "CHANNEL_AUX_0"; case MA_CHANNEL_AUX_1 : return "CHANNEL_AUX_1"; case MA_CHANNEL_AUX_2 : return "CHANNEL_AUX_2"; case MA_CHANNEL_AUX_3 : return "CHANNEL_AUX_3"; case MA_CHANNEL_AUX_4 : return "CHANNEL_AUX_4"; case MA_CHANNEL_AUX_5 : return "CHANNEL_AUX_5"; case MA_CHANNEL_AUX_6 : return "CHANNEL_AUX_6"; case MA_CHANNEL_AUX_7 : return "CHANNEL_AUX_7"; case MA_CHANNEL_AUX_8 : return "CHANNEL_AUX_8"; case MA_CHANNEL_AUX_9 : return "CHANNEL_AUX_9"; case MA_CHANNEL_AUX_10 : return "CHANNEL_AUX_10"; case MA_CHANNEL_AUX_11 : return "CHANNEL_AUX_11"; case MA_CHANNEL_AUX_12 : return "CHANNEL_AUX_12"; case MA_CHANNEL_AUX_13 : return "CHANNEL_AUX_13"; case MA_CHANNEL_AUX_14 : return "CHANNEL_AUX_14"; case MA_CHANNEL_AUX_15 : return "CHANNEL_AUX_15"; case MA_CHANNEL_AUX_16 : return "CHANNEL_AUX_16"; case MA_CHANNEL_AUX_17 : return "CHANNEL_AUX_17"; case MA_CHANNEL_AUX_18 : return "CHANNEL_AUX_18"; case MA_CHANNEL_AUX_19 : return "CHANNEL_AUX_19"; case MA_CHANNEL_AUX_20 : return "CHANNEL_AUX_20"; case MA_CHANNEL_AUX_21 : return "CHANNEL_AUX_21"; case MA_CHANNEL_AUX_22 : return "CHANNEL_AUX_22"; case MA_CHANNEL_AUX_23 : return "CHANNEL_AUX_23"; case MA_CHANNEL_AUX_24 : return "CHANNEL_AUX_24"; case MA_CHANNEL_AUX_25 : return "CHANNEL_AUX_25"; case MA_CHANNEL_AUX_26 : return "CHANNEL_AUX_26"; case MA_CHANNEL_AUX_27 : return "CHANNEL_AUX_27"; case MA_CHANNEL_AUX_28 : return "CHANNEL_AUX_28"; case MA_CHANNEL_AUX_29 : return "CHANNEL_AUX_29"; case MA_CHANNEL_AUX_30 : return "CHANNEL_AUX_30"; case MA_CHANNEL_AUX_31 : return "CHANNEL_AUX_31"; default: break; } return "UNKNOWN"; } /************************************************************************************************************************************************************** Conversion Helpers **************************************************************************************************************************************************************/ MA_API ma_uint64 ma_convert_frames(void* pOut, ma_uint64 frameCountOut, ma_format formatOut, ma_uint32 channelsOut, ma_uint32 sampleRateOut, const void* pIn, ma_uint64 frameCountIn, ma_format formatIn, ma_uint32 channelsIn, ma_uint32 sampleRateIn) { ma_data_converter_config config; config = ma_data_converter_config_init(formatIn, formatOut, channelsIn, channelsOut, sampleRateIn, sampleRateOut); config.resampling.linear.lpfOrder = ma_min(MA_DEFAULT_RESAMPLER_LPF_ORDER, MA_MAX_FILTER_ORDER); return ma_convert_frames_ex(pOut, frameCountOut, pIn, frameCountIn, &config); } MA_API ma_uint64 ma_convert_frames_ex(void* pOut, ma_uint64 frameCountOut, const void* pIn, ma_uint64 frameCountIn, const ma_data_converter_config* pConfig) { ma_result result; ma_data_converter converter; if (frameCountIn == 0 || pConfig == NULL) { return 0; } result = ma_data_converter_init(pConfig, NULL, &converter); if (result != MA_SUCCESS) { return 0; /* Failed to initialize the data converter. */ } if (pOut == NULL) { result = ma_data_converter_get_expected_output_frame_count(&converter, frameCountIn, &frameCountOut); if (result != MA_SUCCESS) { if (result == MA_NOT_IMPLEMENTED) { /* No way to calculate the number of frames, so we'll need to brute force it and loop. */ frameCountOut = 0; while (frameCountIn > 0) { ma_uint64 framesProcessedIn = frameCountIn; ma_uint64 framesProcessedOut = 0xFFFFFFFF; result = ma_data_converter_process_pcm_frames(&converter, pIn, &framesProcessedIn, NULL, &framesProcessedOut); if (result != MA_SUCCESS) { break; } frameCountIn -= framesProcessedIn; } } } } else { result = ma_data_converter_process_pcm_frames(&converter, pIn, &frameCountIn, pOut, &frameCountOut); if (result != MA_SUCCESS) { frameCountOut = 0; } } ma_data_converter_uninit(&converter, NULL); return frameCountOut; } /************************************************************************************************************************************************************** Ring Buffer **************************************************************************************************************************************************************/ static MA_INLINE ma_uint32 ma_rb__extract_offset_in_bytes(ma_uint32 encodedOffset) { return encodedOffset & 0x7FFFFFFF; } static MA_INLINE ma_uint32 ma_rb__extract_offset_loop_flag(ma_uint32 encodedOffset) { return encodedOffset & 0x80000000; } static MA_INLINE void* ma_rb__get_read_ptr(ma_rb* pRB) { MA_ASSERT(pRB != NULL); return ma_offset_ptr(pRB->pBuffer, ma_rb__extract_offset_in_bytes(c89atomic_load_32(&pRB->encodedReadOffset))); } static MA_INLINE void* ma_rb__get_write_ptr(ma_rb* pRB) { MA_ASSERT(pRB != NULL); return ma_offset_ptr(pRB->pBuffer, ma_rb__extract_offset_in_bytes(c89atomic_load_32(&pRB->encodedWriteOffset))); } static MA_INLINE ma_uint32 ma_rb__construct_offset(ma_uint32 offsetInBytes, ma_uint32 offsetLoopFlag) { return offsetLoopFlag | offsetInBytes; } static MA_INLINE void ma_rb__deconstruct_offset(ma_uint32 encodedOffset, ma_uint32* pOffsetInBytes, ma_uint32* pOffsetLoopFlag) { MA_ASSERT(pOffsetInBytes != NULL); MA_ASSERT(pOffsetLoopFlag != NULL); *pOffsetInBytes = ma_rb__extract_offset_in_bytes(encodedOffset); *pOffsetLoopFlag = ma_rb__extract_offset_loop_flag(encodedOffset); } MA_API ma_result ma_rb_init_ex(size_t subbufferSizeInBytes, size_t subbufferCount, size_t subbufferStrideInBytes, void* pOptionalPreallocatedBuffer, const ma_allocation_callbacks* pAllocationCallbacks, ma_rb* pRB) { ma_result result; const ma_uint32 maxSubBufferSize = 0x7FFFFFFF - (MA_SIMD_ALIGNMENT-1); if (pRB == NULL) { return MA_INVALID_ARGS; } if (subbufferSizeInBytes == 0 || subbufferCount == 0) { return MA_INVALID_ARGS; } if (subbufferSizeInBytes > maxSubBufferSize) { return MA_INVALID_ARGS; /* Maximum buffer size is ~2GB. The most significant bit is a flag for use internally. */ } MA_ZERO_OBJECT(pRB); result = ma_allocation_callbacks_init_copy(&pRB->allocationCallbacks, pAllocationCallbacks); if (result != MA_SUCCESS) { return result; } pRB->subbufferSizeInBytes = (ma_uint32)subbufferSizeInBytes; pRB->subbufferCount = (ma_uint32)subbufferCount; if (pOptionalPreallocatedBuffer != NULL) { pRB->subbufferStrideInBytes = (ma_uint32)subbufferStrideInBytes; pRB->pBuffer = pOptionalPreallocatedBuffer; } else { size_t bufferSizeInBytes; /* Here is where we allocate our own buffer. We always want to align this to MA_SIMD_ALIGNMENT for future SIMD optimization opportunity. To do this we need to make sure the stride is a multiple of MA_SIMD_ALIGNMENT. */ pRB->subbufferStrideInBytes = (pRB->subbufferSizeInBytes + (MA_SIMD_ALIGNMENT-1)) & ~MA_SIMD_ALIGNMENT; bufferSizeInBytes = (size_t)pRB->subbufferCount*pRB->subbufferStrideInBytes; pRB->pBuffer = ma_aligned_malloc(bufferSizeInBytes, MA_SIMD_ALIGNMENT, &pRB->allocationCallbacks); if (pRB->pBuffer == NULL) { return MA_OUT_OF_MEMORY; } MA_ZERO_MEMORY(pRB->pBuffer, bufferSizeInBytes); pRB->ownsBuffer = MA_TRUE; } return MA_SUCCESS; } MA_API ma_result ma_rb_init(size_t bufferSizeInBytes, void* pOptionalPreallocatedBuffer, const ma_allocation_callbacks* pAllocationCallbacks, ma_rb* pRB) { return ma_rb_init_ex(bufferSizeInBytes, 1, 0, pOptionalPreallocatedBuffer, pAllocationCallbacks, pRB); } MA_API void ma_rb_uninit(ma_rb* pRB) { if (pRB == NULL) { return; } if (pRB->ownsBuffer) { ma_aligned_free(pRB->pBuffer, &pRB->allocationCallbacks); } } MA_API void ma_rb_reset(ma_rb* pRB) { if (pRB == NULL) { return; } c89atomic_exchange_32(&pRB->encodedReadOffset, 0); c89atomic_exchange_32(&pRB->encodedWriteOffset, 0); } MA_API ma_result ma_rb_acquire_read(ma_rb* pRB, size_t* pSizeInBytes, void** ppBufferOut) { ma_uint32 writeOffset; ma_uint32 writeOffsetInBytes; ma_uint32 writeOffsetLoopFlag; ma_uint32 readOffset; ma_uint32 readOffsetInBytes; ma_uint32 readOffsetLoopFlag; size_t bytesAvailable; size_t bytesRequested; if (pRB == NULL || pSizeInBytes == NULL || ppBufferOut == NULL) { return MA_INVALID_ARGS; } /* The returned buffer should never move ahead of the write pointer. */ writeOffset = c89atomic_load_32(&pRB->encodedWriteOffset); ma_rb__deconstruct_offset(writeOffset, &writeOffsetInBytes, &writeOffsetLoopFlag); readOffset = c89atomic_load_32(&pRB->encodedReadOffset); ma_rb__deconstruct_offset(readOffset, &readOffsetInBytes, &readOffsetLoopFlag); /* The number of bytes available depends on whether or not the read and write pointers are on the same loop iteration. If so, we can only read up to the write pointer. If not, we can only read up to the end of the buffer. */ if (readOffsetLoopFlag == writeOffsetLoopFlag) { bytesAvailable = writeOffsetInBytes - readOffsetInBytes; } else { bytesAvailable = pRB->subbufferSizeInBytes - readOffsetInBytes; } bytesRequested = *pSizeInBytes; if (bytesRequested > bytesAvailable) { bytesRequested = bytesAvailable; } *pSizeInBytes = bytesRequested; (*ppBufferOut) = ma_rb__get_read_ptr(pRB); return MA_SUCCESS; } MA_API ma_result ma_rb_commit_read(ma_rb* pRB, size_t sizeInBytes) { ma_uint32 readOffset; ma_uint32 readOffsetInBytes; ma_uint32 readOffsetLoopFlag; ma_uint32 newReadOffsetInBytes; ma_uint32 newReadOffsetLoopFlag; if (pRB == NULL) { return MA_INVALID_ARGS; } readOffset = c89atomic_load_32(&pRB->encodedReadOffset); ma_rb__deconstruct_offset(readOffset, &readOffsetInBytes, &readOffsetLoopFlag); /* Check that sizeInBytes is correct. It should never go beyond the end of the buffer. */ newReadOffsetInBytes = (ma_uint32)(readOffsetInBytes + sizeInBytes); if (newReadOffsetInBytes > pRB->subbufferSizeInBytes) { return MA_INVALID_ARGS; /* <-- sizeInBytes will cause the read offset to overflow. */ } /* Move the read pointer back to the start if necessary. */ newReadOffsetLoopFlag = readOffsetLoopFlag; if (newReadOffsetInBytes == pRB->subbufferSizeInBytes) { newReadOffsetInBytes = 0; newReadOffsetLoopFlag ^= 0x80000000; } c89atomic_exchange_32(&pRB->encodedReadOffset, ma_rb__construct_offset(newReadOffsetLoopFlag, newReadOffsetInBytes)); if (ma_rb_pointer_distance(pRB) == 0) { return MA_AT_END; } else { return MA_SUCCESS; } } MA_API ma_result ma_rb_acquire_write(ma_rb* pRB, size_t* pSizeInBytes, void** ppBufferOut) { ma_uint32 readOffset; ma_uint32 readOffsetInBytes; ma_uint32 readOffsetLoopFlag; ma_uint32 writeOffset; ma_uint32 writeOffsetInBytes; ma_uint32 writeOffsetLoopFlag; size_t bytesAvailable; size_t bytesRequested; if (pRB == NULL || pSizeInBytes == NULL || ppBufferOut == NULL) { return MA_INVALID_ARGS; } /* The returned buffer should never overtake the read buffer. */ readOffset = c89atomic_load_32(&pRB->encodedReadOffset); ma_rb__deconstruct_offset(readOffset, &readOffsetInBytes, &readOffsetLoopFlag); writeOffset = c89atomic_load_32(&pRB->encodedWriteOffset); ma_rb__deconstruct_offset(writeOffset, &writeOffsetInBytes, &writeOffsetLoopFlag); /* In the case of writing, if the write pointer and the read pointer are on the same loop iteration we can only write up to the end of the buffer. Otherwise we can only write up to the read pointer. The write pointer should never overtake the read pointer. */ if (writeOffsetLoopFlag == readOffsetLoopFlag) { bytesAvailable = pRB->subbufferSizeInBytes - writeOffsetInBytes; } else { bytesAvailable = readOffsetInBytes - writeOffsetInBytes; } bytesRequested = *pSizeInBytes; if (bytesRequested > bytesAvailable) { bytesRequested = bytesAvailable; } *pSizeInBytes = bytesRequested; *ppBufferOut = ma_rb__get_write_ptr(pRB); /* Clear the buffer if desired. */ if (pRB->clearOnWriteAcquire) { MA_ZERO_MEMORY(*ppBufferOut, *pSizeInBytes); } return MA_SUCCESS; } MA_API ma_result ma_rb_commit_write(ma_rb* pRB, size_t sizeInBytes) { ma_uint32 writeOffset; ma_uint32 writeOffsetInBytes; ma_uint32 writeOffsetLoopFlag; ma_uint32 newWriteOffsetInBytes; ma_uint32 newWriteOffsetLoopFlag; if (pRB == NULL) { return MA_INVALID_ARGS; } writeOffset = c89atomic_load_32(&pRB->encodedWriteOffset); ma_rb__deconstruct_offset(writeOffset, &writeOffsetInBytes, &writeOffsetLoopFlag); /* Check that sizeInBytes is correct. It should never go beyond the end of the buffer. */ newWriteOffsetInBytes = (ma_uint32)(writeOffsetInBytes + sizeInBytes); if (newWriteOffsetInBytes > pRB->subbufferSizeInBytes) { return MA_INVALID_ARGS; /* <-- sizeInBytes will cause the read offset to overflow. */ } /* Move the read pointer back to the start if necessary. */ newWriteOffsetLoopFlag = writeOffsetLoopFlag; if (newWriteOffsetInBytes == pRB->subbufferSizeInBytes) { newWriteOffsetInBytes = 0; newWriteOffsetLoopFlag ^= 0x80000000; } c89atomic_exchange_32(&pRB->encodedWriteOffset, ma_rb__construct_offset(newWriteOffsetLoopFlag, newWriteOffsetInBytes)); if (ma_rb_pointer_distance(pRB) == 0) { return MA_AT_END; } else { return MA_SUCCESS; } } MA_API ma_result ma_rb_seek_read(ma_rb* pRB, size_t offsetInBytes) { ma_uint32 readOffset; ma_uint32 readOffsetInBytes; ma_uint32 readOffsetLoopFlag; ma_uint32 writeOffset; ma_uint32 writeOffsetInBytes; ma_uint32 writeOffsetLoopFlag; ma_uint32 newReadOffsetInBytes; ma_uint32 newReadOffsetLoopFlag; if (pRB == NULL || offsetInBytes > pRB->subbufferSizeInBytes) { return MA_INVALID_ARGS; } readOffset = c89atomic_load_32(&pRB->encodedReadOffset); ma_rb__deconstruct_offset(readOffset, &readOffsetInBytes, &readOffsetLoopFlag); writeOffset = c89atomic_load_32(&pRB->encodedWriteOffset); ma_rb__deconstruct_offset(writeOffset, &writeOffsetInBytes, &writeOffsetLoopFlag); newReadOffsetLoopFlag = readOffsetLoopFlag; /* We cannot go past the write buffer. */ if (readOffsetLoopFlag == writeOffsetLoopFlag) { if ((readOffsetInBytes + offsetInBytes) > writeOffsetInBytes) { newReadOffsetInBytes = writeOffsetInBytes; } else { newReadOffsetInBytes = (ma_uint32)(readOffsetInBytes + offsetInBytes); } } else { /* May end up looping. */ if ((readOffsetInBytes + offsetInBytes) >= pRB->subbufferSizeInBytes) { newReadOffsetInBytes = (ma_uint32)(readOffsetInBytes + offsetInBytes) - pRB->subbufferSizeInBytes; newReadOffsetLoopFlag ^= 0x80000000; /* <-- Looped. */ } else { newReadOffsetInBytes = (ma_uint32)(readOffsetInBytes + offsetInBytes); } } c89atomic_exchange_32(&pRB->encodedReadOffset, ma_rb__construct_offset(newReadOffsetInBytes, newReadOffsetLoopFlag)); return MA_SUCCESS; } MA_API ma_result ma_rb_seek_write(ma_rb* pRB, size_t offsetInBytes) { ma_uint32 readOffset; ma_uint32 readOffsetInBytes; ma_uint32 readOffsetLoopFlag; ma_uint32 writeOffset; ma_uint32 writeOffsetInBytes; ma_uint32 writeOffsetLoopFlag; ma_uint32 newWriteOffsetInBytes; ma_uint32 newWriteOffsetLoopFlag; if (pRB == NULL) { return MA_INVALID_ARGS; } readOffset = c89atomic_load_32(&pRB->encodedReadOffset); ma_rb__deconstruct_offset(readOffset, &readOffsetInBytes, &readOffsetLoopFlag); writeOffset = c89atomic_load_32(&pRB->encodedWriteOffset); ma_rb__deconstruct_offset(writeOffset, &writeOffsetInBytes, &writeOffsetLoopFlag); newWriteOffsetLoopFlag = writeOffsetLoopFlag; /* We cannot go past the write buffer. */ if (readOffsetLoopFlag == writeOffsetLoopFlag) { /* May end up looping. */ if ((writeOffsetInBytes + offsetInBytes) >= pRB->subbufferSizeInBytes) { newWriteOffsetInBytes = (ma_uint32)(writeOffsetInBytes + offsetInBytes) - pRB->subbufferSizeInBytes; newWriteOffsetLoopFlag ^= 0x80000000; /* <-- Looped. */ } else { newWriteOffsetInBytes = (ma_uint32)(writeOffsetInBytes + offsetInBytes); } } else { if ((writeOffsetInBytes + offsetInBytes) > readOffsetInBytes) { newWriteOffsetInBytes = readOffsetInBytes; } else { newWriteOffsetInBytes = (ma_uint32)(writeOffsetInBytes + offsetInBytes); } } c89atomic_exchange_32(&pRB->encodedWriteOffset, ma_rb__construct_offset(newWriteOffsetInBytes, newWriteOffsetLoopFlag)); return MA_SUCCESS; } MA_API ma_int32 ma_rb_pointer_distance(ma_rb* pRB) { ma_uint32 readOffset; ma_uint32 readOffsetInBytes; ma_uint32 readOffsetLoopFlag; ma_uint32 writeOffset; ma_uint32 writeOffsetInBytes; ma_uint32 writeOffsetLoopFlag; if (pRB == NULL) { return 0; } readOffset = c89atomic_load_32(&pRB->encodedReadOffset); ma_rb__deconstruct_offset(readOffset, &readOffsetInBytes, &readOffsetLoopFlag); writeOffset = c89atomic_load_32(&pRB->encodedWriteOffset); ma_rb__deconstruct_offset(writeOffset, &writeOffsetInBytes, &writeOffsetLoopFlag); if (readOffsetLoopFlag == writeOffsetLoopFlag) { return writeOffsetInBytes - readOffsetInBytes; } else { return writeOffsetInBytes + (pRB->subbufferSizeInBytes - readOffsetInBytes); } } MA_API ma_uint32 ma_rb_available_read(ma_rb* pRB) { ma_int32 dist; if (pRB == NULL) { return 0; } dist = ma_rb_pointer_distance(pRB); if (dist < 0) { return 0; } return dist; } MA_API ma_uint32 ma_rb_available_write(ma_rb* pRB) { if (pRB == NULL) { return 0; } return (ma_uint32)(ma_rb_get_subbuffer_size(pRB) - ma_rb_pointer_distance(pRB)); } MA_API size_t ma_rb_get_subbuffer_size(ma_rb* pRB) { if (pRB == NULL) { return 0; } return pRB->subbufferSizeInBytes; } MA_API size_t ma_rb_get_subbuffer_stride(ma_rb* pRB) { if (pRB == NULL) { return 0; } if (pRB->subbufferStrideInBytes == 0) { return (size_t)pRB->subbufferSizeInBytes; } return (size_t)pRB->subbufferStrideInBytes; } MA_API size_t ma_rb_get_subbuffer_offset(ma_rb* pRB, size_t subbufferIndex) { if (pRB == NULL) { return 0; } return subbufferIndex * ma_rb_get_subbuffer_stride(pRB); } MA_API void* ma_rb_get_subbuffer_ptr(ma_rb* pRB, size_t subbufferIndex, void* pBuffer) { if (pRB == NULL) { return NULL; } return ma_offset_ptr(pBuffer, ma_rb_get_subbuffer_offset(pRB, subbufferIndex)); } static MA_INLINE ma_uint32 ma_pcm_rb_get_bpf(ma_pcm_rb* pRB) { MA_ASSERT(pRB != NULL); return ma_get_bytes_per_frame(pRB->format, pRB->channels); } MA_API ma_result ma_pcm_rb_init_ex(ma_format format, ma_uint32 channels, ma_uint32 subbufferSizeInFrames, ma_uint32 subbufferCount, ma_uint32 subbufferStrideInFrames, void* pOptionalPreallocatedBuffer, const ma_allocation_callbacks* pAllocationCallbacks, ma_pcm_rb* pRB) { ma_uint32 bpf; ma_result result; if (pRB == NULL) { return MA_INVALID_ARGS; } MA_ZERO_OBJECT(pRB); bpf = ma_get_bytes_per_frame(format, channels); if (bpf == 0) { return MA_INVALID_ARGS; } result = ma_rb_init_ex(subbufferSizeInFrames*bpf, subbufferCount, subbufferStrideInFrames*bpf, pOptionalPreallocatedBuffer, pAllocationCallbacks, &pRB->rb); if (result != MA_SUCCESS) { return result; } pRB->format = format; pRB->channels = channels; return MA_SUCCESS; } MA_API ma_result ma_pcm_rb_init(ma_format format, ma_uint32 channels, ma_uint32 bufferSizeInFrames, void* pOptionalPreallocatedBuffer, const ma_allocation_callbacks* pAllocationCallbacks, ma_pcm_rb* pRB) { return ma_pcm_rb_init_ex(format, channels, bufferSizeInFrames, 1, 0, pOptionalPreallocatedBuffer, pAllocationCallbacks, pRB); } MA_API void ma_pcm_rb_uninit(ma_pcm_rb* pRB) { if (pRB == NULL) { return; } ma_rb_uninit(&pRB->rb); } MA_API void ma_pcm_rb_reset(ma_pcm_rb* pRB) { if (pRB == NULL) { return; } ma_rb_reset(&pRB->rb); } MA_API ma_result ma_pcm_rb_acquire_read(ma_pcm_rb* pRB, ma_uint32* pSizeInFrames, void** ppBufferOut) { size_t sizeInBytes; ma_result result; if (pRB == NULL || pSizeInFrames == NULL) { return MA_INVALID_ARGS; } sizeInBytes = *pSizeInFrames * ma_pcm_rb_get_bpf(pRB); result = ma_rb_acquire_read(&pRB->rb, &sizeInBytes, ppBufferOut); if (result != MA_SUCCESS) { return result; } *pSizeInFrames = (ma_uint32)(sizeInBytes / (size_t)ma_pcm_rb_get_bpf(pRB)); return MA_SUCCESS; } MA_API ma_result ma_pcm_rb_commit_read(ma_pcm_rb* pRB, ma_uint32 sizeInFrames) { if (pRB == NULL) { return MA_INVALID_ARGS; } return ma_rb_commit_read(&pRB->rb, sizeInFrames * ma_pcm_rb_get_bpf(pRB)); } MA_API ma_result ma_pcm_rb_acquire_write(ma_pcm_rb* pRB, ma_uint32* pSizeInFrames, void** ppBufferOut) { size_t sizeInBytes; ma_result result; if (pRB == NULL) { return MA_INVALID_ARGS; } sizeInBytes = *pSizeInFrames * ma_pcm_rb_get_bpf(pRB); result = ma_rb_acquire_write(&pRB->rb, &sizeInBytes, ppBufferOut); if (result != MA_SUCCESS) { return result; } *pSizeInFrames = (ma_uint32)(sizeInBytes / ma_pcm_rb_get_bpf(pRB)); return MA_SUCCESS; } MA_API ma_result ma_pcm_rb_commit_write(ma_pcm_rb* pRB, ma_uint32 sizeInFrames) { if (pRB == NULL) { return MA_INVALID_ARGS; } return ma_rb_commit_write(&pRB->rb, sizeInFrames * ma_pcm_rb_get_bpf(pRB)); } MA_API ma_result ma_pcm_rb_seek_read(ma_pcm_rb* pRB, ma_uint32 offsetInFrames) { if (pRB == NULL) { return MA_INVALID_ARGS; } return ma_rb_seek_read(&pRB->rb, offsetInFrames * ma_pcm_rb_get_bpf(pRB)); } MA_API ma_result ma_pcm_rb_seek_write(ma_pcm_rb* pRB, ma_uint32 offsetInFrames) { if (pRB == NULL) { return MA_INVALID_ARGS; } return ma_rb_seek_write(&pRB->rb, offsetInFrames * ma_pcm_rb_get_bpf(pRB)); } MA_API ma_int32 ma_pcm_rb_pointer_distance(ma_pcm_rb* pRB) { if (pRB == NULL) { return 0; } return ma_rb_pointer_distance(&pRB->rb) / ma_pcm_rb_get_bpf(pRB); } MA_API ma_uint32 ma_pcm_rb_available_read(ma_pcm_rb* pRB) { if (pRB == NULL) { return 0; } return ma_rb_available_read(&pRB->rb) / ma_pcm_rb_get_bpf(pRB); } MA_API ma_uint32 ma_pcm_rb_available_write(ma_pcm_rb* pRB) { if (pRB == NULL) { return 0; } return ma_rb_available_write(&pRB->rb) / ma_pcm_rb_get_bpf(pRB); } MA_API ma_uint32 ma_pcm_rb_get_subbuffer_size(ma_pcm_rb* pRB) { if (pRB == NULL) { return 0; } return (ma_uint32)(ma_rb_get_subbuffer_size(&pRB->rb) / ma_pcm_rb_get_bpf(pRB)); } MA_API ma_uint32 ma_pcm_rb_get_subbuffer_stride(ma_pcm_rb* pRB) { if (pRB == NULL) { return 0; } return (ma_uint32)(ma_rb_get_subbuffer_stride(&pRB->rb) / ma_pcm_rb_get_bpf(pRB)); } MA_API ma_uint32 ma_pcm_rb_get_subbuffer_offset(ma_pcm_rb* pRB, ma_uint32 subbufferIndex) { if (pRB == NULL) { return 0; } return (ma_uint32)(ma_rb_get_subbuffer_offset(&pRB->rb, subbufferIndex) / ma_pcm_rb_get_bpf(pRB)); } MA_API void* ma_pcm_rb_get_subbuffer_ptr(ma_pcm_rb* pRB, ma_uint32 subbufferIndex, void* pBuffer) { if (pRB == NULL) { return NULL; } return ma_rb_get_subbuffer_ptr(&pRB->rb, subbufferIndex, pBuffer); } MA_API ma_result ma_duplex_rb_init(ma_format captureFormat, ma_uint32 captureChannels, ma_uint32 sampleRate, ma_uint32 captureInternalSampleRate, ma_uint32 captureInternalPeriodSizeInFrames, const ma_allocation_callbacks* pAllocationCallbacks, ma_duplex_rb* pRB) { ma_result result; ma_uint32 sizeInFrames; sizeInFrames = (ma_uint32)ma_calculate_frame_count_after_resampling(sampleRate, captureInternalSampleRate, captureInternalPeriodSizeInFrames * 5); if (sizeInFrames == 0) { return MA_INVALID_ARGS; } result = ma_pcm_rb_init(captureFormat, captureChannels, sizeInFrames, NULL, pAllocationCallbacks, &pRB->rb); if (result != MA_SUCCESS) { return result; } /* Seek forward a bit so we have a bit of a buffer in case of desyncs. */ ma_pcm_rb_seek_write((ma_pcm_rb*)pRB, captureInternalPeriodSizeInFrames * 2); return MA_SUCCESS; } MA_API ma_result ma_duplex_rb_uninit(ma_duplex_rb* pRB) { ma_pcm_rb_uninit((ma_pcm_rb*)pRB); return MA_SUCCESS; } /************************************************************************************************************************************************************** Miscellaneous Helpers **************************************************************************************************************************************************************/ MA_API const char* ma_result_description(ma_result result) { switch (result) { case MA_SUCCESS: return "No error"; case MA_ERROR: return "Unknown error"; case MA_INVALID_ARGS: return "Invalid argument"; case MA_INVALID_OPERATION: return "Invalid operation"; case MA_OUT_OF_MEMORY: return "Out of memory"; case MA_OUT_OF_RANGE: return "Out of range"; case MA_ACCESS_DENIED: return "Permission denied"; case MA_DOES_NOT_EXIST: return "Resource does not exist"; case MA_ALREADY_EXISTS: return "Resource already exists"; case MA_TOO_MANY_OPEN_FILES: return "Too many open files"; case MA_INVALID_FILE: return "Invalid file"; case MA_TOO_BIG: return "Too large"; case MA_PATH_TOO_LONG: return "Path too long"; case MA_NAME_TOO_LONG: return "Name too long"; case MA_NOT_DIRECTORY: return "Not a directory"; case MA_IS_DIRECTORY: return "Is a directory"; case MA_DIRECTORY_NOT_EMPTY: return "Directory not empty"; case MA_AT_END: return "At end"; case MA_NO_SPACE: return "No space available"; case MA_BUSY: return "Device or resource busy"; case MA_IO_ERROR: return "Input/output error"; case MA_INTERRUPT: return "Interrupted"; case MA_UNAVAILABLE: return "Resource unavailable"; case MA_ALREADY_IN_USE: return "Resource already in use"; case MA_BAD_ADDRESS: return "Bad address"; case MA_BAD_SEEK: return "Illegal seek"; case MA_BAD_PIPE: return "Broken pipe"; case MA_DEADLOCK: return "Deadlock"; case MA_TOO_MANY_LINKS: return "Too many links"; case MA_NOT_IMPLEMENTED: return "Not implemented"; case MA_NO_MESSAGE: return "No message of desired type"; case MA_BAD_MESSAGE: return "Invalid message"; case MA_NO_DATA_AVAILABLE: return "No data available"; case MA_INVALID_DATA: return "Invalid data"; case MA_TIMEOUT: return "Timeout"; case MA_NO_NETWORK: return "Network unavailable"; case MA_NOT_UNIQUE: return "Not unique"; case MA_NOT_SOCKET: return "Socket operation on non-socket"; case MA_NO_ADDRESS: return "Destination address required"; case MA_BAD_PROTOCOL: return "Protocol wrong type for socket"; case MA_PROTOCOL_UNAVAILABLE: return "Protocol not available"; case MA_PROTOCOL_NOT_SUPPORTED: return "Protocol not supported"; case MA_PROTOCOL_FAMILY_NOT_SUPPORTED: return "Protocol family not supported"; case MA_ADDRESS_FAMILY_NOT_SUPPORTED: return "Address family not supported"; case MA_SOCKET_NOT_SUPPORTED: return "Socket type not supported"; case MA_CONNECTION_RESET: return "Connection reset"; case MA_ALREADY_CONNECTED: return "Already connected"; case MA_NOT_CONNECTED: return "Not connected"; case MA_CONNECTION_REFUSED: return "Connection refused"; case MA_NO_HOST: return "No host"; case MA_IN_PROGRESS: return "Operation in progress"; case MA_CANCELLED: return "Operation cancelled"; case MA_MEMORY_ALREADY_MAPPED: return "Memory already mapped"; case MA_FORMAT_NOT_SUPPORTED: return "Format not supported"; case MA_DEVICE_TYPE_NOT_SUPPORTED: return "Device type not supported"; case MA_SHARE_MODE_NOT_SUPPORTED: return "Share mode not supported"; case MA_NO_BACKEND: return "No backend"; case MA_NO_DEVICE: return "No device"; case MA_API_NOT_FOUND: return "API not found"; case MA_INVALID_DEVICE_CONFIG: return "Invalid device config"; case MA_DEVICE_NOT_INITIALIZED: return "Device not initialized"; case MA_DEVICE_NOT_STARTED: return "Device not started"; case MA_FAILED_TO_INIT_BACKEND: return "Failed to initialize backend"; case MA_FAILED_TO_OPEN_BACKEND_DEVICE: return "Failed to open backend device"; case MA_FAILED_TO_START_BACKEND_DEVICE: return "Failed to start backend device"; case MA_FAILED_TO_STOP_BACKEND_DEVICE: return "Failed to stop backend device"; default: return "Unknown error"; } } MA_API void* ma_malloc(size_t sz, const ma_allocation_callbacks* pAllocationCallbacks) { if (pAllocationCallbacks != NULL) { if (pAllocationCallbacks->onMalloc != NULL) { return pAllocationCallbacks->onMalloc(sz, pAllocationCallbacks->pUserData); } else { return NULL; /* Do not fall back to the default implementation. */ } } else { return ma__malloc_default(sz, NULL); } } MA_API void* ma_calloc(size_t sz, const ma_allocation_callbacks* pAllocationCallbacks) { void* p = ma_malloc(sz, pAllocationCallbacks); if (p != NULL) { MA_ZERO_MEMORY(p, sz); } return p; } MA_API void* ma_realloc(void* p, size_t sz, const ma_allocation_callbacks* pAllocationCallbacks) { if (pAllocationCallbacks != NULL) { if (pAllocationCallbacks->onRealloc != NULL) { return pAllocationCallbacks->onRealloc(p, sz, pAllocationCallbacks->pUserData); } else { return NULL; /* Do not fall back to the default implementation. */ } } else { return ma__realloc_default(p, sz, NULL); } } MA_API void ma_free(void* p, const ma_allocation_callbacks* pAllocationCallbacks) { if (p == NULL) { return; } if (pAllocationCallbacks != NULL) { if (pAllocationCallbacks->onFree != NULL) { pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData); } else { return; /* Do no fall back to the default implementation. */ } } else { ma__free_default(p, NULL); } } MA_API void* ma_aligned_malloc(size_t sz, size_t alignment, const ma_allocation_callbacks* pAllocationCallbacks) { size_t extraBytes; void* pUnaligned; void* pAligned; if (alignment == 0) { return 0; } extraBytes = alignment-1 + sizeof(void*); pUnaligned = ma_malloc(sz + extraBytes, pAllocationCallbacks); if (pUnaligned == NULL) { return NULL; } pAligned = (void*)(((ma_uintptr)pUnaligned + extraBytes) & ~((ma_uintptr)(alignment-1))); ((void**)pAligned)[-1] = pUnaligned; return pAligned; } MA_API void ma_aligned_free(void* p, const ma_allocation_callbacks* pAllocationCallbacks) { ma_free(((void**)p)[-1], pAllocationCallbacks); } MA_API const char* ma_get_format_name(ma_format format) { switch (format) { case ma_format_unknown: return "Unknown"; case ma_format_u8: return "8-bit Unsigned Integer"; case ma_format_s16: return "16-bit Signed Integer"; case ma_format_s24: return "24-bit Signed Integer (Tightly Packed)"; case ma_format_s32: return "32-bit Signed Integer"; case ma_format_f32: return "32-bit IEEE Floating Point"; default: return "Invalid"; } } MA_API void ma_blend_f32(float* pOut, float* pInA, float* pInB, float factor, ma_uint32 channels) { ma_uint32 i; for (i = 0; i < channels; ++i) { pOut[i] = ma_mix_f32(pInA[i], pInB[i], factor); } } MA_API ma_uint32 ma_get_bytes_per_sample(ma_format format) { ma_uint32 sizes[] = { 0, /* unknown */ 1, /* u8 */ 2, /* s16 */ 3, /* s24 */ 4, /* s32 */ 4, /* f32 */ }; return sizes[format]; } MA_API ma_data_source_config ma_data_source_config_init(void) { ma_data_source_config config; MA_ZERO_OBJECT(&config); return config; } MA_API ma_result ma_data_source_init(const ma_data_source_config* pConfig, ma_data_source* pDataSource) { ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource; if (pDataSource == NULL) { return MA_INVALID_ARGS; } MA_ZERO_OBJECT(pDataSourceBase); if (pConfig == NULL) { return MA_INVALID_ARGS; } pDataSourceBase->vtable = pConfig->vtable; pDataSourceBase->rangeBegInFrames = 0; pDataSourceBase->rangeEndInFrames = ~((ma_uint64)0); pDataSourceBase->loopBegInFrames = 0; pDataSourceBase->loopEndInFrames = ~((ma_uint64)0); pDataSourceBase->pCurrent = pDataSource; /* Always read from ourself by default. */ pDataSourceBase->pNext = NULL; pDataSourceBase->onGetNext = NULL; return MA_SUCCESS; } MA_API void ma_data_source_uninit(ma_data_source* pDataSource) { if (pDataSource == NULL) { return; } /* This is placeholder in case we need this later. Data sources need to call this in their uninitialization routine to ensure things work later on if something is added here. */ } static ma_result ma_data_source_resolve_current(ma_data_source* pDataSource, ma_data_source** ppCurrentDataSource) { ma_data_source_base* pCurrentDataSource = (ma_data_source_base*)pDataSource; MA_ASSERT(pDataSource != NULL); MA_ASSERT(ppCurrentDataSource != NULL); if (pCurrentDataSource->pCurrent == NULL) { /* The current data source is NULL. If we're using this in the context of a chain we need to return NULL here so that we don't end up looping. Otherwise we just return the data source itself. */ if (pCurrentDataSource->pNext != NULL || pCurrentDataSource->onGetNext != NULL) { pCurrentDataSource = NULL; } else { pCurrentDataSource = (ma_data_source_base*)pDataSource; /* Not being used in a chain. Make sure we just always read from the data source itself at all times. */ } } else { pCurrentDataSource = (ma_data_source_base*)pCurrentDataSource->pCurrent; } *ppCurrentDataSource = pCurrentDataSource; return MA_SUCCESS; } static ma_result ma_data_source_read_pcm_frames_within_range(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) { ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource; ma_result result; ma_uint64 framesRead = 0; ma_bool32 loop = ma_data_source_is_looping(pDataSource); if (pDataSourceBase == NULL) { return MA_AT_END; } if (frameCount == 0) { return MA_INVALID_ARGS; } if ((pDataSourceBase->vtable->flags & MA_DATA_SOURCE_SELF_MANAGED_RANGE_AND_LOOP_POINT) != 0 || (pDataSourceBase->rangeEndInFrames == ~((ma_uint64)0) && (pDataSourceBase->loopEndInFrames == ~((ma_uint64)0) || loop == MA_FALSE))) { /* Either the data source is self-managing the range, or no range is set - just read like normal. The data source itself will tell us when the end is reached. */ result = pDataSourceBase->vtable->onRead(pDataSourceBase, pFramesOut, frameCount, &framesRead); } else { /* Need to clamp to within the range. */ ma_uint64 cursor; result = ma_data_source_get_cursor_in_pcm_frames(pDataSourceBase, &cursor); if (result != MA_SUCCESS) { /* Failed to retrieve the cursor. Cannot read within a range or loop points. Just read like normal - this may happen for things like noise data sources where it doesn't really matter. */ result = pDataSourceBase->vtable->onRead(pDataSourceBase, pFramesOut, frameCount, &framesRead); } else { ma_uint64 rangeEnd; /* We have the cursor. We need to make sure we don't read beyond our range. */ rangeEnd = pDataSourceBase->rangeEndInFrames; /* If looping, make sure we're within range. */ if (loop) { if (pDataSourceBase->loopEndInFrames != ~((ma_uint64)0)) { rangeEnd = ma_min(rangeEnd, pDataSourceBase->rangeBegInFrames + pDataSourceBase->loopEndInFrames); } } if (frameCount > (rangeEnd - cursor) && rangeEnd != ~((ma_uint64)0)) { frameCount = (rangeEnd - cursor); } /* If the cursor is sitting on the end of the range the frame count will be set to 0 which can result in MA_INVALID_ARGS. In this case, we don't want to try reading, but instead return MA_AT_END so the higher level function can know about it. */ if (frameCount > 0) { result = pDataSourceBase->vtable->onRead(pDataSourceBase, pFramesOut, frameCount, &framesRead); } else { result = MA_AT_END; /* The cursor is sitting on the end of the range which means we're at the end. */ } } } if (pFramesRead != NULL) { *pFramesRead = framesRead; } /* We need to make sure MA_AT_END is returned if we hit the end of the range. */ if (result == MA_SUCCESS && framesRead == 0) { result = MA_AT_END; } return result; } MA_API ma_result ma_data_source_read_pcm_frames(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) { ma_result result = MA_SUCCESS; ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource; ma_data_source_base* pCurrentDataSource; void* pRunningFramesOut = pFramesOut; ma_uint64 totalFramesProcessed = 0; ma_format format; ma_uint32 channels; ma_uint32 emptyLoopCounter = 0; /* Keeps track of how many times 0 frames have been read. For infinite loop detection of sounds with no audio data. */ ma_bool32 loop; if (pFramesRead != NULL) { *pFramesRead = 0; } if (frameCount == 0) { return MA_INVALID_ARGS; } if (pDataSourceBase == NULL) { return MA_INVALID_ARGS; } loop = ma_data_source_is_looping(pDataSource); /* We need to know the data format so we can advance the output buffer as we read frames. If this fails, chaining will not work and we'll just read as much as we can from the current source. */ if (ma_data_source_get_data_format(pDataSource, &format, &channels, NULL, NULL, 0) != MA_SUCCESS) { result = ma_data_source_resolve_current(pDataSource, (ma_data_source**)&pCurrentDataSource); if (result != MA_SUCCESS) { return result; } return ma_data_source_read_pcm_frames_within_range(pCurrentDataSource, pFramesOut, frameCount, pFramesRead); } /* Looping is a bit of a special case. When the `loop` argument is true, chaining will not work and only the current data source will be read from. */ /* Keep reading until we've read as many frames as possible. */ while (totalFramesProcessed < frameCount) { ma_uint64 framesProcessed; ma_uint64 framesRemaining = frameCount - totalFramesProcessed; /* We need to resolve the data source that we'll actually be reading from. */ result = ma_data_source_resolve_current(pDataSource, (ma_data_source**)&pCurrentDataSource); if (result != MA_SUCCESS) { break; } if (pCurrentDataSource == NULL) { break; } result = ma_data_source_read_pcm_frames_within_range(pCurrentDataSource, pRunningFramesOut, framesRemaining, &framesProcessed); totalFramesProcessed += framesProcessed; /* If we encounted an error from the read callback, make sure it's propagated to the caller. The caller may need to know whether or not MA_BUSY is returned which is not necessarily considered an error. */ if (result != MA_SUCCESS && result != MA_AT_END) { break; } /* We can determine if we've reached the end by checking if ma_data_source_read_pcm_frames_within_range() returned MA_AT_END. To loop back to the start, all we need to do is seek back to the first frame. */ if (result == MA_AT_END) { /* The result needs to be reset back to MA_SUCCESS (from MA_AT_END) so that we don't accidentally return MA_AT_END when data has been read in prior loop iterations. at the end of this function, the result will be checked for MA_SUCCESS, and if the total number of frames processed is 0, will be explicitly set to MA_AT_END. */ result = MA_SUCCESS; /* We reached the end. If we're looping, we just loop back to the start of the current data source. If we're not looping we need to check if we have another in the chain, and if so, switch to it. */ if (loop) { if (framesProcessed == 0) { emptyLoopCounter += 1; if (emptyLoopCounter > 1) { break; /* Infinite loop detected. Get out. */ } } else { emptyLoopCounter = 0; } result = ma_data_source_seek_to_pcm_frame(pCurrentDataSource, pCurrentDataSource->loopBegInFrames); if (result != MA_SUCCESS) { break; /* Failed to loop. Abort. */ } /* Don't return MA_AT_END for looping sounds. */ result = MA_SUCCESS; } else { if (pCurrentDataSource->pNext != NULL) { pDataSourceBase->pCurrent = pCurrentDataSource->pNext; } else if (pCurrentDataSource->onGetNext != NULL) { pDataSourceBase->pCurrent = pCurrentDataSource->onGetNext(pCurrentDataSource); if (pDataSourceBase->pCurrent == NULL) { break; /* Our callback did not return a next data source. We're done. */ } } else { /* Reached the end of the chain. We're done. */ break; } /* The next data source needs to be rewound to ensure data is read in looping scenarios. */ result = ma_data_source_seek_to_pcm_frame(pDataSourceBase->pCurrent, 0); if (result != MA_SUCCESS) { break; } } } if (pRunningFramesOut != NULL) { pRunningFramesOut = ma_offset_ptr(pRunningFramesOut, framesProcessed * ma_get_bytes_per_frame(format, channels)); } } if (pFramesRead != NULL) { *pFramesRead = totalFramesProcessed; } MA_ASSERT(!(result == MA_AT_END && totalFramesProcessed > 0)); /* We should never be returning MA_AT_END if we read some data. */ if (result == MA_SUCCESS && totalFramesProcessed == 0) { result = MA_AT_END; } return result; } MA_API ma_result ma_data_source_seek_pcm_frames(ma_data_source* pDataSource, ma_uint64 frameCount, ma_uint64* pFramesSeeked) { return ma_data_source_read_pcm_frames(pDataSource, NULL, frameCount, pFramesSeeked); } MA_API ma_result ma_data_source_seek_to_pcm_frame(ma_data_source* pDataSource, ma_uint64 frameIndex) { ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource; if (pDataSourceBase == NULL) { return MA_SUCCESS; } if (pDataSourceBase->vtable->onSeek == NULL) { return MA_NOT_IMPLEMENTED; } if (frameIndex > pDataSourceBase->rangeEndInFrames) { return MA_INVALID_OPERATION; /* Trying to seek to far forward. */ } return pDataSourceBase->vtable->onSeek(pDataSource, pDataSourceBase->rangeBegInFrames + frameIndex); } MA_API ma_result ma_data_source_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) { ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource; ma_result result; ma_format format; ma_uint32 channels; ma_uint32 sampleRate; /* Initialize to defaults for safety just in case the data source does not implement this callback. */ if (pFormat != NULL) { *pFormat = ma_format_unknown; } if (pChannels != NULL) { *pChannels = 0; } if (pSampleRate != NULL) { *pSampleRate = 0; } if (pChannelMap != NULL) { MA_ZERO_MEMORY(pChannelMap, sizeof(*pChannelMap) * channelMapCap); } if (pDataSourceBase == NULL) { return MA_INVALID_ARGS; } if (pDataSourceBase->vtable->onGetDataFormat == NULL) { return MA_NOT_IMPLEMENTED; } result = pDataSourceBase->vtable->onGetDataFormat(pDataSource, &format, &channels, &sampleRate, pChannelMap, channelMapCap); if (result != MA_SUCCESS) { return result; } if (pFormat != NULL) { *pFormat = format; } if (pChannels != NULL) { *pChannels = channels; } if (pSampleRate != NULL) { *pSampleRate = sampleRate; } /* Channel map was passed in directly to the callback. This is safe due to the channelMapCap parameter. */ return MA_SUCCESS; } MA_API ma_result ma_data_source_get_cursor_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pCursor) { ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource; ma_result result; ma_uint64 cursor; if (pCursor == NULL) { return MA_INVALID_ARGS; } *pCursor = 0; if (pDataSourceBase == NULL) { return MA_SUCCESS; } if (pDataSourceBase->vtable->onGetCursor == NULL) { return MA_NOT_IMPLEMENTED; } result = pDataSourceBase->vtable->onGetCursor(pDataSourceBase, &cursor); if (result != MA_SUCCESS) { return result; } /* The cursor needs to be made relative to the start of the range. */ if (cursor < pDataSourceBase->rangeBegInFrames) { /* Safety check so we don't return some huge number. */ *pCursor = 0; } else { *pCursor = cursor - pDataSourceBase->rangeBegInFrames; } return MA_SUCCESS; } MA_API ma_result ma_data_source_get_length_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pLength) { ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource; if (pLength == NULL) { return MA_INVALID_ARGS; } *pLength = 0; if (pDataSourceBase == NULL) { return MA_INVALID_ARGS; } /* If we have a range defined we'll use that to determine the length. This is one of rare times where we'll actually trust the caller. If they've set the range, I think it's mostly safe to assume they've set it based on some higher level knowledge of the structure of the sound bank. */ if (pDataSourceBase->rangeEndInFrames != ~((ma_uint64)0)) { *pLength = pDataSourceBase->rangeEndInFrames - pDataSourceBase->rangeBegInFrames; return MA_SUCCESS; } /* Getting here means a range is not defined so we'll need to get the data source itself to tell us the length. */ if (pDataSourceBase->vtable->onGetLength == NULL) { return MA_NOT_IMPLEMENTED; } return pDataSourceBase->vtable->onGetLength(pDataSource, pLength); } MA_API ma_result ma_data_source_get_cursor_in_seconds(ma_data_source* pDataSource, float* pCursor) { ma_result result; ma_uint64 cursorInPCMFrames; ma_uint32 sampleRate; if (pCursor == NULL) { return MA_INVALID_ARGS; } *pCursor = 0; result = ma_data_source_get_cursor_in_pcm_frames(pDataSource, &cursorInPCMFrames); if (result != MA_SUCCESS) { return result; } result = ma_data_source_get_data_format(pDataSource, NULL, NULL, &sampleRate, NULL, 0); if (result != MA_SUCCESS) { return result; } /* VC6 does not support division of unsigned 64-bit integers with floating point numbers. Need to use a signed number. This shouldn't effect anything in practice. */ *pCursor = (ma_int64)cursorInPCMFrames / (float)sampleRate; return MA_SUCCESS; } MA_API ma_result ma_data_source_get_length_in_seconds(ma_data_source* pDataSource, float* pLength) { ma_result result; ma_uint64 lengthInPCMFrames; ma_uint32 sampleRate; if (pLength == NULL) { return MA_INVALID_ARGS; } *pLength = 0; result = ma_data_source_get_length_in_pcm_frames(pDataSource, &lengthInPCMFrames); if (result != MA_SUCCESS) { return result; } result = ma_data_source_get_data_format(pDataSource, NULL, NULL, &sampleRate, NULL, 0); if (result != MA_SUCCESS) { return result; } /* VC6 does not support division of unsigned 64-bit integers with floating point numbers. Need to use a signed number. This shouldn't effect anything in practice. */ *pLength = (ma_int64)lengthInPCMFrames / (float)sampleRate; return MA_SUCCESS; } MA_API ma_result ma_data_source_set_looping(ma_data_source* pDataSource, ma_bool32 isLooping) { ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource; if (pDataSource == NULL) { return MA_INVALID_ARGS; } c89atomic_exchange_32(&pDataSourceBase->isLooping, isLooping); /* If there's no callback for this just treat it as a successful no-op. */ if (pDataSourceBase->vtable->onSetLooping == NULL) { return MA_SUCCESS; } return pDataSourceBase->vtable->onSetLooping(pDataSource, isLooping); } MA_API ma_bool32 ma_data_source_is_looping(const ma_data_source* pDataSource) { const ma_data_source_base* pDataSourceBase = (const ma_data_source_base*)pDataSource; if (pDataSource == NULL) { return MA_FALSE; } return c89atomic_load_32(&pDataSourceBase->isLooping); } MA_API ma_result ma_data_source_set_range_in_pcm_frames(ma_data_source* pDataSource, ma_uint64 rangeBegInFrames, ma_uint64 rangeEndInFrames) { ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource; ma_result result; ma_uint64 cursor; ma_uint64 loopBegAbsolute; ma_uint64 loopEndAbsolute; if (pDataSource == NULL) { return MA_INVALID_ARGS; } if (rangeEndInFrames < rangeBegInFrames) { return MA_INVALID_ARGS; /* The end of the range must come after the beginning. */ } /* The loop points need to be updated. We'll be storing the loop points relative to the range. We'll update these so that they maintain their absolute positioning. The loop points will then be clamped to the range. */ loopBegAbsolute = pDataSourceBase->loopBegInFrames + pDataSourceBase->rangeBegInFrames; loopEndAbsolute = pDataSourceBase->loopEndInFrames + ((pDataSourceBase->loopEndInFrames != ~((ma_uint64)0)) ? pDataSourceBase->rangeBegInFrames : 0); pDataSourceBase->rangeBegInFrames = rangeBegInFrames; pDataSourceBase->rangeEndInFrames = rangeEndInFrames; /* Make the loop points relative again, and make sure they're clamped to within the range. */ if (loopBegAbsolute > pDataSourceBase->rangeBegInFrames) { pDataSourceBase->loopBegInFrames = loopBegAbsolute - pDataSourceBase->rangeBegInFrames; } else { pDataSourceBase->loopBegInFrames = 0; } if (pDataSourceBase->loopBegInFrames > pDataSourceBase->rangeEndInFrames) { pDataSourceBase->loopBegInFrames = pDataSourceBase->rangeEndInFrames; } /* Only need to update the loop end point if it's not -1. */ if (loopEndAbsolute != ~((ma_uint64)0)) { if (loopEndAbsolute > pDataSourceBase->rangeBegInFrames) { pDataSourceBase->loopEndInFrames = loopEndAbsolute - pDataSourceBase->rangeBegInFrames; } else { pDataSourceBase->loopEndInFrames = 0; } if (pDataSourceBase->loopEndInFrames > pDataSourceBase->rangeEndInFrames && pDataSourceBase->loopEndInFrames) { pDataSourceBase->loopEndInFrames = pDataSourceBase->rangeEndInFrames; } } /* If the new range is past the current cursor position we need to seek to it. */ result = ma_data_source_get_cursor_in_pcm_frames(pDataSource, &cursor); if (result == MA_SUCCESS) { /* Seek to within range. Note that our seek positions here are relative to the new range. */ if (cursor < rangeBegInFrames) { ma_data_source_seek_to_pcm_frame(pDataSource, 0); } else if (cursor > rangeEndInFrames) { ma_data_source_seek_to_pcm_frame(pDataSource, rangeEndInFrames - rangeBegInFrames); } } else { /* We failed to get the cursor position. Probably means the data source has no notion of a cursor such a noise data source. Just pretend the seeking worked. */ } return MA_SUCCESS; } MA_API void ma_data_source_get_range_in_pcm_frames(const ma_data_source* pDataSource, ma_uint64* pRangeBegInFrames, ma_uint64* pRangeEndInFrames) { const ma_data_source_base* pDataSourceBase = (const ma_data_source_base*)pDataSource; if (pDataSource == NULL) { return; } if (pRangeBegInFrames != NULL) { *pRangeBegInFrames = pDataSourceBase->rangeBegInFrames; } if (pRangeEndInFrames != NULL) { *pRangeEndInFrames = pDataSourceBase->rangeEndInFrames; } } MA_API ma_result ma_data_source_set_loop_point_in_pcm_frames(ma_data_source* pDataSource, ma_uint64 loopBegInFrames, ma_uint64 loopEndInFrames) { ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource; if (pDataSource == NULL) { return MA_INVALID_ARGS; } if (loopEndInFrames < loopBegInFrames) { return MA_INVALID_ARGS; /* The end of the loop point must come after the beginning. */ } if (loopEndInFrames > pDataSourceBase->rangeEndInFrames && loopEndInFrames != ~((ma_uint64)0)) { return MA_INVALID_ARGS; /* The end of the loop point must not go beyond the range. */ } pDataSourceBase->loopBegInFrames = loopBegInFrames; pDataSourceBase->loopEndInFrames = loopEndInFrames; /* The end cannot exceed the range. */ if (pDataSourceBase->loopEndInFrames > (pDataSourceBase->rangeEndInFrames - pDataSourceBase->rangeBegInFrames) && pDataSourceBase->loopEndInFrames != ~((ma_uint64)0)) { pDataSourceBase->loopEndInFrames = (pDataSourceBase->rangeEndInFrames - pDataSourceBase->rangeBegInFrames); } return MA_SUCCESS; } MA_API void ma_data_source_get_loop_point_in_pcm_frames(const ma_data_source* pDataSource, ma_uint64* pLoopBegInFrames, ma_uint64* pLoopEndInFrames) { const ma_data_source_base* pDataSourceBase = (const ma_data_source_base*)pDataSource; if (pDataSource == NULL) { return; } if (pLoopBegInFrames != NULL) { *pLoopBegInFrames = pDataSourceBase->loopBegInFrames; } if (pLoopEndInFrames != NULL) { *pLoopEndInFrames = pDataSourceBase->loopEndInFrames; } } MA_API ma_result ma_data_source_set_current(ma_data_source* pDataSource, ma_data_source* pCurrentDataSource) { ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource; if (pDataSource == NULL) { return MA_INVALID_ARGS; } pDataSourceBase->pCurrent = pCurrentDataSource; return MA_SUCCESS; } MA_API ma_data_source* ma_data_source_get_current(const ma_data_source* pDataSource) { const ma_data_source_base* pDataSourceBase = (const ma_data_source_base*)pDataSource; if (pDataSource == NULL) { return NULL; } return pDataSourceBase->pCurrent; } MA_API ma_result ma_data_source_set_next(ma_data_source* pDataSource, ma_data_source* pNextDataSource) { ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource; if (pDataSource == NULL) { return MA_INVALID_ARGS; } pDataSourceBase->pNext = pNextDataSource; return MA_SUCCESS; } MA_API ma_data_source* ma_data_source_get_next(const ma_data_source* pDataSource) { const ma_data_source_base* pDataSourceBase = (const ma_data_source_base*)pDataSource; if (pDataSource == NULL) { return NULL; } return pDataSourceBase->pNext; } MA_API ma_result ma_data_source_set_next_callback(ma_data_source* pDataSource, ma_data_source_get_next_proc onGetNext) { ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource; if (pDataSource == NULL) { return MA_INVALID_ARGS; } pDataSourceBase->onGetNext = onGetNext; return MA_SUCCESS; } MA_API ma_data_source_get_next_proc ma_data_source_get_next_callback(const ma_data_source* pDataSource) { const ma_data_source_base* pDataSourceBase = (const ma_data_source_base*)pDataSource; if (pDataSource == NULL) { return NULL; } return pDataSourceBase->onGetNext; } static ma_result ma_audio_buffer_ref__data_source_on_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) { ma_audio_buffer_ref* pAudioBufferRef = (ma_audio_buffer_ref*)pDataSource; ma_uint64 framesRead = ma_audio_buffer_ref_read_pcm_frames(pAudioBufferRef, pFramesOut, frameCount, MA_FALSE); if (pFramesRead != NULL) { *pFramesRead = framesRead; } if (framesRead < frameCount || framesRead == 0) { return MA_AT_END; } return MA_SUCCESS; } static ma_result ma_audio_buffer_ref__data_source_on_seek(ma_data_source* pDataSource, ma_uint64 frameIndex) { return ma_audio_buffer_ref_seek_to_pcm_frame((ma_audio_buffer_ref*)pDataSource, frameIndex); } static ma_result ma_audio_buffer_ref__data_source_on_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) { ma_audio_buffer_ref* pAudioBufferRef = (ma_audio_buffer_ref*)pDataSource; *pFormat = pAudioBufferRef->format; *pChannels = pAudioBufferRef->channels; *pSampleRate = pAudioBufferRef->sampleRate; ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, pAudioBufferRef->channels); return MA_SUCCESS; } static ma_result ma_audio_buffer_ref__data_source_on_get_cursor(ma_data_source* pDataSource, ma_uint64* pCursor) { ma_audio_buffer_ref* pAudioBufferRef = (ma_audio_buffer_ref*)pDataSource; *pCursor = pAudioBufferRef->cursor; return MA_SUCCESS; } static ma_result ma_audio_buffer_ref__data_source_on_get_length(ma_data_source* pDataSource, ma_uint64* pLength) { ma_audio_buffer_ref* pAudioBufferRef = (ma_audio_buffer_ref*)pDataSource; *pLength = pAudioBufferRef->sizeInFrames; return MA_SUCCESS; } static ma_data_source_vtable g_ma_audio_buffer_ref_data_source_vtable = { ma_audio_buffer_ref__data_source_on_read, ma_audio_buffer_ref__data_source_on_seek, ma_audio_buffer_ref__data_source_on_get_data_format, ma_audio_buffer_ref__data_source_on_get_cursor, ma_audio_buffer_ref__data_source_on_get_length, NULL, /* onSetLooping */ 0 }; MA_API ma_result ma_audio_buffer_ref_init(ma_format format, ma_uint32 channels, const void* pData, ma_uint64 sizeInFrames, ma_audio_buffer_ref* pAudioBufferRef) { ma_result result; ma_data_source_config dataSourceConfig; if (pAudioBufferRef == NULL) { return MA_INVALID_ARGS; } MA_ZERO_OBJECT(pAudioBufferRef); dataSourceConfig = ma_data_source_config_init(); dataSourceConfig.vtable = &g_ma_audio_buffer_ref_data_source_vtable; result = ma_data_source_init(&dataSourceConfig, &pAudioBufferRef->ds); if (result != MA_SUCCESS) { return result; } pAudioBufferRef->format = format; pAudioBufferRef->channels = channels; pAudioBufferRef->sampleRate = 0; /* TODO: Version 0.12. Set this to sampleRate. */ pAudioBufferRef->cursor = 0; pAudioBufferRef->sizeInFrames = sizeInFrames; pAudioBufferRef->pData = pData; return MA_SUCCESS; } MA_API void ma_audio_buffer_ref_uninit(ma_audio_buffer_ref* pAudioBufferRef) { if (pAudioBufferRef == NULL) { return; } ma_data_source_uninit(&pAudioBufferRef->ds); } MA_API ma_result ma_audio_buffer_ref_set_data(ma_audio_buffer_ref* pAudioBufferRef, const void* pData, ma_uint64 sizeInFrames) { if (pAudioBufferRef == NULL) { return MA_INVALID_ARGS; } pAudioBufferRef->cursor = 0; pAudioBufferRef->sizeInFrames = sizeInFrames; pAudioBufferRef->pData = pData; return MA_SUCCESS; } MA_API ma_uint64 ma_audio_buffer_ref_read_pcm_frames(ma_audio_buffer_ref* pAudioBufferRef, void* pFramesOut, ma_uint64 frameCount, ma_bool32 loop) { ma_uint64 totalFramesRead = 0; if (pAudioBufferRef == NULL) { return 0; } if (frameCount == 0) { return 0; } while (totalFramesRead < frameCount) { ma_uint64 framesAvailable = pAudioBufferRef->sizeInFrames - pAudioBufferRef->cursor; ma_uint64 framesRemaining = frameCount - totalFramesRead; ma_uint64 framesToRead; framesToRead = framesRemaining; if (framesToRead > framesAvailable) { framesToRead = framesAvailable; } if (pFramesOut != NULL) { ma_copy_pcm_frames(ma_offset_ptr(pFramesOut, totalFramesRead * ma_get_bytes_per_frame(pAudioBufferRef->format, pAudioBufferRef->channels)), ma_offset_ptr(pAudioBufferRef->pData, pAudioBufferRef->cursor * ma_get_bytes_per_frame(pAudioBufferRef->format, pAudioBufferRef->channels)), framesToRead, pAudioBufferRef->format, pAudioBufferRef->channels); } totalFramesRead += framesToRead; pAudioBufferRef->cursor += framesToRead; if (pAudioBufferRef->cursor == pAudioBufferRef->sizeInFrames) { if (loop) { pAudioBufferRef->cursor = 0; } else { break; /* We've reached the end and we're not looping. Done. */ } } MA_ASSERT(pAudioBufferRef->cursor < pAudioBufferRef->sizeInFrames); } return totalFramesRead; } MA_API ma_result ma_audio_buffer_ref_seek_to_pcm_frame(ma_audio_buffer_ref* pAudioBufferRef, ma_uint64 frameIndex) { if (pAudioBufferRef == NULL) { return MA_INVALID_ARGS; } if (frameIndex > pAudioBufferRef->sizeInFrames) { return MA_INVALID_ARGS; } pAudioBufferRef->cursor = (size_t)frameIndex; return MA_SUCCESS; } MA_API ma_result ma_audio_buffer_ref_map(ma_audio_buffer_ref* pAudioBufferRef, void** ppFramesOut, ma_uint64* pFrameCount) { ma_uint64 framesAvailable; ma_uint64 frameCount = 0; if (ppFramesOut != NULL) { *ppFramesOut = NULL; /* Safety. */ } if (pFrameCount != NULL) { frameCount = *pFrameCount; *pFrameCount = 0; /* Safety. */ } if (pAudioBufferRef == NULL || ppFramesOut == NULL || pFrameCount == NULL) { return MA_INVALID_ARGS; } framesAvailable = pAudioBufferRef->sizeInFrames - pAudioBufferRef->cursor; if (frameCount > framesAvailable) { frameCount = framesAvailable; } *ppFramesOut = ma_offset_ptr(pAudioBufferRef->pData, pAudioBufferRef->cursor * ma_get_bytes_per_frame(pAudioBufferRef->format, pAudioBufferRef->channels)); *pFrameCount = frameCount; return MA_SUCCESS; } MA_API ma_result ma_audio_buffer_ref_unmap(ma_audio_buffer_ref* pAudioBufferRef, ma_uint64 frameCount) { ma_uint64 framesAvailable; if (pAudioBufferRef == NULL) { return MA_INVALID_ARGS; } framesAvailable = pAudioBufferRef->sizeInFrames - pAudioBufferRef->cursor; if (frameCount > framesAvailable) { return MA_INVALID_ARGS; /* The frame count was too big. This should never happen in an unmapping. Need to make sure the caller is aware of this. */ } pAudioBufferRef->cursor += frameCount; if (pAudioBufferRef->cursor == pAudioBufferRef->sizeInFrames) { return MA_AT_END; /* Successful. Need to tell the caller that the end has been reached so that it can loop if desired. */ } else { return MA_SUCCESS; } } MA_API ma_bool32 ma_audio_buffer_ref_at_end(const ma_audio_buffer_ref* pAudioBufferRef) { if (pAudioBufferRef == NULL) { return MA_FALSE; } return pAudioBufferRef->cursor == pAudioBufferRef->sizeInFrames; } MA_API ma_result ma_audio_buffer_ref_get_cursor_in_pcm_frames(const ma_audio_buffer_ref* pAudioBufferRef, ma_uint64* pCursor) { if (pCursor == NULL) { return MA_INVALID_ARGS; } *pCursor = 0; if (pAudioBufferRef == NULL) { return MA_INVALID_ARGS; } *pCursor = pAudioBufferRef->cursor; return MA_SUCCESS; } MA_API ma_result ma_audio_buffer_ref_get_length_in_pcm_frames(const ma_audio_buffer_ref* pAudioBufferRef, ma_uint64* pLength) { if (pLength == NULL) { return MA_INVALID_ARGS; } *pLength = 0; if (pAudioBufferRef == NULL) { return MA_INVALID_ARGS; } *pLength = pAudioBufferRef->sizeInFrames; return MA_SUCCESS; } MA_API ma_result ma_audio_buffer_ref_get_available_frames(const ma_audio_buffer_ref* pAudioBufferRef, ma_uint64* pAvailableFrames) { if (pAvailableFrames == NULL) { return MA_INVALID_ARGS; } *pAvailableFrames = 0; if (pAudioBufferRef == NULL) { return MA_INVALID_ARGS; } if (pAudioBufferRef->sizeInFrames <= pAudioBufferRef->cursor) { *pAvailableFrames = 0; } else { *pAvailableFrames = pAudioBufferRef->sizeInFrames - pAudioBufferRef->cursor; } return MA_SUCCESS; } MA_API ma_audio_buffer_config ma_audio_buffer_config_init(ma_format format, ma_uint32 channels, ma_uint64 sizeInFrames, const void* pData, const ma_allocation_callbacks* pAllocationCallbacks) { ma_audio_buffer_config config; MA_ZERO_OBJECT(&config); config.format = format; config.channels = channels; config.sampleRate = 0; /* TODO: Version 0.12. Set this to sampleRate. */ config.sizeInFrames = sizeInFrames; config.pData = pData; ma_allocation_callbacks_init_copy(&config.allocationCallbacks, pAllocationCallbacks); return config; } static ma_result ma_audio_buffer_init_ex(const ma_audio_buffer_config* pConfig, ma_bool32 doCopy, ma_audio_buffer* pAudioBuffer) { ma_result result; if (pAudioBuffer == NULL) { return MA_INVALID_ARGS; } MA_ZERO_MEMORY(pAudioBuffer, sizeof(*pAudioBuffer) - sizeof(pAudioBuffer->_pExtraData)); /* Safety. Don't overwrite the extra data. */ if (pConfig == NULL) { return MA_INVALID_ARGS; } if (pConfig->sizeInFrames == 0) { return MA_INVALID_ARGS; /* Not allowing buffer sizes of 0 frames. */ } result = ma_audio_buffer_ref_init(pConfig->format, pConfig->channels, NULL, 0, &pAudioBuffer->ref); if (result != MA_SUCCESS) { return result; } /* TODO: Version 0.12. Set this in ma_audio_buffer_ref_init() instead of here. */ pAudioBuffer->ref.sampleRate = pConfig->sampleRate; ma_allocation_callbacks_init_copy(&pAudioBuffer->allocationCallbacks, &pConfig->allocationCallbacks); if (doCopy) { ma_uint64 allocationSizeInBytes; void* pData; allocationSizeInBytes = pConfig->sizeInFrames * ma_get_bytes_per_frame(pConfig->format, pConfig->channels); if (allocationSizeInBytes > MA_SIZE_MAX) { return MA_OUT_OF_MEMORY; /* Too big. */ } pData = ma_malloc((size_t)allocationSizeInBytes, &pAudioBuffer->allocationCallbacks); /* Safe cast to size_t. */ if (pData == NULL) { return MA_OUT_OF_MEMORY; } if (pConfig->pData != NULL) { ma_copy_pcm_frames(pData, pConfig->pData, pConfig->sizeInFrames, pConfig->format, pConfig->channels); } else { ma_silence_pcm_frames(pData, pConfig->sizeInFrames, pConfig->format, pConfig->channels); } ma_audio_buffer_ref_set_data(&pAudioBuffer->ref, pData, pConfig->sizeInFrames); pAudioBuffer->ownsData = MA_TRUE; } else { ma_audio_buffer_ref_set_data(&pAudioBuffer->ref, pConfig->pData, pConfig->sizeInFrames); pAudioBuffer->ownsData = MA_FALSE; } return MA_SUCCESS; } static void ma_audio_buffer_uninit_ex(ma_audio_buffer* pAudioBuffer, ma_bool32 doFree) { if (pAudioBuffer == NULL) { return; } if (pAudioBuffer->ownsData && pAudioBuffer->ref.pData != &pAudioBuffer->_pExtraData[0]) { ma_free((void*)pAudioBuffer->ref.pData, &pAudioBuffer->allocationCallbacks); /* Naugty const cast, but OK in this case since we've guarded it with the ownsData check. */ } if (doFree) { ma_free(pAudioBuffer, &pAudioBuffer->allocationCallbacks); } ma_audio_buffer_ref_uninit(&pAudioBuffer->ref); } MA_API ma_result ma_audio_buffer_init(const ma_audio_buffer_config* pConfig, ma_audio_buffer* pAudioBuffer) { return ma_audio_buffer_init_ex(pConfig, MA_FALSE, pAudioBuffer); } MA_API ma_result ma_audio_buffer_init_copy(const ma_audio_buffer_config* pConfig, ma_audio_buffer* pAudioBuffer) { return ma_audio_buffer_init_ex(pConfig, MA_TRUE, pAudioBuffer); } MA_API ma_result ma_audio_buffer_alloc_and_init(const ma_audio_buffer_config* pConfig, ma_audio_buffer** ppAudioBuffer) { ma_result result; ma_audio_buffer* pAudioBuffer; ma_audio_buffer_config innerConfig; /* We'll be making some changes to the config, so need to make a copy. */ ma_uint64 allocationSizeInBytes; if (ppAudioBuffer == NULL) { return MA_INVALID_ARGS; } *ppAudioBuffer = NULL; /* Safety. */ if (pConfig == NULL) { return MA_INVALID_ARGS; } innerConfig = *pConfig; ma_allocation_callbacks_init_copy(&innerConfig.allocationCallbacks, &pConfig->allocationCallbacks); allocationSizeInBytes = sizeof(*pAudioBuffer) - sizeof(pAudioBuffer->_pExtraData) + (pConfig->sizeInFrames * ma_get_bytes_per_frame(pConfig->format, pConfig->channels)); if (allocationSizeInBytes > MA_SIZE_MAX) { return MA_OUT_OF_MEMORY; /* Too big. */ } pAudioBuffer = (ma_audio_buffer*)ma_malloc((size_t)allocationSizeInBytes, &innerConfig.allocationCallbacks); /* Safe cast to size_t. */ if (pAudioBuffer == NULL) { return MA_OUT_OF_MEMORY; } if (pConfig->pData != NULL) { ma_copy_pcm_frames(&pAudioBuffer->_pExtraData[0], pConfig->pData, pConfig->sizeInFrames, pConfig->format, pConfig->channels); } else { ma_silence_pcm_frames(&pAudioBuffer->_pExtraData[0], pConfig->sizeInFrames, pConfig->format, pConfig->channels); } innerConfig.pData = &pAudioBuffer->_pExtraData[0]; result = ma_audio_buffer_init_ex(&innerConfig, MA_FALSE, pAudioBuffer); if (result != MA_SUCCESS) { ma_free(pAudioBuffer, &innerConfig.allocationCallbacks); return result; } *ppAudioBuffer = pAudioBuffer; return MA_SUCCESS; } MA_API void ma_audio_buffer_uninit(ma_audio_buffer* pAudioBuffer) { ma_audio_buffer_uninit_ex(pAudioBuffer, MA_FALSE); } MA_API void ma_audio_buffer_uninit_and_free(ma_audio_buffer* pAudioBuffer) { ma_audio_buffer_uninit_ex(pAudioBuffer, MA_TRUE); } MA_API ma_uint64 ma_audio_buffer_read_pcm_frames(ma_audio_buffer* pAudioBuffer, void* pFramesOut, ma_uint64 frameCount, ma_bool32 loop) { if (pAudioBuffer == NULL) { return 0; } return ma_audio_buffer_ref_read_pcm_frames(&pAudioBuffer->ref, pFramesOut, frameCount, loop); } MA_API ma_result ma_audio_buffer_seek_to_pcm_frame(ma_audio_buffer* pAudioBuffer, ma_uint64 frameIndex) { if (pAudioBuffer == NULL) { return MA_INVALID_ARGS; } return ma_audio_buffer_ref_seek_to_pcm_frame(&pAudioBuffer->ref, frameIndex); } MA_API ma_result ma_audio_buffer_map(ma_audio_buffer* pAudioBuffer, void** ppFramesOut, ma_uint64* pFrameCount) { if (ppFramesOut != NULL) { *ppFramesOut = NULL; /* Safety. */ } if (pAudioBuffer == NULL) { if (pFrameCount != NULL) { *pFrameCount = 0; } return MA_INVALID_ARGS; } return ma_audio_buffer_ref_map(&pAudioBuffer->ref, ppFramesOut, pFrameCount); } MA_API ma_result ma_audio_buffer_unmap(ma_audio_buffer* pAudioBuffer, ma_uint64 frameCount) { if (pAudioBuffer == NULL) { return MA_INVALID_ARGS; } return ma_audio_buffer_ref_unmap(&pAudioBuffer->ref, frameCount); } MA_API ma_bool32 ma_audio_buffer_at_end(const ma_audio_buffer* pAudioBuffer) { if (pAudioBuffer == NULL) { return MA_FALSE; } return ma_audio_buffer_ref_at_end(&pAudioBuffer->ref); } MA_API ma_result ma_audio_buffer_get_cursor_in_pcm_frames(const ma_audio_buffer* pAudioBuffer, ma_uint64* pCursor) { if (pAudioBuffer == NULL) { return MA_INVALID_ARGS; } return ma_audio_buffer_ref_get_cursor_in_pcm_frames(&pAudioBuffer->ref, pCursor); } MA_API ma_result ma_audio_buffer_get_length_in_pcm_frames(const ma_audio_buffer* pAudioBuffer, ma_uint64* pLength) { if (pAudioBuffer == NULL) { return MA_INVALID_ARGS; } return ma_audio_buffer_ref_get_length_in_pcm_frames(&pAudioBuffer->ref, pLength); } MA_API ma_result ma_audio_buffer_get_available_frames(const ma_audio_buffer* pAudioBuffer, ma_uint64* pAvailableFrames) { if (pAvailableFrames == NULL) { return MA_INVALID_ARGS; } *pAvailableFrames = 0; if (pAudioBuffer == NULL) { return MA_INVALID_ARGS; } return ma_audio_buffer_ref_get_available_frames(&pAudioBuffer->ref, pAvailableFrames); } MA_API ma_result ma_paged_audio_buffer_data_init(ma_format format, ma_uint32 channels, ma_paged_audio_buffer_data* pData) { if (pData == NULL) { return MA_INVALID_ARGS; } MA_ZERO_OBJECT(pData); pData->format = format; pData->channels = channels; pData->pTail = &pData->head; return MA_SUCCESS; } MA_API void ma_paged_audio_buffer_data_uninit(ma_paged_audio_buffer_data* pData, const ma_allocation_callbacks* pAllocationCallbacks) { ma_paged_audio_buffer_page* pPage; if (pData == NULL) { return; } /* All pages need to be freed. */ pPage = (ma_paged_audio_buffer_page*)c89atomic_load_ptr(&pData->head.pNext); while (pPage != NULL) { ma_paged_audio_buffer_page* pNext = (ma_paged_audio_buffer_page*)c89atomic_load_ptr(&pPage->pNext); ma_free(pPage, pAllocationCallbacks); pPage = pNext; } } MA_API ma_paged_audio_buffer_page* ma_paged_audio_buffer_data_get_head(ma_paged_audio_buffer_data* pData) { if (pData == NULL) { return NULL; } return &pData->head; } MA_API ma_paged_audio_buffer_page* ma_paged_audio_buffer_data_get_tail(ma_paged_audio_buffer_data* pData) { if (pData == NULL) { return NULL; } return pData->pTail; } MA_API ma_result ma_paged_audio_buffer_data_get_length_in_pcm_frames(ma_paged_audio_buffer_data* pData, ma_uint64* pLength) { ma_paged_audio_buffer_page* pPage; if (pLength == NULL) { return MA_INVALID_ARGS; } *pLength = 0; if (pData == NULL) { return MA_INVALID_ARGS; } /* Calculate the length from the linked list. */ for (pPage = (ma_paged_audio_buffer_page*)c89atomic_load_ptr(&pData->head.pNext); pPage != NULL; pPage = (ma_paged_audio_buffer_page*)c89atomic_load_ptr(&pPage->pNext)) { *pLength += pPage->sizeInFrames; } return MA_SUCCESS; } MA_API ma_result ma_paged_audio_buffer_data_allocate_page(ma_paged_audio_buffer_data* pData, ma_uint64 pageSizeInFrames, const void* pInitialData, const ma_allocation_callbacks* pAllocationCallbacks, ma_paged_audio_buffer_page** ppPage) { ma_paged_audio_buffer_page* pPage; ma_uint64 allocationSize; if (ppPage == NULL) { return MA_INVALID_ARGS; } *ppPage = NULL; if (pData == NULL) { return MA_INVALID_ARGS; } allocationSize = sizeof(*pPage) + (pageSizeInFrames * ma_get_bytes_per_frame(pData->format, pData->channels)); if (allocationSize > MA_SIZE_MAX) { return MA_OUT_OF_MEMORY; /* Too big. */ } pPage = (ma_paged_audio_buffer_page*)ma_malloc((size_t)allocationSize, pAllocationCallbacks); /* Safe cast to size_t. */ if (pPage == NULL) { return MA_OUT_OF_MEMORY; } pPage->pNext = NULL; pPage->sizeInFrames = pageSizeInFrames; if (pInitialData != NULL) { ma_copy_pcm_frames(pPage->pAudioData, pInitialData, pageSizeInFrames, pData->format, pData->channels); } *ppPage = pPage; return MA_SUCCESS; } MA_API ma_result ma_paged_audio_buffer_data_free_page(ma_paged_audio_buffer_data* pData, ma_paged_audio_buffer_page* pPage, const ma_allocation_callbacks* pAllocationCallbacks) { if (pData == NULL || pPage == NULL) { return MA_INVALID_ARGS; } /* It's assumed the page is not attached to the list. */ ma_free(pPage, pAllocationCallbacks); return MA_SUCCESS; } MA_API ma_result ma_paged_audio_buffer_data_append_page(ma_paged_audio_buffer_data* pData, ma_paged_audio_buffer_page* pPage) { if (pData == NULL || pPage == NULL) { return MA_INVALID_ARGS; } /* This function assumes the page has been filled with audio data by this point. As soon as we append, the page will be available for reading. */ /* First thing to do is update the tail. */ for (;;) { ma_paged_audio_buffer_page* pOldTail = (ma_paged_audio_buffer_page*)c89atomic_load_ptr(&pData->pTail); ma_paged_audio_buffer_page* pNewTail = pPage; if (c89atomic_compare_exchange_weak_ptr((volatile void**)&pData->pTail, (void**)&pOldTail, pNewTail)) { /* Here is where we append the page to the list. After this, the page is attached to the list and ready to be read from. */ c89atomic_exchange_ptr(&pOldTail->pNext, pPage); break; /* Done. */ } } return MA_SUCCESS; } MA_API ma_result ma_paged_audio_buffer_data_allocate_and_append_page(ma_paged_audio_buffer_data* pData, ma_uint32 pageSizeInFrames, const void* pInitialData, const ma_allocation_callbacks* pAllocationCallbacks) { ma_result result; ma_paged_audio_buffer_page* pPage; result = ma_paged_audio_buffer_data_allocate_page(pData, pageSizeInFrames, pInitialData, pAllocationCallbacks, &pPage); if (result != MA_SUCCESS) { return result; } return ma_paged_audio_buffer_data_append_page(pData, pPage); /* <-- Should never fail. */ } MA_API ma_paged_audio_buffer_config ma_paged_audio_buffer_config_init(ma_paged_audio_buffer_data* pData) { ma_paged_audio_buffer_config config; MA_ZERO_OBJECT(&config); config.pData = pData; return config; } static ma_result ma_paged_audio_buffer__data_source_on_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) { return ma_paged_audio_buffer_read_pcm_frames((ma_paged_audio_buffer*)pDataSource, pFramesOut, frameCount, pFramesRead); } static ma_result ma_paged_audio_buffer__data_source_on_seek(ma_data_source* pDataSource, ma_uint64 frameIndex) { return ma_paged_audio_buffer_seek_to_pcm_frame((ma_paged_audio_buffer*)pDataSource, frameIndex); } static ma_result ma_paged_audio_buffer__data_source_on_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) { ma_paged_audio_buffer* pPagedAudioBuffer = (ma_paged_audio_buffer*)pDataSource; *pFormat = pPagedAudioBuffer->pData->format; *pChannels = pPagedAudioBuffer->pData->channels; *pSampleRate = 0; /* There is no notion of a sample rate with audio buffers. */ ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, pPagedAudioBuffer->pData->channels); return MA_SUCCESS; } static ma_result ma_paged_audio_buffer__data_source_on_get_cursor(ma_data_source* pDataSource, ma_uint64* pCursor) { return ma_paged_audio_buffer_get_cursor_in_pcm_frames((ma_paged_audio_buffer*)pDataSource, pCursor); } static ma_result ma_paged_audio_buffer__data_source_on_get_length(ma_data_source* pDataSource, ma_uint64* pLength) { return ma_paged_audio_buffer_get_length_in_pcm_frames((ma_paged_audio_buffer*)pDataSource, pLength); } static ma_data_source_vtable g_ma_paged_audio_buffer_data_source_vtable = { ma_paged_audio_buffer__data_source_on_read, ma_paged_audio_buffer__data_source_on_seek, ma_paged_audio_buffer__data_source_on_get_data_format, ma_paged_audio_buffer__data_source_on_get_cursor, ma_paged_audio_buffer__data_source_on_get_length, NULL, /* onSetLooping */ 0 }; MA_API ma_result ma_paged_audio_buffer_init(const ma_paged_audio_buffer_config* pConfig, ma_paged_audio_buffer* pPagedAudioBuffer) { ma_result result; ma_data_source_config dataSourceConfig; if (pPagedAudioBuffer == NULL) { return MA_INVALID_ARGS; } MA_ZERO_OBJECT(pPagedAudioBuffer); /* A config is required for the format and channel count. */ if (pConfig == NULL) { return MA_INVALID_ARGS; } if (pConfig->pData == NULL) { return MA_INVALID_ARGS; /* No underlying data specified. */ } dataSourceConfig = ma_data_source_config_init(); dataSourceConfig.vtable = &g_ma_paged_audio_buffer_data_source_vtable; result = ma_data_source_init(&dataSourceConfig, &pPagedAudioBuffer->ds); if (result != MA_SUCCESS) { return result; } pPagedAudioBuffer->pData = pConfig->pData; pPagedAudioBuffer->pCurrent = ma_paged_audio_buffer_data_get_head(pConfig->pData); pPagedAudioBuffer->relativeCursor = 0; pPagedAudioBuffer->absoluteCursor = 0; return MA_SUCCESS; } MA_API void ma_paged_audio_buffer_uninit(ma_paged_audio_buffer* pPagedAudioBuffer) { if (pPagedAudioBuffer == NULL) { return; } /* Nothing to do. The data needs to be deleted separately. */ } MA_API ma_result ma_paged_audio_buffer_read_pcm_frames(ma_paged_audio_buffer* pPagedAudioBuffer, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) { ma_result result = MA_SUCCESS; ma_uint64 totalFramesRead = 0; ma_format format; ma_uint32 channels; if (pPagedAudioBuffer == NULL) { return MA_INVALID_ARGS; } format = pPagedAudioBuffer->pData->format; channels = pPagedAudioBuffer->pData->channels; while (totalFramesRead < frameCount) { /* Read from the current page. The buffer should never be in a state where this is NULL. */ ma_uint64 framesRemainingInCurrentPage; ma_uint64 framesRemainingToRead = frameCount - totalFramesRead; ma_uint64 framesToReadThisIteration; MA_ASSERT(pPagedAudioBuffer->pCurrent != NULL); framesRemainingInCurrentPage = pPagedAudioBuffer->pCurrent->sizeInFrames - pPagedAudioBuffer->relativeCursor; framesToReadThisIteration = ma_min(framesRemainingInCurrentPage, framesRemainingToRead); ma_copy_pcm_frames(ma_offset_pcm_frames_ptr(pFramesOut, totalFramesRead, format, channels), ma_offset_pcm_frames_ptr(pPagedAudioBuffer->pCurrent->pAudioData, pPagedAudioBuffer->relativeCursor, format, channels), framesToReadThisIteration, format, channels); totalFramesRead += framesToReadThisIteration; pPagedAudioBuffer->absoluteCursor += framesToReadThisIteration; pPagedAudioBuffer->relativeCursor += framesToReadThisIteration; /* Move to the next page if necessary. If there's no more pages, we need to return MA_AT_END. */ MA_ASSERT(pPagedAudioBuffer->relativeCursor <= pPagedAudioBuffer->pCurrent->sizeInFrames); if (pPagedAudioBuffer->relativeCursor == pPagedAudioBuffer->pCurrent->sizeInFrames) { /* We reached the end of the page. Need to move to the next. If there's no more pages, we're done. */ ma_paged_audio_buffer_page* pNext = (ma_paged_audio_buffer_page*)c89atomic_load_ptr(&pPagedAudioBuffer->pCurrent->pNext); if (pNext == NULL) { result = MA_AT_END; break; /* We've reached the end. */ } else { pPagedAudioBuffer->pCurrent = pNext; pPagedAudioBuffer->relativeCursor = 0; } } } if (pFramesRead != NULL) { *pFramesRead = totalFramesRead; } return result; } MA_API ma_result ma_paged_audio_buffer_seek_to_pcm_frame(ma_paged_audio_buffer* pPagedAudioBuffer, ma_uint64 frameIndex) { if (pPagedAudioBuffer == NULL) { return MA_INVALID_ARGS; } if (frameIndex == pPagedAudioBuffer->absoluteCursor) { return MA_SUCCESS; /* Nothing to do. */ } if (frameIndex < pPagedAudioBuffer->absoluteCursor) { /* Moving backwards. Need to move the cursor back to the start, and then move forward. */ pPagedAudioBuffer->pCurrent = ma_paged_audio_buffer_data_get_head(pPagedAudioBuffer->pData); pPagedAudioBuffer->absoluteCursor = 0; pPagedAudioBuffer->relativeCursor = 0; /* Fall through to the forward seeking section below. */ } if (frameIndex > pPagedAudioBuffer->absoluteCursor) { /* Moving forward. */ ma_paged_audio_buffer_page* pPage; ma_uint64 runningCursor = 0; for (pPage = (ma_paged_audio_buffer_page*)c89atomic_load_ptr(&ma_paged_audio_buffer_data_get_head(pPagedAudioBuffer->pData)->pNext); pPage != NULL; pPage = (ma_paged_audio_buffer_page*)c89atomic_load_ptr(&pPage->pNext)) { ma_uint64 pageRangeBeg = runningCursor; ma_uint64 pageRangeEnd = pageRangeBeg + pPage->sizeInFrames; if (frameIndex >= pageRangeBeg) { if (frameIndex < pageRangeEnd || (frameIndex == pageRangeEnd && pPage == (ma_paged_audio_buffer_page*)c89atomic_load_ptr(ma_paged_audio_buffer_data_get_tail(pPagedAudioBuffer->pData)))) { /* A small edge case - allow seeking to the very end of the buffer. */ /* We found the page. */ pPagedAudioBuffer->pCurrent = pPage; pPagedAudioBuffer->absoluteCursor = frameIndex; pPagedAudioBuffer->relativeCursor = frameIndex - pageRangeBeg; return MA_SUCCESS; } } runningCursor = pageRangeEnd; } /* Getting here means we tried seeking too far forward. Don't change any state. */ return MA_BAD_SEEK; } return MA_SUCCESS; } MA_API ma_result ma_paged_audio_buffer_get_cursor_in_pcm_frames(ma_paged_audio_buffer* pPagedAudioBuffer, ma_uint64* pCursor) { if (pCursor == NULL) { return MA_INVALID_ARGS; } *pCursor = 0; /* Safety. */ if (pPagedAudioBuffer == NULL) { return MA_INVALID_ARGS; } *pCursor = pPagedAudioBuffer->absoluteCursor; return MA_SUCCESS; } MA_API ma_result ma_paged_audio_buffer_get_length_in_pcm_frames(ma_paged_audio_buffer* pPagedAudioBuffer, ma_uint64* pLength) { return ma_paged_audio_buffer_data_get_length_in_pcm_frames(pPagedAudioBuffer->pData, pLength); } /************************************************************************************************************************************************************** VFS **************************************************************************************************************************************************************/ MA_API ma_result ma_vfs_open(ma_vfs* pVFS, const char* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile) { ma_vfs_callbacks* pCallbacks = (ma_vfs_callbacks*)pVFS; if (pFile == NULL) { return MA_INVALID_ARGS; } *pFile = NULL; if (pVFS == NULL || pFilePath == NULL || openMode == 0) { return MA_INVALID_ARGS; } if (pCallbacks->onOpen == NULL) { return MA_NOT_IMPLEMENTED; } return pCallbacks->onOpen(pVFS, pFilePath, openMode, pFile); } MA_API ma_result ma_vfs_open_w(ma_vfs* pVFS, const wchar_t* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile) { ma_vfs_callbacks* pCallbacks = (ma_vfs_callbacks*)pVFS; if (pFile == NULL) { return MA_INVALID_ARGS; } *pFile = NULL; if (pVFS == NULL || pFilePath == NULL || openMode == 0) { return MA_INVALID_ARGS; } if (pCallbacks->onOpenW == NULL) { return MA_NOT_IMPLEMENTED; } return pCallbacks->onOpenW(pVFS, pFilePath, openMode, pFile); } MA_API ma_result ma_vfs_close(ma_vfs* pVFS, ma_vfs_file file) { ma_vfs_callbacks* pCallbacks = (ma_vfs_callbacks*)pVFS; if (pVFS == NULL || file == NULL) { return MA_INVALID_ARGS; } if (pCallbacks->onClose == NULL) { return MA_NOT_IMPLEMENTED; } return pCallbacks->onClose(pVFS, file); } MA_API ma_result ma_vfs_read(ma_vfs* pVFS, ma_vfs_file file, void* pDst, size_t sizeInBytes, size_t* pBytesRead) { ma_vfs_callbacks* pCallbacks = (ma_vfs_callbacks*)pVFS; ma_result result; size_t bytesRead; if (pBytesRead != NULL) { *pBytesRead = 0; } if (pVFS == NULL || file == NULL || pDst == NULL) { return MA_INVALID_ARGS; } if (pCallbacks->onRead == NULL) { return MA_NOT_IMPLEMENTED; } result = pCallbacks->onRead(pVFS, file, pDst, sizeInBytes, &bytesRead); if (pBytesRead != NULL) { *pBytesRead = bytesRead; } if (result == MA_SUCCESS && bytesRead == 0 && sizeInBytes > 0) { result = MA_AT_END; } return result; } MA_API ma_result ma_vfs_write(ma_vfs* pVFS, ma_vfs_file file, const void* pSrc, size_t sizeInBytes, size_t* pBytesWritten) { ma_vfs_callbacks* pCallbacks = (ma_vfs_callbacks*)pVFS; if (pBytesWritten != NULL) { *pBytesWritten = 0; } if (pVFS == NULL || file == NULL || pSrc == NULL) { return MA_INVALID_ARGS; } if (pCallbacks->onWrite == NULL) { return MA_NOT_IMPLEMENTED; } return pCallbacks->onWrite(pVFS, file, pSrc, sizeInBytes, pBytesWritten); } MA_API ma_result ma_vfs_seek(ma_vfs* pVFS, ma_vfs_file file, ma_int64 offset, ma_seek_origin origin) { ma_vfs_callbacks* pCallbacks = (ma_vfs_callbacks*)pVFS; if (pVFS == NULL || file == NULL) { return MA_INVALID_ARGS; } if (pCallbacks->onSeek == NULL) { return MA_NOT_IMPLEMENTED; } return pCallbacks->onSeek(pVFS, file, offset, origin); } MA_API ma_result ma_vfs_tell(ma_vfs* pVFS, ma_vfs_file file, ma_int64* pCursor) { ma_vfs_callbacks* pCallbacks = (ma_vfs_callbacks*)pVFS; if (pCursor == NULL) { return MA_INVALID_ARGS; } *pCursor = 0; if (pVFS == NULL || file == NULL) { return MA_INVALID_ARGS; } if (pCallbacks->onTell == NULL) { return MA_NOT_IMPLEMENTED; } return pCallbacks->onTell(pVFS, file, pCursor); } MA_API ma_result ma_vfs_info(ma_vfs* pVFS, ma_vfs_file file, ma_file_info* pInfo) { ma_vfs_callbacks* pCallbacks = (ma_vfs_callbacks*)pVFS; if (pInfo == NULL) { return MA_INVALID_ARGS; } MA_ZERO_OBJECT(pInfo); if (pVFS == NULL || file == NULL) { return MA_INVALID_ARGS; } if (pCallbacks->onInfo == NULL) { return MA_NOT_IMPLEMENTED; } return pCallbacks->onInfo(pVFS, file, pInfo); } static ma_result ma_vfs_open_and_read_file_ex(ma_vfs* pVFS, const char* pFilePath, const wchar_t* pFilePathW, void** ppData, size_t* pSize, const ma_allocation_callbacks* pAllocationCallbacks) { ma_result result; ma_vfs_file file; ma_file_info info; void* pData; size_t bytesRead; if (ppData != NULL) { *ppData = NULL; } if (pSize != NULL) { *pSize = 0; } if (ppData == NULL) { return MA_INVALID_ARGS; } if (pFilePath != NULL) { result = ma_vfs_open(pVFS, pFilePath, MA_OPEN_MODE_READ, &file); } else { result = ma_vfs_open_w(pVFS, pFilePathW, MA_OPEN_MODE_READ, &file); } if (result != MA_SUCCESS) { return result; } result = ma_vfs_info(pVFS, file, &info); if (result != MA_SUCCESS) { ma_vfs_close(pVFS, file); return result; } if (info.sizeInBytes > MA_SIZE_MAX) { ma_vfs_close(pVFS, file); return MA_TOO_BIG; } pData = ma_malloc((size_t)info.sizeInBytes, pAllocationCallbacks); /* Safe cast. */ if (pData == NULL) { ma_vfs_close(pVFS, file); return result; } result = ma_vfs_read(pVFS, file, pData, (size_t)info.sizeInBytes, &bytesRead); /* Safe cast. */ ma_vfs_close(pVFS, file); if (result != MA_SUCCESS) { ma_free(pData, pAllocationCallbacks); return result; } if (pSize != NULL) { *pSize = bytesRead; } MA_ASSERT(ppData != NULL); *ppData = pData; return MA_SUCCESS; } MA_API ma_result ma_vfs_open_and_read_file(ma_vfs* pVFS, const char* pFilePath, void** ppData, size_t* pSize, const ma_allocation_callbacks* pAllocationCallbacks) { return ma_vfs_open_and_read_file_ex(pVFS, pFilePath, NULL, ppData, pSize, pAllocationCallbacks); } MA_API ma_result ma_vfs_open_and_read_file_w(ma_vfs* pVFS, const wchar_t* pFilePath, void** ppData, size_t* pSize, const ma_allocation_callbacks* pAllocationCallbacks) { return ma_vfs_open_and_read_file_ex(pVFS, NULL, pFilePath, ppData, pSize, pAllocationCallbacks); } #if defined(MA_WIN32) && defined(MA_WIN32_DESKTOP) && !defined(MA_NO_WIN32_FILEIO) static void ma_default_vfs__get_open_settings_win32(ma_uint32 openMode, DWORD* pDesiredAccess, DWORD* pShareMode, DWORD* pCreationDisposition) { *pDesiredAccess = 0; if ((openMode & MA_OPEN_MODE_READ) != 0) { *pDesiredAccess |= GENERIC_READ; } if ((openMode & MA_OPEN_MODE_WRITE) != 0) { *pDesiredAccess |= GENERIC_WRITE; } *pShareMode = 0; if ((openMode & MA_OPEN_MODE_READ) != 0) { *pShareMode |= FILE_SHARE_READ; } if ((openMode & MA_OPEN_MODE_WRITE) != 0) { *pCreationDisposition = CREATE_ALWAYS; /* Opening in write mode. Truncate. */ } else { *pCreationDisposition = OPEN_EXISTING; /* Opening in read mode. File must exist. */ } } static ma_result ma_default_vfs_open__win32(ma_vfs* pVFS, const char* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile) { HANDLE hFile; DWORD dwDesiredAccess; DWORD dwShareMode; DWORD dwCreationDisposition; (void)pVFS; ma_default_vfs__get_open_settings_win32(openMode, &dwDesiredAccess, &dwShareMode, &dwCreationDisposition); hFile = CreateFileA(pFilePath, dwDesiredAccess, dwShareMode, NULL, dwCreationDisposition, FILE_ATTRIBUTE_NORMAL, NULL); if (hFile == INVALID_HANDLE_VALUE) { return ma_result_from_GetLastError(GetLastError()); } *pFile = hFile; return MA_SUCCESS; } static ma_result ma_default_vfs_open_w__win32(ma_vfs* pVFS, const wchar_t* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile) { HANDLE hFile; DWORD dwDesiredAccess; DWORD dwShareMode; DWORD dwCreationDisposition; (void)pVFS; ma_default_vfs__get_open_settings_win32(openMode, &dwDesiredAccess, &dwShareMode, &dwCreationDisposition); hFile = CreateFileW(pFilePath, dwDesiredAccess, dwShareMode, NULL, dwCreationDisposition, FILE_ATTRIBUTE_NORMAL, NULL); if (hFile == INVALID_HANDLE_VALUE) { return ma_result_from_GetLastError(GetLastError()); } *pFile = hFile; return MA_SUCCESS; } static ma_result ma_default_vfs_close__win32(ma_vfs* pVFS, ma_vfs_file file) { (void)pVFS; if (CloseHandle((HANDLE)file) == 0) { return ma_result_from_GetLastError(GetLastError()); } return MA_SUCCESS; } static ma_result ma_default_vfs_read__win32(ma_vfs* pVFS, ma_vfs_file file, void* pDst, size_t sizeInBytes, size_t* pBytesRead) { ma_result result = MA_SUCCESS; size_t totalBytesRead; (void)pVFS; totalBytesRead = 0; while (totalBytesRead < sizeInBytes) { size_t bytesRemaining; DWORD bytesToRead; DWORD bytesRead; BOOL readResult; bytesRemaining = sizeInBytes - totalBytesRead; if (bytesRemaining >= 0xFFFFFFFF) { bytesToRead = 0xFFFFFFFF; } else { bytesToRead = (DWORD)bytesRemaining; } readResult = ReadFile((HANDLE)file, ma_offset_ptr(pDst, totalBytesRead), bytesToRead, &bytesRead, NULL); if (readResult == 1 && bytesRead == 0) { result = MA_AT_END; break; /* EOF */ } totalBytesRead += bytesRead; if (bytesRead < bytesToRead) { break; /* EOF */ } if (readResult == 0) { result = ma_result_from_GetLastError(GetLastError()); break; } } if (pBytesRead != NULL) { *pBytesRead = totalBytesRead; } return result; } static ma_result ma_default_vfs_write__win32(ma_vfs* pVFS, ma_vfs_file file, const void* pSrc, size_t sizeInBytes, size_t* pBytesWritten) { ma_result result = MA_SUCCESS; size_t totalBytesWritten; (void)pVFS; totalBytesWritten = 0; while (totalBytesWritten < sizeInBytes) { size_t bytesRemaining; DWORD bytesToWrite; DWORD bytesWritten; BOOL writeResult; bytesRemaining = sizeInBytes - totalBytesWritten; if (bytesRemaining >= 0xFFFFFFFF) { bytesToWrite = 0xFFFFFFFF; } else { bytesToWrite = (DWORD)bytesRemaining; } writeResult = WriteFile((HANDLE)file, ma_offset_ptr(pSrc, totalBytesWritten), bytesToWrite, &bytesWritten, NULL); totalBytesWritten += bytesWritten; if (writeResult == 0) { result = ma_result_from_GetLastError(GetLastError()); break; } } if (pBytesWritten != NULL) { *pBytesWritten = totalBytesWritten; } return result; } static ma_result ma_default_vfs_seek__win32(ma_vfs* pVFS, ma_vfs_file file, ma_int64 offset, ma_seek_origin origin) { LARGE_INTEGER liDistanceToMove; DWORD dwMoveMethod; BOOL result; (void)pVFS; liDistanceToMove.QuadPart = offset; /* */ if (origin == ma_seek_origin_current) { dwMoveMethod = FILE_CURRENT; } else if (origin == ma_seek_origin_end) { dwMoveMethod = FILE_END; } else { dwMoveMethod = FILE_BEGIN; } #if (defined(_MSC_VER) && _MSC_VER <= 1200) || defined(__DMC__) /* No SetFilePointerEx() so restrict to 31 bits. */ if (origin > 0x7FFFFFFF) { return MA_OUT_OF_RANGE; } result = SetFilePointer((HANDLE)file, (LONG)liDistanceToMove.QuadPart, NULL, dwMoveMethod); #else result = SetFilePointerEx((HANDLE)file, liDistanceToMove, NULL, dwMoveMethod); #endif if (result == 0) { return ma_result_from_GetLastError(GetLastError()); } return MA_SUCCESS; } static ma_result ma_default_vfs_tell__win32(ma_vfs* pVFS, ma_vfs_file file, ma_int64* pCursor) { LARGE_INTEGER liZero; LARGE_INTEGER liTell; BOOL result; #if (defined(_MSC_VER) && _MSC_VER <= 1200) || defined(__DMC__) LONG tell; #endif (void)pVFS; liZero.QuadPart = 0; #if (defined(_MSC_VER) && _MSC_VER <= 1200) || defined(__DMC__) result = SetFilePointer((HANDLE)file, (LONG)liZero.QuadPart, &tell, FILE_CURRENT); liTell.QuadPart = tell; #else result = SetFilePointerEx((HANDLE)file, liZero, &liTell, FILE_CURRENT); #endif if (result == 0) { return ma_result_from_GetLastError(GetLastError()); } if (pCursor != NULL) { *pCursor = liTell.QuadPart; } return MA_SUCCESS; } static ma_result ma_default_vfs_info__win32(ma_vfs* pVFS, ma_vfs_file file, ma_file_info* pInfo) { BY_HANDLE_FILE_INFORMATION fi; BOOL result; (void)pVFS; result = GetFileInformationByHandle((HANDLE)file, &fi); if (result == 0) { return ma_result_from_GetLastError(GetLastError()); } pInfo->sizeInBytes = ((ma_uint64)fi.nFileSizeHigh << 32) | ((ma_uint64)fi.nFileSizeLow); return MA_SUCCESS; } #else static ma_result ma_default_vfs_open__stdio(ma_vfs* pVFS, const char* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile) { ma_result result; FILE* pFileStd; const char* pOpenModeStr; MA_ASSERT(pFilePath != NULL); MA_ASSERT(openMode != 0); MA_ASSERT(pFile != NULL); (void)pVFS; if ((openMode & MA_OPEN_MODE_READ) != 0) { if ((openMode & MA_OPEN_MODE_WRITE) != 0) { pOpenModeStr = "r+"; } else { pOpenModeStr = "rb"; } } else { pOpenModeStr = "wb"; } result = ma_fopen(&pFileStd, pFilePath, pOpenModeStr); if (result != MA_SUCCESS) { return result; } *pFile = pFileStd; return MA_SUCCESS; } static ma_result ma_default_vfs_open_w__stdio(ma_vfs* pVFS, const wchar_t* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile) { ma_result result; FILE* pFileStd; const wchar_t* pOpenModeStr; MA_ASSERT(pFilePath != NULL); MA_ASSERT(openMode != 0); MA_ASSERT(pFile != NULL); (void)pVFS; if ((openMode & MA_OPEN_MODE_READ) != 0) { if ((openMode & MA_OPEN_MODE_WRITE) != 0) { pOpenModeStr = L"r+"; } else { pOpenModeStr = L"rb"; } } else { pOpenModeStr = L"wb"; } result = ma_wfopen(&pFileStd, pFilePath, pOpenModeStr, (pVFS != NULL) ? &((ma_default_vfs*)pVFS)->allocationCallbacks : NULL); if (result != MA_SUCCESS) { return result; } *pFile = pFileStd; return MA_SUCCESS; } static ma_result ma_default_vfs_close__stdio(ma_vfs* pVFS, ma_vfs_file file) { MA_ASSERT(file != NULL); (void)pVFS; fclose((FILE*)file); return MA_SUCCESS; } static ma_result ma_default_vfs_read__stdio(ma_vfs* pVFS, ma_vfs_file file, void* pDst, size_t sizeInBytes, size_t* pBytesRead) { size_t result; MA_ASSERT(file != NULL); MA_ASSERT(pDst != NULL); (void)pVFS; result = fread(pDst, 1, sizeInBytes, (FILE*)file); if (pBytesRead != NULL) { *pBytesRead = result; } if (result != sizeInBytes) { if (result == 0 && feof((FILE*)file)) { return MA_AT_END; } else { return ma_result_from_errno(ferror((FILE*)file)); } } return MA_SUCCESS; } static ma_result ma_default_vfs_write__stdio(ma_vfs* pVFS, ma_vfs_file file, const void* pSrc, size_t sizeInBytes, size_t* pBytesWritten) { size_t result; MA_ASSERT(file != NULL); MA_ASSERT(pSrc != NULL); (void)pVFS; result = fwrite(pSrc, 1, sizeInBytes, (FILE*)file); if (pBytesWritten != NULL) { *pBytesWritten = result; } if (result != sizeInBytes) { return ma_result_from_errno(ferror((FILE*)file)); } return MA_SUCCESS; } static ma_result ma_default_vfs_seek__stdio(ma_vfs* pVFS, ma_vfs_file file, ma_int64 offset, ma_seek_origin origin) { int result; int whence; MA_ASSERT(file != NULL); (void)pVFS; if (origin == ma_seek_origin_start) { whence = SEEK_SET; } else if (origin == ma_seek_origin_end) { whence = SEEK_END; } else { whence = SEEK_CUR; } #if defined(_WIN32) #if defined(_MSC_VER) && _MSC_VER > 1200 result = _fseeki64((FILE*)file, offset, whence); #else /* No _fseeki64() so restrict to 31 bits. */ if (origin > 0x7FFFFFFF) { return MA_OUT_OF_RANGE; } result = fseek((FILE*)file, (int)offset, whence); #endif #else result = fseek((FILE*)file, (long int)offset, whence); #endif if (result != 0) { return MA_ERROR; } return MA_SUCCESS; } static ma_result ma_default_vfs_tell__stdio(ma_vfs* pVFS, ma_vfs_file file, ma_int64* pCursor) { ma_int64 result; MA_ASSERT(file != NULL); MA_ASSERT(pCursor != NULL); (void)pVFS; #if defined(_WIN32) #if defined(_MSC_VER) && _MSC_VER > 1200 result = _ftelli64((FILE*)file); #else result = ftell((FILE*)file); #endif #else result = ftell((FILE*)file); #endif *pCursor = result; return MA_SUCCESS; } #if !defined(_MSC_VER) && !((defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 1) || defined(_XOPEN_SOURCE) || defined(_POSIX_SOURCE)) && !defined(MA_BSD) int fileno(FILE *stream); #endif static ma_result ma_default_vfs_info__stdio(ma_vfs* pVFS, ma_vfs_file file, ma_file_info* pInfo) { int fd; struct stat info; MA_ASSERT(file != NULL); MA_ASSERT(pInfo != NULL); (void)pVFS; #if defined(_MSC_VER) fd = _fileno((FILE*)file); #else fd = fileno((FILE*)file); #endif if (fstat(fd, &info) != 0) { return ma_result_from_errno(errno); } pInfo->sizeInBytes = info.st_size; return MA_SUCCESS; } #endif static ma_result ma_default_vfs_open(ma_vfs* pVFS, const char* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile) { if (pFile == NULL) { return MA_INVALID_ARGS; } *pFile = NULL; if (pFilePath == NULL || openMode == 0) { return MA_INVALID_ARGS; } #if defined(MA_WIN32) && defined(MA_WIN32_DESKTOP) && !defined(MA_NO_WIN32_FILEIO) return ma_default_vfs_open__win32(pVFS, pFilePath, openMode, pFile); #else return ma_default_vfs_open__stdio(pVFS, pFilePath, openMode, pFile); #endif } static ma_result ma_default_vfs_open_w(ma_vfs* pVFS, const wchar_t* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile) { if (pFile == NULL) { return MA_INVALID_ARGS; } *pFile = NULL; if (pFilePath == NULL || openMode == 0) { return MA_INVALID_ARGS; } #if defined(MA_WIN32) && defined(MA_WIN32_DESKTOP) && !defined(MA_NO_WIN32_FILEIO) return ma_default_vfs_open_w__win32(pVFS, pFilePath, openMode, pFile); #else return ma_default_vfs_open_w__stdio(pVFS, pFilePath, openMode, pFile); #endif } static ma_result ma_default_vfs_close(ma_vfs* pVFS, ma_vfs_file file) { if (file == NULL) { return MA_INVALID_ARGS; } #if defined(MA_WIN32) && defined(MA_WIN32_DESKTOP) && !defined(MA_NO_WIN32_FILEIO) return ma_default_vfs_close__win32(pVFS, file); #else return ma_default_vfs_close__stdio(pVFS, file); #endif } static ma_result ma_default_vfs_read(ma_vfs* pVFS, ma_vfs_file file, void* pDst, size_t sizeInBytes, size_t* pBytesRead) { if (pBytesRead != NULL) { *pBytesRead = 0; } if (file == NULL || pDst == NULL) { return MA_INVALID_ARGS; } #if defined(MA_WIN32) && defined(MA_WIN32_DESKTOP) && !defined(MA_NO_WIN32_FILEIO) return ma_default_vfs_read__win32(pVFS, file, pDst, sizeInBytes, pBytesRead); #else return ma_default_vfs_read__stdio(pVFS, file, pDst, sizeInBytes, pBytesRead); #endif } static ma_result ma_default_vfs_write(ma_vfs* pVFS, ma_vfs_file file, const void* pSrc, size_t sizeInBytes, size_t* pBytesWritten) { if (pBytesWritten != NULL) { *pBytesWritten = 0; } if (file == NULL || pSrc == NULL) { return MA_INVALID_ARGS; } #if defined(MA_WIN32) && defined(MA_WIN32_DESKTOP) && !defined(MA_NO_WIN32_FILEIO) return ma_default_vfs_write__win32(pVFS, file, pSrc, sizeInBytes, pBytesWritten); #else return ma_default_vfs_write__stdio(pVFS, file, pSrc, sizeInBytes, pBytesWritten); #endif } static ma_result ma_default_vfs_seek(ma_vfs* pVFS, ma_vfs_file file, ma_int64 offset, ma_seek_origin origin) { if (file == NULL) { return MA_INVALID_ARGS; } #if defined(MA_WIN32) && defined(MA_WIN32_DESKTOP) && !defined(MA_NO_WIN32_FILEIO) return ma_default_vfs_seek__win32(pVFS, file, offset, origin); #else return ma_default_vfs_seek__stdio(pVFS, file, offset, origin); #endif } static ma_result ma_default_vfs_tell(ma_vfs* pVFS, ma_vfs_file file, ma_int64* pCursor) { if (pCursor == NULL) { return MA_INVALID_ARGS; } *pCursor = 0; if (file == NULL) { return MA_INVALID_ARGS; } #if defined(MA_WIN32) && defined(MA_WIN32_DESKTOP) && !defined(MA_NO_WIN32_FILEIO) return ma_default_vfs_tell__win32(pVFS, file, pCursor); #else return ma_default_vfs_tell__stdio(pVFS, file, pCursor); #endif } static ma_result ma_default_vfs_info(ma_vfs* pVFS, ma_vfs_file file, ma_file_info* pInfo) { if (pInfo == NULL) { return MA_INVALID_ARGS; } MA_ZERO_OBJECT(pInfo); if (file == NULL) { return MA_INVALID_ARGS; } #if defined(MA_WIN32) && defined(MA_WIN32_DESKTOP) && !defined(MA_NO_WIN32_FILEIO) return ma_default_vfs_info__win32(pVFS, file, pInfo); #else return ma_default_vfs_info__stdio(pVFS, file, pInfo); #endif } MA_API ma_result ma_default_vfs_init(ma_default_vfs* pVFS, const ma_allocation_callbacks* pAllocationCallbacks) { if (pVFS == NULL) { return MA_INVALID_ARGS; } pVFS->cb.onOpen = ma_default_vfs_open; pVFS->cb.onOpenW = ma_default_vfs_open_w; pVFS->cb.onClose = ma_default_vfs_close; pVFS->cb.onRead = ma_default_vfs_read; pVFS->cb.onWrite = ma_default_vfs_write; pVFS->cb.onSeek = ma_default_vfs_seek; pVFS->cb.onTell = ma_default_vfs_tell; pVFS->cb.onInfo = ma_default_vfs_info; ma_allocation_callbacks_init_copy(&pVFS->allocationCallbacks, pAllocationCallbacks); return MA_SUCCESS; } MA_API ma_result ma_vfs_or_default_open(ma_vfs* pVFS, const char* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile) { if (pVFS != NULL) { return ma_vfs_open(pVFS, pFilePath, openMode, pFile); } else { return ma_default_vfs_open(pVFS, pFilePath, openMode, pFile); } } MA_API ma_result ma_vfs_or_default_open_w(ma_vfs* pVFS, const wchar_t* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile) { if (pVFS != NULL) { return ma_vfs_open_w(pVFS, pFilePath, openMode, pFile); } else { return ma_default_vfs_open_w(pVFS, pFilePath, openMode, pFile); } } MA_API ma_result ma_vfs_or_default_close(ma_vfs* pVFS, ma_vfs_file file) { if (pVFS != NULL) { return ma_vfs_close(pVFS, file); } else { return ma_default_vfs_close(pVFS, file); } } MA_API ma_result ma_vfs_or_default_read(ma_vfs* pVFS, ma_vfs_file file, void* pDst, size_t sizeInBytes, size_t* pBytesRead) { if (pVFS != NULL) { return ma_vfs_read(pVFS, file, pDst, sizeInBytes, pBytesRead); } else { return ma_default_vfs_read(pVFS, file, pDst, sizeInBytes, pBytesRead); } } MA_API ma_result ma_vfs_or_default_write(ma_vfs* pVFS, ma_vfs_file file, const void* pSrc, size_t sizeInBytes, size_t* pBytesWritten) { if (pVFS != NULL) { return ma_vfs_write(pVFS, file, pSrc, sizeInBytes, pBytesWritten); } else { return ma_default_vfs_write(pVFS, file, pSrc, sizeInBytes, pBytesWritten); } } MA_API ma_result ma_vfs_or_default_seek(ma_vfs* pVFS, ma_vfs_file file, ma_int64 offset, ma_seek_origin origin) { if (pVFS != NULL) { return ma_vfs_seek(pVFS, file, offset, origin); } else { return ma_default_vfs_seek(pVFS, file, offset, origin); } } MA_API ma_result ma_vfs_or_default_tell(ma_vfs* pVFS, ma_vfs_file file, ma_int64* pCursor) { if (pVFS != NULL) { return ma_vfs_tell(pVFS, file, pCursor); } else { return ma_default_vfs_tell(pVFS, file, pCursor); } } MA_API ma_result ma_vfs_or_default_info(ma_vfs* pVFS, ma_vfs_file file, ma_file_info* pInfo) { if (pVFS != NULL) { return ma_vfs_info(pVFS, file, pInfo); } else { return ma_default_vfs_info(pVFS, file, pInfo); } } /************************************************************************************************************************************************************** Decoding and Encoding Headers. These are auto-generated from a tool. **************************************************************************************************************************************************************/ #if !defined(MA_NO_WAV) && (!defined(MA_NO_DECODING) || !defined(MA_NO_ENCODING)) /* dr_wav_h begin */ #ifndef dr_wav_h #define dr_wav_h #ifdef __cplusplus extern "C" { #endif #define DRWAV_STRINGIFY(x) #x #define DRWAV_XSTRINGIFY(x) DRWAV_STRINGIFY(x) #define DRWAV_VERSION_MAJOR 0 #define DRWAV_VERSION_MINOR 13 #define DRWAV_VERSION_REVISION 7 #define DRWAV_VERSION_STRING DRWAV_XSTRINGIFY(DRWAV_VERSION_MAJOR) "." DRWAV_XSTRINGIFY(DRWAV_VERSION_MINOR) "." DRWAV_XSTRINGIFY(DRWAV_VERSION_REVISION) #include typedef signed char drwav_int8; typedef unsigned char drwav_uint8; typedef signed short drwav_int16; typedef unsigned short drwav_uint16; typedef signed int drwav_int32; typedef unsigned int drwav_uint32; #if defined(_MSC_VER) && !defined(__clang__) typedef signed __int64 drwav_int64; typedef unsigned __int64 drwav_uint64; #else #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wlong-long" #if defined(__clang__) #pragma GCC diagnostic ignored "-Wc++11-long-long" #endif #endif typedef signed long long drwav_int64; typedef unsigned long long drwav_uint64; #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) #pragma GCC diagnostic pop #endif #endif #if defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) && !defined(__ILP32__)) || defined(_M_X64) || defined(__ia64) || defined (_M_IA64) || defined(__aarch64__) || defined(_M_ARM64) || defined(__powerpc64__) typedef drwav_uint64 drwav_uintptr; #else typedef drwav_uint32 drwav_uintptr; #endif typedef drwav_uint8 drwav_bool8; typedef drwav_uint32 drwav_bool32; #define DRWAV_TRUE 1 #define DRWAV_FALSE 0 #if !defined(DRWAV_API) #if defined(DRWAV_DLL) #if defined(_WIN32) #define DRWAV_DLL_IMPORT __declspec(dllimport) #define DRWAV_DLL_EXPORT __declspec(dllexport) #define DRWAV_DLL_PRIVATE static #else #if defined(__GNUC__) && __GNUC__ >= 4 #define DRWAV_DLL_IMPORT __attribute__((visibility("default"))) #define DRWAV_DLL_EXPORT __attribute__((visibility("default"))) #define DRWAV_DLL_PRIVATE __attribute__((visibility("hidden"))) #else #define DRWAV_DLL_IMPORT #define DRWAV_DLL_EXPORT #define DRWAV_DLL_PRIVATE static #endif #endif #if defined(DR_WAV_IMPLEMENTATION) || defined(DRWAV_IMPLEMENTATION) #define DRWAV_API DRWAV_DLL_EXPORT #else #define DRWAV_API DRWAV_DLL_IMPORT #endif #define DRWAV_PRIVATE DRWAV_DLL_PRIVATE #else #define DRWAV_API extern #define DRWAV_PRIVATE static #endif #endif typedef drwav_int32 drwav_result; #define DRWAV_SUCCESS 0 #define DRWAV_ERROR -1 #define DRWAV_INVALID_ARGS -2 #define DRWAV_INVALID_OPERATION -3 #define DRWAV_OUT_OF_MEMORY -4 #define DRWAV_OUT_OF_RANGE -5 #define DRWAV_ACCESS_DENIED -6 #define DRWAV_DOES_NOT_EXIST -7 #define DRWAV_ALREADY_EXISTS -8 #define DRWAV_TOO_MANY_OPEN_FILES -9 #define DRWAV_INVALID_FILE -10 #define DRWAV_TOO_BIG -11 #define DRWAV_PATH_TOO_LONG -12 #define DRWAV_NAME_TOO_LONG -13 #define DRWAV_NOT_DIRECTORY -14 #define DRWAV_IS_DIRECTORY -15 #define DRWAV_DIRECTORY_NOT_EMPTY -16 #define DRWAV_END_OF_FILE -17 #define DRWAV_NO_SPACE -18 #define DRWAV_BUSY -19 #define DRWAV_IO_ERROR -20 #define DRWAV_INTERRUPT -21 #define DRWAV_UNAVAILABLE -22 #define DRWAV_ALREADY_IN_USE -23 #define DRWAV_BAD_ADDRESS -24 #define DRWAV_BAD_SEEK -25 #define DRWAV_BAD_PIPE -26 #define DRWAV_DEADLOCK -27 #define DRWAV_TOO_MANY_LINKS -28 #define DRWAV_NOT_IMPLEMENTED -29 #define DRWAV_NO_MESSAGE -30 #define DRWAV_BAD_MESSAGE -31 #define DRWAV_NO_DATA_AVAILABLE -32 #define DRWAV_INVALID_DATA -33 #define DRWAV_TIMEOUT -34 #define DRWAV_NO_NETWORK -35 #define DRWAV_NOT_UNIQUE -36 #define DRWAV_NOT_SOCKET -37 #define DRWAV_NO_ADDRESS -38 #define DRWAV_BAD_PROTOCOL -39 #define DRWAV_PROTOCOL_UNAVAILABLE -40 #define DRWAV_PROTOCOL_NOT_SUPPORTED -41 #define DRWAV_PROTOCOL_FAMILY_NOT_SUPPORTED -42 #define DRWAV_ADDRESS_FAMILY_NOT_SUPPORTED -43 #define DRWAV_SOCKET_NOT_SUPPORTED -44 #define DRWAV_CONNECTION_RESET -45 #define DRWAV_ALREADY_CONNECTED -46 #define DRWAV_NOT_CONNECTED -47 #define DRWAV_CONNECTION_REFUSED -48 #define DRWAV_NO_HOST -49 #define DRWAV_IN_PROGRESS -50 #define DRWAV_CANCELLED -51 #define DRWAV_MEMORY_ALREADY_MAPPED -52 #define DRWAV_AT_END -53 #define DR_WAVE_FORMAT_PCM 0x1 #define DR_WAVE_FORMAT_ADPCM 0x2 #define DR_WAVE_FORMAT_IEEE_FLOAT 0x3 #define DR_WAVE_FORMAT_ALAW 0x6 #define DR_WAVE_FORMAT_MULAW 0x7 #define DR_WAVE_FORMAT_DVI_ADPCM 0x11 #define DR_WAVE_FORMAT_EXTENSIBLE 0xFFFE #define DRWAV_SEQUENTIAL 0x00000001 DRWAV_API void drwav_version(drwav_uint32* pMajor, drwav_uint32* pMinor, drwav_uint32* pRevision); DRWAV_API const char* drwav_version_string(void); typedef enum { drwav_seek_origin_start, drwav_seek_origin_current } drwav_seek_origin; typedef enum { drwav_container_riff, drwav_container_w64, drwav_container_rf64 } drwav_container; typedef struct { union { drwav_uint8 fourcc[4]; drwav_uint8 guid[16]; } id; drwav_uint64 sizeInBytes; unsigned int paddingSize; } drwav_chunk_header; typedef struct { drwav_uint16 formatTag; drwav_uint16 channels; drwav_uint32 sampleRate; drwav_uint32 avgBytesPerSec; drwav_uint16 blockAlign; drwav_uint16 bitsPerSample; drwav_uint16 extendedSize; drwav_uint16 validBitsPerSample; drwav_uint32 channelMask; drwav_uint8 subFormat[16]; } drwav_fmt; DRWAV_API drwav_uint16 drwav_fmt_get_format(const drwav_fmt* pFMT); typedef size_t (* drwav_read_proc)(void* pUserData, void* pBufferOut, size_t bytesToRead); typedef size_t (* drwav_write_proc)(void* pUserData, const void* pData, size_t bytesToWrite); typedef drwav_bool32 (* drwav_seek_proc)(void* pUserData, int offset, drwav_seek_origin origin); typedef drwav_uint64 (* drwav_chunk_proc)(void* pChunkUserData, drwav_read_proc onRead, drwav_seek_proc onSeek, void* pReadSeekUserData, const drwav_chunk_header* pChunkHeader, drwav_container container, const drwav_fmt* pFMT); typedef struct { void* pUserData; void* (* onMalloc)(size_t sz, void* pUserData); void* (* onRealloc)(void* p, size_t sz, void* pUserData); void (* onFree)(void* p, void* pUserData); } drwav_allocation_callbacks; typedef struct { const drwav_uint8* data; size_t dataSize; size_t currentReadPos; } drwav__memory_stream; typedef struct { void** ppData; size_t* pDataSize; size_t dataSize; size_t dataCapacity; size_t currentWritePos; } drwav__memory_stream_write; typedef struct { drwav_container container; drwav_uint32 format; drwav_uint32 channels; drwav_uint32 sampleRate; drwav_uint32 bitsPerSample; } drwav_data_format; typedef enum { drwav_metadata_type_none = 0, drwav_metadata_type_unknown = 1 << 0, drwav_metadata_type_smpl = 1 << 1, drwav_metadata_type_inst = 1 << 2, drwav_metadata_type_cue = 1 << 3, drwav_metadata_type_acid = 1 << 4, drwav_metadata_type_bext = 1 << 5, drwav_metadata_type_list_label = 1 << 6, drwav_metadata_type_list_note = 1 << 7, drwav_metadata_type_list_labelled_cue_region = 1 << 8, drwav_metadata_type_list_info_software = 1 << 9, drwav_metadata_type_list_info_copyright = 1 << 10, drwav_metadata_type_list_info_title = 1 << 11, drwav_metadata_type_list_info_artist = 1 << 12, drwav_metadata_type_list_info_comment = 1 << 13, drwav_metadata_type_list_info_date = 1 << 14, drwav_metadata_type_list_info_genre = 1 << 15, drwav_metadata_type_list_info_album = 1 << 16, drwav_metadata_type_list_info_tracknumber = 1 << 17, drwav_metadata_type_list_all_info_strings = drwav_metadata_type_list_info_software | drwav_metadata_type_list_info_copyright | drwav_metadata_type_list_info_title | drwav_metadata_type_list_info_artist | drwav_metadata_type_list_info_comment | drwav_metadata_type_list_info_date | drwav_metadata_type_list_info_genre | drwav_metadata_type_list_info_album | drwav_metadata_type_list_info_tracknumber, drwav_metadata_type_list_all_adtl = drwav_metadata_type_list_label | drwav_metadata_type_list_note | drwav_metadata_type_list_labelled_cue_region, drwav_metadata_type_all = -2, drwav_metadata_type_all_including_unknown = -1 } drwav_metadata_type; typedef enum { drwav_smpl_loop_type_forward = 0, drwav_smpl_loop_type_pingpong = 1, drwav_smpl_loop_type_backward = 2 } drwav_smpl_loop_type; typedef struct { drwav_uint32 cuePointId; drwav_uint32 type; drwav_uint32 firstSampleByteOffset; drwav_uint32 lastSampleByteOffset; drwav_uint32 sampleFraction; drwav_uint32 playCount; } drwav_smpl_loop; typedef struct { drwav_uint32 manufacturerId; drwav_uint32 productId; drwav_uint32 samplePeriodNanoseconds; drwav_uint32 midiUnityNote; drwav_uint32 midiPitchFraction; drwav_uint32 smpteFormat; drwav_uint32 smpteOffset; drwav_uint32 sampleLoopCount; drwav_uint32 samplerSpecificDataSizeInBytes; drwav_smpl_loop* pLoops; drwav_uint8* pSamplerSpecificData; } drwav_smpl; typedef struct { drwav_int8 midiUnityNote; drwav_int8 fineTuneCents; drwav_int8 gainDecibels; drwav_int8 lowNote; drwav_int8 highNote; drwav_int8 lowVelocity; drwav_int8 highVelocity; } drwav_inst; typedef struct { drwav_uint32 id; drwav_uint32 playOrderPosition; drwav_uint8 dataChunkId[4]; drwav_uint32 chunkStart; drwav_uint32 blockStart; drwav_uint32 sampleByteOffset; } drwav_cue_point; typedef struct { drwav_uint32 cuePointCount; drwav_cue_point *pCuePoints; } drwav_cue; typedef enum { drwav_acid_flag_one_shot = 1, drwav_acid_flag_root_note_set = 2, drwav_acid_flag_stretch = 4, drwav_acid_flag_disk_based = 8, drwav_acid_flag_acidizer = 16 } drwav_acid_flag; typedef struct { drwav_uint32 flags; drwav_uint16 midiUnityNote; drwav_uint16 reserved1; float reserved2; drwav_uint32 numBeats; drwav_uint16 meterDenominator; drwav_uint16 meterNumerator; float tempo; } drwav_acid; typedef struct { drwav_uint32 cuePointId; drwav_uint32 stringLength; char* pString; } drwav_list_label_or_note; typedef struct { char* pDescription; char* pOriginatorName; char* pOriginatorReference; char pOriginationDate[10]; char pOriginationTime[8]; drwav_uint64 timeReference; drwav_uint16 version; char* pCodingHistory; drwav_uint32 codingHistorySize; drwav_uint8* pUMID; drwav_uint16 loudnessValue; drwav_uint16 loudnessRange; drwav_uint16 maxTruePeakLevel; drwav_uint16 maxMomentaryLoudness; drwav_uint16 maxShortTermLoudness; } drwav_bext; typedef struct { drwav_uint32 stringLength; char* pString; } drwav_list_info_text; typedef struct { drwav_uint32 cuePointId; drwav_uint32 sampleLength; drwav_uint8 purposeId[4]; drwav_uint16 country; drwav_uint16 language; drwav_uint16 dialect; drwav_uint16 codePage; drwav_uint32 stringLength; char* pString; } drwav_list_labelled_cue_region; typedef enum { drwav_metadata_location_invalid, drwav_metadata_location_top_level, drwav_metadata_location_inside_info_list, drwav_metadata_location_inside_adtl_list } drwav_metadata_location; typedef struct { drwav_uint8 id[4]; drwav_metadata_location chunkLocation; drwav_uint32 dataSizeInBytes; drwav_uint8* pData; } drwav_unknown_metadata; typedef struct { drwav_metadata_type type; union { drwav_cue cue; drwav_smpl smpl; drwav_acid acid; drwav_inst inst; drwav_bext bext; drwav_list_label_or_note labelOrNote; drwav_list_labelled_cue_region labelledCueRegion; drwav_list_info_text infoText; drwav_unknown_metadata unknown; } data; } drwav_metadata; typedef struct { drwav_read_proc onRead; drwav_write_proc onWrite; drwav_seek_proc onSeek; void* pUserData; drwav_allocation_callbacks allocationCallbacks; drwav_container container; drwav_fmt fmt; drwav_uint32 sampleRate; drwav_uint16 channels; drwav_uint16 bitsPerSample; drwav_uint16 translatedFormatTag; drwav_uint64 totalPCMFrameCount; drwav_uint64 dataChunkDataSize; drwav_uint64 dataChunkDataPos; drwav_uint64 bytesRemaining; drwav_uint64 readCursorInPCMFrames; drwav_uint64 dataChunkDataSizeTargetWrite; drwav_bool32 isSequentialWrite; drwav_metadata_type allowedMetadataTypes; drwav_metadata* pMetadata; drwav_uint32 metadataCount; drwav__memory_stream memoryStream; drwav__memory_stream_write memoryStreamWrite; struct { drwav_uint32 bytesRemainingInBlock; drwav_uint16 predictor[2]; drwav_int32 delta[2]; drwav_int32 cachedFrames[4]; drwav_uint32 cachedFrameCount; drwav_int32 prevFrames[2][2]; } msadpcm; struct { drwav_uint32 bytesRemainingInBlock; drwav_int32 predictor[2]; drwav_int32 stepIndex[2]; drwav_int32 cachedFrames[16]; drwav_uint32 cachedFrameCount; } ima; } drwav; DRWAV_API drwav_bool32 drwav_init(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks); DRWAV_API drwav_bool32 drwav_init_ex(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onSeek, drwav_chunk_proc onChunk, void* pReadSeekUserData, void* pChunkUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks); DRWAV_API drwav_bool32 drwav_init_with_metadata(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks); DRWAV_API drwav_bool32 drwav_init_write(drwav* pWav, const drwav_data_format* pFormat, drwav_write_proc onWrite, drwav_seek_proc onSeek, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks); DRWAV_API drwav_bool32 drwav_init_write_sequential(drwav* pWav, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, drwav_write_proc onWrite, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks); DRWAV_API drwav_bool32 drwav_init_write_sequential_pcm_frames(drwav* pWav, const drwav_data_format* pFormat, drwav_uint64 totalPCMFrameCount, drwav_write_proc onWrite, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks); DRWAV_API drwav_bool32 drwav_init_write_with_metadata(drwav* pWav, const drwav_data_format* pFormat, drwav_write_proc onWrite, drwav_seek_proc onSeek, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks, drwav_metadata* pMetadata, drwav_uint32 metadataCount); DRWAV_API drwav_uint64 drwav_target_write_size_bytes(const drwav_data_format* pFormat, drwav_uint64 totalFrameCount, drwav_metadata* pMetadata, drwav_uint32 metadataCount); DRWAV_API drwav_metadata* drwav_take_ownership_of_metadata(drwav* pWav); DRWAV_API drwav_result drwav_uninit(drwav* pWav); DRWAV_API size_t drwav_read_raw(drwav* pWav, size_t bytesToRead, void* pBufferOut); DRWAV_API drwav_uint64 drwav_read_pcm_frames(drwav* pWav, drwav_uint64 framesToRead, void* pBufferOut); DRWAV_API drwav_uint64 drwav_read_pcm_frames_le(drwav* pWav, drwav_uint64 framesToRead, void* pBufferOut); DRWAV_API drwav_uint64 drwav_read_pcm_frames_be(drwav* pWav, drwav_uint64 framesToRead, void* pBufferOut); DRWAV_API drwav_bool32 drwav_seek_to_pcm_frame(drwav* pWav, drwav_uint64 targetFrameIndex); DRWAV_API drwav_result drwav_get_cursor_in_pcm_frames(drwav* pWav, drwav_uint64* pCursor); DRWAV_API drwav_result drwav_get_length_in_pcm_frames(drwav* pWav, drwav_uint64* pLength); DRWAV_API size_t drwav_write_raw(drwav* pWav, size_t bytesToWrite, const void* pData); DRWAV_API drwav_uint64 drwav_write_pcm_frames(drwav* pWav, drwav_uint64 framesToWrite, const void* pData); DRWAV_API drwav_uint64 drwav_write_pcm_frames_le(drwav* pWav, drwav_uint64 framesToWrite, const void* pData); DRWAV_API drwav_uint64 drwav_write_pcm_frames_be(drwav* pWav, drwav_uint64 framesToWrite, const void* pData); #ifndef DR_WAV_NO_CONVERSION_API DRWAV_API drwav_uint64 drwav_read_pcm_frames_s16(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut); DRWAV_API drwav_uint64 drwav_read_pcm_frames_s16le(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut); DRWAV_API drwav_uint64 drwav_read_pcm_frames_s16be(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut); DRWAV_API void drwav_u8_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCount); DRWAV_API void drwav_s24_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCount); DRWAV_API void drwav_s32_to_s16(drwav_int16* pOut, const drwav_int32* pIn, size_t sampleCount); DRWAV_API void drwav_f32_to_s16(drwav_int16* pOut, const float* pIn, size_t sampleCount); DRWAV_API void drwav_f64_to_s16(drwav_int16* pOut, const double* pIn, size_t sampleCount); DRWAV_API void drwav_alaw_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCount); DRWAV_API void drwav_mulaw_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCount); DRWAV_API drwav_uint64 drwav_read_pcm_frames_f32(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut); DRWAV_API drwav_uint64 drwav_read_pcm_frames_f32le(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut); DRWAV_API drwav_uint64 drwav_read_pcm_frames_f32be(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut); DRWAV_API void drwav_u8_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount); DRWAV_API void drwav_s16_to_f32(float* pOut, const drwav_int16* pIn, size_t sampleCount); DRWAV_API void drwav_s24_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount); DRWAV_API void drwav_s32_to_f32(float* pOut, const drwav_int32* pIn, size_t sampleCount); DRWAV_API void drwav_f64_to_f32(float* pOut, const double* pIn, size_t sampleCount); DRWAV_API void drwav_alaw_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount); DRWAV_API void drwav_mulaw_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount); DRWAV_API drwav_uint64 drwav_read_pcm_frames_s32(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut); DRWAV_API drwav_uint64 drwav_read_pcm_frames_s32le(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut); DRWAV_API drwav_uint64 drwav_read_pcm_frames_s32be(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut); DRWAV_API void drwav_u8_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sampleCount); DRWAV_API void drwav_s16_to_s32(drwav_int32* pOut, const drwav_int16* pIn, size_t sampleCount); DRWAV_API void drwav_s24_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sampleCount); DRWAV_API void drwav_f32_to_s32(drwav_int32* pOut, const float* pIn, size_t sampleCount); DRWAV_API void drwav_f64_to_s32(drwav_int32* pOut, const double* pIn, size_t sampleCount); DRWAV_API void drwav_alaw_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sampleCount); DRWAV_API void drwav_mulaw_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sampleCount); #endif #ifndef DR_WAV_NO_STDIO DRWAV_API drwav_bool32 drwav_init_file(drwav* pWav, const char* filename, const drwav_allocation_callbacks* pAllocationCallbacks); DRWAV_API drwav_bool32 drwav_init_file_ex(drwav* pWav, const char* filename, drwav_chunk_proc onChunk, void* pChunkUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks); DRWAV_API drwav_bool32 drwav_init_file_w(drwav* pWav, const wchar_t* filename, const drwav_allocation_callbacks* pAllocationCallbacks); DRWAV_API drwav_bool32 drwav_init_file_ex_w(drwav* pWav, const wchar_t* filename, drwav_chunk_proc onChunk, void* pChunkUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks); DRWAV_API drwav_bool32 drwav_init_file_with_metadata(drwav* pWav, const char* filename, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks); DRWAV_API drwav_bool32 drwav_init_file_with_metadata_w(drwav* pWav, const wchar_t* filename, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks); DRWAV_API drwav_bool32 drwav_init_file_write(drwav* pWav, const char* filename, const drwav_data_format* pFormat, const drwav_allocation_callbacks* pAllocationCallbacks); DRWAV_API drwav_bool32 drwav_init_file_write_sequential(drwav* pWav, const char* filename, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, const drwav_allocation_callbacks* pAllocationCallbacks); DRWAV_API drwav_bool32 drwav_init_file_write_sequential_pcm_frames(drwav* pWav, const char* filename, const drwav_data_format* pFormat, drwav_uint64 totalPCMFrameCount, const drwav_allocation_callbacks* pAllocationCallbacks); DRWAV_API drwav_bool32 drwav_init_file_write_w(drwav* pWav, const wchar_t* filename, const drwav_data_format* pFormat, const drwav_allocation_callbacks* pAllocationCallbacks); DRWAV_API drwav_bool32 drwav_init_file_write_sequential_w(drwav* pWav, const wchar_t* filename, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, const drwav_allocation_callbacks* pAllocationCallbacks); DRWAV_API drwav_bool32 drwav_init_file_write_sequential_pcm_frames_w(drwav* pWav, const wchar_t* filename, const drwav_data_format* pFormat, drwav_uint64 totalPCMFrameCount, const drwav_allocation_callbacks* pAllocationCallbacks); #endif DRWAV_API drwav_bool32 drwav_init_memory(drwav* pWav, const void* data, size_t dataSize, const drwav_allocation_callbacks* pAllocationCallbacks); DRWAV_API drwav_bool32 drwav_init_memory_ex(drwav* pWav, const void* data, size_t dataSize, drwav_chunk_proc onChunk, void* pChunkUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks); DRWAV_API drwav_bool32 drwav_init_memory_with_metadata(drwav* pWav, const void* data, size_t dataSize, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks); DRWAV_API drwav_bool32 drwav_init_memory_write(drwav* pWav, void** ppData, size_t* pDataSize, const drwav_data_format* pFormat, const drwav_allocation_callbacks* pAllocationCallbacks); DRWAV_API drwav_bool32 drwav_init_memory_write_sequential(drwav* pWav, void** ppData, size_t* pDataSize, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, const drwav_allocation_callbacks* pAllocationCallbacks); DRWAV_API drwav_bool32 drwav_init_memory_write_sequential_pcm_frames(drwav* pWav, void** ppData, size_t* pDataSize, const drwav_data_format* pFormat, drwav_uint64 totalPCMFrameCount, const drwav_allocation_callbacks* pAllocationCallbacks); #ifndef DR_WAV_NO_CONVERSION_API DRWAV_API drwav_int16* drwav_open_and_read_pcm_frames_s16(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks); DRWAV_API float* drwav_open_and_read_pcm_frames_f32(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks); DRWAV_API drwav_int32* drwav_open_and_read_pcm_frames_s32(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks); #ifndef DR_WAV_NO_STDIO DRWAV_API drwav_int16* drwav_open_file_and_read_pcm_frames_s16(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks); DRWAV_API float* drwav_open_file_and_read_pcm_frames_f32(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks); DRWAV_API drwav_int32* drwav_open_file_and_read_pcm_frames_s32(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks); DRWAV_API drwav_int16* drwav_open_file_and_read_pcm_frames_s16_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks); DRWAV_API float* drwav_open_file_and_read_pcm_frames_f32_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks); DRWAV_API drwav_int32* drwav_open_file_and_read_pcm_frames_s32_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks); #endif DRWAV_API drwav_int16* drwav_open_memory_and_read_pcm_frames_s16(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks); DRWAV_API float* drwav_open_memory_and_read_pcm_frames_f32(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks); DRWAV_API drwav_int32* drwav_open_memory_and_read_pcm_frames_s32(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks); #endif DRWAV_API void drwav_free(void* p, const drwav_allocation_callbacks* pAllocationCallbacks); DRWAV_API drwav_uint16 drwav_bytes_to_u16(const drwav_uint8* data); DRWAV_API drwav_int16 drwav_bytes_to_s16(const drwav_uint8* data); DRWAV_API drwav_uint32 drwav_bytes_to_u32(const drwav_uint8* data); DRWAV_API drwav_int32 drwav_bytes_to_s32(const drwav_uint8* data); DRWAV_API drwav_uint64 drwav_bytes_to_u64(const drwav_uint8* data); DRWAV_API drwav_int64 drwav_bytes_to_s64(const drwav_uint8* data); DRWAV_API float drwav_bytes_to_f32(const drwav_uint8* data); DRWAV_API drwav_bool32 drwav_guid_equal(const drwav_uint8 a[16], const drwav_uint8 b[16]); DRWAV_API drwav_bool32 drwav_fourcc_equal(const drwav_uint8* a, const char* b); #ifdef __cplusplus } #endif #endif /* dr_wav_h end */ #endif /* MA_NO_WAV */ #if !defined(MA_NO_FLAC) && !defined(MA_NO_DECODING) /* dr_flac_h begin */ #ifndef dr_flac_h #define dr_flac_h #ifdef __cplusplus extern "C" { #endif #define DRFLAC_STRINGIFY(x) #x #define DRFLAC_XSTRINGIFY(x) DRFLAC_STRINGIFY(x) #define DRFLAC_VERSION_MAJOR 0 #define DRFLAC_VERSION_MINOR 12 #define DRFLAC_VERSION_REVISION 39 #define DRFLAC_VERSION_STRING DRFLAC_XSTRINGIFY(DRFLAC_VERSION_MAJOR) "." DRFLAC_XSTRINGIFY(DRFLAC_VERSION_MINOR) "." DRFLAC_XSTRINGIFY(DRFLAC_VERSION_REVISION) #include typedef signed char drflac_int8; typedef unsigned char drflac_uint8; typedef signed short drflac_int16; typedef unsigned short drflac_uint16; typedef signed int drflac_int32; typedef unsigned int drflac_uint32; #if defined(_MSC_VER) && !defined(__clang__) typedef signed __int64 drflac_int64; typedef unsigned __int64 drflac_uint64; #else #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wlong-long" #if defined(__clang__) #pragma GCC diagnostic ignored "-Wc++11-long-long" #endif #endif typedef signed long long drflac_int64; typedef unsigned long long drflac_uint64; #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) #pragma GCC diagnostic pop #endif #endif #if defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) && !defined(__ILP32__)) || defined(_M_X64) || defined(__ia64) || defined(_M_IA64) || defined(__aarch64__) || defined(_M_ARM64) || defined(__powerpc64__) typedef drflac_uint64 drflac_uintptr; #else typedef drflac_uint32 drflac_uintptr; #endif typedef drflac_uint8 drflac_bool8; typedef drflac_uint32 drflac_bool32; #define DRFLAC_TRUE 1 #define DRFLAC_FALSE 0 #if !defined(DRFLAC_API) #if defined(DRFLAC_DLL) #if defined(_WIN32) #define DRFLAC_DLL_IMPORT __declspec(dllimport) #define DRFLAC_DLL_EXPORT __declspec(dllexport) #define DRFLAC_DLL_PRIVATE static #else #if defined(__GNUC__) && __GNUC__ >= 4 #define DRFLAC_DLL_IMPORT __attribute__((visibility("default"))) #define DRFLAC_DLL_EXPORT __attribute__((visibility("default"))) #define DRFLAC_DLL_PRIVATE __attribute__((visibility("hidden"))) #else #define DRFLAC_DLL_IMPORT #define DRFLAC_DLL_EXPORT #define DRFLAC_DLL_PRIVATE static #endif #endif #if defined(DR_FLAC_IMPLEMENTATION) || defined(DRFLAC_IMPLEMENTATION) #define DRFLAC_API DRFLAC_DLL_EXPORT #else #define DRFLAC_API DRFLAC_DLL_IMPORT #endif #define DRFLAC_PRIVATE DRFLAC_DLL_PRIVATE #else #define DRFLAC_API extern #define DRFLAC_PRIVATE static #endif #endif #if defined(_MSC_VER) && _MSC_VER >= 1700 #define DRFLAC_DEPRECATED __declspec(deprecated) #elif (defined(__GNUC__) && __GNUC__ >= 4) #define DRFLAC_DEPRECATED __attribute__((deprecated)) #elif defined(__has_feature) #if __has_feature(attribute_deprecated) #define DRFLAC_DEPRECATED __attribute__((deprecated)) #else #define DRFLAC_DEPRECATED #endif #else #define DRFLAC_DEPRECATED #endif DRFLAC_API void drflac_version(drflac_uint32* pMajor, drflac_uint32* pMinor, drflac_uint32* pRevision); DRFLAC_API const char* drflac_version_string(void); #ifndef DR_FLAC_BUFFER_SIZE #define DR_FLAC_BUFFER_SIZE 4096 #endif #if defined(_WIN64) || defined(_LP64) || defined(__LP64__) #define DRFLAC_64BIT #endif #ifdef DRFLAC_64BIT typedef drflac_uint64 drflac_cache_t; #else typedef drflac_uint32 drflac_cache_t; #endif #define DRFLAC_METADATA_BLOCK_TYPE_STREAMINFO 0 #define DRFLAC_METADATA_BLOCK_TYPE_PADDING 1 #define DRFLAC_METADATA_BLOCK_TYPE_APPLICATION 2 #define DRFLAC_METADATA_BLOCK_TYPE_SEEKTABLE 3 #define DRFLAC_METADATA_BLOCK_TYPE_VORBIS_COMMENT 4 #define DRFLAC_METADATA_BLOCK_TYPE_CUESHEET 5 #define DRFLAC_METADATA_BLOCK_TYPE_PICTURE 6 #define DRFLAC_METADATA_BLOCK_TYPE_INVALID 127 #define DRFLAC_PICTURE_TYPE_OTHER 0 #define DRFLAC_PICTURE_TYPE_FILE_ICON 1 #define DRFLAC_PICTURE_TYPE_OTHER_FILE_ICON 2 #define DRFLAC_PICTURE_TYPE_COVER_FRONT 3 #define DRFLAC_PICTURE_TYPE_COVER_BACK 4 #define DRFLAC_PICTURE_TYPE_LEAFLET_PAGE 5 #define DRFLAC_PICTURE_TYPE_MEDIA 6 #define DRFLAC_PICTURE_TYPE_LEAD_ARTIST 7 #define DRFLAC_PICTURE_TYPE_ARTIST 8 #define DRFLAC_PICTURE_TYPE_CONDUCTOR 9 #define DRFLAC_PICTURE_TYPE_BAND 10 #define DRFLAC_PICTURE_TYPE_COMPOSER 11 #define DRFLAC_PICTURE_TYPE_LYRICIST 12 #define DRFLAC_PICTURE_TYPE_RECORDING_LOCATION 13 #define DRFLAC_PICTURE_TYPE_DURING_RECORDING 14 #define DRFLAC_PICTURE_TYPE_DURING_PERFORMANCE 15 #define DRFLAC_PICTURE_TYPE_SCREEN_CAPTURE 16 #define DRFLAC_PICTURE_TYPE_BRIGHT_COLORED_FISH 17 #define DRFLAC_PICTURE_TYPE_ILLUSTRATION 18 #define DRFLAC_PICTURE_TYPE_BAND_LOGOTYPE 19 #define DRFLAC_PICTURE_TYPE_PUBLISHER_LOGOTYPE 20 typedef enum { drflac_container_native, drflac_container_ogg, drflac_container_unknown } drflac_container; typedef enum { drflac_seek_origin_start, drflac_seek_origin_current } drflac_seek_origin; typedef struct { drflac_uint64 firstPCMFrame; drflac_uint64 flacFrameOffset; drflac_uint16 pcmFrameCount; } drflac_seekpoint; typedef struct { drflac_uint16 minBlockSizeInPCMFrames; drflac_uint16 maxBlockSizeInPCMFrames; drflac_uint32 minFrameSizeInPCMFrames; drflac_uint32 maxFrameSizeInPCMFrames; drflac_uint32 sampleRate; drflac_uint8 channels; drflac_uint8 bitsPerSample; drflac_uint64 totalPCMFrameCount; drflac_uint8 md5[16]; } drflac_streaminfo; typedef struct { drflac_uint32 type; const void* pRawData; drflac_uint32 rawDataSize; union { drflac_streaminfo streaminfo; struct { int unused; } padding; struct { drflac_uint32 id; const void* pData; drflac_uint32 dataSize; } application; struct { drflac_uint32 seekpointCount; const drflac_seekpoint* pSeekpoints; } seektable; struct { drflac_uint32 vendorLength; const char* vendor; drflac_uint32 commentCount; const void* pComments; } vorbis_comment; struct { char catalog[128]; drflac_uint64 leadInSampleCount; drflac_bool32 isCD; drflac_uint8 trackCount; const void* pTrackData; } cuesheet; struct { drflac_uint32 type; drflac_uint32 mimeLength; const char* mime; drflac_uint32 descriptionLength; const char* description; drflac_uint32 width; drflac_uint32 height; drflac_uint32 colorDepth; drflac_uint32 indexColorCount; drflac_uint32 pictureDataSize; const drflac_uint8* pPictureData; } picture; } data; } drflac_metadata; typedef size_t (* drflac_read_proc)(void* pUserData, void* pBufferOut, size_t bytesToRead); typedef drflac_bool32 (* drflac_seek_proc)(void* pUserData, int offset, drflac_seek_origin origin); typedef void (* drflac_meta_proc)(void* pUserData, drflac_metadata* pMetadata); typedef struct { void* pUserData; void* (* onMalloc)(size_t sz, void* pUserData); void* (* onRealloc)(void* p, size_t sz, void* pUserData); void (* onFree)(void* p, void* pUserData); } drflac_allocation_callbacks; typedef struct { const drflac_uint8* data; size_t dataSize; size_t currentReadPos; } drflac__memory_stream; typedef struct { drflac_read_proc onRead; drflac_seek_proc onSeek; void* pUserData; size_t unalignedByteCount; drflac_cache_t unalignedCache; drflac_uint32 nextL2Line; drflac_uint32 consumedBits; drflac_cache_t cacheL2[DR_FLAC_BUFFER_SIZE/sizeof(drflac_cache_t)]; drflac_cache_t cache; drflac_uint16 crc16; drflac_cache_t crc16Cache; drflac_uint32 crc16CacheIgnoredBytes; } drflac_bs; typedef struct { drflac_uint8 subframeType; drflac_uint8 wastedBitsPerSample; drflac_uint8 lpcOrder; drflac_int32* pSamplesS32; } drflac_subframe; typedef struct { drflac_uint64 pcmFrameNumber; drflac_uint32 flacFrameNumber; drflac_uint32 sampleRate; drflac_uint16 blockSizeInPCMFrames; drflac_uint8 channelAssignment; drflac_uint8 bitsPerSample; drflac_uint8 crc8; } drflac_frame_header; typedef struct { drflac_frame_header header; drflac_uint32 pcmFramesRemaining; drflac_subframe subframes[8]; } drflac_frame; typedef struct { drflac_meta_proc onMeta; void* pUserDataMD; drflac_allocation_callbacks allocationCallbacks; drflac_uint32 sampleRate; drflac_uint8 channels; drflac_uint8 bitsPerSample; drflac_uint16 maxBlockSizeInPCMFrames; drflac_uint64 totalPCMFrameCount; drflac_container container; drflac_uint32 seekpointCount; drflac_frame currentFLACFrame; drflac_uint64 currentPCMFrame; drflac_uint64 firstFLACFramePosInBytes; drflac__memory_stream memoryStream; drflac_int32* pDecodedSamples; drflac_seekpoint* pSeekpoints; void* _oggbs; drflac_bool32 _noSeekTableSeek : 1; drflac_bool32 _noBinarySearchSeek : 1; drflac_bool32 _noBruteForceSeek : 1; drflac_bs bs; drflac_uint8 pExtraData[1]; } drflac; DRFLAC_API drflac* drflac_open(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks); DRFLAC_API drflac* drflac_open_relaxed(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_container container, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks); DRFLAC_API drflac* drflac_open_with_metadata(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks); DRFLAC_API drflac* drflac_open_with_metadata_relaxed(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, drflac_container container, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks); DRFLAC_API void drflac_close(drflac* pFlac); DRFLAC_API drflac_uint64 drflac_read_pcm_frames_s32(drflac* pFlac, drflac_uint64 framesToRead, drflac_int32* pBufferOut); DRFLAC_API drflac_uint64 drflac_read_pcm_frames_s16(drflac* pFlac, drflac_uint64 framesToRead, drflac_int16* pBufferOut); DRFLAC_API drflac_uint64 drflac_read_pcm_frames_f32(drflac* pFlac, drflac_uint64 framesToRead, float* pBufferOut); DRFLAC_API drflac_bool32 drflac_seek_to_pcm_frame(drflac* pFlac, drflac_uint64 pcmFrameIndex); #ifndef DR_FLAC_NO_STDIO DRFLAC_API drflac* drflac_open_file(const char* pFileName, const drflac_allocation_callbacks* pAllocationCallbacks); DRFLAC_API drflac* drflac_open_file_w(const wchar_t* pFileName, const drflac_allocation_callbacks* pAllocationCallbacks); DRFLAC_API drflac* drflac_open_file_with_metadata(const char* pFileName, drflac_meta_proc onMeta, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks); DRFLAC_API drflac* drflac_open_file_with_metadata_w(const wchar_t* pFileName, drflac_meta_proc onMeta, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks); #endif DRFLAC_API drflac* drflac_open_memory(const void* pData, size_t dataSize, const drflac_allocation_callbacks* pAllocationCallbacks); DRFLAC_API drflac* drflac_open_memory_with_metadata(const void* pData, size_t dataSize, drflac_meta_proc onMeta, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks); DRFLAC_API drflac_int32* drflac_open_and_read_pcm_frames_s32(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks); DRFLAC_API drflac_int16* drflac_open_and_read_pcm_frames_s16(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks); DRFLAC_API float* drflac_open_and_read_pcm_frames_f32(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks); #ifndef DR_FLAC_NO_STDIO DRFLAC_API drflac_int32* drflac_open_file_and_read_pcm_frames_s32(const char* filename, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks); DRFLAC_API drflac_int16* drflac_open_file_and_read_pcm_frames_s16(const char* filename, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks); DRFLAC_API float* drflac_open_file_and_read_pcm_frames_f32(const char* filename, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks); #endif DRFLAC_API drflac_int32* drflac_open_memory_and_read_pcm_frames_s32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks); DRFLAC_API drflac_int16* drflac_open_memory_and_read_pcm_frames_s16(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks); DRFLAC_API float* drflac_open_memory_and_read_pcm_frames_f32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks); DRFLAC_API void drflac_free(void* p, const drflac_allocation_callbacks* pAllocationCallbacks); typedef struct { drflac_uint32 countRemaining; const char* pRunningData; } drflac_vorbis_comment_iterator; DRFLAC_API void drflac_init_vorbis_comment_iterator(drflac_vorbis_comment_iterator* pIter, drflac_uint32 commentCount, const void* pComments); DRFLAC_API const char* drflac_next_vorbis_comment(drflac_vorbis_comment_iterator* pIter, drflac_uint32* pCommentLengthOut); typedef struct { drflac_uint32 countRemaining; const char* pRunningData; } drflac_cuesheet_track_iterator; typedef struct { drflac_uint64 offset; drflac_uint8 index; drflac_uint8 reserved[3]; } drflac_cuesheet_track_index; typedef struct { drflac_uint64 offset; drflac_uint8 trackNumber; char ISRC[12]; drflac_bool8 isAudio; drflac_bool8 preEmphasis; drflac_uint8 indexCount; const drflac_cuesheet_track_index* pIndexPoints; } drflac_cuesheet_track; DRFLAC_API void drflac_init_cuesheet_track_iterator(drflac_cuesheet_track_iterator* pIter, drflac_uint32 trackCount, const void* pTrackData); DRFLAC_API drflac_bool32 drflac_next_cuesheet_track(drflac_cuesheet_track_iterator* pIter, drflac_cuesheet_track* pCuesheetTrack); #ifdef __cplusplus } #endif #endif /* dr_flac_h end */ #endif /* MA_NO_FLAC */ #if !defined(MA_NO_MP3) && !defined(MA_NO_DECODING) /* dr_mp3_h begin */ #ifndef dr_mp3_h #define dr_mp3_h #ifdef __cplusplus extern "C" { #endif #define DRMP3_STRINGIFY(x) #x #define DRMP3_XSTRINGIFY(x) DRMP3_STRINGIFY(x) #define DRMP3_VERSION_MAJOR 0 #define DRMP3_VERSION_MINOR 6 #define DRMP3_VERSION_REVISION 34 #define DRMP3_VERSION_STRING DRMP3_XSTRINGIFY(DRMP3_VERSION_MAJOR) "." DRMP3_XSTRINGIFY(DRMP3_VERSION_MINOR) "." DRMP3_XSTRINGIFY(DRMP3_VERSION_REVISION) #include typedef signed char drmp3_int8; typedef unsigned char drmp3_uint8; typedef signed short drmp3_int16; typedef unsigned short drmp3_uint16; typedef signed int drmp3_int32; typedef unsigned int drmp3_uint32; #if defined(_MSC_VER) && !defined(__clang__) typedef signed __int64 drmp3_int64; typedef unsigned __int64 drmp3_uint64; #else #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wlong-long" #if defined(__clang__) #pragma GCC diagnostic ignored "-Wc++11-long-long" #endif #endif typedef signed long long drmp3_int64; typedef unsigned long long drmp3_uint64; #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) #pragma GCC diagnostic pop #endif #endif #if defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) && !defined(__ILP32__)) || defined(_M_X64) || defined(__ia64) || defined (_M_IA64) || defined(__aarch64__) || defined(_M_ARM64) || defined(__powerpc64__) typedef drmp3_uint64 drmp3_uintptr; #else typedef drmp3_uint32 drmp3_uintptr; #endif typedef drmp3_uint8 drmp3_bool8; typedef drmp3_uint32 drmp3_bool32; #define DRMP3_TRUE 1 #define DRMP3_FALSE 0 #if !defined(DRMP3_API) #if defined(DRMP3_DLL) #if defined(_WIN32) #define DRMP3_DLL_IMPORT __declspec(dllimport) #define DRMP3_DLL_EXPORT __declspec(dllexport) #define DRMP3_DLL_PRIVATE static #else #if defined(__GNUC__) && __GNUC__ >= 4 #define DRMP3_DLL_IMPORT __attribute__((visibility("default"))) #define DRMP3_DLL_EXPORT __attribute__((visibility("default"))) #define DRMP3_DLL_PRIVATE __attribute__((visibility("hidden"))) #else #define DRMP3_DLL_IMPORT #define DRMP3_DLL_EXPORT #define DRMP3_DLL_PRIVATE static #endif #endif #if defined(DR_MP3_IMPLEMENTATION) || defined(DRMP3_IMPLEMENTATION) #define DRMP3_API DRMP3_DLL_EXPORT #else #define DRMP3_API DRMP3_DLL_IMPORT #endif #define DRMP3_PRIVATE DRMP3_DLL_PRIVATE #else #define DRMP3_API extern #define DRMP3_PRIVATE static #endif #endif typedef drmp3_int32 drmp3_result; #define DRMP3_SUCCESS 0 #define DRMP3_ERROR -1 #define DRMP3_INVALID_ARGS -2 #define DRMP3_INVALID_OPERATION -3 #define DRMP3_OUT_OF_MEMORY -4 #define DRMP3_OUT_OF_RANGE -5 #define DRMP3_ACCESS_DENIED -6 #define DRMP3_DOES_NOT_EXIST -7 #define DRMP3_ALREADY_EXISTS -8 #define DRMP3_TOO_MANY_OPEN_FILES -9 #define DRMP3_INVALID_FILE -10 #define DRMP3_TOO_BIG -11 #define DRMP3_PATH_TOO_LONG -12 #define DRMP3_NAME_TOO_LONG -13 #define DRMP3_NOT_DIRECTORY -14 #define DRMP3_IS_DIRECTORY -15 #define DRMP3_DIRECTORY_NOT_EMPTY -16 #define DRMP3_END_OF_FILE -17 #define DRMP3_NO_SPACE -18 #define DRMP3_BUSY -19 #define DRMP3_IO_ERROR -20 #define DRMP3_INTERRUPT -21 #define DRMP3_UNAVAILABLE -22 #define DRMP3_ALREADY_IN_USE -23 #define DRMP3_BAD_ADDRESS -24 #define DRMP3_BAD_SEEK -25 #define DRMP3_BAD_PIPE -26 #define DRMP3_DEADLOCK -27 #define DRMP3_TOO_MANY_LINKS -28 #define DRMP3_NOT_IMPLEMENTED -29 #define DRMP3_NO_MESSAGE -30 #define DRMP3_BAD_MESSAGE -31 #define DRMP3_NO_DATA_AVAILABLE -32 #define DRMP3_INVALID_DATA -33 #define DRMP3_TIMEOUT -34 #define DRMP3_NO_NETWORK -35 #define DRMP3_NOT_UNIQUE -36 #define DRMP3_NOT_SOCKET -37 #define DRMP3_NO_ADDRESS -38 #define DRMP3_BAD_PROTOCOL -39 #define DRMP3_PROTOCOL_UNAVAILABLE -40 #define DRMP3_PROTOCOL_NOT_SUPPORTED -41 #define DRMP3_PROTOCOL_FAMILY_NOT_SUPPORTED -42 #define DRMP3_ADDRESS_FAMILY_NOT_SUPPORTED -43 #define DRMP3_SOCKET_NOT_SUPPORTED -44 #define DRMP3_CONNECTION_RESET -45 #define DRMP3_ALREADY_CONNECTED -46 #define DRMP3_NOT_CONNECTED -47 #define DRMP3_CONNECTION_REFUSED -48 #define DRMP3_NO_HOST -49 #define DRMP3_IN_PROGRESS -50 #define DRMP3_CANCELLED -51 #define DRMP3_MEMORY_ALREADY_MAPPED -52 #define DRMP3_AT_END -53 #define DRMP3_MAX_PCM_FRAMES_PER_MP3_FRAME 1152 #define DRMP3_MAX_SAMPLES_PER_FRAME (DRMP3_MAX_PCM_FRAMES_PER_MP3_FRAME*2) #ifdef _MSC_VER #define DRMP3_INLINE __forceinline #elif defined(__GNUC__) #if defined(__STRICT_ANSI__) #define DRMP3_GNUC_INLINE_HINT __inline__ #else #define DRMP3_GNUC_INLINE_HINT inline #endif #if (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 2)) || defined(__clang__) #define DRMP3_INLINE DRMP3_GNUC_INLINE_HINT __attribute__((always_inline)) #else #define DRMP3_INLINE DRMP3_GNUC_INLINE_HINT #endif #elif defined(__WATCOMC__) #define DRMP3_INLINE __inline #else #define DRMP3_INLINE #endif DRMP3_API void drmp3_version(drmp3_uint32* pMajor, drmp3_uint32* pMinor, drmp3_uint32* pRevision); DRMP3_API const char* drmp3_version_string(void); typedef struct { int frame_bytes, channels, hz, layer, bitrate_kbps; } drmp3dec_frame_info; 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; DRMP3_API void drmp3dec_init(drmp3dec *dec); DRMP3_API int drmp3dec_decode_frame(drmp3dec *dec, const drmp3_uint8 *mp3, int mp3_bytes, void *pcm, drmp3dec_frame_info *info); DRMP3_API void drmp3dec_f32_to_s16(const float *in, drmp3_int16 *out, size_t num_samples); typedef enum { drmp3_seek_origin_start, drmp3_seek_origin_current } drmp3_seek_origin; typedef struct { drmp3_uint64 seekPosInBytes; drmp3_uint64 pcmFrameIndex; drmp3_uint16 mp3FramesToDiscard; drmp3_uint16 pcmFramesToDiscard; } drmp3_seek_point; typedef size_t (* drmp3_read_proc)(void* pUserData, void* pBufferOut, size_t bytesToRead); typedef drmp3_bool32 (* drmp3_seek_proc)(void* pUserData, int offset, drmp3_seek_origin origin); typedef struct { void* pUserData; void* (* onMalloc)(size_t sz, void* pUserData); void* (* onRealloc)(void* p, size_t sz, void* pUserData); void (* onFree)(void* p, void* pUserData); } drmp3_allocation_callbacks; typedef struct { drmp3_uint32 channels; drmp3_uint32 sampleRate; } drmp3_config; typedef struct { drmp3dec decoder; drmp3_uint32 channels; drmp3_uint32 sampleRate; drmp3_read_proc onRead; drmp3_seek_proc onSeek; void* pUserData; drmp3_allocation_callbacks allocationCallbacks; drmp3_uint32 mp3FrameChannels; drmp3_uint32 mp3FrameSampleRate; drmp3_uint32 pcmFramesConsumedInMP3Frame; drmp3_uint32 pcmFramesRemainingInMP3Frame; drmp3_uint8 pcmFrames[sizeof(float)*DRMP3_MAX_SAMPLES_PER_FRAME]; drmp3_uint64 currentPCMFrame; drmp3_uint64 streamCursor; drmp3_seek_point* pSeekPoints; drmp3_uint32 seekPointCount; size_t dataSize; size_t dataCapacity; size_t dataConsumed; drmp3_uint8* pData; drmp3_bool32 atEnd : 1; struct { const drmp3_uint8* pData; size_t dataSize; size_t currentReadPos; } memory; } drmp3; DRMP3_API drmp3_bool32 drmp3_init(drmp3* pMP3, drmp3_read_proc onRead, drmp3_seek_proc onSeek, void* pUserData, const drmp3_allocation_callbacks* pAllocationCallbacks); DRMP3_API drmp3_bool32 drmp3_init_memory(drmp3* pMP3, const void* pData, size_t dataSize, const drmp3_allocation_callbacks* pAllocationCallbacks); #ifndef DR_MP3_NO_STDIO DRMP3_API drmp3_bool32 drmp3_init_file(drmp3* pMP3, const char* pFilePath, const drmp3_allocation_callbacks* pAllocationCallbacks); DRMP3_API drmp3_bool32 drmp3_init_file_w(drmp3* pMP3, const wchar_t* pFilePath, const drmp3_allocation_callbacks* pAllocationCallbacks); #endif DRMP3_API void drmp3_uninit(drmp3* pMP3); DRMP3_API drmp3_uint64 drmp3_read_pcm_frames_f32(drmp3* pMP3, drmp3_uint64 framesToRead, float* pBufferOut); DRMP3_API drmp3_uint64 drmp3_read_pcm_frames_s16(drmp3* pMP3, drmp3_uint64 framesToRead, drmp3_int16* pBufferOut); DRMP3_API drmp3_bool32 drmp3_seek_to_pcm_frame(drmp3* pMP3, drmp3_uint64 frameIndex); DRMP3_API drmp3_uint64 drmp3_get_pcm_frame_count(drmp3* pMP3); DRMP3_API drmp3_uint64 drmp3_get_mp3_frame_count(drmp3* pMP3); DRMP3_API drmp3_bool32 drmp3_get_mp3_and_pcm_frame_count(drmp3* pMP3, drmp3_uint64* pMP3FrameCount, drmp3_uint64* pPCMFrameCount); DRMP3_API drmp3_bool32 drmp3_calculate_seek_points(drmp3* pMP3, drmp3_uint32* pSeekPointCount, drmp3_seek_point* pSeekPoints); DRMP3_API drmp3_bool32 drmp3_bind_seek_table(drmp3* pMP3, drmp3_uint32 seekPointCount, drmp3_seek_point* pSeekPoints); DRMP3_API float* drmp3_open_and_read_pcm_frames_f32(drmp3_read_proc onRead, drmp3_seek_proc onSeek, void* pUserData, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks); DRMP3_API drmp3_int16* drmp3_open_and_read_pcm_frames_s16(drmp3_read_proc onRead, drmp3_seek_proc onSeek, void* pUserData, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks); DRMP3_API float* drmp3_open_memory_and_read_pcm_frames_f32(const void* pData, size_t dataSize, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks); DRMP3_API drmp3_int16* drmp3_open_memory_and_read_pcm_frames_s16(const void* pData, size_t dataSize, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks); #ifndef DR_MP3_NO_STDIO DRMP3_API float* drmp3_open_file_and_read_pcm_frames_f32(const char* filePath, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks); DRMP3_API drmp3_int16* drmp3_open_file_and_read_pcm_frames_s16(const char* filePath, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks); #endif DRMP3_API void* drmp3_malloc(size_t sz, const drmp3_allocation_callbacks* pAllocationCallbacks); DRMP3_API void drmp3_free(void* p, const drmp3_allocation_callbacks* pAllocationCallbacks); #ifdef __cplusplus } #endif #endif /* dr_mp3_h end */ #endif /* MA_NO_MP3 */ /************************************************************************************************************************************************************** Decoding **************************************************************************************************************************************************************/ #ifndef MA_NO_DECODING static ma_result ma_decoder_read_bytes(ma_decoder* pDecoder, void* pBufferOut, size_t bytesToRead, size_t* pBytesRead) { MA_ASSERT(pDecoder != NULL); return pDecoder->onRead(pDecoder, pBufferOut, bytesToRead, pBytesRead); } static ma_result ma_decoder_seek_bytes(ma_decoder* pDecoder, ma_int64 byteOffset, ma_seek_origin origin) { MA_ASSERT(pDecoder != NULL); return pDecoder->onSeek(pDecoder, byteOffset, origin); } static ma_result ma_decoder_tell_bytes(ma_decoder* pDecoder, ma_int64* pCursor) { MA_ASSERT(pDecoder != NULL); if (pDecoder->onTell == NULL) { return MA_NOT_IMPLEMENTED; } return pDecoder->onTell(pDecoder, pCursor); } MA_API ma_decoding_backend_config ma_decoding_backend_config_init(ma_format preferredFormat, ma_uint32 seekPointCount) { ma_decoding_backend_config config; MA_ZERO_OBJECT(&config); config.preferredFormat = preferredFormat; config.seekPointCount = seekPointCount; return config; } MA_API ma_decoder_config ma_decoder_config_init(ma_format outputFormat, ma_uint32 outputChannels, ma_uint32 outputSampleRate) { ma_decoder_config config; MA_ZERO_OBJECT(&config); config.format = outputFormat; config.channels = outputChannels; config.sampleRate = outputSampleRate; config.resampling = ma_resampler_config_init(ma_format_unknown, 0, 0, 0, ma_resample_algorithm_linear); /* Format/channels/rate doesn't matter here. */ config.encodingFormat = ma_encoding_format_unknown; /* Note that we are intentionally leaving the channel map empty here which will cause the default channel map to be used. */ return config; } MA_API ma_decoder_config ma_decoder_config_init_default() { return ma_decoder_config_init(ma_format_unknown, 0, 0); } MA_API ma_decoder_config ma_decoder_config_init_copy(const ma_decoder_config* pConfig) { ma_decoder_config config; if (pConfig != NULL) { config = *pConfig; } else { MA_ZERO_OBJECT(&config); } return config; } static ma_result ma_decoder__init_data_converter(ma_decoder* pDecoder, const ma_decoder_config* pConfig) { ma_result result; ma_data_converter_config converterConfig; ma_format internalFormat; ma_uint32 internalChannels; ma_uint32 internalSampleRate; ma_channel internalChannelMap[MA_MAX_CHANNELS]; MA_ASSERT(pDecoder != NULL); MA_ASSERT(pConfig != NULL); result = ma_data_source_get_data_format(pDecoder->pBackend, &internalFormat, &internalChannels, &internalSampleRate, internalChannelMap, ma_countof(internalChannelMap)); if (result != MA_SUCCESS) { return result; /* Failed to retrieve the internal data format. */ } /* Make sure we're not asking for too many channels. */ if (pConfig->channels > MA_MAX_CHANNELS) { return MA_INVALID_ARGS; } /* The internal channels should have already been validated at a higher level, but we'll do it again explicitly here for safety. */ if (internalChannels > MA_MAX_CHANNELS) { return MA_INVALID_ARGS; } /* Output format. */ if (pConfig->format == ma_format_unknown) { pDecoder->outputFormat = internalFormat; } else { pDecoder->outputFormat = pConfig->format; } if (pConfig->channels == 0) { pDecoder->outputChannels = internalChannels; } else { pDecoder->outputChannels = pConfig->channels; } if (pConfig->sampleRate == 0) { pDecoder->outputSampleRate = internalSampleRate; } else { pDecoder->outputSampleRate = pConfig->sampleRate; } converterConfig = ma_data_converter_config_init( internalFormat, pDecoder->outputFormat, internalChannels, pDecoder->outputChannels, internalSampleRate, pDecoder->outputSampleRate ); converterConfig.pChannelMapIn = internalChannelMap; converterConfig.pChannelMapOut = pConfig->pChannelMap; converterConfig.channelMixMode = pConfig->channelMixMode; converterConfig.ditherMode = pConfig->ditherMode; converterConfig.allowDynamicSampleRate = MA_FALSE; /* Never allow dynamic sample rate conversion. Setting this to true will disable passthrough optimizations. */ converterConfig.resampling = pConfig->resampling; result = ma_data_converter_init(&converterConfig, &pDecoder->allocationCallbacks, &pDecoder->converter); if (result != MA_SUCCESS) { return result; } /* Now that we have the decoder we need to determine whether or not we need a heap-allocated cache. We'll need this if the data converter does not support calculation of the required input frame count. To determine support for this we'll just run a test. */ { ma_uint64 unused; result = ma_data_converter_get_required_input_frame_count(&pDecoder->converter, 1, &unused); if (result != MA_SUCCESS) { /* We were unable to calculate the required input frame count which means we'll need to use a heap-allocated cache. */ ma_uint64 inputCacheCapSizeInBytes; pDecoder->inputCacheCap = MA_DATA_CONVERTER_STACK_BUFFER_SIZE / ma_get_bytes_per_frame(internalFormat, internalChannels); /* Not strictly necessary, but keeping here for safety in case we change the default value of pDecoder->inputCacheCap. */ inputCacheCapSizeInBytes = pDecoder->inputCacheCap * ma_get_bytes_per_frame(internalFormat, internalChannels); if (inputCacheCapSizeInBytes > MA_SIZE_MAX) { ma_data_converter_uninit(&pDecoder->converter, &pDecoder->allocationCallbacks); return MA_OUT_OF_MEMORY; } pDecoder->pInputCache = ma_malloc((size_t)inputCacheCapSizeInBytes, &pDecoder->allocationCallbacks); /* Safe cast to size_t. */ if (pDecoder->pInputCache == NULL) { ma_data_converter_uninit(&pDecoder->converter, &pDecoder->allocationCallbacks); return MA_OUT_OF_MEMORY; } } } return MA_SUCCESS; } static ma_result ma_decoder_internal_on_read__custom(void* pUserData, void* pBufferOut, size_t bytesToRead, size_t* pBytesRead) { ma_decoder* pDecoder = (ma_decoder*)pUserData; MA_ASSERT(pDecoder != NULL); return ma_decoder_read_bytes(pDecoder, pBufferOut, bytesToRead, pBytesRead); } static ma_result ma_decoder_internal_on_seek__custom(void* pUserData, ma_int64 offset, ma_seek_origin origin) { ma_decoder* pDecoder = (ma_decoder*)pUserData; MA_ASSERT(pDecoder != NULL); return ma_decoder_seek_bytes(pDecoder, offset, origin); } static ma_result ma_decoder_internal_on_tell__custom(void* pUserData, ma_int64* pCursor) { ma_decoder* pDecoder = (ma_decoder*)pUserData; MA_ASSERT(pDecoder != NULL); return ma_decoder_tell_bytes(pDecoder, pCursor); } static ma_result ma_decoder_init_from_vtable(const ma_decoding_backend_vtable* pVTable, void* pVTableUserData, const ma_decoder_config* pConfig, ma_decoder* pDecoder) { ma_result result; ma_decoding_backend_config backendConfig; ma_data_source* pBackend; MA_ASSERT(pVTable != NULL); MA_ASSERT(pConfig != NULL); MA_ASSERT(pDecoder != NULL); if (pVTable->onInit == NULL) { return MA_NOT_IMPLEMENTED; } backendConfig = ma_decoding_backend_config_init(pConfig->format, pConfig->seekPointCount); result = pVTable->onInit(pVTableUserData, ma_decoder_internal_on_read__custom, ma_decoder_internal_on_seek__custom, ma_decoder_internal_on_tell__custom, pDecoder, &backendConfig, &pDecoder->allocationCallbacks, &pBackend); if (result != MA_SUCCESS) { return result; /* Failed to initialize the backend from this vtable. */ } /* Getting here means we were able to initialize the backend so we can now initialize the decoder. */ pDecoder->pBackend = pBackend; pDecoder->pBackendVTable = pVTable; pDecoder->pBackendUserData = pConfig->pCustomBackendUserData; return MA_SUCCESS; } static ma_result ma_decoder_init_custom__internal(const ma_decoder_config* pConfig, ma_decoder* pDecoder) { ma_result result = MA_NO_BACKEND; size_t ivtable; MA_ASSERT(pConfig != NULL); MA_ASSERT(pDecoder != NULL); if (pConfig->ppCustomBackendVTables == NULL) { return MA_NO_BACKEND; } /* The order each backend is listed is what defines the priority. */ for (ivtable = 0; ivtable < pConfig->customBackendCount; ivtable += 1) { const ma_decoding_backend_vtable* pVTable = pConfig->ppCustomBackendVTables[ivtable]; if (pVTable != NULL && pVTable->onInit != NULL) { result = ma_decoder_init_from_vtable(pVTable, pConfig->pCustomBackendUserData, pConfig, pDecoder); if (result == MA_SUCCESS) { return MA_SUCCESS; } else { /* Initialization failed. Move on to the next one, but seek back to the start first so the next vtable starts from the first byte of the file. */ result = ma_decoder_seek_bytes(pDecoder, 0, ma_seek_origin_start); if (result != MA_SUCCESS) { return result; /* Failed to seek back to the start. */ } } } else { /* No vtable. */ } } /* Getting here means we couldn't find a backend. */ return MA_NO_BACKEND; } /* WAV */ #ifdef dr_wav_h #define MA_HAS_WAV typedef struct { ma_data_source_base ds; ma_read_proc onRead; ma_seek_proc onSeek; ma_tell_proc onTell; void* pReadSeekTellUserData; ma_format format; /* Can be f32, s16 or s32. */ #if !defined(MA_NO_WAV) drwav dr; #endif } ma_wav; MA_API ma_result ma_wav_init(ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_wav* pWav); MA_API ma_result ma_wav_init_file(const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_wav* pWav); MA_API ma_result ma_wav_init_file_w(const wchar_t* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_wav* pWav); MA_API ma_result ma_wav_init_memory(const void* pData, size_t dataSize, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_wav* pWav); MA_API void ma_wav_uninit(ma_wav* pWav, const ma_allocation_callbacks* pAllocationCallbacks); MA_API ma_result ma_wav_read_pcm_frames(ma_wav* pWav, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); MA_API ma_result ma_wav_seek_to_pcm_frame(ma_wav* pWav, ma_uint64 frameIndex); MA_API ma_result ma_wav_get_data_format(ma_wav* pWav, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap); MA_API ma_result ma_wav_get_cursor_in_pcm_frames(ma_wav* pWav, ma_uint64* pCursor); MA_API ma_result ma_wav_get_length_in_pcm_frames(ma_wav* pWav, ma_uint64* pLength); static ma_result ma_wav_ds_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) { return ma_wav_read_pcm_frames((ma_wav*)pDataSource, pFramesOut, frameCount, pFramesRead); } static ma_result ma_wav_ds_seek(ma_data_source* pDataSource, ma_uint64 frameIndex) { return ma_wav_seek_to_pcm_frame((ma_wav*)pDataSource, frameIndex); } static ma_result ma_wav_ds_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) { return ma_wav_get_data_format((ma_wav*)pDataSource, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap); } static ma_result ma_wav_ds_get_cursor(ma_data_source* pDataSource, ma_uint64* pCursor) { return ma_wav_get_cursor_in_pcm_frames((ma_wav*)pDataSource, pCursor); } static ma_result ma_wav_ds_get_length(ma_data_source* pDataSource, ma_uint64* pLength) { return ma_wav_get_length_in_pcm_frames((ma_wav*)pDataSource, pLength); } static ma_data_source_vtable g_ma_wav_ds_vtable = { ma_wav_ds_read, ma_wav_ds_seek, ma_wav_ds_get_data_format, ma_wav_ds_get_cursor, ma_wav_ds_get_length, NULL, /* onSetLooping */ 0 }; #if !defined(MA_NO_WAV) static drwav_allocation_callbacks drwav_allocation_callbacks_from_miniaudio(const ma_allocation_callbacks* pAllocationCallbacks) { drwav_allocation_callbacks callbacks; if (pAllocationCallbacks != NULL) { callbacks.onMalloc = pAllocationCallbacks->onMalloc; callbacks.onRealloc = pAllocationCallbacks->onRealloc; callbacks.onFree = pAllocationCallbacks->onFree; callbacks.pUserData = pAllocationCallbacks->pUserData; } else { callbacks.onMalloc = ma__malloc_default; callbacks.onRealloc = ma__realloc_default; callbacks.onFree = ma__free_default; callbacks.pUserData = NULL; } return callbacks; } static size_t ma_wav_dr_callback__read(void* pUserData, void* pBufferOut, size_t bytesToRead) { ma_wav* pWav = (ma_wav*)pUserData; ma_result result; size_t bytesRead; MA_ASSERT(pWav != NULL); result = pWav->onRead(pWav->pReadSeekTellUserData, pBufferOut, bytesToRead, &bytesRead); (void)result; return bytesRead; } static drwav_bool32 ma_wav_dr_callback__seek(void* pUserData, int offset, drwav_seek_origin origin) { ma_wav* pWav = (ma_wav*)pUserData; ma_result result; ma_seek_origin maSeekOrigin; MA_ASSERT(pWav != NULL); maSeekOrigin = ma_seek_origin_start; if (origin == drwav_seek_origin_current) { maSeekOrigin = ma_seek_origin_current; } result = pWav->onSeek(pWav->pReadSeekTellUserData, offset, maSeekOrigin); if (result != MA_SUCCESS) { return MA_FALSE; } return MA_TRUE; } #endif static ma_result ma_wav_init_internal(const ma_decoding_backend_config* pConfig, ma_wav* pWav) { ma_result result; ma_data_source_config dataSourceConfig; if (pWav == NULL) { return MA_INVALID_ARGS; } MA_ZERO_OBJECT(pWav); pWav->format = ma_format_unknown; /* Use closest match to source file by default. */ if (pConfig != NULL && (pConfig->preferredFormat == ma_format_f32 || pConfig->preferredFormat == ma_format_s16 || pConfig->preferredFormat == ma_format_s32)) { pWav->format = pConfig->preferredFormat; } else { /* Getting here means something other than f32 and s16 was specified. Just leave this unset to use the default format. */ } dataSourceConfig = ma_data_source_config_init(); dataSourceConfig.vtable = &g_ma_wav_ds_vtable; result = ma_data_source_init(&dataSourceConfig, &pWav->ds); if (result != MA_SUCCESS) { return result; /* Failed to initialize the base data source. */ } return MA_SUCCESS; } MA_API ma_result ma_wav_init(ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_wav* pWav) { ma_result result; result = ma_wav_init_internal(pConfig, pWav); if (result != MA_SUCCESS) { return result; } if (onRead == NULL || onSeek == NULL) { return MA_INVALID_ARGS; /* onRead and onSeek are mandatory. */ } pWav->onRead = onRead; pWav->onSeek = onSeek; pWav->onTell = onTell; pWav->pReadSeekTellUserData = pReadSeekTellUserData; #if !defined(MA_NO_WAV) { drwav_allocation_callbacks wavAllocationCallbacks = drwav_allocation_callbacks_from_miniaudio(pAllocationCallbacks); drwav_bool32 wavResult; wavResult = drwav_init(&pWav->dr, ma_wav_dr_callback__read, ma_wav_dr_callback__seek, pWav, &wavAllocationCallbacks); if (wavResult != MA_TRUE) { return MA_INVALID_FILE; } /* If an explicit format was not specified, try picking the closest match based on the internal format. The format needs to be supported by miniaudio. */ if (pWav->format == ma_format_unknown) { switch (pWav->dr.translatedFormatTag) { case DR_WAVE_FORMAT_PCM: { if (pWav->dr.bitsPerSample == 8) { pWav->format = ma_format_u8; } else if (pWav->dr.bitsPerSample == 16) { pWav->format = ma_format_s16; } else if (pWav->dr.bitsPerSample == 24) { pWav->format = ma_format_s24; } else if (pWav->dr.bitsPerSample == 32) { pWav->format = ma_format_s32; } } break; case DR_WAVE_FORMAT_IEEE_FLOAT: { if (pWav->dr.bitsPerSample == 32) { pWav->format = ma_format_f32; } } break; default: break; } /* Fall back to f32 if we couldn't find anything. */ if (pWav->format == ma_format_unknown) { pWav->format = ma_format_f32; } } return MA_SUCCESS; } #else { /* wav is disabled. */ (void)pAllocationCallbacks; return MA_NOT_IMPLEMENTED; } #endif } MA_API ma_result ma_wav_init_file(const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_wav* pWav) { ma_result result; result = ma_wav_init_internal(pConfig, pWav); if (result != MA_SUCCESS) { return result; } #if !defined(MA_NO_WAV) { drwav_allocation_callbacks wavAllocationCallbacks = drwav_allocation_callbacks_from_miniaudio(pAllocationCallbacks); drwav_bool32 wavResult; wavResult = drwav_init_file(&pWav->dr, pFilePath, &wavAllocationCallbacks); if (wavResult != MA_TRUE) { return MA_INVALID_FILE; } return MA_SUCCESS; } #else { /* wav is disabled. */ (void)pFilePath; (void)pAllocationCallbacks; return MA_NOT_IMPLEMENTED; } #endif } MA_API ma_result ma_wav_init_file_w(const wchar_t* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_wav* pWav) { ma_result result; result = ma_wav_init_internal(pConfig, pWav); if (result != MA_SUCCESS) { return result; } #if !defined(MA_NO_WAV) { drwav_allocation_callbacks wavAllocationCallbacks = drwav_allocation_callbacks_from_miniaudio(pAllocationCallbacks); drwav_bool32 wavResult; wavResult = drwav_init_file_w(&pWav->dr, pFilePath, &wavAllocationCallbacks); if (wavResult != MA_TRUE) { return MA_INVALID_FILE; } return MA_SUCCESS; } #else { /* wav is disabled. */ (void)pFilePath; (void)pAllocationCallbacks; return MA_NOT_IMPLEMENTED; } #endif } MA_API ma_result ma_wav_init_memory(const void* pData, size_t dataSize, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_wav* pWav) { ma_result result; result = ma_wav_init_internal(pConfig, pWav); if (result != MA_SUCCESS) { return result; } #if !defined(MA_NO_WAV) { drwav_allocation_callbacks wavAllocationCallbacks = drwav_allocation_callbacks_from_miniaudio(pAllocationCallbacks); drwav_bool32 wavResult; wavResult = drwav_init_memory(&pWav->dr, pData, dataSize, &wavAllocationCallbacks); if (wavResult != MA_TRUE) { return MA_INVALID_FILE; } return MA_SUCCESS; } #else { /* wav is disabled. */ (void)pData; (void)dataSize; (void)pAllocationCallbacks; return MA_NOT_IMPLEMENTED; } #endif } MA_API void ma_wav_uninit(ma_wav* pWav, const ma_allocation_callbacks* pAllocationCallbacks) { if (pWav == NULL) { return; } (void)pAllocationCallbacks; #if !defined(MA_NO_WAV) { drwav_uninit(&pWav->dr); } #else { /* wav is disabled. Should never hit this since initialization would have failed. */ MA_ASSERT(MA_FALSE); } #endif ma_data_source_uninit(&pWav->ds); } MA_API ma_result ma_wav_read_pcm_frames(ma_wav* pWav, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) { if (pFramesRead != NULL) { *pFramesRead = 0; } if (frameCount == 0) { return MA_INVALID_ARGS; } if (pWav == NULL) { return MA_INVALID_ARGS; } #if !defined(MA_NO_WAV) { /* We always use floating point format. */ ma_result result = MA_SUCCESS; /* Must be initialized to MA_SUCCESS. */ ma_uint64 totalFramesRead = 0; ma_format format; ma_wav_get_data_format(pWav, &format, NULL, NULL, NULL, 0); switch (format) { case ma_format_f32: { totalFramesRead = drwav_read_pcm_frames_f32(&pWav->dr, frameCount, (float*)pFramesOut); } break; case ma_format_s16: { totalFramesRead = drwav_read_pcm_frames_s16(&pWav->dr, frameCount, (drwav_int16*)pFramesOut); } break; case ma_format_s32: { totalFramesRead = drwav_read_pcm_frames_s32(&pWav->dr, frameCount, (drwav_int32*)pFramesOut); } break; /* Fallback to a raw read. */ case ma_format_unknown: return MA_INVALID_OPERATION; /* <-- this should never be hit because initialization would just fall back to a supported format. */ default: { totalFramesRead = drwav_read_pcm_frames(&pWav->dr, frameCount, pFramesOut); } break; } /* In the future we'll update dr_wav to return MA_AT_END for us. */ if (totalFramesRead == 0) { result = MA_AT_END; } if (pFramesRead != NULL) { *pFramesRead = totalFramesRead; } if (result == MA_SUCCESS && totalFramesRead == 0) { result = MA_AT_END; } return result; } #else { /* wav is disabled. Should never hit this since initialization would have failed. */ MA_ASSERT(MA_FALSE); (void)pFramesOut; (void)frameCount; (void)pFramesRead; return MA_NOT_IMPLEMENTED; } #endif } MA_API ma_result ma_wav_seek_to_pcm_frame(ma_wav* pWav, ma_uint64 frameIndex) { if (pWav == NULL) { return MA_INVALID_ARGS; } #if !defined(MA_NO_WAV) { drwav_bool32 wavResult; wavResult = drwav_seek_to_pcm_frame(&pWav->dr, frameIndex); if (wavResult != DRWAV_TRUE) { return MA_ERROR; } return MA_SUCCESS; } #else { /* wav is disabled. Should never hit this since initialization would have failed. */ MA_ASSERT(MA_FALSE); (void)frameIndex; return MA_NOT_IMPLEMENTED; } #endif } MA_API ma_result ma_wav_get_data_format(ma_wav* pWav, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) { /* Defaults for safety. */ if (pFormat != NULL) { *pFormat = ma_format_unknown; } if (pChannels != NULL) { *pChannels = 0; } if (pSampleRate != NULL) { *pSampleRate = 0; } if (pChannelMap != NULL) { MA_ZERO_MEMORY(pChannelMap, sizeof(*pChannelMap) * channelMapCap); } if (pWav == NULL) { return MA_INVALID_OPERATION; } if (pFormat != NULL) { *pFormat = pWav->format; } #if !defined(MA_NO_WAV) { if (pChannels != NULL) { *pChannels = pWav->dr.channels; } if (pSampleRate != NULL) { *pSampleRate = pWav->dr.sampleRate; } if (pChannelMap != NULL) { ma_channel_map_init_standard(ma_standard_channel_map_microsoft, pChannelMap, channelMapCap, pWav->dr.channels); } return MA_SUCCESS; } #else { /* wav is disabled. Should never hit this since initialization would have failed. */ MA_ASSERT(MA_FALSE); return MA_NOT_IMPLEMENTED; } #endif } MA_API ma_result ma_wav_get_cursor_in_pcm_frames(ma_wav* pWav, ma_uint64* pCursor) { if (pCursor == NULL) { return MA_INVALID_ARGS; } *pCursor = 0; /* Safety. */ if (pWav == NULL) { return MA_INVALID_ARGS; } #if !defined(MA_NO_WAV) { drwav_result wavResult = drwav_get_cursor_in_pcm_frames(&pWav->dr, pCursor); if (wavResult != DRWAV_SUCCESS) { return (ma_result)wavResult; /* dr_wav result codes map to miniaudio's. */ } return MA_SUCCESS; } #else { /* wav is disabled. Should never hit this since initialization would have failed. */ MA_ASSERT(MA_FALSE); return MA_NOT_IMPLEMENTED; } #endif } MA_API ma_result ma_wav_get_length_in_pcm_frames(ma_wav* pWav, ma_uint64* pLength) { if (pLength == NULL) { return MA_INVALID_ARGS; } *pLength = 0; /* Safety. */ if (pWav == NULL) { return MA_INVALID_ARGS; } #if !defined(MA_NO_WAV) { drwav_result wavResult = drwav_get_length_in_pcm_frames(&pWav->dr, pLength); if (wavResult != DRWAV_SUCCESS) { return (ma_result)wavResult; /* dr_wav result codes map to miniaudio's. */ } return MA_SUCCESS; } #else { /* wav is disabled. Should never hit this since initialization would have failed. */ MA_ASSERT(MA_FALSE); return MA_NOT_IMPLEMENTED; } #endif } static ma_result ma_decoding_backend_init__wav(void* pUserData, ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend) { ma_result result; ma_wav* pWav; (void)pUserData; /* For now not using pUserData, but once we start storing the vorbis decoder state within the ma_decoder structure this will be set to the decoder so we can avoid a malloc. */ /* For now we're just allocating the decoder backend on the heap. */ pWav = (ma_wav*)ma_malloc(sizeof(*pWav), pAllocationCallbacks); if (pWav == NULL) { return MA_OUT_OF_MEMORY; } result = ma_wav_init(onRead, onSeek, onTell, pReadSeekTellUserData, pConfig, pAllocationCallbacks, pWav); if (result != MA_SUCCESS) { ma_free(pWav, pAllocationCallbacks); return result; } *ppBackend = pWav; return MA_SUCCESS; } static ma_result ma_decoding_backend_init_file__wav(void* pUserData, const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend) { ma_result result; ma_wav* pWav; (void)pUserData; /* For now not using pUserData, but once we start storing the vorbis decoder state within the ma_decoder structure this will be set to the decoder so we can avoid a malloc. */ /* For now we're just allocating the decoder backend on the heap. */ pWav = (ma_wav*)ma_malloc(sizeof(*pWav), pAllocationCallbacks); if (pWav == NULL) { return MA_OUT_OF_MEMORY; } result = ma_wav_init_file(pFilePath, pConfig, pAllocationCallbacks, pWav); if (result != MA_SUCCESS) { ma_free(pWav, pAllocationCallbacks); return result; } *ppBackend = pWav; return MA_SUCCESS; } static ma_result ma_decoding_backend_init_file_w__wav(void* pUserData, const wchar_t* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend) { ma_result result; ma_wav* pWav; (void)pUserData; /* For now not using pUserData, but once we start storing the vorbis decoder state within the ma_decoder structure this will be set to the decoder so we can avoid a malloc. */ /* For now we're just allocating the decoder backend on the heap. */ pWav = (ma_wav*)ma_malloc(sizeof(*pWav), pAllocationCallbacks); if (pWav == NULL) { return MA_OUT_OF_MEMORY; } result = ma_wav_init_file_w(pFilePath, pConfig, pAllocationCallbacks, pWav); if (result != MA_SUCCESS) { ma_free(pWav, pAllocationCallbacks); return result; } *ppBackend = pWav; return MA_SUCCESS; } static ma_result ma_decoding_backend_init_memory__wav(void* pUserData, const void* pData, size_t dataSize, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend) { ma_result result; ma_wav* pWav; (void)pUserData; /* For now not using pUserData, but once we start storing the vorbis decoder state within the ma_decoder structure this will be set to the decoder so we can avoid a malloc. */ /* For now we're just allocating the decoder backend on the heap. */ pWav = (ma_wav*)ma_malloc(sizeof(*pWav), pAllocationCallbacks); if (pWav == NULL) { return MA_OUT_OF_MEMORY; } result = ma_wav_init_memory(pData, dataSize, pConfig, pAllocationCallbacks, pWav); if (result != MA_SUCCESS) { ma_free(pWav, pAllocationCallbacks); return result; } *ppBackend = pWav; return MA_SUCCESS; } static void ma_decoding_backend_uninit__wav(void* pUserData, ma_data_source* pBackend, const ma_allocation_callbacks* pAllocationCallbacks) { ma_wav* pWav = (ma_wav*)pBackend; (void)pUserData; ma_wav_uninit(pWav, pAllocationCallbacks); ma_free(pWav, pAllocationCallbacks); } static ma_decoding_backend_vtable g_ma_decoding_backend_vtable_wav = { ma_decoding_backend_init__wav, ma_decoding_backend_init_file__wav, ma_decoding_backend_init_file_w__wav, ma_decoding_backend_init_memory__wav, ma_decoding_backend_uninit__wav }; static ma_result ma_decoder_init_wav__internal(const ma_decoder_config* pConfig, ma_decoder* pDecoder) { return ma_decoder_init_from_vtable(&g_ma_decoding_backend_vtable_wav, NULL, pConfig, pDecoder); } #endif /* dr_wav_h */ /* FLAC */ #ifdef dr_flac_h #define MA_HAS_FLAC typedef struct { ma_data_source_base ds; ma_read_proc onRead; ma_seek_proc onSeek; ma_tell_proc onTell; void* pReadSeekTellUserData; ma_format format; /* Can be f32, s16 or s32. */ #if !defined(MA_NO_FLAC) drflac* dr; #endif } ma_flac; MA_API ma_result ma_flac_init(ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_flac* pFlac); MA_API ma_result ma_flac_init_file(const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_flac* pFlac); MA_API ma_result ma_flac_init_file_w(const wchar_t* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_flac* pFlac); MA_API ma_result ma_flac_init_memory(const void* pData, size_t dataSize, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_flac* pFlac); MA_API void ma_flac_uninit(ma_flac* pFlac, const ma_allocation_callbacks* pAllocationCallbacks); MA_API ma_result ma_flac_read_pcm_frames(ma_flac* pFlac, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); MA_API ma_result ma_flac_seek_to_pcm_frame(ma_flac* pFlac, ma_uint64 frameIndex); MA_API ma_result ma_flac_get_data_format(ma_flac* pFlac, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap); MA_API ma_result ma_flac_get_cursor_in_pcm_frames(ma_flac* pFlac, ma_uint64* pCursor); MA_API ma_result ma_flac_get_length_in_pcm_frames(ma_flac* pFlac, ma_uint64* pLength); static ma_result ma_flac_ds_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) { return ma_flac_read_pcm_frames((ma_flac*)pDataSource, pFramesOut, frameCount, pFramesRead); } static ma_result ma_flac_ds_seek(ma_data_source* pDataSource, ma_uint64 frameIndex) { return ma_flac_seek_to_pcm_frame((ma_flac*)pDataSource, frameIndex); } static ma_result ma_flac_ds_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) { return ma_flac_get_data_format((ma_flac*)pDataSource, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap); } static ma_result ma_flac_ds_get_cursor(ma_data_source* pDataSource, ma_uint64* pCursor) { return ma_flac_get_cursor_in_pcm_frames((ma_flac*)pDataSource, pCursor); } static ma_result ma_flac_ds_get_length(ma_data_source* pDataSource, ma_uint64* pLength) { return ma_flac_get_length_in_pcm_frames((ma_flac*)pDataSource, pLength); } static ma_data_source_vtable g_ma_flac_ds_vtable = { ma_flac_ds_read, ma_flac_ds_seek, ma_flac_ds_get_data_format, ma_flac_ds_get_cursor, ma_flac_ds_get_length, NULL, /* onSetLooping */ 0 }; #if !defined(MA_NO_FLAC) static drflac_allocation_callbacks drflac_allocation_callbacks_from_miniaudio(const ma_allocation_callbacks* pAllocationCallbacks) { drflac_allocation_callbacks callbacks; if (pAllocationCallbacks != NULL) { callbacks.onMalloc = pAllocationCallbacks->onMalloc; callbacks.onRealloc = pAllocationCallbacks->onRealloc; callbacks.onFree = pAllocationCallbacks->onFree; callbacks.pUserData = pAllocationCallbacks->pUserData; } else { callbacks.onMalloc = ma__malloc_default; callbacks.onRealloc = ma__realloc_default; callbacks.onFree = ma__free_default; callbacks.pUserData = NULL; } return callbacks; } static size_t ma_flac_dr_callback__read(void* pUserData, void* pBufferOut, size_t bytesToRead) { ma_flac* pFlac = (ma_flac*)pUserData; ma_result result; size_t bytesRead; MA_ASSERT(pFlac != NULL); result = pFlac->onRead(pFlac->pReadSeekTellUserData, pBufferOut, bytesToRead, &bytesRead); (void)result; return bytesRead; } static drflac_bool32 ma_flac_dr_callback__seek(void* pUserData, int offset, drflac_seek_origin origin) { ma_flac* pFlac = (ma_flac*)pUserData; ma_result result; ma_seek_origin maSeekOrigin; MA_ASSERT(pFlac != NULL); maSeekOrigin = ma_seek_origin_start; if (origin == drflac_seek_origin_current) { maSeekOrigin = ma_seek_origin_current; } result = pFlac->onSeek(pFlac->pReadSeekTellUserData, offset, maSeekOrigin); if (result != MA_SUCCESS) { return MA_FALSE; } return MA_TRUE; } #endif static ma_result ma_flac_init_internal(const ma_decoding_backend_config* pConfig, ma_flac* pFlac) { ma_result result; ma_data_source_config dataSourceConfig; if (pFlac == NULL) { return MA_INVALID_ARGS; } MA_ZERO_OBJECT(pFlac); pFlac->format = ma_format_f32; /* f32 by default. */ if (pConfig != NULL && (pConfig->preferredFormat == ma_format_f32 || pConfig->preferredFormat == ma_format_s16 || pConfig->preferredFormat == ma_format_s32)) { pFlac->format = pConfig->preferredFormat; } else { /* Getting here means something other than f32 and s16 was specified. Just leave this unset to use the default format. */ } dataSourceConfig = ma_data_source_config_init(); dataSourceConfig.vtable = &g_ma_flac_ds_vtable; result = ma_data_source_init(&dataSourceConfig, &pFlac->ds); if (result != MA_SUCCESS) { return result; /* Failed to initialize the base data source. */ } return MA_SUCCESS; } MA_API ma_result ma_flac_init(ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_flac* pFlac) { ma_result result; result = ma_flac_init_internal(pConfig, pFlac); if (result != MA_SUCCESS) { return result; } if (onRead == NULL || onSeek == NULL) { return MA_INVALID_ARGS; /* onRead and onSeek are mandatory. */ } pFlac->onRead = onRead; pFlac->onSeek = onSeek; pFlac->onTell = onTell; pFlac->pReadSeekTellUserData = pReadSeekTellUserData; #if !defined(MA_NO_FLAC) { drflac_allocation_callbacks flacAllocationCallbacks = drflac_allocation_callbacks_from_miniaudio(pAllocationCallbacks); pFlac->dr = drflac_open(ma_flac_dr_callback__read, ma_flac_dr_callback__seek, pFlac, &flacAllocationCallbacks); if (pFlac->dr == NULL) { return MA_INVALID_FILE; } return MA_SUCCESS; } #else { /* flac is disabled. */ (void)pAllocationCallbacks; return MA_NOT_IMPLEMENTED; } #endif } MA_API ma_result ma_flac_init_file(const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_flac* pFlac) { ma_result result; result = ma_flac_init_internal(pConfig, pFlac); if (result != MA_SUCCESS) { return result; } #if !defined(MA_NO_FLAC) { drflac_allocation_callbacks flacAllocationCallbacks = drflac_allocation_callbacks_from_miniaudio(pAllocationCallbacks); pFlac->dr = drflac_open_file(pFilePath, &flacAllocationCallbacks); if (pFlac->dr == NULL) { return MA_INVALID_FILE; } return MA_SUCCESS; } #else { /* flac is disabled. */ (void)pFilePath; (void)pAllocationCallbacks; return MA_NOT_IMPLEMENTED; } #endif } MA_API ma_result ma_flac_init_file_w(const wchar_t* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_flac* pFlac) { ma_result result; result = ma_flac_init_internal(pConfig, pFlac); if (result != MA_SUCCESS) { return result; } #if !defined(MA_NO_FLAC) { drflac_allocation_callbacks flacAllocationCallbacks = drflac_allocation_callbacks_from_miniaudio(pAllocationCallbacks); pFlac->dr = drflac_open_file_w(pFilePath, &flacAllocationCallbacks); if (pFlac->dr == NULL) { return MA_INVALID_FILE; } return MA_SUCCESS; } #else { /* flac is disabled. */ (void)pFilePath; (void)pAllocationCallbacks; return MA_NOT_IMPLEMENTED; } #endif } MA_API ma_result ma_flac_init_memory(const void* pData, size_t dataSize, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_flac* pFlac) { ma_result result; result = ma_flac_init_internal(pConfig, pFlac); if (result != MA_SUCCESS) { return result; } #if !defined(MA_NO_FLAC) { drflac_allocation_callbacks flacAllocationCallbacks = drflac_allocation_callbacks_from_miniaudio(pAllocationCallbacks); pFlac->dr = drflac_open_memory(pData, dataSize, &flacAllocationCallbacks); if (pFlac->dr == NULL) { return MA_INVALID_FILE; } return MA_SUCCESS; } #else { /* flac is disabled. */ (void)pData; (void)dataSize; (void)pAllocationCallbacks; return MA_NOT_IMPLEMENTED; } #endif } MA_API void ma_flac_uninit(ma_flac* pFlac, const ma_allocation_callbacks* pAllocationCallbacks) { if (pFlac == NULL) { return; } (void)pAllocationCallbacks; #if !defined(MA_NO_FLAC) { drflac_close(pFlac->dr); } #else { /* flac is disabled. Should never hit this since initialization would have failed. */ MA_ASSERT(MA_FALSE); } #endif ma_data_source_uninit(&pFlac->ds); } MA_API ma_result ma_flac_read_pcm_frames(ma_flac* pFlac, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) { if (pFramesRead != NULL) { *pFramesRead = 0; } if (frameCount == 0) { return MA_INVALID_ARGS; } if (pFlac == NULL) { return MA_INVALID_ARGS; } #if !defined(MA_NO_FLAC) { /* We always use floating point format. */ ma_result result = MA_SUCCESS; /* Must be initialized to MA_SUCCESS. */ ma_uint64 totalFramesRead = 0; ma_format format; ma_flac_get_data_format(pFlac, &format, NULL, NULL, NULL, 0); switch (format) { case ma_format_f32: { totalFramesRead = drflac_read_pcm_frames_f32(pFlac->dr, frameCount, (float*)pFramesOut); } break; case ma_format_s16: { totalFramesRead = drflac_read_pcm_frames_s16(pFlac->dr, frameCount, (drflac_int16*)pFramesOut); } break; case ma_format_s32: { totalFramesRead = drflac_read_pcm_frames_s32(pFlac->dr, frameCount, (drflac_int32*)pFramesOut); } break; case ma_format_u8: case ma_format_s24: case ma_format_unknown: default: { return MA_INVALID_OPERATION; }; } /* In the future we'll update dr_flac to return MA_AT_END for us. */ if (totalFramesRead == 0) { result = MA_AT_END; } if (pFramesRead != NULL) { *pFramesRead = totalFramesRead; } if (result == MA_SUCCESS && totalFramesRead == 0) { result = MA_AT_END; } return result; } #else { /* flac is disabled. Should never hit this since initialization would have failed. */ MA_ASSERT(MA_FALSE); (void)pFramesOut; (void)frameCount; (void)pFramesRead; return MA_NOT_IMPLEMENTED; } #endif } MA_API ma_result ma_flac_seek_to_pcm_frame(ma_flac* pFlac, ma_uint64 frameIndex) { if (pFlac == NULL) { return MA_INVALID_ARGS; } #if !defined(MA_NO_FLAC) { drflac_bool32 flacResult; flacResult = drflac_seek_to_pcm_frame(pFlac->dr, frameIndex); if (flacResult != DRFLAC_TRUE) { return MA_ERROR; } return MA_SUCCESS; } #else { /* flac is disabled. Should never hit this since initialization would have failed. */ MA_ASSERT(MA_FALSE); (void)frameIndex; return MA_NOT_IMPLEMENTED; } #endif } MA_API ma_result ma_flac_get_data_format(ma_flac* pFlac, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) { /* Defaults for safety. */ if (pFormat != NULL) { *pFormat = ma_format_unknown; } if (pChannels != NULL) { *pChannels = 0; } if (pSampleRate != NULL) { *pSampleRate = 0; } if (pChannelMap != NULL) { MA_ZERO_MEMORY(pChannelMap, sizeof(*pChannelMap) * channelMapCap); } if (pFlac == NULL) { return MA_INVALID_OPERATION; } if (pFormat != NULL) { *pFormat = pFlac->format; } #if !defined(MA_NO_FLAC) { if (pChannels != NULL) { *pChannels = pFlac->dr->channels; } if (pSampleRate != NULL) { *pSampleRate = pFlac->dr->sampleRate; } if (pChannelMap != NULL) { ma_channel_map_init_standard(ma_standard_channel_map_microsoft, pChannelMap, channelMapCap, pFlac->dr->channels); } return MA_SUCCESS; } #else { /* flac is disabled. Should never hit this since initialization would have failed. */ MA_ASSERT(MA_FALSE); return MA_NOT_IMPLEMENTED; } #endif } MA_API ma_result ma_flac_get_cursor_in_pcm_frames(ma_flac* pFlac, ma_uint64* pCursor) { if (pCursor == NULL) { return MA_INVALID_ARGS; } *pCursor = 0; /* Safety. */ if (pFlac == NULL) { return MA_INVALID_ARGS; } #if !defined(MA_NO_FLAC) { *pCursor = pFlac->dr->currentPCMFrame; return MA_SUCCESS; } #else { /* flac is disabled. Should never hit this since initialization would have failed. */ MA_ASSERT(MA_FALSE); return MA_NOT_IMPLEMENTED; } #endif } MA_API ma_result ma_flac_get_length_in_pcm_frames(ma_flac* pFlac, ma_uint64* pLength) { if (pLength == NULL) { return MA_INVALID_ARGS; } *pLength = 0; /* Safety. */ if (pFlac == NULL) { return MA_INVALID_ARGS; } #if !defined(MA_NO_FLAC) { *pLength = pFlac->dr->totalPCMFrameCount; return MA_SUCCESS; } #else { /* flac is disabled. Should never hit this since initialization would have failed. */ MA_ASSERT(MA_FALSE); return MA_NOT_IMPLEMENTED; } #endif } static ma_result ma_decoding_backend_init__flac(void* pUserData, ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend) { ma_result result; ma_flac* pFlac; (void)pUserData; /* For now not using pUserData, but once we start storing the vorbis decoder state within the ma_decoder structure this will be set to the decoder so we can avoid a malloc. */ /* For now we're just allocating the decoder backend on the heap. */ pFlac = (ma_flac*)ma_malloc(sizeof(*pFlac), pAllocationCallbacks); if (pFlac == NULL) { return MA_OUT_OF_MEMORY; } result = ma_flac_init(onRead, onSeek, onTell, pReadSeekTellUserData, pConfig, pAllocationCallbacks, pFlac); if (result != MA_SUCCESS) { ma_free(pFlac, pAllocationCallbacks); return result; } *ppBackend = pFlac; return MA_SUCCESS; } static ma_result ma_decoding_backend_init_file__flac(void* pUserData, const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend) { ma_result result; ma_flac* pFlac; (void)pUserData; /* For now not using pUserData, but once we start storing the vorbis decoder state within the ma_decoder structure this will be set to the decoder so we can avoid a malloc. */ /* For now we're just allocating the decoder backend on the heap. */ pFlac = (ma_flac*)ma_malloc(sizeof(*pFlac), pAllocationCallbacks); if (pFlac == NULL) { return MA_OUT_OF_MEMORY; } result = ma_flac_init_file(pFilePath, pConfig, pAllocationCallbacks, pFlac); if (result != MA_SUCCESS) { ma_free(pFlac, pAllocationCallbacks); return result; } *ppBackend = pFlac; return MA_SUCCESS; } static ma_result ma_decoding_backend_init_file_w__flac(void* pUserData, const wchar_t* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend) { ma_result result; ma_flac* pFlac; (void)pUserData; /* For now not using pUserData, but once we start storing the vorbis decoder state within the ma_decoder structure this will be set to the decoder so we can avoid a malloc. */ /* For now we're just allocating the decoder backend on the heap. */ pFlac = (ma_flac*)ma_malloc(sizeof(*pFlac), pAllocationCallbacks); if (pFlac == NULL) { return MA_OUT_OF_MEMORY; } result = ma_flac_init_file_w(pFilePath, pConfig, pAllocationCallbacks, pFlac); if (result != MA_SUCCESS) { ma_free(pFlac, pAllocationCallbacks); return result; } *ppBackend = pFlac; return MA_SUCCESS; } static ma_result ma_decoding_backend_init_memory__flac(void* pUserData, const void* pData, size_t dataSize, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend) { ma_result result; ma_flac* pFlac; (void)pUserData; /* For now not using pUserData, but once we start storing the vorbis decoder state within the ma_decoder structure this will be set to the decoder so we can avoid a malloc. */ /* For now we're just allocating the decoder backend on the heap. */ pFlac = (ma_flac*)ma_malloc(sizeof(*pFlac), pAllocationCallbacks); if (pFlac == NULL) { return MA_OUT_OF_MEMORY; } result = ma_flac_init_memory(pData, dataSize, pConfig, pAllocationCallbacks, pFlac); if (result != MA_SUCCESS) { ma_free(pFlac, pAllocationCallbacks); return result; } *ppBackend = pFlac; return MA_SUCCESS; } static void ma_decoding_backend_uninit__flac(void* pUserData, ma_data_source* pBackend, const ma_allocation_callbacks* pAllocationCallbacks) { ma_flac* pFlac = (ma_flac*)pBackend; (void)pUserData; ma_flac_uninit(pFlac, pAllocationCallbacks); ma_free(pFlac, pAllocationCallbacks); } static ma_decoding_backend_vtable g_ma_decoding_backend_vtable_flac = { ma_decoding_backend_init__flac, ma_decoding_backend_init_file__flac, ma_decoding_backend_init_file_w__flac, ma_decoding_backend_init_memory__flac, ma_decoding_backend_uninit__flac }; static ma_result ma_decoder_init_flac__internal(const ma_decoder_config* pConfig, ma_decoder* pDecoder) { return ma_decoder_init_from_vtable(&g_ma_decoding_backend_vtable_flac, NULL, pConfig, pDecoder); } #endif /* dr_flac_h */ /* MP3 */ #ifdef dr_mp3_h #define MA_HAS_MP3 typedef struct { ma_data_source_base ds; ma_read_proc onRead; ma_seek_proc onSeek; ma_tell_proc onTell; void* pReadSeekTellUserData; ma_format format; /* Can be f32 or s16. */ #if !defined(MA_NO_MP3) drmp3 dr; drmp3_uint32 seekPointCount; drmp3_seek_point* pSeekPoints; /* Only used if seek table generation is used. */ #endif } ma_mp3; MA_API ma_result ma_mp3_init(ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_mp3* pMP3); MA_API ma_result ma_mp3_init_file(const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_mp3* pMP3); MA_API ma_result ma_mp3_init_file_w(const wchar_t* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_mp3* pMP3); MA_API ma_result ma_mp3_init_memory(const void* pData, size_t dataSize, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_mp3* pMP3); MA_API void ma_mp3_uninit(ma_mp3* pMP3, const ma_allocation_callbacks* pAllocationCallbacks); MA_API ma_result ma_mp3_read_pcm_frames(ma_mp3* pMP3, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); MA_API ma_result ma_mp3_seek_to_pcm_frame(ma_mp3* pMP3, ma_uint64 frameIndex); MA_API ma_result ma_mp3_get_data_format(ma_mp3* pMP3, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap); MA_API ma_result ma_mp3_get_cursor_in_pcm_frames(ma_mp3* pMP3, ma_uint64* pCursor); MA_API ma_result ma_mp3_get_length_in_pcm_frames(ma_mp3* pMP3, ma_uint64* pLength); static ma_result ma_mp3_ds_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) { return ma_mp3_read_pcm_frames((ma_mp3*)pDataSource, pFramesOut, frameCount, pFramesRead); } static ma_result ma_mp3_ds_seek(ma_data_source* pDataSource, ma_uint64 frameIndex) { return ma_mp3_seek_to_pcm_frame((ma_mp3*)pDataSource, frameIndex); } static ma_result ma_mp3_ds_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) { return ma_mp3_get_data_format((ma_mp3*)pDataSource, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap); } static ma_result ma_mp3_ds_get_cursor(ma_data_source* pDataSource, ma_uint64* pCursor) { return ma_mp3_get_cursor_in_pcm_frames((ma_mp3*)pDataSource, pCursor); } static ma_result ma_mp3_ds_get_length(ma_data_source* pDataSource, ma_uint64* pLength) { return ma_mp3_get_length_in_pcm_frames((ma_mp3*)pDataSource, pLength); } static ma_data_source_vtable g_ma_mp3_ds_vtable = { ma_mp3_ds_read, ma_mp3_ds_seek, ma_mp3_ds_get_data_format, ma_mp3_ds_get_cursor, ma_mp3_ds_get_length, NULL, /* onSetLooping */ 0 }; #if !defined(MA_NO_MP3) static drmp3_allocation_callbacks drmp3_allocation_callbacks_from_miniaudio(const ma_allocation_callbacks* pAllocationCallbacks) { drmp3_allocation_callbacks callbacks; if (pAllocationCallbacks != NULL) { callbacks.onMalloc = pAllocationCallbacks->onMalloc; callbacks.onRealloc = pAllocationCallbacks->onRealloc; callbacks.onFree = pAllocationCallbacks->onFree; callbacks.pUserData = pAllocationCallbacks->pUserData; } else { callbacks.onMalloc = ma__malloc_default; callbacks.onRealloc = ma__realloc_default; callbacks.onFree = ma__free_default; callbacks.pUserData = NULL; } return callbacks; } static size_t ma_mp3_dr_callback__read(void* pUserData, void* pBufferOut, size_t bytesToRead) { ma_mp3* pMP3 = (ma_mp3*)pUserData; ma_result result; size_t bytesRead; MA_ASSERT(pMP3 != NULL); result = pMP3->onRead(pMP3->pReadSeekTellUserData, pBufferOut, bytesToRead, &bytesRead); (void)result; return bytesRead; } static drmp3_bool32 ma_mp3_dr_callback__seek(void* pUserData, int offset, drmp3_seek_origin origin) { ma_mp3* pMP3 = (ma_mp3*)pUserData; ma_result result; ma_seek_origin maSeekOrigin; MA_ASSERT(pMP3 != NULL); maSeekOrigin = ma_seek_origin_start; if (origin == drmp3_seek_origin_current) { maSeekOrigin = ma_seek_origin_current; } result = pMP3->onSeek(pMP3->pReadSeekTellUserData, offset, maSeekOrigin); if (result != MA_SUCCESS) { return MA_FALSE; } return MA_TRUE; } #endif static ma_result ma_mp3_init_internal(const ma_decoding_backend_config* pConfig, ma_mp3* pMP3) { ma_result result; ma_data_source_config dataSourceConfig; if (pMP3 == NULL) { return MA_INVALID_ARGS; } MA_ZERO_OBJECT(pMP3); pMP3->format = ma_format_f32; /* f32 by default. */ if (pConfig != NULL && (pConfig->preferredFormat == ma_format_f32 || pConfig->preferredFormat == ma_format_s16)) { pMP3->format = pConfig->preferredFormat; } else { /* Getting here means something other than f32 and s16 was specified. Just leave this unset to use the default format. */ } dataSourceConfig = ma_data_source_config_init(); dataSourceConfig.vtable = &g_ma_mp3_ds_vtable; result = ma_data_source_init(&dataSourceConfig, &pMP3->ds); if (result != MA_SUCCESS) { return result; /* Failed to initialize the base data source. */ } return MA_SUCCESS; } static ma_result ma_mp3_generate_seek_table(ma_mp3* pMP3, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks) { drmp3_bool32 mp3Result; drmp3_uint32 seekPointCount = 0; drmp3_seek_point* pSeekPoints = NULL; MA_ASSERT(pMP3 != NULL); MA_ASSERT(pConfig != NULL); seekPointCount = pConfig->seekPointCount; if (seekPointCount > 0) { pSeekPoints = (drmp3_seek_point*)ma_malloc(sizeof(*pMP3->pSeekPoints) * seekPointCount, pAllocationCallbacks); if (pSeekPoints == NULL) { return MA_OUT_OF_MEMORY; } } mp3Result = drmp3_calculate_seek_points(&pMP3->dr, &seekPointCount, pSeekPoints); if (mp3Result != MA_TRUE) { ma_free(pSeekPoints, pAllocationCallbacks); return MA_ERROR; } mp3Result = drmp3_bind_seek_table(&pMP3->dr, seekPointCount, pSeekPoints); if (mp3Result != MA_TRUE) { ma_free(pSeekPoints, pAllocationCallbacks); return MA_ERROR; } pMP3->seekPointCount = seekPointCount; pMP3->pSeekPoints = pSeekPoints; return MA_SUCCESS; } MA_API ma_result ma_mp3_init(ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_mp3* pMP3) { ma_result result; result = ma_mp3_init_internal(pConfig, pMP3); if (result != MA_SUCCESS) { return result; } if (onRead == NULL || onSeek == NULL) { return MA_INVALID_ARGS; /* onRead and onSeek are mandatory. */ } pMP3->onRead = onRead; pMP3->onSeek = onSeek; pMP3->onTell = onTell; pMP3->pReadSeekTellUserData = pReadSeekTellUserData; #if !defined(MA_NO_MP3) { drmp3_allocation_callbacks mp3AllocationCallbacks = drmp3_allocation_callbacks_from_miniaudio(pAllocationCallbacks); drmp3_bool32 mp3Result; mp3Result = drmp3_init(&pMP3->dr, ma_mp3_dr_callback__read, ma_mp3_dr_callback__seek, pMP3, &mp3AllocationCallbacks); if (mp3Result != MA_TRUE) { return MA_INVALID_FILE; } ma_mp3_generate_seek_table(pMP3, pConfig, pAllocationCallbacks); return MA_SUCCESS; } #else { /* mp3 is disabled. */ (void)pAllocationCallbacks; return MA_NOT_IMPLEMENTED; } #endif } MA_API ma_result ma_mp3_init_file(const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_mp3* pMP3) { ma_result result; result = ma_mp3_init_internal(pConfig, pMP3); if (result != MA_SUCCESS) { return result; } #if !defined(MA_NO_MP3) { drmp3_allocation_callbacks mp3AllocationCallbacks = drmp3_allocation_callbacks_from_miniaudio(pAllocationCallbacks); drmp3_bool32 mp3Result; mp3Result = drmp3_init_file(&pMP3->dr, pFilePath, &mp3AllocationCallbacks); if (mp3Result != MA_TRUE) { return MA_INVALID_FILE; } ma_mp3_generate_seek_table(pMP3, pConfig, pAllocationCallbacks); return MA_SUCCESS; } #else { /* mp3 is disabled. */ (void)pFilePath; (void)pAllocationCallbacks; return MA_NOT_IMPLEMENTED; } #endif } MA_API ma_result ma_mp3_init_file_w(const wchar_t* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_mp3* pMP3) { ma_result result; result = ma_mp3_init_internal(pConfig, pMP3); if (result != MA_SUCCESS) { return result; } #if !defined(MA_NO_MP3) { drmp3_allocation_callbacks mp3AllocationCallbacks = drmp3_allocation_callbacks_from_miniaudio(pAllocationCallbacks); drmp3_bool32 mp3Result; mp3Result = drmp3_init_file_w(&pMP3->dr, pFilePath, &mp3AllocationCallbacks); if (mp3Result != MA_TRUE) { return MA_INVALID_FILE; } ma_mp3_generate_seek_table(pMP3, pConfig, pAllocationCallbacks); return MA_SUCCESS; } #else { /* mp3 is disabled. */ (void)pFilePath; (void)pAllocationCallbacks; return MA_NOT_IMPLEMENTED; } #endif } MA_API ma_result ma_mp3_init_memory(const void* pData, size_t dataSize, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_mp3* pMP3) { ma_result result; result = ma_mp3_init_internal(pConfig, pMP3); if (result != MA_SUCCESS) { return result; } #if !defined(MA_NO_MP3) { drmp3_allocation_callbacks mp3AllocationCallbacks = drmp3_allocation_callbacks_from_miniaudio(pAllocationCallbacks); drmp3_bool32 mp3Result; mp3Result = drmp3_init_memory(&pMP3->dr, pData, dataSize, &mp3AllocationCallbacks); if (mp3Result != MA_TRUE) { return MA_INVALID_FILE; } ma_mp3_generate_seek_table(pMP3, pConfig, pAllocationCallbacks); return MA_SUCCESS; } #else { /* mp3 is disabled. */ (void)pData; (void)dataSize; (void)pAllocationCallbacks; return MA_NOT_IMPLEMENTED; } #endif } MA_API void ma_mp3_uninit(ma_mp3* pMP3, const ma_allocation_callbacks* pAllocationCallbacks) { if (pMP3 == NULL) { return; } #if !defined(MA_NO_MP3) { drmp3_uninit(&pMP3->dr); } #else { /* mp3 is disabled. Should never hit this since initialization would have failed. */ MA_ASSERT(MA_FALSE); } #endif /* Seek points need to be freed after the MP3 decoder has been uninitialized to ensure they're no longer being referenced. */ ma_free(pMP3->pSeekPoints, pAllocationCallbacks); ma_data_source_uninit(&pMP3->ds); } MA_API ma_result ma_mp3_read_pcm_frames(ma_mp3* pMP3, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) { if (pFramesRead != NULL) { *pFramesRead = 0; } if (frameCount == 0) { return MA_INVALID_ARGS; } if (pMP3 == NULL) { return MA_INVALID_ARGS; } #if !defined(MA_NO_MP3) { /* We always use floating point format. */ ma_result result = MA_SUCCESS; /* Must be initialized to MA_SUCCESS. */ ma_uint64 totalFramesRead = 0; ma_format format; ma_mp3_get_data_format(pMP3, &format, NULL, NULL, NULL, 0); switch (format) { case ma_format_f32: { totalFramesRead = drmp3_read_pcm_frames_f32(&pMP3->dr, frameCount, (float*)pFramesOut); } break; case ma_format_s16: { totalFramesRead = drmp3_read_pcm_frames_s16(&pMP3->dr, frameCount, (drmp3_int16*)pFramesOut); } break; case ma_format_u8: case ma_format_s24: case ma_format_s32: case ma_format_unknown: default: { return MA_INVALID_OPERATION; }; } /* In the future we'll update dr_mp3 to return MA_AT_END for us. */ if (totalFramesRead == 0) { result = MA_AT_END; } if (pFramesRead != NULL) { *pFramesRead = totalFramesRead; } return result; } #else { /* mp3 is disabled. Should never hit this since initialization would have failed. */ MA_ASSERT(MA_FALSE); (void)pFramesOut; (void)frameCount; (void)pFramesRead; return MA_NOT_IMPLEMENTED; } #endif } MA_API ma_result ma_mp3_seek_to_pcm_frame(ma_mp3* pMP3, ma_uint64 frameIndex) { if (pMP3 == NULL) { return MA_INVALID_ARGS; } #if !defined(MA_NO_MP3) { drmp3_bool32 mp3Result; mp3Result = drmp3_seek_to_pcm_frame(&pMP3->dr, frameIndex); if (mp3Result != DRMP3_TRUE) { return MA_ERROR; } return MA_SUCCESS; } #else { /* mp3 is disabled. Should never hit this since initialization would have failed. */ MA_ASSERT(MA_FALSE); (void)frameIndex; return MA_NOT_IMPLEMENTED; } #endif } MA_API ma_result ma_mp3_get_data_format(ma_mp3* pMP3, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) { /* Defaults for safety. */ if (pFormat != NULL) { *pFormat = ma_format_unknown; } if (pChannels != NULL) { *pChannels = 0; } if (pSampleRate != NULL) { *pSampleRate = 0; } if (pChannelMap != NULL) { MA_ZERO_MEMORY(pChannelMap, sizeof(*pChannelMap) * channelMapCap); } if (pMP3 == NULL) { return MA_INVALID_OPERATION; } if (pFormat != NULL) { *pFormat = pMP3->format; } #if !defined(MA_NO_MP3) { if (pChannels != NULL) { *pChannels = pMP3->dr.channels; } if (pSampleRate != NULL) { *pSampleRate = pMP3->dr.sampleRate; } if (pChannelMap != NULL) { ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, pMP3->dr.channels); } return MA_SUCCESS; } #else { /* mp3 is disabled. Should never hit this since initialization would have failed. */ MA_ASSERT(MA_FALSE); return MA_NOT_IMPLEMENTED; } #endif } MA_API ma_result ma_mp3_get_cursor_in_pcm_frames(ma_mp3* pMP3, ma_uint64* pCursor) { if (pCursor == NULL) { return MA_INVALID_ARGS; } *pCursor = 0; /* Safety. */ if (pMP3 == NULL) { return MA_INVALID_ARGS; } #if !defined(MA_NO_MP3) { *pCursor = pMP3->dr.currentPCMFrame; return MA_SUCCESS; } #else { /* mp3 is disabled. Should never hit this since initialization would have failed. */ MA_ASSERT(MA_FALSE); return MA_NOT_IMPLEMENTED; } #endif } MA_API ma_result ma_mp3_get_length_in_pcm_frames(ma_mp3* pMP3, ma_uint64* pLength) { if (pLength == NULL) { return MA_INVALID_ARGS; } *pLength = 0; /* Safety. */ if (pMP3 == NULL) { return MA_INVALID_ARGS; } #if !defined(MA_NO_MP3) { *pLength = drmp3_get_pcm_frame_count(&pMP3->dr); return MA_SUCCESS; } #else { /* mp3 is disabled. Should never hit this since initialization would have failed. */ MA_ASSERT(MA_FALSE); return MA_NOT_IMPLEMENTED; } #endif } static ma_result ma_decoding_backend_init__mp3(void* pUserData, ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend) { ma_result result; ma_mp3* pMP3; (void)pUserData; /* For now not using pUserData, but once we start storing the vorbis decoder state within the ma_decoder structure this will be set to the decoder so we can avoid a malloc. */ /* For now we're just allocating the decoder backend on the heap. */ pMP3 = (ma_mp3*)ma_malloc(sizeof(*pMP3), pAllocationCallbacks); if (pMP3 == NULL) { return MA_OUT_OF_MEMORY; } result = ma_mp3_init(onRead, onSeek, onTell, pReadSeekTellUserData, pConfig, pAllocationCallbacks, pMP3); if (result != MA_SUCCESS) { ma_free(pMP3, pAllocationCallbacks); return result; } *ppBackend = pMP3; return MA_SUCCESS; } static ma_result ma_decoding_backend_init_file__mp3(void* pUserData, const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend) { ma_result result; ma_mp3* pMP3; (void)pUserData; /* For now not using pUserData, but once we start storing the vorbis decoder state within the ma_decoder structure this will be set to the decoder so we can avoid a malloc. */ /* For now we're just allocating the decoder backend on the heap. */ pMP3 = (ma_mp3*)ma_malloc(sizeof(*pMP3), pAllocationCallbacks); if (pMP3 == NULL) { return MA_OUT_OF_MEMORY; } result = ma_mp3_init_file(pFilePath, pConfig, pAllocationCallbacks, pMP3); if (result != MA_SUCCESS) { ma_free(pMP3, pAllocationCallbacks); return result; } *ppBackend = pMP3; return MA_SUCCESS; } static ma_result ma_decoding_backend_init_file_w__mp3(void* pUserData, const wchar_t* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend) { ma_result result; ma_mp3* pMP3; (void)pUserData; /* For now not using pUserData, but once we start storing the vorbis decoder state within the ma_decoder structure this will be set to the decoder so we can avoid a malloc. */ /* For now we're just allocating the decoder backend on the heap. */ pMP3 = (ma_mp3*)ma_malloc(sizeof(*pMP3), pAllocationCallbacks); if (pMP3 == NULL) { return MA_OUT_OF_MEMORY; } result = ma_mp3_init_file_w(pFilePath, pConfig, pAllocationCallbacks, pMP3); if (result != MA_SUCCESS) { ma_free(pMP3, pAllocationCallbacks); return result; } *ppBackend = pMP3; return MA_SUCCESS; } static ma_result ma_decoding_backend_init_memory__mp3(void* pUserData, const void* pData, size_t dataSize, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend) { ma_result result; ma_mp3* pMP3; (void)pUserData; /* For now not using pUserData, but once we start storing the vorbis decoder state within the ma_decoder structure this will be set to the decoder so we can avoid a malloc. */ /* For now we're just allocating the decoder backend on the heap. */ pMP3 = (ma_mp3*)ma_malloc(sizeof(*pMP3), pAllocationCallbacks); if (pMP3 == NULL) { return MA_OUT_OF_MEMORY; } result = ma_mp3_init_memory(pData, dataSize, pConfig, pAllocationCallbacks, pMP3); if (result != MA_SUCCESS) { ma_free(pMP3, pAllocationCallbacks); return result; } *ppBackend = pMP3; return MA_SUCCESS; } static void ma_decoding_backend_uninit__mp3(void* pUserData, ma_data_source* pBackend, const ma_allocation_callbacks* pAllocationCallbacks) { ma_mp3* pMP3 = (ma_mp3*)pBackend; (void)pUserData; ma_mp3_uninit(pMP3, pAllocationCallbacks); ma_free(pMP3, pAllocationCallbacks); } static ma_decoding_backend_vtable g_ma_decoding_backend_vtable_mp3 = { ma_decoding_backend_init__mp3, ma_decoding_backend_init_file__mp3, ma_decoding_backend_init_file_w__mp3, ma_decoding_backend_init_memory__mp3, ma_decoding_backend_uninit__mp3 }; static ma_result ma_decoder_init_mp3__internal(const ma_decoder_config* pConfig, ma_decoder* pDecoder) { return ma_decoder_init_from_vtable(&g_ma_decoding_backend_vtable_mp3, NULL, pConfig, pDecoder); } #endif /* dr_mp3_h */ /* Vorbis */ #ifdef STB_VORBIS_INCLUDE_STB_VORBIS_H #define MA_HAS_VORBIS /* The size in bytes of each chunk of data to read from the Vorbis stream. */ #define MA_VORBIS_DATA_CHUNK_SIZE 4096 typedef struct { ma_data_source_base ds; ma_read_proc onRead; ma_seek_proc onSeek; ma_tell_proc onTell; void* pReadSeekTellUserData; ma_allocation_callbacks allocationCallbacks; /* Store the allocation callbacks within the structure because we may need to dynamically expand a buffer in ma_stbvorbis_read_pcm_frames() when using push mode. */ ma_format format; /* Only f32 is allowed with stb_vorbis. */ ma_uint32 channels; ma_uint32 sampleRate; ma_uint64 cursor; #if !defined(MA_NO_VORBIS) stb_vorbis* stb; ma_bool32 usingPushMode; struct { ma_uint8* pData; size_t dataSize; size_t dataCapacity; ma_uint32 framesConsumed; /* The number of frames consumed in ppPacketData. */ ma_uint32 framesRemaining; /* The number of frames remaining in ppPacketData. */ float** ppPacketData; } push; #endif } ma_stbvorbis; MA_API ma_result ma_stbvorbis_init(ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_stbvorbis* pVorbis); MA_API ma_result ma_stbvorbis_init_file(const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_stbvorbis* pVorbis); MA_API ma_result ma_stbvorbis_init_memory(const void* pData, size_t dataSize, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_stbvorbis* pVorbis); MA_API void ma_stbvorbis_uninit(ma_stbvorbis* pVorbis, const ma_allocation_callbacks* pAllocationCallbacks); MA_API ma_result ma_stbvorbis_read_pcm_frames(ma_stbvorbis* pVorbis, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); MA_API ma_result ma_stbvorbis_seek_to_pcm_frame(ma_stbvorbis* pVorbis, ma_uint64 frameIndex); MA_API ma_result ma_stbvorbis_get_data_format(ma_stbvorbis* pVorbis, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap); MA_API ma_result ma_stbvorbis_get_cursor_in_pcm_frames(ma_stbvorbis* pVorbis, ma_uint64* pCursor); MA_API ma_result ma_stbvorbis_get_length_in_pcm_frames(ma_stbvorbis* pVorbis, ma_uint64* pLength); static ma_result ma_stbvorbis_ds_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) { return ma_stbvorbis_read_pcm_frames((ma_stbvorbis*)pDataSource, pFramesOut, frameCount, pFramesRead); } static ma_result ma_stbvorbis_ds_seek(ma_data_source* pDataSource, ma_uint64 frameIndex) { return ma_stbvorbis_seek_to_pcm_frame((ma_stbvorbis*)pDataSource, frameIndex); } static ma_result ma_stbvorbis_ds_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) { return ma_stbvorbis_get_data_format((ma_stbvorbis*)pDataSource, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap); } static ma_result ma_stbvorbis_ds_get_cursor(ma_data_source* pDataSource, ma_uint64* pCursor) { return ma_stbvorbis_get_cursor_in_pcm_frames((ma_stbvorbis*)pDataSource, pCursor); } static ma_result ma_stbvorbis_ds_get_length(ma_data_source* pDataSource, ma_uint64* pLength) { return ma_stbvorbis_get_length_in_pcm_frames((ma_stbvorbis*)pDataSource, pLength); } static ma_data_source_vtable g_ma_stbvorbis_ds_vtable = { ma_stbvorbis_ds_read, ma_stbvorbis_ds_seek, ma_stbvorbis_ds_get_data_format, ma_stbvorbis_ds_get_cursor, ma_stbvorbis_ds_get_length, NULL, /* onSetLooping */ 0 }; static ma_result ma_stbvorbis_init_internal(const ma_decoding_backend_config* pConfig, ma_stbvorbis* pVorbis) { ma_result result; ma_data_source_config dataSourceConfig; (void)pConfig; if (pVorbis == NULL) { return MA_INVALID_ARGS; } MA_ZERO_OBJECT(pVorbis); pVorbis->format = ma_format_f32; /* Only supporting f32. */ dataSourceConfig = ma_data_source_config_init(); dataSourceConfig.vtable = &g_ma_stbvorbis_ds_vtable; result = ma_data_source_init(&dataSourceConfig, &pVorbis->ds); if (result != MA_SUCCESS) { return result; /* Failed to initialize the base data source. */ } return MA_SUCCESS; } #if !defined(MA_NO_VORBIS) static ma_result ma_stbvorbis_post_init(ma_stbvorbis* pVorbis) { stb_vorbis_info info; MA_ASSERT(pVorbis != NULL); info = stb_vorbis_get_info(pVorbis->stb); pVorbis->channels = info.channels; pVorbis->sampleRate = info.sample_rate; return MA_SUCCESS; } #endif MA_API ma_result ma_stbvorbis_init(ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_stbvorbis* pVorbis) { ma_result result; result = ma_stbvorbis_init_internal(pConfig, pVorbis); if (result != MA_SUCCESS) { return result; } if (onRead == NULL || onSeek == NULL) { return MA_INVALID_ARGS; /* onRead and onSeek are mandatory. */ } pVorbis->onRead = onRead; pVorbis->onSeek = onSeek; pVorbis->onTell = onTell; pVorbis->pReadSeekTellUserData = pReadSeekTellUserData; ma_allocation_callbacks_init_copy(&pVorbis->allocationCallbacks, pAllocationCallbacks); #if !defined(MA_NO_VORBIS) { /* stb_vorbis lacks a callback based API for it's pulling API which means we're stuck with the pushing API. In order for us to be able to successfully initialize the decoder we need to supply it with enough data. We need to keep loading data until we have enough. */ stb_vorbis* stb; size_t dataSize = 0; size_t dataCapacity = 0; ma_uint8* pData = NULL; /* <-- Must be initialized to NULL. */ for (;;) { int vorbisError; int consumedDataSize; /* <-- Fill by stb_vorbis_open_pushdata(). */ size_t bytesRead; ma_uint8* pNewData; /* Allocate memory for the new chunk. */ dataCapacity += MA_VORBIS_DATA_CHUNK_SIZE; pNewData = (ma_uint8*)ma_realloc(pData, dataCapacity, pAllocationCallbacks); if (pNewData == NULL) { ma_free(pData, pAllocationCallbacks); return MA_OUT_OF_MEMORY; } pData = pNewData; /* Read in the next chunk. */ result = pVorbis->onRead(pVorbis->pReadSeekTellUserData, ma_offset_ptr(pData, dataSize), (dataCapacity - dataSize), &bytesRead); dataSize += bytesRead; if (result != MA_SUCCESS) { ma_free(pData, pAllocationCallbacks); return result; } /* We have a maximum of 31 bits with stb_vorbis. */ if (dataSize > INT_MAX) { ma_free(pData, pAllocationCallbacks); return MA_TOO_BIG; } stb = stb_vorbis_open_pushdata(pData, (int)dataSize, &consumedDataSize, &vorbisError, NULL); if (stb != NULL) { /* Successfully opened the Vorbis decoder. We might have some leftover unprocessed data so we'll need to move that down to the front. */ dataSize -= (size_t)consumedDataSize; /* Consume the data. */ MA_MOVE_MEMORY(pData, ma_offset_ptr(pData, consumedDataSize), dataSize); break; } else { /* Failed to open the decoder. */ if (vorbisError == VORBIS_need_more_data) { continue; } else { ma_free(pData, pAllocationCallbacks); return MA_ERROR; /* Failed to open the stb_vorbis decoder. */ } } } MA_ASSERT(stb != NULL); pVorbis->stb = stb; pVorbis->push.pData = pData; pVorbis->push.dataSize = dataSize; pVorbis->push.dataCapacity = dataCapacity; pVorbis->usingPushMode = MA_TRUE; result = ma_stbvorbis_post_init(pVorbis); if (result != MA_SUCCESS) { stb_vorbis_close(pVorbis->stb); ma_free(pData, pAllocationCallbacks); return result; } return MA_SUCCESS; } #else { /* vorbis is disabled. */ (void)pAllocationCallbacks; return MA_NOT_IMPLEMENTED; } #endif } MA_API ma_result ma_stbvorbis_init_file(const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_stbvorbis* pVorbis) { ma_result result; result = ma_stbvorbis_init_internal(pConfig, pVorbis); if (result != MA_SUCCESS) { return result; } #if !defined(MA_NO_VORBIS) { (void)pAllocationCallbacks; /* Don't know how to make use of this with stb_vorbis. */ /* We can use stb_vorbis' pull mode for file based streams. */ pVorbis->stb = stb_vorbis_open_filename(pFilePath, NULL, NULL); if (pVorbis->stb == NULL) { return MA_INVALID_FILE; } pVorbis->usingPushMode = MA_FALSE; result = ma_stbvorbis_post_init(pVorbis); if (result != MA_SUCCESS) { stb_vorbis_close(pVorbis->stb); return result; } return MA_SUCCESS; } #else { /* vorbis is disabled. */ (void)pFilePath; (void)pAllocationCallbacks; return MA_NOT_IMPLEMENTED; } #endif } MA_API ma_result ma_stbvorbis_init_memory(const void* pData, size_t dataSize, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_stbvorbis* pVorbis) { ma_result result; result = ma_stbvorbis_init_internal(pConfig, pVorbis); if (result != MA_SUCCESS) { return result; } #if !defined(MA_NO_VORBIS) { (void)pAllocationCallbacks; /* stb_vorbis uses an int as it's size specifier, restricting it to 32-bit even on 64-bit systems. *sigh*. */ if (dataSize > INT_MAX) { return MA_TOO_BIG; } pVorbis->stb = stb_vorbis_open_memory((const unsigned char*)pData, (int)dataSize, NULL, NULL); if (pVorbis->stb == NULL) { return MA_INVALID_FILE; } pVorbis->usingPushMode = MA_FALSE; result = ma_stbvorbis_post_init(pVorbis); if (result != MA_SUCCESS) { stb_vorbis_close(pVorbis->stb); return result; } return MA_SUCCESS; } #else { /* vorbis is disabled. */ (void)pData; (void)dataSize; (void)pAllocationCallbacks; return MA_NOT_IMPLEMENTED; } #endif } MA_API void ma_stbvorbis_uninit(ma_stbvorbis* pVorbis, const ma_allocation_callbacks* pAllocationCallbacks) { if (pVorbis == NULL) { return; } #if !defined(MA_NO_VORBIS) { stb_vorbis_close(pVorbis->stb); /* We'll have to clear some memory if we're using push mode. */ if (pVorbis->usingPushMode) { ma_free(pVorbis->push.pData, pAllocationCallbacks); } } #else { /* vorbis is disabled. Should never hit this since initialization would have failed. */ MA_ASSERT(MA_FALSE); } #endif ma_data_source_uninit(&pVorbis->ds); } MA_API ma_result ma_stbvorbis_read_pcm_frames(ma_stbvorbis* pVorbis, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) { if (pFramesRead != NULL) { *pFramesRead = 0; } if (frameCount == 0) { return MA_INVALID_ARGS; } if (pVorbis == NULL) { return MA_INVALID_ARGS; } #if !defined(MA_NO_VORBIS) { /* We always use floating point format. */ ma_result result = MA_SUCCESS; /* Must be initialized to MA_SUCCESS. */ ma_uint64 totalFramesRead = 0; ma_format format; ma_uint32 channels; ma_stbvorbis_get_data_format(pVorbis, &format, &channels, NULL, NULL, 0); if (format == ma_format_f32) { /* We read differently depending on whether or not we're using push mode. */ if (pVorbis->usingPushMode) { /* Push mode. This is the complex case. */ float* pFramesOutF32 = (float*)pFramesOut; while (totalFramesRead < frameCount) { /* The first thing to do is read from any already-cached frames. */ ma_uint32 framesToReadFromCache = (ma_uint32)ma_min(pVorbis->push.framesRemaining, (frameCount - totalFramesRead)); /* Safe cast because pVorbis->framesRemaining is 32-bit. */ /* The output pointer can be null in which case we just treate it as a seek. */ if (pFramesOut != NULL) { ma_uint64 iFrame; for (iFrame = 0; iFrame < framesToReadFromCache; iFrame += 1) { ma_uint32 iChannel; for (iChannel = 0; iChannel < pVorbis->channels; iChannel += 1) { pFramesOutF32[iChannel] = pVorbis->push.ppPacketData[iChannel][pVorbis->push.framesConsumed + iFrame]; } pFramesOutF32 += pVorbis->channels; } } /* Update pointers and counters. */ pVorbis->push.framesConsumed += framesToReadFromCache; pVorbis->push.framesRemaining -= framesToReadFromCache; totalFramesRead += framesToReadFromCache; /* Don't bother reading any more frames right now if we've just finished loading. */ if (totalFramesRead == frameCount) { break; } MA_ASSERT(pVorbis->push.framesRemaining == 0); /* Getting here means we've run out of cached frames. We'll need to load some more. */ for (;;) { int samplesRead = 0; int consumedDataSize; /* We need to case dataSize to an int, so make sure we can do it safely. */ if (pVorbis->push.dataSize > INT_MAX) { break; /* Too big. */ } consumedDataSize = stb_vorbis_decode_frame_pushdata(pVorbis->stb, pVorbis->push.pData, (int)pVorbis->push.dataSize, NULL, &pVorbis->push.ppPacketData, &samplesRead); if (consumedDataSize != 0) { /* Successfully decoded a Vorbis frame. Consume the data. */ pVorbis->push.dataSize -= (size_t)consumedDataSize; MA_MOVE_MEMORY(pVorbis->push.pData, ma_offset_ptr(pVorbis->push.pData, consumedDataSize), pVorbis->push.dataSize); pVorbis->push.framesConsumed = 0; pVorbis->push.framesRemaining = samplesRead; break; } else { /* Not enough data. Read more. */ size_t bytesRead; /* Expand the data buffer if necessary. */ if (pVorbis->push.dataCapacity == pVorbis->push.dataSize) { size_t newCap = pVorbis->push.dataCapacity + MA_VORBIS_DATA_CHUNK_SIZE; ma_uint8* pNewData; pNewData = (ma_uint8*)ma_realloc(pVorbis->push.pData, newCap, &pVorbis->allocationCallbacks); if (pNewData == NULL) { result = MA_OUT_OF_MEMORY; break; } pVorbis->push.pData = pNewData; pVorbis->push.dataCapacity = newCap; } /* We should have enough room to load some data. */ result = pVorbis->onRead(pVorbis->pReadSeekTellUserData, ma_offset_ptr(pVorbis->push.pData, pVorbis->push.dataSize), (pVorbis->push.dataCapacity - pVorbis->push.dataSize), &bytesRead); pVorbis->push.dataSize += bytesRead; if (result != MA_SUCCESS) { break; /* Failed to read any data. Get out. */ } } } /* If we don't have a success code at this point it means we've encounted an error or the end of the file has been reached (probably the latter). */ if (result != MA_SUCCESS) { break; } } } else { /* Pull mode. This is the simple case, but we still need to run in a loop because stb_vorbis loves using 32-bit instead of 64-bit. */ while (totalFramesRead < frameCount) { ma_uint64 framesRemaining = (frameCount - totalFramesRead); int framesRead; if (framesRemaining > INT_MAX) { framesRemaining = INT_MAX; } framesRead = stb_vorbis_get_samples_float_interleaved(pVorbis->stb, channels, (float*)ma_offset_pcm_frames_ptr(pFramesOut, totalFramesRead, format, channels), (int)framesRemaining * channels); /* Safe cast. */ totalFramesRead += framesRead; if (framesRead < (int)framesRemaining) { break; /* Nothing left to read. Get out. */ } } } } else { result = MA_INVALID_ARGS; } pVorbis->cursor += totalFramesRead; if (totalFramesRead == 0) { result = MA_AT_END; } if (pFramesRead != NULL) { *pFramesRead = totalFramesRead; } if (result == MA_SUCCESS && totalFramesRead == 0) { result = MA_AT_END; } return result; } #else { /* vorbis is disabled. Should never hit this since initialization would have failed. */ MA_ASSERT(MA_FALSE); (void)pFramesOut; (void)frameCount; (void)pFramesRead; return MA_NOT_IMPLEMENTED; } #endif } MA_API ma_result ma_stbvorbis_seek_to_pcm_frame(ma_stbvorbis* pVorbis, ma_uint64 frameIndex) { if (pVorbis == NULL) { return MA_INVALID_ARGS; } #if !defined(MA_NO_VORBIS) { /* Different seeking methods depending on whether or not we're using push mode. */ if (pVorbis->usingPushMode) { /* Push mode. This is the complex case. */ ma_result result; float buffer[4096]; /* This is terribly inefficient because stb_vorbis does not have a good seeking solution with it's push API. Currently this just performs a full decode right from the start of the stream. Later on I'll need to write a layer that goes through all of the Ogg pages until we find the one containing the sample we need. Then we know exactly where to seek for stb_vorbis. TODO: Use seeking logic documented for stb_vorbis_flush_pushdata(). */ /* Seek to the start of the file to begin with. */ result = pVorbis->onSeek(pVorbis->pReadSeekTellUserData, 0, ma_seek_origin_start); if (result != MA_SUCCESS) { return result; } stb_vorbis_flush_pushdata(pVorbis->stb); pVorbis->push.framesRemaining = 0; pVorbis->push.dataSize = 0; /* Move the cursor back to the start. We'll increment this in the loop below. */ pVorbis->cursor = 0; while (pVorbis->cursor < frameIndex) { ma_uint64 framesRead; ma_uint64 framesToRead = ma_countof(buffer)/pVorbis->channels; if (framesToRead > (frameIndex - pVorbis->cursor)) { framesToRead = (frameIndex - pVorbis->cursor); } result = ma_stbvorbis_read_pcm_frames(pVorbis, buffer, framesToRead, &framesRead); pVorbis->cursor += framesRead; if (result != MA_SUCCESS) { return result; } } } else { /* Pull mode. This is the simple case. */ int vorbisResult; if (frameIndex > UINT_MAX) { return MA_INVALID_ARGS; /* Trying to seek beyond the 32-bit maximum of stb_vorbis. */ } vorbisResult = stb_vorbis_seek(pVorbis->stb, (unsigned int)frameIndex); /* Safe cast. */ if (vorbisResult == 0) { return MA_ERROR; /* See failed. */ } pVorbis->cursor = frameIndex; } return MA_SUCCESS; } #else { /* vorbis is disabled. Should never hit this since initialization would have failed. */ MA_ASSERT(MA_FALSE); (void)frameIndex; return MA_NOT_IMPLEMENTED; } #endif } MA_API ma_result ma_stbvorbis_get_data_format(ma_stbvorbis* pVorbis, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) { /* Defaults for safety. */ if (pFormat != NULL) { *pFormat = ma_format_unknown; } if (pChannels != NULL) { *pChannels = 0; } if (pSampleRate != NULL) { *pSampleRate = 0; } if (pChannelMap != NULL) { MA_ZERO_MEMORY(pChannelMap, sizeof(*pChannelMap) * channelMapCap); } if (pVorbis == NULL) { return MA_INVALID_OPERATION; } if (pFormat != NULL) { *pFormat = pVorbis->format; } #if !defined(MA_NO_VORBIS) { if (pChannels != NULL) { *pChannels = pVorbis->channels; } if (pSampleRate != NULL) { *pSampleRate = pVorbis->sampleRate; } if (pChannelMap != NULL) { ma_channel_map_init_standard(ma_standard_channel_map_vorbis, pChannelMap, channelMapCap, pVorbis->channels); } return MA_SUCCESS; } #else { /* vorbis is disabled. Should never hit this since initialization would have failed. */ MA_ASSERT(MA_FALSE); return MA_NOT_IMPLEMENTED; } #endif } MA_API ma_result ma_stbvorbis_get_cursor_in_pcm_frames(ma_stbvorbis* pVorbis, ma_uint64* pCursor) { if (pCursor == NULL) { return MA_INVALID_ARGS; } *pCursor = 0; /* Safety. */ if (pVorbis == NULL) { return MA_INVALID_ARGS; } #if !defined(MA_NO_VORBIS) { *pCursor = pVorbis->cursor; return MA_SUCCESS; } #else { /* vorbis is disabled. Should never hit this since initialization would have failed. */ MA_ASSERT(MA_FALSE); return MA_NOT_IMPLEMENTED; } #endif } MA_API ma_result ma_stbvorbis_get_length_in_pcm_frames(ma_stbvorbis* pVorbis, ma_uint64* pLength) { if (pLength == NULL) { return MA_INVALID_ARGS; } *pLength = 0; /* Safety. */ if (pVorbis == NULL) { return MA_INVALID_ARGS; } #if !defined(MA_NO_VORBIS) { if (pVorbis->usingPushMode) { *pLength = 0; /* I don't know of a good way to determine this reliably with stb_vorbis and push mode. */ } else { *pLength = stb_vorbis_stream_length_in_samples(pVorbis->stb); } return MA_SUCCESS; } #else { /* vorbis is disabled. Should never hit this since initialization would have failed. */ MA_ASSERT(MA_FALSE); return MA_NOT_IMPLEMENTED; } #endif } static ma_result ma_decoding_backend_init__stbvorbis(void* pUserData, ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend) { ma_result result; ma_stbvorbis* pVorbis; (void)pUserData; /* For now not using pUserData, but once we start storing the vorbis decoder state within the ma_decoder structure this will be set to the decoder so we can avoid a malloc. */ /* For now we're just allocating the decoder backend on the heap. */ pVorbis = (ma_stbvorbis*)ma_malloc(sizeof(*pVorbis), pAllocationCallbacks); if (pVorbis == NULL) { return MA_OUT_OF_MEMORY; } result = ma_stbvorbis_init(onRead, onSeek, onTell, pReadSeekTellUserData, pConfig, pAllocationCallbacks, pVorbis); if (result != MA_SUCCESS) { ma_free(pVorbis, pAllocationCallbacks); return result; } *ppBackend = pVorbis; return MA_SUCCESS; } static ma_result ma_decoding_backend_init_file__stbvorbis(void* pUserData, const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend) { ma_result result; ma_stbvorbis* pVorbis; (void)pUserData; /* For now not using pUserData, but once we start storing the vorbis decoder state within the ma_decoder structure this will be set to the decoder so we can avoid a malloc. */ /* For now we're just allocating the decoder backend on the heap. */ pVorbis = (ma_stbvorbis*)ma_malloc(sizeof(*pVorbis), pAllocationCallbacks); if (pVorbis == NULL) { return MA_OUT_OF_MEMORY; } result = ma_stbvorbis_init_file(pFilePath, pConfig, pAllocationCallbacks, pVorbis); if (result != MA_SUCCESS) { ma_free(pVorbis, pAllocationCallbacks); return result; } *ppBackend = pVorbis; return MA_SUCCESS; } static ma_result ma_decoding_backend_init_memory__stbvorbis(void* pUserData, const void* pData, size_t dataSize, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend) { ma_result result; ma_stbvorbis* pVorbis; (void)pUserData; /* For now not using pUserData, but once we start storing the vorbis decoder state within the ma_decoder structure this will be set to the decoder so we can avoid a malloc. */ /* For now we're just allocating the decoder backend on the heap. */ pVorbis = (ma_stbvorbis*)ma_malloc(sizeof(*pVorbis), pAllocationCallbacks); if (pVorbis == NULL) { return MA_OUT_OF_MEMORY; } result = ma_stbvorbis_init_memory(pData, dataSize, pConfig, pAllocationCallbacks, pVorbis); if (result != MA_SUCCESS) { ma_free(pVorbis, pAllocationCallbacks); return result; } *ppBackend = pVorbis; return MA_SUCCESS; } static void ma_decoding_backend_uninit__stbvorbis(void* pUserData, ma_data_source* pBackend, const ma_allocation_callbacks* pAllocationCallbacks) { ma_stbvorbis* pVorbis = (ma_stbvorbis*)pBackend; (void)pUserData; ma_stbvorbis_uninit(pVorbis, pAllocationCallbacks); ma_free(pVorbis, pAllocationCallbacks); } static ma_decoding_backend_vtable g_ma_decoding_backend_vtable_stbvorbis = { ma_decoding_backend_init__stbvorbis, ma_decoding_backend_init_file__stbvorbis, NULL, /* onInitFileW() */ ma_decoding_backend_init_memory__stbvorbis, ma_decoding_backend_uninit__stbvorbis }; static ma_result ma_decoder_init_vorbis__internal(const ma_decoder_config* pConfig, ma_decoder* pDecoder) { return ma_decoder_init_from_vtable(&g_ma_decoding_backend_vtable_stbvorbis, NULL, pConfig, pDecoder); } #endif /* STB_VORBIS_INCLUDE_STB_VORBIS_H */ static ma_result ma_decoder__init_allocation_callbacks(const ma_decoder_config* pConfig, ma_decoder* pDecoder) { MA_ASSERT(pDecoder != NULL); if (pConfig != NULL) { return ma_allocation_callbacks_init_copy(&pDecoder->allocationCallbacks, &pConfig->allocationCallbacks); } else { pDecoder->allocationCallbacks = ma_allocation_callbacks_init_default(); return MA_SUCCESS; } } static ma_result ma_decoder__data_source_on_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) { return ma_decoder_read_pcm_frames((ma_decoder*)pDataSource, pFramesOut, frameCount, pFramesRead); } static ma_result ma_decoder__data_source_on_seek(ma_data_source* pDataSource, ma_uint64 frameIndex) { return ma_decoder_seek_to_pcm_frame((ma_decoder*)pDataSource, frameIndex); } static ma_result ma_decoder__data_source_on_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) { return ma_decoder_get_data_format((ma_decoder*)pDataSource, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap); } static ma_result ma_decoder__data_source_on_get_cursor(ma_data_source* pDataSource, ma_uint64* pCursor) { return ma_decoder_get_cursor_in_pcm_frames((ma_decoder*)pDataSource, pCursor); } static ma_result ma_decoder__data_source_on_get_length(ma_data_source* pDataSource, ma_uint64* pLength) { return ma_decoder_get_length_in_pcm_frames((ma_decoder*)pDataSource, pLength); } static ma_data_source_vtable g_ma_decoder_data_source_vtable = { ma_decoder__data_source_on_read, ma_decoder__data_source_on_seek, ma_decoder__data_source_on_get_data_format, ma_decoder__data_source_on_get_cursor, ma_decoder__data_source_on_get_length, NULL, /* onSetLooping */ 0 }; static ma_result ma_decoder__preinit(ma_decoder_read_proc onRead, ma_decoder_seek_proc onSeek, ma_decoder_tell_proc onTell, void* pUserData, const ma_decoder_config* pConfig, ma_decoder* pDecoder) { ma_result result; ma_data_source_config dataSourceConfig; MA_ASSERT(pConfig != NULL); if (pDecoder == NULL) { return MA_INVALID_ARGS; } MA_ZERO_OBJECT(pDecoder); if (onRead == NULL || onSeek == NULL) { return MA_INVALID_ARGS; } dataSourceConfig = ma_data_source_config_init(); dataSourceConfig.vtable = &g_ma_decoder_data_source_vtable; result = ma_data_source_init(&dataSourceConfig, &pDecoder->ds); if (result != MA_SUCCESS) { return result; } pDecoder->onRead = onRead; pDecoder->onSeek = onSeek; pDecoder->onTell = onTell; pDecoder->pUserData = pUserData; result = ma_decoder__init_allocation_callbacks(pConfig, pDecoder); if (result != MA_SUCCESS) { ma_data_source_uninit(&pDecoder->ds); return result; } return MA_SUCCESS; } static ma_result ma_decoder__postinit(const ma_decoder_config* pConfig, ma_decoder* pDecoder) { ma_result result; result = ma_decoder__init_data_converter(pDecoder, pConfig); /* If we failed post initialization we need to uninitialize the decoder before returning to prevent a memory leak. */ if (result != MA_SUCCESS) { ma_decoder_uninit(pDecoder); return result; } return result; } static ma_result ma_decoder_init__internal(ma_decoder_read_proc onRead, ma_decoder_seek_proc onSeek, void* pUserData, const ma_decoder_config* pConfig, ma_decoder* pDecoder) { ma_result result = MA_NO_BACKEND; MA_ASSERT(pConfig != NULL); MA_ASSERT(pDecoder != NULL); /* Silence some warnings in the case that we don't have any decoder backends enabled. */ (void)onRead; (void)onSeek; (void)pUserData; /* If we've specified a specific encoding type, try that first. */ if (pConfig->encodingFormat != ma_encoding_format_unknown) { #ifdef MA_HAS_WAV if (pConfig->encodingFormat == ma_encoding_format_wav) { result = ma_decoder_init_wav__internal(pConfig, pDecoder); } #endif #ifdef MA_HAS_FLAC if (pConfig->encodingFormat == ma_encoding_format_flac) { result = ma_decoder_init_flac__internal(pConfig, pDecoder); } #endif #ifdef MA_HAS_MP3 if (pConfig->encodingFormat == ma_encoding_format_mp3) { result = ma_decoder_init_mp3__internal(pConfig, pDecoder); } #endif #ifdef MA_HAS_VORBIS if (pConfig->encodingFormat == ma_encoding_format_vorbis) { result = ma_decoder_init_vorbis__internal(pConfig, pDecoder); } #endif /* If we weren't able to initialize the decoder, seek back to the start to give the next attempts a clean start. */ if (result != MA_SUCCESS) { onSeek(pDecoder, 0, ma_seek_origin_start); } } if (result != MA_SUCCESS) { /* Getting here means we couldn't load a specific decoding backend based on the encoding format. */ /* We use trial and error to open a decoder. We prioritize custom decoders so that if they implement the same encoding format they take priority over the built-in decoders. */ if (result != MA_SUCCESS) { result = ma_decoder_init_custom__internal(pConfig, pDecoder); if (result != MA_SUCCESS) { onSeek(pDecoder, 0, ma_seek_origin_start); } } /* If we get to this point and we still haven't found a decoder, and the caller has requested a specific encoding format, there's no hope for it. Abort. */ if (pConfig->encodingFormat != ma_encoding_format_unknown) { return MA_NO_BACKEND; } #ifdef MA_HAS_WAV if (result != MA_SUCCESS) { result = ma_decoder_init_wav__internal(pConfig, pDecoder); if (result != MA_SUCCESS) { onSeek(pDecoder, 0, ma_seek_origin_start); } } #endif #ifdef MA_HAS_FLAC if (result != MA_SUCCESS) { result = ma_decoder_init_flac__internal(pConfig, pDecoder); if (result != MA_SUCCESS) { onSeek(pDecoder, 0, ma_seek_origin_start); } } #endif #ifdef MA_HAS_MP3 if (result != MA_SUCCESS) { result = ma_decoder_init_mp3__internal(pConfig, pDecoder); if (result != MA_SUCCESS) { onSeek(pDecoder, 0, ma_seek_origin_start); } } #endif #ifdef MA_HAS_VORBIS if (result != MA_SUCCESS) { result = ma_decoder_init_vorbis__internal(pConfig, pDecoder); if (result != MA_SUCCESS) { onSeek(pDecoder, 0, ma_seek_origin_start); } } #endif } if (result != MA_SUCCESS) { return result; } return ma_decoder__postinit(pConfig, pDecoder); } MA_API ma_result ma_decoder_init(ma_decoder_read_proc onRead, ma_decoder_seek_proc onSeek, void* pUserData, const ma_decoder_config* pConfig, ma_decoder* pDecoder) { ma_decoder_config config; ma_result result; config = ma_decoder_config_init_copy(pConfig); result = ma_decoder__preinit(onRead, onSeek, NULL, pUserData, &config, pDecoder); if (result != MA_SUCCESS) { return result; } return ma_decoder_init__internal(onRead, onSeek, pUserData, &config, pDecoder); } static ma_result ma_decoder__on_read_memory(ma_decoder* pDecoder, void* pBufferOut, size_t bytesToRead, size_t* pBytesRead) { size_t bytesRemaining; MA_ASSERT(pDecoder->data.memory.dataSize >= pDecoder->data.memory.currentReadPos); if (pBytesRead != NULL) { *pBytesRead = 0; } bytesRemaining = pDecoder->data.memory.dataSize - pDecoder->data.memory.currentReadPos; if (bytesToRead > bytesRemaining) { bytesToRead = bytesRemaining; } if (bytesRemaining == 0) { return MA_AT_END; } if (bytesToRead > 0) { MA_COPY_MEMORY(pBufferOut, pDecoder->data.memory.pData + pDecoder->data.memory.currentReadPos, bytesToRead); pDecoder->data.memory.currentReadPos += bytesToRead; } if (pBytesRead != NULL) { *pBytesRead = bytesToRead; } return MA_SUCCESS; } static ma_result ma_decoder__on_seek_memory(ma_decoder* pDecoder, ma_int64 byteOffset, ma_seek_origin origin) { if (byteOffset > 0 && (ma_uint64)byteOffset > MA_SIZE_MAX) { return MA_BAD_SEEK; } if (origin == ma_seek_origin_current) { if (byteOffset > 0) { if (pDecoder->data.memory.currentReadPos + byteOffset > pDecoder->data.memory.dataSize) { byteOffset = (ma_int64)(pDecoder->data.memory.dataSize - pDecoder->data.memory.currentReadPos); /* Trying to seek too far forward. */ } pDecoder->data.memory.currentReadPos += (size_t)byteOffset; } else { if (pDecoder->data.memory.currentReadPos < (size_t)-byteOffset) { byteOffset = -(ma_int64)pDecoder->data.memory.currentReadPos; /* Trying to seek too far backwards. */ } pDecoder->data.memory.currentReadPos -= (size_t)-byteOffset; } } else { if (origin == ma_seek_origin_end) { if (byteOffset < 0) { byteOffset = -byteOffset; } if (byteOffset > (ma_int64)pDecoder->data.memory.dataSize) { pDecoder->data.memory.currentReadPos = 0; /* Trying to seek too far back. */ } else { pDecoder->data.memory.currentReadPos = pDecoder->data.memory.dataSize - (size_t)byteOffset; } } else { if ((size_t)byteOffset <= pDecoder->data.memory.dataSize) { pDecoder->data.memory.currentReadPos = (size_t)byteOffset; } else { pDecoder->data.memory.currentReadPos = pDecoder->data.memory.dataSize; /* Trying to seek too far forward. */ } } } return MA_SUCCESS; } static ma_result ma_decoder__on_tell_memory(ma_decoder* pDecoder, ma_int64* pCursor) { MA_ASSERT(pDecoder != NULL); MA_ASSERT(pCursor != NULL); *pCursor = (ma_int64)pDecoder->data.memory.currentReadPos; return MA_SUCCESS; } static ma_result ma_decoder__preinit_memory(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder) { ma_result result = ma_decoder__preinit(ma_decoder__on_read_memory, ma_decoder__on_seek_memory, ma_decoder__on_tell_memory, NULL, pConfig, pDecoder); if (result != MA_SUCCESS) { return result; } if (pData == NULL || dataSize == 0) { return MA_INVALID_ARGS; } pDecoder->data.memory.pData = (const ma_uint8*)pData; pDecoder->data.memory.dataSize = dataSize; pDecoder->data.memory.currentReadPos = 0; (void)pConfig; return MA_SUCCESS; } MA_API ma_result ma_decoder_init_memory(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder) { ma_decoder_config config; ma_result result; config = ma_decoder_config_init_copy(pConfig); /* Make sure the config is not NULL. */ result = ma_decoder__preinit_memory(pData, dataSize, &config, pDecoder); if (result != MA_SUCCESS) { return result; } return ma_decoder_init__internal(ma_decoder__on_read_memory, ma_decoder__on_seek_memory, NULL, &config, pDecoder); } #if defined(MA_HAS_WAV) || \ defined(MA_HAS_MP3) || \ defined(MA_HAS_FLAC) || \ defined(MA_HAS_VORBIS) || \ defined(MA_HAS_OPUS) #define MA_HAS_PATH_API #endif #if defined(MA_HAS_PATH_API) static const char* ma_path_file_name(const char* path) { const char* fileName; if (path == NULL) { return NULL; } fileName = path; /* We just loop through the path until we find the last slash. */ while (path[0] != '\0') { if (path[0] == '/' || path[0] == '\\') { fileName = path; } path += 1; } /* At this point the file name is sitting on a slash, so just move forward. */ while (fileName[0] != '\0' && (fileName[0] == '/' || fileName[0] == '\\')) { fileName += 1; } return fileName; } static const wchar_t* ma_path_file_name_w(const wchar_t* path) { const wchar_t* fileName; if (path == NULL) { return NULL; } fileName = path; /* We just loop through the path until we find the last slash. */ while (path[0] != '\0') { if (path[0] == '/' || path[0] == '\\') { fileName = path; } path += 1; } /* At this point the file name is sitting on a slash, so just move forward. */ while (fileName[0] != '\0' && (fileName[0] == '/' || fileName[0] == '\\')) { fileName += 1; } return fileName; } static const char* ma_path_extension(const char* path) { const char* extension; const char* lastOccurance; if (path == NULL) { path = ""; } extension = ma_path_file_name(path); lastOccurance = NULL; /* Just find the last '.' and return. */ while (extension[0] != '\0') { if (extension[0] == '.') { extension += 1; lastOccurance = extension; } extension += 1; } return (lastOccurance != NULL) ? lastOccurance : extension; } static const wchar_t* ma_path_extension_w(const wchar_t* path) { const wchar_t* extension; const wchar_t* lastOccurance; if (path == NULL) { path = L""; } extension = ma_path_file_name_w(path); lastOccurance = NULL; /* Just find the last '.' and return. */ while (extension[0] != '\0') { if (extension[0] == '.') { extension += 1; lastOccurance = extension; } extension += 1; } return (lastOccurance != NULL) ? lastOccurance : extension; } static ma_bool32 ma_path_extension_equal(const char* path, const char* extension) { const char* ext1; const char* ext2; if (path == NULL || extension == NULL) { return MA_FALSE; } ext1 = extension; ext2 = ma_path_extension(path); #if defined(_MSC_VER) || defined(__DMC__) return _stricmp(ext1, ext2) == 0; #else return strcasecmp(ext1, ext2) == 0; #endif } static ma_bool32 ma_path_extension_equal_w(const wchar_t* path, const wchar_t* extension) { const wchar_t* ext1; const wchar_t* ext2; if (path == NULL || extension == NULL) { return MA_FALSE; } ext1 = extension; ext2 = ma_path_extension_w(path); #if defined(_MSC_VER) || defined(__WATCOMC__) || defined(__DMC__) return _wcsicmp(ext1, ext2) == 0; #else /* I'm not aware of a wide character version of strcasecmp(). I'm therefore converting the extensions to multibyte strings and comparing those. This isn't the most efficient way to do it, but it should work OK. */ { char ext1MB[4096]; char ext2MB[4096]; const wchar_t* pext1 = ext1; const wchar_t* pext2 = ext2; mbstate_t mbs1; mbstate_t mbs2; MA_ZERO_OBJECT(&mbs1); MA_ZERO_OBJECT(&mbs2); if (wcsrtombs(ext1MB, &pext1, sizeof(ext1MB), &mbs1) == (size_t)-1) { return MA_FALSE; } if (wcsrtombs(ext2MB, &pext2, sizeof(ext2MB), &mbs2) == (size_t)-1) { return MA_FALSE; } return strcasecmp(ext1MB, ext2MB) == 0; } #endif } #endif /* MA_HAS_PATH_API */ static ma_result ma_decoder__on_read_vfs(ma_decoder* pDecoder, void* pBufferOut, size_t bytesToRead, size_t* pBytesRead) { MA_ASSERT(pDecoder != NULL); MA_ASSERT(pBufferOut != NULL); return ma_vfs_or_default_read(pDecoder->data.vfs.pVFS, pDecoder->data.vfs.file, pBufferOut, bytesToRead, pBytesRead); } static ma_result ma_decoder__on_seek_vfs(ma_decoder* pDecoder, ma_int64 offset, ma_seek_origin origin) { MA_ASSERT(pDecoder != NULL); return ma_vfs_or_default_seek(pDecoder->data.vfs.pVFS, pDecoder->data.vfs.file, offset, origin); } static ma_result ma_decoder__on_tell_vfs(ma_decoder* pDecoder, ma_int64* pCursor) { MA_ASSERT(pDecoder != NULL); return ma_vfs_or_default_tell(pDecoder->data.vfs.pVFS, pDecoder->data.vfs.file, pCursor); } static ma_result ma_decoder__preinit_vfs(ma_vfs* pVFS, const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) { ma_result result; ma_vfs_file file; result = ma_decoder__preinit(ma_decoder__on_read_vfs, ma_decoder__on_seek_vfs, ma_decoder__on_tell_vfs, NULL, pConfig, pDecoder); if (result != MA_SUCCESS) { return result; } if (pFilePath == NULL || pFilePath[0] == '\0') { return MA_INVALID_ARGS; } result = ma_vfs_or_default_open(pVFS, pFilePath, MA_OPEN_MODE_READ, &file); if (result != MA_SUCCESS) { return result; } pDecoder->data.vfs.pVFS = pVFS; pDecoder->data.vfs.file = file; return MA_SUCCESS; } MA_API ma_result ma_decoder_init_vfs(ma_vfs* pVFS, const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) { ma_result result; ma_decoder_config config; config = ma_decoder_config_init_copy(pConfig); result = ma_decoder__preinit_vfs(pVFS, pFilePath, &config, pDecoder); if (result != MA_SUCCESS) { return result; } result = MA_NO_BACKEND; if (config.encodingFormat != ma_encoding_format_unknown) { #ifdef MA_HAS_WAV if (config.encodingFormat == ma_encoding_format_wav) { result = ma_decoder_init_wav__internal(&config, pDecoder); } #endif #ifdef MA_HAS_FLAC if (config.encodingFormat == ma_encoding_format_flac) { result = ma_decoder_init_flac__internal(&config, pDecoder); } #endif #ifdef MA_HAS_MP3 if (config.encodingFormat == ma_encoding_format_mp3) { result = ma_decoder_init_mp3__internal(&config, pDecoder); } #endif #ifdef MA_HAS_VORBIS if (config.encodingFormat == ma_encoding_format_vorbis) { result = ma_decoder_init_vorbis__internal(&config, pDecoder); } #endif /* Make sure we seek back to the start if we didn't initialize a decoder successfully so the next attempts have a fresh start. */ if (result != MA_SUCCESS) { ma_decoder__on_seek_vfs(pDecoder, 0, ma_seek_origin_start); } } if (result != MA_SUCCESS) { /* Getting here means we weren't able to initialize a decoder of a specific encoding format. */ /* We use trial and error to open a decoder. We prioritize custom decoders so that if they implement the same encoding format they take priority over the built-in decoders. */ if (result != MA_SUCCESS) { result = ma_decoder_init_custom__internal(&config, pDecoder); if (result != MA_SUCCESS) { ma_decoder__on_seek_vfs(pDecoder, 0, ma_seek_origin_start); } } /* If we get to this point and we still haven't found a decoder, and the caller has requested a specific encoding format, there's no hope for it. Abort. */ if (config.encodingFormat != ma_encoding_format_unknown) { return MA_NO_BACKEND; } #ifdef MA_HAS_WAV if (result != MA_SUCCESS && ma_path_extension_equal(pFilePath, "wav")) { result = ma_decoder_init_wav__internal(&config, pDecoder); if (result != MA_SUCCESS) { ma_decoder__on_seek_vfs(pDecoder, 0, ma_seek_origin_start); } } #endif #ifdef MA_HAS_FLAC if (result != MA_SUCCESS && ma_path_extension_equal(pFilePath, "flac")) { result = ma_decoder_init_flac__internal(&config, pDecoder); if (result != MA_SUCCESS) { ma_decoder__on_seek_vfs(pDecoder, 0, ma_seek_origin_start); } } #endif #ifdef MA_HAS_MP3 if (result != MA_SUCCESS && ma_path_extension_equal(pFilePath, "mp3")) { result = ma_decoder_init_mp3__internal(&config, pDecoder); if (result != MA_SUCCESS) { ma_decoder__on_seek_vfs(pDecoder, 0, ma_seek_origin_start); } } #endif } /* If we still haven't got a result just use trial and error. Otherwise we can finish up. */ if (result != MA_SUCCESS) { result = ma_decoder_init__internal(ma_decoder__on_read_vfs, ma_decoder__on_seek_vfs, NULL, &config, pDecoder); } else { result = ma_decoder__postinit(&config, pDecoder); } if (result != MA_SUCCESS) { if (pDecoder->data.vfs.file != NULL) { /* <-- Will be reset to NULL if ma_decoder_uninit() is called in one of the steps above which allows us to avoid a double close of the file. */ ma_vfs_or_default_close(pVFS, pDecoder->data.vfs.file); } return result; } return MA_SUCCESS; } static ma_result ma_decoder__preinit_vfs_w(ma_vfs* pVFS, const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) { ma_result result; ma_vfs_file file; result = ma_decoder__preinit(ma_decoder__on_read_vfs, ma_decoder__on_seek_vfs, ma_decoder__on_tell_vfs, NULL, pConfig, pDecoder); if (result != MA_SUCCESS) { return result; } if (pFilePath == NULL || pFilePath[0] == '\0') { return MA_INVALID_ARGS; } result = ma_vfs_or_default_open_w(pVFS, pFilePath, MA_OPEN_MODE_READ, &file); if (result != MA_SUCCESS) { return result; } pDecoder->data.vfs.pVFS = pVFS; pDecoder->data.vfs.file = file; return MA_SUCCESS; } MA_API ma_result ma_decoder_init_vfs_w(ma_vfs* pVFS, const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) { ma_result result; ma_decoder_config config; config = ma_decoder_config_init_copy(pConfig); result = ma_decoder__preinit_vfs_w(pVFS, pFilePath, &config, pDecoder); if (result != MA_SUCCESS) { return result; } result = MA_NO_BACKEND; if (config.encodingFormat != ma_encoding_format_unknown) { #ifdef MA_HAS_WAV if (config.encodingFormat == ma_encoding_format_wav) { result = ma_decoder_init_wav__internal(&config, pDecoder); } #endif #ifdef MA_HAS_FLAC if (config.encodingFormat == ma_encoding_format_flac) { result = ma_decoder_init_flac__internal(&config, pDecoder); } #endif #ifdef MA_HAS_MP3 if (config.encodingFormat == ma_encoding_format_mp3) { result = ma_decoder_init_mp3__internal(&config, pDecoder); } #endif #ifdef MA_HAS_VORBIS if (config.encodingFormat == ma_encoding_format_vorbis) { result = ma_decoder_init_vorbis__internal(&config, pDecoder); } #endif /* Make sure we seek back to the start if we didn't initialize a decoder successfully so the next attempts have a fresh start. */ if (result != MA_SUCCESS) { ma_decoder__on_seek_vfs(pDecoder, 0, ma_seek_origin_start); } } if (result != MA_SUCCESS) { /* Getting here means we weren't able to initialize a decoder of a specific encoding format. */ /* We use trial and error to open a decoder. We prioritize custom decoders so that if they implement the same encoding format they take priority over the built-in decoders. */ if (result != MA_SUCCESS) { result = ma_decoder_init_custom__internal(&config, pDecoder); if (result != MA_SUCCESS) { ma_decoder__on_seek_vfs(pDecoder, 0, ma_seek_origin_start); } } /* If we get to this point and we still haven't found a decoder, and the caller has requested a specific encoding format, there's no hope for it. Abort. */ if (config.encodingFormat != ma_encoding_format_unknown) { return MA_NO_BACKEND; } #ifdef MA_HAS_WAV if (result != MA_SUCCESS && ma_path_extension_equal_w(pFilePath, L"wav")) { result = ma_decoder_init_wav__internal(&config, pDecoder); if (result != MA_SUCCESS) { ma_decoder__on_seek_vfs(pDecoder, 0, ma_seek_origin_start); } } #endif #ifdef MA_HAS_FLAC if (result != MA_SUCCESS && ma_path_extension_equal_w(pFilePath, L"flac")) { result = ma_decoder_init_flac__internal(&config, pDecoder); if (result != MA_SUCCESS) { ma_decoder__on_seek_vfs(pDecoder, 0, ma_seek_origin_start); } } #endif #ifdef MA_HAS_MP3 if (result != MA_SUCCESS && ma_path_extension_equal_w(pFilePath, L"mp3")) { result = ma_decoder_init_mp3__internal(&config, pDecoder); if (result != MA_SUCCESS) { ma_decoder__on_seek_vfs(pDecoder, 0, ma_seek_origin_start); } } #endif } /* If we still haven't got a result just use trial and error. Otherwise we can finish up. */ if (result != MA_SUCCESS) { result = ma_decoder_init__internal(ma_decoder__on_read_vfs, ma_decoder__on_seek_vfs, NULL, &config, pDecoder); } else { result = ma_decoder__postinit(&config, pDecoder); } if (result != MA_SUCCESS) { ma_vfs_or_default_close(pVFS, pDecoder->data.vfs.file); return result; } return MA_SUCCESS; } MA_API ma_result ma_decoder_init_file(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) { return ma_decoder_init_vfs(NULL, pFilePath, pConfig, pDecoder); } MA_API ma_result ma_decoder_init_file_w(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) { return ma_decoder_init_vfs_w(NULL, pFilePath, pConfig, pDecoder); } MA_API ma_result ma_decoder_uninit(ma_decoder* pDecoder) { if (pDecoder == NULL) { return MA_INVALID_ARGS; } if (pDecoder->pBackend != NULL) { if (pDecoder->pBackendVTable != NULL && pDecoder->pBackendVTable->onUninit != NULL) { pDecoder->pBackendVTable->onUninit(pDecoder->pBackendUserData, pDecoder->pBackend, &pDecoder->allocationCallbacks); } } if (pDecoder->onRead == ma_decoder__on_read_vfs) { ma_vfs_or_default_close(pDecoder->data.vfs.pVFS, pDecoder->data.vfs.file); pDecoder->data.vfs.file = NULL; } ma_data_converter_uninit(&pDecoder->converter, &pDecoder->allocationCallbacks); ma_data_source_uninit(&pDecoder->ds); if (pDecoder->pInputCache != NULL) { ma_free(pDecoder->pInputCache, &pDecoder->allocationCallbacks); } return MA_SUCCESS; } MA_API ma_result ma_decoder_read_pcm_frames(ma_decoder* pDecoder, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) { ma_result result = MA_SUCCESS; ma_uint64 totalFramesReadOut; void* pRunningFramesOut; if (pFramesRead != NULL) { *pFramesRead = 0; /* Safety. */ } if (frameCount == 0) { return MA_INVALID_ARGS; } if (pDecoder == NULL) { return MA_INVALID_ARGS; } if (pDecoder->pBackend == NULL) { return MA_INVALID_OPERATION; } /* Fast path. */ if (pDecoder->converter.isPassthrough) { result = ma_data_source_read_pcm_frames(pDecoder->pBackend, pFramesOut, frameCount, &totalFramesReadOut); } else { /* Getting here means we need to do data conversion. If we're seeking forward and are _not_ doing resampling we can run this in a fast path. If we're doing resampling we need to run through each sample because we need to ensure it's internal cache is updated. */ if (pFramesOut == NULL && pDecoder->converter.hasResampler == MA_FALSE) { result = ma_data_source_read_pcm_frames(pDecoder->pBackend, NULL, frameCount, &totalFramesReadOut); } else { /* Slow path. Need to run everything through the data converter. */ ma_format internalFormat; ma_uint32 internalChannels; totalFramesReadOut = 0; pRunningFramesOut = pFramesOut; result = ma_data_source_get_data_format(pDecoder->pBackend, &internalFormat, &internalChannels, NULL, NULL, 0); if (result != MA_SUCCESS) { return result; /* Failed to retrieve the internal format and channel count. */ } /* We run a different path depending on whether or not we are using a heap-allocated intermediary buffer or not. If the data converter does not support the calculation of the required number of input frames, we'll use the heap-allocated path. Otherwise we'll use the stack-allocated path. */ if (pDecoder->pInputCache != NULL) { /* We don't have a way of determining the required number of input frames, so need to persistently store input data in a cache. */ while (totalFramesReadOut < frameCount) { ma_uint64 framesToReadThisIterationIn; ma_uint64 framesToReadThisIterationOut; /* If there's any data available in the cache, that needs to get processed first. */ if (pDecoder->inputCacheRemaining > 0) { framesToReadThisIterationOut = (frameCount - totalFramesReadOut); framesToReadThisIterationIn = framesToReadThisIterationOut; if (framesToReadThisIterationIn > pDecoder->inputCacheRemaining) { framesToReadThisIterationIn = pDecoder->inputCacheRemaining; } result = ma_data_converter_process_pcm_frames(&pDecoder->converter, ma_offset_pcm_frames_ptr(pDecoder->pInputCache, pDecoder->inputCacheConsumed, internalFormat, internalChannels), &framesToReadThisIterationIn, pRunningFramesOut, &framesToReadThisIterationOut); if (result != MA_SUCCESS) { break; } pDecoder->inputCacheConsumed += framesToReadThisIterationIn; pDecoder->inputCacheRemaining -= framesToReadThisIterationIn; totalFramesReadOut += framesToReadThisIterationOut; if (pRunningFramesOut != NULL) { pRunningFramesOut = ma_offset_ptr(pRunningFramesOut, framesToReadThisIterationOut * ma_get_bytes_per_frame(pDecoder->outputFormat, pDecoder->outputChannels)); } if (framesToReadThisIterationIn == 0 && framesToReadThisIterationOut == 0) { break; /* We're done. */ } } /* Getting here means there's no data in the cache and we need to fill it up from the data source. */ if (pDecoder->inputCacheRemaining == 0) { pDecoder->inputCacheConsumed = 0; result = ma_data_source_read_pcm_frames(pDecoder->pBackend, pDecoder->pInputCache, pDecoder->inputCacheCap, &pDecoder->inputCacheRemaining); if (result != MA_SUCCESS) { break; } } } } else { /* We have a way of determining the required number of input frames so just use the stack. */ while (totalFramesReadOut < frameCount) { ma_uint8 pIntermediaryBuffer[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; /* In internal format. */ ma_uint64 intermediaryBufferCap = sizeof(pIntermediaryBuffer) / ma_get_bytes_per_frame(internalFormat, internalChannels); ma_uint64 framesToReadThisIterationIn; ma_uint64 framesReadThisIterationIn; ma_uint64 framesToReadThisIterationOut; ma_uint64 framesReadThisIterationOut; ma_uint64 requiredInputFrameCount; framesToReadThisIterationOut = (frameCount - totalFramesReadOut); framesToReadThisIterationIn = framesToReadThisIterationOut; if (framesToReadThisIterationIn > intermediaryBufferCap) { framesToReadThisIterationIn = intermediaryBufferCap; } ma_data_converter_get_required_input_frame_count(&pDecoder->converter, framesToReadThisIterationOut, &requiredInputFrameCount); if (framesToReadThisIterationIn > requiredInputFrameCount) { framesToReadThisIterationIn = requiredInputFrameCount; } if (requiredInputFrameCount > 0) { result = ma_data_source_read_pcm_frames(pDecoder->pBackend, pIntermediaryBuffer, framesToReadThisIterationIn, &framesReadThisIterationIn); } else { framesReadThisIterationIn = 0; } /* At this point we have our decoded data in input format and now we need to convert to output format. Note that even if we didn't read any input frames, we still want to try processing frames because there may some output frames generated from cached input data. */ framesReadThisIterationOut = framesToReadThisIterationOut; result = ma_data_converter_process_pcm_frames(&pDecoder->converter, pIntermediaryBuffer, &framesReadThisIterationIn, pRunningFramesOut, &framesReadThisIterationOut); if (result != MA_SUCCESS) { break; } totalFramesReadOut += framesReadThisIterationOut; if (pRunningFramesOut != NULL) { pRunningFramesOut = ma_offset_ptr(pRunningFramesOut, framesReadThisIterationOut * ma_get_bytes_per_frame(pDecoder->outputFormat, pDecoder->outputChannels)); } if (framesReadThisIterationIn == 0 && framesReadThisIterationOut == 0) { break; /* We're done. */ } } } } } pDecoder->readPointerInPCMFrames += totalFramesReadOut; if (pFramesRead != NULL) { *pFramesRead = totalFramesReadOut; } if (result == MA_SUCCESS && totalFramesReadOut == 0) { result = MA_AT_END; } return result; } MA_API ma_result ma_decoder_seek_to_pcm_frame(ma_decoder* pDecoder, ma_uint64 frameIndex) { if (pDecoder == NULL) { return MA_INVALID_ARGS; } if (pDecoder->pBackend != NULL) { ma_result result; ma_uint64 internalFrameIndex; ma_uint32 internalSampleRate; ma_uint64 currentFrameIndex; result = ma_data_source_get_data_format(pDecoder->pBackend, NULL, NULL, &internalSampleRate, NULL, 0); if (result != MA_SUCCESS) { return result; /* Failed to retrieve the internal sample rate. */ } if (internalSampleRate == pDecoder->outputSampleRate) { internalFrameIndex = frameIndex; } else { internalFrameIndex = ma_calculate_frame_count_after_resampling(internalSampleRate, pDecoder->outputSampleRate, frameIndex); } /* Only seek if we're requesting a different frame to what we're currently sitting on. */ ma_data_source_get_cursor_in_pcm_frames(pDecoder->pBackend, ¤tFrameIndex); if (currentFrameIndex != internalFrameIndex) { result = ma_data_source_seek_to_pcm_frame(pDecoder->pBackend, internalFrameIndex); if (result == MA_SUCCESS) { pDecoder->readPointerInPCMFrames = frameIndex; } /* Reset the data converter so that any cached data in the resampler is cleared. */ ma_data_converter_reset(&pDecoder->converter); } return result; } /* Should never get here, but if we do it means onSeekToPCMFrame was not set by the backend. */ return MA_INVALID_ARGS; } MA_API ma_result ma_decoder_get_data_format(ma_decoder* pDecoder, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) { if (pDecoder == NULL) { return MA_INVALID_ARGS; } if (pFormat != NULL) { *pFormat = pDecoder->outputFormat; } if (pChannels != NULL) { *pChannels = pDecoder->outputChannels; } if (pSampleRate != NULL) { *pSampleRate = pDecoder->outputSampleRate; } if (pChannelMap != NULL) { ma_data_converter_get_output_channel_map(&pDecoder->converter, pChannelMap, channelMapCap); } return MA_SUCCESS; } MA_API ma_result ma_decoder_get_cursor_in_pcm_frames(ma_decoder* pDecoder, ma_uint64* pCursor) { if (pCursor == NULL) { return MA_INVALID_ARGS; } *pCursor = 0; if (pDecoder == NULL) { return MA_INVALID_ARGS; } *pCursor = pDecoder->readPointerInPCMFrames; return MA_SUCCESS; } MA_API ma_result ma_decoder_get_length_in_pcm_frames(ma_decoder* pDecoder, ma_uint64* pLength) { if (pLength == NULL) { return MA_INVALID_ARGS; } *pLength = 0; if (pDecoder == NULL) { return MA_INVALID_ARGS; } if (pDecoder->pBackend != NULL) { ma_result result; ma_uint64 internalLengthInPCMFrames; ma_uint32 internalSampleRate; result = ma_data_source_get_length_in_pcm_frames(pDecoder->pBackend, &internalLengthInPCMFrames); if (result != MA_SUCCESS) { return result; /* Failed to retrieve the internal length. */ } result = ma_data_source_get_data_format(pDecoder->pBackend, NULL, NULL, &internalSampleRate, NULL, 0); if (result != MA_SUCCESS) { return result; /* Failed to retrieve the internal sample rate. */ } if (internalSampleRate == pDecoder->outputSampleRate) { *pLength = internalLengthInPCMFrames; } else { *pLength = ma_calculate_frame_count_after_resampling(pDecoder->outputSampleRate, internalSampleRate, internalLengthInPCMFrames); } return MA_SUCCESS; } else { return MA_NO_BACKEND; } } MA_API ma_result ma_decoder_get_available_frames(ma_decoder* pDecoder, ma_uint64* pAvailableFrames) { ma_result result; ma_uint64 totalFrameCount; if (pAvailableFrames == NULL) { return MA_INVALID_ARGS; } *pAvailableFrames = 0; if (pDecoder == NULL) { return MA_INVALID_ARGS; } result = ma_decoder_get_length_in_pcm_frames(pDecoder, &totalFrameCount); if (result != MA_SUCCESS) { return result; } if (totalFrameCount <= pDecoder->readPointerInPCMFrames) { *pAvailableFrames = 0; } else { *pAvailableFrames = totalFrameCount - pDecoder->readPointerInPCMFrames; } return MA_SUCCESS; } static ma_result ma_decoder__full_decode_and_uninit(ma_decoder* pDecoder, ma_decoder_config* pConfigOut, ma_uint64* pFrameCountOut, void** ppPCMFramesOut) { ma_result result; ma_uint64 totalFrameCount; ma_uint64 bpf; ma_uint64 dataCapInFrames; void* pPCMFramesOut; MA_ASSERT(pDecoder != NULL); totalFrameCount = 0; bpf = ma_get_bytes_per_frame(pDecoder->outputFormat, pDecoder->outputChannels); /* The frame count is unknown until we try reading. Thus, we just run in a loop. */ dataCapInFrames = 0; pPCMFramesOut = NULL; for (;;) { ma_uint64 frameCountToTryReading; ma_uint64 framesJustRead; /* Make room if there's not enough. */ if (totalFrameCount == dataCapInFrames) { void* pNewPCMFramesOut; ma_uint64 newDataCapInFrames = dataCapInFrames*2; if (newDataCapInFrames == 0) { newDataCapInFrames = 4096; } if ((newDataCapInFrames * bpf) > MA_SIZE_MAX) { ma_free(pPCMFramesOut, &pDecoder->allocationCallbacks); return MA_TOO_BIG; } pNewPCMFramesOut = (void*)ma_realloc(pPCMFramesOut, (size_t)(newDataCapInFrames * bpf), &pDecoder->allocationCallbacks); if (pNewPCMFramesOut == NULL) { ma_free(pPCMFramesOut, &pDecoder->allocationCallbacks); return MA_OUT_OF_MEMORY; } dataCapInFrames = newDataCapInFrames; pPCMFramesOut = pNewPCMFramesOut; } frameCountToTryReading = dataCapInFrames - totalFrameCount; MA_ASSERT(frameCountToTryReading > 0); result = ma_decoder_read_pcm_frames(pDecoder, (ma_uint8*)pPCMFramesOut + (totalFrameCount * bpf), frameCountToTryReading, &framesJustRead); totalFrameCount += framesJustRead; if (result != MA_SUCCESS) { break; } if (framesJustRead < frameCountToTryReading) { break; } } if (pConfigOut != NULL) { pConfigOut->format = pDecoder->outputFormat; pConfigOut->channels = pDecoder->outputChannels; pConfigOut->sampleRate = pDecoder->outputSampleRate; } if (ppPCMFramesOut != NULL) { *ppPCMFramesOut = pPCMFramesOut; } else { ma_free(pPCMFramesOut, &pDecoder->allocationCallbacks); } if (pFrameCountOut != NULL) { *pFrameCountOut = totalFrameCount; } ma_decoder_uninit(pDecoder); return MA_SUCCESS; } MA_API ma_result ma_decode_from_vfs(ma_vfs* pVFS, const char* pFilePath, ma_decoder_config* pConfig, ma_uint64* pFrameCountOut, void** ppPCMFramesOut) { ma_result result; ma_decoder_config config; ma_decoder decoder; if (pFrameCountOut != NULL) { *pFrameCountOut = 0; } if (ppPCMFramesOut != NULL) { *ppPCMFramesOut = NULL; } config = ma_decoder_config_init_copy(pConfig); result = ma_decoder_init_vfs(pVFS, pFilePath, &config, &decoder); if (result != MA_SUCCESS) { return result; } result = ma_decoder__full_decode_and_uninit(&decoder, pConfig, pFrameCountOut, ppPCMFramesOut); return result; } MA_API ma_result ma_decode_file(const char* pFilePath, ma_decoder_config* pConfig, ma_uint64* pFrameCountOut, void** ppPCMFramesOut) { return ma_decode_from_vfs(NULL, pFilePath, pConfig, pFrameCountOut, ppPCMFramesOut); } MA_API ma_result ma_decode_memory(const void* pData, size_t dataSize, ma_decoder_config* pConfig, ma_uint64* pFrameCountOut, void** ppPCMFramesOut) { ma_decoder_config config; ma_decoder decoder; ma_result result; if (pFrameCountOut != NULL) { *pFrameCountOut = 0; } if (ppPCMFramesOut != NULL) { *ppPCMFramesOut = NULL; } if (pData == NULL || dataSize == 0) { return MA_INVALID_ARGS; } config = ma_decoder_config_init_copy(pConfig); result = ma_decoder_init_memory(pData, dataSize, &config, &decoder); if (result != MA_SUCCESS) { return result; } return ma_decoder__full_decode_and_uninit(&decoder, pConfig, pFrameCountOut, ppPCMFramesOut); } #endif /* MA_NO_DECODING */ #ifndef MA_NO_ENCODING #if defined(MA_HAS_WAV) static size_t ma_encoder__internal_on_write_wav(void* pUserData, const void* pData, size_t bytesToWrite) { ma_encoder* pEncoder = (ma_encoder*)pUserData; size_t bytesWritten = 0; MA_ASSERT(pEncoder != NULL); pEncoder->onWrite(pEncoder, pData, bytesToWrite, &bytesWritten); return bytesWritten; } static drwav_bool32 ma_encoder__internal_on_seek_wav(void* pUserData, int offset, drwav_seek_origin origin) { ma_encoder* pEncoder = (ma_encoder*)pUserData; ma_result result; MA_ASSERT(pEncoder != NULL); result = pEncoder->onSeek(pEncoder, offset, (origin == drwav_seek_origin_start) ? ma_seek_origin_start : ma_seek_origin_current); if (result != MA_SUCCESS) { return DRWAV_FALSE; } else { return DRWAV_TRUE; } } static ma_result ma_encoder__on_init_wav(ma_encoder* pEncoder) { drwav_data_format wavFormat; drwav_allocation_callbacks allocationCallbacks; drwav* pWav; MA_ASSERT(pEncoder != NULL); pWav = (drwav*)ma_malloc(sizeof(*pWav), &pEncoder->config.allocationCallbacks); if (pWav == NULL) { return MA_OUT_OF_MEMORY; } wavFormat.container = drwav_container_riff; wavFormat.channels = pEncoder->config.channels; wavFormat.sampleRate = pEncoder->config.sampleRate; wavFormat.bitsPerSample = ma_get_bytes_per_sample(pEncoder->config.format) * 8; if (pEncoder->config.format == ma_format_f32) { wavFormat.format = DR_WAVE_FORMAT_IEEE_FLOAT; } else { wavFormat.format = DR_WAVE_FORMAT_PCM; } allocationCallbacks.pUserData = pEncoder->config.allocationCallbacks.pUserData; allocationCallbacks.onMalloc = pEncoder->config.allocationCallbacks.onMalloc; allocationCallbacks.onRealloc = pEncoder->config.allocationCallbacks.onRealloc; allocationCallbacks.onFree = pEncoder->config.allocationCallbacks.onFree; if (!drwav_init_write(pWav, &wavFormat, ma_encoder__internal_on_write_wav, ma_encoder__internal_on_seek_wav, pEncoder, &allocationCallbacks)) { return MA_ERROR; } pEncoder->pInternalEncoder = pWav; return MA_SUCCESS; } static void ma_encoder__on_uninit_wav(ma_encoder* pEncoder) { drwav* pWav; MA_ASSERT(pEncoder != NULL); pWav = (drwav*)pEncoder->pInternalEncoder; MA_ASSERT(pWav != NULL); drwav_uninit(pWav); ma_free(pWav, &pEncoder->config.allocationCallbacks); } static ma_result ma_encoder__on_write_pcm_frames_wav(ma_encoder* pEncoder, const void* pFramesIn, ma_uint64 frameCount, ma_uint64* pFramesWritten) { drwav* pWav; ma_uint64 framesWritten; MA_ASSERT(pEncoder != NULL); pWav = (drwav*)pEncoder->pInternalEncoder; MA_ASSERT(pWav != NULL); framesWritten = drwav_write_pcm_frames(pWav, frameCount, pFramesIn); if (pFramesWritten != NULL) { *pFramesWritten = framesWritten; } return MA_SUCCESS; } #endif MA_API ma_encoder_config ma_encoder_config_init(ma_encoding_format encodingFormat, ma_format format, ma_uint32 channels, ma_uint32 sampleRate) { ma_encoder_config config; MA_ZERO_OBJECT(&config); config.encodingFormat = encodingFormat; config.format = format; config.channels = channels; config.sampleRate = sampleRate; return config; } MA_API ma_result ma_encoder_preinit(const ma_encoder_config* pConfig, ma_encoder* pEncoder) { ma_result result; if (pEncoder == NULL) { return MA_INVALID_ARGS; } MA_ZERO_OBJECT(pEncoder); if (pConfig == NULL) { return MA_INVALID_ARGS; } if (pConfig->format == ma_format_unknown || pConfig->channels == 0 || pConfig->sampleRate == 0) { return MA_INVALID_ARGS; } pEncoder->config = *pConfig; result = ma_allocation_callbacks_init_copy(&pEncoder->config.allocationCallbacks, &pConfig->allocationCallbacks); if (result != MA_SUCCESS) { return result; } return MA_SUCCESS; } MA_API ma_result ma_encoder_init__internal(ma_encoder_write_proc onWrite, ma_encoder_seek_proc onSeek, void* pUserData, ma_encoder* pEncoder) { ma_result result = MA_SUCCESS; /* This assumes ma_encoder_preinit() has been called prior. */ MA_ASSERT(pEncoder != NULL); if (onWrite == NULL || onSeek == NULL) { return MA_INVALID_ARGS; } pEncoder->onWrite = onWrite; pEncoder->onSeek = onSeek; pEncoder->pUserData = pUserData; switch (pEncoder->config.encodingFormat) { case ma_encoding_format_wav: { #if defined(MA_HAS_WAV) pEncoder->onInit = ma_encoder__on_init_wav; pEncoder->onUninit = ma_encoder__on_uninit_wav; pEncoder->onWritePCMFrames = ma_encoder__on_write_pcm_frames_wav; #else result = MA_NO_BACKEND; #endif } break; default: { result = MA_INVALID_ARGS; } break; } /* Getting here means we should have our backend callbacks set up. */ if (result == MA_SUCCESS) { result = pEncoder->onInit(pEncoder); } return result; } static ma_result ma_encoder__on_write_vfs(ma_encoder* pEncoder, const void* pBufferIn, size_t bytesToWrite, size_t* pBytesWritten) { return ma_vfs_or_default_write(pEncoder->data.vfs.pVFS, pEncoder->data.vfs.file, pBufferIn, bytesToWrite, pBytesWritten); } static ma_result ma_encoder__on_seek_vfs(ma_encoder* pEncoder, ma_int64 offset, ma_seek_origin origin) { return ma_vfs_or_default_seek(pEncoder->data.vfs.pVFS, pEncoder->data.vfs.file, offset, origin); } MA_API ma_result ma_encoder_init_vfs(ma_vfs* pVFS, const char* pFilePath, const ma_encoder_config* pConfig, ma_encoder* pEncoder) { ma_result result; ma_vfs_file file; result = ma_encoder_preinit(pConfig, pEncoder); if (result != MA_SUCCESS) { return result; } /* Now open the file. If this fails we don't need to uninitialize the encoder. */ result = ma_vfs_or_default_open(pVFS, pFilePath, MA_OPEN_MODE_WRITE, &file); if (result != MA_SUCCESS) { return result; } pEncoder->data.vfs.pVFS = pVFS; pEncoder->data.vfs.file = file; result = ma_encoder_init__internal(ma_encoder__on_write_vfs, ma_encoder__on_seek_vfs, NULL, pEncoder); if (result != MA_SUCCESS) { ma_vfs_or_default_close(pVFS, file); return result; } return MA_SUCCESS; } MA_API ma_result ma_encoder_init_vfs_w(ma_vfs* pVFS, const wchar_t* pFilePath, const ma_encoder_config* pConfig, ma_encoder* pEncoder) { ma_result result; ma_vfs_file file; result = ma_encoder_preinit(pConfig, pEncoder); if (result != MA_SUCCESS) { return result; } /* Now open the file. If this fails we don't need to uninitialize the encoder. */ result = ma_vfs_or_default_open_w(pVFS, pFilePath, MA_OPEN_MODE_WRITE, &file); if (result != MA_SUCCESS) { return result; } pEncoder->data.vfs.pVFS = pVFS; pEncoder->data.vfs.file = file; result = ma_encoder_init__internal(ma_encoder__on_write_vfs, ma_encoder__on_seek_vfs, NULL, pEncoder); if (result != MA_SUCCESS) { ma_vfs_or_default_close(pVFS, file); return result; } return MA_SUCCESS; } MA_API ma_result ma_encoder_init_file(const char* pFilePath, const ma_encoder_config* pConfig, ma_encoder* pEncoder) { return ma_encoder_init_vfs(NULL, pFilePath, pConfig, pEncoder); } MA_API ma_result ma_encoder_init_file_w(const wchar_t* pFilePath, const ma_encoder_config* pConfig, ma_encoder* pEncoder) { return ma_encoder_init_vfs_w(NULL, pFilePath, pConfig, pEncoder); } MA_API ma_result ma_encoder_init(ma_encoder_write_proc onWrite, ma_encoder_seek_proc onSeek, void* pUserData, const ma_encoder_config* pConfig, ma_encoder* pEncoder) { ma_result result; result = ma_encoder_preinit(pConfig, pEncoder); if (result != MA_SUCCESS) { return result; } return ma_encoder_init__internal(onWrite, onSeek, pUserData, pEncoder); } MA_API void ma_encoder_uninit(ma_encoder* pEncoder) { if (pEncoder == NULL) { return; } if (pEncoder->onUninit) { pEncoder->onUninit(pEncoder); } /* If we have a file handle, close it. */ if (pEncoder->onWrite == ma_encoder__on_write_vfs) { ma_vfs_or_default_close(pEncoder->data.vfs.pVFS, pEncoder->data.vfs.file); pEncoder->data.vfs.file = NULL; } } MA_API ma_result ma_encoder_write_pcm_frames(ma_encoder* pEncoder, const void* pFramesIn, ma_uint64 frameCount, ma_uint64* pFramesWritten) { if (pFramesWritten != NULL) { *pFramesWritten = 0; } if (pEncoder == NULL || pFramesIn == NULL) { return MA_INVALID_ARGS; } return pEncoder->onWritePCMFrames(pEncoder, pFramesIn, frameCount, pFramesWritten); } #endif /* MA_NO_ENCODING */ /************************************************************************************************************************************************************** Generation **************************************************************************************************************************************************************/ #ifndef MA_NO_GENERATION MA_API ma_waveform_config ma_waveform_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, ma_waveform_type type, double amplitude, double frequency) { ma_waveform_config config; MA_ZERO_OBJECT(&config); config.format = format; config.channels = channels; config.sampleRate = sampleRate; config.type = type; config.amplitude = amplitude; config.frequency = frequency; return config; } static ma_result ma_waveform__data_source_on_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) { return ma_waveform_read_pcm_frames((ma_waveform*)pDataSource, pFramesOut, frameCount, pFramesRead); } static ma_result ma_waveform__data_source_on_seek(ma_data_source* pDataSource, ma_uint64 frameIndex) { return ma_waveform_seek_to_pcm_frame((ma_waveform*)pDataSource, frameIndex); } static ma_result ma_waveform__data_source_on_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) { ma_waveform* pWaveform = (ma_waveform*)pDataSource; *pFormat = pWaveform->config.format; *pChannels = pWaveform->config.channels; *pSampleRate = pWaveform->config.sampleRate; ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, pWaveform->config.channels); return MA_SUCCESS; } static ma_result ma_waveform__data_source_on_get_cursor(ma_data_source* pDataSource, ma_uint64* pCursor) { ma_waveform* pWaveform = (ma_waveform*)pDataSource; *pCursor = (ma_uint64)(pWaveform->time / pWaveform->advance); return MA_SUCCESS; } static double ma_waveform__calculate_advance(ma_uint32 sampleRate, double frequency) { return (1.0 / (sampleRate / frequency)); } static void ma_waveform__update_advance(ma_waveform* pWaveform) { pWaveform->advance = ma_waveform__calculate_advance(pWaveform->config.sampleRate, pWaveform->config.frequency); } static ma_data_source_vtable g_ma_waveform_data_source_vtable = { ma_waveform__data_source_on_read, ma_waveform__data_source_on_seek, ma_waveform__data_source_on_get_data_format, ma_waveform__data_source_on_get_cursor, NULL, /* onGetLength. There's no notion of a length in waveforms. */ NULL, /* onSetLooping */ 0 }; MA_API ma_result ma_waveform_init(const ma_waveform_config* pConfig, ma_waveform* pWaveform) { ma_result result; ma_data_source_config dataSourceConfig; if (pWaveform == NULL) { return MA_INVALID_ARGS; } MA_ZERO_OBJECT(pWaveform); dataSourceConfig = ma_data_source_config_init(); dataSourceConfig.vtable = &g_ma_waveform_data_source_vtable; result = ma_data_source_init(&dataSourceConfig, &pWaveform->ds); if (result != MA_SUCCESS) { return result; } pWaveform->config = *pConfig; pWaveform->advance = ma_waveform__calculate_advance(pWaveform->config.sampleRate, pWaveform->config.frequency); pWaveform->time = 0; return MA_SUCCESS; } MA_API void ma_waveform_uninit(ma_waveform* pWaveform) { if (pWaveform == NULL) { return; } ma_data_source_uninit(&pWaveform->ds); } MA_API ma_result ma_waveform_set_amplitude(ma_waveform* pWaveform, double amplitude) { if (pWaveform == NULL) { return MA_INVALID_ARGS; } pWaveform->config.amplitude = amplitude; return MA_SUCCESS; } MA_API ma_result ma_waveform_set_frequency(ma_waveform* pWaveform, double frequency) { if (pWaveform == NULL) { return MA_INVALID_ARGS; } pWaveform->config.frequency = frequency; ma_waveform__update_advance(pWaveform); return MA_SUCCESS; } MA_API ma_result ma_waveform_set_type(ma_waveform* pWaveform, ma_waveform_type type) { if (pWaveform == NULL) { return MA_INVALID_ARGS; } pWaveform->config.type = type; return MA_SUCCESS; } MA_API ma_result ma_waveform_set_sample_rate(ma_waveform* pWaveform, ma_uint32 sampleRate) { if (pWaveform == NULL) { return MA_INVALID_ARGS; } pWaveform->config.sampleRate = sampleRate; ma_waveform__update_advance(pWaveform); return MA_SUCCESS; } static float ma_waveform_sine_f32(double time, double amplitude) { return (float)(ma_sind(MA_TAU_D * time) * amplitude); } static ma_int16 ma_waveform_sine_s16(double time, double amplitude) { return ma_pcm_sample_f32_to_s16(ma_waveform_sine_f32(time, amplitude)); } static float ma_waveform_square_f32(double time, double amplitude) { double f = time - (ma_int64)time; double r; if (f < 0.5) { r = amplitude; } else { r = -amplitude; } return (float)r; } static ma_int16 ma_waveform_square_s16(double time, double amplitude) { return ma_pcm_sample_f32_to_s16(ma_waveform_square_f32(time, amplitude)); } static float ma_waveform_triangle_f32(double time, double amplitude) { double f = time - (ma_int64)time; double r; r = 2 * ma_abs(2 * (f - 0.5)) - 1; return (float)(r * amplitude); } static ma_int16 ma_waveform_triangle_s16(double time, double amplitude) { return ma_pcm_sample_f32_to_s16(ma_waveform_triangle_f32(time, amplitude)); } static float ma_waveform_sawtooth_f32(double time, double amplitude) { double f = time - (ma_int64)time; double r; r = 2 * (f - 0.5); return (float)(r * amplitude); } static ma_int16 ma_waveform_sawtooth_s16(double time, double amplitude) { return ma_pcm_sample_f32_to_s16(ma_waveform_sawtooth_f32(time, amplitude)); } static void ma_waveform_read_pcm_frames__sine(ma_waveform* pWaveform, void* pFramesOut, ma_uint64 frameCount) { ma_uint64 iFrame; ma_uint64 iChannel; ma_uint32 bps = ma_get_bytes_per_sample(pWaveform->config.format); ma_uint32 bpf = bps * pWaveform->config.channels; MA_ASSERT(pWaveform != NULL); MA_ASSERT(pFramesOut != NULL); if (pWaveform->config.format == ma_format_f32) { float* pFramesOutF32 = (float*)pFramesOut; for (iFrame = 0; iFrame < frameCount; iFrame += 1) { float s = ma_waveform_sine_f32(pWaveform->time, pWaveform->config.amplitude); pWaveform->time += pWaveform->advance; for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) { pFramesOutF32[iFrame*pWaveform->config.channels + iChannel] = s; } } } else if (pWaveform->config.format == ma_format_s16) { ma_int16* pFramesOutS16 = (ma_int16*)pFramesOut; for (iFrame = 0; iFrame < frameCount; iFrame += 1) { ma_int16 s = ma_waveform_sine_s16(pWaveform->time, pWaveform->config.amplitude); pWaveform->time += pWaveform->advance; for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) { pFramesOutS16[iFrame*pWaveform->config.channels + iChannel] = s; } } } else { for (iFrame = 0; iFrame < frameCount; iFrame += 1) { float s = ma_waveform_sine_f32(pWaveform->time, pWaveform->config.amplitude); pWaveform->time += pWaveform->advance; for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) { ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pWaveform->config.format, &s, ma_format_f32, 1, ma_dither_mode_none); } } } } static void ma_waveform_read_pcm_frames__square(ma_waveform* pWaveform, void* pFramesOut, ma_uint64 frameCount) { ma_uint64 iFrame; ma_uint64 iChannel; ma_uint32 bps = ma_get_bytes_per_sample(pWaveform->config.format); ma_uint32 bpf = bps * pWaveform->config.channels; MA_ASSERT(pWaveform != NULL); MA_ASSERT(pFramesOut != NULL); if (pWaveform->config.format == ma_format_f32) { float* pFramesOutF32 = (float*)pFramesOut; for (iFrame = 0; iFrame < frameCount; iFrame += 1) { float s = ma_waveform_square_f32(pWaveform->time, pWaveform->config.amplitude); pWaveform->time += pWaveform->advance; for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) { pFramesOutF32[iFrame*pWaveform->config.channels + iChannel] = s; } } } else if (pWaveform->config.format == ma_format_s16) { ma_int16* pFramesOutS16 = (ma_int16*)pFramesOut; for (iFrame = 0; iFrame < frameCount; iFrame += 1) { ma_int16 s = ma_waveform_square_s16(pWaveform->time, pWaveform->config.amplitude); pWaveform->time += pWaveform->advance; for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) { pFramesOutS16[iFrame*pWaveform->config.channels + iChannel] = s; } } } else { for (iFrame = 0; iFrame < frameCount; iFrame += 1) { float s = ma_waveform_square_f32(pWaveform->time, pWaveform->config.amplitude); pWaveform->time += pWaveform->advance; for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) { ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pWaveform->config.format, &s, ma_format_f32, 1, ma_dither_mode_none); } } } } static void ma_waveform_read_pcm_frames__triangle(ma_waveform* pWaveform, void* pFramesOut, ma_uint64 frameCount) { ma_uint64 iFrame; ma_uint64 iChannel; ma_uint32 bps = ma_get_bytes_per_sample(pWaveform->config.format); ma_uint32 bpf = bps * pWaveform->config.channels; MA_ASSERT(pWaveform != NULL); MA_ASSERT(pFramesOut != NULL); if (pWaveform->config.format == ma_format_f32) { float* pFramesOutF32 = (float*)pFramesOut; for (iFrame = 0; iFrame < frameCount; iFrame += 1) { float s = ma_waveform_triangle_f32(pWaveform->time, pWaveform->config.amplitude); pWaveform->time += pWaveform->advance; for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) { pFramesOutF32[iFrame*pWaveform->config.channels + iChannel] = s; } } } else if (pWaveform->config.format == ma_format_s16) { ma_int16* pFramesOutS16 = (ma_int16*)pFramesOut; for (iFrame = 0; iFrame < frameCount; iFrame += 1) { ma_int16 s = ma_waveform_triangle_s16(pWaveform->time, pWaveform->config.amplitude); pWaveform->time += pWaveform->advance; for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) { pFramesOutS16[iFrame*pWaveform->config.channels + iChannel] = s; } } } else { for (iFrame = 0; iFrame < frameCount; iFrame += 1) { float s = ma_waveform_triangle_f32(pWaveform->time, pWaveform->config.amplitude); pWaveform->time += pWaveform->advance; for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) { ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pWaveform->config.format, &s, ma_format_f32, 1, ma_dither_mode_none); } } } } static void ma_waveform_read_pcm_frames__sawtooth(ma_waveform* pWaveform, void* pFramesOut, ma_uint64 frameCount) { ma_uint64 iFrame; ma_uint64 iChannel; ma_uint32 bps = ma_get_bytes_per_sample(pWaveform->config.format); ma_uint32 bpf = bps * pWaveform->config.channels; MA_ASSERT(pWaveform != NULL); MA_ASSERT(pFramesOut != NULL); if (pWaveform->config.format == ma_format_f32) { float* pFramesOutF32 = (float*)pFramesOut; for (iFrame = 0; iFrame < frameCount; iFrame += 1) { float s = ma_waveform_sawtooth_f32(pWaveform->time, pWaveform->config.amplitude); pWaveform->time += pWaveform->advance; for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) { pFramesOutF32[iFrame*pWaveform->config.channels + iChannel] = s; } } } else if (pWaveform->config.format == ma_format_s16) { ma_int16* pFramesOutS16 = (ma_int16*)pFramesOut; for (iFrame = 0; iFrame < frameCount; iFrame += 1) { ma_int16 s = ma_waveform_sawtooth_s16(pWaveform->time, pWaveform->config.amplitude); pWaveform->time += pWaveform->advance; for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) { pFramesOutS16[iFrame*pWaveform->config.channels + iChannel] = s; } } } else { for (iFrame = 0; iFrame < frameCount; iFrame += 1) { float s = ma_waveform_sawtooth_f32(pWaveform->time, pWaveform->config.amplitude); pWaveform->time += pWaveform->advance; for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) { ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pWaveform->config.format, &s, ma_format_f32, 1, ma_dither_mode_none); } } } } MA_API ma_result ma_waveform_read_pcm_frames(ma_waveform* pWaveform, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) { if (pFramesRead != NULL) { *pFramesRead = 0; } if (frameCount == 0) { return MA_INVALID_ARGS; } if (pWaveform == NULL) { return MA_INVALID_ARGS; } if (pFramesOut != NULL) { switch (pWaveform->config.type) { case ma_waveform_type_sine: { ma_waveform_read_pcm_frames__sine(pWaveform, pFramesOut, frameCount); } break; case ma_waveform_type_square: { ma_waveform_read_pcm_frames__square(pWaveform, pFramesOut, frameCount); } break; case ma_waveform_type_triangle: { ma_waveform_read_pcm_frames__triangle(pWaveform, pFramesOut, frameCount); } break; case ma_waveform_type_sawtooth: { ma_waveform_read_pcm_frames__sawtooth(pWaveform, pFramesOut, frameCount); } break; default: return MA_INVALID_OPERATION; /* Unknown waveform type. */ } } else { pWaveform->time += pWaveform->advance * (ma_int64)frameCount; /* Cast to int64 required for VC6. Won't affect anything in practice. */ } if (pFramesRead != NULL) { *pFramesRead = frameCount; } return MA_SUCCESS; } MA_API ma_result ma_waveform_seek_to_pcm_frame(ma_waveform* pWaveform, ma_uint64 frameIndex) { if (pWaveform == NULL) { return MA_INVALID_ARGS; } pWaveform->time = pWaveform->advance * (ma_int64)frameIndex; /* Casting for VC6. Won't be an issue in practice. */ return MA_SUCCESS; } MA_API ma_noise_config ma_noise_config_init(ma_format format, ma_uint32 channels, ma_noise_type type, ma_int32 seed, double amplitude) { ma_noise_config config; MA_ZERO_OBJECT(&config); config.format = format; config.channels = channels; config.type = type; config.seed = seed; config.amplitude = amplitude; if (config.seed == 0) { config.seed = MA_DEFAULT_LCG_SEED; } return config; } static ma_result ma_noise__data_source_on_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) { return ma_noise_read_pcm_frames((ma_noise*)pDataSource, pFramesOut, frameCount, pFramesRead); } static ma_result ma_noise__data_source_on_seek(ma_data_source* pDataSource, ma_uint64 frameIndex) { /* No-op. Just pretend to be successful. */ (void)pDataSource; (void)frameIndex; return MA_SUCCESS; } static ma_result ma_noise__data_source_on_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) { ma_noise* pNoise = (ma_noise*)pDataSource; *pFormat = pNoise->config.format; *pChannels = pNoise->config.channels; *pSampleRate = 0; /* There is no notion of sample rate with noise generation. */ ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, pNoise->config.channels); return MA_SUCCESS; } static ma_data_source_vtable g_ma_noise_data_source_vtable = { ma_noise__data_source_on_read, ma_noise__data_source_on_seek, /* No-op for noise. */ ma_noise__data_source_on_get_data_format, NULL, /* onGetCursor. No notion of a cursor for noise. */ NULL, /* onGetLength. No notion of a length for noise. */ NULL, /* onSetLooping */ 0 }; #ifndef MA_PINK_NOISE_BIN_SIZE #define MA_PINK_NOISE_BIN_SIZE 16 #endif typedef struct { size_t sizeInBytes; struct { size_t binOffset; size_t accumulationOffset; size_t counterOffset; } pink; struct { size_t accumulationOffset; } brownian; } ma_noise_heap_layout; static ma_result ma_noise_get_heap_layout(const ma_noise_config* pConfig, ma_noise_heap_layout* pHeapLayout) { MA_ASSERT(pHeapLayout != NULL); MA_ZERO_OBJECT(pHeapLayout); if (pConfig == NULL) { return MA_INVALID_ARGS; } if (pConfig->channels == 0) { return MA_INVALID_ARGS; } pHeapLayout->sizeInBytes = 0; /* Pink. */ if (pConfig->type == ma_noise_type_pink) { /* bin */ pHeapLayout->pink.binOffset = pHeapLayout->sizeInBytes; pHeapLayout->sizeInBytes += sizeof(double*) * pConfig->channels; pHeapLayout->sizeInBytes += sizeof(double ) * pConfig->channels * MA_PINK_NOISE_BIN_SIZE; /* accumulation */ pHeapLayout->pink.accumulationOffset = pHeapLayout->sizeInBytes; pHeapLayout->sizeInBytes += sizeof(double) * pConfig->channels; /* counter */ pHeapLayout->pink.counterOffset = pHeapLayout->sizeInBytes; pHeapLayout->sizeInBytes += sizeof(ma_uint32) * pConfig->channels; } /* Brownian. */ if (pConfig->type == ma_noise_type_brownian) { /* accumulation */ pHeapLayout->brownian.accumulationOffset = pHeapLayout->sizeInBytes; pHeapLayout->sizeInBytes += sizeof(double) * pConfig->channels; } /* Make sure allocation size is aligned. */ pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes); return MA_SUCCESS; } MA_API ma_result ma_noise_get_heap_size(const ma_noise_config* pConfig, size_t* pHeapSizeInBytes) { ma_result result; ma_noise_heap_layout heapLayout; if (pHeapSizeInBytes == NULL) { return MA_INVALID_ARGS; } *pHeapSizeInBytes = 0; result = ma_noise_get_heap_layout(pConfig, &heapLayout); if (result != MA_SUCCESS) { return result; } *pHeapSizeInBytes = heapLayout.sizeInBytes; return MA_SUCCESS; } MA_API ma_result ma_noise_init_preallocated(const ma_noise_config* pConfig, void* pHeap, ma_noise* pNoise) { ma_result result; ma_noise_heap_layout heapLayout; ma_data_source_config dataSourceConfig; ma_uint32 iChannel; if (pNoise == NULL) { return MA_INVALID_ARGS; } MA_ZERO_OBJECT(pNoise); result = ma_noise_get_heap_layout(pConfig, &heapLayout); if (result != MA_SUCCESS) { return result; } pNoise->_pHeap = pHeap; MA_ZERO_MEMORY(pNoise->_pHeap, heapLayout.sizeInBytes); dataSourceConfig = ma_data_source_config_init(); dataSourceConfig.vtable = &g_ma_noise_data_source_vtable; result = ma_data_source_init(&dataSourceConfig, &pNoise->ds); if (result != MA_SUCCESS) { return result; } pNoise->config = *pConfig; ma_lcg_seed(&pNoise->lcg, pConfig->seed); if (pNoise->config.type == ma_noise_type_pink) { pNoise->state.pink.bin = (double** )ma_offset_ptr(pHeap, heapLayout.pink.binOffset); pNoise->state.pink.accumulation = (double* )ma_offset_ptr(pHeap, heapLayout.pink.accumulationOffset); pNoise->state.pink.counter = (ma_uint32*)ma_offset_ptr(pHeap, heapLayout.pink.counterOffset); for (iChannel = 0; iChannel < pConfig->channels; iChannel += 1) { pNoise->state.pink.bin[iChannel] = (double*)ma_offset_ptr(pHeap, heapLayout.pink.binOffset + (sizeof(double*) * pConfig->channels) + (sizeof(double) * MA_PINK_NOISE_BIN_SIZE * iChannel)); pNoise->state.pink.accumulation[iChannel] = 0; pNoise->state.pink.counter[iChannel] = 1; } } if (pNoise->config.type == ma_noise_type_brownian) { pNoise->state.brownian.accumulation = (double*)ma_offset_ptr(pHeap, heapLayout.brownian.accumulationOffset); for (iChannel = 0; iChannel < pConfig->channels; iChannel += 1) { pNoise->state.brownian.accumulation[iChannel] = 0; } } return MA_SUCCESS; } MA_API ma_result ma_noise_init(const ma_noise_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_noise* pNoise) { ma_result result; size_t heapSizeInBytes; void* pHeap; result = ma_noise_get_heap_size(pConfig, &heapSizeInBytes); if (result != MA_SUCCESS) { return result; } if (heapSizeInBytes > 0) { pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); if (pHeap == NULL) { return MA_OUT_OF_MEMORY; } } else { pHeap = NULL; } result = ma_noise_init_preallocated(pConfig, pHeap, pNoise); if (result != MA_SUCCESS) { ma_free(pHeap, pAllocationCallbacks); return result; } pNoise->_ownsHeap = MA_TRUE; return MA_SUCCESS; } MA_API void ma_noise_uninit(ma_noise* pNoise, const ma_allocation_callbacks* pAllocationCallbacks) { if (pNoise == NULL) { return; } ma_data_source_uninit(&pNoise->ds); if (pNoise->_ownsHeap) { ma_free(pNoise->_pHeap, pAllocationCallbacks); } } MA_API ma_result ma_noise_set_amplitude(ma_noise* pNoise, double amplitude) { if (pNoise == NULL) { return MA_INVALID_ARGS; } pNoise->config.amplitude = amplitude; return MA_SUCCESS; } MA_API ma_result ma_noise_set_seed(ma_noise* pNoise, ma_int32 seed) { if (pNoise == NULL) { return MA_INVALID_ARGS; } pNoise->lcg.state = seed; return MA_SUCCESS; } MA_API ma_result ma_noise_set_type(ma_noise* pNoise, ma_noise_type type) { if (pNoise == NULL) { return MA_INVALID_ARGS; } pNoise->config.type = type; return MA_SUCCESS; } static MA_INLINE float ma_noise_f32_white(ma_noise* pNoise) { return (float)(ma_lcg_rand_f64(&pNoise->lcg) * pNoise->config.amplitude); } static MA_INLINE ma_int16 ma_noise_s16_white(ma_noise* pNoise) { return ma_pcm_sample_f32_to_s16(ma_noise_f32_white(pNoise)); } static MA_INLINE ma_uint64 ma_noise_read_pcm_frames__white(ma_noise* pNoise, void* pFramesOut, ma_uint64 frameCount) { ma_uint64 iFrame; ma_uint32 iChannel; const ma_uint32 channels = pNoise->config.channels; MA_ASSUME(channels > 0); if (pNoise->config.format == ma_format_f32) { float* pFramesOutF32 = (float*)pFramesOut; if (pNoise->config.duplicateChannels) { for (iFrame = 0; iFrame < frameCount; iFrame += 1) { float s = ma_noise_f32_white(pNoise); for (iChannel = 0; iChannel < channels; iChannel += 1) { pFramesOutF32[iFrame*channels + iChannel] = s; } } } else { for (iFrame = 0; iFrame < frameCount; iFrame += 1) { for (iChannel = 0; iChannel < channels; iChannel += 1) { pFramesOutF32[iFrame*channels + iChannel] = ma_noise_f32_white(pNoise); } } } } else if (pNoise->config.format == ma_format_s16) { ma_int16* pFramesOutS16 = (ma_int16*)pFramesOut; if (pNoise->config.duplicateChannels) { for (iFrame = 0; iFrame < frameCount; iFrame += 1) { ma_int16 s = ma_noise_s16_white(pNoise); for (iChannel = 0; iChannel < channels; iChannel += 1) { pFramesOutS16[iFrame*channels + iChannel] = s; } } } else { for (iFrame = 0; iFrame < frameCount; iFrame += 1) { for (iChannel = 0; iChannel < channels; iChannel += 1) { pFramesOutS16[iFrame*channels + iChannel] = ma_noise_s16_white(pNoise); } } } } else { const ma_uint32 bps = ma_get_bytes_per_sample(pNoise->config.format); const ma_uint32 bpf = bps * channels; if (pNoise->config.duplicateChannels) { for (iFrame = 0; iFrame < frameCount; iFrame += 1) { float s = ma_noise_f32_white(pNoise); for (iChannel = 0; iChannel < channels; iChannel += 1) { ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pNoise->config.format, &s, ma_format_f32, 1, ma_dither_mode_none); } } } else { for (iFrame = 0; iFrame < frameCount; iFrame += 1) { for (iChannel = 0; iChannel < channels; iChannel += 1) { float s = ma_noise_f32_white(pNoise); ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pNoise->config.format, &s, ma_format_f32, 1, ma_dither_mode_none); } } } } return frameCount; } static MA_INLINE unsigned int ma_tzcnt32(unsigned int x) { unsigned int n; /* Special case for odd numbers since they should happen about half the time. */ if (x & 0x1) { return 0; } if (x == 0) { return sizeof(x) << 3; } n = 1; if ((x & 0x0000FFFF) == 0) { x >>= 16; n += 16; } if ((x & 0x000000FF) == 0) { x >>= 8; n += 8; } if ((x & 0x0000000F) == 0) { x >>= 4; n += 4; } if ((x & 0x00000003) == 0) { x >>= 2; n += 2; } n -= x & 0x00000001; return n; } /* Pink noise generation based on Tonic (public domain) with modifications. https://github.com/TonicAudio/Tonic/blob/master/src/Tonic/Noise.h This is basically _the_ reference for pink noise from what I've found: http://www.firstpr.com.au/dsp/pink-noise/ */ static MA_INLINE float ma_noise_f32_pink(ma_noise* pNoise, ma_uint32 iChannel) { double result; double binPrev; double binNext; unsigned int ibin; ibin = ma_tzcnt32(pNoise->state.pink.counter[iChannel]) & (MA_PINK_NOISE_BIN_SIZE - 1); binPrev = pNoise->state.pink.bin[iChannel][ibin]; binNext = ma_lcg_rand_f64(&pNoise->lcg); pNoise->state.pink.bin[iChannel][ibin] = binNext; pNoise->state.pink.accumulation[iChannel] += (binNext - binPrev); pNoise->state.pink.counter[iChannel] += 1; result = (ma_lcg_rand_f64(&pNoise->lcg) + pNoise->state.pink.accumulation[iChannel]); result /= 10; return (float)(result * pNoise->config.amplitude); } static MA_INLINE ma_int16 ma_noise_s16_pink(ma_noise* pNoise, ma_uint32 iChannel) { return ma_pcm_sample_f32_to_s16(ma_noise_f32_pink(pNoise, iChannel)); } static MA_INLINE ma_uint64 ma_noise_read_pcm_frames__pink(ma_noise* pNoise, void* pFramesOut, ma_uint64 frameCount) { ma_uint64 iFrame; ma_uint32 iChannel; const ma_uint32 channels = pNoise->config.channels; MA_ASSUME(channels > 0); if (pNoise->config.format == ma_format_f32) { float* pFramesOutF32 = (float*)pFramesOut; if (pNoise->config.duplicateChannels) { for (iFrame = 0; iFrame < frameCount; iFrame += 1) { float s = ma_noise_f32_pink(pNoise, 0); for (iChannel = 0; iChannel < channels; iChannel += 1) { pFramesOutF32[iFrame*channels + iChannel] = s; } } } else { for (iFrame = 0; iFrame < frameCount; iFrame += 1) { for (iChannel = 0; iChannel < channels; iChannel += 1) { pFramesOutF32[iFrame*channels + iChannel] = ma_noise_f32_pink(pNoise, iChannel); } } } } else if (pNoise->config.format == ma_format_s16) { ma_int16* pFramesOutS16 = (ma_int16*)pFramesOut; if (pNoise->config.duplicateChannels) { for (iFrame = 0; iFrame < frameCount; iFrame += 1) { ma_int16 s = ma_noise_s16_pink(pNoise, 0); for (iChannel = 0; iChannel < channels; iChannel += 1) { pFramesOutS16[iFrame*channels + iChannel] = s; } } } else { for (iFrame = 0; iFrame < frameCount; iFrame += 1) { for (iChannel = 0; iChannel < channels; iChannel += 1) { pFramesOutS16[iFrame*channels + iChannel] = ma_noise_s16_pink(pNoise, iChannel); } } } } else { const ma_uint32 bps = ma_get_bytes_per_sample(pNoise->config.format); const ma_uint32 bpf = bps * channels; if (pNoise->config.duplicateChannels) { for (iFrame = 0; iFrame < frameCount; iFrame += 1) { float s = ma_noise_f32_pink(pNoise, 0); for (iChannel = 0; iChannel < channels; iChannel += 1) { ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pNoise->config.format, &s, ma_format_f32, 1, ma_dither_mode_none); } } } else { for (iFrame = 0; iFrame < frameCount; iFrame += 1) { for (iChannel = 0; iChannel < channels; iChannel += 1) { float s = ma_noise_f32_pink(pNoise, iChannel); ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pNoise->config.format, &s, ma_format_f32, 1, ma_dither_mode_none); } } } } return frameCount; } static MA_INLINE float ma_noise_f32_brownian(ma_noise* pNoise, ma_uint32 iChannel) { double result; result = (ma_lcg_rand_f64(&pNoise->lcg) + pNoise->state.brownian.accumulation[iChannel]); result /= 1.005; /* Don't escape the -1..1 range on average. */ pNoise->state.brownian.accumulation[iChannel] = result; result /= 20; return (float)(result * pNoise->config.amplitude); } static MA_INLINE ma_int16 ma_noise_s16_brownian(ma_noise* pNoise, ma_uint32 iChannel) { return ma_pcm_sample_f32_to_s16(ma_noise_f32_brownian(pNoise, iChannel)); } static MA_INLINE ma_uint64 ma_noise_read_pcm_frames__brownian(ma_noise* pNoise, void* pFramesOut, ma_uint64 frameCount) { ma_uint64 iFrame; ma_uint32 iChannel; const ma_uint32 channels = pNoise->config.channels; MA_ASSUME(channels > 0); if (pNoise->config.format == ma_format_f32) { float* pFramesOutF32 = (float*)pFramesOut; if (pNoise->config.duplicateChannels) { for (iFrame = 0; iFrame < frameCount; iFrame += 1) { float s = ma_noise_f32_brownian(pNoise, 0); for (iChannel = 0; iChannel < channels; iChannel += 1) { pFramesOutF32[iFrame*channels + iChannel] = s; } } } else { for (iFrame = 0; iFrame < frameCount; iFrame += 1) { for (iChannel = 0; iChannel < channels; iChannel += 1) { pFramesOutF32[iFrame*channels + iChannel] = ma_noise_f32_brownian(pNoise, iChannel); } } } } else if (pNoise->config.format == ma_format_s16) { ma_int16* pFramesOutS16 = (ma_int16*)pFramesOut; if (pNoise->config.duplicateChannels) { for (iFrame = 0; iFrame < frameCount; iFrame += 1) { ma_int16 s = ma_noise_s16_brownian(pNoise, 0); for (iChannel = 0; iChannel < channels; iChannel += 1) { pFramesOutS16[iFrame*channels + iChannel] = s; } } } else { for (iFrame = 0; iFrame < frameCount; iFrame += 1) { for (iChannel = 0; iChannel < channels; iChannel += 1) { pFramesOutS16[iFrame*channels + iChannel] = ma_noise_s16_brownian(pNoise, iChannel); } } } } else { const ma_uint32 bps = ma_get_bytes_per_sample(pNoise->config.format); const ma_uint32 bpf = bps * channels; if (pNoise->config.duplicateChannels) { for (iFrame = 0; iFrame < frameCount; iFrame += 1) { float s = ma_noise_f32_brownian(pNoise, 0); for (iChannel = 0; iChannel < channels; iChannel += 1) { ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pNoise->config.format, &s, ma_format_f32, 1, ma_dither_mode_none); } } } else { for (iFrame = 0; iFrame < frameCount; iFrame += 1) { for (iChannel = 0; iChannel < channels; iChannel += 1) { float s = ma_noise_f32_brownian(pNoise, iChannel); ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pNoise->config.format, &s, ma_format_f32, 1, ma_dither_mode_none); } } } } return frameCount; } MA_API ma_result ma_noise_read_pcm_frames(ma_noise* pNoise, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) { ma_uint64 framesRead = 0; if (pFramesRead != NULL) { *pFramesRead = 0; } if (frameCount == 0) { return MA_INVALID_ARGS; } if (pNoise == NULL) { return MA_INVALID_ARGS; } /* The output buffer is allowed to be NULL. Since we aren't tracking cursors or anything we can just do nothing and pretend to be successful. */ if (pFramesOut == NULL) { framesRead = frameCount; } else { switch (pNoise->config.type) { case ma_noise_type_white: framesRead = ma_noise_read_pcm_frames__white (pNoise, pFramesOut, frameCount); break; case ma_noise_type_pink: framesRead = ma_noise_read_pcm_frames__pink (pNoise, pFramesOut, frameCount); break; case ma_noise_type_brownian: framesRead = ma_noise_read_pcm_frames__brownian(pNoise, pFramesOut, frameCount); break; default: return MA_INVALID_OPERATION; /* Unknown noise type. */ } } if (pFramesRead != NULL) { *pFramesRead = framesRead; } return MA_SUCCESS; } #endif /* MA_NO_GENERATION */ #ifndef MA_NO_RESOURCE_MANAGER #ifndef MA_RESOURCE_MANAGER_PAGE_SIZE_IN_MILLISECONDS #define MA_RESOURCE_MANAGER_PAGE_SIZE_IN_MILLISECONDS 1000 #endif #ifndef MA_JOB_TYPE_RESOURCE_MANAGER_QUEUE_CAPACITY #define MA_JOB_TYPE_RESOURCE_MANAGER_QUEUE_CAPACITY 1024 #endif MA_API ma_resource_manager_pipeline_notifications ma_resource_manager_pipeline_notifications_init(void) { ma_resource_manager_pipeline_notifications notifications; MA_ZERO_OBJECT(¬ifications); return notifications; } static void ma_resource_manager_pipeline_notifications_signal_all_notifications(const ma_resource_manager_pipeline_notifications* pPipelineNotifications) { if (pPipelineNotifications == NULL) { return; } if (pPipelineNotifications->init.pNotification) { ma_async_notification_signal(pPipelineNotifications->init.pNotification); } if (pPipelineNotifications->done.pNotification) { ma_async_notification_signal(pPipelineNotifications->done.pNotification); } } static void ma_resource_manager_pipeline_notifications_acquire_all_fences(const ma_resource_manager_pipeline_notifications* pPipelineNotifications) { if (pPipelineNotifications == NULL) { return; } if (pPipelineNotifications->init.pFence != NULL) { ma_fence_acquire(pPipelineNotifications->init.pFence); } if (pPipelineNotifications->done.pFence != NULL) { ma_fence_acquire(pPipelineNotifications->done.pFence); } } static void ma_resource_manager_pipeline_notifications_release_all_fences(const ma_resource_manager_pipeline_notifications* pPipelineNotifications) { if (pPipelineNotifications == NULL) { return; } if (pPipelineNotifications->init.pFence != NULL) { ma_fence_release(pPipelineNotifications->init.pFence); } if (pPipelineNotifications->done.pFence != NULL) { ma_fence_release(pPipelineNotifications->done.pFence); } } #ifndef MA_DEFAULT_HASH_SEED #define MA_DEFAULT_HASH_SEED 42 #endif /* MurmurHash3. Based on code from https://github.com/PeterScott/murmur3/blob/master/murmur3.c (public domain). */ #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) #pragma GCC diagnostic push #if __GNUC__ >= 7 #pragma GCC diagnostic ignored "-Wimplicit-fallthrough" #endif #endif static MA_INLINE ma_uint32 ma_rotl32(ma_uint32 x, ma_int8 r) { return (x << r) | (x >> (32 - r)); } static MA_INLINE ma_uint32 ma_hash_getblock(const ma_uint32* blocks, int i) { ma_uint32 block; /* Try silencing a sanitization warning about unaligned access by doing a memcpy() instead of assignment. */ MA_COPY_MEMORY(&block, ma_offset_ptr(blocks, i * sizeof(block)), sizeof(block)); if (ma_is_little_endian()) { return block; } else { return ma_swap_endian_uint32(block); } } static MA_INLINE ma_uint32 ma_hash_fmix32(ma_uint32 h) { h ^= h >> 16; h *= 0x85ebca6b; h ^= h >> 13; h *= 0xc2b2ae35; h ^= h >> 16; return h; } static ma_uint32 ma_hash_32(const void* key, int len, ma_uint32 seed) { const ma_uint8* data = (const ma_uint8*)key; const ma_uint32* blocks; const ma_uint8* tail; const int nblocks = len / 4; ma_uint32 h1 = seed; ma_uint32 c1 = 0xcc9e2d51; ma_uint32 c2 = 0x1b873593; ma_uint32 k1; int i; blocks = (const ma_uint32 *)(data + nblocks*4); for(i = -nblocks; i; i++) { k1 = ma_hash_getblock(blocks,i); k1 *= c1; k1 = ma_rotl32(k1, 15); k1 *= c2; h1 ^= k1; h1 = ma_rotl32(h1, 13); h1 = h1*5 + 0xe6546b64; } tail = (const ma_uint8*)(data + nblocks*4); k1 = 0; switch(len & 3) { case 3: k1 ^= tail[2] << 16; case 2: k1 ^= tail[1] << 8; case 1: k1 ^= tail[0]; k1 *= c1; k1 = ma_rotl32(k1, 15); k1 *= c2; h1 ^= k1; }; h1 ^= len; h1 = ma_hash_fmix32(h1); return h1; } #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) #pragma GCC diagnostic push #endif /* End MurmurHash3 */ static ma_uint32 ma_hash_string_32(const char* str) { return ma_hash_32(str, (int)strlen(str), MA_DEFAULT_HASH_SEED); } static ma_uint32 ma_hash_string_w_32(const wchar_t* str) { return ma_hash_32(str, (int)wcslen(str) * sizeof(*str), MA_DEFAULT_HASH_SEED); } /* Basic BST Functions */ static ma_result ma_resource_manager_data_buffer_node_search(ma_resource_manager* pResourceManager, ma_uint32 hashedName32, ma_resource_manager_data_buffer_node** ppDataBufferNode) { ma_resource_manager_data_buffer_node* pCurrentNode; MA_ASSERT(pResourceManager != NULL); MA_ASSERT(ppDataBufferNode != NULL); pCurrentNode = pResourceManager->pRootDataBufferNode; while (pCurrentNode != NULL) { if (hashedName32 == pCurrentNode->hashedName32) { break; /* Found. */ } else if (hashedName32 < pCurrentNode->hashedName32) { pCurrentNode = pCurrentNode->pChildLo; } else { pCurrentNode = pCurrentNode->pChildHi; } } *ppDataBufferNode = pCurrentNode; if (pCurrentNode == NULL) { return MA_DOES_NOT_EXIST; } else { return MA_SUCCESS; } } static ma_result ma_resource_manager_data_buffer_node_insert_point(ma_resource_manager* pResourceManager, ma_uint32 hashedName32, ma_resource_manager_data_buffer_node** ppInsertPoint) { ma_result result = MA_SUCCESS; ma_resource_manager_data_buffer_node* pCurrentNode; MA_ASSERT(pResourceManager != NULL); MA_ASSERT(ppInsertPoint != NULL); *ppInsertPoint = NULL; if (pResourceManager->pRootDataBufferNode == NULL) { return MA_SUCCESS; /* No items. */ } /* We need to find the node that will become the parent of the new node. If a node is found that already has the same hashed name we need to return MA_ALREADY_EXISTS. */ pCurrentNode = pResourceManager->pRootDataBufferNode; while (pCurrentNode != NULL) { if (hashedName32 == pCurrentNode->hashedName32) { result = MA_ALREADY_EXISTS; break; } else { if (hashedName32 < pCurrentNode->hashedName32) { if (pCurrentNode->pChildLo == NULL) { result = MA_SUCCESS; break; } else { pCurrentNode = pCurrentNode->pChildLo; } } else { if (pCurrentNode->pChildHi == NULL) { result = MA_SUCCESS; break; } else { pCurrentNode = pCurrentNode->pChildHi; } } } } *ppInsertPoint = pCurrentNode; return result; } static ma_result ma_resource_manager_data_buffer_node_insert_at(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer_node* pDataBufferNode, ma_resource_manager_data_buffer_node* pInsertPoint) { MA_ASSERT(pResourceManager != NULL); MA_ASSERT(pDataBufferNode != NULL); /* The key must have been set before calling this function. */ MA_ASSERT(pDataBufferNode->hashedName32 != 0); if (pInsertPoint == NULL) { /* It's the first node. */ pResourceManager->pRootDataBufferNode = pDataBufferNode; } else { /* It's not the first node. It needs to be inserted. */ if (pDataBufferNode->hashedName32 < pInsertPoint->hashedName32) { MA_ASSERT(pInsertPoint->pChildLo == NULL); pInsertPoint->pChildLo = pDataBufferNode; } else { MA_ASSERT(pInsertPoint->pChildHi == NULL); pInsertPoint->pChildHi = pDataBufferNode; } } pDataBufferNode->pParent = pInsertPoint; return MA_SUCCESS; } #if 0 /* Unused for now. */ static ma_result ma_resource_manager_data_buffer_node_insert(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer_node* pDataBufferNode) { ma_result result; ma_resource_manager_data_buffer_node* pInsertPoint; MA_ASSERT(pResourceManager != NULL); MA_ASSERT(pDataBufferNode != NULL); result = ma_resource_manager_data_buffer_node_insert_point(pResourceManager, pDataBufferNode->hashedName32, &pInsertPoint); if (result != MA_SUCCESS) { return MA_INVALID_ARGS; } return ma_resource_manager_data_buffer_node_insert_at(pResourceManager, pDataBufferNode, pInsertPoint); } #endif static MA_INLINE ma_resource_manager_data_buffer_node* ma_resource_manager_data_buffer_node_find_min(ma_resource_manager_data_buffer_node* pDataBufferNode) { ma_resource_manager_data_buffer_node* pCurrentNode; MA_ASSERT(pDataBufferNode != NULL); pCurrentNode = pDataBufferNode; while (pCurrentNode->pChildLo != NULL) { pCurrentNode = pCurrentNode->pChildLo; } return pCurrentNode; } static MA_INLINE ma_resource_manager_data_buffer_node* ma_resource_manager_data_buffer_node_find_max(ma_resource_manager_data_buffer_node* pDataBufferNode) { ma_resource_manager_data_buffer_node* pCurrentNode; MA_ASSERT(pDataBufferNode != NULL); pCurrentNode = pDataBufferNode; while (pCurrentNode->pChildHi != NULL) { pCurrentNode = pCurrentNode->pChildHi; } return pCurrentNode; } static MA_INLINE ma_resource_manager_data_buffer_node* ma_resource_manager_data_buffer_node_find_inorder_successor(ma_resource_manager_data_buffer_node* pDataBufferNode) { MA_ASSERT(pDataBufferNode != NULL); MA_ASSERT(pDataBufferNode->pChildHi != NULL); return ma_resource_manager_data_buffer_node_find_min(pDataBufferNode->pChildHi); } static MA_INLINE ma_resource_manager_data_buffer_node* ma_resource_manager_data_buffer_node_find_inorder_predecessor(ma_resource_manager_data_buffer_node* pDataBufferNode) { MA_ASSERT(pDataBufferNode != NULL); MA_ASSERT(pDataBufferNode->pChildLo != NULL); return ma_resource_manager_data_buffer_node_find_max(pDataBufferNode->pChildLo); } static ma_result ma_resource_manager_data_buffer_node_remove(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer_node* pDataBufferNode) { MA_ASSERT(pResourceManager != NULL); MA_ASSERT(pDataBufferNode != NULL); if (pDataBufferNode->pChildLo == NULL) { if (pDataBufferNode->pChildHi == NULL) { /* Simple case - deleting a buffer with no children. */ if (pDataBufferNode->pParent == NULL) { MA_ASSERT(pResourceManager->pRootDataBufferNode == pDataBufferNode); /* There is only a single buffer in the tree which should be equal to the root node. */ pResourceManager->pRootDataBufferNode = NULL; } else { if (pDataBufferNode->pParent->pChildLo == pDataBufferNode) { pDataBufferNode->pParent->pChildLo = NULL; } else { pDataBufferNode->pParent->pChildHi = NULL; } } } else { /* Node has one child - pChildHi != NULL. */ pDataBufferNode->pChildHi->pParent = pDataBufferNode->pParent; if (pDataBufferNode->pParent == NULL) { MA_ASSERT(pResourceManager->pRootDataBufferNode == pDataBufferNode); pResourceManager->pRootDataBufferNode = pDataBufferNode->pChildHi; } else { if (pDataBufferNode->pParent->pChildLo == pDataBufferNode) { pDataBufferNode->pParent->pChildLo = pDataBufferNode->pChildHi; } else { pDataBufferNode->pParent->pChildHi = pDataBufferNode->pChildHi; } } } } else { if (pDataBufferNode->pChildHi == NULL) { /* Node has one child - pChildLo != NULL. */ pDataBufferNode->pChildLo->pParent = pDataBufferNode->pParent; if (pDataBufferNode->pParent == NULL) { MA_ASSERT(pResourceManager->pRootDataBufferNode == pDataBufferNode); pResourceManager->pRootDataBufferNode = pDataBufferNode->pChildLo; } else { if (pDataBufferNode->pParent->pChildLo == pDataBufferNode) { pDataBufferNode->pParent->pChildLo = pDataBufferNode->pChildLo; } else { pDataBufferNode->pParent->pChildHi = pDataBufferNode->pChildLo; } } } else { /* Complex case - deleting a node with two children. */ ma_resource_manager_data_buffer_node* pReplacementDataBufferNode; /* For now we are just going to use the in-order successor as the replacement, but we may want to try to keep this balanced by switching between the two. */ pReplacementDataBufferNode = ma_resource_manager_data_buffer_node_find_inorder_successor(pDataBufferNode); MA_ASSERT(pReplacementDataBufferNode != NULL); /* Now that we have our replacement node we can make the change. The simple way to do this would be to just exchange the values, and then remove the replacement node, however we track specific nodes via pointers which means we can't just swap out the values. We need to instead just change the pointers around. The replacement node should have at most 1 child. Therefore, we can detach it in terms of our simpler cases above. What we're essentially doing is detaching the replacement node and reinserting it into the same position as the deleted node. */ MA_ASSERT(pReplacementDataBufferNode->pParent != NULL); /* The replacement node should never be the root which means it should always have a parent. */ MA_ASSERT(pReplacementDataBufferNode->pChildLo == NULL); /* Because we used in-order successor. This would be pChildHi == NULL if we used in-order predecessor. */ if (pReplacementDataBufferNode->pChildHi == NULL) { if (pReplacementDataBufferNode->pParent->pChildLo == pReplacementDataBufferNode) { pReplacementDataBufferNode->pParent->pChildLo = NULL; } else { pReplacementDataBufferNode->pParent->pChildHi = NULL; } } else { pReplacementDataBufferNode->pChildHi->pParent = pReplacementDataBufferNode->pParent; if (pReplacementDataBufferNode->pParent->pChildLo == pReplacementDataBufferNode) { pReplacementDataBufferNode->pParent->pChildLo = pReplacementDataBufferNode->pChildHi; } else { pReplacementDataBufferNode->pParent->pChildHi = pReplacementDataBufferNode->pChildHi; } } /* The replacement node has essentially been detached from the binary tree, so now we need to replace the old data buffer with it. The first thing to update is the parent */ if (pDataBufferNode->pParent != NULL) { if (pDataBufferNode->pParent->pChildLo == pDataBufferNode) { pDataBufferNode->pParent->pChildLo = pReplacementDataBufferNode; } else { pDataBufferNode->pParent->pChildHi = pReplacementDataBufferNode; } } /* Now need to update the replacement node's pointers. */ pReplacementDataBufferNode->pParent = pDataBufferNode->pParent; pReplacementDataBufferNode->pChildLo = pDataBufferNode->pChildLo; pReplacementDataBufferNode->pChildHi = pDataBufferNode->pChildHi; /* Now the children of the replacement node need to have their parent pointers updated. */ if (pReplacementDataBufferNode->pChildLo != NULL) { pReplacementDataBufferNode->pChildLo->pParent = pReplacementDataBufferNode; } if (pReplacementDataBufferNode->pChildHi != NULL) { pReplacementDataBufferNode->pChildHi->pParent = pReplacementDataBufferNode; } /* Now the root node needs to be updated. */ if (pResourceManager->pRootDataBufferNode == pDataBufferNode) { pResourceManager->pRootDataBufferNode = pReplacementDataBufferNode; } } } return MA_SUCCESS; } #if 0 /* Unused for now. */ static ma_result ma_resource_manager_data_buffer_node_remove_by_key(ma_resource_manager* pResourceManager, ma_uint32 hashedName32) { ma_result result; ma_resource_manager_data_buffer_node* pDataBufferNode; result = ma_resource_manager_data_buffer_search(pResourceManager, hashedName32, &pDataBufferNode); if (result != MA_SUCCESS) { return result; /* Could not find the data buffer. */ } return ma_resource_manager_data_buffer_remove(pResourceManager, pDataBufferNode); } #endif static ma_resource_manager_data_supply_type ma_resource_manager_data_buffer_node_get_data_supply_type(ma_resource_manager_data_buffer_node* pDataBufferNode) { return (ma_resource_manager_data_supply_type)c89atomic_load_i32(&pDataBufferNode->data.type); } static void ma_resource_manager_data_buffer_node_set_data_supply_type(ma_resource_manager_data_buffer_node* pDataBufferNode, ma_resource_manager_data_supply_type supplyType) { c89atomic_exchange_i32(&pDataBufferNode->data.type, supplyType); } static ma_result ma_resource_manager_data_buffer_node_increment_ref(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer_node* pDataBufferNode, ma_uint32* pNewRefCount) { ma_uint32 refCount; MA_ASSERT(pResourceManager != NULL); MA_ASSERT(pDataBufferNode != NULL); (void)pResourceManager; refCount = c89atomic_fetch_add_32(&pDataBufferNode->refCount, 1) + 1; if (pNewRefCount != NULL) { *pNewRefCount = refCount; } return MA_SUCCESS; } static ma_result ma_resource_manager_data_buffer_node_decrement_ref(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer_node* pDataBufferNode, ma_uint32* pNewRefCount) { ma_uint32 refCount; MA_ASSERT(pResourceManager != NULL); MA_ASSERT(pDataBufferNode != NULL); (void)pResourceManager; refCount = c89atomic_fetch_sub_32(&pDataBufferNode->refCount, 1) - 1; if (pNewRefCount != NULL) { *pNewRefCount = refCount; } return MA_SUCCESS; } static void ma_resource_manager_data_buffer_node_free(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer_node* pDataBufferNode) { MA_ASSERT(pResourceManager != NULL); MA_ASSERT(pDataBufferNode != NULL); if (pDataBufferNode->isDataOwnedByResourceManager) { if (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBufferNode) == ma_resource_manager_data_supply_type_encoded) { ma_free((void*)pDataBufferNode->data.backend.encoded.pData, &pResourceManager->config.allocationCallbacks); pDataBufferNode->data.backend.encoded.pData = NULL; pDataBufferNode->data.backend.encoded.sizeInBytes = 0; } else if (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBufferNode) == ma_resource_manager_data_supply_type_decoded) { ma_free((void*)pDataBufferNode->data.backend.decoded.pData, &pResourceManager->config.allocationCallbacks); pDataBufferNode->data.backend.decoded.pData = NULL; pDataBufferNode->data.backend.decoded.totalFrameCount = 0; } else if (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBufferNode) == ma_resource_manager_data_supply_type_decoded_paged) { ma_paged_audio_buffer_data_uninit(&pDataBufferNode->data.backend.decodedPaged.data, &pResourceManager->config.allocationCallbacks); } else { /* Should never hit this if the node was successfully initialized. */ MA_ASSERT(pDataBufferNode->result != MA_SUCCESS); } } /* The data buffer itself needs to be freed. */ ma_free(pDataBufferNode, &pResourceManager->config.allocationCallbacks); } static ma_result ma_resource_manager_data_buffer_node_result(const ma_resource_manager_data_buffer_node* pDataBufferNode) { MA_ASSERT(pDataBufferNode != NULL); return (ma_result)c89atomic_load_i32((ma_result*)&pDataBufferNode->result); /* Need a naughty const-cast here. */ } static ma_bool32 ma_resource_manager_is_threading_enabled(const ma_resource_manager* pResourceManager) { MA_ASSERT(pResourceManager != NULL); return (pResourceManager->config.flags & MA_RESOURCE_MANAGER_FLAG_NO_THREADING) == 0; } typedef struct { union { ma_async_notification_event e; ma_async_notification_poll p; } backend; /* Must be the first member. */ ma_resource_manager* pResourceManager; } ma_resource_manager_inline_notification; static ma_result ma_resource_manager_inline_notification_init(ma_resource_manager* pResourceManager, ma_resource_manager_inline_notification* pNotification) { MA_ASSERT(pResourceManager != NULL); MA_ASSERT(pNotification != NULL); pNotification->pResourceManager = pResourceManager; if (ma_resource_manager_is_threading_enabled(pResourceManager)) { return ma_async_notification_event_init(&pNotification->backend.e); } else { return ma_async_notification_poll_init(&pNotification->backend.p); } } static void ma_resource_manager_inline_notification_uninit(ma_resource_manager_inline_notification* pNotification) { MA_ASSERT(pNotification != NULL); if (ma_resource_manager_is_threading_enabled(pNotification->pResourceManager)) { ma_async_notification_event_uninit(&pNotification->backend.e); } else { /* No need to uninitialize a polling notification. */ } } static void ma_resource_manager_inline_notification_wait(ma_resource_manager_inline_notification* pNotification) { MA_ASSERT(pNotification != NULL); if (ma_resource_manager_is_threading_enabled(pNotification->pResourceManager)) { ma_async_notification_event_wait(&pNotification->backend.e); } else { while (ma_async_notification_poll_is_signalled(&pNotification->backend.p) == MA_FALSE) { ma_result result = ma_resource_manager_process_next_job(pNotification->pResourceManager); if (result == MA_NO_DATA_AVAILABLE || result == MA_CANCELLED) { break; } } } } static void ma_resource_manager_inline_notification_wait_and_uninit(ma_resource_manager_inline_notification* pNotification) { ma_resource_manager_inline_notification_wait(pNotification); ma_resource_manager_inline_notification_uninit(pNotification); } static void ma_resource_manager_data_buffer_bst_lock(ma_resource_manager* pResourceManager) { MA_ASSERT(pResourceManager != NULL); if (ma_resource_manager_is_threading_enabled(pResourceManager)) { #ifndef MA_NO_THREADING { ma_mutex_lock(&pResourceManager->dataBufferBSTLock); } #else { MA_ASSERT(MA_FALSE); /* Should never hit this. */ } #endif } else { /* Threading not enabled. Do nothing. */ } } static void ma_resource_manager_data_buffer_bst_unlock(ma_resource_manager* pResourceManager) { MA_ASSERT(pResourceManager != NULL); if (ma_resource_manager_is_threading_enabled(pResourceManager)) { #ifndef MA_NO_THREADING { ma_mutex_unlock(&pResourceManager->dataBufferBSTLock); } #else { MA_ASSERT(MA_FALSE); /* Should never hit this. */ } #endif } else { /* Threading not enabled. Do nothing. */ } } #ifndef MA_NO_THREADING static ma_thread_result MA_THREADCALL ma_resource_manager_job_thread(void* pUserData) { ma_resource_manager* pResourceManager = (ma_resource_manager*)pUserData; MA_ASSERT(pResourceManager != NULL); for (;;) { ma_result result; ma_job job; result = ma_resource_manager_next_job(pResourceManager, &job); if (result != MA_SUCCESS) { break; } /* Terminate if we got a quit message. */ if (job.toc.breakup.code == MA_JOB_TYPE_QUIT) { break; } ma_job_process(&job); } return (ma_thread_result)0; } #endif MA_API ma_resource_manager_config ma_resource_manager_config_init(void) { ma_resource_manager_config config; MA_ZERO_OBJECT(&config); config.decodedFormat = ma_format_unknown; config.decodedChannels = 0; config.decodedSampleRate = 0; config.jobThreadCount = 1; /* A single miniaudio-managed job thread by default. */ config.jobQueueCapacity = MA_JOB_TYPE_RESOURCE_MANAGER_QUEUE_CAPACITY; /* Flags. */ config.flags = 0; #ifdef MA_NO_THREADING { /* Threading is disabled at compile time so disable threading at runtime as well by default. */ config.flags |= MA_RESOURCE_MANAGER_FLAG_NO_THREADING; config.jobThreadCount = 0; } #endif return config; } MA_API ma_result ma_resource_manager_init(const ma_resource_manager_config* pConfig, ma_resource_manager* pResourceManager) { ma_result result; ma_job_queue_config jobQueueConfig; if (pResourceManager == NULL) { return MA_INVALID_ARGS; } MA_ZERO_OBJECT(pResourceManager); if (pConfig == NULL) { return MA_INVALID_ARGS; } #ifndef MA_NO_THREADING { if (pConfig->jobThreadCount > ma_countof(pResourceManager->jobThreads)) { return MA_INVALID_ARGS; /* Requesting too many job threads. */ } } #endif pResourceManager->config = *pConfig; ma_allocation_callbacks_init_copy(&pResourceManager->config.allocationCallbacks, &pConfig->allocationCallbacks); /* Get the log set up early so we can start using it as soon as possible. */ if (pResourceManager->config.pLog == NULL) { result = ma_log_init(&pResourceManager->config.allocationCallbacks, &pResourceManager->log); if (result == MA_SUCCESS) { pResourceManager->config.pLog = &pResourceManager->log; } else { pResourceManager->config.pLog = NULL; /* Logging is unavailable. */ } } if (pResourceManager->config.pVFS == NULL) { result = ma_default_vfs_init(&pResourceManager->defaultVFS, &pResourceManager->config.allocationCallbacks); if (result != MA_SUCCESS) { return result; /* Failed to initialize the default file system. */ } pResourceManager->config.pVFS = &pResourceManager->defaultVFS; } /* If threading has been disabled at compile time, enfore it at run time as well. */ #ifdef MA_NO_THREADING { pResourceManager->config.flags |= MA_RESOURCE_MANAGER_FLAG_NO_THREADING; } #endif /* We need to force MA_RESOURCE_MANAGER_FLAG_NON_BLOCKING if MA_RESOURCE_MANAGER_FLAG_NO_THREADING is set. */ if ((pResourceManager->config.flags & MA_RESOURCE_MANAGER_FLAG_NO_THREADING) != 0) { pResourceManager->config.flags |= MA_RESOURCE_MANAGER_FLAG_NON_BLOCKING; /* We cannot allow job threads when MA_RESOURCE_MANAGER_FLAG_NO_THREADING has been set. This is an invalid use case. */ if (pResourceManager->config.jobThreadCount > 0) { return MA_INVALID_ARGS; } } /* Job queue. */ jobQueueConfig.capacity = pResourceManager->config.jobQueueCapacity; jobQueueConfig.flags = 0; if ((pResourceManager->config.flags & MA_RESOURCE_MANAGER_FLAG_NON_BLOCKING) != 0) { if (pResourceManager->config.jobThreadCount > 0) { return MA_INVALID_ARGS; /* Non-blocking mode is only valid for self-managed job threads. */ } jobQueueConfig.flags |= MA_JOB_QUEUE_FLAG_NON_BLOCKING; } result = ma_job_queue_init(&jobQueueConfig, &pResourceManager->config.allocationCallbacks, &pResourceManager->jobQueue); if (result != MA_SUCCESS) { return result; } /* Custom decoding backends. */ if (pConfig->ppCustomDecodingBackendVTables != NULL && pConfig->customDecodingBackendCount > 0) { size_t sizeInBytes = sizeof(*pResourceManager->config.ppCustomDecodingBackendVTables) * pConfig->customDecodingBackendCount; pResourceManager->config.ppCustomDecodingBackendVTables = (ma_decoding_backend_vtable**)ma_malloc(sizeInBytes, &pResourceManager->config.allocationCallbacks); if (pResourceManager->config.ppCustomDecodingBackendVTables == NULL) { ma_job_queue_uninit(&pResourceManager->jobQueue, &pResourceManager->config.allocationCallbacks); return MA_OUT_OF_MEMORY; } MA_COPY_MEMORY(pResourceManager->config.ppCustomDecodingBackendVTables, pConfig->ppCustomDecodingBackendVTables, sizeInBytes); pResourceManager->config.customDecodingBackendCount = pConfig->customDecodingBackendCount; pResourceManager->config.pCustomDecodingBackendUserData = pConfig->pCustomDecodingBackendUserData; } /* Here is where we initialize our threading stuff. We don't do this if we don't support threading. */ if (ma_resource_manager_is_threading_enabled(pResourceManager)) { #ifndef MA_NO_THREADING { ma_uint32 iJobThread; /* Data buffer lock. */ result = ma_mutex_init(&pResourceManager->dataBufferBSTLock); if (result != MA_SUCCESS) { ma_job_queue_uninit(&pResourceManager->jobQueue, &pResourceManager->config.allocationCallbacks); return result; } /* Create the job threads last to ensure the threads has access to valid data. */ for (iJobThread = 0; iJobThread < pResourceManager->config.jobThreadCount; iJobThread += 1) { result = ma_thread_create(&pResourceManager->jobThreads[iJobThread], ma_thread_priority_normal, 0, ma_resource_manager_job_thread, pResourceManager, &pResourceManager->config.allocationCallbacks); if (result != MA_SUCCESS) { ma_mutex_uninit(&pResourceManager->dataBufferBSTLock); ma_job_queue_uninit(&pResourceManager->jobQueue, &pResourceManager->config.allocationCallbacks); return result; } } } #else { /* Threading is disabled at compile time. We should never get here because validation checks should have already been performed. */ MA_ASSERT(MA_FALSE); } #endif } return MA_SUCCESS; } static void ma_resource_manager_delete_all_data_buffer_nodes(ma_resource_manager* pResourceManager) { MA_ASSERT(pResourceManager); /* If everything was done properly, there shouldn't be any active data buffers. */ while (pResourceManager->pRootDataBufferNode != NULL) { ma_resource_manager_data_buffer_node* pDataBufferNode = pResourceManager->pRootDataBufferNode; ma_resource_manager_data_buffer_node_remove(pResourceManager, pDataBufferNode); /* The data buffer has been removed from the BST, so now we need to free it's data. */ ma_resource_manager_data_buffer_node_free(pResourceManager, pDataBufferNode); } } MA_API void ma_resource_manager_uninit(ma_resource_manager* pResourceManager) { if (pResourceManager == NULL) { return; } /* Job threads need to be killed first. To do this we need to post a quit message to the message queue and then wait for the thread. The quit message will never be removed from the queue which means it will never not be returned after being encounted for the first time which means all threads will eventually receive it. */ ma_resource_manager_post_job_quit(pResourceManager); /* Wait for every job to finish before continuing to ensure nothing is sill trying to access any of our objects below. */ if (ma_resource_manager_is_threading_enabled(pResourceManager)) { #ifndef MA_NO_THREADING { ma_uint32 iJobThread; for (iJobThread = 0; iJobThread < pResourceManager->config.jobThreadCount; iJobThread += 1) { ma_thread_wait(&pResourceManager->jobThreads[iJobThread]); } } #else { MA_ASSERT(MA_FALSE); /* Should never hit this. */ } #endif } /* At this point the thread should have returned and no other thread should be accessing our data. We can now delete all data buffers. */ ma_resource_manager_delete_all_data_buffer_nodes(pResourceManager); /* The job queue is no longer needed. */ ma_job_queue_uninit(&pResourceManager->jobQueue, &pResourceManager->config.allocationCallbacks); /* We're no longer doing anything with data buffers so the lock can now be uninitialized. */ if (ma_resource_manager_is_threading_enabled(pResourceManager)) { #ifndef MA_NO_THREADING { ma_mutex_uninit(&pResourceManager->dataBufferBSTLock); } #else { MA_ASSERT(MA_FALSE); /* Should never hit this. */ } #endif } ma_free(pResourceManager->config.ppCustomDecodingBackendVTables, &pResourceManager->config.allocationCallbacks); if (pResourceManager->config.pLog == &pResourceManager->log) { ma_log_uninit(&pResourceManager->log); } } MA_API ma_log* ma_resource_manager_get_log(ma_resource_manager* pResourceManager) { if (pResourceManager == NULL) { return NULL; } return pResourceManager->config.pLog; } MA_API ma_resource_manager_data_source_config ma_resource_manager_data_source_config_init(void) { ma_resource_manager_data_source_config config; MA_ZERO_OBJECT(&config); config.rangeEndInPCMFrames = ~((ma_uint64)0); config.loopPointEndInPCMFrames = ~((ma_uint64)0); return config; } static ma_decoder_config ma_resource_manager__init_decoder_config(ma_resource_manager* pResourceManager) { ma_decoder_config config; config = ma_decoder_config_init(pResourceManager->config.decodedFormat, pResourceManager->config.decodedChannels, pResourceManager->config.decodedSampleRate); config.allocationCallbacks = pResourceManager->config.allocationCallbacks; config.ppCustomBackendVTables = pResourceManager->config.ppCustomDecodingBackendVTables; config.customBackendCount = pResourceManager->config.customDecodingBackendCount; config.pCustomBackendUserData = pResourceManager->config.pCustomDecodingBackendUserData; return config; } static ma_result ma_resource_manager__init_decoder(ma_resource_manager* pResourceManager, const char* pFilePath, const wchar_t* pFilePathW, ma_decoder* pDecoder) { ma_result result; ma_decoder_config config; MA_ASSERT(pResourceManager != NULL); MA_ASSERT(pFilePath != NULL || pFilePathW != NULL); MA_ASSERT(pDecoder != NULL); config = ma_resource_manager__init_decoder_config(pResourceManager); if (pFilePath != NULL) { result = ma_decoder_init_vfs(pResourceManager->config.pVFS, pFilePath, &config, pDecoder); if (result != MA_SUCCESS) { ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_WARNING, "Failed to load file \"%s\". %s.\n", pFilePath, ma_result_description(result)); return result; } } else { result = ma_decoder_init_vfs_w(pResourceManager->config.pVFS, pFilePathW, &config, pDecoder); if (result != MA_SUCCESS) { #if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(_MSC_VER) ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_WARNING, "Failed to load file \"%ls\". %s.\n", pFilePathW, ma_result_description(result)); #endif return result; } } return MA_SUCCESS; } static ma_data_source* ma_resource_manager_data_buffer_get_connector(ma_resource_manager_data_buffer* pDataBuffer) { switch (pDataBuffer->pNode->data.type) { case ma_resource_manager_data_supply_type_encoded: return &pDataBuffer->connector.decoder; case ma_resource_manager_data_supply_type_decoded: return &pDataBuffer->connector.buffer; case ma_resource_manager_data_supply_type_decoded_paged: return &pDataBuffer->connector.pagedBuffer; case ma_resource_manager_data_supply_type_unknown: default: { ma_log_postf(ma_resource_manager_get_log(pDataBuffer->pResourceManager), MA_LOG_LEVEL_ERROR, "Failed to retrieve data buffer connector. Unknown data supply type.\n"); return NULL; }; }; } static ma_result ma_resource_manager_data_buffer_init_connector(ma_resource_manager_data_buffer* pDataBuffer, const ma_resource_manager_data_source_config* pConfig, ma_async_notification* pInitNotification, ma_fence* pInitFence) { ma_result result; MA_ASSERT(pDataBuffer != NULL); MA_ASSERT(pConfig != NULL); MA_ASSERT(pDataBuffer->isConnectorInitialized == MA_FALSE); /* The underlying data buffer must be initialized before we'll be able to know how to initialize the backend. */ result = ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode); if (result != MA_SUCCESS && result != MA_BUSY) { return result; /* The data buffer is in an erroneous state. */ } /* We need to initialize either a ma_decoder or an ma_audio_buffer depending on whether or not the backing data is encoded or decoded. These act as the "instance" to the data and are used to form the connection between underlying data buffer and the data source. If the data buffer is decoded, we can use an ma_audio_buffer. This enables us to use memory mapping when mixing which saves us a bit of data movement overhead. */ switch (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode)) { case ma_resource_manager_data_supply_type_encoded: /* Connector is a decoder. */ { ma_decoder_config config; config = ma_resource_manager__init_decoder_config(pDataBuffer->pResourceManager); result = ma_decoder_init_memory(pDataBuffer->pNode->data.backend.encoded.pData, pDataBuffer->pNode->data.backend.encoded.sizeInBytes, &config, &pDataBuffer->connector.decoder); } break; case ma_resource_manager_data_supply_type_decoded: /* Connector is an audio buffer. */ { ma_audio_buffer_config config; config = ma_audio_buffer_config_init(pDataBuffer->pNode->data.backend.decoded.format, pDataBuffer->pNode->data.backend.decoded.channels, pDataBuffer->pNode->data.backend.decoded.totalFrameCount, pDataBuffer->pNode->data.backend.decoded.pData, NULL); result = ma_audio_buffer_init(&config, &pDataBuffer->connector.buffer); } break; case ma_resource_manager_data_supply_type_decoded_paged: /* Connector is a paged audio buffer. */ { ma_paged_audio_buffer_config config; config = ma_paged_audio_buffer_config_init(&pDataBuffer->pNode->data.backend.decodedPaged.data); result = ma_paged_audio_buffer_init(&config, &pDataBuffer->connector.pagedBuffer); } break; case ma_resource_manager_data_supply_type_unknown: default: { /* Unknown data supply type. Should never happen. Need to post an error here. */ return MA_INVALID_ARGS; }; } /* Initialization of the connector is when we can fire the init notification. This will give the application access to the format/channels/rate of the data source. */ if (result == MA_SUCCESS) { /* Make sure the looping state is set before returning in order to handle the case where the loop state was set on the data buffer before the connector was initialized. */ ma_data_source_set_range_in_pcm_frames(pDataBuffer, pConfig->rangeBegInPCMFrames, pConfig->rangeEndInPCMFrames); ma_data_source_set_loop_point_in_pcm_frames(pDataBuffer, pConfig->loopPointBegInPCMFrames, pConfig->loopPointEndInPCMFrames); ma_data_source_set_looping(pDataBuffer, pConfig->isLooping); pDataBuffer->isConnectorInitialized = MA_TRUE; if (pInitNotification != NULL) { ma_async_notification_signal(pInitNotification); } if (pInitFence != NULL) { ma_fence_release(pInitFence); } } /* At this point the backend should be initialized. We do *not* want to set pDataSource->result here - that needs to be done at a higher level to ensure it's done as the last step. */ return result; } static ma_result ma_resource_manager_data_buffer_uninit_connector(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer* pDataBuffer) { MA_ASSERT(pResourceManager != NULL); MA_ASSERT(pDataBuffer != NULL); switch (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode)) { case ma_resource_manager_data_supply_type_encoded: /* Connector is a decoder. */ { ma_decoder_uninit(&pDataBuffer->connector.decoder); } break; case ma_resource_manager_data_supply_type_decoded: /* Connector is an audio buffer. */ { ma_audio_buffer_uninit(&pDataBuffer->connector.buffer); } break; case ma_resource_manager_data_supply_type_decoded_paged: /* Connector is a paged audio buffer. */ { ma_paged_audio_buffer_uninit(&pDataBuffer->connector.pagedBuffer); } break; case ma_resource_manager_data_supply_type_unknown: default: { /* Unknown data supply type. Should never happen. Need to post an error here. */ return MA_INVALID_ARGS; }; } return MA_SUCCESS; } static ma_uint32 ma_resource_manager_data_buffer_node_next_execution_order(ma_resource_manager_data_buffer_node* pDataBufferNode) { MA_ASSERT(pDataBufferNode != NULL); return c89atomic_fetch_add_32(&pDataBufferNode->executionCounter, 1); } static ma_result ma_resource_manager_data_buffer_node_init_supply_encoded(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer_node* pDataBufferNode, const char* pFilePath, const wchar_t* pFilePathW) { ma_result result; size_t dataSizeInBytes; void* pData; MA_ASSERT(pResourceManager != NULL); MA_ASSERT(pDataBufferNode != NULL); MA_ASSERT(pFilePath != NULL || pFilePathW != NULL); result = ma_vfs_open_and_read_file_ex(pResourceManager->config.pVFS, pFilePath, pFilePathW, &pData, &dataSizeInBytes, &pResourceManager->config.allocationCallbacks); if (result != MA_SUCCESS) { if (pFilePath != NULL) { ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_WARNING, "Failed to load file \"%s\". %s.\n", pFilePath, ma_result_description(result)); } else { #if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(_MSC_VER) ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_WARNING, "Failed to load file \"%ls\". %s.\n", pFilePathW, ma_result_description(result)); #endif } return result; } pDataBufferNode->data.backend.encoded.pData = pData; pDataBufferNode->data.backend.encoded.sizeInBytes = dataSizeInBytes; ma_resource_manager_data_buffer_node_set_data_supply_type(pDataBufferNode, ma_resource_manager_data_supply_type_encoded); /* <-- Must be set last. */ return MA_SUCCESS; } static ma_result ma_resource_manager_data_buffer_node_init_supply_decoded(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer_node* pDataBufferNode, const char* pFilePath, const wchar_t* pFilePathW, ma_uint32 flags, ma_decoder** ppDecoder) { ma_result result = MA_SUCCESS; ma_decoder* pDecoder; ma_uint64 totalFrameCount; MA_ASSERT(pResourceManager != NULL); MA_ASSERT(pDataBufferNode != NULL); MA_ASSERT(ppDecoder != NULL); MA_ASSERT(pFilePath != NULL || pFilePathW != NULL); *ppDecoder = NULL; /* For safety. */ pDecoder = (ma_decoder*)ma_malloc(sizeof(*pDecoder), &pResourceManager->config.allocationCallbacks); if (pDecoder == NULL) { return MA_OUT_OF_MEMORY; } result = ma_resource_manager__init_decoder(pResourceManager, pFilePath, pFilePathW, pDecoder); if (result != MA_SUCCESS) { ma_free(pDecoder, &pResourceManager->config.allocationCallbacks); return result; } /* At this point we have the decoder and we now need to initialize the data supply. This will be either a decoded buffer, or a decoded paged buffer. A regular buffer is just one big heap allocated buffer, whereas a paged buffer is a linked list of paged-sized buffers. The latter is used when the length of a sound is unknown until a full decode has been performed. */ if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_UNKNOWN_LENGTH) == 0) { result = ma_decoder_get_length_in_pcm_frames(pDecoder, &totalFrameCount); if (result != MA_SUCCESS) { return result; } } else { totalFrameCount = 0; } if (totalFrameCount > 0) { /* It's a known length. The data supply is a regular decoded buffer. */ ma_uint64 dataSizeInBytes; void* pData; dataSizeInBytes = totalFrameCount * ma_get_bytes_per_frame(pDecoder->outputFormat, pDecoder->outputChannels); if (dataSizeInBytes > MA_SIZE_MAX) { ma_decoder_uninit(pDecoder); ma_free(pDecoder, &pResourceManager->config.allocationCallbacks); return MA_TOO_BIG; } pData = ma_malloc((size_t)dataSizeInBytes, &pResourceManager->config.allocationCallbacks); if (pData == NULL) { ma_decoder_uninit(pDecoder); ma_free(pDecoder, &pResourceManager->config.allocationCallbacks); return MA_OUT_OF_MEMORY; } /* The buffer needs to be initialized to silence in case the caller reads from it. */ ma_silence_pcm_frames(pData, totalFrameCount, pDecoder->outputFormat, pDecoder->outputChannels); /* Data has been allocated and the data supply can now be initialized. */ pDataBufferNode->data.backend.decoded.pData = pData; pDataBufferNode->data.backend.decoded.totalFrameCount = totalFrameCount; pDataBufferNode->data.backend.decoded.format = pDecoder->outputFormat; pDataBufferNode->data.backend.decoded.channels = pDecoder->outputChannels; pDataBufferNode->data.backend.decoded.sampleRate = pDecoder->outputSampleRate; pDataBufferNode->data.backend.decoded.decodedFrameCount = 0; ma_resource_manager_data_buffer_node_set_data_supply_type(pDataBufferNode, ma_resource_manager_data_supply_type_decoded); /* <-- Must be set last. */ } else { /* It's an unknown length. The data supply is a paged decoded buffer. Setting this up is actually easier than the non-paged decoded buffer because we just need to initialize a ma_paged_audio_buffer object. */ result = ma_paged_audio_buffer_data_init(pDecoder->outputFormat, pDecoder->outputChannels, &pDataBufferNode->data.backend.decodedPaged.data); if (result != MA_SUCCESS) { ma_decoder_uninit(pDecoder); ma_free(pDecoder, &pResourceManager->config.allocationCallbacks); return result; } pDataBufferNode->data.backend.decodedPaged.sampleRate = pDecoder->outputSampleRate; pDataBufferNode->data.backend.decodedPaged.decodedFrameCount = 0; ma_resource_manager_data_buffer_node_set_data_supply_type(pDataBufferNode, ma_resource_manager_data_supply_type_decoded_paged); /* <-- Must be set last. */ } *ppDecoder = pDecoder; return MA_SUCCESS; } static ma_result ma_resource_manager_data_buffer_node_decode_next_page(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer_node* pDataBufferNode, ma_decoder* pDecoder) { ma_result result = MA_SUCCESS; ma_uint64 pageSizeInFrames; ma_uint64 framesToTryReading; ma_uint64 framesRead; MA_ASSERT(pResourceManager != NULL); MA_ASSERT(pDataBufferNode != NULL); MA_ASSERT(pDecoder != NULL); /* We need to know the size of a page in frames to know how many frames to decode. */ pageSizeInFrames = MA_RESOURCE_MANAGER_PAGE_SIZE_IN_MILLISECONDS * (pDecoder->outputSampleRate/1000); framesToTryReading = pageSizeInFrames; /* Here is where we do the decoding of the next page. We'll run a slightly different path depending on whether or not we're using a flat or paged buffer because the allocation of the page differs between the two. For a flat buffer it's an offset to an already-allocated buffer. For a paged buffer, we need to allocate a new page and attach it to the linked list. */ switch (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBufferNode)) { case ma_resource_manager_data_supply_type_decoded: { /* The destination buffer is an offset to the existing buffer. Don't read more than we originally retrieved when we first initialized the decoder. */ void* pDst; ma_uint64 framesRemaining = pDataBufferNode->data.backend.decoded.totalFrameCount - pDataBufferNode->data.backend.decoded.decodedFrameCount; if (framesToTryReading > framesRemaining) { framesToTryReading = framesRemaining; } if (framesToTryReading > 0) { pDst = ma_offset_ptr( pDataBufferNode->data.backend.decoded.pData, pDataBufferNode->data.backend.decoded.decodedFrameCount * ma_get_bytes_per_frame(pDataBufferNode->data.backend.decoded.format, pDataBufferNode->data.backend.decoded.channels) ); MA_ASSERT(pDst != NULL); result = ma_decoder_read_pcm_frames(pDecoder, pDst, framesToTryReading, &framesRead); if (framesRead > 0) { pDataBufferNode->data.backend.decoded.decodedFrameCount += framesRead; } } else { framesRead = 0; } } break; case ma_resource_manager_data_supply_type_decoded_paged: { /* The destination buffer is a freshly allocated page. */ ma_paged_audio_buffer_page* pPage; result = ma_paged_audio_buffer_data_allocate_page(&pDataBufferNode->data.backend.decodedPaged.data, framesToTryReading, NULL, &pResourceManager->config.allocationCallbacks, &pPage); if (result != MA_SUCCESS) { return result; } result = ma_decoder_read_pcm_frames(pDecoder, pPage->pAudioData, framesToTryReading, &framesRead); if (framesRead > 0) { pPage->sizeInFrames = framesRead; result = ma_paged_audio_buffer_data_append_page(&pDataBufferNode->data.backend.decodedPaged.data, pPage); if (result == MA_SUCCESS) { pDataBufferNode->data.backend.decodedPaged.decodedFrameCount += framesRead; } else { /* Failed to append the page. Just abort and set the status to MA_AT_END. */ ma_paged_audio_buffer_data_free_page(&pDataBufferNode->data.backend.decodedPaged.data, pPage, &pResourceManager->config.allocationCallbacks); result = MA_AT_END; } } else { /* No frames were read. Free the page and just set the status to MA_AT_END. */ ma_paged_audio_buffer_data_free_page(&pDataBufferNode->data.backend.decodedPaged.data, pPage, &pResourceManager->config.allocationCallbacks); result = MA_AT_END; } } break; case ma_resource_manager_data_supply_type_encoded: case ma_resource_manager_data_supply_type_unknown: default: { /* Unexpected data supply type. */ ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_ERROR, "Unexpected data supply type (%d) when decoding page.", ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBufferNode)); return MA_ERROR; }; } if (result == MA_SUCCESS && framesRead == 0) { result = MA_AT_END; } return result; } static ma_result ma_resource_manager_data_buffer_node_acquire_critical_section(ma_resource_manager* pResourceManager, const char* pFilePath, const wchar_t* pFilePathW, ma_uint32 hashedName32, ma_uint32 flags, const ma_resource_manager_data_supply* pExistingData, ma_fence* pInitFence, ma_fence* pDoneFence, ma_resource_manager_inline_notification* pInitNotification, ma_resource_manager_data_buffer_node** ppDataBufferNode) { ma_result result = MA_SUCCESS; ma_resource_manager_data_buffer_node* pDataBufferNode = NULL; ma_resource_manager_data_buffer_node* pInsertPoint; if (ppDataBufferNode != NULL) { *ppDataBufferNode = NULL; } result = ma_resource_manager_data_buffer_node_insert_point(pResourceManager, hashedName32, &pInsertPoint); if (result == MA_ALREADY_EXISTS) { /* The node already exists. We just need to increment the reference count. */ pDataBufferNode = pInsertPoint; result = ma_resource_manager_data_buffer_node_increment_ref(pResourceManager, pDataBufferNode, NULL); if (result != MA_SUCCESS) { return result; /* Should never happen. Failed to increment the reference count. */ } result = MA_ALREADY_EXISTS; goto done; } else { /* The node does not already exist. We need to post a LOAD_DATA_BUFFER_NODE job here. This needs to be done inside the critical section to ensure an uninitialization of the node does not occur before initialization on another thread. */ pDataBufferNode = (ma_resource_manager_data_buffer_node*)ma_malloc(sizeof(*pDataBufferNode), &pResourceManager->config.allocationCallbacks); if (pDataBufferNode == NULL) { return MA_OUT_OF_MEMORY; } MA_ZERO_OBJECT(pDataBufferNode); pDataBufferNode->hashedName32 = hashedName32; pDataBufferNode->refCount = 1; /* Always set to 1 by default (this is our first reference). */ if (pExistingData == NULL) { pDataBufferNode->data.type = ma_resource_manager_data_supply_type_unknown; /* <-- We won't know this until we start decoding. */ pDataBufferNode->result = MA_BUSY; /* Must be set to MA_BUSY before we leave the critical section, so might as well do it now. */ pDataBufferNode->isDataOwnedByResourceManager = MA_TRUE; } else { pDataBufferNode->data = *pExistingData; pDataBufferNode->result = MA_SUCCESS; /* Not loading asynchronously, so just set the status */ pDataBufferNode->isDataOwnedByResourceManager = MA_FALSE; } result = ma_resource_manager_data_buffer_node_insert_at(pResourceManager, pDataBufferNode, pInsertPoint); if (result != MA_SUCCESS) { ma_free(pDataBufferNode, &pResourceManager->config.allocationCallbacks); return result; /* Should never happen. Failed to insert the data buffer into the BST. */ } /* Here is where we'll post the job, but only if we're loading asynchronously. If we're loading synchronously we'll defer loading to a later stage, outside of the critical section. */ if (pDataBufferNode->isDataOwnedByResourceManager && (flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC) != 0) { /* Loading asynchronously. Post the job. */ ma_job job; char* pFilePathCopy = NULL; wchar_t* pFilePathWCopy = NULL; /* We need a copy of the file path. We should probably make this more efficient, but for now we'll do a transient memory allocation. */ if (pFilePath != NULL) { pFilePathCopy = ma_copy_string(pFilePath, &pResourceManager->config.allocationCallbacks); } else { pFilePathWCopy = ma_copy_string_w(pFilePathW, &pResourceManager->config.allocationCallbacks); } if (pFilePathCopy == NULL && pFilePathWCopy == NULL) { ma_resource_manager_data_buffer_node_remove(pResourceManager, pDataBufferNode); ma_free(pDataBufferNode, &pResourceManager->config.allocationCallbacks); return MA_OUT_OF_MEMORY; } if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) { ma_resource_manager_inline_notification_init(pResourceManager, pInitNotification); } /* Acquire init and done fences before posting the job. These will be unacquired by the job thread. */ if (pInitFence != NULL) { ma_fence_acquire(pInitFence); } if (pDoneFence != NULL) { ma_fence_acquire(pDoneFence); } /* We now have everything we need to post the job to the job thread. */ job = ma_job_init(MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_BUFFER_NODE); job.order = ma_resource_manager_data_buffer_node_next_execution_order(pDataBufferNode); job.data.resourceManager.loadDataBufferNode.pResourceManager = pResourceManager; job.data.resourceManager.loadDataBufferNode.pDataBufferNode = pDataBufferNode; job.data.resourceManager.loadDataBufferNode.pFilePath = pFilePathCopy; job.data.resourceManager.loadDataBufferNode.pFilePathW = pFilePathWCopy; job.data.resourceManager.loadDataBufferNode.flags = flags; job.data.resourceManager.loadDataBufferNode.pInitNotification = ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) ? pInitNotification : NULL; job.data.resourceManager.loadDataBufferNode.pDoneNotification = NULL; job.data.resourceManager.loadDataBufferNode.pInitFence = pInitFence; job.data.resourceManager.loadDataBufferNode.pDoneFence = pDoneFence; result = ma_resource_manager_post_job(pResourceManager, &job); if (result != MA_SUCCESS) { /* Failed to post job. Probably ran out of memory. */ ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_ERROR, "Failed to post MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_BUFFER_NODE job. %s.\n", ma_result_description(result)); /* Fences were acquired before posting the job, but since the job was not able to be posted, we need to make sure we release them so nothing gets stuck waiting. */ if (pInitFence != NULL) { ma_fence_release(pInitFence); } if (pDoneFence != NULL) { ma_fence_release(pDoneFence); } if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) { ma_resource_manager_inline_notification_init(pResourceManager, pInitNotification); } ma_free(pFilePathCopy, &pResourceManager->config.allocationCallbacks); ma_free(pFilePathWCopy, &pResourceManager->config.allocationCallbacks); ma_resource_manager_data_buffer_node_remove(pResourceManager, pDataBufferNode); ma_free(pDataBufferNode, &pResourceManager->config.allocationCallbacks); return result; } } } done: if (ppDataBufferNode != NULL) { *ppDataBufferNode = pDataBufferNode; } return result; } static ma_result ma_resource_manager_data_buffer_node_acquire(ma_resource_manager* pResourceManager, const char* pFilePath, const wchar_t* pFilePathW, ma_uint32 hashedName32, ma_uint32 flags, const ma_resource_manager_data_supply* pExistingData, ma_fence* pInitFence, ma_fence* pDoneFence, ma_resource_manager_data_buffer_node** ppDataBufferNode) { ma_result result = MA_SUCCESS; ma_bool32 nodeAlreadyExists = MA_FALSE; ma_resource_manager_data_buffer_node* pDataBufferNode = NULL; ma_resource_manager_inline_notification initNotification; /* Used when the WAIT_INIT flag is set. */ if (ppDataBufferNode != NULL) { *ppDataBufferNode = NULL; /* Safety. */ } if (pResourceManager == NULL || (pFilePath == NULL && pFilePathW == NULL && hashedName32 == 0)) { return MA_INVALID_ARGS; } /* If we're specifying existing data, it must be valid. */ if (pExistingData != NULL && pExistingData->type == ma_resource_manager_data_supply_type_unknown) { return MA_INVALID_ARGS; } /* If we don't support threading, remove the ASYNC flag to make the rest of this a bit simpler. */ if (ma_resource_manager_is_threading_enabled(pResourceManager) == MA_FALSE) { flags &= ~MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC; } if (hashedName32 == 0) { if (pFilePath != NULL) { hashedName32 = ma_hash_string_32(pFilePath); } else { hashedName32 = ma_hash_string_w_32(pFilePathW); } } /* Here is where we either increment the node's reference count or allocate a new one and add it to the BST. When allocating a new node, we need to make sure the LOAD_DATA_BUFFER_NODE job is posted inside the critical section just in case the caller immediately uninitializes the node as this will ensure the FREE_DATA_BUFFER_NODE job is given an execution order such that the node is not uninitialized before initialization. */ ma_resource_manager_data_buffer_bst_lock(pResourceManager); { result = ma_resource_manager_data_buffer_node_acquire_critical_section(pResourceManager, pFilePath, pFilePathW, hashedName32, flags, pExistingData, pInitFence, pDoneFence, &initNotification, &pDataBufferNode); } ma_resource_manager_data_buffer_bst_unlock(pResourceManager); if (result == MA_ALREADY_EXISTS) { nodeAlreadyExists = MA_TRUE; result = MA_SUCCESS; } else { if (result != MA_SUCCESS) { return result; } } /* If we're loading synchronously, we'll need to load everything now. When loading asynchronously, a job will have been posted inside the BST critical section so that an uninitialization can be allocated an appropriate execution order thereby preventing it from being uninitialized before the node is initialized by the decoding thread(s). */ if (nodeAlreadyExists == MA_FALSE) { /* Don't need to try loading anything if the node already exists. */ if (pFilePath == NULL && pFilePathW == NULL) { /* If this path is hit, it means a buffer is being copied (i.e. initialized from only the hashed name), but that node has been freed in the meantime, probably from some other thread. This is an invalid operation. */ ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_WARNING, "Cloning data buffer node failed because the source node was released. The source node must remain valid until the cloning has completed.\n"); result = MA_INVALID_OPERATION; goto done; } if (pDataBufferNode->isDataOwnedByResourceManager) { if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC) == 0) { /* Loading synchronously. Load the sound in it's entirety here. */ if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_DECODE) == 0) { /* No decoding. This is the simple case - just store the file contents in memory. */ result = ma_resource_manager_data_buffer_node_init_supply_encoded(pResourceManager, pDataBufferNode, pFilePath, pFilePathW); if (result != MA_SUCCESS) { goto done; } } else { /* Decoding. We do this the same way as we do when loading asynchronously. */ ma_decoder* pDecoder; result = ma_resource_manager_data_buffer_node_init_supply_decoded(pResourceManager, pDataBufferNode, pFilePath, pFilePathW, flags, &pDecoder); if (result != MA_SUCCESS) { goto done; } /* We have the decoder, now decode page by page just like we do when loading asynchronously. */ for (;;) { /* Decode next page. */ result = ma_resource_manager_data_buffer_node_decode_next_page(pResourceManager, pDataBufferNode, pDecoder); if (result != MA_SUCCESS) { break; /* Will return MA_AT_END when the last page has been decoded. */ } } /* Reaching the end needs to be considered successful. */ if (result == MA_AT_END) { result = MA_SUCCESS; } /* At this point the data buffer is either fully decoded or some error occurred. Either way, the decoder is no longer necessary. */ ma_decoder_uninit(pDecoder); ma_free(pDecoder, &pResourceManager->config.allocationCallbacks); } /* Getting here means we were successful. Make sure the status of the node is updated accordingly. */ c89atomic_exchange_i32(&pDataBufferNode->result, result); } else { /* Loading asynchronously. We may need to wait for initialization. */ if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) { ma_resource_manager_inline_notification_wait(&initNotification); } } } else { /* The data is not managed by the resource manager so there's nothing else to do. */ MA_ASSERT(pExistingData != NULL); } } done: /* If we failed to initialize the data buffer we need to free it. */ if (result != MA_SUCCESS) { if (nodeAlreadyExists == MA_FALSE) { ma_resource_manager_data_buffer_node_remove(pResourceManager, pDataBufferNode); ma_free(pDataBufferNode, &pResourceManager->config.allocationCallbacks); } } /* The init notification needs to be uninitialized. This will be used if the node does not already exist, and we've specified ASYNC | WAIT_INIT. */ if (nodeAlreadyExists == MA_FALSE && pDataBufferNode->isDataOwnedByResourceManager && (flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC) != 0) { if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) { ma_resource_manager_inline_notification_uninit(&initNotification); } } if (ppDataBufferNode != NULL) { *ppDataBufferNode = pDataBufferNode; } return result; } static ma_result ma_resource_manager_data_buffer_node_unacquire(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer_node* pDataBufferNode, const char* pName, const wchar_t* pNameW) { ma_result result = MA_SUCCESS; ma_uint32 refCount = 0xFFFFFFFF; /* The new reference count of the node after decrementing. Initialize to non-0 to be safe we don't fall into the freeing path. */ ma_uint32 hashedName32 = 0; if (pResourceManager == NULL) { return MA_INVALID_ARGS; } if (pDataBufferNode == NULL) { if (pName == NULL && pNameW == NULL) { return MA_INVALID_ARGS; } if (pName != NULL) { hashedName32 = ma_hash_string_32(pName); } else { hashedName32 = ma_hash_string_w_32(pNameW); } } /* The first thing to do is decrement the reference counter of the node. Then, if the reference count is zero, we need to free the node. If the node is still in the process of loading, we'll need to post a job to the job queue to free the node. Otherwise we'll just do it here. */ ma_resource_manager_data_buffer_bst_lock(pResourceManager); { /* Might need to find the node. Must be done inside the critical section. */ if (pDataBufferNode == NULL) { result = ma_resource_manager_data_buffer_node_search(pResourceManager, hashedName32, &pDataBufferNode); if (result != MA_SUCCESS) { goto stage2; /* Couldn't find the node. */ } } result = ma_resource_manager_data_buffer_node_decrement_ref(pResourceManager, pDataBufferNode, &refCount); if (result != MA_SUCCESS) { goto stage2; /* Should never happen. */ } if (refCount == 0) { result = ma_resource_manager_data_buffer_node_remove(pResourceManager, pDataBufferNode); if (result != MA_SUCCESS) { goto stage2; /* An error occurred when trying to remove the data buffer. This should never happen. */ } } } ma_resource_manager_data_buffer_bst_unlock(pResourceManager); stage2: if (result != MA_SUCCESS) { return result; } /* Here is where we need to free the node. We don't want to do this inside the critical section above because we want to keep that as small as possible for multi-threaded efficiency. */ if (refCount == 0) { if (ma_resource_manager_data_buffer_node_result(pDataBufferNode) == MA_BUSY) { /* The sound is still loading. We need to delay the freeing of the node to a safe time. */ ma_job job; /* We need to mark the node as unavailable for the sake of the resource manager worker threads. */ c89atomic_exchange_i32(&pDataBufferNode->result, MA_UNAVAILABLE); job = ma_job_init(MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_BUFFER_NODE); job.order = ma_resource_manager_data_buffer_node_next_execution_order(pDataBufferNode); job.data.resourceManager.freeDataBufferNode.pResourceManager = pResourceManager; job.data.resourceManager.freeDataBufferNode.pDataBufferNode = pDataBufferNode; result = ma_resource_manager_post_job(pResourceManager, &job); if (result != MA_SUCCESS) { ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_ERROR, "Failed to post MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_BUFFER_NODE job. %s.\n", ma_result_description(result)); return result; } /* If we don't support threading, process the job queue here. */ if (ma_resource_manager_is_threading_enabled(pResourceManager) == MA_FALSE) { while (ma_resource_manager_data_buffer_node_result(pDataBufferNode) == MA_BUSY) { result = ma_resource_manager_process_next_job(pResourceManager); if (result == MA_NO_DATA_AVAILABLE || result == MA_CANCELLED) { result = MA_SUCCESS; break; } } } else { /* Threading is enabled. The job queue will deal with the rest of the cleanup from here. */ } } else { /* The sound isn't loading so we can just free the node here. */ ma_resource_manager_data_buffer_node_free(pResourceManager, pDataBufferNode); } } return result; } static ma_uint32 ma_resource_manager_data_buffer_next_execution_order(ma_resource_manager_data_buffer* pDataBuffer) { MA_ASSERT(pDataBuffer != NULL); return c89atomic_fetch_add_32(&pDataBuffer->executionCounter, 1); } static ma_result ma_resource_manager_data_buffer_cb__read_pcm_frames(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) { return ma_resource_manager_data_buffer_read_pcm_frames((ma_resource_manager_data_buffer*)pDataSource, pFramesOut, frameCount, pFramesRead); } static ma_result ma_resource_manager_data_buffer_cb__seek_to_pcm_frame(ma_data_source* pDataSource, ma_uint64 frameIndex) { return ma_resource_manager_data_buffer_seek_to_pcm_frame((ma_resource_manager_data_buffer*)pDataSource, frameIndex); } static ma_result ma_resource_manager_data_buffer_cb__get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) { return ma_resource_manager_data_buffer_get_data_format((ma_resource_manager_data_buffer*)pDataSource, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap); } static ma_result ma_resource_manager_data_buffer_cb__get_cursor_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pCursor) { return ma_resource_manager_data_buffer_get_cursor_in_pcm_frames((ma_resource_manager_data_buffer*)pDataSource, pCursor); } static ma_result ma_resource_manager_data_buffer_cb__get_length_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pLength) { return ma_resource_manager_data_buffer_get_length_in_pcm_frames((ma_resource_manager_data_buffer*)pDataSource, pLength); } static ma_result ma_resource_manager_data_buffer_cb__set_looping(ma_data_source* pDataSource, ma_bool32 isLooping) { ma_resource_manager_data_buffer* pDataBuffer = (ma_resource_manager_data_buffer*)pDataSource; MA_ASSERT(pDataBuffer != NULL); c89atomic_exchange_32(&pDataBuffer->isLooping, isLooping); /* The looping state needs to be set on the connector as well or else looping won't work when we read audio data. */ ma_data_source_set_looping(ma_resource_manager_data_buffer_get_connector(pDataBuffer), isLooping); return MA_SUCCESS; } static ma_data_source_vtable g_ma_resource_manager_data_buffer_vtable = { ma_resource_manager_data_buffer_cb__read_pcm_frames, ma_resource_manager_data_buffer_cb__seek_to_pcm_frame, ma_resource_manager_data_buffer_cb__get_data_format, ma_resource_manager_data_buffer_cb__get_cursor_in_pcm_frames, ma_resource_manager_data_buffer_cb__get_length_in_pcm_frames, ma_resource_manager_data_buffer_cb__set_looping, 0 }; static ma_result ma_resource_manager_data_buffer_init_ex_internal(ma_resource_manager* pResourceManager, const ma_resource_manager_data_source_config* pConfig, ma_uint32 hashedName32, ma_resource_manager_data_buffer* pDataBuffer) { ma_result result = MA_SUCCESS; ma_resource_manager_data_buffer_node* pDataBufferNode; ma_data_source_config dataSourceConfig; ma_bool32 async; ma_uint32 flags; ma_resource_manager_pipeline_notifications notifications; if (pDataBuffer == NULL) { if (pConfig != NULL && pConfig->pNotifications != NULL) { ma_resource_manager_pipeline_notifications_signal_all_notifications(pConfig->pNotifications); } return MA_INVALID_ARGS; } MA_ZERO_OBJECT(pDataBuffer); if (pConfig == NULL) { return MA_INVALID_ARGS; } if (pConfig->pNotifications != NULL) { notifications = *pConfig->pNotifications; /* From here on out we should be referencing `notifications` instead of `pNotifications`. Set this to NULL to catch errors at testing time. */ } else { MA_ZERO_OBJECT(¬ifications); } /* For safety, always remove the ASYNC flag if threading is disabled on the resource manager. */ flags = pConfig->flags; if (ma_resource_manager_is_threading_enabled(pResourceManager) == MA_FALSE) { flags &= ~MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC; } async = (flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC) != 0; /* Fences need to be acquired before doing anything. These must be aquired and released outside of the node to ensure there's no holes where ma_fence_wait() could prematurely return before the data buffer has completed initialization. When loading asynchronously, the node acquisition routine below will acquire the fences on this thread and then release them on the async thread when the operation is complete. These fences are always released at the "done" tag at the end of this function. They'll be acquired a second if loading asynchronously. This double acquisition system is just done to simplify code maintanence. */ ma_resource_manager_pipeline_notifications_acquire_all_fences(¬ifications); { /* We first need to acquire a node. If ASYNC is not set, this will not return until the entire sound has been loaded. */ result = ma_resource_manager_data_buffer_node_acquire(pResourceManager, pConfig->pFilePath, pConfig->pFilePathW, hashedName32, flags, NULL, notifications.init.pFence, notifications.done.pFence, &pDataBufferNode); if (result != MA_SUCCESS) { ma_resource_manager_pipeline_notifications_signal_all_notifications(¬ifications); goto done; } dataSourceConfig = ma_data_source_config_init(); dataSourceConfig.vtable = &g_ma_resource_manager_data_buffer_vtable; result = ma_data_source_init(&dataSourceConfig, &pDataBuffer->ds); if (result != MA_SUCCESS) { ma_resource_manager_data_buffer_node_unacquire(pResourceManager, pDataBufferNode, NULL, NULL); ma_resource_manager_pipeline_notifications_signal_all_notifications(¬ifications); goto done; } pDataBuffer->pResourceManager = pResourceManager; pDataBuffer->pNode = pDataBufferNode; pDataBuffer->flags = flags; pDataBuffer->result = MA_BUSY; /* Always default to MA_BUSY for safety. It'll be overwritten when loading completes or an error occurs. */ /* If we're loading asynchronously we need to post a job to the job queue to initialize the connector. */ if (async == MA_FALSE || ma_resource_manager_data_buffer_node_result(pDataBufferNode) == MA_SUCCESS) { /* Loading synchronously or the data has already been fully loaded. We can just initialize the connector from here without a job. */ result = ma_resource_manager_data_buffer_init_connector(pDataBuffer, pConfig, NULL, NULL); c89atomic_exchange_i32(&pDataBuffer->result, result); ma_resource_manager_pipeline_notifications_signal_all_notifications(¬ifications); goto done; } else { /* The node's data supply isn't initialized yet. The caller has requested that we load asynchronously so we need to post a job to do this. */ ma_job job; ma_resource_manager_inline_notification initNotification; /* Used when the WAIT_INIT flag is set. */ if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) { ma_resource_manager_inline_notification_init(pResourceManager, &initNotification); } /* The status of the data buffer needs to be set to MA_BUSY before posting the job so that the worker thread is aware of it's busy state. If the LOAD_DATA_BUFFER job sees a status other than MA_BUSY, it'll assume an error and fall through to an early exit. */ c89atomic_exchange_i32(&pDataBuffer->result, MA_BUSY); /* Acquire fences a second time. These will be released by the async thread. */ ma_resource_manager_pipeline_notifications_acquire_all_fences(¬ifications); job = ma_job_init(MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_BUFFER); job.order = ma_resource_manager_data_buffer_next_execution_order(pDataBuffer); job.data.resourceManager.loadDataBuffer.pDataBuffer = pDataBuffer; job.data.resourceManager.loadDataBuffer.pInitNotification = ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) ? &initNotification : notifications.init.pNotification; job.data.resourceManager.loadDataBuffer.pDoneNotification = notifications.done.pNotification; job.data.resourceManager.loadDataBuffer.pInitFence = notifications.init.pFence; job.data.resourceManager.loadDataBuffer.pDoneFence = notifications.done.pFence; job.data.resourceManager.loadDataBuffer.rangeBegInPCMFrames = pConfig->rangeBegInPCMFrames; job.data.resourceManager.loadDataBuffer.rangeEndInPCMFrames = pConfig->rangeEndInPCMFrames; job.data.resourceManager.loadDataBuffer.loopPointBegInPCMFrames = pConfig->loopPointBegInPCMFrames; job.data.resourceManager.loadDataBuffer.loopPointEndInPCMFrames = pConfig->loopPointEndInPCMFrames; job.data.resourceManager.loadDataBuffer.isLooping = pConfig->isLooping; result = ma_resource_manager_post_job(pResourceManager, &job); if (result != MA_SUCCESS) { /* We failed to post the job. Most likely there isn't enough room in the queue's buffer. */ ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_ERROR, "Failed to post MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_BUFFER job. %s.\n", ma_result_description(result)); c89atomic_exchange_i32(&pDataBuffer->result, result); /* Release the fences after the result has been set on the data buffer. */ ma_resource_manager_pipeline_notifications_release_all_fences(¬ifications); } else { if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) { ma_resource_manager_inline_notification_wait(&initNotification); if (notifications.init.pNotification != NULL) { ma_async_notification_signal(notifications.init.pNotification); } /* NOTE: Do not release the init fence here. It will have been done by the job. */ /* Make sure we return an error if initialization failed on the async thread. */ result = ma_resource_manager_data_buffer_result(pDataBuffer); if (result == MA_BUSY) { result = MA_SUCCESS; } } } if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) { ma_resource_manager_inline_notification_uninit(&initNotification); } } if (result != MA_SUCCESS) { ma_resource_manager_data_buffer_node_unacquire(pResourceManager, pDataBufferNode, NULL, NULL); goto done; } } done: if (result == MA_SUCCESS) { if (pConfig->initialSeekPointInPCMFrames > 0) { ma_resource_manager_data_buffer_seek_to_pcm_frame(pDataBuffer, pConfig->initialSeekPointInPCMFrames); } } ma_resource_manager_pipeline_notifications_release_all_fences(¬ifications); return result; } MA_API ma_result ma_resource_manager_data_buffer_init_ex(ma_resource_manager* pResourceManager, const ma_resource_manager_data_source_config* pConfig, ma_resource_manager_data_buffer* pDataBuffer) { return ma_resource_manager_data_buffer_init_ex_internal(pResourceManager, pConfig, 0, pDataBuffer); } MA_API ma_result ma_resource_manager_data_buffer_init(ma_resource_manager* pResourceManager, const char* pFilePath, ma_uint32 flags, const ma_resource_manager_pipeline_notifications* pNotifications, ma_resource_manager_data_buffer* pDataBuffer) { ma_resource_manager_data_source_config config; config = ma_resource_manager_data_source_config_init(); config.pFilePath = pFilePath; config.flags = flags; config.pNotifications = pNotifications; return ma_resource_manager_data_buffer_init_ex(pResourceManager, &config, pDataBuffer); } MA_API ma_result ma_resource_manager_data_buffer_init_w(ma_resource_manager* pResourceManager, const wchar_t* pFilePath, ma_uint32 flags, const ma_resource_manager_pipeline_notifications* pNotifications, ma_resource_manager_data_buffer* pDataBuffer) { ma_resource_manager_data_source_config config; config = ma_resource_manager_data_source_config_init(); config.pFilePathW = pFilePath; config.flags = flags; config.pNotifications = pNotifications; return ma_resource_manager_data_buffer_init_ex(pResourceManager, &config, pDataBuffer); } MA_API ma_result ma_resource_manager_data_buffer_init_copy(ma_resource_manager* pResourceManager, const ma_resource_manager_data_buffer* pExistingDataBuffer, ma_resource_manager_data_buffer* pDataBuffer) { ma_resource_manager_data_source_config config; if (pExistingDataBuffer == NULL) { return MA_INVALID_ARGS; } MA_ASSERT(pExistingDataBuffer->pNode != NULL); /* <-- If you've triggered this, you've passed in an invalid existing data buffer. */ config = ma_resource_manager_data_source_config_init(); config.flags = pExistingDataBuffer->flags; return ma_resource_manager_data_buffer_init_ex_internal(pResourceManager, &config, pExistingDataBuffer->pNode->hashedName32, pDataBuffer); } static ma_result ma_resource_manager_data_buffer_uninit_internal(ma_resource_manager_data_buffer* pDataBuffer) { MA_ASSERT(pDataBuffer != NULL); /* The connector should be uninitialized first. */ ma_resource_manager_data_buffer_uninit_connector(pDataBuffer->pResourceManager, pDataBuffer); /* With the connector uninitialized we can unacquire the node. */ ma_resource_manager_data_buffer_node_unacquire(pDataBuffer->pResourceManager, pDataBuffer->pNode, NULL, NULL); /* The base data source needs to be uninitialized as well. */ ma_data_source_uninit(&pDataBuffer->ds); return MA_SUCCESS; } MA_API ma_result ma_resource_manager_data_buffer_uninit(ma_resource_manager_data_buffer* pDataBuffer) { ma_result result; if (pDataBuffer == NULL) { return MA_INVALID_ARGS; } if (ma_resource_manager_data_buffer_result(pDataBuffer) == MA_SUCCESS) { /* The data buffer can be deleted synchronously. */ return ma_resource_manager_data_buffer_uninit_internal(pDataBuffer); } else { /* The data buffer needs to be deleted asynchronously because it's still loading. With the status set to MA_UNAVAILABLE, no more pages will be loaded and the uninitialization should happen fairly quickly. Since the caller owns the data buffer, we need to wait for this event to get processed before returning. */ ma_resource_manager_inline_notification notification; ma_job job; /* We need to mark the node as unavailable so we don't try reading from it anymore, but also to let the loading thread know that it needs to abort it's loading procedure. */ c89atomic_exchange_i32(&pDataBuffer->result, MA_UNAVAILABLE); result = ma_resource_manager_inline_notification_init(pDataBuffer->pResourceManager, ¬ification); if (result != MA_SUCCESS) { return result; /* Failed to create the notification. This should rarely, if ever, happen. */ } job = ma_job_init(MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_BUFFER); job.order = ma_resource_manager_data_buffer_next_execution_order(pDataBuffer); job.data.resourceManager.freeDataBuffer.pDataBuffer = pDataBuffer; job.data.resourceManager.freeDataBuffer.pDoneNotification = ¬ification; job.data.resourceManager.freeDataBuffer.pDoneFence = NULL; result = ma_resource_manager_post_job(pDataBuffer->pResourceManager, &job); if (result != MA_SUCCESS) { ma_resource_manager_inline_notification_uninit(¬ification); return result; } ma_resource_manager_inline_notification_wait_and_uninit(¬ification); } return result; } MA_API ma_result ma_resource_manager_data_buffer_read_pcm_frames(ma_resource_manager_data_buffer* pDataBuffer, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) { ma_result result = MA_SUCCESS; ma_uint64 framesRead = 0; ma_bool32 isDecodedBufferBusy = MA_FALSE; /* Safety. */ if (pFramesRead != NULL) { *pFramesRead = 0; } if (frameCount == 0) { return MA_INVALID_ARGS; } /* We cannot be using the data buffer after it's been uninitialized. If you trigger this assert it means you're trying to read from the data buffer after it's been uninitialized or is in the process of uninitializing. */ MA_ASSERT(ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode) != MA_UNAVAILABLE); /* If the node is not initialized we need to abort with a busy code. */ if (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode) == ma_resource_manager_data_supply_type_unknown) { return MA_BUSY; /* Still loading. */ } if (pDataBuffer->seekToCursorOnNextRead) { pDataBuffer->seekToCursorOnNextRead = MA_FALSE; result = ma_data_source_seek_to_pcm_frame(ma_resource_manager_data_buffer_get_connector(pDataBuffer), pDataBuffer->seekTargetInPCMFrames); if (result != MA_SUCCESS) { return result; } } /* For decoded buffers (not paged) we need to check beforehand how many frames we have available. We cannot exceed this amount. We'll read as much as we can, and then return MA_BUSY. */ if (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode) == ma_resource_manager_data_supply_type_decoded) { ma_uint64 availableFrames; isDecodedBufferBusy = (ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode) == MA_BUSY); if (ma_resource_manager_data_buffer_get_available_frames(pDataBuffer, &availableFrames) == MA_SUCCESS) { /* Don't try reading more than the available frame count. */ if (frameCount > availableFrames) { frameCount = availableFrames; /* If there's no frames available we want to set the status to MA_AT_END. The logic below will check if the node is busy, and if so, change it to MA_BUSY. The reason we do this is because we don't want to call `ma_data_source_read_pcm_frames()` if the frame count is 0 because that'll result in a situation where it's possible MA_AT_END won't get returned. */ if (frameCount == 0) { result = MA_AT_END; } } else { isDecodedBufferBusy = MA_FALSE; /* We have enough frames available in the buffer to avoid a MA_BUSY status. */ } } } /* Don't attempt to read anything if we've got no frames available. */ if (frameCount > 0) { result = ma_data_source_read_pcm_frames(ma_resource_manager_data_buffer_get_connector(pDataBuffer), pFramesOut, frameCount, &framesRead); } /* If we returned MA_AT_END, but the node is still loading, we don't want to return that code or else the caller will interpret the sound as at the end and terminate decoding. */ if (result == MA_AT_END) { if (ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode) == MA_BUSY) { result = MA_BUSY; } } if (isDecodedBufferBusy) { result = MA_BUSY; } if (pFramesRead != NULL) { *pFramesRead = framesRead; } if (result == MA_SUCCESS && framesRead == 0) { result = MA_AT_END; } return result; } MA_API ma_result ma_resource_manager_data_buffer_seek_to_pcm_frame(ma_resource_manager_data_buffer* pDataBuffer, ma_uint64 frameIndex) { ma_result result; /* We cannot be using the data source after it's been uninitialized. */ MA_ASSERT(ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode) != MA_UNAVAILABLE); /* If we haven't yet got a connector we need to abort. */ if (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode) == ma_resource_manager_data_supply_type_unknown) { pDataBuffer->seekTargetInPCMFrames = frameIndex; pDataBuffer->seekToCursorOnNextRead = MA_TRUE; return MA_BUSY; /* Still loading. */ } result = ma_data_source_seek_to_pcm_frame(ma_resource_manager_data_buffer_get_connector(pDataBuffer), frameIndex); if (result != MA_SUCCESS) { return result; } pDataBuffer->seekTargetInPCMFrames = ~(ma_uint64)0; /* <-- For identification purposes. */ pDataBuffer->seekToCursorOnNextRead = MA_FALSE; return MA_SUCCESS; } MA_API ma_result ma_resource_manager_data_buffer_get_data_format(ma_resource_manager_data_buffer* pDataBuffer, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) { /* We cannot be using the data source after it's been uninitialized. */ MA_ASSERT(ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode) != MA_UNAVAILABLE); switch (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode)) { case ma_resource_manager_data_supply_type_encoded: { return ma_data_source_get_data_format(&pDataBuffer->connector.decoder, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap); }; case ma_resource_manager_data_supply_type_decoded: { *pFormat = pDataBuffer->pNode->data.backend.decoded.format; *pChannels = pDataBuffer->pNode->data.backend.decoded.channels; *pSampleRate = pDataBuffer->pNode->data.backend.decoded.sampleRate; ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, pDataBuffer->pNode->data.backend.decoded.channels); return MA_SUCCESS; }; case ma_resource_manager_data_supply_type_decoded_paged: { *pFormat = pDataBuffer->pNode->data.backend.decodedPaged.data.format; *pChannels = pDataBuffer->pNode->data.backend.decodedPaged.data.channels; *pSampleRate = pDataBuffer->pNode->data.backend.decodedPaged.sampleRate; ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, pDataBuffer->pNode->data.backend.decoded.channels); return MA_SUCCESS; }; case ma_resource_manager_data_supply_type_unknown: { return MA_BUSY; /* Still loading. */ }; default: { /* Unknown supply type. Should never hit this. */ return MA_INVALID_ARGS; } } } MA_API ma_result ma_resource_manager_data_buffer_get_cursor_in_pcm_frames(ma_resource_manager_data_buffer* pDataBuffer, ma_uint64* pCursor) { /* We cannot be using the data source after it's been uninitialized. */ MA_ASSERT(ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode) != MA_UNAVAILABLE); if (pDataBuffer == NULL || pCursor == NULL) { return MA_INVALID_ARGS; } *pCursor = 0; switch (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode)) { case ma_resource_manager_data_supply_type_encoded: { return ma_decoder_get_cursor_in_pcm_frames(&pDataBuffer->connector.decoder, pCursor); }; case ma_resource_manager_data_supply_type_decoded: { return ma_audio_buffer_get_cursor_in_pcm_frames(&pDataBuffer->connector.buffer, pCursor); }; case ma_resource_manager_data_supply_type_decoded_paged: { return ma_paged_audio_buffer_get_cursor_in_pcm_frames(&pDataBuffer->connector.pagedBuffer, pCursor); }; case ma_resource_manager_data_supply_type_unknown: { return MA_BUSY; }; default: { return MA_INVALID_ARGS; } } } MA_API ma_result ma_resource_manager_data_buffer_get_length_in_pcm_frames(ma_resource_manager_data_buffer* pDataBuffer, ma_uint64* pLength) { /* We cannot be using the data source after it's been uninitialized. */ MA_ASSERT(ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode) != MA_UNAVAILABLE); if (pDataBuffer == NULL || pLength == NULL) { return MA_INVALID_ARGS; } if (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode) == ma_resource_manager_data_supply_type_unknown) { return MA_BUSY; /* Still loading. */ } return ma_data_source_get_length_in_pcm_frames(ma_resource_manager_data_buffer_get_connector(pDataBuffer), pLength); } MA_API ma_result ma_resource_manager_data_buffer_result(const ma_resource_manager_data_buffer* pDataBuffer) { if (pDataBuffer == NULL) { return MA_INVALID_ARGS; } return (ma_result)c89atomic_load_i32((ma_result*)&pDataBuffer->result); /* Need a naughty const-cast here. */ } MA_API ma_result ma_resource_manager_data_buffer_set_looping(ma_resource_manager_data_buffer* pDataBuffer, ma_bool32 isLooping) { return ma_data_source_set_looping(pDataBuffer, isLooping); } MA_API ma_bool32 ma_resource_manager_data_buffer_is_looping(const ma_resource_manager_data_buffer* pDataBuffer) { return ma_data_source_is_looping(pDataBuffer); } MA_API ma_result ma_resource_manager_data_buffer_get_available_frames(ma_resource_manager_data_buffer* pDataBuffer, ma_uint64* pAvailableFrames) { if (pAvailableFrames == NULL) { return MA_INVALID_ARGS; } *pAvailableFrames = 0; if (pDataBuffer == NULL) { return MA_INVALID_ARGS; } if (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode) == ma_resource_manager_data_supply_type_unknown) { if (ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode) == MA_BUSY) { return MA_BUSY; } else { return MA_INVALID_OPERATION; /* No connector. */ } } switch (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode)) { case ma_resource_manager_data_supply_type_encoded: { return ma_decoder_get_available_frames(&pDataBuffer->connector.decoder, pAvailableFrames); }; case ma_resource_manager_data_supply_type_decoded: { return ma_audio_buffer_get_available_frames(&pDataBuffer->connector.buffer, pAvailableFrames); }; case ma_resource_manager_data_supply_type_decoded_paged: { ma_uint64 cursor; ma_paged_audio_buffer_get_cursor_in_pcm_frames(&pDataBuffer->connector.pagedBuffer, &cursor); if (pDataBuffer->pNode->data.backend.decodedPaged.decodedFrameCount > cursor) { *pAvailableFrames = pDataBuffer->pNode->data.backend.decodedPaged.decodedFrameCount - cursor; } else { *pAvailableFrames = 0; } return MA_SUCCESS; }; case ma_resource_manager_data_supply_type_unknown: default: { /* Unknown supply type. Should never hit this. */ return MA_INVALID_ARGS; } } } MA_API ma_result ma_resource_manager_register_file(ma_resource_manager* pResourceManager, const char* pFilePath, ma_uint32 flags) { return ma_resource_manager_data_buffer_node_acquire(pResourceManager, pFilePath, NULL, 0, flags, NULL, NULL, NULL, NULL); } MA_API ma_result ma_resource_manager_register_file_w(ma_resource_manager* pResourceManager, const wchar_t* pFilePath, ma_uint32 flags) { return ma_resource_manager_data_buffer_node_acquire(pResourceManager, NULL, pFilePath, 0, flags, NULL, NULL, NULL, NULL); } static ma_result ma_resource_manager_register_data(ma_resource_manager* pResourceManager, const char* pName, const wchar_t* pNameW, ma_resource_manager_data_supply* pExistingData) { return ma_resource_manager_data_buffer_node_acquire(pResourceManager, pName, pNameW, 0, 0, pExistingData, NULL, NULL, NULL); } static ma_result ma_resource_manager_register_decoded_data_internal(ma_resource_manager* pResourceManager, const char* pName, const wchar_t* pNameW, const void* pData, ma_uint64 frameCount, ma_format format, ma_uint32 channels, ma_uint32 sampleRate) { ma_resource_manager_data_supply data; data.type = ma_resource_manager_data_supply_type_decoded; data.backend.decoded.pData = pData; data.backend.decoded.totalFrameCount = frameCount; data.backend.decoded.format = format; data.backend.decoded.channels = channels; data.backend.decoded.sampleRate = sampleRate; return ma_resource_manager_register_data(pResourceManager, pName, pNameW, &data); } MA_API ma_result ma_resource_manager_register_decoded_data(ma_resource_manager* pResourceManager, const char* pName, const void* pData, ma_uint64 frameCount, ma_format format, ma_uint32 channels, ma_uint32 sampleRate) { return ma_resource_manager_register_decoded_data_internal(pResourceManager, pName, NULL, pData, frameCount, format, channels, sampleRate); } MA_API ma_result ma_resource_manager_register_decoded_data_w(ma_resource_manager* pResourceManager, const wchar_t* pName, const void* pData, ma_uint64 frameCount, ma_format format, ma_uint32 channels, ma_uint32 sampleRate) { return ma_resource_manager_register_decoded_data_internal(pResourceManager, NULL, pName, pData, frameCount, format, channels, sampleRate); } static ma_result ma_resource_manager_register_encoded_data_internal(ma_resource_manager* pResourceManager, const char* pName, const wchar_t* pNameW, const void* pData, size_t sizeInBytes) { ma_resource_manager_data_supply data; data.type = ma_resource_manager_data_supply_type_encoded; data.backend.encoded.pData = pData; data.backend.encoded.sizeInBytes = sizeInBytes; return ma_resource_manager_register_data(pResourceManager, pName, pNameW, &data); } MA_API ma_result ma_resource_manager_register_encoded_data(ma_resource_manager* pResourceManager, const char* pName, const void* pData, size_t sizeInBytes) { return ma_resource_manager_register_encoded_data_internal(pResourceManager, pName, NULL, pData, sizeInBytes); } MA_API ma_result ma_resource_manager_register_encoded_data_w(ma_resource_manager* pResourceManager, const wchar_t* pName, const void* pData, size_t sizeInBytes) { return ma_resource_manager_register_encoded_data_internal(pResourceManager, NULL, pName, pData, sizeInBytes); } MA_API ma_result ma_resource_manager_unregister_file(ma_resource_manager* pResourceManager, const char* pFilePath) { return ma_resource_manager_unregister_data(pResourceManager, pFilePath); } MA_API ma_result ma_resource_manager_unregister_file_w(ma_resource_manager* pResourceManager, const wchar_t* pFilePath) { return ma_resource_manager_unregister_data_w(pResourceManager, pFilePath); } MA_API ma_result ma_resource_manager_unregister_data(ma_resource_manager* pResourceManager, const char* pName) { return ma_resource_manager_data_buffer_node_unacquire(pResourceManager, NULL, pName, NULL); } MA_API ma_result ma_resource_manager_unregister_data_w(ma_resource_manager* pResourceManager, const wchar_t* pName) { return ma_resource_manager_data_buffer_node_unacquire(pResourceManager, NULL, NULL, pName); } static ma_uint32 ma_resource_manager_data_stream_next_execution_order(ma_resource_manager_data_stream* pDataStream) { MA_ASSERT(pDataStream != NULL); return c89atomic_fetch_add_32(&pDataStream->executionCounter, 1); } static ma_bool32 ma_resource_manager_data_stream_is_decoder_at_end(const ma_resource_manager_data_stream* pDataStream) { MA_ASSERT(pDataStream != NULL); return c89atomic_load_32((ma_bool32*)&pDataStream->isDecoderAtEnd); } static ma_uint32 ma_resource_manager_data_stream_seek_counter(const ma_resource_manager_data_stream* pDataStream) { MA_ASSERT(pDataStream != NULL); return c89atomic_load_32((ma_uint32*)&pDataStream->seekCounter); } static ma_result ma_resource_manager_data_stream_cb__read_pcm_frames(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) { return ma_resource_manager_data_stream_read_pcm_frames((ma_resource_manager_data_stream*)pDataSource, pFramesOut, frameCount, pFramesRead); } static ma_result ma_resource_manager_data_stream_cb__seek_to_pcm_frame(ma_data_source* pDataSource, ma_uint64 frameIndex) { return ma_resource_manager_data_stream_seek_to_pcm_frame((ma_resource_manager_data_stream*)pDataSource, frameIndex); } static ma_result ma_resource_manager_data_stream_cb__get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) { return ma_resource_manager_data_stream_get_data_format((ma_resource_manager_data_stream*)pDataSource, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap); } static ma_result ma_resource_manager_data_stream_cb__get_cursor_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pCursor) { return ma_resource_manager_data_stream_get_cursor_in_pcm_frames((ma_resource_manager_data_stream*)pDataSource, pCursor); } static ma_result ma_resource_manager_data_stream_cb__get_length_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pLength) { return ma_resource_manager_data_stream_get_length_in_pcm_frames((ma_resource_manager_data_stream*)pDataSource, pLength); } static ma_result ma_resource_manager_data_stream_cb__set_looping(ma_data_source* pDataSource, ma_bool32 isLooping) { ma_resource_manager_data_stream* pDataStream = (ma_resource_manager_data_stream*)pDataSource; MA_ASSERT(pDataStream != NULL); c89atomic_exchange_32(&pDataStream->isLooping, isLooping); return MA_SUCCESS; } static ma_data_source_vtable g_ma_resource_manager_data_stream_vtable = { ma_resource_manager_data_stream_cb__read_pcm_frames, ma_resource_manager_data_stream_cb__seek_to_pcm_frame, ma_resource_manager_data_stream_cb__get_data_format, ma_resource_manager_data_stream_cb__get_cursor_in_pcm_frames, ma_resource_manager_data_stream_cb__get_length_in_pcm_frames, ma_resource_manager_data_stream_cb__set_looping, MA_DATA_SOURCE_SELF_MANAGED_RANGE_AND_LOOP_POINT }; static void ma_resource_manager_data_stream_set_absolute_cursor(ma_resource_manager_data_stream* pDataStream, ma_uint64 absoluteCursor) { /* Loop if possible. */ if (absoluteCursor > pDataStream->totalLengthInPCMFrames && pDataStream->totalLengthInPCMFrames > 0) { absoluteCursor = absoluteCursor % pDataStream->totalLengthInPCMFrames; } c89atomic_exchange_64(&pDataStream->absoluteCursor, absoluteCursor); } MA_API ma_result ma_resource_manager_data_stream_init_ex(ma_resource_manager* pResourceManager, const ma_resource_manager_data_source_config* pConfig, ma_resource_manager_data_stream* pDataStream) { ma_result result; ma_data_source_config dataSourceConfig; char* pFilePathCopy = NULL; wchar_t* pFilePathWCopy = NULL; ma_job job; ma_bool32 waitBeforeReturning = MA_FALSE; ma_resource_manager_inline_notification waitNotification; ma_resource_manager_pipeline_notifications notifications; if (pDataStream == NULL) { if (pConfig != NULL && pConfig->pNotifications != NULL) { ma_resource_manager_pipeline_notifications_signal_all_notifications(pConfig->pNotifications); } return MA_INVALID_ARGS; } MA_ZERO_OBJECT(pDataStream); if (pConfig == NULL) { return MA_INVALID_ARGS; } if (pConfig->pNotifications != NULL) { notifications = *pConfig->pNotifications; /* From here on out, `notifications` should be used instead of `pNotifications`. Setting this to NULL to catch any errors at testing time. */ } else { MA_ZERO_OBJECT(¬ifications); } dataSourceConfig = ma_data_source_config_init(); dataSourceConfig.vtable = &g_ma_resource_manager_data_stream_vtable; result = ma_data_source_init(&dataSourceConfig, &pDataStream->ds); if (result != MA_SUCCESS) { ma_resource_manager_pipeline_notifications_signal_all_notifications(¬ifications); return result; } pDataStream->pResourceManager = pResourceManager; pDataStream->flags = pConfig->flags; pDataStream->result = MA_BUSY; ma_data_source_set_range_in_pcm_frames(pDataStream, pConfig->rangeBegInPCMFrames, pConfig->rangeEndInPCMFrames); ma_data_source_set_loop_point_in_pcm_frames(pDataStream, pConfig->loopPointBegInPCMFrames, pConfig->loopPointEndInPCMFrames); ma_data_source_set_looping(pDataStream, pConfig->isLooping); if (pResourceManager == NULL || (pConfig->pFilePath == NULL && pConfig->pFilePathW == NULL)) { ma_resource_manager_pipeline_notifications_signal_all_notifications(¬ifications); return MA_INVALID_ARGS; } /* We want all access to the VFS and the internal decoder to happen on the job thread just to keep things easier to manage for the VFS. */ /* We need a copy of the file path. We should probably make this more efficient, but for now we'll do a transient memory allocation. */ if (pConfig->pFilePath != NULL) { pFilePathCopy = ma_copy_string(pConfig->pFilePath, &pResourceManager->config.allocationCallbacks); } else { pFilePathWCopy = ma_copy_string_w(pConfig->pFilePathW, &pResourceManager->config.allocationCallbacks); } if (pFilePathCopy == NULL && pFilePathWCopy == NULL) { ma_resource_manager_pipeline_notifications_signal_all_notifications(¬ifications); return MA_OUT_OF_MEMORY; } /* We need to check for the presence of MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC. If it's not set, we need to wait before returning. Otherwise we can return immediately. Likewise, we'll also check for MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT and do the same. */ if ((pConfig->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC) == 0 || (pConfig->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) { waitBeforeReturning = MA_TRUE; ma_resource_manager_inline_notification_init(pResourceManager, &waitNotification); } ma_resource_manager_pipeline_notifications_acquire_all_fences(¬ifications); /* Set the absolute cursor to our initial seek position so retrieval of the cursor returns a good value. */ ma_resource_manager_data_stream_set_absolute_cursor(pDataStream, pConfig->initialSeekPointInPCMFrames); /* We now have everything we need to post the job. This is the last thing we need to do from here. The rest will be done by the job thread. */ job = ma_job_init(MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_STREAM); job.order = ma_resource_manager_data_stream_next_execution_order(pDataStream); job.data.resourceManager.loadDataStream.pDataStream = pDataStream; job.data.resourceManager.loadDataStream.pFilePath = pFilePathCopy; job.data.resourceManager.loadDataStream.pFilePathW = pFilePathWCopy; job.data.resourceManager.loadDataStream.initialSeekPoint = pConfig->initialSeekPointInPCMFrames; job.data.resourceManager.loadDataStream.pInitNotification = (waitBeforeReturning == MA_TRUE) ? &waitNotification : notifications.init.pNotification; job.data.resourceManager.loadDataStream.pInitFence = notifications.init.pFence; result = ma_resource_manager_post_job(pResourceManager, &job); if (result != MA_SUCCESS) { ma_resource_manager_pipeline_notifications_signal_all_notifications(¬ifications); ma_resource_manager_pipeline_notifications_release_all_fences(¬ifications); if (waitBeforeReturning) { ma_resource_manager_inline_notification_uninit(&waitNotification); } ma_free(pFilePathCopy, &pResourceManager->config.allocationCallbacks); ma_free(pFilePathWCopy, &pResourceManager->config.allocationCallbacks); return result; } /* Wait if needed. */ if (waitBeforeReturning) { ma_resource_manager_inline_notification_wait_and_uninit(&waitNotification); if (notifications.init.pNotification != NULL) { ma_async_notification_signal(notifications.init.pNotification); } /* NOTE: Do not release pInitFence here. That will be done by the job. */ } return MA_SUCCESS; } MA_API ma_result ma_resource_manager_data_stream_init(ma_resource_manager* pResourceManager, const char* pFilePath, ma_uint32 flags, const ma_resource_manager_pipeline_notifications* pNotifications, ma_resource_manager_data_stream* pDataStream) { ma_resource_manager_data_source_config config; config = ma_resource_manager_data_source_config_init(); config.pFilePath = pFilePath; config.flags = flags; config.pNotifications = pNotifications; return ma_resource_manager_data_stream_init_ex(pResourceManager, &config, pDataStream); } MA_API ma_result ma_resource_manager_data_stream_init_w(ma_resource_manager* pResourceManager, const wchar_t* pFilePath, ma_uint32 flags, const ma_resource_manager_pipeline_notifications* pNotifications, ma_resource_manager_data_stream* pDataStream) { ma_resource_manager_data_source_config config; config = ma_resource_manager_data_source_config_init(); config.pFilePathW = pFilePath; config.flags = flags; config.pNotifications = pNotifications; return ma_resource_manager_data_stream_init_ex(pResourceManager, &config, pDataStream); } MA_API ma_result ma_resource_manager_data_stream_uninit(ma_resource_manager_data_stream* pDataStream) { ma_resource_manager_inline_notification freeEvent; ma_job job; if (pDataStream == NULL) { return MA_INVALID_ARGS; } /* The first thing to do is set the result to unavailable. This will prevent future page decoding. */ c89atomic_exchange_i32(&pDataStream->result, MA_UNAVAILABLE); /* We need to post a job to ensure we're not in the middle or decoding or anything. Because the object is owned by the caller, we'll need to wait for it to complete before returning which means we need an event. */ ma_resource_manager_inline_notification_init(pDataStream->pResourceManager, &freeEvent); job = ma_job_init(MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_STREAM); job.order = ma_resource_manager_data_stream_next_execution_order(pDataStream); job.data.resourceManager.freeDataStream.pDataStream = pDataStream; job.data.resourceManager.freeDataStream.pDoneNotification = &freeEvent; job.data.resourceManager.freeDataStream.pDoneFence = NULL; ma_resource_manager_post_job(pDataStream->pResourceManager, &job); /* We need to wait for the job to finish processing before we return. */ ma_resource_manager_inline_notification_wait_and_uninit(&freeEvent); return MA_SUCCESS; } static ma_uint32 ma_resource_manager_data_stream_get_page_size_in_frames(ma_resource_manager_data_stream* pDataStream) { MA_ASSERT(pDataStream != NULL); MA_ASSERT(pDataStream->isDecoderInitialized == MA_TRUE); return MA_RESOURCE_MANAGER_PAGE_SIZE_IN_MILLISECONDS * (pDataStream->decoder.outputSampleRate/1000); } static void* ma_resource_manager_data_stream_get_page_data_pointer(ma_resource_manager_data_stream* pDataStream, ma_uint32 pageIndex, ma_uint32 relativeCursor) { MA_ASSERT(pDataStream != NULL); MA_ASSERT(pDataStream->isDecoderInitialized == MA_TRUE); MA_ASSERT(pageIndex == 0 || pageIndex == 1); return ma_offset_ptr(pDataStream->pPageData, ((ma_resource_manager_data_stream_get_page_size_in_frames(pDataStream) * pageIndex) + relativeCursor) * ma_get_bytes_per_frame(pDataStream->decoder.outputFormat, pDataStream->decoder.outputChannels)); } static void ma_resource_manager_data_stream_fill_page(ma_resource_manager_data_stream* pDataStream, ma_uint32 pageIndex) { ma_result result = MA_SUCCESS; ma_uint64 pageSizeInFrames; ma_uint64 totalFramesReadForThisPage = 0; void* pPageData = ma_resource_manager_data_stream_get_page_data_pointer(pDataStream, pageIndex, 0); pageSizeInFrames = ma_resource_manager_data_stream_get_page_size_in_frames(pDataStream); /* The decoder needs to inherit the stream's looping and range state. */ { ma_uint64 rangeBeg; ma_uint64 rangeEnd; ma_uint64 loopPointBeg; ma_uint64 loopPointEnd; ma_data_source_set_looping(&pDataStream->decoder, ma_resource_manager_data_stream_is_looping(pDataStream)); ma_data_source_get_range_in_pcm_frames(pDataStream, &rangeBeg, &rangeEnd); ma_data_source_set_range_in_pcm_frames(&pDataStream->decoder, rangeBeg, rangeEnd); ma_data_source_get_loop_point_in_pcm_frames(pDataStream, &loopPointBeg, &loopPointEnd); ma_data_source_set_loop_point_in_pcm_frames(&pDataStream->decoder, loopPointBeg, loopPointEnd); } /* Just read straight from the decoder. It will deal with ranges and looping for us. */ result = ma_data_source_read_pcm_frames(&pDataStream->decoder, pPageData, pageSizeInFrames, &totalFramesReadForThisPage); if (result == MA_AT_END || totalFramesReadForThisPage < pageSizeInFrames) { c89atomic_exchange_32(&pDataStream->isDecoderAtEnd, MA_TRUE); } c89atomic_exchange_32(&pDataStream->pageFrameCount[pageIndex], (ma_uint32)totalFramesReadForThisPage); c89atomic_exchange_32(&pDataStream->isPageValid[pageIndex], MA_TRUE); } static void ma_resource_manager_data_stream_fill_pages(ma_resource_manager_data_stream* pDataStream) { ma_uint32 iPage; MA_ASSERT(pDataStream != NULL); for (iPage = 0; iPage < 2; iPage += 1) { ma_resource_manager_data_stream_fill_page(pDataStream, iPage); } } static ma_result ma_resource_manager_data_stream_map(ma_resource_manager_data_stream* pDataStream, void** ppFramesOut, ma_uint64* pFrameCount) { ma_uint64 framesAvailable; ma_uint64 frameCount = 0; /* We cannot be using the data source after it's been uninitialized. */ MA_ASSERT(ma_resource_manager_data_stream_result(pDataStream) != MA_UNAVAILABLE); if (pFrameCount != NULL) { frameCount = *pFrameCount; *pFrameCount = 0; } if (ppFramesOut != NULL) { *ppFramesOut = NULL; } if (pDataStream == NULL || ppFramesOut == NULL || pFrameCount == NULL) { return MA_INVALID_ARGS; } if (ma_resource_manager_data_stream_result(pDataStream) != MA_SUCCESS) { return MA_INVALID_OPERATION; } /* Don't attempt to read while we're in the middle of seeking. Tell the caller that we're busy. */ if (ma_resource_manager_data_stream_seek_counter(pDataStream) > 0) { return MA_BUSY; } /* If the page we're on is invalid it means we've caught up to the job thread. */ if (c89atomic_load_32(&pDataStream->isPageValid[pDataStream->currentPageIndex]) == MA_FALSE) { framesAvailable = 0; } else { /* The page we're on is valid so we must have some frames available. We need to make sure that we don't overflow into the next page, even if it's valid. The reason is that the unmap process will only post an update for one page at a time. Keeping mapping tied to page boundaries makes this simpler. */ ma_uint32 currentPageFrameCount = c89atomic_load_32(&pDataStream->pageFrameCount[pDataStream->currentPageIndex]); MA_ASSERT(currentPageFrameCount >= pDataStream->relativeCursor); framesAvailable = currentPageFrameCount - pDataStream->relativeCursor; } /* If there's no frames available and the result is set to MA_AT_END we need to return MA_AT_END. */ if (framesAvailable == 0) { if (ma_resource_manager_data_stream_is_decoder_at_end(pDataStream)) { return MA_AT_END; } else { return MA_BUSY; /* There are no frames available, but we're not marked as EOF so we might have caught up to the job thread. Need to return MA_BUSY and wait for more data. */ } } MA_ASSERT(framesAvailable > 0); if (frameCount > framesAvailable) { frameCount = framesAvailable; } *ppFramesOut = ma_resource_manager_data_stream_get_page_data_pointer(pDataStream, pDataStream->currentPageIndex, pDataStream->relativeCursor); *pFrameCount = frameCount; return MA_SUCCESS; } static ma_result ma_resource_manager_data_stream_unmap(ma_resource_manager_data_stream* pDataStream, ma_uint64 frameCount) { ma_uint32 newRelativeCursor; ma_uint32 pageSizeInFrames; ma_job job; /* We cannot be using the data source after it's been uninitialized. */ MA_ASSERT(ma_resource_manager_data_stream_result(pDataStream) != MA_UNAVAILABLE); if (pDataStream == NULL) { return MA_INVALID_ARGS; } if (ma_resource_manager_data_stream_result(pDataStream) != MA_SUCCESS) { return MA_INVALID_OPERATION; } /* The frame count should always fit inside a 32-bit integer. */ if (frameCount > 0xFFFFFFFF) { return MA_INVALID_ARGS; } pageSizeInFrames = ma_resource_manager_data_stream_get_page_size_in_frames(pDataStream); /* The absolute cursor needs to be updated for ma_resource_manager_data_stream_get_cursor_in_pcm_frames(). */ ma_resource_manager_data_stream_set_absolute_cursor(pDataStream, c89atomic_load_64(&pDataStream->absoluteCursor) + frameCount); /* Here is where we need to check if we need to load a new page, and if so, post a job to load it. */ newRelativeCursor = pDataStream->relativeCursor + (ma_uint32)frameCount; /* If the new cursor has flowed over to the next page we need to mark the old one as invalid and post an event for it. */ if (newRelativeCursor >= pageSizeInFrames) { newRelativeCursor -= pageSizeInFrames; /* Here is where we post the job start decoding. */ job = ma_job_init(MA_JOB_TYPE_RESOURCE_MANAGER_PAGE_DATA_STREAM); job.order = ma_resource_manager_data_stream_next_execution_order(pDataStream); job.data.resourceManager.pageDataStream.pDataStream = pDataStream; job.data.resourceManager.pageDataStream.pageIndex = pDataStream->currentPageIndex; /* The page needs to be marked as invalid so that the public API doesn't try reading from it. */ c89atomic_exchange_32(&pDataStream->isPageValid[pDataStream->currentPageIndex], MA_FALSE); /* Before posting the job we need to make sure we set some state. */ pDataStream->relativeCursor = newRelativeCursor; pDataStream->currentPageIndex = (pDataStream->currentPageIndex + 1) & 0x01; return ma_resource_manager_post_job(pDataStream->pResourceManager, &job); } else { /* We haven't moved into a new page so we can just move the cursor forward. */ pDataStream->relativeCursor = newRelativeCursor; return MA_SUCCESS; } } MA_API ma_result ma_resource_manager_data_stream_read_pcm_frames(ma_resource_manager_data_stream* pDataStream, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) { ma_result result = MA_SUCCESS; ma_uint64 totalFramesProcessed; ma_format format; ma_uint32 channels; /* Safety. */ if (pFramesRead != NULL) { *pFramesRead = 0; } if (frameCount == 0) { return MA_INVALID_ARGS; } /* We cannot be using the data source after it's been uninitialized. */ MA_ASSERT(ma_resource_manager_data_stream_result(pDataStream) != MA_UNAVAILABLE); if (pDataStream == NULL) { return MA_INVALID_ARGS; } if (ma_resource_manager_data_stream_result(pDataStream) != MA_SUCCESS) { return MA_INVALID_OPERATION; } /* Don't attempt to read while we're in the middle of seeking. Tell the caller that we're busy. */ if (ma_resource_manager_data_stream_seek_counter(pDataStream) > 0) { return MA_BUSY; } ma_resource_manager_data_stream_get_data_format(pDataStream, &format, &channels, NULL, NULL, 0); /* Reading is implemented in terms of map/unmap. We need to run this in a loop because mapping is clamped against page boundaries. */ totalFramesProcessed = 0; while (totalFramesProcessed < frameCount) { void* pMappedFrames; ma_uint64 mappedFrameCount; mappedFrameCount = frameCount - totalFramesProcessed; result = ma_resource_manager_data_stream_map(pDataStream, &pMappedFrames, &mappedFrameCount); if (result != MA_SUCCESS) { break; } /* Copy the mapped data to the output buffer if we have one. It's allowed for pFramesOut to be NULL in which case a relative forward seek is performed. */ if (pFramesOut != NULL) { ma_copy_pcm_frames(ma_offset_pcm_frames_ptr(pFramesOut, totalFramesProcessed, format, channels), pMappedFrames, mappedFrameCount, format, channels); } totalFramesProcessed += mappedFrameCount; result = ma_resource_manager_data_stream_unmap(pDataStream, mappedFrameCount); if (result != MA_SUCCESS) { break; /* This is really bad - will only get an error here if we failed to post a job to the queue for loading the next page. */ } } if (pFramesRead != NULL) { *pFramesRead = totalFramesProcessed; } if (result == MA_SUCCESS && totalFramesProcessed == 0) { result = MA_AT_END; } return result; } MA_API ma_result ma_resource_manager_data_stream_seek_to_pcm_frame(ma_resource_manager_data_stream* pDataStream, ma_uint64 frameIndex) { ma_job job; ma_result streamResult; streamResult = ma_resource_manager_data_stream_result(pDataStream); /* We cannot be using the data source after it's been uninitialized. */ MA_ASSERT(streamResult != MA_UNAVAILABLE); if (pDataStream == NULL) { return MA_INVALID_ARGS; } if (streamResult != MA_SUCCESS && streamResult != MA_BUSY) { return MA_INVALID_OPERATION; } /* If we're not already seeking and we're sitting on the same frame, just make this a no-op. */ if (c89atomic_load_32(&pDataStream->seekCounter) == 0) { if (c89atomic_load_64(&pDataStream->absoluteCursor) == frameIndex) { return MA_SUCCESS; } } /* Increment the seek counter first to indicate to read_paged_pcm_frames() and map_paged_pcm_frames() that we are in the middle of a seek and MA_BUSY should be returned. */ c89atomic_fetch_add_32(&pDataStream->seekCounter, 1); /* Update the absolute cursor so that ma_resource_manager_data_stream_get_cursor_in_pcm_frames() returns the new position. */ ma_resource_manager_data_stream_set_absolute_cursor(pDataStream, frameIndex); /* We need to clear our currently loaded pages so that the stream starts playback from the new seek point as soon as possible. These are for the purpose of the public API and will be ignored by the seek job. The seek job will operate on the assumption that both pages have been marked as invalid and the cursor is at the start of the first page. */ pDataStream->relativeCursor = 0; pDataStream->currentPageIndex = 0; c89atomic_exchange_32(&pDataStream->isPageValid[0], MA_FALSE); c89atomic_exchange_32(&pDataStream->isPageValid[1], MA_FALSE); /* Make sure the data stream is not marked as at the end or else if we seek in response to hitting the end, we won't be able to read any more data. */ c89atomic_exchange_32(&pDataStream->isDecoderAtEnd, MA_FALSE); /* The public API is not allowed to touch the internal decoder so we need to use a job to perform the seek. When seeking, the job thread will assume both pages are invalid and any content contained within them will be discarded and replaced with newly decoded data. */ job = ma_job_init(MA_JOB_TYPE_RESOURCE_MANAGER_SEEK_DATA_STREAM); job.order = ma_resource_manager_data_stream_next_execution_order(pDataStream); job.data.resourceManager.seekDataStream.pDataStream = pDataStream; job.data.resourceManager.seekDataStream.frameIndex = frameIndex; return ma_resource_manager_post_job(pDataStream->pResourceManager, &job); } MA_API ma_result ma_resource_manager_data_stream_get_data_format(ma_resource_manager_data_stream* pDataStream, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) { /* We cannot be using the data source after it's been uninitialized. */ MA_ASSERT(ma_resource_manager_data_stream_result(pDataStream) != MA_UNAVAILABLE); if (pFormat != NULL) { *pFormat = ma_format_unknown; } if (pChannels != NULL) { *pChannels = 0; } if (pSampleRate != NULL) { *pSampleRate = 0; } if (pChannelMap != NULL) { MA_ZERO_MEMORY(pChannelMap, sizeof(*pChannelMap) * channelMapCap); } if (pDataStream == NULL) { return MA_INVALID_ARGS; } if (ma_resource_manager_data_stream_result(pDataStream) != MA_SUCCESS) { return MA_INVALID_OPERATION; } /* We're being a little bit naughty here and accessing the internal decoder from the public API. The output data format is constant, and we've defined this function such that the application is responsible for ensuring it's not called while uninitializing so it should be safe. */ return ma_data_source_get_data_format(&pDataStream->decoder, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap); } MA_API ma_result ma_resource_manager_data_stream_get_cursor_in_pcm_frames(ma_resource_manager_data_stream* pDataStream, ma_uint64* pCursor) { ma_result result; if (pCursor == NULL) { return MA_INVALID_ARGS; } *pCursor = 0; /* We cannot be using the data source after it's been uninitialized. */ MA_ASSERT(ma_resource_manager_data_stream_result(pDataStream) != MA_UNAVAILABLE); if (pDataStream == NULL) { return MA_INVALID_ARGS; } /* If the stream is in an erroneous state we need to return an invalid operation. We can allow this to be called when the data stream is in a busy state because the caller may have asked for an initial seek position and it's convenient to return that as the cursor position. */ result = ma_resource_manager_data_stream_result(pDataStream); if (result != MA_SUCCESS && result != MA_BUSY) { return MA_INVALID_OPERATION; } *pCursor = c89atomic_load_64(&pDataStream->absoluteCursor); return MA_SUCCESS; } MA_API ma_result ma_resource_manager_data_stream_get_length_in_pcm_frames(ma_resource_manager_data_stream* pDataStream, ma_uint64* pLength) { ma_result streamResult; if (pLength == NULL) { return MA_INVALID_ARGS; } *pLength = 0; streamResult = ma_resource_manager_data_stream_result(pDataStream); /* We cannot be using the data source after it's been uninitialized. */ MA_ASSERT(streamResult != MA_UNAVAILABLE); if (pDataStream == NULL) { return MA_INVALID_ARGS; } if (streamResult != MA_SUCCESS) { return streamResult; } /* We most definitely do not want to be calling ma_decoder_get_length_in_pcm_frames() directly. Instead we want to use a cached value that we calculated when we initialized it on the job thread. */ *pLength = pDataStream->totalLengthInPCMFrames; if (*pLength == 0) { return MA_NOT_IMPLEMENTED; /* Some decoders may not have a known length. */ } return MA_SUCCESS; } MA_API ma_result ma_resource_manager_data_stream_result(const ma_resource_manager_data_stream* pDataStream) { if (pDataStream == NULL) { return MA_INVALID_ARGS; } return (ma_result)c89atomic_load_i32(&pDataStream->result); } MA_API ma_result ma_resource_manager_data_stream_set_looping(ma_resource_manager_data_stream* pDataStream, ma_bool32 isLooping) { return ma_data_source_set_looping(pDataStream, isLooping); } MA_API ma_bool32 ma_resource_manager_data_stream_is_looping(const ma_resource_manager_data_stream* pDataStream) { if (pDataStream == NULL) { return MA_FALSE; } return c89atomic_load_32((ma_bool32*)&pDataStream->isLooping); /* Naughty const-cast. Value won't change from here in practice (maybe from another thread). */ } MA_API ma_result ma_resource_manager_data_stream_get_available_frames(ma_resource_manager_data_stream* pDataStream, ma_uint64* pAvailableFrames) { ma_uint32 pageIndex0; ma_uint32 pageIndex1; ma_uint32 relativeCursor; ma_uint64 availableFrames; if (pAvailableFrames == NULL) { return MA_INVALID_ARGS; } *pAvailableFrames = 0; if (pDataStream == NULL) { return MA_INVALID_ARGS; } pageIndex0 = pDataStream->currentPageIndex; pageIndex1 = (pDataStream->currentPageIndex + 1) & 0x01; relativeCursor = pDataStream->relativeCursor; availableFrames = 0; if (c89atomic_load_32(&pDataStream->isPageValid[pageIndex0])) { availableFrames += c89atomic_load_32(&pDataStream->pageFrameCount[pageIndex0]) - relativeCursor; if (c89atomic_load_32(&pDataStream->isPageValid[pageIndex1])) { availableFrames += c89atomic_load_32(&pDataStream->pageFrameCount[pageIndex1]); } } *pAvailableFrames = availableFrames; return MA_SUCCESS; } static ma_result ma_resource_manager_data_source_preinit(ma_resource_manager* pResourceManager, const ma_resource_manager_data_source_config* pConfig, ma_resource_manager_data_source* pDataSource) { if (pDataSource == NULL) { return MA_INVALID_ARGS; } MA_ZERO_OBJECT(pDataSource); if (pConfig == NULL) { return MA_INVALID_ARGS; } if (pResourceManager == NULL) { return MA_INVALID_ARGS; } pDataSource->flags = pConfig->flags; return MA_SUCCESS; } MA_API ma_result ma_resource_manager_data_source_init_ex(ma_resource_manager* pResourceManager, const ma_resource_manager_data_source_config* pConfig, ma_resource_manager_data_source* pDataSource) { ma_result result; result = ma_resource_manager_data_source_preinit(pResourceManager, pConfig, pDataSource); if (result != MA_SUCCESS) { return result; } /* The data source itself is just a data stream or a data buffer. */ if ((pConfig->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) { return ma_resource_manager_data_stream_init_ex(pResourceManager, pConfig, &pDataSource->backend.stream); } else { return ma_resource_manager_data_buffer_init_ex(pResourceManager, pConfig, &pDataSource->backend.buffer); } } MA_API ma_result ma_resource_manager_data_source_init(ma_resource_manager* pResourceManager, const char* pName, ma_uint32 flags, const ma_resource_manager_pipeline_notifications* pNotifications, ma_resource_manager_data_source* pDataSource) { ma_resource_manager_data_source_config config; config = ma_resource_manager_data_source_config_init(); config.pFilePath = pName; config.flags = flags; config.pNotifications = pNotifications; return ma_resource_manager_data_source_init_ex(pResourceManager, &config, pDataSource); } MA_API ma_result ma_resource_manager_data_source_init_w(ma_resource_manager* pResourceManager, const wchar_t* pName, ma_uint32 flags, const ma_resource_manager_pipeline_notifications* pNotifications, ma_resource_manager_data_source* pDataSource) { ma_resource_manager_data_source_config config; config = ma_resource_manager_data_source_config_init(); config.pFilePathW = pName; config.flags = flags; config.pNotifications = pNotifications; return ma_resource_manager_data_source_init_ex(pResourceManager, &config, pDataSource); } MA_API ma_result ma_resource_manager_data_source_init_copy(ma_resource_manager* pResourceManager, const ma_resource_manager_data_source* pExistingDataSource, ma_resource_manager_data_source* pDataSource) { ma_result result; ma_resource_manager_data_source_config config; if (pExistingDataSource == NULL) { return MA_INVALID_ARGS; } config = ma_resource_manager_data_source_config_init(); config.flags = pExistingDataSource->flags; result = ma_resource_manager_data_source_preinit(pResourceManager, &config, pDataSource); if (result != MA_SUCCESS) { return result; } /* Copying can only be done from data buffers. Streams cannot be copied. */ if ((pExistingDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) { return MA_INVALID_OPERATION; } return ma_resource_manager_data_buffer_init_copy(pResourceManager, &pExistingDataSource->backend.buffer, &pDataSource->backend.buffer); } MA_API ma_result ma_resource_manager_data_source_uninit(ma_resource_manager_data_source* pDataSource) { if (pDataSource == NULL) { return MA_INVALID_ARGS; } /* All we need to is uninitialize the underlying data buffer or data stream. */ if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) { return ma_resource_manager_data_stream_uninit(&pDataSource->backend.stream); } else { return ma_resource_manager_data_buffer_uninit(&pDataSource->backend.buffer); } } MA_API ma_result ma_resource_manager_data_source_read_pcm_frames(ma_resource_manager_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) { /* Safety. */ if (pFramesRead != NULL) { *pFramesRead = 0; } if (pDataSource == NULL) { return MA_INVALID_ARGS; } if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) { return ma_resource_manager_data_stream_read_pcm_frames(&pDataSource->backend.stream, pFramesOut, frameCount, pFramesRead); } else { return ma_resource_manager_data_buffer_read_pcm_frames(&pDataSource->backend.buffer, pFramesOut, frameCount, pFramesRead); } } MA_API ma_result ma_resource_manager_data_source_seek_to_pcm_frame(ma_resource_manager_data_source* pDataSource, ma_uint64 frameIndex) { if (pDataSource == NULL) { return MA_INVALID_ARGS; } if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) { return ma_resource_manager_data_stream_seek_to_pcm_frame(&pDataSource->backend.stream, frameIndex); } else { return ma_resource_manager_data_buffer_seek_to_pcm_frame(&pDataSource->backend.buffer, frameIndex); } } MA_API ma_result ma_resource_manager_data_source_map(ma_resource_manager_data_source* pDataSource, void** ppFramesOut, ma_uint64* pFrameCount) { if (pDataSource == NULL) { return MA_INVALID_ARGS; } if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) { return ma_resource_manager_data_stream_map(&pDataSource->backend.stream, ppFramesOut, pFrameCount); } else { return MA_NOT_IMPLEMENTED; /* Mapping not supported with data buffers. */ } } MA_API ma_result ma_resource_manager_data_source_unmap(ma_resource_manager_data_source* pDataSource, ma_uint64 frameCount) { if (pDataSource == NULL) { return MA_INVALID_ARGS; } if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) { return ma_resource_manager_data_stream_unmap(&pDataSource->backend.stream, frameCount); } else { return MA_NOT_IMPLEMENTED; /* Mapping not supported with data buffers. */ } } MA_API ma_result ma_resource_manager_data_source_get_data_format(ma_resource_manager_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) { if (pDataSource == NULL) { return MA_INVALID_ARGS; } if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) { return ma_resource_manager_data_stream_get_data_format(&pDataSource->backend.stream, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap); } else { return ma_resource_manager_data_buffer_get_data_format(&pDataSource->backend.buffer, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap); } } MA_API ma_result ma_resource_manager_data_source_get_cursor_in_pcm_frames(ma_resource_manager_data_source* pDataSource, ma_uint64* pCursor) { if (pDataSource == NULL) { return MA_INVALID_ARGS; } if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) { return ma_resource_manager_data_stream_get_cursor_in_pcm_frames(&pDataSource->backend.stream, pCursor); } else { return ma_resource_manager_data_buffer_get_cursor_in_pcm_frames(&pDataSource->backend.buffer, pCursor); } } MA_API ma_result ma_resource_manager_data_source_get_length_in_pcm_frames(ma_resource_manager_data_source* pDataSource, ma_uint64* pLength) { if (pDataSource == NULL) { return MA_INVALID_ARGS; } if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) { return ma_resource_manager_data_stream_get_length_in_pcm_frames(&pDataSource->backend.stream, pLength); } else { return ma_resource_manager_data_buffer_get_length_in_pcm_frames(&pDataSource->backend.buffer, pLength); } } MA_API ma_result ma_resource_manager_data_source_result(const ma_resource_manager_data_source* pDataSource) { if (pDataSource == NULL) { return MA_INVALID_ARGS; } if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) { return ma_resource_manager_data_stream_result(&pDataSource->backend.stream); } else { return ma_resource_manager_data_buffer_result(&pDataSource->backend.buffer); } } MA_API ma_result ma_resource_manager_data_source_set_looping(ma_resource_manager_data_source* pDataSource, ma_bool32 isLooping) { if (pDataSource == NULL) { return MA_INVALID_ARGS; } if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) { return ma_resource_manager_data_stream_set_looping(&pDataSource->backend.stream, isLooping); } else { return ma_resource_manager_data_buffer_set_looping(&pDataSource->backend.buffer, isLooping); } } MA_API ma_bool32 ma_resource_manager_data_source_is_looping(const ma_resource_manager_data_source* pDataSource) { if (pDataSource == NULL) { return MA_FALSE; } if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) { return ma_resource_manager_data_stream_is_looping(&pDataSource->backend.stream); } else { return ma_resource_manager_data_buffer_is_looping(&pDataSource->backend.buffer); } } MA_API ma_result ma_resource_manager_data_source_get_available_frames(ma_resource_manager_data_source* pDataSource, ma_uint64* pAvailableFrames) { if (pAvailableFrames == NULL) { return MA_INVALID_ARGS; } *pAvailableFrames = 0; if (pDataSource == NULL) { return MA_INVALID_ARGS; } if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) { return ma_resource_manager_data_stream_get_available_frames(&pDataSource->backend.stream, pAvailableFrames); } else { return ma_resource_manager_data_buffer_get_available_frames(&pDataSource->backend.buffer, pAvailableFrames); } } MA_API ma_result ma_resource_manager_post_job(ma_resource_manager* pResourceManager, const ma_job* pJob) { if (pResourceManager == NULL) { return MA_INVALID_ARGS; } return ma_job_queue_post(&pResourceManager->jobQueue, pJob); } MA_API ma_result ma_resource_manager_post_job_quit(ma_resource_manager* pResourceManager) { ma_job job = ma_job_init(MA_JOB_TYPE_QUIT); return ma_resource_manager_post_job(pResourceManager, &job); } MA_API ma_result ma_resource_manager_next_job(ma_resource_manager* pResourceManager, ma_job* pJob) { if (pResourceManager == NULL) { return MA_INVALID_ARGS; } return ma_job_queue_next(&pResourceManager->jobQueue, pJob); } static ma_result ma_job_process__resource_manager__load_data_buffer_node(ma_job* pJob) { ma_result result = MA_SUCCESS; ma_resource_manager* pResourceManager; ma_resource_manager_data_buffer_node* pDataBufferNode; MA_ASSERT(pJob != NULL); pResourceManager = (ma_resource_manager*)pJob->data.resourceManager.loadDataBufferNode.pResourceManager; MA_ASSERT(pResourceManager != NULL); pDataBufferNode = (ma_resource_manager_data_buffer_node*)pJob->data.resourceManager.loadDataBufferNode.pDataBufferNode; MA_ASSERT(pDataBufferNode != NULL); MA_ASSERT(pDataBufferNode->isDataOwnedByResourceManager == MA_TRUE); /* The data should always be owned by the resource manager. */ /* The data buffer is not getting deleted, but we may be getting executed out of order. If so, we need to push the job back onto the queue and return. */ if (pJob->order != c89atomic_load_32(&pDataBufferNode->executionPointer)) { return ma_resource_manager_post_job(pResourceManager, pJob); /* Attempting to execute out of order. Probably interleaved with a MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_BUFFER job. */ } /* First thing we need to do is check whether or not the data buffer is getting deleted. If so we just abort. */ if (ma_resource_manager_data_buffer_node_result(pDataBufferNode) != MA_BUSY) { result = ma_resource_manager_data_buffer_node_result(pDataBufferNode); /* The data buffer may be getting deleted before it's even been loaded. */ goto done; } /* We're ready to start loading. Essentially what we're doing here is initializing the data supply of the node. Once this is complete, data buffers can have their connectors initialized which will allow then to have audio data read from them. Note that when the data supply type has been moved away from "unknown", that is when other threads will determine that the node is available for data delivery and the data buffer connectors can be initialized. Therefore, it's important that it is set after the data supply has been initialized. */ if ((pJob->data.resourceManager.loadDataBufferNode.flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_DECODE) != 0) { /* Decoding. This is the complex case because we're not going to be doing the entire decoding process here. Instead it's going to be split of multiple jobs and loaded in pages. The reason for this is to evenly distribute decoding time across multiple sounds, rather than having one huge sound hog all the available processing resources. The first thing we do is initialize a decoder. This is allocated on the heap and is passed around to the paging jobs. When the last paging job has completed it's processing, it'll free the decoder for us. This job does not do any actual decoding. It instead just posts a PAGE_DATA_BUFFER_NODE job which is where the actual decoding work will be done. However, once this job is complete, the node will be in a state where data buffer connectors can be initialized. */ ma_decoder* pDecoder; /* <-- Free'd on the last page decode. */ ma_job pageDataBufferNodeJob; /* Allocate the decoder by initializing a decoded data supply. */ result = ma_resource_manager_data_buffer_node_init_supply_decoded(pResourceManager, pDataBufferNode, pJob->data.resourceManager.loadDataBufferNode.pFilePath, pJob->data.resourceManager.loadDataBufferNode.pFilePathW, pJob->data.resourceManager.loadDataBufferNode.flags, &pDecoder); /* Don't ever propagate an MA_BUSY result code or else the resource manager will think the node is just busy decoding rather than in an error state. This should never happen, but including this logic for safety just in case. */ if (result == MA_BUSY) { result = MA_ERROR; } if (result != MA_SUCCESS) { if (pJob->data.resourceManager.loadDataBufferNode.pFilePath != NULL) { ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_WARNING, "Failed to initialize data supply for \"%s\". %s.\n", pJob->data.resourceManager.loadDataBufferNode.pFilePath, ma_result_description(result)); } else { #if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(_MSC_VER) ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_WARNING, "Failed to initialize data supply for \"%ls\", %s.\n", pJob->data.resourceManager.loadDataBufferNode.pFilePathW, ma_result_description(result)); #endif } goto done; } /* At this point the node's data supply is initialized and other threads can start initializing their data buffer connectors. However, no data will actually be available until we start to actually decode it. To do this, we need to post a paging job which is where the decoding work is done. Note that if an error occurred at an earlier point, this section will have been skipped. */ pageDataBufferNodeJob = ma_job_init(MA_JOB_TYPE_RESOURCE_MANAGER_PAGE_DATA_BUFFER_NODE); pageDataBufferNodeJob.order = ma_resource_manager_data_buffer_node_next_execution_order(pDataBufferNode); pageDataBufferNodeJob.data.resourceManager.pageDataBufferNode.pResourceManager = pResourceManager; pageDataBufferNodeJob.data.resourceManager.pageDataBufferNode.pDataBufferNode = pDataBufferNode; pageDataBufferNodeJob.data.resourceManager.pageDataBufferNode.pDecoder = pDecoder; pageDataBufferNodeJob.data.resourceManager.pageDataBufferNode.pDoneNotification = pJob->data.resourceManager.loadDataBufferNode.pDoneNotification; pageDataBufferNodeJob.data.resourceManager.pageDataBufferNode.pDoneFence = pJob->data.resourceManager.loadDataBufferNode.pDoneFence; /* The job has been set up so it can now be posted. */ result = ma_resource_manager_post_job(pResourceManager, &pageDataBufferNodeJob); /* When we get here, we want to make sure the result code is set to MA_BUSY. The reason for this is that the result will be copied over to the node's internal result variable. In this case, since the decoding is still in-progress, we need to make sure the result code is set to MA_BUSY. */ if (result != MA_SUCCESS) { ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_ERROR, "Failed to post MA_JOB_TYPE_RESOURCE_MANAGER_PAGE_DATA_BUFFER_NODE job. %s\n", ma_result_description(result)); ma_decoder_uninit(pDecoder); ma_free(pDecoder, &pResourceManager->config.allocationCallbacks); } else { result = MA_BUSY; } } else { /* No decoding. This is the simple case. We need only read the file content into memory and we're done. */ result = ma_resource_manager_data_buffer_node_init_supply_encoded(pResourceManager, pDataBufferNode, pJob->data.resourceManager.loadDataBufferNode.pFilePath, pJob->data.resourceManager.loadDataBufferNode.pFilePathW); } done: /* File paths are no longer needed. */ ma_free(pJob->data.resourceManager.loadDataBufferNode.pFilePath, &pResourceManager->config.allocationCallbacks); ma_free(pJob->data.resourceManager.loadDataBufferNode.pFilePathW, &pResourceManager->config.allocationCallbacks); /* We need to set the result to at the very end to ensure no other threads try reading the data before we've fully initialized the object. Other threads are going to be inspecting this variable to determine whether or not they're ready to read data. We can only change the result if it's set to MA_BUSY because otherwise we may be changing away from an error code which would be bad. An example is if the application creates a data buffer, but then immediately deletes it before we've got to this point. In this case, pDataBuffer->result will be MA_UNAVAILABLE, and setting it to MA_SUCCESS or any other error code would cause the buffer to look like it's in a state that it's not. */ c89atomic_compare_and_swap_i32(&pDataBufferNode->result, MA_BUSY, result); /* At this point initialization is complete and we can signal the notification if any. */ if (pJob->data.resourceManager.loadDataBufferNode.pInitNotification != NULL) { ma_async_notification_signal(pJob->data.resourceManager.loadDataBufferNode.pInitNotification); } if (pJob->data.resourceManager.loadDataBufferNode.pInitFence != NULL) { ma_fence_release(pJob->data.resourceManager.loadDataBufferNode.pInitFence); } /* If we have a success result it means we've fully loaded the buffer. This will happen in the non-decoding case. */ if (result != MA_BUSY) { if (pJob->data.resourceManager.loadDataBufferNode.pDoneNotification != NULL) { ma_async_notification_signal(pJob->data.resourceManager.loadDataBufferNode.pDoneNotification); } if (pJob->data.resourceManager.loadDataBufferNode.pDoneFence != NULL) { ma_fence_release(pJob->data.resourceManager.loadDataBufferNode.pDoneFence); } } /* Increment the node's execution pointer so that the next jobs can be processed. This is how we keep decoding of pages in-order. */ c89atomic_fetch_add_32(&pDataBufferNode->executionPointer, 1); return result; } static ma_result ma_job_process__resource_manager__free_data_buffer_node(ma_job* pJob) { ma_resource_manager* pResourceManager; ma_resource_manager_data_buffer_node* pDataBufferNode; MA_ASSERT(pJob != NULL); pResourceManager = (ma_resource_manager*)pJob->data.resourceManager.freeDataBufferNode.pResourceManager; MA_ASSERT(pResourceManager != NULL); pDataBufferNode = (ma_resource_manager_data_buffer_node*)pJob->data.resourceManager.freeDataBufferNode.pDataBufferNode; MA_ASSERT(pDataBufferNode != NULL); if (pJob->order != c89atomic_load_32(&pDataBufferNode->executionPointer)) { return ma_resource_manager_post_job(pResourceManager, pJob); /* Out of order. */ } ma_resource_manager_data_buffer_node_free(pResourceManager, pDataBufferNode); /* The event needs to be signalled last. */ if (pJob->data.resourceManager.freeDataBufferNode.pDoneNotification != NULL) { ma_async_notification_signal(pJob->data.resourceManager.freeDataBufferNode.pDoneNotification); } if (pJob->data.resourceManager.freeDataBufferNode.pDoneFence != NULL) { ma_fence_release(pJob->data.resourceManager.freeDataBufferNode.pDoneFence); } c89atomic_fetch_add_32(&pDataBufferNode->executionPointer, 1); return MA_SUCCESS; } static ma_result ma_job_process__resource_manager__page_data_buffer_node(ma_job* pJob) { ma_result result = MA_SUCCESS; ma_resource_manager* pResourceManager; ma_resource_manager_data_buffer_node* pDataBufferNode; MA_ASSERT(pJob != NULL); pResourceManager = (ma_resource_manager*)pJob->data.resourceManager.pageDataBufferNode.pResourceManager; MA_ASSERT(pResourceManager != NULL); pDataBufferNode = (ma_resource_manager_data_buffer_node*)pJob->data.resourceManager.pageDataBufferNode.pDataBufferNode; MA_ASSERT(pDataBufferNode != NULL); if (pJob->order != c89atomic_load_32(&pDataBufferNode->executionPointer)) { return ma_resource_manager_post_job(pResourceManager, pJob); /* Out of order. */ } /* Don't do any more decoding if the data buffer has started the uninitialization process. */ result = ma_resource_manager_data_buffer_node_result(pDataBufferNode); if (result != MA_BUSY) { goto done; } /* We're ready to decode the next page. */ result = ma_resource_manager_data_buffer_node_decode_next_page(pResourceManager, pDataBufferNode, (ma_decoder*)pJob->data.resourceManager.pageDataBufferNode.pDecoder); /* If we have a success code by this point, we want to post another job. We're going to set the result back to MA_BUSY to make it clear that there's still more to load. */ if (result == MA_SUCCESS) { ma_job newJob; newJob = *pJob; /* Everything is the same as the input job, except the execution order. */ newJob.order = ma_resource_manager_data_buffer_node_next_execution_order(pDataBufferNode); /* We need a fresh execution order. */ result = ma_resource_manager_post_job(pResourceManager, &newJob); /* Since the sound isn't yet fully decoded we want the status to be set to busy. */ if (result == MA_SUCCESS) { result = MA_BUSY; } } done: /* If there's still more to decode the result will be set to MA_BUSY. Otherwise we can free the decoder. */ if (result != MA_BUSY) { ma_decoder_uninit((ma_decoder*)pJob->data.resourceManager.pageDataBufferNode.pDecoder); ma_free(pJob->data.resourceManager.pageDataBufferNode.pDecoder, &pResourceManager->config.allocationCallbacks); } /* If we reached the end we need to treat it as successful. */ if (result == MA_AT_END) { result = MA_SUCCESS; } /* Make sure we set the result of node in case some error occurred. */ c89atomic_compare_and_swap_i32(&pDataBufferNode->result, MA_BUSY, result); /* Signal the notification after setting the result in case the notification callback wants to inspect the result code. */ if (result != MA_BUSY) { if (pJob->data.resourceManager.pageDataBufferNode.pDoneNotification != NULL) { ma_async_notification_signal(pJob->data.resourceManager.pageDataBufferNode.pDoneNotification); } if (pJob->data.resourceManager.pageDataBufferNode.pDoneFence != NULL) { ma_fence_release(pJob->data.resourceManager.pageDataBufferNode.pDoneFence); } } c89atomic_fetch_add_32(&pDataBufferNode->executionPointer, 1); return result; } static ma_result ma_job_process__resource_manager__load_data_buffer(ma_job* pJob) { ma_result result = MA_SUCCESS; ma_resource_manager* pResourceManager; ma_resource_manager_data_buffer* pDataBuffer; ma_resource_manager_data_supply_type dataSupplyType = ma_resource_manager_data_supply_type_unknown; ma_bool32 isConnectorInitialized = MA_FALSE; /* All we're doing here is checking if the node has finished loading. If not, we just re-post the job and keep waiting. Otherwise we increment the execution counter and set the buffer's result code. */ MA_ASSERT(pJob != NULL); pDataBuffer = (ma_resource_manager_data_buffer*)pJob->data.resourceManager.loadDataBuffer.pDataBuffer; MA_ASSERT(pDataBuffer != NULL); pResourceManager = pDataBuffer->pResourceManager; if (pJob->order != c89atomic_load_32(&pDataBuffer->executionPointer)) { return ma_resource_manager_post_job(pResourceManager, pJob); /* Attempting to execute out of order. Probably interleaved with a MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_BUFFER job. */ } /* First thing we need to do is check whether or not the data buffer is getting deleted. If so we just abort, but making sure we increment the execution pointer. */ result = ma_resource_manager_data_buffer_result(pDataBuffer); if (result != MA_BUSY) { goto done; /* <-- This will ensure the exucution pointer is incremented. */ } else { result = MA_SUCCESS; /* <-- Make sure this is reset. */ } /* Try initializing the connector if we haven't already. */ isConnectorInitialized = pDataBuffer->isConnectorInitialized; if (isConnectorInitialized == MA_FALSE) { dataSupplyType = ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode); if (dataSupplyType != ma_resource_manager_data_supply_type_unknown) { /* We can now initialize the connector. If this fails, we need to abort. It's very rare for this to fail. */ ma_resource_manager_data_source_config dataSourceConfig; /* For setting initial looping state and range. */ dataSourceConfig = ma_resource_manager_data_source_config_init(); dataSourceConfig.rangeBegInPCMFrames = pJob->data.resourceManager.loadDataBuffer.rangeBegInPCMFrames; dataSourceConfig.rangeEndInPCMFrames = pJob->data.resourceManager.loadDataBuffer.rangeEndInPCMFrames; dataSourceConfig.loopPointBegInPCMFrames = pJob->data.resourceManager.loadDataBuffer.loopPointBegInPCMFrames; dataSourceConfig.loopPointEndInPCMFrames = pJob->data.resourceManager.loadDataBuffer.loopPointEndInPCMFrames; dataSourceConfig.isLooping = pJob->data.resourceManager.loadDataBuffer.isLooping; result = ma_resource_manager_data_buffer_init_connector(pDataBuffer, &dataSourceConfig, pJob->data.resourceManager.loadDataBuffer.pInitNotification, pJob->data.resourceManager.loadDataBuffer.pInitFence); if (result != MA_SUCCESS) { ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_ERROR, "Failed to initialize connector for data buffer. %s.\n", ma_result_description(result)); goto done; } } else { /* Don't have a known data supply type. Most likely the data buffer node is still loading, but it could be that an error occurred. */ } } else { /* The connector is already initialized. Nothing to do here. */ } /* If the data node is still loading, we need to repost the job and *not* increment the execution pointer (i.e. we need to not fall through to the "done" label). There is a hole between here and the where the data connector is initialized where the data buffer node may have finished initializing. We need to check for this by checking the result of the data buffer node and whether or not we had an unknown data supply type at the time of trying to initialize the data connector. */ result = ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode); if (result == MA_BUSY || (result == MA_SUCCESS && isConnectorInitialized == MA_FALSE && dataSupplyType == ma_resource_manager_data_supply_type_unknown)) { return ma_resource_manager_post_job(pResourceManager, pJob); } done: /* Only move away from a busy code so that we don't trash any existing error codes. */ c89atomic_compare_and_swap_i32(&pDataBuffer->result, MA_BUSY, result); /* Only signal the other threads after the result has been set just for cleanliness sake. */ if (pJob->data.resourceManager.loadDataBuffer.pDoneNotification != NULL) { ma_async_notification_signal(pJob->data.resourceManager.loadDataBuffer.pDoneNotification); } if (pJob->data.resourceManager.loadDataBuffer.pDoneFence != NULL) { ma_fence_release(pJob->data.resourceManager.loadDataBuffer.pDoneFence); } /* If at this point the data buffer has not had it's connector initialized, it means the notification event was never signalled which means we need to signal it here. */ if (pDataBuffer->isConnectorInitialized == MA_FALSE && result != MA_SUCCESS) { if (pJob->data.resourceManager.loadDataBuffer.pInitNotification != NULL) { ma_async_notification_signal(pJob->data.resourceManager.loadDataBuffer.pInitNotification); } if (pJob->data.resourceManager.loadDataBuffer.pInitFence != NULL) { ma_fence_release(pJob->data.resourceManager.loadDataBuffer.pInitFence); } } c89atomic_fetch_add_32(&pDataBuffer->executionPointer, 1); return result; } static ma_result ma_job_process__resource_manager__free_data_buffer(ma_job* pJob) { ma_resource_manager* pResourceManager; ma_resource_manager_data_buffer* pDataBuffer; MA_ASSERT(pJob != NULL); pDataBuffer = (ma_resource_manager_data_buffer*)pJob->data.resourceManager.freeDataBuffer.pDataBuffer; MA_ASSERT(pDataBuffer != NULL); pResourceManager = pDataBuffer->pResourceManager; if (pJob->order != c89atomic_load_32(&pDataBuffer->executionPointer)) { return ma_resource_manager_post_job(pResourceManager, pJob); /* Out of order. */ } ma_resource_manager_data_buffer_uninit_internal(pDataBuffer); /* The event needs to be signalled last. */ if (pJob->data.resourceManager.freeDataBuffer.pDoneNotification != NULL) { ma_async_notification_signal(pJob->data.resourceManager.freeDataBuffer.pDoneNotification); } if (pJob->data.resourceManager.freeDataBuffer.pDoneFence != NULL) { ma_fence_release(pJob->data.resourceManager.freeDataBuffer.pDoneFence); } c89atomic_fetch_add_32(&pDataBuffer->executionPointer, 1); return MA_SUCCESS; } static ma_result ma_job_process__resource_manager__load_data_stream(ma_job* pJob) { ma_result result = MA_SUCCESS; ma_decoder_config decoderConfig; ma_uint32 pageBufferSizeInBytes; ma_resource_manager* pResourceManager; ma_resource_manager_data_stream* pDataStream; MA_ASSERT(pJob != NULL); pDataStream = (ma_resource_manager_data_stream*)pJob->data.resourceManager.loadDataStream.pDataStream; MA_ASSERT(pDataStream != NULL); pResourceManager = pDataStream->pResourceManager; if (pJob->order != c89atomic_load_32(&pDataStream->executionPointer)) { return ma_resource_manager_post_job(pResourceManager, pJob); /* Out of order. */ } if (ma_resource_manager_data_stream_result(pDataStream) != MA_BUSY) { result = MA_INVALID_OPERATION; /* Most likely the data stream is being uninitialized. */ goto done; } /* We need to initialize the decoder first so we can determine the size of the pages. */ decoderConfig = ma_resource_manager__init_decoder_config(pResourceManager); if (pJob->data.resourceManager.loadDataStream.pFilePath != NULL) { result = ma_decoder_init_vfs(pResourceManager->config.pVFS, pJob->data.resourceManager.loadDataStream.pFilePath, &decoderConfig, &pDataStream->decoder); } else { result = ma_decoder_init_vfs_w(pResourceManager->config.pVFS, pJob->data.resourceManager.loadDataStream.pFilePathW, &decoderConfig, &pDataStream->decoder); } if (result != MA_SUCCESS) { goto done; } /* Retrieve the total length of the file before marking the decoder are loaded. */ if ((pDataStream->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_UNKNOWN_LENGTH) == 0) { result = ma_decoder_get_length_in_pcm_frames(&pDataStream->decoder, &pDataStream->totalLengthInPCMFrames); if (result != MA_SUCCESS) { goto done; /* Failed to retrieve the length. */ } } else { pDataStream->totalLengthInPCMFrames = 0; } /* Only mark the decoder as initialized when the length of the decoder has been retrieved because that can possibly require a scan over the whole file and we don't want to have another thread trying to access the decoder while it's scanning. */ pDataStream->isDecoderInitialized = MA_TRUE; /* We have the decoder so we can now initialize our page buffer. */ pageBufferSizeInBytes = ma_resource_manager_data_stream_get_page_size_in_frames(pDataStream) * 2 * ma_get_bytes_per_frame(pDataStream->decoder.outputFormat, pDataStream->decoder.outputChannels); pDataStream->pPageData = ma_malloc(pageBufferSizeInBytes, &pResourceManager->config.allocationCallbacks); if (pDataStream->pPageData == NULL) { ma_decoder_uninit(&pDataStream->decoder); result = MA_OUT_OF_MEMORY; goto done; } /* Seek to our initial seek point before filling the initial pages. */ ma_decoder_seek_to_pcm_frame(&pDataStream->decoder, pJob->data.resourceManager.loadDataStream.initialSeekPoint); /* We have our decoder and our page buffer, so now we need to fill our pages. */ ma_resource_manager_data_stream_fill_pages(pDataStream); /* And now we're done. We want to make sure the result is MA_SUCCESS. */ result = MA_SUCCESS; done: ma_free(pJob->data.resourceManager.loadDataStream.pFilePath, &pResourceManager->config.allocationCallbacks); ma_free(pJob->data.resourceManager.loadDataStream.pFilePathW, &pResourceManager->config.allocationCallbacks); /* We can only change the status away from MA_BUSY. If it's set to anything else it means an error has occurred somewhere or the uninitialization process has started (most likely). */ c89atomic_compare_and_swap_i32(&pDataStream->result, MA_BUSY, result); /* Only signal the other threads after the result has been set just for cleanliness sake. */ if (pJob->data.resourceManager.loadDataStream.pInitNotification != NULL) { ma_async_notification_signal(pJob->data.resourceManager.loadDataStream.pInitNotification); } if (pJob->data.resourceManager.loadDataStream.pInitFence != NULL) { ma_fence_release(pJob->data.resourceManager.loadDataStream.pInitFence); } c89atomic_fetch_add_32(&pDataStream->executionPointer, 1); return result; } static ma_result ma_job_process__resource_manager__free_data_stream(ma_job* pJob) { ma_resource_manager* pResourceManager; ma_resource_manager_data_stream* pDataStream; MA_ASSERT(pJob != NULL); pDataStream = (ma_resource_manager_data_stream*)pJob->data.resourceManager.freeDataStream.pDataStream; MA_ASSERT(pDataStream != NULL); pResourceManager = pDataStream->pResourceManager; if (pJob->order != c89atomic_load_32(&pDataStream->executionPointer)) { return ma_resource_manager_post_job(pResourceManager, pJob); /* Out of order. */ } /* If our status is not MA_UNAVAILABLE we have a bug somewhere. */ MA_ASSERT(ma_resource_manager_data_stream_result(pDataStream) == MA_UNAVAILABLE); if (pDataStream->isDecoderInitialized) { ma_decoder_uninit(&pDataStream->decoder); } if (pDataStream->pPageData != NULL) { ma_free(pDataStream->pPageData, &pResourceManager->config.allocationCallbacks); pDataStream->pPageData = NULL; /* Just in case... */ } ma_data_source_uninit(&pDataStream->ds); /* The event needs to be signalled last. */ if (pJob->data.resourceManager.freeDataStream.pDoneNotification != NULL) { ma_async_notification_signal(pJob->data.resourceManager.freeDataStream.pDoneNotification); } if (pJob->data.resourceManager.freeDataStream.pDoneFence != NULL) { ma_fence_release(pJob->data.resourceManager.freeDataStream.pDoneFence); } /*c89atomic_fetch_add_32(&pDataStream->executionPointer, 1);*/ return MA_SUCCESS; } static ma_result ma_job_process__resource_manager__page_data_stream(ma_job* pJob) { ma_result result = MA_SUCCESS; ma_resource_manager* pResourceManager; ma_resource_manager_data_stream* pDataStream; MA_ASSERT(pJob != NULL); pDataStream = (ma_resource_manager_data_stream*)pJob->data.resourceManager.pageDataStream.pDataStream; MA_ASSERT(pDataStream != NULL); pResourceManager = pDataStream->pResourceManager; if (pJob->order != c89atomic_load_32(&pDataStream->executionPointer)) { return ma_resource_manager_post_job(pResourceManager, pJob); /* Out of order. */ } /* For streams, the status should be MA_SUCCESS. */ if (ma_resource_manager_data_stream_result(pDataStream) != MA_SUCCESS) { result = MA_INVALID_OPERATION; goto done; } ma_resource_manager_data_stream_fill_page(pDataStream, pJob->data.resourceManager.pageDataStream.pageIndex); done: c89atomic_fetch_add_32(&pDataStream->executionPointer, 1); return result; } static ma_result ma_job_process__resource_manager__seek_data_stream(ma_job* pJob) { ma_result result = MA_SUCCESS; ma_resource_manager* pResourceManager; ma_resource_manager_data_stream* pDataStream; MA_ASSERT(pJob != NULL); pDataStream = (ma_resource_manager_data_stream*)pJob->data.resourceManager.seekDataStream.pDataStream; MA_ASSERT(pDataStream != NULL); pResourceManager = pDataStream->pResourceManager; if (pJob->order != c89atomic_load_32(&pDataStream->executionPointer)) { return ma_resource_manager_post_job(pResourceManager, pJob); /* Out of order. */ } /* For streams the status should be MA_SUCCESS for this to do anything. */ if (ma_resource_manager_data_stream_result(pDataStream) != MA_SUCCESS || pDataStream->isDecoderInitialized == MA_FALSE) { result = MA_INVALID_OPERATION; goto done; } /* With seeking we just assume both pages are invalid and the relative frame cursor at position 0. This is basically exactly the same as loading, except instead of initializing the decoder, we seek to a frame. */ ma_decoder_seek_to_pcm_frame(&pDataStream->decoder, pJob->data.resourceManager.seekDataStream.frameIndex); /* After seeking we'll need to reload the pages. */ ma_resource_manager_data_stream_fill_pages(pDataStream); /* We need to let the public API know that we're done seeking. */ c89atomic_fetch_sub_32(&pDataStream->seekCounter, 1); done: c89atomic_fetch_add_32(&pDataStream->executionPointer, 1); return result; } MA_API ma_result ma_resource_manager_process_job(ma_resource_manager* pResourceManager, ma_job* pJob) { if (pResourceManager == NULL || pJob == NULL) { return MA_INVALID_ARGS; } return ma_job_process(pJob); } MA_API ma_result ma_resource_manager_process_next_job(ma_resource_manager* pResourceManager) { ma_result result; ma_job job; if (pResourceManager == NULL) { return MA_INVALID_ARGS; } /* This will return MA_CANCELLED if the next job is a quit job. */ result = ma_resource_manager_next_job(pResourceManager, &job); if (result != MA_SUCCESS) { return result; } return ma_job_process(&job); } #else /* We'll get here if the resource manager is being excluded from the build. We need to define the job processing callbacks as no-ops. */ static ma_result ma_job_process__resource_manager__load_data_buffer_node(ma_job* pJob) { return ma_job_process__noop(pJob); } static ma_result ma_job_process__resource_manager__free_data_buffer_node(ma_job* pJob) { return ma_job_process__noop(pJob); } static ma_result ma_job_process__resource_manager__page_data_buffer_node(ma_job* pJob) { return ma_job_process__noop(pJob); } static ma_result ma_job_process__resource_manager__load_data_buffer(ma_job* pJob) { return ma_job_process__noop(pJob); } static ma_result ma_job_process__resource_manager__free_data_buffer(ma_job* pJob) { return ma_job_process__noop(pJob); } static ma_result ma_job_process__resource_manager__load_data_stream(ma_job* pJob) { return ma_job_process__noop(pJob); } static ma_result ma_job_process__resource_manager__free_data_stream(ma_job* pJob) { return ma_job_process__noop(pJob); } static ma_result ma_job_process__resource_manager__page_data_stream(ma_job* pJob) { return ma_job_process__noop(pJob); } static ma_result ma_job_process__resource_manager__seek_data_stream(ma_job* pJob) { return ma_job_process__noop(pJob); } #endif /* MA_NO_RESOURCE_MANAGER */ #ifndef MA_NO_NODE_GRAPH /* 10ms @ 48K = 480. Must never exceed 65535. */ #ifndef MA_DEFAULT_NODE_CACHE_CAP_IN_FRAMES_PER_BUS #define MA_DEFAULT_NODE_CACHE_CAP_IN_FRAMES_PER_BUS 480 #endif static ma_result ma_node_read_pcm_frames(ma_node* pNode, ma_uint32 outputBusIndex, float* pFramesOut, ma_uint32 frameCount, ma_uint32* pFramesRead, ma_uint64 globalTime); MA_API void ma_debug_fill_pcm_frames_with_sine_wave(float* pFramesOut, ma_uint32 frameCount, ma_format format, ma_uint32 channels, ma_uint32 sampleRate) { #ifndef MA_NO_GENERATION { ma_waveform_config waveformConfig; ma_waveform waveform; waveformConfig = ma_waveform_config_init(format, channels, sampleRate, ma_waveform_type_sine, 1.0, 400); ma_waveform_init(&waveformConfig, &waveform); ma_waveform_read_pcm_frames(&waveform, pFramesOut, frameCount, NULL); } #else { (void)pFramesOut; (void)frameCount; (void)format; (void)channels; (void)sampleRate; #if defined(MA_DEBUG_OUTPUT) { #if _MSC_VER #pragma message ("ma_debug_fill_pcm_frames_with_sine_wave() will do nothing because MA_NO_GENERATION is enabled.") #endif } #endif } #endif } static ma_result ma_mix_pcm_frames_f32(float* pDst, const float* pSrc, ma_uint64 frameCount, ma_uint32 channels, float volume) { ma_uint64 iSample; ma_uint64 sampleCount; if (pDst == NULL || pSrc == NULL || channels == 0) { return MA_INVALID_ARGS; } if (volume == 0) { return MA_SUCCESS; /* No changes if the volume is 0. */ } sampleCount = frameCount * channels; if (volume == 1) { for (iSample = 0; iSample < sampleCount; iSample += 1) { pDst[iSample] += pSrc[iSample]; } } else { for (iSample = 0; iSample < sampleCount; iSample += 1) { pDst[iSample] += ma_apply_volume_unclipped_f32(pSrc[iSample], volume); } } return MA_SUCCESS; } MA_API ma_node_graph_config ma_node_graph_config_init(ma_uint32 channels) { ma_node_graph_config config; MA_ZERO_OBJECT(&config); config.channels = channels; config.nodeCacheCapInFrames = MA_DEFAULT_NODE_CACHE_CAP_IN_FRAMES_PER_BUS; return config; } static void ma_node_graph_set_is_reading(ma_node_graph* pNodeGraph, ma_bool32 isReading) { MA_ASSERT(pNodeGraph != NULL); c89atomic_exchange_32(&pNodeGraph->isReading, isReading); } #if 0 static ma_bool32 ma_node_graph_is_reading(ma_node_graph* pNodeGraph) { MA_ASSERT(pNodeGraph != NULL); return c89atomic_load_32(&pNodeGraph->isReading); } #endif static void ma_node_graph_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) { ma_node_graph* pNodeGraph = (ma_node_graph*)pNode; ma_uint64 framesRead; ma_node_graph_read_pcm_frames(pNodeGraph, ppFramesOut[0], *pFrameCountOut, &framesRead); *pFrameCountOut = (ma_uint32)framesRead; /* Safe cast. */ (void)ppFramesIn; (void)pFrameCountIn; } static ma_node_vtable g_node_graph_node_vtable = { ma_node_graph_node_process_pcm_frames, NULL, /* onGetRequiredInputFrameCount */ 0, /* 0 input buses. */ 1, /* 1 output bus. */ 0 /* Flags. */ }; static void ma_node_graph_endpoint_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) { MA_ASSERT(pNode != NULL); MA_ASSERT(ma_node_get_input_bus_count(pNode) == 1); MA_ASSERT(ma_node_get_output_bus_count(pNode) == 1); /* Input channel count needs to be the same as the output channel count. */ MA_ASSERT(ma_node_get_input_channels(pNode, 0) == ma_node_get_output_channels(pNode, 0)); /* We don't need to do anything here because it's a passthrough. */ (void)pNode; (void)ppFramesIn; (void)pFrameCountIn; (void)ppFramesOut; (void)pFrameCountOut; #if 0 /* The data has already been mixed. We just need to move it to the output buffer. */ if (ppFramesIn != NULL) { ma_copy_pcm_frames(ppFramesOut[0], ppFramesIn[0], *pFrameCountOut, ma_format_f32, ma_node_get_output_channels(pNode, 0)); } #endif } static ma_node_vtable g_node_graph_endpoint_vtable = { ma_node_graph_endpoint_process_pcm_frames, NULL, /* onGetRequiredInputFrameCount */ 1, /* 1 input bus. */ 1, /* 1 output bus. */ MA_NODE_FLAG_PASSTHROUGH /* Flags. The endpoint is a passthrough. */ }; MA_API ma_result ma_node_graph_init(const ma_node_graph_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_node_graph* pNodeGraph) { ma_result result; ma_node_config baseConfig; ma_node_config endpointConfig; if (pNodeGraph == NULL) { return MA_INVALID_ARGS; } MA_ZERO_OBJECT(pNodeGraph); pNodeGraph->nodeCacheCapInFrames = pConfig->nodeCacheCapInFrames; if (pNodeGraph->nodeCacheCapInFrames == 0) { pNodeGraph->nodeCacheCapInFrames = MA_DEFAULT_NODE_CACHE_CAP_IN_FRAMES_PER_BUS; } /* Base node so we can use the node graph as a node into another graph. */ baseConfig = ma_node_config_init(); baseConfig.vtable = &g_node_graph_node_vtable; baseConfig.pOutputChannels = &pConfig->channels; result = ma_node_init(pNodeGraph, &baseConfig, pAllocationCallbacks, &pNodeGraph->base); if (result != MA_SUCCESS) { return result; } /* Endpoint. */ endpointConfig = ma_node_config_init(); endpointConfig.vtable = &g_node_graph_endpoint_vtable; endpointConfig.pInputChannels = &pConfig->channels; endpointConfig.pOutputChannels = &pConfig->channels; result = ma_node_init(pNodeGraph, &endpointConfig, pAllocationCallbacks, &pNodeGraph->endpoint); if (result != MA_SUCCESS) { ma_node_uninit(&pNodeGraph->base, pAllocationCallbacks); return result; } return MA_SUCCESS; } MA_API void ma_node_graph_uninit(ma_node_graph* pNodeGraph, const ma_allocation_callbacks* pAllocationCallbacks) { if (pNodeGraph == NULL) { return; } ma_node_uninit(&pNodeGraph->endpoint, pAllocationCallbacks); } MA_API ma_node* ma_node_graph_get_endpoint(ma_node_graph* pNodeGraph) { if (pNodeGraph == NULL) { return NULL; } return &pNodeGraph->endpoint; } MA_API ma_result ma_node_graph_read_pcm_frames(ma_node_graph* pNodeGraph, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) { ma_result result = MA_SUCCESS; ma_uint64 totalFramesRead; ma_uint32 channels; if (pFramesRead != NULL) { *pFramesRead = 0; /* Safety. */ } if (pNodeGraph == NULL) { return MA_INVALID_ARGS; } channels = ma_node_get_output_channels(&pNodeGraph->endpoint, 0); /* We'll be nice and try to do a full read of all frameCount frames. */ totalFramesRead = 0; while (totalFramesRead < frameCount) { ma_uint32 framesJustRead; ma_uint64 framesToRead = frameCount - totalFramesRead; if (framesToRead > 0xFFFFFFFF) { framesToRead = 0xFFFFFFFF; } ma_node_graph_set_is_reading(pNodeGraph, MA_TRUE); { result = ma_node_read_pcm_frames(&pNodeGraph->endpoint, 0, (float*)ma_offset_pcm_frames_ptr(pFramesOut, totalFramesRead, ma_format_f32, channels), (ma_uint32)framesToRead, &framesJustRead, ma_node_get_time(&pNodeGraph->endpoint)); } ma_node_graph_set_is_reading(pNodeGraph, MA_FALSE); totalFramesRead += framesJustRead; if (result != MA_SUCCESS) { break; } /* Abort if we weren't able to read any frames or else we risk getting stuck in a loop. */ if (framesJustRead == 0) { break; } } /* Let's go ahead and silence any leftover frames just for some added safety to ensure the caller doesn't try emitting garbage out of the speakers. */ if (totalFramesRead < frameCount) { ma_silence_pcm_frames(ma_offset_pcm_frames_ptr(pFramesOut, totalFramesRead, ma_format_f32, channels), (frameCount - totalFramesRead), ma_format_f32, channels); } if (pFramesRead != NULL) { *pFramesRead = totalFramesRead; } return result; } MA_API ma_uint32 ma_node_graph_get_channels(const ma_node_graph* pNodeGraph) { if (pNodeGraph == NULL) { return 0; } return ma_node_get_output_channels(&pNodeGraph->endpoint, 0); } MA_API ma_uint64 ma_node_graph_get_time(const ma_node_graph* pNodeGraph) { if (pNodeGraph == NULL) { return 0; } return ma_node_get_time(&pNodeGraph->endpoint); /* Global time is just the local time of the endpoint. */ } MA_API ma_result ma_node_graph_set_time(ma_node_graph* pNodeGraph, ma_uint64 globalTime) { if (pNodeGraph == NULL) { return MA_INVALID_ARGS; } return ma_node_set_time(&pNodeGraph->endpoint, globalTime); /* Global time is just the local time of the endpoint. */ } #define MA_NODE_OUTPUT_BUS_FLAG_HAS_READ 0x01 /* Whether or not this bus ready to read more data. Only used on nodes with multiple output buses. */ static ma_result ma_node_output_bus_init(ma_node* pNode, ma_uint32 outputBusIndex, ma_uint32 channels, ma_node_output_bus* pOutputBus) { MA_ASSERT(pOutputBus != NULL); MA_ASSERT(outputBusIndex < MA_MAX_NODE_BUS_COUNT); MA_ASSERT(outputBusIndex < ma_node_get_output_bus_count(pNode)); MA_ASSERT(channels < 256); MA_ZERO_OBJECT(pOutputBus); if (channels == 0) { return MA_INVALID_ARGS; } pOutputBus->pNode = pNode; pOutputBus->outputBusIndex = (ma_uint8)outputBusIndex; pOutputBus->channels = (ma_uint8)channels; pOutputBus->flags = MA_NODE_OUTPUT_BUS_FLAG_HAS_READ; /* <-- Important that this flag is set by default. */ pOutputBus->volume = 1; return MA_SUCCESS; } static void ma_node_output_bus_lock(ma_node_output_bus* pOutputBus) { ma_spinlock_lock(&pOutputBus->lock); } static void ma_node_output_bus_unlock(ma_node_output_bus* pOutputBus) { ma_spinlock_unlock(&pOutputBus->lock); } static ma_uint32 ma_node_output_bus_get_channels(const ma_node_output_bus* pOutputBus) { return pOutputBus->channels; } static void ma_node_output_bus_set_has_read(ma_node_output_bus* pOutputBus, ma_bool32 hasRead) { if (hasRead) { c89atomic_fetch_or_32(&pOutputBus->flags, MA_NODE_OUTPUT_BUS_FLAG_HAS_READ); } else { c89atomic_fetch_and_32(&pOutputBus->flags, (ma_uint32)~MA_NODE_OUTPUT_BUS_FLAG_HAS_READ); } } static ma_bool32 ma_node_output_bus_has_read(ma_node_output_bus* pOutputBus) { return (c89atomic_load_32(&pOutputBus->flags) & MA_NODE_OUTPUT_BUS_FLAG_HAS_READ) != 0; } static void ma_node_output_bus_set_is_attached(ma_node_output_bus* pOutputBus, ma_bool32 isAttached) { c89atomic_exchange_32(&pOutputBus->isAttached, isAttached); } static ma_bool32 ma_node_output_bus_is_attached(ma_node_output_bus* pOutputBus) { return c89atomic_load_32(&pOutputBus->isAttached); } static ma_result ma_node_output_bus_set_volume(ma_node_output_bus* pOutputBus, float volume) { MA_ASSERT(pOutputBus != NULL); if (volume < 0.0f) { volume = 0.0f; } c89atomic_exchange_f32(&pOutputBus->volume, volume); return MA_SUCCESS; } static float ma_node_output_bus_get_volume(const ma_node_output_bus* pOutputBus) { return c89atomic_load_f32((float*)&pOutputBus->volume); } static ma_result ma_node_input_bus_init(ma_uint32 channels, ma_node_input_bus* pInputBus) { MA_ASSERT(pInputBus != NULL); MA_ASSERT(channels < 256); MA_ZERO_OBJECT(pInputBus); if (channels == 0) { return MA_INVALID_ARGS; } pInputBus->channels = (ma_uint8)channels; return MA_SUCCESS; } static void ma_node_input_bus_lock(ma_node_input_bus* pInputBus) { MA_ASSERT(pInputBus != NULL); ma_spinlock_lock(&pInputBus->lock); } static void ma_node_input_bus_unlock(ma_node_input_bus* pInputBus) { MA_ASSERT(pInputBus != NULL); ma_spinlock_unlock(&pInputBus->lock); } static void ma_node_input_bus_next_begin(ma_node_input_bus* pInputBus) { c89atomic_fetch_add_32(&pInputBus->nextCounter, 1); } static void ma_node_input_bus_next_end(ma_node_input_bus* pInputBus) { c89atomic_fetch_sub_32(&pInputBus->nextCounter, 1); } static ma_uint32 ma_node_input_bus_get_next_counter(ma_node_input_bus* pInputBus) { return c89atomic_load_32(&pInputBus->nextCounter); } static ma_uint32 ma_node_input_bus_get_channels(const ma_node_input_bus* pInputBus) { return pInputBus->channels; } static void ma_node_input_bus_detach__no_output_bus_lock(ma_node_input_bus* pInputBus, ma_node_output_bus* pOutputBus) { MA_ASSERT(pInputBus != NULL); MA_ASSERT(pOutputBus != NULL); /* Mark the output bus as detached first. This will prevent future iterations on the audio thread from iterating this output bus. */ ma_node_output_bus_set_is_attached(pOutputBus, MA_FALSE); /* We cannot use the output bus lock here since it'll be getting used at a higher level, but we do still need to use the input bus lock since we'll be updating pointers on two different output buses. The same rules apply here as the attaching case. Although we're using a lock here, we're *not* using a lock when iterating over the list in the audio thread. We therefore need to craft this in a way such that the iteration on the audio thread doesn't break. The the first thing to do is swap out the "next" pointer of the previous output bus with the new "next" output bus. This is the operation that matters for iteration on the audio thread. After that, the previous pointer on the new "next" pointer needs to be updated, after which point the linked list will be in a good state. */ ma_node_input_bus_lock(pInputBus); { ma_node_output_bus* pOldPrev = (ma_node_output_bus*)c89atomic_load_ptr(&pOutputBus->pPrev); ma_node_output_bus* pOldNext = (ma_node_output_bus*)c89atomic_load_ptr(&pOutputBus->pNext); if (pOldPrev != NULL) { c89atomic_exchange_ptr(&pOldPrev->pNext, pOldNext); /* <-- This is where the output bus is detached from the list. */ } if (pOldNext != NULL) { c89atomic_exchange_ptr(&pOldNext->pPrev, pOldPrev); /* <-- This is required for detachment. */ } } ma_node_input_bus_unlock(pInputBus); /* At this point the output bus is detached and the linked list is completely unaware of it. Reset some data for safety. */ c89atomic_exchange_ptr(&pOutputBus->pNext, NULL); /* Using atomic exchanges here, mainly for the benefit of analysis tools which don't always recognize spinlocks. */ c89atomic_exchange_ptr(&pOutputBus->pPrev, NULL); /* As above. */ pOutputBus->pInputNode = NULL; pOutputBus->inputNodeInputBusIndex = 0; /* For thread-safety reasons, we don't want to be returning from this straight away. We need to wait for the audio thread to finish with the output bus. There's two things we need to wait for. The first is the part that selects the next output bus in the list, and the other is the part that reads from the output bus. Basically all we're doing is waiting for the input bus to stop referencing the output bus. We're doing this part last because we want the section above to run while the audio thread is finishing up with the output bus, just for efficiency reasons. We marked the output bus as detached right at the top of this function which is going to prevent the audio thread from iterating the output bus again. */ /* Part 1: Wait for the current iteration to complete. */ while (ma_node_input_bus_get_next_counter(pInputBus) > 0) { ma_yield(); } /* Part 2: Wait for any reads to complete. */ while (c89atomic_load_32(&pOutputBus->refCount) > 0) { ma_yield(); } /* At this point we're done detaching and we can be guaranteed that the audio thread is not going to attempt to reference this output bus again (until attached again). */ } #if 0 /* Not used at the moment, but leaving here in case I need it later. */ static void ma_node_input_bus_detach(ma_node_input_bus* pInputBus, ma_node_output_bus* pOutputBus) { MA_ASSERT(pInputBus != NULL); MA_ASSERT(pOutputBus != NULL); ma_node_output_bus_lock(pOutputBus); { ma_node_input_bus_detach__no_output_bus_lock(pInputBus, pOutputBus); } ma_node_output_bus_unlock(pOutputBus); } #endif static void ma_node_input_bus_attach(ma_node_input_bus* pInputBus, ma_node_output_bus* pOutputBus, ma_node* pNewInputNode, ma_uint32 inputNodeInputBusIndex) { MA_ASSERT(pInputBus != NULL); MA_ASSERT(pOutputBus != NULL); ma_node_output_bus_lock(pOutputBus); { ma_node_output_bus* pOldInputNode = (ma_node_output_bus*)c89atomic_load_ptr(&pOutputBus->pInputNode); /* Detach from any existing attachment first if necessary. */ if (pOldInputNode != NULL) { ma_node_input_bus_detach__no_output_bus_lock(pInputBus, pOutputBus); } /* At this point we can be sure the output bus is not attached to anything. The linked list in the old input bus has been updated so that pOutputBus will not get iterated again. */ pOutputBus->pInputNode = pNewInputNode; /* No need for an atomic assignment here because modification of this variable always happens within a lock. */ pOutputBus->inputNodeInputBusIndex = (ma_uint8)inputNodeInputBusIndex; /* As above. */ /* Now we need to attach the output bus to the linked list. This involves updating two pointers on two different output buses so I'm going to go ahead and keep this simple and just use a lock. There are ways to do this without a lock, but it's just too hard to maintain for it's value. Although we're locking here, it's important to remember that we're *not* locking when iterating and reading audio data since that'll be running on the audio thread. As a result we need to be careful how we craft this so that we don't break iteration. What we're going to do is always attach the new item so that it becomes the first item in the list. That way, as we're iterating we won't break any links in the list and iteration will continue safely. The detaching case will also be crafted in a way as to not break list iteration. It's important to remember to use atomic exchanges here since no locking is happening on the audio thread during iteration. */ ma_node_input_bus_lock(pInputBus); { ma_node_output_bus* pNewPrev = &pInputBus->head; ma_node_output_bus* pNewNext = (ma_node_output_bus*)c89atomic_load_ptr(&pInputBus->head.pNext); /* Update the local output bus. */ c89atomic_exchange_ptr(&pOutputBus->pPrev, pNewPrev); c89atomic_exchange_ptr(&pOutputBus->pNext, pNewNext); /* Update the other output buses to point back to the local output bus. */ c89atomic_exchange_ptr(&pInputBus->head.pNext, pOutputBus); /* <-- This is where the output bus is actually attached to the input bus. */ /* Do the previous pointer last. This is only used for detachment. */ if (pNewNext != NULL) { c89atomic_exchange_ptr(&pNewNext->pPrev, pOutputBus); } } ma_node_input_bus_unlock(pInputBus); /* Mark the node as attached last. This is used to controlling whether or the output bus will be iterated on the audio thread. Mainly required for detachment purposes. */ ma_node_output_bus_set_is_attached(pOutputBus, MA_TRUE); } ma_node_output_bus_unlock(pOutputBus); } static ma_node_output_bus* ma_node_input_bus_next(ma_node_input_bus* pInputBus, ma_node_output_bus* pOutputBus) { ma_node_output_bus* pNext; MA_ASSERT(pInputBus != NULL); if (pOutputBus == NULL) { return NULL; } ma_node_input_bus_next_begin(pInputBus); { pNext = pOutputBus; for (;;) { pNext = (ma_node_output_bus*)c89atomic_load_ptr(&pNext->pNext); if (pNext == NULL) { break; /* Reached the end. */ } if (ma_node_output_bus_is_attached(pNext) == MA_FALSE) { continue; /* The node is not attached. Keep checking. */ } /* The next node has been selected. */ break; } /* We need to increment the reference count of the selected node. */ if (pNext != NULL) { c89atomic_fetch_add_32(&pNext->refCount, 1); } /* The previous node is no longer being referenced. */ c89atomic_fetch_sub_32(&pOutputBus->refCount, 1); } ma_node_input_bus_next_end(pInputBus); return pNext; } static ma_node_output_bus* ma_node_input_bus_first(ma_node_input_bus* pInputBus) { return ma_node_input_bus_next(pInputBus, &pInputBus->head); } static ma_result ma_node_input_bus_read_pcm_frames(ma_node* pInputNode, ma_node_input_bus* pInputBus, float* pFramesOut, ma_uint32 frameCount, ma_uint32* pFramesRead, ma_uint64 globalTime) { ma_result result = MA_SUCCESS; ma_node_output_bus* pOutputBus; ma_node_output_bus* pFirst; ma_uint32 inputChannels; ma_bool32 doesOutputBufferHaveContent = MA_FALSE; /* This will be called from the audio thread which means we can't be doing any locking. Basically, this function will not perfom any locking, whereas attaching and detaching will, but crafted in such a way that we don't need to perform any locking here. The important thing to remember is to always iterate in a forward direction. In order to process any data we need to first read from all input buses. That's where this function comes in. This iterates over each of the attachments and accumulates/mixes them. We also convert the channels to the nodes output channel count before mixing. We want to do this channel conversion so that the caller of this function can invoke the processing callback without having to do it themselves. When we iterate over each of the attachments on the input bus, we need to read as much data as we can from each of them so that we don't end up with holes between each of the attachments. To do this, we need to read from each attachment in a loop and read as many frames as we can, up to `frameCount`. */ MA_ASSERT(pInputNode != NULL); MA_ASSERT(pFramesRead != NULL); /* pFramesRead is critical and must always be specified. On input it's undefined and on output it'll be set to the number of frames actually read. */ *pFramesRead = 0; /* Safety. */ inputChannels = ma_node_input_bus_get_channels(pInputBus); /* We need to be careful with how we call ma_node_input_bus_first() and ma_node_input_bus_next(). They are both critical to our lock-free thread-safety system. We can only call ma_node_input_bus_first() once per iteration, however we have an optimization to checks whether or not it's the first item in the list. We therefore need to store a pointer to the first item rather than repeatedly calling ma_node_input_bus_first(). It's safe to keep hold of this pointer, so long as we don't dereference it after calling ma_node_input_bus_next(), which we won't be. */ pFirst = ma_node_input_bus_first(pInputBus); if (pFirst == NULL) { return MA_SUCCESS; /* No attachments. Read nothing. */ } for (pOutputBus = pFirst; pOutputBus != NULL; pOutputBus = ma_node_input_bus_next(pInputBus, pOutputBus)) { ma_uint32 framesProcessed = 0; ma_bool32 isSilentOutput = MA_FALSE; MA_ASSERT(pOutputBus->pNode != NULL); isSilentOutput = (((ma_node_base*)pOutputBus->pNode)->vtable->flags & MA_NODE_FLAG_SILENT_OUTPUT) != 0; if (pFramesOut != NULL) { /* Read. */ float temp[MA_DATA_CONVERTER_STACK_BUFFER_SIZE / sizeof(float)]; ma_uint32 tempCapInFrames = ma_countof(temp) / inputChannels; while (framesProcessed < frameCount) { float* pRunningFramesOut; ma_uint32 framesToRead; ma_uint32 framesJustRead; framesToRead = frameCount - framesProcessed; if (framesToRead > tempCapInFrames) { framesToRead = tempCapInFrames; } pRunningFramesOut = ma_offset_pcm_frames_ptr_f32(pFramesOut, framesProcessed, inputChannels); if (doesOutputBufferHaveContent == MA_FALSE) { /* Fast path. First attachment. We just read straight into the output buffer (no mixing required). */ result = ma_node_read_pcm_frames(pOutputBus->pNode, pOutputBus->outputBusIndex, pRunningFramesOut, framesToRead, &framesJustRead, globalTime + framesProcessed); } else { /* Slow path. Not the first attachment. Mixing required. */ result = ma_node_read_pcm_frames(pOutputBus->pNode, pOutputBus->outputBusIndex, temp, framesToRead, &framesJustRead, globalTime + framesProcessed); if (result == MA_SUCCESS || result == MA_AT_END) { if (isSilentOutput == MA_FALSE) { /* Don't mix if the node outputs silence. */ ma_mix_pcm_frames_f32(pRunningFramesOut, temp, framesJustRead, inputChannels, /*volume*/1); } } } framesProcessed += framesJustRead; /* If we reached the end or otherwise failed to read any data we need to finish up with this output node. */ if (result != MA_SUCCESS) { break; } /* If we didn't read anything, abort so we don't get stuck in a loop. */ if (framesJustRead == 0) { break; } } /* If it's the first attachment we didn't do any mixing. Any leftover samples need to be silenced. */ if (pOutputBus == pFirst && framesProcessed < frameCount) { ma_silence_pcm_frames(ma_offset_pcm_frames_ptr(pFramesOut, framesProcessed, ma_format_f32, inputChannels), (frameCount - framesProcessed), ma_format_f32, inputChannels); } if (isSilentOutput == MA_FALSE) { doesOutputBufferHaveContent = MA_TRUE; } } else { /* Seek. */ ma_node_read_pcm_frames(pOutputBus->pNode, pOutputBus->outputBusIndex, NULL, frameCount, &framesProcessed, globalTime); } } /* If we didn't output anything, output silence. */ if (doesOutputBufferHaveContent == MA_FALSE && pFramesOut != NULL) { ma_silence_pcm_frames(pFramesOut, frameCount, ma_format_f32, inputChannels); } /* In this path we always "process" the entire amount. */ *pFramesRead = frameCount; return result; } MA_API ma_node_config ma_node_config_init(void) { ma_node_config config; MA_ZERO_OBJECT(&config); config.initialState = ma_node_state_started; /* Nodes are started by default. */ config.inputBusCount = MA_NODE_BUS_COUNT_UNKNOWN; config.outputBusCount = MA_NODE_BUS_COUNT_UNKNOWN; return config; } static ma_result ma_node_detach_full(ma_node* pNode); static float* ma_node_get_cached_input_ptr(ma_node* pNode, ma_uint32 inputBusIndex) { ma_node_base* pNodeBase = (ma_node_base*)pNode; ma_uint32 iInputBus; float* pBasePtr; MA_ASSERT(pNodeBase != NULL); /* Input data is stored at the front of the buffer. */ pBasePtr = pNodeBase->pCachedData; for (iInputBus = 0; iInputBus < inputBusIndex; iInputBus += 1) { pBasePtr += pNodeBase->cachedDataCapInFramesPerBus * ma_node_input_bus_get_channels(&pNodeBase->pInputBuses[iInputBus]); } return pBasePtr; } static float* ma_node_get_cached_output_ptr(ma_node* pNode, ma_uint32 outputBusIndex) { ma_node_base* pNodeBase = (ma_node_base*)pNode; ma_uint32 iInputBus; ma_uint32 iOutputBus; float* pBasePtr; MA_ASSERT(pNodeBase != NULL); /* Cached output data starts after the input data. */ pBasePtr = pNodeBase->pCachedData; for (iInputBus = 0; iInputBus < ma_node_get_input_bus_count(pNodeBase); iInputBus += 1) { pBasePtr += pNodeBase->cachedDataCapInFramesPerBus * ma_node_input_bus_get_channels(&pNodeBase->pInputBuses[iInputBus]); } for (iOutputBus = 0; iOutputBus < outputBusIndex; iOutputBus += 1) { pBasePtr += pNodeBase->cachedDataCapInFramesPerBus * ma_node_output_bus_get_channels(&pNodeBase->pOutputBuses[iOutputBus]); } return pBasePtr; } typedef struct { size_t sizeInBytes; size_t inputBusOffset; size_t outputBusOffset; size_t cachedDataOffset; ma_uint32 inputBusCount; /* So it doesn't have to be calculated twice. */ ma_uint32 outputBusCount; /* So it doesn't have to be calculated twice. */ } ma_node_heap_layout; static ma_result ma_node_translate_bus_counts(const ma_node_config* pConfig, ma_uint32* pInputBusCount, ma_uint32* pOutputBusCount) { ma_uint32 inputBusCount; ma_uint32 outputBusCount; MA_ASSERT(pConfig != NULL); MA_ASSERT(pInputBusCount != NULL); MA_ASSERT(pOutputBusCount != NULL); /* Bus counts are determined by the vtable, unless they're set to `MA_NODE_BUS_COUNT_UNKNWON`, in which case they're taken from the config. */ if (pConfig->vtable->inputBusCount == MA_NODE_BUS_COUNT_UNKNOWN) { inputBusCount = pConfig->inputBusCount; } else { inputBusCount = pConfig->vtable->inputBusCount; if (pConfig->inputBusCount != MA_NODE_BUS_COUNT_UNKNOWN && pConfig->inputBusCount != pConfig->vtable->inputBusCount) { return MA_INVALID_ARGS; /* Invalid configuration. You must not specify a conflicting bus count between the node's config and the vtable. */ } } if (pConfig->vtable->outputBusCount == MA_NODE_BUS_COUNT_UNKNOWN) { outputBusCount = pConfig->outputBusCount; } else { outputBusCount = pConfig->vtable->outputBusCount; if (pConfig->outputBusCount != MA_NODE_BUS_COUNT_UNKNOWN && pConfig->outputBusCount != pConfig->vtable->outputBusCount) { return MA_INVALID_ARGS; /* Invalid configuration. You must not specify a conflicting bus count between the node's config and the vtable. */ } } /* Bus counts must be within limits. */ if (inputBusCount > MA_MAX_NODE_BUS_COUNT || outputBusCount > MA_MAX_NODE_BUS_COUNT) { return MA_INVALID_ARGS; } /* We must have channel counts for each bus. */ if ((inputBusCount > 0 && pConfig->pInputChannels == NULL) || (outputBusCount > 0 && pConfig->pOutputChannels == NULL)) { return MA_INVALID_ARGS; /* You must specify channel counts for each input and output bus. */ } /* Some special rules for passthrough nodes. */ if ((pConfig->vtable->flags & MA_NODE_FLAG_PASSTHROUGH) != 0) { if (pConfig->vtable->inputBusCount != 1 || pConfig->vtable->outputBusCount != 1) { return MA_INVALID_ARGS; /* Passthrough nodes must have exactly 1 input bus and 1 output bus. */ } if (pConfig->pInputChannels[0] != pConfig->pOutputChannels[0]) { return MA_INVALID_ARGS; /* Passthrough nodes must have the same number of channels between input and output nodes. */ } } *pInputBusCount = inputBusCount; *pOutputBusCount = outputBusCount; return MA_SUCCESS; } static ma_result ma_node_get_heap_layout(ma_node_graph* pNodeGraph, const ma_node_config* pConfig, ma_node_heap_layout* pHeapLayout) { ma_result result; ma_uint32 inputBusCount; ma_uint32 outputBusCount; MA_ASSERT(pHeapLayout != NULL); MA_ZERO_OBJECT(pHeapLayout); if (pConfig == NULL || pConfig->vtable == NULL || pConfig->vtable->onProcess == NULL) { return MA_INVALID_ARGS; } result = ma_node_translate_bus_counts(pConfig, &inputBusCount, &outputBusCount); if (result != MA_SUCCESS) { return result; } pHeapLayout->sizeInBytes = 0; /* Input buses. */ if (inputBusCount > MA_MAX_NODE_LOCAL_BUS_COUNT) { pHeapLayout->inputBusOffset = pHeapLayout->sizeInBytes; pHeapLayout->sizeInBytes += ma_align_64(sizeof(ma_node_input_bus) * inputBusCount); } else { pHeapLayout->inputBusOffset = MA_SIZE_MAX; /* MA_SIZE_MAX indicates that no heap allocation is required for the input bus. */ } /* Output buses. */ if (outputBusCount > MA_MAX_NODE_LOCAL_BUS_COUNT) { pHeapLayout->outputBusOffset = pHeapLayout->sizeInBytes; pHeapLayout->sizeInBytes += ma_align_64(sizeof(ma_node_output_bus) * outputBusCount); } else { pHeapLayout->outputBusOffset = MA_SIZE_MAX; } /* Cached audio data. We need to allocate memory for a caching both input and output data. We have an optimization where no caching is necessary for specific conditions: - The node has 0 inputs and 1 output. When a node meets the above conditions, no cache is allocated. The size choice for this buffer is a little bit finicky. We don't want to be too wasteful by allocating too much, but at the same time we want it be large enough so that enough frames can be processed for each call to ma_node_read_pcm_frames() so that it keeps things efficient. For now I'm going with 10ms @ 48K which is 480 frames per bus. This is configurable at compile time. It might also be worth investigating whether or not this can be configured at run time. */ if (inputBusCount == 0 && outputBusCount == 1) { /* Fast path. No cache needed. */ pHeapLayout->cachedDataOffset = MA_SIZE_MAX; } else { /* Slow path. Cache needed. */ size_t cachedDataSizeInBytes = 0; ma_uint32 iBus; for (iBus = 0; iBus < inputBusCount; iBus += 1) { cachedDataSizeInBytes += pNodeGraph->nodeCacheCapInFrames * ma_get_bytes_per_frame(ma_format_f32, pConfig->pInputChannels[iBus]); } for (iBus = 0; iBus < outputBusCount; iBus += 1) { cachedDataSizeInBytes += pNodeGraph->nodeCacheCapInFrames * ma_get_bytes_per_frame(ma_format_f32, pConfig->pOutputChannels[iBus]); } pHeapLayout->cachedDataOffset = pHeapLayout->sizeInBytes; pHeapLayout->sizeInBytes += ma_align_64(cachedDataSizeInBytes); } /* Not technically part of the heap, but we can output the input and output bus counts so we can avoid a redundant call to ma_node_translate_bus_counts(). */ pHeapLayout->inputBusCount = inputBusCount; pHeapLayout->outputBusCount = outputBusCount; /* Make sure allocation size is aligned. */ pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes); return MA_SUCCESS; } MA_API ma_result ma_node_get_heap_size(ma_node_graph* pNodeGraph, const ma_node_config* pConfig, size_t* pHeapSizeInBytes) { ma_result result; ma_node_heap_layout heapLayout; if (pHeapSizeInBytes == NULL) { return MA_INVALID_ARGS; } *pHeapSizeInBytes = 0; result = ma_node_get_heap_layout(pNodeGraph, pConfig, &heapLayout); if (result != MA_SUCCESS) { return result; } *pHeapSizeInBytes = heapLayout.sizeInBytes; return MA_SUCCESS; } MA_API ma_result ma_node_init_preallocated(ma_node_graph* pNodeGraph, const ma_node_config* pConfig, void* pHeap, ma_node* pNode) { ma_node_base* pNodeBase = (ma_node_base*)pNode; ma_result result; ma_node_heap_layout heapLayout; ma_uint32 iInputBus; ma_uint32 iOutputBus; if (pNodeBase == NULL) { return MA_INVALID_ARGS; } MA_ZERO_OBJECT(pNodeBase); result = ma_node_get_heap_layout(pNodeGraph, pConfig, &heapLayout); if (result != MA_SUCCESS) { return result; } pNodeBase->_pHeap = pHeap; MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes); pNodeBase->pNodeGraph = pNodeGraph; pNodeBase->vtable = pConfig->vtable; pNodeBase->state = pConfig->initialState; pNodeBase->stateTimes[ma_node_state_started] = 0; pNodeBase->stateTimes[ma_node_state_stopped] = (ma_uint64)(ma_int64)-1; /* Weird casting for VC6 compatibility. */ pNodeBase->inputBusCount = heapLayout.inputBusCount; pNodeBase->outputBusCount = heapLayout.outputBusCount; if (heapLayout.inputBusOffset != MA_SIZE_MAX) { pNodeBase->pInputBuses = (ma_node_input_bus*)ma_offset_ptr(pHeap, heapLayout.inputBusOffset); } else { pNodeBase->pInputBuses = pNodeBase->_inputBuses; } if (heapLayout.outputBusOffset != MA_SIZE_MAX) { pNodeBase->pOutputBuses = (ma_node_output_bus*)ma_offset_ptr(pHeap, heapLayout.inputBusOffset); } else { pNodeBase->pOutputBuses = pNodeBase->_outputBuses; } if (heapLayout.cachedDataOffset != MA_SIZE_MAX) { pNodeBase->pCachedData = (float*)ma_offset_ptr(pHeap, heapLayout.cachedDataOffset); pNodeBase->cachedDataCapInFramesPerBus = pNodeGraph->nodeCacheCapInFrames; } else { pNodeBase->pCachedData = NULL; } /* We need to run an initialization step for each input and output bus. */ for (iInputBus = 0; iInputBus < ma_node_get_input_bus_count(pNodeBase); iInputBus += 1) { result = ma_node_input_bus_init(pConfig->pInputChannels[iInputBus], &pNodeBase->pInputBuses[iInputBus]); if (result != MA_SUCCESS) { return result; } } for (iOutputBus = 0; iOutputBus < ma_node_get_output_bus_count(pNodeBase); iOutputBus += 1) { result = ma_node_output_bus_init(pNodeBase, iOutputBus, pConfig->pOutputChannels[iOutputBus], &pNodeBase->pOutputBuses[iOutputBus]); if (result != MA_SUCCESS) { return result; } } /* The cached data needs to be initialized to silence (or a sine wave tone if we're debugging). */ if (pNodeBase->pCachedData != NULL) { ma_uint32 iBus; #if 1 /* Toggle this between 0 and 1 to turn debugging on or off. 1 = fill with a sine wave for debugging; 0 = fill with silence. */ /* For safety we'll go ahead and default the buffer to silence. */ for (iBus = 0; iBus < ma_node_get_input_bus_count(pNodeBase); iBus += 1) { ma_silence_pcm_frames(ma_node_get_cached_input_ptr(pNode, iBus), pNodeBase->cachedDataCapInFramesPerBus, ma_format_f32, ma_node_input_bus_get_channels(&pNodeBase->pInputBuses[iBus])); } for (iBus = 0; iBus < ma_node_get_output_bus_count(pNodeBase); iBus += 1) { ma_silence_pcm_frames(ma_node_get_cached_output_ptr(pNode, iBus), pNodeBase->cachedDataCapInFramesPerBus, ma_format_f32, ma_node_output_bus_get_channels(&pNodeBase->pOutputBuses[iBus])); } #else /* For debugging. Default to a sine wave. */ for (iBus = 0; iBus < ma_node_get_input_bus_count(pNodeBase); iBus += 1) { ma_debug_fill_pcm_frames_with_sine_wave(ma_node_get_cached_input_ptr(pNode, iBus), pNodeBase->cachedDataCapInFramesPerBus, ma_format_f32, ma_node_input_bus_get_channels(&pNodeBase->pInputBuses[iBus]), 48000); } for (iBus = 0; iBus < ma_node_get_output_bus_count(pNodeBase); iBus += 1) { ma_debug_fill_pcm_frames_with_sine_wave(ma_node_get_cached_output_ptr(pNode, iBus), pNodeBase->cachedDataCapInFramesPerBus, ma_format_f32, ma_node_output_bus_get_channels(&pNodeBase->pOutputBuses[iBus]), 48000); } #endif } return MA_SUCCESS; } MA_API ma_result ma_node_init(ma_node_graph* pNodeGraph, const ma_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_node* pNode) { ma_result result; size_t heapSizeInBytes; void* pHeap; result = ma_node_get_heap_size(pNodeGraph, pConfig, &heapSizeInBytes); if (result != MA_SUCCESS) { return result; } if (heapSizeInBytes > 0) { pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); if (pHeap == NULL) { return MA_OUT_OF_MEMORY; } } else { pHeap = NULL; } result = ma_node_init_preallocated(pNodeGraph, pConfig, pHeap, pNode); if (result != MA_SUCCESS) { ma_free(pHeap, pAllocationCallbacks); return result; } ((ma_node_base*)pNode)->_ownsHeap = MA_TRUE; return MA_SUCCESS; } MA_API void ma_node_uninit(ma_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks) { ma_node_base* pNodeBase = (ma_node_base*)pNode; if (pNodeBase == NULL) { return; } /* The first thing we need to do is fully detach the node. This will detach all inputs and outputs. We need to do this first because it will sever the connection with the node graph and allow us to complete uninitialization without needing to worry about thread-safety with the audio thread. The detachment process will wait for any local processing of the node to finish. */ ma_node_detach_full(pNode); /* At this point the node should be completely unreferenced by the node graph and we can finish up the uninitialization process without needing to worry about thread-safety. */ if (pNodeBase->_ownsHeap) { ma_free(pNodeBase->_pHeap, pAllocationCallbacks); } } MA_API ma_node_graph* ma_node_get_node_graph(const ma_node* pNode) { if (pNode == NULL) { return NULL; } return ((const ma_node_base*)pNode)->pNodeGraph; } MA_API ma_uint32 ma_node_get_input_bus_count(const ma_node* pNode) { if (pNode == NULL) { return 0; } return ((ma_node_base*)pNode)->inputBusCount; } MA_API ma_uint32 ma_node_get_output_bus_count(const ma_node* pNode) { if (pNode == NULL) { return 0; } return ((ma_node_base*)pNode)->outputBusCount; } MA_API ma_uint32 ma_node_get_input_channels(const ma_node* pNode, ma_uint32 inputBusIndex) { const ma_node_base* pNodeBase = (const ma_node_base*)pNode; if (pNode == NULL) { return 0; } if (inputBusIndex >= ma_node_get_input_bus_count(pNode)) { return 0; /* Invalid bus index. */ } return ma_node_input_bus_get_channels(&pNodeBase->pInputBuses[inputBusIndex]); } MA_API ma_uint32 ma_node_get_output_channels(const ma_node* pNode, ma_uint32 outputBusIndex) { const ma_node_base* pNodeBase = (const ma_node_base*)pNode; if (pNode == NULL) { return 0; } if (outputBusIndex >= ma_node_get_output_bus_count(pNode)) { return 0; /* Invalid bus index. */ } return ma_node_output_bus_get_channels(&pNodeBase->pOutputBuses[outputBusIndex]); } static ma_result ma_node_detach_full(ma_node* pNode) { ma_node_base* pNodeBase = (ma_node_base*)pNode; ma_uint32 iInputBus; if (pNodeBase == NULL) { return MA_INVALID_ARGS; } /* Make sure the node is completely detached first. This will not return until the output bus is guaranteed to no longer be referenced by the audio thread. */ ma_node_detach_all_output_buses(pNode); /* At this point all output buses will have been detached from the graph and we can be guaranteed that none of it's input nodes will be getting processed by the graph. We can detach these without needing to worry about the audio thread touching them. */ for (iInputBus = 0; iInputBus < ma_node_get_input_bus_count(pNode); iInputBus += 1) { ma_node_input_bus* pInputBus; ma_node_output_bus* pOutputBus; pInputBus = &pNodeBase->pInputBuses[iInputBus]; /* This is important. We cannot be using ma_node_input_bus_first() or ma_node_input_bus_next(). Those functions are specifically for the audio thread. We'll instead just manually iterate using standard linked list logic. We don't need to worry about the audio thread referencing these because the step above severed the connection to the graph. */ for (pOutputBus = (ma_node_output_bus*)c89atomic_load_ptr(&pInputBus->head.pNext); pOutputBus != NULL; pOutputBus = (ma_node_output_bus*)c89atomic_load_ptr(&pOutputBus->pNext)) { ma_node_detach_output_bus(pOutputBus->pNode, pOutputBus->outputBusIndex); /* This won't do any waiting in practice and should be efficient. */ } } return MA_SUCCESS; } MA_API ma_result ma_node_detach_output_bus(ma_node* pNode, ma_uint32 outputBusIndex) { ma_result result = MA_SUCCESS; ma_node_base* pNodeBase = (ma_node_base*)pNode; ma_node_base* pInputNodeBase; if (pNode == NULL) { return MA_INVALID_ARGS; } if (outputBusIndex >= ma_node_get_output_bus_count(pNode)) { return MA_INVALID_ARGS; /* Invalid output bus index. */ } /* We need to lock the output bus because we need to inspect the input node and grab it's input bus. */ ma_node_output_bus_lock(&pNodeBase->pOutputBuses[outputBusIndex]); { pInputNodeBase = (ma_node_base*)pNodeBase->pOutputBuses[outputBusIndex].pInputNode; if (pInputNodeBase != NULL) { ma_node_input_bus_detach__no_output_bus_lock(&pInputNodeBase->pInputBuses[pNodeBase->pOutputBuses[outputBusIndex].inputNodeInputBusIndex], &pNodeBase->pOutputBuses[outputBusIndex]); } } ma_node_output_bus_unlock(&pNodeBase->pOutputBuses[outputBusIndex]); return result; } MA_API ma_result ma_node_detach_all_output_buses(ma_node* pNode) { ma_uint32 iOutputBus; if (pNode == NULL) { return MA_INVALID_ARGS; } for (iOutputBus = 0; iOutputBus < ma_node_get_output_bus_count(pNode); iOutputBus += 1) { ma_node_detach_output_bus(pNode, iOutputBus); } return MA_SUCCESS; } MA_API ma_result ma_node_attach_output_bus(ma_node* pNode, ma_uint32 outputBusIndex, ma_node* pOtherNode, ma_uint32 otherNodeInputBusIndex) { ma_node_base* pNodeBase = (ma_node_base*)pNode; ma_node_base* pOtherNodeBase = (ma_node_base*)pOtherNode; if (pNodeBase == NULL || pOtherNodeBase == NULL) { return MA_INVALID_ARGS; } if (pNodeBase == pOtherNodeBase) { return MA_INVALID_OPERATION; /* Cannot attach a node to itself. */ } if (outputBusIndex >= ma_node_get_output_bus_count(pNode) || otherNodeInputBusIndex >= ma_node_get_input_bus_count(pOtherNode)) { return MA_INVALID_OPERATION; /* Invalid bus index. */ } /* The output channel count of the output node must be the same as the input channel count of the input node. */ if (ma_node_get_output_channels(pNode, outputBusIndex) != ma_node_get_input_channels(pOtherNode, otherNodeInputBusIndex)) { return MA_INVALID_OPERATION; /* Channel count is incompatible. */ } /* This will deal with detaching if the output bus is already attached to something. */ ma_node_input_bus_attach(&pOtherNodeBase->pInputBuses[otherNodeInputBusIndex], &pNodeBase->pOutputBuses[outputBusIndex], pOtherNode, otherNodeInputBusIndex); return MA_SUCCESS; } MA_API ma_result ma_node_set_output_bus_volume(ma_node* pNode, ma_uint32 outputBusIndex, float volume) { ma_node_base* pNodeBase = (ma_node_base*)pNode; if (pNodeBase == NULL) { return MA_INVALID_ARGS; } if (outputBusIndex >= ma_node_get_output_bus_count(pNode)) { return MA_INVALID_ARGS; /* Invalid bus index. */ } return ma_node_output_bus_set_volume(&pNodeBase->pOutputBuses[outputBusIndex], volume); } MA_API float ma_node_get_output_bus_volume(const ma_node* pNode, ma_uint32 outputBusIndex) { const ma_node_base* pNodeBase = (const ma_node_base*)pNode; if (pNodeBase == NULL) { return 0; } if (outputBusIndex >= ma_node_get_output_bus_count(pNode)) { return 0; /* Invalid bus index. */ } return ma_node_output_bus_get_volume(&pNodeBase->pOutputBuses[outputBusIndex]); } MA_API ma_result ma_node_set_state(ma_node* pNode, ma_node_state state) { ma_node_base* pNodeBase = (ma_node_base*)pNode; if (pNodeBase == NULL) { return MA_INVALID_ARGS; } c89atomic_exchange_i32(&pNodeBase->state, state); return MA_SUCCESS; } MA_API ma_node_state ma_node_get_state(const ma_node* pNode) { const ma_node_base* pNodeBase = (const ma_node_base*)pNode; if (pNodeBase == NULL) { return ma_node_state_stopped; } return (ma_node_state)c89atomic_load_i32(&pNodeBase->state); } MA_API ma_result ma_node_set_state_time(ma_node* pNode, ma_node_state state, ma_uint64 globalTime) { if (pNode == NULL) { return MA_INVALID_ARGS; } /* Validation check for safety since we'll be using this as an index into stateTimes[]. */ if (state != ma_node_state_started && state != ma_node_state_stopped) { return MA_INVALID_ARGS; } c89atomic_exchange_64(&((ma_node_base*)pNode)->stateTimes[state], globalTime); return MA_SUCCESS; } MA_API ma_uint64 ma_node_get_state_time(const ma_node* pNode, ma_node_state state) { if (pNode == NULL) { return 0; } /* Validation check for safety since we'll be using this as an index into stateTimes[]. */ if (state != ma_node_state_started && state != ma_node_state_stopped) { return 0; } return c89atomic_load_64(&((ma_node_base*)pNode)->stateTimes[state]); } MA_API ma_node_state ma_node_get_state_by_time(const ma_node* pNode, ma_uint64 globalTime) { if (pNode == NULL) { return ma_node_state_stopped; } return ma_node_get_state_by_time_range(pNode, globalTime, globalTime); } MA_API ma_node_state ma_node_get_state_by_time_range(const ma_node* pNode, ma_uint64 globalTimeBeg, ma_uint64 globalTimeEnd) { ma_node_state state; if (pNode == NULL) { return ma_node_state_stopped; } state = ma_node_get_state(pNode); /* An explicitly stopped node is always stopped. */ if (state == ma_node_state_stopped) { return ma_node_state_stopped; } /* Getting here means the node is marked as started, but it may still not be truly started due to it's start time not having been reached yet. Also, the stop time may have also been reached in which case it'll be considered stopped. */ if (ma_node_get_state_time(pNode, ma_node_state_started) > globalTimeBeg) { return ma_node_state_stopped; /* Start time has not yet been reached. */ } if (ma_node_get_state_time(pNode, ma_node_state_stopped) <= globalTimeEnd) { return ma_node_state_stopped; /* Stop time has been reached. */ } /* Getting here means the node is marked as started and is within it's start/stop times. */ return ma_node_state_started; } MA_API ma_uint64 ma_node_get_time(const ma_node* pNode) { if (pNode == NULL) { return 0; } return c89atomic_load_64(&((ma_node_base*)pNode)->localTime); } MA_API ma_result ma_node_set_time(ma_node* pNode, ma_uint64 localTime) { if (pNode == NULL) { return MA_INVALID_ARGS; } c89atomic_exchange_64(&((ma_node_base*)pNode)->localTime, localTime); return MA_SUCCESS; } static void ma_node_process_pcm_frames_internal(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) { ma_node_base* pNodeBase = (ma_node_base*)pNode; MA_ASSERT(pNode != NULL); if (pNodeBase->vtable->onProcess) { pNodeBase->vtable->onProcess(pNode, ppFramesIn, pFrameCountIn, ppFramesOut, pFrameCountOut); } } static ma_result ma_node_read_pcm_frames(ma_node* pNode, ma_uint32 outputBusIndex, float* pFramesOut, ma_uint32 frameCount, ma_uint32* pFramesRead, ma_uint64 globalTime) { ma_node_base* pNodeBase = (ma_node_base*)pNode; ma_result result = MA_SUCCESS; ma_uint32 iInputBus; ma_uint32 iOutputBus; ma_uint32 inputBusCount; ma_uint32 outputBusCount; ma_uint32 totalFramesRead = 0; float* ppFramesIn[MA_MAX_NODE_BUS_COUNT]; float* ppFramesOut[MA_MAX_NODE_BUS_COUNT]; ma_uint64 globalTimeBeg; ma_uint64 globalTimeEnd; ma_uint64 startTime; ma_uint64 stopTime; ma_uint32 timeOffsetBeg; ma_uint32 timeOffsetEnd; ma_uint32 frameCountIn; ma_uint32 frameCountOut; /* pFramesRead is mandatory. It must be used to determine how many frames were read. It's normal and expected that the number of frames read may be different to that requested. Therefore, the caller must look at this value to correctly determine how many frames were read. */ MA_ASSERT(pFramesRead != NULL); /* <-- If you've triggered this assert, you're using this function wrong. You *must* use this variable and inspect it after the call returns. */ if (pFramesRead == NULL) { return MA_INVALID_ARGS; } *pFramesRead = 0; /* Safety. */ if (pNodeBase == NULL) { return MA_INVALID_ARGS; } if (outputBusIndex >= ma_node_get_output_bus_count(pNodeBase)) { return MA_INVALID_ARGS; /* Invalid output bus index. */ } /* Don't do anything if we're in a stopped state. */ if (ma_node_get_state_by_time_range(pNode, globalTime, globalTime + frameCount) != ma_node_state_started) { return MA_SUCCESS; /* We're in a stopped state. This is not an error - we just need to not read anything. */ } globalTimeBeg = globalTime; globalTimeEnd = globalTime + frameCount; startTime = ma_node_get_state_time(pNode, ma_node_state_started); stopTime = ma_node_get_state_time(pNode, ma_node_state_stopped); /* At this point we know that we are inside our start/stop times. However, we may need to adjust our frame count and output pointer to accomodate since we could be straddling the time period that this function is getting called for. It's possible (and likely) that the start time does not line up with the output buffer. We therefore need to offset it by a number of frames to accomodate. The same thing applies for the stop time. */ timeOffsetBeg = (globalTimeBeg < startTime) ? (ma_uint32)(globalTimeEnd - startTime) : 0; timeOffsetEnd = (globalTimeEnd > stopTime) ? (ma_uint32)(globalTimeEnd - stopTime) : 0; /* Trim based on the start offset. We need to silence the start of the buffer. */ if (timeOffsetBeg > 0) { ma_silence_pcm_frames(pFramesOut, timeOffsetBeg, ma_format_f32, ma_node_get_output_channels(pNode, outputBusIndex)); pFramesOut += timeOffsetBeg * ma_node_get_output_channels(pNode, outputBusIndex); frameCount -= timeOffsetBeg; } /* Trim based on the end offset. We don't need to silence the tail section because we'll just have a reduced value written to pFramesRead. */ if (timeOffsetEnd > 0) { frameCount -= timeOffsetEnd; } /* We run on different paths depending on the bus counts. */ inputBusCount = ma_node_get_input_bus_count(pNode); outputBusCount = ma_node_get_output_bus_count(pNode); /* Run a simplified path when there are no inputs and one output. In this case there's nothing to actually read and we can go straight to output. This is a very common scenario because the vast majority of data source nodes will use this setup so this optimization I think is worthwhile. */ if (inputBusCount == 0 && outputBusCount == 1) { /* Fast path. No need to read from input and no need for any caching. */ frameCountIn = 0; frameCountOut = frameCount; /* Just read as much as we can. The callback will return what was actually read. */ ppFramesOut[0] = pFramesOut; ma_node_process_pcm_frames_internal(pNode, NULL, &frameCountIn, ppFramesOut, &frameCountOut); totalFramesRead = frameCountOut; } else { /* Slow path. Need to read input data. */ if ((pNodeBase->vtable->flags & MA_NODE_FLAG_PASSTHROUGH) != 0) { /* Fast path. We're running a passthrough. We need to read directly into the output buffer, but still fire the callback so that event handling and trigger nodes can do their thing. Since it's a passthrough there's no need for any kind of caching logic. */ MA_ASSERT(outputBusCount == inputBusCount); MA_ASSERT(outputBusCount == 1); MA_ASSERT(outputBusIndex == 0); /* We just read directly from input bus to output buffer, and then afterwards fire the callback. */ ppFramesOut[0] = pFramesOut; ppFramesIn[0] = ppFramesOut[0]; result = ma_node_input_bus_read_pcm_frames(pNodeBase, &pNodeBase->pInputBuses[0], ppFramesIn[0], frameCount, &totalFramesRead, globalTime); if (result == MA_SUCCESS) { /* Even though it's a passthrough, we still need to fire the callback. */ frameCountIn = totalFramesRead; frameCountOut = totalFramesRead; if (totalFramesRead > 0) { ma_node_process_pcm_frames_internal(pNode, (const float**)ppFramesIn, &frameCountIn, ppFramesOut, &frameCountOut); /* From GCC: expected 'const float **' but argument is of type 'float **'. Shouldn't this be implicit? Excplicit cast to silence the warning. */ } /* A passthrough should never have modified the input and output frame counts. If you're triggering these assers you need to fix your processing callback. */ MA_ASSERT(frameCountIn == totalFramesRead); MA_ASSERT(frameCountOut == totalFramesRead); } } else { /* Slow path. Need to do caching. */ ma_uint32 framesToProcessIn; ma_uint32 framesToProcessOut; ma_bool32 consumeNullInput = MA_FALSE; /* We use frameCount as a basis for the number of frames to read since that's what's being requested, however we still need to clamp it to whatever can fit in the cache. This will also be used as the basis for determining how many input frames to read. This is not ideal because it can result in too many input frames being read which introduces latency. To solve this, nodes can implement an optional callback called onGetRequiredInputFrameCount which is used as hint to miniaudio as to how many input frames it needs to read at a time. This callback is completely optional, and if it's not set, miniaudio will assume `frameCount`. This function will be called multiple times for each period of time, once for each output node. We cannot read from each input node each time this function is called. Instead we need to check whether or not this is first output bus to be read from for this time period, and if so, read from our input data. To determine whether or not we're ready to read data, we check a flag. There will be one flag for each output. When the flag is set, it means data has been read previously and that we're ready to advance time forward for our input nodes by reading fresh data. */ framesToProcessOut = frameCount; if (framesToProcessOut > pNodeBase->cachedDataCapInFramesPerBus) { framesToProcessOut = pNodeBase->cachedDataCapInFramesPerBus; } framesToProcessIn = frameCount; if (pNodeBase->vtable->onGetRequiredInputFrameCount) { pNodeBase->vtable->onGetRequiredInputFrameCount(pNode, framesToProcessOut, &framesToProcessIn); /* <-- It does not matter if this fails. */ } if (framesToProcessIn > pNodeBase->cachedDataCapInFramesPerBus) { framesToProcessIn = pNodeBase->cachedDataCapInFramesPerBus; } MA_ASSERT(framesToProcessIn <= 0xFFFF); MA_ASSERT(framesToProcessOut <= 0xFFFF); if (ma_node_output_bus_has_read(&pNodeBase->pOutputBuses[outputBusIndex])) { /* Getting here means we need to do another round of processing. */ pNodeBase->cachedFrameCountOut = 0; for (;;) { frameCountOut = 0; /* We need to prepare our output frame pointers for processing. In the same iteration we need to mark every output bus as unread so that future calls to this function for different buses for the current time period don't pull in data when they should instead be reading from cache. */ for (iOutputBus = 0; iOutputBus < outputBusCount; iOutputBus += 1) { ma_node_output_bus_set_has_read(&pNodeBase->pOutputBuses[iOutputBus], MA_FALSE); /* <-- This is what tells the next calls to this function for other output buses for this time period to read from cache instead of pulling in more data. */ ppFramesOut[iOutputBus] = ma_node_get_cached_output_ptr(pNode, iOutputBus); } /* We only need to read from input buses if there isn't already some data in the cache. */ if (pNodeBase->cachedFrameCountIn == 0) { ma_uint32 maxFramesReadIn = 0; /* Here is where we pull in data from the input buses. This is what will trigger an advance in time. */ for (iInputBus = 0; iInputBus < inputBusCount; iInputBus += 1) { ma_uint32 framesRead; /* The first thing to do is get the offset within our bulk allocation to store this input data. */ ppFramesIn[iInputBus] = ma_node_get_cached_input_ptr(pNode, iInputBus); /* Once we've determined our destination pointer we can read. Note that we must inspect the number of frames read and fill any leftovers with silence for safety. */ result = ma_node_input_bus_read_pcm_frames(pNodeBase, &pNodeBase->pInputBuses[iInputBus], ppFramesIn[iInputBus], framesToProcessIn, &framesRead, globalTime); if (result != MA_SUCCESS) { /* It doesn't really matter if we fail because we'll just fill with silence. */ framesRead = 0; /* Just for safety, but I don't think it's really needed. */ } /* TODO: Minor optimization opportunity here. If no frames were read and the buffer is already filled with silence, no need to re-silence it. */ /* Any leftover frames need to silenced for safety. */ if (framesRead < framesToProcessIn) { ma_silence_pcm_frames(ppFramesIn[iInputBus] + (framesRead * ma_node_get_input_channels(pNodeBase, iInputBus)), (framesToProcessIn - framesRead), ma_format_f32, ma_node_get_input_channels(pNodeBase, iInputBus)); } maxFramesReadIn = ma_max(maxFramesReadIn, framesRead); } /* This was a fresh load of input data so reset our consumption counter. */ pNodeBase->consumedFrameCountIn = 0; /* We don't want to keep processing if there's nothing to process, so set the number of cached input frames to the maximum number we read from each attachment (the lesser will be padded with silence). If we didn't read anything, this will be set to 0 and the entire buffer will have been assigned to silence. This being equal to 0 is an important property for us because it allows us to detect when NULL can be passed into the processing callback for the input buffer for the purpose of continuous processing. */ pNodeBase->cachedFrameCountIn = (ma_uint16)maxFramesReadIn; } else { /* We don't need to read anything, but we do need to prepare our input frame pointers. */ for (iInputBus = 0; iInputBus < inputBusCount; iInputBus += 1) { ppFramesIn[iInputBus] = ma_node_get_cached_input_ptr(pNode, iInputBus) + (pNodeBase->consumedFrameCountIn * ma_node_get_input_channels(pNodeBase, iInputBus)); } } /* At this point we have our input data so now we need to do some processing. Sneaky little optimization here - we can set the pointer to the output buffer for this output bus so that the final copy into the output buffer is done directly by onProcess(). */ if (pFramesOut != NULL) { ppFramesOut[outputBusIndex] = ma_offset_pcm_frames_ptr_f32(pFramesOut, pNodeBase->cachedFrameCountOut, ma_node_get_output_channels(pNode, outputBusIndex)); } /* Give the processing function the entire capacity of the output buffer. */ frameCountOut = (framesToProcessOut - pNodeBase->cachedFrameCountOut); /* We need to treat nodes with continuous processing a little differently. For these ones, we always want to fire the callback with the requested number of frames, regardless of pNodeBase->cachedFrameCountIn, which could be 0. Also, we want to check if we can pass in NULL for the input buffer to the callback. */ if ((pNodeBase->vtable->flags & MA_NODE_FLAG_CONTINUOUS_PROCESSING) != 0) { /* We're using continuous processing. Make sure we specify the whole frame count at all times. */ frameCountIn = framesToProcessIn; /* Give the processing function as much input data as we've got in the buffer, including any silenced padding from short reads. */ if ((pNodeBase->vtable->flags & MA_NODE_FLAG_ALLOW_NULL_INPUT) != 0 && pNodeBase->consumedFrameCountIn == 0 && pNodeBase->cachedFrameCountIn == 0) { consumeNullInput = MA_TRUE; } else { consumeNullInput = MA_FALSE; } /* Since we're using continuous processing we're always passing in a full frame count regardless of how much input data was read. If this is greater than what we read as input, we'll end up with an underflow. We instead need to make sure our cached frame count is set to the number of frames we'll be passing to the data callback. Not doing this will result in an underflow when we "consume" the cached data later on. Note that this check needs to be done after the "consumeNullInput" check above because we use the property of cachedFrameCountIn being 0 to determine whether or not we should be passing in a null pointer to the processing callback for when the node is configured with MA_NODE_FLAG_ALLOW_NULL_INPUT. */ if (pNodeBase->cachedFrameCountIn < (ma_uint16)frameCountIn) { pNodeBase->cachedFrameCountIn = (ma_uint16)frameCountIn; } } else { frameCountIn = pNodeBase->cachedFrameCountIn; /* Give the processing function as much valid input data as we've got. */ consumeNullInput = MA_FALSE; } /* Process data slightly differently depending on whether or not we're consuming NULL input (checked just above). */ if (consumeNullInput) { ma_node_process_pcm_frames_internal(pNode, NULL, &frameCountIn, ppFramesOut, &frameCountOut); } else { /* We want to skip processing if there's no input data, but we can only do that safely if we know that there is no chance of any output frames being produced. If continuous processing is being used, this won't be a problem because the input frame count will always be non-0. However, if continuous processing is *not* enabled and input and output data is processed at different rates, we still need to process that last input frame because there could be a few excess output frames needing to be produced from cached data. The `MA_NODE_FLAG_DIFFERENT_PROCESSING_RATES` flag is used as the indicator for determining whether or not we need to process the node even when there are no input frames available right now. */ if (frameCountIn > 0 || (pNodeBase->vtable->flags & MA_NODE_FLAG_DIFFERENT_PROCESSING_RATES) != 0) { ma_node_process_pcm_frames_internal(pNode, (const float**)ppFramesIn, &frameCountIn, ppFramesOut, &frameCountOut); /* From GCC: expected 'const float **' but argument is of type 'float **'. Shouldn't this be implicit? Excplicit cast to silence the warning. */ } else { frameCountOut = 0; /* No data was processed. */ } } /* Thanks to our sneaky optimization above we don't need to do any data copying directly into the output buffer - the onProcess() callback just did that for us. We do, however, need to apply the number of input and output frames that were processed. Note that due to continuous processing above, we need to do explicit checks here. If we just consumed a NULL input buffer it means that no actual input data was processed from the internal buffers and we don't want to be modifying any counters. */ if (consumeNullInput == MA_FALSE) { pNodeBase->consumedFrameCountIn += (ma_uint16)frameCountIn; pNodeBase->cachedFrameCountIn -= (ma_uint16)frameCountIn; } /* The cached output frame count is always equal to what we just read. */ pNodeBase->cachedFrameCountOut += (ma_uint16)frameCountOut; /* If we couldn't process any data, we're done. The loop needs to be terminated here or else we'll get stuck in a loop. */ if (pNodeBase->cachedFrameCountOut == framesToProcessOut || (frameCountOut == 0 && frameCountIn == 0)) { break; } } } else { /* We're not needing to read anything from the input buffer so just read directly from our already-processed data. */ if (pFramesOut != NULL) { ma_copy_pcm_frames(pFramesOut, ma_node_get_cached_output_ptr(pNodeBase, outputBusIndex), pNodeBase->cachedFrameCountOut, ma_format_f32, ma_node_get_output_channels(pNodeBase, outputBusIndex)); } } /* The number of frames read is always equal to the number of cached output frames. */ totalFramesRead = pNodeBase->cachedFrameCountOut; /* Now that we've read the data, make sure our read flag is set. */ ma_node_output_bus_set_has_read(&pNodeBase->pOutputBuses[outputBusIndex], MA_TRUE); } } /* Apply volume, if necessary. */ ma_apply_volume_factor_f32(pFramesOut, totalFramesRead * ma_node_get_output_channels(pNodeBase, outputBusIndex), ma_node_output_bus_get_volume(&pNodeBase->pOutputBuses[outputBusIndex])); /* Advance our local time forward. */ c89atomic_fetch_add_64(&pNodeBase->localTime, (ma_uint64)totalFramesRead); *pFramesRead = totalFramesRead + timeOffsetBeg; /* Must include the silenced section at the start of the buffer. */ return result; } /* Data source node. */ MA_API ma_data_source_node_config ma_data_source_node_config_init(ma_data_source* pDataSource) { ma_data_source_node_config config; MA_ZERO_OBJECT(&config); config.nodeConfig = ma_node_config_init(); config.pDataSource = pDataSource; return config; } static void ma_data_source_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) { ma_data_source_node* pDataSourceNode = (ma_data_source_node*)pNode; ma_format format; ma_uint32 channels; ma_uint32 frameCount; ma_uint64 framesRead = 0; MA_ASSERT(pDataSourceNode != NULL); MA_ASSERT(pDataSourceNode->pDataSource != NULL); MA_ASSERT(ma_node_get_input_bus_count(pDataSourceNode) == 0); MA_ASSERT(ma_node_get_output_bus_count(pDataSourceNode) == 1); /* We don't want to read from ppFramesIn at all. Instead we read from the data source. */ (void)ppFramesIn; (void)pFrameCountIn; frameCount = *pFrameCountOut; /* miniaudio should never be calling this with a frame count of zero. */ MA_ASSERT(frameCount > 0); if (ma_data_source_get_data_format(pDataSourceNode->pDataSource, &format, &channels, NULL, NULL, 0) == MA_SUCCESS) { /* <-- Don't care about sample rate here. */ /* The node graph system requires samples be in floating point format. This is checked in ma_data_source_node_init(). */ MA_ASSERT(format == ma_format_f32); (void)format; /* Just to silence some static analysis tools. */ ma_data_source_read_pcm_frames(pDataSourceNode->pDataSource, ppFramesOut[0], frameCount, &framesRead); } *pFrameCountOut = (ma_uint32)framesRead; } static ma_node_vtable g_ma_data_source_node_vtable = { ma_data_source_node_process_pcm_frames, NULL, /* onGetRequiredInputFrameCount */ 0, /* 0 input buses. */ 1, /* 1 output bus. */ 0 }; MA_API ma_result ma_data_source_node_init(ma_node_graph* pNodeGraph, const ma_data_source_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source_node* pDataSourceNode) { ma_result result; ma_format format; /* For validating the format, which must be ma_format_f32. */ ma_uint32 channels; /* For specifying the channel count of the output bus. */ ma_node_config baseConfig; if (pDataSourceNode == NULL) { return MA_INVALID_ARGS; } MA_ZERO_OBJECT(pDataSourceNode); if (pConfig == NULL) { return MA_INVALID_ARGS; } result = ma_data_source_get_data_format(pConfig->pDataSource, &format, &channels, NULL, NULL, 0); /* Don't care about sample rate. This will check pDataSource for NULL. */ if (result != MA_SUCCESS) { return result; } MA_ASSERT(format == ma_format_f32); /* <-- If you've triggered this it means your data source is not outputting floating-point samples. You must configure your data source to use ma_format_f32. */ if (format != ma_format_f32) { return MA_INVALID_ARGS; /* Invalid format. */ } /* The channel count is defined by the data source. If the caller has manually changed the channels we just ignore it. */ baseConfig = pConfig->nodeConfig; baseConfig.vtable = &g_ma_data_source_node_vtable; /* Explicitly set the vtable here to prevent callers from setting it incorrectly. */ /* The channel count is defined by the data source. It is invalid for the caller to manually set the channel counts in the config. `ma_data_source_node_config_init()` will have defaulted the channel count pointer to NULL which is how it must remain. If you trigger any of these asserts it means you're explicitly setting the channel count. Instead, configure the output channel count of your data source to be the necessary channel count. */ if (baseConfig.pOutputChannels != NULL) { return MA_INVALID_ARGS; } baseConfig.pOutputChannels = &channels; result = ma_node_init(pNodeGraph, &baseConfig, pAllocationCallbacks, &pDataSourceNode->base); if (result != MA_SUCCESS) { return result; } pDataSourceNode->pDataSource = pConfig->pDataSource; return MA_SUCCESS; } MA_API void ma_data_source_node_uninit(ma_data_source_node* pDataSourceNode, const ma_allocation_callbacks* pAllocationCallbacks) { ma_node_uninit(&pDataSourceNode->base, pAllocationCallbacks); } MA_API ma_result ma_data_source_node_set_looping(ma_data_source_node* pDataSourceNode, ma_bool32 isLooping) { if (pDataSourceNode == NULL) { return MA_INVALID_ARGS; } return ma_data_source_set_looping(pDataSourceNode->pDataSource, isLooping); } MA_API ma_bool32 ma_data_source_node_is_looping(ma_data_source_node* pDataSourceNode) { if (pDataSourceNode == NULL) { return MA_FALSE; } return ma_data_source_is_looping(pDataSourceNode->pDataSource); } /* Splitter Node. */ MA_API ma_splitter_node_config ma_splitter_node_config_init(ma_uint32 channels) { ma_splitter_node_config config; MA_ZERO_OBJECT(&config); config.nodeConfig = ma_node_config_init(); config.channels = channels; config.outputBusCount = 2; return config; } static void ma_splitter_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) { ma_node_base* pNodeBase = (ma_node_base*)pNode; ma_uint32 iOutputBus; ma_uint32 channels; MA_ASSERT(pNodeBase != NULL); MA_ASSERT(ma_node_get_input_bus_count(pNodeBase) == 1); MA_ASSERT(ma_node_get_output_bus_count(pNodeBase) >= 2); /* We don't need to consider the input frame count - it'll be the same as the output frame count and we process everything. */ (void)pFrameCountIn; /* NOTE: This assumes the same number of channels for all inputs and outputs. This was checked in ma_splitter_node_init(). */ channels = ma_node_get_input_channels(pNodeBase, 0); /* Splitting is just copying the first input bus and copying it over to each output bus. */ for (iOutputBus = 0; iOutputBus < ma_node_get_output_bus_count(pNodeBase); iOutputBus += 1) { ma_copy_pcm_frames(ppFramesOut[iOutputBus], ppFramesIn[0], *pFrameCountOut, ma_format_f32, channels); } } static ma_node_vtable g_ma_splitter_node_vtable = { ma_splitter_node_process_pcm_frames, NULL, /* onGetRequiredInputFrameCount */ 1, /* 1 input bus. */ MA_NODE_BUS_COUNT_UNKNOWN, /* The output bus count is specified on a per-node basis. */ 0 }; MA_API ma_result ma_splitter_node_init(ma_node_graph* pNodeGraph, const ma_splitter_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_splitter_node* pSplitterNode) { ma_result result; ma_node_config baseConfig; ma_uint32 pInputChannels[1]; ma_uint32 pOutputChannels[MA_MAX_NODE_BUS_COUNT]; ma_uint32 iOutputBus; if (pSplitterNode == NULL) { return MA_INVALID_ARGS; } MA_ZERO_OBJECT(pSplitterNode); if (pConfig == NULL) { return MA_INVALID_ARGS; } if (pConfig->outputBusCount > MA_MAX_NODE_BUS_COUNT) { return MA_INVALID_ARGS; /* Too many output buses. */ } /* Splitters require the same number of channels between inputs and outputs. */ pInputChannels[0] = pConfig->channels; for (iOutputBus = 0; iOutputBus < pConfig->outputBusCount; iOutputBus += 1) { pOutputChannels[iOutputBus] = pConfig->channels; } baseConfig = pConfig->nodeConfig; baseConfig.vtable = &g_ma_splitter_node_vtable; baseConfig.pInputChannels = pInputChannels; baseConfig.pOutputChannels = pOutputChannels; baseConfig.outputBusCount = pConfig->outputBusCount; result = ma_node_init(pNodeGraph, &baseConfig, pAllocationCallbacks, &pSplitterNode->base); if (result != MA_SUCCESS) { return result; /* Failed to initialize the base node. */ } return MA_SUCCESS; } MA_API void ma_splitter_node_uninit(ma_splitter_node* pSplitterNode, const ma_allocation_callbacks* pAllocationCallbacks) { ma_node_uninit(pSplitterNode, pAllocationCallbacks); } /* Biquad Node */ MA_API ma_biquad_node_config ma_biquad_node_config_init(ma_uint32 channels, float b0, float b1, float b2, float a0, float a1, float a2) { ma_biquad_node_config config; config.nodeConfig = ma_node_config_init(); config.biquad = ma_biquad_config_init(ma_format_f32, channels, b0, b1, b2, a0, a1, a2); return config; } static void ma_biquad_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) { ma_biquad_node* pLPFNode = (ma_biquad_node*)pNode; MA_ASSERT(pNode != NULL); (void)pFrameCountIn; ma_biquad_process_pcm_frames(&pLPFNode->biquad, ppFramesOut[0], ppFramesIn[0], *pFrameCountOut); } static ma_node_vtable g_ma_biquad_node_vtable = { ma_biquad_node_process_pcm_frames, NULL, /* onGetRequiredInputFrameCount */ 1, /* One input. */ 1, /* One output. */ 0 /* Default flags. */ }; MA_API ma_result ma_biquad_node_init(ma_node_graph* pNodeGraph, const ma_biquad_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_biquad_node* pNode) { ma_result result; ma_node_config baseNodeConfig; if (pNode == NULL) { return MA_INVALID_ARGS; } MA_ZERO_OBJECT(pNode); if (pConfig == NULL) { return MA_INVALID_ARGS; } if (pConfig->biquad.format != ma_format_f32) { return MA_INVALID_ARGS; /* The format must be f32. */ } result = ma_biquad_init(&pConfig->biquad, pAllocationCallbacks, &pNode->biquad); if (result != MA_SUCCESS) { return result; } baseNodeConfig = ma_node_config_init(); baseNodeConfig.vtable = &g_ma_biquad_node_vtable; baseNodeConfig.pInputChannels = &pConfig->biquad.channels; baseNodeConfig.pOutputChannels = &pConfig->biquad.channels; result = ma_node_init(pNodeGraph, &baseNodeConfig, pAllocationCallbacks, pNode); if (result != MA_SUCCESS) { return result; } return result; } MA_API ma_result ma_biquad_node_reinit(const ma_biquad_config* pConfig, ma_biquad_node* pNode) { ma_biquad_node* pLPFNode = (ma_biquad_node*)pNode; MA_ASSERT(pNode != NULL); return ma_biquad_reinit(pConfig, &pLPFNode->biquad); } MA_API void ma_biquad_node_uninit(ma_biquad_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks) { ma_biquad_node* pLPFNode = (ma_biquad_node*)pNode; if (pNode == NULL) { return; } ma_node_uninit(pNode, pAllocationCallbacks); ma_biquad_uninit(&pLPFNode->biquad, pAllocationCallbacks); } /* Low Pass Filter Node */ MA_API ma_lpf_node_config ma_lpf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order) { ma_lpf_node_config config; config.nodeConfig = ma_node_config_init(); config.lpf = ma_lpf_config_init(ma_format_f32, channels, sampleRate, cutoffFrequency, order); return config; } static void ma_lpf_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) { ma_lpf_node* pLPFNode = (ma_lpf_node*)pNode; MA_ASSERT(pNode != NULL); (void)pFrameCountIn; ma_lpf_process_pcm_frames(&pLPFNode->lpf, ppFramesOut[0], ppFramesIn[0], *pFrameCountOut); } static ma_node_vtable g_ma_lpf_node_vtable = { ma_lpf_node_process_pcm_frames, NULL, /* onGetRequiredInputFrameCount */ 1, /* One input. */ 1, /* One output. */ 0 /* Default flags. */ }; MA_API ma_result ma_lpf_node_init(ma_node_graph* pNodeGraph, const ma_lpf_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_lpf_node* pNode) { ma_result result; ma_node_config baseNodeConfig; if (pNode == NULL) { return MA_INVALID_ARGS; } MA_ZERO_OBJECT(pNode); if (pConfig == NULL) { return MA_INVALID_ARGS; } if (pConfig->lpf.format != ma_format_f32) { return MA_INVALID_ARGS; /* The format must be f32. */ } result = ma_lpf_init(&pConfig->lpf, pAllocationCallbacks, &pNode->lpf); if (result != MA_SUCCESS) { return result; } baseNodeConfig = ma_node_config_init(); baseNodeConfig.vtable = &g_ma_lpf_node_vtable; baseNodeConfig.pInputChannels = &pConfig->lpf.channels; baseNodeConfig.pOutputChannels = &pConfig->lpf.channels; result = ma_node_init(pNodeGraph, &baseNodeConfig, pAllocationCallbacks, pNode); if (result != MA_SUCCESS) { return result; } return result; } MA_API ma_result ma_lpf_node_reinit(const ma_lpf_config* pConfig, ma_lpf_node* pNode) { ma_lpf_node* pLPFNode = (ma_lpf_node*)pNode; if (pNode == NULL) { return MA_INVALID_ARGS; } return ma_lpf_reinit(pConfig, &pLPFNode->lpf); } MA_API void ma_lpf_node_uninit(ma_lpf_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks) { ma_lpf_node* pLPFNode = (ma_lpf_node*)pNode; if (pNode == NULL) { return; } ma_node_uninit(pNode, pAllocationCallbacks); ma_lpf_uninit(&pLPFNode->lpf, pAllocationCallbacks); } /* High Pass Filter Node */ MA_API ma_hpf_node_config ma_hpf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order) { ma_hpf_node_config config; config.nodeConfig = ma_node_config_init(); config.hpf = ma_hpf_config_init(ma_format_f32, channels, sampleRate, cutoffFrequency, order); return config; } static void ma_hpf_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) { ma_hpf_node* pHPFNode = (ma_hpf_node*)pNode; MA_ASSERT(pNode != NULL); (void)pFrameCountIn; ma_hpf_process_pcm_frames(&pHPFNode->hpf, ppFramesOut[0], ppFramesIn[0], *pFrameCountOut); } static ma_node_vtable g_ma_hpf_node_vtable = { ma_hpf_node_process_pcm_frames, NULL, /* onGetRequiredInputFrameCount */ 1, /* One input. */ 1, /* One output. */ 0 /* Default flags. */ }; MA_API ma_result ma_hpf_node_init(ma_node_graph* pNodeGraph, const ma_hpf_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hpf_node* pNode) { ma_result result; ma_node_config baseNodeConfig; if (pNode == NULL) { return MA_INVALID_ARGS; } MA_ZERO_OBJECT(pNode); if (pConfig == NULL) { return MA_INVALID_ARGS; } if (pConfig->hpf.format != ma_format_f32) { return MA_INVALID_ARGS; /* The format must be f32. */ } result = ma_hpf_init(&pConfig->hpf, pAllocationCallbacks, &pNode->hpf); if (result != MA_SUCCESS) { return result; } baseNodeConfig = ma_node_config_init(); baseNodeConfig.vtable = &g_ma_hpf_node_vtable; baseNodeConfig.pInputChannels = &pConfig->hpf.channels; baseNodeConfig.pOutputChannels = &pConfig->hpf.channels; result = ma_node_init(pNodeGraph, &baseNodeConfig, pAllocationCallbacks, pNode); if (result != MA_SUCCESS) { return result; } return result; } MA_API ma_result ma_hpf_node_reinit(const ma_hpf_config* pConfig, ma_hpf_node* pNode) { ma_hpf_node* pHPFNode = (ma_hpf_node*)pNode; if (pNode == NULL) { return MA_INVALID_ARGS; } return ma_hpf_reinit(pConfig, &pHPFNode->hpf); } MA_API void ma_hpf_node_uninit(ma_hpf_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks) { ma_hpf_node* pHPFNode = (ma_hpf_node*)pNode; if (pNode == NULL) { return; } ma_node_uninit(pNode, pAllocationCallbacks); ma_hpf_uninit(&pHPFNode->hpf, pAllocationCallbacks); } /* Band Pass Filter Node */ MA_API ma_bpf_node_config ma_bpf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order) { ma_bpf_node_config config; config.nodeConfig = ma_node_config_init(); config.bpf = ma_bpf_config_init(ma_format_f32, channels, sampleRate, cutoffFrequency, order); return config; } static void ma_bpf_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) { ma_bpf_node* pBPFNode = (ma_bpf_node*)pNode; MA_ASSERT(pNode != NULL); (void)pFrameCountIn; ma_bpf_process_pcm_frames(&pBPFNode->bpf, ppFramesOut[0], ppFramesIn[0], *pFrameCountOut); } static ma_node_vtable g_ma_bpf_node_vtable = { ma_bpf_node_process_pcm_frames, NULL, /* onGetRequiredInputFrameCount */ 1, /* One input. */ 1, /* One output. */ 0 /* Default flags. */ }; MA_API ma_result ma_bpf_node_init(ma_node_graph* pNodeGraph, const ma_bpf_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_bpf_node* pNode) { ma_result result; ma_node_config baseNodeConfig; if (pNode == NULL) { return MA_INVALID_ARGS; } MA_ZERO_OBJECT(pNode); if (pConfig == NULL) { return MA_INVALID_ARGS; } if (pConfig->bpf.format != ma_format_f32) { return MA_INVALID_ARGS; /* The format must be f32. */ } result = ma_bpf_init(&pConfig->bpf, pAllocationCallbacks, &pNode->bpf); if (result != MA_SUCCESS) { return result; } baseNodeConfig = ma_node_config_init(); baseNodeConfig.vtable = &g_ma_bpf_node_vtable; baseNodeConfig.pInputChannels = &pConfig->bpf.channels; baseNodeConfig.pOutputChannels = &pConfig->bpf.channels; result = ma_node_init(pNodeGraph, &baseNodeConfig, pAllocationCallbacks, pNode); if (result != MA_SUCCESS) { return result; } return result; } MA_API ma_result ma_bpf_node_reinit(const ma_bpf_config* pConfig, ma_bpf_node* pNode) { ma_bpf_node* pBPFNode = (ma_bpf_node*)pNode; if (pNode == NULL) { return MA_INVALID_ARGS; } return ma_bpf_reinit(pConfig, &pBPFNode->bpf); } MA_API void ma_bpf_node_uninit(ma_bpf_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks) { ma_bpf_node* pBPFNode = (ma_bpf_node*)pNode; if (pNode == NULL) { return; } ma_node_uninit(pNode, pAllocationCallbacks); ma_bpf_uninit(&pBPFNode->bpf, pAllocationCallbacks); } /* Notching Filter Node */ MA_API ma_notch_node_config ma_notch_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double q, double frequency) { ma_notch_node_config config; config.nodeConfig = ma_node_config_init(); config.notch = ma_notch2_config_init(ma_format_f32, channels, sampleRate, q, frequency); return config; } static void ma_notch_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) { ma_notch_node* pBPFNode = (ma_notch_node*)pNode; MA_ASSERT(pNode != NULL); (void)pFrameCountIn; ma_notch2_process_pcm_frames(&pBPFNode->notch, ppFramesOut[0], ppFramesIn[0], *pFrameCountOut); } static ma_node_vtable g_ma_notch_node_vtable = { ma_notch_node_process_pcm_frames, NULL, /* onGetRequiredInputFrameCount */ 1, /* One input. */ 1, /* One output. */ 0 /* Default flags. */ }; MA_API ma_result ma_notch_node_init(ma_node_graph* pNodeGraph, const ma_notch_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_notch_node* pNode) { ma_result result; ma_node_config baseNodeConfig; if (pNode == NULL) { return MA_INVALID_ARGS; } MA_ZERO_OBJECT(pNode); if (pConfig == NULL) { return MA_INVALID_ARGS; } if (pConfig->notch.format != ma_format_f32) { return MA_INVALID_ARGS; /* The format must be f32. */ } result = ma_notch2_init(&pConfig->notch, pAllocationCallbacks, &pNode->notch); if (result != MA_SUCCESS) { return result; } baseNodeConfig = ma_node_config_init(); baseNodeConfig.vtable = &g_ma_notch_node_vtable; baseNodeConfig.pInputChannels = &pConfig->notch.channels; baseNodeConfig.pOutputChannels = &pConfig->notch.channels; result = ma_node_init(pNodeGraph, &baseNodeConfig, pAllocationCallbacks, pNode); if (result != MA_SUCCESS) { return result; } return result; } MA_API ma_result ma_notch_node_reinit(const ma_notch_config* pConfig, ma_notch_node* pNode) { ma_notch_node* pNotchNode = (ma_notch_node*)pNode; if (pNode == NULL) { return MA_INVALID_ARGS; } return ma_notch2_reinit(pConfig, &pNotchNode->notch); } MA_API void ma_notch_node_uninit(ma_notch_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks) { ma_notch_node* pNotchNode = (ma_notch_node*)pNode; if (pNode == NULL) { return; } ma_node_uninit(pNode, pAllocationCallbacks); ma_notch2_uninit(&pNotchNode->notch, pAllocationCallbacks); } /* Peaking Filter Node */ MA_API ma_peak_node_config ma_peak_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double q, double frequency) { ma_peak_node_config config; config.nodeConfig = ma_node_config_init(); config.peak = ma_peak2_config_init(ma_format_f32, channels, sampleRate, gainDB, q, frequency); return config; } static void ma_peak_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) { ma_peak_node* pBPFNode = (ma_peak_node*)pNode; MA_ASSERT(pNode != NULL); (void)pFrameCountIn; ma_peak2_process_pcm_frames(&pBPFNode->peak, ppFramesOut[0], ppFramesIn[0], *pFrameCountOut); } static ma_node_vtable g_ma_peak_node_vtable = { ma_peak_node_process_pcm_frames, NULL, /* onGetRequiredInputFrameCount */ 1, /* One input. */ 1, /* One output. */ 0 /* Default flags. */ }; MA_API ma_result ma_peak_node_init(ma_node_graph* pNodeGraph, const ma_peak_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_peak_node* pNode) { ma_result result; ma_node_config baseNodeConfig; if (pNode == NULL) { return MA_INVALID_ARGS; } MA_ZERO_OBJECT(pNode); if (pConfig == NULL) { return MA_INVALID_ARGS; } if (pConfig->peak.format != ma_format_f32) { return MA_INVALID_ARGS; /* The format must be f32. */ } result = ma_peak2_init(&pConfig->peak, pAllocationCallbacks, &pNode->peak); if (result != MA_SUCCESS) { ma_node_uninit(pNode, pAllocationCallbacks); return result; } baseNodeConfig = ma_node_config_init(); baseNodeConfig.vtable = &g_ma_peak_node_vtable; baseNodeConfig.pInputChannels = &pConfig->peak.channels; baseNodeConfig.pOutputChannels = &pConfig->peak.channels; result = ma_node_init(pNodeGraph, &baseNodeConfig, pAllocationCallbacks, pNode); if (result != MA_SUCCESS) { return result; } return result; } MA_API ma_result ma_peak_node_reinit(const ma_peak_config* pConfig, ma_peak_node* pNode) { ma_peak_node* pPeakNode = (ma_peak_node*)pNode; if (pNode == NULL) { return MA_INVALID_ARGS; } return ma_peak2_reinit(pConfig, &pPeakNode->peak); } MA_API void ma_peak_node_uninit(ma_peak_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks) { ma_peak_node* pPeakNode = (ma_peak_node*)pNode; if (pNode == NULL) { return; } ma_node_uninit(pNode, pAllocationCallbacks); ma_peak2_uninit(&pPeakNode->peak, pAllocationCallbacks); } /* Low Shelf Filter Node */ MA_API ma_loshelf_node_config ma_loshelf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double q, double frequency) { ma_loshelf_node_config config; config.nodeConfig = ma_node_config_init(); config.loshelf = ma_loshelf2_config_init(ma_format_f32, channels, sampleRate, gainDB, q, frequency); return config; } static void ma_loshelf_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) { ma_loshelf_node* pBPFNode = (ma_loshelf_node*)pNode; MA_ASSERT(pNode != NULL); (void)pFrameCountIn; ma_loshelf2_process_pcm_frames(&pBPFNode->loshelf, ppFramesOut[0], ppFramesIn[0], *pFrameCountOut); } static ma_node_vtable g_ma_loshelf_node_vtable = { ma_loshelf_node_process_pcm_frames, NULL, /* onGetRequiredInputFrameCount */ 1, /* One input. */ 1, /* One output. */ 0 /* Default flags. */ }; MA_API ma_result ma_loshelf_node_init(ma_node_graph* pNodeGraph, const ma_loshelf_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_loshelf_node* pNode) { ma_result result; ma_node_config baseNodeConfig; if (pNode == NULL) { return MA_INVALID_ARGS; } MA_ZERO_OBJECT(pNode); if (pConfig == NULL) { return MA_INVALID_ARGS; } if (pConfig->loshelf.format != ma_format_f32) { return MA_INVALID_ARGS; /* The format must be f32. */ } result = ma_loshelf2_init(&pConfig->loshelf, pAllocationCallbacks, &pNode->loshelf); if (result != MA_SUCCESS) { return result; } baseNodeConfig = ma_node_config_init(); baseNodeConfig.vtable = &g_ma_loshelf_node_vtable; baseNodeConfig.pInputChannels = &pConfig->loshelf.channels; baseNodeConfig.pOutputChannels = &pConfig->loshelf.channels; result = ma_node_init(pNodeGraph, &baseNodeConfig, pAllocationCallbacks, pNode); if (result != MA_SUCCESS) { return result; } return result; } MA_API ma_result ma_loshelf_node_reinit(const ma_loshelf_config* pConfig, ma_loshelf_node* pNode) { ma_loshelf_node* pLoshelfNode = (ma_loshelf_node*)pNode; if (pNode == NULL) { return MA_INVALID_ARGS; } return ma_loshelf2_reinit(pConfig, &pLoshelfNode->loshelf); } MA_API void ma_loshelf_node_uninit(ma_loshelf_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks) { ma_loshelf_node* pLoshelfNode = (ma_loshelf_node*)pNode; if (pNode == NULL) { return; } ma_node_uninit(pNode, pAllocationCallbacks); ma_loshelf2_uninit(&pLoshelfNode->loshelf, pAllocationCallbacks); } /* High Shelf Filter Node */ MA_API ma_hishelf_node_config ma_hishelf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double q, double frequency) { ma_hishelf_node_config config; config.nodeConfig = ma_node_config_init(); config.hishelf = ma_hishelf2_config_init(ma_format_f32, channels, sampleRate, gainDB, q, frequency); return config; } static void ma_hishelf_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) { ma_hishelf_node* pBPFNode = (ma_hishelf_node*)pNode; MA_ASSERT(pNode != NULL); (void)pFrameCountIn; ma_hishelf2_process_pcm_frames(&pBPFNode->hishelf, ppFramesOut[0], ppFramesIn[0], *pFrameCountOut); } static ma_node_vtable g_ma_hishelf_node_vtable = { ma_hishelf_node_process_pcm_frames, NULL, /* onGetRequiredInputFrameCount */ 1, /* One input. */ 1, /* One output. */ 0 /* Default flags. */ }; MA_API ma_result ma_hishelf_node_init(ma_node_graph* pNodeGraph, const ma_hishelf_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hishelf_node* pNode) { ma_result result; ma_node_config baseNodeConfig; if (pNode == NULL) { return MA_INVALID_ARGS; } MA_ZERO_OBJECT(pNode); if (pConfig == NULL) { return MA_INVALID_ARGS; } if (pConfig->hishelf.format != ma_format_f32) { return MA_INVALID_ARGS; /* The format must be f32. */ } result = ma_hishelf2_init(&pConfig->hishelf, pAllocationCallbacks, &pNode->hishelf); if (result != MA_SUCCESS) { return result; } baseNodeConfig = ma_node_config_init(); baseNodeConfig.vtable = &g_ma_hishelf_node_vtable; baseNodeConfig.pInputChannels = &pConfig->hishelf.channels; baseNodeConfig.pOutputChannels = &pConfig->hishelf.channels; result = ma_node_init(pNodeGraph, &baseNodeConfig, pAllocationCallbacks, pNode); if (result != MA_SUCCESS) { return result; } return result; } MA_API ma_result ma_hishelf_node_reinit(const ma_hishelf_config* pConfig, ma_hishelf_node* pNode) { ma_hishelf_node* pHishelfNode = (ma_hishelf_node*)pNode; if (pNode == NULL) { return MA_INVALID_ARGS; } return ma_hishelf2_reinit(pConfig, &pHishelfNode->hishelf); } MA_API void ma_hishelf_node_uninit(ma_hishelf_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks) { ma_hishelf_node* pHishelfNode = (ma_hishelf_node*)pNode; if (pNode == NULL) { return; } ma_node_uninit(pNode, pAllocationCallbacks); ma_hishelf2_uninit(&pHishelfNode->hishelf, pAllocationCallbacks); } MA_API ma_delay_node_config ma_delay_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, ma_uint32 delayInFrames, float decay) { ma_delay_node_config config; config.nodeConfig = ma_node_config_init(); config.delay = ma_delay_config_init(channels, sampleRate, delayInFrames, decay); return config; } static void ma_delay_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) { ma_delay_node* pDelayNode = (ma_delay_node*)pNode; (void)pFrameCountIn; ma_delay_process_pcm_frames(&pDelayNode->delay, ppFramesOut[0], ppFramesIn[0], *pFrameCountOut); } static ma_node_vtable g_ma_delay_node_vtable = { ma_delay_node_process_pcm_frames, NULL, 1, /* 1 input channels. */ 1, /* 1 output channel. */ MA_NODE_FLAG_CONTINUOUS_PROCESSING /* Delay requires continuous processing to ensure the tail get's processed. */ }; MA_API ma_result ma_delay_node_init(ma_node_graph* pNodeGraph, const ma_delay_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_delay_node* pDelayNode) { ma_result result; ma_node_config baseConfig; if (pDelayNode == NULL) { return MA_INVALID_ARGS; } MA_ZERO_OBJECT(pDelayNode); result = ma_delay_init(&pConfig->delay, pAllocationCallbacks, &pDelayNode->delay); if (result != MA_SUCCESS) { return result; } baseConfig = pConfig->nodeConfig; baseConfig.vtable = &g_ma_delay_node_vtable; baseConfig.pInputChannels = &pConfig->delay.channels; baseConfig.pOutputChannels = &pConfig->delay.channels; result = ma_node_init(pNodeGraph, &baseConfig, pAllocationCallbacks, &pDelayNode->baseNode); if (result != MA_SUCCESS) { ma_delay_uninit(&pDelayNode->delay, pAllocationCallbacks); return result; } return result; } MA_API void ma_delay_node_uninit(ma_delay_node* pDelayNode, const ma_allocation_callbacks* pAllocationCallbacks) { if (pDelayNode == NULL) { return; } /* The base node is always uninitialized first. */ ma_node_uninit(pDelayNode, pAllocationCallbacks); ma_delay_uninit(&pDelayNode->delay, pAllocationCallbacks); } MA_API void ma_delay_node_set_wet(ma_delay_node* pDelayNode, float value) { if (pDelayNode == NULL) { return; } ma_delay_set_wet(&pDelayNode->delay, value); } MA_API float ma_delay_node_get_wet(const ma_delay_node* pDelayNode) { if (pDelayNode == NULL) { return 0; } return ma_delay_get_wet(&pDelayNode->delay); } MA_API void ma_delay_node_set_dry(ma_delay_node* pDelayNode, float value) { if (pDelayNode == NULL) { return; } ma_delay_set_dry(&pDelayNode->delay, value); } MA_API float ma_delay_node_get_dry(const ma_delay_node* pDelayNode) { if (pDelayNode == NULL) { return 0; } return ma_delay_get_dry(&pDelayNode->delay); } MA_API void ma_delay_node_set_decay(ma_delay_node* pDelayNode, float value) { if (pDelayNode == NULL) { return; } ma_delay_set_decay(&pDelayNode->delay, value); } MA_API float ma_delay_node_get_decay(const ma_delay_node* pDelayNode) { if (pDelayNode == NULL) { return 0; } return ma_delay_get_decay(&pDelayNode->delay); } #endif /* MA_NO_NODE_GRAPH */ /* SECTION: miniaudio_engine.c */ #if !defined(MA_NO_ENGINE) && !defined(MA_NO_NODE_GRAPH) /************************************************************************************************************************************************************** Engine **************************************************************************************************************************************************************/ #define MA_SEEK_TARGET_NONE (~(ma_uint64)0) MA_API ma_engine_node_config ma_engine_node_config_init(ma_engine* pEngine, ma_engine_node_type type, ma_uint32 flags) { ma_engine_node_config config; MA_ZERO_OBJECT(&config); config.pEngine = pEngine; config.type = type; config.isPitchDisabled = (flags & MA_SOUND_FLAG_NO_PITCH) != 0; config.isSpatializationDisabled = (flags & MA_SOUND_FLAG_NO_SPATIALIZATION) != 0; config.monoExpansionMode = pEngine->monoExpansionMode; return config; } static void ma_engine_node_update_pitch_if_required(ma_engine_node* pEngineNode) { ma_bool32 isUpdateRequired = MA_FALSE; float newPitch; MA_ASSERT(pEngineNode != NULL); newPitch = c89atomic_load_explicit_f32(&pEngineNode->pitch, c89atomic_memory_order_acquire); if (pEngineNode->oldPitch != newPitch) { pEngineNode->oldPitch = newPitch; isUpdateRequired = MA_TRUE; } if (pEngineNode->oldDopplerPitch != pEngineNode->spatializer.dopplerPitch) { pEngineNode->oldDopplerPitch = pEngineNode->spatializer.dopplerPitch; isUpdateRequired = MA_TRUE; } if (isUpdateRequired) { float basePitch = (float)pEngineNode->sampleRate / ma_engine_get_sample_rate(pEngineNode->pEngine); ma_linear_resampler_set_rate_ratio(&pEngineNode->resampler, basePitch * pEngineNode->oldPitch * pEngineNode->oldDopplerPitch); } } static ma_bool32 ma_engine_node_is_pitching_enabled(const ma_engine_node* pEngineNode) { MA_ASSERT(pEngineNode != NULL); /* Don't try to be clever by skiping resampling in the pitch=1 case or else you'll glitch when moving away from 1. */ return !c89atomic_load_explicit_32(&pEngineNode->isPitchDisabled, c89atomic_memory_order_acquire); } static ma_bool32 ma_engine_node_is_spatialization_enabled(const ma_engine_node* pEngineNode) { MA_ASSERT(pEngineNode != NULL); return !c89atomic_load_explicit_32(&pEngineNode->isSpatializationDisabled, c89atomic_memory_order_acquire); } static ma_uint64 ma_engine_node_get_required_input_frame_count(const ma_engine_node* pEngineNode, ma_uint64 outputFrameCount) { ma_uint64 inputFrameCount = 0; if (ma_engine_node_is_pitching_enabled(pEngineNode)) { ma_result result = ma_linear_resampler_get_required_input_frame_count(&pEngineNode->resampler, outputFrameCount, &inputFrameCount); if (result != MA_SUCCESS) { inputFrameCount = 0; } } else { inputFrameCount = outputFrameCount; /* No resampling, so 1:1. */ } return inputFrameCount; } static void ma_engine_node_process_pcm_frames__general(ma_engine_node* pEngineNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) { ma_uint32 frameCountIn; ma_uint32 frameCountOut; ma_uint32 totalFramesProcessedIn; ma_uint32 totalFramesProcessedOut; ma_uint32 channelsIn; ma_uint32 channelsOut; ma_bool32 isPitchingEnabled; ma_bool32 isFadingEnabled; ma_bool32 isSpatializationEnabled; ma_bool32 isPanningEnabled; frameCountIn = *pFrameCountIn; frameCountOut = *pFrameCountOut; channelsIn = ma_spatializer_get_input_channels(&pEngineNode->spatializer); channelsOut = ma_spatializer_get_output_channels(&pEngineNode->spatializer); totalFramesProcessedIn = 0; totalFramesProcessedOut = 0; isPitchingEnabled = ma_engine_node_is_pitching_enabled(pEngineNode); isFadingEnabled = pEngineNode->fader.volumeBeg != 1 || pEngineNode->fader.volumeEnd != 1; isSpatializationEnabled = ma_engine_node_is_spatialization_enabled(pEngineNode); isPanningEnabled = pEngineNode->panner.pan != 0 && channelsOut != 1; /* Keep going while we've still got data available for processing. */ while (totalFramesProcessedOut < frameCountOut) { /* We need to process in a specific order. We always do resampling first because it's likely we're going to be increasing the channel count after spatialization. Also, I want to do fading based on the output sample rate. We'll first read into a buffer from the resampler. Then we'll do all processing that operates on the on the input channel count. We'll then get the spatializer to output to the output buffer and then do all effects from that point directly in the output buffer in-place. Note that we're always running the resampler. If we try to be clever and skip resampling when the pitch is 1, we'll get a glitch when we move away from 1, back to 1, and then away from 1 again. We'll want to implement any pitch=1 optimizations in the resampler itself. There's a small optimization here that we'll utilize since it might be a fairly common case. When the input and output channel counts are the same, we'll read straight into the output buffer from the resampler and do everything in-place. */ const float* pRunningFramesIn; float* pRunningFramesOut; float* pWorkingBuffer; /* This is the buffer that we'll be processing frames in. This is in input channels. */ float temp[MA_DATA_CONVERTER_STACK_BUFFER_SIZE / sizeof(float)]; ma_uint32 tempCapInFrames = ma_countof(temp) / channelsIn; ma_uint32 framesAvailableIn; ma_uint32 framesAvailableOut; ma_uint32 framesJustProcessedIn; ma_uint32 framesJustProcessedOut; ma_bool32 isWorkingBufferValid = MA_FALSE; framesAvailableIn = frameCountIn - totalFramesProcessedIn; framesAvailableOut = frameCountOut - totalFramesProcessedOut; pRunningFramesIn = ma_offset_pcm_frames_const_ptr_f32(ppFramesIn[0], totalFramesProcessedIn, channelsIn); pRunningFramesOut = ma_offset_pcm_frames_ptr_f32(ppFramesOut[0], totalFramesProcessedOut, channelsOut); if (channelsIn == channelsOut) { /* Fast path. Channel counts are the same. No need for an intermediary input buffer. */ pWorkingBuffer = pRunningFramesOut; } else { /* Slow path. Channel counts are different. Need to use an intermediary input buffer. */ pWorkingBuffer = temp; if (framesAvailableOut > tempCapInFrames) { framesAvailableOut = tempCapInFrames; } } /* First is resampler. */ if (isPitchingEnabled) { ma_uint64 resampleFrameCountIn = framesAvailableIn; ma_uint64 resampleFrameCountOut = framesAvailableOut; ma_linear_resampler_process_pcm_frames(&pEngineNode->resampler, pRunningFramesIn, &resampleFrameCountIn, pWorkingBuffer, &resampleFrameCountOut); isWorkingBufferValid = MA_TRUE; framesJustProcessedIn = (ma_uint32)resampleFrameCountIn; framesJustProcessedOut = (ma_uint32)resampleFrameCountOut; } else { framesJustProcessedIn = ma_min(framesAvailableIn, framesAvailableOut); framesJustProcessedOut = framesJustProcessedIn; /* When no resampling is being performed, the number of output frames is the same as input frames. */ } /* Fading. */ if (isFadingEnabled) { if (isWorkingBufferValid) { ma_fader_process_pcm_frames(&pEngineNode->fader, pWorkingBuffer, pWorkingBuffer, framesJustProcessedOut); /* In-place processing. */ } else { ma_fader_process_pcm_frames(&pEngineNode->fader, pWorkingBuffer, pRunningFramesIn, framesJustProcessedOut); isWorkingBufferValid = MA_TRUE; } } /* If at this point we still haven't actually done anything with the working buffer we need to just read straight from the input buffer. */ if (isWorkingBufferValid == MA_FALSE) { pWorkingBuffer = (float*)pRunningFramesIn; /* Naughty const cast, but it's safe at this point because we won't ever be writing to it from this point out. */ } /* Spatialization. */ if (isSpatializationEnabled) { ma_uint32 iListener; /* When determining the listener to use, we first check to see if the sound is pinned to a specific listener. If so, we use that. Otherwise we just use the closest listener. */ if (pEngineNode->pinnedListenerIndex != MA_LISTENER_INDEX_CLOSEST && pEngineNode->pinnedListenerIndex < ma_engine_get_listener_count(pEngineNode->pEngine)) { iListener = pEngineNode->pinnedListenerIndex; } else { iListener = ma_engine_find_closest_listener(pEngineNode->pEngine, pEngineNode->spatializer.position.x, pEngineNode->spatializer.position.y, pEngineNode->spatializer.position.z); } ma_spatializer_process_pcm_frames(&pEngineNode->spatializer, &pEngineNode->pEngine->listeners[iListener], pRunningFramesOut, pWorkingBuffer, framesJustProcessedOut); } else { /* No spatialization, but we still need to do channel conversion. */ if (channelsIn == channelsOut) { /* No channel conversion required. Just copy straight to the output buffer. */ ma_copy_pcm_frames(pRunningFramesOut, pWorkingBuffer, framesJustProcessedOut, ma_format_f32, channelsOut); } else { /* Channel conversion required. TODO: Add support for channel maps here. */ ma_channel_map_apply_f32(pRunningFramesOut, NULL, channelsOut, pWorkingBuffer, NULL, channelsIn, framesJustProcessedOut, ma_channel_mix_mode_simple, pEngineNode->monoExpansionMode); } } /* At this point we can guarantee that the output buffer contains valid data. We can process everything in place now. */ /* Panning. */ if (isPanningEnabled) { ma_panner_process_pcm_frames(&pEngineNode->panner, pRunningFramesOut, pRunningFramesOut, framesJustProcessedOut); /* In-place processing. */ } /* We're done for this chunk. */ totalFramesProcessedIn += framesJustProcessedIn; totalFramesProcessedOut += framesJustProcessedOut; /* If we didn't process any output frames this iteration it means we've either run out of input data, or run out of room in the output buffer. */ if (framesJustProcessedOut == 0) { break; } } /* At this point we're done processing. */ *pFrameCountIn = totalFramesProcessedIn; *pFrameCountOut = totalFramesProcessedOut; } static void ma_engine_node_process_pcm_frames__sound(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) { /* For sounds, we need to first read from the data source. Then we need to apply the engine effects (pan, pitch, fades, etc.). */ ma_result result = MA_SUCCESS; ma_sound* pSound = (ma_sound*)pNode; ma_uint32 frameCount = *pFrameCountOut; ma_uint32 totalFramesRead = 0; ma_format dataSourceFormat; ma_uint32 dataSourceChannels; ma_uint8 temp[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; ma_uint32 tempCapInFrames; ma_uint64 seekTarget; /* This is a data source node which means no input buses. */ (void)ppFramesIn; (void)pFrameCountIn; /* If we're marked at the end we need to stop the sound and do nothing. */ if (ma_sound_at_end(pSound)) { ma_sound_stop(pSound); *pFrameCountOut = 0; return; } /* If we're seeking, do so now before reading. */ seekTarget = c89atomic_load_64(&pSound->seekTarget); if (seekTarget != MA_SEEK_TARGET_NONE) { ma_data_source_seek_to_pcm_frame(pSound->pDataSource, seekTarget); /* Any time-dependant effects need to have their times updated. */ ma_node_set_time(pSound, seekTarget); c89atomic_exchange_64(&pSound->seekTarget, MA_SEEK_TARGET_NONE); } /* We want to update the pitch once. For sounds, this can be either at the start or at the end. If we don't force this to only ever be updating once, we could end up in a situation where retrieving the required input frame count ends up being different to what we actually retrieve. What could happen is that the required input frame count is calculated, the pitch is update, and then this processing function is called resulting in a different number of input frames being processed. Do not call this in ma_engine_node_process_pcm_frames__general() or else you'll hit the aforementioned bug. */ ma_engine_node_update_pitch_if_required(&pSound->engineNode); /* For the convenience of the caller, we're doing to allow data sources to use non-floating-point formats and channel counts that differ from the main engine. */ result = ma_data_source_get_data_format(pSound->pDataSource, &dataSourceFormat, &dataSourceChannels, NULL, NULL, 0); if (result == MA_SUCCESS) { tempCapInFrames = sizeof(temp) / ma_get_bytes_per_frame(dataSourceFormat, dataSourceChannels); /* Keep reading until we've read as much as was requested or we reach the end of the data source. */ while (totalFramesRead < frameCount) { ma_uint32 framesRemaining = frameCount - totalFramesRead; ma_uint32 framesToRead; ma_uint64 framesJustRead; ma_uint32 frameCountIn; ma_uint32 frameCountOut; const float* pRunningFramesIn; float* pRunningFramesOut; /* The first thing we need to do is read into the temporary buffer. We can calculate exactly how many input frames we'll need after resampling. */ framesToRead = (ma_uint32)ma_engine_node_get_required_input_frame_count(&pSound->engineNode, framesRemaining); if (framesToRead > tempCapInFrames) { framesToRead = tempCapInFrames; } result = ma_data_source_read_pcm_frames(pSound->pDataSource, temp, framesToRead, &framesJustRead); /* If we reached the end of the sound we'll want to mark it as at the end and stop it. This should never be returned for looping sounds. */ if (result == MA_AT_END) { c89atomic_exchange_32(&pSound->atEnd, MA_TRUE); /* This will be set to false in ma_sound_start(). */ } pRunningFramesOut = ma_offset_pcm_frames_ptr_f32(ppFramesOut[0], totalFramesRead, ma_engine_get_channels(ma_sound_get_engine(pSound))); frameCountIn = (ma_uint32)framesJustRead; frameCountOut = framesRemaining; /* Convert if necessary. */ if (dataSourceFormat == ma_format_f32) { /* Fast path. No data conversion necessary. */ pRunningFramesIn = (float*)temp; ma_engine_node_process_pcm_frames__general(&pSound->engineNode, &pRunningFramesIn, &frameCountIn, &pRunningFramesOut, &frameCountOut); } else { /* Slow path. Need to do sample format conversion to f32. If we give the f32 buffer the same count as the first temp buffer, we're guaranteed it'll be large enough. */ float tempf32[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; /* Do not do `MA_DATA_CONVERTER_STACK_BUFFER_SIZE/sizeof(float)` here like we've done in other places. */ ma_convert_pcm_frames_format(tempf32, ma_format_f32, temp, dataSourceFormat, framesJustRead, dataSourceChannels, ma_dither_mode_none); /* Now that we have our samples in f32 format we can process like normal. */ pRunningFramesIn = tempf32; ma_engine_node_process_pcm_frames__general(&pSound->engineNode, &pRunningFramesIn, &frameCountIn, &pRunningFramesOut, &frameCountOut); } /* We should have processed all of our input frames since we calculated the required number of input frames at the top. */ MA_ASSERT(frameCountIn == framesJustRead); totalFramesRead += (ma_uint32)frameCountOut; /* Safe cast. */ if (result != MA_SUCCESS || ma_sound_at_end(pSound)) { break; /* Might have reached the end. */ } } } *pFrameCountOut = totalFramesRead; } static void ma_engine_node_process_pcm_frames__group(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) { /* Make sure the pitch is updated before trying to read anything. It's important that this is done only once and not in ma_engine_node_process_pcm_frames__general(). The reason for this is that ma_engine_node_process_pcm_frames__general() will call ma_engine_node_get_required_input_frame_count(), and if another thread modifies the pitch just after that call it can result in a glitch due to the input rate changing. */ ma_engine_node_update_pitch_if_required((ma_engine_node*)pNode); /* For groups, the input data has already been read and we just need to apply the effect. */ ma_engine_node_process_pcm_frames__general((ma_engine_node*)pNode, ppFramesIn, pFrameCountIn, ppFramesOut, pFrameCountOut); } static ma_result ma_engine_node_get_required_input_frame_count__group(ma_node* pNode, ma_uint32 outputFrameCount, ma_uint32* pInputFrameCount) { ma_uint64 inputFrameCount; MA_ASSERT(pInputFrameCount != NULL); /* Our pitch will affect this calculation. We need to update it. */ ma_engine_node_update_pitch_if_required((ma_engine_node*)pNode); inputFrameCount = ma_engine_node_get_required_input_frame_count((ma_engine_node*)pNode, outputFrameCount); if (inputFrameCount > 0xFFFFFFFF) { inputFrameCount = 0xFFFFFFFF; /* Will never happen because miniaudio will only ever process in relatively small chunks. */ } *pInputFrameCount = (ma_uint32)inputFrameCount; return MA_SUCCESS; } static ma_node_vtable g_ma_engine_node_vtable__sound = { ma_engine_node_process_pcm_frames__sound, NULL, /* onGetRequiredInputFrameCount */ 0, /* Sounds are data source nodes which means they have zero inputs (their input is drawn from the data source itself). */ 1, /* Sounds have one output bus. */ 0 /* Default flags. */ }; static ma_node_vtable g_ma_engine_node_vtable__group = { ma_engine_node_process_pcm_frames__group, ma_engine_node_get_required_input_frame_count__group, 1, /* Groups have one input bus. */ 1, /* Groups have one output bus. */ MA_NODE_FLAG_DIFFERENT_PROCESSING_RATES /* The engine node does resampling so should let miniaudio know about it. */ }; static ma_node_config ma_engine_node_base_node_config_init(const ma_engine_node_config* pConfig) { ma_node_config baseNodeConfig; if (pConfig->type == ma_engine_node_type_sound) { /* Sound. */ baseNodeConfig = ma_node_config_init(); baseNodeConfig.vtable = &g_ma_engine_node_vtable__sound; baseNodeConfig.initialState = ma_node_state_stopped; /* Sounds are stopped by default. */ } else { /* Group. */ baseNodeConfig = ma_node_config_init(); baseNodeConfig.vtable = &g_ma_engine_node_vtable__group; baseNodeConfig.initialState = ma_node_state_started; /* Groups are started by default. */ } return baseNodeConfig; } static ma_spatializer_config ma_engine_node_spatializer_config_init(const ma_node_config* pBaseNodeConfig) { return ma_spatializer_config_init(pBaseNodeConfig->pInputChannels[0], pBaseNodeConfig->pOutputChannels[0]); } typedef struct { size_t sizeInBytes; size_t baseNodeOffset; size_t resamplerOffset; size_t spatializerOffset; } ma_engine_node_heap_layout; static ma_result ma_engine_node_get_heap_layout(const ma_engine_node_config* pConfig, ma_engine_node_heap_layout* pHeapLayout) { ma_result result; size_t tempHeapSize; ma_node_config baseNodeConfig; ma_linear_resampler_config resamplerConfig; ma_spatializer_config spatializerConfig; ma_uint32 channelsIn; ma_uint32 channelsOut; MA_ASSERT(pHeapLayout); MA_ZERO_OBJECT(pHeapLayout); if (pConfig == NULL) { return MA_INVALID_ARGS; } if (pConfig->pEngine == NULL) { return MA_INVALID_ARGS; /* An engine must be specified. */ } pHeapLayout->sizeInBytes = 0; channelsIn = (pConfig->channelsIn != 0) ? pConfig->channelsIn : ma_engine_get_channels(pConfig->pEngine); channelsOut = (pConfig->channelsOut != 0) ? pConfig->channelsOut : ma_engine_get_channels(pConfig->pEngine); /* Base node. */ baseNodeConfig = ma_engine_node_base_node_config_init(pConfig); baseNodeConfig.pInputChannels = &channelsIn; baseNodeConfig.pOutputChannels = &channelsOut; result = ma_node_get_heap_size(ma_engine_get_node_graph(pConfig->pEngine), &baseNodeConfig, &tempHeapSize); if (result != MA_SUCCESS) { return result; /* Failed to retrieve the size of the heap for the base node. */ } pHeapLayout->baseNodeOffset = pHeapLayout->sizeInBytes; pHeapLayout->sizeInBytes += ma_align_64(tempHeapSize); /* Resmapler. */ resamplerConfig = ma_linear_resampler_config_init(ma_format_f32, channelsIn, 1, 1); /* Input and output sample rates don't affect the calculation of the heap size. */ resamplerConfig.lpfOrder = 0; result = ma_linear_resampler_get_heap_size(&resamplerConfig, &tempHeapSize); if (result != MA_SUCCESS) { return result; /* Failed to retrieve the size of the heap for the resampler. */ } pHeapLayout->resamplerOffset = pHeapLayout->sizeInBytes; pHeapLayout->sizeInBytes += ma_align_64(tempHeapSize); /* Spatializer. */ spatializerConfig = ma_engine_node_spatializer_config_init(&baseNodeConfig); result = ma_spatializer_get_heap_size(&spatializerConfig, &tempHeapSize); if (result != MA_SUCCESS) { return result; /* Failed to retrieve the size of the heap for the spatializer. */ } pHeapLayout->spatializerOffset = pHeapLayout->sizeInBytes; pHeapLayout->sizeInBytes += ma_align_64(tempHeapSize); return MA_SUCCESS; } MA_API ma_result ma_engine_node_get_heap_size(const ma_engine_node_config* pConfig, size_t* pHeapSizeInBytes) { ma_result result; ma_engine_node_heap_layout heapLayout; if (pHeapSizeInBytes == NULL) { return MA_INVALID_ARGS; } *pHeapSizeInBytes = 0; result = ma_engine_node_get_heap_layout(pConfig, &heapLayout); if (result != MA_SUCCESS) { return result; } *pHeapSizeInBytes = heapLayout.sizeInBytes; return MA_SUCCESS; } MA_API ma_result ma_engine_node_init_preallocated(const ma_engine_node_config* pConfig, void* pHeap, ma_engine_node* pEngineNode) { ma_result result; ma_engine_node_heap_layout heapLayout; ma_node_config baseNodeConfig; ma_linear_resampler_config resamplerConfig; ma_fader_config faderConfig; ma_spatializer_config spatializerConfig; ma_panner_config pannerConfig; ma_uint32 channelsIn; ma_uint32 channelsOut; if (pEngineNode == NULL) { return MA_INVALID_ARGS; } MA_ZERO_OBJECT(pEngineNode); result = ma_engine_node_get_heap_layout(pConfig, &heapLayout); if (result != MA_SUCCESS) { return result; } if (pConfig->pinnedListenerIndex != MA_LISTENER_INDEX_CLOSEST && pConfig->pinnedListenerIndex >= ma_engine_get_listener_count(pConfig->pEngine)) { return MA_INVALID_ARGS; /* Invalid listener. */ } pEngineNode->_pHeap = pHeap; MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes); pEngineNode->pEngine = pConfig->pEngine; pEngineNode->sampleRate = (pConfig->sampleRate > 0) ? pConfig->sampleRate : ma_engine_get_sample_rate(pEngineNode->pEngine); pEngineNode->monoExpansionMode = pConfig->monoExpansionMode; pEngineNode->pitch = 1; pEngineNode->oldPitch = 1; pEngineNode->oldDopplerPitch = 1; pEngineNode->isPitchDisabled = pConfig->isPitchDisabled; pEngineNode->isSpatializationDisabled = pConfig->isSpatializationDisabled; pEngineNode->pinnedListenerIndex = pConfig->pinnedListenerIndex; channelsIn = (pConfig->channelsIn != 0) ? pConfig->channelsIn : ma_engine_get_channels(pConfig->pEngine); channelsOut = (pConfig->channelsOut != 0) ? pConfig->channelsOut : ma_engine_get_channels(pConfig->pEngine); /* Base node. */ baseNodeConfig = ma_engine_node_base_node_config_init(pConfig); baseNodeConfig.pInputChannels = &channelsIn; baseNodeConfig.pOutputChannels = &channelsOut; result = ma_node_init_preallocated(&pConfig->pEngine->nodeGraph, &baseNodeConfig, ma_offset_ptr(pHeap, heapLayout.baseNodeOffset), &pEngineNode->baseNode); if (result != MA_SUCCESS) { goto error0; } /* We can now initialize the effects we need in order to implement the engine node. There's a defined order of operations here, mainly centered around when we convert our channels from the data source's native channel count to the engine's channel count. As a rule, we want to do as much computation as possible before spatialization because there's a chance that will increase the channel count, thereby increasing the amount of work needing to be done to process. */ /* We'll always do resampling first. */ resamplerConfig = ma_linear_resampler_config_init(ma_format_f32, baseNodeConfig.pInputChannels[0], pEngineNode->sampleRate, ma_engine_get_sample_rate(pEngineNode->pEngine)); resamplerConfig.lpfOrder = 0; /* <-- Need to disable low-pass filtering for pitch shifting for now because there's cases where the biquads are becoming unstable. Need to figure out a better fix for this. */ result = ma_linear_resampler_init_preallocated(&resamplerConfig, ma_offset_ptr(pHeap, heapLayout.resamplerOffset), &pEngineNode->resampler); if (result != MA_SUCCESS) { goto error1; } /* After resampling will come the fader. */ faderConfig = ma_fader_config_init(ma_format_f32, baseNodeConfig.pInputChannels[0], ma_engine_get_sample_rate(pEngineNode->pEngine)); result = ma_fader_init(&faderConfig, &pEngineNode->fader); if (result != MA_SUCCESS) { goto error2; } /* Spatialization comes next. We spatialize based ont he node's output channel count. It's up the caller to ensure channels counts link up correctly in the node graph. */ spatializerConfig = ma_engine_node_spatializer_config_init(&baseNodeConfig); spatializerConfig.gainSmoothTimeInFrames = pEngineNode->pEngine->gainSmoothTimeInFrames; result = ma_spatializer_init_preallocated(&spatializerConfig, ma_offset_ptr(pHeap, heapLayout.spatializerOffset), &pEngineNode->spatializer); if (result != MA_SUCCESS) { goto error2; } /* After spatialization comes panning. We need to do this after spatialization because otherwise we wouldn't be able to pan mono sounds. */ pannerConfig = ma_panner_config_init(ma_format_f32, baseNodeConfig.pOutputChannels[0]); result = ma_panner_init(&pannerConfig, &pEngineNode->panner); if (result != MA_SUCCESS) { goto error3; } return MA_SUCCESS; /* No need for allocation callbacks here because we use a preallocated heap. */ error3: ma_spatializer_uninit(&pEngineNode->spatializer, NULL); error2: ma_linear_resampler_uninit(&pEngineNode->resampler, NULL); error1: ma_node_uninit(&pEngineNode->baseNode, NULL); error0: return result; } MA_API ma_result ma_engine_node_init(const ma_engine_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_engine_node* pEngineNode) { ma_result result; size_t heapSizeInBytes; void* pHeap; result = ma_engine_node_get_heap_size(pConfig, &heapSizeInBytes); if (result != MA_SUCCESS) { return result; } if (heapSizeInBytes > 0) { pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks); if (pHeap == NULL) { return MA_OUT_OF_MEMORY; } } else { pHeap = NULL; } result = ma_engine_node_init_preallocated(pConfig, pHeap, pEngineNode); if (result != MA_SUCCESS) { ma_free(pHeap, pAllocationCallbacks); return result; } pEngineNode->_ownsHeap = MA_TRUE; return MA_SUCCESS; } MA_API void ma_engine_node_uninit(ma_engine_node* pEngineNode, const ma_allocation_callbacks* pAllocationCallbacks) { /* The base node always needs to be uninitialized first to ensure it's detached from the graph completely before we destroy anything that might be in the middle of being used by the processing function. */ ma_node_uninit(&pEngineNode->baseNode, pAllocationCallbacks); /* Now that the node has been uninitialized we can safely uninitialize the rest. */ ma_spatializer_uninit(&pEngineNode->spatializer, pAllocationCallbacks); ma_linear_resampler_uninit(&pEngineNode->resampler, pAllocationCallbacks); /* Free the heap last. */ if (pEngineNode->_ownsHeap) { ma_free(pEngineNode->_pHeap, pAllocationCallbacks); } } MA_API ma_sound_config ma_sound_config_init(void) { return ma_sound_config_init_2(NULL); } MA_API ma_sound_config ma_sound_config_init_2(ma_engine* pEngine) { ma_sound_config config; MA_ZERO_OBJECT(&config); if (pEngine != NULL) { config.monoExpansionMode = pEngine->monoExpansionMode; } else { config.monoExpansionMode = ma_mono_expansion_mode_default; } config.rangeEndInPCMFrames = ~((ma_uint64)0); config.loopPointEndInPCMFrames = ~((ma_uint64)0); return config; } MA_API ma_sound_group_config ma_sound_group_config_init(void) { return ma_sound_group_config_init_2(NULL); } MA_API ma_sound_group_config ma_sound_group_config_init_2(ma_engine* pEngine) { ma_sound_group_config config; MA_ZERO_OBJECT(&config); if (pEngine != NULL) { config.monoExpansionMode = pEngine->monoExpansionMode; } else { config.monoExpansionMode = ma_mono_expansion_mode_default; } return config; } MA_API ma_engine_config ma_engine_config_init(void) { ma_engine_config config; MA_ZERO_OBJECT(&config); config.listenerCount = 1; /* Always want at least one listener. */ config.monoExpansionMode = ma_mono_expansion_mode_default; return config; } #if !defined(MA_NO_DEVICE_IO) static void ma_engine_data_callback_internal(ma_device* pDevice, void* pFramesOut, const void* pFramesIn, ma_uint32 frameCount) { ma_engine* pEngine = (ma_engine*)pDevice->pUserData; (void)pFramesIn; /* Experiment: Try processing a resource manager job if we're on the Emscripten build. This serves two purposes: 1) It ensures jobs are actually processed at some point since we cannot guarantee that the caller is doing the right thing and calling ma_resource_manager_process_next_job(); and 2) It's an attempt at working around an issue where processing jobs on the Emscripten main loop doesn't work as well as it should. When trying to load sounds without the `DECODE` flag or with the `ASYNC` flag, the sound data is just not able to be loaded in time before the callback is processed. I think it's got something to do with the single- threaded nature of Web, but I'm not entirely sure. */ #if !defined(MA_NO_RESOURCE_MANAGER) && defined(MA_EMSCRIPTEN) { if (pEngine->pResourceManager != NULL) { if ((pEngine->pResourceManager->config.flags & MA_RESOURCE_MANAGER_FLAG_NO_THREADING) != 0) { ma_resource_manager_process_next_job(pEngine->pResourceManager); } } } #endif ma_engine_read_pcm_frames(pEngine, pFramesOut, frameCount, NULL); } #endif MA_API ma_result ma_engine_init(const ma_engine_config* pConfig, ma_engine* pEngine) { ma_result result; ma_node_graph_config nodeGraphConfig; ma_engine_config engineConfig; ma_spatializer_listener_config listenerConfig; ma_uint32 iListener; if (pEngine == NULL) { return MA_INVALID_ARGS; } MA_ZERO_OBJECT(pEngine); /* The config is allowed to be NULL in which case we use defaults for everything. */ if (pConfig != NULL) { engineConfig = *pConfig; } else { engineConfig = ma_engine_config_init(); } pEngine->monoExpansionMode = engineConfig.monoExpansionMode; ma_allocation_callbacks_init_copy(&pEngine->allocationCallbacks, &engineConfig.allocationCallbacks); #if !defined(MA_NO_RESOURCE_MANAGER) { pEngine->pResourceManager = engineConfig.pResourceManager; } #endif #if !defined(MA_NO_DEVICE_IO) { pEngine->pDevice = engineConfig.pDevice; /* If we don't have a device, we need one. */ if (pEngine->pDevice == NULL && engineConfig.noDevice == MA_FALSE) { ma_device_config deviceConfig; pEngine->pDevice = (ma_device*)ma_malloc(sizeof(*pEngine->pDevice), &pEngine->allocationCallbacks); if (pEngine->pDevice == NULL) { return MA_OUT_OF_MEMORY; } deviceConfig = ma_device_config_init(ma_device_type_playback); deviceConfig.playback.pDeviceID = engineConfig.pPlaybackDeviceID; deviceConfig.playback.format = ma_format_f32; deviceConfig.playback.channels = engineConfig.channels; deviceConfig.sampleRate = engineConfig.sampleRate; deviceConfig.dataCallback = ma_engine_data_callback_internal; deviceConfig.pUserData = pEngine; deviceConfig.notificationCallback = engineConfig.notificationCallback; deviceConfig.periodSizeInFrames = engineConfig.periodSizeInFrames; deviceConfig.periodSizeInMilliseconds = engineConfig.periodSizeInMilliseconds; deviceConfig.noPreSilencedOutputBuffer = MA_TRUE; /* We'll always be outputting to every frame in the callback so there's no need for a pre-silenced buffer. */ deviceConfig.noClip = MA_TRUE; /* The engine will do clipping itself. */ if (engineConfig.pContext == NULL) { ma_context_config contextConfig = ma_context_config_init(); contextConfig.allocationCallbacks = pEngine->allocationCallbacks; contextConfig.pLog = engineConfig.pLog; /* If the engine config does not specify a log, use the resource manager's if we have one. */ #ifndef MA_NO_RESOURCE_MANAGER { if (contextConfig.pLog == NULL && engineConfig.pResourceManager != NULL) { contextConfig.pLog = ma_resource_manager_get_log(engineConfig.pResourceManager); } } #endif result = ma_device_init_ex(NULL, 0, &contextConfig, &deviceConfig, pEngine->pDevice); } else { result = ma_device_init(engineConfig.pContext, &deviceConfig, pEngine->pDevice); } if (result != MA_SUCCESS) { ma_free(pEngine->pDevice, &pEngine->allocationCallbacks); pEngine->pDevice = NULL; return result; } pEngine->ownsDevice = MA_TRUE; } /* Update the channel count and sample rate of the engine config so we can reference it below. */ if (pEngine->pDevice != NULL) { engineConfig.channels = pEngine->pDevice->playback.channels; engineConfig.sampleRate = pEngine->pDevice->sampleRate; } } #endif if (engineConfig.channels == 0 || engineConfig.sampleRate == 0) { return MA_INVALID_ARGS; } pEngine->sampleRate = engineConfig.sampleRate; /* The engine always uses either the log that was passed into the config, or the context's log is available. */ if (engineConfig.pLog != NULL) { pEngine->pLog = engineConfig.pLog; } else { #if !defined(MA_NO_DEVICE_IO) { pEngine->pLog = ma_device_get_log(pEngine->pDevice); } #else { pEngine->pLog = NULL; } #endif } /* The engine is a node graph. This needs to be initialized after we have the device so we can can determine the channel count. */ nodeGraphConfig = ma_node_graph_config_init(engineConfig.channels); nodeGraphConfig.nodeCacheCapInFrames = (engineConfig.periodSizeInFrames > 0xFFFF) ? 0xFFFF : (ma_uint16)engineConfig.periodSizeInFrames; result = ma_node_graph_init(&nodeGraphConfig, &pEngine->allocationCallbacks, &pEngine->nodeGraph); if (result != MA_SUCCESS) { goto on_error_1; } /* We need at least one listener. */ if (engineConfig.listenerCount == 0) { engineConfig.listenerCount = 1; } if (engineConfig.listenerCount > MA_ENGINE_MAX_LISTENERS) { result = MA_INVALID_ARGS; /* Too many listeners. */ goto on_error_1; } for (iListener = 0; iListener < engineConfig.listenerCount; iListener += 1) { listenerConfig = ma_spatializer_listener_config_init(ma_node_graph_get_channels(&pEngine->nodeGraph)); /* If we're using a device, use the device's channel map for the listener. Otherwise just use miniaudio's default channel map. */ #if !defined(MA_NO_DEVICE_IO) { if (pEngine->pDevice != NULL) { /* Temporarily disabled. There is a subtle bug here where front-left and front-right will be used by the device's channel map, but this is not what we want to use for spatialization. Instead we want to use side-left and side-right. I need to figure out a better solution for this. For now, disabling the user of device channel maps. */ /*listenerConfig.pChannelMapOut = pEngine->pDevice->playback.channelMap;*/ } } #endif result = ma_spatializer_listener_init(&listenerConfig, &pEngine->allocationCallbacks, &pEngine->listeners[iListener]); /* TODO: Change this to a pre-allocated heap. */ if (result != MA_SUCCESS) { goto on_error_2; } pEngine->listenerCount += 1; } /* Gain smoothing for spatialized sounds. */ pEngine->gainSmoothTimeInFrames = engineConfig.gainSmoothTimeInFrames; if (pEngine->gainSmoothTimeInFrames == 0) { ma_uint32 gainSmoothTimeInMilliseconds = engineConfig.gainSmoothTimeInMilliseconds; if (gainSmoothTimeInMilliseconds == 0) { gainSmoothTimeInMilliseconds = 8; } pEngine->gainSmoothTimeInFrames = (gainSmoothTimeInMilliseconds * ma_engine_get_sample_rate(pEngine)) / 1000; /* 8ms by default. */ } /* We need a resource manager. */ #ifndef MA_NO_RESOURCE_MANAGER { if (pEngine->pResourceManager == NULL) { ma_resource_manager_config resourceManagerConfig; pEngine->pResourceManager = (ma_resource_manager*)ma_malloc(sizeof(*pEngine->pResourceManager), &pEngine->allocationCallbacks); if (pEngine->pResourceManager == NULL) { result = MA_OUT_OF_MEMORY; goto on_error_2; } resourceManagerConfig = ma_resource_manager_config_init(); resourceManagerConfig.pLog = pEngine->pLog; /* Always use the engine's log for internally-managed resource managers. */ resourceManagerConfig.decodedFormat = ma_format_f32; resourceManagerConfig.decodedChannels = 0; /* Leave the decoded channel count as 0 so we can get good spatialization. */ resourceManagerConfig.decodedSampleRate = ma_engine_get_sample_rate(pEngine); ma_allocation_callbacks_init_copy(&resourceManagerConfig.allocationCallbacks, &pEngine->allocationCallbacks); resourceManagerConfig.pVFS = engineConfig.pResourceManagerVFS; /* The Emscripten build cannot use threads. */ #if defined(MA_EMSCRIPTEN) { resourceManagerConfig.jobThreadCount = 0; resourceManagerConfig.flags |= MA_RESOURCE_MANAGER_FLAG_NO_THREADING; } #endif result = ma_resource_manager_init(&resourceManagerConfig, pEngine->pResourceManager); if (result != MA_SUCCESS) { goto on_error_3; } pEngine->ownsResourceManager = MA_TRUE; } } #endif /* Setup some stuff for inlined sounds. That is sounds played with ma_engine_play_sound(). */ pEngine->inlinedSoundLock = 0; pEngine->pInlinedSoundHead = NULL; /* Start the engine if required. This should always be the last step. */ #if !defined(MA_NO_DEVICE_IO) { if (engineConfig.noAutoStart == MA_FALSE && pEngine->pDevice != NULL) { result = ma_engine_start(pEngine); if (result != MA_SUCCESS) { goto on_error_4; /* Failed to start the engine. */ } } } #endif return MA_SUCCESS; #if !defined(MA_NO_DEVICE_IO) on_error_4: #endif #if !defined(MA_NO_RESOURCE_MANAGER) on_error_3: if (pEngine->ownsResourceManager) { ma_free(pEngine->pResourceManager, &pEngine->allocationCallbacks); } #endif /* MA_NO_RESOURCE_MANAGER */ on_error_2: for (iListener = 0; iListener < pEngine->listenerCount; iListener += 1) { ma_spatializer_listener_uninit(&pEngine->listeners[iListener], &pEngine->allocationCallbacks); } ma_node_graph_uninit(&pEngine->nodeGraph, &pEngine->allocationCallbacks); on_error_1: #if !defined(MA_NO_DEVICE_IO) { if (pEngine->ownsDevice) { ma_device_uninit(pEngine->pDevice); ma_free(pEngine->pDevice, &pEngine->allocationCallbacks); } } #endif return result; } MA_API void ma_engine_uninit(ma_engine* pEngine) { ma_uint32 iListener; if (pEngine == NULL) { return; } /* The device must be uninitialized before the node graph to ensure the audio thread doesn't try accessing it. */ #if !defined(MA_NO_DEVICE_IO) { if (pEngine->ownsDevice) { ma_device_uninit(pEngine->pDevice); ma_free(pEngine->pDevice, &pEngine->allocationCallbacks); } else { if (pEngine->pDevice != NULL) { ma_device_stop(pEngine->pDevice); } } } #endif /* All inlined sounds need to be deleted. I'm going to use a lock here just to future proof in case I want to do some kind of garbage collection later on. */ ma_spinlock_lock(&pEngine->inlinedSoundLock); { for (;;) { ma_sound_inlined* pSoundToDelete = pEngine->pInlinedSoundHead; if (pSoundToDelete == NULL) { break; /* Done. */ } pEngine->pInlinedSoundHead = pSoundToDelete->pNext; ma_sound_uninit(&pSoundToDelete->sound); ma_free(pSoundToDelete, &pEngine->allocationCallbacks); } } ma_spinlock_unlock(&pEngine->inlinedSoundLock); for (iListener = 0; iListener < pEngine->listenerCount; iListener += 1) { ma_spatializer_listener_uninit(&pEngine->listeners[iListener], &pEngine->allocationCallbacks); } /* Make sure the node graph is uninitialized after the audio thread has been shutdown to prevent accessing of the node graph after being uninitialized. */ ma_node_graph_uninit(&pEngine->nodeGraph, &pEngine->allocationCallbacks); /* Uninitialize the resource manager last to ensure we don't have a thread still trying to access it. */ #ifndef MA_NO_RESOURCE_MANAGER if (pEngine->ownsResourceManager) { ma_resource_manager_uninit(pEngine->pResourceManager); ma_free(pEngine->pResourceManager, &pEngine->allocationCallbacks); } #endif } MA_API ma_result ma_engine_read_pcm_frames(ma_engine* pEngine, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) { return ma_node_graph_read_pcm_frames(&pEngine->nodeGraph, pFramesOut, frameCount, pFramesRead); } MA_API ma_node_graph* ma_engine_get_node_graph(ma_engine* pEngine) { if (pEngine == NULL) { return NULL; } return &pEngine->nodeGraph; } #if !defined(MA_NO_RESOURCE_MANAGER) MA_API ma_resource_manager* ma_engine_get_resource_manager(ma_engine* pEngine) { if (pEngine == NULL) { return NULL; } #if !defined(MA_NO_RESOURCE_MANAGER) { return pEngine->pResourceManager; } #else { return NULL; } #endif } #endif MA_API ma_device* ma_engine_get_device(ma_engine* pEngine) { if (pEngine == NULL) { return NULL; } #if !defined(MA_NO_DEVICE_IO) { return pEngine->pDevice; } #else { return NULL; } #endif } MA_API ma_log* ma_engine_get_log(ma_engine* pEngine) { if (pEngine == NULL) { return NULL; } if (pEngine->pLog != NULL) { return pEngine->pLog; } else { #if !defined(MA_NO_DEVICE_IO) { return ma_device_get_log(ma_engine_get_device(pEngine)); } #else { return NULL; } #endif } } MA_API ma_node* ma_engine_get_endpoint(ma_engine* pEngine) { return ma_node_graph_get_endpoint(&pEngine->nodeGraph); } MA_API ma_uint64 ma_engine_get_time(const ma_engine* pEngine) { return ma_node_graph_get_time(&pEngine->nodeGraph); } MA_API ma_result ma_engine_set_time(ma_engine* pEngine, ma_uint64 globalTime) { return ma_node_graph_set_time(&pEngine->nodeGraph, globalTime); } MA_API ma_uint32 ma_engine_get_channels(const ma_engine* pEngine) { return ma_node_graph_get_channels(&pEngine->nodeGraph); } MA_API ma_uint32 ma_engine_get_sample_rate(const ma_engine* pEngine) { if (pEngine == NULL) { return 0; } return pEngine->sampleRate; } MA_API ma_result ma_engine_start(ma_engine* pEngine) { ma_result result; if (pEngine == NULL) { return MA_INVALID_ARGS; } #if !defined(MA_NO_DEVICE_IO) { if (pEngine->pDevice != NULL) { result = ma_device_start(pEngine->pDevice); } else { result = MA_INVALID_OPERATION; /* The engine is running without a device which means there's no real notion of "starting" the engine. */ } } #else { result = MA_INVALID_OPERATION; /* Device IO is disabled, so there's no real notion of "starting" the engine. */ } #endif if (result != MA_SUCCESS) { return result; } return MA_SUCCESS; } MA_API ma_result ma_engine_stop(ma_engine* pEngine) { ma_result result; if (pEngine == NULL) { return MA_INVALID_ARGS; } #if !defined(MA_NO_DEVICE_IO) { if (pEngine->pDevice != NULL) { result = ma_device_stop(pEngine->pDevice); } else { result = MA_INVALID_OPERATION; /* The engine is running without a device which means there's no real notion of "stopping" the engine. */ } } #else { result = MA_INVALID_OPERATION; /* Device IO is disabled, so there's no real notion of "stopping" the engine. */ } #endif if (result != MA_SUCCESS) { return result; } return MA_SUCCESS; } MA_API ma_result ma_engine_set_volume(ma_engine* pEngine, float volume) { if (pEngine == NULL) { return MA_INVALID_ARGS; } return ma_node_set_output_bus_volume(ma_node_graph_get_endpoint(&pEngine->nodeGraph), 0, volume); } MA_API ma_result ma_engine_set_gain_db(ma_engine* pEngine, float gainDB) { if (pEngine == NULL) { return MA_INVALID_ARGS; } return ma_node_set_output_bus_volume(ma_node_graph_get_endpoint(&pEngine->nodeGraph), 0, ma_volume_db_to_linear(gainDB)); } MA_API ma_uint32 ma_engine_get_listener_count(const ma_engine* pEngine) { if (pEngine == NULL) { return 0; } return pEngine->listenerCount; } MA_API ma_uint32 ma_engine_find_closest_listener(const ma_engine* pEngine, float absolutePosX, float absolutePosY, float absolutePosZ) { ma_uint32 iListener; ma_uint32 iListenerClosest; float closestLen2 = MA_FLT_MAX; if (pEngine == NULL || pEngine->listenerCount == 1) { return 0; } iListenerClosest = 0; for (iListener = 0; iListener < pEngine->listenerCount; iListener += 1) { if (ma_engine_listener_is_enabled(pEngine, iListener)) { float len2 = ma_vec3f_len2(ma_vec3f_sub(pEngine->listeners[iListener].position, ma_vec3f_init_3f(absolutePosX, absolutePosY, absolutePosZ))); if (closestLen2 > len2) { closestLen2 = len2; iListenerClosest = iListener; } } } MA_ASSERT(iListenerClosest < 255); return iListenerClosest; } MA_API void ma_engine_listener_set_position(ma_engine* pEngine, ma_uint32 listenerIndex, float x, float y, float z) { if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) { return; } ma_spatializer_listener_set_position(&pEngine->listeners[listenerIndex], x, y, z); } MA_API ma_vec3f ma_engine_listener_get_position(const ma_engine* pEngine, ma_uint32 listenerIndex) { if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) { return ma_vec3f_init_3f(0, 0, 0); } return ma_spatializer_listener_get_position(&pEngine->listeners[listenerIndex]); } MA_API void ma_engine_listener_set_direction(ma_engine* pEngine, ma_uint32 listenerIndex, float x, float y, float z) { if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) { return; } ma_spatializer_listener_set_direction(&pEngine->listeners[listenerIndex], x, y, z); } MA_API ma_vec3f ma_engine_listener_get_direction(const ma_engine* pEngine, ma_uint32 listenerIndex) { if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) { return ma_vec3f_init_3f(0, 0, -1); } return ma_spatializer_listener_get_direction(&pEngine->listeners[listenerIndex]); } MA_API void ma_engine_listener_set_velocity(ma_engine* pEngine, ma_uint32 listenerIndex, float x, float y, float z) { if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) { return; } ma_spatializer_listener_set_velocity(&pEngine->listeners[listenerIndex], x, y, z); } MA_API ma_vec3f ma_engine_listener_get_velocity(const ma_engine* pEngine, ma_uint32 listenerIndex) { if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) { return ma_vec3f_init_3f(0, 0, 0); } return ma_spatializer_listener_get_velocity(&pEngine->listeners[listenerIndex]); } MA_API void ma_engine_listener_set_cone(ma_engine* pEngine, ma_uint32 listenerIndex, float innerAngleInRadians, float outerAngleInRadians, float outerGain) { if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) { return; } ma_spatializer_listener_set_cone(&pEngine->listeners[listenerIndex], innerAngleInRadians, outerAngleInRadians, outerGain); } MA_API void ma_engine_listener_get_cone(const ma_engine* pEngine, ma_uint32 listenerIndex, float* pInnerAngleInRadians, float* pOuterAngleInRadians, float* pOuterGain) { if (pInnerAngleInRadians != NULL) { *pInnerAngleInRadians = 0; } if (pOuterAngleInRadians != NULL) { *pOuterAngleInRadians = 0; } if (pOuterGain != NULL) { *pOuterGain = 0; } ma_spatializer_listener_get_cone(&pEngine->listeners[listenerIndex], pInnerAngleInRadians, pOuterAngleInRadians, pOuterGain); } MA_API void ma_engine_listener_set_world_up(ma_engine* pEngine, ma_uint32 listenerIndex, float x, float y, float z) { if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) { return; } ma_spatializer_listener_set_world_up(&pEngine->listeners[listenerIndex], x, y, z); } MA_API ma_vec3f ma_engine_listener_get_world_up(const ma_engine* pEngine, ma_uint32 listenerIndex) { if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) { return ma_vec3f_init_3f(0, 1, 0); } return ma_spatializer_listener_get_world_up(&pEngine->listeners[listenerIndex]); } MA_API void ma_engine_listener_set_enabled(ma_engine* pEngine, ma_uint32 listenerIndex, ma_bool32 isEnabled) { if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) { return; } ma_spatializer_listener_set_enabled(&pEngine->listeners[listenerIndex], isEnabled); } MA_API ma_bool32 ma_engine_listener_is_enabled(const ma_engine* pEngine, ma_uint32 listenerIndex) { if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) { return MA_FALSE; } return ma_spatializer_listener_is_enabled(&pEngine->listeners[listenerIndex]); } #ifndef MA_NO_RESOURCE_MANAGER MA_API ma_result ma_engine_play_sound_ex(ma_engine* pEngine, const char* pFilePath, ma_node* pNode, ma_uint32 nodeInputBusIndex) { ma_result result = MA_SUCCESS; ma_sound_inlined* pSound = NULL; ma_sound_inlined* pNextSound = NULL; if (pEngine == NULL || pFilePath == NULL) { return MA_INVALID_ARGS; } /* Attach to the endpoint node if nothing is specicied. */ if (pNode == NULL) { pNode = ma_node_graph_get_endpoint(&pEngine->nodeGraph); nodeInputBusIndex = 0; } /* We want to check if we can recycle an already-allocated inlined sound. Since this is just a helper I'm not *too* concerned about performance here and I'm happy to use a lock to keep the implementation simple. Maybe this can be optimized later if there's enough demand, but if this function is being used it probably means the caller doesn't really care too much. What we do is check the atEnd flag. When this is true, we can recycle the sound. Otherwise we just keep iterating. If we reach the end without finding a sound to recycle we just allocate a new one. This doesn't scale well for a massive number of sounds being played simultaneously as we don't ever actually free the sound objects. Some kind of garbage collection routine might be valuable for this which I'll think about. */ ma_spinlock_lock(&pEngine->inlinedSoundLock); { ma_uint32 soundFlags = 0; for (pNextSound = pEngine->pInlinedSoundHead; pNextSound != NULL; pNextSound = pNextSound->pNext) { if (ma_sound_at_end(&pNextSound->sound)) { /* The sound is at the end which means it's available for recycling. All we need to do is uninitialize it and reinitialize it. All we're doing is recycling memory. */ pSound = pNextSound; c89atomic_fetch_sub_32(&pEngine->inlinedSoundCount, 1); break; } } if (pSound != NULL) { /* We actually want to detach the sound from the list here. The reason is because we want the sound to be in a consistent state at the non-recycled case to simplify the logic below. */ if (pEngine->pInlinedSoundHead == pSound) { pEngine->pInlinedSoundHead = pSound->pNext; } if (pSound->pPrev != NULL) { pSound->pPrev->pNext = pSound->pNext; } if (pSound->pNext != NULL) { pSound->pNext->pPrev = pSound->pPrev; } /* Now the previous sound needs to be uninitialized. */ ma_sound_uninit(&pNextSound->sound); } else { /* No sound available for recycling. Allocate one now. */ pSound = (ma_sound_inlined*)ma_malloc(sizeof(*pSound), &pEngine->allocationCallbacks); } if (pSound != NULL) { /* Safety check for the allocation above. */ /* At this point we should have memory allocated for the inlined sound. We just need to initialize it like a normal sound now. */ soundFlags |= MA_SOUND_FLAG_ASYNC; /* For inlined sounds we don't want to be sitting around waiting for stuff to load so force an async load. */ soundFlags |= MA_SOUND_FLAG_NO_DEFAULT_ATTACHMENT; /* We want specific control over where the sound is attached in the graph. We'll attach it manually just before playing the sound. */ soundFlags |= MA_SOUND_FLAG_NO_PITCH; /* Pitching isn't usable with inlined sounds, so disable it to save on speed. */ soundFlags |= MA_SOUND_FLAG_NO_SPATIALIZATION; /* Not currently doing spatialization with inlined sounds, but this might actually change later. For now disable spatialization. Will be removed if we ever add support for spatialization here. */ result = ma_sound_init_from_file(pEngine, pFilePath, soundFlags, NULL, NULL, &pSound->sound); if (result == MA_SUCCESS) { /* Now attach the sound to the graph. */ result = ma_node_attach_output_bus(pSound, 0, pNode, nodeInputBusIndex); if (result == MA_SUCCESS) { /* At this point the sound should be loaded and we can go ahead and add it to the list. The new item becomes the new head. */ pSound->pNext = pEngine->pInlinedSoundHead; pSound->pPrev = NULL; pEngine->pInlinedSoundHead = pSound; /* <-- This is what attaches the sound to the list. */ if (pSound->pNext != NULL) { pSound->pNext->pPrev = pSound; } } else { ma_free(pSound, &pEngine->allocationCallbacks); } } else { ma_free(pSound, &pEngine->allocationCallbacks); } } else { result = MA_OUT_OF_MEMORY; } } ma_spinlock_unlock(&pEngine->inlinedSoundLock); if (result != MA_SUCCESS) { return result; } /* Finally we can start playing the sound. */ result = ma_sound_start(&pSound->sound); if (result != MA_SUCCESS) { /* Failed to start the sound. We need to mark it for recycling and return an error. */ c89atomic_exchange_32(&pSound->sound.atEnd, MA_TRUE); return result; } c89atomic_fetch_add_32(&pEngine->inlinedSoundCount, 1); return result; } MA_API ma_result ma_engine_play_sound(ma_engine* pEngine, const char* pFilePath, ma_sound_group* pGroup) { return ma_engine_play_sound_ex(pEngine, pFilePath, pGroup, 0); } #endif static ma_result ma_sound_preinit(ma_engine* pEngine, ma_sound* pSound) { if (pSound == NULL) { return MA_INVALID_ARGS; } MA_ZERO_OBJECT(pSound); pSound->seekTarget = MA_SEEK_TARGET_NONE; if (pEngine == NULL) { return MA_INVALID_ARGS; } return MA_SUCCESS; } static ma_result ma_sound_init_from_data_source_internal(ma_engine* pEngine, const ma_sound_config* pConfig, ma_sound* pSound) { ma_result result; ma_engine_node_config engineNodeConfig; ma_engine_node_type type; /* Will be set to ma_engine_node_type_group if no data source is specified. */ /* Do not clear pSound to zero here - that's done at a higher level with ma_sound_preinit(). */ MA_ASSERT(pEngine != NULL); MA_ASSERT(pSound != NULL); if (pConfig == NULL) { return MA_INVALID_ARGS; } pSound->pDataSource = pConfig->pDataSource; if (pConfig->pDataSource != NULL) { type = ma_engine_node_type_sound; } else { type = ma_engine_node_type_group; } /* Sounds are engine nodes. Before we can initialize this we need to determine the channel count. If we can't do this we need to abort. It's up to the caller to ensure they're using a data source that provides this information upfront. */ engineNodeConfig = ma_engine_node_config_init(pEngine, type, pConfig->flags); engineNodeConfig.channelsIn = pConfig->channelsIn; engineNodeConfig.channelsOut = pConfig->channelsOut; engineNodeConfig.monoExpansionMode = pConfig->monoExpansionMode; /* If we're loading from a data source the input channel count needs to be the data source's native channel count. */ if (pConfig->pDataSource != NULL) { result = ma_data_source_get_data_format(pConfig->pDataSource, NULL, &engineNodeConfig.channelsIn, &engineNodeConfig.sampleRate, NULL, 0); if (result != MA_SUCCESS) { return result; /* Failed to retrieve the channel count. */ } if (engineNodeConfig.channelsIn == 0) { return MA_INVALID_OPERATION; /* Invalid channel count. */ } if (engineNodeConfig.channelsOut == MA_SOUND_SOURCE_CHANNEL_COUNT) { engineNodeConfig.channelsOut = engineNodeConfig.channelsIn; } } /* Getting here means we should have a valid channel count and we can initialize the engine node. */ result = ma_engine_node_init(&engineNodeConfig, &pEngine->allocationCallbacks, &pSound->engineNode); if (result != MA_SUCCESS) { return result; } /* If no attachment is specified, attach the sound straight to the endpoint. */ if (pConfig->pInitialAttachment == NULL) { /* No group. Attach straight to the endpoint by default, unless the caller has requested that it not. */ if ((pConfig->flags & MA_SOUND_FLAG_NO_DEFAULT_ATTACHMENT) == 0) { result = ma_node_attach_output_bus(pSound, 0, ma_node_graph_get_endpoint(&pEngine->nodeGraph), 0); } } else { /* An attachment is specified. Attach to it by default. The sound has only a single output bus, and the config will specify which input bus to attach to. */ result = ma_node_attach_output_bus(pSound, 0, pConfig->pInitialAttachment, pConfig->initialAttachmentInputBusIndex); } if (result != MA_SUCCESS) { ma_engine_node_uninit(&pSound->engineNode, &pEngine->allocationCallbacks); return result; } /* Apply initial range and looping state to the data source if applicable. */ if (pConfig->rangeBegInPCMFrames != 0 || pConfig->rangeEndInPCMFrames != ~((ma_uint64)0)) { ma_data_source_set_range_in_pcm_frames(ma_sound_get_data_source(pSound), pConfig->rangeBegInPCMFrames, pConfig->rangeEndInPCMFrames); } if (pConfig->loopPointBegInPCMFrames != 0 || pConfig->loopPointEndInPCMFrames != ~((ma_uint64)0)) { ma_data_source_set_range_in_pcm_frames(ma_sound_get_data_source(pSound), pConfig->loopPointBegInPCMFrames, pConfig->loopPointEndInPCMFrames); } ma_sound_set_looping(pSound, pConfig->isLooping); return MA_SUCCESS; } #ifndef MA_NO_RESOURCE_MANAGER MA_API ma_result ma_sound_init_from_file_internal(ma_engine* pEngine, const ma_sound_config* pConfig, ma_sound* pSound) { ma_result result = MA_SUCCESS; ma_uint32 flags; ma_sound_config config; ma_resource_manager_pipeline_notifications notifications; /* The engine requires knowledge of the channel count of the underlying data source before it can initialize the sound. Therefore, we need to make the resource manager wait until initialization of the underlying data source to be initialized so we can get access to the channel count. To do this, the MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT is forced. Because we're initializing the data source before the sound, there's a chance the notification will get triggered before this function returns. This is OK, so long as the caller is aware of it and can avoid accessing the sound from within the notification. */ flags = pConfig->flags | MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT; pSound->pResourceManagerDataSource = (ma_resource_manager_data_source*)ma_malloc(sizeof(*pSound->pResourceManagerDataSource), &pEngine->allocationCallbacks); if (pSound->pResourceManagerDataSource == NULL) { return MA_OUT_OF_MEMORY; } notifications = ma_resource_manager_pipeline_notifications_init(); notifications.done.pFence = pConfig->pDoneFence; /* We must wrap everything around the fence if one was specified. This ensures ma_fence_wait() does not return prematurely before the sound has finished initializing. */ if (notifications.done.pFence) { ma_fence_acquire(notifications.done.pFence); } { ma_resource_manager_data_source_config resourceManagerDataSourceConfig = ma_resource_manager_data_source_config_init(); resourceManagerDataSourceConfig.pFilePath = pConfig->pFilePath; resourceManagerDataSourceConfig.pFilePathW = pConfig->pFilePathW; resourceManagerDataSourceConfig.flags = flags; resourceManagerDataSourceConfig.pNotifications = ¬ifications; resourceManagerDataSourceConfig.initialSeekPointInPCMFrames = pConfig->initialSeekPointInPCMFrames; resourceManagerDataSourceConfig.rangeBegInPCMFrames = pConfig->rangeBegInPCMFrames; resourceManagerDataSourceConfig.rangeEndInPCMFrames = pConfig->rangeEndInPCMFrames; resourceManagerDataSourceConfig.loopPointBegInPCMFrames = pConfig->loopPointBegInPCMFrames; resourceManagerDataSourceConfig.loopPointEndInPCMFrames = pConfig->loopPointEndInPCMFrames; resourceManagerDataSourceConfig.isLooping = pConfig->isLooping; result = ma_resource_manager_data_source_init_ex(pEngine->pResourceManager, &resourceManagerDataSourceConfig, pSound->pResourceManagerDataSource); if (result != MA_SUCCESS) { goto done; } pSound->ownsDataSource = MA_TRUE; /* <-- Important. Not setting this will result in the resource manager data source never getting uninitialized. */ /* We need to use a slightly customized version of the config so we'll need to make a copy. */ config = *pConfig; config.pFilePath = NULL; config.pFilePathW = NULL; config.pDataSource = pSound->pResourceManagerDataSource; result = ma_sound_init_from_data_source_internal(pEngine, &config, pSound); if (result != MA_SUCCESS) { ma_resource_manager_data_source_uninit(pSound->pResourceManagerDataSource); ma_free(pSound->pResourceManagerDataSource, &pEngine->allocationCallbacks); MA_ZERO_OBJECT(pSound); goto done; } } done: if (notifications.done.pFence) { ma_fence_release(notifications.done.pFence); } return result; } MA_API ma_result ma_sound_init_from_file(ma_engine* pEngine, const char* pFilePath, ma_uint32 flags, ma_sound_group* pGroup, ma_fence* pDoneFence, ma_sound* pSound) { ma_sound_config config = ma_sound_config_init_2(pEngine); config.pFilePath = pFilePath; config.flags = flags; config.pInitialAttachment = pGroup; config.pDoneFence = pDoneFence; return ma_sound_init_ex(pEngine, &config, pSound); } MA_API ma_result ma_sound_init_from_file_w(ma_engine* pEngine, const wchar_t* pFilePath, ma_uint32 flags, ma_sound_group* pGroup, ma_fence* pDoneFence, ma_sound* pSound) { ma_sound_config config = ma_sound_config_init_2(pEngine); config.pFilePathW = pFilePath; config.flags = flags; config.pInitialAttachment = pGroup; config.pDoneFence = pDoneFence; return ma_sound_init_ex(pEngine, &config, pSound); } MA_API ma_result ma_sound_init_copy(ma_engine* pEngine, const ma_sound* pExistingSound, ma_uint32 flags, ma_sound_group* pGroup, ma_sound* pSound) { ma_result result; ma_sound_config config; result = ma_sound_preinit(pEngine, pSound); if (result != MA_SUCCESS) { return result; } if (pExistingSound == NULL) { return MA_INVALID_ARGS; } /* Cloning only works for data buffers (not streams) that are loaded from the resource manager. */ if (pExistingSound->pResourceManagerDataSource == NULL) { return MA_INVALID_OPERATION; } /* We need to make a clone of the data source. If the data source is not a data buffer (i.e. a stream) the this will fail. */ pSound->pResourceManagerDataSource = (ma_resource_manager_data_source*)ma_malloc(sizeof(*pSound->pResourceManagerDataSource), &pEngine->allocationCallbacks); if (pSound->pResourceManagerDataSource == NULL) { return MA_OUT_OF_MEMORY; } result = ma_resource_manager_data_source_init_copy(pEngine->pResourceManager, pExistingSound->pResourceManagerDataSource, pSound->pResourceManagerDataSource); if (result != MA_SUCCESS) { ma_free(pSound->pResourceManagerDataSource, &pEngine->allocationCallbacks); return result; } config = ma_sound_config_init_2(pEngine); config.pDataSource = pSound->pResourceManagerDataSource; config.flags = flags; config.pInitialAttachment = pGroup; config.monoExpansionMode = pExistingSound->engineNode.monoExpansionMode; result = ma_sound_init_from_data_source_internal(pEngine, &config, pSound); if (result != MA_SUCCESS) { ma_resource_manager_data_source_uninit(pSound->pResourceManagerDataSource); ma_free(pSound->pResourceManagerDataSource, &pEngine->allocationCallbacks); MA_ZERO_OBJECT(pSound); return result; } return MA_SUCCESS; } #endif MA_API ma_result ma_sound_init_from_data_source(ma_engine* pEngine, ma_data_source* pDataSource, ma_uint32 flags, ma_sound_group* pGroup, ma_sound* pSound) { ma_sound_config config = ma_sound_config_init_2(pEngine); config.pDataSource = pDataSource; config.flags = flags; config.pInitialAttachment = pGroup; return ma_sound_init_ex(pEngine, &config, pSound); } MA_API ma_result ma_sound_init_ex(ma_engine* pEngine, const ma_sound_config* pConfig, ma_sound* pSound) { ma_result result; result = ma_sound_preinit(pEngine, pSound); if (result != MA_SUCCESS) { return result; } if (pConfig == NULL) { return MA_INVALID_ARGS; } /* We need to load the sound differently depending on whether or not we're loading from a file. */ #ifndef MA_NO_RESOURCE_MANAGER if (pConfig->pFilePath != NULL || pConfig->pFilePathW != NULL) { return ma_sound_init_from_file_internal(pEngine, pConfig, pSound); } else #endif { /* Getting here means we're not loading from a file. We may be loading from an already-initialized data source, or none at all. If we aren't specifying any data source, we'll be initializing the the equivalent to a group. ma_data_source_init_from_data_source_internal() will deal with this for us, so no special treatment required here. */ return ma_sound_init_from_data_source_internal(pEngine, pConfig, pSound); } } MA_API void ma_sound_uninit(ma_sound* pSound) { if (pSound == NULL) { return; } /* Always uninitialize the node first. This ensures it's detached from the graph and does not return until it has done so which makes thread safety beyond this point trivial. */ ma_engine_node_uninit(&pSound->engineNode, &pSound->engineNode.pEngine->allocationCallbacks); /* Once the sound is detached from the group we can guarantee that it won't be referenced by the mixer thread which means it's safe for us to destroy the data source. */ #ifndef MA_NO_RESOURCE_MANAGER if (pSound->ownsDataSource) { ma_resource_manager_data_source_uninit(pSound->pResourceManagerDataSource); ma_free(pSound->pResourceManagerDataSource, &pSound->engineNode.pEngine->allocationCallbacks); pSound->pDataSource = NULL; } #else MA_ASSERT(pSound->ownsDataSource == MA_FALSE); #endif } MA_API ma_engine* ma_sound_get_engine(const ma_sound* pSound) { if (pSound == NULL) { return NULL; } return pSound->engineNode.pEngine; } MA_API ma_data_source* ma_sound_get_data_source(const ma_sound* pSound) { if (pSound == NULL) { return NULL; } return pSound->pDataSource; } MA_API ma_result ma_sound_start(ma_sound* pSound) { if (pSound == NULL) { return MA_INVALID_ARGS; } /* If the sound is already playing, do nothing. */ if (ma_sound_is_playing(pSound)) { return MA_SUCCESS; } /* If the sound is at the end it means we want to start from the start again. */ if (ma_sound_at_end(pSound)) { ma_result result = ma_data_source_seek_to_pcm_frame(pSound->pDataSource, 0); if (result != MA_SUCCESS && result != MA_NOT_IMPLEMENTED) { return result; /* Failed to seek back to the start. */ } /* Make sure we clear the end indicator. */ c89atomic_exchange_32(&pSound->atEnd, MA_FALSE); } /* Make sure the sound is started. If there's a start delay, the sound won't actually start until the start time is reached. */ ma_node_set_state(pSound, ma_node_state_started); return MA_SUCCESS; } MA_API ma_result ma_sound_stop(ma_sound* pSound) { if (pSound == NULL) { return MA_INVALID_ARGS; } /* This will stop the sound immediately. Use ma_sound_set_stop_time() to stop the sound at a specific time. */ ma_node_set_state(pSound, ma_node_state_stopped); return MA_SUCCESS; } MA_API void ma_sound_set_volume(ma_sound* pSound, float volume) { if (pSound == NULL) { return; } /* The volume is controlled via the output bus. */ ma_node_set_output_bus_volume(pSound, 0, volume); } MA_API float ma_sound_get_volume(const ma_sound* pSound) { if (pSound == NULL) { return 0; } return ma_node_get_output_bus_volume(pSound, 0); } MA_API void ma_sound_set_pan(ma_sound* pSound, float pan) { if (pSound == NULL) { return; } ma_panner_set_pan(&pSound->engineNode.panner, pan); } MA_API float ma_sound_get_pan(const ma_sound* pSound) { if (pSound == NULL) { return 0; } return ma_panner_get_pan(&pSound->engineNode.panner); } MA_API void ma_sound_set_pan_mode(ma_sound* pSound, ma_pan_mode panMode) { if (pSound == NULL) { return; } ma_panner_set_mode(&pSound->engineNode.panner, panMode); } MA_API ma_pan_mode ma_sound_get_pan_mode(const ma_sound* pSound) { if (pSound == NULL) { return ma_pan_mode_balance; } return ma_panner_get_mode(&pSound->engineNode.panner); } MA_API void ma_sound_set_pitch(ma_sound* pSound, float pitch) { if (pSound == NULL) { return; } if (pitch <= 0) { return; } c89atomic_exchange_explicit_f32(&pSound->engineNode.pitch, pitch, c89atomic_memory_order_release); } MA_API float ma_sound_get_pitch(const ma_sound* pSound) { if (pSound == NULL) { return 0; } return c89atomic_load_f32(&pSound->engineNode.pitch); /* Naughty const-cast for this. */ } MA_API void ma_sound_set_spatialization_enabled(ma_sound* pSound, ma_bool32 enabled) { if (pSound == NULL) { return; } c89atomic_exchange_explicit_32(&pSound->engineNode.isSpatializationDisabled, !enabled, c89atomic_memory_order_release); } MA_API ma_bool32 ma_sound_is_spatialization_enabled(const ma_sound* pSound) { if (pSound == NULL) { return MA_FALSE; } return ma_engine_node_is_spatialization_enabled(&pSound->engineNode); } MA_API void ma_sound_set_pinned_listener_index(ma_sound* pSound, ma_uint32 listenerIndex) { if (pSound == NULL || listenerIndex >= ma_engine_get_listener_count(ma_sound_get_engine(pSound))) { return; } c89atomic_exchange_explicit_32(&pSound->engineNode.pinnedListenerIndex, listenerIndex, c89atomic_memory_order_release); } MA_API ma_uint32 ma_sound_get_pinned_listener_index(const ma_sound* pSound) { if (pSound == NULL) { return MA_LISTENER_INDEX_CLOSEST; } return c89atomic_load_explicit_32(&pSound->engineNode.pinnedListenerIndex, c89atomic_memory_order_acquire); } MA_API ma_uint32 ma_sound_get_listener_index(const ma_sound* pSound) { ma_uint32 listenerIndex; if (pSound == NULL) { return 0; } listenerIndex = ma_sound_get_pinned_listener_index(pSound); if (listenerIndex == MA_LISTENER_INDEX_CLOSEST) { ma_vec3f position = ma_sound_get_position(pSound); return ma_engine_find_closest_listener(ma_sound_get_engine(pSound), position.x, position.y, position.z); } return listenerIndex; } MA_API ma_vec3f ma_sound_get_direction_to_listener(const ma_sound* pSound) { ma_vec3f relativePos; ma_engine* pEngine; if (pSound == NULL) { return ma_vec3f_init_3f(0, 0, -1); } pEngine = ma_sound_get_engine(pSound); if (pEngine == NULL) { return ma_vec3f_init_3f(0, 0, -1); } ma_spatializer_get_relative_position_and_direction(&pSound->engineNode.spatializer, &pEngine->listeners[ma_sound_get_listener_index(pSound)], &relativePos, NULL); return ma_vec3f_normalize(ma_vec3f_neg(relativePos)); } MA_API void ma_sound_set_position(ma_sound* pSound, float x, float y, float z) { if (pSound == NULL) { return; } ma_spatializer_set_position(&pSound->engineNode.spatializer, x, y, z); } MA_API ma_vec3f ma_sound_get_position(const ma_sound* pSound) { if (pSound == NULL) { return ma_vec3f_init_3f(0, 0, 0); } return ma_spatializer_get_position(&pSound->engineNode.spatializer); } MA_API void ma_sound_set_direction(ma_sound* pSound, float x, float y, float z) { if (pSound == NULL) { return; } ma_spatializer_set_direction(&pSound->engineNode.spatializer, x, y, z); } MA_API ma_vec3f ma_sound_get_direction(const ma_sound* pSound) { if (pSound == NULL) { return ma_vec3f_init_3f(0, 0, 0); } return ma_spatializer_get_direction(&pSound->engineNode.spatializer); } MA_API void ma_sound_set_velocity(ma_sound* pSound, float x, float y, float z) { if (pSound == NULL) { return; } ma_spatializer_set_velocity(&pSound->engineNode.spatializer, x, y, z); } MA_API ma_vec3f ma_sound_get_velocity(const ma_sound* pSound) { if (pSound == NULL) { return ma_vec3f_init_3f(0, 0, 0); } return ma_spatializer_get_velocity(&pSound->engineNode.spatializer); } MA_API void ma_sound_set_attenuation_model(ma_sound* pSound, ma_attenuation_model attenuationModel) { if (pSound == NULL) { return; } ma_spatializer_set_attenuation_model(&pSound->engineNode.spatializer, attenuationModel); } MA_API ma_attenuation_model ma_sound_get_attenuation_model(const ma_sound* pSound) { if (pSound == NULL) { return ma_attenuation_model_none; } return ma_spatializer_get_attenuation_model(&pSound->engineNode.spatializer); } MA_API void ma_sound_set_positioning(ma_sound* pSound, ma_positioning positioning) { if (pSound == NULL) { return; } ma_spatializer_set_positioning(&pSound->engineNode.spatializer, positioning); } MA_API ma_positioning ma_sound_get_positioning(const ma_sound* pSound) { if (pSound == NULL) { return ma_positioning_absolute; } return ma_spatializer_get_positioning(&pSound->engineNode.spatializer); } MA_API void ma_sound_set_rolloff(ma_sound* pSound, float rolloff) { if (pSound == NULL) { return; } ma_spatializer_set_rolloff(&pSound->engineNode.spatializer, rolloff); } MA_API float ma_sound_get_rolloff(const ma_sound* pSound) { if (pSound == NULL) { return 0; } return ma_spatializer_get_rolloff(&pSound->engineNode.spatializer); } MA_API void ma_sound_set_min_gain(ma_sound* pSound, float minGain) { if (pSound == NULL) { return; } ma_spatializer_set_min_gain(&pSound->engineNode.spatializer, minGain); } MA_API float ma_sound_get_min_gain(const ma_sound* pSound) { if (pSound == NULL) { return 0; } return ma_spatializer_get_min_gain(&pSound->engineNode.spatializer); } MA_API void ma_sound_set_max_gain(ma_sound* pSound, float maxGain) { if (pSound == NULL) { return; } ma_spatializer_set_max_gain(&pSound->engineNode.spatializer, maxGain); } MA_API float ma_sound_get_max_gain(const ma_sound* pSound) { if (pSound == NULL) { return 0; } return ma_spatializer_get_max_gain(&pSound->engineNode.spatializer); } MA_API void ma_sound_set_min_distance(ma_sound* pSound, float minDistance) { if (pSound == NULL) { return; } ma_spatializer_set_min_distance(&pSound->engineNode.spatializer, minDistance); } MA_API float ma_sound_get_min_distance(const ma_sound* pSound) { if (pSound == NULL) { return 0; } return ma_spatializer_get_min_distance(&pSound->engineNode.spatializer); } MA_API void ma_sound_set_max_distance(ma_sound* pSound, float maxDistance) { if (pSound == NULL) { return; } ma_spatializer_set_max_distance(&pSound->engineNode.spatializer, maxDistance); } MA_API float ma_sound_get_max_distance(const ma_sound* pSound) { if (pSound == NULL) { return 0; } return ma_spatializer_get_max_distance(&pSound->engineNode.spatializer); } MA_API void ma_sound_set_cone(ma_sound* pSound, float innerAngleInRadians, float outerAngleInRadians, float outerGain) { if (pSound == NULL) { return; } ma_spatializer_set_cone(&pSound->engineNode.spatializer, innerAngleInRadians, outerAngleInRadians, outerGain); } MA_API void ma_sound_get_cone(const ma_sound* pSound, float* pInnerAngleInRadians, float* pOuterAngleInRadians, float* pOuterGain) { if (pInnerAngleInRadians != NULL) { *pInnerAngleInRadians = 0; } if (pOuterAngleInRadians != NULL) { *pOuterAngleInRadians = 0; } if (pOuterGain != NULL) { *pOuterGain = 0; } ma_spatializer_get_cone(&pSound->engineNode.spatializer, pInnerAngleInRadians, pOuterAngleInRadians, pOuterGain); } MA_API void ma_sound_set_doppler_factor(ma_sound* pSound, float dopplerFactor) { if (pSound == NULL) { return; } ma_spatializer_set_doppler_factor(&pSound->engineNode.spatializer, dopplerFactor); } MA_API float ma_sound_get_doppler_factor(const ma_sound* pSound) { if (pSound == NULL) { return 0; } return ma_spatializer_get_doppler_factor(&pSound->engineNode.spatializer); } MA_API void ma_sound_set_directional_attenuation_factor(ma_sound* pSound, float directionalAttenuationFactor) { if (pSound == NULL) { return; } ma_spatializer_set_directional_attenuation_factor(&pSound->engineNode.spatializer, directionalAttenuationFactor); } MA_API float ma_sound_get_directional_attenuation_factor(const ma_sound* pSound) { if (pSound == NULL) { return 1; } return ma_spatializer_get_directional_attenuation_factor(&pSound->engineNode.spatializer); } MA_API void ma_sound_set_fade_in_pcm_frames(ma_sound* pSound, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInFrames) { if (pSound == NULL) { return; } ma_fader_set_fade(&pSound->engineNode.fader, volumeBeg, volumeEnd, fadeLengthInFrames); } MA_API void ma_sound_set_fade_in_milliseconds(ma_sound* pSound, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInMilliseconds) { if (pSound == NULL) { return; } ma_sound_set_fade_in_pcm_frames(pSound, volumeBeg, volumeEnd, (fadeLengthInMilliseconds * pSound->engineNode.fader.config.sampleRate) / 1000); } MA_API float ma_sound_get_current_fade_volume(ma_sound* pSound) { if (pSound == NULL) { return MA_INVALID_ARGS; } return ma_fader_get_current_volume(&pSound->engineNode.fader); } MA_API void ma_sound_set_start_time_in_pcm_frames(ma_sound* pSound, ma_uint64 absoluteGlobalTimeInFrames) { if (pSound == NULL) { return; } ma_node_set_state_time(pSound, ma_node_state_started, absoluteGlobalTimeInFrames); } MA_API void ma_sound_set_start_time_in_milliseconds(ma_sound* pSound, ma_uint64 absoluteGlobalTimeInMilliseconds) { if (pSound == NULL) { return; } ma_sound_set_start_time_in_pcm_frames(pSound, absoluteGlobalTimeInMilliseconds * ma_engine_get_sample_rate(ma_sound_get_engine(pSound)) / 1000); } MA_API void ma_sound_set_stop_time_in_pcm_frames(ma_sound* pSound, ma_uint64 absoluteGlobalTimeInFrames) { if (pSound == NULL) { return; } ma_node_set_state_time(pSound, ma_node_state_stopped, absoluteGlobalTimeInFrames); } MA_API void ma_sound_set_stop_time_in_milliseconds(ma_sound* pSound, ma_uint64 absoluteGlobalTimeInMilliseconds) { if (pSound == NULL) { return; } ma_sound_set_stop_time_in_pcm_frames(pSound, absoluteGlobalTimeInMilliseconds * ma_engine_get_sample_rate(ma_sound_get_engine(pSound)) / 1000); } MA_API ma_bool32 ma_sound_is_playing(const ma_sound* pSound) { if (pSound == NULL) { return MA_FALSE; } return ma_node_get_state_by_time(pSound, ma_engine_get_time(ma_sound_get_engine(pSound))) == ma_node_state_started; } MA_API ma_uint64 ma_sound_get_time_in_pcm_frames(const ma_sound* pSound) { if (pSound == NULL) { return 0; } return ma_node_get_time(pSound); } MA_API void ma_sound_set_looping(ma_sound* pSound, ma_bool32 isLooping) { if (pSound == NULL) { return; } /* Looping is only a valid concept if the sound is backed by a data source. */ if (pSound->pDataSource == NULL) { return; } /* The looping state needs to be applied to the data source in order for any looping to actually happen. */ ma_data_source_set_looping(pSound->pDataSource, isLooping); } MA_API ma_bool32 ma_sound_is_looping(const ma_sound* pSound) { if (pSound == NULL) { return MA_FALSE; } /* There is no notion of looping for sounds that are not backed by a data source. */ if (pSound->pDataSource == NULL) { return MA_FALSE; } return ma_data_source_is_looping(pSound->pDataSource); } MA_API ma_bool32 ma_sound_at_end(const ma_sound* pSound) { if (pSound == NULL) { return MA_FALSE; } /* There is no notion of an end of a sound if it's not backed by a data source. */ if (pSound->pDataSource == NULL) { return MA_FALSE; } return c89atomic_load_32(&pSound->atEnd); } MA_API ma_result ma_sound_seek_to_pcm_frame(ma_sound* pSound, ma_uint64 frameIndex) { if (pSound == NULL) { return MA_INVALID_ARGS; } /* Seeking is only valid for sounds that are backed by a data source. */ if (pSound->pDataSource == NULL) { return MA_INVALID_OPERATION; } /* We can't be seeking while reading at the same time. We just set the seek target and get the mixing thread to do the actual seek. */ c89atomic_exchange_64(&pSound->seekTarget, frameIndex); return MA_SUCCESS; } MA_API ma_result ma_sound_get_data_format(ma_sound* pSound, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) { if (pSound == NULL) { return MA_INVALID_ARGS; } /* The data format is retrieved directly from the data source if the sound is backed by one. Otherwise we pull it from the node. */ if (pSound->pDataSource == NULL) { ma_uint32 channels; if (pFormat != NULL) { *pFormat = ma_format_f32; } channels = ma_node_get_input_channels(&pSound->engineNode, 0); if (pChannels != NULL) { *pChannels = channels; } if (pSampleRate != NULL) { *pSampleRate = pSound->engineNode.resampler.config.sampleRateIn; } if (pChannelMap != NULL) { ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, channels); } return MA_SUCCESS; } else { return ma_data_source_get_data_format(pSound->pDataSource, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap); } } MA_API ma_result ma_sound_get_cursor_in_pcm_frames(ma_sound* pSound, ma_uint64* pCursor) { if (pSound == NULL) { return MA_INVALID_ARGS; } /* The notion of a cursor is only valid for sounds that are backed by a data source. */ if (pSound->pDataSource == NULL) { return MA_INVALID_OPERATION; } return ma_data_source_get_cursor_in_pcm_frames(pSound->pDataSource, pCursor); } MA_API ma_result ma_sound_get_length_in_pcm_frames(ma_sound* pSound, ma_uint64* pLength) { if (pSound == NULL) { return MA_INVALID_ARGS; } /* The notion of a sound length is only valid for sounds that are backed by a data source. */ if (pSound->pDataSource == NULL) { return MA_INVALID_OPERATION; } return ma_data_source_get_length_in_pcm_frames(pSound->pDataSource, pLength); } MA_API ma_result ma_sound_get_cursor_in_seconds(ma_sound* pSound, float* pCursor) { if (pSound == NULL) { return MA_INVALID_ARGS; } /* The notion of a cursor is only valid for sounds that are backed by a data source. */ if (pSound->pDataSource == NULL) { return MA_INVALID_OPERATION; } return ma_data_source_get_cursor_in_seconds(pSound->pDataSource, pCursor); } MA_API ma_result ma_sound_get_length_in_seconds(ma_sound* pSound, float* pLength) { if (pSound == NULL) { return MA_INVALID_ARGS; } /* The notion of a sound length is only valid for sounds that are backed by a data source. */ if (pSound->pDataSource == NULL) { return MA_INVALID_OPERATION; } return ma_data_source_get_length_in_seconds(pSound->pDataSource, pLength); } MA_API ma_result ma_sound_group_init(ma_engine* pEngine, ma_uint32 flags, ma_sound_group* pParentGroup, ma_sound_group* pGroup) { ma_sound_group_config config = ma_sound_group_config_init_2(pEngine); config.flags = flags; config.pInitialAttachment = pParentGroup; return ma_sound_group_init_ex(pEngine, &config, pGroup); } MA_API ma_result ma_sound_group_init_ex(ma_engine* pEngine, const ma_sound_group_config* pConfig, ma_sound_group* pGroup) { ma_sound_config soundConfig; if (pGroup == NULL) { return MA_INVALID_ARGS; } MA_ZERO_OBJECT(pGroup); if (pConfig == NULL) { return MA_INVALID_ARGS; } /* A sound group is just a sound without a data source. */ soundConfig = *pConfig; soundConfig.pFilePath = NULL; soundConfig.pFilePathW = NULL; soundConfig.pDataSource = NULL; /* Groups need to have spatialization disabled by default because I think it'll be pretty rare that programs will want to spatialize groups (but not unheard of). Certainly it feels like disabling this by default feels like the right option. Spatialization can be enabled with a call to ma_sound_group_set_spatialization_enabled(). */ soundConfig.flags |= MA_SOUND_FLAG_NO_SPATIALIZATION; return ma_sound_init_ex(pEngine, &soundConfig, pGroup); } MA_API void ma_sound_group_uninit(ma_sound_group* pGroup) { ma_sound_uninit(pGroup); } MA_API ma_engine* ma_sound_group_get_engine(const ma_sound_group* pGroup) { return ma_sound_get_engine(pGroup); } MA_API ma_result ma_sound_group_start(ma_sound_group* pGroup) { return ma_sound_start(pGroup); } MA_API ma_result ma_sound_group_stop(ma_sound_group* pGroup) { return ma_sound_stop(pGroup); } MA_API void ma_sound_group_set_volume(ma_sound_group* pGroup, float volume) { ma_sound_set_volume(pGroup, volume); } MA_API float ma_sound_group_get_volume(const ma_sound_group* pGroup) { return ma_sound_get_volume(pGroup); } MA_API void ma_sound_group_set_pan(ma_sound_group* pGroup, float pan) { ma_sound_set_pan(pGroup, pan); } MA_API float ma_sound_group_get_pan(const ma_sound_group* pGroup) { return ma_sound_get_pan(pGroup); } MA_API void ma_sound_group_set_pan_mode(ma_sound_group* pGroup, ma_pan_mode panMode) { ma_sound_set_pan_mode(pGroup, panMode); } MA_API ma_pan_mode ma_sound_group_get_pan_mode(const ma_sound_group* pGroup) { return ma_sound_get_pan_mode(pGroup); } MA_API void ma_sound_group_set_pitch(ma_sound_group* pGroup, float pitch) { ma_sound_set_pitch(pGroup, pitch); } MA_API float ma_sound_group_get_pitch(const ma_sound_group* pGroup) { return ma_sound_get_pitch(pGroup); } MA_API void ma_sound_group_set_spatialization_enabled(ma_sound_group* pGroup, ma_bool32 enabled) { ma_sound_set_spatialization_enabled(pGroup, enabled); } MA_API ma_bool32 ma_sound_group_is_spatialization_enabled(const ma_sound_group* pGroup) { return ma_sound_is_spatialization_enabled(pGroup); } MA_API void ma_sound_group_set_pinned_listener_index(ma_sound_group* pGroup, ma_uint32 listenerIndex) { ma_sound_set_pinned_listener_index(pGroup, listenerIndex); } MA_API ma_uint32 ma_sound_group_get_pinned_listener_index(const ma_sound_group* pGroup) { return ma_sound_get_pinned_listener_index(pGroup); } MA_API ma_uint32 ma_sound_group_get_listener_index(const ma_sound_group* pGroup) { return ma_sound_get_listener_index(pGroup); } MA_API ma_vec3f ma_sound_group_get_direction_to_listener(const ma_sound_group* pGroup) { return ma_sound_get_direction_to_listener(pGroup); } MA_API void ma_sound_group_set_position(ma_sound_group* pGroup, float x, float y, float z) { ma_sound_set_position(pGroup, x, y, z); } MA_API ma_vec3f ma_sound_group_get_position(const ma_sound_group* pGroup) { return ma_sound_get_position(pGroup); } MA_API void ma_sound_group_set_direction(ma_sound_group* pGroup, float x, float y, float z) { ma_sound_set_direction(pGroup, x, y, z); } MA_API ma_vec3f ma_sound_group_get_direction(const ma_sound_group* pGroup) { return ma_sound_get_direction(pGroup); } MA_API void ma_sound_group_set_velocity(ma_sound_group* pGroup, float x, float y, float z) { ma_sound_set_velocity(pGroup, x, y, z); } MA_API ma_vec3f ma_sound_group_get_velocity(const ma_sound_group* pGroup) { return ma_sound_get_velocity(pGroup); } MA_API void ma_sound_group_set_attenuation_model(ma_sound_group* pGroup, ma_attenuation_model attenuationModel) { ma_sound_set_attenuation_model(pGroup, attenuationModel); } MA_API ma_attenuation_model ma_sound_group_get_attenuation_model(const ma_sound_group* pGroup) { return ma_sound_get_attenuation_model(pGroup); } MA_API void ma_sound_group_set_positioning(ma_sound_group* pGroup, ma_positioning positioning) { ma_sound_set_positioning(pGroup, positioning); } MA_API ma_positioning ma_sound_group_get_positioning(const ma_sound_group* pGroup) { return ma_sound_get_positioning(pGroup); } MA_API void ma_sound_group_set_rolloff(ma_sound_group* pGroup, float rolloff) { ma_sound_set_rolloff(pGroup, rolloff); } MA_API float ma_sound_group_get_rolloff(const ma_sound_group* pGroup) { return ma_sound_get_rolloff(pGroup); } MA_API void ma_sound_group_set_min_gain(ma_sound_group* pGroup, float minGain) { ma_sound_set_min_gain(pGroup, minGain); } MA_API float ma_sound_group_get_min_gain(const ma_sound_group* pGroup) { return ma_sound_get_min_gain(pGroup); } MA_API void ma_sound_group_set_max_gain(ma_sound_group* pGroup, float maxGain) { ma_sound_set_max_gain(pGroup, maxGain); } MA_API float ma_sound_group_get_max_gain(const ma_sound_group* pGroup) { return ma_sound_get_max_gain(pGroup); } MA_API void ma_sound_group_set_min_distance(ma_sound_group* pGroup, float minDistance) { ma_sound_set_min_distance(pGroup, minDistance); } MA_API float ma_sound_group_get_min_distance(const ma_sound_group* pGroup) { return ma_sound_get_min_distance(pGroup); } MA_API void ma_sound_group_set_max_distance(ma_sound_group* pGroup, float maxDistance) { ma_sound_set_max_distance(pGroup, maxDistance); } MA_API float ma_sound_group_get_max_distance(const ma_sound_group* pGroup) { return ma_sound_get_max_distance(pGroup); } MA_API void ma_sound_group_set_cone(ma_sound_group* pGroup, float innerAngleInRadians, float outerAngleInRadians, float outerGain) { ma_sound_set_cone(pGroup, innerAngleInRadians, outerAngleInRadians, outerGain); } MA_API void ma_sound_group_get_cone(const ma_sound_group* pGroup, float* pInnerAngleInRadians, float* pOuterAngleInRadians, float* pOuterGain) { ma_sound_get_cone(pGroup, pInnerAngleInRadians, pOuterAngleInRadians, pOuterGain); } MA_API void ma_sound_group_set_doppler_factor(ma_sound_group* pGroup, float dopplerFactor) { ma_sound_set_doppler_factor(pGroup, dopplerFactor); } MA_API float ma_sound_group_get_doppler_factor(const ma_sound_group* pGroup) { return ma_sound_get_doppler_factor(pGroup); } MA_API void ma_sound_group_set_directional_attenuation_factor(ma_sound_group* pGroup, float directionalAttenuationFactor) { ma_sound_set_directional_attenuation_factor(pGroup, directionalAttenuationFactor); } MA_API float ma_sound_group_get_directional_attenuation_factor(const ma_sound_group* pGroup) { return ma_sound_get_directional_attenuation_factor(pGroup); } MA_API void ma_sound_group_set_fade_in_pcm_frames(ma_sound_group* pGroup, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInFrames) { ma_sound_set_fade_in_pcm_frames(pGroup, volumeBeg, volumeEnd, fadeLengthInFrames); } MA_API void ma_sound_group_set_fade_in_milliseconds(ma_sound_group* pGroup, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInMilliseconds) { ma_sound_set_fade_in_milliseconds(pGroup, volumeBeg, volumeEnd, fadeLengthInMilliseconds); } MA_API float ma_sound_group_get_current_fade_volume(ma_sound_group* pGroup) { return ma_sound_get_current_fade_volume(pGroup); } MA_API void ma_sound_group_set_start_time_in_pcm_frames(ma_sound_group* pGroup, ma_uint64 absoluteGlobalTimeInFrames) { ma_sound_set_start_time_in_pcm_frames(pGroup, absoluteGlobalTimeInFrames); } MA_API void ma_sound_group_set_start_time_in_milliseconds(ma_sound_group* pGroup, ma_uint64 absoluteGlobalTimeInMilliseconds) { ma_sound_set_start_time_in_milliseconds(pGroup, absoluteGlobalTimeInMilliseconds); } MA_API void ma_sound_group_set_stop_time_in_pcm_frames(ma_sound_group* pGroup, ma_uint64 absoluteGlobalTimeInFrames) { ma_sound_set_stop_time_in_pcm_frames(pGroup, absoluteGlobalTimeInFrames); } MA_API void ma_sound_group_set_stop_time_in_milliseconds(ma_sound_group* pGroup, ma_uint64 absoluteGlobalTimeInMilliseconds) { ma_sound_set_stop_time_in_milliseconds(pGroup, absoluteGlobalTimeInMilliseconds); } MA_API ma_bool32 ma_sound_group_is_playing(const ma_sound_group* pGroup) { return ma_sound_is_playing(pGroup); } MA_API ma_uint64 ma_sound_group_get_time_in_pcm_frames(const ma_sound_group* pGroup) { return ma_sound_get_time_in_pcm_frames(pGroup); } #endif /* MA_NO_ENGINE */ /* END SECTION: miniaudio_engine.c */ /************************************************************************************************************************************************************** *************************************************************************************************************************************************************** Auto Generated ============== All code below is auto-generated from a tool. This mostly consists of decoding backend implementations such as dr_wav, dr_flac, etc. If you find a bug in the code below please report the bug to the respective repository for the relevant project (probably dr_libs). *************************************************************************************************************************************************************** **************************************************************************************************************************************************************/ #if !defined(MA_NO_WAV) && (!defined(MA_NO_DECODING) || !defined(MA_NO_ENCODING)) #if !defined(DR_WAV_IMPLEMENTATION) && !defined(DRWAV_IMPLEMENTATION) /* For backwards compatibility. Will be removed in version 0.11 for cleanliness. */ /* dr_wav_c begin */ #ifndef dr_wav_c #define dr_wav_c #ifdef __MRC__ #pragma options opt off #endif #include #include #include #ifndef DR_WAV_NO_STDIO #include #ifndef DR_WAV_NO_WCHAR #include #endif #endif #ifndef DRWAV_ASSERT #include #define DRWAV_ASSERT(expression) assert(expression) #endif #ifndef DRWAV_MALLOC #define DRWAV_MALLOC(sz) malloc((sz)) #endif #ifndef DRWAV_REALLOC #define DRWAV_REALLOC(p, sz) realloc((p), (sz)) #endif #ifndef DRWAV_FREE #define DRWAV_FREE(p) free((p)) #endif #ifndef DRWAV_COPY_MEMORY #define DRWAV_COPY_MEMORY(dst, src, sz) memcpy((dst), (src), (sz)) #endif #ifndef DRWAV_ZERO_MEMORY #define DRWAV_ZERO_MEMORY(p, sz) memset((p), 0, (sz)) #endif #ifndef DRWAV_ZERO_OBJECT #define DRWAV_ZERO_OBJECT(p) DRWAV_ZERO_MEMORY((p), sizeof(*p)) #endif #define drwav_countof(x) (sizeof(x) / sizeof(x[0])) #define drwav_align(x, a) ((((x) + (a) - 1) / (a)) * (a)) #define drwav_min(a, b) (((a) < (b)) ? (a) : (b)) #define drwav_max(a, b) (((a) > (b)) ? (a) : (b)) #define drwav_clamp(x, lo, hi) (drwav_max((lo), drwav_min((hi), (x)))) #define drwav_offset_ptr(p, offset) (((drwav_uint8*)(p)) + (offset)) #define DRWAV_MAX_SIMD_VECTOR_SIZE 64 #if defined(__x86_64__) || defined(_M_X64) #define DRWAV_X64 #elif defined(__i386) || defined(_M_IX86) #define DRWAV_X86 #elif defined(__arm__) || defined(_M_ARM) #define DRWAV_ARM #endif #ifdef _MSC_VER #define DRWAV_INLINE __forceinline #elif defined(__GNUC__) #if defined(__STRICT_ANSI__) #define DRWAV_GNUC_INLINE_HINT __inline__ #else #define DRWAV_GNUC_INLINE_HINT inline #endif #if (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 2)) || defined(__clang__) #define DRWAV_INLINE DRWAV_GNUC_INLINE_HINT __attribute__((always_inline)) #else #define DRWAV_INLINE DRWAV_GNUC_INLINE_HINT #endif #elif defined(__WATCOMC__) #define DRWAV_INLINE __inline #else #define DRWAV_INLINE #endif #if defined(SIZE_MAX) #define DRWAV_SIZE_MAX SIZE_MAX #else #if defined(_WIN64) || defined(_LP64) || defined(__LP64__) #define DRWAV_SIZE_MAX ((drwav_uint64)0xFFFFFFFFFFFFFFFF) #else #define DRWAV_SIZE_MAX 0xFFFFFFFF #endif #endif #if defined(_MSC_VER) && _MSC_VER >= 1400 #define DRWAV_HAS_BYTESWAP16_INTRINSIC #define DRWAV_HAS_BYTESWAP32_INTRINSIC #define DRWAV_HAS_BYTESWAP64_INTRINSIC #elif defined(__clang__) #if defined(__has_builtin) #if __has_builtin(__builtin_bswap16) #define DRWAV_HAS_BYTESWAP16_INTRINSIC #endif #if __has_builtin(__builtin_bswap32) #define DRWAV_HAS_BYTESWAP32_INTRINSIC #endif #if __has_builtin(__builtin_bswap64) #define DRWAV_HAS_BYTESWAP64_INTRINSIC #endif #endif #elif defined(__GNUC__) #if ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3)) #define DRWAV_HAS_BYTESWAP32_INTRINSIC #define DRWAV_HAS_BYTESWAP64_INTRINSIC #endif #if ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)) #define DRWAV_HAS_BYTESWAP16_INTRINSIC #endif #endif DRWAV_API void drwav_version(drwav_uint32* pMajor, drwav_uint32* pMinor, drwav_uint32* pRevision) { if (pMajor) { *pMajor = DRWAV_VERSION_MAJOR; } if (pMinor) { *pMinor = DRWAV_VERSION_MINOR; } if (pRevision) { *pRevision = DRWAV_VERSION_REVISION; } } DRWAV_API const char* drwav_version_string(void) { return DRWAV_VERSION_STRING; } #ifndef DRWAV_MAX_SAMPLE_RATE #define DRWAV_MAX_SAMPLE_RATE 384000 #endif #ifndef DRWAV_MAX_CHANNELS #define DRWAV_MAX_CHANNELS 256 #endif #ifndef DRWAV_MAX_BITS_PER_SAMPLE #define DRWAV_MAX_BITS_PER_SAMPLE 64 #endif static const drwav_uint8 drwavGUID_W64_RIFF[16] = {0x72,0x69,0x66,0x66, 0x2E,0x91, 0xCF,0x11, 0xA5,0xD6, 0x28,0xDB,0x04,0xC1,0x00,0x00}; static const drwav_uint8 drwavGUID_W64_WAVE[16] = {0x77,0x61,0x76,0x65, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A}; static const drwav_uint8 drwavGUID_W64_FMT [16] = {0x66,0x6D,0x74,0x20, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A}; static const drwav_uint8 drwavGUID_W64_FACT[16] = {0x66,0x61,0x63,0x74, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A}; static const drwav_uint8 drwavGUID_W64_DATA[16] = {0x64,0x61,0x74,0x61, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A}; static DRWAV_INLINE int drwav__is_little_endian(void) { #if defined(DRWAV_X86) || defined(DRWAV_X64) return DRWAV_TRUE; #elif defined(__BYTE_ORDER) && defined(__LITTLE_ENDIAN) && __BYTE_ORDER == __LITTLE_ENDIAN return DRWAV_TRUE; #else int n = 1; return (*(char*)&n) == 1; #endif } static DRWAV_INLINE void drwav_bytes_to_guid(const drwav_uint8* data, drwav_uint8* guid) { int i; for (i = 0; i < 16; ++i) { guid[i] = data[i]; } } static DRWAV_INLINE drwav_uint16 drwav__bswap16(drwav_uint16 n) { #ifdef DRWAV_HAS_BYTESWAP16_INTRINSIC #if defined(_MSC_VER) return _byteswap_ushort(n); #elif defined(__GNUC__) || defined(__clang__) return __builtin_bswap16(n); #else #error "This compiler does not support the byte swap intrinsic." #endif #else return ((n & 0xFF00) >> 8) | ((n & 0x00FF) << 8); #endif } static DRWAV_INLINE drwav_uint32 drwav__bswap32(drwav_uint32 n) { #ifdef DRWAV_HAS_BYTESWAP32_INTRINSIC #if defined(_MSC_VER) return _byteswap_ulong(n); #elif defined(__GNUC__) || defined(__clang__) #if defined(DRWAV_ARM) && (defined(__ARM_ARCH) && __ARM_ARCH >= 6) && !defined(DRWAV_64BIT) drwav_uint32 r; __asm__ __volatile__ ( #if defined(DRWAV_64BIT) "rev %w[out], %w[in]" : [out]"=r"(r) : [in]"r"(n) #else "rev %[out], %[in]" : [out]"=r"(r) : [in]"r"(n) #endif ); return r; #else return __builtin_bswap32(n); #endif #else #error "This compiler does not support the byte swap intrinsic." #endif #else return ((n & 0xFF000000) >> 24) | ((n & 0x00FF0000) >> 8) | ((n & 0x0000FF00) << 8) | ((n & 0x000000FF) << 24); #endif } static DRWAV_INLINE drwav_uint64 drwav__bswap64(drwav_uint64 n) { #ifdef DRWAV_HAS_BYTESWAP64_INTRINSIC #if defined(_MSC_VER) return _byteswap_uint64(n); #elif defined(__GNUC__) || defined(__clang__) return __builtin_bswap64(n); #else #error "This compiler does not support the byte swap intrinsic." #endif #else return ((n & ((drwav_uint64)0xFF000000 << 32)) >> 56) | ((n & ((drwav_uint64)0x00FF0000 << 32)) >> 40) | ((n & ((drwav_uint64)0x0000FF00 << 32)) >> 24) | ((n & ((drwav_uint64)0x000000FF << 32)) >> 8) | ((n & ((drwav_uint64)0xFF000000 )) << 8) | ((n & ((drwav_uint64)0x00FF0000 )) << 24) | ((n & ((drwav_uint64)0x0000FF00 )) << 40) | ((n & ((drwav_uint64)0x000000FF )) << 56); #endif } static DRWAV_INLINE drwav_int16 drwav__bswap_s16(drwav_int16 n) { return (drwav_int16)drwav__bswap16((drwav_uint16)n); } static DRWAV_INLINE void drwav__bswap_samples_s16(drwav_int16* pSamples, drwav_uint64 sampleCount) { drwav_uint64 iSample; for (iSample = 0; iSample < sampleCount; iSample += 1) { pSamples[iSample] = drwav__bswap_s16(pSamples[iSample]); } } static DRWAV_INLINE void drwav__bswap_s24(drwav_uint8* p) { drwav_uint8 t; t = p[0]; p[0] = p[2]; p[2] = t; } static DRWAV_INLINE void drwav__bswap_samples_s24(drwav_uint8* pSamples, drwav_uint64 sampleCount) { drwav_uint64 iSample; for (iSample = 0; iSample < sampleCount; iSample += 1) { drwav_uint8* pSample = pSamples + (iSample*3); drwav__bswap_s24(pSample); } } static DRWAV_INLINE drwav_int32 drwav__bswap_s32(drwav_int32 n) { return (drwav_int32)drwav__bswap32((drwav_uint32)n); } static DRWAV_INLINE void drwav__bswap_samples_s32(drwav_int32* pSamples, drwav_uint64 sampleCount) { drwav_uint64 iSample; for (iSample = 0; iSample < sampleCount; iSample += 1) { pSamples[iSample] = drwav__bswap_s32(pSamples[iSample]); } } static DRWAV_INLINE float drwav__bswap_f32(float n) { union { drwav_uint32 i; float f; } x; x.f = n; x.i = drwav__bswap32(x.i); return x.f; } static DRWAV_INLINE void drwav__bswap_samples_f32(float* pSamples, drwav_uint64 sampleCount) { drwav_uint64 iSample; for (iSample = 0; iSample < sampleCount; iSample += 1) { pSamples[iSample] = drwav__bswap_f32(pSamples[iSample]); } } static DRWAV_INLINE double drwav__bswap_f64(double n) { union { drwav_uint64 i; double f; } x; x.f = n; x.i = drwav__bswap64(x.i); return x.f; } static DRWAV_INLINE void drwav__bswap_samples_f64(double* pSamples, drwav_uint64 sampleCount) { drwav_uint64 iSample; for (iSample = 0; iSample < sampleCount; iSample += 1) { pSamples[iSample] = drwav__bswap_f64(pSamples[iSample]); } } static DRWAV_INLINE void drwav__bswap_samples_pcm(void* pSamples, drwav_uint64 sampleCount, drwav_uint32 bytesPerSample) { switch (bytesPerSample) { case 1: { } break; case 2: { drwav__bswap_samples_s16((drwav_int16*)pSamples, sampleCount); } break; case 3: { drwav__bswap_samples_s24((drwav_uint8*)pSamples, sampleCount); } break; case 4: { drwav__bswap_samples_s32((drwav_int32*)pSamples, sampleCount); } break; default: { DRWAV_ASSERT(DRWAV_FALSE); } break; } } static DRWAV_INLINE void drwav__bswap_samples_ieee(void* pSamples, drwav_uint64 sampleCount, drwav_uint32 bytesPerSample) { switch (bytesPerSample) { #if 0 case 2: { drwav__bswap_samples_f16((drwav_float16*)pSamples, sampleCount); } break; #endif case 4: { drwav__bswap_samples_f32((float*)pSamples, sampleCount); } break; case 8: { drwav__bswap_samples_f64((double*)pSamples, sampleCount); } break; default: { DRWAV_ASSERT(DRWAV_FALSE); } break; } } static DRWAV_INLINE void drwav__bswap_samples(void* pSamples, drwav_uint64 sampleCount, drwav_uint32 bytesPerSample, drwav_uint16 format) { switch (format) { case DR_WAVE_FORMAT_PCM: { drwav__bswap_samples_pcm(pSamples, sampleCount, bytesPerSample); } break; case DR_WAVE_FORMAT_IEEE_FLOAT: { drwav__bswap_samples_ieee(pSamples, sampleCount, bytesPerSample); } break; case DR_WAVE_FORMAT_ALAW: case DR_WAVE_FORMAT_MULAW: { drwav__bswap_samples_s16((drwav_int16*)pSamples, sampleCount); } break; case DR_WAVE_FORMAT_ADPCM: case DR_WAVE_FORMAT_DVI_ADPCM: default: { DRWAV_ASSERT(DRWAV_FALSE); } break; } } DRWAV_PRIVATE void* drwav__malloc_default(size_t sz, void* pUserData) { (void)pUserData; return DRWAV_MALLOC(sz); } DRWAV_PRIVATE void* drwav__realloc_default(void* p, size_t sz, void* pUserData) { (void)pUserData; return DRWAV_REALLOC(p, sz); } DRWAV_PRIVATE void drwav__free_default(void* p, void* pUserData) { (void)pUserData; DRWAV_FREE(p); } DRWAV_PRIVATE void* drwav__malloc_from_callbacks(size_t sz, const drwav_allocation_callbacks* pAllocationCallbacks) { if (pAllocationCallbacks == NULL) { return NULL; } if (pAllocationCallbacks->onMalloc != NULL) { return pAllocationCallbacks->onMalloc(sz, pAllocationCallbacks->pUserData); } if (pAllocationCallbacks->onRealloc != NULL) { return pAllocationCallbacks->onRealloc(NULL, sz, pAllocationCallbacks->pUserData); } return NULL; } DRWAV_PRIVATE void* drwav__realloc_from_callbacks(void* p, size_t szNew, size_t szOld, const drwav_allocation_callbacks* pAllocationCallbacks) { if (pAllocationCallbacks == NULL) { return NULL; } if (pAllocationCallbacks->onRealloc != NULL) { return pAllocationCallbacks->onRealloc(p, szNew, pAllocationCallbacks->pUserData); } if (pAllocationCallbacks->onMalloc != NULL && pAllocationCallbacks->onFree != NULL) { void* p2; p2 = pAllocationCallbacks->onMalloc(szNew, pAllocationCallbacks->pUserData); if (p2 == NULL) { return NULL; } if (p != NULL) { DRWAV_COPY_MEMORY(p2, p, szOld); pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData); } return p2; } return NULL; } DRWAV_PRIVATE void drwav__free_from_callbacks(void* p, const drwav_allocation_callbacks* pAllocationCallbacks) { if (p == NULL || pAllocationCallbacks == NULL) { return; } if (pAllocationCallbacks->onFree != NULL) { pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData); } } DRWAV_PRIVATE drwav_allocation_callbacks drwav_copy_allocation_callbacks_or_defaults(const drwav_allocation_callbacks* pAllocationCallbacks) { if (pAllocationCallbacks != NULL) { return *pAllocationCallbacks; } else { drwav_allocation_callbacks allocationCallbacks; allocationCallbacks.pUserData = NULL; allocationCallbacks.onMalloc = drwav__malloc_default; allocationCallbacks.onRealloc = drwav__realloc_default; allocationCallbacks.onFree = drwav__free_default; return allocationCallbacks; } } static DRWAV_INLINE drwav_bool32 drwav__is_compressed_format_tag(drwav_uint16 formatTag) { return formatTag == DR_WAVE_FORMAT_ADPCM || formatTag == DR_WAVE_FORMAT_DVI_ADPCM; } DRWAV_PRIVATE unsigned int drwav__chunk_padding_size_riff(drwav_uint64 chunkSize) { return (unsigned int)(chunkSize % 2); } DRWAV_PRIVATE unsigned int drwav__chunk_padding_size_w64(drwav_uint64 chunkSize) { return (unsigned int)(chunkSize % 8); } DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__msadpcm(drwav* pWav, drwav_uint64 samplesToRead, drwav_int16* pBufferOut); DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__ima(drwav* pWav, drwav_uint64 samplesToRead, drwav_int16* pBufferOut); DRWAV_PRIVATE drwav_bool32 drwav_init_write__internal(drwav* pWav, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount); DRWAV_PRIVATE drwav_result drwav__read_chunk_header(drwav_read_proc onRead, void* pUserData, drwav_container container, drwav_uint64* pRunningBytesReadOut, drwav_chunk_header* pHeaderOut) { if (container == drwav_container_riff || container == drwav_container_rf64) { drwav_uint8 sizeInBytes[4]; if (onRead(pUserData, pHeaderOut->id.fourcc, 4) != 4) { return DRWAV_AT_END; } if (onRead(pUserData, sizeInBytes, 4) != 4) { return DRWAV_INVALID_FILE; } pHeaderOut->sizeInBytes = drwav_bytes_to_u32(sizeInBytes); pHeaderOut->paddingSize = drwav__chunk_padding_size_riff(pHeaderOut->sizeInBytes); *pRunningBytesReadOut += 8; } else { drwav_uint8 sizeInBytes[8]; if (onRead(pUserData, pHeaderOut->id.guid, 16) != 16) { return DRWAV_AT_END; } if (onRead(pUserData, sizeInBytes, 8) != 8) { return DRWAV_INVALID_FILE; } pHeaderOut->sizeInBytes = drwav_bytes_to_u64(sizeInBytes) - 24; pHeaderOut->paddingSize = drwav__chunk_padding_size_w64(pHeaderOut->sizeInBytes); *pRunningBytesReadOut += 24; } return DRWAV_SUCCESS; } DRWAV_PRIVATE drwav_bool32 drwav__seek_forward(drwav_seek_proc onSeek, drwav_uint64 offset, void* pUserData) { drwav_uint64 bytesRemainingToSeek = offset; while (bytesRemainingToSeek > 0) { if (bytesRemainingToSeek > 0x7FFFFFFF) { if (!onSeek(pUserData, 0x7FFFFFFF, drwav_seek_origin_current)) { return DRWAV_FALSE; } bytesRemainingToSeek -= 0x7FFFFFFF; } else { if (!onSeek(pUserData, (int)bytesRemainingToSeek, drwav_seek_origin_current)) { return DRWAV_FALSE; } bytesRemainingToSeek = 0; } } return DRWAV_TRUE; } DRWAV_PRIVATE drwav_bool32 drwav__seek_from_start(drwav_seek_proc onSeek, drwav_uint64 offset, void* pUserData) { if (offset <= 0x7FFFFFFF) { return onSeek(pUserData, (int)offset, drwav_seek_origin_start); } if (!onSeek(pUserData, 0x7FFFFFFF, drwav_seek_origin_start)) { return DRWAV_FALSE; } offset -= 0x7FFFFFFF; for (;;) { if (offset <= 0x7FFFFFFF) { return onSeek(pUserData, (int)offset, drwav_seek_origin_current); } if (!onSeek(pUserData, 0x7FFFFFFF, drwav_seek_origin_current)) { return DRWAV_FALSE; } offset -= 0x7FFFFFFF; } } DRWAV_PRIVATE drwav_bool32 drwav__read_fmt(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, drwav_container container, drwav_uint64* pRunningBytesReadOut, drwav_fmt* fmtOut) { drwav_chunk_header header; drwav_uint8 fmt[16]; if (drwav__read_chunk_header(onRead, pUserData, container, pRunningBytesReadOut, &header) != DRWAV_SUCCESS) { return DRWAV_FALSE; } while (((container == drwav_container_riff || container == drwav_container_rf64) && !drwav_fourcc_equal(header.id.fourcc, "fmt ")) || (container == drwav_container_w64 && !drwav_guid_equal(header.id.guid, drwavGUID_W64_FMT))) { if (!drwav__seek_forward(onSeek, header.sizeInBytes + header.paddingSize, pUserData)) { return DRWAV_FALSE; } *pRunningBytesReadOut += header.sizeInBytes + header.paddingSize; if (drwav__read_chunk_header(onRead, pUserData, container, pRunningBytesReadOut, &header) != DRWAV_SUCCESS) { return DRWAV_FALSE; } } if (container == drwav_container_riff || container == drwav_container_rf64) { if (!drwav_fourcc_equal(header.id.fourcc, "fmt ")) { return DRWAV_FALSE; } } else { if (!drwav_guid_equal(header.id.guid, drwavGUID_W64_FMT)) { return DRWAV_FALSE; } } if (onRead(pUserData, fmt, sizeof(fmt)) != sizeof(fmt)) { return DRWAV_FALSE; } *pRunningBytesReadOut += sizeof(fmt); fmtOut->formatTag = drwav_bytes_to_u16(fmt + 0); fmtOut->channels = drwav_bytes_to_u16(fmt + 2); fmtOut->sampleRate = drwav_bytes_to_u32(fmt + 4); fmtOut->avgBytesPerSec = drwav_bytes_to_u32(fmt + 8); fmtOut->blockAlign = drwav_bytes_to_u16(fmt + 12); fmtOut->bitsPerSample = drwav_bytes_to_u16(fmt + 14); fmtOut->extendedSize = 0; fmtOut->validBitsPerSample = 0; fmtOut->channelMask = 0; DRWAV_ZERO_MEMORY(fmtOut->subFormat, sizeof(fmtOut->subFormat)); if (header.sizeInBytes > 16) { drwav_uint8 fmt_cbSize[2]; int bytesReadSoFar = 0; if (onRead(pUserData, fmt_cbSize, sizeof(fmt_cbSize)) != sizeof(fmt_cbSize)) { return DRWAV_FALSE; } *pRunningBytesReadOut += sizeof(fmt_cbSize); bytesReadSoFar = 18; fmtOut->extendedSize = drwav_bytes_to_u16(fmt_cbSize); if (fmtOut->extendedSize > 0) { if (fmtOut->formatTag == DR_WAVE_FORMAT_EXTENSIBLE) { if (fmtOut->extendedSize != 22) { return DRWAV_FALSE; } } if (fmtOut->formatTag == DR_WAVE_FORMAT_EXTENSIBLE) { drwav_uint8 fmtext[22]; if (onRead(pUserData, fmtext, fmtOut->extendedSize) != fmtOut->extendedSize) { return DRWAV_FALSE; } fmtOut->validBitsPerSample = drwav_bytes_to_u16(fmtext + 0); fmtOut->channelMask = drwav_bytes_to_u32(fmtext + 2); drwav_bytes_to_guid(fmtext + 6, fmtOut->subFormat); } else { if (!onSeek(pUserData, fmtOut->extendedSize, drwav_seek_origin_current)) { return DRWAV_FALSE; } } *pRunningBytesReadOut += fmtOut->extendedSize; bytesReadSoFar += fmtOut->extendedSize; } if (!onSeek(pUserData, (int)(header.sizeInBytes - bytesReadSoFar), drwav_seek_origin_current)) { return DRWAV_FALSE; } *pRunningBytesReadOut += (header.sizeInBytes - bytesReadSoFar); } if (header.paddingSize > 0) { if (!onSeek(pUserData, header.paddingSize, drwav_seek_origin_current)) { return DRWAV_FALSE; } *pRunningBytesReadOut += header.paddingSize; } return DRWAV_TRUE; } DRWAV_PRIVATE size_t drwav__on_read(drwav_read_proc onRead, void* pUserData, void* pBufferOut, size_t bytesToRead, drwav_uint64* pCursor) { size_t bytesRead; DRWAV_ASSERT(onRead != NULL); DRWAV_ASSERT(pCursor != NULL); bytesRead = onRead(pUserData, pBufferOut, bytesToRead); *pCursor += bytesRead; return bytesRead; } #if 0 DRWAV_PRIVATE drwav_bool32 drwav__on_seek(drwav_seek_proc onSeek, void* pUserData, int offset, drwav_seek_origin origin, drwav_uint64* pCursor) { DRWAV_ASSERT(onSeek != NULL); DRWAV_ASSERT(pCursor != NULL); if (!onSeek(pUserData, offset, origin)) { return DRWAV_FALSE; } if (origin == drwav_seek_origin_start) { *pCursor = offset; } else { *pCursor += offset; } return DRWAV_TRUE; } #endif #define DRWAV_SMPL_BYTES 36 #define DRWAV_SMPL_LOOP_BYTES 24 #define DRWAV_INST_BYTES 7 #define DRWAV_ACID_BYTES 24 #define DRWAV_CUE_BYTES 4 #define DRWAV_BEXT_BYTES 602 #define DRWAV_BEXT_DESCRIPTION_BYTES 256 #define DRWAV_BEXT_ORIGINATOR_NAME_BYTES 32 #define DRWAV_BEXT_ORIGINATOR_REF_BYTES 32 #define DRWAV_BEXT_RESERVED_BYTES 180 #define DRWAV_BEXT_UMID_BYTES 64 #define DRWAV_CUE_POINT_BYTES 24 #define DRWAV_LIST_LABEL_OR_NOTE_BYTES 4 #define DRWAV_LIST_LABELLED_TEXT_BYTES 20 #define DRWAV_METADATA_ALIGNMENT 8 typedef enum { drwav__metadata_parser_stage_count, drwav__metadata_parser_stage_read } drwav__metadata_parser_stage; typedef struct { drwav_read_proc onRead; drwav_seek_proc onSeek; void *pReadSeekUserData; drwav__metadata_parser_stage stage; drwav_metadata *pMetadata; drwav_uint32 metadataCount; drwav_uint8 *pData; drwav_uint8 *pDataCursor; drwav_uint64 metadataCursor; drwav_uint64 extraCapacity; } drwav__metadata_parser; DRWAV_PRIVATE size_t drwav__metadata_memory_capacity(drwav__metadata_parser* pParser) { drwav_uint64 cap = sizeof(drwav_metadata) * (drwav_uint64)pParser->metadataCount + pParser->extraCapacity; if (cap > DRWAV_SIZE_MAX) { return 0; } return (size_t)cap; } DRWAV_PRIVATE drwav_uint8* drwav__metadata_get_memory(drwav__metadata_parser* pParser, size_t size, size_t align) { drwav_uint8* pResult; if (align) { drwav_uintptr modulo = (drwav_uintptr)pParser->pDataCursor % align; if (modulo != 0) { pParser->pDataCursor += align - modulo; } } pResult = pParser->pDataCursor; DRWAV_ASSERT((pResult + size) <= (pParser->pData + drwav__metadata_memory_capacity(pParser))); pParser->pDataCursor += size; return pResult; } DRWAV_PRIVATE void drwav__metadata_request_extra_memory_for_stage_2(drwav__metadata_parser* pParser, size_t bytes, size_t align) { size_t extra = bytes + (align ? (align - 1) : 0); pParser->extraCapacity += extra; } DRWAV_PRIVATE drwav_result drwav__metadata_alloc(drwav__metadata_parser* pParser, drwav_allocation_callbacks* pAllocationCallbacks) { if (pParser->extraCapacity != 0 || pParser->metadataCount != 0) { pAllocationCallbacks->onFree(pParser->pData, pAllocationCallbacks->pUserData); pParser->pData = (drwav_uint8*)pAllocationCallbacks->onMalloc(drwav__metadata_memory_capacity(pParser), pAllocationCallbacks->pUserData); pParser->pDataCursor = pParser->pData; if (pParser->pData == NULL) { return DRWAV_OUT_OF_MEMORY; } pParser->pMetadata = (drwav_metadata*)drwav__metadata_get_memory(pParser, sizeof(drwav_metadata) * pParser->metadataCount, 1); pParser->metadataCursor = 0; } return DRWAV_SUCCESS; } DRWAV_PRIVATE size_t drwav__metadata_parser_read(drwav__metadata_parser* pParser, void* pBufferOut, size_t bytesToRead, drwav_uint64* pCursor) { if (pCursor != NULL) { return drwav__on_read(pParser->onRead, pParser->pReadSeekUserData, pBufferOut, bytesToRead, pCursor); } else { return pParser->onRead(pParser->pReadSeekUserData, pBufferOut, bytesToRead); } } DRWAV_PRIVATE drwav_uint64 drwav__read_smpl_to_metadata_obj(drwav__metadata_parser* pParser, const drwav_chunk_header* pChunkHeader, drwav_metadata* pMetadata) { drwav_uint8 smplHeaderData[DRWAV_SMPL_BYTES]; drwav_uint64 totalBytesRead = 0; size_t bytesJustRead = drwav__metadata_parser_read(pParser, smplHeaderData, sizeof(smplHeaderData), &totalBytesRead); DRWAV_ASSERT(pParser->stage == drwav__metadata_parser_stage_read); DRWAV_ASSERT(pChunkHeader != NULL); if (bytesJustRead == sizeof(smplHeaderData)) { drwav_uint32 iSampleLoop; pMetadata->type = drwav_metadata_type_smpl; pMetadata->data.smpl.manufacturerId = drwav_bytes_to_u32(smplHeaderData + 0); pMetadata->data.smpl.productId = drwav_bytes_to_u32(smplHeaderData + 4); pMetadata->data.smpl.samplePeriodNanoseconds = drwav_bytes_to_u32(smplHeaderData + 8); pMetadata->data.smpl.midiUnityNote = drwav_bytes_to_u32(smplHeaderData + 12); pMetadata->data.smpl.midiPitchFraction = drwav_bytes_to_u32(smplHeaderData + 16); pMetadata->data.smpl.smpteFormat = drwav_bytes_to_u32(smplHeaderData + 20); pMetadata->data.smpl.smpteOffset = drwav_bytes_to_u32(smplHeaderData + 24); pMetadata->data.smpl.sampleLoopCount = drwav_bytes_to_u32(smplHeaderData + 28); pMetadata->data.smpl.samplerSpecificDataSizeInBytes = drwav_bytes_to_u32(smplHeaderData + 32); if (pMetadata->data.smpl.sampleLoopCount == (pChunkHeader->sizeInBytes - DRWAV_SMPL_BYTES) / DRWAV_SMPL_LOOP_BYTES) { 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) { drwav_uint8 smplLoopData[DRWAV_SMPL_LOOP_BYTES]; bytesJustRead = drwav__metadata_parser_read(pParser, smplLoopData, sizeof(smplLoopData), &totalBytesRead); if (bytesJustRead == sizeof(smplLoopData)) { pMetadata->data.smpl.pLoops[iSampleLoop].cuePointId = drwav_bytes_to_u32(smplLoopData + 0); pMetadata->data.smpl.pLoops[iSampleLoop].type = drwav_bytes_to_u32(smplLoopData + 4); pMetadata->data.smpl.pLoops[iSampleLoop].firstSampleByteOffset = drwav_bytes_to_u32(smplLoopData + 8); pMetadata->data.smpl.pLoops[iSampleLoop].lastSampleByteOffset = drwav_bytes_to_u32(smplLoopData + 12); pMetadata->data.smpl.pLoops[iSampleLoop].sampleFraction = drwav_bytes_to_u32(smplLoopData + 16); pMetadata->data.smpl.pLoops[iSampleLoop].playCount = drwav_bytes_to_u32(smplLoopData + 20); } else { break; } } if (pMetadata->data.smpl.samplerSpecificDataSizeInBytes > 0) { pMetadata->data.smpl.pSamplerSpecificData = drwav__metadata_get_memory(pParser, pMetadata->data.smpl.samplerSpecificDataSizeInBytes, 1); DRWAV_ASSERT(pMetadata->data.smpl.pSamplerSpecificData != NULL); drwav__metadata_parser_read(pParser, pMetadata->data.smpl.pSamplerSpecificData, pMetadata->data.smpl.samplerSpecificDataSizeInBytes, &totalBytesRead); } } } return totalBytesRead; } DRWAV_PRIVATE drwav_uint64 drwav__read_cue_to_metadata_obj(drwav__metadata_parser* pParser, const drwav_chunk_header* pChunkHeader, drwav_metadata* pMetadata) { drwav_uint8 cueHeaderSectionData[DRWAV_CUE_BYTES]; drwav_uint64 totalBytesRead = 0; size_t bytesJustRead = drwav__metadata_parser_read(pParser, cueHeaderSectionData, sizeof(cueHeaderSectionData), &totalBytesRead); DRWAV_ASSERT(pParser->stage == drwav__metadata_parser_stage_read); if (bytesJustRead == sizeof(cueHeaderSectionData)) { pMetadata->type = drwav_metadata_type_cue; pMetadata->data.cue.cuePointCount = drwav_bytes_to_u32(cueHeaderSectionData); if (pMetadata->data.cue.cuePointCount == (pChunkHeader->sizeInBytes - DRWAV_CUE_BYTES) / DRWAV_CUE_POINT_BYTES) { pMetadata->data.cue.pCuePoints = (drwav_cue_point*)drwav__metadata_get_memory(pParser, sizeof(drwav_cue_point) * pMetadata->data.cue.cuePointCount, DRWAV_METADATA_ALIGNMENT); DRWAV_ASSERT(pMetadata->data.cue.pCuePoints != NULL); if (pMetadata->data.cue.cuePointCount > 0) { drwav_uint32 iCuePoint; for (iCuePoint = 0; iCuePoint < pMetadata->data.cue.cuePointCount; ++iCuePoint) { drwav_uint8 cuePointData[DRWAV_CUE_POINT_BYTES]; bytesJustRead = drwav__metadata_parser_read(pParser, cuePointData, sizeof(cuePointData), &totalBytesRead); if (bytesJustRead == sizeof(cuePointData)) { pMetadata->data.cue.pCuePoints[iCuePoint].id = drwav_bytes_to_u32(cuePointData + 0); pMetadata->data.cue.pCuePoints[iCuePoint].playOrderPosition = drwav_bytes_to_u32(cuePointData + 4); pMetadata->data.cue.pCuePoints[iCuePoint].dataChunkId[0] = cuePointData[8]; pMetadata->data.cue.pCuePoints[iCuePoint].dataChunkId[1] = cuePointData[9]; pMetadata->data.cue.pCuePoints[iCuePoint].dataChunkId[2] = cuePointData[10]; pMetadata->data.cue.pCuePoints[iCuePoint].dataChunkId[3] = cuePointData[11]; pMetadata->data.cue.pCuePoints[iCuePoint].chunkStart = drwav_bytes_to_u32(cuePointData + 12); pMetadata->data.cue.pCuePoints[iCuePoint].blockStart = drwav_bytes_to_u32(cuePointData + 16); pMetadata->data.cue.pCuePoints[iCuePoint].sampleByteOffset = drwav_bytes_to_u32(cuePointData + 20); } else { break; } } } } } return totalBytesRead; } DRWAV_PRIVATE drwav_uint64 drwav__read_inst_to_metadata_obj(drwav__metadata_parser* pParser, drwav_metadata* pMetadata) { drwav_uint8 instData[DRWAV_INST_BYTES]; drwav_uint64 bytesRead = drwav__metadata_parser_read(pParser, instData, sizeof(instData), NULL); DRWAV_ASSERT(pParser->stage == drwav__metadata_parser_stage_read); if (bytesRead == sizeof(instData)) { pMetadata->type = drwav_metadata_type_inst; pMetadata->data.inst.midiUnityNote = (drwav_int8)instData[0]; pMetadata->data.inst.fineTuneCents = (drwav_int8)instData[1]; pMetadata->data.inst.gainDecibels = (drwav_int8)instData[2]; pMetadata->data.inst.lowNote = (drwav_int8)instData[3]; pMetadata->data.inst.highNote = (drwav_int8)instData[4]; pMetadata->data.inst.lowVelocity = (drwav_int8)instData[5]; pMetadata->data.inst.highVelocity = (drwav_int8)instData[6]; } return bytesRead; } DRWAV_PRIVATE drwav_uint64 drwav__read_acid_to_metadata_obj(drwav__metadata_parser* pParser, drwav_metadata* pMetadata) { drwav_uint8 acidData[DRWAV_ACID_BYTES]; drwav_uint64 bytesRead = drwav__metadata_parser_read(pParser, acidData, sizeof(acidData), NULL); DRWAV_ASSERT(pParser->stage == drwav__metadata_parser_stage_read); if (bytesRead == sizeof(acidData)) { pMetadata->type = drwav_metadata_type_acid; pMetadata->data.acid.flags = drwav_bytes_to_u32(acidData + 0); pMetadata->data.acid.midiUnityNote = drwav_bytes_to_u16(acidData + 4); pMetadata->data.acid.reserved1 = drwav_bytes_to_u16(acidData + 6); pMetadata->data.acid.reserved2 = drwav_bytes_to_f32(acidData + 8); pMetadata->data.acid.numBeats = drwav_bytes_to_u32(acidData + 12); pMetadata->data.acid.meterDenominator = drwav_bytes_to_u16(acidData + 16); pMetadata->data.acid.meterNumerator = drwav_bytes_to_u16(acidData + 18); pMetadata->data.acid.tempo = drwav_bytes_to_f32(acidData + 20); } return bytesRead; } DRWAV_PRIVATE size_t drwav__strlen(const char* str) { size_t result = 0; while (*str++) { result += 1; } return result; } DRWAV_PRIVATE size_t drwav__strlen_clamped(const char* str, size_t maxToRead) { size_t result = 0; while (*str++ && result < maxToRead) { result += 1; } return result; } DRWAV_PRIVATE char* drwav__metadata_copy_string(drwav__metadata_parser* pParser, const char* str, size_t maxToRead) { size_t len = drwav__strlen_clamped(str, maxToRead); if (len) { char* result = (char*)drwav__metadata_get_memory(pParser, len + 1, 1); DRWAV_ASSERT(result != NULL); DRWAV_COPY_MEMORY(result, str, len); result[len] = '\0'; return result; } else { return NULL; } } typedef struct { const void* pBuffer; size_t sizeInBytes; size_t cursor; } drwav_buffer_reader; DRWAV_PRIVATE drwav_result drwav_buffer_reader_init(const void* pBuffer, size_t sizeInBytes, drwav_buffer_reader* pReader) { DRWAV_ASSERT(pBuffer != NULL); DRWAV_ASSERT(pReader != NULL); DRWAV_ZERO_OBJECT(pReader); pReader->pBuffer = pBuffer; pReader->sizeInBytes = sizeInBytes; pReader->cursor = 0; return DRWAV_SUCCESS; } DRWAV_PRIVATE const void* drwav_buffer_reader_ptr(const drwav_buffer_reader* pReader) { DRWAV_ASSERT(pReader != NULL); return drwav_offset_ptr(pReader->pBuffer, pReader->cursor); } DRWAV_PRIVATE drwav_result drwav_buffer_reader_seek(drwav_buffer_reader* pReader, size_t bytesToSeek) { DRWAV_ASSERT(pReader != NULL); if (pReader->cursor + bytesToSeek > pReader->sizeInBytes) { return DRWAV_BAD_SEEK; } pReader->cursor += bytesToSeek; return DRWAV_SUCCESS; } DRWAV_PRIVATE drwav_result drwav_buffer_reader_read(drwav_buffer_reader* pReader, void* pDst, size_t bytesToRead, size_t* pBytesRead) { drwav_result result = DRWAV_SUCCESS; size_t bytesRemaining; DRWAV_ASSERT(pReader != NULL); if (pBytesRead != NULL) { *pBytesRead = 0; } bytesRemaining = (pReader->sizeInBytes - pReader->cursor); if (bytesToRead > bytesRemaining) { bytesToRead = bytesRemaining; } if (pDst == NULL) { result = drwav_buffer_reader_seek(pReader, bytesToRead); } else { DRWAV_COPY_MEMORY(pDst, drwav_buffer_reader_ptr(pReader), bytesToRead); pReader->cursor += bytesToRead; } DRWAV_ASSERT(pReader->cursor <= pReader->sizeInBytes); if (result == DRWAV_SUCCESS) { if (pBytesRead != NULL) { *pBytesRead = bytesToRead; } } return DRWAV_SUCCESS; } DRWAV_PRIVATE drwav_result drwav_buffer_reader_read_u16(drwav_buffer_reader* pReader, drwav_uint16* pDst) { drwav_result result; size_t bytesRead; drwav_uint8 data[2]; DRWAV_ASSERT(pReader != NULL); DRWAV_ASSERT(pDst != NULL); *pDst = 0; result = drwav_buffer_reader_read(pReader, data, sizeof(*pDst), &bytesRead); if (result != DRWAV_SUCCESS || bytesRead != sizeof(*pDst)) { return result; } *pDst = drwav_bytes_to_u16(data); return DRWAV_SUCCESS; } DRWAV_PRIVATE drwav_result drwav_buffer_reader_read_u32(drwav_buffer_reader* pReader, drwav_uint32* pDst) { drwav_result result; size_t bytesRead; drwav_uint8 data[4]; DRWAV_ASSERT(pReader != NULL); DRWAV_ASSERT(pDst != NULL); *pDst = 0; result = drwav_buffer_reader_read(pReader, data, sizeof(*pDst), &bytesRead); if (result != DRWAV_SUCCESS || bytesRead != sizeof(*pDst)) { return result; } *pDst = drwav_bytes_to_u32(data); return DRWAV_SUCCESS; } DRWAV_PRIVATE drwav_uint64 drwav__read_bext_to_metadata_obj(drwav__metadata_parser* pParser, drwav_metadata* pMetadata, drwav_uint64 chunkSize) { drwav_uint8 bextData[DRWAV_BEXT_BYTES]; size_t bytesRead = drwav__metadata_parser_read(pParser, bextData, sizeof(bextData), NULL); DRWAV_ASSERT(pParser->stage == drwav__metadata_parser_stage_read); if (bytesRead == sizeof(bextData)) { drwav_buffer_reader reader; drwav_uint32 timeReferenceLow; drwav_uint32 timeReferenceHigh; size_t extraBytes; pMetadata->type = drwav_metadata_type_bext; if (drwav_buffer_reader_init(bextData, bytesRead, &reader) == DRWAV_SUCCESS) { pMetadata->data.bext.pDescription = drwav__metadata_copy_string(pParser, (const char*)drwav_buffer_reader_ptr(&reader), DRWAV_BEXT_DESCRIPTION_BYTES); drwav_buffer_reader_seek(&reader, DRWAV_BEXT_DESCRIPTION_BYTES); pMetadata->data.bext.pOriginatorName = drwav__metadata_copy_string(pParser, (const char*)drwav_buffer_reader_ptr(&reader), DRWAV_BEXT_ORIGINATOR_NAME_BYTES); drwav_buffer_reader_seek(&reader, DRWAV_BEXT_ORIGINATOR_NAME_BYTES); pMetadata->data.bext.pOriginatorReference = drwav__metadata_copy_string(pParser, (const char*)drwav_buffer_reader_ptr(&reader), DRWAV_BEXT_ORIGINATOR_REF_BYTES); drwav_buffer_reader_seek(&reader, DRWAV_BEXT_ORIGINATOR_REF_BYTES); drwav_buffer_reader_read(&reader, pMetadata->data.bext.pOriginationDate, sizeof(pMetadata->data.bext.pOriginationDate), NULL); drwav_buffer_reader_read(&reader, pMetadata->data.bext.pOriginationTime, sizeof(pMetadata->data.bext.pOriginationTime), NULL); drwav_buffer_reader_read_u32(&reader, &timeReferenceLow); drwav_buffer_reader_read_u32(&reader, &timeReferenceHigh); pMetadata->data.bext.timeReference = ((drwav_uint64)timeReferenceHigh << 32) + timeReferenceLow; drwav_buffer_reader_read_u16(&reader, &pMetadata->data.bext.version); pMetadata->data.bext.pUMID = drwav__metadata_get_memory(pParser, DRWAV_BEXT_UMID_BYTES, 1); drwav_buffer_reader_read(&reader, pMetadata->data.bext.pUMID, DRWAV_BEXT_UMID_BYTES, NULL); drwav_buffer_reader_read_u16(&reader, &pMetadata->data.bext.loudnessValue); drwav_buffer_reader_read_u16(&reader, &pMetadata->data.bext.loudnessRange); drwav_buffer_reader_read_u16(&reader, &pMetadata->data.bext.maxTruePeakLevel); drwav_buffer_reader_read_u16(&reader, &pMetadata->data.bext.maxMomentaryLoudness); drwav_buffer_reader_read_u16(&reader, &pMetadata->data.bext.maxShortTermLoudness); DRWAV_ASSERT((drwav_offset_ptr(drwav_buffer_reader_ptr(&reader), DRWAV_BEXT_RESERVED_BYTES)) == (bextData + DRWAV_BEXT_BYTES)); extraBytes = (size_t)(chunkSize - DRWAV_BEXT_BYTES); if (extraBytes > 0) { pMetadata->data.bext.pCodingHistory = (char*)drwav__metadata_get_memory(pParser, extraBytes + 1, 1); DRWAV_ASSERT(pMetadata->data.bext.pCodingHistory != NULL); bytesRead += drwav__metadata_parser_read(pParser, pMetadata->data.bext.pCodingHistory, extraBytes, NULL); pMetadata->data.bext.codingHistorySize = (drwav_uint32)drwav__strlen(pMetadata->data.bext.pCodingHistory); } else { pMetadata->data.bext.pCodingHistory = NULL; pMetadata->data.bext.codingHistorySize = 0; } } } return bytesRead; } DRWAV_PRIVATE drwav_uint64 drwav__read_list_label_or_note_to_metadata_obj(drwav__metadata_parser* pParser, drwav_metadata* pMetadata, drwav_uint64 chunkSize, drwav_metadata_type type) { drwav_uint8 cueIDBuffer[DRWAV_LIST_LABEL_OR_NOTE_BYTES]; drwav_uint64 totalBytesRead = 0; size_t bytesJustRead = drwav__metadata_parser_read(pParser, cueIDBuffer, sizeof(cueIDBuffer), &totalBytesRead); DRWAV_ASSERT(pParser->stage == drwav__metadata_parser_stage_read); if (bytesJustRead == sizeof(cueIDBuffer)) { drwav_uint32 sizeIncludingNullTerminator; pMetadata->type = type; pMetadata->data.labelOrNote.cuePointId = drwav_bytes_to_u32(cueIDBuffer); sizeIncludingNullTerminator = (drwav_uint32)chunkSize - DRWAV_LIST_LABEL_OR_NOTE_BYTES; if (sizeIncludingNullTerminator > 0) { pMetadata->data.labelOrNote.stringLength = sizeIncludingNullTerminator - 1; pMetadata->data.labelOrNote.pString = (char*)drwav__metadata_get_memory(pParser, sizeIncludingNullTerminator, 1); DRWAV_ASSERT(pMetadata->data.labelOrNote.pString != NULL); drwav__metadata_parser_read(pParser, pMetadata->data.labelOrNote.pString, sizeIncludingNullTerminator, &totalBytesRead); } else { pMetadata->data.labelOrNote.stringLength = 0; pMetadata->data.labelOrNote.pString = NULL; } } return totalBytesRead; } DRWAV_PRIVATE drwav_uint64 drwav__read_list_labelled_cue_region_to_metadata_obj(drwav__metadata_parser* pParser, drwav_metadata* pMetadata, drwav_uint64 chunkSize) { drwav_uint8 buffer[DRWAV_LIST_LABELLED_TEXT_BYTES]; drwav_uint64 totalBytesRead = 0; size_t bytesJustRead = drwav__metadata_parser_read(pParser, buffer, sizeof(buffer), &totalBytesRead); DRWAV_ASSERT(pParser->stage == drwav__metadata_parser_stage_read); if (bytesJustRead == sizeof(buffer)) { drwav_uint32 sizeIncludingNullTerminator; pMetadata->type = drwav_metadata_type_list_labelled_cue_region; pMetadata->data.labelledCueRegion.cuePointId = drwav_bytes_to_u32(buffer + 0); pMetadata->data.labelledCueRegion.sampleLength = drwav_bytes_to_u32(buffer + 4); pMetadata->data.labelledCueRegion.purposeId[0] = buffer[8]; pMetadata->data.labelledCueRegion.purposeId[1] = buffer[9]; pMetadata->data.labelledCueRegion.purposeId[2] = buffer[10]; pMetadata->data.labelledCueRegion.purposeId[3] = buffer[11]; pMetadata->data.labelledCueRegion.country = drwav_bytes_to_u16(buffer + 12); pMetadata->data.labelledCueRegion.language = drwav_bytes_to_u16(buffer + 14); pMetadata->data.labelledCueRegion.dialect = drwav_bytes_to_u16(buffer + 16); pMetadata->data.labelledCueRegion.codePage = drwav_bytes_to_u16(buffer + 18); sizeIncludingNullTerminator = (drwav_uint32)chunkSize - DRWAV_LIST_LABELLED_TEXT_BYTES; if (sizeIncludingNullTerminator > 0) { pMetadata->data.labelledCueRegion.stringLength = sizeIncludingNullTerminator - 1; pMetadata->data.labelledCueRegion.pString = (char*)drwav__metadata_get_memory(pParser, sizeIncludingNullTerminator, 1); DRWAV_ASSERT(pMetadata->data.labelledCueRegion.pString != NULL); drwav__metadata_parser_read(pParser, pMetadata->data.labelledCueRegion.pString, sizeIncludingNullTerminator, &totalBytesRead); } else { pMetadata->data.labelledCueRegion.stringLength = 0; pMetadata->data.labelledCueRegion.pString = NULL; } } return totalBytesRead; } DRWAV_PRIVATE drwav_uint64 drwav__metadata_process_info_text_chunk(drwav__metadata_parser* pParser, drwav_uint64 chunkSize, drwav_metadata_type type) { drwav_uint64 bytesRead = 0; drwav_uint32 stringSizeWithNullTerminator = (drwav_uint32)chunkSize; if (pParser->stage == drwav__metadata_parser_stage_count) { pParser->metadataCount += 1; drwav__metadata_request_extra_memory_for_stage_2(pParser, stringSizeWithNullTerminator, 1); } else { drwav_metadata* pMetadata = &pParser->pMetadata[pParser->metadataCursor]; pMetadata->type = type; if (stringSizeWithNullTerminator > 0) { pMetadata->data.infoText.stringLength = stringSizeWithNullTerminator - 1; pMetadata->data.infoText.pString = (char*)drwav__metadata_get_memory(pParser, stringSizeWithNullTerminator, 1); DRWAV_ASSERT(pMetadata->data.infoText.pString != NULL); bytesRead = drwav__metadata_parser_read(pParser, pMetadata->data.infoText.pString, (size_t)stringSizeWithNullTerminator, NULL); if (bytesRead == chunkSize) { pParser->metadataCursor += 1; } else { } } else { pMetadata->data.infoText.stringLength = 0; pMetadata->data.infoText.pString = NULL; pParser->metadataCursor += 1; } } return bytesRead; } DRWAV_PRIVATE drwav_uint64 drwav__metadata_process_unknown_chunk(drwav__metadata_parser* pParser, const drwav_uint8* pChunkId, drwav_uint64 chunkSize, drwav_metadata_location location) { drwav_uint64 bytesRead = 0; if (location == drwav_metadata_location_invalid) { return 0; } if (drwav_fourcc_equal(pChunkId, "data") || drwav_fourcc_equal(pChunkId, "fmt") || drwav_fourcc_equal(pChunkId, "fact")) { return 0; } if (pParser->stage == drwav__metadata_parser_stage_count) { pParser->metadataCount += 1; drwav__metadata_request_extra_memory_for_stage_2(pParser, (size_t)chunkSize, 1); } else { drwav_metadata* pMetadata = &pParser->pMetadata[pParser->metadataCursor]; pMetadata->type = drwav_metadata_type_unknown; pMetadata->data.unknown.chunkLocation = location; pMetadata->data.unknown.id[0] = pChunkId[0]; pMetadata->data.unknown.id[1] = pChunkId[1]; pMetadata->data.unknown.id[2] = pChunkId[2]; pMetadata->data.unknown.id[3] = pChunkId[3]; pMetadata->data.unknown.dataSizeInBytes = (drwav_uint32)chunkSize; pMetadata->data.unknown.pData = (drwav_uint8 *)drwav__metadata_get_memory(pParser, (size_t)chunkSize, 1); DRWAV_ASSERT(pMetadata->data.unknown.pData != NULL); bytesRead = drwav__metadata_parser_read(pParser, pMetadata->data.unknown.pData, pMetadata->data.unknown.dataSizeInBytes, NULL); if (bytesRead == pMetadata->data.unknown.dataSizeInBytes) { pParser->metadataCursor += 1; } else { } } return bytesRead; } DRWAV_PRIVATE drwav_bool32 drwav__chunk_matches(drwav_metadata_type allowedMetadataTypes, const drwav_uint8* pChunkID, drwav_metadata_type type, const char* pID) { return (allowedMetadataTypes & type) && drwav_fourcc_equal(pChunkID, pID); } DRWAV_PRIVATE drwav_uint64 drwav__metadata_process_chunk(drwav__metadata_parser* pParser, const drwav_chunk_header* pChunkHeader, drwav_metadata_type allowedMetadataTypes) { const drwav_uint8 *pChunkID = pChunkHeader->id.fourcc; drwav_uint64 bytesRead = 0; if (drwav__chunk_matches(allowedMetadataTypes, pChunkID, drwav_metadata_type_smpl, "smpl")) { if (pChunkHeader->sizeInBytes >= DRWAV_SMPL_BYTES) { if (pParser->stage == drwav__metadata_parser_stage_count) { drwav_uint8 buffer[4]; size_t bytesJustRead; if (!pParser->onSeek(pParser->pReadSeekUserData, 28, drwav_seek_origin_current)) { return bytesRead; } bytesRead += 28; bytesJustRead = drwav__metadata_parser_read(pParser, buffer, sizeof(buffer), &bytesRead); if (bytesJustRead == sizeof(buffer)) { drwav_uint32 loopCount = drwav_bytes_to_u32(buffer); drwav_uint64 calculatedLoopCount; calculatedLoopCount = (pChunkHeader->sizeInBytes - DRWAV_SMPL_BYTES) / DRWAV_SMPL_LOOP_BYTES; if (calculatedLoopCount == loopCount) { bytesJustRead = drwav__metadata_parser_read(pParser, buffer, sizeof(buffer), &bytesRead); if (bytesJustRead == sizeof(buffer)) { drwav_uint32 samplerSpecificDataSizeInBytes = drwav_bytes_to_u32(buffer); pParser->metadataCount += 1; drwav__metadata_request_extra_memory_for_stage_2(pParser, sizeof(drwav_smpl_loop) * loopCount, DRWAV_METADATA_ALIGNMENT); drwav__metadata_request_extra_memory_for_stage_2(pParser, samplerSpecificDataSizeInBytes, 1); } } else { } } } else { bytesRead = drwav__read_smpl_to_metadata_obj(pParser, pChunkHeader, &pParser->pMetadata[pParser->metadataCursor]); if (bytesRead == pChunkHeader->sizeInBytes) { pParser->metadataCursor += 1; } else { } } } else { } } else if (drwav__chunk_matches(allowedMetadataTypes, pChunkID, drwav_metadata_type_inst, "inst")) { if (pChunkHeader->sizeInBytes == DRWAV_INST_BYTES) { if (pParser->stage == drwav__metadata_parser_stage_count) { pParser->metadataCount += 1; } else { bytesRead = drwav__read_inst_to_metadata_obj(pParser, &pParser->pMetadata[pParser->metadataCursor]); if (bytesRead == pChunkHeader->sizeInBytes) { pParser->metadataCursor += 1; } else { } } } else { } } else if (drwav__chunk_matches(allowedMetadataTypes, pChunkID, drwav_metadata_type_acid, "acid")) { if (pChunkHeader->sizeInBytes == DRWAV_ACID_BYTES) { if (pParser->stage == drwav__metadata_parser_stage_count) { pParser->metadataCount += 1; } else { bytesRead = drwav__read_acid_to_metadata_obj(pParser, &pParser->pMetadata[pParser->metadataCursor]); if (bytesRead == pChunkHeader->sizeInBytes) { pParser->metadataCursor += 1; } else { } } } else { } } else if (drwav__chunk_matches(allowedMetadataTypes, pChunkID, drwav_metadata_type_cue, "cue ")) { if (pChunkHeader->sizeInBytes >= DRWAV_CUE_BYTES) { if (pParser->stage == drwav__metadata_parser_stage_count) { size_t cueCount; pParser->metadataCount += 1; cueCount = (size_t)(pChunkHeader->sizeInBytes - DRWAV_CUE_BYTES) / DRWAV_CUE_POINT_BYTES; drwav__metadata_request_extra_memory_for_stage_2(pParser, sizeof(drwav_cue_point) * cueCount, DRWAV_METADATA_ALIGNMENT); } else { bytesRead = drwav__read_cue_to_metadata_obj(pParser, pChunkHeader, &pParser->pMetadata[pParser->metadataCursor]); if (bytesRead == pChunkHeader->sizeInBytes) { pParser->metadataCursor += 1; } else { } } } else { } } else if (drwav__chunk_matches(allowedMetadataTypes, pChunkID, drwav_metadata_type_bext, "bext")) { if (pChunkHeader->sizeInBytes >= DRWAV_BEXT_BYTES) { if (pParser->stage == drwav__metadata_parser_stage_count) { char buffer[DRWAV_BEXT_DESCRIPTION_BYTES + 1]; size_t allocSizeNeeded = DRWAV_BEXT_UMID_BYTES; size_t bytesJustRead; buffer[DRWAV_BEXT_DESCRIPTION_BYTES] = '\0'; bytesJustRead = drwav__metadata_parser_read(pParser, buffer, DRWAV_BEXT_DESCRIPTION_BYTES, &bytesRead); if (bytesJustRead != DRWAV_BEXT_DESCRIPTION_BYTES) { return bytesRead; } allocSizeNeeded += drwav__strlen(buffer) + 1; buffer[DRWAV_BEXT_ORIGINATOR_NAME_BYTES] = '\0'; bytesJustRead = drwav__metadata_parser_read(pParser, buffer, DRWAV_BEXT_ORIGINATOR_NAME_BYTES, &bytesRead); if (bytesJustRead != DRWAV_BEXT_ORIGINATOR_NAME_BYTES) { return bytesRead; } allocSizeNeeded += drwav__strlen(buffer) + 1; buffer[DRWAV_BEXT_ORIGINATOR_REF_BYTES] = '\0'; bytesJustRead = drwav__metadata_parser_read(pParser, buffer, DRWAV_BEXT_ORIGINATOR_REF_BYTES, &bytesRead); if (bytesJustRead != DRWAV_BEXT_ORIGINATOR_REF_BYTES) { return bytesRead; } allocSizeNeeded += drwav__strlen(buffer) + 1; allocSizeNeeded += (size_t)pChunkHeader->sizeInBytes - DRWAV_BEXT_BYTES; drwav__metadata_request_extra_memory_for_stage_2(pParser, allocSizeNeeded, 1); pParser->metadataCount += 1; } else { bytesRead = drwav__read_bext_to_metadata_obj(pParser, &pParser->pMetadata[pParser->metadataCursor], pChunkHeader->sizeInBytes); if (bytesRead == pChunkHeader->sizeInBytes) { pParser->metadataCursor += 1; } else { } } } else { } } else if (drwav_fourcc_equal(pChunkID, "LIST") || drwav_fourcc_equal(pChunkID, "list")) { drwav_metadata_location listType = drwav_metadata_location_invalid; while (bytesRead < pChunkHeader->sizeInBytes) { drwav_uint8 subchunkId[4]; drwav_uint8 subchunkSizeBuffer[4]; drwav_uint64 subchunkDataSize; drwav_uint64 subchunkBytesRead = 0; drwav_uint64 bytesJustRead = drwav__metadata_parser_read(pParser, subchunkId, sizeof(subchunkId), &bytesRead); if (bytesJustRead != sizeof(subchunkId)) { break; } if (drwav_fourcc_equal(subchunkId, "adtl")) { listType = drwav_metadata_location_inside_adtl_list; continue; } else if (drwav_fourcc_equal(subchunkId, "INFO")) { listType = drwav_metadata_location_inside_info_list; continue; } bytesJustRead = drwav__metadata_parser_read(pParser, subchunkSizeBuffer, sizeof(subchunkSizeBuffer), &bytesRead); if (bytesJustRead != sizeof(subchunkSizeBuffer)) { break; } subchunkDataSize = drwav_bytes_to_u32(subchunkSizeBuffer); if (drwav__chunk_matches(allowedMetadataTypes, subchunkId, drwav_metadata_type_list_label, "labl") || drwav__chunk_matches(allowedMetadataTypes, subchunkId, drwav_metadata_type_list_note, "note")) { if (subchunkDataSize >= DRWAV_LIST_LABEL_OR_NOTE_BYTES) { drwav_uint64 stringSizeWithNullTerm = subchunkDataSize - DRWAV_LIST_LABEL_OR_NOTE_BYTES; if (pParser->stage == drwav__metadata_parser_stage_count) { pParser->metadataCount += 1; drwav__metadata_request_extra_memory_for_stage_2(pParser, (size_t)stringSizeWithNullTerm, 1); } else { subchunkBytesRead = drwav__read_list_label_or_note_to_metadata_obj(pParser, &pParser->pMetadata[pParser->metadataCursor], subchunkDataSize, drwav_fourcc_equal(subchunkId, "labl") ? drwav_metadata_type_list_label : drwav_metadata_type_list_note); if (subchunkBytesRead == subchunkDataSize) { pParser->metadataCursor += 1; } else { } } } else { } } else if (drwav__chunk_matches(allowedMetadataTypes, subchunkId, drwav_metadata_type_list_labelled_cue_region, "ltxt")) { if (subchunkDataSize >= DRWAV_LIST_LABELLED_TEXT_BYTES) { drwav_uint64 stringSizeWithNullTerminator = subchunkDataSize - DRWAV_LIST_LABELLED_TEXT_BYTES; if (pParser->stage == drwav__metadata_parser_stage_count) { pParser->metadataCount += 1; drwav__metadata_request_extra_memory_for_stage_2(pParser, (size_t)stringSizeWithNullTerminator, 1); } else { subchunkBytesRead = drwav__read_list_labelled_cue_region_to_metadata_obj(pParser, &pParser->pMetadata[pParser->metadataCursor], subchunkDataSize); if (subchunkBytesRead == subchunkDataSize) { pParser->metadataCursor += 1; } else { } } } else { } } else if (drwav__chunk_matches(allowedMetadataTypes, subchunkId, drwav_metadata_type_list_info_software, "ISFT")) { subchunkBytesRead = drwav__metadata_process_info_text_chunk(pParser, subchunkDataSize, drwav_metadata_type_list_info_software); } else if (drwav__chunk_matches(allowedMetadataTypes, subchunkId, drwav_metadata_type_list_info_copyright, "ICOP")) { subchunkBytesRead = drwav__metadata_process_info_text_chunk(pParser, subchunkDataSize, drwav_metadata_type_list_info_copyright); } else if (drwav__chunk_matches(allowedMetadataTypes, subchunkId, drwav_metadata_type_list_info_title, "INAM")) { subchunkBytesRead = drwav__metadata_process_info_text_chunk(pParser, subchunkDataSize, drwav_metadata_type_list_info_title); } else if (drwav__chunk_matches(allowedMetadataTypes, subchunkId, drwav_metadata_type_list_info_artist, "IART")) { subchunkBytesRead = drwav__metadata_process_info_text_chunk(pParser, subchunkDataSize, drwav_metadata_type_list_info_artist); } else if (drwav__chunk_matches(allowedMetadataTypes, subchunkId, drwav_metadata_type_list_info_comment, "ICMT")) { subchunkBytesRead = drwav__metadata_process_info_text_chunk(pParser, subchunkDataSize, drwav_metadata_type_list_info_comment); } else if (drwav__chunk_matches(allowedMetadataTypes, subchunkId, drwav_metadata_type_list_info_date, "ICRD")) { subchunkBytesRead = drwav__metadata_process_info_text_chunk(pParser, subchunkDataSize, drwav_metadata_type_list_info_date); } else if (drwav__chunk_matches(allowedMetadataTypes, subchunkId, drwav_metadata_type_list_info_genre, "IGNR")) { subchunkBytesRead = drwav__metadata_process_info_text_chunk(pParser, subchunkDataSize, drwav_metadata_type_list_info_genre); } else if (drwav__chunk_matches(allowedMetadataTypes, subchunkId, drwav_metadata_type_list_info_album, "IPRD")) { subchunkBytesRead = drwav__metadata_process_info_text_chunk(pParser, subchunkDataSize, drwav_metadata_type_list_info_album); } else if (drwav__chunk_matches(allowedMetadataTypes, subchunkId, drwav_metadata_type_list_info_tracknumber, "ITRK")) { subchunkBytesRead = drwav__metadata_process_info_text_chunk(pParser, subchunkDataSize, drwav_metadata_type_list_info_tracknumber); } else if ((allowedMetadataTypes & drwav_metadata_type_unknown) != 0) { subchunkBytesRead = drwav__metadata_process_unknown_chunk(pParser, subchunkId, subchunkDataSize, listType); } bytesRead += subchunkBytesRead; DRWAV_ASSERT(subchunkBytesRead <= subchunkDataSize); if (subchunkBytesRead < subchunkDataSize) { drwav_uint64 bytesToSeek = subchunkDataSize - subchunkBytesRead; if (!pParser->onSeek(pParser->pReadSeekUserData, (int)bytesToSeek, drwav_seek_origin_current)) { break; } bytesRead += bytesToSeek; } if ((subchunkDataSize % 2) == 1) { if (!pParser->onSeek(pParser->pReadSeekUserData, 1, drwav_seek_origin_current)) { break; } bytesRead += 1; } } } else if ((allowedMetadataTypes & drwav_metadata_type_unknown) != 0) { bytesRead = drwav__metadata_process_unknown_chunk(pParser, pChunkID, pChunkHeader->sizeInBytes, drwav_metadata_location_top_level); } return bytesRead; } DRWAV_PRIVATE drwav_uint32 drwav_get_bytes_per_pcm_frame(drwav* pWav) { drwav_uint32 bytesPerFrame; if ((pWav->bitsPerSample & 0x7) == 0) { bytesPerFrame = (pWav->bitsPerSample * pWav->fmt.channels) >> 3; } else { bytesPerFrame = pWav->fmt.blockAlign; } if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ALAW || pWav->translatedFormatTag == DR_WAVE_FORMAT_MULAW) { if (bytesPerFrame != pWav->fmt.channels) { return 0; } } return bytesPerFrame; } DRWAV_API drwav_uint16 drwav_fmt_get_format(const drwav_fmt* pFMT) { if (pFMT == NULL) { return 0; } if (pFMT->formatTag != DR_WAVE_FORMAT_EXTENSIBLE) { return pFMT->formatTag; } else { return drwav_bytes_to_u16(pFMT->subFormat); } } DRWAV_PRIVATE drwav_bool32 drwav_preinit(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onSeek, void* pReadSeekUserData, const drwav_allocation_callbacks* pAllocationCallbacks) { if (pWav == NULL || onRead == NULL || onSeek == NULL) { return DRWAV_FALSE; } DRWAV_ZERO_MEMORY(pWav, sizeof(*pWav)); pWav->onRead = onRead; pWav->onSeek = onSeek; pWav->pUserData = pReadSeekUserData; pWav->allocationCallbacks = drwav_copy_allocation_callbacks_or_defaults(pAllocationCallbacks); if (pWav->allocationCallbacks.onFree == NULL || (pWav->allocationCallbacks.onMalloc == NULL && pWav->allocationCallbacks.onRealloc == NULL)) { return DRWAV_FALSE; } return DRWAV_TRUE; } DRWAV_PRIVATE drwav_bool32 drwav_init__internal(drwav* pWav, drwav_chunk_proc onChunk, void* pChunkUserData, drwav_uint32 flags) { drwav_uint64 cursor; drwav_bool32 sequential; drwav_uint8 riff[4]; drwav_fmt fmt; unsigned short translatedFormatTag; drwav_bool32 foundDataChunk; drwav_uint64 dataChunkSize = 0; drwav_uint64 sampleCountFromFactChunk = 0; drwav_uint64 chunkSize; drwav__metadata_parser metadataParser; cursor = 0; sequential = (flags & DRWAV_SEQUENTIAL) != 0; if (drwav__on_read(pWav->onRead, pWav->pUserData, riff, sizeof(riff), &cursor) != sizeof(riff)) { return DRWAV_FALSE; } if (drwav_fourcc_equal(riff, "RIFF")) { pWav->container = drwav_container_riff; } else if (drwav_fourcc_equal(riff, "riff")) { int i; drwav_uint8 riff2[12]; pWav->container = drwav_container_w64; if (drwav__on_read(pWav->onRead, pWav->pUserData, riff2, sizeof(riff2), &cursor) != sizeof(riff2)) { return DRWAV_FALSE; } for (i = 0; i < 12; ++i) { if (riff2[i] != drwavGUID_W64_RIFF[i+4]) { return DRWAV_FALSE; } } } else if (drwav_fourcc_equal(riff, "RF64")) { pWav->container = drwav_container_rf64; } else { return DRWAV_FALSE; } if (pWav->container == drwav_container_riff || pWav->container == drwav_container_rf64) { drwav_uint8 chunkSizeBytes[4]; drwav_uint8 wave[4]; if (drwav__on_read(pWav->onRead, pWav->pUserData, chunkSizeBytes, sizeof(chunkSizeBytes), &cursor) != sizeof(chunkSizeBytes)) { return DRWAV_FALSE; } if (pWav->container == drwav_container_riff) { if (drwav_bytes_to_u32(chunkSizeBytes) < 36) { return DRWAV_FALSE; } } else { if (drwav_bytes_to_u32(chunkSizeBytes) != 0xFFFFFFFF) { return DRWAV_FALSE; } } if (drwav__on_read(pWav->onRead, pWav->pUserData, wave, sizeof(wave), &cursor) != sizeof(wave)) { return DRWAV_FALSE; } if (!drwav_fourcc_equal(wave, "WAVE")) { return DRWAV_FALSE; } } else { drwav_uint8 chunkSizeBytes[8]; drwav_uint8 wave[16]; if (drwav__on_read(pWav->onRead, pWav->pUserData, chunkSizeBytes, sizeof(chunkSizeBytes), &cursor) != sizeof(chunkSizeBytes)) { return DRWAV_FALSE; } if (drwav_bytes_to_u64(chunkSizeBytes) < 80) { return DRWAV_FALSE; } if (drwav__on_read(pWav->onRead, pWav->pUserData, wave, sizeof(wave), &cursor) != sizeof(wave)) { return DRWAV_FALSE; } if (!drwav_guid_equal(wave, drwavGUID_W64_WAVE)) { return DRWAV_FALSE; } } if (pWav->container == drwav_container_rf64) { drwav_uint8 sizeBytes[8]; drwav_uint64 bytesRemainingInChunk; drwav_chunk_header header; drwav_result result = drwav__read_chunk_header(pWav->onRead, pWav->pUserData, pWav->container, &cursor, &header); if (result != DRWAV_SUCCESS) { return DRWAV_FALSE; } if (!drwav_fourcc_equal(header.id.fourcc, "ds64")) { return DRWAV_FALSE; } bytesRemainingInChunk = header.sizeInBytes + header.paddingSize; if (!drwav__seek_forward(pWav->onSeek, 8, pWav->pUserData)) { return DRWAV_FALSE; } bytesRemainingInChunk -= 8; cursor += 8; if (drwav__on_read(pWav->onRead, pWav->pUserData, sizeBytes, sizeof(sizeBytes), &cursor) != sizeof(sizeBytes)) { return DRWAV_FALSE; } bytesRemainingInChunk -= 8; dataChunkSize = drwav_bytes_to_u64(sizeBytes); if (drwav__on_read(pWav->onRead, pWav->pUserData, sizeBytes, sizeof(sizeBytes), &cursor) != sizeof(sizeBytes)) { return DRWAV_FALSE; } bytesRemainingInChunk -= 8; sampleCountFromFactChunk = drwav_bytes_to_u64(sizeBytes); if (!drwav__seek_forward(pWav->onSeek, bytesRemainingInChunk, pWav->pUserData)) { return DRWAV_FALSE; } cursor += bytesRemainingInChunk; } if (!drwav__read_fmt(pWav->onRead, pWav->onSeek, pWav->pUserData, pWav->container, &cursor, &fmt)) { return DRWAV_FALSE; } if ((fmt.sampleRate == 0 || fmt.sampleRate > DRWAV_MAX_SAMPLE_RATE) || (fmt.channels == 0 || fmt.channels > DRWAV_MAX_CHANNELS) || (fmt.bitsPerSample == 0 || fmt.bitsPerSample > DRWAV_MAX_BITS_PER_SAMPLE) || fmt.blockAlign == 0) { return DRWAV_FALSE; } translatedFormatTag = fmt.formatTag; if (translatedFormatTag == DR_WAVE_FORMAT_EXTENSIBLE) { translatedFormatTag = drwav_bytes_to_u16(fmt.subFormat + 0); } DRWAV_ZERO_MEMORY(&metadataParser, sizeof(metadataParser)); if (!sequential && pWav->allowedMetadataTypes != drwav_metadata_type_none && (pWav->container == drwav_container_riff || pWav->container == drwav_container_rf64)) { drwav_uint64 cursorForMetadata = cursor; metadataParser.onRead = pWav->onRead; metadataParser.onSeek = pWav->onSeek; metadataParser.pReadSeekUserData = pWav->pUserData; metadataParser.stage = drwav__metadata_parser_stage_count; for (;;) { drwav_result result; drwav_uint64 bytesRead; drwav_uint64 remainingBytes; drwav_chunk_header header; result = drwav__read_chunk_header(pWav->onRead, pWav->pUserData, pWav->container, &cursorForMetadata, &header); if (result != DRWAV_SUCCESS) { break; } bytesRead = drwav__metadata_process_chunk(&metadataParser, &header, pWav->allowedMetadataTypes); DRWAV_ASSERT(bytesRead <= header.sizeInBytes); remainingBytes = header.sizeInBytes - bytesRead + header.paddingSize; if (!drwav__seek_forward(pWav->onSeek, remainingBytes, pWav->pUserData)) { break; } cursorForMetadata += remainingBytes; } if (!drwav__seek_from_start(pWav->onSeek, cursor, pWav->pUserData)) { return DRWAV_FALSE; } drwav__metadata_alloc(&metadataParser, &pWav->allocationCallbacks); metadataParser.stage = drwav__metadata_parser_stage_read; } foundDataChunk = DRWAV_FALSE; for (;;) { drwav_chunk_header header; drwav_result result = drwav__read_chunk_header(pWav->onRead, pWav->pUserData, pWav->container, &cursor, &header); if (result != DRWAV_SUCCESS) { if (!foundDataChunk) { return DRWAV_FALSE; } else { break; } } if (!sequential && onChunk != NULL) { drwav_uint64 callbackBytesRead = onChunk(pChunkUserData, pWav->onRead, pWav->onSeek, pWav->pUserData, &header, pWav->container, &fmt); if (callbackBytesRead > 0) { if (!drwav__seek_from_start(pWav->onSeek, cursor, pWav->pUserData)) { return DRWAV_FALSE; } } } if (!sequential && pWav->allowedMetadataTypes != drwav_metadata_type_none && (pWav->container == drwav_container_riff || pWav->container == drwav_container_rf64)) { drwav_uint64 bytesRead = drwav__metadata_process_chunk(&metadataParser, &header, pWav->allowedMetadataTypes); if (bytesRead > 0) { if (!drwav__seek_from_start(pWav->onSeek, cursor, pWav->pUserData)) { return DRWAV_FALSE; } } } if (!foundDataChunk) { pWav->dataChunkDataPos = cursor; } chunkSize = header.sizeInBytes; if (pWav->container == drwav_container_riff || pWav->container == drwav_container_rf64) { if (drwav_fourcc_equal(header.id.fourcc, "data")) { foundDataChunk = DRWAV_TRUE; if (pWav->container != drwav_container_rf64) { dataChunkSize = chunkSize; } } } else { if (drwav_guid_equal(header.id.guid, drwavGUID_W64_DATA)) { foundDataChunk = DRWAV_TRUE; dataChunkSize = chunkSize; } } if (foundDataChunk && sequential) { break; } if (pWav->container == drwav_container_riff) { if (drwav_fourcc_equal(header.id.fourcc, "fact")) { drwav_uint32 sampleCount; if (drwav__on_read(pWav->onRead, pWav->pUserData, &sampleCount, 4, &cursor) != 4) { return DRWAV_FALSE; } chunkSize -= 4; if (!foundDataChunk) { pWav->dataChunkDataPos = cursor; } if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM) { sampleCountFromFactChunk = sampleCount; } else { sampleCountFromFactChunk = 0; } } } else if (pWav->container == drwav_container_w64) { if (drwav_guid_equal(header.id.guid, drwavGUID_W64_FACT)) { if (drwav__on_read(pWav->onRead, pWav->pUserData, &sampleCountFromFactChunk, 8, &cursor) != 8) { return DRWAV_FALSE; } chunkSize -= 8; if (!foundDataChunk) { pWav->dataChunkDataPos = cursor; } } } else if (pWav->container == drwav_container_rf64) { } chunkSize += header.paddingSize; if (!drwav__seek_forward(pWav->onSeek, chunkSize, pWav->pUserData)) { break; } cursor += chunkSize; if (!foundDataChunk) { pWav->dataChunkDataPos = cursor; } } pWav->pMetadata = metadataParser.pMetadata; pWav->metadataCount = metadataParser.metadataCount; if (!foundDataChunk) { return DRWAV_FALSE; } if (!sequential) { if (!drwav__seek_from_start(pWav->onSeek, pWav->dataChunkDataPos, pWav->pUserData)) { return DRWAV_FALSE; } cursor = pWav->dataChunkDataPos; } pWav->fmt = fmt; pWav->sampleRate = fmt.sampleRate; pWav->channels = fmt.channels; pWav->bitsPerSample = fmt.bitsPerSample; pWav->bytesRemaining = dataChunkSize; pWav->translatedFormatTag = translatedFormatTag; pWav->dataChunkDataSize = dataChunkSize; if (sampleCountFromFactChunk != 0) { pWav->totalPCMFrameCount = sampleCountFromFactChunk; } else { drwav_uint32 bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav); if (bytesPerFrame == 0) { return DRWAV_FALSE; } pWav->totalPCMFrameCount = dataChunkSize / bytesPerFrame; if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM) { drwav_uint64 totalBlockHeaderSizeInBytes; drwav_uint64 blockCount = dataChunkSize / fmt.blockAlign; if ((blockCount * fmt.blockAlign) < dataChunkSize) { blockCount += 1; } totalBlockHeaderSizeInBytes = blockCount * (6*fmt.channels); pWav->totalPCMFrameCount = ((dataChunkSize - totalBlockHeaderSizeInBytes) * 2) / fmt.channels; } if (pWav->translatedFormatTag == DR_WAVE_FORMAT_DVI_ADPCM) { drwav_uint64 totalBlockHeaderSizeInBytes; drwav_uint64 blockCount = dataChunkSize / fmt.blockAlign; if ((blockCount * fmt.blockAlign) < dataChunkSize) { blockCount += 1; } totalBlockHeaderSizeInBytes = blockCount * (4*fmt.channels); pWav->totalPCMFrameCount = ((dataChunkSize - totalBlockHeaderSizeInBytes) * 2) / fmt.channels; pWav->totalPCMFrameCount += blockCount; } } if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM || pWav->translatedFormatTag == DR_WAVE_FORMAT_DVI_ADPCM) { if (pWav->channels > 2) { return DRWAV_FALSE; } } if (drwav_get_bytes_per_pcm_frame(pWav) == 0) { return DRWAV_FALSE; } #ifdef DR_WAV_LIBSNDFILE_COMPAT if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM) { drwav_uint64 blockCount = dataChunkSize / fmt.blockAlign; pWav->totalPCMFrameCount = (((blockCount * (fmt.blockAlign - (6*pWav->channels))) * 2)) / fmt.channels; } if (pWav->translatedFormatTag == DR_WAVE_FORMAT_DVI_ADPCM) { drwav_uint64 blockCount = dataChunkSize / fmt.blockAlign; pWav->totalPCMFrameCount = (((blockCount * (fmt.blockAlign - (4*pWav->channels))) * 2) + (blockCount * pWav->channels)) / fmt.channels; } #endif return DRWAV_TRUE; } DRWAV_API drwav_bool32 drwav_init(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks) { return drwav_init_ex(pWav, onRead, onSeek, NULL, pUserData, NULL, 0, pAllocationCallbacks); } DRWAV_API drwav_bool32 drwav_init_ex(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onSeek, drwav_chunk_proc onChunk, void* pReadSeekUserData, void* pChunkUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks) { if (!drwav_preinit(pWav, onRead, onSeek, pReadSeekUserData, pAllocationCallbacks)) { return DRWAV_FALSE; } return drwav_init__internal(pWav, onChunk, pChunkUserData, flags); } DRWAV_API drwav_bool32 drwav_init_with_metadata(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks) { if (!drwav_preinit(pWav, onRead, onSeek, pUserData, pAllocationCallbacks)) { return DRWAV_FALSE; } pWav->allowedMetadataTypes = drwav_metadata_type_all_including_unknown; return drwav_init__internal(pWav, NULL, NULL, flags); } DRWAV_API drwav_metadata* drwav_take_ownership_of_metadata(drwav* pWav) { drwav_metadata *result = pWav->pMetadata; pWav->pMetadata = NULL; pWav->metadataCount = 0; return result; } DRWAV_PRIVATE size_t drwav__write(drwav* pWav, const void* pData, size_t dataSize) { DRWAV_ASSERT(pWav != NULL); DRWAV_ASSERT(pWav->onWrite != NULL); return pWav->onWrite(pWav->pUserData, pData, dataSize); } DRWAV_PRIVATE size_t drwav__write_byte(drwav* pWav, drwav_uint8 byte) { DRWAV_ASSERT(pWav != NULL); DRWAV_ASSERT(pWav->onWrite != NULL); return pWav->onWrite(pWav->pUserData, &byte, 1); } DRWAV_PRIVATE size_t drwav__write_u16ne_to_le(drwav* pWav, drwav_uint16 value) { DRWAV_ASSERT(pWav != NULL); DRWAV_ASSERT(pWav->onWrite != NULL); if (!drwav__is_little_endian()) { value = drwav__bswap16(value); } return drwav__write(pWav, &value, 2); } DRWAV_PRIVATE size_t drwav__write_u32ne_to_le(drwav* pWav, drwav_uint32 value) { DRWAV_ASSERT(pWav != NULL); DRWAV_ASSERT(pWav->onWrite != NULL); if (!drwav__is_little_endian()) { value = drwav__bswap32(value); } return drwav__write(pWav, &value, 4); } DRWAV_PRIVATE size_t drwav__write_u64ne_to_le(drwav* pWav, drwav_uint64 value) { DRWAV_ASSERT(pWav != NULL); DRWAV_ASSERT(pWav->onWrite != NULL); if (!drwav__is_little_endian()) { value = drwav__bswap64(value); } return drwav__write(pWav, &value, 8); } DRWAV_PRIVATE size_t drwav__write_f32ne_to_le(drwav* pWav, float value) { union { drwav_uint32 u32; float f32; } u; DRWAV_ASSERT(pWav != NULL); DRWAV_ASSERT(pWav->onWrite != NULL); u.f32 = value; if (!drwav__is_little_endian()) { u.u32 = drwav__bswap32(u.u32); } return drwav__write(pWav, &u.u32, 4); } DRWAV_PRIVATE size_t drwav__write_or_count(drwav* pWav, const void* pData, size_t dataSize) { if (pWav == NULL) { return dataSize; } return drwav__write(pWav, pData, dataSize); } DRWAV_PRIVATE size_t drwav__write_or_count_byte(drwav* pWav, drwav_uint8 byte) { if (pWav == NULL) { return 1; } return drwav__write_byte(pWav, byte); } DRWAV_PRIVATE size_t drwav__write_or_count_u16ne_to_le(drwav* pWav, drwav_uint16 value) { if (pWav == NULL) { return 2; } return drwav__write_u16ne_to_le(pWav, value); } DRWAV_PRIVATE size_t drwav__write_or_count_u32ne_to_le(drwav* pWav, drwav_uint32 value) { if (pWav == NULL) { return 4; } return drwav__write_u32ne_to_le(pWav, value); } #if 0 DRWAV_PRIVATE size_t drwav__write_or_count_u64ne_to_le(drwav* pWav, drwav_uint64 value) { if (pWav == NULL) { return 8; } return drwav__write_u64ne_to_le(pWav, value); } #endif DRWAV_PRIVATE size_t drwav__write_or_count_f32ne_to_le(drwav* pWav, float value) { if (pWav == NULL) { return 4; } return drwav__write_f32ne_to_le(pWav, value); } DRWAV_PRIVATE size_t drwav__write_or_count_string_to_fixed_size_buf(drwav* pWav, char* str, size_t bufFixedSize) { size_t len; if (pWav == NULL) { return bufFixedSize; } len = drwav__strlen_clamped(str, bufFixedSize); drwav__write_or_count(pWav, str, len); if (len < bufFixedSize) { size_t i; for (i = 0; i < bufFixedSize - len; ++i) { drwav__write_byte(pWav, 0); } } return bufFixedSize; } DRWAV_PRIVATE size_t drwav__write_or_count_metadata(drwav* pWav, drwav_metadata* pMetadatas, drwav_uint32 metadataCount) { size_t bytesWritten = 0; drwav_bool32 hasListAdtl = DRWAV_FALSE; drwav_bool32 hasListInfo = DRWAV_FALSE; drwav_uint32 iMetadata; if (pMetadatas == NULL || metadataCount == 0) { return 0; } for (iMetadata = 0; iMetadata < metadataCount; ++iMetadata) { drwav_metadata* pMetadata = &pMetadatas[iMetadata]; drwav_uint32 chunkSize = 0; if ((pMetadata->type & drwav_metadata_type_list_all_info_strings) || (pMetadata->type == drwav_metadata_type_unknown && pMetadata->data.unknown.chunkLocation == drwav_metadata_location_inside_info_list)) { hasListInfo = DRWAV_TRUE; } if ((pMetadata->type & drwav_metadata_type_list_all_adtl) || (pMetadata->type == drwav_metadata_type_unknown && pMetadata->data.unknown.chunkLocation == drwav_metadata_location_inside_adtl_list)) { hasListAdtl = DRWAV_TRUE; } switch (pMetadata->type) { case drwav_metadata_type_smpl: { drwav_uint32 iLoop; chunkSize = DRWAV_SMPL_BYTES + DRWAV_SMPL_LOOP_BYTES * pMetadata->data.smpl.sampleLoopCount + pMetadata->data.smpl.samplerSpecificDataSizeInBytes; bytesWritten += drwav__write_or_count(pWav, "smpl", 4); bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, chunkSize); bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.manufacturerId); bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.productId); bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.samplePeriodNanoseconds); bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.midiUnityNote); bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.midiPitchFraction); bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.smpteFormat); bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.smpteOffset); bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.sampleLoopCount); bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.samplerSpecificDataSizeInBytes); for (iLoop = 0; iLoop < pMetadata->data.smpl.sampleLoopCount; ++iLoop) { bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].cuePointId); bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].type); bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].firstSampleByteOffset); bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].lastSampleByteOffset); bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].sampleFraction); bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].playCount); } if (pMetadata->data.smpl.samplerSpecificDataSizeInBytes > 0) { bytesWritten += drwav__write(pWav, pMetadata->data.smpl.pSamplerSpecificData, pMetadata->data.smpl.samplerSpecificDataSizeInBytes); } } break; case drwav_metadata_type_inst: { chunkSize = DRWAV_INST_BYTES; bytesWritten += drwav__write_or_count(pWav, "inst", 4); bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, chunkSize); bytesWritten += drwav__write_or_count(pWav, &pMetadata->data.inst.midiUnityNote, 1); bytesWritten += drwav__write_or_count(pWav, &pMetadata->data.inst.fineTuneCents, 1); bytesWritten += drwav__write_or_count(pWav, &pMetadata->data.inst.gainDecibels, 1); bytesWritten += drwav__write_or_count(pWav, &pMetadata->data.inst.lowNote, 1); bytesWritten += drwav__write_or_count(pWav, &pMetadata->data.inst.highNote, 1); bytesWritten += drwav__write_or_count(pWav, &pMetadata->data.inst.lowVelocity, 1); bytesWritten += drwav__write_or_count(pWav, &pMetadata->data.inst.highVelocity, 1); } break; case drwav_metadata_type_cue: { drwav_uint32 iCuePoint; chunkSize = DRWAV_CUE_BYTES + DRWAV_CUE_POINT_BYTES * pMetadata->data.cue.cuePointCount; bytesWritten += drwav__write_or_count(pWav, "cue ", 4); bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, chunkSize); bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.cuePointCount); for (iCuePoint = 0; iCuePoint < pMetadata->data.cue.cuePointCount; ++iCuePoint) { bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].id); bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].playOrderPosition); bytesWritten += drwav__write_or_count(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].dataChunkId, 4); bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].chunkStart); bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].blockStart); bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].sampleByteOffset); } } break; case drwav_metadata_type_acid: { chunkSize = DRWAV_ACID_BYTES; bytesWritten += drwav__write_or_count(pWav, "acid", 4); bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, chunkSize); bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.acid.flags); bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.acid.midiUnityNote); bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.acid.reserved1); bytesWritten += drwav__write_or_count_f32ne_to_le(pWav, pMetadata->data.acid.reserved2); bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.acid.numBeats); bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.acid.meterDenominator); bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.acid.meterNumerator); bytesWritten += drwav__write_or_count_f32ne_to_le(pWav, pMetadata->data.acid.tempo); } break; case drwav_metadata_type_bext: { char reservedBuf[DRWAV_BEXT_RESERVED_BYTES]; drwav_uint32 timeReferenceLow; drwav_uint32 timeReferenceHigh; chunkSize = DRWAV_BEXT_BYTES + pMetadata->data.bext.codingHistorySize; bytesWritten += drwav__write_or_count(pWav, "bext", 4); bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, chunkSize); bytesWritten += drwav__write_or_count_string_to_fixed_size_buf(pWav, pMetadata->data.bext.pDescription, DRWAV_BEXT_DESCRIPTION_BYTES); bytesWritten += drwav__write_or_count_string_to_fixed_size_buf(pWav, pMetadata->data.bext.pOriginatorName, DRWAV_BEXT_ORIGINATOR_NAME_BYTES); bytesWritten += drwav__write_or_count_string_to_fixed_size_buf(pWav, pMetadata->data.bext.pOriginatorReference, DRWAV_BEXT_ORIGINATOR_REF_BYTES); bytesWritten += drwav__write_or_count(pWav, pMetadata->data.bext.pOriginationDate, sizeof(pMetadata->data.bext.pOriginationDate)); bytesWritten += drwav__write_or_count(pWav, pMetadata->data.bext.pOriginationTime, sizeof(pMetadata->data.bext.pOriginationTime)); timeReferenceLow = (drwav_uint32)(pMetadata->data.bext.timeReference & 0xFFFFFFFF); timeReferenceHigh = (drwav_uint32)(pMetadata->data.bext.timeReference >> 32); bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, timeReferenceLow); bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, timeReferenceHigh); bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.bext.version); bytesWritten += drwav__write_or_count(pWav, pMetadata->data.bext.pUMID, DRWAV_BEXT_UMID_BYTES); bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.bext.loudnessValue); bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.bext.loudnessRange); bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.bext.maxTruePeakLevel); bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.bext.maxMomentaryLoudness); bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.bext.maxShortTermLoudness); DRWAV_ZERO_MEMORY(reservedBuf, sizeof(reservedBuf)); bytesWritten += drwav__write_or_count(pWav, reservedBuf, sizeof(reservedBuf)); if (pMetadata->data.bext.codingHistorySize > 0) { bytesWritten += drwav__write_or_count(pWav, pMetadata->data.bext.pCodingHistory, pMetadata->data.bext.codingHistorySize); } } break; case drwav_metadata_type_unknown: { if (pMetadata->data.unknown.chunkLocation == drwav_metadata_location_top_level) { chunkSize = pMetadata->data.unknown.dataSizeInBytes; bytesWritten += drwav__write_or_count(pWav, pMetadata->data.unknown.id, 4); bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, chunkSize); bytesWritten += drwav__write_or_count(pWav, pMetadata->data.unknown.pData, pMetadata->data.unknown.dataSizeInBytes); } } break; default: break; } if ((chunkSize % 2) != 0) { bytesWritten += drwav__write_or_count_byte(pWav, 0); } } if (hasListInfo) { drwav_uint32 chunkSize = 4; for (iMetadata = 0; iMetadata < metadataCount; ++iMetadata) { drwav_metadata* pMetadata = &pMetadatas[iMetadata]; if ((pMetadata->type & drwav_metadata_type_list_all_info_strings)) { chunkSize += 8; chunkSize += pMetadata->data.infoText.stringLength + 1; } else if (pMetadata->type == drwav_metadata_type_unknown && pMetadata->data.unknown.chunkLocation == drwav_metadata_location_inside_info_list) { chunkSize += 8; chunkSize += pMetadata->data.unknown.dataSizeInBytes; } if ((chunkSize % 2) != 0) { chunkSize += 1; } } bytesWritten += drwav__write_or_count(pWav, "LIST", 4); bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, chunkSize); bytesWritten += drwav__write_or_count(pWav, "INFO", 4); for (iMetadata = 0; iMetadata < metadataCount; ++iMetadata) { drwav_metadata* pMetadata = &pMetadatas[iMetadata]; drwav_uint32 subchunkSize = 0; if (pMetadata->type & drwav_metadata_type_list_all_info_strings) { const char* pID = NULL; switch (pMetadata->type) { case drwav_metadata_type_list_info_software: pID = "ISFT"; break; case drwav_metadata_type_list_info_copyright: pID = "ICOP"; break; case drwav_metadata_type_list_info_title: pID = "INAM"; break; case drwav_metadata_type_list_info_artist: pID = "IART"; break; case drwav_metadata_type_list_info_comment: pID = "ICMT"; break; case drwav_metadata_type_list_info_date: pID = "ICRD"; break; case drwav_metadata_type_list_info_genre: pID = "IGNR"; break; case drwav_metadata_type_list_info_album: pID = "IPRD"; break; case drwav_metadata_type_list_info_tracknumber: pID = "ITRK"; break; default: break; } DRWAV_ASSERT(pID != NULL); if (pMetadata->data.infoText.stringLength) { subchunkSize = pMetadata->data.infoText.stringLength + 1; bytesWritten += drwav__write_or_count(pWav, pID, 4); bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, subchunkSize); bytesWritten += drwav__write_or_count(pWav, pMetadata->data.infoText.pString, pMetadata->data.infoText.stringLength); bytesWritten += drwav__write_or_count_byte(pWav, '\0'); } } else if (pMetadata->type == drwav_metadata_type_unknown && pMetadata->data.unknown.chunkLocation == drwav_metadata_location_inside_info_list) { if (pMetadata->data.unknown.dataSizeInBytes) { subchunkSize = pMetadata->data.unknown.dataSizeInBytes; bytesWritten += drwav__write_or_count(pWav, pMetadata->data.unknown.id, 4); bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.unknown.dataSizeInBytes); bytesWritten += drwav__write_or_count(pWav, pMetadata->data.unknown.pData, subchunkSize); } } if ((subchunkSize % 2) != 0) { bytesWritten += drwav__write_or_count_byte(pWav, 0); } } } if (hasListAdtl) { drwav_uint32 chunkSize = 4; for (iMetadata = 0; iMetadata < metadataCount; ++iMetadata) { drwav_metadata* pMetadata = &pMetadatas[iMetadata]; switch (pMetadata->type) { case drwav_metadata_type_list_label: case drwav_metadata_type_list_note: { chunkSize += 8; chunkSize += DRWAV_LIST_LABEL_OR_NOTE_BYTES; if (pMetadata->data.labelOrNote.stringLength > 0) { chunkSize += pMetadata->data.labelOrNote.stringLength + 1; } } break; case drwav_metadata_type_list_labelled_cue_region: { chunkSize += 8; chunkSize += DRWAV_LIST_LABELLED_TEXT_BYTES; if (pMetadata->data.labelledCueRegion.stringLength > 0) { chunkSize += pMetadata->data.labelledCueRegion.stringLength + 1; } } break; case drwav_metadata_type_unknown: { if (pMetadata->data.unknown.chunkLocation == drwav_metadata_location_inside_adtl_list) { chunkSize += 8; chunkSize += pMetadata->data.unknown.dataSizeInBytes; } } break; default: break; } if ((chunkSize % 2) != 0) { chunkSize += 1; } } bytesWritten += drwav__write_or_count(pWav, "LIST", 4); bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, chunkSize); bytesWritten += drwav__write_or_count(pWav, "adtl", 4); for (iMetadata = 0; iMetadata < metadataCount; ++iMetadata) { drwav_metadata* pMetadata = &pMetadatas[iMetadata]; drwav_uint32 subchunkSize = 0; switch (pMetadata->type) { case drwav_metadata_type_list_label: case drwav_metadata_type_list_note: { if (pMetadata->data.labelOrNote.stringLength > 0) { const char *pID = NULL; if (pMetadata->type == drwav_metadata_type_list_label) { pID = "labl"; } else if (pMetadata->type == drwav_metadata_type_list_note) { pID = "note"; } DRWAV_ASSERT(pID != NULL); DRWAV_ASSERT(pMetadata->data.labelOrNote.pString != NULL); subchunkSize = DRWAV_LIST_LABEL_OR_NOTE_BYTES; bytesWritten += drwav__write_or_count(pWav, pID, 4); subchunkSize += pMetadata->data.labelOrNote.stringLength + 1; bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, subchunkSize); bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.labelOrNote.cuePointId); bytesWritten += drwav__write_or_count(pWav, pMetadata->data.labelOrNote.pString, pMetadata->data.labelOrNote.stringLength); bytesWritten += drwav__write_or_count_byte(pWav, '\0'); } } break; case drwav_metadata_type_list_labelled_cue_region: { subchunkSize = DRWAV_LIST_LABELLED_TEXT_BYTES; bytesWritten += drwav__write_or_count(pWav, "ltxt", 4); if (pMetadata->data.labelledCueRegion.stringLength > 0) { subchunkSize += pMetadata->data.labelledCueRegion.stringLength + 1; } bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, subchunkSize); bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.labelledCueRegion.cuePointId); bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.labelledCueRegion.sampleLength); bytesWritten += drwav__write_or_count(pWav, pMetadata->data.labelledCueRegion.purposeId, 4); bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.labelledCueRegion.country); bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.labelledCueRegion.language); bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.labelledCueRegion.dialect); bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.labelledCueRegion.codePage); if (pMetadata->data.labelledCueRegion.stringLength > 0) { DRWAV_ASSERT(pMetadata->data.labelledCueRegion.pString != NULL); bytesWritten += drwav__write_or_count(pWav, pMetadata->data.labelledCueRegion.pString, pMetadata->data.labelledCueRegion.stringLength); bytesWritten += drwav__write_or_count_byte(pWav, '\0'); } } break; case drwav_metadata_type_unknown: { if (pMetadata->data.unknown.chunkLocation == drwav_metadata_location_inside_adtl_list) { subchunkSize = pMetadata->data.unknown.dataSizeInBytes; DRWAV_ASSERT(pMetadata->data.unknown.pData != NULL); bytesWritten += drwav__write_or_count(pWav, pMetadata->data.unknown.id, 4); bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, subchunkSize); bytesWritten += drwav__write_or_count(pWav, pMetadata->data.unknown.pData, subchunkSize); } } break; default: break; } if ((subchunkSize % 2) != 0) { bytesWritten += drwav__write_or_count_byte(pWav, 0); } } } DRWAV_ASSERT((bytesWritten % 2) == 0); return bytesWritten; } DRWAV_PRIVATE drwav_uint32 drwav__riff_chunk_size_riff(drwav_uint64 dataChunkSize, drwav_metadata* pMetadata, drwav_uint32 metadataCount) { drwav_uint64 chunkSize = 4 + 24 + (drwav_uint64)drwav__write_or_count_metadata(NULL, pMetadata, metadataCount) + 8 + dataChunkSize + drwav__chunk_padding_size_riff(dataChunkSize); if (chunkSize > 0xFFFFFFFFUL) { chunkSize = 0xFFFFFFFFUL; } return (drwav_uint32)chunkSize; } DRWAV_PRIVATE drwav_uint32 drwav__data_chunk_size_riff(drwav_uint64 dataChunkSize) { if (dataChunkSize <= 0xFFFFFFFFUL) { return (drwav_uint32)dataChunkSize; } else { return 0xFFFFFFFFUL; } } DRWAV_PRIVATE drwav_uint64 drwav__riff_chunk_size_w64(drwav_uint64 dataChunkSize) { drwav_uint64 dataSubchunkPaddingSize = drwav__chunk_padding_size_w64(dataChunkSize); return 80 + 24 + dataChunkSize + dataSubchunkPaddingSize; } DRWAV_PRIVATE drwav_uint64 drwav__data_chunk_size_w64(drwav_uint64 dataChunkSize) { return 24 + dataChunkSize; } DRWAV_PRIVATE drwav_uint64 drwav__riff_chunk_size_rf64(drwav_uint64 dataChunkSize, drwav_metadata *metadata, drwav_uint32 numMetadata) { drwav_uint64 chunkSize = 4 + 36 + 24 + (drwav_uint64)drwav__write_or_count_metadata(NULL, metadata, numMetadata) + 8 + dataChunkSize + drwav__chunk_padding_size_riff(dataChunkSize); if (chunkSize > 0xFFFFFFFFUL) { chunkSize = 0xFFFFFFFFUL; } return chunkSize; } DRWAV_PRIVATE drwav_uint64 drwav__data_chunk_size_rf64(drwav_uint64 dataChunkSize) { return dataChunkSize; } DRWAV_PRIVATE drwav_bool32 drwav_preinit_write(drwav* pWav, const drwav_data_format* pFormat, drwav_bool32 isSequential, drwav_write_proc onWrite, drwav_seek_proc onSeek, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks) { if (pWav == NULL || onWrite == NULL) { return DRWAV_FALSE; } if (!isSequential && onSeek == NULL) { return DRWAV_FALSE; } if (pFormat->format == DR_WAVE_FORMAT_EXTENSIBLE) { return DRWAV_FALSE; } if (pFormat->format == DR_WAVE_FORMAT_ADPCM || pFormat->format == DR_WAVE_FORMAT_DVI_ADPCM) { return DRWAV_FALSE; } DRWAV_ZERO_MEMORY(pWav, sizeof(*pWav)); pWav->onWrite = onWrite; pWav->onSeek = onSeek; pWav->pUserData = pUserData; pWav->allocationCallbacks = drwav_copy_allocation_callbacks_or_defaults(pAllocationCallbacks); if (pWav->allocationCallbacks.onFree == NULL || (pWav->allocationCallbacks.onMalloc == NULL && pWav->allocationCallbacks.onRealloc == NULL)) { return DRWAV_FALSE; } pWav->fmt.formatTag = (drwav_uint16)pFormat->format; pWav->fmt.channels = (drwav_uint16)pFormat->channels; pWav->fmt.sampleRate = pFormat->sampleRate; pWav->fmt.avgBytesPerSec = (drwav_uint32)((pFormat->bitsPerSample * pFormat->sampleRate * pFormat->channels) / 8); pWav->fmt.blockAlign = (drwav_uint16)((pFormat->channels * pFormat->bitsPerSample) / 8); pWav->fmt.bitsPerSample = (drwav_uint16)pFormat->bitsPerSample; pWav->fmt.extendedSize = 0; pWav->isSequentialWrite = isSequential; return DRWAV_TRUE; } DRWAV_PRIVATE drwav_bool32 drwav_init_write__internal(drwav* pWav, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount) { size_t runningPos = 0; drwav_uint64 initialDataChunkSize = 0; drwav_uint64 chunkSizeFMT; if (pWav->isSequentialWrite) { initialDataChunkSize = (totalSampleCount * pWav->fmt.bitsPerSample) / 8; if (pFormat->container == drwav_container_riff) { if (initialDataChunkSize > (0xFFFFFFFFUL - 36)) { return DRWAV_FALSE; } } } pWav->dataChunkDataSizeTargetWrite = initialDataChunkSize; if (pFormat->container == drwav_container_riff) { drwav_uint32 chunkSizeRIFF = 28 + (drwav_uint32)initialDataChunkSize; runningPos += drwav__write(pWav, "RIFF", 4); runningPos += drwav__write_u32ne_to_le(pWav, chunkSizeRIFF); runningPos += drwav__write(pWav, "WAVE", 4); } else if (pFormat->container == drwav_container_w64) { drwav_uint64 chunkSizeRIFF = 80 + 24 + initialDataChunkSize; runningPos += drwav__write(pWav, drwavGUID_W64_RIFF, 16); runningPos += drwav__write_u64ne_to_le(pWav, chunkSizeRIFF); runningPos += drwav__write(pWav, drwavGUID_W64_WAVE, 16); } else if (pFormat->container == drwav_container_rf64) { runningPos += drwav__write(pWav, "RF64", 4); runningPos += drwav__write_u32ne_to_le(pWav, 0xFFFFFFFF); runningPos += drwav__write(pWav, "WAVE", 4); } if (pFormat->container == drwav_container_rf64) { drwav_uint32 initialds64ChunkSize = 28; drwav_uint64 initialRiffChunkSize = 8 + initialds64ChunkSize + initialDataChunkSize; runningPos += drwav__write(pWav, "ds64", 4); runningPos += drwav__write_u32ne_to_le(pWav, initialds64ChunkSize); runningPos += drwav__write_u64ne_to_le(pWav, initialRiffChunkSize); runningPos += drwav__write_u64ne_to_le(pWav, initialDataChunkSize); runningPos += drwav__write_u64ne_to_le(pWav, totalSampleCount); runningPos += drwav__write_u32ne_to_le(pWav, 0); } if (pFormat->container == drwav_container_riff || pFormat->container == drwav_container_rf64) { chunkSizeFMT = 16; runningPos += drwav__write(pWav, "fmt ", 4); runningPos += drwav__write_u32ne_to_le(pWav, (drwav_uint32)chunkSizeFMT); } else if (pFormat->container == drwav_container_w64) { chunkSizeFMT = 40; runningPos += drwav__write(pWav, drwavGUID_W64_FMT, 16); runningPos += drwav__write_u64ne_to_le(pWav, chunkSizeFMT); } runningPos += drwav__write_u16ne_to_le(pWav, pWav->fmt.formatTag); runningPos += drwav__write_u16ne_to_le(pWav, pWav->fmt.channels); runningPos += drwav__write_u32ne_to_le(pWav, pWav->fmt.sampleRate); runningPos += drwav__write_u32ne_to_le(pWav, pWav->fmt.avgBytesPerSec); runningPos += drwav__write_u16ne_to_le(pWav, pWav->fmt.blockAlign); runningPos += drwav__write_u16ne_to_le(pWav, pWav->fmt.bitsPerSample); if (!pWav->isSequentialWrite && pWav->pMetadata != NULL && pWav->metadataCount > 0 && (pFormat->container == drwav_container_riff || pFormat->container == drwav_container_rf64)) { runningPos += drwav__write_or_count_metadata(pWav, pWav->pMetadata, pWav->metadataCount); } pWav->dataChunkDataPos = runningPos; if (pFormat->container == drwav_container_riff) { drwav_uint32 chunkSizeDATA = (drwav_uint32)initialDataChunkSize; runningPos += drwav__write(pWav, "data", 4); runningPos += drwav__write_u32ne_to_le(pWav, chunkSizeDATA); } else if (pFormat->container == drwav_container_w64) { drwav_uint64 chunkSizeDATA = 24 + initialDataChunkSize; runningPos += drwav__write(pWav, drwavGUID_W64_DATA, 16); runningPos += drwav__write_u64ne_to_le(pWav, chunkSizeDATA); } else if (pFormat->container == drwav_container_rf64) { runningPos += drwav__write(pWav, "data", 4); runningPos += drwav__write_u32ne_to_le(pWav, 0xFFFFFFFF); } pWav->container = pFormat->container; pWav->channels = (drwav_uint16)pFormat->channels; pWav->sampleRate = pFormat->sampleRate; pWav->bitsPerSample = (drwav_uint16)pFormat->bitsPerSample; pWav->translatedFormatTag = (drwav_uint16)pFormat->format; pWav->dataChunkDataPos = runningPos; return DRWAV_TRUE; } DRWAV_API drwav_bool32 drwav_init_write(drwav* pWav, const drwav_data_format* pFormat, drwav_write_proc onWrite, drwav_seek_proc onSeek, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks) { if (!drwav_preinit_write(pWav, pFormat, DRWAV_FALSE, onWrite, onSeek, pUserData, pAllocationCallbacks)) { return DRWAV_FALSE; } return drwav_init_write__internal(pWav, pFormat, 0); } DRWAV_API drwav_bool32 drwav_init_write_sequential(drwav* pWav, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, drwav_write_proc onWrite, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks) { if (!drwav_preinit_write(pWav, pFormat, DRWAV_TRUE, onWrite, NULL, pUserData, pAllocationCallbacks)) { return DRWAV_FALSE; } return drwav_init_write__internal(pWav, pFormat, totalSampleCount); } DRWAV_API drwav_bool32 drwav_init_write_sequential_pcm_frames(drwav* pWav, const drwav_data_format* pFormat, drwav_uint64 totalPCMFrameCount, drwav_write_proc onWrite, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks) { if (pFormat == NULL) { return DRWAV_FALSE; } return drwav_init_write_sequential(pWav, pFormat, totalPCMFrameCount*pFormat->channels, onWrite, pUserData, pAllocationCallbacks); } DRWAV_API drwav_bool32 drwav_init_write_with_metadata(drwav* pWav, const drwav_data_format* pFormat, drwav_write_proc onWrite, drwav_seek_proc onSeek, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks, drwav_metadata* pMetadata, drwav_uint32 metadataCount) { if (!drwav_preinit_write(pWav, pFormat, DRWAV_FALSE, onWrite, onSeek, pUserData, pAllocationCallbacks)) { return DRWAV_FALSE; } pWav->pMetadata = pMetadata; pWav->metadataCount = metadataCount; return drwav_init_write__internal(pWav, pFormat, 0); } DRWAV_API drwav_uint64 drwav_target_write_size_bytes(const drwav_data_format* pFormat, drwav_uint64 totalFrameCount, drwav_metadata* pMetadata, drwav_uint32 metadataCount) { drwav_uint64 targetDataSizeBytes = (drwav_uint64)((drwav_int64)totalFrameCount * pFormat->channels * pFormat->bitsPerSample/8.0); drwav_uint64 riffChunkSizeBytes; drwav_uint64 fileSizeBytes = 0; if (pFormat->container == drwav_container_riff) { riffChunkSizeBytes = drwav__riff_chunk_size_riff(targetDataSizeBytes, pMetadata, metadataCount); fileSizeBytes = (8 + riffChunkSizeBytes); } else if (pFormat->container == drwav_container_w64) { riffChunkSizeBytes = drwav__riff_chunk_size_w64(targetDataSizeBytes); fileSizeBytes = riffChunkSizeBytes; } else if (pFormat->container == drwav_container_rf64) { riffChunkSizeBytes = drwav__riff_chunk_size_rf64(targetDataSizeBytes, pMetadata, metadataCount); fileSizeBytes = (8 + riffChunkSizeBytes); } return fileSizeBytes; } #ifndef DR_WAV_NO_STDIO #include DRWAV_PRIVATE drwav_result drwav_result_from_errno(int e) { switch (e) { case 0: return DRWAV_SUCCESS; #ifdef EPERM case EPERM: return DRWAV_INVALID_OPERATION; #endif #ifdef ENOENT case ENOENT: return DRWAV_DOES_NOT_EXIST; #endif #ifdef ESRCH case ESRCH: return DRWAV_DOES_NOT_EXIST; #endif #ifdef EINTR case EINTR: return DRWAV_INTERRUPT; #endif #ifdef EIO case EIO: return DRWAV_IO_ERROR; #endif #ifdef ENXIO case ENXIO: return DRWAV_DOES_NOT_EXIST; #endif #ifdef E2BIG case E2BIG: return DRWAV_INVALID_ARGS; #endif #ifdef ENOEXEC case ENOEXEC: return DRWAV_INVALID_FILE; #endif #ifdef EBADF case EBADF: return DRWAV_INVALID_FILE; #endif #ifdef ECHILD case ECHILD: return DRWAV_ERROR; #endif #ifdef EAGAIN case EAGAIN: return DRWAV_UNAVAILABLE; #endif #ifdef ENOMEM case ENOMEM: return DRWAV_OUT_OF_MEMORY; #endif #ifdef EACCES case EACCES: return DRWAV_ACCESS_DENIED; #endif #ifdef EFAULT case EFAULT: return DRWAV_BAD_ADDRESS; #endif #ifdef ENOTBLK case ENOTBLK: return DRWAV_ERROR; #endif #ifdef EBUSY case EBUSY: return DRWAV_BUSY; #endif #ifdef EEXIST case EEXIST: return DRWAV_ALREADY_EXISTS; #endif #ifdef EXDEV case EXDEV: return DRWAV_ERROR; #endif #ifdef ENODEV case ENODEV: return DRWAV_DOES_NOT_EXIST; #endif #ifdef ENOTDIR case ENOTDIR: return DRWAV_NOT_DIRECTORY; #endif #ifdef EISDIR case EISDIR: return DRWAV_IS_DIRECTORY; #endif #ifdef EINVAL case EINVAL: return DRWAV_INVALID_ARGS; #endif #ifdef ENFILE case ENFILE: return DRWAV_TOO_MANY_OPEN_FILES; #endif #ifdef EMFILE case EMFILE: return DRWAV_TOO_MANY_OPEN_FILES; #endif #ifdef ENOTTY case ENOTTY: return DRWAV_INVALID_OPERATION; #endif #ifdef ETXTBSY case ETXTBSY: return DRWAV_BUSY; #endif #ifdef EFBIG case EFBIG: return DRWAV_TOO_BIG; #endif #ifdef ENOSPC case ENOSPC: return DRWAV_NO_SPACE; #endif #ifdef ESPIPE case ESPIPE: return DRWAV_BAD_SEEK; #endif #ifdef EROFS case EROFS: return DRWAV_ACCESS_DENIED; #endif #ifdef EMLINK case EMLINK: return DRWAV_TOO_MANY_LINKS; #endif #ifdef EPIPE case EPIPE: return DRWAV_BAD_PIPE; #endif #ifdef EDOM case EDOM: return DRWAV_OUT_OF_RANGE; #endif #ifdef ERANGE case ERANGE: return DRWAV_OUT_OF_RANGE; #endif #ifdef EDEADLK case EDEADLK: return DRWAV_DEADLOCK; #endif #ifdef ENAMETOOLONG case ENAMETOOLONG: return DRWAV_PATH_TOO_LONG; #endif #ifdef ENOLCK case ENOLCK: return DRWAV_ERROR; #endif #ifdef ENOSYS case ENOSYS: return DRWAV_NOT_IMPLEMENTED; #endif #ifdef ENOTEMPTY case ENOTEMPTY: return DRWAV_DIRECTORY_NOT_EMPTY; #endif #ifdef ELOOP case ELOOP: return DRWAV_TOO_MANY_LINKS; #endif #ifdef ENOMSG case ENOMSG: return DRWAV_NO_MESSAGE; #endif #ifdef EIDRM case EIDRM: return DRWAV_ERROR; #endif #ifdef ECHRNG case ECHRNG: return DRWAV_ERROR; #endif #ifdef EL2NSYNC case EL2NSYNC: return DRWAV_ERROR; #endif #ifdef EL3HLT case EL3HLT: return DRWAV_ERROR; #endif #ifdef EL3RST case EL3RST: return DRWAV_ERROR; #endif #ifdef ELNRNG case ELNRNG: return DRWAV_OUT_OF_RANGE; #endif #ifdef EUNATCH case EUNATCH: return DRWAV_ERROR; #endif #ifdef ENOCSI case ENOCSI: return DRWAV_ERROR; #endif #ifdef EL2HLT case EL2HLT: return DRWAV_ERROR; #endif #ifdef EBADE case EBADE: return DRWAV_ERROR; #endif #ifdef EBADR case EBADR: return DRWAV_ERROR; #endif #ifdef EXFULL case EXFULL: return DRWAV_ERROR; #endif #ifdef ENOANO case ENOANO: return DRWAV_ERROR; #endif #ifdef EBADRQC case EBADRQC: return DRWAV_ERROR; #endif #ifdef EBADSLT case EBADSLT: return DRWAV_ERROR; #endif #ifdef EBFONT case EBFONT: return DRWAV_INVALID_FILE; #endif #ifdef ENOSTR case ENOSTR: return DRWAV_ERROR; #endif #ifdef ENODATA case ENODATA: return DRWAV_NO_DATA_AVAILABLE; #endif #ifdef ETIME case ETIME: return DRWAV_TIMEOUT; #endif #ifdef ENOSR case ENOSR: return DRWAV_NO_DATA_AVAILABLE; #endif #ifdef ENONET case ENONET: return DRWAV_NO_NETWORK; #endif #ifdef ENOPKG case ENOPKG: return DRWAV_ERROR; #endif #ifdef EREMOTE case EREMOTE: return DRWAV_ERROR; #endif #ifdef ENOLINK case ENOLINK: return DRWAV_ERROR; #endif #ifdef EADV case EADV: return DRWAV_ERROR; #endif #ifdef ESRMNT case ESRMNT: return DRWAV_ERROR; #endif #ifdef ECOMM case ECOMM: return DRWAV_ERROR; #endif #ifdef EPROTO case EPROTO: return DRWAV_ERROR; #endif #ifdef EMULTIHOP case EMULTIHOP: return DRWAV_ERROR; #endif #ifdef EDOTDOT case EDOTDOT: return DRWAV_ERROR; #endif #ifdef EBADMSG case EBADMSG: return DRWAV_BAD_MESSAGE; #endif #ifdef EOVERFLOW case EOVERFLOW: return DRWAV_TOO_BIG; #endif #ifdef ENOTUNIQ case ENOTUNIQ: return DRWAV_NOT_UNIQUE; #endif #ifdef EBADFD case EBADFD: return DRWAV_ERROR; #endif #ifdef EREMCHG case EREMCHG: return DRWAV_ERROR; #endif #ifdef ELIBACC case ELIBACC: return DRWAV_ACCESS_DENIED; #endif #ifdef ELIBBAD case ELIBBAD: return DRWAV_INVALID_FILE; #endif #ifdef ELIBSCN case ELIBSCN: return DRWAV_INVALID_FILE; #endif #ifdef ELIBMAX case ELIBMAX: return DRWAV_ERROR; #endif #ifdef ELIBEXEC case ELIBEXEC: return DRWAV_ERROR; #endif #ifdef EILSEQ case EILSEQ: return DRWAV_INVALID_DATA; #endif #ifdef ERESTART case ERESTART: return DRWAV_ERROR; #endif #ifdef ESTRPIPE case ESTRPIPE: return DRWAV_ERROR; #endif #ifdef EUSERS case EUSERS: return DRWAV_ERROR; #endif #ifdef ENOTSOCK case ENOTSOCK: return DRWAV_NOT_SOCKET; #endif #ifdef EDESTADDRREQ case EDESTADDRREQ: return DRWAV_NO_ADDRESS; #endif #ifdef EMSGSIZE case EMSGSIZE: return DRWAV_TOO_BIG; #endif #ifdef EPROTOTYPE case EPROTOTYPE: return DRWAV_BAD_PROTOCOL; #endif #ifdef ENOPROTOOPT case ENOPROTOOPT: return DRWAV_PROTOCOL_UNAVAILABLE; #endif #ifdef EPROTONOSUPPORT case EPROTONOSUPPORT: return DRWAV_PROTOCOL_NOT_SUPPORTED; #endif #ifdef ESOCKTNOSUPPORT case ESOCKTNOSUPPORT: return DRWAV_SOCKET_NOT_SUPPORTED; #endif #ifdef EOPNOTSUPP case EOPNOTSUPP: return DRWAV_INVALID_OPERATION; #endif #ifdef EPFNOSUPPORT case EPFNOSUPPORT: return DRWAV_PROTOCOL_FAMILY_NOT_SUPPORTED; #endif #ifdef EAFNOSUPPORT case EAFNOSUPPORT: return DRWAV_ADDRESS_FAMILY_NOT_SUPPORTED; #endif #ifdef EADDRINUSE case EADDRINUSE: return DRWAV_ALREADY_IN_USE; #endif #ifdef EADDRNOTAVAIL case EADDRNOTAVAIL: return DRWAV_ERROR; #endif #ifdef ENETDOWN case ENETDOWN: return DRWAV_NO_NETWORK; #endif #ifdef ENETUNREACH case ENETUNREACH: return DRWAV_NO_NETWORK; #endif #ifdef ENETRESET case ENETRESET: return DRWAV_NO_NETWORK; #endif #ifdef ECONNABORTED case ECONNABORTED: return DRWAV_NO_NETWORK; #endif #ifdef ECONNRESET case ECONNRESET: return DRWAV_CONNECTION_RESET; #endif #ifdef ENOBUFS case ENOBUFS: return DRWAV_NO_SPACE; #endif #ifdef EISCONN case EISCONN: return DRWAV_ALREADY_CONNECTED; #endif #ifdef ENOTCONN case ENOTCONN: return DRWAV_NOT_CONNECTED; #endif #ifdef ESHUTDOWN case ESHUTDOWN: return DRWAV_ERROR; #endif #ifdef ETOOMANYREFS case ETOOMANYREFS: return DRWAV_ERROR; #endif #ifdef ETIMEDOUT case ETIMEDOUT: return DRWAV_TIMEOUT; #endif #ifdef ECONNREFUSED case ECONNREFUSED: return DRWAV_CONNECTION_REFUSED; #endif #ifdef EHOSTDOWN case EHOSTDOWN: return DRWAV_NO_HOST; #endif #ifdef EHOSTUNREACH case EHOSTUNREACH: return DRWAV_NO_HOST; #endif #ifdef EALREADY case EALREADY: return DRWAV_IN_PROGRESS; #endif #ifdef EINPROGRESS case EINPROGRESS: return DRWAV_IN_PROGRESS; #endif #ifdef ESTALE case ESTALE: return DRWAV_INVALID_FILE; #endif #ifdef EUCLEAN case EUCLEAN: return DRWAV_ERROR; #endif #ifdef ENOTNAM case ENOTNAM: return DRWAV_ERROR; #endif #ifdef ENAVAIL case ENAVAIL: return DRWAV_ERROR; #endif #ifdef EISNAM case EISNAM: return DRWAV_ERROR; #endif #ifdef EREMOTEIO case EREMOTEIO: return DRWAV_IO_ERROR; #endif #ifdef EDQUOT case EDQUOT: return DRWAV_NO_SPACE; #endif #ifdef ENOMEDIUM case ENOMEDIUM: return DRWAV_DOES_NOT_EXIST; #endif #ifdef EMEDIUMTYPE case EMEDIUMTYPE: return DRWAV_ERROR; #endif #ifdef ECANCELED case ECANCELED: return DRWAV_CANCELLED; #endif #ifdef ENOKEY case ENOKEY: return DRWAV_ERROR; #endif #ifdef EKEYEXPIRED case EKEYEXPIRED: return DRWAV_ERROR; #endif #ifdef EKEYREVOKED case EKEYREVOKED: return DRWAV_ERROR; #endif #ifdef EKEYREJECTED case EKEYREJECTED: return DRWAV_ERROR; #endif #ifdef EOWNERDEAD case EOWNERDEAD: return DRWAV_ERROR; #endif #ifdef ENOTRECOVERABLE case ENOTRECOVERABLE: return DRWAV_ERROR; #endif #ifdef ERFKILL case ERFKILL: return DRWAV_ERROR; #endif #ifdef EHWPOISON case EHWPOISON: return DRWAV_ERROR; #endif default: return DRWAV_ERROR; } } DRWAV_PRIVATE drwav_result drwav_fopen(FILE** ppFile, const char* pFilePath, const char* pOpenMode) { #if defined(_MSC_VER) && _MSC_VER >= 1400 errno_t err; #endif if (ppFile != NULL) { *ppFile = NULL; } if (pFilePath == NULL || pOpenMode == NULL || ppFile == NULL) { return DRWAV_INVALID_ARGS; } #if defined(_MSC_VER) && _MSC_VER >= 1400 err = fopen_s(ppFile, pFilePath, pOpenMode); if (err != 0) { return drwav_result_from_errno(err); } #else #if defined(_WIN32) || defined(__APPLE__) *ppFile = fopen(pFilePath, pOpenMode); #else #if defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS == 64 && defined(_LARGEFILE64_SOURCE) *ppFile = fopen64(pFilePath, pOpenMode); #else *ppFile = fopen(pFilePath, pOpenMode); #endif #endif if (*ppFile == NULL) { drwav_result result = drwav_result_from_errno(errno); if (result == DRWAV_SUCCESS) { result = DRWAV_ERROR; } return result; } #endif return DRWAV_SUCCESS; } #if defined(_WIN32) #if defined(_MSC_VER) || defined(__MINGW64__) || (!defined(__STRICT_ANSI__) && !defined(_NO_EXT_KEYS)) #define DRWAV_HAS_WFOPEN #endif #endif #ifndef DR_WAV_NO_WCHAR DRWAV_PRIVATE drwav_result drwav_wfopen(FILE** ppFile, const wchar_t* pFilePath, const wchar_t* pOpenMode, const drwav_allocation_callbacks* pAllocationCallbacks) { if (ppFile != NULL) { *ppFile = NULL; } if (pFilePath == NULL || pOpenMode == NULL || ppFile == NULL) { return DRWAV_INVALID_ARGS; } #if defined(DRWAV_HAS_WFOPEN) { #if defined(_MSC_VER) && _MSC_VER >= 1400 errno_t err = _wfopen_s(ppFile, pFilePath, pOpenMode); if (err != 0) { return drwav_result_from_errno(err); } #else *ppFile = _wfopen(pFilePath, pOpenMode); if (*ppFile == NULL) { return drwav_result_from_errno(errno); } #endif (void)pAllocationCallbacks; } #else #if defined(__DJGPP__) { } #else { mbstate_t mbs; size_t lenMB; const wchar_t* pFilePathTemp = pFilePath; char* pFilePathMB = NULL; char pOpenModeMB[32] = {0}; DRWAV_ZERO_OBJECT(&mbs); lenMB = wcsrtombs(NULL, &pFilePathTemp, 0, &mbs); if (lenMB == (size_t)-1) { return drwav_result_from_errno(errno); } pFilePathMB = (char*)drwav__malloc_from_callbacks(lenMB + 1, pAllocationCallbacks); if (pFilePathMB == NULL) { return DRWAV_OUT_OF_MEMORY; } pFilePathTemp = pFilePath; DRWAV_ZERO_OBJECT(&mbs); wcsrtombs(pFilePathMB, &pFilePathTemp, lenMB + 1, &mbs); { size_t i = 0; for (;;) { if (pOpenMode[i] == 0) { pOpenModeMB[i] = '\0'; break; } pOpenModeMB[i] = (char)pOpenMode[i]; i += 1; } } *ppFile = fopen(pFilePathMB, pOpenModeMB); drwav__free_from_callbacks(pFilePathMB, pAllocationCallbacks); } #endif if (*ppFile == NULL) { return DRWAV_ERROR; } #endif return DRWAV_SUCCESS; } #endif DRWAV_PRIVATE size_t drwav__on_read_stdio(void* pUserData, void* pBufferOut, size_t bytesToRead) { return fread(pBufferOut, 1, bytesToRead, (FILE*)pUserData); } DRWAV_PRIVATE size_t drwav__on_write_stdio(void* pUserData, const void* pData, size_t bytesToWrite) { return fwrite(pData, 1, bytesToWrite, (FILE*)pUserData); } DRWAV_PRIVATE drwav_bool32 drwav__on_seek_stdio(void* pUserData, int offset, drwav_seek_origin origin) { return fseek((FILE*)pUserData, offset, (origin == drwav_seek_origin_current) ? SEEK_CUR : SEEK_SET) == 0; } DRWAV_API drwav_bool32 drwav_init_file(drwav* pWav, const char* filename, const drwav_allocation_callbacks* pAllocationCallbacks) { return drwav_init_file_ex(pWav, filename, NULL, NULL, 0, pAllocationCallbacks); } DRWAV_PRIVATE drwav_bool32 drwav_init_file__internal_FILE(drwav* pWav, FILE* pFile, drwav_chunk_proc onChunk, void* pChunkUserData, drwav_uint32 flags, drwav_metadata_type allowedMetadataTypes, const drwav_allocation_callbacks* pAllocationCallbacks) { drwav_bool32 result; result = drwav_preinit(pWav, drwav__on_read_stdio, drwav__on_seek_stdio, (void*)pFile, pAllocationCallbacks); if (result != DRWAV_TRUE) { fclose(pFile); return result; } pWav->allowedMetadataTypes = allowedMetadataTypes; result = drwav_init__internal(pWav, onChunk, pChunkUserData, flags); if (result != DRWAV_TRUE) { fclose(pFile); return result; } return DRWAV_TRUE; } DRWAV_API drwav_bool32 drwav_init_file_ex(drwav* pWav, const char* filename, drwav_chunk_proc onChunk, void* pChunkUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks) { FILE* pFile; if (drwav_fopen(&pFile, filename, "rb") != DRWAV_SUCCESS) { return DRWAV_FALSE; } return drwav_init_file__internal_FILE(pWav, pFile, onChunk, pChunkUserData, flags, drwav_metadata_type_none, pAllocationCallbacks); } #ifndef DR_WAV_NO_WCHAR DRWAV_API drwav_bool32 drwav_init_file_w(drwav* pWav, const wchar_t* filename, const drwav_allocation_callbacks* pAllocationCallbacks) { return drwav_init_file_ex_w(pWav, filename, NULL, NULL, 0, pAllocationCallbacks); } DRWAV_API drwav_bool32 drwav_init_file_ex_w(drwav* pWav, const wchar_t* filename, drwav_chunk_proc onChunk, void* pChunkUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks) { FILE* pFile; if (drwav_wfopen(&pFile, filename, L"rb", pAllocationCallbacks) != DRWAV_SUCCESS) { return DRWAV_FALSE; } return drwav_init_file__internal_FILE(pWav, pFile, onChunk, pChunkUserData, flags, drwav_metadata_type_none, pAllocationCallbacks); } #endif DRWAV_API drwav_bool32 drwav_init_file_with_metadata(drwav* pWav, const char* filename, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks) { FILE* pFile; if (drwav_fopen(&pFile, filename, "rb") != DRWAV_SUCCESS) { return DRWAV_FALSE; } return drwav_init_file__internal_FILE(pWav, pFile, NULL, NULL, flags, drwav_metadata_type_all_including_unknown, pAllocationCallbacks); } #ifndef DR_WAV_NO_WCHAR DRWAV_API drwav_bool32 drwav_init_file_with_metadata_w(drwav* pWav, const wchar_t* filename, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks) { FILE* pFile; if (drwav_wfopen(&pFile, filename, L"rb", pAllocationCallbacks) != DRWAV_SUCCESS) { return DRWAV_FALSE; } return drwav_init_file__internal_FILE(pWav, pFile, NULL, NULL, flags, drwav_metadata_type_all_including_unknown, pAllocationCallbacks); } #endif DRWAV_PRIVATE drwav_bool32 drwav_init_file_write__internal_FILE(drwav* pWav, FILE* pFile, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, drwav_bool32 isSequential, const drwav_allocation_callbacks* pAllocationCallbacks) { drwav_bool32 result; result = drwav_preinit_write(pWav, pFormat, isSequential, drwav__on_write_stdio, drwav__on_seek_stdio, (void*)pFile, pAllocationCallbacks); if (result != DRWAV_TRUE) { fclose(pFile); return result; } result = drwav_init_write__internal(pWav, pFormat, totalSampleCount); if (result != DRWAV_TRUE) { fclose(pFile); return result; } return DRWAV_TRUE; } DRWAV_PRIVATE drwav_bool32 drwav_init_file_write__internal(drwav* pWav, const char* filename, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, drwav_bool32 isSequential, const drwav_allocation_callbacks* pAllocationCallbacks) { FILE* pFile; if (drwav_fopen(&pFile, filename, "wb") != DRWAV_SUCCESS) { return DRWAV_FALSE; } return drwav_init_file_write__internal_FILE(pWav, pFile, pFormat, totalSampleCount, isSequential, pAllocationCallbacks); } #ifndef DR_WAV_NO_WCHAR DRWAV_PRIVATE drwav_bool32 drwav_init_file_write_w__internal(drwav* pWav, const wchar_t* filename, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, drwav_bool32 isSequential, const drwav_allocation_callbacks* pAllocationCallbacks) { FILE* pFile; if (drwav_wfopen(&pFile, filename, L"wb", pAllocationCallbacks) != DRWAV_SUCCESS) { return DRWAV_FALSE; } return drwav_init_file_write__internal_FILE(pWav, pFile, pFormat, totalSampleCount, isSequential, pAllocationCallbacks); } #endif DRWAV_API drwav_bool32 drwav_init_file_write(drwav* pWav, const char* filename, const drwav_data_format* pFormat, const drwav_allocation_callbacks* pAllocationCallbacks) { return drwav_init_file_write__internal(pWav, filename, pFormat, 0, DRWAV_FALSE, pAllocationCallbacks); } DRWAV_API drwav_bool32 drwav_init_file_write_sequential(drwav* pWav, const char* filename, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, const drwav_allocation_callbacks* pAllocationCallbacks) { return drwav_init_file_write__internal(pWav, filename, pFormat, totalSampleCount, DRWAV_TRUE, pAllocationCallbacks); } DRWAV_API drwav_bool32 drwav_init_file_write_sequential_pcm_frames(drwav* pWav, const char* filename, const drwav_data_format* pFormat, drwav_uint64 totalPCMFrameCount, const drwav_allocation_callbacks* pAllocationCallbacks) { if (pFormat == NULL) { return DRWAV_FALSE; } return drwav_init_file_write_sequential(pWav, filename, pFormat, totalPCMFrameCount*pFormat->channels, pAllocationCallbacks); } #ifndef DR_WAV_NO_WCHAR DRWAV_API drwav_bool32 drwav_init_file_write_w(drwav* pWav, const wchar_t* filename, const drwav_data_format* pFormat, const drwav_allocation_callbacks* pAllocationCallbacks) { return drwav_init_file_write_w__internal(pWav, filename, pFormat, 0, DRWAV_FALSE, pAllocationCallbacks); } DRWAV_API drwav_bool32 drwav_init_file_write_sequential_w(drwav* pWav, const wchar_t* filename, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, const drwav_allocation_callbacks* pAllocationCallbacks) { return drwav_init_file_write_w__internal(pWav, filename, pFormat, totalSampleCount, DRWAV_TRUE, pAllocationCallbacks); } DRWAV_API drwav_bool32 drwav_init_file_write_sequential_pcm_frames_w(drwav* pWav, const wchar_t* filename, const drwav_data_format* pFormat, drwav_uint64 totalPCMFrameCount, const drwav_allocation_callbacks* pAllocationCallbacks) { if (pFormat == NULL) { return DRWAV_FALSE; } return drwav_init_file_write_sequential_w(pWav, filename, pFormat, totalPCMFrameCount*pFormat->channels, pAllocationCallbacks); } #endif #endif DRWAV_PRIVATE size_t drwav__on_read_memory(void* pUserData, void* pBufferOut, size_t bytesToRead) { drwav* pWav = (drwav*)pUserData; size_t bytesRemaining; DRWAV_ASSERT(pWav != NULL); DRWAV_ASSERT(pWav->memoryStream.dataSize >= pWav->memoryStream.currentReadPos); bytesRemaining = pWav->memoryStream.dataSize - pWav->memoryStream.currentReadPos; if (bytesToRead > bytesRemaining) { bytesToRead = bytesRemaining; } if (bytesToRead > 0) { DRWAV_COPY_MEMORY(pBufferOut, pWav->memoryStream.data + pWav->memoryStream.currentReadPos, bytesToRead); pWav->memoryStream.currentReadPos += bytesToRead; } return bytesToRead; } DRWAV_PRIVATE drwav_bool32 drwav__on_seek_memory(void* pUserData, int offset, drwav_seek_origin origin) { drwav* pWav = (drwav*)pUserData; DRWAV_ASSERT(pWav != NULL); if (origin == drwav_seek_origin_current) { if (offset > 0) { if (pWav->memoryStream.currentReadPos + offset > pWav->memoryStream.dataSize) { return DRWAV_FALSE; } } else { if (pWav->memoryStream.currentReadPos < (size_t)-offset) { return DRWAV_FALSE; } } pWav->memoryStream.currentReadPos += offset; } else { if ((drwav_uint32)offset <= pWav->memoryStream.dataSize) { pWav->memoryStream.currentReadPos = offset; } else { return DRWAV_FALSE; } } return DRWAV_TRUE; } DRWAV_PRIVATE size_t drwav__on_write_memory(void* pUserData, const void* pDataIn, size_t bytesToWrite) { drwav* pWav = (drwav*)pUserData; size_t bytesRemaining; DRWAV_ASSERT(pWav != NULL); DRWAV_ASSERT(pWav->memoryStreamWrite.dataCapacity >= pWav->memoryStreamWrite.currentWritePos); bytesRemaining = pWav->memoryStreamWrite.dataCapacity - pWav->memoryStreamWrite.currentWritePos; if (bytesRemaining < bytesToWrite) { void* pNewData; size_t newDataCapacity = (pWav->memoryStreamWrite.dataCapacity == 0) ? 256 : pWav->memoryStreamWrite.dataCapacity * 2; if ((newDataCapacity - pWav->memoryStreamWrite.currentWritePos) < bytesToWrite) { newDataCapacity = pWav->memoryStreamWrite.currentWritePos + bytesToWrite; } pNewData = drwav__realloc_from_callbacks(*pWav->memoryStreamWrite.ppData, newDataCapacity, pWav->memoryStreamWrite.dataCapacity, &pWav->allocationCallbacks); if (pNewData == NULL) { return 0; } *pWav->memoryStreamWrite.ppData = pNewData; pWav->memoryStreamWrite.dataCapacity = newDataCapacity; } DRWAV_COPY_MEMORY(((drwav_uint8*)(*pWav->memoryStreamWrite.ppData)) + pWav->memoryStreamWrite.currentWritePos, pDataIn, bytesToWrite); pWav->memoryStreamWrite.currentWritePos += bytesToWrite; if (pWav->memoryStreamWrite.dataSize < pWav->memoryStreamWrite.currentWritePos) { pWav->memoryStreamWrite.dataSize = pWav->memoryStreamWrite.currentWritePos; } *pWav->memoryStreamWrite.pDataSize = pWav->memoryStreamWrite.dataSize; return bytesToWrite; } DRWAV_PRIVATE drwav_bool32 drwav__on_seek_memory_write(void* pUserData, int offset, drwav_seek_origin origin) { drwav* pWav = (drwav*)pUserData; DRWAV_ASSERT(pWav != NULL); if (origin == drwav_seek_origin_current) { if (offset > 0) { if (pWav->memoryStreamWrite.currentWritePos + offset > pWav->memoryStreamWrite.dataSize) { offset = (int)(pWav->memoryStreamWrite.dataSize - pWav->memoryStreamWrite.currentWritePos); } } else { if (pWav->memoryStreamWrite.currentWritePos < (size_t)-offset) { offset = -(int)pWav->memoryStreamWrite.currentWritePos; } } pWav->memoryStreamWrite.currentWritePos += offset; } else { if ((drwav_uint32)offset <= pWav->memoryStreamWrite.dataSize) { pWav->memoryStreamWrite.currentWritePos = offset; } else { pWav->memoryStreamWrite.currentWritePos = pWav->memoryStreamWrite.dataSize; } } return DRWAV_TRUE; } DRWAV_API drwav_bool32 drwav_init_memory(drwav* pWav, const void* data, size_t dataSize, const drwav_allocation_callbacks* pAllocationCallbacks) { return drwav_init_memory_ex(pWav, data, dataSize, NULL, NULL, 0, pAllocationCallbacks); } DRWAV_API drwav_bool32 drwav_init_memory_ex(drwav* pWav, const void* data, size_t dataSize, drwav_chunk_proc onChunk, void* pChunkUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks) { if (data == NULL || dataSize == 0) { return DRWAV_FALSE; } if (!drwav_preinit(pWav, drwav__on_read_memory, drwav__on_seek_memory, pWav, pAllocationCallbacks)) { return DRWAV_FALSE; } pWav->memoryStream.data = (const drwav_uint8*)data; pWav->memoryStream.dataSize = dataSize; pWav->memoryStream.currentReadPos = 0; return drwav_init__internal(pWav, onChunk, pChunkUserData, flags); } DRWAV_API drwav_bool32 drwav_init_memory_with_metadata(drwav* pWav, const void* data, size_t dataSize, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks) { if (data == NULL || dataSize == 0) { return DRWAV_FALSE; } if (!drwav_preinit(pWav, drwav__on_read_memory, drwav__on_seek_memory, pWav, pAllocationCallbacks)) { return DRWAV_FALSE; } pWav->memoryStream.data = (const drwav_uint8*)data; pWav->memoryStream.dataSize = dataSize; pWav->memoryStream.currentReadPos = 0; pWav->allowedMetadataTypes = drwav_metadata_type_all_including_unknown; return drwav_init__internal(pWav, NULL, NULL, flags); } DRWAV_PRIVATE drwav_bool32 drwav_init_memory_write__internal(drwav* pWav, void** ppData, size_t* pDataSize, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, drwav_bool32 isSequential, const drwav_allocation_callbacks* pAllocationCallbacks) { if (ppData == NULL || pDataSize == NULL) { return DRWAV_FALSE; } *ppData = NULL; *pDataSize = 0; if (!drwav_preinit_write(pWav, pFormat, isSequential, drwav__on_write_memory, drwav__on_seek_memory_write, pWav, pAllocationCallbacks)) { return DRWAV_FALSE; } pWav->memoryStreamWrite.ppData = ppData; pWav->memoryStreamWrite.pDataSize = pDataSize; pWav->memoryStreamWrite.dataSize = 0; pWav->memoryStreamWrite.dataCapacity = 0; pWav->memoryStreamWrite.currentWritePos = 0; return drwav_init_write__internal(pWav, pFormat, totalSampleCount); } DRWAV_API drwav_bool32 drwav_init_memory_write(drwav* pWav, void** ppData, size_t* pDataSize, const drwav_data_format* pFormat, const drwav_allocation_callbacks* pAllocationCallbacks) { return drwav_init_memory_write__internal(pWav, ppData, pDataSize, pFormat, 0, DRWAV_FALSE, pAllocationCallbacks); } DRWAV_API drwav_bool32 drwav_init_memory_write_sequential(drwav* pWav, void** ppData, size_t* pDataSize, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, const drwav_allocation_callbacks* pAllocationCallbacks) { return drwav_init_memory_write__internal(pWav, ppData, pDataSize, pFormat, totalSampleCount, DRWAV_TRUE, pAllocationCallbacks); } DRWAV_API drwav_bool32 drwav_init_memory_write_sequential_pcm_frames(drwav* pWav, void** ppData, size_t* pDataSize, const drwav_data_format* pFormat, drwav_uint64 totalPCMFrameCount, const drwav_allocation_callbacks* pAllocationCallbacks) { if (pFormat == NULL) { return DRWAV_FALSE; } return drwav_init_memory_write_sequential(pWav, ppData, pDataSize, pFormat, totalPCMFrameCount*pFormat->channels, pAllocationCallbacks); } DRWAV_API drwav_result drwav_uninit(drwav* pWav) { drwav_result result = DRWAV_SUCCESS; if (pWav == NULL) { return DRWAV_INVALID_ARGS; } if (pWav->onWrite != NULL) { drwav_uint32 paddingSize = 0; if (pWav->container == drwav_container_riff || pWav->container == drwav_container_rf64) { paddingSize = drwav__chunk_padding_size_riff(pWav->dataChunkDataSize); } else { paddingSize = drwav__chunk_padding_size_w64(pWav->dataChunkDataSize); } if (paddingSize > 0) { drwav_uint64 paddingData = 0; drwav__write(pWav, &paddingData, paddingSize); } if (pWav->onSeek && !pWav->isSequentialWrite) { if (pWav->container == drwav_container_riff) { if (pWav->onSeek(pWav->pUserData, 4, drwav_seek_origin_start)) { drwav_uint32 riffChunkSize = drwav__riff_chunk_size_riff(pWav->dataChunkDataSize, pWav->pMetadata, pWav->metadataCount); drwav__write_u32ne_to_le(pWav, riffChunkSize); } if (pWav->onSeek(pWav->pUserData, (int)pWav->dataChunkDataPos - 4, drwav_seek_origin_start)) { drwav_uint32 dataChunkSize = drwav__data_chunk_size_riff(pWav->dataChunkDataSize); drwav__write_u32ne_to_le(pWav, dataChunkSize); } } else if (pWav->container == drwav_container_w64) { if (pWav->onSeek(pWav->pUserData, 16, drwav_seek_origin_start)) { drwav_uint64 riffChunkSize = drwav__riff_chunk_size_w64(pWav->dataChunkDataSize); drwav__write_u64ne_to_le(pWav, riffChunkSize); } if (pWav->onSeek(pWav->pUserData, (int)pWav->dataChunkDataPos - 8, drwav_seek_origin_start)) { drwav_uint64 dataChunkSize = drwav__data_chunk_size_w64(pWav->dataChunkDataSize); drwav__write_u64ne_to_le(pWav, dataChunkSize); } } else if (pWav->container == drwav_container_rf64) { int ds64BodyPos = 12 + 8; if (pWav->onSeek(pWav->pUserData, ds64BodyPos + 0, drwav_seek_origin_start)) { drwav_uint64 riffChunkSize = drwav__riff_chunk_size_rf64(pWav->dataChunkDataSize, pWav->pMetadata, pWav->metadataCount); drwav__write_u64ne_to_le(pWav, riffChunkSize); } if (pWav->onSeek(pWav->pUserData, ds64BodyPos + 8, drwav_seek_origin_start)) { drwav_uint64 dataChunkSize = drwav__data_chunk_size_rf64(pWav->dataChunkDataSize); drwav__write_u64ne_to_le(pWav, dataChunkSize); } } } if (pWav->isSequentialWrite) { if (pWav->dataChunkDataSize != pWav->dataChunkDataSizeTargetWrite) { result = DRWAV_INVALID_FILE; } } } else { if (pWav->pMetadata != NULL) { pWav->allocationCallbacks.onFree(pWav->pMetadata, pWav->allocationCallbacks.pUserData); } } #ifndef DR_WAV_NO_STDIO if (pWav->onRead == drwav__on_read_stdio || pWav->onWrite == drwav__on_write_stdio) { fclose((FILE*)pWav->pUserData); } #endif return result; } DRWAV_API size_t drwav_read_raw(drwav* pWav, size_t bytesToRead, void* pBufferOut) { size_t bytesRead; drwav_uint32 bytesPerFrame; if (pWav == NULL || bytesToRead == 0) { return 0; } if (bytesToRead > pWav->bytesRemaining) { bytesToRead = (size_t)pWav->bytesRemaining; } if (bytesToRead == 0) { return 0; } bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav); if (bytesPerFrame == 0) { return 0; } if (pBufferOut != NULL) { bytesRead = pWav->onRead(pWav->pUserData, pBufferOut, bytesToRead); } else { bytesRead = 0; while (bytesRead < bytesToRead) { size_t bytesToSeek = (bytesToRead - bytesRead); if (bytesToSeek > 0x7FFFFFFF) { bytesToSeek = 0x7FFFFFFF; } if (pWav->onSeek(pWav->pUserData, (int)bytesToSeek, drwav_seek_origin_current) == DRWAV_FALSE) { break; } bytesRead += bytesToSeek; } while (bytesRead < bytesToRead) { drwav_uint8 buffer[4096]; size_t bytesSeeked; size_t bytesToSeek = (bytesToRead - bytesRead); if (bytesToSeek > sizeof(buffer)) { bytesToSeek = sizeof(buffer); } bytesSeeked = pWav->onRead(pWav->pUserData, buffer, bytesToSeek); bytesRead += bytesSeeked; if (bytesSeeked < bytesToSeek) { break; } } } pWav->readCursorInPCMFrames += bytesRead / bytesPerFrame; pWav->bytesRemaining -= bytesRead; return bytesRead; } DRWAV_API drwav_uint64 drwav_read_pcm_frames_le(drwav* pWav, drwav_uint64 framesToRead, void* pBufferOut) { drwav_uint32 bytesPerFrame; drwav_uint64 bytesToRead; if (pWav == NULL || framesToRead == 0) { return 0; } if (drwav__is_compressed_format_tag(pWav->translatedFormatTag)) { return 0; } bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav); if (bytesPerFrame == 0) { return 0; } bytesToRead = framesToRead * bytesPerFrame; if (bytesToRead > DRWAV_SIZE_MAX) { bytesToRead = (DRWAV_SIZE_MAX / bytesPerFrame) * bytesPerFrame; } if (bytesToRead == 0) { return 0; } return drwav_read_raw(pWav, (size_t)bytesToRead, pBufferOut) / bytesPerFrame; } DRWAV_API drwav_uint64 drwav_read_pcm_frames_be(drwav* pWav, drwav_uint64 framesToRead, void* pBufferOut) { drwav_uint64 framesRead = drwav_read_pcm_frames_le(pWav, framesToRead, pBufferOut); if (pBufferOut != NULL) { drwav_uint32 bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav); if (bytesPerFrame == 0) { return 0; } drwav__bswap_samples(pBufferOut, framesRead*pWav->channels, bytesPerFrame/pWav->channels, pWav->translatedFormatTag); } return framesRead; } DRWAV_API drwav_uint64 drwav_read_pcm_frames(drwav* pWav, drwav_uint64 framesToRead, void* pBufferOut) { if (drwav__is_little_endian()) { return drwav_read_pcm_frames_le(pWav, framesToRead, pBufferOut); } else { return drwav_read_pcm_frames_be(pWav, framesToRead, pBufferOut); } } DRWAV_PRIVATE drwav_bool32 drwav_seek_to_first_pcm_frame(drwav* pWav) { if (pWav->onWrite != NULL) { return DRWAV_FALSE; } if (!pWav->onSeek(pWav->pUserData, (int)pWav->dataChunkDataPos, drwav_seek_origin_start)) { return DRWAV_FALSE; } if (drwav__is_compressed_format_tag(pWav->translatedFormatTag)) { if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM) { DRWAV_ZERO_OBJECT(&pWav->msadpcm); } else if (pWav->translatedFormatTag == DR_WAVE_FORMAT_DVI_ADPCM) { DRWAV_ZERO_OBJECT(&pWav->ima); } else { DRWAV_ASSERT(DRWAV_FALSE); } } pWav->readCursorInPCMFrames = 0; pWav->bytesRemaining = pWav->dataChunkDataSize; return DRWAV_TRUE; } DRWAV_API drwav_bool32 drwav_seek_to_pcm_frame(drwav* pWav, drwav_uint64 targetFrameIndex) { if (pWav == NULL || pWav->onSeek == NULL) { return DRWAV_FALSE; } if (pWav->onWrite != NULL) { return DRWAV_FALSE; } if (pWav->totalPCMFrameCount == 0) { return DRWAV_TRUE; } if (targetFrameIndex > pWav->totalPCMFrameCount) { targetFrameIndex = pWav->totalPCMFrameCount; } if (drwav__is_compressed_format_tag(pWav->translatedFormatTag)) { if (targetFrameIndex < pWav->readCursorInPCMFrames) { if (!drwav_seek_to_first_pcm_frame(pWav)) { return DRWAV_FALSE; } } if (targetFrameIndex > pWav->readCursorInPCMFrames) { drwav_uint64 offsetInFrames = targetFrameIndex - pWav->readCursorInPCMFrames; drwav_int16 devnull[2048]; while (offsetInFrames > 0) { drwav_uint64 framesRead = 0; drwav_uint64 framesToRead = offsetInFrames; if (framesToRead > drwav_countof(devnull)/pWav->channels) { framesToRead = drwav_countof(devnull)/pWav->channels; } if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM) { framesRead = drwav_read_pcm_frames_s16__msadpcm(pWav, framesToRead, devnull); } else if (pWav->translatedFormatTag == DR_WAVE_FORMAT_DVI_ADPCM) { framesRead = drwav_read_pcm_frames_s16__ima(pWav, framesToRead, devnull); } else { DRWAV_ASSERT(DRWAV_FALSE); } if (framesRead != framesToRead) { return DRWAV_FALSE; } offsetInFrames -= framesRead; } } } else { drwav_uint64 totalSizeInBytes; drwav_uint64 currentBytePos; drwav_uint64 targetBytePos; drwav_uint64 offset; drwav_uint32 bytesPerFrame; bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav); if (bytesPerFrame == 0) { return DRWAV_FALSE; } totalSizeInBytes = pWav->totalPCMFrameCount * bytesPerFrame; DRWAV_ASSERT(totalSizeInBytes >= pWav->bytesRemaining); currentBytePos = totalSizeInBytes - pWav->bytesRemaining; targetBytePos = targetFrameIndex * bytesPerFrame; if (currentBytePos < targetBytePos) { offset = (targetBytePos - currentBytePos); } else { if (!drwav_seek_to_first_pcm_frame(pWav)) { return DRWAV_FALSE; } offset = targetBytePos; } while (offset > 0) { int offset32 = ((offset > INT_MAX) ? INT_MAX : (int)offset); if (!pWav->onSeek(pWav->pUserData, offset32, drwav_seek_origin_current)) { return DRWAV_FALSE; } pWav->readCursorInPCMFrames += offset32 / bytesPerFrame; pWav->bytesRemaining -= offset32; offset -= offset32; } } return DRWAV_TRUE; } DRWAV_API drwav_result drwav_get_cursor_in_pcm_frames(drwav* pWav, drwav_uint64* pCursor) { if (pCursor == NULL) { return DRWAV_INVALID_ARGS; } *pCursor = 0; if (pWav == NULL) { return DRWAV_INVALID_ARGS; } *pCursor = pWav->readCursorInPCMFrames; return DRWAV_SUCCESS; } DRWAV_API drwav_result drwav_get_length_in_pcm_frames(drwav* pWav, drwav_uint64* pLength) { if (pLength == NULL) { return DRWAV_INVALID_ARGS; } *pLength = 0; if (pWav == NULL) { return DRWAV_INVALID_ARGS; } *pLength = pWav->totalPCMFrameCount; return DRWAV_SUCCESS; } DRWAV_API size_t drwav_write_raw(drwav* pWav, size_t bytesToWrite, const void* pData) { size_t bytesWritten; if (pWav == NULL || bytesToWrite == 0 || pData == NULL) { return 0; } bytesWritten = pWav->onWrite(pWav->pUserData, pData, bytesToWrite); pWav->dataChunkDataSize += bytesWritten; return bytesWritten; } DRWAV_API drwav_uint64 drwav_write_pcm_frames_le(drwav* pWav, drwav_uint64 framesToWrite, const void* pData) { drwav_uint64 bytesToWrite; drwav_uint64 bytesWritten; const drwav_uint8* pRunningData; if (pWav == NULL || framesToWrite == 0 || pData == NULL) { return 0; } bytesToWrite = ((framesToWrite * pWav->channels * pWav->bitsPerSample) / 8); if (bytesToWrite > DRWAV_SIZE_MAX) { return 0; } bytesWritten = 0; pRunningData = (const drwav_uint8*)pData; while (bytesToWrite > 0) { size_t bytesJustWritten; drwav_uint64 bytesToWriteThisIteration; bytesToWriteThisIteration = bytesToWrite; DRWAV_ASSERT(bytesToWriteThisIteration <= DRWAV_SIZE_MAX); bytesJustWritten = drwav_write_raw(pWav, (size_t)bytesToWriteThisIteration, pRunningData); if (bytesJustWritten == 0) { break; } bytesToWrite -= bytesJustWritten; bytesWritten += bytesJustWritten; pRunningData += bytesJustWritten; } return (bytesWritten * 8) / pWav->bitsPerSample / pWav->channels; } DRWAV_API drwav_uint64 drwav_write_pcm_frames_be(drwav* pWav, drwav_uint64 framesToWrite, const void* pData) { drwav_uint64 bytesToWrite; drwav_uint64 bytesWritten; drwav_uint32 bytesPerSample; const drwav_uint8* pRunningData; if (pWav == NULL || framesToWrite == 0 || pData == NULL) { return 0; } bytesToWrite = ((framesToWrite * pWav->channels * pWav->bitsPerSample) / 8); if (bytesToWrite > DRWAV_SIZE_MAX) { return 0; } bytesWritten = 0; pRunningData = (const drwav_uint8*)pData; bytesPerSample = drwav_get_bytes_per_pcm_frame(pWav) / pWav->channels; if (bytesPerSample == 0) { return 0; } while (bytesToWrite > 0) { drwav_uint8 temp[4096]; drwav_uint32 sampleCount; size_t bytesJustWritten; drwav_uint64 bytesToWriteThisIteration; bytesToWriteThisIteration = bytesToWrite; DRWAV_ASSERT(bytesToWriteThisIteration <= DRWAV_SIZE_MAX); sampleCount = sizeof(temp)/bytesPerSample; if (bytesToWriteThisIteration > ((drwav_uint64)sampleCount)*bytesPerSample) { bytesToWriteThisIteration = ((drwav_uint64)sampleCount)*bytesPerSample; } DRWAV_COPY_MEMORY(temp, pRunningData, (size_t)bytesToWriteThisIteration); drwav__bswap_samples(temp, sampleCount, bytesPerSample, pWav->translatedFormatTag); bytesJustWritten = drwav_write_raw(pWav, (size_t)bytesToWriteThisIteration, temp); if (bytesJustWritten == 0) { break; } bytesToWrite -= bytesJustWritten; bytesWritten += bytesJustWritten; pRunningData += bytesJustWritten; } return (bytesWritten * 8) / pWav->bitsPerSample / pWav->channels; } DRWAV_API drwav_uint64 drwav_write_pcm_frames(drwav* pWav, drwav_uint64 framesToWrite, const void* pData) { if (drwav__is_little_endian()) { return drwav_write_pcm_frames_le(pWav, framesToWrite, pData); } else { return drwav_write_pcm_frames_be(pWav, framesToWrite, pData); } } DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__msadpcm(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut) { drwav_uint64 totalFramesRead = 0; DRWAV_ASSERT(pWav != NULL); DRWAV_ASSERT(framesToRead > 0); while (pWav->readCursorInPCMFrames < pWav->totalPCMFrameCount) { DRWAV_ASSERT(framesToRead > 0); if (pWav->msadpcm.cachedFrameCount == 0 && pWav->msadpcm.bytesRemainingInBlock == 0) { if (pWav->channels == 1) { drwav_uint8 header[7]; if (pWav->onRead(pWav->pUserData, header, sizeof(header)) != sizeof(header)) { return totalFramesRead; } pWav->msadpcm.bytesRemainingInBlock = pWav->fmt.blockAlign - sizeof(header); pWav->msadpcm.predictor[0] = header[0]; pWav->msadpcm.delta[0] = drwav_bytes_to_s16(header + 1); pWav->msadpcm.prevFrames[0][1] = (drwav_int32)drwav_bytes_to_s16(header + 3); pWav->msadpcm.prevFrames[0][0] = (drwav_int32)drwav_bytes_to_s16(header + 5); pWav->msadpcm.cachedFrames[2] = pWav->msadpcm.prevFrames[0][0]; pWav->msadpcm.cachedFrames[3] = pWav->msadpcm.prevFrames[0][1]; pWav->msadpcm.cachedFrameCount = 2; } else { drwav_uint8 header[14]; if (pWav->onRead(pWav->pUserData, header, sizeof(header)) != sizeof(header)) { return totalFramesRead; } pWav->msadpcm.bytesRemainingInBlock = pWav->fmt.blockAlign - sizeof(header); pWav->msadpcm.predictor[0] = header[0]; pWav->msadpcm.predictor[1] = header[1]; pWav->msadpcm.delta[0] = drwav_bytes_to_s16(header + 2); pWav->msadpcm.delta[1] = drwav_bytes_to_s16(header + 4); pWav->msadpcm.prevFrames[0][1] = (drwav_int32)drwav_bytes_to_s16(header + 6); pWav->msadpcm.prevFrames[1][1] = (drwav_int32)drwav_bytes_to_s16(header + 8); pWav->msadpcm.prevFrames[0][0] = (drwav_int32)drwav_bytes_to_s16(header + 10); pWav->msadpcm.prevFrames[1][0] = (drwav_int32)drwav_bytes_to_s16(header + 12); pWav->msadpcm.cachedFrames[0] = pWav->msadpcm.prevFrames[0][0]; pWav->msadpcm.cachedFrames[1] = pWav->msadpcm.prevFrames[1][0]; pWav->msadpcm.cachedFrames[2] = pWav->msadpcm.prevFrames[0][1]; pWav->msadpcm.cachedFrames[3] = pWav->msadpcm.prevFrames[1][1]; pWav->msadpcm.cachedFrameCount = 2; } } while (framesToRead > 0 && pWav->msadpcm.cachedFrameCount > 0 && pWav->readCursorInPCMFrames < pWav->totalPCMFrameCount) { if (pBufferOut != NULL) { drwav_uint32 iSample = 0; for (iSample = 0; iSample < pWav->channels; iSample += 1) { pBufferOut[iSample] = (drwav_int16)pWav->msadpcm.cachedFrames[(drwav_countof(pWav->msadpcm.cachedFrames) - (pWav->msadpcm.cachedFrameCount*pWav->channels)) + iSample]; } pBufferOut += pWav->channels; } framesToRead -= 1; totalFramesRead += 1; pWav->readCursorInPCMFrames += 1; pWav->msadpcm.cachedFrameCount -= 1; } if (framesToRead == 0) { break; } if (pWav->msadpcm.cachedFrameCount == 0) { if (pWav->msadpcm.bytesRemainingInBlock == 0) { continue; } else { static 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 }; drwav_uint8 nibbles; drwav_int32 nibble0; drwav_int32 nibble1; if (pWav->onRead(pWav->pUserData, &nibbles, 1) != 1) { return totalFramesRead; } pWav->msadpcm.bytesRemainingInBlock -= 1; nibble0 = ((nibbles & 0xF0) >> 4); if ((nibbles & 0x80)) { nibble0 |= 0xFFFFFFF0UL; } nibble1 = ((nibbles & 0x0F) >> 0); if ((nibbles & 0x08)) { nibble1 |= 0xFFFFFFF0UL; } if (pWav->channels == 1) { drwav_int32 newSample0; drwav_int32 newSample1; 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.prevFrames[0][0] = pWav->msadpcm.prevFrames[0][1]; pWav->msadpcm.prevFrames[0][1] = newSample0; newSample1 = ((pWav->msadpcm.prevFrames[0][1] * coeff1Table[pWav->msadpcm.predictor[0]]) + (pWav->msadpcm.prevFrames[0][0] * coeff2Table[pWav->msadpcm.predictor[0]])) >> 8; 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.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; } else { drwav_int32 newSample0; drwav_int32 newSample1; 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.prevFrames[0][0] = pWav->msadpcm.prevFrames[0][1]; pWav->msadpcm.prevFrames[0][1] = newSample0; 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.prevFrames[1][0] = pWav->msadpcm.prevFrames[1][1]; pWav->msadpcm.prevFrames[1][1] = newSample1; pWav->msadpcm.cachedFrames[2] = newSample0; pWav->msadpcm.cachedFrames[3] = newSample1; pWav->msadpcm.cachedFrameCount = 1; } } } } return totalFramesRead; } DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__ima(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut) { drwav_uint64 totalFramesRead = 0; drwav_uint32 iChannel; static 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] = { 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, 130, 143, 157, 173, 190, 209, 230, 253, 279, 307, 337, 371, 408, 449, 494, 544, 598, 658, 724, 796, 876, 963, 1060, 1166, 1282, 1411, 1552, 1707, 1878, 2066, 2272, 2499, 2749, 3024, 3327, 3660, 4026, 4428, 4871, 5358, 5894, 6484, 7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899, 15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, 32767 }; DRWAV_ASSERT(pWav != NULL); DRWAV_ASSERT(framesToRead > 0); while (pWav->readCursorInPCMFrames < pWav->totalPCMFrameCount) { DRWAV_ASSERT(framesToRead > 0); if (pWav->ima.cachedFrameCount == 0 && pWav->ima.bytesRemainingInBlock == 0) { if (pWav->channels == 1) { drwav_uint8 header[4]; if (pWav->onRead(pWav->pUserData, header, sizeof(header)) != sizeof(header)) { return totalFramesRead; } pWav->ima.bytesRemainingInBlock = pWav->fmt.blockAlign - sizeof(header); if (header[2] >= drwav_countof(stepTable)) { pWav->onSeek(pWav->pUserData, pWav->ima.bytesRemainingInBlock, drwav_seek_origin_current); pWav->ima.bytesRemainingInBlock = 0; return totalFramesRead; } pWav->ima.predictor[0] = drwav_bytes_to_s16(header + 0); pWav->ima.stepIndex[0] = drwav_clamp(header[2], 0, (drwav_int32)drwav_countof(stepTable)-1); pWav->ima.cachedFrames[drwav_countof(pWav->ima.cachedFrames) - 1] = pWav->ima.predictor[0]; pWav->ima.cachedFrameCount = 1; } else { drwav_uint8 header[8]; if (pWav->onRead(pWav->pUserData, header, sizeof(header)) != sizeof(header)) { return totalFramesRead; } pWav->ima.bytesRemainingInBlock = pWav->fmt.blockAlign - sizeof(header); if (header[2] >= drwav_countof(stepTable) || header[6] >= drwav_countof(stepTable)) { pWav->onSeek(pWav->pUserData, pWav->ima.bytesRemainingInBlock, drwav_seek_origin_current); pWav->ima.bytesRemainingInBlock = 0; return totalFramesRead; } pWav->ima.predictor[0] = drwav_bytes_to_s16(header + 0); pWav->ima.stepIndex[0] = drwav_clamp(header[2], 0, (drwav_int32)drwav_countof(stepTable)-1); pWav->ima.predictor[1] = drwav_bytes_to_s16(header + 4); pWav->ima.stepIndex[1] = drwav_clamp(header[6], 0, (drwav_int32)drwav_countof(stepTable)-1); pWav->ima.cachedFrames[drwav_countof(pWav->ima.cachedFrames) - 2] = pWav->ima.predictor[0]; pWav->ima.cachedFrames[drwav_countof(pWav->ima.cachedFrames) - 1] = pWav->ima.predictor[1]; pWav->ima.cachedFrameCount = 1; } } while (framesToRead > 0 && pWav->ima.cachedFrameCount > 0 && pWav->readCursorInPCMFrames < pWav->totalPCMFrameCount) { if (pBufferOut != NULL) { drwav_uint32 iSample; for (iSample = 0; iSample < pWav->channels; iSample += 1) { pBufferOut[iSample] = (drwav_int16)pWav->ima.cachedFrames[(drwav_countof(pWav->ima.cachedFrames) - (pWav->ima.cachedFrameCount*pWav->channels)) + iSample]; } pBufferOut += pWav->channels; } framesToRead -= 1; totalFramesRead += 1; pWav->readCursorInPCMFrames += 1; pWav->ima.cachedFrameCount -= 1; } if (framesToRead == 0) { break; } if (pWav->ima.cachedFrameCount == 0) { if (pWav->ima.bytesRemainingInBlock == 0) { continue; } else { pWav->ima.cachedFrameCount = 8; for (iChannel = 0; iChannel < pWav->channels; ++iChannel) { drwav_uint32 iByte; drwav_uint8 nibbles[4]; if (pWav->onRead(pWav->pUserData, &nibbles, 4) != 4) { pWav->ima.cachedFrameCount = 0; return totalFramesRead; } pWav->ima.bytesRemainingInBlock -= 4; for (iByte = 0; iByte < 4; ++iByte) { drwav_uint8 nibble0 = ((nibbles[iByte] & 0x0F) >> 0); drwav_uint8 nibble1 = ((nibbles[iByte] & 0xF0) >> 4); drwav_int32 step = stepTable[pWav->ima.stepIndex[iChannel]]; drwav_int32 predictor = pWav->ima.predictor[iChannel]; drwav_int32 diff = step >> 3; if (nibble0 & 1) diff += step >> 2; if (nibble0 & 2) diff += step >> 1; if (nibble0 & 4) diff += step; if (nibble0 & 8) diff = -diff; predictor = drwav_clamp(predictor + diff, -32768, 32767); pWav->ima.predictor[iChannel] = predictor; pWav->ima.stepIndex[iChannel] = drwav_clamp(pWav->ima.stepIndex[iChannel] + indexTable[nibble0], 0, (drwav_int32)drwav_countof(stepTable)-1); pWav->ima.cachedFrames[(drwav_countof(pWav->ima.cachedFrames) - (pWav->ima.cachedFrameCount*pWav->channels)) + (iByte*2+0)*pWav->channels + iChannel] = predictor; step = stepTable[pWav->ima.stepIndex[iChannel]]; predictor = pWav->ima.predictor[iChannel]; diff = step >> 3; if (nibble1 & 1) diff += step >> 2; if (nibble1 & 2) diff += step >> 1; if (nibble1 & 4) diff += step; if (nibble1 & 8) diff = -diff; predictor = drwav_clamp(predictor + diff, -32768, 32767); pWav->ima.predictor[iChannel] = predictor; pWav->ima.stepIndex[iChannel] = drwav_clamp(pWav->ima.stepIndex[iChannel] + indexTable[nibble1], 0, (drwav_int32)drwav_countof(stepTable)-1); pWav->ima.cachedFrames[(drwav_countof(pWav->ima.cachedFrames) - (pWav->ima.cachedFrameCount*pWav->channels)) + (iByte*2+1)*pWav->channels + iChannel] = predictor; } } } } } return totalFramesRead; } #ifndef DR_WAV_NO_CONVERSION_API static 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, 0xD500, 0xD700, 0xD100, 0xD300, 0xDD00, 0xDF00, 0xD900, 0xDB00, 0xC500, 0xC700, 0xC100, 0xC300, 0xCD00, 0xCF00, 0xC900, 0xCB00, 0xFEA8, 0xFEB8, 0xFE88, 0xFE98, 0xFEE8, 0xFEF8, 0xFEC8, 0xFED8, 0xFE28, 0xFE38, 0xFE08, 0xFE18, 0xFE68, 0xFE78, 0xFE48, 0xFE58, 0xFFA8, 0xFFB8, 0xFF88, 0xFF98, 0xFFE8, 0xFFF8, 0xFFC8, 0xFFD8, 0xFF28, 0xFF38, 0xFF08, 0xFF18, 0xFF68, 0xFF78, 0xFF48, 0xFF58, 0xFAA0, 0xFAE0, 0xFA20, 0xFA60, 0xFBA0, 0xFBE0, 0xFB20, 0xFB60, 0xF8A0, 0xF8E0, 0xF820, 0xF860, 0xF9A0, 0xF9E0, 0xF920, 0xF960, 0xFD50, 0xFD70, 0xFD10, 0xFD30, 0xFDD0, 0xFDF0, 0xFD90, 0xFDB0, 0xFC50, 0xFC70, 0xFC10, 0xFC30, 0xFCD0, 0xFCF0, 0xFC90, 0xFCB0, 0x1580, 0x1480, 0x1780, 0x1680, 0x1180, 0x1080, 0x1380, 0x1280, 0x1D80, 0x1C80, 0x1F80, 0x1E80, 0x1980, 0x1880, 0x1B80, 0x1A80, 0x0AC0, 0x0A40, 0x0BC0, 0x0B40, 0x08C0, 0x0840, 0x09C0, 0x0940, 0x0EC0, 0x0E40, 0x0FC0, 0x0F40, 0x0CC0, 0x0C40, 0x0DC0, 0x0D40, 0x5600, 0x5200, 0x5E00, 0x5A00, 0x4600, 0x4200, 0x4E00, 0x4A00, 0x7600, 0x7200, 0x7E00, 0x7A00, 0x6600, 0x6200, 0x6E00, 0x6A00, 0x2B00, 0x2900, 0x2F00, 0x2D00, 0x2300, 0x2100, 0x2700, 0x2500, 0x3B00, 0x3900, 0x3F00, 0x3D00, 0x3300, 0x3100, 0x3700, 0x3500, 0x0158, 0x0148, 0x0178, 0x0168, 0x0118, 0x0108, 0x0138, 0x0128, 0x01D8, 0x01C8, 0x01F8, 0x01E8, 0x0198, 0x0188, 0x01B8, 0x01A8, 0x0058, 0x0048, 0x0078, 0x0068, 0x0018, 0x0008, 0x0038, 0x0028, 0x00D8, 0x00C8, 0x00F8, 0x00E8, 0x0098, 0x0088, 0x00B8, 0x00A8, 0x0560, 0x0520, 0x05E0, 0x05A0, 0x0460, 0x0420, 0x04E0, 0x04A0, 0x0760, 0x0720, 0x07E0, 0x07A0, 0x0660, 0x0620, 0x06E0, 0x06A0, 0x02B0, 0x0290, 0x02F0, 0x02D0, 0x0230, 0x0210, 0x0270, 0x0250, 0x03B0, 0x0390, 0x03F0, 0x03D0, 0x0330, 0x0310, 0x0370, 0x0350 }; static 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, 0xF0C4, 0xF144, 0xF1C4, 0xF244, 0xF2C4, 0xF344, 0xF3C4, 0xF444, 0xF4C4, 0xF544, 0xF5C4, 0xF644, 0xF6C4, 0xF744, 0xF7C4, 0xF844, 0xF8A4, 0xF8E4, 0xF924, 0xF964, 0xF9A4, 0xF9E4, 0xFA24, 0xFA64, 0xFAA4, 0xFAE4, 0xFB24, 0xFB64, 0xFBA4, 0xFBE4, 0xFC24, 0xFC64, 0xFC94, 0xFCB4, 0xFCD4, 0xFCF4, 0xFD14, 0xFD34, 0xFD54, 0xFD74, 0xFD94, 0xFDB4, 0xFDD4, 0xFDF4, 0xFE14, 0xFE34, 0xFE54, 0xFE74, 0xFE8C, 0xFE9C, 0xFEAC, 0xFEBC, 0xFECC, 0xFEDC, 0xFEEC, 0xFEFC, 0xFF0C, 0xFF1C, 0xFF2C, 0xFF3C, 0xFF4C, 0xFF5C, 0xFF6C, 0xFF7C, 0xFF88, 0xFF90, 0xFF98, 0xFFA0, 0xFFA8, 0xFFB0, 0xFFB8, 0xFFC0, 0xFFC8, 0xFFD0, 0xFFD8, 0xFFE0, 0xFFE8, 0xFFF0, 0xFFF8, 0x0000, 0x7D7C, 0x797C, 0x757C, 0x717C, 0x6D7C, 0x697C, 0x657C, 0x617C, 0x5D7C, 0x597C, 0x557C, 0x517C, 0x4D7C, 0x497C, 0x457C, 0x417C, 0x3E7C, 0x3C7C, 0x3A7C, 0x387C, 0x367C, 0x347C, 0x327C, 0x307C, 0x2E7C, 0x2C7C, 0x2A7C, 0x287C, 0x267C, 0x247C, 0x227C, 0x207C, 0x1EFC, 0x1DFC, 0x1CFC, 0x1BFC, 0x1AFC, 0x19FC, 0x18FC, 0x17FC, 0x16FC, 0x15FC, 0x14FC, 0x13FC, 0x12FC, 0x11FC, 0x10FC, 0x0FFC, 0x0F3C, 0x0EBC, 0x0E3C, 0x0DBC, 0x0D3C, 0x0CBC, 0x0C3C, 0x0BBC, 0x0B3C, 0x0ABC, 0x0A3C, 0x09BC, 0x093C, 0x08BC, 0x083C, 0x07BC, 0x075C, 0x071C, 0x06DC, 0x069C, 0x065C, 0x061C, 0x05DC, 0x059C, 0x055C, 0x051C, 0x04DC, 0x049C, 0x045C, 0x041C, 0x03DC, 0x039C, 0x036C, 0x034C, 0x032C, 0x030C, 0x02EC, 0x02CC, 0x02AC, 0x028C, 0x026C, 0x024C, 0x022C, 0x020C, 0x01EC, 0x01CC, 0x01AC, 0x018C, 0x0174, 0x0164, 0x0154, 0x0144, 0x0134, 0x0124, 0x0114, 0x0104, 0x00F4, 0x00E4, 0x00D4, 0x00C4, 0x00B4, 0x00A4, 0x0094, 0x0084, 0x0078, 0x0070, 0x0068, 0x0060, 0x0058, 0x0050, 0x0048, 0x0040, 0x0038, 0x0030, 0x0028, 0x0020, 0x0018, 0x0010, 0x0008, 0x0000 }; static DRWAV_INLINE drwav_int16 drwav__alaw_to_s16(drwav_uint8 sampleIn) { return (short)g_drwavAlawTable[sampleIn]; } static DRWAV_INLINE drwav_int16 drwav__mulaw_to_s16(drwav_uint8 sampleIn) { return (short)g_drwavMulawTable[sampleIn]; } DRWAV_PRIVATE void drwav__pcm_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t totalSampleCount, unsigned int bytesPerSample) { size_t i; if (bytesPerSample == 1) { drwav_u8_to_s16(pOut, pIn, totalSampleCount); return; } if (bytesPerSample == 2) { for (i = 0; i < totalSampleCount; ++i) { *pOut++ = ((const drwav_int16*)pIn)[i]; } return; } if (bytesPerSample == 3) { drwav_s24_to_s16(pOut, pIn, totalSampleCount); return; } if (bytesPerSample == 4) { drwav_s32_to_s16(pOut, (const drwav_int32*)pIn, totalSampleCount); return; } if (bytesPerSample > 8) { DRWAV_ZERO_MEMORY(pOut, totalSampleCount * sizeof(*pOut)); return; } for (i = 0; i < totalSampleCount; ++i) { drwav_uint64 sample = 0; unsigned int shift = (8 - bytesPerSample) * 8; unsigned int j; for (j = 0; j < bytesPerSample; j += 1) { DRWAV_ASSERT(j < 8); sample |= (drwav_uint64)(pIn[j]) << shift; shift += 8; } pIn += j; *pOut++ = (drwav_int16)((drwav_int64)sample >> 48); } } DRWAV_PRIVATE void drwav__ieee_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t totalSampleCount, unsigned int bytesPerSample) { if (bytesPerSample == 4) { drwav_f32_to_s16(pOut, (const float*)pIn, totalSampleCount); return; } else if (bytesPerSample == 8) { drwav_f64_to_s16(pOut, (const double*)pIn, totalSampleCount); return; } else { DRWAV_ZERO_MEMORY(pOut, totalSampleCount * sizeof(*pOut)); return; } } DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__pcm(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut) { drwav_uint64 totalFramesRead; drwav_uint8 sampleData[4096] = {0}; drwav_uint32 bytesPerFrame; drwav_uint32 bytesPerSample; drwav_uint64 samplesRead; if ((pWav->translatedFormatTag == DR_WAVE_FORMAT_PCM && pWav->bitsPerSample == 16) || pBufferOut == NULL) { return drwav_read_pcm_frames(pWav, framesToRead, pBufferOut); } bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav); if (bytesPerFrame == 0) { return 0; } bytesPerSample = bytesPerFrame / pWav->channels; if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) { return 0; } totalFramesRead = 0; while (framesToRead > 0) { drwav_uint64 framesToReadThisIteration = drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); if (framesRead == 0) { break; } DRWAV_ASSERT(framesRead <= framesToReadThisIteration); samplesRead = framesRead * pWav->channels; if ((samplesRead * bytesPerSample) > sizeof(sampleData)) { DRWAV_ASSERT(DRWAV_FALSE); break; } drwav__pcm_to_s16(pBufferOut, sampleData, (size_t)samplesRead, bytesPerSample); pBufferOut += samplesRead; framesToRead -= framesRead; totalFramesRead += framesRead; } return totalFramesRead; } DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__ieee(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut) { drwav_uint64 totalFramesRead; drwav_uint8 sampleData[4096] = {0}; drwav_uint32 bytesPerFrame; drwav_uint32 bytesPerSample; drwav_uint64 samplesRead; if (pBufferOut == NULL) { return drwav_read_pcm_frames(pWav, framesToRead, NULL); } bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav); if (bytesPerFrame == 0) { return 0; } bytesPerSample = bytesPerFrame / pWav->channels; if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) { return 0; } totalFramesRead = 0; while (framesToRead > 0) { drwav_uint64 framesToReadThisIteration = drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); if (framesRead == 0) { break; } DRWAV_ASSERT(framesRead <= framesToReadThisIteration); samplesRead = framesRead * pWav->channels; if ((samplesRead * bytesPerSample) > sizeof(sampleData)) { DRWAV_ASSERT(DRWAV_FALSE); break; } drwav__ieee_to_s16(pBufferOut, sampleData, (size_t)samplesRead, bytesPerSample); pBufferOut += samplesRead; framesToRead -= framesRead; totalFramesRead += framesRead; } return totalFramesRead; } DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__alaw(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut) { drwav_uint64 totalFramesRead; drwav_uint8 sampleData[4096] = {0}; drwav_uint32 bytesPerFrame; drwav_uint32 bytesPerSample; drwav_uint64 samplesRead; if (pBufferOut == NULL) { return drwav_read_pcm_frames(pWav, framesToRead, NULL); } bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav); if (bytesPerFrame == 0) { return 0; } bytesPerSample = bytesPerFrame / pWav->channels; if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) { return 0; } totalFramesRead = 0; while (framesToRead > 0) { drwav_uint64 framesToReadThisIteration = drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); if (framesRead == 0) { break; } DRWAV_ASSERT(framesRead <= framesToReadThisIteration); samplesRead = framesRead * pWav->channels; if ((samplesRead * bytesPerSample) > sizeof(sampleData)) { DRWAV_ASSERT(DRWAV_FALSE); break; } drwav_alaw_to_s16(pBufferOut, sampleData, (size_t)samplesRead); pBufferOut += samplesRead; framesToRead -= framesRead; totalFramesRead += framesRead; } return totalFramesRead; } DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__mulaw(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut) { drwav_uint64 totalFramesRead; drwav_uint8 sampleData[4096] = {0}; drwav_uint32 bytesPerFrame; drwav_uint32 bytesPerSample; drwav_uint64 samplesRead; if (pBufferOut == NULL) { return drwav_read_pcm_frames(pWav, framesToRead, NULL); } bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav); if (bytesPerFrame == 0) { return 0; } bytesPerSample = bytesPerFrame / pWav->channels; if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) { return 0; } totalFramesRead = 0; while (framesToRead > 0) { drwav_uint64 framesToReadThisIteration = drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); if (framesRead == 0) { break; } DRWAV_ASSERT(framesRead <= framesToReadThisIteration); samplesRead = framesRead * pWav->channels; if ((samplesRead * bytesPerSample) > sizeof(sampleData)) { DRWAV_ASSERT(DRWAV_FALSE); break; } drwav_mulaw_to_s16(pBufferOut, sampleData, (size_t)samplesRead); pBufferOut += samplesRead; framesToRead -= framesRead; totalFramesRead += framesRead; } return totalFramesRead; } DRWAV_API drwav_uint64 drwav_read_pcm_frames_s16(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut) { if (pWav == NULL || framesToRead == 0) { return 0; } if (pBufferOut == NULL) { return drwav_read_pcm_frames(pWav, framesToRead, NULL); } if (framesToRead * pWav->channels * sizeof(drwav_int16) > DRWAV_SIZE_MAX) { framesToRead = DRWAV_SIZE_MAX / sizeof(drwav_int16) / pWav->channels; } if (pWav->translatedFormatTag == DR_WAVE_FORMAT_PCM) { return drwav_read_pcm_frames_s16__pcm(pWav, framesToRead, pBufferOut); } if (pWav->translatedFormatTag == DR_WAVE_FORMAT_IEEE_FLOAT) { return drwav_read_pcm_frames_s16__ieee(pWav, framesToRead, pBufferOut); } if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ALAW) { return drwav_read_pcm_frames_s16__alaw(pWav, framesToRead, pBufferOut); } if (pWav->translatedFormatTag == DR_WAVE_FORMAT_MULAW) { return drwav_read_pcm_frames_s16__mulaw(pWav, framesToRead, pBufferOut); } if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM) { return drwav_read_pcm_frames_s16__msadpcm(pWav, framesToRead, pBufferOut); } if (pWav->translatedFormatTag == DR_WAVE_FORMAT_DVI_ADPCM) { return drwav_read_pcm_frames_s16__ima(pWav, framesToRead, pBufferOut); } return 0; } DRWAV_API drwav_uint64 drwav_read_pcm_frames_s16le(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut) { drwav_uint64 framesRead = drwav_read_pcm_frames_s16(pWav, framesToRead, pBufferOut); if (pBufferOut != NULL && drwav__is_little_endian() == DRWAV_FALSE) { drwav__bswap_samples_s16(pBufferOut, framesRead*pWav->channels); } return framesRead; } DRWAV_API drwav_uint64 drwav_read_pcm_frames_s16be(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut) { drwav_uint64 framesRead = drwav_read_pcm_frames_s16(pWav, framesToRead, pBufferOut); if (pBufferOut != NULL && drwav__is_little_endian() == DRWAV_TRUE) { drwav__bswap_samples_s16(pBufferOut, framesRead*pWav->channels); } return framesRead; } DRWAV_API void drwav_u8_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCount) { int r; size_t i; for (i = 0; i < sampleCount; ++i) { int x = pIn[i]; r = x << 8; r = r - 32768; pOut[i] = (short)r; } } DRWAV_API void drwav_s24_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCount) { int r; size_t i; for (i = 0; i < sampleCount; ++i) { int x = ((int)(((unsigned int)(((const drwav_uint8*)pIn)[i*3+0]) << 8) | ((unsigned int)(((const drwav_uint8*)pIn)[i*3+1]) << 16) | ((unsigned int)(((const drwav_uint8*)pIn)[i*3+2])) << 24)) >> 8; r = x >> 8; pOut[i] = (short)r; } } DRWAV_API void drwav_s32_to_s16(drwav_int16* pOut, const drwav_int32* pIn, size_t sampleCount) { int r; size_t i; for (i = 0; i < sampleCount; ++i) { int x = pIn[i]; r = x >> 16; pOut[i] = (short)r; } } DRWAV_API void drwav_f32_to_s16(drwav_int16* pOut, const float* pIn, size_t sampleCount) { int r; size_t i; for (i = 0; i < sampleCount; ++i) { float x = pIn[i]; float c; c = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); c = c + 1; r = (int)(c * 32767.5f); r = r - 32768; pOut[i] = (short)r; } } DRWAV_API void drwav_f64_to_s16(drwav_int16* pOut, const double* pIn, size_t sampleCount) { int r; size_t i; for (i = 0; i < sampleCount; ++i) { double x = pIn[i]; double c; c = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); c = c + 1; r = (int)(c * 32767.5); r = r - 32768; pOut[i] = (short)r; } } DRWAV_API void drwav_alaw_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCount) { size_t i; for (i = 0; i < sampleCount; ++i) { pOut[i] = drwav__alaw_to_s16(pIn[i]); } } DRWAV_API void drwav_mulaw_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCount) { size_t i; for (i = 0; i < sampleCount; ++i) { pOut[i] = drwav__mulaw_to_s16(pIn[i]); } } DRWAV_PRIVATE void drwav__pcm_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount, unsigned int bytesPerSample) { unsigned int i; if (bytesPerSample == 1) { drwav_u8_to_f32(pOut, pIn, sampleCount); return; } if (bytesPerSample == 2) { drwav_s16_to_f32(pOut, (const drwav_int16*)pIn, sampleCount); return; } if (bytesPerSample == 3) { drwav_s24_to_f32(pOut, pIn, sampleCount); return; } if (bytesPerSample == 4) { drwav_s32_to_f32(pOut, (const drwav_int32*)pIn, sampleCount); return; } if (bytesPerSample > 8) { DRWAV_ZERO_MEMORY(pOut, sampleCount * sizeof(*pOut)); return; } for (i = 0; i < sampleCount; ++i) { drwav_uint64 sample = 0; unsigned int shift = (8 - bytesPerSample) * 8; unsigned int j; for (j = 0; j < bytesPerSample; j += 1) { DRWAV_ASSERT(j < 8); sample |= (drwav_uint64)(pIn[j]) << shift; shift += 8; } pIn += j; *pOut++ = (float)((drwav_int64)sample / 9223372036854775807.0); } } DRWAV_PRIVATE void drwav__ieee_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount, unsigned int bytesPerSample) { if (bytesPerSample == 4) { unsigned int i; for (i = 0; i < sampleCount; ++i) { *pOut++ = ((const float*)pIn)[i]; } return; } else if (bytesPerSample == 8) { drwav_f64_to_f32(pOut, (const double*)pIn, sampleCount); return; } else { DRWAV_ZERO_MEMORY(pOut, sampleCount * sizeof(*pOut)); return; } } DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_f32__pcm(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut) { drwav_uint64 totalFramesRead; drwav_uint8 sampleData[4096] = {0}; drwav_uint32 bytesPerFrame; drwav_uint32 bytesPerSample; drwav_uint64 samplesRead; bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav); if (bytesPerFrame == 0) { return 0; } bytesPerSample = bytesPerFrame / pWav->channels; if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) { return 0; } totalFramesRead = 0; while (framesToRead > 0) { drwav_uint64 framesToReadThisIteration = drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); if (framesRead == 0) { break; } DRWAV_ASSERT(framesRead <= framesToReadThisIteration); samplesRead = framesRead * pWav->channels; if ((samplesRead * bytesPerSample) > sizeof(sampleData)) { DRWAV_ASSERT(DRWAV_FALSE); break; } drwav__pcm_to_f32(pBufferOut, sampleData, (size_t)samplesRead, bytesPerSample); pBufferOut += samplesRead; framesToRead -= framesRead; totalFramesRead += framesRead; } return totalFramesRead; } DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_f32__msadpcm_ima(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut) { drwav_uint64 totalFramesRead; drwav_int16 samples16[2048]; totalFramesRead = 0; while (framesToRead > 0) { drwav_uint64 framesToReadThisIteration = drwav_min(framesToRead, drwav_countof(samples16)/pWav->channels); drwav_uint64 framesRead = drwav_read_pcm_frames_s16(pWav, framesToReadThisIteration, samples16); if (framesRead == 0) { break; } DRWAV_ASSERT(framesRead <= framesToReadThisIteration); drwav_s16_to_f32(pBufferOut, samples16, (size_t)(framesRead*pWav->channels)); pBufferOut += framesRead*pWav->channels; framesToRead -= framesRead; totalFramesRead += framesRead; } return totalFramesRead; } DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_f32__ieee(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut) { drwav_uint64 totalFramesRead; drwav_uint8 sampleData[4096] = {0}; drwav_uint32 bytesPerFrame; drwav_uint32 bytesPerSample; drwav_uint64 samplesRead; if (pWav->translatedFormatTag == DR_WAVE_FORMAT_IEEE_FLOAT && pWav->bitsPerSample == 32) { return drwav_read_pcm_frames(pWav, framesToRead, pBufferOut); } bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav); if (bytesPerFrame == 0) { return 0; } bytesPerSample = bytesPerFrame / pWav->channels; if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) { return 0; } totalFramesRead = 0; while (framesToRead > 0) { drwav_uint64 framesToReadThisIteration = drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); if (framesRead == 0) { break; } DRWAV_ASSERT(framesRead <= framesToReadThisIteration); samplesRead = framesRead * pWav->channels; if ((samplesRead * bytesPerSample) > sizeof(sampleData)) { DRWAV_ASSERT(DRWAV_FALSE); break; } drwav__ieee_to_f32(pBufferOut, sampleData, (size_t)samplesRead, bytesPerSample); pBufferOut += samplesRead; framesToRead -= framesRead; totalFramesRead += framesRead; } return totalFramesRead; } DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_f32__alaw(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut) { drwav_uint64 totalFramesRead; drwav_uint8 sampleData[4096] = {0}; drwav_uint32 bytesPerFrame; drwav_uint32 bytesPerSample; drwav_uint64 samplesRead; bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav); if (bytesPerFrame == 0) { return 0; } bytesPerSample = bytesPerFrame / pWav->channels; if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) { return 0; } totalFramesRead = 0; while (framesToRead > 0) { drwav_uint64 framesToReadThisIteration = drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); if (framesRead == 0) { break; } DRWAV_ASSERT(framesRead <= framesToReadThisIteration); samplesRead = framesRead * pWav->channels; if ((samplesRead * bytesPerSample) > sizeof(sampleData)) { DRWAV_ASSERT(DRWAV_FALSE); break; } drwav_alaw_to_f32(pBufferOut, sampleData, (size_t)samplesRead); pBufferOut += samplesRead; framesToRead -= framesRead; totalFramesRead += framesRead; } return totalFramesRead; } DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_f32__mulaw(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut) { drwav_uint64 totalFramesRead; drwav_uint8 sampleData[4096] = {0}; drwav_uint32 bytesPerFrame; drwav_uint32 bytesPerSample; drwav_uint64 samplesRead; bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav); if (bytesPerFrame == 0) { return 0; } bytesPerSample = bytesPerFrame / pWav->channels; if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) { return 0; } totalFramesRead = 0; while (framesToRead > 0) { drwav_uint64 framesToReadThisIteration = drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); if (framesRead == 0) { break; } DRWAV_ASSERT(framesRead <= framesToReadThisIteration); samplesRead = framesRead * pWav->channels; if ((samplesRead * bytesPerSample) > sizeof(sampleData)) { DRWAV_ASSERT(DRWAV_FALSE); break; } drwav_mulaw_to_f32(pBufferOut, sampleData, (size_t)samplesRead); pBufferOut += samplesRead; framesToRead -= framesRead; totalFramesRead += framesRead; } return totalFramesRead; } DRWAV_API drwav_uint64 drwav_read_pcm_frames_f32(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut) { if (pWav == NULL || framesToRead == 0) { return 0; } if (pBufferOut == NULL) { return drwav_read_pcm_frames(pWav, framesToRead, NULL); } if (framesToRead * pWav->channels * sizeof(float) > DRWAV_SIZE_MAX) { framesToRead = DRWAV_SIZE_MAX / sizeof(float) / pWav->channels; } if (pWav->translatedFormatTag == DR_WAVE_FORMAT_PCM) { return drwav_read_pcm_frames_f32__pcm(pWav, framesToRead, pBufferOut); } if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM || pWav->translatedFormatTag == DR_WAVE_FORMAT_DVI_ADPCM) { return drwav_read_pcm_frames_f32__msadpcm_ima(pWav, framesToRead, pBufferOut); } if (pWav->translatedFormatTag == DR_WAVE_FORMAT_IEEE_FLOAT) { return drwav_read_pcm_frames_f32__ieee(pWav, framesToRead, pBufferOut); } if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ALAW) { return drwav_read_pcm_frames_f32__alaw(pWav, framesToRead, pBufferOut); } if (pWav->translatedFormatTag == DR_WAVE_FORMAT_MULAW) { return drwav_read_pcm_frames_f32__mulaw(pWav, framesToRead, pBufferOut); } return 0; } DRWAV_API drwav_uint64 drwav_read_pcm_frames_f32le(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut) { drwav_uint64 framesRead = drwav_read_pcm_frames_f32(pWav, framesToRead, pBufferOut); if (pBufferOut != NULL && drwav__is_little_endian() == DRWAV_FALSE) { drwav__bswap_samples_f32(pBufferOut, framesRead*pWav->channels); } return framesRead; } DRWAV_API drwav_uint64 drwav_read_pcm_frames_f32be(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut) { drwav_uint64 framesRead = drwav_read_pcm_frames_f32(pWav, framesToRead, pBufferOut); if (pBufferOut != NULL && drwav__is_little_endian() == DRWAV_TRUE) { drwav__bswap_samples_f32(pBufferOut, framesRead*pWav->channels); } return framesRead; } DRWAV_API void drwav_u8_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount) { size_t i; if (pOut == NULL || pIn == NULL) { return; } #ifdef DR_WAV_LIBSNDFILE_COMPAT for (i = 0; i < sampleCount; ++i) { *pOut++ = (pIn[i] / 256.0f) * 2 - 1; } #else for (i = 0; i < sampleCount; ++i) { float x = pIn[i]; x = x * 0.00784313725490196078f; x = x - 1; *pOut++ = x; } #endif } DRWAV_API void drwav_s16_to_f32(float* pOut, const drwav_int16* pIn, size_t sampleCount) { size_t i; if (pOut == NULL || pIn == NULL) { return; } for (i = 0; i < sampleCount; ++i) { *pOut++ = pIn[i] * 0.000030517578125f; } } DRWAV_API void drwav_s24_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount) { size_t i; if (pOut == NULL || pIn == NULL) { return; } for (i = 0; i < sampleCount; ++i) { double x; drwav_uint32 a = ((drwav_uint32)(pIn[i*3+0]) << 8); drwav_uint32 b = ((drwav_uint32)(pIn[i*3+1]) << 16); drwav_uint32 c = ((drwav_uint32)(pIn[i*3+2]) << 24); x = (double)((drwav_int32)(a | b | c) >> 8); *pOut++ = (float)(x * 0.00000011920928955078125); } } DRWAV_API void drwav_s32_to_f32(float* pOut, const drwav_int32* pIn, size_t sampleCount) { size_t i; if (pOut == NULL || pIn == NULL) { return; } for (i = 0; i < sampleCount; ++i) { *pOut++ = (float)(pIn[i] / 2147483648.0); } } DRWAV_API void drwav_f64_to_f32(float* pOut, const double* pIn, size_t sampleCount) { size_t i; if (pOut == NULL || pIn == NULL) { return; } for (i = 0; i < sampleCount; ++i) { *pOut++ = (float)pIn[i]; } } DRWAV_API void drwav_alaw_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount) { size_t i; if (pOut == NULL || pIn == NULL) { return; } for (i = 0; i < sampleCount; ++i) { *pOut++ = drwav__alaw_to_s16(pIn[i]) / 32768.0f; } } DRWAV_API void drwav_mulaw_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount) { size_t i; if (pOut == NULL || pIn == NULL) { return; } for (i = 0; i < sampleCount; ++i) { *pOut++ = drwav__mulaw_to_s16(pIn[i]) / 32768.0f; } } DRWAV_PRIVATE void drwav__pcm_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t totalSampleCount, unsigned int bytesPerSample) { unsigned int i; if (bytesPerSample == 1) { drwav_u8_to_s32(pOut, pIn, totalSampleCount); return; } if (bytesPerSample == 2) { drwav_s16_to_s32(pOut, (const drwav_int16*)pIn, totalSampleCount); return; } if (bytesPerSample == 3) { drwav_s24_to_s32(pOut, pIn, totalSampleCount); return; } if (bytesPerSample == 4) { for (i = 0; i < totalSampleCount; ++i) { *pOut++ = ((const drwav_int32*)pIn)[i]; } return; } if (bytesPerSample > 8) { DRWAV_ZERO_MEMORY(pOut, totalSampleCount * sizeof(*pOut)); return; } for (i = 0; i < totalSampleCount; ++i) { drwav_uint64 sample = 0; unsigned int shift = (8 - bytesPerSample) * 8; unsigned int j; for (j = 0; j < bytesPerSample; j += 1) { DRWAV_ASSERT(j < 8); sample |= (drwav_uint64)(pIn[j]) << shift; shift += 8; } pIn += j; *pOut++ = (drwav_int32)((drwav_int64)sample >> 32); } } DRWAV_PRIVATE void drwav__ieee_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t totalSampleCount, unsigned int bytesPerSample) { if (bytesPerSample == 4) { drwav_f32_to_s32(pOut, (const float*)pIn, totalSampleCount); return; } else if (bytesPerSample == 8) { drwav_f64_to_s32(pOut, (const double*)pIn, totalSampleCount); return; } else { DRWAV_ZERO_MEMORY(pOut, totalSampleCount * sizeof(*pOut)); return; } } DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s32__pcm(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut) { drwav_uint64 totalFramesRead; drwav_uint8 sampleData[4096] = {0}; drwav_uint32 bytesPerFrame; drwav_uint32 bytesPerSample; drwav_uint64 samplesRead; if (pWav->translatedFormatTag == DR_WAVE_FORMAT_PCM && pWav->bitsPerSample == 32) { return drwav_read_pcm_frames(pWav, framesToRead, pBufferOut); } bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav); if (bytesPerFrame == 0) { return 0; } bytesPerSample = bytesPerFrame / pWav->channels; if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) { return 0; } totalFramesRead = 0; while (framesToRead > 0) { drwav_uint64 framesToReadThisIteration = drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); if (framesRead == 0) { break; } DRWAV_ASSERT(framesRead <= framesToReadThisIteration); samplesRead = framesRead * pWav->channels; if ((samplesRead * bytesPerSample) > sizeof(sampleData)) { DRWAV_ASSERT(DRWAV_FALSE); break; } drwav__pcm_to_s32(pBufferOut, sampleData, (size_t)samplesRead, bytesPerSample); pBufferOut += samplesRead; framesToRead -= framesRead; totalFramesRead += framesRead; } return totalFramesRead; } DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s32__msadpcm_ima(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut) { drwav_uint64 totalFramesRead = 0; drwav_int16 samples16[2048]; while (framesToRead > 0) { drwav_uint64 framesToReadThisIteration = drwav_min(framesToRead, drwav_countof(samples16)/pWav->channels); drwav_uint64 framesRead = drwav_read_pcm_frames_s16(pWav, framesToReadThisIteration, samples16); if (framesRead == 0) { break; } DRWAV_ASSERT(framesRead <= framesToReadThisIteration); drwav_s16_to_s32(pBufferOut, samples16, (size_t)(framesRead*pWav->channels)); pBufferOut += framesRead*pWav->channels; framesToRead -= framesRead; totalFramesRead += framesRead; } return totalFramesRead; } DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s32__ieee(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut) { drwav_uint64 totalFramesRead; drwav_uint8 sampleData[4096] = {0}; drwav_uint32 bytesPerFrame; drwav_uint32 bytesPerSample; drwav_uint64 samplesRead; bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav); if (bytesPerFrame == 0) { return 0; } bytesPerSample = bytesPerFrame / pWav->channels; if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) { return 0; } totalFramesRead = 0; while (framesToRead > 0) { drwav_uint64 framesToReadThisIteration = drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); if (framesRead == 0) { break; } DRWAV_ASSERT(framesRead <= framesToReadThisIteration); samplesRead = framesRead * pWav->channels; if ((samplesRead * bytesPerSample) > sizeof(sampleData)) { DRWAV_ASSERT(DRWAV_FALSE); break; } drwav__ieee_to_s32(pBufferOut, sampleData, (size_t)samplesRead, bytesPerSample); pBufferOut += samplesRead; framesToRead -= framesRead; totalFramesRead += framesRead; } return totalFramesRead; } DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s32__alaw(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut) { drwav_uint64 totalFramesRead; drwav_uint8 sampleData[4096] = {0}; drwav_uint32 bytesPerFrame; drwav_uint32 bytesPerSample; drwav_uint64 samplesRead; bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav); if (bytesPerFrame == 0) { return 0; } bytesPerSample = bytesPerFrame / pWav->channels; if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) { return 0; } totalFramesRead = 0; while (framesToRead > 0) { drwav_uint64 framesToReadThisIteration = drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); if (framesRead == 0) { break; } DRWAV_ASSERT(framesRead <= framesToReadThisIteration); samplesRead = framesRead * pWav->channels; if ((samplesRead * bytesPerSample) > sizeof(sampleData)) { DRWAV_ASSERT(DRWAV_FALSE); break; } drwav_alaw_to_s32(pBufferOut, sampleData, (size_t)samplesRead); pBufferOut += samplesRead; framesToRead -= framesRead; totalFramesRead += framesRead; } return totalFramesRead; } DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s32__mulaw(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut) { drwav_uint64 totalFramesRead; drwav_uint8 sampleData[4096] = {0}; drwav_uint32 bytesPerFrame; drwav_uint32 bytesPerSample; drwav_uint64 samplesRead; bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav); if (bytesPerFrame == 0) { return 0; } bytesPerSample = bytesPerFrame / pWav->channels; if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) { return 0; } totalFramesRead = 0; while (framesToRead > 0) { drwav_uint64 framesToReadThisIteration = drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); if (framesRead == 0) { break; } DRWAV_ASSERT(framesRead <= framesToReadThisIteration); samplesRead = framesRead * pWav->channels; if ((samplesRead * bytesPerSample) > sizeof(sampleData)) { DRWAV_ASSERT(DRWAV_FALSE); break; } drwav_mulaw_to_s32(pBufferOut, sampleData, (size_t)samplesRead); pBufferOut += samplesRead; framesToRead -= framesRead; totalFramesRead += framesRead; } return totalFramesRead; } DRWAV_API drwav_uint64 drwav_read_pcm_frames_s32(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut) { if (pWav == NULL || framesToRead == 0) { return 0; } if (pBufferOut == NULL) { return drwav_read_pcm_frames(pWav, framesToRead, NULL); } if (framesToRead * pWav->channels * sizeof(drwav_int32) > DRWAV_SIZE_MAX) { framesToRead = DRWAV_SIZE_MAX / sizeof(drwav_int32) / pWav->channels; } if (pWav->translatedFormatTag == DR_WAVE_FORMAT_PCM) { return drwav_read_pcm_frames_s32__pcm(pWav, framesToRead, pBufferOut); } if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM || pWav->translatedFormatTag == DR_WAVE_FORMAT_DVI_ADPCM) { return drwav_read_pcm_frames_s32__msadpcm_ima(pWav, framesToRead, pBufferOut); } if (pWav->translatedFormatTag == DR_WAVE_FORMAT_IEEE_FLOAT) { return drwav_read_pcm_frames_s32__ieee(pWav, framesToRead, pBufferOut); } if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ALAW) { return drwav_read_pcm_frames_s32__alaw(pWav, framesToRead, pBufferOut); } if (pWav->translatedFormatTag == DR_WAVE_FORMAT_MULAW) { return drwav_read_pcm_frames_s32__mulaw(pWav, framesToRead, pBufferOut); } return 0; } DRWAV_API drwav_uint64 drwav_read_pcm_frames_s32le(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut) { drwav_uint64 framesRead = drwav_read_pcm_frames_s32(pWav, framesToRead, pBufferOut); if (pBufferOut != NULL && drwav__is_little_endian() == DRWAV_FALSE) { drwav__bswap_samples_s32(pBufferOut, framesRead*pWav->channels); } return framesRead; } DRWAV_API drwav_uint64 drwav_read_pcm_frames_s32be(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut) { drwav_uint64 framesRead = drwav_read_pcm_frames_s32(pWav, framesToRead, pBufferOut); if (pBufferOut != NULL && drwav__is_little_endian() == DRWAV_TRUE) { drwav__bswap_samples_s32(pBufferOut, framesRead*pWav->channels); } return framesRead; } DRWAV_API void drwav_u8_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sampleCount) { size_t i; if (pOut == NULL || pIn == NULL) { return; } for (i = 0; i < sampleCount; ++i) { *pOut++ = ((int)pIn[i] - 128) << 24; } } DRWAV_API void drwav_s16_to_s32(drwav_int32* pOut, const drwav_int16* pIn, size_t sampleCount) { size_t i; if (pOut == NULL || pIn == NULL) { return; } for (i = 0; i < sampleCount; ++i) { *pOut++ = pIn[i] << 16; } } DRWAV_API void drwav_s24_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sampleCount) { size_t i; if (pOut == NULL || pIn == NULL) { return; } for (i = 0; i < sampleCount; ++i) { unsigned int s0 = pIn[i*3 + 0]; unsigned int s1 = pIn[i*3 + 1]; unsigned int s2 = pIn[i*3 + 2]; drwav_int32 sample32 = (drwav_int32)((s0 << 8) | (s1 << 16) | (s2 << 24)); *pOut++ = sample32; } } DRWAV_API void drwav_f32_to_s32(drwav_int32* pOut, const float* pIn, size_t sampleCount) { size_t i; if (pOut == NULL || pIn == NULL) { return; } for (i = 0; i < sampleCount; ++i) { *pOut++ = (drwav_int32)(2147483648.0 * pIn[i]); } } DRWAV_API void drwav_f64_to_s32(drwav_int32* pOut, const double* pIn, size_t sampleCount) { size_t i; if (pOut == NULL || pIn == NULL) { return; } for (i = 0; i < sampleCount; ++i) { *pOut++ = (drwav_int32)(2147483648.0 * pIn[i]); } } DRWAV_API void drwav_alaw_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sampleCount) { size_t i; if (pOut == NULL || pIn == NULL) { return; } for (i = 0; i < sampleCount; ++i) { *pOut++ = ((drwav_int32)drwav__alaw_to_s16(pIn[i])) << 16; } } DRWAV_API void drwav_mulaw_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sampleCount) { size_t i; if (pOut == NULL || pIn == NULL) { return; } for (i= 0; i < sampleCount; ++i) { *pOut++ = ((drwav_int32)drwav__mulaw_to_s16(pIn[i])) << 16; } } DRWAV_PRIVATE drwav_int16* drwav__read_pcm_frames_and_close_s16(drwav* pWav, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalFrameCount) { drwav_uint64 sampleDataSize; drwav_int16* pSampleData; drwav_uint64 framesRead; DRWAV_ASSERT(pWav != NULL); sampleDataSize = pWav->totalPCMFrameCount * pWav->channels * sizeof(drwav_int16); if (sampleDataSize > DRWAV_SIZE_MAX) { drwav_uninit(pWav); return NULL; } pSampleData = (drwav_int16*)drwav__malloc_from_callbacks((size_t)sampleDataSize, &pWav->allocationCallbacks); if (pSampleData == NULL) { drwav_uninit(pWav); return NULL; } framesRead = drwav_read_pcm_frames_s16(pWav, (size_t)pWav->totalPCMFrameCount, pSampleData); if (framesRead != pWav->totalPCMFrameCount) { drwav__free_from_callbacks(pSampleData, &pWav->allocationCallbacks); drwav_uninit(pWav); return NULL; } drwav_uninit(pWav); if (sampleRate) { *sampleRate = pWav->sampleRate; } if (channels) { *channels = pWav->channels; } if (totalFrameCount) { *totalFrameCount = pWav->totalPCMFrameCount; } return pSampleData; } DRWAV_PRIVATE float* drwav__read_pcm_frames_and_close_f32(drwav* pWav, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalFrameCount) { drwav_uint64 sampleDataSize; float* pSampleData; drwav_uint64 framesRead; DRWAV_ASSERT(pWav != NULL); sampleDataSize = pWav->totalPCMFrameCount * pWav->channels * sizeof(float); if (sampleDataSize > DRWAV_SIZE_MAX) { drwav_uninit(pWav); return NULL; } pSampleData = (float*)drwav__malloc_from_callbacks((size_t)sampleDataSize, &pWav->allocationCallbacks); if (pSampleData == NULL) { drwav_uninit(pWav); return NULL; } framesRead = drwav_read_pcm_frames_f32(pWav, (size_t)pWav->totalPCMFrameCount, pSampleData); if (framesRead != pWav->totalPCMFrameCount) { drwav__free_from_callbacks(pSampleData, &pWav->allocationCallbacks); drwav_uninit(pWav); return NULL; } drwav_uninit(pWav); if (sampleRate) { *sampleRate = pWav->sampleRate; } if (channels) { *channels = pWav->channels; } if (totalFrameCount) { *totalFrameCount = pWav->totalPCMFrameCount; } return pSampleData; } DRWAV_PRIVATE drwav_int32* drwav__read_pcm_frames_and_close_s32(drwav* pWav, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalFrameCount) { drwav_uint64 sampleDataSize; drwav_int32* pSampleData; drwav_uint64 framesRead; DRWAV_ASSERT(pWav != NULL); sampleDataSize = pWav->totalPCMFrameCount * pWav->channels * sizeof(drwav_int32); if (sampleDataSize > DRWAV_SIZE_MAX) { drwav_uninit(pWav); return NULL; } pSampleData = (drwav_int32*)drwav__malloc_from_callbacks((size_t)sampleDataSize, &pWav->allocationCallbacks); if (pSampleData == NULL) { drwav_uninit(pWav); return NULL; } framesRead = drwav_read_pcm_frames_s32(pWav, (size_t)pWav->totalPCMFrameCount, pSampleData); if (framesRead != pWav->totalPCMFrameCount) { drwav__free_from_callbacks(pSampleData, &pWav->allocationCallbacks); drwav_uninit(pWav); return NULL; } drwav_uninit(pWav); if (sampleRate) { *sampleRate = pWav->sampleRate; } if (channels) { *channels = pWav->channels; } if (totalFrameCount) { *totalFrameCount = pWav->totalPCMFrameCount; } return pSampleData; } DRWAV_API drwav_int16* drwav_open_and_read_pcm_frames_s16(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks) { drwav wav; if (channelsOut) { *channelsOut = 0; } if (sampleRateOut) { *sampleRateOut = 0; } if (totalFrameCountOut) { *totalFrameCountOut = 0; } if (!drwav_init(&wav, onRead, onSeek, pUserData, pAllocationCallbacks)) { return NULL; } return drwav__read_pcm_frames_and_close_s16(&wav, channelsOut, sampleRateOut, totalFrameCountOut); } DRWAV_API float* drwav_open_and_read_pcm_frames_f32(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks) { drwav wav; if (channelsOut) { *channelsOut = 0; } if (sampleRateOut) { *sampleRateOut = 0; } if (totalFrameCountOut) { *totalFrameCountOut = 0; } if (!drwav_init(&wav, onRead, onSeek, pUserData, pAllocationCallbacks)) { return NULL; } return drwav__read_pcm_frames_and_close_f32(&wav, channelsOut, sampleRateOut, totalFrameCountOut); } DRWAV_API drwav_int32* drwav_open_and_read_pcm_frames_s32(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks) { drwav wav; if (channelsOut) { *channelsOut = 0; } if (sampleRateOut) { *sampleRateOut = 0; } if (totalFrameCountOut) { *totalFrameCountOut = 0; } if (!drwav_init(&wav, onRead, onSeek, pUserData, pAllocationCallbacks)) { return NULL; } return drwav__read_pcm_frames_and_close_s32(&wav, channelsOut, sampleRateOut, totalFrameCountOut); } #ifndef DR_WAV_NO_STDIO DRWAV_API drwav_int16* drwav_open_file_and_read_pcm_frames_s16(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks) { drwav wav; if (channelsOut) { *channelsOut = 0; } if (sampleRateOut) { *sampleRateOut = 0; } if (totalFrameCountOut) { *totalFrameCountOut = 0; } if (!drwav_init_file(&wav, filename, pAllocationCallbacks)) { return NULL; } return drwav__read_pcm_frames_and_close_s16(&wav, channelsOut, sampleRateOut, totalFrameCountOut); } DRWAV_API float* drwav_open_file_and_read_pcm_frames_f32(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks) { drwav wav; if (channelsOut) { *channelsOut = 0; } if (sampleRateOut) { *sampleRateOut = 0; } if (totalFrameCountOut) { *totalFrameCountOut = 0; } if (!drwav_init_file(&wav, filename, pAllocationCallbacks)) { return NULL; } return drwav__read_pcm_frames_and_close_f32(&wav, channelsOut, sampleRateOut, totalFrameCountOut); } DRWAV_API drwav_int32* drwav_open_file_and_read_pcm_frames_s32(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks) { drwav wav; if (channelsOut) { *channelsOut = 0; } if (sampleRateOut) { *sampleRateOut = 0; } if (totalFrameCountOut) { *totalFrameCountOut = 0; } if (!drwav_init_file(&wav, filename, pAllocationCallbacks)) { return NULL; } return drwav__read_pcm_frames_and_close_s32(&wav, channelsOut, sampleRateOut, totalFrameCountOut); } #ifndef DR_WAV_NO_WCHAR DRWAV_API drwav_int16* drwav_open_file_and_read_pcm_frames_s16_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks) { drwav wav; if (sampleRateOut) { *sampleRateOut = 0; } if (channelsOut) { *channelsOut = 0; } if (totalFrameCountOut) { *totalFrameCountOut = 0; } if (!drwav_init_file_w(&wav, filename, pAllocationCallbacks)) { return NULL; } return drwav__read_pcm_frames_and_close_s16(&wav, channelsOut, sampleRateOut, totalFrameCountOut); } DRWAV_API float* drwav_open_file_and_read_pcm_frames_f32_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks) { drwav wav; if (sampleRateOut) { *sampleRateOut = 0; } if (channelsOut) { *channelsOut = 0; } if (totalFrameCountOut) { *totalFrameCountOut = 0; } if (!drwav_init_file_w(&wav, filename, pAllocationCallbacks)) { return NULL; } return drwav__read_pcm_frames_and_close_f32(&wav, channelsOut, sampleRateOut, totalFrameCountOut); } DRWAV_API drwav_int32* drwav_open_file_and_read_pcm_frames_s32_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks) { drwav wav; if (sampleRateOut) { *sampleRateOut = 0; } if (channelsOut) { *channelsOut = 0; } if (totalFrameCountOut) { *totalFrameCountOut = 0; } if (!drwav_init_file_w(&wav, filename, pAllocationCallbacks)) { return NULL; } return drwav__read_pcm_frames_and_close_s32(&wav, channelsOut, sampleRateOut, totalFrameCountOut); } #endif #endif DRWAV_API drwav_int16* drwav_open_memory_and_read_pcm_frames_s16(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks) { drwav wav; if (channelsOut) { *channelsOut = 0; } if (sampleRateOut) { *sampleRateOut = 0; } if (totalFrameCountOut) { *totalFrameCountOut = 0; } if (!drwav_init_memory(&wav, data, dataSize, pAllocationCallbacks)) { return NULL; } return drwav__read_pcm_frames_and_close_s16(&wav, channelsOut, sampleRateOut, totalFrameCountOut); } DRWAV_API float* drwav_open_memory_and_read_pcm_frames_f32(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks) { drwav wav; if (channelsOut) { *channelsOut = 0; } if (sampleRateOut) { *sampleRateOut = 0; } if (totalFrameCountOut) { *totalFrameCountOut = 0; } if (!drwav_init_memory(&wav, data, dataSize, pAllocationCallbacks)) { return NULL; } return drwav__read_pcm_frames_and_close_f32(&wav, channelsOut, sampleRateOut, totalFrameCountOut); } DRWAV_API drwav_int32* drwav_open_memory_and_read_pcm_frames_s32(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks) { drwav wav; if (channelsOut) { *channelsOut = 0; } if (sampleRateOut) { *sampleRateOut = 0; } if (totalFrameCountOut) { *totalFrameCountOut = 0; } if (!drwav_init_memory(&wav, data, dataSize, pAllocationCallbacks)) { return NULL; } return drwav__read_pcm_frames_and_close_s32(&wav, channelsOut, sampleRateOut, totalFrameCountOut); } #endif DRWAV_API void drwav_free(void* p, const drwav_allocation_callbacks* pAllocationCallbacks) { if (pAllocationCallbacks != NULL) { drwav__free_from_callbacks(p, pAllocationCallbacks); } else { drwav__free_default(p, NULL); } } DRWAV_API drwav_uint16 drwav_bytes_to_u16(const drwav_uint8* data) { return ((drwav_uint16)data[0] << 0) | ((drwav_uint16)data[1] << 8); } DRWAV_API drwav_int16 drwav_bytes_to_s16(const drwav_uint8* data) { return (drwav_int16)drwav_bytes_to_u16(data); } DRWAV_API drwav_uint32 drwav_bytes_to_u32(const drwav_uint8* data) { return ((drwav_uint32)data[0] << 0) | ((drwav_uint32)data[1] << 8) | ((drwav_uint32)data[2] << 16) | ((drwav_uint32)data[3] << 24); } DRWAV_API float drwav_bytes_to_f32(const drwav_uint8* data) { union { drwav_uint32 u32; float f32; } value; value.u32 = drwav_bytes_to_u32(data); return value.f32; } DRWAV_API drwav_int32 drwav_bytes_to_s32(const drwav_uint8* data) { return (drwav_int32)drwav_bytes_to_u32(data); } DRWAV_API drwav_uint64 drwav_bytes_to_u64(const drwav_uint8* data) { return ((drwav_uint64)data[0] << 0) | ((drwav_uint64)data[1] << 8) | ((drwav_uint64)data[2] << 16) | ((drwav_uint64)data[3] << 24) | ((drwav_uint64)data[4] << 32) | ((drwav_uint64)data[5] << 40) | ((drwav_uint64)data[6] << 48) | ((drwav_uint64)data[7] << 56); } DRWAV_API drwav_int64 drwav_bytes_to_s64(const drwav_uint8* data) { return (drwav_int64)drwav_bytes_to_u64(data); } DRWAV_API drwav_bool32 drwav_guid_equal(const drwav_uint8 a[16], const drwav_uint8 b[16]) { int i; for (i = 0; i < 16; i += 1) { if (a[i] != b[i]) { return DRWAV_FALSE; } } return DRWAV_TRUE; } DRWAV_API drwav_bool32 drwav_fourcc_equal(const drwav_uint8* a, const char* b) { return a[0] == b[0] && a[1] == b[1] && a[2] == b[2] && a[3] == b[3]; } #ifdef __MRC__ #pragma options opt reset #endif #endif /* dr_wav_c end */ #endif /* DRWAV_IMPLEMENTATION */ #endif /* MA_NO_WAV */ #if !defined(MA_NO_FLAC) && !defined(MA_NO_DECODING) #if !defined(DR_FLAC_IMPLEMENTATION) && !defined(DRFLAC_IMPLEMENTATION) /* For backwards compatibility. Will be removed in version 0.11 for cleanliness. */ /* dr_flac_c begin */ #ifndef dr_flac_c #define dr_flac_c #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) #pragma GCC diagnostic push #if __GNUC__ >= 7 #pragma GCC diagnostic ignored "-Wimplicit-fallthrough" #endif #endif #ifdef __linux__ #ifndef _BSD_SOURCE #define _BSD_SOURCE #endif #ifndef _DEFAULT_SOURCE #define _DEFAULT_SOURCE #endif #ifndef __USE_BSD #define __USE_BSD #endif #include #endif #include #include #ifdef _MSC_VER #define DRFLAC_INLINE __forceinline #elif defined(__GNUC__) #if defined(__STRICT_ANSI__) #define DRFLAC_GNUC_INLINE_HINT __inline__ #else #define DRFLAC_GNUC_INLINE_HINT inline #endif #if (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 2)) || defined(__clang__) #define DRFLAC_INLINE DRFLAC_GNUC_INLINE_HINT __attribute__((always_inline)) #else #define DRFLAC_INLINE DRFLAC_GNUC_INLINE_HINT #endif #elif defined(__WATCOMC__) #define DRFLAC_INLINE __inline #else #define DRFLAC_INLINE #endif #if defined(__x86_64__) || defined(_M_X64) #define DRFLAC_X64 #elif defined(__i386) || defined(_M_IX86) #define DRFLAC_X86 #elif defined(__arm__) || defined(_M_ARM) || defined(__arm64) || defined(__arm64__) || defined(__aarch64__) || defined(_M_ARM64) #define DRFLAC_ARM #endif #if !defined(DR_FLAC_NO_SIMD) #if defined(DRFLAC_X64) || defined(DRFLAC_X86) #if defined(_MSC_VER) && !defined(__clang__) #if _MSC_VER >= 1400 && !defined(DRFLAC_NO_SSE2) #define DRFLAC_SUPPORT_SSE2 #endif #if _MSC_VER >= 1600 && !defined(DRFLAC_NO_SSE41) #define DRFLAC_SUPPORT_SSE41 #endif #elif defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3))) #if defined(__SSE2__) && !defined(DRFLAC_NO_SSE2) #define DRFLAC_SUPPORT_SSE2 #endif #if defined(__SSE4_1__) && !defined(DRFLAC_NO_SSE41) #define DRFLAC_SUPPORT_SSE41 #endif #endif #if !defined(__GNUC__) && !defined(__clang__) && defined(__has_include) #if !defined(DRFLAC_SUPPORT_SSE2) && !defined(DRFLAC_NO_SSE2) && __has_include() #define DRFLAC_SUPPORT_SSE2 #endif #if !defined(DRFLAC_SUPPORT_SSE41) && !defined(DRFLAC_NO_SSE41) && __has_include() #define DRFLAC_SUPPORT_SSE41 #endif #endif #if defined(DRFLAC_SUPPORT_SSE41) #include #elif defined(DRFLAC_SUPPORT_SSE2) #include #endif #endif #if defined(DRFLAC_ARM) #if !defined(DRFLAC_NO_NEON) && (defined(__ARM_NEON) || defined(__aarch64__) || defined(_M_ARM64)) #define DRFLAC_SUPPORT_NEON #include #endif #endif #endif #if !defined(DR_FLAC_NO_SIMD) && (defined(DRFLAC_X86) || defined(DRFLAC_X64)) #if defined(_MSC_VER) && !defined(__clang__) #if _MSC_VER >= 1400 #include static void drflac__cpuid(int info[4], int fid) { __cpuid(info, fid); } #else #define DRFLAC_NO_CPUID #endif #else #if defined(__GNUC__) || defined(__clang__) static void drflac__cpuid(int info[4], int fid) { #if defined(DRFLAC_X86) && defined(__PIC__) __asm__ __volatile__ ( "xchg{l} {%%}ebx, %k1;" "cpuid;" "xchg{l} {%%}ebx, %k1;" : "=a"(info[0]), "=&r"(info[1]), "=c"(info[2]), "=d"(info[3]) : "a"(fid), "c"(0) ); #else __asm__ __volatile__ ( "cpuid" : "=a"(info[0]), "=b"(info[1]), "=c"(info[2]), "=d"(info[3]) : "a"(fid), "c"(0) ); #endif } #else #define DRFLAC_NO_CPUID #endif #endif #else #define DRFLAC_NO_CPUID #endif static DRFLAC_INLINE drflac_bool32 drflac_has_sse2(void) { #if defined(DRFLAC_SUPPORT_SSE2) #if (defined(DRFLAC_X64) || defined(DRFLAC_X86)) && !defined(DRFLAC_NO_SSE2) #if defined(DRFLAC_X64) return DRFLAC_TRUE; #elif (defined(_M_IX86_FP) && _M_IX86_FP == 2) || defined(__SSE2__) return DRFLAC_TRUE; #else #if defined(DRFLAC_NO_CPUID) return DRFLAC_FALSE; #else int info[4]; drflac__cpuid(info, 1); return (info[3] & (1 << 26)) != 0; #endif #endif #else return DRFLAC_FALSE; #endif #else return DRFLAC_FALSE; #endif } static DRFLAC_INLINE drflac_bool32 drflac_has_sse41(void) { #if defined(DRFLAC_SUPPORT_SSE41) #if (defined(DRFLAC_X64) || defined(DRFLAC_X86)) && !defined(DRFLAC_NO_SSE41) #if defined(__SSE4_1__) || defined(__AVX__) return DRFLAC_TRUE; #else #if defined(DRFLAC_NO_CPUID) return DRFLAC_FALSE; #else int info[4]; drflac__cpuid(info, 1); return (info[2] & (1 << 19)) != 0; #endif #endif #else return DRFLAC_FALSE; #endif #else return DRFLAC_FALSE; #endif } #if defined(_MSC_VER) && _MSC_VER >= 1500 && (defined(DRFLAC_X86) || defined(DRFLAC_X64)) && !defined(__clang__) #define DRFLAC_HAS_LZCNT_INTRINSIC #elif (defined(__GNUC__) && ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7))) #define DRFLAC_HAS_LZCNT_INTRINSIC #elif defined(__clang__) #if defined(__has_builtin) #if __has_builtin(__builtin_clzll) || __has_builtin(__builtin_clzl) #define DRFLAC_HAS_LZCNT_INTRINSIC #endif #endif #endif #if defined(_MSC_VER) && _MSC_VER >= 1400 && !defined(__clang__) #define DRFLAC_HAS_BYTESWAP16_INTRINSIC #define DRFLAC_HAS_BYTESWAP32_INTRINSIC #define DRFLAC_HAS_BYTESWAP64_INTRINSIC #elif defined(__clang__) #if defined(__has_builtin) #if __has_builtin(__builtin_bswap16) #define DRFLAC_HAS_BYTESWAP16_INTRINSIC #endif #if __has_builtin(__builtin_bswap32) #define DRFLAC_HAS_BYTESWAP32_INTRINSIC #endif #if __has_builtin(__builtin_bswap64) #define DRFLAC_HAS_BYTESWAP64_INTRINSIC #endif #endif #elif defined(__GNUC__) #if ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3)) #define DRFLAC_HAS_BYTESWAP32_INTRINSIC #define DRFLAC_HAS_BYTESWAP64_INTRINSIC #endif #if ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)) #define DRFLAC_HAS_BYTESWAP16_INTRINSIC #endif #elif defined(__WATCOMC__) && defined(__386__) #define DRFLAC_HAS_BYTESWAP16_INTRINSIC #define DRFLAC_HAS_BYTESWAP32_INTRINSIC #define DRFLAC_HAS_BYTESWAP64_INTRINSIC extern __inline drflac_uint16 _watcom_bswap16(drflac_uint16); extern __inline drflac_uint32 _watcom_bswap32(drflac_uint32); extern __inline drflac_uint64 _watcom_bswap64(drflac_uint64); #pragma aux _watcom_bswap16 = \ "xchg al, ah" \ parm [ax] \ value [ax] \ modify nomemory; #pragma aux _watcom_bswap32 = \ "bswap eax" \ parm [eax] \ value [eax] \ modify nomemory; #pragma aux _watcom_bswap64 = \ "bswap eax" \ "bswap edx" \ "xchg eax,edx" \ parm [eax edx] \ value [eax edx] \ modify nomemory; #endif #ifndef DRFLAC_ASSERT #include #define DRFLAC_ASSERT(expression) assert(expression) #endif #ifndef DRFLAC_MALLOC #define DRFLAC_MALLOC(sz) malloc((sz)) #endif #ifndef DRFLAC_REALLOC #define DRFLAC_REALLOC(p, sz) realloc((p), (sz)) #endif #ifndef DRFLAC_FREE #define DRFLAC_FREE(p) free((p)) #endif #ifndef DRFLAC_COPY_MEMORY #define DRFLAC_COPY_MEMORY(dst, src, sz) memcpy((dst), (src), (sz)) #endif #ifndef DRFLAC_ZERO_MEMORY #define DRFLAC_ZERO_MEMORY(p, sz) memset((p), 0, (sz)) #endif #ifndef DRFLAC_ZERO_OBJECT #define DRFLAC_ZERO_OBJECT(p) DRFLAC_ZERO_MEMORY((p), sizeof(*(p))) #endif #define DRFLAC_MAX_SIMD_VECTOR_SIZE 64 typedef drflac_int32 drflac_result; #define DRFLAC_SUCCESS 0 #define DRFLAC_ERROR -1 #define DRFLAC_INVALID_ARGS -2 #define DRFLAC_INVALID_OPERATION -3 #define DRFLAC_OUT_OF_MEMORY -4 #define DRFLAC_OUT_OF_RANGE -5 #define DRFLAC_ACCESS_DENIED -6 #define DRFLAC_DOES_NOT_EXIST -7 #define DRFLAC_ALREADY_EXISTS -8 #define DRFLAC_TOO_MANY_OPEN_FILES -9 #define DRFLAC_INVALID_FILE -10 #define DRFLAC_TOO_BIG -11 #define DRFLAC_PATH_TOO_LONG -12 #define DRFLAC_NAME_TOO_LONG -13 #define DRFLAC_NOT_DIRECTORY -14 #define DRFLAC_IS_DIRECTORY -15 #define DRFLAC_DIRECTORY_NOT_EMPTY -16 #define DRFLAC_END_OF_FILE -17 #define DRFLAC_NO_SPACE -18 #define DRFLAC_BUSY -19 #define DRFLAC_IO_ERROR -20 #define DRFLAC_INTERRUPT -21 #define DRFLAC_UNAVAILABLE -22 #define DRFLAC_ALREADY_IN_USE -23 #define DRFLAC_BAD_ADDRESS -24 #define DRFLAC_BAD_SEEK -25 #define DRFLAC_BAD_PIPE -26 #define DRFLAC_DEADLOCK -27 #define DRFLAC_TOO_MANY_LINKS -28 #define DRFLAC_NOT_IMPLEMENTED -29 #define DRFLAC_NO_MESSAGE -30 #define DRFLAC_BAD_MESSAGE -31 #define DRFLAC_NO_DATA_AVAILABLE -32 #define DRFLAC_INVALID_DATA -33 #define DRFLAC_TIMEOUT -34 #define DRFLAC_NO_NETWORK -35 #define DRFLAC_NOT_UNIQUE -36 #define DRFLAC_NOT_SOCKET -37 #define DRFLAC_NO_ADDRESS -38 #define DRFLAC_BAD_PROTOCOL -39 #define DRFLAC_PROTOCOL_UNAVAILABLE -40 #define DRFLAC_PROTOCOL_NOT_SUPPORTED -41 #define DRFLAC_PROTOCOL_FAMILY_NOT_SUPPORTED -42 #define DRFLAC_ADDRESS_FAMILY_NOT_SUPPORTED -43 #define DRFLAC_SOCKET_NOT_SUPPORTED -44 #define DRFLAC_CONNECTION_RESET -45 #define DRFLAC_ALREADY_CONNECTED -46 #define DRFLAC_NOT_CONNECTED -47 #define DRFLAC_CONNECTION_REFUSED -48 #define DRFLAC_NO_HOST -49 #define DRFLAC_IN_PROGRESS -50 #define DRFLAC_CANCELLED -51 #define DRFLAC_MEMORY_ALREADY_MAPPED -52 #define DRFLAC_AT_END -53 #define DRFLAC_CRC_MISMATCH -128 #define DRFLAC_SUBFRAME_CONSTANT 0 #define DRFLAC_SUBFRAME_VERBATIM 1 #define DRFLAC_SUBFRAME_FIXED 8 #define DRFLAC_SUBFRAME_LPC 32 #define DRFLAC_SUBFRAME_RESERVED 255 #define DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE 0 #define DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2 1 #define DRFLAC_CHANNEL_ASSIGNMENT_INDEPENDENT 0 #define DRFLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE 8 #define DRFLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE 9 #define DRFLAC_CHANNEL_ASSIGNMENT_MID_SIDE 10 #define DRFLAC_SEEKPOINT_SIZE_IN_BYTES 18 #define DRFLAC_CUESHEET_TRACK_SIZE_IN_BYTES 36 #define DRFLAC_CUESHEET_TRACK_INDEX_SIZE_IN_BYTES 12 #define drflac_align(x, a) ((((x) + (a) - 1) / (a)) * (a)) DRFLAC_API void drflac_version(drflac_uint32* pMajor, drflac_uint32* pMinor, drflac_uint32* pRevision) { if (pMajor) { *pMajor = DRFLAC_VERSION_MAJOR; } if (pMinor) { *pMinor = DRFLAC_VERSION_MINOR; } if (pRevision) { *pRevision = DRFLAC_VERSION_REVISION; } } DRFLAC_API const char* drflac_version_string(void) { return DRFLAC_VERSION_STRING; } #if defined(__has_feature) #if __has_feature(thread_sanitizer) #define DRFLAC_NO_THREAD_SANITIZE __attribute__((no_sanitize("thread"))) #else #define DRFLAC_NO_THREAD_SANITIZE #endif #else #define DRFLAC_NO_THREAD_SANITIZE #endif #if defined(DRFLAC_HAS_LZCNT_INTRINSIC) static drflac_bool32 drflac__gIsLZCNTSupported = DRFLAC_FALSE; #endif #ifndef DRFLAC_NO_CPUID static drflac_bool32 drflac__gIsSSE2Supported = DRFLAC_FALSE; static drflac_bool32 drflac__gIsSSE41Supported = DRFLAC_FALSE; DRFLAC_NO_THREAD_SANITIZE static void drflac__init_cpu_caps(void) { static drflac_bool32 isCPUCapsInitialized = DRFLAC_FALSE; if (!isCPUCapsInitialized) { #if defined(DRFLAC_HAS_LZCNT_INTRINSIC) int info[4] = {0}; drflac__cpuid(info, 0x80000001); drflac__gIsLZCNTSupported = (info[2] & (1 << 5)) != 0; #endif drflac__gIsSSE2Supported = drflac_has_sse2(); drflac__gIsSSE41Supported = drflac_has_sse41(); isCPUCapsInitialized = DRFLAC_TRUE; } } #else static drflac_bool32 drflac__gIsNEONSupported = DRFLAC_FALSE; static DRFLAC_INLINE drflac_bool32 drflac__has_neon(void) { #if defined(DRFLAC_SUPPORT_NEON) #if defined(DRFLAC_ARM) && !defined(DRFLAC_NO_NEON) #if (defined(__ARM_NEON) || defined(__aarch64__) || defined(_M_ARM64)) return DRFLAC_TRUE; #else return DRFLAC_FALSE; #endif #else return DRFLAC_FALSE; #endif #else return DRFLAC_FALSE; #endif } DRFLAC_NO_THREAD_SANITIZE static void drflac__init_cpu_caps(void) { drflac__gIsNEONSupported = drflac__has_neon(); #if defined(DRFLAC_HAS_LZCNT_INTRINSIC) && defined(DRFLAC_ARM) && (defined(__ARM_ARCH) && __ARM_ARCH >= 5) drflac__gIsLZCNTSupported = DRFLAC_TRUE; #endif } #endif static DRFLAC_INLINE drflac_bool32 drflac__is_little_endian(void) { #if defined(DRFLAC_X86) || defined(DRFLAC_X64) return DRFLAC_TRUE; #elif defined(__BYTE_ORDER) && defined(__LITTLE_ENDIAN) && __BYTE_ORDER == __LITTLE_ENDIAN return DRFLAC_TRUE; #else int n = 1; return (*(char*)&n) == 1; #endif } static DRFLAC_INLINE drflac_uint16 drflac__swap_endian_uint16(drflac_uint16 n) { #ifdef DRFLAC_HAS_BYTESWAP16_INTRINSIC #if defined(_MSC_VER) && !defined(__clang__) return _byteswap_ushort(n); #elif defined(__GNUC__) || defined(__clang__) return __builtin_bswap16(n); #elif defined(__WATCOMC__) && defined(__386__) return _watcom_bswap16(n); #else #error "This compiler does not support the byte swap intrinsic." #endif #else return ((n & 0xFF00) >> 8) | ((n & 0x00FF) << 8); #endif } static DRFLAC_INLINE drflac_uint32 drflac__swap_endian_uint32(drflac_uint32 n) { #ifdef DRFLAC_HAS_BYTESWAP32_INTRINSIC #if defined(_MSC_VER) && !defined(__clang__) return _byteswap_ulong(n); #elif defined(__GNUC__) || defined(__clang__) #if defined(DRFLAC_ARM) && (defined(__ARM_ARCH) && __ARM_ARCH >= 6) && !defined(DRFLAC_64BIT) drflac_uint32 r; __asm__ __volatile__ ( #if defined(DRFLAC_64BIT) "rev %w[out], %w[in]" : [out]"=r"(r) : [in]"r"(n) #else "rev %[out], %[in]" : [out]"=r"(r) : [in]"r"(n) #endif ); return r; #else return __builtin_bswap32(n); #endif #elif defined(__WATCOMC__) && defined(__386__) return _watcom_bswap32(n); #else #error "This compiler does not support the byte swap intrinsic." #endif #else return ((n & 0xFF000000) >> 24) | ((n & 0x00FF0000) >> 8) | ((n & 0x0000FF00) << 8) | ((n & 0x000000FF) << 24); #endif } static DRFLAC_INLINE drflac_uint64 drflac__swap_endian_uint64(drflac_uint64 n) { #ifdef DRFLAC_HAS_BYTESWAP64_INTRINSIC #if defined(_MSC_VER) && !defined(__clang__) return _byteswap_uint64(n); #elif defined(__GNUC__) || defined(__clang__) return __builtin_bswap64(n); #elif defined(__WATCOMC__) && defined(__386__) return _watcom_bswap64(n); #else #error "This compiler does not support the byte swap intrinsic." #endif #else return ((n & ((drflac_uint64)0xFF000000 << 32)) >> 56) | ((n & ((drflac_uint64)0x00FF0000 << 32)) >> 40) | ((n & ((drflac_uint64)0x0000FF00 << 32)) >> 24) | ((n & ((drflac_uint64)0x000000FF << 32)) >> 8) | ((n & ((drflac_uint64)0xFF000000 )) << 8) | ((n & ((drflac_uint64)0x00FF0000 )) << 24) | ((n & ((drflac_uint64)0x0000FF00 )) << 40) | ((n & ((drflac_uint64)0x000000FF )) << 56); #endif } static DRFLAC_INLINE drflac_uint16 drflac__be2host_16(drflac_uint16 n) { if (drflac__is_little_endian()) { return drflac__swap_endian_uint16(n); } return n; } static DRFLAC_INLINE drflac_uint32 drflac__be2host_32(drflac_uint32 n) { if (drflac__is_little_endian()) { return drflac__swap_endian_uint32(n); } return n; } static DRFLAC_INLINE drflac_uint32 drflac__be2host_32_ptr_unaligned(const void* pData) { const drflac_uint8* pNum = (drflac_uint8*)pData; return *(pNum) << 24 | *(pNum+1) << 16 | *(pNum+2) << 8 | *(pNum+3); } static DRFLAC_INLINE drflac_uint64 drflac__be2host_64(drflac_uint64 n) { if (drflac__is_little_endian()) { return drflac__swap_endian_uint64(n); } return n; } static DRFLAC_INLINE drflac_uint32 drflac__le2host_32(drflac_uint32 n) { if (!drflac__is_little_endian()) { return drflac__swap_endian_uint32(n); } return n; } static DRFLAC_INLINE drflac_uint32 drflac__le2host_32_ptr_unaligned(const void* pData) { const drflac_uint8* pNum = (drflac_uint8*)pData; return *pNum | *(pNum+1) << 8 | *(pNum+2) << 16 | *(pNum+3) << 24; } static DRFLAC_INLINE drflac_uint32 drflac__unsynchsafe_32(drflac_uint32 n) { drflac_uint32 result = 0; result |= (n & 0x7F000000) >> 3; result |= (n & 0x007F0000) >> 2; result |= (n & 0x00007F00) >> 1; result |= (n & 0x0000007F) >> 0; return result; } static drflac_uint8 drflac__crc8_table[] = { 0x00, 0x07, 0x0E, 0x09, 0x1C, 0x1B, 0x12, 0x15, 0x38, 0x3F, 0x36, 0x31, 0x24, 0x23, 0x2A, 0x2D, 0x70, 0x77, 0x7E, 0x79, 0x6C, 0x6B, 0x62, 0x65, 0x48, 0x4F, 0x46, 0x41, 0x54, 0x53, 0x5A, 0x5D, 0xE0, 0xE7, 0xEE, 0xE9, 0xFC, 0xFB, 0xF2, 0xF5, 0xD8, 0xDF, 0xD6, 0xD1, 0xC4, 0xC3, 0xCA, 0xCD, 0x90, 0x97, 0x9E, 0x99, 0x8C, 0x8B, 0x82, 0x85, 0xA8, 0xAF, 0xA6, 0xA1, 0xB4, 0xB3, 0xBA, 0xBD, 0xC7, 0xC0, 0xC9, 0xCE, 0xDB, 0xDC, 0xD5, 0xD2, 0xFF, 0xF8, 0xF1, 0xF6, 0xE3, 0xE4, 0xED, 0xEA, 0xB7, 0xB0, 0xB9, 0xBE, 0xAB, 0xAC, 0xA5, 0xA2, 0x8F, 0x88, 0x81, 0x86, 0x93, 0x94, 0x9D, 0x9A, 0x27, 0x20, 0x29, 0x2E, 0x3B, 0x3C, 0x35, 0x32, 0x1F, 0x18, 0x11, 0x16, 0x03, 0x04, 0x0D, 0x0A, 0x57, 0x50, 0x59, 0x5E, 0x4B, 0x4C, 0x45, 0x42, 0x6F, 0x68, 0x61, 0x66, 0x73, 0x74, 0x7D, 0x7A, 0x89, 0x8E, 0x87, 0x80, 0x95, 0x92, 0x9B, 0x9C, 0xB1, 0xB6, 0xBF, 0xB8, 0xAD, 0xAA, 0xA3, 0xA4, 0xF9, 0xFE, 0xF7, 0xF0, 0xE5, 0xE2, 0xEB, 0xEC, 0xC1, 0xC6, 0xCF, 0xC8, 0xDD, 0xDA, 0xD3, 0xD4, 0x69, 0x6E, 0x67, 0x60, 0x75, 0x72, 0x7B, 0x7C, 0x51, 0x56, 0x5F, 0x58, 0x4D, 0x4A, 0x43, 0x44, 0x19, 0x1E, 0x17, 0x10, 0x05, 0x02, 0x0B, 0x0C, 0x21, 0x26, 0x2F, 0x28, 0x3D, 0x3A, 0x33, 0x34, 0x4E, 0x49, 0x40, 0x47, 0x52, 0x55, 0x5C, 0x5B, 0x76, 0x71, 0x78, 0x7F, 0x6A, 0x6D, 0x64, 0x63, 0x3E, 0x39, 0x30, 0x37, 0x22, 0x25, 0x2C, 0x2B, 0x06, 0x01, 0x08, 0x0F, 0x1A, 0x1D, 0x14, 0x13, 0xAE, 0xA9, 0xA0, 0xA7, 0xB2, 0xB5, 0xBC, 0xBB, 0x96, 0x91, 0x98, 0x9F, 0x8A, 0x8D, 0x84, 0x83, 0xDE, 0xD9, 0xD0, 0xD7, 0xC2, 0xC5, 0xCC, 0xCB, 0xE6, 0xE1, 0xE8, 0xEF, 0xFA, 0xFD, 0xF4, 0xF3 }; static drflac_uint16 drflac__crc16_table[] = { 0x0000, 0x8005, 0x800F, 0x000A, 0x801B, 0x001E, 0x0014, 0x8011, 0x8033, 0x0036, 0x003C, 0x8039, 0x0028, 0x802D, 0x8027, 0x0022, 0x8063, 0x0066, 0x006C, 0x8069, 0x0078, 0x807D, 0x8077, 0x0072, 0x0050, 0x8055, 0x805F, 0x005A, 0x804B, 0x004E, 0x0044, 0x8041, 0x80C3, 0x00C6, 0x00CC, 0x80C9, 0x00D8, 0x80DD, 0x80D7, 0x00D2, 0x00F0, 0x80F5, 0x80FF, 0x00FA, 0x80EB, 0x00EE, 0x00E4, 0x80E1, 0x00A0, 0x80A5, 0x80AF, 0x00AA, 0x80BB, 0x00BE, 0x00B4, 0x80B1, 0x8093, 0x0096, 0x009C, 0x8099, 0x0088, 0x808D, 0x8087, 0x0082, 0x8183, 0x0186, 0x018C, 0x8189, 0x0198, 0x819D, 0x8197, 0x0192, 0x01B0, 0x81B5, 0x81BF, 0x01BA, 0x81AB, 0x01AE, 0x01A4, 0x81A1, 0x01E0, 0x81E5, 0x81EF, 0x01EA, 0x81FB, 0x01FE, 0x01F4, 0x81F1, 0x81D3, 0x01D6, 0x01DC, 0x81D9, 0x01C8, 0x81CD, 0x81C7, 0x01C2, 0x0140, 0x8145, 0x814F, 0x014A, 0x815B, 0x015E, 0x0154, 0x8151, 0x8173, 0x0176, 0x017C, 0x8179, 0x0168, 0x816D, 0x8167, 0x0162, 0x8123, 0x0126, 0x012C, 0x8129, 0x0138, 0x813D, 0x8137, 0x0132, 0x0110, 0x8115, 0x811F, 0x011A, 0x810B, 0x010E, 0x0104, 0x8101, 0x8303, 0x0306, 0x030C, 0x8309, 0x0318, 0x831D, 0x8317, 0x0312, 0x0330, 0x8335, 0x833F, 0x033A, 0x832B, 0x032E, 0x0324, 0x8321, 0x0360, 0x8365, 0x836F, 0x036A, 0x837B, 0x037E, 0x0374, 0x8371, 0x8353, 0x0356, 0x035C, 0x8359, 0x0348, 0x834D, 0x8347, 0x0342, 0x03C0, 0x83C5, 0x83CF, 0x03CA, 0x83DB, 0x03DE, 0x03D4, 0x83D1, 0x83F3, 0x03F6, 0x03FC, 0x83F9, 0x03E8, 0x83ED, 0x83E7, 0x03E2, 0x83A3, 0x03A6, 0x03AC, 0x83A9, 0x03B8, 0x83BD, 0x83B7, 0x03B2, 0x0390, 0x8395, 0x839F, 0x039A, 0x838B, 0x038E, 0x0384, 0x8381, 0x0280, 0x8285, 0x828F, 0x028A, 0x829B, 0x029E, 0x0294, 0x8291, 0x82B3, 0x02B6, 0x02BC, 0x82B9, 0x02A8, 0x82AD, 0x82A7, 0x02A2, 0x82E3, 0x02E6, 0x02EC, 0x82E9, 0x02F8, 0x82FD, 0x82F7, 0x02F2, 0x02D0, 0x82D5, 0x82DF, 0x02DA, 0x82CB, 0x02CE, 0x02C4, 0x82C1, 0x8243, 0x0246, 0x024C, 0x8249, 0x0258, 0x825D, 0x8257, 0x0252, 0x0270, 0x8275, 0x827F, 0x027A, 0x826B, 0x026E, 0x0264, 0x8261, 0x0220, 0x8225, 0x822F, 0x022A, 0x823B, 0x023E, 0x0234, 0x8231, 0x8213, 0x0216, 0x021C, 0x8219, 0x0208, 0x820D, 0x8207, 0x0202 }; static DRFLAC_INLINE drflac_uint8 drflac_crc8_byte(drflac_uint8 crc, drflac_uint8 data) { return drflac__crc8_table[crc ^ data]; } static DRFLAC_INLINE drflac_uint8 drflac_crc8(drflac_uint8 crc, drflac_uint32 data, drflac_uint32 count) { #ifdef DR_FLAC_NO_CRC (void)crc; (void)data; (void)count; return 0; #else #if 0 drflac_uint8 p = 0x07; for (int i = count-1; i >= 0; --i) { drflac_uint8 bit = (data & (1 << i)) >> i; if (crc & 0x80) { crc = ((crc << 1) | bit) ^ p; } else { crc = ((crc << 1) | bit); } } return crc; #else drflac_uint32 wholeBytes; drflac_uint32 leftoverBits; drflac_uint64 leftoverDataMask; static drflac_uint64 leftoverDataMaskTable[8] = { 0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F }; DRFLAC_ASSERT(count <= 32); wholeBytes = count >> 3; leftoverBits = count - (wholeBytes*8); leftoverDataMask = leftoverDataMaskTable[leftoverBits]; switch (wholeBytes) { case 4: crc = drflac_crc8_byte(crc, (drflac_uint8)((data & (0xFF000000UL << leftoverBits)) >> (24 + leftoverBits))); case 3: crc = drflac_crc8_byte(crc, (drflac_uint8)((data & (0x00FF0000UL << leftoverBits)) >> (16 + leftoverBits))); case 2: crc = drflac_crc8_byte(crc, (drflac_uint8)((data & (0x0000FF00UL << leftoverBits)) >> ( 8 + leftoverBits))); case 1: crc = drflac_crc8_byte(crc, (drflac_uint8)((data & (0x000000FFUL << leftoverBits)) >> ( 0 + leftoverBits))); case 0: if (leftoverBits > 0) crc = (drflac_uint8)((crc << leftoverBits) ^ drflac__crc8_table[(crc >> (8 - leftoverBits)) ^ (data & leftoverDataMask)]); } return crc; #endif #endif } static DRFLAC_INLINE drflac_uint16 drflac_crc16_byte(drflac_uint16 crc, drflac_uint8 data) { return (crc << 8) ^ drflac__crc16_table[(drflac_uint8)(crc >> 8) ^ data]; } static DRFLAC_INLINE drflac_uint16 drflac_crc16_cache(drflac_uint16 crc, drflac_cache_t data) { #ifdef DRFLAC_64BIT crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 56) & 0xFF)); crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 48) & 0xFF)); crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 40) & 0xFF)); crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 32) & 0xFF)); #endif crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 24) & 0xFF)); crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 16) & 0xFF)); crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 8) & 0xFF)); crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 0) & 0xFF)); return crc; } static DRFLAC_INLINE drflac_uint16 drflac_crc16_bytes(drflac_uint16 crc, drflac_cache_t data, drflac_uint32 byteCount) { switch (byteCount) { #ifdef DRFLAC_64BIT case 8: crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 56) & 0xFF)); case 7: crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 48) & 0xFF)); case 6: crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 40) & 0xFF)); case 5: crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 32) & 0xFF)); #endif case 4: crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 24) & 0xFF)); case 3: crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 16) & 0xFF)); case 2: crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 8) & 0xFF)); case 1: crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 0) & 0xFF)); } return crc; } #if 0 static DRFLAC_INLINE drflac_uint16 drflac_crc16__32bit(drflac_uint16 crc, drflac_uint32 data, drflac_uint32 count) { #ifdef DR_FLAC_NO_CRC (void)crc; (void)data; (void)count; return 0; #else #if 0 drflac_uint16 p = 0x8005; for (int i = count-1; i >= 0; --i) { drflac_uint16 bit = (data & (1ULL << i)) >> i; if (r & 0x8000) { r = ((r << 1) | bit) ^ p; } else { r = ((r << 1) | bit); } } return crc; #else drflac_uint32 wholeBytes; drflac_uint32 leftoverBits; drflac_uint64 leftoverDataMask; static drflac_uint64 leftoverDataMaskTable[8] = { 0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F }; DRFLAC_ASSERT(count <= 64); wholeBytes = count >> 3; leftoverBits = count & 7; leftoverDataMask = leftoverDataMaskTable[leftoverBits]; switch (wholeBytes) { default: case 4: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (0xFF000000UL << leftoverBits)) >> (24 + leftoverBits))); case 3: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (0x00FF0000UL << leftoverBits)) >> (16 + leftoverBits))); case 2: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (0x0000FF00UL << leftoverBits)) >> ( 8 + leftoverBits))); case 1: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (0x000000FFUL << leftoverBits)) >> ( 0 + leftoverBits))); case 0: if (leftoverBits > 0) crc = (crc << leftoverBits) ^ drflac__crc16_table[(crc >> (16 - leftoverBits)) ^ (data & leftoverDataMask)]; } return crc; #endif #endif } static DRFLAC_INLINE drflac_uint16 drflac_crc16__64bit(drflac_uint16 crc, drflac_uint64 data, drflac_uint32 count) { #ifdef DR_FLAC_NO_CRC (void)crc; (void)data; (void)count; return 0; #else drflac_uint32 wholeBytes; drflac_uint32 leftoverBits; drflac_uint64 leftoverDataMask; static drflac_uint64 leftoverDataMaskTable[8] = { 0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F }; DRFLAC_ASSERT(count <= 64); wholeBytes = count >> 3; leftoverBits = count & 7; leftoverDataMask = leftoverDataMaskTable[leftoverBits]; switch (wholeBytes) { default: case 8: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (((drflac_uint64)0xFF000000 << 32) << leftoverBits)) >> (56 + leftoverBits))); case 7: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (((drflac_uint64)0x00FF0000 << 32) << leftoverBits)) >> (48 + leftoverBits))); case 6: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (((drflac_uint64)0x0000FF00 << 32) << leftoverBits)) >> (40 + leftoverBits))); case 5: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (((drflac_uint64)0x000000FF << 32) << leftoverBits)) >> (32 + leftoverBits))); case 4: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (((drflac_uint64)0xFF000000 ) << leftoverBits)) >> (24 + leftoverBits))); case 3: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (((drflac_uint64)0x00FF0000 ) << leftoverBits)) >> (16 + leftoverBits))); case 2: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (((drflac_uint64)0x0000FF00 ) << leftoverBits)) >> ( 8 + leftoverBits))); case 1: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (((drflac_uint64)0x000000FF ) << leftoverBits)) >> ( 0 + leftoverBits))); case 0: if (leftoverBits > 0) crc = (crc << leftoverBits) ^ drflac__crc16_table[(crc >> (16 - leftoverBits)) ^ (data & leftoverDataMask)]; } return crc; #endif } static DRFLAC_INLINE drflac_uint16 drflac_crc16(drflac_uint16 crc, drflac_cache_t data, drflac_uint32 count) { #ifdef DRFLAC_64BIT return drflac_crc16__64bit(crc, data, count); #else return drflac_crc16__32bit(crc, data, count); #endif } #endif #ifdef DRFLAC_64BIT #define drflac__be2host__cache_line drflac__be2host_64 #else #define drflac__be2host__cache_line drflac__be2host_32 #endif #define DRFLAC_CACHE_L1_SIZE_BYTES(bs) (sizeof((bs)->cache)) #define DRFLAC_CACHE_L1_SIZE_BITS(bs) (sizeof((bs)->cache)*8) #define DRFLAC_CACHE_L1_BITS_REMAINING(bs) (DRFLAC_CACHE_L1_SIZE_BITS(bs) - (bs)->consumedBits) #define DRFLAC_CACHE_L1_SELECTION_MASK(_bitCount) (~((~(drflac_cache_t)0) >> (_bitCount))) #define DRFLAC_CACHE_L1_SELECTION_SHIFT(bs, _bitCount) (DRFLAC_CACHE_L1_SIZE_BITS(bs) - (_bitCount)) #define DRFLAC_CACHE_L1_SELECT(bs, _bitCount) (((bs)->cache) & DRFLAC_CACHE_L1_SELECTION_MASK(_bitCount)) #define DRFLAC_CACHE_L1_SELECT_AND_SHIFT(bs, _bitCount) (DRFLAC_CACHE_L1_SELECT((bs), (_bitCount)) >> DRFLAC_CACHE_L1_SELECTION_SHIFT((bs), (_bitCount))) #define DRFLAC_CACHE_L1_SELECT_AND_SHIFT_SAFE(bs, _bitCount)(DRFLAC_CACHE_L1_SELECT((bs), (_bitCount)) >> (DRFLAC_CACHE_L1_SELECTION_SHIFT((bs), (_bitCount)) & (DRFLAC_CACHE_L1_SIZE_BITS(bs)-1))) #define DRFLAC_CACHE_L2_SIZE_BYTES(bs) (sizeof((bs)->cacheL2)) #define DRFLAC_CACHE_L2_LINE_COUNT(bs) (DRFLAC_CACHE_L2_SIZE_BYTES(bs) / sizeof((bs)->cacheL2[0])) #define DRFLAC_CACHE_L2_LINES_REMAINING(bs) (DRFLAC_CACHE_L2_LINE_COUNT(bs) - (bs)->nextL2Line) #ifndef DR_FLAC_NO_CRC static DRFLAC_INLINE void drflac__reset_crc16(drflac_bs* bs) { bs->crc16 = 0; bs->crc16CacheIgnoredBytes = bs->consumedBits >> 3; } static DRFLAC_INLINE void drflac__update_crc16(drflac_bs* bs) { if (bs->crc16CacheIgnoredBytes == 0) { bs->crc16 = drflac_crc16_cache(bs->crc16, bs->crc16Cache); } else { bs->crc16 = drflac_crc16_bytes(bs->crc16, bs->crc16Cache, DRFLAC_CACHE_L1_SIZE_BYTES(bs) - bs->crc16CacheIgnoredBytes); bs->crc16CacheIgnoredBytes = 0; } } static DRFLAC_INLINE drflac_uint16 drflac__flush_crc16(drflac_bs* bs) { DRFLAC_ASSERT((DRFLAC_CACHE_L1_BITS_REMAINING(bs) & 7) == 0); if (DRFLAC_CACHE_L1_BITS_REMAINING(bs) == 0) { drflac__update_crc16(bs); } else { bs->crc16 = drflac_crc16_bytes(bs->crc16, bs->crc16Cache >> DRFLAC_CACHE_L1_BITS_REMAINING(bs), (bs->consumedBits >> 3) - bs->crc16CacheIgnoredBytes); bs->crc16CacheIgnoredBytes = bs->consumedBits >> 3; } return bs->crc16; } #endif static DRFLAC_INLINE drflac_bool32 drflac__reload_l1_cache_from_l2(drflac_bs* bs) { size_t bytesRead; size_t alignedL1LineCount; if (bs->nextL2Line < DRFLAC_CACHE_L2_LINE_COUNT(bs)) { bs->cache = bs->cacheL2[bs->nextL2Line++]; return DRFLAC_TRUE; } if (bs->unalignedByteCount > 0) { return DRFLAC_FALSE; } bytesRead = bs->onRead(bs->pUserData, bs->cacheL2, DRFLAC_CACHE_L2_SIZE_BYTES(bs)); bs->nextL2Line = 0; if (bytesRead == DRFLAC_CACHE_L2_SIZE_BYTES(bs)) { bs->cache = bs->cacheL2[bs->nextL2Line++]; return DRFLAC_TRUE; } alignedL1LineCount = bytesRead / DRFLAC_CACHE_L1_SIZE_BYTES(bs); bs->unalignedByteCount = bytesRead - (alignedL1LineCount * DRFLAC_CACHE_L1_SIZE_BYTES(bs)); if (bs->unalignedByteCount > 0) { bs->unalignedCache = bs->cacheL2[alignedL1LineCount]; } if (alignedL1LineCount > 0) { size_t offset = DRFLAC_CACHE_L2_LINE_COUNT(bs) - alignedL1LineCount; size_t i; for (i = alignedL1LineCount; i > 0; --i) { bs->cacheL2[i-1 + offset] = bs->cacheL2[i-1]; } bs->nextL2Line = (drflac_uint32)offset; bs->cache = bs->cacheL2[bs->nextL2Line++]; return DRFLAC_TRUE; } else { bs->nextL2Line = DRFLAC_CACHE_L2_LINE_COUNT(bs); return DRFLAC_FALSE; } } static drflac_bool32 drflac__reload_cache(drflac_bs* bs) { size_t bytesRead; #ifndef DR_FLAC_NO_CRC drflac__update_crc16(bs); #endif if (drflac__reload_l1_cache_from_l2(bs)) { bs->cache = drflac__be2host__cache_line(bs->cache); bs->consumedBits = 0; #ifndef DR_FLAC_NO_CRC bs->crc16Cache = bs->cache; #endif return DRFLAC_TRUE; } bytesRead = bs->unalignedByteCount; if (bytesRead == 0) { bs->consumedBits = DRFLAC_CACHE_L1_SIZE_BITS(bs); return DRFLAC_FALSE; } DRFLAC_ASSERT(bytesRead < DRFLAC_CACHE_L1_SIZE_BYTES(bs)); bs->consumedBits = (drflac_uint32)(DRFLAC_CACHE_L1_SIZE_BYTES(bs) - bytesRead) * 8; bs->cache = drflac__be2host__cache_line(bs->unalignedCache); bs->cache &= DRFLAC_CACHE_L1_SELECTION_MASK(DRFLAC_CACHE_L1_BITS_REMAINING(bs)); bs->unalignedByteCount = 0; #ifndef DR_FLAC_NO_CRC bs->crc16Cache = bs->cache >> bs->consumedBits; bs->crc16CacheIgnoredBytes = bs->consumedBits >> 3; #endif return DRFLAC_TRUE; } static void drflac__reset_cache(drflac_bs* bs) { bs->nextL2Line = DRFLAC_CACHE_L2_LINE_COUNT(bs); bs->consumedBits = DRFLAC_CACHE_L1_SIZE_BITS(bs); bs->cache = 0; bs->unalignedByteCount = 0; bs->unalignedCache = 0; #ifndef DR_FLAC_NO_CRC bs->crc16Cache = 0; bs->crc16CacheIgnoredBytes = 0; #endif } static DRFLAC_INLINE drflac_bool32 drflac__read_uint32(drflac_bs* bs, unsigned int bitCount, drflac_uint32* pResultOut) { DRFLAC_ASSERT(bs != NULL); DRFLAC_ASSERT(pResultOut != NULL); DRFLAC_ASSERT(bitCount > 0); DRFLAC_ASSERT(bitCount <= 32); if (bs->consumedBits == DRFLAC_CACHE_L1_SIZE_BITS(bs)) { if (!drflac__reload_cache(bs)) { return DRFLAC_FALSE; } } if (bitCount <= DRFLAC_CACHE_L1_BITS_REMAINING(bs)) { #ifdef DRFLAC_64BIT *pResultOut = (drflac_uint32)DRFLAC_CACHE_L1_SELECT_AND_SHIFT(bs, bitCount); bs->consumedBits += bitCount; bs->cache <<= bitCount; #else if (bitCount < DRFLAC_CACHE_L1_SIZE_BITS(bs)) { *pResultOut = (drflac_uint32)DRFLAC_CACHE_L1_SELECT_AND_SHIFT(bs, bitCount); bs->consumedBits += bitCount; bs->cache <<= bitCount; } else { *pResultOut = (drflac_uint32)bs->cache; bs->consumedBits = DRFLAC_CACHE_L1_SIZE_BITS(bs); bs->cache = 0; } #endif return DRFLAC_TRUE; } else { drflac_uint32 bitCountHi = DRFLAC_CACHE_L1_BITS_REMAINING(bs); drflac_uint32 bitCountLo = bitCount - bitCountHi; drflac_uint32 resultHi; DRFLAC_ASSERT(bitCountHi > 0); DRFLAC_ASSERT(bitCountHi < 32); resultHi = (drflac_uint32)DRFLAC_CACHE_L1_SELECT_AND_SHIFT(bs, bitCountHi); if (!drflac__reload_cache(bs)) { return DRFLAC_FALSE; } if (bitCountLo > DRFLAC_CACHE_L1_BITS_REMAINING(bs)) { return DRFLAC_FALSE; } *pResultOut = (resultHi << bitCountLo) | (drflac_uint32)DRFLAC_CACHE_L1_SELECT_AND_SHIFT(bs, bitCountLo); bs->consumedBits += bitCountLo; bs->cache <<= bitCountLo; return DRFLAC_TRUE; } } static drflac_bool32 drflac__read_int32(drflac_bs* bs, unsigned int bitCount, drflac_int32* pResult) { drflac_uint32 result; DRFLAC_ASSERT(bs != NULL); DRFLAC_ASSERT(pResult != NULL); DRFLAC_ASSERT(bitCount > 0); DRFLAC_ASSERT(bitCount <= 32); if (!drflac__read_uint32(bs, bitCount, &result)) { return DRFLAC_FALSE; } if (bitCount < 32) { drflac_uint32 signbit; signbit = ((result >> (bitCount-1)) & 0x01); result |= (~signbit + 1) << bitCount; } *pResult = (drflac_int32)result; return DRFLAC_TRUE; } #ifdef DRFLAC_64BIT static drflac_bool32 drflac__read_uint64(drflac_bs* bs, unsigned int bitCount, drflac_uint64* pResultOut) { drflac_uint32 resultHi; drflac_uint32 resultLo; DRFLAC_ASSERT(bitCount <= 64); DRFLAC_ASSERT(bitCount > 32); if (!drflac__read_uint32(bs, bitCount - 32, &resultHi)) { return DRFLAC_FALSE; } if (!drflac__read_uint32(bs, 32, &resultLo)) { return DRFLAC_FALSE; } *pResultOut = (((drflac_uint64)resultHi) << 32) | ((drflac_uint64)resultLo); return DRFLAC_TRUE; } #endif #if 0 static drflac_bool32 drflac__read_int64(drflac_bs* bs, unsigned int bitCount, drflac_int64* pResultOut) { drflac_uint64 result; drflac_uint64 signbit; DRFLAC_ASSERT(bitCount <= 64); if (!drflac__read_uint64(bs, bitCount, &result)) { return DRFLAC_FALSE; } signbit = ((result >> (bitCount-1)) & 0x01); result |= (~signbit + 1) << bitCount; *pResultOut = (drflac_int64)result; return DRFLAC_TRUE; } #endif static drflac_bool32 drflac__read_uint16(drflac_bs* bs, unsigned int bitCount, drflac_uint16* pResult) { drflac_uint32 result; DRFLAC_ASSERT(bs != NULL); DRFLAC_ASSERT(pResult != NULL); DRFLAC_ASSERT(bitCount > 0); DRFLAC_ASSERT(bitCount <= 16); if (!drflac__read_uint32(bs, bitCount, &result)) { return DRFLAC_FALSE; } *pResult = (drflac_uint16)result; return DRFLAC_TRUE; } #if 0 static drflac_bool32 drflac__read_int16(drflac_bs* bs, unsigned int bitCount, drflac_int16* pResult) { drflac_int32 result; DRFLAC_ASSERT(bs != NULL); DRFLAC_ASSERT(pResult != NULL); DRFLAC_ASSERT(bitCount > 0); DRFLAC_ASSERT(bitCount <= 16); if (!drflac__read_int32(bs, bitCount, &result)) { return DRFLAC_FALSE; } *pResult = (drflac_int16)result; return DRFLAC_TRUE; } #endif static drflac_bool32 drflac__read_uint8(drflac_bs* bs, unsigned int bitCount, drflac_uint8* pResult) { drflac_uint32 result; DRFLAC_ASSERT(bs != NULL); DRFLAC_ASSERT(pResult != NULL); DRFLAC_ASSERT(bitCount > 0); DRFLAC_ASSERT(bitCount <= 8); if (!drflac__read_uint32(bs, bitCount, &result)) { return DRFLAC_FALSE; } *pResult = (drflac_uint8)result; return DRFLAC_TRUE; } static drflac_bool32 drflac__read_int8(drflac_bs* bs, unsigned int bitCount, drflac_int8* pResult) { drflac_int32 result; DRFLAC_ASSERT(bs != NULL); DRFLAC_ASSERT(pResult != NULL); DRFLAC_ASSERT(bitCount > 0); DRFLAC_ASSERT(bitCount <= 8); if (!drflac__read_int32(bs, bitCount, &result)) { return DRFLAC_FALSE; } *pResult = (drflac_int8)result; return DRFLAC_TRUE; } static drflac_bool32 drflac__seek_bits(drflac_bs* bs, size_t bitsToSeek) { if (bitsToSeek <= DRFLAC_CACHE_L1_BITS_REMAINING(bs)) { bs->consumedBits += (drflac_uint32)bitsToSeek; bs->cache <<= bitsToSeek; return DRFLAC_TRUE; } else { bitsToSeek -= DRFLAC_CACHE_L1_BITS_REMAINING(bs); bs->consumedBits += DRFLAC_CACHE_L1_BITS_REMAINING(bs); bs->cache = 0; #ifdef DRFLAC_64BIT while (bitsToSeek >= DRFLAC_CACHE_L1_SIZE_BITS(bs)) { drflac_uint64 bin; if (!drflac__read_uint64(bs, DRFLAC_CACHE_L1_SIZE_BITS(bs), &bin)) { return DRFLAC_FALSE; } bitsToSeek -= DRFLAC_CACHE_L1_SIZE_BITS(bs); } #else while (bitsToSeek >= DRFLAC_CACHE_L1_SIZE_BITS(bs)) { drflac_uint32 bin; if (!drflac__read_uint32(bs, DRFLAC_CACHE_L1_SIZE_BITS(bs), &bin)) { return DRFLAC_FALSE; } bitsToSeek -= DRFLAC_CACHE_L1_SIZE_BITS(bs); } #endif while (bitsToSeek >= 8) { drflac_uint8 bin; if (!drflac__read_uint8(bs, 8, &bin)) { return DRFLAC_FALSE; } bitsToSeek -= 8; } if (bitsToSeek > 0) { drflac_uint8 bin; if (!drflac__read_uint8(bs, (drflac_uint32)bitsToSeek, &bin)) { return DRFLAC_FALSE; } bitsToSeek = 0; } DRFLAC_ASSERT(bitsToSeek == 0); return DRFLAC_TRUE; } } static drflac_bool32 drflac__find_and_seek_to_next_sync_code(drflac_bs* bs) { DRFLAC_ASSERT(bs != NULL); if (!drflac__seek_bits(bs, DRFLAC_CACHE_L1_BITS_REMAINING(bs) & 7)) { return DRFLAC_FALSE; } for (;;) { drflac_uint8 hi; #ifndef DR_FLAC_NO_CRC drflac__reset_crc16(bs); #endif if (!drflac__read_uint8(bs, 8, &hi)) { return DRFLAC_FALSE; } if (hi == 0xFF) { drflac_uint8 lo; if (!drflac__read_uint8(bs, 6, &lo)) { return DRFLAC_FALSE; } if (lo == 0x3E) { return DRFLAC_TRUE; } else { if (!drflac__seek_bits(bs, DRFLAC_CACHE_L1_BITS_REMAINING(bs) & 7)) { return DRFLAC_FALSE; } } } } } #if defined(DRFLAC_HAS_LZCNT_INTRINSIC) #define DRFLAC_IMPLEMENT_CLZ_LZCNT #endif #if defined(_MSC_VER) && _MSC_VER >= 1400 && (defined(DRFLAC_X64) || defined(DRFLAC_X86)) && !defined(__clang__) #define DRFLAC_IMPLEMENT_CLZ_MSVC #endif #if defined(__WATCOMC__) && defined(__386__) #define DRFLAC_IMPLEMENT_CLZ_WATCOM #endif #ifdef __MRC__ #include #define DRFLAC_IMPLEMENT_CLZ_MRC #endif static DRFLAC_INLINE drflac_uint32 drflac__clz_software(drflac_cache_t x) { drflac_uint32 n; static drflac_uint32 clz_table_4[] = { 0, 4, 3, 3, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1 }; if (x == 0) { return sizeof(x)*8; } n = clz_table_4[x >> (sizeof(x)*8 - 4)]; if (n == 0) { #ifdef DRFLAC_64BIT if ((x & ((drflac_uint64)0xFFFFFFFF << 32)) == 0) { n = 32; x <<= 32; } if ((x & ((drflac_uint64)0xFFFF0000 << 32)) == 0) { n += 16; x <<= 16; } if ((x & ((drflac_uint64)0xFF000000 << 32)) == 0) { n += 8; x <<= 8; } if ((x & ((drflac_uint64)0xF0000000 << 32)) == 0) { n += 4; x <<= 4; } #else if ((x & 0xFFFF0000) == 0) { n = 16; x <<= 16; } if ((x & 0xFF000000) == 0) { n += 8; x <<= 8; } if ((x & 0xF0000000) == 0) { n += 4; x <<= 4; } #endif n += clz_table_4[x >> (sizeof(x)*8 - 4)]; } return n - 1; } #ifdef DRFLAC_IMPLEMENT_CLZ_LZCNT static DRFLAC_INLINE drflac_bool32 drflac__is_lzcnt_supported(void) { #if defined(DRFLAC_HAS_LZCNT_INTRINSIC) && defined(DRFLAC_ARM) && (defined(__ARM_ARCH) && __ARM_ARCH >= 5) return DRFLAC_TRUE; #elif defined(__MRC__) return DRFLAC_TRUE; #else #ifdef DRFLAC_HAS_LZCNT_INTRINSIC return drflac__gIsLZCNTSupported; #else return DRFLAC_FALSE; #endif #endif } static DRFLAC_INLINE drflac_uint32 drflac__clz_lzcnt(drflac_cache_t x) { #if defined(_MSC_VER) #ifdef DRFLAC_64BIT return (drflac_uint32)__lzcnt64(x); #else return (drflac_uint32)__lzcnt(x); #endif #else #if defined(__GNUC__) || defined(__clang__) #if defined(DRFLAC_X64) { drflac_uint64 r; __asm__ __volatile__ ( "lzcnt{ %1, %0| %0, %1}" : "=r"(r) : "r"(x) : "cc" ); return (drflac_uint32)r; } #elif defined(DRFLAC_X86) { drflac_uint32 r; __asm__ __volatile__ ( "lzcnt{l %1, %0| %0, %1}" : "=r"(r) : "r"(x) : "cc" ); return r; } #elif defined(DRFLAC_ARM) && (defined(__ARM_ARCH) && __ARM_ARCH >= 5) && !defined(DRFLAC_64BIT) { unsigned int r; __asm__ __volatile__ ( #if defined(DRFLAC_64BIT) "clz %w[out], %w[in]" : [out]"=r"(r) : [in]"r"(x) #else "clz %[out], %[in]" : [out]"=r"(r) : [in]"r"(x) #endif ); return r; } #else if (x == 0) { return sizeof(x)*8; } #ifdef DRFLAC_64BIT return (drflac_uint32)__builtin_clzll((drflac_uint64)x); #else return (drflac_uint32)__builtin_clzl((drflac_uint32)x); #endif #endif #else #error "This compiler does not support the lzcnt intrinsic." #endif #endif } #endif #ifdef DRFLAC_IMPLEMENT_CLZ_MSVC #include static DRFLAC_INLINE drflac_uint32 drflac__clz_msvc(drflac_cache_t x) { drflac_uint32 n; if (x == 0) { return sizeof(x)*8; } #ifdef DRFLAC_64BIT _BitScanReverse64((unsigned long*)&n, x); #else _BitScanReverse((unsigned long*)&n, x); #endif return sizeof(x)*8 - n - 1; } #endif #ifdef DRFLAC_IMPLEMENT_CLZ_WATCOM static __inline drflac_uint32 drflac__clz_watcom (drflac_uint32); #ifdef DRFLAC_IMPLEMENT_CLZ_WATCOM_LZCNT #pragma aux drflac__clz_watcom_lzcnt = \ "db 0F3h, 0Fh, 0BDh, 0C0h" \ parm [eax] \ value [eax] \ modify nomemory; #else #pragma aux drflac__clz_watcom = \ "bsr eax, eax" \ "xor eax, 31" \ parm [eax] nomemory \ value [eax] \ modify exact [eax] nomemory; #endif #endif static DRFLAC_INLINE drflac_uint32 drflac__clz(drflac_cache_t x) { #ifdef DRFLAC_IMPLEMENT_CLZ_LZCNT if (drflac__is_lzcnt_supported()) { return drflac__clz_lzcnt(x); } else #endif { #ifdef DRFLAC_IMPLEMENT_CLZ_MSVC return drflac__clz_msvc(x); #elif defined(DRFLAC_IMPLEMENT_CLZ_WATCOM_LZCNT) return drflac__clz_watcom_lzcnt(x); #elif defined(DRFLAC_IMPLEMENT_CLZ_WATCOM) return (x == 0) ? sizeof(x)*8 : drflac__clz_watcom(x); #elif defined(__MRC__) return __cntlzw(x); #else return drflac__clz_software(x); #endif } } static DRFLAC_INLINE drflac_bool32 drflac__seek_past_next_set_bit(drflac_bs* bs, unsigned int* pOffsetOut) { drflac_uint32 zeroCounter = 0; drflac_uint32 setBitOffsetPlus1; while (bs->cache == 0) { zeroCounter += (drflac_uint32)DRFLAC_CACHE_L1_BITS_REMAINING(bs); if (!drflac__reload_cache(bs)) { return DRFLAC_FALSE; } } if (bs->cache == 1) { *pOffsetOut = zeroCounter + (drflac_uint32)DRFLAC_CACHE_L1_BITS_REMAINING(bs) - 1; if (!drflac__reload_cache(bs)) { return DRFLAC_FALSE; } return DRFLAC_TRUE; } setBitOffsetPlus1 = drflac__clz(bs->cache); setBitOffsetPlus1 += 1; if (setBitOffsetPlus1 > DRFLAC_CACHE_L1_BITS_REMAINING(bs)) { return DRFLAC_FALSE; } bs->consumedBits += setBitOffsetPlus1; bs->cache <<= setBitOffsetPlus1; *pOffsetOut = zeroCounter + setBitOffsetPlus1 - 1; return DRFLAC_TRUE; } static drflac_bool32 drflac__seek_to_byte(drflac_bs* bs, drflac_uint64 offsetFromStart) { DRFLAC_ASSERT(bs != NULL); DRFLAC_ASSERT(offsetFromStart > 0); if (offsetFromStart > 0x7FFFFFFF) { drflac_uint64 bytesRemaining = offsetFromStart; if (!bs->onSeek(bs->pUserData, 0x7FFFFFFF, drflac_seek_origin_start)) { return DRFLAC_FALSE; } bytesRemaining -= 0x7FFFFFFF; while (bytesRemaining > 0x7FFFFFFF) { if (!bs->onSeek(bs->pUserData, 0x7FFFFFFF, drflac_seek_origin_current)) { return DRFLAC_FALSE; } bytesRemaining -= 0x7FFFFFFF; } if (bytesRemaining > 0) { if (!bs->onSeek(bs->pUserData, (int)bytesRemaining, drflac_seek_origin_current)) { return DRFLAC_FALSE; } } } else { if (!bs->onSeek(bs->pUserData, (int)offsetFromStart, drflac_seek_origin_start)) { return DRFLAC_FALSE; } } drflac__reset_cache(bs); return DRFLAC_TRUE; } static drflac_result drflac__read_utf8_coded_number(drflac_bs* bs, drflac_uint64* pNumberOut, drflac_uint8* pCRCOut) { drflac_uint8 crc; drflac_uint64 result; drflac_uint8 utf8[7] = {0}; int byteCount; int i; DRFLAC_ASSERT(bs != NULL); DRFLAC_ASSERT(pNumberOut != NULL); DRFLAC_ASSERT(pCRCOut != NULL); crc = *pCRCOut; if (!drflac__read_uint8(bs, 8, utf8)) { *pNumberOut = 0; return DRFLAC_AT_END; } crc = drflac_crc8(crc, utf8[0], 8); if ((utf8[0] & 0x80) == 0) { *pNumberOut = utf8[0]; *pCRCOut = crc; return DRFLAC_SUCCESS; } if ((utf8[0] & 0xE0) == 0xC0) { byteCount = 2; } else if ((utf8[0] & 0xF0) == 0xE0) { byteCount = 3; } else if ((utf8[0] & 0xF8) == 0xF0) { byteCount = 4; } else if ((utf8[0] & 0xFC) == 0xF8) { byteCount = 5; } else if ((utf8[0] & 0xFE) == 0xFC) { byteCount = 6; } else if ((utf8[0] & 0xFF) == 0xFE) { byteCount = 7; } else { *pNumberOut = 0; return DRFLAC_CRC_MISMATCH; } DRFLAC_ASSERT(byteCount > 1); result = (drflac_uint64)(utf8[0] & (0xFF >> (byteCount + 1))); for (i = 1; i < byteCount; ++i) { if (!drflac__read_uint8(bs, 8, utf8 + i)) { *pNumberOut = 0; return DRFLAC_AT_END; } crc = drflac_crc8(crc, utf8[i], 8); result = (result << 6) | (utf8[i] & 0x3F); } *pNumberOut = result; *pCRCOut = crc; return DRFLAC_SUCCESS; } static DRFLAC_INLINE drflac_uint32 drflac__ilog2_u32(drflac_uint32 x) { #if 1 drflac_uint32 result = 0; while (x > 0) { result += 1; x >>= 1; } return result; #endif } static DRFLAC_INLINE drflac_bool32 drflac__use_64_bit_prediction(drflac_uint32 bitsPerSample, drflac_uint32 order, drflac_uint32 precision) { return bitsPerSample + precision + drflac__ilog2_u32(order) > 32; } #if defined(__clang__) __attribute__((no_sanitize("signed-integer-overflow"))) #endif static DRFLAC_INLINE drflac_int32 drflac__calculate_prediction_32(drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, drflac_int32* pDecodedSamples) { drflac_int32 prediction = 0; DRFLAC_ASSERT(order <= 32); switch (order) { case 32: prediction += coefficients[31] * pDecodedSamples[-32]; case 31: prediction += coefficients[30] * pDecodedSamples[-31]; case 30: prediction += coefficients[29] * pDecodedSamples[-30]; case 29: prediction += coefficients[28] * pDecodedSamples[-29]; case 28: prediction += coefficients[27] * pDecodedSamples[-28]; case 27: prediction += coefficients[26] * pDecodedSamples[-27]; case 26: prediction += coefficients[25] * pDecodedSamples[-26]; case 25: prediction += coefficients[24] * pDecodedSamples[-25]; case 24: prediction += coefficients[23] * pDecodedSamples[-24]; case 23: prediction += coefficients[22] * pDecodedSamples[-23]; case 22: prediction += coefficients[21] * pDecodedSamples[-22]; case 21: prediction += coefficients[20] * pDecodedSamples[-21]; case 20: prediction += coefficients[19] * pDecodedSamples[-20]; case 19: prediction += coefficients[18] * pDecodedSamples[-19]; case 18: prediction += coefficients[17] * pDecodedSamples[-18]; case 17: prediction += coefficients[16] * pDecodedSamples[-17]; case 16: prediction += coefficients[15] * pDecodedSamples[-16]; case 15: prediction += coefficients[14] * pDecodedSamples[-15]; case 14: prediction += coefficients[13] * pDecodedSamples[-14]; case 13: prediction += coefficients[12] * pDecodedSamples[-13]; case 12: prediction += coefficients[11] * pDecodedSamples[-12]; case 11: prediction += coefficients[10] * pDecodedSamples[-11]; case 10: prediction += coefficients[ 9] * pDecodedSamples[-10]; case 9: prediction += coefficients[ 8] * pDecodedSamples[- 9]; case 8: prediction += coefficients[ 7] * pDecodedSamples[- 8]; case 7: prediction += coefficients[ 6] * pDecodedSamples[- 7]; case 6: prediction += coefficients[ 5] * pDecodedSamples[- 6]; case 5: prediction += coefficients[ 4] * pDecodedSamples[- 5]; case 4: prediction += coefficients[ 3] * pDecodedSamples[- 4]; case 3: prediction += coefficients[ 2] * pDecodedSamples[- 3]; case 2: prediction += coefficients[ 1] * pDecodedSamples[- 2]; case 1: prediction += coefficients[ 0] * pDecodedSamples[- 1]; } return (drflac_int32)(prediction >> shift); } static DRFLAC_INLINE drflac_int32 drflac__calculate_prediction_64(drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, drflac_int32* pDecodedSamples) { drflac_int64 prediction; DRFLAC_ASSERT(order <= 32); #ifndef DRFLAC_64BIT if (order == 8) { prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1]; prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2]; prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3]; prediction += coefficients[3] * (drflac_int64)pDecodedSamples[-4]; prediction += coefficients[4] * (drflac_int64)pDecodedSamples[-5]; prediction += coefficients[5] * (drflac_int64)pDecodedSamples[-6]; prediction += coefficients[6] * (drflac_int64)pDecodedSamples[-7]; prediction += coefficients[7] * (drflac_int64)pDecodedSamples[-8]; } else if (order == 7) { prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1]; prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2]; prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3]; prediction += coefficients[3] * (drflac_int64)pDecodedSamples[-4]; prediction += coefficients[4] * (drflac_int64)pDecodedSamples[-5]; prediction += coefficients[5] * (drflac_int64)pDecodedSamples[-6]; prediction += coefficients[6] * (drflac_int64)pDecodedSamples[-7]; } else if (order == 3) { prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1]; prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2]; prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3]; } else if (order == 6) { prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1]; prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2]; prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3]; prediction += coefficients[3] * (drflac_int64)pDecodedSamples[-4]; prediction += coefficients[4] * (drflac_int64)pDecodedSamples[-5]; prediction += coefficients[5] * (drflac_int64)pDecodedSamples[-6]; } else if (order == 5) { prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1]; prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2]; prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3]; prediction += coefficients[3] * (drflac_int64)pDecodedSamples[-4]; prediction += coefficients[4] * (drflac_int64)pDecodedSamples[-5]; } else if (order == 4) { prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1]; prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2]; prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3]; prediction += coefficients[3] * (drflac_int64)pDecodedSamples[-4]; } else if (order == 12) { prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1]; prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2]; prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3]; prediction += coefficients[3] * (drflac_int64)pDecodedSamples[-4]; prediction += coefficients[4] * (drflac_int64)pDecodedSamples[-5]; prediction += coefficients[5] * (drflac_int64)pDecodedSamples[-6]; prediction += coefficients[6] * (drflac_int64)pDecodedSamples[-7]; prediction += coefficients[7] * (drflac_int64)pDecodedSamples[-8]; prediction += coefficients[8] * (drflac_int64)pDecodedSamples[-9]; prediction += coefficients[9] * (drflac_int64)pDecodedSamples[-10]; prediction += coefficients[10] * (drflac_int64)pDecodedSamples[-11]; prediction += coefficients[11] * (drflac_int64)pDecodedSamples[-12]; } else if (order == 2) { prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1]; prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2]; } else if (order == 1) { prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1]; } else if (order == 10) { prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1]; prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2]; prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3]; prediction += coefficients[3] * (drflac_int64)pDecodedSamples[-4]; prediction += coefficients[4] * (drflac_int64)pDecodedSamples[-5]; prediction += coefficients[5] * (drflac_int64)pDecodedSamples[-6]; prediction += coefficients[6] * (drflac_int64)pDecodedSamples[-7]; prediction += coefficients[7] * (drflac_int64)pDecodedSamples[-8]; prediction += coefficients[8] * (drflac_int64)pDecodedSamples[-9]; prediction += coefficients[9] * (drflac_int64)pDecodedSamples[-10]; } else if (order == 9) { prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1]; prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2]; prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3]; prediction += coefficients[3] * (drflac_int64)pDecodedSamples[-4]; prediction += coefficients[4] * (drflac_int64)pDecodedSamples[-5]; prediction += coefficients[5] * (drflac_int64)pDecodedSamples[-6]; prediction += coefficients[6] * (drflac_int64)pDecodedSamples[-7]; prediction += coefficients[7] * (drflac_int64)pDecodedSamples[-8]; prediction += coefficients[8] * (drflac_int64)pDecodedSamples[-9]; } else if (order == 11) { prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1]; prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2]; prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3]; prediction += coefficients[3] * (drflac_int64)pDecodedSamples[-4]; prediction += coefficients[4] * (drflac_int64)pDecodedSamples[-5]; prediction += coefficients[5] * (drflac_int64)pDecodedSamples[-6]; prediction += coefficients[6] * (drflac_int64)pDecodedSamples[-7]; prediction += coefficients[7] * (drflac_int64)pDecodedSamples[-8]; prediction += coefficients[8] * (drflac_int64)pDecodedSamples[-9]; prediction += coefficients[9] * (drflac_int64)pDecodedSamples[-10]; prediction += coefficients[10] * (drflac_int64)pDecodedSamples[-11]; } else { int j; prediction = 0; for (j = 0; j < (int)order; ++j) { prediction += coefficients[j] * (drflac_int64)pDecodedSamples[-j-1]; } } #endif #ifdef DRFLAC_64BIT prediction = 0; switch (order) { case 32: prediction += coefficients[31] * (drflac_int64)pDecodedSamples[-32]; case 31: prediction += coefficients[30] * (drflac_int64)pDecodedSamples[-31]; case 30: prediction += coefficients[29] * (drflac_int64)pDecodedSamples[-30]; case 29: prediction += coefficients[28] * (drflac_int64)pDecodedSamples[-29]; case 28: prediction += coefficients[27] * (drflac_int64)pDecodedSamples[-28]; case 27: prediction += coefficients[26] * (drflac_int64)pDecodedSamples[-27]; case 26: prediction += coefficients[25] * (drflac_int64)pDecodedSamples[-26]; case 25: prediction += coefficients[24] * (drflac_int64)pDecodedSamples[-25]; case 24: prediction += coefficients[23] * (drflac_int64)pDecodedSamples[-24]; case 23: prediction += coefficients[22] * (drflac_int64)pDecodedSamples[-23]; case 22: prediction += coefficients[21] * (drflac_int64)pDecodedSamples[-22]; case 21: prediction += coefficients[20] * (drflac_int64)pDecodedSamples[-21]; case 20: prediction += coefficients[19] * (drflac_int64)pDecodedSamples[-20]; case 19: prediction += coefficients[18] * (drflac_int64)pDecodedSamples[-19]; case 18: prediction += coefficients[17] * (drflac_int64)pDecodedSamples[-18]; case 17: prediction += coefficients[16] * (drflac_int64)pDecodedSamples[-17]; case 16: prediction += coefficients[15] * (drflac_int64)pDecodedSamples[-16]; case 15: prediction += coefficients[14] * (drflac_int64)pDecodedSamples[-15]; case 14: prediction += coefficients[13] * (drflac_int64)pDecodedSamples[-14]; case 13: prediction += coefficients[12] * (drflac_int64)pDecodedSamples[-13]; case 12: prediction += coefficients[11] * (drflac_int64)pDecodedSamples[-12]; case 11: prediction += coefficients[10] * (drflac_int64)pDecodedSamples[-11]; case 10: prediction += coefficients[ 9] * (drflac_int64)pDecodedSamples[-10]; case 9: prediction += coefficients[ 8] * (drflac_int64)pDecodedSamples[- 9]; case 8: prediction += coefficients[ 7] * (drflac_int64)pDecodedSamples[- 8]; case 7: prediction += coefficients[ 6] * (drflac_int64)pDecodedSamples[- 7]; case 6: prediction += coefficients[ 5] * (drflac_int64)pDecodedSamples[- 6]; case 5: prediction += coefficients[ 4] * (drflac_int64)pDecodedSamples[- 5]; case 4: prediction += coefficients[ 3] * (drflac_int64)pDecodedSamples[- 4]; case 3: prediction += coefficients[ 2] * (drflac_int64)pDecodedSamples[- 3]; case 2: prediction += coefficients[ 1] * (drflac_int64)pDecodedSamples[- 2]; case 1: prediction += coefficients[ 0] * (drflac_int64)pDecodedSamples[- 1]; } #endif return (drflac_int32)(prediction >> shift); } #if 0 static drflac_bool32 drflac__decode_samples_with_residual__rice__reference(drflac_bs* bs, drflac_uint32 bitsPerSample, drflac_uint32 count, drflac_uint8 riceParam, drflac_uint32 lpcOrder, drflac_int32 lpcShift, drflac_uint32 lpcPrecision, const drflac_int32* coefficients, drflac_int32* pSamplesOut) { drflac_uint32 i; DRFLAC_ASSERT(bs != NULL); DRFLAC_ASSERT(pSamplesOut != NULL); for (i = 0; i < count; ++i) { drflac_uint32 zeroCounter = 0; for (;;) { drflac_uint8 bit; if (!drflac__read_uint8(bs, 1, &bit)) { return DRFLAC_FALSE; } if (bit == 0) { zeroCounter += 1; } else { break; } } drflac_uint32 decodedRice; if (riceParam > 0) { if (!drflac__read_uint32(bs, riceParam, &decodedRice)) { return DRFLAC_FALSE; } } else { decodedRice = 0; } decodedRice |= (zeroCounter << riceParam); if ((decodedRice & 0x01)) { decodedRice = ~(decodedRice >> 1); } else { decodedRice = (decodedRice >> 1); } if (drflac__use_64_bit_prediction(bitsPerSample, lpcOrder, lpcPrecision)) { pSamplesOut[i] = decodedRice + drflac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + i); } else { pSamplesOut[i] = decodedRice + drflac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + i); } } return DRFLAC_TRUE; } #endif #if 0 static drflac_bool32 drflac__read_rice_parts__reference(drflac_bs* bs, drflac_uint8 riceParam, drflac_uint32* pZeroCounterOut, drflac_uint32* pRiceParamPartOut) { drflac_uint32 zeroCounter = 0; drflac_uint32 decodedRice; for (;;) { drflac_uint8 bit; if (!drflac__read_uint8(bs, 1, &bit)) { return DRFLAC_FALSE; } if (bit == 0) { zeroCounter += 1; } else { break; } } if (riceParam > 0) { if (!drflac__read_uint32(bs, riceParam, &decodedRice)) { return DRFLAC_FALSE; } } else { decodedRice = 0; } *pZeroCounterOut = zeroCounter; *pRiceParamPartOut = decodedRice; return DRFLAC_TRUE; } #endif #if 0 static DRFLAC_INLINE drflac_bool32 drflac__read_rice_parts(drflac_bs* bs, drflac_uint8 riceParam, drflac_uint32* pZeroCounterOut, drflac_uint32* pRiceParamPartOut) { drflac_cache_t riceParamMask; drflac_uint32 zeroCounter; drflac_uint32 setBitOffsetPlus1; drflac_uint32 riceParamPart; drflac_uint32 riceLength; DRFLAC_ASSERT(riceParam > 0); riceParamMask = DRFLAC_CACHE_L1_SELECTION_MASK(riceParam); zeroCounter = 0; while (bs->cache == 0) { zeroCounter += (drflac_uint32)DRFLAC_CACHE_L1_BITS_REMAINING(bs); if (!drflac__reload_cache(bs)) { return DRFLAC_FALSE; } } setBitOffsetPlus1 = drflac__clz(bs->cache); zeroCounter += setBitOffsetPlus1; setBitOffsetPlus1 += 1; riceLength = setBitOffsetPlus1 + riceParam; if (riceLength < DRFLAC_CACHE_L1_BITS_REMAINING(bs)) { riceParamPart = (drflac_uint32)((bs->cache & (riceParamMask >> setBitOffsetPlus1)) >> DRFLAC_CACHE_L1_SELECTION_SHIFT(bs, riceLength)); bs->consumedBits += riceLength; bs->cache <<= riceLength; } else { drflac_uint32 bitCountLo; drflac_cache_t resultHi; bs->consumedBits += riceLength; bs->cache <<= setBitOffsetPlus1 & (DRFLAC_CACHE_L1_SIZE_BITS(bs)-1); bitCountLo = bs->consumedBits - DRFLAC_CACHE_L1_SIZE_BITS(bs); resultHi = DRFLAC_CACHE_L1_SELECT_AND_SHIFT(bs, riceParam); if (bs->nextL2Line < DRFLAC_CACHE_L2_LINE_COUNT(bs)) { #ifndef DR_FLAC_NO_CRC drflac__update_crc16(bs); #endif bs->cache = drflac__be2host__cache_line(bs->cacheL2[bs->nextL2Line++]); bs->consumedBits = 0; #ifndef DR_FLAC_NO_CRC bs->crc16Cache = bs->cache; #endif } else { if (!drflac__reload_cache(bs)) { return DRFLAC_FALSE; } if (bitCountLo > DRFLAC_CACHE_L1_BITS_REMAINING(bs)) { return DRFLAC_FALSE; } } riceParamPart = (drflac_uint32)(resultHi | DRFLAC_CACHE_L1_SELECT_AND_SHIFT_SAFE(bs, bitCountLo)); bs->consumedBits += bitCountLo; bs->cache <<= bitCountLo; } pZeroCounterOut[0] = zeroCounter; pRiceParamPartOut[0] = riceParamPart; return DRFLAC_TRUE; } #endif static DRFLAC_INLINE drflac_bool32 drflac__read_rice_parts_x1(drflac_bs* bs, drflac_uint8 riceParam, drflac_uint32* pZeroCounterOut, drflac_uint32* pRiceParamPartOut) { drflac_uint32 riceParamPlus1 = riceParam + 1; drflac_uint32 riceParamPlus1Shift = DRFLAC_CACHE_L1_SELECTION_SHIFT(bs, riceParamPlus1); drflac_uint32 riceParamPlus1MaxConsumedBits = DRFLAC_CACHE_L1_SIZE_BITS(bs) - riceParamPlus1; drflac_cache_t bs_cache = bs->cache; drflac_uint32 bs_consumedBits = bs->consumedBits; drflac_uint32 lzcount = drflac__clz(bs_cache); if (lzcount < sizeof(bs_cache)*8) { pZeroCounterOut[0] = lzcount; extract_rice_param_part: bs_cache <<= lzcount; bs_consumedBits += lzcount; if (bs_consumedBits <= riceParamPlus1MaxConsumedBits) { pRiceParamPartOut[0] = (drflac_uint32)(bs_cache >> riceParamPlus1Shift); bs_cache <<= riceParamPlus1; bs_consumedBits += riceParamPlus1; } else { drflac_uint32 riceParamPartHi; drflac_uint32 riceParamPartLo; drflac_uint32 riceParamPartLoBitCount; riceParamPartHi = (drflac_uint32)(bs_cache >> riceParamPlus1Shift); riceParamPartLoBitCount = bs_consumedBits - riceParamPlus1MaxConsumedBits; DRFLAC_ASSERT(riceParamPartLoBitCount > 0 && riceParamPartLoBitCount < 32); if (bs->nextL2Line < DRFLAC_CACHE_L2_LINE_COUNT(bs)) { #ifndef DR_FLAC_NO_CRC drflac__update_crc16(bs); #endif bs_cache = drflac__be2host__cache_line(bs->cacheL2[bs->nextL2Line++]); bs_consumedBits = riceParamPartLoBitCount; #ifndef DR_FLAC_NO_CRC bs->crc16Cache = bs_cache; #endif } else { if (!drflac__reload_cache(bs)) { return DRFLAC_FALSE; } if (riceParamPartLoBitCount > DRFLAC_CACHE_L1_BITS_REMAINING(bs)) { return DRFLAC_FALSE; } bs_cache = bs->cache; bs_consumedBits = bs->consumedBits + riceParamPartLoBitCount; } riceParamPartLo = (drflac_uint32)(bs_cache >> (DRFLAC_CACHE_L1_SELECTION_SHIFT(bs, riceParamPartLoBitCount))); pRiceParamPartOut[0] = riceParamPartHi | riceParamPartLo; bs_cache <<= riceParamPartLoBitCount; } } else { drflac_uint32 zeroCounter = (drflac_uint32)(DRFLAC_CACHE_L1_SIZE_BITS(bs) - bs_consumedBits); for (;;) { if (bs->nextL2Line < DRFLAC_CACHE_L2_LINE_COUNT(bs)) { #ifndef DR_FLAC_NO_CRC drflac__update_crc16(bs); #endif bs_cache = drflac__be2host__cache_line(bs->cacheL2[bs->nextL2Line++]); bs_consumedBits = 0; #ifndef DR_FLAC_NO_CRC bs->crc16Cache = bs_cache; #endif } else { if (!drflac__reload_cache(bs)) { return DRFLAC_FALSE; } bs_cache = bs->cache; bs_consumedBits = bs->consumedBits; } lzcount = drflac__clz(bs_cache); zeroCounter += lzcount; if (lzcount < sizeof(bs_cache)*8) { break; } } pZeroCounterOut[0] = zeroCounter; goto extract_rice_param_part; } bs->cache = bs_cache; bs->consumedBits = bs_consumedBits; return DRFLAC_TRUE; } static DRFLAC_INLINE drflac_bool32 drflac__seek_rice_parts(drflac_bs* bs, drflac_uint8 riceParam) { drflac_uint32 riceParamPlus1 = riceParam + 1; drflac_uint32 riceParamPlus1MaxConsumedBits = DRFLAC_CACHE_L1_SIZE_BITS(bs) - riceParamPlus1; drflac_cache_t bs_cache = bs->cache; drflac_uint32 bs_consumedBits = bs->consumedBits; drflac_uint32 lzcount = drflac__clz(bs_cache); if (lzcount < sizeof(bs_cache)*8) { extract_rice_param_part: bs_cache <<= lzcount; bs_consumedBits += lzcount; if (bs_consumedBits <= riceParamPlus1MaxConsumedBits) { bs_cache <<= riceParamPlus1; bs_consumedBits += riceParamPlus1; } else { drflac_uint32 riceParamPartLoBitCount = bs_consumedBits - riceParamPlus1MaxConsumedBits; DRFLAC_ASSERT(riceParamPartLoBitCount > 0 && riceParamPartLoBitCount < 32); if (bs->nextL2Line < DRFLAC_CACHE_L2_LINE_COUNT(bs)) { #ifndef DR_FLAC_NO_CRC drflac__update_crc16(bs); #endif bs_cache = drflac__be2host__cache_line(bs->cacheL2[bs->nextL2Line++]); bs_consumedBits = riceParamPartLoBitCount; #ifndef DR_FLAC_NO_CRC bs->crc16Cache = bs_cache; #endif } else { if (!drflac__reload_cache(bs)) { return DRFLAC_FALSE; } if (riceParamPartLoBitCount > DRFLAC_CACHE_L1_BITS_REMAINING(bs)) { return DRFLAC_FALSE; } bs_cache = bs->cache; bs_consumedBits = bs->consumedBits + riceParamPartLoBitCount; } bs_cache <<= riceParamPartLoBitCount; } } else { for (;;) { if (bs->nextL2Line < DRFLAC_CACHE_L2_LINE_COUNT(bs)) { #ifndef DR_FLAC_NO_CRC drflac__update_crc16(bs); #endif bs_cache = drflac__be2host__cache_line(bs->cacheL2[bs->nextL2Line++]); bs_consumedBits = 0; #ifndef DR_FLAC_NO_CRC bs->crc16Cache = bs_cache; #endif } else { if (!drflac__reload_cache(bs)) { return DRFLAC_FALSE; } bs_cache = bs->cache; bs_consumedBits = bs->consumedBits; } lzcount = drflac__clz(bs_cache); if (lzcount < sizeof(bs_cache)*8) { break; } } goto extract_rice_param_part; } bs->cache = bs_cache; bs->consumedBits = bs_consumedBits; return DRFLAC_TRUE; } static drflac_bool32 drflac__decode_samples_with_residual__rice__scalar_zeroorder(drflac_bs* bs, drflac_uint32 bitsPerSample, drflac_uint32 count, drflac_uint8 riceParam, drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, drflac_int32* pSamplesOut) { drflac_uint32 t[2] = {0x00000000, 0xFFFFFFFF}; drflac_uint32 zeroCountPart0; drflac_uint32 riceParamPart0; drflac_uint32 riceParamMask; drflac_uint32 i; DRFLAC_ASSERT(bs != NULL); DRFLAC_ASSERT(pSamplesOut != NULL); (void)bitsPerSample; (void)order; (void)shift; (void)coefficients; riceParamMask = (drflac_uint32)~((~0UL) << riceParam); i = 0; while (i < count) { if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountPart0, &riceParamPart0)) { return DRFLAC_FALSE; } riceParamPart0 &= riceParamMask; riceParamPart0 |= (zeroCountPart0 << riceParam); riceParamPart0 = (riceParamPart0 >> 1) ^ t[riceParamPart0 & 0x01]; pSamplesOut[i] = riceParamPart0; i += 1; } return DRFLAC_TRUE; } static drflac_bool32 drflac__decode_samples_with_residual__rice__scalar(drflac_bs* bs, drflac_uint32 bitsPerSample, drflac_uint32 count, drflac_uint8 riceParam, drflac_uint32 lpcOrder, drflac_int32 lpcShift, drflac_uint32 lpcPrecision, const drflac_int32* coefficients, drflac_int32* pSamplesOut) { drflac_uint32 t[2] = {0x00000000, 0xFFFFFFFF}; drflac_uint32 zeroCountPart0 = 0; drflac_uint32 zeroCountPart1 = 0; drflac_uint32 zeroCountPart2 = 0; drflac_uint32 zeroCountPart3 = 0; drflac_uint32 riceParamPart0 = 0; drflac_uint32 riceParamPart1 = 0; drflac_uint32 riceParamPart2 = 0; drflac_uint32 riceParamPart3 = 0; drflac_uint32 riceParamMask; const drflac_int32* pSamplesOutEnd; drflac_uint32 i; DRFLAC_ASSERT(bs != NULL); DRFLAC_ASSERT(pSamplesOut != NULL); if (lpcOrder == 0) { return drflac__decode_samples_with_residual__rice__scalar_zeroorder(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, coefficients, pSamplesOut); } riceParamMask = (drflac_uint32)~((~0UL) << riceParam); pSamplesOutEnd = pSamplesOut + (count & ~3); if (drflac__use_64_bit_prediction(bitsPerSample, lpcOrder, lpcPrecision)) { while (pSamplesOut < pSamplesOutEnd) { if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountPart0, &riceParamPart0) || !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountPart1, &riceParamPart1) || !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountPart2, &riceParamPart2) || !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountPart3, &riceParamPart3)) { return DRFLAC_FALSE; } riceParamPart0 &= riceParamMask; riceParamPart1 &= riceParamMask; riceParamPart2 &= riceParamMask; riceParamPart3 &= riceParamMask; riceParamPart0 |= (zeroCountPart0 << riceParam); riceParamPart1 |= (zeroCountPart1 << riceParam); riceParamPart2 |= (zeroCountPart2 << riceParam); riceParamPart3 |= (zeroCountPart3 << riceParam); riceParamPart0 = (riceParamPart0 >> 1) ^ t[riceParamPart0 & 0x01]; riceParamPart1 = (riceParamPart1 >> 1) ^ t[riceParamPart1 & 0x01]; riceParamPart2 = (riceParamPart2 >> 1) ^ t[riceParamPart2 & 0x01]; riceParamPart3 = (riceParamPart3 >> 1) ^ t[riceParamPart3 & 0x01]; pSamplesOut[0] = riceParamPart0 + drflac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + 0); pSamplesOut[1] = riceParamPart1 + drflac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + 1); pSamplesOut[2] = riceParamPart2 + drflac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + 2); pSamplesOut[3] = riceParamPart3 + drflac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + 3); pSamplesOut += 4; } } else { while (pSamplesOut < pSamplesOutEnd) { if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountPart0, &riceParamPart0) || !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountPart1, &riceParamPart1) || !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountPart2, &riceParamPart2) || !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountPart3, &riceParamPart3)) { return DRFLAC_FALSE; } riceParamPart0 &= riceParamMask; riceParamPart1 &= riceParamMask; riceParamPart2 &= riceParamMask; riceParamPart3 &= riceParamMask; riceParamPart0 |= (zeroCountPart0 << riceParam); riceParamPart1 |= (zeroCountPart1 << riceParam); riceParamPart2 |= (zeroCountPart2 << riceParam); riceParamPart3 |= (zeroCountPart3 << riceParam); riceParamPart0 = (riceParamPart0 >> 1) ^ t[riceParamPart0 & 0x01]; riceParamPart1 = (riceParamPart1 >> 1) ^ t[riceParamPart1 & 0x01]; riceParamPart2 = (riceParamPart2 >> 1) ^ t[riceParamPart2 & 0x01]; riceParamPart3 = (riceParamPart3 >> 1) ^ t[riceParamPart3 & 0x01]; pSamplesOut[0] = riceParamPart0 + drflac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + 0); pSamplesOut[1] = riceParamPart1 + drflac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + 1); pSamplesOut[2] = riceParamPart2 + drflac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + 2); pSamplesOut[3] = riceParamPart3 + drflac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + 3); pSamplesOut += 4; } } i = (count & ~3); while (i < count) { if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountPart0, &riceParamPart0)) { return DRFLAC_FALSE; } riceParamPart0 &= riceParamMask; riceParamPart0 |= (zeroCountPart0 << riceParam); riceParamPart0 = (riceParamPart0 >> 1) ^ t[riceParamPart0 & 0x01]; if (drflac__use_64_bit_prediction(bitsPerSample, lpcOrder, lpcPrecision)) { pSamplesOut[0] = riceParamPart0 + drflac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + 0); } else { pSamplesOut[0] = riceParamPart0 + drflac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + 0); } i += 1; pSamplesOut += 1; } return DRFLAC_TRUE; } #if defined(DRFLAC_SUPPORT_SSE2) static DRFLAC_INLINE __m128i drflac__mm_packs_interleaved_epi32(__m128i a, __m128i b) { __m128i r; r = _mm_packs_epi32(a, b); r = _mm_shuffle_epi32(r, _MM_SHUFFLE(3, 1, 2, 0)); r = _mm_shufflehi_epi16(r, _MM_SHUFFLE(3, 1, 2, 0)); r = _mm_shufflelo_epi16(r, _MM_SHUFFLE(3, 1, 2, 0)); return r; } #endif #if defined(DRFLAC_SUPPORT_SSE41) static DRFLAC_INLINE __m128i drflac__mm_not_si128(__m128i a) { return _mm_xor_si128(a, _mm_cmpeq_epi32(_mm_setzero_si128(), _mm_setzero_si128())); } static DRFLAC_INLINE __m128i drflac__mm_hadd_epi32(__m128i x) { __m128i x64 = _mm_add_epi32(x, _mm_shuffle_epi32(x, _MM_SHUFFLE(1, 0, 3, 2))); __m128i x32 = _mm_shufflelo_epi16(x64, _MM_SHUFFLE(1, 0, 3, 2)); return _mm_add_epi32(x64, x32); } static DRFLAC_INLINE __m128i drflac__mm_hadd_epi64(__m128i x) { return _mm_add_epi64(x, _mm_shuffle_epi32(x, _MM_SHUFFLE(1, 0, 3, 2))); } static DRFLAC_INLINE __m128i drflac__mm_srai_epi64(__m128i x, int count) { __m128i lo = _mm_srli_epi64(x, count); __m128i hi = _mm_srai_epi32(x, count); hi = _mm_and_si128(hi, _mm_set_epi32(0xFFFFFFFF, 0, 0xFFFFFFFF, 0)); return _mm_or_si128(lo, hi); } static drflac_bool32 drflac__decode_samples_with_residual__rice__sse41_32(drflac_bs* bs, drflac_uint32 count, drflac_uint8 riceParam, drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, drflac_int32* pSamplesOut) { int i; drflac_uint32 riceParamMask; drflac_int32* pDecodedSamples = pSamplesOut; drflac_int32* pDecodedSamplesEnd = pSamplesOut + (count & ~3); drflac_uint32 zeroCountParts0 = 0; drflac_uint32 zeroCountParts1 = 0; drflac_uint32 zeroCountParts2 = 0; drflac_uint32 zeroCountParts3 = 0; drflac_uint32 riceParamParts0 = 0; drflac_uint32 riceParamParts1 = 0; drflac_uint32 riceParamParts2 = 0; drflac_uint32 riceParamParts3 = 0; __m128i coefficients128_0; __m128i coefficients128_4; __m128i coefficients128_8; __m128i samples128_0; __m128i samples128_4; __m128i samples128_8; __m128i riceParamMask128; const drflac_uint32 t[2] = {0x00000000, 0xFFFFFFFF}; riceParamMask = (drflac_uint32)~((~0UL) << riceParam); riceParamMask128 = _mm_set1_epi32(riceParamMask); coefficients128_0 = _mm_setzero_si128(); coefficients128_4 = _mm_setzero_si128(); coefficients128_8 = _mm_setzero_si128(); samples128_0 = _mm_setzero_si128(); samples128_4 = _mm_setzero_si128(); samples128_8 = _mm_setzero_si128(); #if 1 { int runningOrder = order; if (runningOrder >= 4) { coefficients128_0 = _mm_loadu_si128((const __m128i*)(coefficients + 0)); samples128_0 = _mm_loadu_si128((const __m128i*)(pSamplesOut - 4)); runningOrder -= 4; } else { switch (runningOrder) { case 3: coefficients128_0 = _mm_set_epi32(0, coefficients[2], coefficients[1], coefficients[0]); samples128_0 = _mm_set_epi32(pSamplesOut[-1], pSamplesOut[-2], pSamplesOut[-3], 0); break; case 2: coefficients128_0 = _mm_set_epi32(0, 0, coefficients[1], coefficients[0]); samples128_0 = _mm_set_epi32(pSamplesOut[-1], pSamplesOut[-2], 0, 0); break; case 1: coefficients128_0 = _mm_set_epi32(0, 0, 0, coefficients[0]); samples128_0 = _mm_set_epi32(pSamplesOut[-1], 0, 0, 0); break; } runningOrder = 0; } if (runningOrder >= 4) { coefficients128_4 = _mm_loadu_si128((const __m128i*)(coefficients + 4)); samples128_4 = _mm_loadu_si128((const __m128i*)(pSamplesOut - 8)); runningOrder -= 4; } else { switch (runningOrder) { case 3: coefficients128_4 = _mm_set_epi32(0, coefficients[6], coefficients[5], coefficients[4]); samples128_4 = _mm_set_epi32(pSamplesOut[-5], pSamplesOut[-6], pSamplesOut[-7], 0); break; case 2: coefficients128_4 = _mm_set_epi32(0, 0, coefficients[5], coefficients[4]); samples128_4 = _mm_set_epi32(pSamplesOut[-5], pSamplesOut[-6], 0, 0); break; case 1: coefficients128_4 = _mm_set_epi32(0, 0, 0, coefficients[4]); samples128_4 = _mm_set_epi32(pSamplesOut[-5], 0, 0, 0); break; } runningOrder = 0; } if (runningOrder == 4) { coefficients128_8 = _mm_loadu_si128((const __m128i*)(coefficients + 8)); samples128_8 = _mm_loadu_si128((const __m128i*)(pSamplesOut - 12)); runningOrder -= 4; } else { switch (runningOrder) { case 3: coefficients128_8 = _mm_set_epi32(0, coefficients[10], coefficients[9], coefficients[8]); samples128_8 = _mm_set_epi32(pSamplesOut[-9], pSamplesOut[-10], pSamplesOut[-11], 0); break; case 2: coefficients128_8 = _mm_set_epi32(0, 0, coefficients[9], coefficients[8]); samples128_8 = _mm_set_epi32(pSamplesOut[-9], pSamplesOut[-10], 0, 0); break; case 1: coefficients128_8 = _mm_set_epi32(0, 0, 0, coefficients[8]); samples128_8 = _mm_set_epi32(pSamplesOut[-9], 0, 0, 0); break; } runningOrder = 0; } coefficients128_0 = _mm_shuffle_epi32(coefficients128_0, _MM_SHUFFLE(0, 1, 2, 3)); coefficients128_4 = _mm_shuffle_epi32(coefficients128_4, _MM_SHUFFLE(0, 1, 2, 3)); coefficients128_8 = _mm_shuffle_epi32(coefficients128_8, _MM_SHUFFLE(0, 1, 2, 3)); } #else switch (order) { case 12: ((drflac_int32*)&coefficients128_8)[0] = coefficients[11]; ((drflac_int32*)&samples128_8)[0] = pDecodedSamples[-12]; case 11: ((drflac_int32*)&coefficients128_8)[1] = coefficients[10]; ((drflac_int32*)&samples128_8)[1] = pDecodedSamples[-11]; case 10: ((drflac_int32*)&coefficients128_8)[2] = coefficients[ 9]; ((drflac_int32*)&samples128_8)[2] = pDecodedSamples[-10]; case 9: ((drflac_int32*)&coefficients128_8)[3] = coefficients[ 8]; ((drflac_int32*)&samples128_8)[3] = pDecodedSamples[- 9]; case 8: ((drflac_int32*)&coefficients128_4)[0] = coefficients[ 7]; ((drflac_int32*)&samples128_4)[0] = pDecodedSamples[- 8]; case 7: ((drflac_int32*)&coefficients128_4)[1] = coefficients[ 6]; ((drflac_int32*)&samples128_4)[1] = pDecodedSamples[- 7]; case 6: ((drflac_int32*)&coefficients128_4)[2] = coefficients[ 5]; ((drflac_int32*)&samples128_4)[2] = pDecodedSamples[- 6]; case 5: ((drflac_int32*)&coefficients128_4)[3] = coefficients[ 4]; ((drflac_int32*)&samples128_4)[3] = pDecodedSamples[- 5]; case 4: ((drflac_int32*)&coefficients128_0)[0] = coefficients[ 3]; ((drflac_int32*)&samples128_0)[0] = pDecodedSamples[- 4]; case 3: ((drflac_int32*)&coefficients128_0)[1] = coefficients[ 2]; ((drflac_int32*)&samples128_0)[1] = pDecodedSamples[- 3]; case 2: ((drflac_int32*)&coefficients128_0)[2] = coefficients[ 1]; ((drflac_int32*)&samples128_0)[2] = pDecodedSamples[- 2]; case 1: ((drflac_int32*)&coefficients128_0)[3] = coefficients[ 0]; ((drflac_int32*)&samples128_0)[3] = pDecodedSamples[- 1]; } #endif while (pDecodedSamples < pDecodedSamplesEnd) { __m128i prediction128; __m128i zeroCountPart128; __m128i riceParamPart128; if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts0, &riceParamParts0) || !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts1, &riceParamParts1) || !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts2, &riceParamParts2) || !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts3, &riceParamParts3)) { return DRFLAC_FALSE; } zeroCountPart128 = _mm_set_epi32(zeroCountParts3, zeroCountParts2, zeroCountParts1, zeroCountParts0); riceParamPart128 = _mm_set_epi32(riceParamParts3, riceParamParts2, riceParamParts1, riceParamParts0); riceParamPart128 = _mm_and_si128(riceParamPart128, riceParamMask128); riceParamPart128 = _mm_or_si128(riceParamPart128, _mm_slli_epi32(zeroCountPart128, riceParam)); riceParamPart128 = _mm_xor_si128(_mm_srli_epi32(riceParamPart128, 1), _mm_add_epi32(drflac__mm_not_si128(_mm_and_si128(riceParamPart128, _mm_set1_epi32(0x01))), _mm_set1_epi32(0x01))); if (order <= 4) { for (i = 0; i < 4; i += 1) { prediction128 = _mm_mullo_epi32(coefficients128_0, samples128_0); prediction128 = drflac__mm_hadd_epi32(prediction128); prediction128 = _mm_srai_epi32(prediction128, shift); prediction128 = _mm_add_epi32(riceParamPart128, prediction128); samples128_0 = _mm_alignr_epi8(prediction128, samples128_0, 4); riceParamPart128 = _mm_alignr_epi8(_mm_setzero_si128(), riceParamPart128, 4); } } else if (order <= 8) { for (i = 0; i < 4; i += 1) { prediction128 = _mm_mullo_epi32(coefficients128_4, samples128_4); prediction128 = _mm_add_epi32(prediction128, _mm_mullo_epi32(coefficients128_0, samples128_0)); prediction128 = drflac__mm_hadd_epi32(prediction128); prediction128 = _mm_srai_epi32(prediction128, shift); prediction128 = _mm_add_epi32(riceParamPart128, prediction128); samples128_4 = _mm_alignr_epi8(samples128_0, samples128_4, 4); samples128_0 = _mm_alignr_epi8(prediction128, samples128_0, 4); riceParamPart128 = _mm_alignr_epi8(_mm_setzero_si128(), riceParamPart128, 4); } } else { for (i = 0; i < 4; i += 1) { prediction128 = _mm_mullo_epi32(coefficients128_8, samples128_8); prediction128 = _mm_add_epi32(prediction128, _mm_mullo_epi32(coefficients128_4, samples128_4)); prediction128 = _mm_add_epi32(prediction128, _mm_mullo_epi32(coefficients128_0, samples128_0)); prediction128 = drflac__mm_hadd_epi32(prediction128); prediction128 = _mm_srai_epi32(prediction128, shift); prediction128 = _mm_add_epi32(riceParamPart128, prediction128); samples128_8 = _mm_alignr_epi8(samples128_4, samples128_8, 4); samples128_4 = _mm_alignr_epi8(samples128_0, samples128_4, 4); samples128_0 = _mm_alignr_epi8(prediction128, samples128_0, 4); riceParamPart128 = _mm_alignr_epi8(_mm_setzero_si128(), riceParamPart128, 4); } } _mm_storeu_si128((__m128i*)pDecodedSamples, samples128_0); pDecodedSamples += 4; } i = (count & ~3); while (i < (int)count) { if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts0, &riceParamParts0)) { return DRFLAC_FALSE; } riceParamParts0 &= riceParamMask; riceParamParts0 |= (zeroCountParts0 << riceParam); riceParamParts0 = (riceParamParts0 >> 1) ^ t[riceParamParts0 & 0x01]; pDecodedSamples[0] = riceParamParts0 + drflac__calculate_prediction_32(order, shift, coefficients, pDecodedSamples); i += 1; pDecodedSamples += 1; } return DRFLAC_TRUE; } static drflac_bool32 drflac__decode_samples_with_residual__rice__sse41_64(drflac_bs* bs, drflac_uint32 count, drflac_uint8 riceParam, drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, drflac_int32* pSamplesOut) { int i; drflac_uint32 riceParamMask; drflac_int32* pDecodedSamples = pSamplesOut; drflac_int32* pDecodedSamplesEnd = pSamplesOut + (count & ~3); drflac_uint32 zeroCountParts0 = 0; drflac_uint32 zeroCountParts1 = 0; drflac_uint32 zeroCountParts2 = 0; drflac_uint32 zeroCountParts3 = 0; drflac_uint32 riceParamParts0 = 0; drflac_uint32 riceParamParts1 = 0; drflac_uint32 riceParamParts2 = 0; drflac_uint32 riceParamParts3 = 0; __m128i coefficients128_0; __m128i coefficients128_4; __m128i coefficients128_8; __m128i samples128_0; __m128i samples128_4; __m128i samples128_8; __m128i prediction128; __m128i riceParamMask128; const drflac_uint32 t[2] = {0x00000000, 0xFFFFFFFF}; DRFLAC_ASSERT(order <= 12); riceParamMask = (drflac_uint32)~((~0UL) << riceParam); riceParamMask128 = _mm_set1_epi32(riceParamMask); prediction128 = _mm_setzero_si128(); coefficients128_0 = _mm_setzero_si128(); coefficients128_4 = _mm_setzero_si128(); coefficients128_8 = _mm_setzero_si128(); samples128_0 = _mm_setzero_si128(); samples128_4 = _mm_setzero_si128(); samples128_8 = _mm_setzero_si128(); #if 1 { int runningOrder = order; if (runningOrder >= 4) { coefficients128_0 = _mm_loadu_si128((const __m128i*)(coefficients + 0)); samples128_0 = _mm_loadu_si128((const __m128i*)(pSamplesOut - 4)); runningOrder -= 4; } else { switch (runningOrder) { case 3: coefficients128_0 = _mm_set_epi32(0, coefficients[2], coefficients[1], coefficients[0]); samples128_0 = _mm_set_epi32(pSamplesOut[-1], pSamplesOut[-2], pSamplesOut[-3], 0); break; case 2: coefficients128_0 = _mm_set_epi32(0, 0, coefficients[1], coefficients[0]); samples128_0 = _mm_set_epi32(pSamplesOut[-1], pSamplesOut[-2], 0, 0); break; case 1: coefficients128_0 = _mm_set_epi32(0, 0, 0, coefficients[0]); samples128_0 = _mm_set_epi32(pSamplesOut[-1], 0, 0, 0); break; } runningOrder = 0; } if (runningOrder >= 4) { coefficients128_4 = _mm_loadu_si128((const __m128i*)(coefficients + 4)); samples128_4 = _mm_loadu_si128((const __m128i*)(pSamplesOut - 8)); runningOrder -= 4; } else { switch (runningOrder) { case 3: coefficients128_4 = _mm_set_epi32(0, coefficients[6], coefficients[5], coefficients[4]); samples128_4 = _mm_set_epi32(pSamplesOut[-5], pSamplesOut[-6], pSamplesOut[-7], 0); break; case 2: coefficients128_4 = _mm_set_epi32(0, 0, coefficients[5], coefficients[4]); samples128_4 = _mm_set_epi32(pSamplesOut[-5], pSamplesOut[-6], 0, 0); break; case 1: coefficients128_4 = _mm_set_epi32(0, 0, 0, coefficients[4]); samples128_4 = _mm_set_epi32(pSamplesOut[-5], 0, 0, 0); break; } runningOrder = 0; } if (runningOrder == 4) { coefficients128_8 = _mm_loadu_si128((const __m128i*)(coefficients + 8)); samples128_8 = _mm_loadu_si128((const __m128i*)(pSamplesOut - 12)); runningOrder -= 4; } else { switch (runningOrder) { case 3: coefficients128_8 = _mm_set_epi32(0, coefficients[10], coefficients[9], coefficients[8]); samples128_8 = _mm_set_epi32(pSamplesOut[-9], pSamplesOut[-10], pSamplesOut[-11], 0); break; case 2: coefficients128_8 = _mm_set_epi32(0, 0, coefficients[9], coefficients[8]); samples128_8 = _mm_set_epi32(pSamplesOut[-9], pSamplesOut[-10], 0, 0); break; case 1: coefficients128_8 = _mm_set_epi32(0, 0, 0, coefficients[8]); samples128_8 = _mm_set_epi32(pSamplesOut[-9], 0, 0, 0); break; } runningOrder = 0; } coefficients128_0 = _mm_shuffle_epi32(coefficients128_0, _MM_SHUFFLE(0, 1, 2, 3)); coefficients128_4 = _mm_shuffle_epi32(coefficients128_4, _MM_SHUFFLE(0, 1, 2, 3)); coefficients128_8 = _mm_shuffle_epi32(coefficients128_8, _MM_SHUFFLE(0, 1, 2, 3)); } #else switch (order) { case 12: ((drflac_int32*)&coefficients128_8)[0] = coefficients[11]; ((drflac_int32*)&samples128_8)[0] = pDecodedSamples[-12]; case 11: ((drflac_int32*)&coefficients128_8)[1] = coefficients[10]; ((drflac_int32*)&samples128_8)[1] = pDecodedSamples[-11]; case 10: ((drflac_int32*)&coefficients128_8)[2] = coefficients[ 9]; ((drflac_int32*)&samples128_8)[2] = pDecodedSamples[-10]; case 9: ((drflac_int32*)&coefficients128_8)[3] = coefficients[ 8]; ((drflac_int32*)&samples128_8)[3] = pDecodedSamples[- 9]; case 8: ((drflac_int32*)&coefficients128_4)[0] = coefficients[ 7]; ((drflac_int32*)&samples128_4)[0] = pDecodedSamples[- 8]; case 7: ((drflac_int32*)&coefficients128_4)[1] = coefficients[ 6]; ((drflac_int32*)&samples128_4)[1] = pDecodedSamples[- 7]; case 6: ((drflac_int32*)&coefficients128_4)[2] = coefficients[ 5]; ((drflac_int32*)&samples128_4)[2] = pDecodedSamples[- 6]; case 5: ((drflac_int32*)&coefficients128_4)[3] = coefficients[ 4]; ((drflac_int32*)&samples128_4)[3] = pDecodedSamples[- 5]; case 4: ((drflac_int32*)&coefficients128_0)[0] = coefficients[ 3]; ((drflac_int32*)&samples128_0)[0] = pDecodedSamples[- 4]; case 3: ((drflac_int32*)&coefficients128_0)[1] = coefficients[ 2]; ((drflac_int32*)&samples128_0)[1] = pDecodedSamples[- 3]; case 2: ((drflac_int32*)&coefficients128_0)[2] = coefficients[ 1]; ((drflac_int32*)&samples128_0)[2] = pDecodedSamples[- 2]; case 1: ((drflac_int32*)&coefficients128_0)[3] = coefficients[ 0]; ((drflac_int32*)&samples128_0)[3] = pDecodedSamples[- 1]; } #endif while (pDecodedSamples < pDecodedSamplesEnd) { __m128i zeroCountPart128; __m128i riceParamPart128; if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts0, &riceParamParts0) || !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts1, &riceParamParts1) || !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts2, &riceParamParts2) || !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts3, &riceParamParts3)) { return DRFLAC_FALSE; } zeroCountPart128 = _mm_set_epi32(zeroCountParts3, zeroCountParts2, zeroCountParts1, zeroCountParts0); riceParamPart128 = _mm_set_epi32(riceParamParts3, riceParamParts2, riceParamParts1, riceParamParts0); riceParamPart128 = _mm_and_si128(riceParamPart128, riceParamMask128); riceParamPart128 = _mm_or_si128(riceParamPart128, _mm_slli_epi32(zeroCountPart128, riceParam)); riceParamPart128 = _mm_xor_si128(_mm_srli_epi32(riceParamPart128, 1), _mm_add_epi32(drflac__mm_not_si128(_mm_and_si128(riceParamPart128, _mm_set1_epi32(1))), _mm_set1_epi32(1))); for (i = 0; i < 4; i += 1) { prediction128 = _mm_xor_si128(prediction128, prediction128); switch (order) { case 12: case 11: prediction128 = _mm_add_epi64(prediction128, _mm_mul_epi32(_mm_shuffle_epi32(coefficients128_8, _MM_SHUFFLE(1, 1, 0, 0)), _mm_shuffle_epi32(samples128_8, _MM_SHUFFLE(1, 1, 0, 0)))); case 10: case 9: prediction128 = _mm_add_epi64(prediction128, _mm_mul_epi32(_mm_shuffle_epi32(coefficients128_8, _MM_SHUFFLE(3, 3, 2, 2)), _mm_shuffle_epi32(samples128_8, _MM_SHUFFLE(3, 3, 2, 2)))); case 8: case 7: prediction128 = _mm_add_epi64(prediction128, _mm_mul_epi32(_mm_shuffle_epi32(coefficients128_4, _MM_SHUFFLE(1, 1, 0, 0)), _mm_shuffle_epi32(samples128_4, _MM_SHUFFLE(1, 1, 0, 0)))); case 6: case 5: prediction128 = _mm_add_epi64(prediction128, _mm_mul_epi32(_mm_shuffle_epi32(coefficients128_4, _MM_SHUFFLE(3, 3, 2, 2)), _mm_shuffle_epi32(samples128_4, _MM_SHUFFLE(3, 3, 2, 2)))); case 4: case 3: prediction128 = _mm_add_epi64(prediction128, _mm_mul_epi32(_mm_shuffle_epi32(coefficients128_0, _MM_SHUFFLE(1, 1, 0, 0)), _mm_shuffle_epi32(samples128_0, _MM_SHUFFLE(1, 1, 0, 0)))); case 2: case 1: prediction128 = _mm_add_epi64(prediction128, _mm_mul_epi32(_mm_shuffle_epi32(coefficients128_0, _MM_SHUFFLE(3, 3, 2, 2)), _mm_shuffle_epi32(samples128_0, _MM_SHUFFLE(3, 3, 2, 2)))); } prediction128 = drflac__mm_hadd_epi64(prediction128); prediction128 = drflac__mm_srai_epi64(prediction128, shift); prediction128 = _mm_add_epi32(riceParamPart128, prediction128); samples128_8 = _mm_alignr_epi8(samples128_4, samples128_8, 4); samples128_4 = _mm_alignr_epi8(samples128_0, samples128_4, 4); samples128_0 = _mm_alignr_epi8(prediction128, samples128_0, 4); riceParamPart128 = _mm_alignr_epi8(_mm_setzero_si128(), riceParamPart128, 4); } _mm_storeu_si128((__m128i*)pDecodedSamples, samples128_0); pDecodedSamples += 4; } i = (count & ~3); while (i < (int)count) { if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts0, &riceParamParts0)) { return DRFLAC_FALSE; } riceParamParts0 &= riceParamMask; riceParamParts0 |= (zeroCountParts0 << riceParam); riceParamParts0 = (riceParamParts0 >> 1) ^ t[riceParamParts0 & 0x01]; pDecodedSamples[0] = riceParamParts0 + drflac__calculate_prediction_64(order, shift, coefficients, pDecodedSamples); i += 1; pDecodedSamples += 1; } return DRFLAC_TRUE; } static drflac_bool32 drflac__decode_samples_with_residual__rice__sse41(drflac_bs* bs, drflac_uint32 bitsPerSample, drflac_uint32 count, drflac_uint8 riceParam, drflac_uint32 lpcOrder, drflac_int32 lpcShift, drflac_uint32 lpcPrecision, const drflac_int32* coefficients, drflac_int32* pSamplesOut) { DRFLAC_ASSERT(bs != NULL); DRFLAC_ASSERT(pSamplesOut != NULL); if (lpcOrder > 0 && lpcOrder <= 12) { if (drflac__use_64_bit_prediction(bitsPerSample, lpcOrder, lpcPrecision)) { return drflac__decode_samples_with_residual__rice__sse41_64(bs, count, riceParam, lpcOrder, lpcShift, coefficients, pSamplesOut); } else { return drflac__decode_samples_with_residual__rice__sse41_32(bs, count, riceParam, lpcOrder, lpcShift, coefficients, pSamplesOut); } } else { return drflac__decode_samples_with_residual__rice__scalar(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pSamplesOut); } } #endif #if defined(DRFLAC_SUPPORT_NEON) static DRFLAC_INLINE void drflac__vst2q_s32(drflac_int32* p, int32x4x2_t x) { vst1q_s32(p+0, x.val[0]); vst1q_s32(p+4, x.val[1]); } static DRFLAC_INLINE void drflac__vst2q_u32(drflac_uint32* p, uint32x4x2_t x) { vst1q_u32(p+0, x.val[0]); vst1q_u32(p+4, x.val[1]); } static DRFLAC_INLINE void drflac__vst2q_f32(float* p, float32x4x2_t x) { vst1q_f32(p+0, x.val[0]); vst1q_f32(p+4, x.val[1]); } static DRFLAC_INLINE void drflac__vst2q_s16(drflac_int16* p, int16x4x2_t x) { vst1q_s16(p, vcombine_s16(x.val[0], x.val[1])); } static DRFLAC_INLINE void drflac__vst2q_u16(drflac_uint16* p, uint16x4x2_t x) { vst1q_u16(p, vcombine_u16(x.val[0], x.val[1])); } static DRFLAC_INLINE int32x4_t drflac__vdupq_n_s32x4(drflac_int32 x3, drflac_int32 x2, drflac_int32 x1, drflac_int32 x0) { drflac_int32 x[4]; x[3] = x3; x[2] = x2; x[1] = x1; x[0] = x0; return vld1q_s32(x); } static DRFLAC_INLINE int32x4_t drflac__valignrq_s32_1(int32x4_t a, int32x4_t b) { return vextq_s32(b, a, 1); } static DRFLAC_INLINE uint32x4_t drflac__valignrq_u32_1(uint32x4_t a, uint32x4_t b) { return vextq_u32(b, a, 1); } static DRFLAC_INLINE int32x2_t drflac__vhaddq_s32(int32x4_t x) { int32x2_t r = vadd_s32(vget_high_s32(x), vget_low_s32(x)); return vpadd_s32(r, r); } static DRFLAC_INLINE int64x1_t drflac__vhaddq_s64(int64x2_t x) { return vadd_s64(vget_high_s64(x), vget_low_s64(x)); } static DRFLAC_INLINE int32x4_t drflac__vrevq_s32(int32x4_t x) { return vrev64q_s32(vcombine_s32(vget_high_s32(x), vget_low_s32(x))); } static DRFLAC_INLINE int32x4_t drflac__vnotq_s32(int32x4_t x) { return veorq_s32(x, vdupq_n_s32(0xFFFFFFFF)); } static DRFLAC_INLINE uint32x4_t drflac__vnotq_u32(uint32x4_t x) { return veorq_u32(x, vdupq_n_u32(0xFFFFFFFF)); } static drflac_bool32 drflac__decode_samples_with_residual__rice__neon_32(drflac_bs* bs, drflac_uint32 count, drflac_uint8 riceParam, drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, drflac_int32* pSamplesOut) { int i; drflac_uint32 riceParamMask; drflac_int32* pDecodedSamples = pSamplesOut; drflac_int32* pDecodedSamplesEnd = pSamplesOut + (count & ~3); drflac_uint32 zeroCountParts[4]; drflac_uint32 riceParamParts[4]; int32x4_t coefficients128_0; int32x4_t coefficients128_4; int32x4_t coefficients128_8; int32x4_t samples128_0; int32x4_t samples128_4; int32x4_t samples128_8; uint32x4_t riceParamMask128; int32x4_t riceParam128; int32x2_t shift64; uint32x4_t one128; const drflac_uint32 t[2] = {0x00000000, 0xFFFFFFFF}; riceParamMask = (drflac_uint32)~((~0UL) << riceParam); riceParamMask128 = vdupq_n_u32(riceParamMask); riceParam128 = vdupq_n_s32(riceParam); shift64 = vdup_n_s32(-shift); one128 = vdupq_n_u32(1); { int runningOrder = order; drflac_int32 tempC[4] = {0, 0, 0, 0}; drflac_int32 tempS[4] = {0, 0, 0, 0}; if (runningOrder >= 4) { coefficients128_0 = vld1q_s32(coefficients + 0); samples128_0 = vld1q_s32(pSamplesOut - 4); runningOrder -= 4; } else { switch (runningOrder) { case 3: tempC[2] = coefficients[2]; tempS[1] = pSamplesOut[-3]; case 2: tempC[1] = coefficients[1]; tempS[2] = pSamplesOut[-2]; case 1: tempC[0] = coefficients[0]; tempS[3] = pSamplesOut[-1]; } coefficients128_0 = vld1q_s32(tempC); samples128_0 = vld1q_s32(tempS); runningOrder = 0; } if (runningOrder >= 4) { coefficients128_4 = vld1q_s32(coefficients + 4); samples128_4 = vld1q_s32(pSamplesOut - 8); runningOrder -= 4; } else { switch (runningOrder) { case 3: tempC[2] = coefficients[6]; tempS[1] = pSamplesOut[-7]; case 2: tempC[1] = coefficients[5]; tempS[2] = pSamplesOut[-6]; case 1: tempC[0] = coefficients[4]; tempS[3] = pSamplesOut[-5]; } coefficients128_4 = vld1q_s32(tempC); samples128_4 = vld1q_s32(tempS); runningOrder = 0; } if (runningOrder == 4) { coefficients128_8 = vld1q_s32(coefficients + 8); samples128_8 = vld1q_s32(pSamplesOut - 12); runningOrder -= 4; } else { switch (runningOrder) { case 3: tempC[2] = coefficients[10]; tempS[1] = pSamplesOut[-11]; case 2: tempC[1] = coefficients[ 9]; tempS[2] = pSamplesOut[-10]; case 1: tempC[0] = coefficients[ 8]; tempS[3] = pSamplesOut[- 9]; } coefficients128_8 = vld1q_s32(tempC); samples128_8 = vld1q_s32(tempS); runningOrder = 0; } coefficients128_0 = drflac__vrevq_s32(coefficients128_0); coefficients128_4 = drflac__vrevq_s32(coefficients128_4); coefficients128_8 = drflac__vrevq_s32(coefficients128_8); } while (pDecodedSamples < pDecodedSamplesEnd) { int32x4_t prediction128; int32x2_t prediction64; uint32x4_t zeroCountPart128; uint32x4_t riceParamPart128; if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[0], &riceParamParts[0]) || !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[1], &riceParamParts[1]) || !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[2], &riceParamParts[2]) || !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[3], &riceParamParts[3])) { return DRFLAC_FALSE; } zeroCountPart128 = vld1q_u32(zeroCountParts); riceParamPart128 = vld1q_u32(riceParamParts); riceParamPart128 = vandq_u32(riceParamPart128, riceParamMask128); riceParamPart128 = vorrq_u32(riceParamPart128, vshlq_u32(zeroCountPart128, riceParam128)); riceParamPart128 = veorq_u32(vshrq_n_u32(riceParamPart128, 1), vaddq_u32(drflac__vnotq_u32(vandq_u32(riceParamPart128, one128)), one128)); if (order <= 4) { for (i = 0; i < 4; i += 1) { prediction128 = vmulq_s32(coefficients128_0, samples128_0); prediction64 = drflac__vhaddq_s32(prediction128); prediction64 = vshl_s32(prediction64, shift64); prediction64 = vadd_s32(prediction64, vget_low_s32(vreinterpretq_s32_u32(riceParamPart128))); samples128_0 = drflac__valignrq_s32_1(vcombine_s32(prediction64, vdup_n_s32(0)), samples128_0); riceParamPart128 = drflac__valignrq_u32_1(vdupq_n_u32(0), riceParamPart128); } } else if (order <= 8) { for (i = 0; i < 4; i += 1) { prediction128 = vmulq_s32(coefficients128_4, samples128_4); prediction128 = vmlaq_s32(prediction128, coefficients128_0, samples128_0); prediction64 = drflac__vhaddq_s32(prediction128); prediction64 = vshl_s32(prediction64, shift64); prediction64 = vadd_s32(prediction64, vget_low_s32(vreinterpretq_s32_u32(riceParamPart128))); samples128_4 = drflac__valignrq_s32_1(samples128_0, samples128_4); samples128_0 = drflac__valignrq_s32_1(vcombine_s32(prediction64, vdup_n_s32(0)), samples128_0); riceParamPart128 = drflac__valignrq_u32_1(vdupq_n_u32(0), riceParamPart128); } } else { for (i = 0; i < 4; i += 1) { prediction128 = vmulq_s32(coefficients128_8, samples128_8); prediction128 = vmlaq_s32(prediction128, coefficients128_4, samples128_4); prediction128 = vmlaq_s32(prediction128, coefficients128_0, samples128_0); prediction64 = drflac__vhaddq_s32(prediction128); prediction64 = vshl_s32(prediction64, shift64); prediction64 = vadd_s32(prediction64, vget_low_s32(vreinterpretq_s32_u32(riceParamPart128))); samples128_8 = drflac__valignrq_s32_1(samples128_4, samples128_8); samples128_4 = drflac__valignrq_s32_1(samples128_0, samples128_4); samples128_0 = drflac__valignrq_s32_1(vcombine_s32(prediction64, vdup_n_s32(0)), samples128_0); riceParamPart128 = drflac__valignrq_u32_1(vdupq_n_u32(0), riceParamPart128); } } vst1q_s32(pDecodedSamples, samples128_0); pDecodedSamples += 4; } i = (count & ~3); while (i < (int)count) { if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[0], &riceParamParts[0])) { return DRFLAC_FALSE; } riceParamParts[0] &= riceParamMask; riceParamParts[0] |= (zeroCountParts[0] << riceParam); riceParamParts[0] = (riceParamParts[0] >> 1) ^ t[riceParamParts[0] & 0x01]; pDecodedSamples[0] = riceParamParts[0] + drflac__calculate_prediction_32(order, shift, coefficients, pDecodedSamples); i += 1; pDecodedSamples += 1; } return DRFLAC_TRUE; } static drflac_bool32 drflac__decode_samples_with_residual__rice__neon_64(drflac_bs* bs, drflac_uint32 count, drflac_uint8 riceParam, drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, drflac_int32* pSamplesOut) { int i; drflac_uint32 riceParamMask; drflac_int32* pDecodedSamples = pSamplesOut; drflac_int32* pDecodedSamplesEnd = pSamplesOut + (count & ~3); drflac_uint32 zeroCountParts[4]; drflac_uint32 riceParamParts[4]; int32x4_t coefficients128_0; int32x4_t coefficients128_4; int32x4_t coefficients128_8; int32x4_t samples128_0; int32x4_t samples128_4; int32x4_t samples128_8; uint32x4_t riceParamMask128; int32x4_t riceParam128; int64x1_t shift64; uint32x4_t one128; int64x2_t prediction128 = { 0 }; uint32x4_t zeroCountPart128; uint32x4_t riceParamPart128; const drflac_uint32 t[2] = {0x00000000, 0xFFFFFFFF}; riceParamMask = (drflac_uint32)~((~0UL) << riceParam); riceParamMask128 = vdupq_n_u32(riceParamMask); riceParam128 = vdupq_n_s32(riceParam); shift64 = vdup_n_s64(-shift); one128 = vdupq_n_u32(1); { int runningOrder = order; drflac_int32 tempC[4] = {0, 0, 0, 0}; drflac_int32 tempS[4] = {0, 0, 0, 0}; if (runningOrder >= 4) { coefficients128_0 = vld1q_s32(coefficients + 0); samples128_0 = vld1q_s32(pSamplesOut - 4); runningOrder -= 4; } else { switch (runningOrder) { case 3: tempC[2] = coefficients[2]; tempS[1] = pSamplesOut[-3]; case 2: tempC[1] = coefficients[1]; tempS[2] = pSamplesOut[-2]; case 1: tempC[0] = coefficients[0]; tempS[3] = pSamplesOut[-1]; } coefficients128_0 = vld1q_s32(tempC); samples128_0 = vld1q_s32(tempS); runningOrder = 0; } if (runningOrder >= 4) { coefficients128_4 = vld1q_s32(coefficients + 4); samples128_4 = vld1q_s32(pSamplesOut - 8); runningOrder -= 4; } else { switch (runningOrder) { case 3: tempC[2] = coefficients[6]; tempS[1] = pSamplesOut[-7]; case 2: tempC[1] = coefficients[5]; tempS[2] = pSamplesOut[-6]; case 1: tempC[0] = coefficients[4]; tempS[3] = pSamplesOut[-5]; } coefficients128_4 = vld1q_s32(tempC); samples128_4 = vld1q_s32(tempS); runningOrder = 0; } if (runningOrder == 4) { coefficients128_8 = vld1q_s32(coefficients + 8); samples128_8 = vld1q_s32(pSamplesOut - 12); runningOrder -= 4; } else { switch (runningOrder) { case 3: tempC[2] = coefficients[10]; tempS[1] = pSamplesOut[-11]; case 2: tempC[1] = coefficients[ 9]; tempS[2] = pSamplesOut[-10]; case 1: tempC[0] = coefficients[ 8]; tempS[3] = pSamplesOut[- 9]; } coefficients128_8 = vld1q_s32(tempC); samples128_8 = vld1q_s32(tempS); runningOrder = 0; } coefficients128_0 = drflac__vrevq_s32(coefficients128_0); coefficients128_4 = drflac__vrevq_s32(coefficients128_4); coefficients128_8 = drflac__vrevq_s32(coefficients128_8); } while (pDecodedSamples < pDecodedSamplesEnd) { if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[0], &riceParamParts[0]) || !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[1], &riceParamParts[1]) || !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[2], &riceParamParts[2]) || !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[3], &riceParamParts[3])) { return DRFLAC_FALSE; } zeroCountPart128 = vld1q_u32(zeroCountParts); riceParamPart128 = vld1q_u32(riceParamParts); riceParamPart128 = vandq_u32(riceParamPart128, riceParamMask128); riceParamPart128 = vorrq_u32(riceParamPart128, vshlq_u32(zeroCountPart128, riceParam128)); riceParamPart128 = veorq_u32(vshrq_n_u32(riceParamPart128, 1), vaddq_u32(drflac__vnotq_u32(vandq_u32(riceParamPart128, one128)), one128)); for (i = 0; i < 4; i += 1) { int64x1_t prediction64; prediction128 = veorq_s64(prediction128, prediction128); switch (order) { case 12: case 11: prediction128 = vaddq_s64(prediction128, vmull_s32(vget_low_s32(coefficients128_8), vget_low_s32(samples128_8))); case 10: case 9: prediction128 = vaddq_s64(prediction128, vmull_s32(vget_high_s32(coefficients128_8), vget_high_s32(samples128_8))); case 8: case 7: prediction128 = vaddq_s64(prediction128, vmull_s32(vget_low_s32(coefficients128_4), vget_low_s32(samples128_4))); case 6: case 5: prediction128 = vaddq_s64(prediction128, vmull_s32(vget_high_s32(coefficients128_4), vget_high_s32(samples128_4))); case 4: case 3: prediction128 = vaddq_s64(prediction128, vmull_s32(vget_low_s32(coefficients128_0), vget_low_s32(samples128_0))); case 2: case 1: prediction128 = vaddq_s64(prediction128, vmull_s32(vget_high_s32(coefficients128_0), vget_high_s32(samples128_0))); } prediction64 = drflac__vhaddq_s64(prediction128); prediction64 = vshl_s64(prediction64, shift64); prediction64 = vadd_s64(prediction64, vdup_n_s64(vgetq_lane_u32(riceParamPart128, 0))); samples128_8 = drflac__valignrq_s32_1(samples128_4, samples128_8); samples128_4 = drflac__valignrq_s32_1(samples128_0, samples128_4); samples128_0 = drflac__valignrq_s32_1(vcombine_s32(vreinterpret_s32_s64(prediction64), vdup_n_s32(0)), samples128_0); riceParamPart128 = drflac__valignrq_u32_1(vdupq_n_u32(0), riceParamPart128); } vst1q_s32(pDecodedSamples, samples128_0); pDecodedSamples += 4; } i = (count & ~3); while (i < (int)count) { if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[0], &riceParamParts[0])) { return DRFLAC_FALSE; } riceParamParts[0] &= riceParamMask; riceParamParts[0] |= (zeroCountParts[0] << riceParam); riceParamParts[0] = (riceParamParts[0] >> 1) ^ t[riceParamParts[0] & 0x01]; pDecodedSamples[0] = riceParamParts[0] + drflac__calculate_prediction_64(order, shift, coefficients, pDecodedSamples); i += 1; pDecodedSamples += 1; } return DRFLAC_TRUE; } static drflac_bool32 drflac__decode_samples_with_residual__rice__neon(drflac_bs* bs, drflac_uint32 bitsPerSample, drflac_uint32 count, drflac_uint8 riceParam, drflac_uint32 lpcOrder, drflac_int32 lpcShift, drflac_uint32 lpcPrecision, const drflac_int32* coefficients, drflac_int32* pSamplesOut) { DRFLAC_ASSERT(bs != NULL); DRFLAC_ASSERT(pSamplesOut != NULL); if (lpcOrder > 0 && lpcOrder <= 12) { if (drflac__use_64_bit_prediction(bitsPerSample, lpcOrder, lpcPrecision)) { return drflac__decode_samples_with_residual__rice__neon_64(bs, count, riceParam, lpcOrder, lpcShift, coefficients, pSamplesOut); } else { return drflac__decode_samples_with_residual__rice__neon_32(bs, count, riceParam, lpcOrder, lpcShift, coefficients, pSamplesOut); } } else { return drflac__decode_samples_with_residual__rice__scalar(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pSamplesOut); } } #endif static drflac_bool32 drflac__decode_samples_with_residual__rice(drflac_bs* bs, drflac_uint32 bitsPerSample, drflac_uint32 count, drflac_uint8 riceParam, drflac_uint32 lpcOrder, drflac_int32 lpcShift, drflac_uint32 lpcPrecision, const drflac_int32* coefficients, drflac_int32* pSamplesOut) { #if defined(DRFLAC_SUPPORT_SSE41) if (drflac__gIsSSE41Supported) { return drflac__decode_samples_with_residual__rice__sse41(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pSamplesOut); } else #elif defined(DRFLAC_SUPPORT_NEON) if (drflac__gIsNEONSupported) { return drflac__decode_samples_with_residual__rice__neon(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pSamplesOut); } else #endif { #if 0 return drflac__decode_samples_with_residual__rice__reference(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pSamplesOut); #else return drflac__decode_samples_with_residual__rice__scalar(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pSamplesOut); #endif } } static drflac_bool32 drflac__read_and_seek_residual__rice(drflac_bs* bs, drflac_uint32 count, drflac_uint8 riceParam) { drflac_uint32 i; DRFLAC_ASSERT(bs != NULL); for (i = 0; i < count; ++i) { if (!drflac__seek_rice_parts(bs, riceParam)) { return DRFLAC_FALSE; } } return DRFLAC_TRUE; } #if defined(__clang__) __attribute__((no_sanitize("signed-integer-overflow"))) #endif static drflac_bool32 drflac__decode_samples_with_residual__unencoded(drflac_bs* bs, drflac_uint32 bitsPerSample, drflac_uint32 count, drflac_uint8 unencodedBitsPerSample, drflac_uint32 lpcOrder, drflac_int32 lpcShift, drflac_uint32 lpcPrecision, const drflac_int32* coefficients, drflac_int32* pSamplesOut) { drflac_uint32 i; DRFLAC_ASSERT(bs != NULL); DRFLAC_ASSERT(unencodedBitsPerSample <= 31); DRFLAC_ASSERT(pSamplesOut != NULL); for (i = 0; i < count; ++i) { if (unencodedBitsPerSample > 0) { if (!drflac__read_int32(bs, unencodedBitsPerSample, pSamplesOut + i)) { return DRFLAC_FALSE; } } else { pSamplesOut[i] = 0; } if (drflac__use_64_bit_prediction(bitsPerSample, lpcOrder, lpcPrecision)) { pSamplesOut[i] += drflac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + i); } else { pSamplesOut[i] += drflac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + i); } } return DRFLAC_TRUE; } static drflac_bool32 drflac__decode_samples_with_residual(drflac_bs* bs, drflac_uint32 bitsPerSample, drflac_uint32 blockSize, drflac_uint32 lpcOrder, drflac_int32 lpcShift, drflac_uint32 lpcPrecision, const drflac_int32* coefficients, drflac_int32* pDecodedSamples) { drflac_uint8 residualMethod; drflac_uint8 partitionOrder; drflac_uint32 samplesInPartition; drflac_uint32 partitionsRemaining; DRFLAC_ASSERT(bs != NULL); DRFLAC_ASSERT(blockSize != 0); DRFLAC_ASSERT(pDecodedSamples != NULL); if (!drflac__read_uint8(bs, 2, &residualMethod)) { return DRFLAC_FALSE; } if (residualMethod != DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE && residualMethod != DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2) { return DRFLAC_FALSE; } pDecodedSamples += lpcOrder; if (!drflac__read_uint8(bs, 4, &partitionOrder)) { return DRFLAC_FALSE; } if (partitionOrder > 8) { return DRFLAC_FALSE; } if ((blockSize / (1 << partitionOrder)) < lpcOrder) { return DRFLAC_FALSE; } samplesInPartition = (blockSize / (1 << partitionOrder)) - lpcOrder; partitionsRemaining = (1 << partitionOrder); for (;;) { drflac_uint8 riceParam = 0; if (residualMethod == DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE) { if (!drflac__read_uint8(bs, 4, &riceParam)) { return DRFLAC_FALSE; } if (riceParam == 15) { riceParam = 0xFF; } } else if (residualMethod == DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2) { if (!drflac__read_uint8(bs, 5, &riceParam)) { return DRFLAC_FALSE; } if (riceParam == 31) { riceParam = 0xFF; } } if (riceParam != 0xFF) { if (!drflac__decode_samples_with_residual__rice(bs, bitsPerSample, samplesInPartition, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pDecodedSamples)) { return DRFLAC_FALSE; } } else { drflac_uint8 unencodedBitsPerSample = 0; if (!drflac__read_uint8(bs, 5, &unencodedBitsPerSample)) { return DRFLAC_FALSE; } if (!drflac__decode_samples_with_residual__unencoded(bs, bitsPerSample, samplesInPartition, unencodedBitsPerSample, lpcOrder, lpcShift, lpcPrecision, coefficients, pDecodedSamples)) { return DRFLAC_FALSE; } } pDecodedSamples += samplesInPartition; if (partitionsRemaining == 1) { break; } partitionsRemaining -= 1; if (partitionOrder != 0) { samplesInPartition = blockSize / (1 << partitionOrder); } } return DRFLAC_TRUE; } static drflac_bool32 drflac__read_and_seek_residual(drflac_bs* bs, drflac_uint32 blockSize, drflac_uint32 order) { drflac_uint8 residualMethod; drflac_uint8 partitionOrder; drflac_uint32 samplesInPartition; drflac_uint32 partitionsRemaining; DRFLAC_ASSERT(bs != NULL); DRFLAC_ASSERT(blockSize != 0); if (!drflac__read_uint8(bs, 2, &residualMethod)) { return DRFLAC_FALSE; } if (residualMethod != DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE && residualMethod != DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2) { return DRFLAC_FALSE; } if (!drflac__read_uint8(bs, 4, &partitionOrder)) { return DRFLAC_FALSE; } if (partitionOrder > 8) { return DRFLAC_FALSE; } if ((blockSize / (1 << partitionOrder)) <= order) { return DRFLAC_FALSE; } samplesInPartition = (blockSize / (1 << partitionOrder)) - order; partitionsRemaining = (1 << partitionOrder); for (;;) { drflac_uint8 riceParam = 0; if (residualMethod == DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE) { if (!drflac__read_uint8(bs, 4, &riceParam)) { return DRFLAC_FALSE; } if (riceParam == 15) { riceParam = 0xFF; } } else if (residualMethod == DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2) { if (!drflac__read_uint8(bs, 5, &riceParam)) { return DRFLAC_FALSE; } if (riceParam == 31) { riceParam = 0xFF; } } if (riceParam != 0xFF) { if (!drflac__read_and_seek_residual__rice(bs, samplesInPartition, riceParam)) { return DRFLAC_FALSE; } } else { drflac_uint8 unencodedBitsPerSample = 0; if (!drflac__read_uint8(bs, 5, &unencodedBitsPerSample)) { return DRFLAC_FALSE; } if (!drflac__seek_bits(bs, unencodedBitsPerSample * samplesInPartition)) { return DRFLAC_FALSE; } } if (partitionsRemaining == 1) { break; } partitionsRemaining -= 1; samplesInPartition = blockSize / (1 << partitionOrder); } return DRFLAC_TRUE; } static drflac_bool32 drflac__decode_samples__constant(drflac_bs* bs, drflac_uint32 blockSize, drflac_uint32 subframeBitsPerSample, drflac_int32* pDecodedSamples) { drflac_uint32 i; drflac_int32 sample; if (!drflac__read_int32(bs, subframeBitsPerSample, &sample)) { return DRFLAC_FALSE; } for (i = 0; i < blockSize; ++i) { pDecodedSamples[i] = sample; } return DRFLAC_TRUE; } static drflac_bool32 drflac__decode_samples__verbatim(drflac_bs* bs, drflac_uint32 blockSize, drflac_uint32 subframeBitsPerSample, drflac_int32* pDecodedSamples) { drflac_uint32 i; for (i = 0; i < blockSize; ++i) { drflac_int32 sample; if (!drflac__read_int32(bs, subframeBitsPerSample, &sample)) { return DRFLAC_FALSE; } pDecodedSamples[i] = sample; } return DRFLAC_TRUE; } static drflac_bool32 drflac__decode_samples__fixed(drflac_bs* bs, drflac_uint32 blockSize, drflac_uint32 subframeBitsPerSample, drflac_uint8 lpcOrder, drflac_int32* pDecodedSamples) { drflac_uint32 i; static drflac_int32 lpcCoefficientsTable[5][4] = { {0, 0, 0, 0}, {1, 0, 0, 0}, {2, -1, 0, 0}, {3, -3, 1, 0}, {4, -6, 4, -1} }; for (i = 0; i < lpcOrder; ++i) { drflac_int32 sample; if (!drflac__read_int32(bs, subframeBitsPerSample, &sample)) { return DRFLAC_FALSE; } pDecodedSamples[i] = sample; } if (!drflac__decode_samples_with_residual(bs, subframeBitsPerSample, blockSize, lpcOrder, 0, 4, lpcCoefficientsTable[lpcOrder], pDecodedSamples)) { return DRFLAC_FALSE; } return DRFLAC_TRUE; } static drflac_bool32 drflac__decode_samples__lpc(drflac_bs* bs, drflac_uint32 blockSize, drflac_uint32 bitsPerSample, drflac_uint8 lpcOrder, drflac_int32* pDecodedSamples) { drflac_uint8 i; drflac_uint8 lpcPrecision; drflac_int8 lpcShift; drflac_int32 coefficients[32]; for (i = 0; i < lpcOrder; ++i) { drflac_int32 sample; if (!drflac__read_int32(bs, bitsPerSample, &sample)) { return DRFLAC_FALSE; } pDecodedSamples[i] = sample; } if (!drflac__read_uint8(bs, 4, &lpcPrecision)) { return DRFLAC_FALSE; } if (lpcPrecision == 15) { return DRFLAC_FALSE; } lpcPrecision += 1; if (!drflac__read_int8(bs, 5, &lpcShift)) { return DRFLAC_FALSE; } if (lpcShift < 0) { return DRFLAC_FALSE; } DRFLAC_ZERO_MEMORY(coefficients, sizeof(coefficients)); for (i = 0; i < lpcOrder; ++i) { if (!drflac__read_int32(bs, lpcPrecision, coefficients + i)) { return DRFLAC_FALSE; } } if (!drflac__decode_samples_with_residual(bs, bitsPerSample, blockSize, lpcOrder, lpcShift, lpcPrecision, coefficients, pDecodedSamples)) { return DRFLAC_FALSE; } return DRFLAC_TRUE; } static drflac_bool32 drflac__read_next_flac_frame_header(drflac_bs* bs, drflac_uint8 streaminfoBitsPerSample, drflac_frame_header* header) { const drflac_uint32 sampleRateTable[12] = {0, 88200, 176400, 192000, 8000, 16000, 22050, 24000, 32000, 44100, 48000, 96000}; const drflac_uint8 bitsPerSampleTable[8] = {0, 8, 12, (drflac_uint8)-1, 16, 20, 24, (drflac_uint8)-1}; DRFLAC_ASSERT(bs != NULL); DRFLAC_ASSERT(header != NULL); for (;;) { drflac_uint8 crc8 = 0xCE; drflac_uint8 reserved = 0; drflac_uint8 blockingStrategy = 0; drflac_uint8 blockSize = 0; drflac_uint8 sampleRate = 0; drflac_uint8 channelAssignment = 0; drflac_uint8 bitsPerSample = 0; drflac_bool32 isVariableBlockSize; if (!drflac__find_and_seek_to_next_sync_code(bs)) { return DRFLAC_FALSE; } if (!drflac__read_uint8(bs, 1, &reserved)) { return DRFLAC_FALSE; } if (reserved == 1) { continue; } crc8 = drflac_crc8(crc8, reserved, 1); if (!drflac__read_uint8(bs, 1, &blockingStrategy)) { return DRFLAC_FALSE; } crc8 = drflac_crc8(crc8, blockingStrategy, 1); if (!drflac__read_uint8(bs, 4, &blockSize)) { return DRFLAC_FALSE; } if (blockSize == 0) { continue; } crc8 = drflac_crc8(crc8, blockSize, 4); if (!drflac__read_uint8(bs, 4, &sampleRate)) { return DRFLAC_FALSE; } crc8 = drflac_crc8(crc8, sampleRate, 4); if (!drflac__read_uint8(bs, 4, &channelAssignment)) { return DRFLAC_FALSE; } if (channelAssignment > 10) { continue; } crc8 = drflac_crc8(crc8, channelAssignment, 4); if (!drflac__read_uint8(bs, 3, &bitsPerSample)) { return DRFLAC_FALSE; } if (bitsPerSample == 3 || bitsPerSample == 7) { continue; } crc8 = drflac_crc8(crc8, bitsPerSample, 3); if (!drflac__read_uint8(bs, 1, &reserved)) { return DRFLAC_FALSE; } if (reserved == 1) { continue; } crc8 = drflac_crc8(crc8, reserved, 1); isVariableBlockSize = blockingStrategy == 1; if (isVariableBlockSize) { drflac_uint64 pcmFrameNumber; drflac_result result = drflac__read_utf8_coded_number(bs, &pcmFrameNumber, &crc8); if (result != DRFLAC_SUCCESS) { if (result == DRFLAC_AT_END) { return DRFLAC_FALSE; } else { continue; } } header->flacFrameNumber = 0; header->pcmFrameNumber = pcmFrameNumber; } else { drflac_uint64 flacFrameNumber = 0; drflac_result result = drflac__read_utf8_coded_number(bs, &flacFrameNumber, &crc8); if (result != DRFLAC_SUCCESS) { if (result == DRFLAC_AT_END) { return DRFLAC_FALSE; } else { continue; } } header->flacFrameNumber = (drflac_uint32)flacFrameNumber; header->pcmFrameNumber = 0; } DRFLAC_ASSERT(blockSize > 0); if (blockSize == 1) { header->blockSizeInPCMFrames = 192; } else if (blockSize <= 5) { DRFLAC_ASSERT(blockSize >= 2); header->blockSizeInPCMFrames = 576 * (1 << (blockSize - 2)); } else if (blockSize == 6) { if (!drflac__read_uint16(bs, 8, &header->blockSizeInPCMFrames)) { return DRFLAC_FALSE; } crc8 = drflac_crc8(crc8, header->blockSizeInPCMFrames, 8); header->blockSizeInPCMFrames += 1; } else if (blockSize == 7) { if (!drflac__read_uint16(bs, 16, &header->blockSizeInPCMFrames)) { return DRFLAC_FALSE; } crc8 = drflac_crc8(crc8, header->blockSizeInPCMFrames, 16); if (header->blockSizeInPCMFrames == 0xFFFF) { return DRFLAC_FALSE; } header->blockSizeInPCMFrames += 1; } else { DRFLAC_ASSERT(blockSize >= 8); header->blockSizeInPCMFrames = 256 * (1 << (blockSize - 8)); } if (sampleRate <= 11) { header->sampleRate = sampleRateTable[sampleRate]; } else if (sampleRate == 12) { if (!drflac__read_uint32(bs, 8, &header->sampleRate)) { return DRFLAC_FALSE; } crc8 = drflac_crc8(crc8, header->sampleRate, 8); header->sampleRate *= 1000; } else if (sampleRate == 13) { if (!drflac__read_uint32(bs, 16, &header->sampleRate)) { return DRFLAC_FALSE; } crc8 = drflac_crc8(crc8, header->sampleRate, 16); } else if (sampleRate == 14) { if (!drflac__read_uint32(bs, 16, &header->sampleRate)) { return DRFLAC_FALSE; } crc8 = drflac_crc8(crc8, header->sampleRate, 16); header->sampleRate *= 10; } else { continue; } header->channelAssignment = channelAssignment; header->bitsPerSample = bitsPerSampleTable[bitsPerSample]; if (header->bitsPerSample == 0) { header->bitsPerSample = streaminfoBitsPerSample; } if (header->bitsPerSample != streaminfoBitsPerSample) { return DRFLAC_FALSE; } if (!drflac__read_uint8(bs, 8, &header->crc8)) { return DRFLAC_FALSE; } #ifndef DR_FLAC_NO_CRC if (header->crc8 != crc8) { continue; } #endif return DRFLAC_TRUE; } } static drflac_bool32 drflac__read_subframe_header(drflac_bs* bs, drflac_subframe* pSubframe) { drflac_uint8 header; int type; if (!drflac__read_uint8(bs, 8, &header)) { return DRFLAC_FALSE; } if ((header & 0x80) != 0) { return DRFLAC_FALSE; } type = (header & 0x7E) >> 1; if (type == 0) { pSubframe->subframeType = DRFLAC_SUBFRAME_CONSTANT; } else if (type == 1) { pSubframe->subframeType = DRFLAC_SUBFRAME_VERBATIM; } else { if ((type & 0x20) != 0) { pSubframe->subframeType = DRFLAC_SUBFRAME_LPC; pSubframe->lpcOrder = (drflac_uint8)(type & 0x1F) + 1; } else if ((type & 0x08) != 0) { pSubframe->subframeType = DRFLAC_SUBFRAME_FIXED; pSubframe->lpcOrder = (drflac_uint8)(type & 0x07); if (pSubframe->lpcOrder > 4) { pSubframe->subframeType = DRFLAC_SUBFRAME_RESERVED; pSubframe->lpcOrder = 0; } } else { pSubframe->subframeType = DRFLAC_SUBFRAME_RESERVED; } } if (pSubframe->subframeType == DRFLAC_SUBFRAME_RESERVED) { return DRFLAC_FALSE; } pSubframe->wastedBitsPerSample = 0; if ((header & 0x01) == 1) { unsigned int wastedBitsPerSample; if (!drflac__seek_past_next_set_bit(bs, &wastedBitsPerSample)) { return DRFLAC_FALSE; } pSubframe->wastedBitsPerSample = (drflac_uint8)wastedBitsPerSample + 1; } return DRFLAC_TRUE; } static drflac_bool32 drflac__decode_subframe(drflac_bs* bs, drflac_frame* frame, int subframeIndex, drflac_int32* pDecodedSamplesOut) { drflac_subframe* pSubframe; drflac_uint32 subframeBitsPerSample; DRFLAC_ASSERT(bs != NULL); DRFLAC_ASSERT(frame != NULL); pSubframe = frame->subframes + subframeIndex; if (!drflac__read_subframe_header(bs, pSubframe)) { return DRFLAC_FALSE; } subframeBitsPerSample = frame->header.bitsPerSample; if ((frame->header.channelAssignment == DRFLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE || frame->header.channelAssignment == DRFLAC_CHANNEL_ASSIGNMENT_MID_SIDE) && subframeIndex == 1) { subframeBitsPerSample += 1; } else if (frame->header.channelAssignment == DRFLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE && subframeIndex == 0) { subframeBitsPerSample += 1; } if (subframeBitsPerSample > 32) { return DRFLAC_FALSE; } if (pSubframe->wastedBitsPerSample >= subframeBitsPerSample) { return DRFLAC_FALSE; } subframeBitsPerSample -= pSubframe->wastedBitsPerSample; pSubframe->pSamplesS32 = pDecodedSamplesOut; switch (pSubframe->subframeType) { case DRFLAC_SUBFRAME_CONSTANT: { drflac__decode_samples__constant(bs, frame->header.blockSizeInPCMFrames, subframeBitsPerSample, pSubframe->pSamplesS32); } break; case DRFLAC_SUBFRAME_VERBATIM: { drflac__decode_samples__verbatim(bs, frame->header.blockSizeInPCMFrames, subframeBitsPerSample, pSubframe->pSamplesS32); } break; case DRFLAC_SUBFRAME_FIXED: { drflac__decode_samples__fixed(bs, frame->header.blockSizeInPCMFrames, subframeBitsPerSample, pSubframe->lpcOrder, pSubframe->pSamplesS32); } break; case DRFLAC_SUBFRAME_LPC: { drflac__decode_samples__lpc(bs, frame->header.blockSizeInPCMFrames, subframeBitsPerSample, pSubframe->lpcOrder, pSubframe->pSamplesS32); } break; default: return DRFLAC_FALSE; } return DRFLAC_TRUE; } static drflac_bool32 drflac__seek_subframe(drflac_bs* bs, drflac_frame* frame, int subframeIndex) { drflac_subframe* pSubframe; drflac_uint32 subframeBitsPerSample; DRFLAC_ASSERT(bs != NULL); DRFLAC_ASSERT(frame != NULL); pSubframe = frame->subframes + subframeIndex; if (!drflac__read_subframe_header(bs, pSubframe)) { return DRFLAC_FALSE; } subframeBitsPerSample = frame->header.bitsPerSample; if ((frame->header.channelAssignment == DRFLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE || frame->header.channelAssignment == DRFLAC_CHANNEL_ASSIGNMENT_MID_SIDE) && subframeIndex == 1) { subframeBitsPerSample += 1; } else if (frame->header.channelAssignment == DRFLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE && subframeIndex == 0) { subframeBitsPerSample += 1; } if (pSubframe->wastedBitsPerSample >= subframeBitsPerSample) { return DRFLAC_FALSE; } subframeBitsPerSample -= pSubframe->wastedBitsPerSample; pSubframe->pSamplesS32 = NULL; switch (pSubframe->subframeType) { case DRFLAC_SUBFRAME_CONSTANT: { if (!drflac__seek_bits(bs, subframeBitsPerSample)) { return DRFLAC_FALSE; } } break; case DRFLAC_SUBFRAME_VERBATIM: { unsigned int bitsToSeek = frame->header.blockSizeInPCMFrames * subframeBitsPerSample; if (!drflac__seek_bits(bs, bitsToSeek)) { return DRFLAC_FALSE; } } break; case DRFLAC_SUBFRAME_FIXED: { unsigned int bitsToSeek = pSubframe->lpcOrder * subframeBitsPerSample; if (!drflac__seek_bits(bs, bitsToSeek)) { return DRFLAC_FALSE; } if (!drflac__read_and_seek_residual(bs, frame->header.blockSizeInPCMFrames, pSubframe->lpcOrder)) { return DRFLAC_FALSE; } } break; case DRFLAC_SUBFRAME_LPC: { drflac_uint8 lpcPrecision; unsigned int bitsToSeek = pSubframe->lpcOrder * subframeBitsPerSample; if (!drflac__seek_bits(bs, bitsToSeek)) { return DRFLAC_FALSE; } if (!drflac__read_uint8(bs, 4, &lpcPrecision)) { return DRFLAC_FALSE; } if (lpcPrecision == 15) { return DRFLAC_FALSE; } lpcPrecision += 1; bitsToSeek = (pSubframe->lpcOrder * lpcPrecision) + 5; if (!drflac__seek_bits(bs, bitsToSeek)) { return DRFLAC_FALSE; } if (!drflac__read_and_seek_residual(bs, frame->header.blockSizeInPCMFrames, pSubframe->lpcOrder)) { return DRFLAC_FALSE; } } break; default: return DRFLAC_FALSE; } return DRFLAC_TRUE; } static DRFLAC_INLINE drflac_uint8 drflac__get_channel_count_from_channel_assignment(drflac_int8 channelAssignment) { drflac_uint8 lookup[] = {1, 2, 3, 4, 5, 6, 7, 8, 2, 2, 2}; DRFLAC_ASSERT(channelAssignment <= 10); return lookup[channelAssignment]; } static drflac_result drflac__decode_flac_frame(drflac* pFlac) { int channelCount; int i; drflac_uint8 paddingSizeInBits; drflac_uint16 desiredCRC16; #ifndef DR_FLAC_NO_CRC drflac_uint16 actualCRC16; #endif DRFLAC_ZERO_MEMORY(pFlac->currentFLACFrame.subframes, sizeof(pFlac->currentFLACFrame.subframes)); if (pFlac->currentFLACFrame.header.blockSizeInPCMFrames > pFlac->maxBlockSizeInPCMFrames) { return DRFLAC_ERROR; } channelCount = drflac__get_channel_count_from_channel_assignment(pFlac->currentFLACFrame.header.channelAssignment); if (channelCount != (int)pFlac->channels) { return DRFLAC_ERROR; } for (i = 0; i < channelCount; ++i) { if (!drflac__decode_subframe(&pFlac->bs, &pFlac->currentFLACFrame, i, pFlac->pDecodedSamples + (pFlac->currentFLACFrame.header.blockSizeInPCMFrames * i))) { return DRFLAC_ERROR; } } paddingSizeInBits = (drflac_uint8)(DRFLAC_CACHE_L1_BITS_REMAINING(&pFlac->bs) & 7); if (paddingSizeInBits > 0) { drflac_uint8 padding = 0; if (!drflac__read_uint8(&pFlac->bs, paddingSizeInBits, &padding)) { return DRFLAC_AT_END; } } #ifndef DR_FLAC_NO_CRC actualCRC16 = drflac__flush_crc16(&pFlac->bs); #endif if (!drflac__read_uint16(&pFlac->bs, 16, &desiredCRC16)) { return DRFLAC_AT_END; } #ifndef DR_FLAC_NO_CRC if (actualCRC16 != desiredCRC16) { return DRFLAC_CRC_MISMATCH; } #endif pFlac->currentFLACFrame.pcmFramesRemaining = pFlac->currentFLACFrame.header.blockSizeInPCMFrames; return DRFLAC_SUCCESS; } static drflac_result drflac__seek_flac_frame(drflac* pFlac) { int channelCount; int i; drflac_uint16 desiredCRC16; #ifndef DR_FLAC_NO_CRC drflac_uint16 actualCRC16; #endif channelCount = drflac__get_channel_count_from_channel_assignment(pFlac->currentFLACFrame.header.channelAssignment); for (i = 0; i < channelCount; ++i) { if (!drflac__seek_subframe(&pFlac->bs, &pFlac->currentFLACFrame, i)) { return DRFLAC_ERROR; } } if (!drflac__seek_bits(&pFlac->bs, DRFLAC_CACHE_L1_BITS_REMAINING(&pFlac->bs) & 7)) { return DRFLAC_ERROR; } #ifndef DR_FLAC_NO_CRC actualCRC16 = drflac__flush_crc16(&pFlac->bs); #endif if (!drflac__read_uint16(&pFlac->bs, 16, &desiredCRC16)) { return DRFLAC_AT_END; } #ifndef DR_FLAC_NO_CRC if (actualCRC16 != desiredCRC16) { return DRFLAC_CRC_MISMATCH; } #endif return DRFLAC_SUCCESS; } static drflac_bool32 drflac__read_and_decode_next_flac_frame(drflac* pFlac) { DRFLAC_ASSERT(pFlac != NULL); for (;;) { drflac_result result; if (!drflac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { return DRFLAC_FALSE; } result = drflac__decode_flac_frame(pFlac); if (result != DRFLAC_SUCCESS) { if (result == DRFLAC_CRC_MISMATCH) { continue; } else { return DRFLAC_FALSE; } } return DRFLAC_TRUE; } } static void drflac__get_pcm_frame_range_of_current_flac_frame(drflac* pFlac, drflac_uint64* pFirstPCMFrame, drflac_uint64* pLastPCMFrame) { drflac_uint64 firstPCMFrame; drflac_uint64 lastPCMFrame; DRFLAC_ASSERT(pFlac != NULL); firstPCMFrame = pFlac->currentFLACFrame.header.pcmFrameNumber; if (firstPCMFrame == 0) { firstPCMFrame = ((drflac_uint64)pFlac->currentFLACFrame.header.flacFrameNumber) * pFlac->maxBlockSizeInPCMFrames; } lastPCMFrame = firstPCMFrame + pFlac->currentFLACFrame.header.blockSizeInPCMFrames; if (lastPCMFrame > 0) { lastPCMFrame -= 1; } if (pFirstPCMFrame) { *pFirstPCMFrame = firstPCMFrame; } if (pLastPCMFrame) { *pLastPCMFrame = lastPCMFrame; } } static drflac_bool32 drflac__seek_to_first_frame(drflac* pFlac) { drflac_bool32 result; DRFLAC_ASSERT(pFlac != NULL); result = drflac__seek_to_byte(&pFlac->bs, pFlac->firstFLACFramePosInBytes); DRFLAC_ZERO_MEMORY(&pFlac->currentFLACFrame, sizeof(pFlac->currentFLACFrame)); pFlac->currentPCMFrame = 0; return result; } static DRFLAC_INLINE drflac_result drflac__seek_to_next_flac_frame(drflac* pFlac) { DRFLAC_ASSERT(pFlac != NULL); return drflac__seek_flac_frame(pFlac); } static drflac_uint64 drflac__seek_forward_by_pcm_frames(drflac* pFlac, drflac_uint64 pcmFramesToSeek) { drflac_uint64 pcmFramesRead = 0; while (pcmFramesToSeek > 0) { if (pFlac->currentFLACFrame.pcmFramesRemaining == 0) { if (!drflac__read_and_decode_next_flac_frame(pFlac)) { break; } } else { if (pFlac->currentFLACFrame.pcmFramesRemaining > pcmFramesToSeek) { pcmFramesRead += pcmFramesToSeek; pFlac->currentFLACFrame.pcmFramesRemaining -= (drflac_uint32)pcmFramesToSeek; pcmFramesToSeek = 0; } else { pcmFramesRead += pFlac->currentFLACFrame.pcmFramesRemaining; pcmFramesToSeek -= pFlac->currentFLACFrame.pcmFramesRemaining; pFlac->currentFLACFrame.pcmFramesRemaining = 0; } } } pFlac->currentPCMFrame += pcmFramesRead; return pcmFramesRead; } static drflac_bool32 drflac__seek_to_pcm_frame__brute_force(drflac* pFlac, drflac_uint64 pcmFrameIndex) { drflac_bool32 isMidFrame = DRFLAC_FALSE; drflac_uint64 runningPCMFrameCount; DRFLAC_ASSERT(pFlac != NULL); if (pcmFrameIndex >= pFlac->currentPCMFrame) { runningPCMFrameCount = pFlac->currentPCMFrame; if (pFlac->currentPCMFrame == 0 && pFlac->currentFLACFrame.pcmFramesRemaining == 0) { if (!drflac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { return DRFLAC_FALSE; } } else { isMidFrame = DRFLAC_TRUE; } } else { runningPCMFrameCount = 0; if (!drflac__seek_to_first_frame(pFlac)) { return DRFLAC_FALSE; } if (!drflac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { return DRFLAC_FALSE; } } for (;;) { drflac_uint64 pcmFrameCountInThisFLACFrame; drflac_uint64 firstPCMFrameInFLACFrame = 0; drflac_uint64 lastPCMFrameInFLACFrame = 0; drflac__get_pcm_frame_range_of_current_flac_frame(pFlac, &firstPCMFrameInFLACFrame, &lastPCMFrameInFLACFrame); pcmFrameCountInThisFLACFrame = (lastPCMFrameInFLACFrame - firstPCMFrameInFLACFrame) + 1; if (pcmFrameIndex < (runningPCMFrameCount + pcmFrameCountInThisFLACFrame)) { drflac_uint64 pcmFramesToDecode = pcmFrameIndex - runningPCMFrameCount; if (!isMidFrame) { drflac_result result = drflac__decode_flac_frame(pFlac); if (result == DRFLAC_SUCCESS) { return drflac__seek_forward_by_pcm_frames(pFlac, pcmFramesToDecode) == pcmFramesToDecode; } else { if (result == DRFLAC_CRC_MISMATCH) { goto next_iteration; } else { return DRFLAC_FALSE; } } } else { return drflac__seek_forward_by_pcm_frames(pFlac, pcmFramesToDecode) == pcmFramesToDecode; } } else { if (!isMidFrame) { drflac_result result = drflac__seek_to_next_flac_frame(pFlac); if (result == DRFLAC_SUCCESS) { runningPCMFrameCount += pcmFrameCountInThisFLACFrame; } else { if (result == DRFLAC_CRC_MISMATCH) { goto next_iteration; } else { return DRFLAC_FALSE; } } } else { runningPCMFrameCount += pFlac->currentFLACFrame.pcmFramesRemaining; pFlac->currentFLACFrame.pcmFramesRemaining = 0; isMidFrame = DRFLAC_FALSE; } if (pcmFrameIndex == pFlac->totalPCMFrameCount && runningPCMFrameCount == pFlac->totalPCMFrameCount) { return DRFLAC_TRUE; } } next_iteration: if (!drflac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { return DRFLAC_FALSE; } } } #if !defined(DR_FLAC_NO_CRC) #define DRFLAC_BINARY_SEARCH_APPROX_COMPRESSION_RATIO 0.6f static drflac_bool32 drflac__seek_to_approximate_flac_frame_to_byte(drflac* pFlac, drflac_uint64 targetByte, drflac_uint64 rangeLo, drflac_uint64 rangeHi, drflac_uint64* pLastSuccessfulSeekOffset) { DRFLAC_ASSERT(pFlac != NULL); DRFLAC_ASSERT(pLastSuccessfulSeekOffset != NULL); DRFLAC_ASSERT(targetByte >= rangeLo); DRFLAC_ASSERT(targetByte <= rangeHi); *pLastSuccessfulSeekOffset = pFlac->firstFLACFramePosInBytes; for (;;) { drflac_uint64 lastTargetByte = targetByte; if (!drflac__seek_to_byte(&pFlac->bs, targetByte)) { if (targetByte == 0) { drflac__seek_to_first_frame(pFlac); return DRFLAC_FALSE; } targetByte = rangeLo + ((rangeHi - rangeLo)/2); rangeHi = targetByte; } else { DRFLAC_ZERO_MEMORY(&pFlac->currentFLACFrame, sizeof(pFlac->currentFLACFrame)); #if 1 if (!drflac__read_and_decode_next_flac_frame(pFlac)) { targetByte = rangeLo + ((rangeHi - rangeLo)/2); rangeHi = targetByte; } else { break; } #else if (!drflac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { targetByte = rangeLo + ((rangeHi - rangeLo)/2); rangeHi = targetByte; } else { break; } #endif } if(targetByte == lastTargetByte) { return DRFLAC_FALSE; } } drflac__get_pcm_frame_range_of_current_flac_frame(pFlac, &pFlac->currentPCMFrame, NULL); DRFLAC_ASSERT(targetByte <= rangeHi); *pLastSuccessfulSeekOffset = targetByte; return DRFLAC_TRUE; } static drflac_bool32 drflac__decode_flac_frame_and_seek_forward_by_pcm_frames(drflac* pFlac, drflac_uint64 offset) { #if 0 if (drflac__decode_flac_frame(pFlac) != DRFLAC_SUCCESS) { if (drflac__read_and_decode_next_flac_frame(pFlac) == DRFLAC_FALSE) { return DRFLAC_FALSE; } } #endif return drflac__seek_forward_by_pcm_frames(pFlac, offset) == offset; } static drflac_bool32 drflac__seek_to_pcm_frame__binary_search_internal(drflac* pFlac, drflac_uint64 pcmFrameIndex, drflac_uint64 byteRangeLo, drflac_uint64 byteRangeHi) { drflac_uint64 targetByte; drflac_uint64 pcmRangeLo = pFlac->totalPCMFrameCount; drflac_uint64 pcmRangeHi = 0; drflac_uint64 lastSuccessfulSeekOffset = (drflac_uint64)-1; drflac_uint64 closestSeekOffsetBeforeTargetPCMFrame = byteRangeLo; drflac_uint32 seekForwardThreshold = (pFlac->maxBlockSizeInPCMFrames != 0) ? pFlac->maxBlockSizeInPCMFrames*2 : 4096; targetByte = byteRangeLo + (drflac_uint64)(((drflac_int64)((pcmFrameIndex - pFlac->currentPCMFrame) * pFlac->channels * pFlac->bitsPerSample)/8.0f) * DRFLAC_BINARY_SEARCH_APPROX_COMPRESSION_RATIO); if (targetByte > byteRangeHi) { targetByte = byteRangeHi; } for (;;) { if (drflac__seek_to_approximate_flac_frame_to_byte(pFlac, targetByte, byteRangeLo, byteRangeHi, &lastSuccessfulSeekOffset)) { drflac_uint64 newPCMRangeLo; drflac_uint64 newPCMRangeHi; drflac__get_pcm_frame_range_of_current_flac_frame(pFlac, &newPCMRangeLo, &newPCMRangeHi); if (pcmRangeLo == newPCMRangeLo) { if (!drflac__seek_to_approximate_flac_frame_to_byte(pFlac, closestSeekOffsetBeforeTargetPCMFrame, closestSeekOffsetBeforeTargetPCMFrame, byteRangeHi, &lastSuccessfulSeekOffset)) { break; } if (drflac__decode_flac_frame_and_seek_forward_by_pcm_frames(pFlac, pcmFrameIndex - pFlac->currentPCMFrame)) { return DRFLAC_TRUE; } else { break; } } pcmRangeLo = newPCMRangeLo; pcmRangeHi = newPCMRangeHi; if (pcmRangeLo <= pcmFrameIndex && pcmRangeHi >= pcmFrameIndex) { if (drflac__decode_flac_frame_and_seek_forward_by_pcm_frames(pFlac, pcmFrameIndex - pFlac->currentPCMFrame) ) { return DRFLAC_TRUE; } else { break; } } else { const float approxCompressionRatio = (drflac_int64)(lastSuccessfulSeekOffset - pFlac->firstFLACFramePosInBytes) / ((drflac_int64)(pcmRangeLo * pFlac->channels * pFlac->bitsPerSample)/8.0f); if (pcmRangeLo > pcmFrameIndex) { byteRangeHi = lastSuccessfulSeekOffset; if (byteRangeLo > byteRangeHi) { byteRangeLo = byteRangeHi; } targetByte = byteRangeLo + ((byteRangeHi - byteRangeLo) / 2); if (targetByte < byteRangeLo) { targetByte = byteRangeLo; } } else { if ((pcmFrameIndex - pcmRangeLo) < seekForwardThreshold) { if (drflac__decode_flac_frame_and_seek_forward_by_pcm_frames(pFlac, pcmFrameIndex - pFlac->currentPCMFrame)) { return DRFLAC_TRUE; } else { break; } } else { byteRangeLo = lastSuccessfulSeekOffset; if (byteRangeHi < byteRangeLo) { byteRangeHi = byteRangeLo; } targetByte = lastSuccessfulSeekOffset + (drflac_uint64)(((drflac_int64)((pcmFrameIndex-pcmRangeLo) * pFlac->channels * pFlac->bitsPerSample)/8.0f) * approxCompressionRatio); if (targetByte > byteRangeHi) { targetByte = byteRangeHi; } if (closestSeekOffsetBeforeTargetPCMFrame < lastSuccessfulSeekOffset) { closestSeekOffsetBeforeTargetPCMFrame = lastSuccessfulSeekOffset; } } } } } else { break; } } drflac__seek_to_first_frame(pFlac); return DRFLAC_FALSE; } static drflac_bool32 drflac__seek_to_pcm_frame__binary_search(drflac* pFlac, drflac_uint64 pcmFrameIndex) { drflac_uint64 byteRangeLo; drflac_uint64 byteRangeHi; drflac_uint32 seekForwardThreshold = (pFlac->maxBlockSizeInPCMFrames != 0) ? pFlac->maxBlockSizeInPCMFrames*2 : 4096; if (drflac__seek_to_first_frame(pFlac) == DRFLAC_FALSE) { return DRFLAC_FALSE; } if (pcmFrameIndex < seekForwardThreshold) { return drflac__seek_forward_by_pcm_frames(pFlac, pcmFrameIndex) == pcmFrameIndex; } byteRangeLo = pFlac->firstFLACFramePosInBytes; byteRangeHi = pFlac->firstFLACFramePosInBytes + (drflac_uint64)((drflac_int64)(pFlac->totalPCMFrameCount * pFlac->channels * pFlac->bitsPerSample)/8.0f); return drflac__seek_to_pcm_frame__binary_search_internal(pFlac, pcmFrameIndex, byteRangeLo, byteRangeHi); } #endif static drflac_bool32 drflac__seek_to_pcm_frame__seek_table(drflac* pFlac, drflac_uint64 pcmFrameIndex) { drflac_uint32 iClosestSeekpoint = 0; drflac_bool32 isMidFrame = DRFLAC_FALSE; drflac_uint64 runningPCMFrameCount; drflac_uint32 iSeekpoint; DRFLAC_ASSERT(pFlac != NULL); if (pFlac->pSeekpoints == NULL || pFlac->seekpointCount == 0) { return DRFLAC_FALSE; } if (pFlac->pSeekpoints[0].firstPCMFrame > pcmFrameIndex) { return DRFLAC_FALSE; } for (iSeekpoint = 0; iSeekpoint < pFlac->seekpointCount; ++iSeekpoint) { if (pFlac->pSeekpoints[iSeekpoint].firstPCMFrame >= pcmFrameIndex) { break; } iClosestSeekpoint = iSeekpoint; } if (pFlac->pSeekpoints[iClosestSeekpoint].pcmFrameCount == 0 || pFlac->pSeekpoints[iClosestSeekpoint].pcmFrameCount > pFlac->maxBlockSizeInPCMFrames) { return DRFLAC_FALSE; } if (pFlac->pSeekpoints[iClosestSeekpoint].firstPCMFrame > pFlac->totalPCMFrameCount && pFlac->totalPCMFrameCount > 0) { return DRFLAC_FALSE; } #if !defined(DR_FLAC_NO_CRC) if (pFlac->totalPCMFrameCount > 0) { drflac_uint64 byteRangeLo; drflac_uint64 byteRangeHi; byteRangeHi = pFlac->firstFLACFramePosInBytes + (drflac_uint64)((drflac_int64)(pFlac->totalPCMFrameCount * pFlac->channels * pFlac->bitsPerSample)/8.0f); byteRangeLo = pFlac->firstFLACFramePosInBytes + pFlac->pSeekpoints[iClosestSeekpoint].flacFrameOffset; if (iClosestSeekpoint < pFlac->seekpointCount-1) { drflac_uint32 iNextSeekpoint = iClosestSeekpoint + 1; if (pFlac->pSeekpoints[iClosestSeekpoint].flacFrameOffset >= pFlac->pSeekpoints[iNextSeekpoint].flacFrameOffset || pFlac->pSeekpoints[iNextSeekpoint].pcmFrameCount == 0) { return DRFLAC_FALSE; } if (pFlac->pSeekpoints[iNextSeekpoint].firstPCMFrame != (((drflac_uint64)0xFFFFFFFF << 32) | 0xFFFFFFFF)) { byteRangeHi = pFlac->firstFLACFramePosInBytes + pFlac->pSeekpoints[iNextSeekpoint].flacFrameOffset - 1; } } if (drflac__seek_to_byte(&pFlac->bs, pFlac->firstFLACFramePosInBytes + pFlac->pSeekpoints[iClosestSeekpoint].flacFrameOffset)) { if (drflac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { drflac__get_pcm_frame_range_of_current_flac_frame(pFlac, &pFlac->currentPCMFrame, NULL); if (drflac__seek_to_pcm_frame__binary_search_internal(pFlac, pcmFrameIndex, byteRangeLo, byteRangeHi)) { return DRFLAC_TRUE; } } } } #endif if (pcmFrameIndex >= pFlac->currentPCMFrame && pFlac->pSeekpoints[iClosestSeekpoint].firstPCMFrame <= pFlac->currentPCMFrame) { runningPCMFrameCount = pFlac->currentPCMFrame; if (pFlac->currentPCMFrame == 0 && pFlac->currentFLACFrame.pcmFramesRemaining == 0) { if (!drflac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { return DRFLAC_FALSE; } } else { isMidFrame = DRFLAC_TRUE; } } else { runningPCMFrameCount = pFlac->pSeekpoints[iClosestSeekpoint].firstPCMFrame; if (!drflac__seek_to_byte(&pFlac->bs, pFlac->firstFLACFramePosInBytes + pFlac->pSeekpoints[iClosestSeekpoint].flacFrameOffset)) { return DRFLAC_FALSE; } if (!drflac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { return DRFLAC_FALSE; } } for (;;) { drflac_uint64 pcmFrameCountInThisFLACFrame; drflac_uint64 firstPCMFrameInFLACFrame = 0; drflac_uint64 lastPCMFrameInFLACFrame = 0; drflac__get_pcm_frame_range_of_current_flac_frame(pFlac, &firstPCMFrameInFLACFrame, &lastPCMFrameInFLACFrame); pcmFrameCountInThisFLACFrame = (lastPCMFrameInFLACFrame - firstPCMFrameInFLACFrame) + 1; if (pcmFrameIndex < (runningPCMFrameCount + pcmFrameCountInThisFLACFrame)) { drflac_uint64 pcmFramesToDecode = pcmFrameIndex - runningPCMFrameCount; if (!isMidFrame) { drflac_result result = drflac__decode_flac_frame(pFlac); if (result == DRFLAC_SUCCESS) { return drflac__seek_forward_by_pcm_frames(pFlac, pcmFramesToDecode) == pcmFramesToDecode; } else { if (result == DRFLAC_CRC_MISMATCH) { goto next_iteration; } else { return DRFLAC_FALSE; } } } else { return drflac__seek_forward_by_pcm_frames(pFlac, pcmFramesToDecode) == pcmFramesToDecode; } } else { if (!isMidFrame) { drflac_result result = drflac__seek_to_next_flac_frame(pFlac); if (result == DRFLAC_SUCCESS) { runningPCMFrameCount += pcmFrameCountInThisFLACFrame; } else { if (result == DRFLAC_CRC_MISMATCH) { goto next_iteration; } else { return DRFLAC_FALSE; } } } else { runningPCMFrameCount += pFlac->currentFLACFrame.pcmFramesRemaining; pFlac->currentFLACFrame.pcmFramesRemaining = 0; isMidFrame = DRFLAC_FALSE; } if (pcmFrameIndex == pFlac->totalPCMFrameCount && runningPCMFrameCount == pFlac->totalPCMFrameCount) { return DRFLAC_TRUE; } } next_iteration: if (!drflac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { return DRFLAC_FALSE; } } } #ifndef DR_FLAC_NO_OGG typedef struct { drflac_uint8 capturePattern[4]; drflac_uint8 structureVersion; drflac_uint8 headerType; drflac_uint64 granulePosition; drflac_uint32 serialNumber; drflac_uint32 sequenceNumber; drflac_uint32 checksum; drflac_uint8 segmentCount; drflac_uint8 segmentTable[255]; } drflac_ogg_page_header; #endif typedef struct { drflac_read_proc onRead; drflac_seek_proc onSeek; drflac_meta_proc onMeta; drflac_container container; void* pUserData; void* pUserDataMD; drflac_uint32 sampleRate; drflac_uint8 channels; drflac_uint8 bitsPerSample; drflac_uint64 totalPCMFrameCount; drflac_uint16 maxBlockSizeInPCMFrames; drflac_uint64 runningFilePos; drflac_bool32 hasStreamInfoBlock; drflac_bool32 hasMetadataBlocks; drflac_bs bs; drflac_frame_header firstFrameHeader; #ifndef DR_FLAC_NO_OGG drflac_uint32 oggSerial; drflac_uint64 oggFirstBytePos; drflac_ogg_page_header oggBosHeader; #endif } drflac_init_info; static DRFLAC_INLINE void drflac__decode_block_header(drflac_uint32 blockHeader, drflac_uint8* isLastBlock, drflac_uint8* blockType, drflac_uint32* blockSize) { blockHeader = drflac__be2host_32(blockHeader); *isLastBlock = (drflac_uint8)((blockHeader & 0x80000000UL) >> 31); *blockType = (drflac_uint8)((blockHeader & 0x7F000000UL) >> 24); *blockSize = (blockHeader & 0x00FFFFFFUL); } static DRFLAC_INLINE drflac_bool32 drflac__read_and_decode_block_header(drflac_read_proc onRead, void* pUserData, drflac_uint8* isLastBlock, drflac_uint8* blockType, drflac_uint32* blockSize) { drflac_uint32 blockHeader; *blockSize = 0; if (onRead(pUserData, &blockHeader, 4) != 4) { return DRFLAC_FALSE; } drflac__decode_block_header(blockHeader, isLastBlock, blockType, blockSize); return DRFLAC_TRUE; } static drflac_bool32 drflac__read_streaminfo(drflac_read_proc onRead, void* pUserData, drflac_streaminfo* pStreamInfo) { drflac_uint32 blockSizes; drflac_uint64 frameSizes = 0; drflac_uint64 importantProps; drflac_uint8 md5[16]; if (onRead(pUserData, &blockSizes, 4) != 4) { return DRFLAC_FALSE; } if (onRead(pUserData, &frameSizes, 6) != 6) { return DRFLAC_FALSE; } if (onRead(pUserData, &importantProps, 8) != 8) { return DRFLAC_FALSE; } if (onRead(pUserData, md5, sizeof(md5)) != sizeof(md5)) { return DRFLAC_FALSE; } blockSizes = drflac__be2host_32(blockSizes); frameSizes = drflac__be2host_64(frameSizes); importantProps = drflac__be2host_64(importantProps); pStreamInfo->minBlockSizeInPCMFrames = (drflac_uint16)((blockSizes & 0xFFFF0000) >> 16); pStreamInfo->maxBlockSizeInPCMFrames = (drflac_uint16) (blockSizes & 0x0000FFFF); pStreamInfo->minFrameSizeInPCMFrames = (drflac_uint32)((frameSizes & (((drflac_uint64)0x00FFFFFF << 16) << 24)) >> 40); pStreamInfo->maxFrameSizeInPCMFrames = (drflac_uint32)((frameSizes & (((drflac_uint64)0x00FFFFFF << 16) << 0)) >> 16); pStreamInfo->sampleRate = (drflac_uint32)((importantProps & (((drflac_uint64)0x000FFFFF << 16) << 28)) >> 44); pStreamInfo->channels = (drflac_uint8 )((importantProps & (((drflac_uint64)0x0000000E << 16) << 24)) >> 41) + 1; pStreamInfo->bitsPerSample = (drflac_uint8 )((importantProps & (((drflac_uint64)0x0000001F << 16) << 20)) >> 36) + 1; pStreamInfo->totalPCMFrameCount = ((importantProps & ((((drflac_uint64)0x0000000F << 16) << 16) | 0xFFFFFFFF))); DRFLAC_COPY_MEMORY(pStreamInfo->md5, md5, sizeof(md5)); return DRFLAC_TRUE; } static void* drflac__malloc_default(size_t sz, void* pUserData) { (void)pUserData; return DRFLAC_MALLOC(sz); } static void* drflac__realloc_default(void* p, size_t sz, void* pUserData) { (void)pUserData; return DRFLAC_REALLOC(p, sz); } static void drflac__free_default(void* p, void* pUserData) { (void)pUserData; DRFLAC_FREE(p); } static void* drflac__malloc_from_callbacks(size_t sz, const drflac_allocation_callbacks* pAllocationCallbacks) { if (pAllocationCallbacks == NULL) { return NULL; } if (pAllocationCallbacks->onMalloc != NULL) { return pAllocationCallbacks->onMalloc(sz, pAllocationCallbacks->pUserData); } if (pAllocationCallbacks->onRealloc != NULL) { return pAllocationCallbacks->onRealloc(NULL, sz, pAllocationCallbacks->pUserData); } return NULL; } static void* drflac__realloc_from_callbacks(void* p, size_t szNew, size_t szOld, const drflac_allocation_callbacks* pAllocationCallbacks) { if (pAllocationCallbacks == NULL) { return NULL; } if (pAllocationCallbacks->onRealloc != NULL) { return pAllocationCallbacks->onRealloc(p, szNew, pAllocationCallbacks->pUserData); } if (pAllocationCallbacks->onMalloc != NULL && pAllocationCallbacks->onFree != NULL) { void* p2; p2 = pAllocationCallbacks->onMalloc(szNew, pAllocationCallbacks->pUserData); if (p2 == NULL) { return NULL; } if (p != NULL) { DRFLAC_COPY_MEMORY(p2, p, szOld); pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData); } return p2; } return NULL; } static void drflac__free_from_callbacks(void* p, const drflac_allocation_callbacks* pAllocationCallbacks) { if (p == NULL || pAllocationCallbacks == NULL) { return; } if (pAllocationCallbacks->onFree != NULL) { pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData); } } static drflac_bool32 drflac__read_and_decode_metadata(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, void* pUserData, void* pUserDataMD, drflac_uint64* pFirstFramePos, drflac_uint64* pSeektablePos, drflac_uint32* pSeekpointCount, drflac_allocation_callbacks* pAllocationCallbacks) { drflac_uint64 runningFilePos = 42; drflac_uint64 seektablePos = 0; drflac_uint32 seektableSize = 0; for (;;) { drflac_metadata metadata; drflac_uint8 isLastBlock = 0; drflac_uint8 blockType; drflac_uint32 blockSize; if (drflac__read_and_decode_block_header(onRead, pUserData, &isLastBlock, &blockType, &blockSize) == DRFLAC_FALSE) { return DRFLAC_FALSE; } runningFilePos += 4; metadata.type = blockType; metadata.pRawData = NULL; metadata.rawDataSize = 0; switch (blockType) { case DRFLAC_METADATA_BLOCK_TYPE_APPLICATION: { if (blockSize < 4) { return DRFLAC_FALSE; } 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; } metadata.pRawData = pRawData; metadata.rawDataSize = blockSize; metadata.data.application.id = drflac__be2host_32(*(drflac_uint32*)pRawData); metadata.data.application.pData = (const void*)((drflac_uint8*)pRawData + sizeof(drflac_uint32)); metadata.data.application.dataSize = blockSize - sizeof(drflac_uint32); onMeta(pUserDataMD, &metadata); drflac__free_from_callbacks(pRawData, pAllocationCallbacks); } } break; case DRFLAC_METADATA_BLOCK_TYPE_SEEKTABLE: { seektablePos = runningFilePos; seektableSize = blockSize; if (onMeta) { drflac_uint32 seekpointCount; drflac_uint32 iSeekpoint; void* pRawData; seekpointCount = blockSize/DRFLAC_SEEKPOINT_SIZE_IN_BYTES; pRawData = drflac__malloc_from_callbacks(seekpointCount * sizeof(drflac_seekpoint), pAllocationCallbacks); if (pRawData == NULL) { return DRFLAC_FALSE; } for (iSeekpoint = 0; iSeekpoint < seekpointCount; ++iSeekpoint) { drflac_seekpoint* pSeekpoint = (drflac_seekpoint*)pRawData + iSeekpoint; if (onRead(pUserData, pSeekpoint, DRFLAC_SEEKPOINT_SIZE_IN_BYTES) != DRFLAC_SEEKPOINT_SIZE_IN_BYTES) { drflac__free_from_callbacks(pRawData, pAllocationCallbacks); return DRFLAC_FALSE; } pSeekpoint->firstPCMFrame = drflac__be2host_64(pSeekpoint->firstPCMFrame); pSeekpoint->flacFrameOffset = drflac__be2host_64(pSeekpoint->flacFrameOffset); pSeekpoint->pcmFrameCount = drflac__be2host_16(pSeekpoint->pcmFrameCount); } metadata.pRawData = pRawData; metadata.rawDataSize = blockSize; metadata.data.seektable.seekpointCount = seekpointCount; metadata.data.seektable.pSeekpoints = (const drflac_seekpoint*)pRawData; onMeta(pUserDataMD, &metadata); drflac__free_from_callbacks(pRawData, pAllocationCallbacks); } } break; case DRFLAC_METADATA_BLOCK_TYPE_VORBIS_COMMENT: { if (blockSize < 8) { return DRFLAC_FALSE; } if (onMeta) { void* pRawData; const char* pRunningData; const char* pRunningDataEnd; drflac_uint32 i; 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; } metadata.pRawData = pRawData; metadata.rawDataSize = blockSize; pRunningData = (const char*)pRawData; pRunningDataEnd = (const char*)pRawData + blockSize; metadata.data.vorbis_comment.vendorLength = drflac__le2host_32_ptr_unaligned(pRunningData); pRunningData += 4; if ((pRunningDataEnd - pRunningData) - 4 < (drflac_int64)metadata.data.vorbis_comment.vendorLength) { drflac__free_from_callbacks(pRawData, pAllocationCallbacks); return DRFLAC_FALSE; } metadata.data.vorbis_comment.vendor = pRunningData; pRunningData += metadata.data.vorbis_comment.vendorLength; metadata.data.vorbis_comment.commentCount = drflac__le2host_32_ptr_unaligned(pRunningData); pRunningData += 4; if ((pRunningDataEnd - pRunningData) / sizeof(drflac_uint32) < metadata.data.vorbis_comment.commentCount) { drflac__free_from_callbacks(pRawData, pAllocationCallbacks); return DRFLAC_FALSE; } metadata.data.vorbis_comment.pComments = pRunningData; for (i = 0; i < metadata.data.vorbis_comment.commentCount; ++i) { drflac_uint32 commentLength; if (pRunningDataEnd - pRunningData < 4) { drflac__free_from_callbacks(pRawData, pAllocationCallbacks); return DRFLAC_FALSE; } commentLength = drflac__le2host_32_ptr_unaligned(pRunningData); pRunningData += 4; if (pRunningDataEnd - pRunningData < (drflac_int64)commentLength) { drflac__free_from_callbacks(pRawData, pAllocationCallbacks); return DRFLAC_FALSE; } pRunningData += commentLength; } onMeta(pUserDataMD, &metadata); drflac__free_from_callbacks(pRawData, pAllocationCallbacks); } } break; case DRFLAC_METADATA_BLOCK_TYPE_CUESHEET: { if (blockSize < 396) { return DRFLAC_FALSE; } if (onMeta) { void* pRawData; const char* pRunningData; const char* pRunningDataEnd; size_t bufferSize; drflac_uint8 iTrack; drflac_uint8 iIndex; void* pTrackData; 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; } metadata.pRawData = pRawData; metadata.rawDataSize = blockSize; pRunningData = (const char*)pRawData; pRunningDataEnd = (const char*)pRawData + blockSize; DRFLAC_COPY_MEMORY(metadata.data.cuesheet.catalog, pRunningData, 128); pRunningData += 128; metadata.data.cuesheet.leadInSampleCount = drflac__be2host_64(*(const drflac_uint64*)pRunningData); pRunningData += 8; metadata.data.cuesheet.isCD = (pRunningData[0] & 0x80) != 0; pRunningData += 259; metadata.data.cuesheet.trackCount = pRunningData[0]; pRunningData += 1; metadata.data.cuesheet.pTrackData = NULL; { const char* pRunningDataSaved = pRunningData; bufferSize = metadata.data.cuesheet.trackCount * DRFLAC_CUESHEET_TRACK_SIZE_IN_BYTES; for (iTrack = 0; iTrack < metadata.data.cuesheet.trackCount; ++iTrack) { drflac_uint8 indexCount; drflac_uint32 indexPointSize; if (pRunningDataEnd - pRunningData < DRFLAC_CUESHEET_TRACK_SIZE_IN_BYTES) { drflac__free_from_callbacks(pRawData, pAllocationCallbacks); return DRFLAC_FALSE; } pRunningData += 35; indexCount = pRunningData[0]; pRunningData += 1; bufferSize += indexCount * sizeof(drflac_cuesheet_track_index); indexPointSize = indexCount * DRFLAC_CUESHEET_TRACK_INDEX_SIZE_IN_BYTES; if (pRunningDataEnd - pRunningData < (drflac_int64)indexPointSize) { drflac__free_from_callbacks(pRawData, pAllocationCallbacks); return DRFLAC_FALSE; } pRunningData += indexPointSize; } pRunningData = pRunningDataSaved; } { char* pRunningTrackData; pTrackData = drflac__malloc_from_callbacks(bufferSize, pAllocationCallbacks); if (pTrackData == NULL) { drflac__free_from_callbacks(pRawData, pAllocationCallbacks); return DRFLAC_FALSE; } pRunningTrackData = (char*)pTrackData; for (iTrack = 0; iTrack < metadata.data.cuesheet.trackCount; ++iTrack) { drflac_uint8 indexCount; DRFLAC_COPY_MEMORY(pRunningTrackData, pRunningData, DRFLAC_CUESHEET_TRACK_SIZE_IN_BYTES); pRunningData += DRFLAC_CUESHEET_TRACK_SIZE_IN_BYTES-1; pRunningTrackData += DRFLAC_CUESHEET_TRACK_SIZE_IN_BYTES-1; indexCount = pRunningData[0]; pRunningData += 1; pRunningTrackData += 1; for (iIndex = 0; iIndex < indexCount; ++iIndex) { drflac_cuesheet_track_index* pTrackIndex = (drflac_cuesheet_track_index*)pRunningTrackData; DRFLAC_COPY_MEMORY(pRunningTrackData, pRunningData, DRFLAC_CUESHEET_TRACK_INDEX_SIZE_IN_BYTES); pRunningData += DRFLAC_CUESHEET_TRACK_INDEX_SIZE_IN_BYTES; pRunningTrackData += sizeof(drflac_cuesheet_track_index); pTrackIndex->offset = drflac__be2host_64(pTrackIndex->offset); } } metadata.data.cuesheet.pTrackData = pTrackData; } drflac__free_from_callbacks(pRawData, pAllocationCallbacks); pRawData = NULL; onMeta(pUserDataMD, &metadata); drflac__free_from_callbacks(pTrackData, pAllocationCallbacks); pTrackData = NULL; } } break; case DRFLAC_METADATA_BLOCK_TYPE_PICTURE: { if (blockSize < 32) { return DRFLAC_FALSE; } if (onMeta) { void* pRawData; const char* pRunningData; const char* pRunningDataEnd; 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; } 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; if ((pRunningDataEnd - pRunningData) - 24 < (drflac_int64)metadata.data.picture.mimeLength) { drflac__free_from_callbacks(pRawData, pAllocationCallbacks); return DRFLAC_FALSE; } metadata.data.picture.mime = pRunningData; pRunningData += metadata.data.picture.mimeLength; metadata.data.picture.descriptionLength = drflac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; if ((pRunningDataEnd - pRunningData) - 20 < (drflac_int64)metadata.data.picture.descriptionLength) { drflac__free_from_callbacks(pRawData, pAllocationCallbacks); return DRFLAC_FALSE; } 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; if (pRunningDataEnd - pRunningData < (drflac_int64)metadata.data.picture.pictureDataSize) { drflac__free_from_callbacks(pRawData, pAllocationCallbacks); return DRFLAC_FALSE; } onMeta(pUserDataMD, &metadata); drflac__free_from_callbacks(pRawData, pAllocationCallbacks); } } break; case DRFLAC_METADATA_BLOCK_TYPE_PADDING: { if (onMeta) { metadata.data.padding.unused = 0; if (!onSeek(pUserData, blockSize, drflac_seek_origin_current)) { isLastBlock = DRFLAC_TRUE; } else { onMeta(pUserDataMD, &metadata); } } } break; case DRFLAC_METADATA_BLOCK_TYPE_INVALID: { if (onMeta) { if (!onSeek(pUserData, blockSize, drflac_seek_origin_current)) { isLastBlock = DRFLAC_TRUE; } } } break; default: { 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; } metadata.pRawData = pRawData; metadata.rawDataSize = blockSize; onMeta(pUserDataMD, &metadata); drflac__free_from_callbacks(pRawData, pAllocationCallbacks); } } break; } if (onMeta == NULL && blockSize > 0) { if (!onSeek(pUserData, blockSize, drflac_seek_origin_current)) { isLastBlock = DRFLAC_TRUE; } } runningFilePos += blockSize; if (isLastBlock) { break; } } *pSeektablePos = seektablePos; *pSeekpointCount = seektableSize / DRFLAC_SEEKPOINT_SIZE_IN_BYTES; *pFirstFramePos = runningFilePos; return DRFLAC_TRUE; } static drflac_bool32 drflac__init_private__native(drflac_init_info* pInit, drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, void* pUserData, void* pUserDataMD, drflac_bool32 relaxed) { drflac_uint8 isLastBlock; drflac_uint8 blockType; drflac_uint32 blockSize; (void)onSeek; pInit->container = drflac_container_native; if (!drflac__read_and_decode_block_header(onRead, pUserData, &isLastBlock, &blockType, &blockSize)) { return DRFLAC_FALSE; } if (blockType != DRFLAC_METADATA_BLOCK_TYPE_STREAMINFO || blockSize != 34) { if (!relaxed) { return DRFLAC_FALSE; } else { pInit->hasStreamInfoBlock = DRFLAC_FALSE; pInit->hasMetadataBlocks = DRFLAC_FALSE; if (!drflac__read_next_flac_frame_header(&pInit->bs, 0, &pInit->firstFrameHeader)) { return DRFLAC_FALSE; } if (pInit->firstFrameHeader.bitsPerSample == 0) { return DRFLAC_FALSE; } pInit->sampleRate = pInit->firstFrameHeader.sampleRate; pInit->channels = drflac__get_channel_count_from_channel_assignment(pInit->firstFrameHeader.channelAssignment); pInit->bitsPerSample = pInit->firstFrameHeader.bitsPerSample; pInit->maxBlockSizeInPCMFrames = 65535; return DRFLAC_TRUE; } } else { drflac_streaminfo streaminfo; if (!drflac__read_streaminfo(onRead, pUserData, &streaminfo)) { return DRFLAC_FALSE; } pInit->hasStreamInfoBlock = DRFLAC_TRUE; pInit->sampleRate = streaminfo.sampleRate; pInit->channels = streaminfo.channels; pInit->bitsPerSample = streaminfo.bitsPerSample; pInit->totalPCMFrameCount = streaminfo.totalPCMFrameCount; pInit->maxBlockSizeInPCMFrames = streaminfo.maxBlockSizeInPCMFrames; pInit->hasMetadataBlocks = !isLastBlock; if (onMeta) { drflac_metadata metadata; metadata.type = DRFLAC_METADATA_BLOCK_TYPE_STREAMINFO; metadata.pRawData = NULL; metadata.rawDataSize = 0; metadata.data.streaminfo = streaminfo; onMeta(pUserDataMD, &metadata); } return DRFLAC_TRUE; } } #ifndef DR_FLAC_NO_OGG #define DRFLAC_OGG_MAX_PAGE_SIZE 65307 #define DRFLAC_OGG_CAPTURE_PATTERN_CRC32 1605413199 typedef enum { drflac_ogg_recover_on_crc_mismatch, drflac_ogg_fail_on_crc_mismatch } drflac_ogg_crc_mismatch_recovery; #ifndef DR_FLAC_NO_CRC static drflac_uint32 drflac__crc32_table[] = { 0x00000000L, 0x04C11DB7L, 0x09823B6EL, 0x0D4326D9L, 0x130476DCL, 0x17C56B6BL, 0x1A864DB2L, 0x1E475005L, 0x2608EDB8L, 0x22C9F00FL, 0x2F8AD6D6L, 0x2B4BCB61L, 0x350C9B64L, 0x31CD86D3L, 0x3C8EA00AL, 0x384FBDBDL, 0x4C11DB70L, 0x48D0C6C7L, 0x4593E01EL, 0x4152FDA9L, 0x5F15ADACL, 0x5BD4B01BL, 0x569796C2L, 0x52568B75L, 0x6A1936C8L, 0x6ED82B7FL, 0x639B0DA6L, 0x675A1011L, 0x791D4014L, 0x7DDC5DA3L, 0x709F7B7AL, 0x745E66CDL, 0x9823B6E0L, 0x9CE2AB57L, 0x91A18D8EL, 0x95609039L, 0x8B27C03CL, 0x8FE6DD8BL, 0x82A5FB52L, 0x8664E6E5L, 0xBE2B5B58L, 0xBAEA46EFL, 0xB7A96036L, 0xB3687D81L, 0xAD2F2D84L, 0xA9EE3033L, 0xA4AD16EAL, 0xA06C0B5DL, 0xD4326D90L, 0xD0F37027L, 0xDDB056FEL, 0xD9714B49L, 0xC7361B4CL, 0xC3F706FBL, 0xCEB42022L, 0xCA753D95L, 0xF23A8028L, 0xF6FB9D9FL, 0xFBB8BB46L, 0xFF79A6F1L, 0xE13EF6F4L, 0xE5FFEB43L, 0xE8BCCD9AL, 0xEC7DD02DL, 0x34867077L, 0x30476DC0L, 0x3D044B19L, 0x39C556AEL, 0x278206ABL, 0x23431B1CL, 0x2E003DC5L, 0x2AC12072L, 0x128E9DCFL, 0x164F8078L, 0x1B0CA6A1L, 0x1FCDBB16L, 0x018AEB13L, 0x054BF6A4L, 0x0808D07DL, 0x0CC9CDCAL, 0x7897AB07L, 0x7C56B6B0L, 0x71159069L, 0x75D48DDEL, 0x6B93DDDBL, 0x6F52C06CL, 0x6211E6B5L, 0x66D0FB02L, 0x5E9F46BFL, 0x5A5E5B08L, 0x571D7DD1L, 0x53DC6066L, 0x4D9B3063L, 0x495A2DD4L, 0x44190B0DL, 0x40D816BAL, 0xACA5C697L, 0xA864DB20L, 0xA527FDF9L, 0xA1E6E04EL, 0xBFA1B04BL, 0xBB60ADFCL, 0xB6238B25L, 0xB2E29692L, 0x8AAD2B2FL, 0x8E6C3698L, 0x832F1041L, 0x87EE0DF6L, 0x99A95DF3L, 0x9D684044L, 0x902B669DL, 0x94EA7B2AL, 0xE0B41DE7L, 0xE4750050L, 0xE9362689L, 0xEDF73B3EL, 0xF3B06B3BL, 0xF771768CL, 0xFA325055L, 0xFEF34DE2L, 0xC6BCF05FL, 0xC27DEDE8L, 0xCF3ECB31L, 0xCBFFD686L, 0xD5B88683L, 0xD1799B34L, 0xDC3ABDEDL, 0xD8FBA05AL, 0x690CE0EEL, 0x6DCDFD59L, 0x608EDB80L, 0x644FC637L, 0x7A089632L, 0x7EC98B85L, 0x738AAD5CL, 0x774BB0EBL, 0x4F040D56L, 0x4BC510E1L, 0x46863638L, 0x42472B8FL, 0x5C007B8AL, 0x58C1663DL, 0x558240E4L, 0x51435D53L, 0x251D3B9EL, 0x21DC2629L, 0x2C9F00F0L, 0x285E1D47L, 0x36194D42L, 0x32D850F5L, 0x3F9B762CL, 0x3B5A6B9BL, 0x0315D626L, 0x07D4CB91L, 0x0A97ED48L, 0x0E56F0FFL, 0x1011A0FAL, 0x14D0BD4DL, 0x19939B94L, 0x1D528623L, 0xF12F560EL, 0xF5EE4BB9L, 0xF8AD6D60L, 0xFC6C70D7L, 0xE22B20D2L, 0xE6EA3D65L, 0xEBA91BBCL, 0xEF68060BL, 0xD727BBB6L, 0xD3E6A601L, 0xDEA580D8L, 0xDA649D6FL, 0xC423CD6AL, 0xC0E2D0DDL, 0xCDA1F604L, 0xC960EBB3L, 0xBD3E8D7EL, 0xB9FF90C9L, 0xB4BCB610L, 0xB07DABA7L, 0xAE3AFBA2L, 0xAAFBE615L, 0xA7B8C0CCL, 0xA379DD7BL, 0x9B3660C6L, 0x9FF77D71L, 0x92B45BA8L, 0x9675461FL, 0x8832161AL, 0x8CF30BADL, 0x81B02D74L, 0x857130C3L, 0x5D8A9099L, 0x594B8D2EL, 0x5408ABF7L, 0x50C9B640L, 0x4E8EE645L, 0x4A4FFBF2L, 0x470CDD2BL, 0x43CDC09CL, 0x7B827D21L, 0x7F436096L, 0x7200464FL, 0x76C15BF8L, 0x68860BFDL, 0x6C47164AL, 0x61043093L, 0x65C52D24L, 0x119B4BE9L, 0x155A565EL, 0x18197087L, 0x1CD86D30L, 0x029F3D35L, 0x065E2082L, 0x0B1D065BL, 0x0FDC1BECL, 0x3793A651L, 0x3352BBE6L, 0x3E119D3FL, 0x3AD08088L, 0x2497D08DL, 0x2056CD3AL, 0x2D15EBE3L, 0x29D4F654L, 0xC5A92679L, 0xC1683BCEL, 0xCC2B1D17L, 0xC8EA00A0L, 0xD6AD50A5L, 0xD26C4D12L, 0xDF2F6BCBL, 0xDBEE767CL, 0xE3A1CBC1L, 0xE760D676L, 0xEA23F0AFL, 0xEEE2ED18L, 0xF0A5BD1DL, 0xF464A0AAL, 0xF9278673L, 0xFDE69BC4L, 0x89B8FD09L, 0x8D79E0BEL, 0x803AC667L, 0x84FBDBD0L, 0x9ABC8BD5L, 0x9E7D9662L, 0x933EB0BBL, 0x97FFAD0CL, 0xAFB010B1L, 0xAB710D06L, 0xA6322BDFL, 0xA2F33668L, 0xBCB4666DL, 0xB8757BDAL, 0xB5365D03L, 0xB1F740B4L }; #endif static DRFLAC_INLINE drflac_uint32 drflac_crc32_byte(drflac_uint32 crc32, drflac_uint8 data) { #ifndef DR_FLAC_NO_CRC return (crc32 << 8) ^ drflac__crc32_table[(drflac_uint8)((crc32 >> 24) & 0xFF) ^ data]; #else (void)data; return crc32; #endif } #if 0 static DRFLAC_INLINE drflac_uint32 drflac_crc32_uint32(drflac_uint32 crc32, drflac_uint32 data) { crc32 = drflac_crc32_byte(crc32, (drflac_uint8)((data >> 24) & 0xFF)); crc32 = drflac_crc32_byte(crc32, (drflac_uint8)((data >> 16) & 0xFF)); crc32 = drflac_crc32_byte(crc32, (drflac_uint8)((data >> 8) & 0xFF)); crc32 = drflac_crc32_byte(crc32, (drflac_uint8)((data >> 0) & 0xFF)); return crc32; } static DRFLAC_INLINE drflac_uint32 drflac_crc32_uint64(drflac_uint32 crc32, drflac_uint64 data) { crc32 = drflac_crc32_uint32(crc32, (drflac_uint32)((data >> 32) & 0xFFFFFFFF)); crc32 = drflac_crc32_uint32(crc32, (drflac_uint32)((data >> 0) & 0xFFFFFFFF)); return crc32; } #endif static DRFLAC_INLINE drflac_uint32 drflac_crc32_buffer(drflac_uint32 crc32, drflac_uint8* pData, drflac_uint32 dataSize) { drflac_uint32 i; for (i = 0; i < dataSize; ++i) { crc32 = drflac_crc32_byte(crc32, pData[i]); } return crc32; } static DRFLAC_INLINE drflac_bool32 drflac_ogg__is_capture_pattern(drflac_uint8 pattern[4]) { return pattern[0] == 'O' && pattern[1] == 'g' && pattern[2] == 'g' && pattern[3] == 'S'; } static DRFLAC_INLINE drflac_uint32 drflac_ogg__get_page_header_size(drflac_ogg_page_header* pHeader) { return 27 + pHeader->segmentCount; } static DRFLAC_INLINE drflac_uint32 drflac_ogg__get_page_body_size(drflac_ogg_page_header* pHeader) { drflac_uint32 pageBodySize = 0; int i; for (i = 0; i < pHeader->segmentCount; ++i) { pageBodySize += pHeader->segmentTable[i]; } return pageBodySize; } static drflac_result drflac_ogg__read_page_header_after_capture_pattern(drflac_read_proc onRead, void* pUserData, drflac_ogg_page_header* pHeader, drflac_uint32* pBytesRead, drflac_uint32* pCRC32) { drflac_uint8 data[23]; drflac_uint32 i; DRFLAC_ASSERT(*pCRC32 == DRFLAC_OGG_CAPTURE_PATTERN_CRC32); if (onRead(pUserData, data, 23) != 23) { return DRFLAC_AT_END; } *pBytesRead += 23; pHeader->capturePattern[0] = 'O'; pHeader->capturePattern[1] = 'g'; pHeader->capturePattern[2] = 'g'; pHeader->capturePattern[3] = 'S'; pHeader->structureVersion = data[0]; pHeader->headerType = data[1]; DRFLAC_COPY_MEMORY(&pHeader->granulePosition, &data[ 2], 8); DRFLAC_COPY_MEMORY(&pHeader->serialNumber, &data[10], 4); DRFLAC_COPY_MEMORY(&pHeader->sequenceNumber, &data[14], 4); DRFLAC_COPY_MEMORY(&pHeader->checksum, &data[18], 4); pHeader->segmentCount = data[22]; data[18] = 0; data[19] = 0; data[20] = 0; data[21] = 0; for (i = 0; i < 23; ++i) { *pCRC32 = drflac_crc32_byte(*pCRC32, data[i]); } if (onRead(pUserData, pHeader->segmentTable, pHeader->segmentCount) != pHeader->segmentCount) { return DRFLAC_AT_END; } *pBytesRead += pHeader->segmentCount; for (i = 0; i < pHeader->segmentCount; ++i) { *pCRC32 = drflac_crc32_byte(*pCRC32, pHeader->segmentTable[i]); } return DRFLAC_SUCCESS; } static drflac_result drflac_ogg__read_page_header(drflac_read_proc onRead, void* pUserData, drflac_ogg_page_header* pHeader, drflac_uint32* pBytesRead, drflac_uint32* pCRC32) { drflac_uint8 id[4]; *pBytesRead = 0; if (onRead(pUserData, id, 4) != 4) { return DRFLAC_AT_END; } *pBytesRead += 4; for (;;) { if (drflac_ogg__is_capture_pattern(id)) { drflac_result result; *pCRC32 = DRFLAC_OGG_CAPTURE_PATTERN_CRC32; result = drflac_ogg__read_page_header_after_capture_pattern(onRead, pUserData, pHeader, pBytesRead, pCRC32); if (result == DRFLAC_SUCCESS) { return DRFLAC_SUCCESS; } else { if (result == DRFLAC_CRC_MISMATCH) { continue; } else { return result; } } } else { id[0] = id[1]; id[1] = id[2]; id[2] = id[3]; if (onRead(pUserData, &id[3], 1) != 1) { return DRFLAC_AT_END; } *pBytesRead += 1; } } } typedef struct { drflac_read_proc onRead; drflac_seek_proc onSeek; void* pUserData; drflac_uint64 currentBytePos; drflac_uint64 firstBytePos; drflac_uint32 serialNumber; drflac_ogg_page_header bosPageHeader; drflac_ogg_page_header currentPageHeader; drflac_uint32 bytesRemainingInPage; drflac_uint32 pageDataSize; drflac_uint8 pageData[DRFLAC_OGG_MAX_PAGE_SIZE]; } drflac_oggbs; static size_t drflac_oggbs__read_physical(drflac_oggbs* oggbs, void* bufferOut, size_t bytesToRead) { size_t bytesActuallyRead = oggbs->onRead(oggbs->pUserData, bufferOut, bytesToRead); oggbs->currentBytePos += bytesActuallyRead; return bytesActuallyRead; } static drflac_bool32 drflac_oggbs__seek_physical(drflac_oggbs* oggbs, drflac_uint64 offset, drflac_seek_origin origin) { if (origin == drflac_seek_origin_start) { if (offset <= 0x7FFFFFFF) { if (!oggbs->onSeek(oggbs->pUserData, (int)offset, drflac_seek_origin_start)) { return DRFLAC_FALSE; } oggbs->currentBytePos = offset; return DRFLAC_TRUE; } else { if (!oggbs->onSeek(oggbs->pUserData, 0x7FFFFFFF, drflac_seek_origin_start)) { return DRFLAC_FALSE; } oggbs->currentBytePos = offset; return drflac_oggbs__seek_physical(oggbs, offset - 0x7FFFFFFF, drflac_seek_origin_current); } } else { while (offset > 0x7FFFFFFF) { if (!oggbs->onSeek(oggbs->pUserData, 0x7FFFFFFF, drflac_seek_origin_current)) { return DRFLAC_FALSE; } oggbs->currentBytePos += 0x7FFFFFFF; offset -= 0x7FFFFFFF; } if (!oggbs->onSeek(oggbs->pUserData, (int)offset, drflac_seek_origin_current)) { return DRFLAC_FALSE; } oggbs->currentBytePos += offset; return DRFLAC_TRUE; } } static drflac_bool32 drflac_oggbs__goto_next_page(drflac_oggbs* oggbs, drflac_ogg_crc_mismatch_recovery recoveryMethod) { drflac_ogg_page_header header; for (;;) { drflac_uint32 crc32 = 0; drflac_uint32 bytesRead; drflac_uint32 pageBodySize; #ifndef DR_FLAC_NO_CRC drflac_uint32 actualCRC32; #endif if (drflac_ogg__read_page_header(oggbs->onRead, oggbs->pUserData, &header, &bytesRead, &crc32) != DRFLAC_SUCCESS) { return DRFLAC_FALSE; } oggbs->currentBytePos += bytesRead; pageBodySize = drflac_ogg__get_page_body_size(&header); if (pageBodySize > DRFLAC_OGG_MAX_PAGE_SIZE) { continue; } if (header.serialNumber != oggbs->serialNumber) { if (pageBodySize > 0 && !drflac_oggbs__seek_physical(oggbs, pageBodySize, drflac_seek_origin_current)) { return DRFLAC_FALSE; } continue; } if (drflac_oggbs__read_physical(oggbs, oggbs->pageData, pageBodySize) != pageBodySize) { return DRFLAC_FALSE; } oggbs->pageDataSize = pageBodySize; #ifndef DR_FLAC_NO_CRC actualCRC32 = drflac_crc32_buffer(crc32, oggbs->pageData, oggbs->pageDataSize); if (actualCRC32 != header.checksum) { if (recoveryMethod == drflac_ogg_recover_on_crc_mismatch) { continue; } else { drflac_oggbs__goto_next_page(oggbs, drflac_ogg_recover_on_crc_mismatch); return DRFLAC_FALSE; } } #else (void)recoveryMethod; #endif oggbs->currentPageHeader = header; oggbs->bytesRemainingInPage = pageBodySize; return DRFLAC_TRUE; } } #if 0 static drflac_uint8 drflac_oggbs__get_current_segment_index(drflac_oggbs* oggbs, drflac_uint8* pBytesRemainingInSeg) { drflac_uint32 bytesConsumedInPage = drflac_ogg__get_page_body_size(&oggbs->currentPageHeader) - oggbs->bytesRemainingInPage; drflac_uint8 iSeg = 0; drflac_uint32 iByte = 0; while (iByte < bytesConsumedInPage) { drflac_uint8 segmentSize = oggbs->currentPageHeader.segmentTable[iSeg]; if (iByte + segmentSize > bytesConsumedInPage) { break; } else { iSeg += 1; iByte += segmentSize; } } *pBytesRemainingInSeg = oggbs->currentPageHeader.segmentTable[iSeg] - (drflac_uint8)(bytesConsumedInPage - iByte); return iSeg; } static drflac_bool32 drflac_oggbs__seek_to_next_packet(drflac_oggbs* oggbs) { for (;;) { drflac_bool32 atEndOfPage = DRFLAC_FALSE; drflac_uint8 bytesRemainingInSeg; drflac_uint8 iFirstSeg = drflac_oggbs__get_current_segment_index(oggbs, &bytesRemainingInSeg); drflac_uint32 bytesToEndOfPacketOrPage = bytesRemainingInSeg; for (drflac_uint8 iSeg = iFirstSeg; iSeg < oggbs->currentPageHeader.segmentCount; ++iSeg) { drflac_uint8 segmentSize = oggbs->currentPageHeader.segmentTable[iSeg]; if (segmentSize < 255) { if (iSeg == oggbs->currentPageHeader.segmentCount-1) { atEndOfPage = DRFLAC_TRUE; } break; } bytesToEndOfPacketOrPage += segmentSize; } drflac_oggbs__seek_physical(oggbs, bytesToEndOfPacketOrPage, drflac_seek_origin_current); oggbs->bytesRemainingInPage -= bytesToEndOfPacketOrPage; if (atEndOfPage) { if (!drflac_oggbs__goto_next_page(oggbs)) { return DRFLAC_FALSE; } if ((oggbs->currentPageHeader.headerType & 0x01) == 0) { return DRFLAC_TRUE; } } else { return DRFLAC_TRUE; } } } static drflac_bool32 drflac_oggbs__seek_to_next_frame(drflac_oggbs* oggbs) { return drflac_oggbs__seek_to_next_packet(oggbs); } #endif static size_t drflac__on_read_ogg(void* pUserData, void* bufferOut, size_t bytesToRead) { drflac_oggbs* oggbs = (drflac_oggbs*)pUserData; drflac_uint8* pRunningBufferOut = (drflac_uint8*)bufferOut; size_t bytesRead = 0; DRFLAC_ASSERT(oggbs != NULL); DRFLAC_ASSERT(pRunningBufferOut != NULL); while (bytesRead < bytesToRead) { size_t bytesRemainingToRead = bytesToRead - bytesRead; if (oggbs->bytesRemainingInPage >= bytesRemainingToRead) { DRFLAC_COPY_MEMORY(pRunningBufferOut, oggbs->pageData + (oggbs->pageDataSize - oggbs->bytesRemainingInPage), bytesRemainingToRead); bytesRead += bytesRemainingToRead; oggbs->bytesRemainingInPage -= (drflac_uint32)bytesRemainingToRead; break; } if (oggbs->bytesRemainingInPage > 0) { DRFLAC_COPY_MEMORY(pRunningBufferOut, oggbs->pageData + (oggbs->pageDataSize - oggbs->bytesRemainingInPage), oggbs->bytesRemainingInPage); bytesRead += oggbs->bytesRemainingInPage; pRunningBufferOut += oggbs->bytesRemainingInPage; oggbs->bytesRemainingInPage = 0; } DRFLAC_ASSERT(bytesRemainingToRead > 0); if (!drflac_oggbs__goto_next_page(oggbs, drflac_ogg_recover_on_crc_mismatch)) { break; } } return bytesRead; } static drflac_bool32 drflac__on_seek_ogg(void* pUserData, int offset, drflac_seek_origin origin) { drflac_oggbs* oggbs = (drflac_oggbs*)pUserData; int bytesSeeked = 0; DRFLAC_ASSERT(oggbs != NULL); DRFLAC_ASSERT(offset >= 0); if (origin == drflac_seek_origin_start) { if (!drflac_oggbs__seek_physical(oggbs, (int)oggbs->firstBytePos, drflac_seek_origin_start)) { return DRFLAC_FALSE; } if (!drflac_oggbs__goto_next_page(oggbs, drflac_ogg_fail_on_crc_mismatch)) { return DRFLAC_FALSE; } return drflac__on_seek_ogg(pUserData, offset, drflac_seek_origin_current); } DRFLAC_ASSERT(origin == drflac_seek_origin_current); while (bytesSeeked < offset) { int bytesRemainingToSeek = offset - bytesSeeked; DRFLAC_ASSERT(bytesRemainingToSeek >= 0); if (oggbs->bytesRemainingInPage >= (size_t)bytesRemainingToSeek) { bytesSeeked += bytesRemainingToSeek; (void)bytesSeeked; oggbs->bytesRemainingInPage -= bytesRemainingToSeek; break; } if (oggbs->bytesRemainingInPage > 0) { bytesSeeked += (int)oggbs->bytesRemainingInPage; oggbs->bytesRemainingInPage = 0; } DRFLAC_ASSERT(bytesRemainingToSeek > 0); if (!drflac_oggbs__goto_next_page(oggbs, drflac_ogg_fail_on_crc_mismatch)) { return DRFLAC_FALSE; } } return DRFLAC_TRUE; } static drflac_bool32 drflac_ogg__seek_to_pcm_frame(drflac* pFlac, drflac_uint64 pcmFrameIndex) { drflac_oggbs* oggbs = (drflac_oggbs*)pFlac->_oggbs; drflac_uint64 originalBytePos; drflac_uint64 runningGranulePosition; drflac_uint64 runningFrameBytePos; drflac_uint64 runningPCMFrameCount; DRFLAC_ASSERT(oggbs != NULL); originalBytePos = oggbs->currentBytePos; if (!drflac__seek_to_byte(&pFlac->bs, pFlac->firstFLACFramePosInBytes)) { return DRFLAC_FALSE; } oggbs->bytesRemainingInPage = 0; runningGranulePosition = 0; for (;;) { if (!drflac_oggbs__goto_next_page(oggbs, drflac_ogg_recover_on_crc_mismatch)) { drflac_oggbs__seek_physical(oggbs, originalBytePos, drflac_seek_origin_start); return DRFLAC_FALSE; } runningFrameBytePos = oggbs->currentBytePos - drflac_ogg__get_page_header_size(&oggbs->currentPageHeader) - oggbs->pageDataSize; if (oggbs->currentPageHeader.granulePosition >= pcmFrameIndex) { break; } if ((oggbs->currentPageHeader.headerType & 0x01) == 0) { if (oggbs->currentPageHeader.segmentTable[0] >= 2) { drflac_uint8 firstBytesInPage[2]; firstBytesInPage[0] = oggbs->pageData[0]; firstBytesInPage[1] = oggbs->pageData[1]; if ((firstBytesInPage[0] == 0xFF) && (firstBytesInPage[1] & 0xFC) == 0xF8) { runningGranulePosition = oggbs->currentPageHeader.granulePosition; } continue; } } } if (!drflac_oggbs__seek_physical(oggbs, runningFrameBytePos, drflac_seek_origin_start)) { return DRFLAC_FALSE; } if (!drflac_oggbs__goto_next_page(oggbs, drflac_ogg_recover_on_crc_mismatch)) { return DRFLAC_FALSE; } runningPCMFrameCount = runningGranulePosition; for (;;) { drflac_uint64 firstPCMFrameInFLACFrame = 0; drflac_uint64 lastPCMFrameInFLACFrame = 0; drflac_uint64 pcmFrameCountInThisFrame; if (!drflac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { return DRFLAC_FALSE; } drflac__get_pcm_frame_range_of_current_flac_frame(pFlac, &firstPCMFrameInFLACFrame, &lastPCMFrameInFLACFrame); pcmFrameCountInThisFrame = (lastPCMFrameInFLACFrame - firstPCMFrameInFLACFrame) + 1; if (pcmFrameIndex == pFlac->totalPCMFrameCount && (runningPCMFrameCount + pcmFrameCountInThisFrame) == pFlac->totalPCMFrameCount) { drflac_result result = drflac__decode_flac_frame(pFlac); if (result == DRFLAC_SUCCESS) { pFlac->currentPCMFrame = pcmFrameIndex; pFlac->currentFLACFrame.pcmFramesRemaining = 0; return DRFLAC_TRUE; } else { return DRFLAC_FALSE; } } if (pcmFrameIndex < (runningPCMFrameCount + pcmFrameCountInThisFrame)) { drflac_result result = drflac__decode_flac_frame(pFlac); if (result == DRFLAC_SUCCESS) { drflac_uint64 pcmFramesToDecode = (size_t)(pcmFrameIndex - runningPCMFrameCount); if (pcmFramesToDecode == 0) { return DRFLAC_TRUE; } pFlac->currentPCMFrame = runningPCMFrameCount; return drflac__seek_forward_by_pcm_frames(pFlac, pcmFramesToDecode) == pcmFramesToDecode; } else { if (result == DRFLAC_CRC_MISMATCH) { continue; } else { return DRFLAC_FALSE; } } } else { drflac_result result = drflac__seek_to_next_flac_frame(pFlac); if (result == DRFLAC_SUCCESS) { runningPCMFrameCount += pcmFrameCountInThisFrame; } else { if (result == DRFLAC_CRC_MISMATCH) { continue; } else { return DRFLAC_FALSE; } } } } } static drflac_bool32 drflac__init_private__ogg(drflac_init_info* pInit, drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, void* pUserData, void* pUserDataMD, drflac_bool32 relaxed) { drflac_ogg_page_header header; drflac_uint32 crc32 = DRFLAC_OGG_CAPTURE_PATTERN_CRC32; drflac_uint32 bytesRead = 0; (void)relaxed; pInit->container = drflac_container_ogg; pInit->oggFirstBytePos = 0; if (drflac_ogg__read_page_header_after_capture_pattern(onRead, pUserData, &header, &bytesRead, &crc32) != DRFLAC_SUCCESS) { return DRFLAC_FALSE; } pInit->runningFilePos += bytesRead; for (;;) { int pageBodySize; if ((header.headerType & 0x02) == 0) { return DRFLAC_FALSE; } pageBodySize = drflac_ogg__get_page_body_size(&header); if (pageBodySize == 51) { drflac_uint32 bytesRemainingInPage = pageBodySize; drflac_uint8 packetType; if (onRead(pUserData, &packetType, 1) != 1) { return DRFLAC_FALSE; } bytesRemainingInPage -= 1; if (packetType == 0x7F) { drflac_uint8 sig[4]; if (onRead(pUserData, sig, 4) != 4) { return DRFLAC_FALSE; } bytesRemainingInPage -= 4; if (sig[0] == 'F' && sig[1] == 'L' && sig[2] == 'A' && sig[3] == 'C') { drflac_uint8 mappingVersion[2]; if (onRead(pUserData, mappingVersion, 2) != 2) { return DRFLAC_FALSE; } if (mappingVersion[0] != 1) { return DRFLAC_FALSE; } if (!onSeek(pUserData, 2, drflac_seek_origin_current)) { return DRFLAC_FALSE; } if (onRead(pUserData, sig, 4) != 4) { return DRFLAC_FALSE; } if (sig[0] == 'f' && sig[1] == 'L' && sig[2] == 'a' && sig[3] == 'C') { drflac_streaminfo streaminfo; drflac_uint8 isLastBlock; drflac_uint8 blockType; drflac_uint32 blockSize; if (!drflac__read_and_decode_block_header(onRead, pUserData, &isLastBlock, &blockType, &blockSize)) { return DRFLAC_FALSE; } if (blockType != DRFLAC_METADATA_BLOCK_TYPE_STREAMINFO || blockSize != 34) { return DRFLAC_FALSE; } if (drflac__read_streaminfo(onRead, pUserData, &streaminfo)) { pInit->hasStreamInfoBlock = DRFLAC_TRUE; pInit->sampleRate = streaminfo.sampleRate; pInit->channels = streaminfo.channels; pInit->bitsPerSample = streaminfo.bitsPerSample; pInit->totalPCMFrameCount = streaminfo.totalPCMFrameCount; pInit->maxBlockSizeInPCMFrames = streaminfo.maxBlockSizeInPCMFrames; pInit->hasMetadataBlocks = !isLastBlock; if (onMeta) { drflac_metadata metadata; metadata.type = DRFLAC_METADATA_BLOCK_TYPE_STREAMINFO; metadata.pRawData = NULL; metadata.rawDataSize = 0; metadata.data.streaminfo = streaminfo; onMeta(pUserDataMD, &metadata); } pInit->runningFilePos += pageBodySize; pInit->oggFirstBytePos = pInit->runningFilePos - 79; pInit->oggSerial = header.serialNumber; pInit->oggBosHeader = header; break; } else { return DRFLAC_FALSE; } } else { return DRFLAC_FALSE; } } else { if (!onSeek(pUserData, bytesRemainingInPage, drflac_seek_origin_current)) { return DRFLAC_FALSE; } } } else { if (!onSeek(pUserData, bytesRemainingInPage, drflac_seek_origin_current)) { return DRFLAC_FALSE; } } } else { if (!onSeek(pUserData, pageBodySize, drflac_seek_origin_current)) { return DRFLAC_FALSE; } } pInit->runningFilePos += pageBodySize; if (drflac_ogg__read_page_header(onRead, pUserData, &header, &bytesRead, &crc32) != DRFLAC_SUCCESS) { return DRFLAC_FALSE; } pInit->runningFilePos += bytesRead; } pInit->hasMetadataBlocks = DRFLAC_TRUE; return DRFLAC_TRUE; } #endif static drflac_bool32 drflac__init_private(drflac_init_info* pInit, drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, drflac_container container, void* pUserData, void* pUserDataMD) { drflac_bool32 relaxed; drflac_uint8 id[4]; if (pInit == NULL || onRead == NULL || onSeek == NULL) { return DRFLAC_FALSE; } DRFLAC_ZERO_MEMORY(pInit, sizeof(*pInit)); pInit->onRead = onRead; pInit->onSeek = onSeek; pInit->onMeta = onMeta; pInit->container = container; pInit->pUserData = pUserData; pInit->pUserDataMD = pUserDataMD; pInit->bs.onRead = onRead; pInit->bs.onSeek = onSeek; pInit->bs.pUserData = pUserData; drflac__reset_cache(&pInit->bs); relaxed = container != drflac_container_unknown; for (;;) { if (onRead(pUserData, id, 4) != 4) { return DRFLAC_FALSE; } pInit->runningFilePos += 4; if (id[0] == 'I' && id[1] == 'D' && id[2] == '3') { drflac_uint8 header[6]; drflac_uint8 flags; drflac_uint32 headerSize; if (onRead(pUserData, header, 6) != 6) { return DRFLAC_FALSE; } pInit->runningFilePos += 6; flags = header[1]; DRFLAC_COPY_MEMORY(&headerSize, header+2, 4); headerSize = drflac__unsynchsafe_32(drflac__be2host_32(headerSize)); if (flags & 0x10) { headerSize += 10; } if (!onSeek(pUserData, headerSize, drflac_seek_origin_current)) { return DRFLAC_FALSE; } pInit->runningFilePos += headerSize; } else { break; } } if (id[0] == 'f' && id[1] == 'L' && id[2] == 'a' && id[3] == 'C') { return drflac__init_private__native(pInit, onRead, onSeek, onMeta, pUserData, pUserDataMD, relaxed); } #ifndef DR_FLAC_NO_OGG if (id[0] == 'O' && id[1] == 'g' && id[2] == 'g' && id[3] == 'S') { return drflac__init_private__ogg(pInit, onRead, onSeek, onMeta, pUserData, pUserDataMD, relaxed); } #endif if (relaxed) { if (container == drflac_container_native) { return drflac__init_private__native(pInit, onRead, onSeek, onMeta, pUserData, pUserDataMD, relaxed); } #ifndef DR_FLAC_NO_OGG if (container == drflac_container_ogg) { return drflac__init_private__ogg(pInit, onRead, onSeek, onMeta, pUserData, pUserDataMD, relaxed); } #endif } return DRFLAC_FALSE; } static void drflac__init_from_info(drflac* pFlac, const drflac_init_info* pInit) { DRFLAC_ASSERT(pFlac != NULL); DRFLAC_ASSERT(pInit != NULL); DRFLAC_ZERO_MEMORY(pFlac, sizeof(*pFlac)); pFlac->bs = pInit->bs; pFlac->onMeta = pInit->onMeta; pFlac->pUserDataMD = pInit->pUserDataMD; pFlac->maxBlockSizeInPCMFrames = pInit->maxBlockSizeInPCMFrames; pFlac->sampleRate = pInit->sampleRate; pFlac->channels = (drflac_uint8)pInit->channels; pFlac->bitsPerSample = (drflac_uint8)pInit->bitsPerSample; pFlac->totalPCMFrameCount = pInit->totalPCMFrameCount; pFlac->container = pInit->container; } static drflac* drflac_open_with_metadata_private(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, drflac_container container, void* pUserData, void* pUserDataMD, const drflac_allocation_callbacks* pAllocationCallbacks) { drflac_init_info init; drflac_uint32 allocationSize; drflac_uint32 wholeSIMDVectorCountPerChannel; drflac_uint32 decodedSamplesAllocationSize; #ifndef DR_FLAC_NO_OGG drflac_oggbs* pOggbs = NULL; #endif drflac_uint64 firstFramePos; drflac_uint64 seektablePos; drflac_uint32 seekpointCount; drflac_allocation_callbacks allocationCallbacks; drflac* pFlac; drflac__init_cpu_caps(); if (!drflac__init_private(&init, onRead, onSeek, onMeta, container, pUserData, pUserDataMD)) { return NULL; } if (pAllocationCallbacks != NULL) { allocationCallbacks = *pAllocationCallbacks; if (allocationCallbacks.onFree == NULL || (allocationCallbacks.onMalloc == NULL && allocationCallbacks.onRealloc == NULL)) { return NULL; } } else { allocationCallbacks.pUserData = NULL; allocationCallbacks.onMalloc = drflac__malloc_default; allocationCallbacks.onRealloc = drflac__realloc_default; allocationCallbacks.onFree = drflac__free_default; } allocationSize = sizeof(drflac); if ((init.maxBlockSizeInPCMFrames % (DRFLAC_MAX_SIMD_VECTOR_SIZE / sizeof(drflac_int32))) == 0) { wholeSIMDVectorCountPerChannel = (init.maxBlockSizeInPCMFrames / (DRFLAC_MAX_SIMD_VECTOR_SIZE / sizeof(drflac_int32))); } else { wholeSIMDVectorCountPerChannel = (init.maxBlockSizeInPCMFrames / (DRFLAC_MAX_SIMD_VECTOR_SIZE / sizeof(drflac_int32))) + 1; } decodedSamplesAllocationSize = wholeSIMDVectorCountPerChannel * DRFLAC_MAX_SIMD_VECTOR_SIZE * init.channels; allocationSize += decodedSamplesAllocationSize; allocationSize += DRFLAC_MAX_SIMD_VECTOR_SIZE; #ifndef DR_FLAC_NO_OGG if (init.container == drflac_container_ogg) { allocationSize += sizeof(drflac_oggbs); pOggbs = (drflac_oggbs*)drflac__malloc_from_callbacks(sizeof(*pOggbs), &allocationCallbacks); if (pOggbs == NULL) { return NULL; } DRFLAC_ZERO_MEMORY(pOggbs, sizeof(*pOggbs)); pOggbs->onRead = onRead; pOggbs->onSeek = onSeek; pOggbs->pUserData = pUserData; pOggbs->currentBytePos = init.oggFirstBytePos; pOggbs->firstBytePos = init.oggFirstBytePos; pOggbs->serialNumber = init.oggSerial; pOggbs->bosPageHeader = init.oggBosHeader; pOggbs->bytesRemainingInPage = 0; } #endif firstFramePos = 42; seektablePos = 0; seekpointCount = 0; if (init.hasMetadataBlocks) { drflac_read_proc onReadOverride = onRead; drflac_seek_proc onSeekOverride = onSeek; void* pUserDataOverride = pUserData; #ifndef DR_FLAC_NO_OGG if (init.container == drflac_container_ogg) { onReadOverride = drflac__on_read_ogg; onSeekOverride = drflac__on_seek_ogg; pUserDataOverride = (void*)pOggbs; } #endif if (!drflac__read_and_decode_metadata(onReadOverride, onSeekOverride, onMeta, pUserDataOverride, pUserDataMD, &firstFramePos, &seektablePos, &seekpointCount, &allocationCallbacks)) { #ifndef DR_FLAC_NO_OGG drflac__free_from_callbacks(pOggbs, &allocationCallbacks); #endif return NULL; } allocationSize += seekpointCount * sizeof(drflac_seekpoint); } pFlac = (drflac*)drflac__malloc_from_callbacks(allocationSize, &allocationCallbacks); if (pFlac == NULL) { #ifndef DR_FLAC_NO_OGG drflac__free_from_callbacks(pOggbs, &allocationCallbacks); #endif return NULL; } drflac__init_from_info(pFlac, &init); pFlac->allocationCallbacks = allocationCallbacks; pFlac->pDecodedSamples = (drflac_int32*)drflac_align((size_t)pFlac->pExtraData, DRFLAC_MAX_SIMD_VECTOR_SIZE); #ifndef DR_FLAC_NO_OGG if (init.container == drflac_container_ogg) { drflac_oggbs* pInternalOggbs = (drflac_oggbs*)((drflac_uint8*)pFlac->pDecodedSamples + decodedSamplesAllocationSize + (seekpointCount * sizeof(drflac_seekpoint))); DRFLAC_COPY_MEMORY(pInternalOggbs, pOggbs, sizeof(*pOggbs)); drflac__free_from_callbacks(pOggbs, &allocationCallbacks); pOggbs = NULL; pFlac->bs.onRead = drflac__on_read_ogg; pFlac->bs.onSeek = drflac__on_seek_ogg; pFlac->bs.pUserData = (void*)pInternalOggbs; pFlac->_oggbs = (void*)pInternalOggbs; } #endif pFlac->firstFLACFramePosInBytes = firstFramePos; #ifndef DR_FLAC_NO_OGG if (init.container == drflac_container_ogg) { pFlac->pSeekpoints = NULL; pFlac->seekpointCount = 0; } else #endif { if (seektablePos != 0) { pFlac->seekpointCount = seekpointCount; pFlac->pSeekpoints = (drflac_seekpoint*)((drflac_uint8*)pFlac->pDecodedSamples + decodedSamplesAllocationSize); DRFLAC_ASSERT(pFlac->bs.onSeek != NULL); DRFLAC_ASSERT(pFlac->bs.onRead != NULL); if (pFlac->bs.onSeek(pFlac->bs.pUserData, (int)seektablePos, drflac_seek_origin_start)) { drflac_uint32 iSeekpoint; for (iSeekpoint = 0; iSeekpoint < seekpointCount; iSeekpoint += 1) { if (pFlac->bs.onRead(pFlac->bs.pUserData, pFlac->pSeekpoints + iSeekpoint, DRFLAC_SEEKPOINT_SIZE_IN_BYTES) == DRFLAC_SEEKPOINT_SIZE_IN_BYTES) { pFlac->pSeekpoints[iSeekpoint].firstPCMFrame = drflac__be2host_64(pFlac->pSeekpoints[iSeekpoint].firstPCMFrame); pFlac->pSeekpoints[iSeekpoint].flacFrameOffset = drflac__be2host_64(pFlac->pSeekpoints[iSeekpoint].flacFrameOffset); pFlac->pSeekpoints[iSeekpoint].pcmFrameCount = drflac__be2host_16(pFlac->pSeekpoints[iSeekpoint].pcmFrameCount); } else { pFlac->pSeekpoints = NULL; pFlac->seekpointCount = 0; break; } } if (!pFlac->bs.onSeek(pFlac->bs.pUserData, (int)pFlac->firstFLACFramePosInBytes, drflac_seek_origin_start)) { drflac__free_from_callbacks(pFlac, &allocationCallbacks); return NULL; } } else { pFlac->pSeekpoints = NULL; pFlac->seekpointCount = 0; } } } if (!init.hasStreamInfoBlock) { pFlac->currentFLACFrame.header = init.firstFrameHeader; for (;;) { drflac_result result = drflac__decode_flac_frame(pFlac); if (result == DRFLAC_SUCCESS) { break; } else { if (result == DRFLAC_CRC_MISMATCH) { if (!drflac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { drflac__free_from_callbacks(pFlac, &allocationCallbacks); return NULL; } continue; } else { drflac__free_from_callbacks(pFlac, &allocationCallbacks); return NULL; } } } } return pFlac; } #ifndef DR_FLAC_NO_STDIO #include #ifndef DR_FLAC_NO_WCHAR #include #endif #include static drflac_result drflac_result_from_errno(int e) { switch (e) { case 0: return DRFLAC_SUCCESS; #ifdef EPERM case EPERM: return DRFLAC_INVALID_OPERATION; #endif #ifdef ENOENT case ENOENT: return DRFLAC_DOES_NOT_EXIST; #endif #ifdef ESRCH case ESRCH: return DRFLAC_DOES_NOT_EXIST; #endif #ifdef EINTR case EINTR: return DRFLAC_INTERRUPT; #endif #ifdef EIO case EIO: return DRFLAC_IO_ERROR; #endif #ifdef ENXIO case ENXIO: return DRFLAC_DOES_NOT_EXIST; #endif #ifdef E2BIG case E2BIG: return DRFLAC_INVALID_ARGS; #endif #ifdef ENOEXEC case ENOEXEC: return DRFLAC_INVALID_FILE; #endif #ifdef EBADF case EBADF: return DRFLAC_INVALID_FILE; #endif #ifdef ECHILD case ECHILD: return DRFLAC_ERROR; #endif #ifdef EAGAIN case EAGAIN: return DRFLAC_UNAVAILABLE; #endif #ifdef ENOMEM case ENOMEM: return DRFLAC_OUT_OF_MEMORY; #endif #ifdef EACCES case EACCES: return DRFLAC_ACCESS_DENIED; #endif #ifdef EFAULT case EFAULT: return DRFLAC_BAD_ADDRESS; #endif #ifdef ENOTBLK case ENOTBLK: return DRFLAC_ERROR; #endif #ifdef EBUSY case EBUSY: return DRFLAC_BUSY; #endif #ifdef EEXIST case EEXIST: return DRFLAC_ALREADY_EXISTS; #endif #ifdef EXDEV case EXDEV: return DRFLAC_ERROR; #endif #ifdef ENODEV case ENODEV: return DRFLAC_DOES_NOT_EXIST; #endif #ifdef ENOTDIR case ENOTDIR: return DRFLAC_NOT_DIRECTORY; #endif #ifdef EISDIR case EISDIR: return DRFLAC_IS_DIRECTORY; #endif #ifdef EINVAL case EINVAL: return DRFLAC_INVALID_ARGS; #endif #ifdef ENFILE case ENFILE: return DRFLAC_TOO_MANY_OPEN_FILES; #endif #ifdef EMFILE case EMFILE: return DRFLAC_TOO_MANY_OPEN_FILES; #endif #ifdef ENOTTY case ENOTTY: return DRFLAC_INVALID_OPERATION; #endif #ifdef ETXTBSY case ETXTBSY: return DRFLAC_BUSY; #endif #ifdef EFBIG case EFBIG: return DRFLAC_TOO_BIG; #endif #ifdef ENOSPC case ENOSPC: return DRFLAC_NO_SPACE; #endif #ifdef ESPIPE case ESPIPE: return DRFLAC_BAD_SEEK; #endif #ifdef EROFS case EROFS: return DRFLAC_ACCESS_DENIED; #endif #ifdef EMLINK case EMLINK: return DRFLAC_TOO_MANY_LINKS; #endif #ifdef EPIPE case EPIPE: return DRFLAC_BAD_PIPE; #endif #ifdef EDOM case EDOM: return DRFLAC_OUT_OF_RANGE; #endif #ifdef ERANGE case ERANGE: return DRFLAC_OUT_OF_RANGE; #endif #ifdef EDEADLK case EDEADLK: return DRFLAC_DEADLOCK; #endif #ifdef ENAMETOOLONG case ENAMETOOLONG: return DRFLAC_PATH_TOO_LONG; #endif #ifdef ENOLCK case ENOLCK: return DRFLAC_ERROR; #endif #ifdef ENOSYS case ENOSYS: return DRFLAC_NOT_IMPLEMENTED; #endif #ifdef ENOTEMPTY case ENOTEMPTY: return DRFLAC_DIRECTORY_NOT_EMPTY; #endif #ifdef ELOOP case ELOOP: return DRFLAC_TOO_MANY_LINKS; #endif #ifdef ENOMSG case ENOMSG: return DRFLAC_NO_MESSAGE; #endif #ifdef EIDRM case EIDRM: return DRFLAC_ERROR; #endif #ifdef ECHRNG case ECHRNG: return DRFLAC_ERROR; #endif #ifdef EL2NSYNC case EL2NSYNC: return DRFLAC_ERROR; #endif #ifdef EL3HLT case EL3HLT: return DRFLAC_ERROR; #endif #ifdef EL3RST case EL3RST: return DRFLAC_ERROR; #endif #ifdef ELNRNG case ELNRNG: return DRFLAC_OUT_OF_RANGE; #endif #ifdef EUNATCH case EUNATCH: return DRFLAC_ERROR; #endif #ifdef ENOCSI case ENOCSI: return DRFLAC_ERROR; #endif #ifdef EL2HLT case EL2HLT: return DRFLAC_ERROR; #endif #ifdef EBADE case EBADE: return DRFLAC_ERROR; #endif #ifdef EBADR case EBADR: return DRFLAC_ERROR; #endif #ifdef EXFULL case EXFULL: return DRFLAC_ERROR; #endif #ifdef ENOANO case ENOANO: return DRFLAC_ERROR; #endif #ifdef EBADRQC case EBADRQC: return DRFLAC_ERROR; #endif #ifdef EBADSLT case EBADSLT: return DRFLAC_ERROR; #endif #ifdef EBFONT case EBFONT: return DRFLAC_INVALID_FILE; #endif #ifdef ENOSTR case ENOSTR: return DRFLAC_ERROR; #endif #ifdef ENODATA case ENODATA: return DRFLAC_NO_DATA_AVAILABLE; #endif #ifdef ETIME case ETIME: return DRFLAC_TIMEOUT; #endif #ifdef ENOSR case ENOSR: return DRFLAC_NO_DATA_AVAILABLE; #endif #ifdef ENONET case ENONET: return DRFLAC_NO_NETWORK; #endif #ifdef ENOPKG case ENOPKG: return DRFLAC_ERROR; #endif #ifdef EREMOTE case EREMOTE: return DRFLAC_ERROR; #endif #ifdef ENOLINK case ENOLINK: return DRFLAC_ERROR; #endif #ifdef EADV case EADV: return DRFLAC_ERROR; #endif #ifdef ESRMNT case ESRMNT: return DRFLAC_ERROR; #endif #ifdef ECOMM case ECOMM: return DRFLAC_ERROR; #endif #ifdef EPROTO case EPROTO: return DRFLAC_ERROR; #endif #ifdef EMULTIHOP case EMULTIHOP: return DRFLAC_ERROR; #endif #ifdef EDOTDOT case EDOTDOT: return DRFLAC_ERROR; #endif #ifdef EBADMSG case EBADMSG: return DRFLAC_BAD_MESSAGE; #endif #ifdef EOVERFLOW case EOVERFLOW: return DRFLAC_TOO_BIG; #endif #ifdef ENOTUNIQ case ENOTUNIQ: return DRFLAC_NOT_UNIQUE; #endif #ifdef EBADFD case EBADFD: return DRFLAC_ERROR; #endif #ifdef EREMCHG case EREMCHG: return DRFLAC_ERROR; #endif #ifdef ELIBACC case ELIBACC: return DRFLAC_ACCESS_DENIED; #endif #ifdef ELIBBAD case ELIBBAD: return DRFLAC_INVALID_FILE; #endif #ifdef ELIBSCN case ELIBSCN: return DRFLAC_INVALID_FILE; #endif #ifdef ELIBMAX case ELIBMAX: return DRFLAC_ERROR; #endif #ifdef ELIBEXEC case ELIBEXEC: return DRFLAC_ERROR; #endif #ifdef EILSEQ case EILSEQ: return DRFLAC_INVALID_DATA; #endif #ifdef ERESTART case ERESTART: return DRFLAC_ERROR; #endif #ifdef ESTRPIPE case ESTRPIPE: return DRFLAC_ERROR; #endif #ifdef EUSERS case EUSERS: return DRFLAC_ERROR; #endif #ifdef ENOTSOCK case ENOTSOCK: return DRFLAC_NOT_SOCKET; #endif #ifdef EDESTADDRREQ case EDESTADDRREQ: return DRFLAC_NO_ADDRESS; #endif #ifdef EMSGSIZE case EMSGSIZE: return DRFLAC_TOO_BIG; #endif #ifdef EPROTOTYPE case EPROTOTYPE: return DRFLAC_BAD_PROTOCOL; #endif #ifdef ENOPROTOOPT case ENOPROTOOPT: return DRFLAC_PROTOCOL_UNAVAILABLE; #endif #ifdef EPROTONOSUPPORT case EPROTONOSUPPORT: return DRFLAC_PROTOCOL_NOT_SUPPORTED; #endif #ifdef ESOCKTNOSUPPORT case ESOCKTNOSUPPORT: return DRFLAC_SOCKET_NOT_SUPPORTED; #endif #ifdef EOPNOTSUPP case EOPNOTSUPP: return DRFLAC_INVALID_OPERATION; #endif #ifdef EPFNOSUPPORT case EPFNOSUPPORT: return DRFLAC_PROTOCOL_FAMILY_NOT_SUPPORTED; #endif #ifdef EAFNOSUPPORT case EAFNOSUPPORT: return DRFLAC_ADDRESS_FAMILY_NOT_SUPPORTED; #endif #ifdef EADDRINUSE case EADDRINUSE: return DRFLAC_ALREADY_IN_USE; #endif #ifdef EADDRNOTAVAIL case EADDRNOTAVAIL: return DRFLAC_ERROR; #endif #ifdef ENETDOWN case ENETDOWN: return DRFLAC_NO_NETWORK; #endif #ifdef ENETUNREACH case ENETUNREACH: return DRFLAC_NO_NETWORK; #endif #ifdef ENETRESET case ENETRESET: return DRFLAC_NO_NETWORK; #endif #ifdef ECONNABORTED case ECONNABORTED: return DRFLAC_NO_NETWORK; #endif #ifdef ECONNRESET case ECONNRESET: return DRFLAC_CONNECTION_RESET; #endif #ifdef ENOBUFS case ENOBUFS: return DRFLAC_NO_SPACE; #endif #ifdef EISCONN case EISCONN: return DRFLAC_ALREADY_CONNECTED; #endif #ifdef ENOTCONN case ENOTCONN: return DRFLAC_NOT_CONNECTED; #endif #ifdef ESHUTDOWN case ESHUTDOWN: return DRFLAC_ERROR; #endif #ifdef ETOOMANYREFS case ETOOMANYREFS: return DRFLAC_ERROR; #endif #ifdef ETIMEDOUT case ETIMEDOUT: return DRFLAC_TIMEOUT; #endif #ifdef ECONNREFUSED case ECONNREFUSED: return DRFLAC_CONNECTION_REFUSED; #endif #ifdef EHOSTDOWN case EHOSTDOWN: return DRFLAC_NO_HOST; #endif #ifdef EHOSTUNREACH case EHOSTUNREACH: return DRFLAC_NO_HOST; #endif #ifdef EALREADY case EALREADY: return DRFLAC_IN_PROGRESS; #endif #ifdef EINPROGRESS case EINPROGRESS: return DRFLAC_IN_PROGRESS; #endif #ifdef ESTALE case ESTALE: return DRFLAC_INVALID_FILE; #endif #ifdef EUCLEAN case EUCLEAN: return DRFLAC_ERROR; #endif #ifdef ENOTNAM case ENOTNAM: return DRFLAC_ERROR; #endif #ifdef ENAVAIL case ENAVAIL: return DRFLAC_ERROR; #endif #ifdef EISNAM case EISNAM: return DRFLAC_ERROR; #endif #ifdef EREMOTEIO case EREMOTEIO: return DRFLAC_IO_ERROR; #endif #ifdef EDQUOT case EDQUOT: return DRFLAC_NO_SPACE; #endif #ifdef ENOMEDIUM case ENOMEDIUM: return DRFLAC_DOES_NOT_EXIST; #endif #ifdef EMEDIUMTYPE case EMEDIUMTYPE: return DRFLAC_ERROR; #endif #ifdef ECANCELED case ECANCELED: return DRFLAC_CANCELLED; #endif #ifdef ENOKEY case ENOKEY: return DRFLAC_ERROR; #endif #ifdef EKEYEXPIRED case EKEYEXPIRED: return DRFLAC_ERROR; #endif #ifdef EKEYREVOKED case EKEYREVOKED: return DRFLAC_ERROR; #endif #ifdef EKEYREJECTED case EKEYREJECTED: return DRFLAC_ERROR; #endif #ifdef EOWNERDEAD case EOWNERDEAD: return DRFLAC_ERROR; #endif #ifdef ENOTRECOVERABLE case ENOTRECOVERABLE: return DRFLAC_ERROR; #endif #ifdef ERFKILL case ERFKILL: return DRFLAC_ERROR; #endif #ifdef EHWPOISON case EHWPOISON: return DRFLAC_ERROR; #endif default: return DRFLAC_ERROR; } } static drflac_result drflac_fopen(FILE** ppFile, const char* pFilePath, const char* pOpenMode) { #if defined(_MSC_VER) && _MSC_VER >= 1400 errno_t err; #endif if (ppFile != NULL) { *ppFile = NULL; } if (pFilePath == NULL || pOpenMode == NULL || ppFile == NULL) { return DRFLAC_INVALID_ARGS; } #if defined(_MSC_VER) && _MSC_VER >= 1400 err = fopen_s(ppFile, pFilePath, pOpenMode); if (err != 0) { return drflac_result_from_errno(err); } #else #if defined(_WIN32) || defined(__APPLE__) *ppFile = fopen(pFilePath, pOpenMode); #else #if defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS == 64 && defined(_LARGEFILE64_SOURCE) *ppFile = fopen64(pFilePath, pOpenMode); #else *ppFile = fopen(pFilePath, pOpenMode); #endif #endif if (*ppFile == NULL) { drflac_result result = drflac_result_from_errno(errno); if (result == DRFLAC_SUCCESS) { result = DRFLAC_ERROR; } return result; } #endif return DRFLAC_SUCCESS; } #if defined(_WIN32) #if defined(_MSC_VER) || defined(__MINGW64__) || (!defined(__STRICT_ANSI__) && !defined(_NO_EXT_KEYS)) #define DRFLAC_HAS_WFOPEN #endif #endif #ifndef DR_FLAC_NO_WCHAR static drflac_result drflac_wfopen(FILE** ppFile, const wchar_t* pFilePath, const wchar_t* pOpenMode, const drflac_allocation_callbacks* pAllocationCallbacks) { if (ppFile != NULL) { *ppFile = NULL; } if (pFilePath == NULL || pOpenMode == NULL || ppFile == NULL) { return DRFLAC_INVALID_ARGS; } #if defined(DRFLAC_HAS_WFOPEN) { #if defined(_MSC_VER) && _MSC_VER >= 1400 errno_t err = _wfopen_s(ppFile, pFilePath, pOpenMode); if (err != 0) { return drflac_result_from_errno(err); } #else *ppFile = _wfopen(pFilePath, pOpenMode); if (*ppFile == NULL) { return drflac_result_from_errno(errno); } #endif (void)pAllocationCallbacks; } #else #if defined(__DJGPP__) { } #else { mbstate_t mbs; size_t lenMB; const wchar_t* pFilePathTemp = pFilePath; char* pFilePathMB = NULL; char pOpenModeMB[32] = {0}; DRFLAC_ZERO_OBJECT(&mbs); lenMB = wcsrtombs(NULL, &pFilePathTemp, 0, &mbs); if (lenMB == (size_t)-1) { return drflac_result_from_errno(errno); } pFilePathMB = (char*)drflac__malloc_from_callbacks(lenMB + 1, pAllocationCallbacks); if (pFilePathMB == NULL) { return DRFLAC_OUT_OF_MEMORY; } pFilePathTemp = pFilePath; DRFLAC_ZERO_OBJECT(&mbs); wcsrtombs(pFilePathMB, &pFilePathTemp, lenMB + 1, &mbs); { size_t i = 0; for (;;) { if (pOpenMode[i] == 0) { pOpenModeMB[i] = '\0'; break; } pOpenModeMB[i] = (char)pOpenMode[i]; i += 1; } } *ppFile = fopen(pFilePathMB, pOpenModeMB); drflac__free_from_callbacks(pFilePathMB, pAllocationCallbacks); } #endif if (*ppFile == NULL) { return DRFLAC_ERROR; } #endif return DRFLAC_SUCCESS; } #endif static size_t drflac__on_read_stdio(void* pUserData, void* bufferOut, size_t bytesToRead) { return fread(bufferOut, 1, bytesToRead, (FILE*)pUserData); } static drflac_bool32 drflac__on_seek_stdio(void* pUserData, int offset, drflac_seek_origin origin) { DRFLAC_ASSERT(offset >= 0); return fseek((FILE*)pUserData, offset, (origin == drflac_seek_origin_current) ? SEEK_CUR : SEEK_SET) == 0; } DRFLAC_API drflac* drflac_open_file(const char* pFileName, const drflac_allocation_callbacks* pAllocationCallbacks) { drflac* pFlac; FILE* pFile; if (drflac_fopen(&pFile, pFileName, "rb") != DRFLAC_SUCCESS) { return NULL; } pFlac = drflac_open(drflac__on_read_stdio, drflac__on_seek_stdio, (void*)pFile, pAllocationCallbacks); if (pFlac == NULL) { fclose(pFile); return NULL; } return pFlac; } #ifndef DR_FLAC_NO_WCHAR DRFLAC_API drflac* drflac_open_file_w(const wchar_t* pFileName, const drflac_allocation_callbacks* pAllocationCallbacks) { drflac* pFlac; FILE* pFile; if (drflac_wfopen(&pFile, pFileName, L"rb", pAllocationCallbacks) != DRFLAC_SUCCESS) { return NULL; } pFlac = drflac_open(drflac__on_read_stdio, drflac__on_seek_stdio, (void*)pFile, pAllocationCallbacks); if (pFlac == NULL) { fclose(pFile); return NULL; } return pFlac; } #endif DRFLAC_API drflac* drflac_open_file_with_metadata(const char* pFileName, drflac_meta_proc onMeta, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks) { drflac* pFlac; FILE* pFile; if (drflac_fopen(&pFile, pFileName, "rb") != DRFLAC_SUCCESS) { return NULL; } pFlac = drflac_open_with_metadata_private(drflac__on_read_stdio, drflac__on_seek_stdio, onMeta, drflac_container_unknown, (void*)pFile, pUserData, pAllocationCallbacks); if (pFlac == NULL) { fclose(pFile); return pFlac; } return pFlac; } #ifndef DR_FLAC_NO_WCHAR DRFLAC_API drflac* drflac_open_file_with_metadata_w(const wchar_t* pFileName, drflac_meta_proc onMeta, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks) { drflac* pFlac; FILE* pFile; if (drflac_wfopen(&pFile, pFileName, L"rb", pAllocationCallbacks) != DRFLAC_SUCCESS) { return NULL; } pFlac = drflac_open_with_metadata_private(drflac__on_read_stdio, drflac__on_seek_stdio, onMeta, drflac_container_unknown, (void*)pFile, pUserData, pAllocationCallbacks); if (pFlac == NULL) { fclose(pFile); return pFlac; } return pFlac; } #endif #endif static size_t drflac__on_read_memory(void* pUserData, void* bufferOut, size_t bytesToRead) { drflac__memory_stream* memoryStream = (drflac__memory_stream*)pUserData; size_t bytesRemaining; DRFLAC_ASSERT(memoryStream != NULL); DRFLAC_ASSERT(memoryStream->dataSize >= memoryStream->currentReadPos); bytesRemaining = memoryStream->dataSize - memoryStream->currentReadPos; if (bytesToRead > bytesRemaining) { bytesToRead = bytesRemaining; } if (bytesToRead > 0) { DRFLAC_COPY_MEMORY(bufferOut, memoryStream->data + memoryStream->currentReadPos, bytesToRead); memoryStream->currentReadPos += bytesToRead; } return bytesToRead; } static drflac_bool32 drflac__on_seek_memory(void* pUserData, int offset, drflac_seek_origin origin) { drflac__memory_stream* memoryStream = (drflac__memory_stream*)pUserData; DRFLAC_ASSERT(memoryStream != NULL); DRFLAC_ASSERT(offset >= 0); if (offset > (drflac_int64)memoryStream->dataSize) { return DRFLAC_FALSE; } if (origin == drflac_seek_origin_current) { if (memoryStream->currentReadPos + offset <= memoryStream->dataSize) { memoryStream->currentReadPos += offset; } else { return DRFLAC_FALSE; } } else { if ((drflac_uint32)offset <= memoryStream->dataSize) { memoryStream->currentReadPos = offset; } else { return DRFLAC_FALSE; } } return DRFLAC_TRUE; } DRFLAC_API drflac* drflac_open_memory(const void* pData, size_t dataSize, const drflac_allocation_callbacks* pAllocationCallbacks) { drflac__memory_stream memoryStream; drflac* pFlac; memoryStream.data = (const drflac_uint8*)pData; memoryStream.dataSize = dataSize; memoryStream.currentReadPos = 0; pFlac = drflac_open(drflac__on_read_memory, drflac__on_seek_memory, &memoryStream, pAllocationCallbacks); if (pFlac == NULL) { return NULL; } pFlac->memoryStream = memoryStream; #ifndef DR_FLAC_NO_OGG if (pFlac->container == drflac_container_ogg) { drflac_oggbs* oggbs = (drflac_oggbs*)pFlac->_oggbs; oggbs->pUserData = &pFlac->memoryStream; } else #endif { pFlac->bs.pUserData = &pFlac->memoryStream; } return pFlac; } DRFLAC_API drflac* drflac_open_memory_with_metadata(const void* pData, size_t dataSize, drflac_meta_proc onMeta, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks) { drflac__memory_stream memoryStream; drflac* pFlac; memoryStream.data = (const drflac_uint8*)pData; memoryStream.dataSize = dataSize; memoryStream.currentReadPos = 0; pFlac = drflac_open_with_metadata_private(drflac__on_read_memory, drflac__on_seek_memory, onMeta, drflac_container_unknown, &memoryStream, pUserData, pAllocationCallbacks); if (pFlac == NULL) { return NULL; } pFlac->memoryStream = memoryStream; #ifndef DR_FLAC_NO_OGG if (pFlac->container == drflac_container_ogg) { drflac_oggbs* oggbs = (drflac_oggbs*)pFlac->_oggbs; oggbs->pUserData = &pFlac->memoryStream; } else #endif { pFlac->bs.pUserData = &pFlac->memoryStream; } return pFlac; } DRFLAC_API drflac* drflac_open(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks) { return drflac_open_with_metadata_private(onRead, onSeek, NULL, drflac_container_unknown, pUserData, pUserData, pAllocationCallbacks); } DRFLAC_API drflac* drflac_open_relaxed(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_container container, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks) { return drflac_open_with_metadata_private(onRead, onSeek, NULL, container, pUserData, pUserData, pAllocationCallbacks); } DRFLAC_API drflac* drflac_open_with_metadata(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks) { return drflac_open_with_metadata_private(onRead, onSeek, onMeta, drflac_container_unknown, pUserData, pUserData, pAllocationCallbacks); } DRFLAC_API drflac* drflac_open_with_metadata_relaxed(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, drflac_container container, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks) { return drflac_open_with_metadata_private(onRead, onSeek, onMeta, container, pUserData, pUserData, pAllocationCallbacks); } DRFLAC_API void drflac_close(drflac* pFlac) { if (pFlac == NULL) { return; } #ifndef DR_FLAC_NO_STDIO if (pFlac->bs.onRead == drflac__on_read_stdio) { fclose((FILE*)pFlac->bs.pUserData); } #ifndef DR_FLAC_NO_OGG if (pFlac->container == drflac_container_ogg) { drflac_oggbs* oggbs = (drflac_oggbs*)pFlac->_oggbs; DRFLAC_ASSERT(pFlac->bs.onRead == drflac__on_read_ogg); if (oggbs->onRead == drflac__on_read_stdio) { fclose((FILE*)oggbs->pUserData); } } #endif #endif drflac__free_from_callbacks(pFlac, &pFlac->allocationCallbacks); } #if 0 static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_left_side__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) { drflac_uint64 i; for (i = 0; i < frameCount; ++i) { drflac_uint32 left = (drflac_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); drflac_uint32 side = (drflac_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); drflac_uint32 right = left - side; pOutputSamples[i*2+0] = (drflac_int32)left; pOutputSamples[i*2+1] = (drflac_int32)right; } } #endif static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_left_side__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) { drflac_uint64 i; drflac_uint64 frameCount4 = frameCount >> 2; const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; for (i = 0; i < frameCount4; ++i) { drflac_uint32 left0 = pInputSamples0U32[i*4+0] << shift0; drflac_uint32 left1 = pInputSamples0U32[i*4+1] << shift0; drflac_uint32 left2 = pInputSamples0U32[i*4+2] << shift0; drflac_uint32 left3 = pInputSamples0U32[i*4+3] << shift0; drflac_uint32 side0 = pInputSamples1U32[i*4+0] << shift1; drflac_uint32 side1 = pInputSamples1U32[i*4+1] << shift1; drflac_uint32 side2 = pInputSamples1U32[i*4+2] << shift1; drflac_uint32 side3 = pInputSamples1U32[i*4+3] << shift1; drflac_uint32 right0 = left0 - side0; drflac_uint32 right1 = left1 - side1; drflac_uint32 right2 = left2 - side2; drflac_uint32 right3 = left3 - side3; pOutputSamples[i*8+0] = (drflac_int32)left0; pOutputSamples[i*8+1] = (drflac_int32)right0; pOutputSamples[i*8+2] = (drflac_int32)left1; pOutputSamples[i*8+3] = (drflac_int32)right1; pOutputSamples[i*8+4] = (drflac_int32)left2; pOutputSamples[i*8+5] = (drflac_int32)right2; pOutputSamples[i*8+6] = (drflac_int32)left3; pOutputSamples[i*8+7] = (drflac_int32)right3; } for (i = (frameCount4 << 2); i < frameCount; ++i) { drflac_uint32 left = pInputSamples0U32[i] << shift0; drflac_uint32 side = pInputSamples1U32[i] << shift1; drflac_uint32 right = left - side; pOutputSamples[i*2+0] = (drflac_int32)left; pOutputSamples[i*2+1] = (drflac_int32)right; } } #if defined(DRFLAC_SUPPORT_SSE2) static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_left_side__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) { drflac_uint64 i; drflac_uint64 frameCount4 = frameCount >> 2; const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); for (i = 0; i < frameCount4; ++i) { __m128i left = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0); __m128i side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1); __m128i right = _mm_sub_epi32(left, side); _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 0), _mm_unpacklo_epi32(left, right)); _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 4), _mm_unpackhi_epi32(left, right)); } for (i = (frameCount4 << 2); i < frameCount; ++i) { drflac_uint32 left = pInputSamples0U32[i] << shift0; drflac_uint32 side = pInputSamples1U32[i] << shift1; drflac_uint32 right = left - side; pOutputSamples[i*2+0] = (drflac_int32)left; pOutputSamples[i*2+1] = (drflac_int32)right; } } #endif #if defined(DRFLAC_SUPPORT_NEON) static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_left_side__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) { drflac_uint64 i; drflac_uint64 frameCount4 = frameCount >> 2; const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; int32x4_t shift0_4; int32x4_t shift1_4; DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); shift0_4 = vdupq_n_s32(shift0); shift1_4 = vdupq_n_s32(shift1); for (i = 0; i < frameCount4; ++i) { uint32x4_t left; uint32x4_t side; uint32x4_t right; left = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4); side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4); right = vsubq_u32(left, side); drflac__vst2q_u32((drflac_uint32*)pOutputSamples + i*8, vzipq_u32(left, right)); } for (i = (frameCount4 << 2); i < frameCount; ++i) { drflac_uint32 left = pInputSamples0U32[i] << shift0; drflac_uint32 side = pInputSamples1U32[i] << shift1; drflac_uint32 right = left - side; pOutputSamples[i*2+0] = (drflac_int32)left; pOutputSamples[i*2+1] = (drflac_int32)right; } } #endif static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_left_side(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) { #if defined(DRFLAC_SUPPORT_SSE2) if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { drflac_read_pcm_frames_s32__decode_left_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); } else #elif defined(DRFLAC_SUPPORT_NEON) if (drflac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { drflac_read_pcm_frames_s32__decode_left_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); } else #endif { #if 0 drflac_read_pcm_frames_s32__decode_left_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); #else drflac_read_pcm_frames_s32__decode_left_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); #endif } } #if 0 static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_right_side__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) { drflac_uint64 i; for (i = 0; i < frameCount; ++i) { drflac_uint32 side = (drflac_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); drflac_uint32 right = (drflac_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); drflac_uint32 left = right + side; pOutputSamples[i*2+0] = (drflac_int32)left; pOutputSamples[i*2+1] = (drflac_int32)right; } } #endif static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_right_side__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) { drflac_uint64 i; drflac_uint64 frameCount4 = frameCount >> 2; const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; for (i = 0; i < frameCount4; ++i) { drflac_uint32 side0 = pInputSamples0U32[i*4+0] << shift0; drflac_uint32 side1 = pInputSamples0U32[i*4+1] << shift0; drflac_uint32 side2 = pInputSamples0U32[i*4+2] << shift0; drflac_uint32 side3 = pInputSamples0U32[i*4+3] << shift0; drflac_uint32 right0 = pInputSamples1U32[i*4+0] << shift1; drflac_uint32 right1 = pInputSamples1U32[i*4+1] << shift1; drflac_uint32 right2 = pInputSamples1U32[i*4+2] << shift1; drflac_uint32 right3 = pInputSamples1U32[i*4+3] << shift1; drflac_uint32 left0 = right0 + side0; drflac_uint32 left1 = right1 + side1; drflac_uint32 left2 = right2 + side2; drflac_uint32 left3 = right3 + side3; pOutputSamples[i*8+0] = (drflac_int32)left0; pOutputSamples[i*8+1] = (drflac_int32)right0; pOutputSamples[i*8+2] = (drflac_int32)left1; pOutputSamples[i*8+3] = (drflac_int32)right1; pOutputSamples[i*8+4] = (drflac_int32)left2; pOutputSamples[i*8+5] = (drflac_int32)right2; pOutputSamples[i*8+6] = (drflac_int32)left3; pOutputSamples[i*8+7] = (drflac_int32)right3; } for (i = (frameCount4 << 2); i < frameCount; ++i) { drflac_uint32 side = pInputSamples0U32[i] << shift0; drflac_uint32 right = pInputSamples1U32[i] << shift1; drflac_uint32 left = right + side; pOutputSamples[i*2+0] = (drflac_int32)left; pOutputSamples[i*2+1] = (drflac_int32)right; } } #if defined(DRFLAC_SUPPORT_SSE2) static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_right_side__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) { drflac_uint64 i; drflac_uint64 frameCount4 = frameCount >> 2; const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); for (i = 0; i < frameCount4; ++i) { __m128i side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0); __m128i right = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1); __m128i left = _mm_add_epi32(right, side); _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 0), _mm_unpacklo_epi32(left, right)); _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 4), _mm_unpackhi_epi32(left, right)); } for (i = (frameCount4 << 2); i < frameCount; ++i) { drflac_uint32 side = pInputSamples0U32[i] << shift0; drflac_uint32 right = pInputSamples1U32[i] << shift1; drflac_uint32 left = right + side; pOutputSamples[i*2+0] = (drflac_int32)left; pOutputSamples[i*2+1] = (drflac_int32)right; } } #endif #if defined(DRFLAC_SUPPORT_NEON) static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_right_side__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) { drflac_uint64 i; drflac_uint64 frameCount4 = frameCount >> 2; const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; int32x4_t shift0_4; int32x4_t shift1_4; DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); shift0_4 = vdupq_n_s32(shift0); shift1_4 = vdupq_n_s32(shift1); for (i = 0; i < frameCount4; ++i) { uint32x4_t side; uint32x4_t right; uint32x4_t left; side = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4); right = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4); left = vaddq_u32(right, side); drflac__vst2q_u32((drflac_uint32*)pOutputSamples + i*8, vzipq_u32(left, right)); } for (i = (frameCount4 << 2); i < frameCount; ++i) { drflac_uint32 side = pInputSamples0U32[i] << shift0; drflac_uint32 right = pInputSamples1U32[i] << shift1; drflac_uint32 left = right + side; pOutputSamples[i*2+0] = (drflac_int32)left; pOutputSamples[i*2+1] = (drflac_int32)right; } } #endif static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_right_side(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) { #if defined(DRFLAC_SUPPORT_SSE2) if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { drflac_read_pcm_frames_s32__decode_right_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); } else #elif defined(DRFLAC_SUPPORT_NEON) if (drflac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { drflac_read_pcm_frames_s32__decode_right_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); } else #endif { #if 0 drflac_read_pcm_frames_s32__decode_right_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); #else drflac_read_pcm_frames_s32__decode_right_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); #endif } } #if 0 static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_mid_side__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) { for (drflac_uint64 i = 0; i < frameCount; ++i) { drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; mid = (mid << 1) | (side & 0x01); pOutputSamples[i*2+0] = (drflac_int32)((drflac_uint32)((drflac_int32)(mid + side) >> 1) << unusedBitsPerSample); pOutputSamples[i*2+1] = (drflac_int32)((drflac_uint32)((drflac_int32)(mid - side) >> 1) << unusedBitsPerSample); } } #endif static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_mid_side__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) { drflac_uint64 i; drflac_uint64 frameCount4 = frameCount >> 2; const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; drflac_int32 shift = unusedBitsPerSample; if (shift > 0) { shift -= 1; for (i = 0; i < frameCount4; ++i) { drflac_uint32 temp0L; drflac_uint32 temp1L; drflac_uint32 temp2L; drflac_uint32 temp3L; drflac_uint32 temp0R; drflac_uint32 temp1R; drflac_uint32 temp2R; drflac_uint32 temp3R; drflac_uint32 mid0 = pInputSamples0U32[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; drflac_uint32 mid1 = pInputSamples0U32[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; drflac_uint32 mid2 = pInputSamples0U32[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; drflac_uint32 mid3 = pInputSamples0U32[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; drflac_uint32 side0 = pInputSamples1U32[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; drflac_uint32 side1 = pInputSamples1U32[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; drflac_uint32 side2 = pInputSamples1U32[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; drflac_uint32 side3 = pInputSamples1U32[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; mid0 = (mid0 << 1) | (side0 & 0x01); mid1 = (mid1 << 1) | (side1 & 0x01); mid2 = (mid2 << 1) | (side2 & 0x01); mid3 = (mid3 << 1) | (side3 & 0x01); temp0L = (mid0 + side0) << shift; temp1L = (mid1 + side1) << shift; temp2L = (mid2 + side2) << shift; temp3L = (mid3 + side3) << shift; temp0R = (mid0 - side0) << shift; temp1R = (mid1 - side1) << shift; temp2R = (mid2 - side2) << shift; temp3R = (mid3 - side3) << shift; pOutputSamples[i*8+0] = (drflac_int32)temp0L; pOutputSamples[i*8+1] = (drflac_int32)temp0R; pOutputSamples[i*8+2] = (drflac_int32)temp1L; pOutputSamples[i*8+3] = (drflac_int32)temp1R; pOutputSamples[i*8+4] = (drflac_int32)temp2L; pOutputSamples[i*8+5] = (drflac_int32)temp2R; pOutputSamples[i*8+6] = (drflac_int32)temp3L; pOutputSamples[i*8+7] = (drflac_int32)temp3R; } } else { for (i = 0; i < frameCount4; ++i) { drflac_uint32 temp0L; drflac_uint32 temp1L; drflac_uint32 temp2L; drflac_uint32 temp3L; drflac_uint32 temp0R; drflac_uint32 temp1R; drflac_uint32 temp2R; drflac_uint32 temp3R; drflac_uint32 mid0 = pInputSamples0U32[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; drflac_uint32 mid1 = pInputSamples0U32[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; drflac_uint32 mid2 = pInputSamples0U32[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; drflac_uint32 mid3 = pInputSamples0U32[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; drflac_uint32 side0 = pInputSamples1U32[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; drflac_uint32 side1 = pInputSamples1U32[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; drflac_uint32 side2 = pInputSamples1U32[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; drflac_uint32 side3 = pInputSamples1U32[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; mid0 = (mid0 << 1) | (side0 & 0x01); mid1 = (mid1 << 1) | (side1 & 0x01); mid2 = (mid2 << 1) | (side2 & 0x01); mid3 = (mid3 << 1) | (side3 & 0x01); temp0L = (drflac_uint32)((drflac_int32)(mid0 + side0) >> 1); temp1L = (drflac_uint32)((drflac_int32)(mid1 + side1) >> 1); temp2L = (drflac_uint32)((drflac_int32)(mid2 + side2) >> 1); temp3L = (drflac_uint32)((drflac_int32)(mid3 + side3) >> 1); temp0R = (drflac_uint32)((drflac_int32)(mid0 - side0) >> 1); temp1R = (drflac_uint32)((drflac_int32)(mid1 - side1) >> 1); temp2R = (drflac_uint32)((drflac_int32)(mid2 - side2) >> 1); temp3R = (drflac_uint32)((drflac_int32)(mid3 - side3) >> 1); pOutputSamples[i*8+0] = (drflac_int32)temp0L; pOutputSamples[i*8+1] = (drflac_int32)temp0R; pOutputSamples[i*8+2] = (drflac_int32)temp1L; pOutputSamples[i*8+3] = (drflac_int32)temp1R; pOutputSamples[i*8+4] = (drflac_int32)temp2L; pOutputSamples[i*8+5] = (drflac_int32)temp2R; pOutputSamples[i*8+6] = (drflac_int32)temp3L; pOutputSamples[i*8+7] = (drflac_int32)temp3R; } } for (i = (frameCount4 << 2); i < frameCount; ++i) { drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; mid = (mid << 1) | (side & 0x01); pOutputSamples[i*2+0] = (drflac_int32)((drflac_uint32)((drflac_int32)(mid + side) >> 1) << unusedBitsPerSample); pOutputSamples[i*2+1] = (drflac_int32)((drflac_uint32)((drflac_int32)(mid - side) >> 1) << unusedBitsPerSample); } } #if defined(DRFLAC_SUPPORT_SSE2) static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_mid_side__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) { drflac_uint64 i; drflac_uint64 frameCount4 = frameCount >> 2; const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; drflac_int32 shift = unusedBitsPerSample; DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); if (shift == 0) { for (i = 0; i < frameCount4; ++i) { __m128i mid; __m128i side; __m128i left; __m128i right; mid = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); mid = _mm_or_si128(_mm_slli_epi32(mid, 1), _mm_and_si128(side, _mm_set1_epi32(0x01))); left = _mm_srai_epi32(_mm_add_epi32(mid, side), 1); right = _mm_srai_epi32(_mm_sub_epi32(mid, side), 1); _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 0), _mm_unpacklo_epi32(left, right)); _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 4), _mm_unpackhi_epi32(left, right)); } for (i = (frameCount4 << 2); i < frameCount; ++i) { drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; mid = (mid << 1) | (side & 0x01); pOutputSamples[i*2+0] = (drflac_int32)(mid + side) >> 1; pOutputSamples[i*2+1] = (drflac_int32)(mid - side) >> 1; } } else { shift -= 1; for (i = 0; i < frameCount4; ++i) { __m128i mid; __m128i side; __m128i left; __m128i right; mid = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); mid = _mm_or_si128(_mm_slli_epi32(mid, 1), _mm_and_si128(side, _mm_set1_epi32(0x01))); left = _mm_slli_epi32(_mm_add_epi32(mid, side), shift); right = _mm_slli_epi32(_mm_sub_epi32(mid, side), shift); _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 0), _mm_unpacklo_epi32(left, right)); _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 4), _mm_unpackhi_epi32(left, right)); } for (i = (frameCount4 << 2); i < frameCount; ++i) { drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; mid = (mid << 1) | (side & 0x01); pOutputSamples[i*2+0] = (drflac_int32)((mid + side) << shift); pOutputSamples[i*2+1] = (drflac_int32)((mid - side) << shift); } } } #endif #if defined(DRFLAC_SUPPORT_NEON) static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_mid_side__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) { drflac_uint64 i; drflac_uint64 frameCount4 = frameCount >> 2; const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; drflac_int32 shift = unusedBitsPerSample; int32x4_t wbpsShift0_4; int32x4_t wbpsShift1_4; uint32x4_t one4; DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); wbpsShift0_4 = vdupq_n_s32(pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); wbpsShift1_4 = vdupq_n_s32(pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); one4 = vdupq_n_u32(1); if (shift == 0) { for (i = 0; i < frameCount4; ++i) { uint32x4_t mid; uint32x4_t side; int32x4_t left; int32x4_t right; mid = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), wbpsShift0_4); side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), wbpsShift1_4); mid = vorrq_u32(vshlq_n_u32(mid, 1), vandq_u32(side, one4)); left = vshrq_n_s32(vreinterpretq_s32_u32(vaddq_u32(mid, side)), 1); right = vshrq_n_s32(vreinterpretq_s32_u32(vsubq_u32(mid, side)), 1); drflac__vst2q_s32(pOutputSamples + i*8, vzipq_s32(left, right)); } for (i = (frameCount4 << 2); i < frameCount; ++i) { drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; mid = (mid << 1) | (side & 0x01); pOutputSamples[i*2+0] = (drflac_int32)(mid + side) >> 1; pOutputSamples[i*2+1] = (drflac_int32)(mid - side) >> 1; } } else { int32x4_t shift4; shift -= 1; shift4 = vdupq_n_s32(shift); for (i = 0; i < frameCount4; ++i) { uint32x4_t mid; uint32x4_t side; int32x4_t left; int32x4_t right; mid = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), wbpsShift0_4); side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), wbpsShift1_4); mid = vorrq_u32(vshlq_n_u32(mid, 1), vandq_u32(side, one4)); left = vreinterpretq_s32_u32(vshlq_u32(vaddq_u32(mid, side), shift4)); right = vreinterpretq_s32_u32(vshlq_u32(vsubq_u32(mid, side), shift4)); drflac__vst2q_s32(pOutputSamples + i*8, vzipq_s32(left, right)); } for (i = (frameCount4 << 2); i < frameCount; ++i) { drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; mid = (mid << 1) | (side & 0x01); pOutputSamples[i*2+0] = (drflac_int32)((mid + side) << shift); pOutputSamples[i*2+1] = (drflac_int32)((mid - side) << shift); } } } #endif static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_mid_side(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) { #if defined(DRFLAC_SUPPORT_SSE2) if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { drflac_read_pcm_frames_s32__decode_mid_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); } else #elif defined(DRFLAC_SUPPORT_NEON) if (drflac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { drflac_read_pcm_frames_s32__decode_mid_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); } else #endif { #if 0 drflac_read_pcm_frames_s32__decode_mid_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); #else drflac_read_pcm_frames_s32__decode_mid_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); #endif } } #if 0 static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_independent_stereo__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) { for (drflac_uint64 i = 0; i < frameCount; ++i) { pOutputSamples[i*2+0] = (drflac_int32)((drflac_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample)); pOutputSamples[i*2+1] = (drflac_int32)((drflac_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample)); } } #endif static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_independent_stereo__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) { drflac_uint64 i; drflac_uint64 frameCount4 = frameCount >> 2; const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; for (i = 0; i < frameCount4; ++i) { drflac_uint32 tempL0 = pInputSamples0U32[i*4+0] << shift0; drflac_uint32 tempL1 = pInputSamples0U32[i*4+1] << shift0; drflac_uint32 tempL2 = pInputSamples0U32[i*4+2] << shift0; drflac_uint32 tempL3 = pInputSamples0U32[i*4+3] << shift0; drflac_uint32 tempR0 = pInputSamples1U32[i*4+0] << shift1; drflac_uint32 tempR1 = pInputSamples1U32[i*4+1] << shift1; drflac_uint32 tempR2 = pInputSamples1U32[i*4+2] << shift1; drflac_uint32 tempR3 = pInputSamples1U32[i*4+3] << shift1; pOutputSamples[i*8+0] = (drflac_int32)tempL0; pOutputSamples[i*8+1] = (drflac_int32)tempR0; pOutputSamples[i*8+2] = (drflac_int32)tempL1; pOutputSamples[i*8+3] = (drflac_int32)tempR1; pOutputSamples[i*8+4] = (drflac_int32)tempL2; pOutputSamples[i*8+5] = (drflac_int32)tempR2; pOutputSamples[i*8+6] = (drflac_int32)tempL3; pOutputSamples[i*8+7] = (drflac_int32)tempR3; } for (i = (frameCount4 << 2); i < frameCount; ++i) { pOutputSamples[i*2+0] = (drflac_int32)(pInputSamples0U32[i] << shift0); pOutputSamples[i*2+1] = (drflac_int32)(pInputSamples1U32[i] << shift1); } } #if defined(DRFLAC_SUPPORT_SSE2) static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_independent_stereo__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) { drflac_uint64 i; drflac_uint64 frameCount4 = frameCount >> 2; const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; for (i = 0; i < frameCount4; ++i) { __m128i left = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0); __m128i right = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1); _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 0), _mm_unpacklo_epi32(left, right)); _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 4), _mm_unpackhi_epi32(left, right)); } for (i = (frameCount4 << 2); i < frameCount; ++i) { pOutputSamples[i*2+0] = (drflac_int32)(pInputSamples0U32[i] << shift0); pOutputSamples[i*2+1] = (drflac_int32)(pInputSamples1U32[i] << shift1); } } #endif #if defined(DRFLAC_SUPPORT_NEON) static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_independent_stereo__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) { drflac_uint64 i; drflac_uint64 frameCount4 = frameCount >> 2; const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; int32x4_t shift4_0 = vdupq_n_s32(shift0); int32x4_t shift4_1 = vdupq_n_s32(shift1); for (i = 0; i < frameCount4; ++i) { int32x4_t left; int32x4_t right; left = vreinterpretq_s32_u32(vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift4_0)); right = vreinterpretq_s32_u32(vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift4_1)); drflac__vst2q_s32(pOutputSamples + i*8, vzipq_s32(left, right)); } for (i = (frameCount4 << 2); i < frameCount; ++i) { pOutputSamples[i*2+0] = (drflac_int32)(pInputSamples0U32[i] << shift0); pOutputSamples[i*2+1] = (drflac_int32)(pInputSamples1U32[i] << shift1); } } #endif static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_independent_stereo(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) { #if defined(DRFLAC_SUPPORT_SSE2) if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { drflac_read_pcm_frames_s32__decode_independent_stereo__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); } else #elif defined(DRFLAC_SUPPORT_NEON) if (drflac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { drflac_read_pcm_frames_s32__decode_independent_stereo__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); } else #endif { #if 0 drflac_read_pcm_frames_s32__decode_independent_stereo__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); #else drflac_read_pcm_frames_s32__decode_independent_stereo__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); #endif } } DRFLAC_API drflac_uint64 drflac_read_pcm_frames_s32(drflac* pFlac, drflac_uint64 framesToRead, drflac_int32* pBufferOut) { drflac_uint64 framesRead; drflac_uint32 unusedBitsPerSample; if (pFlac == NULL || framesToRead == 0) { return 0; } if (pBufferOut == NULL) { return drflac__seek_forward_by_pcm_frames(pFlac, framesToRead); } DRFLAC_ASSERT(pFlac->bitsPerSample <= 32); unusedBitsPerSample = 32 - pFlac->bitsPerSample; framesRead = 0; while (framesToRead > 0) { if (pFlac->currentFLACFrame.pcmFramesRemaining == 0) { if (!drflac__read_and_decode_next_flac_frame(pFlac)) { break; } } else { unsigned int channelCount = drflac__get_channel_count_from_channel_assignment(pFlac->currentFLACFrame.header.channelAssignment); drflac_uint64 iFirstPCMFrame = pFlac->currentFLACFrame.header.blockSizeInPCMFrames - pFlac->currentFLACFrame.pcmFramesRemaining; drflac_uint64 frameCountThisIteration = framesToRead; if (frameCountThisIteration > pFlac->currentFLACFrame.pcmFramesRemaining) { frameCountThisIteration = pFlac->currentFLACFrame.pcmFramesRemaining; } if (channelCount == 2) { const drflac_int32* pDecodedSamples0 = pFlac->currentFLACFrame.subframes[0].pSamplesS32 + iFirstPCMFrame; const drflac_int32* pDecodedSamples1 = pFlac->currentFLACFrame.subframes[1].pSamplesS32 + iFirstPCMFrame; switch (pFlac->currentFLACFrame.header.channelAssignment) { case DRFLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE: { drflac_read_pcm_frames_s32__decode_left_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); } break; case DRFLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE: { drflac_read_pcm_frames_s32__decode_right_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); } break; case DRFLAC_CHANNEL_ASSIGNMENT_MID_SIDE: { drflac_read_pcm_frames_s32__decode_mid_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); } break; case DRFLAC_CHANNEL_ASSIGNMENT_INDEPENDENT: default: { drflac_read_pcm_frames_s32__decode_independent_stereo(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); } break; } } else { drflac_uint64 i; for (i = 0; i < frameCountThisIteration; ++i) { unsigned int j; for (j = 0; j < channelCount; ++j) { pBufferOut[(i*channelCount)+j] = (drflac_int32)((drflac_uint32)(pFlac->currentFLACFrame.subframes[j].pSamplesS32[iFirstPCMFrame + i]) << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[j].wastedBitsPerSample)); } } } framesRead += frameCountThisIteration; pBufferOut += frameCountThisIteration * channelCount; framesToRead -= frameCountThisIteration; pFlac->currentPCMFrame += frameCountThisIteration; pFlac->currentFLACFrame.pcmFramesRemaining -= (drflac_uint32)frameCountThisIteration; } } return framesRead; } #if 0 static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_left_side__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) { drflac_uint64 i; for (i = 0; i < frameCount; ++i) { drflac_uint32 left = (drflac_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); drflac_uint32 side = (drflac_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); drflac_uint32 right = left - side; left >>= 16; right >>= 16; pOutputSamples[i*2+0] = (drflac_int16)left; pOutputSamples[i*2+1] = (drflac_int16)right; } } #endif static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_left_side__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) { drflac_uint64 i; drflac_uint64 frameCount4 = frameCount >> 2; const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; for (i = 0; i < frameCount4; ++i) { drflac_uint32 left0 = pInputSamples0U32[i*4+0] << shift0; drflac_uint32 left1 = pInputSamples0U32[i*4+1] << shift0; drflac_uint32 left2 = pInputSamples0U32[i*4+2] << shift0; drflac_uint32 left3 = pInputSamples0U32[i*4+3] << shift0; drflac_uint32 side0 = pInputSamples1U32[i*4+0] << shift1; drflac_uint32 side1 = pInputSamples1U32[i*4+1] << shift1; drflac_uint32 side2 = pInputSamples1U32[i*4+2] << shift1; drflac_uint32 side3 = pInputSamples1U32[i*4+3] << shift1; drflac_uint32 right0 = left0 - side0; drflac_uint32 right1 = left1 - side1; drflac_uint32 right2 = left2 - side2; drflac_uint32 right3 = left3 - side3; left0 >>= 16; left1 >>= 16; left2 >>= 16; left3 >>= 16; right0 >>= 16; right1 >>= 16; right2 >>= 16; right3 >>= 16; pOutputSamples[i*8+0] = (drflac_int16)left0; pOutputSamples[i*8+1] = (drflac_int16)right0; pOutputSamples[i*8+2] = (drflac_int16)left1; pOutputSamples[i*8+3] = (drflac_int16)right1; pOutputSamples[i*8+4] = (drflac_int16)left2; pOutputSamples[i*8+5] = (drflac_int16)right2; pOutputSamples[i*8+6] = (drflac_int16)left3; pOutputSamples[i*8+7] = (drflac_int16)right3; } for (i = (frameCount4 << 2); i < frameCount; ++i) { drflac_uint32 left = pInputSamples0U32[i] << shift0; drflac_uint32 side = pInputSamples1U32[i] << shift1; drflac_uint32 right = left - side; left >>= 16; right >>= 16; pOutputSamples[i*2+0] = (drflac_int16)left; pOutputSamples[i*2+1] = (drflac_int16)right; } } #if defined(DRFLAC_SUPPORT_SSE2) static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_left_side__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) { drflac_uint64 i; drflac_uint64 frameCount4 = frameCount >> 2; const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); for (i = 0; i < frameCount4; ++i) { __m128i left = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0); __m128i side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1); __m128i right = _mm_sub_epi32(left, side); left = _mm_srai_epi32(left, 16); right = _mm_srai_epi32(right, 16); _mm_storeu_si128((__m128i*)(pOutputSamples + i*8), drflac__mm_packs_interleaved_epi32(left, right)); } for (i = (frameCount4 << 2); i < frameCount; ++i) { drflac_uint32 left = pInputSamples0U32[i] << shift0; drflac_uint32 side = pInputSamples1U32[i] << shift1; drflac_uint32 right = left - side; left >>= 16; right >>= 16; pOutputSamples[i*2+0] = (drflac_int16)left; pOutputSamples[i*2+1] = (drflac_int16)right; } } #endif #if defined(DRFLAC_SUPPORT_NEON) static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_left_side__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) { drflac_uint64 i; drflac_uint64 frameCount4 = frameCount >> 2; const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; int32x4_t shift0_4; int32x4_t shift1_4; DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); shift0_4 = vdupq_n_s32(shift0); shift1_4 = vdupq_n_s32(shift1); for (i = 0; i < frameCount4; ++i) { uint32x4_t left; uint32x4_t side; uint32x4_t right; left = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4); side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4); right = vsubq_u32(left, side); left = vshrq_n_u32(left, 16); right = vshrq_n_u32(right, 16); drflac__vst2q_u16((drflac_uint16*)pOutputSamples + i*8, vzip_u16(vmovn_u32(left), vmovn_u32(right))); } for (i = (frameCount4 << 2); i < frameCount; ++i) { drflac_uint32 left = pInputSamples0U32[i] << shift0; drflac_uint32 side = pInputSamples1U32[i] << shift1; drflac_uint32 right = left - side; left >>= 16; right >>= 16; pOutputSamples[i*2+0] = (drflac_int16)left; pOutputSamples[i*2+1] = (drflac_int16)right; } } #endif static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_left_side(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) { #if defined(DRFLAC_SUPPORT_SSE2) if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { drflac_read_pcm_frames_s16__decode_left_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); } else #elif defined(DRFLAC_SUPPORT_NEON) if (drflac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { drflac_read_pcm_frames_s16__decode_left_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); } else #endif { #if 0 drflac_read_pcm_frames_s16__decode_left_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); #else drflac_read_pcm_frames_s16__decode_left_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); #endif } } #if 0 static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_right_side__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) { drflac_uint64 i; for (i = 0; i < frameCount; ++i) { drflac_uint32 side = (drflac_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); drflac_uint32 right = (drflac_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); drflac_uint32 left = right + side; left >>= 16; right >>= 16; pOutputSamples[i*2+0] = (drflac_int16)left; pOutputSamples[i*2+1] = (drflac_int16)right; } } #endif static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_right_side__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) { drflac_uint64 i; drflac_uint64 frameCount4 = frameCount >> 2; const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; for (i = 0; i < frameCount4; ++i) { drflac_uint32 side0 = pInputSamples0U32[i*4+0] << shift0; drflac_uint32 side1 = pInputSamples0U32[i*4+1] << shift0; drflac_uint32 side2 = pInputSamples0U32[i*4+2] << shift0; drflac_uint32 side3 = pInputSamples0U32[i*4+3] << shift0; drflac_uint32 right0 = pInputSamples1U32[i*4+0] << shift1; drflac_uint32 right1 = pInputSamples1U32[i*4+1] << shift1; drflac_uint32 right2 = pInputSamples1U32[i*4+2] << shift1; drflac_uint32 right3 = pInputSamples1U32[i*4+3] << shift1; drflac_uint32 left0 = right0 + side0; drflac_uint32 left1 = right1 + side1; drflac_uint32 left2 = right2 + side2; drflac_uint32 left3 = right3 + side3; left0 >>= 16; left1 >>= 16; left2 >>= 16; left3 >>= 16; right0 >>= 16; right1 >>= 16; right2 >>= 16; right3 >>= 16; pOutputSamples[i*8+0] = (drflac_int16)left0; pOutputSamples[i*8+1] = (drflac_int16)right0; pOutputSamples[i*8+2] = (drflac_int16)left1; pOutputSamples[i*8+3] = (drflac_int16)right1; pOutputSamples[i*8+4] = (drflac_int16)left2; pOutputSamples[i*8+5] = (drflac_int16)right2; pOutputSamples[i*8+6] = (drflac_int16)left3; pOutputSamples[i*8+7] = (drflac_int16)right3; } for (i = (frameCount4 << 2); i < frameCount; ++i) { drflac_uint32 side = pInputSamples0U32[i] << shift0; drflac_uint32 right = pInputSamples1U32[i] << shift1; drflac_uint32 left = right + side; left >>= 16; right >>= 16; pOutputSamples[i*2+0] = (drflac_int16)left; pOutputSamples[i*2+1] = (drflac_int16)right; } } #if defined(DRFLAC_SUPPORT_SSE2) static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_right_side__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) { drflac_uint64 i; drflac_uint64 frameCount4 = frameCount >> 2; const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); for (i = 0; i < frameCount4; ++i) { __m128i side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0); __m128i right = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1); __m128i left = _mm_add_epi32(right, side); left = _mm_srai_epi32(left, 16); right = _mm_srai_epi32(right, 16); _mm_storeu_si128((__m128i*)(pOutputSamples + i*8), drflac__mm_packs_interleaved_epi32(left, right)); } for (i = (frameCount4 << 2); i < frameCount; ++i) { drflac_uint32 side = pInputSamples0U32[i] << shift0; drflac_uint32 right = pInputSamples1U32[i] << shift1; drflac_uint32 left = right + side; left >>= 16; right >>= 16; pOutputSamples[i*2+0] = (drflac_int16)left; pOutputSamples[i*2+1] = (drflac_int16)right; } } #endif #if defined(DRFLAC_SUPPORT_NEON) static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_right_side__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) { drflac_uint64 i; drflac_uint64 frameCount4 = frameCount >> 2; const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; int32x4_t shift0_4; int32x4_t shift1_4; DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); shift0_4 = vdupq_n_s32(shift0); shift1_4 = vdupq_n_s32(shift1); for (i = 0; i < frameCount4; ++i) { uint32x4_t side; uint32x4_t right; uint32x4_t left; side = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4); right = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4); left = vaddq_u32(right, side); left = vshrq_n_u32(left, 16); right = vshrq_n_u32(right, 16); drflac__vst2q_u16((drflac_uint16*)pOutputSamples + i*8, vzip_u16(vmovn_u32(left), vmovn_u32(right))); } for (i = (frameCount4 << 2); i < frameCount; ++i) { drflac_uint32 side = pInputSamples0U32[i] << shift0; drflac_uint32 right = pInputSamples1U32[i] << shift1; drflac_uint32 left = right + side; left >>= 16; right >>= 16; pOutputSamples[i*2+0] = (drflac_int16)left; pOutputSamples[i*2+1] = (drflac_int16)right; } } #endif static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_right_side(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) { #if defined(DRFLAC_SUPPORT_SSE2) if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { drflac_read_pcm_frames_s16__decode_right_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); } else #elif defined(DRFLAC_SUPPORT_NEON) if (drflac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { drflac_read_pcm_frames_s16__decode_right_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); } else #endif { #if 0 drflac_read_pcm_frames_s16__decode_right_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); #else drflac_read_pcm_frames_s16__decode_right_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); #endif } } #if 0 static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_mid_side__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) { for (drflac_uint64 i = 0; i < frameCount; ++i) { drflac_uint32 mid = (drflac_uint32)pInputSamples0[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; drflac_uint32 side = (drflac_uint32)pInputSamples1[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; mid = (mid << 1) | (side & 0x01); pOutputSamples[i*2+0] = (drflac_int16)(((drflac_uint32)((drflac_int32)(mid + side) >> 1) << unusedBitsPerSample) >> 16); pOutputSamples[i*2+1] = (drflac_int16)(((drflac_uint32)((drflac_int32)(mid - side) >> 1) << unusedBitsPerSample) >> 16); } } #endif static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_mid_side__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) { drflac_uint64 i; drflac_uint64 frameCount4 = frameCount >> 2; const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; drflac_uint32 shift = unusedBitsPerSample; if (shift > 0) { shift -= 1; for (i = 0; i < frameCount4; ++i) { drflac_uint32 temp0L; drflac_uint32 temp1L; drflac_uint32 temp2L; drflac_uint32 temp3L; drflac_uint32 temp0R; drflac_uint32 temp1R; drflac_uint32 temp2R; drflac_uint32 temp3R; drflac_uint32 mid0 = pInputSamples0U32[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; drflac_uint32 mid1 = pInputSamples0U32[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; drflac_uint32 mid2 = pInputSamples0U32[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; drflac_uint32 mid3 = pInputSamples0U32[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; drflac_uint32 side0 = pInputSamples1U32[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; drflac_uint32 side1 = pInputSamples1U32[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; drflac_uint32 side2 = pInputSamples1U32[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; drflac_uint32 side3 = pInputSamples1U32[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; mid0 = (mid0 << 1) | (side0 & 0x01); mid1 = (mid1 << 1) | (side1 & 0x01); mid2 = (mid2 << 1) | (side2 & 0x01); mid3 = (mid3 << 1) | (side3 & 0x01); temp0L = (mid0 + side0) << shift; temp1L = (mid1 + side1) << shift; temp2L = (mid2 + side2) << shift; temp3L = (mid3 + side3) << shift; temp0R = (mid0 - side0) << shift; temp1R = (mid1 - side1) << shift; temp2R = (mid2 - side2) << shift; temp3R = (mid3 - side3) << shift; temp0L >>= 16; temp1L >>= 16; temp2L >>= 16; temp3L >>= 16; temp0R >>= 16; temp1R >>= 16; temp2R >>= 16; temp3R >>= 16; pOutputSamples[i*8+0] = (drflac_int16)temp0L; pOutputSamples[i*8+1] = (drflac_int16)temp0R; pOutputSamples[i*8+2] = (drflac_int16)temp1L; pOutputSamples[i*8+3] = (drflac_int16)temp1R; pOutputSamples[i*8+4] = (drflac_int16)temp2L; pOutputSamples[i*8+5] = (drflac_int16)temp2R; pOutputSamples[i*8+6] = (drflac_int16)temp3L; pOutputSamples[i*8+7] = (drflac_int16)temp3R; } } else { for (i = 0; i < frameCount4; ++i) { drflac_uint32 temp0L; drflac_uint32 temp1L; drflac_uint32 temp2L; drflac_uint32 temp3L; drflac_uint32 temp0R; drflac_uint32 temp1R; drflac_uint32 temp2R; drflac_uint32 temp3R; drflac_uint32 mid0 = pInputSamples0U32[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; drflac_uint32 mid1 = pInputSamples0U32[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; drflac_uint32 mid2 = pInputSamples0U32[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; drflac_uint32 mid3 = pInputSamples0U32[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; drflac_uint32 side0 = pInputSamples1U32[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; drflac_uint32 side1 = pInputSamples1U32[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; drflac_uint32 side2 = pInputSamples1U32[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; drflac_uint32 side3 = pInputSamples1U32[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; mid0 = (mid0 << 1) | (side0 & 0x01); mid1 = (mid1 << 1) | (side1 & 0x01); mid2 = (mid2 << 1) | (side2 & 0x01); mid3 = (mid3 << 1) | (side3 & 0x01); temp0L = ((drflac_int32)(mid0 + side0) >> 1); temp1L = ((drflac_int32)(mid1 + side1) >> 1); temp2L = ((drflac_int32)(mid2 + side2) >> 1); temp3L = ((drflac_int32)(mid3 + side3) >> 1); temp0R = ((drflac_int32)(mid0 - side0) >> 1); temp1R = ((drflac_int32)(mid1 - side1) >> 1); temp2R = ((drflac_int32)(mid2 - side2) >> 1); temp3R = ((drflac_int32)(mid3 - side3) >> 1); temp0L >>= 16; temp1L >>= 16; temp2L >>= 16; temp3L >>= 16; temp0R >>= 16; temp1R >>= 16; temp2R >>= 16; temp3R >>= 16; pOutputSamples[i*8+0] = (drflac_int16)temp0L; pOutputSamples[i*8+1] = (drflac_int16)temp0R; pOutputSamples[i*8+2] = (drflac_int16)temp1L; pOutputSamples[i*8+3] = (drflac_int16)temp1R; pOutputSamples[i*8+4] = (drflac_int16)temp2L; pOutputSamples[i*8+5] = (drflac_int16)temp2R; pOutputSamples[i*8+6] = (drflac_int16)temp3L; pOutputSamples[i*8+7] = (drflac_int16)temp3R; } } for (i = (frameCount4 << 2); i < frameCount; ++i) { drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; mid = (mid << 1) | (side & 0x01); pOutputSamples[i*2+0] = (drflac_int16)(((drflac_uint32)((drflac_int32)(mid + side) >> 1) << unusedBitsPerSample) >> 16); pOutputSamples[i*2+1] = (drflac_int16)(((drflac_uint32)((drflac_int32)(mid - side) >> 1) << unusedBitsPerSample) >> 16); } } #if defined(DRFLAC_SUPPORT_SSE2) static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_mid_side__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) { drflac_uint64 i; drflac_uint64 frameCount4 = frameCount >> 2; const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; drflac_uint32 shift = unusedBitsPerSample; DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); if (shift == 0) { for (i = 0; i < frameCount4; ++i) { __m128i mid; __m128i side; __m128i left; __m128i right; mid = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); mid = _mm_or_si128(_mm_slli_epi32(mid, 1), _mm_and_si128(side, _mm_set1_epi32(0x01))); left = _mm_srai_epi32(_mm_add_epi32(mid, side), 1); right = _mm_srai_epi32(_mm_sub_epi32(mid, side), 1); left = _mm_srai_epi32(left, 16); right = _mm_srai_epi32(right, 16); _mm_storeu_si128((__m128i*)(pOutputSamples + i*8), drflac__mm_packs_interleaved_epi32(left, right)); } for (i = (frameCount4 << 2); i < frameCount; ++i) { drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; mid = (mid << 1) | (side & 0x01); pOutputSamples[i*2+0] = (drflac_int16)(((drflac_int32)(mid + side) >> 1) >> 16); pOutputSamples[i*2+1] = (drflac_int16)(((drflac_int32)(mid - side) >> 1) >> 16); } } else { shift -= 1; for (i = 0; i < frameCount4; ++i) { __m128i mid; __m128i side; __m128i left; __m128i right; mid = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); mid = _mm_or_si128(_mm_slli_epi32(mid, 1), _mm_and_si128(side, _mm_set1_epi32(0x01))); left = _mm_slli_epi32(_mm_add_epi32(mid, side), shift); right = _mm_slli_epi32(_mm_sub_epi32(mid, side), shift); left = _mm_srai_epi32(left, 16); right = _mm_srai_epi32(right, 16); _mm_storeu_si128((__m128i*)(pOutputSamples + i*8), drflac__mm_packs_interleaved_epi32(left, right)); } for (i = (frameCount4 << 2); i < frameCount; ++i) { drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; mid = (mid << 1) | (side & 0x01); pOutputSamples[i*2+0] = (drflac_int16)(((mid + side) << shift) >> 16); pOutputSamples[i*2+1] = (drflac_int16)(((mid - side) << shift) >> 16); } } } #endif #if defined(DRFLAC_SUPPORT_NEON) static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_mid_side__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) { drflac_uint64 i; drflac_uint64 frameCount4 = frameCount >> 2; const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; drflac_uint32 shift = unusedBitsPerSample; int32x4_t wbpsShift0_4; int32x4_t wbpsShift1_4; DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); wbpsShift0_4 = vdupq_n_s32(pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); wbpsShift1_4 = vdupq_n_s32(pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); if (shift == 0) { for (i = 0; i < frameCount4; ++i) { uint32x4_t mid; uint32x4_t side; int32x4_t left; int32x4_t right; mid = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), wbpsShift0_4); side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), wbpsShift1_4); mid = vorrq_u32(vshlq_n_u32(mid, 1), vandq_u32(side, vdupq_n_u32(1))); left = vshrq_n_s32(vreinterpretq_s32_u32(vaddq_u32(mid, side)), 1); right = vshrq_n_s32(vreinterpretq_s32_u32(vsubq_u32(mid, side)), 1); left = vshrq_n_s32(left, 16); right = vshrq_n_s32(right, 16); drflac__vst2q_s16(pOutputSamples + i*8, vzip_s16(vmovn_s32(left), vmovn_s32(right))); } for (i = (frameCount4 << 2); i < frameCount; ++i) { drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; mid = (mid << 1) | (side & 0x01); pOutputSamples[i*2+0] = (drflac_int16)(((drflac_int32)(mid + side) >> 1) >> 16); pOutputSamples[i*2+1] = (drflac_int16)(((drflac_int32)(mid - side) >> 1) >> 16); } } else { int32x4_t shift4; shift -= 1; shift4 = vdupq_n_s32(shift); for (i = 0; i < frameCount4; ++i) { uint32x4_t mid; uint32x4_t side; int32x4_t left; int32x4_t right; mid = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), wbpsShift0_4); side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), wbpsShift1_4); mid = vorrq_u32(vshlq_n_u32(mid, 1), vandq_u32(side, vdupq_n_u32(1))); left = vreinterpretq_s32_u32(vshlq_u32(vaddq_u32(mid, side), shift4)); right = vreinterpretq_s32_u32(vshlq_u32(vsubq_u32(mid, side), shift4)); left = vshrq_n_s32(left, 16); right = vshrq_n_s32(right, 16); drflac__vst2q_s16(pOutputSamples + i*8, vzip_s16(vmovn_s32(left), vmovn_s32(right))); } for (i = (frameCount4 << 2); i < frameCount; ++i) { drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; mid = (mid << 1) | (side & 0x01); pOutputSamples[i*2+0] = (drflac_int16)(((mid + side) << shift) >> 16); pOutputSamples[i*2+1] = (drflac_int16)(((mid - side) << shift) >> 16); } } } #endif static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_mid_side(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) { #if defined(DRFLAC_SUPPORT_SSE2) if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { drflac_read_pcm_frames_s16__decode_mid_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); } else #elif defined(DRFLAC_SUPPORT_NEON) if (drflac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { drflac_read_pcm_frames_s16__decode_mid_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); } else #endif { #if 0 drflac_read_pcm_frames_s16__decode_mid_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); #else drflac_read_pcm_frames_s16__decode_mid_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); #endif } } #if 0 static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_independent_stereo__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) { for (drflac_uint64 i = 0; i < frameCount; ++i) { pOutputSamples[i*2+0] = (drflac_int16)((drflac_int32)((drflac_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample)) >> 16); pOutputSamples[i*2+1] = (drflac_int16)((drflac_int32)((drflac_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample)) >> 16); } } #endif static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_independent_stereo__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) { drflac_uint64 i; drflac_uint64 frameCount4 = frameCount >> 2; const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; for (i = 0; i < frameCount4; ++i) { drflac_uint32 tempL0 = pInputSamples0U32[i*4+0] << shift0; drflac_uint32 tempL1 = pInputSamples0U32[i*4+1] << shift0; drflac_uint32 tempL2 = pInputSamples0U32[i*4+2] << shift0; drflac_uint32 tempL3 = pInputSamples0U32[i*4+3] << shift0; drflac_uint32 tempR0 = pInputSamples1U32[i*4+0] << shift1; drflac_uint32 tempR1 = pInputSamples1U32[i*4+1] << shift1; drflac_uint32 tempR2 = pInputSamples1U32[i*4+2] << shift1; drflac_uint32 tempR3 = pInputSamples1U32[i*4+3] << shift1; tempL0 >>= 16; tempL1 >>= 16; tempL2 >>= 16; tempL3 >>= 16; tempR0 >>= 16; tempR1 >>= 16; tempR2 >>= 16; tempR3 >>= 16; pOutputSamples[i*8+0] = (drflac_int16)tempL0; pOutputSamples[i*8+1] = (drflac_int16)tempR0; pOutputSamples[i*8+2] = (drflac_int16)tempL1; pOutputSamples[i*8+3] = (drflac_int16)tempR1; pOutputSamples[i*8+4] = (drflac_int16)tempL2; pOutputSamples[i*8+5] = (drflac_int16)tempR2; pOutputSamples[i*8+6] = (drflac_int16)tempL3; pOutputSamples[i*8+7] = (drflac_int16)tempR3; } for (i = (frameCount4 << 2); i < frameCount; ++i) { pOutputSamples[i*2+0] = (drflac_int16)((pInputSamples0U32[i] << shift0) >> 16); pOutputSamples[i*2+1] = (drflac_int16)((pInputSamples1U32[i] << shift1) >> 16); } } #if defined(DRFLAC_SUPPORT_SSE2) static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_independent_stereo__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) { drflac_uint64 i; drflac_uint64 frameCount4 = frameCount >> 2; const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; for (i = 0; i < frameCount4; ++i) { __m128i left = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0); __m128i right = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1); left = _mm_srai_epi32(left, 16); right = _mm_srai_epi32(right, 16); _mm_storeu_si128((__m128i*)(pOutputSamples + i*8), drflac__mm_packs_interleaved_epi32(left, right)); } for (i = (frameCount4 << 2); i < frameCount; ++i) { pOutputSamples[i*2+0] = (drflac_int16)((pInputSamples0U32[i] << shift0) >> 16); pOutputSamples[i*2+1] = (drflac_int16)((pInputSamples1U32[i] << shift1) >> 16); } } #endif #if defined(DRFLAC_SUPPORT_NEON) static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_independent_stereo__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) { drflac_uint64 i; drflac_uint64 frameCount4 = frameCount >> 2; const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; int32x4_t shift0_4 = vdupq_n_s32(shift0); int32x4_t shift1_4 = vdupq_n_s32(shift1); for (i = 0; i < frameCount4; ++i) { int32x4_t left; int32x4_t right; left = vreinterpretq_s32_u32(vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4)); right = vreinterpretq_s32_u32(vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4)); left = vshrq_n_s32(left, 16); right = vshrq_n_s32(right, 16); drflac__vst2q_s16(pOutputSamples + i*8, vzip_s16(vmovn_s32(left), vmovn_s32(right))); } for (i = (frameCount4 << 2); i < frameCount; ++i) { pOutputSamples[i*2+0] = (drflac_int16)((pInputSamples0U32[i] << shift0) >> 16); pOutputSamples[i*2+1] = (drflac_int16)((pInputSamples1U32[i] << shift1) >> 16); } } #endif static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_independent_stereo(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) { #if defined(DRFLAC_SUPPORT_SSE2) if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { drflac_read_pcm_frames_s16__decode_independent_stereo__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); } else #elif defined(DRFLAC_SUPPORT_NEON) if (drflac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { drflac_read_pcm_frames_s16__decode_independent_stereo__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); } else #endif { #if 0 drflac_read_pcm_frames_s16__decode_independent_stereo__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); #else drflac_read_pcm_frames_s16__decode_independent_stereo__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); #endif } } DRFLAC_API drflac_uint64 drflac_read_pcm_frames_s16(drflac* pFlac, drflac_uint64 framesToRead, drflac_int16* pBufferOut) { drflac_uint64 framesRead; drflac_uint32 unusedBitsPerSample; if (pFlac == NULL || framesToRead == 0) { return 0; } if (pBufferOut == NULL) { return drflac__seek_forward_by_pcm_frames(pFlac, framesToRead); } DRFLAC_ASSERT(pFlac->bitsPerSample <= 32); unusedBitsPerSample = 32 - pFlac->bitsPerSample; framesRead = 0; while (framesToRead > 0) { if (pFlac->currentFLACFrame.pcmFramesRemaining == 0) { if (!drflac__read_and_decode_next_flac_frame(pFlac)) { break; } } else { unsigned int channelCount = drflac__get_channel_count_from_channel_assignment(pFlac->currentFLACFrame.header.channelAssignment); drflac_uint64 iFirstPCMFrame = pFlac->currentFLACFrame.header.blockSizeInPCMFrames - pFlac->currentFLACFrame.pcmFramesRemaining; drflac_uint64 frameCountThisIteration = framesToRead; if (frameCountThisIteration > pFlac->currentFLACFrame.pcmFramesRemaining) { frameCountThisIteration = pFlac->currentFLACFrame.pcmFramesRemaining; } if (channelCount == 2) { const drflac_int32* pDecodedSamples0 = pFlac->currentFLACFrame.subframes[0].pSamplesS32 + iFirstPCMFrame; const drflac_int32* pDecodedSamples1 = pFlac->currentFLACFrame.subframes[1].pSamplesS32 + iFirstPCMFrame; switch (pFlac->currentFLACFrame.header.channelAssignment) { case DRFLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE: { drflac_read_pcm_frames_s16__decode_left_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); } break; case DRFLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE: { drflac_read_pcm_frames_s16__decode_right_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); } break; case DRFLAC_CHANNEL_ASSIGNMENT_MID_SIDE: { drflac_read_pcm_frames_s16__decode_mid_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); } break; case DRFLAC_CHANNEL_ASSIGNMENT_INDEPENDENT: default: { drflac_read_pcm_frames_s16__decode_independent_stereo(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); } break; } } else { drflac_uint64 i; for (i = 0; i < frameCountThisIteration; ++i) { unsigned int j; for (j = 0; j < channelCount; ++j) { drflac_int32 sampleS32 = (drflac_int32)((drflac_uint32)(pFlac->currentFLACFrame.subframes[j].pSamplesS32[iFirstPCMFrame + i]) << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[j].wastedBitsPerSample)); pBufferOut[(i*channelCount)+j] = (drflac_int16)(sampleS32 >> 16); } } } framesRead += frameCountThisIteration; pBufferOut += frameCountThisIteration * channelCount; framesToRead -= frameCountThisIteration; pFlac->currentPCMFrame += frameCountThisIteration; pFlac->currentFLACFrame.pcmFramesRemaining -= (drflac_uint32)frameCountThisIteration; } } return framesRead; } #if 0 static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_left_side__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) { drflac_uint64 i; for (i = 0; i < frameCount; ++i) { drflac_uint32 left = (drflac_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); drflac_uint32 side = (drflac_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); drflac_uint32 right = left - side; pOutputSamples[i*2+0] = (float)((drflac_int32)left / 2147483648.0); pOutputSamples[i*2+1] = (float)((drflac_int32)right / 2147483648.0); } } #endif static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_left_side__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) { drflac_uint64 i; drflac_uint64 frameCount4 = frameCount >> 2; const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; float factor = 1 / 2147483648.0; for (i = 0; i < frameCount4; ++i) { drflac_uint32 left0 = pInputSamples0U32[i*4+0] << shift0; drflac_uint32 left1 = pInputSamples0U32[i*4+1] << shift0; drflac_uint32 left2 = pInputSamples0U32[i*4+2] << shift0; drflac_uint32 left3 = pInputSamples0U32[i*4+3] << shift0; drflac_uint32 side0 = pInputSamples1U32[i*4+0] << shift1; drflac_uint32 side1 = pInputSamples1U32[i*4+1] << shift1; drflac_uint32 side2 = pInputSamples1U32[i*4+2] << shift1; drflac_uint32 side3 = pInputSamples1U32[i*4+3] << shift1; drflac_uint32 right0 = left0 - side0; drflac_uint32 right1 = left1 - side1; drflac_uint32 right2 = left2 - side2; drflac_uint32 right3 = left3 - side3; pOutputSamples[i*8+0] = (drflac_int32)left0 * factor; pOutputSamples[i*8+1] = (drflac_int32)right0 * factor; pOutputSamples[i*8+2] = (drflac_int32)left1 * factor; pOutputSamples[i*8+3] = (drflac_int32)right1 * factor; pOutputSamples[i*8+4] = (drflac_int32)left2 * factor; pOutputSamples[i*8+5] = (drflac_int32)right2 * factor; pOutputSamples[i*8+6] = (drflac_int32)left3 * factor; pOutputSamples[i*8+7] = (drflac_int32)right3 * factor; } for (i = (frameCount4 << 2); i < frameCount; ++i) { drflac_uint32 left = pInputSamples0U32[i] << shift0; drflac_uint32 side = pInputSamples1U32[i] << shift1; drflac_uint32 right = left - side; pOutputSamples[i*2+0] = (drflac_int32)left * factor; pOutputSamples[i*2+1] = (drflac_int32)right * factor; } } #if defined(DRFLAC_SUPPORT_SSE2) static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_left_side__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) { drflac_uint64 i; drflac_uint64 frameCount4 = frameCount >> 2; const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; drflac_uint32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8; drflac_uint32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8; __m128 factor; DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); factor = _mm_set1_ps(1.0f / 8388608.0f); for (i = 0; i < frameCount4; ++i) { __m128i left = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0); __m128i side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1); __m128i right = _mm_sub_epi32(left, side); __m128 leftf = _mm_mul_ps(_mm_cvtepi32_ps(left), factor); __m128 rightf = _mm_mul_ps(_mm_cvtepi32_ps(right), factor); _mm_storeu_ps(pOutputSamples + i*8 + 0, _mm_unpacklo_ps(leftf, rightf)); _mm_storeu_ps(pOutputSamples + i*8 + 4, _mm_unpackhi_ps(leftf, rightf)); } for (i = (frameCount4 << 2); i < frameCount; ++i) { drflac_uint32 left = pInputSamples0U32[i] << shift0; drflac_uint32 side = pInputSamples1U32[i] << shift1; drflac_uint32 right = left - side; pOutputSamples[i*2+0] = (drflac_int32)left / 8388608.0f; pOutputSamples[i*2+1] = (drflac_int32)right / 8388608.0f; } } #endif #if defined(DRFLAC_SUPPORT_NEON) static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_left_side__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) { drflac_uint64 i; drflac_uint64 frameCount4 = frameCount >> 2; const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; drflac_uint32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8; drflac_uint32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8; float32x4_t factor4; int32x4_t shift0_4; int32x4_t shift1_4; DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); factor4 = vdupq_n_f32(1.0f / 8388608.0f); shift0_4 = vdupq_n_s32(shift0); shift1_4 = vdupq_n_s32(shift1); for (i = 0; i < frameCount4; ++i) { uint32x4_t left; uint32x4_t side; uint32x4_t right; float32x4_t leftf; float32x4_t rightf; left = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4); side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4); right = vsubq_u32(left, side); leftf = vmulq_f32(vcvtq_f32_s32(vreinterpretq_s32_u32(left)), factor4); rightf = vmulq_f32(vcvtq_f32_s32(vreinterpretq_s32_u32(right)), factor4); drflac__vst2q_f32(pOutputSamples + i*8, vzipq_f32(leftf, rightf)); } for (i = (frameCount4 << 2); i < frameCount; ++i) { drflac_uint32 left = pInputSamples0U32[i] << shift0; drflac_uint32 side = pInputSamples1U32[i] << shift1; drflac_uint32 right = left - side; pOutputSamples[i*2+0] = (drflac_int32)left / 8388608.0f; pOutputSamples[i*2+1] = (drflac_int32)right / 8388608.0f; } } #endif static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_left_side(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) { #if defined(DRFLAC_SUPPORT_SSE2) if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { drflac_read_pcm_frames_f32__decode_left_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); } else #elif defined(DRFLAC_SUPPORT_NEON) if (drflac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { drflac_read_pcm_frames_f32__decode_left_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); } else #endif { #if 0 drflac_read_pcm_frames_f32__decode_left_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); #else drflac_read_pcm_frames_f32__decode_left_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); #endif } } #if 0 static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_right_side__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) { drflac_uint64 i; for (i = 0; i < frameCount; ++i) { drflac_uint32 side = (drflac_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); drflac_uint32 right = (drflac_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); drflac_uint32 left = right + side; pOutputSamples[i*2+0] = (float)((drflac_int32)left / 2147483648.0); pOutputSamples[i*2+1] = (float)((drflac_int32)right / 2147483648.0); } } #endif static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_right_side__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) { drflac_uint64 i; drflac_uint64 frameCount4 = frameCount >> 2; const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; float factor = 1 / 2147483648.0; for (i = 0; i < frameCount4; ++i) { drflac_uint32 side0 = pInputSamples0U32[i*4+0] << shift0; drflac_uint32 side1 = pInputSamples0U32[i*4+1] << shift0; drflac_uint32 side2 = pInputSamples0U32[i*4+2] << shift0; drflac_uint32 side3 = pInputSamples0U32[i*4+3] << shift0; drflac_uint32 right0 = pInputSamples1U32[i*4+0] << shift1; drflac_uint32 right1 = pInputSamples1U32[i*4+1] << shift1; drflac_uint32 right2 = pInputSamples1U32[i*4+2] << shift1; drflac_uint32 right3 = pInputSamples1U32[i*4+3] << shift1; drflac_uint32 left0 = right0 + side0; drflac_uint32 left1 = right1 + side1; drflac_uint32 left2 = right2 + side2; drflac_uint32 left3 = right3 + side3; pOutputSamples[i*8+0] = (drflac_int32)left0 * factor; pOutputSamples[i*8+1] = (drflac_int32)right0 * factor; pOutputSamples[i*8+2] = (drflac_int32)left1 * factor; pOutputSamples[i*8+3] = (drflac_int32)right1 * factor; pOutputSamples[i*8+4] = (drflac_int32)left2 * factor; pOutputSamples[i*8+5] = (drflac_int32)right2 * factor; pOutputSamples[i*8+6] = (drflac_int32)left3 * factor; pOutputSamples[i*8+7] = (drflac_int32)right3 * factor; } for (i = (frameCount4 << 2); i < frameCount; ++i) { drflac_uint32 side = pInputSamples0U32[i] << shift0; drflac_uint32 right = pInputSamples1U32[i] << shift1; drflac_uint32 left = right + side; pOutputSamples[i*2+0] = (drflac_int32)left * factor; pOutputSamples[i*2+1] = (drflac_int32)right * factor; } } #if defined(DRFLAC_SUPPORT_SSE2) static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_right_side__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) { drflac_uint64 i; drflac_uint64 frameCount4 = frameCount >> 2; const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; drflac_uint32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8; drflac_uint32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8; __m128 factor; DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); factor = _mm_set1_ps(1.0f / 8388608.0f); for (i = 0; i < frameCount4; ++i) { __m128i side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0); __m128i right = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1); __m128i left = _mm_add_epi32(right, side); __m128 leftf = _mm_mul_ps(_mm_cvtepi32_ps(left), factor); __m128 rightf = _mm_mul_ps(_mm_cvtepi32_ps(right), factor); _mm_storeu_ps(pOutputSamples + i*8 + 0, _mm_unpacklo_ps(leftf, rightf)); _mm_storeu_ps(pOutputSamples + i*8 + 4, _mm_unpackhi_ps(leftf, rightf)); } for (i = (frameCount4 << 2); i < frameCount; ++i) { drflac_uint32 side = pInputSamples0U32[i] << shift0; drflac_uint32 right = pInputSamples1U32[i] << shift1; drflac_uint32 left = right + side; pOutputSamples[i*2+0] = (drflac_int32)left / 8388608.0f; pOutputSamples[i*2+1] = (drflac_int32)right / 8388608.0f; } } #endif #if defined(DRFLAC_SUPPORT_NEON) static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_right_side__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) { drflac_uint64 i; drflac_uint64 frameCount4 = frameCount >> 2; const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; drflac_uint32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8; drflac_uint32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8; float32x4_t factor4; int32x4_t shift0_4; int32x4_t shift1_4; DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); factor4 = vdupq_n_f32(1.0f / 8388608.0f); shift0_4 = vdupq_n_s32(shift0); shift1_4 = vdupq_n_s32(shift1); for (i = 0; i < frameCount4; ++i) { uint32x4_t side; uint32x4_t right; uint32x4_t left; float32x4_t leftf; float32x4_t rightf; side = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4); right = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4); left = vaddq_u32(right, side); leftf = vmulq_f32(vcvtq_f32_s32(vreinterpretq_s32_u32(left)), factor4); rightf = vmulq_f32(vcvtq_f32_s32(vreinterpretq_s32_u32(right)), factor4); drflac__vst2q_f32(pOutputSamples + i*8, vzipq_f32(leftf, rightf)); } for (i = (frameCount4 << 2); i < frameCount; ++i) { drflac_uint32 side = pInputSamples0U32[i] << shift0; drflac_uint32 right = pInputSamples1U32[i] << shift1; drflac_uint32 left = right + side; pOutputSamples[i*2+0] = (drflac_int32)left / 8388608.0f; pOutputSamples[i*2+1] = (drflac_int32)right / 8388608.0f; } } #endif static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_right_side(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) { #if defined(DRFLAC_SUPPORT_SSE2) if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { drflac_read_pcm_frames_f32__decode_right_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); } else #elif defined(DRFLAC_SUPPORT_NEON) if (drflac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { drflac_read_pcm_frames_f32__decode_right_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); } else #endif { #if 0 drflac_read_pcm_frames_f32__decode_right_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); #else drflac_read_pcm_frames_f32__decode_right_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); #endif } } #if 0 static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_mid_side__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) { for (drflac_uint64 i = 0; i < frameCount; ++i) { drflac_uint32 mid = (drflac_uint32)pInputSamples0[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; drflac_uint32 side = (drflac_uint32)pInputSamples1[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; mid = (mid << 1) | (side & 0x01); pOutputSamples[i*2+0] = (float)((((drflac_int32)(mid + side) >> 1) << (unusedBitsPerSample)) / 2147483648.0); pOutputSamples[i*2+1] = (float)((((drflac_int32)(mid - side) >> 1) << (unusedBitsPerSample)) / 2147483648.0); } } #endif static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_mid_side__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) { drflac_uint64 i; drflac_uint64 frameCount4 = frameCount >> 2; const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; drflac_uint32 shift = unusedBitsPerSample; float factor = 1 / 2147483648.0; if (shift > 0) { shift -= 1; for (i = 0; i < frameCount4; ++i) { drflac_uint32 temp0L; drflac_uint32 temp1L; drflac_uint32 temp2L; drflac_uint32 temp3L; drflac_uint32 temp0R; drflac_uint32 temp1R; drflac_uint32 temp2R; drflac_uint32 temp3R; drflac_uint32 mid0 = pInputSamples0U32[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; drflac_uint32 mid1 = pInputSamples0U32[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; drflac_uint32 mid2 = pInputSamples0U32[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; drflac_uint32 mid3 = pInputSamples0U32[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; drflac_uint32 side0 = pInputSamples1U32[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; drflac_uint32 side1 = pInputSamples1U32[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; drflac_uint32 side2 = pInputSamples1U32[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; drflac_uint32 side3 = pInputSamples1U32[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; mid0 = (mid0 << 1) | (side0 & 0x01); mid1 = (mid1 << 1) | (side1 & 0x01); mid2 = (mid2 << 1) | (side2 & 0x01); mid3 = (mid3 << 1) | (side3 & 0x01); temp0L = (mid0 + side0) << shift; temp1L = (mid1 + side1) << shift; temp2L = (mid2 + side2) << shift; temp3L = (mid3 + side3) << shift; temp0R = (mid0 - side0) << shift; temp1R = (mid1 - side1) << shift; temp2R = (mid2 - side2) << shift; temp3R = (mid3 - side3) << shift; pOutputSamples[i*8+0] = (drflac_int32)temp0L * factor; pOutputSamples[i*8+1] = (drflac_int32)temp0R * factor; pOutputSamples[i*8+2] = (drflac_int32)temp1L * factor; pOutputSamples[i*8+3] = (drflac_int32)temp1R * factor; pOutputSamples[i*8+4] = (drflac_int32)temp2L * factor; pOutputSamples[i*8+5] = (drflac_int32)temp2R * factor; pOutputSamples[i*8+6] = (drflac_int32)temp3L * factor; pOutputSamples[i*8+7] = (drflac_int32)temp3R * factor; } } else { for (i = 0; i < frameCount4; ++i) { drflac_uint32 temp0L; drflac_uint32 temp1L; drflac_uint32 temp2L; drflac_uint32 temp3L; drflac_uint32 temp0R; drflac_uint32 temp1R; drflac_uint32 temp2R; drflac_uint32 temp3R; drflac_uint32 mid0 = pInputSamples0U32[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; drflac_uint32 mid1 = pInputSamples0U32[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; drflac_uint32 mid2 = pInputSamples0U32[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; drflac_uint32 mid3 = pInputSamples0U32[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; drflac_uint32 side0 = pInputSamples1U32[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; drflac_uint32 side1 = pInputSamples1U32[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; drflac_uint32 side2 = pInputSamples1U32[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; drflac_uint32 side3 = pInputSamples1U32[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; mid0 = (mid0 << 1) | (side0 & 0x01); mid1 = (mid1 << 1) | (side1 & 0x01); mid2 = (mid2 << 1) | (side2 & 0x01); mid3 = (mid3 << 1) | (side3 & 0x01); temp0L = (drflac_uint32)((drflac_int32)(mid0 + side0) >> 1); temp1L = (drflac_uint32)((drflac_int32)(mid1 + side1) >> 1); temp2L = (drflac_uint32)((drflac_int32)(mid2 + side2) >> 1); temp3L = (drflac_uint32)((drflac_int32)(mid3 + side3) >> 1); temp0R = (drflac_uint32)((drflac_int32)(mid0 - side0) >> 1); temp1R = (drflac_uint32)((drflac_int32)(mid1 - side1) >> 1); temp2R = (drflac_uint32)((drflac_int32)(mid2 - side2) >> 1); temp3R = (drflac_uint32)((drflac_int32)(mid3 - side3) >> 1); pOutputSamples[i*8+0] = (drflac_int32)temp0L * factor; pOutputSamples[i*8+1] = (drflac_int32)temp0R * factor; pOutputSamples[i*8+2] = (drflac_int32)temp1L * factor; pOutputSamples[i*8+3] = (drflac_int32)temp1R * factor; pOutputSamples[i*8+4] = (drflac_int32)temp2L * factor; pOutputSamples[i*8+5] = (drflac_int32)temp2R * factor; pOutputSamples[i*8+6] = (drflac_int32)temp3L * factor; pOutputSamples[i*8+7] = (drflac_int32)temp3R * factor; } } for (i = (frameCount4 << 2); i < frameCount; ++i) { drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; mid = (mid << 1) | (side & 0x01); pOutputSamples[i*2+0] = (drflac_int32)((drflac_uint32)((drflac_int32)(mid + side) >> 1) << unusedBitsPerSample) * factor; pOutputSamples[i*2+1] = (drflac_int32)((drflac_uint32)((drflac_int32)(mid - side) >> 1) << unusedBitsPerSample) * factor; } } #if defined(DRFLAC_SUPPORT_SSE2) static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_mid_side__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) { drflac_uint64 i; drflac_uint64 frameCount4 = frameCount >> 2; const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; drflac_uint32 shift = unusedBitsPerSample - 8; float factor; __m128 factor128; DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); factor = 1.0f / 8388608.0f; factor128 = _mm_set1_ps(factor); if (shift == 0) { for (i = 0; i < frameCount4; ++i) { __m128i mid; __m128i side; __m128i tempL; __m128i tempR; __m128 leftf; __m128 rightf; mid = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); mid = _mm_or_si128(_mm_slli_epi32(mid, 1), _mm_and_si128(side, _mm_set1_epi32(0x01))); tempL = _mm_srai_epi32(_mm_add_epi32(mid, side), 1); tempR = _mm_srai_epi32(_mm_sub_epi32(mid, side), 1); leftf = _mm_mul_ps(_mm_cvtepi32_ps(tempL), factor128); rightf = _mm_mul_ps(_mm_cvtepi32_ps(tempR), factor128); _mm_storeu_ps(pOutputSamples + i*8 + 0, _mm_unpacklo_ps(leftf, rightf)); _mm_storeu_ps(pOutputSamples + i*8 + 4, _mm_unpackhi_ps(leftf, rightf)); } for (i = (frameCount4 << 2); i < frameCount; ++i) { drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; mid = (mid << 1) | (side & 0x01); pOutputSamples[i*2+0] = ((drflac_int32)(mid + side) >> 1) * factor; pOutputSamples[i*2+1] = ((drflac_int32)(mid - side) >> 1) * factor; } } else { shift -= 1; for (i = 0; i < frameCount4; ++i) { __m128i mid; __m128i side; __m128i tempL; __m128i tempR; __m128 leftf; __m128 rightf; mid = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); mid = _mm_or_si128(_mm_slli_epi32(mid, 1), _mm_and_si128(side, _mm_set1_epi32(0x01))); tempL = _mm_slli_epi32(_mm_add_epi32(mid, side), shift); tempR = _mm_slli_epi32(_mm_sub_epi32(mid, side), shift); leftf = _mm_mul_ps(_mm_cvtepi32_ps(tempL), factor128); rightf = _mm_mul_ps(_mm_cvtepi32_ps(tempR), factor128); _mm_storeu_ps(pOutputSamples + i*8 + 0, _mm_unpacklo_ps(leftf, rightf)); _mm_storeu_ps(pOutputSamples + i*8 + 4, _mm_unpackhi_ps(leftf, rightf)); } for (i = (frameCount4 << 2); i < frameCount; ++i) { drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; mid = (mid << 1) | (side & 0x01); pOutputSamples[i*2+0] = (drflac_int32)((mid + side) << shift) * factor; pOutputSamples[i*2+1] = (drflac_int32)((mid - side) << shift) * factor; } } } #endif #if defined(DRFLAC_SUPPORT_NEON) static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_mid_side__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) { drflac_uint64 i; drflac_uint64 frameCount4 = frameCount >> 2; const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; drflac_uint32 shift = unusedBitsPerSample - 8; float factor; float32x4_t factor4; int32x4_t shift4; int32x4_t wbps0_4; int32x4_t wbps1_4; DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); factor = 1.0f / 8388608.0f; factor4 = vdupq_n_f32(factor); wbps0_4 = vdupq_n_s32(pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); wbps1_4 = vdupq_n_s32(pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); if (shift == 0) { for (i = 0; i < frameCount4; ++i) { int32x4_t lefti; int32x4_t righti; float32x4_t leftf; float32x4_t rightf; uint32x4_t mid = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), wbps0_4); uint32x4_t side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), wbps1_4); mid = vorrq_u32(vshlq_n_u32(mid, 1), vandq_u32(side, vdupq_n_u32(1))); lefti = vshrq_n_s32(vreinterpretq_s32_u32(vaddq_u32(mid, side)), 1); righti = vshrq_n_s32(vreinterpretq_s32_u32(vsubq_u32(mid, side)), 1); leftf = vmulq_f32(vcvtq_f32_s32(lefti), factor4); rightf = vmulq_f32(vcvtq_f32_s32(righti), factor4); drflac__vst2q_f32(pOutputSamples + i*8, vzipq_f32(leftf, rightf)); } for (i = (frameCount4 << 2); i < frameCount; ++i) { drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; mid = (mid << 1) | (side & 0x01); pOutputSamples[i*2+0] = ((drflac_int32)(mid + side) >> 1) * factor; pOutputSamples[i*2+1] = ((drflac_int32)(mid - side) >> 1) * factor; } } else { shift -= 1; shift4 = vdupq_n_s32(shift); for (i = 0; i < frameCount4; ++i) { uint32x4_t mid; uint32x4_t side; int32x4_t lefti; int32x4_t righti; float32x4_t leftf; float32x4_t rightf; mid = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), wbps0_4); side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), wbps1_4); mid = vorrq_u32(vshlq_n_u32(mid, 1), vandq_u32(side, vdupq_n_u32(1))); lefti = vreinterpretq_s32_u32(vshlq_u32(vaddq_u32(mid, side), shift4)); righti = vreinterpretq_s32_u32(vshlq_u32(vsubq_u32(mid, side), shift4)); leftf = vmulq_f32(vcvtq_f32_s32(lefti), factor4); rightf = vmulq_f32(vcvtq_f32_s32(righti), factor4); drflac__vst2q_f32(pOutputSamples + i*8, vzipq_f32(leftf, rightf)); } for (i = (frameCount4 << 2); i < frameCount; ++i) { drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; mid = (mid << 1) | (side & 0x01); pOutputSamples[i*2+0] = (drflac_int32)((mid + side) << shift) * factor; pOutputSamples[i*2+1] = (drflac_int32)((mid - side) << shift) * factor; } } } #endif static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_mid_side(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) { #if defined(DRFLAC_SUPPORT_SSE2) if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { drflac_read_pcm_frames_f32__decode_mid_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); } else #elif defined(DRFLAC_SUPPORT_NEON) if (drflac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { drflac_read_pcm_frames_f32__decode_mid_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); } else #endif { #if 0 drflac_read_pcm_frames_f32__decode_mid_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); #else drflac_read_pcm_frames_f32__decode_mid_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); #endif } } #if 0 static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_independent_stereo__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) { for (drflac_uint64 i = 0; i < frameCount; ++i) { pOutputSamples[i*2+0] = (float)((drflac_int32)((drflac_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample)) / 2147483648.0); pOutputSamples[i*2+1] = (float)((drflac_int32)((drflac_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample)) / 2147483648.0); } } #endif static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_independent_stereo__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) { drflac_uint64 i; drflac_uint64 frameCount4 = frameCount >> 2; const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; float factor = 1 / 2147483648.0; for (i = 0; i < frameCount4; ++i) { drflac_uint32 tempL0 = pInputSamples0U32[i*4+0] << shift0; drflac_uint32 tempL1 = pInputSamples0U32[i*4+1] << shift0; drflac_uint32 tempL2 = pInputSamples0U32[i*4+2] << shift0; drflac_uint32 tempL3 = pInputSamples0U32[i*4+3] << shift0; drflac_uint32 tempR0 = pInputSamples1U32[i*4+0] << shift1; drflac_uint32 tempR1 = pInputSamples1U32[i*4+1] << shift1; drflac_uint32 tempR2 = pInputSamples1U32[i*4+2] << shift1; drflac_uint32 tempR3 = pInputSamples1U32[i*4+3] << shift1; pOutputSamples[i*8+0] = (drflac_int32)tempL0 * factor; pOutputSamples[i*8+1] = (drflac_int32)tempR0 * factor; pOutputSamples[i*8+2] = (drflac_int32)tempL1 * factor; pOutputSamples[i*8+3] = (drflac_int32)tempR1 * factor; pOutputSamples[i*8+4] = (drflac_int32)tempL2 * factor; pOutputSamples[i*8+5] = (drflac_int32)tempR2 * factor; pOutputSamples[i*8+6] = (drflac_int32)tempL3 * factor; pOutputSamples[i*8+7] = (drflac_int32)tempR3 * factor; } for (i = (frameCount4 << 2); i < frameCount; ++i) { pOutputSamples[i*2+0] = (drflac_int32)(pInputSamples0U32[i] << shift0) * factor; pOutputSamples[i*2+1] = (drflac_int32)(pInputSamples1U32[i] << shift1) * factor; } } #if defined(DRFLAC_SUPPORT_SSE2) static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_independent_stereo__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) { drflac_uint64 i; drflac_uint64 frameCount4 = frameCount >> 2; const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; drflac_uint32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8; drflac_uint32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8; float factor = 1.0f / 8388608.0f; __m128 factor128 = _mm_set1_ps(factor); for (i = 0; i < frameCount4; ++i) { __m128i lefti; __m128i righti; __m128 leftf; __m128 rightf; lefti = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0); righti = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1); leftf = _mm_mul_ps(_mm_cvtepi32_ps(lefti), factor128); rightf = _mm_mul_ps(_mm_cvtepi32_ps(righti), factor128); _mm_storeu_ps(pOutputSamples + i*8 + 0, _mm_unpacklo_ps(leftf, rightf)); _mm_storeu_ps(pOutputSamples + i*8 + 4, _mm_unpackhi_ps(leftf, rightf)); } for (i = (frameCount4 << 2); i < frameCount; ++i) { pOutputSamples[i*2+0] = (drflac_int32)(pInputSamples0U32[i] << shift0) * factor; pOutputSamples[i*2+1] = (drflac_int32)(pInputSamples1U32[i] << shift1) * factor; } } #endif #if defined(DRFLAC_SUPPORT_NEON) static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_independent_stereo__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) { drflac_uint64 i; drflac_uint64 frameCount4 = frameCount >> 2; const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; drflac_uint32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8; drflac_uint32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8; float factor = 1.0f / 8388608.0f; float32x4_t factor4 = vdupq_n_f32(factor); int32x4_t shift0_4 = vdupq_n_s32(shift0); int32x4_t shift1_4 = vdupq_n_s32(shift1); for (i = 0; i < frameCount4; ++i) { int32x4_t lefti; int32x4_t righti; float32x4_t leftf; float32x4_t rightf; lefti = vreinterpretq_s32_u32(vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4)); righti = vreinterpretq_s32_u32(vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4)); leftf = vmulq_f32(vcvtq_f32_s32(lefti), factor4); rightf = vmulq_f32(vcvtq_f32_s32(righti), factor4); drflac__vst2q_f32(pOutputSamples + i*8, vzipq_f32(leftf, rightf)); } for (i = (frameCount4 << 2); i < frameCount; ++i) { pOutputSamples[i*2+0] = (drflac_int32)(pInputSamples0U32[i] << shift0) * factor; pOutputSamples[i*2+1] = (drflac_int32)(pInputSamples1U32[i] << shift1) * factor; } } #endif static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_independent_stereo(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) { #if defined(DRFLAC_SUPPORT_SSE2) if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { drflac_read_pcm_frames_f32__decode_independent_stereo__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); } else #elif defined(DRFLAC_SUPPORT_NEON) if (drflac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { drflac_read_pcm_frames_f32__decode_independent_stereo__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); } else #endif { #if 0 drflac_read_pcm_frames_f32__decode_independent_stereo__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); #else drflac_read_pcm_frames_f32__decode_independent_stereo__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); #endif } } DRFLAC_API drflac_uint64 drflac_read_pcm_frames_f32(drflac* pFlac, drflac_uint64 framesToRead, float* pBufferOut) { drflac_uint64 framesRead; drflac_uint32 unusedBitsPerSample; if (pFlac == NULL || framesToRead == 0) { return 0; } if (pBufferOut == NULL) { return drflac__seek_forward_by_pcm_frames(pFlac, framesToRead); } DRFLAC_ASSERT(pFlac->bitsPerSample <= 32); unusedBitsPerSample = 32 - pFlac->bitsPerSample; framesRead = 0; while (framesToRead > 0) { if (pFlac->currentFLACFrame.pcmFramesRemaining == 0) { if (!drflac__read_and_decode_next_flac_frame(pFlac)) { break; } } else { unsigned int channelCount = drflac__get_channel_count_from_channel_assignment(pFlac->currentFLACFrame.header.channelAssignment); drflac_uint64 iFirstPCMFrame = pFlac->currentFLACFrame.header.blockSizeInPCMFrames - pFlac->currentFLACFrame.pcmFramesRemaining; drflac_uint64 frameCountThisIteration = framesToRead; if (frameCountThisIteration > pFlac->currentFLACFrame.pcmFramesRemaining) { frameCountThisIteration = pFlac->currentFLACFrame.pcmFramesRemaining; } if (channelCount == 2) { const drflac_int32* pDecodedSamples0 = pFlac->currentFLACFrame.subframes[0].pSamplesS32 + iFirstPCMFrame; const drflac_int32* pDecodedSamples1 = pFlac->currentFLACFrame.subframes[1].pSamplesS32 + iFirstPCMFrame; switch (pFlac->currentFLACFrame.header.channelAssignment) { case DRFLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE: { drflac_read_pcm_frames_f32__decode_left_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); } break; case DRFLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE: { drflac_read_pcm_frames_f32__decode_right_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); } break; case DRFLAC_CHANNEL_ASSIGNMENT_MID_SIDE: { drflac_read_pcm_frames_f32__decode_mid_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); } break; case DRFLAC_CHANNEL_ASSIGNMENT_INDEPENDENT: default: { drflac_read_pcm_frames_f32__decode_independent_stereo(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); } break; } } else { drflac_uint64 i; for (i = 0; i < frameCountThisIteration; ++i) { unsigned int j; for (j = 0; j < channelCount; ++j) { drflac_int32 sampleS32 = (drflac_int32)((drflac_uint32)(pFlac->currentFLACFrame.subframes[j].pSamplesS32[iFirstPCMFrame + i]) << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[j].wastedBitsPerSample)); pBufferOut[(i*channelCount)+j] = (float)(sampleS32 / 2147483648.0); } } } framesRead += frameCountThisIteration; pBufferOut += frameCountThisIteration * channelCount; framesToRead -= frameCountThisIteration; pFlac->currentPCMFrame += frameCountThisIteration; pFlac->currentFLACFrame.pcmFramesRemaining -= (unsigned int)frameCountThisIteration; } } return framesRead; } DRFLAC_API drflac_bool32 drflac_seek_to_pcm_frame(drflac* pFlac, drflac_uint64 pcmFrameIndex) { if (pFlac == NULL) { return DRFLAC_FALSE; } if (pFlac->currentPCMFrame == pcmFrameIndex) { return DRFLAC_TRUE; } if (pFlac->firstFLACFramePosInBytes == 0) { return DRFLAC_FALSE; } if (pcmFrameIndex == 0) { pFlac->currentPCMFrame = 0; return drflac__seek_to_first_frame(pFlac); } else { drflac_bool32 wasSuccessful = DRFLAC_FALSE; drflac_uint64 originalPCMFrame = pFlac->currentPCMFrame; if (pcmFrameIndex > pFlac->totalPCMFrameCount) { pcmFrameIndex = pFlac->totalPCMFrameCount; } if (pcmFrameIndex > pFlac->currentPCMFrame) { drflac_uint32 offset = (drflac_uint32)(pcmFrameIndex - pFlac->currentPCMFrame); if (pFlac->currentFLACFrame.pcmFramesRemaining > offset) { pFlac->currentFLACFrame.pcmFramesRemaining -= offset; pFlac->currentPCMFrame = pcmFrameIndex; return DRFLAC_TRUE; } } else { drflac_uint32 offsetAbs = (drflac_uint32)(pFlac->currentPCMFrame - pcmFrameIndex); drflac_uint32 currentFLACFramePCMFrameCount = pFlac->currentFLACFrame.header.blockSizeInPCMFrames; drflac_uint32 currentFLACFramePCMFramesConsumed = currentFLACFramePCMFrameCount - pFlac->currentFLACFrame.pcmFramesRemaining; if (currentFLACFramePCMFramesConsumed > offsetAbs) { pFlac->currentFLACFrame.pcmFramesRemaining += offsetAbs; pFlac->currentPCMFrame = pcmFrameIndex; return DRFLAC_TRUE; } } #ifndef DR_FLAC_NO_OGG if (pFlac->container == drflac_container_ogg) { wasSuccessful = drflac_ogg__seek_to_pcm_frame(pFlac, pcmFrameIndex); } else #endif { if (!pFlac->_noSeekTableSeek) { wasSuccessful = drflac__seek_to_pcm_frame__seek_table(pFlac, pcmFrameIndex); } #if !defined(DR_FLAC_NO_CRC) if (!wasSuccessful && !pFlac->_noBinarySearchSeek && pFlac->totalPCMFrameCount > 0) { wasSuccessful = drflac__seek_to_pcm_frame__binary_search(pFlac, pcmFrameIndex); } #endif if (!wasSuccessful && !pFlac->_noBruteForceSeek) { wasSuccessful = drflac__seek_to_pcm_frame__brute_force(pFlac, pcmFrameIndex); } } if (wasSuccessful) { pFlac->currentPCMFrame = pcmFrameIndex; } else { if (drflac_seek_to_pcm_frame(pFlac, originalPCMFrame) == DRFLAC_FALSE) { drflac_seek_to_pcm_frame(pFlac, 0); } } return wasSuccessful; } } #if defined(SIZE_MAX) #define DRFLAC_SIZE_MAX SIZE_MAX #else #if defined(DRFLAC_64BIT) #define DRFLAC_SIZE_MAX ((drflac_uint64)0xFFFFFFFFFFFFFFFF) #else #define DRFLAC_SIZE_MAX 0xFFFFFFFF #endif #endif #define DRFLAC_DEFINE_FULL_READ_AND_CLOSE(extension, type) \ static type* drflac__full_read_and_close_ ## extension (drflac* pFlac, unsigned int* channelsOut, unsigned int* sampleRateOut, drflac_uint64* totalPCMFrameCountOut)\ { \ type* pSampleData = NULL; \ drflac_uint64 totalPCMFrameCount; \ \ DRFLAC_ASSERT(pFlac != NULL); \ \ totalPCMFrameCount = pFlac->totalPCMFrameCount; \ \ 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; \ } \ \ 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; \ } \ \ DRFLAC_COPY_MEMORY(pSampleData + (totalPCMFrameCount*pFlac->channels), buffer, (size_t)(pcmFramesRead*pFlac->channels*sizeof(type))); \ totalPCMFrameCount += pcmFramesRead; \ } \ \ \ 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; \ } \ \ pSampleData = (type*)drflac__malloc_from_callbacks((size_t)dataSize, &pFlac->allocationCallbacks); \ if (pSampleData == NULL) { \ goto on_error; \ } \ \ totalPCMFrameCount = drflac_read_pcm_frames_##extension(pFlac, pFlac->totalPCMFrameCount, pSampleData); \ } \ \ if (sampleRateOut) *sampleRateOut = pFlac->sampleRate; \ if (channelsOut) *channelsOut = pFlac->channels; \ if (totalPCMFrameCountOut) *totalPCMFrameCountOut = totalPCMFrameCount; \ \ drflac_close(pFlac); \ return pSampleData; \ \ on_error: \ drflac_close(pFlac); \ return NULL; \ } DRFLAC_DEFINE_FULL_READ_AND_CLOSE(s32, drflac_int32) DRFLAC_DEFINE_FULL_READ_AND_CLOSE(s16, drflac_int16) DRFLAC_DEFINE_FULL_READ_AND_CLOSE(f32, float) DRFLAC_API drflac_int32* drflac_open_and_read_pcm_frames_s32(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drflac_uint64* totalPCMFrameCountOut, const drflac_allocation_callbacks* pAllocationCallbacks) { drflac* pFlac; if (channelsOut) { *channelsOut = 0; } if (sampleRateOut) { *sampleRateOut = 0; } if (totalPCMFrameCountOut) { *totalPCMFrameCountOut = 0; } pFlac = drflac_open(onRead, onSeek, pUserData, pAllocationCallbacks); if (pFlac == NULL) { return NULL; } return drflac__full_read_and_close_s32(pFlac, channelsOut, sampleRateOut, totalPCMFrameCountOut); } DRFLAC_API drflac_int16* drflac_open_and_read_pcm_frames_s16(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drflac_uint64* totalPCMFrameCountOut, const drflac_allocation_callbacks* pAllocationCallbacks) { drflac* pFlac; if (channelsOut) { *channelsOut = 0; } if (sampleRateOut) { *sampleRateOut = 0; } if (totalPCMFrameCountOut) { *totalPCMFrameCountOut = 0; } pFlac = drflac_open(onRead, onSeek, pUserData, pAllocationCallbacks); if (pFlac == NULL) { return NULL; } return drflac__full_read_and_close_s16(pFlac, channelsOut, sampleRateOut, totalPCMFrameCountOut); } DRFLAC_API float* drflac_open_and_read_pcm_frames_f32(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drflac_uint64* totalPCMFrameCountOut, const drflac_allocation_callbacks* pAllocationCallbacks) { drflac* pFlac; if (channelsOut) { *channelsOut = 0; } if (sampleRateOut) { *sampleRateOut = 0; } if (totalPCMFrameCountOut) { *totalPCMFrameCountOut = 0; } pFlac = drflac_open(onRead, onSeek, pUserData, pAllocationCallbacks); if (pFlac == NULL) { return NULL; } return drflac__full_read_and_close_f32(pFlac, channelsOut, sampleRateOut, totalPCMFrameCountOut); } #ifndef DR_FLAC_NO_STDIO DRFLAC_API drflac_int32* drflac_open_file_and_read_pcm_frames_s32(const char* filename, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks) { drflac* pFlac; if (sampleRate) { *sampleRate = 0; } if (channels) { *channels = 0; } if (totalPCMFrameCount) { *totalPCMFrameCount = 0; } pFlac = drflac_open_file(filename, pAllocationCallbacks); if (pFlac == NULL) { return NULL; } return drflac__full_read_and_close_s32(pFlac, channels, sampleRate, totalPCMFrameCount); } DRFLAC_API drflac_int16* drflac_open_file_and_read_pcm_frames_s16(const char* filename, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks) { drflac* pFlac; if (sampleRate) { *sampleRate = 0; } if (channels) { *channels = 0; } if (totalPCMFrameCount) { *totalPCMFrameCount = 0; } pFlac = drflac_open_file(filename, pAllocationCallbacks); if (pFlac == NULL) { return NULL; } return drflac__full_read_and_close_s16(pFlac, channels, sampleRate, totalPCMFrameCount); } DRFLAC_API float* drflac_open_file_and_read_pcm_frames_f32(const char* filename, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks) { drflac* pFlac; if (sampleRate) { *sampleRate = 0; } if (channels) { *channels = 0; } if (totalPCMFrameCount) { *totalPCMFrameCount = 0; } pFlac = drflac_open_file(filename, pAllocationCallbacks); if (pFlac == NULL) { return NULL; } return drflac__full_read_and_close_f32(pFlac, channels, sampleRate, totalPCMFrameCount); } #endif DRFLAC_API drflac_int32* drflac_open_memory_and_read_pcm_frames_s32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks) { drflac* pFlac; if (sampleRate) { *sampleRate = 0; } if (channels) { *channels = 0; } if (totalPCMFrameCount) { *totalPCMFrameCount = 0; } pFlac = drflac_open_memory(data, dataSize, pAllocationCallbacks); if (pFlac == NULL) { return NULL; } return drflac__full_read_and_close_s32(pFlac, channels, sampleRate, totalPCMFrameCount); } DRFLAC_API drflac_int16* drflac_open_memory_and_read_pcm_frames_s16(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks) { drflac* pFlac; if (sampleRate) { *sampleRate = 0; } if (channels) { *channels = 0; } if (totalPCMFrameCount) { *totalPCMFrameCount = 0; } pFlac = drflac_open_memory(data, dataSize, pAllocationCallbacks); if (pFlac == NULL) { return NULL; } return drflac__full_read_and_close_s16(pFlac, channels, sampleRate, totalPCMFrameCount); } DRFLAC_API float* drflac_open_memory_and_read_pcm_frames_f32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks) { drflac* pFlac; if (sampleRate) { *sampleRate = 0; } if (channels) { *channels = 0; } if (totalPCMFrameCount) { *totalPCMFrameCount = 0; } pFlac = drflac_open_memory(data, dataSize, pAllocationCallbacks); if (pFlac == NULL) { return NULL; } return drflac__full_read_and_close_f32(pFlac, channels, sampleRate, totalPCMFrameCount); } DRFLAC_API void drflac_free(void* p, const drflac_allocation_callbacks* pAllocationCallbacks) { if (pAllocationCallbacks != NULL) { drflac__free_from_callbacks(p, pAllocationCallbacks); } else { drflac__free_default(p, NULL); } } DRFLAC_API void drflac_init_vorbis_comment_iterator(drflac_vorbis_comment_iterator* pIter, drflac_uint32 commentCount, const void* pComments) { if (pIter == NULL) { return; } pIter->countRemaining = commentCount; pIter->pRunningData = (const char*)pComments; } DRFLAC_API const char* drflac_next_vorbis_comment(drflac_vorbis_comment_iterator* pIter, drflac_uint32* pCommentLengthOut) { drflac_int32 length; const char* pComment; if (pCommentLengthOut) { *pCommentLengthOut = 0; } if (pIter == NULL || pIter->countRemaining == 0 || pIter->pRunningData == NULL) { return NULL; } length = drflac__le2host_32_ptr_unaligned(pIter->pRunningData); pIter->pRunningData += 4; pComment = pIter->pRunningData; pIter->pRunningData += length; pIter->countRemaining -= 1; if (pCommentLengthOut) { *pCommentLengthOut = length; } return pComment; } DRFLAC_API void drflac_init_cuesheet_track_iterator(drflac_cuesheet_track_iterator* pIter, drflac_uint32 trackCount, const void* pTrackData) { if (pIter == NULL) { return; } pIter->countRemaining = trackCount; pIter->pRunningData = (const char*)pTrackData; } DRFLAC_API drflac_bool32 drflac_next_cuesheet_track(drflac_cuesheet_track_iterator* pIter, drflac_cuesheet_track* pCuesheetTrack) { drflac_cuesheet_track cuesheetTrack; const char* pRunningData; drflac_uint64 offsetHi; drflac_uint64 offsetLo; if (pIter == NULL || pIter->countRemaining == 0 || pIter->pRunningData == NULL) { return DRFLAC_FALSE; } pRunningData = pIter->pRunningData; offsetHi = drflac__be2host_32(*(const drflac_uint32*)pRunningData); pRunningData += 4; offsetLo = drflac__be2host_32(*(const drflac_uint32*)pRunningData); pRunningData += 4; cuesheetTrack.offset = offsetLo | (offsetHi << 32); cuesheetTrack.trackNumber = pRunningData[0]; pRunningData += 1; DRFLAC_COPY_MEMORY(cuesheetTrack.ISRC, pRunningData, sizeof(cuesheetTrack.ISRC)); pRunningData += 12; cuesheetTrack.isAudio = (pRunningData[0] & 0x80) != 0; cuesheetTrack.preEmphasis = (pRunningData[0] & 0x40) != 0; pRunningData += 14; cuesheetTrack.indexCount = pRunningData[0]; pRunningData += 1; cuesheetTrack.pIndexPoints = (const drflac_cuesheet_track_index*)pRunningData; pRunningData += cuesheetTrack.indexCount * sizeof(drflac_cuesheet_track_index); pIter->pRunningData = pRunningData; pIter->countRemaining -= 1; if (pCuesheetTrack) { *pCuesheetTrack = cuesheetTrack; } return DRFLAC_TRUE; } #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) #pragma GCC diagnostic pop #endif #endif /* dr_flac_c end */ #endif /* DRFLAC_IMPLEMENTATION */ #endif /* MA_NO_FLAC */ #if !defined(MA_NO_MP3) && !defined(MA_NO_DECODING) #if !defined(DR_MP3_IMPLEMENTATION) && !defined(DRMP3_IMPLEMENTATION) /* For backwards compatibility. Will be removed in version 0.11 for cleanliness. */ /* dr_mp3_c begin */ #ifndef dr_mp3_c #define dr_mp3_c #include #include #include DRMP3_API void drmp3_version(drmp3_uint32* pMajor, drmp3_uint32* pMinor, drmp3_uint32* pRevision) { if (pMajor) { *pMajor = DRMP3_VERSION_MAJOR; } if (pMinor) { *pMinor = DRMP3_VERSION_MINOR; } if (pRevision) { *pRevision = DRMP3_VERSION_REVISION; } } DRMP3_API const char* drmp3_version_string(void) { return DRMP3_VERSION_STRING; } #if defined(__TINYC__) #define DR_MP3_NO_SIMD #endif #define DRMP3_OFFSET_PTR(p, offset) ((void*)((drmp3_uint8*)(p) + (offset))) #define DRMP3_MAX_FREE_FORMAT_FRAME_SIZE 2304 #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 #define DRMP3_MAX_BITRESERVOIR_BYTES 511 #define DRMP3_SHORT_BLOCK_TYPE 2 #define DRMP3_STOP_BLOCK_TYPE 3 #define DRMP3_MODE_MONO 3 #define DRMP3_MODE_JOINT_STEREO 1 #define DRMP3_HDR_SIZE 4 #define DRMP3_HDR_IS_MONO(h) (((h[3]) & 0xC0) == 0xC0) #define DRMP3_HDR_IS_MS_STEREO(h) (((h[3]) & 0xE0) == 0x60) #define DRMP3_HDR_IS_FREE_FORMAT(h) (((h[2]) & 0xF0) == 0) #define DRMP3_HDR_IS_CRC(h) (!((h[1]) & 1)) #define DRMP3_HDR_TEST_PADDING(h) ((h[2]) & 0x2) #define DRMP3_HDR_TEST_MPEG1(h) ((h[1]) & 0x8) #define DRMP3_HDR_TEST_NOT_MPEG25(h) ((h[1]) & 0x10) #define DRMP3_HDR_TEST_I_STEREO(h) ((h[3]) & 0x10) #define DRMP3_HDR_TEST_MS_STEREO(h) ((h[3]) & 0x20) #define DRMP3_HDR_GET_STEREO_MODE(h) (((h[3]) >> 6) & 3) #define DRMP3_HDR_GET_STEREO_MODE_EXT(h) (((h[3]) >> 4) & 3) #define DRMP3_HDR_GET_LAYER(h) (((h[1]) >> 1) & 3) #define DRMP3_HDR_GET_BITRATE(h) ((h[2]) >> 4) #define DRMP3_HDR_GET_SAMPLE_RATE(h) (((h[2]) >> 2) & 3) #define DRMP3_HDR_GET_MY_SAMPLE_RATE(h) (DRMP3_HDR_GET_SAMPLE_RATE(h) + (((h[1] >> 3) & 1) + ((h[1] >> 4) & 1))*3) #define DRMP3_HDR_IS_FRAME_576(h) ((h[1] & 14) == 2) #define DRMP3_HDR_IS_LAYER_1(h) ((h[1] & 6) == 6) #define DRMP3_BITS_DEQUANTIZER_OUT -1 #define DRMP3_MAX_SCF (255 + DRMP3_BITS_DEQUANTIZER_OUT*4 - 210) #define DRMP3_MAX_SCFI ((DRMP3_MAX_SCF + 3) & ~3) #define DRMP3_MIN(a, b) ((a) > (b) ? (b) : (a)) #define DRMP3_MAX(a, b) ((a) < (b) ? (b) : (a)) #if !defined(DR_MP3_NO_SIMD) #if !defined(DR_MP3_ONLY_SIMD) && (defined(_M_X64) || defined(__x86_64__) || defined(__aarch64__) || defined(_M_ARM64)) #define DR_MP3_ONLY_SIMD #endif #if ((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__))) #if defined(_MSC_VER) #include #endif #include #define DRMP3_HAVE_SSE 1 #define DRMP3_HAVE_SIMD 1 #define DRMP3_VSTORE _mm_storeu_ps #define DRMP3_VLD _mm_loadu_ps #define DRMP3_VSET _mm_set1_ps #define DRMP3_VADD _mm_add_ps #define DRMP3_VSUB _mm_sub_ps #define DRMP3_VMUL _mm_mul_ps #define DRMP3_VMAC(a, x, y) _mm_add_ps(a, _mm_mul_ps(x, y)) #define DRMP3_VMSB(a, x, y) _mm_sub_ps(a, _mm_mul_ps(x, y)) #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) #define drmp3_cpuid __cpuid #else static __inline__ __attribute__((always_inline)) void drmp3_cpuid(int CPUInfo[], const int InfoType) { #if defined(__PIC__) __asm__ __volatile__( #if defined(__x86_64__) "push %%rbx\n" "cpuid\n" "xchgl %%ebx, %1\n" "pop %%rbx\n" #else "xchgl %%ebx, %1\n" "cpuid\n" "xchgl %%ebx, %1\n" #endif : "=a" (CPUInfo[0]), "=r" (CPUInfo[1]), "=c" (CPUInfo[2]), "=d" (CPUInfo[3]) : "a" (InfoType)); #else __asm__ __volatile__( "cpuid" : "=a" (CPUInfo[0]), "=b" (CPUInfo[1]), "=c" (CPUInfo[2]), "=d" (CPUInfo[3]) : "a" (InfoType)); #endif } #endif static int drmp3_have_simd(void) { #ifdef DR_MP3_ONLY_SIMD return 1; #else static int g_have_simd; int CPUInfo[4]; #ifdef MINIMP3_TEST static int g_counter; if (g_counter++ > 100) return 0; #endif if (g_have_simd) goto end; drmp3_cpuid(CPUInfo, 0); if (CPUInfo[0] > 0) { drmp3_cpuid(CPUInfo, 1); g_have_simd = (CPUInfo[3] & (1 << 26)) + 1; return g_have_simd - 1; } end: return g_have_simd - 1; #endif } #elif defined(__ARM_NEON) || defined(__aarch64__) || defined(_M_ARM64) #include #define DRMP3_HAVE_SSE 0 #define DRMP3_HAVE_SIMD 1 #define DRMP3_VSTORE vst1q_f32 #define DRMP3_VLD vld1q_f32 #define DRMP3_VSET vmovq_n_f32 #define DRMP3_VADD vaddq_f32 #define DRMP3_VSUB vsubq_f32 #define DRMP3_VMUL vmulq_f32 #define DRMP3_VMAC(a, x, y) vmlaq_f32(a, x, y) #define DRMP3_VMSB(a, x, y) vmlsq_f32(a, x, y) #define DRMP3_VMUL_S(x, s) vmulq_f32(x, vmovq_n_f32(s)) #define DRMP3_VREV(x) vcombine_f32(vget_high_f32(vrev64q_f32(x)), vget_low_f32(vrev64q_f32(x))) typedef float32x4_t drmp3_f4; static int drmp3_have_simd(void) { return 1; } #else #define DRMP3_HAVE_SSE 0 #define DRMP3_HAVE_SIMD 0 #ifdef DR_MP3_ONLY_SIMD #error DR_MP3_ONLY_SIMD used, but SSE/NEON not enabled #endif #endif #else #define DRMP3_HAVE_SIMD 0 #endif #if defined(__ARM_ARCH) && (__ARM_ARCH >= 6) && !defined(__aarch64__) && !defined(_M_ARM64) #define DRMP3_HAVE_ARMV6 1 static __inline__ __attribute__((always_inline)) drmp3_int32 drmp3_clip_int16_arm(drmp3_int32 a) { drmp3_int32 x = 0; __asm__ ("ssat %0, #16, %1" : "=r"(x) : "r"(a)); return x; } #else #define DRMP3_HAVE_ARMV6 0 #endif #ifndef DRMP3_ASSERT #include #define DRMP3_ASSERT(expression) assert(expression) #endif #ifndef DRMP3_COPY_MEMORY #define DRMP3_COPY_MEMORY(dst, src, sz) memcpy((dst), (src), (sz)) #endif #ifndef DRMP3_MOVE_MEMORY #define DRMP3_MOVE_MEMORY(dst, src, sz) memmove((dst), (src), (sz)) #endif #ifndef DRMP3_ZERO_MEMORY #define DRMP3_ZERO_MEMORY(p, sz) memset((p), 0, (sz)) #endif #define DRMP3_ZERO_OBJECT(p) DRMP3_ZERO_MEMORY((p), sizeof(*(p))) #ifndef DRMP3_MALLOC #define DRMP3_MALLOC(sz) malloc((sz)) #endif #ifndef DRMP3_REALLOC #define DRMP3_REALLOC(p, sz) realloc((p), (sz)) #endif #ifndef DRMP3_FREE #define DRMP3_FREE(p) free((p)) #endif typedef struct { const drmp3_uint8 *buf; int pos, limit; } drmp3_bs; typedef struct { float scf[3*64]; drmp3_uint8 total_bands, stereo_bands, bitalloc[64], scfcod[64]; } drmp3_L12_scale_info; 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; bs->pos = 0; bs->limit = bytes*8; } static drmp3_uint32 drmp3_bs_get_bits(drmp3_bs *bs, int n) { drmp3_uint32 next, cache = 0, s = bs->pos & 7; int shl = n + s; const drmp3_uint8 *p = bs->buf + (bs->pos >> 3); if ((bs->pos += n) > bs->limit) return 0; next = *p++ & (255 >> s); while ((shl -= 8) > 0) { cache |= next << shl; next = *p++; } return cache | (next >> -shl); } static int drmp3_hdr_valid(const drmp3_uint8 *h) { return h[0] == 0xff && ((h[1] & 0xF0) == 0xf0 || (h[1] & 0xFE) == 0xe2) && (DRMP3_HDR_GET_LAYER(h) != 0) && (DRMP3_HDR_GET_BITRATE(h) != 15) && (DRMP3_HDR_GET_SAMPLE_RATE(h) != 3); } static int drmp3_hdr_compare(const drmp3_uint8 *h1, const drmp3_uint8 *h2) { return drmp3_hdr_valid(h2) && ((h1[1] ^ h2[1]) & 0xFE) == 0 && ((h1[2] ^ h2[2]) & 0x0C) == 0 && !(DRMP3_HDR_IS_FREE_FORMAT(h1) ^ DRMP3_HDR_IS_FREE_FORMAT(h2)); } static unsigned drmp3_hdr_bitrate_kbps(const drmp3_uint8 *h) { static const drmp3_uint8 halfrate[2][3][15] = { { { 0,4,8,12,16,20,24,28,32,40,48,56,64,72,80 }, { 0,4,8,12,16,20,24,28,32,40,48,56,64,72,80 }, { 0,16,24,28,32,40,48,56,64,72,80,88,96,112,128 } }, { { 0,16,20,24,28,32,40,48,56,64,80,96,112,128,160 }, { 0,16,24,28,32,40,48,56,64,80,96,112,128,160,192 }, { 0,16,32,48,64,80,96,112,128,144,160,176,192,208,224 } }, }; return 2*halfrate[!!DRMP3_HDR_TEST_MPEG1(h)][DRMP3_HDR_GET_LAYER(h) - 1][DRMP3_HDR_GET_BITRATE(h)]; } static unsigned drmp3_hdr_sample_rate_hz(const drmp3_uint8 *h) { static const unsigned g_hz[3] = { 44100, 48000, 32000 }; return g_hz[DRMP3_HDR_GET_SAMPLE_RATE(h)] >> (int)!DRMP3_HDR_TEST_MPEG1(h) >> (int)!DRMP3_HDR_TEST_NOT_MPEG25(h); } static unsigned drmp3_hdr_frame_samples(const drmp3_uint8 *h) { return DRMP3_HDR_IS_LAYER_1(h) ? 384 : (1152 >> (int)DRMP3_HDR_IS_FRAME_576(h)); } static int drmp3_hdr_frame_bytes(const drmp3_uint8 *h, int free_format_size) { int frame_bytes = drmp3_hdr_frame_samples(h)*drmp3_hdr_bitrate_kbps(h)*125/drmp3_hdr_sample_rate_hz(h); if (DRMP3_HDR_IS_LAYER_1(h)) { frame_bytes &= ~3; } return frame_bytes ? frame_bytes : free_format_size; } static int drmp3_hdr_padding(const drmp3_uint8 *h) { return DRMP3_HDR_TEST_PADDING(h) ? (DRMP3_HDR_IS_LAYER_1(h) ? 4 : 1) : 0; } #ifndef DR_MP3_ONLY_MP3 static const drmp3_L12_subband_alloc *drmp3_L12_subband_alloc_table(const drmp3_uint8 *hdr, drmp3_L12_scale_info *sci) { const drmp3_L12_subband_alloc *alloc; int mode = DRMP3_HDR_GET_STEREO_MODE(hdr); int nbands, stereo_bands = (mode == DRMP3_MODE_MONO) ? 0 : (mode == DRMP3_MODE_JOINT_STEREO) ? (DRMP3_HDR_GET_STEREO_MODE_EXT(hdr) << 2) + 4 : 32; if (DRMP3_HDR_IS_LAYER_1(hdr)) { static const drmp3_L12_subband_alloc g_alloc_L1[] = { { 76, 4, 32 } }; alloc = g_alloc_L1; nbands = 32; } else if (!DRMP3_HDR_TEST_MPEG1(hdr)) { static const drmp3_L12_subband_alloc g_alloc_L2M2[] = { { 60, 4, 4 }, { 44, 3, 7 }, { 44, 2, 19 } }; alloc = g_alloc_L2M2; nbands = 30; } else { static const drmp3_L12_subband_alloc g_alloc_L2M1[] = { { 0, 4, 3 }, { 16, 4, 8 }, { 32, 3, 12 }, { 40, 2, 7 } }; int sample_rate_idx = DRMP3_HDR_GET_SAMPLE_RATE(hdr); unsigned kbps = drmp3_hdr_bitrate_kbps(hdr) >> (int)(mode != DRMP3_MODE_MONO); if (!kbps) { kbps = 192; } alloc = g_alloc_L2M1; nbands = 27; if (kbps < 56) { static const drmp3_L12_subband_alloc g_alloc_L2M1_lowrate[] = { { 44, 4, 2 }, { 44, 3, 10 } }; alloc = g_alloc_L2M1_lowrate; nbands = sample_rate_idx == 2 ? 12 : 8; } else if (kbps >= 96 && sample_rate_idx != 1) { nbands = 30; } } sci->total_bands = (drmp3_uint8)nbands; sci->stereo_bands = (drmp3_uint8)DRMP3_MIN(stereo_bands, nbands); return alloc; } static void drmp3_L12_read_scalefactors(drmp3_bs *bs, drmp3_uint8 *pba, drmp3_uint8 *scfcod, int bands, float *scf) { static const float g_deq_L12[18*3] = { #define DRMP3_DQ(x) 9.53674316e-07f/x, 7.56931807e-07f/x, 6.00777173e-07f/x DRMP3_DQ(3),DRMP3_DQ(7),DRMP3_DQ(15),DRMP3_DQ(31),DRMP3_DQ(63),DRMP3_DQ(127),DRMP3_DQ(255),DRMP3_DQ(511),DRMP3_DQ(1023),DRMP3_DQ(2047),DRMP3_DQ(4095),DRMP3_DQ(8191),DRMP3_DQ(16383),DRMP3_DQ(32767),DRMP3_DQ(65535),DRMP3_DQ(3),DRMP3_DQ(5),DRMP3_DQ(9) }; int i, m; for (i = 0; i < bands; i++) { float s = 0; int ba = *pba++; int mask = ba ? 4 + ((19 >> scfcod[i]) & 3) : 0; for (m = 4; m; m >>= 1) { if (mask & m) { int b = drmp3_bs_get_bits(bs, 6); s = g_deq_L12[ba*3 - 6 + b % 3]*(int)(1 << 21 >> b/3); } *scf++ = s; } } } static void drmp3_L12_read_scale_info(const drmp3_uint8 *hdr, drmp3_bs *bs, drmp3_L12_scale_info *sci) { static const drmp3_uint8 g_bitalloc_code_tab[] = { 0,17, 3, 4, 5,6,7, 8,9,10,11,12,13,14,15,16, 0,17,18, 3,19,4,5, 6,7, 8, 9,10,11,12,13,16, 0,17,18, 3,19,4,5,16, 0,17,18,16, 0,17,18,19, 4,5,6, 7,8, 9,10,11,12,13,14,15, 0,17,18, 3,19,4,5, 6,7, 8, 9,10,11,12,13,14, 0, 2, 3, 4, 5,6,7, 8,9,10,11,12,13,14,15,16 }; const drmp3_L12_subband_alloc *subband_alloc = drmp3_L12_subband_alloc_table(hdr, sci); int i, k = 0, ba_bits = 0; const drmp3_uint8 *ba_code_tab = g_bitalloc_code_tab; for (i = 0; i < sci->total_bands; i++) { drmp3_uint8 ba; if (i == k) { k += subband_alloc->band_count; ba_bits = subband_alloc->code_tab_width; ba_code_tab = g_bitalloc_code_tab + subband_alloc->tab_offset; subband_alloc++; } ba = ba_code_tab[drmp3_bs_get_bits(bs, ba_bits)]; sci->bitalloc[2*i] = ba; if (i < sci->stereo_bands) { ba = ba_code_tab[drmp3_bs_get_bits(bs, ba_bits)]; } sci->bitalloc[2*i + 1] = sci->stereo_bands ? ba : 0; } for (i = 0; i < 2*sci->total_bands; i++) { sci->scfcod[i] = (drmp3_uint8)(sci->bitalloc[i] ? DRMP3_HDR_IS_LAYER_1(hdr) ? 2 : drmp3_bs_get_bits(bs, 2) : 6); } drmp3_L12_read_scalefactors(bs, sci->bitalloc, sci->scfcod, sci->total_bands*2, sci->scf); for (i = sci->stereo_bands; i < sci->total_bands; i++) { sci->bitalloc[2*i + 1] = 0; } } static int drmp3_L12_dequantize_granule(float *grbuf, drmp3_bs *bs, drmp3_L12_scale_info *sci, int group_size) { int i, j, k, choff = 576; for (j = 0; j < 4; j++) { float *dst = grbuf + group_size*j; for (i = 0; i < 2*sci->total_bands; i++) { int ba = sci->bitalloc[i]; if (ba != 0) { if (ba < 17) { int half = (1 << (ba - 1)) - 1; for (k = 0; k < group_size; k++) { dst[k] = (float)((int)drmp3_bs_get_bits(bs, ba) - half); } } else { unsigned mod = (2 << (ba - 17)) + 1; unsigned code = drmp3_bs_get_bits(bs, mod + 2 - (mod >> 3)); for (k = 0; k < group_size; k++, code /= mod) { dst[k] = (float)((int)(code % mod - mod/2)); } } } dst += choff; choff = 18 - choff; } } return group_size*4; } static void drmp3_L12_apply_scf_384(drmp3_L12_scale_info *sci, const float *scf, float *dst) { int i, k; DRMP3_COPY_MEMORY(dst + 576 + sci->stereo_bands*18, dst + sci->stereo_bands*18, (sci->total_bands - sci->stereo_bands)*18*sizeof(float)); for (i = 0; i < sci->total_bands; i++, dst += 18, scf += 6) { for (k = 0; k < 12; k++) { dst[k + 0] *= scf[0]; dst[k + 576] *= scf[3]; } } } #endif static int drmp3_L3_read_side_info(drmp3_bs *bs, drmp3_L3_gr_info *gr, const drmp3_uint8 *hdr) { static const drmp3_uint8 g_scf_long[8][23] = { { 6,6,6,6,6,6,8,10,12,14,16,20,24,28,32,38,46,52,60,68,58,54,0 }, { 12,12,12,12,12,12,16,20,24,28,32,40,48,56,64,76,90,2,2,2,2,2,0 }, { 6,6,6,6,6,6,8,10,12,14,16,20,24,28,32,38,46,52,60,68,58,54,0 }, { 6,6,6,6,6,6,8,10,12,14,16,18,22,26,32,38,46,54,62,70,76,36,0 }, { 6,6,6,6,6,6,8,10,12,14,16,20,24,28,32,38,46,52,60,68,58,54,0 }, { 4,4,4,4,4,4,6,6,8,8,10,12,16,20,24,28,34,42,50,54,76,158,0 }, { 4,4,4,4,4,4,6,6,6,8,10,12,16,18,22,28,34,40,46,54,54,192,0 }, { 4,4,4,4,4,4,6,6,8,10,12,16,20,24,30,38,46,56,68,84,102,26,0 } }; static const drmp3_uint8 g_scf_short[8][40] = { { 4,4,4,4,4,4,4,4,4,6,6,6,8,8,8,10,10,10,12,12,12,14,14,14,18,18,18,24,24,24,30,30,30,40,40,40,18,18,18,0 }, { 8,8,8,8,8,8,8,8,8,12,12,12,16,16,16,20,20,20,24,24,24,28,28,28,36,36,36,2,2,2,2,2,2,2,2,2,26,26,26,0 }, { 4,4,4,4,4,4,4,4,4,6,6,6,6,6,6,8,8,8,10,10,10,14,14,14,18,18,18,26,26,26,32,32,32,42,42,42,18,18,18,0 }, { 4,4,4,4,4,4,4,4,4,6,6,6,8,8,8,10,10,10,12,12,12,14,14,14,18,18,18,24,24,24,32,32,32,44,44,44,12,12,12,0 }, { 4,4,4,4,4,4,4,4,4,6,6,6,8,8,8,10,10,10,12,12,12,14,14,14,18,18,18,24,24,24,30,30,30,40,40,40,18,18,18,0 }, { 4,4,4,4,4,4,4,4,4,4,4,4,6,6,6,8,8,8,10,10,10,12,12,12,14,14,14,18,18,18,22,22,22,30,30,30,56,56,56,0 }, { 4,4,4,4,4,4,4,4,4,4,4,4,6,6,6,6,6,6,10,10,10,12,12,12,14,14,14,16,16,16,20,20,20,26,26,26,66,66,66,0 }, { 4,4,4,4,4,4,4,4,4,4,4,4,6,6,6,8,8,8,12,12,12,16,16,16,20,20,20,26,26,26,34,34,34,42,42,42,12,12,12,0 } }; static const drmp3_uint8 g_scf_mixed[8][40] = { { 6,6,6,6,6,6,6,6,6,8,8,8,10,10,10,12,12,12,14,14,14,18,18,18,24,24,24,30,30,30,40,40,40,18,18,18,0 }, { 12,12,12,4,4,4,8,8,8,12,12,12,16,16,16,20,20,20,24,24,24,28,28,28,36,36,36,2,2,2,2,2,2,2,2,2,26,26,26,0 }, { 6,6,6,6,6,6,6,6,6,6,6,6,8,8,8,10,10,10,14,14,14,18,18,18,26,26,26,32,32,32,42,42,42,18,18,18,0 }, { 6,6,6,6,6,6,6,6,6,8,8,8,10,10,10,12,12,12,14,14,14,18,18,18,24,24,24,32,32,32,44,44,44,12,12,12,0 }, { 6,6,6,6,6,6,6,6,6,8,8,8,10,10,10,12,12,12,14,14,14,18,18,18,24,24,24,30,30,30,40,40,40,18,18,18,0 }, { 4,4,4,4,4,4,6,6,4,4,4,6,6,6,8,8,8,10,10,10,12,12,12,14,14,14,18,18,18,22,22,22,30,30,30,56,56,56,0 }, { 4,4,4,4,4,4,6,6,4,4,4,6,6,6,6,6,6,10,10,10,12,12,12,14,14,14,16,16,16,20,20,20,26,26,26,66,66,66,0 }, { 4,4,4,4,4,4,6,6,4,4,4,6,6,6,8,8,8,12,12,12,16,16,16,20,20,20,26,26,26,34,34,34,42,42,42,12,12,12,0 } }; unsigned tables, scfsi = 0; int main_data_begin, part_23_sum = 0; int gr_count = DRMP3_HDR_IS_MONO(hdr) ? 1 : 2; int sr_idx = DRMP3_HDR_GET_MY_SAMPLE_RATE(hdr); sr_idx -= (sr_idx != 0); if (DRMP3_HDR_TEST_MPEG1(hdr)) { gr_count *= 2; main_data_begin = drmp3_bs_get_bits(bs, 9); scfsi = drmp3_bs_get_bits(bs, 7 + gr_count); } else { main_data_begin = drmp3_bs_get_bits(bs, 8 + gr_count) >> gr_count; } do { if (DRMP3_HDR_IS_MONO(hdr)) { scfsi <<= 4; } gr->part_23_length = (drmp3_uint16)drmp3_bs_get_bits(bs, 12); part_23_sum += gr->part_23_length; gr->big_values = (drmp3_uint16)drmp3_bs_get_bits(bs, 9); if (gr->big_values > 288) { return -1; } gr->global_gain = (drmp3_uint8)drmp3_bs_get_bits(bs, 8); gr->scalefac_compress = (drmp3_uint16)drmp3_bs_get_bits(bs, DRMP3_HDR_TEST_MPEG1(hdr) ? 4 : 9); gr->sfbtab = g_scf_long[sr_idx]; gr->n_long_sfb = 22; gr->n_short_sfb = 0; if (drmp3_bs_get_bits(bs, 1)) { gr->block_type = (drmp3_uint8)drmp3_bs_get_bits(bs, 2); if (!gr->block_type) { return -1; } gr->mixed_block_flag = (drmp3_uint8)drmp3_bs_get_bits(bs, 1); gr->region_count[0] = 7; gr->region_count[1] = 255; if (gr->block_type == DRMP3_SHORT_BLOCK_TYPE) { scfsi &= 0x0F0F; if (!gr->mixed_block_flag) { gr->region_count[0] = 8; gr->sfbtab = g_scf_short[sr_idx]; gr->n_long_sfb = 0; gr->n_short_sfb = 39; } else { gr->sfbtab = g_scf_mixed[sr_idx]; gr->n_long_sfb = DRMP3_HDR_TEST_MPEG1(hdr) ? 8 : 6; gr->n_short_sfb = 30; } } tables = drmp3_bs_get_bits(bs, 10); tables <<= 5; gr->subblock_gain[0] = (drmp3_uint8)drmp3_bs_get_bits(bs, 3); gr->subblock_gain[1] = (drmp3_uint8)drmp3_bs_get_bits(bs, 3); gr->subblock_gain[2] = (drmp3_uint8)drmp3_bs_get_bits(bs, 3); } else { gr->block_type = 0; gr->mixed_block_flag = 0; tables = drmp3_bs_get_bits(bs, 15); gr->region_count[0] = (drmp3_uint8)drmp3_bs_get_bits(bs, 4); gr->region_count[1] = (drmp3_uint8)drmp3_bs_get_bits(bs, 3); gr->region_count[2] = 255; } gr->table_select[0] = (drmp3_uint8)(tables >> 10); gr->table_select[1] = (drmp3_uint8)((tables >> 5) & 31); gr->table_select[2] = (drmp3_uint8)((tables) & 31); gr->preflag = (drmp3_uint8)(DRMP3_HDR_TEST_MPEG1(hdr) ? drmp3_bs_get_bits(bs, 1) : (gr->scalefac_compress >= 500)); gr->scalefac_scale = (drmp3_uint8)drmp3_bs_get_bits(bs, 1); gr->count1_table = (drmp3_uint8)drmp3_bs_get_bits(bs, 1); gr->scfsi = (drmp3_uint8)((scfsi >> 12) & 15); scfsi <<= 4; gr++; } while(--gr_count); if (part_23_sum + bs->pos > bs->limit + main_data_begin*8) { return -1; } return main_data_begin; } static void drmp3_L3_read_scalefactors(drmp3_uint8 *scf, drmp3_uint8 *ist_pos, const drmp3_uint8 *scf_size, const drmp3_uint8 *scf_count, drmp3_bs *bitbuf, int scfsi) { int i, k; for (i = 0; i < 4 && scf_count[i]; i++, scfsi *= 2) { int cnt = scf_count[i]; if (scfsi & 8) { DRMP3_COPY_MEMORY(scf, ist_pos, cnt); } else { int bits = scf_size[i]; if (!bits) { DRMP3_ZERO_MEMORY(scf, cnt); DRMP3_ZERO_MEMORY(ist_pos, cnt); } else { int max_scf = (scfsi < 0) ? (1 << bits) - 1 : -1; for (k = 0; k < cnt; k++) { int s = drmp3_bs_get_bits(bitbuf, bits); ist_pos[k] = (drmp3_uint8)(s == max_scf ? -1 : s); scf[k] = (drmp3_uint8)s; } } } ist_pos += cnt; scf += cnt; } scf[0] = scf[1] = scf[2] = 0; } static float drmp3_L3_ldexp_q2(float y, int exp_q2) { static const float g_expfrac[4] = { 9.31322575e-10f,7.83145814e-10f,6.58544508e-10f,5.53767716e-10f }; int e; do { e = DRMP3_MIN(30*4, exp_q2); y *= g_expfrac[e & 3]*(1 << 30 >> (e >> 2)); } while ((exp_q2 -= e) > 0); return y; } 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] = { { 6,5,5, 5,6,5,5,5,6,5, 7,3,11,10,0,0, 7, 7, 7,0, 6, 6,6,3, 8, 8,5,0 }, { 8,9,6,12,6,9,9,9,6,9,12,6,15,18,0,0, 6,15,12,0, 6,12,9,6, 6,18,9,0 }, { 9,9,6,12,9,9,9,9,9,9,12,6,18,18,0,0,12,12,12,0,12, 9,9,6,15,12,9,0 } }; const drmp3_uint8 *scf_partition = g_scf_partitions[!!gr->n_short_sfb + !gr->n_long_sfb]; drmp3_uint8 scf_size[4], iscf[40]; int i, scf_shift = gr->scalefac_scale + 1, gain_exp, scfsi = gr->scfsi; float gain; if (DRMP3_HDR_TEST_MPEG1(hdr)) { static const drmp3_uint8 g_scfc_decode[16] = { 0,1,2,3, 12,5,6,7, 9,10,11,13, 14,15,18,19 }; int part = g_scfc_decode[gr->scalefac_compress]; scf_size[1] = scf_size[0] = (drmp3_uint8)(part >> 2); scf_size[3] = scf_size[2] = (drmp3_uint8)(part & 3); } else { static const drmp3_uint8 g_mod[6*4] = { 5,5,4,4,5,5,4,1,4,3,1,1,5,6,6,1,4,4,4,1,4,3,1,1 }; int k, modprod, sfc, ist = DRMP3_HDR_TEST_I_STEREO(hdr) && ch; sfc = gr->scalefac_compress >> ist; for (k = ist*3*4; sfc >= 0; sfc -= modprod, k += 4) { for (modprod = 1, i = 3; i >= 0; i--) { scf_size[i] = (drmp3_uint8)(sfc / modprod % g_mod[k + i]); modprod *= g_mod[k + i]; } } scf_partition += k; scfsi = -16; } drmp3_L3_read_scalefactors(iscf, ist_pos, scf_size, scf_partition, bs, scfsi); if (gr->n_short_sfb) { int sh = 3 - scf_shift; for (i = 0; i < gr->n_short_sfb; i += 3) { iscf[gr->n_long_sfb + i + 0] = (drmp3_uint8)(iscf[gr->n_long_sfb + i + 0] + (gr->subblock_gain[0] << sh)); iscf[gr->n_long_sfb + i + 1] = (drmp3_uint8)(iscf[gr->n_long_sfb + i + 1] + (gr->subblock_gain[1] << sh)); iscf[gr->n_long_sfb + i + 2] = (drmp3_uint8)(iscf[gr->n_long_sfb + i + 2] + (gr->subblock_gain[2] << sh)); } } else if (gr->preflag) { static const drmp3_uint8 g_preamp[10] = { 1,1,1,1,2,2,3,3,3,2 }; for (i = 0; i < 10; i++) { iscf[11 + i] = (drmp3_uint8)(iscf[11 + i] + g_preamp[i]); } } gain_exp = gr->global_gain + DRMP3_BITS_DEQUANTIZER_OUT*4 - 210 - (DRMP3_HDR_IS_MS_STEREO(hdr) ? 2 : 0); gain = drmp3_L3_ldexp_q2(1 << (DRMP3_MAX_SCFI/4), DRMP3_MAX_SCFI - gain_exp); for (i = 0; i < (int)(gr->n_long_sfb + gr->n_short_sfb); i++) { scf[i] = drmp3_L3_ldexp_q2(gain, iscf[i] << scf_shift); } } 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, 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,40.317474f,43.711787f,47.173345f,50.699631f,54.288352f,57.937408f,61.644865f,65.408941f,69.227979f,73.100443f,77.024898f,81.000000f,85.024491f,89.097188f,93.216975f,97.382800f,101.593667f,105.848633f,110.146801f,114.487321f,118.869381f,123.292209f,127.755065f,132.257246f,136.798076f,141.376907f,145.993119f,150.646117f,155.335327f,160.060199f,164.820202f,169.614826f,174.443577f,179.305980f,184.201575f,189.129918f,194.090580f,199.083145f,204.107210f,209.162385f,214.248292f,219.364564f,224.510845f,229.686789f,234.892058f,240.126328f,245.389280f,250.680604f,256.000000f,261.347174f,266.721841f,272.123723f,277.552547f,283.008049f,288.489971f,293.998060f,299.532071f,305.091761f,310.676898f,316.287249f,321.922592f,327.582707f,333.267377f,338.976394f,344.709550f,350.466646f,356.247482f,362.051866f,367.879608f,373.730522f,379.604427f,385.501143f,391.420496f,397.362314f,403.326427f,409.312672f,415.320884f,421.350905f,427.402579f,433.475750f,439.570269f,445.685987f,451.822757f,457.980436f,464.158883f,470.357960f,476.577530f,482.817459f,489.077615f,495.357868f,501.658090f,507.978156f,514.317941f,520.677324f,527.056184f,533.454404f,539.871867f,546.308458f,552.764065f,559.238575f,565.731879f,572.243870f,578.774440f,585.323483f,591.890898f,598.476581f,605.080431f,611.702349f,618.342238f,625.000000f,631.675540f,638.368763f,645.079578f }; static float drmp3_L3_pow_43(int x) { float frac; int sign, mult = 256; if (x < 129) { return g_drmp3_pow43[16 + x]; } if (x < 1024) { mult = 16; x <<= 3; } sign = 2*x & 64; frac = (float)((x & 63) - sign) / ((x & ~63) + sign); return g_drmp3_pow43[16 + ((x + sign) >> 6)]*(1.f + frac*((4.f/3) + frac*(2.f/9)))*mult; } static void drmp3_L3_huffman(float *dst, drmp3_bs *bs, const drmp3_L3_gr_info *gr_info, const float *scf, int layer3gr_limit) { static const drmp3_int16 tabs[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 785,785,785,785,784,784,784,784,513,513,513,513,513,513,513,513,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256, -255,1313,1298,1282,785,785,785,785,784,784,784,784,769,769,769,769,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,290,288, -255,1313,1298,1282,769,769,769,769,529,529,529,529,529,529,529,529,528,528,528,528,528,528,528,528,512,512,512,512,512,512,512,512,290,288, -253,-318,-351,-367,785,785,785,785,784,784,784,784,769,769,769,769,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,819,818,547,547,275,275,275,275,561,560,515,546,289,274,288,258, -254,-287,1329,1299,1314,1312,1057,1057,1042,1042,1026,1026,784,784,784,784,529,529,529,529,529,529,529,529,769,769,769,769,768,768,768,768,563,560,306,306,291,259, -252,-413,-477,-542,1298,-575,1041,1041,784,784,784,784,769,769,769,769,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,-383,-399,1107,1092,1106,1061,849,849,789,789,1104,1091,773,773,1076,1075,341,340,325,309,834,804,577,577,532,532,516,516,832,818,803,816,561,561,531,531,515,546,289,289,288,258, -252,-429,-493,-559,1057,1057,1042,1042,529,529,529,529,529,529,529,529,784,784,784,784,769,769,769,769,512,512,512,512,512,512,512,512,-382,1077,-415,1106,1061,1104,849,849,789,789,1091,1076,1029,1075,834,834,597,581,340,340,339,324,804,833,532,532,832,772,818,803,817,787,816,771,290,290,290,290,288,258, -253,-349,-414,-447,-463,1329,1299,-479,1314,1312,1057,1057,1042,1042,1026,1026,785,785,785,785,784,784,784,784,769,769,769,769,768,768,768,768,-319,851,821,-335,836,850,805,849,341,340,325,336,533,533,579,579,564,564,773,832,578,548,563,516,321,276,306,291,304,259, -251,-572,-733,-830,-863,-879,1041,1041,784,784,784,784,769,769,769,769,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,-511,-527,-543,1396,1351,1381,1366,1395,1335,1380,-559,1334,1138,1138,1063,1063,1350,1392,1031,1031,1062,1062,1364,1363,1120,1120,1333,1348,881,881,881,881,375,374,359,373,343,358,341,325,791,791,1123,1122,-703,1105,1045,-719,865,865,790,790,774,774,1104,1029,338,293,323,308,-799,-815,833,788,772,818,803,816,322,292,307,320,561,531,515,546,289,274,288,258, -251,-525,-605,-685,-765,-831,-846,1298,1057,1057,1312,1282,785,785,785,785,784,784,784,784,769,769,769,769,512,512,512,512,512,512,512,512,1399,1398,1383,1367,1382,1396,1351,-511,1381,1366,1139,1139,1079,1079,1124,1124,1364,1349,1363,1333,882,882,882,882,807,807,807,807,1094,1094,1136,1136,373,341,535,535,881,775,867,822,774,-591,324,338,-671,849,550,550,866,864,609,609,293,336,534,534,789,835,773,-751,834,804,308,307,833,788,832,772,562,562,547,547,305,275,560,515,290,290, -252,-397,-477,-557,-622,-653,-719,-735,-750,1329,1299,1314,1057,1057,1042,1042,1312,1282,1024,1024,785,785,785,785,784,784,784,784,769,769,769,769,-383,1127,1141,1111,1126,1140,1095,1110,869,869,883,883,1079,1109,882,882,375,374,807,868,838,881,791,-463,867,822,368,263,852,837,836,-543,610,610,550,550,352,336,534,534,865,774,851,821,850,805,593,533,579,564,773,832,578,578,548,548,577,577,307,276,306,291,516,560,259,259, -250,-2107,-2507,-2764,-2909,-2974,-3007,-3023,1041,1041,1040,1040,769,769,769,769,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,-767,-1052,-1213,-1277,-1358,-1405,-1469,-1535,-1550,-1582,-1614,-1647,-1662,-1694,-1726,-1759,-1774,-1807,-1822,-1854,-1886,1565,-1919,-1935,-1951,-1967,1731,1730,1580,1717,-1983,1729,1564,-1999,1548,-2015,-2031,1715,1595,-2047,1714,-2063,1610,-2079,1609,-2095,1323,1323,1457,1457,1307,1307,1712,1547,1641,1700,1699,1594,1685,1625,1442,1442,1322,1322,-780,-973,-910,1279,1278,1277,1262,1276,1261,1275,1215,1260,1229,-959,974,974,989,989,-943,735,478,478,495,463,506,414,-1039,1003,958,1017,927,942,987,957,431,476,1272,1167,1228,-1183,1256,-1199,895,895,941,941,1242,1227,1212,1135,1014,1014,490,489,503,487,910,1013,985,925,863,894,970,955,1012,847,-1343,831,755,755,984,909,428,366,754,559,-1391,752,486,457,924,997,698,698,983,893,740,740,908,877,739,739,667,667,953,938,497,287,271,271,683,606,590,712,726,574,302,302,738,736,481,286,526,725,605,711,636,724,696,651,589,681,666,710,364,467,573,695,466,466,301,465,379,379,709,604,665,679,316,316,634,633,436,436,464,269,424,394,452,332,438,363,347,408,393,448,331,422,362,407,392,421,346,406,391,376,375,359,1441,1306,-2367,1290,-2383,1337,-2399,-2415,1426,1321,-2431,1411,1336,-2447,-2463,-2479,1169,1169,1049,1049,1424,1289,1412,1352,1319,-2495,1154,1154,1064,1064,1153,1153,416,390,360,404,403,389,344,374,373,343,358,372,327,357,342,311,356,326,1395,1394,1137,1137,1047,1047,1365,1392,1287,1379,1334,1364,1349,1378,1318,1363,792,792,792,792,1152,1152,1032,1032,1121,1121,1046,1046,1120,1120,1030,1030,-2895,1106,1061,1104,849,849,789,789,1091,1076,1029,1090,1060,1075,833,833,309,324,532,532,832,772,818,803,561,561,531,560,515,546,289,274,288,258, -250,-1179,-1579,-1836,-1996,-2124,-2253,-2333,-2413,-2477,-2542,-2574,-2607,-2622,-2655,1314,1313,1298,1312,1282,785,785,785,785,1040,1040,1025,1025,768,768,768,768,-766,-798,-830,-862,-895,-911,-927,-943,-959,-975,-991,-1007,-1023,-1039,-1055,-1070,1724,1647,-1103,-1119,1631,1767,1662,1738,1708,1723,-1135,1780,1615,1779,1599,1677,1646,1778,1583,-1151,1777,1567,1737,1692,1765,1722,1707,1630,1751,1661,1764,1614,1736,1676,1763,1750,1645,1598,1721,1691,1762,1706,1582,1761,1566,-1167,1749,1629,767,766,751,765,494,494,735,764,719,749,734,763,447,447,748,718,477,506,431,491,446,476,461,505,415,430,475,445,504,399,460,489,414,503,383,474,429,459,502,502,746,752,488,398,501,473,413,472,486,271,480,270,-1439,-1455,1357,-1471,-1487,-1503,1341,1325,-1519,1489,1463,1403,1309,-1535,1372,1448,1418,1476,1356,1462,1387,-1551,1475,1340,1447,1402,1386,-1567,1068,1068,1474,1461,455,380,468,440,395,425,410,454,364,467,466,464,453,269,409,448,268,432,1371,1473,1432,1417,1308,1460,1355,1446,1459,1431,1083,1083,1401,1416,1458,1445,1067,1067,1370,1457,1051,1051,1291,1430,1385,1444,1354,1415,1400,1443,1082,1082,1173,1113,1186,1066,1185,1050,-1967,1158,1128,1172,1097,1171,1081,-1983,1157,1112,416,266,375,400,1170,1142,1127,1065,793,793,1169,1033,1156,1096,1141,1111,1155,1080,1126,1140,898,898,808,808,897,897,792,792,1095,1152,1032,1125,1110,1139,1079,1124,882,807,838,881,853,791,-2319,867,368,263,822,852,837,866,806,865,-2399,851,352,262,534,534,821,836,594,594,549,549,593,593,533,533,848,773,579,579,564,578,548,563,276,276,577,576,306,291,516,560,305,305,275,259, -251,-892,-2058,-2620,-2828,-2957,-3023,-3039,1041,1041,1040,1040,769,769,769,769,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,-511,-527,-543,-559,1530,-575,-591,1528,1527,1407,1526,1391,1023,1023,1023,1023,1525,1375,1268,1268,1103,1103,1087,1087,1039,1039,1523,-604,815,815,815,815,510,495,509,479,508,463,507,447,431,505,415,399,-734,-782,1262,-815,1259,1244,-831,1258,1228,-847,-863,1196,-879,1253,987,987,748,-767,493,493,462,477,414,414,686,669,478,446,461,445,474,429,487,458,412,471,1266,1264,1009,1009,799,799,-1019,-1276,-1452,-1581,-1677,-1757,-1821,-1886,-1933,-1997,1257,1257,1483,1468,1512,1422,1497,1406,1467,1496,1421,1510,1134,1134,1225,1225,1466,1451,1374,1405,1252,1252,1358,1480,1164,1164,1251,1251,1238,1238,1389,1465,-1407,1054,1101,-1423,1207,-1439,830,830,1248,1038,1237,1117,1223,1148,1236,1208,411,426,395,410,379,269,1193,1222,1132,1235,1221,1116,976,976,1192,1162,1177,1220,1131,1191,963,963,-1647,961,780,-1663,558,558,994,993,437,408,393,407,829,978,813,797,947,-1743,721,721,377,392,844,950,828,890,706,706,812,859,796,960,948,843,934,874,571,571,-1919,690,555,689,421,346,539,539,944,779,918,873,932,842,903,888,570,570,931,917,674,674,-2575,1562,-2591,1609,-2607,1654,1322,1322,1441,1441,1696,1546,1683,1593,1669,1624,1426,1426,1321,1321,1639,1680,1425,1425,1305,1305,1545,1668,1608,1623,1667,1592,1638,1666,1320,1320,1652,1607,1409,1409,1304,1304,1288,1288,1664,1637,1395,1395,1335,1335,1622,1636,1394,1394,1319,1319,1606,1621,1392,1392,1137,1137,1137,1137,345,390,360,375,404,373,1047,-2751,-2767,-2783,1062,1121,1046,-2799,1077,-2815,1106,1061,789,789,1105,1104,263,355,310,340,325,354,352,262,339,324,1091,1076,1029,1090,1060,1075,833,833,788,788,1088,1028,818,818,803,803,561,561,531,531,816,771,546,546,289,274,288,258, -253,-317,-381,-446,-478,-509,1279,1279,-811,-1179,-1451,-1756,-1900,-2028,-2189,-2253,-2333,-2414,-2445,-2511,-2526,1313,1298,-2559,1041,1041,1040,1040,1025,1025,1024,1024,1022,1007,1021,991,1020,975,1019,959,687,687,1018,1017,671,671,655,655,1016,1015,639,639,758,758,623,623,757,607,756,591,755,575,754,559,543,543,1009,783,-575,-621,-685,-749,496,-590,750,749,734,748,974,989,1003,958,988,973,1002,942,987,957,972,1001,926,986,941,971,956,1000,910,985,925,999,894,970,-1071,-1087,-1102,1390,-1135,1436,1509,1451,1374,-1151,1405,1358,1480,1420,-1167,1507,1494,1389,1342,1465,1435,1450,1326,1505,1310,1493,1373,1479,1404,1492,1464,1419,428,443,472,397,736,526,464,464,486,457,442,471,484,482,1357,1449,1434,1478,1388,1491,1341,1490,1325,1489,1463,1403,1309,1477,1372,1448,1418,1433,1476,1356,1462,1387,-1439,1475,1340,1447,1402,1474,1324,1461,1371,1473,269,448,1432,1417,1308,1460,-1711,1459,-1727,1441,1099,1099,1446,1386,1431,1401,-1743,1289,1083,1083,1160,1160,1458,1445,1067,1067,1370,1457,1307,1430,1129,1129,1098,1098,268,432,267,416,266,400,-1887,1144,1187,1082,1173,1113,1186,1066,1050,1158,1128,1143,1172,1097,1171,1081,420,391,1157,1112,1170,1142,1127,1065,1169,1049,1156,1096,1141,1111,1155,1080,1126,1154,1064,1153,1140,1095,1048,-2159,1125,1110,1137,-2175,823,823,1139,1138,807,807,384,264,368,263,868,838,853,791,867,822,852,837,866,806,865,790,-2319,851,821,836,352,262,850,805,849,-2399,533,533,835,820,336,261,578,548,563,577,532,532,832,772,562,562,547,547,305,275,560,515,290,290,288,258 }; static const drmp3_uint8 tab32[] = { 130,162,193,209,44,28,76,140,9,9,9,9,9,9,9,9,190,254,222,238,126,94,157,157,109,61,173,205}; static const drmp3_uint8 tab33[] = { 252,236,220,204,188,172,156,140,124,108,92,76,60,44,28,12 }; static const drmp3_int16 tabindex[2*16] = { 0,32,64,98,0,132,180,218,292,364,426,538,648,746,0,1126,1460,1460,1460,1460,1460,1460,1460,1460,1842,1842,1842,1842,1842,1842,1842,1842 }; static const drmp3_uint8 g_linbits[] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,2,3,4,6,8,10,13,4,5,6,7,8,9,11,13 }; #define DRMP3_PEEK_BITS(n) (bs_cache >> (32 - (n))) #define DRMP3_FLUSH_BITS(n) { bs_cache <<= (n); bs_sh += (n); } #define DRMP3_CHECK_BITS while (bs_sh >= 0) { bs_cache |= (drmp3_uint32)*bs_next_ptr++ << bs_sh; bs_sh -= 8; } #define DRMP3_BSPOS ((bs_next_ptr - bs->buf)*8 - 24 + bs_sh) float one = 0.0f; int ireg = 0, big_val_cnt = gr_info->big_values; const drmp3_uint8 *sfb = gr_info->sfbtab; const drmp3_uint8 *bs_next_ptr = bs->buf + bs->pos/8; drmp3_uint32 bs_cache = (((bs_next_ptr[0]*256u + bs_next_ptr[1])*256u + bs_next_ptr[2])*256u + bs_next_ptr[3]) << (bs->pos & 7); int pairs_to_decode, np, bs_sh = (bs->pos & 7) - 8; bs_next_ptr += 4; while (big_val_cnt > 0) { int tab_num = gr_info->table_select[ireg]; int sfb_cnt = gr_info->region_count[ireg++]; const drmp3_int16 *codebook = tabs + tabindex[tab_num]; int linbits = g_linbits[tab_num]; if (linbits) { do { np = *sfb++ / 2; pairs_to_decode = DRMP3_MIN(big_val_cnt, np); one = *scf++; do { int j, w = 5; int leaf = codebook[DRMP3_PEEK_BITS(w)]; while (leaf < 0) { DRMP3_FLUSH_BITS(w); w = leaf & 7; leaf = codebook[DRMP3_PEEK_BITS(w) - (leaf >> 3)]; } DRMP3_FLUSH_BITS(leaf >> 8); for (j = 0; j < 2; j++, dst++, leaf >>= 4) { int lsb = leaf & 0x0F; if (lsb == 15) { lsb += DRMP3_PEEK_BITS(linbits); DRMP3_FLUSH_BITS(linbits); DRMP3_CHECK_BITS; *dst = one*drmp3_L3_pow_43(lsb)*((drmp3_int32)bs_cache < 0 ? -1: 1); } else { *dst = g_drmp3_pow43[16 + lsb - 16*(bs_cache >> 31)]*one; } DRMP3_FLUSH_BITS(lsb ? 1 : 0); } DRMP3_CHECK_BITS; } while (--pairs_to_decode); } while ((big_val_cnt -= np) > 0 && --sfb_cnt >= 0); } else { do { np = *sfb++ / 2; pairs_to_decode = DRMP3_MIN(big_val_cnt, np); one = *scf++; do { int j, w = 5; int leaf = codebook[DRMP3_PEEK_BITS(w)]; while (leaf < 0) { DRMP3_FLUSH_BITS(w); w = leaf & 7; leaf = codebook[DRMP3_PEEK_BITS(w) - (leaf >> 3)]; } DRMP3_FLUSH_BITS(leaf >> 8); for (j = 0; j < 2; j++, dst++, leaf >>= 4) { int lsb = leaf & 0x0F; *dst = g_drmp3_pow43[16 + lsb - 16*(bs_cache >> 31)]*one; DRMP3_FLUSH_BITS(lsb ? 1 : 0); } DRMP3_CHECK_BITS; } while (--pairs_to_decode); } while ((big_val_cnt -= np) > 0 && --sfb_cnt >= 0); } } for (np = 1 - big_val_cnt;; dst += 4) { const drmp3_uint8 *codebook_count1 = (gr_info->count1_table) ? tab33 : tab32; int leaf = codebook_count1[DRMP3_PEEK_BITS(4)]; if (!(leaf & 8)) { leaf = codebook_count1[(leaf >> 3) + (bs_cache << 4 >> (32 - (leaf & 3)))]; } DRMP3_FLUSH_BITS(leaf & 7); if (DRMP3_BSPOS > layer3gr_limit) { break; } #define DRMP3_RELOAD_SCALEFACTOR if (!--np) { np = *sfb++/2; if (!np) break; one = *scf++; } #define DRMP3_DEQ_COUNT1(s) if (leaf & (128 >> s)) { dst[s] = ((drmp3_int32)bs_cache < 0) ? -one : one; DRMP3_FLUSH_BITS(1) } DRMP3_RELOAD_SCALEFACTOR; DRMP3_DEQ_COUNT1(0); DRMP3_DEQ_COUNT1(1); DRMP3_RELOAD_SCALEFACTOR; DRMP3_DEQ_COUNT1(2); DRMP3_DEQ_COUNT1(3); DRMP3_CHECK_BITS; } bs->pos = layer3gr_limit; } static void drmp3_L3_midside_stereo(float *left, int n) { int i = 0; float *right = left + 576; #if DRMP3_HAVE_SIMD if (drmp3_have_simd()) { for (; i < n - 3; i += 4) { drmp3_f4 vl = DRMP3_VLD(left + i); drmp3_f4 vr = DRMP3_VLD(right + i); DRMP3_VSTORE(left + i, DRMP3_VADD(vl, vr)); DRMP3_VSTORE(right + i, DRMP3_VSUB(vl, vr)); } #ifdef __GNUC__ if (__builtin_constant_p(n % 4 == 0) && n % 4 == 0) return; #endif } #endif for (; i < n; i++) { float a = left[i]; float b = right[i]; left[i] = a + b; right[i] = a - b; } } static void drmp3_L3_intensity_stereo_band(float *left, int n, float kl, float kr) { int i; for (i = 0; i < n; i++) { left[i + 576] = left[i]*kr; left[i] = left[i]*kl; } } static void drmp3_L3_stereo_top_band(const float *right, const drmp3_uint8 *sfb, int nbands, int max_band[3]) { int i, k; max_band[0] = max_band[1] = max_band[2] = -1; for (i = 0; i < nbands; i++) { for (k = 0; k < sfb[i]; k += 2) { if (right[k] != 0 || right[k + 1] != 0) { max_band[i % 3] = i; break; } } right += sfb[i]; } } static void drmp3_L3_stereo_process(float *left, const drmp3_uint8 *ist_pos, const drmp3_uint8 *sfb, const drmp3_uint8 *hdr, int max_band[3], int mpeg2_sh) { static const float g_pan[7*2] = { 0,1,0.21132487f,0.78867513f,0.36602540f,0.63397460f,0.5f,0.5f,0.63397460f,0.36602540f,0.78867513f,0.21132487f,1,0 }; unsigned i, max_pos = DRMP3_HDR_TEST_MPEG1(hdr) ? 7 : 64; for (i = 0; sfb[i]; i++) { unsigned ipos = ist_pos[i]; if ((int)i > max_band[i % 3] && ipos < max_pos) { float kl, kr, s = DRMP3_HDR_TEST_MS_STEREO(hdr) ? 1.41421356f : 1; if (DRMP3_HDR_TEST_MPEG1(hdr)) { kl = g_pan[2*ipos]; kr = g_pan[2*ipos + 1]; } else { kl = 1; kr = drmp3_L3_ldexp_q2(1, (ipos + 1) >> 1 << mpeg2_sh); if (ipos & 1) { kl = kr; kr = 1; } } drmp3_L3_intensity_stereo_band(left, sfb[i], kl*s, kr*s); } else if (DRMP3_HDR_TEST_MS_STEREO(hdr)) { drmp3_L3_midside_stereo(left, sfb[i]); } left += sfb[i]; } } static void drmp3_L3_intensity_stereo(float *left, drmp3_uint8 *ist_pos, const drmp3_L3_gr_info *gr, const drmp3_uint8 *hdr) { int max_band[3], n_sfb = gr->n_long_sfb + gr->n_short_sfb; int i, max_blocks = gr->n_short_sfb ? 3 : 1; drmp3_L3_stereo_top_band(left + 576, gr->sfbtab, n_sfb, max_band); if (gr->n_long_sfb) { max_band[0] = max_band[1] = max_band[2] = DRMP3_MAX(DRMP3_MAX(max_band[0], max_band[1]), max_band[2]); } for (i = 0; i < max_blocks; i++) { int default_pos = DRMP3_HDR_TEST_MPEG1(hdr) ? 3 : 0; int itop = n_sfb - max_blocks + i; int prev = itop - max_blocks; ist_pos[itop] = (drmp3_uint8)(max_band[i] >= prev ? default_pos : ist_pos[prev]); } drmp3_L3_stereo_process(left, ist_pos, gr->sfbtab, hdr, max_band, gr[1].scalefac_compress & 1); } static void drmp3_L3_reorder(float *grbuf, float *scratch, const drmp3_uint8 *sfb) { int i, len; float *src = grbuf, *dst = scratch; for (;0 != (len = *sfb); sfb += 3, src += 2*len) { for (i = 0; i < len; i++, src++) { *dst++ = src[0*len]; *dst++ = src[1*len]; *dst++ = src[2*len]; } } DRMP3_COPY_MEMORY(grbuf, scratch, (dst - scratch)*sizeof(float)); } static void drmp3_L3_antialias(float *grbuf, int nbands) { static const float g_aa[2][8] = { {0.85749293f,0.88174200f,0.94962865f,0.98331459f,0.99551782f,0.99916056f,0.99989920f,0.99999316f}, {0.51449576f,0.47173197f,0.31337745f,0.18191320f,0.09457419f,0.04096558f,0.01419856f,0.00369997f} }; for (; nbands > 0; nbands--, grbuf += 18) { int i = 0; #if DRMP3_HAVE_SIMD if (drmp3_have_simd()) for (; i < 8; i += 4) { drmp3_f4 vu = DRMP3_VLD(grbuf + 18 + i); drmp3_f4 vd = DRMP3_VLD(grbuf + 14 - i); drmp3_f4 vc0 = DRMP3_VLD(g_aa[0] + i); drmp3_f4 vc1 = DRMP3_VLD(g_aa[1] + i); vd = DRMP3_VREV(vd); DRMP3_VSTORE(grbuf + 18 + i, DRMP3_VSUB(DRMP3_VMUL(vu, vc0), DRMP3_VMUL(vd, vc1))); vd = DRMP3_VADD(DRMP3_VMUL(vu, vc1), DRMP3_VMUL(vd, vc0)); DRMP3_VSTORE(grbuf + 14 - i, DRMP3_VREV(vd)); } #endif #ifndef DR_MP3_ONLY_SIMD for(; i < 8; i++) { float u = grbuf[18 + i]; float d = grbuf[17 - i]; grbuf[18 + i] = u*g_aa[0][i] - d*g_aa[1][i]; grbuf[17 - i] = u*g_aa[1][i] + d*g_aa[0][i]; } #endif } } static void drmp3_L3_dct3_9(float *y) { float s0, s1, s2, s3, s4, s5, s6, s7, s8, t0, t2, t4; s0 = y[0]; s2 = y[2]; s4 = y[4]; s6 = y[6]; s8 = y[8]; t0 = s0 + s6*0.5f; s0 -= s6; t4 = (s4 + s2)*0.93969262f; t2 = (s8 + s2)*0.76604444f; s6 = (s4 - s8)*0.17364818f; s4 += s8 - s2; s2 = s0 - s4*0.5f; y[4] = s4 + s0; s8 = t0 - t2 + s6; s0 = t0 - t4 + t2; s4 = t0 + t4 - s6; s1 = y[1]; s3 = y[3]; s5 = y[5]; s7 = y[7]; s3 *= 0.86602540f; t0 = (s5 + s1)*0.98480775f; t4 = (s5 - s7)*0.34202014f; t2 = (s1 + s7)*0.64278761f; s1 = (s1 - s5 - s7)*0.86602540f; s5 = t0 - s3 - t2; s7 = t4 - s3 - t0; s3 = t4 + s3 - t2; y[0] = s4 - s7; y[1] = s2 + s1; y[2] = s0 - s3; y[3] = s8 + s5; y[5] = s8 - s5; y[6] = s0 + s3; y[7] = s2 - s1; y[8] = s4 + s7; } static void drmp3_L3_imdct36(float *grbuf, float *overlap, const float *window, int nbands) { int i, j; static const float g_twid9[18] = { 0.73727734f,0.79335334f,0.84339145f,0.88701083f,0.92387953f,0.95371695f,0.97629601f,0.99144486f,0.99904822f,0.67559021f,0.60876143f,0.53729961f,0.46174861f,0.38268343f,0.30070580f,0.21643961f,0.13052619f,0.04361938f }; for (j = 0; j < nbands; j++, grbuf += 18, overlap += 9) { float co[9], si[9]; co[0] = -grbuf[0]; si[0] = grbuf[17]; for (i = 0; i < 4; i++) { si[8 - 2*i] = grbuf[4*i + 1] - grbuf[4*i + 2]; co[1 + 2*i] = grbuf[4*i + 1] + grbuf[4*i + 2]; si[7 - 2*i] = grbuf[4*i + 4] - grbuf[4*i + 3]; co[2 + 2*i] = -(grbuf[4*i + 3] + grbuf[4*i + 4]); } drmp3_L3_dct3_9(co); drmp3_L3_dct3_9(si); si[1] = -si[1]; si[3] = -si[3]; si[5] = -si[5]; si[7] = -si[7]; i = 0; #if DRMP3_HAVE_SIMD if (drmp3_have_simd()) for (; i < 8; i += 4) { drmp3_f4 vovl = DRMP3_VLD(overlap + i); drmp3_f4 vc = DRMP3_VLD(co + i); drmp3_f4 vs = DRMP3_VLD(si + i); drmp3_f4 vr0 = DRMP3_VLD(g_twid9 + i); drmp3_f4 vr1 = DRMP3_VLD(g_twid9 + 9 + i); drmp3_f4 vw0 = DRMP3_VLD(window + i); drmp3_f4 vw1 = DRMP3_VLD(window + 9 + i); drmp3_f4 vsum = DRMP3_VADD(DRMP3_VMUL(vc, vr1), DRMP3_VMUL(vs, vr0)); DRMP3_VSTORE(overlap + i, DRMP3_VSUB(DRMP3_VMUL(vc, vr0), DRMP3_VMUL(vs, vr1))); DRMP3_VSTORE(grbuf + i, DRMP3_VSUB(DRMP3_VMUL(vovl, vw0), DRMP3_VMUL(vsum, vw1))); vsum = DRMP3_VADD(DRMP3_VMUL(vovl, vw1), DRMP3_VMUL(vsum, vw0)); DRMP3_VSTORE(grbuf + 14 - i, DRMP3_VREV(vsum)); } #endif for (; i < 9; i++) { float ovl = overlap[i]; float sum = co[i]*g_twid9[9 + i] + si[i]*g_twid9[0 + i]; overlap[i] = co[i]*g_twid9[0 + i] - si[i]*g_twid9[9 + i]; grbuf[i] = ovl*window[0 + i] - sum*window[9 + i]; grbuf[17 - i] = ovl*window[9 + i] + sum*window[0 + i]; } } } static void drmp3_L3_idct3(float x0, float x1, float x2, float *dst) { float m1 = x1*0.86602540f; float a1 = x0 - x2*0.5f; dst[1] = x0 + x2; dst[0] = a1 + m1; dst[2] = a1 - m1; } static void drmp3_L3_imdct12(float *x, float *dst, float *overlap) { static const float g_twid3[6] = { 0.79335334f,0.92387953f,0.99144486f, 0.60876143f,0.38268343f,0.13052619f }; float co[3], si[3]; int i; drmp3_L3_idct3(-x[0], x[6] + x[3], x[12] + x[9], co); drmp3_L3_idct3(x[15], x[12] - x[9], x[6] - x[3], si); si[1] = -si[1]; for (i = 0; i < 3; i++) { float ovl = overlap[i]; float sum = co[i]*g_twid3[3 + i] + si[i]*g_twid3[0 + i]; overlap[i] = co[i]*g_twid3[0 + i] - si[i]*g_twid3[3 + i]; dst[i] = ovl*g_twid3[2 - i] - sum*g_twid3[5 - i]; dst[5 - i] = ovl*g_twid3[5 - i] + sum*g_twid3[2 - i]; } } static void drmp3_L3_imdct_short(float *grbuf, float *overlap, int nbands) { for (;nbands > 0; nbands--, overlap += 9, grbuf += 18) { float tmp[18]; DRMP3_COPY_MEMORY(tmp, grbuf, sizeof(tmp)); DRMP3_COPY_MEMORY(grbuf, overlap, 6*sizeof(float)); drmp3_L3_imdct12(tmp, grbuf + 6, overlap + 6); drmp3_L3_imdct12(tmp + 1, grbuf + 12, overlap + 6); drmp3_L3_imdct12(tmp + 2, overlap, overlap + 6); } } static void drmp3_L3_change_sign(float *grbuf) { int b, i; for (b = 0, grbuf += 18; b < 32; b += 2, grbuf += 36) for (i = 1; i < 18; i += 2) grbuf[i] = -grbuf[i]; } static void drmp3_L3_imdct_gr(float *grbuf, float *overlap, unsigned block_type, unsigned n_long_bands) { static const float g_mdct_window[2][18] = { { 0.99904822f,0.99144486f,0.97629601f,0.95371695f,0.92387953f,0.88701083f,0.84339145f,0.79335334f,0.73727734f,0.04361938f,0.13052619f,0.21643961f,0.30070580f,0.38268343f,0.46174861f,0.53729961f,0.60876143f,0.67559021f }, { 1,1,1,1,1,1,0.99144486f,0.92387953f,0.79335334f,0,0,0,0,0,0,0.13052619f,0.38268343f,0.60876143f } }; if (n_long_bands) { drmp3_L3_imdct36(grbuf, overlap, g_mdct_window[0], n_long_bands); grbuf += 18*n_long_bands; overlap += 9*n_long_bands; } if (block_type == DRMP3_SHORT_BLOCK_TYPE) drmp3_L3_imdct_short(grbuf, overlap, 32 - n_long_bands); else drmp3_L3_imdct36(grbuf, overlap, g_mdct_window[block_type == DRMP3_STOP_BLOCK_TYPE], 32 - n_long_bands); } static void drmp3_L3_save_reservoir(drmp3dec *h, drmp3dec_scratch *s) { int pos = (s->bs.pos + 7)/8u; int remains = s->bs.limit/8u - pos; if (remains > DRMP3_MAX_BITRESERVOIR_BYTES) { pos += remains - DRMP3_MAX_BITRESERVOIR_BYTES; remains = DRMP3_MAX_BITRESERVOIR_BYTES; } if (remains > 0) { DRMP3_MOVE_MEMORY(h->reserv_buf, s->maindata + pos, remains); } h->reserv = remains; } static int drmp3_L3_restore_reservoir(drmp3dec *h, drmp3_bs *bs, drmp3dec_scratch *s, int main_data_begin) { int frame_bytes = (bs->limit - bs->pos)/8; int bytes_have = DRMP3_MIN(h->reserv, main_data_begin); DRMP3_COPY_MEMORY(s->maindata, h->reserv_buf + DRMP3_MAX(0, h->reserv - main_data_begin), DRMP3_MIN(h->reserv, main_data_begin)); DRMP3_COPY_MEMORY(s->maindata + bytes_have, bs->buf + bs->pos/8, frame_bytes); drmp3_bs_init(&s->bs, s->maindata, bytes_have + frame_bytes); return h->reserv >= main_data_begin; } static void drmp3_L3_decode(drmp3dec *h, drmp3dec_scratch *s, drmp3_L3_gr_info *gr_info, int nch) { int ch; for (ch = 0; ch < nch; ch++) { int layer3gr_limit = s->bs.pos + gr_info[ch].part_23_length; drmp3_L3_decode_scalefactors(h->header, s->ist_pos[ch], &s->bs, gr_info + ch, s->scf, ch); drmp3_L3_huffman(s->grbuf[ch], &s->bs, gr_info + ch, s->scf, layer3gr_limit); } if (DRMP3_HDR_TEST_I_STEREO(h->header)) { drmp3_L3_intensity_stereo(s->grbuf[0], s->ist_pos[1], gr_info, h->header); } else if (DRMP3_HDR_IS_MS_STEREO(h->header)) { drmp3_L3_midside_stereo(s->grbuf[0], 576); } for (ch = 0; ch < nch; ch++, gr_info++) { int aa_bands = 31; int n_long_bands = (gr_info->mixed_block_flag ? 2 : 0) << (int)(DRMP3_HDR_GET_MY_SAMPLE_RATE(h->header) == 2); if (gr_info->n_short_sfb) { aa_bands = n_long_bands - 1; drmp3_L3_reorder(s->grbuf[ch] + n_long_bands*18, s->syn[0], gr_info->sfbtab + gr_info->n_long_sfb); } drmp3_L3_antialias(s->grbuf[ch], aa_bands); drmp3_L3_imdct_gr(s->grbuf[ch], h->mdct_overlap[ch], gr_info->block_type, n_long_bands); drmp3_L3_change_sign(s->grbuf[ch]); } } static void drmp3d_DCT_II(float *grbuf, int n) { static const float g_sec[24] = { 10.19000816f,0.50060302f,0.50241929f,3.40760851f,0.50547093f,0.52249861f,2.05778098f,0.51544732f,0.56694406f,1.48416460f,0.53104258f,0.64682180f,1.16943991f,0.55310392f,0.78815460f,0.97256821f,0.58293498f,1.06067765f,0.83934963f,0.62250412f,1.72244716f,0.74453628f,0.67480832f,5.10114861f }; int i, k = 0; #if DRMP3_HAVE_SIMD if (drmp3_have_simd()) for (; k < n; k += 4) { drmp3_f4 t[4][8], *x; float *y = grbuf + k; for (x = t[0], i = 0; i < 8; i++, x++) { drmp3_f4 x0 = DRMP3_VLD(&y[i*18]); drmp3_f4 x1 = DRMP3_VLD(&y[(15 - i)*18]); drmp3_f4 x2 = DRMP3_VLD(&y[(16 + i)*18]); drmp3_f4 x3 = DRMP3_VLD(&y[(31 - i)*18]); drmp3_f4 t0 = DRMP3_VADD(x0, x3); drmp3_f4 t1 = DRMP3_VADD(x1, x2); drmp3_f4 t2 = DRMP3_VMUL_S(DRMP3_VSUB(x1, x2), g_sec[3*i + 0]); drmp3_f4 t3 = DRMP3_VMUL_S(DRMP3_VSUB(x0, x3), g_sec[3*i + 1]); x[0] = DRMP3_VADD(t0, t1); x[8] = DRMP3_VMUL_S(DRMP3_VSUB(t0, t1), g_sec[3*i + 2]); x[16] = DRMP3_VADD(t3, t2); x[24] = DRMP3_VMUL_S(DRMP3_VSUB(t3, t2), g_sec[3*i + 2]); } for (x = t[0], i = 0; i < 4; i++, x += 8) { drmp3_f4 x0 = x[0], x1 = x[1], x2 = x[2], x3 = x[3], x4 = x[4], x5 = x[5], x6 = x[6], x7 = x[7], xt; xt = DRMP3_VSUB(x0, x7); x0 = DRMP3_VADD(x0, x7); x7 = DRMP3_VSUB(x1, x6); x1 = DRMP3_VADD(x1, x6); x6 = DRMP3_VSUB(x2, x5); x2 = DRMP3_VADD(x2, x5); x5 = DRMP3_VSUB(x3, x4); x3 = DRMP3_VADD(x3, x4); x4 = DRMP3_VSUB(x0, x3); x0 = DRMP3_VADD(x0, x3); x3 = DRMP3_VSUB(x1, x2); x1 = DRMP3_VADD(x1, x2); x[0] = DRMP3_VADD(x0, x1); x[4] = DRMP3_VMUL_S(DRMP3_VSUB(x0, x1), 0.70710677f); x5 = DRMP3_VADD(x5, x6); x6 = DRMP3_VMUL_S(DRMP3_VADD(x6, x7), 0.70710677f); x7 = DRMP3_VADD(x7, xt); x3 = DRMP3_VMUL_S(DRMP3_VADD(x3, x4), 0.70710677f); x5 = DRMP3_VSUB(x5, DRMP3_VMUL_S(x7, 0.198912367f)); x7 = DRMP3_VADD(x7, DRMP3_VMUL_S(x5, 0.382683432f)); x5 = DRMP3_VSUB(x5, DRMP3_VMUL_S(x7, 0.198912367f)); x0 = DRMP3_VSUB(xt, x6); xt = DRMP3_VADD(xt, x6); x[1] = DRMP3_VMUL_S(DRMP3_VADD(xt, x7), 0.50979561f); x[2] = DRMP3_VMUL_S(DRMP3_VADD(x4, x3), 0.54119611f); x[3] = DRMP3_VMUL_S(DRMP3_VSUB(x0, x5), 0.60134488f); x[5] = DRMP3_VMUL_S(DRMP3_VADD(x0, x5), 0.89997619f); x[6] = DRMP3_VMUL_S(DRMP3_VSUB(x4, x3), 1.30656302f); x[7] = DRMP3_VMUL_S(DRMP3_VSUB(xt, x7), 2.56291556f); } if (k > n - 3) { #if DRMP3_HAVE_SSE #define DRMP3_VSAVE2(i, v) _mm_storel_pi((__m64 *)(void*)&y[i*18], v) #else #define DRMP3_VSAVE2(i, v) vst1_f32((float32_t *)&y[(i)*18], vget_low_f32(v)) #endif for (i = 0; i < 7; i++, y += 4*18) { drmp3_f4 s = DRMP3_VADD(t[3][i], t[3][i + 1]); DRMP3_VSAVE2(0, t[0][i]); DRMP3_VSAVE2(1, DRMP3_VADD(t[2][i], s)); DRMP3_VSAVE2(2, DRMP3_VADD(t[1][i], t[1][i + 1])); DRMP3_VSAVE2(3, DRMP3_VADD(t[2][1 + i], s)); } DRMP3_VSAVE2(0, t[0][7]); DRMP3_VSAVE2(1, DRMP3_VADD(t[2][7], t[3][7])); DRMP3_VSAVE2(2, t[1][7]); DRMP3_VSAVE2(3, t[3][7]); } else { #define DRMP3_VSAVE4(i, v) DRMP3_VSTORE(&y[(i)*18], v) for (i = 0; i < 7; i++, y += 4*18) { drmp3_f4 s = DRMP3_VADD(t[3][i], t[3][i + 1]); DRMP3_VSAVE4(0, t[0][i]); DRMP3_VSAVE4(1, DRMP3_VADD(t[2][i], s)); DRMP3_VSAVE4(2, DRMP3_VADD(t[1][i], t[1][i + 1])); DRMP3_VSAVE4(3, DRMP3_VADD(t[2][1 + i], s)); } DRMP3_VSAVE4(0, t[0][7]); DRMP3_VSAVE4(1, DRMP3_VADD(t[2][7], t[3][7])); DRMP3_VSAVE4(2, t[1][7]); DRMP3_VSAVE4(3, t[3][7]); } } else #endif #ifdef DR_MP3_ONLY_SIMD {} #else for (; k < n; k++) { float t[4][8], *x, *y = grbuf + k; for (x = t[0], i = 0; i < 8; i++, x++) { float x0 = y[i*18]; float x1 = y[(15 - i)*18]; float x2 = y[(16 + i)*18]; float x3 = y[(31 - i)*18]; float t0 = x0 + x3; float t1 = x1 + x2; float t2 = (x1 - x2)*g_sec[3*i + 0]; float t3 = (x0 - x3)*g_sec[3*i + 1]; x[0] = t0 + t1; x[8] = (t0 - t1)*g_sec[3*i + 2]; x[16] = t3 + t2; x[24] = (t3 - t2)*g_sec[3*i + 2]; } for (x = t[0], i = 0; i < 4; i++, x += 8) { float x0 = x[0], x1 = x[1], x2 = x[2], x3 = x[3], x4 = x[4], x5 = x[5], x6 = x[6], x7 = x[7], xt; xt = x0 - x7; x0 += x7; x7 = x1 - x6; x1 += x6; x6 = x2 - x5; x2 += x5; x5 = x3 - x4; x3 += x4; x4 = x0 - x3; x0 += x3; x3 = x1 - x2; x1 += x2; x[0] = x0 + x1; x[4] = (x0 - x1)*0.70710677f; x5 = x5 + x6; x6 = (x6 + x7)*0.70710677f; x7 = x7 + xt; x3 = (x3 + x4)*0.70710677f; x5 -= x7*0.198912367f; x7 += x5*0.382683432f; x5 -= x7*0.198912367f; x0 = xt - x6; xt += x6; x[1] = (xt + x7)*0.50979561f; x[2] = (x4 + x3)*0.54119611f; x[3] = (x0 - x5)*0.60134488f; x[5] = (x0 + x5)*0.89997619f; x[6] = (x4 - x3)*1.30656302f; x[7] = (xt - x7)*2.56291556f; } for (i = 0; i < 7; i++, y += 4*18) { y[0*18] = t[0][i]; y[1*18] = t[2][i] + t[3][i] + t[3][i + 1]; y[2*18] = t[1][i] + t[1][i + 1]; y[3*18] = t[2][i + 1] + t[3][i] + t[3][i + 1]; } y[0*18] = t[0][7]; y[1*18] = t[2][7] + t[3][7]; y[2*18] = t[1][7]; y[3*18] = t[3][7]; } #endif } #ifndef DR_MP3_FLOAT_OUTPUT typedef drmp3_int16 drmp3d_sample_t; static drmp3_int16 drmp3d_scale_pcm(float sample) { drmp3_int16 s; #if DRMP3_HAVE_ARMV6 drmp3_int32 s32 = (drmp3_int32)(sample + .5f); s32 -= (s32 < 0); s = (drmp3_int16)drmp3_clip_int16_arm(s32); #else if (sample >= 32766.5) return (drmp3_int16) 32767; if (sample <= -32767.5) return (drmp3_int16)-32768; s = (drmp3_int16)(sample + .5f); s -= (s < 0); #endif return s; } #else typedef float drmp3d_sample_t; static float drmp3d_scale_pcm(float sample) { return sample*(1.f/32768.f); } #endif static void drmp3d_synth_pair(drmp3d_sample_t *pcm, int nch, const float *z) { float a; a = (z[14*64] - z[ 0]) * 29; a += (z[ 1*64] + z[13*64]) * 213; a += (z[12*64] - z[ 2*64]) * 459; a += (z[ 3*64] + z[11*64]) * 2037; a += (z[10*64] - z[ 4*64]) * 5153; a += (z[ 5*64] + z[ 9*64]) * 6574; a += (z[ 8*64] - z[ 6*64]) * 37489; a += z[ 7*64] * 75038; pcm[0] = drmp3d_scale_pcm(a); z += 2; a = z[14*64] * 104; a += z[12*64] * 1567; a += z[10*64] * 9727; a += z[ 8*64] * 64019; a += z[ 6*64] * -9975; a += z[ 4*64] * -45; a += z[ 2*64] * 146; a += z[ 0*64] * -5; pcm[16*nch] = drmp3d_scale_pcm(a); } static void drmp3d_synth(float *xl, drmp3d_sample_t *dstl, int nch, float *lins) { int i; float *xr = xl + 576*(nch - 1); drmp3d_sample_t *dstr = dstl + (nch - 1); static const float g_win[] = { -1,26,-31,208,218,401,-519,2063,2000,4788,-5517,7134,5959,35640,-39336,74992, -1,24,-35,202,222,347,-581,2080,1952,4425,-5879,7640,5288,33791,-41176,74856, -1,21,-38,196,225,294,-645,2087,1893,4063,-6237,8092,4561,31947,-43006,74630, -1,19,-41,190,227,244,-711,2085,1822,3705,-6589,8492,3776,30112,-44821,74313, -1,17,-45,183,228,197,-779,2075,1739,3351,-6935,8840,2935,28289,-46617,73908, -1,16,-49,176,228,153,-848,2057,1644,3004,-7271,9139,2037,26482,-48390,73415, -2,14,-53,169,227,111,-919,2032,1535,2663,-7597,9389,1082,24694,-50137,72835, -2,13,-58,161,224,72,-991,2001,1414,2330,-7910,9592,70,22929,-51853,72169, -2,11,-63,154,221,36,-1064,1962,1280,2006,-8209,9750,-998,21189,-53534,71420, -2,10,-68,147,215,2,-1137,1919,1131,1692,-8491,9863,-2122,19478,-55178,70590, -3,9,-73,139,208,-29,-1210,1870,970,1388,-8755,9935,-3300,17799,-56778,69679, -3,8,-79,132,200,-57,-1283,1817,794,1095,-8998,9966,-4533,16155,-58333,68692, -4,7,-85,125,189,-83,-1356,1759,605,814,-9219,9959,-5818,14548,-59838,67629, -4,7,-91,117,177,-106,-1428,1698,402,545,-9416,9916,-7154,12980,-61289,66494, -5,6,-97,111,163,-127,-1498,1634,185,288,-9585,9838,-8540,11455,-62684,65290 }; float *zlin = lins + 15*64; const float *w = g_win; zlin[4*15] = xl[18*16]; zlin[4*15 + 1] = xr[18*16]; zlin[4*15 + 2] = xl[0]; zlin[4*15 + 3] = xr[0]; zlin[4*31] = xl[1 + 18*16]; zlin[4*31 + 1] = xr[1 + 18*16]; zlin[4*31 + 2] = xl[1]; zlin[4*31 + 3] = xr[1]; drmp3d_synth_pair(dstr, nch, lins + 4*15 + 1); drmp3d_synth_pair(dstr + 32*nch, nch, lins + 4*15 + 64 + 1); drmp3d_synth_pair(dstl, nch, lins + 4*15); drmp3d_synth_pair(dstl + 32*nch, nch, lins + 4*15 + 64); #if DRMP3_HAVE_SIMD if (drmp3_have_simd()) for (i = 14; i >= 0; i--) { #define DRMP3_VLOAD(k) drmp3_f4 w0 = DRMP3_VSET(*w++); drmp3_f4 w1 = DRMP3_VSET(*w++); drmp3_f4 vz = DRMP3_VLD(&zlin[4*i - 64*k]); drmp3_f4 vy = DRMP3_VLD(&zlin[4*i - 64*(15 - k)]); #define DRMP3_V0(k) { DRMP3_VLOAD(k) b = DRMP3_VADD(DRMP3_VMUL(vz, w1), DRMP3_VMUL(vy, w0)) ; a = DRMP3_VSUB(DRMP3_VMUL(vz, w0), DRMP3_VMUL(vy, w1)); } #define DRMP3_V1(k) { DRMP3_VLOAD(k) b = DRMP3_VADD(b, DRMP3_VADD(DRMP3_VMUL(vz, w1), DRMP3_VMUL(vy, w0))); a = DRMP3_VADD(a, DRMP3_VSUB(DRMP3_VMUL(vz, w0), DRMP3_VMUL(vy, w1))); } #define DRMP3_V2(k) { DRMP3_VLOAD(k) b = DRMP3_VADD(b, DRMP3_VADD(DRMP3_VMUL(vz, w1), DRMP3_VMUL(vy, w0))); a = DRMP3_VADD(a, DRMP3_VSUB(DRMP3_VMUL(vy, w1), DRMP3_VMUL(vz, w0))); } drmp3_f4 a, b; zlin[4*i] = xl[18*(31 - i)]; zlin[4*i + 1] = xr[18*(31 - i)]; zlin[4*i + 2] = xl[1 + 18*(31 - i)]; zlin[4*i + 3] = xr[1 + 18*(31 - i)]; zlin[4*i + 64] = xl[1 + 18*(1 + i)]; zlin[4*i + 64 + 1] = xr[1 + 18*(1 + i)]; zlin[4*i - 64 + 2] = xl[18*(1 + i)]; zlin[4*i - 64 + 3] = xr[18*(1 + i)]; DRMP3_V0(0) DRMP3_V2(1) DRMP3_V1(2) DRMP3_V2(3) DRMP3_V1(4) DRMP3_V2(5) DRMP3_V1(6) DRMP3_V2(7) { #ifndef DR_MP3_FLOAT_OUTPUT #if DRMP3_HAVE_SSE static const drmp3_f4 g_max = { 32767.0f, 32767.0f, 32767.0f, 32767.0f }; static const drmp3_f4 g_min = { -32768.0f, -32768.0f, -32768.0f, -32768.0f }; __m128i pcm8 = _mm_packs_epi32(_mm_cvtps_epi32(_mm_max_ps(_mm_min_ps(a, g_max), g_min)), _mm_cvtps_epi32(_mm_max_ps(_mm_min_ps(b, g_max), g_min))); dstr[(15 - i)*nch] = (drmp3_int16)_mm_extract_epi16(pcm8, 1); dstr[(17 + i)*nch] = (drmp3_int16)_mm_extract_epi16(pcm8, 5); dstl[(15 - i)*nch] = (drmp3_int16)_mm_extract_epi16(pcm8, 0); dstl[(17 + i)*nch] = (drmp3_int16)_mm_extract_epi16(pcm8, 4); dstr[(47 - i)*nch] = (drmp3_int16)_mm_extract_epi16(pcm8, 3); dstr[(49 + i)*nch] = (drmp3_int16)_mm_extract_epi16(pcm8, 7); dstl[(47 - i)*nch] = (drmp3_int16)_mm_extract_epi16(pcm8, 2); dstl[(49 + i)*nch] = (drmp3_int16)_mm_extract_epi16(pcm8, 6); #else int16x4_t pcma, pcmb; a = DRMP3_VADD(a, DRMP3_VSET(0.5f)); b = DRMP3_VADD(b, DRMP3_VSET(0.5f)); pcma = vqmovn_s32(vqaddq_s32(vcvtq_s32_f32(a), vreinterpretq_s32_u32(vcltq_f32(a, DRMP3_VSET(0))))); pcmb = vqmovn_s32(vqaddq_s32(vcvtq_s32_f32(b), vreinterpretq_s32_u32(vcltq_f32(b, DRMP3_VSET(0))))); vst1_lane_s16(dstr + (15 - i)*nch, pcma, 1); vst1_lane_s16(dstr + (17 + i)*nch, pcmb, 1); vst1_lane_s16(dstl + (15 - i)*nch, pcma, 0); vst1_lane_s16(dstl + (17 + i)*nch, pcmb, 0); vst1_lane_s16(dstr + (47 - i)*nch, pcma, 3); vst1_lane_s16(dstr + (49 + i)*nch, pcmb, 3); vst1_lane_s16(dstl + (47 - i)*nch, pcma, 2); vst1_lane_s16(dstl + (49 + i)*nch, pcmb, 2); #endif #else #if DRMP3_HAVE_SSE static const drmp3_f4 g_scale = { 1.0f/32768.0f, 1.0f/32768.0f, 1.0f/32768.0f, 1.0f/32768.0f }; #else const drmp3_f4 g_scale = vdupq_n_f32(1.0f/32768.0f); #endif a = DRMP3_VMUL(a, g_scale); b = DRMP3_VMUL(b, g_scale); #if DRMP3_HAVE_SSE _mm_store_ss(dstr + (15 - i)*nch, _mm_shuffle_ps(a, a, _MM_SHUFFLE(1, 1, 1, 1))); _mm_store_ss(dstr + (17 + i)*nch, _mm_shuffle_ps(b, b, _MM_SHUFFLE(1, 1, 1, 1))); _mm_store_ss(dstl + (15 - i)*nch, _mm_shuffle_ps(a, a, _MM_SHUFFLE(0, 0, 0, 0))); _mm_store_ss(dstl + (17 + i)*nch, _mm_shuffle_ps(b, b, _MM_SHUFFLE(0, 0, 0, 0))); _mm_store_ss(dstr + (47 - i)*nch, _mm_shuffle_ps(a, a, _MM_SHUFFLE(3, 3, 3, 3))); _mm_store_ss(dstr + (49 + i)*nch, _mm_shuffle_ps(b, b, _MM_SHUFFLE(3, 3, 3, 3))); _mm_store_ss(dstl + (47 - i)*nch, _mm_shuffle_ps(a, a, _MM_SHUFFLE(2, 2, 2, 2))); _mm_store_ss(dstl + (49 + i)*nch, _mm_shuffle_ps(b, b, _MM_SHUFFLE(2, 2, 2, 2))); #else vst1q_lane_f32(dstr + (15 - i)*nch, a, 1); vst1q_lane_f32(dstr + (17 + i)*nch, b, 1); vst1q_lane_f32(dstl + (15 - i)*nch, a, 0); vst1q_lane_f32(dstl + (17 + i)*nch, b, 0); vst1q_lane_f32(dstr + (47 - i)*nch, a, 3); vst1q_lane_f32(dstr + (49 + i)*nch, b, 3); vst1q_lane_f32(dstl + (47 - i)*nch, a, 2); vst1q_lane_f32(dstl + (49 + i)*nch, b, 2); #endif #endif } } else #endif #ifdef DR_MP3_ONLY_SIMD {} #else for (i = 14; i >= 0; i--) { #define DRMP3_LOAD(k) float w0 = *w++; float w1 = *w++; float *vz = &zlin[4*i - k*64]; float *vy = &zlin[4*i - (15 - k)*64]; #define DRMP3_S0(k) { int j; DRMP3_LOAD(k); for (j = 0; j < 4; j++) b[j] = vz[j]*w1 + vy[j]*w0, a[j] = vz[j]*w0 - vy[j]*w1; } #define DRMP3_S1(k) { int j; DRMP3_LOAD(k); for (j = 0; j < 4; j++) b[j] += vz[j]*w1 + vy[j]*w0, a[j] += vz[j]*w0 - vy[j]*w1; } #define DRMP3_S2(k) { int j; DRMP3_LOAD(k); for (j = 0; j < 4; j++) b[j] += vz[j]*w1 + vy[j]*w0, a[j] += vy[j]*w1 - vz[j]*w0; } float a[4], b[4]; zlin[4*i] = xl[18*(31 - i)]; zlin[4*i + 1] = xr[18*(31 - i)]; zlin[4*i + 2] = xl[1 + 18*(31 - i)]; zlin[4*i + 3] = xr[1 + 18*(31 - i)]; zlin[4*(i + 16)] = xl[1 + 18*(1 + i)]; zlin[4*(i + 16) + 1] = xr[1 + 18*(1 + i)]; zlin[4*(i - 16) + 2] = xl[18*(1 + i)]; zlin[4*(i - 16) + 3] = xr[18*(1 + i)]; DRMP3_S0(0) DRMP3_S2(1) DRMP3_S1(2) DRMP3_S2(3) DRMP3_S1(4) DRMP3_S2(5) DRMP3_S1(6) DRMP3_S2(7) dstr[(15 - i)*nch] = drmp3d_scale_pcm(a[1]); dstr[(17 + i)*nch] = drmp3d_scale_pcm(b[1]); dstl[(15 - i)*nch] = drmp3d_scale_pcm(a[0]); dstl[(17 + i)*nch] = drmp3d_scale_pcm(b[0]); dstr[(47 - i)*nch] = drmp3d_scale_pcm(a[3]); dstr[(49 + i)*nch] = drmp3d_scale_pcm(b[3]); dstl[(47 - i)*nch] = drmp3d_scale_pcm(a[2]); dstl[(49 + i)*nch] = drmp3d_scale_pcm(b[2]); } #endif } static void drmp3d_synth_granule(float *qmf_state, float *grbuf, int nbands, int nch, drmp3d_sample_t *pcm, float *lins) { int i; for (i = 0; i < nch; i++) { drmp3d_DCT_II(grbuf + 576*i, nbands); } DRMP3_COPY_MEMORY(lins, qmf_state, sizeof(float)*15*64); for (i = 0; i < nbands; i += 2) { drmp3d_synth(grbuf + i, pcm + 32*nch*i, nch, lins + i*64); } #ifndef DR_MP3_NONSTANDARD_BUT_LOGICAL if (nch == 1) { for (i = 0; i < 15*64; i += 2) { qmf_state[i] = lins[nbands*64 + i]; } } else #endif { DRMP3_COPY_MEMORY(qmf_state, lins + nbands*64, sizeof(float)*15*64); } } static int drmp3d_match_frame(const drmp3_uint8 *hdr, int mp3_bytes, int frame_bytes) { int i, nmatch; for (i = 0, nmatch = 0; nmatch < DRMP3_MAX_FRAME_SYNC_MATCHES; nmatch++) { i += drmp3_hdr_frame_bytes(hdr + i, frame_bytes) + drmp3_hdr_padding(hdr + i); if (i + DRMP3_HDR_SIZE > mp3_bytes) return nmatch > 0; if (!drmp3_hdr_compare(hdr, hdr + i)) return 0; } return 1; } static int drmp3d_find_frame(const drmp3_uint8 *mp3, int mp3_bytes, int *free_format_bytes, int *ptr_frame_bytes) { int i, k; for (i = 0; i < mp3_bytes - DRMP3_HDR_SIZE; i++, mp3++) { if (drmp3_hdr_valid(mp3)) { int frame_bytes = drmp3_hdr_frame_bytes(mp3, *free_format_bytes); int frame_and_padding = frame_bytes + drmp3_hdr_padding(mp3); for (k = DRMP3_HDR_SIZE; !frame_bytes && k < DRMP3_MAX_FREE_FORMAT_FRAME_SIZE && i + 2*k < mp3_bytes - DRMP3_HDR_SIZE; k++) { if (drmp3_hdr_compare(mp3, mp3 + k)) { int fb = k - drmp3_hdr_padding(mp3); int nextfb = fb + drmp3_hdr_padding(mp3 + k); if (i + k + nextfb + DRMP3_HDR_SIZE > mp3_bytes || !drmp3_hdr_compare(mp3, mp3 + k + nextfb)) continue; frame_and_padding = k; frame_bytes = fb; *free_format_bytes = fb; } } if ((frame_bytes && i + frame_and_padding <= mp3_bytes && drmp3d_match_frame(mp3, mp3_bytes - i, frame_bytes)) || (!i && frame_and_padding == mp3_bytes)) { *ptr_frame_bytes = frame_and_padding; return i; } *free_format_bytes = 0; } } *ptr_frame_bytes = 0; return mp3_bytes; } DRMP3_API void drmp3dec_init(drmp3dec *dec) { dec->header[0] = 0; } DRMP3_API int drmp3dec_decode_frame(drmp3dec *dec, const drmp3_uint8 *mp3, int mp3_bytes, void *pcm, drmp3dec_frame_info *info) { 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)) { frame_size = drmp3_hdr_frame_bytes(mp3, dec->free_format_bytes) + drmp3_hdr_padding(mp3); if (frame_size != mp3_bytes && (frame_size + DRMP3_HDR_SIZE > mp3_bytes || !drmp3_hdr_compare(mp3, mp3 + frame_size))) { frame_size = 0; } } if (!frame_size) { DRMP3_ZERO_MEMORY(dec, sizeof(drmp3dec)); i = drmp3d_find_frame(mp3, mp3_bytes, &dec->free_format_bytes, &frame_size); if (!frame_size || i + frame_size > mp3_bytes) { info->frame_bytes = i; return 0; } } hdr = mp3 + i; DRMP3_COPY_MEMORY(dec->header, hdr, DRMP3_HDR_SIZE); info->frame_bytes = i + frame_size; info->channels = DRMP3_HDR_IS_MONO(hdr) ? 1 : 2; info->hz = drmp3_hdr_sample_rate_hz(hdr); info->layer = 4 - DRMP3_HDR_GET_LAYER(hdr); info->bitrate_kbps = drmp3_hdr_bitrate_kbps(hdr); drmp3_bs_init(bs_frame, hdr + DRMP3_HDR_SIZE, frame_size - DRMP3_HDR_SIZE); if (DRMP3_HDR_IS_CRC(hdr)) { drmp3_bs_get_bits(bs_frame, 16); } if (info->layer == 3) { int main_data_begin = drmp3_L3_read_side_info(bs_frame, 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); 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_L3_save_reservoir(dec, &scratch); } else { #ifdef DR_MP3_ONLY_MP3 return 0; #else drmp3_L12_scale_info sci[1]; if (pcm == NULL) { return drmp3_hdr_frame_samples(hdr); } drmp3_L12_read_scale_info(hdr, bs_frame, sci); DRMP3_ZERO_MEMORY(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))) { 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)); pcm = DRMP3_OFFSET_PTR(pcm, sizeof(drmp3d_sample_t)*384*info->channels); } if (bs_frame->pos > bs_frame->limit) { drmp3dec_init(dec); return 0; } } #endif } return success*drmp3_hdr_frame_samples(dec->header); } DRMP3_API void drmp3dec_f32_to_s16(const float *in, drmp3_int16 *out, size_t num_samples) { size_t i = 0; #if DRMP3_HAVE_SIMD size_t aligned_count = num_samples & ~7; for(; i < aligned_count; i+=8) { drmp3_f4 scale = DRMP3_VSET(32768.0f); drmp3_f4 a = DRMP3_VMUL(DRMP3_VLD(&in[i ]), scale); drmp3_f4 b = DRMP3_VMUL(DRMP3_VLD(&in[i+4]), scale); #if DRMP3_HAVE_SSE drmp3_f4 s16max = DRMP3_VSET( 32767.0f); drmp3_f4 s16min = DRMP3_VSET(-32768.0f); __m128i pcm8 = _mm_packs_epi32(_mm_cvtps_epi32(_mm_max_ps(_mm_min_ps(a, s16max), s16min)), _mm_cvtps_epi32(_mm_max_ps(_mm_min_ps(b, s16max), s16min))); out[i ] = (drmp3_int16)_mm_extract_epi16(pcm8, 0); out[i+1] = (drmp3_int16)_mm_extract_epi16(pcm8, 1); out[i+2] = (drmp3_int16)_mm_extract_epi16(pcm8, 2); out[i+3] = (drmp3_int16)_mm_extract_epi16(pcm8, 3); out[i+4] = (drmp3_int16)_mm_extract_epi16(pcm8, 4); out[i+5] = (drmp3_int16)_mm_extract_epi16(pcm8, 5); out[i+6] = (drmp3_int16)_mm_extract_epi16(pcm8, 6); out[i+7] = (drmp3_int16)_mm_extract_epi16(pcm8, 7); #else int16x4_t pcma, pcmb; a = DRMP3_VADD(a, DRMP3_VSET(0.5f)); b = DRMP3_VADD(b, DRMP3_VSET(0.5f)); pcma = vqmovn_s32(vqaddq_s32(vcvtq_s32_f32(a), vreinterpretq_s32_u32(vcltq_f32(a, DRMP3_VSET(0))))); pcmb = vqmovn_s32(vqaddq_s32(vcvtq_s32_f32(b), vreinterpretq_s32_u32(vcltq_f32(b, DRMP3_VSET(0))))); vst1_lane_s16(out+i , pcma, 0); vst1_lane_s16(out+i+1, pcma, 1); vst1_lane_s16(out+i+2, pcma, 2); vst1_lane_s16(out+i+3, pcma, 3); vst1_lane_s16(out+i+4, pcmb, 0); vst1_lane_s16(out+i+5, pcmb, 1); vst1_lane_s16(out+i+6, pcmb, 2); vst1_lane_s16(out+i+7, pcmb, 3); #endif } #endif for(; i < num_samples; i++) { float sample = in[i] * 32768.0f; if (sample >= 32766.5) out[i] = (drmp3_int16) 32767; else if (sample <= -32767.5) out[i] = (drmp3_int16)-32768; else { short s = (drmp3_int16)(sample + .5f); s -= (s < 0); out[i] = s; } } } #if defined(SIZE_MAX) #define DRMP3_SIZE_MAX SIZE_MAX #else #if defined(_WIN64) || defined(_LP64) || defined(__LP64__) #define DRMP3_SIZE_MAX ((drmp3_uint64)0xFFFFFFFFFFFFFFFF) #else #define DRMP3_SIZE_MAX 0xFFFFFFFF #endif #endif #ifndef DRMP3_SEEK_LEADING_MP3_FRAMES #define DRMP3_SEEK_LEADING_MP3_FRAMES 2 #endif #define DRMP3_MIN_DATA_CHUNK_SIZE 16384 #ifndef DRMP3_DATA_CHUNK_SIZE #define DRMP3_DATA_CHUNK_SIZE (DRMP3_MIN_DATA_CHUNK_SIZE*4) #endif #define DRMP3_COUNTOF(x) (sizeof(x) / sizeof(x[0])) #define DRMP3_CLAMP(x, lo, hi) (DRMP3_MAX(lo, DRMP3_MIN(x, hi))) #ifndef DRMP3_PI_D #define DRMP3_PI_D 3.14159265358979323846264 #endif #define DRMP3_DEFAULT_RESAMPLER_LPF_ORDER 2 static DRMP3_INLINE float drmp3_mix_f32(float x, float y, float a) { return x*(1-a) + y*a; } static DRMP3_INLINE float drmp3_mix_f32_fast(float x, float y, float a) { float r0 = (y - x); float r1 = r0*a; return x + r1; } static DRMP3_INLINE drmp3_uint32 drmp3_gcf_u32(drmp3_uint32 a, drmp3_uint32 b) { for (;;) { if (b == 0) { break; } else { drmp3_uint32 t = a; a = b; b = t % a; } } return a; } static void* drmp3__malloc_default(size_t sz, void* pUserData) { (void)pUserData; return DRMP3_MALLOC(sz); } static void* drmp3__realloc_default(void* p, size_t sz, void* pUserData) { (void)pUserData; return DRMP3_REALLOC(p, sz); } static void drmp3__free_default(void* p, void* pUserData) { (void)pUserData; DRMP3_FREE(p); } static void* drmp3__malloc_from_callbacks(size_t sz, const drmp3_allocation_callbacks* pAllocationCallbacks) { if (pAllocationCallbacks == NULL) { return NULL; } if (pAllocationCallbacks->onMalloc != NULL) { return pAllocationCallbacks->onMalloc(sz, pAllocationCallbacks->pUserData); } if (pAllocationCallbacks->onRealloc != NULL) { return pAllocationCallbacks->onRealloc(NULL, sz, pAllocationCallbacks->pUserData); } return NULL; } static void* drmp3__realloc_from_callbacks(void* p, size_t szNew, size_t szOld, const drmp3_allocation_callbacks* pAllocationCallbacks) { if (pAllocationCallbacks == NULL) { return NULL; } if (pAllocationCallbacks->onRealloc != NULL) { return pAllocationCallbacks->onRealloc(p, szNew, pAllocationCallbacks->pUserData); } if (pAllocationCallbacks->onMalloc != NULL && pAllocationCallbacks->onFree != NULL) { void* p2; p2 = pAllocationCallbacks->onMalloc(szNew, pAllocationCallbacks->pUserData); if (p2 == NULL) { return NULL; } if (p != NULL) { DRMP3_COPY_MEMORY(p2, p, szOld); pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData); } return p2; } return NULL; } static void drmp3__free_from_callbacks(void* p, const drmp3_allocation_callbacks* pAllocationCallbacks) { if (p == NULL || pAllocationCallbacks == NULL) { return; } if (pAllocationCallbacks->onFree != NULL) { pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData); } } static drmp3_allocation_callbacks drmp3_copy_allocation_callbacks_or_defaults(const drmp3_allocation_callbacks* pAllocationCallbacks) { if (pAllocationCallbacks != NULL) { return *pAllocationCallbacks; } else { drmp3_allocation_callbacks allocationCallbacks; allocationCallbacks.pUserData = NULL; allocationCallbacks.onMalloc = drmp3__malloc_default; allocationCallbacks.onRealloc = drmp3__realloc_default; allocationCallbacks.onFree = drmp3__free_default; return allocationCallbacks; } } static size_t drmp3__on_read(drmp3* pMP3, void* pBufferOut, size_t bytesToRead) { size_t bytesRead = pMP3->onRead(pMP3->pUserData, pBufferOut, bytesToRead); pMP3->streamCursor += bytesRead; return bytesRead; } static drmp3_bool32 drmp3__on_seek(drmp3* pMP3, int offset, drmp3_seek_origin origin) { DRMP3_ASSERT(offset >= 0); if (!pMP3->onSeek(pMP3->pUserData, offset, origin)) { return DRMP3_FALSE; } if (origin == drmp3_seek_origin_start) { pMP3->streamCursor = (drmp3_uint64)offset; } else { pMP3->streamCursor += offset; } return DRMP3_TRUE; } static drmp3_bool32 drmp3__on_seek_64(drmp3* pMP3, drmp3_uint64 offset, drmp3_seek_origin origin) { if (offset <= 0x7FFFFFFF) { return drmp3__on_seek(pMP3, (int)offset, origin); } if (!drmp3__on_seek(pMP3, 0x7FFFFFFF, drmp3_seek_origin_start)) { return DRMP3_FALSE; } offset -= 0x7FFFFFFF; while (offset > 0) { if (offset <= 0x7FFFFFFF) { if (!drmp3__on_seek(pMP3, (int)offset, drmp3_seek_origin_current)) { return DRMP3_FALSE; } offset = 0; } else { if (!drmp3__on_seek(pMP3, 0x7FFFFFFF, drmp3_seek_origin_current)) { return DRMP3_FALSE; } offset -= 0x7FFFFFFF; } } return DRMP3_TRUE; } static drmp3_uint32 drmp3_decode_next_frame_ex__callbacks(drmp3* pMP3, drmp3d_sample_t* pPCMFrames) { drmp3_uint32 pcmFramesRead = 0; DRMP3_ASSERT(pMP3 != NULL); DRMP3_ASSERT(pMP3->onRead != NULL); if (pMP3->atEnd) { return 0; } for (;;) { drmp3dec_frame_info info; if (pMP3->dataSize < DRMP3_MIN_DATA_CHUNK_SIZE) { size_t bytesRead; if (pMP3->pData != NULL) { DRMP3_MOVE_MEMORY(pMP3->pData, pMP3->pData + pMP3->dataConsumed, pMP3->dataSize); } pMP3->dataConsumed = 0; if (pMP3->dataCapacity < DRMP3_DATA_CHUNK_SIZE) { drmp3_uint8* pNewData; size_t newDataCap; newDataCap = DRMP3_DATA_CHUNK_SIZE; pNewData = (drmp3_uint8*)drmp3__realloc_from_callbacks(pMP3->pData, newDataCap, pMP3->dataCapacity, &pMP3->allocationCallbacks); if (pNewData == NULL) { return 0; } pMP3->pData = pNewData; pMP3->dataCapacity = newDataCap; } bytesRead = drmp3__on_read(pMP3, pMP3->pData + pMP3->dataSize, (pMP3->dataCapacity - pMP3->dataSize)); if (bytesRead == 0) { if (pMP3->dataSize == 0) { pMP3->atEnd = DRMP3_TRUE; return 0; } } pMP3->dataSize += bytesRead; } if (pMP3->dataSize > INT_MAX) { pMP3->atEnd = DRMP3_TRUE; return 0; } DRMP3_ASSERT(pMP3->pData != NULL); DRMP3_ASSERT(pMP3->dataCapacity > 0); pcmFramesRead = drmp3dec_decode_frame(&pMP3->decoder, pMP3->pData + pMP3->dataConsumed, (int)pMP3->dataSize, pPCMFrames, &info); if (info.frame_bytes > 0) { pMP3->dataConsumed += (size_t)info.frame_bytes; pMP3->dataSize -= (size_t)info.frame_bytes; } if (pcmFramesRead > 0) { pcmFramesRead = drmp3_hdr_frame_samples(pMP3->decoder.header); pMP3->pcmFramesConsumedInMP3Frame = 0; pMP3->pcmFramesRemainingInMP3Frame = pcmFramesRead; pMP3->mp3FrameChannels = info.channels; pMP3->mp3FrameSampleRate = info.hz; break; } else if (info.frame_bytes == 0) { size_t bytesRead; DRMP3_MOVE_MEMORY(pMP3->pData, pMP3->pData + pMP3->dataConsumed, pMP3->dataSize); pMP3->dataConsumed = 0; if (pMP3->dataCapacity == pMP3->dataSize) { drmp3_uint8* pNewData; size_t newDataCap; newDataCap = pMP3->dataCapacity + DRMP3_DATA_CHUNK_SIZE; pNewData = (drmp3_uint8*)drmp3__realloc_from_callbacks(pMP3->pData, newDataCap, pMP3->dataCapacity, &pMP3->allocationCallbacks); if (pNewData == NULL) { return 0; } pMP3->pData = pNewData; pMP3->dataCapacity = newDataCap; } bytesRead = drmp3__on_read(pMP3, pMP3->pData + pMP3->dataSize, (pMP3->dataCapacity - pMP3->dataSize)); if (bytesRead == 0) { pMP3->atEnd = DRMP3_TRUE; return 0; } pMP3->dataSize += bytesRead; } }; return pcmFramesRead; } static drmp3_uint32 drmp3_decode_next_frame_ex__memory(drmp3* pMP3, drmp3d_sample_t* pPCMFrames) { drmp3_uint32 pcmFramesRead = 0; drmp3dec_frame_info info; DRMP3_ASSERT(pMP3 != NULL); DRMP3_ASSERT(pMP3->memory.pData != NULL); if (pMP3->atEnd) { return 0; } for (;;) { pcmFramesRead = drmp3dec_decode_frame(&pMP3->decoder, pMP3->memory.pData + pMP3->memory.currentReadPos, (int)(pMP3->memory.dataSize - pMP3->memory.currentReadPos), pPCMFrames, &info); if (pcmFramesRead > 0) { pcmFramesRead = drmp3_hdr_frame_samples(pMP3->decoder.header); pMP3->pcmFramesConsumedInMP3Frame = 0; pMP3->pcmFramesRemainingInMP3Frame = pcmFramesRead; pMP3->mp3FrameChannels = info.channels; pMP3->mp3FrameSampleRate = info.hz; break; } else if (info.frame_bytes > 0) { pMP3->memory.currentReadPos += (size_t)info.frame_bytes; } else { break; } } pMP3->memory.currentReadPos += (size_t)info.frame_bytes; return pcmFramesRead; } static drmp3_uint32 drmp3_decode_next_frame_ex(drmp3* pMP3, drmp3d_sample_t* pPCMFrames) { if (pMP3->memory.pData != NULL && pMP3->memory.dataSize > 0) { return drmp3_decode_next_frame_ex__memory(pMP3, pPCMFrames); } else { return drmp3_decode_next_frame_ex__callbacks(pMP3, pPCMFrames); } } static drmp3_uint32 drmp3_decode_next_frame(drmp3* pMP3) { DRMP3_ASSERT(pMP3 != NULL); return drmp3_decode_next_frame_ex(pMP3, (drmp3d_sample_t*)pMP3->pcmFrames); } #if 0 static drmp3_uint32 drmp3_seek_next_frame(drmp3* pMP3) { drmp3_uint32 pcmFrameCount; DRMP3_ASSERT(pMP3 != NULL); pcmFrameCount = drmp3_decode_next_frame_ex(pMP3, NULL); if (pcmFrameCount == 0) { return 0; } pMP3->currentPCMFrame += pcmFrameCount; pMP3->pcmFramesConsumedInMP3Frame = pcmFrameCount; pMP3->pcmFramesRemainingInMP3Frame = 0; return pcmFrameCount; } #endif static drmp3_bool32 drmp3_init_internal(drmp3* pMP3, drmp3_read_proc onRead, drmp3_seek_proc onSeek, void* pUserData, const drmp3_allocation_callbacks* pAllocationCallbacks) { DRMP3_ASSERT(pMP3 != NULL); DRMP3_ASSERT(onRead != NULL); drmp3dec_init(&pMP3->decoder); pMP3->onRead = onRead; pMP3->onSeek = onSeek; pMP3->pUserData = pUserData; pMP3->allocationCallbacks = drmp3_copy_allocation_callbacks_or_defaults(pAllocationCallbacks); if (pMP3->allocationCallbacks.onFree == NULL || (pMP3->allocationCallbacks.onMalloc == NULL && pMP3->allocationCallbacks.onRealloc == NULL)) { return DRMP3_FALSE; } if (drmp3_decode_next_frame(pMP3) == 0) { drmp3__free_from_callbacks(pMP3->pData, &pMP3->allocationCallbacks); return DRMP3_FALSE; } pMP3->channels = pMP3->mp3FrameChannels; pMP3->sampleRate = pMP3->mp3FrameSampleRate; return DRMP3_TRUE; } DRMP3_API drmp3_bool32 drmp3_init(drmp3* pMP3, drmp3_read_proc onRead, drmp3_seek_proc onSeek, void* pUserData, const drmp3_allocation_callbacks* pAllocationCallbacks) { if (pMP3 == NULL || onRead == NULL) { return DRMP3_FALSE; } DRMP3_ZERO_OBJECT(pMP3); return drmp3_init_internal(pMP3, onRead, onSeek, pUserData, pAllocationCallbacks); } static size_t drmp3__on_read_memory(void* pUserData, void* pBufferOut, size_t bytesToRead) { drmp3* pMP3 = (drmp3*)pUserData; size_t bytesRemaining; DRMP3_ASSERT(pMP3 != NULL); DRMP3_ASSERT(pMP3->memory.dataSize >= pMP3->memory.currentReadPos); bytesRemaining = pMP3->memory.dataSize - pMP3->memory.currentReadPos; if (bytesToRead > bytesRemaining) { bytesToRead = bytesRemaining; } if (bytesToRead > 0) { DRMP3_COPY_MEMORY(pBufferOut, pMP3->memory.pData + pMP3->memory.currentReadPos, bytesToRead); pMP3->memory.currentReadPos += bytesToRead; } return bytesToRead; } static drmp3_bool32 drmp3__on_seek_memory(void* pUserData, int byteOffset, drmp3_seek_origin origin) { drmp3* pMP3 = (drmp3*)pUserData; DRMP3_ASSERT(pMP3 != NULL); if (origin == drmp3_seek_origin_current) { if (byteOffset > 0) { if (pMP3->memory.currentReadPos + byteOffset > pMP3->memory.dataSize) { byteOffset = (int)(pMP3->memory.dataSize - pMP3->memory.currentReadPos); } } else { if (pMP3->memory.currentReadPos < (size_t)-byteOffset) { byteOffset = -(int)pMP3->memory.currentReadPos; } } pMP3->memory.currentReadPos += byteOffset; } else { if ((drmp3_uint32)byteOffset <= pMP3->memory.dataSize) { pMP3->memory.currentReadPos = byteOffset; } else { pMP3->memory.currentReadPos = pMP3->memory.dataSize; } } return DRMP3_TRUE; } DRMP3_API drmp3_bool32 drmp3_init_memory(drmp3* pMP3, const void* pData, size_t dataSize, const drmp3_allocation_callbacks* pAllocationCallbacks) { if (pMP3 == NULL) { return DRMP3_FALSE; } DRMP3_ZERO_OBJECT(pMP3); if (pData == NULL || dataSize == 0) { return DRMP3_FALSE; } pMP3->memory.pData = (const drmp3_uint8*)pData; pMP3->memory.dataSize = dataSize; pMP3->memory.currentReadPos = 0; return drmp3_init_internal(pMP3, drmp3__on_read_memory, drmp3__on_seek_memory, pMP3, pAllocationCallbacks); } #ifndef DR_MP3_NO_STDIO #include #include #include static drmp3_result drmp3_result_from_errno(int e) { switch (e) { case 0: return DRMP3_SUCCESS; #ifdef EPERM case EPERM: return DRMP3_INVALID_OPERATION; #endif #ifdef ENOENT case ENOENT: return DRMP3_DOES_NOT_EXIST; #endif #ifdef ESRCH case ESRCH: return DRMP3_DOES_NOT_EXIST; #endif #ifdef EINTR case EINTR: return DRMP3_INTERRUPT; #endif #ifdef EIO case EIO: return DRMP3_IO_ERROR; #endif #ifdef ENXIO case ENXIO: return DRMP3_DOES_NOT_EXIST; #endif #ifdef E2BIG case E2BIG: return DRMP3_INVALID_ARGS; #endif #ifdef ENOEXEC case ENOEXEC: return DRMP3_INVALID_FILE; #endif #ifdef EBADF case EBADF: return DRMP3_INVALID_FILE; #endif #ifdef ECHILD case ECHILD: return DRMP3_ERROR; #endif #ifdef EAGAIN case EAGAIN: return DRMP3_UNAVAILABLE; #endif #ifdef ENOMEM case ENOMEM: return DRMP3_OUT_OF_MEMORY; #endif #ifdef EACCES case EACCES: return DRMP3_ACCESS_DENIED; #endif #ifdef EFAULT case EFAULT: return DRMP3_BAD_ADDRESS; #endif #ifdef ENOTBLK case ENOTBLK: return DRMP3_ERROR; #endif #ifdef EBUSY case EBUSY: return DRMP3_BUSY; #endif #ifdef EEXIST case EEXIST: return DRMP3_ALREADY_EXISTS; #endif #ifdef EXDEV case EXDEV: return DRMP3_ERROR; #endif #ifdef ENODEV case ENODEV: return DRMP3_DOES_NOT_EXIST; #endif #ifdef ENOTDIR case ENOTDIR: return DRMP3_NOT_DIRECTORY; #endif #ifdef EISDIR case EISDIR: return DRMP3_IS_DIRECTORY; #endif #ifdef EINVAL case EINVAL: return DRMP3_INVALID_ARGS; #endif #ifdef ENFILE case ENFILE: return DRMP3_TOO_MANY_OPEN_FILES; #endif #ifdef EMFILE case EMFILE: return DRMP3_TOO_MANY_OPEN_FILES; #endif #ifdef ENOTTY case ENOTTY: return DRMP3_INVALID_OPERATION; #endif #ifdef ETXTBSY case ETXTBSY: return DRMP3_BUSY; #endif #ifdef EFBIG case EFBIG: return DRMP3_TOO_BIG; #endif #ifdef ENOSPC case ENOSPC: return DRMP3_NO_SPACE; #endif #ifdef ESPIPE case ESPIPE: return DRMP3_BAD_SEEK; #endif #ifdef EROFS case EROFS: return DRMP3_ACCESS_DENIED; #endif #ifdef EMLINK case EMLINK: return DRMP3_TOO_MANY_LINKS; #endif #ifdef EPIPE case EPIPE: return DRMP3_BAD_PIPE; #endif #ifdef EDOM case EDOM: return DRMP3_OUT_OF_RANGE; #endif #ifdef ERANGE case ERANGE: return DRMP3_OUT_OF_RANGE; #endif #ifdef EDEADLK case EDEADLK: return DRMP3_DEADLOCK; #endif #ifdef ENAMETOOLONG case ENAMETOOLONG: return DRMP3_PATH_TOO_LONG; #endif #ifdef ENOLCK case ENOLCK: return DRMP3_ERROR; #endif #ifdef ENOSYS case ENOSYS: return DRMP3_NOT_IMPLEMENTED; #endif #ifdef ENOTEMPTY case ENOTEMPTY: return DRMP3_DIRECTORY_NOT_EMPTY; #endif #ifdef ELOOP case ELOOP: return DRMP3_TOO_MANY_LINKS; #endif #ifdef ENOMSG case ENOMSG: return DRMP3_NO_MESSAGE; #endif #ifdef EIDRM case EIDRM: return DRMP3_ERROR; #endif #ifdef ECHRNG case ECHRNG: return DRMP3_ERROR; #endif #ifdef EL2NSYNC case EL2NSYNC: return DRMP3_ERROR; #endif #ifdef EL3HLT case EL3HLT: return DRMP3_ERROR; #endif #ifdef EL3RST case EL3RST: return DRMP3_ERROR; #endif #ifdef ELNRNG case ELNRNG: return DRMP3_OUT_OF_RANGE; #endif #ifdef EUNATCH case EUNATCH: return DRMP3_ERROR; #endif #ifdef ENOCSI case ENOCSI: return DRMP3_ERROR; #endif #ifdef EL2HLT case EL2HLT: return DRMP3_ERROR; #endif #ifdef EBADE case EBADE: return DRMP3_ERROR; #endif #ifdef EBADR case EBADR: return DRMP3_ERROR; #endif #ifdef EXFULL case EXFULL: return DRMP3_ERROR; #endif #ifdef ENOANO case ENOANO: return DRMP3_ERROR; #endif #ifdef EBADRQC case EBADRQC: return DRMP3_ERROR; #endif #ifdef EBADSLT case EBADSLT: return DRMP3_ERROR; #endif #ifdef EBFONT case EBFONT: return DRMP3_INVALID_FILE; #endif #ifdef ENOSTR case ENOSTR: return DRMP3_ERROR; #endif #ifdef ENODATA case ENODATA: return DRMP3_NO_DATA_AVAILABLE; #endif #ifdef ETIME case ETIME: return DRMP3_TIMEOUT; #endif #ifdef ENOSR case ENOSR: return DRMP3_NO_DATA_AVAILABLE; #endif #ifdef ENONET case ENONET: return DRMP3_NO_NETWORK; #endif #ifdef ENOPKG case ENOPKG: return DRMP3_ERROR; #endif #ifdef EREMOTE case EREMOTE: return DRMP3_ERROR; #endif #ifdef ENOLINK case ENOLINK: return DRMP3_ERROR; #endif #ifdef EADV case EADV: return DRMP3_ERROR; #endif #ifdef ESRMNT case ESRMNT: return DRMP3_ERROR; #endif #ifdef ECOMM case ECOMM: return DRMP3_ERROR; #endif #ifdef EPROTO case EPROTO: return DRMP3_ERROR; #endif #ifdef EMULTIHOP case EMULTIHOP: return DRMP3_ERROR; #endif #ifdef EDOTDOT case EDOTDOT: return DRMP3_ERROR; #endif #ifdef EBADMSG case EBADMSG: return DRMP3_BAD_MESSAGE; #endif #ifdef EOVERFLOW case EOVERFLOW: return DRMP3_TOO_BIG; #endif #ifdef ENOTUNIQ case ENOTUNIQ: return DRMP3_NOT_UNIQUE; #endif #ifdef EBADFD case EBADFD: return DRMP3_ERROR; #endif #ifdef EREMCHG case EREMCHG: return DRMP3_ERROR; #endif #ifdef ELIBACC case ELIBACC: return DRMP3_ACCESS_DENIED; #endif #ifdef ELIBBAD case ELIBBAD: return DRMP3_INVALID_FILE; #endif #ifdef ELIBSCN case ELIBSCN: return DRMP3_INVALID_FILE; #endif #ifdef ELIBMAX case ELIBMAX: return DRMP3_ERROR; #endif #ifdef ELIBEXEC case ELIBEXEC: return DRMP3_ERROR; #endif #ifdef EILSEQ case EILSEQ: return DRMP3_INVALID_DATA; #endif #ifdef ERESTART case ERESTART: return DRMP3_ERROR; #endif #ifdef ESTRPIPE case ESTRPIPE: return DRMP3_ERROR; #endif #ifdef EUSERS case EUSERS: return DRMP3_ERROR; #endif #ifdef ENOTSOCK case ENOTSOCK: return DRMP3_NOT_SOCKET; #endif #ifdef EDESTADDRREQ case EDESTADDRREQ: return DRMP3_NO_ADDRESS; #endif #ifdef EMSGSIZE case EMSGSIZE: return DRMP3_TOO_BIG; #endif #ifdef EPROTOTYPE case EPROTOTYPE: return DRMP3_BAD_PROTOCOL; #endif #ifdef ENOPROTOOPT case ENOPROTOOPT: return DRMP3_PROTOCOL_UNAVAILABLE; #endif #ifdef EPROTONOSUPPORT case EPROTONOSUPPORT: return DRMP3_PROTOCOL_NOT_SUPPORTED; #endif #ifdef ESOCKTNOSUPPORT case ESOCKTNOSUPPORT: return DRMP3_SOCKET_NOT_SUPPORTED; #endif #ifdef EOPNOTSUPP case EOPNOTSUPP: return DRMP3_INVALID_OPERATION; #endif #ifdef EPFNOSUPPORT case EPFNOSUPPORT: return DRMP3_PROTOCOL_FAMILY_NOT_SUPPORTED; #endif #ifdef EAFNOSUPPORT case EAFNOSUPPORT: return DRMP3_ADDRESS_FAMILY_NOT_SUPPORTED; #endif #ifdef EADDRINUSE case EADDRINUSE: return DRMP3_ALREADY_IN_USE; #endif #ifdef EADDRNOTAVAIL case EADDRNOTAVAIL: return DRMP3_ERROR; #endif #ifdef ENETDOWN case ENETDOWN: return DRMP3_NO_NETWORK; #endif #ifdef ENETUNREACH case ENETUNREACH: return DRMP3_NO_NETWORK; #endif #ifdef ENETRESET case ENETRESET: return DRMP3_NO_NETWORK; #endif #ifdef ECONNABORTED case ECONNABORTED: return DRMP3_NO_NETWORK; #endif #ifdef ECONNRESET case ECONNRESET: return DRMP3_CONNECTION_RESET; #endif #ifdef ENOBUFS case ENOBUFS: return DRMP3_NO_SPACE; #endif #ifdef EISCONN case EISCONN: return DRMP3_ALREADY_CONNECTED; #endif #ifdef ENOTCONN case ENOTCONN: return DRMP3_NOT_CONNECTED; #endif #ifdef ESHUTDOWN case ESHUTDOWN: return DRMP3_ERROR; #endif #ifdef ETOOMANYREFS case ETOOMANYREFS: return DRMP3_ERROR; #endif #ifdef ETIMEDOUT case ETIMEDOUT: return DRMP3_TIMEOUT; #endif #ifdef ECONNREFUSED case ECONNREFUSED: return DRMP3_CONNECTION_REFUSED; #endif #ifdef EHOSTDOWN case EHOSTDOWN: return DRMP3_NO_HOST; #endif #ifdef EHOSTUNREACH case EHOSTUNREACH: return DRMP3_NO_HOST; #endif #ifdef EALREADY case EALREADY: return DRMP3_IN_PROGRESS; #endif #ifdef EINPROGRESS case EINPROGRESS: return DRMP3_IN_PROGRESS; #endif #ifdef ESTALE case ESTALE: return DRMP3_INVALID_FILE; #endif #ifdef EUCLEAN case EUCLEAN: return DRMP3_ERROR; #endif #ifdef ENOTNAM case ENOTNAM: return DRMP3_ERROR; #endif #ifdef ENAVAIL case ENAVAIL: return DRMP3_ERROR; #endif #ifdef EISNAM case EISNAM: return DRMP3_ERROR; #endif #ifdef EREMOTEIO case EREMOTEIO: return DRMP3_IO_ERROR; #endif #ifdef EDQUOT case EDQUOT: return DRMP3_NO_SPACE; #endif #ifdef ENOMEDIUM case ENOMEDIUM: return DRMP3_DOES_NOT_EXIST; #endif #ifdef EMEDIUMTYPE case EMEDIUMTYPE: return DRMP3_ERROR; #endif #ifdef ECANCELED case ECANCELED: return DRMP3_CANCELLED; #endif #ifdef ENOKEY case ENOKEY: return DRMP3_ERROR; #endif #ifdef EKEYEXPIRED case EKEYEXPIRED: return DRMP3_ERROR; #endif #ifdef EKEYREVOKED case EKEYREVOKED: return DRMP3_ERROR; #endif #ifdef EKEYREJECTED case EKEYREJECTED: return DRMP3_ERROR; #endif #ifdef EOWNERDEAD case EOWNERDEAD: return DRMP3_ERROR; #endif #ifdef ENOTRECOVERABLE case ENOTRECOVERABLE: return DRMP3_ERROR; #endif #ifdef ERFKILL case ERFKILL: return DRMP3_ERROR; #endif #ifdef EHWPOISON case EHWPOISON: return DRMP3_ERROR; #endif default: return DRMP3_ERROR; } } static drmp3_result drmp3_fopen(FILE** ppFile, const char* pFilePath, const char* pOpenMode) { #if defined(_MSC_VER) && _MSC_VER >= 1400 errno_t err; #endif if (ppFile != NULL) { *ppFile = NULL; } if (pFilePath == NULL || pOpenMode == NULL || ppFile == NULL) { return DRMP3_INVALID_ARGS; } #if defined(_MSC_VER) && _MSC_VER >= 1400 err = fopen_s(ppFile, pFilePath, pOpenMode); if (err != 0) { return drmp3_result_from_errno(err); } #else #if defined(_WIN32) || defined(__APPLE__) *ppFile = fopen(pFilePath, pOpenMode); #else #if defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS == 64 && defined(_LARGEFILE64_SOURCE) *ppFile = fopen64(pFilePath, pOpenMode); #else *ppFile = fopen(pFilePath, pOpenMode); #endif #endif if (*ppFile == NULL) { drmp3_result result = drmp3_result_from_errno(errno); if (result == DRMP3_SUCCESS) { result = DRMP3_ERROR; } return result; } #endif return DRMP3_SUCCESS; } #if defined(_WIN32) #if defined(_MSC_VER) || defined(__MINGW64__) || (!defined(__STRICT_ANSI__) && !defined(_NO_EXT_KEYS)) #define DRMP3_HAS_WFOPEN #endif #endif static drmp3_result drmp3_wfopen(FILE** ppFile, const wchar_t* pFilePath, const wchar_t* pOpenMode, const drmp3_allocation_callbacks* pAllocationCallbacks) { if (ppFile != NULL) { *ppFile = NULL; } if (pFilePath == NULL || pOpenMode == NULL || ppFile == NULL) { return DRMP3_INVALID_ARGS; } #if defined(DRMP3_HAS_WFOPEN) { #if defined(_MSC_VER) && _MSC_VER >= 1400 errno_t err = _wfopen_s(ppFile, pFilePath, pOpenMode); if (err != 0) { return drmp3_result_from_errno(err); } #else *ppFile = _wfopen(pFilePath, pOpenMode); if (*ppFile == NULL) { return drmp3_result_from_errno(errno); } #endif (void)pAllocationCallbacks; } #else #if defined(__DJGPP__) { } #else { mbstate_t mbs; size_t lenMB; const wchar_t* pFilePathTemp = pFilePath; char* pFilePathMB = NULL; char pOpenModeMB[32] = {0}; DRMP3_ZERO_OBJECT(&mbs); lenMB = wcsrtombs(NULL, &pFilePathTemp, 0, &mbs); if (lenMB == (size_t)-1) { return drmp3_result_from_errno(errno); } pFilePathMB = (char*)drmp3__malloc_from_callbacks(lenMB + 1, pAllocationCallbacks); if (pFilePathMB == NULL) { return DRMP3_OUT_OF_MEMORY; } pFilePathTemp = pFilePath; DRMP3_ZERO_OBJECT(&mbs); wcsrtombs(pFilePathMB, &pFilePathTemp, lenMB + 1, &mbs); { size_t i = 0; for (;;) { if (pOpenMode[i] == 0) { pOpenModeMB[i] = '\0'; break; } pOpenModeMB[i] = (char)pOpenMode[i]; i += 1; } } *ppFile = fopen(pFilePathMB, pOpenModeMB); drmp3__free_from_callbacks(pFilePathMB, pAllocationCallbacks); } #endif if (*ppFile == NULL) { return DRMP3_ERROR; } #endif return DRMP3_SUCCESS; } static size_t drmp3__on_read_stdio(void* pUserData, void* pBufferOut, size_t bytesToRead) { return fread(pBufferOut, 1, bytesToRead, (FILE*)pUserData); } static drmp3_bool32 drmp3__on_seek_stdio(void* pUserData, int offset, drmp3_seek_origin origin) { return fseek((FILE*)pUserData, offset, (origin == drmp3_seek_origin_current) ? SEEK_CUR : SEEK_SET) == 0; } DRMP3_API drmp3_bool32 drmp3_init_file(drmp3* pMP3, const char* pFilePath, const drmp3_allocation_callbacks* pAllocationCallbacks) { drmp3_bool32 result; FILE* pFile; if (drmp3_fopen(&pFile, pFilePath, "rb") != DRMP3_SUCCESS) { return DRMP3_FALSE; } result = drmp3_init(pMP3, drmp3__on_read_stdio, drmp3__on_seek_stdio, (void*)pFile, pAllocationCallbacks); if (result != DRMP3_TRUE) { fclose(pFile); return result; } return DRMP3_TRUE; } DRMP3_API drmp3_bool32 drmp3_init_file_w(drmp3* pMP3, const wchar_t* pFilePath, const drmp3_allocation_callbacks* pAllocationCallbacks) { drmp3_bool32 result; FILE* pFile; if (drmp3_wfopen(&pFile, pFilePath, L"rb", pAllocationCallbacks) != DRMP3_SUCCESS) { return DRMP3_FALSE; } result = drmp3_init(pMP3, drmp3__on_read_stdio, drmp3__on_seek_stdio, (void*)pFile, pAllocationCallbacks); if (result != DRMP3_TRUE) { fclose(pFile); return result; } return DRMP3_TRUE; } #endif DRMP3_API void drmp3_uninit(drmp3* pMP3) { if (pMP3 == NULL) { return; } #ifndef DR_MP3_NO_STDIO if (pMP3->onRead == drmp3__on_read_stdio) { FILE* pFile = (FILE*)pMP3->pUserData; if (pFile != NULL) { fclose(pFile); pMP3->pUserData = NULL; } } #endif drmp3__free_from_callbacks(pMP3->pData, &pMP3->allocationCallbacks); } #if defined(DR_MP3_FLOAT_OUTPUT) static void drmp3_f32_to_s16(drmp3_int16* dst, const float* src, drmp3_uint64 sampleCount) { drmp3_uint64 i; drmp3_uint64 i4; drmp3_uint64 sampleCount4; i = 0; sampleCount4 = sampleCount >> 2; for (i4 = 0; i4 < sampleCount4; i4 += 1) { float x0 = src[i+0]; float x1 = src[i+1]; float x2 = src[i+2]; float x3 = src[i+3]; x0 = ((x0 < -1) ? -1 : ((x0 > 1) ? 1 : x0)); x1 = ((x1 < -1) ? -1 : ((x1 > 1) ? 1 : x1)); x2 = ((x2 < -1) ? -1 : ((x2 > 1) ? 1 : x2)); x3 = ((x3 < -1) ? -1 : ((x3 > 1) ? 1 : x3)); x0 = x0 * 32767.0f; x1 = x1 * 32767.0f; x2 = x2 * 32767.0f; x3 = x3 * 32767.0f; dst[i+0] = (drmp3_int16)x0; dst[i+1] = (drmp3_int16)x1; dst[i+2] = (drmp3_int16)x2; dst[i+3] = (drmp3_int16)x3; i += 4; } for (; i < sampleCount; i += 1) { float x = src[i]; x = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); x = x * 32767.0f; dst[i] = (drmp3_int16)x; } } #endif #if !defined(DR_MP3_FLOAT_OUTPUT) static void drmp3_s16_to_f32(float* dst, const drmp3_int16* src, drmp3_uint64 sampleCount) { drmp3_uint64 i; for (i = 0; i < sampleCount; i += 1) { float x = (float)src[i]; x = x * 0.000030517578125f; dst[i] = x; } } #endif static drmp3_uint64 drmp3_read_pcm_frames_raw(drmp3* pMP3, drmp3_uint64 framesToRead, void* pBufferOut) { drmp3_uint64 totalFramesRead = 0; DRMP3_ASSERT(pMP3 != NULL); DRMP3_ASSERT(pMP3->onRead != NULL); while (framesToRead > 0) { drmp3_uint32 framesToConsume = (drmp3_uint32)DRMP3_MIN(pMP3->pcmFramesRemainingInMP3Frame, framesToRead); if (pBufferOut != NULL) { #if defined(DR_MP3_FLOAT_OUTPUT) float* pFramesOutF32 = (float*)DRMP3_OFFSET_PTR(pBufferOut, sizeof(float) * totalFramesRead * pMP3->channels); float* pFramesInF32 = (float*)DRMP3_OFFSET_PTR(&pMP3->pcmFrames[0], sizeof(float) * pMP3->pcmFramesConsumedInMP3Frame * pMP3->mp3FrameChannels); DRMP3_COPY_MEMORY(pFramesOutF32, pFramesInF32, sizeof(float) * framesToConsume * pMP3->channels); #else drmp3_int16* pFramesOutS16 = (drmp3_int16*)DRMP3_OFFSET_PTR(pBufferOut, sizeof(drmp3_int16) * totalFramesRead * pMP3->channels); drmp3_int16* pFramesInS16 = (drmp3_int16*)DRMP3_OFFSET_PTR(&pMP3->pcmFrames[0], sizeof(drmp3_int16) * pMP3->pcmFramesConsumedInMP3Frame * pMP3->mp3FrameChannels); DRMP3_COPY_MEMORY(pFramesOutS16, pFramesInS16, sizeof(drmp3_int16) * framesToConsume * pMP3->channels); #endif } pMP3->currentPCMFrame += framesToConsume; pMP3->pcmFramesConsumedInMP3Frame += framesToConsume; pMP3->pcmFramesRemainingInMP3Frame -= framesToConsume; totalFramesRead += framesToConsume; framesToRead -= framesToConsume; if (framesToRead == 0) { break; } DRMP3_ASSERT(pMP3->pcmFramesRemainingInMP3Frame == 0); if (drmp3_decode_next_frame(pMP3) == 0) { break; } } return totalFramesRead; } DRMP3_API drmp3_uint64 drmp3_read_pcm_frames_f32(drmp3* pMP3, drmp3_uint64 framesToRead, float* pBufferOut) { if (pMP3 == NULL || pMP3->onRead == NULL) { return 0; } #if defined(DR_MP3_FLOAT_OUTPUT) return drmp3_read_pcm_frames_raw(pMP3, framesToRead, pBufferOut); #else { drmp3_int16 pTempS16[8192]; drmp3_uint64 totalPCMFramesRead = 0; while (totalPCMFramesRead < framesToRead) { drmp3_uint64 framesJustRead; drmp3_uint64 framesRemaining = framesToRead - totalPCMFramesRead; drmp3_uint64 framesToReadNow = DRMP3_COUNTOF(pTempS16) / pMP3->channels; if (framesToReadNow > framesRemaining) { framesToReadNow = framesRemaining; } framesJustRead = drmp3_read_pcm_frames_raw(pMP3, framesToReadNow, pTempS16); if (framesJustRead == 0) { break; } drmp3_s16_to_f32((float*)DRMP3_OFFSET_PTR(pBufferOut, sizeof(float) * totalPCMFramesRead * pMP3->channels), pTempS16, framesJustRead * pMP3->channels); totalPCMFramesRead += framesJustRead; } return totalPCMFramesRead; } #endif } DRMP3_API drmp3_uint64 drmp3_read_pcm_frames_s16(drmp3* pMP3, drmp3_uint64 framesToRead, drmp3_int16* pBufferOut) { if (pMP3 == NULL || pMP3->onRead == NULL) { return 0; } #if !defined(DR_MP3_FLOAT_OUTPUT) return drmp3_read_pcm_frames_raw(pMP3, framesToRead, pBufferOut); #else { float pTempF32[4096]; drmp3_uint64 totalPCMFramesRead = 0; while (totalPCMFramesRead < framesToRead) { drmp3_uint64 framesJustRead; drmp3_uint64 framesRemaining = framesToRead - totalPCMFramesRead; drmp3_uint64 framesToReadNow = DRMP3_COUNTOF(pTempF32) / pMP3->channels; if (framesToReadNow > framesRemaining) { framesToReadNow = framesRemaining; } framesJustRead = drmp3_read_pcm_frames_raw(pMP3, framesToReadNow, pTempF32); if (framesJustRead == 0) { break; } drmp3_f32_to_s16((drmp3_int16*)DRMP3_OFFSET_PTR(pBufferOut, sizeof(drmp3_int16) * totalPCMFramesRead * pMP3->channels), pTempF32, framesJustRead * pMP3->channels); totalPCMFramesRead += framesJustRead; } return totalPCMFramesRead; } #endif } static void drmp3_reset(drmp3* pMP3) { DRMP3_ASSERT(pMP3 != NULL); pMP3->pcmFramesConsumedInMP3Frame = 0; pMP3->pcmFramesRemainingInMP3Frame = 0; pMP3->currentPCMFrame = 0; pMP3->dataSize = 0; pMP3->atEnd = DRMP3_FALSE; drmp3dec_init(&pMP3->decoder); } static drmp3_bool32 drmp3_seek_to_start_of_stream(drmp3* pMP3) { DRMP3_ASSERT(pMP3 != NULL); DRMP3_ASSERT(pMP3->onSeek != NULL); if (!drmp3__on_seek(pMP3, 0, drmp3_seek_origin_start)) { return DRMP3_FALSE; } drmp3_reset(pMP3); return DRMP3_TRUE; } static drmp3_bool32 drmp3_seek_forward_by_pcm_frames__brute_force(drmp3* pMP3, drmp3_uint64 frameOffset) { drmp3_uint64 framesRead; #if defined(DR_MP3_FLOAT_OUTPUT) framesRead = drmp3_read_pcm_frames_f32(pMP3, frameOffset, NULL); #else framesRead = drmp3_read_pcm_frames_s16(pMP3, frameOffset, NULL); #endif if (framesRead != frameOffset) { return DRMP3_FALSE; } return DRMP3_TRUE; } static drmp3_bool32 drmp3_seek_to_pcm_frame__brute_force(drmp3* pMP3, drmp3_uint64 frameIndex) { DRMP3_ASSERT(pMP3 != NULL); if (frameIndex == pMP3->currentPCMFrame) { return DRMP3_TRUE; } if (frameIndex < pMP3->currentPCMFrame) { if (!drmp3_seek_to_start_of_stream(pMP3)) { return DRMP3_FALSE; } } DRMP3_ASSERT(frameIndex >= pMP3->currentPCMFrame); return drmp3_seek_forward_by_pcm_frames__brute_force(pMP3, (frameIndex - pMP3->currentPCMFrame)); } static drmp3_bool32 drmp3_find_closest_seek_point(drmp3* pMP3, drmp3_uint64 frameIndex, drmp3_uint32* pSeekPointIndex) { drmp3_uint32 iSeekPoint; DRMP3_ASSERT(pSeekPointIndex != NULL); *pSeekPointIndex = 0; if (frameIndex < pMP3->pSeekPoints[0].pcmFrameIndex) { return DRMP3_FALSE; } for (iSeekPoint = 0; iSeekPoint < pMP3->seekPointCount; ++iSeekPoint) { if (pMP3->pSeekPoints[iSeekPoint].pcmFrameIndex > frameIndex) { break; } *pSeekPointIndex = iSeekPoint; } return DRMP3_TRUE; } static drmp3_bool32 drmp3_seek_to_pcm_frame__seek_table(drmp3* pMP3, drmp3_uint64 frameIndex) { drmp3_seek_point seekPoint; drmp3_uint32 priorSeekPointIndex; drmp3_uint16 iMP3Frame; drmp3_uint64 leftoverFrames; DRMP3_ASSERT(pMP3 != NULL); DRMP3_ASSERT(pMP3->pSeekPoints != NULL); DRMP3_ASSERT(pMP3->seekPointCount > 0); if (drmp3_find_closest_seek_point(pMP3, frameIndex, &priorSeekPointIndex)) { seekPoint = pMP3->pSeekPoints[priorSeekPointIndex]; } else { seekPoint.seekPosInBytes = 0; seekPoint.pcmFrameIndex = 0; seekPoint.mp3FramesToDiscard = 0; seekPoint.pcmFramesToDiscard = 0; } if (!drmp3__on_seek_64(pMP3, seekPoint.seekPosInBytes, drmp3_seek_origin_start)) { return DRMP3_FALSE; } drmp3_reset(pMP3); for (iMP3Frame = 0; iMP3Frame < seekPoint.mp3FramesToDiscard; ++iMP3Frame) { drmp3_uint32 pcmFramesRead; drmp3d_sample_t* pPCMFrames; pPCMFrames = NULL; if (iMP3Frame == seekPoint.mp3FramesToDiscard-1) { pPCMFrames = (drmp3d_sample_t*)pMP3->pcmFrames; } pcmFramesRead = drmp3_decode_next_frame_ex(pMP3, pPCMFrames); if (pcmFramesRead == 0) { return DRMP3_FALSE; } } pMP3->currentPCMFrame = seekPoint.pcmFrameIndex - seekPoint.pcmFramesToDiscard; leftoverFrames = frameIndex - pMP3->currentPCMFrame; return drmp3_seek_forward_by_pcm_frames__brute_force(pMP3, leftoverFrames); } DRMP3_API drmp3_bool32 drmp3_seek_to_pcm_frame(drmp3* pMP3, drmp3_uint64 frameIndex) { if (pMP3 == NULL || pMP3->onSeek == NULL) { return DRMP3_FALSE; } if (frameIndex == 0) { return drmp3_seek_to_start_of_stream(pMP3); } if (pMP3->pSeekPoints != NULL && pMP3->seekPointCount > 0) { return drmp3_seek_to_pcm_frame__seek_table(pMP3, frameIndex); } else { return drmp3_seek_to_pcm_frame__brute_force(pMP3, frameIndex); } } DRMP3_API drmp3_bool32 drmp3_get_mp3_and_pcm_frame_count(drmp3* pMP3, drmp3_uint64* pMP3FrameCount, drmp3_uint64* pPCMFrameCount) { drmp3_uint64 currentPCMFrame; drmp3_uint64 totalPCMFrameCount; drmp3_uint64 totalMP3FrameCount; if (pMP3 == NULL) { return DRMP3_FALSE; } if (pMP3->onSeek == NULL) { return DRMP3_FALSE; } currentPCMFrame = pMP3->currentPCMFrame; if (!drmp3_seek_to_start_of_stream(pMP3)) { return DRMP3_FALSE; } totalPCMFrameCount = 0; totalMP3FrameCount = 0; for (;;) { drmp3_uint32 pcmFramesInCurrentMP3Frame; pcmFramesInCurrentMP3Frame = drmp3_decode_next_frame_ex(pMP3, NULL); if (pcmFramesInCurrentMP3Frame == 0) { break; } totalPCMFrameCount += pcmFramesInCurrentMP3Frame; totalMP3FrameCount += 1; } if (!drmp3_seek_to_start_of_stream(pMP3)) { return DRMP3_FALSE; } if (!drmp3_seek_to_pcm_frame(pMP3, currentPCMFrame)) { return DRMP3_FALSE; } if (pMP3FrameCount != NULL) { *pMP3FrameCount = totalMP3FrameCount; } if (pPCMFrameCount != NULL) { *pPCMFrameCount = totalPCMFrameCount; } return DRMP3_TRUE; } DRMP3_API drmp3_uint64 drmp3_get_pcm_frame_count(drmp3* pMP3) { drmp3_uint64 totalPCMFrameCount; if (!drmp3_get_mp3_and_pcm_frame_count(pMP3, NULL, &totalPCMFrameCount)) { return 0; } return totalPCMFrameCount; } DRMP3_API drmp3_uint64 drmp3_get_mp3_frame_count(drmp3* pMP3) { drmp3_uint64 totalMP3FrameCount; if (!drmp3_get_mp3_and_pcm_frame_count(pMP3, &totalMP3FrameCount, NULL)) { return 0; } return totalMP3FrameCount; } static void drmp3__accumulate_running_pcm_frame_count(drmp3* pMP3, drmp3_uint32 pcmFrameCountIn, drmp3_uint64* pRunningPCMFrameCount, float* pRunningPCMFrameCountFractionalPart) { float srcRatio; float pcmFrameCountOutF; drmp3_uint32 pcmFrameCountOut; srcRatio = (float)pMP3->mp3FrameSampleRate / (float)pMP3->sampleRate; DRMP3_ASSERT(srcRatio > 0); pcmFrameCountOutF = *pRunningPCMFrameCountFractionalPart + (pcmFrameCountIn / srcRatio); pcmFrameCountOut = (drmp3_uint32)pcmFrameCountOutF; *pRunningPCMFrameCountFractionalPart = pcmFrameCountOutF - pcmFrameCountOut; *pRunningPCMFrameCount += pcmFrameCountOut; } typedef struct { drmp3_uint64 bytePos; drmp3_uint64 pcmFrameIndex; } drmp3__seeking_mp3_frame_info; DRMP3_API drmp3_bool32 drmp3_calculate_seek_points(drmp3* pMP3, drmp3_uint32* pSeekPointCount, drmp3_seek_point* pSeekPoints) { drmp3_uint32 seekPointCount; drmp3_uint64 currentPCMFrame; drmp3_uint64 totalMP3FrameCount; drmp3_uint64 totalPCMFrameCount; if (pMP3 == NULL || pSeekPointCount == NULL || pSeekPoints == NULL) { return DRMP3_FALSE; } seekPointCount = *pSeekPointCount; if (seekPointCount == 0) { return DRMP3_FALSE; } currentPCMFrame = pMP3->currentPCMFrame; if (!drmp3_get_mp3_and_pcm_frame_count(pMP3, &totalMP3FrameCount, &totalPCMFrameCount)) { return DRMP3_FALSE; } if (totalMP3FrameCount < DRMP3_SEEK_LEADING_MP3_FRAMES+1) { seekPointCount = 1; pSeekPoints[0].seekPosInBytes = 0; pSeekPoints[0].pcmFrameIndex = 0; pSeekPoints[0].mp3FramesToDiscard = 0; pSeekPoints[0].pcmFramesToDiscard = 0; } else { drmp3_uint64 pcmFramesBetweenSeekPoints; drmp3__seeking_mp3_frame_info mp3FrameInfo[DRMP3_SEEK_LEADING_MP3_FRAMES+1]; drmp3_uint64 runningPCMFrameCount = 0; float runningPCMFrameCountFractionalPart = 0; drmp3_uint64 nextTargetPCMFrame; drmp3_uint32 iMP3Frame; drmp3_uint32 iSeekPoint; if (seekPointCount > totalMP3FrameCount-1) { seekPointCount = (drmp3_uint32)totalMP3FrameCount-1; } pcmFramesBetweenSeekPoints = totalPCMFrameCount / (seekPointCount+1); if (!drmp3_seek_to_start_of_stream(pMP3)) { return DRMP3_FALSE; } for (iMP3Frame = 0; iMP3Frame < DRMP3_SEEK_LEADING_MP3_FRAMES+1; ++iMP3Frame) { drmp3_uint32 pcmFramesInCurrentMP3FrameIn; DRMP3_ASSERT(pMP3->streamCursor >= pMP3->dataSize); mp3FrameInfo[iMP3Frame].bytePos = pMP3->streamCursor - pMP3->dataSize; mp3FrameInfo[iMP3Frame].pcmFrameIndex = runningPCMFrameCount; pcmFramesInCurrentMP3FrameIn = drmp3_decode_next_frame_ex(pMP3, NULL); if (pcmFramesInCurrentMP3FrameIn == 0) { return DRMP3_FALSE; } drmp3__accumulate_running_pcm_frame_count(pMP3, pcmFramesInCurrentMP3FrameIn, &runningPCMFrameCount, &runningPCMFrameCountFractionalPart); } nextTargetPCMFrame = 0; for (iSeekPoint = 0; iSeekPoint < seekPointCount; ++iSeekPoint) { nextTargetPCMFrame += pcmFramesBetweenSeekPoints; for (;;) { if (nextTargetPCMFrame < runningPCMFrameCount) { pSeekPoints[iSeekPoint].seekPosInBytes = mp3FrameInfo[0].bytePos; pSeekPoints[iSeekPoint].pcmFrameIndex = nextTargetPCMFrame; pSeekPoints[iSeekPoint].mp3FramesToDiscard = DRMP3_SEEK_LEADING_MP3_FRAMES; pSeekPoints[iSeekPoint].pcmFramesToDiscard = (drmp3_uint16)(nextTargetPCMFrame - mp3FrameInfo[DRMP3_SEEK_LEADING_MP3_FRAMES-1].pcmFrameIndex); break; } else { size_t i; drmp3_uint32 pcmFramesInCurrentMP3FrameIn; for (i = 0; i < DRMP3_COUNTOF(mp3FrameInfo)-1; ++i) { mp3FrameInfo[i] = mp3FrameInfo[i+1]; } mp3FrameInfo[DRMP3_COUNTOF(mp3FrameInfo)-1].bytePos = pMP3->streamCursor - pMP3->dataSize; mp3FrameInfo[DRMP3_COUNTOF(mp3FrameInfo)-1].pcmFrameIndex = runningPCMFrameCount; pcmFramesInCurrentMP3FrameIn = drmp3_decode_next_frame_ex(pMP3, NULL); if (pcmFramesInCurrentMP3FrameIn == 0) { pSeekPoints[iSeekPoint].seekPosInBytes = mp3FrameInfo[0].bytePos; pSeekPoints[iSeekPoint].pcmFrameIndex = nextTargetPCMFrame; pSeekPoints[iSeekPoint].mp3FramesToDiscard = DRMP3_SEEK_LEADING_MP3_FRAMES; pSeekPoints[iSeekPoint].pcmFramesToDiscard = (drmp3_uint16)(nextTargetPCMFrame - mp3FrameInfo[DRMP3_SEEK_LEADING_MP3_FRAMES-1].pcmFrameIndex); break; } drmp3__accumulate_running_pcm_frame_count(pMP3, pcmFramesInCurrentMP3FrameIn, &runningPCMFrameCount, &runningPCMFrameCountFractionalPart); } } } if (!drmp3_seek_to_start_of_stream(pMP3)) { return DRMP3_FALSE; } if (!drmp3_seek_to_pcm_frame(pMP3, currentPCMFrame)) { return DRMP3_FALSE; } } *pSeekPointCount = seekPointCount; return DRMP3_TRUE; } DRMP3_API drmp3_bool32 drmp3_bind_seek_table(drmp3* pMP3, drmp3_uint32 seekPointCount, drmp3_seek_point* pSeekPoints) { if (pMP3 == NULL) { return DRMP3_FALSE; } if (seekPointCount == 0 || pSeekPoints == NULL) { pMP3->seekPointCount = 0; pMP3->pSeekPoints = NULL; } else { pMP3->seekPointCount = seekPointCount; pMP3->pSeekPoints = pSeekPoints; } return DRMP3_TRUE; } static float* drmp3__full_read_and_close_f32(drmp3* pMP3, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount) { drmp3_uint64 totalFramesRead = 0; drmp3_uint64 framesCapacity = 0; float* pFrames = NULL; float temp[4096]; DRMP3_ASSERT(pMP3 != NULL); for (;;) { drmp3_uint64 framesToReadRightNow = DRMP3_COUNTOF(temp) / pMP3->channels; drmp3_uint64 framesJustRead = drmp3_read_pcm_frames_f32(pMP3, framesToReadRightNow, temp); if (framesJustRead == 0) { break; } if (framesCapacity < totalFramesRead + framesJustRead) { drmp3_uint64 oldFramesBufferSize; drmp3_uint64 newFramesBufferSize; drmp3_uint64 newFramesCap; float* pNewFrames; newFramesCap = framesCapacity * 2; if (newFramesCap < totalFramesRead + framesJustRead) { newFramesCap = totalFramesRead + framesJustRead; } oldFramesBufferSize = framesCapacity * pMP3->channels * sizeof(float); newFramesBufferSize = newFramesCap * pMP3->channels * sizeof(float); if (newFramesBufferSize > (drmp3_uint64)DRMP3_SIZE_MAX) { break; } 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); break; } pFrames = pNewFrames; framesCapacity = newFramesCap; } DRMP3_COPY_MEMORY(pFrames + totalFramesRead*pMP3->channels, temp, (size_t)(framesJustRead*pMP3->channels*sizeof(float))); totalFramesRead += framesJustRead; if (framesJustRead != framesToReadRightNow) { break; } } if (pConfig != NULL) { pConfig->channels = pMP3->channels; pConfig->sampleRate = pMP3->sampleRate; } drmp3_uninit(pMP3); if (pTotalFrameCount) { *pTotalFrameCount = totalFramesRead; } return pFrames; } static drmp3_int16* drmp3__full_read_and_close_s16(drmp3* pMP3, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount) { drmp3_uint64 totalFramesRead = 0; drmp3_uint64 framesCapacity = 0; drmp3_int16* pFrames = NULL; drmp3_int16 temp[4096]; DRMP3_ASSERT(pMP3 != NULL); for (;;) { drmp3_uint64 framesToReadRightNow = DRMP3_COUNTOF(temp) / pMP3->channels; drmp3_uint64 framesJustRead = drmp3_read_pcm_frames_s16(pMP3, framesToReadRightNow, temp); if (framesJustRead == 0) { break; } if (framesCapacity < totalFramesRead + framesJustRead) { drmp3_uint64 newFramesBufferSize; drmp3_uint64 oldFramesBufferSize; drmp3_uint64 newFramesCap; drmp3_int16* pNewFrames; newFramesCap = framesCapacity * 2; if (newFramesCap < totalFramesRead + framesJustRead) { newFramesCap = totalFramesRead + framesJustRead; } oldFramesBufferSize = framesCapacity * pMP3->channels * sizeof(drmp3_int16); newFramesBufferSize = newFramesCap * pMP3->channels * sizeof(drmp3_int16); if (newFramesBufferSize > (drmp3_uint64)DRMP3_SIZE_MAX) { break; } 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); break; } pFrames = pNewFrames; framesCapacity = newFramesCap; } DRMP3_COPY_MEMORY(pFrames + totalFramesRead*pMP3->channels, temp, (size_t)(framesJustRead*pMP3->channels*sizeof(drmp3_int16))); totalFramesRead += framesJustRead; if (framesJustRead != framesToReadRightNow) { break; } } if (pConfig != NULL) { pConfig->channels = pMP3->channels; pConfig->sampleRate = pMP3->sampleRate; } drmp3_uninit(pMP3); if (pTotalFrameCount) { *pTotalFrameCount = totalFramesRead; } return pFrames; } DRMP3_API float* drmp3_open_and_read_pcm_frames_f32(drmp3_read_proc onRead, drmp3_seek_proc onSeek, void* pUserData, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks) { drmp3 mp3; if (!drmp3_init(&mp3, onRead, onSeek, pUserData, pAllocationCallbacks)) { return NULL; } return drmp3__full_read_and_close_f32(&mp3, pConfig, pTotalFrameCount); } DRMP3_API drmp3_int16* drmp3_open_and_read_pcm_frames_s16(drmp3_read_proc onRead, drmp3_seek_proc onSeek, void* pUserData, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks) { drmp3 mp3; if (!drmp3_init(&mp3, onRead, onSeek, pUserData, pAllocationCallbacks)) { return NULL; } return drmp3__full_read_and_close_s16(&mp3, pConfig, pTotalFrameCount); } DRMP3_API float* drmp3_open_memory_and_read_pcm_frames_f32(const void* pData, size_t dataSize, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks) { drmp3 mp3; if (!drmp3_init_memory(&mp3, pData, dataSize, pAllocationCallbacks)) { return NULL; } return drmp3__full_read_and_close_f32(&mp3, pConfig, pTotalFrameCount); } DRMP3_API drmp3_int16* drmp3_open_memory_and_read_pcm_frames_s16(const void* pData, size_t dataSize, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks) { drmp3 mp3; if (!drmp3_init_memory(&mp3, pData, dataSize, pAllocationCallbacks)) { return NULL; } return drmp3__full_read_and_close_s16(&mp3, pConfig, pTotalFrameCount); } #ifndef DR_MP3_NO_STDIO DRMP3_API float* drmp3_open_file_and_read_pcm_frames_f32(const char* filePath, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks) { drmp3 mp3; if (!drmp3_init_file(&mp3, filePath, pAllocationCallbacks)) { return NULL; } return drmp3__full_read_and_close_f32(&mp3, pConfig, pTotalFrameCount); } DRMP3_API drmp3_int16* drmp3_open_file_and_read_pcm_frames_s16(const char* filePath, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks) { drmp3 mp3; if (!drmp3_init_file(&mp3, filePath, pAllocationCallbacks)) { return NULL; } return drmp3__full_read_and_close_s16(&mp3, pConfig, pTotalFrameCount); } #endif DRMP3_API void* drmp3_malloc(size_t sz, const drmp3_allocation_callbacks* pAllocationCallbacks) { if (pAllocationCallbacks != NULL) { return drmp3__malloc_from_callbacks(sz, pAllocationCallbacks); } else { return drmp3__malloc_default(sz, NULL); } } DRMP3_API void drmp3_free(void* p, const drmp3_allocation_callbacks* pAllocationCallbacks) { if (pAllocationCallbacks != NULL) { drmp3__free_from_callbacks(p, pAllocationCallbacks); } else { drmp3__free_default(p, NULL); } } #endif /* dr_mp3_c end */ #endif /* DRMP3_IMPLEMENTATION */ #endif /* MA_NO_MP3 */ /* End globally disabled warnings. */ #if defined(_MSC_VER) #pragma warning(pop) #endif #endif /* miniaudio_c */ #endif /* MINIAUDIO_IMPLEMENTATION */ /* This software is available as a choice of the following licenses. Choose whichever you prefer. =============================================================================== ALTERNATIVE 1 - Public Domain (www.unlicense.org) =============================================================================== This is free and unencumbered software released into the public domain. Anyone is free to copy, modify, publish, use, compile, sell, or distribute this software, either in source code form or as a compiled binary, for any purpose, commercial or non-commercial, and by any means. In jurisdictions that recognize copyright laws, the author or authors of this software dedicate any and all copyright interest in the software to the public domain. We make this dedication for the benefit of the public at large and to the detriment of our heirs and successors. We intend this dedication to be an overt act of relinquishment in perpetuity of all present and future rights to this software under copyright law. 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 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. For more information, please refer to =============================================================================== ALTERNATIVE 2 - MIT No Attribution =============================================================================== Copyright 2020 David Reid 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. 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. */ #line 0 //--- #undef L #undef C #undef R #define error l_error #define panic l_panic #line 1 "3rd_lua.h" /* minilua.h -- Lua in a single header Project URL: https://github.com/edubart/minilua This is Lua 5.4.4 contained in a single header to be bundled in C/C++ applications with ease. Lua is a powerful, efficient, lightweight, embeddable scripting language. Do the following in *one* C file to create the implementation: #define LUA_IMPL By default it detects the system platform to use, however you could explicitly define one. Note that almost no modification was made in the Lua implementation code, thus there are some C variable names that may collide with your code, therefore it is best to declare the Lua implementation in dedicated C file. Optionally provide the following defines: LUA_MAKE_LUA - implement the Lua command line REPL LICENSE MIT License, same as Lua, see end of file. */ /* detect system platform */ #if !defined(LUA_USE_WINDOWS) && !defined(LUA_USE_LINUX) && !defined(LUA_USE_MACOSX) && !defined(LUA_USE_POSIX) && !defined(LUA_USE_C89) #if defined(_WIN32) #define LUA_USE_WINDOWS #elif defined(__linux__) #define LUA_USE_LINUX #elif defined(__APPLE__) #define LUA_USE_MACOSX #else /* probably a POSIX system */ #define LUA_USE_POSIX #define LUA_USE_DLOPEN #endif #endif #ifdef LUA_IMPL #define LUA_CORE /* ** $Id: lprefix.h $ ** Definitions for Lua code that must come before any other header file ** See Copyright Notice in lua.h */ #ifndef lprefix_h #define lprefix_h /* ** Allows POSIX/XSI stuff */ #if !defined(LUA_USE_C89) /* { */ #if !defined(_XOPEN_SOURCE) #define _XOPEN_SOURCE 600 #elif _XOPEN_SOURCE == 0 #undef _XOPEN_SOURCE /* use -D_XOPEN_SOURCE=0 to undefine it */ #endif /* ** Allows manipulation of large files in gcc and some other compilers */ #if !defined(LUA_32BITS) && !defined(_FILE_OFFSET_BITS) #define _LARGEFILE_SOURCE 1 #define _FILE_OFFSET_BITS 64 #endif #endif /* } */ /* ** Windows stuff */ #if defined(_WIN32) /* { */ #if !defined(_CRT_SECURE_NO_WARNINGS) #define _CRT_SECURE_NO_WARNINGS /* avoid warnings about ISO C functions */ #endif #endif /* } */ #endif #endif /* LUA_IMPL */ #ifdef __cplusplus extern "C" { #endif /* ** $Id: luaconf.h $ ** Configuration file for Lua ** See Copyright Notice in lua.h */ #ifndef luaconf_h #define luaconf_h #include #include /* ** =================================================================== ** General Configuration File for Lua ** ** Some definitions here can be changed externally, through the compiler ** (e.g., with '-D' options): They are commented out or protected ** by '#if !defined' guards. However, several other definitions ** should be changed directly here, either because they affect the ** Lua ABI (by making the changes here, you ensure that all software ** connected to Lua, such as C libraries, will be compiled with the same ** configuration); or because they are seldom changed. ** ** Search for "@@" to find all configurable definitions. ** =================================================================== */ /* ** {==================================================================== ** System Configuration: macros to adapt (if needed) Lua to some ** particular platform, for instance restricting it to C89. ** ===================================================================== */ /* @@ LUA_USE_C89 controls the use of non-ISO-C89 features. ** Define it if you want Lua to avoid the use of a few C99 features ** or Windows-specific features on Windows. */ /* #define LUA_USE_C89 */ /* ** By default, Lua on Windows use (some) specific Windows features */ #if !defined(LUA_USE_C89) && defined(_WIN32) && !defined(_WIN32_WCE) #define LUA_USE_WINDOWS /* enable goodies for regular Windows */ #endif #if defined(LUA_USE_WINDOWS) #define LUA_DL_DLL /* enable support for DLL */ #define LUA_USE_C89 /* broadly, Windows is C89 */ #endif #if defined(LUA_USE_LINUX) #define LUA_USE_POSIX #define LUA_USE_DLOPEN /* needs an extra library: -ldl */ #endif #if defined(LUA_USE_MACOSX) #define LUA_USE_POSIX #define LUA_USE_DLOPEN /* MacOS does not need -ldl */ #endif /* @@ LUAI_IS32INT is true iff 'int' has (at least) 32 bits. */ #define LUAI_IS32INT ((UINT_MAX >> 30) >= 3) /* }================================================================== */ /* ** {================================================================== ** Configuration for Number types. These options should not be ** set externally, because any other code connected to Lua must ** use the same configuration. ** =================================================================== */ /* @@ LUA_INT_TYPE defines the type for Lua integers. @@ LUA_FLOAT_TYPE defines the type for Lua floats. ** Lua should work fine with any mix of these options supported ** by your C compiler. The usual configurations are 64-bit integers ** and 'double' (the default), 32-bit integers and 'float' (for ** restricted platforms), and 'long'/'double' (for C compilers not ** compliant with C99, which may not have support for 'long long'). */ /* predefined options for LUA_INT_TYPE */ #define LUA_INT_INT 1 #define LUA_INT_LONG 2 #define LUA_INT_LONGLONG 3 /* predefined options for LUA_FLOAT_TYPE */ #define LUA_FLOAT_FLOAT 1 #define LUA_FLOAT_DOUBLE 2 #define LUA_FLOAT_LONGDOUBLE 3 /* Default configuration ('long long' and 'double', for 64-bit Lua) */ #define LUA_INT_DEFAULT LUA_INT_LONGLONG #define LUA_FLOAT_DEFAULT LUA_FLOAT_DOUBLE /* @@ LUA_32BITS enables Lua with 32-bit integers and 32-bit floats. */ #define LUA_32BITS 0 /* @@ LUA_C89_NUMBERS ensures that Lua uses the largest types available for ** C89 ('long' and 'double'); Windows always has '__int64', so it does ** not need to use this case. */ #if defined(LUA_USE_C89) && !defined(LUA_USE_WINDOWS) #define LUA_C89_NUMBERS 1 #else #define LUA_C89_NUMBERS 0 #endif #if LUA_32BITS /* { */ /* ** 32-bit integers and 'float' */ #if LUAI_IS32INT /* use 'int' if big enough */ #define LUA_INT_TYPE LUA_INT_INT #else /* otherwise use 'long' */ #define LUA_INT_TYPE LUA_INT_LONG #endif #define LUA_FLOAT_TYPE LUA_FLOAT_FLOAT #elif LUA_C89_NUMBERS /* }{ */ /* ** largest types available for C89 ('long' and 'double') */ #define LUA_INT_TYPE LUA_INT_LONG #define LUA_FLOAT_TYPE LUA_FLOAT_DOUBLE #else /* }{ */ /* use defaults */ #define LUA_INT_TYPE LUA_INT_DEFAULT #define LUA_FLOAT_TYPE LUA_FLOAT_DEFAULT #endif /* } */ /* }================================================================== */ /* ** {================================================================== ** Configuration for Paths. ** =================================================================== */ /* ** LUA_PATH_SEP is the character that separates templates in a path. ** LUA_PATH_MARK is the string that marks the substitution points in a ** template. ** LUA_EXEC_DIR in a Windows path is replaced by the executable's ** directory. */ #define LUA_PATH_SEP ";" #define LUA_PATH_MARK "?" #define LUA_EXEC_DIR "!" /* @@ LUA_PATH_DEFAULT is the default path that Lua uses to look for ** Lua libraries. @@ LUA_CPATH_DEFAULT is the default path that Lua uses to look for ** C libraries. ** CHANGE them if your machine has a non-conventional directory ** hierarchy or if you want to install your libraries in ** non-conventional directories. */ #define LUA_VDIR LUA_VERSION_MAJOR "." LUA_VERSION_MINOR #if defined(_WIN32) /* { */ /* ** In Windows, any exclamation mark ('!') in the path is replaced by the ** path of the directory of the executable file of the current process. */ #define LUA_LDIR "!\\lua\\" #define LUA_CDIR "!\\" #define LUA_SHRDIR "!\\..\\share\\lua\\" LUA_VDIR "\\" #if !defined(LUA_PATH_DEFAULT) #define LUA_PATH_DEFAULT \ LUA_LDIR"?.lua;" LUA_LDIR"?\\init.lua;" \ LUA_CDIR"?.lua;" LUA_CDIR"?\\init.lua;" \ LUA_SHRDIR"?.lua;" LUA_SHRDIR"?\\init.lua;" \ ".\\?.lua;" ".\\?\\init.lua" #endif #if !defined(LUA_CPATH_DEFAULT) #define LUA_CPATH_DEFAULT \ LUA_CDIR"?.dll;" \ LUA_CDIR"..\\lib\\lua\\" LUA_VDIR "\\?.dll;" \ LUA_CDIR"loadall.dll;" ".\\?.dll" #endif #else /* }{ */ #define LUA_ROOT "/usr/local/" #define LUA_LDIR LUA_ROOT "share/lua/" LUA_VDIR "/" #define LUA_CDIR LUA_ROOT "lib/lua/" LUA_VDIR "/" #if !defined(LUA_PATH_DEFAULT) #define LUA_PATH_DEFAULT \ LUA_LDIR"?.lua;" LUA_LDIR"?/init.lua;" \ LUA_CDIR"?.lua;" LUA_CDIR"?/init.lua;" \ "./?.lua;" "./?/init.lua" #endif #if !defined(LUA_CPATH_DEFAULT) #define LUA_CPATH_DEFAULT \ LUA_CDIR"?.so;" LUA_CDIR"loadall.so;" "./?.so" #endif #endif /* } */ /* @@ LUA_DIRSEP is the directory separator (for submodules). ** CHANGE it if your machine does not use "/" as the directory separator ** and is not Windows. (On Windows Lua automatically uses "\".) */ #if !defined(LUA_DIRSEP) #if defined(_WIN32) #define LUA_DIRSEP "\\" #else #define LUA_DIRSEP "/" #endif #endif /* }================================================================== */ /* ** {================================================================== ** Marks for exported symbols in the C code ** =================================================================== */ /* @@ LUA_API is a mark for all core API functions. @@ LUALIB_API is a mark for all auxiliary library functions. @@ LUAMOD_API is a mark for all standard library opening functions. ** CHANGE them if you need to define those functions in some special way. ** For instance, if you want to create one Windows DLL with the core and ** the libraries, you may want to use the following definition (define ** LUA_BUILD_AS_DLL to get it). */ #if defined(LUA_BUILD_AS_DLL) /* { */ #if defined(LUA_CORE) || defined(LUA_LIB) /* { */ #define LUA_API __declspec(dllexport) #else /* }{ */ #define LUA_API __declspec(dllimport) #endif /* } */ #else /* }{ */ #define LUA_API extern #endif /* } */ /* ** More often than not the libs go together with the core. */ #define LUALIB_API LUA_API #define LUAMOD_API LUA_API /* @@ LUAI_FUNC is a mark for all extern functions that are not to be ** exported to outside modules. @@ LUAI_DDEF and LUAI_DDEC are marks for all extern (const) variables, ** none of which to be exported to outside modules (LUAI_DDEF for ** definitions and LUAI_DDEC for declarations). ** CHANGE them if you need to mark them in some special way. Elf/gcc ** (versions 3.2 and later) mark them as "hidden" to optimize access ** when Lua is compiled as a shared library. Not all elf targets support ** this attribute. Unfortunately, gcc does not offer a way to check ** whether the target offers that support, and those without support ** give a warning about it. To avoid these warnings, change to the ** default definition. */ #if defined(__GNUC__) && ((__GNUC__*100 + __GNUC_MINOR__) >= 302) && \ defined(__ELF__) /* { */ #define LUAI_FUNC __attribute__((visibility("internal"))) extern #else /* }{ */ #define LUAI_FUNC extern #endif /* } */ #define LUAI_DDEC(dec) LUAI_FUNC dec #define LUAI_DDEF /* empty */ /* }================================================================== */ /* ** {================================================================== ** Compatibility with previous versions ** =================================================================== */ /* @@ LUA_COMPAT_5_3 controls other macros for compatibility with Lua 5.3. ** You can define it to get all options, or change specific options ** to fit your specific needs. */ #if defined(LUA_COMPAT_5_3) /* { */ /* @@ LUA_COMPAT_MATHLIB controls the presence of several deprecated ** functions in the mathematical library. ** (These functions were already officially removed in 5.3; ** nevertheless they are still available here.) */ #define LUA_COMPAT_MATHLIB /* @@ LUA_COMPAT_APIINTCASTS controls the presence of macros for ** manipulating other integer types (lua_pushunsigned, lua_tounsigned, ** luaL_checkint, luaL_checklong, etc.) ** (These macros were also officially removed in 5.3, but they are still ** available here.) */ #define LUA_COMPAT_APIINTCASTS /* @@ LUA_COMPAT_LT_LE controls the emulation of the '__le' metamethod ** using '__lt'. */ #define LUA_COMPAT_LT_LE /* @@ The following macros supply trivial compatibility for some ** changes in the API. The macros themselves document how to ** change your code to avoid using them. ** (Once more, these macros were officially removed in 5.3, but they are ** still available here.) */ #define lua_strlen(L,i) lua_rawlen(L, (i)) #define lua_objlen(L,i) lua_rawlen(L, (i)) #define lua_equal(L,idx1,idx2) lua_compare(L,(idx1),(idx2),LUA_OPEQ) #define lua_lessthan(L,idx1,idx2) lua_compare(L,(idx1),(idx2),LUA_OPLT) #endif /* } */ /* }================================================================== */ /* ** {================================================================== ** Configuration for Numbers (low-level part). ** Change these definitions if no predefined LUA_FLOAT_* / LUA_INT_* ** satisfy your needs. ** =================================================================== */ /* @@ LUAI_UACNUMBER is the result of a 'default argument promotion' @@ over a floating number. @@ l_floatatt(x) corrects float attribute 'x' to the proper float type ** by prefixing it with one of FLT/DBL/LDBL. @@ LUA_NUMBER_FRMLEN is the length modifier for writing floats. @@ LUA_NUMBER_FMT is the format for writing floats. @@ lua_number2str converts a float to a string. @@ l_mathop allows the addition of an 'l' or 'f' to all math operations. @@ l_floor takes the floor of a float. @@ lua_str2number converts a decimal numeral to a number. */ /* The following definitions are good for most cases here */ #define l_floor(x) (l_mathop(floor)(x)) #define lua_number2str(s,sz,n) \ l_sprintf((s), sz, LUA_NUMBER_FMT, (LUAI_UACNUMBER)(n)) /* @@ lua_numbertointeger converts a float number with an integral value ** to an integer, or returns 0 if float is not within the range of ** a lua_Integer. (The range comparisons are tricky because of ** rounding. The tests here assume a two-complement representation, ** where MININTEGER always has an exact representation as a float; ** MAXINTEGER may not have one, and therefore its conversion to float ** may have an ill-defined value.) */ #define lua_numbertointeger(n,p) \ ((n) >= (LUA_NUMBER)(LUA_MININTEGER) && \ (n) < -(LUA_NUMBER)(LUA_MININTEGER) && \ (*(p) = (LUA_INTEGER)(n), 1)) /* now the variable definitions */ #if LUA_FLOAT_TYPE == LUA_FLOAT_FLOAT /* { single float */ #define LUA_NUMBER float #define l_floatatt(n) (FLT_##n) #define LUAI_UACNUMBER double #define LUA_NUMBER_FRMLEN "" #define LUA_NUMBER_FMT "%.7g" #define l_mathop(op) op##f #define lua_str2number(s,p) strtof((s), (p)) #elif LUA_FLOAT_TYPE == LUA_FLOAT_LONGDOUBLE /* }{ long double */ #define LUA_NUMBER long double #define l_floatatt(n) (LDBL_##n) #define LUAI_UACNUMBER long double #define LUA_NUMBER_FRMLEN "L" #define LUA_NUMBER_FMT "%.19Lg" #define l_mathop(op) op##l #define lua_str2number(s,p) strtold((s), (p)) #elif LUA_FLOAT_TYPE == LUA_FLOAT_DOUBLE /* }{ double */ #define LUA_NUMBER double #define l_floatatt(n) (DBL_##n) #define LUAI_UACNUMBER double #define LUA_NUMBER_FRMLEN "" #define LUA_NUMBER_FMT "%.14g" #define l_mathop(op) op #define lua_str2number(s,p) strtod((s), (p)) #else /* }{ */ #error "numeric float type not defined" #endif /* } */ /* @@ LUA_UNSIGNED is the unsigned version of LUA_INTEGER. @@ LUAI_UACINT is the result of a 'default argument promotion' @@ over a LUA_INTEGER. @@ LUA_INTEGER_FRMLEN is the length modifier for reading/writing integers. @@ LUA_INTEGER_FMT is the format for writing integers. @@ LUA_MAXINTEGER is the maximum value for a LUA_INTEGER. @@ LUA_MININTEGER is the minimum value for a LUA_INTEGER. @@ LUA_MAXUNSIGNED is the maximum value for a LUA_UNSIGNED. @@ lua_integer2str converts an integer to a string. */ /* The following definitions are good for most cases here */ #define LUA_INTEGER_FMT "%" LUA_INTEGER_FRMLEN "d" #define LUAI_UACINT LUA_INTEGER #define lua_integer2str(s,sz,n) \ l_sprintf((s), sz, LUA_INTEGER_FMT, (LUAI_UACINT)(n)) /* ** use LUAI_UACINT here to avoid problems with promotions (which ** can turn a comparison between unsigneds into a signed comparison) */ #define LUA_UNSIGNED unsigned LUAI_UACINT /* now the variable definitions */ #if LUA_INT_TYPE == LUA_INT_INT /* { int */ #define LUA_INTEGER int #define LUA_INTEGER_FRMLEN "" #define LUA_MAXINTEGER INT_MAX #define LUA_MININTEGER INT_MIN #define LUA_MAXUNSIGNED UINT_MAX #elif LUA_INT_TYPE == LUA_INT_LONG /* }{ long */ #define LUA_INTEGER long #define LUA_INTEGER_FRMLEN "l" #define LUA_MAXINTEGER LONG_MAX #define LUA_MININTEGER LONG_MIN #define LUA_MAXUNSIGNED ULONG_MAX #elif LUA_INT_TYPE == LUA_INT_LONGLONG /* }{ long long */ /* use presence of macro LLONG_MAX as proxy for C99 compliance */ #if defined(LLONG_MAX) /* { */ /* use ISO C99 stuff */ #define LUA_INTEGER long long #define LUA_INTEGER_FRMLEN "ll" #define LUA_MAXINTEGER LLONG_MAX #define LUA_MININTEGER LLONG_MIN #define LUA_MAXUNSIGNED ULLONG_MAX #elif defined(LUA_USE_WINDOWS) /* }{ */ /* in Windows, can use specific Windows types */ #define LUA_INTEGER __int64 #define LUA_INTEGER_FRMLEN "I64" #define LUA_MAXINTEGER _I64_MAX #define LUA_MININTEGER _I64_MIN #define LUA_MAXUNSIGNED _UI64_MAX #else /* }{ */ #error "Compiler does not support 'long long'. Use option '-DLUA_32BITS' \ or '-DLUA_C89_NUMBERS' (see file 'luaconf.h' for details)" #endif /* } */ #else /* }{ */ #error "numeric integer type not defined" #endif /* } */ /* }================================================================== */ /* ** {================================================================== ** Dependencies with C99 and other C details ** =================================================================== */ /* @@ l_sprintf is equivalent to 'snprintf' or 'sprintf' in C89. ** (All uses in Lua have only one format item.) */ #if !defined(LUA_USE_C89) #define l_sprintf(s,sz,f,i) snprintf(s,sz,f,i) #else #define l_sprintf(s,sz,f,i) ((void)(sz), sprintf(s,f,i)) #endif /* @@ lua_strx2number converts a hexadecimal numeral to a number. ** In C99, 'strtod' does that conversion. Otherwise, you can ** leave 'lua_strx2number' undefined and Lua will provide its own ** implementation. */ #if !defined(LUA_USE_C89) #define lua_strx2number(s,p) lua_str2number(s,p) #endif /* @@ lua_pointer2str converts a pointer to a readable string in a ** non-specified way. */ #define lua_pointer2str(buff,sz,p) l_sprintf(buff,sz,"%p",p) /* @@ lua_number2strx converts a float to a hexadecimal numeral. ** In C99, 'sprintf' (with format specifiers '%a'/'%A') does that. ** Otherwise, you can leave 'lua_number2strx' undefined and Lua will ** provide its own implementation. */ #if !defined(LUA_USE_C89) #define lua_number2strx(L,b,sz,f,n) \ ((void)L, l_sprintf(b,sz,f,(LUAI_UACNUMBER)(n))) #endif /* ** 'strtof' and 'opf' variants for math functions are not valid in ** C89. Otherwise, the macro 'HUGE_VALF' is a good proxy for testing the ** availability of these variants. ('math.h' is already included in ** all files that use these macros.) */ #if defined(LUA_USE_C89) || (defined(HUGE_VAL) && !defined(HUGE_VALF)) #undef l_mathop /* variants not available */ #undef lua_str2number #define l_mathop(op) (lua_Number)op /* no variant */ #define lua_str2number(s,p) ((lua_Number)strtod((s), (p))) #endif /* @@ LUA_KCONTEXT is the type of the context ('ctx') for continuation ** functions. It must be a numerical type; Lua will use 'intptr_t' if ** available, otherwise it will use 'ptrdiff_t' (the nearest thing to ** 'intptr_t' in C89) */ #define LUA_KCONTEXT ptrdiff_t #if !defined(LUA_USE_C89) && defined(__STDC_VERSION__) && \ __STDC_VERSION__ >= 199901L #include #if defined(INTPTR_MAX) /* even in C99 this type is optional */ #undef LUA_KCONTEXT #define LUA_KCONTEXT intptr_t #endif #endif /* @@ lua_getlocaledecpoint gets the locale "radix character" (decimal point). ** Change that if you do not want to use C locales. (Code using this ** macro must include the header 'locale.h'.) */ #if !defined(lua_getlocaledecpoint) #define lua_getlocaledecpoint() (localeconv()->decimal_point[0]) #endif /* ** macros to improve jump prediction, used mostly for error handling ** and debug facilities. (Some macros in the Lua API use these macros. ** Define LUA_NOBUILTIN if you do not want '__builtin_expect' in your ** code.) */ #if !defined(luai_likely) #if defined(__GNUC__) && !defined(LUA_NOBUILTIN) #define luai_likely(x) (__builtin_expect(((x) != 0), 1)) #define luai_unlikely(x) (__builtin_expect(((x) != 0), 0)) #else #define luai_likely(x) (x) #define luai_unlikely(x) (x) #endif #endif #if defined(LUA_CORE) || defined(LUA_LIB) /* shorter names for Lua's own use */ #define l_likely(x) luai_likely(x) #define l_unlikely(x) luai_unlikely(x) #endif /* }================================================================== */ /* ** {================================================================== ** Language Variations ** ===================================================================== */ /* @@ LUA_NOCVTN2S/LUA_NOCVTS2N control how Lua performs some ** coercions. Define LUA_NOCVTN2S to turn off automatic coercion from ** numbers to strings. Define LUA_NOCVTS2N to turn off automatic ** coercion from strings to numbers. */ /* #define LUA_NOCVTN2S */ /* #define LUA_NOCVTS2N */ /* @@ LUA_USE_APICHECK turns on several consistency checks on the C API. ** Define it as a help when debugging C code. */ #if defined(LUA_USE_APICHECK) #include #define luai_apicheck(l,e) assert(e) #endif /* }================================================================== */ /* ** {================================================================== ** Macros that affect the API and must be stable (that is, must be the ** same when you compile Lua and when you compile code that links to ** Lua). ** ===================================================================== */ /* @@ LUAI_MAXSTACK limits the size of the Lua stack. ** CHANGE it if you need a different limit. This limit is arbitrary; ** its only purpose is to stop Lua from consuming unlimited stack ** space (and to reserve some numbers for pseudo-indices). ** (It must fit into max(size_t)/32.) */ #if LUAI_IS32INT #define LUAI_MAXSTACK 1000000 #else #define LUAI_MAXSTACK 15000 #endif /* @@ LUA_EXTRASPACE defines the size of a raw memory area associated with ** a Lua state with very fast access. ** CHANGE it if you need a different size. */ #define LUA_EXTRASPACE (sizeof(void *)) /* @@ LUA_IDSIZE gives the maximum size for the description of the source @@ of a function in debug information. ** CHANGE it if you want a different size. */ #define LUA_IDSIZE 60 /* @@ LUAL_BUFFERSIZE is the buffer size used by the lauxlib buffer system. */ #define LUAL_BUFFERSIZE ((int)(16 * sizeof(void*) * sizeof(lua_Number))) /* @@ LUAI_MAXALIGN defines fields that, when used in a union, ensure ** maximum alignment for the other items in that union. */ #define LUAI_MAXALIGN lua_Number n; double u; void *s; lua_Integer i; long l /* }================================================================== */ /* =================================================================== */ /* ** Local configuration. You can use this space to add your redefinitions ** without modifying the main part of the file. */ #endif /* ** $Id: lua.h $ ** Lua - A Scripting Language ** Lua.org, PUC-Rio, Brazil (http://www.lua.org) ** See Copyright Notice at the end of this file */ #ifndef lua_h #define lua_h #include #include /*#include "luaconf.h"*/ #define LUA_VERSION_MAJOR "5" #define LUA_VERSION_MINOR "4" #define LUA_VERSION_RELEASE "4" #define LUA_VERSION_NUM 504 #define LUA_VERSION_RELEASE_NUM (LUA_VERSION_NUM * 100 + 4) #define LUA_VERSION "Lua " LUA_VERSION_MAJOR "." LUA_VERSION_MINOR #define LUA_RELEASE LUA_VERSION "." LUA_VERSION_RELEASE #define LUA_COPYRIGHT LUA_RELEASE " Copyright (C) 1994-2022 Lua.org, PUC-Rio" #define LUA_AUTHORS "R. Ierusalimschy, L. H. de Figueiredo, W. Celes" /* mark for precompiled code ('Lua') */ #define LUA_SIGNATURE "\x1bLua" /* option for multiple returns in 'lua_pcall' and 'lua_call' */ #define LUA_MULTRET (-1) /* ** Pseudo-indices ** (-LUAI_MAXSTACK is the minimum valid index; we keep some free empty ** space after that to help overflow detection) */ #define LUA_REGISTRYINDEX (-LUAI_MAXSTACK - 1000) #define lua_upvalueindex(i) (LUA_REGISTRYINDEX - (i)) /* thread status */ #define LUA_OK 0 #define LUA_YIELD 1 #define LUA_ERRRUN 2 #define LUA_ERRSYNTAX 3 #define LUA_ERRMEM 4 #define LUA_ERRERR 5 typedef struct lua_State lua_State; /* ** basic types */ #define LUA_TNONE (-1) #define LUA_TNIL 0 #define LUA_TBOOLEAN 1 #define LUA_TLIGHTUSERDATA 2 #define LUA_TNUMBER 3 #define LUA_TSTRING 4 #define LUA_TTABLE 5 #define LUA_TFUNCTION 6 #define LUA_TUSERDATA 7 #define LUA_TTHREAD 8 #define LUA_NUMTYPES 9 /* minimum Lua stack available to a C function */ #define LUA_MINSTACK 20 /* predefined values in the registry */ #define LUA_RIDX_MAINTHREAD 1 #define LUA_RIDX_GLOBALS 2 #define LUA_RIDX_LAST LUA_RIDX_GLOBALS /* type of numbers in Lua */ typedef LUA_NUMBER lua_Number; /* type for integer functions */ typedef LUA_INTEGER lua_Integer; /* unsigned integer type */ typedef LUA_UNSIGNED lua_Unsigned; /* type for continuation-function contexts */ typedef LUA_KCONTEXT lua_KContext; /* ** Type for C functions registered with Lua */ typedef int (*lua_CFunction) (lua_State *L); /* ** Type for continuation functions */ typedef int (*lua_KFunction) (lua_State *L, int status, lua_KContext ctx); /* ** Type for functions that read/write blocks when loading/dumping Lua chunks */ typedef const char * (*lua_Reader) (lua_State *L, void *ud, size_t *sz); typedef int (*lua_Writer) (lua_State *L, const void *p, size_t sz, void *ud); /* ** Type for memory-allocation functions */ typedef void * (*lua_Alloc) (void *ud, void *ptr, size_t osize, size_t nsize); /* ** Type for warning functions */ typedef void (*lua_WarnFunction) (void *ud, const char *msg, int tocont); /* ** generic extra include file */ #if defined(LUA_USER_H) #include LUA_USER_H #endif /* ** RCS ident string */ extern const char lua_ident[]; /* ** state manipulation */ LUA_API lua_State *(lua_newstate) (lua_Alloc f, void *ud); LUA_API void (lua_close) (lua_State *L); LUA_API lua_State *(lua_newthread) (lua_State *L); LUA_API int (lua_resetthread) (lua_State *L); LUA_API lua_CFunction (lua_atpanic) (lua_State *L, lua_CFunction panicf); LUA_API lua_Number (lua_version) (lua_State *L); /* ** basic stack manipulation */ LUA_API int (lua_absindex) (lua_State *L, int idx); LUA_API int (lua_gettop) (lua_State *L); LUA_API void (lua_settop) (lua_State *L, int idx); LUA_API void (lua_pushvalue) (lua_State *L, int idx); LUA_API void (lua_rotate) (lua_State *L, int idx, int n); LUA_API void (lua_copy) (lua_State *L, int fromidx, int toidx); LUA_API int (lua_checkstack) (lua_State *L, int n); LUA_API void (lua_xmove) (lua_State *from, lua_State *to, int n); /* ** access functions (stack -> C) */ LUA_API int (lua_isnumber) (lua_State *L, int idx); LUA_API int (lua_isstring) (lua_State *L, int idx); LUA_API int (lua_iscfunction) (lua_State *L, int idx); LUA_API int (lua_isinteger) (lua_State *L, int idx); LUA_API int (lua_isuserdata) (lua_State *L, int idx); LUA_API int (lua_type) (lua_State *L, int idx); LUA_API const char *(lua_typename) (lua_State *L, int tp); LUA_API lua_Number (lua_tonumberx) (lua_State *L, int idx, int *isnum); LUA_API lua_Integer (lua_tointegerx) (lua_State *L, int idx, int *isnum); LUA_API int (lua_toboolean) (lua_State *L, int idx); LUA_API const char *(lua_tolstring) (lua_State *L, int idx, size_t *len); LUA_API lua_Unsigned (lua_rawlen) (lua_State *L, int idx); LUA_API lua_CFunction (lua_tocfunction) (lua_State *L, int idx); LUA_API void *(lua_touserdata) (lua_State *L, int idx); LUA_API lua_State *(lua_tothread) (lua_State *L, int idx); LUA_API const void *(lua_topointer) (lua_State *L, int idx); /* ** Comparison and arithmetic functions */ #define LUA_OPADD 0 /* ORDER TM, ORDER OP */ #define LUA_OPSUB 1 #define LUA_OPMUL 2 #define LUA_OPMOD 3 #define LUA_OPPOW 4 #define LUA_OPDIV 5 #define LUA_OPIDIV 6 #define LUA_OPBAND 7 #define LUA_OPBOR 8 #define LUA_OPBXOR 9 #define LUA_OPSHL 10 #define LUA_OPSHR 11 #define LUA_OPUNM 12 #define LUA_OPBNOT 13 LUA_API void (lua_arith) (lua_State *L, int op); #define LUA_OPEQ 0 #define LUA_OPLT 1 #define LUA_OPLE 2 LUA_API int (lua_rawequal) (lua_State *L, int idx1, int idx2); LUA_API int (lua_compare) (lua_State *L, int idx1, int idx2, int op); /* ** push functions (C -> stack) */ LUA_API void (lua_pushnil) (lua_State *L); LUA_API void (lua_pushnumber) (lua_State *L, lua_Number n); LUA_API void (lua_pushinteger) (lua_State *L, lua_Integer n); LUA_API const char *(lua_pushlstring) (lua_State *L, const char *s, size_t len); LUA_API const char *(lua_pushstring) (lua_State *L, const char *s); LUA_API const char *(lua_pushvfstring) (lua_State *L, const char *fmt, va_list argp); LUA_API const char *(lua_pushfstring) (lua_State *L, const char *fmt, ...); LUA_API void (lua_pushcclosure) (lua_State *L, lua_CFunction fn, int n); LUA_API void (lua_pushboolean) (lua_State *L, int b); LUA_API void (lua_pushlightuserdata) (lua_State *L, void *p); LUA_API int (lua_pushthread) (lua_State *L); /* ** get functions (Lua -> stack) */ LUA_API int (lua_getglobal) (lua_State *L, const char *name); LUA_API int (lua_gettable) (lua_State *L, int idx); LUA_API int (lua_getfield) (lua_State *L, int idx, const char *k); LUA_API int (lua_geti) (lua_State *L, int idx, lua_Integer n); LUA_API int (lua_rawget) (lua_State *L, int idx); LUA_API int (lua_rawgeti) (lua_State *L, int idx, lua_Integer n); LUA_API int (lua_rawgetp) (lua_State *L, int idx, const void *p); LUA_API void (lua_createtable) (lua_State *L, int narr, int nrec); LUA_API void *(lua_newuserdatauv) (lua_State *L, size_t sz, int nuvalue); LUA_API int (lua_getmetatable) (lua_State *L, int objindex); LUA_API int (lua_getiuservalue) (lua_State *L, int idx, int n); /* ** set functions (stack -> Lua) */ LUA_API void (lua_setglobal) (lua_State *L, const char *name); LUA_API void (lua_settable) (lua_State *L, int idx); LUA_API void (lua_setfield) (lua_State *L, int idx, const char *k); LUA_API void (lua_seti) (lua_State *L, int idx, lua_Integer n); LUA_API void (lua_rawset) (lua_State *L, int idx); LUA_API void (lua_rawseti) (lua_State *L, int idx, lua_Integer n); LUA_API void (lua_rawsetp) (lua_State *L, int idx, const void *p); LUA_API int (lua_setmetatable) (lua_State *L, int objindex); LUA_API int (lua_setiuservalue) (lua_State *L, int idx, int n); /* ** 'load' and 'call' functions (load and run Lua code) */ LUA_API void (lua_callk) (lua_State *L, int nargs, int nresults, lua_KContext ctx, lua_KFunction k); #define lua_call(L,n,r) lua_callk(L, (n), (r), 0, NULL) LUA_API int (lua_pcallk) (lua_State *L, int nargs, int nresults, int errfunc, lua_KContext ctx, lua_KFunction k); #define lua_pcall(L,n,r,f) lua_pcallk(L, (n), (r), (f), 0, NULL) LUA_API int (lua_load) (lua_State *L, lua_Reader reader, void *dt, const char *chunkname, const char *mode); LUA_API int (lua_dump) (lua_State *L, lua_Writer writer, void *data, int strip); /* ** coroutine functions */ LUA_API int (lua_yieldk) (lua_State *L, int nresults, lua_KContext ctx, lua_KFunction k); LUA_API int (lua_resume) (lua_State *L, lua_State *from, int narg, int *nres); LUA_API int (lua_status) (lua_State *L); LUA_API int (lua_isyieldable) (lua_State *L); #define lua_yield(L,n) lua_yieldk(L, (n), 0, NULL) /* ** Warning-related functions */ LUA_API void (lua_setwarnf) (lua_State *L, lua_WarnFunction f, void *ud); LUA_API void (lua_warning) (lua_State *L, const char *msg, int tocont); /* ** garbage-collection function and options */ #define LUA_GCSTOP 0 #define LUA_GCRESTART 1 #define LUA_GCCOLLECT 2 #define LUA_GCCOUNT 3 #define LUA_GCCOUNTB 4 #define LUA_GCSTEP 5 #define LUA_GCSETPAUSE 6 #define LUA_GCSETSTEPMUL 7 #define LUA_GCISRUNNING 9 #define LUA_GCGEN 10 #define LUA_GCINC 11 LUA_API int (lua_gc) (lua_State *L, int what, ...); /* ** miscellaneous functions */ LUA_API int (lua_error) (lua_State *L); LUA_API int (lua_next) (lua_State *L, int idx); LUA_API void (lua_concat) (lua_State *L, int n); LUA_API void (lua_len) (lua_State *L, int idx); LUA_API size_t (lua_stringtonumber) (lua_State *L, const char *s); LUA_API lua_Alloc (lua_getallocf) (lua_State *L, void **ud); LUA_API void (lua_setallocf) (lua_State *L, lua_Alloc f, void *ud); LUA_API void (lua_toclose) (lua_State *L, int idx); LUA_API void (lua_closeslot) (lua_State *L, int idx); /* ** {============================================================== ** some useful macros ** =============================================================== */ #define lua_getextraspace(L) ((void *)((char *)(L) - LUA_EXTRASPACE)) #define lua_tonumber(L,i) lua_tonumberx(L,(i),NULL) #define lua_tointeger(L,i) lua_tointegerx(L,(i),NULL) #define lua_pop(L,n) lua_settop(L, -(n)-1) #define lua_newtable(L) lua_createtable(L, 0, 0) #define lua_register(L,n,f) (lua_pushcfunction(L, (f)), lua_setglobal(L, (n))) #define lua_pushcfunction(L,f) lua_pushcclosure(L, (f), 0) #define lua_isfunction(L,n) (lua_type(L, (n)) == LUA_TFUNCTION) #define lua_istable(L,n) (lua_type(L, (n)) == LUA_TTABLE) #define lua_islightuserdata(L,n) (lua_type(L, (n)) == LUA_TLIGHTUSERDATA) #define lua_isnil(L,n) (lua_type(L, (n)) == LUA_TNIL) #define lua_isboolean(L,n) (lua_type(L, (n)) == LUA_TBOOLEAN) #define lua_isthread(L,n) (lua_type(L, (n)) == LUA_TTHREAD) #define lua_isnone(L,n) (lua_type(L, (n)) == LUA_TNONE) #define lua_isnoneornil(L, n) (lua_type(L, (n)) <= 0) #define lua_pushliteral(L, s) lua_pushstring(L, "" s) #define lua_pushglobaltable(L) \ ((void)lua_rawgeti(L, LUA_REGISTRYINDEX, LUA_RIDX_GLOBALS)) #define lua_tostring(L,i) lua_tolstring(L, (i), NULL) #define lua_insert(L,idx) lua_rotate(L, (idx), 1) #define lua_remove(L,idx) (lua_rotate(L, (idx), -1), lua_pop(L, 1)) #define lua_replace(L,idx) (lua_copy(L, -1, (idx)), lua_pop(L, 1)) /* }============================================================== */ /* ** {============================================================== ** compatibility macros ** =============================================================== */ #if defined(LUA_COMPAT_APIINTCASTS) #define lua_pushunsigned(L,n) lua_pushinteger(L, (lua_Integer)(n)) #define lua_tounsignedx(L,i,is) ((lua_Unsigned)lua_tointegerx(L,i,is)) #define lua_tounsigned(L,i) lua_tounsignedx(L,(i),NULL) #endif #define lua_newuserdata(L,s) lua_newuserdatauv(L,s,1) #define lua_getuservalue(L,idx) lua_getiuservalue(L,idx,1) #define lua_setuservalue(L,idx) lua_setiuservalue(L,idx,1) #define LUA_NUMTAGS LUA_NUMTYPES /* }============================================================== */ /* ** {====================================================================== ** Debug API ** ======================================================================= */ /* ** Event codes */ #define LUA_HOOKCALL 0 #define LUA_HOOKRET 1 #define LUA_HOOKLINE 2 #define LUA_HOOKCOUNT 3 #define LUA_HOOKTAILCALL 4 /* ** Event masks */ #define LUA_MASKCALL (1 << LUA_HOOKCALL) #define LUA_MASKRET (1 << LUA_HOOKRET) #define LUA_MASKLINE (1 << LUA_HOOKLINE) #define LUA_MASKCOUNT (1 << LUA_HOOKCOUNT) typedef struct lua_Debug lua_Debug; /* activation record */ /* Functions to be called by the debugger in specific events */ typedef void (*lua_Hook) (lua_State *L, lua_Debug *ar); LUA_API int (lua_getstack) (lua_State *L, int level, lua_Debug *ar); LUA_API int (lua_getinfo) (lua_State *L, const char *what, lua_Debug *ar); LUA_API const char *(lua_getlocal) (lua_State *L, const lua_Debug *ar, int n); LUA_API const char *(lua_setlocal) (lua_State *L, const lua_Debug *ar, int n); LUA_API const char *(lua_getupvalue) (lua_State *L, int funcindex, int n); LUA_API const char *(lua_setupvalue) (lua_State *L, int funcindex, int n); LUA_API void *(lua_upvalueid) (lua_State *L, int fidx, int n); LUA_API void (lua_upvaluejoin) (lua_State *L, int fidx1, int n1, int fidx2, int n2); LUA_API void (lua_sethook) (lua_State *L, lua_Hook func, int mask, int count); LUA_API lua_Hook (lua_gethook) (lua_State *L); LUA_API int (lua_gethookmask) (lua_State *L); LUA_API int (lua_gethookcount) (lua_State *L); LUA_API int (lua_setcstacklimit) (lua_State *L, unsigned int limit); struct lua_Debug { int event; const char *name; /* (n) */ const char *namewhat; /* (n) 'global', 'local', 'field', 'method' */ const char *what; /* (S) 'Lua', 'C', 'main', 'tail' */ const char *source; /* (S) */ size_t srclen; /* (S) */ int currentline; /* (l) */ int linedefined; /* (S) */ int lastlinedefined; /* (S) */ unsigned char nups; /* (u) number of upvalues */ unsigned char nparams;/* (u) number of parameters */ char isvararg; /* (u) */ char istailcall; /* (t) */ unsigned short ftransfer; /* (r) index of first value transferred */ unsigned short ntransfer; /* (r) number of transferred values */ char short_src[LUA_IDSIZE]; /* (S) */ /* private part */ struct CallInfo *i_ci; /* active function */ }; /* }====================================================================== */ /****************************************************************************** * Copyright (C) 1994-2022 Lua.org, PUC-Rio. * * 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. ******************************************************************************/ #endif /* ** $Id: lauxlib.h $ ** Auxiliary functions for building Lua libraries ** See Copyright Notice in lua.h */ #ifndef lauxlib_h #define lauxlib_h #include #include /*#include "luaconf.h"*/ /*#include "lua.h"*/ /* global table */ #define LUA_GNAME "_G" typedef struct luaL_Buffer luaL_Buffer; /* extra error code for 'luaL_loadfilex' */ #define LUA_ERRFILE (LUA_ERRERR+1) /* key, in the registry, for table of loaded modules */ #define LUA_LOADED_TABLE "_LOADED" /* key, in the registry, for table of preloaded loaders */ #define LUA_PRELOAD_TABLE "_PRELOAD" typedef struct luaL_Reg { const char *name; lua_CFunction func; } luaL_Reg; #define LUAL_NUMSIZES (sizeof(lua_Integer)*16 + sizeof(lua_Number)) LUALIB_API void (luaL_checkversion_) (lua_State *L, lua_Number ver, size_t sz); #define luaL_checkversion(L) \ luaL_checkversion_(L, LUA_VERSION_NUM, LUAL_NUMSIZES) LUALIB_API int (luaL_getmetafield) (lua_State *L, int obj, const char *e); LUALIB_API int (luaL_callmeta) (lua_State *L, int obj, const char *e); LUALIB_API const char *(luaL_tolstring) (lua_State *L, int idx, size_t *len); LUALIB_API int (luaL_argerror) (lua_State *L, int arg, const char *extramsg); LUALIB_API int (luaL_typeerror) (lua_State *L, int arg, const char *tname); LUALIB_API const char *(luaL_checklstring) (lua_State *L, int arg, size_t *l); LUALIB_API const char *(luaL_optlstring) (lua_State *L, int arg, const char *def, size_t *l); LUALIB_API lua_Number (luaL_checknumber) (lua_State *L, int arg); LUALIB_API lua_Number (luaL_optnumber) (lua_State *L, int arg, lua_Number def); LUALIB_API lua_Integer (luaL_checkinteger) (lua_State *L, int arg); LUALIB_API lua_Integer (luaL_optinteger) (lua_State *L, int arg, lua_Integer def); LUALIB_API void (luaL_checkstack) (lua_State *L, int sz, const char *msg); LUALIB_API void (luaL_checktype) (lua_State *L, int arg, int t); LUALIB_API void (luaL_checkany) (lua_State *L, int arg); LUALIB_API int (luaL_newmetatable) (lua_State *L, const char *tname); LUALIB_API void (luaL_setmetatable) (lua_State *L, const char *tname); LUALIB_API void *(luaL_testudata) (lua_State *L, int ud, const char *tname); LUALIB_API void *(luaL_checkudata) (lua_State *L, int ud, const char *tname); LUALIB_API void (luaL_where) (lua_State *L, int lvl); LUALIB_API int (luaL_error) (lua_State *L, const char *fmt, ...); LUALIB_API int (luaL_checkoption) (lua_State *L, int arg, const char *def, const char *const lst[]); LUALIB_API int (luaL_fileresult) (lua_State *L, int stat, const char *fname); LUALIB_API int (luaL_execresult) (lua_State *L, int stat); /* predefined references */ #define LUA_NOREF (-2) #define LUA_REFNIL (-1) LUALIB_API int (luaL_ref) (lua_State *L, int t); LUALIB_API void (luaL_unref) (lua_State *L, int t, int ref); LUALIB_API int (luaL_loadfilex) (lua_State *L, const char *filename, const char *mode); #define luaL_loadfile(L,f) luaL_loadfilex(L,f,NULL) LUALIB_API int (luaL_loadbufferx) (lua_State *L, const char *buff, size_t sz, const char *name, const char *mode); LUALIB_API int (luaL_loadstring) (lua_State *L, const char *s); LUALIB_API lua_State *(luaL_newstate) (void); LUALIB_API lua_Integer (luaL_len) (lua_State *L, int idx); LUALIB_API void (luaL_addgsub) (luaL_Buffer *b, const char *s, const char *p, const char *r); LUALIB_API const char *(luaL_gsub) (lua_State *L, const char *s, const char *p, const char *r); LUALIB_API void (luaL_setfuncs) (lua_State *L, const luaL_Reg *l, int nup); LUALIB_API int (luaL_getsubtable) (lua_State *L, int idx, const char *fname); LUALIB_API void (luaL_traceback) (lua_State *L, lua_State *L1, const char *msg, int level); LUALIB_API void (luaL_requiref) (lua_State *L, const char *modname, lua_CFunction openf, int glb); /* ** =============================================================== ** some useful macros ** =============================================================== */ #define luaL_newlibtable(L,l) \ lua_createtable(L, 0, sizeof(l)/sizeof((l)[0]) - 1) #define luaL_newlib(L,l) \ (luaL_checkversion(L), luaL_newlibtable(L,l), luaL_setfuncs(L,l,0)) #define luaL_argcheck(L, cond,arg,extramsg) \ ((void)(luai_likely(cond) || luaL_argerror(L, (arg), (extramsg)))) #define luaL_argexpected(L,cond,arg,tname) \ ((void)(luai_likely(cond) || luaL_typeerror(L, (arg), (tname)))) #define luaL_checkstring(L,n) (luaL_checklstring(L, (n), NULL)) #define luaL_optstring(L,n,d) (luaL_optlstring(L, (n), (d), NULL)) #define luaL_typename(L,i) lua_typename(L, lua_type(L,(i))) #define luaL_dofile(L, fn) \ (luaL_loadfile(L, fn) || lua_pcall(L, 0, LUA_MULTRET, 0)) #define luaL_dostring(L, s) \ (luaL_loadstring(L, s) || lua_pcall(L, 0, LUA_MULTRET, 0)) #define luaL_getmetatable(L,n) (lua_getfield(L, LUA_REGISTRYINDEX, (n))) #define luaL_opt(L,f,n,d) (lua_isnoneornil(L,(n)) ? (d) : f(L,(n))) #define luaL_loadbuffer(L,s,sz,n) luaL_loadbufferx(L,s,sz,n,NULL) /* ** Perform arithmetic operations on lua_Integer values with wrap-around ** semantics, as the Lua core does. */ #define luaL_intop(op,v1,v2) \ ((lua_Integer)((lua_Unsigned)(v1) op (lua_Unsigned)(v2))) /* push the value used to represent failure/error */ #define luaL_pushfail(L) lua_pushnil(L) /* ** Internal assertions for in-house debugging */ #if !defined(lua_assert) #if defined LUAI_ASSERT #include #define lua_assert(c) assert(c) #else #define lua_assert(c) ((void)0) #endif #endif /* ** {====================================================== ** Generic Buffer manipulation ** ======================================================= */ struct luaL_Buffer { char *b; /* buffer address */ size_t size; /* buffer size */ size_t n; /* number of characters in buffer */ lua_State *L; union { LUAI_MAXALIGN; /* ensure maximum alignment for buffer */ char b[LUAL_BUFFERSIZE]; /* initial buffer */ } init; }; #define luaL_bufflen(bf) ((bf)->n) #define luaL_buffaddr(bf) ((bf)->b) #define luaL_addchar(B,c) \ ((void)((B)->n < (B)->size || luaL_prepbuffsize((B), 1)), \ ((B)->b[(B)->n++] = (c))) #define luaL_addsize(B,s) ((B)->n += (s)) #define luaL_buffsub(B,s) ((B)->n -= (s)) LUALIB_API void (luaL_buffinit) (lua_State *L, luaL_Buffer *B); LUALIB_API char *(luaL_prepbuffsize) (luaL_Buffer *B, size_t sz); LUALIB_API void (luaL_addlstring) (luaL_Buffer *B, const char *s, size_t l); LUALIB_API void (luaL_addstring) (luaL_Buffer *B, const char *s); LUALIB_API void (luaL_addvalue) (luaL_Buffer *B); LUALIB_API void (luaL_pushresult) (luaL_Buffer *B); LUALIB_API void (luaL_pushresultsize) (luaL_Buffer *B, size_t sz); LUALIB_API char *(luaL_buffinitsize) (lua_State *L, luaL_Buffer *B, size_t sz); #define luaL_prepbuffer(B) luaL_prepbuffsize(B, LUAL_BUFFERSIZE) /* }====================================================== */ /* ** {====================================================== ** File handles for IO library ** ======================================================= */ /* ** A file handle is a userdata with metatable 'LUA_FILEHANDLE' and ** initial structure 'luaL_Stream' (it may contain other fields ** after that initial structure). */ #define LUA_FILEHANDLE "FILE*" typedef struct luaL_Stream { FILE *f; /* stream (NULL for incompletely created streams) */ lua_CFunction closef; /* to close stream (NULL for closed streams) */ } luaL_Stream; /* }====================================================== */ /* ** {================================================================== ** "Abstraction Layer" for basic report of messages and errors ** =================================================================== */ /* print a string */ #if !defined(lua_writestring) #define lua_writestring(s,l) fwrite((s), sizeof(char), (l), stdout) #endif /* print a newline and flush the output */ #if !defined(lua_writeline) #define lua_writeline() (lua_writestring("\n", 1), fflush(stdout)) #endif /* print an error message */ #if !defined(lua_writestringerror) #define lua_writestringerror(s,p) \ (fprintf(stderr, (s), (p)), fflush(stderr)) #endif /* }================================================================== */ /* ** {============================================================ ** Compatibility with deprecated conversions ** ============================================================= */ #if defined(LUA_COMPAT_APIINTCASTS) #define luaL_checkunsigned(L,a) ((lua_Unsigned)luaL_checkinteger(L,a)) #define luaL_optunsigned(L,a,d) \ ((lua_Unsigned)luaL_optinteger(L,a,(lua_Integer)(d))) #define luaL_checkint(L,n) ((int)luaL_checkinteger(L, (n))) #define luaL_optint(L,n,d) ((int)luaL_optinteger(L, (n), (d))) #define luaL_checklong(L,n) ((long)luaL_checkinteger(L, (n))) #define luaL_optlong(L,n,d) ((long)luaL_optinteger(L, (n), (d))) #endif /* }============================================================ */ #endif /* ** $Id: lualib.h $ ** Lua standard libraries ** See Copyright Notice in lua.h */ #ifndef lualib_h #define lualib_h /*#include "lua.h"*/ /* version suffix for environment variable names */ #define LUA_VERSUFFIX "_" LUA_VERSION_MAJOR "_" LUA_VERSION_MINOR LUAMOD_API int (luaopen_base) (lua_State *L); #define LUA_COLIBNAME "coroutine" LUAMOD_API int (luaopen_coroutine) (lua_State *L); #define LUA_TABLIBNAME "table" LUAMOD_API int (luaopen_table) (lua_State *L); #define LUA_IOLIBNAME "io" LUAMOD_API int (luaopen_io) (lua_State *L); #define LUA_OSLIBNAME "os" LUAMOD_API int (luaopen_os) (lua_State *L); #define LUA_STRLIBNAME "string" LUAMOD_API int (luaopen_string) (lua_State *L); #define LUA_UTF8LIBNAME "utf8" LUAMOD_API int (luaopen_utf8) (lua_State *L); #define LUA_MATHLIBNAME "math" LUAMOD_API int (luaopen_math) (lua_State *L); #define LUA_DBLIBNAME "debug" LUAMOD_API int (luaopen_debug) (lua_State *L); #define LUA_LOADLIBNAME "package" LUAMOD_API int (luaopen_package) (lua_State *L); /* open all previous libraries */ LUALIB_API void (luaL_openlibs) (lua_State *L); #endif #ifdef LUA_IMPL /* ** $Id: llimits.h $ ** Limits, basic types, and some other 'installation-dependent' definitions ** See Copyright Notice in lua.h */ #ifndef llimits_h #define llimits_h #include #include /*#include "lua.h"*/ /* ** 'lu_mem' and 'l_mem' are unsigned/signed integers big enough to count ** the total memory used by Lua (in bytes). Usually, 'size_t' and ** 'ptrdiff_t' should work, but we use 'long' for 16-bit machines. */ #if defined(LUAI_MEM) /* { external definitions? */ typedef LUAI_UMEM lu_mem; typedef LUAI_MEM l_mem; #elif LUAI_IS32INT /* }{ */ typedef size_t lu_mem; typedef ptrdiff_t l_mem; #else /* 16-bit ints */ /* }{ */ typedef unsigned long lu_mem; typedef long l_mem; #endif /* } */ /* chars used as small naturals (so that 'char' is reserved for characters) */ typedef unsigned char lu_byte; typedef signed char ls_byte; /* maximum value for size_t */ #define MAX_SIZET ((size_t)(~(size_t)0)) /* maximum size visible for Lua (must be representable in a lua_Integer) */ #define MAX_SIZE (sizeof(size_t) < sizeof(lua_Integer) ? MAX_SIZET \ : (size_t)(LUA_MAXINTEGER)) #define MAX_LUMEM ((lu_mem)(~(lu_mem)0)) #define MAX_LMEM ((l_mem)(MAX_LUMEM >> 1)) #define MAX_INT INT_MAX /* maximum value of an int */ /* ** floor of the log2 of the maximum signed value for integral type 't'. ** (That is, maximum 'n' such that '2^n' fits in the given signed type.) */ #define log2maxs(t) (sizeof(t) * 8 - 2) /* ** test whether an unsigned value is a power of 2 (or zero) */ #define ispow2(x) (((x) & ((x) - 1)) == 0) /* number of chars of a literal string without the ending \0 */ #define LL(x) (sizeof(x)/sizeof(char) - 1) /* ** conversion of pointer to unsigned integer: ** this is for hashing only; there is no problem if the integer ** cannot hold the whole pointer value */ #define point2uint(p) ((unsigned int)((size_t)(p) & UINT_MAX)) /* types of 'usual argument conversions' for lua_Number and lua_Integer */ typedef LUAI_UACNUMBER l_uacNumber; typedef LUAI_UACINT l_uacInt; /* ** Internal assertions for in-house debugging */ #if defined LUAI_ASSERT #undef NDEBUG #include #define lua_assert(c) assert(c) #endif #if defined(lua_assert) #define check_exp(c,e) (lua_assert(c), (e)) /* to avoid problems with conditions too long */ #define lua_longassert(c) ((c) ? (void)0 : lua_assert(0)) #else #define lua_assert(c) ((void)0) #define check_exp(c,e) (e) #define lua_longassert(c) ((void)0) #endif /* ** assertion for checking API calls */ #if !defined(luai_apicheck) #define luai_apicheck(l,e) ((void)l, lua_assert(e)) #endif #define api_check(l,e,msg) luai_apicheck(l,(e) && msg) /* macro to avoid warnings about unused variables */ #if !defined(UNUSED) #define UNUSED(x) ((void)(x)) #endif /* type casts (a macro highlights casts in the code) */ #define cast(t, exp) ((t)(exp)) #define cast_void(i) cast(void, (i)) #define cast_voidp(i) cast(void *, (i)) #define cast_num(i) cast(lua_Number, (i)) #define cast_int(i) cast(int, (i)) #define cast_uint(i) cast(unsigned int, (i)) #define cast_byte(i) cast(lu_byte, (i)) #define cast_uchar(i) cast(unsigned char, (i)) #define cast_char(i) cast(char, (i)) #define cast_charp(i) cast(char *, (i)) #define cast_sizet(i) cast(size_t, (i)) /* cast a signed lua_Integer to lua_Unsigned */ #if !defined(l_castS2U) #define l_castS2U(i) ((lua_Unsigned)(i)) #endif /* ** cast a lua_Unsigned to a signed lua_Integer; this cast is ** not strict ISO C, but two-complement architectures should ** work fine. */ #if !defined(l_castU2S) #define l_castU2S(i) ((lua_Integer)(i)) #endif /* ** non-return type */ #if !defined(l_noret) #if defined(__GNUC__) #define l_noret void __attribute__((noreturn)) #elif defined(_MSC_VER) && _MSC_VER >= 1200 #define l_noret void __declspec(noreturn) #else #define l_noret void #endif #endif /* ** Inline functions */ #if !defined(LUA_USE_C89) #define l_inline inline #elif defined(__GNUC__) #define l_inline __inline__ #else #define l_inline /* empty */ #endif #define l_sinline static l_inline /* ** type for virtual-machine instructions; ** must be an unsigned with (at least) 4 bytes (see details in lopcodes.h) */ #if LUAI_IS32INT typedef unsigned int l_uint32; #else typedef unsigned long l_uint32; #endif typedef l_uint32 Instruction; /* ** Maximum length for short strings, that is, strings that are ** internalized. (Cannot be smaller than reserved words or tags for ** metamethods, as these strings must be internalized; ** #("function") = 8, #("__newindex") = 10.) */ #if !defined(LUAI_MAXSHORTLEN) #define LUAI_MAXSHORTLEN 40 #endif /* ** Initial size for the string table (must be power of 2). ** The Lua core alone registers ~50 strings (reserved words + ** metaevent keys + a few others). Libraries would typically add ** a few dozens more. */ #if !defined(MINSTRTABSIZE) #define MINSTRTABSIZE 128 #endif /* ** Size of cache for strings in the API. 'N' is the number of ** sets (better be a prime) and "M" is the size of each set (M == 1 ** makes a direct cache.) */ #if !defined(STRCACHE_N) #define STRCACHE_N 53 #define STRCACHE_M 2 #endif /* minimum size for string buffer */ #if !defined(LUA_MINBUFFER) #define LUA_MINBUFFER 32 #endif /* ** Maximum depth for nested C calls, syntactical nested non-terminals, ** and other features implemented through recursion in C. (Value must ** fit in a 16-bit unsigned integer. It must also be compatible with ** the size of the C stack.) */ #if !defined(LUAI_MAXCCALLS) #define LUAI_MAXCCALLS 200 #endif /* ** macros that are executed whenever program enters the Lua core ** ('lua_lock') and leaves the core ('lua_unlock') */ #if !defined(lua_lock) #define lua_lock(L) ((void) 0) #define lua_unlock(L) ((void) 0) #endif /* ** macro executed during Lua functions at points where the ** function can yield. */ #if !defined(luai_threadyield) #define luai_threadyield(L) {lua_unlock(L); lua_lock(L);} #endif /* ** these macros allow user-specific actions when a thread is ** created/deleted/resumed/yielded. */ #if !defined(luai_userstateopen) #define luai_userstateopen(L) ((void)L) #endif #if !defined(luai_userstateclose) #define luai_userstateclose(L) ((void)L) #endif #if !defined(luai_userstatethread) #define luai_userstatethread(L,L1) ((void)L) #endif #if !defined(luai_userstatefree) #define luai_userstatefree(L,L1) ((void)L) #endif #if !defined(luai_userstateresume) #define luai_userstateresume(L,n) ((void)L) #endif #if !defined(luai_userstateyield) #define luai_userstateyield(L,n) ((void)L) #endif /* ** The luai_num* macros define the primitive operations over numbers. */ /* floor division (defined as 'floor(a/b)') */ #if !defined(luai_numidiv) #define luai_numidiv(L,a,b) ((void)L, l_floor(luai_numdiv(L,a,b))) #endif /* float division */ #if !defined(luai_numdiv) #define luai_numdiv(L,a,b) ((a)/(b)) #endif /* ** modulo: defined as 'a - floor(a/b)*b'; the direct computation ** using this definition has several problems with rounding errors, ** so it is better to use 'fmod'. 'fmod' gives the result of ** 'a - trunc(a/b)*b', and therefore must be corrected when ** 'trunc(a/b) ~= floor(a/b)'. That happens when the division has a ** non-integer negative result: non-integer result is equivalent to ** a non-zero remainder 'm'; negative result is equivalent to 'a' and ** 'b' with different signs, or 'm' and 'b' with different signs ** (as the result 'm' of 'fmod' has the same sign of 'a'). */ #if !defined(luai_nummod) #define luai_nummod(L,a,b,m) \ { (void)L; (m) = l_mathop(fmod)(a,b); \ if (((m) > 0) ? (b) < 0 : ((m) < 0 && (b) > 0)) (m) += (b); } #endif /* exponentiation */ #if !defined(luai_numpow) #define luai_numpow(L,a,b) \ ((void)L, (b == 2) ? (a)*(a) : l_mathop(pow)(a,b)) #endif /* the others are quite standard operations */ #if !defined(luai_numadd) #define luai_numadd(L,a,b) ((a)+(b)) #define luai_numsub(L,a,b) ((a)-(b)) #define luai_nummul(L,a,b) ((a)*(b)) #define luai_numunm(L,a) (-(a)) #define luai_numeq(a,b) ((a)==(b)) #define luai_numlt(a,b) ((a)<(b)) #define luai_numle(a,b) ((a)<=(b)) #define luai_numgt(a,b) ((a)>(b)) #define luai_numge(a,b) ((a)>=(b)) #define luai_numisnan(a) (!luai_numeq((a), (a))) #endif /* ** macro to control inclusion of some hard tests on stack reallocation */ #if !defined(HARDSTACKTESTS) #define condmovestack(L,pre,pos) ((void)0) #else /* realloc stack keeping its size */ #define condmovestack(L,pre,pos) \ { int sz_ = stacksize(L); pre; luaD_reallocstack((L), sz_, 0); pos; } #endif #if !defined(HARDMEMTESTS) #define condchangemem(L,pre,pos) ((void)0) #else #define condchangemem(L,pre,pos) \ { if (gcrunning(G(L))) { pre; luaC_fullgc(L, 0); pos; } } #endif #endif /* ** $Id: lobject.h $ ** Type definitions for Lua objects ** See Copyright Notice in lua.h */ #ifndef lobject_h #define lobject_h #include /*#include "llimits.h"*/ /*#include "lua.h"*/ /* ** Extra types for collectable non-values */ #define LUA_TUPVAL LUA_NUMTYPES /* upvalues */ #define LUA_TPROTO (LUA_NUMTYPES+1) /* function prototypes */ #define LUA_TDEADKEY (LUA_NUMTYPES+2) /* removed keys in tables */ /* ** number of all possible types (including LUA_TNONE but excluding DEADKEY) */ #define LUA_TOTALTYPES (LUA_TPROTO + 2) /* ** tags for Tagged Values have the following use of bits: ** bits 0-3: actual tag (a LUA_T* constant) ** bits 4-5: variant bits ** bit 6: whether value is collectable */ /* add variant bits to a type */ #define makevariant(t,v) ((t) | ((v) << 4)) /* ** Union of all Lua values */ typedef union Value { struct GCObject *gc; /* collectable objects */ void *p; /* light userdata */ lua_CFunction f; /* light C functions */ lua_Integer i; /* integer numbers */ lua_Number n; /* float numbers */ } Value; /* ** Tagged Values. This is the basic representation of values in Lua: ** an actual value plus a tag with its type. */ #define TValuefields Value value_; lu_byte tt_ typedef struct TValue { TValuefields; } TValue; #define val_(o) ((o)->value_) #define valraw(o) (val_(o)) /* raw type tag of a TValue */ #define rawtt(o) ((o)->tt_) /* tag with no variants (bits 0-3) */ #define novariant(t) ((t) & 0x0F) /* type tag of a TValue (bits 0-3 for tags + variant bits 4-5) */ #define withvariant(t) ((t) & 0x3F) #define ttypetag(o) withvariant(rawtt(o)) /* type of a TValue */ #define ttype(o) (novariant(rawtt(o))) /* Macros to test type */ #define checktag(o,t) (rawtt(o) == (t)) #define checktype(o,t) (ttype(o) == (t)) /* Macros for internal tests */ /* collectable object has the same tag as the original value */ #define righttt(obj) (ttypetag(obj) == gcvalue(obj)->tt) /* ** Any value being manipulated by the program either is non ** collectable, or the collectable object has the right tag ** and it is not dead. The option 'L == NULL' allows other ** macros using this one to be used where L is not available. */ #define checkliveness(L,obj) \ ((void)L, lua_longassert(!iscollectable(obj) || \ (righttt(obj) && (L == NULL || !isdead(G(L),gcvalue(obj)))))) /* Macros to set values */ /* set a value's tag */ #define settt_(o,t) ((o)->tt_=(t)) /* main macro to copy values (from 'obj2' to 'obj1') */ #define setobj(L,obj1,obj2) \ { TValue *io1=(obj1); const TValue *io2=(obj2); \ io1->value_ = io2->value_; settt_(io1, io2->tt_); \ checkliveness(L,io1); lua_assert(!isnonstrictnil(io1)); } /* ** Different types of assignments, according to source and destination. ** (They are mostly equal now, but may be different in the future.) */ /* from stack to stack */ #define setobjs2s(L,o1,o2) setobj(L,s2v(o1),s2v(o2)) /* to stack (not from same stack) */ #define setobj2s(L,o1,o2) setobj(L,s2v(o1),o2) /* from table to same table */ #define setobjt2t setobj /* to new object */ #define setobj2n setobj /* to table */ #define setobj2t setobj /* ** Entries in a Lua stack. Field 'tbclist' forms a list of all ** to-be-closed variables active in this stack. Dummy entries are ** used when the distance between two tbc variables does not fit ** in an unsigned short. They are represented by delta==0, and ** their real delta is always the maximum value that fits in ** that field. */ typedef union StackValue { TValue val; struct { TValuefields; unsigned short delta; } tbclist; } StackValue; /* index to stack elements */ typedef StackValue *StkId; /* convert a 'StackValue' to a 'TValue' */ #define s2v(o) (&(o)->val) /* ** {================================================================== ** Nil ** =================================================================== */ /* Standard nil */ #define LUA_VNIL makevariant(LUA_TNIL, 0) /* Empty slot (which might be different from a slot containing nil) */ #define LUA_VEMPTY makevariant(LUA_TNIL, 1) /* Value returned for a key not found in a table (absent key) */ #define LUA_VABSTKEY makevariant(LUA_TNIL, 2) /* macro to test for (any kind of) nil */ #define ttisnil(v) checktype((v), LUA_TNIL) /* macro to test for a standard nil */ #define ttisstrictnil(o) checktag((o), LUA_VNIL) #define setnilvalue(obj) settt_(obj, LUA_VNIL) #define isabstkey(v) checktag((v), LUA_VABSTKEY) /* ** macro to detect non-standard nils (used only in assertions) */ #define isnonstrictnil(v) (ttisnil(v) && !ttisstrictnil(v)) /* ** By default, entries with any kind of nil are considered empty. ** (In any definition, values associated with absent keys must also ** be accepted as empty.) */ #define isempty(v) ttisnil(v) /* macro defining a value corresponding to an absent key */ #define ABSTKEYCONSTANT {NULL}, LUA_VABSTKEY /* mark an entry as empty */ #define setempty(v) settt_(v, LUA_VEMPTY) /* }================================================================== */ /* ** {================================================================== ** Booleans ** =================================================================== */ #define LUA_VFALSE makevariant(LUA_TBOOLEAN, 0) #define LUA_VTRUE makevariant(LUA_TBOOLEAN, 1) #define ttisboolean(o) checktype((o), LUA_TBOOLEAN) #define ttisfalse(o) checktag((o), LUA_VFALSE) #define ttistrue(o) checktag((o), LUA_VTRUE) #define l_isfalse(o) (ttisfalse(o) || ttisnil(o)) #define setbfvalue(obj) settt_(obj, LUA_VFALSE) #define setbtvalue(obj) settt_(obj, LUA_VTRUE) /* }================================================================== */ /* ** {================================================================== ** Threads ** =================================================================== */ #define LUA_VTHREAD makevariant(LUA_TTHREAD, 0) #define ttisthread(o) checktag((o), ctb(LUA_VTHREAD)) #define thvalue(o) check_exp(ttisthread(o), gco2th(val_(o).gc)) #define setthvalue(L,obj,x) \ { TValue *io = (obj); lua_State *x_ = (x); \ val_(io).gc = obj2gco(x_); settt_(io, ctb(LUA_VTHREAD)); \ checkliveness(L,io); } #define setthvalue2s(L,o,t) setthvalue(L,s2v(o),t) /* }================================================================== */ /* ** {================================================================== ** Collectable Objects ** =================================================================== */ /* ** Common Header for all collectable objects (in macro form, to be ** included in other objects) */ #define CommonHeader struct GCObject *next; lu_byte tt; lu_byte marked /* Common type for all collectable objects */ typedef struct GCObject { CommonHeader; } GCObject; /* Bit mark for collectable types */ #define BIT_ISCOLLECTABLE (1 << 6) #define iscollectable(o) (rawtt(o) & BIT_ISCOLLECTABLE) /* mark a tag as collectable */ #define ctb(t) ((t) | BIT_ISCOLLECTABLE) #define gcvalue(o) check_exp(iscollectable(o), val_(o).gc) #define gcvalueraw(v) ((v).gc) #define setgcovalue(L,obj,x) \ { TValue *io = (obj); GCObject *i_g=(x); \ val_(io).gc = i_g; settt_(io, ctb(i_g->tt)); } /* }================================================================== */ /* ** {================================================================== ** Numbers ** =================================================================== */ /* Variant tags for numbers */ #define LUA_VNUMINT makevariant(LUA_TNUMBER, 0) /* integer numbers */ #define LUA_VNUMFLT makevariant(LUA_TNUMBER, 1) /* float numbers */ #define ttisnumber(o) checktype((o), LUA_TNUMBER) #define ttisfloat(o) checktag((o), LUA_VNUMFLT) #define ttisinteger(o) checktag((o), LUA_VNUMINT) #define nvalue(o) check_exp(ttisnumber(o), \ (ttisinteger(o) ? cast_num(ivalue(o)) : fltvalue(o))) #define fltvalue(o) check_exp(ttisfloat(o), val_(o).n) #define ivalue(o) check_exp(ttisinteger(o), val_(o).i) #define fltvalueraw(v) ((v).n) #define ivalueraw(v) ((v).i) #define setfltvalue(obj,x) \ { TValue *io=(obj); val_(io).n=(x); settt_(io, LUA_VNUMFLT); } #define chgfltvalue(obj,x) \ { TValue *io=(obj); lua_assert(ttisfloat(io)); val_(io).n=(x); } #define setivalue(obj,x) \ { TValue *io=(obj); val_(io).i=(x); settt_(io, LUA_VNUMINT); } #define chgivalue(obj,x) \ { TValue *io=(obj); lua_assert(ttisinteger(io)); val_(io).i=(x); } /* }================================================================== */ /* ** {================================================================== ** Strings ** =================================================================== */ /* Variant tags for strings */ #define LUA_VSHRSTR makevariant(LUA_TSTRING, 0) /* short strings */ #define LUA_VLNGSTR makevariant(LUA_TSTRING, 1) /* long strings */ #define ttisstring(o) checktype((o), LUA_TSTRING) #define ttisshrstring(o) checktag((o), ctb(LUA_VSHRSTR)) #define ttislngstring(o) checktag((o), ctb(LUA_VLNGSTR)) #define tsvalueraw(v) (gco2ts((v).gc)) #define tsvalue(o) check_exp(ttisstring(o), gco2ts(val_(o).gc)) #define setsvalue(L,obj,x) \ { TValue *io = (obj); TString *x_ = (x); \ val_(io).gc = obj2gco(x_); settt_(io, ctb(x_->tt)); \ checkliveness(L,io); } /* set a string to the stack */ #define setsvalue2s(L,o,s) setsvalue(L,s2v(o),s) /* set a string to a new object */ #define setsvalue2n setsvalue /* ** Header for a string value. */ typedef struct TString { CommonHeader; lu_byte extra; /* reserved words for short strings; "has hash" for longs */ lu_byte shrlen; /* length for short strings */ unsigned int hash; union { size_t lnglen; /* length for long strings */ struct TString *hnext; /* linked list for hash table */ } u; char contents[1]; } TString; /* ** Get the actual string (array of bytes) from a 'TString'. */ #define getstr(ts) ((ts)->contents) /* get the actual string (array of bytes) from a Lua value */ #define svalue(o) getstr(tsvalue(o)) /* get string length from 'TString *s' */ #define tsslen(s) ((s)->tt == LUA_VSHRSTR ? (s)->shrlen : (s)->u.lnglen) /* get string length from 'TValue *o' */ #define vslen(o) tsslen(tsvalue(o)) /* }================================================================== */ /* ** {================================================================== ** Userdata ** =================================================================== */ /* ** Light userdata should be a variant of userdata, but for compatibility ** reasons they are also different types. */ #define LUA_VLIGHTUSERDATA makevariant(LUA_TLIGHTUSERDATA, 0) #define LUA_VUSERDATA makevariant(LUA_TUSERDATA, 0) #define ttislightuserdata(o) checktag((o), LUA_VLIGHTUSERDATA) #define ttisfulluserdata(o) checktag((o), ctb(LUA_VUSERDATA)) #define pvalue(o) check_exp(ttislightuserdata(o), val_(o).p) #define uvalue(o) check_exp(ttisfulluserdata(o), gco2u(val_(o).gc)) #define pvalueraw(v) ((v).p) #define setpvalue(obj,x) \ { TValue *io=(obj); val_(io).p=(x); settt_(io, LUA_VLIGHTUSERDATA); } #define setuvalue(L,obj,x) \ { TValue *io = (obj); Udata *x_ = (x); \ val_(io).gc = obj2gco(x_); settt_(io, ctb(LUA_VUSERDATA)); \ checkliveness(L,io); } /* Ensures that addresses after this type are always fully aligned. */ typedef union UValue { TValue uv; LUAI_MAXALIGN; /* ensures maximum alignment for udata bytes */ } UValue; /* ** Header for userdata with user values; ** memory area follows the end of this structure. */ typedef struct Udata { CommonHeader; unsigned short nuvalue; /* number of user values */ size_t len; /* number of bytes */ struct Table *metatable; GCObject *gclist; UValue uv[1]; /* user values */ } Udata; /* ** Header for userdata with no user values. These userdata do not need ** to be gray during GC, and therefore do not need a 'gclist' field. ** To simplify, the code always use 'Udata' for both kinds of userdata, ** making sure it never accesses 'gclist' on userdata with no user values. ** This structure here is used only to compute the correct size for ** this representation. (The 'bindata' field in its end ensures correct ** alignment for binary data following this header.) */ typedef struct Udata0 { CommonHeader; unsigned short nuvalue; /* number of user values */ size_t len; /* number of bytes */ struct Table *metatable; union {LUAI_MAXALIGN;} bindata; } Udata0; /* compute the offset of the memory area of a userdata */ #define udatamemoffset(nuv) \ ((nuv) == 0 ? offsetof(Udata0, bindata) \ : offsetof(Udata, uv) + (sizeof(UValue) * (nuv))) /* get the address of the memory block inside 'Udata' */ #define getudatamem(u) (cast_charp(u) + udatamemoffset((u)->nuvalue)) /* compute the size of a userdata */ #define sizeudata(nuv,nb) (udatamemoffset(nuv) + (nb)) /* }================================================================== */ /* ** {================================================================== ** Prototypes ** =================================================================== */ #define LUA_VPROTO makevariant(LUA_TPROTO, 0) /* ** Description of an upvalue for function prototypes */ typedef struct Upvaldesc { TString *name; /* upvalue name (for debug information) */ lu_byte instack; /* whether it is in stack (register) */ lu_byte idx; /* index of upvalue (in stack or in outer function's list) */ lu_byte kind; /* kind of corresponding variable */ } Upvaldesc; /* ** Description of a local variable for function prototypes ** (used for debug information) */ typedef struct LocVar { TString *varname; int startpc; /* first point where variable is active */ int endpc; /* first point where variable is dead */ } LocVar; /* ** Associates the absolute line source for a given instruction ('pc'). ** The array 'lineinfo' gives, for each instruction, the difference in ** lines from the previous instruction. When that difference does not ** fit into a byte, Lua saves the absolute line for that instruction. ** (Lua also saves the absolute line periodically, to speed up the ** computation of a line number: we can use binary search in the ** absolute-line array, but we must traverse the 'lineinfo' array ** linearly to compute a line.) */ typedef struct AbsLineInfo { int pc; int line; } AbsLineInfo; /* ** Function Prototypes */ typedef struct Proto { CommonHeader; lu_byte numparams; /* number of fixed (named) parameters */ lu_byte is_vararg; lu_byte maxstacksize; /* number of registers needed by this function */ int sizeupvalues; /* size of 'upvalues' */ int sizek; /* size of 'k' */ int sizecode; int sizelineinfo; int sizep; /* size of 'p' */ int sizelocvars; int sizeabslineinfo; /* size of 'abslineinfo' */ int linedefined; /* debug information */ int lastlinedefined; /* debug information */ TValue *k; /* constants used by the function */ Instruction *code; /* opcodes */ struct Proto **p; /* functions defined inside the function */ Upvaldesc *upvalues; /* upvalue information */ ls_byte *lineinfo; /* information about source lines (debug information) */ AbsLineInfo *abslineinfo; /* idem */ LocVar *locvars; /* information about local variables (debug information) */ TString *source; /* used for debug information */ GCObject *gclist; } Proto; /* }================================================================== */ /* ** {================================================================== ** Functions ** =================================================================== */ #define LUA_VUPVAL makevariant(LUA_TUPVAL, 0) /* Variant tags for functions */ #define LUA_VLCL makevariant(LUA_TFUNCTION, 0) /* Lua closure */ #define LUA_VLCF makevariant(LUA_TFUNCTION, 1) /* light C function */ #define LUA_VCCL makevariant(LUA_TFUNCTION, 2) /* C closure */ #define ttisfunction(o) checktype(o, LUA_TFUNCTION) #define ttisLclosure(o) checktag((o), ctb(LUA_VLCL)) #define ttislcf(o) checktag((o), LUA_VLCF) #define ttisCclosure(o) checktag((o), ctb(LUA_VCCL)) #define ttisclosure(o) (ttisLclosure(o) || ttisCclosure(o)) #define isLfunction(o) ttisLclosure(o) #define clvalue(o) check_exp(ttisclosure(o), gco2cl(val_(o).gc)) #define clLvalue(o) check_exp(ttisLclosure(o), gco2lcl(val_(o).gc)) #define fvalue(o) check_exp(ttislcf(o), val_(o).f) #define clCvalue(o) check_exp(ttisCclosure(o), gco2ccl(val_(o).gc)) #define fvalueraw(v) ((v).f) #define setclLvalue(L,obj,x) \ { TValue *io = (obj); LClosure *x_ = (x); \ val_(io).gc = obj2gco(x_); settt_(io, ctb(LUA_VLCL)); \ checkliveness(L,io); } #define setclLvalue2s(L,o,cl) setclLvalue(L,s2v(o),cl) #define setfvalue(obj,x) \ { TValue *io=(obj); val_(io).f=(x); settt_(io, LUA_VLCF); } #define setclCvalue(L,obj,x) \ { TValue *io = (obj); CClosure *x_ = (x); \ val_(io).gc = obj2gco(x_); settt_(io, ctb(LUA_VCCL)); \ checkliveness(L,io); } /* ** Upvalues for Lua closures */ typedef struct UpVal { CommonHeader; lu_byte tbc; /* true if it represents a to-be-closed variable */ TValue *v; /* points to stack or to its own value */ union { struct { /* (when open) */ struct UpVal *next; /* linked list */ struct UpVal **previous; } open; TValue value; /* the value (when closed) */ } u; } UpVal; #define ClosureHeader \ CommonHeader; lu_byte nupvalues; GCObject *gclist typedef struct CClosure { ClosureHeader; lua_CFunction f; TValue upvalue[1]; /* list of upvalues */ } CClosure; typedef struct LClosure { ClosureHeader; struct Proto *p; UpVal *upvals[1]; /* list of upvalues */ } LClosure; typedef union Closure { CClosure c; LClosure l; } Closure; #define getproto(o) (clLvalue(o)->p) /* }================================================================== */ /* ** {================================================================== ** Tables ** =================================================================== */ #define LUA_VTABLE makevariant(LUA_TTABLE, 0) #define ttistable(o) checktag((o), ctb(LUA_VTABLE)) #define hvalue(o) check_exp(ttistable(o), gco2t(val_(o).gc)) #define sethvalue(L,obj,x) \ { TValue *io = (obj); Table *x_ = (x); \ val_(io).gc = obj2gco(x_); settt_(io, ctb(LUA_VTABLE)); \ checkliveness(L,io); } #define sethvalue2s(L,o,h) sethvalue(L,s2v(o),h) /* ** Nodes for Hash tables: A pack of two TValue's (key-value pairs) ** plus a 'next' field to link colliding entries. The distribution ** of the key's fields ('key_tt' and 'key_val') not forming a proper ** 'TValue' allows for a smaller size for 'Node' both in 4-byte ** and 8-byte alignments. */ typedef union Node { struct NodeKey { TValuefields; /* fields for value */ lu_byte key_tt; /* key type */ int next; /* for chaining */ Value key_val; /* key value */ } u; TValue i_val; /* direct access to node's value as a proper 'TValue' */ } Node; /* copy a value into a key */ #define setnodekey(L,node,obj) \ { Node *n_=(node); const TValue *io_=(obj); \ n_->u.key_val = io_->value_; n_->u.key_tt = io_->tt_; \ checkliveness(L,io_); } /* copy a value from a key */ #define getnodekey(L,obj,node) \ { TValue *io_=(obj); const Node *n_=(node); \ io_->value_ = n_->u.key_val; io_->tt_ = n_->u.key_tt; \ checkliveness(L,io_); } /* ** About 'alimit': if 'isrealasize(t)' is true, then 'alimit' is the ** real size of 'array'. Otherwise, the real size of 'array' is the ** smallest power of two not smaller than 'alimit' (or zero iff 'alimit' ** is zero); 'alimit' is then used as a hint for #t. */ #define BITRAS (1 << 7) #define isrealasize(t) (!((t)->flags & BITRAS)) #define setrealasize(t) ((t)->flags &= cast_byte(~BITRAS)) #define setnorealasize(t) ((t)->flags |= BITRAS) typedef struct Table { CommonHeader; lu_byte flags; /* 1<

u.key_tt) #define keyval(node) ((node)->u.key_val) #define keyisnil(node) (keytt(node) == LUA_TNIL) #define keyisinteger(node) (keytt(node) == LUA_VNUMINT) #define keyival(node) (keyval(node).i) #define keyisshrstr(node) (keytt(node) == ctb(LUA_VSHRSTR)) #define keystrval(node) (gco2ts(keyval(node).gc)) #define setnilkey(node) (keytt(node) = LUA_TNIL) #define keyiscollectable(n) (keytt(n) & BIT_ISCOLLECTABLE) #define gckey(n) (keyval(n).gc) #define gckeyN(n) (keyiscollectable(n) ? gckey(n) : NULL) /* ** Dead keys in tables have the tag DEADKEY but keep their original ** gcvalue. This distinguishes them from regular keys but allows them to ** be found when searched in a special way. ('next' needs that to find ** keys removed from a table during a traversal.) */ #define setdeadkey(node) (keytt(node) = LUA_TDEADKEY) #define keyisdead(node) (keytt(node) == LUA_TDEADKEY) /* }================================================================== */ /* ** 'module' operation for hashing (size is always a power of 2) */ #define lmod(s,size) \ (check_exp((size&(size-1))==0, (cast_int((s) & ((size)-1))))) #define twoto(x) (1<<(x)) #define sizenode(t) (twoto((t)->lsizenode)) /* size of buffer for 'luaO_utf8esc' function */ #define UTF8BUFFSZ 8 LUAI_FUNC int luaO_utf8esc (char *buff, unsigned long x); LUAI_FUNC int luaO_ceillog2 (unsigned int x); LUAI_FUNC int luaO_rawarith (lua_State *L, int op, const TValue *p1, const TValue *p2, TValue *res); LUAI_FUNC void luaO_arith (lua_State *L, int op, const TValue *p1, const TValue *p2, StkId res); LUAI_FUNC size_t luaO_str2num (const char *s, TValue *o); LUAI_FUNC int luaO_hexavalue (int c); LUAI_FUNC void luaO_tostring (lua_State *L, TValue *obj); LUAI_FUNC const char *luaO_pushvfstring (lua_State *L, const char *fmt, va_list argp); LUAI_FUNC const char *luaO_pushfstring (lua_State *L, const char *fmt, ...); LUAI_FUNC void luaO_chunkid (char *out, const char *source, size_t srclen); #endif /* ** $Id: lmem.h $ ** Interface to Memory Manager ** See Copyright Notice in lua.h */ #ifndef lmem_h #define lmem_h #include /*#include "llimits.h"*/ /*#include "lua.h"*/ #define luaM_error(L) luaD_throw(L, LUA_ERRMEM) /* ** This macro tests whether it is safe to multiply 'n' by the size of ** type 't' without overflows. Because 'e' is always constant, it avoids ** the runtime division MAX_SIZET/(e). ** (The macro is somewhat complex to avoid warnings: The 'sizeof' ** comparison avoids a runtime comparison when overflow cannot occur. ** The compiler should be able to optimize the real test by itself, but ** when it does it, it may give a warning about "comparison is always ** false due to limited range of data type"; the +1 tricks the compiler, ** avoiding this warning but also this optimization.) */ #define luaM_testsize(n,e) \ (sizeof(n) >= sizeof(size_t) && cast_sizet((n)) + 1 > MAX_SIZET/(e)) #define luaM_checksize(L,n,e) \ (luaM_testsize(n,e) ? luaM_toobig(L) : cast_void(0)) /* ** Computes the minimum between 'n' and 'MAX_SIZET/sizeof(t)', so that ** the result is not larger than 'n' and cannot overflow a 'size_t' ** when multiplied by the size of type 't'. (Assumes that 'n' is an ** 'int' or 'unsigned int' and that 'int' is not larger than 'size_t'.) */ #define luaM_limitN(n,t) \ ((cast_sizet(n) <= MAX_SIZET/sizeof(t)) ? (n) : \ cast_uint((MAX_SIZET/sizeof(t)))) /* ** Arrays of chars do not need any test */ #define luaM_reallocvchar(L,b,on,n) \ cast_charp(luaM_saferealloc_(L, (b), (on)*sizeof(char), (n)*sizeof(char))) #define luaM_freemem(L, b, s) luaM_free_(L, (b), (s)) #define luaM_free(L, b) luaM_free_(L, (b), sizeof(*(b))) #define luaM_freearray(L, b, n) luaM_free_(L, (b), (n)*sizeof(*(b))) #define luaM_new(L,t) cast(t*, luaM_malloc_(L, sizeof(t), 0)) #define luaM_newvector(L,n,t) cast(t*, luaM_malloc_(L, (n)*sizeof(t), 0)) #define luaM_newvectorchecked(L,n,t) \ (luaM_checksize(L,n,sizeof(t)), luaM_newvector(L,n,t)) #define luaM_newobject(L,tag,s) luaM_malloc_(L, (s), tag) #define luaM_growvector(L,v,nelems,size,t,limit,e) \ ((v)=cast(t *, luaM_growaux_(L,v,nelems,&(size),sizeof(t), \ luaM_limitN(limit,t),e))) #define luaM_reallocvector(L, v,oldn,n,t) \ (cast(t *, luaM_realloc_(L, v, cast_sizet(oldn) * sizeof(t), \ cast_sizet(n) * sizeof(t)))) #define luaM_shrinkvector(L,v,size,fs,t) \ ((v)=cast(t *, luaM_shrinkvector_(L, v, &(size), fs, sizeof(t)))) LUAI_FUNC l_noret luaM_toobig (lua_State *L); /* not to be called directly */ LUAI_FUNC void *luaM_realloc_ (lua_State *L, void *block, size_t oldsize, size_t size); LUAI_FUNC void *luaM_saferealloc_ (lua_State *L, void *block, size_t oldsize, size_t size); LUAI_FUNC void luaM_free_ (lua_State *L, void *block, size_t osize); LUAI_FUNC void *luaM_growaux_ (lua_State *L, void *block, int nelems, int *size, int size_elem, int limit, const char *what); LUAI_FUNC void *luaM_shrinkvector_ (lua_State *L, void *block, int *nelem, int final_n, int size_elem); LUAI_FUNC void *luaM_malloc_ (lua_State *L, size_t size, int tag); #endif /* ** $Id: ltm.h $ ** Tag methods ** See Copyright Notice in lua.h */ #ifndef ltm_h #define ltm_h /*#include "lobject.h"*/ /* * WARNING: if you change the order of this enumeration, * grep "ORDER TM" and "ORDER OP" */ typedef enum { TM_INDEX, TM_NEWINDEX, TM_GC, TM_MODE, TM_LEN, TM_EQ, /* last tag method with fast access */ TM_ADD, TM_SUB, TM_MUL, TM_MOD, TM_POW, TM_DIV, TM_IDIV, TM_BAND, TM_BOR, TM_BXOR, TM_SHL, TM_SHR, TM_UNM, TM_BNOT, TM_LT, TM_LE, TM_CONCAT, TM_CALL, TM_CLOSE, TM_N /* number of elements in the enum */ } TMS; /* ** Mask with 1 in all fast-access methods. A 1 in any of these bits ** in the flag of a (meta)table means the metatable does not have the ** corresponding metamethod field. (Bit 7 of the flag is used for ** 'isrealasize'.) */ #define maskflags (~(~0u << (TM_EQ + 1))) /* ** Test whether there is no tagmethod. ** (Because tagmethods use raw accesses, the result may be an "empty" nil.) */ #define notm(tm) ttisnil(tm) #define gfasttm(g,et,e) ((et) == NULL ? NULL : \ ((et)->flags & (1u<<(e))) ? NULL : luaT_gettm(et, e, (g)->tmname[e])) #define fasttm(l,et,e) gfasttm(G(l), et, e) #define ttypename(x) luaT_typenames_[(x) + 1] LUAI_DDEC(const char *const luaT_typenames_[LUA_TOTALTYPES];) LUAI_FUNC const char *luaT_objtypename (lua_State *L, const TValue *o); LUAI_FUNC const TValue *luaT_gettm (Table *events, TMS event, TString *ename); LUAI_FUNC const TValue *luaT_gettmbyobj (lua_State *L, const TValue *o, TMS event); LUAI_FUNC void luaT_init (lua_State *L); LUAI_FUNC void luaT_callTM (lua_State *L, const TValue *f, const TValue *p1, const TValue *p2, const TValue *p3); LUAI_FUNC void luaT_callTMres (lua_State *L, const TValue *f, const TValue *p1, const TValue *p2, StkId p3); LUAI_FUNC void luaT_trybinTM (lua_State *L, const TValue *p1, const TValue *p2, StkId res, TMS event); LUAI_FUNC void luaT_tryconcatTM (lua_State *L); LUAI_FUNC void luaT_trybinassocTM (lua_State *L, const TValue *p1, const TValue *p2, int inv, StkId res, TMS event); LUAI_FUNC void luaT_trybiniTM (lua_State *L, const TValue *p1, lua_Integer i2, int inv, StkId res, TMS event); LUAI_FUNC int luaT_callorderTM (lua_State *L, const TValue *p1, const TValue *p2, TMS event); LUAI_FUNC int luaT_callorderiTM (lua_State *L, const TValue *p1, int v2, int inv, int isfloat, TMS event); LUAI_FUNC void luaT_adjustvarargs (lua_State *L, int nfixparams, struct CallInfo *ci, const Proto *p); LUAI_FUNC void luaT_getvarargs (lua_State *L, struct CallInfo *ci, StkId where, int wanted); #endif /* ** $Id: lstate.h $ ** Global State ** See Copyright Notice in lua.h */ #ifndef lstate_h #define lstate_h /*#include "lua.h"*/ /*#include "lobject.h"*/ /*#include "ltm.h"*/ /*#include "lzio.h"*/ /* ** Some notes about garbage-collected objects: All objects in Lua must ** be kept somehow accessible until being freed, so all objects always ** belong to one (and only one) of these lists, using field 'next' of ** the 'CommonHeader' for the link: ** ** 'allgc': all objects not marked for finalization; ** 'finobj': all objects marked for finalization; ** 'tobefnz': all objects ready to be finalized; ** 'fixedgc': all objects that are not to be collected (currently ** only small strings, such as reserved words). ** ** For the generational collector, some of these lists have marks for ** generations. Each mark points to the first element in the list for ** that particular generation; that generation goes until the next mark. ** ** 'allgc' -> 'survival': new objects; ** 'survival' -> 'old': objects that survived one collection; ** 'old1' -> 'reallyold': objects that became old in last collection; ** 'reallyold' -> NULL: objects old for more than one cycle. ** ** 'finobj' -> 'finobjsur': new objects marked for finalization; ** 'finobjsur' -> 'finobjold1': survived """"; ** 'finobjold1' -> 'finobjrold': just old """"; ** 'finobjrold' -> NULL: really old """". ** ** All lists can contain elements older than their main ages, due ** to 'luaC_checkfinalizer' and 'udata2finalize', which move ** objects between the normal lists and the "marked for finalization" ** lists. Moreover, barriers can age young objects in young lists as ** OLD0, which then become OLD1. However, a list never contains ** elements younger than their main ages. ** ** The generational collector also uses a pointer 'firstold1', which ** points to the first OLD1 object in the list. It is used to optimize ** 'markold'. (Potentially OLD1 objects can be anywhere between 'allgc' ** and 'reallyold', but often the list has no OLD1 objects or they are ** after 'old1'.) Note the difference between it and 'old1': ** 'firstold1': no OLD1 objects before this point; there can be all ** ages after it. ** 'old1': no objects younger than OLD1 after this point. */ /* ** Moreover, there is another set of lists that control gray objects. ** These lists are linked by fields 'gclist'. (All objects that ** can become gray have such a field. The field is not the same ** in all objects, but it always has this name.) Any gray object ** must belong to one of these lists, and all objects in these lists ** must be gray (with two exceptions explained below): ** ** 'gray': regular gray objects, still waiting to be visited. ** 'grayagain': objects that must be revisited at the atomic phase. ** That includes ** - black objects got in a write barrier; ** - all kinds of weak tables during propagation phase; ** - all threads. ** 'weak': tables with weak values to be cleared; ** 'ephemeron': ephemeron tables with white->white entries; ** 'allweak': tables with weak keys and/or weak values to be cleared. ** ** The exceptions to that "gray rule" are: ** - TOUCHED2 objects in generational mode stay in a gray list (because ** they must be visited again at the end of the cycle), but they are ** marked black because assignments to them must activate barriers (to ** move them back to TOUCHED1). ** - Open upvales are kept gray to avoid barriers, but they stay out ** of gray lists. (They don't even have a 'gclist' field.) */ /* ** About 'nCcalls': This count has two parts: the lower 16 bits counts ** the number of recursive invocations in the C stack; the higher ** 16 bits counts the number of non-yieldable calls in the stack. ** (They are together so that we can change and save both with one ** instruction.) */ /* true if this thread does not have non-yieldable calls in the stack */ #define yieldable(L) (((L)->nCcalls & 0xffff0000) == 0) /* real number of C calls */ #define getCcalls(L) ((L)->nCcalls & 0xffff) /* Increment the number of non-yieldable calls */ #define incnny(L) ((L)->nCcalls += 0x10000) /* Decrement the number of non-yieldable calls */ #define decnny(L) ((L)->nCcalls -= 0x10000) /* Non-yieldable call increment */ #define nyci (0x10000 | 1) struct lua_longjmp; /* defined in ldo.c */ /* ** Atomic type (relative to signals) to better ensure that 'lua_sethook' ** is thread safe */ #if !defined(l_signalT) #include #define l_signalT sig_atomic_t #endif /* ** Extra stack space to handle TM calls and some other extras. This ** space is not included in 'stack_last'. It is used only to avoid stack ** checks, either because the element will be promptly popped or because ** there will be a stack check soon after the push. Function frames ** never use this extra space, so it does not need to be kept clean. */ #define EXTRA_STACK 5 #define BASIC_STACK_SIZE (2*LUA_MINSTACK) #define stacksize(th) cast_int((th)->stack_last - (th)->stack) /* kinds of Garbage Collection */ #define KGC_INC 0 /* incremental gc */ #define KGC_GEN 1 /* generational gc */ typedef struct stringtable { TString **hash; int nuse; /* number of elements */ int size; } stringtable; /* ** Information about a call. ** About union 'u': ** - field 'l' is used only for Lua functions; ** - field 'c' is used only for C functions. ** About union 'u2': ** - field 'funcidx' is used only by C functions while doing a ** protected call; ** - field 'nyield' is used only while a function is "doing" an ** yield (from the yield until the next resume); ** - field 'nres' is used only while closing tbc variables when ** returning from a function; ** - field 'transferinfo' is used only during call/returnhooks, ** before the function starts or after it ends. */ typedef struct CallInfo { StkId func; /* function index in the stack */ StkId top; /* top for this function */ struct CallInfo *previous, *next; /* dynamic call link */ union { struct { /* only for Lua functions */ const Instruction *savedpc; volatile l_signalT trap; int nextraargs; /* # of extra arguments in vararg functions */ } l; struct { /* only for C functions */ lua_KFunction k; /* continuation in case of yields */ ptrdiff_t old_errfunc; lua_KContext ctx; /* context info. in case of yields */ } c; } u; union { int funcidx; /* called-function index */ int nyield; /* number of values yielded */ int nres; /* number of values returned */ struct { /* info about transferred values (for call/return hooks) */ unsigned short ftransfer; /* offset of first value transferred */ unsigned short ntransfer; /* number of values transferred */ } transferinfo; } u2; short nresults; /* expected number of results from this function */ unsigned short callstatus; } CallInfo; /* ** Bits in CallInfo status */ #define CIST_OAH (1<<0) /* original value of 'allowhook' */ #define CIST_C (1<<1) /* call is running a C function */ #define CIST_FRESH (1<<2) /* call is on a fresh "luaV_execute" frame */ #define CIST_HOOKED (1<<3) /* call is running a debug hook */ #define CIST_YPCALL (1<<4) /* doing a yieldable protected call */ #define CIST_TAIL (1<<5) /* call was tail called */ #define CIST_HOOKYIELD (1<<6) /* last hook called yielded */ #define CIST_FIN (1<<7) /* function "called" a finalizer */ #define CIST_TRAN (1<<8) /* 'ci' has transfer information */ #define CIST_CLSRET (1<<9) /* function is closing tbc variables */ /* Bits 10-12 are used for CIST_RECST (see below) */ #define CIST_RECST 10 #if defined(LUA_COMPAT_LT_LE) #define CIST_LEQ (1<<13) /* using __lt for __le */ #endif /* ** Field CIST_RECST stores the "recover status", used to keep the error ** status while closing to-be-closed variables in coroutines, so that ** Lua can correctly resume after an yield from a __close method called ** because of an error. (Three bits are enough for error status.) */ #define getcistrecst(ci) (((ci)->callstatus >> CIST_RECST) & 7) #define setcistrecst(ci,st) \ check_exp(((st) & 7) == (st), /* status must fit in three bits */ \ ((ci)->callstatus = ((ci)->callstatus & ~(7 << CIST_RECST)) \ | ((st) << CIST_RECST))) /* active function is a Lua function */ #define isLua(ci) (!((ci)->callstatus & CIST_C)) /* call is running Lua code (not a hook) */ #define isLuacode(ci) (!((ci)->callstatus & (CIST_C | CIST_HOOKED))) /* assume that CIST_OAH has offset 0 and that 'v' is strictly 0/1 */ #define setoah(st,v) ((st) = ((st) & ~CIST_OAH) | (v)) #define getoah(st) ((st) & CIST_OAH) /* ** 'global state', shared by all threads of this state */ typedef struct global_State { lua_Alloc frealloc; /* function to reallocate memory */ void *ud; /* auxiliary data to 'frealloc' */ l_mem totalbytes; /* number of bytes currently allocated - GCdebt */ l_mem GCdebt; /* bytes allocated not yet compensated by the collector */ lu_mem GCestimate; /* an estimate of the non-garbage memory in use */ lu_mem lastatomic; /* see function 'genstep' in file 'lgc.c' */ stringtable strt; /* hash table for strings */ TValue l_registry; TValue nilvalue; /* a nil value */ unsigned int seed; /* randomized seed for hashes */ lu_byte currentwhite; lu_byte gcstate; /* state of garbage collector */ lu_byte gckind; /* kind of GC running */ lu_byte gcstopem; /* stops emergency collections */ lu_byte genminormul; /* control for minor generational collections */ lu_byte genmajormul; /* control for major generational collections */ lu_byte gcstp; /* control whether GC is running */ lu_byte gcemergency; /* true if this is an emergency collection */ lu_byte gcpause; /* size of pause between successive GCs */ lu_byte gcstepmul; /* GC "speed" */ lu_byte gcstepsize; /* (log2 of) GC granularity */ GCObject *allgc; /* list of all collectable objects */ GCObject **sweepgc; /* current position of sweep in list */ GCObject *finobj; /* list of collectable objects with finalizers */ GCObject *gray; /* list of gray objects */ GCObject *grayagain; /* list of objects to be traversed atomically */ GCObject *weak; /* list of tables with weak values */ GCObject *ephemeron; /* list of ephemeron tables (weak keys) */ GCObject *allweak; /* list of all-weak tables */ GCObject *tobefnz; /* list of userdata to be GC */ GCObject *fixedgc; /* list of objects not to be collected */ /* fields for generational collector */ GCObject *survival; /* start of objects that survived one GC cycle */ GCObject *old1; /* start of old1 objects */ GCObject *reallyold; /* objects more than one cycle old ("really old") */ GCObject *firstold1; /* first OLD1 object in the list (if any) */ GCObject *finobjsur; /* list of survival objects with finalizers */ GCObject *finobjold1; /* list of old1 objects with finalizers */ GCObject *finobjrold; /* list of really old objects with finalizers */ struct lua_State *twups; /* list of threads with open upvalues */ lua_CFunction panic; /* to be called in unprotected errors */ struct lua_State *mainthread; TString *memerrmsg; /* message for memory-allocation errors */ TString *tmname[TM_N]; /* array with tag-method names */ struct Table *mt[LUA_NUMTAGS]; /* metatables for basic types */ TString *strcache[STRCACHE_N][STRCACHE_M]; /* cache for strings in API */ lua_WarnFunction warnf; /* warning function */ void *ud_warn; /* auxiliary data to 'warnf' */ } global_State; /* ** 'per thread' state */ struct lua_State { CommonHeader; lu_byte status; lu_byte allowhook; unsigned short nci; /* number of items in 'ci' list */ StkId top; /* first free slot in the stack */ global_State *l_G; CallInfo *ci; /* call info for current function */ StkId stack_last; /* end of stack (last element + 1) */ StkId stack; /* stack base */ UpVal *openupval; /* list of open upvalues in this stack */ StkId tbclist; /* list of to-be-closed variables */ GCObject *gclist; struct lua_State *twups; /* list of threads with open upvalues */ struct lua_longjmp *errorJmp; /* current error recover point */ CallInfo base_ci; /* CallInfo for first level (C calling Lua) */ volatile lua_Hook hook; ptrdiff_t errfunc; /* current error handling function (stack index) */ l_uint32 nCcalls; /* number of nested (non-yieldable | C) calls */ int oldpc; /* last pc traced */ int basehookcount; int hookcount; volatile l_signalT hookmask; }; #define G(L) (L->l_G) /* ** 'g->nilvalue' being a nil value flags that the state was completely ** build. */ #define completestate(g) ttisnil(&g->nilvalue) /* ** Union of all collectable objects (only for conversions) ** ISO C99, 6.5.2.3 p.5: ** "if a union contains several structures that share a common initial ** sequence [...], and if the union object currently contains one ** of these structures, it is permitted to inspect the common initial ** part of any of them anywhere that a declaration of the complete type ** of the union is visible." */ union GCUnion { GCObject gc; /* common header */ struct TString ts; struct Udata u; union Closure cl; struct Table h; struct Proto p; struct lua_State th; /* thread */ struct UpVal upv; }; /* ** ISO C99, 6.7.2.1 p.14: ** "A pointer to a union object, suitably converted, points to each of ** its members [...], and vice versa." */ #define cast_u(o) cast(union GCUnion *, (o)) /* macros to convert a GCObject into a specific value */ #define gco2ts(o) \ check_exp(novariant((o)->tt) == LUA_TSTRING, &((cast_u(o))->ts)) #define gco2u(o) check_exp((o)->tt == LUA_VUSERDATA, &((cast_u(o))->u)) #define gco2lcl(o) check_exp((o)->tt == LUA_VLCL, &((cast_u(o))->cl.l)) #define gco2ccl(o) check_exp((o)->tt == LUA_VCCL, &((cast_u(o))->cl.c)) #define gco2cl(o) \ check_exp(novariant((o)->tt) == LUA_TFUNCTION, &((cast_u(o))->cl)) #define gco2t(o) check_exp((o)->tt == LUA_VTABLE, &((cast_u(o))->h)) #define gco2p(o) check_exp((o)->tt == LUA_VPROTO, &((cast_u(o))->p)) #define gco2th(o) check_exp((o)->tt == LUA_VTHREAD, &((cast_u(o))->th)) #define gco2upv(o) check_exp((o)->tt == LUA_VUPVAL, &((cast_u(o))->upv)) /* ** macro to convert a Lua object into a GCObject ** (The access to 'tt' tries to ensure that 'v' is actually a Lua object.) */ #define obj2gco(v) check_exp((v)->tt >= LUA_TSTRING, &(cast_u(v)->gc)) /* actual number of total bytes allocated */ #define gettotalbytes(g) cast(lu_mem, (g)->totalbytes + (g)->GCdebt) LUAI_FUNC void luaE_setdebt (global_State *g, l_mem debt); LUAI_FUNC void luaE_freethread (lua_State *L, lua_State *L1); LUAI_FUNC CallInfo *luaE_extendCI (lua_State *L); LUAI_FUNC void luaE_freeCI (lua_State *L); LUAI_FUNC void luaE_shrinkCI (lua_State *L); LUAI_FUNC void luaE_checkcstack (lua_State *L); LUAI_FUNC void luaE_incCstack (lua_State *L); LUAI_FUNC void luaE_warning (lua_State *L, const char *msg, int tocont); LUAI_FUNC void luaE_warnerror (lua_State *L, const char *where); LUAI_FUNC int luaE_resetthread (lua_State *L, int status); #endif /* ** $Id: lzio.h $ ** Buffered streams ** See Copyright Notice in lua.h */ #ifndef lzio_h #define lzio_h /*#include "lua.h"*/ /*#include "lmem.h"*/ #define EOZ (-1) /* end of stream */ typedef struct Zio ZIO; #define zgetc(z) (((z)->n--)>0 ? cast_uchar(*(z)->p++) : luaZ_fill(z)) typedef struct Mbuffer { char *buffer; size_t n; size_t buffsize; } Mbuffer; #define luaZ_initbuffer(L, buff) ((buff)->buffer = NULL, (buff)->buffsize = 0) #define luaZ_buffer(buff) ((buff)->buffer) #define luaZ_sizebuffer(buff) ((buff)->buffsize) #define luaZ_bufflen(buff) ((buff)->n) #define luaZ_buffremove(buff,i) ((buff)->n -= (i)) #define luaZ_resetbuffer(buff) ((buff)->n = 0) #define luaZ_resizebuffer(L, buff, size) \ ((buff)->buffer = luaM_reallocvchar(L, (buff)->buffer, \ (buff)->buffsize, size), \ (buff)->buffsize = size) #define luaZ_freebuffer(L, buff) luaZ_resizebuffer(L, buff, 0) LUAI_FUNC void luaZ_init (lua_State *L, ZIO *z, lua_Reader reader, void *data); LUAI_FUNC size_t luaZ_read (ZIO* z, void *b, size_t n); /* read next n bytes */ /* --------- Private Part ------------------ */ struct Zio { size_t n; /* bytes still unread */ const char *p; /* current position in buffer */ lua_Reader reader; /* reader function */ void *data; /* additional data */ lua_State *L; /* Lua state (for reader) */ }; LUAI_FUNC int luaZ_fill (ZIO *z); #endif /* ** $Id: lopcodes.h $ ** Opcodes for Lua virtual machine ** See Copyright Notice in lua.h */ #ifndef lopcodes_h #define lopcodes_h /*#include "llimits.h"*/ /*=========================================================================== We assume that instructions are unsigned 32-bit integers. All instructions have an opcode in the first 7 bits. Instructions can have the following formats: 3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 iABC C(8) | B(8) |k| A(8) | Op(7) | iABx Bx(17) | A(8) | Op(7) | iAsBx sBx (signed)(17) | A(8) | Op(7) | iAx Ax(25) | Op(7) | isJ sJ(25) | Op(7) | A signed argument is represented in excess K: the represented value is the written unsigned value minus K, where K is half the maximum for the corresponding unsigned argument. ===========================================================================*/ enum OpMode {iABC, iABx, iAsBx, iAx, isJ}; /* basic instruction formats */ /* ** size and position of opcode arguments. */ #define SIZE_C 8 #define SIZE_B 8 #define SIZE_Bx (SIZE_C + SIZE_B + 1) #define SIZE_A 8 #define SIZE_Ax (SIZE_Bx + SIZE_A) #define SIZE_sJ (SIZE_Bx + SIZE_A) #define SIZE_OP 7 #define POS_OP 0 #define POS_A (POS_OP + SIZE_OP) #define POS_k (POS_A + SIZE_A) #define POS_B (POS_k + 1) #define POS_C (POS_B + SIZE_B) #define POS_Bx POS_k #define POS_Ax POS_A #define POS_sJ POS_A /* ** limits for opcode arguments. ** we use (signed) 'int' to manipulate most arguments, ** so they must fit in ints. */ /* Check whether type 'int' has at least 'b' bits ('b' < 32) */ #define L_INTHASBITS(b) ((UINT_MAX >> ((b) - 1)) >= 1) #if L_INTHASBITS(SIZE_Bx) #define MAXARG_Bx ((1<>1) /* 'sBx' is signed */ #if L_INTHASBITS(SIZE_Ax) #define MAXARG_Ax ((1<> 1) #define MAXARG_A ((1<> 1) #define int2sC(i) ((i) + OFFSET_sC) #define sC2int(i) ((i) - OFFSET_sC) /* creates a mask with 'n' 1 bits at position 'p' */ #define MASK1(n,p) ((~((~(Instruction)0)<<(n)))<<(p)) /* creates a mask with 'n' 0 bits at position 'p' */ #define MASK0(n,p) (~MASK1(n,p)) /* ** the following macros help to manipulate instructions */ #define GET_OPCODE(i) (cast(OpCode, ((i)>>POS_OP) & MASK1(SIZE_OP,0))) #define SET_OPCODE(i,o) ((i) = (((i)&MASK0(SIZE_OP,POS_OP)) | \ ((cast(Instruction, o)<>(pos)) & MASK1(size,0))) #define setarg(i,v,pos,size) ((i) = (((i)&MASK0(size,pos)) | \ ((cast(Instruction, v)<> sC */ OP_SHLI,/* A B sC R[A] := sC << R[B] */ OP_ADD,/* A B C R[A] := R[B] + R[C] */ OP_SUB,/* A B C R[A] := R[B] - R[C] */ OP_MUL,/* A B C R[A] := R[B] * R[C] */ OP_MOD,/* A B C R[A] := R[B] % R[C] */ OP_POW,/* A B C R[A] := R[B] ^ R[C] */ OP_DIV,/* A B C R[A] := R[B] / R[C] */ OP_IDIV,/* A B C R[A] := R[B] // R[C] */ OP_BAND,/* A B C R[A] := R[B] & R[C] */ OP_BOR,/* A B C R[A] := R[B] | R[C] */ OP_BXOR,/* A B C R[A] := R[B] ~ R[C] */ OP_SHL,/* A B C R[A] := R[B] << R[C] */ OP_SHR,/* A B C R[A] := R[B] >> R[C] */ OP_MMBIN,/* A B C call C metamethod over R[A] and R[B] (*) */ OP_MMBINI,/* A sB C k call C metamethod over R[A] and sB */ OP_MMBINK,/* A B C k call C metamethod over R[A] and K[B] */ OP_UNM,/* A B R[A] := -R[B] */ OP_BNOT,/* A B R[A] := ~R[B] */ OP_NOT,/* A B R[A] := not R[B] */ OP_LEN,/* A B R[A] := #R[B] (length operator) */ OP_CONCAT,/* A B R[A] := R[A].. ... ..R[A + B - 1] */ OP_CLOSE,/* A close all upvalues >= R[A] */ OP_TBC,/* A mark variable A "to be closed" */ OP_JMP,/* sJ pc += sJ */ OP_EQ,/* A B k if ((R[A] == R[B]) ~= k) then pc++ */ OP_LT,/* A B k if ((R[A] < R[B]) ~= k) then pc++ */ OP_LE,/* A B k if ((R[A] <= R[B]) ~= k) then pc++ */ OP_EQK,/* A B k if ((R[A] == K[B]) ~= k) then pc++ */ OP_EQI,/* A sB k if ((R[A] == sB) ~= k) then pc++ */ OP_LTI,/* A sB k if ((R[A] < sB) ~= k) then pc++ */ OP_LEI,/* A sB k if ((R[A] <= sB) ~= k) then pc++ */ OP_GTI,/* A sB k if ((R[A] > sB) ~= k) then pc++ */ OP_GEI,/* A sB k if ((R[A] >= sB) ~= k) then pc++ */ OP_TEST,/* A k if (not R[A] == k) then pc++ */ OP_TESTSET,/* A B k if (not R[B] == k) then pc++ else R[A] := R[B] (*) */ OP_CALL,/* A B C R[A], ... ,R[A+C-2] := R[A](R[A+1], ... ,R[A+B-1]) */ OP_TAILCALL,/* A B C k return R[A](R[A+1], ... ,R[A+B-1]) */ OP_RETURN,/* A B C k return R[A], ... ,R[A+B-2] (see note) */ OP_RETURN0,/* return */ OP_RETURN1,/* A return R[A] */ OP_FORLOOP,/* A Bx update counters; if loop continues then pc-=Bx; */ OP_FORPREP,/* A Bx ; if not to run then pc+=Bx+1; */ OP_TFORPREP,/* A Bx create upvalue for R[A + 3]; pc+=Bx */ OP_TFORCALL,/* A C R[A+4], ... ,R[A+3+C] := R[A](R[A+1], R[A+2]); */ OP_TFORLOOP,/* A Bx if R[A+2] ~= nil then { R[A]=R[A+2]; pc -= Bx } */ OP_SETLIST,/* A B C k R[A][C+i] := R[A+i], 1 <= i <= B */ OP_CLOSURE,/* A Bx R[A] := closure(KPROTO[Bx]) */ OP_VARARG,/* A C R[A], R[A+1], ..., R[A+C-2] = vararg */ OP_VARARGPREP,/*A (adjust vararg parameters) */ OP_EXTRAARG/* Ax extra (larger) argument for previous opcode */ } OpCode; #define NUM_OPCODES ((int)(OP_EXTRAARG) + 1) /*=========================================================================== Notes: (*) Opcode OP_LFALSESKIP is used to convert a condition to a boolean value, in a code equivalent to (not cond ? false : true). (It produces false and skips the next instruction producing true.) (*) Opcodes OP_MMBIN and variants follow each arithmetic and bitwise opcode. If the operation succeeds, it skips this next opcode. Otherwise, this opcode calls the corresponding metamethod. (*) Opcode OP_TESTSET is used in short-circuit expressions that need both to jump and to produce a value, such as (a = b or c). (*) In OP_CALL, if (B == 0) then B = top - A. If (C == 0), then 'top' is set to last_result+1, so next open instruction (OP_CALL, OP_RETURN*, OP_SETLIST) may use 'top'. (*) In OP_VARARG, if (C == 0) then use actual number of varargs and set top (like in OP_CALL with C == 0). (*) In OP_RETURN, if (B == 0) then return up to 'top'. (*) In OP_LOADKX and OP_NEWTABLE, the next instruction is always OP_EXTRAARG. (*) In OP_SETLIST, if (B == 0) then real B = 'top'; if k, then real C = EXTRAARG _ C (the bits of EXTRAARG concatenated with the bits of C). (*) In OP_NEWTABLE, B is log2 of the hash size (which is always a power of 2) plus 1, or zero for size zero. If not k, the array size is C. Otherwise, the array size is EXTRAARG _ C. (*) For comparisons, k specifies what condition the test should accept (true or false). (*) In OP_MMBINI/OP_MMBINK, k means the arguments were flipped (the constant is the first operand). (*) All 'skips' (pc++) assume that next instruction is a jump. (*) In instructions OP_RETURN/OP_TAILCALL, 'k' specifies that the function builds upvalues, which may need to be closed. C > 0 means the function is vararg, so that its 'func' must be corrected before returning; in this case, (C - 1) is its number of fixed parameters. (*) In comparisons with an immediate operand, C signals whether the original operand was a float. (It must be corrected in case of metamethods.) ===========================================================================*/ /* ** masks for instruction properties. The format is: ** bits 0-2: op mode ** bit 3: instruction set register A ** bit 4: operator is a test (next instruction must be a jump) ** bit 5: instruction uses 'L->top' set by previous instruction (when B == 0) ** bit 6: instruction sets 'L->top' for next instruction (when C == 0) ** bit 7: instruction is an MM instruction (call a metamethod) */ LUAI_DDEC(const lu_byte luaP_opmodes[NUM_OPCODES];) #define getOpMode(m) (cast(enum OpMode, luaP_opmodes[m] & 7)) #define testAMode(m) (luaP_opmodes[m] & (1 << 3)) #define testTMode(m) (luaP_opmodes[m] & (1 << 4)) #define testITMode(m) (luaP_opmodes[m] & (1 << 5)) #define testOTMode(m) (luaP_opmodes[m] & (1 << 6)) #define testMMMode(m) (luaP_opmodes[m] & (1 << 7)) /* "out top" (set top for next instruction) */ #define isOT(i) \ ((testOTMode(GET_OPCODE(i)) && GETARG_C(i) == 0) || \ GET_OPCODE(i) == OP_TAILCALL) /* "in top" (uses top from previous instruction) */ #define isIT(i) (testITMode(GET_OPCODE(i)) && GETARG_B(i) == 0) #define opmode(mm,ot,it,t,a,m) \ (((mm) << 7) | ((ot) << 6) | ((it) << 5) | ((t) << 4) | ((a) << 3) | (m)) /* number of list items to accumulate before a SETLIST instruction */ #define LFIELDS_PER_FLUSH 50 #endif /* ** $Id: ldebug.h $ ** Auxiliary functions from Debug Interface module ** See Copyright Notice in lua.h */ #ifndef ldebug_h #define ldebug_h /*#include "lstate.h"*/ #define pcRel(pc, p) (cast_int((pc) - (p)->code) - 1) /* Active Lua function (given call info) */ #define ci_func(ci) (clLvalue(s2v((ci)->func))) #define resethookcount(L) (L->hookcount = L->basehookcount) /* ** mark for entries in 'lineinfo' array that has absolute information in ** 'abslineinfo' array */ #define ABSLINEINFO (-0x80) /* ** MAXimum number of successive Instructions WiTHout ABSolute line ** information. (A power of two allows fast divisions.) */ #if !defined(MAXIWTHABS) #define MAXIWTHABS 128 #endif LUAI_FUNC int luaG_getfuncline (const Proto *f, int pc); LUAI_FUNC const char *luaG_findlocal (lua_State *L, CallInfo *ci, int n, StkId *pos); LUAI_FUNC l_noret luaG_typeerror (lua_State *L, const TValue *o, const char *opname); LUAI_FUNC l_noret luaG_callerror (lua_State *L, const TValue *o); LUAI_FUNC l_noret luaG_forerror (lua_State *L, const TValue *o, const char *what); LUAI_FUNC l_noret luaG_concaterror (lua_State *L, const TValue *p1, const TValue *p2); LUAI_FUNC l_noret luaG_opinterror (lua_State *L, const TValue *p1, const TValue *p2, const char *msg); LUAI_FUNC l_noret luaG_tointerror (lua_State *L, const TValue *p1, const TValue *p2); LUAI_FUNC l_noret luaG_ordererror (lua_State *L, const TValue *p1, const TValue *p2); LUAI_FUNC l_noret luaG_runerror (lua_State *L, const char *fmt, ...); LUAI_FUNC const char *luaG_addinfo (lua_State *L, const char *msg, TString *src, int line); LUAI_FUNC l_noret luaG_errormsg (lua_State *L); LUAI_FUNC int luaG_traceexec (lua_State *L, const Instruction *pc); #endif /* ** $Id: ldo.h $ ** Stack and Call structure of Lua ** See Copyright Notice in lua.h */ #ifndef ldo_h #define ldo_h /*#include "lobject.h"*/ /*#include "lstate.h"*/ /*#include "lzio.h"*/ /* ** Macro to check stack size and grow stack if needed. Parameters ** 'pre'/'pos' allow the macro to preserve a pointer into the ** stack across reallocations, doing the work only when needed. ** It also allows the running of one GC step when the stack is ** reallocated. ** 'condmovestack' is used in heavy tests to force a stack reallocation ** at every check. */ #define luaD_checkstackaux(L,n,pre,pos) \ if (l_unlikely(L->stack_last - L->top <= (n))) \ { pre; luaD_growstack(L, n, 1); pos; } \ else { condmovestack(L,pre,pos); } /* In general, 'pre'/'pos' are empty (nothing to save) */ #define luaD_checkstack(L,n) luaD_checkstackaux(L,n,(void)0,(void)0) #define savestack(L,p) ((char *)(p) - (char *)L->stack) #define restorestack(L,n) ((StkId)((char *)L->stack + (n))) /* macro to check stack size, preserving 'p' */ #define checkstackGCp(L,n,p) \ luaD_checkstackaux(L, n, \ ptrdiff_t t__ = savestack(L, p); /* save 'p' */ \ luaC_checkGC(L), /* stack grow uses memory */ \ p = restorestack(L, t__)) /* 'pos' part: restore 'p' */ /* macro to check stack size and GC */ #define checkstackGC(L,fsize) \ luaD_checkstackaux(L, (fsize), luaC_checkGC(L), (void)0) /* type of protected functions, to be ran by 'runprotected' */ typedef void (*Pfunc) (lua_State *L, void *ud); LUAI_FUNC void luaD_seterrorobj (lua_State *L, int errcode, StkId oldtop); LUAI_FUNC int luaD_protectedparser (lua_State *L, ZIO *z, const char *name, const char *mode); LUAI_FUNC void luaD_hook (lua_State *L, int event, int line, int fTransfer, int nTransfer); LUAI_FUNC void luaD_hookcall (lua_State *L, CallInfo *ci); LUAI_FUNC int luaD_pretailcall (lua_State *L, CallInfo *ci, StkId func, int narg1, int delta); LUAI_FUNC CallInfo *luaD_precall (lua_State *L, StkId func, int nResults); LUAI_FUNC void luaD_call (lua_State *L, StkId func, int nResults); LUAI_FUNC void luaD_callnoyield (lua_State *L, StkId func, int nResults); LUAI_FUNC StkId luaD_tryfuncTM (lua_State *L, StkId func); LUAI_FUNC int luaD_closeprotected (lua_State *L, ptrdiff_t level, int status); LUAI_FUNC int luaD_pcall (lua_State *L, Pfunc func, void *u, ptrdiff_t oldtop, ptrdiff_t ef); LUAI_FUNC void luaD_poscall (lua_State *L, CallInfo *ci, int nres); LUAI_FUNC int luaD_reallocstack (lua_State *L, int newsize, int raiseerror); LUAI_FUNC int luaD_growstack (lua_State *L, int n, int raiseerror); LUAI_FUNC void luaD_shrinkstack (lua_State *L); LUAI_FUNC void luaD_inctop (lua_State *L); LUAI_FUNC l_noret luaD_throw (lua_State *L, int errcode); LUAI_FUNC int luaD_rawrunprotected (lua_State *L, Pfunc f, void *ud); #endif /* ** $Id: lgc.h $ ** Garbage Collector ** See Copyright Notice in lua.h */ #ifndef lgc_h #define lgc_h /*#include "lobject.h"*/ /*#include "lstate.h"*/ /* ** Collectable objects may have one of three colors: white, which means ** the object is not marked; gray, which means the object is marked, but ** its references may be not marked; and black, which means that the ** object and all its references are marked. The main invariant of the ** garbage collector, while marking objects, is that a black object can ** never point to a white one. Moreover, any gray object must be in a ** "gray list" (gray, grayagain, weak, allweak, ephemeron) so that it ** can be visited again before finishing the collection cycle. (Open ** upvalues are an exception to this rule.) These lists have no meaning ** when the invariant is not being enforced (e.g., sweep phase). */ /* ** Possible states of the Garbage Collector */ #define GCSpropagate 0 #define GCSenteratomic 1 #define GCSatomic 2 #define GCSswpallgc 3 #define GCSswpfinobj 4 #define GCSswptobefnz 5 #define GCSswpend 6 #define GCScallfin 7 #define GCSpause 8 #define issweepphase(g) \ (GCSswpallgc <= (g)->gcstate && (g)->gcstate <= GCSswpend) /* ** macro to tell when main invariant (white objects cannot point to black ** ones) must be kept. During a collection, the sweep ** phase may break the invariant, as objects turned white may point to ** still-black objects. The invariant is restored when sweep ends and ** all objects are white again. */ #define keepinvariant(g) ((g)->gcstate <= GCSatomic) /* ** some useful bit tricks */ #define resetbits(x,m) ((x) &= cast_byte(~(m))) #define setbits(x,m) ((x) |= (m)) #define testbits(x,m) ((x) & (m)) #define bitmask(b) (1<<(b)) #define bit2mask(b1,b2) (bitmask(b1) | bitmask(b2)) #define l_setbit(x,b) setbits(x, bitmask(b)) #define resetbit(x,b) resetbits(x, bitmask(b)) #define testbit(x,b) testbits(x, bitmask(b)) /* ** Layout for bit use in 'marked' field. First three bits are ** used for object "age" in generational mode. Last bit is used ** by tests. */ #define WHITE0BIT 3 /* object is white (type 0) */ #define WHITE1BIT 4 /* object is white (type 1) */ #define BLACKBIT 5 /* object is black */ #define FINALIZEDBIT 6 /* object has been marked for finalization */ #define TESTBIT 7 #define WHITEBITS bit2mask(WHITE0BIT, WHITE1BIT) #define iswhite(x) testbits((x)->marked, WHITEBITS) #define isblack(x) testbit((x)->marked, BLACKBIT) #define isgray(x) /* neither white nor black */ \ (!testbits((x)->marked, WHITEBITS | bitmask(BLACKBIT))) #define tofinalize(x) testbit((x)->marked, FINALIZEDBIT) #define otherwhite(g) ((g)->currentwhite ^ WHITEBITS) #define isdeadm(ow,m) ((m) & (ow)) #define isdead(g,v) isdeadm(otherwhite(g), (v)->marked) #define changewhite(x) ((x)->marked ^= WHITEBITS) #define nw2black(x) \ check_exp(!iswhite(x), l_setbit((x)->marked, BLACKBIT)) #define luaC_white(g) cast_byte((g)->currentwhite & WHITEBITS) /* object age in generational mode */ #define G_NEW 0 /* created in current cycle */ #define G_SURVIVAL 1 /* created in previous cycle */ #define G_OLD0 2 /* marked old by frw. barrier in this cycle */ #define G_OLD1 3 /* first full cycle as old */ #define G_OLD 4 /* really old object (not to be visited) */ #define G_TOUCHED1 5 /* old object touched this cycle */ #define G_TOUCHED2 6 /* old object touched in previous cycle */ #define AGEBITS 7 /* all age bits (111) */ #define getage(o) ((o)->marked & AGEBITS) #define setage(o,a) ((o)->marked = cast_byte(((o)->marked & (~AGEBITS)) | a)) #define isold(o) (getage(o) > G_SURVIVAL) #define changeage(o,f,t) \ check_exp(getage(o) == (f), (o)->marked ^= ((f)^(t))) /* Default Values for GC parameters */ #define LUAI_GENMAJORMUL 100 #define LUAI_GENMINORMUL 20 /* wait memory to double before starting new cycle */ #define LUAI_GCPAUSE 200 /* ** some gc parameters are stored divided by 4 to allow a maximum value ** up to 1023 in a 'lu_byte'. */ #define getgcparam(p) ((p) * 4) #define setgcparam(p,v) ((p) = (v) / 4) #define LUAI_GCMUL 100 /* how much to allocate before next GC step (log2) */ #define LUAI_GCSTEPSIZE 13 /* 8 KB */ /* ** Check whether the declared GC mode is generational. While in ** generational mode, the collector can go temporarily to incremental ** mode to improve performance. This is signaled by 'g->lastatomic != 0'. */ #define isdecGCmodegen(g) (g->gckind == KGC_GEN || g->lastatomic != 0) /* ** Control when GC is running: */ #define GCSTPUSR 1 /* bit true when GC stopped by user */ #define GCSTPGC 2 /* bit true when GC stopped by itself */ #define GCSTPCLS 4 /* bit true when closing Lua state */ #define gcrunning(g) ((g)->gcstp == 0) /* ** Does one step of collection when debt becomes positive. 'pre'/'pos' ** allows some adjustments to be done only when needed. macro ** 'condchangemem' is used only for heavy tests (forcing a full ** GC cycle on every opportunity) */ #define luaC_condGC(L,pre,pos) \ { if (G(L)->GCdebt > 0) { pre; luaC_step(L); pos;}; \ condchangemem(L,pre,pos); } /* more often than not, 'pre'/'pos' are empty */ #define luaC_checkGC(L) luaC_condGC(L,(void)0,(void)0) #define luaC_barrier(L,p,v) ( \ (iscollectable(v) && isblack(p) && iswhite(gcvalue(v))) ? \ luaC_barrier_(L,obj2gco(p),gcvalue(v)) : cast_void(0)) #define luaC_barrierback(L,p,v) ( \ (iscollectable(v) && isblack(p) && iswhite(gcvalue(v))) ? \ luaC_barrierback_(L,p) : cast_void(0)) #define luaC_objbarrier(L,p,o) ( \ (isblack(p) && iswhite(o)) ? \ luaC_barrier_(L,obj2gco(p),obj2gco(o)) : cast_void(0)) LUAI_FUNC void luaC_fix (lua_State *L, GCObject *o); LUAI_FUNC void luaC_freeallobjects (lua_State *L); LUAI_FUNC void luaC_step (lua_State *L); LUAI_FUNC void luaC_runtilstate (lua_State *L, int statesmask); LUAI_FUNC void luaC_fullgc (lua_State *L, int isemergency); LUAI_FUNC GCObject *luaC_newobj (lua_State *L, int tt, size_t sz); LUAI_FUNC void luaC_barrier_ (lua_State *L, GCObject *o, GCObject *v); LUAI_FUNC void luaC_barrierback_ (lua_State *L, GCObject *o); LUAI_FUNC void luaC_checkfinalizer (lua_State *L, GCObject *o, Table *mt); LUAI_FUNC void luaC_changemode (lua_State *L, int newmode); #endif /* ** $Id: lfunc.h $ ** Auxiliary functions to manipulate prototypes and closures ** See Copyright Notice in lua.h */ #ifndef lfunc_h #define lfunc_h /*#include "lobject.h"*/ #define sizeCclosure(n) (cast_int(offsetof(CClosure, upvalue)) + \ cast_int(sizeof(TValue)) * (n)) #define sizeLclosure(n) (cast_int(offsetof(LClosure, upvals)) + \ cast_int(sizeof(TValue *)) * (n)) /* test whether thread is in 'twups' list */ #define isintwups(L) (L->twups != L) /* ** maximum number of upvalues in a closure (both C and Lua). (Value ** must fit in a VM register.) */ #define MAXUPVAL 255 #define upisopen(up) ((up)->v != &(up)->u.value) #define uplevel(up) check_exp(upisopen(up), cast(StkId, (up)->v)) /* ** maximum number of misses before giving up the cache of closures ** in prototypes */ #define MAXMISS 10 /* special status to close upvalues preserving the top of the stack */ #define CLOSEKTOP (-1) LUAI_FUNC Proto *luaF_newproto (lua_State *L); LUAI_FUNC CClosure *luaF_newCclosure (lua_State *L, int nupvals); LUAI_FUNC LClosure *luaF_newLclosure (lua_State *L, int nupvals); LUAI_FUNC void luaF_initupvals (lua_State *L, LClosure *cl); LUAI_FUNC UpVal *luaF_findupval (lua_State *L, StkId level); LUAI_FUNC void luaF_newtbcupval (lua_State *L, StkId level); LUAI_FUNC void luaF_closeupval (lua_State *L, StkId level); LUAI_FUNC void luaF_close (lua_State *L, StkId level, int status, int yy); LUAI_FUNC void luaF_unlinkupval (UpVal *uv); LUAI_FUNC void luaF_freeproto (lua_State *L, Proto *f); LUAI_FUNC const char *luaF_getlocalname (const Proto *func, int local_number, int pc); #endif /* ** $Id: lstring.h $ ** String table (keep all strings handled by Lua) ** See Copyright Notice in lua.h */ #ifndef lstring_h #define lstring_h /*#include "lgc.h"*/ /*#include "lobject.h"*/ /*#include "lstate.h"*/ /* ** Memory-allocation error message must be preallocated (it cannot ** be created after memory is exhausted) */ #define MEMERRMSG "not enough memory" /* ** Size of a TString: Size of the header plus space for the string ** itself (including final '\0'). */ #define sizelstring(l) (offsetof(TString, contents) + ((l) + 1) * sizeof(char)) #define luaS_newliteral(L, s) (luaS_newlstr(L, "" s, \ (sizeof(s)/sizeof(char))-1)) /* ** test whether a string is a reserved word */ #define isreserved(s) ((s)->tt == LUA_VSHRSTR && (s)->extra > 0) /* ** equality for short strings, which are always internalized */ #define eqshrstr(a,b) check_exp((a)->tt == LUA_VSHRSTR, (a) == (b)) LUAI_FUNC unsigned int luaS_hash (const char *str, size_t l, unsigned int seed); LUAI_FUNC unsigned int luaS_hashlongstr (TString *ts); LUAI_FUNC int luaS_eqlngstr (TString *a, TString *b); LUAI_FUNC void luaS_resize (lua_State *L, int newsize); LUAI_FUNC void luaS_clearcache (global_State *g); LUAI_FUNC void luaS_init (lua_State *L); LUAI_FUNC void luaS_remove (lua_State *L, TString *ts); LUAI_FUNC Udata *luaS_newudata (lua_State *L, size_t s, int nuvalue); LUAI_FUNC TString *luaS_newlstr (lua_State *L, const char *str, size_t l); LUAI_FUNC TString *luaS_new (lua_State *L, const char *str); LUAI_FUNC TString *luaS_createlngstrobj (lua_State *L, size_t l); #endif /* ** $Id: lundump.h $ ** load precompiled Lua chunks ** See Copyright Notice in lua.h */ #ifndef lundump_h #define lundump_h /*#include "llimits.h"*/ /*#include "lobject.h"*/ /*#include "lzio.h"*/ /* data to catch conversion errors */ #define LUAC_DATA "\x19\x93\r\n\x1a\n" #define LUAC_INT 0x5678 #define LUAC_NUM cast_num(370.5) /* ** Encode major-minor version in one byte, one nibble for each */ #define MYINT(s) (s[0]-'0') /* assume one-digit numerals */ #define LUAC_VERSION (MYINT(LUA_VERSION_MAJOR)*16+MYINT(LUA_VERSION_MINOR)) #define LUAC_FORMAT 0 /* this is the official format */ /* load one chunk; from lundump.c */ LUAI_FUNC LClosure* luaU_undump (lua_State* L, ZIO* Z, const char* name); /* dump one chunk; from ldump.c */ LUAI_FUNC int luaU_dump (lua_State* L, const Proto* f, lua_Writer w, void* data, int strip); #endif /* ** $Id: lapi.h $ ** Auxiliary functions from Lua API ** See Copyright Notice in lua.h */ #ifndef lapi_h #define lapi_h /*#include "llimits.h"*/ /*#include "lstate.h"*/ /* Increments 'L->top', checking for stack overflows */ #define api_incr_top(L) {L->top++; api_check(L, L->top <= L->ci->top, \ "stack overflow");} /* ** If a call returns too many multiple returns, the callee may not have ** stack space to accommodate all results. In this case, this macro ** increases its stack space ('L->ci->top'). */ #define adjustresults(L,nres) \ { if ((nres) <= LUA_MULTRET && L->ci->top < L->top) L->ci->top = L->top; } /* Ensure the stack has at least 'n' elements */ #define api_checknelems(L,n) api_check(L, (n) < (L->top - L->ci->func), \ "not enough elements in the stack") /* ** To reduce the overhead of returning from C functions, the presence of ** to-be-closed variables in these functions is coded in the CallInfo's ** field 'nresults', in a way that functions with no to-be-closed variables ** with zero, one, or "all" wanted results have no overhead. Functions ** with other number of wanted results, as well as functions with ** variables to be closed, have an extra check. */ #define hastocloseCfunc(n) ((n) < LUA_MULTRET) /* Map [-1, inf) (range of 'nresults') into (-inf, -2] */ #define codeNresults(n) (-(n) - 3) #define decodeNresults(n) (-(n) - 3) #endif /* ** $Id: llex.h $ ** Lexical Analyzer ** See Copyright Notice in lua.h */ #ifndef llex_h #define llex_h #include /*#include "lobject.h"*/ /*#include "lzio.h"*/ /* ** Single-char tokens (terminal symbols) are represented by their own ** numeric code. Other tokens start at the following value. */ #define FIRST_RESERVED (UCHAR_MAX + 1) #if !defined(LUA_ENV) #define LUA_ENV "_ENV" #endif /* * WARNING: if you change the order of this enumeration, * grep "ORDER RESERVED" */ enum RESERVED { /* terminal symbols denoted by reserved words */ TK_AND = FIRST_RESERVED, TK_BREAK, TK_DO, TK_ELSE, TK_ELSEIF, TK_END, TK_FALSE, TK_FOR, TK_FUNCTION, TK_GOTO, TK_IF, TK_IN, TK_LOCAL, TK_NIL, TK_NOT, TK_OR, TK_REPEAT, TK_RETURN, TK_THEN, TK_TRUE, TK_UNTIL, TK_WHILE, /* other terminal symbols */ TK_IDIV, TK_CONCAT, TK_DOTS, TK_EQ, TK_GE, TK_LE, TK_NE, TK_SHL, TK_SHR, TK_DBCOLON, TK_EOS, TK_FLT, TK_INT, TK_NAME, TK_STRING }; /* number of reserved words */ #define NUM_RESERVED (cast_int(TK_WHILE-FIRST_RESERVED + 1)) typedef union { lua_Number r; lua_Integer i; TString *ts; } SemInfo; /* semantics information */ typedef struct Token { int token; SemInfo seminfo; } Token; /* state of the lexer plus state of the parser when shared by all functions */ typedef struct LexState { int current; /* current character (charint) */ int linenumber; /* input line counter */ int lastline; /* line of last token 'consumed' */ Token t; /* current token */ Token lookahead; /* look ahead token */ struct FuncState *fs; /* current function (parser) */ struct lua_State *L; ZIO *z; /* input stream */ Mbuffer *buff; /* buffer for tokens */ Table *h; /* to avoid collection/reuse strings */ struct Dyndata *dyd; /* dynamic structures used by the parser */ TString *source; /* current source name */ TString *envn; /* environment variable name */ } LexState; LUAI_FUNC void luaX_init (lua_State *L); LUAI_FUNC void luaX_setinput (lua_State *L, LexState *ls, ZIO *z, TString *source, int firstchar); LUAI_FUNC TString *luaX_newstring (LexState *ls, const char *str, size_t l); LUAI_FUNC void luaX_next (LexState *ls); LUAI_FUNC int luaX_lookahead (LexState *ls); LUAI_FUNC l_noret luaX_syntaxerror (LexState *ls, const char *s); LUAI_FUNC const char *luaX_token2str (LexState *ls, int token); #endif /* ** $Id: ltable.h $ ** Lua tables (hash) ** See Copyright Notice in lua.h */ #ifndef ltable_h #define ltable_h /*#include "lobject.h"*/ #define gnode(t,i) (&(t)->node[i]) #define gval(n) (&(n)->i_val) #define gnext(n) ((n)->u.next) /* ** Clear all bits of fast-access metamethods, which means that the table ** may have any of these metamethods. (First access that fails after the ** clearing will set the bit again.) */ #define invalidateTMcache(t) ((t)->flags &= ~maskflags) /* true when 't' is using 'dummynode' as its hash part */ #define isdummy(t) ((t)->lastfree == NULL) /* allocated size for hash nodes */ #define allocsizenode(t) (isdummy(t) ? 0 : sizenode(t)) /* returns the Node, given the value of a table entry */ #define nodefromval(v) cast(Node *, (v)) LUAI_FUNC const TValue *luaH_getint (Table *t, lua_Integer key); LUAI_FUNC void luaH_setint (lua_State *L, Table *t, lua_Integer key, TValue *value); LUAI_FUNC const TValue *luaH_getshortstr (Table *t, TString *key); LUAI_FUNC const TValue *luaH_getstr (Table *t, TString *key); LUAI_FUNC const TValue *luaH_get (Table *t, const TValue *key); LUAI_FUNC void luaH_newkey (lua_State *L, Table *t, const TValue *key, TValue *value); LUAI_FUNC void luaH_set (lua_State *L, Table *t, const TValue *key, TValue *value); LUAI_FUNC void luaH_finishset (lua_State *L, Table *t, const TValue *key, const TValue *slot, TValue *value); LUAI_FUNC Table *luaH_new (lua_State *L); LUAI_FUNC void luaH_resize (lua_State *L, Table *t, unsigned int nasize, unsigned int nhsize); LUAI_FUNC void luaH_resizearray (lua_State *L, Table *t, unsigned int nasize); LUAI_FUNC void luaH_free (lua_State *L, Table *t); LUAI_FUNC int luaH_next (lua_State *L, Table *t, StkId key); LUAI_FUNC lua_Unsigned luaH_getn (Table *t); LUAI_FUNC unsigned int luaH_realasize (const Table *t); #if defined(LUA_DEBUG) LUAI_FUNC Node *luaH_mainposition (const Table *t, const TValue *key); LUAI_FUNC int luaH_isdummy (const Table *t); #endif #endif /* ** $Id: lparser.h $ ** Lua Parser ** See Copyright Notice in lua.h */ #ifndef lparser_h #define lparser_h /*#include "llimits.h"*/ /*#include "lobject.h"*/ /*#include "lzio.h"*/ /* ** Expression and variable descriptor. ** Code generation for variables and expressions can be delayed to allow ** optimizations; An 'expdesc' structure describes a potentially-delayed ** variable/expression. It has a description of its "main" value plus a ** list of conditional jumps that can also produce its value (generated ** by short-circuit operators 'and'/'or'). */ /* kinds of variables/expressions */ typedef enum { VVOID, /* when 'expdesc' describes the last expression of a list, this kind means an empty list (so, no expression) */ VNIL, /* constant nil */ VTRUE, /* constant true */ VFALSE, /* constant false */ VK, /* constant in 'k'; info = index of constant in 'k' */ VKFLT, /* floating constant; nval = numerical float value */ VKINT, /* integer constant; ival = numerical integer value */ VKSTR, /* string constant; strval = TString address; (string is fixed by the lexer) */ VNONRELOC, /* expression has its value in a fixed register; info = result register */ VLOCAL, /* local variable; var.ridx = register index; var.vidx = relative index in 'actvar.arr' */ VUPVAL, /* upvalue variable; info = index of upvalue in 'upvalues' */ VCONST, /* compile-time variable; info = absolute index in 'actvar.arr' */ VINDEXED, /* indexed variable; ind.t = table register; ind.idx = key's R index */ VINDEXUP, /* indexed upvalue; ind.t = table upvalue; ind.idx = key's K index */ VINDEXI, /* indexed variable with constant integer; ind.t = table register; ind.idx = key's value */ VINDEXSTR, /* indexed variable with literal string; ind.t = table register; ind.idx = key's K index */ VJMP, /* expression is a test/comparison; info = pc of corresponding jump instruction */ VRELOC, /* expression can put result in any register; info = instruction pc */ VCALL, /* expression is a function call; info = instruction pc */ VVARARG /* vararg expression; info = instruction pc */ } expkind; #define vkisvar(k) (VLOCAL <= (k) && (k) <= VINDEXSTR) #define vkisindexed(k) (VINDEXED <= (k) && (k) <= VINDEXSTR) typedef struct expdesc { expkind k; union { lua_Integer ival; /* for VKINT */ lua_Number nval; /* for VKFLT */ TString *strval; /* for VKSTR */ int info; /* for generic use */ struct { /* for indexed variables */ short idx; /* index (R or "long" K) */ lu_byte t; /* table (register or upvalue) */ } ind; struct { /* for local variables */ lu_byte ridx; /* register holding the variable */ unsigned short vidx; /* compiler index (in 'actvar.arr') */ } var; } u; int t; /* patch list of 'exit when true' */ int f; /* patch list of 'exit when false' */ } expdesc; /* kinds of variables */ #define VDKREG 0 /* regular */ #define RDKCONST 1 /* constant */ #define RDKTOCLOSE 2 /* to-be-closed */ #define RDKCTC 3 /* compile-time constant */ /* description of an active local variable */ typedef union Vardesc { struct { TValuefields; /* constant value (if it is a compile-time constant) */ lu_byte kind; lu_byte ridx; /* register holding the variable */ short pidx; /* index of the variable in the Proto's 'locvars' array */ TString *name; /* variable name */ } vd; TValue k; /* constant value (if any) */ } Vardesc; /* description of pending goto statements and label statements */ typedef struct Labeldesc { TString *name; /* label identifier */ int pc; /* position in code */ int line; /* line where it appeared */ lu_byte nactvar; /* number of active variables in that position */ lu_byte close; /* goto that escapes upvalues */ } Labeldesc; /* list of labels or gotos */ typedef struct Labellist { Labeldesc *arr; /* array */ int n; /* number of entries in use */ int size; /* array size */ } Labellist; /* dynamic structures used by the parser */ typedef struct Dyndata { struct { /* list of all active local variables */ Vardesc *arr; int n; int size; } actvar; Labellist gt; /* list of pending gotos */ Labellist label; /* list of active labels */ } Dyndata; /* control of blocks */ struct BlockCnt; /* defined in lparser.c */ /* state needed to generate code for a given function */ typedef struct FuncState { Proto *f; /* current function header */ struct FuncState *prev; /* enclosing function */ struct LexState *ls; /* lexical state */ struct BlockCnt *bl; /* chain of current blocks */ int pc; /* next position to code (equivalent to 'ncode') */ int lasttarget; /* 'label' of last 'jump label' */ int previousline; /* last line that was saved in 'lineinfo' */ int nk; /* number of elements in 'k' */ int np; /* number of elements in 'p' */ int nabslineinfo; /* number of elements in 'abslineinfo' */ int firstlocal; /* index of first local var (in Dyndata array) */ int firstlabel; /* index of first label (in 'dyd->label->arr') */ short ndebugvars; /* number of elements in 'f->locvars' */ lu_byte nactvar; /* number of active local variables */ lu_byte nups; /* number of upvalues */ lu_byte freereg; /* first free register */ lu_byte iwthabs; /* instructions issued since last absolute line info */ lu_byte needclose; /* function needs to close upvalues when returning */ } FuncState; LUAI_FUNC int luaY_nvarstack (FuncState *fs); LUAI_FUNC LClosure *luaY_parser (lua_State *L, ZIO *z, Mbuffer *buff, Dyndata *dyd, const char *name, int firstchar); #endif /* ** $Id: lcode.h $ ** Code generator for Lua ** See Copyright Notice in lua.h */ #ifndef lcode_h #define lcode_h /*#include "llex.h"*/ /*#include "lobject.h"*/ /*#include "lopcodes.h"*/ /*#include "lparser.h"*/ /* ** Marks the end of a patch list. It is an invalid value both as an absolute ** address, and as a list link (would link an element to itself). */ #define NO_JUMP (-1) /* ** grep "ORDER OPR" if you change these enums (ORDER OP) */ typedef enum BinOpr { /* arithmetic operators */ OPR_ADD, OPR_SUB, OPR_MUL, OPR_MOD, OPR_POW, OPR_DIV, OPR_IDIV, /* bitwise operators */ OPR_BAND, OPR_BOR, OPR_BXOR, OPR_SHL, OPR_SHR, /* string operator */ OPR_CONCAT, /* comparison operators */ OPR_EQ, OPR_LT, OPR_LE, OPR_NE, OPR_GT, OPR_GE, /* logical operators */ OPR_AND, OPR_OR, OPR_NOBINOPR } BinOpr; /* true if operation is foldable (that is, it is arithmetic or bitwise) */ #define foldbinop(op) ((op) <= OPR_SHR) #define luaK_codeABC(fs,o,a,b,c) luaK_codeABCk(fs,o,a,b,c,0) typedef enum UnOpr { OPR_MINUS, OPR_BNOT, OPR_NOT, OPR_LEN, OPR_NOUNOPR } UnOpr; /* get (pointer to) instruction of given 'expdesc' */ #define getinstruction(fs,e) ((fs)->f->code[(e)->u.info]) #define luaK_setmultret(fs,e) luaK_setreturns(fs, e, LUA_MULTRET) #define luaK_jumpto(fs,t) luaK_patchlist(fs, luaK_jump(fs), t) LUAI_FUNC int luaK_code (FuncState *fs, Instruction i); LUAI_FUNC int luaK_codeABx (FuncState *fs, OpCode o, int A, unsigned int Bx); LUAI_FUNC int luaK_codeAsBx (FuncState *fs, OpCode o, int A, int Bx); LUAI_FUNC int luaK_codeABCk (FuncState *fs, OpCode o, int A, int B, int C, int k); LUAI_FUNC int luaK_isKint (expdesc *e); LUAI_FUNC int luaK_exp2const (FuncState *fs, const expdesc *e, TValue *v); LUAI_FUNC void luaK_fixline (FuncState *fs, int line); LUAI_FUNC void luaK_nil (FuncState *fs, int from, int n); LUAI_FUNC void luaK_reserveregs (FuncState *fs, int n); LUAI_FUNC void luaK_checkstack (FuncState *fs, int n); LUAI_FUNC void luaK_int (FuncState *fs, int reg, lua_Integer n); LUAI_FUNC void luaK_dischargevars (FuncState *fs, expdesc *e); LUAI_FUNC int luaK_exp2anyreg (FuncState *fs, expdesc *e); LUAI_FUNC void luaK_exp2anyregup (FuncState *fs, expdesc *e); LUAI_FUNC void luaK_exp2nextreg (FuncState *fs, expdesc *e); LUAI_FUNC void luaK_exp2val (FuncState *fs, expdesc *e); LUAI_FUNC int luaK_exp2RK (FuncState *fs, expdesc *e); LUAI_FUNC void luaK_self (FuncState *fs, expdesc *e, expdesc *key); LUAI_FUNC void luaK_indexed (FuncState *fs, expdesc *t, expdesc *k); LUAI_FUNC void luaK_goiftrue (FuncState *fs, expdesc *e); LUAI_FUNC void luaK_goiffalse (FuncState *fs, expdesc *e); LUAI_FUNC void luaK_storevar (FuncState *fs, expdesc *var, expdesc *e); LUAI_FUNC void luaK_setreturns (FuncState *fs, expdesc *e, int nresults); LUAI_FUNC void luaK_setoneret (FuncState *fs, expdesc *e); LUAI_FUNC int luaK_jump (FuncState *fs); LUAI_FUNC void luaK_ret (FuncState *fs, int first, int nret); LUAI_FUNC void luaK_patchlist (FuncState *fs, int list, int target); LUAI_FUNC void luaK_patchtohere (FuncState *fs, int list); LUAI_FUNC void luaK_concat (FuncState *fs, int *l1, int l2); LUAI_FUNC int luaK_getlabel (FuncState *fs); LUAI_FUNC void luaK_prefix (FuncState *fs, UnOpr op, expdesc *v, int line); LUAI_FUNC void luaK_infix (FuncState *fs, BinOpr op, expdesc *v); LUAI_FUNC void luaK_posfix (FuncState *fs, BinOpr op, expdesc *v1, expdesc *v2, int line); LUAI_FUNC void luaK_settablesize (FuncState *fs, int pc, int ra, int asize, int hsize); LUAI_FUNC void luaK_setlist (FuncState *fs, int base, int nelems, int tostore); LUAI_FUNC void luaK_finish (FuncState *fs); LUAI_FUNC l_noret luaK_semerror (LexState *ls, const char *msg); #endif /* ** $Id: lvm.h $ ** Lua virtual machine ** See Copyright Notice in lua.h */ #ifndef lvm_h #define lvm_h /*#include "ldo.h"*/ /*#include "lobject.h"*/ /*#include "ltm.h"*/ #if !defined(LUA_NOCVTN2S) #define cvt2str(o) ttisnumber(o) #else #define cvt2str(o) 0 /* no conversion from numbers to strings */ #endif #if !defined(LUA_NOCVTS2N) #define cvt2num(o) ttisstring(o) #else #define cvt2num(o) 0 /* no conversion from strings to numbers */ #endif /* ** You can define LUA_FLOORN2I if you want to convert floats to integers ** by flooring them (instead of raising an error if they are not ** integral values) */ #if !defined(LUA_FLOORN2I) #define LUA_FLOORN2I F2Ieq #endif /* ** Rounding modes for float->integer coercion */ typedef enum { F2Ieq, /* no rounding; accepts only integral values */ F2Ifloor, /* takes the floor of the number */ F2Iceil /* takes the ceil of the number */ } F2Imod; /* convert an object to a float (including string coercion) */ #define tonumber(o,n) \ (ttisfloat(o) ? (*(n) = fltvalue(o), 1) : luaV_tonumber_(o,n)) /* convert an object to a float (without string coercion) */ #define tonumberns(o,n) \ (ttisfloat(o) ? ((n) = fltvalue(o), 1) : \ (ttisinteger(o) ? ((n) = cast_num(ivalue(o)), 1) : 0)) /* convert an object to an integer (including string coercion) */ #define tointeger(o,i) \ (l_likely(ttisinteger(o)) ? (*(i) = ivalue(o), 1) \ : luaV_tointeger(o,i,LUA_FLOORN2I)) /* convert an object to an integer (without string coercion) */ #define tointegerns(o,i) \ (l_likely(ttisinteger(o)) ? (*(i) = ivalue(o), 1) \ : luaV_tointegerns(o,i,LUA_FLOORN2I)) #define intop(op,v1,v2) l_castU2S(l_castS2U(v1) op l_castS2U(v2)) #define luaV_rawequalobj(t1,t2) luaV_equalobj(NULL,t1,t2) /* ** fast track for 'gettable': if 't' is a table and 't[k]' is present, ** return 1 with 'slot' pointing to 't[k]' (position of final result). ** Otherwise, return 0 (meaning it will have to check metamethod) ** with 'slot' pointing to an empty 't[k]' (if 't' is a table) or NULL ** (otherwise). 'f' is the raw get function to use. */ #define luaV_fastget(L,t,k,slot,f) \ (!ttistable(t) \ ? (slot = NULL, 0) /* not a table; 'slot' is NULL and result is 0 */ \ : (slot = f(hvalue(t), k), /* else, do raw access */ \ !isempty(slot))) /* result not empty? */ /* ** Special case of 'luaV_fastget' for integers, inlining the fast case ** of 'luaH_getint'. */ #define luaV_fastgeti(L,t,k,slot) \ (!ttistable(t) \ ? (slot = NULL, 0) /* not a table; 'slot' is NULL and result is 0 */ \ : (slot = (l_castS2U(k) - 1u < hvalue(t)->alimit) \ ? &hvalue(t)->array[k - 1] : luaH_getint(hvalue(t), k), \ !isempty(slot))) /* result not empty? */ /* ** Finish a fast set operation (when fast get succeeds). In that case, ** 'slot' points to the place to put the value. */ #define luaV_finishfastset(L,t,slot,v) \ { setobj2t(L, cast(TValue *,slot), v); \ luaC_barrierback(L, gcvalue(t), v); } LUAI_FUNC int luaV_equalobj (lua_State *L, const TValue *t1, const TValue *t2); LUAI_FUNC int luaV_lessthan (lua_State *L, const TValue *l, const TValue *r); LUAI_FUNC int luaV_lessequal (lua_State *L, const TValue *l, const TValue *r); LUAI_FUNC int luaV_tonumber_ (const TValue *obj, lua_Number *n); LUAI_FUNC int luaV_tointeger (const TValue *obj, lua_Integer *p, F2Imod mode); LUAI_FUNC int luaV_tointegerns (const TValue *obj, lua_Integer *p, F2Imod mode); LUAI_FUNC int luaV_flttointeger (lua_Number n, lua_Integer *p, F2Imod mode); LUAI_FUNC void luaV_finishget (lua_State *L, const TValue *t, TValue *key, StkId val, const TValue *slot); LUAI_FUNC void luaV_finishset (lua_State *L, const TValue *t, TValue *key, TValue *val, const TValue *slot); LUAI_FUNC void luaV_finishOp (lua_State *L); LUAI_FUNC void luaV_execute (lua_State *L, CallInfo *ci); LUAI_FUNC void luaV_concat (lua_State *L, int total); LUAI_FUNC lua_Integer luaV_idiv (lua_State *L, lua_Integer x, lua_Integer y); LUAI_FUNC lua_Integer luaV_mod (lua_State *L, lua_Integer x, lua_Integer y); LUAI_FUNC lua_Number luaV_modf (lua_State *L, lua_Number x, lua_Number y); LUAI_FUNC lua_Integer luaV_shiftl (lua_Integer x, lua_Integer y); LUAI_FUNC void luaV_objlen (lua_State *L, StkId ra, const TValue *rb); #endif /* ** $Id: lctype.h $ ** 'ctype' functions for Lua ** See Copyright Notice in lua.h */ #ifndef lctype_h #define lctype_h /*#include "lua.h"*/ /* ** WARNING: the functions defined here do not necessarily correspond ** to the similar functions in the standard C ctype.h. They are ** optimized for the specific needs of Lua. */ #if !defined(LUA_USE_CTYPE) #if 'A' == 65 && '0' == 48 /* ASCII case: can use its own tables; faster and fixed */ #define LUA_USE_CTYPE 0 #else /* must use standard C ctype */ #define LUA_USE_CTYPE 1 #endif #endif #if !LUA_USE_CTYPE /* { */ #include /*#include "llimits.h"*/ #define ALPHABIT 0 #define DIGITBIT 1 #define PRINTBIT 2 #define SPACEBIT 3 #define XDIGITBIT 4 #define MASK(B) (1 << (B)) /* ** add 1 to char to allow index -1 (EOZ) */ #define testprop(c,p) (luai_ctype_[(c)+1] & (p)) /* ** 'lalpha' (Lua alphabetic) and 'lalnum' (Lua alphanumeric) both include '_' */ #define lislalpha(c) testprop(c, MASK(ALPHABIT)) #define lislalnum(c) testprop(c, (MASK(ALPHABIT) | MASK(DIGITBIT))) #define lisdigit(c) testprop(c, MASK(DIGITBIT)) #define lisspace(c) testprop(c, MASK(SPACEBIT)) #define lisprint(c) testprop(c, MASK(PRINTBIT)) #define lisxdigit(c) testprop(c, MASK(XDIGITBIT)) /* ** In ASCII, this 'ltolower' is correct for alphabetic characters and ** for '.'. That is enough for Lua needs. ('check_exp' ensures that ** the character either is an upper-case letter or is unchanged by ** the transformation, which holds for lower-case letters and '.'.) */ #define ltolower(c) \ check_exp(('A' <= (c) && (c) <= 'Z') || (c) == ((c) | ('A' ^ 'a')), \ (c) | ('A' ^ 'a')) /* one entry for each character and for -1 (EOZ) */ LUAI_DDEC(const lu_byte luai_ctype_[UCHAR_MAX + 2];) #else /* }{ */ /* ** use standard C ctypes */ #include #define lislalpha(c) (isalpha(c) || (c) == '_') #define lislalnum(c) (isalnum(c) || (c) == '_') #define lisdigit(c) (isdigit(c)) #define lisspace(c) (isspace(c)) #define lisprint(c) (isprint(c)) #define lisxdigit(c) (isxdigit(c)) #define ltolower(c) (tolower(c)) #endif /* } */ #endif /* ** $Id: lzio.c $ ** Buffered streams ** See Copyright Notice in lua.h */ #define lzio_c #define LUA_CORE /*#include "lprefix.h"*/ #include /*#include "lua.h"*/ /*#include "llimits.h"*/ /*#include "lmem.h"*/ /*#include "lstate.h"*/ /*#include "lzio.h"*/ int luaZ_fill (ZIO *z) { size_t size; lua_State *L = z->L; const char *buff; lua_unlock(L); buff = z->reader(L, z->data, &size); lua_lock(L); if (buff == NULL || size == 0) return EOZ; z->n = size - 1; /* discount char being returned */ z->p = buff; return cast_uchar(*(z->p++)); } void luaZ_init (lua_State *L, ZIO *z, lua_Reader reader, void *data) { z->L = L; z->reader = reader; z->data = data; z->n = 0; z->p = NULL; } /* --------------------------------------------------------------- read --- */ size_t luaZ_read (ZIO *z, void *b, size_t n) { while (n) { size_t m; if (z->n == 0) { /* no bytes in buffer? */ if (luaZ_fill(z) == EOZ) /* try to read more */ return n; /* no more input; return number of missing bytes */ else { z->n++; /* luaZ_fill consumed first byte; put it back */ z->p--; } } m = (n <= z->n) ? n : z->n; /* min. between n and z->n */ memcpy(b, z->p, m); z->n -= m; z->p += m; b = (char *)b + m; n -= m; } return 0; } /* ** $Id: lctype.c $ ** 'ctype' functions for Lua ** See Copyright Notice in lua.h */ #define lctype_c #define LUA_CORE /*#include "lprefix.h"*/ /*#include "lctype.h"*/ #if !LUA_USE_CTYPE /* { */ #include #if defined (LUA_UCID) /* accept UniCode IDentifiers? */ /* consider all non-ascii codepoints to be alphabetic */ #define NONA 0x01 #else #define NONA 0x00 /* default */ #endif LUAI_DDEF const lu_byte luai_ctype_[UCHAR_MAX + 2] = { 0x00, /* EOZ */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0. */ 0x00, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 1. */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, /* 2. */ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, /* 3. */ 0x16, 0x16, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x05, /* 4. */ 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, /* 5. */ 0x05, 0x05, 0x05, 0x04, 0x04, 0x04, 0x04, 0x05, 0x04, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x05, /* 6. */ 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, /* 7. */ 0x05, 0x05, 0x05, 0x04, 0x04, 0x04, 0x04, 0x00, NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, /* 8. */ NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, /* 9. */ NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, /* a. */ NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, /* b. */ NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, 0x00, 0x00, NONA, NONA, NONA, NONA, NONA, NONA, /* c. */ NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, /* d. */ NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, /* e. */ NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, 0x00, 0x00, 0x00, /* f. */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; #endif /* } */ /* ** $Id: lopcodes.c $ ** Opcodes for Lua virtual machine ** See Copyright Notice in lua.h */ #define lopcodes_c #define LUA_CORE /*#include "lprefix.h"*/ /*#include "lopcodes.h"*/ /* ORDER OP */ LUAI_DDEF const lu_byte luaP_opmodes[NUM_OPCODES] = { /* MM OT IT T A mode opcode */ opmode(0, 0, 0, 0, 1, iABC) /* OP_MOVE */ ,opmode(0, 0, 0, 0, 1, iAsBx) /* OP_LOADI */ ,opmode(0, 0, 0, 0, 1, iAsBx) /* OP_LOADF */ ,opmode(0, 0, 0, 0, 1, iABx) /* OP_LOADK */ ,opmode(0, 0, 0, 0, 1, iABx) /* OP_LOADKX */ ,opmode(0, 0, 0, 0, 1, iABC) /* OP_LOADFALSE */ ,opmode(0, 0, 0, 0, 1, iABC) /* OP_LFALSESKIP */ ,opmode(0, 0, 0, 0, 1, iABC) /* OP_LOADTRUE */ ,opmode(0, 0, 0, 0, 1, iABC) /* OP_LOADNIL */ ,opmode(0, 0, 0, 0, 1, iABC) /* OP_GETUPVAL */ ,opmode(0, 0, 0, 0, 0, iABC) /* OP_SETUPVAL */ ,opmode(0, 0, 0, 0, 1, iABC) /* OP_GETTABUP */ ,opmode(0, 0, 0, 0, 1, iABC) /* OP_GETTABLE */ ,opmode(0, 0, 0, 0, 1, iABC) /* OP_GETI */ ,opmode(0, 0, 0, 0, 1, iABC) /* OP_GETFIELD */ ,opmode(0, 0, 0, 0, 0, iABC) /* OP_SETTABUP */ ,opmode(0, 0, 0, 0, 0, iABC) /* OP_SETTABLE */ ,opmode(0, 0, 0, 0, 0, iABC) /* OP_SETI */ ,opmode(0, 0, 0, 0, 0, iABC) /* OP_SETFIELD */ ,opmode(0, 0, 0, 0, 1, iABC) /* OP_NEWTABLE */ ,opmode(0, 0, 0, 0, 1, iABC) /* OP_SELF */ ,opmode(0, 0, 0, 0, 1, iABC) /* OP_ADDI */ ,opmode(0, 0, 0, 0, 1, iABC) /* OP_ADDK */ ,opmode(0, 0, 0, 0, 1, iABC) /* OP_SUBK */ ,opmode(0, 0, 0, 0, 1, iABC) /* OP_MULK */ ,opmode(0, 0, 0, 0, 1, iABC) /* OP_MODK */ ,opmode(0, 0, 0, 0, 1, iABC) /* OP_POWK */ ,opmode(0, 0, 0, 0, 1, iABC) /* OP_DIVK */ ,opmode(0, 0, 0, 0, 1, iABC) /* OP_IDIVK */ ,opmode(0, 0, 0, 0, 1, iABC) /* OP_BANDK */ ,opmode(0, 0, 0, 0, 1, iABC) /* OP_BORK */ ,opmode(0, 0, 0, 0, 1, iABC) /* OP_BXORK */ ,opmode(0, 0, 0, 0, 1, iABC) /* OP_SHRI */ ,opmode(0, 0, 0, 0, 1, iABC) /* OP_SHLI */ ,opmode(0, 0, 0, 0, 1, iABC) /* OP_ADD */ ,opmode(0, 0, 0, 0, 1, iABC) /* OP_SUB */ ,opmode(0, 0, 0, 0, 1, iABC) /* OP_MUL */ ,opmode(0, 0, 0, 0, 1, iABC) /* OP_MOD */ ,opmode(0, 0, 0, 0, 1, iABC) /* OP_POW */ ,opmode(0, 0, 0, 0, 1, iABC) /* OP_DIV */ ,opmode(0, 0, 0, 0, 1, iABC) /* OP_IDIV */ ,opmode(0, 0, 0, 0, 1, iABC) /* OP_BAND */ ,opmode(0, 0, 0, 0, 1, iABC) /* OP_BOR */ ,opmode(0, 0, 0, 0, 1, iABC) /* OP_BXOR */ ,opmode(0, 0, 0, 0, 1, iABC) /* OP_SHL */ ,opmode(0, 0, 0, 0, 1, iABC) /* OP_SHR */ ,opmode(1, 0, 0, 0, 0, iABC) /* OP_MMBIN */ ,opmode(1, 0, 0, 0, 0, iABC) /* OP_MMBINI*/ ,opmode(1, 0, 0, 0, 0, iABC) /* OP_MMBINK*/ ,opmode(0, 0, 0, 0, 1, iABC) /* OP_UNM */ ,opmode(0, 0, 0, 0, 1, iABC) /* OP_BNOT */ ,opmode(0, 0, 0, 0, 1, iABC) /* OP_NOT */ ,opmode(0, 0, 0, 0, 1, iABC) /* OP_LEN */ ,opmode(0, 0, 0, 0, 1, iABC) /* OP_CONCAT */ ,opmode(0, 0, 0, 0, 0, iABC) /* OP_CLOSE */ ,opmode(0, 0, 0, 0, 0, iABC) /* OP_TBC */ ,opmode(0, 0, 0, 0, 0, isJ) /* OP_JMP */ ,opmode(0, 0, 0, 1, 0, iABC) /* OP_EQ */ ,opmode(0, 0, 0, 1, 0, iABC) /* OP_LT */ ,opmode(0, 0, 0, 1, 0, iABC) /* OP_LE */ ,opmode(0, 0, 0, 1, 0, iABC) /* OP_EQK */ ,opmode(0, 0, 0, 1, 0, iABC) /* OP_EQI */ ,opmode(0, 0, 0, 1, 0, iABC) /* OP_LTI */ ,opmode(0, 0, 0, 1, 0, iABC) /* OP_LEI */ ,opmode(0, 0, 0, 1, 0, iABC) /* OP_GTI */ ,opmode(0, 0, 0, 1, 0, iABC) /* OP_GEI */ ,opmode(0, 0, 0, 1, 0, iABC) /* OP_TEST */ ,opmode(0, 0, 0, 1, 1, iABC) /* OP_TESTSET */ ,opmode(0, 1, 1, 0, 1, iABC) /* OP_CALL */ ,opmode(0, 1, 1, 0, 1, iABC) /* OP_TAILCALL */ ,opmode(0, 0, 1, 0, 0, iABC) /* OP_RETURN */ ,opmode(0, 0, 0, 0, 0, iABC) /* OP_RETURN0 */ ,opmode(0, 0, 0, 0, 0, iABC) /* OP_RETURN1 */ ,opmode(0, 0, 0, 0, 1, iABx) /* OP_FORLOOP */ ,opmode(0, 0, 0, 0, 1, iABx) /* OP_FORPREP */ ,opmode(0, 0, 0, 0, 0, iABx) /* OP_TFORPREP */ ,opmode(0, 0, 0, 0, 0, iABC) /* OP_TFORCALL */ ,opmode(0, 0, 0, 0, 1, iABx) /* OP_TFORLOOP */ ,opmode(0, 0, 1, 0, 0, iABC) /* OP_SETLIST */ ,opmode(0, 0, 0, 0, 1, iABx) /* OP_CLOSURE */ ,opmode(0, 1, 0, 0, 1, iABC) /* OP_VARARG */ ,opmode(0, 0, 1, 0, 1, iABC) /* OP_VARARGPREP */ ,opmode(0, 0, 0, 0, 0, iAx) /* OP_EXTRAARG */ }; /* ** $Id: lmem.c $ ** Interface to Memory Manager ** See Copyright Notice in lua.h */ #define lmem_c #define LUA_CORE /*#include "lprefix.h"*/ #include /*#include "lua.h"*/ /*#include "ldebug.h"*/ /*#include "ldo.h"*/ /*#include "lgc.h"*/ /*#include "lmem.h"*/ /*#include "lobject.h"*/ /*#include "lstate.h"*/ #if defined(EMERGENCYGCTESTS) /* ** First allocation will fail whenever not building initial state. ** (This fail will trigger 'tryagain' and a full GC cycle at every ** allocation.) */ static void *firsttry (global_State *g, void *block, size_t os, size_t ns) { if (completestate(g) && ns > 0) /* frees never fail */ return NULL; /* fail */ else /* normal allocation */ return (*g->frealloc)(g->ud, block, os, ns); } #else #define firsttry(g,block,os,ns) ((*g->frealloc)(g->ud, block, os, ns)) #endif /* ** About the realloc function: ** void *frealloc (void *ud, void *ptr, size_t osize, size_t nsize); ** ('osize' is the old size, 'nsize' is the new size) ** ** - frealloc(ud, p, x, 0) frees the block 'p' and returns NULL. ** Particularly, frealloc(ud, NULL, 0, 0) does nothing, ** which is equivalent to free(NULL) in ISO C. ** ** - frealloc(ud, NULL, x, s) creates a new block of size 's' ** (no matter 'x'). Returns NULL if it cannot create the new block. ** ** - otherwise, frealloc(ud, b, x, y) reallocates the block 'b' from ** size 'x' to size 'y'. Returns NULL if it cannot reallocate the ** block to the new size. */ /* ** {================================================================== ** Functions to allocate/deallocate arrays for the Parser ** =================================================================== */ /* ** Minimum size for arrays during parsing, to avoid overhead of ** reallocating to size 1, then 2, and then 4. All these arrays ** will be reallocated to exact sizes or erased when parsing ends. */ #define MINSIZEARRAY 4 void *luaM_growaux_ (lua_State *L, void *block, int nelems, int *psize, int size_elems, int limit, const char *what) { void *newblock; int size = *psize; if (nelems + 1 <= size) /* does one extra element still fit? */ return block; /* nothing to be done */ if (size >= limit / 2) { /* cannot double it? */ if (l_unlikely(size >= limit)) /* cannot grow even a little? */ luaG_runerror(L, "too many %s (limit is %d)", what, limit); size = limit; /* still have at least one free place */ } else { size *= 2; if (size < MINSIZEARRAY) size = MINSIZEARRAY; /* minimum size */ } lua_assert(nelems + 1 <= size && size <= limit); /* 'limit' ensures that multiplication will not overflow */ newblock = luaM_saferealloc_(L, block, cast_sizet(*psize) * size_elems, cast_sizet(size) * size_elems); *psize = size; /* update only when everything else is OK */ return newblock; } /* ** In prototypes, the size of the array is also its number of ** elements (to save memory). So, if it cannot shrink an array ** to its number of elements, the only option is to raise an ** error. */ void *luaM_shrinkvector_ (lua_State *L, void *block, int *size, int final_n, int size_elem) { void *newblock; size_t oldsize = cast_sizet((*size) * size_elem); size_t newsize = cast_sizet(final_n * size_elem); lua_assert(newsize <= oldsize); newblock = luaM_saferealloc_(L, block, oldsize, newsize); *size = final_n; return newblock; } /* }================================================================== */ l_noret luaM_toobig (lua_State *L) { luaG_runerror(L, "memory allocation error: block too big"); } /* ** Free memory */ void luaM_free_ (lua_State *L, void *block, size_t osize) { global_State *g = G(L); lua_assert((osize == 0) == (block == NULL)); (*g->frealloc)(g->ud, block, osize, 0); g->GCdebt -= osize; } /* ** In case of allocation fail, this function will do an emergency ** collection to free some memory and then try the allocation again. ** The GC should not be called while state is not fully built, as the ** collector is not yet fully initialized. Also, it should not be called ** when 'gcstopem' is true, because then the interpreter is in the ** middle of a collection step. */ static void *tryagain (lua_State *L, void *block, size_t osize, size_t nsize) { global_State *g = G(L); if (completestate(g) && !g->gcstopem) { luaC_fullgc(L, 1); /* try to free some memory... */ return (*g->frealloc)(g->ud, block, osize, nsize); /* try again */ } else return NULL; /* cannot free any memory without a full state */ } /* ** Generic allocation routine. */ void *luaM_realloc_ (lua_State *L, void *block, size_t osize, size_t nsize) { void *newblock; global_State *g = G(L); lua_assert((osize == 0) == (block == NULL)); newblock = firsttry(g, block, osize, nsize); if (l_unlikely(newblock == NULL && nsize > 0)) { newblock = tryagain(L, block, osize, nsize); if (newblock == NULL) /* still no memory? */ return NULL; /* do not update 'GCdebt' */ } lua_assert((nsize == 0) == (newblock == NULL)); g->GCdebt = (g->GCdebt + nsize) - osize; return newblock; } void *luaM_saferealloc_ (lua_State *L, void *block, size_t osize, size_t nsize) { void *newblock = luaM_realloc_(L, block, osize, nsize); if (l_unlikely(newblock == NULL && nsize > 0)) /* allocation failed? */ luaM_error(L); return newblock; } void *luaM_malloc_ (lua_State *L, size_t size, int tag) { if (size == 0) return NULL; /* that's all */ else { global_State *g = G(L); void *newblock = firsttry(g, NULL, tag, size); if (l_unlikely(newblock == NULL)) { newblock = tryagain(L, NULL, tag, size); if (newblock == NULL) luaM_error(L); } g->GCdebt += size; return newblock; } } /* ** $Id: lundump.c $ ** load precompiled Lua chunks ** See Copyright Notice in lua.h */ #define lundump_c #define LUA_CORE /*#include "lprefix.h"*/ #include #include /*#include "lua.h"*/ /*#include "ldebug.h"*/ /*#include "ldo.h"*/ /*#include "lfunc.h"*/ /*#include "lmem.h"*/ /*#include "lobject.h"*/ /*#include "lstring.h"*/ /*#include "lundump.h"*/ /*#include "lzio.h"*/ #if !defined(luai_verifycode) #define luai_verifycode(L,f) /* empty */ #endif typedef struct { lua_State *L; ZIO *Z; const char *name; } LoadState; static l_noret error (LoadState *S, const char *why) { luaO_pushfstring(S->L, "%s: bad binary format (%s)", S->name, why); luaD_throw(S->L, LUA_ERRSYNTAX); } /* ** All high-level loads go through loadVector; you can change it to ** adapt to the endianness of the input */ #define loadVector(S,b,n) loadBlock(S,b,(n)*sizeof((b)[0])) static void loadBlock (LoadState *S, void *b, size_t size) { if (luaZ_read(S->Z, b, size) != 0) error(S, "truncated chunk"); } #define loadVar(S,x) loadVector(S,&x,1) static lu_byte loadByte (LoadState *S) { int b = zgetc(S->Z); if (b == EOZ) error(S, "truncated chunk"); return cast_byte(b); } static size_t loadUnsigned (LoadState *S, size_t limit) { size_t x = 0; int b; limit >>= 7; do { b = loadByte(S); if (x >= limit) error(S, "integer overflow"); x = (x << 7) | (b & 0x7f); } while ((b & 0x80) == 0); return x; } static size_t loadSize (LoadState *S) { return loadUnsigned(S, ~(size_t)0); } static int loadInt (LoadState *S) { return cast_int(loadUnsigned(S, INT_MAX)); } static lua_Number loadNumber (LoadState *S) { lua_Number x; loadVar(S, x); return x; } static lua_Integer loadInteger (LoadState *S) { lua_Integer x; loadVar(S, x); return x; } /* ** Load a nullable string into prototype 'p'. */ static TString *loadStringN (LoadState *S, Proto *p) { lua_State *L = S->L; TString *ts; size_t size = loadSize(S); if (size == 0) /* no string? */ return NULL; else if (--size <= LUAI_MAXSHORTLEN) { /* short string? */ char buff[LUAI_MAXSHORTLEN]; loadVector(S, buff, size); /* load string into buffer */ ts = luaS_newlstr(L, buff, size); /* create string */ } else { /* long string */ ts = luaS_createlngstrobj(L, size); /* create string */ setsvalue2s(L, L->top, ts); /* anchor it ('loadVector' can GC) */ luaD_inctop(L); loadVector(S, getstr(ts), size); /* load directly in final place */ L->top--; /* pop string */ } luaC_objbarrier(L, p, ts); return ts; } /* ** Load a non-nullable string into prototype 'p'. */ static TString *loadString (LoadState *S, Proto *p) { TString *st = loadStringN(S, p); if (st == NULL) error(S, "bad format for constant string"); return st; } static void loadCode (LoadState *S, Proto *f) { int n = loadInt(S); f->code = luaM_newvectorchecked(S->L, n, Instruction); f->sizecode = n; loadVector(S, f->code, n); } static void loadFunction(LoadState *S, Proto *f, TString *psource); static void loadConstants (LoadState *S, Proto *f) { int i; int n = loadInt(S); f->k = luaM_newvectorchecked(S->L, n, TValue); f->sizek = n; for (i = 0; i < n; i++) setnilvalue(&f->k[i]); for (i = 0; i < n; i++) { TValue *o = &f->k[i]; int t = loadByte(S); switch (t) { case LUA_VNIL: setnilvalue(o); break; case LUA_VFALSE: setbfvalue(o); break; case LUA_VTRUE: setbtvalue(o); break; case LUA_VNUMFLT: setfltvalue(o, loadNumber(S)); break; case LUA_VNUMINT: setivalue(o, loadInteger(S)); break; case LUA_VSHRSTR: case LUA_VLNGSTR: setsvalue2n(S->L, o, loadString(S, f)); break; default: lua_assert(0); } } } static void loadProtos (LoadState *S, Proto *f) { int i; int n = loadInt(S); f->p = luaM_newvectorchecked(S->L, n, Proto *); f->sizep = n; for (i = 0; i < n; i++) f->p[i] = NULL; for (i = 0; i < n; i++) { f->p[i] = luaF_newproto(S->L); luaC_objbarrier(S->L, f, f->p[i]); loadFunction(S, f->p[i], f->source); } } /* ** Load the upvalues for a function. The names must be filled first, ** because the filling of the other fields can raise read errors and ** the creation of the error message can call an emergency collection; ** in that case all prototypes must be consistent for the GC. */ static void loadUpvalues (LoadState *S, Proto *f) { int i, n; n = loadInt(S); f->upvalues = luaM_newvectorchecked(S->L, n, Upvaldesc); f->sizeupvalues = n; for (i = 0; i < n; i++) /* make array valid for GC */ f->upvalues[i].name = NULL; for (i = 0; i < n; i++) { /* following calls can raise errors */ f->upvalues[i].instack = loadByte(S); f->upvalues[i].idx = loadByte(S); f->upvalues[i].kind = loadByte(S); } } static void loadDebug (LoadState *S, Proto *f) { int i, n; n = loadInt(S); f->lineinfo = luaM_newvectorchecked(S->L, n, ls_byte); f->sizelineinfo = n; loadVector(S, f->lineinfo, n); n = loadInt(S); f->abslineinfo = luaM_newvectorchecked(S->L, n, AbsLineInfo); f->sizeabslineinfo = n; for (i = 0; i < n; i++) { f->abslineinfo[i].pc = loadInt(S); f->abslineinfo[i].line = loadInt(S); } n = loadInt(S); f->locvars = luaM_newvectorchecked(S->L, n, LocVar); f->sizelocvars = n; for (i = 0; i < n; i++) f->locvars[i].varname = NULL; for (i = 0; i < n; i++) { f->locvars[i].varname = loadStringN(S, f); f->locvars[i].startpc = loadInt(S); f->locvars[i].endpc = loadInt(S); } n = loadInt(S); for (i = 0; i < n; i++) f->upvalues[i].name = loadStringN(S, f); } static void loadFunction (LoadState *S, Proto *f, TString *psource) { f->source = loadStringN(S, f); if (f->source == NULL) /* no source in dump? */ f->source = psource; /* reuse parent's source */ f->linedefined = loadInt(S); f->lastlinedefined = loadInt(S); f->numparams = loadByte(S); f->is_vararg = loadByte(S); f->maxstacksize = loadByte(S); loadCode(S, f); loadConstants(S, f); loadUpvalues(S, f); loadProtos(S, f); loadDebug(S, f); } static void checkliteral (LoadState *S, const char *s, const char *msg) { char buff[sizeof(LUA_SIGNATURE) + sizeof(LUAC_DATA)]; /* larger than both */ size_t len = strlen(s); loadVector(S, buff, len); if (memcmp(s, buff, len) != 0) error(S, msg); } static void fchecksize (LoadState *S, size_t size, const char *tname) { if (loadByte(S) != size) error(S, luaO_pushfstring(S->L, "%s size mismatch", tname)); } #define checksize(S,t) fchecksize(S,sizeof(t),#t) static void checkHeader (LoadState *S) { /* skip 1st char (already read and checked) */ checkliteral(S, &LUA_SIGNATURE[1], "not a binary chunk"); if (loadByte(S) != LUAC_VERSION) error(S, "version mismatch"); if (loadByte(S) != LUAC_FORMAT) error(S, "format mismatch"); checkliteral(S, LUAC_DATA, "corrupted chunk"); checksize(S, Instruction); checksize(S, lua_Integer); checksize(S, lua_Number); if (loadInteger(S) != LUAC_INT) error(S, "integer format mismatch"); if (loadNumber(S) != LUAC_NUM) error(S, "float format mismatch"); } /* ** Load precompiled chunk. */ LClosure *luaU_undump(lua_State *L, ZIO *Z, const char *name) { LoadState S; LClosure *cl; if (*name == '@' || *name == '=') S.name = name + 1; else if (*name == LUA_SIGNATURE[0]) S.name = "binary string"; else S.name = name; S.L = L; S.Z = Z; checkHeader(&S); cl = luaF_newLclosure(L, loadByte(&S)); setclLvalue2s(L, L->top, cl); luaD_inctop(L); cl->p = luaF_newproto(L); luaC_objbarrier(L, cl, cl->p); loadFunction(&S, cl->p, NULL); lua_assert(cl->nupvalues == cl->p->sizeupvalues); luai_verifycode(L, cl->p); return cl; } /* ** $Id: ldump.c $ ** save precompiled Lua chunks ** See Copyright Notice in lua.h */ #define ldump_c #define LUA_CORE /*#include "lprefix.h"*/ #include /*#include "lua.h"*/ /*#include "lobject.h"*/ /*#include "lstate.h"*/ /*#include "lundump.h"*/ typedef struct { lua_State *L; lua_Writer writer; void *data; int strip; int status; } DumpState; /* ** All high-level dumps go through dumpVector; you can change it to ** change the endianness of the result */ #define dumpVector(D,v,n) dumpBlock(D,v,(n)*sizeof((v)[0])) #define dumpLiteral(D, s) dumpBlock(D,s,sizeof(s) - sizeof(char)) static void dumpBlock (DumpState *D, const void *b, size_t size) { if (D->status == 0 && size > 0) { lua_unlock(D->L); D->status = (*D->writer)(D->L, b, size, D->data); lua_lock(D->L); } } #define dumpVar(D,x) dumpVector(D,&x,1) static void dumpByte (DumpState *D, int y) { lu_byte x = (lu_byte)y; dumpVar(D, x); } /* dumpInt Buff Size */ #define DIBS ((sizeof(size_t) * 8 / 7) + 1) static void dumpSize (DumpState *D, size_t x) { lu_byte buff[DIBS]; int n = 0; do { buff[DIBS - (++n)] = x & 0x7f; /* fill buffer in reverse order */ x >>= 7; } while (x != 0); buff[DIBS - 1] |= 0x80; /* mark last byte */ dumpVector(D, buff + DIBS - n, n); } static void dumpInt (DumpState *D, int x) { dumpSize(D, x); } static void dumpNumber (DumpState *D, lua_Number x) { dumpVar(D, x); } static void dumpInteger (DumpState *D, lua_Integer x) { dumpVar(D, x); } static void dumpString (DumpState *D, const TString *s) { if (s == NULL) dumpSize(D, 0); else { size_t size = tsslen(s); const char *str = getstr(s); dumpSize(D, size + 1); dumpVector(D, str, size); } } static void dumpCode (DumpState *D, const Proto *f) { dumpInt(D, f->sizecode); dumpVector(D, f->code, f->sizecode); } static void dumpFunction(DumpState *D, const Proto *f, TString *psource); static void dumpConstants (DumpState *D, const Proto *f) { int i; int n = f->sizek; dumpInt(D, n); for (i = 0; i < n; i++) { const TValue *o = &f->k[i]; int tt = ttypetag(o); dumpByte(D, tt); switch (tt) { case LUA_VNUMFLT: dumpNumber(D, fltvalue(o)); break; case LUA_VNUMINT: dumpInteger(D, ivalue(o)); break; case LUA_VSHRSTR: case LUA_VLNGSTR: dumpString(D, tsvalue(o)); break; default: lua_assert(tt == LUA_VNIL || tt == LUA_VFALSE || tt == LUA_VTRUE); } } } static void dumpProtos (DumpState *D, const Proto *f) { int i; int n = f->sizep; dumpInt(D, n); for (i = 0; i < n; i++) dumpFunction(D, f->p[i], f->source); } static void dumpUpvalues (DumpState *D, const Proto *f) { int i, n = f->sizeupvalues; dumpInt(D, n); for (i = 0; i < n; i++) { dumpByte(D, f->upvalues[i].instack); dumpByte(D, f->upvalues[i].idx); dumpByte(D, f->upvalues[i].kind); } } static void dumpDebug (DumpState *D, const Proto *f) { int i, n; n = (D->strip) ? 0 : f->sizelineinfo; dumpInt(D, n); dumpVector(D, f->lineinfo, n); n = (D->strip) ? 0 : f->sizeabslineinfo; dumpInt(D, n); for (i = 0; i < n; i++) { dumpInt(D, f->abslineinfo[i].pc); dumpInt(D, f->abslineinfo[i].line); } n = (D->strip) ? 0 : f->sizelocvars; dumpInt(D, n); for (i = 0; i < n; i++) { dumpString(D, f->locvars[i].varname); dumpInt(D, f->locvars[i].startpc); dumpInt(D, f->locvars[i].endpc); } n = (D->strip) ? 0 : f->sizeupvalues; dumpInt(D, n); for (i = 0; i < n; i++) dumpString(D, f->upvalues[i].name); } static void dumpFunction (DumpState *D, const Proto *f, TString *psource) { if (D->strip || f->source == psource) dumpString(D, NULL); /* no debug info or same source as its parent */ else dumpString(D, f->source); dumpInt(D, f->linedefined); dumpInt(D, f->lastlinedefined); dumpByte(D, f->numparams); dumpByte(D, f->is_vararg); dumpByte(D, f->maxstacksize); dumpCode(D, f); dumpConstants(D, f); dumpUpvalues(D, f); dumpProtos(D, f); dumpDebug(D, f); } static void dumpHeader (DumpState *D) { dumpLiteral(D, LUA_SIGNATURE); dumpByte(D, LUAC_VERSION); dumpByte(D, LUAC_FORMAT); dumpLiteral(D, LUAC_DATA); dumpByte(D, sizeof(Instruction)); dumpByte(D, sizeof(lua_Integer)); dumpByte(D, sizeof(lua_Number)); dumpInteger(D, LUAC_INT); dumpNumber(D, LUAC_NUM); } /* ** dump Lua function as precompiled chunk */ int luaU_dump(lua_State *L, const Proto *f, lua_Writer w, void *data, int strip) { DumpState D; D.L = L; D.writer = w; D.data = data; D.strip = strip; D.status = 0; dumpHeader(&D); dumpByte(&D, f->sizeupvalues); dumpFunction(&D, f, NULL); return D.status; } /* ** $Id: lstate.c $ ** Global State ** See Copyright Notice in lua.h */ #define lstate_c #define LUA_CORE /*#include "lprefix.h"*/ #include #include /*#include "lua.h"*/ /*#include "lapi.h"*/ /*#include "ldebug.h"*/ /*#include "ldo.h"*/ /*#include "lfunc.h"*/ /*#include "lgc.h"*/ /*#include "llex.h"*/ /*#include "lmem.h"*/ /*#include "lstate.h"*/ /*#include "lstring.h"*/ /*#include "ltable.h"*/ /*#include "ltm.h"*/ /* ** thread state + extra space */ typedef struct LX { lu_byte extra_[LUA_EXTRASPACE]; lua_State l; } LX; /* ** Main thread combines a thread state and the global state */ typedef struct LG { LX l; global_State g; } LG; #define fromstate(L) (cast(LX *, cast(lu_byte *, (L)) - offsetof(LX, l))) /* ** A macro to create a "random" seed when a state is created; ** the seed is used to randomize string hashes. */ #if !defined(luai_makeseed) #include /* ** Compute an initial seed with some level of randomness. ** Rely on Address Space Layout Randomization (if present) and ** current time. */ #define addbuff(b,p,e) \ { size_t t = cast_sizet(e); \ memcpy(b + p, &t, sizeof(t)); p += sizeof(t); } static unsigned int luai_makeseed (lua_State *L) { char buff[3 * sizeof(size_t)]; unsigned int h = cast_uint(time(NULL)); int p = 0; addbuff(buff, p, L); /* heap variable */ addbuff(buff, p, &h); /* local variable */ addbuff(buff, p, &lua_newstate); /* public function */ lua_assert(p == sizeof(buff)); return luaS_hash(buff, p, h); } #endif /* ** set GCdebt to a new value keeping the value (totalbytes + GCdebt) ** invariant (and avoiding underflows in 'totalbytes') */ void luaE_setdebt (global_State *g, l_mem debt) { l_mem tb = gettotalbytes(g); lua_assert(tb > 0); if (debt < tb - MAX_LMEM) debt = tb - MAX_LMEM; /* will make 'totalbytes == MAX_LMEM' */ g->totalbytes = tb - debt; g->GCdebt = debt; } LUA_API int lua_setcstacklimit (lua_State *L, unsigned int limit) { UNUSED(L); UNUSED(limit); return LUAI_MAXCCALLS; /* warning?? */ } CallInfo *luaE_extendCI (lua_State *L) { CallInfo *ci; lua_assert(L->ci->next == NULL); ci = luaM_new(L, CallInfo); lua_assert(L->ci->next == NULL); L->ci->next = ci; ci->previous = L->ci; ci->next = NULL; ci->u.l.trap = 0; L->nci++; return ci; } /* ** free all CallInfo structures not in use by a thread */ void luaE_freeCI (lua_State *L) { CallInfo *ci = L->ci; CallInfo *next = ci->next; ci->next = NULL; while ((ci = next) != NULL) { next = ci->next; luaM_free(L, ci); L->nci--; } } /* ** free half of the CallInfo structures not in use by a thread, ** keeping the first one. */ void luaE_shrinkCI (lua_State *L) { CallInfo *ci = L->ci->next; /* first free CallInfo */ CallInfo *next; if (ci == NULL) return; /* no extra elements */ while ((next = ci->next) != NULL) { /* two extra elements? */ CallInfo *next2 = next->next; /* next's next */ ci->next = next2; /* remove next from the list */ L->nci--; luaM_free(L, next); /* free next */ if (next2 == NULL) break; /* no more elements */ else { next2->previous = ci; ci = next2; /* continue */ } } } /* ** Called when 'getCcalls(L)' larger or equal to LUAI_MAXCCALLS. ** If equal, raises an overflow error. If value is larger than ** LUAI_MAXCCALLS (which means it is handling an overflow) but ** not much larger, does not report an error (to allow overflow ** handling to work). */ void luaE_checkcstack (lua_State *L) { if (getCcalls(L) == LUAI_MAXCCALLS) luaG_runerror(L, "C stack overflow"); else if (getCcalls(L) >= (LUAI_MAXCCALLS / 10 * 11)) luaD_throw(L, LUA_ERRERR); /* error while handling stack error */ } LUAI_FUNC void luaE_incCstack (lua_State *L) { L->nCcalls++; if (l_unlikely(getCcalls(L) >= LUAI_MAXCCALLS)) luaE_checkcstack(L); } static void stack_init (lua_State *L1, lua_State *L) { int i; CallInfo *ci; /* initialize stack array */ L1->stack = luaM_newvector(L, BASIC_STACK_SIZE + EXTRA_STACK, StackValue); L1->tbclist = L1->stack; for (i = 0; i < BASIC_STACK_SIZE + EXTRA_STACK; i++) setnilvalue(s2v(L1->stack + i)); /* erase new stack */ L1->top = L1->stack; L1->stack_last = L1->stack + BASIC_STACK_SIZE; /* initialize first ci */ ci = &L1->base_ci; ci->next = ci->previous = NULL; ci->callstatus = CIST_C; ci->func = L1->top; ci->u.c.k = NULL; ci->nresults = 0; setnilvalue(s2v(L1->top)); /* 'function' entry for this 'ci' */ L1->top++; ci->top = L1->top + LUA_MINSTACK; L1->ci = ci; } static void freestack (lua_State *L) { if (L->stack == NULL) return; /* stack not completely built yet */ L->ci = &L->base_ci; /* free the entire 'ci' list */ luaE_freeCI(L); lua_assert(L->nci == 0); luaM_freearray(L, L->stack, stacksize(L) + EXTRA_STACK); /* free stack */ } /* ** Create registry table and its predefined values */ static void init_registry (lua_State *L, global_State *g) { /* create registry */ Table *registry = luaH_new(L); sethvalue(L, &g->l_registry, registry); luaH_resize(L, registry, LUA_RIDX_LAST, 0); /* registry[LUA_RIDX_MAINTHREAD] = L */ setthvalue(L, ®istry->array[LUA_RIDX_MAINTHREAD - 1], L); /* registry[LUA_RIDX_GLOBALS] = new table (table of globals) */ sethvalue(L, ®istry->array[LUA_RIDX_GLOBALS - 1], luaH_new(L)); } /* ** open parts of the state that may cause memory-allocation errors. */ static void f_luaopen (lua_State *L, void *ud) { global_State *g = G(L); UNUSED(ud); stack_init(L, L); /* init stack */ init_registry(L, g); luaS_init(L); luaT_init(L); luaX_init(L); g->gcstp = 0; /* allow gc */ setnilvalue(&g->nilvalue); /* now state is complete */ luai_userstateopen(L); } /* ** preinitialize a thread with consistent values without allocating ** any memory (to avoid errors) */ static void preinit_thread (lua_State *L, global_State *g) { G(L) = g; L->stack = NULL; L->ci = NULL; L->nci = 0; L->twups = L; /* thread has no upvalues */ L->nCcalls = 0; L->errorJmp = NULL; L->hook = NULL; L->hookmask = 0; L->basehookcount = 0; L->allowhook = 1; resethookcount(L); L->openupval = NULL; L->status = LUA_OK; L->errfunc = 0; L->oldpc = 0; } static void close_state (lua_State *L) { global_State *g = G(L); if (!completestate(g)) /* closing a partially built state? */ luaC_freeallobjects(L); /* just collect its objects */ else { /* closing a fully built state */ L->ci = &L->base_ci; /* unwind CallInfo list */ luaD_closeprotected(L, 1, LUA_OK); /* close all upvalues */ luaC_freeallobjects(L); /* collect all objects */ luai_userstateclose(L); } luaM_freearray(L, G(L)->strt.hash, G(L)->strt.size); freestack(L); lua_assert(gettotalbytes(g) == sizeof(LG)); (*g->frealloc)(g->ud, fromstate(L), sizeof(LG), 0); /* free main block */ } LUA_API lua_State *lua_newthread (lua_State *L) { global_State *g; lua_State *L1; lua_lock(L); g = G(L); luaC_checkGC(L); /* create new thread */ L1 = &cast(LX *, luaM_newobject(L, LUA_TTHREAD, sizeof(LX)))->l; L1->marked = luaC_white(g); L1->tt = LUA_VTHREAD; /* link it on list 'allgc' */ L1->next = g->allgc; g->allgc = obj2gco(L1); /* anchor it on L stack */ setthvalue2s(L, L->top, L1); api_incr_top(L); preinit_thread(L1, g); L1->hookmask = L->hookmask; L1->basehookcount = L->basehookcount; L1->hook = L->hook; resethookcount(L1); /* initialize L1 extra space */ memcpy(lua_getextraspace(L1), lua_getextraspace(g->mainthread), LUA_EXTRASPACE); luai_userstatethread(L, L1); stack_init(L1, L); /* init stack */ lua_unlock(L); return L1; } void luaE_freethread (lua_State *L, lua_State *L1) { LX *l = fromstate(L1); luaF_closeupval(L1, L1->stack); /* close all upvalues */ lua_assert(L1->openupval == NULL); luai_userstatefree(L, L1); freestack(L1); luaM_free(L, l); } int luaE_resetthread (lua_State *L, int status) { CallInfo *ci = L->ci = &L->base_ci; /* unwind CallInfo list */ setnilvalue(s2v(L->stack)); /* 'function' entry for basic 'ci' */ ci->func = L->stack; ci->callstatus = CIST_C; if (status == LUA_YIELD) status = LUA_OK; L->status = LUA_OK; /* so it can run __close metamethods */ status = luaD_closeprotected(L, 1, status); if (status != LUA_OK) /* errors? */ luaD_seterrorobj(L, status, L->stack + 1); else L->top = L->stack + 1; ci->top = L->top + LUA_MINSTACK; luaD_reallocstack(L, cast_int(ci->top - L->stack), 0); return status; } LUA_API int lua_resetthread (lua_State *L) { int status; lua_lock(L); status = luaE_resetthread(L, L->status); lua_unlock(L); return status; } LUA_API lua_State *lua_newstate (lua_Alloc f, void *ud) { int i; lua_State *L; global_State *g; LG *l = cast(LG *, (*f)(ud, NULL, LUA_TTHREAD, sizeof(LG))); if (l == NULL) return NULL; L = &l->l.l; g = &l->g; L->tt = LUA_VTHREAD; g->currentwhite = bitmask(WHITE0BIT); L->marked = luaC_white(g); preinit_thread(L, g); g->allgc = obj2gco(L); /* by now, only object is the main thread */ L->next = NULL; incnny(L); /* main thread is always non yieldable */ g->frealloc = f; g->ud = ud; g->warnf = NULL; g->ud_warn = NULL; g->mainthread = L; g->seed = luai_makeseed(L); g->gcstp = GCSTPGC; /* no GC while building state */ g->strt.size = g->strt.nuse = 0; g->strt.hash = NULL; setnilvalue(&g->l_registry); g->panic = NULL; g->gcstate = GCSpause; g->gckind = KGC_INC; g->gcstopem = 0; g->gcemergency = 0; g->finobj = g->tobefnz = g->fixedgc = NULL; g->firstold1 = g->survival = g->old1 = g->reallyold = NULL; g->finobjsur = g->finobjold1 = g->finobjrold = NULL; g->sweepgc = NULL; g->gray = g->grayagain = NULL; g->weak = g->ephemeron = g->allweak = NULL; g->twups = NULL; g->totalbytes = sizeof(LG); g->GCdebt = 0; g->lastatomic = 0; setivalue(&g->nilvalue, 0); /* to signal that state is not yet built */ setgcparam(g->gcpause, LUAI_GCPAUSE); setgcparam(g->gcstepmul, LUAI_GCMUL); g->gcstepsize = LUAI_GCSTEPSIZE; setgcparam(g->genmajormul, LUAI_GENMAJORMUL); g->genminormul = LUAI_GENMINORMUL; for (i=0; i < LUA_NUMTAGS; i++) g->mt[i] = NULL; if (luaD_rawrunprotected(L, f_luaopen, NULL) != LUA_OK) { /* memory allocation error: free partial state */ close_state(L); L = NULL; } return L; } LUA_API void lua_close (lua_State *L) { lua_lock(L); L = G(L)->mainthread; /* only the main thread can be closed */ close_state(L); } void luaE_warning (lua_State *L, const char *msg, int tocont) { lua_WarnFunction wf = G(L)->warnf; if (wf != NULL) wf(G(L)->ud_warn, msg, tocont); } /* ** Generate a warning from an error message */ void luaE_warnerror (lua_State *L, const char *where) { TValue *errobj = s2v(L->top - 1); /* error object */ const char *msg = (ttisstring(errobj)) ? svalue(errobj) : "error object is not a string"; /* produce warning "error in %s (%s)" (where, msg) */ luaE_warning(L, "error in ", 1); luaE_warning(L, where, 1); luaE_warning(L, " (", 1); luaE_warning(L, msg, 1); luaE_warning(L, ")", 0); } /* ** $Id: lgc.c $ ** Garbage Collector ** See Copyright Notice in lua.h */ #define lgc_c #define LUA_CORE /*#include "lprefix.h"*/ #include #include /*#include "lua.h"*/ /*#include "ldebug.h"*/ /*#include "ldo.h"*/ /*#include "lfunc.h"*/ /*#include "lgc.h"*/ /*#include "lmem.h"*/ /*#include "lobject.h"*/ /*#include "lstate.h"*/ /*#include "lstring.h"*/ /*#include "ltable.h"*/ /*#include "ltm.h"*/ /* ** Maximum number of elements to sweep in each single step. ** (Large enough to dissipate fixed overheads but small enough ** to allow small steps for the collector.) */ #define GCSWEEPMAX 100 /* ** Maximum number of finalizers to call in each single step. */ #define GCFINMAX 10 /* ** Cost of calling one finalizer. */ #define GCFINALIZECOST 50 /* ** The equivalent, in bytes, of one unit of "work" (visiting a slot, ** sweeping an object, etc.) */ #define WORK2MEM sizeof(TValue) /* ** macro to adjust 'pause': 'pause' is actually used like ** 'pause / PAUSEADJ' (value chosen by tests) */ #define PAUSEADJ 100 /* mask with all color bits */ #define maskcolors (bitmask(BLACKBIT) | WHITEBITS) /* mask with all GC bits */ #define maskgcbits (maskcolors | AGEBITS) /* macro to erase all color bits then set only the current white bit */ #define makewhite(g,x) \ (x->marked = cast_byte((x->marked & ~maskcolors) | luaC_white(g))) /* make an object gray (neither white nor black) */ #define set2gray(x) resetbits(x->marked, maskcolors) /* make an object black (coming from any color) */ #define set2black(x) \ (x->marked = cast_byte((x->marked & ~WHITEBITS) | bitmask(BLACKBIT))) #define valiswhite(x) (iscollectable(x) && iswhite(gcvalue(x))) #define keyiswhite(n) (keyiscollectable(n) && iswhite(gckey(n))) /* ** Protected access to objects in values */ #define gcvalueN(o) (iscollectable(o) ? gcvalue(o) : NULL) #define markvalue(g,o) { checkliveness(g->mainthread,o); \ if (valiswhite(o)) reallymarkobject(g,gcvalue(o)); } #define markkey(g, n) { if keyiswhite(n) reallymarkobject(g,gckey(n)); } #define markobject(g,t) { if (iswhite(t)) reallymarkobject(g, obj2gco(t)); } /* ** mark an object that can be NULL (either because it is really optional, ** or it was stripped as debug info, or inside an uncompleted structure) */ #define markobjectN(g,t) { if (t) markobject(g,t); } static void reallymarkobject (global_State *g, GCObject *o); static lu_mem atomic (lua_State *L); static void entersweep (lua_State *L); /* ** {====================================================== ** Generic functions ** ======================================================= */ /* ** one after last element in a hash array */ #define gnodelast(h) gnode(h, cast_sizet(sizenode(h))) static GCObject **getgclist (GCObject *o) { switch (o->tt) { case LUA_VTABLE: return &gco2t(o)->gclist; case LUA_VLCL: return &gco2lcl(o)->gclist; case LUA_VCCL: return &gco2ccl(o)->gclist; case LUA_VTHREAD: return &gco2th(o)->gclist; case LUA_VPROTO: return &gco2p(o)->gclist; case LUA_VUSERDATA: { Udata *u = gco2u(o); lua_assert(u->nuvalue > 0); return &u->gclist; } default: lua_assert(0); return 0; } } /* ** Link a collectable object 'o' with a known type into the list 'p'. ** (Must be a macro to access the 'gclist' field in different types.) */ #define linkgclist(o,p) linkgclist_(obj2gco(o), &(o)->gclist, &(p)) static void linkgclist_ (GCObject *o, GCObject **pnext, GCObject **list) { lua_assert(!isgray(o)); /* cannot be in a gray list */ *pnext = *list; *list = o; set2gray(o); /* now it is */ } /* ** Link a generic collectable object 'o' into the list 'p'. */ #define linkobjgclist(o,p) linkgclist_(obj2gco(o), getgclist(o), &(p)) /* ** Clear keys for empty entries in tables. If entry is empty, mark its ** entry as dead. This allows the collection of the key, but keeps its ** entry in the table: its removal could break a chain and could break ** a table traversal. Other places never manipulate dead keys, because ** its associated empty value is enough to signal that the entry is ** logically empty. */ static void clearkey (Node *n) { lua_assert(isempty(gval(n))); if (keyiscollectable(n)) setdeadkey(n); /* unused key; remove it */ } /* ** tells whether a key or value can be cleared from a weak ** table. Non-collectable objects are never removed from weak ** tables. Strings behave as 'values', so are never removed too. for ** other objects: if really collected, cannot keep them; for objects ** being finalized, keep them in keys, but not in values */ static int iscleared (global_State *g, const GCObject *o) { if (o == NULL) return 0; /* non-collectable value */ else if (novariant(o->tt) == LUA_TSTRING) { markobject(g, o); /* strings are 'values', so are never weak */ return 0; } else return iswhite(o); } /* ** Barrier that moves collector forward, that is, marks the white object ** 'v' being pointed by the black object 'o'. In the generational ** mode, 'v' must also become old, if 'o' is old; however, it cannot ** be changed directly to OLD, because it may still point to non-old ** objects. So, it is marked as OLD0. In the next cycle it will become ** OLD1, and in the next it will finally become OLD (regular old). By ** then, any object it points to will also be old. If called in the ** incremental sweep phase, it clears the black object to white (sweep ** it) to avoid other barrier calls for this same object. (That cannot ** be done is generational mode, as its sweep does not distinguish ** whites from deads.) */ void luaC_barrier_ (lua_State *L, GCObject *o, GCObject *v) { global_State *g = G(L); lua_assert(isblack(o) && iswhite(v) && !isdead(g, v) && !isdead(g, o)); if (keepinvariant(g)) { /* must keep invariant? */ reallymarkobject(g, v); /* restore invariant */ if (isold(o)) { lua_assert(!isold(v)); /* white object could not be old */ setage(v, G_OLD0); /* restore generational invariant */ } } else { /* sweep phase */ lua_assert(issweepphase(g)); if (g->gckind == KGC_INC) /* incremental mode? */ makewhite(g, o); /* mark 'o' as white to avoid other barriers */ } } /* ** barrier that moves collector backward, that is, mark the black object ** pointing to a white object as gray again. */ void luaC_barrierback_ (lua_State *L, GCObject *o) { global_State *g = G(L); lua_assert(isblack(o) && !isdead(g, o)); lua_assert((g->gckind == KGC_GEN) == (isold(o) && getage(o) != G_TOUCHED1)); if (getage(o) == G_TOUCHED2) /* already in gray list? */ set2gray(o); /* make it gray to become touched1 */ else /* link it in 'grayagain' and paint it gray */ linkobjgclist(o, g->grayagain); if (isold(o)) /* generational mode? */ setage(o, G_TOUCHED1); /* touched in current cycle */ } void luaC_fix (lua_State *L, GCObject *o) { global_State *g = G(L); lua_assert(g->allgc == o); /* object must be 1st in 'allgc' list! */ set2gray(o); /* they will be gray forever */ setage(o, G_OLD); /* and old forever */ g->allgc = o->next; /* remove object from 'allgc' list */ o->next = g->fixedgc; /* link it to 'fixedgc' list */ g->fixedgc = o; } /* ** create a new collectable object (with given type and size) and link ** it to 'allgc' list. */ GCObject *luaC_newobj (lua_State *L, int tt, size_t sz) { global_State *g = G(L); GCObject *o = cast(GCObject *, luaM_newobject(L, novariant(tt), sz)); o->marked = luaC_white(g); o->tt = tt; o->next = g->allgc; g->allgc = o; return o; } /* }====================================================== */ /* ** {====================================================== ** Mark functions ** ======================================================= */ /* ** Mark an object. Userdata with no user values, strings, and closed ** upvalues are visited and turned black here. Open upvalues are ** already indirectly linked through their respective threads in the ** 'twups' list, so they don't go to the gray list; nevertheless, they ** are kept gray to avoid barriers, as their values will be revisited ** by the thread or by 'remarkupvals'. Other objects are added to the ** gray list to be visited (and turned black) later. Both userdata and ** upvalues can call this function recursively, but this recursion goes ** for at most two levels: An upvalue cannot refer to another upvalue ** (only closures can), and a userdata's metatable must be a table. */ static void reallymarkobject (global_State *g, GCObject *o) { switch (o->tt) { case LUA_VSHRSTR: case LUA_VLNGSTR: { set2black(o); /* nothing to visit */ break; } case LUA_VUPVAL: { UpVal *uv = gco2upv(o); if (upisopen(uv)) set2gray(uv); /* open upvalues are kept gray */ else set2black(uv); /* closed upvalues are visited here */ markvalue(g, uv->v); /* mark its content */ break; } case LUA_VUSERDATA: { Udata *u = gco2u(o); if (u->nuvalue == 0) { /* no user values? */ markobjectN(g, u->metatable); /* mark its metatable */ set2black(u); /* nothing else to mark */ break; } /* else... */ } /* FALLTHROUGH */ case LUA_VLCL: case LUA_VCCL: case LUA_VTABLE: case LUA_VTHREAD: case LUA_VPROTO: { linkobjgclist(o, g->gray); /* to be visited later */ break; } default: lua_assert(0); break; } } /* ** mark metamethods for basic types */ static void markmt (global_State *g) { int i; for (i=0; i < LUA_NUMTAGS; i++) markobjectN(g, g->mt[i]); } /* ** mark all objects in list of being-finalized */ static lu_mem markbeingfnz (global_State *g) { GCObject *o; lu_mem count = 0; for (o = g->tobefnz; o != NULL; o = o->next) { count++; markobject(g, o); } return count; } /* ** For each non-marked thread, simulates a barrier between each open ** upvalue and its value. (If the thread is collected, the value will be ** assigned to the upvalue, but then it can be too late for the barrier ** to act. The "barrier" does not need to check colors: A non-marked ** thread must be young; upvalues cannot be older than their threads; so ** any visited upvalue must be young too.) Also removes the thread from ** the list, as it was already visited. Removes also threads with no ** upvalues, as they have nothing to be checked. (If the thread gets an ** upvalue later, it will be linked in the list again.) */ static int remarkupvals (global_State *g) { lua_State *thread; lua_State **p = &g->twups; int work = 0; /* estimate of how much work was done here */ while ((thread = *p) != NULL) { work++; if (!iswhite(thread) && thread->openupval != NULL) p = &thread->twups; /* keep marked thread with upvalues in the list */ else { /* thread is not marked or without upvalues */ UpVal *uv; lua_assert(!isold(thread) || thread->openupval == NULL); *p = thread->twups; /* remove thread from the list */ thread->twups = thread; /* mark that it is out of list */ for (uv = thread->openupval; uv != NULL; uv = uv->u.open.next) { lua_assert(getage(uv) <= getage(thread)); work++; if (!iswhite(uv)) { /* upvalue already visited? */ lua_assert(upisopen(uv) && isgray(uv)); markvalue(g, uv->v); /* mark its value */ } } } } return work; } static void cleargraylists (global_State *g) { g->gray = g->grayagain = NULL; g->weak = g->allweak = g->ephemeron = NULL; } /* ** mark root set and reset all gray lists, to start a new collection */ static void restartcollection (global_State *g) { cleargraylists(g); markobject(g, g->mainthread); markvalue(g, &g->l_registry); markmt(g); markbeingfnz(g); /* mark any finalizing object left from previous cycle */ } /* }====================================================== */ /* ** {====================================================== ** Traverse functions ** ======================================================= */ /* ** Check whether object 'o' should be kept in the 'grayagain' list for ** post-processing by 'correctgraylist'. (It could put all old objects ** in the list and leave all the work to 'correctgraylist', but it is ** more efficient to avoid adding elements that will be removed.) Only ** TOUCHED1 objects need to be in the list. TOUCHED2 doesn't need to go ** back to a gray list, but then it must become OLD. (That is what ** 'correctgraylist' does when it finds a TOUCHED2 object.) */ static void genlink (global_State *g, GCObject *o) { lua_assert(isblack(o)); if (getage(o) == G_TOUCHED1) { /* touched in this cycle? */ linkobjgclist(o, g->grayagain); /* link it back in 'grayagain' */ } /* everything else do not need to be linked back */ else if (getage(o) == G_TOUCHED2) changeage(o, G_TOUCHED2, G_OLD); /* advance age */ } /* ** Traverse a table with weak values and link it to proper list. During ** propagate phase, keep it in 'grayagain' list, to be revisited in the ** atomic phase. In the atomic phase, if table has any white value, ** put it in 'weak' list, to be cleared. */ static void traverseweakvalue (global_State *g, Table *h) { Node *n, *limit = gnodelast(h); /* if there is array part, assume it may have white values (it is not worth traversing it now just to check) */ int hasclears = (h->alimit > 0); for (n = gnode(h, 0); n < limit; n++) { /* traverse hash part */ if (isempty(gval(n))) /* entry is empty? */ clearkey(n); /* clear its key */ else { lua_assert(!keyisnil(n)); markkey(g, n); if (!hasclears && iscleared(g, gcvalueN(gval(n)))) /* a white value? */ hasclears = 1; /* table will have to be cleared */ } } if (g->gcstate == GCSatomic && hasclears) linkgclist(h, g->weak); /* has to be cleared later */ else linkgclist(h, g->grayagain); /* must retraverse it in atomic phase */ } /* ** Traverse an ephemeron table and link it to proper list. Returns true ** iff any object was marked during this traversal (which implies that ** convergence has to continue). During propagation phase, keep table ** in 'grayagain' list, to be visited again in the atomic phase. In ** the atomic phase, if table has any white->white entry, it has to ** be revisited during ephemeron convergence (as that key may turn ** black). Otherwise, if it has any white key, table has to be cleared ** (in the atomic phase). In generational mode, some tables ** must be kept in some gray list for post-processing; this is done ** by 'genlink'. */ static int traverseephemeron (global_State *g, Table *h, int inv) { int marked = 0; /* true if an object is marked in this traversal */ int hasclears = 0; /* true if table has white keys */ int hasww = 0; /* true if table has entry "white-key -> white-value" */ unsigned int i; unsigned int asize = luaH_realasize(h); unsigned int nsize = sizenode(h); /* traverse array part */ for (i = 0; i < asize; i++) { if (valiswhite(&h->array[i])) { marked = 1; reallymarkobject(g, gcvalue(&h->array[i])); } } /* traverse hash part; if 'inv', traverse descending (see 'convergeephemerons') */ for (i = 0; i < nsize; i++) { Node *n = inv ? gnode(h, nsize - 1 - i) : gnode(h, i); if (isempty(gval(n))) /* entry is empty? */ clearkey(n); /* clear its key */ else if (iscleared(g, gckeyN(n))) { /* key is not marked (yet)? */ hasclears = 1; /* table must be cleared */ if (valiswhite(gval(n))) /* value not marked yet? */ hasww = 1; /* white-white entry */ } else if (valiswhite(gval(n))) { /* value not marked yet? */ marked = 1; reallymarkobject(g, gcvalue(gval(n))); /* mark it now */ } } /* link table into proper list */ if (g->gcstate == GCSpropagate) linkgclist(h, g->grayagain); /* must retraverse it in atomic phase */ else if (hasww) /* table has white->white entries? */ linkgclist(h, g->ephemeron); /* have to propagate again */ else if (hasclears) /* table has white keys? */ linkgclist(h, g->allweak); /* may have to clean white keys */ else genlink(g, obj2gco(h)); /* check whether collector still needs to see it */ return marked; } static void traversestrongtable (global_State *g, Table *h) { Node *n, *limit = gnodelast(h); unsigned int i; unsigned int asize = luaH_realasize(h); for (i = 0; i < asize; i++) /* traverse array part */ markvalue(g, &h->array[i]); for (n = gnode(h, 0); n < limit; n++) { /* traverse hash part */ if (isempty(gval(n))) /* entry is empty? */ clearkey(n); /* clear its key */ else { lua_assert(!keyisnil(n)); markkey(g, n); markvalue(g, gval(n)); } } genlink(g, obj2gco(h)); } static lu_mem traversetable (global_State *g, Table *h) { const char *weakkey, *weakvalue; const TValue *mode = gfasttm(g, h->metatable, TM_MODE); markobjectN(g, h->metatable); if (mode && ttisstring(mode) && /* is there a weak mode? */ (cast_void(weakkey = strchr(svalue(mode), 'k')), cast_void(weakvalue = strchr(svalue(mode), 'v')), (weakkey || weakvalue))) { /* is really weak? */ if (!weakkey) /* strong keys? */ traverseweakvalue(g, h); else if (!weakvalue) /* strong values? */ traverseephemeron(g, h, 0); else /* all weak */ linkgclist(h, g->allweak); /* nothing to traverse now */ } else /* not weak */ traversestrongtable(g, h); return 1 + h->alimit + 2 * allocsizenode(h); } static int traverseudata (global_State *g, Udata *u) { int i; markobjectN(g, u->metatable); /* mark its metatable */ for (i = 0; i < u->nuvalue; i++) markvalue(g, &u->uv[i].uv); genlink(g, obj2gco(u)); return 1 + u->nuvalue; } /* ** Traverse a prototype. (While a prototype is being build, its ** arrays can be larger than needed; the extra slots are filled with ** NULL, so the use of 'markobjectN') */ static int traverseproto (global_State *g, Proto *f) { int i; markobjectN(g, f->source); for (i = 0; i < f->sizek; i++) /* mark literals */ markvalue(g, &f->k[i]); for (i = 0; i < f->sizeupvalues; i++) /* mark upvalue names */ markobjectN(g, f->upvalues[i].name); for (i = 0; i < f->sizep; i++) /* mark nested protos */ markobjectN(g, f->p[i]); for (i = 0; i < f->sizelocvars; i++) /* mark local-variable names */ markobjectN(g, f->locvars[i].varname); return 1 + f->sizek + f->sizeupvalues + f->sizep + f->sizelocvars; } static int traverseCclosure (global_State *g, CClosure *cl) { int i; for (i = 0; i < cl->nupvalues; i++) /* mark its upvalues */ markvalue(g, &cl->upvalue[i]); return 1 + cl->nupvalues; } /* ** Traverse a Lua closure, marking its prototype and its upvalues. ** (Both can be NULL while closure is being created.) */ static int traverseLclosure (global_State *g, LClosure *cl) { int i; markobjectN(g, cl->p); /* mark its prototype */ for (i = 0; i < cl->nupvalues; i++) { /* visit its upvalues */ UpVal *uv = cl->upvals[i]; markobjectN(g, uv); /* mark upvalue */ } return 1 + cl->nupvalues; } /* ** Traverse a thread, marking the elements in the stack up to its top ** and cleaning the rest of the stack in the final traversal. That ** ensures that the entire stack have valid (non-dead) objects. ** Threads have no barriers. In gen. mode, old threads must be visited ** at every cycle, because they might point to young objects. In inc. ** mode, the thread can still be modified before the end of the cycle, ** and therefore it must be visited again in the atomic phase. To ensure ** these visits, threads must return to a gray list if they are not new ** (which can only happen in generational mode) or if the traverse is in ** the propagate phase (which can only happen in incremental mode). */ static int traversethread (global_State *g, lua_State *th) { UpVal *uv; StkId o = th->stack; if (isold(th) || g->gcstate == GCSpropagate) linkgclist(th, g->grayagain); /* insert into 'grayagain' list */ if (o == NULL) return 1; /* stack not completely built yet */ lua_assert(g->gcstate == GCSatomic || th->openupval == NULL || isintwups(th)); for (; o < th->top; o++) /* mark live elements in the stack */ markvalue(g, s2v(o)); for (uv = th->openupval; uv != NULL; uv = uv->u.open.next) markobject(g, uv); /* open upvalues cannot be collected */ if (g->gcstate == GCSatomic) { /* final traversal? */ for (; o < th->stack_last + EXTRA_STACK; o++) setnilvalue(s2v(o)); /* clear dead stack slice */ /* 'remarkupvals' may have removed thread from 'twups' list */ if (!isintwups(th) && th->openupval != NULL) { th->twups = g->twups; /* link it back to the list */ g->twups = th; } } else if (!g->gcemergency) luaD_shrinkstack(th); /* do not change stack in emergency cycle */ return 1 + stacksize(th); } /* ** traverse one gray object, turning it to black. */ static lu_mem propagatemark (global_State *g) { GCObject *o = g->gray; nw2black(o); g->gray = *getgclist(o); /* remove from 'gray' list */ switch (o->tt) { case LUA_VTABLE: return traversetable(g, gco2t(o)); case LUA_VUSERDATA: return traverseudata(g, gco2u(o)); case LUA_VLCL: return traverseLclosure(g, gco2lcl(o)); case LUA_VCCL: return traverseCclosure(g, gco2ccl(o)); case LUA_VPROTO: return traverseproto(g, gco2p(o)); case LUA_VTHREAD: return traversethread(g, gco2th(o)); default: lua_assert(0); return 0; } } static lu_mem propagateall (global_State *g) { lu_mem tot = 0; while (g->gray) tot += propagatemark(g); return tot; } /* ** Traverse all ephemeron tables propagating marks from keys to values. ** Repeat until it converges, that is, nothing new is marked. 'dir' ** inverts the direction of the traversals, trying to speed up ** convergence on chains in the same table. ** */ static void convergeephemerons (global_State *g) { int changed; int dir = 0; do { GCObject *w; GCObject *next = g->ephemeron; /* get ephemeron list */ g->ephemeron = NULL; /* tables may return to this list when traversed */ changed = 0; while ((w = next) != NULL) { /* for each ephemeron table */ Table *h = gco2t(w); next = h->gclist; /* list is rebuilt during loop */ nw2black(h); /* out of the list (for now) */ if (traverseephemeron(g, h, dir)) { /* marked some value? */ propagateall(g); /* propagate changes */ changed = 1; /* will have to revisit all ephemeron tables */ } } dir = !dir; /* invert direction next time */ } while (changed); /* repeat until no more changes */ } /* }====================================================== */ /* ** {====================================================== ** Sweep Functions ** ======================================================= */ /* ** clear entries with unmarked keys from all weaktables in list 'l' */ static void clearbykeys (global_State *g, GCObject *l) { for (; l; l = gco2t(l)->gclist) { Table *h = gco2t(l); Node *limit = gnodelast(h); Node *n; for (n = gnode(h, 0); n < limit; n++) { if (iscleared(g, gckeyN(n))) /* unmarked key? */ setempty(gval(n)); /* remove entry */ if (isempty(gval(n))) /* is entry empty? */ clearkey(n); /* clear its key */ } } } /* ** clear entries with unmarked values from all weaktables in list 'l' up ** to element 'f' */ static void clearbyvalues (global_State *g, GCObject *l, GCObject *f) { for (; l != f; l = gco2t(l)->gclist) { Table *h = gco2t(l); Node *n, *limit = gnodelast(h); unsigned int i; unsigned int asize = luaH_realasize(h); for (i = 0; i < asize; i++) { TValue *o = &h->array[i]; if (iscleared(g, gcvalueN(o))) /* value was collected? */ setempty(o); /* remove entry */ } for (n = gnode(h, 0); n < limit; n++) { if (iscleared(g, gcvalueN(gval(n)))) /* unmarked value? */ setempty(gval(n)); /* remove entry */ if (isempty(gval(n))) /* is entry empty? */ clearkey(n); /* clear its key */ } } } static void freeupval (lua_State *L, UpVal *uv) { if (upisopen(uv)) luaF_unlinkupval(uv); luaM_free(L, uv); } static void freeobj (lua_State *L, GCObject *o) { switch (o->tt) { case LUA_VPROTO: luaF_freeproto(L, gco2p(o)); break; case LUA_VUPVAL: freeupval(L, gco2upv(o)); break; case LUA_VLCL: { LClosure *cl = gco2lcl(o); luaM_freemem(L, cl, sizeLclosure(cl->nupvalues)); break; } case LUA_VCCL: { CClosure *cl = gco2ccl(o); luaM_freemem(L, cl, sizeCclosure(cl->nupvalues)); break; } case LUA_VTABLE: luaH_free(L, gco2t(o)); break; case LUA_VTHREAD: luaE_freethread(L, gco2th(o)); break; case LUA_VUSERDATA: { Udata *u = gco2u(o); luaM_freemem(L, o, sizeudata(u->nuvalue, u->len)); break; } case LUA_VSHRSTR: { TString *ts = gco2ts(o); luaS_remove(L, ts); /* remove it from hash table */ luaM_freemem(L, ts, sizelstring(ts->shrlen)); break; } case LUA_VLNGSTR: { TString *ts = gco2ts(o); luaM_freemem(L, ts, sizelstring(ts->u.lnglen)); break; } default: lua_assert(0); } } /* ** sweep at most 'countin' elements from a list of GCObjects erasing dead ** objects, where a dead object is one marked with the old (non current) ** white; change all non-dead objects back to white, preparing for next ** collection cycle. Return where to continue the traversal or NULL if ** list is finished. ('*countout' gets the number of elements traversed.) */ static GCObject **sweeplist (lua_State *L, GCObject **p, int countin, int *countout) { global_State *g = G(L); int ow = otherwhite(g); int i; int white = luaC_white(g); /* current white */ for (i = 0; *p != NULL && i < countin; i++) { GCObject *curr = *p; int marked = curr->marked; if (isdeadm(ow, marked)) { /* is 'curr' dead? */ *p = curr->next; /* remove 'curr' from list */ freeobj(L, curr); /* erase 'curr' */ } else { /* change mark to 'white' */ curr->marked = cast_byte((marked & ~maskgcbits) | white); p = &curr->next; /* go to next element */ } } if (countout) *countout = i; /* number of elements traversed */ return (*p == NULL) ? NULL : p; } /* ** sweep a list until a live object (or end of list) */ static GCObject **sweeptolive (lua_State *L, GCObject **p) { GCObject **old = p; do { p = sweeplist(L, p, 1, NULL); } while (p == old); return p; } /* }====================================================== */ /* ** {====================================================== ** Finalization ** ======================================================= */ /* ** If possible, shrink string table. */ static void checkSizes (lua_State *L, global_State *g) { if (!g->gcemergency) { if (g->strt.nuse < g->strt.size / 4) { /* string table too big? */ l_mem olddebt = g->GCdebt; luaS_resize(L, g->strt.size / 2); g->GCestimate += g->GCdebt - olddebt; /* correct estimate */ } } } /* ** Get the next udata to be finalized from the 'tobefnz' list, and ** link it back into the 'allgc' list. */ static GCObject *udata2finalize (global_State *g) { GCObject *o = g->tobefnz; /* get first element */ lua_assert(tofinalize(o)); g->tobefnz = o->next; /* remove it from 'tobefnz' list */ o->next = g->allgc; /* return it to 'allgc' list */ g->allgc = o; resetbit(o->marked, FINALIZEDBIT); /* object is "normal" again */ if (issweepphase(g)) makewhite(g, o); /* "sweep" object */ else if (getage(o) == G_OLD1) g->firstold1 = o; /* it is the first OLD1 object in the list */ return o; } static void dothecall (lua_State *L, void *ud) { UNUSED(ud); luaD_callnoyield(L, L->top - 2, 0); } static void GCTM (lua_State *L) { global_State *g = G(L); const TValue *tm; TValue v; lua_assert(!g->gcemergency); setgcovalue(L, &v, udata2finalize(g)); tm = luaT_gettmbyobj(L, &v, TM_GC); if (!notm(tm)) { /* is there a finalizer? */ int status; lu_byte oldah = L->allowhook; int oldgcstp = g->gcstp; g->gcstp |= GCSTPGC; /* avoid GC steps */ L->allowhook = 0; /* stop debug hooks during GC metamethod */ setobj2s(L, L->top++, tm); /* push finalizer... */ setobj2s(L, L->top++, &v); /* ... and its argument */ L->ci->callstatus |= CIST_FIN; /* will run a finalizer */ status = luaD_pcall(L, dothecall, NULL, savestack(L, L->top - 2), 0); L->ci->callstatus &= ~CIST_FIN; /* not running a finalizer anymore */ L->allowhook = oldah; /* restore hooks */ g->gcstp = oldgcstp; /* restore state */ if (l_unlikely(status != LUA_OK)) { /* error while running __gc? */ luaE_warnerror(L, "__gc"); L->top--; /* pops error object */ } } } /* ** Call a few finalizers */ static int runafewfinalizers (lua_State *L, int n) { global_State *g = G(L); int i; for (i = 0; i < n && g->tobefnz; i++) GCTM(L); /* call one finalizer */ return i; } /* ** call all pending finalizers */ static void callallpendingfinalizers (lua_State *L) { global_State *g = G(L); while (g->tobefnz) GCTM(L); } /* ** find last 'next' field in list 'p' list (to add elements in its end) */ static GCObject **findlast (GCObject **p) { while (*p != NULL) p = &(*p)->next; return p; } /* ** Move all unreachable objects (or 'all' objects) that need ** finalization from list 'finobj' to list 'tobefnz' (to be finalized). ** (Note that objects after 'finobjold1' cannot be white, so they ** don't need to be traversed. In incremental mode, 'finobjold1' is NULL, ** so the whole list is traversed.) */ static void separatetobefnz (global_State *g, int all) { GCObject *curr; GCObject **p = &g->finobj; GCObject **lastnext = findlast(&g->tobefnz); while ((curr = *p) != g->finobjold1) { /* traverse all finalizable objects */ lua_assert(tofinalize(curr)); if (!(iswhite(curr) || all)) /* not being collected? */ p = &curr->next; /* don't bother with it */ else { if (curr == g->finobjsur) /* removing 'finobjsur'? */ g->finobjsur = curr->next; /* correct it */ *p = curr->next; /* remove 'curr' from 'finobj' list */ curr->next = *lastnext; /* link at the end of 'tobefnz' list */ *lastnext = curr; lastnext = &curr->next; } } } /* ** If pointer 'p' points to 'o', move it to the next element. */ static void checkpointer (GCObject **p, GCObject *o) { if (o == *p) *p = o->next; } /* ** Correct pointers to objects inside 'allgc' list when ** object 'o' is being removed from the list. */ static void correctpointers (global_State *g, GCObject *o) { checkpointer(&g->survival, o); checkpointer(&g->old1, o); checkpointer(&g->reallyold, o); checkpointer(&g->firstold1, o); } /* ** if object 'o' has a finalizer, remove it from 'allgc' list (must ** search the list to find it) and link it in 'finobj' list. */ void luaC_checkfinalizer (lua_State *L, GCObject *o, Table *mt) { global_State *g = G(L); if (tofinalize(o) || /* obj. is already marked... */ gfasttm(g, mt, TM_GC) == NULL || /* or has no finalizer... */ (g->gcstp & GCSTPCLS)) /* or closing state? */ return; /* nothing to be done */ else { /* move 'o' to 'finobj' list */ GCObject **p; if (issweepphase(g)) { makewhite(g, o); /* "sweep" object 'o' */ if (g->sweepgc == &o->next) /* should not remove 'sweepgc' object */ g->sweepgc = sweeptolive(L, g->sweepgc); /* change 'sweepgc' */ } else correctpointers(g, o); /* search for pointer pointing to 'o' */ for (p = &g->allgc; *p != o; p = &(*p)->next) { /* empty */ } *p = o->next; /* remove 'o' from 'allgc' list */ o->next = g->finobj; /* link it in 'finobj' list */ g->finobj = o; l_setbit(o->marked, FINALIZEDBIT); /* mark it as such */ } } /* }====================================================== */ /* ** {====================================================== ** Generational Collector ** ======================================================= */ static void setpause (global_State *g); /* ** Sweep a list of objects to enter generational mode. Deletes dead ** objects and turns the non dead to old. All non-dead threads---which ** are now old---must be in a gray list. Everything else is not in a ** gray list. Open upvalues are also kept gray. */ static void sweep2old (lua_State *L, GCObject **p) { GCObject *curr; global_State *g = G(L); while ((curr = *p) != NULL) { if (iswhite(curr)) { /* is 'curr' dead? */ lua_assert(isdead(g, curr)); *p = curr->next; /* remove 'curr' from list */ freeobj(L, curr); /* erase 'curr' */ } else { /* all surviving objects become old */ setage(curr, G_OLD); if (curr->tt == LUA_VTHREAD) { /* threads must be watched */ lua_State *th = gco2th(curr); linkgclist(th, g->grayagain); /* insert into 'grayagain' list */ } else if (curr->tt == LUA_VUPVAL && upisopen(gco2upv(curr))) set2gray(curr); /* open upvalues are always gray */ else /* everything else is black */ nw2black(curr); p = &curr->next; /* go to next element */ } } } /* ** Sweep for generational mode. Delete dead objects. (Because the ** collection is not incremental, there are no "new white" objects ** during the sweep. So, any white object must be dead.) For ** non-dead objects, advance their ages and clear the color of ** new objects. (Old objects keep their colors.) ** The ages of G_TOUCHED1 and G_TOUCHED2 objects cannot be advanced ** here, because these old-generation objects are usually not swept ** here. They will all be advanced in 'correctgraylist'. That function ** will also remove objects turned white here from any gray list. */ static GCObject **sweepgen (lua_State *L, global_State *g, GCObject **p, GCObject *limit, GCObject **pfirstold1) { static const lu_byte nextage[] = { G_SURVIVAL, /* from G_NEW */ G_OLD1, /* from G_SURVIVAL */ G_OLD1, /* from G_OLD0 */ G_OLD, /* from G_OLD1 */ G_OLD, /* from G_OLD (do not change) */ G_TOUCHED1, /* from G_TOUCHED1 (do not change) */ G_TOUCHED2 /* from G_TOUCHED2 (do not change) */ }; int white = luaC_white(g); GCObject *curr; while ((curr = *p) != limit) { if (iswhite(curr)) { /* is 'curr' dead? */ lua_assert(!isold(curr) && isdead(g, curr)); *p = curr->next; /* remove 'curr' from list */ freeobj(L, curr); /* erase 'curr' */ } else { /* correct mark and age */ if (getage(curr) == G_NEW) { /* new objects go back to white */ int marked = curr->marked & ~maskgcbits; /* erase GC bits */ curr->marked = cast_byte(marked | G_SURVIVAL | white); } else { /* all other objects will be old, and so keep their color */ setage(curr, nextage[getage(curr)]); if (getage(curr) == G_OLD1 && *pfirstold1 == NULL) *pfirstold1 = curr; /* first OLD1 object in the list */ } p = &curr->next; /* go to next element */ } } return p; } /* ** Traverse a list making all its elements white and clearing their ** age. In incremental mode, all objects are 'new' all the time, ** except for fixed strings (which are always old). */ static void whitelist (global_State *g, GCObject *p) { int white = luaC_white(g); for (; p != NULL; p = p->next) p->marked = cast_byte((p->marked & ~maskgcbits) | white); } /* ** Correct a list of gray objects. Return pointer to where rest of the ** list should be linked. ** Because this correction is done after sweeping, young objects might ** be turned white and still be in the list. They are only removed. ** 'TOUCHED1' objects are advanced to 'TOUCHED2' and remain on the list; ** Non-white threads also remain on the list; 'TOUCHED2' objects become ** regular old; they and anything else are removed from the list. */ static GCObject **correctgraylist (GCObject **p) { GCObject *curr; while ((curr = *p) != NULL) { GCObject **next = getgclist(curr); if (iswhite(curr)) goto remove; /* remove all white objects */ else if (getage(curr) == G_TOUCHED1) { /* touched in this cycle? */ lua_assert(isgray(curr)); nw2black(curr); /* make it black, for next barrier */ changeage(curr, G_TOUCHED1, G_TOUCHED2); goto remain; /* keep it in the list and go to next element */ } else if (curr->tt == LUA_VTHREAD) { lua_assert(isgray(curr)); goto remain; /* keep non-white threads on the list */ } else { /* everything else is removed */ lua_assert(isold(curr)); /* young objects should be white here */ if (getage(curr) == G_TOUCHED2) /* advance from TOUCHED2... */ changeage(curr, G_TOUCHED2, G_OLD); /* ... to OLD */ nw2black(curr); /* make object black (to be removed) */ goto remove; } remove: *p = *next; continue; remain: p = next; continue; } return p; } /* ** Correct all gray lists, coalescing them into 'grayagain'. */ static void correctgraylists (global_State *g) { GCObject **list = correctgraylist(&g->grayagain); *list = g->weak; g->weak = NULL; list = correctgraylist(list); *list = g->allweak; g->allweak = NULL; list = correctgraylist(list); *list = g->ephemeron; g->ephemeron = NULL; correctgraylist(list); } /* ** Mark black 'OLD1' objects when starting a new young collection. ** Gray objects are already in some gray list, and so will be visited ** in the atomic step. */ static void markold (global_State *g, GCObject *from, GCObject *to) { GCObject *p; for (p = from; p != to; p = p->next) { if (getage(p) == G_OLD1) { lua_assert(!iswhite(p)); changeage(p, G_OLD1, G_OLD); /* now they are old */ if (isblack(p)) reallymarkobject(g, p); } } } /* ** Finish a young-generation collection. */ static void finishgencycle (lua_State *L, global_State *g) { correctgraylists(g); checkSizes(L, g); g->gcstate = GCSpropagate; /* skip restart */ if (!g->gcemergency) callallpendingfinalizers(L); } /* ** Does a young collection. First, mark 'OLD1' objects. Then does the ** atomic step. Then, sweep all lists and advance pointers. Finally, ** finish the collection. */ static void youngcollection (lua_State *L, global_State *g) { GCObject **psurvival; /* to point to first non-dead survival object */ GCObject *dummy; /* dummy out parameter to 'sweepgen' */ lua_assert(g->gcstate == GCSpropagate); if (g->firstold1) { /* are there regular OLD1 objects? */ markold(g, g->firstold1, g->reallyold); /* mark them */ g->firstold1 = NULL; /* no more OLD1 objects (for now) */ } markold(g, g->finobj, g->finobjrold); markold(g, g->tobefnz, NULL); atomic(L); /* sweep nursery and get a pointer to its last live element */ g->gcstate = GCSswpallgc; psurvival = sweepgen(L, g, &g->allgc, g->survival, &g->firstold1); /* sweep 'survival' */ sweepgen(L, g, psurvival, g->old1, &g->firstold1); g->reallyold = g->old1; g->old1 = *psurvival; /* 'survival' survivals are old now */ g->survival = g->allgc; /* all news are survivals */ /* repeat for 'finobj' lists */ dummy = NULL; /* no 'firstold1' optimization for 'finobj' lists */ psurvival = sweepgen(L, g, &g->finobj, g->finobjsur, &dummy); /* sweep 'survival' */ sweepgen(L, g, psurvival, g->finobjold1, &dummy); g->finobjrold = g->finobjold1; g->finobjold1 = *psurvival; /* 'survival' survivals are old now */ g->finobjsur = g->finobj; /* all news are survivals */ sweepgen(L, g, &g->tobefnz, NULL, &dummy); finishgencycle(L, g); } /* ** Clears all gray lists, sweeps objects, and prepare sublists to enter ** generational mode. The sweeps remove dead objects and turn all ** surviving objects to old. Threads go back to 'grayagain'; everything ** else is turned black (not in any gray list). */ static void atomic2gen (lua_State *L, global_State *g) { cleargraylists(g); /* sweep all elements making them old */ g->gcstate = GCSswpallgc; sweep2old(L, &g->allgc); /* everything alive now is old */ g->reallyold = g->old1 = g->survival = g->allgc; g->firstold1 = NULL; /* there are no OLD1 objects anywhere */ /* repeat for 'finobj' lists */ sweep2old(L, &g->finobj); g->finobjrold = g->finobjold1 = g->finobjsur = g->finobj; sweep2old(L, &g->tobefnz); g->gckind = KGC_GEN; g->lastatomic = 0; g->GCestimate = gettotalbytes(g); /* base for memory control */ finishgencycle(L, g); } /* ** Enter generational mode. Must go until the end of an atomic cycle ** to ensure that all objects are correctly marked and weak tables ** are cleared. Then, turn all objects into old and finishes the ** collection. */ static lu_mem entergen (lua_State *L, global_State *g) { lu_mem numobjs; luaC_runtilstate(L, bitmask(GCSpause)); /* prepare to start a new cycle */ luaC_runtilstate(L, bitmask(GCSpropagate)); /* start new cycle */ numobjs = atomic(L); /* propagates all and then do the atomic stuff */ atomic2gen(L, g); return numobjs; } /* ** Enter incremental mode. Turn all objects white, make all ** intermediate lists point to NULL (to avoid invalid pointers), ** and go to the pause state. */ static void enterinc (global_State *g) { whitelist(g, g->allgc); g->reallyold = g->old1 = g->survival = NULL; whitelist(g, g->finobj); whitelist(g, g->tobefnz); g->finobjrold = g->finobjold1 = g->finobjsur = NULL; g->gcstate = GCSpause; g->gckind = KGC_INC; g->lastatomic = 0; } /* ** Change collector mode to 'newmode'. */ void luaC_changemode (lua_State *L, int newmode) { global_State *g = G(L); if (newmode != g->gckind) { if (newmode == KGC_GEN) /* entering generational mode? */ entergen(L, g); else enterinc(g); /* entering incremental mode */ } g->lastatomic = 0; } /* ** Does a full collection in generational mode. */ static lu_mem fullgen (lua_State *L, global_State *g) { enterinc(g); return entergen(L, g); } /* ** Set debt for the next minor collection, which will happen when ** memory grows 'genminormul'%. */ static void setminordebt (global_State *g) { luaE_setdebt(g, -(cast(l_mem, (gettotalbytes(g) / 100)) * g->genminormul)); } /* ** Does a major collection after last collection was a "bad collection". ** ** When the program is building a big structure, it allocates lots of ** memory but generates very little garbage. In those scenarios, ** the generational mode just wastes time doing small collections, and ** major collections are frequently what we call a "bad collection", a ** collection that frees too few objects. To avoid the cost of switching ** between generational mode and the incremental mode needed for full ** (major) collections, the collector tries to stay in incremental mode ** after a bad collection, and to switch back to generational mode only ** after a "good" collection (one that traverses less than 9/8 objects ** of the previous one). ** The collector must choose whether to stay in incremental mode or to ** switch back to generational mode before sweeping. At this point, it ** does not know the real memory in use, so it cannot use memory to ** decide whether to return to generational mode. Instead, it uses the ** number of objects traversed (returned by 'atomic') as a proxy. The ** field 'g->lastatomic' keeps this count from the last collection. ** ('g->lastatomic != 0' also means that the last collection was bad.) */ static void stepgenfull (lua_State *L, global_State *g) { lu_mem newatomic; /* count of traversed objects */ lu_mem lastatomic = g->lastatomic; /* count from last collection */ if (g->gckind == KGC_GEN) /* still in generational mode? */ enterinc(g); /* enter incremental mode */ luaC_runtilstate(L, bitmask(GCSpropagate)); /* start new cycle */ newatomic = atomic(L); /* mark everybody */ if (newatomic < lastatomic + (lastatomic >> 3)) { /* good collection? */ atomic2gen(L, g); /* return to generational mode */ setminordebt(g); } else { /* another bad collection; stay in incremental mode */ g->GCestimate = gettotalbytes(g); /* first estimate */; entersweep(L); luaC_runtilstate(L, bitmask(GCSpause)); /* finish collection */ setpause(g); g->lastatomic = newatomic; } } /* ** Does a generational "step". ** Usually, this means doing a minor collection and setting the debt to ** make another collection when memory grows 'genminormul'% larger. ** ** However, there are exceptions. If memory grows 'genmajormul'% ** larger than it was at the end of the last major collection (kept ** in 'g->GCestimate'), the function does a major collection. At the ** end, it checks whether the major collection was able to free a ** decent amount of memory (at least half the growth in memory since ** previous major collection). If so, the collector keeps its state, ** and the next collection will probably be minor again. Otherwise, ** we have what we call a "bad collection". In that case, set the field ** 'g->lastatomic' to signal that fact, so that the next collection will ** go to 'stepgenfull'. ** ** 'GCdebt <= 0' means an explicit call to GC step with "size" zero; ** in that case, do a minor collection. */ static void genstep (lua_State *L, global_State *g) { if (g->lastatomic != 0) /* last collection was a bad one? */ stepgenfull(L, g); /* do a full step */ else { lu_mem majorbase = g->GCestimate; /* memory after last major collection */ lu_mem majorinc = (majorbase / 100) * getgcparam(g->genmajormul); if (g->GCdebt > 0 && gettotalbytes(g) > majorbase + majorinc) { lu_mem numobjs = fullgen(L, g); /* do a major collection */ if (gettotalbytes(g) < majorbase + (majorinc / 2)) { /* collected at least half of memory growth since last major collection; keep doing minor collections */ setminordebt(g); } else { /* bad collection */ g->lastatomic = numobjs; /* signal that last collection was bad */ setpause(g); /* do a long wait for next (major) collection */ } } else { /* regular case; do a minor collection */ youngcollection(L, g); setminordebt(g); g->GCestimate = majorbase; /* preserve base value */ } } lua_assert(isdecGCmodegen(g)); } /* }====================================================== */ /* ** {====================================================== ** GC control ** ======================================================= */ /* ** Set the "time" to wait before starting a new GC cycle; cycle will ** start when memory use hits the threshold of ('estimate' * pause / ** PAUSEADJ). (Division by 'estimate' should be OK: it cannot be zero, ** because Lua cannot even start with less than PAUSEADJ bytes). */ static void setpause (global_State *g) { l_mem threshold, debt; int pause = getgcparam(g->gcpause); l_mem estimate = g->GCestimate / PAUSEADJ; /* adjust 'estimate' */ lua_assert(estimate > 0); threshold = (pause < MAX_LMEM / estimate) /* overflow? */ ? estimate * pause /* no overflow */ : MAX_LMEM; /* overflow; truncate to maximum */ debt = gettotalbytes(g) - threshold; if (debt > 0) debt = 0; luaE_setdebt(g, debt); } /* ** Enter first sweep phase. ** The call to 'sweeptolive' makes the pointer point to an object ** inside the list (instead of to the header), so that the real sweep do ** not need to skip objects created between "now" and the start of the ** real sweep. */ static void entersweep (lua_State *L) { global_State *g = G(L); g->gcstate = GCSswpallgc; lua_assert(g->sweepgc == NULL); g->sweepgc = sweeptolive(L, &g->allgc); } /* ** Delete all objects in list 'p' until (but not including) object ** 'limit'. */ static void deletelist (lua_State *L, GCObject *p, GCObject *limit) { while (p != limit) { GCObject *next = p->next; freeobj(L, p); p = next; } } /* ** Call all finalizers of the objects in the given Lua state, and ** then free all objects, except for the main thread. */ void luaC_freeallobjects (lua_State *L) { global_State *g = G(L); g->gcstp = GCSTPCLS; /* no extra finalizers after here */ luaC_changemode(L, KGC_INC); separatetobefnz(g, 1); /* separate all objects with finalizers */ lua_assert(g->finobj == NULL); callallpendingfinalizers(L); deletelist(L, g->allgc, obj2gco(g->mainthread)); lua_assert(g->finobj == NULL); /* no new finalizers */ deletelist(L, g->fixedgc, NULL); /* collect fixed objects */ lua_assert(g->strt.nuse == 0); } static lu_mem atomic (lua_State *L) { global_State *g = G(L); lu_mem work = 0; GCObject *origweak, *origall; GCObject *grayagain = g->grayagain; /* save original list */ g->grayagain = NULL; lua_assert(g->ephemeron == NULL && g->weak == NULL); lua_assert(!iswhite(g->mainthread)); g->gcstate = GCSatomic; markobject(g, L); /* mark running thread */ /* registry and global metatables may be changed by API */ markvalue(g, &g->l_registry); markmt(g); /* mark global metatables */ work += propagateall(g); /* empties 'gray' list */ /* remark occasional upvalues of (maybe) dead threads */ work += remarkupvals(g); work += propagateall(g); /* propagate changes */ g->gray = grayagain; work += propagateall(g); /* traverse 'grayagain' list */ convergeephemerons(g); /* at this point, all strongly accessible objects are marked. */ /* Clear values from weak tables, before checking finalizers */ clearbyvalues(g, g->weak, NULL); clearbyvalues(g, g->allweak, NULL); origweak = g->weak; origall = g->allweak; separatetobefnz(g, 0); /* separate objects to be finalized */ work += markbeingfnz(g); /* mark objects that will be finalized */ work += propagateall(g); /* remark, to propagate 'resurrection' */ convergeephemerons(g); /* at this point, all resurrected objects are marked. */ /* remove dead objects from weak tables */ clearbykeys(g, g->ephemeron); /* clear keys from all ephemeron tables */ clearbykeys(g, g->allweak); /* clear keys from all 'allweak' tables */ /* clear values from resurrected weak tables */ clearbyvalues(g, g->weak, origweak); clearbyvalues(g, g->allweak, origall); luaS_clearcache(g); g->currentwhite = cast_byte(otherwhite(g)); /* flip current white */ lua_assert(g->gray == NULL); return work; /* estimate of slots marked by 'atomic' */ } static int sweepstep (lua_State *L, global_State *g, int nextstate, GCObject **nextlist) { if (g->sweepgc) { l_mem olddebt = g->GCdebt; int count; g->sweepgc = sweeplist(L, g->sweepgc, GCSWEEPMAX, &count); g->GCestimate += g->GCdebt - olddebt; /* update estimate */ return count; } else { /* enter next state */ g->gcstate = nextstate; g->sweepgc = nextlist; return 0; /* no work done */ } } static lu_mem singlestep (lua_State *L) { global_State *g = G(L); lu_mem work; lua_assert(!g->gcstopem); /* collector is not reentrant */ g->gcstopem = 1; /* no emergency collections while collecting */ switch (g->gcstate) { case GCSpause: { restartcollection(g); g->gcstate = GCSpropagate; work = 1; break; } case GCSpropagate: { if (g->gray == NULL) { /* no more gray objects? */ g->gcstate = GCSenteratomic; /* finish propagate phase */ work = 0; } else work = propagatemark(g); /* traverse one gray object */ break; } case GCSenteratomic: { work = atomic(L); /* work is what was traversed by 'atomic' */ entersweep(L); g->GCestimate = gettotalbytes(g); /* first estimate */; break; } case GCSswpallgc: { /* sweep "regular" objects */ work = sweepstep(L, g, GCSswpfinobj, &g->finobj); break; } case GCSswpfinobj: { /* sweep objects with finalizers */ work = sweepstep(L, g, GCSswptobefnz, &g->tobefnz); break; } case GCSswptobefnz: { /* sweep objects to be finalized */ work = sweepstep(L, g, GCSswpend, NULL); break; } case GCSswpend: { /* finish sweeps */ checkSizes(L, g); g->gcstate = GCScallfin; work = 0; break; } case GCScallfin: { /* call remaining finalizers */ if (g->tobefnz && !g->gcemergency) { g->gcstopem = 0; /* ok collections during finalizers */ work = runafewfinalizers(L, GCFINMAX) * GCFINALIZECOST; } else { /* emergency mode or no more finalizers */ g->gcstate = GCSpause; /* finish collection */ work = 0; } break; } default: lua_assert(0); return 0; } g->gcstopem = 0; return work; } /* ** advances the garbage collector until it reaches a state allowed ** by 'statemask' */ void luaC_runtilstate (lua_State *L, int statesmask) { global_State *g = G(L); while (!testbit(statesmask, g->gcstate)) singlestep(L); } /* ** Performs a basic incremental step. The debt and step size are ** converted from bytes to "units of work"; then the function loops ** running single steps until adding that many units of work or ** finishing a cycle (pause state). Finally, it sets the debt that ** controls when next step will be performed. */ static void incstep (lua_State *L, global_State *g) { int stepmul = (getgcparam(g->gcstepmul) | 1); /* avoid division by 0 */ l_mem debt = (g->GCdebt / WORK2MEM) * stepmul; l_mem stepsize = (g->gcstepsize <= log2maxs(l_mem)) ? ((cast(l_mem, 1) << g->gcstepsize) / WORK2MEM) * stepmul : MAX_LMEM; /* overflow; keep maximum value */ do { /* repeat until pause or enough "credit" (negative debt) */ lu_mem work = singlestep(L); /* perform one single step */ debt -= work; } while (debt > -stepsize && g->gcstate != GCSpause); if (g->gcstate == GCSpause) setpause(g); /* pause until next cycle */ else { debt = (debt / stepmul) * WORK2MEM; /* convert 'work units' to bytes */ luaE_setdebt(g, debt); } } /* ** performs a basic GC step if collector is running */ void luaC_step (lua_State *L) { global_State *g = G(L); lua_assert(!g->gcemergency); if (gcrunning(g)) { /* running? */ if(isdecGCmodegen(g)) genstep(L, g); else incstep(L, g); } } /* ** Perform a full collection in incremental mode. ** Before running the collection, check 'keepinvariant'; if it is true, ** there may be some objects marked as black, so the collector has ** to sweep all objects to turn them back to white (as white has not ** changed, nothing will be collected). */ static void fullinc (lua_State *L, global_State *g) { if (keepinvariant(g)) /* black objects? */ entersweep(L); /* sweep everything to turn them back to white */ /* finish any pending sweep phase to start a new cycle */ luaC_runtilstate(L, bitmask(GCSpause)); luaC_runtilstate(L, bitmask(GCScallfin)); /* run up to finalizers */ /* estimate must be correct after a full GC cycle */ lua_assert(g->GCestimate == gettotalbytes(g)); luaC_runtilstate(L, bitmask(GCSpause)); /* finish collection */ setpause(g); } /* ** Performs a full GC cycle; if 'isemergency', set a flag to avoid ** some operations which could change the interpreter state in some ** unexpected ways (running finalizers and shrinking some structures). */ void luaC_fullgc (lua_State *L, int isemergency) { global_State *g = G(L); lua_assert(!g->gcemergency); g->gcemergency = isemergency; /* set flag */ if (g->gckind == KGC_INC) fullinc(L, g); else fullgen(L, g); g->gcemergency = 0; } /* }====================================================== */ /* ** $Id: llex.c $ ** Lexical Analyzer ** See Copyright Notice in lua.h */ #define llex_c #define LUA_CORE /*#include "lprefix.h"*/ #include #include /*#include "lua.h"*/ /*#include "lctype.h"*/ /*#include "ldebug.h"*/ /*#include "ldo.h"*/ /*#include "lgc.h"*/ /*#include "llex.h"*/ /*#include "lobject.h"*/ /*#include "lparser.h"*/ /*#include "lstate.h"*/ /*#include "lstring.h"*/ /*#include "ltable.h"*/ /*#include "lzio.h"*/ #define next(ls) (ls->current = zgetc(ls->z)) #define currIsNewline(ls) (ls->current == '\n' || ls->current == '\r') /* ORDER RESERVED */ static const char *const luaX_tokens [] = { "and", "break", "do", "else", "elseif", "end", "false", "for", "function", "goto", "if", "in", "local", "nil", "not", "or", "repeat", "return", "then", "true", "until", "while", "//", "..", "...", "==", ">=", "<=", "~=", "<<", ">>", "::", "", "", "", "", "" }; #define save_and_next(ls) (save(ls, ls->current), next(ls)) static l_noret lexerror (LexState *ls, const char *msg, int token); static void save (LexState *ls, int c) { Mbuffer *b = ls->buff; if (luaZ_bufflen(b) + 1 > luaZ_sizebuffer(b)) { size_t newsize; if (luaZ_sizebuffer(b) >= MAX_SIZE/2) lexerror(ls, "lexical element too long", 0); newsize = luaZ_sizebuffer(b) * 2; luaZ_resizebuffer(ls->L, b, newsize); } b->buffer[luaZ_bufflen(b)++] = cast_char(c); } void luaX_init (lua_State *L) { int i; TString *e = luaS_newliteral(L, LUA_ENV); /* create env name */ luaC_fix(L, obj2gco(e)); /* never collect this name */ for (i=0; iextra = cast_byte(i+1); /* reserved word */ } } const char *luaX_token2str (LexState *ls, int token) { if (token < FIRST_RESERVED) { /* single-byte symbols? */ if (lisprint(token)) return luaO_pushfstring(ls->L, "'%c'", token); else /* control character */ return luaO_pushfstring(ls->L, "'<\\%d>'", token); } else { const char *s = luaX_tokens[token - FIRST_RESERVED]; if (token < TK_EOS) /* fixed format (symbols and reserved words)? */ return luaO_pushfstring(ls->L, "'%s'", s); else /* names, strings, and numerals */ return s; } } static const char *txtToken (LexState *ls, int token) { switch (token) { case TK_NAME: case TK_STRING: case TK_FLT: case TK_INT: save(ls, '\0'); return luaO_pushfstring(ls->L, "'%s'", luaZ_buffer(ls->buff)); default: return luaX_token2str(ls, token); } } static l_noret lexerror (LexState *ls, const char *msg, int token) { msg = luaG_addinfo(ls->L, msg, ls->source, ls->linenumber); if (token) luaO_pushfstring(ls->L, "%s near %s", msg, txtToken(ls, token)); luaD_throw(ls->L, LUA_ERRSYNTAX); } l_noret luaX_syntaxerror (LexState *ls, const char *msg) { lexerror(ls, msg, ls->t.token); } /* ** Creates a new string and anchors it in scanner's table so that it ** will not be collected until the end of the compilation; by that time ** it should be anchored somewhere. It also internalizes long strings, ** ensuring there is only one copy of each unique string. The table ** here is used as a set: the string enters as the key, while its value ** is irrelevant. We use the string itself as the value only because it ** is a TValue readly available. Later, the code generation can change ** this value. */ TString *luaX_newstring (LexState *ls, const char *str, size_t l) { lua_State *L = ls->L; TString *ts = luaS_newlstr(L, str, l); /* create new string */ const TValue *o = luaH_getstr(ls->h, ts); if (!ttisnil(o)) /* string already present? */ ts = keystrval(nodefromval(o)); /* get saved copy */ else { /* not in use yet */ TValue *stv = s2v(L->top++); /* reserve stack space for string */ setsvalue(L, stv, ts); /* temporarily anchor the string */ luaH_finishset(L, ls->h, stv, o, stv); /* t[string] = string */ /* table is not a metatable, so it does not need to invalidate cache */ luaC_checkGC(L); L->top--; /* remove string from stack */ } return ts; } /* ** increment line number and skips newline sequence (any of ** \n, \r, \n\r, or \r\n) */ static void inclinenumber (LexState *ls) { int old = ls->current; lua_assert(currIsNewline(ls)); next(ls); /* skip '\n' or '\r' */ if (currIsNewline(ls) && ls->current != old) next(ls); /* skip '\n\r' or '\r\n' */ if (++ls->linenumber >= MAX_INT) lexerror(ls, "chunk has too many lines", 0); } void luaX_setinput (lua_State *L, LexState *ls, ZIO *z, TString *source, int firstchar) { ls->t.token = 0; ls->L = L; ls->current = firstchar; ls->lookahead.token = TK_EOS; /* no look-ahead token */ ls->z = z; ls->fs = NULL; ls->linenumber = 1; ls->lastline = 1; ls->source = source; ls->envn = luaS_newliteral(L, LUA_ENV); /* get env name */ luaZ_resizebuffer(ls->L, ls->buff, LUA_MINBUFFER); /* initialize buffer */ } /* ** ======================================================= ** LEXICAL ANALYZER ** ======================================================= */ static int check_next1 (LexState *ls, int c) { if (ls->current == c) { next(ls); return 1; } else return 0; } /* ** Check whether current char is in set 'set' (with two chars) and ** saves it */ static int check_next2 (LexState *ls, const char *set) { lua_assert(set[2] == '\0'); if (ls->current == set[0] || ls->current == set[1]) { save_and_next(ls); return 1; } else return 0; } /* LUA_NUMBER */ /* ** This function is quite liberal in what it accepts, as 'luaO_str2num' ** will reject ill-formed numerals. Roughly, it accepts the following ** pattern: ** ** %d(%x|%.|([Ee][+-]?))* | 0[Xx](%x|%.|([Pp][+-]?))* ** ** The only tricky part is to accept [+-] only after a valid exponent ** mark, to avoid reading '3-4' or '0xe+1' as a single number. ** ** The caller might have already read an initial dot. */ static int read_numeral (LexState *ls, SemInfo *seminfo) { TValue obj; const char *expo = "Ee"; int first = ls->current; lua_assert(lisdigit(ls->current)); save_and_next(ls); if (first == '0' && check_next2(ls, "xX")) /* hexadecimal? */ expo = "Pp"; for (;;) { if (check_next2(ls, expo)) /* exponent mark? */ check_next2(ls, "-+"); /* optional exponent sign */ else if (lisxdigit(ls->current) || ls->current == '.') /* '%x|%.' */ save_and_next(ls); else break; } if (lislalpha(ls->current)) /* is numeral touching a letter? */ save_and_next(ls); /* force an error */ save(ls, '\0'); if (luaO_str2num(luaZ_buffer(ls->buff), &obj) == 0) /* format error? */ lexerror(ls, "malformed number", TK_FLT); if (ttisinteger(&obj)) { seminfo->i = ivalue(&obj); return TK_INT; } else { lua_assert(ttisfloat(&obj)); seminfo->r = fltvalue(&obj); return TK_FLT; } } /* ** read a sequence '[=*[' or ']=*]', leaving the last bracket. If ** sequence is well formed, return its number of '='s + 2; otherwise, ** return 1 if it is a single bracket (no '='s and no 2nd bracket); ** otherwise (an unfinished '[==...') return 0. */ static size_t skip_sep (LexState *ls) { size_t count = 0; int s = ls->current; lua_assert(s == '[' || s == ']'); save_and_next(ls); while (ls->current == '=') { save_and_next(ls); count++; } return (ls->current == s) ? count + 2 : (count == 0) ? 1 : 0; } static void read_long_string (LexState *ls, SemInfo *seminfo, size_t sep) { int line = ls->linenumber; /* initial line (for error message) */ save_and_next(ls); /* skip 2nd '[' */ if (currIsNewline(ls)) /* string starts with a newline? */ inclinenumber(ls); /* skip it */ for (;;) { switch (ls->current) { case EOZ: { /* error */ const char *what = (seminfo ? "string" : "comment"); const char *msg = luaO_pushfstring(ls->L, "unfinished long %s (starting at line %d)", what, line); lexerror(ls, msg, TK_EOS); break; /* to avoid warnings */ } case ']': { if (skip_sep(ls) == sep) { save_and_next(ls); /* skip 2nd ']' */ goto endloop; } break; } case '\n': case '\r': { save(ls, '\n'); inclinenumber(ls); if (!seminfo) luaZ_resetbuffer(ls->buff); /* avoid wasting space */ break; } default: { if (seminfo) save_and_next(ls); else next(ls); } } } endloop: if (seminfo) seminfo->ts = luaX_newstring(ls, luaZ_buffer(ls->buff) + sep, luaZ_bufflen(ls->buff) - 2 * sep); } static void esccheck (LexState *ls, int c, const char *msg) { if (!c) { if (ls->current != EOZ) save_and_next(ls); /* add current to buffer for error message */ lexerror(ls, msg, TK_STRING); } } static int gethexa (LexState *ls) { save_and_next(ls); esccheck (ls, lisxdigit(ls->current), "hexadecimal digit expected"); return luaO_hexavalue(ls->current); } static int readhexaesc (LexState *ls) { int r = gethexa(ls); r = (r << 4) + gethexa(ls); luaZ_buffremove(ls->buff, 2); /* remove saved chars from buffer */ return r; } static unsigned long readutf8esc (LexState *ls) { unsigned long r; int i = 4; /* chars to be removed: '\', 'u', '{', and first digit */ save_and_next(ls); /* skip 'u' */ esccheck(ls, ls->current == '{', "missing '{'"); r = gethexa(ls); /* must have at least one digit */ while (cast_void(save_and_next(ls)), lisxdigit(ls->current)) { i++; esccheck(ls, r <= (0x7FFFFFFFu >> 4), "UTF-8 value too large"); r = (r << 4) + luaO_hexavalue(ls->current); } esccheck(ls, ls->current == '}', "missing '}'"); next(ls); /* skip '}' */ luaZ_buffremove(ls->buff, i); /* remove saved chars from buffer */ return r; } static void utf8esc (LexState *ls) { char buff[UTF8BUFFSZ]; int n = luaO_utf8esc(buff, readutf8esc(ls)); for (; n > 0; n--) /* add 'buff' to string */ save(ls, buff[UTF8BUFFSZ - n]); } static int readdecesc (LexState *ls) { int i; int r = 0; /* result accumulator */ for (i = 0; i < 3 && lisdigit(ls->current); i++) { /* read up to 3 digits */ r = 10*r + ls->current - '0'; save_and_next(ls); } esccheck(ls, r <= UCHAR_MAX, "decimal escape too large"); luaZ_buffremove(ls->buff, i); /* remove read digits from buffer */ return r; } static void read_string (LexState *ls, int del, SemInfo *seminfo) { save_and_next(ls); /* keep delimiter (for error messages) */ while (ls->current != del) { switch (ls->current) { case EOZ: lexerror(ls, "unfinished string", TK_EOS); break; /* to avoid warnings */ case '\n': case '\r': lexerror(ls, "unfinished string", TK_STRING); break; /* to avoid warnings */ case '\\': { /* escape sequences */ int c; /* final character to be saved */ save_and_next(ls); /* keep '\\' for error messages */ switch (ls->current) { case 'a': c = '\a'; goto read_save; case 'b': c = '\b'; goto read_save; case 'f': c = '\f'; goto read_save; case 'n': c = '\n'; goto read_save; case 'r': c = '\r'; goto read_save; case 't': c = '\t'; goto read_save; case 'v': c = '\v'; goto read_save; case 'x': c = readhexaesc(ls); goto read_save; case 'u': utf8esc(ls); goto no_save; case '\n': case '\r': inclinenumber(ls); c = '\n'; goto only_save; case '\\': case '\"': case '\'': c = ls->current; goto read_save; case EOZ: goto no_save; /* will raise an error next loop */ case 'z': { /* zap following span of spaces */ luaZ_buffremove(ls->buff, 1); /* remove '\\' */ next(ls); /* skip the 'z' */ while (lisspace(ls->current)) { if (currIsNewline(ls)) inclinenumber(ls); else next(ls); } goto no_save; } default: { esccheck(ls, lisdigit(ls->current), "invalid escape sequence"); c = readdecesc(ls); /* digital escape '\ddd' */ goto only_save; } } read_save: next(ls); /* go through */ only_save: luaZ_buffremove(ls->buff, 1); /* remove '\\' */ save(ls, c); /* go through */ no_save: break; } default: save_and_next(ls); } } save_and_next(ls); /* skip delimiter */ seminfo->ts = luaX_newstring(ls, luaZ_buffer(ls->buff) + 1, luaZ_bufflen(ls->buff) - 2); } static int llex (LexState *ls, SemInfo *seminfo) { luaZ_resetbuffer(ls->buff); for (;;) { switch (ls->current) { case '\n': case '\r': { /* line breaks */ inclinenumber(ls); break; } case ' ': case '\f': case '\t': case '\v': { /* spaces */ next(ls); break; } case '-': { /* '-' or '--' (comment) */ next(ls); if (ls->current != '-') return '-'; /* else is a comment */ next(ls); if (ls->current == '[') { /* long comment? */ size_t sep = skip_sep(ls); luaZ_resetbuffer(ls->buff); /* 'skip_sep' may dirty the buffer */ if (sep >= 2) { read_long_string(ls, NULL, sep); /* skip long comment */ luaZ_resetbuffer(ls->buff); /* previous call may dirty the buff. */ break; } } /* else short comment */ while (!currIsNewline(ls) && ls->current != EOZ) next(ls); /* skip until end of line (or end of file) */ break; } case '[': { /* long string or simply '[' */ size_t sep = skip_sep(ls); if (sep >= 2) { read_long_string(ls, seminfo, sep); return TK_STRING; } else if (sep == 0) /* '[=...' missing second bracket? */ lexerror(ls, "invalid long string delimiter", TK_STRING); return '['; } case '=': { next(ls); if (check_next1(ls, '=')) return TK_EQ; /* '==' */ else return '='; } case '<': { next(ls); if (check_next1(ls, '=')) return TK_LE; /* '<=' */ else if (check_next1(ls, '<')) return TK_SHL; /* '<<' */ else return '<'; } case '>': { next(ls); if (check_next1(ls, '=')) return TK_GE; /* '>=' */ else if (check_next1(ls, '>')) return TK_SHR; /* '>>' */ else return '>'; } case '/': { next(ls); if (check_next1(ls, '/')) return TK_IDIV; /* '//' */ else return '/'; } case '~': { next(ls); if (check_next1(ls, '=')) return TK_NE; /* '~=' */ else return '~'; } case ':': { next(ls); if (check_next1(ls, ':')) return TK_DBCOLON; /* '::' */ else return ':'; } case '"': case '\'': { /* short literal strings */ read_string(ls, ls->current, seminfo); return TK_STRING; } case '.': { /* '.', '..', '...', or number */ save_and_next(ls); if (check_next1(ls, '.')) { if (check_next1(ls, '.')) return TK_DOTS; /* '...' */ else return TK_CONCAT; /* '..' */ } else if (!lisdigit(ls->current)) return '.'; else return read_numeral(ls, seminfo); } case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': { return read_numeral(ls, seminfo); } case EOZ: { return TK_EOS; } default: { if (lislalpha(ls->current)) { /* identifier or reserved word? */ TString *ts; do { save_and_next(ls); } while (lislalnum(ls->current)); ts = luaX_newstring(ls, luaZ_buffer(ls->buff), luaZ_bufflen(ls->buff)); seminfo->ts = ts; if (isreserved(ts)) /* reserved word? */ return ts->extra - 1 + FIRST_RESERVED; else { return TK_NAME; } } else { /* single-char tokens ('+', '*', '%', '{', '}', ...) */ int c = ls->current; next(ls); return c; } } } } } void luaX_next (LexState *ls) { ls->lastline = ls->linenumber; if (ls->lookahead.token != TK_EOS) { /* is there a look-ahead token? */ ls->t = ls->lookahead; /* use this one */ ls->lookahead.token = TK_EOS; /* and discharge it */ } else ls->t.token = llex(ls, &ls->t.seminfo); /* read next token */ } int luaX_lookahead (LexState *ls) { lua_assert(ls->lookahead.token == TK_EOS); ls->lookahead.token = llex(ls, &ls->lookahead.seminfo); return ls->lookahead.token; } /* ** $Id: lcode.c $ ** Code generator for Lua ** See Copyright Notice in lua.h */ #define lcode_c #define LUA_CORE /*#include "lprefix.h"*/ #include #include #include #include /*#include "lua.h"*/ /*#include "lcode.h"*/ /*#include "ldebug.h"*/ /*#include "ldo.h"*/ /*#include "lgc.h"*/ /*#include "llex.h"*/ /*#include "lmem.h"*/ /*#include "lobject.h"*/ /*#include "lopcodes.h"*/ /*#include "lparser.h"*/ /*#include "lstring.h"*/ /*#include "ltable.h"*/ /*#include "lvm.h"*/ /* Maximum number of registers in a Lua function (must fit in 8 bits) */ #define MAXREGS 255 #define hasjumps(e) ((e)->t != (e)->f) static int codesJ (FuncState *fs, OpCode o, int sj, int k); /* semantic error */ l_noret luaK_semerror (LexState *ls, const char *msg) { ls->t.token = 0; /* remove "near " from final message */ luaX_syntaxerror(ls, msg); } /* ** If expression is a numeric constant, fills 'v' with its value ** and returns 1. Otherwise, returns 0. */ static int tonumeral (const expdesc *e, TValue *v) { if (hasjumps(e)) return 0; /* not a numeral */ switch (e->k) { case VKINT: if (v) setivalue(v, e->u.ival); return 1; case VKFLT: if (v) setfltvalue(v, e->u.nval); return 1; default: return 0; } } /* ** Get the constant value from a constant expression */ static TValue *const2val (FuncState *fs, const expdesc *e) { lua_assert(e->k == VCONST); return &fs->ls->dyd->actvar.arr[e->u.info].k; } /* ** If expression is a constant, fills 'v' with its value ** and returns 1. Otherwise, returns 0. */ int luaK_exp2const (FuncState *fs, const expdesc *e, TValue *v) { if (hasjumps(e)) return 0; /* not a constant */ switch (e->k) { case VFALSE: setbfvalue(v); return 1; case VTRUE: setbtvalue(v); return 1; case VNIL: setnilvalue(v); return 1; case VKSTR: { setsvalue(fs->ls->L, v, e->u.strval); return 1; } case VCONST: { setobj(fs->ls->L, v, const2val(fs, e)); return 1; } default: return tonumeral(e, v); } } /* ** Return the previous instruction of the current code. If there ** may be a jump target between the current instruction and the ** previous one, return an invalid instruction (to avoid wrong ** optimizations). */ static Instruction *previousinstruction (FuncState *fs) { static const Instruction invalidinstruction = ~(Instruction)0; if (fs->pc > fs->lasttarget) return &fs->f->code[fs->pc - 1]; /* previous instruction */ else return cast(Instruction*, &invalidinstruction); } /* ** Create a OP_LOADNIL instruction, but try to optimize: if the previous ** instruction is also OP_LOADNIL and ranges are compatible, adjust ** range of previous instruction instead of emitting a new one. (For ** instance, 'local a; local b' will generate a single opcode.) */ void luaK_nil (FuncState *fs, int from, int n) { int l = from + n - 1; /* last register to set nil */ Instruction *previous = previousinstruction(fs); if (GET_OPCODE(*previous) == OP_LOADNIL) { /* previous is LOADNIL? */ int pfrom = GETARG_A(*previous); /* get previous range */ int pl = pfrom + GETARG_B(*previous); if ((pfrom <= from && from <= pl + 1) || (from <= pfrom && pfrom <= l + 1)) { /* can connect both? */ if (pfrom < from) from = pfrom; /* from = min(from, pfrom) */ if (pl > l) l = pl; /* l = max(l, pl) */ SETARG_A(*previous, from); SETARG_B(*previous, l - from); return; } /* else go through */ } luaK_codeABC(fs, OP_LOADNIL, from, n - 1, 0); /* else no optimization */ } /* ** Gets the destination address of a jump instruction. Used to traverse ** a list of jumps. */ static int getjump (FuncState *fs, int pc) { int offset = GETARG_sJ(fs->f->code[pc]); if (offset == NO_JUMP) /* point to itself represents end of list */ return NO_JUMP; /* end of list */ else return (pc+1)+offset; /* turn offset into absolute position */ } /* ** Fix jump instruction at position 'pc' to jump to 'dest'. ** (Jump addresses are relative in Lua) */ static void fixjump (FuncState *fs, int pc, int dest) { Instruction *jmp = &fs->f->code[pc]; int offset = dest - (pc + 1); lua_assert(dest != NO_JUMP); if (!(-OFFSET_sJ <= offset && offset <= MAXARG_sJ - OFFSET_sJ)) luaX_syntaxerror(fs->ls, "control structure too long"); lua_assert(GET_OPCODE(*jmp) == OP_JMP); SETARG_sJ(*jmp, offset); } /* ** Concatenate jump-list 'l2' into jump-list 'l1' */ void luaK_concat (FuncState *fs, int *l1, int l2) { if (l2 == NO_JUMP) return; /* nothing to concatenate? */ else if (*l1 == NO_JUMP) /* no original list? */ *l1 = l2; /* 'l1' points to 'l2' */ else { int list = *l1; int next; while ((next = getjump(fs, list)) != NO_JUMP) /* find last element */ list = next; fixjump(fs, list, l2); /* last element links to 'l2' */ } } /* ** Create a jump instruction and return its position, so its destination ** can be fixed later (with 'fixjump'). */ int luaK_jump (FuncState *fs) { return codesJ(fs, OP_JMP, NO_JUMP, 0); } /* ** Code a 'return' instruction */ void luaK_ret (FuncState *fs, int first, int nret) { OpCode op; switch (nret) { case 0: op = OP_RETURN0; break; case 1: op = OP_RETURN1; break; default: op = OP_RETURN; break; } luaK_codeABC(fs, op, first, nret + 1, 0); } /* ** Code a "conditional jump", that is, a test or comparison opcode ** followed by a jump. Return jump position. */ static int condjump (FuncState *fs, OpCode op, int A, int B, int C, int k) { luaK_codeABCk(fs, op, A, B, C, k); return luaK_jump(fs); } /* ** returns current 'pc' and marks it as a jump target (to avoid wrong ** optimizations with consecutive instructions not in the same basic block). */ int luaK_getlabel (FuncState *fs) { fs->lasttarget = fs->pc; return fs->pc; } /* ** Returns the position of the instruction "controlling" a given ** jump (that is, its condition), or the jump itself if it is ** unconditional. */ static Instruction *getjumpcontrol (FuncState *fs, int pc) { Instruction *pi = &fs->f->code[pc]; if (pc >= 1 && testTMode(GET_OPCODE(*(pi-1)))) return pi-1; else return pi; } /* ** Patch destination register for a TESTSET instruction. ** If instruction in position 'node' is not a TESTSET, return 0 ("fails"). ** Otherwise, if 'reg' is not 'NO_REG', set it as the destination ** register. Otherwise, change instruction to a simple 'TEST' (produces ** no register value) */ static int patchtestreg (FuncState *fs, int node, int reg) { Instruction *i = getjumpcontrol(fs, node); if (GET_OPCODE(*i) != OP_TESTSET) return 0; /* cannot patch other instructions */ if (reg != NO_REG && reg != GETARG_B(*i)) SETARG_A(*i, reg); else { /* no register to put value or register already has the value; change instruction to simple test */ *i = CREATE_ABCk(OP_TEST, GETARG_B(*i), 0, 0, GETARG_k(*i)); } return 1; } /* ** Traverse a list of tests ensuring no one produces a value */ static void removevalues (FuncState *fs, int list) { for (; list != NO_JUMP; list = getjump(fs, list)) patchtestreg(fs, list, NO_REG); } /* ** Traverse a list of tests, patching their destination address and ** registers: tests producing values jump to 'vtarget' (and put their ** values in 'reg'), other tests jump to 'dtarget'. */ static void patchlistaux (FuncState *fs, int list, int vtarget, int reg, int dtarget) { while (list != NO_JUMP) { int next = getjump(fs, list); if (patchtestreg(fs, list, reg)) fixjump(fs, list, vtarget); else fixjump(fs, list, dtarget); /* jump to default target */ list = next; } } /* ** Path all jumps in 'list' to jump to 'target'. ** (The assert means that we cannot fix a jump to a forward address ** because we only know addresses once code is generated.) */ void luaK_patchlist (FuncState *fs, int list, int target) { lua_assert(target <= fs->pc); patchlistaux(fs, list, target, NO_REG, target); } void luaK_patchtohere (FuncState *fs, int list) { int hr = luaK_getlabel(fs); /* mark "here" as a jump target */ luaK_patchlist(fs, list, hr); } /* limit for difference between lines in relative line info. */ #define LIMLINEDIFF 0x80 /* ** Save line info for a new instruction. If difference from last line ** does not fit in a byte, of after that many instructions, save a new ** absolute line info; (in that case, the special value 'ABSLINEINFO' ** in 'lineinfo' signals the existence of this absolute information.) ** Otherwise, store the difference from last line in 'lineinfo'. */ static void savelineinfo (FuncState *fs, Proto *f, int line) { int linedif = line - fs->previousline; int pc = fs->pc - 1; /* last instruction coded */ if (abs(linedif) >= LIMLINEDIFF || fs->iwthabs++ >= MAXIWTHABS) { luaM_growvector(fs->ls->L, f->abslineinfo, fs->nabslineinfo, f->sizeabslineinfo, AbsLineInfo, MAX_INT, "lines"); f->abslineinfo[fs->nabslineinfo].pc = pc; f->abslineinfo[fs->nabslineinfo++].line = line; linedif = ABSLINEINFO; /* signal that there is absolute information */ fs->iwthabs = 1; /* restart counter */ } luaM_growvector(fs->ls->L, f->lineinfo, pc, f->sizelineinfo, ls_byte, MAX_INT, "opcodes"); f->lineinfo[pc] = linedif; fs->previousline = line; /* last line saved */ } /* ** Remove line information from the last instruction. ** If line information for that instruction is absolute, set 'iwthabs' ** above its max to force the new (replacing) instruction to have ** absolute line info, too. */ static void removelastlineinfo (FuncState *fs) { Proto *f = fs->f; int pc = fs->pc - 1; /* last instruction coded */ if (f->lineinfo[pc] != ABSLINEINFO) { /* relative line info? */ fs->previousline -= f->lineinfo[pc]; /* correct last line saved */ fs->iwthabs--; /* undo previous increment */ } else { /* absolute line information */ lua_assert(f->abslineinfo[fs->nabslineinfo - 1].pc == pc); fs->nabslineinfo--; /* remove it */ fs->iwthabs = MAXIWTHABS + 1; /* force next line info to be absolute */ } } /* ** Remove the last instruction created, correcting line information ** accordingly. */ static void removelastinstruction (FuncState *fs) { removelastlineinfo(fs); fs->pc--; } /* ** Emit instruction 'i', checking for array sizes and saving also its ** line information. Return 'i' position. */ int luaK_code (FuncState *fs, Instruction i) { Proto *f = fs->f; /* put new instruction in code array */ luaM_growvector(fs->ls->L, f->code, fs->pc, f->sizecode, Instruction, MAX_INT, "opcodes"); f->code[fs->pc++] = i; savelineinfo(fs, f, fs->ls->lastline); return fs->pc - 1; /* index of new instruction */ } /* ** Format and emit an 'iABC' instruction. (Assertions check consistency ** of parameters versus opcode.) */ int luaK_codeABCk (FuncState *fs, OpCode o, int a, int b, int c, int k) { lua_assert(getOpMode(o) == iABC); lua_assert(a <= MAXARG_A && b <= MAXARG_B && c <= MAXARG_C && (k & ~1) == 0); return luaK_code(fs, CREATE_ABCk(o, a, b, c, k)); } /* ** Format and emit an 'iABx' instruction. */ int luaK_codeABx (FuncState *fs, OpCode o, int a, unsigned int bc) { lua_assert(getOpMode(o) == iABx); lua_assert(a <= MAXARG_A && bc <= MAXARG_Bx); return luaK_code(fs, CREATE_ABx(o, a, bc)); } /* ** Format and emit an 'iAsBx' instruction. */ int luaK_codeAsBx (FuncState *fs, OpCode o, int a, int bc) { unsigned int b = bc + OFFSET_sBx; lua_assert(getOpMode(o) == iAsBx); lua_assert(a <= MAXARG_A && b <= MAXARG_Bx); return luaK_code(fs, CREATE_ABx(o, a, b)); } /* ** Format and emit an 'isJ' instruction. */ static int codesJ (FuncState *fs, OpCode o, int sj, int k) { unsigned int j = sj + OFFSET_sJ; lua_assert(getOpMode(o) == isJ); lua_assert(j <= MAXARG_sJ && (k & ~1) == 0); return luaK_code(fs, CREATE_sJ(o, j, k)); } /* ** Emit an "extra argument" instruction (format 'iAx') */ static int codeextraarg (FuncState *fs, int a) { lua_assert(a <= MAXARG_Ax); return luaK_code(fs, CREATE_Ax(OP_EXTRAARG, a)); } /* ** Emit a "load constant" instruction, using either 'OP_LOADK' ** (if constant index 'k' fits in 18 bits) or an 'OP_LOADKX' ** instruction with "extra argument". */ static int luaK_codek (FuncState *fs, int reg, int k) { if (k <= MAXARG_Bx) return luaK_codeABx(fs, OP_LOADK, reg, k); else { int p = luaK_codeABx(fs, OP_LOADKX, reg, 0); codeextraarg(fs, k); return p; } } /* ** Check register-stack level, keeping track of its maximum size ** in field 'maxstacksize' */ void luaK_checkstack (FuncState *fs, int n) { int newstack = fs->freereg + n; if (newstack > fs->f->maxstacksize) { if (newstack >= MAXREGS) luaX_syntaxerror(fs->ls, "function or expression needs too many registers"); fs->f->maxstacksize = cast_byte(newstack); } } /* ** Reserve 'n' registers in register stack */ void luaK_reserveregs (FuncState *fs, int n) { luaK_checkstack(fs, n); fs->freereg += n; } /* ** Free register 'reg', if it is neither a constant index nor ** a local variable. ) */ static void freereg (FuncState *fs, int reg) { if (reg >= luaY_nvarstack(fs)) { fs->freereg--; lua_assert(reg == fs->freereg); } } /* ** Free two registers in proper order */ static void freeregs (FuncState *fs, int r1, int r2) { if (r1 > r2) { freereg(fs, r1); freereg(fs, r2); } else { freereg(fs, r2); freereg(fs, r1); } } /* ** Free register used by expression 'e' (if any) */ static void freeexp (FuncState *fs, expdesc *e) { if (e->k == VNONRELOC) freereg(fs, e->u.info); } /* ** Free registers used by expressions 'e1' and 'e2' (if any) in proper ** order. */ static void freeexps (FuncState *fs, expdesc *e1, expdesc *e2) { int r1 = (e1->k == VNONRELOC) ? e1->u.info : -1; int r2 = (e2->k == VNONRELOC) ? e2->u.info : -1; freeregs(fs, r1, r2); } /* ** Add constant 'v' to prototype's list of constants (field 'k'). ** Use scanner's table to cache position of constants in constant list ** and try to reuse constants. Because some values should not be used ** as keys (nil cannot be a key, integer keys can collapse with float ** keys), the caller must provide a useful 'key' for indexing the cache. ** Note that all functions share the same table, so entering or exiting ** a function can make some indices wrong. */ static int addk (FuncState *fs, TValue *key, TValue *v) { TValue val; lua_State *L = fs->ls->L; Proto *f = fs->f; const TValue *idx = luaH_get(fs->ls->h, key); /* query scanner table */ int k, oldsize; if (ttisinteger(idx)) { /* is there an index there? */ k = cast_int(ivalue(idx)); /* correct value? (warning: must distinguish floats from integers!) */ if (k < fs->nk && ttypetag(&f->k[k]) == ttypetag(v) && luaV_rawequalobj(&f->k[k], v)) return k; /* reuse index */ } /* constant not found; create a new entry */ oldsize = f->sizek; k = fs->nk; /* numerical value does not need GC barrier; table has no metatable, so it does not need to invalidate cache */ setivalue(&val, k); luaH_finishset(L, fs->ls->h, key, idx, &val); luaM_growvector(L, f->k, k, f->sizek, TValue, MAXARG_Ax, "constants"); while (oldsize < f->sizek) setnilvalue(&f->k[oldsize++]); setobj(L, &f->k[k], v); fs->nk++; luaC_barrier(L, f, v); return k; } /* ** Add a string to list of constants and return its index. */ static int stringK (FuncState *fs, TString *s) { TValue o; setsvalue(fs->ls->L, &o, s); return addk(fs, &o, &o); /* use string itself as key */ } /* ** Add an integer to list of constants and return its index. */ static int luaK_intK (FuncState *fs, lua_Integer n) { TValue o; setivalue(&o, n); return addk(fs, &o, &o); /* use integer itself as key */ } /* ** Add a float to list of constants and return its index. Floats ** with integral values need a different key, to avoid collision ** with actual integers. To that, we add to the number its smaller ** power-of-two fraction that is still significant in its scale. ** For doubles, that would be 1/2^52. ** (This method is not bulletproof: there may be another float ** with that value, and for floats larger than 2^53 the result is ** still an integer. At worst, this only wastes an entry with ** a duplicate.) */ static int luaK_numberK (FuncState *fs, lua_Number r) { TValue o; lua_Integer ik; setfltvalue(&o, r); if (!luaV_flttointeger(r, &ik, F2Ieq)) /* not an integral value? */ return addk(fs, &o, &o); /* use number itself as key */ else { /* must build an alternative key */ const int nbm = l_floatatt(MANT_DIG); const lua_Number q = l_mathop(ldexp)(l_mathop(1.0), -nbm + 1); const lua_Number k = (ik == 0) ? q : r + r*q; /* new key */ TValue kv; setfltvalue(&kv, k); /* result is not an integral value, unless value is too large */ lua_assert(!luaV_flttointeger(k, &ik, F2Ieq) || l_mathop(fabs)(r) >= l_mathop(1e6)); return addk(fs, &kv, &o); } } /* ** Add a false to list of constants and return its index. */ static int boolF (FuncState *fs) { TValue o; setbfvalue(&o); return addk(fs, &o, &o); /* use boolean itself as key */ } /* ** Add a true to list of constants and return its index. */ static int boolT (FuncState *fs) { TValue o; setbtvalue(&o); return addk(fs, &o, &o); /* use boolean itself as key */ } /* ** Add nil to list of constants and return its index. */ static int nilK (FuncState *fs) { TValue k, v; setnilvalue(&v); /* cannot use nil as key; instead use table itself to represent nil */ sethvalue(fs->ls->L, &k, fs->ls->h); return addk(fs, &k, &v); } /* ** Check whether 'i' can be stored in an 'sC' operand. Equivalent to ** (0 <= int2sC(i) && int2sC(i) <= MAXARG_C) but without risk of ** overflows in the hidden addition inside 'int2sC'. */ static int fitsC (lua_Integer i) { return (l_castS2U(i) + OFFSET_sC <= cast_uint(MAXARG_C)); } /* ** Check whether 'i' can be stored in an 'sBx' operand. */ static int fitsBx (lua_Integer i) { return (-OFFSET_sBx <= i && i <= MAXARG_Bx - OFFSET_sBx); } void luaK_int (FuncState *fs, int reg, lua_Integer i) { if (fitsBx(i)) luaK_codeAsBx(fs, OP_LOADI, reg, cast_int(i)); else luaK_codek(fs, reg, luaK_intK(fs, i)); } static void luaK_float (FuncState *fs, int reg, lua_Number f) { lua_Integer fi; if (luaV_flttointeger(f, &fi, F2Ieq) && fitsBx(fi)) luaK_codeAsBx(fs, OP_LOADF, reg, cast_int(fi)); else luaK_codek(fs, reg, luaK_numberK(fs, f)); } /* ** Convert a constant in 'v' into an expression description 'e' */ static void const2exp (TValue *v, expdesc *e) { switch (ttypetag(v)) { case LUA_VNUMINT: e->k = VKINT; e->u.ival = ivalue(v); break; case LUA_VNUMFLT: e->k = VKFLT; e->u.nval = fltvalue(v); break; case LUA_VFALSE: e->k = VFALSE; break; case LUA_VTRUE: e->k = VTRUE; break; case LUA_VNIL: e->k = VNIL; break; case LUA_VSHRSTR: case LUA_VLNGSTR: e->k = VKSTR; e->u.strval = tsvalue(v); break; default: lua_assert(0); } } /* ** Fix an expression to return the number of results 'nresults'. ** 'e' must be a multi-ret expression (function call or vararg). */ void luaK_setreturns (FuncState *fs, expdesc *e, int nresults) { Instruction *pc = &getinstruction(fs, e); if (e->k == VCALL) /* expression is an open function call? */ SETARG_C(*pc, nresults + 1); else { lua_assert(e->k == VVARARG); SETARG_C(*pc, nresults + 1); SETARG_A(*pc, fs->freereg); luaK_reserveregs(fs, 1); } } /* ** Convert a VKSTR to a VK */ static void str2K (FuncState *fs, expdesc *e) { lua_assert(e->k == VKSTR); e->u.info = stringK(fs, e->u.strval); e->k = VK; } /* ** Fix an expression to return one result. ** If expression is not a multi-ret expression (function call or ** vararg), it already returns one result, so nothing needs to be done. ** Function calls become VNONRELOC expressions (as its result comes ** fixed in the base register of the call), while vararg expressions ** become VRELOC (as OP_VARARG puts its results where it wants). ** (Calls are created returning one result, so that does not need ** to be fixed.) */ void luaK_setoneret (FuncState *fs, expdesc *e) { if (e->k == VCALL) { /* expression is an open function call? */ /* already returns 1 value */ lua_assert(GETARG_C(getinstruction(fs, e)) == 2); e->k = VNONRELOC; /* result has fixed position */ e->u.info = GETARG_A(getinstruction(fs, e)); } else if (e->k == VVARARG) { SETARG_C(getinstruction(fs, e), 2); e->k = VRELOC; /* can relocate its simple result */ } } /* ** Ensure that expression 'e' is not a variable (nor a ). ** (Expression still may have jump lists.) */ void luaK_dischargevars (FuncState *fs, expdesc *e) { switch (e->k) { case VCONST: { const2exp(const2val(fs, e), e); break; } case VLOCAL: { /* already in a register */ e->u.info = e->u.var.ridx; e->k = VNONRELOC; /* becomes a non-relocatable value */ break; } case VUPVAL: { /* move value to some (pending) register */ e->u.info = luaK_codeABC(fs, OP_GETUPVAL, 0, e->u.info, 0); e->k = VRELOC; break; } case VINDEXUP: { e->u.info = luaK_codeABC(fs, OP_GETTABUP, 0, e->u.ind.t, e->u.ind.idx); e->k = VRELOC; break; } case VINDEXI: { freereg(fs, e->u.ind.t); e->u.info = luaK_codeABC(fs, OP_GETI, 0, e->u.ind.t, e->u.ind.idx); e->k = VRELOC; break; } case VINDEXSTR: { freereg(fs, e->u.ind.t); e->u.info = luaK_codeABC(fs, OP_GETFIELD, 0, e->u.ind.t, e->u.ind.idx); e->k = VRELOC; break; } case VINDEXED: { freeregs(fs, e->u.ind.t, e->u.ind.idx); e->u.info = luaK_codeABC(fs, OP_GETTABLE, 0, e->u.ind.t, e->u.ind.idx); e->k = VRELOC; break; } case VVARARG: case VCALL: { luaK_setoneret(fs, e); break; } default: break; /* there is one value available (somewhere) */ } } /* ** Ensure expression value is in register 'reg', making 'e' a ** non-relocatable expression. ** (Expression still may have jump lists.) */ static void discharge2reg (FuncState *fs, expdesc *e, int reg) { luaK_dischargevars(fs, e); switch (e->k) { case VNIL: { luaK_nil(fs, reg, 1); break; } case VFALSE: { luaK_codeABC(fs, OP_LOADFALSE, reg, 0, 0); break; } case VTRUE: { luaK_codeABC(fs, OP_LOADTRUE, reg, 0, 0); break; } case VKSTR: { str2K(fs, e); } /* FALLTHROUGH */ case VK: { luaK_codek(fs, reg, e->u.info); break; } case VKFLT: { luaK_float(fs, reg, e->u.nval); break; } case VKINT: { luaK_int(fs, reg, e->u.ival); break; } case VRELOC: { Instruction *pc = &getinstruction(fs, e); SETARG_A(*pc, reg); /* instruction will put result in 'reg' */ break; } case VNONRELOC: { if (reg != e->u.info) luaK_codeABC(fs, OP_MOVE, reg, e->u.info, 0); break; } default: { lua_assert(e->k == VJMP); return; /* nothing to do... */ } } e->u.info = reg; e->k = VNONRELOC; } /* ** Ensure expression value is in a register, making 'e' a ** non-relocatable expression. ** (Expression still may have jump lists.) */ static void discharge2anyreg (FuncState *fs, expdesc *e) { if (e->k != VNONRELOC) { /* no fixed register yet? */ luaK_reserveregs(fs, 1); /* get a register */ discharge2reg(fs, e, fs->freereg-1); /* put value there */ } } static int code_loadbool (FuncState *fs, int A, OpCode op) { luaK_getlabel(fs); /* those instructions may be jump targets */ return luaK_codeABC(fs, op, A, 0, 0); } /* ** check whether list has any jump that do not produce a value ** or produce an inverted value */ static int need_value (FuncState *fs, int list) { for (; list != NO_JUMP; list = getjump(fs, list)) { Instruction i = *getjumpcontrol(fs, list); if (GET_OPCODE(i) != OP_TESTSET) return 1; } return 0; /* not found */ } /* ** Ensures final expression result (which includes results from its ** jump lists) is in register 'reg'. ** If expression has jumps, need to patch these jumps either to ** its final position or to "load" instructions (for those tests ** that do not produce values). */ static void exp2reg (FuncState *fs, expdesc *e, int reg) { discharge2reg(fs, e, reg); if (e->k == VJMP) /* expression itself is a test? */ luaK_concat(fs, &e->t, e->u.info); /* put this jump in 't' list */ if (hasjumps(e)) { int final; /* position after whole expression */ int p_f = NO_JUMP; /* position of an eventual LOAD false */ int p_t = NO_JUMP; /* position of an eventual LOAD true */ if (need_value(fs, e->t) || need_value(fs, e->f)) { int fj = (e->k == VJMP) ? NO_JUMP : luaK_jump(fs); p_f = code_loadbool(fs, reg, OP_LFALSESKIP); /* skip next inst. */ p_t = code_loadbool(fs, reg, OP_LOADTRUE); /* jump around these booleans if 'e' is not a test */ luaK_patchtohere(fs, fj); } final = luaK_getlabel(fs); patchlistaux(fs, e->f, final, reg, p_f); patchlistaux(fs, e->t, final, reg, p_t); } e->f = e->t = NO_JUMP; e->u.info = reg; e->k = VNONRELOC; } /* ** Ensures final expression result is in next available register. */ void luaK_exp2nextreg (FuncState *fs, expdesc *e) { luaK_dischargevars(fs, e); freeexp(fs, e); luaK_reserveregs(fs, 1); exp2reg(fs, e, fs->freereg - 1); } /* ** Ensures final expression result is in some (any) register ** and return that register. */ int luaK_exp2anyreg (FuncState *fs, expdesc *e) { luaK_dischargevars(fs, e); if (e->k == VNONRELOC) { /* expression already has a register? */ if (!hasjumps(e)) /* no jumps? */ return e->u.info; /* result is already in a register */ if (e->u.info >= luaY_nvarstack(fs)) { /* reg. is not a local? */ exp2reg(fs, e, e->u.info); /* put final result in it */ return e->u.info; } /* else expression has jumps and cannot change its register to hold the jump values, because it is a local variable. Go through to the default case. */ } luaK_exp2nextreg(fs, e); /* default: use next available register */ return e->u.info; } /* ** Ensures final expression result is either in a register ** or in an upvalue. */ void luaK_exp2anyregup (FuncState *fs, expdesc *e) { if (e->k != VUPVAL || hasjumps(e)) luaK_exp2anyreg(fs, e); } /* ** Ensures final expression result is either in a register ** or it is a constant. */ void luaK_exp2val (FuncState *fs, expdesc *e) { if (hasjumps(e)) luaK_exp2anyreg(fs, e); else luaK_dischargevars(fs, e); } /* ** Try to make 'e' a K expression with an index in the range of R/K ** indices. Return true iff succeeded. */ static int luaK_exp2K (FuncState *fs, expdesc *e) { if (!hasjumps(e)) { int info; switch (e->k) { /* move constants to 'k' */ case VTRUE: info = boolT(fs); break; case VFALSE: info = boolF(fs); break; case VNIL: info = nilK(fs); break; case VKINT: info = luaK_intK(fs, e->u.ival); break; case VKFLT: info = luaK_numberK(fs, e->u.nval); break; case VKSTR: info = stringK(fs, e->u.strval); break; case VK: info = e->u.info; break; default: return 0; /* not a constant */ } if (info <= MAXINDEXRK) { /* does constant fit in 'argC'? */ e->k = VK; /* make expression a 'K' expression */ e->u.info = info; return 1; } } /* else, expression doesn't fit; leave it unchanged */ return 0; } /* ** Ensures final expression result is in a valid R/K index ** (that is, it is either in a register or in 'k' with an index ** in the range of R/K indices). ** Returns 1 iff expression is K. */ int luaK_exp2RK (FuncState *fs, expdesc *e) { if (luaK_exp2K(fs, e)) return 1; else { /* not a constant in the right range: put it in a register */ luaK_exp2anyreg(fs, e); return 0; } } static void codeABRK (FuncState *fs, OpCode o, int a, int b, expdesc *ec) { int k = luaK_exp2RK(fs, ec); luaK_codeABCk(fs, o, a, b, ec->u.info, k); } /* ** Generate code to store result of expression 'ex' into variable 'var'. */ void luaK_storevar (FuncState *fs, expdesc *var, expdesc *ex) { switch (var->k) { case VLOCAL: { freeexp(fs, ex); exp2reg(fs, ex, var->u.var.ridx); /* compute 'ex' into proper place */ return; } case VUPVAL: { int e = luaK_exp2anyreg(fs, ex); luaK_codeABC(fs, OP_SETUPVAL, e, var->u.info, 0); break; } case VINDEXUP: { codeABRK(fs, OP_SETTABUP, var->u.ind.t, var->u.ind.idx, ex); break; } case VINDEXI: { codeABRK(fs, OP_SETI, var->u.ind.t, var->u.ind.idx, ex); break; } case VINDEXSTR: { codeABRK(fs, OP_SETFIELD, var->u.ind.t, var->u.ind.idx, ex); break; } case VINDEXED: { codeABRK(fs, OP_SETTABLE, var->u.ind.t, var->u.ind.idx, ex); break; } default: lua_assert(0); /* invalid var kind to store */ } freeexp(fs, ex); } /* ** Emit SELF instruction (convert expression 'e' into 'e:key(e,'). */ void luaK_self (FuncState *fs, expdesc *e, expdesc *key) { int ereg; luaK_exp2anyreg(fs, e); ereg = e->u.info; /* register where 'e' was placed */ freeexp(fs, e); e->u.info = fs->freereg; /* base register for op_self */ e->k = VNONRELOC; /* self expression has a fixed register */ luaK_reserveregs(fs, 2); /* function and 'self' produced by op_self */ codeABRK(fs, OP_SELF, e->u.info, ereg, key); freeexp(fs, key); } /* ** Negate condition 'e' (where 'e' is a comparison). */ static void negatecondition (FuncState *fs, expdesc *e) { Instruction *pc = getjumpcontrol(fs, e->u.info); lua_assert(testTMode(GET_OPCODE(*pc)) && GET_OPCODE(*pc) != OP_TESTSET && GET_OPCODE(*pc) != OP_TEST); SETARG_k(*pc, (GETARG_k(*pc) ^ 1)); } /* ** Emit instruction to jump if 'e' is 'cond' (that is, if 'cond' ** is true, code will jump if 'e' is true.) Return jump position. ** Optimize when 'e' is 'not' something, inverting the condition ** and removing the 'not'. */ static int jumponcond (FuncState *fs, expdesc *e, int cond) { if (e->k == VRELOC) { Instruction ie = getinstruction(fs, e); if (GET_OPCODE(ie) == OP_NOT) { removelastinstruction(fs); /* remove previous OP_NOT */ return condjump(fs, OP_TEST, GETARG_B(ie), 0, 0, !cond); } /* else go through */ } discharge2anyreg(fs, e); freeexp(fs, e); return condjump(fs, OP_TESTSET, NO_REG, e->u.info, 0, cond); } /* ** Emit code to go through if 'e' is true, jump otherwise. */ void luaK_goiftrue (FuncState *fs, expdesc *e) { int pc; /* pc of new jump */ luaK_dischargevars(fs, e); switch (e->k) { case VJMP: { /* condition? */ negatecondition(fs, e); /* jump when it is false */ pc = e->u.info; /* save jump position */ break; } case VK: case VKFLT: case VKINT: case VKSTR: case VTRUE: { pc = NO_JUMP; /* always true; do nothing */ break; } default: { pc = jumponcond(fs, e, 0); /* jump when false */ break; } } luaK_concat(fs, &e->f, pc); /* insert new jump in false list */ luaK_patchtohere(fs, e->t); /* true list jumps to here (to go through) */ e->t = NO_JUMP; } /* ** Emit code to go through if 'e' is false, jump otherwise. */ void luaK_goiffalse (FuncState *fs, expdesc *e) { int pc; /* pc of new jump */ luaK_dischargevars(fs, e); switch (e->k) { case VJMP: { pc = e->u.info; /* already jump if true */ break; } case VNIL: case VFALSE: { pc = NO_JUMP; /* always false; do nothing */ break; } default: { pc = jumponcond(fs, e, 1); /* jump if true */ break; } } luaK_concat(fs, &e->t, pc); /* insert new jump in 't' list */ luaK_patchtohere(fs, e->f); /* false list jumps to here (to go through) */ e->f = NO_JUMP; } /* ** Code 'not e', doing constant folding. */ static void codenot (FuncState *fs, expdesc *e) { switch (e->k) { case VNIL: case VFALSE: { e->k = VTRUE; /* true == not nil == not false */ break; } case VK: case VKFLT: case VKINT: case VKSTR: case VTRUE: { e->k = VFALSE; /* false == not "x" == not 0.5 == not 1 == not true */ break; } case VJMP: { negatecondition(fs, e); break; } case VRELOC: case VNONRELOC: { discharge2anyreg(fs, e); freeexp(fs, e); e->u.info = luaK_codeABC(fs, OP_NOT, 0, e->u.info, 0); e->k = VRELOC; break; } default: lua_assert(0); /* cannot happen */ } /* interchange true and false lists */ { int temp = e->f; e->f = e->t; e->t = temp; } removevalues(fs, e->f); /* values are useless when negated */ removevalues(fs, e->t); } /* ** Check whether expression 'e' is a small literal string */ static int isKstr (FuncState *fs, expdesc *e) { return (e->k == VK && !hasjumps(e) && e->u.info <= MAXARG_B && ttisshrstring(&fs->f->k[e->u.info])); } /* ** Check whether expression 'e' is a literal integer. */ int luaK_isKint (expdesc *e) { return (e->k == VKINT && !hasjumps(e)); } /* ** Check whether expression 'e' is a literal integer in ** proper range to fit in register C */ static int isCint (expdesc *e) { return luaK_isKint(e) && (l_castS2U(e->u.ival) <= l_castS2U(MAXARG_C)); } /* ** Check whether expression 'e' is a literal integer in ** proper range to fit in register sC */ static int isSCint (expdesc *e) { return luaK_isKint(e) && fitsC(e->u.ival); } /* ** Check whether expression 'e' is a literal integer or float in ** proper range to fit in a register (sB or sC). */ static int isSCnumber (expdesc *e, int *pi, int *isfloat) { lua_Integer i; if (e->k == VKINT) i = e->u.ival; else if (e->k == VKFLT && luaV_flttointeger(e->u.nval, &i, F2Ieq)) *isfloat = 1; else return 0; /* not a number */ if (!hasjumps(e) && fitsC(i)) { *pi = int2sC(cast_int(i)); return 1; } else return 0; } /* ** Create expression 't[k]'. 't' must have its final result already in a ** register or upvalue. Upvalues can only be indexed by literal strings. ** Keys can be literal strings in the constant table or arbitrary ** values in registers. */ void luaK_indexed (FuncState *fs, expdesc *t, expdesc *k) { if (k->k == VKSTR) str2K(fs, k); lua_assert(!hasjumps(t) && (t->k == VLOCAL || t->k == VNONRELOC || t->k == VUPVAL)); if (t->k == VUPVAL && !isKstr(fs, k)) /* upvalue indexed by non 'Kstr'? */ luaK_exp2anyreg(fs, t); /* put it in a register */ if (t->k == VUPVAL) { t->u.ind.t = t->u.info; /* upvalue index */ t->u.ind.idx = k->u.info; /* literal string */ t->k = VINDEXUP; } else { /* register index of the table */ t->u.ind.t = (t->k == VLOCAL) ? t->u.var.ridx: t->u.info; if (isKstr(fs, k)) { t->u.ind.idx = k->u.info; /* literal string */ t->k = VINDEXSTR; } else if (isCint(k)) { t->u.ind.idx = cast_int(k->u.ival); /* int. constant in proper range */ t->k = VINDEXI; } else { t->u.ind.idx = luaK_exp2anyreg(fs, k); /* register */ t->k = VINDEXED; } } } /* ** Return false if folding can raise an error. ** Bitwise operations need operands convertible to integers; division ** operations cannot have 0 as divisor. */ static int validop (int op, TValue *v1, TValue *v2) { switch (op) { case LUA_OPBAND: case LUA_OPBOR: case LUA_OPBXOR: case LUA_OPSHL: case LUA_OPSHR: case LUA_OPBNOT: { /* conversion errors */ lua_Integer i; return (luaV_tointegerns(v1, &i, LUA_FLOORN2I) && luaV_tointegerns(v2, &i, LUA_FLOORN2I)); } case LUA_OPDIV: case LUA_OPIDIV: case LUA_OPMOD: /* division by 0 */ return (nvalue(v2) != 0); default: return 1; /* everything else is valid */ } } /* ** Try to "constant-fold" an operation; return 1 iff successful. ** (In this case, 'e1' has the final result.) */ static int constfolding (FuncState *fs, int op, expdesc *e1, const expdesc *e2) { TValue v1, v2, res; if (!tonumeral(e1, &v1) || !tonumeral(e2, &v2) || !validop(op, &v1, &v2)) return 0; /* non-numeric operands or not safe to fold */ luaO_rawarith(fs->ls->L, op, &v1, &v2, &res); /* does operation */ if (ttisinteger(&res)) { e1->k = VKINT; e1->u.ival = ivalue(&res); } else { /* folds neither NaN nor 0.0 (to avoid problems with -0.0) */ lua_Number n = fltvalue(&res); if (luai_numisnan(n) || n == 0) return 0; e1->k = VKFLT; e1->u.nval = n; } return 1; } /* ** Emit code for unary expressions that "produce values" ** (everything but 'not'). ** Expression to produce final result will be encoded in 'e'. */ static void codeunexpval (FuncState *fs, OpCode op, expdesc *e, int line) { int r = luaK_exp2anyreg(fs, e); /* opcodes operate only on registers */ freeexp(fs, e); e->u.info = luaK_codeABC(fs, op, 0, r, 0); /* generate opcode */ e->k = VRELOC; /* all those operations are relocatable */ luaK_fixline(fs, line); } /* ** Emit code for binary expressions that "produce values" ** (everything but logical operators 'and'/'or' and comparison ** operators). ** Expression to produce final result will be encoded in 'e1'. */ static void finishbinexpval (FuncState *fs, expdesc *e1, expdesc *e2, OpCode op, int v2, int flip, int line, OpCode mmop, TMS event) { int v1 = luaK_exp2anyreg(fs, e1); int pc = luaK_codeABCk(fs, op, 0, v1, v2, 0); freeexps(fs, e1, e2); e1->u.info = pc; e1->k = VRELOC; /* all those operations are relocatable */ luaK_fixline(fs, line); luaK_codeABCk(fs, mmop, v1, v2, event, flip); /* to call metamethod */ luaK_fixline(fs, line); } /* ** Emit code for binary expressions that "produce values" over ** two registers. */ static void codebinexpval (FuncState *fs, OpCode op, expdesc *e1, expdesc *e2, int line) { int v2 = luaK_exp2anyreg(fs, e2); /* both operands are in registers */ lua_assert(OP_ADD <= op && op <= OP_SHR); finishbinexpval(fs, e1, e2, op, v2, 0, line, OP_MMBIN, cast(TMS, (op - OP_ADD) + TM_ADD)); } /* ** Code binary operators with immediate operands. */ static void codebini (FuncState *fs, OpCode op, expdesc *e1, expdesc *e2, int flip, int line, TMS event) { int v2 = int2sC(cast_int(e2->u.ival)); /* immediate operand */ lua_assert(e2->k == VKINT); finishbinexpval(fs, e1, e2, op, v2, flip, line, OP_MMBINI, event); } /* Try to code a binary operator negating its second operand. ** For the metamethod, 2nd operand must keep its original value. */ static int finishbinexpneg (FuncState *fs, expdesc *e1, expdesc *e2, OpCode op, int line, TMS event) { if (!luaK_isKint(e2)) return 0; /* not an integer constant */ else { lua_Integer i2 = e2->u.ival; if (!(fitsC(i2) && fitsC(-i2))) return 0; /* not in the proper range */ else { /* operating a small integer constant */ int v2 = cast_int(i2); finishbinexpval(fs, e1, e2, op, int2sC(-v2), 0, line, OP_MMBINI, event); /* correct metamethod argument */ SETARG_B(fs->f->code[fs->pc - 1], int2sC(v2)); return 1; /* successfully coded */ } } } static void swapexps (expdesc *e1, expdesc *e2) { expdesc temp = *e1; *e1 = *e2; *e2 = temp; /* swap 'e1' and 'e2' */ } /* ** Code arithmetic operators ('+', '-', ...). If second operand is a ** constant in the proper range, use variant opcodes with K operands. */ static void codearith (FuncState *fs, BinOpr opr, expdesc *e1, expdesc *e2, int flip, int line) { TMS event = cast(TMS, opr + TM_ADD); if (tonumeral(e2, NULL) && luaK_exp2K(fs, e2)) { /* K operand? */ int v2 = e2->u.info; /* K index */ OpCode op = cast(OpCode, opr + OP_ADDK); finishbinexpval(fs, e1, e2, op, v2, flip, line, OP_MMBINK, event); } else { /* 'e2' is neither an immediate nor a K operand */ OpCode op = cast(OpCode, opr + OP_ADD); if (flip) swapexps(e1, e2); /* back to original order */ codebinexpval(fs, op, e1, e2, line); /* use standard operators */ } } /* ** Code commutative operators ('+', '*'). If first operand is a ** numeric constant, change order of operands to try to use an ** immediate or K operator. */ static void codecommutative (FuncState *fs, BinOpr op, expdesc *e1, expdesc *e2, int line) { int flip = 0; if (tonumeral(e1, NULL)) { /* is first operand a numeric constant? */ swapexps(e1, e2); /* change order */ flip = 1; } if (op == OPR_ADD && isSCint(e2)) /* immediate operand? */ codebini(fs, cast(OpCode, OP_ADDI), e1, e2, flip, line, TM_ADD); else codearith(fs, op, e1, e2, flip, line); } /* ** Code bitwise operations; they are all associative, so the function ** tries to put an integer constant as the 2nd operand (a K operand). */ static void codebitwise (FuncState *fs, BinOpr opr, expdesc *e1, expdesc *e2, int line) { int flip = 0; int v2; OpCode op; if (e1->k == VKINT && luaK_exp2RK(fs, e1)) { swapexps(e1, e2); /* 'e2' will be the constant operand */ flip = 1; } else if (!(e2->k == VKINT && luaK_exp2RK(fs, e2))) { /* no constants? */ op = cast(OpCode, opr + OP_ADD); codebinexpval(fs, op, e1, e2, line); /* all-register opcodes */ return; } v2 = e2->u.info; /* index in K array */ op = cast(OpCode, opr + OP_ADDK); lua_assert(ttisinteger(&fs->f->k[v2])); finishbinexpval(fs, e1, e2, op, v2, flip, line, OP_MMBINK, cast(TMS, opr + TM_ADD)); } /* ** Emit code for order comparisons. When using an immediate operand, ** 'isfloat' tells whether the original value was a float. */ static void codeorder (FuncState *fs, OpCode op, expdesc *e1, expdesc *e2) { int r1, r2; int im; int isfloat = 0; if (isSCnumber(e2, &im, &isfloat)) { /* use immediate operand */ r1 = luaK_exp2anyreg(fs, e1); r2 = im; op = cast(OpCode, (op - OP_LT) + OP_LTI); } else if (isSCnumber(e1, &im, &isfloat)) { /* transform (A < B) to (B > A) and (A <= B) to (B >= A) */ r1 = luaK_exp2anyreg(fs, e2); r2 = im; op = (op == OP_LT) ? OP_GTI : OP_GEI; } else { /* regular case, compare two registers */ r1 = luaK_exp2anyreg(fs, e1); r2 = luaK_exp2anyreg(fs, e2); } freeexps(fs, e1, e2); e1->u.info = condjump(fs, op, r1, r2, isfloat, 1); e1->k = VJMP; } /* ** Emit code for equality comparisons ('==', '~='). ** 'e1' was already put as RK by 'luaK_infix'. */ static void codeeq (FuncState *fs, BinOpr opr, expdesc *e1, expdesc *e2) { int r1, r2; int im; int isfloat = 0; /* not needed here, but kept for symmetry */ OpCode op; if (e1->k != VNONRELOC) { lua_assert(e1->k == VK || e1->k == VKINT || e1->k == VKFLT); swapexps(e1, e2); } r1 = luaK_exp2anyreg(fs, e1); /* 1st expression must be in register */ if (isSCnumber(e2, &im, &isfloat)) { op = OP_EQI; r2 = im; /* immediate operand */ } else if (luaK_exp2RK(fs, e2)) { /* 1st expression is constant? */ op = OP_EQK; r2 = e2->u.info; /* constant index */ } else { op = OP_EQ; /* will compare two registers */ r2 = luaK_exp2anyreg(fs, e2); } freeexps(fs, e1, e2); e1->u.info = condjump(fs, op, r1, r2, isfloat, (opr == OPR_EQ)); e1->k = VJMP; } /* ** Apply prefix operation 'op' to expression 'e'. */ void luaK_prefix (FuncState *fs, UnOpr op, expdesc *e, int line) { static const expdesc ef = {VKINT, {0}, NO_JUMP, NO_JUMP}; luaK_dischargevars(fs, e); switch (op) { case OPR_MINUS: case OPR_BNOT: /* use 'ef' as fake 2nd operand */ if (constfolding(fs, op + LUA_OPUNM, e, &ef)) break; /* else */ /* FALLTHROUGH */ case OPR_LEN: codeunexpval(fs, cast(OpCode, op + OP_UNM), e, line); break; case OPR_NOT: codenot(fs, e); break; default: lua_assert(0); } } /* ** Process 1st operand 'v' of binary operation 'op' before reading ** 2nd operand. */ void luaK_infix (FuncState *fs, BinOpr op, expdesc *v) { luaK_dischargevars(fs, v); switch (op) { case OPR_AND: { luaK_goiftrue(fs, v); /* go ahead only if 'v' is true */ break; } case OPR_OR: { luaK_goiffalse(fs, v); /* go ahead only if 'v' is false */ break; } case OPR_CONCAT: { luaK_exp2nextreg(fs, v); /* operand must be on the stack */ break; } case OPR_ADD: case OPR_SUB: case OPR_MUL: case OPR_DIV: case OPR_IDIV: case OPR_MOD: case OPR_POW: case OPR_BAND: case OPR_BOR: case OPR_BXOR: case OPR_SHL: case OPR_SHR: { if (!tonumeral(v, NULL)) luaK_exp2anyreg(fs, v); /* else keep numeral, which may be folded with 2nd operand */ break; } case OPR_EQ: case OPR_NE: { if (!tonumeral(v, NULL)) luaK_exp2RK(fs, v); /* else keep numeral, which may be an immediate operand */ break; } case OPR_LT: case OPR_LE: case OPR_GT: case OPR_GE: { int dummy, dummy2; if (!isSCnumber(v, &dummy, &dummy2)) luaK_exp2anyreg(fs, v); /* else keep numeral, which may be an immediate operand */ break; } default: lua_assert(0); } } /* ** Create code for '(e1 .. e2)'. ** For '(e1 .. e2.1 .. e2.2)' (which is '(e1 .. (e2.1 .. e2.2))', ** because concatenation is right associative), merge both CONCATs. */ static void codeconcat (FuncState *fs, expdesc *e1, expdesc *e2, int line) { Instruction *ie2 = previousinstruction(fs); if (GET_OPCODE(*ie2) == OP_CONCAT) { /* is 'e2' a concatenation? */ int n = GETARG_B(*ie2); /* # of elements concatenated in 'e2' */ lua_assert(e1->u.info + 1 == GETARG_A(*ie2)); freeexp(fs, e2); SETARG_A(*ie2, e1->u.info); /* correct first element ('e1') */ SETARG_B(*ie2, n + 1); /* will concatenate one more element */ } else { /* 'e2' is not a concatenation */ luaK_codeABC(fs, OP_CONCAT, e1->u.info, 2, 0); /* new concat opcode */ freeexp(fs, e2); luaK_fixline(fs, line); } } /* ** Finalize code for binary operation, after reading 2nd operand. */ void luaK_posfix (FuncState *fs, BinOpr opr, expdesc *e1, expdesc *e2, int line) { luaK_dischargevars(fs, e2); if (foldbinop(opr) && constfolding(fs, opr + LUA_OPADD, e1, e2)) return; /* done by folding */ switch (opr) { case OPR_AND: { lua_assert(e1->t == NO_JUMP); /* list closed by 'luaK_infix' */ luaK_concat(fs, &e2->f, e1->f); *e1 = *e2; break; } case OPR_OR: { lua_assert(e1->f == NO_JUMP); /* list closed by 'luaK_infix' */ luaK_concat(fs, &e2->t, e1->t); *e1 = *e2; break; } case OPR_CONCAT: { /* e1 .. e2 */ luaK_exp2nextreg(fs, e2); codeconcat(fs, e1, e2, line); break; } case OPR_ADD: case OPR_MUL: { codecommutative(fs, opr, e1, e2, line); break; } case OPR_SUB: { if (finishbinexpneg(fs, e1, e2, OP_ADDI, line, TM_SUB)) break; /* coded as (r1 + -I) */ /* ELSE */ } /* FALLTHROUGH */ case OPR_DIV: case OPR_IDIV: case OPR_MOD: case OPR_POW: { codearith(fs, opr, e1, e2, 0, line); break; } case OPR_BAND: case OPR_BOR: case OPR_BXOR: { codebitwise(fs, opr, e1, e2, line); break; } case OPR_SHL: { if (isSCint(e1)) { swapexps(e1, e2); codebini(fs, OP_SHLI, e1, e2, 1, line, TM_SHL); /* I << r2 */ } else if (finishbinexpneg(fs, e1, e2, OP_SHRI, line, TM_SHL)) { /* coded as (r1 >> -I) */; } else /* regular case (two registers) */ codebinexpval(fs, OP_SHL, e1, e2, line); break; } case OPR_SHR: { if (isSCint(e2)) codebini(fs, OP_SHRI, e1, e2, 0, line, TM_SHR); /* r1 >> I */ else /* regular case (two registers) */ codebinexpval(fs, OP_SHR, e1, e2, line); break; } case OPR_EQ: case OPR_NE: { codeeq(fs, opr, e1, e2); break; } case OPR_LT: case OPR_LE: { OpCode op = cast(OpCode, (opr - OPR_EQ) + OP_EQ); codeorder(fs, op, e1, e2); break; } case OPR_GT: case OPR_GE: { /* '(a > b)' <=> '(b < a)'; '(a >= b)' <=> '(b <= a)' */ OpCode op = cast(OpCode, (opr - OPR_NE) + OP_EQ); swapexps(e1, e2); codeorder(fs, op, e1, e2); break; } default: lua_assert(0); } } /* ** Change line information associated with current position, by removing ** previous info and adding it again with new line. */ void luaK_fixline (FuncState *fs, int line) { removelastlineinfo(fs); savelineinfo(fs, fs->f, line); } void luaK_settablesize (FuncState *fs, int pc, int ra, int asize, int hsize) { Instruction *inst = &fs->f->code[pc]; int rb = (hsize != 0) ? luaO_ceillog2(hsize) + 1 : 0; /* hash size */ int extra = asize / (MAXARG_C + 1); /* higher bits of array size */ int rc = asize % (MAXARG_C + 1); /* lower bits of array size */ int k = (extra > 0); /* true iff needs extra argument */ *inst = CREATE_ABCk(OP_NEWTABLE, ra, rb, rc, k); *(inst + 1) = CREATE_Ax(OP_EXTRAARG, extra); } /* ** Emit a SETLIST instruction. ** 'base' is register that keeps table; ** 'nelems' is #table plus those to be stored now; ** 'tostore' is number of values (in registers 'base + 1',...) to add to ** table (or LUA_MULTRET to add up to stack top). */ void luaK_setlist (FuncState *fs, int base, int nelems, int tostore) { lua_assert(tostore != 0 && tostore <= LFIELDS_PER_FLUSH); if (tostore == LUA_MULTRET) tostore = 0; if (nelems <= MAXARG_C) luaK_codeABC(fs, OP_SETLIST, base, tostore, nelems); else { int extra = nelems / (MAXARG_C + 1); nelems %= (MAXARG_C + 1); luaK_codeABCk(fs, OP_SETLIST, base, tostore, nelems, 1); codeextraarg(fs, extra); } fs->freereg = base + 1; /* free registers with list values */ } /* ** return the final target of a jump (skipping jumps to jumps) */ static int finaltarget (Instruction *code, int i) { int count; for (count = 0; count < 100; count++) { /* avoid infinite loops */ Instruction pc = code[i]; if (GET_OPCODE(pc) != OP_JMP) break; else i += GETARG_sJ(pc) + 1; } return i; } /* ** Do a final pass over the code of a function, doing small peephole ** optimizations and adjustments. */ void luaK_finish (FuncState *fs) { int i; Proto *p = fs->f; for (i = 0; i < fs->pc; i++) { Instruction *pc = &p->code[i]; lua_assert(i == 0 || isOT(*(pc - 1)) == isIT(*pc)); switch (GET_OPCODE(*pc)) { case OP_RETURN0: case OP_RETURN1: { if (!(fs->needclose || p->is_vararg)) break; /* no extra work */ /* else use OP_RETURN to do the extra work */ SET_OPCODE(*pc, OP_RETURN); } /* FALLTHROUGH */ case OP_RETURN: case OP_TAILCALL: { if (fs->needclose) SETARG_k(*pc, 1); /* signal that it needs to close */ if (p->is_vararg) SETARG_C(*pc, p->numparams + 1); /* signal that it is vararg */ break; } case OP_JMP: { int target = finaltarget(p->code, i); fixjump(fs, i, target); break; } default: break; } } } /* ** $Id: lparser.c $ ** Lua Parser ** See Copyright Notice in lua.h */ #define lparser_c #define LUA_CORE /*#include "lprefix.h"*/ #include #include /*#include "lua.h"*/ /*#include "lcode.h"*/ /*#include "ldebug.h"*/ /*#include "ldo.h"*/ /*#include "lfunc.h"*/ /*#include "llex.h"*/ /*#include "lmem.h"*/ /*#include "lobject.h"*/ /*#include "lopcodes.h"*/ /*#include "lparser.h"*/ /*#include "lstate.h"*/ /*#include "lstring.h"*/ /*#include "ltable.h"*/ /* maximum number of local variables per function (must be smaller than 250, due to the bytecode format) */ #define MAXVARS 200 #define hasmultret(k) ((k) == VCALL || (k) == VVARARG) /* because all strings are unified by the scanner, the parser can use pointer equality for string equality */ #define eqstr(a,b) ((a) == (b)) /* ** nodes for block list (list of active blocks) */ typedef struct BlockCnt { struct BlockCnt *previous; /* chain */ int firstlabel; /* index of first label in this block */ int firstgoto; /* index of first pending goto in this block */ lu_byte nactvar; /* # active locals outside the block */ lu_byte upval; /* true if some variable in the block is an upvalue */ lu_byte isloop; /* true if 'block' is a loop */ lu_byte insidetbc; /* true if inside the scope of a to-be-closed var. */ } BlockCnt; /* ** prototypes for recursive non-terminal functions */ static void statement (LexState *ls); static void expr (LexState *ls, expdesc *v); static l_noret error_expected (LexState *ls, int token) { luaX_syntaxerror(ls, luaO_pushfstring(ls->L, "%s expected", luaX_token2str(ls, token))); } static l_noret errorlimit (FuncState *fs, int limit, const char *what) { lua_State *L = fs->ls->L; const char *msg; int line = fs->f->linedefined; const char *where = (line == 0) ? "main function" : luaO_pushfstring(L, "function at line %d", line); msg = luaO_pushfstring(L, "too many %s (limit is %d) in %s", what, limit, where); luaX_syntaxerror(fs->ls, msg); } static void checklimit (FuncState *fs, int v, int l, const char *what) { if (v > l) errorlimit(fs, l, what); } /* ** Test whether next token is 'c'; if so, skip it. */ static int testnext (LexState *ls, int c) { if (ls->t.token == c) { luaX_next(ls); return 1; } else return 0; } /* ** Check that next token is 'c'. */ static void check (LexState *ls, int c) { if (ls->t.token != c) error_expected(ls, c); } /* ** Check that next token is 'c' and skip it. */ static void checknext (LexState *ls, int c) { check(ls, c); luaX_next(ls); } #define check_condition(ls,c,msg) { if (!(c)) luaX_syntaxerror(ls, msg); } /* ** Check that next token is 'what' and skip it. In case of error, ** raise an error that the expected 'what' should match a 'who' ** in line 'where' (if that is not the current line). */ static void check_match (LexState *ls, int what, int who, int where) { if (l_unlikely(!testnext(ls, what))) { if (where == ls->linenumber) /* all in the same line? */ error_expected(ls, what); /* do not need a complex message */ else { luaX_syntaxerror(ls, luaO_pushfstring(ls->L, "%s expected (to close %s at line %d)", luaX_token2str(ls, what), luaX_token2str(ls, who), where)); } } } static TString *str_checkname (LexState *ls) { TString *ts; check(ls, TK_NAME); ts = ls->t.seminfo.ts; luaX_next(ls); return ts; } static void init_exp (expdesc *e, expkind k, int i) { e->f = e->t = NO_JUMP; e->k = k; e->u.info = i; } static void codestring (expdesc *e, TString *s) { e->f = e->t = NO_JUMP; e->k = VKSTR; e->u.strval = s; } static void codename (LexState *ls, expdesc *e) { codestring(e, str_checkname(ls)); } /* ** Register a new local variable in the active 'Proto' (for debug ** information). */ static int registerlocalvar (LexState *ls, FuncState *fs, TString *varname) { Proto *f = fs->f; int oldsize = f->sizelocvars; luaM_growvector(ls->L, f->locvars, fs->ndebugvars, f->sizelocvars, LocVar, SHRT_MAX, "local variables"); while (oldsize < f->sizelocvars) f->locvars[oldsize++].varname = NULL; f->locvars[fs->ndebugvars].varname = varname; f->locvars[fs->ndebugvars].startpc = fs->pc; luaC_objbarrier(ls->L, f, varname); return fs->ndebugvars++; } /* ** Create a new local variable with the given 'name'. Return its index ** in the function. */ static int new_localvar (LexState *ls, TString *name) { lua_State *L = ls->L; FuncState *fs = ls->fs; Dyndata *dyd = ls->dyd; Vardesc *var; checklimit(fs, dyd->actvar.n + 1 - fs->firstlocal, MAXVARS, "local variables"); luaM_growvector(L, dyd->actvar.arr, dyd->actvar.n + 1, dyd->actvar.size, Vardesc, USHRT_MAX, "local variables"); var = &dyd->actvar.arr[dyd->actvar.n++]; var->vd.kind = VDKREG; /* default */ var->vd.name = name; return dyd->actvar.n - 1 - fs->firstlocal; } #define new_localvarliteral(ls,v) \ new_localvar(ls, \ luaX_newstring(ls, "" v, (sizeof(v)/sizeof(char)) - 1)); /* ** Return the "variable description" (Vardesc) of a given variable. ** (Unless noted otherwise, all variables are referred to by their ** compiler indices.) */ static Vardesc *getlocalvardesc (FuncState *fs, int vidx) { return &fs->ls->dyd->actvar.arr[fs->firstlocal + vidx]; } /* ** Convert 'nvar', a compiler index level, to its corresponding ** register. For that, search for the highest variable below that level ** that is in a register and uses its register index ('ridx') plus one. */ static int reglevel (FuncState *fs, int nvar) { while (nvar-- > 0) { Vardesc *vd = getlocalvardesc(fs, nvar); /* get previous variable */ if (vd->vd.kind != RDKCTC) /* is in a register? */ return vd->vd.ridx + 1; } return 0; /* no variables in registers */ } /* ** Return the number of variables in the register stack for the given ** function. */ int luaY_nvarstack (FuncState *fs) { return reglevel(fs, fs->nactvar); } /* ** Get the debug-information entry for current variable 'vidx'. */ static LocVar *localdebuginfo (FuncState *fs, int vidx) { Vardesc *vd = getlocalvardesc(fs, vidx); if (vd->vd.kind == RDKCTC) return NULL; /* no debug info. for constants */ else { int idx = vd->vd.pidx; lua_assert(idx < fs->ndebugvars); return &fs->f->locvars[idx]; } } /* ** Create an expression representing variable 'vidx' */ static void init_var (FuncState *fs, expdesc *e, int vidx) { e->f = e->t = NO_JUMP; e->k = VLOCAL; e->u.var.vidx = vidx; e->u.var.ridx = getlocalvardesc(fs, vidx)->vd.ridx; } /* ** Raises an error if variable described by 'e' is read only */ static void check_readonly (LexState *ls, expdesc *e) { FuncState *fs = ls->fs; TString *varname = NULL; /* to be set if variable is const */ switch (e->k) { case VCONST: { varname = ls->dyd->actvar.arr[e->u.info].vd.name; break; } case VLOCAL: { Vardesc *vardesc = getlocalvardesc(fs, e->u.var.vidx); if (vardesc->vd.kind != VDKREG) /* not a regular variable? */ varname = vardesc->vd.name; break; } case VUPVAL: { Upvaldesc *up = &fs->f->upvalues[e->u.info]; if (up->kind != VDKREG) varname = up->name; break; } default: return; /* other cases cannot be read-only */ } if (varname) { const char *msg = luaO_pushfstring(ls->L, "attempt to assign to const variable '%s'", getstr(varname)); luaK_semerror(ls, msg); /* error */ } } /* ** Start the scope for the last 'nvars' created variables. */ static void adjustlocalvars (LexState *ls, int nvars) { FuncState *fs = ls->fs; int reglevel = luaY_nvarstack(fs); int i; for (i = 0; i < nvars; i++) { int vidx = fs->nactvar++; Vardesc *var = getlocalvardesc(fs, vidx); var->vd.ridx = reglevel++; var->vd.pidx = registerlocalvar(ls, fs, var->vd.name); } } /* ** Close the scope for all variables up to level 'tolevel'. ** (debug info.) */ static void removevars (FuncState *fs, int tolevel) { fs->ls->dyd->actvar.n -= (fs->nactvar - tolevel); while (fs->nactvar > tolevel) { LocVar *var = localdebuginfo(fs, --fs->nactvar); if (var) /* does it have debug information? */ var->endpc = fs->pc; } } /* ** Search the upvalues of the function 'fs' for one ** with the given 'name'. */ static int searchupvalue (FuncState *fs, TString *name) { int i; Upvaldesc *up = fs->f->upvalues; for (i = 0; i < fs->nups; i++) { if (eqstr(up[i].name, name)) return i; } return -1; /* not found */ } static Upvaldesc *allocupvalue (FuncState *fs) { Proto *f = fs->f; int oldsize = f->sizeupvalues; checklimit(fs, fs->nups + 1, MAXUPVAL, "upvalues"); luaM_growvector(fs->ls->L, f->upvalues, fs->nups, f->sizeupvalues, Upvaldesc, MAXUPVAL, "upvalues"); while (oldsize < f->sizeupvalues) f->upvalues[oldsize++].name = NULL; return &f->upvalues[fs->nups++]; } static int newupvalue (FuncState *fs, TString *name, expdesc *v) { Upvaldesc *up = allocupvalue(fs); FuncState *prev = fs->prev; if (v->k == VLOCAL) { up->instack = 1; up->idx = v->u.var.ridx; up->kind = getlocalvardesc(prev, v->u.var.vidx)->vd.kind; lua_assert(eqstr(name, getlocalvardesc(prev, v->u.var.vidx)->vd.name)); } else { up->instack = 0; up->idx = cast_byte(v->u.info); up->kind = prev->f->upvalues[v->u.info].kind; lua_assert(eqstr(name, prev->f->upvalues[v->u.info].name)); } up->name = name; luaC_objbarrier(fs->ls->L, fs->f, name); return fs->nups - 1; } /* ** Look for an active local variable with the name 'n' in the ** function 'fs'. If found, initialize 'var' with it and return ** its expression kind; otherwise return -1. */ static int searchvar (FuncState *fs, TString *n, expdesc *var) { int i; for (i = cast_int(fs->nactvar) - 1; i >= 0; i--) { Vardesc *vd = getlocalvardesc(fs, i); if (eqstr(n, vd->vd.name)) { /* found? */ if (vd->vd.kind == RDKCTC) /* compile-time constant? */ init_exp(var, VCONST, fs->firstlocal + i); else /* real variable */ init_var(fs, var, i); return var->k; } } return -1; /* not found */ } /* ** Mark block where variable at given level was defined ** (to emit close instructions later). */ static void markupval (FuncState *fs, int level) { BlockCnt *bl = fs->bl; while (bl->nactvar > level) bl = bl->previous; bl->upval = 1; fs->needclose = 1; } /* ** Mark that current block has a to-be-closed variable. */ static void marktobeclosed (FuncState *fs) { BlockCnt *bl = fs->bl; bl->upval = 1; bl->insidetbc = 1; fs->needclose = 1; } /* ** Find a variable with the given name 'n'. If it is an upvalue, add ** this upvalue into all intermediate functions. If it is a global, set ** 'var' as 'void' as a flag. */ static void singlevaraux (FuncState *fs, TString *n, expdesc *var, int base) { if (fs == NULL) /* no more levels? */ init_exp(var, VVOID, 0); /* default is global */ else { int v = searchvar(fs, n, var); /* look up locals at current level */ if (v >= 0) { /* found? */ if (v == VLOCAL && !base) markupval(fs, var->u.var.vidx); /* local will be used as an upval */ } else { /* not found as local at current level; try upvalues */ int idx = searchupvalue(fs, n); /* try existing upvalues */ if (idx < 0) { /* not found? */ singlevaraux(fs->prev, n, var, 0); /* try upper levels */ if (var->k == VLOCAL || var->k == VUPVAL) /* local or upvalue? */ idx = newupvalue(fs, n, var); /* will be a new upvalue */ else /* it is a global or a constant */ return; /* don't need to do anything at this level */ } init_exp(var, VUPVAL, idx); /* new or old upvalue */ } } } /* ** Find a variable with the given name 'n', handling global variables ** too. */ static void singlevar (LexState *ls, expdesc *var) { TString *varname = str_checkname(ls); FuncState *fs = ls->fs; singlevaraux(fs, varname, var, 1); if (var->k == VVOID) { /* global name? */ expdesc key; singlevaraux(fs, ls->envn, var, 1); /* get environment variable */ lua_assert(var->k != VVOID); /* this one must exist */ codestring(&key, varname); /* key is variable name */ luaK_indexed(fs, var, &key); /* env[varname] */ } } /* ** Adjust the number of results from an expression list 'e' with 'nexps' ** expressions to 'nvars' values. */ static void adjust_assign (LexState *ls, int nvars, int nexps, expdesc *e) { FuncState *fs = ls->fs; int needed = nvars - nexps; /* extra values needed */ if (hasmultret(e->k)) { /* last expression has multiple returns? */ int extra = needed + 1; /* discount last expression itself */ if (extra < 0) extra = 0; luaK_setreturns(fs, e, extra); /* last exp. provides the difference */ } else { if (e->k != VVOID) /* at least one expression? */ luaK_exp2nextreg(fs, e); /* close last expression */ if (needed > 0) /* missing values? */ luaK_nil(fs, fs->freereg, needed); /* complete with nils */ } if (needed > 0) luaK_reserveregs(fs, needed); /* registers for extra values */ else /* adding 'needed' is actually a subtraction */ fs->freereg += needed; /* remove extra values */ } #define enterlevel(ls) luaE_incCstack(ls->L) #define leavelevel(ls) ((ls)->L->nCcalls--) /* ** Generates an error that a goto jumps into the scope of some ** local variable. */ static l_noret jumpscopeerror (LexState *ls, Labeldesc *gt) { const char *varname = getstr(getlocalvardesc(ls->fs, gt->nactvar)->vd.name); const char *msg = " at line %d jumps into the scope of local '%s'"; msg = luaO_pushfstring(ls->L, msg, getstr(gt->name), gt->line, varname); luaK_semerror(ls, msg); /* raise the error */ } /* ** Solves the goto at index 'g' to given 'label' and removes it ** from the list of pending goto's. ** If it jumps into the scope of some variable, raises an error. */ static void solvegoto (LexState *ls, int g, Labeldesc *label) { int i; Labellist *gl = &ls->dyd->gt; /* list of goto's */ Labeldesc *gt = &gl->arr[g]; /* goto to be resolved */ lua_assert(eqstr(gt->name, label->name)); if (l_unlikely(gt->nactvar < label->nactvar)) /* enter some scope? */ jumpscopeerror(ls, gt); luaK_patchlist(ls->fs, gt->pc, label->pc); for (i = g; i < gl->n - 1; i++) /* remove goto from pending list */ gl->arr[i] = gl->arr[i + 1]; gl->n--; } /* ** Search for an active label with the given name. */ static Labeldesc *findlabel (LexState *ls, TString *name) { int i; Dyndata *dyd = ls->dyd; /* check labels in current function for a match */ for (i = ls->fs->firstlabel; i < dyd->label.n; i++) { Labeldesc *lb = &dyd->label.arr[i]; if (eqstr(lb->name, name)) /* correct label? */ return lb; } return NULL; /* label not found */ } /* ** Adds a new label/goto in the corresponding list. */ static int newlabelentry (LexState *ls, Labellist *l, TString *name, int line, int pc) { int n = l->n; luaM_growvector(ls->L, l->arr, n, l->size, Labeldesc, SHRT_MAX, "labels/gotos"); l->arr[n].name = name; l->arr[n].line = line; l->arr[n].nactvar = ls->fs->nactvar; l->arr[n].close = 0; l->arr[n].pc = pc; l->n = n + 1; return n; } static int newgotoentry (LexState *ls, TString *name, int line, int pc) { return newlabelentry(ls, &ls->dyd->gt, name, line, pc); } /* ** Solves forward jumps. Check whether new label 'lb' matches any ** pending gotos in current block and solves them. Return true ** if any of the goto's need to close upvalues. */ static int solvegotos (LexState *ls, Labeldesc *lb) { Labellist *gl = &ls->dyd->gt; int i = ls->fs->bl->firstgoto; int needsclose = 0; while (i < gl->n) { if (eqstr(gl->arr[i].name, lb->name)) { needsclose |= gl->arr[i].close; solvegoto(ls, i, lb); /* will remove 'i' from the list */ } else i++; } return needsclose; } /* ** Create a new label with the given 'name' at the given 'line'. ** 'last' tells whether label is the last non-op statement in its ** block. Solves all pending goto's to this new label and adds ** a close instruction if necessary. ** Returns true iff it added a close instruction. */ static int createlabel (LexState *ls, TString *name, int line, int last) { FuncState *fs = ls->fs; Labellist *ll = &ls->dyd->label; int l = newlabelentry(ls, ll, name, line, luaK_getlabel(fs)); if (last) { /* label is last no-op statement in the block? */ /* assume that locals are already out of scope */ ll->arr[l].nactvar = fs->bl->nactvar; } if (solvegotos(ls, &ll->arr[l])) { /* need close? */ luaK_codeABC(fs, OP_CLOSE, luaY_nvarstack(fs), 0, 0); return 1; } return 0; } /* ** Adjust pending gotos to outer level of a block. */ static void movegotosout (FuncState *fs, BlockCnt *bl) { int i; Labellist *gl = &fs->ls->dyd->gt; /* correct pending gotos to current block */ for (i = bl->firstgoto; i < gl->n; i++) { /* for each pending goto */ Labeldesc *gt = &gl->arr[i]; /* leaving a variable scope? */ if (reglevel(fs, gt->nactvar) > reglevel(fs, bl->nactvar)) gt->close |= bl->upval; /* jump may need a close */ gt->nactvar = bl->nactvar; /* update goto level */ } } static void enterblock (FuncState *fs, BlockCnt *bl, lu_byte isloop) { bl->isloop = isloop; bl->nactvar = fs->nactvar; bl->firstlabel = fs->ls->dyd->label.n; bl->firstgoto = fs->ls->dyd->gt.n; bl->upval = 0; bl->insidetbc = (fs->bl != NULL && fs->bl->insidetbc); bl->previous = fs->bl; fs->bl = bl; lua_assert(fs->freereg == luaY_nvarstack(fs)); } /* ** generates an error for an undefined 'goto'. */ static l_noret undefgoto (LexState *ls, Labeldesc *gt) { const char *msg; if (eqstr(gt->name, luaS_newliteral(ls->L, "break"))) { msg = "break outside loop at line %d"; msg = luaO_pushfstring(ls->L, msg, gt->line); } else { msg = "no visible label '%s' for at line %d"; msg = luaO_pushfstring(ls->L, msg, getstr(gt->name), gt->line); } luaK_semerror(ls, msg); } static void leaveblock (FuncState *fs) { BlockCnt *bl = fs->bl; LexState *ls = fs->ls; int hasclose = 0; int stklevel = reglevel(fs, bl->nactvar); /* level outside the block */ if (bl->isloop) /* fix pending breaks? */ hasclose = createlabel(ls, luaS_newliteral(ls->L, "break"), 0, 0); if (!hasclose && bl->previous && bl->upval) luaK_codeABC(fs, OP_CLOSE, stklevel, 0, 0); fs->bl = bl->previous; removevars(fs, bl->nactvar); lua_assert(bl->nactvar == fs->nactvar); fs->freereg = stklevel; /* free registers */ ls->dyd->label.n = bl->firstlabel; /* remove local labels */ if (bl->previous) /* inner block? */ movegotosout(fs, bl); /* update pending gotos to outer block */ else { if (bl->firstgoto < ls->dyd->gt.n) /* pending gotos in outer block? */ undefgoto(ls, &ls->dyd->gt.arr[bl->firstgoto]); /* error */ } } /* ** adds a new prototype into list of prototypes */ static Proto *addprototype (LexState *ls) { Proto *clp; lua_State *L = ls->L; FuncState *fs = ls->fs; Proto *f = fs->f; /* prototype of current function */ if (fs->np >= f->sizep) { int oldsize = f->sizep; luaM_growvector(L, f->p, fs->np, f->sizep, Proto *, MAXARG_Bx, "functions"); while (oldsize < f->sizep) f->p[oldsize++] = NULL; } f->p[fs->np++] = clp = luaF_newproto(L); luaC_objbarrier(L, f, clp); return clp; } /* ** codes instruction to create new closure in parent function. ** The OP_CLOSURE instruction uses the last available register, ** so that, if it invokes the GC, the GC knows which registers ** are in use at that time. */ static void codeclosure (LexState *ls, expdesc *v) { FuncState *fs = ls->fs->prev; init_exp(v, VRELOC, luaK_codeABx(fs, OP_CLOSURE, 0, fs->np - 1)); luaK_exp2nextreg(fs, v); /* fix it at the last register */ } static void open_func (LexState *ls, FuncState *fs, BlockCnt *bl) { Proto *f = fs->f; fs->prev = ls->fs; /* linked list of funcstates */ fs->ls = ls; ls->fs = fs; fs->pc = 0; fs->previousline = f->linedefined; fs->iwthabs = 0; fs->lasttarget = 0; fs->freereg = 0; fs->nk = 0; fs->nabslineinfo = 0; fs->np = 0; fs->nups = 0; fs->ndebugvars = 0; fs->nactvar = 0; fs->needclose = 0; fs->firstlocal = ls->dyd->actvar.n; fs->firstlabel = ls->dyd->label.n; fs->bl = NULL; f->source = ls->source; luaC_objbarrier(ls->L, f, f->source); f->maxstacksize = 2; /* registers 0/1 are always valid */ enterblock(fs, bl, 0); } static void close_func (LexState *ls) { lua_State *L = ls->L; FuncState *fs = ls->fs; Proto *f = fs->f; luaK_ret(fs, luaY_nvarstack(fs), 0); /* final return */ leaveblock(fs); lua_assert(fs->bl == NULL); luaK_finish(fs); luaM_shrinkvector(L, f->code, f->sizecode, fs->pc, Instruction); luaM_shrinkvector(L, f->lineinfo, f->sizelineinfo, fs->pc, ls_byte); luaM_shrinkvector(L, f->abslineinfo, f->sizeabslineinfo, fs->nabslineinfo, AbsLineInfo); luaM_shrinkvector(L, f->k, f->sizek, fs->nk, TValue); luaM_shrinkvector(L, f->p, f->sizep, fs->np, Proto *); luaM_shrinkvector(L, f->locvars, f->sizelocvars, fs->ndebugvars, LocVar); luaM_shrinkvector(L, f->upvalues, f->sizeupvalues, fs->nups, Upvaldesc); ls->fs = fs->prev; luaC_checkGC(L); } /*============================================================*/ /* GRAMMAR RULES */ /*============================================================*/ /* ** check whether current token is in the follow set of a block. ** 'until' closes syntactical blocks, but do not close scope, ** so it is handled in separate. */ static int block_follow (LexState *ls, int withuntil) { switch (ls->t.token) { case TK_ELSE: case TK_ELSEIF: case TK_END: case TK_EOS: return 1; case TK_UNTIL: return withuntil; default: return 0; } } static void statlist (LexState *ls) { /* statlist -> { stat [';'] } */ while (!block_follow(ls, 1)) { if (ls->t.token == TK_RETURN) { statement(ls); return; /* 'return' must be last statement */ } statement(ls); } } static void fieldsel (LexState *ls, expdesc *v) { /* fieldsel -> ['.' | ':'] NAME */ FuncState *fs = ls->fs; expdesc key; luaK_exp2anyregup(fs, v); luaX_next(ls); /* skip the dot or colon */ codename(ls, &key); luaK_indexed(fs, v, &key); } static void yindex (LexState *ls, expdesc *v) { /* index -> '[' expr ']' */ luaX_next(ls); /* skip the '[' */ expr(ls, v); luaK_exp2val(ls->fs, v); checknext(ls, ']'); } /* ** {====================================================================== ** Rules for Constructors ** ======================================================================= */ typedef struct ConsControl { expdesc v; /* last list item read */ expdesc *t; /* table descriptor */ int nh; /* total number of 'record' elements */ int na; /* number of array elements already stored */ int tostore; /* number of array elements pending to be stored */ } ConsControl; static void recfield (LexState *ls, ConsControl *cc) { /* recfield -> (NAME | '['exp']') = exp */ FuncState *fs = ls->fs; int reg = ls->fs->freereg; expdesc tab, key, val; if (ls->t.token == TK_NAME) { checklimit(fs, cc->nh, MAX_INT, "items in a constructor"); codename(ls, &key); } else /* ls->t.token == '[' */ yindex(ls, &key); cc->nh++; checknext(ls, '='); tab = *cc->t; luaK_indexed(fs, &tab, &key); expr(ls, &val); luaK_storevar(fs, &tab, &val); fs->freereg = reg; /* free registers */ } static void closelistfield (FuncState *fs, ConsControl *cc) { if (cc->v.k == VVOID) return; /* there is no list item */ luaK_exp2nextreg(fs, &cc->v); cc->v.k = VVOID; if (cc->tostore == LFIELDS_PER_FLUSH) { luaK_setlist(fs, cc->t->u.info, cc->na, cc->tostore); /* flush */ cc->na += cc->tostore; cc->tostore = 0; /* no more items pending */ } } static void lastlistfield (FuncState *fs, ConsControl *cc) { if (cc->tostore == 0) return; if (hasmultret(cc->v.k)) { luaK_setmultret(fs, &cc->v); luaK_setlist(fs, cc->t->u.info, cc->na, LUA_MULTRET); cc->na--; /* do not count last expression (unknown number of elements) */ } else { if (cc->v.k != VVOID) luaK_exp2nextreg(fs, &cc->v); luaK_setlist(fs, cc->t->u.info, cc->na, cc->tostore); } cc->na += cc->tostore; } static void listfield (LexState *ls, ConsControl *cc) { /* listfield -> exp */ expr(ls, &cc->v); cc->tostore++; } static void field (LexState *ls, ConsControl *cc) { /* field -> listfield | recfield */ switch(ls->t.token) { case TK_NAME: { /* may be 'listfield' or 'recfield' */ if (luaX_lookahead(ls) != '=') /* expression? */ listfield(ls, cc); else recfield(ls, cc); break; } case '[': { recfield(ls, cc); break; } default: { listfield(ls, cc); break; } } } static void constructor (LexState *ls, expdesc *t) { /* constructor -> '{' [ field { sep field } [sep] ] '}' sep -> ',' | ';' */ FuncState *fs = ls->fs; int line = ls->linenumber; int pc = luaK_codeABC(fs, OP_NEWTABLE, 0, 0, 0); ConsControl cc; luaK_code(fs, 0); /* space for extra arg. */ cc.na = cc.nh = cc.tostore = 0; cc.t = t; init_exp(t, VNONRELOC, fs->freereg); /* table will be at stack top */ luaK_reserveregs(fs, 1); init_exp(&cc.v, VVOID, 0); /* no value (yet) */ checknext(ls, '{'); do { lua_assert(cc.v.k == VVOID || cc.tostore > 0); if (ls->t.token == '}') break; closelistfield(fs, &cc); field(ls, &cc); } while (testnext(ls, ',') || testnext(ls, ';')); check_match(ls, '}', '{', line); lastlistfield(fs, &cc); luaK_settablesize(fs, pc, t->u.info, cc.na, cc.nh); } /* }====================================================================== */ static void setvararg (FuncState *fs, int nparams) { fs->f->is_vararg = 1; luaK_codeABC(fs, OP_VARARGPREP, nparams, 0, 0); } static void parlist (LexState *ls) { /* parlist -> [ {NAME ','} (NAME | '...') ] */ FuncState *fs = ls->fs; Proto *f = fs->f; int nparams = 0; int isvararg = 0; if (ls->t.token != ')') { /* is 'parlist' not empty? */ do { switch (ls->t.token) { case TK_NAME: { new_localvar(ls, str_checkname(ls)); nparams++; break; } case TK_DOTS: { luaX_next(ls); isvararg = 1; break; } default: luaX_syntaxerror(ls, " or '...' expected"); } } while (!isvararg && testnext(ls, ',')); } adjustlocalvars(ls, nparams); f->numparams = cast_byte(fs->nactvar); if (isvararg) setvararg(fs, f->numparams); /* declared vararg */ luaK_reserveregs(fs, fs->nactvar); /* reserve registers for parameters */ } static void body (LexState *ls, expdesc *e, int ismethod, int line) { /* body -> '(' parlist ')' block END */ FuncState new_fs; BlockCnt bl; new_fs.f = addprototype(ls); new_fs.f->linedefined = line; open_func(ls, &new_fs, &bl); checknext(ls, '('); if (ismethod) { new_localvarliteral(ls, "self"); /* create 'self' parameter */ adjustlocalvars(ls, 1); } parlist(ls); checknext(ls, ')'); statlist(ls); new_fs.f->lastlinedefined = ls->linenumber; check_match(ls, TK_END, TK_FUNCTION, line); codeclosure(ls, e); close_func(ls); } static int explist (LexState *ls, expdesc *v) { /* explist -> expr { ',' expr } */ int n = 1; /* at least one expression */ expr(ls, v); while (testnext(ls, ',')) { luaK_exp2nextreg(ls->fs, v); expr(ls, v); n++; } return n; } static void funcargs (LexState *ls, expdesc *f, int line) { FuncState *fs = ls->fs; expdesc args; int base, nparams; switch (ls->t.token) { case '(': { /* funcargs -> '(' [ explist ] ')' */ luaX_next(ls); if (ls->t.token == ')') /* arg list is empty? */ args.k = VVOID; else { explist(ls, &args); if (hasmultret(args.k)) luaK_setmultret(fs, &args); } check_match(ls, ')', '(', line); break; } case '{': { /* funcargs -> constructor */ constructor(ls, &args); break; } case TK_STRING: { /* funcargs -> STRING */ codestring(&args, ls->t.seminfo.ts); luaX_next(ls); /* must use 'seminfo' before 'next' */ break; } default: { luaX_syntaxerror(ls, "function arguments expected"); } } lua_assert(f->k == VNONRELOC); base = f->u.info; /* base register for call */ if (hasmultret(args.k)) nparams = LUA_MULTRET; /* open call */ else { if (args.k != VVOID) luaK_exp2nextreg(fs, &args); /* close last argument */ nparams = fs->freereg - (base+1); } init_exp(f, VCALL, luaK_codeABC(fs, OP_CALL, base, nparams+1, 2)); luaK_fixline(fs, line); fs->freereg = base+1; /* call remove function and arguments and leaves (unless changed) one result */ } /* ** {====================================================================== ** Expression parsing ** ======================================================================= */ static void primaryexp (LexState *ls, expdesc *v) { /* primaryexp -> NAME | '(' expr ')' */ switch (ls->t.token) { case '(': { int line = ls->linenumber; luaX_next(ls); expr(ls, v); check_match(ls, ')', '(', line); luaK_dischargevars(ls->fs, v); return; } case TK_NAME: { singlevar(ls, v); return; } default: { luaX_syntaxerror(ls, "unexpected symbol"); } } } static void suffixedexp (LexState *ls, expdesc *v) { /* suffixedexp -> primaryexp { '.' NAME | '[' exp ']' | ':' NAME funcargs | funcargs } */ FuncState *fs = ls->fs; int line = ls->linenumber; primaryexp(ls, v); for (;;) { switch (ls->t.token) { case '.': { /* fieldsel */ fieldsel(ls, v); break; } case '[': { /* '[' exp ']' */ expdesc key; luaK_exp2anyregup(fs, v); yindex(ls, &key); luaK_indexed(fs, v, &key); break; } case ':': { /* ':' NAME funcargs */ expdesc key; luaX_next(ls); codename(ls, &key); luaK_self(fs, v, &key); funcargs(ls, v, line); break; } case '(': case TK_STRING: case '{': { /* funcargs */ luaK_exp2nextreg(fs, v); funcargs(ls, v, line); break; } default: return; } } } static void simpleexp (LexState *ls, expdesc *v) { /* simpleexp -> FLT | INT | STRING | NIL | TRUE | FALSE | ... | constructor | FUNCTION body | suffixedexp */ switch (ls->t.token) { case TK_FLT: { init_exp(v, VKFLT, 0); v->u.nval = ls->t.seminfo.r; break; } case TK_INT: { init_exp(v, VKINT, 0); v->u.ival = ls->t.seminfo.i; break; } case TK_STRING: { codestring(v, ls->t.seminfo.ts); break; } case TK_NIL: { init_exp(v, VNIL, 0); break; } case TK_TRUE: { init_exp(v, VTRUE, 0); break; } case TK_FALSE: { init_exp(v, VFALSE, 0); break; } case TK_DOTS: { /* vararg */ FuncState *fs = ls->fs; check_condition(ls, fs->f->is_vararg, "cannot use '...' outside a vararg function"); init_exp(v, VVARARG, luaK_codeABC(fs, OP_VARARG, 0, 0, 1)); break; } case '{': { /* constructor */ constructor(ls, v); return; } case TK_FUNCTION: { luaX_next(ls); body(ls, v, 0, ls->linenumber); return; } default: { suffixedexp(ls, v); return; } } luaX_next(ls); } static UnOpr getunopr (int op) { switch (op) { case TK_NOT: return OPR_NOT; case '-': return OPR_MINUS; case '~': return OPR_BNOT; case '#': return OPR_LEN; default: return OPR_NOUNOPR; } } static BinOpr getbinopr (int op) { switch (op) { case '+': return OPR_ADD; case '-': return OPR_SUB; case '*': return OPR_MUL; case '%': return OPR_MOD; case '^': return OPR_POW; case '/': return OPR_DIV; case TK_IDIV: return OPR_IDIV; case '&': return OPR_BAND; case '|': return OPR_BOR; case '~': return OPR_BXOR; case TK_SHL: return OPR_SHL; case TK_SHR: return OPR_SHR; case TK_CONCAT: return OPR_CONCAT; case TK_NE: return OPR_NE; case TK_EQ: return OPR_EQ; case '<': return OPR_LT; case TK_LE: return OPR_LE; case '>': return OPR_GT; case TK_GE: return OPR_GE; case TK_AND: return OPR_AND; case TK_OR: return OPR_OR; default: return OPR_NOBINOPR; } } /* ** Priority table for binary operators. */ static const struct { lu_byte left; /* left priority for each binary operator */ lu_byte right; /* right priority */ } priority[] = { /* ORDER OPR */ {10, 10}, {10, 10}, /* '+' '-' */ {11, 11}, {11, 11}, /* '*' '%' */ {14, 13}, /* '^' (right associative) */ {11, 11}, {11, 11}, /* '/' '//' */ {6, 6}, {4, 4}, {5, 5}, /* '&' '|' '~' */ {7, 7}, {7, 7}, /* '<<' '>>' */ {9, 8}, /* '..' (right associative) */ {3, 3}, {3, 3}, {3, 3}, /* ==, <, <= */ {3, 3}, {3, 3}, {3, 3}, /* ~=, >, >= */ {2, 2}, {1, 1} /* and, or */ }; #define UNARY_PRIORITY 12 /* priority for unary operators */ /* ** subexpr -> (simpleexp | unop subexpr) { binop subexpr } ** where 'binop' is any binary operator with a priority higher than 'limit' */ static BinOpr subexpr (LexState *ls, expdesc *v, int limit) { BinOpr op; UnOpr uop; enterlevel(ls); uop = getunopr(ls->t.token); if (uop != OPR_NOUNOPR) { /* prefix (unary) operator? */ int line = ls->linenumber; luaX_next(ls); /* skip operator */ subexpr(ls, v, UNARY_PRIORITY); luaK_prefix(ls->fs, uop, v, line); } else simpleexp(ls, v); /* expand while operators have priorities higher than 'limit' */ op = getbinopr(ls->t.token); while (op != OPR_NOBINOPR && priority[op].left > limit) { expdesc v2; BinOpr nextop; int line = ls->linenumber; luaX_next(ls); /* skip operator */ luaK_infix(ls->fs, op, v); /* read sub-expression with higher priority */ nextop = subexpr(ls, &v2, priority[op].right); luaK_posfix(ls->fs, op, v, &v2, line); op = nextop; } leavelevel(ls); return op; /* return first untreated operator */ } static void expr (LexState *ls, expdesc *v) { subexpr(ls, v, 0); } /* }==================================================================== */ /* ** {====================================================================== ** Rules for Statements ** ======================================================================= */ static void block (LexState *ls) { /* block -> statlist */ FuncState *fs = ls->fs; BlockCnt bl; enterblock(fs, &bl, 0); statlist(ls); leaveblock(fs); } /* ** structure to chain all variables in the left-hand side of an ** assignment */ struct LHS_assign { struct LHS_assign *prev; expdesc v; /* variable (global, local, upvalue, or indexed) */ }; /* ** check whether, in an assignment to an upvalue/local variable, the ** upvalue/local variable is begin used in a previous assignment to a ** table. If so, save original upvalue/local value in a safe place and ** use this safe copy in the previous assignment. */ static void check_conflict (LexState *ls, struct LHS_assign *lh, expdesc *v) { FuncState *fs = ls->fs; int extra = fs->freereg; /* eventual position to save local variable */ int conflict = 0; for (; lh; lh = lh->prev) { /* check all previous assignments */ if (vkisindexed(lh->v.k)) { /* assignment to table field? */ if (lh->v.k == VINDEXUP) { /* is table an upvalue? */ if (v->k == VUPVAL && lh->v.u.ind.t == v->u.info) { conflict = 1; /* table is the upvalue being assigned now */ lh->v.k = VINDEXSTR; lh->v.u.ind.t = extra; /* assignment will use safe copy */ } } else { /* table is a register */ if (v->k == VLOCAL && lh->v.u.ind.t == v->u.var.ridx) { conflict = 1; /* table is the local being assigned now */ lh->v.u.ind.t = extra; /* assignment will use safe copy */ } /* is index the local being assigned? */ if (lh->v.k == VINDEXED && v->k == VLOCAL && lh->v.u.ind.idx == v->u.var.ridx) { conflict = 1; lh->v.u.ind.idx = extra; /* previous assignment will use safe copy */ } } } } if (conflict) { /* copy upvalue/local value to a temporary (in position 'extra') */ if (v->k == VLOCAL) luaK_codeABC(fs, OP_MOVE, extra, v->u.var.ridx, 0); else luaK_codeABC(fs, OP_GETUPVAL, extra, v->u.info, 0); luaK_reserveregs(fs, 1); } } /* ** Parse and compile a multiple assignment. The first "variable" ** (a 'suffixedexp') was already read by the caller. ** ** assignment -> suffixedexp restassign ** restassign -> ',' suffixedexp restassign | '=' explist */ static void restassign (LexState *ls, struct LHS_assign *lh, int nvars) { expdesc e; check_condition(ls, vkisvar(lh->v.k), "syntax error"); check_readonly(ls, &lh->v); if (testnext(ls, ',')) { /* restassign -> ',' suffixedexp restassign */ struct LHS_assign nv; nv.prev = lh; suffixedexp(ls, &nv.v); if (!vkisindexed(nv.v.k)) check_conflict(ls, lh, &nv.v); enterlevel(ls); /* control recursion depth */ restassign(ls, &nv, nvars+1); leavelevel(ls); } else { /* restassign -> '=' explist */ int nexps; checknext(ls, '='); nexps = explist(ls, &e); if (nexps != nvars) adjust_assign(ls, nvars, nexps, &e); else { luaK_setoneret(ls->fs, &e); /* close last expression */ luaK_storevar(ls->fs, &lh->v, &e); return; /* avoid default */ } } init_exp(&e, VNONRELOC, ls->fs->freereg-1); /* default assignment */ luaK_storevar(ls->fs, &lh->v, &e); } static int cond (LexState *ls) { /* cond -> exp */ expdesc v; expr(ls, &v); /* read condition */ if (v.k == VNIL) v.k = VFALSE; /* 'falses' are all equal here */ luaK_goiftrue(ls->fs, &v); return v.f; } static void gotostat (LexState *ls) { FuncState *fs = ls->fs; int line = ls->linenumber; TString *name = str_checkname(ls); /* label's name */ Labeldesc *lb = findlabel(ls, name); if (lb == NULL) /* no label? */ /* forward jump; will be resolved when the label is declared */ newgotoentry(ls, name, line, luaK_jump(fs)); else { /* found a label */ /* backward jump; will be resolved here */ int lblevel = reglevel(fs, lb->nactvar); /* label level */ if (luaY_nvarstack(fs) > lblevel) /* leaving the scope of a variable? */ luaK_codeABC(fs, OP_CLOSE, lblevel, 0, 0); /* create jump and link it to the label */ luaK_patchlist(fs, luaK_jump(fs), lb->pc); } } /* ** Break statement. Semantically equivalent to "goto break". */ static void breakstat (LexState *ls) { int line = ls->linenumber; luaX_next(ls); /* skip break */ newgotoentry(ls, luaS_newliteral(ls->L, "break"), line, luaK_jump(ls->fs)); } /* ** Check whether there is already a label with the given 'name'. */ static void checkrepeated (LexState *ls, TString *name) { Labeldesc *lb = findlabel(ls, name); if (l_unlikely(lb != NULL)) { /* already defined? */ const char *msg = "label '%s' already defined on line %d"; msg = luaO_pushfstring(ls->L, msg, getstr(name), lb->line); luaK_semerror(ls, msg); /* error */ } } static void labelstat (LexState *ls, TString *name, int line) { /* label -> '::' NAME '::' */ checknext(ls, TK_DBCOLON); /* skip double colon */ while (ls->t.token == ';' || ls->t.token == TK_DBCOLON) statement(ls); /* skip other no-op statements */ checkrepeated(ls, name); /* check for repeated labels */ createlabel(ls, name, line, block_follow(ls, 0)); } static void whilestat (LexState *ls, int line) { /* whilestat -> WHILE cond DO block END */ FuncState *fs = ls->fs; int whileinit; int condexit; BlockCnt bl; luaX_next(ls); /* skip WHILE */ whileinit = luaK_getlabel(fs); condexit = cond(ls); enterblock(fs, &bl, 1); checknext(ls, TK_DO); block(ls); luaK_jumpto(fs, whileinit); check_match(ls, TK_END, TK_WHILE, line); leaveblock(fs); luaK_patchtohere(fs, condexit); /* false conditions finish the loop */ } static void repeatstat (LexState *ls, int line) { /* repeatstat -> REPEAT block UNTIL cond */ int condexit; FuncState *fs = ls->fs; int repeat_init = luaK_getlabel(fs); BlockCnt bl1, bl2; enterblock(fs, &bl1, 1); /* loop block */ enterblock(fs, &bl2, 0); /* scope block */ luaX_next(ls); /* skip REPEAT */ statlist(ls); check_match(ls, TK_UNTIL, TK_REPEAT, line); condexit = cond(ls); /* read condition (inside scope block) */ leaveblock(fs); /* finish scope */ if (bl2.upval) { /* upvalues? */ int exit = luaK_jump(fs); /* normal exit must jump over fix */ luaK_patchtohere(fs, condexit); /* repetition must close upvalues */ luaK_codeABC(fs, OP_CLOSE, reglevel(fs, bl2.nactvar), 0, 0); condexit = luaK_jump(fs); /* repeat after closing upvalues */ luaK_patchtohere(fs, exit); /* normal exit comes to here */ } luaK_patchlist(fs, condexit, repeat_init); /* close the loop */ leaveblock(fs); /* finish loop */ } /* ** Read an expression and generate code to put its results in next ** stack slot. ** */ static void exp1 (LexState *ls) { expdesc e; expr(ls, &e); luaK_exp2nextreg(ls->fs, &e); lua_assert(e.k == VNONRELOC); } /* ** Fix for instruction at position 'pc' to jump to 'dest'. ** (Jump addresses are relative in Lua). 'back' true means ** a back jump. */ static void fixforjump (FuncState *fs, int pc, int dest, int back) { Instruction *jmp = &fs->f->code[pc]; int offset = dest - (pc + 1); if (back) offset = -offset; if (l_unlikely(offset > MAXARG_Bx)) luaX_syntaxerror(fs->ls, "control structure too long"); SETARG_Bx(*jmp, offset); } /* ** Generate code for a 'for' loop. */ static void forbody (LexState *ls, int base, int line, int nvars, int isgen) { /* forbody -> DO block */ static const OpCode forprep[2] = {OP_FORPREP, OP_TFORPREP}; static const OpCode forloop[2] = {OP_FORLOOP, OP_TFORLOOP}; BlockCnt bl; FuncState *fs = ls->fs; int prep, endfor; checknext(ls, TK_DO); prep = luaK_codeABx(fs, forprep[isgen], base, 0); enterblock(fs, &bl, 0); /* scope for declared variables */ adjustlocalvars(ls, nvars); luaK_reserveregs(fs, nvars); block(ls); leaveblock(fs); /* end of scope for declared variables */ fixforjump(fs, prep, luaK_getlabel(fs), 0); if (isgen) { /* generic for? */ luaK_codeABC(fs, OP_TFORCALL, base, 0, nvars); luaK_fixline(fs, line); } endfor = luaK_codeABx(fs, forloop[isgen], base, 0); fixforjump(fs, endfor, prep + 1, 1); luaK_fixline(fs, line); } static void fornum (LexState *ls, TString *varname, int line) { /* fornum -> NAME = exp,exp[,exp] forbody */ FuncState *fs = ls->fs; int base = fs->freereg; new_localvarliteral(ls, "(for state)"); new_localvarliteral(ls, "(for state)"); new_localvarliteral(ls, "(for state)"); new_localvar(ls, varname); checknext(ls, '='); exp1(ls); /* initial value */ checknext(ls, ','); exp1(ls); /* limit */ if (testnext(ls, ',')) exp1(ls); /* optional step */ else { /* default step = 1 */ luaK_int(fs, fs->freereg, 1); luaK_reserveregs(fs, 1); } adjustlocalvars(ls, 3); /* control variables */ forbody(ls, base, line, 1, 0); } static void forlist (LexState *ls, TString *indexname) { /* forlist -> NAME {,NAME} IN explist forbody */ FuncState *fs = ls->fs; expdesc e; int nvars = 5; /* gen, state, control, toclose, 'indexname' */ int line; int base = fs->freereg; /* create control variables */ new_localvarliteral(ls, "(for state)"); new_localvarliteral(ls, "(for state)"); new_localvarliteral(ls, "(for state)"); new_localvarliteral(ls, "(for state)"); /* create declared variables */ new_localvar(ls, indexname); while (testnext(ls, ',')) { new_localvar(ls, str_checkname(ls)); nvars++; } checknext(ls, TK_IN); line = ls->linenumber; adjust_assign(ls, 4, explist(ls, &e), &e); adjustlocalvars(ls, 4); /* control variables */ marktobeclosed(fs); /* last control var. must be closed */ luaK_checkstack(fs, 3); /* extra space to call generator */ forbody(ls, base, line, nvars - 4, 1); } static void forstat (LexState *ls, int line) { /* forstat -> FOR (fornum | forlist) END */ FuncState *fs = ls->fs; TString *varname; BlockCnt bl; enterblock(fs, &bl, 1); /* scope for loop and control variables */ luaX_next(ls); /* skip 'for' */ varname = str_checkname(ls); /* first variable name */ switch (ls->t.token) { case '=': fornum(ls, varname, line); break; case ',': case TK_IN: forlist(ls, varname); break; default: luaX_syntaxerror(ls, "'=' or 'in' expected"); } check_match(ls, TK_END, TK_FOR, line); leaveblock(fs); /* loop scope ('break' jumps to this point) */ } static void test_then_block (LexState *ls, int *escapelist) { /* test_then_block -> [IF | ELSEIF] cond THEN block */ BlockCnt bl; FuncState *fs = ls->fs; expdesc v; int jf; /* instruction to skip 'then' code (if condition is false) */ luaX_next(ls); /* skip IF or ELSEIF */ expr(ls, &v); /* read condition */ checknext(ls, TK_THEN); if (ls->t.token == TK_BREAK) { /* 'if x then break' ? */ int line = ls->linenumber; luaK_goiffalse(ls->fs, &v); /* will jump if condition is true */ luaX_next(ls); /* skip 'break' */ enterblock(fs, &bl, 0); /* must enter block before 'goto' */ newgotoentry(ls, luaS_newliteral(ls->L, "break"), line, v.t); while (testnext(ls, ';')) {} /* skip semicolons */ if (block_follow(ls, 0)) { /* jump is the entire block? */ leaveblock(fs); return; /* and that is it */ } else /* must skip over 'then' part if condition is false */ jf = luaK_jump(fs); } else { /* regular case (not a break) */ luaK_goiftrue(ls->fs, &v); /* skip over block if condition is false */ enterblock(fs, &bl, 0); jf = v.f; } statlist(ls); /* 'then' part */ leaveblock(fs); if (ls->t.token == TK_ELSE || ls->t.token == TK_ELSEIF) /* followed by 'else'/'elseif'? */ luaK_concat(fs, escapelist, luaK_jump(fs)); /* must jump over it */ luaK_patchtohere(fs, jf); } static void ifstat (LexState *ls, int line) { /* ifstat -> IF cond THEN block {ELSEIF cond THEN block} [ELSE block] END */ FuncState *fs = ls->fs; int escapelist = NO_JUMP; /* exit list for finished parts */ test_then_block(ls, &escapelist); /* IF cond THEN block */ while (ls->t.token == TK_ELSEIF) test_then_block(ls, &escapelist); /* ELSEIF cond THEN block */ if (testnext(ls, TK_ELSE)) block(ls); /* 'else' part */ check_match(ls, TK_END, TK_IF, line); luaK_patchtohere(fs, escapelist); /* patch escape list to 'if' end */ } static void localfunc (LexState *ls) { expdesc b; FuncState *fs = ls->fs; int fvar = fs->nactvar; /* function's variable index */ new_localvar(ls, str_checkname(ls)); /* new local variable */ adjustlocalvars(ls, 1); /* enter its scope */ body(ls, &b, 0, ls->linenumber); /* function created in next register */ /* debug information will only see the variable after this point! */ localdebuginfo(fs, fvar)->startpc = fs->pc; } static int getlocalattribute (LexState *ls) { /* ATTRIB -> ['<' Name '>'] */ if (testnext(ls, '<')) { const char *attr = getstr(str_checkname(ls)); checknext(ls, '>'); if (strcmp(attr, "const") == 0) return RDKCONST; /* read-only variable */ else if (strcmp(attr, "close") == 0) return RDKTOCLOSE; /* to-be-closed variable */ else luaK_semerror(ls, luaO_pushfstring(ls->L, "unknown attribute '%s'", attr)); } return VDKREG; /* regular variable */ } static void checktoclose (FuncState *fs, int level) { if (level != -1) { /* is there a to-be-closed variable? */ marktobeclosed(fs); luaK_codeABC(fs, OP_TBC, reglevel(fs, level), 0, 0); } } static void localstat (LexState *ls) { /* stat -> LOCAL NAME ATTRIB { ',' NAME ATTRIB } ['=' explist] */ FuncState *fs = ls->fs; int toclose = -1; /* index of to-be-closed variable (if any) */ Vardesc *var; /* last variable */ int vidx, kind; /* index and kind of last variable */ int nvars = 0; int nexps; expdesc e; do { vidx = new_localvar(ls, str_checkname(ls)); kind = getlocalattribute(ls); getlocalvardesc(fs, vidx)->vd.kind = kind; if (kind == RDKTOCLOSE) { /* to-be-closed? */ if (toclose != -1) /* one already present? */ luaK_semerror(ls, "multiple to-be-closed variables in local list"); toclose = fs->nactvar + nvars; } nvars++; } while (testnext(ls, ',')); if (testnext(ls, '=')) nexps = explist(ls, &e); else { e.k = VVOID; nexps = 0; } var = getlocalvardesc(fs, vidx); /* get last variable */ if (nvars == nexps && /* no adjustments? */ var->vd.kind == RDKCONST && /* last variable is const? */ luaK_exp2const(fs, &e, &var->k)) { /* compile-time constant? */ var->vd.kind = RDKCTC; /* variable is a compile-time constant */ adjustlocalvars(ls, nvars - 1); /* exclude last variable */ fs->nactvar++; /* but count it */ } else { adjust_assign(ls, nvars, nexps, &e); adjustlocalvars(ls, nvars); } checktoclose(fs, toclose); } static int funcname (LexState *ls, expdesc *v) { /* funcname -> NAME {fieldsel} [':' NAME] */ int ismethod = 0; singlevar(ls, v); while (ls->t.token == '.') fieldsel(ls, v); if (ls->t.token == ':') { ismethod = 1; fieldsel(ls, v); } return ismethod; } static void funcstat (LexState *ls, int line) { /* funcstat -> FUNCTION funcname body */ int ismethod; expdesc v, b; luaX_next(ls); /* skip FUNCTION */ ismethod = funcname(ls, &v); body(ls, &b, ismethod, line); check_readonly(ls, &v); luaK_storevar(ls->fs, &v, &b); luaK_fixline(ls->fs, line); /* definition "happens" in the first line */ } static void exprstat (LexState *ls) { /* stat -> func | assignment */ FuncState *fs = ls->fs; struct LHS_assign v; suffixedexp(ls, &v.v); if (ls->t.token == '=' || ls->t.token == ',') { /* stat -> assignment ? */ v.prev = NULL; restassign(ls, &v, 1); } else { /* stat -> func */ Instruction *inst; check_condition(ls, v.v.k == VCALL, "syntax error"); inst = &getinstruction(fs, &v.v); SETARG_C(*inst, 1); /* call statement uses no results */ } } static void retstat (LexState *ls) { /* stat -> RETURN [explist] [';'] */ FuncState *fs = ls->fs; expdesc e; int nret; /* number of values being returned */ int first = luaY_nvarstack(fs); /* first slot to be returned */ if (block_follow(ls, 1) || ls->t.token == ';') nret = 0; /* return no values */ else { nret = explist(ls, &e); /* optional return values */ if (hasmultret(e.k)) { luaK_setmultret(fs, &e); if (e.k == VCALL && nret == 1 && !fs->bl->insidetbc) { /* tail call? */ SET_OPCODE(getinstruction(fs,&e), OP_TAILCALL); lua_assert(GETARG_A(getinstruction(fs,&e)) == luaY_nvarstack(fs)); } nret = LUA_MULTRET; /* return all values */ } else { if (nret == 1) /* only one single value? */ first = luaK_exp2anyreg(fs, &e); /* can use original slot */ else { /* values must go to the top of the stack */ luaK_exp2nextreg(fs, &e); lua_assert(nret == fs->freereg - first); } } } luaK_ret(fs, first, nret); testnext(ls, ';'); /* skip optional semicolon */ } static void statement (LexState *ls) { int line = ls->linenumber; /* may be needed for error messages */ enterlevel(ls); switch (ls->t.token) { case ';': { /* stat -> ';' (empty statement) */ luaX_next(ls); /* skip ';' */ break; } case TK_IF: { /* stat -> ifstat */ ifstat(ls, line); break; } case TK_WHILE: { /* stat -> whilestat */ whilestat(ls, line); break; } case TK_DO: { /* stat -> DO block END */ luaX_next(ls); /* skip DO */ block(ls); check_match(ls, TK_END, TK_DO, line); break; } case TK_FOR: { /* stat -> forstat */ forstat(ls, line); break; } case TK_REPEAT: { /* stat -> repeatstat */ repeatstat(ls, line); break; } case TK_FUNCTION: { /* stat -> funcstat */ funcstat(ls, line); break; } case TK_LOCAL: { /* stat -> localstat */ luaX_next(ls); /* skip LOCAL */ if (testnext(ls, TK_FUNCTION)) /* local function? */ localfunc(ls); else localstat(ls); break; } case TK_DBCOLON: { /* stat -> label */ luaX_next(ls); /* skip double colon */ labelstat(ls, str_checkname(ls), line); break; } case TK_RETURN: { /* stat -> retstat */ luaX_next(ls); /* skip RETURN */ retstat(ls); break; } case TK_BREAK: { /* stat -> breakstat */ breakstat(ls); break; } case TK_GOTO: { /* stat -> 'goto' NAME */ luaX_next(ls); /* skip 'goto' */ gotostat(ls); break; } default: { /* stat -> func | assignment */ exprstat(ls); break; } } lua_assert(ls->fs->f->maxstacksize >= ls->fs->freereg && ls->fs->freereg >= luaY_nvarstack(ls->fs)); ls->fs->freereg = luaY_nvarstack(ls->fs); /* free registers */ leavelevel(ls); } /* }====================================================================== */ /* ** compiles the main function, which is a regular vararg function with an ** upvalue named LUA_ENV */ static void mainfunc (LexState *ls, FuncState *fs) { BlockCnt bl; Upvaldesc *env; open_func(ls, fs, &bl); setvararg(fs, 0); /* main function is always declared vararg */ env = allocupvalue(fs); /* ...set environment upvalue */ env->instack = 1; env->idx = 0; env->kind = VDKREG; env->name = ls->envn; luaC_objbarrier(ls->L, fs->f, env->name); luaX_next(ls); /* read first token */ statlist(ls); /* parse main body */ check(ls, TK_EOS); close_func(ls); } LClosure *luaY_parser (lua_State *L, ZIO *z, Mbuffer *buff, Dyndata *dyd, const char *name, int firstchar) { LexState lexstate; FuncState funcstate; LClosure *cl = luaF_newLclosure(L, 1); /* create main closure */ setclLvalue2s(L, L->top, cl); /* anchor it (to avoid being collected) */ luaD_inctop(L); lexstate.h = luaH_new(L); /* create table for scanner */ sethvalue2s(L, L->top, lexstate.h); /* anchor it */ luaD_inctop(L); funcstate.f = cl->p = luaF_newproto(L); luaC_objbarrier(L, cl, cl->p); funcstate.f->source = luaS_new(L, name); /* create and anchor TString */ luaC_objbarrier(L, funcstate.f, funcstate.f->source); lexstate.buff = buff; lexstate.dyd = dyd; dyd->actvar.n = dyd->gt.n = dyd->label.n = 0; luaX_setinput(L, &lexstate, z, funcstate.f->source, firstchar); mainfunc(&lexstate, &funcstate); lua_assert(!funcstate.prev && funcstate.nups == 1 && !lexstate.fs); /* all scopes should be correctly finished */ lua_assert(dyd->actvar.n == 0 && dyd->gt.n == 0 && dyd->label.n == 0); L->top--; /* remove scanner's table */ return cl; /* closure is on the stack, too */ } /* ** $Id: ldebug.c $ ** Debug Interface ** See Copyright Notice in lua.h */ #define ldebug_c #define LUA_CORE /*#include "lprefix.h"*/ #include #include #include /*#include "lua.h"*/ /*#include "lapi.h"*/ /*#include "lcode.h"*/ /*#include "ldebug.h"*/ /*#include "ldo.h"*/ /*#include "lfunc.h"*/ /*#include "lobject.h"*/ /*#include "lopcodes.h"*/ /*#include "lstate.h"*/ /*#include "lstring.h"*/ /*#include "ltable.h"*/ /*#include "ltm.h"*/ /*#include "lvm.h"*/ #define noLuaClosure(f) ((f) == NULL || (f)->c.tt == LUA_VCCL) static const char *funcnamefromcall (lua_State *L, CallInfo *ci, const char **name); static int currentpc (CallInfo *ci) { lua_assert(isLua(ci)); return pcRel(ci->u.l.savedpc, ci_func(ci)->p); } /* ** Get a "base line" to find the line corresponding to an instruction. ** Base lines are regularly placed at MAXIWTHABS intervals, so usually ** an integer division gets the right place. When the source file has ** large sequences of empty/comment lines, it may need extra entries, ** so the original estimate needs a correction. ** If the original estimate is -1, the initial 'if' ensures that the ** 'while' will run at least once. ** The assertion that the estimate is a lower bound for the correct base ** is valid as long as the debug info has been generated with the same ** value for MAXIWTHABS or smaller. (Previous releases use a little ** smaller value.) */ static int getbaseline (const Proto *f, int pc, int *basepc) { if (f->sizeabslineinfo == 0 || pc < f->abslineinfo[0].pc) { *basepc = -1; /* start from the beginning */ return f->linedefined; } else { int i = cast_uint(pc) / MAXIWTHABS - 1; /* get an estimate */ /* estimate must be a lower bound of the correct base */ lua_assert(i < 0 || (i < f->sizeabslineinfo && f->abslineinfo[i].pc <= pc)); while (i + 1 < f->sizeabslineinfo && pc >= f->abslineinfo[i + 1].pc) i++; /* low estimate; adjust it */ *basepc = f->abslineinfo[i].pc; return f->abslineinfo[i].line; } } /* ** Get the line corresponding to instruction 'pc' in function 'f'; ** first gets a base line and from there does the increments until ** the desired instruction. */ int luaG_getfuncline (const Proto *f, int pc) { if (f->lineinfo == NULL) /* no debug information? */ return -1; else { int basepc; int baseline = getbaseline(f, pc, &basepc); while (basepc++ < pc) { /* walk until given instruction */ lua_assert(f->lineinfo[basepc] != ABSLINEINFO); baseline += f->lineinfo[basepc]; /* correct line */ } return baseline; } } static int getcurrentline (CallInfo *ci) { return luaG_getfuncline(ci_func(ci)->p, currentpc(ci)); } /* ** Set 'trap' for all active Lua frames. ** This function can be called during a signal, under "reasonable" ** assumptions. A new 'ci' is completely linked in the list before it ** becomes part of the "active" list, and we assume that pointers are ** atomic; see comment in next function. ** (A compiler doing interprocedural optimizations could, theoretically, ** reorder memory writes in such a way that the list could be ** temporarily broken while inserting a new element. We simply assume it ** has no good reasons to do that.) */ static void settraps (CallInfo *ci) { for (; ci != NULL; ci = ci->previous) if (isLua(ci)) ci->u.l.trap = 1; } /* ** This function can be called during a signal, under "reasonable" ** assumptions. ** Fields 'basehookcount' and 'hookcount' (set by 'resethookcount') ** are for debug only, and it is no problem if they get arbitrary ** values (causes at most one wrong hook call). 'hookmask' is an atomic ** value. We assume that pointers are atomic too (e.g., gcc ensures that ** for all platforms where it runs). Moreover, 'hook' is always checked ** before being called (see 'luaD_hook'). */ LUA_API void lua_sethook (lua_State *L, lua_Hook func, int mask, int count) { if (func == NULL || mask == 0) { /* turn off hooks? */ mask = 0; func = NULL; } L->hook = func; L->basehookcount = count; resethookcount(L); L->hookmask = cast_byte(mask); if (mask) settraps(L->ci); /* to trace inside 'luaV_execute' */ } LUA_API lua_Hook lua_gethook (lua_State *L) { return L->hook; } LUA_API int lua_gethookmask (lua_State *L) { return L->hookmask; } LUA_API int lua_gethookcount (lua_State *L) { return L->basehookcount; } LUA_API int lua_getstack (lua_State *L, int level, lua_Debug *ar) { int status; CallInfo *ci; if (level < 0) return 0; /* invalid (negative) level */ lua_lock(L); for (ci = L->ci; level > 0 && ci != &L->base_ci; ci = ci->previous) level--; if (level == 0 && ci != &L->base_ci) { /* level found? */ status = 1; ar->i_ci = ci; } else status = 0; /* no such level */ lua_unlock(L); return status; } static const char *upvalname (const Proto *p, int uv) { TString *s = check_exp(uv < p->sizeupvalues, p->upvalues[uv].name); if (s == NULL) return "?"; else return getstr(s); } static const char *findvararg (CallInfo *ci, int n, StkId *pos) { if (clLvalue(s2v(ci->func))->p->is_vararg) { int nextra = ci->u.l.nextraargs; if (n >= -nextra) { /* 'n' is negative */ *pos = ci->func - nextra - (n + 1); return "(vararg)"; /* generic name for any vararg */ } } return NULL; /* no such vararg */ } const char *luaG_findlocal (lua_State *L, CallInfo *ci, int n, StkId *pos) { StkId base = ci->func + 1; const char *name = NULL; if (isLua(ci)) { if (n < 0) /* access to vararg values? */ return findvararg(ci, n, pos); else name = luaF_getlocalname(ci_func(ci)->p, n, currentpc(ci)); } if (name == NULL) { /* no 'standard' name? */ StkId limit = (ci == L->ci) ? L->top : ci->next->func; if (limit - base >= n && n > 0) { /* is 'n' inside 'ci' stack? */ /* generic name for any valid slot */ name = isLua(ci) ? "(temporary)" : "(C temporary)"; } else return NULL; /* no name */ } if (pos) *pos = base + (n - 1); return name; } LUA_API const char *lua_getlocal (lua_State *L, const lua_Debug *ar, int n) { const char *name; lua_lock(L); if (ar == NULL) { /* information about non-active function? */ if (!isLfunction(s2v(L->top - 1))) /* not a Lua function? */ name = NULL; else /* consider live variables at function start (parameters) */ name = luaF_getlocalname(clLvalue(s2v(L->top - 1))->p, n, 0); } else { /* active function; get information through 'ar' */ StkId pos = NULL; /* to avoid warnings */ name = luaG_findlocal(L, ar->i_ci, n, &pos); if (name) { setobjs2s(L, L->top, pos); api_incr_top(L); } } lua_unlock(L); return name; } LUA_API const char *lua_setlocal (lua_State *L, const lua_Debug *ar, int n) { StkId pos = NULL; /* to avoid warnings */ const char *name; lua_lock(L); name = luaG_findlocal(L, ar->i_ci, n, &pos); if (name) { setobjs2s(L, pos, L->top - 1); L->top--; /* pop value */ } lua_unlock(L); return name; } static void funcinfo (lua_Debug *ar, Closure *cl) { if (noLuaClosure(cl)) { ar->source = "=[C]"; ar->srclen = LL("=[C]"); ar->linedefined = -1; ar->lastlinedefined = -1; ar->what = "C"; } else { const Proto *p = cl->l.p; if (p->source) { ar->source = getstr(p->source); ar->srclen = tsslen(p->source); } else { ar->source = "=?"; ar->srclen = LL("=?"); } ar->linedefined = p->linedefined; ar->lastlinedefined = p->lastlinedefined; ar->what = (ar->linedefined == 0) ? "main" : "Lua"; } luaO_chunkid(ar->short_src, ar->source, ar->srclen); } static int nextline (const Proto *p, int currentline, int pc) { if (p->lineinfo[pc] != ABSLINEINFO) return currentline + p->lineinfo[pc]; else return luaG_getfuncline(p, pc); } static void collectvalidlines (lua_State *L, Closure *f) { if (noLuaClosure(f)) { setnilvalue(s2v(L->top)); api_incr_top(L); } else { int i; TValue v; const Proto *p = f->l.p; int currentline = p->linedefined; Table *t = luaH_new(L); /* new table to store active lines */ sethvalue2s(L, L->top, t); /* push it on stack */ api_incr_top(L); setbtvalue(&v); /* boolean 'true' to be the value of all indices */ if (!p->is_vararg) /* regular function? */ i = 0; /* consider all instructions */ else { /* vararg function */ lua_assert(GET_OPCODE(p->code[0]) == OP_VARARGPREP); currentline = nextline(p, currentline, 0); i = 1; /* skip first instruction (OP_VARARGPREP) */ } for (; i < p->sizelineinfo; i++) { /* for each instruction */ currentline = nextline(p, currentline, i); /* get its line */ luaH_setint(L, t, currentline, &v); /* table[line] = true */ } } } static const char *getfuncname (lua_State *L, CallInfo *ci, const char **name) { /* calling function is a known function? */ if (ci != NULL && !(ci->callstatus & CIST_TAIL)) return funcnamefromcall(L, ci->previous, name); else return NULL; /* no way to find a name */ } static int auxgetinfo (lua_State *L, const char *what, lua_Debug *ar, Closure *f, CallInfo *ci) { int status = 1; for (; *what; what++) { switch (*what) { case 'S': { funcinfo(ar, f); break; } case 'l': { ar->currentline = (ci && isLua(ci)) ? getcurrentline(ci) : -1; break; } case 'u': { ar->nups = (f == NULL) ? 0 : f->c.nupvalues; if (noLuaClosure(f)) { ar->isvararg = 1; ar->nparams = 0; } else { ar->isvararg = f->l.p->is_vararg; ar->nparams = f->l.p->numparams; } break; } case 't': { ar->istailcall = (ci) ? ci->callstatus & CIST_TAIL : 0; break; } case 'n': { ar->namewhat = getfuncname(L, ci, &ar->name); if (ar->namewhat == NULL) { ar->namewhat = ""; /* not found */ ar->name = NULL; } break; } case 'r': { if (ci == NULL || !(ci->callstatus & CIST_TRAN)) ar->ftransfer = ar->ntransfer = 0; else { ar->ftransfer = ci->u2.transferinfo.ftransfer; ar->ntransfer = ci->u2.transferinfo.ntransfer; } break; } case 'L': case 'f': /* handled by lua_getinfo */ break; default: status = 0; /* invalid option */ } } return status; } LUA_API int lua_getinfo (lua_State *L, const char *what, lua_Debug *ar) { int status; Closure *cl; CallInfo *ci; TValue *func; lua_lock(L); if (*what == '>') { ci = NULL; func = s2v(L->top - 1); api_check(L, ttisfunction(func), "function expected"); what++; /* skip the '>' */ L->top--; /* pop function */ } else { ci = ar->i_ci; func = s2v(ci->func); lua_assert(ttisfunction(func)); } cl = ttisclosure(func) ? clvalue(func) : NULL; status = auxgetinfo(L, what, ar, cl, ci); if (strchr(what, 'f')) { setobj2s(L, L->top, func); api_incr_top(L); } if (strchr(what, 'L')) collectvalidlines(L, cl); lua_unlock(L); return status; } /* ** {====================================================== ** Symbolic Execution ** ======================================================= */ static const char *getobjname (const Proto *p, int lastpc, int reg, const char **name); /* ** Find a "name" for the constant 'c'. */ static void kname (const Proto *p, int c, const char **name) { TValue *kvalue = &p->k[c]; *name = (ttisstring(kvalue)) ? svalue(kvalue) : "?"; } /* ** Find a "name" for the register 'c'. */ static void rname (const Proto *p, int pc, int c, const char **name) { const char *what = getobjname(p, pc, c, name); /* search for 'c' */ if (!(what && *what == 'c')) /* did not find a constant name? */ *name = "?"; } /* ** Find a "name" for a 'C' value in an RK instruction. */ static void rkname (const Proto *p, int pc, Instruction i, const char **name) { int c = GETARG_C(i); /* key index */ if (GETARG_k(i)) /* is 'c' a constant? */ kname(p, c, name); else /* 'c' is a register */ rname(p, pc, c, name); } static int filterpc (int pc, int jmptarget) { if (pc < jmptarget) /* is code conditional (inside a jump)? */ return -1; /* cannot know who sets that register */ else return pc; /* current position sets that register */ } /* ** Try to find last instruction before 'lastpc' that modified register 'reg'. */ static int findsetreg (const Proto *p, int lastpc, int reg) { int pc; int setreg = -1; /* keep last instruction that changed 'reg' */ int jmptarget = 0; /* any code before this address is conditional */ if (testMMMode(GET_OPCODE(p->code[lastpc]))) lastpc--; /* previous instruction was not actually executed */ for (pc = 0; pc < lastpc; pc++) { Instruction i = p->code[pc]; OpCode op = GET_OPCODE(i); int a = GETARG_A(i); int change; /* true if current instruction changed 'reg' */ switch (op) { case OP_LOADNIL: { /* set registers from 'a' to 'a+b' */ int b = GETARG_B(i); change = (a <= reg && reg <= a + b); break; } case OP_TFORCALL: { /* affect all regs above its base */ change = (reg >= a + 2); break; } case OP_CALL: case OP_TAILCALL: { /* affect all registers above base */ change = (reg >= a); break; } case OP_JMP: { /* doesn't change registers, but changes 'jmptarget' */ int b = GETARG_sJ(i); int dest = pc + 1 + b; /* jump does not skip 'lastpc' and is larger than current one? */ if (dest <= lastpc && dest > jmptarget) jmptarget = dest; /* update 'jmptarget' */ change = 0; break; } default: /* any instruction that sets A */ change = (testAMode(op) && reg == a); break; } if (change) setreg = filterpc(pc, jmptarget); } return setreg; } /* ** Check whether table being indexed by instruction 'i' is the ** environment '_ENV' */ static const char *gxf (const Proto *p, int pc, Instruction i, int isup) { int t = GETARG_B(i); /* table index */ const char *name; /* name of indexed variable */ if (isup) /* is an upvalue? */ name = upvalname(p, t); else getobjname(p, pc, t, &name); return (name && strcmp(name, LUA_ENV) == 0) ? "global" : "field"; } static const char *getobjname (const Proto *p, int lastpc, int reg, const char **name) { int pc; *name = luaF_getlocalname(p, reg + 1, lastpc); if (*name) /* is a local? */ return "local"; /* else try symbolic execution */ pc = findsetreg(p, lastpc, reg); if (pc != -1) { /* could find instruction? */ Instruction i = p->code[pc]; OpCode op = GET_OPCODE(i); switch (op) { case OP_MOVE: { int b = GETARG_B(i); /* move from 'b' to 'a' */ if (b < GETARG_A(i)) return getobjname(p, pc, b, name); /* get name for 'b' */ break; } case OP_GETTABUP: { int k = GETARG_C(i); /* key index */ kname(p, k, name); return gxf(p, pc, i, 1); } case OP_GETTABLE: { int k = GETARG_C(i); /* key index */ rname(p, pc, k, name); return gxf(p, pc, i, 0); } case OP_GETI: { *name = "integer index"; return "field"; } case OP_GETFIELD: { int k = GETARG_C(i); /* key index */ kname(p, k, name); return gxf(p, pc, i, 0); } case OP_GETUPVAL: { *name = upvalname(p, GETARG_B(i)); return "upvalue"; } case OP_LOADK: case OP_LOADKX: { int b = (op == OP_LOADK) ? GETARG_Bx(i) : GETARG_Ax(p->code[pc + 1]); if (ttisstring(&p->k[b])) { *name = svalue(&p->k[b]); return "constant"; } break; } case OP_SELF: { rkname(p, pc, i, name); return "method"; } default: break; /* go through to return NULL */ } } return NULL; /* could not find reasonable name */ } /* ** Try to find a name for a function based on the code that called it. ** (Only works when function was called by a Lua function.) ** Returns what the name is (e.g., "for iterator", "method", ** "metamethod") and sets '*name' to point to the name. */ static const char *funcnamefromcode (lua_State *L, const Proto *p, int pc, const char **name) { TMS tm = (TMS)0; /* (initial value avoids warnings) */ Instruction i = p->code[pc]; /* calling instruction */ switch (GET_OPCODE(i)) { case OP_CALL: case OP_TAILCALL: return getobjname(p, pc, GETARG_A(i), name); /* get function name */ case OP_TFORCALL: { /* for iterator */ *name = "for iterator"; return "for iterator"; } /* other instructions can do calls through metamethods */ case OP_SELF: case OP_GETTABUP: case OP_GETTABLE: case OP_GETI: case OP_GETFIELD: tm = TM_INDEX; break; case OP_SETTABUP: case OP_SETTABLE: case OP_SETI: case OP_SETFIELD: tm = TM_NEWINDEX; break; case OP_MMBIN: case OP_MMBINI: case OP_MMBINK: { tm = cast(TMS, GETARG_C(i)); break; } case OP_UNM: tm = TM_UNM; break; case OP_BNOT: tm = TM_BNOT; break; case OP_LEN: tm = TM_LEN; break; case OP_CONCAT: tm = TM_CONCAT; break; case OP_EQ: tm = TM_EQ; break; /* no cases for OP_EQI and OP_EQK, as they don't call metamethods */ case OP_LT: case OP_LTI: case OP_GTI: tm = TM_LT; break; case OP_LE: case OP_LEI: case OP_GEI: tm = TM_LE; break; case OP_CLOSE: case OP_RETURN: tm = TM_CLOSE; break; default: return NULL; /* cannot find a reasonable name */ } *name = getstr(G(L)->tmname[tm]) + 2; return "metamethod"; } /* ** Try to find a name for a function based on how it was called. */ static const char *funcnamefromcall (lua_State *L, CallInfo *ci, const char **name) { if (ci->callstatus & CIST_HOOKED) { /* was it called inside a hook? */ *name = "?"; return "hook"; } else if (ci->callstatus & CIST_FIN) { /* was it called as a finalizer? */ *name = "__gc"; return "metamethod"; /* report it as such */ } else if (isLua(ci)) return funcnamefromcode(L, ci_func(ci)->p, currentpc(ci), name); else return NULL; } /* }====================================================== */ /* ** Check whether pointer 'o' points to some value in the stack ** frame of the current function. Because 'o' may not point to a ** value in this stack, we cannot compare it with the region ** boundaries (undefined behaviour in ISO C). */ static int isinstack (CallInfo *ci, const TValue *o) { StkId pos; for (pos = ci->func + 1; pos < ci->top; pos++) { if (o == s2v(pos)) return 1; } return 0; /* not found */ } /* ** Checks whether value 'o' came from an upvalue. (That can only happen ** with instructions OP_GETTABUP/OP_SETTABUP, which operate directly on ** upvalues.) */ static const char *getupvalname (CallInfo *ci, const TValue *o, const char **name) { LClosure *c = ci_func(ci); int i; for (i = 0; i < c->nupvalues; i++) { if (c->upvals[i]->v == o) { *name = upvalname(c->p, i); return "upvalue"; } } return NULL; } static const char *formatvarinfo (lua_State *L, const char *kind, const char *name) { if (kind == NULL) return ""; /* no information */ else return luaO_pushfstring(L, " (%s '%s')", kind, name); } /* ** Build a string with a "description" for the value 'o', such as ** "variable 'x'" or "upvalue 'y'". */ static const char *varinfo (lua_State *L, const TValue *o) { CallInfo *ci = L->ci; const char *name = NULL; /* to avoid warnings */ const char *kind = NULL; if (isLua(ci)) { kind = getupvalname(ci, o, &name); /* check whether 'o' is an upvalue */ if (!kind && isinstack(ci, o)) /* no? try a register */ kind = getobjname(ci_func(ci)->p, currentpc(ci), cast_int(cast(StkId, o) - (ci->func + 1)), &name); } return formatvarinfo(L, kind, name); } /* ** Raise a type error */ static l_noret typeerror (lua_State *L, const TValue *o, const char *op, const char *extra) { const char *t = luaT_objtypename(L, o); luaG_runerror(L, "attempt to %s a %s value%s", op, t, extra); } /* ** Raise a type error with "standard" information about the faulty ** object 'o' (using 'varinfo'). */ l_noret luaG_typeerror (lua_State *L, const TValue *o, const char *op) { typeerror(L, o, op, varinfo(L, o)); } /* ** Raise an error for calling a non-callable object. Try to find a name ** for the object based on how it was called ('funcnamefromcall'); if it ** cannot get a name there, try 'varinfo'. */ l_noret luaG_callerror (lua_State *L, const TValue *o) { CallInfo *ci = L->ci; const char *name = NULL; /* to avoid warnings */ const char *kind = funcnamefromcall(L, ci, &name); const char *extra = kind ? formatvarinfo(L, kind, name) : varinfo(L, o); typeerror(L, o, "call", extra); } l_noret luaG_forerror (lua_State *L, const TValue *o, const char *what) { luaG_runerror(L, "bad 'for' %s (number expected, got %s)", what, luaT_objtypename(L, o)); } l_noret luaG_concaterror (lua_State *L, const TValue *p1, const TValue *p2) { if (ttisstring(p1) || cvt2str(p1)) p1 = p2; luaG_typeerror(L, p1, "concatenate"); } l_noret luaG_opinterror (lua_State *L, const TValue *p1, const TValue *p2, const char *msg) { if (!ttisnumber(p1)) /* first operand is wrong? */ p2 = p1; /* now second is wrong */ luaG_typeerror(L, p2, msg); } /* ** Error when both values are convertible to numbers, but not to integers */ l_noret luaG_tointerror (lua_State *L, const TValue *p1, const TValue *p2) { lua_Integer temp; if (!luaV_tointegerns(p1, &temp, LUA_FLOORN2I)) p2 = p1; luaG_runerror(L, "number%s has no integer representation", varinfo(L, p2)); } l_noret luaG_ordererror (lua_State *L, const TValue *p1, const TValue *p2) { const char *t1 = luaT_objtypename(L, p1); const char *t2 = luaT_objtypename(L, p2); if (strcmp(t1, t2) == 0) luaG_runerror(L, "attempt to compare two %s values", t1); else luaG_runerror(L, "attempt to compare %s with %s", t1, t2); } /* add src:line information to 'msg' */ const char *luaG_addinfo (lua_State *L, const char *msg, TString *src, int line) { char buff[LUA_IDSIZE]; if (src) luaO_chunkid(buff, getstr(src), tsslen(src)); else { /* no source available; use "?" instead */ buff[0] = '?'; buff[1] = '\0'; } return luaO_pushfstring(L, "%s:%d: %s", buff, line, msg); } l_noret luaG_errormsg (lua_State *L) { if (L->errfunc != 0) { /* is there an error handling function? */ StkId errfunc = restorestack(L, L->errfunc); lua_assert(ttisfunction(s2v(errfunc))); setobjs2s(L, L->top, L->top - 1); /* move argument */ setobjs2s(L, L->top - 1, errfunc); /* push function */ L->top++; /* assume EXTRA_STACK */ luaD_callnoyield(L, L->top - 2, 1); /* call it */ } luaD_throw(L, LUA_ERRRUN); } l_noret luaG_runerror (lua_State *L, const char *fmt, ...) { CallInfo *ci = L->ci; const char *msg; va_list argp; luaC_checkGC(L); /* error message uses memory */ va_start(argp, fmt); msg = luaO_pushvfstring(L, fmt, argp); /* format message */ va_end(argp); if (isLua(ci)) /* if Lua function, add source:line information */ luaG_addinfo(L, msg, ci_func(ci)->p->source, getcurrentline(ci)); luaG_errormsg(L); } /* ** Check whether new instruction 'newpc' is in a different line from ** previous instruction 'oldpc'. More often than not, 'newpc' is only ** one or a few instructions after 'oldpc' (it must be after, see ** caller), so try to avoid calling 'luaG_getfuncline'. If they are ** too far apart, there is a good chance of a ABSLINEINFO in the way, ** so it goes directly to 'luaG_getfuncline'. */ static int changedline (const Proto *p, int oldpc, int newpc) { if (p->lineinfo == NULL) /* no debug information? */ return 0; if (newpc - oldpc < MAXIWTHABS / 2) { /* not too far apart? */ int delta = 0; /* line diference */ int pc = oldpc; for (;;) { int lineinfo = p->lineinfo[++pc]; if (lineinfo == ABSLINEINFO) break; /* cannot compute delta; fall through */ delta += lineinfo; if (pc == newpc) return (delta != 0); /* delta computed successfully */ } } /* either instructions are too far apart or there is an absolute line info in the way; compute line difference explicitly */ return (luaG_getfuncline(p, oldpc) != luaG_getfuncline(p, newpc)); } /* ** Traces the execution of a Lua function. Called before the execution ** of each opcode, when debug is on. 'L->oldpc' stores the last ** instruction traced, to detect line changes. When entering a new ** function, 'npci' will be zero and will test as a new line whatever ** the value of 'oldpc'. Some exceptional conditions may return to ** a function without setting 'oldpc'. In that case, 'oldpc' may be ** invalid; if so, use zero as a valid value. (A wrong but valid 'oldpc' ** at most causes an extra call to a line hook.) ** This function is not "Protected" when called, so it should correct ** 'L->top' before calling anything that can run the GC. */ int luaG_traceexec (lua_State *L, const Instruction *pc) { CallInfo *ci = L->ci; lu_byte mask = L->hookmask; const Proto *p = ci_func(ci)->p; int counthook; if (!(mask & (LUA_MASKLINE | LUA_MASKCOUNT))) { /* no hooks? */ ci->u.l.trap = 0; /* don't need to stop again */ return 0; /* turn off 'trap' */ } pc++; /* reference is always next instruction */ ci->u.l.savedpc = pc; /* save 'pc' */ counthook = (--L->hookcount == 0 && (mask & LUA_MASKCOUNT)); if (counthook) resethookcount(L); /* reset count */ else if (!(mask & LUA_MASKLINE)) return 1; /* no line hook and count != 0; nothing to be done now */ if (ci->callstatus & CIST_HOOKYIELD) { /* called hook last time? */ ci->callstatus &= ~CIST_HOOKYIELD; /* erase mark */ return 1; /* do not call hook again (VM yielded, so it did not move) */ } if (!isIT(*(ci->u.l.savedpc - 1))) /* top not being used? */ L->top = ci->top; /* correct top */ if (counthook) luaD_hook(L, LUA_HOOKCOUNT, -1, 0, 0); /* call count hook */ if (mask & LUA_MASKLINE) { /* 'L->oldpc' may be invalid; use zero in this case */ int oldpc = (L->oldpc < p->sizecode) ? L->oldpc : 0; int npci = pcRel(pc, p); if (npci <= oldpc || /* call hook when jump back (loop), */ changedline(p, oldpc, npci)) { /* or when enter new line */ int newline = luaG_getfuncline(p, npci); luaD_hook(L, LUA_HOOKLINE, newline, 0, 0); /* call line hook */ } L->oldpc = npci; /* 'pc' of last call to line hook */ } if (L->status == LUA_YIELD) { /* did hook yield? */ if (counthook) L->hookcount = 1; /* undo decrement to zero */ ci->u.l.savedpc--; /* undo increment (resume will increment it again) */ ci->callstatus |= CIST_HOOKYIELD; /* mark that it yielded */ luaD_throw(L, LUA_YIELD); } return 1; /* keep 'trap' on */ } /* ** $Id: lfunc.c $ ** Auxiliary functions to manipulate prototypes and closures ** See Copyright Notice in lua.h */ #define lfunc_c #define LUA_CORE /*#include "lprefix.h"*/ #include /*#include "lua.h"*/ /*#include "ldebug.h"*/ /*#include "ldo.h"*/ /*#include "lfunc.h"*/ /*#include "lgc.h"*/ /*#include "lmem.h"*/ /*#include "lobject.h"*/ /*#include "lstate.h"*/ CClosure *luaF_newCclosure (lua_State *L, int nupvals) { GCObject *o = luaC_newobj(L, LUA_VCCL, sizeCclosure(nupvals)); CClosure *c = gco2ccl(o); c->nupvalues = cast_byte(nupvals); return c; } LClosure *luaF_newLclosure (lua_State *L, int nupvals) { GCObject *o = luaC_newobj(L, LUA_VLCL, sizeLclosure(nupvals)); LClosure *c = gco2lcl(o); c->p = NULL; c->nupvalues = cast_byte(nupvals); while (nupvals--) c->upvals[nupvals] = NULL; return c; } /* ** fill a closure with new closed upvalues */ void luaF_initupvals (lua_State *L, LClosure *cl) { int i; for (i = 0; i < cl->nupvalues; i++) { GCObject *o = luaC_newobj(L, LUA_VUPVAL, sizeof(UpVal)); UpVal *uv = gco2upv(o); uv->v = &uv->u.value; /* make it closed */ setnilvalue(uv->v); cl->upvals[i] = uv; luaC_objbarrier(L, cl, uv); } } /* ** Create a new upvalue at the given level, and link it to the list of ** open upvalues of 'L' after entry 'prev'. **/ static UpVal *newupval (lua_State *L, int tbc, StkId level, UpVal **prev) { GCObject *o = luaC_newobj(L, LUA_VUPVAL, sizeof(UpVal)); UpVal *uv = gco2upv(o); UpVal *next = *prev; uv->v = s2v(level); /* current value lives in the stack */ uv->tbc = tbc; uv->u.open.next = next; /* link it to list of open upvalues */ uv->u.open.previous = prev; if (next) next->u.open.previous = &uv->u.open.next; *prev = uv; if (!isintwups(L)) { /* thread not in list of threads with upvalues? */ L->twups = G(L)->twups; /* link it to the list */ G(L)->twups = L; } return uv; } /* ** Find and reuse, or create if it does not exist, an upvalue ** at the given level. */ UpVal *luaF_findupval (lua_State *L, StkId level) { UpVal **pp = &L->openupval; UpVal *p; lua_assert(isintwups(L) || L->openupval == NULL); while ((p = *pp) != NULL && uplevel(p) >= level) { /* search for it */ lua_assert(!isdead(G(L), p)); if (uplevel(p) == level) /* corresponding upvalue? */ return p; /* return it */ pp = &p->u.open.next; } /* not found: create a new upvalue after 'pp' */ return newupval(L, 0, level, pp); } /* ** Call closing method for object 'obj' with error message 'err'. The ** boolean 'yy' controls whether the call is yieldable. ** (This function assumes EXTRA_STACK.) */ static void callclosemethod (lua_State *L, TValue *obj, TValue *err, int yy) { StkId top = L->top; const TValue *tm = luaT_gettmbyobj(L, obj, TM_CLOSE); setobj2s(L, top, tm); /* will call metamethod... */ setobj2s(L, top + 1, obj); /* with 'self' as the 1st argument */ setobj2s(L, top + 2, err); /* and error msg. as 2nd argument */ L->top = top + 3; /* add function and arguments */ if (yy) luaD_call(L, top, 0); else luaD_callnoyield(L, top, 0); } /* ** Check whether object at given level has a close metamethod and raise ** an error if not. */ static void checkclosemth (lua_State *L, StkId level) { const TValue *tm = luaT_gettmbyobj(L, s2v(level), TM_CLOSE); if (ttisnil(tm)) { /* no metamethod? */ int idx = cast_int(level - L->ci->func); /* variable index */ const char *vname = luaG_findlocal(L, L->ci, idx, NULL); if (vname == NULL) vname = "?"; luaG_runerror(L, "variable '%s' got a non-closable value", vname); } } /* ** Prepare and call a closing method. ** If status is CLOSEKTOP, the call to the closing method will be pushed ** at the top of the stack. Otherwise, values can be pushed right after ** the 'level' of the upvalue being closed, as everything after that ** won't be used again. */ static void prepcallclosemth (lua_State *L, StkId level, int status, int yy) { TValue *uv = s2v(level); /* value being closed */ TValue *errobj; if (status == CLOSEKTOP) errobj = &G(L)->nilvalue; /* error object is nil */ else { /* 'luaD_seterrorobj' will set top to level + 2 */ errobj = s2v(level + 1); /* error object goes after 'uv' */ luaD_seterrorobj(L, status, level + 1); /* set error object */ } callclosemethod(L, uv, errobj, yy); } /* ** Maximum value for deltas in 'tbclist', dependent on the type ** of delta. (This macro assumes that an 'L' is in scope where it ** is used.) */ #define MAXDELTA \ ((256ul << ((sizeof(L->stack->tbclist.delta) - 1) * 8)) - 1) /* ** Insert a variable in the list of to-be-closed variables. */ void luaF_newtbcupval (lua_State *L, StkId level) { lua_assert(level > L->tbclist); if (l_isfalse(s2v(level))) return; /* false doesn't need to be closed */ checkclosemth(L, level); /* value must have a close method */ while (cast_uint(level - L->tbclist) > MAXDELTA) { L->tbclist += MAXDELTA; /* create a dummy node at maximum delta */ L->tbclist->tbclist.delta = 0; } level->tbclist.delta = cast(unsigned short, level - L->tbclist); L->tbclist = level; } void luaF_unlinkupval (UpVal *uv) { lua_assert(upisopen(uv)); *uv->u.open.previous = uv->u.open.next; if (uv->u.open.next) uv->u.open.next->u.open.previous = uv->u.open.previous; } /* ** Close all upvalues up to the given stack level. */ void luaF_closeupval (lua_State *L, StkId level) { UpVal *uv; StkId upl; /* stack index pointed by 'uv' */ while ((uv = L->openupval) != NULL && (upl = uplevel(uv)) >= level) { TValue *slot = &uv->u.value; /* new position for value */ lua_assert(uplevel(uv) < L->top); luaF_unlinkupval(uv); /* remove upvalue from 'openupval' list */ setobj(L, slot, uv->v); /* move value to upvalue slot */ uv->v = slot; /* now current value lives here */ if (!iswhite(uv)) { /* neither white nor dead? */ nw2black(uv); /* closed upvalues cannot be gray */ luaC_barrier(L, uv, slot); } } } /* ** Remove firt element from the tbclist plus its dummy nodes. */ static void poptbclist (lua_State *L) { StkId tbc = L->tbclist; lua_assert(tbc->tbclist.delta > 0); /* first element cannot be dummy */ tbc -= tbc->tbclist.delta; while (tbc > L->stack && tbc->tbclist.delta == 0) tbc -= MAXDELTA; /* remove dummy nodes */ L->tbclist = tbc; } /* ** Close all upvalues and to-be-closed variables up to the given stack ** level. */ void luaF_close (lua_State *L, StkId level, int status, int yy) { ptrdiff_t levelrel = savestack(L, level); luaF_closeupval(L, level); /* first, close the upvalues */ while (L->tbclist >= level) { /* traverse tbc's down to that level */ StkId tbc = L->tbclist; /* get variable index */ poptbclist(L); /* remove it from list */ prepcallclosemth(L, tbc, status, yy); /* close variable */ level = restorestack(L, levelrel); } } Proto *luaF_newproto (lua_State *L) { GCObject *o = luaC_newobj(L, LUA_VPROTO, sizeof(Proto)); Proto *f = gco2p(o); f->k = NULL; f->sizek = 0; f->p = NULL; f->sizep = 0; f->code = NULL; f->sizecode = 0; f->lineinfo = NULL; f->sizelineinfo = 0; f->abslineinfo = NULL; f->sizeabslineinfo = 0; f->upvalues = NULL; f->sizeupvalues = 0; f->numparams = 0; f->is_vararg = 0; f->maxstacksize = 0; f->locvars = NULL; f->sizelocvars = 0; f->linedefined = 0; f->lastlinedefined = 0; f->source = NULL; return f; } void luaF_freeproto (lua_State *L, Proto *f) { luaM_freearray(L, f->code, f->sizecode); luaM_freearray(L, f->p, f->sizep); luaM_freearray(L, f->k, f->sizek); luaM_freearray(L, f->lineinfo, f->sizelineinfo); luaM_freearray(L, f->abslineinfo, f->sizeabslineinfo); luaM_freearray(L, f->locvars, f->sizelocvars); luaM_freearray(L, f->upvalues, f->sizeupvalues); luaM_free(L, f); } /* ** Look for n-th local variable at line 'line' in function 'func'. ** Returns NULL if not found. */ const char *luaF_getlocalname (const Proto *f, int local_number, int pc) { int i; for (i = 0; isizelocvars && f->locvars[i].startpc <= pc; i++) { if (pc < f->locvars[i].endpc) { /* is variable active? */ local_number--; if (local_number == 0) return getstr(f->locvars[i].varname); } } return NULL; /* not found */ } /* ** $Id: lobject.c $ ** Some generic functions over Lua objects ** See Copyright Notice in lua.h */ #define lobject_c #define LUA_CORE /*#include "lprefix.h"*/ #include #include #include #include #include #include /*#include "lua.h"*/ /*#include "lctype.h"*/ /*#include "ldebug.h"*/ /*#include "ldo.h"*/ /*#include "lmem.h"*/ /*#include "lobject.h"*/ /*#include "lstate.h"*/ /*#include "lstring.h"*/ /*#include "lvm.h"*/ /* ** Computes ceil(log2(x)) */ int luaO_ceillog2 (unsigned int x) { static const lu_byte log_2[256] = { /* log_2[i] = ceil(log2(i - 1)) */ 0,1,2,2,3,3,3,3,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8 }; int l = 0; x--; while (x >= 256) { l += 8; x >>= 8; } return l + log_2[x]; } static lua_Integer intarith (lua_State *L, int op, lua_Integer v1, lua_Integer v2) { switch (op) { case LUA_OPADD: return intop(+, v1, v2); case LUA_OPSUB:return intop(-, v1, v2); case LUA_OPMUL:return intop(*, v1, v2); case LUA_OPMOD: return luaV_mod(L, v1, v2); case LUA_OPIDIV: return luaV_idiv(L, v1, v2); case LUA_OPBAND: return intop(&, v1, v2); case LUA_OPBOR: return intop(|, v1, v2); case LUA_OPBXOR: return intop(^, v1, v2); case LUA_OPSHL: return luaV_shiftl(v1, v2); case LUA_OPSHR: return luaV_shiftl(v1, -v2); case LUA_OPUNM: return intop(-, 0, v1); case LUA_OPBNOT: return intop(^, ~l_castS2U(0), v1); default: lua_assert(0); return 0; } } static lua_Number numarith (lua_State *L, int op, lua_Number v1, lua_Number v2) { switch (op) { case LUA_OPADD: return luai_numadd(L, v1, v2); case LUA_OPSUB: return luai_numsub(L, v1, v2); case LUA_OPMUL: return luai_nummul(L, v1, v2); case LUA_OPDIV: return luai_numdiv(L, v1, v2); case LUA_OPPOW: return luai_numpow(L, v1, v2); case LUA_OPIDIV: return luai_numidiv(L, v1, v2); case LUA_OPUNM: return luai_numunm(L, v1); case LUA_OPMOD: return luaV_modf(L, v1, v2); default: lua_assert(0); return 0; } } int luaO_rawarith (lua_State *L, int op, const TValue *p1, const TValue *p2, TValue *res) { switch (op) { case LUA_OPBAND: case LUA_OPBOR: case LUA_OPBXOR: case LUA_OPSHL: case LUA_OPSHR: case LUA_OPBNOT: { /* operate only on integers */ lua_Integer i1; lua_Integer i2; if (tointegerns(p1, &i1) && tointegerns(p2, &i2)) { setivalue(res, intarith(L, op, i1, i2)); return 1; } else return 0; /* fail */ } case LUA_OPDIV: case LUA_OPPOW: { /* operate only on floats */ lua_Number n1; lua_Number n2; if (tonumberns(p1, n1) && tonumberns(p2, n2)) { setfltvalue(res, numarith(L, op, n1, n2)); return 1; } else return 0; /* fail */ } default: { /* other operations */ lua_Number n1; lua_Number n2; if (ttisinteger(p1) && ttisinteger(p2)) { setivalue(res, intarith(L, op, ivalue(p1), ivalue(p2))); return 1; } else if (tonumberns(p1, n1) && tonumberns(p2, n2)) { setfltvalue(res, numarith(L, op, n1, n2)); return 1; } else return 0; /* fail */ } } } void luaO_arith (lua_State *L, int op, const TValue *p1, const TValue *p2, StkId res) { if (!luaO_rawarith(L, op, p1, p2, s2v(res))) { /* could not perform raw operation; try metamethod */ luaT_trybinTM(L, p1, p2, res, cast(TMS, (op - LUA_OPADD) + TM_ADD)); } } int luaO_hexavalue (int c) { if (lisdigit(c)) return c - '0'; else return (ltolower(c) - 'a') + 10; } static int isneg (const char **s) { if (**s == '-') { (*s)++; return 1; } else if (**s == '+') (*s)++; return 0; } /* ** {================================================================== ** Lua's implementation for 'lua_strx2number' ** =================================================================== */ #if !defined(lua_strx2number) /* maximum number of significant digits to read (to avoid overflows even with single floats) */ #define MAXSIGDIG 30 /* ** convert a hexadecimal numeric string to a number, following ** C99 specification for 'strtod' */ static lua_Number lua_strx2number (const char *s, char **endptr) { int dot = lua_getlocaledecpoint(); lua_Number r = l_mathop(0.0); /* result (accumulator) */ int sigdig = 0; /* number of significant digits */ int nosigdig = 0; /* number of non-significant digits */ int e = 0; /* exponent correction */ int neg; /* 1 if number is negative */ int hasdot = 0; /* true after seen a dot */ *endptr = cast_charp(s); /* nothing is valid yet */ while (lisspace(cast_uchar(*s))) s++; /* skip initial spaces */ neg = isneg(&s); /* check sign */ if (!(*s == '0' && (*(s + 1) == 'x' || *(s + 1) == 'X'))) /* check '0x' */ return l_mathop(0.0); /* invalid format (no '0x') */ for (s += 2; ; s++) { /* skip '0x' and read numeral */ if (*s == dot) { if (hasdot) break; /* second dot? stop loop */ else hasdot = 1; } else if (lisxdigit(cast_uchar(*s))) { if (sigdig == 0 && *s == '0') /* non-significant digit (zero)? */ nosigdig++; else if (++sigdig <= MAXSIGDIG) /* can read it without overflow? */ r = (r * l_mathop(16.0)) + luaO_hexavalue(*s); else e++; /* too many digits; ignore, but still count for exponent */ if (hasdot) e--; /* decimal digit? correct exponent */ } else break; /* neither a dot nor a digit */ } if (nosigdig + sigdig == 0) /* no digits? */ return l_mathop(0.0); /* invalid format */ *endptr = cast_charp(s); /* valid up to here */ e *= 4; /* each digit multiplies/divides value by 2^4 */ if (*s == 'p' || *s == 'P') { /* exponent part? */ int exp1 = 0; /* exponent value */ int neg1; /* exponent sign */ s++; /* skip 'p' */ neg1 = isneg(&s); /* sign */ if (!lisdigit(cast_uchar(*s))) return l_mathop(0.0); /* invalid; must have at least one digit */ while (lisdigit(cast_uchar(*s))) /* read exponent */ exp1 = exp1 * 10 + *(s++) - '0'; if (neg1) exp1 = -exp1; e += exp1; *endptr = cast_charp(s); /* valid up to here */ } if (neg) r = -r; return l_mathop(ldexp)(r, e); } #endif /* }====================================================== */ /* maximum length of a numeral to be converted to a number */ #if !defined (L_MAXLENNUM) #define L_MAXLENNUM 200 #endif /* ** Convert string 's' to a Lua number (put in 'result'). Return NULL on ** fail or the address of the ending '\0' on success. ('mode' == 'x') ** means a hexadecimal numeral. */ static const char *l_str2dloc (const char *s, lua_Number *result, int mode) { char *endptr; *result = (mode == 'x') ? lua_strx2number(s, &endptr) /* try to convert */ : lua_str2number(s, &endptr); if (endptr == s) return NULL; /* nothing recognized? */ while (lisspace(cast_uchar(*endptr))) endptr++; /* skip trailing spaces */ return (*endptr == '\0') ? endptr : NULL; /* OK iff no trailing chars */ } /* ** Convert string 's' to a Lua number (put in 'result') handling the ** current locale. ** This function accepts both the current locale or a dot as the radix ** mark. If the conversion fails, it may mean number has a dot but ** locale accepts something else. In that case, the code copies 's' ** to a buffer (because 's' is read-only), changes the dot to the ** current locale radix mark, and tries to convert again. ** The variable 'mode' checks for special characters in the string: ** - 'n' means 'inf' or 'nan' (which should be rejected) ** - 'x' means a hexadecimal numeral ** - '.' just optimizes the search for the common case (no special chars) */ static const char *l_str2d (const char *s, lua_Number *result) { const char *endptr; const char *pmode = strpbrk(s, ".xXnN"); /* look for special chars */ int mode = pmode ? ltolower(cast_uchar(*pmode)) : 0; if (mode == 'n') /* reject 'inf' and 'nan' */ return NULL; endptr = l_str2dloc(s, result, mode); /* try to convert */ if (endptr == NULL) { /* failed? may be a different locale */ char buff[L_MAXLENNUM + 1]; const char *pdot = strchr(s, '.'); if (pdot == NULL || strlen(s) > L_MAXLENNUM) return NULL; /* string too long or no dot; fail */ strcpy(buff, s); /* copy string to buffer */ buff[pdot - s] = lua_getlocaledecpoint(); /* correct decimal point */ endptr = l_str2dloc(buff, result, mode); /* try again */ if (endptr != NULL) endptr = s + (endptr - buff); /* make relative to 's' */ } return endptr; } #define MAXBY10 cast(lua_Unsigned, LUA_MAXINTEGER / 10) #define MAXLASTD cast_int(LUA_MAXINTEGER % 10) static const char *l_str2int (const char *s, lua_Integer *result) { lua_Unsigned a = 0; int empty = 1; int neg; while (lisspace(cast_uchar(*s))) s++; /* skip initial spaces */ neg = isneg(&s); if (s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) { /* hex? */ s += 2; /* skip '0x' */ for (; lisxdigit(cast_uchar(*s)); s++) { a = a * 16 + luaO_hexavalue(*s); empty = 0; } } else { /* decimal */ for (; lisdigit(cast_uchar(*s)); s++) { int d = *s - '0'; if (a >= MAXBY10 && (a > MAXBY10 || d > MAXLASTD + neg)) /* overflow? */ return NULL; /* do not accept it (as integer) */ a = a * 10 + d; empty = 0; } } while (lisspace(cast_uchar(*s))) s++; /* skip trailing spaces */ if (empty || *s != '\0') return NULL; /* something wrong in the numeral */ else { *result = l_castU2S((neg) ? 0u - a : a); return s; } } size_t luaO_str2num (const char *s, TValue *o) { lua_Integer i; lua_Number n; const char *e; if ((e = l_str2int(s, &i)) != NULL) { /* try as an integer */ setivalue(o, i); } else if ((e = l_str2d(s, &n)) != NULL) { /* else try as a float */ setfltvalue(o, n); } else return 0; /* conversion failed */ return (e - s) + 1; /* success; return string size */ } int luaO_utf8esc (char *buff, unsigned long x) { int n = 1; /* number of bytes put in buffer (backwards) */ lua_assert(x <= 0x7FFFFFFFu); if (x < 0x80) /* ascii? */ buff[UTF8BUFFSZ - 1] = cast_char(x); else { /* need continuation bytes */ unsigned int mfb = 0x3f; /* maximum that fits in first byte */ do { /* add continuation bytes */ buff[UTF8BUFFSZ - (n++)] = cast_char(0x80 | (x & 0x3f)); x >>= 6; /* remove added bits */ mfb >>= 1; /* now there is one less bit available in first byte */ } while (x > mfb); /* still needs continuation byte? */ buff[UTF8BUFFSZ - n] = cast_char((~mfb << 1) | x); /* add first byte */ } return n; } /* ** Maximum length of the conversion of a number to a string. Must be ** enough to accommodate both LUA_INTEGER_FMT and LUA_NUMBER_FMT. ** (For a long long int, this is 19 digits plus a sign and a final '\0', ** adding to 21. For a long double, it can go to a sign, 33 digits, ** the dot, an exponent letter, an exponent sign, 5 exponent digits, ** and a final '\0', adding to 43.) */ #define MAXNUMBER2STR 44 /* ** Convert a number object to a string, adding it to a buffer */ static int tostringbuff (TValue *obj, char *buff) { int len; lua_assert(ttisnumber(obj)); if (ttisinteger(obj)) len = lua_integer2str(buff, MAXNUMBER2STR, ivalue(obj)); else { len = lua_number2str(buff, MAXNUMBER2STR, fltvalue(obj)); if (buff[strspn(buff, "-0123456789")] == '\0') { /* looks like an int? */ buff[len++] = lua_getlocaledecpoint(); buff[len++] = '0'; /* adds '.0' to result */ } } return len; } /* ** Convert a number object to a Lua string, replacing the value at 'obj' */ void luaO_tostring (lua_State *L, TValue *obj) { char buff[MAXNUMBER2STR]; int len = tostringbuff(obj, buff); setsvalue(L, obj, luaS_newlstr(L, buff, len)); } /* ** {================================================================== ** 'luaO_pushvfstring' ** =================================================================== */ /* size for buffer space used by 'luaO_pushvfstring' */ #define BUFVFS 200 /* buffer used by 'luaO_pushvfstring' */ typedef struct BuffFS { lua_State *L; int pushed; /* number of string pieces already on the stack */ int blen; /* length of partial string in 'space' */ char space[BUFVFS]; /* holds last part of the result */ } BuffFS; /* ** Push given string to the stack, as part of the buffer, and ** join the partial strings in the stack into one. */ static void pushstr (BuffFS *buff, const char *str, size_t l) { lua_State *L = buff->L; setsvalue2s(L, L->top, luaS_newlstr(L, str, l)); L->top++; /* may use one extra slot */ buff->pushed++; luaV_concat(L, buff->pushed); /* join partial results into one */ buff->pushed = 1; } /* ** empty the buffer space into the stack */ static void clearbuff (BuffFS *buff) { pushstr(buff, buff->space, buff->blen); /* push buffer contents */ buff->blen = 0; /* space now is empty */ } /* ** Get a space of size 'sz' in the buffer. If buffer has not enough ** space, empty it. 'sz' must fit in an empty buffer. */ static char *getbuff (BuffFS *buff, int sz) { lua_assert(buff->blen <= BUFVFS); lua_assert(sz <= BUFVFS); if (sz > BUFVFS - buff->blen) /* not enough space? */ clearbuff(buff); return buff->space + buff->blen; } #define addsize(b,sz) ((b)->blen += (sz)) /* ** Add 'str' to the buffer. If string is larger than the buffer space, ** push the string directly to the stack. */ static void addstr2buff (BuffFS *buff, const char *str, size_t slen) { if (slen <= BUFVFS) { /* does string fit into buffer? */ char *bf = getbuff(buff, cast_int(slen)); memcpy(bf, str, slen); /* add string to buffer */ addsize(buff, cast_int(slen)); } else { /* string larger than buffer */ clearbuff(buff); /* string comes after buffer's content */ pushstr(buff, str, slen); /* push string */ } } /* ** Add a number to the buffer. */ static void addnum2buff (BuffFS *buff, TValue *num) { char *numbuff = getbuff(buff, MAXNUMBER2STR); int len = tostringbuff(num, numbuff); /* format number into 'numbuff' */ addsize(buff, len); } /* ** this function handles only '%d', '%c', '%f', '%p', '%s', and '%%' conventional formats, plus Lua-specific '%I' and '%U' */ const char *luaO_pushvfstring (lua_State *L, const char *fmt, va_list argp) { BuffFS buff; /* holds last part of the result */ const char *e; /* points to next '%' */ buff.pushed = buff.blen = 0; buff.L = L; while ((e = strchr(fmt, '%')) != NULL) { addstr2buff(&buff, fmt, e - fmt); /* add 'fmt' up to '%' */ switch (*(e + 1)) { /* conversion specifier */ case 's': { /* zero-terminated string */ const char *s = va_arg(argp, char *); if (s == NULL) s = "(null)"; addstr2buff(&buff, s, strlen(s)); break; } case 'c': { /* an 'int' as a character */ char c = cast_uchar(va_arg(argp, int)); addstr2buff(&buff, &c, sizeof(char)); break; } case 'd': { /* an 'int' */ TValue num; setivalue(&num, va_arg(argp, int)); addnum2buff(&buff, &num); break; } case 'I': { /* a 'lua_Integer' */ TValue num; setivalue(&num, cast(lua_Integer, va_arg(argp, l_uacInt))); addnum2buff(&buff, &num); break; } case 'f': { /* a 'lua_Number' */ TValue num; setfltvalue(&num, cast_num(va_arg(argp, l_uacNumber))); addnum2buff(&buff, &num); break; } case 'p': { /* a pointer */ const int sz = 3 * sizeof(void*) + 8; /* enough space for '%p' */ char *bf = getbuff(&buff, sz); void *p = va_arg(argp, void *); int len = lua_pointer2str(bf, sz, p); addsize(&buff, len); break; } case 'U': { /* a 'long' as a UTF-8 sequence */ char bf[UTF8BUFFSZ]; int len = luaO_utf8esc(bf, va_arg(argp, long)); addstr2buff(&buff, bf + UTF8BUFFSZ - len, len); break; } case '%': { addstr2buff(&buff, "%", 1); break; } default: { luaG_runerror(L, "invalid option '%%%c' to 'lua_pushfstring'", *(e + 1)); } } fmt = e + 2; /* skip '%' and the specifier */ } addstr2buff(&buff, fmt, strlen(fmt)); /* rest of 'fmt' */ clearbuff(&buff); /* empty buffer into the stack */ lua_assert(buff.pushed == 1); return svalue(s2v(L->top - 1)); } const char *luaO_pushfstring (lua_State *L, const char *fmt, ...) { const char *msg; va_list argp; va_start(argp, fmt); msg = luaO_pushvfstring(L, fmt, argp); va_end(argp); return msg; } /* }================================================================== */ #define RETS "..." #define PRE "[string \"" #define POS "\"]" #define addstr(a,b,l) ( memcpy(a,b,(l) * sizeof(char)), a += (l) ) void luaO_chunkid (char *out, const char *source, size_t srclen) { size_t bufflen = LUA_IDSIZE; /* free space in buffer */ if (*source == '=') { /* 'literal' source */ if (srclen <= bufflen) /* small enough? */ memcpy(out, source + 1, srclen * sizeof(char)); else { /* truncate it */ addstr(out, source + 1, bufflen - 1); *out = '\0'; } } else if (*source == '@') { /* file name */ if (srclen <= bufflen) /* small enough? */ memcpy(out, source + 1, srclen * sizeof(char)); else { /* add '...' before rest of name */ addstr(out, RETS, LL(RETS)); bufflen -= LL(RETS); memcpy(out, source + 1 + srclen - bufflen, bufflen * sizeof(char)); } } else { /* string; format as [string "source"] */ const char *nl = strchr(source, '\n'); /* find first new line (if any) */ addstr(out, PRE, LL(PRE)); /* add prefix */ bufflen -= LL(PRE RETS POS) + 1; /* save space for prefix+suffix+'\0' */ if (srclen < bufflen && nl == NULL) { /* small one-line source? */ addstr(out, source, srclen); /* keep it */ } else { if (nl != NULL) srclen = nl - source; /* stop at first newline */ if (srclen > bufflen) srclen = bufflen; addstr(out, source, srclen); addstr(out, RETS, LL(RETS)); } memcpy(out, POS, (LL(POS) + 1) * sizeof(char)); } } /* ** $Id: ltm.c $ ** Tag methods ** See Copyright Notice in lua.h */ #define ltm_c #define LUA_CORE /*#include "lprefix.h"*/ #include /*#include "lua.h"*/ /*#include "ldebug.h"*/ /*#include "ldo.h"*/ /*#include "lgc.h"*/ /*#include "lobject.h"*/ /*#include "lstate.h"*/ /*#include "lstring.h"*/ /*#include "ltable.h"*/ /*#include "ltm.h"*/ /*#include "lvm.h"*/ static const char udatatypename[] = "userdata"; LUAI_DDEF const char *const luaT_typenames_[LUA_TOTALTYPES] = { "no value", "nil", "boolean", udatatypename, "number", "string", "table", "function", udatatypename, "thread", "upvalue", "proto" /* these last cases are used for tests only */ }; void luaT_init (lua_State *L) { static const char *const luaT_eventname[] = { /* ORDER TM */ "__index", "__newindex", "__gc", "__mode", "__len", "__eq", "__add", "__sub", "__mul", "__mod", "__pow", "__div", "__idiv", "__band", "__bor", "__bxor", "__shl", "__shr", "__unm", "__bnot", "__lt", "__le", "__concat", "__call", "__close" }; int i; for (i=0; itmname[i] = luaS_new(L, luaT_eventname[i]); luaC_fix(L, obj2gco(G(L)->tmname[i])); /* never collect these names */ } } /* ** function to be used with macro "fasttm": optimized for absence of ** tag methods */ const TValue *luaT_gettm (Table *events, TMS event, TString *ename) { const TValue *tm = luaH_getshortstr(events, ename); lua_assert(event <= TM_EQ); if (notm(tm)) { /* no tag method? */ events->flags |= cast_byte(1u<metatable; break; case LUA_TUSERDATA: mt = uvalue(o)->metatable; break; default: mt = G(L)->mt[ttype(o)]; } return (mt ? luaH_getshortstr(mt, G(L)->tmname[event]) : &G(L)->nilvalue); } /* ** Return the name of the type of an object. For tables and userdata ** with metatable, use their '__name' metafield, if present. */ const char *luaT_objtypename (lua_State *L, const TValue *o) { Table *mt; if ((ttistable(o) && (mt = hvalue(o)->metatable) != NULL) || (ttisfulluserdata(o) && (mt = uvalue(o)->metatable) != NULL)) { const TValue *name = luaH_getshortstr(mt, luaS_new(L, "__name")); if (ttisstring(name)) /* is '__name' a string? */ return getstr(tsvalue(name)); /* use it as type name */ } return ttypename(ttype(o)); /* else use standard type name */ } void luaT_callTM (lua_State *L, const TValue *f, const TValue *p1, const TValue *p2, const TValue *p3) { StkId func = L->top; setobj2s(L, func, f); /* push function (assume EXTRA_STACK) */ setobj2s(L, func + 1, p1); /* 1st argument */ setobj2s(L, func + 2, p2); /* 2nd argument */ setobj2s(L, func + 3, p3); /* 3rd argument */ L->top = func + 4; /* metamethod may yield only when called from Lua code */ if (isLuacode(L->ci)) luaD_call(L, func, 0); else luaD_callnoyield(L, func, 0); } void luaT_callTMres (lua_State *L, const TValue *f, const TValue *p1, const TValue *p2, StkId res) { ptrdiff_t result = savestack(L, res); StkId func = L->top; setobj2s(L, func, f); /* push function (assume EXTRA_STACK) */ setobj2s(L, func + 1, p1); /* 1st argument */ setobj2s(L, func + 2, p2); /* 2nd argument */ L->top += 3; /* metamethod may yield only when called from Lua code */ if (isLuacode(L->ci)) luaD_call(L, func, 1); else luaD_callnoyield(L, func, 1); res = restorestack(L, result); setobjs2s(L, res, --L->top); /* move result to its place */ } static int callbinTM (lua_State *L, const TValue *p1, const TValue *p2, StkId res, TMS event) { const TValue *tm = luaT_gettmbyobj(L, p1, event); /* try first operand */ if (notm(tm)) tm = luaT_gettmbyobj(L, p2, event); /* try second operand */ if (notm(tm)) return 0; luaT_callTMres(L, tm, p1, p2, res); return 1; } void luaT_trybinTM (lua_State *L, const TValue *p1, const TValue *p2, StkId res, TMS event) { if (l_unlikely(!callbinTM(L, p1, p2, res, event))) { switch (event) { case TM_BAND: case TM_BOR: case TM_BXOR: case TM_SHL: case TM_SHR: case TM_BNOT: { if (ttisnumber(p1) && ttisnumber(p2)) luaG_tointerror(L, p1, p2); else luaG_opinterror(L, p1, p2, "perform bitwise operation on"); } /* calls never return, but to avoid warnings: *//* FALLTHROUGH */ default: luaG_opinterror(L, p1, p2, "perform arithmetic on"); } } } void luaT_tryconcatTM (lua_State *L) { StkId top = L->top; if (l_unlikely(!callbinTM(L, s2v(top - 2), s2v(top - 1), top - 2, TM_CONCAT))) luaG_concaterror(L, s2v(top - 2), s2v(top - 1)); } void luaT_trybinassocTM (lua_State *L, const TValue *p1, const TValue *p2, int flip, StkId res, TMS event) { if (flip) luaT_trybinTM(L, p2, p1, res, event); else luaT_trybinTM(L, p1, p2, res, event); } void luaT_trybiniTM (lua_State *L, const TValue *p1, lua_Integer i2, int flip, StkId res, TMS event) { TValue aux; setivalue(&aux, i2); luaT_trybinassocTM(L, p1, &aux, flip, res, event); } /* ** Calls an order tag method. ** For lessequal, LUA_COMPAT_LT_LE keeps compatibility with old ** behavior: if there is no '__le', try '__lt', based on l <= r iff ** !(r < l) (assuming a total order). If the metamethod yields during ** this substitution, the continuation has to know about it (to negate ** the result of rtop, event)) /* try original event */ return !l_isfalse(s2v(L->top)); #if defined(LUA_COMPAT_LT_LE) else if (event == TM_LE) { /* try '!(p2 < p1)' for '(p1 <= p2)' */ L->ci->callstatus |= CIST_LEQ; /* mark it is doing 'lt' for 'le' */ if (callbinTM(L, p2, p1, L->top, TM_LT)) { L->ci->callstatus ^= CIST_LEQ; /* clear mark */ return l_isfalse(s2v(L->top)); } /* else error will remove this 'ci'; no need to clear mark */ } #endif luaG_ordererror(L, p1, p2); /* no metamethod found */ return 0; /* to avoid warnings */ } int luaT_callorderiTM (lua_State *L, const TValue *p1, int v2, int flip, int isfloat, TMS event) { TValue aux; const TValue *p2; if (isfloat) { setfltvalue(&aux, cast_num(v2)); } else setivalue(&aux, v2); if (flip) { /* arguments were exchanged? */ p2 = p1; p1 = &aux; /* correct them */ } else p2 = &aux; return luaT_callorderTM(L, p1, p2, event); } void luaT_adjustvarargs (lua_State *L, int nfixparams, CallInfo *ci, const Proto *p) { int i; int actual = cast_int(L->top - ci->func) - 1; /* number of arguments */ int nextra = actual - nfixparams; /* number of extra arguments */ ci->u.l.nextraargs = nextra; luaD_checkstack(L, p->maxstacksize + 1); /* copy function to the top of the stack */ setobjs2s(L, L->top++, ci->func); /* move fixed parameters to the top of the stack */ for (i = 1; i <= nfixparams; i++) { setobjs2s(L, L->top++, ci->func + i); setnilvalue(s2v(ci->func + i)); /* erase original parameter (for GC) */ } ci->func += actual + 1; ci->top += actual + 1; lua_assert(L->top <= ci->top && ci->top <= L->stack_last); } void luaT_getvarargs (lua_State *L, CallInfo *ci, StkId where, int wanted) { int i; int nextra = ci->u.l.nextraargs; if (wanted < 0) { wanted = nextra; /* get all extra arguments available */ checkstackGCp(L, nextra, where); /* ensure stack space */ L->top = where + nextra; /* next instruction will need top */ } for (i = 0; i < wanted && i < nextra; i++) setobjs2s(L, where + i, ci->func - nextra + i); for (; i < wanted; i++) /* complete required results with nil */ setnilvalue(s2v(where + i)); } /* ** $Id: lstring.c $ ** String table (keeps all strings handled by Lua) ** See Copyright Notice in lua.h */ #define lstring_c #define LUA_CORE /*#include "lprefix.h"*/ #include /*#include "lua.h"*/ /*#include "ldebug.h"*/ /*#include "ldo.h"*/ /*#include "lmem.h"*/ /*#include "lobject.h"*/ /*#include "lstate.h"*/ /*#include "lstring.h"*/ /* ** Maximum size for string table. */ #define MAXSTRTB cast_int(luaM_limitN(MAX_INT, TString*)) /* ** equality for long strings */ int luaS_eqlngstr (TString *a, TString *b) { size_t len = a->u.lnglen; lua_assert(a->tt == LUA_VLNGSTR && b->tt == LUA_VLNGSTR); return (a == b) || /* same instance or... */ ((len == b->u.lnglen) && /* equal length and ... */ (memcmp(getstr(a), getstr(b), len) == 0)); /* equal contents */ } unsigned int luaS_hash (const char *str, size_t l, unsigned int seed) { unsigned int h = seed ^ cast_uint(l); for (; l > 0; l--) h ^= ((h<<5) + (h>>2) + cast_byte(str[l - 1])); return h; } unsigned int luaS_hashlongstr (TString *ts) { lua_assert(ts->tt == LUA_VLNGSTR); if (ts->extra == 0) { /* no hash? */ size_t len = ts->u.lnglen; ts->hash = luaS_hash(getstr(ts), len, ts->hash); ts->extra = 1; /* now it has its hash */ } return ts->hash; } static void tablerehash (TString **vect, int osize, int nsize) { int i; for (i = osize; i < nsize; i++) /* clear new elements */ vect[i] = NULL; for (i = 0; i < osize; i++) { /* rehash old part of the array */ TString *p = vect[i]; vect[i] = NULL; while (p) { /* for each string in the list */ TString *hnext = p->u.hnext; /* save next */ unsigned int h = lmod(p->hash, nsize); /* new position */ p->u.hnext = vect[h]; /* chain it into array */ vect[h] = p; p = hnext; } } } /* ** Resize the string table. If allocation fails, keep the current size. ** (This can degrade performance, but any non-zero size should work ** correctly.) */ void luaS_resize (lua_State *L, int nsize) { stringtable *tb = &G(L)->strt; int osize = tb->size; TString **newvect; if (nsize < osize) /* shrinking table? */ tablerehash(tb->hash, osize, nsize); /* depopulate shrinking part */ newvect = luaM_reallocvector(L, tb->hash, osize, nsize, TString*); if (l_unlikely(newvect == NULL)) { /* reallocation failed? */ if (nsize < osize) /* was it shrinking table? */ tablerehash(tb->hash, nsize, osize); /* restore to original size */ /* leave table as it was */ } else { /* allocation succeeded */ tb->hash = newvect; tb->size = nsize; if (nsize > osize) tablerehash(newvect, osize, nsize); /* rehash for new size */ } } /* ** Clear API string cache. (Entries cannot be empty, so fill them with ** a non-collectable string.) */ void luaS_clearcache (global_State *g) { int i, j; for (i = 0; i < STRCACHE_N; i++) for (j = 0; j < STRCACHE_M; j++) { if (iswhite(g->strcache[i][j])) /* will entry be collected? */ g->strcache[i][j] = g->memerrmsg; /* replace it with something fixed */ } } /* ** Initialize the string table and the string cache */ void luaS_init (lua_State *L) { global_State *g = G(L); int i, j; stringtable *tb = &G(L)->strt; tb->hash = luaM_newvector(L, MINSTRTABSIZE, TString*); tablerehash(tb->hash, 0, MINSTRTABSIZE); /* clear array */ tb->size = MINSTRTABSIZE; /* pre-create memory-error message */ g->memerrmsg = luaS_newliteral(L, MEMERRMSG); luaC_fix(L, obj2gco(g->memerrmsg)); /* it should never be collected */ for (i = 0; i < STRCACHE_N; i++) /* fill cache with valid strings */ for (j = 0; j < STRCACHE_M; j++) g->strcache[i][j] = g->memerrmsg; } /* ** creates a new string object */ static TString *createstrobj (lua_State *L, size_t l, int tag, unsigned int h) { TString *ts; GCObject *o; size_t totalsize; /* total size of TString object */ totalsize = sizelstring(l); o = luaC_newobj(L, tag, totalsize); ts = gco2ts(o); ts->hash = h; ts->extra = 0; getstr(ts)[l] = '\0'; /* ending 0 */ return ts; } TString *luaS_createlngstrobj (lua_State *L, size_t l) { TString *ts = createstrobj(L, l, LUA_VLNGSTR, G(L)->seed); ts->u.lnglen = l; return ts; } void luaS_remove (lua_State *L, TString *ts) { stringtable *tb = &G(L)->strt; TString **p = &tb->hash[lmod(ts->hash, tb->size)]; while (*p != ts) /* find previous element */ p = &(*p)->u.hnext; *p = (*p)->u.hnext; /* remove element from its list */ tb->nuse--; } static void growstrtab (lua_State *L, stringtable *tb) { if (l_unlikely(tb->nuse == MAX_INT)) { /* too many strings? */ luaC_fullgc(L, 1); /* try to free some... */ if (tb->nuse == MAX_INT) /* still too many? */ luaM_error(L); /* cannot even create a message... */ } if (tb->size <= MAXSTRTB / 2) /* can grow string table? */ luaS_resize(L, tb->size * 2); } /* ** Checks whether short string exists and reuses it or creates a new one. */ static TString *internshrstr (lua_State *L, const char *str, size_t l) { TString *ts; global_State *g = G(L); stringtable *tb = &g->strt; unsigned int h = luaS_hash(str, l, g->seed); TString **list = &tb->hash[lmod(h, tb->size)]; lua_assert(str != NULL); /* otherwise 'memcmp'/'memcpy' are undefined */ for (ts = *list; ts != NULL; ts = ts->u.hnext) { if (l == ts->shrlen && (memcmp(str, getstr(ts), l * sizeof(char)) == 0)) { /* found! */ if (isdead(g, ts)) /* dead (but not collected yet)? */ changewhite(ts); /* resurrect it */ return ts; } } /* else must create a new string */ if (tb->nuse >= tb->size) { /* need to grow string table? */ growstrtab(L, tb); list = &tb->hash[lmod(h, tb->size)]; /* rehash with new size */ } ts = createstrobj(L, l, LUA_VSHRSTR, h); memcpy(getstr(ts), str, l * sizeof(char)); ts->shrlen = cast_byte(l); ts->u.hnext = *list; *list = ts; tb->nuse++; return ts; } /* ** new string (with explicit length) */ TString *luaS_newlstr (lua_State *L, const char *str, size_t l) { if (l <= LUAI_MAXSHORTLEN) /* short string? */ return internshrstr(L, str, l); else { TString *ts; if (l_unlikely(l >= (MAX_SIZE - sizeof(TString))/sizeof(char))) luaM_toobig(L); ts = luaS_createlngstrobj(L, l); memcpy(getstr(ts), str, l * sizeof(char)); return ts; } } /* ** Create or reuse a zero-terminated string, first checking in the ** cache (using the string address as a key). The cache can contain ** only zero-terminated strings, so it is safe to use 'strcmp' to ** check hits. */ TString *luaS_new (lua_State *L, const char *str) { unsigned int i = point2uint(str) % STRCACHE_N; /* hash */ int j; TString **p = G(L)->strcache[i]; for (j = 0; j < STRCACHE_M; j++) { if (strcmp(str, getstr(p[j])) == 0) /* hit? */ return p[j]; /* that is it */ } /* normal route */ for (j = STRCACHE_M - 1; j > 0; j--) p[j] = p[j - 1]; /* move out last element */ /* new element is first in the list */ p[0] = luaS_newlstr(L, str, strlen(str)); return p[0]; } Udata *luaS_newudata (lua_State *L, size_t s, int nuvalue) { Udata *u; int i; GCObject *o; if (l_unlikely(s > MAX_SIZE - udatamemoffset(nuvalue))) luaM_toobig(L); o = luaC_newobj(L, LUA_VUSERDATA, sizeudata(nuvalue, s)); u = gco2u(o); u->len = s; u->nuvalue = nuvalue; u->metatable = NULL; for (i = 0; i < nuvalue; i++) setnilvalue(&u->uv[i].uv); return u; } /* ** $Id: ltable.c $ ** Lua tables (hash) ** See Copyright Notice in lua.h */ #define ltable_c #define LUA_CORE /*#include "lprefix.h"*/ /* ** Implementation of tables (aka arrays, objects, or hash tables). ** Tables keep its elements in two parts: an array part and a hash part. ** Non-negative integer keys are all candidates to be kept in the array ** part. The actual size of the array is the largest 'n' such that ** more than half the slots between 1 and n are in use. ** Hash uses a mix of chained scatter table with Brent's variation. ** A main invariant of these tables is that, if an element is not ** in its main position (i.e. the 'original' position that its hash gives ** to it), then the colliding element is in its own main position. ** Hence even when the load factor reaches 100%, performance remains good. */ #include #include /*#include "lua.h"*/ /*#include "ldebug.h"*/ /*#include "ldo.h"*/ /*#include "lgc.h"*/ /*#include "lmem.h"*/ /*#include "lobject.h"*/ /*#include "lstate.h"*/ /*#include "lstring.h"*/ /*#include "ltable.h"*/ /*#include "lvm.h"*/ /* ** MAXABITS is the largest integer such that MAXASIZE fits in an ** unsigned int. */ #define MAXABITS cast_int(sizeof(int) * CHAR_BIT - 1) /* ** MAXASIZE is the maximum size of the array part. It is the minimum ** between 2^MAXABITS and the maximum size that, measured in bytes, ** fits in a 'size_t'. */ #define MAXASIZE luaM_limitN(1u << MAXABITS, TValue) /* ** MAXHBITS is the largest integer such that 2^MAXHBITS fits in a ** signed int. */ #define MAXHBITS (MAXABITS - 1) /* ** MAXHSIZE is the maximum size of the hash part. It is the minimum ** between 2^MAXHBITS and the maximum size such that, measured in bytes, ** it fits in a 'size_t'. */ #define MAXHSIZE luaM_limitN(1u << MAXHBITS, Node) /* ** When the original hash value is good, hashing by a power of 2 ** avoids the cost of '%'. */ #define hashpow2(t,n) (gnode(t, lmod((n), sizenode(t)))) /* ** for other types, it is better to avoid modulo by power of 2, as ** they can have many 2 factors. */ #define hashmod(t,n) (gnode(t, ((n) % ((sizenode(t)-1)|1)))) #define hashstr(t,str) hashpow2(t, (str)->hash) #define hashboolean(t,p) hashpow2(t, p) #define hashpointer(t,p) hashmod(t, point2uint(p)) #define dummynode (&dummynode_) static const Node dummynode_ = { {{NULL}, LUA_VEMPTY, /* value's value and type */ LUA_VNIL, 0, {NULL}} /* key type, next, and key value */ }; static const TValue absentkey = {ABSTKEYCONSTANT}; /* ** Hash for integers. To allow a good hash, use the remainder operator ** ('%'). If integer fits as a non-negative int, compute an int ** remainder, which is faster. Otherwise, use an unsigned-integer ** remainder, which uses all bits and ensures a non-negative result. */ static Node *hashint (const Table *t, lua_Integer i) { lua_Unsigned ui = l_castS2U(i); if (ui <= (unsigned int)INT_MAX) return hashmod(t, cast_int(ui)); else return hashmod(t, ui); } /* ** Hash for floating-point numbers. ** The main computation should be just ** n = frexp(n, &i); return (n * INT_MAX) + i ** but there are some numerical subtleties. ** In a two-complement representation, INT_MAX does not has an exact ** representation as a float, but INT_MIN does; because the absolute ** value of 'frexp' is smaller than 1 (unless 'n' is inf/NaN), the ** absolute value of the product 'frexp * -INT_MIN' is smaller or equal ** to INT_MAX. Next, the use of 'unsigned int' avoids overflows when ** adding 'i'; the use of '~u' (instead of '-u') avoids problems with ** INT_MIN. */ #if !defined(l_hashfloat) static int l_hashfloat (lua_Number n) { int i; lua_Integer ni; n = l_mathop(frexp)(n, &i) * -cast_num(INT_MIN); if (!lua_numbertointeger(n, &ni)) { /* is 'n' inf/-inf/NaN? */ lua_assert(luai_numisnan(n) || l_mathop(fabs)(n) == cast_num(HUGE_VAL)); return 0; } else { /* normal case */ unsigned int u = cast_uint(i) + cast_uint(ni); return cast_int(u <= cast_uint(INT_MAX) ? u : ~u); } } #endif /* ** returns the 'main' position of an element in a table (that is, ** the index of its hash value). */ static Node *mainpositionTV (const Table *t, const TValue *key) { switch (ttypetag(key)) { case LUA_VNUMINT: { lua_Integer i = ivalue(key); return hashint(t, i); } case LUA_VNUMFLT: { lua_Number n = fltvalue(key); return hashmod(t, l_hashfloat(n)); } case LUA_VSHRSTR: { TString *ts = tsvalue(key); return hashstr(t, ts); } case LUA_VLNGSTR: { TString *ts = tsvalue(key); return hashpow2(t, luaS_hashlongstr(ts)); } case LUA_VFALSE: return hashboolean(t, 0); case LUA_VTRUE: return hashboolean(t, 1); case LUA_VLIGHTUSERDATA: { void *p = pvalue(key); return hashpointer(t, p); } case LUA_VLCF: { lua_CFunction f = fvalue(key); return hashpointer(t, f); } default: { GCObject *o = gcvalue(key); return hashpointer(t, o); } } } l_sinline Node *mainpositionfromnode (const Table *t, Node *nd) { TValue key; getnodekey(cast(lua_State *, NULL), &key, nd); return mainpositionTV(t, &key); } /* ** Check whether key 'k1' is equal to the key in node 'n2'. This ** equality is raw, so there are no metamethods. Floats with integer ** values have been normalized, so integers cannot be equal to ** floats. It is assumed that 'eqshrstr' is simply pointer equality, so ** that short strings are handled in the default case. ** A true 'deadok' means to accept dead keys as equal to their original ** values. All dead keys are compared in the default case, by pointer ** identity. (Only collectable objects can produce dead keys.) Note that ** dead long strings are also compared by identity. ** Once a key is dead, its corresponding value may be collected, and ** then another value can be created with the same address. If this ** other value is given to 'next', 'equalkey' will signal a false ** positive. In a regular traversal, this situation should never happen, ** as all keys given to 'next' came from the table itself, and therefore ** could not have been collected. Outside a regular traversal, we ** have garbage in, garbage out. What is relevant is that this false ** positive does not break anything. (In particular, 'next' will return ** some other valid item on the table or nil.) */ static int equalkey (const TValue *k1, const Node *n2, int deadok) { if ((rawtt(k1) != keytt(n2)) && /* not the same variants? */ !(deadok && keyisdead(n2) && iscollectable(k1))) return 0; /* cannot be same key */ switch (keytt(n2)) { case LUA_VNIL: case LUA_VFALSE: case LUA_VTRUE: return 1; case LUA_VNUMINT: return (ivalue(k1) == keyival(n2)); case LUA_VNUMFLT: return luai_numeq(fltvalue(k1), fltvalueraw(keyval(n2))); case LUA_VLIGHTUSERDATA: return pvalue(k1) == pvalueraw(keyval(n2)); case LUA_VLCF: return fvalue(k1) == fvalueraw(keyval(n2)); case ctb(LUA_VLNGSTR): return luaS_eqlngstr(tsvalue(k1), keystrval(n2)); default: return gcvalue(k1) == gcvalueraw(keyval(n2)); } } /* ** True if value of 'alimit' is equal to the real size of the array ** part of table 't'. (Otherwise, the array part must be larger than ** 'alimit'.) */ #define limitequalsasize(t) (isrealasize(t) || ispow2((t)->alimit)) /* ** Returns the real size of the 'array' array */ LUAI_FUNC unsigned int luaH_realasize (const Table *t) { if (limitequalsasize(t)) return t->alimit; /* this is the size */ else { unsigned int size = t->alimit; /* compute the smallest power of 2 not smaller than 'n' */ size |= (size >> 1); size |= (size >> 2); size |= (size >> 4); size |= (size >> 8); size |= (size >> 16); #if (UINT_MAX >> 30) > 3 size |= (size >> 32); /* unsigned int has more than 32 bits */ #endif size++; lua_assert(ispow2(size) && size/2 < t->alimit && t->alimit < size); return size; } } /* ** Check whether real size of the array is a power of 2. ** (If it is not, 'alimit' cannot be changed to any other value ** without changing the real size.) */ static int ispow2realasize (const Table *t) { return (!isrealasize(t) || ispow2(t->alimit)); } static unsigned int setlimittosize (Table *t) { t->alimit = luaH_realasize(t); setrealasize(t); return t->alimit; } #define limitasasize(t) check_exp(isrealasize(t), t->alimit) /* ** "Generic" get version. (Not that generic: not valid for integers, ** which may be in array part, nor for floats with integral values.) ** See explanation about 'deadok' in function 'equalkey'. */ static const TValue *getgeneric (Table *t, const TValue *key, int deadok) { Node *n = mainpositionTV(t, key); for (;;) { /* check whether 'key' is somewhere in the chain */ if (equalkey(key, n, deadok)) return gval(n); /* that's it */ else { int nx = gnext(n); if (nx == 0) return &absentkey; /* not found */ n += nx; } } } /* ** returns the index for 'k' if 'k' is an appropriate key to live in ** the array part of a table, 0 otherwise. */ static unsigned int arrayindex (lua_Integer k) { if (l_castS2U(k) - 1u < MAXASIZE) /* 'k' in [1, MAXASIZE]? */ return cast_uint(k); /* 'key' is an appropriate array index */ else return 0; } /* ** returns the index of a 'key' for table traversals. First goes all ** elements in the array part, then elements in the hash part. The ** beginning of a traversal is signaled by 0. */ static unsigned int findindex (lua_State *L, Table *t, TValue *key, unsigned int asize) { unsigned int i; if (ttisnil(key)) return 0; /* first iteration */ i = ttisinteger(key) ? arrayindex(ivalue(key)) : 0; if (i - 1u < asize) /* is 'key' inside array part? */ return i; /* yes; that's the index */ else { const TValue *n = getgeneric(t, key, 1); if (l_unlikely(isabstkey(n))) luaG_runerror(L, "invalid key to 'next'"); /* key not found */ i = cast_int(nodefromval(n) - gnode(t, 0)); /* key index in hash table */ /* hash elements are numbered after array ones */ return (i + 1) + asize; } } int luaH_next (lua_State *L, Table *t, StkId key) { unsigned int asize = luaH_realasize(t); unsigned int i = findindex(L, t, s2v(key), asize); /* find original key */ for (; i < asize; i++) { /* try first array part */ if (!isempty(&t->array[i])) { /* a non-empty entry? */ setivalue(s2v(key), i + 1); setobj2s(L, key + 1, &t->array[i]); return 1; } } for (i -= asize; cast_int(i) < sizenode(t); i++) { /* hash part */ if (!isempty(gval(gnode(t, i)))) { /* a non-empty entry? */ Node *n = gnode(t, i); getnodekey(L, s2v(key), n); setobj2s(L, key + 1, gval(n)); return 1; } } return 0; /* no more elements */ } static void freehash (lua_State *L, Table *t) { if (!isdummy(t)) luaM_freearray(L, t->node, cast_sizet(sizenode(t))); } /* ** {============================================================= ** Rehash ** ============================================================== */ /* ** Compute the optimal size for the array part of table 't'. 'nums' is a ** "count array" where 'nums[i]' is the number of integers in the table ** between 2^(i - 1) + 1 and 2^i. 'pna' enters with the total number of ** integer keys in the table and leaves with the number of keys that ** will go to the array part; return the optimal size. (The condition ** 'twotoi > 0' in the for loop stops the loop if 'twotoi' overflows.) */ static unsigned int computesizes (unsigned int nums[], unsigned int *pna) { int i; unsigned int twotoi; /* 2^i (candidate for optimal size) */ unsigned int a = 0; /* number of elements smaller than 2^i */ unsigned int na = 0; /* number of elements to go to array part */ unsigned int optimal = 0; /* optimal size for array part */ /* loop while keys can fill more than half of total size */ for (i = 0, twotoi = 1; twotoi > 0 && *pna > twotoi / 2; i++, twotoi *= 2) { a += nums[i]; if (a > twotoi/2) { /* more than half elements present? */ optimal = twotoi; /* optimal size (till now) */ na = a; /* all elements up to 'optimal' will go to array part */ } } lua_assert((optimal == 0 || optimal / 2 < na) && na <= optimal); *pna = na; return optimal; } static int countint (lua_Integer key, unsigned int *nums) { unsigned int k = arrayindex(key); if (k != 0) { /* is 'key' an appropriate array index? */ nums[luaO_ceillog2(k)]++; /* count as such */ return 1; } else return 0; } /* ** Count keys in array part of table 't': Fill 'nums[i]' with ** number of keys that will go into corresponding slice and return ** total number of non-nil keys. */ static unsigned int numusearray (const Table *t, unsigned int *nums) { int lg; unsigned int ttlg; /* 2^lg */ unsigned int ause = 0; /* summation of 'nums' */ unsigned int i = 1; /* count to traverse all array keys */ unsigned int asize = limitasasize(t); /* real array size */ /* traverse each slice */ for (lg = 0, ttlg = 1; lg <= MAXABITS; lg++, ttlg *= 2) { unsigned int lc = 0; /* counter */ unsigned int lim = ttlg; if (lim > asize) { lim = asize; /* adjust upper limit */ if (i > lim) break; /* no more elements to count */ } /* count elements in range (2^(lg - 1), 2^lg] */ for (; i <= lim; i++) { if (!isempty(&t->array[i-1])) lc++; } nums[lg] += lc; ause += lc; } return ause; } static int numusehash (const Table *t, unsigned int *nums, unsigned int *pna) { int totaluse = 0; /* total number of elements */ int ause = 0; /* elements added to 'nums' (can go to array part) */ int i = sizenode(t); while (i--) { Node *n = &t->node[i]; if (!isempty(gval(n))) { if (keyisinteger(n)) ause += countint(keyival(n), nums); totaluse++; } } *pna += ause; return totaluse; } /* ** Creates an array for the hash part of a table with the given ** size, or reuses the dummy node if size is zero. ** The computation for size overflow is in two steps: the first ** comparison ensures that the shift in the second one does not ** overflow. */ static void setnodevector (lua_State *L, Table *t, unsigned int size) { if (size == 0) { /* no elements to hash part? */ t->node = cast(Node *, dummynode); /* use common 'dummynode' */ t->lsizenode = 0; t->lastfree = NULL; /* signal that it is using dummy node */ } else { int i; int lsize = luaO_ceillog2(size); if (lsize > MAXHBITS || (1u << lsize) > MAXHSIZE) luaG_runerror(L, "table overflow"); size = twoto(lsize); t->node = luaM_newvector(L, size, Node); for (i = 0; i < (int)size; i++) { Node *n = gnode(t, i); gnext(n) = 0; setnilkey(n); setempty(gval(n)); } t->lsizenode = cast_byte(lsize); t->lastfree = gnode(t, size); /* all positions are free */ } } /* ** (Re)insert all elements from the hash part of 'ot' into table 't'. */ static void reinsert (lua_State *L, Table *ot, Table *t) { int j; int size = sizenode(ot); for (j = 0; j < size; j++) { Node *old = gnode(ot, j); if (!isempty(gval(old))) { /* doesn't need barrier/invalidate cache, as entry was already present in the table */ TValue k; getnodekey(L, &k, old); luaH_set(L, t, &k, gval(old)); } } } /* ** Exchange the hash part of 't1' and 't2'. */ static void exchangehashpart (Table *t1, Table *t2) { lu_byte lsizenode = t1->lsizenode; Node *node = t1->node; Node *lastfree = t1->lastfree; t1->lsizenode = t2->lsizenode; t1->node = t2->node; t1->lastfree = t2->lastfree; t2->lsizenode = lsizenode; t2->node = node; t2->lastfree = lastfree; } /* ** Resize table 't' for the new given sizes. Both allocations (for ** the hash part and for the array part) can fail, which creates some ** subtleties. If the first allocation, for the hash part, fails, an ** error is raised and that is it. Otherwise, it copies the elements from ** the shrinking part of the array (if it is shrinking) into the new ** hash. Then it reallocates the array part. If that fails, the table ** is in its original state; the function frees the new hash part and then ** raises the allocation error. Otherwise, it sets the new hash part ** into the table, initializes the new part of the array (if any) with ** nils and reinserts the elements of the old hash back into the new ** parts of the table. */ void luaH_resize (lua_State *L, Table *t, unsigned int newasize, unsigned int nhsize) { unsigned int i; Table newt; /* to keep the new hash part */ unsigned int oldasize = setlimittosize(t); TValue *newarray; /* create new hash part with appropriate size into 'newt' */ setnodevector(L, &newt, nhsize); if (newasize < oldasize) { /* will array shrink? */ t->alimit = newasize; /* pretend array has new size... */ exchangehashpart(t, &newt); /* and new hash */ /* re-insert into the new hash the elements from vanishing slice */ for (i = newasize; i < oldasize; i++) { if (!isempty(&t->array[i])) luaH_setint(L, t, i + 1, &t->array[i]); } t->alimit = oldasize; /* restore current size... */ exchangehashpart(t, &newt); /* and hash (in case of errors) */ } /* allocate new array */ newarray = luaM_reallocvector(L, t->array, oldasize, newasize, TValue); if (l_unlikely(newarray == NULL && newasize > 0)) { /* allocation failed? */ freehash(L, &newt); /* release new hash part */ luaM_error(L); /* raise error (with array unchanged) */ } /* allocation ok; initialize new part of the array */ exchangehashpart(t, &newt); /* 't' has the new hash ('newt' has the old) */ t->array = newarray; /* set new array part */ t->alimit = newasize; for (i = oldasize; i < newasize; i++) /* clear new slice of the array */ setempty(&t->array[i]); /* re-insert elements from old hash part into new parts */ reinsert(L, &newt, t); /* 'newt' now has the old hash */ freehash(L, &newt); /* free old hash part */ } void luaH_resizearray (lua_State *L, Table *t, unsigned int nasize) { int nsize = allocsizenode(t); luaH_resize(L, t, nasize, nsize); } /* ** nums[i] = number of keys 'k' where 2^(i - 1) < k <= 2^i */ static void rehash (lua_State *L, Table *t, const TValue *ek) { unsigned int asize; /* optimal size for array part */ unsigned int na; /* number of keys in the array part */ unsigned int nums[MAXABITS + 1]; int i; int totaluse; for (i = 0; i <= MAXABITS; i++) nums[i] = 0; /* reset counts */ setlimittosize(t); na = numusearray(t, nums); /* count keys in array part */ totaluse = na; /* all those keys are integer keys */ totaluse += numusehash(t, nums, &na); /* count keys in hash part */ /* count extra key */ if (ttisinteger(ek)) na += countint(ivalue(ek), nums); totaluse++; /* compute new size for array part */ asize = computesizes(nums, &na); /* resize the table to new computed sizes */ luaH_resize(L, t, asize, totaluse - na); } /* ** }============================================================= */ Table *luaH_new (lua_State *L) { GCObject *o = luaC_newobj(L, LUA_VTABLE, sizeof(Table)); Table *t = gco2t(o); t->metatable = NULL; t->flags = cast_byte(maskflags); /* table has no metamethod fields */ t->array = NULL; t->alimit = 0; setnodevector(L, t, 0); return t; } void luaH_free (lua_State *L, Table *t) { freehash(L, t); luaM_freearray(L, t->array, luaH_realasize(t)); luaM_free(L, t); } static Node *getfreepos (Table *t) { if (!isdummy(t)) { while (t->lastfree > t->node) { t->lastfree--; if (keyisnil(t->lastfree)) return t->lastfree; } } return NULL; /* could not find a free place */ } /* ** inserts a new key into a hash table; first, check whether key's main ** position is free. If not, check whether colliding node is in its main ** position or not: if it is not, move colliding node to an empty place and ** put new key in its main position; otherwise (colliding node is in its main ** position), new key goes to an empty position. */ void luaH_newkey (lua_State *L, Table *t, const TValue *key, TValue *value) { Node *mp; TValue aux; if (l_unlikely(ttisnil(key))) luaG_runerror(L, "table index is nil"); else if (ttisfloat(key)) { lua_Number f = fltvalue(key); lua_Integer k; if (luaV_flttointeger(f, &k, F2Ieq)) { /* does key fit in an integer? */ setivalue(&aux, k); key = &aux; /* insert it as an integer */ } else if (l_unlikely(luai_numisnan(f))) luaG_runerror(L, "table index is NaN"); } if (ttisnil(value)) return; /* do not insert nil values */ mp = mainpositionTV(t, key); if (!isempty(gval(mp)) || isdummy(t)) { /* main position is taken? */ Node *othern; Node *f = getfreepos(t); /* get a free place */ if (f == NULL) { /* cannot find a free place? */ rehash(L, t, key); /* grow table */ /* whatever called 'newkey' takes care of TM cache */ luaH_set(L, t, key, value); /* insert key into grown table */ return; } lua_assert(!isdummy(t)); othern = mainpositionfromnode(t, mp); if (othern != mp) { /* is colliding node out of its main position? */ /* yes; move colliding node into free position */ while (othern + gnext(othern) != mp) /* find previous */ othern += gnext(othern); gnext(othern) = cast_int(f - othern); /* rechain to point to 'f' */ *f = *mp; /* copy colliding node into free pos. (mp->next also goes) */ if (gnext(mp) != 0) { gnext(f) += cast_int(mp - f); /* correct 'next' */ gnext(mp) = 0; /* now 'mp' is free */ } setempty(gval(mp)); } else { /* colliding node is in its own main position */ /* new node will go into free position */ if (gnext(mp) != 0) gnext(f) = cast_int((mp + gnext(mp)) - f); /* chain new position */ else lua_assert(gnext(f) == 0); gnext(mp) = cast_int(f - mp); mp = f; } } setnodekey(L, mp, key); luaC_barrierback(L, obj2gco(t), key); lua_assert(isempty(gval(mp))); setobj2t(L, gval(mp), value); } /* ** Search function for integers. If integer is inside 'alimit', get it ** directly from the array part. Otherwise, if 'alimit' is not equal to ** the real size of the array, key still can be in the array part. In ** this case, try to avoid a call to 'luaH_realasize' when key is just ** one more than the limit (so that it can be incremented without ** changing the real size of the array). */ const TValue *luaH_getint (Table *t, lua_Integer key) { if (l_castS2U(key) - 1u < t->alimit) /* 'key' in [1, t->alimit]? */ return &t->array[key - 1]; else if (!limitequalsasize(t) && /* key still may be in the array part? */ (l_castS2U(key) == t->alimit + 1 || l_castS2U(key) - 1u < luaH_realasize(t))) { t->alimit = cast_uint(key); /* probably '#t' is here now */ return &t->array[key - 1]; } else { Node *n = hashint(t, key); for (;;) { /* check whether 'key' is somewhere in the chain */ if (keyisinteger(n) && keyival(n) == key) return gval(n); /* that's it */ else { int nx = gnext(n); if (nx == 0) break; n += nx; } } return &absentkey; } } /* ** search function for short strings */ const TValue *luaH_getshortstr (Table *t, TString *key) { Node *n = hashstr(t, key); lua_assert(key->tt == LUA_VSHRSTR); for (;;) { /* check whether 'key' is somewhere in the chain */ if (keyisshrstr(n) && eqshrstr(keystrval(n), key)) return gval(n); /* that's it */ else { int nx = gnext(n); if (nx == 0) return &absentkey; /* not found */ n += nx; } } } const TValue *luaH_getstr (Table *t, TString *key) { if (key->tt == LUA_VSHRSTR) return luaH_getshortstr(t, key); else { /* for long strings, use generic case */ TValue ko; setsvalue(cast(lua_State *, NULL), &ko, key); return getgeneric(t, &ko, 0); } } /* ** main search function */ const TValue *luaH_get (Table *t, const TValue *key) { switch (ttypetag(key)) { case LUA_VSHRSTR: return luaH_getshortstr(t, tsvalue(key)); case LUA_VNUMINT: return luaH_getint(t, ivalue(key)); case LUA_VNIL: return &absentkey; case LUA_VNUMFLT: { lua_Integer k; if (luaV_flttointeger(fltvalue(key), &k, F2Ieq)) /* integral index? */ return luaH_getint(t, k); /* use specialized version */ /* else... */ } /* FALLTHROUGH */ default: return getgeneric(t, key, 0); } } /* ** Finish a raw "set table" operation, where 'slot' is where the value ** should have been (the result of a previous "get table"). ** Beware: when using this function you probably need to check a GC ** barrier and invalidate the TM cache. */ void luaH_finishset (lua_State *L, Table *t, const TValue *key, const TValue *slot, TValue *value) { if (isabstkey(slot)) luaH_newkey(L, t, key, value); else setobj2t(L, cast(TValue *, slot), value); } /* ** beware: when using this function you probably need to check a GC ** barrier and invalidate the TM cache. */ void luaH_set (lua_State *L, Table *t, const TValue *key, TValue *value) { const TValue *slot = luaH_get(t, key); luaH_finishset(L, t, key, slot, value); } void luaH_setint (lua_State *L, Table *t, lua_Integer key, TValue *value) { const TValue *p = luaH_getint(t, key); if (isabstkey(p)) { TValue k; setivalue(&k, key); luaH_newkey(L, t, &k, value); } else setobj2t(L, cast(TValue *, p), value); } /* ** Try to find a boundary in the hash part of table 't'. From the ** caller, we know that 'j' is zero or present and that 'j + 1' is ** present. We want to find a larger key that is absent from the ** table, so that we can do a binary search between the two keys to ** find a boundary. We keep doubling 'j' until we get an absent index. ** If the doubling would overflow, we try LUA_MAXINTEGER. If it is ** absent, we are ready for the binary search. ('j', being max integer, ** is larger or equal to 'i', but it cannot be equal because it is ** absent while 'i' is present; so 'j > i'.) Otherwise, 'j' is a ** boundary. ('j + 1' cannot be a present integer key because it is ** not a valid integer in Lua.) */ static lua_Unsigned hash_search (Table *t, lua_Unsigned j) { lua_Unsigned i; if (j == 0) j++; /* the caller ensures 'j + 1' is present */ do { i = j; /* 'i' is a present index */ if (j <= l_castS2U(LUA_MAXINTEGER) / 2) j *= 2; else { j = LUA_MAXINTEGER; if (isempty(luaH_getint(t, j))) /* t[j] not present? */ break; /* 'j' now is an absent index */ else /* weird case */ return j; /* well, max integer is a boundary... */ } } while (!isempty(luaH_getint(t, j))); /* repeat until an absent t[j] */ /* i < j && t[i] present && t[j] absent */ while (j - i > 1u) { /* do a binary search between them */ lua_Unsigned m = (i + j) / 2; if (isempty(luaH_getint(t, m))) j = m; else i = m; } return i; } static unsigned int binsearch (const TValue *array, unsigned int i, unsigned int j) { while (j - i > 1u) { /* binary search */ unsigned int m = (i + j) / 2; if (isempty(&array[m - 1])) j = m; else i = m; } return i; } /* ** Try to find a boundary in table 't'. (A 'boundary' is an integer index ** such that t[i] is present and t[i+1] is absent, or 0 if t[1] is absent ** and 'maxinteger' if t[maxinteger] is present.) ** (In the next explanation, we use Lua indices, that is, with base 1. ** The code itself uses base 0 when indexing the array part of the table.) ** The code starts with 'limit = t->alimit', a position in the array ** part that may be a boundary. ** ** (1) If 't[limit]' is empty, there must be a boundary before it. ** As a common case (e.g., after 't[#t]=nil'), check whether 'limit-1' ** is present. If so, it is a boundary. Otherwise, do a binary search ** between 0 and limit to find a boundary. In both cases, try to ** use this boundary as the new 'alimit', as a hint for the next call. ** ** (2) If 't[limit]' is not empty and the array has more elements ** after 'limit', try to find a boundary there. Again, try first ** the special case (which should be quite frequent) where 'limit+1' ** is empty, so that 'limit' is a boundary. Otherwise, check the ** last element of the array part. If it is empty, there must be a ** boundary between the old limit (present) and the last element ** (absent), which is found with a binary search. (This boundary always ** can be a new limit.) ** ** (3) The last case is when there are no elements in the array part ** (limit == 0) or its last element (the new limit) is present. ** In this case, must check the hash part. If there is no hash part ** or 'limit+1' is absent, 'limit' is a boundary. Otherwise, call ** 'hash_search' to find a boundary in the hash part of the table. ** (In those cases, the boundary is not inside the array part, and ** therefore cannot be used as a new limit.) */ lua_Unsigned luaH_getn (Table *t) { unsigned int limit = t->alimit; if (limit > 0 && isempty(&t->array[limit - 1])) { /* (1)? */ /* there must be a boundary before 'limit' */ if (limit >= 2 && !isempty(&t->array[limit - 2])) { /* 'limit - 1' is a boundary; can it be a new limit? */ if (ispow2realasize(t) && !ispow2(limit - 1)) { t->alimit = limit - 1; setnorealasize(t); /* now 'alimit' is not the real size */ } return limit - 1; } else { /* must search for a boundary in [0, limit] */ unsigned int boundary = binsearch(t->array, 0, limit); /* can this boundary represent the real size of the array? */ if (ispow2realasize(t) && boundary > luaH_realasize(t) / 2) { t->alimit = boundary; /* use it as the new limit */ setnorealasize(t); } return boundary; } } /* 'limit' is zero or present in table */ if (!limitequalsasize(t)) { /* (2)? */ /* 'limit' > 0 and array has more elements after 'limit' */ if (isempty(&t->array[limit])) /* 'limit + 1' is empty? */ return limit; /* this is the boundary */ /* else, try last element in the array */ limit = luaH_realasize(t); if (isempty(&t->array[limit - 1])) { /* empty? */ /* there must be a boundary in the array after old limit, and it must be a valid new limit */ unsigned int boundary = binsearch(t->array, t->alimit, limit); t->alimit = boundary; return boundary; } /* else, new limit is present in the table; check the hash part */ } /* (3) 'limit' is the last element and either is zero or present in table */ lua_assert(limit == luaH_realasize(t) && (limit == 0 || !isempty(&t->array[limit - 1]))); if (isdummy(t) || isempty(luaH_getint(t, cast(lua_Integer, limit + 1)))) return limit; /* 'limit + 1' is absent */ else /* 'limit + 1' is also present */ return hash_search(t, limit); } #if defined(LUA_DEBUG) /* export these functions for the test library */ Node *luaH_mainposition (const Table *t, const TValue *key) { return mainpositionTV(t, key); } int luaH_isdummy (const Table *t) { return isdummy(t); } #endif /* ** $Id: ldo.c $ ** Stack and Call structure of Lua ** See Copyright Notice in lua.h */ #define ldo_c #define LUA_CORE /*#include "lprefix.h"*/ #include #include #include /*#include "lua.h"*/ /*#include "lapi.h"*/ /*#include "ldebug.h"*/ /*#include "ldo.h"*/ /*#include "lfunc.h"*/ /*#include "lgc.h"*/ /*#include "lmem.h"*/ /*#include "lobject.h"*/ /*#include "lopcodes.h"*/ /*#include "lparser.h"*/ /*#include "lstate.h"*/ /*#include "lstring.h"*/ /*#include "ltable.h"*/ /*#include "ltm.h"*/ /*#include "lundump.h"*/ /*#include "lvm.h"*/ /*#include "lzio.h"*/ #define errorstatus(s) ((s) > LUA_YIELD) /* ** {====================================================== ** Error-recovery functions ** ======================================================= */ /* ** LUAI_THROW/LUAI_TRY define how Lua does exception handling. By ** default, Lua handles errors with exceptions when compiling as ** C++ code, with _longjmp/_setjmp when asked to use them, and with ** longjmp/setjmp otherwise. */ #if !defined(LUAI_THROW) /* { */ #if defined(__cplusplus) && !defined(LUA_USE_LONGJMP) /* { */ /* C++ exceptions */ #define LUAI_THROW(L,c) throw(c) #define LUAI_TRY(L,c,a) \ try { a } catch(...) { if ((c)->status == 0) (c)->status = -1; } #define luai_jmpbuf int /* dummy variable */ #elif defined(LUA_USE_POSIX) /* }{ */ /* in POSIX, try _longjmp/_setjmp (more efficient) */ #define LUAI_THROW(L,c) _longjmp((c)->b, 1) #define LUAI_TRY(L,c,a) if (_setjmp((c)->b) == 0) { a } #define luai_jmpbuf jmp_buf #else /* }{ */ /* ISO C handling with long jumps */ #define LUAI_THROW(L,c) longjmp((c)->b, 1) #define LUAI_TRY(L,c,a) if (setjmp((c)->b) == 0) { a } #define luai_jmpbuf jmp_buf #endif /* } */ #endif /* } */ /* chain list of long jump buffers */ struct lua_longjmp { struct lua_longjmp *previous; luai_jmpbuf b; volatile int status; /* error code */ }; void luaD_seterrorobj (lua_State *L, int errcode, StkId oldtop) { switch (errcode) { case LUA_ERRMEM: { /* memory error? */ setsvalue2s(L, oldtop, G(L)->memerrmsg); /* reuse preregistered msg. */ break; } case LUA_ERRERR: { setsvalue2s(L, oldtop, luaS_newliteral(L, "error in error handling")); break; } case LUA_OK: { /* special case only for closing upvalues */ setnilvalue(s2v(oldtop)); /* no error message */ break; } default: { lua_assert(errorstatus(errcode)); /* real error */ setobjs2s(L, oldtop, L->top - 1); /* error message on current top */ break; } } L->top = oldtop + 1; } l_noret luaD_throw (lua_State *L, int errcode) { if (L->errorJmp) { /* thread has an error handler? */ L->errorJmp->status = errcode; /* set status */ LUAI_THROW(L, L->errorJmp); /* jump to it */ } else { /* thread has no error handler */ global_State *g = G(L); errcode = luaE_resetthread(L, errcode); /* close all upvalues */ if (g->mainthread->errorJmp) { /* main thread has a handler? */ setobjs2s(L, g->mainthread->top++, L->top - 1); /* copy error obj. */ luaD_throw(g->mainthread, errcode); /* re-throw in main thread */ } else { /* no handler at all; abort */ if (g->panic) { /* panic function? */ lua_unlock(L); g->panic(L); /* call panic function (last chance to jump out) */ } abort(); } } } int luaD_rawrunprotected (lua_State *L, Pfunc f, void *ud) { l_uint32 oldnCcalls = L->nCcalls; struct lua_longjmp lj; lj.status = LUA_OK; lj.previous = L->errorJmp; /* chain new error handler */ L->errorJmp = &lj; LUAI_TRY(L, &lj, (*f)(L, ud); ); L->errorJmp = lj.previous; /* restore old error handler */ L->nCcalls = oldnCcalls; return lj.status; } /* }====================================================== */ /* ** {================================================================== ** Stack reallocation ** =================================================================== */ static void correctstack (lua_State *L, StkId oldstack, StkId newstack) { CallInfo *ci; UpVal *up; L->top = (L->top - oldstack) + newstack; L->tbclist = (L->tbclist - oldstack) + newstack; for (up = L->openupval; up != NULL; up = up->u.open.next) up->v = s2v((uplevel(up) - oldstack) + newstack); for (ci = L->ci; ci != NULL; ci = ci->previous) { ci->top = (ci->top - oldstack) + newstack; ci->func = (ci->func - oldstack) + newstack; if (isLua(ci)) ci->u.l.trap = 1; /* signal to update 'trap' in 'luaV_execute' */ } } /* some space for error handling */ #define ERRORSTACKSIZE (LUAI_MAXSTACK + 200) /* ** Reallocate the stack to a new size, correcting all pointers into ** it. (There are pointers to a stack from its upvalues, from its list ** of call infos, plus a few individual pointers.) The reallocation is ** done in two steps (allocation + free) because the correction must be ** done while both addresses (the old stack and the new one) are valid. ** (In ISO C, any pointer use after the pointer has been deallocated is ** undefined behavior.) ** In case of allocation error, raise an error or return false according ** to 'raiseerror'. */ int luaD_reallocstack (lua_State *L, int newsize, int raiseerror) { int oldsize = stacksize(L); int i; StkId newstack = luaM_reallocvector(L, NULL, 0, newsize + EXTRA_STACK, StackValue); lua_assert(newsize <= LUAI_MAXSTACK || newsize == ERRORSTACKSIZE); if (l_unlikely(newstack == NULL)) { /* reallocation failed? */ if (raiseerror) luaM_error(L); else return 0; /* do not raise an error */ } /* number of elements to be copied to the new stack */ i = ((oldsize <= newsize) ? oldsize : newsize) + EXTRA_STACK; memcpy(newstack, L->stack, i * sizeof(StackValue)); for (; i < newsize + EXTRA_STACK; i++) setnilvalue(s2v(newstack + i)); /* erase new segment */ correctstack(L, L->stack, newstack); luaM_freearray(L, L->stack, oldsize + EXTRA_STACK); L->stack = newstack; L->stack_last = L->stack + newsize; return 1; } /* ** Try to grow the stack by at least 'n' elements. when 'raiseerror' ** is true, raises any error; otherwise, return 0 in case of errors. */ int luaD_growstack (lua_State *L, int n, int raiseerror) { int size = stacksize(L); if (l_unlikely(size > LUAI_MAXSTACK)) { /* if stack is larger than maximum, thread is already using the extra space reserved for errors, that is, thread is handling a stack error; cannot grow further than that. */ lua_assert(stacksize(L) == ERRORSTACKSIZE); if (raiseerror) luaD_throw(L, LUA_ERRERR); /* error inside message handler */ return 0; /* if not 'raiseerror', just signal it */ } else { int newsize = 2 * size; /* tentative new size */ int needed = cast_int(L->top - L->stack) + n; if (newsize > LUAI_MAXSTACK) /* cannot cross the limit */ newsize = LUAI_MAXSTACK; if (newsize < needed) /* but must respect what was asked for */ newsize = needed; if (l_likely(newsize <= LUAI_MAXSTACK)) return luaD_reallocstack(L, newsize, raiseerror); else { /* stack overflow */ /* add extra size to be able to handle the error message */ luaD_reallocstack(L, ERRORSTACKSIZE, raiseerror); if (raiseerror) luaG_runerror(L, "stack overflow"); return 0; } } } static int stackinuse (lua_State *L) { CallInfo *ci; int res; StkId lim = L->top; for (ci = L->ci; ci != NULL; ci = ci->previous) { if (lim < ci->top) lim = ci->top; } lua_assert(lim <= L->stack_last); res = cast_int(lim - L->stack) + 1; /* part of stack in use */ if (res < LUA_MINSTACK) res = LUA_MINSTACK; /* ensure a minimum size */ return res; } /* ** If stack size is more than 3 times the current use, reduce that size ** to twice the current use. (So, the final stack size is at most 2/3 the ** previous size, and half of its entries are empty.) ** As a particular case, if stack was handling a stack overflow and now ** it is not, 'max' (limited by LUAI_MAXSTACK) will be smaller than ** stacksize (equal to ERRORSTACKSIZE in this case), and so the stack ** will be reduced to a "regular" size. */ void luaD_shrinkstack (lua_State *L) { int inuse = stackinuse(L); int nsize = inuse * 2; /* proposed new size */ int max = inuse * 3; /* maximum "reasonable" size */ if (max > LUAI_MAXSTACK) { max = LUAI_MAXSTACK; /* respect stack limit */ if (nsize > LUAI_MAXSTACK) nsize = LUAI_MAXSTACK; } /* if thread is currently not handling a stack overflow and its size is larger than maximum "reasonable" size, shrink it */ if (inuse <= LUAI_MAXSTACK && stacksize(L) > max) luaD_reallocstack(L, nsize, 0); /* ok if that fails */ else /* don't change stack */ condmovestack(L,{},{}); /* (change only for debugging) */ luaE_shrinkCI(L); /* shrink CI list */ } void luaD_inctop (lua_State *L) { luaD_checkstack(L, 1); L->top++; } /* }================================================================== */ /* ** Call a hook for the given event. Make sure there is a hook to be ** called. (Both 'L->hook' and 'L->hookmask', which trigger this ** function, can be changed asynchronously by signals.) */ void luaD_hook (lua_State *L, int event, int line, int ftransfer, int ntransfer) { lua_Hook hook = L->hook; if (hook && L->allowhook) { /* make sure there is a hook */ int mask = CIST_HOOKED; CallInfo *ci = L->ci; ptrdiff_t top = savestack(L, L->top); /* preserve original 'top' */ ptrdiff_t ci_top = savestack(L, ci->top); /* idem for 'ci->top' */ lua_Debug ar; ar.event = event; ar.currentline = line; ar.i_ci = ci; if (ntransfer != 0) { mask |= CIST_TRAN; /* 'ci' has transfer information */ ci->u2.transferinfo.ftransfer = ftransfer; ci->u2.transferinfo.ntransfer = ntransfer; } if (isLua(ci) && L->top < ci->top) L->top = ci->top; /* protect entire activation register */ luaD_checkstack(L, LUA_MINSTACK); /* ensure minimum stack size */ if (ci->top < L->top + LUA_MINSTACK) ci->top = L->top + LUA_MINSTACK; L->allowhook = 0; /* cannot call hooks inside a hook */ ci->callstatus |= mask; lua_unlock(L); (*hook)(L, &ar); lua_lock(L); lua_assert(!L->allowhook); L->allowhook = 1; ci->top = restorestack(L, ci_top); L->top = restorestack(L, top); ci->callstatus &= ~mask; } } /* ** Executes a call hook for Lua functions. This function is called ** whenever 'hookmask' is not zero, so it checks whether call hooks are ** active. */ void luaD_hookcall (lua_State *L, CallInfo *ci) { L->oldpc = 0; /* set 'oldpc' for new function */ if (L->hookmask & LUA_MASKCALL) { /* is call hook on? */ int event = (ci->callstatus & CIST_TAIL) ? LUA_HOOKTAILCALL : LUA_HOOKCALL; Proto *p = ci_func(ci)->p; ci->u.l.savedpc++; /* hooks assume 'pc' is already incremented */ luaD_hook(L, event, -1, 1, p->numparams); ci->u.l.savedpc--; /* correct 'pc' */ } } /* ** Executes a return hook for Lua and C functions and sets/corrects ** 'oldpc'. (Note that this correction is needed by the line hook, so it ** is done even when return hooks are off.) */ static void rethook (lua_State *L, CallInfo *ci, int nres) { if (L->hookmask & LUA_MASKRET) { /* is return hook on? */ StkId firstres = L->top - nres; /* index of first result */ int delta = 0; /* correction for vararg functions */ int ftransfer; if (isLua(ci)) { Proto *p = ci_func(ci)->p; if (p->is_vararg) delta = ci->u.l.nextraargs + p->numparams + 1; } ci->func += delta; /* if vararg, back to virtual 'func' */ ftransfer = cast(unsigned short, firstres - ci->func); luaD_hook(L, LUA_HOOKRET, -1, ftransfer, nres); /* call it */ ci->func -= delta; } if (isLua(ci = ci->previous)) L->oldpc = pcRel(ci->u.l.savedpc, ci_func(ci)->p); /* set 'oldpc' */ } /* ** Check whether 'func' has a '__call' metafield. If so, put it in the ** stack, below original 'func', so that 'luaD_precall' can call it. Raise ** an error if there is no '__call' metafield. */ StkId luaD_tryfuncTM (lua_State *L, StkId func) { const TValue *tm; StkId p; checkstackGCp(L, 1, func); /* space for metamethod */ tm = luaT_gettmbyobj(L, s2v(func), TM_CALL); /* (after previous GC) */ if (l_unlikely(ttisnil(tm))) luaG_callerror(L, s2v(func)); /* nothing to call */ for (p = L->top; p > func; p--) /* open space for metamethod */ setobjs2s(L, p, p-1); L->top++; /* stack space pre-allocated by the caller */ setobj2s(L, func, tm); /* metamethod is the new function to be called */ return func; } /* ** Given 'nres' results at 'firstResult', move 'wanted' of them to 'res'. ** Handle most typical cases (zero results for commands, one result for ** expressions, multiple results for tail calls/single parameters) ** separated. */ l_sinline void moveresults (lua_State *L, StkId res, int nres, int wanted) { StkId firstresult; int i; switch (wanted) { /* handle typical cases separately */ case 0: /* no values needed */ L->top = res; return; case 1: /* one value needed */ if (nres == 0) /* no results? */ setnilvalue(s2v(res)); /* adjust with nil */ else /* at least one result */ setobjs2s(L, res, L->top - nres); /* move it to proper place */ L->top = res + 1; return; case LUA_MULTRET: wanted = nres; /* we want all results */ break; default: /* two/more results and/or to-be-closed variables */ if (hastocloseCfunc(wanted)) { /* to-be-closed variables? */ ptrdiff_t savedres = savestack(L, res); L->ci->callstatus |= CIST_CLSRET; /* in case of yields */ L->ci->u2.nres = nres; luaF_close(L, res, CLOSEKTOP, 1); L->ci->callstatus &= ~CIST_CLSRET; if (L->hookmask) /* if needed, call hook after '__close's */ rethook(L, L->ci, nres); res = restorestack(L, savedres); /* close and hook can move stack */ wanted = decodeNresults(wanted); if (wanted == LUA_MULTRET) wanted = nres; /* we want all results */ } break; } /* generic case */ firstresult = L->top - nres; /* index of first result */ if (nres > wanted) /* extra results? */ nres = wanted; /* don't need them */ for (i = 0; i < nres; i++) /* move all results to correct place */ setobjs2s(L, res + i, firstresult + i); for (; i < wanted; i++) /* complete wanted number of results */ setnilvalue(s2v(res + i)); L->top = res + wanted; /* top points after the last result */ } /* ** Finishes a function call: calls hook if necessary, moves current ** number of results to proper place, and returns to previous call ** info. If function has to close variables, hook must be called after ** that. */ void luaD_poscall (lua_State *L, CallInfo *ci, int nres) { int wanted = ci->nresults; if (l_unlikely(L->hookmask && !hastocloseCfunc(wanted))) rethook(L, ci, nres); /* move results to proper place */ moveresults(L, ci->func, nres, wanted); /* function cannot be in any of these cases when returning */ lua_assert(!(ci->callstatus & (CIST_HOOKED | CIST_YPCALL | CIST_FIN | CIST_TRAN | CIST_CLSRET))); L->ci = ci->previous; /* back to caller (after closing variables) */ } #define next_ci(L) (L->ci->next ? L->ci->next : luaE_extendCI(L)) l_sinline CallInfo *prepCallInfo (lua_State *L, StkId func, int nret, int mask, StkId top) { CallInfo *ci = L->ci = next_ci(L); /* new frame */ ci->func = func; ci->nresults = nret; ci->callstatus = mask; ci->top = top; return ci; } /* ** precall for C functions */ l_sinline int precallC (lua_State *L, StkId func, int nresults, lua_CFunction f) { int n; /* number of returns */ CallInfo *ci; checkstackGCp(L, LUA_MINSTACK, func); /* ensure minimum stack size */ L->ci = ci = prepCallInfo(L, func, nresults, CIST_C, L->top + LUA_MINSTACK); lua_assert(ci->top <= L->stack_last); if (l_unlikely(L->hookmask & LUA_MASKCALL)) { int narg = cast_int(L->top - func) - 1; luaD_hook(L, LUA_HOOKCALL, -1, 1, narg); } lua_unlock(L); n = (*f)(L); /* do the actual call */ lua_lock(L); api_checknelems(L, n); luaD_poscall(L, ci, n); return n; } /* ** Prepare a function for a tail call, building its call info on top ** of the current call info. 'narg1' is the number of arguments plus 1 ** (so that it includes the function itself). Return the number of ** results, if it was a C function, or -1 for a Lua function. */ int luaD_pretailcall (lua_State *L, CallInfo *ci, StkId func, int narg1, int delta) { retry: switch (ttypetag(s2v(func))) { case LUA_VCCL: /* C closure */ return precallC(L, func, LUA_MULTRET, clCvalue(s2v(func))->f); case LUA_VLCF: /* light C function */ return precallC(L, func, LUA_MULTRET, fvalue(s2v(func))); case LUA_VLCL: { /* Lua function */ Proto *p = clLvalue(s2v(func))->p; int fsize = p->maxstacksize; /* frame size */ int nfixparams = p->numparams; int i; checkstackGCp(L, fsize - delta, func); ci->func -= delta; /* restore 'func' (if vararg) */ for (i = 0; i < narg1; i++) /* move down function and arguments */ setobjs2s(L, ci->func + i, func + i); func = ci->func; /* moved-down function */ for (; narg1 <= nfixparams; narg1++) setnilvalue(s2v(func + narg1)); /* complete missing arguments */ ci->top = func + 1 + fsize; /* top for new function */ lua_assert(ci->top <= L->stack_last); ci->u.l.savedpc = p->code; /* starting point */ ci->callstatus |= CIST_TAIL; L->top = func + narg1; /* set top */ return -1; } default: { /* not a function */ func = luaD_tryfuncTM(L, func); /* try to get '__call' metamethod */ /* return luaD_pretailcall(L, ci, func, narg1 + 1, delta); */ narg1++; goto retry; /* try again */ } } } /* ** Prepares the call to a function (C or Lua). For C functions, also do ** the call. The function to be called is at '*func'. The arguments ** are on the stack, right after the function. Returns the CallInfo ** to be executed, if it was a Lua function. Otherwise (a C function) ** returns NULL, with all the results on the stack, starting at the ** original function position. */ CallInfo *luaD_precall (lua_State *L, StkId func, int nresults) { retry: switch (ttypetag(s2v(func))) { case LUA_VCCL: /* C closure */ precallC(L, func, nresults, clCvalue(s2v(func))->f); return NULL; case LUA_VLCF: /* light C function */ precallC(L, func, nresults, fvalue(s2v(func))); return NULL; case LUA_VLCL: { /* Lua function */ CallInfo *ci; Proto *p = clLvalue(s2v(func))->p; int narg = cast_int(L->top - func) - 1; /* number of real arguments */ int nfixparams = p->numparams; int fsize = p->maxstacksize; /* frame size */ checkstackGCp(L, fsize, func); L->ci = ci = prepCallInfo(L, func, nresults, 0, func + 1 + fsize); ci->u.l.savedpc = p->code; /* starting point */ for (; narg < nfixparams; narg++) setnilvalue(s2v(L->top++)); /* complete missing arguments */ lua_assert(ci->top <= L->stack_last); return ci; } default: { /* not a function */ func = luaD_tryfuncTM(L, func); /* try to get '__call' metamethod */ /* return luaD_precall(L, func, nresults); */ goto retry; /* try again with metamethod */ } } } /* ** Call a function (C or Lua) through C. 'inc' can be 1 (increment ** number of recursive invocations in the C stack) or nyci (the same ** plus increment number of non-yieldable calls). */ l_sinline void ccall (lua_State *L, StkId func, int nResults, int inc) { CallInfo *ci; L->nCcalls += inc; if (l_unlikely(getCcalls(L) >= LUAI_MAXCCALLS)) luaE_checkcstack(L); if ((ci = luaD_precall(L, func, nResults)) != NULL) { /* Lua function? */ ci->callstatus = CIST_FRESH; /* mark that it is a "fresh" execute */ luaV_execute(L, ci); /* call it */ } L->nCcalls -= inc; } /* ** External interface for 'ccall' */ void luaD_call (lua_State *L, StkId func, int nResults) { ccall(L, func, nResults, 1); } /* ** Similar to 'luaD_call', but does not allow yields during the call. */ void luaD_callnoyield (lua_State *L, StkId func, int nResults) { ccall(L, func, nResults, nyci); } /* ** Finish the job of 'lua_pcallk' after it was interrupted by an yield. ** (The caller, 'finishCcall', does the final call to 'adjustresults'.) ** The main job is to complete the 'luaD_pcall' called by 'lua_pcallk'. ** If a '__close' method yields here, eventually control will be back ** to 'finishCcall' (when that '__close' method finally returns) and ** 'finishpcallk' will run again and close any still pending '__close' ** methods. Similarly, if a '__close' method errs, 'precover' calls ** 'unroll' which calls ''finishCcall' and we are back here again, to ** close any pending '__close' methods. ** Note that, up to the call to 'luaF_close', the corresponding ** 'CallInfo' is not modified, so that this repeated run works like the ** first one (except that it has at least one less '__close' to do). In ** particular, field CIST_RECST preserves the error status across these ** multiple runs, changing only if there is a new error. */ static int finishpcallk (lua_State *L, CallInfo *ci) { int status = getcistrecst(ci); /* get original status */ if (l_likely(status == LUA_OK)) /* no error? */ status = LUA_YIELD; /* was interrupted by an yield */ else { /* error */ StkId func = restorestack(L, ci->u2.funcidx); L->allowhook = getoah(ci->callstatus); /* restore 'allowhook' */ luaF_close(L, func, status, 1); /* can yield or raise an error */ func = restorestack(L, ci->u2.funcidx); /* stack may be moved */ luaD_seterrorobj(L, status, func); luaD_shrinkstack(L); /* restore stack size in case of overflow */ setcistrecst(ci, LUA_OK); /* clear original status */ } ci->callstatus &= ~CIST_YPCALL; L->errfunc = ci->u.c.old_errfunc; /* if it is here, there were errors or yields; unlike 'lua_pcallk', do not change status */ return status; } /* ** Completes the execution of a C function interrupted by an yield. ** The interruption must have happened while the function was either ** closing its tbc variables in 'moveresults' or executing ** 'lua_callk'/'lua_pcallk'. In the first case, it just redoes ** 'luaD_poscall'. In the second case, the call to 'finishpcallk' ** finishes the interrupted execution of 'lua_pcallk'. After that, it ** calls the continuation of the interrupted function and finally it ** completes the job of the 'luaD_call' that called the function. In ** the call to 'adjustresults', we do not know the number of results ** of the function called by 'lua_callk'/'lua_pcallk', so we are ** conservative and use LUA_MULTRET (always adjust). */ static void finishCcall (lua_State *L, CallInfo *ci) { int n; /* actual number of results from C function */ if (ci->callstatus & CIST_CLSRET) { /* was returning? */ lua_assert(hastocloseCfunc(ci->nresults)); n = ci->u2.nres; /* just redo 'luaD_poscall' */ /* don't need to reset CIST_CLSRET, as it will be set again anyway */ } else { int status = LUA_YIELD; /* default if there were no errors */ /* must have a continuation and must be able to call it */ lua_assert(ci->u.c.k != NULL && yieldable(L)); if (ci->callstatus & CIST_YPCALL) /* was inside a 'lua_pcallk'? */ status = finishpcallk(L, ci); /* finish it */ adjustresults(L, LUA_MULTRET); /* finish 'lua_callk' */ lua_unlock(L); n = (*ci->u.c.k)(L, status, ci->u.c.ctx); /* call continuation */ lua_lock(L); api_checknelems(L, n); } luaD_poscall(L, ci, n); /* finish 'luaD_call' */ } /* ** Executes "full continuation" (everything in the stack) of a ** previously interrupted coroutine until the stack is empty (or another ** interruption long-jumps out of the loop). */ static void unroll (lua_State *L, void *ud) { CallInfo *ci; UNUSED(ud); while ((ci = L->ci) != &L->base_ci) { /* something in the stack */ if (!isLua(ci)) /* C function? */ finishCcall(L, ci); /* complete its execution */ else { /* Lua function */ luaV_finishOp(L); /* finish interrupted instruction */ luaV_execute(L, ci); /* execute down to higher C 'boundary' */ } } } /* ** Try to find a suspended protected call (a "recover point") for the ** given thread. */ static CallInfo *findpcall (lua_State *L) { CallInfo *ci; for (ci = L->ci; ci != NULL; ci = ci->previous) { /* search for a pcall */ if (ci->callstatus & CIST_YPCALL) return ci; } return NULL; /* no pending pcall */ } /* ** Signal an error in the call to 'lua_resume', not in the execution ** of the coroutine itself. (Such errors should not be handled by any ** coroutine error handler and should not kill the coroutine.) */ static int resume_error (lua_State *L, const char *msg, int narg) { L->top -= narg; /* remove args from the stack */ setsvalue2s(L, L->top, luaS_new(L, msg)); /* push error message */ api_incr_top(L); lua_unlock(L); return LUA_ERRRUN; } /* ** Do the work for 'lua_resume' in protected mode. Most of the work ** depends on the status of the coroutine: initial state, suspended ** inside a hook, or regularly suspended (optionally with a continuation ** function), plus erroneous cases: non-suspended coroutine or dead ** coroutine. */ static void resume (lua_State *L, void *ud) { int n = *(cast(int*, ud)); /* number of arguments */ StkId firstArg = L->top - n; /* first argument */ CallInfo *ci = L->ci; if (L->status == LUA_OK) /* starting a coroutine? */ ccall(L, firstArg - 1, LUA_MULTRET, 0); /* just call its body */ else { /* resuming from previous yield */ lua_assert(L->status == LUA_YIELD); L->status = LUA_OK; /* mark that it is running (again) */ if (isLua(ci)) { /* yielded inside a hook? */ L->top = firstArg; /* discard arguments */ luaV_execute(L, ci); /* just continue running Lua code */ } else { /* 'common' yield */ if (ci->u.c.k != NULL) { /* does it have a continuation function? */ lua_unlock(L); n = (*ci->u.c.k)(L, LUA_YIELD, ci->u.c.ctx); /* call continuation */ lua_lock(L); api_checknelems(L, n); } luaD_poscall(L, ci, n); /* finish 'luaD_call' */ } unroll(L, NULL); /* run continuation */ } } /* ** Unrolls a coroutine in protected mode while there are recoverable ** errors, that is, errors inside a protected call. (Any error ** interrupts 'unroll', and this loop protects it again so it can ** continue.) Stops with a normal end (status == LUA_OK), an yield ** (status == LUA_YIELD), or an unprotected error ('findpcall' doesn't ** find a recover point). */ static int precover (lua_State *L, int status) { CallInfo *ci; while (errorstatus(status) && (ci = findpcall(L)) != NULL) { L->ci = ci; /* go down to recovery functions */ setcistrecst(ci, status); /* status to finish 'pcall' */ status = luaD_rawrunprotected(L, unroll, NULL); } return status; } LUA_API int lua_resume (lua_State *L, lua_State *from, int nargs, int *nresults) { int status; lua_lock(L); if (L->status == LUA_OK) { /* may be starting a coroutine */ if (L->ci != &L->base_ci) /* not in base level? */ return resume_error(L, "cannot resume non-suspended coroutine", nargs); else if (L->top - (L->ci->func + 1) == nargs) /* no function? */ return resume_error(L, "cannot resume dead coroutine", nargs); } else if (L->status != LUA_YIELD) /* ended with errors? */ return resume_error(L, "cannot resume dead coroutine", nargs); L->nCcalls = (from) ? getCcalls(from) : 0; if (getCcalls(L) >= LUAI_MAXCCALLS) return resume_error(L, "C stack overflow", nargs); L->nCcalls++; luai_userstateresume(L, nargs); api_checknelems(L, (L->status == LUA_OK) ? nargs + 1 : nargs); status = luaD_rawrunprotected(L, resume, &nargs); /* continue running after recoverable errors */ status = precover(L, status); if (l_likely(!errorstatus(status))) lua_assert(status == L->status); /* normal end or yield */ else { /* unrecoverable error */ L->status = cast_byte(status); /* mark thread as 'dead' */ luaD_seterrorobj(L, status, L->top); /* push error message */ L->ci->top = L->top; } *nresults = (status == LUA_YIELD) ? L->ci->u2.nyield : cast_int(L->top - (L->ci->func + 1)); lua_unlock(L); return status; } LUA_API int lua_isyieldable (lua_State *L) { return yieldable(L); } LUA_API int lua_yieldk (lua_State *L, int nresults, lua_KContext ctx, lua_KFunction k) { CallInfo *ci; luai_userstateyield(L, nresults); lua_lock(L); ci = L->ci; api_checknelems(L, nresults); if (l_unlikely(!yieldable(L))) { if (L != G(L)->mainthread) luaG_runerror(L, "attempt to yield across a C-call boundary"); else luaG_runerror(L, "attempt to yield from outside a coroutine"); } L->status = LUA_YIELD; ci->u2.nyield = nresults; /* save number of results */ if (isLua(ci)) { /* inside a hook? */ lua_assert(!isLuacode(ci)); api_check(L, nresults == 0, "hooks cannot yield values"); api_check(L, k == NULL, "hooks cannot continue after yielding"); } else { if ((ci->u.c.k = k) != NULL) /* is there a continuation? */ ci->u.c.ctx = ctx; /* save context */ luaD_throw(L, LUA_YIELD); } lua_assert(ci->callstatus & CIST_HOOKED); /* must be inside a hook */ lua_unlock(L); return 0; /* return to 'luaD_hook' */ } /* ** Auxiliary structure to call 'luaF_close' in protected mode. */ struct CloseP { StkId level; int status; }; /* ** Auxiliary function to call 'luaF_close' in protected mode. */ static void closepaux (lua_State *L, void *ud) { struct CloseP *pcl = cast(struct CloseP *, ud); luaF_close(L, pcl->level, pcl->status, 0); } /* ** Calls 'luaF_close' in protected mode. Return the original status ** or, in case of errors, the new status. */ int luaD_closeprotected (lua_State *L, ptrdiff_t level, int status) { CallInfo *old_ci = L->ci; lu_byte old_allowhooks = L->allowhook; for (;;) { /* keep closing upvalues until no more errors */ struct CloseP pcl; pcl.level = restorestack(L, level); pcl.status = status; status = luaD_rawrunprotected(L, &closepaux, &pcl); if (l_likely(status == LUA_OK)) /* no more errors? */ return pcl.status; else { /* an error occurred; restore saved state and repeat */ L->ci = old_ci; L->allowhook = old_allowhooks; } } } /* ** Call the C function 'func' in protected mode, restoring basic ** thread information ('allowhook', etc.) and in particular ** its stack level in case of errors. */ int luaD_pcall (lua_State *L, Pfunc func, void *u, ptrdiff_t old_top, ptrdiff_t ef) { int status; CallInfo *old_ci = L->ci; lu_byte old_allowhooks = L->allowhook; ptrdiff_t old_errfunc = L->errfunc; L->errfunc = ef; status = luaD_rawrunprotected(L, func, u); if (l_unlikely(status != LUA_OK)) { /* an error occurred? */ L->ci = old_ci; L->allowhook = old_allowhooks; status = luaD_closeprotected(L, old_top, status); luaD_seterrorobj(L, status, restorestack(L, old_top)); luaD_shrinkstack(L); /* restore stack size in case of overflow */ } L->errfunc = old_errfunc; return status; } /* ** Execute a protected parser. */ struct SParser { /* data to 'f_parser' */ ZIO *z; Mbuffer buff; /* dynamic structure used by the scanner */ Dyndata dyd; /* dynamic structures used by the parser */ const char *mode; const char *name; }; static void checkmode (lua_State *L, const char *mode, const char *x) { if (mode && strchr(mode, x[0]) == NULL) { luaO_pushfstring(L, "attempt to load a %s chunk (mode is '%s')", x, mode); luaD_throw(L, LUA_ERRSYNTAX); } } static void f_parser (lua_State *L, void *ud) { LClosure *cl; struct SParser *p = cast(struct SParser *, ud); int c = zgetc(p->z); /* read first character */ if (c == LUA_SIGNATURE[0]) { checkmode(L, p->mode, "binary"); cl = luaU_undump(L, p->z, p->name); } else { checkmode(L, p->mode, "text"); cl = luaY_parser(L, p->z, &p->buff, &p->dyd, p->name, c); } lua_assert(cl->nupvalues == cl->p->sizeupvalues); luaF_initupvals(L, cl); } int luaD_protectedparser (lua_State *L, ZIO *z, const char *name, const char *mode) { struct SParser p; int status; incnny(L); /* cannot yield during parsing */ p.z = z; p.name = name; p.mode = mode; p.dyd.actvar.arr = NULL; p.dyd.actvar.size = 0; p.dyd.gt.arr = NULL; p.dyd.gt.size = 0; p.dyd.label.arr = NULL; p.dyd.label.size = 0; luaZ_initbuffer(L, &p.buff); status = luaD_pcall(L, f_parser, &p, savestack(L, L->top), L->errfunc); luaZ_freebuffer(L, &p.buff); luaM_freearray(L, p.dyd.actvar.arr, p.dyd.actvar.size); luaM_freearray(L, p.dyd.gt.arr, p.dyd.gt.size); luaM_freearray(L, p.dyd.label.arr, p.dyd.label.size); decnny(L); return status; } /* ** $Id: lvm.c $ ** Lua virtual machine ** See Copyright Notice in lua.h */ #define lvm_c #define LUA_CORE /*#include "lprefix.h"*/ #include #include #include #include #include #include /*#include "lua.h"*/ /*#include "ldebug.h"*/ /*#include "ldo.h"*/ /*#include "lfunc.h"*/ /*#include "lgc.h"*/ /*#include "lobject.h"*/ /*#include "lopcodes.h"*/ /*#include "lstate.h"*/ /*#include "lstring.h"*/ /*#include "ltable.h"*/ /*#include "ltm.h"*/ /*#include "lvm.h"*/ /* ** By default, use jump tables in the main interpreter loop on gcc ** and compatible compilers. */ #if !defined(LUA_USE_JUMPTABLE) #if defined(__GNUC__) #define LUA_USE_JUMPTABLE 1 #else #define LUA_USE_JUMPTABLE 0 #endif #endif /* limit for table tag-method chains (to avoid infinite loops) */ #define MAXTAGLOOP 2000 /* ** 'l_intfitsf' checks whether a given integer is in the range that ** can be converted to a float without rounding. Used in comparisons. */ /* number of bits in the mantissa of a float */ #define NBM (l_floatatt(MANT_DIG)) /* ** Check whether some integers may not fit in a float, testing whether ** (maxinteger >> NBM) > 0. (That implies (1 << NBM) <= maxinteger.) ** (The shifts are done in parts, to avoid shifting by more than the size ** of an integer. In a worst case, NBM == 113 for long double and ** sizeof(long) == 32.) */ #if ((((LUA_MAXINTEGER >> (NBM / 4)) >> (NBM / 4)) >> (NBM / 4)) \ >> (NBM - (3 * (NBM / 4)))) > 0 /* limit for integers that fit in a float */ #define MAXINTFITSF ((lua_Unsigned)1 << NBM) /* check whether 'i' is in the interval [-MAXINTFITSF, MAXINTFITSF] */ #define l_intfitsf(i) ((MAXINTFITSF + l_castS2U(i)) <= (2 * MAXINTFITSF)) #else /* all integers fit in a float precisely */ #define l_intfitsf(i) 1 #endif /* ** Try to convert a value from string to a number value. ** If the value is not a string or is a string not representing ** a valid numeral (or if coercions from strings to numbers ** are disabled via macro 'cvt2num'), do not modify 'result' ** and return 0. */ static int l_strton (const TValue *obj, TValue *result) { lua_assert(obj != result); if (!cvt2num(obj)) /* is object not a string? */ return 0; else return (luaO_str2num(svalue(obj), result) == vslen(obj) + 1); } /* ** Try to convert a value to a float. The float case is already handled ** by the macro 'tonumber'. */ int luaV_tonumber_ (const TValue *obj, lua_Number *n) { TValue v; if (ttisinteger(obj)) { *n = cast_num(ivalue(obj)); return 1; } else if (l_strton(obj, &v)) { /* string coercible to number? */ *n = nvalue(&v); /* convert result of 'luaO_str2num' to a float */ return 1; } else return 0; /* conversion failed */ } /* ** try to convert a float to an integer, rounding according to 'mode'. */ int luaV_flttointeger (lua_Number n, lua_Integer *p, F2Imod mode) { lua_Number f = l_floor(n); if (n != f) { /* not an integral value? */ if (mode == F2Ieq) return 0; /* fails if mode demands integral value */ else if (mode == F2Iceil) /* needs ceil? */ f += 1; /* convert floor to ceil (remember: n != f) */ } return lua_numbertointeger(f, p); } /* ** try to convert a value to an integer, rounding according to 'mode', ** without string coercion. ** ("Fast track" handled by macro 'tointegerns'.) */ int luaV_tointegerns (const TValue *obj, lua_Integer *p, F2Imod mode) { if (ttisfloat(obj)) return luaV_flttointeger(fltvalue(obj), p, mode); else if (ttisinteger(obj)) { *p = ivalue(obj); return 1; } else return 0; } /* ** try to convert a value to an integer. */ int luaV_tointeger (const TValue *obj, lua_Integer *p, F2Imod mode) { TValue v; if (l_strton(obj, &v)) /* does 'obj' point to a numerical string? */ obj = &v; /* change it to point to its corresponding number */ return luaV_tointegerns(obj, p, mode); } /* ** Try to convert a 'for' limit to an integer, preserving the semantics ** of the loop. Return true if the loop must not run; otherwise, '*p' ** gets the integer limit. ** (The following explanation assumes a positive step; it is valid for ** negative steps mutatis mutandis.) ** If the limit is an integer or can be converted to an integer, ** rounding down, that is the limit. ** Otherwise, check whether the limit can be converted to a float. If ** the float is too large, clip it to LUA_MAXINTEGER. If the float ** is too negative, the loop should not run, because any initial ** integer value is greater than such limit; so, the function returns ** true to signal that. (For this latter case, no integer limit would be ** correct; even a limit of LUA_MININTEGER would run the loop once for ** an initial value equal to LUA_MININTEGER.) */ static int forlimit (lua_State *L, lua_Integer init, const TValue *lim, lua_Integer *p, lua_Integer step) { if (!luaV_tointeger(lim, p, (step < 0 ? F2Iceil : F2Ifloor))) { /* not coercible to in integer */ lua_Number flim; /* try to convert to float */ if (!tonumber(lim, &flim)) /* cannot convert to float? */ luaG_forerror(L, lim, "limit"); /* else 'flim' is a float out of integer bounds */ if (luai_numlt(0, flim)) { /* if it is positive, it is too large */ if (step < 0) return 1; /* initial value must be less than it */ *p = LUA_MAXINTEGER; /* truncate */ } else { /* it is less than min integer */ if (step > 0) return 1; /* initial value must be greater than it */ *p = LUA_MININTEGER; /* truncate */ } } return (step > 0 ? init > *p : init < *p); /* not to run? */ } /* ** Prepare a numerical for loop (opcode OP_FORPREP). ** Return true to skip the loop. Otherwise, ** after preparation, stack will be as follows: ** ra : internal index (safe copy of the control variable) ** ra + 1 : loop counter (integer loops) or limit (float loops) ** ra + 2 : step ** ra + 3 : control variable */ static int forprep (lua_State *L, StkId ra) { TValue *pinit = s2v(ra); TValue *plimit = s2v(ra + 1); TValue *pstep = s2v(ra + 2); if (ttisinteger(pinit) && ttisinteger(pstep)) { /* integer loop? */ lua_Integer init = ivalue(pinit); lua_Integer step = ivalue(pstep); lua_Integer limit; if (step == 0) luaG_runerror(L, "'for' step is zero"); setivalue(s2v(ra + 3), init); /* control variable */ if (forlimit(L, init, plimit, &limit, step)) return 1; /* skip the loop */ else { /* prepare loop counter */ lua_Unsigned count; if (step > 0) { /* ascending loop? */ count = l_castS2U(limit) - l_castS2U(init); if (step != 1) /* avoid division in the too common case */ count /= l_castS2U(step); } else { /* step < 0; descending loop */ count = l_castS2U(init) - l_castS2U(limit); /* 'step+1' avoids negating 'mininteger' */ count /= l_castS2U(-(step + 1)) + 1u; } /* store the counter in place of the limit (which won't be needed anymore) */ setivalue(plimit, l_castU2S(count)); } } else { /* try making all values floats */ lua_Number init; lua_Number limit; lua_Number step; if (l_unlikely(!tonumber(plimit, &limit))) luaG_forerror(L, plimit, "limit"); if (l_unlikely(!tonumber(pstep, &step))) luaG_forerror(L, pstep, "step"); if (l_unlikely(!tonumber(pinit, &init))) luaG_forerror(L, pinit, "initial value"); if (step == 0) luaG_runerror(L, "'for' step is zero"); if (luai_numlt(0, step) ? luai_numlt(limit, init) : luai_numlt(init, limit)) return 1; /* skip the loop */ else { /* make sure internal values are all floats */ setfltvalue(plimit, limit); setfltvalue(pstep, step); setfltvalue(s2v(ra), init); /* internal index */ setfltvalue(s2v(ra + 3), init); /* control variable */ } } return 0; } /* ** Execute a step of a float numerical for loop, returning ** true iff the loop must continue. (The integer case is ** written online with opcode OP_FORLOOP, for performance.) */ static int floatforloop (StkId ra) { lua_Number step = fltvalue(s2v(ra + 2)); lua_Number limit = fltvalue(s2v(ra + 1)); lua_Number idx = fltvalue(s2v(ra)); /* internal index */ idx = luai_numadd(L, idx, step); /* increment index */ if (luai_numlt(0, step) ? luai_numle(idx, limit) : luai_numle(limit, idx)) { chgfltvalue(s2v(ra), idx); /* update internal index */ setfltvalue(s2v(ra + 3), idx); /* and control variable */ return 1; /* jump back */ } else return 0; /* finish the loop */ } /* ** Finish the table access 'val = t[key]'. ** if 'slot' is NULL, 't' is not a table; otherwise, 'slot' points to ** t[k] entry (which must be empty). */ void luaV_finishget (lua_State *L, const TValue *t, TValue *key, StkId val, const TValue *slot) { int loop; /* counter to avoid infinite loops */ const TValue *tm; /* metamethod */ for (loop = 0; loop < MAXTAGLOOP; loop++) { if (slot == NULL) { /* 't' is not a table? */ lua_assert(!ttistable(t)); tm = luaT_gettmbyobj(L, t, TM_INDEX); if (l_unlikely(notm(tm))) luaG_typeerror(L, t, "index"); /* no metamethod */ /* else will try the metamethod */ } else { /* 't' is a table */ lua_assert(isempty(slot)); tm = fasttm(L, hvalue(t)->metatable, TM_INDEX); /* table's metamethod */ if (tm == NULL) { /* no metamethod? */ setnilvalue(s2v(val)); /* result is nil */ return; } /* else will try the metamethod */ } if (ttisfunction(tm)) { /* is metamethod a function? */ luaT_callTMres(L, tm, t, key, val); /* call it */ return; } t = tm; /* else try to access 'tm[key]' */ if (luaV_fastget(L, t, key, slot, luaH_get)) { /* fast track? */ setobj2s(L, val, slot); /* done */ return; } /* else repeat (tail call 'luaV_finishget') */ } luaG_runerror(L, "'__index' chain too long; possible loop"); } /* ** Finish a table assignment 't[key] = val'. ** If 'slot' is NULL, 't' is not a table. Otherwise, 'slot' points ** to the entry 't[key]', or to a value with an absent key if there ** is no such entry. (The value at 'slot' must be empty, otherwise ** 'luaV_fastget' would have done the job.) */ void luaV_finishset (lua_State *L, const TValue *t, TValue *key, TValue *val, const TValue *slot) { int loop; /* counter to avoid infinite loops */ for (loop = 0; loop < MAXTAGLOOP; loop++) { const TValue *tm; /* '__newindex' metamethod */ if (slot != NULL) { /* is 't' a table? */ Table *h = hvalue(t); /* save 't' table */ lua_assert(isempty(slot)); /* slot must be empty */ tm = fasttm(L, h->metatable, TM_NEWINDEX); /* get metamethod */ if (tm == NULL) { /* no metamethod? */ luaH_finishset(L, h, key, slot, val); /* set new value */ invalidateTMcache(h); luaC_barrierback(L, obj2gco(h), val); return; } /* else will try the metamethod */ } else { /* not a table; check metamethod */ tm = luaT_gettmbyobj(L, t, TM_NEWINDEX); if (l_unlikely(notm(tm))) luaG_typeerror(L, t, "index"); } /* try the metamethod */ if (ttisfunction(tm)) { luaT_callTM(L, tm, t, key, val); return; } t = tm; /* else repeat assignment over 'tm' */ if (luaV_fastget(L, t, key, slot, luaH_get)) { luaV_finishfastset(L, t, slot, val); return; /* done */ } /* else 'return luaV_finishset(L, t, key, val, slot)' (loop) */ } luaG_runerror(L, "'__newindex' chain too long; possible loop"); } /* ** Compare two strings 'ls' x 'rs', returning an integer less-equal- ** -greater than zero if 'ls' is less-equal-greater than 'rs'. ** The code is a little tricky because it allows '\0' in the strings ** and it uses 'strcoll' (to respect locales) for each segments ** of the strings. */ static int l_strcmp (const TString *ls, const TString *rs) { const char *l = getstr(ls); size_t ll = tsslen(ls); const char *r = getstr(rs); size_t lr = tsslen(rs); for (;;) { /* for each segment */ int temp = strcoll(l, r); if (temp != 0) /* not equal? */ return temp; /* done */ else { /* strings are equal up to a '\0' */ size_t len = strlen(l); /* index of first '\0' in both strings */ if (len == lr) /* 'rs' is finished? */ return (len == ll) ? 0 : 1; /* check 'ls' */ else if (len == ll) /* 'ls' is finished? */ return -1; /* 'ls' is less than 'rs' ('rs' is not finished) */ /* both strings longer than 'len'; go on comparing after the '\0' */ len++; l += len; ll -= len; r += len; lr -= len; } } } /* ** Check whether integer 'i' is less than float 'f'. If 'i' has an ** exact representation as a float ('l_intfitsf'), compare numbers as ** floats. Otherwise, use the equivalence 'i < f <=> i < ceil(f)'. ** If 'ceil(f)' is out of integer range, either 'f' is greater than ** all integers or less than all integers. ** (The test with 'l_intfitsf' is only for performance; the else ** case is correct for all values, but it is slow due to the conversion ** from float to int.) ** When 'f' is NaN, comparisons must result in false. */ l_sinline int LTintfloat (lua_Integer i, lua_Number f) { if (l_intfitsf(i)) return luai_numlt(cast_num(i), f); /* compare them as floats */ else { /* i < f <=> i < ceil(f) */ lua_Integer fi; if (luaV_flttointeger(f, &fi, F2Iceil)) /* fi = ceil(f) */ return i < fi; /* compare them as integers */ else /* 'f' is either greater or less than all integers */ return f > 0; /* greater? */ } } /* ** Check whether integer 'i' is less than or equal to float 'f'. ** See comments on previous function. */ l_sinline int LEintfloat (lua_Integer i, lua_Number f) { if (l_intfitsf(i)) return luai_numle(cast_num(i), f); /* compare them as floats */ else { /* i <= f <=> i <= floor(f) */ lua_Integer fi; if (luaV_flttointeger(f, &fi, F2Ifloor)) /* fi = floor(f) */ return i <= fi; /* compare them as integers */ else /* 'f' is either greater or less than all integers */ return f > 0; /* greater? */ } } /* ** Check whether float 'f' is less than integer 'i'. ** See comments on previous function. */ l_sinline int LTfloatint (lua_Number f, lua_Integer i) { if (l_intfitsf(i)) return luai_numlt(f, cast_num(i)); /* compare them as floats */ else { /* f < i <=> floor(f) < i */ lua_Integer fi; if (luaV_flttointeger(f, &fi, F2Ifloor)) /* fi = floor(f) */ return fi < i; /* compare them as integers */ else /* 'f' is either greater or less than all integers */ return f < 0; /* less? */ } } /* ** Check whether float 'f' is less than or equal to integer 'i'. ** See comments on previous function. */ l_sinline int LEfloatint (lua_Number f, lua_Integer i) { if (l_intfitsf(i)) return luai_numle(f, cast_num(i)); /* compare them as floats */ else { /* f <= i <=> ceil(f) <= i */ lua_Integer fi; if (luaV_flttointeger(f, &fi, F2Iceil)) /* fi = ceil(f) */ return fi <= i; /* compare them as integers */ else /* 'f' is either greater or less than all integers */ return f < 0; /* less? */ } } /* ** Return 'l < r', for numbers. */ l_sinline int LTnum (const TValue *l, const TValue *r) { lua_assert(ttisnumber(l) && ttisnumber(r)); if (ttisinteger(l)) { lua_Integer li = ivalue(l); if (ttisinteger(r)) return li < ivalue(r); /* both are integers */ else /* 'l' is int and 'r' is float */ return LTintfloat(li, fltvalue(r)); /* l < r ? */ } else { lua_Number lf = fltvalue(l); /* 'l' must be float */ if (ttisfloat(r)) return luai_numlt(lf, fltvalue(r)); /* both are float */ else /* 'l' is float and 'r' is int */ return LTfloatint(lf, ivalue(r)); } } /* ** Return 'l <= r', for numbers. */ l_sinline int LEnum (const TValue *l, const TValue *r) { lua_assert(ttisnumber(l) && ttisnumber(r)); if (ttisinteger(l)) { lua_Integer li = ivalue(l); if (ttisinteger(r)) return li <= ivalue(r); /* both are integers */ else /* 'l' is int and 'r' is float */ return LEintfloat(li, fltvalue(r)); /* l <= r ? */ } else { lua_Number lf = fltvalue(l); /* 'l' must be float */ if (ttisfloat(r)) return luai_numle(lf, fltvalue(r)); /* both are float */ else /* 'l' is float and 'r' is int */ return LEfloatint(lf, ivalue(r)); } } /* ** return 'l < r' for non-numbers. */ static int lessthanothers (lua_State *L, const TValue *l, const TValue *r) { lua_assert(!ttisnumber(l) || !ttisnumber(r)); if (ttisstring(l) && ttisstring(r)) /* both are strings? */ return l_strcmp(tsvalue(l), tsvalue(r)) < 0; else return luaT_callorderTM(L, l, r, TM_LT); } /* ** Main operation less than; return 'l < r'. */ int luaV_lessthan (lua_State *L, const TValue *l, const TValue *r) { if (ttisnumber(l) && ttisnumber(r)) /* both operands are numbers? */ return LTnum(l, r); else return lessthanothers(L, l, r); } /* ** return 'l <= r' for non-numbers. */ static int lessequalothers (lua_State *L, const TValue *l, const TValue *r) { lua_assert(!ttisnumber(l) || !ttisnumber(r)); if (ttisstring(l) && ttisstring(r)) /* both are strings? */ return l_strcmp(tsvalue(l), tsvalue(r)) <= 0; else return luaT_callorderTM(L, l, r, TM_LE); } /* ** Main operation less than or equal to; return 'l <= r'. */ int luaV_lessequal (lua_State *L, const TValue *l, const TValue *r) { if (ttisnumber(l) && ttisnumber(r)) /* both operands are numbers? */ return LEnum(l, r); else return lessequalothers(L, l, r); } /* ** Main operation for equality of Lua values; return 't1 == t2'. ** L == NULL means raw equality (no metamethods) */ int luaV_equalobj (lua_State *L, const TValue *t1, const TValue *t2) { const TValue *tm; if (ttypetag(t1) != ttypetag(t2)) { /* not the same variant? */ if (ttype(t1) != ttype(t2) || ttype(t1) != LUA_TNUMBER) return 0; /* only numbers can be equal with different variants */ else { /* two numbers with different variants */ /* One of them is an integer. If the other does not have an integer value, they cannot be equal; otherwise, compare their integer values. */ lua_Integer i1, i2; return (luaV_tointegerns(t1, &i1, F2Ieq) && luaV_tointegerns(t2, &i2, F2Ieq) && i1 == i2); } } /* values have same type and same variant */ switch (ttypetag(t1)) { case LUA_VNIL: case LUA_VFALSE: case LUA_VTRUE: return 1; case LUA_VNUMINT: return (ivalue(t1) == ivalue(t2)); case LUA_VNUMFLT: return luai_numeq(fltvalue(t1), fltvalue(t2)); case LUA_VLIGHTUSERDATA: return pvalue(t1) == pvalue(t2); case LUA_VLCF: return fvalue(t1) == fvalue(t2); case LUA_VSHRSTR: return eqshrstr(tsvalue(t1), tsvalue(t2)); case LUA_VLNGSTR: return luaS_eqlngstr(tsvalue(t1), tsvalue(t2)); case LUA_VUSERDATA: { if (uvalue(t1) == uvalue(t2)) return 1; else if (L == NULL) return 0; tm = fasttm(L, uvalue(t1)->metatable, TM_EQ); if (tm == NULL) tm = fasttm(L, uvalue(t2)->metatable, TM_EQ); break; /* will try TM */ } case LUA_VTABLE: { if (hvalue(t1) == hvalue(t2)) return 1; else if (L == NULL) return 0; tm = fasttm(L, hvalue(t1)->metatable, TM_EQ); if (tm == NULL) tm = fasttm(L, hvalue(t2)->metatable, TM_EQ); break; /* will try TM */ } default: return gcvalue(t1) == gcvalue(t2); } if (tm == NULL) /* no TM? */ return 0; /* objects are different */ else { luaT_callTMres(L, tm, t1, t2, L->top); /* call TM */ return !l_isfalse(s2v(L->top)); } } /* macro used by 'luaV_concat' to ensure that element at 'o' is a string */ #define tostring(L,o) \ (ttisstring(o) || (cvt2str(o) && (luaO_tostring(L, o), 1))) #define isemptystr(o) (ttisshrstring(o) && tsvalue(o)->shrlen == 0) /* copy strings in stack from top - n up to top - 1 to buffer */ static void copy2buff (StkId top, int n, char *buff) { size_t tl = 0; /* size already copied */ do { size_t l = vslen(s2v(top - n)); /* length of string being copied */ memcpy(buff + tl, svalue(s2v(top - n)), l * sizeof(char)); tl += l; } while (--n > 0); } /* ** Main operation for concatenation: concat 'total' values in the stack, ** from 'L->top - total' up to 'L->top - 1'. */ void luaV_concat (lua_State *L, int total) { if (total == 1) return; /* "all" values already concatenated */ do { StkId top = L->top; int n = 2; /* number of elements handled in this pass (at least 2) */ if (!(ttisstring(s2v(top - 2)) || cvt2str(s2v(top - 2))) || !tostring(L, s2v(top - 1))) luaT_tryconcatTM(L); else if (isemptystr(s2v(top - 1))) /* second operand is empty? */ cast_void(tostring(L, s2v(top - 2))); /* result is first operand */ else if (isemptystr(s2v(top - 2))) { /* first operand is empty string? */ setobjs2s(L, top - 2, top - 1); /* result is second op. */ } else { /* at least two non-empty string values; get as many as possible */ size_t tl = vslen(s2v(top - 1)); TString *ts; /* collect total length and number of strings */ for (n = 1; n < total && tostring(L, s2v(top - n - 1)); n++) { size_t l = vslen(s2v(top - n - 1)); if (l_unlikely(l >= (MAX_SIZE/sizeof(char)) - tl)) luaG_runerror(L, "string length overflow"); tl += l; } if (tl <= LUAI_MAXSHORTLEN) { /* is result a short string? */ char buff[LUAI_MAXSHORTLEN]; copy2buff(top, n, buff); /* copy strings to buffer */ ts = luaS_newlstr(L, buff, tl); } else { /* long string; copy strings directly to final result */ ts = luaS_createlngstrobj(L, tl); copy2buff(top, n, getstr(ts)); } setsvalue2s(L, top - n, ts); /* create result */ } total -= n-1; /* got 'n' strings to create 1 new */ L->top -= n-1; /* popped 'n' strings and pushed one */ } while (total > 1); /* repeat until only 1 result left */ } /* ** Main operation 'ra = #rb'. */ void luaV_objlen (lua_State *L, StkId ra, const TValue *rb) { const TValue *tm; switch (ttypetag(rb)) { case LUA_VTABLE: { Table *h = hvalue(rb); tm = fasttm(L, h->metatable, TM_LEN); if (tm) break; /* metamethod? break switch to call it */ setivalue(s2v(ra), luaH_getn(h)); /* else primitive len */ return; } case LUA_VSHRSTR: { setivalue(s2v(ra), tsvalue(rb)->shrlen); return; } case LUA_VLNGSTR: { setivalue(s2v(ra), tsvalue(rb)->u.lnglen); return; } default: { /* try metamethod */ tm = luaT_gettmbyobj(L, rb, TM_LEN); if (l_unlikely(notm(tm))) /* no metamethod? */ luaG_typeerror(L, rb, "get length of"); break; } } luaT_callTMres(L, tm, rb, rb, ra); } /* ** Integer division; return 'm // n', that is, floor(m/n). ** C division truncates its result (rounds towards zero). ** 'floor(q) == trunc(q)' when 'q >= 0' or when 'q' is integer, ** otherwise 'floor(q) == trunc(q) - 1'. */ lua_Integer luaV_idiv (lua_State *L, lua_Integer m, lua_Integer n) { if (l_unlikely(l_castS2U(n) + 1u <= 1u)) { /* special cases: -1 or 0 */ if (n == 0) luaG_runerror(L, "attempt to divide by zero"); return intop(-, 0, m); /* n==-1; avoid overflow with 0x80000...//-1 */ } else { lua_Integer q = m / n; /* perform C division */ if ((m ^ n) < 0 && m % n != 0) /* 'm/n' would be negative non-integer? */ q -= 1; /* correct result for different rounding */ return q; } } /* ** Integer modulus; return 'm % n'. (Assume that C '%' with ** negative operands follows C99 behavior. See previous comment ** about luaV_idiv.) */ lua_Integer luaV_mod (lua_State *L, lua_Integer m, lua_Integer n) { if (l_unlikely(l_castS2U(n) + 1u <= 1u)) { /* special cases: -1 or 0 */ if (n == 0) luaG_runerror(L, "attempt to perform 'n%%0'"); return 0; /* m % -1 == 0; avoid overflow with 0x80000...%-1 */ } else { lua_Integer r = m % n; if (r != 0 && (r ^ n) < 0) /* 'm/n' would be non-integer negative? */ r += n; /* correct result for different rounding */ return r; } } /* ** Float modulus */ lua_Number luaV_modf (lua_State *L, lua_Number m, lua_Number n) { lua_Number r; luai_nummod(L, m, n, r); return r; } /* number of bits in an integer */ #define NBITS cast_int(sizeof(lua_Integer) * CHAR_BIT) /* ** Shift left operation. (Shift right just negates 'y'.) */ #define luaV_shiftr(x,y) luaV_shiftl(x,intop(-, 0, y)) lua_Integer luaV_shiftl (lua_Integer x, lua_Integer y) { if (y < 0) { /* shift right? */ if (y <= -NBITS) return 0; else return intop(>>, x, -y); } else { /* shift left */ if (y >= NBITS) return 0; else return intop(<<, x, y); } } /* ** create a new Lua closure, push it in the stack, and initialize ** its upvalues. */ static void pushclosure (lua_State *L, Proto *p, UpVal **encup, StkId base, StkId ra) { int nup = p->sizeupvalues; Upvaldesc *uv = p->upvalues; int i; LClosure *ncl = luaF_newLclosure(L, nup); ncl->p = p; setclLvalue2s(L, ra, ncl); /* anchor new closure in stack */ for (i = 0; i < nup; i++) { /* fill in its upvalues */ if (uv[i].instack) /* upvalue refers to local variable? */ ncl->upvals[i] = luaF_findupval(L, base + uv[i].idx); else /* get upvalue from enclosing function */ ncl->upvals[i] = encup[uv[i].idx]; luaC_objbarrier(L, ncl, ncl->upvals[i]); } } /* ** finish execution of an opcode interrupted by a yield */ void luaV_finishOp (lua_State *L) { CallInfo *ci = L->ci; StkId base = ci->func + 1; Instruction inst = *(ci->u.l.savedpc - 1); /* interrupted instruction */ OpCode op = GET_OPCODE(inst); switch (op) { /* finish its execution */ case OP_MMBIN: case OP_MMBINI: case OP_MMBINK: { setobjs2s(L, base + GETARG_A(*(ci->u.l.savedpc - 2)), --L->top); break; } case OP_UNM: case OP_BNOT: case OP_LEN: case OP_GETTABUP: case OP_GETTABLE: case OP_GETI: case OP_GETFIELD: case OP_SELF: { setobjs2s(L, base + GETARG_A(inst), --L->top); break; } case OP_LT: case OP_LE: case OP_LTI: case OP_LEI: case OP_GTI: case OP_GEI: case OP_EQ: { /* note that 'OP_EQI'/'OP_EQK' cannot yield */ int res = !l_isfalse(s2v(L->top - 1)); L->top--; #if defined(LUA_COMPAT_LT_LE) if (ci->callstatus & CIST_LEQ) { /* "<=" using "<" instead? */ ci->callstatus ^= CIST_LEQ; /* clear mark */ res = !res; /* negate result */ } #endif lua_assert(GET_OPCODE(*ci->u.l.savedpc) == OP_JMP); if (res != GETARG_k(inst)) /* condition failed? */ ci->u.l.savedpc++; /* skip jump instruction */ break; } case OP_CONCAT: { StkId top = L->top - 1; /* top when 'luaT_tryconcatTM' was called */ int a = GETARG_A(inst); /* first element to concatenate */ int total = cast_int(top - 1 - (base + a)); /* yet to concatenate */ setobjs2s(L, top - 2, top); /* put TM result in proper position */ L->top = top - 1; /* top is one after last element (at top-2) */ luaV_concat(L, total); /* concat them (may yield again) */ break; } case OP_CLOSE: { /* yielded closing variables */ ci->u.l.savedpc--; /* repeat instruction to close other vars. */ break; } case OP_RETURN: { /* yielded closing variables */ StkId ra = base + GETARG_A(inst); /* adjust top to signal correct number of returns, in case the return is "up to top" ('isIT') */ L->top = ra + ci->u2.nres; /* repeat instruction to close other vars. and complete the return */ ci->u.l.savedpc--; break; } default: { /* only these other opcodes can yield */ lua_assert(op == OP_TFORCALL || op == OP_CALL || op == OP_TAILCALL || op == OP_SETTABUP || op == OP_SETTABLE || op == OP_SETI || op == OP_SETFIELD); break; } } } /* ** {================================================================== ** Macros for arithmetic/bitwise/comparison opcodes in 'luaV_execute' ** =================================================================== */ #define l_addi(L,a,b) intop(+, a, b) #define l_subi(L,a,b) intop(-, a, b) #define l_muli(L,a,b) intop(*, a, b) #define l_band(a,b) intop(&, a, b) #define l_bor(a,b) intop(|, a, b) #define l_bxor(a,b) intop(^, a, b) #define l_lti(a,b) (a < b) #define l_lei(a,b) (a <= b) #define l_gti(a,b) (a > b) #define l_gei(a,b) (a >= b) /* ** Arithmetic operations with immediate operands. 'iop' is the integer ** operation, 'fop' is the float operation. */ #define op_arithI(L,iop,fop) { \ TValue *v1 = vRB(i); \ int imm = GETARG_sC(i); \ if (ttisinteger(v1)) { \ lua_Integer iv1 = ivalue(v1); \ pc++; setivalue(s2v(ra), iop(L, iv1, imm)); \ } \ else if (ttisfloat(v1)) { \ lua_Number nb = fltvalue(v1); \ lua_Number fimm = cast_num(imm); \ pc++; setfltvalue(s2v(ra), fop(L, nb, fimm)); \ }} /* ** Auxiliary function for arithmetic operations over floats and others ** with two register operands. */ #define op_arithf_aux(L,v1,v2,fop) { \ lua_Number n1; lua_Number n2; \ if (tonumberns(v1, n1) && tonumberns(v2, n2)) { \ pc++; setfltvalue(s2v(ra), fop(L, n1, n2)); \ }} /* ** Arithmetic operations over floats and others with register operands. */ #define op_arithf(L,fop) { \ TValue *v1 = vRB(i); \ TValue *v2 = vRC(i); \ op_arithf_aux(L, v1, v2, fop); } /* ** Arithmetic operations with K operands for floats. */ #define op_arithfK(L,fop) { \ TValue *v1 = vRB(i); \ TValue *v2 = KC(i); lua_assert(ttisnumber(v2)); \ op_arithf_aux(L, v1, v2, fop); } /* ** Arithmetic operations over integers and floats. */ #define op_arith_aux(L,v1,v2,iop,fop) { \ if (ttisinteger(v1) && ttisinteger(v2)) { \ lua_Integer i1 = ivalue(v1); lua_Integer i2 = ivalue(v2); \ pc++; setivalue(s2v(ra), iop(L, i1, i2)); \ } \ else op_arithf_aux(L, v1, v2, fop); } /* ** Arithmetic operations with register operands. */ #define op_arith(L,iop,fop) { \ TValue *v1 = vRB(i); \ TValue *v2 = vRC(i); \ op_arith_aux(L, v1, v2, iop, fop); } /* ** Arithmetic operations with K operands. */ #define op_arithK(L,iop,fop) { \ TValue *v1 = vRB(i); \ TValue *v2 = KC(i); lua_assert(ttisnumber(v2)); \ op_arith_aux(L, v1, v2, iop, fop); } /* ** Bitwise operations with constant operand. */ #define op_bitwiseK(L,op) { \ TValue *v1 = vRB(i); \ TValue *v2 = KC(i); \ lua_Integer i1; \ lua_Integer i2 = ivalue(v2); \ if (tointegerns(v1, &i1)) { \ pc++; setivalue(s2v(ra), op(i1, i2)); \ }} /* ** Bitwise operations with register operands. */ #define op_bitwise(L,op) { \ TValue *v1 = vRB(i); \ TValue *v2 = vRC(i); \ lua_Integer i1; lua_Integer i2; \ if (tointegerns(v1, &i1) && tointegerns(v2, &i2)) { \ pc++; setivalue(s2v(ra), op(i1, i2)); \ }} /* ** Order operations with register operands. 'opn' actually works ** for all numbers, but the fast track improves performance for ** integers. */ #define op_order(L,opi,opn,other) { \ int cond; \ TValue *rb = vRB(i); \ if (ttisinteger(s2v(ra)) && ttisinteger(rb)) { \ lua_Integer ia = ivalue(s2v(ra)); \ lua_Integer ib = ivalue(rb); \ cond = opi(ia, ib); \ } \ else if (ttisnumber(s2v(ra)) && ttisnumber(rb)) \ cond = opn(s2v(ra), rb); \ else \ Protect(cond = other(L, s2v(ra), rb)); \ docondjump(); } /* ** Order operations with immediate operand. (Immediate operand is ** always small enough to have an exact representation as a float.) */ #define op_orderI(L,opi,opf,inv,tm) { \ int cond; \ int im = GETARG_sB(i); \ if (ttisinteger(s2v(ra))) \ cond = opi(ivalue(s2v(ra)), im); \ else if (ttisfloat(s2v(ra))) { \ lua_Number fa = fltvalue(s2v(ra)); \ lua_Number fim = cast_num(im); \ cond = opf(fa, fim); \ } \ else { \ int isf = GETARG_C(i); \ Protect(cond = luaT_callorderiTM(L, s2v(ra), im, inv, isf, tm)); \ } \ docondjump(); } /* }================================================================== */ /* ** {================================================================== ** Function 'luaV_execute': main interpreter loop ** =================================================================== */ /* ** some macros for common tasks in 'luaV_execute' */ #define RA(i) (base+GETARG_A(i)) #define RB(i) (base+GETARG_B(i)) #define vRB(i) s2v(RB(i)) #define KB(i) (k+GETARG_B(i)) #define RC(i) (base+GETARG_C(i)) #define vRC(i) s2v(RC(i)) #define KC(i) (k+GETARG_C(i)) #define RKC(i) ((TESTARG_k(i)) ? k + GETARG_C(i) : s2v(base + GETARG_C(i))) #define updatetrap(ci) (trap = ci->u.l.trap) #define updatebase(ci) (base = ci->func + 1) #define updatestack(ci) \ { if (l_unlikely(trap)) { updatebase(ci); ra = RA(i); } } /* ** Execute a jump instruction. The 'updatetrap' allows signals to stop ** tight loops. (Without it, the local copy of 'trap' could never change.) */ #define dojump(ci,i,e) { pc += GETARG_sJ(i) + e; updatetrap(ci); } /* for test instructions, execute the jump instruction that follows it */ #define donextjump(ci) { Instruction ni = *pc; dojump(ci, ni, 1); } /* ** do a conditional jump: skip next instruction if 'cond' is not what ** was expected (parameter 'k'), else do next instruction, which must ** be a jump. */ #define docondjump() if (cond != GETARG_k(i)) pc++; else donextjump(ci); /* ** Correct global 'pc'. */ #define savepc(L) (ci->u.l.savedpc = pc) /* ** Whenever code can raise errors, the global 'pc' and the global ** 'top' must be correct to report occasional errors. */ #define savestate(L,ci) (savepc(L), L->top = ci->top) /* ** Protect code that, in general, can raise errors, reallocate the ** stack, and change the hooks. */ #define Protect(exp) (savestate(L,ci), (exp), updatetrap(ci)) /* special version that does not change the top */ #define ProtectNT(exp) (savepc(L), (exp), updatetrap(ci)) /* ** Protect code that can only raise errors. (That is, it cannot change ** the stack or hooks.) */ #define halfProtect(exp) (savestate(L,ci), (exp)) /* 'c' is the limit of live values in the stack */ #define checkGC(L,c) \ { luaC_condGC(L, (savepc(L), L->top = (c)), \ updatetrap(ci)); \ luai_threadyield(L); } /* fetch an instruction and prepare its execution */ #define vmfetch() { \ if (l_unlikely(trap)) { /* stack reallocation or hooks? */ \ trap = luaG_traceexec(L, pc); /* handle hooks */ \ updatebase(ci); /* correct stack */ \ } \ i = *(pc++); \ ra = RA(i); /* WARNING: any stack reallocation invalidates 'ra' */ \ } #define vmdispatch(o) switch(o) #define vmcase(l) case l: #define vmbreak break void luaV_execute (lua_State *L, CallInfo *ci) { LClosure *cl; TValue *k; StkId base; const Instruction *pc; int trap; #if LUA_USE_JUMPTABLE /*#include "ljumptab.h"*/ /* ** $Id: ljumptab.h $ ** Jump Table for the Lua interpreter ** See Copyright Notice in lua.h */ #undef vmdispatch #undef vmcase #undef vmbreak #define vmdispatch(x) goto *disptab[x]; #define vmcase(l) L_##l: #define vmbreak vmfetch(); vmdispatch(GET_OPCODE(i)); static const void *const disptab[NUM_OPCODES] = { #if 0 ** you can update the following list with this command: ** ** sed -n '/^OP_/\!d; s/OP_/\&\&L_OP_/ ; s/,.*/,/ ; s/\/.*// ; p' lopcodes.h ** #endif &&L_OP_MOVE, &&L_OP_LOADI, &&L_OP_LOADF, &&L_OP_LOADK, &&L_OP_LOADKX, &&L_OP_LOADFALSE, &&L_OP_LFALSESKIP, &&L_OP_LOADTRUE, &&L_OP_LOADNIL, &&L_OP_GETUPVAL, &&L_OP_SETUPVAL, &&L_OP_GETTABUP, &&L_OP_GETTABLE, &&L_OP_GETI, &&L_OP_GETFIELD, &&L_OP_SETTABUP, &&L_OP_SETTABLE, &&L_OP_SETI, &&L_OP_SETFIELD, &&L_OP_NEWTABLE, &&L_OP_SELF, &&L_OP_ADDI, &&L_OP_ADDK, &&L_OP_SUBK, &&L_OP_MULK, &&L_OP_MODK, &&L_OP_POWK, &&L_OP_DIVK, &&L_OP_IDIVK, &&L_OP_BANDK, &&L_OP_BORK, &&L_OP_BXORK, &&L_OP_SHRI, &&L_OP_SHLI, &&L_OP_ADD, &&L_OP_SUB, &&L_OP_MUL, &&L_OP_MOD, &&L_OP_POW, &&L_OP_DIV, &&L_OP_IDIV, &&L_OP_BAND, &&L_OP_BOR, &&L_OP_BXOR, &&L_OP_SHL, &&L_OP_SHR, &&L_OP_MMBIN, &&L_OP_MMBINI, &&L_OP_MMBINK, &&L_OP_UNM, &&L_OP_BNOT, &&L_OP_NOT, &&L_OP_LEN, &&L_OP_CONCAT, &&L_OP_CLOSE, &&L_OP_TBC, &&L_OP_JMP, &&L_OP_EQ, &&L_OP_LT, &&L_OP_LE, &&L_OP_EQK, &&L_OP_EQI, &&L_OP_LTI, &&L_OP_LEI, &&L_OP_GTI, &&L_OP_GEI, &&L_OP_TEST, &&L_OP_TESTSET, &&L_OP_CALL, &&L_OP_TAILCALL, &&L_OP_RETURN, &&L_OP_RETURN0, &&L_OP_RETURN1, &&L_OP_FORLOOP, &&L_OP_FORPREP, &&L_OP_TFORPREP, &&L_OP_TFORCALL, &&L_OP_TFORLOOP, &&L_OP_SETLIST, &&L_OP_CLOSURE, &&L_OP_VARARG, &&L_OP_VARARGPREP, &&L_OP_EXTRAARG }; #endif startfunc: trap = L->hookmask; returning: /* trap already set */ cl = clLvalue(s2v(ci->func)); k = cl->p->k; pc = ci->u.l.savedpc; if (l_unlikely(trap)) { if (pc == cl->p->code) { /* first instruction (not resuming)? */ if (cl->p->is_vararg) trap = 0; /* hooks will start after VARARGPREP instruction */ else /* check 'call' hook */ luaD_hookcall(L, ci); } ci->u.l.trap = 1; /* assume trap is on, for now */ } base = ci->func + 1; /* main loop of interpreter */ for (;;) { Instruction i; /* instruction being executed */ StkId ra; /* instruction's A register */ vmfetch(); #if 0 /* low-level line tracing for debugging Lua */ printf("line: %d\n", luaG_getfuncline(cl->p, pcRel(pc, cl->p))); #endif lua_assert(base == ci->func + 1); lua_assert(base <= L->top && L->top < L->stack_last); /* invalidate top for instructions not expecting it */ lua_assert(isIT(i) || (cast_void(L->top = base), 1)); vmdispatch (GET_OPCODE(i)) { vmcase(OP_MOVE) { setobjs2s(L, ra, RB(i)); vmbreak; } vmcase(OP_LOADI) { lua_Integer b = GETARG_sBx(i); setivalue(s2v(ra), b); vmbreak; } vmcase(OP_LOADF) { int b = GETARG_sBx(i); setfltvalue(s2v(ra), cast_num(b)); vmbreak; } vmcase(OP_LOADK) { TValue *rb = k + GETARG_Bx(i); setobj2s(L, ra, rb); vmbreak; } vmcase(OP_LOADKX) { TValue *rb; rb = k + GETARG_Ax(*pc); pc++; setobj2s(L, ra, rb); vmbreak; } vmcase(OP_LOADFALSE) { setbfvalue(s2v(ra)); vmbreak; } vmcase(OP_LFALSESKIP) { setbfvalue(s2v(ra)); pc++; /* skip next instruction */ vmbreak; } vmcase(OP_LOADTRUE) { setbtvalue(s2v(ra)); vmbreak; } vmcase(OP_LOADNIL) { int b = GETARG_B(i); do { setnilvalue(s2v(ra++)); } while (b--); vmbreak; } vmcase(OP_GETUPVAL) { int b = GETARG_B(i); setobj2s(L, ra, cl->upvals[b]->v); vmbreak; } vmcase(OP_SETUPVAL) { UpVal *uv = cl->upvals[GETARG_B(i)]; setobj(L, uv->v, s2v(ra)); luaC_barrier(L, uv, s2v(ra)); vmbreak; } vmcase(OP_GETTABUP) { const TValue *slot; TValue *upval = cl->upvals[GETARG_B(i)]->v; TValue *rc = KC(i); TString *key = tsvalue(rc); /* key must be a string */ if (luaV_fastget(L, upval, key, slot, luaH_getshortstr)) { setobj2s(L, ra, slot); } else Protect(luaV_finishget(L, upval, rc, ra, slot)); vmbreak; } vmcase(OP_GETTABLE) { const TValue *slot; TValue *rb = vRB(i); TValue *rc = vRC(i); lua_Unsigned n; if (ttisinteger(rc) /* fast track for integers? */ ? (cast_void(n = ivalue(rc)), luaV_fastgeti(L, rb, n, slot)) : luaV_fastget(L, rb, rc, slot, luaH_get)) { setobj2s(L, ra, slot); } else Protect(luaV_finishget(L, rb, rc, ra, slot)); vmbreak; } vmcase(OP_GETI) { const TValue *slot; TValue *rb = vRB(i); int c = GETARG_C(i); if (luaV_fastgeti(L, rb, c, slot)) { setobj2s(L, ra, slot); } else { TValue key; setivalue(&key, c); Protect(luaV_finishget(L, rb, &key, ra, slot)); } vmbreak; } vmcase(OP_GETFIELD) { const TValue *slot; TValue *rb = vRB(i); TValue *rc = KC(i); TString *key = tsvalue(rc); /* key must be a string */ if (luaV_fastget(L, rb, key, slot, luaH_getshortstr)) { setobj2s(L, ra, slot); } else Protect(luaV_finishget(L, rb, rc, ra, slot)); vmbreak; } vmcase(OP_SETTABUP) { const TValue *slot; TValue *upval = cl->upvals[GETARG_A(i)]->v; TValue *rb = KB(i); TValue *rc = RKC(i); TString *key = tsvalue(rb); /* key must be a string */ if (luaV_fastget(L, upval, key, slot, luaH_getshortstr)) { luaV_finishfastset(L, upval, slot, rc); } else Protect(luaV_finishset(L, upval, rb, rc, slot)); vmbreak; } vmcase(OP_SETTABLE) { const TValue *slot; TValue *rb = vRB(i); /* key (table is in 'ra') */ TValue *rc = RKC(i); /* value */ lua_Unsigned n; if (ttisinteger(rb) /* fast track for integers? */ ? (cast_void(n = ivalue(rb)), luaV_fastgeti(L, s2v(ra), n, slot)) : luaV_fastget(L, s2v(ra), rb, slot, luaH_get)) { luaV_finishfastset(L, s2v(ra), slot, rc); } else Protect(luaV_finishset(L, s2v(ra), rb, rc, slot)); vmbreak; } vmcase(OP_SETI) { const TValue *slot; int c = GETARG_B(i); TValue *rc = RKC(i); if (luaV_fastgeti(L, s2v(ra), c, slot)) { luaV_finishfastset(L, s2v(ra), slot, rc); } else { TValue key; setivalue(&key, c); Protect(luaV_finishset(L, s2v(ra), &key, rc, slot)); } vmbreak; } vmcase(OP_SETFIELD) { const TValue *slot; TValue *rb = KB(i); TValue *rc = RKC(i); TString *key = tsvalue(rb); /* key must be a string */ if (luaV_fastget(L, s2v(ra), key, slot, luaH_getshortstr)) { luaV_finishfastset(L, s2v(ra), slot, rc); } else Protect(luaV_finishset(L, s2v(ra), rb, rc, slot)); vmbreak; } vmcase(OP_NEWTABLE) { int b = GETARG_B(i); /* log2(hash size) + 1 */ int c = GETARG_C(i); /* array size */ Table *t; if (b > 0) b = 1 << (b - 1); /* size is 2^(b - 1) */ lua_assert((!TESTARG_k(i)) == (GETARG_Ax(*pc) == 0)); if (TESTARG_k(i)) /* non-zero extra argument? */ c += GETARG_Ax(*pc) * (MAXARG_C + 1); /* add it to size */ pc++; /* skip extra argument */ L->top = ra + 1; /* correct top in case of emergency GC */ t = luaH_new(L); /* memory allocation */ sethvalue2s(L, ra, t); if (b != 0 || c != 0) luaH_resize(L, t, c, b); /* idem */ checkGC(L, ra + 1); vmbreak; } vmcase(OP_SELF) { const TValue *slot; TValue *rb = vRB(i); TValue *rc = RKC(i); TString *key = tsvalue(rc); /* key must be a string */ setobj2s(L, ra + 1, rb); if (luaV_fastget(L, rb, key, slot, luaH_getstr)) { setobj2s(L, ra, slot); } else Protect(luaV_finishget(L, rb, rc, ra, slot)); vmbreak; } vmcase(OP_ADDI) { op_arithI(L, l_addi, luai_numadd); vmbreak; } vmcase(OP_ADDK) { op_arithK(L, l_addi, luai_numadd); vmbreak; } vmcase(OP_SUBK) { op_arithK(L, l_subi, luai_numsub); vmbreak; } vmcase(OP_MULK) { op_arithK(L, l_muli, luai_nummul); vmbreak; } vmcase(OP_MODK) { op_arithK(L, luaV_mod, luaV_modf); vmbreak; } vmcase(OP_POWK) { op_arithfK(L, luai_numpow); vmbreak; } vmcase(OP_DIVK) { op_arithfK(L, luai_numdiv); vmbreak; } vmcase(OP_IDIVK) { op_arithK(L, luaV_idiv, luai_numidiv); vmbreak; } vmcase(OP_BANDK) { op_bitwiseK(L, l_band); vmbreak; } vmcase(OP_BORK) { op_bitwiseK(L, l_bor); vmbreak; } vmcase(OP_BXORK) { op_bitwiseK(L, l_bxor); vmbreak; } vmcase(OP_SHRI) { TValue *rb = vRB(i); int ic = GETARG_sC(i); lua_Integer ib; if (tointegerns(rb, &ib)) { pc++; setivalue(s2v(ra), luaV_shiftl(ib, -ic)); } vmbreak; } vmcase(OP_SHLI) { TValue *rb = vRB(i); int ic = GETARG_sC(i); lua_Integer ib; if (tointegerns(rb, &ib)) { pc++; setivalue(s2v(ra), luaV_shiftl(ic, ib)); } vmbreak; } vmcase(OP_ADD) { op_arith(L, l_addi, luai_numadd); vmbreak; } vmcase(OP_SUB) { op_arith(L, l_subi, luai_numsub); vmbreak; } vmcase(OP_MUL) { op_arith(L, l_muli, luai_nummul); vmbreak; } vmcase(OP_MOD) { op_arith(L, luaV_mod, luaV_modf); vmbreak; } vmcase(OP_POW) { op_arithf(L, luai_numpow); vmbreak; } vmcase(OP_DIV) { /* float division (always with floats) */ op_arithf(L, luai_numdiv); vmbreak; } vmcase(OP_IDIV) { /* floor division */ op_arith(L, luaV_idiv, luai_numidiv); vmbreak; } vmcase(OP_BAND) { op_bitwise(L, l_band); vmbreak; } vmcase(OP_BOR) { op_bitwise(L, l_bor); vmbreak; } vmcase(OP_BXOR) { op_bitwise(L, l_bxor); vmbreak; } vmcase(OP_SHR) { op_bitwise(L, luaV_shiftr); vmbreak; } vmcase(OP_SHL) { op_bitwise(L, luaV_shiftl); vmbreak; } vmcase(OP_MMBIN) { Instruction pi = *(pc - 2); /* original arith. expression */ TValue *rb = vRB(i); TMS tm = (TMS)GETARG_C(i); StkId result = RA(pi); lua_assert(OP_ADD <= GET_OPCODE(pi) && GET_OPCODE(pi) <= OP_SHR); Protect(luaT_trybinTM(L, s2v(ra), rb, result, tm)); vmbreak; } vmcase(OP_MMBINI) { Instruction pi = *(pc - 2); /* original arith. expression */ int imm = GETARG_sB(i); TMS tm = (TMS)GETARG_C(i); int flip = GETARG_k(i); StkId result = RA(pi); Protect(luaT_trybiniTM(L, s2v(ra), imm, flip, result, tm)); vmbreak; } vmcase(OP_MMBINK) { Instruction pi = *(pc - 2); /* original arith. expression */ TValue *imm = KB(i); TMS tm = (TMS)GETARG_C(i); int flip = GETARG_k(i); StkId result = RA(pi); Protect(luaT_trybinassocTM(L, s2v(ra), imm, flip, result, tm)); vmbreak; } vmcase(OP_UNM) { TValue *rb = vRB(i); lua_Number nb; if (ttisinteger(rb)) { lua_Integer ib = ivalue(rb); setivalue(s2v(ra), intop(-, 0, ib)); } else if (tonumberns(rb, nb)) { setfltvalue(s2v(ra), luai_numunm(L, nb)); } else Protect(luaT_trybinTM(L, rb, rb, ra, TM_UNM)); vmbreak; } vmcase(OP_BNOT) { TValue *rb = vRB(i); lua_Integer ib; if (tointegerns(rb, &ib)) { setivalue(s2v(ra), intop(^, ~l_castS2U(0), ib)); } else Protect(luaT_trybinTM(L, rb, rb, ra, TM_BNOT)); vmbreak; } vmcase(OP_NOT) { TValue *rb = vRB(i); if (l_isfalse(rb)) setbtvalue(s2v(ra)); else setbfvalue(s2v(ra)); vmbreak; } vmcase(OP_LEN) { Protect(luaV_objlen(L, ra, vRB(i))); vmbreak; } vmcase(OP_CONCAT) { int n = GETARG_B(i); /* number of elements to concatenate */ L->top = ra + n; /* mark the end of concat operands */ ProtectNT(luaV_concat(L, n)); checkGC(L, L->top); /* 'luaV_concat' ensures correct top */ vmbreak; } vmcase(OP_CLOSE) { Protect(luaF_close(L, ra, LUA_OK, 1)); vmbreak; } vmcase(OP_TBC) { /* create new to-be-closed upvalue */ halfProtect(luaF_newtbcupval(L, ra)); vmbreak; } vmcase(OP_JMP) { dojump(ci, i, 0); vmbreak; } vmcase(OP_EQ) { int cond; TValue *rb = vRB(i); Protect(cond = luaV_equalobj(L, s2v(ra), rb)); docondjump(); vmbreak; } vmcase(OP_LT) { op_order(L, l_lti, LTnum, lessthanothers); vmbreak; } vmcase(OP_LE) { op_order(L, l_lei, LEnum, lessequalothers); vmbreak; } vmcase(OP_EQK) { TValue *rb = KB(i); /* basic types do not use '__eq'; we can use raw equality */ int cond = luaV_rawequalobj(s2v(ra), rb); docondjump(); vmbreak; } vmcase(OP_EQI) { int cond; int im = GETARG_sB(i); if (ttisinteger(s2v(ra))) cond = (ivalue(s2v(ra)) == im); else if (ttisfloat(s2v(ra))) cond = luai_numeq(fltvalue(s2v(ra)), cast_num(im)); else cond = 0; /* other types cannot be equal to a number */ docondjump(); vmbreak; } vmcase(OP_LTI) { op_orderI(L, l_lti, luai_numlt, 0, TM_LT); vmbreak; } vmcase(OP_LEI) { op_orderI(L, l_lei, luai_numle, 0, TM_LE); vmbreak; } vmcase(OP_GTI) { op_orderI(L, l_gti, luai_numgt, 1, TM_LT); vmbreak; } vmcase(OP_GEI) { op_orderI(L, l_gei, luai_numge, 1, TM_LE); vmbreak; } vmcase(OP_TEST) { int cond = !l_isfalse(s2v(ra)); docondjump(); vmbreak; } vmcase(OP_TESTSET) { TValue *rb = vRB(i); if (l_isfalse(rb) == GETARG_k(i)) pc++; else { setobj2s(L, ra, rb); donextjump(ci); } vmbreak; } vmcase(OP_CALL) { CallInfo *newci; int b = GETARG_B(i); int nresults = GETARG_C(i) - 1; if (b != 0) /* fixed number of arguments? */ L->top = ra + b; /* top signals number of arguments */ /* else previous instruction set top */ savepc(L); /* in case of errors */ if ((newci = luaD_precall(L, ra, nresults)) == NULL) updatetrap(ci); /* C call; nothing else to be done */ else { /* Lua call: run function in this same C frame */ ci = newci; goto startfunc; } vmbreak; } vmcase(OP_TAILCALL) { int b = GETARG_B(i); /* number of arguments + 1 (function) */ int n; /* number of results when calling a C function */ int nparams1 = GETARG_C(i); /* delta is virtual 'func' - real 'func' (vararg functions) */ int delta = (nparams1) ? ci->u.l.nextraargs + nparams1 : 0; if (b != 0) L->top = ra + b; else /* previous instruction set top */ b = cast_int(L->top - ra); savepc(ci); /* several calls here can raise errors */ if (TESTARG_k(i)) { luaF_closeupval(L, base); /* close upvalues from current call */ lua_assert(L->tbclist < base); /* no pending tbc variables */ lua_assert(base == ci->func + 1); } if ((n = luaD_pretailcall(L, ci, ra, b, delta)) < 0) /* Lua function? */ goto startfunc; /* execute the callee */ else { /* C function? */ ci->func -= delta; /* restore 'func' (if vararg) */ luaD_poscall(L, ci, n); /* finish caller */ updatetrap(ci); /* 'luaD_poscall' can change hooks */ goto ret; /* caller returns after the tail call */ } } vmcase(OP_RETURN) { int n = GETARG_B(i) - 1; /* number of results */ int nparams1 = GETARG_C(i); if (n < 0) /* not fixed? */ n = cast_int(L->top - ra); /* get what is available */ savepc(ci); if (TESTARG_k(i)) { /* may there be open upvalues? */ ci->u2.nres = n; /* save number of returns */ if (L->top < ci->top) L->top = ci->top; luaF_close(L, base, CLOSEKTOP, 1); updatetrap(ci); updatestack(ci); } if (nparams1) /* vararg function? */ ci->func -= ci->u.l.nextraargs + nparams1; L->top = ra + n; /* set call for 'luaD_poscall' */ luaD_poscall(L, ci, n); updatetrap(ci); /* 'luaD_poscall' can change hooks */ goto ret; } vmcase(OP_RETURN0) { if (l_unlikely(L->hookmask)) { L->top = ra; savepc(ci); luaD_poscall(L, ci, 0); /* no hurry... */ trap = 1; } else { /* do the 'poscall' here */ int nres; L->ci = ci->previous; /* back to caller */ L->top = base - 1; for (nres = ci->nresults; l_unlikely(nres > 0); nres--) setnilvalue(s2v(L->top++)); /* all results are nil */ } goto ret; } vmcase(OP_RETURN1) { if (l_unlikely(L->hookmask)) { L->top = ra + 1; savepc(ci); luaD_poscall(L, ci, 1); /* no hurry... */ trap = 1; } else { /* do the 'poscall' here */ int nres = ci->nresults; L->ci = ci->previous; /* back to caller */ if (nres == 0) L->top = base - 1; /* asked for no results */ else { setobjs2s(L, base - 1, ra); /* at least this result */ L->top = base; for (; l_unlikely(nres > 1); nres--) setnilvalue(s2v(L->top++)); /* complete missing results */ } } ret: /* return from a Lua function */ if (ci->callstatus & CIST_FRESH) return; /* end this frame */ else { ci = ci->previous; goto returning; /* continue running caller in this frame */ } } vmcase(OP_FORLOOP) { if (ttisinteger(s2v(ra + 2))) { /* integer loop? */ lua_Unsigned count = l_castS2U(ivalue(s2v(ra + 1))); if (count > 0) { /* still more iterations? */ lua_Integer step = ivalue(s2v(ra + 2)); lua_Integer idx = ivalue(s2v(ra)); /* internal index */ chgivalue(s2v(ra + 1), count - 1); /* update counter */ idx = intop(+, idx, step); /* add step to index */ chgivalue(s2v(ra), idx); /* update internal index */ setivalue(s2v(ra + 3), idx); /* and control variable */ pc -= GETARG_Bx(i); /* jump back */ } } else if (floatforloop(ra)) /* float loop */ pc -= GETARG_Bx(i); /* jump back */ updatetrap(ci); /* allows a signal to break the loop */ vmbreak; } vmcase(OP_FORPREP) { savestate(L, ci); /* in case of errors */ if (forprep(L, ra)) pc += GETARG_Bx(i) + 1; /* skip the loop */ vmbreak; } vmcase(OP_TFORPREP) { /* create to-be-closed upvalue (if needed) */ halfProtect(luaF_newtbcupval(L, ra + 3)); pc += GETARG_Bx(i); i = *(pc++); /* go to next instruction */ lua_assert(GET_OPCODE(i) == OP_TFORCALL && ra == RA(i)); goto l_tforcall; } vmcase(OP_TFORCALL) { l_tforcall: /* 'ra' has the iterator function, 'ra + 1' has the state, 'ra + 2' has the control variable, and 'ra + 3' has the to-be-closed variable. The call will use the stack after these values (starting at 'ra + 4') */ /* push function, state, and control variable */ memcpy(ra + 4, ra, 3 * sizeof(*ra)); L->top = ra + 4 + 3; ProtectNT(luaD_call(L, ra + 4, GETARG_C(i))); /* do the call */ updatestack(ci); /* stack may have changed */ i = *(pc++); /* go to next instruction */ lua_assert(GET_OPCODE(i) == OP_TFORLOOP && ra == RA(i)); goto l_tforloop; } vmcase(OP_TFORLOOP) { l_tforloop: if (!ttisnil(s2v(ra + 4))) { /* continue loop? */ setobjs2s(L, ra + 2, ra + 4); /* save control variable */ pc -= GETARG_Bx(i); /* jump back */ } vmbreak; } vmcase(OP_SETLIST) { int n = GETARG_B(i); unsigned int last = GETARG_C(i); Table *h = hvalue(s2v(ra)); if (n == 0) n = cast_int(L->top - ra) - 1; /* get up to the top */ else L->top = ci->top; /* correct top in case of emergency GC */ last += n; if (TESTARG_k(i)) { last += GETARG_Ax(*pc) * (MAXARG_C + 1); pc++; } if (last > luaH_realasize(h)) /* needs more space? */ luaH_resizearray(L, h, last); /* preallocate it at once */ for (; n > 0; n--) { TValue *val = s2v(ra + n); setobj2t(L, &h->array[last - 1], val); last--; luaC_barrierback(L, obj2gco(h), val); } vmbreak; } vmcase(OP_CLOSURE) { Proto *p = cl->p->p[GETARG_Bx(i)]; halfProtect(pushclosure(L, p, cl->upvals, base, ra)); checkGC(L, ra + 1); vmbreak; } vmcase(OP_VARARG) { int n = GETARG_C(i) - 1; /* required results */ Protect(luaT_getvarargs(L, ci, ra, n)); vmbreak; } vmcase(OP_VARARGPREP) { ProtectNT(luaT_adjustvarargs(L, GETARG_A(i), ci, cl->p)); if (l_unlikely(trap)) { /* previous "Protect" updated trap */ luaD_hookcall(L, ci); L->oldpc = 1; /* next opcode will be seen as a "new" line */ } updatebase(ci); /* function has new base after adjustment */ vmbreak; } vmcase(OP_EXTRAARG) { lua_assert(0); vmbreak; } } } } /* }================================================================== */ /* ** $Id: lapi.c $ ** Lua API ** See Copyright Notice in lua.h */ #define lapi_c #define LUA_CORE /*#include "lprefix.h"*/ #include #include #include /*#include "lua.h"*/ /*#include "lapi.h"*/ /*#include "ldebug.h"*/ /*#include "ldo.h"*/ /*#include "lfunc.h"*/ /*#include "lgc.h"*/ /*#include "lmem.h"*/ /*#include "lobject.h"*/ /*#include "lstate.h"*/ /*#include "lstring.h"*/ /*#include "ltable.h"*/ /*#include "ltm.h"*/ /*#include "lundump.h"*/ /*#include "lvm.h"*/ const char lua_ident[] = "$LuaVersion: " LUA_COPYRIGHT " $" "$LuaAuthors: " LUA_AUTHORS " $"; /* ** Test for a valid index (one that is not the 'nilvalue'). ** '!ttisnil(o)' implies 'o != &G(L)->nilvalue', so it is not needed. ** However, it covers the most common cases in a faster way. */ #define isvalid(L, o) (!ttisnil(o) || o != &G(L)->nilvalue) /* test for pseudo index */ #define ispseudo(i) ((i) <= LUA_REGISTRYINDEX) /* test for upvalue */ #define isupvalue(i) ((i) < LUA_REGISTRYINDEX) /* ** Convert an acceptable index to a pointer to its respective value. ** Non-valid indices return the special nil value 'G(L)->nilvalue'. */ static TValue *index2value (lua_State *L, int idx) { CallInfo *ci = L->ci; if (idx > 0) { StkId o = ci->func + idx; api_check(L, idx <= L->ci->top - (ci->func + 1), "unacceptable index"); if (o >= L->top) return &G(L)->nilvalue; else return s2v(o); } else if (!ispseudo(idx)) { /* negative index */ api_check(L, idx != 0 && -idx <= L->top - (ci->func + 1), "invalid index"); return s2v(L->top + idx); } else if (idx == LUA_REGISTRYINDEX) return &G(L)->l_registry; else { /* upvalues */ idx = LUA_REGISTRYINDEX - idx; api_check(L, idx <= MAXUPVAL + 1, "upvalue index too large"); if (ttisCclosure(s2v(ci->func))) { /* C closure? */ CClosure *func = clCvalue(s2v(ci->func)); return (idx <= func->nupvalues) ? &func->upvalue[idx-1] : &G(L)->nilvalue; } else { /* light C function or Lua function (through a hook)?) */ api_check(L, ttislcf(s2v(ci->func)), "caller not a C function"); return &G(L)->nilvalue; /* no upvalues */ } } } /* ** Convert a valid actual index (not a pseudo-index) to its address. */ l_sinline StkId index2stack (lua_State *L, int idx) { CallInfo *ci = L->ci; if (idx > 0) { StkId o = ci->func + idx; api_check(L, o < L->top, "invalid index"); return o; } else { /* non-positive index */ api_check(L, idx != 0 && -idx <= L->top - (ci->func + 1), "invalid index"); api_check(L, !ispseudo(idx), "invalid index"); return L->top + idx; } } LUA_API int lua_checkstack (lua_State *L, int n) { int res; CallInfo *ci; lua_lock(L); ci = L->ci; api_check(L, n >= 0, "negative 'n'"); if (L->stack_last - L->top > n) /* stack large enough? */ res = 1; /* yes; check is OK */ else { /* no; need to grow stack */ int inuse = cast_int(L->top - L->stack) + EXTRA_STACK; if (inuse > LUAI_MAXSTACK - n) /* can grow without overflow? */ res = 0; /* no */ else /* try to grow stack */ res = luaD_growstack(L, n, 0); } if (res && ci->top < L->top + n) ci->top = L->top + n; /* adjust frame top */ lua_unlock(L); return res; } LUA_API void lua_xmove (lua_State *from, lua_State *to, int n) { int i; if (from == to) return; lua_lock(to); api_checknelems(from, n); api_check(from, G(from) == G(to), "moving among independent states"); api_check(from, to->ci->top - to->top >= n, "stack overflow"); from->top -= n; for (i = 0; i < n; i++) { setobjs2s(to, to->top, from->top + i); to->top++; /* stack already checked by previous 'api_check' */ } lua_unlock(to); } LUA_API lua_CFunction lua_atpanic (lua_State *L, lua_CFunction panicf) { lua_CFunction old; lua_lock(L); old = G(L)->panic; G(L)->panic = panicf; lua_unlock(L); return old; } LUA_API lua_Number lua_version (lua_State *L) { UNUSED(L); return LUA_VERSION_NUM; } /* ** basic stack manipulation */ /* ** convert an acceptable stack index into an absolute index */ LUA_API int lua_absindex (lua_State *L, int idx) { return (idx > 0 || ispseudo(idx)) ? idx : cast_int(L->top - L->ci->func) + idx; } LUA_API int lua_gettop (lua_State *L) { return cast_int(L->top - (L->ci->func + 1)); } LUA_API void lua_settop (lua_State *L, int idx) { CallInfo *ci; StkId func, newtop; ptrdiff_t diff; /* difference for new top */ lua_lock(L); ci = L->ci; func = ci->func; if (idx >= 0) { api_check(L, idx <= ci->top - (func + 1), "new top too large"); diff = ((func + 1) + idx) - L->top; for (; diff > 0; diff--) setnilvalue(s2v(L->top++)); /* clear new slots */ } else { api_check(L, -(idx+1) <= (L->top - (func + 1)), "invalid new top"); diff = idx + 1; /* will "subtract" index (as it is negative) */ } api_check(L, L->tbclist < L->top, "previous pop of an unclosed slot"); newtop = L->top + diff; if (diff < 0 && L->tbclist >= newtop) { lua_assert(hastocloseCfunc(ci->nresults)); luaF_close(L, newtop, CLOSEKTOP, 0); } L->top = newtop; /* correct top only after closing any upvalue */ lua_unlock(L); } LUA_API void lua_closeslot (lua_State *L, int idx) { StkId level; lua_lock(L); level = index2stack(L, idx); api_check(L, hastocloseCfunc(L->ci->nresults) && L->tbclist == level, "no variable to close at given level"); luaF_close(L, level, CLOSEKTOP, 0); level = index2stack(L, idx); /* stack may be moved */ setnilvalue(s2v(level)); lua_unlock(L); } /* ** Reverse the stack segment from 'from' to 'to' ** (auxiliary to 'lua_rotate') ** Note that we move(copy) only the value inside the stack. ** (We do not move additional fields that may exist.) */ l_sinline void reverse (lua_State *L, StkId from, StkId to) { for (; from < to; from++, to--) { TValue temp; setobj(L, &temp, s2v(from)); setobjs2s(L, from, to); setobj2s(L, to, &temp); } } /* ** Let x = AB, where A is a prefix of length 'n'. Then, ** rotate x n == BA. But BA == (A^r . B^r)^r. */ LUA_API void lua_rotate (lua_State *L, int idx, int n) { StkId p, t, m; lua_lock(L); t = L->top - 1; /* end of stack segment being rotated */ p = index2stack(L, idx); /* start of segment */ api_check(L, (n >= 0 ? n : -n) <= (t - p + 1), "invalid 'n'"); m = (n >= 0 ? t - n : p - n - 1); /* end of prefix */ reverse(L, p, m); /* reverse the prefix with length 'n' */ reverse(L, m + 1, t); /* reverse the suffix */ reverse(L, p, t); /* reverse the entire segment */ lua_unlock(L); } LUA_API void lua_copy (lua_State *L, int fromidx, int toidx) { TValue *fr, *to; lua_lock(L); fr = index2value(L, fromidx); to = index2value(L, toidx); api_check(L, isvalid(L, to), "invalid index"); setobj(L, to, fr); if (isupvalue(toidx)) /* function upvalue? */ luaC_barrier(L, clCvalue(s2v(L->ci->func)), fr); /* LUA_REGISTRYINDEX does not need gc barrier (collector revisits it before finishing collection) */ lua_unlock(L); } LUA_API void lua_pushvalue (lua_State *L, int idx) { lua_lock(L); setobj2s(L, L->top, index2value(L, idx)); api_incr_top(L); lua_unlock(L); } /* ** access functions (stack -> C) */ LUA_API int lua_type (lua_State *L, int idx) { const TValue *o = index2value(L, idx); return (isvalid(L, o) ? ttype(o) : LUA_TNONE); } LUA_API const char *lua_typename (lua_State *L, int t) { UNUSED(L); api_check(L, LUA_TNONE <= t && t < LUA_NUMTYPES, "invalid type"); return ttypename(t); } LUA_API int lua_iscfunction (lua_State *L, int idx) { const TValue *o = index2value(L, idx); return (ttislcf(o) || (ttisCclosure(o))); } LUA_API int lua_isinteger (lua_State *L, int idx) { const TValue *o = index2value(L, idx); return ttisinteger(o); } LUA_API int lua_isnumber (lua_State *L, int idx) { lua_Number n; const TValue *o = index2value(L, idx); return tonumber(o, &n); } LUA_API int lua_isstring (lua_State *L, int idx) { const TValue *o = index2value(L, idx); return (ttisstring(o) || cvt2str(o)); } LUA_API int lua_isuserdata (lua_State *L, int idx) { const TValue *o = index2value(L, idx); return (ttisfulluserdata(o) || ttislightuserdata(o)); } LUA_API int lua_rawequal (lua_State *L, int index1, int index2) { const TValue *o1 = index2value(L, index1); const TValue *o2 = index2value(L, index2); return (isvalid(L, o1) && isvalid(L, o2)) ? luaV_rawequalobj(o1, o2) : 0; } LUA_API void lua_arith (lua_State *L, int op) { lua_lock(L); if (op != LUA_OPUNM && op != LUA_OPBNOT) api_checknelems(L, 2); /* all other operations expect two operands */ else { /* for unary operations, add fake 2nd operand */ api_checknelems(L, 1); setobjs2s(L, L->top, L->top - 1); api_incr_top(L); } /* first operand at top - 2, second at top - 1; result go to top - 2 */ luaO_arith(L, op, s2v(L->top - 2), s2v(L->top - 1), L->top - 2); L->top--; /* remove second operand */ lua_unlock(L); } LUA_API int lua_compare (lua_State *L, int index1, int index2, int op) { const TValue *o1; const TValue *o2; int i = 0; lua_lock(L); /* may call tag method */ o1 = index2value(L, index1); o2 = index2value(L, index2); if (isvalid(L, o1) && isvalid(L, o2)) { switch (op) { case LUA_OPEQ: i = luaV_equalobj(L, o1, o2); break; case LUA_OPLT: i = luaV_lessthan(L, o1, o2); break; case LUA_OPLE: i = luaV_lessequal(L, o1, o2); break; default: api_check(L, 0, "invalid option"); } } lua_unlock(L); return i; } LUA_API size_t lua_stringtonumber (lua_State *L, const char *s) { size_t sz = luaO_str2num(s, s2v(L->top)); if (sz != 0) api_incr_top(L); return sz; } LUA_API lua_Number lua_tonumberx (lua_State *L, int idx, int *pisnum) { lua_Number n = 0; const TValue *o = index2value(L, idx); int isnum = tonumber(o, &n); if (pisnum) *pisnum = isnum; return n; } LUA_API lua_Integer lua_tointegerx (lua_State *L, int idx, int *pisnum) { lua_Integer res = 0; const TValue *o = index2value(L, idx); int isnum = tointeger(o, &res); if (pisnum) *pisnum = isnum; return res; } LUA_API int lua_toboolean (lua_State *L, int idx) { const TValue *o = index2value(L, idx); return !l_isfalse(o); } LUA_API const char *lua_tolstring (lua_State *L, int idx, size_t *len) { TValue *o; lua_lock(L); o = index2value(L, idx); if (!ttisstring(o)) { if (!cvt2str(o)) { /* not convertible? */ if (len != NULL) *len = 0; lua_unlock(L); return NULL; } luaO_tostring(L, o); luaC_checkGC(L); o = index2value(L, idx); /* previous call may reallocate the stack */ } if (len != NULL) *len = vslen(o); lua_unlock(L); return svalue(o); } LUA_API lua_Unsigned lua_rawlen (lua_State *L, int idx) { const TValue *o = index2value(L, idx); switch (ttypetag(o)) { case LUA_VSHRSTR: return tsvalue(o)->shrlen; case LUA_VLNGSTR: return tsvalue(o)->u.lnglen; case LUA_VUSERDATA: return uvalue(o)->len; case LUA_VTABLE: return luaH_getn(hvalue(o)); default: return 0; } } LUA_API lua_CFunction lua_tocfunction (lua_State *L, int idx) { const TValue *o = index2value(L, idx); if (ttislcf(o)) return fvalue(o); else if (ttisCclosure(o)) return clCvalue(o)->f; else return NULL; /* not a C function */ } l_sinline void *touserdata (const TValue *o) { switch (ttype(o)) { case LUA_TUSERDATA: return getudatamem(uvalue(o)); case LUA_TLIGHTUSERDATA: return pvalue(o); default: return NULL; } } LUA_API void *lua_touserdata (lua_State *L, int idx) { const TValue *o = index2value(L, idx); return touserdata(o); } LUA_API lua_State *lua_tothread (lua_State *L, int idx) { const TValue *o = index2value(L, idx); return (!ttisthread(o)) ? NULL : thvalue(o); } /* ** Returns a pointer to the internal representation of an object. ** Note that ANSI C does not allow the conversion of a pointer to ** function to a 'void*', so the conversion here goes through ** a 'size_t'. (As the returned pointer is only informative, this ** conversion should not be a problem.) */ LUA_API const void *lua_topointer (lua_State *L, int idx) { const TValue *o = index2value(L, idx); switch (ttypetag(o)) { case LUA_VLCF: return cast_voidp(cast_sizet(fvalue(o))); case LUA_VUSERDATA: case LUA_VLIGHTUSERDATA: return touserdata(o); default: { if (iscollectable(o)) return gcvalue(o); else return NULL; } } } /* ** push functions (C -> stack) */ LUA_API void lua_pushnil (lua_State *L) { lua_lock(L); setnilvalue(s2v(L->top)); api_incr_top(L); lua_unlock(L); } LUA_API void lua_pushnumber (lua_State *L, lua_Number n) { lua_lock(L); setfltvalue(s2v(L->top), n); api_incr_top(L); lua_unlock(L); } LUA_API void lua_pushinteger (lua_State *L, lua_Integer n) { lua_lock(L); setivalue(s2v(L->top), n); api_incr_top(L); lua_unlock(L); } /* ** Pushes on the stack a string with given length. Avoid using 's' when ** 'len' == 0 (as 's' can be NULL in that case), due to later use of ** 'memcmp' and 'memcpy'. */ LUA_API const char *lua_pushlstring (lua_State *L, const char *s, size_t len) { TString *ts; lua_lock(L); ts = (len == 0) ? luaS_new(L, "") : luaS_newlstr(L, s, len); setsvalue2s(L, L->top, ts); api_incr_top(L); luaC_checkGC(L); lua_unlock(L); return getstr(ts); } LUA_API const char *lua_pushstring (lua_State *L, const char *s) { lua_lock(L); if (s == NULL) setnilvalue(s2v(L->top)); else { TString *ts; ts = luaS_new(L, s); setsvalue2s(L, L->top, ts); s = getstr(ts); /* internal copy's address */ } api_incr_top(L); luaC_checkGC(L); lua_unlock(L); return s; } LUA_API const char *lua_pushvfstring (lua_State *L, const char *fmt, va_list argp) { const char *ret; lua_lock(L); ret = luaO_pushvfstring(L, fmt, argp); luaC_checkGC(L); lua_unlock(L); return ret; } LUA_API const char *lua_pushfstring (lua_State *L, const char *fmt, ...) { const char *ret; va_list argp; lua_lock(L); va_start(argp, fmt); ret = luaO_pushvfstring(L, fmt, argp); va_end(argp); luaC_checkGC(L); lua_unlock(L); return ret; } LUA_API void lua_pushcclosure (lua_State *L, lua_CFunction fn, int n) { lua_lock(L); if (n == 0) { setfvalue(s2v(L->top), fn); api_incr_top(L); } else { CClosure *cl; api_checknelems(L, n); api_check(L, n <= MAXUPVAL, "upvalue index too large"); cl = luaF_newCclosure(L, n); cl->f = fn; L->top -= n; while (n--) { setobj2n(L, &cl->upvalue[n], s2v(L->top + n)); /* does not need barrier because closure is white */ lua_assert(iswhite(cl)); } setclCvalue(L, s2v(L->top), cl); api_incr_top(L); luaC_checkGC(L); } lua_unlock(L); } LUA_API void lua_pushboolean (lua_State *L, int b) { lua_lock(L); if (b) setbtvalue(s2v(L->top)); else setbfvalue(s2v(L->top)); api_incr_top(L); lua_unlock(L); } LUA_API void lua_pushlightuserdata (lua_State *L, void *p) { lua_lock(L); setpvalue(s2v(L->top), p); api_incr_top(L); lua_unlock(L); } LUA_API int lua_pushthread (lua_State *L) { lua_lock(L); setthvalue(L, s2v(L->top), L); api_incr_top(L); lua_unlock(L); return (G(L)->mainthread == L); } /* ** get functions (Lua -> stack) */ l_sinline int auxgetstr (lua_State *L, const TValue *t, const char *k) { const TValue *slot; TString *str = luaS_new(L, k); if (luaV_fastget(L, t, str, slot, luaH_getstr)) { setobj2s(L, L->top, slot); api_incr_top(L); } else { setsvalue2s(L, L->top, str); api_incr_top(L); luaV_finishget(L, t, s2v(L->top - 1), L->top - 1, slot); } lua_unlock(L); return ttype(s2v(L->top - 1)); } /* ** Get the global table in the registry. Since all predefined ** indices in the registry were inserted right when the registry ** was created and never removed, they must always be in the array ** part of the registry. */ #define getGtable(L) \ (&hvalue(&G(L)->l_registry)->array[LUA_RIDX_GLOBALS - 1]) LUA_API int lua_getglobal (lua_State *L, const char *name) { const TValue *G; lua_lock(L); G = getGtable(L); return auxgetstr(L, G, name); } LUA_API int lua_gettable (lua_State *L, int idx) { const TValue *slot; TValue *t; lua_lock(L); t = index2value(L, idx); if (luaV_fastget(L, t, s2v(L->top - 1), slot, luaH_get)) { setobj2s(L, L->top - 1, slot); } else luaV_finishget(L, t, s2v(L->top - 1), L->top - 1, slot); lua_unlock(L); return ttype(s2v(L->top - 1)); } LUA_API int lua_getfield (lua_State *L, int idx, const char *k) { lua_lock(L); return auxgetstr(L, index2value(L, idx), k); } LUA_API int lua_geti (lua_State *L, int idx, lua_Integer n) { TValue *t; const TValue *slot; lua_lock(L); t = index2value(L, idx); if (luaV_fastgeti(L, t, n, slot)) { setobj2s(L, L->top, slot); } else { TValue aux; setivalue(&aux, n); luaV_finishget(L, t, &aux, L->top, slot); } api_incr_top(L); lua_unlock(L); return ttype(s2v(L->top - 1)); } l_sinline int finishrawget (lua_State *L, const TValue *val) { if (isempty(val)) /* avoid copying empty items to the stack */ setnilvalue(s2v(L->top)); else setobj2s(L, L->top, val); api_incr_top(L); lua_unlock(L); return ttype(s2v(L->top - 1)); } static Table *gettable (lua_State *L, int idx) { TValue *t = index2value(L, idx); api_check(L, ttistable(t), "table expected"); return hvalue(t); } LUA_API int lua_rawget (lua_State *L, int idx) { Table *t; const TValue *val; lua_lock(L); api_checknelems(L, 1); t = gettable(L, idx); val = luaH_get(t, s2v(L->top - 1)); L->top--; /* remove key */ return finishrawget(L, val); } LUA_API int lua_rawgeti (lua_State *L, int idx, lua_Integer n) { Table *t; lua_lock(L); t = gettable(L, idx); return finishrawget(L, luaH_getint(t, n)); } LUA_API int lua_rawgetp (lua_State *L, int idx, const void *p) { Table *t; TValue k; lua_lock(L); t = gettable(L, idx); setpvalue(&k, cast_voidp(p)); return finishrawget(L, luaH_get(t, &k)); } LUA_API void lua_createtable (lua_State *L, int narray, int nrec) { Table *t; lua_lock(L); t = luaH_new(L); sethvalue2s(L, L->top, t); api_incr_top(L); if (narray > 0 || nrec > 0) luaH_resize(L, t, narray, nrec); luaC_checkGC(L); lua_unlock(L); } LUA_API int lua_getmetatable (lua_State *L, int objindex) { const TValue *obj; Table *mt; int res = 0; lua_lock(L); obj = index2value(L, objindex); switch (ttype(obj)) { case LUA_TTABLE: mt = hvalue(obj)->metatable; break; case LUA_TUSERDATA: mt = uvalue(obj)->metatable; break; default: mt = G(L)->mt[ttype(obj)]; break; } if (mt != NULL) { sethvalue2s(L, L->top, mt); api_incr_top(L); res = 1; } lua_unlock(L); return res; } LUA_API int lua_getiuservalue (lua_State *L, int idx, int n) { TValue *o; int t; lua_lock(L); o = index2value(L, idx); api_check(L, ttisfulluserdata(o), "full userdata expected"); if (n <= 0 || n > uvalue(o)->nuvalue) { setnilvalue(s2v(L->top)); t = LUA_TNONE; } else { setobj2s(L, L->top, &uvalue(o)->uv[n - 1].uv); t = ttype(s2v(L->top)); } api_incr_top(L); lua_unlock(L); return t; } /* ** set functions (stack -> Lua) */ /* ** t[k] = value at the top of the stack (where 'k' is a string) */ static void auxsetstr (lua_State *L, const TValue *t, const char *k) { const TValue *slot; TString *str = luaS_new(L, k); api_checknelems(L, 1); if (luaV_fastget(L, t, str, slot, luaH_getstr)) { luaV_finishfastset(L, t, slot, s2v(L->top - 1)); L->top--; /* pop value */ } else { setsvalue2s(L, L->top, str); /* push 'str' (to make it a TValue) */ api_incr_top(L); luaV_finishset(L, t, s2v(L->top - 1), s2v(L->top - 2), slot); L->top -= 2; /* pop value and key */ } lua_unlock(L); /* lock done by caller */ } LUA_API void lua_setglobal (lua_State *L, const char *name) { const TValue *G; lua_lock(L); /* unlock done in 'auxsetstr' */ G = getGtable(L); auxsetstr(L, G, name); } LUA_API void lua_settable (lua_State *L, int idx) { TValue *t; const TValue *slot; lua_lock(L); api_checknelems(L, 2); t = index2value(L, idx); if (luaV_fastget(L, t, s2v(L->top - 2), slot, luaH_get)) { luaV_finishfastset(L, t, slot, s2v(L->top - 1)); } else luaV_finishset(L, t, s2v(L->top - 2), s2v(L->top - 1), slot); L->top -= 2; /* pop index and value */ lua_unlock(L); } LUA_API void lua_setfield (lua_State *L, int idx, const char *k) { lua_lock(L); /* unlock done in 'auxsetstr' */ auxsetstr(L, index2value(L, idx), k); } LUA_API void lua_seti (lua_State *L, int idx, lua_Integer n) { TValue *t; const TValue *slot; lua_lock(L); api_checknelems(L, 1); t = index2value(L, idx); if (luaV_fastgeti(L, t, n, slot)) { luaV_finishfastset(L, t, slot, s2v(L->top - 1)); } else { TValue aux; setivalue(&aux, n); luaV_finishset(L, t, &aux, s2v(L->top - 1), slot); } L->top--; /* pop value */ lua_unlock(L); } static void aux_rawset (lua_State *L, int idx, TValue *key, int n) { Table *t; lua_lock(L); api_checknelems(L, n); t = gettable(L, idx); luaH_set(L, t, key, s2v(L->top - 1)); invalidateTMcache(t); luaC_barrierback(L, obj2gco(t), s2v(L->top - 1)); L->top -= n; lua_unlock(L); } LUA_API void lua_rawset (lua_State *L, int idx) { aux_rawset(L, idx, s2v(L->top - 2), 2); } LUA_API void lua_rawsetp (lua_State *L, int idx, const void *p) { TValue k; setpvalue(&k, cast_voidp(p)); aux_rawset(L, idx, &k, 1); } LUA_API void lua_rawseti (lua_State *L, int idx, lua_Integer n) { Table *t; lua_lock(L); api_checknelems(L, 1); t = gettable(L, idx); luaH_setint(L, t, n, s2v(L->top - 1)); luaC_barrierback(L, obj2gco(t), s2v(L->top - 1)); L->top--; lua_unlock(L); } LUA_API int lua_setmetatable (lua_State *L, int objindex) { TValue *obj; Table *mt; lua_lock(L); api_checknelems(L, 1); obj = index2value(L, objindex); if (ttisnil(s2v(L->top - 1))) mt = NULL; else { api_check(L, ttistable(s2v(L->top - 1)), "table expected"); mt = hvalue(s2v(L->top - 1)); } switch (ttype(obj)) { case LUA_TTABLE: { hvalue(obj)->metatable = mt; if (mt) { luaC_objbarrier(L, gcvalue(obj), mt); luaC_checkfinalizer(L, gcvalue(obj), mt); } break; } case LUA_TUSERDATA: { uvalue(obj)->metatable = mt; if (mt) { luaC_objbarrier(L, uvalue(obj), mt); luaC_checkfinalizer(L, gcvalue(obj), mt); } break; } default: { G(L)->mt[ttype(obj)] = mt; break; } } L->top--; lua_unlock(L); return 1; } LUA_API int lua_setiuservalue (lua_State *L, int idx, int n) { TValue *o; int res; lua_lock(L); api_checknelems(L, 1); o = index2value(L, idx); api_check(L, ttisfulluserdata(o), "full userdata expected"); if (!(cast_uint(n) - 1u < cast_uint(uvalue(o)->nuvalue))) res = 0; /* 'n' not in [1, uvalue(o)->nuvalue] */ else { setobj(L, &uvalue(o)->uv[n - 1].uv, s2v(L->top - 1)); luaC_barrierback(L, gcvalue(o), s2v(L->top - 1)); res = 1; } L->top--; lua_unlock(L); return res; } /* ** 'load' and 'call' functions (run Lua code) */ #define checkresults(L,na,nr) \ api_check(L, (nr) == LUA_MULTRET || (L->ci->top - L->top >= (nr) - (na)), \ "results from function overflow current stack size") LUA_API void lua_callk (lua_State *L, int nargs, int nresults, lua_KContext ctx, lua_KFunction k) { StkId func; lua_lock(L); api_check(L, k == NULL || !isLua(L->ci), "cannot use continuations inside hooks"); api_checknelems(L, nargs+1); api_check(L, L->status == LUA_OK, "cannot do calls on non-normal thread"); checkresults(L, nargs, nresults); func = L->top - (nargs+1); if (k != NULL && yieldable(L)) { /* need to prepare continuation? */ L->ci->u.c.k = k; /* save continuation */ L->ci->u.c.ctx = ctx; /* save context */ luaD_call(L, func, nresults); /* do the call */ } else /* no continuation or no yieldable */ luaD_callnoyield(L, func, nresults); /* just do the call */ adjustresults(L, nresults); lua_unlock(L); } /* ** Execute a protected call. */ struct CallS { /* data to 'f_call' */ StkId func; int nresults; }; static void f_call (lua_State *L, void *ud) { struct CallS *c = cast(struct CallS *, ud); luaD_callnoyield(L, c->func, c->nresults); } LUA_API int lua_pcallk (lua_State *L, int nargs, int nresults, int errfunc, lua_KContext ctx, lua_KFunction k) { struct CallS c; int status; ptrdiff_t func; lua_lock(L); api_check(L, k == NULL || !isLua(L->ci), "cannot use continuations inside hooks"); api_checknelems(L, nargs+1); api_check(L, L->status == LUA_OK, "cannot do calls on non-normal thread"); checkresults(L, nargs, nresults); if (errfunc == 0) func = 0; else { StkId o = index2stack(L, errfunc); api_check(L, ttisfunction(s2v(o)), "error handler must be a function"); func = savestack(L, o); } c.func = L->top - (nargs+1); /* function to be called */ if (k == NULL || !yieldable(L)) { /* no continuation or no yieldable? */ c.nresults = nresults; /* do a 'conventional' protected call */ status = luaD_pcall(L, f_call, &c, savestack(L, c.func), func); } else { /* prepare continuation (call is already protected by 'resume') */ CallInfo *ci = L->ci; ci->u.c.k = k; /* save continuation */ ci->u.c.ctx = ctx; /* save context */ /* save information for error recovery */ ci->u2.funcidx = cast_int(savestack(L, c.func)); ci->u.c.old_errfunc = L->errfunc; L->errfunc = func; setoah(ci->callstatus, L->allowhook); /* save value of 'allowhook' */ ci->callstatus |= CIST_YPCALL; /* function can do error recovery */ luaD_call(L, c.func, nresults); /* do the call */ ci->callstatus &= ~CIST_YPCALL; L->errfunc = ci->u.c.old_errfunc; status = LUA_OK; /* if it is here, there were no errors */ } adjustresults(L, nresults); lua_unlock(L); return status; } LUA_API int lua_load (lua_State *L, lua_Reader reader, void *data, const char *chunkname, const char *mode) { ZIO z; int status; lua_lock(L); if (!chunkname) chunkname = "?"; luaZ_init(L, &z, reader, data); status = luaD_protectedparser(L, &z, chunkname, mode); if (status == LUA_OK) { /* no errors? */ LClosure *f = clLvalue(s2v(L->top - 1)); /* get newly created function */ if (f->nupvalues >= 1) { /* does it have an upvalue? */ /* get global table from registry */ const TValue *gt = getGtable(L); /* set global table as 1st upvalue of 'f' (may be LUA_ENV) */ setobj(L, f->upvals[0]->v, gt); luaC_barrier(L, f->upvals[0], gt); } } lua_unlock(L); return status; } LUA_API int lua_dump (lua_State *L, lua_Writer writer, void *data, int strip) { int status; TValue *o; lua_lock(L); api_checknelems(L, 1); o = s2v(L->top - 1); if (isLfunction(o)) status = luaU_dump(L, getproto(o), writer, data, strip); else status = 1; lua_unlock(L); return status; } LUA_API int lua_status (lua_State *L) { return L->status; } /* ** Garbage-collection function */ LUA_API int lua_gc (lua_State *L, int what, ...) { va_list argp; int res = 0; global_State *g = G(L); if (g->gcstp & GCSTPGC) /* internal stop? */ return -1; /* all options are invalid when stopped */ lua_lock(L); va_start(argp, what); switch (what) { case LUA_GCSTOP: { g->gcstp = GCSTPUSR; /* stopped by the user */ break; } case LUA_GCRESTART: { luaE_setdebt(g, 0); g->gcstp = 0; /* (GCSTPGC must be already zero here) */ break; } case LUA_GCCOLLECT: { luaC_fullgc(L, 0); break; } case LUA_GCCOUNT: { /* GC values are expressed in Kbytes: #bytes/2^10 */ res = cast_int(gettotalbytes(g) >> 10); break; } case LUA_GCCOUNTB: { res = cast_int(gettotalbytes(g) & 0x3ff); break; } case LUA_GCSTEP: { int data = va_arg(argp, int); l_mem debt = 1; /* =1 to signal that it did an actual step */ lu_byte oldstp = g->gcstp; g->gcstp = 0; /* allow GC to run (GCSTPGC must be zero here) */ if (data == 0) { luaE_setdebt(g, 0); /* do a basic step */ luaC_step(L); } else { /* add 'data' to total debt */ debt = cast(l_mem, data) * 1024 + g->GCdebt; luaE_setdebt(g, debt); luaC_checkGC(L); } g->gcstp = oldstp; /* restore previous state */ if (debt > 0 && g->gcstate == GCSpause) /* end of cycle? */ res = 1; /* signal it */ break; } case LUA_GCSETPAUSE: { int data = va_arg(argp, int); res = getgcparam(g->gcpause); setgcparam(g->gcpause, data); break; } case LUA_GCSETSTEPMUL: { int data = va_arg(argp, int); res = getgcparam(g->gcstepmul); setgcparam(g->gcstepmul, data); break; } case LUA_GCISRUNNING: { res = gcrunning(g); break; } case LUA_GCGEN: { int minormul = va_arg(argp, int); int majormul = va_arg(argp, int); res = isdecGCmodegen(g) ? LUA_GCGEN : LUA_GCINC; if (minormul != 0) g->genminormul = minormul; if (majormul != 0) setgcparam(g->genmajormul, majormul); luaC_changemode(L, KGC_GEN); break; } case LUA_GCINC: { int pause = va_arg(argp, int); int stepmul = va_arg(argp, int); int stepsize = va_arg(argp, int); res = isdecGCmodegen(g) ? LUA_GCGEN : LUA_GCINC; if (pause != 0) setgcparam(g->gcpause, pause); if (stepmul != 0) setgcparam(g->gcstepmul, stepmul); if (stepsize != 0) g->gcstepsize = stepsize; luaC_changemode(L, KGC_INC); break; } default: res = -1; /* invalid option */ } va_end(argp); lua_unlock(L); return res; } /* ** miscellaneous functions */ LUA_API int lua_error (lua_State *L) { TValue *errobj; lua_lock(L); errobj = s2v(L->top - 1); api_checknelems(L, 1); /* error object is the memory error message? */ if (ttisshrstring(errobj) && eqshrstr(tsvalue(errobj), G(L)->memerrmsg)) luaM_error(L); /* raise a memory error */ else luaG_errormsg(L); /* raise a regular error */ /* code unreachable; will unlock when control actually leaves the kernel */ return 0; /* to avoid warnings */ } LUA_API int lua_next (lua_State *L, int idx) { Table *t; int more; lua_lock(L); api_checknelems(L, 1); t = gettable(L, idx); more = luaH_next(L, t, L->top - 1); if (more) { api_incr_top(L); } else /* no more elements */ L->top -= 1; /* remove key */ lua_unlock(L); return more; } LUA_API void lua_toclose (lua_State *L, int idx) { int nresults; StkId o; lua_lock(L); o = index2stack(L, idx); nresults = L->ci->nresults; api_check(L, L->tbclist < o, "given index below or equal a marked one"); luaF_newtbcupval(L, o); /* create new to-be-closed upvalue */ if (!hastocloseCfunc(nresults)) /* function not marked yet? */ L->ci->nresults = codeNresults(nresults); /* mark it */ lua_assert(hastocloseCfunc(L->ci->nresults)); lua_unlock(L); } LUA_API void lua_concat (lua_State *L, int n) { lua_lock(L); api_checknelems(L, n); if (n > 0) luaV_concat(L, n); else { /* nothing to concatenate */ setsvalue2s(L, L->top, luaS_newlstr(L, "", 0)); /* push empty string */ api_incr_top(L); } luaC_checkGC(L); lua_unlock(L); } LUA_API void lua_len (lua_State *L, int idx) { TValue *t; lua_lock(L); t = index2value(L, idx); luaV_objlen(L, L->top, t); api_incr_top(L); lua_unlock(L); } LUA_API lua_Alloc lua_getallocf (lua_State *L, void **ud) { lua_Alloc f; lua_lock(L); if (ud) *ud = G(L)->ud; f = G(L)->frealloc; lua_unlock(L); return f; } LUA_API void lua_setallocf (lua_State *L, lua_Alloc f, void *ud) { lua_lock(L); G(L)->ud = ud; G(L)->frealloc = f; lua_unlock(L); } void lua_setwarnf (lua_State *L, lua_WarnFunction f, void *ud) { lua_lock(L); G(L)->ud_warn = ud; G(L)->warnf = f; lua_unlock(L); } void lua_warning (lua_State *L, const char *msg, int tocont) { lua_lock(L); luaE_warning(L, msg, tocont); lua_unlock(L); } LUA_API void *lua_newuserdatauv (lua_State *L, size_t size, int nuvalue) { Udata *u; lua_lock(L); api_check(L, 0 <= nuvalue && nuvalue < USHRT_MAX, "invalid value"); u = luaS_newudata(L, size, nuvalue); setuvalue(L, s2v(L->top), u); api_incr_top(L); luaC_checkGC(L); lua_unlock(L); return getudatamem(u); } static const char *aux_upvalue (TValue *fi, int n, TValue **val, GCObject **owner) { switch (ttypetag(fi)) { case LUA_VCCL: { /* C closure */ CClosure *f = clCvalue(fi); if (!(cast_uint(n) - 1u < cast_uint(f->nupvalues))) return NULL; /* 'n' not in [1, f->nupvalues] */ *val = &f->upvalue[n-1]; if (owner) *owner = obj2gco(f); return ""; } case LUA_VLCL: { /* Lua closure */ LClosure *f = clLvalue(fi); TString *name; Proto *p = f->p; if (!(cast_uint(n) - 1u < cast_uint(p->sizeupvalues))) return NULL; /* 'n' not in [1, p->sizeupvalues] */ *val = f->upvals[n-1]->v; if (owner) *owner = obj2gco(f->upvals[n - 1]); name = p->upvalues[n-1].name; return (name == NULL) ? "(no name)" : getstr(name); } default: return NULL; /* not a closure */ } } LUA_API const char *lua_getupvalue (lua_State *L, int funcindex, int n) { const char *name; TValue *val = NULL; /* to avoid warnings */ lua_lock(L); name = aux_upvalue(index2value(L, funcindex), n, &val, NULL); if (name) { setobj2s(L, L->top, val); api_incr_top(L); } lua_unlock(L); return name; } LUA_API const char *lua_setupvalue (lua_State *L, int funcindex, int n) { const char *name; TValue *val = NULL; /* to avoid warnings */ GCObject *owner = NULL; /* to avoid warnings */ TValue *fi; lua_lock(L); fi = index2value(L, funcindex); api_checknelems(L, 1); name = aux_upvalue(fi, n, &val, &owner); if (name) { L->top--; setobj(L, val, s2v(L->top)); luaC_barrier(L, owner, val); } lua_unlock(L); return name; } static UpVal **getupvalref (lua_State *L, int fidx, int n, LClosure **pf) { static const UpVal *const nullup = NULL; LClosure *f; TValue *fi = index2value(L, fidx); api_check(L, ttisLclosure(fi), "Lua function expected"); f = clLvalue(fi); if (pf) *pf = f; if (1 <= n && n <= f->p->sizeupvalues) return &f->upvals[n - 1]; /* get its upvalue pointer */ else return (UpVal**)&nullup; } LUA_API void *lua_upvalueid (lua_State *L, int fidx, int n) { TValue *fi = index2value(L, fidx); switch (ttypetag(fi)) { case LUA_VLCL: { /* lua closure */ return *getupvalref(L, fidx, n, NULL); } case LUA_VCCL: { /* C closure */ CClosure *f = clCvalue(fi); if (1 <= n && n <= f->nupvalues) return &f->upvalue[n - 1]; /* else */ } /* FALLTHROUGH */ case LUA_VLCF: return NULL; /* light C functions have no upvalues */ default: { api_check(L, 0, "function expected"); return NULL; } } } LUA_API void lua_upvaluejoin (lua_State *L, int fidx1, int n1, int fidx2, int n2) { LClosure *f1; UpVal **up1 = getupvalref(L, fidx1, n1, &f1); UpVal **up2 = getupvalref(L, fidx2, n2, NULL); api_check(L, *up1 != NULL && *up2 != NULL, "invalid upvalue index"); *up1 = *up2; luaC_objbarrier(L, f1, *up1); } /* ** $Id: lauxlib.c $ ** Auxiliary functions for building Lua libraries ** See Copyright Notice in lua.h */ #define lauxlib_c #define LUA_LIB /*#include "lprefix.h"*/ #include #include #include #include #include /* ** This file uses only the official API of Lua. ** Any function declared here could be written as an application function. */ /*#include "lua.h"*/ /*#include "lauxlib.h"*/ #if !defined(MAX_SIZET) /* maximum value for size_t */ #define MAX_SIZET ((size_t)(~(size_t)0)) #endif /* ** {====================================================== ** Traceback ** ======================================================= */ #define LEVELS1 10 /* size of the first part of the stack */ #define LEVELS2 11 /* size of the second part of the stack */ /* ** Search for 'objidx' in table at index -1. ('objidx' must be an ** absolute index.) Return 1 + string at top if it found a good name. */ static int findfield (lua_State *L, int objidx, int level) { if (level == 0 || !lua_istable(L, -1)) return 0; /* not found */ lua_pushnil(L); /* start 'next' loop */ while (lua_next(L, -2)) { /* for each pair in table */ if (lua_type(L, -2) == LUA_TSTRING) { /* ignore non-string keys */ if (lua_rawequal(L, objidx, -1)) { /* found object? */ lua_pop(L, 1); /* remove value (but keep name) */ return 1; } else if (findfield(L, objidx, level - 1)) { /* try recursively */ /* stack: lib_name, lib_table, field_name (top) */ lua_pushliteral(L, "."); /* place '.' between the two names */ lua_replace(L, -3); /* (in the slot occupied by table) */ lua_concat(L, 3); /* lib_name.field_name */ return 1; } } lua_pop(L, 1); /* remove value */ } return 0; /* not found */ } /* ** Search for a name for a function in all loaded modules */ static int pushglobalfuncname (lua_State *L, lua_Debug *ar) { int top = lua_gettop(L); lua_getinfo(L, "f", ar); /* push function */ lua_getfield(L, LUA_REGISTRYINDEX, LUA_LOADED_TABLE); if (findfield(L, top + 1, 2)) { const char *name = lua_tostring(L, -1); if (strncmp(name, LUA_GNAME ".", 3) == 0) { /* name start with '_G.'? */ lua_pushstring(L, name + 3); /* push name without prefix */ lua_remove(L, -2); /* remove original name */ } lua_copy(L, -1, top + 1); /* copy name to proper place */ lua_settop(L, top + 1); /* remove table "loaded" and name copy */ return 1; } else { lua_settop(L, top); /* remove function and global table */ return 0; } } static void pushfuncname (lua_State *L, lua_Debug *ar) { if (pushglobalfuncname(L, ar)) { /* try first a global name */ lua_pushfstring(L, "function '%s'", lua_tostring(L, -1)); lua_remove(L, -2); /* remove name */ } else if (*ar->namewhat != '\0') /* is there a name from code? */ lua_pushfstring(L, "%s '%s'", ar->namewhat, ar->name); /* use it */ else if (*ar->what == 'm') /* main? */ lua_pushliteral(L, "main chunk"); else if (*ar->what != 'C') /* for Lua functions, use */ lua_pushfstring(L, "function <%s:%d>", ar->short_src, ar->linedefined); else /* nothing left... */ lua_pushliteral(L, "?"); } static int lastlevel (lua_State *L) { lua_Debug ar; int li = 1, le = 1; /* find an upper bound */ while (lua_getstack(L, le, &ar)) { li = le; le *= 2; } /* do a binary search */ while (li < le) { int m = (li + le)/2; if (lua_getstack(L, m, &ar)) li = m + 1; else le = m; } return le - 1; } LUALIB_API void luaL_traceback (lua_State *L, lua_State *L1, const char *msg, int level) { luaL_Buffer b; lua_Debug ar; int last = lastlevel(L1); int limit2show = (last - level > LEVELS1 + LEVELS2) ? LEVELS1 : -1; luaL_buffinit(L, &b); if (msg) { luaL_addstring(&b, msg); luaL_addchar(&b, '\n'); } luaL_addstring(&b, "stack traceback:"); while (lua_getstack(L1, level++, &ar)) { if (limit2show-- == 0) { /* too many levels? */ int n = last - level - LEVELS2 + 1; /* number of levels to skip */ lua_pushfstring(L, "\n\t...\t(skipping %d levels)", n); luaL_addvalue(&b); /* add warning about skip */ level += n; /* and skip to last levels */ } else { lua_getinfo(L1, "Slnt", &ar); if (ar.currentline <= 0) lua_pushfstring(L, "\n\t%s: in ", ar.short_src); else lua_pushfstring(L, "\n\t%s:%d: in ", ar.short_src, ar.currentline); luaL_addvalue(&b); pushfuncname(L, &ar); luaL_addvalue(&b); if (ar.istailcall) luaL_addstring(&b, "\n\t(...tail calls...)"); } } luaL_pushresult(&b); } /* }====================================================== */ /* ** {====================================================== ** Error-report functions ** ======================================================= */ LUALIB_API int luaL_argerror (lua_State *L, int arg, const char *extramsg) { lua_Debug ar; if (!lua_getstack(L, 0, &ar)) /* no stack frame? */ return luaL_error(L, "bad argument #%d (%s)", arg, extramsg); lua_getinfo(L, "n", &ar); if (strcmp(ar.namewhat, "method") == 0) { arg--; /* do not count 'self' */ if (arg == 0) /* error is in the self argument itself? */ return luaL_error(L, "calling '%s' on bad self (%s)", ar.name, extramsg); } if (ar.name == NULL) ar.name = (pushglobalfuncname(L, &ar)) ? lua_tostring(L, -1) : "?"; return luaL_error(L, "bad argument #%d to '%s' (%s)", arg, ar.name, extramsg); } LUALIB_API int luaL_typeerror (lua_State *L, int arg, const char *tname) { const char *msg; const char *typearg; /* name for the type of the actual argument */ if (luaL_getmetafield(L, arg, "__name") == LUA_TSTRING) typearg = lua_tostring(L, -1); /* use the given type name */ else if (lua_type(L, arg) == LUA_TLIGHTUSERDATA) typearg = "light userdata"; /* special name for messages */ else typearg = luaL_typename(L, arg); /* standard name */ msg = lua_pushfstring(L, "%s expected, got %s", tname, typearg); return luaL_argerror(L, arg, msg); } static void tag_error (lua_State *L, int arg, int tag) { luaL_typeerror(L, arg, lua_typename(L, tag)); } /* ** The use of 'lua_pushfstring' ensures this function does not ** need reserved stack space when called. */ LUALIB_API void luaL_where (lua_State *L, int level) { lua_Debug ar; if (lua_getstack(L, level, &ar)) { /* check function at level */ lua_getinfo(L, "Sl", &ar); /* get info about it */ if (ar.currentline > 0) { /* is there info? */ lua_pushfstring(L, "%s:%d: ", ar.short_src, ar.currentline); return; } } lua_pushfstring(L, ""); /* else, no information available... */ } /* ** Again, the use of 'lua_pushvfstring' ensures this function does ** not need reserved stack space when called. (At worst, it generates ** an error with "stack overflow" instead of the given message.) */ LUALIB_API int luaL_error (lua_State *L, const char *fmt, ...) { va_list argp; va_start(argp, fmt); luaL_where(L, 1); lua_pushvfstring(L, fmt, argp); va_end(argp); lua_concat(L, 2); return lua_error(L); } LUALIB_API int luaL_fileresult (lua_State *L, int stat, const char *fname) { int en = errno; /* calls to Lua API may change this value */ if (stat) { lua_pushboolean(L, 1); return 1; } else { luaL_pushfail(L); if (fname) lua_pushfstring(L, "%s: %s", fname, strerror(en)); else lua_pushstring(L, strerror(en)); lua_pushinteger(L, en); return 3; } } #if !defined(l_inspectstat) /* { */ #if defined(LUA_USE_POSIX) #include /* ** use appropriate macros to interpret 'pclose' return status */ #define l_inspectstat(stat,what) \ if (WIFEXITED(stat)) { stat = WEXITSTATUS(stat); } \ else if (WIFSIGNALED(stat)) { stat = WTERMSIG(stat); what = "signal"; } #else #define l_inspectstat(stat,what) /* no op */ #endif #endif /* } */ LUALIB_API int luaL_execresult (lua_State *L, int stat) { if (stat != 0 && errno != 0) /* error with an 'errno'? */ return luaL_fileresult(L, 0, NULL); else { const char *what = "exit"; /* type of termination */ l_inspectstat(stat, what); /* interpret result */ if (*what == 'e' && stat == 0) /* successful termination? */ lua_pushboolean(L, 1); else luaL_pushfail(L); lua_pushstring(L, what); lua_pushinteger(L, stat); return 3; /* return true/fail,what,code */ } } /* }====================================================== */ /* ** {====================================================== ** Userdata's metatable manipulation ** ======================================================= */ LUALIB_API int luaL_newmetatable (lua_State *L, const char *tname) { if (luaL_getmetatable(L, tname) != LUA_TNIL) /* name already in use? */ return 0; /* leave previous value on top, but return 0 */ lua_pop(L, 1); lua_createtable(L, 0, 2); /* create metatable */ lua_pushstring(L, tname); lua_setfield(L, -2, "__name"); /* metatable.__name = tname */ lua_pushvalue(L, -1); lua_setfield(L, LUA_REGISTRYINDEX, tname); /* registry.name = metatable */ return 1; } LUALIB_API void luaL_setmetatable (lua_State *L, const char *tname) { luaL_getmetatable(L, tname); lua_setmetatable(L, -2); } LUALIB_API void *luaL_testudata (lua_State *L, int ud, const char *tname) { void *p = lua_touserdata(L, ud); if (p != NULL) { /* value is a userdata? */ if (lua_getmetatable(L, ud)) { /* does it have a metatable? */ luaL_getmetatable(L, tname); /* get correct metatable */ if (!lua_rawequal(L, -1, -2)) /* not the same? */ p = NULL; /* value is a userdata with wrong metatable */ lua_pop(L, 2); /* remove both metatables */ return p; } } return NULL; /* value is not a userdata with a metatable */ } LUALIB_API void *luaL_checkudata (lua_State *L, int ud, const char *tname) { void *p = luaL_testudata(L, ud, tname); luaL_argexpected(L, p != NULL, ud, tname); return p; } /* }====================================================== */ /* ** {====================================================== ** Argument check functions ** ======================================================= */ LUALIB_API int luaL_checkoption (lua_State *L, int arg, const char *def, const char *const lst[]) { const char *name = (def) ? luaL_optstring(L, arg, def) : luaL_checkstring(L, arg); int i; for (i=0; lst[i]; i++) if (strcmp(lst[i], name) == 0) return i; return luaL_argerror(L, arg, lua_pushfstring(L, "invalid option '%s'", name)); } /* ** Ensures the stack has at least 'space' extra slots, raising an error ** if it cannot fulfill the request. (The error handling needs a few ** extra slots to format the error message. In case of an error without ** this extra space, Lua will generate the same 'stack overflow' error, ** but without 'msg'.) */ LUALIB_API void luaL_checkstack (lua_State *L, int space, const char *msg) { if (l_unlikely(!lua_checkstack(L, space))) { if (msg) luaL_error(L, "stack overflow (%s)", msg); else luaL_error(L, "stack overflow"); } } LUALIB_API void luaL_checktype (lua_State *L, int arg, int t) { if (l_unlikely(lua_type(L, arg) != t)) tag_error(L, arg, t); } LUALIB_API void luaL_checkany (lua_State *L, int arg) { if (l_unlikely(lua_type(L, arg) == LUA_TNONE)) luaL_argerror(L, arg, "value expected"); } LUALIB_API const char *luaL_checklstring (lua_State *L, int arg, size_t *len) { const char *s = lua_tolstring(L, arg, len); if (l_unlikely(!s)) tag_error(L, arg, LUA_TSTRING); return s; } LUALIB_API const char *luaL_optlstring (lua_State *L, int arg, const char *def, size_t *len) { if (lua_isnoneornil(L, arg)) { if (len) *len = (def ? strlen(def) : 0); return def; } else return luaL_checklstring(L, arg, len); } LUALIB_API lua_Number luaL_checknumber (lua_State *L, int arg) { int isnum; lua_Number d = lua_tonumberx(L, arg, &isnum); if (l_unlikely(!isnum)) tag_error(L, arg, LUA_TNUMBER); return d; } LUALIB_API lua_Number luaL_optnumber (lua_State *L, int arg, lua_Number def) { return luaL_opt(L, luaL_checknumber, arg, def); } static void interror (lua_State *L, int arg) { if (lua_isnumber(L, arg)) luaL_argerror(L, arg, "number has no integer representation"); else tag_error(L, arg, LUA_TNUMBER); } LUALIB_API lua_Integer luaL_checkinteger (lua_State *L, int arg) { int isnum; lua_Integer d = lua_tointegerx(L, arg, &isnum); if (l_unlikely(!isnum)) { interror(L, arg); } return d; } LUALIB_API lua_Integer luaL_optinteger (lua_State *L, int arg, lua_Integer def) { return luaL_opt(L, luaL_checkinteger, arg, def); } /* }====================================================== */ /* ** {====================================================== ** Generic Buffer manipulation ** ======================================================= */ /* userdata to box arbitrary data */ typedef struct UBox { void *box; size_t bsize; } UBox; static void *resizebox (lua_State *L, int idx, size_t newsize) { void *ud; lua_Alloc allocf = lua_getallocf(L, &ud); UBox *box = (UBox *)lua_touserdata(L, idx); void *temp = allocf(ud, box->box, box->bsize, newsize); if (l_unlikely(temp == NULL && newsize > 0)) { /* allocation error? */ lua_pushliteral(L, "not enough memory"); lua_error(L); /* raise a memory error */ } box->box = temp; box->bsize = newsize; return temp; } static int boxgc (lua_State *L) { resizebox(L, 1, 0); return 0; } static const luaL_Reg boxmt[] = { /* box metamethods */ {"__gc", boxgc}, {"__close", boxgc}, {NULL, NULL} }; static void newbox (lua_State *L) { UBox *box = (UBox *)lua_newuserdatauv(L, sizeof(UBox), 0); box->box = NULL; box->bsize = 0; if (luaL_newmetatable(L, "_UBOX*")) /* creating metatable? */ luaL_setfuncs(L, boxmt, 0); /* set its metamethods */ lua_setmetatable(L, -2); } /* ** check whether buffer is using a userdata on the stack as a temporary ** buffer */ #define buffonstack(B) ((B)->b != (B)->init.b) /* ** Whenever buffer is accessed, slot 'idx' must either be a box (which ** cannot be NULL) or it is a placeholder for the buffer. */ #define checkbufferlevel(B,idx) \ lua_assert(buffonstack(B) ? lua_touserdata(B->L, idx) != NULL \ : lua_touserdata(B->L, idx) == (void*)B) /* ** Compute new size for buffer 'B', enough to accommodate extra 'sz' ** bytes. */ static size_t newbuffsize (luaL_Buffer *B, size_t sz) { size_t newsize = B->size * 2; /* double buffer size */ if (l_unlikely(MAX_SIZET - sz < B->n)) /* overflow in (B->n + sz)? */ return luaL_error(B->L, "buffer too large"); if (newsize < B->n + sz) /* double is not big enough? */ newsize = B->n + sz; return newsize; } /* ** Returns a pointer to a free area with at least 'sz' bytes in buffer ** 'B'. 'boxidx' is the relative position in the stack where is the ** buffer's box or its placeholder. */ static char *prepbuffsize (luaL_Buffer *B, size_t sz, int boxidx) { checkbufferlevel(B, boxidx); if (B->size - B->n >= sz) /* enough space? */ return B->b + B->n; else { lua_State *L = B->L; char *newbuff; size_t newsize = newbuffsize(B, sz); /* create larger buffer */ if (buffonstack(B)) /* buffer already has a box? */ newbuff = (char *)resizebox(L, boxidx, newsize); /* resize it */ else { /* no box yet */ lua_remove(L, boxidx); /* remove placeholder */ newbox(L); /* create a new box */ lua_insert(L, boxidx); /* move box to its intended position */ lua_toclose(L, boxidx); newbuff = (char *)resizebox(L, boxidx, newsize); memcpy(newbuff, B->b, B->n * sizeof(char)); /* copy original content */ } B->b = newbuff; B->size = newsize; return newbuff + B->n; } } /* ** returns a pointer to a free area with at least 'sz' bytes */ LUALIB_API char *luaL_prepbuffsize (luaL_Buffer *B, size_t sz) { return prepbuffsize(B, sz, -1); } LUALIB_API void luaL_addlstring (luaL_Buffer *B, const char *s, size_t l) { if (l > 0) { /* avoid 'memcpy' when 's' can be NULL */ char *b = prepbuffsize(B, l, -1); memcpy(b, s, l * sizeof(char)); luaL_addsize(B, l); } } LUALIB_API void luaL_addstring (luaL_Buffer *B, const char *s) { luaL_addlstring(B, s, strlen(s)); } LUALIB_API void luaL_pushresult (luaL_Buffer *B) { lua_State *L = B->L; checkbufferlevel(B, -1); lua_pushlstring(L, B->b, B->n); if (buffonstack(B)) lua_closeslot(L, -2); /* close the box */ lua_remove(L, -2); /* remove box or placeholder from the stack */ } LUALIB_API void luaL_pushresultsize (luaL_Buffer *B, size_t sz) { luaL_addsize(B, sz); luaL_pushresult(B); } /* ** 'luaL_addvalue' is the only function in the Buffer system where the ** box (if existent) is not on the top of the stack. So, instead of ** calling 'luaL_addlstring', it replicates the code using -2 as the ** last argument to 'prepbuffsize', signaling that the box is (or will ** be) bellow the string being added to the buffer. (Box creation can ** trigger an emergency GC, so we should not remove the string from the ** stack before we have the space guaranteed.) */ LUALIB_API void luaL_addvalue (luaL_Buffer *B) { lua_State *L = B->L; size_t len; const char *s = lua_tolstring(L, -1, &len); char *b = prepbuffsize(B, len, -2); memcpy(b, s, len * sizeof(char)); luaL_addsize(B, len); lua_pop(L, 1); /* pop string */ } LUALIB_API void luaL_buffinit (lua_State *L, luaL_Buffer *B) { B->L = L; B->b = B->init.b; B->n = 0; B->size = LUAL_BUFFERSIZE; lua_pushlightuserdata(L, (void*)B); /* push placeholder */ } LUALIB_API char *luaL_buffinitsize (lua_State *L, luaL_Buffer *B, size_t sz) { luaL_buffinit(L, B); return prepbuffsize(B, sz, -1); } /* }====================================================== */ /* ** {====================================================== ** Reference system ** ======================================================= */ /* index of free-list header (after the predefined values) */ #define freelist (LUA_RIDX_LAST + 1) /* ** The previously freed references form a linked list: ** t[freelist] is the index of a first free index, or zero if list is ** empty; t[t[freelist]] is the index of the second element; etc. */ LUALIB_API int luaL_ref (lua_State *L, int t) { int ref; if (lua_isnil(L, -1)) { lua_pop(L, 1); /* remove from stack */ return LUA_REFNIL; /* 'nil' has a unique fixed reference */ } t = lua_absindex(L, t); if (lua_rawgeti(L, t, freelist) == LUA_TNIL) { /* first access? */ ref = 0; /* list is empty */ lua_pushinteger(L, 0); /* initialize as an empty list */ lua_rawseti(L, t, freelist); /* ref = t[freelist] = 0 */ } else { /* already initialized */ lua_assert(lua_isinteger(L, -1)); ref = (int)lua_tointeger(L, -1); /* ref = t[freelist] */ } lua_pop(L, 1); /* remove element from stack */ if (ref != 0) { /* any free element? */ lua_rawgeti(L, t, ref); /* remove it from list */ lua_rawseti(L, t, freelist); /* (t[freelist] = t[ref]) */ } else /* no free elements */ ref = (int)lua_rawlen(L, t) + 1; /* get a new reference */ lua_rawseti(L, t, ref); return ref; } LUALIB_API void luaL_unref (lua_State *L, int t, int ref) { if (ref >= 0) { t = lua_absindex(L, t); lua_rawgeti(L, t, freelist); lua_assert(lua_isinteger(L, -1)); lua_rawseti(L, t, ref); /* t[ref] = t[freelist] */ lua_pushinteger(L, ref); lua_rawseti(L, t, freelist); /* t[freelist] = ref */ } } /* }====================================================== */ /* ** {====================================================== ** Load functions ** ======================================================= */ typedef struct LoadF { int n; /* number of pre-read characters */ FILE *f; /* file being read */ char buff[BUFSIZ]; /* area for reading file */ } LoadF; static const char *getF (lua_State *L, void *ud, size_t *size) { LoadF *lf = (LoadF *)ud; (void)L; /* not used */ if (lf->n > 0) { /* are there pre-read characters to be read? */ *size = lf->n; /* return them (chars already in buffer) */ lf->n = 0; /* no more pre-read characters */ } else { /* read a block from file */ /* 'fread' can return > 0 *and* set the EOF flag. If next call to 'getF' called 'fread', it might still wait for user input. The next check avoids this problem. */ if (feof(lf->f)) return NULL; *size = fread(lf->buff, 1, sizeof(lf->buff), lf->f); /* read block */ } return lf->buff; } static int errfile (lua_State *L, const char *what, int fnameindex) { const char *serr = strerror(errno); const char *filename = lua_tostring(L, fnameindex) + 1; lua_pushfstring(L, "cannot %s %s: %s", what, filename, serr); lua_remove(L, fnameindex); return LUA_ERRFILE; } static int skipBOM (LoadF *lf) { const char *p = "\xEF\xBB\xBF"; /* UTF-8 BOM mark */ int c; lf->n = 0; do { c = getc(lf->f); if (c == EOF || c != *(const unsigned char *)p++) return c; lf->buff[lf->n++] = c; /* to be read by the parser */ } while (*p != '\0'); lf->n = 0; /* prefix matched; discard it */ return getc(lf->f); /* return next character */ } /* ** reads the first character of file 'f' and skips an optional BOM mark ** in its beginning plus its first line if it starts with '#'. Returns ** true if it skipped the first line. In any case, '*cp' has the ** first "valid" character of the file (after the optional BOM and ** a first-line comment). */ static int skipcomment (LoadF *lf, int *cp) { int c = *cp = skipBOM(lf); if (c == '#') { /* first line is a comment (Unix exec. file)? */ do { /* skip first line */ c = getc(lf->f); } while (c != EOF && c != '\n'); *cp = getc(lf->f); /* skip end-of-line, if present */ return 1; /* there was a comment */ } else return 0; /* no comment */ } LUALIB_API int luaL_loadfilex (lua_State *L, const char *filename, const char *mode) { LoadF lf; int status, readstatus; int c; int fnameindex = lua_gettop(L) + 1; /* index of filename on the stack */ if (filename == NULL) { lua_pushliteral(L, "=stdin"); lf.f = stdin; } else { lua_pushfstring(L, "@%s", filename); lf.f = fopen(filename, "r"); if (lf.f == NULL) return errfile(L, "open", fnameindex); } if (skipcomment(&lf, &c)) /* read initial portion */ lf.buff[lf.n++] = '\n'; /* add line to correct line numbers */ if (c == LUA_SIGNATURE[0] && filename) { /* binary file? */ lf.f = freopen(filename, "rb", lf.f); /* reopen in binary mode */ if (lf.f == NULL) return errfile(L, "reopen", fnameindex); skipcomment(&lf, &c); /* re-read initial portion */ } if (c != EOF) lf.buff[lf.n++] = c; /* 'c' is the first character of the stream */ status = lua_load(L, getF, &lf, lua_tostring(L, -1), mode); readstatus = ferror(lf.f); if (filename) fclose(lf.f); /* close file (even in case of errors) */ if (readstatus) { lua_settop(L, fnameindex); /* ignore results from 'lua_load' */ return errfile(L, "read", fnameindex); } lua_remove(L, fnameindex); return status; } typedef struct LoadS { const char *s; size_t size; } LoadS; static const char *getS (lua_State *L, void *ud, size_t *size) { LoadS *ls = (LoadS *)ud; (void)L; /* not used */ if (ls->size == 0) return NULL; *size = ls->size; ls->size = 0; return ls->s; } LUALIB_API int luaL_loadbufferx (lua_State *L, const char *buff, size_t size, const char *name, const char *mode) { LoadS ls; ls.s = buff; ls.size = size; return lua_load(L, getS, &ls, name, mode); } LUALIB_API int luaL_loadstring (lua_State *L, const char *s) { return luaL_loadbuffer(L, s, strlen(s), s); } /* }====================================================== */ LUALIB_API int luaL_getmetafield (lua_State *L, int obj, const char *event) { if (!lua_getmetatable(L, obj)) /* no metatable? */ return LUA_TNIL; else { int tt; lua_pushstring(L, event); tt = lua_rawget(L, -2); if (tt == LUA_TNIL) /* is metafield nil? */ lua_pop(L, 2); /* remove metatable and metafield */ else lua_remove(L, -2); /* remove only metatable */ return tt; /* return metafield type */ } } LUALIB_API int luaL_callmeta (lua_State *L, int obj, const char *event) { obj = lua_absindex(L, obj); if (luaL_getmetafield(L, obj, event) == LUA_TNIL) /* no metafield? */ return 0; lua_pushvalue(L, obj); lua_call(L, 1, 1); return 1; } LUALIB_API lua_Integer luaL_len (lua_State *L, int idx) { lua_Integer l; int isnum; lua_len(L, idx); l = lua_tointegerx(L, -1, &isnum); if (l_unlikely(!isnum)) luaL_error(L, "object length is not an integer"); lua_pop(L, 1); /* remove object */ return l; } LUALIB_API const char *luaL_tolstring (lua_State *L, int idx, size_t *len) { idx = lua_absindex(L,idx); if (luaL_callmeta(L, idx, "__tostring")) { /* metafield? */ if (!lua_isstring(L, -1)) luaL_error(L, "'__tostring' must return a string"); } else { switch (lua_type(L, idx)) { case LUA_TNUMBER: { if (lua_isinteger(L, idx)) lua_pushfstring(L, "%I", (LUAI_UACINT)lua_tointeger(L, idx)); else lua_pushfstring(L, "%f", (LUAI_UACNUMBER)lua_tonumber(L, idx)); break; } case LUA_TSTRING: lua_pushvalue(L, idx); break; case LUA_TBOOLEAN: lua_pushstring(L, (lua_toboolean(L, idx) ? "true" : "false")); break; case LUA_TNIL: lua_pushliteral(L, "nil"); break; default: { int tt = luaL_getmetafield(L, idx, "__name"); /* try name */ const char *kind = (tt == LUA_TSTRING) ? lua_tostring(L, -1) : luaL_typename(L, idx); lua_pushfstring(L, "%s: %p", kind, lua_topointer(L, idx)); if (tt != LUA_TNIL) lua_remove(L, -2); /* remove '__name' */ break; } } } return lua_tolstring(L, -1, len); } /* ** set functions from list 'l' into table at top - 'nup'; each ** function gets the 'nup' elements at the top as upvalues. ** Returns with only the table at the stack. */ LUALIB_API void luaL_setfuncs (lua_State *L, const luaL_Reg *l, int nup) { luaL_checkstack(L, nup, "too many upvalues"); for (; l->name != NULL; l++) { /* fill the table with given functions */ if (l->func == NULL) /* place holder? */ lua_pushboolean(L, 0); else { int i; for (i = 0; i < nup; i++) /* copy upvalues to the top */ lua_pushvalue(L, -nup); lua_pushcclosure(L, l->func, nup); /* closure with those upvalues */ } lua_setfield(L, -(nup + 2), l->name); } lua_pop(L, nup); /* remove upvalues */ } /* ** ensure that stack[idx][fname] has a table and push that table ** into the stack */ LUALIB_API int luaL_getsubtable (lua_State *L, int idx, const char *fname) { if (lua_getfield(L, idx, fname) == LUA_TTABLE) return 1; /* table already there */ else { lua_pop(L, 1); /* remove previous result */ idx = lua_absindex(L, idx); lua_newtable(L); lua_pushvalue(L, -1); /* copy to be left at top */ lua_setfield(L, idx, fname); /* assign new table to field */ return 0; /* false, because did not find table there */ } } /* ** Stripped-down 'require': After checking "loaded" table, calls 'openf' ** to open a module, registers the result in 'package.loaded' table and, ** if 'glb' is true, also registers the result in the global table. ** Leaves resulting module on the top. */ LUALIB_API void luaL_requiref (lua_State *L, const char *modname, lua_CFunction openf, int glb) { luaL_getsubtable(L, LUA_REGISTRYINDEX, LUA_LOADED_TABLE); lua_getfield(L, -1, modname); /* LOADED[modname] */ if (!lua_toboolean(L, -1)) { /* package not already loaded? */ lua_pop(L, 1); /* remove field */ lua_pushcfunction(L, openf); lua_pushstring(L, modname); /* argument to open function */ lua_call(L, 1, 1); /* call 'openf' to open module */ lua_pushvalue(L, -1); /* make copy of module (call result) */ lua_setfield(L, -3, modname); /* LOADED[modname] = module */ } lua_remove(L, -2); /* remove LOADED table */ if (glb) { lua_pushvalue(L, -1); /* copy of module */ lua_setglobal(L, modname); /* _G[modname] = module */ } } LUALIB_API void luaL_addgsub (luaL_Buffer *b, const char *s, const char *p, const char *r) { const char *wild; size_t l = strlen(p); while ((wild = strstr(s, p)) != NULL) { luaL_addlstring(b, s, wild - s); /* push prefix */ luaL_addstring(b, r); /* push replacement in place of pattern */ s = wild + l; /* continue after 'p' */ } luaL_addstring(b, s); /* push last suffix */ } LUALIB_API const char *luaL_gsub (lua_State *L, const char *s, const char *p, const char *r) { luaL_Buffer b; luaL_buffinit(L, &b); luaL_addgsub(&b, s, p, r); luaL_pushresult(&b); return lua_tostring(L, -1); } static void *l_alloc (void *ud, void *ptr, size_t osize, size_t nsize) { (void)ud; (void)osize; /* not used */ if (nsize == 0) { free(ptr); return NULL; } else return realloc(ptr, nsize); } static int panic (lua_State *L) { const char *msg = lua_tostring(L, -1); if (msg == NULL) msg = "error object is not a string"; lua_writestringerror("PANIC: unprotected error in call to Lua API (%s)\n", msg); return 0; /* return to Lua to abort */ } /* ** Warning functions: ** warnfoff: warning system is off ** warnfon: ready to start a new message ** warnfcont: previous message is to be continued */ static void warnfoff (void *ud, const char *message, int tocont); static void warnfon (void *ud, const char *message, int tocont); static void warnfcont (void *ud, const char *message, int tocont); /* ** Check whether message is a control message. If so, execute the ** control or ignore it if unknown. */ static int checkcontrol (lua_State *L, const char *message, int tocont) { if (tocont || *(message++) != '@') /* not a control message? */ return 0; else { if (strcmp(message, "off") == 0) lua_setwarnf(L, warnfoff, L); /* turn warnings off */ else if (strcmp(message, "on") == 0) lua_setwarnf(L, warnfon, L); /* turn warnings on */ return 1; /* it was a control message */ } } static void warnfoff (void *ud, const char *message, int tocont) { checkcontrol((lua_State *)ud, message, tocont); } /* ** Writes the message and handle 'tocont', finishing the message ** if needed and setting the next warn function. */ static void warnfcont (void *ud, const char *message, int tocont) { lua_State *L = (lua_State *)ud; lua_writestringerror("%s", message); /* write message */ if (tocont) /* not the last part? */ lua_setwarnf(L, warnfcont, L); /* to be continued */ else { /* last part */ lua_writestringerror("%s", "\n"); /* finish message with end-of-line */ lua_setwarnf(L, warnfon, L); /* next call is a new message */ } } static void warnfon (void *ud, const char *message, int tocont) { if (checkcontrol((lua_State *)ud, message, tocont)) /* control message? */ return; /* nothing else to be done */ lua_writestringerror("%s", "Lua warning: "); /* start a new warning */ warnfcont(ud, message, tocont); /* finish processing */ } LUALIB_API lua_State *luaL_newstate (void) { lua_State *L = lua_newstate(l_alloc, NULL); if (l_likely(L)) { lua_atpanic(L, &panic); lua_setwarnf(L, warnfoff, L); /* default is warnings off */ } return L; } LUALIB_API void luaL_checkversion_ (lua_State *L, lua_Number ver, size_t sz) { lua_Number v = lua_version(L); if (sz != LUAL_NUMSIZES) /* check numeric types */ luaL_error(L, "core and library have incompatible numeric types"); else if (v != ver) luaL_error(L, "version mismatch: app. needs %f, Lua core provides %f", (LUAI_UACNUMBER)ver, (LUAI_UACNUMBER)v); } /* ** $Id: lbaselib.c $ ** Basic library ** See Copyright Notice in lua.h */ #define lbaselib_c #define LUA_LIB /*#include "lprefix.h"*/ #include #include #include #include /*#include "lua.h"*/ /*#include "lauxlib.h"*/ /*#include "lualib.h"*/ static int luaB_print (lua_State *L) { int n = lua_gettop(L); /* number of arguments */ int i; for (i = 1; i <= n; i++) { /* for each argument */ size_t l; const char *s = luaL_tolstring(L, i, &l); /* convert it to string */ if (i > 1) /* not the first element? */ lua_writestring("\t", 1); /* add a tab before it */ lua_writestring(s, l); /* print it */ lua_pop(L, 1); /* pop result */ } lua_writeline(); return 0; } /* ** Creates a warning with all given arguments. ** Check first for errors; otherwise an error may interrupt ** the composition of a warning, leaving it unfinished. */ static int luaB_warn (lua_State *L) { int n = lua_gettop(L); /* number of arguments */ int i; luaL_checkstring(L, 1); /* at least one argument */ for (i = 2; i <= n; i++) luaL_checkstring(L, i); /* make sure all arguments are strings */ for (i = 1; i < n; i++) /* compose warning */ lua_warning(L, lua_tostring(L, i), 1); lua_warning(L, lua_tostring(L, n), 0); /* close warning */ return 0; } #define SPACECHARS " \f\n\r\t\v" static const char *b_str2int (const char *s, int base, lua_Integer *pn) { lua_Unsigned n = 0; int neg = 0; s += strspn(s, SPACECHARS); /* skip initial spaces */ if (*s == '-') { s++; neg = 1; } /* handle sign */ else if (*s == '+') s++; if (!isalnum((unsigned char)*s)) /* no digit? */ return NULL; do { int digit = (isdigit((unsigned char)*s)) ? *s - '0' : (toupper((unsigned char)*s) - 'A') + 10; if (digit >= base) return NULL; /* invalid numeral */ n = n * base + digit; s++; } while (isalnum((unsigned char)*s)); s += strspn(s, SPACECHARS); /* skip trailing spaces */ *pn = (lua_Integer)((neg) ? (0u - n) : n); return s; } static int luaB_tonumber (lua_State *L) { if (lua_isnoneornil(L, 2)) { /* standard conversion? */ if (lua_type(L, 1) == LUA_TNUMBER) { /* already a number? */ lua_settop(L, 1); /* yes; return it */ return 1; } else { size_t l; const char *s = lua_tolstring(L, 1, &l); if (s != NULL && lua_stringtonumber(L, s) == l + 1) return 1; /* successful conversion to number */ /* else not a number */ luaL_checkany(L, 1); /* (but there must be some parameter) */ } } else { size_t l; const char *s; lua_Integer n = 0; /* to avoid warnings */ lua_Integer base = luaL_checkinteger(L, 2); luaL_checktype(L, 1, LUA_TSTRING); /* no numbers as strings */ s = lua_tolstring(L, 1, &l); luaL_argcheck(L, 2 <= base && base <= 36, 2, "base out of range"); if (b_str2int(s, (int)base, &n) == s + l) { lua_pushinteger(L, n); return 1; } /* else not a number */ } /* else not a number */ luaL_pushfail(L); /* not a number */ return 1; } static int luaB_error (lua_State *L) { int level = (int)luaL_optinteger(L, 2, 1); lua_settop(L, 1); if (lua_type(L, 1) == LUA_TSTRING && level > 0) { luaL_where(L, level); /* add extra information */ lua_pushvalue(L, 1); lua_concat(L, 2); } return lua_error(L); } static int luaB_getmetatable (lua_State *L) { luaL_checkany(L, 1); if (!lua_getmetatable(L, 1)) { lua_pushnil(L); return 1; /* no metatable */ } luaL_getmetafield(L, 1, "__metatable"); return 1; /* returns either __metatable field (if present) or metatable */ } static int luaB_setmetatable (lua_State *L) { int t = lua_type(L, 2); luaL_checktype(L, 1, LUA_TTABLE); luaL_argexpected(L, t == LUA_TNIL || t == LUA_TTABLE, 2, "nil or table"); if (l_unlikely(luaL_getmetafield(L, 1, "__metatable") != LUA_TNIL)) return luaL_error(L, "cannot change a protected metatable"); lua_settop(L, 2); lua_setmetatable(L, 1); return 1; } static int luaB_rawequal (lua_State *L) { luaL_checkany(L, 1); luaL_checkany(L, 2); lua_pushboolean(L, lua_rawequal(L, 1, 2)); return 1; } static int luaB_rawlen (lua_State *L) { int t = lua_type(L, 1); luaL_argexpected(L, t == LUA_TTABLE || t == LUA_TSTRING, 1, "table or string"); lua_pushinteger(L, lua_rawlen(L, 1)); return 1; } static int luaB_rawget (lua_State *L) { luaL_checktype(L, 1, LUA_TTABLE); luaL_checkany(L, 2); lua_settop(L, 2); lua_rawget(L, 1); return 1; } static int luaB_rawset (lua_State *L) { luaL_checktype(L, 1, LUA_TTABLE); luaL_checkany(L, 2); luaL_checkany(L, 3); lua_settop(L, 3); lua_rawset(L, 1); return 1; } static int pushmode (lua_State *L, int oldmode) { if (oldmode == -1) luaL_pushfail(L); /* invalid call to 'lua_gc' */ else lua_pushstring(L, (oldmode == LUA_GCINC) ? "incremental" : "generational"); return 1; } /* ** check whether call to 'lua_gc' was valid (not inside a finalizer) */ #define checkvalres(res) { if (res == -1) break; } static int luaB_collectgarbage (lua_State *L) { static const char *const opts[] = {"stop", "restart", "collect", "count", "step", "setpause", "setstepmul", "isrunning", "generational", "incremental", NULL}; static const int optsnum[] = {LUA_GCSTOP, LUA_GCRESTART, LUA_GCCOLLECT, LUA_GCCOUNT, LUA_GCSTEP, LUA_GCSETPAUSE, LUA_GCSETSTEPMUL, LUA_GCISRUNNING, LUA_GCGEN, LUA_GCINC}; int o = optsnum[luaL_checkoption(L, 1, "collect", opts)]; switch (o) { case LUA_GCCOUNT: { int k = lua_gc(L, o); int b = lua_gc(L, LUA_GCCOUNTB); checkvalres(k); lua_pushnumber(L, (lua_Number)k + ((lua_Number)b/1024)); return 1; } case LUA_GCSTEP: { int step = (int)luaL_optinteger(L, 2, 0); int res = lua_gc(L, o, step); checkvalres(res); lua_pushboolean(L, res); return 1; } case LUA_GCSETPAUSE: case LUA_GCSETSTEPMUL: { int p = (int)luaL_optinteger(L, 2, 0); int previous = lua_gc(L, o, p); checkvalres(previous); lua_pushinteger(L, previous); return 1; } case LUA_GCISRUNNING: { int res = lua_gc(L, o); checkvalres(res); lua_pushboolean(L, res); return 1; } case LUA_GCGEN: { int minormul = (int)luaL_optinteger(L, 2, 0); int majormul = (int)luaL_optinteger(L, 3, 0); return pushmode(L, lua_gc(L, o, minormul, majormul)); } case LUA_GCINC: { int pause = (int)luaL_optinteger(L, 2, 0); int stepmul = (int)luaL_optinteger(L, 3, 0); int stepsize = (int)luaL_optinteger(L, 4, 0); return pushmode(L, lua_gc(L, o, pause, stepmul, stepsize)); } default: { int res = lua_gc(L, o); checkvalres(res); lua_pushinteger(L, res); return 1; } } luaL_pushfail(L); /* invalid call (inside a finalizer) */ return 1; } static int luaB_type (lua_State *L) { int t = lua_type(L, 1); luaL_argcheck(L, t != LUA_TNONE, 1, "value expected"); lua_pushstring(L, lua_typename(L, t)); return 1; } static int luaB_next (lua_State *L) { luaL_checktype(L, 1, LUA_TTABLE); lua_settop(L, 2); /* create a 2nd argument if there isn't one */ if (lua_next(L, 1)) return 2; else { lua_pushnil(L); return 1; } } static int pairscont (lua_State *L, int status, lua_KContext k) { (void)L; (void)status; (void)k; /* unused */ return 3; } static int luaB_pairs (lua_State *L) { luaL_checkany(L, 1); if (luaL_getmetafield(L, 1, "__pairs") == LUA_TNIL) { /* no metamethod? */ lua_pushcfunction(L, luaB_next); /* will return generator, */ lua_pushvalue(L, 1); /* state, */ lua_pushnil(L); /* and initial value */ } else { lua_pushvalue(L, 1); /* argument 'self' to metamethod */ lua_callk(L, 1, 3, 0, pairscont); /* get 3 values from metamethod */ } return 3; } /* ** Traversal function for 'ipairs' */ static int ipairsaux (lua_State *L) { lua_Integer i = luaL_checkinteger(L, 2); i = luaL_intop(+, i, 1); lua_pushinteger(L, i); return (lua_geti(L, 1, i) == LUA_TNIL) ? 1 : 2; } /* ** 'ipairs' function. Returns 'ipairsaux', given "table", 0. ** (The given "table" may not be a table.) */ static int luaB_ipairs (lua_State *L) { luaL_checkany(L, 1); lua_pushcfunction(L, ipairsaux); /* iteration function */ lua_pushvalue(L, 1); /* state */ lua_pushinteger(L, 0); /* initial value */ return 3; } static int load_aux (lua_State *L, int status, int envidx) { if (l_likely(status == LUA_OK)) { if (envidx != 0) { /* 'env' parameter? */ lua_pushvalue(L, envidx); /* environment for loaded function */ if (!lua_setupvalue(L, -2, 1)) /* set it as 1st upvalue */ lua_pop(L, 1); /* remove 'env' if not used by previous call */ } return 1; } else { /* error (message is on top of the stack) */ luaL_pushfail(L); lua_insert(L, -2); /* put before error message */ return 2; /* return fail plus error message */ } } static int luaB_loadfile (lua_State *L) { const char *fname = luaL_optstring(L, 1, NULL); const char *mode = luaL_optstring(L, 2, NULL); int env = (!lua_isnone(L, 3) ? 3 : 0); /* 'env' index or 0 if no 'env' */ int status = luaL_loadfilex(L, fname, mode); return load_aux(L, status, env); } /* ** {====================================================== ** Generic Read function ** ======================================================= */ /* ** reserved slot, above all arguments, to hold a copy of the returned ** string to avoid it being collected while parsed. 'load' has four ** optional arguments (chunk, source name, mode, and environment). */ #define RESERVEDSLOT 5 /* ** Reader for generic 'load' function: 'lua_load' uses the ** stack for internal stuff, so the reader cannot change the ** stack top. Instead, it keeps its resulting string in a ** reserved slot inside the stack. */ static const char *generic_reader (lua_State *L, void *ud, size_t *size) { (void)(ud); /* not used */ luaL_checkstack(L, 2, "too many nested functions"); lua_pushvalue(L, 1); /* get function */ lua_call(L, 0, 1); /* call it */ if (lua_isnil(L, -1)) { lua_pop(L, 1); /* pop result */ *size = 0; return NULL; } else if (l_unlikely(!lua_isstring(L, -1))) luaL_error(L, "reader function must return a string"); lua_replace(L, RESERVEDSLOT); /* save string in reserved slot */ return lua_tolstring(L, RESERVEDSLOT, size); } static int luaB_load (lua_State *L) { int status; size_t l; const char *s = lua_tolstring(L, 1, &l); const char *mode = luaL_optstring(L, 3, "bt"); int env = (!lua_isnone(L, 4) ? 4 : 0); /* 'env' index or 0 if no 'env' */ if (s != NULL) { /* loading a string? */ const char *chunkname = luaL_optstring(L, 2, s); status = luaL_loadbufferx(L, s, l, chunkname, mode); } else { /* loading from a reader function */ const char *chunkname = luaL_optstring(L, 2, "=(load)"); luaL_checktype(L, 1, LUA_TFUNCTION); lua_settop(L, RESERVEDSLOT); /* create reserved slot */ status = lua_load(L, generic_reader, NULL, chunkname, mode); } return load_aux(L, status, env); } /* }====================================================== */ static int dofilecont (lua_State *L, int d1, lua_KContext d2) { (void)d1; (void)d2; /* only to match 'lua_Kfunction' prototype */ return lua_gettop(L) - 1; } static int luaB_dofile (lua_State *L) { const char *fname = luaL_optstring(L, 1, NULL); lua_settop(L, 1); if (l_unlikely(luaL_loadfile(L, fname) != LUA_OK)) return lua_error(L); lua_callk(L, 0, LUA_MULTRET, 0, dofilecont); return dofilecont(L, 0, 0); } static int luaB_assert (lua_State *L) { if (l_likely(lua_toboolean(L, 1))) /* condition is true? */ return lua_gettop(L); /* return all arguments */ else { /* error */ luaL_checkany(L, 1); /* there must be a condition */ lua_remove(L, 1); /* remove it */ lua_pushliteral(L, "assertion failed!"); /* default message */ lua_settop(L, 1); /* leave only message (default if no other one) */ return luaB_error(L); /* call 'error' */ } } static int luaB_select (lua_State *L) { int n = lua_gettop(L); if (lua_type(L, 1) == LUA_TSTRING && *lua_tostring(L, 1) == '#') { lua_pushinteger(L, n-1); return 1; } else { lua_Integer i = luaL_checkinteger(L, 1); if (i < 0) i = n + i; else if (i > n) i = n; luaL_argcheck(L, 1 <= i, 1, "index out of range"); return n - (int)i; } } /* ** Continuation function for 'pcall' and 'xpcall'. Both functions ** already pushed a 'true' before doing the call, so in case of success ** 'finishpcall' only has to return everything in the stack minus ** 'extra' values (where 'extra' is exactly the number of items to be ** ignored). */ static int finishpcall (lua_State *L, int status, lua_KContext extra) { if (l_unlikely(status != LUA_OK && status != LUA_YIELD)) { /* error? */ lua_pushboolean(L, 0); /* first result (false) */ lua_pushvalue(L, -2); /* error message */ return 2; /* return false, msg */ } else return lua_gettop(L) - (int)extra; /* return all results */ } static int luaB_pcall (lua_State *L) { int status; luaL_checkany(L, 1); lua_pushboolean(L, 1); /* first result if no errors */ lua_insert(L, 1); /* put it in place */ status = lua_pcallk(L, lua_gettop(L) - 2, LUA_MULTRET, 0, 0, finishpcall); return finishpcall(L, status, 0); } /* ** Do a protected call with error handling. After 'lua_rotate', the ** stack will have ; so, the function passes ** 2 to 'finishpcall' to skip the 2 first values when returning results. */ static int luaB_xpcall (lua_State *L) { int status; int n = lua_gettop(L); luaL_checktype(L, 2, LUA_TFUNCTION); /* check error function */ lua_pushboolean(L, 1); /* first result */ lua_pushvalue(L, 1); /* function */ lua_rotate(L, 3, 2); /* move them below function's arguments */ status = lua_pcallk(L, n - 2, LUA_MULTRET, 2, 2, finishpcall); return finishpcall(L, status, 2); } static int luaB_tostring (lua_State *L) { luaL_checkany(L, 1); luaL_tolstring(L, 1, NULL); return 1; } static const luaL_Reg base_funcs[] = { {"assert", luaB_assert}, {"collectgarbage", luaB_collectgarbage}, {"dofile", luaB_dofile}, {"error", luaB_error}, {"getmetatable", luaB_getmetatable}, {"ipairs", luaB_ipairs}, {"loadfile", luaB_loadfile}, {"load", luaB_load}, {"next", luaB_next}, {"pairs", luaB_pairs}, {"pcall", luaB_pcall}, {"print", luaB_print}, {"warn", luaB_warn}, {"rawequal", luaB_rawequal}, {"rawlen", luaB_rawlen}, {"rawget", luaB_rawget}, {"rawset", luaB_rawset}, {"select", luaB_select}, {"setmetatable", luaB_setmetatable}, {"tonumber", luaB_tonumber}, {"tostring", luaB_tostring}, {"type", luaB_type}, {"xpcall", luaB_xpcall}, /* placeholders */ {LUA_GNAME, NULL}, {"_VERSION", NULL}, {NULL, NULL} }; LUAMOD_API int luaopen_base (lua_State *L) { /* open lib into global table */ lua_pushglobaltable(L); luaL_setfuncs(L, base_funcs, 0); /* set global _G */ lua_pushvalue(L, -1); lua_setfield(L, -2, LUA_GNAME); /* set global _VERSION */ lua_pushliteral(L, LUA_VERSION); lua_setfield(L, -2, "_VERSION"); return 1; } /* ** $Id: lcorolib.c $ ** Coroutine Library ** See Copyright Notice in lua.h */ #define lcorolib_c #define LUA_LIB /*#include "lprefix.h"*/ #include /*#include "lua.h"*/ /*#include "lauxlib.h"*/ /*#include "lualib.h"*/ static lua_State *getco (lua_State *L) { lua_State *co = lua_tothread(L, 1); luaL_argexpected(L, co, 1, "thread"); return co; } /* ** Resumes a coroutine. Returns the number of results for non-error ** cases or -1 for errors. */ static int auxresume (lua_State *L, lua_State *co, int narg) { int status, nres; if (l_unlikely(!lua_checkstack(co, narg))) { lua_pushliteral(L, "too many arguments to resume"); return -1; /* error flag */ } lua_xmove(L, co, narg); status = lua_resume(co, L, narg, &nres); if (l_likely(status == LUA_OK || status == LUA_YIELD)) { if (l_unlikely(!lua_checkstack(L, nres + 1))) { lua_pop(co, nres); /* remove results anyway */ lua_pushliteral(L, "too many results to resume"); return -1; /* error flag */ } lua_xmove(co, L, nres); /* move yielded values */ return nres; } else { lua_xmove(co, L, 1); /* move error message */ return -1; /* error flag */ } } static int luaB_coresume (lua_State *L) { lua_State *co = getco(L); int r; r = auxresume(L, co, lua_gettop(L) - 1); if (l_unlikely(r < 0)) { lua_pushboolean(L, 0); lua_insert(L, -2); return 2; /* return false + error message */ } else { lua_pushboolean(L, 1); lua_insert(L, -(r + 1)); return r + 1; /* return true + 'resume' returns */ } } static int luaB_auxwrap (lua_State *L) { lua_State *co = lua_tothread(L, lua_upvalueindex(1)); int r = auxresume(L, co, lua_gettop(L)); if (l_unlikely(r < 0)) { /* error? */ int stat = lua_status(co); if (stat != LUA_OK && stat != LUA_YIELD) { /* error in the coroutine? */ stat = lua_resetthread(co); /* close its tbc variables */ lua_assert(stat != LUA_OK); lua_xmove(co, L, 1); /* move error message to the caller */ } if (stat != LUA_ERRMEM && /* not a memory error and ... */ lua_type(L, -1) == LUA_TSTRING) { /* ... error object is a string? */ luaL_where(L, 1); /* add extra info, if available */ lua_insert(L, -2); lua_concat(L, 2); } return lua_error(L); /* propagate error */ } return r; } static int luaB_cocreate (lua_State *L) { lua_State *NL; luaL_checktype(L, 1, LUA_TFUNCTION); NL = lua_newthread(L); lua_pushvalue(L, 1); /* move function to top */ lua_xmove(L, NL, 1); /* move function from L to NL */ return 1; } static int luaB_cowrap (lua_State *L) { luaB_cocreate(L); lua_pushcclosure(L, luaB_auxwrap, 1); return 1; } static int luaB_yield (lua_State *L) { return lua_yield(L, lua_gettop(L)); } #define COS_RUN 0 #define COS_DEAD 1 #define COS_YIELD 2 #define COS_NORM 3 static const char *const statname[] = {"running", "dead", "suspended", "normal"}; static int auxstatus (lua_State *L, lua_State *co) { if (L == co) return COS_RUN; else { switch (lua_status(co)) { case LUA_YIELD: return COS_YIELD; case LUA_OK: { lua_Debug ar; if (lua_getstack(co, 0, &ar)) /* does it have frames? */ return COS_NORM; /* it is running */ else if (lua_gettop(co) == 0) return COS_DEAD; else return COS_YIELD; /* initial state */ } default: /* some error occurred */ return COS_DEAD; } } } static int luaB_costatus (lua_State *L) { lua_State *co = getco(L); lua_pushstring(L, statname[auxstatus(L, co)]); return 1; } static int luaB_yieldable (lua_State *L) { lua_State *co = lua_isnone(L, 1) ? L : getco(L); lua_pushboolean(L, lua_isyieldable(co)); return 1; } static int luaB_corunning (lua_State *L) { int ismain = lua_pushthread(L); lua_pushboolean(L, ismain); return 2; } static int luaB_close (lua_State *L) { lua_State *co = getco(L); int status = auxstatus(L, co); switch (status) { case COS_DEAD: case COS_YIELD: { status = lua_resetthread(co); if (status == LUA_OK) { lua_pushboolean(L, 1); return 1; } else { lua_pushboolean(L, 0); lua_xmove(co, L, 1); /* move error message */ return 2; } } default: /* normal or running coroutine */ return luaL_error(L, "cannot close a %s coroutine", statname[status]); } } static const luaL_Reg co_funcs[] = { {"create", luaB_cocreate}, {"resume", luaB_coresume}, {"running", luaB_corunning}, {"status", luaB_costatus}, {"wrap", luaB_cowrap}, {"yield", luaB_yield}, {"isyieldable", luaB_yieldable}, {"close", luaB_close}, {NULL, NULL} }; LUAMOD_API int luaopen_coroutine (lua_State *L) { luaL_newlib(L, co_funcs); return 1; } /* ** $Id: ldblib.c $ ** Interface from Lua to its debug API ** See Copyright Notice in lua.h */ #define ldblib_c #define LUA_LIB /*#include "lprefix.h"*/ #include #include #include /*#include "lua.h"*/ /*#include "lauxlib.h"*/ /*#include "lualib.h"*/ /* ** The hook table at registry[HOOKKEY] maps threads to their current ** hook function. */ static const char *const HOOKKEY = "_HOOKKEY"; /* ** If L1 != L, L1 can be in any state, and therefore there are no ** guarantees about its stack space; any push in L1 must be ** checked. */ static void checkstack (lua_State *L, lua_State *L1, int n) { if (l_unlikely(L != L1 && !lua_checkstack(L1, n))) luaL_error(L, "stack overflow"); } static int db_getregistry (lua_State *L) { lua_pushvalue(L, LUA_REGISTRYINDEX); return 1; } static int db_getmetatable (lua_State *L) { luaL_checkany(L, 1); if (!lua_getmetatable(L, 1)) { lua_pushnil(L); /* no metatable */ } return 1; } static int db_setmetatable (lua_State *L) { int t = lua_type(L, 2); luaL_argexpected(L, t == LUA_TNIL || t == LUA_TTABLE, 2, "nil or table"); lua_settop(L, 2); lua_setmetatable(L, 1); return 1; /* return 1st argument */ } static int db_getuservalue (lua_State *L) { int n = (int)luaL_optinteger(L, 2, 1); if (lua_type(L, 1) != LUA_TUSERDATA) luaL_pushfail(L); else if (lua_getiuservalue(L, 1, n) != LUA_TNONE) { lua_pushboolean(L, 1); return 2; } return 1; } static int db_setuservalue (lua_State *L) { int n = (int)luaL_optinteger(L, 3, 1); luaL_checktype(L, 1, LUA_TUSERDATA); luaL_checkany(L, 2); lua_settop(L, 2); if (!lua_setiuservalue(L, 1, n)) luaL_pushfail(L); return 1; } /* ** Auxiliary function used by several library functions: check for ** an optional thread as function's first argument and set 'arg' with ** 1 if this argument is present (so that functions can skip it to ** access their other arguments) */ static lua_State *getthread (lua_State *L, int *arg) { if (lua_isthread(L, 1)) { *arg = 1; return lua_tothread(L, 1); } else { *arg = 0; return L; /* function will operate over current thread */ } } /* ** Variations of 'lua_settable', used by 'db_getinfo' to put results ** from 'lua_getinfo' into result table. Key is always a string; ** value can be a string, an int, or a boolean. */ static void settabss (lua_State *L, const char *k, const char *v) { lua_pushstring(L, v); lua_setfield(L, -2, k); } static void settabsi (lua_State *L, const char *k, int v) { lua_pushinteger(L, v); lua_setfield(L, -2, k); } static void settabsb (lua_State *L, const char *k, int v) { lua_pushboolean(L, v); lua_setfield(L, -2, k); } /* ** In function 'db_getinfo', the call to 'lua_getinfo' may push ** results on the stack; later it creates the result table to put ** these objects. Function 'treatstackoption' puts the result from ** 'lua_getinfo' on top of the result table so that it can call ** 'lua_setfield'. */ static void treatstackoption (lua_State *L, lua_State *L1, const char *fname) { if (L == L1) lua_rotate(L, -2, 1); /* exchange object and table */ else lua_xmove(L1, L, 1); /* move object to the "main" stack */ lua_setfield(L, -2, fname); /* put object into table */ } /* ** Calls 'lua_getinfo' and collects all results in a new table. ** L1 needs stack space for an optional input (function) plus ** two optional outputs (function and line table) from function ** 'lua_getinfo'. */ static int db_getinfo (lua_State *L) { lua_Debug ar; int arg; lua_State *L1 = getthread(L, &arg); const char *options = luaL_optstring(L, arg+2, "flnSrtu"); checkstack(L, L1, 3); luaL_argcheck(L, options[0] != '>', arg + 2, "invalid option '>'"); if (lua_isfunction(L, arg + 1)) { /* info about a function? */ options = lua_pushfstring(L, ">%s", options); /* add '>' to 'options' */ lua_pushvalue(L, arg + 1); /* move function to 'L1' stack */ lua_xmove(L, L1, 1); } else { /* stack level */ if (!lua_getstack(L1, (int)luaL_checkinteger(L, arg + 1), &ar)) { luaL_pushfail(L); /* level out of range */ return 1; } } if (!lua_getinfo(L1, options, &ar)) return luaL_argerror(L, arg+2, "invalid option"); lua_newtable(L); /* table to collect results */ if (strchr(options, 'S')) { lua_pushlstring(L, ar.source, ar.srclen); lua_setfield(L, -2, "source"); settabss(L, "short_src", ar.short_src); settabsi(L, "linedefined", ar.linedefined); settabsi(L, "lastlinedefined", ar.lastlinedefined); settabss(L, "what", ar.what); } if (strchr(options, 'l')) settabsi(L, "currentline", ar.currentline); if (strchr(options, 'u')) { settabsi(L, "nups", ar.nups); settabsi(L, "nparams", ar.nparams); settabsb(L, "isvararg", ar.isvararg); } if (strchr(options, 'n')) { settabss(L, "name", ar.name); settabss(L, "namewhat", ar.namewhat); } if (strchr(options, 'r')) { settabsi(L, "ftransfer", ar.ftransfer); settabsi(L, "ntransfer", ar.ntransfer); } if (strchr(options, 't')) settabsb(L, "istailcall", ar.istailcall); if (strchr(options, 'L')) treatstackoption(L, L1, "activelines"); if (strchr(options, 'f')) treatstackoption(L, L1, "func"); return 1; /* return table */ } static int db_getlocal (lua_State *L) { int arg; lua_State *L1 = getthread(L, &arg); int nvar = (int)luaL_checkinteger(L, arg + 2); /* local-variable index */ if (lua_isfunction(L, arg + 1)) { /* function argument? */ lua_pushvalue(L, arg + 1); /* push function */ lua_pushstring(L, lua_getlocal(L, NULL, nvar)); /* push local name */ return 1; /* return only name (there is no value) */ } else { /* stack-level argument */ lua_Debug ar; const char *name; int level = (int)luaL_checkinteger(L, arg + 1); if (l_unlikely(!lua_getstack(L1, level, &ar))) /* out of range? */ return luaL_argerror(L, arg+1, "level out of range"); checkstack(L, L1, 1); name = lua_getlocal(L1, &ar, nvar); if (name) { lua_xmove(L1, L, 1); /* move local value */ lua_pushstring(L, name); /* push name */ lua_rotate(L, -2, 1); /* re-order */ return 2; } else { luaL_pushfail(L); /* no name (nor value) */ return 1; } } } static int db_setlocal (lua_State *L) { int arg; const char *name; lua_State *L1 = getthread(L, &arg); lua_Debug ar; int level = (int)luaL_checkinteger(L, arg + 1); int nvar = (int)luaL_checkinteger(L, arg + 2); if (l_unlikely(!lua_getstack(L1, level, &ar))) /* out of range? */ return luaL_argerror(L, arg+1, "level out of range"); luaL_checkany(L, arg+3); lua_settop(L, arg+3); checkstack(L, L1, 1); lua_xmove(L, L1, 1); name = lua_setlocal(L1, &ar, nvar); if (name == NULL) lua_pop(L1, 1); /* pop value (if not popped by 'lua_setlocal') */ lua_pushstring(L, name); return 1; } /* ** get (if 'get' is true) or set an upvalue from a closure */ static int auxupvalue (lua_State *L, int get) { const char *name; int n = (int)luaL_checkinteger(L, 2); /* upvalue index */ luaL_checktype(L, 1, LUA_TFUNCTION); /* closure */ name = get ? lua_getupvalue(L, 1, n) : lua_setupvalue(L, 1, n); if (name == NULL) return 0; lua_pushstring(L, name); lua_insert(L, -(get+1)); /* no-op if get is false */ return get + 1; } static int db_getupvalue (lua_State *L) { return auxupvalue(L, 1); } static int db_setupvalue (lua_State *L) { luaL_checkany(L, 3); return auxupvalue(L, 0); } /* ** Check whether a given upvalue from a given closure exists and ** returns its index */ static void *checkupval (lua_State *L, int argf, int argnup, int *pnup) { void *id; int nup = (int)luaL_checkinteger(L, argnup); /* upvalue index */ luaL_checktype(L, argf, LUA_TFUNCTION); /* closure */ id = lua_upvalueid(L, argf, nup); if (pnup) { luaL_argcheck(L, id != NULL, argnup, "invalid upvalue index"); *pnup = nup; } return id; } static int db_upvalueid (lua_State *L) { void *id = checkupval(L, 1, 2, NULL); if (id != NULL) lua_pushlightuserdata(L, id); else luaL_pushfail(L); return 1; } static int db_upvaluejoin (lua_State *L) { int n1, n2; checkupval(L, 1, 2, &n1); checkupval(L, 3, 4, &n2); luaL_argcheck(L, !lua_iscfunction(L, 1), 1, "Lua function expected"); luaL_argcheck(L, !lua_iscfunction(L, 3), 3, "Lua function expected"); lua_upvaluejoin(L, 1, n1, 3, n2); return 0; } /* ** Call hook function registered at hook table for the current ** thread (if there is one) */ static void hookf (lua_State *L, lua_Debug *ar) { static const char *const hooknames[] = {"call", "return", "line", "count", "tail call"}; lua_getfield(L, LUA_REGISTRYINDEX, HOOKKEY); lua_pushthread(L); if (lua_rawget(L, -2) == LUA_TFUNCTION) { /* is there a hook function? */ lua_pushstring(L, hooknames[(int)ar->event]); /* push event name */ if (ar->currentline >= 0) lua_pushinteger(L, ar->currentline); /* push current line */ else lua_pushnil(L); lua_assert(lua_getinfo(L, "lS", ar)); lua_call(L, 2, 0); /* call hook function */ } } /* ** Convert a string mask (for 'sethook') into a bit mask */ static int makemask (const char *smask, int count) { int mask = 0; if (strchr(smask, 'c')) mask |= LUA_MASKCALL; if (strchr(smask, 'r')) mask |= LUA_MASKRET; if (strchr(smask, 'l')) mask |= LUA_MASKLINE; if (count > 0) mask |= LUA_MASKCOUNT; return mask; } /* ** Convert a bit mask (for 'gethook') into a string mask */ static char *unmakemask (int mask, char *smask) { int i = 0; if (mask & LUA_MASKCALL) smask[i++] = 'c'; if (mask & LUA_MASKRET) smask[i++] = 'r'; if (mask & LUA_MASKLINE) smask[i++] = 'l'; smask[i] = '\0'; return smask; } static int db_sethook (lua_State *L) { int arg, mask, count; lua_Hook func; lua_State *L1 = getthread(L, &arg); if (lua_isnoneornil(L, arg+1)) { /* no hook? */ lua_settop(L, arg+1); func = NULL; mask = 0; count = 0; /* turn off hooks */ } else { const char *smask = luaL_checkstring(L, arg+2); luaL_checktype(L, arg+1, LUA_TFUNCTION); count = (int)luaL_optinteger(L, arg + 3, 0); func = hookf; mask = makemask(smask, count); } if (!luaL_getsubtable(L, LUA_REGISTRYINDEX, HOOKKEY)) { /* table just created; initialize it */ lua_pushliteral(L, "k"); lua_setfield(L, -2, "__mode"); /** hooktable.__mode = "k" */ lua_pushvalue(L, -1); lua_setmetatable(L, -2); /* metatable(hooktable) = hooktable */ } checkstack(L, L1, 1); lua_pushthread(L1); lua_xmove(L1, L, 1); /* key (thread) */ lua_pushvalue(L, arg + 1); /* value (hook function) */ lua_rawset(L, -3); /* hooktable[L1] = new Lua hook */ lua_sethook(L1, func, mask, count); return 0; } static int db_gethook (lua_State *L) { int arg; lua_State *L1 = getthread(L, &arg); char buff[5]; int mask = lua_gethookmask(L1); lua_Hook hook = lua_gethook(L1); if (hook == NULL) { /* no hook? */ luaL_pushfail(L); return 1; } else if (hook != hookf) /* external hook? */ lua_pushliteral(L, "external hook"); else { /* hook table must exist */ lua_getfield(L, LUA_REGISTRYINDEX, HOOKKEY); checkstack(L, L1, 1); lua_pushthread(L1); lua_xmove(L1, L, 1); lua_rawget(L, -2); /* 1st result = hooktable[L1] */ lua_remove(L, -2); /* remove hook table */ } lua_pushstring(L, unmakemask(mask, buff)); /* 2nd result = mask */ lua_pushinteger(L, lua_gethookcount(L1)); /* 3rd result = count */ return 3; } static int db_debug (lua_State *L) { for (;;) { char buffer[250]; lua_writestringerror("%s", "lua_debug> "); if (fgets(buffer, sizeof(buffer), stdin) == NULL || strcmp(buffer, "cont\n") == 0) return 0; if (luaL_loadbuffer(L, buffer, strlen(buffer), "=(debug command)") || lua_pcall(L, 0, 0, 0)) lua_writestringerror("%s\n", luaL_tolstring(L, -1, NULL)); lua_settop(L, 0); /* remove eventual returns */ } } static int db_traceback (lua_State *L) { int arg; lua_State *L1 = getthread(L, &arg); const char *msg = lua_tostring(L, arg + 1); if (msg == NULL && !lua_isnoneornil(L, arg + 1)) /* non-string 'msg'? */ lua_pushvalue(L, arg + 1); /* return it untouched */ else { int level = (int)luaL_optinteger(L, arg + 2, (L == L1) ? 1 : 0); luaL_traceback(L, L1, msg, level); } return 1; } static int db_setcstacklimit (lua_State *L) { int limit = (int)luaL_checkinteger(L, 1); int res = lua_setcstacklimit(L, limit); lua_pushinteger(L, res); return 1; } static const luaL_Reg dblib[] = { {"debug", db_debug}, {"getuservalue", db_getuservalue}, {"gethook", db_gethook}, {"getinfo", db_getinfo}, {"getlocal", db_getlocal}, {"getregistry", db_getregistry}, {"getmetatable", db_getmetatable}, {"getupvalue", db_getupvalue}, {"upvaluejoin", db_upvaluejoin}, {"upvalueid", db_upvalueid}, {"setuservalue", db_setuservalue}, {"sethook", db_sethook}, {"setlocal", db_setlocal}, {"setmetatable", db_setmetatable}, {"setupvalue", db_setupvalue}, {"traceback", db_traceback}, {"setcstacklimit", db_setcstacklimit}, {NULL, NULL} }; LUAMOD_API int luaopen_debug (lua_State *L) { luaL_newlib(L, dblib); return 1; } /* ** $Id: liolib.c $ ** Standard I/O (and system) library ** See Copyright Notice in lua.h */ #define liolib_c #define LUA_LIB /*#include "lprefix.h"*/ #include #include #include #include #include #include /*#include "lua.h"*/ /*#include "lauxlib.h"*/ /*#include "lualib.h"*/ /* ** Change this macro to accept other modes for 'fopen' besides ** the standard ones. */ #if !defined(l_checkmode) /* accepted extensions to 'mode' in 'fopen' */ #if !defined(L_MODEEXT) #define L_MODEEXT "b" #endif /* Check whether 'mode' matches '[rwa]%+?[L_MODEEXT]*' */ static int l_checkmode (const char *mode) { return (*mode != '\0' && strchr("rwa", *(mode++)) != NULL && (*mode != '+' || ((void)(++mode), 1)) && /* skip if char is '+' */ (strspn(mode, L_MODEEXT) == strlen(mode))); /* check extensions */ } #endif /* ** {====================================================== ** l_popen spawns a new process connected to the current ** one through the file streams. ** ======================================================= */ #if !defined(l_popen) /* { */ #if defined(LUA_USE_POSIX) /* { */ #define l_popen(L,c,m) (fflush(NULL), popen(c,m)) #define l_pclose(L,file) (pclose(file)) #elif defined(LUA_USE_WINDOWS) /* }{ */ #define l_popen(L,c,m) (_popen(c,m)) #define l_pclose(L,file) (_pclose(file)) #if !defined(l_checkmodep) /* Windows accepts "[rw][bt]?" as valid modes */ #define l_checkmodep(m) ((m[0] == 'r' || m[0] == 'w') && \ (m[1] == '\0' || ((m[1] == 'b' || m[1] == 't') && m[2] == '\0'))) #endif #else /* }{ */ /* ISO C definitions */ #define l_popen(L,c,m) \ ((void)c, (void)m, \ luaL_error(L, "'popen' not supported"), \ (FILE*)0) #define l_pclose(L,file) ((void)L, (void)file, -1) #endif /* } */ #endif /* } */ #if !defined(l_checkmodep) /* By default, Lua accepts only "r" or "w" as valid modes */ #define l_checkmodep(m) ((m[0] == 'r' || m[0] == 'w') && m[1] == '\0') #endif /* }====================================================== */ #if !defined(l_getc) /* { */ #if defined(LUA_USE_POSIX) #define l_getc(f) getc_unlocked(f) #define l_lockfile(f) flockfile(f) #define l_unlockfile(f) funlockfile(f) #else #define l_getc(f) getc(f) #define l_lockfile(f) ((void)0) #define l_unlockfile(f) ((void)0) #endif #endif /* } */ /* ** {====================================================== ** l_fseek: configuration for longer offsets ** ======================================================= */ #if !defined(l_fseek) /* { */ #if defined(LUA_USE_POSIX) /* { */ #include #define l_fseek(f,o,w) fseeko(f,o,w) #define l_ftell(f) ftello(f) #define l_seeknum off_t #elif defined(LUA_USE_WINDOWS) && !defined(_CRTIMP_TYPEINFO) \ && defined(_MSC_VER) && (_MSC_VER >= 1400) /* }{ */ /* Windows (but not DDK) and Visual C++ 2005 or higher */ #define l_fseek(f,o,w) _fseeki64(f,o,w) #define l_ftell(f) _ftelli64(f) #define l_seeknum __int64 #else /* }{ */ /* ISO C definitions */ #define l_fseek(f,o,w) fseek(f,o,w) #define l_ftell(f) ftell(f) #define l_seeknum long #endif /* } */ #endif /* } */ /* }====================================================== */ #define IO_PREFIX "_IO_" #define IOPREF_LEN (sizeof(IO_PREFIX)/sizeof(char) - 1) #define IO_INPUT (IO_PREFIX "input") #define IO_OUTPUT (IO_PREFIX "output") typedef luaL_Stream LStream; #define tolstream(L) ((LStream *)luaL_checkudata(L, 1, LUA_FILEHANDLE)) #define isclosed(p) ((p)->closef == NULL) static int io_type (lua_State *L) { LStream *p; luaL_checkany(L, 1); p = (LStream *)luaL_testudata(L, 1, LUA_FILEHANDLE); if (p == NULL) luaL_pushfail(L); /* not a file */ else if (isclosed(p)) lua_pushliteral(L, "closed file"); else lua_pushliteral(L, "file"); return 1; } static int f_tostring (lua_State *L) { LStream *p = tolstream(L); if (isclosed(p)) lua_pushliteral(L, "file (closed)"); else lua_pushfstring(L, "file (%p)", p->f); return 1; } static FILE *tofile (lua_State *L) { LStream *p = tolstream(L); if (l_unlikely(isclosed(p))) luaL_error(L, "attempt to use a closed file"); lua_assert(p->f); return p->f; } /* ** When creating file handles, always creates a 'closed' file handle ** before opening the actual file; so, if there is a memory error, the ** handle is in a consistent state. */ static LStream *newprefile (lua_State *L) { LStream *p = (LStream *)lua_newuserdatauv(L, sizeof(LStream), 0); p->closef = NULL; /* mark file handle as 'closed' */ luaL_setmetatable(L, LUA_FILEHANDLE); return p; } /* ** Calls the 'close' function from a file handle. The 'volatile' avoids ** a bug in some versions of the Clang compiler (e.g., clang 3.0 for ** 32 bits). */ static int aux_close (lua_State *L) { LStream *p = tolstream(L); volatile lua_CFunction cf = p->closef; p->closef = NULL; /* mark stream as closed */ return (*cf)(L); /* close it */ } static int f_close (lua_State *L) { tofile(L); /* make sure argument is an open stream */ return aux_close(L); } static int io_close (lua_State *L) { if (lua_isnone(L, 1)) /* no argument? */ lua_getfield(L, LUA_REGISTRYINDEX, IO_OUTPUT); /* use default output */ return f_close(L); } static int f_gc (lua_State *L) { LStream *p = tolstream(L); if (!isclosed(p) && p->f != NULL) aux_close(L); /* ignore closed and incompletely open files */ return 0; } /* ** function to close regular files */ static int io_fclose (lua_State *L) { LStream *p = tolstream(L); int res = fclose(p->f); return luaL_fileresult(L, (res == 0), NULL); } static LStream *newfile (lua_State *L) { LStream *p = newprefile(L); p->f = NULL; p->closef = &io_fclose; return p; } static void opencheck (lua_State *L, const char *fname, const char *mode) { LStream *p = newfile(L); p->f = fopen(fname, mode); if (l_unlikely(p->f == NULL)) luaL_error(L, "cannot open file '%s' (%s)", fname, strerror(errno)); } static int io_open (lua_State *L) { const char *filename = luaL_checkstring(L, 1); const char *mode = luaL_optstring(L, 2, "r"); LStream *p = newfile(L); const char *md = mode; /* to traverse/check mode */ luaL_argcheck(L, l_checkmode(md), 2, "invalid mode"); p->f = fopen(filename, mode); return (p->f == NULL) ? luaL_fileresult(L, 0, filename) : 1; } /* ** function to close 'popen' files */ static int io_pclose (lua_State *L) { LStream *p = tolstream(L); errno = 0; return luaL_execresult(L, l_pclose(L, p->f)); } static int io_popen (lua_State *L) { const char *filename = luaL_checkstring(L, 1); const char *mode = luaL_optstring(L, 2, "r"); LStream *p = newprefile(L); luaL_argcheck(L, l_checkmodep(mode), 2, "invalid mode"); p->f = l_popen(L, filename, mode); p->closef = &io_pclose; return (p->f == NULL) ? luaL_fileresult(L, 0, filename) : 1; } static int io_tmpfile (lua_State *L) { LStream *p = newfile(L); p->f = tmpfile(); return (p->f == NULL) ? luaL_fileresult(L, 0, NULL) : 1; } static FILE *getiofile (lua_State *L, const char *findex) { LStream *p; lua_getfield(L, LUA_REGISTRYINDEX, findex); p = (LStream *)lua_touserdata(L, -1); if (l_unlikely(isclosed(p))) luaL_error(L, "default %s file is closed", findex + IOPREF_LEN); return p->f; } static int g_iofile (lua_State *L, const char *f, const char *mode) { if (!lua_isnoneornil(L, 1)) { const char *filename = lua_tostring(L, 1); if (filename) opencheck(L, filename, mode); else { tofile(L); /* check that it's a valid file handle */ lua_pushvalue(L, 1); } lua_setfield(L, LUA_REGISTRYINDEX, f); } /* return current value */ lua_getfield(L, LUA_REGISTRYINDEX, f); return 1; } static int io_input (lua_State *L) { return g_iofile(L, IO_INPUT, "r"); } static int io_output (lua_State *L) { return g_iofile(L, IO_OUTPUT, "w"); } static int io_readline (lua_State *L); /* ** maximum number of arguments to 'f:lines'/'io.lines' (it + 3 must fit ** in the limit for upvalues of a closure) */ #define MAXARGLINE 250 /* ** Auxiliary function to create the iteration function for 'lines'. ** The iteration function is a closure over 'io_readline', with ** the following upvalues: ** 1) The file being read (first value in the stack) ** 2) the number of arguments to read ** 3) a boolean, true iff file has to be closed when finished ('toclose') ** *) a variable number of format arguments (rest of the stack) */ static void aux_lines (lua_State *L, int toclose) { int n = lua_gettop(L) - 1; /* number of arguments to read */ luaL_argcheck(L, n <= MAXARGLINE, MAXARGLINE + 2, "too many arguments"); lua_pushvalue(L, 1); /* file */ lua_pushinteger(L, n); /* number of arguments to read */ lua_pushboolean(L, toclose); /* close/not close file when finished */ lua_rotate(L, 2, 3); /* move the three values to their positions */ lua_pushcclosure(L, io_readline, 3 + n); } static int f_lines (lua_State *L) { tofile(L); /* check that it's a valid file handle */ aux_lines(L, 0); return 1; } /* ** Return an iteration function for 'io.lines'. If file has to be ** closed, also returns the file itself as a second result (to be ** closed as the state at the exit of a generic for). */ static int io_lines (lua_State *L) { int toclose; if (lua_isnone(L, 1)) lua_pushnil(L); /* at least one argument */ if (lua_isnil(L, 1)) { /* no file name? */ lua_getfield(L, LUA_REGISTRYINDEX, IO_INPUT); /* get default input */ lua_replace(L, 1); /* put it at index 1 */ tofile(L); /* check that it's a valid file handle */ toclose = 0; /* do not close it after iteration */ } else { /* open a new file */ const char *filename = luaL_checkstring(L, 1); opencheck(L, filename, "r"); lua_replace(L, 1); /* put file at index 1 */ toclose = 1; /* close it after iteration */ } aux_lines(L, toclose); /* push iteration function */ if (toclose) { lua_pushnil(L); /* state */ lua_pushnil(L); /* control */ lua_pushvalue(L, 1); /* file is the to-be-closed variable (4th result) */ return 4; } else return 1; } /* ** {====================================================== ** READ ** ======================================================= */ /* maximum length of a numeral */ #if !defined (L_MAXLENNUM) #define L_MAXLENNUM 200 #endif /* auxiliary structure used by 'read_number' */ typedef struct { FILE *f; /* file being read */ int c; /* current character (look ahead) */ int n; /* number of elements in buffer 'buff' */ char buff[L_MAXLENNUM + 1]; /* +1 for ending '\0' */ } RN; /* ** Add current char to buffer (if not out of space) and read next one */ static int nextc (RN *rn) { if (l_unlikely(rn->n >= L_MAXLENNUM)) { /* buffer overflow? */ rn->buff[0] = '\0'; /* invalidate result */ return 0; /* fail */ } else { rn->buff[rn->n++] = rn->c; /* save current char */ rn->c = l_getc(rn->f); /* read next one */ return 1; } } /* ** Accept current char if it is in 'set' (of size 2) */ static int test2 (RN *rn, const char *set) { if (rn->c == set[0] || rn->c == set[1]) return nextc(rn); else return 0; } /* ** Read a sequence of (hex)digits */ static int readdigits (RN *rn, int hex) { int count = 0; while ((hex ? isxdigit(rn->c) : isdigit(rn->c)) && nextc(rn)) count++; return count; } /* ** Read a number: first reads a valid prefix of a numeral into a buffer. ** Then it calls 'lua_stringtonumber' to check whether the format is ** correct and to convert it to a Lua number. */ static int read_number (lua_State *L, FILE *f) { RN rn; int count = 0; int hex = 0; char decp[2]; rn.f = f; rn.n = 0; decp[0] = lua_getlocaledecpoint(); /* get decimal point from locale */ decp[1] = '.'; /* always accept a dot */ l_lockfile(rn.f); do { rn.c = l_getc(rn.f); } while (isspace(rn.c)); /* skip spaces */ test2(&rn, "-+"); /* optional sign */ if (test2(&rn, "00")) { if (test2(&rn, "xX")) hex = 1; /* numeral is hexadecimal */ else count = 1; /* count initial '0' as a valid digit */ } count += readdigits(&rn, hex); /* integral part */ if (test2(&rn, decp)) /* decimal point? */ count += readdigits(&rn, hex); /* fractional part */ if (count > 0 && test2(&rn, (hex ? "pP" : "eE"))) { /* exponent mark? */ test2(&rn, "-+"); /* exponent sign */ readdigits(&rn, 0); /* exponent digits */ } ungetc(rn.c, rn.f); /* unread look-ahead char */ l_unlockfile(rn.f); rn.buff[rn.n] = '\0'; /* finish string */ if (l_likely(lua_stringtonumber(L, rn.buff))) return 1; /* ok, it is a valid number */ else { /* invalid format */ lua_pushnil(L); /* "result" to be removed */ return 0; /* read fails */ } } static int test_eof (lua_State *L, FILE *f) { int c = getc(f); ungetc(c, f); /* no-op when c == EOF */ lua_pushliteral(L, ""); return (c != EOF); } static int read_line (lua_State *L, FILE *f, int chop) { luaL_Buffer b; int c; luaL_buffinit(L, &b); do { /* may need to read several chunks to get whole line */ char *buff = luaL_prepbuffer(&b); /* preallocate buffer space */ int i = 0; l_lockfile(f); /* no memory errors can happen inside the lock */ while (i < LUAL_BUFFERSIZE && (c = l_getc(f)) != EOF && c != '\n') buff[i++] = c; /* read up to end of line or buffer limit */ l_unlockfile(f); luaL_addsize(&b, i); } while (c != EOF && c != '\n'); /* repeat until end of line */ if (!chop && c == '\n') /* want a newline and have one? */ luaL_addchar(&b, c); /* add ending newline to result */ luaL_pushresult(&b); /* close buffer */ /* return ok if read something (either a newline or something else) */ return (c == '\n' || lua_rawlen(L, -1) > 0); } static void read_all (lua_State *L, FILE *f) { size_t nr; luaL_Buffer b; luaL_buffinit(L, &b); do { /* read file in chunks of LUAL_BUFFERSIZE bytes */ char *p = luaL_prepbuffer(&b); nr = fread(p, sizeof(char), LUAL_BUFFERSIZE, f); luaL_addsize(&b, nr); } while (nr == LUAL_BUFFERSIZE); luaL_pushresult(&b); /* close buffer */ } static int read_chars (lua_State *L, FILE *f, size_t n) { size_t nr; /* number of chars actually read */ char *p; luaL_Buffer b; luaL_buffinit(L, &b); p = luaL_prepbuffsize(&b, n); /* prepare buffer to read whole block */ nr = fread(p, sizeof(char), n, f); /* try to read 'n' chars */ luaL_addsize(&b, nr); luaL_pushresult(&b); /* close buffer */ return (nr > 0); /* true iff read something */ } static int g_read (lua_State *L, FILE *f, int first) { int nargs = lua_gettop(L) - 1; int n, success; clearerr(f); if (nargs == 0) { /* no arguments? */ success = read_line(L, f, 1); n = first + 1; /* to return 1 result */ } else { /* ensure stack space for all results and for auxlib's buffer */ luaL_checkstack(L, nargs+LUA_MINSTACK, "too many arguments"); success = 1; for (n = first; nargs-- && success; n++) { if (lua_type(L, n) == LUA_TNUMBER) { size_t l = (size_t)luaL_checkinteger(L, n); success = (l == 0) ? test_eof(L, f) : read_chars(L, f, l); } else { const char *p = luaL_checkstring(L, n); if (*p == '*') p++; /* skip optional '*' (for compatibility) */ switch (*p) { case 'n': /* number */ success = read_number(L, f); break; case 'l': /* line */ success = read_line(L, f, 1); break; case 'L': /* line with end-of-line */ success = read_line(L, f, 0); break; case 'a': /* file */ read_all(L, f); /* read entire file */ success = 1; /* always success */ break; default: return luaL_argerror(L, n, "invalid format"); } } } } if (ferror(f)) return luaL_fileresult(L, 0, NULL); if (!success) { lua_pop(L, 1); /* remove last result */ luaL_pushfail(L); /* push nil instead */ } return n - first; } static int io_read (lua_State *L) { return g_read(L, getiofile(L, IO_INPUT), 1); } static int f_read (lua_State *L) { return g_read(L, tofile(L), 2); } /* ** Iteration function for 'lines'. */ static int io_readline (lua_State *L) { LStream *p = (LStream *)lua_touserdata(L, lua_upvalueindex(1)); int i; int n = (int)lua_tointeger(L, lua_upvalueindex(2)); if (isclosed(p)) /* file is already closed? */ return luaL_error(L, "file is already closed"); lua_settop(L , 1); luaL_checkstack(L, n, "too many arguments"); for (i = 1; i <= n; i++) /* push arguments to 'g_read' */ lua_pushvalue(L, lua_upvalueindex(3 + i)); n = g_read(L, p->f, 2); /* 'n' is number of results */ lua_assert(n > 0); /* should return at least a nil */ if (lua_toboolean(L, -n)) /* read at least one value? */ return n; /* return them */ else { /* first result is false: EOF or error */ if (n > 1) { /* is there error information? */ /* 2nd result is error message */ return luaL_error(L, "%s", lua_tostring(L, -n + 1)); } if (lua_toboolean(L, lua_upvalueindex(3))) { /* generator created file? */ lua_settop(L, 0); /* clear stack */ lua_pushvalue(L, lua_upvalueindex(1)); /* push file at index 1 */ aux_close(L); /* close it */ } return 0; } } /* }====================================================== */ static int g_write (lua_State *L, FILE *f, int arg) { int nargs = lua_gettop(L) - arg; int status = 1; for (; nargs--; arg++) { if (lua_type(L, arg) == LUA_TNUMBER) { /* optimization: could be done exactly as for strings */ int len = lua_isinteger(L, arg) ? fprintf(f, LUA_INTEGER_FMT, (LUAI_UACINT)lua_tointeger(L, arg)) : fprintf(f, LUA_NUMBER_FMT, (LUAI_UACNUMBER)lua_tonumber(L, arg)); status = status && (len > 0); } else { size_t l; const char *s = luaL_checklstring(L, arg, &l); status = status && (fwrite(s, sizeof(char), l, f) == l); } } if (l_likely(status)) return 1; /* file handle already on stack top */ else return luaL_fileresult(L, status, NULL); } static int io_write (lua_State *L) { return g_write(L, getiofile(L, IO_OUTPUT), 1); } static int f_write (lua_State *L) { FILE *f = tofile(L); lua_pushvalue(L, 1); /* push file at the stack top (to be returned) */ return g_write(L, f, 2); } static int f_seek (lua_State *L) { static const int mode[] = {SEEK_SET, SEEK_CUR, SEEK_END}; static const char *const modenames[] = {"set", "cur", "end", NULL}; FILE *f = tofile(L); int op = luaL_checkoption(L, 2, "cur", modenames); lua_Integer p3 = luaL_optinteger(L, 3, 0); l_seeknum offset = (l_seeknum)p3; luaL_argcheck(L, (lua_Integer)offset == p3, 3, "not an integer in proper range"); op = l_fseek(f, offset, mode[op]); if (l_unlikely(op)) return luaL_fileresult(L, 0, NULL); /* error */ else { lua_pushinteger(L, (lua_Integer)l_ftell(f)); return 1; } } static int f_setvbuf (lua_State *L) { static const int mode[] = {_IONBF, _IOFBF, _IOLBF}; static const char *const modenames[] = {"no", "full", "line", NULL}; FILE *f = tofile(L); int op = luaL_checkoption(L, 2, NULL, modenames); lua_Integer sz = luaL_optinteger(L, 3, LUAL_BUFFERSIZE); int res = setvbuf(f, NULL, mode[op], (size_t)sz); return luaL_fileresult(L, res == 0, NULL); } static int io_flush (lua_State *L) { return luaL_fileresult(L, fflush(getiofile(L, IO_OUTPUT)) == 0, NULL); } static int f_flush (lua_State *L) { return luaL_fileresult(L, fflush(tofile(L)) == 0, NULL); } /* ** functions for 'io' library */ static const luaL_Reg iolib[] = { {"close", io_close}, {"flush", io_flush}, {"input", io_input}, {"lines", io_lines}, {"open", io_open}, {"output", io_output}, {"popen", io_popen}, {"read", io_read}, {"tmpfile", io_tmpfile}, {"type", io_type}, {"write", io_write}, {NULL, NULL} }; /* ** methods for file handles */ static const luaL_Reg meth[] = { {"read", f_read}, {"write", f_write}, {"lines", f_lines}, {"flush", f_flush}, {"seek", f_seek}, {"close", f_close}, {"setvbuf", f_setvbuf}, {NULL, NULL} }; /* ** metamethods for file handles */ static const luaL_Reg metameth[] = { {"__index", NULL}, /* place holder */ {"__gc", f_gc}, {"__close", f_gc}, {"__tostring", f_tostring}, {NULL, NULL} }; static void createmeta (lua_State *L) { luaL_newmetatable(L, LUA_FILEHANDLE); /* metatable for file handles */ luaL_setfuncs(L, metameth, 0); /* add metamethods to new metatable */ luaL_newlibtable(L, meth); /* create method table */ luaL_setfuncs(L, meth, 0); /* add file methods to method table */ lua_setfield(L, -2, "__index"); /* metatable.__index = method table */ lua_pop(L, 1); /* pop metatable */ } /* ** function to (not) close the standard files stdin, stdout, and stderr */ static int io_noclose (lua_State *L) { LStream *p = tolstream(L); p->closef = &io_noclose; /* keep file opened */ luaL_pushfail(L); lua_pushliteral(L, "cannot close standard file"); return 2; } static void createstdfile (lua_State *L, FILE *f, const char *k, const char *fname) { LStream *p = newprefile(L); p->f = f; p->closef = &io_noclose; if (k != NULL) { lua_pushvalue(L, -1); lua_setfield(L, LUA_REGISTRYINDEX, k); /* add file to registry */ } lua_setfield(L, -2, fname); /* add file to module */ } LUAMOD_API int luaopen_io (lua_State *L) { luaL_newlib(L, iolib); /* new module */ createmeta(L); /* create (and set) default files */ createstdfile(L, stdin, IO_INPUT, "stdin"); createstdfile(L, stdout, IO_OUTPUT, "stdout"); createstdfile(L, stderr, NULL, "stderr"); return 1; } /* ** $Id: lmathlib.c $ ** Standard mathematical library ** See Copyright Notice in lua.h */ #define lmathlib_c #define LUA_LIB /*#include "lprefix.h"*/ #include #include #include #include #include /*#include "lua.h"*/ /*#include "lauxlib.h"*/ /*#include "lualib.h"*/ #undef PI #define PI (l_mathop(3.141592653589793238462643383279502884)) static int math_abs (lua_State *L) { if (lua_isinteger(L, 1)) { lua_Integer n = lua_tointeger(L, 1); if (n < 0) n = (lua_Integer)(0u - (lua_Unsigned)n); lua_pushinteger(L, n); } else lua_pushnumber(L, l_mathop(fabs)(luaL_checknumber(L, 1))); return 1; } static int math_sin (lua_State *L) { lua_pushnumber(L, l_mathop(sin)(luaL_checknumber(L, 1))); return 1; } static int math_cos (lua_State *L) { lua_pushnumber(L, l_mathop(cos)(luaL_checknumber(L, 1))); return 1; } static int math_tan (lua_State *L) { lua_pushnumber(L, l_mathop(tan)(luaL_checknumber(L, 1))); return 1; } static int math_asin (lua_State *L) { lua_pushnumber(L, l_mathop(asin)(luaL_checknumber(L, 1))); return 1; } static int math_acos (lua_State *L) { lua_pushnumber(L, l_mathop(acos)(luaL_checknumber(L, 1))); return 1; } static int math_atan (lua_State *L) { lua_Number y = luaL_checknumber(L, 1); lua_Number x = luaL_optnumber(L, 2, 1); lua_pushnumber(L, l_mathop(atan2)(y, x)); return 1; } static int math_toint (lua_State *L) { int valid; lua_Integer n = lua_tointegerx(L, 1, &valid); if (l_likely(valid)) lua_pushinteger(L, n); else { luaL_checkany(L, 1); luaL_pushfail(L); /* value is not convertible to integer */ } return 1; } static void pushnumint (lua_State *L, lua_Number d) { lua_Integer n; if (lua_numbertointeger(d, &n)) /* does 'd' fit in an integer? */ lua_pushinteger(L, n); /* result is integer */ else lua_pushnumber(L, d); /* result is float */ } static int math_floor (lua_State *L) { if (lua_isinteger(L, 1)) lua_settop(L, 1); /* integer is its own floor */ else { lua_Number d = l_mathop(floor)(luaL_checknumber(L, 1)); pushnumint(L, d); } return 1; } static int math_ceil (lua_State *L) { if (lua_isinteger(L, 1)) lua_settop(L, 1); /* integer is its own ceil */ else { lua_Number d = l_mathop(ceil)(luaL_checknumber(L, 1)); pushnumint(L, d); } return 1; } static int math_fmod (lua_State *L) { if (lua_isinteger(L, 1) && lua_isinteger(L, 2)) { lua_Integer d = lua_tointeger(L, 2); if ((lua_Unsigned)d + 1u <= 1u) { /* special cases: -1 or 0 */ luaL_argcheck(L, d != 0, 2, "zero"); lua_pushinteger(L, 0); /* avoid overflow with 0x80000... / -1 */ } else lua_pushinteger(L, lua_tointeger(L, 1) % d); } else lua_pushnumber(L, l_mathop(fmod)(luaL_checknumber(L, 1), luaL_checknumber(L, 2))); return 1; } /* ** next function does not use 'modf', avoiding problems with 'double*' ** (which is not compatible with 'float*') when lua_Number is not ** 'double'. */ static int math_modf (lua_State *L) { if (lua_isinteger(L ,1)) { lua_settop(L, 1); /* number is its own integer part */ lua_pushnumber(L, 0); /* no fractional part */ } else { lua_Number n = luaL_checknumber(L, 1); /* integer part (rounds toward zero) */ lua_Number ip = (n < 0) ? l_mathop(ceil)(n) : l_mathop(floor)(n); pushnumint(L, ip); /* fractional part (test needed for inf/-inf) */ lua_pushnumber(L, (n == ip) ? l_mathop(0.0) : (n - ip)); } return 2; } static int math_sqrt (lua_State *L) { lua_pushnumber(L, l_mathop(sqrt)(luaL_checknumber(L, 1))); return 1; } static int math_ult (lua_State *L) { lua_Integer a = luaL_checkinteger(L, 1); lua_Integer b = luaL_checkinteger(L, 2); lua_pushboolean(L, (lua_Unsigned)a < (lua_Unsigned)b); return 1; } static int math_log (lua_State *L) { lua_Number x = luaL_checknumber(L, 1); lua_Number res; if (lua_isnoneornil(L, 2)) res = l_mathop(log)(x); else { lua_Number base = luaL_checknumber(L, 2); #if !defined(LUA_USE_C89) if (base == l_mathop(2.0)) res = l_mathop(log2)(x); else #endif if (base == l_mathop(10.0)) res = l_mathop(log10)(x); else res = l_mathop(log)(x)/l_mathop(log)(base); } lua_pushnumber(L, res); return 1; } static int math_exp (lua_State *L) { lua_pushnumber(L, l_mathop(exp)(luaL_checknumber(L, 1))); return 1; } static int math_deg (lua_State *L) { lua_pushnumber(L, luaL_checknumber(L, 1) * (l_mathop(180.0) / PI)); return 1; } static int math_rad (lua_State *L) { lua_pushnumber(L, luaL_checknumber(L, 1) * (PI / l_mathop(180.0))); return 1; } static int math_min (lua_State *L) { int n = lua_gettop(L); /* number of arguments */ int imin = 1; /* index of current minimum value */ int i; luaL_argcheck(L, n >= 1, 1, "value expected"); for (i = 2; i <= n; i++) { if (lua_compare(L, i, imin, LUA_OPLT)) imin = i; } lua_pushvalue(L, imin); return 1; } static int math_max (lua_State *L) { int n = lua_gettop(L); /* number of arguments */ int imax = 1; /* index of current maximum value */ int i; luaL_argcheck(L, n >= 1, 1, "value expected"); for (i = 2; i <= n; i++) { if (lua_compare(L, imax, i, LUA_OPLT)) imax = i; } lua_pushvalue(L, imax); return 1; } static int math_type (lua_State *L) { if (lua_type(L, 1) == LUA_TNUMBER) lua_pushstring(L, (lua_isinteger(L, 1)) ? "integer" : "float"); else { luaL_checkany(L, 1); luaL_pushfail(L); } return 1; } /* ** {================================================================== ** Pseudo-Random Number Generator based on 'xoshiro256**'. ** =================================================================== */ /* number of binary digits in the mantissa of a float */ #define FIGS l_floatatt(MANT_DIG) #if FIGS > 64 /* there are only 64 random bits; use them all */ #undef FIGS #define FIGS 64 #endif /* ** LUA_RAND32 forces the use of 32-bit integers in the implementation ** of the PRN generator (mainly for testing). */ #if !defined(LUA_RAND32) && !defined(Rand64) /* try to find an integer type with at least 64 bits */ #if (ULONG_MAX >> 31 >> 31) >= 3 /* 'long' has at least 64 bits */ #define Rand64 unsigned long #elif !defined(LUA_USE_C89) && defined(LLONG_MAX) /* there is a 'long long' type (which must have at least 64 bits) */ #define Rand64 unsigned long long #elif (LUA_MAXUNSIGNED >> 31 >> 31) >= 3 /* 'lua_Integer' has at least 64 bits */ #define Rand64 lua_Unsigned #endif #endif #if defined(Rand64) /* { */ /* ** Standard implementation, using 64-bit integers. ** If 'Rand64' has more than 64 bits, the extra bits do not interfere ** with the 64 initial bits, except in a right shift. Moreover, the ** final result has to discard the extra bits. */ /* avoid using extra bits when needed */ #define trim64(x) ((x) & 0xffffffffffffffffu) /* rotate left 'x' by 'n' bits */ static Rand64 rotl (Rand64 x, int n) { return (x << n) | (trim64(x) >> (64 - n)); } static Rand64 nextrand (Rand64 *state) { Rand64 state0 = state[0]; Rand64 state1 = state[1]; Rand64 state2 = state[2] ^ state0; Rand64 state3 = state[3] ^ state1; Rand64 res = rotl(state1 * 5, 7) * 9; state[0] = state0 ^ state3; state[1] = state1 ^ state2; state[2] = state2 ^ (state1 << 17); state[3] = rotl(state3, 45); return res; } /* must take care to not shift stuff by more than 63 slots */ /* ** Convert bits from a random integer into a float in the ** interval [0,1), getting the higher FIG bits from the ** random unsigned integer and converting that to a float. */ /* must throw out the extra (64 - FIGS) bits */ #define shift64_FIG (64 - FIGS) /* to scale to [0, 1), multiply by scaleFIG = 2^(-FIGS) */ #define scaleFIG (l_mathop(0.5) / ((Rand64)1 << (FIGS - 1))) static lua_Number I2d (Rand64 x) { return (lua_Number)(trim64(x) >> shift64_FIG) * scaleFIG; } /* convert a 'Rand64' to a 'lua_Unsigned' */ #define I2UInt(x) ((lua_Unsigned)trim64(x)) /* convert a 'lua_Unsigned' to a 'Rand64' */ #define Int2I(x) ((Rand64)(x)) #else /* no 'Rand64' }{ */ /* get an integer with at least 32 bits */ #if LUAI_IS32INT typedef unsigned int lu_int32; #else typedef unsigned long lu_int32; #endif /* ** Use two 32-bit integers to represent a 64-bit quantity. */ typedef struct Rand64 { lu_int32 h; /* higher half */ lu_int32 l; /* lower half */ } Rand64; /* ** If 'lu_int32' has more than 32 bits, the extra bits do not interfere ** with the 32 initial bits, except in a right shift and comparisons. ** Moreover, the final result has to discard the extra bits. */ /* avoid using extra bits when needed */ #define trim32(x) ((x) & 0xffffffffu) /* ** basic operations on 'Rand64' values */ /* build a new Rand64 value */ static Rand64 packI (lu_int32 h, lu_int32 l) { Rand64 result; result.h = h; result.l = l; return result; } /* return i << n */ static Rand64 Ishl (Rand64 i, int n) { lua_assert(n > 0 && n < 32); return packI((i.h << n) | (trim32(i.l) >> (32 - n)), i.l << n); } /* i1 ^= i2 */ static void Ixor (Rand64 *i1, Rand64 i2) { i1->h ^= i2.h; i1->l ^= i2.l; } /* return i1 + i2 */ static Rand64 Iadd (Rand64 i1, Rand64 i2) { Rand64 result = packI(i1.h + i2.h, i1.l + i2.l); if (trim32(result.l) < trim32(i1.l)) /* carry? */ result.h++; return result; } /* return i * 5 */ static Rand64 times5 (Rand64 i) { return Iadd(Ishl(i, 2), i); /* i * 5 == (i << 2) + i */ } /* return i * 9 */ static Rand64 times9 (Rand64 i) { return Iadd(Ishl(i, 3), i); /* i * 9 == (i << 3) + i */ } /* return 'i' rotated left 'n' bits */ static Rand64 rotl (Rand64 i, int n) { lua_assert(n > 0 && n < 32); return packI((i.h << n) | (trim32(i.l) >> (32 - n)), (trim32(i.h) >> (32 - n)) | (i.l << n)); } /* for offsets larger than 32, rotate right by 64 - offset */ static Rand64 rotl1 (Rand64 i, int n) { lua_assert(n > 32 && n < 64); n = 64 - n; return packI((trim32(i.h) >> n) | (i.l << (32 - n)), (i.h << (32 - n)) | (trim32(i.l) >> n)); } /* ** implementation of 'xoshiro256**' algorithm on 'Rand64' values */ static Rand64 nextrand (Rand64 *state) { Rand64 res = times9(rotl(times5(state[1]), 7)); Rand64 t = Ishl(state[1], 17); Ixor(&state[2], state[0]); Ixor(&state[3], state[1]); Ixor(&state[1], state[2]); Ixor(&state[0], state[3]); Ixor(&state[2], t); state[3] = rotl1(state[3], 45); return res; } /* ** Converts a 'Rand64' into a float. */ /* an unsigned 1 with proper type */ #define UONE ((lu_int32)1) #if FIGS <= 32 /* 2^(-FIGS) */ #define scaleFIG (l_mathop(0.5) / (UONE << (FIGS - 1))) /* ** get up to 32 bits from higher half, shifting right to ** throw out the extra bits. */ static lua_Number I2d (Rand64 x) { lua_Number h = (lua_Number)(trim32(x.h) >> (32 - FIGS)); return h * scaleFIG; } #else /* 32 < FIGS <= 64 */ /* must take care to not shift stuff by more than 31 slots */ /* 2^(-FIGS) = 1.0 / 2^30 / 2^3 / 2^(FIGS-33) */ #define scaleFIG \ (l_mathop(1.0) / (UONE << 30) / l_mathop(8.0) / (UONE << (FIGS - 33))) /* ** use FIGS - 32 bits from lower half, throwing out the other ** (32 - (FIGS - 32)) = (64 - FIGS) bits */ #define shiftLOW (64 - FIGS) /* ** higher 32 bits go after those (FIGS - 32) bits: shiftHI = 2^(FIGS - 32) */ #define shiftHI ((lua_Number)(UONE << (FIGS - 33)) * l_mathop(2.0)) static lua_Number I2d (Rand64 x) { lua_Number h = (lua_Number)trim32(x.h) * shiftHI; lua_Number l = (lua_Number)(trim32(x.l) >> shiftLOW); return (h + l) * scaleFIG; } #endif /* convert a 'Rand64' to a 'lua_Unsigned' */ static lua_Unsigned I2UInt (Rand64 x) { return ((lua_Unsigned)trim32(x.h) << 31 << 1) | (lua_Unsigned)trim32(x.l); } /* convert a 'lua_Unsigned' to a 'Rand64' */ static Rand64 Int2I (lua_Unsigned n) { return packI((lu_int32)(n >> 31 >> 1), (lu_int32)n); } #endif /* } */ /* ** A state uses four 'Rand64' values. */ typedef struct { Rand64 s[4]; } RanState; /* ** Project the random integer 'ran' into the interval [0, n]. ** Because 'ran' has 2^B possible values, the projection can only be ** uniform when the size of the interval is a power of 2 (exact ** division). Otherwise, to get a uniform projection into [0, n], we ** first compute 'lim', the smallest Mersenne number not smaller than ** 'n'. We then project 'ran' into the interval [0, lim]. If the result ** is inside [0, n], we are done. Otherwise, we try with another 'ran', ** until we have a result inside the interval. */ static lua_Unsigned project (lua_Unsigned ran, lua_Unsigned n, RanState *state) { if ((n & (n + 1)) == 0) /* is 'n + 1' a power of 2? */ return ran & n; /* no bias */ else { lua_Unsigned lim = n; /* compute the smallest (2^b - 1) not smaller than 'n' */ lim |= (lim >> 1); lim |= (lim >> 2); lim |= (lim >> 4); lim |= (lim >> 8); lim |= (lim >> 16); #if (LUA_MAXUNSIGNED >> 31) >= 3 lim |= (lim >> 32); /* integer type has more than 32 bits */ #endif lua_assert((lim & (lim + 1)) == 0 /* 'lim + 1' is a power of 2, */ && lim >= n /* not smaller than 'n', */ && (lim >> 1) < n); /* and it is the smallest one */ while ((ran &= lim) > n) /* project 'ran' into [0..lim] */ ran = I2UInt(nextrand(state->s)); /* not inside [0..n]? try again */ return ran; } } static int math_random (lua_State *L) { lua_Integer low, up; lua_Unsigned p; RanState *state = (RanState *)lua_touserdata(L, lua_upvalueindex(1)); Rand64 rv = nextrand(state->s); /* next pseudo-random value */ switch (lua_gettop(L)) { /* check number of arguments */ case 0: { /* no arguments */ lua_pushnumber(L, I2d(rv)); /* float between 0 and 1 */ return 1; } case 1: { /* only upper limit */ low = 1; up = luaL_checkinteger(L, 1); if (up == 0) { /* single 0 as argument? */ lua_pushinteger(L, I2UInt(rv)); /* full random integer */ return 1; } break; } case 2: { /* lower and upper limits */ low = luaL_checkinteger(L, 1); up = luaL_checkinteger(L, 2); break; } default: return luaL_error(L, "wrong number of arguments"); } /* random integer in the interval [low, up] */ luaL_argcheck(L, low <= up, 1, "interval is empty"); /* project random integer into the interval [0, up - low] */ p = project(I2UInt(rv), (lua_Unsigned)up - (lua_Unsigned)low, state); lua_pushinteger(L, p + (lua_Unsigned)low); return 1; } static void setseed (lua_State *L, Rand64 *state, lua_Unsigned n1, lua_Unsigned n2) { int i; state[0] = Int2I(n1); state[1] = Int2I(0xff); /* avoid a zero state */ state[2] = Int2I(n2); state[3] = Int2I(0); for (i = 0; i < 16; i++) nextrand(state); /* discard initial values to "spread" seed */ lua_pushinteger(L, n1); lua_pushinteger(L, n2); } /* ** Set a "random" seed. To get some randomness, use the current time ** and the address of 'L' (in case the machine does address space layout ** randomization). */ static void randseed (lua_State *L, RanState *state) { lua_Unsigned seed1 = (lua_Unsigned)time(NULL); lua_Unsigned seed2 = (lua_Unsigned)(size_t)L; setseed(L, state->s, seed1, seed2); } static int math_randomseed (lua_State *L) { RanState *state = (RanState *)lua_touserdata(L, lua_upvalueindex(1)); if (lua_isnone(L, 1)) { randseed(L, state); } else { lua_Integer n1 = luaL_checkinteger(L, 1); lua_Integer n2 = luaL_optinteger(L, 2, 0); setseed(L, state->s, n1, n2); } return 2; /* return seeds */ } static const luaL_Reg randfuncs[] = { {"random", math_random}, {"randomseed", math_randomseed}, {NULL, NULL} }; /* ** Register the random functions and initialize their state. */ static void setrandfunc (lua_State *L) { RanState *state = (RanState *)lua_newuserdatauv(L, sizeof(RanState), 0); randseed(L, state); /* initialize with a "random" seed */ lua_pop(L, 2); /* remove pushed seeds */ luaL_setfuncs(L, randfuncs, 1); } /* }================================================================== */ /* ** {================================================================== ** Deprecated functions (for compatibility only) ** =================================================================== */ #if defined(LUA_COMPAT_MATHLIB) static int math_cosh (lua_State *L) { lua_pushnumber(L, l_mathop(cosh)(luaL_checknumber(L, 1))); return 1; } static int math_sinh (lua_State *L) { lua_pushnumber(L, l_mathop(sinh)(luaL_checknumber(L, 1))); return 1; } static int math_tanh (lua_State *L) { lua_pushnumber(L, l_mathop(tanh)(luaL_checknumber(L, 1))); return 1; } static int math_pow (lua_State *L) { lua_Number x = luaL_checknumber(L, 1); lua_Number y = luaL_checknumber(L, 2); lua_pushnumber(L, l_mathop(pow)(x, y)); return 1; } static int math_frexp (lua_State *L) { int e; lua_pushnumber(L, l_mathop(frexp)(luaL_checknumber(L, 1), &e)); lua_pushinteger(L, e); return 2; } static int math_ldexp (lua_State *L) { lua_Number x = luaL_checknumber(L, 1); int ep = (int)luaL_checkinteger(L, 2); lua_pushnumber(L, l_mathop(ldexp)(x, ep)); return 1; } static int math_log10 (lua_State *L) { lua_pushnumber(L, l_mathop(log10)(luaL_checknumber(L, 1))); return 1; } #endif /* }================================================================== */ static const luaL_Reg mathlib[] = { {"abs", math_abs}, {"acos", math_acos}, {"asin", math_asin}, {"atan", math_atan}, {"ceil", math_ceil}, {"cos", math_cos}, {"deg", math_deg}, {"exp", math_exp}, {"tointeger", math_toint}, {"floor", math_floor}, {"fmod", math_fmod}, {"ult", math_ult}, {"log", math_log}, {"max", math_max}, {"min", math_min}, {"modf", math_modf}, {"rad", math_rad}, {"sin", math_sin}, {"sqrt", math_sqrt}, {"tan", math_tan}, {"type", math_type}, #if defined(LUA_COMPAT_MATHLIB) {"atan2", math_atan}, {"cosh", math_cosh}, {"sinh", math_sinh}, {"tanh", math_tanh}, {"pow", math_pow}, {"frexp", math_frexp}, {"ldexp", math_ldexp}, {"log10", math_log10}, #endif /* placeholders */ {"random", NULL}, {"randomseed", NULL}, {"pi", NULL}, {"huge", NULL}, {"maxinteger", NULL}, {"mininteger", NULL}, {NULL, NULL} }; /* ** Open math library */ LUAMOD_API int luaopen_math (lua_State *L) { luaL_newlib(L, mathlib); lua_pushnumber(L, PI); lua_setfield(L, -2, "pi"); lua_pushnumber(L, (lua_Number)HUGE_VAL); lua_setfield(L, -2, "huge"); lua_pushinteger(L, LUA_MAXINTEGER); lua_setfield(L, -2, "maxinteger"); lua_pushinteger(L, LUA_MININTEGER); lua_setfield(L, -2, "mininteger"); setrandfunc(L); return 1; } /* ** $Id: loadlib.c $ ** Dynamic library loader for Lua ** See Copyright Notice in lua.h ** ** This module contains an implementation of loadlib for Unix systems ** that have dlfcn, an implementation for Windows, and a stub for other ** systems. */ #define loadlib_c #define LUA_LIB /*#include "lprefix.h"*/ #include #include #include /*#include "lua.h"*/ /*#include "lauxlib.h"*/ /*#include "lualib.h"*/ /* ** LUA_IGMARK is a mark to ignore all before it when building the ** luaopen_ function name. */ #if !defined (LUA_IGMARK) #define LUA_IGMARK "-" #endif /* ** LUA_CSUBSEP is the character that replaces dots in submodule names ** when searching for a C loader. ** LUA_LSUBSEP is the character that replaces dots in submodule names ** when searching for a Lua loader. */ #if !defined(LUA_CSUBSEP) #define LUA_CSUBSEP LUA_DIRSEP #endif #if !defined(LUA_LSUBSEP) #define LUA_LSUBSEP LUA_DIRSEP #endif /* prefix for open functions in C libraries */ #define LUA_POF "luaopen_" /* separator for open functions in C libraries */ #define LUA_OFSEP "_" /* ** key for table in the registry that keeps handles ** for all loaded C libraries */ static const char *const CLIBS = "_CLIBS"; #define LIB_FAIL "open" #define setprogdir(L) ((void)0) /* ** Special type equivalent to '(void*)' for functions in gcc ** (to suppress warnings when converting function pointers) */ typedef void (*voidf)(void); /* ** system-dependent functions */ /* ** unload library 'lib' */ static void lsys_unloadlib (void *lib); /* ** load C library in file 'path'. If 'seeglb', load with all names in ** the library global. ** Returns the library; in case of error, returns NULL plus an ** error string in the stack. */ static void *lsys_load (lua_State *L, const char *path, int seeglb); /* ** Try to find a function named 'sym' in library 'lib'. ** Returns the function; in case of error, returns NULL plus an ** error string in the stack. */ static lua_CFunction lsys_sym (lua_State *L, void *lib, const char *sym); #if defined(LUA_USE_DLOPEN) /* { */ /* ** {======================================================================== ** This is an implementation of loadlib based on the dlfcn interface. ** The dlfcn interface is available in Linux, SunOS, Solaris, IRIX, FreeBSD, ** NetBSD, AIX 4.2, HPUX 11, and probably most other Unix flavors, at least ** as an emulation layer on top of native functions. ** ========================================================================= */ #include /* ** Macro to convert pointer-to-void* to pointer-to-function. This cast ** is undefined according to ISO C, but POSIX assumes that it works. ** (The '__extension__' in gnu compilers is only to avoid warnings.) */ #if defined(__GNUC__) #define cast_func(p) (__extension__ (lua_CFunction)(p)) #else #define cast_func(p) ((lua_CFunction)(p)) #endif static void lsys_unloadlib (void *lib) { dlclose(lib); } static void *lsys_load (lua_State *L, const char *path, int seeglb) { void *lib = dlopen(path, RTLD_NOW | (seeglb ? RTLD_GLOBAL : RTLD_LOCAL)); if (l_unlikely(lib == NULL)) lua_pushstring(L, dlerror()); return lib; } static lua_CFunction lsys_sym (lua_State *L, void *lib, const char *sym) { lua_CFunction f = cast_func(dlsym(lib, sym)); if (l_unlikely(f == NULL)) lua_pushstring(L, dlerror()); return f; } /* }====================================================== */ #elif defined(LUA_DL_DLL) /* }{ */ /* ** {====================================================================== ** This is an implementation of loadlib for Windows using native functions. ** ======================================================================= */ #include /* ** optional flags for LoadLibraryEx */ #if !defined(LUA_LLE_FLAGS) #define LUA_LLE_FLAGS 0 #endif #undef setprogdir /* ** Replace in the path (on the top of the stack) any occurrence ** of LUA_EXEC_DIR with the executable's path. */ static void setprogdir (lua_State *L) { char buff[MAX_PATH + 1]; char *lb; DWORD nsize = sizeof(buff)/sizeof(char); DWORD n = GetModuleFileNameA(NULL, buff, nsize); /* get exec. name */ if (n == 0 || n == nsize || (lb = strrchr(buff, '\\')) == NULL) luaL_error(L, "unable to get ModuleFileName"); else { *lb = '\0'; /* cut name on the last '\\' to get the path */ luaL_gsub(L, lua_tostring(L, -1), LUA_EXEC_DIR, buff); lua_remove(L, -2); /* remove original string */ } } static void pusherror (lua_State *L) { int error = GetLastError(); char buffer[128]; if (FormatMessageA(FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_SYSTEM, NULL, error, 0, buffer, sizeof(buffer)/sizeof(char), NULL)) lua_pushstring(L, buffer); else lua_pushfstring(L, "system error %d\n", error); } static void lsys_unloadlib (void *lib) { FreeLibrary((HMODULE)lib); } static void *lsys_load (lua_State *L, const char *path, int seeglb) { HMODULE lib = LoadLibraryExA(path, NULL, LUA_LLE_FLAGS); (void)(seeglb); /* not used: symbols are 'global' by default */ if (lib == NULL) pusherror(L); return lib; } static lua_CFunction lsys_sym (lua_State *L, void *lib, const char *sym) { lua_CFunction f = (lua_CFunction)(voidf)GetProcAddress((HMODULE)lib, sym); if (f == NULL) pusherror(L); return f; } /* }====================================================== */ #else /* }{ */ /* ** {====================================================== ** Fallback for other systems ** ======================================================= */ #undef LIB_FAIL #define LIB_FAIL "absent" #define DLMSG "dynamic libraries not enabled; check your Lua installation" static void lsys_unloadlib (void *lib) { (void)(lib); /* not used */ } static void *lsys_load (lua_State *L, const char *path, int seeglb) { (void)(path); (void)(seeglb); /* not used */ lua_pushliteral(L, DLMSG); return NULL; } static lua_CFunction lsys_sym (lua_State *L, void *lib, const char *sym) { (void)(lib); (void)(sym); /* not used */ lua_pushliteral(L, DLMSG); return NULL; } /* }====================================================== */ #endif /* } */ /* ** {================================================================== ** Set Paths ** =================================================================== */ /* ** LUA_PATH_VAR and LUA_CPATH_VAR are the names of the environment ** variables that Lua check to set its paths. */ #if !defined(LUA_PATH_VAR) #define LUA_PATH_VAR "LUA_PATH" #endif #if !defined(LUA_CPATH_VAR) #define LUA_CPATH_VAR "LUA_CPATH" #endif /* ** return registry.LUA_NOENV as a boolean */ static int noenv (lua_State *L) { int b; lua_getfield(L, LUA_REGISTRYINDEX, "LUA_NOENV"); b = lua_toboolean(L, -1); lua_pop(L, 1); /* remove value */ return b; } /* ** Set a path */ static void setpath (lua_State *L, const char *fieldname, const char *envname, const char *dft) { const char *dftmark; const char *nver = lua_pushfstring(L, "%s%s", envname, LUA_VERSUFFIX); const char *path = getenv(nver); /* try versioned name */ if (path == NULL) /* no versioned environment variable? */ path = getenv(envname); /* try unversioned name */ if (path == NULL || noenv(L)) /* no environment variable? */ lua_pushstring(L, dft); /* use default */ else if ((dftmark = strstr(path, LUA_PATH_SEP LUA_PATH_SEP)) == NULL) lua_pushstring(L, path); /* nothing to change */ else { /* path contains a ";;": insert default path in its place */ size_t len = strlen(path); luaL_Buffer b; luaL_buffinit(L, &b); if (path < dftmark) { /* is there a prefix before ';;'? */ luaL_addlstring(&b, path, dftmark - path); /* add it */ luaL_addchar(&b, *LUA_PATH_SEP); } luaL_addstring(&b, dft); /* add default */ if (dftmark < path + len - 2) { /* is there a suffix after ';;'? */ luaL_addchar(&b, *LUA_PATH_SEP); luaL_addlstring(&b, dftmark + 2, (path + len - 2) - dftmark); } luaL_pushresult(&b); } setprogdir(L); lua_setfield(L, -3, fieldname); /* package[fieldname] = path value */ lua_pop(L, 1); /* pop versioned variable name ('nver') */ } /* }================================================================== */ /* ** return registry.CLIBS[path] */ static void *checkclib (lua_State *L, const char *path) { void *plib; lua_getfield(L, LUA_REGISTRYINDEX, CLIBS); lua_getfield(L, -1, path); plib = lua_touserdata(L, -1); /* plib = CLIBS[path] */ lua_pop(L, 2); /* pop CLIBS table and 'plib' */ return plib; } /* ** registry.CLIBS[path] = plib -- for queries ** registry.CLIBS[#CLIBS + 1] = plib -- also keep a list of all libraries */ static void addtoclib (lua_State *L, const char *path, void *plib) { lua_getfield(L, LUA_REGISTRYINDEX, CLIBS); lua_pushlightuserdata(L, plib); lua_pushvalue(L, -1); lua_setfield(L, -3, path); /* CLIBS[path] = plib */ lua_rawseti(L, -2, luaL_len(L, -2) + 1); /* CLIBS[#CLIBS + 1] = plib */ lua_pop(L, 1); /* pop CLIBS table */ } /* ** __gc tag method for CLIBS table: calls 'lsys_unloadlib' for all lib ** handles in list CLIBS */ static int gctm (lua_State *L) { lua_Integer n = luaL_len(L, 1); for (; n >= 1; n--) { /* for each handle, in reverse order */ lua_rawgeti(L, 1, n); /* get handle CLIBS[n] */ lsys_unloadlib(lua_touserdata(L, -1)); lua_pop(L, 1); /* pop handle */ } return 0; } /* error codes for 'lookforfunc' */ #define ERRLIB 1 #define ERRFUNC 2 /* ** Look for a C function named 'sym' in a dynamically loaded library ** 'path'. ** First, check whether the library is already loaded; if not, try ** to load it. ** Then, if 'sym' is '*', return true (as library has been loaded). ** Otherwise, look for symbol 'sym' in the library and push a ** C function with that symbol. ** Return 0 and 'true' or a function in the stack; in case of ** errors, return an error code and an error message in the stack. */ static int lookforfunc (lua_State *L, const char *path, const char *sym) { void *reg = checkclib(L, path); /* check loaded C libraries */ if (reg == NULL) { /* must load library? */ reg = lsys_load(L, path, *sym == '*'); /* global symbols if 'sym'=='*' */ if (reg == NULL) return ERRLIB; /* unable to load library */ addtoclib(L, path, reg); } if (*sym == '*') { /* loading only library (no function)? */ lua_pushboolean(L, 1); /* return 'true' */ return 0; /* no errors */ } else { lua_CFunction f = lsys_sym(L, reg, sym); if (f == NULL) return ERRFUNC; /* unable to find function */ lua_pushcfunction(L, f); /* else create new function */ return 0; /* no errors */ } } static int ll_loadlib (lua_State *L) { const char *path = luaL_checkstring(L, 1); const char *init = luaL_checkstring(L, 2); int stat = lookforfunc(L, path, init); if (l_likely(stat == 0)) /* no errors? */ return 1; /* return the loaded function */ else { /* error; error message is on stack top */ luaL_pushfail(L); lua_insert(L, -2); lua_pushstring(L, (stat == ERRLIB) ? LIB_FAIL : "init"); return 3; /* return fail, error message, and where */ } } /* ** {====================================================== ** 'require' function ** ======================================================= */ static int readable (const char *filename) { FILE *f = fopen(filename, "r"); /* try to open file */ if (f == NULL) return 0; /* open failed */ fclose(f); return 1; } /* ** Get the next name in '*path' = 'name1;name2;name3;...', changing ** the ending ';' to '\0' to create a zero-terminated string. Return ** NULL when list ends. */ static const char *getnextfilename (char **path, char *end) { char *sep; char *name = *path; if (name == end) return NULL; /* no more names */ else if (*name == '\0') { /* from previous iteration? */ *name = *LUA_PATH_SEP; /* restore separator */ name++; /* skip it */ } sep = strchr(name, *LUA_PATH_SEP); /* find next separator */ if (sep == NULL) /* separator not found? */ sep = end; /* name goes until the end */ *sep = '\0'; /* finish file name */ *path = sep; /* will start next search from here */ return name; } /* ** Given a path such as ";blabla.so;blublu.so", pushes the string ** ** no file 'blabla.so' ** no file 'blublu.so' */ static void pusherrornotfound (lua_State *L, const char *path) { luaL_Buffer b; luaL_buffinit(L, &b); luaL_addstring(&b, "no file '"); luaL_addgsub(&b, path, LUA_PATH_SEP, "'\n\tno file '"); luaL_addstring(&b, "'"); luaL_pushresult(&b); } static const char *searchpath (lua_State *L, const char *name, const char *path, const char *sep, const char *dirsep) { luaL_Buffer buff; char *pathname; /* path with name inserted */ char *endpathname; /* its end */ const char *filename; /* separator is non-empty and appears in 'name'? */ if (*sep != '\0' && strchr(name, *sep) != NULL) name = luaL_gsub(L, name, sep, dirsep); /* replace it by 'dirsep' */ luaL_buffinit(L, &buff); /* add path to the buffer, replacing marks ('?') with the file name */ luaL_addgsub(&buff, path, LUA_PATH_MARK, name); luaL_addchar(&buff, '\0'); pathname = luaL_buffaddr(&buff); /* writable list of file names */ endpathname = pathname + luaL_bufflen(&buff) - 1; while ((filename = getnextfilename(&pathname, endpathname)) != NULL) { if (readable(filename)) /* does file exist and is readable? */ return lua_pushstring(L, filename); /* save and return name */ } luaL_pushresult(&buff); /* push path to create error message */ pusherrornotfound(L, lua_tostring(L, -1)); /* create error message */ return NULL; /* not found */ } static int ll_searchpath (lua_State *L) { const char *f = searchpath(L, luaL_checkstring(L, 1), luaL_checkstring(L, 2), luaL_optstring(L, 3, "."), luaL_optstring(L, 4, LUA_DIRSEP)); if (f != NULL) return 1; else { /* error message is on top of the stack */ luaL_pushfail(L); lua_insert(L, -2); return 2; /* return fail + error message */ } } static const char *findfile (lua_State *L, const char *name, const char *pname, const char *dirsep) { const char *path; lua_getfield(L, lua_upvalueindex(1), pname); path = lua_tostring(L, -1); if (l_unlikely(path == NULL)) luaL_error(L, "'package.%s' must be a string", pname); return searchpath(L, name, path, ".", dirsep); } static int checkload (lua_State *L, int stat, const char *filename) { if (l_likely(stat)) { /* module loaded successfully? */ lua_pushstring(L, filename); /* will be 2nd argument to module */ return 2; /* return open function and file name */ } else return luaL_error(L, "error loading module '%s' from file '%s':\n\t%s", lua_tostring(L, 1), filename, lua_tostring(L, -1)); } static int searcher_Lua (lua_State *L) { const char *filename; const char *name = luaL_checkstring(L, 1); filename = findfile(L, name, "path", LUA_LSUBSEP); if (filename == NULL) return 1; /* module not found in this path */ return checkload(L, (luaL_loadfile(L, filename) == LUA_OK), filename); } /* ** Try to find a load function for module 'modname' at file 'filename'. ** First, change '.' to '_' in 'modname'; then, if 'modname' has ** the form X-Y (that is, it has an "ignore mark"), build a function ** name "luaopen_X" and look for it. (For compatibility, if that ** fails, it also tries "luaopen_Y".) If there is no ignore mark, ** look for a function named "luaopen_modname". */ static int loadfunc (lua_State *L, const char *filename, const char *modname) { const char *openfunc; const char *mark; modname = luaL_gsub(L, modname, ".", LUA_OFSEP); mark = strchr(modname, *LUA_IGMARK); if (mark) { int stat; openfunc = lua_pushlstring(L, modname, mark - modname); openfunc = lua_pushfstring(L, LUA_POF"%s", openfunc); stat = lookforfunc(L, filename, openfunc); if (stat != ERRFUNC) return stat; modname = mark + 1; /* else go ahead and try old-style name */ } openfunc = lua_pushfstring(L, LUA_POF"%s", modname); return lookforfunc(L, filename, openfunc); } static int searcher_C (lua_State *L) { const char *name = luaL_checkstring(L, 1); const char *filename = findfile(L, name, "cpath", LUA_CSUBSEP); if (filename == NULL) return 1; /* module not found in this path */ return checkload(L, (loadfunc(L, filename, name) == 0), filename); } static int searcher_Croot (lua_State *L) { const char *filename; const char *name = luaL_checkstring(L, 1); const char *p = strchr(name, '.'); int stat; if (p == NULL) return 0; /* is root */ lua_pushlstring(L, name, p - name); filename = findfile(L, lua_tostring(L, -1), "cpath", LUA_CSUBSEP); if (filename == NULL) return 1; /* root not found */ if ((stat = loadfunc(L, filename, name)) != 0) { if (stat != ERRFUNC) return checkload(L, 0, filename); /* real error */ else { /* open function not found */ lua_pushfstring(L, "no module '%s' in file '%s'", name, filename); return 1; } } lua_pushstring(L, filename); /* will be 2nd argument to module */ return 2; } static int searcher_preload (lua_State *L) { const char *name = luaL_checkstring(L, 1); lua_getfield(L, LUA_REGISTRYINDEX, LUA_PRELOAD_TABLE); if (lua_getfield(L, -1, name) == LUA_TNIL) { /* not found? */ lua_pushfstring(L, "no field package.preload['%s']", name); return 1; } else { lua_pushliteral(L, ":preload:"); return 2; } } static void findloader (lua_State *L, const char *name) { int i; luaL_Buffer msg; /* to build error message */ /* push 'package.searchers' to index 3 in the stack */ if (l_unlikely(lua_getfield(L, lua_upvalueindex(1), "searchers") != LUA_TTABLE)) luaL_error(L, "'package.searchers' must be a table"); luaL_buffinit(L, &msg); /* iterate over available searchers to find a loader */ for (i = 1; ; i++) { luaL_addstring(&msg, "\n\t"); /* error-message prefix */ if (l_unlikely(lua_rawgeti(L, 3, i) == LUA_TNIL)) { /* no more searchers? */ lua_pop(L, 1); /* remove nil */ luaL_buffsub(&msg, 2); /* remove prefix */ luaL_pushresult(&msg); /* create error message */ luaL_error(L, "module '%s' not found:%s", name, lua_tostring(L, -1)); } lua_pushstring(L, name); lua_call(L, 1, 2); /* call it */ if (lua_isfunction(L, -2)) /* did it find a loader? */ return; /* module loader found */ else if (lua_isstring(L, -2)) { /* searcher returned error message? */ lua_pop(L, 1); /* remove extra return */ luaL_addvalue(&msg); /* concatenate error message */ } else { /* no error message */ lua_pop(L, 2); /* remove both returns */ luaL_buffsub(&msg, 2); /* remove prefix */ } } } static int ll_require (lua_State *L) { const char *name = luaL_checkstring(L, 1); lua_settop(L, 1); /* LOADED table will be at index 2 */ lua_getfield(L, LUA_REGISTRYINDEX, LUA_LOADED_TABLE); lua_getfield(L, 2, name); /* LOADED[name] */ if (lua_toboolean(L, -1)) /* is it there? */ return 1; /* package is already loaded */ /* else must load package */ lua_pop(L, 1); /* remove 'getfield' result */ findloader(L, name); lua_rotate(L, -2, 1); /* function <-> loader data */ lua_pushvalue(L, 1); /* name is 1st argument to module loader */ lua_pushvalue(L, -3); /* loader data is 2nd argument */ /* stack: ...; loader data; loader function; mod. name; loader data */ lua_call(L, 2, 1); /* run loader to load module */ /* stack: ...; loader data; result from loader */ if (!lua_isnil(L, -1)) /* non-nil return? */ lua_setfield(L, 2, name); /* LOADED[name] = returned value */ else lua_pop(L, 1); /* pop nil */ if (lua_getfield(L, 2, name) == LUA_TNIL) { /* module set no value? */ lua_pushboolean(L, 1); /* use true as result */ lua_copy(L, -1, -2); /* replace loader result */ lua_setfield(L, 2, name); /* LOADED[name] = true */ } lua_rotate(L, -2, 1); /* loader data <-> module result */ return 2; /* return module result and loader data */ } /* }====================================================== */ static const luaL_Reg pk_funcs[] = { {"loadlib", ll_loadlib}, {"searchpath", ll_searchpath}, /* placeholders */ {"preload", NULL}, {"cpath", NULL}, {"path", NULL}, {"searchers", NULL}, {"loaded", NULL}, {NULL, NULL} }; static const luaL_Reg ll_funcs[] = { {"require", ll_require}, {NULL, NULL} }; static void createsearcherstable (lua_State *L) { static const lua_CFunction searchers[] = {searcher_preload, searcher_Lua, searcher_C, searcher_Croot, NULL}; int i; /* create 'searchers' table */ lua_createtable(L, sizeof(searchers)/sizeof(searchers[0]) - 1, 0); /* fill it with predefined searchers */ for (i=0; searchers[i] != NULL; i++) { lua_pushvalue(L, -2); /* set 'package' as upvalue for all searchers */ lua_pushcclosure(L, searchers[i], 1); lua_rawseti(L, -2, i+1); } lua_setfield(L, -2, "searchers"); /* put it in field 'searchers' */ } /* ** create table CLIBS to keep track of loaded C libraries, ** setting a finalizer to close all libraries when closing state. */ static void createclibstable (lua_State *L) { luaL_getsubtable(L, LUA_REGISTRYINDEX, CLIBS); /* create CLIBS table */ lua_createtable(L, 0, 1); /* create metatable for CLIBS */ lua_pushcfunction(L, gctm); lua_setfield(L, -2, "__gc"); /* set finalizer for CLIBS table */ lua_setmetatable(L, -2); } LUAMOD_API int luaopen_package (lua_State *L) { createclibstable(L); luaL_newlib(L, pk_funcs); /* create 'package' table */ createsearcherstable(L); /* set paths */ setpath(L, "path", LUA_PATH_VAR, LUA_PATH_DEFAULT); setpath(L, "cpath", LUA_CPATH_VAR, LUA_CPATH_DEFAULT); /* store config information */ lua_pushliteral(L, LUA_DIRSEP "\n" LUA_PATH_SEP "\n" LUA_PATH_MARK "\n" LUA_EXEC_DIR "\n" LUA_IGMARK "\n"); lua_setfield(L, -2, "config"); /* set field 'loaded' */ luaL_getsubtable(L, LUA_REGISTRYINDEX, LUA_LOADED_TABLE); lua_setfield(L, -2, "loaded"); /* set field 'preload' */ luaL_getsubtable(L, LUA_REGISTRYINDEX, LUA_PRELOAD_TABLE); lua_setfield(L, -2, "preload"); lua_pushglobaltable(L); lua_pushvalue(L, -2); /* set 'package' as upvalue for next lib */ luaL_setfuncs(L, ll_funcs, 1); /* open lib into global table */ lua_pop(L, 1); /* pop global table */ return 1; /* return 'package' table */ } /* ** $Id: loslib.c $ ** Standard Operating System library ** See Copyright Notice in lua.h */ #define loslib_c #define LUA_LIB /*#include "lprefix.h"*/ #include #include #include #include #include /*#include "lua.h"*/ /*#include "lauxlib.h"*/ /*#include "lualib.h"*/ /* ** {================================================================== ** List of valid conversion specifiers for the 'strftime' function; ** options are grouped by length; group of length 2 start with '||'. ** =================================================================== */ #if !defined(LUA_STRFTIMEOPTIONS) /* { */ /* options for ANSI C 89 (only 1-char options) */ #define L_STRFTIMEC89 "aAbBcdHIjmMpSUwWxXyYZ%" /* options for ISO C 99 and POSIX */ #define L_STRFTIMEC99 "aAbBcCdDeFgGhHIjmMnprRStTuUVwWxXyYzZ%" \ "||" "EcECExEXEyEY" "OdOeOHOIOmOMOSOuOUOVOwOWOy" /* two-char options */ /* options for Windows */ #define L_STRFTIMEWIN "aAbBcdHIjmMpSUwWxXyYzZ%" \ "||" "#c#x#d#H#I#j#m#M#S#U#w#W#y#Y" /* two-char options */ #if defined(LUA_USE_WINDOWS) #define LUA_STRFTIMEOPTIONS L_STRFTIMEWIN #elif defined(LUA_USE_C89) #define LUA_STRFTIMEOPTIONS L_STRFTIMEC89 #else /* C99 specification */ #define LUA_STRFTIMEOPTIONS L_STRFTIMEC99 #endif #endif /* } */ /* }================================================================== */ /* ** {================================================================== ** Configuration for time-related stuff ** =================================================================== */ /* ** type to represent time_t in Lua */ #if !defined(LUA_NUMTIME) /* { */ #define l_timet lua_Integer #define l_pushtime(L,t) lua_pushinteger(L,(lua_Integer)(t)) #define l_gettime(L,arg) luaL_checkinteger(L, arg) #else /* }{ */ #define l_timet lua_Number #define l_pushtime(L,t) lua_pushnumber(L,(lua_Number)(t)) #define l_gettime(L,arg) luaL_checknumber(L, arg) #endif /* } */ #if !defined(l_gmtime) /* { */ /* ** By default, Lua uses gmtime/localtime, except when POSIX is available, ** where it uses gmtime_r/localtime_r */ #if defined(LUA_USE_POSIX) /* { */ #define l_gmtime(t,r) gmtime_r(t,r) #define l_localtime(t,r) localtime_r(t,r) #else /* }{ */ /* ISO C definitions */ #define l_gmtime(t,r) ((void)(r)->tm_sec, gmtime(t)) #define l_localtime(t,r) ((void)(r)->tm_sec, localtime(t)) #endif /* } */ #endif /* } */ /* }================================================================== */ /* ** {================================================================== ** Configuration for 'tmpnam': ** By default, Lua uses tmpnam except when POSIX is available, where ** it uses mkstemp. ** =================================================================== */ #if !defined(lua_tmpnam) /* { */ #if defined(LUA_USE_POSIX) /* { */ #include #define LUA_TMPNAMBUFSIZE 32 #if !defined(LUA_TMPNAMTEMPLATE) #define LUA_TMPNAMTEMPLATE "/tmp/lua_XXXXXX" #endif #define lua_tmpnam(b,e) { \ strcpy(b, LUA_TMPNAMTEMPLATE); \ e = mkstemp(b); \ if (e != -1) close(e); \ e = (e == -1); } #else /* }{ */ /* ISO C definitions */ #define LUA_TMPNAMBUFSIZE L_tmpnam #define lua_tmpnam(b,e) { e = (tmpnam(b) == NULL); } #endif /* } */ #endif /* } */ /* }================================================================== */ static int os_execute (lua_State *L) { const char *cmd = luaL_optstring(L, 1, NULL); int stat; errno = 0; stat = system(cmd); if (cmd != NULL) return luaL_execresult(L, stat); else { lua_pushboolean(L, stat); /* true if there is a shell */ return 1; } } static int os_remove (lua_State *L) { const char *filename = luaL_checkstring(L, 1); return luaL_fileresult(L, remove(filename) == 0, filename); } static int os_rename (lua_State *L) { const char *fromname = luaL_checkstring(L, 1); const char *toname = luaL_checkstring(L, 2); return luaL_fileresult(L, rename(fromname, toname) == 0, NULL); } static int os_tmpname (lua_State *L) { char buff[LUA_TMPNAMBUFSIZE]; int err; lua_tmpnam(buff, err); if (l_unlikely(err)) return luaL_error(L, "unable to generate a unique filename"); lua_pushstring(L, buff); return 1; } static int os_getenv (lua_State *L) { lua_pushstring(L, getenv(luaL_checkstring(L, 1))); /* if NULL push nil */ return 1; } static int os_clock (lua_State *L) { lua_pushnumber(L, ((lua_Number)clock())/(lua_Number)CLOCKS_PER_SEC); return 1; } /* ** {====================================================== ** Time/Date operations ** { year=%Y, month=%m, day=%d, hour=%H, min=%M, sec=%S, ** wday=%w+1, yday=%j, isdst=? } ** ======================================================= */ /* ** About the overflow check: an overflow cannot occur when time ** is represented by a lua_Integer, because either lua_Integer is ** large enough to represent all int fields or it is not large enough ** to represent a time that cause a field to overflow. However, if ** times are represented as doubles and lua_Integer is int, then the ** time 0x1.e1853b0d184f6p+55 would cause an overflow when adding 1900 ** to compute the year. */ static void setfield (lua_State *L, const char *key, int value, int delta) { #if (defined(LUA_NUMTIME) && LUA_MAXINTEGER <= INT_MAX) if (l_unlikely(value > LUA_MAXINTEGER - delta)) luaL_error(L, "field '%s' is out-of-bound", key); #endif lua_pushinteger(L, (lua_Integer)value + delta); lua_setfield(L, -2, key); } static void setboolfield (lua_State *L, const char *key, int value) { if (value < 0) /* undefined? */ return; /* does not set field */ lua_pushboolean(L, value); lua_setfield(L, -2, key); } /* ** Set all fields from structure 'tm' in the table on top of the stack */ static void setallfields (lua_State *L, struct tm *stm) { setfield(L, "year", stm->tm_year, 1900); setfield(L, "month", stm->tm_mon, 1); setfield(L, "day", stm->tm_mday, 0); setfield(L, "hour", stm->tm_hour, 0); setfield(L, "min", stm->tm_min, 0); setfield(L, "sec", stm->tm_sec, 0); setfield(L, "yday", stm->tm_yday, 1); setfield(L, "wday", stm->tm_wday, 1); setboolfield(L, "isdst", stm->tm_isdst); } static int getboolfield (lua_State *L, const char *key) { int res; res = (lua_getfield(L, -1, key) == LUA_TNIL) ? -1 : lua_toboolean(L, -1); lua_pop(L, 1); return res; } static int getfield (lua_State *L, const char *key, int d, int delta) { int isnum; int t = lua_getfield(L, -1, key); /* get field and its type */ lua_Integer res = lua_tointegerx(L, -1, &isnum); if (!isnum) { /* field is not an integer? */ if (l_unlikely(t != LUA_TNIL)) /* some other value? */ return luaL_error(L, "field '%s' is not an integer", key); else if (l_unlikely(d < 0)) /* absent field; no default? */ return luaL_error(L, "field '%s' missing in date table", key); res = d; } else { /* unsigned avoids overflow when lua_Integer has 32 bits */ if (!(res >= 0 ? (lua_Unsigned)res <= (lua_Unsigned)INT_MAX + delta : (lua_Integer)INT_MIN + delta <= res)) return luaL_error(L, "field '%s' is out-of-bound", key); res -= delta; } lua_pop(L, 1); return (int)res; } static const char *checkoption (lua_State *L, const char *conv, ptrdiff_t convlen, char *buff) { const char *option = LUA_STRFTIMEOPTIONS; int oplen = 1; /* length of options being checked */ for (; *option != '\0' && oplen <= convlen; option += oplen) { if (*option == '|') /* next block? */ oplen++; /* will check options with next length (+1) */ else if (memcmp(conv, option, oplen) == 0) { /* match? */ memcpy(buff, conv, oplen); /* copy valid option to buffer */ buff[oplen] = '\0'; return conv + oplen; /* return next item */ } } luaL_argerror(L, 1, lua_pushfstring(L, "invalid conversion specifier '%%%s'", conv)); return conv; /* to avoid warnings */ } static time_t l_checktime (lua_State *L, int arg) { l_timet t = l_gettime(L, arg); luaL_argcheck(L, (time_t)t == t, arg, "time out-of-bounds"); return (time_t)t; } /* maximum size for an individual 'strftime' item */ #define SIZETIMEFMT 250 static int os_date (lua_State *L) { size_t slen; const char *s = luaL_optlstring(L, 1, "%c", &slen); time_t t = luaL_opt(L, l_checktime, 2, time(NULL)); const char *se = s + slen; /* 's' end */ struct tm tmr, *stm; if (*s == '!') { /* UTC? */ stm = l_gmtime(&t, &tmr); s++; /* skip '!' */ } else stm = l_localtime(&t, &tmr); if (stm == NULL) /* invalid date? */ return luaL_error(L, "date result cannot be represented in this installation"); if (strcmp(s, "*t") == 0) { lua_createtable(L, 0, 9); /* 9 = number of fields */ setallfields(L, stm); } else { char cc[4]; /* buffer for individual conversion specifiers */ luaL_Buffer b; cc[0] = '%'; luaL_buffinit(L, &b); while (s < se) { if (*s != '%') /* not a conversion specifier? */ luaL_addchar(&b, *s++); else { size_t reslen; char *buff = luaL_prepbuffsize(&b, SIZETIMEFMT); s++; /* skip '%' */ s = checkoption(L, s, se - s, cc + 1); /* copy specifier to 'cc' */ reslen = strftime(buff, SIZETIMEFMT, cc, stm); luaL_addsize(&b, reslen); } } luaL_pushresult(&b); } return 1; } static int os_time (lua_State *L) { time_t t; if (lua_isnoneornil(L, 1)) /* called without args? */ t = time(NULL); /* get current time */ else { struct tm ts; luaL_checktype(L, 1, LUA_TTABLE); lua_settop(L, 1); /* make sure table is at the top */ ts.tm_year = getfield(L, "year", -1, 1900); ts.tm_mon = getfield(L, "month", -1, 1); ts.tm_mday = getfield(L, "day", -1, 0); ts.tm_hour = getfield(L, "hour", 12, 0); ts.tm_min = getfield(L, "min", 0, 0); ts.tm_sec = getfield(L, "sec", 0, 0); ts.tm_isdst = getboolfield(L, "isdst"); t = mktime(&ts); setallfields(L, &ts); /* update fields with normalized values */ } if (t != (time_t)(l_timet)t || t == (time_t)(-1)) return luaL_error(L, "time result cannot be represented in this installation"); l_pushtime(L, t); return 1; } static int os_difftime (lua_State *L) { time_t t1 = l_checktime(L, 1); time_t t2 = l_checktime(L, 2); lua_pushnumber(L, (lua_Number)difftime(t1, t2)); return 1; } /* }====================================================== */ static int os_setlocale (lua_State *L) { static const int cat[] = {LC_ALL, LC_COLLATE, LC_CTYPE, LC_MONETARY, LC_NUMERIC, LC_TIME}; static const char *const catnames[] = {"all", "collate", "ctype", "monetary", "numeric", "time", NULL}; const char *l = luaL_optstring(L, 1, NULL); int op = luaL_checkoption(L, 2, "all", catnames); lua_pushstring(L, setlocale(cat[op], l)); return 1; } static int os_exit (lua_State *L) { int status; if (lua_isboolean(L, 1)) status = (lua_toboolean(L, 1) ? EXIT_SUCCESS : EXIT_FAILURE); else status = (int)luaL_optinteger(L, 1, EXIT_SUCCESS); if (lua_toboolean(L, 2)) lua_close(L); if (L) exit(status); /* 'if' to avoid warnings for unreachable 'return' */ return 0; } static const luaL_Reg syslib[] = { {"clock", os_clock}, {"date", os_date}, {"difftime", os_difftime}, {"execute", os_execute}, {"exit", os_exit}, {"getenv", os_getenv}, {"remove", os_remove}, {"rename", os_rename}, {"setlocale", os_setlocale}, {"time", os_time}, {"tmpname", os_tmpname}, {NULL, NULL} }; /* }====================================================== */ LUAMOD_API int luaopen_os (lua_State *L) { luaL_newlib(L, syslib); return 1; } /* ** $Id: lstrlib.c $ ** Standard library for string operations and pattern-matching ** See Copyright Notice in lua.h */ #define lstrlib_c #define LUA_LIB /*#include "lprefix.h"*/ #include #include #include #include #include #include #include #include #include /*#include "lua.h"*/ /*#include "lauxlib.h"*/ /*#include "lualib.h"*/ /* ** maximum number of captures that a pattern can do during ** pattern-matching. This limit is arbitrary, but must fit in ** an unsigned char. */ #if !defined(LUA_MAXCAPTURES) #define LUA_MAXCAPTURES 32 #endif /* macro to 'unsign' a character */ #define uchar(c) ((unsigned char)(c)) /* ** Some sizes are better limited to fit in 'int', but must also fit in ** 'size_t'. (We assume that 'lua_Integer' cannot be smaller than 'int'.) */ #define MAX_SIZET ((size_t)(~(size_t)0)) #define MAXSIZE \ (sizeof(size_t) < sizeof(int) ? MAX_SIZET : (size_t)(INT_MAX)) static int str_len (lua_State *L) { size_t l; luaL_checklstring(L, 1, &l); lua_pushinteger(L, (lua_Integer)l); return 1; } /* ** translate a relative initial string position ** (negative means back from end): clip result to [1, inf). ** The length of any string in Lua must fit in a lua_Integer, ** so there are no overflows in the casts. ** The inverted comparison avoids a possible overflow ** computing '-pos'. */ static size_t posrelatI (lua_Integer pos, size_t len) { if (pos > 0) return (size_t)pos; else if (pos == 0) return 1; else if (pos < -(lua_Integer)len) /* inverted comparison */ return 1; /* clip to 1 */ else return len + (size_t)pos + 1; } /* ** Gets an optional ending string position from argument 'arg', ** with default value 'def'. ** Negative means back from end: clip result to [0, len] */ static size_t getendpos (lua_State *L, int arg, lua_Integer def, size_t len) { lua_Integer pos = luaL_optinteger(L, arg, def); if (pos > (lua_Integer)len) return len; else if (pos >= 0) return (size_t)pos; else if (pos < -(lua_Integer)len) return 0; else return len + (size_t)pos + 1; } static int str_sub (lua_State *L) { size_t l; const char *s = luaL_checklstring(L, 1, &l); size_t start = posrelatI(luaL_checkinteger(L, 2), l); size_t end = getendpos(L, 3, -1, l); if (start <= end) lua_pushlstring(L, s + start - 1, (end - start) + 1); else lua_pushliteral(L, ""); return 1; } static int str_reverse (lua_State *L) { size_t l, i; luaL_Buffer b; const char *s = luaL_checklstring(L, 1, &l); char *p = luaL_buffinitsize(L, &b, l); for (i = 0; i < l; i++) p[i] = s[l - i - 1]; luaL_pushresultsize(&b, l); return 1; } static int str_lower (lua_State *L) { size_t l; size_t i; luaL_Buffer b; const char *s = luaL_checklstring(L, 1, &l); char *p = luaL_buffinitsize(L, &b, l); for (i=0; i MAXSIZE / n)) return luaL_error(L, "resulting string too large"); else { size_t totallen = (size_t)n * l + (size_t)(n - 1) * lsep; luaL_Buffer b; char *p = luaL_buffinitsize(L, &b, totallen); while (n-- > 1) { /* first n-1 copies (followed by separator) */ memcpy(p, s, l * sizeof(char)); p += l; if (lsep > 0) { /* empty 'memcpy' is not that cheap */ memcpy(p, sep, lsep * sizeof(char)); p += lsep; } } memcpy(p, s, l * sizeof(char)); /* last copy (not followed by separator) */ luaL_pushresultsize(&b, totallen); } return 1; } static int str_byte (lua_State *L) { size_t l; const char *s = luaL_checklstring(L, 1, &l); lua_Integer pi = luaL_optinteger(L, 2, 1); size_t posi = posrelatI(pi, l); size_t pose = getendpos(L, 3, pi, l); int n, i; if (posi > pose) return 0; /* empty interval; return no values */ if (l_unlikely(pose - posi >= (size_t)INT_MAX)) /* arithmetic overflow? */ return luaL_error(L, "string slice too long"); n = (int)(pose - posi) + 1; luaL_checkstack(L, n, "string slice too long"); for (i=0; iinit) { state->init = 1; luaL_buffinit(L, &state->B); } luaL_addlstring(&state->B, (const char *)b, size); return 0; } static int str_dump (lua_State *L) { struct str_Writer state; int strip = lua_toboolean(L, 2); luaL_checktype(L, 1, LUA_TFUNCTION); lua_settop(L, 1); /* ensure function is on the top of the stack */ state.init = 0; if (l_unlikely(lua_dump(L, writer, &state, strip) != 0)) return luaL_error(L, "unable to dump given function"); luaL_pushresult(&state.B); return 1; } /* ** {====================================================== ** METAMETHODS ** ======================================================= */ #if defined(LUA_NOCVTS2N) /* { */ /* no coercion from strings to numbers */ static const luaL_Reg stringmetamethods[] = { {"__index", NULL}, /* placeholder */ {NULL, NULL} }; #else /* }{ */ static int tonum (lua_State *L, int arg) { if (lua_type(L, arg) == LUA_TNUMBER) { /* already a number? */ lua_pushvalue(L, arg); return 1; } else { /* check whether it is a numerical string */ size_t len; const char *s = lua_tolstring(L, arg, &len); return (s != NULL && lua_stringtonumber(L, s) == len + 1); } } static void trymt (lua_State *L, const char *mtname) { lua_settop(L, 2); /* back to the original arguments */ if (l_unlikely(lua_type(L, 2) == LUA_TSTRING || !luaL_getmetafield(L, 2, mtname))) luaL_error(L, "attempt to %s a '%s' with a '%s'", mtname + 2, luaL_typename(L, -2), luaL_typename(L, -1)); lua_insert(L, -3); /* put metamethod before arguments */ lua_call(L, 2, 1); /* call metamethod */ } static int arith (lua_State *L, int op, const char *mtname) { if (tonum(L, 1) && tonum(L, 2)) lua_arith(L, op); /* result will be on the top */ else trymt(L, mtname); return 1; } static int arith_add (lua_State *L) { return arith(L, LUA_OPADD, "__add"); } static int arith_sub (lua_State *L) { return arith(L, LUA_OPSUB, "__sub"); } static int arith_mul (lua_State *L) { return arith(L, LUA_OPMUL, "__mul"); } static int arith_mod (lua_State *L) { return arith(L, LUA_OPMOD, "__mod"); } static int arith_pow (lua_State *L) { return arith(L, LUA_OPPOW, "__pow"); } static int arith_div (lua_State *L) { return arith(L, LUA_OPDIV, "__div"); } static int arith_idiv (lua_State *L) { return arith(L, LUA_OPIDIV, "__idiv"); } static int arith_unm (lua_State *L) { return arith(L, LUA_OPUNM, "__unm"); } static const luaL_Reg stringmetamethods[] = { {"__add", arith_add}, {"__sub", arith_sub}, {"__mul", arith_mul}, {"__mod", arith_mod}, {"__pow", arith_pow}, {"__div", arith_div}, {"__idiv", arith_idiv}, {"__unm", arith_unm}, {"__index", NULL}, /* placeholder */ {NULL, NULL} }; #endif /* } */ /* }====================================================== */ /* ** {====================================================== ** PATTERN MATCHING ** ======================================================= */ #define CAP_UNFINISHED (-1) #define CAP_POSITION (-2) typedef struct MatchState { const char *src_init; /* init of source string */ const char *src_end; /* end ('\0') of source string */ const char *p_end; /* end ('\0') of pattern */ lua_State *L; int matchdepth; /* control for recursive depth (to avoid C stack overflow) */ unsigned char level; /* total number of captures (finished or unfinished) */ struct { const char *init; ptrdiff_t len; } capture[LUA_MAXCAPTURES]; } MatchState; /* recursive function */ static const char *match (MatchState *ms, const char *s, const char *p); /* maximum recursion depth for 'match' */ #if !defined(MAXCCALLS) #define MAXCCALLS 200 #endif #define L_ESC '%' #define SPECIALS "^$*+?.([%-" static int check_capture (MatchState *ms, int l) { l -= '1'; if (l_unlikely(l < 0 || l >= ms->level || ms->capture[l].len == CAP_UNFINISHED)) return luaL_error(ms->L, "invalid capture index %%%d", l + 1); return l; } static int capture_to_close (MatchState *ms) { int level = ms->level; for (level--; level>=0; level--) if (ms->capture[level].len == CAP_UNFINISHED) return level; return luaL_error(ms->L, "invalid pattern capture"); } static const char *classend (MatchState *ms, const char *p) { switch (*p++) { case L_ESC: { if (l_unlikely(p == ms->p_end)) luaL_error(ms->L, "malformed pattern (ends with '%%')"); return p+1; } case '[': { if (*p == '^') p++; do { /* look for a ']' */ if (l_unlikely(p == ms->p_end)) luaL_error(ms->L, "malformed pattern (missing ']')"); if (*(p++) == L_ESC && p < ms->p_end) p++; /* skip escapes (e.g. '%]') */ } while (*p != ']'); return p+1; } default: { return p; } } } static int match_class (int c, int cl) { int res; switch (tolower(cl)) { case 'a' : res = isalpha(c); break; case 'c' : res = iscntrl(c); break; case 'd' : res = isdigit(c); break; case 'g' : res = isgraph(c); break; case 'l' : res = islower(c); break; case 'p' : res = ispunct(c); break; case 's' : res = isspace(c); break; case 'u' : res = isupper(c); break; case 'w' : res = isalnum(c); break; case 'x' : res = isxdigit(c); break; case 'z' : res = (c == 0); break; /* deprecated option */ default: return (cl == c); } return (islower(cl) ? res : !res); } static int matchbracketclass (int c, const char *p, const char *ec) { int sig = 1; if (*(p+1) == '^') { sig = 0; p++; /* skip the '^' */ } while (++p < ec) { if (*p == L_ESC) { p++; if (match_class(c, uchar(*p))) return sig; } else if ((*(p+1) == '-') && (p+2 < ec)) { p+=2; if (uchar(*(p-2)) <= c && c <= uchar(*p)) return sig; } else if (uchar(*p) == c) return sig; } return !sig; } static int singlematch (MatchState *ms, const char *s, const char *p, const char *ep) { if (s >= ms->src_end) return 0; else { int c = uchar(*s); switch (*p) { case '.': return 1; /* matches any char */ case L_ESC: return match_class(c, uchar(*(p+1))); case '[': return matchbracketclass(c, p, ep-1); default: return (uchar(*p) == c); } } } static const char *matchbalance (MatchState *ms, const char *s, const char *p) { if (l_unlikely(p >= ms->p_end - 1)) luaL_error(ms->L, "malformed pattern (missing arguments to '%%b')"); if (*s != *p) return NULL; else { int b = *p; int e = *(p+1); int cont = 1; while (++s < ms->src_end) { if (*s == e) { if (--cont == 0) return s+1; } else if (*s == b) cont++; } } return NULL; /* string ends out of balance */ } static const char *max_expand (MatchState *ms, const char *s, const char *p, const char *ep) { ptrdiff_t i = 0; /* counts maximum expand for item */ while (singlematch(ms, s + i, p, ep)) i++; /* keeps trying to match with the maximum repetitions */ while (i>=0) { const char *res = match(ms, (s+i), ep+1); if (res) return res; i--; /* else didn't match; reduce 1 repetition to try again */ } return NULL; } static const char *min_expand (MatchState *ms, const char *s, const char *p, const char *ep) { for (;;) { const char *res = match(ms, s, ep+1); if (res != NULL) return res; else if (singlematch(ms, s, p, ep)) s++; /* try with one more repetition */ else return NULL; } } static const char *start_capture (MatchState *ms, const char *s, const char *p, int what) { const char *res; int level = ms->level; if (level >= LUA_MAXCAPTURES) luaL_error(ms->L, "too many captures"); ms->capture[level].init = s; ms->capture[level].len = what; ms->level = level+1; if ((res=match(ms, s, p)) == NULL) /* match failed? */ ms->level--; /* undo capture */ return res; } static const char *end_capture (MatchState *ms, const char *s, const char *p) { int l = capture_to_close(ms); const char *res; ms->capture[l].len = s - ms->capture[l].init; /* close capture */ if ((res = match(ms, s, p)) == NULL) /* match failed? */ ms->capture[l].len = CAP_UNFINISHED; /* undo capture */ return res; } static const char *match_capture (MatchState *ms, const char *s, int l) { size_t len; l = check_capture(ms, l); len = ms->capture[l].len; if ((size_t)(ms->src_end-s) >= len && memcmp(ms->capture[l].init, s, len) == 0) return s+len; else return NULL; } static const char *match (MatchState *ms, const char *s, const char *p) { if (l_unlikely(ms->matchdepth-- == 0)) luaL_error(ms->L, "pattern too complex"); init: /* using goto's to optimize tail recursion */ if (p != ms->p_end) { /* end of pattern? */ switch (*p) { case '(': { /* start capture */ if (*(p + 1) == ')') /* position capture? */ s = start_capture(ms, s, p + 2, CAP_POSITION); else s = start_capture(ms, s, p + 1, CAP_UNFINISHED); break; } case ')': { /* end capture */ s = end_capture(ms, s, p + 1); break; } case '$': { if ((p + 1) != ms->p_end) /* is the '$' the last char in pattern? */ goto dflt; /* no; go to default */ s = (s == ms->src_end) ? s : NULL; /* check end of string */ break; } case L_ESC: { /* escaped sequences not in the format class[*+?-]? */ switch (*(p + 1)) { case 'b': { /* balanced string? */ s = matchbalance(ms, s, p + 2); if (s != NULL) { p += 4; goto init; /* return match(ms, s, p + 4); */ } /* else fail (s == NULL) */ break; } case 'f': { /* frontier? */ const char *ep; char previous; p += 2; if (l_unlikely(*p != '[')) luaL_error(ms->L, "missing '[' after '%%f' in pattern"); ep = classend(ms, p); /* points to what is next */ previous = (s == ms->src_init) ? '\0' : *(s - 1); if (!matchbracketclass(uchar(previous), p, ep - 1) && matchbracketclass(uchar(*s), p, ep - 1)) { p = ep; goto init; /* return match(ms, s, ep); */ } s = NULL; /* match failed */ break; } case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': { /* capture results (%0-%9)? */ s = match_capture(ms, s, uchar(*(p + 1))); if (s != NULL) { p += 2; goto init; /* return match(ms, s, p + 2) */ } break; } default: goto dflt; } break; } default: dflt: { /* pattern class plus optional suffix */ const char *ep = classend(ms, p); /* points to optional suffix */ /* does not match at least once? */ if (!singlematch(ms, s, p, ep)) { if (*ep == '*' || *ep == '?' || *ep == '-') { /* accept empty? */ p = ep + 1; goto init; /* return match(ms, s, ep + 1); */ } else /* '+' or no suffix */ s = NULL; /* fail */ } else { /* matched once */ switch (*ep) { /* handle optional suffix */ case '?': { /* optional */ const char *res; if ((res = match(ms, s + 1, ep + 1)) != NULL) s = res; else { p = ep + 1; goto init; /* else return match(ms, s, ep + 1); */ } break; } case '+': /* 1 or more repetitions */ s++; /* 1 match already done */ /* FALLTHROUGH */ case '*': /* 0 or more repetitions */ s = max_expand(ms, s, p, ep); break; case '-': /* 0 or more repetitions (minimum) */ s = min_expand(ms, s, p, ep); break; default: /* no suffix */ s++; p = ep; goto init; /* return match(ms, s + 1, ep); */ } } break; } } } ms->matchdepth++; return s; } static const char *lmemfind (const char *s1, size_t l1, const char *s2, size_t l2) { if (l2 == 0) return s1; /* empty strings are everywhere */ else if (l2 > l1) return NULL; /* avoids a negative 'l1' */ else { const char *init; /* to search for a '*s2' inside 's1' */ l2--; /* 1st char will be checked by 'memchr' */ l1 = l1-l2; /* 's2' cannot be found after that */ while (l1 > 0 && (init = (const char *)memchr(s1, *s2, l1)) != NULL) { init++; /* 1st char is already checked */ if (memcmp(init, s2+1, l2) == 0) return init-1; else { /* correct 'l1' and 's1' to try again */ l1 -= init-s1; s1 = init; } } return NULL; /* not found */ } } /* ** get information about the i-th capture. If there are no captures ** and 'i==0', return information about the whole match, which ** is the range 's'..'e'. If the capture is a string, return ** its length and put its address in '*cap'. If it is an integer ** (a position), push it on the stack and return CAP_POSITION. */ static size_t get_onecapture (MatchState *ms, int i, const char *s, const char *e, const char **cap) { if (i >= ms->level) { if (l_unlikely(i != 0)) luaL_error(ms->L, "invalid capture index %%%d", i + 1); *cap = s; return e - s; } else { ptrdiff_t capl = ms->capture[i].len; *cap = ms->capture[i].init; if (l_unlikely(capl == CAP_UNFINISHED)) luaL_error(ms->L, "unfinished capture"); else if (capl == CAP_POSITION) lua_pushinteger(ms->L, (ms->capture[i].init - ms->src_init) + 1); return capl; } } /* ** Push the i-th capture on the stack. */ static void push_onecapture (MatchState *ms, int i, const char *s, const char *e) { const char *cap; ptrdiff_t l = get_onecapture(ms, i, s, e, &cap); if (l != CAP_POSITION) lua_pushlstring(ms->L, cap, l); /* else position was already pushed */ } static int push_captures (MatchState *ms, const char *s, const char *e) { int i; int nlevels = (ms->level == 0 && s) ? 1 : ms->level; luaL_checkstack(ms->L, nlevels, "too many captures"); for (i = 0; i < nlevels; i++) push_onecapture(ms, i, s, e); return nlevels; /* number of strings pushed */ } /* check whether pattern has no special characters */ static int nospecials (const char *p, size_t l) { size_t upto = 0; do { if (strpbrk(p + upto, SPECIALS)) return 0; /* pattern has a special character */ upto += strlen(p + upto) + 1; /* may have more after \0 */ } while (upto <= l); return 1; /* no special chars found */ } static void prepstate (MatchState *ms, lua_State *L, const char *s, size_t ls, const char *p, size_t lp) { ms->L = L; ms->matchdepth = MAXCCALLS; ms->src_init = s; ms->src_end = s + ls; ms->p_end = p + lp; } static void reprepstate (MatchState *ms) { ms->level = 0; lua_assert(ms->matchdepth == MAXCCALLS); } static int str_find_aux (lua_State *L, int find) { size_t ls, lp; const char *s = luaL_checklstring(L, 1, &ls); const char *p = luaL_checklstring(L, 2, &lp); size_t init = posrelatI(luaL_optinteger(L, 3, 1), ls) - 1; if (init > ls) { /* start after string's end? */ luaL_pushfail(L); /* cannot find anything */ return 1; } /* explicit request or no special characters? */ if (find && (lua_toboolean(L, 4) || nospecials(p, lp))) { /* do a plain search */ const char *s2 = lmemfind(s + init, ls - init, p, lp); if (s2) { lua_pushinteger(L, (s2 - s) + 1); lua_pushinteger(L, (s2 - s) + lp); return 2; } } else { MatchState ms; const char *s1 = s + init; int anchor = (*p == '^'); if (anchor) { p++; lp--; /* skip anchor character */ } prepstate(&ms, L, s, ls, p, lp); do { const char *res; reprepstate(&ms); if ((res=match(&ms, s1, p)) != NULL) { if (find) { lua_pushinteger(L, (s1 - s) + 1); /* start */ lua_pushinteger(L, res - s); /* end */ return push_captures(&ms, NULL, 0) + 2; } else return push_captures(&ms, s1, res); } } while (s1++ < ms.src_end && !anchor); } luaL_pushfail(L); /* not found */ return 1; } static int str_find (lua_State *L) { return str_find_aux(L, 1); } static int str_match (lua_State *L) { return str_find_aux(L, 0); } /* state for 'gmatch' */ typedef struct GMatchState { const char *src; /* current position */ const char *p; /* pattern */ const char *lastmatch; /* end of last match */ MatchState ms; /* match state */ } GMatchState; static int gmatch_aux (lua_State *L) { GMatchState *gm = (GMatchState *)lua_touserdata(L, lua_upvalueindex(3)); const char *src; gm->ms.L = L; for (src = gm->src; src <= gm->ms.src_end; src++) { const char *e; reprepstate(&gm->ms); if ((e = match(&gm->ms, src, gm->p)) != NULL && e != gm->lastmatch) { gm->src = gm->lastmatch = e; return push_captures(&gm->ms, src, e); } } return 0; /* not found */ } static int gmatch (lua_State *L) { size_t ls, lp; const char *s = luaL_checklstring(L, 1, &ls); const char *p = luaL_checklstring(L, 2, &lp); size_t init = posrelatI(luaL_optinteger(L, 3, 1), ls) - 1; GMatchState *gm; lua_settop(L, 2); /* keep strings on closure to avoid being collected */ gm = (GMatchState *)lua_newuserdatauv(L, sizeof(GMatchState), 0); if (init > ls) /* start after string's end? */ init = ls + 1; /* avoid overflows in 's + init' */ prepstate(&gm->ms, L, s, ls, p, lp); gm->src = s + init; gm->p = p; gm->lastmatch = NULL; lua_pushcclosure(L, gmatch_aux, 3); return 1; } static void add_s (MatchState *ms, luaL_Buffer *b, const char *s, const char *e) { size_t l; lua_State *L = ms->L; const char *news = lua_tolstring(L, 3, &l); const char *p; while ((p = (char *)memchr(news, L_ESC, l)) != NULL) { luaL_addlstring(b, news, p - news); p++; /* skip ESC */ if (*p == L_ESC) /* '%%' */ luaL_addchar(b, *p); else if (*p == '0') /* '%0' */ luaL_addlstring(b, s, e - s); else if (isdigit(uchar(*p))) { /* '%n' */ const char *cap; ptrdiff_t resl = get_onecapture(ms, *p - '1', s, e, &cap); if (resl == CAP_POSITION) luaL_addvalue(b); /* add position to accumulated result */ else luaL_addlstring(b, cap, resl); } else luaL_error(L, "invalid use of '%c' in replacement string", L_ESC); l -= p + 1 - news; news = p + 1; } luaL_addlstring(b, news, l); } /* ** Add the replacement value to the string buffer 'b'. ** Return true if the original string was changed. (Function calls and ** table indexing resulting in nil or false do not change the subject.) */ static int add_value (MatchState *ms, luaL_Buffer *b, const char *s, const char *e, int tr) { lua_State *L = ms->L; switch (tr) { case LUA_TFUNCTION: { /* call the function */ int n; lua_pushvalue(L, 3); /* push the function */ n = push_captures(ms, s, e); /* all captures as arguments */ lua_call(L, n, 1); /* call it */ break; } case LUA_TTABLE: { /* index the table */ push_onecapture(ms, 0, s, e); /* first capture is the index */ lua_gettable(L, 3); break; } default: { /* LUA_TNUMBER or LUA_TSTRING */ add_s(ms, b, s, e); /* add value to the buffer */ return 1; /* something changed */ } } if (!lua_toboolean(L, -1)) { /* nil or false? */ lua_pop(L, 1); /* remove value */ luaL_addlstring(b, s, e - s); /* keep original text */ return 0; /* no changes */ } else if (l_unlikely(!lua_isstring(L, -1))) return luaL_error(L, "invalid replacement value (a %s)", luaL_typename(L, -1)); else { luaL_addvalue(b); /* add result to accumulator */ return 1; /* something changed */ } } static int str_gsub (lua_State *L) { size_t srcl, lp; const char *src = luaL_checklstring(L, 1, &srcl); /* subject */ const char *p = luaL_checklstring(L, 2, &lp); /* pattern */ const char *lastmatch = NULL; /* end of last match */ int tr = lua_type(L, 3); /* replacement type */ lua_Integer max_s = luaL_optinteger(L, 4, srcl + 1); /* max replacements */ int anchor = (*p == '^'); lua_Integer n = 0; /* replacement count */ int changed = 0; /* change flag */ MatchState ms; luaL_Buffer b; luaL_argexpected(L, tr == LUA_TNUMBER || tr == LUA_TSTRING || tr == LUA_TFUNCTION || tr == LUA_TTABLE, 3, "string/function/table"); luaL_buffinit(L, &b); if (anchor) { p++; lp--; /* skip anchor character */ } prepstate(&ms, L, src, srcl, p, lp); while (n < max_s) { const char *e; reprepstate(&ms); /* (re)prepare state for new match */ if ((e = match(&ms, src, p)) != NULL && e != lastmatch) { /* match? */ n++; changed = add_value(&ms, &b, src, e, tr) | changed; src = lastmatch = e; } else if (src < ms.src_end) /* otherwise, skip one character */ luaL_addchar(&b, *src++); else break; /* end of subject */ if (anchor) break; } if (!changed) /* no changes? */ lua_pushvalue(L, 1); /* return original string */ else { /* something changed */ luaL_addlstring(&b, src, ms.src_end-src); luaL_pushresult(&b); /* create and return new string */ } lua_pushinteger(L, n); /* number of substitutions */ return 2; } /* }====================================================== */ /* ** {====================================================== ** STRING FORMAT ** ======================================================= */ #if !defined(lua_number2strx) /* { */ /* ** Hexadecimal floating-point formatter */ #define SIZELENMOD (sizeof(LUA_NUMBER_FRMLEN)/sizeof(char)) /* ** Number of bits that goes into the first digit. It can be any value ** between 1 and 4; the following definition tries to align the number ** to nibble boundaries by making what is left after that first digit a ** multiple of 4. */ #define L_NBFD ((l_floatatt(MANT_DIG) - 1)%4 + 1) /* ** Add integer part of 'x' to buffer and return new 'x' */ static lua_Number adddigit (char *buff, int n, lua_Number x) { lua_Number dd = l_mathop(floor)(x); /* get integer part from 'x' */ int d = (int)dd; buff[n] = (d < 10 ? d + '0' : d - 10 + 'a'); /* add to buffer */ return x - dd; /* return what is left */ } static int num2straux (char *buff, int sz, lua_Number x) { /* if 'inf' or 'NaN', format it like '%g' */ if (x != x || x == (lua_Number)HUGE_VAL || x == -(lua_Number)HUGE_VAL) return l_sprintf(buff, sz, LUA_NUMBER_FMT, (LUAI_UACNUMBER)x); else if (x == 0) { /* can be -0... */ /* create "0" or "-0" followed by exponent */ return l_sprintf(buff, sz, LUA_NUMBER_FMT "x0p+0", (LUAI_UACNUMBER)x); } else { int e; lua_Number m = l_mathop(frexp)(x, &e); /* 'x' fraction and exponent */ int n = 0; /* character count */ if (m < 0) { /* is number negative? */ buff[n++] = '-'; /* add sign */ m = -m; /* make it positive */ } buff[n++] = '0'; buff[n++] = 'x'; /* add "0x" */ m = adddigit(buff, n++, m * (1 << L_NBFD)); /* add first digit */ e -= L_NBFD; /* this digit goes before the radix point */ if (m > 0) { /* more digits? */ buff[n++] = lua_getlocaledecpoint(); /* add radix point */ do { /* add as many digits as needed */ m = adddigit(buff, n++, m * 16); } while (m > 0); } n += l_sprintf(buff + n, sz - n, "p%+d", e); /* add exponent */ lua_assert(n < sz); return n; } } static int lua_number2strx (lua_State *L, char *buff, int sz, const char *fmt, lua_Number x) { int n = num2straux(buff, sz, x); if (fmt[SIZELENMOD] == 'A') { int i; for (i = 0; i < n; i++) buff[i] = toupper(uchar(buff[i])); } else if (l_unlikely(fmt[SIZELENMOD] != 'a')) return luaL_error(L, "modifiers for format '%%a'/'%%A' not implemented"); return n; } #endif /* } */ /* ** Maximum size for items formatted with '%f'. This size is produced ** by format('%.99f', -maxfloat), and is equal to 99 + 3 ('-', '.', ** and '\0') + number of decimal digits to represent maxfloat (which ** is maximum exponent + 1). (99+3+1, adding some extra, 110) */ #define MAX_ITEMF (110 + l_floatatt(MAX_10_EXP)) /* ** All formats except '%f' do not need that large limit. The other ** float formats use exponents, so that they fit in the 99 limit for ** significant digits; 's' for large strings and 'q' add items directly ** to the buffer; all integer formats also fit in the 99 limit. The ** worst case are floats: they may need 99 significant digits, plus ** '0x', '-', '.', 'e+XXXX', and '\0'. Adding some extra, 120. */ #define MAX_ITEM 120 /* valid flags in a format specification */ #if !defined(L_FMTFLAGSF) /* valid flags for a, A, e, E, f, F, g, and G conversions */ #define L_FMTFLAGSF "-+#0 " /* valid flags for o, x, and X conversions */ #define L_FMTFLAGSX "-#0" /* valid flags for d and i conversions */ #define L_FMTFLAGSI "-+0 " /* valid flags for u conversions */ #define L_FMTFLAGSU "-0" /* valid flags for c, p, and s conversions */ #define L_FMTFLAGSC "-" #endif /* ** Maximum size of each format specification (such as "%-099.99d"): ** Initial '%', flags (up to 5), width (2), period, precision (2), ** length modifier (8), conversion specifier, and final '\0', plus some ** extra. */ #define MAX_FORMAT 32 static void addquoted (luaL_Buffer *b, const char *s, size_t len) { luaL_addchar(b, '"'); while (len--) { if (*s == '"' || *s == '\\' || *s == '\n') { luaL_addchar(b, '\\'); luaL_addchar(b, *s); } else if (iscntrl(uchar(*s))) { char buff[10]; if (!isdigit(uchar(*(s+1)))) l_sprintf(buff, sizeof(buff), "\\%d", (int)uchar(*s)); else l_sprintf(buff, sizeof(buff), "\\%03d", (int)uchar(*s)); luaL_addstring(b, buff); } else luaL_addchar(b, *s); s++; } luaL_addchar(b, '"'); } /* ** Serialize a floating-point number in such a way that it can be ** scanned back by Lua. Use hexadecimal format for "common" numbers ** (to preserve precision); inf, -inf, and NaN are handled separately. ** (NaN cannot be expressed as a numeral, so we write '(0/0)' for it.) */ static int quotefloat (lua_State *L, char *buff, lua_Number n) { const char *s; /* for the fixed representations */ if (n == (lua_Number)HUGE_VAL) /* inf? */ s = "1e9999"; else if (n == -(lua_Number)HUGE_VAL) /* -inf? */ s = "-1e9999"; else if (n != n) /* NaN? */ s = "(0/0)"; else { /* format number as hexadecimal */ int nb = lua_number2strx(L, buff, MAX_ITEM, "%" LUA_NUMBER_FRMLEN "a", n); /* ensures that 'buff' string uses a dot as the radix character */ if (memchr(buff, '.', nb) == NULL) { /* no dot? */ char point = lua_getlocaledecpoint(); /* try locale point */ char *ppoint = (char *)memchr(buff, point, nb); if (ppoint) *ppoint = '.'; /* change it to a dot */ } return nb; } /* for the fixed representations */ return l_sprintf(buff, MAX_ITEM, "%s", s); } static void addliteral (lua_State *L, luaL_Buffer *b, int arg) { switch (lua_type(L, arg)) { case LUA_TSTRING: { size_t len; const char *s = lua_tolstring(L, arg, &len); addquoted(b, s, len); break; } case LUA_TNUMBER: { char *buff = luaL_prepbuffsize(b, MAX_ITEM); int nb; if (!lua_isinteger(L, arg)) /* float? */ nb = quotefloat(L, buff, lua_tonumber(L, arg)); else { /* integers */ lua_Integer n = lua_tointeger(L, arg); const char *format = (n == LUA_MININTEGER) /* corner case? */ ? "0x%" LUA_INTEGER_FRMLEN "x" /* use hex */ : LUA_INTEGER_FMT; /* else use default format */ nb = l_sprintf(buff, MAX_ITEM, format, (LUAI_UACINT)n); } luaL_addsize(b, nb); break; } case LUA_TNIL: case LUA_TBOOLEAN: { luaL_tolstring(L, arg, NULL); luaL_addvalue(b); break; } default: { luaL_argerror(L, arg, "value has no literal form"); } } } static const char *get2digits (const char *s) { if (isdigit(uchar(*s))) { s++; if (isdigit(uchar(*s))) s++; /* (2 digits at most) */ } return s; } /* ** Check whether a conversion specification is valid. When called, ** first character in 'form' must be '%' and last character must ** be a valid conversion specifier. 'flags' are the accepted flags; ** 'precision' signals whether to accept a precision. */ static void checkformat (lua_State *L, const char *form, const char *flags, int precision) { const char *spec = form + 1; /* skip '%' */ spec += strspn(spec, flags); /* skip flags */ if (*spec != '0') { /* a width cannot start with '0' */ spec = get2digits(spec); /* skip width */ if (*spec == '.' && precision) { spec++; spec = get2digits(spec); /* skip precision */ } } if (!isalpha(uchar(*spec))) /* did not go to the end? */ luaL_error(L, "invalid conversion specification: '%s'", form); } /* ** Get a conversion specification and copy it to 'form'. ** Return the address of its last character. */ static const char *getformat (lua_State *L, const char *strfrmt, char *form) { /* spans flags, width, and precision ('0' is included as a flag) */ size_t len = strspn(strfrmt, L_FMTFLAGSF "123456789."); len++; /* adds following character (should be the specifier) */ /* still needs space for '%', '\0', plus a length modifier */ if (len >= MAX_FORMAT - 10) luaL_error(L, "invalid format (too long)"); *(form++) = '%'; memcpy(form, strfrmt, len * sizeof(char)); *(form + len) = '\0'; return strfrmt + len - 1; } /* ** add length modifier into formats */ static void addlenmod (char *form, const char *lenmod) { size_t l = strlen(form); size_t lm = strlen(lenmod); char spec = form[l - 1]; strcpy(form + l - 1, lenmod); form[l + lm - 1] = spec; form[l + lm] = '\0'; } static int str_format (lua_State *L) { int top = lua_gettop(L); int arg = 1; size_t sfl; const char *strfrmt = luaL_checklstring(L, arg, &sfl); const char *strfrmt_end = strfrmt+sfl; const char *flags; luaL_Buffer b; luaL_buffinit(L, &b); while (strfrmt < strfrmt_end) { if (*strfrmt != L_ESC) luaL_addchar(&b, *strfrmt++); else if (*++strfrmt == L_ESC) luaL_addchar(&b, *strfrmt++); /* %% */ else { /* format item */ char form[MAX_FORMAT]; /* to store the format ('%...') */ int maxitem = MAX_ITEM; /* maximum length for the result */ char *buff = luaL_prepbuffsize(&b, maxitem); /* to put result */ int nb = 0; /* number of bytes in result */ if (++arg > top) return luaL_argerror(L, arg, "no value"); strfrmt = getformat(L, strfrmt, form); switch (*strfrmt++) { case 'c': { checkformat(L, form, L_FMTFLAGSC, 0); nb = l_sprintf(buff, maxitem, form, (int)luaL_checkinteger(L, arg)); break; } case 'd': case 'i': flags = L_FMTFLAGSI; goto intcase; case 'u': flags = L_FMTFLAGSU; goto intcase; case 'o': case 'x': case 'X': flags = L_FMTFLAGSX; intcase: { lua_Integer n = luaL_checkinteger(L, arg); checkformat(L, form, flags, 1); addlenmod(form, LUA_INTEGER_FRMLEN); nb = l_sprintf(buff, maxitem, form, (LUAI_UACINT)n); break; } case 'a': case 'A': checkformat(L, form, L_FMTFLAGSF, 1); addlenmod(form, LUA_NUMBER_FRMLEN); nb = lua_number2strx(L, buff, maxitem, form, luaL_checknumber(L, arg)); break; case 'f': maxitem = MAX_ITEMF; /* extra space for '%f' */ buff = luaL_prepbuffsize(&b, maxitem); /* FALLTHROUGH */ case 'e': case 'E': case 'g': case 'G': { lua_Number n = luaL_checknumber(L, arg); checkformat(L, form, L_FMTFLAGSF, 1); addlenmod(form, LUA_NUMBER_FRMLEN); nb = l_sprintf(buff, maxitem, form, (LUAI_UACNUMBER)n); break; } case 'p': { const void *p = lua_topointer(L, arg); checkformat(L, form, L_FMTFLAGSC, 0); if (p == NULL) { /* avoid calling 'printf' with argument NULL */ p = "(null)"; /* result */ form[strlen(form) - 1] = 's'; /* format it as a string */ } nb = l_sprintf(buff, maxitem, form, p); break; } case 'q': { if (form[2] != '\0') /* modifiers? */ return luaL_error(L, "specifier '%%q' cannot have modifiers"); addliteral(L, &b, arg); break; } case 's': { size_t l; const char *s = luaL_tolstring(L, arg, &l); if (form[2] == '\0') /* no modifiers? */ luaL_addvalue(&b); /* keep entire string */ else { luaL_argcheck(L, l == strlen(s), arg, "string contains zeros"); checkformat(L, form, L_FMTFLAGSC, 1); if (strchr(form, '.') == NULL && l >= 100) { /* no precision and string is too long to be formatted */ luaL_addvalue(&b); /* keep entire string */ } else { /* format the string into 'buff' */ nb = l_sprintf(buff, maxitem, form, s); lua_pop(L, 1); /* remove result from 'luaL_tolstring' */ } } break; } default: { /* also treat cases 'pnLlh' */ return luaL_error(L, "invalid conversion '%s' to 'format'", form); } } lua_assert(nb < maxitem); luaL_addsize(&b, nb); } } luaL_pushresult(&b); return 1; } /* }====================================================== */ /* ** {====================================================== ** PACK/UNPACK ** ======================================================= */ /* value used for padding */ #if !defined(LUAL_PACKPADBYTE) #define LUAL_PACKPADBYTE 0x00 #endif /* maximum size for the binary representation of an integer */ #define MAXINTSIZE 16 /* number of bits in a character */ #define NB CHAR_BIT /* mask for one character (NB 1's) */ #define MC ((1 << NB) - 1) /* size of a lua_Integer */ #define SZINT ((int)sizeof(lua_Integer)) /* dummy union to get native endianness */ static const union { int dummy; char little; /* true iff machine is little endian */ } nativeendian = {1}; /* ** information to pack/unpack stuff */ typedef struct Header { lua_State *L; int islittle; int maxalign; } Header; /* ** options for pack/unpack */ typedef enum KOption { Kint, /* signed integers */ Kuint, /* unsigned integers */ Kfloat, /* single-precision floating-point numbers */ Knumber, /* Lua "native" floating-point numbers */ Kdouble, /* double-precision floating-point numbers */ Kchar, /* fixed-length strings */ Kstring, /* strings with prefixed length */ Kzstr, /* zero-terminated strings */ Kpadding, /* padding */ Kpaddalign, /* padding for alignment */ Knop /* no-op (configuration or spaces) */ } KOption; /* ** Read an integer numeral from string 'fmt' or return 'df' if ** there is no numeral */ static int digit (int c) { return '0' <= c && c <= '9'; } static int getnum (const char **fmt, int df) { if (!digit(**fmt)) /* no number? */ return df; /* return default value */ else { int a = 0; do { a = a*10 + (*((*fmt)++) - '0'); } while (digit(**fmt) && a <= ((int)MAXSIZE - 9)/10); return a; } } /* ** Read an integer numeral and raises an error if it is larger ** than the maximum size for integers. */ static int getnumlimit (Header *h, const char **fmt, int df) { int sz = getnum(fmt, df); if (l_unlikely(sz > MAXINTSIZE || sz <= 0)) return luaL_error(h->L, "integral size (%d) out of limits [1,%d]", sz, MAXINTSIZE); return sz; } /* ** Initialize Header */ static void initheader (lua_State *L, Header *h) { h->L = L; h->islittle = nativeendian.little; h->maxalign = 1; } /* ** Read and classify next option. 'size' is filled with option's size. */ static KOption getoption (Header *h, const char **fmt, int *size) { /* dummy structure to get native alignment requirements */ struct cD { char c; union { LUAI_MAXALIGN; } u; }; int opt = *((*fmt)++); *size = 0; /* default */ switch (opt) { case 'b': *size = sizeof(char); return Kint; case 'B': *size = sizeof(char); return Kuint; case 'h': *size = sizeof(short); return Kint; case 'H': *size = sizeof(short); return Kuint; case 'l': *size = sizeof(long); return Kint; case 'L': *size = sizeof(long); return Kuint; case 'j': *size = sizeof(lua_Integer); return Kint; case 'J': *size = sizeof(lua_Integer); return Kuint; case 'T': *size = sizeof(size_t); return Kuint; case 'f': *size = sizeof(float); return Kfloat; case 'n': *size = sizeof(lua_Number); return Knumber; case 'd': *size = sizeof(double); return Kdouble; case 'i': *size = getnumlimit(h, fmt, sizeof(int)); return Kint; case 'I': *size = getnumlimit(h, fmt, sizeof(int)); return Kuint; case 's': *size = getnumlimit(h, fmt, sizeof(size_t)); return Kstring; case 'c': *size = getnum(fmt, -1); if (l_unlikely(*size == -1)) luaL_error(h->L, "missing size for format option 'c'"); return Kchar; case 'z': return Kzstr; case 'x': *size = 1; return Kpadding; case 'X': return Kpaddalign; case ' ': break; case '<': h->islittle = 1; break; case '>': h->islittle = 0; break; case '=': h->islittle = nativeendian.little; break; case '!': { const int maxalign = offsetof(struct cD, u); h->maxalign = getnumlimit(h, fmt, maxalign); break; } default: luaL_error(h->L, "invalid format option '%c'", opt); } return Knop; } /* ** Read, classify, and fill other details about the next option. ** 'psize' is filled with option's size, 'notoalign' with its ** alignment requirements. ** Local variable 'size' gets the size to be aligned. (Kpadal option ** always gets its full alignment, other options are limited by ** the maximum alignment ('maxalign'). Kchar option needs no alignment ** despite its size. */ static KOption getdetails (Header *h, size_t totalsize, const char **fmt, int *psize, int *ntoalign) { KOption opt = getoption(h, fmt, psize); int align = *psize; /* usually, alignment follows size */ if (opt == Kpaddalign) { /* 'X' gets alignment from following option */ if (**fmt == '\0' || getoption(h, fmt, &align) == Kchar || align == 0) luaL_argerror(h->L, 1, "invalid next option for option 'X'"); } if (align <= 1 || opt == Kchar) /* need no alignment? */ *ntoalign = 0; else { if (align > h->maxalign) /* enforce maximum alignment */ align = h->maxalign; if (l_unlikely((align & (align - 1)) != 0)) /* not a power of 2? */ luaL_argerror(h->L, 1, "format asks for alignment not power of 2"); *ntoalign = (align - (int)(totalsize & (align - 1))) & (align - 1); } return opt; } /* ** Pack integer 'n' with 'size' bytes and 'islittle' endianness. ** The final 'if' handles the case when 'size' is larger than ** the size of a Lua integer, correcting the extra sign-extension ** bytes if necessary (by default they would be zeros). */ static void packint (luaL_Buffer *b, lua_Unsigned n, int islittle, int size, int neg) { char *buff = luaL_prepbuffsize(b, size); int i; buff[islittle ? 0 : size - 1] = (char)(n & MC); /* first byte */ for (i = 1; i < size; i++) { n >>= NB; buff[islittle ? i : size - 1 - i] = (char)(n & MC); } if (neg && size > SZINT) { /* negative number need sign extension? */ for (i = SZINT; i < size; i++) /* correct extra bytes */ buff[islittle ? i : size - 1 - i] = (char)MC; } luaL_addsize(b, size); /* add result to buffer */ } /* ** Copy 'size' bytes from 'src' to 'dest', correcting endianness if ** given 'islittle' is different from native endianness. */ static void copywithendian (char *dest, const char *src, int size, int islittle) { if (islittle == nativeendian.little) memcpy(dest, src, size); else { dest += size - 1; while (size-- != 0) *(dest--) = *(src++); } } static int str_pack (lua_State *L) { luaL_Buffer b; Header h; const char *fmt = luaL_checkstring(L, 1); /* format string */ int arg = 1; /* current argument to pack */ size_t totalsize = 0; /* accumulate total size of result */ initheader(L, &h); lua_pushnil(L); /* mark to separate arguments from string buffer */ luaL_buffinit(L, &b); while (*fmt != '\0') { int size, ntoalign; KOption opt = getdetails(&h, totalsize, &fmt, &size, &ntoalign); totalsize += ntoalign + size; while (ntoalign-- > 0) luaL_addchar(&b, LUAL_PACKPADBYTE); /* fill alignment */ arg++; switch (opt) { case Kint: { /* signed integers */ lua_Integer n = luaL_checkinteger(L, arg); if (size < SZINT) { /* need overflow check? */ lua_Integer lim = (lua_Integer)1 << ((size * NB) - 1); luaL_argcheck(L, -lim <= n && n < lim, arg, "integer overflow"); } packint(&b, (lua_Unsigned)n, h.islittle, size, (n < 0)); break; } case Kuint: { /* unsigned integers */ lua_Integer n = luaL_checkinteger(L, arg); if (size < SZINT) /* need overflow check? */ luaL_argcheck(L, (lua_Unsigned)n < ((lua_Unsigned)1 << (size * NB)), arg, "unsigned overflow"); packint(&b, (lua_Unsigned)n, h.islittle, size, 0); break; } case Kfloat: { /* C float */ float f = (float)luaL_checknumber(L, arg); /* get argument */ char *buff = luaL_prepbuffsize(&b, sizeof(f)); /* move 'f' to final result, correcting endianness if needed */ copywithendian(buff, (char *)&f, sizeof(f), h.islittle); luaL_addsize(&b, size); break; } case Knumber: { /* Lua float */ lua_Number f = luaL_checknumber(L, arg); /* get argument */ char *buff = luaL_prepbuffsize(&b, sizeof(f)); /* move 'f' to final result, correcting endianness if needed */ copywithendian(buff, (char *)&f, sizeof(f), h.islittle); luaL_addsize(&b, size); break; } case Kdouble: { /* C double */ double f = (double)luaL_checknumber(L, arg); /* get argument */ char *buff = luaL_prepbuffsize(&b, sizeof(f)); /* move 'f' to final result, correcting endianness if needed */ copywithendian(buff, (char *)&f, sizeof(f), h.islittle); luaL_addsize(&b, size); break; } case Kchar: { /* fixed-size string */ size_t len; const char *s = luaL_checklstring(L, arg, &len); luaL_argcheck(L, len <= (size_t)size, arg, "string longer than given size"); luaL_addlstring(&b, s, len); /* add string */ while (len++ < (size_t)size) /* pad extra space */ luaL_addchar(&b, LUAL_PACKPADBYTE); break; } case Kstring: { /* strings with length count */ size_t len; const char *s = luaL_checklstring(L, arg, &len); luaL_argcheck(L, size >= (int)sizeof(size_t) || len < ((size_t)1 << (size * NB)), arg, "string length does not fit in given size"); packint(&b, (lua_Unsigned)len, h.islittle, size, 0); /* pack length */ luaL_addlstring(&b, s, len); totalsize += len; break; } case Kzstr: { /* zero-terminated string */ size_t len; const char *s = luaL_checklstring(L, arg, &len); luaL_argcheck(L, strlen(s) == len, arg, "string contains zeros"); luaL_addlstring(&b, s, len); luaL_addchar(&b, '\0'); /* add zero at the end */ totalsize += len + 1; break; } case Kpadding: luaL_addchar(&b, LUAL_PACKPADBYTE); /* FALLTHROUGH */ case Kpaddalign: case Knop: arg--; /* undo increment */ break; } } luaL_pushresult(&b); return 1; } static int str_packsize (lua_State *L) { Header h; const char *fmt = luaL_checkstring(L, 1); /* format string */ size_t totalsize = 0; /* accumulate total size of result */ initheader(L, &h); while (*fmt != '\0') { int size, ntoalign; KOption opt = getdetails(&h, totalsize, &fmt, &size, &ntoalign); luaL_argcheck(L, opt != Kstring && opt != Kzstr, 1, "variable-length format"); size += ntoalign; /* total space used by option */ luaL_argcheck(L, totalsize <= MAXSIZE - size, 1, "format result too large"); totalsize += size; } lua_pushinteger(L, (lua_Integer)totalsize); return 1; } /* ** Unpack an integer with 'size' bytes and 'islittle' endianness. ** If size is smaller than the size of a Lua integer and integer ** is signed, must do sign extension (propagating the sign to the ** higher bits); if size is larger than the size of a Lua integer, ** it must check the unread bytes to see whether they do not cause an ** overflow. */ static lua_Integer unpackint (lua_State *L, const char *str, int islittle, int size, int issigned) { lua_Unsigned res = 0; int i; int limit = (size <= SZINT) ? size : SZINT; for (i = limit - 1; i >= 0; i--) { res <<= NB; res |= (lua_Unsigned)(unsigned char)str[islittle ? i : size - 1 - i]; } if (size < SZINT) { /* real size smaller than lua_Integer? */ if (issigned) { /* needs sign extension? */ lua_Unsigned mask = (lua_Unsigned)1 << (size*NB - 1); res = ((res ^ mask) - mask); /* do sign extension */ } } else if (size > SZINT) { /* must check unread bytes */ int mask = (!issigned || (lua_Integer)res >= 0) ? 0 : MC; for (i = limit; i < size; i++) { if (l_unlikely((unsigned char)str[islittle ? i : size - 1 - i] != mask)) luaL_error(L, "%d-byte integer does not fit into Lua Integer", size); } } return (lua_Integer)res; } static int str_unpack (lua_State *L) { Header h; const char *fmt = luaL_checkstring(L, 1); size_t ld; const char *data = luaL_checklstring(L, 2, &ld); size_t pos = posrelatI(luaL_optinteger(L, 3, 1), ld) - 1; int n = 0; /* number of results */ luaL_argcheck(L, pos <= ld, 3, "initial position out of string"); initheader(L, &h); while (*fmt != '\0') { int size, ntoalign; KOption opt = getdetails(&h, pos, &fmt, &size, &ntoalign); luaL_argcheck(L, (size_t)ntoalign + size <= ld - pos, 2, "data string too short"); pos += ntoalign; /* skip alignment */ /* stack space for item + next position */ luaL_checkstack(L, 2, "too many results"); n++; switch (opt) { case Kint: case Kuint: { lua_Integer res = unpackint(L, data + pos, h.islittle, size, (opt == Kint)); lua_pushinteger(L, res); break; } case Kfloat: { float f; copywithendian((char *)&f, data + pos, sizeof(f), h.islittle); lua_pushnumber(L, (lua_Number)f); break; } case Knumber: { lua_Number f; copywithendian((char *)&f, data + pos, sizeof(f), h.islittle); lua_pushnumber(L, f); break; } case Kdouble: { double f; copywithendian((char *)&f, data + pos, sizeof(f), h.islittle); lua_pushnumber(L, (lua_Number)f); break; } case Kchar: { lua_pushlstring(L, data + pos, size); break; } case Kstring: { size_t len = (size_t)unpackint(L, data + pos, h.islittle, size, 0); luaL_argcheck(L, len <= ld - pos - size, 2, "data string too short"); lua_pushlstring(L, data + pos + size, len); pos += len; /* skip string */ break; } case Kzstr: { size_t len = strlen(data + pos); luaL_argcheck(L, pos + len < ld, 2, "unfinished string for format 'z'"); lua_pushlstring(L, data + pos, len); pos += len + 1; /* skip string plus final '\0' */ break; } case Kpaddalign: case Kpadding: case Knop: n--; /* undo increment */ break; } pos += size; } lua_pushinteger(L, pos + 1); /* next position */ return n + 1; } /* }====================================================== */ static const luaL_Reg strlib[] = { {"byte", str_byte}, {"char", str_char}, {"dump", str_dump}, {"find", str_find}, {"format", str_format}, {"gmatch", gmatch}, {"gsub", str_gsub}, {"len", str_len}, {"lower", str_lower}, {"match", str_match}, {"rep", str_rep}, {"reverse", str_reverse}, {"sub", str_sub}, {"upper", str_upper}, {"pack", str_pack}, {"packsize", str_packsize}, {"unpack", str_unpack}, {NULL, NULL} }; static void createmetatable (lua_State *L) { /* table to be metatable for strings */ luaL_newlibtable(L, stringmetamethods); luaL_setfuncs(L, stringmetamethods, 0); lua_pushliteral(L, ""); /* dummy string */ lua_pushvalue(L, -2); /* copy table */ lua_setmetatable(L, -2); /* set table as metatable for strings */ lua_pop(L, 1); /* pop dummy string */ lua_pushvalue(L, -2); /* get string library */ lua_setfield(L, -2, "__index"); /* metatable.__index = string */ lua_pop(L, 1); /* pop metatable */ } /* ** Open string library */ LUAMOD_API int luaopen_string (lua_State *L) { luaL_newlib(L, strlib); createmetatable(L); return 1; } /* ** $Id: ltablib.c $ ** Library for Table Manipulation ** See Copyright Notice in lua.h */ #define ltablib_c #define LUA_LIB /*#include "lprefix.h"*/ #include #include #include /*#include "lua.h"*/ /*#include "lauxlib.h"*/ /*#include "lualib.h"*/ /* ** Operations that an object must define to mimic a table ** (some functions only need some of them) */ #define TAB_R 1 /* read */ #define TAB_W 2 /* write */ #define TAB_L 4 /* length */ #define TAB_RW (TAB_R | TAB_W) /* read/write */ #define aux_getn(L,n,w) (checktab(L, n, (w) | TAB_L), luaL_len(L, n)) static int checkfield (lua_State *L, const char *key, int n) { lua_pushstring(L, key); return (lua_rawget(L, -n) != LUA_TNIL); } /* ** Check that 'arg' either is a table or can behave like one (that is, ** has a metatable with the required metamethods) */ static void checktab (lua_State *L, int arg, int what) { if (lua_type(L, arg) != LUA_TTABLE) { /* is it not a table? */ int n = 1; /* number of elements to pop */ if (lua_getmetatable(L, arg) && /* must have metatable */ (!(what & TAB_R) || checkfield(L, "__index", ++n)) && (!(what & TAB_W) || checkfield(L, "__newindex", ++n)) && (!(what & TAB_L) || checkfield(L, "__len", ++n))) { lua_pop(L, n); /* pop metatable and tested metamethods */ } else luaL_checktype(L, arg, LUA_TTABLE); /* force an error */ } } static int tinsert (lua_State *L) { lua_Integer pos; /* where to insert new element */ lua_Integer e = aux_getn(L, 1, TAB_RW); e = luaL_intop(+, e, 1); /* first empty element */ switch (lua_gettop(L)) { case 2: { /* called with only 2 arguments */ pos = e; /* insert new element at the end */ break; } case 3: { lua_Integer i; pos = luaL_checkinteger(L, 2); /* 2nd argument is the position */ /* check whether 'pos' is in [1, e] */ luaL_argcheck(L, (lua_Unsigned)pos - 1u < (lua_Unsigned)e, 2, "position out of bounds"); for (i = e; i > pos; i--) { /* move up elements */ lua_geti(L, 1, i - 1); lua_seti(L, 1, i); /* t[i] = t[i - 1] */ } break; } default: { return luaL_error(L, "wrong number of arguments to 'insert'"); } } lua_seti(L, 1, pos); /* t[pos] = v */ return 0; } static int tremove (lua_State *L) { lua_Integer size = aux_getn(L, 1, TAB_RW); lua_Integer pos = luaL_optinteger(L, 2, size); if (pos != size) /* validate 'pos' if given */ /* check whether 'pos' is in [1, size + 1] */ luaL_argcheck(L, (lua_Unsigned)pos - 1u <= (lua_Unsigned)size, 1, "position out of bounds"); lua_geti(L, 1, pos); /* result = t[pos] */ for ( ; pos < size; pos++) { lua_geti(L, 1, pos + 1); lua_seti(L, 1, pos); /* t[pos] = t[pos + 1] */ } lua_pushnil(L); lua_seti(L, 1, pos); /* remove entry t[pos] */ return 1; } /* ** Copy elements (1[f], ..., 1[e]) into (tt[t], tt[t+1], ...). Whenever ** possible, copy in increasing order, which is better for rehashing. ** "possible" means destination after original range, or smaller ** than origin, or copying to another table. */ static int tmove (lua_State *L) { lua_Integer f = luaL_checkinteger(L, 2); lua_Integer e = luaL_checkinteger(L, 3); lua_Integer t = luaL_checkinteger(L, 4); int tt = !lua_isnoneornil(L, 5) ? 5 : 1; /* destination table */ checktab(L, 1, TAB_R); checktab(L, tt, TAB_W); if (e >= f) { /* otherwise, nothing to move */ lua_Integer n, i; luaL_argcheck(L, f > 0 || e < LUA_MAXINTEGER + f, 3, "too many elements to move"); n = e - f + 1; /* number of elements to move */ luaL_argcheck(L, t <= LUA_MAXINTEGER - n + 1, 4, "destination wrap around"); if (t > e || t <= f || (tt != 1 && !lua_compare(L, 1, tt, LUA_OPEQ))) { for (i = 0; i < n; i++) { lua_geti(L, 1, f + i); lua_seti(L, tt, t + i); } } else { for (i = n - 1; i >= 0; i--) { lua_geti(L, 1, f + i); lua_seti(L, tt, t + i); } } } lua_pushvalue(L, tt); /* return destination table */ return 1; } static void addfield (lua_State *L, luaL_Buffer *b, lua_Integer i) { lua_geti(L, 1, i); if (l_unlikely(!lua_isstring(L, -1))) luaL_error(L, "invalid value (%s) at index %I in table for 'concat'", luaL_typename(L, -1), (LUAI_UACINT)i); luaL_addvalue(b); } static int tconcat (lua_State *L) { luaL_Buffer b; lua_Integer last = aux_getn(L, 1, TAB_R); size_t lsep; const char *sep = luaL_optlstring(L, 2, "", &lsep); lua_Integer i = luaL_optinteger(L, 3, 1); last = luaL_optinteger(L, 4, last); luaL_buffinit(L, &b); for (; i < last; i++) { addfield(L, &b, i); luaL_addlstring(&b, sep, lsep); } if (i == last) /* add last value (if interval was not empty) */ addfield(L, &b, i); luaL_pushresult(&b); return 1; } /* ** {====================================================== ** Pack/unpack ** ======================================================= */ static int tpack (lua_State *L) { int i; int n = lua_gettop(L); /* number of elements to pack */ lua_createtable(L, n, 1); /* create result table */ lua_insert(L, 1); /* put it at index 1 */ for (i = n; i >= 1; i--) /* assign elements */ lua_seti(L, 1, i); lua_pushinteger(L, n); lua_setfield(L, 1, "n"); /* t.n = number of elements */ return 1; /* return table */ } static int tunpack (lua_State *L) { lua_Unsigned n; lua_Integer i = luaL_optinteger(L, 2, 1); lua_Integer e = luaL_opt(L, luaL_checkinteger, 3, luaL_len(L, 1)); if (i > e) return 0; /* empty range */ n = (lua_Unsigned)e - i; /* number of elements minus 1 (avoid overflows) */ if (l_unlikely(n >= (unsigned int)INT_MAX || !lua_checkstack(L, (int)(++n)))) return luaL_error(L, "too many results to unpack"); for (; i < e; i++) { /* push arg[i..e - 1] (to avoid overflows) */ lua_geti(L, 1, i); } lua_geti(L, 1, e); /* push last element */ return (int)n; } /* }====================================================== */ /* ** {====================================================== ** Quicksort ** (based on 'Algorithms in MODULA-3', Robert Sedgewick; ** Addison-Wesley, 1993.) ** ======================================================= */ /* type for array indices */ typedef unsigned int IdxT; /* ** Produce a "random" 'unsigned int' to randomize pivot choice. This ** macro is used only when 'sort' detects a big imbalance in the result ** of a partition. (If you don't want/need this "randomness", ~0 is a ** good choice.) */ #if !defined(l_randomizePivot) /* { */ #include /* size of 'e' measured in number of 'unsigned int's */ #define sof(e) (sizeof(e) / sizeof(unsigned int)) /* ** Use 'time' and 'clock' as sources of "randomness". Because we don't ** know the types 'clock_t' and 'time_t', we cannot cast them to ** anything without risking overflows. A safe way to use their values ** is to copy them to an array of a known type and use the array values. */ static unsigned int l_randomizePivot (void) { clock_t c = clock(); time_t t = time(NULL); unsigned int buff[sof(c) + sof(t)]; unsigned int i, rnd = 0; memcpy(buff, &c, sof(c) * sizeof(unsigned int)); memcpy(buff + sof(c), &t, sof(t) * sizeof(unsigned int)); for (i = 0; i < sof(buff); i++) rnd += buff[i]; return rnd; } #endif /* } */ /* arrays larger than 'RANLIMIT' may use randomized pivots */ #define RANLIMIT 100u static void set2 (lua_State *L, IdxT i, IdxT j) { lua_seti(L, 1, i); lua_seti(L, 1, j); } /* ** Return true iff value at stack index 'a' is less than the value at ** index 'b' (according to the order of the sort). */ static int sort_comp (lua_State *L, int a, int b) { if (lua_isnil(L, 2)) /* no function? */ return lua_compare(L, a, b, LUA_OPLT); /* a < b */ else { /* function */ int res; lua_pushvalue(L, 2); /* push function */ lua_pushvalue(L, a-1); /* -1 to compensate function */ lua_pushvalue(L, b-2); /* -2 to compensate function and 'a' */ lua_call(L, 2, 1); /* call function */ res = lua_toboolean(L, -1); /* get result */ lua_pop(L, 1); /* pop result */ return res; } } /* ** Does the partition: Pivot P is at the top of the stack. ** precondition: a[lo] <= P == a[up-1] <= a[up], ** so it only needs to do the partition from lo + 1 to up - 2. ** Pos-condition: a[lo .. i - 1] <= a[i] == P <= a[i + 1 .. up] ** returns 'i'. */ static IdxT partition (lua_State *L, IdxT lo, IdxT up) { IdxT i = lo; /* will be incremented before first use */ IdxT j = up - 1; /* will be decremented before first use */ /* loop invariant: a[lo .. i] <= P <= a[j .. up] */ for (;;) { /* next loop: repeat ++i while a[i] < P */ while ((void)lua_geti(L, 1, ++i), sort_comp(L, -1, -2)) { if (l_unlikely(i == up - 1)) /* a[i] < P but a[up - 1] == P ?? */ luaL_error(L, "invalid order function for sorting"); lua_pop(L, 1); /* remove a[i] */ } /* after the loop, a[i] >= P and a[lo .. i - 1] < P */ /* next loop: repeat --j while P < a[j] */ while ((void)lua_geti(L, 1, --j), sort_comp(L, -3, -1)) { if (l_unlikely(j < i)) /* j < i but a[j] > P ?? */ luaL_error(L, "invalid order function for sorting"); lua_pop(L, 1); /* remove a[j] */ } /* after the loop, a[j] <= P and a[j + 1 .. up] >= P */ if (j < i) { /* no elements out of place? */ /* a[lo .. i - 1] <= P <= a[j + 1 .. i .. up] */ lua_pop(L, 1); /* pop a[j] */ /* swap pivot (a[up - 1]) with a[i] to satisfy pos-condition */ set2(L, up - 1, i); return i; } /* otherwise, swap a[i] - a[j] to restore invariant and repeat */ set2(L, i, j); } } /* ** Choose an element in the middle (2nd-3th quarters) of [lo,up] ** "randomized" by 'rnd' */ static IdxT choosePivot (IdxT lo, IdxT up, unsigned int rnd) { IdxT r4 = (up - lo) / 4; /* range/4 */ IdxT p = rnd % (r4 * 2) + (lo + r4); lua_assert(lo + r4 <= p && p <= up - r4); return p; } /* ** Quicksort algorithm (recursive function) */ static void auxsort (lua_State *L, IdxT lo, IdxT up, unsigned int rnd) { while (lo < up) { /* loop for tail recursion */ IdxT p; /* Pivot index */ IdxT n; /* to be used later */ /* sort elements 'lo', 'p', and 'up' */ lua_geti(L, 1, lo); lua_geti(L, 1, up); if (sort_comp(L, -1, -2)) /* a[up] < a[lo]? */ set2(L, lo, up); /* swap a[lo] - a[up] */ else lua_pop(L, 2); /* remove both values */ if (up - lo == 1) /* only 2 elements? */ return; /* already sorted */ if (up - lo < RANLIMIT || rnd == 0) /* small interval or no randomize? */ p = (lo + up)/2; /* middle element is a good pivot */ else /* for larger intervals, it is worth a random pivot */ p = choosePivot(lo, up, rnd); lua_geti(L, 1, p); lua_geti(L, 1, lo); if (sort_comp(L, -2, -1)) /* a[p] < a[lo]? */ set2(L, p, lo); /* swap a[p] - a[lo] */ else { lua_pop(L, 1); /* remove a[lo] */ lua_geti(L, 1, up); if (sort_comp(L, -1, -2)) /* a[up] < a[p]? */ set2(L, p, up); /* swap a[up] - a[p] */ else lua_pop(L, 2); } if (up - lo == 2) /* only 3 elements? */ return; /* already sorted */ lua_geti(L, 1, p); /* get middle element (Pivot) */ lua_pushvalue(L, -1); /* push Pivot */ lua_geti(L, 1, up - 1); /* push a[up - 1] */ set2(L, p, up - 1); /* swap Pivot (a[p]) with a[up - 1] */ p = partition(L, lo, up); /* a[lo .. p - 1] <= a[p] == P <= a[p + 1 .. up] */ if (p - lo < up - p) { /* lower interval is smaller? */ auxsort(L, lo, p - 1, rnd); /* call recursively for lower interval */ n = p - lo; /* size of smaller interval */ lo = p + 1; /* tail call for [p + 1 .. up] (upper interval) */ } else { auxsort(L, p + 1, up, rnd); /* call recursively for upper interval */ n = up - p; /* size of smaller interval */ up = p - 1; /* tail call for [lo .. p - 1] (lower interval) */ } if ((up - lo) / 128 > n) /* partition too imbalanced? */ rnd = l_randomizePivot(); /* try a new randomization */ } /* tail call auxsort(L, lo, up, rnd) */ } static int sort (lua_State *L) { lua_Integer n = aux_getn(L, 1, TAB_RW); if (n > 1) { /* non-trivial interval? */ luaL_argcheck(L, n < INT_MAX, 1, "array too big"); if (!lua_isnoneornil(L, 2)) /* is there a 2nd argument? */ luaL_checktype(L, 2, LUA_TFUNCTION); /* must be a function */ lua_settop(L, 2); /* make sure there are two arguments */ auxsort(L, 1, (IdxT)n, 0); } return 0; } /* }====================================================== */ static const luaL_Reg tab_funcs[] = { {"concat", tconcat}, {"insert", tinsert}, {"pack", tpack}, {"unpack", tunpack}, {"remove", tremove}, {"move", tmove}, {"sort", sort}, {NULL, NULL} }; LUAMOD_API int luaopen_table (lua_State *L) { luaL_newlib(L, tab_funcs); return 1; } /* ** $Id: lutf8lib.c $ ** Standard library for UTF-8 manipulation ** See Copyright Notice in lua.h */ #define lutf8lib_c #define LUA_LIB /*#include "lprefix.h"*/ #include #include #include #include /*#include "lua.h"*/ /*#include "lauxlib.h"*/ /*#include "lualib.h"*/ #define MAXUNICODE 0x10FFFFu #define MAXUTF 0x7FFFFFFFu /* ** Integer type for decoded UTF-8 values; MAXUTF needs 31 bits. */ #if (UINT_MAX >> 30) >= 1 typedef unsigned int utfint; #else typedef unsigned long utfint; #endif #define iscont(p) ((*(p) & 0xC0) == 0x80) /* from strlib */ /* translate a relative string position: negative means back from end */ static lua_Integer u_posrelat (lua_Integer pos, size_t len) { if (pos >= 0) return pos; else if (0u - (size_t)pos > len) return 0; else return (lua_Integer)len + pos + 1; } /* ** Decode one UTF-8 sequence, returning NULL if byte sequence is ** invalid. The array 'limits' stores the minimum value for each ** sequence length, to check for overlong representations. Its first ** entry forces an error for non-ascii bytes with no continuation ** bytes (count == 0). */ static const char *utf8_decode (const char *s, utfint *val, int strict) { static const utfint limits[] = {~(utfint)0, 0x80, 0x800, 0x10000u, 0x200000u, 0x4000000u}; unsigned int c = (unsigned char)s[0]; utfint res = 0; /* final result */ if (c < 0x80) /* ascii? */ res = c; else { int count = 0; /* to count number of continuation bytes */ for (; c & 0x40; c <<= 1) { /* while it needs continuation bytes... */ unsigned int cc = (unsigned char)s[++count]; /* read next byte */ if ((cc & 0xC0) != 0x80) /* not a continuation byte? */ return NULL; /* invalid byte sequence */ res = (res << 6) | (cc & 0x3F); /* add lower 6 bits from cont. byte */ } res |= ((utfint)(c & 0x7F) << (count * 5)); /* add first byte */ if (count > 5 || res > MAXUTF || res < limits[count]) return NULL; /* invalid byte sequence */ s += count; /* skip continuation bytes read */ } if (strict) { /* check for invalid code points; too large or surrogates */ if (res > MAXUNICODE || (0xD800u <= res && res <= 0xDFFFu)) return NULL; } if (val) *val = res; return s + 1; /* +1 to include first byte */ } /* ** utf8len(s [, i [, j [, lax]]]) --> number of characters that ** start in the range [i,j], or nil + current position if 's' is not ** well formed in that interval */ static int utflen (lua_State *L) { lua_Integer n = 0; /* counter for the number of characters */ size_t len; /* string length in bytes */ const char *s = luaL_checklstring(L, 1, &len); lua_Integer posi = u_posrelat(luaL_optinteger(L, 2, 1), len); lua_Integer posj = u_posrelat(luaL_optinteger(L, 3, -1), len); int lax = lua_toboolean(L, 4); luaL_argcheck(L, 1 <= posi && --posi <= (lua_Integer)len, 2, "initial position out of bounds"); luaL_argcheck(L, --posj < (lua_Integer)len, 3, "final position out of bounds"); while (posi <= posj) { const char *s1 = utf8_decode(s + posi, NULL, !lax); if (s1 == NULL) { /* conversion error? */ luaL_pushfail(L); /* return fail ... */ lua_pushinteger(L, posi + 1); /* ... and current position */ return 2; } posi = s1 - s; n++; } lua_pushinteger(L, n); return 1; } /* ** codepoint(s, [i, [j [, lax]]]) -> returns codepoints for all ** characters that start in the range [i,j] */ static int codepoint (lua_State *L) { size_t len; const char *s = luaL_checklstring(L, 1, &len); lua_Integer posi = u_posrelat(luaL_optinteger(L, 2, 1), len); lua_Integer pose = u_posrelat(luaL_optinteger(L, 3, posi), len); int lax = lua_toboolean(L, 4); int n; const char *se; luaL_argcheck(L, posi >= 1, 2, "out of bounds"); luaL_argcheck(L, pose <= (lua_Integer)len, 3, "out of bounds"); if (posi > pose) return 0; /* empty interval; return no values */ if (pose - posi >= INT_MAX) /* (lua_Integer -> int) overflow? */ return luaL_error(L, "string slice too long"); n = (int)(pose - posi) + 1; /* upper bound for number of returns */ luaL_checkstack(L, n, "string slice too long"); n = 0; /* count the number of returns */ se = s + pose; /* string end */ for (s += posi - 1; s < se;) { utfint code; s = utf8_decode(s, &code, !lax); if (s == NULL) return luaL_error(L, "invalid UTF-8 code"); lua_pushinteger(L, code); n++; } return n; } static void pushutfchar (lua_State *L, int arg) { lua_Unsigned code = (lua_Unsigned)luaL_checkinteger(L, arg); luaL_argcheck(L, code <= MAXUTF, arg, "value out of range"); lua_pushfstring(L, "%U", (long)code); } /* ** utfchar(n1, n2, ...) -> char(n1)..char(n2)... */ static int utfchar (lua_State *L) { int n = lua_gettop(L); /* number of arguments */ if (n == 1) /* optimize common case of single char */ pushutfchar(L, 1); else { int i; luaL_Buffer b; luaL_buffinit(L, &b); for (i = 1; i <= n; i++) { pushutfchar(L, i); luaL_addvalue(&b); } luaL_pushresult(&b); } return 1; } /* ** offset(s, n, [i]) -> index where n-th character counting from ** position 'i' starts; 0 means character at 'i'. */ static int byteoffset (lua_State *L) { size_t len; const char *s = luaL_checklstring(L, 1, &len); lua_Integer n = luaL_checkinteger(L, 2); lua_Integer posi = (n >= 0) ? 1 : len + 1; posi = u_posrelat(luaL_optinteger(L, 3, posi), len); luaL_argcheck(L, 1 <= posi && --posi <= (lua_Integer)len, 3, "position out of bounds"); if (n == 0) { /* find beginning of current byte sequence */ while (posi > 0 && iscont(s + posi)) posi--; } else { if (iscont(s + posi)) return luaL_error(L, "initial position is a continuation byte"); if (n < 0) { while (n < 0 && posi > 0) { /* move back */ do { /* find beginning of previous character */ posi--; } while (posi > 0 && iscont(s + posi)); n++; } } else { n--; /* do not move for 1st character */ while (n > 0 && posi < (lua_Integer)len) { do { /* find beginning of next character */ posi++; } while (iscont(s + posi)); /* (cannot pass final '\0') */ n--; } } } if (n == 0) /* did it find given character? */ lua_pushinteger(L, posi + 1); else /* no such character */ luaL_pushfail(L); return 1; } static int iter_aux (lua_State *L, int strict) { size_t len; const char *s = luaL_checklstring(L, 1, &len); lua_Unsigned n = (lua_Unsigned)lua_tointeger(L, 2); if (n < len) { while (iscont(s + n)) n++; /* skip continuation bytes */ } if (n >= len) /* (also handles original 'n' being negative) */ return 0; /* no more codepoints */ else { utfint code; const char *next = utf8_decode(s + n, &code, strict); if (next == NULL) return luaL_error(L, "invalid UTF-8 code"); lua_pushinteger(L, n + 1); lua_pushinteger(L, code); return 2; } } static int iter_auxstrict (lua_State *L) { return iter_aux(L, 1); } static int iter_auxlax (lua_State *L) { return iter_aux(L, 0); } static int iter_codes (lua_State *L) { int lax = lua_toboolean(L, 2); luaL_checkstring(L, 1); lua_pushcfunction(L, lax ? iter_auxlax : iter_auxstrict); lua_pushvalue(L, 1); lua_pushinteger(L, 0); return 3; } /* pattern to match a single UTF-8 character */ #define UTF8PATT "[\0-\x7F\xC2-\xFD][\x80-\xBF]*" static const luaL_Reg funcs[] = { {"offset", byteoffset}, {"codepoint", codepoint}, {"char", utfchar}, {"len", utflen}, {"codes", iter_codes}, /* placeholders */ {"charpattern", NULL}, {NULL, NULL} }; LUAMOD_API int luaopen_utf8 (lua_State *L) { luaL_newlib(L, funcs); lua_pushlstring(L, UTF8PATT, sizeof(UTF8PATT)/sizeof(char) - 1); lua_setfield(L, -2, "charpattern"); return 1; } /* ** $Id: linit.c $ ** Initialization of libraries for lua.c and other clients ** See Copyright Notice in lua.h */ #define linit_c #define LUA_LIB /* ** If you embed Lua in your program and need to open the standard ** libraries, call luaL_openlibs in your program. If you need a ** different set of libraries, copy this file to your project and edit ** it to suit your needs. ** ** You can also *preload* libraries, so that a later 'require' can ** open the library, which is already linked to the application. ** For that, do the following code: ** ** luaL_getsubtable(L, LUA_REGISTRYINDEX, LUA_PRELOAD_TABLE); ** lua_pushcfunction(L, luaopen_modname); ** lua_setfield(L, -2, modname); ** lua_pop(L, 1); // remove PRELOAD table */ /*#include "lprefix.h"*/ #include /*#include "lua.h"*/ /*#include "lualib.h"*/ /*#include "lauxlib.h"*/ /* ** these libs are loaded by lua.c and are readily available to any Lua ** program */ static const luaL_Reg loadedlibs[] = { {LUA_GNAME, luaopen_base}, {LUA_LOADLIBNAME, luaopen_package}, {LUA_COLIBNAME, luaopen_coroutine}, {LUA_TABLIBNAME, luaopen_table}, {LUA_IOLIBNAME, luaopen_io}, {LUA_OSLIBNAME, luaopen_os}, {LUA_STRLIBNAME, luaopen_string}, {LUA_MATHLIBNAME, luaopen_math}, {LUA_UTF8LIBNAME, luaopen_utf8}, {LUA_DBLIBNAME, luaopen_debug}, {NULL, NULL} }; LUALIB_API void luaL_openlibs (lua_State *L) { const luaL_Reg *lib; /* "require" functions from 'loadedlibs' and set results to global table */ for (lib = loadedlibs; lib->func; lib++) { luaL_requiref(L, lib->name, lib->func, 1); lua_pop(L, 1); /* remove lib */ } } #endif /* LUA_IMPL */ #ifdef __cplusplus } #endif #ifdef LUA_MAKE_LUA /* ** $Id: lua.c $ ** Lua stand-alone interpreter ** See Copyright Notice in lua.h */ #define lua_c /*#include "lprefix.h"*/ #include #include #include #include /*#include "lua.h"*/ /*#include "lauxlib.h"*/ /*#include "lualib.h"*/ #if !defined(LUA_PROGNAME) #define LUA_PROGNAME "lua" #endif #if !defined(LUA_INIT_VAR) #define LUA_INIT_VAR "LUA_INIT" #endif #define LUA_INITVARVERSION LUA_INIT_VAR LUA_VERSUFFIX static lua_State *globalL = NULL; static const char *progname = LUA_PROGNAME; #if defined(LUA_USE_POSIX) /* { */ /* ** Use 'sigaction' when available. */ static void setsignal (int sig, void (*handler)(int)) { struct sigaction sa; sa.sa_handler = handler; sa.sa_flags = 0; sigemptyset(&sa.sa_mask); /* do not mask any signal */ sigaction(sig, &sa, NULL); } #else /* }{ */ #define setsignal signal #endif /* } */ /* ** Hook set by signal function to stop the interpreter. */ static void lstop (lua_State *L, lua_Debug *ar) { (void)ar; /* unused arg. */ lua_sethook(L, NULL, 0, 0); /* reset hook */ luaL_error(L, "interrupted!"); } /* ** Function to be called at a C signal. Because a C signal cannot ** just change a Lua state (as there is no proper synchronization), ** this function only sets a hook that, when called, will stop the ** interpreter. */ static void laction (int i) { int flag = LUA_MASKCALL | LUA_MASKRET | LUA_MASKLINE | LUA_MASKCOUNT; setsignal(i, SIG_DFL); /* if another SIGINT happens, terminate process */ lua_sethook(globalL, lstop, flag, 1); } static void print_usage (const char *badoption) { lua_writestringerror("%s: ", progname); if (badoption[1] == 'e' || badoption[1] == 'l') lua_writestringerror("'%s' needs argument\n", badoption); else lua_writestringerror("unrecognized option '%s'\n", badoption); lua_writestringerror( "usage: %s [options] [script [args]]\n" "Available options are:\n" " -e stat execute string 'stat'\n" " -i enter interactive mode after executing 'script'\n" " -l mod require library 'mod' into global 'mod'\n" " -l g=mod require library 'mod' into global 'g'\n" " -v show version information\n" " -E ignore environment variables\n" " -W turn warnings on\n" " -- stop handling options\n" " - stop handling options and execute stdin\n" , progname); } /* ** Prints an error message, adding the program name in front of it ** (if present) */ static void l_message (const char *pname, const char *msg) { if (pname) lua_writestringerror("%s: ", pname); lua_writestringerror("%s\n", msg); } /* ** Check whether 'status' is not OK and, if so, prints the error ** message on the top of the stack. It assumes that the error object ** is a string, as it was either generated by Lua or by 'msghandler'. */ static int report (lua_State *L, int status) { if (status != LUA_OK) { const char *msg = lua_tostring(L, -1); l_message(progname, msg); lua_pop(L, 1); /* remove message */ } return status; } /* ** Message handler used to run all chunks */ static int msghandler (lua_State *L) { const char *msg = lua_tostring(L, 1); if (msg == NULL) { /* is error object not a string? */ if (luaL_callmeta(L, 1, "__tostring") && /* does it have a metamethod */ lua_type(L, -1) == LUA_TSTRING) /* that produces a string? */ return 1; /* that is the message */ else msg = lua_pushfstring(L, "(error object is a %s value)", luaL_typename(L, 1)); } luaL_traceback(L, L, msg, 1); /* append a standard traceback */ return 1; /* return the traceback */ } /* ** Interface to 'lua_pcall', which sets appropriate message function ** and C-signal handler. Used to run all chunks. */ static int docall (lua_State *L, int narg, int nres) { int status; int base = lua_gettop(L) - narg; /* function index */ lua_pushcfunction(L, msghandler); /* push message handler */ lua_insert(L, base); /* put it under function and args */ globalL = L; /* to be available to 'laction' */ setsignal(SIGINT, laction); /* set C-signal handler */ status = lua_pcall(L, narg, nres, base); setsignal(SIGINT, SIG_DFL); /* reset C-signal handler */ lua_remove(L, base); /* remove message handler from the stack */ return status; } static void print_version (void) { lua_writestring(LUA_COPYRIGHT, strlen(LUA_COPYRIGHT)); lua_writeline(); } /* ** Create the 'arg' table, which stores all arguments from the ** command line ('argv'). It should be aligned so that, at index 0, ** it has 'argv[script]', which is the script name. The arguments ** to the script (everything after 'script') go to positive indices; ** other arguments (before the script name) go to negative indices. ** If there is no script name, assume interpreter's name as base. */ static void createargtable (lua_State *L, char **argv, int argc, int script) { int i, narg; if (script == argc) script = 0; /* no script name? */ narg = argc - (script + 1); /* number of positive indices */ lua_createtable(L, narg, script + 1); for (i = 0; i < argc; i++) { lua_pushstring(L, argv[i]); lua_rawseti(L, -2, i - script); } lua_setglobal(L, "arg"); } static int dochunk (lua_State *L, int status) { if (status == LUA_OK) status = docall(L, 0, 0); return report(L, status); } static int dofile (lua_State *L, const char *name) { return dochunk(L, luaL_loadfile(L, name)); } static int dostring (lua_State *L, const char *s, const char *name) { return dochunk(L, luaL_loadbuffer(L, s, strlen(s), name)); } /* ** Receives 'globname[=modname]' and runs 'globname = require(modname)'. */ static int dolibrary (lua_State *L, char *globname) { int status; char *modname = strchr(globname, '='); if (modname == NULL) /* no explicit name? */ modname = globname; /* module name is equal to global name */ else { *modname = '\0'; /* global name ends here */ modname++; /* module name starts after the '=' */ } lua_getglobal(L, "require"); lua_pushstring(L, modname); status = docall(L, 1, 1); /* call 'require(modname)' */ if (status == LUA_OK) lua_setglobal(L, globname); /* globname = require(modname) */ return report(L, status); } /* ** Push on the stack the contents of table 'arg' from 1 to #arg */ static int pushargs (lua_State *L) { int i, n; if (lua_getglobal(L, "arg") != LUA_TTABLE) luaL_error(L, "'arg' is not a table"); n = (int)luaL_len(L, -1); luaL_checkstack(L, n + 3, "too many arguments to script"); for (i = 1; i <= n; i++) lua_rawgeti(L, -i, i); lua_remove(L, -i); /* remove table from the stack */ return n; } static int handle_script (lua_State *L, char **argv) { int status; const char *fname = argv[0]; if (strcmp(fname, "-") == 0 && strcmp(argv[-1], "--") != 0) fname = NULL; /* stdin */ status = luaL_loadfile(L, fname); if (status == LUA_OK) { int n = pushargs(L); /* push arguments to script */ status = docall(L, n, LUA_MULTRET); } return report(L, status); } /* bits of various argument indicators in 'args' */ #define has_error 1 /* bad option */ #define has_i 2 /* -i */ #define has_v 4 /* -v */ #define has_e 8 /* -e */ #define has_E 16 /* -E */ /* ** Traverses all arguments from 'argv', returning a mask with those ** needed before running any Lua code (or an error code if it finds ** any invalid argument). 'first' returns the first not-handled argument ** (either the script name or a bad argument in case of error). */ static int collectargs (char **argv, int *first) { int args = 0; int i; for (i = 1; argv[i] != NULL; i++) { *first = i; if (argv[i][0] != '-') /* not an option? */ return args; /* stop handling options */ switch (argv[i][1]) { /* else check option */ case '-': /* '--' */ if (argv[i][2] != '\0') /* extra characters after '--'? */ return has_error; /* invalid option */ *first = i + 1; return args; case '\0': /* '-' */ return args; /* script "name" is '-' */ case 'E': if (argv[i][2] != '\0') /* extra characters? */ return has_error; /* invalid option */ args |= has_E; break; case 'W': if (argv[i][2] != '\0') /* extra characters? */ return has_error; /* invalid option */ break; case 'i': args |= has_i; /* (-i implies -v) *//* FALLTHROUGH */ case 'v': if (argv[i][2] != '\0') /* extra characters? */ return has_error; /* invalid option */ args |= has_v; break; case 'e': args |= has_e; /* FALLTHROUGH */ case 'l': /* both options need an argument */ if (argv[i][2] == '\0') { /* no concatenated argument? */ i++; /* try next 'argv' */ if (argv[i] == NULL || argv[i][0] == '-') return has_error; /* no next argument or it is another option */ } break; default: /* invalid option */ return has_error; } } *first = i; /* no script name */ return args; } /* ** Processes options 'e' and 'l', which involve running Lua code, and ** 'W', which also affects the state. ** Returns 0 if some code raises an error. */ static int runargs (lua_State *L, char **argv, int n) { int i; for (i = 1; i < n; i++) { int option = argv[i][1]; lua_assert(argv[i][0] == '-'); /* already checked */ switch (option) { case 'e': case 'l': { int status; char *extra = argv[i] + 2; /* both options need an argument */ if (*extra == '\0') extra = argv[++i]; lua_assert(extra != NULL); status = (option == 'e') ? dostring(L, extra, "=(command line)") : dolibrary(L, extra); if (status != LUA_OK) return 0; break; } case 'W': lua_warning(L, "@on", 0); /* warnings on */ break; } } return 1; } static int handle_luainit (lua_State *L) { const char *name = "=" LUA_INITVARVERSION; const char *init = getenv(name + 1); if (init == NULL) { name = "=" LUA_INIT_VAR; init = getenv(name + 1); /* try alternative name */ } if (init == NULL) return LUA_OK; else if (init[0] == '@') return dofile(L, init+1); else return dostring(L, init, name); } /* ** {================================================================== ** Read-Eval-Print Loop (REPL) ** =================================================================== */ #if !defined(LUA_PROMPT) #define LUA_PROMPT "> " #define LUA_PROMPT2 ">> " #endif #if !defined(LUA_MAXINPUT) #define LUA_MAXINPUT 512 #endif /* ** lua_stdin_is_tty detects whether the standard input is a 'tty' (that ** is, whether we're running lua interactively). */ #if !defined(lua_stdin_is_tty) /* { */ #if defined(LUA_USE_POSIX) /* { */ #include #define lua_stdin_is_tty() isatty(0) #elif defined(LUA_USE_WINDOWS) /* }{ */ #include #include #define lua_stdin_is_tty() _isatty(_fileno(stdin)) #else /* }{ */ /* ISO C definition */ #define lua_stdin_is_tty() 1 /* assume stdin is a tty */ #endif /* } */ #endif /* } */ /* ** lua_readline defines how to show a prompt and then read a line from ** the standard input. ** lua_saveline defines how to "save" a read line in a "history". ** lua_freeline defines how to free a line read by lua_readline. */ #if !defined(lua_readline) /* { */ #if defined(LUA_USE_READLINE) /* { */ #include #include #define lua_initreadline(L) ((void)L, rl_readline_name="lua") #define lua_readline(L,b,p) ((void)L, ((b)=readline(p)) != NULL) #define lua_saveline(L,line) ((void)L, add_history(line)) #define lua_freeline(L,b) ((void)L, free(b)) #else /* }{ */ #define lua_initreadline(L) ((void)L) #define lua_readline(L,b,p) \ ((void)L, fputs(p, stdout), fflush(stdout), /* show prompt */ \ fgets(b, LUA_MAXINPUT, stdin) != NULL) /* get line */ #define lua_saveline(L,line) { (void)L; (void)line; } #define lua_freeline(L,b) { (void)L; (void)b; } #endif /* } */ #endif /* } */ /* ** Return the string to be used as a prompt by the interpreter. Leave ** the string (or nil, if using the default value) on the stack, to keep ** it anchored. */ static const char *get_prompt (lua_State *L, int firstline) { if (lua_getglobal(L, firstline ? "_PROMPT" : "_PROMPT2") == LUA_TNIL) return (firstline ? LUA_PROMPT : LUA_PROMPT2); /* use the default */ else { /* apply 'tostring' over the value */ const char *p = luaL_tolstring(L, -1, NULL); lua_remove(L, -2); /* remove original value */ return p; } } /* mark in error messages for incomplete statements */ #define EOFMARK "" #define marklen (sizeof(EOFMARK)/sizeof(char) - 1) /* ** Check whether 'status' signals a syntax error and the error ** message at the top of the stack ends with the above mark for ** incomplete statements. */ static int incomplete (lua_State *L, int status) { if (status == LUA_ERRSYNTAX) { size_t lmsg; const char *msg = lua_tolstring(L, -1, &lmsg); if (lmsg >= marklen && strcmp(msg + lmsg - marklen, EOFMARK) == 0) { lua_pop(L, 1); return 1; } } return 0; /* else... */ } /* ** Prompt the user, read a line, and push it into the Lua stack. */ static int pushline (lua_State *L, int firstline) { char buffer[LUA_MAXINPUT]; char *b = buffer; size_t l; const char *prmt = get_prompt(L, firstline); int readstatus = lua_readline(L, b, prmt); if (readstatus == 0) return 0; /* no input (prompt will be popped by caller) */ lua_pop(L, 1); /* remove prompt */ l = strlen(b); if (l > 0 && b[l-1] == '\n') /* line ends with newline? */ b[--l] = '\0'; /* remove it */ if (firstline && b[0] == '=') /* for compatibility with 5.2, ... */ lua_pushfstring(L, "return %s", b + 1); /* change '=' to 'return' */ else lua_pushlstring(L, b, l); lua_freeline(L, b); return 1; } /* ** Try to compile line on the stack as 'return ;'; on return, stack ** has either compiled chunk or original line (if compilation failed). */ static int addreturn (lua_State *L) { const char *line = lua_tostring(L, -1); /* original line */ const char *retline = lua_pushfstring(L, "return %s;", line); int status = luaL_loadbuffer(L, retline, strlen(retline), "=stdin"); if (status == LUA_OK) { lua_remove(L, -2); /* remove modified line */ if (line[0] != '\0') /* non empty? */ lua_saveline(L, line); /* keep history */ } else lua_pop(L, 2); /* pop result from 'luaL_loadbuffer' and modified line */ return status; } /* ** Read multiple lines until a complete Lua statement */ static int multiline (lua_State *L) { for (;;) { /* repeat until gets a complete statement */ size_t len; const char *line = lua_tolstring(L, 1, &len); /* get what it has */ int status = luaL_loadbuffer(L, line, len, "=stdin"); /* try it */ if (!incomplete(L, status) || !pushline(L, 0)) { lua_saveline(L, line); /* keep history */ return status; /* cannot or should not try to add continuation line */ } lua_pushliteral(L, "\n"); /* add newline... */ lua_insert(L, -2); /* ...between the two lines */ lua_concat(L, 3); /* join them */ } } /* ** Read a line and try to load (compile) it first as an expression (by ** adding "return " in front of it) and second as a statement. Return ** the final status of load/call with the resulting function (if any) ** in the top of the stack. */ static int loadline (lua_State *L) { int status; lua_settop(L, 0); if (!pushline(L, 1)) return -1; /* no input */ if ((status = addreturn(L)) != LUA_OK) /* 'return ...' did not work? */ status = multiline(L); /* try as command, maybe with continuation lines */ lua_remove(L, 1); /* remove line from the stack */ lua_assert(lua_gettop(L) == 1); return status; } /* ** Prints (calling the Lua 'print' function) any values on the stack */ static void l_print (lua_State *L) { int n = lua_gettop(L); if (n > 0) { /* any result to be printed? */ luaL_checkstack(L, LUA_MINSTACK, "too many results to print"); lua_getglobal(L, "print"); lua_insert(L, 1); if (lua_pcall(L, n, 0, 0) != LUA_OK) l_message(progname, lua_pushfstring(L, "error calling 'print' (%s)", lua_tostring(L, -1))); } } /* ** Do the REPL: repeatedly read (load) a line, evaluate (call) it, and ** print any results. */ static void doREPL (lua_State *L) { int status; const char *oldprogname = progname; progname = NULL; /* no 'progname' on errors in interactive mode */ lua_initreadline(L); while ((status = loadline(L)) != -1) { if (status == LUA_OK) status = docall(L, 0, LUA_MULTRET); if (status == LUA_OK) l_print(L); else report(L, status); } lua_settop(L, 0); /* clear stack */ lua_writeline(); progname = oldprogname; } /* }================================================================== */ /* ** Main body of stand-alone interpreter (to be called in protected mode). ** Reads the options and handles them all. */ static int pmain (lua_State *L) { int argc = (int)lua_tointeger(L, 1); char **argv = (char **)lua_touserdata(L, 2); int script; int args = collectargs(argv, &script); luaL_checkversion(L); /* check that interpreter has correct version */ if (argv[0] && argv[0][0]) progname = argv[0]; if (args == has_error) { /* bad arg? */ print_usage(argv[script]); /* 'script' has index of bad arg. */ return 0; } if (args & has_v) /* option '-v'? */ print_version(); if (args & has_E) { /* option '-E'? */ lua_pushboolean(L, 1); /* signal for libraries to ignore env. vars. */ lua_setfield(L, LUA_REGISTRYINDEX, "LUA_NOENV"); } luaL_openlibs(L); /* open standard libraries */ createargtable(L, argv, argc, script); /* create table 'arg' */ lua_gc(L, LUA_GCGEN, 0, 0); /* GC in generational mode */ if (!(args & has_E)) { /* no option '-E'? */ if (handle_luainit(L) != LUA_OK) /* run LUA_INIT */ return 0; /* error running LUA_INIT */ } if (!runargs(L, argv, script)) /* execute arguments -e and -l */ return 0; /* something failed */ if (script < argc && /* execute main script (if there is one) */ handle_script(L, argv + script) != LUA_OK) return 0; if (args & has_i) /* -i option? */ doREPL(L); /* do read-eval-print loop */ else if (script == argc && !(args & (has_e | has_v))) { /* no arguments? */ if (lua_stdin_is_tty()) { /* running in interactive mode? */ print_version(); doREPL(L); /* do read-eval-print loop */ } else dofile(L, NULL); /* executes stdin as a file */ } lua_pushboolean(L, 1); /* signal no errors */ return 1; } int main (int argc, char **argv) { int status, result; lua_State *L = luaL_newstate(); /* create state */ if (L == NULL) { l_message(argv[0], "cannot create state: not enough memory"); return EXIT_FAILURE; } lua_pushcfunction(L, &pmain); /* to call 'pmain' in protected mode */ lua_pushinteger(L, argc); /* 1st argument */ lua_pushlightuserdata(L, argv); /* 2nd argument */ status = lua_pcall(L, 2, 1, 0); /* do the call */ result = lua_toboolean(L, -1); /* get result */ report(L, status); lua_close(L); return (result && status == LUA_OK) ? EXIT_SUCCESS : EXIT_FAILURE; } #endif /* LUA_MAKE_LUA */ /* MIT License Copyright (c) 1994–2019 Lua.org, PUC-Rio. Copyright (c) 2020-2022 Eduardo Bart (https://github.com/edubart). 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. */ #line 0 #undef cast #undef G //--- #line 1 "3rd_stb_image.h" /* stb_image - v2.26 - public domain image loader - http://nothings.org/stb no warranty implied; use at your own risk Do this: #define STB_IMAGE_IMPLEMENTATION before you include this file in *one* C or C++ file to create the implementation. // i.e. it should look like this: #include ... #include ... #include ... #define STB_IMAGE_IMPLEMENTATION #include "stb_image.h" You can #define STBI_ASSERT(x) before the #include to avoid using assert.h. And #define STBI_MALLOC, STBI_REALLOC, and STBI_FREE to avoid using malloc,realloc,free QUICK NOTES: Primarily of interest to game developers and other people who can avoid problematic images and only need the trivial interface JPEG baseline & progressive (12 bpc/arithmetic not supported, same as stock IJG lib) PNG 1/2/4/8/16-bit-per-channel TGA (not sure what subset, if a subset) BMP non-1bpp, non-RLE PSD (composited view only, no extra channels, 8/16 bit-per-channel) GIF (*comp always reports as 4-channel) HDR (radiance rgbE format) PIC (Softimage PIC) PNM (PPM and PGM binary only) Animated GIF still needs a proper API, but here's one way to do it: http://gist.github.com/urraka/685d9a6340b26b830d49 - decode from memory or through FILE (define STBI_NO_STDIO to remove code) - decode from arbitrary I/O callbacks - SIMD acceleration on x86/x64 (SSE2) and ARM (NEON) Full documentation under "DOCUMENTATION" below. LICENSE See end of file for license information. RECENT REVISION HISTORY: 2.26 (2020-07-13) many minor fixes 2.25 (2020-02-02) fix warnings 2.24 (2020-02-02) fix warnings; thread-local failure_reason and flip_vertically 2.23 (2019-08-11) fix clang static analysis warning 2.22 (2019-03-04) gif fixes, fix warnings 2.21 (2019-02-25) fix typo in comment 2.20 (2019-02-07) support utf8 filenames in Windows; fix warnings and platform ifdefs 2.19 (2018-02-11) fix warning 2.18 (2018-01-30) fix warnings 2.17 (2018-01-29) bugfix, 1-bit BMP, 16-bitness query, fix warnings 2.16 (2017-07-23) all functions have 16-bit variants; optimizations; bugfixes 2.15 (2017-03-18) fix png-1,2,4; all Imagenet JPGs; no runtime SSE detection on GCC 2.14 (2017-03-03) remove deprecated STBI_JPEG_OLD; fixes for Imagenet JPGs 2.13 (2016-12-04) experimental 16-bit API, only for PNG so far; fixes 2.12 (2016-04-02) fix typo in 2.11 PSD fix that caused crashes 2.11 (2016-04-02) 16-bit PNGS; enable SSE2 in non-gcc x64 RGB-format JPEG; remove white matting in PSD; allocate large structures on the stack; correct channel count for PNG & BMP 2.10 (2016-01-22) avoid warning introduced in 2.09 2.09 (2016-01-16) 16-bit TGA; comments in PNM files; STBI_REALLOC_SIZED See end of file for full revision history. ============================ Contributors ========================= Image formats Extensions, features Sean Barrett (jpeg, png, bmp) Jetro Lauha (stbi_info) Nicolas Schulz (hdr, psd) Martin "SpartanJ" Golini (stbi_info) Jonathan Dummer (tga) James "moose2000" Brown (iPhone PNG) Jean-Marc Lienher (gif) Ben "Disch" Wenger (io callbacks) Tom Seddon (pic) Omar Cornut (1/2/4-bit PNG) Thatcher Ulrich (psd) Nicolas Guillemot (vertical flip) Ken Miller (pgm, ppm) Richard Mitton (16-bit PSD) github:urraka (animated gif) Junggon Kim (PNM comments) Christopher Forseth (animated gif) Daniel Gibson (16-bit TGA) socks-the-fox (16-bit PNG) Jeremy Sawicki (handle all ImageNet JPGs) Optimizations & bugfixes Mikhail Morozov (1-bit BMP) Fabian "ryg" Giesen Anael Seghezzi (is-16-bit query) Arseny Kapoulkine John-Mark Allen Carmelo J Fdez-Aguera Bug & warning fixes Marc LeBlanc David Woo Guillaume George Martins Mozeiko Christpher Lloyd Jerry Jansson Joseph Thomson Blazej Dariusz Roszkowski Phil Jordan Dave Moore Roy Eltham Hayaki Saito Nathan Reed Won Chun Luke Graham Johan Duparc Nick Verigakis the Horde3D community Thomas Ruf Ronny Chevalier github:rlyeh Janez Zemva John Bartholomew Michal Cichon github:romigrou Jonathan Blow Ken Hamada Tero Hanninen github:svdijk Laurent Gomila Cort Stratton github:snagar Aruelien Pocheville Sergio Gonzalez Thibault Reuille github:Zelex Cass Everitt Ryamond Barbiero github:grim210 Paul Du Bois Engin Manap Aldo Culquicondor github:sammyhw Philipp Wiesemann Dale Weiler Oriol Ferrer Mesia github:phprus Josh Tobin Matthew Gregan github:poppolopoppo Julian Raschke Gregory Mullen Christian Floisand github:darealshinji Baldur Karlsson Kevin Schmidt JR Smith github:Michaelangel007 Brad Weinberger Matvey Cherevko [reserved] Luca Sas Alexander Veselov Zack Middleton [reserved] Ryan C. Gordon [reserved] [reserved] DO NOT ADD YOUR NAME HERE To add your name to the credits, pick a random blank space in the middle and fill it. 80% of merge conflicts on stb PRs are due to people adding their name at the end of the credits. */ #ifndef STBI_INCLUDE_STB_IMAGE_H #define STBI_INCLUDE_STB_IMAGE_H // DOCUMENTATION // // Limitations: // - no 12-bit-per-channel JPEG // - no JPEGs with arithmetic coding // - GIF always returns *comp=4 // // Basic usage (see HDR discussion below for HDR usage): // int x,y,n; // unsigned char *data = stbi_load(filename, &x, &y, &n, 0); // // ... process data if not NULL ... // // ... x = width, y = height, n = # 8-bit components per pixel ... // // ... replace '0' with '1'..'4' to force that many components per pixel // // ... but 'n' will always be the number that it would have been if you said 0 // stbi_image_free(data) // // Standard parameters: // int *x -- outputs image width in pixels // int *y -- outputs image height in pixels // int *channels_in_file -- outputs # of image components in image file // int desired_channels -- if non-zero, # of image components requested in result // // The return value from an image loader is an 'unsigned char *' which points // to the pixel data, or NULL on an allocation failure or if the image is // corrupt or invalid. The pixel data consists of *y scanlines of *x pixels, // with each pixel consisting of N interleaved 8-bit components; the first // pixel pointed to is top-left-most in the image. There is no padding between // image scanlines or between pixels, regardless of format. The number of // components N is 'desired_channels' if desired_channels is non-zero, or // *channels_in_file otherwise. If desired_channels is non-zero, // *channels_in_file has the number of components that _would_ have been // output otherwise. E.g. if you set desired_channels to 4, you will always // get RGBA output, but you can check *channels_in_file to see if it's trivially // opaque because e.g. there were only 3 channels in the source image. // // An output image with N components has the following components interleaved // in this order in each pixel: // // N=#comp components // 1 grey // 2 grey, alpha // 3 red, green, blue // 4 red, green, blue, alpha // // If image loading fails for any reason, the return value will be NULL, // and *x, *y, *channels_in_file will be unchanged. The function // stbi_failure_reason() can be queried for an extremely brief, end-user // unfriendly explanation of why the load failed. Define STBI_NO_FAILURE_STRINGS // to avoid compiling these strings at all, and STBI_FAILURE_USERMSG to get slightly // more user-friendly ones. // // Paletted PNG, BMP, GIF, and PIC images are automatically depalettized. // // =========================================================================== // // UNICODE: // // If compiling for Windows and you wish to use Unicode filenames, compile // with // #define STBI_WINDOWS_UTF8 // and pass utf8-encoded filenames. Call stbi_convert_wchar_to_utf8 to convert // Windows wchar_t filenames to utf8. // // =========================================================================== // // Philosophy // // stb libraries are designed with the following priorities: // // 1. easy to use // 2. easy to maintain // 3. good performance // // Sometimes I let "good performance" creep up in priority over "easy to maintain", // and for best performance I may provide less-easy-to-use APIs that give higher // performance, in addition to the easy-to-use ones. Nevertheless, it's important // to keep in mind that from the standpoint of you, a client of this library, // all you care about is #1 and #3, and stb libraries DO NOT emphasize #3 above all. // // Some secondary priorities arise directly from the first two, some of which // provide more explicit reasons why performance can't be emphasized. // // - Portable ("ease of use") // - Small source code footprint ("easy to maintain") // - No dependencies ("ease of use") // // =========================================================================== // // I/O callbacks // // I/O callbacks allow you to read from arbitrary sources, like packaged // files or some other source. Data read from callbacks are processed // through a small internal buffer (currently 128 bytes) to try to reduce // overhead. // // The three functions you must define are "read" (reads some bytes of data), // "skip" (skips some bytes of data), "eof" (reports if the stream is at the end). // // =========================================================================== // // SIMD support // // The JPEG decoder will try to automatically use SIMD kernels on x86 when // supported by the compiler. For ARM Neon support, you must explicitly // request it. // // (The old do-it-yourself SIMD API is no longer supported in the current // code.) // // On x86, SSE2 will automatically be used when available based on a run-time // test; if not, the generic C versions are used as a fall-back. On ARM targets, // the typical path is to have separate builds for NEON and non-NEON devices // (at least this is true for iOS and Android). Therefore, the NEON support is // toggled by a build flag: define STBI_NEON to get NEON loops. // // If for some reason you do not want to use any of SIMD code, or if // you have issues compiling it, you can disable it entirely by // defining STBI_NO_SIMD. // // =========================================================================== // // HDR image support (disable by defining STBI_NO_HDR) // // stb_image supports loading HDR images in general, and currently the Radiance // .HDR file format specifically. You can still load any file through the existing // interface; if you attempt to load an HDR file, it will be automatically remapped // to LDR, assuming gamma 2.2 and an arbitrary scale factor defaulting to 1; // both of these constants can be reconfigured through this interface: // // stbi_hdr_to_ldr_gamma(2.2f); // stbi_hdr_to_ldr_scale(1.0f); // // (note, do not use _inverse_ constants; stbi_image will invert them // appropriately). // // Additionally, there is a new, parallel interface for loading files as // (linear) floats to preserve the full dynamic range: // // float *data = stbi_loadf(filename, &x, &y, &n, 0); // // If you load LDR images through this interface, those images will // be promoted to floating point values, run through the inverse of // constants corresponding to the above: // // stbi_ldr_to_hdr_scale(1.0f); // stbi_ldr_to_hdr_gamma(2.2f); // // Finally, given a filename (or an open file or memory block--see header // file for details) containing image data, you can query for the "most // appropriate" interface to use (that is, whether the image is HDR or // not), using: // // stbi_is_hdr(char *filename); // // =========================================================================== // // iPhone PNG support: // // By default we convert iphone-formatted PNGs back to RGB, even though // they are internally encoded differently. You can disable this conversion // by calling stbi_convert_iphone_png_to_rgb(0), in which case // you will always just get the native iphone "format" through (which // is BGR stored in RGB). // // Call stbi_set_unpremultiply_on_load(1) as well to force a divide per // pixel to remove any premultiplied alpha *only* if the image file explicitly // says there's premultiplied data (currently only happens in iPhone images, // and only if iPhone convert-to-rgb processing is on). // // =========================================================================== // // ADDITIONAL CONFIGURATION // // - You can suppress implementation of any of the decoders to reduce // your code footprint by #defining one or more of the following // symbols before creating the implementation. // // STBI_NO_JPEG // STBI_NO_PNG // STBI_NO_BMP // STBI_NO_PSD // STBI_NO_TGA // STBI_NO_GIF // STBI_NO_HDR // STBI_NO_PIC // STBI_NO_PNM (.ppm and .pgm) // // - You can request *only* certain decoders and suppress all other ones // (this will be more forward-compatible, as addition of new decoders // doesn't require you to disable them explicitly): // // STBI_ONLY_JPEG // STBI_ONLY_PNG // STBI_ONLY_BMP // STBI_ONLY_PSD // STBI_ONLY_TGA // STBI_ONLY_GIF // STBI_ONLY_HDR // STBI_ONLY_PIC // STBI_ONLY_PNM (.ppm and .pgm) // // - If you use STBI_NO_PNG (or _ONLY_ without PNG), and you still // want the zlib decoder to be available, #define STBI_SUPPORT_ZLIB // // - If you define STBI_MAX_DIMENSIONS, stb_image will reject images greater // than that size (in either width or height) without further processing. // This is to let programs in the wild set an upper bound to prevent // denial-of-service attacks on untrusted data, as one could generate a // valid image of gigantic dimensions and force stb_image to allocate a // huge block of memory and spend disproportionate time decoding it. By // default this is set to (1 << 24), which is 16777216, but that's still // very big. #ifndef STBI_NO_STDIO #include #endif // STBI_NO_STDIO #define STBI_VERSION 1 enum { STBI_default = 0, // only used for desired_channels STBI_grey = 1, STBI_grey_alpha = 2, STBI_rgb = 3, STBI_rgb_alpha = 4 }; #include typedef unsigned char stbi_uc; typedef unsigned short stbi_us; #ifdef __cplusplus extern "C" { #endif #ifndef STBIDEF #ifdef STB_IMAGE_STATIC #define STBIDEF static #else #define STBIDEF extern #endif #endif ////////////////////////////////////////////////////////////////////////////// // // PRIMARY API - works on images of any type // // // load image by filename, open file, or memory buffer // typedef struct { int (*read) (void *user,char *data,int size); // fill 'data' with 'size' bytes. return number of bytes actually read void (*skip) (void *user,int n); // skip the next 'n' bytes, or 'unget' the last -n bytes if negative int (*eof) (void *user); // returns nonzero if we are at end of file/data } stbi_io_callbacks; //////////////////////////////////// // // 8-bits-per-channel interface // STBIDEF stbi_uc *stbi_load_from_memory (stbi_uc const *buffer, int len , int *x, int *y, int *channels_in_file, int desired_channels); STBIDEF stbi_uc *stbi_load_from_callbacks(stbi_io_callbacks const *clbk , void *user, int *x, int *y, int *channels_in_file, int desired_channels); #ifndef STBI_NO_STDIO STBIDEF stbi_uc *stbi_load (char const *filename, int *x, int *y, int *channels_in_file, int desired_channels); STBIDEF stbi_uc *stbi_load_from_file (FILE *f, int *x, int *y, int *channels_in_file, int desired_channels); // for stbi_load_from_file, file pointer is left pointing immediately after image #endif #ifndef STBI_NO_GIF STBIDEF stbi_uc *stbi_load_gif_from_memory(stbi_uc const *buffer, int len, int **delays, int *x, int *y, int *z, int *comp, int req_comp); #endif #ifdef STBI_WINDOWS_UTF8 STBIDEF int stbi_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wchar_t* input); #endif //////////////////////////////////// // // 16-bits-per-channel interface // STBIDEF stbi_us *stbi_load_16_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels); STBIDEF stbi_us *stbi_load_16_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *channels_in_file, int desired_channels); #ifndef STBI_NO_STDIO STBIDEF stbi_us *stbi_load_16 (char const *filename, int *x, int *y, int *channels_in_file, int desired_channels); STBIDEF stbi_us *stbi_load_from_file_16(FILE *f, int *x, int *y, int *channels_in_file, int desired_channels); #endif //////////////////////////////////// // // float-per-channel interface // #ifndef STBI_NO_LINEAR STBIDEF float *stbi_loadf_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels); STBIDEF float *stbi_loadf_from_callbacks (stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *channels_in_file, int desired_channels); #ifndef STBI_NO_STDIO STBIDEF float *stbi_loadf (char const *filename, int *x, int *y, int *channels_in_file, int desired_channels); STBIDEF float *stbi_loadf_from_file (FILE *f, int *x, int *y, int *channels_in_file, int desired_channels); #endif #endif #ifndef STBI_NO_HDR STBIDEF void stbi_hdr_to_ldr_gamma(float gamma); STBIDEF void stbi_hdr_to_ldr_scale(float scale); #endif // STBI_NO_HDR #ifndef STBI_NO_LINEAR STBIDEF void stbi_ldr_to_hdr_gamma(float gamma); STBIDEF void stbi_ldr_to_hdr_scale(float scale); #endif // STBI_NO_LINEAR // stbi_is_hdr is always defined, but always returns false if STBI_NO_HDR STBIDEF int stbi_is_hdr_from_callbacks(stbi_io_callbacks const *clbk, void *user); STBIDEF int stbi_is_hdr_from_memory(stbi_uc const *buffer, int len); #ifndef STBI_NO_STDIO STBIDEF int stbi_is_hdr (char const *filename); STBIDEF int stbi_is_hdr_from_file(FILE *f); #endif // STBI_NO_STDIO // get a VERY brief reason for failure // on most compilers (and ALL modern mainstream compilers) this is threadsafe STBIDEF const char *stbi_failure_reason (void); // free the loaded image -- this is just free() STBIDEF void stbi_image_free (void *retval_from_stbi_load); // get image dimensions & components without fully decoding STBIDEF int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp); STBIDEF int stbi_info_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp); STBIDEF int stbi_is_16_bit_from_memory(stbi_uc const *buffer, int len); STBIDEF int stbi_is_16_bit_from_callbacks(stbi_io_callbacks const *clbk, void *user); #ifndef STBI_NO_STDIO STBIDEF int stbi_info (char const *filename, int *x, int *y, int *comp); STBIDEF int stbi_info_from_file (FILE *f, int *x, int *y, int *comp); STBIDEF int stbi_is_16_bit (char const *filename); STBIDEF int stbi_is_16_bit_from_file(FILE *f); #endif // for image formats that explicitly notate that they have premultiplied alpha, // we just return the colors as stored in the file. set this flag to force // unpremultiplication. results are undefined if the unpremultiply overflow. STBIDEF void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpremultiply); // indicate whether we should process iphone images back to canonical format, // or just pass them through "as-is" STBIDEF void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert); // flip the image vertically, so the first pixel in the output array is the bottom left STBIDEF void stbi_set_flip_vertically_on_load(int flag_true_if_should_flip); // as above, but only applies to images loaded on the thread that calls the function // this function is only available if your compiler supports thread-local variables; // calling it will fail to link if your compiler doesn't STBIDEF void stbi_set_flip_vertically_on_load_thread(int flag_true_if_should_flip); // ZLIB client - used by PNG, available for other purposes STBIDEF char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int initial_size, int *outlen); STBIDEF char *stbi_zlib_decode_malloc_guesssize_headerflag(const char *buffer, int len, int initial_size, int *outlen, int parse_header); STBIDEF char *stbi_zlib_decode_malloc(const char *buffer, int len, int *outlen); STBIDEF int stbi_zlib_decode_buffer(char *obuffer, int olen, const char *ibuffer, int ilen); STBIDEF char *stbi_zlib_decode_noheader_malloc(const char *buffer, int len, int *outlen); STBIDEF int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen); #ifdef __cplusplus } #endif // // //// end header file ///////////////////////////////////////////////////// #endif // STBI_INCLUDE_STB_IMAGE_H #ifdef STB_IMAGE_IMPLEMENTATION #if defined(STBI_ONLY_JPEG) || defined(STBI_ONLY_PNG) || defined(STBI_ONLY_BMP) \ || defined(STBI_ONLY_TGA) || defined(STBI_ONLY_GIF) || defined(STBI_ONLY_PSD) \ || defined(STBI_ONLY_HDR) || defined(STBI_ONLY_PIC) || defined(STBI_ONLY_PNM) \ || defined(STBI_ONLY_ZLIB) #ifndef STBI_ONLY_JPEG #define STBI_NO_JPEG #endif #ifndef STBI_ONLY_PNG #define STBI_NO_PNG #endif #ifndef STBI_ONLY_BMP #define STBI_NO_BMP #endif #ifndef STBI_ONLY_PSD #define STBI_NO_PSD #endif #ifndef STBI_ONLY_TGA #define STBI_NO_TGA #endif #ifndef STBI_ONLY_GIF #define STBI_NO_GIF #endif #ifndef STBI_ONLY_HDR #define STBI_NO_HDR #endif #ifndef STBI_ONLY_PIC #define STBI_NO_PIC #endif #ifndef STBI_ONLY_PNM #define STBI_NO_PNM #endif #endif #if defined(STBI_NO_PNG) && !defined(STBI_SUPPORT_ZLIB) && !defined(STBI_NO_ZLIB) #define STBI_NO_ZLIB #endif #include #include // ptrdiff_t on osx #include #include #include #if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) #include // ldexp, pow #endif #ifndef STBI_NO_STDIO #include #endif #ifndef STBI_ASSERT #include #define STBI_ASSERT(x) assert(x) #endif #ifdef __cplusplus #define STBI_EXTERN extern "C" #else #define STBI_EXTERN extern #endif #ifndef _MSC_VER #ifdef __cplusplus #define stbi_inline inline #else #define stbi_inline #endif #else #define stbi_inline __forceinline #endif #ifndef STBI_NO_THREAD_LOCALS #if defined(__cplusplus) && __cplusplus >= 201103L #define STBI_THREAD_LOCAL thread_local #elif defined(__GNUC__) && __GNUC__ < 5 #define STBI_THREAD_LOCAL __thread #elif defined(_MSC_VER) #define STBI_THREAD_LOCAL __declspec(thread) #elif defined (__STDC_VERSION__) && __STDC_VERSION__ >= 201112L && !defined(__STDC_NO_THREADS__) #define STBI_THREAD_LOCAL _Thread_local #endif #ifndef STBI_THREAD_LOCAL #if defined(__GNUC__) #define STBI_THREAD_LOCAL __thread #endif #endif #endif #ifdef _MSC_VER typedef unsigned short stbi__uint16; typedef signed short stbi__int16; typedef unsigned int stbi__uint32; typedef signed int stbi__int32; #else #include typedef uint16_t stbi__uint16; typedef int16_t stbi__int16; typedef uint32_t stbi__uint32; typedef int32_t stbi__int32; #endif // should produce compiler error if size is wrong typedef unsigned char validate_uint32[sizeof(stbi__uint32)==4 ? 1 : -1]; #ifdef _MSC_VER #define STBI_NOTUSED(v) (void)(v) #else #define STBI_NOTUSED(v) (void)sizeof(v) #endif #ifdef _MSC_VER #define STBI_HAS_LROTL #endif #ifdef STBI_HAS_LROTL #define stbi_lrot(x,y) _lrotl(x,y) #else #define stbi_lrot(x,y) (((x) << (y)) | ((x) >> (32 - (y)))) #endif #if defined(STBI_MALLOC) && defined(STBI_FREE) && (defined(STBI_REALLOC) || defined(STBI_REALLOC_SIZED)) // ok #elif !defined(STBI_MALLOC) && !defined(STBI_FREE) && !defined(STBI_REALLOC) && !defined(STBI_REALLOC_SIZED) // ok #else #error "Must define all or none of STBI_MALLOC, STBI_FREE, and STBI_REALLOC (or STBI_REALLOC_SIZED)." #endif #ifndef STBI_MALLOC #define STBI_MALLOC(sz) malloc(sz) #define STBI_REALLOC(p,newsz) realloc(p,newsz) #define STBI_FREE(p) free(p) #endif #ifndef STBI_REALLOC_SIZED #define STBI_REALLOC_SIZED(p,oldsz,newsz) STBI_REALLOC(p,newsz) #endif // x86/x64 detection #if defined(__x86_64__) || defined(_M_X64) #define STBI__X64_TARGET #elif defined(__i386) || defined(_M_IX86) #define STBI__X86_TARGET #endif #if defined(__GNUC__) && defined(STBI__X86_TARGET) && !defined(__SSE2__) && !defined(STBI_NO_SIMD) // gcc doesn't support sse2 intrinsics unless you compile with -msse2, // which in turn means it gets to use SSE2 everywhere. This is unfortunate, // but previous attempts to provide the SSE2 functions with runtime // detection caused numerous issues. The way architecture extensions are // exposed in GCC/Clang is, sadly, not really suited for one-file libs. // New behavior: if compiled with -msse2, we use SSE2 without any // detection; if not, we don't use it at all. #define STBI_NO_SIMD #endif #if defined(__MINGW32__) && defined(STBI__X86_TARGET) && !defined(STBI_MINGW_ENABLE_SSE2) && !defined(STBI_NO_SIMD) // Note that __MINGW32__ doesn't actually mean 32-bit, so we have to avoid STBI__X64_TARGET // // 32-bit MinGW wants ESP to be 16-byte aligned, but this is not in the // Windows ABI and VC++ as well as Windows DLLs don't maintain that invariant. // As a result, enabling SSE2 on 32-bit MinGW is dangerous when not // simultaneously enabling "-mstackrealign". // // See https://github.com/nothings/stb/issues/81 for more information. // // So default to no SSE2 on 32-bit MinGW. If you've read this far and added // -mstackrealign to your build settings, feel free to #define STBI_MINGW_ENABLE_SSE2. #define STBI_NO_SIMD #endif #if !defined(STBI_NO_SIMD) && (defined(STBI__X86_TARGET) || defined(STBI__X64_TARGET)) #define STBI_SSE2 #include #ifdef _MSC_VER #if _MSC_VER >= 1400 // not VC6 #include // __cpuid static int stbi__cpuid3(void) { int info[4]; __cpuid(info,1); return info[3]; } #else static int stbi__cpuid3(void) { int res; __asm { mov eax,1 cpuid mov res,edx } return res; } #endif #define STBI_SIMD_ALIGN(type, name) __declspec(align(16)) type name #if !defined(STBI_NO_JPEG) && defined(STBI_SSE2) static int stbi__sse2_available(void) { int info3 = stbi__cpuid3(); return ((info3 >> 26) & 1) != 0; } #endif #else // assume GCC-style if not VC++ #define STBI_SIMD_ALIGN(type, name) type name __attribute__((aligned(16))) #if !defined(STBI_NO_JPEG) && defined(STBI_SSE2) static int stbi__sse2_available(void) { // If we're even attempting to compile this on GCC/Clang, that means // -msse2 is on, which means the compiler is allowed to use SSE2 // instructions at will, and so are we. return 1; } #endif #endif #endif // ARM NEON #if defined(STBI_NO_SIMD) && defined(STBI_NEON) #undef STBI_NEON #endif #ifdef STBI_NEON #include // assume GCC or Clang on ARM targets #define STBI_SIMD_ALIGN(type, name) type name __attribute__((aligned(16))) #endif #ifndef STBI_SIMD_ALIGN #define STBI_SIMD_ALIGN(type, name) type name #endif #ifndef STBI_MAX_DIMENSIONS #define STBI_MAX_DIMENSIONS (1 << 24) #endif /////////////////////////////////////////////// // // stbi__context struct and start_xxx functions // stbi__context structure is our basic context used by all images, so it // contains all the IO context, plus some basic image information typedef struct { stbi__uint32 img_x, img_y; int img_n, img_out_n; stbi_io_callbacks io; void *io_user_data; int read_from_callbacks; int buflen; stbi_uc buffer_start[128]; int callback_already_read; stbi_uc *img_buffer, *img_buffer_end; stbi_uc *img_buffer_original, *img_buffer_original_end; } stbi__context; static void stbi__refill_buffer(stbi__context *s); // initialize a memory-decode context static void stbi__start_mem(stbi__context *s, stbi_uc const *buffer, int len) { s->io.read = NULL; s->read_from_callbacks = 0; s->callback_already_read = 0; s->img_buffer = s->img_buffer_original = (stbi_uc *) buffer; s->img_buffer_end = s->img_buffer_original_end = (stbi_uc *) buffer+len; } // initialize a callback-based context static void stbi__start_callbacks(stbi__context *s, stbi_io_callbacks *c, void *user) { s->io = *c; s->io_user_data = user; s->buflen = sizeof(s->buffer_start); s->read_from_callbacks = 1; s->callback_already_read = 0; s->img_buffer = s->img_buffer_original = s->buffer_start; stbi__refill_buffer(s); s->img_buffer_original_end = s->img_buffer_end; } #ifndef STBI_NO_STDIO static int stbi__stdio_read(void *user, char *data, int size) { return (int) fread(data,1,size,(FILE*) user); } static void stbi__stdio_skip(void *user, int n) { int ch; fseek((FILE*) user, n, SEEK_CUR); ch = fgetc((FILE*) user); /* have to read a byte to reset feof()'s flag */ if (ch != EOF) { ungetc(ch, (FILE *) user); /* push byte back onto stream if valid. */ } } static int stbi__stdio_eof(void *user) { return feof((FILE*) user) || ferror((FILE *) user); } static stbi_io_callbacks stbi__stdio_callbacks = { stbi__stdio_read, stbi__stdio_skip, stbi__stdio_eof, }; static void stbi__start_file(stbi__context *s, FILE *f) { stbi__start_callbacks(s, &stbi__stdio_callbacks, (void *) f); } //static void stop_file(stbi__context *s) { } #endif // !STBI_NO_STDIO static void stbi__rewind(stbi__context *s) { // conceptually rewind SHOULD rewind to the beginning of the stream, // but we just rewind to the beginning of the initial buffer, because // we only use it after doing 'test', which only ever looks at at most 92 bytes s->img_buffer = s->img_buffer_original; s->img_buffer_end = s->img_buffer_original_end; } enum { STBI_ORDER_RGB, STBI_ORDER_BGR }; typedef struct { int bits_per_channel; int num_channels; int channel_order; } stbi__result_info; #ifndef STBI_NO_JPEG static int stbi__jpeg_test(stbi__context *s); static void *stbi__jpeg_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); static int stbi__jpeg_info(stbi__context *s, int *x, int *y, int *comp); #endif #ifndef STBI_NO_PNG static int stbi__png_test(stbi__context *s); static void *stbi__png_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); static int stbi__png_info(stbi__context *s, int *x, int *y, int *comp); static int stbi__png_is16(stbi__context *s); #endif #ifndef STBI_NO_BMP static int stbi__bmp_test(stbi__context *s); static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); static int stbi__bmp_info(stbi__context *s, int *x, int *y, int *comp); #endif #ifndef STBI_NO_TGA static int stbi__tga_test(stbi__context *s); static void *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); static int stbi__tga_info(stbi__context *s, int *x, int *y, int *comp); #endif #ifndef STBI_NO_PSD static int stbi__psd_test(stbi__context *s); static void *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int bpc); static int stbi__psd_info(stbi__context *s, int *x, int *y, int *comp); static int stbi__psd_is16(stbi__context *s); #endif #ifndef STBI_NO_HDR static int stbi__hdr_test(stbi__context *s); static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); static int stbi__hdr_info(stbi__context *s, int *x, int *y, int *comp); #endif #ifndef STBI_NO_PIC static int stbi__pic_test(stbi__context *s); static void *stbi__pic_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); static int stbi__pic_info(stbi__context *s, int *x, int *y, int *comp); #endif #ifndef STBI_NO_GIF static int stbi__gif_test(stbi__context *s); static void *stbi__gif_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); static void *stbi__load_gif_main(stbi__context *s, int **delays, int *x, int *y, int *z, int *comp, int req_comp); static int stbi__gif_info(stbi__context *s, int *x, int *y, int *comp); #endif #ifndef STBI_NO_PNM static int stbi__pnm_test(stbi__context *s); static void *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); static int stbi__pnm_info(stbi__context *s, int *x, int *y, int *comp); #endif static #ifdef STBI_THREAD_LOCAL STBI_THREAD_LOCAL #endif const char *stbi__g_failure_reason; STBIDEF const char *stbi_failure_reason(void) { return stbi__g_failure_reason; } #ifndef STBI_NO_FAILURE_STRINGS static int stbi__err(const char *str) { stbi__g_failure_reason = str; return 0; } #endif static void *stbi__malloc(size_t size) { return STBI_MALLOC(size); } // stb_image uses ints pervasively, including for offset calculations. // therefore the largest decoded image size we can support with the // current code, even on 64-bit targets, is INT_MAX. this is not a // significant limitation for the intended use case. // // we do, however, need to make sure our size calculations don't // overflow. hence a few helper functions for size calculations that // multiply integers together, making sure that they're non-negative // and no overflow occurs. // return 1 if the sum is valid, 0 on overflow. // negative terms are considered invalid. static int stbi__addsizes_valid(int a, int b) { if (b < 0) return 0; // now 0 <= b <= INT_MAX, hence also // 0 <= INT_MAX - b <= INTMAX. // And "a + b <= INT_MAX" (which might overflow) is the // same as a <= INT_MAX - b (no overflow) return a <= INT_MAX - b; } // returns 1 if the product is valid, 0 on overflow. // negative factors are considered invalid. static int stbi__mul2sizes_valid(int a, int b) { if (a < 0 || b < 0) return 0; if (b == 0) return 1; // mul-by-0 is always safe // portable way to check for no overflows in a*b return a <= INT_MAX/b; } #if !defined(STBI_NO_JPEG) || !defined(STBI_NO_PNG) || !defined(STBI_NO_TGA) || !defined(STBI_NO_HDR) // returns 1 if "a*b + add" has no negative terms/factors and doesn't overflow static int stbi__mad2sizes_valid(int a, int b, int add) { return stbi__mul2sizes_valid(a, b) && stbi__addsizes_valid(a*b, add); } #endif // returns 1 if "a*b*c + add" has no negative terms/factors and doesn't overflow static int stbi__mad3sizes_valid(int a, int b, int c, int add) { return stbi__mul2sizes_valid(a, b) && stbi__mul2sizes_valid(a*b, c) && stbi__addsizes_valid(a*b*c, add); } // returns 1 if "a*b*c*d + add" has no negative terms/factors and doesn't overflow #if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) static int stbi__mad4sizes_valid(int a, int b, int c, int d, int add) { return stbi__mul2sizes_valid(a, b) && stbi__mul2sizes_valid(a*b, c) && stbi__mul2sizes_valid(a*b*c, d) && stbi__addsizes_valid(a*b*c*d, add); } #endif #if !defined(STBI_NO_JPEG) || !defined(STBI_NO_PNG) || !defined(STBI_NO_TGA) || !defined(STBI_NO_HDR) // mallocs with size overflow checking static void *stbi__malloc_mad2(int a, int b, int add) { if (!stbi__mad2sizes_valid(a, b, add)) return NULL; return stbi__malloc(a*b + add); } #endif static void *stbi__malloc_mad3(int a, int b, int c, int add) { if (!stbi__mad3sizes_valid(a, b, c, add)) return NULL; return stbi__malloc(a*b*c + add); } #if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) static void *stbi__malloc_mad4(int a, int b, int c, int d, int add) { if (!stbi__mad4sizes_valid(a, b, c, d, add)) return NULL; return stbi__malloc(a*b*c*d + add); } #endif // stbi__err - error // stbi__errpf - error returning pointer to float // stbi__errpuc - error returning pointer to unsigned char #ifdef STBI_NO_FAILURE_STRINGS #define stbi__err(x,y) 0 #elif defined(STBI_FAILURE_USERMSG) #define stbi__err(x,y) stbi__err(y) #else #define stbi__err(x,y) stbi__err(x) #endif #define stbi__errpf(x,y) ((float *)(size_t) (stbi__err(x,y)?NULL:NULL)) #define stbi__errpuc(x,y) ((unsigned char *)(size_t) (stbi__err(x,y)?NULL:NULL)) STBIDEF void stbi_image_free(void *retval_from_stbi_load) { STBI_FREE(retval_from_stbi_load); } #ifndef STBI_NO_LINEAR static float *stbi__ldr_to_hdr(stbi_uc *data, int x, int y, int comp); #endif #ifndef STBI_NO_HDR static stbi_uc *stbi__hdr_to_ldr(float *data, int x, int y, int comp); #endif static int stbi__vertically_flip_on_load_global = 0; STBIDEF void stbi_set_flip_vertically_on_load(int flag_true_if_should_flip) { stbi__vertically_flip_on_load_global = flag_true_if_should_flip; } #ifndef STBI_THREAD_LOCAL #define stbi__vertically_flip_on_load stbi__vertically_flip_on_load_global #else static STBI_THREAD_LOCAL int stbi__vertically_flip_on_load_local, stbi__vertically_flip_on_load_set; STBIDEF void stbi_set_flip_vertically_on_load_thread(int flag_true_if_should_flip) { stbi__vertically_flip_on_load_local = flag_true_if_should_flip; stbi__vertically_flip_on_load_set = 1; } #define stbi__vertically_flip_on_load (stbi__vertically_flip_on_load_set \ ? stbi__vertically_flip_on_load_local \ : stbi__vertically_flip_on_load_global) #endif // STBI_THREAD_LOCAL static void *stbi__load_main(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int bpc) { memset(ri, 0, sizeof(*ri)); // make sure it's initialized if we add new fields ri->bits_per_channel = 8; // default is 8 so most paths don't have to be changed ri->channel_order = STBI_ORDER_RGB; // all current input & output are this, but this is here so we can add BGR order ri->num_channels = 0; #ifndef STBI_NO_JPEG if (stbi__jpeg_test(s)) return stbi__jpeg_load(s,x,y,comp,req_comp, ri); #endif #ifndef STBI_NO_PNG if (stbi__png_test(s)) return stbi__png_load(s,x,y,comp,req_comp, ri); #endif #ifndef STBI_NO_BMP if (stbi__bmp_test(s)) return stbi__bmp_load(s,x,y,comp,req_comp, ri); #endif #ifndef STBI_NO_GIF if (stbi__gif_test(s)) return stbi__gif_load(s,x,y,comp,req_comp, ri); #endif #ifndef STBI_NO_PSD if (stbi__psd_test(s)) return stbi__psd_load(s,x,y,comp,req_comp, ri, bpc); #else STBI_NOTUSED(bpc); #endif #ifndef STBI_NO_PIC if (stbi__pic_test(s)) return stbi__pic_load(s,x,y,comp,req_comp, ri); #endif #ifndef STBI_NO_PNM if (stbi__pnm_test(s)) return stbi__pnm_load(s,x,y,comp,req_comp, ri); #endif #ifndef STBI_NO_HDR if (stbi__hdr_test(s)) { float *hdr = stbi__hdr_load(s, x,y,comp,req_comp, ri); return stbi__hdr_to_ldr(hdr, *x, *y, req_comp ? req_comp : *comp); } #endif #ifndef STBI_NO_TGA // test tga last because it's a crappy test! if (stbi__tga_test(s)) return stbi__tga_load(s,x,y,comp,req_comp, ri); #endif return stbi__errpuc("unknown image type", "Image not of any known type, or corrupt"); } static stbi_uc *stbi__convert_16_to_8(stbi__uint16 *orig, int w, int h, int channels) { int i; int img_len = w * h * channels; stbi_uc *reduced; reduced = (stbi_uc *) stbi__malloc(img_len); if (reduced == NULL) return stbi__errpuc("outofmem", "Out of memory"); for (i = 0; i < img_len; ++i) reduced[i] = (stbi_uc)((orig[i] >> 8) & 0xFF); // top half of each byte is sufficient approx of 16->8 bit scaling STBI_FREE(orig); return reduced; } static stbi__uint16 *stbi__convert_8_to_16(stbi_uc *orig, int w, int h, int channels) { int i; int img_len = w * h * channels; stbi__uint16 *enlarged; enlarged = (stbi__uint16 *) stbi__malloc(img_len*2); if (enlarged == NULL) return (stbi__uint16 *) stbi__errpuc("outofmem", "Out of memory"); for (i = 0; i < img_len; ++i) enlarged[i] = (stbi__uint16)((orig[i] << 8) + orig[i]); // replicate to high and low byte, maps 0->0, 255->0xffff STBI_FREE(orig); return enlarged; } static void stbi__vertical_flip(void *image, int w, int h, int bytes_per_pixel) { int row; size_t bytes_per_row = (size_t)w * bytes_per_pixel; stbi_uc temp[2048]; stbi_uc *bytes = (stbi_uc *)image; for (row = 0; row < (h>>1); row++) { stbi_uc *row0 = bytes + row*bytes_per_row; stbi_uc *row1 = bytes + (h - row - 1)*bytes_per_row; // swap row0 with row1 size_t bytes_left = bytes_per_row; while (bytes_left) { size_t bytes_copy = (bytes_left < sizeof(temp)) ? bytes_left : sizeof(temp); memcpy(temp, row0, bytes_copy); memcpy(row0, row1, bytes_copy); memcpy(row1, temp, bytes_copy); row0 += bytes_copy; row1 += bytes_copy; bytes_left -= bytes_copy; } } } #ifndef STBI_NO_GIF static void stbi__vertical_flip_slices(void *image, int w, int h, int z, int bytes_per_pixel) { int slice; int slice_size = w * h * bytes_per_pixel; stbi_uc *bytes = (stbi_uc *)image; for (slice = 0; slice < z; ++slice) { stbi__vertical_flip(bytes, w, h, bytes_per_pixel); bytes += slice_size; } } #endif static unsigned char *stbi__load_and_postprocess_8bit(stbi__context *s, int *x, int *y, int *comp, int req_comp) { stbi__result_info ri; void *result = stbi__load_main(s, x, y, comp, req_comp, &ri, 8); if (result == NULL) return NULL; // it is the responsibility of the loaders to make sure we get either 8 or 16 bit. STBI_ASSERT(ri.bits_per_channel == 8 || ri.bits_per_channel == 16); if (ri.bits_per_channel != 8) { result = stbi__convert_16_to_8((stbi__uint16 *) result, *x, *y, req_comp == 0 ? *comp : req_comp); ri.bits_per_channel = 8; } // @TODO: move stbi__convert_format to here if (stbi__vertically_flip_on_load) { int channels = req_comp ? req_comp : *comp; stbi__vertical_flip(result, *x, *y, channels * sizeof(stbi_uc)); } return (unsigned char *) result; } static stbi__uint16 *stbi__load_and_postprocess_16bit(stbi__context *s, int *x, int *y, int *comp, int req_comp) { stbi__result_info ri; void *result = stbi__load_main(s, x, y, comp, req_comp, &ri, 16); if (result == NULL) return NULL; // it is the responsibility of the loaders to make sure we get either 8 or 16 bit. STBI_ASSERT(ri.bits_per_channel == 8 || ri.bits_per_channel == 16); if (ri.bits_per_channel != 16) { result = stbi__convert_8_to_16((stbi_uc *) result, *x, *y, req_comp == 0 ? *comp : req_comp); ri.bits_per_channel = 16; } // @TODO: move stbi__convert_format16 to here // @TODO: special case RGB-to-Y (and RGBA-to-YA) for 8-bit-to-16-bit case to keep more precision if (stbi__vertically_flip_on_load) { int channels = req_comp ? req_comp : *comp; stbi__vertical_flip(result, *x, *y, channels * sizeof(stbi__uint16)); } return (stbi__uint16 *) result; } #if !defined(STBI_NO_HDR) && !defined(STBI_NO_LINEAR) static void stbi__float_postprocess(float *result, int *x, int *y, int *comp, int req_comp) { if (stbi__vertically_flip_on_load && result != NULL) { int channels = req_comp ? req_comp : *comp; stbi__vertical_flip(result, *x, *y, channels * sizeof(float)); } } #endif #ifndef STBI_NO_STDIO #if defined(_MSC_VER) && defined(STBI_WINDOWS_UTF8) STBI_EXTERN __declspec(dllimport) int __stdcall MultiByteToWideChar(unsigned int cp, unsigned long flags, const char *str, int cbmb, wchar_t *widestr, int cchwide); STBI_EXTERN __declspec(dllimport) int __stdcall WideCharToMultiByte(unsigned int cp, unsigned long flags, const wchar_t *widestr, int cchwide, char *str, int cbmb, const char *defchar, int *used_default); #endif #if defined(_MSC_VER) && defined(STBI_WINDOWS_UTF8) STBIDEF int stbi_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wchar_t* input) { return WideCharToMultiByte(65001 /* UTF8 */, 0, input, -1, buffer, (int) bufferlen, NULL, NULL); } #endif static FILE *stbi__fopen(char const *filename, char const *mode) { FILE *f; #if defined(_MSC_VER) && defined(STBI_WINDOWS_UTF8) wchar_t wMode[64]; wchar_t wFilename[1024]; if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, filename, -1, wFilename, sizeof(wFilename))) return 0; if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, mode, -1, wMode, sizeof(wMode))) return 0; #if _MSC_VER >= 1400 if (0 != _wfopen_s(&f, wFilename, wMode)) f = 0; #else f = _wfopen(wFilename, wMode); #endif #elif defined(_MSC_VER) && _MSC_VER >= 1400 if (0 != fopen_s(&f, filename, mode)) f=0; #else f = fopen(filename, mode); #endif return f; } STBIDEF stbi_uc *stbi_load(char const *filename, int *x, int *y, int *comp, int req_comp) { FILE *f = stbi__fopen(filename, "rb"); unsigned char *result; if (!f) return stbi__errpuc("can't fopen", "Unable to open file"); result = stbi_load_from_file(f,x,y,comp,req_comp); fclose(f); return result; } STBIDEF stbi_uc *stbi_load_from_file(FILE *f, int *x, int *y, int *comp, int req_comp) { unsigned char *result; stbi__context s; stbi__start_file(&s,f); result = stbi__load_and_postprocess_8bit(&s,x,y,comp,req_comp); if (result) { // need to 'unget' all the characters in the IO buffer fseek(f, - (int) (s.img_buffer_end - s.img_buffer), SEEK_CUR); } return result; } STBIDEF stbi__uint16 *stbi_load_from_file_16(FILE *f, int *x, int *y, int *comp, int req_comp) { stbi__uint16 *result; stbi__context s; stbi__start_file(&s,f); result = stbi__load_and_postprocess_16bit(&s,x,y,comp,req_comp); if (result) { // need to 'unget' all the characters in the IO buffer fseek(f, - (int) (s.img_buffer_end - s.img_buffer), SEEK_CUR); } return result; } STBIDEF stbi_us *stbi_load_16(char const *filename, int *x, int *y, int *comp, int req_comp) { FILE *f = stbi__fopen(filename, "rb"); stbi__uint16 *result; if (!f) return (stbi_us *) stbi__errpuc("can't fopen", "Unable to open file"); result = stbi_load_from_file_16(f,x,y,comp,req_comp); fclose(f); return result; } #endif //!STBI_NO_STDIO STBIDEF stbi_us *stbi_load_16_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels) { stbi__context s; stbi__start_mem(&s,buffer,len); return stbi__load_and_postprocess_16bit(&s,x,y,channels_in_file,desired_channels); } STBIDEF stbi_us *stbi_load_16_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *channels_in_file, int desired_channels) { stbi__context s; stbi__start_callbacks(&s, (stbi_io_callbacks *)clbk, user); return stbi__load_and_postprocess_16bit(&s,x,y,channels_in_file,desired_channels); } STBIDEF stbi_uc *stbi_load_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) { stbi__context s; stbi__start_mem(&s,buffer,len); return stbi__load_and_postprocess_8bit(&s,x,y,comp,req_comp); } STBIDEF stbi_uc *stbi_load_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp) { stbi__context s; stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user); return stbi__load_and_postprocess_8bit(&s,x,y,comp,req_comp); } #ifndef STBI_NO_GIF STBIDEF stbi_uc *stbi_load_gif_from_memory(stbi_uc const *buffer, int len, int **delays, int *x, int *y, int *z, int *comp, int req_comp) { unsigned char *result; stbi__context s; stbi__start_mem(&s,buffer,len); result = (unsigned char*) stbi__load_gif_main(&s, delays, x, y, z, comp, req_comp); if (stbi__vertically_flip_on_load) { stbi__vertical_flip_slices( result, *x, *y, *z, *comp ); } return result; } #endif #ifndef STBI_NO_LINEAR static float *stbi__loadf_main(stbi__context *s, int *x, int *y, int *comp, int req_comp) { unsigned char *data; #ifndef STBI_NO_HDR if (stbi__hdr_test(s)) { stbi__result_info ri; float *hdr_data = stbi__hdr_load(s,x,y,comp,req_comp, &ri); if (hdr_data) stbi__float_postprocess(hdr_data,x,y,comp,req_comp); return hdr_data; } #endif data = stbi__load_and_postprocess_8bit(s, x, y, comp, req_comp); if (data) return stbi__ldr_to_hdr(data, *x, *y, req_comp ? req_comp : *comp); return stbi__errpf("unknown image type", "Image not of any known type, or corrupt"); } STBIDEF float *stbi_loadf_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) { stbi__context s; stbi__start_mem(&s,buffer,len); return stbi__loadf_main(&s,x,y,comp,req_comp); } STBIDEF float *stbi_loadf_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp) { stbi__context s; stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user); return stbi__loadf_main(&s,x,y,comp,req_comp); } #ifndef STBI_NO_STDIO STBIDEF float *stbi_loadf(char const *filename, int *x, int *y, int *comp, int req_comp) { float *result; FILE *f = stbi__fopen(filename, "rb"); if (!f) return stbi__errpf("can't fopen", "Unable to open file"); result = stbi_loadf_from_file(f,x,y,comp,req_comp); fclose(f); return result; } STBIDEF float *stbi_loadf_from_file(FILE *f, int *x, int *y, int *comp, int req_comp) { stbi__context s; stbi__start_file(&s,f); return stbi__loadf_main(&s,x,y,comp,req_comp); } #endif // !STBI_NO_STDIO #endif // !STBI_NO_LINEAR // these is-hdr-or-not is defined independent of whether STBI_NO_LINEAR is // defined, for API simplicity; if STBI_NO_LINEAR is defined, it always // reports false! STBIDEF int stbi_is_hdr_from_memory(stbi_uc const *buffer, int len) { #ifndef STBI_NO_HDR stbi__context s; stbi__start_mem(&s,buffer,len); return stbi__hdr_test(&s); #else STBI_NOTUSED(buffer); STBI_NOTUSED(len); return 0; #endif } #ifndef STBI_NO_STDIO STBIDEF int stbi_is_hdr (char const *filename) { FILE *f = stbi__fopen(filename, "rb"); int result=0; if (f) { result = stbi_is_hdr_from_file(f); fclose(f); } return result; } STBIDEF int stbi_is_hdr_from_file(FILE *f) { #ifndef STBI_NO_HDR long pos = ftell(f); int res; stbi__context s; stbi__start_file(&s,f); res = stbi__hdr_test(&s); fseek(f, pos, SEEK_SET); return res; #else STBI_NOTUSED(f); return 0; #endif } #endif // !STBI_NO_STDIO STBIDEF int stbi_is_hdr_from_callbacks(stbi_io_callbacks const *clbk, void *user) { #ifndef STBI_NO_HDR stbi__context s; stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user); return stbi__hdr_test(&s); #else STBI_NOTUSED(clbk); STBI_NOTUSED(user); return 0; #endif } #ifndef STBI_NO_LINEAR static float stbi__l2h_gamma=2.2f, stbi__l2h_scale=1.0f; STBIDEF void stbi_ldr_to_hdr_gamma(float gamma) { stbi__l2h_gamma = gamma; } STBIDEF void stbi_ldr_to_hdr_scale(float scale) { stbi__l2h_scale = scale; } #endif static float stbi__h2l_gamma_i=1.0f/2.2f, stbi__h2l_scale_i=1.0f; STBIDEF void stbi_hdr_to_ldr_gamma(float gamma) { stbi__h2l_gamma_i = 1/gamma; } STBIDEF void stbi_hdr_to_ldr_scale(float scale) { stbi__h2l_scale_i = 1/scale; } ////////////////////////////////////////////////////////////////////////////// // // Common code used by all image loaders // enum { STBI__SCAN_load=0, STBI__SCAN_type, STBI__SCAN_header }; static void stbi__refill_buffer(stbi__context *s) { int n = (s->io.read)(s->io_user_data,(char*)s->buffer_start,s->buflen); s->callback_already_read += (int) (s->img_buffer - s->img_buffer_original); if (n == 0) { // at end of file, treat same as if from memory, but need to handle case // where s->img_buffer isn't pointing to safe memory, e.g. 0-byte file s->read_from_callbacks = 0; s->img_buffer = s->buffer_start; s->img_buffer_end = s->buffer_start+1; *s->img_buffer = 0; } else { s->img_buffer = s->buffer_start; s->img_buffer_end = s->buffer_start + n; } } stbi_inline static stbi_uc stbi__get8(stbi__context *s) { if (s->img_buffer < s->img_buffer_end) return *s->img_buffer++; if (s->read_from_callbacks) { stbi__refill_buffer(s); return *s->img_buffer++; } return 0; } #if defined(STBI_NO_JPEG) && defined(STBI_NO_HDR) && defined(STBI_NO_PIC) && defined(STBI_NO_PNM) // nothing #else stbi_inline static int stbi__at_eof(stbi__context *s) { if (s->io.read) { if (!(s->io.eof)(s->io_user_data)) return 0; // if feof() is true, check if buffer = end // special case: we've only got the special 0 character at the end if (s->read_from_callbacks == 0) return 1; } return s->img_buffer >= s->img_buffer_end; } #endif #if defined(STBI_NO_JPEG) && defined(STBI_NO_PNG) && defined(STBI_NO_BMP) && defined(STBI_NO_PSD) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) && defined(STBI_NO_PIC) // nothing #else static void stbi__skip(stbi__context *s, int n) { if (n == 0) return; // already there! if (n < 0) { s->img_buffer = s->img_buffer_end; return; } if (s->io.read) { int blen = (int) (s->img_buffer_end - s->img_buffer); if (blen < n) { s->img_buffer = s->img_buffer_end; (s->io.skip)(s->io_user_data, n - blen); return; } } s->img_buffer += n; } #endif #if defined(STBI_NO_PNG) && defined(STBI_NO_TGA) && defined(STBI_NO_HDR) && defined(STBI_NO_PNM) // nothing #else static int stbi__getn(stbi__context *s, stbi_uc *buffer, int n) { if (s->io.read) { int blen = (int) (s->img_buffer_end - s->img_buffer); if (blen < n) { int res, count; memcpy(buffer, s->img_buffer, blen); count = (s->io.read)(s->io_user_data, (char*) buffer + blen, n - blen); res = (count == (n-blen)); s->img_buffer = s->img_buffer_end; return res; } } if (s->img_buffer+n <= s->img_buffer_end) { memcpy(buffer, s->img_buffer, n); s->img_buffer += n; return 1; } else return 0; } #endif #if defined(STBI_NO_JPEG) && defined(STBI_NO_PNG) && defined(STBI_NO_PSD) && defined(STBI_NO_PIC) // nothing #else static int stbi__get16be(stbi__context *s) { int z = stbi__get8(s); return (z << 8) + stbi__get8(s); } #endif #if defined(STBI_NO_PNG) && defined(STBI_NO_PSD) && defined(STBI_NO_PIC) // nothing #else static stbi__uint32 stbi__get32be(stbi__context *s) { stbi__uint32 z = stbi__get16be(s); return (z << 16) + stbi__get16be(s); } #endif #if defined(STBI_NO_BMP) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) // nothing #else static int stbi__get16le(stbi__context *s) { int z = stbi__get8(s); return z + (stbi__get8(s) << 8); } #endif #ifndef STBI_NO_BMP static stbi__uint32 stbi__get32le(stbi__context *s) { stbi__uint32 z = stbi__get16le(s); return z + (stbi__get16le(s) << 16); } #endif #define STBI__BYTECAST(x) ((stbi_uc) ((x) & 255)) // truncate int to byte without warnings #if defined(STBI_NO_JPEG) && defined(STBI_NO_PNG) && defined(STBI_NO_BMP) && defined(STBI_NO_PSD) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) && defined(STBI_NO_PIC) && defined(STBI_NO_PNM) // nothing #else ////////////////////////////////////////////////////////////////////////////// // // generic converter from built-in img_n to req_comp // individual types do this automatically as much as possible (e.g. jpeg // does all cases internally since it needs to colorspace convert anyway, // and it never has alpha, so very few cases ). png can automatically // interleave an alpha=255 channel, but falls back to this for other cases // // assume data buffer is malloced, so malloc a new one and free that one // only failure mode is malloc failing static stbi_uc stbi__compute_y(int r, int g, int b) { return (stbi_uc) (((r*77) + (g*150) + (29*b)) >> 8); } #endif #if defined(STBI_NO_PNG) && defined(STBI_NO_BMP) && defined(STBI_NO_PSD) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) && defined(STBI_NO_PIC) && defined(STBI_NO_PNM) // nothing #else static unsigned char *stbi__convert_format(unsigned char *data, int img_n, int req_comp, unsigned int x, unsigned int y) { int i,j; unsigned char *good; if (req_comp == img_n) return data; STBI_ASSERT(req_comp >= 1 && req_comp <= 4); good = (unsigned char *) stbi__malloc_mad3(req_comp, x, y, 0); if (good == NULL) { STBI_FREE(data); return stbi__errpuc("outofmem", "Out of memory"); } for (j=0; j < (int) y; ++j) { unsigned char *src = data + j * x * img_n ; unsigned char *dest = good + j * x * req_comp; #define STBI__COMBO(a,b) ((a)*8+(b)) #define STBI__CASE(a,b) case STBI__COMBO(a,b): for(i=x-1; i >= 0; --i, src += a, dest += b) // convert source image with img_n components to one with req_comp components; // avoid switch per pixel, so use switch per scanline and massive macros switch (STBI__COMBO(img_n, req_comp)) { STBI__CASE(1,2) { dest[0]=src[0]; dest[1]=255; } break; STBI__CASE(1,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; STBI__CASE(1,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=255; } break; STBI__CASE(2,1) { dest[0]=src[0]; } break; STBI__CASE(2,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; STBI__CASE(2,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=src[1]; } break; STBI__CASE(3,4) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2];dest[3]=255; } break; STBI__CASE(3,1) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); } break; STBI__CASE(3,2) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); dest[1] = 255; } break; STBI__CASE(4,1) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); } break; STBI__CASE(4,2) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); dest[1] = src[3]; } break; STBI__CASE(4,3) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2]; } break; default: STBI_ASSERT(0); STBI_FREE(data); STBI_FREE(good); return stbi__errpuc("unsupported", "Unsupported format conversion"); } #undef STBI__CASE } STBI_FREE(data); return good; } #endif #if defined(STBI_NO_PNG) && defined(STBI_NO_PSD) // nothing #else static stbi__uint16 stbi__compute_y_16(int r, int g, int b) { return (stbi__uint16) (((r*77) + (g*150) + (29*b)) >> 8); } #endif #if defined(STBI_NO_PNG) && defined(STBI_NO_PSD) // nothing #else static stbi__uint16 *stbi__convert_format16(stbi__uint16 *data, int img_n, int req_comp, unsigned int x, unsigned int y) { int i,j; stbi__uint16 *good; if (req_comp == img_n) return data; STBI_ASSERT(req_comp >= 1 && req_comp <= 4); good = (stbi__uint16 *) stbi__malloc(req_comp * x * y * 2); if (good == NULL) { STBI_FREE(data); return (stbi__uint16 *) stbi__errpuc("outofmem", "Out of memory"); } for (j=0; j < (int) y; ++j) { stbi__uint16 *src = data + j * x * img_n ; stbi__uint16 *dest = good + j * x * req_comp; #define STBI__COMBO(a,b) ((a)*8+(b)) #define STBI__CASE(a,b) case STBI__COMBO(a,b): for(i=x-1; i >= 0; --i, src += a, dest += b) // convert source image with img_n components to one with req_comp components; // avoid switch per pixel, so use switch per scanline and massive macros switch (STBI__COMBO(img_n, req_comp)) { STBI__CASE(1,2) { dest[0]=src[0]; dest[1]=0xffff; } break; STBI__CASE(1,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; STBI__CASE(1,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=0xffff; } break; STBI__CASE(2,1) { dest[0]=src[0]; } break; STBI__CASE(2,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; STBI__CASE(2,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=src[1]; } break; STBI__CASE(3,4) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2];dest[3]=0xffff; } break; STBI__CASE(3,1) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); } break; STBI__CASE(3,2) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); dest[1] = 0xffff; } break; STBI__CASE(4,1) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); } break; STBI__CASE(4,2) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); dest[1] = src[3]; } break; STBI__CASE(4,3) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2]; } break; default: STBI_ASSERT(0); STBI_FREE(data); STBI_FREE(good); return (stbi__uint16*) stbi__errpuc("unsupported", "Unsupported format conversion"); } #undef STBI__CASE } STBI_FREE(data); return good; } #endif #ifndef STBI_NO_LINEAR static float *stbi__ldr_to_hdr(stbi_uc *data, int x, int y, int comp) { int i,k,n; float *output; if (!data) return NULL; output = (float *) stbi__malloc_mad4(x, y, comp, sizeof(float), 0); if (output == NULL) { STBI_FREE(data); return stbi__errpf("outofmem", "Out of memory"); } // compute number of non-alpha components if (comp & 1) n = comp; else n = comp-1; for (i=0; i < x*y; ++i) { for (k=0; k < n; ++k) { output[i*comp + k] = (float) (pow(data[i*comp+k]/255.0f, stbi__l2h_gamma) * stbi__l2h_scale); } } if (n < comp) { for (i=0; i < x*y; ++i) { output[i*comp + n] = data[i*comp + n]/255.0f; } } STBI_FREE(data); return output; } #endif #ifndef STBI_NO_HDR #define stbi__float2int(x) ((int) (x)) static stbi_uc *stbi__hdr_to_ldr(float *data, int x, int y, int comp) { int i,k,n; stbi_uc *output; if (!data) return NULL; output = (stbi_uc *) stbi__malloc_mad3(x, y, comp, 0); if (output == NULL) { STBI_FREE(data); return stbi__errpuc("outofmem", "Out of memory"); } // compute number of non-alpha components if (comp & 1) n = comp; else n = comp-1; for (i=0; i < x*y; ++i) { for (k=0; k < n; ++k) { float z = (float) pow(data[i*comp+k]*stbi__h2l_scale_i, stbi__h2l_gamma_i) * 255 + 0.5f; if (z < 0) z = 0; if (z > 255) z = 255; output[i*comp + k] = (stbi_uc) stbi__float2int(z); } if (k < comp) { float z = data[i*comp+k] * 255 + 0.5f; if (z < 0) z = 0; if (z > 255) z = 255; output[i*comp + k] = (stbi_uc) stbi__float2int(z); } } STBI_FREE(data); return output; } #endif ////////////////////////////////////////////////////////////////////////////// // // "baseline" JPEG/JFIF decoder // // simple implementation // - doesn't support delayed output of y-dimension // - simple interface (only one output format: 8-bit interleaved RGB) // - doesn't try to recover corrupt jpegs // - doesn't allow partial loading, loading multiple at once // - still fast on x86 (copying globals into locals doesn't help x86) // - allocates lots of intermediate memory (full size of all components) // - non-interleaved case requires this anyway // - allows good upsampling (see next) // high-quality // - upsampled channels are bilinearly interpolated, even across blocks // - quality integer IDCT derived from IJG's 'slow' // performance // - fast huffman; reasonable integer IDCT // - some SIMD kernels for common paths on targets with SSE2/NEON // - uses a lot of intermediate memory, could cache poorly #ifndef STBI_NO_JPEG // huffman decoding acceleration #define FAST_BITS 9 // larger handles more cases; smaller stomps less cache typedef struct { stbi_uc fast[1 << FAST_BITS]; // weirdly, repacking this into AoS is a 10% speed loss, instead of a win stbi__uint16 code[256]; stbi_uc values[256]; stbi_uc size[257]; unsigned int maxcode[18]; int delta[17]; // old 'firstsymbol' - old 'firstcode' } stbi__huffman; typedef struct { stbi__context *s; stbi__huffman huff_dc[4]; stbi__huffman huff_ac[4]; stbi__uint16 dequant[4][64]; stbi__int16 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; // definition of jpeg image component struct { int id; int h,v; int tq; int hd,ha; int dc_pred; int x,y,w2,h2; stbi_uc *data; void *raw_data, *raw_coeff; stbi_uc *linebuf; short *coeff; // progressive only int coeff_w, coeff_h; // number of 8x8 coefficient blocks } img_comp[4]; stbi__uint32 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 int progressive; int spec_start; int spec_end; int succ_high; int succ_low; int eob_run; int jfif; int app14_color_transform; // Adobe APP14 tag int rgb; int scan_n, order[4]; int restart_interval, todo; // kernels void (*idct_block_kernel)(stbi_uc *out, int out_stride, short data[64]); void (*YCbCr_to_RGB_kernel)(stbi_uc *out, const stbi_uc *y, const stbi_uc *pcb, const stbi_uc *pcr, int count, int step); stbi_uc *(*resample_row_hv_2_kernel)(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs); } stbi__jpeg; static int stbi__build_huffman(stbi__huffman *h, int *count) { int i,j,k=0; unsigned int code; // build size list for each symbol (from JPEG spec) for (i=0; i < 16; ++i) for (j=0; j < count[i]; ++j) h->size[k++] = (stbi_uc) (i+1); h->size[k] = 0; // compute actual symbols (from jpeg spec) code = 0; k = 0; for(j=1; j <= 16; ++j) { // compute delta to add to code to compute symbol id h->delta[j] = k - code; if (h->size[k] == j) { while (h->size[k] == j) h->code[k++] = (stbi__uint16) (code++); if (code-1 >= (1u << j)) return stbi__err("bad code lengths","Corrupt JPEG"); } // compute largest code + 1 for this size, preshifted as needed later h->maxcode[j] = code << (16-j); code <<= 1; } h->maxcode[j] = 0xffffffff; // build non-spec acceleration table; 255 is flag for not-accelerated memset(h->fast, 255, 1 << FAST_BITS); for (i=0; i < k; ++i) { int s = h->size[i]; if (s <= FAST_BITS) { int c = h->code[i] << (FAST_BITS-s); int m = 1 << (FAST_BITS-s); for (j=0; j < m; ++j) { h->fast[c+j] = (stbi_uc) i; } } } return 1; } // build a table that decodes both magnitude and value of small ACs in // one go. static void stbi__build_fast_ac(stbi__int16 *fast_ac, stbi__huffman *h) { int i; for (i=0; i < (1 << FAST_BITS); ++i) { stbi_uc fast = h->fast[i]; fast_ac[i] = 0; if (fast < 255) { int rs = h->values[fast]; int run = (rs >> 4) & 15; int magbits = rs & 15; int len = h->size[fast]; if (magbits && len + magbits <= FAST_BITS) { // magnitude code followed by receive_extend code int k = ((i << len) & ((1 << FAST_BITS) - 1)) >> (FAST_BITS - magbits); int m = 1 << (magbits - 1); if (k < m) k += (~0U << magbits) + 1; // if the result is small enough, we can fit it in fast_ac table if (k >= -128 && k <= 127) fast_ac[i] = (stbi__int16) ((k * 256) + (run * 16) + (len + magbits)); } } } } static void stbi__grow_buffer_unsafe(stbi__jpeg *j) { do { unsigned int b = j->nomore ? 0 : stbi__get8(j->s); if (b == 0xff) { int c = stbi__get8(j->s); while (c == 0xff) c = stbi__get8(j->s); // consume fill bytes if (c != 0) { j->marker = (unsigned char) c; j->nomore = 1; return; } } j->code_buffer |= b << (24 - j->code_bits); j->code_bits += 8; } while (j->code_bits <= 24); } // (1 << n) - 1 static const stbi__uint32 stbi__bmask[17]={0,1,3,7,15,31,63,127,255,511,1023,2047,4095,8191,16383,32767,65535}; // decode a jpeg huffman value from the bitstream stbi_inline static int stbi__jpeg_huff_decode(stbi__jpeg *j, stbi__huffman *h) { unsigned int temp; int c,k; if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); // look at the top FAST_BITS and determine what symbol ID it is, // if the code is <= FAST_BITS c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1); k = h->fast[c]; if (k < 255) { int s = h->size[k]; if (s > j->code_bits) return -1; j->code_buffer <<= s; j->code_bits -= s; return h->values[k]; } // naive test is to shift the code_buffer down so k bits are // valid, then test against maxcode. To speed this up, we've // preshifted maxcode left so that it has (16-k) 0s at the // end; in other words, regardless of the number of bits, it // wants to be compared against something shifted to have 16; // that way we don't need to shift inside the loop. temp = j->code_buffer >> 16; for (k=FAST_BITS+1 ; ; ++k) if (temp < h->maxcode[k]) break; if (k == 17) { // error! code not found j->code_bits -= 16; return -1; } if (k > j->code_bits) return -1; // convert the huffman code to the symbol id c = ((j->code_buffer >> (32 - k)) & stbi__bmask[k]) + h->delta[k]; STBI_ASSERT((((j->code_buffer) >> (32 - h->size[c])) & stbi__bmask[h->size[c]]) == h->code[c]); // convert the id to a symbol j->code_bits -= k; j->code_buffer <<= k; return h->values[c]; } // bias[n] = (-1<code_bits < n) stbi__grow_buffer_unsafe(j); sgn = (stbi__int32)j->code_buffer >> 31; // sign bit is always in MSB k = stbi_lrot(j->code_buffer, n); if (n < 0 || n >= (int) (sizeof(stbi__bmask)/sizeof(*stbi__bmask))) return 0; j->code_buffer = k & ~stbi__bmask[n]; k &= stbi__bmask[n]; j->code_bits -= n; return k + (stbi__jbias[n] & ~sgn); } // get some unsigned bits stbi_inline static int stbi__jpeg_get_bits(stbi__jpeg *j, int n) { unsigned int k; if (j->code_bits < n) stbi__grow_buffer_unsafe(j); k = stbi_lrot(j->code_buffer, n); j->code_buffer = k & ~stbi__bmask[n]; k &= stbi__bmask[n]; j->code_bits -= n; return k; } stbi_inline static int stbi__jpeg_get_bit(stbi__jpeg *j) { unsigned int k; if (j->code_bits < 1) stbi__grow_buffer_unsafe(j); k = j->code_buffer; j->code_buffer <<= 1; --j->code_bits; return k & 0x80000000; } // given a value that's at position X in the zigzag stream, // where does it appear in the 8x8 matrix coded as row-major? static const stbi_uc stbi__jpeg_dezigzag[64+15] = { 0, 1, 8, 16, 9, 2, 3, 10, 17, 24, 32, 25, 18, 11, 4, 5, 12, 19, 26, 33, 40, 48, 41, 34, 27, 20, 13, 6, 7, 14, 21, 28, 35, 42, 49, 56, 57, 50, 43, 36, 29, 22, 15, 23, 30, 37, 44, 51, 58, 59, 52, 45, 38, 31, 39, 46, 53, 60, 61, 54, 47, 55, 62, 63, // let corrupt input sample past end 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63 }; // decode one 64-entry block-- static int stbi__jpeg_decode_block(stbi__jpeg *j, short data[64], stbi__huffman *hdc, stbi__huffman *hac, stbi__int16 *fac, int b, stbi__uint16 *dequant) { int diff,dc,k; int t; if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); t = stbi__jpeg_huff_decode(j, hdc); if (t < 0) return stbi__err("bad huffman code","Corrupt JPEG"); // 0 all the ac values now so we can do it 32-bits at a time memset(data,0,64*sizeof(data[0])); diff = t ? stbi__extend_receive(j, t) : 0; dc = j->img_comp[b].dc_pred + diff; j->img_comp[b].dc_pred = dc; data[0] = (short) (dc * dequant[0]); // decode AC components, see JPEG spec k = 1; do { unsigned int zig; int c,r,s; if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1); r = fac[c]; if (r) { // fast-AC path k += (r >> 4) & 15; // run s = r & 15; // combined length j->code_buffer <<= s; j->code_bits -= s; // decode into unzigzag'd location zig = stbi__jpeg_dezigzag[k++]; data[zig] = (short) ((r >> 8) * dequant[zig]); } else { int rs = stbi__jpeg_huff_decode(j, hac); if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG"); s = rs & 15; r = rs >> 4; if (s == 0) { if (rs != 0xf0) break; // end block k += 16; } else { k += r; // decode into unzigzag'd location zig = stbi__jpeg_dezigzag[k++]; data[zig] = (short) (stbi__extend_receive(j,s) * dequant[zig]); } } } while (k < 64); return 1; } static int stbi__jpeg_decode_block_prog_dc(stbi__jpeg *j, short data[64], stbi__huffman *hdc, int b) { int diff,dc; int t; if (j->spec_end != 0) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); if (j->succ_high == 0) { // first scan for DC coefficient, must be first memset(data,0,64*sizeof(data[0])); // 0 all the ac values now t = stbi__jpeg_huff_decode(j, hdc); if (t == -1) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); diff = t ? stbi__extend_receive(j, t) : 0; dc = j->img_comp[b].dc_pred + diff; j->img_comp[b].dc_pred = dc; data[0] = (short) (dc << j->succ_low); } else { // refinement scan for DC coefficient if (stbi__jpeg_get_bit(j)) data[0] += (short) (1 << j->succ_low); } return 1; } // @OPTIMIZE: store non-zigzagged during the decode passes, // and only de-zigzag when dequantizing static int stbi__jpeg_decode_block_prog_ac(stbi__jpeg *j, short data[64], stbi__huffman *hac, stbi__int16 *fac) { int k; if (j->spec_start == 0) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); if (j->succ_high == 0) { int shift = j->succ_low; if (j->eob_run) { --j->eob_run; return 1; } k = j->spec_start; do { unsigned int zig; int c,r,s; if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1); r = fac[c]; if (r) { // fast-AC path k += (r >> 4) & 15; // run s = r & 15; // combined length j->code_buffer <<= s; j->code_bits -= s; zig = stbi__jpeg_dezigzag[k++]; data[zig] = (short) ((r >> 8) << shift); } else { int rs = stbi__jpeg_huff_decode(j, hac); if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG"); s = rs & 15; r = rs >> 4; if (s == 0) { if (r < 15) { j->eob_run = (1 << r); if (r) j->eob_run += stbi__jpeg_get_bits(j, r); --j->eob_run; break; } k += 16; } else { k += r; zig = stbi__jpeg_dezigzag[k++]; data[zig] = (short) (stbi__extend_receive(j,s) << shift); } } } while (k <= j->spec_end); } else { // refinement scan for these AC coefficients short bit = (short) (1 << j->succ_low); if (j->eob_run) { --j->eob_run; for (k = j->spec_start; k <= j->spec_end; ++k) { short *p = &data[stbi__jpeg_dezigzag[k]]; if (*p != 0) if (stbi__jpeg_get_bit(j)) if ((*p & bit)==0) { if (*p > 0) *p += bit; else *p -= bit; } } } else { k = j->spec_start; do { int r,s; int rs = stbi__jpeg_huff_decode(j, hac); // @OPTIMIZE see if we can use the fast path here, advance-by-r is so slow, eh if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG"); s = rs & 15; r = rs >> 4; if (s == 0) { if (r < 15) { j->eob_run = (1 << r) - 1; if (r) j->eob_run += stbi__jpeg_get_bits(j, r); r = 64; // force end of block } else { // r=15 s=0 should write 16 0s, so we just do // a run of 15 0s and then write s (which is 0), // so we don't have to do anything special here } } else { if (s != 1) return stbi__err("bad huffman code", "Corrupt JPEG"); // sign bit if (stbi__jpeg_get_bit(j)) s = bit; else s = -bit; } // advance by r while (k <= j->spec_end) { short *p = &data[stbi__jpeg_dezigzag[k++]]; if (*p != 0) { if (stbi__jpeg_get_bit(j)) if ((*p & bit)==0) { if (*p > 0) *p += bit; else *p -= bit; } } else { if (r == 0) { *p = (short) s; break; } --r; } } } while (k <= j->spec_end); } } return 1; } // take a -128..127 value and stbi__clamp it and convert to 0..255 stbi_inline static stbi_uc stbi__clamp(int x) { // trick to use a single test to catch both cases if ((unsigned int) x > 255) { if (x < 0) return 0; if (x > 255) return 255; } return (stbi_uc) x; } #define stbi__f2f(x) ((int) (((x) * 4096 + 0.5))) #define stbi__fsh(x) ((x) * 4096) // derived from jidctint -- DCT_ISLOW #define STBI__IDCT_1D(s0,s1,s2,s3,s4,s5,s6,s7) \ int t0,t1,t2,t3,p1,p2,p3,p4,p5,x0,x1,x2,x3; \ p2 = s2; \ p3 = s6; \ p1 = (p2+p3) * stbi__f2f(0.5411961f); \ t2 = p1 + p3*stbi__f2f(-1.847759065f); \ t3 = p1 + p2*stbi__f2f( 0.765366865f); \ p2 = s0; \ p3 = s4; \ t0 = stbi__fsh(p2+p3); \ t1 = stbi__fsh(p2-p3); \ x0 = t0+t3; \ x3 = t0-t3; \ x1 = t1+t2; \ x2 = t1-t2; \ t0 = s7; \ t1 = s5; \ t2 = s3; \ t3 = s1; \ p3 = t0+t2; \ p4 = t1+t3; \ p1 = t0+t3; \ p2 = t1+t2; \ p5 = (p3+p4)*stbi__f2f( 1.175875602f); \ t0 = t0*stbi__f2f( 0.298631336f); \ t1 = t1*stbi__f2f( 2.053119869f); \ t2 = t2*stbi__f2f( 3.072711026f); \ t3 = t3*stbi__f2f( 1.501321110f); \ p1 = p5 + p1*stbi__f2f(-0.899976223f); \ p2 = p5 + p2*stbi__f2f(-2.562915447f); \ p3 = p3*stbi__f2f(-1.961570560f); \ p4 = p4*stbi__f2f(-0.390180644f); \ t3 += p1+p4; \ t2 += p2+p3; \ t1 += p2+p4; \ t0 += p1+p3; static void stbi__idct_block(stbi_uc *out, int out_stride, short data[64]) { int i,val[64],*v=val; stbi_uc *o; short *d = data; // columns for (i=0; i < 8; ++i,++d, ++v) { // if all zeroes, shortcut -- this avoids dequantizing 0s and IDCTing if (d[ 8]==0 && d[16]==0 && d[24]==0 && d[32]==0 && d[40]==0 && d[48]==0 && d[56]==0) { // no shortcut 0 seconds // (1|2|3|4|5|6|7)==0 0 seconds // all separate -0.047 seconds // 1 && 2|3 && 4|5 && 6|7: -0.047 seconds int dcterm = d[0]*4; v[0] = v[8] = v[16] = v[24] = v[32] = v[40] = v[48] = v[56] = dcterm; } else { STBI__IDCT_1D(d[ 0],d[ 8],d[16],d[24],d[32],d[40],d[48],d[56]) // constants scaled things up by 1<<12; let's bring them back // down, but keep 2 extra bits of precision x0 += 512; x1 += 512; x2 += 512; x3 += 512; v[ 0] = (x0+t3) >> 10; v[56] = (x0-t3) >> 10; v[ 8] = (x1+t2) >> 10; v[48] = (x1-t2) >> 10; v[16] = (x2+t1) >> 10; v[40] = (x2-t1) >> 10; v[24] = (x3+t0) >> 10; v[32] = (x3-t0) >> 10; } } for (i=0, v=val, o=out; i < 8; ++i,v+=8,o+=out_stride) { // no fast case since the first 1D IDCT spread components out STBI__IDCT_1D(v[0],v[1],v[2],v[3],v[4],v[5],v[6],v[7]) // constants scaled things up by 1<<12, plus we had 1<<2 from first // loop, plus horizontal and vertical each scale by sqrt(8) so together // we've got an extra 1<<3, so 1<<17 total we need to remove. // so we want to round that, which means adding 0.5 * 1<<17, // aka 65536. Also, we'll end up with -128 to 127 that we want // to encode as 0..255 by adding 128, so we'll add that before the shift x0 += 65536 + (128<<17); x1 += 65536 + (128<<17); x2 += 65536 + (128<<17); x3 += 65536 + (128<<17); // tried computing the shifts into temps, or'ing the temps to see // if any were out of range, but that was slower o[0] = stbi__clamp((x0+t3) >> 17); o[7] = stbi__clamp((x0-t3) >> 17); o[1] = stbi__clamp((x1+t2) >> 17); o[6] = stbi__clamp((x1-t2) >> 17); o[2] = stbi__clamp((x2+t1) >> 17); o[5] = stbi__clamp((x2-t1) >> 17); o[3] = stbi__clamp((x3+t0) >> 17); o[4] = stbi__clamp((x3-t0) >> 17); } } #ifdef STBI_SSE2 // sse2 integer IDCT. not the fastest possible implementation but it // produces bit-identical results to the generic C version so it's // fully "transparent". static void stbi__idct_simd(stbi_uc *out, int out_stride, short data[64]) { // This is constructed to match our regular (generic) integer IDCT exactly. __m128i row0, row1, row2, row3, row4, row5, row6, row7; __m128i tmp; // dot product constant: even elems=x, odd elems=y #define dct_const(x,y) _mm_setr_epi16((x),(y),(x),(y),(x),(y),(x),(y)) // out(0) = c0[even]*x + c0[odd]*y (c0, x, y 16-bit, out 32-bit) // out(1) = c1[even]*x + c1[odd]*y #define dct_rot(out0,out1, x,y,c0,c1) \ __m128i c0##lo = _mm_unpacklo_epi16((x),(y)); \ __m128i c0##hi = _mm_unpackhi_epi16((x),(y)); \ __m128i out0##_l = _mm_madd_epi16(c0##lo, c0); \ __m128i out0##_h = _mm_madd_epi16(c0##hi, c0); \ __m128i out1##_l = _mm_madd_epi16(c0##lo, c1); \ __m128i out1##_h = _mm_madd_epi16(c0##hi, c1) // out = in << 12 (in 16-bit, out 32-bit) #define dct_widen(out, in) \ __m128i out##_l = _mm_srai_epi32(_mm_unpacklo_epi16(_mm_setzero_si128(), (in)), 4); \ __m128i out##_h = _mm_srai_epi32(_mm_unpackhi_epi16(_mm_setzero_si128(), (in)), 4) // wide add #define dct_wadd(out, a, b) \ __m128i out##_l = _mm_add_epi32(a##_l, b##_l); \ __m128i out##_h = _mm_add_epi32(a##_h, b##_h) // wide sub #define dct_wsub(out, a, b) \ __m128i out##_l = _mm_sub_epi32(a##_l, b##_l); \ __m128i out##_h = _mm_sub_epi32(a##_h, b##_h) // butterfly a/b, add bias, then shift by "s" and pack #define dct_bfly32o(out0, out1, a,b,bias,s) \ { \ __m128i abiased_l = _mm_add_epi32(a##_l, bias); \ __m128i abiased_h = _mm_add_epi32(a##_h, bias); \ dct_wadd(sum, abiased, b); \ dct_wsub(dif, abiased, b); \ out0 = _mm_packs_epi32(_mm_srai_epi32(sum_l, s), _mm_srai_epi32(sum_h, s)); \ out1 = _mm_packs_epi32(_mm_srai_epi32(dif_l, s), _mm_srai_epi32(dif_h, s)); \ } // 8-bit interleave step (for transposes) #define dct_interleave8(a, b) \ tmp = a; \ a = _mm_unpacklo_epi8(a, b); \ b = _mm_unpackhi_epi8(tmp, b) // 16-bit interleave step (for transposes) #define dct_interleave16(a, b) \ tmp = a; \ a = _mm_unpacklo_epi16(a, b); \ b = _mm_unpackhi_epi16(tmp, b) #define dct_pass(bias,shift) \ { \ /* even part */ \ dct_rot(t2e,t3e, row2,row6, rot0_0,rot0_1); \ __m128i sum04 = _mm_add_epi16(row0, row4); \ __m128i dif04 = _mm_sub_epi16(row0, row4); \ dct_widen(t0e, sum04); \ dct_widen(t1e, dif04); \ dct_wadd(x0, t0e, t3e); \ dct_wsub(x3, t0e, t3e); \ dct_wadd(x1, t1e, t2e); \ dct_wsub(x2, t1e, t2e); \ /* odd part */ \ dct_rot(y0o,y2o, row7,row3, rot2_0,rot2_1); \ dct_rot(y1o,y3o, row5,row1, rot3_0,rot3_1); \ __m128i sum17 = _mm_add_epi16(row1, row7); \ __m128i sum35 = _mm_add_epi16(row3, row5); \ dct_rot(y4o,y5o, sum17,sum35, rot1_0,rot1_1); \ dct_wadd(x4, y0o, y4o); \ dct_wadd(x5, y1o, y5o); \ dct_wadd(x6, y2o, y5o); \ dct_wadd(x7, y3o, y4o); \ dct_bfly32o(row0,row7, x0,x7,bias,shift); \ dct_bfly32o(row1,row6, x1,x6,bias,shift); \ dct_bfly32o(row2,row5, x2,x5,bias,shift); \ dct_bfly32o(row3,row4, x3,x4,bias,shift); \ } __m128i rot0_0 = dct_const(stbi__f2f(0.5411961f), stbi__f2f(0.5411961f) + stbi__f2f(-1.847759065f)); __m128i rot0_1 = dct_const(stbi__f2f(0.5411961f) + stbi__f2f( 0.765366865f), stbi__f2f(0.5411961f)); __m128i rot1_0 = dct_const(stbi__f2f(1.175875602f) + stbi__f2f(-0.899976223f), stbi__f2f(1.175875602f)); __m128i rot1_1 = dct_const(stbi__f2f(1.175875602f), stbi__f2f(1.175875602f) + stbi__f2f(-2.562915447f)); __m128i rot2_0 = dct_const(stbi__f2f(-1.961570560f) + stbi__f2f( 0.298631336f), stbi__f2f(-1.961570560f)); __m128i rot2_1 = dct_const(stbi__f2f(-1.961570560f), stbi__f2f(-1.961570560f) + stbi__f2f( 3.072711026f)); __m128i rot3_0 = dct_const(stbi__f2f(-0.390180644f) + stbi__f2f( 2.053119869f), stbi__f2f(-0.390180644f)); __m128i rot3_1 = dct_const(stbi__f2f(-0.390180644f), stbi__f2f(-0.390180644f) + stbi__f2f( 1.501321110f)); // rounding biases in column/row passes, see stbi__idct_block for explanation. __m128i bias_0 = _mm_set1_epi32(512); __m128i bias_1 = _mm_set1_epi32(65536 + (128<<17)); // load row0 = _mm_load_si128((const __m128i *) (data + 0*8)); row1 = _mm_load_si128((const __m128i *) (data + 1*8)); row2 = _mm_load_si128((const __m128i *) (data + 2*8)); row3 = _mm_load_si128((const __m128i *) (data + 3*8)); row4 = _mm_load_si128((const __m128i *) (data + 4*8)); row5 = _mm_load_si128((const __m128i *) (data + 5*8)); row6 = _mm_load_si128((const __m128i *) (data + 6*8)); row7 = _mm_load_si128((const __m128i *) (data + 7*8)); // column pass dct_pass(bias_0, 10); { // 16bit 8x8 transpose pass 1 dct_interleave16(row0, row4); dct_interleave16(row1, row5); dct_interleave16(row2, row6); dct_interleave16(row3, row7); // transpose pass 2 dct_interleave16(row0, row2); dct_interleave16(row1, row3); dct_interleave16(row4, row6); dct_interleave16(row5, row7); // transpose pass 3 dct_interleave16(row0, row1); dct_interleave16(row2, row3); dct_interleave16(row4, row5); dct_interleave16(row6, row7); } // row pass dct_pass(bias_1, 17); { // pack __m128i p0 = _mm_packus_epi16(row0, row1); // a0a1a2a3...a7b0b1b2b3...b7 __m128i p1 = _mm_packus_epi16(row2, row3); __m128i p2 = _mm_packus_epi16(row4, row5); __m128i p3 = _mm_packus_epi16(row6, row7); // 8bit 8x8 transpose pass 1 dct_interleave8(p0, p2); // a0e0a1e1... dct_interleave8(p1, p3); // c0g0c1g1... // transpose pass 2 dct_interleave8(p0, p1); // a0c0e0g0... dct_interleave8(p2, p3); // b0d0f0h0... // transpose pass 3 dct_interleave8(p0, p2); // a0b0c0d0... dct_interleave8(p1, p3); // a4b4c4d4... // store _mm_storel_epi64((__m128i *) out, p0); out += out_stride; _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p0, 0x4e)); out += out_stride; _mm_storel_epi64((__m128i *) out, p2); out += out_stride; _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p2, 0x4e)); out += out_stride; _mm_storel_epi64((__m128i *) out, p1); out += out_stride; _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p1, 0x4e)); out += out_stride; _mm_storel_epi64((__m128i *) out, p3); out += out_stride; _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p3, 0x4e)); } #undef dct_const #undef dct_rot #undef dct_widen #undef dct_wadd #undef dct_wsub #undef dct_bfly32o #undef dct_interleave8 #undef dct_interleave16 #undef dct_pass } #endif // STBI_SSE2 #ifdef STBI_NEON // NEON integer IDCT. should produce bit-identical // results to the generic C version. static void stbi__idct_simd(stbi_uc *out, int out_stride, short data[64]) { int16x8_t row0, row1, row2, row3, row4, row5, row6, row7; int16x4_t rot0_0 = vdup_n_s16(stbi__f2f(0.5411961f)); int16x4_t rot0_1 = vdup_n_s16(stbi__f2f(-1.847759065f)); int16x4_t rot0_2 = vdup_n_s16(stbi__f2f( 0.765366865f)); int16x4_t rot1_0 = vdup_n_s16(stbi__f2f( 1.175875602f)); int16x4_t rot1_1 = vdup_n_s16(stbi__f2f(-0.899976223f)); int16x4_t rot1_2 = vdup_n_s16(stbi__f2f(-2.562915447f)); int16x4_t rot2_0 = vdup_n_s16(stbi__f2f(-1.961570560f)); int16x4_t rot2_1 = vdup_n_s16(stbi__f2f(-0.390180644f)); int16x4_t rot3_0 = vdup_n_s16(stbi__f2f( 0.298631336f)); int16x4_t rot3_1 = vdup_n_s16(stbi__f2f( 2.053119869f)); int16x4_t rot3_2 = vdup_n_s16(stbi__f2f( 3.072711026f)); int16x4_t rot3_3 = vdup_n_s16(stbi__f2f( 1.501321110f)); #define dct_long_mul(out, inq, coeff) \ int32x4_t out##_l = vmull_s16(vget_low_s16(inq), coeff); \ int32x4_t out##_h = vmull_s16(vget_high_s16(inq), coeff) #define dct_long_mac(out, acc, inq, coeff) \ int32x4_t out##_l = vmlal_s16(acc##_l, vget_low_s16(inq), coeff); \ int32x4_t out##_h = vmlal_s16(acc##_h, vget_high_s16(inq), coeff) #define dct_widen(out, inq) \ int32x4_t out##_l = vshll_n_s16(vget_low_s16(inq), 12); \ int32x4_t out##_h = vshll_n_s16(vget_high_s16(inq), 12) // wide add #define dct_wadd(out, a, b) \ int32x4_t out##_l = vaddq_s32(a##_l, b##_l); \ int32x4_t out##_h = vaddq_s32(a##_h, b##_h) // wide sub #define dct_wsub(out, a, b) \ int32x4_t out##_l = vsubq_s32(a##_l, b##_l); \ int32x4_t out##_h = vsubq_s32(a##_h, b##_h) // butterfly a/b, then shift using "shiftop" by "s" and pack #define dct_bfly32o(out0,out1, a,b,shiftop,s) \ { \ dct_wadd(sum, a, b); \ dct_wsub(dif, a, b); \ out0 = vcombine_s16(shiftop(sum_l, s), shiftop(sum_h, s)); \ out1 = vcombine_s16(shiftop(dif_l, s), shiftop(dif_h, s)); \ } #define dct_pass(shiftop, shift) \ { \ /* even part */ \ int16x8_t sum26 = vaddq_s16(row2, row6); \ dct_long_mul(p1e, sum26, rot0_0); \ dct_long_mac(t2e, p1e, row6, rot0_1); \ dct_long_mac(t3e, p1e, row2, rot0_2); \ int16x8_t sum04 = vaddq_s16(row0, row4); \ int16x8_t dif04 = vsubq_s16(row0, row4); \ dct_widen(t0e, sum04); \ dct_widen(t1e, dif04); \ dct_wadd(x0, t0e, t3e); \ dct_wsub(x3, t0e, t3e); \ dct_wadd(x1, t1e, t2e); \ dct_wsub(x2, t1e, t2e); \ /* odd part */ \ int16x8_t sum15 = vaddq_s16(row1, row5); \ int16x8_t sum17 = vaddq_s16(row1, row7); \ int16x8_t sum35 = vaddq_s16(row3, row5); \ int16x8_t sum37 = vaddq_s16(row3, row7); \ int16x8_t sumodd = vaddq_s16(sum17, sum35); \ dct_long_mul(p5o, sumodd, rot1_0); \ dct_long_mac(p1o, p5o, sum17, rot1_1); \ dct_long_mac(p2o, p5o, sum35, rot1_2); \ dct_long_mul(p3o, sum37, rot2_0); \ dct_long_mul(p4o, sum15, rot2_1); \ dct_wadd(sump13o, p1o, p3o); \ dct_wadd(sump24o, p2o, p4o); \ dct_wadd(sump23o, p2o, p3o); \ dct_wadd(sump14o, p1o, p4o); \ dct_long_mac(x4, sump13o, row7, rot3_0); \ dct_long_mac(x5, sump24o, row5, rot3_1); \ dct_long_mac(x6, sump23o, row3, rot3_2); \ dct_long_mac(x7, sump14o, row1, rot3_3); \ dct_bfly32o(row0,row7, x0,x7,shiftop,shift); \ dct_bfly32o(row1,row6, x1,x6,shiftop,shift); \ dct_bfly32o(row2,row5, x2,x5,shiftop,shift); \ dct_bfly32o(row3,row4, x3,x4,shiftop,shift); \ } // load row0 = vld1q_s16(data + 0*8); row1 = vld1q_s16(data + 1*8); row2 = vld1q_s16(data + 2*8); row3 = vld1q_s16(data + 3*8); row4 = vld1q_s16(data + 4*8); row5 = vld1q_s16(data + 5*8); row6 = vld1q_s16(data + 6*8); row7 = vld1q_s16(data + 7*8); // add DC bias row0 = vaddq_s16(row0, vsetq_lane_s16(1024, vdupq_n_s16(0), 0)); // column pass dct_pass(vrshrn_n_s32, 10); // 16bit 8x8 transpose { // these three map to a single VTRN.16, VTRN.32, and VSWP, respectively. // whether compilers actually get this is another story, sadly. #define dct_trn16(x, y) { int16x8x2_t t = vtrnq_s16(x, y); x = t.val[0]; y = t.val[1]; } #define dct_trn32(x, y) { int32x4x2_t t = vtrnq_s32(vreinterpretq_s32_s16(x), vreinterpretq_s32_s16(y)); x = vreinterpretq_s16_s32(t.val[0]); y = vreinterpretq_s16_s32(t.val[1]); } #define dct_trn64(x, y) { int16x8_t x0 = x; int16x8_t y0 = y; x = vcombine_s16(vget_low_s16(x0), vget_low_s16(y0)); y = vcombine_s16(vget_high_s16(x0), vget_high_s16(y0)); } // pass 1 dct_trn16(row0, row1); // a0b0a2b2a4b4a6b6 dct_trn16(row2, row3); dct_trn16(row4, row5); dct_trn16(row6, row7); // pass 2 dct_trn32(row0, row2); // a0b0c0d0a4b4c4d4 dct_trn32(row1, row3); dct_trn32(row4, row6); dct_trn32(row5, row7); // pass 3 dct_trn64(row0, row4); // a0b0c0d0e0f0g0h0 dct_trn64(row1, row5); dct_trn64(row2, row6); dct_trn64(row3, row7); #undef dct_trn16 #undef dct_trn32 #undef dct_trn64 } // row pass // vrshrn_n_s32 only supports shifts up to 16, we need // 17. so do a non-rounding shift of 16 first then follow // up with a rounding shift by 1. dct_pass(vshrn_n_s32, 16); { // pack and round uint8x8_t p0 = vqrshrun_n_s16(row0, 1); uint8x8_t p1 = vqrshrun_n_s16(row1, 1); uint8x8_t p2 = vqrshrun_n_s16(row2, 1); uint8x8_t p3 = vqrshrun_n_s16(row3, 1); uint8x8_t p4 = vqrshrun_n_s16(row4, 1); uint8x8_t p5 = vqrshrun_n_s16(row5, 1); uint8x8_t p6 = vqrshrun_n_s16(row6, 1); uint8x8_t p7 = vqrshrun_n_s16(row7, 1); // again, these can translate into one instruction, but often don't. #define dct_trn8_8(x, y) { uint8x8x2_t t = vtrn_u8(x, y); x = t.val[0]; y = t.val[1]; } #define dct_trn8_16(x, y) { uint16x4x2_t t = vtrn_u16(vreinterpret_u16_u8(x), vreinterpret_u16_u8(y)); x = vreinterpret_u8_u16(t.val[0]); y = vreinterpret_u8_u16(t.val[1]); } #define dct_trn8_32(x, y) { uint32x2x2_t t = vtrn_u32(vreinterpret_u32_u8(x), vreinterpret_u32_u8(y)); x = vreinterpret_u8_u32(t.val[0]); y = vreinterpret_u8_u32(t.val[1]); } // sadly can't use interleaved stores here since we only write // 8 bytes to each scan line! // 8x8 8-bit transpose pass 1 dct_trn8_8(p0, p1); dct_trn8_8(p2, p3); dct_trn8_8(p4, p5); dct_trn8_8(p6, p7); // pass 2 dct_trn8_16(p0, p2); dct_trn8_16(p1, p3); dct_trn8_16(p4, p6); dct_trn8_16(p5, p7); // pass 3 dct_trn8_32(p0, p4); dct_trn8_32(p1, p5); dct_trn8_32(p2, p6); dct_trn8_32(p3, p7); // store vst1_u8(out, p0); out += out_stride; vst1_u8(out, p1); out += out_stride; vst1_u8(out, p2); out += out_stride; vst1_u8(out, p3); out += out_stride; vst1_u8(out, p4); out += out_stride; vst1_u8(out, p5); out += out_stride; vst1_u8(out, p6); out += out_stride; vst1_u8(out, p7); #undef dct_trn8_8 #undef dct_trn8_16 #undef dct_trn8_32 } #undef dct_long_mul #undef dct_long_mac #undef dct_widen #undef dct_wadd #undef dct_wsub #undef dct_bfly32o #undef dct_pass } #endif // STBI_NEON #define STBI__MARKER_none 0xff // if there's a pending marker from the entropy stream, return that // otherwise, fetch from the stream and get a marker. if there's no // marker, return 0xff, which is never a valid marker value static stbi_uc stbi__get_marker(stbi__jpeg *j) { stbi_uc x; if (j->marker != STBI__MARKER_none) { x = j->marker; j->marker = STBI__MARKER_none; return x; } x = stbi__get8(j->s); if (x != 0xff) return STBI__MARKER_none; while (x == 0xff) x = stbi__get8(j->s); // consume repeated 0xff fill bytes return x; } // in each scan, we'll have scan_n components, and the order // of the components is specified by order[] #define STBI__RESTART(x) ((x) >= 0xd0 && (x) <= 0xd7) // after a restart interval, stbi__jpeg_reset the entropy decoder and // the dc prediction static void stbi__jpeg_reset(stbi__jpeg *j) { j->code_bits = 0; j->code_buffer = 0; j->nomore = 0; j->img_comp[0].dc_pred = j->img_comp[1].dc_pred = j->img_comp[2].dc_pred = j->img_comp[3].dc_pred = 0; j->marker = STBI__MARKER_none; j->todo = j->restart_interval ? j->restart_interval : 0x7fffffff; j->eob_run = 0; // no more than 1<<31 MCUs if no restart_interal? that's plenty safe, // since we don't even allow 1<<30 pixels } static int stbi__parse_entropy_coded_data(stbi__jpeg *z) { stbi__jpeg_reset(z); if (!z->progressive) { if (z->scan_n == 1) { int i,j; STBI_SIMD_ALIGN(short, data[64]); int n = z->order[0]; // non-interleaved data, we just need to process one block at a time, // in trivial scanline order // number of blocks to do just depends on how many actual "pixels" this // component has, independent of interleaved MCU blocking and such int w = (z->img_comp[n].x+7) >> 3; int h = (z->img_comp[n].y+7) >> 3; for (j=0; j < h; ++j) { for (i=0; i < w; ++i) { int ha = z->img_comp[n].ha; if (!stbi__jpeg_decode_block(z, data, z->huff_dc+z->img_comp[n].hd, z->huff_ac+ha, z->fast_ac[ha], n, z->dequant[z->img_comp[n].tq])) return 0; z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*j*8+i*8, z->img_comp[n].w2, data); // every data block is an MCU, so countdown the restart interval if (--z->todo <= 0) { if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); // if it's NOT a restart, then just bail, so we get corrupt data // rather than no data if (!STBI__RESTART(z->marker)) return 1; stbi__jpeg_reset(z); } } } return 1; } else { // interleaved int i,j,k,x,y; STBI_SIMD_ALIGN(short, data[64]); for (j=0; j < z->img_mcu_y; ++j) { for (i=0; i < z->img_mcu_x; ++i) { // scan an interleaved mcu... process scan_n components in order for (k=0; k < z->scan_n; ++k) { int n = z->order[k]; // scan out an mcu's worth of this component; that's just determined // by the basic H and V specified for the component for (y=0; y < z->img_comp[n].v; ++y) { for (x=0; x < z->img_comp[n].h; ++x) { int x2 = (i*z->img_comp[n].h + x)*8; int y2 = (j*z->img_comp[n].v + y)*8; int ha = z->img_comp[n].ha; if (!stbi__jpeg_decode_block(z, data, z->huff_dc+z->img_comp[n].hd, z->huff_ac+ha, z->fast_ac[ha], n, z->dequant[z->img_comp[n].tq])) return 0; z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*y2+x2, z->img_comp[n].w2, data); } } } // after all interleaved components, that's an interleaved MCU, // so now count down the restart interval if (--z->todo <= 0) { if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); if (!STBI__RESTART(z->marker)) return 1; stbi__jpeg_reset(z); } } } return 1; } } else { if (z->scan_n == 1) { int i,j; int n = z->order[0]; // non-interleaved data, we just need to process one block at a time, // in trivial scanline order // number of blocks to do just depends on how many actual "pixels" this // component has, independent of interleaved MCU blocking and such int w = (z->img_comp[n].x+7) >> 3; int h = (z->img_comp[n].y+7) >> 3; for (j=0; j < h; ++j) { for (i=0; i < w; ++i) { short *data = z->img_comp[n].coeff + 64 * (i + j * z->img_comp[n].coeff_w); if (z->spec_start == 0) { if (!stbi__jpeg_decode_block_prog_dc(z, data, &z->huff_dc[z->img_comp[n].hd], n)) return 0; } else { int ha = z->img_comp[n].ha; if (!stbi__jpeg_decode_block_prog_ac(z, data, &z->huff_ac[ha], z->fast_ac[ha])) return 0; } // every data block is an MCU, so countdown the restart interval if (--z->todo <= 0) { if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); if (!STBI__RESTART(z->marker)) return 1; stbi__jpeg_reset(z); } } } return 1; } else { // interleaved int i,j,k,x,y; for (j=0; j < z->img_mcu_y; ++j) { for (i=0; i < z->img_mcu_x; ++i) { // scan an interleaved mcu... process scan_n components in order for (k=0; k < z->scan_n; ++k) { int n = z->order[k]; // scan out an mcu's worth of this component; that's just determined // by the basic H and V specified for the component for (y=0; y < z->img_comp[n].v; ++y) { for (x=0; x < z->img_comp[n].h; ++x) { int x2 = (i*z->img_comp[n].h + x); int y2 = (j*z->img_comp[n].v + y); short *data = z->img_comp[n].coeff + 64 * (x2 + y2 * z->img_comp[n].coeff_w); if (!stbi__jpeg_decode_block_prog_dc(z, data, &z->huff_dc[z->img_comp[n].hd], n)) return 0; } } } // after all interleaved components, that's an interleaved MCU, // so now count down the restart interval if (--z->todo <= 0) { if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); if (!STBI__RESTART(z->marker)) return 1; stbi__jpeg_reset(z); } } } return 1; } } } static void stbi__jpeg_dequantize(short *data, stbi__uint16 *dequant) { int i; for (i=0; i < 64; ++i) data[i] *= dequant[i]; } static void stbi__jpeg_finish(stbi__jpeg *z) { if (z->progressive) { // dequantize and idct the data int i,j,n; for (n=0; n < z->s->img_n; ++n) { int w = (z->img_comp[n].x+7) >> 3; int h = (z->img_comp[n].y+7) >> 3; for (j=0; j < h; ++j) { for (i=0; i < w; ++i) { short *data = z->img_comp[n].coeff + 64 * (i + j * z->img_comp[n].coeff_w); stbi__jpeg_dequantize(data, z->dequant[z->img_comp[n].tq]); z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*j*8+i*8, z->img_comp[n].w2, data); } } } } } static int stbi__process_marker(stbi__jpeg *z, int m) { int L; switch (m) { case STBI__MARKER_none: // no marker found return stbi__err("expected marker","Corrupt JPEG"); case 0xDD: // DRI - specify restart interval if (stbi__get16be(z->s) != 4) return stbi__err("bad DRI len","Corrupt JPEG"); z->restart_interval = stbi__get16be(z->s); return 1; case 0xDB: // DQT - define quantization table L = stbi__get16be(z->s)-2; while (L > 0) { int q = stbi__get8(z->s); int p = q >> 4, sixteen = (p != 0); int t = q & 15,i; if (p != 0 && p != 1) return stbi__err("bad DQT type","Corrupt JPEG"); if (t > 3) return stbi__err("bad DQT table","Corrupt JPEG"); for (i=0; i < 64; ++i) z->dequant[t][stbi__jpeg_dezigzag[i]] = (stbi__uint16)(sixteen ? stbi__get16be(z->s) : stbi__get8(z->s)); L -= (sixteen ? 129 : 65); } return L==0; case 0xC4: // DHT - define huffman table L = stbi__get16be(z->s)-2; while (L > 0) { stbi_uc *v; int sizes[16],i,n=0; int q = stbi__get8(z->s); int tc = q >> 4; int th = q & 15; if (tc > 1 || th > 3) return stbi__err("bad DHT header","Corrupt JPEG"); for (i=0; i < 16; ++i) { sizes[i] = stbi__get8(z->s); n += sizes[i]; } L -= 17; if (tc == 0) { if (!stbi__build_huffman(z->huff_dc+th, sizes)) return 0; v = z->huff_dc[th].values; } else { if (!stbi__build_huffman(z->huff_ac+th, sizes)) return 0; v = z->huff_ac[th].values; } for (i=0; i < n; ++i) v[i] = stbi__get8(z->s); if (tc != 0) stbi__build_fast_ac(z->fast_ac[th], z->huff_ac + th); L -= n; } return L==0; } // check for comment block or APP blocks if ((m >= 0xE0 && m <= 0xEF) || m == 0xFE) { L = stbi__get16be(z->s); if (L < 2) { if (m == 0xFE) return stbi__err("bad COM len","Corrupt JPEG"); else return stbi__err("bad APP len","Corrupt JPEG"); } L -= 2; if (m == 0xE0 && L >= 5) { // JFIF APP0 segment static const unsigned char tag[5] = {'J','F','I','F','\0'}; int ok = 1; int i; for (i=0; i < 5; ++i) if (stbi__get8(z->s) != tag[i]) ok = 0; L -= 5; if (ok) z->jfif = 1; } else if (m == 0xEE && L >= 12) { // Adobe APP14 segment static const unsigned char tag[6] = {'A','d','o','b','e','\0'}; int ok = 1; int i; for (i=0; i < 6; ++i) if (stbi__get8(z->s) != tag[i]) ok = 0; L -= 6; if (ok) { stbi__get8(z->s); // version stbi__get16be(z->s); // flags0 stbi__get16be(z->s); // flags1 z->app14_color_transform = stbi__get8(z->s); // color transform L -= 6; } } stbi__skip(z->s, L); return 1; } return stbi__err("unknown marker","Corrupt JPEG"); } // after we see SOS static int stbi__process_scan_header(stbi__jpeg *z) { int i; int Ls = stbi__get16be(z->s); z->scan_n = stbi__get8(z->s); if (z->scan_n < 1 || z->scan_n > 4 || z->scan_n > (int) z->s->img_n) return stbi__err("bad SOS component count","Corrupt JPEG"); if (Ls != 6+2*z->scan_n) return stbi__err("bad SOS len","Corrupt JPEG"); for (i=0; i < z->scan_n; ++i) { int id = stbi__get8(z->s), which; int q = stbi__get8(z->s); for (which = 0; which < z->s->img_n; ++which) if (z->img_comp[which].id == id) break; if (which == z->s->img_n) return 0; // no match z->img_comp[which].hd = q >> 4; if (z->img_comp[which].hd > 3) return stbi__err("bad DC huff","Corrupt JPEG"); z->img_comp[which].ha = q & 15; if (z->img_comp[which].ha > 3) return stbi__err("bad AC huff","Corrupt JPEG"); z->order[i] = which; } { int aa; z->spec_start = stbi__get8(z->s); z->spec_end = stbi__get8(z->s); // should be 63, but might be 0 aa = stbi__get8(z->s); z->succ_high = (aa >> 4); z->succ_low = (aa & 15); if (z->progressive) { if (z->spec_start > 63 || z->spec_end > 63 || z->spec_start > z->spec_end || z->succ_high > 13 || z->succ_low > 13) return stbi__err("bad SOS", "Corrupt JPEG"); } else { if (z->spec_start != 0) return stbi__err("bad SOS","Corrupt JPEG"); if (z->succ_high != 0 || z->succ_low != 0) return stbi__err("bad SOS","Corrupt JPEG"); z->spec_end = 63; } } return 1; } static int stbi__free_jpeg_components(stbi__jpeg *z, int ncomp, int why) { int i; for (i=0; i < ncomp; ++i) { if (z->img_comp[i].raw_data) { STBI_FREE(z->img_comp[i].raw_data); z->img_comp[i].raw_data = NULL; z->img_comp[i].data = NULL; } if (z->img_comp[i].raw_coeff) { STBI_FREE(z->img_comp[i].raw_coeff); z->img_comp[i].raw_coeff = 0; z->img_comp[i].coeff = 0; } if (z->img_comp[i].linebuf) { STBI_FREE(z->img_comp[i].linebuf); z->img_comp[i].linebuf = NULL; } } return why; } static int stbi__process_frame_header(stbi__jpeg *z, int scan) { stbi__context *s = z->s; int Lf,p,i,q, h_max=1,v_max=1,c; Lf = stbi__get16be(s); if (Lf < 11) return stbi__err("bad SOF len","Corrupt JPEG"); // JPEG p = stbi__get8(s); if (p != 8) return stbi__err("only 8-bit","JPEG format not supported: 8-bit only"); // JPEG baseline s->img_y = stbi__get16be(s); if (s->img_y == 0) return stbi__err("no header height", "JPEG format not supported: delayed height"); // Legal, but we don't handle it--but neither does IJG s->img_x = stbi__get16be(s); if (s->img_x == 0) return stbi__err("0 width","Corrupt JPEG"); // JPEG requires if (s->img_y > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); if (s->img_x > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); c = stbi__get8(s); if (c != 3 && c != 1 && c != 4) return stbi__err("bad component count","Corrupt JPEG"); s->img_n = c; for (i=0; i < c; ++i) { z->img_comp[i].data = NULL; z->img_comp[i].linebuf = NULL; } if (Lf != 8+3*s->img_n) return stbi__err("bad SOF len","Corrupt JPEG"); z->rgb = 0; for (i=0; i < s->img_n; ++i) { static const unsigned char rgb[3] = { 'R', 'G', 'B' }; z->img_comp[i].id = stbi__get8(s); if (s->img_n == 3 && z->img_comp[i].id == rgb[i]) ++z->rgb; q = stbi__get8(s); z->img_comp[i].h = (q >> 4); if (!z->img_comp[i].h || z->img_comp[i].h > 4) return stbi__err("bad H","Corrupt JPEG"); z->img_comp[i].v = q & 15; if (!z->img_comp[i].v || z->img_comp[i].v > 4) return stbi__err("bad V","Corrupt JPEG"); z->img_comp[i].tq = stbi__get8(s); if (z->img_comp[i].tq > 3) return stbi__err("bad TQ","Corrupt JPEG"); } if (scan != STBI__SCAN_load) return 1; if (!stbi__mad3sizes_valid(s->img_x, s->img_y, s->img_n, 0)) return stbi__err("too large", "Image too large to decode"); for (i=0; i < s->img_n; ++i) { if (z->img_comp[i].h > h_max) h_max = z->img_comp[i].h; if (z->img_comp[i].v > v_max) v_max = z->img_comp[i].v; } // compute interleaved mcu info z->img_h_max = h_max; z->img_v_max = v_max; z->img_mcu_w = h_max * 8; z->img_mcu_h = v_max * 8; // these sizes can't be more than 17 bits z->img_mcu_x = (s->img_x + z->img_mcu_w-1) / z->img_mcu_w; z->img_mcu_y = (s->img_y + z->img_mcu_h-1) / z->img_mcu_h; for (i=0; i < s->img_n; ++i) { // number of effective pixels (e.g. for non-interleaved MCU) z->img_comp[i].x = (s->img_x * z->img_comp[i].h + h_max-1) / h_max; z->img_comp[i].y = (s->img_y * z->img_comp[i].v + v_max-1) / v_max; // to simplify generation, we'll allocate enough memory to decode // the bogus oversized data from using interleaved MCUs and their // big blocks (e.g. a 16x16 iMCU on an image of width 33); we won't // discard the extra data until colorspace conversion // // img_mcu_x, img_mcu_y: <=17 bits; comp[i].h and .v are <=4 (checked earlier) // so these muls can't overflow with 32-bit ints (which we require) z->img_comp[i].w2 = z->img_mcu_x * z->img_comp[i].h * 8; z->img_comp[i].h2 = z->img_mcu_y * z->img_comp[i].v * 8; z->img_comp[i].coeff = 0; z->img_comp[i].raw_coeff = 0; z->img_comp[i].linebuf = NULL; z->img_comp[i].raw_data = stbi__malloc_mad2(z->img_comp[i].w2, z->img_comp[i].h2, 15); if (z->img_comp[i].raw_data == NULL) return stbi__free_jpeg_components(z, i+1, stbi__err("outofmem", "Out of memory")); // align blocks for idct using mmx/sse z->img_comp[i].data = (stbi_uc*) (((size_t) z->img_comp[i].raw_data + 15) & ~15); if (z->progressive) { // w2, h2 are multiples of 8 (see above) z->img_comp[i].coeff_w = z->img_comp[i].w2 / 8; z->img_comp[i].coeff_h = z->img_comp[i].h2 / 8; z->img_comp[i].raw_coeff = stbi__malloc_mad3(z->img_comp[i].w2, z->img_comp[i].h2, sizeof(short), 15); if (z->img_comp[i].raw_coeff == NULL) return stbi__free_jpeg_components(z, i+1, stbi__err("outofmem", "Out of memory")); z->img_comp[i].coeff = (short*) (((size_t) z->img_comp[i].raw_coeff + 15) & ~15); } } return 1; } // use comparisons since in some cases we handle more than one case (e.g. SOF) #define stbi__DNL(x) ((x) == 0xdc) #define stbi__SOI(x) ((x) == 0xd8) #define stbi__EOI(x) ((x) == 0xd9) #define stbi__SOF(x) ((x) == 0xc0 || (x) == 0xc1 || (x) == 0xc2) #define stbi__SOS(x) ((x) == 0xda) #define stbi__SOF_progressive(x) ((x) == 0xc2) static int stbi__decode_jpeg_header(stbi__jpeg *z, int scan) { int m; z->jfif = 0; z->app14_color_transform = -1; // valid values are 0,1,2 z->marker = STBI__MARKER_none; // initialize cached marker to empty m = stbi__get_marker(z); if (!stbi__SOI(m)) return stbi__err("no SOI","Corrupt JPEG"); if (scan == STBI__SCAN_type) return 1; m = stbi__get_marker(z); while (!stbi__SOF(m)) { if (!stbi__process_marker(z,m)) return 0; m = stbi__get_marker(z); while (m == STBI__MARKER_none) { // some files have extra padding after their blocks, so ok, we'll scan if (stbi__at_eof(z->s)) return stbi__err("no SOF", "Corrupt JPEG"); m = stbi__get_marker(z); } } z->progressive = stbi__SOF_progressive(m); if (!stbi__process_frame_header(z, scan)) return 0; return 1; } // decode image to YCbCr format static int stbi__decode_jpeg_image(stbi__jpeg *j) { int m; for (m = 0; m < 4; m++) { j->img_comp[m].raw_data = NULL; j->img_comp[m].raw_coeff = NULL; } j->restart_interval = 0; if (!stbi__decode_jpeg_header(j, STBI__SCAN_load)) return 0; m = stbi__get_marker(j); while (!stbi__EOI(m)) { if (stbi__SOS(m)) { if (!stbi__process_scan_header(j)) return 0; if (!stbi__parse_entropy_coded_data(j)) return 0; if (j->marker == STBI__MARKER_none ) { // handle 0s at the end of image data from IP Kamera 9060 while (!stbi__at_eof(j->s)) { int x = stbi__get8(j->s); if (x == 255) { j->marker = stbi__get8(j->s); break; } } // if we reach eof without hitting a marker, stbi__get_marker() below will fail and we'll eventually return 0 } } else if (stbi__DNL(m)) { int Ld = stbi__get16be(j->s); stbi__uint32 NL = stbi__get16be(j->s); if (Ld != 4) return stbi__err("bad DNL len", "Corrupt JPEG"); if (NL != j->s->img_y) return stbi__err("bad DNL height", "Corrupt JPEG"); } else { if (!stbi__process_marker(j, m)) return 0; } m = stbi__get_marker(j); } if (j->progressive) stbi__jpeg_finish(j); return 1; } // static jfif-centered resampling (across block boundaries) typedef stbi_uc *(*resample_row_func)(stbi_uc *out, stbi_uc *in0, stbi_uc *in1, int w, int hs); #define stbi__div4(x) ((stbi_uc) ((x) >> 2)) static stbi_uc *resample_row_1(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) { STBI_NOTUSED(out); STBI_NOTUSED(in_far); STBI_NOTUSED(w); STBI_NOTUSED(hs); return in_near; } static stbi_uc* stbi__resample_row_v_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) { // need to generate two samples vertically for every one in input int i; STBI_NOTUSED(hs); for (i=0; i < w; ++i) out[i] = stbi__div4(3*in_near[i] + in_far[i] + 2); return out; } static stbi_uc* stbi__resample_row_h_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) { // need to generate two samples horizontally for every one in input int i; stbi_uc *input = in_near; if (w == 1) { // if only one sample, can't do any interpolation out[0] = out[1] = input[0]; return out; } out[0] = input[0]; out[1] = stbi__div4(input[0]*3 + input[1] + 2); for (i=1; i < w-1; ++i) { int n = 3*input[i]+2; out[i*2+0] = stbi__div4(n+input[i-1]); out[i*2+1] = stbi__div4(n+input[i+1]); } out[i*2+0] = stbi__div4(input[w-2]*3 + input[w-1] + 2); out[i*2+1] = input[w-1]; STBI_NOTUSED(in_far); STBI_NOTUSED(hs); return out; } #define stbi__div16(x) ((stbi_uc) ((x) >> 4)) static stbi_uc *stbi__resample_row_hv_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) { // need to generate 2x2 samples for every one in input int i,t0,t1; if (w == 1) { out[0] = out[1] = stbi__div4(3*in_near[0] + in_far[0] + 2); return out; } t1 = 3*in_near[0] + in_far[0]; out[0] = stbi__div4(t1+2); for (i=1; i < w; ++i) { t0 = t1; t1 = 3*in_near[i]+in_far[i]; out[i*2-1] = stbi__div16(3*t0 + t1 + 8); out[i*2 ] = stbi__div16(3*t1 + t0 + 8); } out[w*2-1] = stbi__div4(t1+2); STBI_NOTUSED(hs); return out; } #if defined(STBI_SSE2) || defined(STBI_NEON) static stbi_uc *stbi__resample_row_hv_2_simd(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) { // need to generate 2x2 samples for every one in input int i=0,t0,t1; if (w == 1) { out[0] = out[1] = stbi__div4(3*in_near[0] + in_far[0] + 2); return out; } t1 = 3*in_near[0] + in_far[0]; // process groups of 8 pixels for as long as we can. // note we can't handle the last pixel in a row in this loop // because we need to handle the filter boundary conditions. for (; i < ((w-1) & ~7); i += 8) { #if defined(STBI_SSE2) // load and perform the vertical filtering pass // this uses 3*x + y = 4*x + (y - x) __m128i zero = _mm_setzero_si128(); __m128i farb = _mm_loadl_epi64((__m128i *) (in_far + i)); __m128i nearb = _mm_loadl_epi64((__m128i *) (in_near + i)); __m128i farw = _mm_unpacklo_epi8(farb, zero); __m128i nearw = _mm_unpacklo_epi8(nearb, zero); __m128i diff = _mm_sub_epi16(farw, nearw); __m128i nears = _mm_slli_epi16(nearw, 2); __m128i curr = _mm_add_epi16(nears, diff); // current row // horizontal filter works the same based on shifted vers of current // row. "prev" is current row shifted right by 1 pixel; we need to // insert the previous pixel value (from t1). // "next" is current row shifted left by 1 pixel, with first pixel // of next block of 8 pixels added in. __m128i prv0 = _mm_slli_si128(curr, 2); __m128i nxt0 = _mm_srli_si128(curr, 2); __m128i prev = _mm_insert_epi16(prv0, t1, 0); __m128i next = _mm_insert_epi16(nxt0, 3*in_near[i+8] + in_far[i+8], 7); // horizontal filter, polyphase implementation since it's convenient: // even pixels = 3*cur + prev = cur*4 + (prev - cur) // odd pixels = 3*cur + next = cur*4 + (next - cur) // note the shared term. __m128i bias = _mm_set1_epi16(8); __m128i curs = _mm_slli_epi16(curr, 2); __m128i prvd = _mm_sub_epi16(prev, curr); __m128i nxtd = _mm_sub_epi16(next, curr); __m128i curb = _mm_add_epi16(curs, bias); __m128i even = _mm_add_epi16(prvd, curb); __m128i odd = _mm_add_epi16(nxtd, curb); // interleave even and odd pixels, then undo scaling. __m128i int0 = _mm_unpacklo_epi16(even, odd); __m128i int1 = _mm_unpackhi_epi16(even, odd); __m128i de0 = _mm_srli_epi16(int0, 4); __m128i de1 = _mm_srli_epi16(int1, 4); // pack and write output __m128i outv = _mm_packus_epi16(de0, de1); _mm_storeu_si128((__m128i *) (out + i*2), outv); #elif defined(STBI_NEON) // load and perform the vertical filtering pass // this uses 3*x + y = 4*x + (y - x) uint8x8_t farb = vld1_u8(in_far + i); uint8x8_t nearb = vld1_u8(in_near + i); int16x8_t diff = vreinterpretq_s16_u16(vsubl_u8(farb, nearb)); int16x8_t nears = vreinterpretq_s16_u16(vshll_n_u8(nearb, 2)); int16x8_t curr = vaddq_s16(nears, diff); // current row // horizontal filter works the same based on shifted vers of current // row. "prev" is current row shifted right by 1 pixel; we need to // insert the previous pixel value (from t1). // "next" is current row shifted left by 1 pixel, with first pixel // of next block of 8 pixels added in. int16x8_t prv0 = vextq_s16(curr, curr, 7); int16x8_t nxt0 = vextq_s16(curr, curr, 1); int16x8_t prev = vsetq_lane_s16(t1, prv0, 0); int16x8_t next = vsetq_lane_s16(3*in_near[i+8] + in_far[i+8], nxt0, 7); // horizontal filter, polyphase implementation since it's convenient: // even pixels = 3*cur + prev = cur*4 + (prev - cur) // odd pixels = 3*cur + next = cur*4 + (next - cur) // note the shared term. int16x8_t curs = vshlq_n_s16(curr, 2); int16x8_t prvd = vsubq_s16(prev, curr); int16x8_t nxtd = vsubq_s16(next, curr); int16x8_t even = vaddq_s16(curs, prvd); int16x8_t odd = vaddq_s16(curs, nxtd); // undo scaling and round, then store with even/odd phases interleaved uint8x8x2_t o; o.val[0] = vqrshrun_n_s16(even, 4); o.val[1] = vqrshrun_n_s16(odd, 4); vst2_u8(out + i*2, o); #endif // "previous" value for next iter t1 = 3*in_near[i+7] + in_far[i+7]; } t0 = t1; t1 = 3*in_near[i] + in_far[i]; out[i*2] = stbi__div16(3*t1 + t0 + 8); for (++i; i < w; ++i) { t0 = t1; t1 = 3*in_near[i]+in_far[i]; out[i*2-1] = stbi__div16(3*t0 + t1 + 8); out[i*2 ] = stbi__div16(3*t1 + t0 + 8); } out[w*2-1] = stbi__div4(t1+2); STBI_NOTUSED(hs); return out; } #endif static stbi_uc *stbi__resample_row_generic(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) { // resample with nearest-neighbor int i,j; STBI_NOTUSED(in_far); for (i=0; i < w; ++i) for (j=0; j < hs; ++j) out[i*hs+j] = in_near[i]; return out; } // this is a reduced-precision calculation of YCbCr-to-RGB introduced // to make sure the code produces the same results in both SIMD and scalar #define stbi__float2fixed(x) (((int) ((x) * 4096.0f + 0.5f)) << 8) static void stbi__YCbCr_to_RGB_row(stbi_uc *out, const stbi_uc *y, const stbi_uc *pcb, const stbi_uc *pcr, int count, int step) { int i; for (i=0; i < count; ++i) { int y_fixed = (y[i] << 20) + (1<<19); // rounding int r,g,b; int cr = pcr[i] - 128; int cb = pcb[i] - 128; r = y_fixed + cr* stbi__float2fixed(1.40200f); g = y_fixed + (cr*-stbi__float2fixed(0.71414f)) + ((cb*-stbi__float2fixed(0.34414f)) & 0xffff0000); b = y_fixed + cb* stbi__float2fixed(1.77200f); r >>= 20; g >>= 20; b >>= 20; if ((unsigned) r > 255) { if (r < 0) r = 0; else r = 255; } if ((unsigned) g > 255) { if (g < 0) g = 0; else g = 255; } if ((unsigned) b > 255) { if (b < 0) b = 0; else b = 255; } out[0] = (stbi_uc)r; out[1] = (stbi_uc)g; out[2] = (stbi_uc)b; out[3] = 255; out += step; } } #if defined(STBI_SSE2) || defined(STBI_NEON) static void stbi__YCbCr_to_RGB_simd(stbi_uc *out, stbi_uc const *y, stbi_uc const *pcb, stbi_uc const *pcr, int count, int step) { int i = 0; #ifdef STBI_SSE2 // step == 3 is pretty ugly on the final interleave, and i'm not convinced // it's useful in practice (you wouldn't use it for textures, for example). // so just accelerate step == 4 case. if (step == 4) { // this is a fairly straightforward implementation and not super-optimized. __m128i signflip = _mm_set1_epi8(-0x80); __m128i cr_const0 = _mm_set1_epi16( (short) ( 1.40200f*4096.0f+0.5f)); __m128i cr_const1 = _mm_set1_epi16( - (short) ( 0.71414f*4096.0f+0.5f)); __m128i cb_const0 = _mm_set1_epi16( - (short) ( 0.34414f*4096.0f+0.5f)); __m128i cb_const1 = _mm_set1_epi16( (short) ( 1.77200f*4096.0f+0.5f)); __m128i y_bias = _mm_set1_epi8((char) (unsigned char) 128); __m128i xw = _mm_set1_epi16(255); // alpha channel for (; i+7 < count; i += 8) { // load __m128i y_bytes = _mm_loadl_epi64((__m128i *) (y+i)); __m128i cr_bytes = _mm_loadl_epi64((__m128i *) (pcr+i)); __m128i cb_bytes = _mm_loadl_epi64((__m128i *) (pcb+i)); __m128i cr_biased = _mm_xor_si128(cr_bytes, signflip); // -128 __m128i cb_biased = _mm_xor_si128(cb_bytes, signflip); // -128 // unpack to short (and left-shift cr, cb by 8) __m128i yw = _mm_unpacklo_epi8(y_bias, y_bytes); __m128i crw = _mm_unpacklo_epi8(_mm_setzero_si128(), cr_biased); __m128i cbw = _mm_unpacklo_epi8(_mm_setzero_si128(), cb_biased); // color transform __m128i yws = _mm_srli_epi16(yw, 4); __m128i cr0 = _mm_mulhi_epi16(cr_const0, crw); __m128i cb0 = _mm_mulhi_epi16(cb_const0, cbw); __m128i cb1 = _mm_mulhi_epi16(cbw, cb_const1); __m128i cr1 = _mm_mulhi_epi16(crw, cr_const1); __m128i rws = _mm_add_epi16(cr0, yws); __m128i gwt = _mm_add_epi16(cb0, yws); __m128i bws = _mm_add_epi16(yws, cb1); __m128i gws = _mm_add_epi16(gwt, cr1); // descale __m128i rw = _mm_srai_epi16(rws, 4); __m128i bw = _mm_srai_epi16(bws, 4); __m128i gw = _mm_srai_epi16(gws, 4); // back to byte, set up for transpose __m128i brb = _mm_packus_epi16(rw, bw); __m128i gxb = _mm_packus_epi16(gw, xw); // transpose to interleave channels __m128i t0 = _mm_unpacklo_epi8(brb, gxb); __m128i t1 = _mm_unpackhi_epi8(brb, gxb); __m128i o0 = _mm_unpacklo_epi16(t0, t1); __m128i o1 = _mm_unpackhi_epi16(t0, t1); // store _mm_storeu_si128((__m128i *) (out + 0), o0); _mm_storeu_si128((__m128i *) (out + 16), o1); out += 32; } } #endif #ifdef STBI_NEON // in this version, step=3 support would be easy to add. but is there demand? if (step == 4) { // this is a fairly straightforward implementation and not super-optimized. uint8x8_t signflip = vdup_n_u8(0x80); int16x8_t cr_const0 = vdupq_n_s16( (short) ( 1.40200f*4096.0f+0.5f)); int16x8_t cr_const1 = vdupq_n_s16( - (short) ( 0.71414f*4096.0f+0.5f)); int16x8_t cb_const0 = vdupq_n_s16( - (short) ( 0.34414f*4096.0f+0.5f)); int16x8_t cb_const1 = vdupq_n_s16( (short) ( 1.77200f*4096.0f+0.5f)); for (; i+7 < count; i += 8) { // load uint8x8_t y_bytes = vld1_u8(y + i); uint8x8_t cr_bytes = vld1_u8(pcr + i); uint8x8_t cb_bytes = vld1_u8(pcb + i); int8x8_t cr_biased = vreinterpret_s8_u8(vsub_u8(cr_bytes, signflip)); int8x8_t cb_biased = vreinterpret_s8_u8(vsub_u8(cb_bytes, signflip)); // expand to s16 int16x8_t yws = vreinterpretq_s16_u16(vshll_n_u8(y_bytes, 4)); int16x8_t crw = vshll_n_s8(cr_biased, 7); int16x8_t cbw = vshll_n_s8(cb_biased, 7); // color transform int16x8_t cr0 = vqdmulhq_s16(crw, cr_const0); int16x8_t cb0 = vqdmulhq_s16(cbw, cb_const0); int16x8_t cr1 = vqdmulhq_s16(crw, cr_const1); int16x8_t cb1 = vqdmulhq_s16(cbw, cb_const1); int16x8_t rws = vaddq_s16(yws, cr0); int16x8_t gws = vaddq_s16(vaddq_s16(yws, cb0), cr1); int16x8_t bws = vaddq_s16(yws, cb1); // undo scaling, round, convert to byte uint8x8x4_t o; o.val[0] = vqrshrun_n_s16(rws, 4); o.val[1] = vqrshrun_n_s16(gws, 4); o.val[2] = vqrshrun_n_s16(bws, 4); o.val[3] = vdup_n_u8(255); // store, interleaving r/g/b/a vst4_u8(out, o); out += 8*4; } } #endif for (; i < count; ++i) { int y_fixed = (y[i] << 20) + (1<<19); // rounding int r,g,b; int cr = pcr[i] - 128; int cb = pcb[i] - 128; r = y_fixed + cr* stbi__float2fixed(1.40200f); g = y_fixed + cr*-stbi__float2fixed(0.71414f) + ((cb*-stbi__float2fixed(0.34414f)) & 0xffff0000); b = y_fixed + cb* stbi__float2fixed(1.77200f); r >>= 20; g >>= 20; b >>= 20; if ((unsigned) r > 255) { if (r < 0) r = 0; else r = 255; } if ((unsigned) g > 255) { if (g < 0) g = 0; else g = 255; } if ((unsigned) b > 255) { if (b < 0) b = 0; else b = 255; } out[0] = (stbi_uc)r; out[1] = (stbi_uc)g; out[2] = (stbi_uc)b; out[3] = 255; out += step; } } #endif // set up the kernels static void stbi__setup_jpeg(stbi__jpeg *j) { j->idct_block_kernel = stbi__idct_block; j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_row; j->resample_row_hv_2_kernel = stbi__resample_row_hv_2; #ifdef STBI_SSE2 if (stbi__sse2_available()) { j->idct_block_kernel = stbi__idct_simd; j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_simd; j->resample_row_hv_2_kernel = stbi__resample_row_hv_2_simd; } #endif #ifdef STBI_NEON j->idct_block_kernel = stbi__idct_simd; j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_simd; j->resample_row_hv_2_kernel = stbi__resample_row_hv_2_simd; #endif } // clean up the temporary component buffers static void stbi__cleanup_jpeg(stbi__jpeg *j) { stbi__free_jpeg_components(j, j->s->img_n, 0); } typedef struct { resample_row_func resample; stbi_uc *line0,*line1; int hs,vs; // expansion factor in each axis int w_lores; // horizontal pixels pre-expansion int ystep; // how far through vertical expansion we are int ypos; // which pre-expansion row we're on } stbi__resample; // fast 0..255 * 0..255 => 0..255 rounded multiplication static stbi_uc stbi__blinn_8x8(stbi_uc x, stbi_uc y) { unsigned int t = x*y + 128; return (stbi_uc) ((t + (t >>8)) >> 8); } static stbi_uc *load_jpeg_image(stbi__jpeg *z, int *out_x, int *out_y, int *comp, int req_comp) { int n, decode_n, is_rgb; z->s->img_n = 0; // make stbi__cleanup_jpeg safe // validate req_comp if (req_comp < 0 || req_comp > 4) return stbi__errpuc("bad req_comp", "Internal error"); // load a jpeg image from whichever source, but leave in YCbCr format if (!stbi__decode_jpeg_image(z)) { stbi__cleanup_jpeg(z); return NULL; } // determine actual number of components to generate n = req_comp ? req_comp : z->s->img_n >= 3 ? 3 : 1; is_rgb = z->s->img_n == 3 && (z->rgb == 3 || (z->app14_color_transform == 0 && !z->jfif)); if (z->s->img_n == 3 && n < 3 && !is_rgb) decode_n = 1; else decode_n = z->s->img_n; // resample and color-convert { int k; unsigned int i,j; stbi_uc *output; stbi_uc *coutput[4] = { NULL, NULL, NULL, NULL }; stbi__resample res_comp[4]; for (k=0; k < decode_n; ++k) { stbi__resample *r = &res_comp[k]; // allocate line buffer big enough for upsampling off the edges // with upsample factor of 4 z->img_comp[k].linebuf = (stbi_uc *) stbi__malloc(z->s->img_x + 3); if (!z->img_comp[k].linebuf) { stbi__cleanup_jpeg(z); return stbi__errpuc("outofmem", "Out of memory"); } r->hs = z->img_h_max / z->img_comp[k].h; r->vs = z->img_v_max / z->img_comp[k].v; r->ystep = r->vs >> 1; r->w_lores = (z->s->img_x + r->hs-1) / r->hs; r->ypos = 0; r->line0 = r->line1 = z->img_comp[k].data; if (r->hs == 1 && r->vs == 1) r->resample = resample_row_1; else if (r->hs == 1 && r->vs == 2) r->resample = stbi__resample_row_v_2; else if (r->hs == 2 && r->vs == 1) r->resample = stbi__resample_row_h_2; else if (r->hs == 2 && r->vs == 2) r->resample = z->resample_row_hv_2_kernel; else r->resample = stbi__resample_row_generic; } // can't error after this so, this is safe output = (stbi_uc *) stbi__malloc_mad3(n, z->s->img_x, z->s->img_y, 1); if (!output) { stbi__cleanup_jpeg(z); return stbi__errpuc("outofmem", "Out of memory"); } // now go ahead and resample for (j=0; j < z->s->img_y; ++j) { stbi_uc *out = output + n * z->s->img_x * j; for (k=0; k < decode_n; ++k) { stbi__resample *r = &res_comp[k]; int y_bot = r->ystep >= (r->vs >> 1); coutput[k] = r->resample(z->img_comp[k].linebuf, y_bot ? r->line1 : r->line0, y_bot ? r->line0 : r->line1, r->w_lores, r->hs); if (++r->ystep >= r->vs) { r->ystep = 0; r->line0 = r->line1; if (++r->ypos < z->img_comp[k].y) r->line1 += z->img_comp[k].w2; } } if (n >= 3) { stbi_uc *y = coutput[0]; if (z->s->img_n == 3) { if (is_rgb) { for (i=0; i < z->s->img_x; ++i) { out[0] = y[i]; out[1] = coutput[1][i]; out[2] = coutput[2][i]; out[3] = 255; out += n; } } else { z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n); } } else if (z->s->img_n == 4) { if (z->app14_color_transform == 0) { // CMYK for (i=0; i < z->s->img_x; ++i) { stbi_uc m = coutput[3][i]; out[0] = stbi__blinn_8x8(coutput[0][i], m); out[1] = stbi__blinn_8x8(coutput[1][i], m); out[2] = stbi__blinn_8x8(coutput[2][i], m); out[3] = 255; out += n; } } else if (z->app14_color_transform == 2) { // YCCK z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n); for (i=0; i < z->s->img_x; ++i) { stbi_uc m = coutput[3][i]; out[0] = stbi__blinn_8x8(255 - out[0], m); out[1] = stbi__blinn_8x8(255 - out[1], m); out[2] = stbi__blinn_8x8(255 - out[2], m); out += n; } } else { // YCbCr + alpha? Ignore the fourth channel for now z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n); } } else for (i=0; i < z->s->img_x; ++i) { out[0] = out[1] = out[2] = y[i]; out[3] = 255; // not used if n==3 out += n; } } else { if (is_rgb) { if (n == 1) for (i=0; i < z->s->img_x; ++i) *out++ = stbi__compute_y(coutput[0][i], coutput[1][i], coutput[2][i]); else { for (i=0; i < z->s->img_x; ++i, out += 2) { out[0] = stbi__compute_y(coutput[0][i], coutput[1][i], coutput[2][i]); out[1] = 255; } } } else if (z->s->img_n == 4 && z->app14_color_transform == 0) { for (i=0; i < z->s->img_x; ++i) { stbi_uc m = coutput[3][i]; stbi_uc r = stbi__blinn_8x8(coutput[0][i], m); stbi_uc g = stbi__blinn_8x8(coutput[1][i], m); stbi_uc b = stbi__blinn_8x8(coutput[2][i], m); out[0] = stbi__compute_y(r, g, b); out[1] = 255; out += n; } } else if (z->s->img_n == 4 && z->app14_color_transform == 2) { for (i=0; i < z->s->img_x; ++i) { out[0] = stbi__blinn_8x8(255 - coutput[0][i], coutput[3][i]); out[1] = 255; out += n; } } else { stbi_uc *y = coutput[0]; if (n == 1) for (i=0; i < z->s->img_x; ++i) out[i] = y[i]; else for (i=0; i < z->s->img_x; ++i) { *out++ = y[i]; *out++ = 255; } } } } stbi__cleanup_jpeg(z); *out_x = z->s->img_x; *out_y = z->s->img_y; if (comp) *comp = z->s->img_n >= 3 ? 3 : 1; // report original components, not output return output; } } static void *stbi__jpeg_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) { unsigned char* result; stbi__jpeg* j = (stbi__jpeg*) stbi__malloc(sizeof(stbi__jpeg)); STBI_NOTUSED(ri); j->s = s; stbi__setup_jpeg(j); result = load_jpeg_image(j, x,y,comp,req_comp); STBI_FREE(j); return result; } static int stbi__jpeg_test(stbi__context *s) { int r; stbi__jpeg* j = (stbi__jpeg*)stbi__malloc(sizeof(stbi__jpeg)); j->s = s; stbi__setup_jpeg(j); r = stbi__decode_jpeg_header(j, STBI__SCAN_type); stbi__rewind(s); STBI_FREE(j); return r; } static int stbi__jpeg_info_raw(stbi__jpeg *j, int *x, int *y, int *comp) { if (!stbi__decode_jpeg_header(j, STBI__SCAN_header)) { stbi__rewind( j->s ); return 0; } if (x) *x = j->s->img_x; if (y) *y = j->s->img_y; if (comp) *comp = j->s->img_n >= 3 ? 3 : 1; return 1; } static int stbi__jpeg_info(stbi__context *s, int *x, int *y, int *comp) { int result; stbi__jpeg* j = (stbi__jpeg*) (stbi__malloc(sizeof(stbi__jpeg))); j->s = s; result = stbi__jpeg_info_raw(j, x, y, comp); STBI_FREE(j); return result; } #endif // public domain zlib decode v0.2 Sean Barrett 2006-11-18 // simple implementation // - all input must be provided in an upfront buffer // - all output is written to a single output buffer (can malloc/realloc) // performance // - fast huffman #ifndef STBI_NO_ZLIB // fast-way is faster to check than jpeg huffman, but slow way is slower #define STBI__ZFAST_BITS 9 // accelerate all cases in default tables #define STBI__ZFAST_MASK ((1 << STBI__ZFAST_BITS) - 1) // zlib-style huffman encoding // (jpegs packs from left, zlib from right, so can't share code) typedef struct { stbi__uint16 fast[1 << STBI__ZFAST_BITS]; stbi__uint16 firstcode[16]; int maxcode[17]; stbi__uint16 firstsymbol[16]; stbi_uc size[288]; stbi__uint16 value[288]; } stbi__zhuffman; stbi_inline static int stbi__bitreverse16(int n) { n = ((n & 0xAAAA) >> 1) | ((n & 0x5555) << 1); n = ((n & 0xCCCC) >> 2) | ((n & 0x3333) << 2); n = ((n & 0xF0F0) >> 4) | ((n & 0x0F0F) << 4); n = ((n & 0xFF00) >> 8) | ((n & 0x00FF) << 8); return n; } stbi_inline static int stbi__bit_reverse(int v, int bits) { STBI_ASSERT(bits <= 16); // to bit reverse n bits, reverse 16 and shift // e.g. 11 bits, bit reverse and shift away 5 return stbi__bitreverse16(v) >> (16-bits); } static int stbi__zbuild_huffman(stbi__zhuffman *z, const stbi_uc *sizelist, int num) { int i,k=0; int code, next_code[16], sizes[17]; // DEFLATE spec for generating codes memset(sizes, 0, sizeof(sizes)); memset(z->fast, 0, sizeof(z->fast)); for (i=0; i < num; ++i) ++sizes[sizelist[i]]; sizes[0] = 0; for (i=1; i < 16; ++i) if (sizes[i] > (1 << i)) return stbi__err("bad sizes", "Corrupt PNG"); code = 0; for (i=1; i < 16; ++i) { next_code[i] = code; z->firstcode[i] = (stbi__uint16) code; z->firstsymbol[i] = (stbi__uint16) k; code = (code + sizes[i]); if (sizes[i]) if (code-1 >= (1 << i)) return stbi__err("bad codelengths","Corrupt PNG"); z->maxcode[i] = code << (16-i); // preshift for inner loop code <<= 1; k += sizes[i]; } z->maxcode[16] = 0x10000; // sentinel for (i=0; i < num; ++i) { int s = sizelist[i]; if (s) { int c = next_code[s] - z->firstcode[s] + z->firstsymbol[s]; stbi__uint16 fastv = (stbi__uint16) ((s << 9) | i); z->size [c] = (stbi_uc ) s; z->value[c] = (stbi__uint16) i; if (s <= STBI__ZFAST_BITS) { int j = stbi__bit_reverse(next_code[s],s); while (j < (1 << STBI__ZFAST_BITS)) { z->fast[j] = fastv; j += (1 << s); } } ++next_code[s]; } } return 1; } // zlib-from-memory implementation for PNG reading // because PNG allows splitting the zlib stream arbitrarily, // and it's annoying structurally to have PNG call ZLIB call PNG, // we require PNG read all the IDATs and combine them into a single // memory buffer typedef struct { stbi_uc *zbuffer, *zbuffer_end; int num_bits; stbi__uint32 code_buffer; char *zout; char *zout_start; char *zout_end; int z_expandable; stbi__zhuffman z_length, z_distance; } stbi__zbuf; stbi_inline static int stbi__zeof(stbi__zbuf *z) { return (z->zbuffer >= z->zbuffer_end); } stbi_inline static stbi_uc stbi__zget8(stbi__zbuf *z) { return stbi__zeof(z) ? 0 : *z->zbuffer++; } static void stbi__fill_bits(stbi__zbuf *z) { do { if (z->code_buffer >= (1U << z->num_bits)) { z->zbuffer = z->zbuffer_end; /* treat this as EOF so we fail. */ return; } z->code_buffer |= (unsigned int) stbi__zget8(z) << z->num_bits; z->num_bits += 8; } while (z->num_bits <= 24); } stbi_inline static unsigned int stbi__zreceive(stbi__zbuf *z, int n) { unsigned int k; if (z->num_bits < n) stbi__fill_bits(z); k = z->code_buffer & ((1 << n) - 1); z->code_buffer >>= n; z->num_bits -= n; return k; } static int stbi__zhuffman_decode_slowpath(stbi__zbuf *a, stbi__zhuffman *z) { int b,s,k; // not resolved by fast table, so compute it the slow way // use jpeg approach, which requires MSbits at top k = stbi__bit_reverse(a->code_buffer, 16); for (s=STBI__ZFAST_BITS+1; ; ++s) if (k < z->maxcode[s]) break; if (s >= 16) return -1; // invalid code! // code size is s, so: b = (k >> (16-s)) - z->firstcode[s] + z->firstsymbol[s]; if (b >= sizeof (z->size)) return -1; // some data was corrupt somewhere! if (z->size[b] != s) return -1; // was originally an assert, but report failure instead. a->code_buffer >>= s; a->num_bits -= s; return z->value[b]; } stbi_inline static int stbi__zhuffman_decode(stbi__zbuf *a, stbi__zhuffman *z) { int b,s; if (a->num_bits < 16) { if (stbi__zeof(a)) { return -1; /* report error for unexpected end of data. */ } stbi__fill_bits(a); } b = z->fast[a->code_buffer & STBI__ZFAST_MASK]; if (b) { s = b >> 9; a->code_buffer >>= s; a->num_bits -= s; return b & 511; } return stbi__zhuffman_decode_slowpath(a, z); } static int stbi__zexpand(stbi__zbuf *z, char *zout, int n) // need to make room for n bytes { char *q; unsigned int cur, limit, old_limit; z->zout = zout; if (!z->z_expandable) return stbi__err("output buffer limit","Corrupt PNG"); cur = (unsigned int) (z->zout - z->zout_start); limit = old_limit = (unsigned) (z->zout_end - z->zout_start); if (UINT_MAX - cur < (unsigned) n) return stbi__err("outofmem", "Out of memory"); while (cur + n > limit) { if(limit > UINT_MAX / 2) return stbi__err("outofmem", "Out of memory"); limit *= 2; } q = (char *) STBI_REALLOC_SIZED(z->zout_start, old_limit, limit); STBI_NOTUSED(old_limit); if (q == NULL) return stbi__err("outofmem", "Out of memory"); z->zout_start = q; z->zout = q + cur; z->zout_end = q + limit; return 1; } static const int stbi__zlength_base[31] = { 3,4,5,6,7,8,9,10,11,13, 15,17,19,23,27,31,35,43,51,59, 67,83,99,115,131,163,195,227,258,0,0 }; static const int stbi__zlength_extra[31]= { 0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0 }; static const int stbi__zdist_base[32] = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193, 257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577,0,0}; static const int stbi__zdist_extra[32] = { 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13}; static int stbi__parse_huffman_block(stbi__zbuf *a) { char *zout = a->zout; for(;;) { int z = stbi__zhuffman_decode(a, &a->z_length); if (z < 256) { if (z < 0) return stbi__err("bad huffman code","Corrupt PNG"); // error in huffman codes if (zout >= a->zout_end) { if (!stbi__zexpand(a, zout, 1)) return 0; zout = a->zout; } *zout++ = (char) z; } else { stbi_uc *p; int len,dist; if (z == 256) { a->zout = zout; return 1; } z -= 257; len = stbi__zlength_base[z]; if (stbi__zlength_extra[z]) len += stbi__zreceive(a, stbi__zlength_extra[z]); z = stbi__zhuffman_decode(a, &a->z_distance); if (z < 0) return stbi__err("bad huffman code","Corrupt PNG"); dist = stbi__zdist_base[z]; if (stbi__zdist_extra[z]) dist += stbi__zreceive(a, stbi__zdist_extra[z]); if (zout - a->zout_start < dist) return stbi__err("bad dist","Corrupt PNG"); if (zout + len > a->zout_end) { if (!stbi__zexpand(a, zout, len)) return 0; zout = a->zout; } p = (stbi_uc *) (zout - dist); if (dist == 1) { // run of one byte; common in images. stbi_uc v = *p; if (len) { do *zout++ = v; while (--len); } } else { if (len) { do *zout++ = *p++; while (--len); } } } } } static int stbi__compute_huffman_codes(stbi__zbuf *a) { static const stbi_uc length_dezigzag[19] = { 16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15 }; stbi__zhuffman z_codelength; stbi_uc lencodes[286+32+137];//padding for maximum single op stbi_uc codelength_sizes[19]; int i,n; int hlit = stbi__zreceive(a,5) + 257; int hdist = stbi__zreceive(a,5) + 1; int hclen = stbi__zreceive(a,4) + 4; int ntot = hlit + hdist; memset(codelength_sizes, 0, sizeof(codelength_sizes)); for (i=0; i < hclen; ++i) { int s = stbi__zreceive(a,3); codelength_sizes[length_dezigzag[i]] = (stbi_uc) s; } if (!stbi__zbuild_huffman(&z_codelength, codelength_sizes, 19)) return 0; n = 0; while (n < ntot) { int c = stbi__zhuffman_decode(a, &z_codelength); if (c < 0 || c >= 19) return stbi__err("bad codelengths", "Corrupt PNG"); if (c < 16) lencodes[n++] = (stbi_uc) c; else { stbi_uc fill = 0; if (c == 16) { c = stbi__zreceive(a,2)+3; if (n == 0) return stbi__err("bad codelengths", "Corrupt PNG"); fill = lencodes[n-1]; } else if (c == 17) { c = stbi__zreceive(a,3)+3; } else if (c == 18) { c = stbi__zreceive(a,7)+11; } else { return stbi__err("bad codelengths", "Corrupt PNG"); } if (ntot - n < c) return stbi__err("bad codelengths", "Corrupt PNG"); memset(lencodes+n, fill, c); n += c; } } if (n != ntot) return stbi__err("bad codelengths","Corrupt PNG"); if (!stbi__zbuild_huffman(&a->z_length, lencodes, hlit)) return 0; if (!stbi__zbuild_huffman(&a->z_distance, lencodes+hlit, hdist)) return 0; return 1; } static int stbi__parse_uncompressed_block(stbi__zbuf *a) { stbi_uc header[4]; int len,nlen,k; if (a->num_bits & 7) stbi__zreceive(a, a->num_bits & 7); // discard // drain the bit-packed data into header k = 0; while (a->num_bits > 0) { header[k++] = (stbi_uc) (a->code_buffer & 255); // suppress MSVC run-time check a->code_buffer >>= 8; a->num_bits -= 8; } if (a->num_bits < 0) return stbi__err("zlib corrupt","Corrupt PNG"); // now fill header the normal way while (k < 4) header[k++] = stbi__zget8(a); len = header[1] * 256 + header[0]; nlen = header[3] * 256 + header[2]; if (nlen != (len ^ 0xffff)) return stbi__err("zlib corrupt","Corrupt PNG"); if (a->zbuffer + len > a->zbuffer_end) return stbi__err("read past buffer","Corrupt PNG"); if (a->zout + len > a->zout_end) if (!stbi__zexpand(a, a->zout, len)) return 0; memcpy(a->zout, a->zbuffer, len); a->zbuffer += len; a->zout += len; return 1; } static int stbi__parse_zlib_header(stbi__zbuf *a) { int cmf = stbi__zget8(a); int cm = cmf & 15; /* int cinfo = cmf >> 4; */ int flg = stbi__zget8(a); if (stbi__zeof(a)) return stbi__err("bad zlib header","Corrupt PNG"); // zlib spec if ((cmf*256+flg) % 31 != 0) return stbi__err("bad zlib header","Corrupt PNG"); // zlib spec if (flg & 32) return stbi__err("no preset dict","Corrupt PNG"); // preset dictionary not allowed in png if (cm != 8) return stbi__err("bad compression","Corrupt PNG"); // DEFLATE required for png // window = 1 << (8 + cinfo)... but who cares, we fully buffer output return 1; } static const stbi_uc stbi__zdefault_length[288] = { 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7,8,8,8,8,8,8,8,8 }; static const stbi_uc stbi__zdefault_distance[32] = { 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5 }; /* Init algorithm: { int i; // use <= to match clearly with spec for (i=0; i <= 143; ++i) stbi__zdefault_length[i] = 8; for ( ; i <= 255; ++i) stbi__zdefault_length[i] = 9; for ( ; i <= 279; ++i) stbi__zdefault_length[i] = 7; for ( ; i <= 287; ++i) stbi__zdefault_length[i] = 8; for (i=0; i <= 31; ++i) stbi__zdefault_distance[i] = 5; } */ static int stbi__parse_zlib(stbi__zbuf *a, int parse_header) { int final, type; if (parse_header) if (!stbi__parse_zlib_header(a)) return 0; a->num_bits = 0; a->code_buffer = 0; do { final = stbi__zreceive(a,1); type = stbi__zreceive(a,2); if (type == 0) { if (!stbi__parse_uncompressed_block(a)) return 0; } else if (type == 3) { return 0; } else { if (type == 1) { // use fixed code lengths if (!stbi__zbuild_huffman(&a->z_length , stbi__zdefault_length , 288)) return 0; if (!stbi__zbuild_huffman(&a->z_distance, stbi__zdefault_distance, 32)) return 0; } else { if (!stbi__compute_huffman_codes(a)) return 0; } if (!stbi__parse_huffman_block(a)) return 0; } } while (!final); return 1; } static int stbi__do_zlib(stbi__zbuf *a, char *obuf, int olen, int exp, int parse_header) { a->zout_start = obuf; a->zout = obuf; a->zout_end = obuf + olen; a->z_expandable = exp; return stbi__parse_zlib(a, parse_header); } STBIDEF char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int initial_size, int *outlen) { stbi__zbuf a; char *p = (char *) stbi__malloc(initial_size); if (p == NULL) return NULL; a.zbuffer = (stbi_uc *) buffer; a.zbuffer_end = (stbi_uc *) buffer + len; if (stbi__do_zlib(&a, p, initial_size, 1, 1)) { if (outlen) *outlen = (int) (a.zout - a.zout_start); return a.zout_start; } else { STBI_FREE(a.zout_start); return NULL; } } STBIDEF char *stbi_zlib_decode_malloc(char const *buffer, int len, int *outlen) { return stbi_zlib_decode_malloc_guesssize(buffer, len, 16384, outlen); } STBIDEF char *stbi_zlib_decode_malloc_guesssize_headerflag(const char *buffer, int len, int initial_size, int *outlen, int parse_header) { stbi__zbuf a; char *p = (char *) stbi__malloc(initial_size); if (p == NULL) return NULL; a.zbuffer = (stbi_uc *) buffer; a.zbuffer_end = (stbi_uc *) buffer + len; if (stbi__do_zlib(&a, p, initial_size, 1, parse_header)) { if (outlen) *outlen = (int) (a.zout - a.zout_start); return a.zout_start; } else { STBI_FREE(a.zout_start); return NULL; } } STBIDEF int stbi_zlib_decode_buffer(char *obuffer, int olen, char const *ibuffer, int ilen) { stbi__zbuf a; a.zbuffer = (stbi_uc *) ibuffer; a.zbuffer_end = (stbi_uc *) ibuffer + ilen; if (stbi__do_zlib(&a, obuffer, olen, 0, 1)) return (int) (a.zout - a.zout_start); else return -1; } STBIDEF char *stbi_zlib_decode_noheader_malloc(char const *buffer, int len, int *outlen) { stbi__zbuf a; char *p = (char *) stbi__malloc(16384); if (p == NULL) return NULL; a.zbuffer = (stbi_uc *) buffer; a.zbuffer_end = (stbi_uc *) buffer+len; if (stbi__do_zlib(&a, p, 16384, 1, 0)) { if (outlen) *outlen = (int) (a.zout - a.zout_start); return a.zout_start; } else { STBI_FREE(a.zout_start); return NULL; } } STBIDEF int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen) { stbi__zbuf a; a.zbuffer = (stbi_uc *) ibuffer; a.zbuffer_end = (stbi_uc *) ibuffer + ilen; if (stbi__do_zlib(&a, obuffer, olen, 0, 0)) return (int) (a.zout - a.zout_start); else return -1; } #endif // public domain "baseline" PNG decoder v0.10 Sean Barrett 2006-11-18 // simple implementation // - only 8-bit samples // - no CRC checking // - allocates lots of intermediate memory // - avoids problem of streaming data between subsystems // - avoids explicit window management // performance // - uses stb_zlib, a PD zlib implementation with fast huffman decoding #ifndef STBI_NO_PNG typedef struct { stbi__uint32 length; stbi__uint32 type; } stbi__pngchunk; static stbi__pngchunk stbi__get_chunk_header(stbi__context *s) { stbi__pngchunk c; c.length = stbi__get32be(s); c.type = stbi__get32be(s); return c; } static int stbi__check_png_header(stbi__context *s) { static const stbi_uc png_sig[8] = { 137,80,78,71,13,10,26,10 }; int i; for (i=0; i < 8; ++i) if (stbi__get8(s) != png_sig[i]) return stbi__err("bad png sig","Not a PNG"); return 1; } typedef struct { stbi__context *s; stbi_uc *idata, *expanded, *out; int depth; } stbi__png; enum { STBI__F_none=0, STBI__F_sub=1, STBI__F_up=2, STBI__F_avg=3, STBI__F_paeth=4, // synthetic filters used for first scanline to avoid needing a dummy row of 0s STBI__F_avg_first, STBI__F_paeth_first }; static stbi_uc first_row_filter[5] = { STBI__F_none, STBI__F_sub, STBI__F_none, STBI__F_avg_first, STBI__F_paeth_first }; static int stbi__paeth(int a, int b, int c) { int p = a + b - c; int pa = abs(p-a); int pb = abs(p-b); int pc = abs(p-c); if (pa <= pb && pa <= pc) return a; if (pb <= pc) return b; return c; } static const stbi_uc stbi__depth_scale_table[9] = { 0, 0xff, 0x55, 0, 0x11, 0,0,0, 0x01 }; // create the png data from post-deflated data static int stbi__create_png_image_raw(stbi__png *a, stbi_uc *raw, stbi__uint32 raw_len, int out_n, stbi__uint32 x, stbi__uint32 y, int depth, int color) { int bytes = (depth == 16? 2 : 1); stbi__context *s = a->s; stbi__uint32 i,j,stride = x*out_n*bytes; stbi__uint32 img_len, img_width_bytes; int k; int img_n = s->img_n; // copy it into a local for later int output_bytes = out_n*bytes; int filter_bytes = img_n*bytes; int width = x; STBI_ASSERT(out_n == s->img_n || out_n == s->img_n+1); a->out = (stbi_uc *) stbi__malloc_mad3(x, y, output_bytes, 0); // extra bytes to write off the end into if (!a->out) return stbi__err("outofmem", "Out of memory"); if (!stbi__mad3sizes_valid(img_n, x, depth, 7)) return stbi__err("too large", "Corrupt PNG"); img_width_bytes = (((img_n * x * depth) + 7) >> 3); img_len = (img_width_bytes + 1) * y; // we used to check for exact match between raw_len and img_len on non-interlaced PNGs, // but issue #276 reported a PNG in the wild that had extra data at the end (all zeros), // so just check for raw_len < img_len always. if (raw_len < img_len) return stbi__err("not enough pixels","Corrupt PNG"); for (j=0; j < y; ++j) { stbi_uc *cur = a->out + stride*j; stbi_uc *prior; int filter = *raw++; if (filter > 4) return stbi__err("invalid filter","Corrupt PNG"); if (depth < 8) { if (img_width_bytes > x) return stbi__err("invalid width","Corrupt PNG"); cur += x*out_n - img_width_bytes; // store output to the rightmost img_len bytes, so we can decode in place filter_bytes = 1; width = img_width_bytes; } prior = cur - stride; // bugfix: need to compute this after 'cur +=' computation above // if first row, use special filter that doesn't sample previous row if (j == 0) filter = first_row_filter[filter]; // handle first byte explicitly for (k=0; k < filter_bytes; ++k) { switch (filter) { case STBI__F_none : cur[k] = raw[k]; break; case STBI__F_sub : cur[k] = raw[k]; break; case STBI__F_up : cur[k] = STBI__BYTECAST(raw[k] + prior[k]); break; case STBI__F_avg : cur[k] = STBI__BYTECAST(raw[k] + (prior[k]>>1)); break; case STBI__F_paeth : cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(0,prior[k],0)); break; case STBI__F_avg_first : cur[k] = raw[k]; break; case STBI__F_paeth_first: cur[k] = raw[k]; break; } } if (depth == 8) { if (img_n != out_n) cur[img_n] = 255; // first pixel raw += img_n; cur += out_n; prior += out_n; } else if (depth == 16) { if (img_n != out_n) { cur[filter_bytes] = 255; // first pixel top byte cur[filter_bytes+1] = 255; // first pixel bottom byte } raw += filter_bytes; cur += output_bytes; prior += output_bytes; } else { raw += 1; cur += 1; prior += 1; } // this is a little gross, so that we don't switch per-pixel or per-component if (depth < 8 || img_n == out_n) { int nk = (width - 1)*filter_bytes; #define STBI__CASE(f) \ case f: \ for (k=0; k < nk; ++k) switch (filter) { // "none" filter turns into a memcpy here; make that explicit. case STBI__F_none: memcpy(cur, raw, nk); break; STBI__CASE(STBI__F_sub) { cur[k] = STBI__BYTECAST(raw[k] + cur[k-filter_bytes]); } break; STBI__CASE(STBI__F_up) { cur[k] = STBI__BYTECAST(raw[k] + prior[k]); } break; STBI__CASE(STBI__F_avg) { cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k-filter_bytes])>>1)); } break; STBI__CASE(STBI__F_paeth) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes],prior[k],prior[k-filter_bytes])); } break; STBI__CASE(STBI__F_avg_first) { cur[k] = STBI__BYTECAST(raw[k] + (cur[k-filter_bytes] >> 1)); } break; STBI__CASE(STBI__F_paeth_first) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes],0,0)); } break; } #undef STBI__CASE raw += nk; } else { STBI_ASSERT(img_n+1 == out_n); #define STBI__CASE(f) \ case f: \ for (i=x-1; i >= 1; --i, cur[filter_bytes]=255,raw+=filter_bytes,cur+=output_bytes,prior+=output_bytes) \ for (k=0; k < filter_bytes; ++k) switch (filter) { STBI__CASE(STBI__F_none) { cur[k] = raw[k]; } break; STBI__CASE(STBI__F_sub) { cur[k] = STBI__BYTECAST(raw[k] + cur[k- output_bytes]); } break; STBI__CASE(STBI__F_up) { cur[k] = STBI__BYTECAST(raw[k] + prior[k]); } break; STBI__CASE(STBI__F_avg) { cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k- output_bytes])>>1)); } break; STBI__CASE(STBI__F_paeth) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k- output_bytes],prior[k],prior[k- output_bytes])); } break; STBI__CASE(STBI__F_avg_first) { cur[k] = STBI__BYTECAST(raw[k] + (cur[k- output_bytes] >> 1)); } break; STBI__CASE(STBI__F_paeth_first) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k- output_bytes],0,0)); } break; } #undef STBI__CASE // the loop above sets the high byte of the pixels' alpha, but for // 16 bit png files we also need the low byte set. we'll do that here. if (depth == 16) { cur = a->out + stride*j; // start at the beginning of the row again for (i=0; i < x; ++i,cur+=output_bytes) { cur[filter_bytes+1] = 255; } } } } // we make a separate pass to expand bits to pixels; for performance, // this could run two scanlines behind the above code, so it won't // intefere with filtering but will still be in the cache. if (depth < 8) { for (j=0; j < y; ++j) { stbi_uc *cur = a->out + stride*j; stbi_uc *in = a->out + stride*j + x*out_n - img_width_bytes; // unpack 1/2/4-bit into a 8-bit buffer. allows us to keep the common 8-bit path optimal at minimal cost for 1/2/4-bit // png guarante byte alignment, if width is not multiple of 8/4/2 we'll decode dummy trailing data that will be skipped in the later loop stbi_uc scale = (color == 0) ? stbi__depth_scale_table[depth] : 1; // scale grayscale values to 0..255 range // note that the final byte might overshoot and write more data than desired. // we can allocate enough data that this never writes out of memory, but it // could also overwrite the next scanline. can it overwrite non-empty data // on the next scanline? yes, consider 1-pixel-wide scanlines with 1-bit-per-pixel. // so we need to explicitly clamp the final ones if (depth == 4) { for (k=x*img_n; k >= 2; k-=2, ++in) { *cur++ = scale * ((*in >> 4) ); *cur++ = scale * ((*in ) & 0x0f); } if (k > 0) *cur++ = scale * ((*in >> 4) ); } else if (depth == 2) { for (k=x*img_n; k >= 4; k-=4, ++in) { *cur++ = scale * ((*in >> 6) ); *cur++ = scale * ((*in >> 4) & 0x03); *cur++ = scale * ((*in >> 2) & 0x03); *cur++ = scale * ((*in ) & 0x03); } if (k > 0) *cur++ = scale * ((*in >> 6) ); if (k > 1) *cur++ = scale * ((*in >> 4) & 0x03); if (k > 2) *cur++ = scale * ((*in >> 2) & 0x03); } else if (depth == 1) { for (k=x*img_n; k >= 8; k-=8, ++in) { *cur++ = scale * ((*in >> 7) ); *cur++ = scale * ((*in >> 6) & 0x01); *cur++ = scale * ((*in >> 5) & 0x01); *cur++ = scale * ((*in >> 4) & 0x01); *cur++ = scale * ((*in >> 3) & 0x01); *cur++ = scale * ((*in >> 2) & 0x01); *cur++ = scale * ((*in >> 1) & 0x01); *cur++ = scale * ((*in ) & 0x01); } if (k > 0) *cur++ = scale * ((*in >> 7) ); if (k > 1) *cur++ = scale * ((*in >> 6) & 0x01); if (k > 2) *cur++ = scale * ((*in >> 5) & 0x01); if (k > 3) *cur++ = scale * ((*in >> 4) & 0x01); if (k > 4) *cur++ = scale * ((*in >> 3) & 0x01); if (k > 5) *cur++ = scale * ((*in >> 2) & 0x01); if (k > 6) *cur++ = scale * ((*in >> 1) & 0x01); } if (img_n != out_n) { int q; // insert alpha = 255 cur = a->out + stride*j; if (img_n == 1) { for (q=x-1; q >= 0; --q) { cur[q*2+1] = 255; cur[q*2+0] = cur[q]; } } else { STBI_ASSERT(img_n == 3); for (q=x-1; q >= 0; --q) { cur[q*4+3] = 255; cur[q*4+2] = cur[q*3+2]; cur[q*4+1] = cur[q*3+1]; cur[q*4+0] = cur[q*3+0]; } } } } } else if (depth == 16) { // force the image data from big-endian to platform-native. // this is done in a separate pass due to the decoding relying // on the data being untouched, but could probably be done // per-line during decode if care is taken. stbi_uc *cur = a->out; stbi__uint16 *cur16 = (stbi__uint16*)cur; for(i=0; i < x*y*out_n; ++i,cur16++,cur+=2) { *cur16 = (cur[0] << 8) | cur[1]; } } return 1; } static int stbi__create_png_image(stbi__png *a, stbi_uc *image_data, stbi__uint32 image_data_len, int out_n, int depth, int color, int interlaced) { int bytes = (depth == 16 ? 2 : 1); int out_bytes = out_n * bytes; stbi_uc *final; int p; if (!interlaced) return stbi__create_png_image_raw(a, image_data, image_data_len, out_n, a->s->img_x, a->s->img_y, depth, color); // de-interlacing final = (stbi_uc *) stbi__malloc_mad3(a->s->img_x, a->s->img_y, out_bytes, 0); for (p=0; p < 7; ++p) { int xorig[] = { 0,4,0,2,0,1,0 }; int yorig[] = { 0,0,4,0,2,0,1 }; int xspc[] = { 8,8,4,4,2,2,1 }; int yspc[] = { 8,8,8,4,4,2,2 }; int i,j,x,y; // pass1_x[4] = 0, pass1_x[5] = 1, pass1_x[12] = 1 x = (a->s->img_x - xorig[p] + xspc[p]-1) / xspc[p]; y = (a->s->img_y - yorig[p] + yspc[p]-1) / yspc[p]; if (x && y) { stbi__uint32 img_len = ((((a->s->img_n * x * depth) + 7) >> 3) + 1) * y; if (!stbi__create_png_image_raw(a, image_data, image_data_len, out_n, x, y, depth, color)) { STBI_FREE(final); return 0; } for (j=0; j < y; ++j) { for (i=0; i < x; ++i) { int out_y = j*yspc[p]+yorig[p]; int out_x = i*xspc[p]+xorig[p]; memcpy(final + out_y*a->s->img_x*out_bytes + out_x*out_bytes, a->out + (j*x+i)*out_bytes, out_bytes); } } STBI_FREE(a->out); image_data += img_len; image_data_len -= img_len; } } a->out = final; return 1; } static int stbi__compute_transparency(stbi__png *z, stbi_uc tc[3], int out_n) { stbi__context *s = z->s; stbi__uint32 i, pixel_count = s->img_x * s->img_y; stbi_uc *p = z->out; // compute color-based transparency, assuming we've // already got 255 as the alpha value in the output STBI_ASSERT(out_n == 2 || out_n == 4); if (out_n == 2) { for (i=0; i < pixel_count; ++i) { p[1] = (p[0] == tc[0] ? 0 : 255); p += 2; } } else { for (i=0; i < pixel_count; ++i) { if (p[0] == tc[0] && p[1] == tc[1] && p[2] == tc[2]) p[3] = 0; p += 4; } } return 1; } static int stbi__compute_transparency16(stbi__png *z, stbi__uint16 tc[3], int out_n) { stbi__context *s = z->s; stbi__uint32 i, pixel_count = s->img_x * s->img_y; stbi__uint16 *p = (stbi__uint16*) z->out; // compute color-based transparency, assuming we've // already got 65535 as the alpha value in the output STBI_ASSERT(out_n == 2 || out_n == 4); if (out_n == 2) { for (i = 0; i < pixel_count; ++i) { p[1] = (p[0] == tc[0] ? 0 : 65535); p += 2; } } else { for (i = 0; i < pixel_count; ++i) { if (p[0] == tc[0] && p[1] == tc[1] && p[2] == tc[2]) p[3] = 0; p += 4; } } return 1; } static int stbi__expand_png_palette(stbi__png *a, stbi_uc *palette, int len, int pal_img_n) { stbi__uint32 i, pixel_count = a->s->img_x * a->s->img_y; stbi_uc *p, *temp_out, *orig = a->out; p = (stbi_uc *) stbi__malloc_mad2(pixel_count, pal_img_n, 0); if (p == NULL) return stbi__err("outofmem", "Out of memory"); // between here and free(out) below, exitting would leak temp_out = p; if (pal_img_n == 3) { for (i=0; i < pixel_count; ++i) { int n = orig[i]*4; p[0] = palette[n ]; p[1] = palette[n+1]; p[2] = palette[n+2]; p += 3; } } else { for (i=0; i < pixel_count; ++i) { int n = orig[i]*4; p[0] = palette[n ]; p[1] = palette[n+1]; p[2] = palette[n+2]; p[3] = palette[n+3]; p += 4; } } STBI_FREE(a->out); a->out = temp_out; STBI_NOTUSED(len); return 1; } static int stbi__unpremultiply_on_load = 0; static int stbi__de_iphone_flag = 0; STBIDEF void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpremultiply) { stbi__unpremultiply_on_load = flag_true_if_should_unpremultiply; } STBIDEF void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert) { stbi__de_iphone_flag = flag_true_if_should_convert; } static void stbi__de_iphone(stbi__png *z) { stbi__context *s = z->s; stbi__uint32 i, pixel_count = s->img_x * s->img_y; stbi_uc *p = z->out; if (s->img_out_n == 3) { // convert bgr to rgb for (i=0; i < pixel_count; ++i) { stbi_uc t = p[0]; p[0] = p[2]; p[2] = t; p += 3; } } else { STBI_ASSERT(s->img_out_n == 4); if (stbi__unpremultiply_on_load) { // convert bgr to rgb and unpremultiply for (i=0; i < pixel_count; ++i) { stbi_uc a = p[3]; stbi_uc t = p[0]; if (a) { stbi_uc half = a / 2; p[0] = (p[2] * 255 + half) / a; p[1] = (p[1] * 255 + half) / a; p[2] = ( t * 255 + half) / a; } else { p[0] = p[2]; p[2] = t; } p += 4; } } else { // convert bgr to rgb for (i=0; i < pixel_count; ++i) { stbi_uc t = p[0]; p[0] = p[2]; p[2] = t; p += 4; } } } } #define STBI__PNG_TYPE(a,b,c,d) (((unsigned) (a) << 24) + ((unsigned) (b) << 16) + ((unsigned) (c) << 8) + (unsigned) (d)) static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp) { stbi_uc palette[1024], pal_img_n=0; stbi_uc has_trans=0, tc[3]={0}; stbi__uint16 tc16[3]; stbi__uint32 ioff=0, idata_limit=0, i, pal_len=0; int first=1,k,interlace=0, color=0, is_iphone=0; stbi__context *s = z->s; z->expanded = NULL; z->idata = NULL; z->out = NULL; if (!stbi__check_png_header(s)) return 0; if (scan == STBI__SCAN_type) return 1; for (;;) { stbi__pngchunk c = stbi__get_chunk_header(s); switch (c.type) { case STBI__PNG_TYPE('C','g','B','I'): is_iphone = 1; stbi__skip(s, c.length); break; case STBI__PNG_TYPE('I','H','D','R'): { int comp,filter; if (!first) return stbi__err("multiple IHDR","Corrupt PNG"); first = 0; if (c.length != 13) return stbi__err("bad IHDR len","Corrupt PNG"); s->img_x = stbi__get32be(s); s->img_y = stbi__get32be(s); if (s->img_y > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); if (s->img_x > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); z->depth = stbi__get8(s); if (z->depth != 1 && z->depth != 2 && z->depth != 4 && z->depth != 8 && z->depth != 16) return stbi__err("1/2/4/8/16-bit only","PNG not supported: 1/2/4/8/16-bit only"); color = stbi__get8(s); if (color > 6) return stbi__err("bad ctype","Corrupt PNG"); if (color == 3 && z->depth == 16) return stbi__err("bad ctype","Corrupt PNG"); if (color == 3) pal_img_n = 3; else if (color & 1) return stbi__err("bad ctype","Corrupt PNG"); comp = stbi__get8(s); if (comp) return stbi__err("bad comp method","Corrupt PNG"); filter= stbi__get8(s); if (filter) return stbi__err("bad filter method","Corrupt PNG"); interlace = stbi__get8(s); if (interlace>1) return stbi__err("bad interlace method","Corrupt PNG"); if (!s->img_x || !s->img_y) return stbi__err("0-pixel image","Corrupt PNG"); if (!pal_img_n) { s->img_n = (color & 2 ? 3 : 1) + (color & 4 ? 1 : 0); if ((1 << 30) / s->img_x / s->img_n < s->img_y) return stbi__err("too large", "Image too large to decode"); if (scan == STBI__SCAN_header) return 1; } else { // if paletted, then pal_n is our final components, and // img_n is # components to decompress/filter. s->img_n = 1; if ((1 << 30) / s->img_x / 4 < s->img_y) return stbi__err("too large","Corrupt PNG"); // if SCAN_header, have to scan to see if we have a tRNS } break; } case STBI__PNG_TYPE('P','L','T','E'): { if (first) return stbi__err("first not IHDR", "Corrupt PNG"); if (c.length > 256*3) return stbi__err("invalid PLTE","Corrupt PNG"); pal_len = c.length / 3; if (pal_len * 3 != c.length) return stbi__err("invalid PLTE","Corrupt PNG"); for (i=0; i < pal_len; ++i) { palette[i*4+0] = stbi__get8(s); palette[i*4+1] = stbi__get8(s); palette[i*4+2] = stbi__get8(s); palette[i*4+3] = 255; } break; } case STBI__PNG_TYPE('t','R','N','S'): { if (first) return stbi__err("first not IHDR", "Corrupt PNG"); if (z->idata) return stbi__err("tRNS after IDAT","Corrupt PNG"); if (pal_img_n) { if (scan == STBI__SCAN_header) { s->img_n = 4; return 1; } if (pal_len == 0) return stbi__err("tRNS before PLTE","Corrupt PNG"); if (c.length > pal_len) return stbi__err("bad tRNS len","Corrupt PNG"); pal_img_n = 4; for (i=0; i < c.length; ++i) palette[i*4+3] = stbi__get8(s); } else { if (!(s->img_n & 1)) return stbi__err("tRNS with alpha","Corrupt PNG"); if (c.length != (stbi__uint32) s->img_n*2) return stbi__err("bad tRNS len","Corrupt PNG"); has_trans = 1; if (z->depth == 16) { for (k = 0; k < s->img_n; ++k) tc16[k] = (stbi__uint16)stbi__get16be(s); // copy the values as-is } else { for (k = 0; k < s->img_n; ++k) tc[k] = (stbi_uc)(stbi__get16be(s) & 255) * stbi__depth_scale_table[z->depth]; // non 8-bit images will be larger } } break; } case STBI__PNG_TYPE('I','D','A','T'): { if (first) return stbi__err("first not IHDR", "Corrupt PNG"); if (pal_img_n && !pal_len) return stbi__err("no PLTE","Corrupt PNG"); if (scan == STBI__SCAN_header) { s->img_n = pal_img_n; return 1; } if ((int)(ioff + c.length) < (int)ioff) return 0; if (ioff + c.length > idata_limit) { stbi__uint32 idata_limit_old = idata_limit; stbi_uc *p; if (idata_limit == 0) idata_limit = c.length > 4096 ? c.length : 4096; while (ioff + c.length > idata_limit) idata_limit *= 2; STBI_NOTUSED(idata_limit_old); p = (stbi_uc *) STBI_REALLOC_SIZED(z->idata, idata_limit_old, idata_limit); if (p == NULL) return stbi__err("outofmem", "Out of memory"); z->idata = p; } if (!stbi__getn(s, z->idata+ioff,c.length)) return stbi__err("outofdata","Corrupt PNG"); ioff += c.length; break; } case STBI__PNG_TYPE('I','E','N','D'): { stbi__uint32 raw_len, bpl; if (first) return stbi__err("first not IHDR", "Corrupt PNG"); if (scan != STBI__SCAN_load) return 1; if (z->idata == NULL) return stbi__err("no IDAT","Corrupt PNG"); // initial guess for decoded data size to avoid unnecessary reallocs bpl = (s->img_x * z->depth + 7) / 8; // bytes per line, per component raw_len = bpl * s->img_y * s->img_n /* pixels */ + s->img_y /* filter mode per row */; z->expanded = (stbi_uc *) stbi_zlib_decode_malloc_guesssize_headerflag((char *) z->idata, ioff, raw_len, (int *) &raw_len, !is_iphone); if (z->expanded == NULL) return 0; // zlib should set error STBI_FREE(z->idata); z->idata = NULL; if ((req_comp == s->img_n+1 && req_comp != 3 && !pal_img_n) || has_trans) s->img_out_n = s->img_n+1; else s->img_out_n = s->img_n; if (!stbi__create_png_image(z, z->expanded, raw_len, s->img_out_n, z->depth, color, interlace)) return 0; if (has_trans) { if (z->depth == 16) { if (!stbi__compute_transparency16(z, tc16, s->img_out_n)) return 0; } else { if (!stbi__compute_transparency(z, tc, s->img_out_n)) return 0; } } if (is_iphone && stbi__de_iphone_flag && s->img_out_n > 2) stbi__de_iphone(z); if (pal_img_n) { // pal_img_n == 3 or 4 s->img_n = pal_img_n; // record the actual colors we had s->img_out_n = pal_img_n; if (req_comp >= 3) s->img_out_n = req_comp; if (!stbi__expand_png_palette(z, palette, pal_len, s->img_out_n)) return 0; } else if (has_trans) { // non-paletted image with tRNS -> source image has (constant) alpha ++s->img_n; } STBI_FREE(z->expanded); z->expanded = NULL; // end of PNG chunk, read and skip CRC stbi__get32be(s); return 1; } default: // if critical, fail if (first) return stbi__err("first not IHDR", "Corrupt PNG"); if ((c.type & (1 << 29)) == 0) { #ifndef STBI_NO_FAILURE_STRINGS // not threadsafe static char invalid_chunk[] = "XXXX PNG chunk not known"; invalid_chunk[0] = STBI__BYTECAST(c.type >> 24); invalid_chunk[1] = STBI__BYTECAST(c.type >> 16); invalid_chunk[2] = STBI__BYTECAST(c.type >> 8); invalid_chunk[3] = STBI__BYTECAST(c.type >> 0); #endif return stbi__err(invalid_chunk, "PNG not supported: unknown PNG chunk type"); } stbi__skip(s, c.length); break; } // end of PNG chunk, read and skip CRC stbi__get32be(s); } } static void *stbi__do_png(stbi__png *p, int *x, int *y, int *n, int req_comp, stbi__result_info *ri) { void *result=NULL; if (req_comp < 0 || req_comp > 4) return stbi__errpuc("bad req_comp", "Internal error"); if (stbi__parse_png_file(p, STBI__SCAN_load, req_comp)) { if (p->depth <= 8) ri->bits_per_channel = 8; else if (p->depth == 16) ri->bits_per_channel = 16; else return stbi__errpuc("bad bits_per_channel", "PNG not supported: unsupported color depth"); result = p->out; p->out = NULL; if (req_comp && req_comp != p->s->img_out_n) { if (ri->bits_per_channel == 8) result = stbi__convert_format((unsigned char *) result, p->s->img_out_n, req_comp, p->s->img_x, p->s->img_y); else result = stbi__convert_format16((stbi__uint16 *) result, p->s->img_out_n, req_comp, p->s->img_x, p->s->img_y); p->s->img_out_n = req_comp; if (result == NULL) return result; } *x = p->s->img_x; *y = p->s->img_y; if (n) *n = p->s->img_n; } STBI_FREE(p->out); p->out = NULL; STBI_FREE(p->expanded); p->expanded = NULL; STBI_FREE(p->idata); p->idata = NULL; return result; } static void *stbi__png_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) { stbi__png p; p.s = s; return stbi__do_png(&p, x,y,comp,req_comp, ri); } static int stbi__png_test(stbi__context *s) { int r; r = stbi__check_png_header(s); stbi__rewind(s); return r; } static int stbi__png_info_raw(stbi__png *p, int *x, int *y, int *comp) { if (!stbi__parse_png_file(p, STBI__SCAN_header, 0)) { stbi__rewind( p->s ); return 0; } if (x) *x = p->s->img_x; if (y) *y = p->s->img_y; if (comp) *comp = p->s->img_n; return 1; } static int stbi__png_info(stbi__context *s, int *x, int *y, int *comp) { stbi__png p; p.s = s; return stbi__png_info_raw(&p, x, y, comp); } static int stbi__png_is16(stbi__context *s) { stbi__png p; p.s = s; if (!stbi__png_info_raw(&p, NULL, NULL, NULL)) return 0; if (p.depth != 16) { stbi__rewind(p.s); return 0; } return 1; } #endif // Microsoft/Windows BMP image #ifndef STBI_NO_BMP static int stbi__bmp_test_raw(stbi__context *s) { int r; int sz; if (stbi__get8(s) != 'B') return 0; if (stbi__get8(s) != 'M') return 0; stbi__get32le(s); // discard filesize stbi__get16le(s); // discard reserved stbi__get16le(s); // discard reserved stbi__get32le(s); // discard data offset sz = stbi__get32le(s); r = (sz == 12 || sz == 40 || sz == 56 || sz == 108 || sz == 124); return r; } static int stbi__bmp_test(stbi__context *s) { int r = stbi__bmp_test_raw(s); stbi__rewind(s); return r; } // returns 0..31 for the highest set bit static int stbi__high_bit(unsigned int z) { int n=0; if (z == 0) return -1; if (z >= 0x10000) { n += 16; z >>= 16; } if (z >= 0x00100) { n += 8; z >>= 8; } if (z >= 0x00010) { n += 4; z >>= 4; } if (z >= 0x00004) { n += 2; z >>= 2; } if (z >= 0x00002) { n += 1;/* >>= 1;*/ } return n; } static int stbi__bitcount(unsigned int a) { a = (a & 0x55555555) + ((a >> 1) & 0x55555555); // max 2 a = (a & 0x33333333) + ((a >> 2) & 0x33333333); // max 4 a = (a + (a >> 4)) & 0x0f0f0f0f; // max 8 per 4, now 8 bits a = (a + (a >> 8)); // max 16 per 8 bits a = (a + (a >> 16)); // max 32 per 8 bits return a & 0xff; } // extract an arbitrarily-aligned N-bit value (N=bits) // from v, and then make it 8-bits long and fractionally // extend it to full full range. static int stbi__shiftsigned(unsigned int v, int shift, int bits) { static unsigned int mul_table[9] = { 0, 0xff/*0b11111111*/, 0x55/*0b01010101*/, 0x49/*0b01001001*/, 0x11/*0b00010001*/, 0x21/*0b00100001*/, 0x41/*0b01000001*/, 0x81/*0b10000001*/, 0x01/*0b00000001*/, }; static unsigned int shift_table[9] = { 0, 0,0,1,0,2,4,6,0, }; if (shift < 0) v <<= -shift; else v >>= shift; STBI_ASSERT(v < 256); v >>= (8-bits); STBI_ASSERT(bits >= 0 && bits <= 8); return (int) ((unsigned) v * mul_table[bits]) >> shift_table[bits]; } typedef struct { int bpp, offset, hsz; unsigned int mr,mg,mb,ma, all_a; int extra_read; } stbi__bmp_data; static void *stbi__bmp_parse_header(stbi__context *s, stbi__bmp_data *info) { int hsz; if (stbi__get8(s) != 'B' || stbi__get8(s) != 'M') return stbi__errpuc("not BMP", "Corrupt BMP"); stbi__get32le(s); // discard filesize stbi__get16le(s); // discard reserved stbi__get16le(s); // discard reserved info->offset = stbi__get32le(s); info->hsz = hsz = stbi__get32le(s); info->mr = info->mg = info->mb = info->ma = 0; info->extra_read = 14; if (info->offset < 0) return stbi__errpuc("bad BMP", "bad BMP"); if (hsz != 12 && hsz != 40 && hsz != 56 && hsz != 108 && hsz != 124) return stbi__errpuc("unknown BMP", "BMP type not supported: unknown"); if (hsz == 12) { s->img_x = stbi__get16le(s); s->img_y = stbi__get16le(s); } else { s->img_x = stbi__get32le(s); s->img_y = stbi__get32le(s); } if (stbi__get16le(s) != 1) return stbi__errpuc("bad BMP", "bad BMP"); info->bpp = stbi__get16le(s); if (hsz != 12) { int compress = stbi__get32le(s); if (compress == 1 || compress == 2) return stbi__errpuc("BMP RLE", "BMP type not supported: RLE"); stbi__get32le(s); // discard sizeof stbi__get32le(s); // discard hres stbi__get32le(s); // discard vres stbi__get32le(s); // discard colorsused stbi__get32le(s); // discard max important if (hsz == 40 || hsz == 56) { if (hsz == 56) { stbi__get32le(s); stbi__get32le(s); stbi__get32le(s); stbi__get32le(s); } if (info->bpp == 16 || info->bpp == 32) { if (compress == 0) { if (info->bpp == 32) { info->mr = 0xffu << 16; info->mg = 0xffu << 8; info->mb = 0xffu << 0; info->ma = 0xffu << 24; info->all_a = 0; // if all_a is 0 at end, then we loaded alpha channel but it was all 0 } else { info->mr = 31u << 10; info->mg = 31u << 5; info->mb = 31u << 0; } } else if (compress == 3) { info->mr = stbi__get32le(s); info->mg = stbi__get32le(s); info->mb = stbi__get32le(s); info->extra_read += 12; // not documented, but generated by photoshop and handled by mspaint if (info->mr == info->mg && info->mg == info->mb) { // ?!?!? return stbi__errpuc("bad BMP", "bad BMP"); } } else return stbi__errpuc("bad BMP", "bad BMP"); } } else { int i; if (hsz != 108 && hsz != 124) return stbi__errpuc("bad BMP", "bad BMP"); info->mr = stbi__get32le(s); info->mg = stbi__get32le(s); info->mb = stbi__get32le(s); info->ma = stbi__get32le(s); stbi__get32le(s); // discard color space for (i=0; i < 12; ++i) stbi__get32le(s); // discard color space parameters if (hsz == 124) { stbi__get32le(s); // discard rendering intent stbi__get32le(s); // discard offset of profile data stbi__get32le(s); // discard size of profile data stbi__get32le(s); // discard reserved } } } return (void *) 1; } static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) { stbi_uc *out; unsigned int mr=0,mg=0,mb=0,ma=0, all_a; stbi_uc pal[256][4]; int psize=0,i,j,width; int flip_vertically, pad, target; stbi__bmp_data info; STBI_NOTUSED(ri); info.all_a = 255; if (stbi__bmp_parse_header(s, &info) == NULL) return NULL; // error code already set flip_vertically = ((int) s->img_y) > 0; s->img_y = abs((int) s->img_y); if (s->img_y > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); if (s->img_x > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); mr = info.mr; mg = info.mg; mb = info.mb; ma = info.ma; all_a = info.all_a; if (info.hsz == 12) { if (info.bpp < 24) psize = (info.offset - info.extra_read - 24) / 3; } else { if (info.bpp < 16) psize = (info.offset - info.extra_read - info.hsz) >> 2; } if (psize == 0) { STBI_ASSERT(info.offset == s->callback_already_read + (int) (s->img_buffer - s->img_buffer_original)); if (info.offset != s->callback_already_read + (s->img_buffer - s->buffer_start)) { return stbi__errpuc("bad offset", "Corrupt BMP"); } } if (info.bpp == 24 && ma == 0xff000000) s->img_n = 3; else s->img_n = ma ? 4 : 3; if (req_comp && req_comp >= 3) // we can directly decode 3 or 4 target = req_comp; else target = s->img_n; // if they want monochrome, we'll post-convert // sanity-check size if (!stbi__mad3sizes_valid(target, s->img_x, s->img_y, 0)) return stbi__errpuc("too large", "Corrupt BMP"); out = (stbi_uc *) stbi__malloc_mad3(target, s->img_x, s->img_y, 0); if (!out) return stbi__errpuc("outofmem", "Out of memory"); if (info.bpp < 16) { int z=0; if (psize == 0 || psize > 256) { STBI_FREE(out); return stbi__errpuc("invalid", "Corrupt BMP"); } for (i=0; i < psize; ++i) { pal[i][2] = stbi__get8(s); pal[i][1] = stbi__get8(s); pal[i][0] = stbi__get8(s); if (info.hsz != 12) stbi__get8(s); pal[i][3] = 255; } stbi__skip(s, info.offset - info.extra_read - info.hsz - psize * (info.hsz == 12 ? 3 : 4)); if (info.bpp == 1) width = (s->img_x + 7) >> 3; else if (info.bpp == 4) width = (s->img_x + 1) >> 1; else if (info.bpp == 8) width = s->img_x; else { STBI_FREE(out); return stbi__errpuc("bad bpp", "Corrupt BMP"); } pad = (-width)&3; if (info.bpp == 1) { for (j=0; j < (int) s->img_y; ++j) { int bit_offset = 7, v = stbi__get8(s); for (i=0; i < (int) s->img_x; ++i) { int color = (v>>bit_offset)&0x1; out[z++] = pal[color][0]; out[z++] = pal[color][1]; out[z++] = pal[color][2]; if (target == 4) out[z++] = 255; if (i+1 == (int) s->img_x) break; if((--bit_offset) < 0) { bit_offset = 7; v = stbi__get8(s); } } stbi__skip(s, pad); } } else { for (j=0; j < (int) s->img_y; ++j) { for (i=0; i < (int) s->img_x; i += 2) { int v=stbi__get8(s),v2=0; if (info.bpp == 4) { v2 = v & 15; v >>= 4; } out[z++] = pal[v][0]; out[z++] = pal[v][1]; out[z++] = pal[v][2]; if (target == 4) out[z++] = 255; if (i+1 == (int) s->img_x) break; v = (info.bpp == 8) ? stbi__get8(s) : v2; out[z++] = pal[v][0]; out[z++] = pal[v][1]; out[z++] = pal[v][2]; if (target == 4) out[z++] = 255; } stbi__skip(s, pad); } } } else { int rshift=0,gshift=0,bshift=0,ashift=0,rcount=0,gcount=0,bcount=0,acount=0; int z = 0; int easy=0; stbi__skip(s, info.offset - info.extra_read - info.hsz); if (info.bpp == 24) width = 3 * s->img_x; else if (info.bpp == 16) width = 2*s->img_x; else /* bpp = 32 and pad = 0 */ width=0; pad = (-width) & 3; if (info.bpp == 24) { easy = 1; } else if (info.bpp == 32) { if (mb == 0xff && mg == 0xff00 && mr == 0x00ff0000 && ma == 0xff000000) easy = 2; } if (!easy) { if (!mr || !mg || !mb) { STBI_FREE(out); return stbi__errpuc("bad masks", "Corrupt BMP"); } // right shift amt to put high bit in position #7 rshift = stbi__high_bit(mr)-7; rcount = stbi__bitcount(mr); gshift = stbi__high_bit(mg)-7; gcount = stbi__bitcount(mg); bshift = stbi__high_bit(mb)-7; bcount = stbi__bitcount(mb); ashift = stbi__high_bit(ma)-7; acount = stbi__bitcount(ma); if (rcount > 8 || gcount > 8 || bcount > 8 || acount > 8) { STBI_FREE(out); return stbi__errpuc("bad masks", "Corrupt BMP"); } } for (j=0; j < (int) s->img_y; ++j) { if (easy) { for (i=0; i < (int) s->img_x; ++i) { unsigned char a; out[z+2] = stbi__get8(s); out[z+1] = stbi__get8(s); out[z+0] = stbi__get8(s); z += 3; a = (easy == 2 ? stbi__get8(s) : 255); all_a |= a; if (target == 4) out[z++] = a; } } else { int bpp = info.bpp; for (i=0; i < (int) s->img_x; ++i) { stbi__uint32 v = (bpp == 16 ? (stbi__uint32) stbi__get16le(s) : stbi__get32le(s)); unsigned int a; out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mr, rshift, rcount)); out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mg, gshift, gcount)); out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mb, bshift, bcount)); a = (ma ? stbi__shiftsigned(v & ma, ashift, acount) : 255); all_a |= a; if (target == 4) out[z++] = STBI__BYTECAST(a); } } stbi__skip(s, pad); } } // if alpha channel is all 0s, replace with all 255s if (target == 4 && all_a == 0) for (i=4*s->img_x*s->img_y-1; i >= 0; i -= 4) out[i] = 255; if (flip_vertically) { stbi_uc t; for (j=0; j < (int) s->img_y>>1; ++j) { stbi_uc *p1 = out + j *s->img_x*target; stbi_uc *p2 = out + (s->img_y-1-j)*s->img_x*target; for (i=0; i < (int) s->img_x*target; ++i) { t = p1[i]; p1[i] = p2[i]; p2[i] = t; } } } if (req_comp && req_comp != target) { out = stbi__convert_format(out, target, req_comp, s->img_x, s->img_y); if (out == NULL) return out; // stbi__convert_format frees input on failure } *x = s->img_x; *y = s->img_y; if (comp) *comp = s->img_n; return out; } #endif // Targa Truevision - TGA // by Jonathan Dummer #ifndef STBI_NO_TGA // returns STBI_rgb or whatever, 0 on error static int stbi__tga_get_comp(int bits_per_pixel, int is_grey, int* is_rgb16) { // only RGB or RGBA (incl. 16bit) or grey allowed if (is_rgb16) *is_rgb16 = 0; switch(bits_per_pixel) { case 8: return STBI_grey; case 16: if(is_grey) return STBI_grey_alpha; // fallthrough case 15: if(is_rgb16) *is_rgb16 = 1; return STBI_rgb; case 24: // fallthrough case 32: return bits_per_pixel/8; default: return 0; } } static int stbi__tga_info(stbi__context *s, int *x, int *y, int *comp) { int tga_w, tga_h, tga_comp, tga_image_type, tga_bits_per_pixel, tga_colormap_bpp; int sz, tga_colormap_type; stbi__get8(s); // discard Offset tga_colormap_type = stbi__get8(s); // colormap type if( tga_colormap_type > 1 ) { stbi__rewind(s); return 0; // only RGB or indexed allowed } tga_image_type = stbi__get8(s); // image type if ( tga_colormap_type == 1 ) { // colormapped (paletted) image if (tga_image_type != 1 && tga_image_type != 9) { stbi__rewind(s); return 0; } stbi__skip(s,4); // skip index of first colormap entry and number of entries sz = stbi__get8(s); // check bits per palette color entry if ( (sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32) ) { stbi__rewind(s); return 0; } stbi__skip(s,4); // skip image x and y origin tga_colormap_bpp = sz; } else { // "normal" image w/o colormap - only RGB or grey allowed, +/- RLE if ( (tga_image_type != 2) && (tga_image_type != 3) && (tga_image_type != 10) && (tga_image_type != 11) ) { stbi__rewind(s); return 0; // only RGB or grey allowed, +/- RLE } stbi__skip(s,9); // skip colormap specification and image x/y origin tga_colormap_bpp = 0; } tga_w = stbi__get16le(s); if( tga_w < 1 ) { stbi__rewind(s); return 0; // test width } tga_h = stbi__get16le(s); if( tga_h < 1 ) { stbi__rewind(s); return 0; // test height } tga_bits_per_pixel = stbi__get8(s); // bits per pixel stbi__get8(s); // ignore alpha bits if (tga_colormap_bpp != 0) { if((tga_bits_per_pixel != 8) && (tga_bits_per_pixel != 16)) { // when using a colormap, tga_bits_per_pixel is the size of the indexes // I don't think anything but 8 or 16bit indexes makes sense stbi__rewind(s); return 0; } tga_comp = stbi__tga_get_comp(tga_colormap_bpp, 0, NULL); } else { tga_comp = stbi__tga_get_comp(tga_bits_per_pixel, (tga_image_type == 3) || (tga_image_type == 11), NULL); } if(!tga_comp) { stbi__rewind(s); return 0; } if (x) *x = tga_w; if (y) *y = tga_h; if (comp) *comp = tga_comp; return 1; // seems to have passed everything } static int stbi__tga_test(stbi__context *s) { int res = 0; int sz, tga_color_type; stbi__get8(s); // discard Offset tga_color_type = stbi__get8(s); // color type if ( tga_color_type > 1 ) goto errorEnd; // only RGB or indexed allowed sz = stbi__get8(s); // image type if ( tga_color_type == 1 ) { // colormapped (paletted) image if (sz != 1 && sz != 9) goto errorEnd; // colortype 1 demands image type 1 or 9 stbi__skip(s,4); // skip index of first colormap entry and number of entries sz = stbi__get8(s); // check bits per palette color entry if ( (sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32) ) goto errorEnd; stbi__skip(s,4); // skip image x and y origin } else { // "normal" image w/o colormap if ( (sz != 2) && (sz != 3) && (sz != 10) && (sz != 11) ) goto errorEnd; // only RGB or grey allowed, +/- RLE stbi__skip(s,9); // skip colormap specification and image x/y origin } if ( stbi__get16le(s) < 1 ) goto errorEnd; // test width if ( stbi__get16le(s) < 1 ) goto errorEnd; // test height sz = stbi__get8(s); // bits per pixel if ( (tga_color_type == 1) && (sz != 8) && (sz != 16) ) goto errorEnd; // for colormapped images, bpp is size of an index if ( (sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32) ) goto errorEnd; res = 1; // if we got this far, everything's good and we can return 1 instead of 0 errorEnd: stbi__rewind(s); return res; } // read 16bit value and convert to 24bit RGB static void stbi__tga_read_rgb16(stbi__context *s, stbi_uc* out) { stbi__uint16 px = (stbi__uint16)stbi__get16le(s); stbi__uint16 fiveBitMask = 31; // we have 3 channels with 5bits each int r = (px >> 10) & fiveBitMask; int g = (px >> 5) & fiveBitMask; int b = px & fiveBitMask; // Note that this saves the data in RGB(A) order, so it doesn't need to be swapped later out[0] = (stbi_uc)((r * 255)/31); out[1] = (stbi_uc)((g * 255)/31); out[2] = (stbi_uc)((b * 255)/31); // some people claim that the most significant bit might be used for alpha // (possibly if an alpha-bit is set in the "image descriptor byte") // but that only made 16bit test images completely translucent.. // so let's treat all 15 and 16bit TGAs as RGB with no alpha. } static void *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) { // read in the TGA header stuff int tga_offset = stbi__get8(s); int tga_indexed = stbi__get8(s); int tga_image_type = stbi__get8(s); int tga_is_RLE = 0; int tga_palette_start = stbi__get16le(s); int tga_palette_len = stbi__get16le(s); int tga_palette_bits = stbi__get8(s); int tga_x_origin = stbi__get16le(s); int tga_y_origin = stbi__get16le(s); int tga_width = stbi__get16le(s); int tga_height = stbi__get16le(s); int tga_bits_per_pixel = stbi__get8(s); int tga_comp, tga_rgb16=0; int tga_inverted = stbi__get8(s); // int tga_alpha_bits = tga_inverted & 15; // the 4 lowest bits - unused (useless?) // image data unsigned char *tga_data; unsigned char *tga_palette = NULL; int i, j; unsigned char raw_data[4] = {0}; int RLE_count = 0; int RLE_repeating = 0; int read_next_pixel = 1; STBI_NOTUSED(ri); STBI_NOTUSED(tga_x_origin); // @TODO STBI_NOTUSED(tga_y_origin); // @TODO if (tga_height > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); if (tga_width > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); // do a tiny bit of precessing if ( tga_image_type >= 8 ) { tga_image_type -= 8; tga_is_RLE = 1; } tga_inverted = 1 - ((tga_inverted >> 5) & 1); // If I'm paletted, then I'll use the number of bits from the palette if ( tga_indexed ) tga_comp = stbi__tga_get_comp(tga_palette_bits, 0, &tga_rgb16); else tga_comp = stbi__tga_get_comp(tga_bits_per_pixel, (tga_image_type == 3), &tga_rgb16); if(!tga_comp) // shouldn't really happen, stbi__tga_test() should have ensured basic consistency return stbi__errpuc("bad format", "Can't find out TGA pixelformat"); // tga info *x = tga_width; *y = tga_height; if (comp) *comp = tga_comp; if (!stbi__mad3sizes_valid(tga_width, tga_height, tga_comp, 0)) return stbi__errpuc("too large", "Corrupt TGA"); tga_data = (unsigned char*)stbi__malloc_mad3(tga_width, tga_height, tga_comp, 0); if (!tga_data) return stbi__errpuc("outofmem", "Out of memory"); // skip to the data's starting position (offset usually = 0) stbi__skip(s, tga_offset ); if ( !tga_indexed && !tga_is_RLE && !tga_rgb16 ) { for (i=0; i < tga_height; ++i) { int row = tga_inverted ? tga_height -i - 1 : i; stbi_uc *tga_row = tga_data + row*tga_width*tga_comp; stbi__getn(s, tga_row, tga_width * tga_comp); } } else { // do I need to load a palette? if ( tga_indexed) { if (tga_palette_len == 0) { /* you have to have at least one entry! */ STBI_FREE(tga_data); return stbi__errpuc("bad palette", "Corrupt TGA"); } // any data to skip? (offset usually = 0) stbi__skip(s, tga_palette_start ); // load the palette tga_palette = (unsigned char*)stbi__malloc_mad2(tga_palette_len, tga_comp, 0); if (!tga_palette) { STBI_FREE(tga_data); return stbi__errpuc("outofmem", "Out of memory"); } if (tga_rgb16) { stbi_uc *pal_entry = tga_palette; STBI_ASSERT(tga_comp == STBI_rgb); for (i=0; i < tga_palette_len; ++i) { stbi__tga_read_rgb16(s, pal_entry); pal_entry += tga_comp; } } else if (!stbi__getn(s, tga_palette, tga_palette_len * tga_comp)) { STBI_FREE(tga_data); STBI_FREE(tga_palette); return stbi__errpuc("bad palette", "Corrupt TGA"); } } // load the data for (i=0; i < tga_width * tga_height; ++i) { // if I'm in RLE mode, do I need to get a RLE stbi__pngchunk? if ( tga_is_RLE ) { if ( RLE_count == 0 ) { // yep, get the next byte as a RLE command int RLE_cmd = stbi__get8(s); RLE_count = 1 + (RLE_cmd & 127); RLE_repeating = RLE_cmd >> 7; read_next_pixel = 1; } else if ( !RLE_repeating ) { read_next_pixel = 1; } } else { read_next_pixel = 1; } // OK, if I need to read a pixel, do it now if ( read_next_pixel ) { // load however much data we did have if ( tga_indexed ) { // read in index, then perform the lookup int pal_idx = (tga_bits_per_pixel == 8) ? stbi__get8(s) : stbi__get16le(s); if ( pal_idx >= tga_palette_len ) { // invalid index pal_idx = 0; } pal_idx *= tga_comp; for (j = 0; j < tga_comp; ++j) { raw_data[j] = tga_palette[pal_idx+j]; } } else if(tga_rgb16) { STBI_ASSERT(tga_comp == STBI_rgb); stbi__tga_read_rgb16(s, raw_data); } else { // read in the data raw for (j = 0; j < tga_comp; ++j) { raw_data[j] = stbi__get8(s); } } // clear the reading flag for the next pixel read_next_pixel = 0; } // end of reading a pixel // copy data for (j = 0; j < tga_comp; ++j) tga_data[i*tga_comp+j] = raw_data[j]; // in case we're in RLE mode, keep counting down --RLE_count; } // do I need to invert the image? if ( tga_inverted ) { for (j = 0; j*2 < tga_height; ++j) { int index1 = j * tga_width * tga_comp; int index2 = (tga_height - 1 - j) * tga_width * tga_comp; for (i = tga_width * tga_comp; i > 0; --i) { unsigned char temp = tga_data[index1]; tga_data[index1] = tga_data[index2]; tga_data[index2] = temp; ++index1; ++index2; } } } // clear my palette, if I had one if ( tga_palette != NULL ) { STBI_FREE( tga_palette ); } } // swap RGB - if the source data was RGB16, it already is in the right order if (tga_comp >= 3 && !tga_rgb16) { unsigned char* tga_pixel = tga_data; for (i=0; i < tga_width * tga_height; ++i) { unsigned char temp = tga_pixel[0]; tga_pixel[0] = tga_pixel[2]; tga_pixel[2] = temp; tga_pixel += tga_comp; } } // convert to target component count if (req_comp && req_comp != tga_comp) tga_data = stbi__convert_format(tga_data, tga_comp, req_comp, tga_width, tga_height); // the things I do to get rid of an error message, and yet keep // Microsoft's C compilers happy... [8^( tga_palette_start = tga_palette_len = tga_palette_bits = tga_x_origin = tga_y_origin = 0; STBI_NOTUSED(tga_palette_start); // OK, done return tga_data; } #endif // ************************************************************************************************* // Photoshop PSD loader -- PD by Thatcher Ulrich, integration by Nicolas Schulz, tweaked by STB #ifndef STBI_NO_PSD static int stbi__psd_test(stbi__context *s) { int r = (stbi__get32be(s) == 0x38425053); stbi__rewind(s); return r; } static int stbi__psd_decode_rle(stbi__context *s, stbi_uc *p, int pixelCount) { int count, nleft, len; count = 0; while ((nleft = pixelCount - count) > 0) { len = stbi__get8(s); if (len == 128) { // No-op. } else if (len < 128) { // Copy next len+1 bytes literally. len++; if (len > nleft) return 0; // corrupt data count += len; while (len) { *p = stbi__get8(s); p += 4; len--; } } else if (len > 128) { stbi_uc val; // Next -len+1 bytes in the dest are replicated from next source byte. // (Interpret len as a negative 8-bit int.) len = 257 - len; if (len > nleft) return 0; // corrupt data val = stbi__get8(s); count += len; while (len) { *p = val; p += 4; len--; } } } return 1; } static void *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int bpc) { int pixelCount; int channelCount, compression; int channel, i; int bitdepth; int w,h; stbi_uc *out; STBI_NOTUSED(ri); // Check identifier if (stbi__get32be(s) != 0x38425053) // "8BPS" return stbi__errpuc("not PSD", "Corrupt PSD image"); // Check file type version. if (stbi__get16be(s) != 1) return stbi__errpuc("wrong version", "Unsupported version of PSD image"); // Skip 6 reserved bytes. stbi__skip(s, 6 ); // Read the number of channels (R, G, B, A, etc). channelCount = stbi__get16be(s); if (channelCount < 0 || channelCount > 16) return stbi__errpuc("wrong channel count", "Unsupported number of channels in PSD image"); // Read the rows and columns of the image. h = stbi__get32be(s); w = stbi__get32be(s); if (h > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); if (w > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); // Make sure the depth is 8 bits. bitdepth = stbi__get16be(s); if (bitdepth != 8 && bitdepth != 16) return stbi__errpuc("unsupported bit depth", "PSD bit depth is not 8 or 16 bit"); // Make sure the color mode is RGB. // Valid options are: // 0: Bitmap // 1: Grayscale // 2: Indexed color // 3: RGB color // 4: CMYK color // 7: Multichannel // 8: Duotone // 9: Lab color if (stbi__get16be(s) != 3) return stbi__errpuc("wrong color format", "PSD is not in RGB color format"); // Skip the Mode Data. (It's the palette for indexed color; other info for other modes.) stbi__skip(s,stbi__get32be(s) ); // Skip the image resources. (resolution, pen tool paths, etc) stbi__skip(s, stbi__get32be(s) ); // Skip the reserved data. stbi__skip(s, stbi__get32be(s) ); // Find out if the data is compressed. // Known values: // 0: no compression // 1: RLE compressed compression = stbi__get16be(s); if (compression > 1) return stbi__errpuc("bad compression", "PSD has an unknown compression format"); // Check size if (!stbi__mad3sizes_valid(4, w, h, 0)) return stbi__errpuc("too large", "Corrupt PSD"); // Create the destination image. if (!compression && bitdepth == 16 && bpc == 16) { out = (stbi_uc *) stbi__malloc_mad3(8, w, h, 0); ri->bits_per_channel = 16; } else out = (stbi_uc *) stbi__malloc(4 * w*h); if (!out) return stbi__errpuc("outofmem", "Out of memory"); pixelCount = w*h; // Initialize the data to zero. //memset( out, 0, pixelCount * 4 ); // Finally, the image data. if (compression) { // RLE as used by .PSD and .TIFF // Loop until you get the number of unpacked bytes you are expecting: // Read the next source byte into n. // If n is between 0 and 127 inclusive, copy the next n+1 bytes literally. // Else if n is between -127 and -1 inclusive, copy the next byte -n+1 times. // Else if n is 128, noop. // Endloop // The RLE-compressed data is preceded by a 2-byte data count for each row in the data, // which we're going to just skip. stbi__skip(s, h * channelCount * 2 ); // Read the RLE data by channel. for (channel = 0; channel < 4; channel++) { stbi_uc *p; p = out+channel; if (channel >= channelCount) { // Fill this channel with default data. for (i = 0; i < pixelCount; i++, p += 4) *p = (channel == 3 ? 255 : 0); } else { // Read the RLE data. if (!stbi__psd_decode_rle(s, p, pixelCount)) { STBI_FREE(out); return stbi__errpuc("corrupt", "bad RLE data"); } } } } else { // We're at the raw image data. It's each channel in order (Red, Green, Blue, Alpha, ...) // where each channel consists of an 8-bit (or 16-bit) value for each pixel in the image. // Read the data by channel. for (channel = 0; channel < 4; channel++) { if (channel >= channelCount) { // Fill this channel with default data. if (bitdepth == 16 && bpc == 16) { stbi__uint16 *q = ((stbi__uint16 *) out) + channel; stbi__uint16 val = channel == 3 ? 65535 : 0; for (i = 0; i < pixelCount; i++, q += 4) *q = val; } else { stbi_uc *p = out+channel; stbi_uc val = channel == 3 ? 255 : 0; for (i = 0; i < pixelCount; i++, p += 4) *p = val; } } else { if (ri->bits_per_channel == 16) { // output bpc stbi__uint16 *q = ((stbi__uint16 *) out) + channel; for (i = 0; i < pixelCount; i++, q += 4) *q = (stbi__uint16) stbi__get16be(s); } else { stbi_uc *p = out+channel; if (bitdepth == 16) { // input bpc for (i = 0; i < pixelCount; i++, p += 4) *p = (stbi_uc) (stbi__get16be(s) >> 8); } else { for (i = 0; i < pixelCount; i++, p += 4) *p = stbi__get8(s); } } } } } // remove weird white matte from PSD if (channelCount >= 4) { if (ri->bits_per_channel == 16) { for (i=0; i < w*h; ++i) { stbi__uint16 *pixel = (stbi__uint16 *) out + 4*i; if (pixel[3] != 0 && pixel[3] != 65535) { float a = pixel[3] / 65535.0f; float ra = 1.0f / a; float inv_a = 65535.0f * (1 - ra); pixel[0] = (stbi__uint16) (pixel[0]*ra + inv_a); pixel[1] = (stbi__uint16) (pixel[1]*ra + inv_a); pixel[2] = (stbi__uint16) (pixel[2]*ra + inv_a); } } } else { for (i=0; i < w*h; ++i) { unsigned char *pixel = out + 4*i; if (pixel[3] != 0 && pixel[3] != 255) { float a = pixel[3] / 255.0f; float ra = 1.0f / a; float inv_a = 255.0f * (1 - ra); pixel[0] = (unsigned char) (pixel[0]*ra + inv_a); pixel[1] = (unsigned char) (pixel[1]*ra + inv_a); pixel[2] = (unsigned char) (pixel[2]*ra + inv_a); } } } } // convert to desired output format if (req_comp && req_comp != 4) { if (ri->bits_per_channel == 16) out = (stbi_uc *) stbi__convert_format16((stbi__uint16 *) out, 4, req_comp, w, h); else out = stbi__convert_format(out, 4, req_comp, w, h); if (out == NULL) return out; // stbi__convert_format frees input on failure } if (comp) *comp = 4; *y = h; *x = w; return out; } #endif // ************************************************************************************************* // Softimage PIC loader // by Tom Seddon // // See http://softimage.wiki.softimage.com/index.php/INFO:_PIC_file_format // See http://ozviz.wasp.uwa.edu.au/~pbourke/dataformats/softimagepic/ #ifndef STBI_NO_PIC static int stbi__pic_is4(stbi__context *s,const char *str) { int i; for (i=0; i<4; ++i) if (stbi__get8(s) != (stbi_uc)str[i]) return 0; return 1; } static int stbi__pic_test_core(stbi__context *s) { int i; if (!stbi__pic_is4(s,"\x53\x80\xF6\x34")) return 0; for(i=0;i<84;++i) stbi__get8(s); if (!stbi__pic_is4(s,"PICT")) return 0; return 1; } typedef struct { stbi_uc size,type,channel; } stbi__pic_packet; static stbi_uc *stbi__readval(stbi__context *s, int channel, stbi_uc *dest) { int mask=0x80, i; for (i=0; i<4; ++i, mask>>=1) { if (channel & mask) { if (stbi__at_eof(s)) return stbi__errpuc("bad file","PIC file too short"); dest[i]=stbi__get8(s); } } return dest; } static void stbi__copyval(int channel,stbi_uc *dest,const stbi_uc *src) { int mask=0x80,i; for (i=0;i<4; ++i, mask>>=1) if (channel&mask) dest[i]=src[i]; } static stbi_uc *stbi__pic_load_core(stbi__context *s,int width,int height,int *comp, stbi_uc *result) { int act_comp=0,num_packets=0,y,chained; stbi__pic_packet packets[10]; // this will (should...) cater for even some bizarre stuff like having data // for the same channel in multiple packets. do { stbi__pic_packet *packet; if (num_packets==sizeof(packets)/sizeof(packets[0])) return stbi__errpuc("bad format","too many packets"); packet = &packets[num_packets++]; chained = stbi__get8(s); packet->size = stbi__get8(s); packet->type = stbi__get8(s); packet->channel = stbi__get8(s); act_comp |= packet->channel; if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (reading packets)"); if (packet->size != 8) return stbi__errpuc("bad format","packet isn't 8bpp"); } while (chained); *comp = (act_comp & 0x10 ? 4 : 3); // has alpha channel? for(y=0; ytype) { default: return stbi__errpuc("bad format","packet has bad compression type"); case 0: {//uncompressed int x; for(x=0;xchannel,dest)) return 0; break; } case 1://Pure RLE { int left=width, i; while (left>0) { stbi_uc count,value[4]; count=stbi__get8(s); if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (pure read count)"); if (count > left) count = (stbi_uc) left; if (!stbi__readval(s,packet->channel,value)) return 0; for(i=0; ichannel,dest,value); left -= count; } } break; case 2: {//Mixed RLE int left=width; while (left>0) { int count = stbi__get8(s), i; if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (mixed read count)"); if (count >= 128) { // Repeated stbi_uc value[4]; if (count==128) count = stbi__get16be(s); else count -= 127; if (count > left) return stbi__errpuc("bad file","scanline overrun"); if (!stbi__readval(s,packet->channel,value)) return 0; for(i=0;ichannel,dest,value); } else { // Raw ++count; if (count>left) return stbi__errpuc("bad file","scanline overrun"); for(i=0;ichannel,dest)) return 0; } left-=count; } break; } } } } return result; } static void *stbi__pic_load(stbi__context *s,int *px,int *py,int *comp,int req_comp, stbi__result_info *ri) { stbi_uc *result; int i, x,y, internal_comp; STBI_NOTUSED(ri); if (!comp) comp = &internal_comp; for (i=0; i<92; ++i) stbi__get8(s); x = stbi__get16be(s); y = stbi__get16be(s); if (y > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); if (x > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (pic header)"); if (!stbi__mad3sizes_valid(x, y, 4, 0)) return stbi__errpuc("too large", "PIC image too large to decode"); stbi__get32be(s); //skip `ratio' stbi__get16be(s); //skip `fields' stbi__get16be(s); //skip `pad' // intermediate buffer is RGBA result = (stbi_uc *) stbi__malloc_mad3(x, y, 4, 0); memset(result, 0xff, x*y*4); if (!stbi__pic_load_core(s,x,y,comp, result)) { STBI_FREE(result); result=0; } *px = x; *py = y; if (req_comp == 0) req_comp = *comp; result=stbi__convert_format(result,4,req_comp,x,y); return result; } static int stbi__pic_test(stbi__context *s) { int r = stbi__pic_test_core(s); stbi__rewind(s); return r; } #endif // ************************************************************************************************* // GIF loader -- public domain by Jean-Marc Lienher -- simplified/shrunk by stb #ifndef STBI_NO_GIF typedef struct { stbi__int16 prefix; stbi_uc first; stbi_uc suffix; } stbi__gif_lzw; typedef struct { int w,h; stbi_uc *out; // output buffer (always 4 components) stbi_uc *background; // The current "background" as far as a gif is concerned stbi_uc *history; int flags, bgindex, ratio, transparent, eflags; stbi_uc pal[256][4]; stbi_uc lpal[256][4]; stbi__gif_lzw codes[8192]; stbi_uc *color_table; int parse, step; int lflags; int start_x, start_y; int max_x, max_y; int cur_x, cur_y; int line_size; int delay; } stbi__gif; static int stbi__gif_test_raw(stbi__context *s) { int sz; if (stbi__get8(s) != 'G' || stbi__get8(s) != 'I' || stbi__get8(s) != 'F' || stbi__get8(s) != '8') return 0; sz = stbi__get8(s); if (sz != '9' && sz != '7') return 0; if (stbi__get8(s) != 'a') return 0; return 1; } static int stbi__gif_test(stbi__context *s) { int r = stbi__gif_test_raw(s); stbi__rewind(s); return r; } static void stbi__gif_parse_colortable(stbi__context *s, stbi_uc pal[256][4], int num_entries, int transp) { int i; for (i=0; i < num_entries; ++i) { pal[i][2] = stbi__get8(s); pal[i][1] = stbi__get8(s); pal[i][0] = stbi__get8(s); pal[i][3] = transp == i ? 0 : 255; } } static int stbi__gif_header(stbi__context *s, stbi__gif *g, int *comp, int is_info) { stbi_uc version; if (stbi__get8(s) != 'G' || stbi__get8(s) != 'I' || stbi__get8(s) != 'F' || stbi__get8(s) != '8') return stbi__err("not GIF", "Corrupt GIF"); version = stbi__get8(s); if (version != '7' && version != '9') return stbi__err("not GIF", "Corrupt GIF"); if (stbi__get8(s) != 'a') return stbi__err("not GIF", "Corrupt GIF"); stbi__g_failure_reason = ""; g->w = stbi__get16le(s); g->h = stbi__get16le(s); g->flags = stbi__get8(s); g->bgindex = stbi__get8(s); g->ratio = stbi__get8(s); g->transparent = -1; if (g->w > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); if (g->h > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); if (comp != 0) *comp = 4; // can't actually tell whether it's 3 or 4 until we parse the comments if (is_info) return 1; if (g->flags & 0x80) stbi__gif_parse_colortable(s,g->pal, 2 << (g->flags & 7), -1); return 1; } static int stbi__gif_info_raw(stbi__context *s, int *x, int *y, int *comp) { stbi__gif* g = (stbi__gif*) stbi__malloc(sizeof(stbi__gif)); if (!stbi__gif_header(s, g, comp, 1)) { STBI_FREE(g); stbi__rewind( s ); return 0; } if (x) *x = g->w; if (y) *y = g->h; STBI_FREE(g); return 1; } static void stbi__out_gif_code(stbi__gif *g, stbi__uint16 code) { stbi_uc *p, *c; int idx; // recurse to decode the prefixes, since the linked-list is backwards, // and working backwards through an interleaved image would be nasty if (g->codes[code].prefix >= 0) stbi__out_gif_code(g, g->codes[code].prefix); if (g->cur_y >= g->max_y) return; idx = g->cur_x + g->cur_y; p = &g->out[idx]; g->history[idx / 4] = 1; c = &g->color_table[g->codes[code].suffix * 4]; if (c[3] > 128) { // don't render transparent pixels; p[0] = c[2]; p[1] = c[1]; p[2] = c[0]; p[3] = c[3]; } g->cur_x += 4; if (g->cur_x >= g->max_x) { g->cur_x = g->start_x; g->cur_y += g->step; while (g->cur_y >= g->max_y && g->parse > 0) { g->step = (1 << g->parse) * g->line_size; g->cur_y = g->start_y + (g->step >> 1); --g->parse; } } } static stbi_uc *stbi__process_gif_raster(stbi__context *s, stbi__gif *g) { stbi_uc lzw_cs; stbi__int32 len, init_code; stbi__uint32 first; stbi__int32 codesize, codemask, avail, oldcode, bits, valid_bits, clear; stbi__gif_lzw *p; lzw_cs = stbi__get8(s); if (lzw_cs > 12) return NULL; clear = 1 << lzw_cs; first = 1; codesize = lzw_cs + 1; codemask = (1 << codesize) - 1; bits = 0; valid_bits = 0; for (init_code = 0; init_code < clear; init_code++) { g->codes[init_code].prefix = -1; g->codes[init_code].first = (stbi_uc) init_code; g->codes[init_code].suffix = (stbi_uc) init_code; } // support no starting clear code avail = clear+2; oldcode = -1; len = 0; for(;;) { if (valid_bits < codesize) { if (len == 0) { len = stbi__get8(s); // start new block if (len == 0) return g->out; } --len; bits |= (stbi__int32) stbi__get8(s) << valid_bits; valid_bits += 8; } else { stbi__int32 code = bits & codemask; bits >>= codesize; valid_bits -= codesize; // @OPTIMIZE: is there some way we can accelerate the non-clear path? if (code == clear) { // clear code codesize = lzw_cs + 1; codemask = (1 << codesize) - 1; avail = clear + 2; oldcode = -1; first = 0; } else if (code == clear + 1) { // end of stream code stbi__skip(s, len); while ((len = stbi__get8(s)) > 0) stbi__skip(s,len); return g->out; } else if (code <= avail) { if (first) { return stbi__errpuc("no clear code", "Corrupt GIF"); } if (oldcode >= 0) { p = &g->codes[avail++]; if (avail > 8192) { return stbi__errpuc("too many codes", "Corrupt GIF"); } p->prefix = (stbi__int16) oldcode; p->first = g->codes[oldcode].first; p->suffix = (code == avail) ? p->first : g->codes[code].first; } else if (code == avail) return stbi__errpuc("illegal code in raster", "Corrupt GIF"); stbi__out_gif_code(g, (stbi__uint16) code); if ((avail & codemask) == 0 && avail <= 0x0FFF) { codesize++; codemask = (1 << codesize) - 1; } oldcode = code; } else { return stbi__errpuc("illegal code in raster", "Corrupt GIF"); } } } } // this function is designed to support animated gifs, although stb_image doesn't support it // two back is the image from two frames ago, used for a very specific disposal format static stbi_uc *stbi__gif_load_next(stbi__context *s, stbi__gif *g, int *comp, int req_comp, stbi_uc *two_back) { int dispose; int first_frame; int pi; int pcount; STBI_NOTUSED(req_comp); // on first frame, any non-written pixels get the background colour (non-transparent) first_frame = 0; if (g->out == 0) { if (!stbi__gif_header(s, g, comp,0)) return 0; // stbi__g_failure_reason set by stbi__gif_header if (!stbi__mad3sizes_valid(4, g->w, g->h, 0)) return stbi__errpuc("too large", "GIF image is too large"); pcount = g->w * g->h; g->out = (stbi_uc *) stbi__malloc(4 * pcount); g->background = (stbi_uc *) stbi__malloc(4 * pcount); g->history = (stbi_uc *) stbi__malloc(pcount); if (!g->out || !g->background || !g->history) return stbi__errpuc("outofmem", "Out of memory"); // image is treated as "transparent" at the start - ie, nothing overwrites the current background; // background colour is only used for pixels that are not rendered first frame, after that "background" // color refers to the color that was there the previous frame. memset(g->out, 0x00, 4 * pcount); memset(g->background, 0x00, 4 * pcount); // state of the background (starts transparent) memset(g->history, 0x00, pcount); // pixels that were affected previous frame first_frame = 1; } else { // second frame - how do we dispose of the previous one? dispose = (g->eflags & 0x1C) >> 2; pcount = g->w * g->h; if ((dispose == 3) && (two_back == 0)) { dispose = 2; // if I don't have an image to revert back to, default to the old background } if (dispose == 3) { // use previous graphic for (pi = 0; pi < pcount; ++pi) { if (g->history[pi]) { memcpy( &g->out[pi * 4], &two_back[pi * 4], 4 ); } } } else if (dispose == 2) { // restore what was changed last frame to background before that frame; for (pi = 0; pi < pcount; ++pi) { if (g->history[pi]) { memcpy( &g->out[pi * 4], &g->background[pi * 4], 4 ); } } } else { // This is a non-disposal case eithe way, so just // leave the pixels as is, and they will become the new background // 1: do not dispose // 0: not specified. } // background is what out is after the undoing of the previou frame; memcpy( g->background, g->out, 4 * g->w * g->h ); } // clear my history; memset( g->history, 0x00, g->w * g->h ); // pixels that were affected previous frame for (;;) { int tag = stbi__get8(s); switch (tag) { case 0x2C: /* Image Descriptor */ { stbi__int32 x, y, w, h; stbi_uc *o; x = stbi__get16le(s); y = stbi__get16le(s); w = stbi__get16le(s); h = stbi__get16le(s); if (((x + w) > (g->w)) || ((y + h) > (g->h))) return stbi__errpuc("bad Image Descriptor", "Corrupt GIF"); g->line_size = g->w * 4; g->start_x = x * 4; g->start_y = y * g->line_size; g->max_x = g->start_x + w * 4; g->max_y = g->start_y + h * g->line_size; g->cur_x = g->start_x; g->cur_y = g->start_y; // if the width of the specified rectangle is 0, that means // we may not see *any* pixels or the image is malformed; // to make sure this is caught, move the current y down to // max_y (which is what out_gif_code checks). if (w == 0) g->cur_y = g->max_y; g->lflags = stbi__get8(s); if (g->lflags & 0x40) { g->step = 8 * g->line_size; // first interlaced spacing g->parse = 3; } else { g->step = g->line_size; g->parse = 0; } if (g->lflags & 0x80) { stbi__gif_parse_colortable(s,g->lpal, 2 << (g->lflags & 7), g->eflags & 0x01 ? g->transparent : -1); g->color_table = (stbi_uc *) g->lpal; } else if (g->flags & 0x80) { g->color_table = (stbi_uc *) g->pal; } else return stbi__errpuc("missing color table", "Corrupt GIF"); o = stbi__process_gif_raster(s, g); if (!o) return NULL; // if this was the first frame, pcount = g->w * g->h; if (first_frame && (g->bgindex > 0)) { // if first frame, any pixel not drawn to gets the background color for (pi = 0; pi < pcount; ++pi) { if (g->history[pi] == 0) { g->pal[g->bgindex][3] = 255; // just in case it was made transparent, undo that; It will be reset next frame if need be; memcpy( &g->out[pi * 4], &g->pal[g->bgindex], 4 ); } } } return o; } case 0x21: // Comment Extension. { int len; int ext = stbi__get8(s); if (ext == 0xF9) { // Graphic Control Extension. len = stbi__get8(s); if (len == 4) { g->eflags = stbi__get8(s); g->delay = 10 * stbi__get16le(s); // delay - 1/100th of a second, saving as 1/1000ths. // unset old transparent if (g->transparent >= 0) { g->pal[g->transparent][3] = 255; } if (g->eflags & 0x01) { g->transparent = stbi__get8(s); if (g->transparent >= 0) { g->pal[g->transparent][3] = 0; } } else { // don't need transparent stbi__skip(s, 1); g->transparent = -1; } } else { stbi__skip(s, len); break; } } while ((len = stbi__get8(s)) != 0) { stbi__skip(s, len); } break; } case 0x3B: // gif stream termination code return (stbi_uc *) s; // using '1' causes warning on some compilers default: return stbi__errpuc("unknown code", "Corrupt GIF"); } } } static void *stbi__load_gif_main(stbi__context *s, int **delays, int *x, int *y, int *z, int *comp, int req_comp) { if (stbi__gif_test(s)) { int layers = 0; stbi_uc *u = 0; stbi_uc *out = 0; stbi_uc *two_back = 0; stbi__gif g; int stride; int out_size = 0; int delays_size = 0; memset(&g, 0, sizeof(g)); if (delays) { *delays = 0; } do { u = stbi__gif_load_next(s, &g, comp, req_comp, two_back); if (u == (stbi_uc *) s) u = 0; // end of animated gif marker if (u) { *x = g.w; *y = g.h; ++layers; stride = g.w * g.h * 4; if (out) { void *tmp = (stbi_uc*) STBI_REALLOC_SIZED( out, out_size, layers * stride ); if (NULL == tmp) { STBI_FREE(g.out); STBI_FREE(g.history); STBI_FREE(g.background); return stbi__errpuc("outofmem", "Out of memory"); } else { out = (stbi_uc*) tmp; out_size = layers * stride; } if (delays) { *delays = (int*) STBI_REALLOC_SIZED( *delays, delays_size, sizeof(int) * layers ); delays_size = layers * sizeof(int); } } else { out = (stbi_uc*)stbi__malloc( layers * stride ); out_size = layers * stride; if (delays) { *delays = (int*) stbi__malloc( layers * sizeof(int) ); delays_size = layers * sizeof(int); } } memcpy( out + ((layers - 1) * stride), u, stride ); if (layers >= 2) { two_back = out - 2 * stride; } if (delays) { (*delays)[layers - 1U] = g.delay; } } } while (u != 0); // free temp buffer; STBI_FREE(g.out); STBI_FREE(g.history); STBI_FREE(g.background); // do the final conversion after loading everything; if (req_comp && req_comp != 4) out = stbi__convert_format(out, 4, req_comp, layers * g.w, g.h); *z = layers; return out; } else { return stbi__errpuc("not GIF", "Image was not as a gif type."); } } static void *stbi__gif_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) { stbi_uc *u = 0; stbi__gif g; memset(&g, 0, sizeof(g)); STBI_NOTUSED(ri); u = stbi__gif_load_next(s, &g, comp, req_comp, 0); if (u == (stbi_uc *) s) u = 0; // end of animated gif marker if (u) { *x = g.w; *y = g.h; // moved conversion to after successful load so that the same // can be done for multiple frames. if (req_comp && req_comp != 4) u = stbi__convert_format(u, 4, req_comp, g.w, g.h); } else if (g.out) { // if there was an error and we allocated an image buffer, free it! STBI_FREE(g.out); } // free buffers needed for multiple frame loading; STBI_FREE(g.history); STBI_FREE(g.background); return u; } static int stbi__gif_info(stbi__context *s, int *x, int *y, int *comp) { return stbi__gif_info_raw(s,x,y,comp); } #endif // ************************************************************************************************* // Radiance RGBE HDR loader // originally by Nicolas Schulz #ifndef STBI_NO_HDR static int stbi__hdr_test_core(stbi__context *s, const char *signature) { int i; for (i=0; signature[i]; ++i) if (stbi__get8(s) != signature[i]) return 0; stbi__rewind(s); return 1; } static int stbi__hdr_test(stbi__context* s) { int r = stbi__hdr_test_core(s, "#?RADIANCE\n"); stbi__rewind(s); if(!r) { r = stbi__hdr_test_core(s, "#?RGBE\n"); stbi__rewind(s); } return r; } #define STBI__HDR_BUFLEN 1024 static char *stbi__hdr_gettoken(stbi__context *z, char *buffer) { int len=0; char c = '\0'; c = (char) stbi__get8(z); while (!stbi__at_eof(z) && c != '\n') { buffer[len++] = c; if (len == STBI__HDR_BUFLEN-1) { // flush to end of line while (!stbi__at_eof(z) && stbi__get8(z) != '\n') ; break; } c = (char) stbi__get8(z); } buffer[len] = 0; return buffer; } static void stbi__hdr_convert(float *output, stbi_uc *input, int req_comp) { if ( input[3] != 0 ) { float f1; // Exponent f1 = (float) ldexp(1.0f, input[3] - (int)(128 + 8)); if (req_comp <= 2) output[0] = (input[0] + input[1] + input[2]) * f1 / 3; else { output[0] = input[0] * f1; output[1] = input[1] * f1; output[2] = input[2] * f1; } if (req_comp == 2) output[1] = 1; if (req_comp == 4) output[3] = 1; } else { switch (req_comp) { case 4: output[3] = 1; /* fallthrough */ case 3: output[0] = output[1] = output[2] = 0; break; case 2: output[1] = 1; /* fallthrough */ case 1: output[0] = 0; break; } } } static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) { char buffer[STBI__HDR_BUFLEN]; char *token; int valid = 0; int width, height; stbi_uc *scanline; float *hdr_data; int len; unsigned char count, value; int i, j, k, c1,c2, z; const char *headerToken; STBI_NOTUSED(ri); // Check identifier headerToken = stbi__hdr_gettoken(s,buffer); if (strcmp(headerToken, "#?RADIANCE") != 0 && strcmp(headerToken, "#?RGBE") != 0) return stbi__errpf("not HDR", "Corrupt HDR image"); // Parse header for(;;) { token = stbi__hdr_gettoken(s,buffer); if (token[0] == 0) break; if (strcmp(token, "FORMAT=32-bit_rle_rgbe") == 0) valid = 1; } if (!valid) return stbi__errpf("unsupported format", "Unsupported HDR format"); // Parse width and height // can't use sscanf() if we're not using stdio! token = stbi__hdr_gettoken(s,buffer); if (strncmp(token, "-Y ", 3)) return stbi__errpf("unsupported data layout", "Unsupported HDR format"); token += 3; height = (int) strtol(token, &token, 10); while (*token == ' ') ++token; if (strncmp(token, "+X ", 3)) return stbi__errpf("unsupported data layout", "Unsupported HDR format"); token += 3; width = (int) strtol(token, NULL, 10); if (height > STBI_MAX_DIMENSIONS) return stbi__errpf("too large","Very large image (corrupt?)"); if (width > STBI_MAX_DIMENSIONS) return stbi__errpf("too large","Very large image (corrupt?)"); *x = width; *y = height; if (comp) *comp = 3; if (req_comp == 0) req_comp = 3; if (!stbi__mad4sizes_valid(width, height, req_comp, sizeof(float), 0)) return stbi__errpf("too large", "HDR image is too large"); // Read data hdr_data = (float *) stbi__malloc_mad4(width, height, req_comp, sizeof(float), 0); if (!hdr_data) return stbi__errpf("outofmem", "Out of memory"); // Load image data // image data is stored as some number of sca if ( width < 8 || width >= 32768) { // Read flat data for (j=0; j < height; ++j) { for (i=0; i < width; ++i) { stbi_uc rgbe[4]; main_decode_loop: stbi__getn(s, rgbe, 4); stbi__hdr_convert(hdr_data + j * width * req_comp + i * req_comp, rgbe, req_comp); } } } else { // Read RLE-encoded data scanline = NULL; for (j = 0; j < height; ++j) { c1 = stbi__get8(s); c2 = stbi__get8(s); len = stbi__get8(s); if (c1 != 2 || c2 != 2 || (len & 0x80)) { // not run-length encoded, so we have to actually use THIS data as a decoded // pixel (note this can't be a valid pixel--one of RGB must be >= 128) stbi_uc rgbe[4]; rgbe[0] = (stbi_uc) c1; rgbe[1] = (stbi_uc) c2; rgbe[2] = (stbi_uc) len; rgbe[3] = (stbi_uc) stbi__get8(s); stbi__hdr_convert(hdr_data, rgbe, req_comp); i = 1; j = 0; STBI_FREE(scanline); goto main_decode_loop; // yes, this makes no sense } len <<= 8; len |= stbi__get8(s); if (len != width) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("invalid decoded scanline length", "corrupt HDR"); } if (scanline == NULL) { scanline = (stbi_uc *) stbi__malloc_mad2(width, 4, 0); if (!scanline) { STBI_FREE(hdr_data); return stbi__errpf("outofmem", "Out of memory"); } } for (k = 0; k < 4; ++k) { int nleft; i = 0; while ((nleft = width - i) > 0) { count = stbi__get8(s); if (count > 128) { // Run value = stbi__get8(s); count -= 128; if (count > nleft) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("corrupt", "bad RLE data in HDR"); } for (z = 0; z < count; ++z) scanline[i++ * 4 + k] = value; } else { // Dump if (count > nleft) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("corrupt", "bad RLE data in HDR"); } for (z = 0; z < count; ++z) scanline[i++ * 4 + k] = stbi__get8(s); } } } for (i=0; i < width; ++i) stbi__hdr_convert(hdr_data+(j*width + i)*req_comp, scanline + i*4, req_comp); } if (scanline) STBI_FREE(scanline); } return hdr_data; } static int stbi__hdr_info(stbi__context *s, int *x, int *y, int *comp) { char buffer[STBI__HDR_BUFLEN]; char *token; int valid = 0; int dummy; if (!x) x = &dummy; if (!y) y = &dummy; if (!comp) comp = &dummy; if (stbi__hdr_test(s) == 0) { stbi__rewind( s ); return 0; } for(;;) { token = stbi__hdr_gettoken(s,buffer); if (token[0] == 0) break; if (strcmp(token, "FORMAT=32-bit_rle_rgbe") == 0) valid = 1; } if (!valid) { stbi__rewind( s ); return 0; } token = stbi__hdr_gettoken(s,buffer); if (strncmp(token, "-Y ", 3)) { stbi__rewind( s ); return 0; } token += 3; *y = (int) strtol(token, &token, 10); while (*token == ' ') ++token; if (strncmp(token, "+X ", 3)) { stbi__rewind( s ); return 0; } token += 3; *x = (int) strtol(token, NULL, 10); *comp = 3; return 1; } #endif // STBI_NO_HDR #ifndef STBI_NO_BMP static int stbi__bmp_info(stbi__context *s, int *x, int *y, int *comp) { void *p; stbi__bmp_data info; info.all_a = 255; p = stbi__bmp_parse_header(s, &info); stbi__rewind( s ); if (p == NULL) return 0; if (x) *x = s->img_x; if (y) *y = s->img_y; if (comp) { if (info.bpp == 24 && info.ma == 0xff000000) *comp = 3; else *comp = info.ma ? 4 : 3; } return 1; } #endif #ifndef STBI_NO_PSD static int stbi__psd_info(stbi__context *s, int *x, int *y, int *comp) { int channelCount, dummy, depth; if (!x) x = &dummy; if (!y) y = &dummy; if (!comp) comp = &dummy; if (stbi__get32be(s) != 0x38425053) { stbi__rewind( s ); return 0; } if (stbi__get16be(s) != 1) { stbi__rewind( s ); return 0; } stbi__skip(s, 6); channelCount = stbi__get16be(s); if (channelCount < 0 || channelCount > 16) { stbi__rewind( s ); return 0; } *y = stbi__get32be(s); *x = stbi__get32be(s); depth = stbi__get16be(s); if (depth != 8 && depth != 16) { stbi__rewind( s ); return 0; } if (stbi__get16be(s) != 3) { stbi__rewind( s ); return 0; } *comp = 4; return 1; } static int stbi__psd_is16(stbi__context *s) { int channelCount, depth; if (stbi__get32be(s) != 0x38425053) { stbi__rewind( s ); return 0; } if (stbi__get16be(s) != 1) { stbi__rewind( s ); return 0; } stbi__skip(s, 6); channelCount = stbi__get16be(s); if (channelCount < 0 || channelCount > 16) { stbi__rewind( s ); return 0; } (void) stbi__get32be(s); (void) stbi__get32be(s); depth = stbi__get16be(s); if (depth != 16) { stbi__rewind( s ); return 0; } return 1; } #endif #ifndef STBI_NO_PIC static int stbi__pic_info(stbi__context *s, int *x, int *y, int *comp) { int act_comp=0,num_packets=0,chained,dummy; stbi__pic_packet packets[10]; if (!x) x = &dummy; if (!y) y = &dummy; if (!comp) comp = &dummy; if (!stbi__pic_is4(s,"\x53\x80\xF6\x34")) { stbi__rewind(s); return 0; } stbi__skip(s, 88); *x = stbi__get16be(s); *y = stbi__get16be(s); if (stbi__at_eof(s)) { stbi__rewind( s); return 0; } if ( (*x) != 0 && (1 << 28) / (*x) < (*y)) { stbi__rewind( s ); return 0; } stbi__skip(s, 8); do { stbi__pic_packet *packet; if (num_packets==sizeof(packets)/sizeof(packets[0])) return 0; packet = &packets[num_packets++]; chained = stbi__get8(s); packet->size = stbi__get8(s); packet->type = stbi__get8(s); packet->channel = stbi__get8(s); act_comp |= packet->channel; if (stbi__at_eof(s)) { stbi__rewind( s ); return 0; } if (packet->size != 8) { stbi__rewind( s ); return 0; } } while (chained); *comp = (act_comp & 0x10 ? 4 : 3); return 1; } #endif // ************************************************************************************************* // Portable Gray Map and Portable Pixel Map loader // by Ken Miller // // PGM: http://netpbm.sourceforge.net/doc/pgm.html // PPM: http://netpbm.sourceforge.net/doc/ppm.html // // Known limitations: // Does not support comments in the header section // Does not support ASCII image data (formats P2 and P3) // Does not support 16-bit-per-channel #ifndef STBI_NO_PNM static int stbi__pnm_test(stbi__context *s) { char p, t; p = (char) stbi__get8(s); t = (char) stbi__get8(s); if (p != 'P' || (t != '5' && t != '6')) { stbi__rewind( s ); return 0; } return 1; } static void *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) { stbi_uc *out; STBI_NOTUSED(ri); if (!stbi__pnm_info(s, (int *)&s->img_x, (int *)&s->img_y, (int *)&s->img_n)) return 0; if (s->img_y > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); if (s->img_x > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); *x = s->img_x; *y = s->img_y; if (comp) *comp = s->img_n; if (!stbi__mad3sizes_valid(s->img_n, s->img_x, s->img_y, 0)) return stbi__errpuc("too large", "PNM too large"); out = (stbi_uc *) stbi__malloc_mad3(s->img_n, s->img_x, s->img_y, 0); if (!out) return stbi__errpuc("outofmem", "Out of memory"); stbi__getn(s, out, s->img_n * s->img_x * s->img_y); if (req_comp && req_comp != s->img_n) { out = stbi__convert_format(out, s->img_n, req_comp, s->img_x, s->img_y); if (out == NULL) return out; // stbi__convert_format frees input on failure } return out; } static int stbi__pnm_isspace(char c) { return c == ' ' || c == '\t' || c == '\n' || c == '\v' || c == '\f' || c == '\r'; } static void stbi__pnm_skip_whitespace(stbi__context *s, char *c) { for (;;) { while (!stbi__at_eof(s) && stbi__pnm_isspace(*c)) *c = (char) stbi__get8(s); if (stbi__at_eof(s) || *c != '#') break; while (!stbi__at_eof(s) && *c != '\n' && *c != '\r' ) *c = (char) stbi__get8(s); } } static int stbi__pnm_isdigit(char c) { return c >= '0' && c <= '9'; } static int stbi__pnm_getinteger(stbi__context *s, char *c) { int value = 0; while (!stbi__at_eof(s) && stbi__pnm_isdigit(*c)) { value = value*10 + (*c - '0'); *c = (char) stbi__get8(s); } return value; } static int stbi__pnm_info(stbi__context *s, int *x, int *y, int *comp) { int maxv, dummy; char c, p, t; if (!x) x = &dummy; if (!y) y = &dummy; if (!comp) comp = &dummy; stbi__rewind(s); // Get identifier p = (char) stbi__get8(s); t = (char) stbi__get8(s); if (p != 'P' || (t != '5' && t != '6')) { stbi__rewind(s); return 0; } *comp = (t == '6') ? 3 : 1; // '5' is 1-component .pgm; '6' is 3-component .ppm c = (char) stbi__get8(s); stbi__pnm_skip_whitespace(s, &c); *x = stbi__pnm_getinteger(s, &c); // read width stbi__pnm_skip_whitespace(s, &c); *y = stbi__pnm_getinteger(s, &c); // read height stbi__pnm_skip_whitespace(s, &c); maxv = stbi__pnm_getinteger(s, &c); // read max value if (maxv > 255) return stbi__err("max value > 255", "PPM image not 8-bit"); else return 1; } #endif static int stbi__info_main(stbi__context *s, int *x, int *y, int *comp) { #ifndef STBI_NO_JPEG if (stbi__jpeg_info(s, x, y, comp)) return 1; #endif #ifndef STBI_NO_PNG if (stbi__png_info(s, x, y, comp)) return 1; #endif #ifndef STBI_NO_GIF if (stbi__gif_info(s, x, y, comp)) return 1; #endif #ifndef STBI_NO_BMP if (stbi__bmp_info(s, x, y, comp)) return 1; #endif #ifndef STBI_NO_PSD if (stbi__psd_info(s, x, y, comp)) return 1; #endif #ifndef STBI_NO_PIC if (stbi__pic_info(s, x, y, comp)) return 1; #endif #ifndef STBI_NO_PNM if (stbi__pnm_info(s, x, y, comp)) return 1; #endif #ifndef STBI_NO_HDR if (stbi__hdr_info(s, x, y, comp)) return 1; #endif // test tga last because it's a crappy test! #ifndef STBI_NO_TGA if (stbi__tga_info(s, x, y, comp)) return 1; #endif return stbi__err("unknown image type", "Image not of any known type, or corrupt"); } static int stbi__is_16_main(stbi__context *s) { #ifndef STBI_NO_PNG if (stbi__png_is16(s)) return 1; #endif #ifndef STBI_NO_PSD if (stbi__psd_is16(s)) return 1; #endif return 0; } #ifndef STBI_NO_STDIO STBIDEF int stbi_info(char const *filename, int *x, int *y, int *comp) { FILE *f = stbi__fopen(filename, "rb"); int result; if (!f) return stbi__err("can't fopen", "Unable to open file"); result = stbi_info_from_file(f, x, y, comp); fclose(f); return result; } STBIDEF int stbi_info_from_file(FILE *f, int *x, int *y, int *comp) { int r; stbi__context s; long pos = ftell(f); stbi__start_file(&s, f); r = stbi__info_main(&s,x,y,comp); fseek(f,pos,SEEK_SET); return r; } STBIDEF int stbi_is_16_bit(char const *filename) { FILE *f = stbi__fopen(filename, "rb"); int result; if (!f) return stbi__err("can't fopen", "Unable to open file"); result = stbi_is_16_bit_from_file(f); fclose(f); return result; } STBIDEF int stbi_is_16_bit_from_file(FILE *f) { int r; stbi__context s; long pos = ftell(f); stbi__start_file(&s, f); r = stbi__is_16_main(&s); fseek(f,pos,SEEK_SET); return r; } #endif // !STBI_NO_STDIO STBIDEF int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp) { stbi__context s; stbi__start_mem(&s,buffer,len); return stbi__info_main(&s,x,y,comp); } STBIDEF int stbi_info_from_callbacks(stbi_io_callbacks const *c, void *user, int *x, int *y, int *comp) { stbi__context s; stbi__start_callbacks(&s, (stbi_io_callbacks *) c, user); return stbi__info_main(&s,x,y,comp); } STBIDEF int stbi_is_16_bit_from_memory(stbi_uc const *buffer, int len) { stbi__context s; stbi__start_mem(&s,buffer,len); return stbi__is_16_main(&s); } STBIDEF int stbi_is_16_bit_from_callbacks(stbi_io_callbacks const *c, void *user) { stbi__context s; stbi__start_callbacks(&s, (stbi_io_callbacks *) c, user); return stbi__is_16_main(&s); } #endif // STB_IMAGE_IMPLEMENTATION /* revision history: 2.20 (2019-02-07) support utf8 filenames in Windows; fix warnings and platform ifdefs 2.19 (2018-02-11) fix warning 2.18 (2018-01-30) fix warnings 2.17 (2018-01-29) change sbti__shiftsigned to avoid clang -O2 bug 1-bit BMP *_is_16_bit api avoid warnings 2.16 (2017-07-23) all functions have 16-bit variants; STBI_NO_STDIO works again; compilation fixes; fix rounding in unpremultiply; optimize vertical flip; disable raw_len validation; documentation fixes 2.15 (2017-03-18) fix png-1,2,4 bug; now all Imagenet JPGs decode; warning fixes; disable run-time SSE detection on gcc; uniform handling of optional "return" values; thread-safe initialization of zlib tables 2.14 (2017-03-03) remove deprecated STBI_JPEG_OLD; fixes for Imagenet JPGs 2.13 (2016-11-29) add 16-bit API, only supported for PNG right now 2.12 (2016-04-02) fix typo in 2.11 PSD fix that caused crashes 2.11 (2016-04-02) allocate large structures on the stack remove white matting for transparent PSD fix reported channel count for PNG & BMP re-enable SSE2 in non-gcc 64-bit support RGB-formatted JPEG read 16-bit PNGs (only as 8-bit) 2.10 (2016-01-22) avoid warning introduced in 2.09 by STBI_REALLOC_SIZED 2.09 (2016-01-16) allow comments in PNM files 16-bit-per-pixel TGA (not bit-per-component) info() for TGA could break due to .hdr handling info() for BMP to shares code instead of sloppy parse can use STBI_REALLOC_SIZED if allocator doesn't support realloc code cleanup 2.08 (2015-09-13) fix to 2.07 cleanup, reading RGB PSD as RGBA 2.07 (2015-09-13) fix compiler warnings partial animated GIF support limited 16-bpc PSD support #ifdef unused functions bug with < 92 byte PIC,PNM,HDR,TGA 2.06 (2015-04-19) fix bug where PSD returns wrong '*comp' value 2.05 (2015-04-19) fix bug in progressive JPEG handling, fix warning 2.04 (2015-04-15) try to re-enable SIMD on MinGW 64-bit 2.03 (2015-04-12) extra corruption checking (mmozeiko) stbi_set_flip_vertically_on_load (nguillemot) fix NEON support; fix mingw support 2.02 (2015-01-19) fix incorrect assert, fix warning 2.01 (2015-01-17) fix various warnings; suppress SIMD on gcc 32-bit without -msse2 2.00b (2014-12-25) fix STBI_MALLOC in progressive JPEG 2.00 (2014-12-25) optimize JPG, including x86 SSE2 & NEON SIMD (ryg) progressive JPEG (stb) PGM/PPM support (Ken Miller) STBI_MALLOC,STBI_REALLOC,STBI_FREE GIF bugfix -- seemingly never worked STBI_NO_*, STBI_ONLY_* 1.48 (2014-12-14) fix incorrectly-named assert() 1.47 (2014-12-14) 1/2/4-bit PNG support, both direct and paletted (Omar Cornut & stb) optimize PNG (ryg) fix bug in interlaced PNG with user-specified channel count (stb) 1.46 (2014-08-26) fix broken tRNS chunk (colorkey-style transparency) in non-paletted PNG 1.45 (2014-08-16) fix MSVC-ARM internal compiler error by wrapping malloc 1.44 (2014-08-07) various warning fixes from Ronny Chevalier 1.43 (2014-07-15) fix MSVC-only compiler problem in code changed in 1.42 1.42 (2014-07-09) don't define _CRT_SECURE_NO_WARNINGS (affects user code) fixes to stbi__cleanup_jpeg path added STBI_ASSERT to avoid requiring assert.h 1.41 (2014-06-25) fix search&replace from 1.36 that messed up comments/error messages 1.40 (2014-06-22) fix gcc struct-initialization warning 1.39 (2014-06-15) fix to TGA optimization when req_comp != number of components in TGA; fix to GIF loading because BMP wasn't rewinding (whoops, no GIFs in my test suite) add support for BMP version 5 (more ignored fields) 1.38 (2014-06-06) suppress MSVC warnings on integer casts truncating values fix accidental rename of 'skip' field of I/O 1.37 (2014-06-04) remove duplicate typedef 1.36 (2014-06-03) convert to header file single-file library if de-iphone isn't set, load iphone images color-swapped instead of returning NULL 1.35 (2014-05-27) various warnings fix broken STBI_SIMD path fix bug where stbi_load_from_file no longer left file pointer in correct place fix broken non-easy path for 32-bit BMP (possibly never used) TGA optimization by Arseny Kapoulkine 1.34 (unknown) use STBI_NOTUSED in stbi__resample_row_generic(), fix one more leak in tga failure case 1.33 (2011-07-14) make stbi_is_hdr work in STBI_NO_HDR (as specified), minor compiler-friendly improvements 1.32 (2011-07-13) support for "info" function for all supported filetypes (SpartanJ) 1.31 (2011-06-20) a few more leak fixes, bug in PNG handling (SpartanJ) 1.30 (2011-06-11) added ability to load files via callbacks to accomidate custom input streams (Ben Wenger) removed deprecated format-specific test/load functions removed support for installable file formats (stbi_loader) -- would have been broken for IO callbacks anyway error cases in bmp and tga give messages and don't leak (Raymond Barbiero, grisha) fix inefficiency in decoding 32-bit BMP (David Woo) 1.29 (2010-08-16) various warning fixes from Aurelien Pocheville 1.28 (2010-08-01) fix bug in GIF palette transparency (SpartanJ) 1.27 (2010-08-01) cast-to-stbi_uc to fix warnings 1.26 (2010-07-24) fix bug in file buffering for PNG reported by SpartanJ 1.25 (2010-07-17) refix trans_data warning (Won Chun) 1.24 (2010-07-12) perf improvements reading from files on platforms with lock-heavy fgetc() minor perf improvements for jpeg deprecated type-specific functions so we'll get feedback if they're needed attempt to fix trans_data warning (Won Chun) 1.23 fixed bug in iPhone support 1.22 (2010-07-10) removed image *writing* support stbi_info support from Jetro Lauha GIF support from Jean-Marc Lienher iPhone PNG-extensions from James Brown warning-fixes from Nicolas Schulz and Janez Zemva (i.stbi__err. Janez (U+017D)emva) 1.21 fix use of 'stbi_uc' in header (reported by jon blow) 1.20 added support for Softimage PIC, by Tom Seddon 1.19 bug in interlaced PNG corruption check (found by ryg) 1.18 (2008-08-02) fix a threading bug (local mutable static) 1.17 support interlaced PNG 1.16 major bugfix - stbi__convert_format converted one too many pixels 1.15 initialize some fields for thread safety 1.14 fix threadsafe conversion bug header-file-only version (#define STBI_HEADER_FILE_ONLY before including) 1.13 threadsafe 1.12 const qualifiers in the API 1.11 Support installable IDCT, colorspace conversion routines 1.10 Fixes for 64-bit (don't use "unsigned long") optimized upsampling by Fabian "ryg" Giesen 1.09 Fix format-conversion for PSD code (bad global variables!) 1.08 Thatcher Ulrich's PSD code integrated by Nicolas Schulz 1.07 attempt to fix C++ warning/errors again 1.06 attempt to fix C++ warning/errors again 1.05 fix TGA loading to return correct *comp and use good luminance calc 1.04 default float alpha is 1, not 255; use 'void *' for stbi_image_free 1.03 bugfixes to STBI_NO_STDIO, STBI_NO_HDR 1.02 support for (subset of) HDR files, float interface for preferred access to them 1.01 fix bug: possible bug in handling right-side up bmps... not sure fix bug: the stbi__bmp_load() and stbi__tga_load() functions didn't work at all 1.00 interface to zlib that skips zlib header 0.99 correct handling of alpha in palette 0.98 TGA loader by lonesock; dynamically add loaders (untested) 0.97 jpeg errors on too large a file; also catch another malloc failure 0.96 fix detection of invalid v value - particleman@mollyrocket forum 0.95 during header scan, seek to markers in case of padding 0.94 STBI_NO_STDIO to disable stdio usage; rename all #defines the same 0.93 handle jpegtran output; verbose errors 0.92 read 4,8,16,24,32-bit BMP files of several formats 0.91 output 24-bit Windows 3.0 BMP files 0.90 fix a few more warnings; bump version number to approach 1.0 0.61 bugfixes due to Marc LeBlanc, Christopher Lloyd 0.60 fix compiling as c++ 0.59 fix warnings: merge Dave Moore's -Wall fixes 0.58 fix bug: zlib uncompressed mode len/nlen was wrong endian 0.57 fix bug: jpg last huffman symbol before marker was >9 bits but less than 16 available 0.56 fix bug: zlib uncompressed mode len vs. nlen 0.55 fix bug: restart_interval not initialized to 0 0.54 allow NULL for 'int *comp' 0.53 fix bug in png 3->4; speedup png decoding 0.52 png handles req_comp=3,4 directly; minor cleanup; jpeg comments 0.51 obey req_comp requests, 1-component jpegs return as 1-component, on 'test' only check type, not whether we support this variant 0.50 (2006-11-19) first released version */ /* ------------------------------------------------------------------------------ This software is available under 2 licenses -- choose whichever you prefer. ------------------------------------------------------------------------------ ALTERNATIVE A - MIT License Copyright (c) 2017 Sean Barrett 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. ------------------------------------------------------------------------------ ALTERNATIVE B - Public Domain (www.unlicense.org) This is free and unencumbered software released into the public domain. Anyone is free to copy, modify, publish, use, compile, sell, or distribute this software, either in source code form or as a compiled binary, for any purpose, commercial or non-commercial, and by any means. In jurisdictions that recognize copyright laws, the author or authors of this software dedicate any and all copyright interest in the software to the public domain. We make this dedication for the benefit of the public at large and to the detriment of our heirs and successors. We intend this dedication to be an overt act of relinquishment in perpetuity of all present and future rights to this software under copyright law. 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 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. ------------------------------------------------------------------------------ */ #line 0 #line 1 "3rd_stb_image_write.h" /* stb_image_write - v1.15 - public domain - http://nothings.org/stb writes out PNG/BMP/TGA/JPEG/HDR images to C stdio - Sean Barrett 2010-2015 no warranty implied; use at your own risk Before #including, #define STB_IMAGE_WRITE_IMPLEMENTATION in the file that you want to have the implementation. Will probably not work correctly with strict-aliasing optimizations. ABOUT: This header file is a library for writing images to C stdio or a callback. The PNG output is not optimal; it is 20-50% larger than the file written by a decent optimizing implementation; though providing a custom zlib compress function (see STBIW_ZLIB_COMPRESS) can mitigate that. This library is designed for source code compactness and simplicity, not optimal image file size or run-time performance. BUILDING: You can #define STBIW_ASSERT(x) before the #include to avoid using assert.h. You can #define STBIW_MALLOC(), STBIW_REALLOC(), and STBIW_FREE() to replace malloc,realloc,free. You can #define STBIW_MEMMOVE() to replace memmove() You can #define STBIW_ZLIB_COMPRESS to use a custom zlib-style compress function for PNG compression (instead of the builtin one), it must have the following signature: unsigned char * my_compress(unsigned char *data, int data_len, int *out_len, int quality); The returned data will be freed with STBIW_FREE() (free() by default), so it must be heap allocated with STBIW_MALLOC() (malloc() by default), UNICODE: If compiling for Windows and you wish to use Unicode filenames, compile with #define STBIW_WINDOWS_UTF8 and pass utf8-encoded filenames. Call stbiw_convert_wchar_to_utf8 to convert Windows wchar_t filenames to utf8. USAGE: There are five functions, one for each image file format: int stbi_write_png(char const *filename, int w, int h, int comp, const void *data, int stride_in_bytes); int stbi_write_bmp(char const *filename, int w, int h, int comp, const void *data); int stbi_write_tga(char const *filename, int w, int h, int comp, const void *data); int stbi_write_jpg(char const *filename, int w, int h, int comp, const void *data, int quality); int stbi_write_hdr(char const *filename, int w, int h, int comp, const float *data); void stbi_flip_vertically_on_write(int flag); // flag is non-zero to flip data vertically There are also five equivalent functions that use an arbitrary write function. You are expected to open/close your file-equivalent before and after calling these: int stbi_write_png_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data, int stride_in_bytes); int stbi_write_bmp_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data); int stbi_write_tga_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data); int stbi_write_hdr_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const float *data); int stbi_write_jpg_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data, int quality); where the callback is: void stbi_write_func(void *context, void *data, int size); You can configure it with these global variables: int stbi_write_tga_with_rle; // defaults to true; set to 0 to disable RLE int stbi_write_png_compression_level; // defaults to 8; set to higher for more compression int stbi_write_force_png_filter; // defaults to -1; set to 0..5 to force a filter mode You can define STBI_WRITE_NO_STDIO to disable the file variant of these functions, so the library will not use stdio.h at all. However, this will also disable HDR writing, because it requires stdio for formatted output. Each function returns 0 on failure and non-0 on success. The functions create an image file defined by the parameters. The image is a rectangle of pixels stored from left-to-right, top-to-bottom. Each pixel contains 'comp' channels of data stored interleaved with 8-bits per channel, in the following order: 1=Y, 2=YA, 3=RGB, 4=RGBA. (Y is monochrome color.) The rectangle is 'w' pixels wide and 'h' pixels tall. The *data pointer points to the first byte of the top-left-most pixel. For PNG, "stride_in_bytes" is the distance in bytes from the first byte of a row of pixels to the first byte of the next row of pixels. PNG creates output files with the same number of components as the input. The BMP format expands Y to RGB in the file format and does not output alpha. PNG supports writing rectangles of data even when the bytes storing rows of data are not consecutive in memory (e.g. sub-rectangles of a larger image), by supplying the stride between the beginning of adjacent rows. The other formats do not. (Thus you cannot write a native-format BMP through the BMP writer, both because it is in BGR order and because it may have padding at the end of the line.) PNG allows you to set the deflate compression level by setting the global variable 'stbi_write_png_compression_level' (it defaults to 8). HDR expects linear float data. Since the format is always 32-bit rgb(e) data, alpha (if provided) is discarded, and for monochrome data it is replicated across all three channels. TGA supports RLE or non-RLE compressed data. To use non-RLE-compressed data, set the global variable 'stbi_write_tga_with_rle' to 0. JPEG does ignore alpha channels in input data; quality is between 1 and 100. Higher quality looks better but results in a bigger image. JPEG baseline (no JPEG progressive). CREDITS: Sean Barrett - PNG/BMP/TGA Baldur Karlsson - HDR Jean-Sebastien Guay - TGA monochrome Tim Kelsey - misc enhancements Alan Hickman - TGA RLE Emmanuel Julien - initial file IO callback implementation Jon Olick - original jo_jpeg.cpp code Daniel Gibson - integrate JPEG, allow external zlib Aarni Koskela - allow choosing PNG filter bugfixes: github:Chribba Guillaume Chereau github:jry2 github:romigrou Sergio Gonzalez Jonas Karlsson Filip Wasil Thatcher Ulrich github:poppolopoppo Patrick Boettcher github:xeekworx Cap Petschulat Simon Rodriguez Ivan Tikhonov github:ignotion Adam Schackart LICENSE See end of file for license information. */ #ifndef INCLUDE_STB_IMAGE_WRITE_H #define INCLUDE_STB_IMAGE_WRITE_H #include // if STB_IMAGE_WRITE_STATIC causes problems, try defining STBIWDEF to 'inline' or 'static inline' #ifndef STBIWDEF #ifdef STB_IMAGE_WRITE_STATIC #define STBIWDEF static #else #ifdef __cplusplus #define STBIWDEF extern "C" #else #define STBIWDEF extern #endif #endif #endif #ifndef STB_IMAGE_WRITE_STATIC // C++ forbids static forward declarations extern int stbi_write_tga_with_rle; extern int stbi_write_png_compression_level; extern int stbi_write_force_png_filter; #endif #ifndef STBI_WRITE_NO_STDIO STBIWDEF int stbi_write_png(char const *filename, int w, int h, int comp, const void *data, int stride_in_bytes); STBIWDEF int stbi_write_bmp(char const *filename, int w, int h, int comp, const void *data); STBIWDEF int stbi_write_tga(char const *filename, int w, int h, int comp, const void *data); STBIWDEF int stbi_write_hdr(char const *filename, int w, int h, int comp, const float *data); STBIWDEF int stbi_write_jpg(char const *filename, int x, int y, int comp, const void *data, int quality); #ifdef STBI_WINDOWS_UTF8 STBIWDEF int stbiw_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wchar_t* input); #endif #endif typedef void stbi_write_func(void *context, void *data, int size); STBIWDEF int stbi_write_png_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data, int stride_in_bytes); STBIWDEF int stbi_write_bmp_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data); STBIWDEF int stbi_write_tga_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data); STBIWDEF int stbi_write_hdr_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const float *data); STBIWDEF int stbi_write_jpg_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data, int quality); STBIWDEF void stbi_flip_vertically_on_write(int flip_boolean); #endif//INCLUDE_STB_IMAGE_WRITE_H #ifdef STB_IMAGE_WRITE_IMPLEMENTATION #ifdef _WIN32 #ifndef _CRT_SECURE_NO_WARNINGS #define _CRT_SECURE_NO_WARNINGS #endif #ifndef _CRT_NONSTDC_NO_DEPRECATE #define _CRT_NONSTDC_NO_DEPRECATE #endif #endif #ifndef STBI_WRITE_NO_STDIO #include #endif // STBI_WRITE_NO_STDIO #include #include #include #include #if defined(STBIW_MALLOC) && defined(STBIW_FREE) && (defined(STBIW_REALLOC) || defined(STBIW_REALLOC_SIZED)) // ok #elif !defined(STBIW_MALLOC) && !defined(STBIW_FREE) && !defined(STBIW_REALLOC) && !defined(STBIW_REALLOC_SIZED) // ok #else #error "Must define all or none of STBIW_MALLOC, STBIW_FREE, and STBIW_REALLOC (or STBIW_REALLOC_SIZED)." #endif #ifndef STBIW_MALLOC #define STBIW_MALLOC(sz) malloc(sz) #define STBIW_REALLOC(p,newsz) realloc(p,newsz) #define STBIW_FREE(p) free(p) #endif #ifndef STBIW_REALLOC_SIZED #define STBIW_REALLOC_SIZED(p,oldsz,newsz) STBIW_REALLOC(p,newsz) #endif #ifndef STBIW_MEMMOVE #define STBIW_MEMMOVE(a,b,sz) memmove(a,b,sz) #endif #ifndef STBIW_ASSERT #include #define STBIW_ASSERT(x) assert(x) #endif #define STBIW_UCHAR(x) (unsigned char) ((x) & 0xff) #ifdef STB_IMAGE_WRITE_STATIC static int stbi_write_png_compression_level = 8; static int stbi_write_tga_with_rle = 1; static int stbi_write_force_png_filter = -1; #else int stbi_write_png_compression_level = 8; int stbi_write_tga_with_rle = 1; int stbi_write_force_png_filter = -1; #endif static int stbi__flip_vertically_on_write = 0; STBIWDEF void stbi_flip_vertically_on_write(int flag) { stbi__flip_vertically_on_write = flag; } typedef struct { stbi_write_func *func; void *context; unsigned char buffer[64]; int buf_used; } stbi__write_context; // initialize a callback-based context static void stbi__start_write_callbacks(stbi__write_context *s, stbi_write_func *c, void *context) { s->func = c; s->context = context; } #ifndef STBI_WRITE_NO_STDIO static void stbi__stdio_write(void *context, void *data, int size) { fwrite(data,1,size,(FILE*) context); } #if defined(_MSC_VER) && defined(STBI_WINDOWS_UTF8) #ifdef __cplusplus #define STBIW_EXTERN extern "C" #else #define STBIW_EXTERN extern #endif STBIW_EXTERN __declspec(dllimport) int __stdcall MultiByteToWideChar(unsigned int cp, unsigned long flags, const char *str, int cbmb, wchar_t *widestr, int cchwide); STBIW_EXTERN __declspec(dllimport) int __stdcall WideCharToMultiByte(unsigned int cp, unsigned long flags, const wchar_t *widestr, int cchwide, char *str, int cbmb, const char *defchar, int *used_default); STBIWDEF int stbiw_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wchar_t* input) { return WideCharToMultiByte(65001 /* UTF8 */, 0, input, -1, buffer, (int) bufferlen, NULL, NULL); } #endif static FILE *stbiw__fopen(char const *filename, char const *mode) { FILE *f; #if defined(_MSC_VER) && defined(STBI_WINDOWS_UTF8) wchar_t wMode[64]; wchar_t wFilename[1024]; if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, filename, -1, wFilename, sizeof(wFilename))) return 0; if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, mode, -1, wMode, sizeof(wMode))) return 0; #if _MSC_VER >= 1400 if (0 != _wfopen_s(&f, wFilename, wMode)) f = 0; #else f = _wfopen(wFilename, wMode); #endif #elif defined(_MSC_VER) && _MSC_VER >= 1400 if (0 != fopen_s(&f, filename, mode)) f=0; #else f = fopen(filename, mode); #endif return f; } static int stbi__start_write_file(stbi__write_context *s, const char *filename) { FILE *f = stbiw__fopen(filename, "wb"); stbi__start_write_callbacks(s, stbi__stdio_write, (void *) f); return f != NULL; } static void stbi__end_write_file(stbi__write_context *s) { fclose((FILE *)s->context); } #endif // !STBI_WRITE_NO_STDIO typedef unsigned int stbiw_uint32; typedef int stb_image_write_test[sizeof(stbiw_uint32)==4 ? 1 : -1]; static void stbiw__writefv(stbi__write_context *s, const char *fmt, va_list v) { while (*fmt) { switch (*fmt++) { case ' ': break; case '1': { unsigned char x = STBIW_UCHAR(va_arg(v, int)); s->func(s->context,&x,1); break; } case '2': { int x = va_arg(v,int); unsigned char b[2]; b[0] = STBIW_UCHAR(x); b[1] = STBIW_UCHAR(x>>8); s->func(s->context,b,2); break; } case '4': { stbiw_uint32 x = va_arg(v,int); unsigned char b[4]; b[0]=STBIW_UCHAR(x); b[1]=STBIW_UCHAR(x>>8); b[2]=STBIW_UCHAR(x>>16); b[3]=STBIW_UCHAR(x>>24); s->func(s->context,b,4); break; } default: STBIW_ASSERT(0); return; } } } static void stbiw__writef(stbi__write_context *s, const char *fmt, ...) { va_list v; va_start(v, fmt); stbiw__writefv(s, fmt, v); va_end(v); } static void stbiw__write_flush(stbi__write_context *s) { if (s->buf_used) { s->func(s->context, &s->buffer, s->buf_used); s->buf_used = 0; } } static void stbiw__putc(stbi__write_context *s, unsigned char c) { s->func(s->context, &c, 1); } static void stbiw__write1(stbi__write_context *s, unsigned char a) { if (s->buf_used + 1 > sizeof(s->buffer)) stbiw__write_flush(s); s->buffer[s->buf_used++] = a; } static void stbiw__write3(stbi__write_context *s, unsigned char a, unsigned char b, unsigned char c) { int n; if (s->buf_used + 3 > sizeof(s->buffer)) stbiw__write_flush(s); n = s->buf_used; s->buf_used = n+3; s->buffer[n+0] = a; s->buffer[n+1] = b; s->buffer[n+2] = c; } static void stbiw__write_pixel(stbi__write_context *s, int rgb_dir, int comp, int write_alpha, int expand_mono, unsigned char *d) { unsigned char bg[3] = { 255, 0, 255}, px[3]; int k; if (write_alpha < 0) stbiw__write1(s, d[comp - 1]); switch (comp) { case 2: // 2 pixels = mono + alpha, alpha is written separately, so same as 1-channel case case 1: if (expand_mono) stbiw__write3(s, d[0], d[0], d[0]); // monochrome bmp else stbiw__write1(s, d[0]); // monochrome TGA break; case 4: if (!write_alpha) { // composite against pink background for (k = 0; k < 3; ++k) px[k] = bg[k] + ((d[k] - bg[k]) * d[3]) / 255; stbiw__write3(s, px[1 - rgb_dir], px[1], px[1 + rgb_dir]); break; } /* FALLTHROUGH */ case 3: stbiw__write3(s, d[1 - rgb_dir], d[1], d[1 + rgb_dir]); break; } if (write_alpha > 0) stbiw__write1(s, d[comp - 1]); } static void stbiw__write_pixels(stbi__write_context *s, int rgb_dir, int vdir, int x, int y, int comp, void *data, int write_alpha, int scanline_pad, int expand_mono) { stbiw_uint32 zero = 0; int i,j, j_end; if (y <= 0) return; if (stbi__flip_vertically_on_write) vdir *= -1; if (vdir < 0) { j_end = -1; j = y-1; } else { j_end = y; j = 0; } for (; j != j_end; j += vdir) { for (i=0; i < x; ++i) { unsigned char *d = (unsigned char *) data + (j*x+i)*comp; stbiw__write_pixel(s, rgb_dir, comp, write_alpha, expand_mono, d); } stbiw__write_flush(s); s->func(s->context, &zero, scanline_pad); } } static int stbiw__outfile(stbi__write_context *s, int rgb_dir, int vdir, int x, int y, int comp, int expand_mono, void *data, int alpha, int pad, const char *fmt, ...) { if (y < 0 || x < 0) { return 0; } else { va_list v; va_start(v, fmt); stbiw__writefv(s, fmt, v); va_end(v); stbiw__write_pixels(s,rgb_dir,vdir,x,y,comp,data,alpha,pad, expand_mono); return 1; } } static int stbi_write_bmp_core(stbi__write_context *s, int x, int y, int comp, const void *data) { int pad = (-x*3) & 3; return stbiw__outfile(s,-1,-1,x,y,comp,1,(void *) data,0,pad, "11 4 22 4" "4 44 22 444444", 'B', 'M', 14+40+(x*3+pad)*y, 0,0, 14+40, // file header 40, x,y, 1,24, 0,0,0,0,0,0); // bitmap header } STBIWDEF int stbi_write_bmp_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data) { stbi__write_context s = { 0 }; stbi__start_write_callbacks(&s, func, context); return stbi_write_bmp_core(&s, x, y, comp, data); } #ifndef STBI_WRITE_NO_STDIO STBIWDEF int stbi_write_bmp(char const *filename, int x, int y, int comp, const void *data) { stbi__write_context s = { 0 }; if (stbi__start_write_file(&s,filename)) { int r = stbi_write_bmp_core(&s, x, y, comp, data); stbi__end_write_file(&s); return r; } else return 0; } #endif //!STBI_WRITE_NO_STDIO static int stbi_write_tga_core(stbi__write_context *s, int x, int y, int comp, void *data) { int has_alpha = (comp == 2 || comp == 4); int colorbytes = has_alpha ? comp-1 : comp; int format = colorbytes < 2 ? 3 : 2; // 3 color channels (RGB/RGBA) = 2, 1 color channel (Y/YA) = 3 if (y < 0 || x < 0) return 0; if (!stbi_write_tga_with_rle) { return stbiw__outfile(s, -1, -1, x, y, comp, 0, (void *) data, has_alpha, 0, "111 221 2222 11", 0, 0, format, 0, 0, 0, 0, 0, x, y, (colorbytes + has_alpha) * 8, has_alpha * 8); } else { int i,j,k; int jend, jdir; stbiw__writef(s, "111 221 2222 11", 0,0,format+8, 0,0,0, 0,0,x,y, (colorbytes + has_alpha) * 8, has_alpha * 8); if (stbi__flip_vertically_on_write) { j = 0; jend = y; jdir = 1; } else { j = y-1; jend = -1; jdir = -1; } for (; j != jend; j += jdir) { unsigned char *row = (unsigned char *) data + j * x * comp; int len; for (i = 0; i < x; i += len) { unsigned char *begin = row + i * comp; int diff = 1; len = 1; if (i < x - 1) { ++len; diff = memcmp(begin, row + (i + 1) * comp, comp); if (diff) { const unsigned char *prev = begin; for (k = i + 2; k < x && len < 128; ++k) { if (memcmp(prev, row + k * comp, comp)) { prev += comp; ++len; } else { --len; break; } } } else { for (k = i + 2; k < x && len < 128; ++k) { if (!memcmp(begin, row + k * comp, comp)) { ++len; } else { break; } } } } if (diff) { unsigned char header = STBIW_UCHAR(len - 1); stbiw__write1(s, header); for (k = 0; k < len; ++k) { stbiw__write_pixel(s, -1, comp, has_alpha, 0, begin + k * comp); } } else { unsigned char header = STBIW_UCHAR(len - 129); stbiw__write1(s, header); stbiw__write_pixel(s, -1, comp, has_alpha, 0, begin); } } } stbiw__write_flush(s); } return 1; } STBIWDEF int stbi_write_tga_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data) { stbi__write_context s = { 0 }; stbi__start_write_callbacks(&s, func, context); return stbi_write_tga_core(&s, x, y, comp, (void *) data); } #ifndef STBI_WRITE_NO_STDIO STBIWDEF int stbi_write_tga(char const *filename, int x, int y, int comp, const void *data) { stbi__write_context s = { 0 }; if (stbi__start_write_file(&s,filename)) { int r = stbi_write_tga_core(&s, x, y, comp, (void *) data); stbi__end_write_file(&s); return r; } else return 0; } #endif // ************************************************************************************************* // Radiance RGBE HDR writer // by Baldur Karlsson #define stbiw__max(a, b) ((a) > (b) ? (a) : (b)) static void stbiw__linear_to_rgbe(unsigned char *rgbe, float *linear) { int exponent; float maxcomp = stbiw__max(linear[0], stbiw__max(linear[1], linear[2])); if (maxcomp < 1e-32f) { rgbe[0] = rgbe[1] = rgbe[2] = rgbe[3] = 0; } else { float normalize = (float) frexp(maxcomp, &exponent) * 256.0f/maxcomp; rgbe[0] = (unsigned char)(linear[0] * normalize); rgbe[1] = (unsigned char)(linear[1] * normalize); rgbe[2] = (unsigned char)(linear[2] * normalize); rgbe[3] = (unsigned char)(exponent + 128); } } static void stbiw__write_run_data(stbi__write_context *s, int length, unsigned char databyte) { unsigned char lengthbyte = STBIW_UCHAR(length+128); STBIW_ASSERT(length+128 <= 255); s->func(s->context, &lengthbyte, 1); s->func(s->context, &databyte, 1); } static void stbiw__write_dump_data(stbi__write_context *s, int length, unsigned char *data) { unsigned char lengthbyte = STBIW_UCHAR(length); STBIW_ASSERT(length <= 128); // inconsistent with spec but consistent with official code s->func(s->context, &lengthbyte, 1); s->func(s->context, data, length); } static void stbiw__write_hdr_scanline(stbi__write_context *s, int width, int ncomp, unsigned char *scratch, float *scanline) { unsigned char scanlineheader[4] = { 2, 2, 0, 0 }; unsigned char rgbe[4]; float linear[3]; int x; scanlineheader[2] = (width&0xff00)>>8; scanlineheader[3] = (width&0x00ff); /* skip RLE for images too small or large */ if (width < 8 || width >= 32768) { for (x=0; x < width; x++) { switch (ncomp) { case 4: /* fallthrough */ case 3: linear[2] = scanline[x*ncomp + 2]; linear[1] = scanline[x*ncomp + 1]; linear[0] = scanline[x*ncomp + 0]; break; default: linear[0] = linear[1] = linear[2] = scanline[x*ncomp + 0]; break; } stbiw__linear_to_rgbe(rgbe, linear); s->func(s->context, rgbe, 4); } } else { int c,r; /* encode into scratch buffer */ for (x=0; x < width; x++) { switch(ncomp) { case 4: /* fallthrough */ case 3: linear[2] = scanline[x*ncomp + 2]; linear[1] = scanline[x*ncomp + 1]; linear[0] = scanline[x*ncomp + 0]; break; default: linear[0] = linear[1] = linear[2] = scanline[x*ncomp + 0]; break; } stbiw__linear_to_rgbe(rgbe, linear); scratch[x + width*0] = rgbe[0]; scratch[x + width*1] = rgbe[1]; scratch[x + width*2] = rgbe[2]; scratch[x + width*3] = rgbe[3]; } s->func(s->context, scanlineheader, 4); /* RLE each component separately */ for (c=0; c < 4; c++) { unsigned char *comp = &scratch[width*c]; x = 0; while (x < width) { // find first run r = x; while (r+2 < width) { if (comp[r] == comp[r+1] && comp[r] == comp[r+2]) break; ++r; } if (r+2 >= width) r = width; // dump up to first run while (x < r) { int len = r-x; if (len > 128) len = 128; stbiw__write_dump_data(s, len, &comp[x]); x += len; } // if there's a run, output it if (r+2 < width) { // same test as what we break out of in search loop, so only true if we break'd // find next byte after run while (r < width && comp[r] == comp[x]) ++r; // output run up to r while (x < r) { int len = r-x; if (len > 127) len = 127; stbiw__write_run_data(s, len, comp[x]); x += len; } } } } } } static int stbi_write_hdr_core(stbi__write_context *s, int x, int y, int comp, float *data) { if (y <= 0 || x <= 0 || data == NULL) return 0; else { // Each component is stored separately. Allocate scratch space for full output scanline. unsigned char *scratch = (unsigned char *) STBIW_MALLOC(x*4); int i, len; char buffer[128]; char header[] = "#?RADIANCE\n# Written by stb_image_write.h\nFORMAT=32-bit_rle_rgbe\n"; s->func(s->context, header, sizeof(header)-1); #ifdef __STDC_WANT_SECURE_LIB__ len = sprintf_s(buffer, sizeof(buffer), "EXPOSURE= 1.0000000000000\n\n-Y %d +X %d\n", y, x); #else len = sprintf(buffer, "EXPOSURE= 1.0000000000000\n\n-Y %d +X %d\n", y, x); #endif s->func(s->context, buffer, len); for(i=0; i < y; i++) stbiw__write_hdr_scanline(s, x, comp, scratch, data + comp*x*(stbi__flip_vertically_on_write ? y-1-i : i)); STBIW_FREE(scratch); return 1; } } STBIWDEF int stbi_write_hdr_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const float *data) { stbi__write_context s = { 0 }; stbi__start_write_callbacks(&s, func, context); return stbi_write_hdr_core(&s, x, y, comp, (float *) data); } #ifndef STBI_WRITE_NO_STDIO STBIWDEF int stbi_write_hdr(char const *filename, int x, int y, int comp, const float *data) { stbi__write_context s = { 0 }; if (stbi__start_write_file(&s,filename)) { int r = stbi_write_hdr_core(&s, x, y, comp, (float *) data); stbi__end_write_file(&s); return r; } else return 0; } #endif // STBI_WRITE_NO_STDIO ////////////////////////////////////////////////////////////////////////////// // // PNG writer // #ifndef STBIW_ZLIB_COMPRESS // stretchy buffer; stbiw__sbpush() == vector<>::push_back() -- stbiw__sbcount() == vector<>::size() #define stbiw__sbraw(a) ((int *) (void *) (a) - 2) #define stbiw__sbm(a) stbiw__sbraw(a)[0] #define stbiw__sbn(a) stbiw__sbraw(a)[1] #define stbiw__sbneedgrow(a,n) ((a)==0 || stbiw__sbn(a)+n >= stbiw__sbm(a)) #define stbiw__sbmaybegrow(a,n) (stbiw__sbneedgrow(a,(n)) ? stbiw__sbgrow(a,n) : 0) #define stbiw__sbgrow(a,n) stbiw__sbgrowf((void **) &(a), (n), sizeof(*(a))) #define stbiw__sbpush(a, v) (stbiw__sbmaybegrow(a,1), (a)[stbiw__sbn(a)++] = (v)) #define stbiw__sbcount(a) ((a) ? stbiw__sbn(a) : 0) #define stbiw__sbfree(a) ((a) ? STBIW_FREE(stbiw__sbraw(a)),0 : 0) static void *stbiw__sbgrowf(void **arr, int increment, int itemsize) { int m = *arr ? 2*stbiw__sbm(*arr)+increment : increment+1; void *p = STBIW_REALLOC_SIZED(*arr ? stbiw__sbraw(*arr) : 0, *arr ? (stbiw__sbm(*arr)*itemsize + sizeof(int)*2) : 0, itemsize * m + sizeof(int)*2); STBIW_ASSERT(p); if (p) { if (!*arr) ((int *) p)[1] = 0; *arr = (void *) ((int *) p + 2); stbiw__sbm(*arr) = m; } return *arr; } static unsigned char *stbiw__zlib_flushf(unsigned char *data, unsigned int *bitbuffer, int *bitcount) { while (*bitcount >= 8) { stbiw__sbpush(data, STBIW_UCHAR(*bitbuffer)); *bitbuffer >>= 8; *bitcount -= 8; } return data; } static int stbiw__zlib_bitrev(int code, int codebits) { int res=0; while (codebits--) { res = (res << 1) | (code & 1); code >>= 1; } return res; } static unsigned int stbiw__zlib_countm(unsigned char *a, unsigned char *b, int limit) { int i; for (i=0; i < limit && i < 258; ++i) if (a[i] != b[i]) break; return i; } static unsigned int stbiw__zhash(unsigned char *data) { stbiw_uint32 hash = data[0] + (data[1] << 8) + (data[2] << 16); hash ^= hash << 3; hash += hash >> 5; hash ^= hash << 4; hash += hash >> 17; hash ^= hash << 25; hash += hash >> 6; return hash; } #define stbiw__zlib_flush() (out = stbiw__zlib_flushf(out, &bitbuf, &bitcount)) #define stbiw__zlib_add(code,codebits) \ (bitbuf |= (code) << bitcount, bitcount += (codebits), stbiw__zlib_flush()) #define stbiw__zlib_huffa(b,c) stbiw__zlib_add(stbiw__zlib_bitrev(b,c),c) // default huffman tables #define stbiw__zlib_huff1(n) stbiw__zlib_huffa(0x30 + (n), 8) #define stbiw__zlib_huff2(n) stbiw__zlib_huffa(0x190 + (n)-144, 9) #define stbiw__zlib_huff3(n) stbiw__zlib_huffa(0 + (n)-256,7) #define stbiw__zlib_huff4(n) stbiw__zlib_huffa(0xc0 + (n)-280,8) #define stbiw__zlib_huff(n) ((n) <= 143 ? stbiw__zlib_huff1(n) : (n) <= 255 ? stbiw__zlib_huff2(n) : (n) <= 279 ? stbiw__zlib_huff3(n) : stbiw__zlib_huff4(n)) #define stbiw__zlib_huffb(n) ((n) <= 143 ? stbiw__zlib_huff1(n) : stbiw__zlib_huff2(n)) #define stbiw__ZHASH 16384 #endif // STBIW_ZLIB_COMPRESS STBIWDEF unsigned char * stbi_zlib_compress(unsigned char *data, int data_len, int *out_len, int quality) { #ifdef STBIW_ZLIB_COMPRESS // user provided a zlib compress implementation, use that return STBIW_ZLIB_COMPRESS(data, data_len, out_len, quality); #else // use builtin static unsigned short lengthc[] = { 3,4,5,6,7,8,9,10,11,13,15,17,19,23,27,31,35,43,51,59,67,83,99,115,131,163,195,227,258, 259 }; static unsigned char lengtheb[]= { 0,0,0,0,0,0,0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0 }; static unsigned short distc[] = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193,257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577, 32768 }; static unsigned char disteb[] = { 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13 }; unsigned int bitbuf=0; int i,j, bitcount=0; unsigned char *out = NULL; unsigned char ***hash_table = (unsigned char***) STBIW_MALLOC(stbiw__ZHASH * sizeof(unsigned char**)); if (hash_table == NULL) return NULL; if (quality < 5) quality = 5; stbiw__sbpush(out, 0x78); // DEFLATE 32K window stbiw__sbpush(out, 0x5e); // FLEVEL = 1 stbiw__zlib_add(1,1); // BFINAL = 1 stbiw__zlib_add(1,2); // BTYPE = 1 -- fixed huffman for (i=0; i < stbiw__ZHASH; ++i) hash_table[i] = NULL; i=0; while (i < data_len-3) { // hash next 3 bytes of data to be compressed int h = stbiw__zhash(data+i)&(stbiw__ZHASH-1), best=3; unsigned char *bestloc = 0; unsigned char **hlist = hash_table[h]; int n = stbiw__sbcount(hlist); for (j=0; j < n; ++j) { if (hlist[j]-data > i-32768) { // if entry lies within window int d = stbiw__zlib_countm(hlist[j], data+i, data_len-i); if (d >= best) { best=d; bestloc=hlist[j]; } } } // when hash table entry is too long, delete half the entries if (hash_table[h] && stbiw__sbn(hash_table[h]) == 2*quality) { STBIW_MEMMOVE(hash_table[h], hash_table[h]+quality, sizeof(hash_table[h][0])*quality); stbiw__sbn(hash_table[h]) = quality; } stbiw__sbpush(hash_table[h],data+i); if (bestloc) { // "lazy matching" - check match at *next* byte, and if it's better, do cur byte as literal h = stbiw__zhash(data+i+1)&(stbiw__ZHASH-1); hlist = hash_table[h]; n = stbiw__sbcount(hlist); for (j=0; j < n; ++j) { if (hlist[j]-data > i-32767) { int e = stbiw__zlib_countm(hlist[j], data+i+1, data_len-i-1); if (e > best) { // if next match is better, bail on current match bestloc = NULL; break; } } } } if (bestloc) { int d = (int) (data+i - bestloc); // distance back STBIW_ASSERT(d <= 32767 && best <= 258); for (j=0; best > lengthc[j+1]-1; ++j); stbiw__zlib_huff(j+257); if (lengtheb[j]) stbiw__zlib_add(best - lengthc[j], lengtheb[j]); for (j=0; d > distc[j+1]-1; ++j); stbiw__zlib_add(stbiw__zlib_bitrev(j,5),5); if (disteb[j]) stbiw__zlib_add(d - distc[j], disteb[j]); i += best; } else { stbiw__zlib_huffb(data[i]); ++i; } } // write out final bytes for (;i < data_len; ++i) stbiw__zlib_huffb(data[i]); stbiw__zlib_huff(256); // end of block // pad with 0 bits to byte boundary while (bitcount) stbiw__zlib_add(0,1); for (i=0; i < stbiw__ZHASH; ++i) (void) stbiw__sbfree(hash_table[i]); STBIW_FREE(hash_table); { // compute adler32 on input unsigned int s1=1, s2=0; int blocklen = (int) (data_len % 5552); j=0; while (j < data_len) { for (i=0; i < blocklen; ++i) { s1 += data[j+i]; s2 += s1; } s1 %= 65521; s2 %= 65521; j += blocklen; blocklen = 5552; } stbiw__sbpush(out, STBIW_UCHAR(s2 >> 8)); stbiw__sbpush(out, STBIW_UCHAR(s2)); stbiw__sbpush(out, STBIW_UCHAR(s1 >> 8)); stbiw__sbpush(out, STBIW_UCHAR(s1)); } *out_len = stbiw__sbn(out); // make returned pointer freeable STBIW_MEMMOVE(stbiw__sbraw(out), out, *out_len); return (unsigned char *) stbiw__sbraw(out); #endif // STBIW_ZLIB_COMPRESS } static unsigned int stbiw__crc32(unsigned char *buffer, int len) { #ifdef STBIW_CRC32 return STBIW_CRC32(buffer, len); #else static unsigned int crc_table[256] = { 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3, 0x0eDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91, 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5, 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172, 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, 0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59, 0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F, 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D, 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433, 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01, 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65, 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB, 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, 0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F, 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD, 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683, 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1, 0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7, 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC, 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, 0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B, 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79, 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D, 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713, 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, 0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777, 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45, 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB, 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9, 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF, 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D }; unsigned int crc = ~0u; int i; for (i=0; i < len; ++i) crc = (crc >> 8) ^ crc_table[buffer[i] ^ (crc & 0xff)]; return ~crc; #endif } #define stbiw__wpng4(o,a,b,c,d) ((o)[0]=STBIW_UCHAR(a),(o)[1]=STBIW_UCHAR(b),(o)[2]=STBIW_UCHAR(c),(o)[3]=STBIW_UCHAR(d),(o)+=4) #define stbiw__wp32(data,v) stbiw__wpng4(data, (v)>>24,(v)>>16,(v)>>8,(v)); #define stbiw__wptag(data,s) stbiw__wpng4(data, s[0],s[1],s[2],s[3]) static void stbiw__wpcrc(unsigned char **data, int len) { unsigned int crc = stbiw__crc32(*data - len - 4, len+4); stbiw__wp32(*data, crc); } static unsigned char stbiw__paeth(int a, int b, int c) { int p = a + b - c, pa = abs(p-a), pb = abs(p-b), pc = abs(p-c); if (pa <= pb && pa <= pc) return STBIW_UCHAR(a); if (pb <= pc) return STBIW_UCHAR(b); return STBIW_UCHAR(c); } // @OPTIMIZE: provide an option that always forces left-predict or paeth predict static void stbiw__encode_png_line(unsigned char *pixels, int stride_bytes, int width, int height, int y, int n, int filter_type, signed char *line_buffer) { static int mapping[] = { 0,1,2,3,4 }; static int firstmap[] = { 0,1,0,5,6 }; int *mymap = (y != 0) ? mapping : firstmap; int i; int type = mymap[filter_type]; unsigned char *z = pixels + stride_bytes * (stbi__flip_vertically_on_write ? height-1-y : y); int signed_stride = stbi__flip_vertically_on_write ? -stride_bytes : stride_bytes; if (type==0) { memcpy(line_buffer, z, width*n); return; } // first loop isn't optimized since it's just one pixel for (i = 0; i < n; ++i) { switch (type) { case 1: line_buffer[i] = z[i]; break; case 2: line_buffer[i] = z[i] - z[i-signed_stride]; break; case 3: line_buffer[i] = z[i] - (z[i-signed_stride]>>1); break; case 4: line_buffer[i] = (signed char) (z[i] - stbiw__paeth(0,z[i-signed_stride],0)); break; case 5: line_buffer[i] = z[i]; break; case 6: line_buffer[i] = z[i]; break; } } switch (type) { case 1: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - z[i-n]; break; case 2: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - z[i-signed_stride]; break; case 3: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - ((z[i-n] + z[i-signed_stride])>>1); break; case 4: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - stbiw__paeth(z[i-n], z[i-signed_stride], z[i-signed_stride-n]); break; case 5: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - (z[i-n]>>1); break; case 6: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - stbiw__paeth(z[i-n], 0,0); break; } } STBIWDEF unsigned char *stbi_write_png_to_mem(const unsigned char *pixels, int stride_bytes, int x, int y, int n, int *out_len) { int force_filter = stbi_write_force_png_filter; int ctype[5] = { -1, 0, 4, 2, 6 }; unsigned char sig[8] = { 137,80,78,71,13,10,26,10 }; unsigned char *out,*o, *filt, *zlib; signed char *line_buffer; int j,zlen; if (stride_bytes == 0) stride_bytes = x * n; if (force_filter >= 5) { force_filter = -1; } filt = (unsigned char *) STBIW_MALLOC((x*n+1) * y); if (!filt) return 0; line_buffer = (signed char *) STBIW_MALLOC(x * n); if (!line_buffer) { STBIW_FREE(filt); return 0; } for (j=0; j < y; ++j) { int filter_type; if (force_filter > -1) { filter_type = force_filter; stbiw__encode_png_line((unsigned char*)(pixels), stride_bytes, x, y, j, n, force_filter, line_buffer); } else { // Estimate the best filter by running through all of them: int best_filter = 0, best_filter_val = 0x7fffffff, est, i; for (filter_type = 0; filter_type < 5; filter_type++) { stbiw__encode_png_line((unsigned char*)(pixels), stride_bytes, x, y, j, n, filter_type, line_buffer); // Estimate the entropy of the line using this filter; the less, the better. est = 0; for (i = 0; i < x*n; ++i) { est += abs((signed char) line_buffer[i]); } if (est < best_filter_val) { best_filter_val = est; best_filter = filter_type; } } if (filter_type != best_filter) { // If the last iteration already got us the best filter, don't redo it stbiw__encode_png_line((unsigned char*)(pixels), stride_bytes, x, y, j, n, best_filter, line_buffer); filter_type = best_filter; } } // when we get here, filter_type contains the filter type, and line_buffer contains the data filt[j*(x*n+1)] = (unsigned char) filter_type; STBIW_MEMMOVE(filt+j*(x*n+1)+1, line_buffer, x*n); } STBIW_FREE(line_buffer); zlib = stbi_zlib_compress(filt, y*( x*n+1), &zlen, stbi_write_png_compression_level); STBIW_FREE(filt); if (!zlib) return 0; // each tag requires 12 bytes of overhead out = (unsigned char *) STBIW_MALLOC(8 + 12+13 + 12+zlen + 12); if (!out) return 0; *out_len = 8 + 12+13 + 12+zlen + 12; o=out; STBIW_MEMMOVE(o,sig,8); o+= 8; stbiw__wp32(o, 13); // header length stbiw__wptag(o, "IHDR"); stbiw__wp32(o, x); stbiw__wp32(o, y); *o++ = 8; *o++ = STBIW_UCHAR(ctype[n]); *o++ = 0; *o++ = 0; *o++ = 0; stbiw__wpcrc(&o,13); stbiw__wp32(o, zlen); stbiw__wptag(o, "IDAT"); STBIW_MEMMOVE(o, zlib, zlen); o += zlen; STBIW_FREE(zlib); stbiw__wpcrc(&o, zlen); stbiw__wp32(o,0); stbiw__wptag(o, "IEND"); stbiw__wpcrc(&o,0); STBIW_ASSERT(o == out + *out_len); return out; } #ifndef STBI_WRITE_NO_STDIO STBIWDEF int stbi_write_png(char const *filename, int x, int y, int comp, const void *data, int stride_bytes) { FILE *f; int len; unsigned char *png = stbi_write_png_to_mem((const unsigned char *) data, stride_bytes, x, y, comp, &len); if (png == NULL) return 0; f = stbiw__fopen(filename, "wb"); if (!f) { STBIW_FREE(png); return 0; } fwrite(png, 1, len, f); fclose(f); STBIW_FREE(png); return 1; } #endif STBIWDEF int stbi_write_png_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data, int stride_bytes) { int len; unsigned char *png = stbi_write_png_to_mem((const unsigned char *) data, stride_bytes, x, y, comp, &len); if (png == NULL) return 0; func(context, png, len); STBIW_FREE(png); return 1; } /* *************************************************************************** * * JPEG writer * * This is based on Jon Olick's jo_jpeg.cpp: * public domain Simple, Minimalistic JPEG writer - http://www.jonolick.com/code.html */ static const unsigned char stbiw__jpg_ZigZag[] = { 0,1,5,6,14,15,27,28,2,4,7,13,16,26,29,42,3,8,12,17,25,30,41,43,9,11,18, 24,31,40,44,53,10,19,23,32,39,45,52,54,20,22,33,38,46,51,55,60,21,34,37,47,50,56,59,61,35,36,48,49,57,58,62,63 }; static void stbiw__jpg_writeBits(stbi__write_context *s, int *bitBufP, int *bitCntP, const unsigned short *bs) { int bitBuf = *bitBufP, bitCnt = *bitCntP; bitCnt += bs[1]; bitBuf |= bs[0] << (24 - bitCnt); while(bitCnt >= 8) { unsigned char c = (bitBuf >> 16) & 255; stbiw__putc(s, c); if(c == 255) { stbiw__putc(s, 0); } bitBuf <<= 8; bitCnt -= 8; } *bitBufP = bitBuf; *bitCntP = bitCnt; } static void stbiw__jpg_DCT(float *d0p, float *d1p, float *d2p, float *d3p, float *d4p, float *d5p, float *d6p, float *d7p) { float d0 = *d0p, d1 = *d1p, d2 = *d2p, d3 = *d3p, d4 = *d4p, d5 = *d5p, d6 = *d6p, d7 = *d7p; float z1, z2, z3, z4, z5, z11, z13; float tmp0 = d0 + d7; float tmp7 = d0 - d7; float tmp1 = d1 + d6; float tmp6 = d1 - d6; float tmp2 = d2 + d5; float tmp5 = d2 - d5; float tmp3 = d3 + d4; float tmp4 = d3 - d4; // Even part float tmp10 = tmp0 + tmp3; // phase 2 float tmp13 = tmp0 - tmp3; float tmp11 = tmp1 + tmp2; float tmp12 = tmp1 - tmp2; d0 = tmp10 + tmp11; // phase 3 d4 = tmp10 - tmp11; z1 = (tmp12 + tmp13) * 0.707106781f; // c4 d2 = tmp13 + z1; // phase 5 d6 = tmp13 - z1; // Odd part tmp10 = tmp4 + tmp5; // phase 2 tmp11 = tmp5 + tmp6; tmp12 = tmp6 + tmp7; // The rotator is modified from fig 4-8 to avoid extra negations. z5 = (tmp10 - tmp12) * 0.382683433f; // c6 z2 = tmp10 * 0.541196100f + z5; // c2-c6 z4 = tmp12 * 1.306562965f + z5; // c2+c6 z3 = tmp11 * 0.707106781f; // c4 z11 = tmp7 + z3; // phase 5 z13 = tmp7 - z3; *d5p = z13 + z2; // phase 6 *d3p = z13 - z2; *d1p = z11 + z4; *d7p = z11 - z4; *d0p = d0; *d2p = d2; *d4p = d4; *d6p = d6; } static void stbiw__jpg_calcBits(int val, unsigned short bits[2]) { int tmp1 = val < 0 ? -val : val; val = val < 0 ? val-1 : val; bits[1] = 1; while(tmp1 >>= 1) { ++bits[1]; } bits[0] = val & ((1<0)&&(DU[end0pos]==0); --end0pos) { } // end0pos = first element in reverse order !=0 if(end0pos == 0) { stbiw__jpg_writeBits(s, bitBuf, bitCnt, EOB); return DU[0]; } for(i = 1; i <= end0pos; ++i) { int startpos = i; int nrzeroes; unsigned short bits[2]; for (; DU[i]==0 && i<=end0pos; ++i) { } nrzeroes = i-startpos; if ( nrzeroes >= 16 ) { int lng = nrzeroes>>4; int nrmarker; for (nrmarker=1; nrmarker <= lng; ++nrmarker) stbiw__jpg_writeBits(s, bitBuf, bitCnt, M16zeroes); nrzeroes &= 15; } stbiw__jpg_calcBits(DU[i], bits); stbiw__jpg_writeBits(s, bitBuf, bitCnt, HTAC[(nrzeroes<<4)+bits[1]]); stbiw__jpg_writeBits(s, bitBuf, bitCnt, bits); } if(end0pos != 63) { stbiw__jpg_writeBits(s, bitBuf, bitCnt, EOB); } return DU[0]; } static int stbi_write_jpg_core(stbi__write_context *s, int width, int height, int comp, const void* data, int quality) { // Constants that don't pollute global namespace static const unsigned char std_dc_luminance_nrcodes[] = {0,0,1,5,1,1,1,1,1,1,0,0,0,0,0,0,0}; static const unsigned char std_dc_luminance_values[] = {0,1,2,3,4,5,6,7,8,9,10,11}; static const unsigned char std_ac_luminance_nrcodes[] = {0,0,2,1,3,3,2,4,3,5,5,4,4,0,0,1,0x7d}; static const unsigned char std_ac_luminance_values[] = { 0x01,0x02,0x03,0x00,0x04,0x11,0x05,0x12,0x21,0x31,0x41,0x06,0x13,0x51,0x61,0x07,0x22,0x71,0x14,0x32,0x81,0x91,0xa1,0x08, 0x23,0x42,0xb1,0xc1,0x15,0x52,0xd1,0xf0,0x24,0x33,0x62,0x72,0x82,0x09,0x0a,0x16,0x17,0x18,0x19,0x1a,0x25,0x26,0x27,0x28, 0x29,0x2a,0x34,0x35,0x36,0x37,0x38,0x39,0x3a,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4a,0x53,0x54,0x55,0x56,0x57,0x58,0x59, 0x5a,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6a,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7a,0x83,0x84,0x85,0x86,0x87,0x88,0x89, 0x8a,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9a,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7,0xa8,0xa9,0xaa,0xb2,0xb3,0xb4,0xb5,0xb6, 0xb7,0xb8,0xb9,0xba,0xc2,0xc3,0xc4,0xc5,0xc6,0xc7,0xc8,0xc9,0xca,0xd2,0xd3,0xd4,0xd5,0xd6,0xd7,0xd8,0xd9,0xda,0xe1,0xe2, 0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,0xea,0xf1,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8,0xf9,0xfa }; static const unsigned char std_dc_chrominance_nrcodes[] = {0,0,3,1,1,1,1,1,1,1,1,1,0,0,0,0,0}; static const unsigned char std_dc_chrominance_values[] = {0,1,2,3,4,5,6,7,8,9,10,11}; static const unsigned char std_ac_chrominance_nrcodes[] = {0,0,2,1,2,4,4,3,4,7,5,4,4,0,1,2,0x77}; static const unsigned char std_ac_chrominance_values[] = { 0x00,0x01,0x02,0x03,0x11,0x04,0x05,0x21,0x31,0x06,0x12,0x41,0x51,0x07,0x61,0x71,0x13,0x22,0x32,0x81,0x08,0x14,0x42,0x91, 0xa1,0xb1,0xc1,0x09,0x23,0x33,0x52,0xf0,0x15,0x62,0x72,0xd1,0x0a,0x16,0x24,0x34,0xe1,0x25,0xf1,0x17,0x18,0x19,0x1a,0x26, 0x27,0x28,0x29,0x2a,0x35,0x36,0x37,0x38,0x39,0x3a,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4a,0x53,0x54,0x55,0x56,0x57,0x58, 0x59,0x5a,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6a,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7a,0x82,0x83,0x84,0x85,0x86,0x87, 0x88,0x89,0x8a,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9a,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7,0xa8,0xa9,0xaa,0xb2,0xb3,0xb4, 0xb5,0xb6,0xb7,0xb8,0xb9,0xba,0xc2,0xc3,0xc4,0xc5,0xc6,0xc7,0xc8,0xc9,0xca,0xd2,0xd3,0xd4,0xd5,0xd6,0xd7,0xd8,0xd9,0xda, 0xe2,0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,0xea,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8,0xf9,0xfa }; // Huffman tables static const unsigned short YDC_HT[256][2] = { {0,2},{2,3},{3,3},{4,3},{5,3},{6,3},{14,4},{30,5},{62,6},{126,7},{254,8},{510,9}}; static const unsigned short UVDC_HT[256][2] = { {0,2},{1,2},{2,2},{6,3},{14,4},{30,5},{62,6},{126,7},{254,8},{510,9},{1022,10},{2046,11}}; static const unsigned short YAC_HT[256][2] = { {10,4},{0,2},{1,2},{4,3},{11,4},{26,5},{120,7},{248,8},{1014,10},{65410,16},{65411,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, {12,4},{27,5},{121,7},{502,9},{2038,11},{65412,16},{65413,16},{65414,16},{65415,16},{65416,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, {28,5},{249,8},{1015,10},{4084,12},{65417,16},{65418,16},{65419,16},{65420,16},{65421,16},{65422,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, {58,6},{503,9},{4085,12},{65423,16},{65424,16},{65425,16},{65426,16},{65427,16},{65428,16},{65429,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, {59,6},{1016,10},{65430,16},{65431,16},{65432,16},{65433,16},{65434,16},{65435,16},{65436,16},{65437,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, {122,7},{2039,11},{65438,16},{65439,16},{65440,16},{65441,16},{65442,16},{65443,16},{65444,16},{65445,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, {123,7},{4086,12},{65446,16},{65447,16},{65448,16},{65449,16},{65450,16},{65451,16},{65452,16},{65453,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, {250,8},{4087,12},{65454,16},{65455,16},{65456,16},{65457,16},{65458,16},{65459,16},{65460,16},{65461,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, {504,9},{32704,15},{65462,16},{65463,16},{65464,16},{65465,16},{65466,16},{65467,16},{65468,16},{65469,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, {505,9},{65470,16},{65471,16},{65472,16},{65473,16},{65474,16},{65475,16},{65476,16},{65477,16},{65478,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, {506,9},{65479,16},{65480,16},{65481,16},{65482,16},{65483,16},{65484,16},{65485,16},{65486,16},{65487,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, {1017,10},{65488,16},{65489,16},{65490,16},{65491,16},{65492,16},{65493,16},{65494,16},{65495,16},{65496,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, {1018,10},{65497,16},{65498,16},{65499,16},{65500,16},{65501,16},{65502,16},{65503,16},{65504,16},{65505,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, {2040,11},{65506,16},{65507,16},{65508,16},{65509,16},{65510,16},{65511,16},{65512,16},{65513,16},{65514,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, {65515,16},{65516,16},{65517,16},{65518,16},{65519,16},{65520,16},{65521,16},{65522,16},{65523,16},{65524,16},{0,0},{0,0},{0,0},{0,0},{0,0}, {2041,11},{65525,16},{65526,16},{65527,16},{65528,16},{65529,16},{65530,16},{65531,16},{65532,16},{65533,16},{65534,16},{0,0},{0,0},{0,0},{0,0},{0,0} }; static const unsigned short UVAC_HT[256][2] = { {0,2},{1,2},{4,3},{10,4},{24,5},{25,5},{56,6},{120,7},{500,9},{1014,10},{4084,12},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, {11,4},{57,6},{246,8},{501,9},{2038,11},{4085,12},{65416,16},{65417,16},{65418,16},{65419,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, {26,5},{247,8},{1015,10},{4086,12},{32706,15},{65420,16},{65421,16},{65422,16},{65423,16},{65424,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, {27,5},{248,8},{1016,10},{4087,12},{65425,16},{65426,16},{65427,16},{65428,16},{65429,16},{65430,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, {58,6},{502,9},{65431,16},{65432,16},{65433,16},{65434,16},{65435,16},{65436,16},{65437,16},{65438,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, {59,6},{1017,10},{65439,16},{65440,16},{65441,16},{65442,16},{65443,16},{65444,16},{65445,16},{65446,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, {121,7},{2039,11},{65447,16},{65448,16},{65449,16},{65450,16},{65451,16},{65452,16},{65453,16},{65454,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, {122,7},{2040,11},{65455,16},{65456,16},{65457,16},{65458,16},{65459,16},{65460,16},{65461,16},{65462,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, {249,8},{65463,16},{65464,16},{65465,16},{65466,16},{65467,16},{65468,16},{65469,16},{65470,16},{65471,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, {503,9},{65472,16},{65473,16},{65474,16},{65475,16},{65476,16},{65477,16},{65478,16},{65479,16},{65480,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, {504,9},{65481,16},{65482,16},{65483,16},{65484,16},{65485,16},{65486,16},{65487,16},{65488,16},{65489,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, {505,9},{65490,16},{65491,16},{65492,16},{65493,16},{65494,16},{65495,16},{65496,16},{65497,16},{65498,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, {506,9},{65499,16},{65500,16},{65501,16},{65502,16},{65503,16},{65504,16},{65505,16},{65506,16},{65507,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, {2041,11},{65508,16},{65509,16},{65510,16},{65511,16},{65512,16},{65513,16},{65514,16},{65515,16},{65516,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, {16352,14},{65517,16},{65518,16},{65519,16},{65520,16},{65521,16},{65522,16},{65523,16},{65524,16},{65525,16},{0,0},{0,0},{0,0},{0,0},{0,0}, {1018,10},{32707,15},{65526,16},{65527,16},{65528,16},{65529,16},{65530,16},{65531,16},{65532,16},{65533,16},{65534,16},{0,0},{0,0},{0,0},{0,0},{0,0} }; static const int YQT[] = {16,11,10,16,24,40,51,61,12,12,14,19,26,58,60,55,14,13,16,24,40,57,69,56,14,17,22,29,51,87,80,62,18,22, 37,56,68,109,103,77,24,35,55,64,81,104,113,92,49,64,78,87,103,121,120,101,72,92,95,98,112,100,103,99}; static const int UVQT[] = {17,18,24,47,99,99,99,99,18,21,26,66,99,99,99,99,24,26,56,99,99,99,99,99,47,66,99,99,99,99,99,99, 99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99}; static const float aasf[] = { 1.0f * 2.828427125f, 1.387039845f * 2.828427125f, 1.306562965f * 2.828427125f, 1.175875602f * 2.828427125f, 1.0f * 2.828427125f, 0.785694958f * 2.828427125f, 0.541196100f * 2.828427125f, 0.275899379f * 2.828427125f }; int row, col, i, k, subsample; float fdtbl_Y[64], fdtbl_UV[64]; unsigned char YTable[64], UVTable[64]; if(!data || !width || !height || comp > 4 || comp < 1) { return 0; } quality = quality ? quality : 90; subsample = quality <= 90 ? 1 : 0; quality = quality < 1 ? 1 : quality > 100 ? 100 : quality; quality = quality < 50 ? 5000 / quality : 200 - quality * 2; for(i = 0; i < 64; ++i) { int uvti, yti = (YQT[i]*quality+50)/100; YTable[stbiw__jpg_ZigZag[i]] = (unsigned char) (yti < 1 ? 1 : yti > 255 ? 255 : yti); uvti = (UVQT[i]*quality+50)/100; UVTable[stbiw__jpg_ZigZag[i]] = (unsigned char) (uvti < 1 ? 1 : uvti > 255 ? 255 : uvti); } for(row = 0, k = 0; row < 8; ++row) { for(col = 0; col < 8; ++col, ++k) { fdtbl_Y[k] = 1 / (YTable [stbiw__jpg_ZigZag[k]] * aasf[row] * aasf[col]); fdtbl_UV[k] = 1 / (UVTable[stbiw__jpg_ZigZag[k]] * aasf[row] * aasf[col]); } } // Write Headers { static const unsigned char head0[] = { 0xFF,0xD8,0xFF,0xE0,0,0x10,'J','F','I','F',0,1,1,0,0,1,0,1,0,0,0xFF,0xDB,0,0x84,0 }; static const unsigned char head2[] = { 0xFF,0xDA,0,0xC,3,1,0,2,0x11,3,0x11,0,0x3F,0 }; const unsigned char head1[] = { 0xFF,0xC0,0,0x11,8,(unsigned char)(height>>8),STBIW_UCHAR(height),(unsigned char)(width>>8),STBIW_UCHAR(width), 3,1,(unsigned char)(subsample?0x22:0x11),0,2,0x11,1,3,0x11,1,0xFF,0xC4,0x01,0xA2,0 }; s->func(s->context, (void*)head0, sizeof(head0)); s->func(s->context, (void*)YTable, sizeof(YTable)); stbiw__putc(s, 1); s->func(s->context, UVTable, sizeof(UVTable)); s->func(s->context, (void*)head1, sizeof(head1)); s->func(s->context, (void*)(std_dc_luminance_nrcodes+1), sizeof(std_dc_luminance_nrcodes)-1); s->func(s->context, (void*)std_dc_luminance_values, sizeof(std_dc_luminance_values)); stbiw__putc(s, 0x10); // HTYACinfo s->func(s->context, (void*)(std_ac_luminance_nrcodes+1), sizeof(std_ac_luminance_nrcodes)-1); s->func(s->context, (void*)std_ac_luminance_values, sizeof(std_ac_luminance_values)); stbiw__putc(s, 1); // HTUDCinfo s->func(s->context, (void*)(std_dc_chrominance_nrcodes+1), sizeof(std_dc_chrominance_nrcodes)-1); s->func(s->context, (void*)std_dc_chrominance_values, sizeof(std_dc_chrominance_values)); stbiw__putc(s, 0x11); // HTUACinfo s->func(s->context, (void*)(std_ac_chrominance_nrcodes+1), sizeof(std_ac_chrominance_nrcodes)-1); s->func(s->context, (void*)std_ac_chrominance_values, sizeof(std_ac_chrominance_values)); s->func(s->context, (void*)head2, sizeof(head2)); } // Encode 8x8 macroblocks { static const unsigned short fillBits[] = {0x7F, 7}; int DCY=0, DCU=0, DCV=0; int bitBuf=0, bitCnt=0; // comp == 2 is grey+alpha (alpha is ignored) int ofsG = comp > 2 ? 1 : 0, ofsB = comp > 2 ? 2 : 0; const unsigned char *dataR = (const unsigned char *)data; const unsigned char *dataG = dataR + ofsG; const unsigned char *dataB = dataR + ofsB; int x, y, pos; if(subsample) { for(y = 0; y < height; y += 16) { for(x = 0; x < width; x += 16) { float Y[256], U[256], V[256]; for(row = y, pos = 0; row < y+16; ++row) { // row >= height => use last input row int clamped_row = (row < height) ? row : height - 1; int base_p = (stbi__flip_vertically_on_write ? (height-1-clamped_row) : clamped_row)*width*comp; for(col = x; col < x+16; ++col, ++pos) { // if col >= width => use pixel from last input column int p = base_p + ((col < width) ? col : (width-1))*comp; float r = dataR[p], g = dataG[p], b = dataB[p]; Y[pos]= +0.29900f*r + 0.58700f*g + 0.11400f*b - 128; U[pos]= -0.16874f*r - 0.33126f*g + 0.50000f*b; V[pos]= +0.50000f*r - 0.41869f*g - 0.08131f*b; } } DCY = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, Y+0, 16, fdtbl_Y, DCY, YDC_HT, YAC_HT); DCY = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, Y+8, 16, fdtbl_Y, DCY, YDC_HT, YAC_HT); DCY = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, Y+128, 16, fdtbl_Y, DCY, YDC_HT, YAC_HT); DCY = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, Y+136, 16, fdtbl_Y, DCY, YDC_HT, YAC_HT); // subsample U,V { float subU[64], subV[64]; int yy, xx; for(yy = 0, pos = 0; yy < 8; ++yy) { for(xx = 0; xx < 8; ++xx, ++pos) { int j = yy*32+xx*2; subU[pos] = (U[j+0] + U[j+1] + U[j+16] + U[j+17]) * 0.25f; subV[pos] = (V[j+0] + V[j+1] + V[j+16] + V[j+17]) * 0.25f; } } DCU = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, subU, 8, fdtbl_UV, DCU, UVDC_HT, UVAC_HT); DCV = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, subV, 8, fdtbl_UV, DCV, UVDC_HT, UVAC_HT); } } } } else { for(y = 0; y < height; y += 8) { for(x = 0; x < width; x += 8) { float Y[64], U[64], V[64]; for(row = y, pos = 0; row < y+8; ++row) { // row >= height => use last input row int clamped_row = (row < height) ? row : height - 1; int base_p = (stbi__flip_vertically_on_write ? (height-1-clamped_row) : clamped_row)*width*comp; for(col = x; col < x+8; ++col, ++pos) { // if col >= width => use pixel from last input column int p = base_p + ((col < width) ? col : (width-1))*comp; float r = dataR[p], g = dataG[p], b = dataB[p]; Y[pos]= +0.29900f*r + 0.58700f*g + 0.11400f*b - 128; U[pos]= -0.16874f*r - 0.33126f*g + 0.50000f*b; V[pos]= +0.50000f*r - 0.41869f*g - 0.08131f*b; } } DCY = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, Y, 8, fdtbl_Y, DCY, YDC_HT, YAC_HT); DCU = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, U, 8, fdtbl_UV, DCU, UVDC_HT, UVAC_HT); DCV = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, V, 8, fdtbl_UV, DCV, UVDC_HT, UVAC_HT); } } } // Do the bit alignment of the EOI marker stbiw__jpg_writeBits(s, &bitBuf, &bitCnt, fillBits); } // EOI stbiw__putc(s, 0xFF); stbiw__putc(s, 0xD9); return 1; } STBIWDEF int stbi_write_jpg_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data, int quality) { stbi__write_context s = { 0 }; stbi__start_write_callbacks(&s, func, context); return stbi_write_jpg_core(&s, x, y, comp, (void *) data, quality); } #ifndef STBI_WRITE_NO_STDIO STBIWDEF int stbi_write_jpg(char const *filename, int x, int y, int comp, const void *data, int quality) { stbi__write_context s = { 0 }; if (stbi__start_write_file(&s,filename)) { int r = stbi_write_jpg_core(&s, x, y, comp, data, quality); stbi__end_write_file(&s); return r; } else return 0; } #endif #endif // STB_IMAGE_WRITE_IMPLEMENTATION /* Revision history 1.14 (2020-02-02) updated JPEG writer to downsample chroma channels 1.13 1.12 1.11 (2019-08-11) 1.10 (2019-02-07) support utf8 filenames in Windows; fix warnings and platform ifdefs 1.09 (2018-02-11) fix typo in zlib quality API, improve STB_I_W_STATIC in C++ 1.08 (2018-01-29) add stbi__flip_vertically_on_write, external zlib, zlib quality, choose PNG filter 1.07 (2017-07-24) doc fix 1.06 (2017-07-23) writing JPEG (using Jon Olick's code) 1.05 ??? 1.04 (2017-03-03) monochrome BMP expansion 1.03 ??? 1.02 (2016-04-02) avoid allocating large structures on the stack 1.01 (2016-01-16) STBIW_REALLOC_SIZED: support allocators with no realloc support avoid race-condition in crc initialization minor compile issues 1.00 (2015-09-14) installable file IO function 0.99 (2015-09-13) warning fixes; TGA rle support 0.98 (2015-04-08) added STBIW_MALLOC, STBIW_ASSERT etc 0.97 (2015-01-18) fixed HDR asserts, rewrote HDR rle logic 0.96 (2015-01-17) add HDR output fix monochrome BMP 0.95 (2014-08-17) add monochrome TGA output 0.94 (2014-05-31) rename private functions to avoid conflicts with stb_image.h 0.93 (2014-05-27) warning fixes 0.92 (2010-08-01) casts to unsigned char to fix warnings 0.91 (2010-07-17) first public release 0.90 first internal release */ /* ------------------------------------------------------------------------------ This software is available under 2 licenses -- choose whichever you prefer. ------------------------------------------------------------------------------ ALTERNATIVE A - MIT License Copyright (c) 2017 Sean Barrett 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. ------------------------------------------------------------------------------ ALTERNATIVE B - Public Domain (www.unlicense.org) This is free and unencumbered software released into the public domain. Anyone is free to copy, modify, publish, use, compile, sell, or distribute this software, either in source code form or as a compiled binary, for any purpose, commercial or non-commercial, and by any means. In jurisdictions that recognize copyright laws, the author or authors of this software dedicate any and all copyright interest in the software to the public domain. We make this dedication for the benefit of the public at large and to the detriment of our heirs and successors. We intend this dedication to be an overt act of relinquishment in perpetuity of all present and future rights to this software under copyright law. 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 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. ------------------------------------------------------------------------------ */ #line 0 //--- #undef freelist #define STBTT_malloc(x,u) ((void)(u),MALLOC(x)) #define STBTT_free(x,u) ((void)(u),FREE(x)) #define NK_ASSERT ASSERT #define NK_DTOA(s,n) strcpy(s, va("%f", n)) // override cos built-in nk_dtoa() will freeze while parsing UINT_MAX otherwise #line 1 "3rd_nuklear.h" /* /// # Nuklear /// ![](https://cloud.githubusercontent.com/assets/8057201/11761525/ae06f0ca-a0c6-11e5-819d-5610b25f6ef4.gif) /// /// ## Contents /// 1. About section /// 2. Highlights section /// 3. Features section /// 4. Usage section /// 1. Flags section /// 2. Constants section /// 3. Dependencies section /// 5. Example section /// 6. API section /// 1. Context section /// 2. Input section /// 3. Drawing section /// 4. Window section /// 5. Layouting section /// 6. Groups section /// 7. Tree section /// 8. Properties section /// 7. License section /// 8. Changelog section /// 9. Gallery section /// 10. Credits section /// /// ## About /// This is a minimal state immediate mode graphical user interface toolkit /// written in ANSI C and licensed under public domain. It was designed as a simple /// embeddable user interface for application and does not have any dependencies, /// a default renderbackend or OS window and input handling but instead provides a very modular /// library approach by using simple input state for input and draw /// commands describing primitive shapes as output. So instead of providing a /// layered library that tries to abstract over a number of platform and /// render backends it only focuses on the actual UI. /// /// ## Highlights /// - Graphical user interface toolkit /// - Single header library /// - Written in C89 (a.k.a. ANSI C or ISO C90) /// - Small codebase (~18kLOC) /// - Focus on portability, efficiency and simplicity /// - No dependencies (not even the standard library if not wanted) /// - Fully skinnable and customizable /// - Low memory footprint with total memory control if needed or wanted /// - UTF-8 support /// - No global or hidden state /// - Customizable library modules (you can compile and use only what you need) /// - Optional font baker and vertex buffer output /// /// ## Features /// - Absolutely no platform dependent code /// - Memory management control ranging from/to /// - Ease of use by allocating everything from standard library /// - Control every byte of memory inside the library /// - Font handling control ranging from/to /// - Use your own font implementation for everything /// - Use this libraries internal font baking and handling API /// - Drawing output control ranging from/to /// - Simple shapes for more high level APIs which already have drawing capabilities /// - Hardware accessible anti-aliased vertex buffer output /// - Customizable colors and properties ranging from/to /// - Simple changes to color by filling a simple color table /// - Complete control with ability to use skinning to decorate widgets /// - Bendable UI library with widget ranging from/to /// - Basic widgets like buttons, checkboxes, slider, ... /// - Advanced widget like abstract comboboxes, contextual menus,... /// - Compile time configuration to only compile what you need /// - Subset which can be used if you do not want to link or use the standard library /// - Can be easily modified to only update on user input instead of frame updates /// /// ## Usage /// This library is self contained in one single header file and can be used either /// in header only mode or in implementation mode. The header only mode is used /// by default when included and allows including this header in other headers /// and does not contain the actual implementation.

/// /// The implementation mode requires to define the preprocessor macro /// NK_IMPLEMENTATION in *one* .c/.cpp file before #including this file, e.g.: /// /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~C /// #define NK_IMPLEMENTATION /// #include "nuklear.h" /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /// /// Also optionally define the symbols listed in the section "OPTIONAL DEFINES" /// below in header and implementation mode if you want to use additional functionality /// or need more control over the library. /// /// !!! WARNING /// Every time nuklear is included define the same compiler flags. This very important not doing so could lead to compiler errors or even worse stack corruptions. /// /// ### Flags /// Flag | Description /// --------------------------------|------------------------------------------ /// NK_PRIVATE | If defined declares all functions as static, so they can only be accessed inside the file that contains the implementation /// NK_INCLUDE_FIXED_TYPES | If defined it will include header `` for fixed sized types otherwise nuklear tries to select the correct type. If that fails it will throw a compiler error and you have to select the correct types yourself. /// NK_INCLUDE_DEFAULT_ALLOCATOR | If defined it will include header `` and provide additional functions to use this library without caring for memory allocation control and therefore ease memory management. /// NK_INCLUDE_STANDARD_IO | If defined it will include header `` and provide additional functions depending on file loading. /// NK_INCLUDE_STANDARD_VARARGS | If defined it will include header and provide additional functions depending on file loading. /// NK_INCLUDE_STANDARD_BOOL | If defined it will include header `` for nk_bool otherwise nuklear defines nk_bool as int. /// NK_INCLUDE_VERTEX_BUFFER_OUTPUT | Defining this adds a vertex draw command list backend to this library, which allows you to convert queue commands into vertex draw commands. This is mainly if you need a hardware accessible format for OpenGL, DirectX, Vulkan, Metal,... /// NK_INCLUDE_FONT_BAKING | Defining this adds `stb_truetype` and `stb_rect_pack` implementation to this library and provides font baking and rendering. If you already have font handling or do not want to use this font handler you don't have to define it. /// NK_INCLUDE_DEFAULT_FONT | Defining this adds the default font: ProggyClean.ttf into this library which can be loaded into a font atlas and allows using this library without having a truetype font /// NK_INCLUDE_COMMAND_USERDATA | Defining this adds a userdata pointer into each command. Can be useful for example if you want to provide custom shaders depending on the used widget. Can be combined with the style structures. /// NK_BUTTON_TRIGGER_ON_RELEASE | Different platforms require button clicks occurring either on buttons being pressed (up to down) or released (down to up). By default this library will react on buttons being pressed, but if you define this it will only trigger if a button is released. /// NK_ZERO_COMMAND_MEMORY | Defining this will zero out memory for each drawing command added to a drawing queue (inside nk_command_buffer_push). Zeroing command memory is very useful for fast checking (using memcmp) if command buffers are equal and avoid drawing frames when nothing on screen has changed since previous frame. /// NK_UINT_DRAW_INDEX | Defining this will set the size of vertex index elements when using NK_VERTEX_BUFFER_OUTPUT to 32bit instead of the default of 16bit /// NK_KEYSTATE_BASED_INPUT | Define this if your backend uses key state for each frame rather than key press/release events /// /// !!! WARNING /// The following flags will pull in the standard C library: /// - NK_INCLUDE_DEFAULT_ALLOCATOR /// - NK_INCLUDE_STANDARD_IO /// - NK_INCLUDE_STANDARD_VARARGS /// /// !!! WARNING /// The following flags if defined need to be defined for both header and implementation: /// - NK_INCLUDE_FIXED_TYPES /// - NK_INCLUDE_DEFAULT_ALLOCATOR /// - NK_INCLUDE_STANDARD_VARARGS /// - NK_INCLUDE_STANDARD_BOOL /// - NK_INCLUDE_VERTEX_BUFFER_OUTPUT /// - NK_INCLUDE_FONT_BAKING /// - NK_INCLUDE_DEFAULT_FONT /// - NK_INCLUDE_STANDARD_VARARGS /// - NK_INCLUDE_COMMAND_USERDATA /// - NK_UINT_DRAW_INDEX /// /// ### Constants /// Define | Description /// --------------------------------|--------------------------------------- /// NK_BUFFER_DEFAULT_INITIAL_SIZE | Initial buffer size allocated by all buffers while using the default allocator functions included by defining NK_INCLUDE_DEFAULT_ALLOCATOR. If you don't want to allocate the default 4k memory then redefine it. /// NK_MAX_NUMBER_BUFFER | Maximum buffer size for the conversion buffer between float and string Under normal circumstances this should be more than sufficient. /// NK_INPUT_MAX | Defines the max number of bytes which can be added as text input in one frame. Under normal circumstances this should be more than sufficient. /// /// !!! WARNING /// The following constants if defined need to be defined for both header and implementation: /// - NK_MAX_NUMBER_BUFFER /// - NK_BUFFER_DEFAULT_INITIAL_SIZE /// - NK_INPUT_MAX /// /// ### Dependencies /// Function | Description /// ------------|--------------------------------------------------------------- /// NK_ASSERT | If you don't define this, nuklear will use with assert(). /// NK_MEMSET | You can define this to 'memset' or your own memset implementation replacement. If not nuklear will use its own version. /// NK_MEMCPY | You can define this to 'memcpy' or your own memcpy implementation replacement. If not nuklear will use its own version. /// NK_INV_SQRT | You can define this to your own inverse sqrt implementation replacement. If not nuklear will use its own slow and not highly accurate version. /// NK_SIN | You can define this to 'sinf' or your own sine implementation replacement. If not nuklear will use its own approximation implementation. /// NK_COS | You can define this to 'cosf' or your own cosine implementation replacement. If not nuklear will use its own approximation implementation. /// NK_STRTOD | You can define this to `strtod` or your own string to double conversion implementation replacement. If not defined nuklear will use its own imprecise and possibly unsafe version (does not handle nan or infinity!). /// NK_DTOA | You can define this to `dtoa` or your own double to string conversion implementation replacement. If not defined nuklear will use its own imprecise and possibly unsafe version (does not handle nan or infinity!). /// NK_VSNPRINTF| If you define `NK_INCLUDE_STANDARD_VARARGS` as well as `NK_INCLUDE_STANDARD_IO` and want to be safe define this to `vsnprintf` on compilers supporting later versions of C or C++. By default nuklear will check for your stdlib version in C as well as compiler version in C++. if `vsnprintf` is available it will define it to `vsnprintf` directly. If not defined and if you have older versions of C or C++ it will be defined to `vsprintf` which is unsafe. /// /// !!! WARNING /// The following dependencies will pull in the standard C library if not redefined: /// - NK_ASSERT /// /// !!! WARNING /// The following dependencies if defined need to be defined for both header and implementation: /// - NK_ASSERT /// /// !!! WARNING /// The following dependencies if defined need to be defined only for the implementation part: /// - NK_MEMSET /// - NK_MEMCPY /// - NK_SQRT /// - NK_SIN /// - NK_COS /// - NK_STRTOD /// - NK_DTOA /// - NK_VSNPRINTF /// /// ## Example /// /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c /// // init gui state /// enum {EASY, HARD}; /// static int op = EASY; /// static float value = 0.6f; /// static int i = 20; /// struct nk_context ctx; /// /// nk_init_fixed(&ctx, calloc(1, MAX_MEMORY), MAX_MEMORY, &font); /// if (nk_begin(&ctx, "Show", nk_rect(50, 50, 220, 220), /// NK_WINDOW_BORDER|NK_WINDOW_MOVABLE|NK_WINDOW_CLOSABLE)) { /// // fixed widget pixel width /// nk_layout_row_static(&ctx, 30, 80, 1); /// if (nk_button_label(&ctx, "button")) { /// // event handling /// } /// /// // fixed widget window ratio width /// nk_layout_row_dynamic(&ctx, 30, 2); /// if (nk_option_label(&ctx, "easy", op == EASY)) op = EASY; /// if (nk_option_label(&ctx, "hard", op == HARD)) op = HARD; /// /// // custom widget pixel width /// nk_layout_row_begin(&ctx, NK_STATIC, 30, 2); /// { /// nk_layout_row_push(&ctx, 50); /// nk_label(&ctx, "Volume:", NK_TEXT_LEFT); /// nk_layout_row_push(&ctx, 110); /// nk_slider_float(&ctx, 0, &value, 1.0f, 0.1f); /// } /// nk_layout_row_end(&ctx); /// } /// nk_end(&ctx); /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /// /// ![](https://cloud.githubusercontent.com/assets/8057201/10187981/584ecd68-675c-11e5-897c-822ef534a876.png) /// /// ## API /// */ #ifndef NK_SINGLE_FILE #define NK_SINGLE_FILE #endif #ifndef NK_NUKLEAR_H_ #define NK_NUKLEAR_H_ #ifdef __cplusplus extern "C" { #endif /* * ============================================================== * * CONSTANTS * * =============================================================== */ #define NK_UNDEFINED (-1.0f) #define NK_UTF_INVALID 0xFFFD /* internal invalid utf8 rune */ #define NK_UTF_SIZE 4 /* describes the number of bytes a glyph consists of*/ #ifndef NK_INPUT_MAX #define NK_INPUT_MAX 16 #endif #ifndef NK_MAX_NUMBER_BUFFER #define NK_MAX_NUMBER_BUFFER 64 #endif #ifndef NK_SCROLLBAR_HIDING_TIMEOUT #define NK_SCROLLBAR_HIDING_TIMEOUT 4.0f #endif /* * ============================================================== * * HELPER * * =============================================================== */ #ifndef NK_API #ifdef NK_PRIVATE #if (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199409L)) #define NK_API static inline #elif defined(__cplusplus) #define NK_API static inline #else #define NK_API static #endif #else #define NK_API extern #endif #endif #ifndef NK_LIB #ifdef NK_SINGLE_FILE #define NK_LIB static #else #define NK_LIB extern #endif #endif #define NK_INTERN static #define NK_STORAGE static #define NK_GLOBAL static #define NK_FLAG(x) (1 << (x)) #define NK_STRINGIFY(x) #x #define NK_MACRO_STRINGIFY(x) NK_STRINGIFY(x) #define NK_STRING_JOIN_IMMEDIATE(arg1, arg2) arg1 ## arg2 #define NK_STRING_JOIN_DELAY(arg1, arg2) NK_STRING_JOIN_IMMEDIATE(arg1, arg2) #define NK_STRING_JOIN(arg1, arg2) NK_STRING_JOIN_DELAY(arg1, arg2) #ifdef _MSC_VER #define NK_UNIQUE_NAME(name) NK_STRING_JOIN(name,__COUNTER__) #else #define NK_UNIQUE_NAME(name) NK_STRING_JOIN(name,__LINE__) #endif #ifndef NK_STATIC_ASSERT #define NK_STATIC_ASSERT(exp) typedef char NK_UNIQUE_NAME(_dummy_array)[(exp)?1:-1] #endif #ifndef NK_FILE_LINE #ifdef _MSC_VER #define NK_FILE_LINE __FILE__ ":" NK_MACRO_STRINGIFY(__COUNTER__) #else #define NK_FILE_LINE __FILE__ ":" NK_MACRO_STRINGIFY(__LINE__) #endif #endif #define NK_MIN(a,b) ((a) < (b) ? (a) : (b)) #define NK_MAX(a,b) ((a) < (b) ? (b) : (a)) #define NK_CLAMP(i,v,x) (NK_MAX(NK_MIN(v,x), i)) #ifdef NK_INCLUDE_STANDARD_VARARGS #include #if defined(_MSC_VER) && (_MSC_VER >= 1600) /* VS 2010 and above */ #include #define NK_PRINTF_FORMAT_STRING _Printf_format_string_ #else #define NK_PRINTF_FORMAT_STRING #endif #if defined(__GNUC__) #define NK_PRINTF_VARARG_FUNC(fmtargnumber) __attribute__((format(__printf__, fmtargnumber, fmtargnumber+1))) #define NK_PRINTF_VALIST_FUNC(fmtargnumber) __attribute__((format(__printf__, fmtargnumber, 0))) #else #define NK_PRINTF_VARARG_FUNC(fmtargnumber) #define NK_PRINTF_VALIST_FUNC(fmtargnumber) #endif #endif /* * =============================================================== * * BASIC * * =============================================================== */ #ifdef NK_INCLUDE_FIXED_TYPES #include #define NK_INT8 int8_t #define NK_UINT8 uint8_t #define NK_INT16 int16_t #define NK_UINT16 uint16_t #define NK_INT32 int32_t #define NK_UINT32 uint32_t #define NK_SIZE_TYPE uintptr_t #define NK_POINTER_TYPE uintptr_t #else #ifndef NK_INT8 #define NK_INT8 signed char #endif #ifndef NK_UINT8 #define NK_UINT8 unsigned char #endif #ifndef NK_INT16 #define NK_INT16 signed short #endif #ifndef NK_UINT16 #define NK_UINT16 unsigned short #endif #ifndef NK_INT32 #if defined(_MSC_VER) #define NK_INT32 __int32 #else #define NK_INT32 signed int #endif #endif #ifndef NK_UINT32 #if defined(_MSC_VER) #define NK_UINT32 unsigned __int32 #else #define NK_UINT32 unsigned int #endif #endif #ifndef NK_SIZE_TYPE #if defined(_WIN64) && defined(_MSC_VER) #define NK_SIZE_TYPE unsigned __int64 #elif (defined(_WIN32) || defined(WIN32)) && defined(_MSC_VER) #define NK_SIZE_TYPE unsigned __int32 #elif defined(__GNUC__) || defined(__clang__) #if defined(__x86_64__) || defined(__ppc64__) #define NK_SIZE_TYPE unsigned long #else #define NK_SIZE_TYPE unsigned int #endif #else #define NK_SIZE_TYPE unsigned long #endif #endif #ifndef NK_POINTER_TYPE #if defined(_WIN64) && defined(_MSC_VER) #define NK_POINTER_TYPE unsigned __int64 #elif (defined(_WIN32) || defined(WIN32)) && defined(_MSC_VER) #define NK_POINTER_TYPE unsigned __int32 #elif defined(__GNUC__) || defined(__clang__) #if defined(__x86_64__) || defined(__ppc64__) #define NK_POINTER_TYPE unsigned long #else #define NK_POINTER_TYPE unsigned int #endif #else #define NK_POINTER_TYPE unsigned long #endif #endif #endif #ifndef NK_BOOL #ifdef NK_INCLUDE_STANDARD_BOOL #include #define NK_BOOL bool #else #define NK_BOOL int /* could be char, use int for drop-in replacement backwards compatibility */ #endif #endif typedef NK_INT8 nk_char; typedef NK_UINT8 nk_uchar; typedef NK_UINT8 nk_byte; typedef NK_INT16 nk_short; typedef NK_UINT16 nk_ushort; typedef NK_INT32 nk_int; typedef NK_UINT32 nk_uint; typedef NK_SIZE_TYPE nk_size; typedef NK_POINTER_TYPE nk_ptr; typedef NK_BOOL nk_bool; typedef nk_uint nk_hash; typedef nk_uint nk_flags; typedef nk_uint nk_rune; /* Make sure correct type size: * This will fire with a negative subscript error if the type sizes * are set incorrectly by the compiler, and compile out if not */ NK_STATIC_ASSERT(sizeof(nk_short) == 2); NK_STATIC_ASSERT(sizeof(nk_ushort) == 2); NK_STATIC_ASSERT(sizeof(nk_uint) == 4); NK_STATIC_ASSERT(sizeof(nk_int) == 4); NK_STATIC_ASSERT(sizeof(nk_byte) == 1); NK_STATIC_ASSERT(sizeof(nk_flags) >= 4); NK_STATIC_ASSERT(sizeof(nk_rune) >= 4); NK_STATIC_ASSERT(sizeof(nk_size) >= sizeof(void*)); NK_STATIC_ASSERT(sizeof(nk_ptr) >= sizeof(void*)); #ifdef NK_INCLUDE_STANDARD_BOOL NK_STATIC_ASSERT(sizeof(nk_bool) == sizeof(bool)); #else NK_STATIC_ASSERT(sizeof(nk_bool) >= 2); #endif /* ============================================================================ * * API * * =========================================================================== */ struct nk_buffer; struct nk_allocator; struct nk_command_buffer; struct nk_draw_command; struct nk_convert_config; struct nk_style_item; struct nk_text_edit; struct nk_draw_list; struct nk_user_font; struct nk_panel; struct nk_context; struct nk_draw_vertex_layout_element; struct nk_style_button; struct nk_style_toggle; struct nk_style_selectable; struct nk_style_slide; struct nk_style_progress; struct nk_style_scrollbar; struct nk_style_edit; struct nk_style_property; struct nk_style_chart; struct nk_style_combo; struct nk_style_tab; struct nk_style_window_header; struct nk_style_window; enum {nk_false, nk_true}; struct nk_color {nk_byte r,g,b,a;}; struct nk_colorf {float r,g,b,a;}; struct nk_vec2 {float x,y;}; struct nk_vec2i {short x, y;}; struct nk_rect {float x,y,w,h;}; struct nk_recti {short x,y,w,h;}; typedef char nk_glyph[NK_UTF_SIZE]; typedef union {void *ptr; int id;} nk_handle; struct nk_image {nk_handle handle; nk_ushort w, h; nk_ushort region[4];}; struct nk_nine_slice {struct nk_image img; nk_ushort l, t, r, b;}; struct nk_cursor {struct nk_image img; struct nk_vec2 size, offset;}; struct nk_scroll {nk_uint x, y;}; enum nk_heading {NK_UP, NK_RIGHT, NK_DOWN, NK_LEFT}; enum nk_button_behavior {NK_BUTTON_DEFAULT, NK_BUTTON_REPEATER}; enum nk_modify {NK_FIXED = nk_false, NK_MODIFIABLE = nk_true}; enum nk_orientation {NK_VERTICAL, NK_HORIZONTAL}; enum nk_collapse_states {NK_MINIMIZED = nk_false, NK_MAXIMIZED = nk_true}; enum nk_show_states {NK_HIDDEN = nk_false, NK_SHOWN = nk_true}; enum nk_chart_type {NK_CHART_LINES, NK_CHART_COLUMN, NK_CHART_MAX}; enum nk_chart_event {NK_CHART_HOVERING = 0x01, NK_CHART_CLICKED = 0x02}; enum nk_color_format {NK_RGB, NK_RGBA}; enum nk_popup_type {NK_POPUP_STATIC, NK_POPUP_DYNAMIC}; enum nk_layout_format {NK_DYNAMIC, NK_STATIC}; enum nk_tree_type {NK_TREE_NODE, NK_TREE_TAB}; typedef void*(*nk_plugin_alloc)(nk_handle, void *old, nk_size); typedef void (*nk_plugin_free)(nk_handle, void *old); typedef nk_bool(*nk_plugin_filter)(const struct nk_text_edit*, nk_rune unicode); typedef void(*nk_plugin_paste)(nk_handle, struct nk_text_edit*); typedef void(*nk_plugin_copy)(nk_handle, const char*, int len); struct nk_allocator { nk_handle userdata; nk_plugin_alloc alloc; nk_plugin_free free; }; enum nk_symbol_type { NK_SYMBOL_NONE, NK_SYMBOL_X, NK_SYMBOL_UNDERSCORE, NK_SYMBOL_CIRCLE_SOLID, NK_SYMBOL_CIRCLE_OUTLINE, NK_SYMBOL_RECT_SOLID, NK_SYMBOL_RECT_OUTLINE, NK_SYMBOL_TRIANGLE_UP, NK_SYMBOL_TRIANGLE_DOWN, NK_SYMBOL_TRIANGLE_LEFT, NK_SYMBOL_TRIANGLE_RIGHT, NK_SYMBOL_PLUS, NK_SYMBOL_MINUS, NK_SYMBOL_PIN, //< @-rlyeh NK_SYMBOL_FLOATING, //< @r-lyeh NK_SYMBOL_FULLSCREEN, //< @r-lyeh NK_SYMBOL_RESTORE, //< @r-lyeh NK_SYMBOL_MAX }; /* ============================================================================= * * CONTEXT * * =============================================================================*/ /*/// ### Context /// Contexts are the main entry point and the majestro of nuklear and contain all required state. /// They are used for window, memory, input, style, stack, commands and time management and need /// to be passed into all nuklear GUI specific functions. /// /// #### Usage /// To use a context it first has to be initialized which can be achieved by calling /// one of either `nk_init_default`, `nk_init_fixed`, `nk_init`, `nk_init_custom`. /// Each takes in a font handle and a specific way of handling memory. Memory control /// hereby ranges from standard library to just specifying a fixed sized block of memory /// which nuklear has to manage itself from. /// /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c /// struct nk_context ctx; /// nk_init_xxx(&ctx, ...); /// while (1) { /// // [...] /// nk_clear(&ctx); /// } /// nk_free(&ctx); /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /// /// #### Reference /// Function | Description /// --------------------|------------------------------------------------------- /// __nk_init_default__ | Initializes context with standard library memory allocation (malloc,free) /// __nk_init_fixed__ | Initializes context from single fixed size memory block /// __nk_init__ | Initializes context with memory allocator callbacks for alloc and free /// __nk_init_custom__ | Initializes context from two buffers. One for draw commands the other for window/panel/table allocations /// __nk_clear__ | Called at the end of the frame to reset and prepare the context for the next frame /// __nk_free__ | Shutdown and free all memory allocated inside the context /// __nk_set_user_data__| Utility function to pass user data to draw command */ #ifdef NK_INCLUDE_DEFAULT_ALLOCATOR /*/// #### nk_init_default /// Initializes a `nk_context` struct with a default standard library allocator. /// Should be used if you don't want to be bothered with memory management in nuklear. /// /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c /// nk_bool nk_init_default(struct nk_context *ctx, const struct nk_user_font *font); /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /// /// Parameter | Description /// ------------|--------------------------------------------------------------- /// __ctx__ | Must point to an either stack or heap allocated `nk_context` struct /// __font__ | Must point to a previously initialized font handle for more info look at font documentation /// /// Returns either `false(0)` on failure or `true(1)` on success. /// */ NK_API nk_bool nk_init_default(struct nk_context*, const struct nk_user_font*); #endif /*/// #### nk_init_fixed /// Initializes a `nk_context` struct from single fixed size memory block /// Should be used if you want complete control over nuklear's memory management. /// Especially recommended for system with little memory or systems with virtual memory. /// For the later case you can just allocate for example 16MB of virtual memory /// and only the required amount of memory will actually be committed. /// /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c /// nk_bool nk_init_fixed(struct nk_context *ctx, void *memory, nk_size size, const struct nk_user_font *font); /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /// /// !!! Warning /// make sure the passed memory block is aligned correctly for `nk_draw_commands`. /// /// Parameter | Description /// ------------|-------------------------------------------------------------- /// __ctx__ | Must point to an either stack or heap allocated `nk_context` struct /// __memory__ | Must point to a previously allocated memory block /// __size__ | Must contain the total size of __memory__ /// __font__ | Must point to a previously initialized font handle for more info look at font documentation /// /// Returns either `false(0)` on failure or `true(1)` on success. */ NK_API nk_bool nk_init_fixed(struct nk_context*, void *memory, nk_size size, const struct nk_user_font*); /*/// #### nk_init /// Initializes a `nk_context` struct with memory allocation callbacks for nuklear to allocate /// memory from. Used internally for `nk_init_default` and provides a kitchen sink allocation /// interface to nuklear. Can be useful for cases like monitoring memory consumption. /// /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c /// nk_bool nk_init(struct nk_context *ctx, struct nk_allocator *alloc, const struct nk_user_font *font); /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /// /// Parameter | Description /// ------------|--------------------------------------------------------------- /// __ctx__ | Must point to an either stack or heap allocated `nk_context` struct /// __alloc__ | Must point to a previously allocated memory allocator /// __font__ | Must point to a previously initialized font handle for more info look at font documentation /// /// Returns either `false(0)` on failure or `true(1)` on success. */ NK_API nk_bool nk_init(struct nk_context*, struct nk_allocator*, const struct nk_user_font*); /*/// #### nk_init_custom /// Initializes a `nk_context` struct from two different either fixed or growing /// buffers. The first buffer is for allocating draw commands while the second buffer is /// used for allocating windows, panels and state tables. /// /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c /// nk_bool nk_init_custom(struct nk_context *ctx, struct nk_buffer *cmds, struct nk_buffer *pool, const struct nk_user_font *font); /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /// /// Parameter | Description /// ------------|--------------------------------------------------------------- /// __ctx__ | Must point to an either stack or heap allocated `nk_context` struct /// __cmds__ | Must point to a previously initialized memory buffer either fixed or dynamic to store draw commands into /// __pool__ | Must point to a previously initialized memory buffer either fixed or dynamic to store windows, panels and tables /// __font__ | Must point to a previously initialized font handle for more info look at font documentation /// /// Returns either `false(0)` on failure or `true(1)` on success. */ NK_API nk_bool nk_init_custom(struct nk_context*, struct nk_buffer *cmds, struct nk_buffer *pool, const struct nk_user_font*); /*/// #### nk_clear /// Resets the context state at the end of the frame. This includes mostly /// garbage collector tasks like removing windows or table not called and therefore /// used anymore. /// /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c /// void nk_clear(struct nk_context *ctx); /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /// /// Parameter | Description /// ------------|----------------------------------------------------------- /// __ctx__ | Must point to a previously initialized `nk_context` struct */ NK_API void nk_clear(struct nk_context*); /*/// #### nk_free /// Frees all memory allocated by nuklear. Not needed if context was /// initialized with `nk_init_fixed`. /// /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c /// void nk_free(struct nk_context *ctx); /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /// /// Parameter | Description /// ------------|----------------------------------------------------------- /// __ctx__ | Must point to a previously initialized `nk_context` struct */ NK_API void nk_free(struct nk_context*); #ifdef NK_INCLUDE_COMMAND_USERDATA /*/// #### nk_set_user_data /// Sets the currently passed userdata passed down into each draw command. /// /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c /// void nk_set_user_data(struct nk_context *ctx, nk_handle data); /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /// /// Parameter | Description /// ------------|-------------------------------------------------------------- /// __ctx__ | Must point to a previously initialized `nk_context` struct /// __data__ | Handle with either pointer or index to be passed into every draw commands */ NK_API void nk_set_user_data(struct nk_context*, nk_handle handle); #endif /* ============================================================================= * * INPUT * * =============================================================================*/ /*/// ### Input /// The input API is responsible for holding the current input state composed of /// mouse, key and text input states. /// It is worth noting that no direct OS or window handling is done in nuklear. /// Instead all input state has to be provided by platform specific code. This on one hand /// expects more work from the user and complicates usage but on the other hand /// provides simple abstraction over a big number of platforms, libraries and other /// already provided functionality. /// /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c /// nk_input_begin(&ctx); /// while (GetEvent(&evt)) { /// if (evt.type == MOUSE_MOVE) /// nk_input_motion(&ctx, evt.motion.x, evt.motion.y); /// else if (evt.type == [...]) { /// // [...] /// } /// } nk_input_end(&ctx); /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /// /// #### Usage /// Input state needs to be provided to nuklear by first calling `nk_input_begin` /// which resets internal state like delta mouse position and button transitions. /// After `nk_input_begin` all current input state needs to be provided. This includes /// mouse motion, button and key pressed and released, text input and scrolling. /// Both event- or state-based input handling are supported by this API /// and should work without problems. Finally after all input state has been /// mirrored `nk_input_end` needs to be called to finish input process. /// /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c /// struct nk_context ctx; /// nk_init_xxx(&ctx, ...); /// while (1) { /// Event evt; /// nk_input_begin(&ctx); /// while (GetEvent(&evt)) { /// if (evt.type == MOUSE_MOVE) /// nk_input_motion(&ctx, evt.motion.x, evt.motion.y); /// else if (evt.type == [...]) { /// // [...] /// } /// } /// nk_input_end(&ctx); /// // [...] /// nk_clear(&ctx); /// } nk_free(&ctx); /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /// /// #### Reference /// Function | Description /// --------------------|------------------------------------------------------- /// __nk_input_begin__ | Begins the input mirroring process. Needs to be called before all other `nk_input_xxx` calls /// __nk_input_motion__ | Mirrors mouse cursor position /// __nk_input_key__ | Mirrors key state with either pressed or released /// __nk_input_button__ | Mirrors mouse button state with either pressed or released /// __nk_input_scroll__ | Mirrors mouse scroll values /// __nk_input_char__ | Adds a single ASCII text character into an internal text buffer /// __nk_input_glyph__ | Adds a single multi-byte UTF-8 character into an internal text buffer /// __nk_input_unicode__| Adds a single unicode rune into an internal text buffer /// __nk_input_end__ | Ends the input mirroring process by calculating state changes. Don't call any `nk_input_xxx` function referenced above after this call */ enum nk_keys { NK_KEY_NONE, NK_KEY_SHIFT, NK_KEY_CTRL, NK_KEY_DEL, NK_KEY_ENTER, NK_KEY_TAB, NK_KEY_BACKSPACE, NK_KEY_COPY, NK_KEY_CUT, NK_KEY_PASTE, NK_KEY_UP, NK_KEY_DOWN, NK_KEY_LEFT, NK_KEY_RIGHT, /* Shortcuts: text field */ NK_KEY_TEXT_INSERT_MODE, NK_KEY_TEXT_REPLACE_MODE, NK_KEY_TEXT_RESET_MODE, NK_KEY_TEXT_LINE_START, NK_KEY_TEXT_LINE_END, NK_KEY_TEXT_START, NK_KEY_TEXT_END, NK_KEY_TEXT_UNDO, NK_KEY_TEXT_REDO, NK_KEY_TEXT_SELECT_ALL, NK_KEY_TEXT_WORD_LEFT, NK_KEY_TEXT_WORD_RIGHT, /* Shortcuts: scrollbar */ NK_KEY_SCROLL_START, NK_KEY_SCROLL_END, NK_KEY_SCROLL_DOWN, NK_KEY_SCROLL_UP, NK_KEY_MAX }; enum nk_buttons { NK_BUTTON_LEFT, NK_BUTTON_MIDDLE, NK_BUTTON_RIGHT, NK_BUTTON_DOUBLE, NK_BUTTON_MAX }; /*/// #### nk_input_begin /// Begins the input mirroring process by resetting text, scroll /// mouse, previous mouse position and movement as well as key state transitions, /// /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c /// void nk_input_begin(struct nk_context*); /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /// /// Parameter | Description /// ------------|----------------------------------------------------------- /// __ctx__ | Must point to a previously initialized `nk_context` struct */ NK_API void nk_input_begin(struct nk_context*); /*/// #### nk_input_motion /// Mirrors current mouse position to nuklear /// /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c /// void nk_input_motion(struct nk_context *ctx, int x, int y); /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /// /// Parameter | Description /// ------------|----------------------------------------------------------- /// __ctx__ | Must point to a previously initialized `nk_context` struct /// __x__ | Must hold an integer describing the current mouse cursor x-position /// __y__ | Must hold an integer describing the current mouse cursor y-position */ NK_API void nk_input_motion(struct nk_context*, int x, int y); /*/// #### nk_input_key /// Mirrors the state of a specific key to nuklear /// /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c /// void nk_input_key(struct nk_context*, enum nk_keys key, nk_bool down); /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /// /// Parameter | Description /// ------------|----------------------------------------------------------- /// __ctx__ | Must point to a previously initialized `nk_context` struct /// __key__ | Must be any value specified in enum `nk_keys` that needs to be mirrored /// __down__ | Must be 0 for key is up and 1 for key is down */ NK_API void nk_input_key(struct nk_context*, enum nk_keys, nk_bool down); /*/// #### nk_input_button /// Mirrors the state of a specific mouse button to nuklear /// /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c /// void nk_input_button(struct nk_context *ctx, enum nk_buttons btn, int x, int y, nk_bool down); /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /// /// Parameter | Description /// ------------|----------------------------------------------------------- /// __ctx__ | Must point to a previously initialized `nk_context` struct /// __btn__ | Must be any value specified in enum `nk_buttons` that needs to be mirrored /// __x__ | Must contain an integer describing mouse cursor x-position on click up/down /// __y__ | Must contain an integer describing mouse cursor y-position on click up/down /// __down__ | Must be 0 for key is up and 1 for key is down */ NK_API void nk_input_button(struct nk_context*, enum nk_buttons, int x, int y, nk_bool down); /*/// #### nk_input_scroll /// Copies the last mouse scroll value to nuklear. Is generally /// a scroll value. So does not have to come from mouse and could also originate /// TODO finish this sentence /// /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c /// void nk_input_scroll(struct nk_context *ctx, struct nk_vec2 val); /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /// /// Parameter | Description /// ------------|----------------------------------------------------------- /// __ctx__ | Must point to a previously initialized `nk_context` struct /// __val__ | vector with both X- as well as Y-scroll value */ NK_API void nk_input_scroll(struct nk_context*, struct nk_vec2 val); /*/// #### nk_input_char /// Copies a single ASCII character into an internal text buffer /// This is basically a helper function to quickly push ASCII characters into /// nuklear. /// /// !!! Note /// Stores up to NK_INPUT_MAX bytes between `nk_input_begin` and `nk_input_end`. /// /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c /// void nk_input_char(struct nk_context *ctx, char c); /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /// /// Parameter | Description /// ------------|----------------------------------------------------------- /// __ctx__ | Must point to a previously initialized `nk_context` struct /// __c__ | Must be a single ASCII character preferable one that can be printed */ NK_API void nk_input_char(struct nk_context*, char); /*/// #### nk_input_glyph /// Converts an encoded unicode rune into UTF-8 and copies the result into an /// internal text buffer. /// /// !!! Note /// Stores up to NK_INPUT_MAX bytes between `nk_input_begin` and `nk_input_end`. /// /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c /// void nk_input_glyph(struct nk_context *ctx, const nk_glyph g); /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /// /// Parameter | Description /// ------------|----------------------------------------------------------- /// __ctx__ | Must point to a previously initialized `nk_context` struct /// __g__ | UTF-32 unicode codepoint */ NK_API void nk_input_glyph(struct nk_context*, const nk_glyph); /*/// #### nk_input_unicode /// Converts a unicode rune into UTF-8 and copies the result /// into an internal text buffer. /// !!! Note /// Stores up to NK_INPUT_MAX bytes between `nk_input_begin` and `nk_input_end`. /// /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c /// void nk_input_unicode(struct nk_context*, nk_rune rune); /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /// /// Parameter | Description /// ------------|----------------------------------------------------------- /// __ctx__ | Must point to a previously initialized `nk_context` struct /// __rune__ | UTF-32 unicode codepoint */ NK_API void nk_input_unicode(struct nk_context*, nk_rune); /*/// #### nk_input_end /// End the input mirroring process by resetting mouse grabbing /// state to ensure the mouse cursor is not grabbed indefinitely. /// /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c /// void nk_input_end(struct nk_context *ctx); /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /// /// Parameter | Description /// ------------|----------------------------------------------------------- /// __ctx__ | Must point to a previously initialized `nk_context` struct */ NK_API void nk_input_end(struct nk_context*); /* ============================================================================= * * DRAWING * * =============================================================================*/ /*/// ### Drawing /// This library was designed to be render backend agnostic so it does /// not draw anything to screen directly. Instead all drawn shapes, widgets /// are made of, are buffered into memory and make up a command queue. /// Each frame therefore fills the command buffer with draw commands /// that then need to be executed by the user and his own render backend. /// After that the command buffer needs to be cleared and a new frame can be /// started. It is probably important to note that the command buffer is the main /// drawing API and the optional vertex buffer API only takes this format and /// converts it into a hardware accessible format. /// /// #### Usage /// To draw all draw commands accumulated over a frame you need your own render /// backend able to draw a number of 2D primitives. This includes at least /// filled and stroked rectangles, circles, text, lines, triangles and scissors. /// As soon as this criterion is met you can iterate over each draw command /// and execute each draw command in a interpreter like fashion: /// /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c /// const struct nk_command *cmd = 0; /// nk_foreach(cmd, &ctx) { /// switch (cmd->type) { /// case NK_COMMAND_LINE: /// your_draw_line_function(...) /// break; /// case NK_COMMAND_RECT /// your_draw_rect_function(...) /// break; /// case //...: /// //[...] /// } /// } /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /// /// In program flow context draw commands need to be executed after input has been /// gathered and the complete UI with windows and their contained widgets have /// been executed and before calling `nk_clear` which frees all previously /// allocated draw commands. /// /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c /// struct nk_context ctx; /// nk_init_xxx(&ctx, ...); /// while (1) { /// Event evt; /// nk_input_begin(&ctx); /// while (GetEvent(&evt)) { /// if (evt.type == MOUSE_MOVE) /// nk_input_motion(&ctx, evt.motion.x, evt.motion.y); /// else if (evt.type == [...]) { /// [...] /// } /// } /// nk_input_end(&ctx); /// // /// // [...] /// // /// const struct nk_command *cmd = 0; /// nk_foreach(cmd, &ctx) { /// switch (cmd->type) { /// case NK_COMMAND_LINE: /// your_draw_line_function(...) /// break; /// case NK_COMMAND_RECT /// your_draw_rect_function(...) /// break; /// case ...: /// // [...] /// } /// nk_clear(&ctx); /// } /// nk_free(&ctx); /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /// /// You probably noticed that you have to draw all of the UI each frame which is /// quite wasteful. While the actual UI updating loop is quite fast rendering /// without actually needing it is not. So there are multiple things you could do. /// /// First is only update on input. This of course is only an option if your /// application only depends on the UI and does not require any outside calculations. /// If you actually only update on input make sure to update the UI two times each /// frame and call `nk_clear` directly after the first pass and only draw in /// the second pass. In addition it is recommended to also add additional timers /// to make sure the UI is not drawn more than a fixed number of frames per second. /// /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c /// struct nk_context ctx; /// nk_init_xxx(&ctx, ...); /// while (1) { /// // [...wait for input ] /// // [...do two UI passes ...] /// do_ui(...) /// nk_clear(&ctx); /// do_ui(...) /// // /// // draw /// const struct nk_command *cmd = 0; /// nk_foreach(cmd, &ctx) { /// switch (cmd->type) { /// case NK_COMMAND_LINE: /// your_draw_line_function(...) /// break; /// case NK_COMMAND_RECT /// your_draw_rect_function(...) /// break; /// case ...: /// //[...] /// } /// nk_clear(&ctx); /// } /// nk_free(&ctx); /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /// /// The second probably more applicable trick is to only draw if anything changed. /// It is not really useful for applications with continuous draw loop but /// quite useful for desktop applications. To actually get nuklear to only /// draw on changes you first have to define `NK_ZERO_COMMAND_MEMORY` and /// allocate a memory buffer that will store each unique drawing output. /// After each frame you compare the draw command memory inside the library /// with your allocated buffer by memcmp. If memcmp detects differences /// you have to copy the command buffer into the allocated buffer /// and then draw like usual (this example uses fixed memory but you could /// use dynamically allocated memory). /// /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c /// //[... other defines ...] /// #define NK_ZERO_COMMAND_MEMORY /// #include "nuklear.h" /// // /// // setup context /// struct nk_context ctx; /// void *last = calloc(1,64*1024); /// void *buf = calloc(1,64*1024); /// nk_init_fixed(&ctx, buf, 64*1024); /// // /// // loop /// while (1) { /// // [...input...] /// // [...ui...] /// void *cmds = nk_buffer_memory(&ctx.memory); /// if (memcmp(cmds, last, ctx.memory.allocated)) { /// memcpy(last,cmds,ctx.memory.allocated); /// const struct nk_command *cmd = 0; /// nk_foreach(cmd, &ctx) { /// switch (cmd->type) { /// case NK_COMMAND_LINE: /// your_draw_line_function(...) /// break; /// case NK_COMMAND_RECT /// your_draw_rect_function(...) /// break; /// case ...: /// // [...] /// } /// } /// } /// nk_clear(&ctx); /// } /// nk_free(&ctx); /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /// /// Finally while using draw commands makes sense for higher abstracted platforms like /// X11 and Win32 or drawing libraries it is often desirable to use graphics /// hardware directly. Therefore it is possible to just define /// `NK_INCLUDE_VERTEX_BUFFER_OUTPUT` which includes optional vertex output. /// To access the vertex output you first have to convert all draw commands into /// vertexes by calling `nk_convert` which takes in your preferred vertex format. /// After successfully converting all draw commands just iterate over and execute all /// vertex draw commands: /// /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c /// // fill configuration /// struct your_vertex /// { /// float pos[2]; // important to keep it to 2 floats /// float uv[2]; /// unsigned char col[4]; /// }; /// struct nk_convert_config cfg = {}; /// static const struct nk_draw_vertex_layout_element vertex_layout[] = { /// {NK_VERTEX_POSITION, NK_FORMAT_FLOAT, NK_OFFSETOF(struct your_vertex, pos)}, /// {NK_VERTEX_TEXCOORD, NK_FORMAT_FLOAT, NK_OFFSETOF(struct your_vertex, uv)}, /// {NK_VERTEX_COLOR, NK_FORMAT_R8G8B8A8, NK_OFFSETOF(struct your_vertex, col)}, /// {NK_VERTEX_LAYOUT_END} /// }; /// cfg.shape_AA = NK_ANTI_ALIASING_ON; /// cfg.line_AA = NK_ANTI_ALIASING_ON; /// cfg.vertex_layout = vertex_layout; /// cfg.vertex_size = sizeof(struct your_vertex); /// cfg.vertex_alignment = NK_ALIGNOF(struct your_vertex); /// cfg.circle_segment_count = 22; /// cfg.curve_segment_count = 22; /// cfg.arc_segment_count = 22; /// cfg.global_alpha = 1.0f; /// cfg.null = dev->null; /// // /// // setup buffers and convert /// struct nk_buffer cmds, verts, idx; /// nk_buffer_init_default(&cmds); /// nk_buffer_init_default(&verts); /// nk_buffer_init_default(&idx); /// nk_convert(&ctx, &cmds, &verts, &idx, &cfg); /// // /// // draw /// nk_draw_foreach(cmd, &ctx, &cmds) { /// if (!cmd->elem_count) continue; /// //[...] /// } /// nk_buffer_free(&cms); /// nk_buffer_free(&verts); /// nk_buffer_free(&idx); /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /// /// #### Reference /// Function | Description /// --------------------|------------------------------------------------------- /// __nk__begin__ | Returns the first draw command in the context draw command list to be drawn /// __nk__next__ | Increments the draw command iterator to the next command inside the context draw command list /// __nk_foreach__ | Iterates over each draw command inside the context draw command list /// __nk_convert__ | Converts from the abstract draw commands list into a hardware accessible vertex format /// __nk_draw_begin__ | Returns the first vertex command in the context vertex draw list to be executed /// __nk__draw_next__ | Increments the vertex command iterator to the next command inside the context vertex command list /// __nk__draw_end__ | Returns the end of the vertex draw list /// __nk_draw_foreach__ | Iterates over each vertex draw command inside the vertex draw list */ enum nk_anti_aliasing {NK_ANTI_ALIASING_OFF, NK_ANTI_ALIASING_ON}; enum nk_convert_result { NK_CONVERT_SUCCESS = 0, NK_CONVERT_INVALID_PARAM = 1, NK_CONVERT_COMMAND_BUFFER_FULL = NK_FLAG(1), NK_CONVERT_VERTEX_BUFFER_FULL = NK_FLAG(2), NK_CONVERT_ELEMENT_BUFFER_FULL = NK_FLAG(3) }; struct nk_draw_null_texture { nk_handle texture; /* texture handle to a texture with a white pixel */ struct nk_vec2 uv; /* coordinates to a white pixel in the texture */ }; struct nk_convert_config { float global_alpha; /* global alpha value */ enum nk_anti_aliasing line_AA; /* line anti-aliasing flag can be turned off if you are tight on memory */ enum nk_anti_aliasing shape_AA; /* shape anti-aliasing flag can be turned off if you are tight on memory */ unsigned circle_segment_count; /* number of segments used for circles: default to 22 */ unsigned arc_segment_count; /* number of segments used for arcs: default to 22 */ unsigned curve_segment_count; /* number of segments used for curves: default to 22 */ struct nk_draw_null_texture null; /* handle to texture with a white pixel for shape drawing */ const struct nk_draw_vertex_layout_element *vertex_layout; /* describes the vertex output format and packing */ nk_size vertex_size; /* sizeof one vertex for vertex packing */ nk_size vertex_alignment; /* vertex alignment: Can be obtained by NK_ALIGNOF */ }; /*/// #### nk__begin /// Returns a draw command list iterator to iterate all draw /// commands accumulated over one frame. /// /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c /// const struct nk_command* nk__begin(struct nk_context*); /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /// /// Parameter | Description /// ------------|----------------------------------------------------------- /// __ctx__ | must point to an previously initialized `nk_context` struct at the end of a frame /// /// Returns draw command pointer pointing to the first command inside the draw command list */ NK_API const struct nk_command* nk__begin(struct nk_context*); /*/// #### nk__next /// Returns draw command pointer pointing to the next command inside the draw command list /// /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c /// const struct nk_command* nk__next(struct nk_context*, const struct nk_command*); /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /// /// Parameter | Description /// ------------|----------------------------------------------------------- /// __ctx__ | Must point to an previously initialized `nk_context` struct at the end of a frame /// __cmd__ | Must point to an previously a draw command either returned by `nk__begin` or `nk__next` /// /// Returns draw command pointer pointing to the next command inside the draw command list */ NK_API const struct nk_command* nk__next(struct nk_context*, const struct nk_command*); /*/// #### nk_foreach /// Iterates over each draw command inside the context draw command list /// /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c /// #define nk_foreach(c, ctx) /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /// /// Parameter | Description /// ------------|----------------------------------------------------------- /// __ctx__ | Must point to an previously initialized `nk_context` struct at the end of a frame /// __cmd__ | Command pointer initialized to NULL /// /// Iterates over each draw command inside the context draw command list */ #define nk_foreach(c, ctx) for((c) = nk__begin(ctx); (c) != 0; (c) = nk__next(ctx,c)) #ifdef NK_INCLUDE_VERTEX_BUFFER_OUTPUT /*/// #### nk_convert /// Converts all internal draw commands into vertex draw commands and fills /// three buffers with vertexes, vertex draw commands and vertex indices. The vertex format /// as well as some other configuration values have to be configured by filling out a /// `nk_convert_config` struct. /// /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c /// nk_flags nk_convert(struct nk_context *ctx, struct nk_buffer *cmds, /// struct nk_buffer *vertices, struct nk_buffer *elements, const struct nk_convert_config*); /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /// /// Parameter | Description /// ------------|----------------------------------------------------------- /// __ctx__ | Must point to an previously initialized `nk_context` struct at the end of a frame /// __cmds__ | Must point to a previously initialized buffer to hold converted vertex draw commands /// __vertices__| Must point to a previously initialized buffer to hold all produced vertices /// __elements__| Must point to a previously initialized buffer to hold all produced vertex indices /// __config__ | Must point to a filled out `nk_config` struct to configure the conversion process /// /// Returns one of enum nk_convert_result error codes /// /// Parameter | Description /// --------------------------------|----------------------------------------------------------- /// NK_CONVERT_SUCCESS | Signals a successful draw command to vertex buffer conversion /// NK_CONVERT_INVALID_PARAM | An invalid argument was passed in the function call /// NK_CONVERT_COMMAND_BUFFER_FULL | The provided buffer for storing draw commands is full or failed to allocate more memory /// NK_CONVERT_VERTEX_BUFFER_FULL | The provided buffer for storing vertices is full or failed to allocate more memory /// NK_CONVERT_ELEMENT_BUFFER_FULL | The provided buffer for storing indices is full or failed to allocate more memory */ NK_API nk_flags nk_convert(struct nk_context*, struct nk_buffer *cmds, struct nk_buffer *vertices, struct nk_buffer *elements, const struct nk_convert_config*); /*/// #### nk__draw_begin /// Returns a draw vertex command buffer iterator to iterate over the vertex draw command buffer /// /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c /// const struct nk_draw_command* nk__draw_begin(const struct nk_context*, const struct nk_buffer*); /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /// /// Parameter | Description /// ------------|----------------------------------------------------------- /// __ctx__ | Must point to an previously initialized `nk_context` struct at the end of a frame /// __buf__ | Must point to an previously by `nk_convert` filled out vertex draw command buffer /// /// Returns vertex draw command pointer pointing to the first command inside the vertex draw command buffer */ NK_API const struct nk_draw_command* nk__draw_begin(const struct nk_context*, const struct nk_buffer*); /*/// #### nk__draw_end /// Returns the vertex draw command at the end of the vertex draw command buffer /// /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c /// const struct nk_draw_command* nk__draw_end(const struct nk_context *ctx, const struct nk_buffer *buf); /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /// /// Parameter | Description /// ------------|----------------------------------------------------------- /// __ctx__ | Must point to an previously initialized `nk_context` struct at the end of a frame /// __buf__ | Must point to an previously by `nk_convert` filled out vertex draw command buffer /// /// Returns vertex draw command pointer pointing to the end of the last vertex draw command inside the vertex draw command buffer */ NK_API const struct nk_draw_command* nk__draw_end(const struct nk_context*, const struct nk_buffer*); /*/// #### nk__draw_next /// Increments the vertex draw command buffer iterator /// /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c /// const struct nk_draw_command* nk__draw_next(const struct nk_draw_command*, const struct nk_buffer*, const struct nk_context*); /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /// /// Parameter | Description /// ------------|----------------------------------------------------------- /// __cmd__ | Must point to an previously either by `nk__draw_begin` or `nk__draw_next` returned vertex draw command /// __buf__ | Must point to an previously by `nk_convert` filled out vertex draw command buffer /// __ctx__ | Must point to an previously initialized `nk_context` struct at the end of a frame /// /// Returns vertex draw command pointer pointing to the end of the last vertex draw command inside the vertex draw command buffer */ NK_API const struct nk_draw_command* nk__draw_next(const struct nk_draw_command*, const struct nk_buffer*, const struct nk_context*); /*/// #### nk_draw_foreach /// Iterates over each vertex draw command inside a vertex draw command buffer /// /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c /// #define nk_draw_foreach(cmd,ctx, b) /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /// /// Parameter | Description /// ------------|----------------------------------------------------------- /// __cmd__ | `nk_draw_command`iterator set to NULL /// __buf__ | Must point to an previously by `nk_convert` filled out vertex draw command buffer /// __ctx__ | Must point to an previously initialized `nk_context` struct at the end of a frame */ #define nk_draw_foreach(cmd,ctx, b) for((cmd)=nk__draw_begin(ctx, b); (cmd)!=0; (cmd)=nk__draw_next(cmd, b, ctx)) #endif /* ============================================================================= * * WINDOW * * ============================================================================= /// ### Window /// Windows are the main persistent state used inside nuklear and are life time /// controlled by simply "retouching" (i.e. calling) each window each frame. /// All widgets inside nuklear can only be added inside the function pair `nk_begin_xxx` /// and `nk_end`. Calling any widgets outside these two functions will result in an /// assert in debug or no state change in release mode.

/// /// Each window holds frame persistent state like position, size, flags, state tables, /// and some garbage collected internal persistent widget state. Each window /// is linked into a window stack list which determines the drawing and overlapping /// order. The topmost window thereby is the currently active window.

/// /// To change window position inside the stack occurs either automatically by /// user input by being clicked on or programmatically by calling `nk_window_focus`. /// Windows by default are visible unless explicitly being defined with flag /// `NK_WINDOW_HIDDEN`, the user clicked the close button on windows with flag /// `NK_WINDOW_CLOSABLE` or if a window was explicitly hidden by calling /// `nk_window_show`. To explicitly close and destroy a window call `nk_window_close`.

/// /// #### Usage /// To create and keep a window you have to call one of the two `nk_begin_xxx` /// functions to start window declarations and `nk_end` at the end. Furthermore it /// is recommended to check the return value of `nk_begin_xxx` and only process /// widgets inside the window if the value is not 0. Either way you have to call /// `nk_end` at the end of window declarations. Furthermore, do not attempt to /// nest `nk_begin_xxx` calls which will hopefully result in an assert or if not /// in a segmentation fault. /// /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c /// if (nk_begin_xxx(...) { /// // [... widgets ...] /// } /// nk_end(ctx); /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /// /// In the grand concept window and widget declarations need to occur after input /// handling and before drawing to screen. Not doing so can result in higher /// latency or at worst invalid behavior. Furthermore make sure that `nk_clear` /// is called at the end of the frame. While nuklear's default platform backends /// already call `nk_clear` for you if you write your own backend not calling /// `nk_clear` can cause asserts or even worse undefined behavior. /// /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c /// struct nk_context ctx; /// nk_init_xxx(&ctx, ...); /// while (1) { /// Event evt; /// nk_input_begin(&ctx); /// while (GetEvent(&evt)) { /// if (evt.type == MOUSE_MOVE) /// nk_input_motion(&ctx, evt.motion.x, evt.motion.y); /// else if (evt.type == [...]) { /// nk_input_xxx(...); /// } /// } /// nk_input_end(&ctx); /// /// if (nk_begin_xxx(...) { /// //[...] /// } /// nk_end(ctx); /// /// const struct nk_command *cmd = 0; /// nk_foreach(cmd, &ctx) { /// case NK_COMMAND_LINE: /// your_draw_line_function(...) /// break; /// case NK_COMMAND_RECT /// your_draw_rect_function(...) /// break; /// case //...: /// //[...] /// } /// nk_clear(&ctx); /// } /// nk_free(&ctx); /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /// /// #### Reference /// Function | Description /// ------------------------------------|---------------------------------------- /// nk_begin | Starts a new window; needs to be called every frame for every window (unless hidden) or otherwise the window gets removed /// nk_begin_titled | Extended window start with separated title and identifier to allow multiple windows with same name but not title /// nk_end | Needs to be called at the end of the window building process to process scaling, scrollbars and general cleanup // /// nk_window_find | Finds and returns the window with give name /// nk_window_get_bounds | Returns a rectangle with screen position and size of the currently processed window. /// nk_window_get_position | Returns the position of the currently processed window /// nk_window_get_size | Returns the size with width and height of the currently processed window /// nk_window_get_width | Returns the width of the currently processed window /// nk_window_get_height | Returns the height of the currently processed window /// nk_window_get_panel | Returns the underlying panel which contains all processing state of the current window /// nk_window_get_content_region | Returns the position and size of the currently visible and non-clipped space inside the currently processed window /// nk_window_get_content_region_min | Returns the upper rectangle position of the currently visible and non-clipped space inside the currently processed window /// nk_window_get_content_region_max | Returns the upper rectangle position of the currently visible and non-clipped space inside the currently processed window /// nk_window_get_content_region_size | Returns the size of the currently visible and non-clipped space inside the currently processed window /// nk_window_get_canvas | Returns the draw command buffer. Can be used to draw custom widgets /// nk_window_get_scroll | Gets the scroll offset of the current window /// nk_window_has_focus | Returns if the currently processed window is currently active /// nk_window_is_collapsed | Returns if the window with given name is currently minimized/collapsed /// nk_window_is_closed | Returns if the currently processed window was closed /// nk_window_is_hidden | Returns if the currently processed window was hidden /// nk_window_is_active | Same as nk_window_has_focus for some reason /// nk_window_is_hovered | Returns if the currently processed window is currently being hovered by mouse /// nk_window_is_any_hovered | Return if any window currently hovered /// nk_item_is_any_active | Returns if any window or widgets is currently hovered or active // /// nk_window_set_bounds | Updates position and size of the currently processed window /// nk_window_set_position | Updates position of the currently process window /// nk_window_set_size | Updates the size of the currently processed window /// nk_window_set_focus | Set the currently processed window as active window /// nk_window_set_scroll | Sets the scroll offset of the current window // /// nk_window_close | Closes the window with given window name which deletes the window at the end of the frame /// nk_window_collapse | Collapses the window with given window name /// nk_window_collapse_if | Collapses the window with given window name if the given condition was met /// nk_window_show | Hides a visible or reshows a hidden window /// nk_window_show_if | Hides/shows a window depending on condition */ /* /// #### nk_panel_flags /// Flag | Description /// ----------------------------|---------------------------------------- /// NK_WINDOW_BORDER | Draws a border around the window to visually separate window from the background /// NK_WINDOW_MOVABLE | The movable flag indicates that a window can be moved by user input or by dragging the window header /// NK_WINDOW_SCALABLE | The scalable flag indicates that a window can be scaled by user input by dragging a scaler icon at the button of the window /// NK_WINDOW_CLOSABLE | Adds a closable icon into the header /// NK_WINDOW_MINIMIZABLE | Adds a minimize icon into the header /// NK_WINDOW_NO_SCROLLBAR | Removes the scrollbar from the window /// NK_WINDOW_TITLE | Forces a header at the top at the window showing the title /// NK_WINDOW_SCROLL_AUTO_HIDE | Automatically hides the window scrollbar if no user interaction: also requires delta time in `nk_context` to be set each frame /// NK_WINDOW_BACKGROUND | Always keep window in the background /// NK_WINDOW_SCALE_LEFT | Puts window scaler in the left-bottom corner instead right-bottom /// NK_WINDOW_NO_INPUT | Prevents window of scaling, moving or getting focus /// /// #### nk_collapse_states /// State | Description /// ----------------|----------------------------------------------------------- /// __NK_MINIMIZED__| UI section is collased and not visible until maximized /// __NK_MAXIMIZED__| UI section is extended and visible until minimized ///

*/ enum nk_panel_flags { NK_WINDOW_BORDER = NK_FLAG(0), NK_WINDOW_MOVABLE = NK_FLAG(1), NK_WINDOW_SCALABLE = NK_FLAG(2), NK_WINDOW_CLOSABLE = NK_FLAG(3), NK_WINDOW_MINIMIZABLE = NK_FLAG(4), NK_WINDOW_TITLE = NK_FLAG(5), NK_WINDOW_SCROLL_AUTO_HIDE = NK_FLAG(6), NK_WINDOW_BACKGROUND = NK_FLAG(7), NK_WINDOW_SCALE_LEFT = NK_FLAG(8), NK_WINDOW_NO_INPUT = NK_FLAG(9), NK_WINDOW_NO_SCROLLBAR_X = NK_FLAG(10), //< @r-lyeh NK_WINDOW_NO_SCROLLBAR_Y = NK_FLAG(11), //< @r-lyeh NK_WINDOW_NO_SCROLLBAR = NK_FLAG(10) | NK_FLAG(11), //< @r-lyeh NK_WINDOW_SCALE_TOP = NK_FLAG(12), //< @r-lyeh NK_WINDOW_PINNABLE = NK_FLAG(13), //< @r-lyeh NK_WINDOW_MAXIMIZABLE = NK_FLAG(14), //< @r-lyeh NK_WINDOW_PUBLIC_FLAGS = 14, //< @r-lyeh }; /*/// #### nk_begin /// Starts a new window; needs to be called every frame for every /// window (unless hidden) or otherwise the window gets removed /// /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c /// nk_bool nk_begin(struct nk_context *ctx, const char *title, struct nk_rect bounds, nk_flags flags); /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /// /// Parameter | Description /// ------------|----------------------------------------------------------- /// __ctx__ | Must point to an previously initialized `nk_context` struct /// __title__ | Window title and identifier. Needs to be persistent over frames to identify the window /// __bounds__ | Initial position and window size. However if you do not define `NK_WINDOW_SCALABLE` or `NK_WINDOW_MOVABLE` you can set window position and size every frame /// __flags__ | Window flags defined in the nk_panel_flags section with a number of different window behaviors /// /// Returns `true(1)` if the window can be filled up with widgets from this point /// until `nk_end` or `false(0)` otherwise for example if minimized */ NK_API nk_bool nk_begin(struct nk_context *ctx, const char *title, struct nk_rect bounds, nk_flags flags); /*/// #### nk_begin_titled /// Extended window start with separated title and identifier to allow multiple /// windows with same title but not name /// /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c /// nk_bool nk_begin_titled(struct nk_context *ctx, const char *name, const char *title, struct nk_rect bounds, nk_flags flags); /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /// /// Parameter | Description /// ------------|----------------------------------------------------------- /// __ctx__ | Must point to an previously initialized `nk_context` struct /// __name__ | Window identifier. Needs to be persistent over frames to identify the window /// __title__ | Window title displayed inside header if flag `NK_WINDOW_TITLE` or either `NK_WINDOW_CLOSABLE` or `NK_WINDOW_MINIMIZED` was set /// __bounds__ | Initial position and window size. However if you do not define `NK_WINDOW_SCALABLE` or `NK_WINDOW_MOVABLE` you can set window position and size every frame /// __flags__ | Window flags defined in the nk_panel_flags section with a number of different window behaviors /// /// Returns `true(1)` if the window can be filled up with widgets from this point /// until `nk_end` or `false(0)` otherwise for example if minimized */ NK_API nk_bool nk_begin_titled(struct nk_context *ctx, const char *name, const char *title, struct nk_rect bounds, nk_flags flags); /*/// #### nk_end /// Needs to be called at the end of the window building process to process scaling, scrollbars and general cleanup. /// All widget calls after this functions will result in asserts or no state changes /// /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c /// void nk_end(struct nk_context *ctx); /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /// /// Parameter | Description /// ------------|----------------------------------------------------------- /// __ctx__ | Must point to an previously initialized `nk_context` struct */ NK_API void nk_end(struct nk_context *ctx); /*/// #### nk_window_find /// Finds and returns a window from passed name /// /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c /// struct nk_window *nk_window_find(struct nk_context *ctx, const char *name); /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /// /// Parameter | Description /// ------------|----------------------------------------------------------- /// __ctx__ | Must point to an previously initialized `nk_context` struct /// __name__ | Window identifier /// /// Returns a `nk_window` struct pointing to the identified window or NULL if /// no window with the given name was found */ NK_API struct nk_window *nk_window_find(struct nk_context *ctx, const char *name); /*/// #### nk_window_get_bounds /// Returns a rectangle with screen position and size of the currently processed window /// /// !!! WARNING /// Only call this function between calls `nk_begin_xxx` and `nk_end` /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c /// struct nk_rect nk_window_get_bounds(const struct nk_context *ctx); /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /// /// Parameter | Description /// ------------|----------------------------------------------------------- /// __ctx__ | Must point to an previously initialized `nk_context` struct /// /// Returns a `nk_rect` struct with window upper left window position and size */ NK_API struct nk_rect nk_window_get_bounds(const struct nk_context *ctx); /*/// #### nk_window_get_position /// Returns the position of the currently processed window. /// /// !!! WARNING /// Only call this function between calls `nk_begin_xxx` and `nk_end` /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c /// struct nk_vec2 nk_window_get_position(const struct nk_context *ctx); /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /// /// Parameter | Description /// ------------|----------------------------------------------------------- /// __ctx__ | Must point to an previously initialized `nk_context` struct /// /// Returns a `nk_vec2` struct with window upper left position */ NK_API struct nk_vec2 nk_window_get_position(const struct nk_context *ctx); /*/// #### nk_window_get_size /// Returns the size with width and height of the currently processed window. /// /// !!! WARNING /// Only call this function between calls `nk_begin_xxx` and `nk_end` /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c /// struct nk_vec2 nk_window_get_size(const struct nk_context *ctx); /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /// /// Parameter | Description /// ------------|----------------------------------------------------------- /// __ctx__ | Must point to an previously initialized `nk_context` struct /// /// Returns a `nk_vec2` struct with window width and height */ NK_API struct nk_vec2 nk_window_get_size(const struct nk_context*); /*/// #### nk_window_get_width /// Returns the width of the currently processed window. /// /// !!! WARNING /// Only call this function between calls `nk_begin_xxx` and `nk_end` /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c /// float nk_window_get_width(const struct nk_context *ctx); /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /// /// Parameter | Description /// ------------|----------------------------------------------------------- /// __ctx__ | Must point to an previously initialized `nk_context` struct /// /// Returns the current window width */ NK_API float nk_window_get_width(const struct nk_context*); /*/// #### nk_window_get_height /// Returns the height of the currently processed window. /// /// !!! WARNING /// Only call this function between calls `nk_begin_xxx` and `nk_end` /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c /// float nk_window_get_height(const struct nk_context *ctx); /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /// /// Parameter | Description /// ------------|----------------------------------------------------------- /// __ctx__ | Must point to an previously initialized `nk_context` struct /// /// Returns the current window height */ NK_API float nk_window_get_height(const struct nk_context*); /*/// #### nk_window_get_panel /// Returns the underlying panel which contains all processing state of the current window. /// /// !!! WARNING /// Only call this function between calls `nk_begin_xxx` and `nk_end` /// !!! WARNING /// Do not keep the returned panel pointer around, it is only valid until `nk_end` /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c /// struct nk_panel* nk_window_get_panel(struct nk_context *ctx); /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /// /// Parameter | Description /// ------------|----------------------------------------------------------- /// __ctx__ | Must point to an previously initialized `nk_context` struct /// /// Returns a pointer to window internal `nk_panel` state. */ NK_API struct nk_panel* nk_window_get_panel(struct nk_context*); /*/// #### nk_window_get_content_region /// Returns the position and size of the currently visible and non-clipped space /// inside the currently processed window. /// /// !!! WARNING /// Only call this function between calls `nk_begin_xxx` and `nk_end` /// /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c /// struct nk_rect nk_window_get_content_region(struct nk_context *ctx); /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /// /// Parameter | Description /// ------------|----------------------------------------------------------- /// __ctx__ | Must point to an previously initialized `nk_context` struct /// /// Returns `nk_rect` struct with screen position and size (no scrollbar offset) /// of the visible space inside the current window */ NK_API struct nk_rect nk_window_get_content_region(struct nk_context*); /*/// #### nk_window_get_content_region_min /// Returns the upper left position of the currently visible and non-clipped /// space inside the currently processed window. /// /// !!! WARNING /// Only call this function between calls `nk_begin_xxx` and `nk_end` /// /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c /// struct nk_vec2 nk_window_get_content_region_min(struct nk_context *ctx); /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /// /// Parameter | Description /// ------------|----------------------------------------------------------- /// __ctx__ | Must point to an previously initialized `nk_context` struct /// /// returns `nk_vec2` struct with upper left screen position (no scrollbar offset) /// of the visible space inside the current window */ NK_API struct nk_vec2 nk_window_get_content_region_min(struct nk_context*); /*/// #### nk_window_get_content_region_max /// Returns the lower right screen position of the currently visible and /// non-clipped space inside the currently processed window. /// /// !!! WARNING /// Only call this function between calls `nk_begin_xxx` and `nk_end` /// /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c /// struct nk_vec2 nk_window_get_content_region_max(struct nk_context *ctx); /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /// /// Parameter | Description /// ------------|----------------------------------------------------------- /// __ctx__ | Must point to an previously initialized `nk_context` struct /// /// Returns `nk_vec2` struct with lower right screen position (no scrollbar offset) /// of the visible space inside the current window */ NK_API struct nk_vec2 nk_window_get_content_region_max(struct nk_context*); /*/// #### nk_window_get_content_region_size /// Returns the size of the currently visible and non-clipped space inside the /// currently processed window /// /// !!! WARNING /// Only call this function between calls `nk_begin_xxx` and `nk_end` /// /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c /// struct nk_vec2 nk_window_get_content_region_size(struct nk_context *ctx); /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /// /// Parameter | Description /// ------------|----------------------------------------------------------- /// __ctx__ | Must point to an previously initialized `nk_context` struct /// /// Returns `nk_vec2` struct with size the visible space inside the current window */ NK_API struct nk_vec2 nk_window_get_content_region_size(struct nk_context*); /*/// #### nk_window_get_canvas /// Returns the draw command buffer. Can be used to draw custom widgets /// !!! WARNING /// Only call this function between calls `nk_begin_xxx` and `nk_end` /// !!! WARNING /// Do not keep the returned command buffer pointer around it is only valid until `nk_end` /// /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c /// struct nk_command_buffer* nk_window_get_canvas(struct nk_context *ctx); /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /// /// Parameter | Description /// ------------|----------------------------------------------------------- /// __ctx__ | Must point to an previously initialized `nk_context` struct /// /// Returns a pointer to window internal `nk_command_buffer` struct used as /// drawing canvas. Can be used to do custom drawing. */ NK_API struct nk_command_buffer* nk_window_get_canvas(struct nk_context*); /*/// #### nk_window_get_scroll /// Gets the scroll offset for the current window /// !!! WARNING /// Only call this function between calls `nk_begin_xxx` and `nk_end` /// /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c /// void nk_window_get_scroll(struct nk_context *ctx, nk_uint *offset_x, nk_uint *offset_y); /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /// /// Parameter | Description /// -------------|----------------------------------------------------------- /// __ctx__ | Must point to an previously initialized `nk_context` struct /// __offset_x__ | A pointer to the x offset output (or NULL to ignore) /// __offset_y__ | A pointer to the y offset output (or NULL to ignore) */ NK_API void nk_window_get_scroll(struct nk_context*, nk_uint *offset_x, nk_uint *offset_y); /*/// #### nk_window_has_focus /// Returns if the currently processed window is currently active /// !!! WARNING /// Only call this function between calls `nk_begin_xxx` and `nk_end` /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c /// nk_bool nk_window_has_focus(const struct nk_context *ctx); /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /// /// Parameter | Description /// ------------|----------------------------------------------------------- /// __ctx__ | Must point to an previously initialized `nk_context` struct /// /// Returns `false(0)` if current window is not active or `true(1)` if it is */ NK_API nk_bool nk_window_has_focus(const struct nk_context*); /*/// #### nk_window_is_hovered /// Return if the current window is being hovered /// !!! WARNING /// Only call this function between calls `nk_begin_xxx` and `nk_end` /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c /// nk_bool nk_window_is_hovered(struct nk_context *ctx); /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /// /// Parameter | Description /// ------------|----------------------------------------------------------- /// __ctx__ | Must point to an previously initialized `nk_context` struct /// /// Returns `true(1)` if current window is hovered or `false(0)` otherwise */ NK_API nk_bool nk_window_is_hovered(struct nk_context*); /*/// #### nk_window_is_collapsed /// Returns if the window with given name is currently minimized/collapsed /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c /// nk_bool nk_window_is_collapsed(struct nk_context *ctx, const char *name); /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /// /// Parameter | Description /// ------------|----------------------------------------------------------- /// __ctx__ | Must point to an previously initialized `nk_context` struct /// __name__ | Identifier of window you want to check if it is collapsed /// /// Returns `true(1)` if current window is minimized and `false(0)` if window not /// found or is not minimized */ NK_API nk_bool nk_window_is_collapsed(struct nk_context *ctx, const char *name); /*/// #### nk_window_is_closed /// Returns if the window with given name was closed by calling `nk_close` /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c /// nk_bool nk_window_is_closed(struct nk_context *ctx, const char *name); /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /// /// Parameter | Description /// ------------|----------------------------------------------------------- /// __ctx__ | Must point to an previously initialized `nk_context` struct /// __name__ | Identifier of window you want to check if it is closed /// /// Returns `true(1)` if current window was closed or `false(0)` window not found or not closed */ NK_API nk_bool nk_window_is_closed(struct nk_context*, const char*); /*/// #### nk_window_is_hidden /// Returns if the window with given name is hidden /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c /// nk_bool nk_window_is_hidden(struct nk_context *ctx, const char *name); /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /// /// Parameter | Description /// ------------|----------------------------------------------------------- /// __ctx__ | Must point to an previously initialized `nk_context` struct /// __name__ | Identifier of window you want to check if it is hidden /// /// Returns `true(1)` if current window is hidden or `false(0)` window not found or visible */ NK_API nk_bool nk_window_is_hidden(struct nk_context*, const char*); /*/// #### nk_window_is_active /// Same as nk_window_has_focus for some reason /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c /// nk_bool nk_window_is_active(struct nk_context *ctx, const char *name); /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /// /// Parameter | Description /// ------------|----------------------------------------------------------- /// __ctx__ | Must point to an previously initialized `nk_context` struct /// __name__ | Identifier of window you want to check if it is active /// /// Returns `true(1)` if current window is active or `false(0)` window not found or not active */ NK_API nk_bool nk_window_is_active(struct nk_context*, const char*); /*/// #### nk_window_is_any_hovered /// Returns if the any window is being hovered /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c /// nk_bool nk_window_is_any_hovered(struct nk_context*); /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /// /// Parameter | Description /// ------------|----------------------------------------------------------- /// __ctx__ | Must point to an previously initialized `nk_context` struct /// /// Returns `true(1)` if any window is hovered or `false(0)` otherwise */ NK_API nk_bool nk_window_is_any_hovered(struct nk_context*); /*/// #### nk_item_is_any_active /// Returns if the any window is being hovered or any widget is currently active. /// Can be used to decide if input should be processed by UI or your specific input handling. /// Example could be UI and 3D camera to move inside a 3D space. /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c /// nk_bool nk_item_is_any_active(struct nk_context*); /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /// /// Parameter | Description /// ------------|----------------------------------------------------------- /// __ctx__ | Must point to an previously initialized `nk_context` struct /// /// Returns `true(1)` if any window is hovered or any item is active or `false(0)` otherwise */ NK_API nk_bool nk_item_is_any_active(struct nk_context*); /*/// #### nk_window_set_bounds /// Updates position and size of window with passed in name /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c /// void nk_window_set_bounds(struct nk_context*, const char *name, struct nk_rect bounds); /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /// /// Parameter | Description /// ------------|----------------------------------------------------------- /// __ctx__ | Must point to an previously initialized `nk_context` struct /// __name__ | Identifier of the window to modify both position and size /// __bounds__ | Must point to a `nk_rect` struct with the new position and size */ NK_API void nk_window_set_bounds(struct nk_context*, const char *name, struct nk_rect bounds); /*/// #### nk_window_set_position /// Updates position of window with passed name /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c /// void nk_window_set_position(struct nk_context*, const char *name, struct nk_vec2 pos); /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /// /// Parameter | Description /// ------------|----------------------------------------------------------- /// __ctx__ | Must point to an previously initialized `nk_context` struct /// __name__ | Identifier of the window to modify both position /// __pos__ | Must point to a `nk_vec2` struct with the new position */ NK_API void nk_window_set_position(struct nk_context*, const char *name, struct nk_vec2 pos); /*/// #### nk_window_set_size /// Updates size of window with passed in name /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c /// void nk_window_set_size(struct nk_context*, const char *name, struct nk_vec2); /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /// /// Parameter | Description /// ------------|----------------------------------------------------------- /// __ctx__ | Must point to an previously initialized `nk_context` struct /// __name__ | Identifier of the window to modify both window size /// __size__ | Must point to a `nk_vec2` struct with new window size */ NK_API void nk_window_set_size(struct nk_context*, const char *name, struct nk_vec2); /*/// #### nk_window_set_focus /// Sets the window with given name as active /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c /// void nk_window_set_focus(struct nk_context*, const char *name); /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /// /// Parameter | Description /// ------------|----------------------------------------------------------- /// __ctx__ | Must point to an previously initialized `nk_context` struct /// __name__ | Identifier of the window to set focus on */ NK_API void nk_window_set_focus(struct nk_context*, const char *name); /*/// #### nk_window_set_scroll /// Sets the scroll offset for the current window /// !!! WARNING /// Only call this function between calls `nk_begin_xxx` and `nk_end` /// /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c /// void nk_window_set_scroll(struct nk_context *ctx, nk_uint offset_x, nk_uint offset_y); /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /// /// Parameter | Description /// -------------|----------------------------------------------------------- /// __ctx__ | Must point to an previously initialized `nk_context` struct /// __offset_x__ | The x offset to scroll to /// __offset_y__ | The y offset to scroll to */ NK_API void nk_window_set_scroll(struct nk_context*, nk_uint offset_x, nk_uint offset_y); /*/// #### nk_window_close /// Closes a window and marks it for being freed at the end of the frame /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c /// void nk_window_close(struct nk_context *ctx, const char *name); /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /// /// Parameter | Description /// ------------|----------------------------------------------------------- /// __ctx__ | Must point to an previously initialized `nk_context` struct /// __name__ | Identifier of the window to close */ NK_API void nk_window_close(struct nk_context *ctx, const char *name); /*/// #### nk_window_collapse /// Updates collapse state of a window with given name /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c /// void nk_window_collapse(struct nk_context*, const char *name, enum nk_collapse_states state); /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /// /// Parameter | Description /// ------------|----------------------------------------------------------- /// __ctx__ | Must point to an previously initialized `nk_context` struct /// __name__ | Identifier of the window to close /// __state__ | value out of nk_collapse_states section */ NK_API void nk_window_collapse(struct nk_context*, const char *name, enum nk_collapse_states state); /*/// #### nk_window_collapse_if /// Updates collapse state of a window with given name if given condition is met /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c /// void nk_window_collapse_if(struct nk_context*, const char *name, enum nk_collapse_states, int cond); /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /// /// Parameter | Description /// ------------|----------------------------------------------------------- /// __ctx__ | Must point to an previously initialized `nk_context` struct /// __name__ | Identifier of the window to either collapse or maximize /// __state__ | value out of nk_collapse_states section the window should be put into /// __cond__ | condition that has to be met to actually commit the collapse state change */ NK_API void nk_window_collapse_if(struct nk_context*, const char *name, enum nk_collapse_states, int cond); /*/// #### nk_window_show /// updates visibility state of a window with given name /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c /// void nk_window_show(struct nk_context*, const char *name, enum nk_show_states); /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /// /// Parameter | Description /// ------------|----------------------------------------------------------- /// __ctx__ | Must point to an previously initialized `nk_context` struct /// __name__ | Identifier of the window to either collapse or maximize /// __state__ | state with either visible or hidden to modify the window with */ NK_API void nk_window_show(struct nk_context*, const char *name, enum nk_show_states); /*/// #### nk_window_show_if /// Updates visibility state of a window with given name if a given condition is met /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c /// void nk_window_show_if(struct nk_context*, const char *name, enum nk_show_states, int cond); /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /// /// Parameter | Description /// ------------|----------------------------------------------------------- /// __ctx__ | Must point to an previously initialized `nk_context` struct /// __name__ | Identifier of the window to either hide or show /// __state__ | state with either visible or hidden to modify the window with /// __cond__ | condition that has to be met to actually commit the visbility state change */ NK_API void nk_window_show_if(struct nk_context*, const char *name, enum nk_show_states, int cond); /* ============================================================================= * * LAYOUT * * ============================================================================= /// ### Layouting /// Layouting in general describes placing widget inside a window with position and size. /// While in this particular implementation there are five different APIs for layouting /// each with different trade offs between control and ease of use.

/// /// All layouting methods in this library are based around the concept of a row. /// A row has a height the window content grows by and a number of columns and each /// layouting method specifies how each widget is placed inside the row. /// After a row has been allocated by calling a layouting functions and then /// filled with widgets will advance an internal pointer over the allocated row.

/// /// To actually define a layout you just call the appropriate layouting function /// and each subsequent widget call will place the widget as specified. Important /// here is that if you define more widgets then columns defined inside the layout /// functions it will allocate the next row without you having to make another layouting

/// call. /// /// Biggest limitation with using all these APIs outside the `nk_layout_space_xxx` API /// is that you have to define the row height for each. However the row height /// often depends on the height of the font.

/// /// To fix that internally nuklear uses a minimum row height that is set to the /// height plus padding of currently active font and overwrites the row height /// value if zero.

/// /// If you manually want to change the minimum row height then /// use nk_layout_set_min_row_height, and use nk_layout_reset_min_row_height to /// reset it back to be derived from font height.

/// /// Also if you change the font in nuklear it will automatically change the minimum /// row height for you and. This means if you change the font but still want /// a minimum row height smaller than the font you have to repush your value.

/// /// For actually more advanced UI I would even recommend using the `nk_layout_space_xxx` /// layouting method in combination with a cassowary constraint solver (there are /// some versions on github with permissive license model) to take over all control over widget /// layouting yourself. However for quick and dirty layouting using all the other layouting /// functions should be fine. /// /// #### Usage /// 1. __nk_layout_row_dynamic__

/// The easiest layouting function is `nk_layout_row_dynamic`. It provides each /// widgets with same horizontal space inside the row and dynamically grows /// if the owning window grows in width. So the number of columns dictates /// the size of each widget dynamically by formula: /// /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c /// widget_width = (window_width - padding - spacing) * (1/colum_count) /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /// /// Just like all other layouting APIs if you define more widget than columns this /// library will allocate a new row and keep all layouting parameters previously /// defined. /// /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c /// if (nk_begin_xxx(...) { /// // first row with height: 30 composed of two widgets /// nk_layout_row_dynamic(&ctx, 30, 2); /// nk_widget(...); /// nk_widget(...); /// // /// // second row with same parameter as defined above /// nk_widget(...); /// nk_widget(...); /// // /// // third row uses 0 for height which will use auto layouting /// nk_layout_row_dynamic(&ctx, 0, 2); /// nk_widget(...); /// nk_widget(...); /// } /// nk_end(...); /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /// /// 2. __nk_layout_row_static__

/// Another easy layouting function is `nk_layout_row_static`. It provides each /// widget with same horizontal pixel width inside the row and does not grow /// if the owning window scales smaller or bigger. /// /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c /// if (nk_begin_xxx(...) { /// // first row with height: 30 composed of two widgets with width: 80 /// nk_layout_row_static(&ctx, 30, 80, 2); /// nk_widget(...); /// nk_widget(...); /// // /// // second row with same parameter as defined above /// nk_widget(...); /// nk_widget(...); /// // /// // third row uses 0 for height which will use auto layouting /// nk_layout_row_static(&ctx, 0, 80, 2); /// nk_widget(...); /// nk_widget(...); /// } /// nk_end(...); /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /// /// 3. __nk_layout_row_xxx__

/// A little bit more advanced layouting API are functions `nk_layout_row_begin`, /// `nk_layout_row_push` and `nk_layout_row_end`. They allow to directly /// specify each column pixel or window ratio in a row. It supports either /// directly setting per column pixel width or widget window ratio but not /// both. Furthermore it is a immediate mode API so each value is directly /// pushed before calling a widget. Therefore the layout is not automatically /// repeating like the last two layouting functions. /// /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c /// if (nk_begin_xxx(...) { /// // first row with height: 25 composed of two widgets with width 60 and 40 /// nk_layout_row_begin(ctx, NK_STATIC, 25, 2); /// nk_layout_row_push(ctx, 60); /// nk_widget(...); /// nk_layout_row_push(ctx, 40); /// nk_widget(...); /// nk_layout_row_end(ctx); /// // /// // second row with height: 25 composed of two widgets with window ratio 0.25 and 0.75 /// nk_layout_row_begin(ctx, NK_DYNAMIC, 25, 2); /// nk_layout_row_push(ctx, 0.25f); /// nk_widget(...); /// nk_layout_row_push(ctx, 0.75f); /// nk_widget(...); /// nk_layout_row_end(ctx); /// // /// // third row with auto generated height: composed of two widgets with window ratio 0.25 and 0.75 /// nk_layout_row_begin(ctx, NK_DYNAMIC, 0, 2); /// nk_layout_row_push(ctx, 0.25f); /// nk_widget(...); /// nk_layout_row_push(ctx, 0.75f); /// nk_widget(...); /// nk_layout_row_end(ctx); /// } /// nk_end(...); /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /// /// 4. __nk_layout_row__

/// The array counterpart to API nk_layout_row_xxx is the single nk_layout_row /// functions. Instead of pushing either pixel or window ratio for every widget /// it allows to define it by array. The trade of for less control is that /// `nk_layout_row` is automatically repeating. Otherwise the behavior is the /// same. /// /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c /// if (nk_begin_xxx(...) { /// // two rows with height: 30 composed of two widgets with width 60 and 40 /// const float size[] = {60,40}; /// nk_layout_row(ctx, NK_STATIC, 30, 2, ratio); /// nk_widget(...); /// nk_widget(...); /// nk_widget(...); /// nk_widget(...); /// // /// // two rows with height: 30 composed of two widgets with window ratio 0.25 and 0.75 /// const float ratio[] = {0.25, 0.75}; /// nk_layout_row(ctx, NK_DYNAMIC, 30, 2, ratio); /// nk_widget(...); /// nk_widget(...); /// nk_widget(...); /// nk_widget(...); /// // /// // two rows with auto generated height composed of two widgets with window ratio 0.25 and 0.75 /// const float ratio[] = {0.25, 0.75}; /// nk_layout_row(ctx, NK_DYNAMIC, 30, 2, ratio); /// nk_widget(...); /// nk_widget(...); /// nk_widget(...); /// nk_widget(...); /// } /// nk_end(...); /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /// /// 5. __nk_layout_row_template_xxx__

/// The most complex and second most flexible API is a simplified flexbox version without /// line wrapping and weights for dynamic widgets. It is an immediate mode API but /// unlike `nk_layout_row_xxx` it has auto repeat behavior and needs to be called /// before calling the templated widgets. /// The row template layout has three different per widget size specifier. The first /// one is the `nk_layout_row_template_push_static` with fixed widget pixel width. /// They do not grow if the row grows and will always stay the same. /// The second size specifier is `nk_layout_row_template_push_variable` /// which defines a minimum widget size but it also can grow if more space is available /// not taken by other widgets. /// Finally there are dynamic widgets with `nk_layout_row_template_push_dynamic` /// which are completely flexible and unlike variable widgets can even shrink /// to zero if not enough space is provided. /// /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c /// if (nk_begin_xxx(...) { /// // two rows with height: 30 composed of three widgets /// nk_layout_row_template_begin(ctx, 30); /// nk_layout_row_template_push_dynamic(ctx); /// nk_layout_row_template_push_variable(ctx, 80); /// nk_layout_row_template_push_static(ctx, 80); /// nk_layout_row_template_end(ctx); /// // /// // first row /// nk_widget(...); // dynamic widget can go to zero if not enough space /// nk_widget(...); // variable widget with min 80 pixel but can grow bigger if enough space /// nk_widget(...); // static widget with fixed 80 pixel width /// // /// // second row same layout /// nk_widget(...); /// nk_widget(...); /// nk_widget(...); /// } /// nk_end(...); /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /// /// 6. __nk_layout_space_xxx__

/// Finally the most flexible API directly allows you to place widgets inside the /// window. The space layout API is an immediate mode API which does not support /// row auto repeat and directly sets position and size of a widget. Position /// and size hereby can be either specified as ratio of allocated space or /// allocated space local position and pixel size. Since this API is quite /// powerful there are a number of utility functions to get the available space /// and convert between local allocated space and screen space. /// /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c /// if (nk_begin_xxx(...) { /// // static row with height: 500 (you can set column count to INT_MAX if you don't want to be bothered) /// nk_layout_space_begin(ctx, NK_STATIC, 500, INT_MAX); /// nk_layout_space_push(ctx, nk_rect(0,0,150,200)); /// nk_widget(...); /// nk_layout_space_push(ctx, nk_rect(200,200,100,200)); /// nk_widget(...); /// nk_layout_space_end(ctx); /// // /// // dynamic row with height: 500 (you can set column count to INT_MAX if you don't want to be bothered) /// nk_layout_space_begin(ctx, NK_DYNAMIC, 500, INT_MAX); /// nk_layout_space_push(ctx, nk_rect(0.5,0.5,0.1,0.1)); /// nk_widget(...); /// nk_layout_space_push(ctx, nk_rect(0.7,0.6,0.1,0.1)); /// nk_widget(...); /// } /// nk_end(...); /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /// /// #### Reference /// Function | Description /// ----------------------------------------|------------------------------------ /// nk_layout_set_min_row_height | Set the currently used minimum row height to a specified value /// nk_layout_reset_min_row_height | Resets the currently used minimum row height to font height /// nk_layout_widget_bounds | Calculates current width a static layout row can fit inside a window /// nk_layout_ratio_from_pixel | Utility functions to calculate window ratio from pixel size // /// nk_layout_row_dynamic | Current layout is divided into n same sized growing columns /// nk_layout_row_static | Current layout is divided into n same fixed sized columns /// nk_layout_row_begin | Starts a new row with given height and number of columns /// nk_layout_row_push | Pushes another column with given size or window ratio /// nk_layout_row_end | Finished previously started row /// nk_layout_row | Specifies row columns in array as either window ratio or size // /// nk_layout_row_template_begin | Begins the row template declaration /// nk_layout_row_template_push_dynamic | Adds a dynamic column that dynamically grows and can go to zero if not enough space /// nk_layout_row_template_push_variable | Adds a variable column that dynamically grows but does not shrink below specified pixel width /// nk_layout_row_template_push_static | Adds a static column that does not grow and will always have the same size /// nk_layout_row_template_end | Marks the end of the row template // /// nk_layout_space_begin | Begins a new layouting space that allows to specify each widgets position and size /// nk_layout_space_push | Pushes position and size of the next widget in own coordinate space either as pixel or ratio /// nk_layout_space_end | Marks the end of the layouting space // /// nk_layout_space_bounds | Callable after nk_layout_space_begin and returns total space allocated /// nk_layout_space_to_screen | Converts vector from nk_layout_space coordinate space into screen space /// nk_layout_space_to_local | Converts vector from screen space into nk_layout_space coordinates /// nk_layout_space_rect_to_screen | Converts rectangle from nk_layout_space coordinate space into screen space /// nk_layout_space_rect_to_local | Converts rectangle from screen space into nk_layout_space coordinates */ /*/// #### nk_layout_set_min_row_height /// Sets the currently used minimum row height. /// !!! WARNING /// The passed height needs to include both your preferred row height /// as well as padding. No internal padding is added. /// /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c /// void nk_layout_set_min_row_height(struct nk_context*, float height); /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /// /// Parameter | Description /// ------------|----------------------------------------------------------- /// __ctx__ | Must point to an previously initialized `nk_context` struct after call `nk_begin_xxx` /// __height__ | New minimum row height to be used for auto generating the row height */ NK_API void nk_layout_set_min_row_height(struct nk_context*, float height); /*/// #### nk_layout_reset_min_row_height /// Reset the currently used minimum row height back to `font_height + text_padding + padding` /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c /// void nk_layout_reset_min_row_height(struct nk_context*); /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /// /// Parameter | Description /// ------------|----------------------------------------------------------- /// __ctx__ | Must point to an previously initialized `nk_context` struct after call `nk_begin_xxx` */ NK_API void nk_layout_reset_min_row_height(struct nk_context*); /*/// #### nk_layout_widget_bounds /// Returns the width of the next row allocate by one of the layouting functions /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c /// struct nk_rect nk_layout_widget_bounds(struct nk_context*); /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /// /// Parameter | Description /// ------------|----------------------------------------------------------- /// __ctx__ | Must point to an previously initialized `nk_context` struct after call `nk_begin_xxx` /// /// Return `nk_rect` with both position and size of the next row */ NK_API struct nk_rect nk_layout_widget_bounds(struct nk_context*); /*/// #### nk_layout_ratio_from_pixel /// Utility functions to calculate window ratio from pixel size /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c /// float nk_layout_ratio_from_pixel(struct nk_context*, float pixel_width); /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /// /// Parameter | Description /// ------------|----------------------------------------------------------- /// __ctx__ | Must point to an previously initialized `nk_context` struct after call `nk_begin_xxx` /// __pixel__ | Pixel_width to convert to window ratio /// /// Returns `nk_rect` with both position and size of the next row */ NK_API float nk_layout_ratio_from_pixel(struct nk_context*, float pixel_width); /*/// #### nk_layout_row_dynamic /// Sets current row layout to share horizontal space /// between @cols number of widgets evenly. Once called all subsequent widget /// calls greater than @cols will allocate a new row with same layout. /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c /// void nk_layout_row_dynamic(struct nk_context *ctx, float height, int cols); /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /// /// Parameter | Description /// ------------|----------------------------------------------------------- /// __ctx__ | Must point to an previously initialized `nk_context` struct after call `nk_begin_xxx` /// __height__ | Holds height of each widget in row or zero for auto layouting /// __columns__ | Number of widget inside row */ NK_API void nk_layout_row_dynamic(struct nk_context *ctx, float height, int cols); /*/// #### nk_layout_row_static /// Sets current row layout to fill @cols number of widgets /// in row with same @item_width horizontal size. Once called all subsequent widget /// calls greater than @cols will allocate a new row with same layout. /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c /// void nk_layout_row_static(struct nk_context *ctx, float height, int item_width, int cols); /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /// /// Parameter | Description /// ------------|----------------------------------------------------------- /// __ctx__ | Must point to an previously initialized `nk_context` struct after call `nk_begin_xxx` /// __height__ | Holds height of each widget in row or zero for auto layouting /// __width__ | Holds pixel width of each widget in the row /// __columns__ | Number of widget inside row */ NK_API void nk_layout_row_static(struct nk_context *ctx, float height, int item_width, int cols); /*/// #### nk_layout_row_begin /// Starts a new dynamic or fixed row with given height and columns. /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c /// void nk_layout_row_begin(struct nk_context *ctx, enum nk_layout_format fmt, float row_height, int cols); /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /// /// Parameter | Description /// ------------|----------------------------------------------------------- /// __ctx__ | Must point to an previously initialized `nk_context` struct after call `nk_begin_xxx` /// __fmt__ | either `NK_DYNAMIC` for window ratio or `NK_STATIC` for fixed size columns /// __height__ | holds height of each widget in row or zero for auto layouting /// __columns__ | Number of widget inside row */ NK_API void nk_layout_row_begin(struct nk_context *ctx, enum nk_layout_format fmt, float row_height, int cols); /*/// #### nk_layout_row_push /// Specifies either window ratio or width of a single column /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c /// void nk_layout_row_push(struct nk_context*, float value); /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /// /// Parameter | Description /// ------------|----------------------------------------------------------- /// __ctx__ | Must point to an previously initialized `nk_context` struct after call `nk_begin_xxx` /// __value__ | either a window ratio or fixed width depending on @fmt in previous `nk_layout_row_begin` call */ NK_API void nk_layout_row_push(struct nk_context*, float value); /*/// #### nk_layout_row_end /// Finished previously started row /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c /// void nk_layout_row_end(struct nk_context*); /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /// /// Parameter | Description /// ------------|----------------------------------------------------------- /// __ctx__ | Must point to an previously initialized `nk_context` struct after call `nk_begin_xxx` */ NK_API void nk_layout_row_end(struct nk_context*); /*/// #### nk_layout_row /// Specifies row columns in array as either window ratio or size /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c /// void nk_layout_row(struct nk_context*, enum nk_layout_format, float height, int cols, const float *ratio); /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /// /// Parameter | Description /// ------------|----------------------------------------------------------- /// __ctx__ | Must point to an previously initialized `nk_context` struct after call `nk_begin_xxx` /// __fmt__ | Either `NK_DYNAMIC` for window ratio or `NK_STATIC` for fixed size columns /// __height__ | Holds height of each widget in row or zero for auto layouting /// __columns__ | Number of widget inside row */ NK_API void nk_layout_row(struct nk_context*, enum nk_layout_format, float height, int cols, const float *ratio); /*/// #### nk_layout_row_template_begin /// Begins the row template declaration /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c /// void nk_layout_row_template_begin(struct nk_context*, float row_height); /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /// /// Parameter | Description /// ------------|----------------------------------------------------------- /// __ctx__ | Must point to an previously initialized `nk_context` struct after call `nk_begin_xxx` /// __height__ | Holds height of each widget in row or zero for auto layouting */ NK_API void nk_layout_row_template_begin(struct nk_context*, float row_height); /*/// #### nk_layout_row_template_push_dynamic /// Adds a dynamic column that dynamically grows and can go to zero if not enough space /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c /// void nk_layout_row_template_push_dynamic(struct nk_context*); /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /// /// Parameter | Description /// ------------|----------------------------------------------------------- /// __ctx__ | Must point to an previously initialized `nk_context` struct after call `nk_begin_xxx` /// __height__ | Holds height of each widget in row or zero for auto layouting */ NK_API void nk_layout_row_template_push_dynamic(struct nk_context*); /*/// #### nk_layout_row_template_push_variable /// Adds a variable column that dynamically grows but does not shrink below specified pixel width /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c /// void nk_layout_row_template_push_variable(struct nk_context*, float min_width); /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /// /// Parameter | Description /// ------------|----------------------------------------------------------- /// __ctx__ | Must point to an previously initialized `nk_context` struct after call `nk_begin_xxx` /// __width__ | Holds the minimum pixel width the next column must always be */ NK_API void nk_layout_row_template_push_variable(struct nk_context*, float min_width); /*/// #### nk_layout_row_template_push_static /// Adds a static column that does not grow and will always have the same size /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c /// void nk_layout_row_template_push_static(struct nk_context*, float width); /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /// /// Parameter | Description /// ------------|----------------------------------------------------------- /// __ctx__ | Must point to an previously initialized `nk_context` struct after call `nk_begin_xxx` /// __width__ | Holds the absolute pixel width value the next column must be */ NK_API void nk_layout_row_template_push_static(struct nk_context*, float width); /*/// #### nk_layout_row_template_end /// Marks the end of the row template /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c /// void nk_layout_row_template_end(struct nk_context*); /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /// /// Parameter | Description /// ------------|----------------------------------------------------------- /// __ctx__ | Must point to an previously initialized `nk_context` struct after call `nk_begin_xxx` */ NK_API void nk_layout_row_template_end(struct nk_context*); /*/// #### nk_layout_space_begin /// Begins a new layouting space that allows to specify each widgets position and size. /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c /// void nk_layout_space_begin(struct nk_context*, enum nk_layout_format, float height, int widget_count); /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /// /// Parameter | Description /// ------------|----------------------------------------------------------- /// __ctx__ | Must point to an previously initialized `nk_context` struct after call `nk_begin_xxx` /// __fmt__ | Either `NK_DYNAMIC` for window ratio or `NK_STATIC` for fixed size columns /// __height__ | Holds height of each widget in row or zero for auto layouting /// __columns__ | Number of widgets inside row */ NK_API void nk_layout_space_begin(struct nk_context*, enum nk_layout_format, float height, int widget_count); /*/// #### nk_layout_space_push /// Pushes position and size of the next widget in own coordinate space either as pixel or ratio /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c /// void nk_layout_space_push(struct nk_context *ctx, struct nk_rect bounds); /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /// /// Parameter | Description /// ------------|----------------------------------------------------------- /// __ctx__ | Must point to an previously initialized `nk_context` struct after call `nk_layout_space_begin` /// __bounds__ | Position and size in laoyut space local coordinates */ NK_API void nk_layout_space_push(struct nk_context*, struct nk_rect bounds); /*/// #### nk_layout_space_end /// Marks the end of the layout space /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c /// void nk_layout_space_end(struct nk_context*); /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /// /// Parameter | Description /// ------------|----------------------------------------------------------- /// __ctx__ | Must point to an previously initialized `nk_context` struct after call `nk_layout_space_begin` */ NK_API void nk_layout_space_end(struct nk_context*); /*/// #### nk_layout_space_bounds /// Utility function to calculate total space allocated for `nk_layout_space` /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c /// struct nk_rect nk_layout_space_bounds(struct nk_context*); /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /// /// Parameter | Description /// ------------|----------------------------------------------------------- /// __ctx__ | Must point to an previously initialized `nk_context` struct after call `nk_layout_space_begin` /// /// Returns `nk_rect` holding the total space allocated */ NK_API struct nk_rect nk_layout_space_bounds(struct nk_context*); /*/// #### nk_layout_space_to_screen /// Converts vector from nk_layout_space coordinate space into screen space /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c /// struct nk_vec2 nk_layout_space_to_screen(struct nk_context*, struct nk_vec2); /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /// /// Parameter | Description /// ------------|----------------------------------------------------------- /// __ctx__ | Must point to an previously initialized `nk_context` struct after call `nk_layout_space_begin` /// __vec__ | Position to convert from layout space into screen coordinate space /// /// Returns transformed `nk_vec2` in screen space coordinates */ NK_API struct nk_vec2 nk_layout_space_to_screen(struct nk_context*, struct nk_vec2); /*/// #### nk_layout_space_to_local /// Converts vector from layout space into screen space /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c /// struct nk_vec2 nk_layout_space_to_local(struct nk_context*, struct nk_vec2); /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /// /// Parameter | Description /// ------------|----------------------------------------------------------- /// __ctx__ | Must point to an previously initialized `nk_context` struct after call `nk_layout_space_begin` /// __vec__ | Position to convert from screen space into layout coordinate space /// /// Returns transformed `nk_vec2` in layout space coordinates */ NK_API struct nk_vec2 nk_layout_space_to_local(struct nk_context*, struct nk_vec2); /*/// #### nk_layout_space_rect_to_screen /// Converts rectangle from screen space into layout space /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c /// struct nk_rect nk_layout_space_rect_to_screen(struct nk_context*, struct nk_rect); /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /// /// Parameter | Description /// ------------|----------------------------------------------------------- /// __ctx__ | Must point to an previously initialized `nk_context` struct after call `nk_layout_space_begin` /// __bounds__ | Rectangle to convert from layout space into screen space /// /// Returns transformed `nk_rect` in screen space coordinates */ NK_API struct nk_rect nk_layout_space_rect_to_screen(struct nk_context*, struct nk_rect); /*/// #### nk_layout_space_rect_to_local /// Converts rectangle from layout space into screen space /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c /// struct nk_rect nk_layout_space_rect_to_local(struct nk_context*, struct nk_rect); /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /// /// Parameter | Description /// ------------|----------------------------------------------------------- /// __ctx__ | Must point to an previously initialized `nk_context` struct after call `nk_layout_space_begin` /// __bounds__ | Rectangle to convert from layout space into screen space /// /// Returns transformed `nk_rect` in layout space coordinates */ NK_API struct nk_rect nk_layout_space_rect_to_local(struct nk_context*, struct nk_rect); /*/// #### nk_spacer /// Spacer is a dummy widget that consumes space as usual but doesn't draw anything /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c /// void nk_spacer(struct nk_context* ); /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /// /// Parameter | Description /// ------------|----------------------------------------------------------- /// __ctx__ | Must point to an previously initialized `nk_context` struct after call `nk_layout_space_begin` /// */ NK_API void nk_spacer(struct nk_context* ); /* ============================================================================= * * GROUP * * ============================================================================= /// ### Groups /// Groups are basically windows inside windows. They allow to subdivide space /// in a window to layout widgets as a group. Almost all more complex widget /// layouting requirements can be solved using groups and basic layouting /// fuctionality. Groups just like windows are identified by an unique name and /// internally keep track of scrollbar offsets by default. However additional /// versions are provided to directly manage the scrollbar. /// /// #### Usage /// To create a group you have to call one of the three `nk_group_begin_xxx` /// functions to start group declarations and `nk_group_end` at the end. Furthermore it /// is required to check the return value of `nk_group_begin_xxx` and only process /// widgets inside the window if the value is not 0. /// Nesting groups is possible and even encouraged since many layouting schemes /// can only be achieved by nesting. Groups, unlike windows, need `nk_group_end` /// to be only called if the corresponding `nk_group_begin_xxx` call does not return 0: /// /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c /// if (nk_group_begin_xxx(ctx, ...) { /// // [... widgets ...] /// nk_group_end(ctx); /// } /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /// /// In the grand concept groups can be called after starting a window /// with `nk_begin_xxx` and before calling `nk_end`: /// /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c /// struct nk_context ctx; /// nk_init_xxx(&ctx, ...); /// while (1) { /// // Input /// Event evt; /// nk_input_begin(&ctx); /// while (GetEvent(&evt)) { /// if (evt.type == MOUSE_MOVE) /// nk_input_motion(&ctx, evt.motion.x, evt.motion.y); /// else if (evt.type == [...]) { /// nk_input_xxx(...); /// } /// } /// nk_input_end(&ctx); /// // /// // Window /// if (nk_begin_xxx(...) { /// // [...widgets...] /// nk_layout_row_dynamic(...); /// if (nk_group_begin_xxx(ctx, ...) { /// //[... widgets ...] /// nk_group_end(ctx); /// } /// } /// nk_end(ctx); /// // /// // Draw /// const struct nk_command *cmd = 0; /// nk_foreach(cmd, &ctx) { /// switch (cmd->type) { /// case NK_COMMAND_LINE: /// your_draw_line_function(...) /// break; /// case NK_COMMAND_RECT /// your_draw_rect_function(...) /// break; /// case ...: /// // [...] /// } /// nk_clear(&ctx); /// } /// nk_free(&ctx); /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /// #### Reference /// Function | Description /// --------------------------------|------------------------------------------- /// nk_group_begin | Start a new group with internal scrollbar handling /// nk_group_begin_titled | Start a new group with separated name and title and internal scrollbar handling /// nk_group_end | Ends a group. Should only be called if nk_group_begin returned non-zero /// nk_group_scrolled_offset_begin | Start a new group with manual separated handling of scrollbar x- and y-offset /// nk_group_scrolled_begin | Start a new group with manual scrollbar handling /// nk_group_scrolled_end | Ends a group with manual scrollbar handling. Should only be called if nk_group_begin returned non-zero /// nk_group_get_scroll | Gets the scroll offset for the given group /// nk_group_set_scroll | Sets the scroll offset for the given group */ /*/// #### nk_group_begin /// Starts a new widget group. Requires a previous layouting function to specify a pos/size. /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c /// nk_bool nk_group_begin(struct nk_context*, const char *title, nk_flags); /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /// /// Parameter | Description /// ------------|----------------------------------------------------------- /// __ctx__ | Must point to an previously initialized `nk_context` struct /// __title__ | Must be an unique identifier for this group that is also used for the group header /// __flags__ | Window flags defined in the nk_panel_flags section with a number of different group behaviors /// /// Returns `true(1)` if visible and fillable with widgets or `false(0)` otherwise */ NK_API nk_bool nk_group_begin(struct nk_context*, const char *title, nk_flags); /*/// #### nk_group_begin_titled /// Starts a new widget group. Requires a previous layouting function to specify a pos/size. /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c /// nk_bool nk_group_begin_titled(struct nk_context*, const char *name, const char *title, nk_flags); /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /// /// Parameter | Description /// ------------|----------------------------------------------------------- /// __ctx__ | Must point to an previously initialized `nk_context` struct /// __id__ | Must be an unique identifier for this group /// __title__ | Group header title /// __flags__ | Window flags defined in the nk_panel_flags section with a number of different group behaviors /// /// Returns `true(1)` if visible and fillable with widgets or `false(0)` otherwise */ NK_API nk_bool nk_group_begin_titled(struct nk_context*, const char *name, const char *title, nk_flags); /*/// #### nk_group_end /// Ends a widget group /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c /// void nk_group_end(struct nk_context*); /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /// /// Parameter | Description /// ------------|----------------------------------------------------------- /// __ctx__ | Must point to an previously initialized `nk_context` struct */ NK_API void nk_group_end(struct nk_context*); /*/// #### nk_group_scrolled_offset_begin /// starts a new widget group. requires a previous layouting function to specify /// a size. Does not keep track of scrollbar. /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c /// nk_bool nk_group_scrolled_offset_begin(struct nk_context*, nk_uint *x_offset, nk_uint *y_offset, const char *title, nk_flags flags); /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /// /// Parameter | Description /// ------------|----------------------------------------------------------- /// __ctx__ | Must point to an previously initialized `nk_context` struct /// __x_offset__| Scrollbar x-offset to offset all widgets inside the group horizontally. /// __y_offset__| Scrollbar y-offset to offset all widgets inside the group vertically /// __title__ | Window unique group title used to both identify and display in the group header /// __flags__ | Window flags from the nk_panel_flags section /// /// Returns `true(1)` if visible and fillable with widgets or `false(0)` otherwise */ NK_API nk_bool nk_group_scrolled_offset_begin(struct nk_context*, nk_uint *x_offset, nk_uint *y_offset, const char *title, nk_flags flags); /*/// #### nk_group_scrolled_begin /// Starts a new widget group. requires a previous /// layouting function to specify a size. Does not keep track of scrollbar. /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c /// nk_bool nk_group_scrolled_begin(struct nk_context*, struct nk_scroll *off, const char *title, nk_flags); /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /// /// Parameter | Description /// ------------|----------------------------------------------------------- /// __ctx__ | Must point to an previously initialized `nk_context` struct /// __off__ | Both x- and y- scroll offset. Allows for manual scrollbar control /// __title__ | Window unique group title used to both identify and display in the group header /// __flags__ | Window flags from nk_panel_flags section /// /// Returns `true(1)` if visible and fillable with widgets or `false(0)` otherwise */ NK_API nk_bool nk_group_scrolled_begin(struct nk_context*, struct nk_scroll *off, const char *title, nk_flags); /*/// #### nk_group_scrolled_end /// Ends a widget group after calling nk_group_scrolled_offset_begin or nk_group_scrolled_begin. /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c /// void nk_group_scrolled_end(struct nk_context*); /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /// /// Parameter | Description /// ------------|----------------------------------------------------------- /// __ctx__ | Must point to an previously initialized `nk_context` struct */ NK_API void nk_group_scrolled_end(struct nk_context*); /*/// #### nk_group_get_scroll /// Gets the scroll position of the given group. /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c /// void nk_group_get_scroll(struct nk_context*, const char *id, nk_uint *x_offset, nk_uint *y_offset); /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /// /// Parameter | Description /// -------------|----------------------------------------------------------- /// __ctx__ | Must point to an previously initialized `nk_context` struct /// __id__ | The id of the group to get the scroll position of /// __x_offset__ | A pointer to the x offset output (or NULL to ignore) /// __y_offset__ | A pointer to the y offset output (or NULL to ignore) */ NK_API void nk_group_get_scroll(struct nk_context*, const char *id, nk_uint *x_offset, nk_uint *y_offset); /*/// #### nk_group_set_scroll /// Sets the scroll position of the given group. /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c /// void nk_group_set_scroll(struct nk_context*, const char *id, nk_uint x_offset, nk_uint y_offset); /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /// /// Parameter | Description /// -------------|----------------------------------------------------------- /// __ctx__ | Must point to an previously initialized `nk_context` struct /// __id__ | The id of the group to scroll /// __x_offset__ | The x offset to scroll to /// __y_offset__ | The y offset to scroll to */ NK_API void nk_group_set_scroll(struct nk_context*, const char *id, nk_uint x_offset, nk_uint y_offset); /* ============================================================================= * * TREE * * ============================================================================= /// ### Tree /// Trees represent two different concept. First the concept of a collapsible /// UI section that can be either in a hidden or visible state. They allow the UI /// user to selectively minimize the current set of visible UI to comprehend. /// The second concept are tree widgets for visual UI representation of trees.

/// /// Trees thereby can be nested for tree representations and multiple nested /// collapsible UI sections. All trees are started by calling of the /// `nk_tree_xxx_push_tree` functions and ended by calling one of the /// `nk_tree_xxx_pop_xxx()` functions. Each starting functions takes a title label /// and optionally an image to be displayed and the initial collapse state from /// the nk_collapse_states section.

/// /// The runtime state of the tree is either stored outside the library by the caller /// or inside which requires a unique ID. The unique ID can either be generated /// automatically from `__FILE__` and `__LINE__` with function `nk_tree_push`, /// by `__FILE__` and a user provided ID generated for example by loop index with /// function `nk_tree_push_id` or completely provided from outside by user with /// function `nk_tree_push_hashed`. /// /// #### Usage /// To create a tree you have to call one of the seven `nk_tree_xxx_push_xxx` /// functions to start a collapsible UI section and `nk_tree_xxx_pop` to mark the /// end. /// Each starting function will either return `false(0)` if the tree is collapsed /// or hidden and therefore does not need to be filled with content or `true(1)` /// if visible and required to be filled. /// /// !!! Note /// The tree header does not require and layouting function and instead /// calculates a auto height based on the currently used font size /// /// The tree ending functions only need to be called if the tree content is /// actually visible. So make sure the tree push function is guarded by `if` /// and the pop call is only taken if the tree is visible. /// /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c /// if (nk_tree_push(ctx, NK_TREE_TAB, "Tree", NK_MINIMIZED)) { /// nk_layout_row_dynamic(...); /// nk_widget(...); /// nk_tree_pop(ctx); /// } /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /// /// #### Reference /// Function | Description /// ----------------------------|------------------------------------------- /// nk_tree_push | Start a collapsible UI section with internal state management /// nk_tree_push_id | Start a collapsible UI section with internal state management callable in a look /// nk_tree_push_hashed | Start a collapsible UI section with internal state management with full control over internal unique ID use to store state /// nk_tree_image_push | Start a collapsible UI section with image and label header /// nk_tree_image_push_id | Start a collapsible UI section with image and label header and internal state management callable in a look /// nk_tree_image_push_hashed | Start a collapsible UI section with image and label header and internal state management with full control over internal unique ID use to store state /// nk_tree_pop | Ends a collapsible UI section // /// nk_tree_state_push | Start a collapsible UI section with external state management /// nk_tree_state_image_push | Start a collapsible UI section with image and label header and external state management /// nk_tree_state_pop | Ends a collapsabale UI section /// /// #### nk_tree_type /// Flag | Description /// ----------------|---------------------------------------- /// NK_TREE_NODE | Highlighted tree header to mark a collapsible UI section /// NK_TREE_TAB | Non-highlighted tree header closer to tree representations */ /*/// #### nk_tree_push /// Starts a collapsible UI section with internal state management /// !!! WARNING /// To keep track of the runtime tree collapsible state this function uses /// defines `__FILE__` and `__LINE__` to generate a unique ID. If you want /// to call this function in a loop please use `nk_tree_push_id` or /// `nk_tree_push_hashed` instead. /// /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c /// #define nk_tree_push(ctx, type, title, state) /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /// /// Parameter | Description /// ------------|----------------------------------------------------------- /// __ctx__ | Must point to an previously initialized `nk_context` struct /// __type__ | Value from the nk_tree_type section to visually mark a tree node header as either a collapseable UI section or tree node /// __title__ | Label printed in the tree header /// __state__ | Initial tree state value out of nk_collapse_states /// /// Returns `true(1)` if visible and fillable with widgets or `false(0)` otherwise */ #define nk_tree_push(ctx, type, title, state) nk_tree_push_hashed(ctx, type, title, state, NK_FILE_LINE,nk_strlen(NK_FILE_LINE),__LINE__) /*/// #### nk_tree_push_id /// Starts a collapsible UI section with internal state management callable in a look /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c /// #define nk_tree_push_id(ctx, type, title, state, id) /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /// /// Parameter | Description /// ------------|----------------------------------------------------------- /// __ctx__ | Must point to an previously initialized `nk_context` struct /// __type__ | Value from the nk_tree_type section to visually mark a tree node header as either a collapseable UI section or tree node /// __title__ | Label printed in the tree header /// __state__ | Initial tree state value out of nk_collapse_states /// __id__ | Loop counter index if this function is called in a loop /// /// Returns `true(1)` if visible and fillable with widgets or `false(0)` otherwise */ #define nk_tree_push_id(ctx, type, title, state, id) nk_tree_push_hashed(ctx, type, title, state, NK_FILE_LINE,nk_strlen(NK_FILE_LINE),id) /*/// #### nk_tree_push_hashed /// Start a collapsible UI section with internal state management with full /// control over internal unique ID used to store state /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c /// nk_bool nk_tree_push_hashed(struct nk_context*, enum nk_tree_type, const char *title, enum nk_collapse_states initial_state, const char *hash, int len,int seed); /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /// /// Parameter | Description /// ------------|----------------------------------------------------------- /// __ctx__ | Must point to an previously initialized `nk_context` struct /// __type__ | Value from the nk_tree_type section to visually mark a tree node header as either a collapseable UI section or tree node /// __title__ | Label printed in the tree header /// __state__ | Initial tree state value out of nk_collapse_states /// __hash__ | Memory block or string to generate the ID from /// __len__ | Size of passed memory block or string in __hash__ /// __seed__ | Seeding value if this function is called in a loop or default to `0` /// /// Returns `true(1)` if visible and fillable with widgets or `false(0)` otherwise */ NK_API nk_bool nk_tree_push_hashed(struct nk_context*, enum nk_tree_type, const char *title, enum nk_collapse_states initial_state, const char *hash, int len,int seed); /*/// #### nk_tree_image_push /// Start a collapsible UI section with image and label header /// !!! WARNING /// To keep track of the runtime tree collapsible state this function uses /// defines `__FILE__` and `__LINE__` to generate a unique ID. If you want /// to call this function in a loop please use `nk_tree_image_push_id` or /// `nk_tree_image_push_hashed` instead. /// /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c /// #define nk_tree_image_push(ctx, type, img, title, state) /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // /// Parameter | Description /// ------------|----------------------------------------------------------- /// __ctx__ | Must point to an previously initialized `nk_context` struct /// __type__ | Value from the nk_tree_type section to visually mark a tree node header as either a collapseable UI section or tree node /// __img__ | Image to display inside the header on the left of the label /// __title__ | Label printed in the tree header /// __state__ | Initial tree state value out of nk_collapse_states /// /// Returns `true(1)` if visible and fillable with widgets or `false(0)` otherwise */ #define nk_tree_image_push(ctx, type, img, title, state) nk_tree_image_push_hashed(ctx, type, img, title, state, NK_FILE_LINE,nk_strlen(NK_FILE_LINE),__LINE__) /*/// #### nk_tree_image_push_id /// Start a collapsible UI section with image and label header and internal state /// management callable in a look /// /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c /// #define nk_tree_image_push_id(ctx, type, img, title, state, id) /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /// /// Parameter | Description /// ------------|----------------------------------------------------------- /// __ctx__ | Must point to an previously initialized `nk_context` struct /// __type__ | Value from the nk_tree_type section to visually mark a tree node header as either a collapseable UI section or tree node /// __img__ | Image to display inside the header on the left of the label /// __title__ | Label printed in the tree header /// __state__ | Initial tree state value out of nk_collapse_states /// __id__ | Loop counter index if this function is called in a loop /// /// Returns `true(1)` if visible and fillable with widgets or `false(0)` otherwise */ #define nk_tree_image_push_id(ctx, type, img, title, state, id) nk_tree_image_push_hashed(ctx, type, img, title, state, NK_FILE_LINE,nk_strlen(NK_FILE_LINE),id) /*/// #### nk_tree_image_push_hashed /// Start a collapsible UI section with internal state management with full /// control over internal unique ID used to store state /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c /// nk_bool nk_tree_image_push_hashed(struct nk_context*, enum nk_tree_type, struct nk_image, const char *title, enum nk_collapse_states initial_state, const char *hash, int len,int seed); /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /// /// Parameter | Description /// ------------|----------------------------------------------------------- /// __ctx__ | Must point to an previously initialized `nk_context` struct /// __type__ | Value from the nk_tree_type section to visually mark a tree node header as either a collapseable UI section or tree node /// __img__ | Image to display inside the header on the left of the label /// __title__ | Label printed in the tree header /// __state__ | Initial tree state value out of nk_collapse_states /// __hash__ | Memory block or string to generate the ID from /// __len__ | Size of passed memory block or string in __hash__ /// __seed__ | Seeding value if this function is called in a loop or default to `0` /// /// Returns `true(1)` if visible and fillable with widgets or `false(0)` otherwise */ NK_API nk_bool nk_tree_image_push_hashed(struct nk_context*, enum nk_tree_type, struct nk_image, const char *title, enum nk_collapse_states initial_state, const char *hash, int len,int seed); /*/// #### nk_tree_pop /// Ends a collapsabale UI section /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c /// void nk_tree_pop(struct nk_context*); /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /// /// Parameter | Description /// ------------|----------------------------------------------------------- /// __ctx__ | Must point to an previously initialized `nk_context` struct after calling `nk_tree_xxx_push_xxx` */ NK_API void nk_tree_pop(struct nk_context*); /*/// #### nk_tree_state_push /// Start a collapsible UI section with external state management /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c /// nk_bool nk_tree_state_push(struct nk_context*, enum nk_tree_type, const char *title, enum nk_collapse_states *state); /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /// /// Parameter | Description /// ------------|----------------------------------------------------------- /// __ctx__ | Must point to an previously initialized `nk_context` struct after calling `nk_tree_xxx_push_xxx` /// __type__ | Value from the nk_tree_type section to visually mark a tree node header as either a collapseable UI section or tree node /// __title__ | Label printed in the tree header /// __state__ | Persistent state to update /// /// Returns `true(1)` if visible and fillable with widgets or `false(0)` otherwise */ NK_API nk_bool nk_tree_state_push(struct nk_context*, enum nk_tree_type, const char *title, enum nk_collapse_states *state); /*/// #### nk_tree_state_image_push /// Start a collapsible UI section with image and label header and external state management /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c /// nk_bool nk_tree_state_image_push(struct nk_context*, enum nk_tree_type, struct nk_image, const char *title, enum nk_collapse_states *state); /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /// /// Parameter | Description /// ------------|----------------------------------------------------------- /// __ctx__ | Must point to an previously initialized `nk_context` struct after calling `nk_tree_xxx_push_xxx` /// __img__ | Image to display inside the header on the left of the label /// __type__ | Value from the nk_tree_type section to visually mark a tree node header as either a collapseable UI section or tree node /// __title__ | Label printed in the tree header /// __state__ | Persistent state to update /// /// Returns `true(1)` if visible and fillable with widgets or `false(0)` otherwise */ NK_API nk_bool nk_tree_state_image_push(struct nk_context*, enum nk_tree_type, struct nk_image, const char *title, enum nk_collapse_states *state); /*/// #### nk_tree_state_pop /// Ends a collapsabale UI section /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c /// void nk_tree_state_pop(struct nk_context*); /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /// /// Parameter | Description /// ------------|----------------------------------------------------------- /// __ctx__ | Must point to an previously initialized `nk_context` struct after calling `nk_tree_xxx_push_xxx` */ NK_API void nk_tree_state_pop(struct nk_context*); #define nk_tree_element_push(ctx, type, title, state, sel) nk_tree_element_push_hashed(ctx, type, title, state, sel, NK_FILE_LINE,nk_strlen(NK_FILE_LINE),__LINE__) #define nk_tree_element_push_id(ctx, type, title, state, sel, id) nk_tree_element_push_hashed(ctx, type, title, state, sel, NK_FILE_LINE,nk_strlen(NK_FILE_LINE),id) NK_API nk_bool nk_tree_element_push_hashed(struct nk_context*, enum nk_tree_type, const char *title, enum nk_collapse_states initial_state, nk_bool *selected, const char *hash, int len, int seed); NK_API nk_bool nk_tree_element_image_push_hashed(struct nk_context*, enum nk_tree_type, struct nk_image, const char *title, enum nk_collapse_states initial_state, nk_bool *selected, const char *hash, int len,int seed); NK_API void nk_tree_element_pop(struct nk_context*); /* ============================================================================= * * LIST VIEW * * ============================================================================= */ struct nk_list_view { /* public: */ int begin, end, count; /* private: */ int total_height; struct nk_context *ctx; nk_uint *scroll_pointer; nk_uint scroll_value; }; NK_API nk_bool nk_list_view_begin(struct nk_context*, struct nk_list_view *out, const char *id, nk_flags, int row_height, int row_count); NK_API void nk_list_view_end(struct nk_list_view*); /* ============================================================================= * * WIDGET * * ============================================================================= */ enum nk_widget_layout_states { NK_WIDGET_INVALID, /* The widget cannot be seen and is completely out of view */ NK_WIDGET_VALID, /* The widget is completely inside the window and can be updated and drawn */ NK_WIDGET_ROM /* The widget is partially visible and cannot be updated */ }; enum nk_widget_states { NK_WIDGET_STATE_MODIFIED = NK_FLAG(1), NK_WIDGET_STATE_INACTIVE = NK_FLAG(2), /* widget is neither active nor hovered */ NK_WIDGET_STATE_ENTERED = NK_FLAG(3), /* widget has been hovered on the current frame */ NK_WIDGET_STATE_HOVER = NK_FLAG(4), /* widget is being hovered */ NK_WIDGET_STATE_ACTIVED = NK_FLAG(5),/* widget is currently activated */ NK_WIDGET_STATE_LEFT = NK_FLAG(6), /* widget is from this frame on not hovered anymore */ NK_WIDGET_STATE_HOVERED = NK_WIDGET_STATE_HOVER|NK_WIDGET_STATE_MODIFIED, /* widget is being hovered */ NK_WIDGET_STATE_ACTIVE = NK_WIDGET_STATE_ACTIVED|NK_WIDGET_STATE_MODIFIED /* widget is currently activated */ }; NK_API enum nk_widget_layout_states nk_widget(struct nk_rect*, const struct nk_context*); NK_API enum nk_widget_layout_states nk_widget_fitting(struct nk_rect*, struct nk_context*, struct nk_vec2); NK_API struct nk_rect nk_widget_bounds(struct nk_context*); NK_API struct nk_vec2 nk_widget_position(struct nk_context*); NK_API struct nk_vec2 nk_widget_size(struct nk_context*); NK_API float nk_widget_width(struct nk_context*); NK_API float nk_widget_height(struct nk_context*); NK_API nk_bool nk_widget_is_hovered(struct nk_context*); NK_API nk_bool nk_widget_is_mouse_clicked(struct nk_context*, enum nk_buttons); NK_API nk_bool nk_widget_has_mouse_click_down(struct nk_context*, enum nk_buttons, nk_bool down); NK_API void nk_spacing(struct nk_context*, int cols); /* ============================================================================= * * TEXT * * ============================================================================= */ enum nk_text_align { NK_TEXT_ALIGN_LEFT = 0x01, NK_TEXT_ALIGN_CENTERED = 0x02, NK_TEXT_ALIGN_RIGHT = 0x04, NK_TEXT_ALIGN_TOP = 0x08, NK_TEXT_ALIGN_MIDDLE = 0x10, NK_TEXT_ALIGN_BOTTOM = 0x20 }; enum nk_text_alignment { NK_TEXT_LEFT = NK_TEXT_ALIGN_MIDDLE|NK_TEXT_ALIGN_LEFT, NK_TEXT_CENTERED = NK_TEXT_ALIGN_MIDDLE|NK_TEXT_ALIGN_CENTERED, NK_TEXT_RIGHT = NK_TEXT_ALIGN_MIDDLE|NK_TEXT_ALIGN_RIGHT }; NK_API void nk_text(struct nk_context*, const char*, int, nk_flags); NK_API void nk_text_colored(struct nk_context*, const char*, int, nk_flags, struct nk_color); NK_API void nk_text_wrap(struct nk_context*, const char*, int); NK_API void nk_text_wrap_colored(struct nk_context*, const char*, int, struct nk_color); NK_API void nk_label(struct nk_context*, const char*, nk_flags align); NK_API void nk_label_colored(struct nk_context*, const char*, nk_flags align, struct nk_color); NK_API void nk_label_wrap(struct nk_context*, const char*); NK_API void nk_label_colored_wrap(struct nk_context*, const char*, struct nk_color); NK_API void nk_image(struct nk_context*, struct nk_image); NK_API void nk_image_color(struct nk_context*, struct nk_image, struct nk_color); #ifdef NK_INCLUDE_STANDARD_VARARGS NK_API void nk_labelf(struct nk_context*, nk_flags, NK_PRINTF_FORMAT_STRING const char*, ...) NK_PRINTF_VARARG_FUNC(3); NK_API void nk_labelf_colored(struct nk_context*, nk_flags, struct nk_color, NK_PRINTF_FORMAT_STRING const char*,...) NK_PRINTF_VARARG_FUNC(4); NK_API void nk_labelf_wrap(struct nk_context*, NK_PRINTF_FORMAT_STRING const char*,...) NK_PRINTF_VARARG_FUNC(2); NK_API void nk_labelf_colored_wrap(struct nk_context*, struct nk_color, NK_PRINTF_FORMAT_STRING const char*,...) NK_PRINTF_VARARG_FUNC(3); NK_API void nk_labelfv(struct nk_context*, nk_flags, NK_PRINTF_FORMAT_STRING const char*, va_list) NK_PRINTF_VALIST_FUNC(3); NK_API void nk_labelfv_colored(struct nk_context*, nk_flags, struct nk_color, NK_PRINTF_FORMAT_STRING const char*, va_list) NK_PRINTF_VALIST_FUNC(4); NK_API void nk_labelfv_wrap(struct nk_context*, NK_PRINTF_FORMAT_STRING const char*, va_list) NK_PRINTF_VALIST_FUNC(2); NK_API void nk_labelfv_colored_wrap(struct nk_context*, struct nk_color, NK_PRINTF_FORMAT_STRING const char*, va_list) NK_PRINTF_VALIST_FUNC(3); NK_API void nk_value_bool(struct nk_context*, const char *prefix, int); NK_API void nk_value_int(struct nk_context*, const char *prefix, int); NK_API void nk_value_uint(struct nk_context*, const char *prefix, unsigned int); NK_API void nk_value_float(struct nk_context*, const char *prefix, float); NK_API void nk_value_color_byte(struct nk_context*, const char *prefix, struct nk_color); NK_API void nk_value_color_float(struct nk_context*, const char *prefix, struct nk_color); NK_API void nk_value_color_hex(struct nk_context*, const char *prefix, struct nk_color); #endif /* ============================================================================= * * BUTTON * * ============================================================================= */ NK_API nk_bool nk_button_text(struct nk_context*, const char *title, int len); NK_API nk_bool nk_button_label(struct nk_context*, const char *title); NK_API nk_bool nk_button_color(struct nk_context*, struct nk_color); NK_API nk_bool nk_button_symbol(struct nk_context*, enum nk_symbol_type); NK_API nk_bool nk_button_image(struct nk_context*, struct nk_image img); NK_API nk_bool nk_button_symbol_label(struct nk_context*, enum nk_symbol_type, const char*, nk_flags text_alignment); NK_API nk_bool nk_button_symbol_text(struct nk_context*, enum nk_symbol_type, const char*, int, nk_flags alignment); NK_API nk_bool nk_button_image_label(struct nk_context*, struct nk_image img, const char*, nk_flags text_alignment); NK_API nk_bool nk_button_image_text(struct nk_context*, struct nk_image img, const char*, int, nk_flags alignment); NK_API nk_bool nk_button_text_styled(struct nk_context*, const struct nk_style_button*, const char *title, int len); NK_API nk_bool nk_button_label_styled(struct nk_context*, const struct nk_style_button*, const char *title); NK_API nk_bool nk_button_symbol_styled(struct nk_context*, const struct nk_style_button*, enum nk_symbol_type); NK_API nk_bool nk_button_image_styled(struct nk_context*, const struct nk_style_button*, struct nk_image img); NK_API nk_bool nk_button_symbol_text_styled(struct nk_context*,const struct nk_style_button*, enum nk_symbol_type, const char*, int, nk_flags alignment); NK_API nk_bool nk_button_symbol_label_styled(struct nk_context *ctx, const struct nk_style_button *style, enum nk_symbol_type symbol, const char *title, nk_flags align); NK_API nk_bool nk_button_image_label_styled(struct nk_context*,const struct nk_style_button*, struct nk_image img, const char*, nk_flags text_alignment); NK_API nk_bool nk_button_image_text_styled(struct nk_context*,const struct nk_style_button*, struct nk_image img, const char*, int, nk_flags alignment); NK_API void nk_button_set_behavior(struct nk_context*, enum nk_button_behavior); NK_API nk_bool nk_button_push_behavior(struct nk_context*, enum nk_button_behavior); NK_API nk_bool nk_button_pop_behavior(struct nk_context*); /* ============================================================================= * * CHECKBOX * * ============================================================================= */ NK_API nk_bool nk_check_label(struct nk_context*, const char*, nk_bool active); NK_API nk_bool nk_check_text(struct nk_context*, const char*, int, nk_bool active); NK_API unsigned nk_check_flags_label(struct nk_context*, const char*, unsigned int flags, unsigned int value); NK_API unsigned nk_check_flags_text(struct nk_context*, const char*, int, unsigned int flags, unsigned int value); NK_API nk_bool nk_checkbox_label(struct nk_context*, const char*, nk_bool *active); NK_API nk_bool nk_checkbox_text(struct nk_context*, const char*, int, nk_bool *active); NK_API nk_bool nk_checkbox_flags_label(struct nk_context*, const char*, unsigned int *flags, unsigned int value); NK_API nk_bool nk_checkbox_flags_text(struct nk_context*, const char*, int, unsigned int *flags, unsigned int value); /* ============================================================================= * * RADIO BUTTON * * ============================================================================= */ NK_API nk_bool nk_radio_label(struct nk_context*, const char*, nk_bool *active); NK_API nk_bool nk_radio_text(struct nk_context*, const char*, int, nk_bool *active); NK_API nk_bool nk_option_label(struct nk_context*, const char*, nk_bool active); NK_API nk_bool nk_option_text(struct nk_context*, const char*, int, nk_bool active); /* ============================================================================= * * SELECTABLE * * ============================================================================= */ NK_API nk_bool nk_selectable_label(struct nk_context*, const char*, nk_flags align, nk_bool *value); NK_API nk_bool nk_selectable_text(struct nk_context*, const char*, int, nk_flags align, nk_bool *value); NK_API nk_bool nk_selectable_image_label(struct nk_context*,struct nk_image, const char*, nk_flags align, nk_bool *value); NK_API nk_bool nk_selectable_image_text(struct nk_context*,struct nk_image, const char*, int, nk_flags align, nk_bool *value); NK_API nk_bool nk_selectable_symbol_label(struct nk_context*,enum nk_symbol_type, const char*, nk_flags align, nk_bool *value); NK_API nk_bool nk_selectable_symbol_text(struct nk_context*,enum nk_symbol_type, const char*, int, nk_flags align, nk_bool *value); NK_API nk_bool nk_select_label(struct nk_context*, const char*, nk_flags align, nk_bool value); NK_API nk_bool nk_select_text(struct nk_context*, const char*, int, nk_flags align, nk_bool value); NK_API nk_bool nk_select_image_label(struct nk_context*, struct nk_image,const char*, nk_flags align, nk_bool value); NK_API nk_bool nk_select_image_text(struct nk_context*, struct nk_image,const char*, int, nk_flags align, nk_bool value); NK_API nk_bool nk_select_symbol_label(struct nk_context*,enum nk_symbol_type, const char*, nk_flags align, nk_bool value); NK_API nk_bool nk_select_symbol_text(struct nk_context*,enum nk_symbol_type, const char*, int, nk_flags align, nk_bool value); /* ============================================================================= * * SLIDER * * ============================================================================= */ NK_API float nk_slide_float(struct nk_context*, float min, float val, float max, float step); NK_API int nk_slide_int(struct nk_context*, int min, int val, int max, int step); NK_API nk_bool nk_slider_float(struct nk_context*, float min, float *val, float max, float step); NK_API nk_bool nk_slider_int(struct nk_context*, int min, int *val, int max, int step); /* ============================================================================= * * PROGRESSBAR * * ============================================================================= */ NK_API nk_bool nk_progress(struct nk_context*, nk_size *cur, nk_size max, nk_bool modifyable); NK_API nk_size nk_prog(struct nk_context*, nk_size cur, nk_size max, nk_bool modifyable); /* ============================================================================= * * COLOR PICKER * * ============================================================================= */ NK_API struct nk_colorf nk_color_picker(struct nk_context*, struct nk_colorf, enum nk_color_format); NK_API nk_bool nk_color_pick(struct nk_context*, struct nk_colorf*, enum nk_color_format); /* ============================================================================= * * PROPERTIES * * ============================================================================= /// ### Properties /// Properties are the main value modification widgets in Nuklear. Changing a value /// can be achieved by dragging, adding/removing incremental steps on button click /// or by directly typing a number. /// /// #### Usage /// Each property requires a unique name for identification that is also used for /// displaying a label. If you want to use the same name multiple times make sure /// add a '#' before your name. The '#' will not be shown but will generate a /// unique ID. Each property also takes in a minimum and maximum value. If you want /// to make use of the complete number range of a type just use the provided /// type limits from `limits.h`. For example `INT_MIN` and `INT_MAX` for /// `nk_property_int` and `nk_propertyi`. In additional each property takes in /// a increment value that will be added or subtracted if either the increment /// decrement button is clicked. Finally there is a value for increment per pixel /// dragged that is added or subtracted from the value. /// /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c /// int value = 0; /// struct nk_context ctx; /// nk_init_xxx(&ctx, ...); /// while (1) { /// // Input /// Event evt; /// nk_input_begin(&ctx); /// while (GetEvent(&evt)) { /// if (evt.type == MOUSE_MOVE) /// nk_input_motion(&ctx, evt.motion.x, evt.motion.y); /// else if (evt.type == [...]) { /// nk_input_xxx(...); /// } /// } /// nk_input_end(&ctx); /// // /// // Window /// if (nk_begin_xxx(...) { /// // Property /// nk_layout_row_dynamic(...); /// nk_property_int(ctx, "ID", INT_MIN, &value, INT_MAX, 1, 1); /// } /// nk_end(ctx); /// // /// // Draw /// const struct nk_command *cmd = 0; /// nk_foreach(cmd, &ctx) { /// switch (cmd->type) { /// case NK_COMMAND_LINE: /// your_draw_line_function(...) /// break; /// case NK_COMMAND_RECT /// your_draw_rect_function(...) /// break; /// case ...: /// // [...] /// } /// nk_clear(&ctx); /// } /// nk_free(&ctx); /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /// /// #### Reference /// Function | Description /// --------------------|------------------------------------------- /// nk_property_int | Integer property directly modifying a passed in value /// nk_property_float | Float property directly modifying a passed in value /// nk_property_double | Double property directly modifying a passed in value /// nk_propertyi | Integer property returning the modified int value /// nk_propertyf | Float property returning the modified float value /// nk_propertyd | Double property returning the modified double value /// */ /*/// #### nk_property_int /// Integer property directly modifying a passed in value /// !!! WARNING /// To generate a unique property ID using the same label make sure to insert /// a `#` at the beginning. It will not be shown but guarantees correct behavior. /// /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c /// void nk_property_int(struct nk_context *ctx, const char *name, int min, int *val, int max, int step, float inc_per_pixel); /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /// /// Parameter | Description /// --------------------|----------------------------------------------------------- /// __ctx__ | Must point to an previously initialized `nk_context` struct after calling a layouting function /// __name__ | String used both as a label as well as a unique identifier /// __min__ | Minimum value not allowed to be underflown /// __val__ | Integer pointer to be modified /// __max__ | Maximum value not allowed to be overflown /// __step__ | Increment added and subtracted on increment and decrement button /// __inc_per_pixel__ | Value per pixel added or subtracted on dragging */ NK_API void nk_property_int(struct nk_context*, const char *name, int min, int *val, int max, int step, float inc_per_pixel); /*/// #### nk_property_float /// Float property directly modifying a passed in value /// !!! WARNING /// To generate a unique property ID using the same label make sure to insert /// a `#` at the beginning. It will not be shown but guarantees correct behavior. /// /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c /// void nk_property_float(struct nk_context *ctx, const char *name, float min, float *val, float max, float step, float inc_per_pixel); /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /// /// Parameter | Description /// --------------------|----------------------------------------------------------- /// __ctx__ | Must point to an previously initialized `nk_context` struct after calling a layouting function /// __name__ | String used both as a label as well as a unique identifier /// __min__ | Minimum value not allowed to be underflown /// __val__ | Float pointer to be modified /// __max__ | Maximum value not allowed to be overflown /// __step__ | Increment added and subtracted on increment and decrement button /// __inc_per_pixel__ | Value per pixel added or subtracted on dragging */ NK_API void nk_property_float(struct nk_context*, const char *name, float min, float *val, float max, float step, float inc_per_pixel); /*/// #### nk_property_double /// Double property directly modifying a passed in value /// !!! WARNING /// To generate a unique property ID using the same label make sure to insert /// a `#` at the beginning. It will not be shown but guarantees correct behavior. /// /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c /// void nk_property_double(struct nk_context *ctx, const char *name, double min, double *val, double max, double step, double inc_per_pixel); /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /// /// Parameter | Description /// --------------------|----------------------------------------------------------- /// __ctx__ | Must point to an previously initialized `nk_context` struct after calling a layouting function /// __name__ | String used both as a label as well as a unique identifier /// __min__ | Minimum value not allowed to be underflown /// __val__ | Double pointer to be modified /// __max__ | Maximum value not allowed to be overflown /// __step__ | Increment added and subtracted on increment and decrement button /// __inc_per_pixel__ | Value per pixel added or subtracted on dragging */ NK_API void nk_property_double(struct nk_context*, const char *name, double min, double *val, double max, double step, float inc_per_pixel); /*/// #### nk_propertyi /// Integer property modifying a passed in value and returning the new value /// !!! WARNING /// To generate a unique property ID using the same label make sure to insert /// a `#` at the beginning. It will not be shown but guarantees correct behavior. /// /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c /// int nk_propertyi(struct nk_context *ctx, const char *name, int min, int val, int max, int step, float inc_per_pixel); /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /// /// Parameter | Description /// --------------------|----------------------------------------------------------- /// __ctx__ | Must point to an previously initialized `nk_context` struct after calling a layouting function /// __name__ | String used both as a label as well as a unique identifier /// __min__ | Minimum value not allowed to be underflown /// __val__ | Current integer value to be modified and returned /// __max__ | Maximum value not allowed to be overflown /// __step__ | Increment added and subtracted on increment and decrement button /// __inc_per_pixel__ | Value per pixel added or subtracted on dragging /// /// Returns the new modified integer value */ NK_API int nk_propertyi(struct nk_context*, const char *name, int min, int val, int max, int step, float inc_per_pixel); /*/// #### nk_propertyf /// Float property modifying a passed in value and returning the new value /// !!! WARNING /// To generate a unique property ID using the same label make sure to insert /// a `#` at the beginning. It will not be shown but guarantees correct behavior. /// /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c /// float nk_propertyf(struct nk_context *ctx, const char *name, float min, float val, float max, float step, float inc_per_pixel); /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /// /// Parameter | Description /// --------------------|----------------------------------------------------------- /// __ctx__ | Must point to an previously initialized `nk_context` struct after calling a layouting function /// __name__ | String used both as a label as well as a unique identifier /// __min__ | Minimum value not allowed to be underflown /// __val__ | Current float value to be modified and returned /// __max__ | Maximum value not allowed to be overflown /// __step__ | Increment added and subtracted on increment and decrement button /// __inc_per_pixel__ | Value per pixel added or subtracted on dragging /// /// Returns the new modified float value */ NK_API float nk_propertyf(struct nk_context*, const char *name, float min, float val, float max, float step, float inc_per_pixel); /*/// #### nk_propertyd /// Float property modifying a passed in value and returning the new value /// !!! WARNING /// To generate a unique property ID using the same label make sure to insert /// a `#` at the beginning. It will not be shown but guarantees correct behavior. /// /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~c /// float nk_propertyd(struct nk_context *ctx, const char *name, double min, double val, double max, double step, double inc_per_pixel); /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /// /// Parameter | Description /// --------------------|----------------------------------------------------------- /// __ctx__ | Must point to an previously initialized `nk_context` struct after calling a layouting function /// __name__ | String used both as a label as well as a unique identifier /// __min__ | Minimum value not allowed to be underflown /// __val__ | Current double value to be modified and returned /// __max__ | Maximum value not allowed to be overflown /// __step__ | Increment added and subtracted on increment and decrement button /// __inc_per_pixel__ | Value per pixel added or subtracted on dragging /// /// Returns the new modified double value */ NK_API double nk_propertyd(struct nk_context*, const char *name, double min, double val, double max, double step, float inc_per_pixel); /* ============================================================================= * * TEXT EDIT * * ============================================================================= */ enum nk_edit_flags { NK_EDIT_DEFAULT = 0, NK_EDIT_READ_ONLY = NK_FLAG(0), NK_EDIT_AUTO_SELECT = NK_FLAG(1), NK_EDIT_SIG_ENTER = NK_FLAG(2), NK_EDIT_ALLOW_TAB = NK_FLAG(3), NK_EDIT_NO_CURSOR = NK_FLAG(4), NK_EDIT_SELECTABLE = NK_FLAG(5), NK_EDIT_CLIPBOARD = NK_FLAG(6), NK_EDIT_CTRL_ENTER_NEWLINE = NK_FLAG(7), NK_EDIT_NO_HORIZONTAL_SCROLL = NK_FLAG(8), NK_EDIT_ALWAYS_INSERT_MODE = NK_FLAG(9), NK_EDIT_MULTILINE = NK_FLAG(10), NK_EDIT_GOTO_END_ON_ACTIVATE = NK_FLAG(11) }; enum nk_edit_types { NK_EDIT_SIMPLE = NK_EDIT_ALWAYS_INSERT_MODE, NK_EDIT_FIELD = NK_EDIT_SIMPLE|NK_EDIT_SELECTABLE|NK_EDIT_CLIPBOARD, NK_EDIT_BOX = NK_EDIT_ALWAYS_INSERT_MODE| NK_EDIT_SELECTABLE| NK_EDIT_MULTILINE|NK_EDIT_ALLOW_TAB|NK_EDIT_CLIPBOARD, NK_EDIT_EDITOR = NK_EDIT_SELECTABLE|NK_EDIT_MULTILINE|NK_EDIT_ALLOW_TAB| NK_EDIT_CLIPBOARD }; enum nk_edit_events { NK_EDIT_ACTIVE = NK_FLAG(0), /* edit widget is currently being modified */ NK_EDIT_INACTIVE = NK_FLAG(1), /* edit widget is not active and is not being modified */ NK_EDIT_ACTIVATED = NK_FLAG(2), /* edit widget went from state inactive to state active */ NK_EDIT_DEACTIVATED = NK_FLAG(3), /* edit widget went from state active to state inactive */ NK_EDIT_COMMITED = NK_FLAG(4) /* edit widget has received an enter and lost focus */ }; NK_API nk_flags nk_edit_string(struct nk_context*, nk_flags, char *buffer, int *len, int max, nk_plugin_filter); NK_API nk_flags nk_edit_string_zero_terminated(struct nk_context*, nk_flags, char *buffer, int max, nk_plugin_filter); NK_API nk_flags nk_edit_buffer(struct nk_context*, nk_flags, struct nk_text_edit*, nk_plugin_filter); NK_API void nk_edit_focus(struct nk_context*, nk_flags flags); NK_API void nk_edit_unfocus(struct nk_context*); /* ============================================================================= * * CHART * * ============================================================================= */ NK_API nk_bool nk_chart_begin(struct nk_context*, enum nk_chart_type, int num, float min, float max); NK_API nk_bool nk_chart_begin_colored(struct nk_context*, enum nk_chart_type, struct nk_color, struct nk_color active, int num, float min, float max); NK_API void nk_chart_add_slot(struct nk_context *ctx, const enum nk_chart_type, int count, float min_value, float max_value); NK_API void nk_chart_add_slot_colored(struct nk_context *ctx, const enum nk_chart_type, struct nk_color, struct nk_color active, int count, float min_value, float max_value); NK_API nk_flags nk_chart_push(struct nk_context*, float); NK_API nk_flags nk_chart_push_slot(struct nk_context*, float, int); NK_API void nk_chart_end(struct nk_context*); NK_API void nk_plot(struct nk_context*, enum nk_chart_type, const float *values, int count, int offset); NK_API void nk_plot_function(struct nk_context*, enum nk_chart_type, void *userdata, float(*value_getter)(void* user, int index), int count, int offset); /* ============================================================================= * * POPUP * * ============================================================================= */ NK_API nk_bool nk_popup_begin(struct nk_context*, enum nk_popup_type, const char*, nk_flags, struct nk_rect bounds); NK_API void nk_popup_close(struct nk_context*); NK_API void nk_popup_end(struct nk_context*); NK_API void nk_popup_get_scroll(struct nk_context*, nk_uint *offset_x, nk_uint *offset_y); NK_API void nk_popup_set_scroll(struct nk_context*, nk_uint offset_x, nk_uint offset_y); /* ============================================================================= * * COMBOBOX * * ============================================================================= */ NK_API int nk_combo(struct nk_context*, const char **items, int count, int selected, int item_height, struct nk_vec2 size); NK_API int nk_combo_separator(struct nk_context*, const char *items_separated_by_separator, int separator, int selected, int count, int item_height, struct nk_vec2 size); NK_API int nk_combo_string(struct nk_context*, const char *items_separated_by_zeros, int selected, int count, int item_height, struct nk_vec2 size); NK_API int nk_combo_callback(struct nk_context*, void(*item_getter)(void*, int, const char**), void *userdata, int selected, int count, int item_height, struct nk_vec2 size); NK_API void nk_combobox(struct nk_context*, const char **items, int count, int *selected, int item_height, struct nk_vec2 size); NK_API void nk_combobox_string(struct nk_context*, const char *items_separated_by_zeros, int *selected, int count, int item_height, struct nk_vec2 size); NK_API void nk_combobox_separator(struct nk_context*, const char *items_separated_by_separator, int separator, int *selected, int count, int item_height, struct nk_vec2 size); NK_API void nk_combobox_callback(struct nk_context*, void(*item_getter)(void*, int, const char**), void*, int *selected, int count, int item_height, struct nk_vec2 size); /* ============================================================================= * * ABSTRACT COMBOBOX * * ============================================================================= */ NK_API nk_bool nk_combo_begin_text(struct nk_context*, const char *selected, int, struct nk_vec2 size); NK_API nk_bool nk_combo_begin_label(struct nk_context*, const char *selected, struct nk_vec2 size); NK_API nk_bool nk_combo_begin_color(struct nk_context*, struct nk_color color, struct nk_vec2 size); NK_API nk_bool nk_combo_begin_symbol(struct nk_context*, enum nk_symbol_type, struct nk_vec2 size); NK_API nk_bool nk_combo_begin_symbol_label(struct nk_context*, const char *selected, enum nk_symbol_type, struct nk_vec2 size); NK_API nk_bool nk_combo_begin_symbol_text(struct nk_context*, const char *selected, int, enum nk_symbol_type, struct nk_vec2 size); NK_API nk_bool nk_combo_begin_image(struct nk_context*, struct nk_image img, struct nk_vec2 size); NK_API nk_bool nk_combo_begin_image_label(struct nk_context*, const char *selected, struct nk_image, struct nk_vec2 size); NK_API nk_bool nk_combo_begin_image_text(struct nk_context*, const char *selected, int, struct nk_image, struct nk_vec2 size); NK_API nk_bool nk_combo_item_label(struct nk_context*, const char*, nk_flags alignment); NK_API nk_bool nk_combo_item_text(struct nk_context*, const char*,int, nk_flags alignment); NK_API nk_bool nk_combo_item_image_label(struct nk_context*, struct nk_image, const char*, nk_flags alignment); NK_API nk_bool nk_combo_item_image_text(struct nk_context*, struct nk_image, const char*, int,nk_flags alignment); NK_API nk_bool nk_combo_item_symbol_label(struct nk_context*, enum nk_symbol_type, const char*, nk_flags alignment); NK_API nk_bool nk_combo_item_symbol_text(struct nk_context*, enum nk_symbol_type, const char*, int, nk_flags alignment); NK_API void nk_combo_close(struct nk_context*); NK_API void nk_combo_end(struct nk_context*); /* ============================================================================= * * CONTEXTUAL * * ============================================================================= */ NK_API nk_bool nk_contextual_begin(struct nk_context*, nk_flags, struct nk_vec2, struct nk_rect trigger_bounds); NK_API nk_bool nk_contextual_item_text(struct nk_context*, const char*, int,nk_flags align); NK_API nk_bool nk_contextual_item_label(struct nk_context*, const char*, nk_flags align); NK_API nk_bool nk_contextual_item_image_label(struct nk_context*, struct nk_image, const char*, nk_flags alignment); NK_API nk_bool nk_contextual_item_image_text(struct nk_context*, struct nk_image, const char*, int len, nk_flags alignment); NK_API nk_bool nk_contextual_item_symbol_label(struct nk_context*, enum nk_symbol_type, const char*, nk_flags alignment); NK_API nk_bool nk_contextual_item_symbol_text(struct nk_context*, enum nk_symbol_type, const char*, int, nk_flags alignment); NK_API void nk_contextual_close(struct nk_context*); NK_API void nk_contextual_end(struct nk_context*); /* ============================================================================= * * TOOLTIP * * ============================================================================= */ NK_API void nk_tooltip(struct nk_context*, const char*); #ifdef NK_INCLUDE_STANDARD_VARARGS NK_API void nk_tooltipf(struct nk_context*, NK_PRINTF_FORMAT_STRING const char*, ...) NK_PRINTF_VARARG_FUNC(2); NK_API void nk_tooltipfv(struct nk_context*, NK_PRINTF_FORMAT_STRING const char*, va_list) NK_PRINTF_VALIST_FUNC(2); #endif NK_API nk_bool nk_tooltip_begin(struct nk_context*, float width); NK_API void nk_tooltip_end(struct nk_context*); /* ============================================================================= * * MENU * * ============================================================================= */ NK_API void nk_menubar_begin(struct nk_context*); NK_API void nk_menubar_end(struct nk_context*); NK_API nk_bool nk_menu_begin_text(struct nk_context*, const char* title, int title_len, nk_flags align, struct nk_vec2 size); NK_API nk_bool nk_menu_begin_label(struct nk_context*, const char*, nk_flags align, struct nk_vec2 size); NK_API nk_bool nk_menu_begin_image(struct nk_context*, const char*, struct nk_image, struct nk_vec2 size); NK_API nk_bool nk_menu_begin_image_text(struct nk_context*, const char*, int,nk_flags align,struct nk_image, struct nk_vec2 size); NK_API nk_bool nk_menu_begin_image_label(struct nk_context*, const char*, nk_flags align,struct nk_image, struct nk_vec2 size); NK_API nk_bool nk_menu_begin_symbol(struct nk_context*, const char*, enum nk_symbol_type, struct nk_vec2 size); NK_API nk_bool nk_menu_begin_symbol_text(struct nk_context*, const char*, int,nk_flags align,enum nk_symbol_type, struct nk_vec2 size); NK_API nk_bool nk_menu_begin_symbol_label(struct nk_context*, const char*, nk_flags align,enum nk_symbol_type, struct nk_vec2 size); NK_API nk_bool nk_menu_item_text(struct nk_context*, const char*, int,nk_flags align); NK_API nk_bool nk_menu_item_label(struct nk_context*, const char*, nk_flags alignment); NK_API nk_bool nk_menu_item_image_label(struct nk_context*, struct nk_image, const char*, nk_flags alignment); NK_API nk_bool nk_menu_item_image_text(struct nk_context*, struct nk_image, const char*, int len, nk_flags alignment); NK_API nk_bool nk_menu_item_symbol_text(struct nk_context*, enum nk_symbol_type, const char*, int, nk_flags alignment); NK_API nk_bool nk_menu_item_symbol_label(struct nk_context*, enum nk_symbol_type, const char*, nk_flags alignment); NK_API void nk_menu_close(struct nk_context*); NK_API void nk_menu_end(struct nk_context*); /* ============================================================================= * * STYLE * * ============================================================================= */ enum nk_style_colors { NK_COLOR_TEXT, NK_COLOR_WINDOW, NK_COLOR_HEADER, NK_COLOR_BORDER, NK_COLOR_BUTTON, NK_COLOR_BUTTON_HOVER, NK_COLOR_BUTTON_ACTIVE, NK_COLOR_TOGGLE, NK_COLOR_TOGGLE_HOVER, NK_COLOR_TOGGLE_CURSOR, NK_COLOR_SELECT, NK_COLOR_SELECT_ACTIVE, NK_COLOR_SLIDER, NK_COLOR_SLIDER_CURSOR, NK_COLOR_SLIDER_CURSOR_HOVER, NK_COLOR_SLIDER_CURSOR_ACTIVE, NK_COLOR_PROPERTY, NK_COLOR_EDIT, NK_COLOR_EDIT_CURSOR, NK_COLOR_COMBO, NK_COLOR_CHART, NK_COLOR_CHART_COLOR, NK_COLOR_CHART_COLOR_HIGHLIGHT, NK_COLOR_SCROLLBAR, NK_COLOR_SCROLLBAR_CURSOR, NK_COLOR_SCROLLBAR_CURSOR_HOVER, NK_COLOR_SCROLLBAR_CURSOR_ACTIVE, NK_COLOR_TAB_HEADER, NK_COLOR_COUNT }; enum nk_style_cursor { NK_CURSOR_ARROW, NK_CURSOR_TEXT, NK_CURSOR_MOVE, NK_CURSOR_RESIZE_VERTICAL, NK_CURSOR_RESIZE_HORIZONTAL, NK_CURSOR_RESIZE_TOP_LEFT_DOWN_RIGHT, NK_CURSOR_RESIZE_TOP_RIGHT_DOWN_LEFT, NK_CURSOR_COUNT }; NK_API void nk_style_default(struct nk_context*); NK_API void nk_style_from_table(struct nk_context*, const struct nk_color*); NK_API void nk_style_load_cursor(struct nk_context*, enum nk_style_cursor, const struct nk_cursor*); NK_API void nk_style_load_all_cursors(struct nk_context*, struct nk_cursor*); NK_API const char* nk_style_get_color_by_name(enum nk_style_colors); NK_API void nk_style_set_font(struct nk_context*, const struct nk_user_font*); NK_API nk_bool nk_style_set_cursor(struct nk_context*, enum nk_style_cursor); NK_API void nk_style_show_cursor(struct nk_context*); NK_API void nk_style_hide_cursor(struct nk_context*); NK_API nk_bool nk_style_push_font(struct nk_context*, const struct nk_user_font*); NK_API nk_bool nk_style_push_float(struct nk_context*, float*, float); NK_API nk_bool nk_style_push_vec2(struct nk_context*, struct nk_vec2*, struct nk_vec2); NK_API nk_bool nk_style_push_style_item(struct nk_context*, struct nk_style_item*, struct nk_style_item); NK_API nk_bool nk_style_push_flags(struct nk_context*, nk_flags*, nk_flags); NK_API nk_bool nk_style_push_color(struct nk_context*, struct nk_color*, struct nk_color); NK_API nk_bool nk_style_pop_font(struct nk_context*); NK_API nk_bool nk_style_pop_float(struct nk_context*); NK_API nk_bool nk_style_pop_vec2(struct nk_context*); NK_API nk_bool nk_style_pop_style_item(struct nk_context*); NK_API nk_bool nk_style_pop_flags(struct nk_context*); NK_API nk_bool nk_style_pop_color(struct nk_context*); /* ============================================================================= * * COLOR * * ============================================================================= */ NK_API struct nk_color nk_rgb(int r, int g, int b); NK_API struct nk_color nk_rgb_iv(const int *rgb); NK_API struct nk_color nk_rgb_bv(const nk_byte* rgb); NK_API struct nk_color nk_rgb_f(float r, float g, float b); NK_API struct nk_color nk_rgb_fv(const float *rgb); NK_API struct nk_color nk_rgb_cf(struct nk_colorf c); NK_API struct nk_color nk_rgb_hex(const char *rgb); NK_API struct nk_color nk_rgba(int r, int g, int b, int a); NK_API struct nk_color nk_rgba_u32(nk_uint); NK_API struct nk_color nk_rgba_iv(const int *rgba); NK_API struct nk_color nk_rgba_bv(const nk_byte *rgba); NK_API struct nk_color nk_rgba_f(float r, float g, float b, float a); NK_API struct nk_color nk_rgba_fv(const float *rgba); NK_API struct nk_color nk_rgba_cf(struct nk_colorf c); NK_API struct nk_color nk_rgba_hex(const char *rgb); NK_API struct nk_colorf nk_hsva_colorf(float h, float s, float v, float a); NK_API struct nk_colorf nk_hsva_colorfv(float *c); NK_API void nk_colorf_hsva_f(float *out_h, float *out_s, float *out_v, float *out_a, struct nk_colorf in); NK_API void nk_colorf_hsva_fv(float *hsva, struct nk_colorf in); NK_API struct nk_color nk_hsv(int h, int s, int v); NK_API struct nk_color nk_hsv_iv(const int *hsv); NK_API struct nk_color nk_hsv_bv(const nk_byte *hsv); NK_API struct nk_color nk_hsv_f(float h, float s, float v); NK_API struct nk_color nk_hsv_fv(const float *hsv); NK_API struct nk_color nk_hsva(int h, int s, int v, int a); NK_API struct nk_color nk_hsva_iv(const int *hsva); NK_API struct nk_color nk_hsva_bv(const nk_byte *hsva); NK_API struct nk_color nk_hsva_f(float h, float s, float v, float a); NK_API struct nk_color nk_hsva_fv(const float *hsva); /* color (conversion nuklear --> user) */ NK_API void nk_color_f(float *r, float *g, float *b, float *a, struct nk_color); NK_API void nk_color_fv(float *rgba_out, struct nk_color); NK_API struct nk_colorf nk_color_cf(struct nk_color); NK_API void nk_color_d(double *r, double *g, double *b, double *a, struct nk_color); NK_API void nk_color_dv(double *rgba_out, struct nk_color); NK_API nk_uint nk_color_u32(struct nk_color); NK_API void nk_color_hex_rgba(char *output, struct nk_color); NK_API void nk_color_hex_rgb(char *output, struct nk_color); NK_API void nk_color_hsv_i(int *out_h, int *out_s, int *out_v, struct nk_color); NK_API void nk_color_hsv_b(nk_byte *out_h, nk_byte *out_s, nk_byte *out_v, struct nk_color); NK_API void nk_color_hsv_iv(int *hsv_out, struct nk_color); NK_API void nk_color_hsv_bv(nk_byte *hsv_out, struct nk_color); NK_API void nk_color_hsv_f(float *out_h, float *out_s, float *out_v, struct nk_color); NK_API void nk_color_hsv_fv(float *hsv_out, struct nk_color); NK_API void nk_color_hsva_i(int *h, int *s, int *v, int *a, struct nk_color); NK_API void nk_color_hsva_b(nk_byte *h, nk_byte *s, nk_byte *v, nk_byte *a, struct nk_color); NK_API void nk_color_hsva_iv(int *hsva_out, struct nk_color); NK_API void nk_color_hsva_bv(nk_byte *hsva_out, struct nk_color); NK_API void nk_color_hsva_f(float *out_h, float *out_s, float *out_v, float *out_a, struct nk_color); NK_API void nk_color_hsva_fv(float *hsva_out, struct nk_color); /* ============================================================================= * * IMAGE * * ============================================================================= */ NK_API nk_handle nk_handle_ptr(void*); NK_API nk_handle nk_handle_id(int); NK_API struct nk_image nk_image_handle(nk_handle); NK_API struct nk_image nk_image_ptr(void*); NK_API struct nk_image nk_image_id(int); NK_API nk_bool nk_image_is_subimage(const struct nk_image* img); NK_API struct nk_image nk_subimage_ptr(void*, nk_ushort w, nk_ushort h, struct nk_rect sub_region); NK_API struct nk_image nk_subimage_id(int, nk_ushort w, nk_ushort h, struct nk_rect sub_region); NK_API struct nk_image nk_subimage_handle(nk_handle, nk_ushort w, nk_ushort h, struct nk_rect sub_region); /* ============================================================================= * * 9-SLICE * * ============================================================================= */ NK_API struct nk_nine_slice nk_nine_slice_handle(nk_handle, nk_ushort l, nk_ushort t, nk_ushort r, nk_ushort b); NK_API struct nk_nine_slice nk_nine_slice_ptr(void*, nk_ushort l, nk_ushort t, nk_ushort r, nk_ushort b); NK_API struct nk_nine_slice nk_nine_slice_id(int, nk_ushort l, nk_ushort t, nk_ushort r, nk_ushort b); NK_API int nk_nine_slice_is_sub9slice(const struct nk_nine_slice* img); NK_API struct nk_nine_slice nk_sub9slice_ptr(void*, nk_ushort w, nk_ushort h, struct nk_rect sub_region, nk_ushort l, nk_ushort t, nk_ushort r, nk_ushort b); NK_API struct nk_nine_slice nk_sub9slice_id(int, nk_ushort w, nk_ushort h, struct nk_rect sub_region, nk_ushort l, nk_ushort t, nk_ushort r, nk_ushort b); NK_API struct nk_nine_slice nk_sub9slice_handle(nk_handle, nk_ushort w, nk_ushort h, struct nk_rect sub_region, nk_ushort l, nk_ushort t, nk_ushort r, nk_ushort b); /* ============================================================================= * * MATH * * ============================================================================= */ NK_API nk_hash nk_murmur_hash(const void *key, int len, nk_hash seed); NK_API void nk_triangle_from_direction(struct nk_vec2 *result, struct nk_rect r, float pad_x, float pad_y, enum nk_heading); NK_API struct nk_vec2 nk_vec2(float x, float y); NK_API struct nk_vec2 nk_vec2i(int x, int y); NK_API struct nk_vec2 nk_vec2v(const float *xy); NK_API struct nk_vec2 nk_vec2iv(const int *xy); NK_API struct nk_rect nk_get_null_rect(void); NK_API struct nk_rect nk_rect(float x, float y, float w, float h); NK_API struct nk_rect nk_recti(int x, int y, int w, int h); NK_API struct nk_rect nk_recta(struct nk_vec2 pos, struct nk_vec2 size); NK_API struct nk_rect nk_rectv(const float *xywh); NK_API struct nk_rect nk_rectiv(const int *xywh); NK_API struct nk_vec2 nk_rect_pos(struct nk_rect); NK_API struct nk_vec2 nk_rect_size(struct nk_rect); /* ============================================================================= * * STRING * * ============================================================================= */ NK_API int nk_strlen(const char *str); NK_API int nk_stricmp(const char *s1, const char *s2); NK_API int nk_stricmpn(const char *s1, const char *s2, int n); NK_API int nk_strtoi(const char *str, const char **endptr); NK_API float nk_strtof(const char *str, const char **endptr); #ifndef NK_STRTOD #define NK_STRTOD nk_strtod NK_API double nk_strtod(const char *str, const char **endptr); #endif NK_API int nk_strfilter(const char *text, const char *regexp); NK_API int nk_strmatch_fuzzy_string(char const *str, char const *pattern, int *out_score); NK_API int nk_strmatch_fuzzy_text(const char *txt, int txt_len, const char *pattern, int *out_score); /* ============================================================================= * * UTF-8 * * ============================================================================= */ NK_API int nk_utf_decode(const char*, nk_rune*, int); NK_API int nk_utf_encode(nk_rune, char*, int); NK_API int nk_utf_len(const char*, int byte_len); NK_API const char* nk_utf_at(const char *buffer, int length, int index, nk_rune *unicode, int *len); /* =============================================================== * * FONT * * ===============================================================*/ /* Font handling in this library was designed to be quite customizable and lets you decide what you want to use and what you want to provide. There are three different ways to use the font atlas. The first two will use your font handling scheme and only requires essential data to run nuklear. The next slightly more advanced features is font handling with vertex buffer output. Finally the most complex API wise is using nuklear's font baking API. 1.) Using your own implementation without vertex buffer output -------------------------------------------------------------- So first up the easiest way to do font handling is by just providing a `nk_user_font` struct which only requires the height in pixel of the used font and a callback to calculate the width of a string. This way of handling fonts is best fitted for using the normal draw shape command API where you do all the text drawing yourself and the library does not require any kind of deeper knowledge about which font handling mechanism you use. IMPORTANT: the `nk_user_font` pointer provided to nuklear has to persist over the complete life time! I know this sucks but it is currently the only way to switch between fonts. float your_text_width_calculation(nk_handle handle, float height, const char *text, int len) { your_font_type *type = handle.ptr; float text_width = ...; return text_width; } struct nk_user_font font; font.userdata.ptr = &your_font_class_or_struct; font.height = your_font_height; font.width = your_text_width_calculation; struct nk_context ctx; nk_init_default(&ctx, &font); 2.) Using your own implementation with vertex buffer output -------------------------------------------------------------- While the first approach works fine if you don't want to use the optional vertex buffer output it is not enough if you do. To get font handling working for these cases you have to provide two additional parameters inside the `nk_user_font`. First a texture atlas handle used to draw text as subimages of a bigger font atlas texture and a callback to query a character's glyph information (offset, size, ...). So it is still possible to provide your own font and use the vertex buffer output. float your_text_width_calculation(nk_handle handle, float height, const char *text, int len) { your_font_type *type = handle.ptr; float text_width = ...; return text_width; } void query_your_font_glyph(nk_handle handle, float font_height, struct nk_user_font_glyph *glyph, nk_rune codepoint, nk_rune next_codepoint) { your_font_type *type = handle.ptr; glyph.width = ...; glyph.height = ...; glyph.xadvance = ...; glyph.uv[0].x = ...; glyph.uv[0].y = ...; glyph.uv[1].x = ...; glyph.uv[1].y = ...; glyph.offset.x = ...; glyph.offset.y = ...; } struct nk_user_font font; font.userdata.ptr = &your_font_class_or_struct; font.height = your_font_height; font.width = your_text_width_calculation; font.query = query_your_font_glyph; font.texture.id = your_font_texture; struct nk_context ctx; nk_init_default(&ctx, &font); 3.) Nuklear font baker ------------------------------------ The final approach if you do not have a font handling functionality or don't want to use it in this library is by using the optional font baker. The font baker APIs can be used to create a font plus font atlas texture and can be used with or without the vertex buffer output. It still uses the `nk_user_font` struct and the two different approaches previously stated still work. The font baker is not located inside `nk_context` like all other systems since it can be understood as more of an extension to nuklear and does not really depend on any `nk_context` state. Font baker need to be initialized first by one of the nk_font_atlas_init_xxx functions. If you don't care about memory just call the default version `nk_font_atlas_init_default` which will allocate all memory from the standard library. If you want to control memory allocation but you don't care if the allocated memory is temporary and therefore can be freed directly after the baking process is over or permanent you can call `nk_font_atlas_init`. After successfully initializing the font baker you can add Truetype(.ttf) fonts from different sources like memory or from file by calling one of the `nk_font_atlas_add_xxx`. functions. Adding font will permanently store each font, font config and ttf memory block(!) inside the font atlas and allows to reuse the font atlas. If you don't want to reuse the font baker by for example adding additional fonts you can call `nk_font_atlas_cleanup` after the baking process is over (after calling nk_font_atlas_end). As soon as you added all fonts you wanted you can now start the baking process for every selected glyph to image by calling `nk_font_atlas_bake`. The baking process returns image memory, width and height which can be used to either create your own image object or upload it to any graphics library. No matter which case you finally have to call `nk_font_atlas_end` which will free all temporary memory including the font atlas image so make sure you created our texture beforehand. `nk_font_atlas_end` requires a handle to your font texture or object and optionally fills a `struct nk_draw_null_texture` which can be used for the optional vertex output. If you don't want it just set the argument to `NULL`. At this point you are done and if you don't want to reuse the font atlas you can call `nk_font_atlas_cleanup` to free all truetype blobs and configuration memory. Finally if you don't use the font atlas and any of it's fonts anymore you need to call `nk_font_atlas_clear` to free all memory still being used. struct nk_font_atlas atlas; nk_font_atlas_init_default(&atlas); nk_font_atlas_begin(&atlas); nk_font *font = nk_font_atlas_add_from_file(&atlas, "Path/To/Your/TTF_Font.ttf", 13, 0); nk_font *font2 = nk_font_atlas_add_from_file(&atlas, "Path/To/Your/TTF_Font2.ttf", 16, 0); const void* img = nk_font_atlas_bake(&atlas, &img_width, &img_height, NK_FONT_ATLAS_RGBA32); nk_font_atlas_end(&atlas, nk_handle_id(texture), 0); struct nk_context ctx; nk_init_default(&ctx, &font->handle); while (1) { } nk_font_atlas_clear(&atlas); The font baker API is probably the most complex API inside this library and I would suggest reading some of my examples `example/` to get a grip on how to use the font atlas. There are a number of details I left out. For example how to merge fonts, configure a font with `nk_font_config` to use other languages, use another texture coordinate format and a lot more: struct nk_font_config cfg = nk_font_config(font_pixel_height); cfg.merge_mode = nk_false or nk_true; cfg.range = nk_font_korean_glyph_ranges(); cfg.coord_type = NK_COORD_PIXEL; nk_font *font = nk_font_atlas_add_from_file(&atlas, "Path/To/Your/TTF_Font.ttf", 13, &cfg); */ struct nk_user_font_glyph; typedef float(*nk_text_width_f)(nk_handle, float h, const char*, int len); typedef void(*nk_query_font_glyph_f)(nk_handle handle, float font_height, struct nk_user_font_glyph *glyph, nk_rune codepoint, nk_rune next_codepoint); #if defined(NK_INCLUDE_VERTEX_BUFFER_OUTPUT) || defined(NK_INCLUDE_SOFTWARE_FONT) struct nk_user_font_glyph { struct nk_vec2 uv[2]; /* texture coordinates */ struct nk_vec2 offset; /* offset between top left and glyph */ float width, height; /* size of the glyph */ float xadvance; /* offset to the next glyph */ }; #endif struct nk_user_font { nk_handle userdata; /* user provided font handle */ float height; /* max height of the font */ nk_text_width_f width; /* font string width in pixel callback */ #ifdef NK_INCLUDE_VERTEX_BUFFER_OUTPUT nk_query_font_glyph_f query; /* font glyph callback to query drawing info */ nk_handle texture; /* texture handle to the used font atlas or texture */ #endif }; #ifdef NK_INCLUDE_FONT_BAKING enum nk_font_coord_type { NK_COORD_UV, /* texture coordinates inside font glyphs are clamped between 0-1 */ NK_COORD_PIXEL /* texture coordinates inside font glyphs are in absolute pixel */ }; struct nk_font; struct nk_baked_font { float height; /* height of the font */ float ascent, descent; /* font glyphs ascent and descent */ nk_rune glyph_offset; /* glyph array offset inside the font glyph baking output array */ nk_rune glyph_count; /* number of glyphs of this font inside the glyph baking array output */ const nk_rune *ranges; /* font codepoint ranges as pairs of (from/to) and 0 as last element */ }; struct nk_font_config { struct nk_font_config *next; /* NOTE: only used internally */ void *ttf_blob; /* pointer to loaded TTF file memory block. * NOTE: not needed for nk_font_atlas_add_from_memory and nk_font_atlas_add_from_file. */ nk_size ttf_size; /* size of the loaded TTF file memory block * NOTE: not needed for nk_font_atlas_add_from_memory and nk_font_atlas_add_from_file. */ unsigned char ttf_data_owned_by_atlas; /* used inside font atlas: default to: 0*/ unsigned char merge_mode; /* merges this font into the last font */ unsigned char pixel_snap; /* align every character to pixel boundary (if true set oversample (1,1)) */ unsigned char oversample_v, oversample_h; /* rasterize at high quality for sub-pixel position */ unsigned char padding[3]; float size; /* baked pixel height of the font */ enum nk_font_coord_type coord_type; /* texture coordinate format with either pixel or UV coordinates */ struct nk_vec2 spacing; /* extra pixel spacing between glyphs */ const nk_rune *range; /* list of unicode ranges (2 values per range, zero terminated) */ struct nk_baked_font *font; /* font to setup in the baking process: NOTE: not needed for font atlas */ nk_rune fallback_glyph; /* fallback glyph to use if a given rune is not found */ struct nk_font_config *n; struct nk_font_config *p; }; struct nk_font_glyph { nk_rune codepoint; float xadvance; float x0, y0, x1, y1, w, h; float u0, v0, u1, v1; float yoffset; //< @r-lyeh }; struct nk_font { struct nk_font *next; struct nk_user_font handle; struct nk_baked_font info; float scale; struct nk_font_glyph *glyphs; const struct nk_font_glyph *fallback; nk_rune fallback_codepoint; nk_handle texture; struct nk_font_config *config; }; enum nk_font_atlas_format { NK_FONT_ATLAS_ALPHA8, NK_FONT_ATLAS_RGBA32 }; struct nk_font_atlas { void *pixel; int tex_width; int tex_height; struct nk_allocator permanent; struct nk_allocator temporary; struct nk_recti custom; struct nk_cursor cursors[NK_CURSOR_COUNT]; int glyph_count; struct nk_font_glyph *glyphs; struct nk_font *default_font; struct nk_font *fonts; struct nk_font_config *config; int font_num; }; /* some language glyph codepoint ranges */ NK_API const nk_rune *nk_font_default_glyph_ranges(void); NK_API const nk_rune *nk_font_chinese_glyph_ranges(void); NK_API const nk_rune *nk_font_cyrillic_glyph_ranges(void); NK_API const nk_rune *nk_font_korean_glyph_ranges(void); #ifdef NK_INCLUDE_DEFAULT_ALLOCATOR NK_API void nk_font_atlas_init_default(struct nk_font_atlas*); #endif NK_API void nk_font_atlas_init(struct nk_font_atlas*, struct nk_allocator*); NK_API void nk_font_atlas_init_custom(struct nk_font_atlas*, struct nk_allocator *persistent, struct nk_allocator *transient); NK_API void nk_font_atlas_begin(struct nk_font_atlas*); NK_API struct nk_font_config nk_font_config(float pixel_height); NK_API struct nk_font *nk_font_atlas_add(struct nk_font_atlas*, const struct nk_font_config*); #ifdef NK_INCLUDE_DEFAULT_FONT NK_API struct nk_font* nk_font_atlas_add_default(struct nk_font_atlas*, float height, const struct nk_font_config*); #endif NK_API struct nk_font* nk_font_atlas_add_from_memory(struct nk_font_atlas *atlas, void *memory, nk_size size, float height, const struct nk_font_config *config); #ifdef NK_INCLUDE_STANDARD_IO NK_API struct nk_font* nk_font_atlas_add_from_file(struct nk_font_atlas *atlas, const char *file_path, float height, const struct nk_font_config*); #endif NK_API struct nk_font *nk_font_atlas_add_compressed(struct nk_font_atlas*, void *memory, nk_size size, float height, const struct nk_font_config*); NK_API struct nk_font* nk_font_atlas_add_compressed_base85(struct nk_font_atlas*, const char *data, float height, const struct nk_font_config *config); NK_API const void* nk_font_atlas_bake(struct nk_font_atlas*, int *width, int *height, enum nk_font_atlas_format); NK_API void nk_font_atlas_end(struct nk_font_atlas*, nk_handle tex, struct nk_draw_null_texture*); NK_API const struct nk_font_glyph* nk_font_find_glyph(struct nk_font*, nk_rune unicode); NK_API void nk_font_atlas_cleanup(struct nk_font_atlas *atlas); NK_API void nk_font_atlas_clear(struct nk_font_atlas*); #endif /* ============================================================== * * MEMORY BUFFER * * ===============================================================*/ /* A basic (double)-buffer with linear allocation and resetting as only freeing policy. The buffer's main purpose is to control all memory management inside the GUI toolkit and still leave memory control as much as possible in the hand of the user while also making sure the library is easy to use if not as much control is needed. In general all memory inside this library can be provided from the user in three different ways. The first way and the one providing most control is by just passing a fixed size memory block. In this case all control lies in the hand of the user since he can exactly control where the memory comes from and how much memory the library should consume. Of course using the fixed size API removes the ability to automatically resize a buffer if not enough memory is provided so you have to take over the resizing. While being a fixed sized buffer sounds quite limiting, it is very effective in this library since the actual memory consumption is quite stable and has a fixed upper bound for a lot of cases. If you don't want to think about how much memory the library should allocate at all time or have a very dynamic UI with unpredictable memory consumption habits but still want control over memory allocation you can use the dynamic allocator based API. The allocator consists of two callbacks for allocating and freeing memory and optional userdata so you can plugin your own allocator. The final and easiest way can be used by defining NK_INCLUDE_DEFAULT_ALLOCATOR which uses the standard library memory allocation functions malloc and free and takes over complete control over memory in this library. */ struct nk_memory_status { void *memory; unsigned int type; nk_size size; nk_size allocated; nk_size needed; nk_size calls; }; enum nk_allocation_type { NK_BUFFER_FIXED, NK_BUFFER_DYNAMIC }; enum nk_buffer_allocation_type { NK_BUFFER_FRONT, NK_BUFFER_BACK, NK_BUFFER_MAX }; struct nk_buffer_marker { nk_bool active; nk_size offset; }; struct nk_memory {void *ptr;nk_size size;}; struct nk_buffer { struct nk_buffer_marker marker[NK_BUFFER_MAX]; /* buffer marker to free a buffer to a certain offset */ struct nk_allocator pool; /* allocator callback for dynamic buffers */ enum nk_allocation_type type; /* memory management type */ struct nk_memory memory; /* memory and size of the current memory block */ float grow_factor; /* growing factor for dynamic memory management */ nk_size allocated; /* total amount of memory allocated */ nk_size needed; /* totally consumed memory given that enough memory is present */ nk_size calls; /* number of allocation calls */ nk_size size; /* current size of the buffer */ }; #ifdef NK_INCLUDE_DEFAULT_ALLOCATOR NK_API void nk_buffer_init_default(struct nk_buffer*); #endif NK_API void nk_buffer_init(struct nk_buffer*, const struct nk_allocator*, nk_size size); NK_API void nk_buffer_init_fixed(struct nk_buffer*, void *memory, nk_size size); NK_API void nk_buffer_info(struct nk_memory_status*, struct nk_buffer*); NK_API void nk_buffer_push(struct nk_buffer*, enum nk_buffer_allocation_type type, const void *memory, nk_size size, nk_size align); NK_API void nk_buffer_mark(struct nk_buffer*, enum nk_buffer_allocation_type type); NK_API void nk_buffer_reset(struct nk_buffer*, enum nk_buffer_allocation_type type); NK_API void nk_buffer_clear(struct nk_buffer*); NK_API void nk_buffer_free(struct nk_buffer*); NK_API void *nk_buffer_memory(struct nk_buffer*); NK_API const void *nk_buffer_memory_const(const struct nk_buffer*); NK_API nk_size nk_buffer_total(struct nk_buffer*); /* ============================================================== * * STRING * * ===============================================================*/ /* Basic string buffer which is only used in context with the text editor * to manage and manipulate dynamic or fixed size string content. This is _NOT_ * the default string handling method. The only instance you should have any contact * with this API is if you interact with an `nk_text_edit` object inside one of the * copy and paste functions and even there only for more advanced cases. */ struct nk_str { struct nk_buffer buffer; int len; /* in codepoints/runes/glyphs */ }; #ifdef NK_INCLUDE_DEFAULT_ALLOCATOR NK_API void nk_str_init_default(struct nk_str*); #endif NK_API void nk_str_init(struct nk_str*, const struct nk_allocator*, nk_size size); NK_API void nk_str_init_fixed(struct nk_str*, void *memory, nk_size size); NK_API void nk_str_clear(struct nk_str*); NK_API void nk_str_free(struct nk_str*); NK_API int nk_str_append_text_char(struct nk_str*, const char*, int); NK_API int nk_str_append_str_char(struct nk_str*, const char*); NK_API int nk_str_append_text_utf8(struct nk_str*, const char*, int); NK_API int nk_str_append_str_utf8(struct nk_str*, const char*); NK_API int nk_str_append_text_runes(struct nk_str*, const nk_rune*, int); NK_API int nk_str_append_str_runes(struct nk_str*, const nk_rune*); NK_API int nk_str_insert_at_char(struct nk_str*, int pos, const char*, int); NK_API int nk_str_insert_at_rune(struct nk_str*, int pos, const char*, int); NK_API int nk_str_insert_text_char(struct nk_str*, int pos, const char*, int); NK_API int nk_str_insert_str_char(struct nk_str*, int pos, const char*); NK_API int nk_str_insert_text_utf8(struct nk_str*, int pos, const char*, int); NK_API int nk_str_insert_str_utf8(struct nk_str*, int pos, const char*); NK_API int nk_str_insert_text_runes(struct nk_str*, int pos, const nk_rune*, int); NK_API int nk_str_insert_str_runes(struct nk_str*, int pos, const nk_rune*); NK_API void nk_str_remove_chars(struct nk_str*, int len); NK_API void nk_str_remove_runes(struct nk_str *str, int len); NK_API void nk_str_delete_chars(struct nk_str*, int pos, int len); NK_API void nk_str_delete_runes(struct nk_str*, int pos, int len); NK_API char *nk_str_at_char(struct nk_str*, int pos); NK_API char *nk_str_at_rune(struct nk_str*, int pos, nk_rune *unicode, int *len); NK_API nk_rune nk_str_rune_at(const struct nk_str*, int pos); NK_API const char *nk_str_at_char_const(const struct nk_str*, int pos); NK_API const char *nk_str_at_const(const struct nk_str*, int pos, nk_rune *unicode, int *len); NK_API char *nk_str_get(struct nk_str*); NK_API const char *nk_str_get_const(const struct nk_str*); NK_API int nk_str_len(struct nk_str*); NK_API int nk_str_len_char(struct nk_str*); /*=============================================================== * * TEXT EDITOR * * ===============================================================*/ /* Editing text in this library is handled by either `nk_edit_string` or * `nk_edit_buffer`. But like almost everything in this library there are multiple * ways of doing it and a balance between control and ease of use with memory * as well as functionality controlled by flags. * * This library generally allows three different levels of memory control: * First of is the most basic way of just providing a simple char array with * string length. This method is probably the easiest way of handling simple * user text input. Main upside is complete control over memory while the biggest * downside in comparison with the other two approaches is missing undo/redo. * * For UIs that require undo/redo the second way was created. It is based on * a fixed size nk_text_edit struct, which has an internal undo/redo stack. * This is mainly useful if you want something more like a text editor but don't want * to have a dynamically growing buffer. * * The final way is using a dynamically growing nk_text_edit struct, which * has both a default version if you don't care where memory comes from and an * allocator version if you do. While the text editor is quite powerful for its * complexity I would not recommend editing gigabytes of data with it. * It is rather designed for uses cases which make sense for a GUI library not for * an full blown text editor. */ #ifndef NK_TEXTEDIT_UNDOSTATECOUNT #define NK_TEXTEDIT_UNDOSTATECOUNT 99 #endif #ifndef NK_TEXTEDIT_UNDOCHARCOUNT #define NK_TEXTEDIT_UNDOCHARCOUNT 999 #endif struct nk_text_edit; struct nk_clipboard { nk_handle userdata; nk_plugin_paste paste; nk_plugin_copy copy; }; struct nk_text_undo_record { int where; short insert_length; short delete_length; short char_storage; }; struct nk_text_undo_state { struct nk_text_undo_record undo_rec[NK_TEXTEDIT_UNDOSTATECOUNT]; nk_rune undo_char[NK_TEXTEDIT_UNDOCHARCOUNT]; short undo_point; short redo_point; short undo_char_point; short redo_char_point; }; enum nk_text_edit_type { NK_TEXT_EDIT_SINGLE_LINE, NK_TEXT_EDIT_MULTI_LINE }; enum nk_text_edit_mode { NK_TEXT_EDIT_MODE_VIEW, NK_TEXT_EDIT_MODE_INSERT, NK_TEXT_EDIT_MODE_REPLACE }; struct nk_text_edit { struct nk_clipboard clip; struct nk_str string; nk_plugin_filter filter; struct nk_vec2 scrollbar; int cursor; int select_start; int select_end; unsigned char mode; unsigned char cursor_at_end_of_line; unsigned char initialized; unsigned char has_preferred_x; unsigned char single_line; unsigned char active; unsigned char padding1; float preferred_x; struct nk_text_undo_state undo; }; /* filter function */ NK_API nk_bool nk_filter_default(const struct nk_text_edit*, nk_rune unicode); NK_API nk_bool nk_filter_ascii(const struct nk_text_edit*, nk_rune unicode); NK_API nk_bool nk_filter_float(const struct nk_text_edit*, nk_rune unicode); NK_API nk_bool nk_filter_decimal(const struct nk_text_edit*, nk_rune unicode); NK_API nk_bool nk_filter_hex(const struct nk_text_edit*, nk_rune unicode); NK_API nk_bool nk_filter_oct(const struct nk_text_edit*, nk_rune unicode); NK_API nk_bool nk_filter_binary(const struct nk_text_edit*, nk_rune unicode); /* text editor */ #ifdef NK_INCLUDE_DEFAULT_ALLOCATOR NK_API void nk_textedit_init_default(struct nk_text_edit*); #endif NK_API void nk_textedit_init(struct nk_text_edit*, struct nk_allocator*, nk_size size); NK_API void nk_textedit_init_fixed(struct nk_text_edit*, void *memory, nk_size size); NK_API void nk_textedit_free(struct nk_text_edit*); NK_API void nk_textedit_text(struct nk_text_edit*, const char*, int total_len); NK_API void nk_textedit_delete(struct nk_text_edit*, int where, int len); NK_API void nk_textedit_delete_selection(struct nk_text_edit*); NK_API void nk_textedit_select_all(struct nk_text_edit*); NK_API nk_bool nk_textedit_cut(struct nk_text_edit*); NK_API nk_bool nk_textedit_paste(struct nk_text_edit*, char const*, int len); NK_API void nk_textedit_undo(struct nk_text_edit*); NK_API void nk_textedit_redo(struct nk_text_edit*); /* =============================================================== * * DRAWING * * ===============================================================*/ /* This library was designed to be render backend agnostic so it does not draw anything to screen. Instead all drawn shapes, widgets are made of, are buffered into memory and make up a command queue. Each frame therefore fills the command buffer with draw commands that then need to be executed by the user and his own render backend. After that the command buffer needs to be cleared and a new frame can be started. It is probably important to note that the command buffer is the main drawing API and the optional vertex buffer API only takes this format and converts it into a hardware accessible format. To use the command queue to draw your own widgets you can access the command buffer of each window by calling `nk_window_get_canvas` after previously having called `nk_begin`: void draw_red_rectangle_widget(struct nk_context *ctx) { struct nk_command_buffer *canvas; struct nk_input *input = &ctx->input; canvas = nk_window_get_canvas(ctx); struct nk_rect space; enum nk_widget_layout_states state; state = nk_widget(&space, ctx); if (!state) return; if (state != NK_WIDGET_ROM) update_your_widget_by_user_input(...); nk_fill_rect(canvas, space, 0, nk_rgb(255,0,0)); } if (nk_begin(...)) { nk_layout_row_dynamic(ctx, 25, 1); draw_red_rectangle_widget(ctx); } nk_end(..) Important to know if you want to create your own widgets is the `nk_widget` call. It allocates space on the panel reserved for this widget to be used, but also returns the state of the widget space. If your widget is not seen and does not have to be updated it is '0' and you can just return. If it only has to be drawn the state will be `NK_WIDGET_ROM` otherwise you can do both update and draw your widget. The reason for separating is to only draw and update what is actually necessary which is crucial for performance. */ enum nk_command_type { NK_COMMAND_NOP, NK_COMMAND_SCISSOR, NK_COMMAND_LINE, NK_COMMAND_CURVE, NK_COMMAND_RECT, NK_COMMAND_RECT_FILLED, NK_COMMAND_RECT_MULTI_COLOR, NK_COMMAND_CIRCLE, NK_COMMAND_CIRCLE_FILLED, NK_COMMAND_ARC, NK_COMMAND_ARC_FILLED, NK_COMMAND_TRIANGLE, NK_COMMAND_TRIANGLE_FILLED, NK_COMMAND_POLYGON, NK_COMMAND_POLYGON_FILLED, NK_COMMAND_POLYLINE, NK_COMMAND_TEXT, NK_COMMAND_IMAGE, NK_COMMAND_IMAGE_FLIPPED, //< @r-lyeh NK_COMMAND_CUSTOM }; /* command base and header of every command inside the buffer */ struct nk_command { enum nk_command_type type; nk_size next; #ifdef NK_INCLUDE_COMMAND_USERDATA nk_handle userdata; #endif }; struct nk_command_scissor { struct nk_command header; short x, y; unsigned short w, h; }; struct nk_command_line { struct nk_command header; unsigned short line_thickness; struct nk_vec2i begin; struct nk_vec2i end; struct nk_color color; }; struct nk_command_curve { struct nk_command header; unsigned short line_thickness; struct nk_vec2i begin; struct nk_vec2i end; struct nk_vec2i ctrl[2]; struct nk_color color; }; struct nk_command_rect { struct nk_command header; unsigned short rounding; unsigned short line_thickness; short x, y; unsigned short w, h; struct nk_color color; }; struct nk_command_rect_filled { struct nk_command header; unsigned short rounding; short x, y; unsigned short w, h; struct nk_color color; }; struct nk_command_rect_multi_color { struct nk_command header; short x, y; unsigned short w, h; struct nk_color left; struct nk_color top; struct nk_color bottom; struct nk_color right; }; struct nk_command_triangle { struct nk_command header; unsigned short line_thickness; struct nk_vec2i a; struct nk_vec2i b; struct nk_vec2i c; struct nk_color color; }; struct nk_command_triangle_filled { struct nk_command header; struct nk_vec2i a; struct nk_vec2i b; struct nk_vec2i c; struct nk_color color; }; struct nk_command_circle { struct nk_command header; short x, y; unsigned short line_thickness; unsigned short w, h; struct nk_color color; }; struct nk_command_circle_filled { struct nk_command header; short x, y; unsigned short w, h; struct nk_color color; }; struct nk_command_arc { struct nk_command header; short cx, cy; unsigned short r; unsigned short line_thickness; float a[2]; struct nk_color color; }; struct nk_command_arc_filled { struct nk_command header; short cx, cy; unsigned short r; float a[2]; struct nk_color color; }; struct nk_command_polygon { struct nk_command header; struct nk_color color; unsigned short line_thickness; unsigned short point_count; struct nk_vec2i points[1]; }; struct nk_command_polygon_filled { struct nk_command header; struct nk_color color; unsigned short point_count; struct nk_vec2i points[1]; }; struct nk_command_polyline { struct nk_command header; struct nk_color color; unsigned short line_thickness; unsigned short point_count; struct nk_vec2i points[1]; }; struct nk_command_image { struct nk_command header; short x, y; unsigned short w, h; struct nk_image img; struct nk_color col; }; typedef void (*nk_command_custom_callback)(void *canvas, short x,short y, unsigned short w, unsigned short h, nk_handle callback_data); struct nk_command_custom { struct nk_command header; short x, y; unsigned short w, h; nk_handle callback_data; nk_command_custom_callback callback; }; struct nk_command_text { struct nk_command header; const struct nk_user_font *font; struct nk_color background; struct nk_color foreground; short x, y; unsigned short w, h; float height; int length; char string[1]; }; enum nk_command_clipping { NK_CLIPPING_OFF = nk_false, NK_CLIPPING_ON = nk_true }; struct nk_command_buffer { struct nk_buffer *base; struct nk_rect clip; int use_clipping; nk_handle userdata; nk_size begin, end, last; }; /* shape outlines */ NK_API void nk_stroke_line(struct nk_command_buffer *b, float x0, float y0, float x1, float y1, float line_thickness, struct nk_color); NK_API void nk_stroke_curve(struct nk_command_buffer*, float, float, float, float, float, float, float, float, float line_thickness, struct nk_color); NK_API void nk_stroke_rect(struct nk_command_buffer*, struct nk_rect, float rounding, float line_thickness, struct nk_color); NK_API void nk_stroke_circle(struct nk_command_buffer*, struct nk_rect, float line_thickness, struct nk_color); NK_API void nk_stroke_arc(struct nk_command_buffer*, float cx, float cy, float radius, float a_min, float a_max, float line_thickness, struct nk_color); NK_API void nk_stroke_triangle(struct nk_command_buffer*, float, float, float, float, float, float, float line_thichness, struct nk_color); NK_API void nk_stroke_polyline(struct nk_command_buffer*, float *points, int point_count, float line_thickness, struct nk_color col); NK_API void nk_stroke_polygon(struct nk_command_buffer*, float*, int point_count, float line_thickness, struct nk_color); /* filled shades */ NK_API void nk_fill_rect(struct nk_command_buffer*, struct nk_rect, float rounding, struct nk_color); NK_API void nk_fill_rect_multi_color(struct nk_command_buffer*, struct nk_rect, struct nk_color left, struct nk_color top, struct nk_color right, struct nk_color bottom); NK_API void nk_fill_circle(struct nk_command_buffer*, struct nk_rect, struct nk_color); NK_API void nk_fill_arc(struct nk_command_buffer*, float cx, float cy, float radius, float a_min, float a_max, struct nk_color); NK_API void nk_fill_triangle(struct nk_command_buffer*, float x0, float y0, float x1, float y1, float x2, float y2, struct nk_color); NK_API void nk_fill_polygon(struct nk_command_buffer*, float*, int point_count, struct nk_color); /* misc */ NK_API void nk_draw_image(struct nk_command_buffer*, struct nk_rect, const struct nk_image*, struct nk_color); NK_API void nk_draw_nine_slice(struct nk_command_buffer*, struct nk_rect, const struct nk_nine_slice*, struct nk_color); NK_API void nk_draw_text(struct nk_command_buffer*, struct nk_rect, const char *text, int len, const struct nk_user_font*, struct nk_color, struct nk_color); NK_API void nk_push_scissor(struct nk_command_buffer*, struct nk_rect); NK_API void nk_push_custom(struct nk_command_buffer*, struct nk_rect, nk_command_custom_callback, nk_handle usr); /* =============================================================== * * INPUT * * ===============================================================*/ struct nk_mouse_button { nk_bool down; unsigned int clicked; struct nk_vec2 clicked_pos; }; struct nk_mouse { struct nk_mouse_button buttons[NK_BUTTON_MAX]; struct nk_vec2 pos; struct nk_vec2 prev; struct nk_vec2 delta; struct nk_vec2 scroll_delta; unsigned char grab; unsigned char grabbed; unsigned char ungrab; }; struct nk_key { nk_bool down; unsigned int clicked; }; struct nk_keyboard { struct nk_key keys[NK_KEY_MAX]; char text[NK_INPUT_MAX]; int text_len; }; struct nk_input { struct nk_keyboard keyboard; struct nk_mouse mouse; }; NK_API nk_bool nk_input_has_mouse_click(const struct nk_input*, enum nk_buttons); NK_API nk_bool nk_input_has_mouse_click_in_rect(const struct nk_input*, enum nk_buttons, struct nk_rect); NK_API nk_bool nk_input_has_mouse_click_down_in_rect(const struct nk_input*, enum nk_buttons, struct nk_rect, nk_bool down); NK_API nk_bool nk_input_is_mouse_click_in_rect(const struct nk_input*, enum nk_buttons, struct nk_rect); NK_API nk_bool nk_input_is_mouse_click_down_in_rect(const struct nk_input *i, enum nk_buttons id, struct nk_rect b, nk_bool down); NK_API nk_bool nk_input_any_mouse_click_in_rect(const struct nk_input*, struct nk_rect); NK_API nk_bool nk_input_is_mouse_prev_hovering_rect(const struct nk_input*, struct nk_rect); NK_API nk_bool nk_input_is_mouse_hovering_rect(const struct nk_input*, struct nk_rect); NK_API nk_bool nk_input_mouse_clicked(const struct nk_input*, enum nk_buttons, struct nk_rect); NK_API nk_bool nk_input_is_mouse_down(const struct nk_input*, enum nk_buttons); NK_API nk_bool nk_input_is_mouse_pressed(const struct nk_input*, enum nk_buttons); NK_API nk_bool nk_input_is_mouse_released(const struct nk_input*, enum nk_buttons); NK_API nk_bool nk_input_is_key_pressed(const struct nk_input*, enum nk_keys); NK_API nk_bool nk_input_is_key_released(const struct nk_input*, enum nk_keys); NK_API nk_bool nk_input_is_key_down(const struct nk_input*, enum nk_keys); /* =============================================================== * * DRAW LIST * * ===============================================================*/ #ifdef NK_INCLUDE_VERTEX_BUFFER_OUTPUT /* The optional vertex buffer draw list provides a 2D drawing context with antialiasing functionality which takes basic filled or outlined shapes or a path and outputs vertexes, elements and draw commands. The actual draw list API is not required to be used directly while using this library since converting the default library draw command output is done by just calling `nk_convert` but I decided to still make this library accessible since it can be useful. The draw list is based on a path buffering and polygon and polyline rendering API which allows a lot of ways to draw 2D content to screen. In fact it is probably more powerful than needed but allows even more crazy things than this library provides by default. */ #ifdef NK_UINT_DRAW_INDEX typedef nk_uint nk_draw_index; #else typedef nk_ushort nk_draw_index; #endif enum nk_draw_list_stroke { NK_STROKE_OPEN = nk_false, /* build up path has no connection back to the beginning */ NK_STROKE_CLOSED = nk_true /* build up path has a connection back to the beginning */ }; enum nk_draw_vertex_layout_attribute { NK_VERTEX_POSITION, NK_VERTEX_COLOR, NK_VERTEX_TEXCOORD, NK_VERTEX_ATTRIBUTE_COUNT }; enum nk_draw_vertex_layout_format { NK_FORMAT_SCHAR, NK_FORMAT_SSHORT, NK_FORMAT_SINT, NK_FORMAT_UCHAR, NK_FORMAT_USHORT, NK_FORMAT_UINT, NK_FORMAT_FLOAT, NK_FORMAT_DOUBLE, NK_FORMAT_COLOR_BEGIN, NK_FORMAT_R8G8B8 = NK_FORMAT_COLOR_BEGIN, NK_FORMAT_R16G15B16, NK_FORMAT_R32G32B32, NK_FORMAT_R8G8B8A8, NK_FORMAT_B8G8R8A8, NK_FORMAT_R16G15B16A16, NK_FORMAT_R32G32B32A32, NK_FORMAT_R32G32B32A32_FLOAT, NK_FORMAT_R32G32B32A32_DOUBLE, NK_FORMAT_RGB32, NK_FORMAT_RGBA32, NK_FORMAT_COLOR_END = NK_FORMAT_RGBA32, NK_FORMAT_COUNT }; #define NK_VERTEX_LAYOUT_END NK_VERTEX_ATTRIBUTE_COUNT,NK_FORMAT_COUNT,0 struct nk_draw_vertex_layout_element { enum nk_draw_vertex_layout_attribute attribute; enum nk_draw_vertex_layout_format format; nk_size offset; }; struct nk_draw_command { unsigned int elem_count; /* number of elements in the current draw batch */ struct nk_rect clip_rect; /* current screen clipping rectangle */ nk_handle texture; /* current texture to set */ #ifdef NK_INCLUDE_COMMAND_USERDATA nk_handle userdata; #endif }; struct nk_draw_list { struct nk_rect clip_rect; struct nk_vec2 circle_vtx[12]; struct nk_convert_config config; struct nk_buffer *buffer; struct nk_buffer *vertices; struct nk_buffer *elements; unsigned int element_count; unsigned int vertex_count; unsigned int cmd_count; nk_size cmd_offset; unsigned int path_count; unsigned int path_offset; enum nk_anti_aliasing line_AA; enum nk_anti_aliasing shape_AA; #ifdef NK_INCLUDE_COMMAND_USERDATA nk_handle userdata; #endif }; /* draw list */ NK_API void nk_draw_list_init(struct nk_draw_list*); NK_API void nk_draw_list_setup(struct nk_draw_list*, const struct nk_convert_config*, struct nk_buffer *cmds, struct nk_buffer *vertices, struct nk_buffer *elements, enum nk_anti_aliasing line_aa,enum nk_anti_aliasing shape_aa); /* drawing */ #define nk_draw_list_foreach(cmd, can, b) for((cmd)=nk__draw_list_begin(can, b); (cmd)!=0; (cmd)=nk__draw_list_next(cmd, b, can)) NK_API const struct nk_draw_command* nk__draw_list_begin(const struct nk_draw_list*, const struct nk_buffer*); NK_API const struct nk_draw_command* nk__draw_list_next(const struct nk_draw_command*, const struct nk_buffer*, const struct nk_draw_list*); NK_API const struct nk_draw_command* nk__draw_list_end(const struct nk_draw_list*, const struct nk_buffer*); /* path */ NK_API void nk_draw_list_path_clear(struct nk_draw_list*); NK_API void nk_draw_list_path_line_to(struct nk_draw_list*, struct nk_vec2 pos); NK_API void nk_draw_list_path_arc_to_fast(struct nk_draw_list*, struct nk_vec2 center, float radius, int a_min, int a_max); NK_API void nk_draw_list_path_arc_to(struct nk_draw_list*, struct nk_vec2 center, float radius, float a_min, float a_max, unsigned int segments); NK_API void nk_draw_list_path_rect_to(struct nk_draw_list*, struct nk_vec2 a, struct nk_vec2 b, float rounding); NK_API void nk_draw_list_path_curve_to(struct nk_draw_list*, struct nk_vec2 p2, struct nk_vec2 p3, struct nk_vec2 p4, unsigned int num_segments); NK_API void nk_draw_list_path_fill(struct nk_draw_list*, struct nk_color); NK_API void nk_draw_list_path_stroke(struct nk_draw_list*, struct nk_color, enum nk_draw_list_stroke closed, float thickness); /* stroke */ NK_API void nk_draw_list_stroke_line(struct nk_draw_list*, struct nk_vec2 a, struct nk_vec2 b, struct nk_color, float thickness); NK_API void nk_draw_list_stroke_rect(struct nk_draw_list*, struct nk_rect rect, struct nk_color, float rounding, float thickness); NK_API void nk_draw_list_stroke_triangle(struct nk_draw_list*, struct nk_vec2 a, struct nk_vec2 b, struct nk_vec2 c, struct nk_color, float thickness); NK_API void nk_draw_list_stroke_circle(struct nk_draw_list*, struct nk_vec2 center, float radius, struct nk_color, unsigned int segs, float thickness); NK_API void nk_draw_list_stroke_curve(struct nk_draw_list*, struct nk_vec2 p0, struct nk_vec2 cp0, struct nk_vec2 cp1, struct nk_vec2 p1, struct nk_color, unsigned int segments, float thickness); NK_API void nk_draw_list_stroke_poly_line(struct nk_draw_list*, const struct nk_vec2 *pnts, const unsigned int cnt, struct nk_color, enum nk_draw_list_stroke, float thickness, enum nk_anti_aliasing); /* fill */ NK_API void nk_draw_list_fill_rect(struct nk_draw_list*, struct nk_rect rect, struct nk_color, float rounding); NK_API void nk_draw_list_fill_rect_multi_color(struct nk_draw_list*, struct nk_rect rect, struct nk_color left, struct nk_color top, struct nk_color right, struct nk_color bottom); NK_API void nk_draw_list_fill_triangle(struct nk_draw_list*, struct nk_vec2 a, struct nk_vec2 b, struct nk_vec2 c, struct nk_color); NK_API void nk_draw_list_fill_circle(struct nk_draw_list*, struct nk_vec2 center, float radius, struct nk_color col, unsigned int segs); NK_API void nk_draw_list_fill_poly_convex(struct nk_draw_list*, const struct nk_vec2 *points, const unsigned int count, struct nk_color, enum nk_anti_aliasing); /* misc */ NK_API void nk_draw_list_add_image(struct nk_draw_list*, struct nk_image texture, struct nk_rect rect, struct nk_color, int flipped); //< @r-lyeh: + flipped NK_API void nk_draw_list_add_text(struct nk_draw_list*, const struct nk_user_font*, struct nk_rect, const char *text, int len, float font_height, struct nk_color); #ifdef NK_INCLUDE_COMMAND_USERDATA NK_API void nk_draw_list_push_userdata(struct nk_draw_list*, nk_handle userdata); #endif #endif /* =============================================================== * * GUI * * ===============================================================*/ enum nk_style_item_type { NK_STYLE_ITEM_COLOR, NK_STYLE_ITEM_IMAGE, NK_STYLE_ITEM_NINE_SLICE }; union nk_style_item_data { struct nk_color color; struct nk_image image; struct nk_nine_slice slice; }; struct nk_style_item { enum nk_style_item_type type; union nk_style_item_data data; }; struct nk_style_text { struct nk_color color; struct nk_vec2 padding; }; struct nk_style_button { /* background */ struct nk_style_item normal; struct nk_style_item hover; struct nk_style_item active; struct nk_color border_color; /* text */ struct nk_color text_background; struct nk_color text_normal; struct nk_color text_hover; struct nk_color text_active; nk_flags text_alignment; /* properties */ float border; float rounding; struct nk_vec2 padding; struct nk_vec2 image_padding; struct nk_vec2 touch_padding; /* optional user callbacks */ nk_handle userdata; void(*draw_begin)(struct nk_command_buffer*, nk_handle userdata); void(*draw_end)(struct nk_command_buffer*, nk_handle userdata); }; struct nk_style_toggle { /* background */ struct nk_style_item normal; struct nk_style_item hover; struct nk_style_item active; struct nk_color border_color; /* cursor */ struct nk_style_item cursor_normal; struct nk_style_item cursor_hover; /* text */ struct nk_color text_normal; struct nk_color text_hover; struct nk_color text_active; struct nk_color text_background; nk_flags text_alignment; /* properties */ struct nk_vec2 padding; struct nk_vec2 touch_padding; float spacing; float border; /* optional user callbacks */ nk_handle userdata; void(*draw_begin)(struct nk_command_buffer*, nk_handle); void(*draw_end)(struct nk_command_buffer*, nk_handle); }; struct nk_style_selectable { /* background (inactive) */ struct nk_style_item normal; struct nk_style_item hover; struct nk_style_item pressed; /* background (active) */ struct nk_style_item normal_active; struct nk_style_item hover_active; struct nk_style_item pressed_active; /* text color (inactive) */ struct nk_color text_normal; struct nk_color text_hover; struct nk_color text_pressed; /* text color (active) */ struct nk_color text_normal_active; struct nk_color text_hover_active; struct nk_color text_pressed_active; struct nk_color text_background; nk_flags text_alignment; /* properties */ float rounding; struct nk_vec2 padding; struct nk_vec2 touch_padding; struct nk_vec2 image_padding; /* optional user callbacks */ nk_handle userdata; void(*draw_begin)(struct nk_command_buffer*, nk_handle); void(*draw_end)(struct nk_command_buffer*, nk_handle); }; struct nk_style_slider { /* background */ struct nk_style_item normal; struct nk_style_item hover; struct nk_style_item active; struct nk_color border_color; /* background bar */ struct nk_color bar_normal; struct nk_color bar_hover; struct nk_color bar_active; struct nk_color bar_filled; /* cursor */ struct nk_style_item cursor_normal; struct nk_style_item cursor_hover; struct nk_style_item cursor_active; /* properties */ float border; float rounding; float bar_height; struct nk_vec2 padding; struct nk_vec2 spacing; struct nk_vec2 cursor_size; /* optional buttons */ int show_buttons; struct nk_style_button inc_button; struct nk_style_button dec_button; enum nk_symbol_type inc_symbol; enum nk_symbol_type dec_symbol; /* optional user callbacks */ nk_handle userdata; void(*draw_begin)(struct nk_command_buffer*, nk_handle); void(*draw_end)(struct nk_command_buffer*, nk_handle); }; struct nk_style_progress { /* background */ struct nk_style_item normal; struct nk_style_item hover; struct nk_style_item active; struct nk_color border_color; /* cursor */ struct nk_style_item cursor_normal; struct nk_style_item cursor_hover; struct nk_style_item cursor_active; struct nk_color cursor_border_color; /* properties */ float rounding; float border; float cursor_border; float cursor_rounding; struct nk_vec2 padding; /* optional user callbacks */ nk_handle userdata; void(*draw_begin)(struct nk_command_buffer*, nk_handle); void(*draw_end)(struct nk_command_buffer*, nk_handle); }; struct nk_style_scrollbar { /* background */ struct nk_style_item normal; struct nk_style_item hover; struct nk_style_item active; struct nk_color border_color; /* cursor */ struct nk_style_item cursor_normal; struct nk_style_item cursor_hover; struct nk_style_item cursor_active; struct nk_color cursor_border_color; /* properties */ float border; float rounding; float border_cursor; float rounding_cursor; struct nk_vec2 padding; /* optional buttons */ int show_buttons; struct nk_style_button inc_button; struct nk_style_button dec_button; enum nk_symbol_type inc_symbol; enum nk_symbol_type dec_symbol; /* optional user callbacks */ nk_handle userdata; void(*draw_begin)(struct nk_command_buffer*, nk_handle); void(*draw_end)(struct nk_command_buffer*, nk_handle); }; struct nk_style_edit { /* background */ struct nk_style_item normal; struct nk_style_item hover; struct nk_style_item active; struct nk_color border_color; struct nk_style_scrollbar scrollbar; /* cursor */ struct nk_color cursor_normal; struct nk_color cursor_hover; struct nk_color cursor_text_normal; struct nk_color cursor_text_hover; /* text (unselected) */ struct nk_color text_normal; struct nk_color text_hover; struct nk_color text_active; /* text (selected) */ struct nk_color selected_normal; struct nk_color selected_hover; struct nk_color selected_text_normal; struct nk_color selected_text_hover; /* properties */ float border; float rounding; float cursor_size; struct nk_vec2 scrollbar_size; struct nk_vec2 padding; float row_padding; }; struct nk_style_property { /* background */ struct nk_style_item normal; struct nk_style_item hover; struct nk_style_item active; struct nk_color border_color; /* text */ struct nk_color label_normal; struct nk_color label_hover; struct nk_color label_active; /* symbols */ enum nk_symbol_type sym_left; enum nk_symbol_type sym_right; /* properties */ float border; float rounding; struct nk_vec2 padding; struct nk_style_edit edit; struct nk_style_button inc_button; struct nk_style_button dec_button; /* optional user callbacks */ nk_handle userdata; void(*draw_begin)(struct nk_command_buffer*, nk_handle); void(*draw_end)(struct nk_command_buffer*, nk_handle); }; struct nk_style_chart { /* colors */ struct nk_style_item background; struct nk_color border_color; struct nk_color selected_color; struct nk_color color; /* properties */ float border; float rounding; struct nk_vec2 padding; }; struct nk_style_combo { /* background */ struct nk_style_item normal; struct nk_style_item hover; struct nk_style_item active; struct nk_color border_color; /* label */ struct nk_color label_normal; struct nk_color label_hover; struct nk_color label_active; /* symbol */ struct nk_color symbol_normal; struct nk_color symbol_hover; struct nk_color symbol_active; /* button */ struct nk_style_button button; enum nk_symbol_type sym_normal; enum nk_symbol_type sym_hover; enum nk_symbol_type sym_active; /* properties */ float border; float rounding; struct nk_vec2 content_padding; struct nk_vec2 button_padding; struct nk_vec2 spacing; }; struct nk_style_tab { /* background */ struct nk_style_item background; struct nk_color border_color; struct nk_color text; /* button */ struct nk_style_button tab_maximize_button; struct nk_style_button tab_minimize_button; struct nk_style_button node_maximize_button; struct nk_style_button node_minimize_button; enum nk_symbol_type sym_minimize; enum nk_symbol_type sym_maximize; /* properties */ float border; float rounding; float indent; struct nk_vec2 padding; struct nk_vec2 spacing; }; enum nk_style_header_align { NK_HEADER_LEFT, NK_HEADER_RIGHT }; struct nk_style_window_header { /* background */ struct nk_style_item normal; struct nk_style_item hover; struct nk_style_item active; /* button */ struct nk_style_button close_button; struct nk_style_button minimize_button; enum nk_symbol_type close_symbol; enum nk_symbol_type minimize_symbol; enum nk_symbol_type maximize_symbol; /* title */ struct nk_color label_normal; struct nk_color label_hover; struct nk_color label_active; /* properties */ enum nk_style_header_align align; struct nk_vec2 padding; struct nk_vec2 label_padding; struct nk_vec2 spacing; }; struct nk_style_window { struct nk_style_window_header header; struct nk_style_item fixed_background; struct nk_color background; struct nk_color border_color; struct nk_color popup_border_color; struct nk_color combo_border_color; struct nk_color contextual_border_color; struct nk_color menu_border_color; struct nk_color group_border_color; struct nk_color tooltip_border_color; struct nk_style_item scaler; float border; float combo_border; float contextual_border; float menu_border; float group_border; float tooltip_border; float popup_border; float min_row_height_padding; float rounding; struct nk_vec2 spacing; struct nk_vec2 scrollbar_size; struct nk_vec2 min_size; struct nk_vec2 padding; struct nk_vec2 group_padding; struct nk_vec2 popup_padding; struct nk_vec2 combo_padding; struct nk_vec2 contextual_padding; struct nk_vec2 menu_padding; struct nk_vec2 tooltip_padding; }; struct nk_style { const struct nk_user_font *font; const struct nk_cursor *cursors[NK_CURSOR_COUNT]; const struct nk_cursor *cursor_active; struct nk_cursor *cursor_last; int cursor_visible; struct nk_style_text text; struct nk_style_button button; struct nk_style_button contextual_button; struct nk_style_button menu_button; struct nk_style_toggle option; struct nk_style_toggle checkbox; struct nk_style_selectable selectable; struct nk_style_slider slider; struct nk_style_progress progress; struct nk_style_property property; struct nk_style_edit edit; struct nk_style_chart chart; struct nk_style_scrollbar scrollh; struct nk_style_scrollbar scrollv; struct nk_style_tab tab; struct nk_style_combo combo; struct nk_style_window window; }; NK_API struct nk_style_item nk_style_item_color(struct nk_color); NK_API struct nk_style_item nk_style_item_image(struct nk_image img); NK_API struct nk_style_item nk_style_item_nine_slice(struct nk_nine_slice slice); NK_API struct nk_style_item nk_style_item_hide(void); /*============================================================== * PANEL * =============================================================*/ #ifndef NK_MAX_LAYOUT_ROW_TEMPLATE_COLUMNS #define NK_MAX_LAYOUT_ROW_TEMPLATE_COLUMNS 16 #endif #ifndef NK_CHART_MAX_SLOT #define NK_CHART_MAX_SLOT 4 #endif enum nk_panel_type { NK_PANEL_NONE = 0, NK_PANEL_WINDOW = NK_FLAG(0), NK_PANEL_GROUP = NK_FLAG(1), NK_PANEL_POPUP = NK_FLAG(2), NK_PANEL_CONTEXTUAL = NK_FLAG(4), NK_PANEL_COMBO = NK_FLAG(5), NK_PANEL_MENU = NK_FLAG(6), NK_PANEL_TOOLTIP = NK_FLAG(7) }; enum nk_panel_set { NK_PANEL_SET_NONBLOCK = NK_PANEL_CONTEXTUAL|NK_PANEL_COMBO|NK_PANEL_MENU|NK_PANEL_TOOLTIP, NK_PANEL_SET_POPUP = NK_PANEL_SET_NONBLOCK|NK_PANEL_POPUP, NK_PANEL_SET_SUB = NK_PANEL_SET_POPUP|NK_PANEL_GROUP }; struct nk_chart_slot { enum nk_chart_type type; struct nk_color color; struct nk_color highlight; float min, max, range; int count; struct nk_vec2 last; int index; }; struct nk_chart { int slot; float x, y, w, h; struct nk_chart_slot slots[NK_CHART_MAX_SLOT]; }; enum nk_panel_row_layout_type { NK_LAYOUT_DYNAMIC_FIXED = 0, NK_LAYOUT_DYNAMIC_ROW, NK_LAYOUT_DYNAMIC_FREE, NK_LAYOUT_DYNAMIC, NK_LAYOUT_STATIC_FIXED, NK_LAYOUT_STATIC_ROW, NK_LAYOUT_STATIC_FREE, NK_LAYOUT_STATIC, NK_LAYOUT_TEMPLATE, NK_LAYOUT_COUNT }; struct nk_row_layout { enum nk_panel_row_layout_type type; int index; float height; float min_height; int columns; const float *ratio; float item_width; float item_height; float item_offset; float filled; struct nk_rect item; int tree_depth; float templates[NK_MAX_LAYOUT_ROW_TEMPLATE_COLUMNS]; }; struct nk_popup_buffer { nk_size begin; nk_size parent; nk_size last; nk_size end; nk_bool active; }; struct nk_menu_state { float x, y, w, h; struct nk_scroll offset; }; struct nk_panel { enum nk_panel_type type; nk_flags flags; struct nk_rect bounds; nk_uint *offset_x; nk_uint *offset_y; float at_x, at_y, max_x; float footer_height; float header_height; float border; unsigned int has_scrolling; struct nk_rect clip; struct nk_menu_state menu; struct nk_row_layout row; struct nk_chart chart; struct nk_command_buffer *buffer; struct nk_panel *parent; }; /*============================================================== * WINDOW * =============================================================*/ #ifndef NK_WINDOW_MAX_NAME #define NK_WINDOW_MAX_NAME 64 #endif struct nk_table; enum nk_window_flags { NK_WINDOW_PRIVATE = NK_FLAG(NK_WINDOW_PUBLIC_FLAGS+1), //< @r-lyeh NK_WINDOW_DYNAMIC = NK_WINDOW_PRIVATE, /* special window type growing up in height while being filled to a certain maximum height */ NK_WINDOW_ROM = NK_FLAG(NK_WINDOW_PUBLIC_FLAGS+2), //< @r-lyeh /* sets window widgets into a read only mode and does not allow input changes */ NK_WINDOW_NOT_INTERACTIVE = NK_WINDOW_ROM|NK_WINDOW_NO_INPUT, /* prevents all interaction caused by input to either window or widgets inside */ NK_WINDOW_HIDDEN = NK_FLAG(NK_WINDOW_PUBLIC_FLAGS+3), //< @r-lyeh /* Hides window and stops any window interaction and drawing */ NK_WINDOW_CLOSED = NK_FLAG(NK_WINDOW_PUBLIC_FLAGS+4), //< @r-lyeh /* Directly closes and frees the window at the end of the frame */ NK_WINDOW_MINIMIZED = NK_FLAG(NK_WINDOW_PUBLIC_FLAGS+5), //< @r-lyeh /* marks the window as minimized */ NK_WINDOW_REMOVE_ROM = NK_FLAG(NK_WINDOW_PUBLIC_FLAGS+6), //< @r-lyeh /* Removes read only mode at the end of the window */ NK_WINDOW_PINNED = NK_FLAG(NK_WINDOW_PUBLIC_FLAGS+7), //< @r-lyeh NK_WINDOW_FULLSCREEN = NK_FLAG(NK_WINDOW_PUBLIC_FLAGS+8) //< @r-lyeh }; struct nk_popup_state { struct nk_window *win; enum nk_panel_type type; struct nk_popup_buffer buf; nk_hash name; nk_bool active; unsigned combo_count; unsigned con_count, con_old; unsigned active_con; struct nk_rect header; }; struct nk_edit_state { nk_hash name; unsigned int seq; unsigned int old; int active, prev; int cursor; int sel_start; int sel_end; struct nk_scroll scrollbar; unsigned char mode; unsigned char single_line; }; struct nk_property_state { int active, prev; char buffer[NK_MAX_NUMBER_BUFFER]; int length; int cursor; int select_start; int select_end; nk_hash name; unsigned int seq; unsigned int old; int state; }; struct nk_window { unsigned int seq; nk_hash name; char name_string[NK_WINDOW_MAX_NAME]; nk_flags flags; struct nk_rect bounds; struct nk_scroll scrollbar; struct nk_command_buffer buffer; struct nk_panel *layout; float scrollbar_hiding_timer; /* persistent widget state */ struct nk_property_state property; struct nk_popup_state popup; struct nk_edit_state edit; unsigned int scrolled; struct nk_table *tables; unsigned int table_count; /* window list hooks */ struct nk_window *next; struct nk_window *prev; struct nk_window *parent; #if 1 //< @r-lyeh nk_bool is_window_resizing; float is_window_restoring; // boolean but also a timer [0..1] normalized #endif }; /*============================================================== * STACK * =============================================================*/ /* The style modifier stack can be used to temporarily change a * property inside `nk_style`. For example if you want a special * red button you can temporarily push the old button color onto a stack * draw the button with a red color and then you just pop the old color * back from the stack: * * nk_style_push_style_item(ctx, &ctx->style.button.normal, nk_style_item_color(nk_rgb(255,0,0))); * nk_style_push_style_item(ctx, &ctx->style.button.hover, nk_style_item_color(nk_rgb(255,0,0))); * nk_style_push_style_item(ctx, &ctx->style.button.active, nk_style_item_color(nk_rgb(255,0,0))); * nk_style_push_vec2(ctx, &cx->style.button.padding, nk_vec2(2,2)); * * nk_button(...); * * nk_style_pop_style_item(ctx); * nk_style_pop_style_item(ctx); * nk_style_pop_style_item(ctx); * nk_style_pop_vec2(ctx); * * Nuklear has a stack for style_items, float properties, vector properties, * flags, colors, fonts and for button_behavior. Each has it's own fixed size stack * which can be changed at compile time. */ #ifndef NK_BUTTON_BEHAVIOR_STACK_SIZE #define NK_BUTTON_BEHAVIOR_STACK_SIZE 8 #endif #ifndef NK_FONT_STACK_SIZE #define NK_FONT_STACK_SIZE 8 #endif #ifndef NK_STYLE_ITEM_STACK_SIZE #define NK_STYLE_ITEM_STACK_SIZE 16 #endif #ifndef NK_FLOAT_STACK_SIZE #define NK_FLOAT_STACK_SIZE 32 #endif #ifndef NK_VECTOR_STACK_SIZE #define NK_VECTOR_STACK_SIZE 16 #endif #ifndef NK_FLAGS_STACK_SIZE #define NK_FLAGS_STACK_SIZE 32 #endif #ifndef NK_COLOR_STACK_SIZE #define NK_COLOR_STACK_SIZE 32 #endif #define NK_CONFIGURATION_STACK_TYPE(prefix, name, type)\ struct nk_config_stack_##name##_element {\ prefix##_##type *address;\ prefix##_##type old_value;\ } #define NK_CONFIG_STACK(type,size)\ struct nk_config_stack_##type {\ int head;\ struct nk_config_stack_##type##_element elements[size];\ } #define nk_float float NK_CONFIGURATION_STACK_TYPE(struct nk, style_item, style_item); NK_CONFIGURATION_STACK_TYPE(nk ,float, float); NK_CONFIGURATION_STACK_TYPE(struct nk, vec2, vec2); NK_CONFIGURATION_STACK_TYPE(nk ,flags, flags); NK_CONFIGURATION_STACK_TYPE(struct nk, color, color); NK_CONFIGURATION_STACK_TYPE(const struct nk, user_font, user_font*); NK_CONFIGURATION_STACK_TYPE(enum nk, button_behavior, button_behavior); NK_CONFIG_STACK(style_item, NK_STYLE_ITEM_STACK_SIZE); NK_CONFIG_STACK(float, NK_FLOAT_STACK_SIZE); NK_CONFIG_STACK(vec2, NK_VECTOR_STACK_SIZE); NK_CONFIG_STACK(flags, NK_FLAGS_STACK_SIZE); NK_CONFIG_STACK(color, NK_COLOR_STACK_SIZE); NK_CONFIG_STACK(user_font, NK_FONT_STACK_SIZE); NK_CONFIG_STACK(button_behavior, NK_BUTTON_BEHAVIOR_STACK_SIZE); struct nk_configuration_stacks { struct nk_config_stack_style_item style_items; struct nk_config_stack_float floats; struct nk_config_stack_vec2 vectors; struct nk_config_stack_flags flags; struct nk_config_stack_color colors; struct nk_config_stack_user_font fonts; struct nk_config_stack_button_behavior button_behaviors; }; /*============================================================== * CONTEXT * =============================================================*/ #define NK_VALUE_PAGE_CAPACITY \ (((NK_MAX(sizeof(struct nk_window),sizeof(struct nk_panel)) / sizeof(nk_uint))) / 2) struct nk_table { unsigned int seq; unsigned int size; nk_hash keys[NK_VALUE_PAGE_CAPACITY]; nk_uint values[NK_VALUE_PAGE_CAPACITY]; struct nk_table *next, *prev; }; union nk_page_data { struct nk_table tbl; struct nk_panel pan; struct nk_window win; }; struct nk_page_element { union nk_page_data data; struct nk_page_element *next; struct nk_page_element *prev; }; struct nk_page { unsigned int size; struct nk_page *next; struct nk_page_element win[1]; }; struct nk_pool { struct nk_allocator alloc; enum nk_allocation_type type; unsigned int page_count; struct nk_page *pages; struct nk_page_element *freelist; unsigned capacity; nk_size size; nk_size cap; }; struct nk_context { /* public: can be accessed freely */ struct nk_input input; struct nk_style style; struct nk_buffer memory; struct nk_clipboard clip; nk_flags last_widget_state; enum nk_button_behavior button_behavior; struct nk_configuration_stacks stacks; float delta_time_seconds; /* private: should only be accessed if you know what you are doing */ #ifdef NK_INCLUDE_VERTEX_BUFFER_OUTPUT struct nk_draw_list draw_list; #endif #ifdef NK_INCLUDE_COMMAND_USERDATA nk_handle userdata; #endif /* text editor objects are quite big because of an internal * undo/redo stack. Therefore it does not make sense to have one for * each window for temporary use cases, so I only provide *one* instance * for all windows. This works because the content is cleared anyway */ struct nk_text_edit text_edit; /* draw buffer used for overlay drawing operation like cursor */ struct nk_command_buffer overlay; /* windows */ int build; int use_pool; struct nk_pool pool; struct nk_window *begin; struct nk_window *end; struct nk_window *active; struct nk_window *current; struct nk_page_element *freelist; unsigned int count; unsigned int seq; }; /* ============================================================== * MATH * =============================================================== */ #define NK_PI 3.141592654f #define NK_UTF_INVALID 0xFFFD #define NK_MAX_FLOAT_PRECISION 2 #define NK_UNUSED(x) ((void)(x)) #define NK_SATURATE(x) (NK_MAX(0, NK_MIN(1.0f, x))) #define NK_LEN(a) (sizeof(a)/sizeof(a)[0]) #define NK_ABS(a) (((a) < 0) ? -(a) : (a)) #define NK_BETWEEN(x, a, b) ((a) <= (x) && (x) < (b)) #define NK_INBOX(px, py, x, y, w, h)\ (NK_BETWEEN(px,x,x+w) && NK_BETWEEN(py,y,y+h)) #define NK_INTERSECT(x0, y0, w0, h0, x1, y1, w1, h1) \ ((x1 < (x0 + w0)) && (x0 < (x1 + w1)) && \ (y1 < (y0 + h0)) && (y0 < (y1 + h1))) #define NK_CONTAINS(x, y, w, h, bx, by, bw, bh)\ (NK_INBOX(x,y, bx, by, bw, bh) && NK_INBOX(x+w,y+h, bx, by, bw, bh)) #define nk_vec2_sub(a, b) nk_vec2((a).x - (b).x, (a).y - (b).y) #define nk_vec2_add(a, b) nk_vec2((a).x + (b).x, (a).y + (b).y) #define nk_vec2_len_sqr(a) ((a).x*(a).x+(a).y*(a).y) #define nk_vec2_muls(a, t) nk_vec2((a).x * (t), (a).y * (t)) #define nk_ptr_add(t, p, i) ((t*)((void*)((nk_byte*)(p) + (i)))) #define nk_ptr_add_const(t, p, i) ((const t*)((const void*)((const nk_byte*)(p) + (i)))) #define nk_zero_struct(s) nk_zero(&s, sizeof(s)) /* ============================================================== * ALIGNMENT * =============================================================== */ /* Pointer to Integer type conversion for pointer alignment */ #if defined(__PTRDIFF_TYPE__) /* This case should work for GCC*/ # define NK_UINT_TO_PTR(x) ((void*)(__PTRDIFF_TYPE__)(x)) # define NK_PTR_TO_UINT(x) ((nk_size)(__PTRDIFF_TYPE__)(x)) #elif !defined(__GNUC__) /* works for compilers other than LLVM */ # define NK_UINT_TO_PTR(x) ((void*)&((char*)0)[x]) # define NK_PTR_TO_UINT(x) ((nk_size)(((char*)x)-(char*)0)) #elif defined(NK_USE_FIXED_TYPES) /* used if we have */ # define NK_UINT_TO_PTR(x) ((void*)(uintptr_t)(x)) # define NK_PTR_TO_UINT(x) ((uintptr_t)(x)) #else /* generates warning but works */ # define NK_UINT_TO_PTR(x) ((void*)(x)) # define NK_PTR_TO_UINT(x) ((nk_size)(x)) #endif #define NK_ALIGN_PTR(x, mask)\ (NK_UINT_TO_PTR((NK_PTR_TO_UINT((nk_byte*)(x) + (mask-1)) & ~(mask-1)))) #define NK_ALIGN_PTR_BACK(x, mask)\ (NK_UINT_TO_PTR((NK_PTR_TO_UINT((nk_byte*)(x)) & ~(mask-1)))) #if (defined(__GNUC__) && __GNUC__ >= 4) || defined(__clang__) #define NK_OFFSETOF(st,m) (__builtin_offsetof(st,m)) #else #define NK_OFFSETOF(st,m) ((nk_ptr)&(((st*)0)->m)) #endif #ifdef __cplusplus } #endif #ifdef __cplusplus template struct nk_alignof; template struct nk_helper{enum {value = size_diff};}; template struct nk_helper{enum {value = nk_alignof::value};}; template struct nk_alignof{struct Big {T x; char c;}; enum { diff = sizeof(Big) - sizeof(T), value = nk_helper::value};}; #define NK_ALIGNOF(t) (nk_alignof::value) #else #define NK_ALIGNOF(t) NK_OFFSETOF(struct {char c; t _h;}, _h) #endif #define NK_CONTAINER_OF(ptr,type,member)\ (type*)((void*)((char*)(1 ? (ptr): &((type*)0)->member) - NK_OFFSETOF(type, member))) #endif /* NK_NUKLEAR_H_ */ #ifdef NK_IMPLEMENTATION #ifndef NK_INTERNAL_H #define NK_INTERNAL_H #ifndef NK_POOL_DEFAULT_CAPACITY #define NK_POOL_DEFAULT_CAPACITY 16 #endif #ifndef NK_DEFAULT_COMMAND_BUFFER_SIZE #define NK_DEFAULT_COMMAND_BUFFER_SIZE (4*1024) #endif #ifndef NK_BUFFER_DEFAULT_INITIAL_SIZE #define NK_BUFFER_DEFAULT_INITIAL_SIZE (4*1024) #endif /* standard library headers */ #ifdef NK_INCLUDE_DEFAULT_ALLOCATOR #include /* malloc, free */ #endif #ifdef NK_INCLUDE_STANDARD_IO #include /* fopen, fclose,... */ #endif #ifdef NK_INCLUDE_STANDARD_VARARGS #include /* valist, va_start, va_end, ... */ #endif #ifndef NK_ASSERT #include #define NK_ASSERT(expr) assert(expr) #endif #define NK_DEFAULT (-1) #ifndef NK_VSNPRINTF /* If your compiler does support `vsnprintf` I would highly recommend * defining this to vsnprintf instead since `vsprintf` is basically * unbelievable unsafe and should *NEVER* be used. But I have to support * it since C89 only provides this unsafe version. */ #if (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)) ||\ (defined(__cplusplus) && (__cplusplus >= 201103L)) || \ (defined(_POSIX_C_SOURCE) && (_POSIX_C_SOURCE >= 200112L)) ||\ (defined(_XOPEN_SOURCE) && (_XOPEN_SOURCE >= 500)) ||\ defined(_ISOC99_SOURCE) || defined(_BSD_SOURCE) #define NK_VSNPRINTF(s,n,f,a) vsnprintf(s,n,f,a) #else #define NK_VSNPRINTF(s,n,f,a) vsprintf(s,f,a) #endif #endif #define NK_SCHAR_MIN (-127) #define NK_SCHAR_MAX 127 #define NK_UCHAR_MIN 0 #define NK_UCHAR_MAX 256 #define NK_SSHORT_MIN (-32767) #define NK_SSHORT_MAX 32767 #define NK_USHORT_MIN 0 #define NK_USHORT_MAX 65535 #define NK_SINT_MIN (-2147483647) #define NK_SINT_MAX 2147483647 #define NK_UINT_MIN 0 #define NK_UINT_MAX 4294967295u /* Make sure correct type size: * This will fire with a negative subscript error if the type sizes * are set incorrectly by the compiler, and compile out if not */ NK_STATIC_ASSERT(sizeof(nk_size) >= sizeof(void*)); NK_STATIC_ASSERT(sizeof(nk_ptr) == sizeof(void*)); NK_STATIC_ASSERT(sizeof(nk_flags) >= 4); NK_STATIC_ASSERT(sizeof(nk_rune) >= 4); NK_STATIC_ASSERT(sizeof(nk_ushort) == 2); NK_STATIC_ASSERT(sizeof(nk_short) == 2); NK_STATIC_ASSERT(sizeof(nk_uint) == 4); NK_STATIC_ASSERT(sizeof(nk_int) == 4); NK_STATIC_ASSERT(sizeof(nk_byte) == 1); #ifdef NK_INCLUDE_STANDARD_BOOL NK_STATIC_ASSERT(sizeof(nk_bool) == sizeof(bool)); #else NK_STATIC_ASSERT(sizeof(nk_bool) == 4); #endif NK_GLOBAL const struct nk_rect nk_null_rect = {-8192.0f, -8192.0f, 16384, 16384}; #define NK_FLOAT_PRECISION 0.00000000000001 NK_GLOBAL const struct nk_color nk_red = {255,0,0,255}; NK_GLOBAL const struct nk_color nk_green = {0,255,0,255}; NK_GLOBAL const struct nk_color nk_blue = {0,0,255,255}; NK_GLOBAL const struct nk_color nk_white = {255,255,255,255}; NK_GLOBAL const struct nk_color nk_black = {0,0,0,255}; NK_GLOBAL const struct nk_color nk_yellow = {255,255,0,255}; /* widget */ #define nk_widget_state_reset(s)\ if ((*(s)) & NK_WIDGET_STATE_MODIFIED)\ (*(s)) = NK_WIDGET_STATE_INACTIVE|NK_WIDGET_STATE_MODIFIED;\ else (*(s)) = NK_WIDGET_STATE_INACTIVE; /* math */ #ifndef NK_INV_SQRT NK_LIB float nk_inv_sqrt(float n); #endif #ifndef NK_SIN NK_LIB float nk_sin(float x); #endif #ifndef NK_COS NK_LIB float nk_cos(float x); #endif NK_LIB nk_uint nk_round_up_pow2(nk_uint v); NK_LIB struct nk_rect nk_shrink_rect(struct nk_rect r, float amount); NK_LIB struct nk_rect nk_pad_rect(struct nk_rect r, struct nk_vec2 pad); NK_LIB void nk_unify(struct nk_rect *clip, const struct nk_rect *a, float x0, float y0, float x1, float y1); NK_LIB double nk_pow(double x, int n); NK_LIB int nk_ifloord(double x); NK_LIB int nk_ifloorf(float x); NK_LIB int nk_iceilf(float x); NK_LIB int nk_log10(double n); /* util */ enum {NK_DO_NOT_STOP_ON_NEW_LINE, NK_STOP_ON_NEW_LINE}; NK_LIB nk_bool nk_is_lower(int c); NK_LIB nk_bool nk_is_upper(int c); NK_LIB int nk_to_upper(int c); NK_LIB int nk_to_lower(int c); #ifndef NK_MEMCPY NK_LIB void* nk_memcopy(void *dst, const void *src, nk_size n); #endif #ifndef NK_MEMSET NK_LIB void nk_memset(void *ptr, int c0, nk_size size); #endif NK_LIB void nk_zero(void *ptr, nk_size size); NK_LIB char *nk_itoa(char *s, long n); NK_LIB int nk_string_float_limit(char *string, int prec); #ifndef NK_DTOA NK_LIB char *nk_dtoa(char *s, double n); #endif NK_LIB int nk_text_clamp(const struct nk_user_font *font, const char *text, int text_len, float space, int *glyphs, float *text_width, nk_rune *sep_list, int sep_count); NK_LIB struct nk_vec2 nk_text_calculate_text_bounds(const struct nk_user_font *font, const char *begin, int byte_len, float row_height, const char **remaining, struct nk_vec2 *out_offset, int *glyphs, int op); #ifdef NK_INCLUDE_STANDARD_VARARGS NK_LIB int nk_strfmt(char *buf, int buf_size, const char *fmt, va_list args); #endif #ifdef NK_INCLUDE_STANDARD_IO NK_LIB char *nk_file_load(const char* path, nk_size* siz, struct nk_allocator *alloc); #endif /* buffer */ #ifdef NK_INCLUDE_DEFAULT_ALLOCATOR NK_LIB void* nk_malloc(nk_handle unused, void *old,nk_size size); NK_LIB void nk_mfree(nk_handle unused, void *ptr); #endif NK_LIB void* nk_buffer_align(void *unaligned, nk_size align, nk_size *alignment, enum nk_buffer_allocation_type type); NK_LIB void* nk_buffer_alloc(struct nk_buffer *b, enum nk_buffer_allocation_type type, nk_size size, nk_size align); NK_LIB void* nk_buffer_realloc(struct nk_buffer *b, nk_size capacity, nk_size *size); /* draw */ NK_LIB void nk_command_buffer_init(struct nk_command_buffer *cb, struct nk_buffer *b, enum nk_command_clipping clip); NK_LIB void nk_command_buffer_reset(struct nk_command_buffer *b); NK_LIB void* nk_command_buffer_push(struct nk_command_buffer* b, enum nk_command_type t, nk_size size); NK_LIB void nk_draw_symbol(struct nk_command_buffer *out, enum nk_symbol_type type, struct nk_rect content, struct nk_color background, struct nk_color foreground, float border_width, const struct nk_user_font *font); /* buffering */ NK_LIB void nk_start_buffer(struct nk_context *ctx, struct nk_command_buffer *b); NK_LIB void nk_start(struct nk_context *ctx, struct nk_window *win); NK_LIB void nk_start_popup(struct nk_context *ctx, struct nk_window *win); NK_LIB void nk_finish_popup(struct nk_context *ctx, struct nk_window*); NK_LIB void nk_finish_buffer(struct nk_context *ctx, struct nk_command_buffer *b); NK_LIB void nk_finish(struct nk_context *ctx, struct nk_window *w); NK_LIB void nk_build(struct nk_context *ctx); /* text editor */ NK_LIB void nk_textedit_clear_state(struct nk_text_edit *state, enum nk_text_edit_type type, nk_plugin_filter filter); NK_LIB void nk_textedit_click(struct nk_text_edit *state, float x, float y, const struct nk_user_font *font, float row_height); NK_LIB void nk_textedit_drag(struct nk_text_edit *state, float x, float y, const struct nk_user_font *font, float row_height); NK_LIB void nk_textedit_key(struct nk_text_edit *state, enum nk_keys key, int shift_mod, const struct nk_user_font *font, float row_height); /* window */ enum nk_window_insert_location { NK_INSERT_BACK, /* inserts window into the back of list (front of screen) */ NK_INSERT_FRONT /* inserts window into the front of list (back of screen) */ }; NK_LIB void *nk_create_window(struct nk_context *ctx); NK_LIB void nk_remove_window(struct nk_context*, struct nk_window*); NK_LIB void nk_free_window(struct nk_context *ctx, struct nk_window *win); NK_LIB struct nk_window *nk_find_window(struct nk_context *ctx, nk_hash hash, const char *name); NK_LIB void nk_insert_window(struct nk_context *ctx, struct nk_window *win, enum nk_window_insert_location loc); /* pool */ NK_LIB void nk_pool_init(struct nk_pool *pool, struct nk_allocator *alloc, unsigned int capacity); NK_LIB void nk_pool_free(struct nk_pool *pool); NK_LIB void nk_pool_init_fixed(struct nk_pool *pool, void *memory, nk_size size); NK_LIB struct nk_page_element *nk_pool_alloc(struct nk_pool *pool); /* page-element */ NK_LIB struct nk_page_element* nk_create_page_element(struct nk_context *ctx); NK_LIB void nk_link_page_element_into_freelist(struct nk_context *ctx, struct nk_page_element *elem); NK_LIB void nk_free_page_element(struct nk_context *ctx, struct nk_page_element *elem); /* table */ NK_LIB struct nk_table* nk_create_table(struct nk_context *ctx); NK_LIB void nk_remove_table(struct nk_window *win, struct nk_table *tbl); NK_LIB void nk_free_table(struct nk_context *ctx, struct nk_table *tbl); NK_LIB void nk_push_table(struct nk_window *win, struct nk_table *tbl); NK_LIB nk_uint *nk_add_value(struct nk_context *ctx, struct nk_window *win, nk_hash name, nk_uint value); NK_LIB nk_uint *nk_find_value(struct nk_window *win, nk_hash name); /* panel */ NK_LIB void *nk_create_panel(struct nk_context *ctx); NK_LIB void nk_free_panel(struct nk_context*, struct nk_panel *pan); NK_LIB nk_bool nk_panel_has_header(nk_flags flags, const char *title); NK_LIB struct nk_vec2 nk_panel_get_padding(const struct nk_style *style, enum nk_panel_type type); NK_LIB float nk_panel_get_border(const struct nk_style *style, nk_flags flags, enum nk_panel_type type); NK_LIB struct nk_color nk_panel_get_border_color(const struct nk_style *style, enum nk_panel_type type); NK_LIB nk_bool nk_panel_is_sub(enum nk_panel_type type); NK_LIB nk_bool nk_panel_is_nonblock(enum nk_panel_type type); NK_LIB nk_bool nk_panel_begin(struct nk_context *ctx, const char *title, enum nk_panel_type panel_type); NK_LIB void nk_panel_end(struct nk_context *ctx); /* layout */ NK_LIB float nk_layout_row_calculate_usable_space(const struct nk_style *style, enum nk_panel_type type, float total_space, int columns); NK_LIB void nk_panel_layout(const struct nk_context *ctx, struct nk_window *win, float height, int cols); NK_LIB void nk_row_layout(struct nk_context *ctx, enum nk_layout_format fmt, float height, int cols, int width); NK_LIB void nk_panel_alloc_row(const struct nk_context *ctx, struct nk_window *win); NK_LIB void nk_layout_widget_space(struct nk_rect *bounds, const struct nk_context *ctx, struct nk_window *win, int modify); NK_LIB void nk_panel_alloc_space(struct nk_rect *bounds, const struct nk_context *ctx); NK_LIB void nk_layout_peek(struct nk_rect *bounds, struct nk_context *ctx); /* popup */ NK_LIB nk_bool nk_nonblock_begin(struct nk_context *ctx, nk_flags flags, struct nk_rect body, struct nk_rect header, enum nk_panel_type panel_type); /* text */ struct nk_text { struct nk_vec2 padding; struct nk_color background; struct nk_color text; }; NK_LIB void nk_widget_text(struct nk_command_buffer *o, struct nk_rect b, const char *string, int len, const struct nk_text *t, nk_flags a, const struct nk_user_font *f); NK_LIB void nk_widget_text_wrap(struct nk_command_buffer *o, struct nk_rect b, const char *string, int len, const struct nk_text *t, const struct nk_user_font *f); /* button */ NK_LIB nk_bool nk_button_behavior(nk_flags *state, struct nk_rect r, const struct nk_input *i, enum nk_button_behavior behavior); NK_LIB const struct nk_style_item* nk_draw_button(struct nk_command_buffer *out, const struct nk_rect *bounds, nk_flags state, const struct nk_style_button *style); NK_LIB nk_bool nk_do_button(nk_flags *state, struct nk_command_buffer *out, struct nk_rect r, const struct nk_style_button *style, const struct nk_input *in, enum nk_button_behavior behavior, struct nk_rect *content); NK_LIB void nk_draw_button_text(struct nk_command_buffer *out, const struct nk_rect *bounds, const struct nk_rect *content, nk_flags state, const struct nk_style_button *style, const char *txt, int len, nk_flags text_alignment, const struct nk_user_font *font); NK_LIB nk_bool nk_do_button_text(nk_flags *state, struct nk_command_buffer *out, struct nk_rect bounds, const char *string, int len, nk_flags align, enum nk_button_behavior behavior, const struct nk_style_button *style, const struct nk_input *in, const struct nk_user_font *font); NK_LIB void nk_draw_button_symbol(struct nk_command_buffer *out, const struct nk_rect *bounds, const struct nk_rect *content, nk_flags state, const struct nk_style_button *style, enum nk_symbol_type type, const struct nk_user_font *font); NK_LIB nk_bool nk_do_button_symbol(nk_flags *state, struct nk_command_buffer *out, struct nk_rect bounds, enum nk_symbol_type symbol, enum nk_button_behavior behavior, const struct nk_style_button *style, const struct nk_input *in, const struct nk_user_font *font); NK_LIB void nk_draw_button_image(struct nk_command_buffer *out, const struct nk_rect *bounds, const struct nk_rect *content, nk_flags state, const struct nk_style_button *style, const struct nk_image *img); NK_LIB nk_bool nk_do_button_image(nk_flags *state, struct nk_command_buffer *out, struct nk_rect bounds, struct nk_image img, enum nk_button_behavior b, const struct nk_style_button *style, const struct nk_input *in); NK_LIB void nk_draw_button_text_symbol(struct nk_command_buffer *out, const struct nk_rect *bounds, const struct nk_rect *label, const struct nk_rect *symbol, nk_flags state, const struct nk_style_button *style, const char *str, int len, enum nk_symbol_type type, const struct nk_user_font *font); NK_LIB nk_bool nk_do_button_text_symbol(nk_flags *state, struct nk_command_buffer *out, struct nk_rect bounds, enum nk_symbol_type symbol, const char *str, int len, nk_flags align, enum nk_button_behavior behavior, const struct nk_style_button *style, const struct nk_user_font *font, const struct nk_input *in); NK_LIB void nk_draw_button_text_image(struct nk_command_buffer *out, const struct nk_rect *bounds, const struct nk_rect *label, const struct nk_rect *image, nk_flags state, const struct nk_style_button *style, const char *str, int len, const struct nk_user_font *font, const struct nk_image *img, nk_flags align); //< @r-lyeh: add last align param NK_LIB nk_bool nk_do_button_text_image(nk_flags *state, struct nk_command_buffer *out, struct nk_rect bounds, struct nk_image img, const char* str, int len, nk_flags align, enum nk_button_behavior behavior, const struct nk_style_button *style, const struct nk_user_font *font, const struct nk_input *in); /* toggle */ enum nk_toggle_type { NK_TOGGLE_CHECK, NK_TOGGLE_OPTION }; NK_LIB nk_bool nk_toggle_behavior(const struct nk_input *in, struct nk_rect select, nk_flags *state, nk_bool active); NK_LIB void nk_draw_checkbox(struct nk_command_buffer *out, nk_flags state, const struct nk_style_toggle *style, nk_bool active, const struct nk_rect *label, const struct nk_rect *selector, const struct nk_rect *cursors, const char *string, int len, const struct nk_user_font *font); NK_LIB void nk_draw_option(struct nk_command_buffer *out, nk_flags state, const struct nk_style_toggle *style, nk_bool active, const struct nk_rect *label, const struct nk_rect *selector, const struct nk_rect *cursors, const char *string, int len, const struct nk_user_font *font); NK_LIB nk_bool nk_do_toggle(nk_flags *state, struct nk_command_buffer *out, struct nk_rect r, nk_bool *active, const char *str, int len, enum nk_toggle_type type, const struct nk_style_toggle *style, const struct nk_input *in, const struct nk_user_font *font); /* progress */ NK_LIB nk_size nk_progress_behavior(nk_flags *state, struct nk_input *in, struct nk_rect r, struct nk_rect cursor, nk_size max, nk_size value, nk_bool modifiable); NK_LIB void nk_draw_progress(struct nk_command_buffer *out, nk_flags state, const struct nk_style_progress *style, const struct nk_rect *bounds, const struct nk_rect *scursor, nk_size value, nk_size max); NK_LIB nk_size nk_do_progress(nk_flags *state, struct nk_command_buffer *out, struct nk_rect bounds, nk_size value, nk_size max, nk_bool modifiable, const struct nk_style_progress *style, struct nk_input *in); /* slider */ NK_LIB float nk_slider_behavior(nk_flags *state, struct nk_rect *logical_cursor, struct nk_rect *visual_cursor, struct nk_input *in, struct nk_rect bounds, float slider_min, float slider_max, float slider_value, float slider_step, float slider_steps); NK_LIB void nk_draw_slider(struct nk_command_buffer *out, nk_flags state, const struct nk_style_slider *style, const struct nk_rect *bounds, const struct nk_rect *visual_cursor, float min, float value, float max); NK_LIB float nk_do_slider(nk_flags *state, struct nk_command_buffer *out, struct nk_rect bounds, float min, float val, float max, float step, const struct nk_style_slider *style, struct nk_input *in, const struct nk_user_font *font); /* scrollbar */ NK_LIB float nk_scrollbar_behavior(nk_flags *state, struct nk_input *in, int has_scrolling, const struct nk_rect *scroll, const struct nk_rect *cursor, const struct nk_rect *empty0, const struct nk_rect *empty1, float scroll_offset, float target, float scroll_step, enum nk_orientation o); NK_LIB void nk_draw_scrollbar(struct nk_command_buffer *out, nk_flags state, const struct nk_style_scrollbar *style, const struct nk_rect *bounds, const struct nk_rect *scroll); NK_LIB float nk_do_scrollbarv(nk_flags *state, struct nk_command_buffer *out, struct nk_rect scroll, int has_scrolling, float offset, float target, float step, float button_pixel_inc, const struct nk_style_scrollbar *style, struct nk_input *in, const struct nk_user_font *font); NK_LIB float nk_do_scrollbarh(nk_flags *state, struct nk_command_buffer *out, struct nk_rect scroll, int has_scrolling, float offset, float target, float step, float button_pixel_inc, const struct nk_style_scrollbar *style, struct nk_input *in, const struct nk_user_font *font); /* selectable */ NK_LIB void nk_draw_selectable(struct nk_command_buffer *out, nk_flags state, const struct nk_style_selectable *style, nk_bool active, const struct nk_rect *bounds, const struct nk_rect *icon, const struct nk_image *img, enum nk_symbol_type sym, const char *string, int len, nk_flags align, const struct nk_user_font *font); NK_LIB nk_bool nk_do_selectable(nk_flags *state, struct nk_command_buffer *out, struct nk_rect bounds, const char *str, int len, nk_flags align, nk_bool *value, const struct nk_style_selectable *style, const struct nk_input *in, const struct nk_user_font *font); NK_LIB nk_bool nk_do_selectable_image(nk_flags *state, struct nk_command_buffer *out, struct nk_rect bounds, const char *str, int len, nk_flags align, nk_bool *value, const struct nk_image *img, const struct nk_style_selectable *style, const struct nk_input *in, const struct nk_user_font *font); /* edit */ NK_LIB void nk_edit_draw_text(struct nk_command_buffer *out, const struct nk_style_edit *style, float pos_x, float pos_y, float x_offset, const char *text, int byte_len, float row_height, const struct nk_user_font *font, struct nk_color background, struct nk_color foreground, nk_bool is_selected); NK_LIB nk_flags nk_do_edit(nk_flags *state, struct nk_command_buffer *out, struct nk_rect bounds, nk_flags flags, nk_plugin_filter filter, struct nk_text_edit *edit, const struct nk_style_edit *style, struct nk_input *in, const struct nk_user_font *font); /* color-picker */ NK_LIB nk_bool nk_color_picker_behavior(nk_flags *state, const struct nk_rect *bounds, const struct nk_rect *matrix, const struct nk_rect *hue_bar, const struct nk_rect *alpha_bar, struct nk_colorf *color, const struct nk_input *in); NK_LIB void nk_draw_color_picker(struct nk_command_buffer *o, const struct nk_rect *matrix, const struct nk_rect *hue_bar, const struct nk_rect *alpha_bar, struct nk_colorf col); NK_LIB nk_bool nk_do_color_picker(nk_flags *state, struct nk_command_buffer *out, struct nk_colorf *col, enum nk_color_format fmt, struct nk_rect bounds, struct nk_vec2 padding, const struct nk_input *in, const struct nk_user_font *font); /* property */ enum nk_property_status { NK_PROPERTY_DEFAULT, NK_PROPERTY_EDIT, NK_PROPERTY_DRAG }; enum nk_property_filter { NK_FILTER_INT, NK_FILTER_FLOAT }; enum nk_property_kind { NK_PROPERTY_INT, NK_PROPERTY_FLOAT, NK_PROPERTY_DOUBLE }; union nk_property { int i; float f; double d; }; struct nk_property_variant { enum nk_property_kind kind; union nk_property value; union nk_property min_value; union nk_property max_value; union nk_property step; }; NK_LIB struct nk_property_variant nk_property_variant_int(int value, int min_value, int max_value, int step); NK_LIB struct nk_property_variant nk_property_variant_float(float value, float min_value, float max_value, float step); NK_LIB struct nk_property_variant nk_property_variant_double(double value, double min_value, double max_value, double step); NK_LIB void nk_drag_behavior(nk_flags *state, const struct nk_input *in, struct nk_rect drag, struct nk_property_variant *variant, float inc_per_pixel); NK_LIB void nk_property_behavior(nk_flags *ws, const struct nk_input *in, struct nk_rect property, struct nk_rect label, struct nk_rect edit, struct nk_rect empty, int *state, struct nk_property_variant *variant, float inc_per_pixel); NK_LIB void nk_draw_property(struct nk_command_buffer *out, const struct nk_style_property *style, const struct nk_rect *bounds, const struct nk_rect *label, nk_flags state, const char *name, int len, const struct nk_user_font *font); NK_LIB void nk_do_property(nk_flags *ws, struct nk_command_buffer *out, struct nk_rect property, const char *name, struct nk_property_variant *variant, float inc_per_pixel, char *buffer, int *len, int *state, int *cursor, int *select_begin, int *select_end, const struct nk_style_property *style, enum nk_property_filter filter, struct nk_input *in, const struct nk_user_font *font, struct nk_text_edit *text_edit, enum nk_button_behavior behavior); NK_LIB void nk_property(struct nk_context *ctx, const char *name, struct nk_property_variant *variant, float inc_per_pixel, const enum nk_property_filter filter); #ifdef NK_INCLUDE_FONT_BAKING #define STB_RECT_PACK_IMPLEMENTATION #define STB_TRUETYPE_IMPLEMENTATION /* Allow consumer to define own STBTT_malloc/STBTT_free, and use the font atlas' allocator otherwise */ #ifndef STBTT_malloc static void* nk_stbtt_malloc(nk_size size, void *user_data) { struct nk_allocator *alloc = (struct nk_allocator *) user_data; return alloc->alloc(alloc->userdata, 0, size); } static void nk_stbtt_free(void *ptr, void *user_data) { struct nk_allocator *alloc = (struct nk_allocator *) user_data; alloc->free(alloc->userdata, ptr); } #define STBTT_malloc(x,u) nk_stbtt_malloc(x,u) #define STBTT_free(x,u) nk_stbtt_free(x,u) #endif /* STBTT_malloc */ #endif /* NK_INCLUDE_FONT_BAKING */ #endif /* =============================================================== * * MATH * * ===============================================================*/ /* Since nuklear is supposed to work on all systems providing floating point math without any dependencies I also had to implement my own math functions for sqrt, sin and cos. Since the actual highly accurate implementations for the standard library functions are quite complex and I do not need high precision for my use cases I use approximations. Sqrt ---- For square root nuklear uses the famous fast inverse square root: https://en.wikipedia.org/wiki/Fast_inverse_square_root with slightly tweaked magic constant. While on today's hardware it is probably not faster it is still fast and accurate enough for nuklear's use cases. IMPORTANT: this requires float format IEEE 754 Sine/Cosine ----------- All constants inside both function are generated Remez's minimax approximations for value range 0...2*PI. The reason why I decided to approximate exactly that range is that nuklear only needs sine and cosine to generate circles which only requires that exact range. In addition I used Remez instead of Taylor for additional precision: www.lolengine.net/blog/2011/12/21/better-function-approximations. The tool I used to generate constants for both sine and cosine (it can actually approximate a lot more functions) can be found here: www.lolengine.net/wiki/oss/lolremez */ #ifndef NK_INV_SQRT #define NK_INV_SQRT nk_inv_sqrt NK_LIB float nk_inv_sqrt(float n) { float x2; const float threehalfs = 1.5f; union {nk_uint i; float f;} conv = {0}; conv.f = n; x2 = n * 0.5f; conv.i = 0x5f375A84 - (conv.i >> 1); conv.f = conv.f * (threehalfs - (x2 * conv.f * conv.f)); return conv.f; } #endif #ifndef NK_SIN #define NK_SIN nk_sin NK_LIB float nk_sin(float x) { NK_STORAGE const float a0 = +1.91059300966915117e-31f; NK_STORAGE const float a1 = +1.00086760103908896f; NK_STORAGE const float a2 = -1.21276126894734565e-2f; NK_STORAGE const float a3 = -1.38078780785773762e-1f; NK_STORAGE const float a4 = -2.67353392911981221e-2f; NK_STORAGE const float a5 = +2.08026600266304389e-2f; NK_STORAGE const float a6 = -3.03996055049204407e-3f; NK_STORAGE const float a7 = +1.38235642404333740e-4f; return a0 + x*(a1 + x*(a2 + x*(a3 + x*(a4 + x*(a5 + x*(a6 + x*a7)))))); } #endif #ifndef NK_COS #define NK_COS nk_cos NK_LIB float nk_cos(float x) { /* New implementation. Also generated using lolremez. */ /* Old version significantly deviated from expected results. */ NK_STORAGE const float a0 = 9.9995999154986614e-1f; NK_STORAGE const float a1 = 1.2548995793001028e-3f; NK_STORAGE const float a2 = -5.0648546280678015e-1f; NK_STORAGE const float a3 = 1.2942246466519995e-2f; NK_STORAGE const float a4 = 2.8668384702547972e-2f; NK_STORAGE const float a5 = 7.3726485210586547e-3f; NK_STORAGE const float a6 = -3.8510875386947414e-3f; NK_STORAGE const float a7 = 4.7196604604366623e-4f; NK_STORAGE const float a8 = -1.8776444013090451e-5f; return a0 + x*(a1 + x*(a2 + x*(a3 + x*(a4 + x*(a5 + x*(a6 + x*(a7 + x*a8))))))); } #endif NK_LIB nk_uint nk_round_up_pow2(nk_uint v) { v--; v |= v >> 1; v |= v >> 2; v |= v >> 4; v |= v >> 8; v |= v >> 16; v++; return v; } NK_LIB double nk_pow(double x, int n) { /* check the sign of n */ double r = 1; int plus = n >= 0; n = (plus) ? n : -n; while (n > 0) { if ((n & 1) == 1) r *= x; n /= 2; x *= x; } return plus ? r : 1.0 / r; } NK_LIB int nk_ifloord(double x) { x = (double)((int)x - ((x < 0.0) ? 1 : 0)); return (int)x; } NK_LIB int nk_ifloorf(float x) { x = (float)((int)x - ((x < 0.0f) ? 1 : 0)); return (int)x; } NK_LIB int nk_iceilf(float x) { if (x >= 0) { int i = (int)x; return (x > i) ? i+1: i; } else { int t = (int)x; float r = x - (float)t; return (r > 0.0f) ? t+1: t; } } NK_LIB int nk_log10(double n) { int neg; int ret; int exp = 0; neg = (n < 0) ? 1 : 0; ret = (neg) ? (int)-n : (int)n; while ((ret / 10) > 0) { ret /= 10; exp++; } if (neg) exp = -exp; return exp; } NK_API struct nk_rect nk_get_null_rect(void) { return nk_null_rect; } NK_API struct nk_rect nk_rect(float x, float y, float w, float h) { struct nk_rect r; r.x = x; r.y = y; r.w = w; r.h = h; return r; } NK_API struct nk_rect nk_recti(int x, int y, int w, int h) { struct nk_rect r; r.x = (float)x; r.y = (float)y; r.w = (float)w; r.h = (float)h; return r; } NK_API struct nk_rect nk_recta(struct nk_vec2 pos, struct nk_vec2 size) { return nk_rect(pos.x, pos.y, size.x, size.y); } NK_API struct nk_rect nk_rectv(const float *r) { return nk_rect(r[0], r[1], r[2], r[3]); } NK_API struct nk_rect nk_rectiv(const int *r) { return nk_recti(r[0], r[1], r[2], r[3]); } NK_API struct nk_vec2 nk_rect_pos(struct nk_rect r) { struct nk_vec2 ret; ret.x = r.x; ret.y = r.y; return ret; } NK_API struct nk_vec2 nk_rect_size(struct nk_rect r) { struct nk_vec2 ret; ret.x = r.w; ret.y = r.h; return ret; } NK_LIB struct nk_rect nk_shrink_rect(struct nk_rect r, float amount) { struct nk_rect res; r.w = NK_MAX(r.w, 2 * amount); r.h = NK_MAX(r.h, 2 * amount); res.x = r.x + amount; res.y = r.y + amount; res.w = r.w - 2 * amount; res.h = r.h - 2 * amount; return res; } NK_LIB struct nk_rect nk_pad_rect(struct nk_rect r, struct nk_vec2 pad) { r.w = NK_MAX(r.w, 2 * pad.x); r.h = NK_MAX(r.h, 2 * pad.y); r.x += pad.x; r.y += pad.y; r.w -= 2 * pad.x; r.h -= 2 * pad.y; return r; } NK_API struct nk_vec2 nk_vec2(float x, float y) { struct nk_vec2 ret; ret.x = x; ret.y = y; return ret; } NK_API struct nk_vec2 nk_vec2i(int x, int y) { struct nk_vec2 ret; ret.x = (float)x; ret.y = (float)y; return ret; } NK_API struct nk_vec2 nk_vec2v(const float *v) { return nk_vec2(v[0], v[1]); } NK_API struct nk_vec2 nk_vec2iv(const int *v) { return nk_vec2i(v[0], v[1]); } NK_LIB void nk_unify(struct nk_rect *clip, const struct nk_rect *a, float x0, float y0, float x1, float y1) { NK_ASSERT(a); NK_ASSERT(clip); clip->x = NK_MAX(a->x, x0); clip->y = NK_MAX(a->y, y0); clip->w = NK_MIN(a->x + a->w, x1) - clip->x; clip->h = NK_MIN(a->y + a->h, y1) - clip->y; clip->w = NK_MAX(0, clip->w); clip->h = NK_MAX(0, clip->h); } NK_API void nk_triangle_from_direction(struct nk_vec2 *result, struct nk_rect r, float pad_x, float pad_y, enum nk_heading direction) { float w_half, h_half; NK_ASSERT(result); r.w = NK_MAX(2 * pad_x, r.w); r.w *= direction == NK_DOWN ? 0.5f : 1; //< @r-lyeh *0.5 r.h = NK_MAX(2 * pad_y, r.h); r.h *= direction == NK_DOWN ? 0.5f : 1; //< @r-lyeh *0.5 r.w = r.w - 2 * pad_x; r.h = r.h - 2 * pad_y; r.x = r.x + pad_x; r.y = r.y + pad_y; if(direction == NK_DOWN) r.x += 5, r.y += 5; //< @r-lyeh w_half = r.w / 2.0f; h_half = r.h / 2.0f; if (direction == NK_UP) { result[0] = nk_vec2(r.x + w_half, r.y); result[1] = nk_vec2(r.x + r.w, r.y + r.h); result[2] = nk_vec2(r.x, r.y + r.h); } else if (direction == NK_RIGHT) { result[0] = nk_vec2(r.x, r.y); result[1] = nk_vec2(r.x + r.w, r.y + h_half); result[2] = nk_vec2(r.x, r.y + r.h); } else if (direction == NK_DOWN) { result[0] = nk_vec2(r.x, r.y); result[1] = nk_vec2(r.x + r.w, r.y); result[2] = nk_vec2(r.x + w_half, r.y + r.h); } else { result[0] = nk_vec2(r.x, r.y + h_half); result[1] = nk_vec2(r.x + r.w, r.y); result[2] = nk_vec2(r.x + r.w, r.y + r.h); } } /* =============================================================== * * UTIL * * ===============================================================*/ NK_INTERN int nk_str_match_here(const char *regexp, const char *text); NK_INTERN int nk_str_match_star(int c, const char *regexp, const char *text); NK_LIB nk_bool nk_is_lower(int c) {return (c >= 'a' && c <= 'z') || (c >= 0xE0 && c <= 0xFF);} NK_LIB nk_bool nk_is_upper(int c){return (c >= 'A' && c <= 'Z') || (c >= 0xC0 && c <= 0xDF);} NK_LIB int nk_to_upper(int c) {return (c >= 'a' && c <= 'z') ? (c - ('a' - 'A')) : c;} NK_LIB int nk_to_lower(int c) {return (c >= 'A' && c <= 'Z') ? (c - ('a' + 'A')) : c;} #ifndef NK_MEMCPY #define NK_MEMCPY nk_memcopy NK_LIB void* nk_memcopy(void *dst0, const void *src0, nk_size length) { nk_ptr t; char *dst = (char*)dst0; const char *src = (const char*)src0; if (length == 0 || dst == src) goto done; #define nk_word int #define nk_wsize sizeof(nk_word) #define nk_wmask (nk_wsize-1) #define NK_TLOOP(s) if (t) NK_TLOOP1(s) #define NK_TLOOP1(s) do { s; } while (--t) if (dst < src) { t = (nk_ptr)src; /* only need low bits */ if ((t | (nk_ptr)dst) & nk_wmask) { if ((t ^ (nk_ptr)dst) & nk_wmask || length < nk_wsize) t = length; else t = nk_wsize - (t & nk_wmask); length -= t; NK_TLOOP1(*dst++ = *src++); } t = length / nk_wsize; NK_TLOOP(*(nk_word*)(void*)dst = *(const nk_word*)(const void*)src; src += nk_wsize; dst += nk_wsize); t = length & nk_wmask; NK_TLOOP(*dst++ = *src++); } else { src += length; dst += length; t = (nk_ptr)src; if ((t | (nk_ptr)dst) & nk_wmask) { if ((t ^ (nk_ptr)dst) & nk_wmask || length <= nk_wsize) t = length; else t &= nk_wmask; length -= t; NK_TLOOP1(*--dst = *--src); } t = length / nk_wsize; NK_TLOOP(src -= nk_wsize; dst -= nk_wsize; *(nk_word*)(void*)dst = *(const nk_word*)(const void*)src); t = length & nk_wmask; NK_TLOOP(*--dst = *--src); } #undef nk_word #undef nk_wsize #undef nk_wmask #undef NK_TLOOP #undef NK_TLOOP1 done: return (dst0); } #endif #ifndef NK_MEMSET #define NK_MEMSET nk_memset NK_LIB void nk_memset(void *ptr, int c0, nk_size size) { #define nk_word unsigned #define nk_wsize sizeof(nk_word) #define nk_wmask (nk_wsize - 1) nk_byte *dst = (nk_byte*)ptr; unsigned c = 0; nk_size t = 0; if ((c = (nk_byte)c0) != 0) { c = (c << 8) | c; /* at least 16-bits */ if (sizeof(unsigned int) > 2) c = (c << 16) | c; /* at least 32-bits*/ } /* too small of a word count */ dst = (nk_byte*)ptr; if (size < 3 * nk_wsize) { while (size--) *dst++ = (nk_byte)c0; return; } /* align destination */ if ((t = NK_PTR_TO_UINT(dst) & nk_wmask) != 0) { t = nk_wsize -t; size -= t; do { *dst++ = (nk_byte)c0; } while (--t != 0); } /* fill word */ t = size / nk_wsize; do { *(nk_word*)((void*)dst) = c; dst += nk_wsize; } while (--t != 0); /* fill trailing bytes */ t = (size & nk_wmask); if (t != 0) { do { *dst++ = (nk_byte)c0; } while (--t != 0); } #undef nk_word #undef nk_wsize #undef nk_wmask } #endif NK_LIB void nk_zero(void *ptr, nk_size size) { NK_ASSERT(ptr); NK_MEMSET(ptr, 0, size); } NK_API int nk_strlen(const char *str) { int siz = 0; NK_ASSERT(str); while (str && *str++ != '\0') siz++; return siz; } NK_API int nk_strtoi(const char *str, const char **endptr) { int neg = 1; const char *p = str; int value = 0; NK_ASSERT(str); if (!str) return 0; /* skip whitespace */ while (*p == ' ') p++; if (*p == '-') { neg = -1; p++; } while (*p && *p >= '0' && *p <= '9') { value = value * 10 + (int) (*p - '0'); p++; } if (endptr) *endptr = p; return neg*value; } NK_API double nk_strtod(const char *str, const char **endptr) { double m; double neg = 1.0; const char *p = str; double value = 0; double number = 0; NK_ASSERT(str); if (!str) return 0; /* skip whitespace */ while (*p == ' ') p++; if (*p == '-') { neg = -1.0; p++; } while (*p && *p != '.' && *p != 'e') { value = value * 10.0 + (double) (*p - '0'); p++; } if (*p == '.') { p++; for(m = 0.1; *p && *p != 'e'; p++ ) { value = value + (double) (*p - '0') * m; m *= 0.1; } } if (*p == 'e') { int i, pow, div; p++; if (*p == '-') { div = nk_true; p++; } else if (*p == '+') { div = nk_false; p++; } else div = nk_false; for (pow = 0; *p; p++) pow = pow * 10 + (int) (*p - '0'); for (m = 1.0, i = 0; i < pow; i++) m *= 10.0; if (div) value /= m; else value *= m; } number = value * neg; if (endptr) *endptr = p; return number; } NK_API float nk_strtof(const char *str, const char **endptr) { float float_value; double double_value; double_value = NK_STRTOD(str, endptr); float_value = (float)double_value; return float_value; } NK_API int nk_stricmp(const char *s1, const char *s2) { nk_int c1,c2,d; do { c1 = *s1++; c2 = *s2++; d = c1 - c2; while (d) { if (c1 <= 'Z' && c1 >= 'A') { d += ('a' - 'A'); if (!d) break; } if (c2 <= 'Z' && c2 >= 'A') { d -= ('a' - 'A'); if (!d) break; } return ((d >= 0) << 1) - 1; } } while (c1); return 0; } NK_API int nk_stricmpn(const char *s1, const char *s2, int n) { int c1,c2,d; NK_ASSERT(n >= 0); do { c1 = *s1++; c2 = *s2++; if (!n--) return 0; d = c1 - c2; while (d) { if (c1 <= 'Z' && c1 >= 'A') { d += ('a' - 'A'); if (!d) break; } if (c2 <= 'Z' && c2 >= 'A') { d -= ('a' - 'A'); if (!d) break; } return ((d >= 0) << 1) - 1; } } while (c1); return 0; } NK_INTERN int nk_str_match_here(const char *regexp, const char *text) { if (regexp[0] == '\0') return 1; if (regexp[1] == '*') return nk_str_match_star(regexp[0], regexp+2, text); if (regexp[0] == '$' && regexp[1] == '\0') return *text == '\0'; if (*text!='\0' && (regexp[0]=='.' || regexp[0]==*text)) return nk_str_match_here(regexp+1, text+1); return 0; } NK_INTERN int nk_str_match_star(int c, const char *regexp, const char *text) { do {/* a '* matches zero or more instances */ if (nk_str_match_here(regexp, text)) return 1; } while (*text != '\0' && (*text++ == c || c == '.')); return 0; } NK_API int nk_strfilter(const char *text, const char *regexp) { /* c matches any literal character c . matches any single character ^ matches the beginning of the input string $ matches the end of the input string * matches zero or more occurrences of the previous character*/ if (regexp[0] == '^') return nk_str_match_here(regexp+1, text); do { /* must look even if string is empty */ if (nk_str_match_here(regexp, text)) return 1; } while (*text++ != '\0'); return 0; } NK_API int nk_strmatch_fuzzy_text(const char *str, int str_len, const char *pattern, int *out_score) { /* Returns true if each character in pattern is found sequentially within str * if found then out_score is also set. Score value has no intrinsic meaning. * Range varies with pattern. Can only compare scores with same search pattern. */ /* bonus for adjacent matches */ #define NK_ADJACENCY_BONUS 5 /* bonus if match occurs after a separator */ #define NK_SEPARATOR_BONUS 10 /* bonus if match is uppercase and prev is lower */ #define NK_CAMEL_BONUS 10 /* penalty applied for every letter in str before the first match */ #define NK_LEADING_LETTER_PENALTY (-3) /* maximum penalty for leading letters */ #define NK_MAX_LEADING_LETTER_PENALTY (-9) /* penalty for every letter that doesn't matter */ #define NK_UNMATCHED_LETTER_PENALTY (-1) /* loop variables */ int score = 0; char const * pattern_iter = pattern; int str_iter = 0; int prev_matched = nk_false; int prev_lower = nk_false; /* true so if first letter match gets separator bonus*/ int prev_separator = nk_true; /* use "best" matched letter if multiple string letters match the pattern */ char const * best_letter = 0; int best_letter_score = 0; /* loop over strings */ NK_ASSERT(str); NK_ASSERT(pattern); if (!str || !str_len || !pattern) return 0; while (str_iter < str_len) { const char pattern_letter = *pattern_iter; const char str_letter = str[str_iter]; int next_match = *pattern_iter != '\0' && nk_to_lower(pattern_letter) == nk_to_lower(str_letter); int rematch = best_letter && nk_to_upper(*best_letter) == nk_to_upper(str_letter); int advanced = next_match && best_letter; int pattern_repeat = best_letter && *pattern_iter != '\0'; pattern_repeat = pattern_repeat && nk_to_lower(*best_letter) == nk_to_lower(pattern_letter); if (advanced || pattern_repeat) { score += best_letter_score; best_letter = 0; best_letter_score = 0; } if (next_match || rematch) { int new_score = 0; /* Apply penalty for each letter before the first pattern match */ if (pattern_iter == pattern) { int count = (int)(&str[str_iter] - str); int penalty = NK_LEADING_LETTER_PENALTY * count; if (penalty < NK_MAX_LEADING_LETTER_PENALTY) penalty = NK_MAX_LEADING_LETTER_PENALTY; score += penalty; } /* apply bonus for consecutive bonuses */ if (prev_matched) new_score += NK_ADJACENCY_BONUS; /* apply bonus for matches after a separator */ if (prev_separator) new_score += NK_SEPARATOR_BONUS; /* apply bonus across camel case boundaries */ if (prev_lower && nk_is_upper(str_letter)) new_score += NK_CAMEL_BONUS; /* update pattern iter IFF the next pattern letter was matched */ if (next_match) ++pattern_iter; /* update best letter in str which may be for a "next" letter or a rematch */ if (new_score >= best_letter_score) { /* apply penalty for now skipped letter */ if (best_letter != 0) score += NK_UNMATCHED_LETTER_PENALTY; best_letter = &str[str_iter]; best_letter_score = new_score; } prev_matched = nk_true; } else { score += NK_UNMATCHED_LETTER_PENALTY; prev_matched = nk_false; } /* separators should be more easily defined */ prev_lower = nk_is_lower(str_letter) != 0; prev_separator = str_letter == '_' || str_letter == ' '; ++str_iter; } /* apply score for last match */ if (best_letter) score += best_letter_score; /* did not match full pattern */ if (*pattern_iter != '\0') return nk_false; if (out_score) *out_score = score; return nk_true; } NK_API int nk_strmatch_fuzzy_string(char const *str, char const *pattern, int *out_score) { return nk_strmatch_fuzzy_text(str, nk_strlen(str), pattern, out_score); } NK_LIB int nk_string_float_limit(char *string, int prec) { int dot = 0; char *c = string; while (*c) { if (*c == '.') { dot = 1; c++; continue; } if (dot == (prec+1)) { *c = 0; break; } if (dot > 0) dot++; c++; } return (int)(c - string); } NK_INTERN void nk_strrev_ascii(char *s) { int len = nk_strlen(s); int end = len / 2; int i = 0; char t; for (; i < end; ++i) { t = s[i]; s[i] = s[len - 1 - i]; s[len -1 - i] = t; } } NK_LIB char* nk_itoa(char *s, long n) { long i = 0; if (n == 0) { s[i++] = '0'; s[i] = 0; return s; } if (n < 0) { s[i++] = '-'; n = -n; } while (n > 0) { s[i++] = (char)('0' + (n % 10)); n /= 10; } s[i] = 0; if (s[0] == '-') ++s; nk_strrev_ascii(s); return s; } #ifndef NK_DTOA #define NK_DTOA nk_dtoa NK_LIB char* nk_dtoa(char *s, double n) { int useExp = 0; int digit = 0, m = 0, m1 = 0; char *c = s; int neg = 0; NK_ASSERT(s); if (!s) return 0; if (n == 0.0) { s[0] = '0'; s[1] = '\0'; return s; } neg = (n < 0); if (neg) n = -n; /* calculate magnitude */ m = nk_log10(n); useExp = (m >= 14 || (neg && m >= 9) || m <= -9); if (neg) *(c++) = '-'; /* set up for scientific notation */ if (useExp) { if (m < 0) m -= 1; n = n / (double)nk_pow(10.0, m); m1 = m; m = 0; } if (m < 1.0) { m = 0; } /* convert the number */ while (n > NK_FLOAT_PRECISION || m >= 0) { double weight = nk_pow(10.0, m); if (weight > 0) { double t = (double)n / weight; digit = nk_ifloord(t); n -= ((double)digit * weight); *(c++) = (char)('0' + (char)digit); } if (m == 0 && n > 0) *(c++) = '.'; m--; } if (useExp) { /* convert the exponent */ int i, j; *(c++) = 'e'; if (m1 > 0) { *(c++) = '+'; } else { *(c++) = '-'; m1 = -m1; } m = 0; while (m1 > 0) { *(c++) = (char)('0' + (char)(m1 % 10)); m1 /= 10; m++; } c -= m; for (i = 0, j = m-1; i= buf_size) break; iter++; /* flag arguments */ while (*iter) { if (*iter == '-') flag |= NK_ARG_FLAG_LEFT; else if (*iter == '+') flag |= NK_ARG_FLAG_PLUS; else if (*iter == ' ') flag |= NK_ARG_FLAG_SPACE; else if (*iter == '#') flag |= NK_ARG_FLAG_NUM; else if (*iter == '0') flag |= NK_ARG_FLAG_ZERO; else break; iter++; } /* width argument */ width = NK_DEFAULT; if (*iter >= '1' && *iter <= '9') { const char *end; width = nk_strtoi(iter, &end); if (end == iter) width = -1; else iter = end; } else if (*iter == '*') { width = va_arg(args, int); iter++; } /* precision argument */ precision = NK_DEFAULT; if (*iter == '.') { iter++; if (*iter == '*') { precision = va_arg(args, int); iter++; } else { const char *end; precision = nk_strtoi(iter, &end); if (end == iter) precision = -1; else iter = end; } } /* length modifier */ if (*iter == 'h') { if (*(iter+1) == 'h') { arg_type = NK_ARG_TYPE_CHAR; iter++; } else arg_type = NK_ARG_TYPE_SHORT; iter++; } else if (*iter == 'l') { arg_type = NK_ARG_TYPE_LONG; iter++; } else arg_type = NK_ARG_TYPE_DEFAULT; /* specifier */ if (*iter == '%') { NK_ASSERT(arg_type == NK_ARG_TYPE_DEFAULT); NK_ASSERT(precision == NK_DEFAULT); NK_ASSERT(width == NK_DEFAULT); if (len < buf_size) buf[len++] = '%'; } else if (*iter == 's') { /* string */ const char *str = va_arg(args, const char*); NK_ASSERT(str != buf && "buffer and argument are not allowed to overlap!"); NK_ASSERT(arg_type == NK_ARG_TYPE_DEFAULT); NK_ASSERT(precision == NK_DEFAULT); NK_ASSERT(width == NK_DEFAULT); if (str == buf) return -1; while (str && *str && len < buf_size) buf[len++] = *str++; } else if (*iter == 'n') { /* current length callback */ signed int *n = va_arg(args, int*); NK_ASSERT(arg_type == NK_ARG_TYPE_DEFAULT); NK_ASSERT(precision == NK_DEFAULT); NK_ASSERT(width == NK_DEFAULT); if (n) *n = len; } else if (*iter == 'c' || *iter == 'i' || *iter == 'd') { /* signed integer */ long value = 0; const char *num_iter; int num_len, num_print, padding; int cur_precision = NK_MAX(precision, 1); int cur_width = NK_MAX(width, 0); /* retrieve correct value type */ if (arg_type == NK_ARG_TYPE_CHAR) value = (signed char)va_arg(args, int); else if (arg_type == NK_ARG_TYPE_SHORT) value = (signed short)va_arg(args, int); else if (arg_type == NK_ARG_TYPE_LONG) value = va_arg(args, signed long); else if (*iter == 'c') value = (unsigned char)va_arg(args, int); else value = va_arg(args, signed int); /* convert number to string */ nk_itoa(number_buffer, value); num_len = nk_strlen(number_buffer); padding = NK_MAX(cur_width - NK_MAX(cur_precision, num_len), 0); if ((flag & NK_ARG_FLAG_PLUS) || (flag & NK_ARG_FLAG_SPACE)) padding = NK_MAX(padding-1, 0); /* fill left padding up to a total of `width` characters */ if (!(flag & NK_ARG_FLAG_LEFT)) { while (padding-- > 0 && (len < buf_size)) { if ((flag & NK_ARG_FLAG_ZERO) && (precision == NK_DEFAULT)) buf[len++] = '0'; else buf[len++] = ' '; } } /* copy string value representation into buffer */ if ((flag & NK_ARG_FLAG_PLUS) && value >= 0 && len < buf_size) buf[len++] = '+'; else if ((flag & NK_ARG_FLAG_SPACE) && value >= 0 && len < buf_size) buf[len++] = ' '; /* fill up to precision number of digits with '0' */ num_print = NK_MAX(cur_precision, num_len); while (precision && (num_print > num_len) && (len < buf_size)) { buf[len++] = '0'; num_print--; } /* copy string value representation into buffer */ num_iter = number_buffer; while (precision && *num_iter && len < buf_size) buf[len++] = *num_iter++; /* fill right padding up to width characters */ if (flag & NK_ARG_FLAG_LEFT) { while ((padding-- > 0) && (len < buf_size)) buf[len++] = ' '; } } else if (*iter == 'o' || *iter == 'x' || *iter == 'X' || *iter == 'u') { /* unsigned integer */ unsigned long value = 0; int num_len = 0, num_print, padding = 0; int cur_precision = NK_MAX(precision, 1); int cur_width = NK_MAX(width, 0); unsigned int base = (*iter == 'o') ? 8: (*iter == 'u')? 10: 16; /* print oct/hex/dec value */ const char *upper_output_format = "0123456789ABCDEF"; const char *lower_output_format = "0123456789abcdef"; const char *output_format = (*iter == 'x') ? lower_output_format: upper_output_format; /* retrieve correct value type */ if (arg_type == NK_ARG_TYPE_CHAR) value = (unsigned char)va_arg(args, int); else if (arg_type == NK_ARG_TYPE_SHORT) value = (unsigned short)va_arg(args, int); else if (arg_type == NK_ARG_TYPE_LONG) value = va_arg(args, unsigned long); else value = va_arg(args, unsigned int); do { /* convert decimal number into hex/oct number */ int digit = output_format[value % base]; if (num_len < NK_MAX_NUMBER_BUFFER) number_buffer[num_len++] = (char)digit; value /= base; } while (value > 0); num_print = NK_MAX(cur_precision, num_len); padding = NK_MAX(cur_width - NK_MAX(cur_precision, num_len), 0); if (flag & NK_ARG_FLAG_NUM) padding = NK_MAX(padding-1, 0); /* fill left padding up to a total of `width` characters */ if (!(flag & NK_ARG_FLAG_LEFT)) { while ((padding-- > 0) && (len < buf_size)) { if ((flag & NK_ARG_FLAG_ZERO) && (precision == NK_DEFAULT)) buf[len++] = '0'; else buf[len++] = ' '; } } /* fill up to precision number of digits */ if (num_print && (flag & NK_ARG_FLAG_NUM)) { if ((*iter == 'o') && (len < buf_size)) { buf[len++] = '0'; } else if ((*iter == 'x') && ((len+1) < buf_size)) { buf[len++] = '0'; buf[len++] = 'x'; } else if ((*iter == 'X') && ((len+1) < buf_size)) { buf[len++] = '0'; buf[len++] = 'X'; } } while (precision && (num_print > num_len) && (len < buf_size)) { buf[len++] = '0'; num_print--; } /* reverse number direction */ while (num_len > 0) { if (precision && (len < buf_size)) buf[len++] = number_buffer[num_len-1]; num_len--; } /* fill right padding up to width characters */ if (flag & NK_ARG_FLAG_LEFT) { while ((padding-- > 0) && (len < buf_size)) buf[len++] = ' '; } } else if (*iter == 'f') { /* floating point */ const char *num_iter; int cur_precision = (precision < 0) ? 6: precision; int prefix, cur_width = NK_MAX(width, 0); double value = va_arg(args, double); int num_len = 0, frac_len = 0, dot = 0; int padding = 0; NK_ASSERT(arg_type == NK_ARG_TYPE_DEFAULT); NK_DTOA(number_buffer, value); num_len = nk_strlen(number_buffer); /* calculate padding */ num_iter = number_buffer; while (*num_iter && *num_iter != '.') num_iter++; prefix = (*num_iter == '.')?(int)(num_iter - number_buffer)+1:0; padding = NK_MAX(cur_width - (prefix + NK_MIN(cur_precision, num_len - prefix)) , 0); if ((flag & NK_ARG_FLAG_PLUS) || (flag & NK_ARG_FLAG_SPACE)) padding = NK_MAX(padding-1, 0); /* fill left padding up to a total of `width` characters */ if (!(flag & NK_ARG_FLAG_LEFT)) { while (padding-- > 0 && (len < buf_size)) { if (flag & NK_ARG_FLAG_ZERO) buf[len++] = '0'; else buf[len++] = ' '; } } /* copy string value representation into buffer */ num_iter = number_buffer; if ((flag & NK_ARG_FLAG_PLUS) && (value >= 0) && (len < buf_size)) buf[len++] = '+'; else if ((flag & NK_ARG_FLAG_SPACE) && (value >= 0) && (len < buf_size)) buf[len++] = ' '; while (*num_iter) { if (dot) frac_len++; if (len < buf_size) buf[len++] = *num_iter; if (*num_iter == '.') dot = 1; if (frac_len >= cur_precision) break; num_iter++; } /* fill number up to precision */ while (frac_len < cur_precision) { if (!dot && len < buf_size) { buf[len++] = '.'; dot = 1; } if (len < buf_size) buf[len++] = '0'; frac_len++; } /* fill right padding up to width characters */ if (flag & NK_ARG_FLAG_LEFT) { while ((padding-- > 0) && (len < buf_size)) buf[len++] = ' '; } } else { /* Specifier not supported: g,G,e,E,p,z */ NK_ASSERT(0 && "specifier is not supported!"); return result; } } buf[(len >= buf_size)?(buf_size-1):len] = 0; result = (len >= buf_size)?-1:len; return result; } #endif NK_LIB int nk_strfmt(char *buf, int buf_size, const char *fmt, va_list args) { int result = -1; NK_ASSERT(buf); NK_ASSERT(buf_size); if (!buf || !buf_size || !fmt) return 0; #ifdef NK_INCLUDE_STANDARD_IO result = NK_VSNPRINTF(buf, (nk_size)buf_size, fmt, args); result = (result >= buf_size) ? -1: result; buf[buf_size-1] = 0; #else result = nk_vsnprintf(buf, buf_size, fmt, args); #endif return result; } #endif NK_API nk_hash nk_murmur_hash(const void * key, int len, nk_hash seed) { /* 32-Bit MurmurHash3: https://code.google.com/p/smhasher/wiki/MurmurHash3*/ #define NK_ROTL(x,r) ((x) << (r) | ((x) >> (32 - r))) nk_uint h1 = seed; nk_uint k1; const nk_byte *data = (const nk_byte*)key; const nk_byte *keyptr = data; nk_byte *k1ptr; const int bsize = sizeof(k1); const int nblocks = len/4; const nk_uint c1 = 0xcc9e2d51; const nk_uint c2 = 0x1b873593; const nk_byte *tail; int i; /* body */ if (!key) return 0; for (i = 0; i < nblocks; ++i, keyptr += bsize) { k1ptr = (nk_byte*)&k1; k1ptr[0] = keyptr[0]; k1ptr[1] = keyptr[1]; k1ptr[2] = keyptr[2]; k1ptr[3] = keyptr[3]; k1 *= c1; k1 = NK_ROTL(k1,15); k1 *= c2; h1 ^= k1; h1 = NK_ROTL(h1,13); h1 = h1*5+0xe6546b64; } /* tail */ tail = (const nk_byte*)(data + nblocks*4); k1 = 0; switch (len & 3) { case 3: k1 ^= (nk_uint)(tail[2] << 16); /* fallthrough */ case 2: k1 ^= (nk_uint)(tail[1] << 8u); /* fallthrough */ case 1: k1 ^= tail[0]; k1 *= c1; k1 = NK_ROTL(k1,15); k1 *= c2; h1 ^= k1; break; default: break; } /* finalization */ h1 ^= (nk_uint)len; /* fmix32 */ h1 ^= h1 >> 16; h1 *= 0x85ebca6b; h1 ^= h1 >> 13; h1 *= 0xc2b2ae35; h1 ^= h1 >> 16; #undef NK_ROTL return h1; } #ifdef NK_INCLUDE_STANDARD_IO NK_LIB char* nk_file_load(const char* path, nk_size* siz, struct nk_allocator *alloc) { char *buf; FILE *fd; long ret; NK_ASSERT(path); NK_ASSERT(siz); NK_ASSERT(alloc); if (!path || !siz || !alloc) return 0; fd = fopen(path, "rb"); if (!fd) return 0; fseek(fd, 0, SEEK_END); ret = ftell(fd); if (ret < 0) { fclose(fd); return 0; } *siz = (nk_size)ret; fseek(fd, 0, SEEK_SET); buf = (char*)alloc->alloc(alloc->userdata,0, *siz); NK_ASSERT(buf); if (!buf) { fclose(fd); return 0; } *siz = (nk_size)fread(buf, 1,*siz, fd); fclose(fd); return buf; } #endif NK_LIB int nk_text_clamp(const struct nk_user_font *font, const char *text, int text_len, float space, int *glyphs, float *text_width, nk_rune *sep_list, int sep_count) { int i = 0; int glyph_len = 0; float last_width = 0; nk_rune unicode = 0; float width = 0; int len = 0; int g = 0; float s; int sep_len = 0; int sep_g = 0; float sep_width = 0; sep_count = NK_MAX(sep_count,0); glyph_len = nk_utf_decode(text, &unicode, text_len); while (glyph_len && (width < space) && (len < text_len)) { len += glyph_len; s = font->width(font->userdata, font->height, text, len); for (i = 0; i < sep_count; ++i) { if (unicode != sep_list[i]) continue; sep_width = last_width = width; sep_g = g+1; sep_len = len; break; } if (i == sep_count){ last_width = sep_width = width; sep_g = g+1; } width = s; glyph_len = nk_utf_decode(&text[len], &unicode, text_len - len); g++; } if (len >= text_len) { *glyphs = g; *text_width = last_width; return len; } else { *glyphs = sep_g; *text_width = sep_width; return (!sep_len) ? len: sep_len; } } NK_LIB struct nk_vec2 nk_text_calculate_text_bounds(const struct nk_user_font *font, const char *begin, int byte_len, float row_height, const char **remaining, struct nk_vec2 *out_offset, int *glyphs, int op) { float line_height = row_height; struct nk_vec2 text_size = nk_vec2(0,0); float line_width = 0.0f; float glyph_width; int glyph_len = 0; nk_rune unicode = 0; int text_len = 0; if (!begin || byte_len <= 0 || !font) return nk_vec2(0,row_height); glyph_len = nk_utf_decode(begin, &unicode, byte_len); if (!glyph_len) return text_size; glyph_width = font->width(font->userdata, font->height, begin, glyph_len); *glyphs = 0; while ((text_len < byte_len) && glyph_len) { if (unicode == '\n') { text_size.x = NK_MAX(text_size.x, line_width); text_size.y += line_height; line_width = 0; *glyphs+=1; if (op == NK_STOP_ON_NEW_LINE) break; text_len++; glyph_len = nk_utf_decode(begin + text_len, &unicode, byte_len-text_len); continue; } if (unicode == '\r') { text_len++; *glyphs+=1; glyph_len = nk_utf_decode(begin + text_len, &unicode, byte_len-text_len); continue; } *glyphs = *glyphs + 1; text_len += glyph_len; line_width += (float)glyph_width; glyph_len = nk_utf_decode(begin + text_len, &unicode, byte_len-text_len); glyph_width = font->width(font->userdata, font->height, begin+text_len, glyph_len); continue; } if (text_size.x < line_width) text_size.x = line_width; if (out_offset) *out_offset = nk_vec2(line_width, text_size.y + line_height); if (line_width > 0 || text_size.y == 0.0f) text_size.y += line_height; if (remaining) *remaining = begin+text_len; return text_size; } /* ============================================================== * * COLOR * * ===============================================================*/ NK_INTERN int nk_parse_hex(const char *p, int length) { int i = 0; int len = 0; while (len < length) { i <<= 4; if (p[len] >= 'a' && p[len] <= 'f') i += ((p[len] - 'a') + 10); else if (p[len] >= 'A' && p[len] <= 'F') i += ((p[len] - 'A') + 10); else i += (p[len] - '0'); len++; } return i; } NK_API struct nk_color nk_rgba(int r, int g, int b, int a) { struct nk_color ret; ret.r = (nk_byte)NK_CLAMP(0, r, 255); ret.g = (nk_byte)NK_CLAMP(0, g, 255); ret.b = (nk_byte)NK_CLAMP(0, b, 255); ret.a = (nk_byte)NK_CLAMP(0, a, 255); return ret; } NK_API struct nk_color nk_rgb_hex(const char *rgb) { struct nk_color col; const char *c = rgb; if (*c == '#') c++; col.r = (nk_byte)nk_parse_hex(c, 2); col.g = (nk_byte)nk_parse_hex(c+2, 2); col.b = (nk_byte)nk_parse_hex(c+4, 2); col.a = 255; return col; } NK_API struct nk_color nk_rgba_hex(const char *rgb) { struct nk_color col; const char *c = rgb; if (*c == '#') c++; col.r = (nk_byte)nk_parse_hex(c, 2); col.g = (nk_byte)nk_parse_hex(c+2, 2); col.b = (nk_byte)nk_parse_hex(c+4, 2); col.a = (nk_byte)nk_parse_hex(c+6, 2); return col; } NK_API void nk_color_hex_rgba(char *output, struct nk_color col) { #define NK_TO_HEX(i) ((i) <= 9 ? '0' + (i): 'A' - 10 + (i)) output[0] = (char)NK_TO_HEX((col.r & 0xF0) >> 4); output[1] = (char)NK_TO_HEX((col.r & 0x0F)); output[2] = (char)NK_TO_HEX((col.g & 0xF0) >> 4); output[3] = (char)NK_TO_HEX((col.g & 0x0F)); output[4] = (char)NK_TO_HEX((col.b & 0xF0) >> 4); output[5] = (char)NK_TO_HEX((col.b & 0x0F)); output[6] = (char)NK_TO_HEX((col.a & 0xF0) >> 4); output[7] = (char)NK_TO_HEX((col.a & 0x0F)); output[8] = '\0'; #undef NK_TO_HEX } NK_API void nk_color_hex_rgb(char *output, struct nk_color col) { #define NK_TO_HEX(i) ((i) <= 9 ? '0' + (i): 'A' - 10 + (i)) output[0] = (char)NK_TO_HEX((col.r & 0xF0) >> 4); output[1] = (char)NK_TO_HEX((col.r & 0x0F)); output[2] = (char)NK_TO_HEX((col.g & 0xF0) >> 4); output[3] = (char)NK_TO_HEX((col.g & 0x0F)); output[4] = (char)NK_TO_HEX((col.b & 0xF0) >> 4); output[5] = (char)NK_TO_HEX((col.b & 0x0F)); output[6] = '\0'; #undef NK_TO_HEX } NK_API struct nk_color nk_rgba_iv(const int *c) { return nk_rgba(c[0], c[1], c[2], c[3]); } NK_API struct nk_color nk_rgba_bv(const nk_byte *c) { return nk_rgba(c[0], c[1], c[2], c[3]); } NK_API struct nk_color nk_rgb(int r, int g, int b) { struct nk_color ret; ret.r = (nk_byte)NK_CLAMP(0, r, 255); ret.g = (nk_byte)NK_CLAMP(0, g, 255); ret.b = (nk_byte)NK_CLAMP(0, b, 255); ret.a = (nk_byte)255; return ret; } NK_API struct nk_color nk_rgb_iv(const int *c) { return nk_rgb(c[0], c[1], c[2]); } NK_API struct nk_color nk_rgb_bv(const nk_byte* c) { return nk_rgb(c[0], c[1], c[2]); } NK_API struct nk_color nk_rgba_u32(nk_uint in) { struct nk_color ret; ret.r = (in & 0xFF); ret.g = ((in >> 8) & 0xFF); ret.b = ((in >> 16) & 0xFF); ret.a = (nk_byte)((in >> 24) & 0xFF); return ret; } NK_API struct nk_color nk_rgba_f(float r, float g, float b, float a) { struct nk_color ret; ret.r = (nk_byte)(NK_SATURATE(r) * 255.0f); ret.g = (nk_byte)(NK_SATURATE(g) * 255.0f); ret.b = (nk_byte)(NK_SATURATE(b) * 255.0f); ret.a = (nk_byte)(NK_SATURATE(a) * 255.0f); return ret; } NK_API struct nk_color nk_rgba_fv(const float *c) { return nk_rgba_f(c[0], c[1], c[2], c[3]); } NK_API struct nk_color nk_rgba_cf(struct nk_colorf c) { return nk_rgba_f(c.r, c.g, c.b, c.a); } NK_API struct nk_color nk_rgb_f(float r, float g, float b) { struct nk_color ret; ret.r = (nk_byte)(NK_SATURATE(r) * 255.0f); ret.g = (nk_byte)(NK_SATURATE(g) * 255.0f); ret.b = (nk_byte)(NK_SATURATE(b) * 255.0f); ret.a = 255; return ret; } NK_API struct nk_color nk_rgb_fv(const float *c) { return nk_rgb_f(c[0], c[1], c[2]); } NK_API struct nk_color nk_rgb_cf(struct nk_colorf c) { return nk_rgb_f(c.r, c.g, c.b); } NK_API struct nk_color nk_hsv(int h, int s, int v) { return nk_hsva(h, s, v, 255); } NK_API struct nk_color nk_hsv_iv(const int *c) { return nk_hsv(c[0], c[1], c[2]); } NK_API struct nk_color nk_hsv_bv(const nk_byte *c) { return nk_hsv(c[0], c[1], c[2]); } NK_API struct nk_color nk_hsv_f(float h, float s, float v) { return nk_hsva_f(h, s, v, 1.0f); } NK_API struct nk_color nk_hsv_fv(const float *c) { return nk_hsv_f(c[0], c[1], c[2]); } NK_API struct nk_color nk_hsva(int h, int s, int v, int a) { float hf = ((float)NK_CLAMP(0, h, 255)) / 255.0f; float sf = ((float)NK_CLAMP(0, s, 255)) / 255.0f; float vf = ((float)NK_CLAMP(0, v, 255)) / 255.0f; float af = ((float)NK_CLAMP(0, a, 255)) / 255.0f; return nk_hsva_f(hf, sf, vf, af); } NK_API struct nk_color nk_hsva_iv(const int *c) { return nk_hsva(c[0], c[1], c[2], c[3]); } NK_API struct nk_color nk_hsva_bv(const nk_byte *c) { return nk_hsva(c[0], c[1], c[2], c[3]); } NK_API struct nk_colorf nk_hsva_colorf(float h, float s, float v, float a) { int i; float p, q, t, f; struct nk_colorf out = {0,0,0,0}; if (s <= 0.0f) { out.r = v; out.g = v; out.b = v; out.a = a; return out; } h = h / (60.0f/360.0f); i = (int)h; f = h - (float)i; p = v * (1.0f - s); q = v * (1.0f - (s * f)); t = v * (1.0f - s * (1.0f - f)); switch (i) { case 0: default: out.r = v; out.g = t; out.b = p; break; case 1: out.r = q; out.g = v; out.b = p; break; case 2: out.r = p; out.g = v; out.b = t; break; case 3: out.r = p; out.g = q; out.b = v; break; case 4: out.r = t; out.g = p; out.b = v; break; case 5: out.r = v; out.g = p; out.b = q; break;} out.a = a; return out; } NK_API struct nk_colorf nk_hsva_colorfv(float *c) { return nk_hsva_colorf(c[0], c[1], c[2], c[3]); } NK_API struct nk_color nk_hsva_f(float h, float s, float v, float a) { struct nk_colorf c = nk_hsva_colorf(h, s, v, a); return nk_rgba_f(c.r, c.g, c.b, c.a); } NK_API struct nk_color nk_hsva_fv(const float *c) { return nk_hsva_f(c[0], c[1], c[2], c[3]); } NK_API nk_uint nk_color_u32(struct nk_color in) { nk_uint out = (nk_uint)in.r; out |= ((nk_uint)in.g << 8); out |= ((nk_uint)in.b << 16); out |= ((nk_uint)in.a << 24); return out; } NK_API void nk_color_f(float *r, float *g, float *b, float *a, struct nk_color in) { NK_STORAGE const float s = 1.0f/255.0f; *r = (float)in.r * s; *g = (float)in.g * s; *b = (float)in.b * s; *a = (float)in.a * s; } NK_API void nk_color_fv(float *c, struct nk_color in) { nk_color_f(&c[0], &c[1], &c[2], &c[3], in); } NK_API struct nk_colorf nk_color_cf(struct nk_color in) { struct nk_colorf o; nk_color_f(&o.r, &o.g, &o.b, &o.a, in); return o; } NK_API void nk_color_d(double *r, double *g, double *b, double *a, struct nk_color in) { NK_STORAGE const double s = 1.0/255.0; *r = (double)in.r * s; *g = (double)in.g * s; *b = (double)in.b * s; *a = (double)in.a * s; } NK_API void nk_color_dv(double *c, struct nk_color in) { nk_color_d(&c[0], &c[1], &c[2], &c[3], in); } NK_API void nk_color_hsv_f(float *out_h, float *out_s, float *out_v, struct nk_color in) { float a; nk_color_hsva_f(out_h, out_s, out_v, &a, in); } NK_API void nk_color_hsv_fv(float *out, struct nk_color in) { float a; nk_color_hsva_f(&out[0], &out[1], &out[2], &a, in); } NK_API void nk_colorf_hsva_f(float *out_h, float *out_s, float *out_v, float *out_a, struct nk_colorf in) { float chroma; float K = 0.0f; if (in.g < in.b) { const float t = in.g; in.g = in.b; in.b = t; K = -1.f; } if (in.r < in.g) { const float t = in.r; in.r = in.g; in.g = t; K = -2.f/6.0f - K; } chroma = in.r - ((in.g < in.b) ? in.g: in.b); *out_h = NK_ABS(K + (in.g - in.b)/(6.0f * chroma + 1e-20f)); *out_s = chroma / (in.r + 1e-20f); *out_v = in.r; *out_a = in.a; } NK_API void nk_colorf_hsva_fv(float *hsva, struct nk_colorf in) { nk_colorf_hsva_f(&hsva[0], &hsva[1], &hsva[2], &hsva[3], in); } NK_API void nk_color_hsva_f(float *out_h, float *out_s, float *out_v, float *out_a, struct nk_color in) { struct nk_colorf col; nk_color_f(&col.r,&col.g,&col.b,&col.a, in); nk_colorf_hsva_f(out_h, out_s, out_v, out_a, col); } NK_API void nk_color_hsva_fv(float *out, struct nk_color in) { nk_color_hsva_f(&out[0], &out[1], &out[2], &out[3], in); } NK_API void nk_color_hsva_i(int *out_h, int *out_s, int *out_v, int *out_a, struct nk_color in) { float h,s,v,a; nk_color_hsva_f(&h, &s, &v, &a, in); *out_h = (nk_byte)(h * 255.0f); *out_s = (nk_byte)(s * 255.0f); *out_v = (nk_byte)(v * 255.0f); *out_a = (nk_byte)(a * 255.0f); } NK_API void nk_color_hsva_iv(int *out, struct nk_color in) { nk_color_hsva_i(&out[0], &out[1], &out[2], &out[3], in); } NK_API void nk_color_hsva_bv(nk_byte *out, struct nk_color in) { int tmp[4]; nk_color_hsva_i(&tmp[0], &tmp[1], &tmp[2], &tmp[3], in); out[0] = (nk_byte)tmp[0]; out[1] = (nk_byte)tmp[1]; out[2] = (nk_byte)tmp[2]; out[3] = (nk_byte)tmp[3]; } NK_API void nk_color_hsva_b(nk_byte *h, nk_byte *s, nk_byte *v, nk_byte *a, struct nk_color in) { int tmp[4]; nk_color_hsva_i(&tmp[0], &tmp[1], &tmp[2], &tmp[3], in); *h = (nk_byte)tmp[0]; *s = (nk_byte)tmp[1]; *v = (nk_byte)tmp[2]; *a = (nk_byte)tmp[3]; } NK_API void nk_color_hsv_i(int *out_h, int *out_s, int *out_v, struct nk_color in) { int a; nk_color_hsva_i(out_h, out_s, out_v, &a, in); } NK_API void nk_color_hsv_b(nk_byte *out_h, nk_byte *out_s, nk_byte *out_v, struct nk_color in) { int tmp[4]; nk_color_hsva_i(&tmp[0], &tmp[1], &tmp[2], &tmp[3], in); *out_h = (nk_byte)tmp[0]; *out_s = (nk_byte)tmp[1]; *out_v = (nk_byte)tmp[2]; } NK_API void nk_color_hsv_iv(int *out, struct nk_color in) { nk_color_hsv_i(&out[0], &out[1], &out[2], in); } NK_API void nk_color_hsv_bv(nk_byte *out, struct nk_color in) { int tmp[4]; nk_color_hsv_i(&tmp[0], &tmp[1], &tmp[2], in); out[0] = (nk_byte)tmp[0]; out[1] = (nk_byte)tmp[1]; out[2] = (nk_byte)tmp[2]; } /* =============================================================== * * UTF-8 * * ===============================================================*/ NK_GLOBAL const nk_byte nk_utfbyte[NK_UTF_SIZE+1] = {0x80, 0, 0xC0, 0xE0, 0xF0}; NK_GLOBAL const nk_byte nk_utfmask[NK_UTF_SIZE+1] = {0xC0, 0x80, 0xE0, 0xF0, 0xF8}; NK_GLOBAL const nk_uint nk_utfmin[NK_UTF_SIZE+1] = {0, 0, 0x80, 0x800, 0x10000}; NK_GLOBAL const nk_uint nk_utfmax[NK_UTF_SIZE+1] = {0x10FFFF, 0x7F, 0x7FF, 0xFFFF, 0x10FFFF}; NK_INTERN int nk_utf_validate(nk_rune *u, int i) { NK_ASSERT(u); if (!u) return 0; if (!NK_BETWEEN(*u, nk_utfmin[i], nk_utfmax[i]) || NK_BETWEEN(*u, 0xD800, 0xDFFF)) *u = NK_UTF_INVALID; for (i = 1; *u > nk_utfmax[i]; ++i); return i; } NK_INTERN nk_rune nk_utf_decode_byte(char c, int *i) { NK_ASSERT(i); if (!i) return 0; for(*i = 0; *i < (int)NK_LEN(nk_utfmask); ++(*i)) { if (((nk_byte)c & nk_utfmask[*i]) == nk_utfbyte[*i]) return (nk_byte)(c & ~nk_utfmask[*i]); } return 0; } NK_API int nk_utf_decode(const char *c, nk_rune *u, int clen) { int i, j, len, type=0; nk_rune udecoded; NK_ASSERT(c); NK_ASSERT(u); if (!c || !u) return 0; if (!clen) return 0; *u = NK_UTF_INVALID; udecoded = nk_utf_decode_byte(c[0], &len); if (!NK_BETWEEN(len, 1, NK_UTF_SIZE)) return 1; for (i = 1, j = 1; i < clen && j < len; ++i, ++j) { udecoded = (udecoded << 6) | nk_utf_decode_byte(c[i], &type); if (type != 0) return j; } if (j < len) return 0; *u = udecoded; nk_utf_validate(u, len); return len; } NK_INTERN char nk_utf_encode_byte(nk_rune u, int i) { return (char)((nk_utfbyte[i]) | ((nk_byte)u & ~nk_utfmask[i])); } NK_API int nk_utf_encode(nk_rune u, char *c, int clen) { int len, i; len = nk_utf_validate(&u, 0); if (clen < len || !len || len > NK_UTF_SIZE) return 0; for (i = len - 1; i != 0; --i) { c[i] = nk_utf_encode_byte(u, 0); u >>= 6; } c[0] = nk_utf_encode_byte(u, len); return len; } NK_API int nk_utf_len(const char *str, int len) { const char *text; int glyphs = 0; int text_len; int glyph_len; int src_len = 0; nk_rune unicode; NK_ASSERT(str); if (!str || !len) return 0; text = str; text_len = len; glyph_len = nk_utf_decode(text, &unicode, text_len); while (glyph_len && src_len < len) { glyphs++; src_len = src_len + glyph_len; glyph_len = nk_utf_decode(text + src_len, &unicode, text_len - src_len); } return glyphs; } NK_API const char* nk_utf_at(const char *buffer, int length, int index, nk_rune *unicode, int *len) { int i = 0; int src_len = 0; int glyph_len = 0; const char *text; int text_len; NK_ASSERT(buffer); NK_ASSERT(unicode); NK_ASSERT(len); if (!buffer || !unicode || !len) return 0; if (index < 0) { *unicode = NK_UTF_INVALID; *len = 0; return 0; } text = buffer; text_len = length; glyph_len = nk_utf_decode(text, unicode, text_len); while (glyph_len) { if (i == index) { *len = glyph_len; break; } i++; src_len = src_len + glyph_len; glyph_len = nk_utf_decode(text + src_len, unicode, text_len - src_len); } if (i != index) return 0; return buffer + src_len; } /* ============================================================== * * BUFFER * * ===============================================================*/ #ifdef NK_INCLUDE_DEFAULT_ALLOCATOR NK_LIB void* nk_malloc(nk_handle unused, void *old,nk_size size) { NK_UNUSED(unused); NK_UNUSED(old); return malloc(size); } NK_LIB void nk_mfree(nk_handle unused, void *ptr) { NK_UNUSED(unused); free(ptr); } NK_API void nk_buffer_init_default(struct nk_buffer *buffer) { struct nk_allocator alloc; alloc.userdata.ptr = 0; alloc.alloc = nk_malloc; alloc.free = nk_mfree; nk_buffer_init(buffer, &alloc, NK_BUFFER_DEFAULT_INITIAL_SIZE); } #endif NK_API void nk_buffer_init(struct nk_buffer *b, const struct nk_allocator *a, nk_size initial_size) { NK_ASSERT(b); NK_ASSERT(a); NK_ASSERT(initial_size); if (!b || !a || !initial_size) return; nk_zero(b, sizeof(*b)); b->type = NK_BUFFER_DYNAMIC; b->memory.ptr = a->alloc(a->userdata,0, initial_size); b->memory.size = initial_size; b->size = initial_size; b->grow_factor = 2.0f; b->pool = *a; } NK_API void nk_buffer_init_fixed(struct nk_buffer *b, void *m, nk_size size) { NK_ASSERT(b); NK_ASSERT(m); NK_ASSERT(size); if (!b || !m || !size) return; nk_zero(b, sizeof(*b)); b->type = NK_BUFFER_FIXED; b->memory.ptr = m; b->memory.size = size; b->size = size; } NK_LIB void* nk_buffer_align(void *unaligned, nk_size align, nk_size *alignment, enum nk_buffer_allocation_type type) { void *memory = 0; switch (type) { default: case NK_BUFFER_MAX: case NK_BUFFER_FRONT: if (align) { memory = NK_ALIGN_PTR(unaligned, align); *alignment = (nk_size)((nk_byte*)memory - (nk_byte*)unaligned); } else { memory = unaligned; *alignment = 0; } break; case NK_BUFFER_BACK: if (align) { memory = NK_ALIGN_PTR_BACK(unaligned, align); *alignment = (nk_size)((nk_byte*)unaligned - (nk_byte*)memory); } else { memory = unaligned; *alignment = 0; } break; } return memory; } NK_LIB void* nk_buffer_realloc(struct nk_buffer *b, nk_size capacity, nk_size *size) { void *temp; nk_size buffer_size; NK_ASSERT(b); NK_ASSERT(size); if (!b || !size || !b->pool.alloc || !b->pool.free) return 0; buffer_size = b->memory.size; temp = b->pool.alloc(b->pool.userdata, b->memory.ptr, capacity); NK_ASSERT(temp); if (!temp) return 0; *size = capacity; if (temp != b->memory.ptr) { NK_MEMCPY(temp, b->memory.ptr, buffer_size); b->pool.free(b->pool.userdata, b->memory.ptr); } if (b->size == buffer_size) { /* no back buffer so just set correct size */ b->size = capacity; return temp; } else { /* copy back buffer to the end of the new buffer */ void *dst, *src; nk_size back_size; back_size = buffer_size - b->size; dst = nk_ptr_add(void, temp, capacity - back_size); src = nk_ptr_add(void, temp, b->size); NK_MEMCPY(dst, src, back_size); b->size = capacity - back_size; } return temp; } NK_LIB void* nk_buffer_alloc(struct nk_buffer *b, enum nk_buffer_allocation_type type, nk_size size, nk_size align) { int full; nk_size alignment; void *unaligned; void *memory; NK_ASSERT(b); NK_ASSERT(size); if (!b || !size) return 0; b->needed += size; /* calculate total size with needed alignment + size */ if (type == NK_BUFFER_FRONT) unaligned = nk_ptr_add(void, b->memory.ptr, b->allocated); else unaligned = nk_ptr_add(void, b->memory.ptr, b->size - size); memory = nk_buffer_align(unaligned, align, &alignment, type); /* check if buffer has enough memory*/ if (type == NK_BUFFER_FRONT) full = ((b->allocated + size + alignment) > b->size); else full = ((b->size - NK_MIN(b->size,(size + alignment))) <= b->allocated); if (full) { nk_size capacity; if (b->type != NK_BUFFER_DYNAMIC) return 0; NK_ASSERT(b->pool.alloc && b->pool.free); if (b->type != NK_BUFFER_DYNAMIC || !b->pool.alloc || !b->pool.free) return 0; /* buffer is full so allocate bigger buffer if dynamic */ capacity = (nk_size)((float)b->memory.size * b->grow_factor); capacity = NK_MAX(capacity, nk_round_up_pow2((nk_uint)(b->allocated + size))); b->memory.ptr = nk_buffer_realloc(b, capacity, &b->memory.size); if (!b->memory.ptr) return 0; /* align newly allocated pointer */ if (type == NK_BUFFER_FRONT) unaligned = nk_ptr_add(void, b->memory.ptr, b->allocated); else unaligned = nk_ptr_add(void, b->memory.ptr, b->size - size); memory = nk_buffer_align(unaligned, align, &alignment, type); } if (type == NK_BUFFER_FRONT) b->allocated += size + alignment; else b->size -= (size + alignment); b->needed += alignment; b->calls++; return memory; } NK_API void nk_buffer_push(struct nk_buffer *b, enum nk_buffer_allocation_type type, const void *memory, nk_size size, nk_size align) { void *mem = nk_buffer_alloc(b, type, size, align); if (!mem) return; NK_MEMCPY(mem, memory, size); } NK_API void nk_buffer_mark(struct nk_buffer *buffer, enum nk_buffer_allocation_type type) { NK_ASSERT(buffer); if (!buffer) return; buffer->marker[type].active = nk_true; if (type == NK_BUFFER_BACK) buffer->marker[type].offset = buffer->size; else buffer->marker[type].offset = buffer->allocated; } NK_API void nk_buffer_reset(struct nk_buffer *buffer, enum nk_buffer_allocation_type type) { NK_ASSERT(buffer); if (!buffer) return; if (type == NK_BUFFER_BACK) { /* reset back buffer either back to marker or empty */ buffer->needed -= (buffer->memory.size - buffer->marker[type].offset); if (buffer->marker[type].active) buffer->size = buffer->marker[type].offset; else buffer->size = buffer->memory.size; buffer->marker[type].active = nk_false; } else { /* reset front buffer either back to back marker or empty */ buffer->needed -= (buffer->allocated - buffer->marker[type].offset); if (buffer->marker[type].active) buffer->allocated = buffer->marker[type].offset; else buffer->allocated = 0; buffer->marker[type].active = nk_false; } } NK_API void nk_buffer_clear(struct nk_buffer *b) { NK_ASSERT(b); if (!b) return; b->allocated = 0; b->size = b->memory.size; b->calls = 0; b->needed = 0; } NK_API void nk_buffer_free(struct nk_buffer *b) { NK_ASSERT(b); if (!b || !b->memory.ptr) return; if (b->type == NK_BUFFER_FIXED) return; if (!b->pool.free) return; NK_ASSERT(b->pool.free); b->pool.free(b->pool.userdata, b->memory.ptr); } NK_API void nk_buffer_info(struct nk_memory_status *s, struct nk_buffer *b) { NK_ASSERT(b); NK_ASSERT(s); if (!s || !b) return; s->allocated = b->allocated; s->size = b->memory.size; s->needed = b->needed; s->memory = b->memory.ptr; s->calls = b->calls; } NK_API void* nk_buffer_memory(struct nk_buffer *buffer) { NK_ASSERT(buffer); if (!buffer) return 0; return buffer->memory.ptr; } NK_API const void* nk_buffer_memory_const(const struct nk_buffer *buffer) { NK_ASSERT(buffer); if (!buffer) return 0; return buffer->memory.ptr; } NK_API nk_size nk_buffer_total(struct nk_buffer *buffer) { NK_ASSERT(buffer); if (!buffer) return 0; return buffer->memory.size; } /* =============================================================== * * STRING * * ===============================================================*/ #ifdef NK_INCLUDE_DEFAULT_ALLOCATOR NK_API void nk_str_init_default(struct nk_str *str) { struct nk_allocator alloc; alloc.userdata.ptr = 0; alloc.alloc = nk_malloc; alloc.free = nk_mfree; nk_buffer_init(&str->buffer, &alloc, 32); str->len = 0; } #endif NK_API void nk_str_init(struct nk_str *str, const struct nk_allocator *alloc, nk_size size) { nk_buffer_init(&str->buffer, alloc, size); str->len = 0; } NK_API void nk_str_init_fixed(struct nk_str *str, void *memory, nk_size size) { nk_buffer_init_fixed(&str->buffer, memory, size); str->len = 0; } NK_API int nk_str_append_text_char(struct nk_str *s, const char *str, int len) { char *mem; NK_ASSERT(s); NK_ASSERT(str); if (!s || !str || !len) return 0; mem = (char*)nk_buffer_alloc(&s->buffer, NK_BUFFER_FRONT, (nk_size)len * sizeof(char), 0); if (!mem) return 0; NK_MEMCPY(mem, str, (nk_size)len * sizeof(char)); s->len += nk_utf_len(str, len); return len; } NK_API int nk_str_append_str_char(struct nk_str *s, const char *str) { return nk_str_append_text_char(s, str, nk_strlen(str)); } NK_API int nk_str_append_text_utf8(struct nk_str *str, const char *text, int len) { int i = 0; int byte_len = 0; nk_rune unicode; if (!str || !text || !len) return 0; for (i = 0; i < len; ++i) byte_len += nk_utf_decode(text+byte_len, &unicode, 4); nk_str_append_text_char(str, text, byte_len); return len; } NK_API int nk_str_append_str_utf8(struct nk_str *str, const char *text) { int runes = 0; int byte_len = 0; int num_runes = 0; int glyph_len = 0; nk_rune unicode; if (!str || !text) return 0; glyph_len = byte_len = nk_utf_decode(text+byte_len, &unicode, 4); while (unicode != '\0' && glyph_len) { glyph_len = nk_utf_decode(text+byte_len, &unicode, 4); byte_len += glyph_len; num_runes++; } nk_str_append_text_char(str, text, byte_len); return runes; } NK_API int nk_str_append_text_runes(struct nk_str *str, const nk_rune *text, int len) { int i = 0; int byte_len = 0; nk_glyph glyph; NK_ASSERT(str); if (!str || !text || !len) return 0; for (i = 0; i < len; ++i) { byte_len = nk_utf_encode(text[i], glyph, NK_UTF_SIZE); if (!byte_len) break; nk_str_append_text_char(str, glyph, byte_len); } return len; } NK_API int nk_str_append_str_runes(struct nk_str *str, const nk_rune *runes) { int i = 0; nk_glyph glyph; int byte_len; NK_ASSERT(str); if (!str || !runes) return 0; while (runes[i] != '\0') { byte_len = nk_utf_encode(runes[i], glyph, NK_UTF_SIZE); nk_str_append_text_char(str, glyph, byte_len); i++; } return i; } NK_API int nk_str_insert_at_char(struct nk_str *s, int pos, const char *str, int len) { int i; void *mem; char *src; char *dst; int copylen; NK_ASSERT(s); NK_ASSERT(str); NK_ASSERT(len >= 0); if (!s || !str || !len || (nk_size)pos > s->buffer.allocated) return 0; if ((s->buffer.allocated + (nk_size)len >= s->buffer.memory.size) && (s->buffer.type == NK_BUFFER_FIXED)) return 0; copylen = (int)s->buffer.allocated - pos; if (!copylen) { nk_str_append_text_char(s, str, len); return 1; } mem = nk_buffer_alloc(&s->buffer, NK_BUFFER_FRONT, (nk_size)len * sizeof(char), 0); if (!mem) return 0; /* memmove */ NK_ASSERT(((int)pos + (int)len + ((int)copylen - 1)) >= 0); NK_ASSERT(((int)pos + ((int)copylen - 1)) >= 0); dst = nk_ptr_add(char, s->buffer.memory.ptr, pos + len + (copylen - 1)); src = nk_ptr_add(char, s->buffer.memory.ptr, pos + (copylen-1)); for (i = 0; i < copylen; ++i) *dst-- = *src--; mem = nk_ptr_add(void, s->buffer.memory.ptr, pos); NK_MEMCPY(mem, str, (nk_size)len * sizeof(char)); s->len = nk_utf_len((char *)s->buffer.memory.ptr, (int)s->buffer.allocated); return 1; } NK_API int nk_str_insert_at_rune(struct nk_str *str, int pos, const char *cstr, int len) { int glyph_len; nk_rune unicode; const char *begin; const char *buffer; NK_ASSERT(str); NK_ASSERT(cstr); NK_ASSERT(len); if (!str || !cstr || !len) return 0; begin = nk_str_at_rune(str, pos, &unicode, &glyph_len); if (!str->len) return nk_str_append_text_char(str, cstr, len); buffer = nk_str_get_const(str); if (!begin) return 0; return nk_str_insert_at_char(str, (int)(begin - buffer), cstr, len); } NK_API int nk_str_insert_text_char(struct nk_str *str, int pos, const char *text, int len) { return nk_str_insert_text_utf8(str, pos, text, len); } NK_API int nk_str_insert_str_char(struct nk_str *str, int pos, const char *text) { return nk_str_insert_text_utf8(str, pos, text, nk_strlen(text)); } NK_API int nk_str_insert_text_utf8(struct nk_str *str, int pos, const char *text, int len) { int i = 0; int byte_len = 0; nk_rune unicode; NK_ASSERT(str); NK_ASSERT(text); if (!str || !text || !len) return 0; for (i = 0; i < len; ++i) byte_len += nk_utf_decode(text+byte_len, &unicode, 4); nk_str_insert_at_rune(str, pos, text, byte_len); return len; } NK_API int nk_str_insert_str_utf8(struct nk_str *str, int pos, const char *text) { int runes = 0; int byte_len = 0; int num_runes = 0; int glyph_len = 0; nk_rune unicode; if (!str || !text) return 0; glyph_len = byte_len = nk_utf_decode(text+byte_len, &unicode, 4); while (unicode != '\0' && glyph_len) { glyph_len = nk_utf_decode(text+byte_len, &unicode, 4); byte_len += glyph_len; num_runes++; } nk_str_insert_at_rune(str, pos, text, byte_len); return runes; } NK_API int nk_str_insert_text_runes(struct nk_str *str, int pos, const nk_rune *runes, int len) { int i = 0; int byte_len = 0; nk_glyph glyph; NK_ASSERT(str); if (!str || !runes || !len) return 0; for (i = 0; i < len; ++i) { byte_len = nk_utf_encode(runes[i], glyph, NK_UTF_SIZE); if (!byte_len) break; nk_str_insert_at_rune(str, pos+i, glyph, byte_len); } return len; } NK_API int nk_str_insert_str_runes(struct nk_str *str, int pos, const nk_rune *runes) { int i = 0; nk_glyph glyph; int byte_len; NK_ASSERT(str); if (!str || !runes) return 0; while (runes[i] != '\0') { byte_len = nk_utf_encode(runes[i], glyph, NK_UTF_SIZE); nk_str_insert_at_rune(str, pos+i, glyph, byte_len); i++; } return i; } NK_API void nk_str_remove_chars(struct nk_str *s, int len) { NK_ASSERT(s); NK_ASSERT(len >= 0); if (!s || len < 0 || (nk_size)len > s->buffer.allocated) return; NK_ASSERT(((int)s->buffer.allocated - (int)len) >= 0); s->buffer.allocated -= (nk_size)len; s->len = nk_utf_len((char *)s->buffer.memory.ptr, (int)s->buffer.allocated); } NK_API void nk_str_remove_runes(struct nk_str *str, int len) { int index; const char *begin; const char *end; nk_rune unicode; NK_ASSERT(str); NK_ASSERT(len >= 0); if (!str || len < 0) return; if (len >= str->len) { str->len = 0; return; } index = str->len - len; begin = nk_str_at_rune(str, index, &unicode, &len); end = (const char*)str->buffer.memory.ptr + str->buffer.allocated; nk_str_remove_chars(str, (int)(end-begin)+1); } NK_API void nk_str_delete_chars(struct nk_str *s, int pos, int len) { NK_ASSERT(s); if (!s || !len || (nk_size)pos > s->buffer.allocated || (nk_size)(pos + len) > s->buffer.allocated) return; if ((nk_size)(pos + len) < s->buffer.allocated) { /* memmove */ char *dst = nk_ptr_add(char, s->buffer.memory.ptr, pos); char *src = nk_ptr_add(char, s->buffer.memory.ptr, pos + len); NK_MEMCPY(dst, src, s->buffer.allocated - (nk_size)(pos + len)); NK_ASSERT(((int)s->buffer.allocated - (int)len) >= 0); s->buffer.allocated -= (nk_size)len; } else nk_str_remove_chars(s, len); s->len = nk_utf_len((char *)s->buffer.memory.ptr, (int)s->buffer.allocated); } NK_API void nk_str_delete_runes(struct nk_str *s, int pos, int len) { char *temp; nk_rune unicode; char *begin; char *end; int unused; NK_ASSERT(s); NK_ASSERT(s->len >= pos + len); if (s->len < pos + len) len = NK_CLAMP(0, (s->len - pos), s->len); if (!len) return; temp = (char *)s->buffer.memory.ptr; begin = nk_str_at_rune(s, pos, &unicode, &unused); if (!begin) return; s->buffer.memory.ptr = begin; end = nk_str_at_rune(s, len, &unicode, &unused); s->buffer.memory.ptr = temp; if (!end) return; nk_str_delete_chars(s, (int)(begin - temp), (int)(end - begin)); } NK_API char* nk_str_at_char(struct nk_str *s, int pos) { NK_ASSERT(s); if (!s || pos > (int)s->buffer.allocated) return 0; return nk_ptr_add(char, s->buffer.memory.ptr, pos); } NK_API char* nk_str_at_rune(struct nk_str *str, int pos, nk_rune *unicode, int *len) { int i = 0; int src_len = 0; int glyph_len = 0; char *text; int text_len; NK_ASSERT(str); NK_ASSERT(unicode); NK_ASSERT(len); if (!str || !unicode || !len) return 0; if (pos < 0) { *unicode = 0; *len = 0; return 0; } text = (char*)str->buffer.memory.ptr; text_len = (int)str->buffer.allocated; glyph_len = nk_utf_decode(text, unicode, text_len); while (glyph_len) { if (i == pos) { *len = glyph_len; break; } i++; src_len = src_len + glyph_len; glyph_len = nk_utf_decode(text + src_len, unicode, text_len - src_len); } if (i != pos) return 0; return text + src_len; } NK_API const char* nk_str_at_char_const(const struct nk_str *s, int pos) { NK_ASSERT(s); if (!s || pos > (int)s->buffer.allocated) return 0; return nk_ptr_add(char, s->buffer.memory.ptr, pos); } NK_API const char* nk_str_at_const(const struct nk_str *str, int pos, nk_rune *unicode, int *len) { int i = 0; int src_len = 0; int glyph_len = 0; char *text; int text_len; NK_ASSERT(str); NK_ASSERT(unicode); NK_ASSERT(len); if (!str || !unicode || !len) return 0; if (pos < 0) { *unicode = 0; *len = 0; return 0; } text = (char*)str->buffer.memory.ptr; text_len = (int)str->buffer.allocated; glyph_len = nk_utf_decode(text, unicode, text_len); while (glyph_len) { if (i == pos) { *len = glyph_len; break; } i++; src_len = src_len + glyph_len; glyph_len = nk_utf_decode(text + src_len, unicode, text_len - src_len); } if (i != pos) return 0; return text + src_len; } NK_API nk_rune nk_str_rune_at(const struct nk_str *str, int pos) { int len; nk_rune unicode = 0; nk_str_at_const(str, pos, &unicode, &len); return unicode; } NK_API char* nk_str_get(struct nk_str *s) { NK_ASSERT(s); if (!s || !s->len || !s->buffer.allocated) return 0; return (char*)s->buffer.memory.ptr; } NK_API const char* nk_str_get_const(const struct nk_str *s) { NK_ASSERT(s); if (!s || !s->len || !s->buffer.allocated) return 0; return (const char*)s->buffer.memory.ptr; } NK_API int nk_str_len(struct nk_str *s) { NK_ASSERT(s); if (!s || !s->len || !s->buffer.allocated) return 0; return s->len; } NK_API int nk_str_len_char(struct nk_str *s) { NK_ASSERT(s); if (!s || !s->len || !s->buffer.allocated) return 0; return (int)s->buffer.allocated; } NK_API void nk_str_clear(struct nk_str *str) { NK_ASSERT(str); nk_buffer_clear(&str->buffer); str->len = 0; } NK_API void nk_str_free(struct nk_str *str) { NK_ASSERT(str); nk_buffer_free(&str->buffer); str->len = 0; } /* ============================================================== * * DRAW * * ===============================================================*/ NK_LIB void nk_command_buffer_init(struct nk_command_buffer *cb, struct nk_buffer *b, enum nk_command_clipping clip) { NK_ASSERT(cb); NK_ASSERT(b); if (!cb || !b) return; cb->base = b; cb->use_clipping = (int)clip; cb->begin = b->allocated; cb->end = b->allocated; cb->last = b->allocated; } NK_LIB void nk_command_buffer_reset(struct nk_command_buffer *b) { NK_ASSERT(b); if (!b) return; b->begin = 0; b->end = 0; b->last = 0; b->clip = nk_null_rect; #ifdef NK_INCLUDE_COMMAND_USERDATA b->userdata.ptr = 0; #endif } NK_LIB void* nk_command_buffer_push(struct nk_command_buffer* b, enum nk_command_type t, nk_size size) { NK_STORAGE const nk_size align = NK_ALIGNOF(struct nk_command); struct nk_command *cmd; nk_size alignment; void *unaligned; void *memory; NK_ASSERT(b); NK_ASSERT(b->base); if (!b) return 0; cmd = (struct nk_command*)nk_buffer_alloc(b->base,NK_BUFFER_FRONT,size,align); if (!cmd) return 0; /* make sure the offset to the next command is aligned */ b->last = (nk_size)((nk_byte*)cmd - (nk_byte*)b->base->memory.ptr); unaligned = (nk_byte*)cmd + size; memory = NK_ALIGN_PTR(unaligned, align); alignment = (nk_size)((nk_byte*)memory - (nk_byte*)unaligned); #ifdef NK_ZERO_COMMAND_MEMORY NK_MEMSET(cmd, 0, size + alignment); #endif cmd->type = t; cmd->next = b->base->allocated + alignment; #ifdef NK_INCLUDE_COMMAND_USERDATA cmd->userdata = b->userdata; #endif b->end = cmd->next; return cmd; } NK_API void nk_push_scissor(struct nk_command_buffer *b, struct nk_rect r) { struct nk_command_scissor *cmd; NK_ASSERT(b); if (!b) return; b->clip.x = r.x; b->clip.y = r.y; b->clip.w = r.w; b->clip.h = r.h; cmd = (struct nk_command_scissor*) nk_command_buffer_push(b, NK_COMMAND_SCISSOR, sizeof(*cmd)); if (!cmd) return; cmd->x = (short)r.x; cmd->y = (short)r.y; cmd->w = (unsigned short)NK_MAX(0, r.w); cmd->h = (unsigned short)NK_MAX(0, r.h); } NK_API void nk_stroke_line(struct nk_command_buffer *b, float x0, float y0, float x1, float y1, float line_thickness, struct nk_color c) { struct nk_command_line *cmd; NK_ASSERT(b); if (!b || line_thickness <= 0) return; cmd = (struct nk_command_line*) nk_command_buffer_push(b, NK_COMMAND_LINE, sizeof(*cmd)); if (!cmd) return; cmd->line_thickness = (unsigned short)line_thickness; cmd->begin.x = (short)x0; cmd->begin.y = (short)y0; cmd->end.x = (short)x1; cmd->end.y = (short)y1; cmd->color = c; } NK_API void nk_stroke_curve(struct nk_command_buffer *b, float ax, float ay, float ctrl0x, float ctrl0y, float ctrl1x, float ctrl1y, float bx, float by, float line_thickness, struct nk_color col) { struct nk_command_curve *cmd; NK_ASSERT(b); if (!b || col.a == 0 || line_thickness <= 0) return; cmd = (struct nk_command_curve*) nk_command_buffer_push(b, NK_COMMAND_CURVE, sizeof(*cmd)); if (!cmd) return; cmd->line_thickness = (unsigned short)line_thickness; cmd->begin.x = (short)ax; cmd->begin.y = (short)ay; cmd->ctrl[0].x = (short)ctrl0x; cmd->ctrl[0].y = (short)ctrl0y; cmd->ctrl[1].x = (short)ctrl1x; cmd->ctrl[1].y = (short)ctrl1y; cmd->end.x = (short)bx; cmd->end.y = (short)by; cmd->color = col; } NK_API void nk_stroke_rect(struct nk_command_buffer *b, struct nk_rect rect, float rounding, float line_thickness, struct nk_color c) { struct nk_command_rect *cmd; NK_ASSERT(b); if (!b || c.a == 0 || rect.w == 0 || rect.h == 0 || line_thickness <= 0) return; if (b->use_clipping) { const struct nk_rect *clip = &b->clip; if (!NK_INTERSECT(rect.x, rect.y, rect.w, rect.h, clip->x, clip->y, clip->w, clip->h)) return; } cmd = (struct nk_command_rect*) nk_command_buffer_push(b, NK_COMMAND_RECT, sizeof(*cmd)); if (!cmd) return; cmd->rounding = (unsigned short)rounding; cmd->line_thickness = (unsigned short)line_thickness; cmd->x = (short)rect.x; cmd->y = (short)rect.y; cmd->w = (unsigned short)NK_MAX(0, rect.w); cmd->h = (unsigned short)NK_MAX(0, rect.h); cmd->color = c; } NK_API void nk_fill_rect(struct nk_command_buffer *b, struct nk_rect rect, float rounding, struct nk_color c) { struct nk_command_rect_filled *cmd; NK_ASSERT(b); if (!b || c.a == 0 || rect.w == 0 || rect.h == 0) return; if (b->use_clipping) { const struct nk_rect *clip = &b->clip; if (!NK_INTERSECT(rect.x, rect.y, rect.w, rect.h, clip->x, clip->y, clip->w, clip->h)) return; } cmd = (struct nk_command_rect_filled*) nk_command_buffer_push(b, NK_COMMAND_RECT_FILLED, sizeof(*cmd)); if (!cmd) return; cmd->rounding = (unsigned short)rounding; cmd->x = (short)rect.x; cmd->y = (short)rect.y; cmd->w = (unsigned short)NK_MAX(0, rect.w); cmd->h = (unsigned short)NK_MAX(0, rect.h); cmd->color = c; } NK_API void nk_fill_rect_multi_color(struct nk_command_buffer *b, struct nk_rect rect, struct nk_color left, struct nk_color top, struct nk_color right, struct nk_color bottom) { struct nk_command_rect_multi_color *cmd; NK_ASSERT(b); if (!b || rect.w == 0 || rect.h == 0) return; if (b->use_clipping) { const struct nk_rect *clip = &b->clip; if (!NK_INTERSECT(rect.x, rect.y, rect.w, rect.h, clip->x, clip->y, clip->w, clip->h)) return; } cmd = (struct nk_command_rect_multi_color*) nk_command_buffer_push(b, NK_COMMAND_RECT_MULTI_COLOR, sizeof(*cmd)); if (!cmd) return; cmd->x = (short)rect.x; cmd->y = (short)rect.y; cmd->w = (unsigned short)NK_MAX(0, rect.w); cmd->h = (unsigned short)NK_MAX(0, rect.h); cmd->left = left; cmd->top = top; cmd->right = right; cmd->bottom = bottom; } NK_API void nk_stroke_circle(struct nk_command_buffer *b, struct nk_rect r, float line_thickness, struct nk_color c) { struct nk_command_circle *cmd; if (!b || r.w == 0 || r.h == 0 || line_thickness <= 0) return; if (b->use_clipping) { const struct nk_rect *clip = &b->clip; if (!NK_INTERSECT(r.x, r.y, r.w, r.h, clip->x, clip->y, clip->w, clip->h)) return; } cmd = (struct nk_command_circle*) nk_command_buffer_push(b, NK_COMMAND_CIRCLE, sizeof(*cmd)); if (!cmd) return; cmd->line_thickness = (unsigned short)line_thickness; cmd->x = (short)r.x; cmd->y = (short)r.y; cmd->w = (unsigned short)NK_MAX(r.w, 0); cmd->h = (unsigned short)NK_MAX(r.h, 0); cmd->color = c; } NK_API void nk_fill_circle(struct nk_command_buffer *b, struct nk_rect r, struct nk_color c) { struct nk_command_circle_filled *cmd; NK_ASSERT(b); if (!b || c.a == 0 || r.w == 0 || r.h == 0) return; if (b->use_clipping) { const struct nk_rect *clip = &b->clip; if (!NK_INTERSECT(r.x, r.y, r.w, r.h, clip->x, clip->y, clip->w, clip->h)) return; } cmd = (struct nk_command_circle_filled*) nk_command_buffer_push(b, NK_COMMAND_CIRCLE_FILLED, sizeof(*cmd)); if (!cmd) return; cmd->x = (short)r.x; cmd->y = (short)r.y; cmd->w = (unsigned short)NK_MAX(r.w, 0); cmd->h = (unsigned short)NK_MAX(r.h, 0); cmd->color = c; } NK_API void nk_stroke_arc(struct nk_command_buffer *b, float cx, float cy, float radius, float a_min, float a_max, float line_thickness, struct nk_color c) { struct nk_command_arc *cmd; if (!b || c.a == 0 || line_thickness <= 0) return; cmd = (struct nk_command_arc*) nk_command_buffer_push(b, NK_COMMAND_ARC, sizeof(*cmd)); if (!cmd) return; cmd->line_thickness = (unsigned short)line_thickness; cmd->cx = (short)cx; cmd->cy = (short)cy; cmd->r = (unsigned short)radius; cmd->a[0] = a_min; cmd->a[1] = a_max; cmd->color = c; } NK_API void nk_fill_arc(struct nk_command_buffer *b, float cx, float cy, float radius, float a_min, float a_max, struct nk_color c) { struct nk_command_arc_filled *cmd; NK_ASSERT(b); if (!b || c.a == 0) return; cmd = (struct nk_command_arc_filled*) nk_command_buffer_push(b, NK_COMMAND_ARC_FILLED, sizeof(*cmd)); if (!cmd) return; cmd->cx = (short)cx; cmd->cy = (short)cy; cmd->r = (unsigned short)radius; cmd->a[0] = a_min; cmd->a[1] = a_max; cmd->color = c; } NK_API void nk_stroke_triangle(struct nk_command_buffer *b, float x0, float y0, float x1, float y1, float x2, float y2, float line_thickness, struct nk_color c) { struct nk_command_triangle *cmd; NK_ASSERT(b); if (!b || c.a == 0 || line_thickness <= 0) return; if (b->use_clipping) { const struct nk_rect *clip = &b->clip; if (!NK_INBOX(x0, y0, clip->x, clip->y, clip->w, clip->h) && !NK_INBOX(x1, y1, clip->x, clip->y, clip->w, clip->h) && !NK_INBOX(x2, y2, clip->x, clip->y, clip->w, clip->h)) return; } cmd = (struct nk_command_triangle*) nk_command_buffer_push(b, NK_COMMAND_TRIANGLE, sizeof(*cmd)); if (!cmd) return; cmd->line_thickness = (unsigned short)line_thickness; cmd->a.x = (short)x0; cmd->a.y = (short)y0; cmd->b.x = (short)x1; cmd->b.y = (short)y1; cmd->c.x = (short)x2; cmd->c.y = (short)y2; cmd->color = c; } NK_API void nk_fill_triangle(struct nk_command_buffer *b, float x0, float y0, float x1, float y1, float x2, float y2, struct nk_color c) { struct nk_command_triangle_filled *cmd; NK_ASSERT(b); if (!b || c.a == 0) return; if (!b) return; if (b->use_clipping) { const struct nk_rect *clip = &b->clip; if (!NK_INBOX(x0, y0, clip->x, clip->y, clip->w, clip->h) && !NK_INBOX(x1, y1, clip->x, clip->y, clip->w, clip->h) && !NK_INBOX(x2, y2, clip->x, clip->y, clip->w, clip->h)) return; } cmd = (struct nk_command_triangle_filled*) nk_command_buffer_push(b, NK_COMMAND_TRIANGLE_FILLED, sizeof(*cmd)); if (!cmd) return; cmd->a.x = (short)x0; cmd->a.y = (short)y0; cmd->b.x = (short)x1; cmd->b.y = (short)y1; cmd->c.x = (short)x2; cmd->c.y = (short)y2; cmd->color = c; } NK_API void nk_stroke_polygon(struct nk_command_buffer *b, float *points, int point_count, float line_thickness, struct nk_color col) { int i; nk_size size = 0; struct nk_command_polygon *cmd; NK_ASSERT(b); if (!b || col.a == 0 || line_thickness <= 0) return; size = sizeof(*cmd) + sizeof(short) * 2 * (nk_size)point_count; cmd = (struct nk_command_polygon*) nk_command_buffer_push(b, NK_COMMAND_POLYGON, size); if (!cmd) return; cmd->color = col; cmd->line_thickness = (unsigned short)line_thickness; cmd->point_count = (unsigned short)point_count; for (i = 0; i < point_count; ++i) { cmd->points[i].x = (short)points[i*2]; cmd->points[i].y = (short)points[i*2+1]; } } NK_API void nk_fill_polygon(struct nk_command_buffer *b, float *points, int point_count, struct nk_color col) { int i; nk_size size = 0; struct nk_command_polygon_filled *cmd; NK_ASSERT(b); if (!b || col.a == 0) return; size = sizeof(*cmd) + sizeof(short) * 2 * (nk_size)point_count; cmd = (struct nk_command_polygon_filled*) nk_command_buffer_push(b, NK_COMMAND_POLYGON_FILLED, size); if (!cmd) return; cmd->color = col; cmd->point_count = (unsigned short)point_count; for (i = 0; i < point_count; ++i) { cmd->points[i].x = (short)points[i*2+0]; cmd->points[i].y = (short)points[i*2+1]; } } NK_API void nk_stroke_polyline(struct nk_command_buffer *b, float *points, int point_count, float line_thickness, struct nk_color col) { int i; nk_size size = 0; struct nk_command_polyline *cmd; NK_ASSERT(b); if (!b || col.a == 0 || line_thickness <= 0) return; size = sizeof(*cmd) + sizeof(short) * 2 * (nk_size)point_count; cmd = (struct nk_command_polyline*) nk_command_buffer_push(b, NK_COMMAND_POLYLINE, size); if (!cmd) return; cmd->color = col; cmd->point_count = (unsigned short)point_count; cmd->line_thickness = (unsigned short)line_thickness; for (i = 0; i < point_count; ++i) { cmd->points[i].x = (short)points[i*2]; cmd->points[i].y = (short)points[i*2+1]; } } NK_API void nk_draw_image(struct nk_command_buffer *b, struct nk_rect r, const struct nk_image *img, struct nk_color col) { struct nk_command_image *cmd; NK_ASSERT(b); if (!b) return; if (b->use_clipping) { const struct nk_rect *c = &b->clip; if (c->w == 0 || c->h == 0 || !NK_INTERSECT(r.x, r.y, r.w, r.h, c->x, c->y, c->w, c->h)) return; } cmd = (struct nk_command_image*) nk_command_buffer_push(b, NK_COMMAND_IMAGE, sizeof(*cmd)); if (!cmd) return; cmd->x = (short)r.x; cmd->y = (short)r.y; cmd->w = (unsigned short)NK_MAX(0, r.w); cmd->h = (unsigned short)NK_MAX(0, r.h); cmd->img = *img; cmd->col = col; } //< @r-lyeh { NK_API void nk_draw_image_flipped(struct nk_command_buffer *b, struct nk_rect r, const struct nk_image *img, struct nk_color col) { struct nk_command_image *cmd; NK_ASSERT(b); if (!b) return; if (b->use_clipping) { const struct nk_rect *c = &b->clip; if (c->w == 0 || c->h == 0 || !NK_INTERSECT(r.x, r.y, r.w, r.h, c->x, c->y, c->w, c->h)) return; } cmd = (struct nk_command_image*) nk_command_buffer_push(b, NK_COMMAND_IMAGE_FLIPPED, sizeof(*cmd)); if (!cmd) return; cmd->x = (short)r.x; cmd->y = (short)r.y; cmd->w = (unsigned short)NK_MAX(0, r.w); cmd->h = (unsigned short)NK_MAX(0, r.h); cmd->img = *img; cmd->col = col; } //< @r-lyeh } NK_API void nk_draw_nine_slice(struct nk_command_buffer *b, struct nk_rect r, const struct nk_nine_slice *slc, struct nk_color col) { struct nk_image img; const struct nk_image *slcimg = (const struct nk_image*)slc; nk_ushort rgnX, rgnY, rgnW, rgnH; rgnX = slcimg->region[0]; rgnY = slcimg->region[1]; rgnW = slcimg->region[2]; rgnH = slcimg->region[3]; /* top-left */ img.handle = slcimg->handle; img.w = slcimg->w; img.h = slcimg->h; img.region[0] = rgnX; img.region[1] = rgnY; img.region[2] = slc->l; img.region[3] = slc->t; nk_draw_image(b, nk_rect(r.x, r.y, (float)slc->l, (float)slc->t), &img, col); #define IMG_RGN(x, y, w, h) img.region[0] = (nk_ushort)(x); img.region[1] = (nk_ushort)(y); img.region[2] = (nk_ushort)(w); img.region[3] = (nk_ushort)(h); /* top-center */ IMG_RGN(rgnX + slc->l, rgnY, rgnW - slc->l - slc->r, slc->t); nk_draw_image(b, nk_rect(r.x + (float)slc->l, r.y, (float)(r.w - slc->l - slc->r), (float)slc->t), &img, col); /* top-right */ IMG_RGN(rgnX + rgnW - slc->r, rgnY, slc->r, slc->t); nk_draw_image(b, nk_rect(r.x + r.w - (float)slc->r, r.y, (float)slc->r, (float)slc->t), &img, col); /* center-left */ IMG_RGN(rgnX, rgnY + slc->t, slc->l, rgnH - slc->t - slc->b); nk_draw_image(b, nk_rect(r.x, r.y + (float)slc->t, (float)slc->l, (float)(r.h - slc->t - slc->b)), &img, col); /* center */ IMG_RGN(rgnX + slc->l, rgnY + slc->t, rgnW - slc->l - slc->r, rgnH - slc->t - slc->b); nk_draw_image(b, nk_rect(r.x + (float)slc->l, r.y + (float)slc->t, (float)(r.w - slc->l - slc->r), (float)(r.h - slc->t - slc->b)), &img, col); /* center-right */ IMG_RGN(rgnX + rgnW - slc->r, rgnY + slc->t, slc->r, rgnH - slc->t - slc->b); nk_draw_image(b, nk_rect(r.x + r.w - (float)slc->r, r.y + (float)slc->t, (float)slc->r, (float)(r.h - slc->t - slc->b)), &img, col); /* bottom-left */ IMG_RGN(rgnX, rgnY + rgnH - slc->b, slc->l, slc->b); nk_draw_image(b, nk_rect(r.x, r.y + r.h - (float)slc->b, (float)slc->l, (float)slc->b), &img, col); /* bottom-center */ IMG_RGN(rgnX + slc->l, rgnY + rgnH - slc->b, rgnW - slc->l - slc->r, slc->b); nk_draw_image(b, nk_rect(r.x + (float)slc->l, r.y + r.h - (float)slc->b, (float)(r.w - slc->l - slc->r), (float)slc->b), &img, col); /* bottom-right */ IMG_RGN(rgnX + rgnW - slc->r, rgnY + rgnH - slc->b, slc->r, slc->b); nk_draw_image(b, nk_rect(r.x + r.w - (float)slc->r, r.y + r.h - (float)slc->b, (float)slc->r, (float)slc->b), &img, col); #undef IMG_RGN } NK_API void nk_push_custom(struct nk_command_buffer *b, struct nk_rect r, nk_command_custom_callback cb, nk_handle usr) { struct nk_command_custom *cmd; NK_ASSERT(b); if (!b) return; if (b->use_clipping) { const struct nk_rect *c = &b->clip; if (c->w == 0 || c->h == 0 || !NK_INTERSECT(r.x, r.y, r.w, r.h, c->x, c->y, c->w, c->h)) return; } cmd = (struct nk_command_custom*) nk_command_buffer_push(b, NK_COMMAND_CUSTOM, sizeof(*cmd)); if (!cmd) return; cmd->x = (short)r.x; cmd->y = (short)r.y; cmd->w = (unsigned short)NK_MAX(0, r.w); cmd->h = (unsigned short)NK_MAX(0, r.h); cmd->callback_data = usr; cmd->callback = cb; } NK_API void nk_draw_text(struct nk_command_buffer *b, struct nk_rect r, const char *string, int length, const struct nk_user_font *font, struct nk_color bg, struct nk_color fg) { float text_width = 0; struct nk_command_text *cmd; NK_ASSERT(b); NK_ASSERT(font); if (!b || !string || !length || (bg.a == 0 && fg.a == 0)) return; if (b->use_clipping) { const struct nk_rect *c = &b->clip; if (c->w == 0 || c->h == 0 || !NK_INTERSECT(r.x, r.y, r.w, r.h, c->x, c->y, c->w, c->h)) return; } /* make sure text fits inside bounds */ text_width = font->width(font->userdata, font->height, string, length); if (text_width > r.w){ int glyphs = 0; float txt_width = (float)text_width; length = nk_text_clamp(font, string, length, r.w, &glyphs, &txt_width, 0,0); } if (!length) return; cmd = (struct nk_command_text*) nk_command_buffer_push(b, NK_COMMAND_TEXT, sizeof(*cmd) + (nk_size)(length + 1)); if (!cmd) return; cmd->x = (short)r.x; cmd->y = (short)r.y; cmd->w = (unsigned short)r.w; cmd->h = (unsigned short)r.h; cmd->background = bg; cmd->foreground = fg; cmd->font = font; cmd->length = length; cmd->height = font->height; NK_MEMCPY(cmd->string, string, (nk_size)length); cmd->string[length] = '\0'; } /* =============================================================== * * VERTEX * * ===============================================================*/ #ifdef NK_INCLUDE_VERTEX_BUFFER_OUTPUT NK_API void nk_draw_list_init(struct nk_draw_list *list) { nk_size i = 0; NK_ASSERT(list); if (!list) return; nk_zero(list, sizeof(*list)); for (i = 0; i < NK_LEN(list->circle_vtx); ++i) { const float a = ((float)i / (float)NK_LEN(list->circle_vtx)) * 2 * NK_PI; list->circle_vtx[i].x = (float)NK_COS(a); list->circle_vtx[i].y = (float)NK_SIN(a); } } NK_API void nk_draw_list_setup(struct nk_draw_list *canvas, const struct nk_convert_config *config, struct nk_buffer *cmds, struct nk_buffer *vertices, struct nk_buffer *elements, enum nk_anti_aliasing line_aa, enum nk_anti_aliasing shape_aa) { NK_ASSERT(canvas); NK_ASSERT(config); NK_ASSERT(cmds); NK_ASSERT(vertices); NK_ASSERT(elements); if (!canvas || !config || !cmds || !vertices || !elements) return; canvas->buffer = cmds; canvas->config = *config; canvas->elements = elements; canvas->vertices = vertices; canvas->line_AA = line_aa; canvas->shape_AA = shape_aa; canvas->clip_rect = nk_null_rect; canvas->cmd_offset = 0; canvas->element_count = 0; canvas->vertex_count = 0; canvas->cmd_offset = 0; canvas->cmd_count = 0; canvas->path_count = 0; } NK_API const struct nk_draw_command* nk__draw_list_begin(const struct nk_draw_list *canvas, const struct nk_buffer *buffer) { nk_byte *memory; nk_size offset; const struct nk_draw_command *cmd; NK_ASSERT(buffer); if (!buffer || !buffer->size || !canvas->cmd_count) return 0; memory = (nk_byte*)buffer->memory.ptr; offset = buffer->memory.size - canvas->cmd_offset; cmd = nk_ptr_add(const struct nk_draw_command, memory, offset); return cmd; } NK_API const struct nk_draw_command* nk__draw_list_end(const struct nk_draw_list *canvas, const struct nk_buffer *buffer) { nk_size size; nk_size offset; nk_byte *memory; const struct nk_draw_command *end; NK_ASSERT(buffer); NK_ASSERT(canvas); if (!buffer || !canvas) return 0; memory = (nk_byte*)buffer->memory.ptr; size = buffer->memory.size; offset = size - canvas->cmd_offset; end = nk_ptr_add(const struct nk_draw_command, memory, offset); end -= (canvas->cmd_count-1); return end; } NK_API const struct nk_draw_command* nk__draw_list_next(const struct nk_draw_command *cmd, const struct nk_buffer *buffer, const struct nk_draw_list *canvas) { const struct nk_draw_command *end; NK_ASSERT(buffer); NK_ASSERT(canvas); if (!cmd || !buffer || !canvas) return 0; end = nk__draw_list_end(canvas, buffer); if (cmd <= end) return 0; return (cmd-1); } NK_INTERN struct nk_vec2* nk_draw_list_alloc_path(struct nk_draw_list *list, int count) { struct nk_vec2 *points; NK_STORAGE const nk_size point_align = NK_ALIGNOF(struct nk_vec2); NK_STORAGE const nk_size point_size = sizeof(struct nk_vec2); points = (struct nk_vec2*) nk_buffer_alloc(list->buffer, NK_BUFFER_FRONT, point_size * (nk_size)count, point_align); if (!points) return 0; if (!list->path_offset) { void *memory = nk_buffer_memory(list->buffer); list->path_offset = (unsigned int)((nk_byte*)points - (nk_byte*)memory); } list->path_count += (unsigned int)count; return points; } NK_INTERN struct nk_vec2 nk_draw_list_path_last(struct nk_draw_list *list) { void *memory; struct nk_vec2 *point; NK_ASSERT(list->path_count); memory = nk_buffer_memory(list->buffer); point = nk_ptr_add(struct nk_vec2, memory, list->path_offset); point += (list->path_count-1); return *point; } NK_INTERN struct nk_draw_command* nk_draw_list_push_command(struct nk_draw_list *list, struct nk_rect clip, nk_handle texture) { NK_STORAGE const nk_size cmd_align = NK_ALIGNOF(struct nk_draw_command); NK_STORAGE const nk_size cmd_size = sizeof(struct nk_draw_command); struct nk_draw_command *cmd; NK_ASSERT(list); cmd = (struct nk_draw_command*) nk_buffer_alloc(list->buffer, NK_BUFFER_BACK, cmd_size, cmd_align); if (!cmd) return 0; if (!list->cmd_count) { nk_byte *memory = (nk_byte*)nk_buffer_memory(list->buffer); nk_size total = nk_buffer_total(list->buffer); memory = nk_ptr_add(nk_byte, memory, total); list->cmd_offset = (nk_size)(memory - (nk_byte*)cmd); } cmd->elem_count = 0; cmd->clip_rect = clip; cmd->texture = texture; #ifdef NK_INCLUDE_COMMAND_USERDATA cmd->userdata = list->userdata; #endif list->cmd_count++; list->clip_rect = clip; return cmd; } NK_INTERN struct nk_draw_command* nk_draw_list_command_last(struct nk_draw_list *list) { void *memory; nk_size size; struct nk_draw_command *cmd; NK_ASSERT(list->cmd_count); memory = nk_buffer_memory(list->buffer); size = nk_buffer_total(list->buffer); cmd = nk_ptr_add(struct nk_draw_command, memory, size - list->cmd_offset); return (cmd - (list->cmd_count-1)); } NK_INTERN void nk_draw_list_add_clip(struct nk_draw_list *list, struct nk_rect rect) { NK_ASSERT(list); if (!list) return; if (!list->cmd_count) { nk_draw_list_push_command(list, rect, list->config.null.texture); } else { struct nk_draw_command *prev = nk_draw_list_command_last(list); if (prev->elem_count == 0) prev->clip_rect = rect; nk_draw_list_push_command(list, rect, prev->texture); } } NK_INTERN void nk_draw_list_push_image(struct nk_draw_list *list, nk_handle texture) { NK_ASSERT(list); if (!list) return; if (!list->cmd_count) { nk_draw_list_push_command(list, nk_null_rect, texture); } else { struct nk_draw_command *prev = nk_draw_list_command_last(list); if (prev->elem_count == 0) { prev->texture = texture; #ifdef NK_INCLUDE_COMMAND_USERDATA prev->userdata = list->userdata; #endif } else if (prev->texture.id != texture.id #ifdef NK_INCLUDE_COMMAND_USERDATA || prev->userdata.id != list->userdata.id #endif ) nk_draw_list_push_command(list, prev->clip_rect, texture); } } #ifdef NK_INCLUDE_COMMAND_USERDATA NK_API void nk_draw_list_push_userdata(struct nk_draw_list *list, nk_handle userdata) { list->userdata = userdata; } #endif NK_INTERN void* nk_draw_list_alloc_vertices(struct nk_draw_list *list, nk_size count) { void *vtx; NK_ASSERT(list); if (!list) return 0; vtx = nk_buffer_alloc(list->vertices, NK_BUFFER_FRONT, list->config.vertex_size*count, list->config.vertex_alignment); if (!vtx) return 0; list->vertex_count += (unsigned int)count; /* This assert triggers because your are drawing a lot of stuff and nuklear * defined `nk_draw_index` as `nk_ushort` to safe space be default. * * So you reached the maximum number of indices or rather vertexes. * To solve this issue please change typedef `nk_draw_index` to `nk_uint` * and don't forget to specify the new element size in your drawing * backend (OpenGL, DirectX, ...). For example in OpenGL for `glDrawElements` * instead of specifying `GL_UNSIGNED_SHORT` you have to define `GL_UNSIGNED_INT`. * Sorry for the inconvenience. */ if(sizeof(nk_draw_index)==2) NK_ASSERT((list->vertex_count < NK_USHORT_MAX && "To many vertices for 16-bit vertex indices. Please read comment above on how to solve this problem")); return vtx; } NK_INTERN nk_draw_index* nk_draw_list_alloc_elements(struct nk_draw_list *list, nk_size count) { nk_draw_index *ids; struct nk_draw_command *cmd; NK_STORAGE const nk_size elem_align = NK_ALIGNOF(nk_draw_index); NK_STORAGE const nk_size elem_size = sizeof(nk_draw_index); NK_ASSERT(list); if (!list) return 0; ids = (nk_draw_index*) nk_buffer_alloc(list->elements, NK_BUFFER_FRONT, elem_size*count, elem_align); if (!ids) return 0; cmd = nk_draw_list_command_last(list); list->element_count += (unsigned int)count; cmd->elem_count += (unsigned int)count; return ids; } NK_INTERN int nk_draw_vertex_layout_element_is_end_of_layout( const struct nk_draw_vertex_layout_element *element) { return (element->attribute == NK_VERTEX_ATTRIBUTE_COUNT || element->format == NK_FORMAT_COUNT); } NK_INTERN void nk_draw_vertex_color(void *attr, const float *vals, enum nk_draw_vertex_layout_format format) { /* if this triggers you tried to provide a value format for a color */ float val[4]; NK_ASSERT(format >= NK_FORMAT_COLOR_BEGIN); NK_ASSERT(format <= NK_FORMAT_COLOR_END); if (format < NK_FORMAT_COLOR_BEGIN || format > NK_FORMAT_COLOR_END) return; val[0] = NK_SATURATE(vals[0]); val[1] = NK_SATURATE(vals[1]); val[2] = NK_SATURATE(vals[2]); val[3] = NK_SATURATE(vals[3]); switch (format) { default: NK_ASSERT(0 && "Invalid vertex layout color format"); break; case NK_FORMAT_R8G8B8A8: case NK_FORMAT_R8G8B8: { struct nk_color col = nk_rgba_fv(val); NK_MEMCPY(attr, &col.r, sizeof(col)); } break; case NK_FORMAT_B8G8R8A8: { struct nk_color col = nk_rgba_fv(val); struct nk_color bgra = nk_rgba(col.b, col.g, col.r, col.a); NK_MEMCPY(attr, &bgra, sizeof(bgra)); } break; case NK_FORMAT_R16G15B16: { nk_ushort col[3]; col[0] = (nk_ushort)(val[0]*(float)NK_USHORT_MAX); col[1] = (nk_ushort)(val[1]*(float)NK_USHORT_MAX); col[2] = (nk_ushort)(val[2]*(float)NK_USHORT_MAX); NK_MEMCPY(attr, col, sizeof(col)); } break; case NK_FORMAT_R16G15B16A16: { nk_ushort col[4]; col[0] = (nk_ushort)(val[0]*(float)NK_USHORT_MAX); col[1] = (nk_ushort)(val[1]*(float)NK_USHORT_MAX); col[2] = (nk_ushort)(val[2]*(float)NK_USHORT_MAX); col[3] = (nk_ushort)(val[3]*(float)NK_USHORT_MAX); NK_MEMCPY(attr, col, sizeof(col)); } break; case NK_FORMAT_R32G32B32: { nk_uint col[3]; col[0] = (nk_uint)(val[0]*(float)NK_UINT_MAX); col[1] = (nk_uint)(val[1]*(float)NK_UINT_MAX); col[2] = (nk_uint)(val[2]*(float)NK_UINT_MAX); NK_MEMCPY(attr, col, sizeof(col)); } break; case NK_FORMAT_R32G32B32A32: { nk_uint col[4]; col[0] = (nk_uint)(val[0]*(float)NK_UINT_MAX); col[1] = (nk_uint)(val[1]*(float)NK_UINT_MAX); col[2] = (nk_uint)(val[2]*(float)NK_UINT_MAX); col[3] = (nk_uint)(val[3]*(float)NK_UINT_MAX); NK_MEMCPY(attr, col, sizeof(col)); } break; case NK_FORMAT_R32G32B32A32_FLOAT: NK_MEMCPY(attr, val, sizeof(float)*4); break; case NK_FORMAT_R32G32B32A32_DOUBLE: { double col[4]; col[0] = (double)val[0]; col[1] = (double)val[1]; col[2] = (double)val[2]; col[3] = (double)val[3]; NK_MEMCPY(attr, col, sizeof(col)); } break; case NK_FORMAT_RGB32: case NK_FORMAT_RGBA32: { struct nk_color col = nk_rgba_fv(val); nk_uint color = nk_color_u32(col); NK_MEMCPY(attr, &color, sizeof(color)); } break; } } NK_INTERN void nk_draw_vertex_element(void *dst, const float *values, int value_count, enum nk_draw_vertex_layout_format format) { int value_index; void *attribute = dst; /* if this triggers you tried to provide a color format for a value */ NK_ASSERT(format < NK_FORMAT_COLOR_BEGIN); if (format >= NK_FORMAT_COLOR_BEGIN && format <= NK_FORMAT_COLOR_END) return; for (value_index = 0; value_index < value_count; ++value_index) { switch (format) { default: NK_ASSERT(0 && "invalid vertex layout format"); break; case NK_FORMAT_SCHAR: { char value = (char)NK_CLAMP((float)NK_SCHAR_MIN, values[value_index], (float)NK_SCHAR_MAX); NK_MEMCPY(attribute, &value, sizeof(value)); attribute = (void*)((char*)attribute + sizeof(char)); } break; case NK_FORMAT_SSHORT: { nk_short value = (nk_short)NK_CLAMP((float)NK_SSHORT_MIN, values[value_index], (float)NK_SSHORT_MAX); NK_MEMCPY(attribute, &value, sizeof(value)); attribute = (void*)((char*)attribute + sizeof(value)); } break; case NK_FORMAT_SINT: { nk_int value = (nk_int)NK_CLAMP((float)NK_SINT_MIN, values[value_index], (float)NK_SINT_MAX); NK_MEMCPY(attribute, &value, sizeof(value)); attribute = (void*)((char*)attribute + sizeof(nk_int)); } break; case NK_FORMAT_UCHAR: { unsigned char value = (unsigned char)NK_CLAMP((float)NK_UCHAR_MIN, values[value_index], (float)NK_UCHAR_MAX); NK_MEMCPY(attribute, &value, sizeof(value)); attribute = (void*)((char*)attribute + sizeof(unsigned char)); } break; case NK_FORMAT_USHORT: { nk_ushort value = (nk_ushort)NK_CLAMP((float)NK_USHORT_MIN, values[value_index], (float)NK_USHORT_MAX); NK_MEMCPY(attribute, &value, sizeof(value)); attribute = (void*)((char*)attribute + sizeof(value)); } break; case NK_FORMAT_UINT: { nk_uint value = (nk_uint)NK_CLAMP((float)NK_UINT_MIN, values[value_index], (float)NK_UINT_MAX); NK_MEMCPY(attribute, &value, sizeof(value)); attribute = (void*)((char*)attribute + sizeof(nk_uint)); } break; case NK_FORMAT_FLOAT: NK_MEMCPY(attribute, &values[value_index], sizeof(values[value_index])); attribute = (void*)((char*)attribute + sizeof(float)); break; case NK_FORMAT_DOUBLE: { double value = (double)values[value_index]; NK_MEMCPY(attribute, &value, sizeof(value)); attribute = (void*)((char*)attribute + sizeof(double)); } break; } } } NK_INTERN void* nk_draw_vertex(void *dst, const struct nk_convert_config *config, struct nk_vec2 pos, struct nk_vec2 uv, struct nk_colorf color) { void *result = (void*)((char*)dst + config->vertex_size); const struct nk_draw_vertex_layout_element *elem_iter = config->vertex_layout; while (!nk_draw_vertex_layout_element_is_end_of_layout(elem_iter)) { void *address = (void*)((char*)dst + elem_iter->offset); switch (elem_iter->attribute) { case NK_VERTEX_ATTRIBUTE_COUNT: default: NK_ASSERT(0 && "wrong element attribute"); break; case NK_VERTEX_POSITION: nk_draw_vertex_element(address, &pos.x, 2, elem_iter->format); break; case NK_VERTEX_TEXCOORD: nk_draw_vertex_element(address, &uv.x, 2, elem_iter->format); break; case NK_VERTEX_COLOR: nk_draw_vertex_color(address, &color.r, elem_iter->format); break; } elem_iter++; } return result; } NK_API void nk_draw_list_stroke_poly_line(struct nk_draw_list *list, const struct nk_vec2 *points, const unsigned int points_count, struct nk_color color, enum nk_draw_list_stroke closed, float thickness, enum nk_anti_aliasing aliasing) { nk_size count; int thick_line; struct nk_colorf col; struct nk_colorf col_trans; NK_ASSERT(list); if (!list || points_count < 2) return; color.a = (nk_byte)((float)color.a * list->config.global_alpha); count = points_count; if (!closed) count = points_count-1; thick_line = thickness > 1.0f; #ifdef NK_INCLUDE_COMMAND_USERDATA nk_draw_list_push_userdata(list, list->userdata); #endif color.a = (nk_byte)((float)color.a * list->config.global_alpha); nk_color_fv(&col.r, color); col_trans = col; col_trans.a = 0; if (aliasing == NK_ANTI_ALIASING_ON) { /* ANTI-ALIASED STROKE */ const float AA_SIZE = 1.0f; NK_STORAGE const nk_size pnt_align = NK_ALIGNOF(struct nk_vec2); NK_STORAGE const nk_size pnt_size = sizeof(struct nk_vec2); /* allocate vertices and elements */ nk_size i1 = 0; nk_size vertex_offset; nk_size index = list->vertex_count; const nk_size idx_count = (thick_line) ? (count * 18) : (count * 12); const nk_size vtx_count = (thick_line) ? (points_count * 4): (points_count *3); void *vtx = nk_draw_list_alloc_vertices(list, vtx_count); nk_draw_index *ids = nk_draw_list_alloc_elements(list, idx_count); nk_size size; struct nk_vec2 *normals, *temp; if (!vtx || !ids) return; /* temporary allocate normals + points */ vertex_offset = (nk_size)((nk_byte*)vtx - (nk_byte*)list->vertices->memory.ptr); nk_buffer_mark(list->vertices, NK_BUFFER_FRONT); size = pnt_size * ((thick_line) ? 5 : 3) * points_count; normals = (struct nk_vec2*) nk_buffer_alloc(list->vertices, NK_BUFFER_FRONT, size, pnt_align); if (!normals) return; temp = normals + points_count; /* make sure vertex pointer is still correct */ vtx = (void*)((nk_byte*)list->vertices->memory.ptr + vertex_offset); /* calculate normals */ for (i1 = 0; i1 < count; ++i1) { const nk_size i2 = ((i1 + 1) == points_count) ? 0 : (i1 + 1); struct nk_vec2 diff = nk_vec2_sub(points[i2], points[i1]); float len; /* vec2 inverted length */ len = nk_vec2_len_sqr(diff); if (len != 0.0f) len = NK_INV_SQRT(len); else len = 1.0f; diff = nk_vec2_muls(diff, len); normals[i1].x = diff.y; normals[i1].y = -diff.x; } if (!closed) normals[points_count-1] = normals[points_count-2]; if (!thick_line) { nk_size idx1, i; if (!closed) { struct nk_vec2 d; temp[0] = nk_vec2_add(points[0], nk_vec2_muls(normals[0], AA_SIZE)); temp[1] = nk_vec2_sub(points[0], nk_vec2_muls(normals[0], AA_SIZE)); d = nk_vec2_muls(normals[points_count-1], AA_SIZE); temp[(points_count-1) * 2 + 0] = nk_vec2_add(points[points_count-1], d); temp[(points_count-1) * 2 + 1] = nk_vec2_sub(points[points_count-1], d); } /* fill elements */ idx1 = index; for (i1 = 0; i1 < count; i1++) { struct nk_vec2 dm; float dmr2; nk_size i2 = ((i1 + 1) == points_count) ? 0 : (i1 + 1); nk_size idx2 = ((i1+1) == points_count) ? index: (idx1 + 3); /* average normals */ dm = nk_vec2_muls(nk_vec2_add(normals[i1], normals[i2]), 0.5f); dmr2 = dm.x * dm.x + dm.y* dm.y; if (dmr2 > 0.000001f) { float scale = 1.0f/dmr2; scale = NK_MIN(100.0f, scale); dm = nk_vec2_muls(dm, scale); } dm = nk_vec2_muls(dm, AA_SIZE); temp[i2*2+0] = nk_vec2_add(points[i2], dm); temp[i2*2+1] = nk_vec2_sub(points[i2], dm); ids[0] = (nk_draw_index)(idx2 + 0); ids[1] = (nk_draw_index)(idx1+0); ids[2] = (nk_draw_index)(idx1 + 2); ids[3] = (nk_draw_index)(idx1+2); ids[4] = (nk_draw_index)(idx2 + 2); ids[5] = (nk_draw_index)(idx2+0); ids[6] = (nk_draw_index)(idx2 + 1); ids[7] = (nk_draw_index)(idx1+1); ids[8] = (nk_draw_index)(idx1 + 0); ids[9] = (nk_draw_index)(idx1+0); ids[10]= (nk_draw_index)(idx2 + 0); ids[11]= (nk_draw_index)(idx2+1); ids += 12; idx1 = idx2; } /* fill vertices */ for (i = 0; i < points_count; ++i) { const struct nk_vec2 uv = list->config.null.uv; vtx = nk_draw_vertex(vtx, &list->config, points[i], uv, col); vtx = nk_draw_vertex(vtx, &list->config, temp[i*2+0], uv, col_trans); vtx = nk_draw_vertex(vtx, &list->config, temp[i*2+1], uv, col_trans); } } else { nk_size idx1, i; const float half_inner_thickness = (thickness - AA_SIZE) * 0.5f; if (!closed) { struct nk_vec2 d1 = nk_vec2_muls(normals[0], half_inner_thickness + AA_SIZE); struct nk_vec2 d2 = nk_vec2_muls(normals[0], half_inner_thickness); temp[0] = nk_vec2_add(points[0], d1); temp[1] = nk_vec2_add(points[0], d2); temp[2] = nk_vec2_sub(points[0], d2); temp[3] = nk_vec2_sub(points[0], d1); d1 = nk_vec2_muls(normals[points_count-1], half_inner_thickness + AA_SIZE); d2 = nk_vec2_muls(normals[points_count-1], half_inner_thickness); temp[(points_count-1)*4+0] = nk_vec2_add(points[points_count-1], d1); temp[(points_count-1)*4+1] = nk_vec2_add(points[points_count-1], d2); temp[(points_count-1)*4+2] = nk_vec2_sub(points[points_count-1], d2); temp[(points_count-1)*4+3] = nk_vec2_sub(points[points_count-1], d1); } /* add all elements */ idx1 = index; for (i1 = 0; i1 < count; ++i1) { struct nk_vec2 dm_out, dm_in; const nk_size i2 = ((i1+1) == points_count) ? 0: (i1 + 1); nk_size idx2 = ((i1+1) == points_count) ? index: (idx1 + 4); /* average normals */ struct nk_vec2 dm = nk_vec2_muls(nk_vec2_add(normals[i1], normals[i2]), 0.5f); float dmr2 = dm.x * dm.x + dm.y* dm.y; if (dmr2 > 0.000001f) { float scale = 1.0f/dmr2; scale = NK_MIN(100.0f, scale); dm = nk_vec2_muls(dm, scale); } dm_out = nk_vec2_muls(dm, ((half_inner_thickness) + AA_SIZE)); dm_in = nk_vec2_muls(dm, half_inner_thickness); temp[i2*4+0] = nk_vec2_add(points[i2], dm_out); temp[i2*4+1] = nk_vec2_add(points[i2], dm_in); temp[i2*4+2] = nk_vec2_sub(points[i2], dm_in); temp[i2*4+3] = nk_vec2_sub(points[i2], dm_out); /* add indexes */ ids[0] = (nk_draw_index)(idx2 + 1); ids[1] = (nk_draw_index)(idx1+1); ids[2] = (nk_draw_index)(idx1 + 2); ids[3] = (nk_draw_index)(idx1+2); ids[4] = (nk_draw_index)(idx2 + 2); ids[5] = (nk_draw_index)(idx2+1); ids[6] = (nk_draw_index)(idx2 + 1); ids[7] = (nk_draw_index)(idx1+1); ids[8] = (nk_draw_index)(idx1 + 0); ids[9] = (nk_draw_index)(idx1+0); ids[10]= (nk_draw_index)(idx2 + 0); ids[11] = (nk_draw_index)(idx2+1); ids[12]= (nk_draw_index)(idx2 + 2); ids[13] = (nk_draw_index)(idx1+2); ids[14]= (nk_draw_index)(idx1 + 3); ids[15] = (nk_draw_index)(idx1+3); ids[16]= (nk_draw_index)(idx2 + 3); ids[17] = (nk_draw_index)(idx2+2); ids += 18; idx1 = idx2; } /* add vertices */ for (i = 0; i < points_count; ++i) { const struct nk_vec2 uv = list->config.null.uv; vtx = nk_draw_vertex(vtx, &list->config, temp[i*4+0], uv, col_trans); vtx = nk_draw_vertex(vtx, &list->config, temp[i*4+1], uv, col); vtx = nk_draw_vertex(vtx, &list->config, temp[i*4+2], uv, col); vtx = nk_draw_vertex(vtx, &list->config, temp[i*4+3], uv, col_trans); } } /* free temporary normals + points */ nk_buffer_reset(list->vertices, NK_BUFFER_FRONT); } else { /* NON ANTI-ALIASED STROKE */ nk_size i1 = 0; nk_size idx = list->vertex_count; const nk_size idx_count = count * 6; const nk_size vtx_count = count * 4; void *vtx = nk_draw_list_alloc_vertices(list, vtx_count); nk_draw_index *ids = nk_draw_list_alloc_elements(list, idx_count); if (!vtx || !ids) return; for (i1 = 0; i1 < count; ++i1) { float dx, dy; const struct nk_vec2 uv = list->config.null.uv; const nk_size i2 = ((i1+1) == points_count) ? 0 : i1 + 1; const struct nk_vec2 p1 = points[i1]; const struct nk_vec2 p2 = points[i2]; struct nk_vec2 diff = nk_vec2_sub(p2, p1); float len; /* vec2 inverted length */ len = nk_vec2_len_sqr(diff); if (len != 0.0f) len = NK_INV_SQRT(len); else len = 1.0f; diff = nk_vec2_muls(diff, len); /* add vertices */ dx = diff.x * (thickness * 0.5f); dy = diff.y * (thickness * 0.5f); vtx = nk_draw_vertex(vtx, &list->config, nk_vec2(p1.x + dy, p1.y - dx), uv, col); vtx = nk_draw_vertex(vtx, &list->config, nk_vec2(p2.x + dy, p2.y - dx), uv, col); vtx = nk_draw_vertex(vtx, &list->config, nk_vec2(p2.x - dy, p2.y + dx), uv, col); vtx = nk_draw_vertex(vtx, &list->config, nk_vec2(p1.x - dy, p1.y + dx), uv, col); ids[0] = (nk_draw_index)(idx+0); ids[1] = (nk_draw_index)(idx+1); ids[2] = (nk_draw_index)(idx+2); ids[3] = (nk_draw_index)(idx+0); ids[4] = (nk_draw_index)(idx+2); ids[5] = (nk_draw_index)(idx+3); ids += 6; idx += 4; } } } NK_API void nk_draw_list_fill_poly_convex(struct nk_draw_list *list, const struct nk_vec2 *points, const unsigned int points_count, struct nk_color color, enum nk_anti_aliasing aliasing) { struct nk_colorf col; struct nk_colorf col_trans; NK_STORAGE const nk_size pnt_align = NK_ALIGNOF(struct nk_vec2); NK_STORAGE const nk_size pnt_size = sizeof(struct nk_vec2); NK_ASSERT(list); if (!list || points_count < 3) return; #ifdef NK_INCLUDE_COMMAND_USERDATA nk_draw_list_push_userdata(list, list->userdata); #endif color.a = (nk_byte)((float)color.a * list->config.global_alpha); nk_color_fv(&col.r, color); col_trans = col; col_trans.a = 0; if (aliasing == NK_ANTI_ALIASING_ON) { nk_size i = 0; nk_size i0 = 0; nk_size i1 = 0; const float AA_SIZE = 1.0f; nk_size vertex_offset = 0; nk_size index = list->vertex_count; const nk_size idx_count = (points_count-2)*3 + points_count*6; const nk_size vtx_count = (points_count*2); void *vtx = nk_draw_list_alloc_vertices(list, vtx_count); nk_draw_index *ids = nk_draw_list_alloc_elements(list, idx_count); nk_size size = 0; struct nk_vec2 *normals = 0; unsigned int vtx_inner_idx = (unsigned int)(index + 0); unsigned int vtx_outer_idx = (unsigned int)(index + 1); if (!vtx || !ids) return; /* temporary allocate normals */ vertex_offset = (nk_size)((nk_byte*)vtx - (nk_byte*)list->vertices->memory.ptr); nk_buffer_mark(list->vertices, NK_BUFFER_FRONT); size = pnt_size * points_count; normals = (struct nk_vec2*) nk_buffer_alloc(list->vertices, NK_BUFFER_FRONT, size, pnt_align); if (!normals) return; vtx = (void*)((nk_byte*)list->vertices->memory.ptr + vertex_offset); /* add elements */ for (i = 2; i < points_count; i++) { ids[0] = (nk_draw_index)(vtx_inner_idx); ids[1] = (nk_draw_index)(vtx_inner_idx + ((i-1) << 1)); ids[2] = (nk_draw_index)(vtx_inner_idx + (i << 1)); ids += 3; } /* compute normals */ for (i0 = points_count-1, i1 = 0; i1 < points_count; i0 = i1++) { struct nk_vec2 p0 = points[i0]; struct nk_vec2 p1 = points[i1]; struct nk_vec2 diff = nk_vec2_sub(p1, p0); /* vec2 inverted length */ float len = nk_vec2_len_sqr(diff); if (len != 0.0f) len = NK_INV_SQRT(len); else len = 1.0f; diff = nk_vec2_muls(diff, len); normals[i0].x = diff.y; normals[i0].y = -diff.x; } /* add vertices + indexes */ for (i0 = points_count-1, i1 = 0; i1 < points_count; i0 = i1++) { const struct nk_vec2 uv = list->config.null.uv; struct nk_vec2 n0 = normals[i0]; struct nk_vec2 n1 = normals[i1]; struct nk_vec2 dm = nk_vec2_muls(nk_vec2_add(n0, n1), 0.5f); float dmr2 = dm.x*dm.x + dm.y*dm.y; if (dmr2 > 0.000001f) { float scale = 1.0f / dmr2; scale = NK_MIN(scale, 100.0f); dm = nk_vec2_muls(dm, scale); } dm = nk_vec2_muls(dm, AA_SIZE * 0.5f); /* add vertices */ vtx = nk_draw_vertex(vtx, &list->config, nk_vec2_sub(points[i1], dm), uv, col); vtx = nk_draw_vertex(vtx, &list->config, nk_vec2_add(points[i1], dm), uv, col_trans); /* add indexes */ ids[0] = (nk_draw_index)(vtx_inner_idx+(i1<<1)); ids[1] = (nk_draw_index)(vtx_inner_idx+(i0<<1)); ids[2] = (nk_draw_index)(vtx_outer_idx+(i0<<1)); ids[3] = (nk_draw_index)(vtx_outer_idx+(i0<<1)); ids[4] = (nk_draw_index)(vtx_outer_idx+(i1<<1)); ids[5] = (nk_draw_index)(vtx_inner_idx+(i1<<1)); ids += 6; } /* free temporary normals + points */ nk_buffer_reset(list->vertices, NK_BUFFER_FRONT); } else { nk_size i = 0; nk_size index = list->vertex_count; const nk_size idx_count = (points_count-2)*3; const nk_size vtx_count = points_count; void *vtx = nk_draw_list_alloc_vertices(list, vtx_count); nk_draw_index *ids = nk_draw_list_alloc_elements(list, idx_count); if (!vtx || !ids) return; for (i = 0; i < vtx_count; ++i) vtx = nk_draw_vertex(vtx, &list->config, points[i], list->config.null.uv, col); for (i = 2; i < points_count; ++i) { ids[0] = (nk_draw_index)index; ids[1] = (nk_draw_index)(index+ i - 1); ids[2] = (nk_draw_index)(index+i); ids += 3; } } } NK_API void nk_draw_list_path_clear(struct nk_draw_list *list) { NK_ASSERT(list); if (!list) return; nk_buffer_reset(list->buffer, NK_BUFFER_FRONT); list->path_count = 0; list->path_offset = 0; } NK_API void nk_draw_list_path_line_to(struct nk_draw_list *list, struct nk_vec2 pos) { struct nk_vec2 *points = 0; struct nk_draw_command *cmd = 0; NK_ASSERT(list); if (!list) return; if (!list->cmd_count) nk_draw_list_add_clip(list, nk_null_rect); cmd = nk_draw_list_command_last(list); if (cmd && cmd->texture.ptr != list->config.null.texture.ptr) nk_draw_list_push_image(list, list->config.null.texture); points = nk_draw_list_alloc_path(list, 1); if (!points) return; points[0] = pos; } NK_API void nk_draw_list_path_arc_to_fast(struct nk_draw_list *list, struct nk_vec2 center, float radius, int a_min, int a_max) { int a = 0; NK_ASSERT(list); if (!list) return; if (a_min <= a_max) { for (a = a_min; a <= a_max; a++) { const struct nk_vec2 c = list->circle_vtx[(nk_size)a % NK_LEN(list->circle_vtx)]; const float x = center.x + c.x * radius; const float y = center.y + c.y * radius; nk_draw_list_path_line_to(list, nk_vec2(x, y)); } } } NK_API void nk_draw_list_path_arc_to(struct nk_draw_list *list, struct nk_vec2 center, float radius, float a_min, float a_max, unsigned int segments) { unsigned int i = 0; NK_ASSERT(list); if (!list) return; if (radius == 0.0f) return; /* This algorithm for arc drawing relies on these two trigonometric identities[1]: sin(a + b) = sin(a) * cos(b) + cos(a) * sin(b) cos(a + b) = cos(a) * cos(b) - sin(a) * sin(b) Two coordinates (x, y) of a point on a circle centered on the origin can be written in polar form as: x = r * cos(a) y = r * sin(a) where r is the radius of the circle, a is the angle between (x, y) and the origin. This allows us to rotate the coordinates around the origin by an angle b using the following transformation: x' = r * cos(a + b) = x * cos(b) - y * sin(b) y' = r * sin(a + b) = y * cos(b) + x * sin(b) [1] https://en.wikipedia.org/wiki/List_of_trigonometric_identities#Angle_sum_and_difference_identities */ {const float d_angle = (a_max - a_min) / (float)segments; const float sin_d = (float)NK_SIN(d_angle); const float cos_d = (float)NK_COS(d_angle); float cx = (float)NK_COS(a_min) * radius; float cy = (float)NK_SIN(a_min) * radius; for(i = 0; i <= segments; ++i) { float new_cx, new_cy; const float x = center.x + cx; const float y = center.y + cy; nk_draw_list_path_line_to(list, nk_vec2(x, y)); new_cx = cx * cos_d - cy * sin_d; new_cy = cy * cos_d + cx * sin_d; cx = new_cx; cy = new_cy; }} } NK_API void nk_draw_list_path_rect_to(struct nk_draw_list *list, struct nk_vec2 a, struct nk_vec2 b, float rounding) { float r; NK_ASSERT(list); if (!list) return; r = rounding; r = NK_MIN(r, ((b.x-a.x) < 0) ? -(b.x-a.x): (b.x-a.x)); r = NK_MIN(r, ((b.y-a.y) < 0) ? -(b.y-a.y): (b.y-a.y)); if (r == 0.0f) { nk_draw_list_path_line_to(list, a); nk_draw_list_path_line_to(list, nk_vec2(b.x,a.y)); nk_draw_list_path_line_to(list, b); nk_draw_list_path_line_to(list, nk_vec2(a.x,b.y)); } else { nk_draw_list_path_arc_to_fast(list, nk_vec2(a.x + r, a.y + r), r, 6, 9); nk_draw_list_path_arc_to_fast(list, nk_vec2(b.x - r, a.y + r), r, 9, 12); nk_draw_list_path_arc_to_fast(list, nk_vec2(b.x - r, b.y - r), r, 0, 3); nk_draw_list_path_arc_to_fast(list, nk_vec2(a.x + r, b.y - r), r, 3, 6); } } NK_API void nk_draw_list_path_curve_to(struct nk_draw_list *list, struct nk_vec2 p2, struct nk_vec2 p3, struct nk_vec2 p4, unsigned int num_segments) { float t_step; unsigned int i_step; struct nk_vec2 p1; NK_ASSERT(list); NK_ASSERT(list->path_count); if (!list || !list->path_count) return; num_segments = NK_MAX(num_segments, 1); p1 = nk_draw_list_path_last(list); t_step = 1.0f/(float)num_segments; for (i_step = 1; i_step <= num_segments; ++i_step) { float t = t_step * (float)i_step; float u = 1.0f - t; float w1 = u*u*u; float w2 = 3*u*u*t; float w3 = 3*u*t*t; float w4 = t * t *t; float x = w1 * p1.x + w2 * p2.x + w3 * p3.x + w4 * p4.x; float y = w1 * p1.y + w2 * p2.y + w3 * p3.y + w4 * p4.y; nk_draw_list_path_line_to(list, nk_vec2(x,y)); } } NK_API void nk_draw_list_path_fill(struct nk_draw_list *list, struct nk_color color) { struct nk_vec2 *points; NK_ASSERT(list); if (!list) return; points = (struct nk_vec2*)nk_buffer_memory(list->buffer); nk_draw_list_fill_poly_convex(list, points, list->path_count, color, list->config.shape_AA); nk_draw_list_path_clear(list); } NK_API void nk_draw_list_path_stroke(struct nk_draw_list *list, struct nk_color color, enum nk_draw_list_stroke closed, float thickness) { struct nk_vec2 *points; NK_ASSERT(list); if (!list) return; points = (struct nk_vec2*)nk_buffer_memory(list->buffer); nk_draw_list_stroke_poly_line(list, points, list->path_count, color, closed, thickness, list->config.line_AA); nk_draw_list_path_clear(list); } NK_API void nk_draw_list_stroke_line(struct nk_draw_list *list, struct nk_vec2 a, struct nk_vec2 b, struct nk_color col, float thickness) { NK_ASSERT(list); if (!list || !col.a) return; if (list->line_AA == NK_ANTI_ALIASING_ON) { nk_draw_list_path_line_to(list, a); nk_draw_list_path_line_to(list, b); } else { nk_draw_list_path_line_to(list, nk_vec2_sub(a,nk_vec2(0.5f,0.5f))); nk_draw_list_path_line_to(list, nk_vec2_sub(b,nk_vec2(0.5f,0.5f))); } nk_draw_list_path_stroke(list, col, NK_STROKE_OPEN, thickness); } NK_API void nk_draw_list_fill_rect(struct nk_draw_list *list, struct nk_rect rect, struct nk_color col, float rounding) { NK_ASSERT(list); if (!list || !col.a) return; if (list->line_AA == NK_ANTI_ALIASING_ON) { nk_draw_list_path_rect_to(list, nk_vec2(rect.x, rect.y), nk_vec2(rect.x + rect.w, rect.y + rect.h), rounding); } else { nk_draw_list_path_rect_to(list, nk_vec2(rect.x-0.5f, rect.y-0.5f), nk_vec2(rect.x + rect.w, rect.y + rect.h), rounding); } nk_draw_list_path_fill(list, col); } NK_API void nk_draw_list_stroke_rect(struct nk_draw_list *list, struct nk_rect rect, struct nk_color col, float rounding, float thickness) { NK_ASSERT(list); if (!list || !col.a) return; if (list->line_AA == NK_ANTI_ALIASING_ON) { nk_draw_list_path_rect_to(list, nk_vec2(rect.x, rect.y), nk_vec2(rect.x + rect.w, rect.y + rect.h), rounding); } else { nk_draw_list_path_rect_to(list, nk_vec2(rect.x-0.5f, rect.y-0.5f), nk_vec2(rect.x + rect.w, rect.y + rect.h), rounding); } nk_draw_list_path_stroke(list, col, NK_STROKE_CLOSED, thickness); } NK_API void nk_draw_list_fill_rect_multi_color(struct nk_draw_list *list, struct nk_rect rect, struct nk_color left, struct nk_color top, struct nk_color right, struct nk_color bottom) { void *vtx; struct nk_colorf col_left, col_top; struct nk_colorf col_right, col_bottom; nk_draw_index *idx; nk_draw_index index; nk_color_fv(&col_left.r, left); nk_color_fv(&col_right.r, right); nk_color_fv(&col_top.r, top); nk_color_fv(&col_bottom.r, bottom); NK_ASSERT(list); if (!list) return; nk_draw_list_push_image(list, list->config.null.texture); index = (nk_draw_index)list->vertex_count; vtx = nk_draw_list_alloc_vertices(list, 4); idx = nk_draw_list_alloc_elements(list, 6); if (!vtx || !idx) return; idx[0] = (nk_draw_index)(index+0); idx[1] = (nk_draw_index)(index+1); idx[2] = (nk_draw_index)(index+2); idx[3] = (nk_draw_index)(index+0); idx[4] = (nk_draw_index)(index+2); idx[5] = (nk_draw_index)(index+3); vtx = nk_draw_vertex(vtx, &list->config, nk_vec2(rect.x, rect.y), list->config.null.uv, col_left); vtx = nk_draw_vertex(vtx, &list->config, nk_vec2(rect.x + rect.w, rect.y), list->config.null.uv, col_top); vtx = nk_draw_vertex(vtx, &list->config, nk_vec2(rect.x + rect.w, rect.y + rect.h), list->config.null.uv, col_right); vtx = nk_draw_vertex(vtx, &list->config, nk_vec2(rect.x, rect.y + rect.h), list->config.null.uv, col_bottom); } NK_API void nk_draw_list_fill_triangle(struct nk_draw_list *list, struct nk_vec2 a, struct nk_vec2 b, struct nk_vec2 c, struct nk_color col) { NK_ASSERT(list); if (!list || !col.a) return; nk_draw_list_path_line_to(list, a); nk_draw_list_path_line_to(list, b); nk_draw_list_path_line_to(list, c); nk_draw_list_path_fill(list, col); } NK_API void nk_draw_list_stroke_triangle(struct nk_draw_list *list, struct nk_vec2 a, struct nk_vec2 b, struct nk_vec2 c, struct nk_color col, float thickness) { NK_ASSERT(list); if (!list || !col.a) return; nk_draw_list_path_line_to(list, a); nk_draw_list_path_line_to(list, b); nk_draw_list_path_line_to(list, c); nk_draw_list_path_stroke(list, col, NK_STROKE_CLOSED, thickness); } NK_API void nk_draw_list_fill_circle(struct nk_draw_list *list, struct nk_vec2 center, float radius, struct nk_color col, unsigned int segs) { float a_max; NK_ASSERT(list); if (!list || !col.a) return; a_max = NK_PI * 2.0f * ((float)segs - 1.0f) / (float)segs; nk_draw_list_path_arc_to(list, center, radius, 0.0f, a_max, segs); nk_draw_list_path_fill(list, col); } NK_API void nk_draw_list_stroke_circle(struct nk_draw_list *list, struct nk_vec2 center, float radius, struct nk_color col, unsigned int segs, float thickness) { float a_max; NK_ASSERT(list); if (!list || !col.a) return; a_max = NK_PI * 2.0f * ((float)segs - 1.0f) / (float)segs; nk_draw_list_path_arc_to(list, center, radius, 0.0f, a_max, segs); nk_draw_list_path_stroke(list, col, NK_STROKE_CLOSED, thickness); } NK_API void nk_draw_list_stroke_curve(struct nk_draw_list *list, struct nk_vec2 p0, struct nk_vec2 cp0, struct nk_vec2 cp1, struct nk_vec2 p1, struct nk_color col, unsigned int segments, float thickness) { NK_ASSERT(list); if (!list || !col.a) return; nk_draw_list_path_line_to(list, p0); nk_draw_list_path_curve_to(list, cp0, cp1, p1, segments); nk_draw_list_path_stroke(list, col, NK_STROKE_OPEN, thickness); } NK_INTERN void nk_draw_list_push_rect_uv(struct nk_draw_list *list, struct nk_vec2 a, struct nk_vec2 c, struct nk_vec2 uva, struct nk_vec2 uvc, struct nk_color color) { void *vtx; struct nk_vec2 uvb; struct nk_vec2 uvd; struct nk_vec2 b; struct nk_vec2 d; struct nk_colorf col; nk_draw_index *idx; nk_draw_index index; NK_ASSERT(list); if (!list) return; nk_color_fv(&col.r, color); uvb = nk_vec2(uvc.x, uva.y); uvd = nk_vec2(uva.x, uvc.y); b = nk_vec2(c.x, a.y); d = nk_vec2(a.x, c.y); index = (nk_draw_index)list->vertex_count; vtx = nk_draw_list_alloc_vertices(list, 4); idx = nk_draw_list_alloc_elements(list, 6); if (!vtx || !idx) return; idx[0] = (nk_draw_index)(index+0); idx[1] = (nk_draw_index)(index+1); idx[2] = (nk_draw_index)(index+2); idx[3] = (nk_draw_index)(index+0); idx[4] = (nk_draw_index)(index+2); idx[5] = (nk_draw_index)(index+3); vtx = nk_draw_vertex(vtx, &list->config, a, uva, col); vtx = nk_draw_vertex(vtx, &list->config, b, uvb, col); vtx = nk_draw_vertex(vtx, &list->config, c, uvc, col); vtx = nk_draw_vertex(vtx, &list->config, d, uvd, col); } NK_API void nk_draw_list_add_image(struct nk_draw_list *list, struct nk_image texture, struct nk_rect rect, struct nk_color color, int flipped) //<@ r-lyeh { NK_ASSERT(list); if (!list) return; /* push new command with given texture */ nk_draw_list_push_image(list, texture.handle); if (nk_image_is_subimage(&texture)) { /* add region inside of the texture */ struct nk_vec2 uv[2]; uv[0].x = (float)texture.region[0]/(float)texture.w; uv[0].y = (float)texture.region[1]/(float)texture.h; uv[1].x = (float)(texture.region[0] + texture.region[2])/(float)texture.w; uv[1].y = (float)(texture.region[1] + texture.region[3])/(float)texture.h; nk_draw_list_push_rect_uv(list, nk_vec2(rect.x, rect.y), nk_vec2(rect.x + rect.w, rect.y + rect.h), uv[0], uv[1], color); } else { if(flipped) //<@ r-lyeh nk_draw_list_push_rect_uv(list, nk_vec2(rect.x, rect.y + rect.h), //<@ r-lyeh nk_vec2(rect.x + rect.w, rect.y), //<@ r-lyeh nk_vec2(0.0f, 0.0f), nk_vec2(1.0f, 1.0f),color); //<@ r-lyeh else nk_draw_list_push_rect_uv(list, nk_vec2(rect.x, rect.y), nk_vec2(rect.x + rect.w, rect.y + rect.h), nk_vec2(0.0f, 0.0f), nk_vec2(1.0f, 1.0f),color); } } NK_API void nk_draw_list_add_text(struct nk_draw_list *list, const struct nk_user_font *font, struct nk_rect rect, const char *text, int len, float font_height, struct nk_color fg) { float x = 0; int text_len = 0; nk_rune unicode = 0; nk_rune next = 0; int glyph_len = 0; int next_glyph_len = 0; struct nk_user_font_glyph g; NK_ASSERT(list); if (!list || !len || !text) return; if (!NK_INTERSECT(rect.x, rect.y, rect.w, rect.h, list->clip_rect.x, list->clip_rect.y, list->clip_rect.w, list->clip_rect.h)) return; nk_draw_list_push_image(list, font->texture); x = rect.x; glyph_len = nk_utf_decode(text, &unicode, len); if (!glyph_len) return; /* draw every glyph image */ fg.a = (nk_byte)((float)fg.a * list->config.global_alpha); while (text_len < len && glyph_len) { float gx, gy, gh, gw; float char_width = 0; if (unicode == NK_UTF_INVALID) break; /* query currently drawn glyph information */ next_glyph_len = nk_utf_decode(text + text_len + glyph_len, &next, (int)len - text_len); font->query(font->userdata, font_height, &g, unicode, (next == NK_UTF_INVALID) ? '\0' : next); /* calculate and draw glyph drawing rectangle and image */ gx = x + g.offset.x; gy = rect.y + g.offset.y; gw = g.width; gh = g.height; char_width = g.xadvance; nk_draw_list_push_rect_uv(list, nk_vec2(gx,gy), nk_vec2(gx + gw, gy+ gh), g.uv[0], g.uv[1], fg); /* offset next glyph */ text_len += glyph_len; x += char_width; glyph_len = next_glyph_len; unicode = next; } } NK_API nk_flags nk_convert(struct nk_context *ctx, struct nk_buffer *cmds, struct nk_buffer *vertices, struct nk_buffer *elements, const struct nk_convert_config *config) { nk_flags res = NK_CONVERT_SUCCESS; const struct nk_command *cmd; NK_ASSERT(ctx); NK_ASSERT(cmds); NK_ASSERT(vertices); NK_ASSERT(elements); NK_ASSERT(config); NK_ASSERT(config->vertex_layout); NK_ASSERT(config->vertex_size); if (!ctx || !cmds || !vertices || !elements || !config || !config->vertex_layout) return NK_CONVERT_INVALID_PARAM; nk_draw_list_setup(&ctx->draw_list, config, cmds, vertices, elements, config->line_AA, config->shape_AA); nk_foreach(cmd, ctx) { #ifdef NK_INCLUDE_COMMAND_USERDATA ctx->draw_list.userdata = cmd->userdata; #endif switch (cmd->type) { case NK_COMMAND_NOP: break; case NK_COMMAND_SCISSOR: { const struct nk_command_scissor *s = (const struct nk_command_scissor*)cmd; nk_draw_list_add_clip(&ctx->draw_list, nk_rect(s->x, s->y, s->w, s->h)); } break; case NK_COMMAND_LINE: { const struct nk_command_line *l = (const struct nk_command_line*)cmd; nk_draw_list_stroke_line(&ctx->draw_list, nk_vec2(l->begin.x, l->begin.y), nk_vec2(l->end.x, l->end.y), l->color, l->line_thickness); } break; case NK_COMMAND_CURVE: { const struct nk_command_curve *q = (const struct nk_command_curve*)cmd; nk_draw_list_stroke_curve(&ctx->draw_list, nk_vec2(q->begin.x, q->begin.y), nk_vec2(q->ctrl[0].x, q->ctrl[0].y), nk_vec2(q->ctrl[1].x, q->ctrl[1].y), nk_vec2(q->end.x, q->end.y), q->color, config->curve_segment_count, q->line_thickness); } break; case NK_COMMAND_RECT: { const struct nk_command_rect *r = (const struct nk_command_rect*)cmd; nk_draw_list_stroke_rect(&ctx->draw_list, nk_rect(r->x, r->y, r->w, r->h), r->color, (float)r->rounding, r->line_thickness); } break; case NK_COMMAND_RECT_FILLED: { const struct nk_command_rect_filled *r = (const struct nk_command_rect_filled*)cmd; nk_draw_list_fill_rect(&ctx->draw_list, nk_rect(r->x, r->y, r->w, r->h), r->color, (float)r->rounding); } break; case NK_COMMAND_RECT_MULTI_COLOR: { const struct nk_command_rect_multi_color *r = (const struct nk_command_rect_multi_color*)cmd; nk_draw_list_fill_rect_multi_color(&ctx->draw_list, nk_rect(r->x, r->y, r->w, r->h), r->left, r->top, r->right, r->bottom); } break; case NK_COMMAND_CIRCLE: { const struct nk_command_circle *c = (const struct nk_command_circle*)cmd; nk_draw_list_stroke_circle(&ctx->draw_list, nk_vec2((float)c->x + (float)c->w/2, (float)c->y + (float)c->h/2), (float)c->w/2, c->color, config->circle_segment_count, c->line_thickness); } break; case NK_COMMAND_CIRCLE_FILLED: { const struct nk_command_circle_filled *c = (const struct nk_command_circle_filled *)cmd; nk_draw_list_fill_circle(&ctx->draw_list, nk_vec2((float)c->x + (float)c->w/2, (float)c->y + (float)c->h/2), (float)c->w/2, c->color, config->circle_segment_count); } break; case NK_COMMAND_ARC: { const struct nk_command_arc *c = (const struct nk_command_arc*)cmd; nk_draw_list_path_line_to(&ctx->draw_list, nk_vec2(c->cx, c->cy)); nk_draw_list_path_arc_to(&ctx->draw_list, nk_vec2(c->cx, c->cy), c->r, c->a[0], c->a[1], config->arc_segment_count); nk_draw_list_path_stroke(&ctx->draw_list, c->color, NK_STROKE_CLOSED, c->line_thickness); } break; case NK_COMMAND_ARC_FILLED: { const struct nk_command_arc_filled *c = (const struct nk_command_arc_filled*)cmd; nk_draw_list_path_line_to(&ctx->draw_list, nk_vec2(c->cx, c->cy)); nk_draw_list_path_arc_to(&ctx->draw_list, nk_vec2(c->cx, c->cy), c->r, c->a[0], c->a[1], config->arc_segment_count); nk_draw_list_path_fill(&ctx->draw_list, c->color); } break; case NK_COMMAND_TRIANGLE: { const struct nk_command_triangle *t = (const struct nk_command_triangle*)cmd; nk_draw_list_stroke_triangle(&ctx->draw_list, nk_vec2(t->a.x, t->a.y), nk_vec2(t->b.x, t->b.y), nk_vec2(t->c.x, t->c.y), t->color, t->line_thickness); } break; case NK_COMMAND_TRIANGLE_FILLED: { const struct nk_command_triangle_filled *t = (const struct nk_command_triangle_filled*)cmd; nk_draw_list_fill_triangle(&ctx->draw_list, nk_vec2(t->a.x, t->a.y), nk_vec2(t->b.x, t->b.y), nk_vec2(t->c.x, t->c.y), t->color); } break; case NK_COMMAND_POLYGON: { int i; const struct nk_command_polygon*p = (const struct nk_command_polygon*)cmd; for (i = 0; i < p->point_count; ++i) { struct nk_vec2 pnt = nk_vec2((float)p->points[i].x, (float)p->points[i].y); nk_draw_list_path_line_to(&ctx->draw_list, pnt); } nk_draw_list_path_stroke(&ctx->draw_list, p->color, NK_STROKE_CLOSED, p->line_thickness); } break; case NK_COMMAND_POLYGON_FILLED: { int i; const struct nk_command_polygon_filled *p = (const struct nk_command_polygon_filled*)cmd; for (i = 0; i < p->point_count; ++i) { struct nk_vec2 pnt = nk_vec2((float)p->points[i].x, (float)p->points[i].y); nk_draw_list_path_line_to(&ctx->draw_list, pnt); } nk_draw_list_path_fill(&ctx->draw_list, p->color); } break; case NK_COMMAND_POLYLINE: { int i; const struct nk_command_polyline *p = (const struct nk_command_polyline*)cmd; for (i = 0; i < p->point_count; ++i) { struct nk_vec2 pnt = nk_vec2((float)p->points[i].x, (float)p->points[i].y); nk_draw_list_path_line_to(&ctx->draw_list, pnt); } nk_draw_list_path_stroke(&ctx->draw_list, p->color, NK_STROKE_OPEN, p->line_thickness); } break; case NK_COMMAND_TEXT: { const struct nk_command_text *t = (const struct nk_command_text*)cmd; nk_draw_list_add_text(&ctx->draw_list, t->font, nk_rect(t->x, t->y, t->w, t->h), t->string, t->length, t->height, t->foreground); } break; case NK_COMMAND_IMAGE: { const struct nk_command_image *i = (const struct nk_command_image*)cmd; nk_draw_list_add_image(&ctx->draw_list, i->img, nk_rect(i->x, i->y, i->w, i->h), i->col, 0); } break; case NK_COMMAND_IMAGE_FLIPPED: { //< @r-lyeh const struct nk_command_image *i = (const struct nk_command_image*)cmd; //< @r-lyeh nk_draw_list_add_image(&ctx->draw_list, i->img, nk_rect(i->x, i->y, i->w, i->h), i->col, 1); //< @r-lyeh } break; //< @r-lyeh case NK_COMMAND_CUSTOM: { const struct nk_command_custom *c = (const struct nk_command_custom*)cmd; c->callback(&ctx->draw_list, c->x, c->y, c->w, c->h, c->callback_data); } break; default: break; } } res |= (cmds->needed > cmds->allocated + (cmds->memory.size - cmds->size)) ? NK_CONVERT_COMMAND_BUFFER_FULL: 0; res |= (vertices->needed > vertices->allocated) ? NK_CONVERT_VERTEX_BUFFER_FULL: 0; res |= (elements->needed > elements->allocated) ? NK_CONVERT_ELEMENT_BUFFER_FULL: 0; return res; } NK_API const struct nk_draw_command* nk__draw_begin(const struct nk_context *ctx, const struct nk_buffer *buffer) { return nk__draw_list_begin(&ctx->draw_list, buffer); } NK_API const struct nk_draw_command* nk__draw_end(const struct nk_context *ctx, const struct nk_buffer *buffer) { return nk__draw_list_end(&ctx->draw_list, buffer); } NK_API const struct nk_draw_command* nk__draw_next(const struct nk_draw_command *cmd, const struct nk_buffer *buffer, const struct nk_context *ctx) { return nk__draw_list_next(cmd, buffer, &ctx->draw_list); } #endif /* stb_rect_pack.h - v1.01 - public domain - rectangle packing */ /* Sean Barrett 2014 */ /* */ /* Useful for e.g. packing rectangular textures into an atlas. */ /* Does not do rotation. */ /* */ /* Before #including, */ /* */ /* #define STB_RECT_PACK_IMPLEMENTATION */ /* */ /* in the file that you want to have the implementation. */ /* */ /* Not necessarily the awesomest packing method, but better than */ /* the totally naive one in stb_truetype (which is primarily what */ /* this is meant to replace). */ /* */ /* Has only had a few tests run, may have issues. */ /* */ /* More docs to come. */ /* */ /* No memory allocations; uses qsort() and assert() from stdlib. */ /* Can override those by defining STBRP_SORT and STBRP_ASSERT. */ /* */ /* This library currently uses the Skyline Bottom-Left algorithm. */ /* */ /* Please note: better rectangle packers are welcome! Please */ /* implement them to the same API, but with a different init */ /* function. */ /* */ /* Credits */ /* */ /* Library */ /* Sean Barrett */ /* Minor features */ /* Martins Mozeiko */ /* github:IntellectualKitty */ /* */ /* Bugfixes / warning fixes */ /* Jeremy Jaussaud */ /* Fabian Giesen */ /* */ /* Version history: */ /* */ /* 1.01 (2021-07-11) always use large rect mode, expose STBRP__MAXVAL in public section */ /* 1.00 (2019-02-25) avoid small space waste; gracefully fail too-wide rectangles */ /* 0.99 (2019-02-07) warning fixes */ /* 0.11 (2017-03-03) return packing success/fail result */ /* 0.10 (2016-10-25) remove cast-away-const to avoid warnings */ /* 0.09 (2016-08-27) fix compiler warnings */ /* 0.08 (2015-09-13) really fix bug with empty rects (w=0 or h=0) */ /* 0.07 (2015-09-13) fix bug with empty rects (w=0 or h=0) */ /* 0.06 (2015-04-15) added STBRP_SORT to allow replacing qsort */ /* 0.05: added STBRP_ASSERT to allow replacing assert */ /* 0.04: fixed minor bug in STBRP_LARGE_RECTS support */ /* 0.01: initial release */ /* */ /* LICENSE */ /* */ /* See end of file for license information. */ /* //////////////////////////////////////////////////////////////////////////// */ /* */ /* INCLUDE SECTION */ /* */ #ifndef STB_INCLUDE_STB_RECT_PACK_H #define STB_INCLUDE_STB_RECT_PACK_H #define STB_RECT_PACK_VERSION 1 #ifdef STBRP_STATIC #define STBRP_DEF static #else #define STBRP_DEF extern #endif #ifdef __cplusplus extern "C" { #endif typedef struct stbrp_context stbrp_context; typedef struct stbrp_node stbrp_node; typedef struct stbrp_rect stbrp_rect; typedef int stbrp_coord; #define STBRP__MAXVAL 0x7fffffff /* Mostly for internal use, but this is the maximum supported coordinate value. */ STBRP_DEF int stbrp_pack_rects (stbrp_context *context, stbrp_rect *rects, int num_rects); /* Assign packed locations to rectangles. The rectangles are of type */ /* 'stbrp_rect' defined below, stored in the array 'rects', and there */ /* are 'num_rects' many of them. */ /* */ /* Rectangles which are successfully packed have the 'was_packed' flag */ /* set to a non-zero value and 'x' and 'y' store the minimum location */ /* on each axis (i.e. bottom-left in cartesian coordinates, top-left */ /* if you imagine y increasing downwards). Rectangles which do not fit */ /* have the 'was_packed' flag set to 0. */ /* */ /* You should not try to access the 'rects' array from another thread */ /* while this function is running, as the function temporarily reorders */ /* the array while it executes. */ /* */ /* To pack into another rectangle, you need to call stbrp_init_target */ /* again. To continue packing into the same rectangle, you can call */ /* this function again. Calling this multiple times with multiple rect */ /* arrays will probably produce worse packing results than calling it */ /* a single time with the full rectangle array, but the option is */ /* available. */ /* */ /* The function returns 1 if all of the rectangles were successfully */ /* packed and 0 otherwise. */ struct stbrp_rect { /* reserved for your use: */ int id; /* input: */ stbrp_coord w, h; /* output: */ stbrp_coord x, y; int was_packed; /* non-zero if valid packing */ }; /* 16 bytes, nominally */ STBRP_DEF void stbrp_init_target (stbrp_context *context, int width, int height, stbrp_node *nodes, int num_nodes); /* Initialize a rectangle packer to: */ /* pack a rectangle that is 'width' by 'height' in dimensions */ /* using temporary storage provided by the array 'nodes', which is 'num_nodes' long */ /* */ /* You must call this function every time you start packing into a new target. */ /* */ /* There is no "shutdown" function. The 'nodes' memory must stay valid for */ /* the following stbrp_pack_rects() call (or calls), but can be freed after */ /* the call (or calls) finish. */ /* */ /* Note: to guarantee best results, either: */ /* 1. make sure 'num_nodes' >= 'width' */ /* or 2. call stbrp_allow_out_of_mem() defined below with 'allow_out_of_mem = 1' */ /* */ /* If you don't do either of the above things, widths will be quantized to multiples */ /* of small integers to guarantee the algorithm doesn't run out of temporary storage. */ /* */ /* If you do #2, then the non-quantized algorithm will be used, but the algorithm */ /* may run out of temporary storage and be unable to pack some rectangles. */ STBRP_DEF void stbrp_setup_allow_out_of_mem (stbrp_context *context, int allow_out_of_mem); /* Optionally call this function after init but before doing any packing to */ /* change the handling of the out-of-temp-memory scenario, described above. */ /* If you call init again, this will be reset to the default (false). */ STBRP_DEF void stbrp_setup_heuristic (stbrp_context *context, int heuristic); /* Optionally select which packing heuristic the library should use. Different */ /* heuristics will produce better/worse results for different data sets. */ /* If you call init again, this will be reset to the default. */ enum { STBRP_HEURISTIC_Skyline_default=0, STBRP_HEURISTIC_Skyline_BL_sortHeight = STBRP_HEURISTIC_Skyline_default, STBRP_HEURISTIC_Skyline_BF_sortHeight }; /* //////////////////////////////////////////////////////////////////////////// */ /* */ /* the details of the following structures don't matter to you, but they must */ /* be visible so you can handle the memory allocations for them */ struct stbrp_node { stbrp_coord x,y; stbrp_node *next; }; struct stbrp_context { int width; int height; int align; int init_mode; int heuristic; int num_nodes; stbrp_node *active_head; stbrp_node *free_head; stbrp_node extra[2]; /* we allocate two extra nodes so optimal user-node-count is 'width' not 'width+2' */ }; #ifdef __cplusplus } #endif #endif /* //////////////////////////////////////////////////////////////////////////// */ /* */ /* IMPLEMENTATION SECTION */ /* */ #ifdef STB_RECT_PACK_IMPLEMENTATION #ifndef STBRP_SORT #include #define STBRP_SORT qsort #endif #ifndef STBRP_ASSERT #include #define STBRP_ASSERT assert #endif #ifdef _MSC_VER #define STBRP__NOTUSED(v) (void)(v) #define STBRP__CDECL __cdecl #else #define STBRP__NOTUSED(v) (void)sizeof(v) #define STBRP__CDECL #endif enum { STBRP__INIT_skyline = 1 }; STBRP_DEF void stbrp_setup_heuristic(stbrp_context *context, int heuristic) { switch (context->init_mode) { case STBRP__INIT_skyline: STBRP_ASSERT(heuristic == STBRP_HEURISTIC_Skyline_BL_sortHeight || heuristic == STBRP_HEURISTIC_Skyline_BF_sortHeight); context->heuristic = heuristic; break; default: STBRP_ASSERT(0); } } STBRP_DEF void stbrp_setup_allow_out_of_mem(stbrp_context *context, int allow_out_of_mem) { if (allow_out_of_mem) /* if it's ok to run out of memory, then don't bother aligning them; */ /* this gives better packing, but may fail due to OOM (even though */ /* the rectangles easily fit). @TODO a smarter approach would be to only */ /* quantize once we've hit OOM, then we could get rid of this parameter. */ context->align = 1; else { /* if it's not ok to run out of memory, then quantize the widths */ /* so that num_nodes is always enough nodes. */ /* */ /* I.e. num_nodes * align >= width */ /* align >= width / num_nodes */ /* align = ceil(width/num_nodes) */ context->align = (context->width + context->num_nodes-1) / context->num_nodes; } } STBRP_DEF void stbrp_init_target(stbrp_context *context, int width, int height, stbrp_node *nodes, int num_nodes) { int i; for (i=0; i < num_nodes-1; ++i) nodes[i].next = &nodes[i+1]; nodes[i].next = NULL; context->init_mode = STBRP__INIT_skyline; context->heuristic = STBRP_HEURISTIC_Skyline_default; context->free_head = &nodes[0]; context->active_head = &context->extra[0]; context->width = width; context->height = height; context->num_nodes = num_nodes; stbrp_setup_allow_out_of_mem(context, 0); /* node 0 is the full width, node 1 is the sentinel (lets us not store width explicitly) */ context->extra[0].x = 0; context->extra[0].y = 0; context->extra[0].next = &context->extra[1]; context->extra[1].x = (stbrp_coord) width; context->extra[1].y = (1<<30); context->extra[1].next = NULL; } /* find minimum y position if it starts at x1 */ static int stbrp__skyline_find_min_y(stbrp_context *c, stbrp_node *first, int x0, int width, int *pwaste) { stbrp_node *node = first; int x1 = x0 + width; int min_y, visited_width, waste_area; STBRP__NOTUSED(c); STBRP_ASSERT(first->x <= x0); #if 0 /* skip in case we're past the node */ while (node->next->x <= x0) ++node; #else STBRP_ASSERT(node->next->x > x0); /* we ended up handling this in the caller for efficiency */ #endif STBRP_ASSERT(node->x <= x0); min_y = 0; waste_area = 0; visited_width = 0; while (node->x < x1) { if (node->y > min_y) { /* raise min_y higher. */ /* we've accounted for all waste up to min_y, */ /* but we'll now add more waste for everything we've visted */ waste_area += visited_width * (node->y - min_y); min_y = node->y; /* the first time through, visited_width might be reduced */ if (node->x < x0) visited_width += node->next->x - x0; else visited_width += node->next->x - node->x; } else { /* add waste area */ int under_width = node->next->x - node->x; if (under_width + visited_width > width) under_width = width - visited_width; waste_area += under_width * (min_y - node->y); visited_width += under_width; } node = node->next; } *pwaste = waste_area; return min_y; } typedef struct { int x,y; stbrp_node **prev_link; } stbrp__findresult; static stbrp__findresult stbrp__skyline_find_best_pos(stbrp_context *c, int width, int height) { int best_waste = (1<<30), best_x, best_y = (1 << 30); stbrp__findresult fr; stbrp_node **prev, *node, *tail, **best = NULL; /* align to multiple of c->align */ width = (width + c->align - 1); width -= width % c->align; STBRP_ASSERT(width % c->align == 0); /* if it can't possibly fit, bail immediately */ if (width > c->width || height > c->height) { fr.prev_link = NULL; fr.x = fr.y = 0; return fr; } node = c->active_head; prev = &c->active_head; while (node->x + width <= c->width) { int y,waste; y = stbrp__skyline_find_min_y(c, node, node->x, width, &waste); if (c->heuristic == STBRP_HEURISTIC_Skyline_BL_sortHeight) { /* actually just want to test BL */ /* bottom left */ if (y < best_y) { best_y = y; best = prev; } } else { /* best-fit */ if (y + height <= c->height) { /* can only use it if it first vertically */ if (y < best_y || (y == best_y && waste < best_waste)) { best_y = y; best_waste = waste; best = prev; } } } prev = &node->next; node = node->next; } best_x = (best == NULL) ? 0 : (*best)->x; /* if doing best-fit (BF), we also have to try aligning right edge to each node position */ /* */ /* e.g, if fitting */ /* */ /* ____________________ */ /* |____________________| */ /* */ /* into */ /* */ /* | | */ /* | ____________| */ /* |____________| */ /* */ /* then right-aligned reduces waste, but bottom-left BL is always chooses left-aligned */ /* */ /* This makes BF take about 2x the time */ if (c->heuristic == STBRP_HEURISTIC_Skyline_BF_sortHeight) { tail = c->active_head; node = c->active_head; prev = &c->active_head; /* find first node that's admissible */ while (tail->x < width) tail = tail->next; while (tail) { int xpos = tail->x - width; int y,waste; STBRP_ASSERT(xpos >= 0); /* find the left position that matches this */ while (node->next->x <= xpos) { prev = &node->next; node = node->next; } STBRP_ASSERT(node->next->x > xpos && node->x <= xpos); y = stbrp__skyline_find_min_y(c, node, xpos, width, &waste); if (y + height <= c->height) { if (y <= best_y) { if (y < best_y || waste < best_waste || (waste==best_waste && xpos < best_x)) { best_x = xpos; STBRP_ASSERT(y <= best_y); best_y = y; best_waste = waste; best = prev; } } } tail = tail->next; } } fr.prev_link = best; fr.x = best_x; fr.y = best_y; return fr; } static stbrp__findresult stbrp__skyline_pack_rectangle(stbrp_context *context, int width, int height) { /* find best position according to heuristic */ stbrp__findresult res = stbrp__skyline_find_best_pos(context, width, height); stbrp_node *node, *cur; /* bail if: */ /* 1. it failed */ /* 2. the best node doesn't fit (we don't always check this) */ /* 3. we're out of memory */ if (res.prev_link == NULL || res.y + height > context->height || context->free_head == NULL) { res.prev_link = NULL; return res; } /* on success, create new node */ node = context->free_head; node->x = (stbrp_coord) res.x; node->y = (stbrp_coord) (res.y + height); context->free_head = node->next; /* insert the new node into the right starting point, and */ /* let 'cur' point to the remaining nodes needing to be */ /* stiched back in */ cur = *res.prev_link; if (cur->x < res.x) { /* preserve the existing one, so start testing with the next one */ stbrp_node *next = cur->next; cur->next = node; cur = next; } else { *res.prev_link = node; } /* from here, traverse cur and free the nodes, until we get to one */ /* that shouldn't be freed */ while (cur->next && cur->next->x <= res.x + width) { stbrp_node *next = cur->next; /* move the current node to the free list */ cur->next = context->free_head; context->free_head = cur; cur = next; } /* stitch the list back in */ node->next = cur; if (cur->x < res.x + width) cur->x = (stbrp_coord) (res.x + width); #ifdef _DEBUG cur = context->active_head; while (cur->x < context->width) { STBRP_ASSERT(cur->x < cur->next->x); cur = cur->next; } STBRP_ASSERT(cur->next == NULL); { int count=0; cur = context->active_head; while (cur) { cur = cur->next; ++count; } cur = context->free_head; while (cur) { cur = cur->next; ++count; } STBRP_ASSERT(count == context->num_nodes+2); } #endif return res; } static int STBRP__CDECL rect_height_compare(const void *a, const void *b) { const stbrp_rect *p = (const stbrp_rect *) a; const stbrp_rect *q = (const stbrp_rect *) b; if (p->h > q->h) return -1; if (p->h < q->h) return 1; return (p->w > q->w) ? -1 : (p->w < q->w); } static int STBRP__CDECL rect_original_order(const void *a, const void *b) { const stbrp_rect *p = (const stbrp_rect *) a; const stbrp_rect *q = (const stbrp_rect *) b; return (p->was_packed < q->was_packed) ? -1 : (p->was_packed > q->was_packed); } STBRP_DEF int stbrp_pack_rects(stbrp_context *context, stbrp_rect *rects, int num_rects) { int i, all_rects_packed = 1; /* we use the 'was_packed' field internally to allow sorting/unsorting */ for (i=0; i < num_rects; ++i) { rects[i].was_packed = i; } /* sort according to heuristic */ STBRP_SORT(rects, num_rects, sizeof(rects[0]), rect_height_compare); for (i=0; i < num_rects; ++i) { if (rects[i].w == 0 || rects[i].h == 0) { rects[i].x = rects[i].y = 0; /* empty rect needs no space */ } else { stbrp__findresult fr = stbrp__skyline_pack_rectangle(context, rects[i].w, rects[i].h); if (fr.prev_link) { rects[i].x = (stbrp_coord) fr.x; rects[i].y = (stbrp_coord) fr.y; } else { rects[i].x = rects[i].y = STBRP__MAXVAL; } } } /* unsort */ STBRP_SORT(rects, num_rects, sizeof(rects[0]), rect_original_order); /* set was_packed flags and all_rects_packed status */ for (i=0; i < num_rects; ++i) { rects[i].was_packed = !(rects[i].x == STBRP__MAXVAL && rects[i].y == STBRP__MAXVAL); if (!rects[i].was_packed) all_rects_packed = 0; } /* return the all_rects_packed status */ return all_rects_packed; } #endif /* ------------------------------------------------------------------------------ This software is available under 2 licenses -- choose whichever you prefer. ------------------------------------------------------------------------------ ALTERNATIVE A - MIT License Copyright (c) 2017 Sean Barrett 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. ------------------------------------------------------------------------------ ALTERNATIVE B - Public Domain (www.unlicense.org) This is free and unencumbered software released into the public domain. Anyone is free to copy, modify, publish, use, compile, sell, or distribute this software, either in source code form or as a compiled binary, for any purpose, commercial or non-commercial, and by any means. In jurisdictions that recognize copyright laws, the author or authors of this software dedicate any and all copyright interest in the software to the public domain. We make this dedication for the benefit of the public at large and to the detriment of our heirs and successors. We intend this dedication to be an overt act of relinquishment in perpetuity of all present and future rights to this software under copyright law. 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 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. ------------------------------------------------------------------------------ */ /* stb_truetype.h - v1.26 - public domain */ /* authored from 2009-2021 by Sean Barrett / RAD Game Tools */ /* */ /* ======================================================================= */ /* */ /* NO SECURITY GUARANTEE -- DO NOT USE THIS ON UNTRUSTED FONT FILES */ /* */ /* This library does no range checking of the offsets found in the file, */ /* meaning an attacker can use it to read arbitrary memory. */ /* */ /* ======================================================================= */ /* */ /* This library processes TrueType files: */ /* parse files */ /* extract glyph metrics */ /* extract glyph shapes */ /* render glyphs to one-channel bitmaps with antialiasing (box filter) */ /* render glyphs to one-channel SDF bitmaps (signed-distance field/function) */ /* */ /* Todo: */ /* non-MS cmaps */ /* crashproof on bad data */ /* hinting? (no longer patented) */ /* cleartype-style AA? */ /* optimize: use simple memory allocator for intermediates */ /* optimize: build edge-list directly from curves */ /* optimize: rasterize directly from curves? */ /* */ /* ADDITIONAL CONTRIBUTORS */ /* */ /* Mikko Mononen: compound shape support, more cmap formats */ /* Tor Andersson: kerning, subpixel rendering */ /* Dougall Johnson: OpenType / Type 2 font handling */ /* Daniel Ribeiro Maciel: basic GPOS-based kerning */ /* */ /* Misc other: */ /* Ryan Gordon */ /* Simon Glass */ /* github:IntellectualKitty */ /* Imanol Celaya */ /* Daniel Ribeiro Maciel */ /* */ /* Bug/warning reports/fixes: */ /* "Zer" on mollyrocket Fabian "ryg" Giesen github:NiLuJe */ /* Cass Everitt Martins Mozeiko github:aloucks */ /* stoiko (Haemimont Games) Cap Petschulat github:oyvindjam */ /* Brian Hook Omar Cornut github:vassvik */ /* Walter van Niftrik Ryan Griege */ /* David Gow Peter LaValle */ /* David Given Sergey Popov */ /* Ivan-Assen Ivanov Giumo X. Clanjor */ /* Anthony Pesch Higor Euripedes */ /* Johan Duparc Thomas Fields */ /* Hou Qiming Derek Vinyard */ /* Rob Loach Cort Stratton */ /* Kenney Phillis Jr. Brian Costabile */ /* Ken Voskuil (kaesve) */ /* */ /* VERSION HISTORY */ /* */ /* 1.26 (2021-08-28) fix broken rasterizer */ /* 1.25 (2021-07-11) many fixes */ /* 1.24 (2020-02-05) fix warning */ /* 1.23 (2020-02-02) query SVG data for glyphs; query whole kerning table (but only kern not GPOS) */ /* 1.22 (2019-08-11) minimize missing-glyph duplication; fix kerning if both 'GPOS' and 'kern' are defined */ /* 1.21 (2019-02-25) fix warning */ /* 1.20 (2019-02-07) PackFontRange skips missing codepoints; GetScaleFontVMetrics() */ /* 1.19 (2018-02-11) GPOS kerning, STBTT_fmod */ /* 1.18 (2018-01-29) add missing function */ /* 1.17 (2017-07-23) make more arguments const; doc fix */ /* 1.16 (2017-07-12) SDF support */ /* 1.15 (2017-03-03) make more arguments const */ /* 1.14 (2017-01-16) num-fonts-in-TTC function */ /* 1.13 (2017-01-02) support OpenType fonts, certain Apple fonts */ /* 1.12 (2016-10-25) suppress warnings about casting away const with -Wcast-qual */ /* 1.11 (2016-04-02) fix unused-variable warning */ /* 1.10 (2016-04-02) user-defined fabs(); rare memory leak; remove duplicate typedef */ /* 1.09 (2016-01-16) warning fix; avoid crash on outofmem; use allocation userdata properly */ /* 1.08 (2015-09-13) document stbtt_Rasterize(); fixes for vertical & horizontal edges */ /* 1.07 (2015-08-01) allow PackFontRanges to accept arrays of sparse codepoints; */ /* variant PackFontRanges to pack and render in separate phases; */ /* fix stbtt_GetFontOFfsetForIndex (never worked for non-0 input?); */ /* fixed an assert() bug in the new rasterizer */ /* replace assert() with STBTT_assert() in new rasterizer */ /* */ /* Full history can be found at the end of this file. */ /* */ /* LICENSE */ /* */ /* See end of file for license information. */ /* */ /* USAGE */ /* */ /* Include this file in whatever places need to refer to it. In ONE C/C++ */ /* file, write: */ /* #define STB_TRUETYPE_IMPLEMENTATION */ /* before the #include of this file. This expands out the actual */ /* implementation into that C/C++ file. */ /* */ /* To make the implementation private to the file that generates the implementation, */ /* #define STBTT_STATIC */ /* */ /* Simple 3D API (don't ship this, but it's fine for tools and quick start) */ /* stbtt_BakeFontBitmap() -- bake a font to a bitmap for use as texture */ /* stbtt_GetBakedQuad() -- compute quad to draw for a given char */ /* */ /* Improved 3D API (more shippable): */ /* #include "stb_rect_pack.h" -- optional, but you really want it */ /* stbtt_PackBegin() */ /* stbtt_PackSetOversampling() -- for improved quality on small fonts */ /* stbtt_PackFontRanges() -- pack and renders */ /* stbtt_PackEnd() */ /* stbtt_GetPackedQuad() */ /* */ /* "Load" a font file from a memory buffer (you have to keep the buffer loaded) */ /* stbtt_InitFont() */ /* stbtt_GetFontOffsetForIndex() -- indexing for TTC font collections */ /* stbtt_GetNumberOfFonts() -- number of fonts for TTC font collections */ /* */ /* Render a unicode codepoint to a bitmap */ /* stbtt_GetCodepointBitmap() -- allocates and returns a bitmap */ /* stbtt_MakeCodepointBitmap() -- renders into bitmap you provide */ /* stbtt_GetCodepointBitmapBox() -- how big the bitmap must be */ /* */ /* Character advance/positioning */ /* stbtt_GetCodepointHMetrics() */ /* stbtt_GetFontVMetrics() */ /* stbtt_GetFontVMetricsOS2() */ /* stbtt_GetCodepointKernAdvance() */ /* */ /* Starting with version 1.06, the rasterizer was replaced with a new, */ /* faster and generally-more-precise rasterizer. The new rasterizer more */ /* accurately measures pixel coverage for anti-aliasing, except in the case */ /* where multiple shapes overlap, in which case it overestimates the AA pixel */ /* coverage. Thus, anti-aliasing of intersecting shapes may look wrong. If */ /* this turns out to be a problem, you can re-enable the old rasterizer with */ /* #define STBTT_RASTERIZER_VERSION 1 */ /* which will incur about a 15% speed hit. */ /* */ /* ADDITIONAL DOCUMENTATION */ /* */ /* Immediately after this block comment are a series of sample programs. */ /* */ /* After the sample programs is the "header file" section. This section */ /* includes documentation for each API function. */ /* */ /* Some important concepts to understand to use this library: */ /* */ /* Codepoint */ /* Characters are defined by unicode codepoints, e.g. 65 is */ /* uppercase A, 231 is lowercase c with a cedilla, 0x7e30 is */ /* the hiragana for "ma". */ /* */ /* Glyph */ /* A visual character shape (every codepoint is rendered as */ /* some glyph) */ /* */ /* Glyph index */ /* A font-specific integer ID representing a glyph */ /* */ /* Baseline */ /* Glyph shapes are defined relative to a baseline, which is the */ /* bottom of uppercase characters. Characters extend both above */ /* and below the baseline. */ /* */ /* Current Point */ /* As you draw text to the screen, you keep track of a "current point" */ /* which is the origin of each character. The current point's vertical */ /* position is the baseline. Even "baked fonts" use this model. */ /* */ /* Vertical Font Metrics */ /* The vertical qualities of the font, used to vertically position */ /* and space the characters. See docs for stbtt_GetFontVMetrics. */ /* */ /* Font Size in Pixels or Points */ /* The preferred interface for specifying font sizes in stb_truetype */ /* is to specify how tall the font's vertical extent should be in pixels. */ /* If that sounds good enough, skip the next paragraph. */ /* */ /* Most font APIs instead use "points", which are a common typographic */ /* measurement for describing font size, defined as 72 points per inch. */ /* stb_truetype provides a point API for compatibility. However, true */ /* "per inch" conventions don't make much sense on computer displays */ /* since different monitors have different number of pixels per */ /* inch. For example, Windows traditionally uses a convention that */ /* there are 96 pixels per inch, thus making 'inch' measurements have */ /* nothing to do with inches, and thus effectively defining a point to */ /* be 1.333 pixels. Additionally, the TrueType font data provides */ /* an explicit scale factor to scale a given font's glyphs to points, */ /* but the author has observed that this scale factor is often wrong */ /* for non-commercial fonts, thus making fonts scaled in points */ /* according to the TrueType spec incoherently sized in practice. */ /* */ /* DETAILED USAGE: */ /* */ /* Scale: */ /* Select how high you want the font to be, in points or pixels. */ /* Call ScaleForPixelHeight or ScaleForMappingEmToPixels to compute */ /* a scale factor SF that will be used by all other functions. */ /* */ /* Baseline: */ /* You need to select a y-coordinate that is the baseline of where */ /* your text will appear. Call GetFontBoundingBox to get the baseline-relative */ /* bounding box for all characters. SF*-y0 will be the distance in pixels */ /* that the worst-case character could extend above the baseline, so if */ /* you want the top edge of characters to appear at the top of the */ /* screen where y=0, then you would set the baseline to SF*-y0. */ /* */ /* Current point: */ /* Set the current point where the first character will appear. The */ /* first character could extend left of the current point; this is font */ /* dependent. You can either choose a current point that is the leftmost */ /* point and hope, or add some padding, or check the bounding box or */ /* left-side-bearing of the first character to be displayed and set */ /* the current point based on that. */ /* */ /* Displaying a character: */ /* Compute the bounding box of the character. It will contain signed values */ /* relative to . I.e. if it returns x0,y0,x1,y1, */ /* then the character should be displayed in the rectangle from */ /* to = 32 && *text < 128) { stbtt_aligned_quad q; stbtt_GetBakedQuad(cdata, 512,512, *text-32, &x,&y,&q,1);/* 1=opengl & d3d10+,0=d3d9 */ glTexCoord2f(q.s0,q.t0); glVertex2f(q.x0,q.y0); glTexCoord2f(q.s1,q.t0); glVertex2f(q.x1,q.y0); glTexCoord2f(q.s1,q.t1); glVertex2f(q.x1,q.y1); glTexCoord2f(q.s0,q.t1); glVertex2f(q.x0,q.y1); } ++text; } glEnd(); } #endif /* */ /* */ /* //////////////////////////////////////////////////////////////////////////// */ /* */ /* Complete program (this compiles): get a single bitmap, print as ASCII art */ /* */ #if 0 #include #define STB_TRUETYPE_IMPLEMENTATION /* force following include to generate implementation */ #include "stb_truetype.h" char ttf_buffer[1<<25]; int main(int argc, char **argv) { stbtt_fontinfo font; unsigned char *bitmap; int w,h,i,j,c = (argc > 1 ? atoi(argv[1]) : 'a'), s = (argc > 2 ? atoi(argv[2]) : 20); fread(ttf_buffer, 1, 1<<25, fopen(argc > 3 ? argv[3] : "c:/windows/fonts/arialbd.ttf", "rb")); stbtt_InitFont(&font, ttf_buffer, stbtt_GetFontOffsetForIndex(ttf_buffer,0)); bitmap = stbtt_GetCodepointBitmap(&font, 0,stbtt_ScaleForPixelHeight(&font, s), c, &w, &h, 0,0); for (j=0; j < h; ++j) { for (i=0; i < w; ++i) putchar(" .:ioVM@"[bitmap[j*w+i]>>5]); putchar('\n'); } return 0; } #endif /* */ /* Output: */ /* */ /* .ii. */ /* @@@@@@. */ /* V@Mio@@o */ /* :i. V@V */ /* :oM@@M */ /* :@@@MM@M */ /* @@o o@M */ /* :@@. M@M */ /* @@@o@@@@ */ /* :M@@V:@@. */ /* */ /* //////////////////////////////////////////////////////////////////////////// */ /* */ /* Complete program: print "Hello World!" banner, with bugs */ /* */ #if 0 char buffer[24<<20]; unsigned char screen[20][79]; int main(int arg, char **argv) { stbtt_fontinfo font; int i,j,ascent,baseline,ch=0; float scale, xpos=2; /* leave a little padding in case the character extends left */ char *text = "Heljo World!"; /* intentionally misspelled to show 'lj' brokenness */ fread(buffer, 1, 1000000, fopen("c:/windows/fonts/arialbd.ttf", "rb")); stbtt_InitFont(&font, buffer, 0); scale = stbtt_ScaleForPixelHeight(&font, 15); stbtt_GetFontVMetrics(&font, &ascent,0,0); baseline = (int) (ascent*scale); while (text[ch]) { int advance,lsb,x0,y0,x1,y1; float x_shift = xpos - (float) floor(xpos); stbtt_GetCodepointHMetrics(&font, text[ch], &advance, &lsb); stbtt_GetCodepointBitmapBoxSubpixel(&font, text[ch], scale,scale,x_shift,0, &x0,&y0,&x1,&y1); stbtt_MakeCodepointBitmapSubpixel(&font, &screen[baseline + y0][(int) xpos + x0], x1-x0,y1-y0, 79, scale,scale,x_shift,0, text[ch]); /* note that this stomps the old data, so where character boxes overlap (e.g. 'lj') it's wrong */ /* because this API is really for baking character bitmaps into textures. if you want to render */ /* a sequence of characters, you really need to render each bitmap to a temp buffer, then */ /* "alpha blend" that into the working buffer */ xpos += (advance * scale); if (text[ch+1]) xpos += scale*stbtt_GetCodepointKernAdvance(&font, text[ch],text[ch+1]); ++ch; } for (j=0; j < 20; ++j) { for (i=0; i < 78; ++i) putchar(" .:ioVM@"[screen[j][i]>>5]); putchar('\n'); } return 0; } #endif /* //////////////////////////////////////////////////////////////////////////// */ /* //////////////////////////////////////////////////////////////////////////// */ /* // */ /* // INTEGRATION WITH YOUR CODEBASE */ /* // */ /* // The following sections allow you to supply alternate definitions */ /* // of C library functions used by stb_truetype, e.g. if you don't */ /* // link with the C runtime library. */ #ifdef STB_TRUETYPE_IMPLEMENTATION /* #define your own (u)stbtt_int8/16/32 before including to override this */ #ifndef stbtt_uint8 typedef unsigned char stbtt_uint8; typedef signed char stbtt_int8; typedef unsigned short stbtt_uint16; typedef signed short stbtt_int16; typedef unsigned int stbtt_uint32; typedef signed int stbtt_int32; #endif typedef char stbtt__check_size32[sizeof(stbtt_int32)==4 ? 1 : -1]; typedef char stbtt__check_size16[sizeof(stbtt_int16)==2 ? 1 : -1]; /* e.g. #define your own STBTT_ifloor/STBTT_iceil() to avoid math.h */ #ifndef STBTT_ifloor #include #define STBTT_ifloor(x) ((int) floor(x)) #define STBTT_iceil(x) ((int) ceil(x)) #endif #ifndef STBTT_sqrt #include #define STBTT_sqrt(x) sqrt(x) #define STBTT_pow(x,y) pow(x,y) #endif #ifndef STBTT_fmod #include #define STBTT_fmod(x,y) fmod(x,y) #endif #ifndef STBTT_cos #include #define STBTT_cos(x) cos(x) #define STBTT_acos(x) acos(x) #endif #ifndef STBTT_fabs #include #define STBTT_fabs(x) fabs(x) #endif /* #define your own functions "STBTT_malloc" / "STBTT_free" to avoid malloc.h */ #ifndef STBTT_malloc #include #define STBTT_malloc(x,u) ((void)(u),malloc(x)) #define STBTT_free(x,u) ((void)(u),free(x)) #endif #ifndef STBTT_assert #include #define STBTT_assert(x) assert(x) #endif #ifndef STBTT_strlen #include #define STBTT_strlen(x) strlen(x) #endif #ifndef STBTT_memcpy #include #define STBTT_memcpy memcpy #define STBTT_memset memset #endif #endif /* ///////////////////////////////////////////////////////////////////////////// */ /* ///////////////////////////////////////////////////////////////////////////// */ /* // */ /* // INTERFACE */ /* // */ /* // */ #ifndef __STB_INCLUDE_STB_TRUETYPE_H__ #define __STB_INCLUDE_STB_TRUETYPE_H__ #ifdef STBTT_STATIC #define STBTT_DEF static #else #define STBTT_DEF extern #endif #ifdef __cplusplus extern "C" { #endif /* private structure */ typedef struct { unsigned char *data; int cursor; int size; } stbtt__buf; /* //////////////////////////////////////////////////////////////////////////// */ /* */ /* TEXTURE BAKING API */ /* */ /* If you use this API, you only have to call two functions ever. */ /* */ typedef struct { unsigned short x0,y0,x1,y1; /* coordinates of bbox in bitmap */ float xoff,yoff,xadvance; } stbtt_bakedchar; STBTT_DEF int stbtt_BakeFontBitmap(const unsigned char *data, int offset, /* font location (use offset=0 for plain .ttf) */ float pixel_height, /* height of font in pixels */ unsigned char *pixels, int pw, int ph, /* bitmap to be filled in */ int first_char, int num_chars, /* characters to bake */ stbtt_bakedchar *chardata); /* you allocate this, it's num_chars long */ /* if return is positive, the first unused row of the bitmap */ /* if return is negative, returns the negative of the number of characters that fit */ /* if return is 0, no characters fit and no rows were used */ /* This uses a very crappy packing. */ typedef struct { float x0,y0,s0,t0; /* top-left */ float x1,y1,s1,t1; /* bottom-right */ } stbtt_aligned_quad; STBTT_DEF void stbtt_GetBakedQuad(const stbtt_bakedchar *chardata, int pw, int ph, /* same data as above */ int char_index, /* character to display */ float *xpos, float *ypos, /* pointers to current position in screen pixel space */ stbtt_aligned_quad *q, /* output: quad to draw */ int opengl_fillrule); /* true if opengl fill rule; false if DX9 or earlier */ /* Call GetBakedQuad with char_index = 'character - first_char', and it */ /* creates the quad you need to draw and advances the current position. */ /* */ /* The coordinate system used assumes y increases downwards. */ /* */ /* Characters will extend both above and below the current position; */ /* see discussion of "BASELINE" above. */ /* */ /* It's inefficient; you might want to c&p it and optimize it. */ STBTT_DEF void stbtt_GetScaledFontVMetrics(const unsigned char *fontdata, int index, float size, float *ascent, float *descent, float *lineGap); /* Query the font vertical metrics without having to create a font first. */ /* //////////////////////////////////////////////////////////////////////////// */ /* */ /* NEW TEXTURE BAKING API */ /* */ /* This provides options for packing multiple fonts into one atlas, not */ /* perfectly but better than nothing. */ typedef struct { unsigned short x0,y0,x1,y1; /* coordinates of bbox in bitmap */ float xoff,yoff,xadvance; float xoff2,yoff2; } stbtt_packedchar; typedef struct stbtt_pack_context stbtt_pack_context; typedef struct stbtt_fontinfo stbtt_fontinfo; #ifndef STB_RECT_PACK_VERSION typedef struct stbrp_rect stbrp_rect; #endif STBTT_DEF int stbtt_PackBegin(stbtt_pack_context *spc, unsigned char *pixels, int width, int height, int stride_in_bytes, int padding, void *alloc_context); /* Initializes a packing context stored in the passed-in stbtt_pack_context. */ /* Future calls using this context will pack characters into the bitmap passed */ /* in here: a 1-channel bitmap that is width * height. stride_in_bytes is */ /* the distance from one row to the next (or 0 to mean they are packed tightly */ /* together). "padding" is the amount of padding to leave between each */ /* character (normally you want '1' for bitmaps you'll use as textures with */ /* bilinear filtering). */ /* */ /* Returns 0 on failure, 1 on success. */ STBTT_DEF void stbtt_PackEnd (stbtt_pack_context *spc); /* Cleans up the packing context and frees all memory. */ #define STBTT_POINT_SIZE(x) (-(x)) STBTT_DEF int stbtt_PackFontRange(stbtt_pack_context *spc, const unsigned char *fontdata, int font_index, float font_size, int first_unicode_char_in_range, int num_chars_in_range, stbtt_packedchar *chardata_for_range); /* Creates character bitmaps from the font_index'th font found in fontdata (use */ /* font_index=0 if you don't know what that is). It creates num_chars_in_range */ /* bitmaps for characters with unicode values starting at first_unicode_char_in_range */ /* and increasing. Data for how to render them is stored in chardata_for_range; */ /* pass these to stbtt_GetPackedQuad to get back renderable quads. */ /* */ /* font_size is the full height of the character from ascender to descender, */ /* as computed by stbtt_ScaleForPixelHeight. To use a point size as computed */ /* by stbtt_ScaleForMappingEmToPixels, wrap the point size in STBTT_POINT_SIZE() */ /* and pass that result as 'font_size': */ /* ..., 20 , ... // font max minus min y is 20 pixels tall */ /* ..., STBTT_POINT_SIZE(20), ... // 'M' is 20 pixels tall */ typedef struct { float font_size; int first_unicode_codepoint_in_range; /* if non-zero, then the chars are continuous, and this is the first codepoint */ int *array_of_unicode_codepoints; /* if non-zero, then this is an array of unicode codepoints */ int num_chars; stbtt_packedchar *chardata_for_range; /* output */ unsigned char h_oversample, v_oversample; /* don't set these, they're used internally */ } stbtt_pack_range; STBTT_DEF int stbtt_PackFontRanges(stbtt_pack_context *spc, const unsigned char *fontdata, int font_index, stbtt_pack_range *ranges, int num_ranges); /* Creates character bitmaps from multiple ranges of characters stored in */ /* ranges. This will usually create a better-packed bitmap than multiple */ /* calls to stbtt_PackFontRange. Note that you can call this multiple */ /* times within a single PackBegin/PackEnd. */ STBTT_DEF void stbtt_PackSetOversampling(stbtt_pack_context *spc, unsigned int h_oversample, unsigned int v_oversample); /* Oversampling a font increases the quality by allowing higher-quality subpixel */ /* positioning, and is especially valuable at smaller text sizes. */ /* */ /* This function sets the amount of oversampling for all following calls to */ /* stbtt_PackFontRange(s) or stbtt_PackFontRangesGatherRects for a given */ /* pack context. The default (no oversampling) is achieved by h_oversample=1 */ /* and v_oversample=1. The total number of pixels required is */ /* h_oversample*v_oversample larger than the default; for example, 2x2 */ /* oversampling requires 4x the storage of 1x1. For best results, render */ /* oversampled textures with bilinear filtering. Look at the readme in */ /* stb/tests/oversample for information about oversampled fonts */ /* */ /* To use with PackFontRangesGather etc., you must set it before calls */ /* call to PackFontRangesGatherRects. */ STBTT_DEF void stbtt_PackSetSkipMissingCodepoints(stbtt_pack_context *spc, int skip); /* If skip != 0, this tells stb_truetype to skip any codepoints for which */ /* there is no corresponding glyph. If skip=0, which is the default, then */ /* codepoints without a glyph recived the font's "missing character" glyph, */ /* typically an empty box by convention. */ STBTT_DEF void stbtt_GetPackedQuad(const stbtt_packedchar *chardata, int pw, int ph, /* same data as above */ int char_index, /* character to display */ float *xpos, float *ypos, /* pointers to current position in screen pixel space */ stbtt_aligned_quad *q, /* output: quad to draw */ int align_to_integer); STBTT_DEF int stbtt_PackFontRangesGatherRects(stbtt_pack_context *spc, const stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects); STBTT_DEF void stbtt_PackFontRangesPackRects(stbtt_pack_context *spc, stbrp_rect *rects, int num_rects); STBTT_DEF int stbtt_PackFontRangesRenderIntoRects(stbtt_pack_context *spc, const stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects); /* Calling these functions in sequence is roughly equivalent to calling */ /* stbtt_PackFontRanges(). If you more control over the packing of multiple */ /* fonts, or if you want to pack custom data into a font texture, take a look */ /* at the source to of stbtt_PackFontRanges() and create a custom version */ /* using these functions, e.g. call GatherRects multiple times, */ /* building up a single array of rects, then call PackRects once, */ /* then call RenderIntoRects repeatedly. This may result in a */ /* better packing than calling PackFontRanges multiple times */ /* (or it may not). */ /* this is an opaque structure that you shouldn't mess with which holds */ /* all the context needed from PackBegin to PackEnd. */ struct stbtt_pack_context { void *user_allocator_context; void *pack_info; int width; int height; int stride_in_bytes; int padding; int skip_missing; unsigned int h_oversample, v_oversample; unsigned char *pixels; void *nodes; }; /* //////////////////////////////////////////////////////////////////////////// */ /* */ /* FONT LOADING */ /* */ /* */ STBTT_DEF int stbtt_GetNumberOfFonts(const unsigned char *data); /* This function will determine the number of fonts in a font file. TrueType */ /* collection (.ttc) files may contain multiple fonts, while TrueType font */ /* (.ttf) files only contain one font. The number of fonts can be used for */ /* indexing with the previous function where the index is between zero and one */ /* less than the total fonts. If an error occurs, -1 is returned. */ STBTT_DEF int stbtt_GetFontOffsetForIndex(const unsigned char *data, int index); /* Each .ttf/.ttc file may have more than one font. Each font has a sequential */ /* index number starting from 0. Call this function to get the font offset for */ /* a given index; it returns -1 if the index is out of range. A regular .ttf */ /* file will only define one font and it always be at offset 0, so it will */ /* return '0' for index 0, and -1 for all other indices. */ /* The following structure is defined publicly so you can declare one on */ /* the stack or as a global or etc, but you should treat it as opaque. */ struct stbtt_fontinfo { void * userdata; unsigned char * data; /* pointer to .ttf file */ int fontstart; /* offset of start of font */ int numGlyphs; /* number of glyphs, needed for range checking */ int loca,head,glyf,hhea,hmtx,kern,gpos,svg; /* table locations as offset from start of .ttf */ int index_map; /* a cmap mapping for our chosen character encoding */ int indexToLocFormat; /* format needed to map from glyph index to glyph */ stbtt__buf cff; /* cff font data */ stbtt__buf charstrings; /* the charstring index */ stbtt__buf gsubrs; /* global charstring subroutines index */ stbtt__buf subrs; /* private charstring subroutines index */ stbtt__buf fontdicts; /* array of font dicts */ stbtt__buf fdselect; /* map from glyph to fontdict */ }; STBTT_DEF int stbtt_InitFont(stbtt_fontinfo *info, const unsigned char *data, int offset); /* Given an offset into the file that defines a font, this function builds */ /* the necessary cached info for the rest of the system. You must allocate */ /* the stbtt_fontinfo yourself, and stbtt_InitFont will fill it out. You don't */ /* need to do anything special to free it, because the contents are pure */ /* value data with no additional data structures. Returns 0 on failure. */ /* //////////////////////////////////////////////////////////////////////////// */ /* */ /* CHARACTER TO GLYPH-INDEX CONVERSIOn */ STBTT_DEF int stbtt_FindGlyphIndex(const stbtt_fontinfo *info, int unicode_codepoint); /* If you're going to perform multiple operations on the same character */ /* and you want a speed-up, call this function with the character you're */ /* going to process, then use glyph-based functions instead of the */ /* codepoint-based functions. */ /* Returns 0 if the character codepoint is not defined in the font. */ /* //////////////////////////////////////////////////////////////////////////// */ /* */ /* CHARACTER PROPERTIES */ /* */ STBTT_DEF float stbtt_ScaleForPixelHeight(const stbtt_fontinfo *info, float pixels); /* computes a scale factor to produce a font whose "height" is 'pixels' tall. */ /* Height is measured as the distance from the highest ascender to the lowest */ /* descender; in other words, it's equivalent to calling stbtt_GetFontVMetrics */ /* and computing: */ /* scale = pixels / (ascent - descent) */ /* so if you prefer to measure height by the ascent only, use a similar calculation. */ STBTT_DEF float stbtt_ScaleForMappingEmToPixels(const stbtt_fontinfo *info, float pixels); /* computes a scale factor to produce a font whose EM size is mapped to */ /* 'pixels' tall. This is probably what traditional APIs compute, but */ /* I'm not positive. */ STBTT_DEF void stbtt_GetFontVMetrics(const stbtt_fontinfo *info, int *ascent, int *descent, int *lineGap); /* ascent is the coordinate above the baseline the font extends; descent */ /* is the coordinate below the baseline the font extends (i.e. it is typically negative) */ /* lineGap is the spacing between one row's descent and the next row's ascent... */ /* so you should advance the vertical position by "*ascent - *descent + *lineGap" */ /* these are expressed in unscaled coordinates, so you must multiply by */ /* the scale factor for a given size */ STBTT_DEF int stbtt_GetFontVMetricsOS2(const stbtt_fontinfo *info, int *typoAscent, int *typoDescent, int *typoLineGap); /* analogous to GetFontVMetrics, but returns the "typographic" values from the OS/2 */ /* table (specific to MS/Windows TTF files). */ /* */ /* Returns 1 on success (table present), 0 on failure. */ STBTT_DEF void stbtt_GetFontBoundingBox(const stbtt_fontinfo *info, int *x0, int *y0, int *x1, int *y1); /* the bounding box around all possible characters */ STBTT_DEF void stbtt_GetCodepointHMetrics(const stbtt_fontinfo *info, int codepoint, int *advanceWidth, int *leftSideBearing); /* leftSideBearing is the offset from the current horizontal position to the left edge of the character */ /* advanceWidth is the offset from the current horizontal position to the next horizontal position */ /* these are expressed in unscaled coordinates */ STBTT_DEF int stbtt_GetCodepointKernAdvance(const stbtt_fontinfo *info, int ch1, int ch2); /* an additional amount to add to the 'advance' value between ch1 and ch2 */ STBTT_DEF int stbtt_GetCodepointBox(const stbtt_fontinfo *info, int codepoint, int *x0, int *y0, int *x1, int *y1); /* Gets the bounding box of the visible part of the glyph, in unscaled coordinates */ STBTT_DEF void stbtt_GetGlyphHMetrics(const stbtt_fontinfo *info, int glyph_index, int *advanceWidth, int *leftSideBearing); STBTT_DEF int stbtt_GetGlyphKernAdvance(const stbtt_fontinfo *info, int glyph1, int glyph2); STBTT_DEF int stbtt_GetGlyphBox(const stbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1); /* as above, but takes one or more glyph indices for greater efficiency */ typedef struct stbtt_kerningentry { int glyph1; /* use stbtt_FindGlyphIndex */ int glyph2; int advance; } stbtt_kerningentry; STBTT_DEF int stbtt_GetKerningTableLength(const stbtt_fontinfo *info); STBTT_DEF int stbtt_GetKerningTable(const stbtt_fontinfo *info, stbtt_kerningentry* table, int table_length); /* Retrieves a complete list of all of the kerning pairs provided by the font */ /* stbtt_GetKerningTable never writes more than table_length entries and returns how many entries it did write. */ /* The table will be sorted by (a.glyph1 == b.glyph1)?(a.glyph2 < b.glyph2):(a.glyph1 < b.glyph1) */ /* //////////////////////////////////////////////////////////////////////////// */ /* */ /* GLYPH SHAPES (you probably don't need these, but they have to go before */ /* the bitmaps for C declaration-order reasons) */ /* */ #ifndef STBTT_vmove /* you can predefine these to use different values (but why?) */ enum { STBTT_vmove=1, STBTT_vline, STBTT_vcurve, STBTT_vcubic }; #endif #ifndef stbtt_vertex /* you can predefine this to use different values */ /* (we share this with other code at RAD) */ #define stbtt_vertex_type short /* can't use stbtt_int16 because that's not visible in the header file */ typedef struct { stbtt_vertex_type x,y,cx,cy,cx1,cy1; unsigned char type,padding; } stbtt_vertex; #endif STBTT_DEF int stbtt_IsGlyphEmpty(const stbtt_fontinfo *info, int glyph_index); /* returns non-zero if nothing is drawn for this glyph */ STBTT_DEF int stbtt_GetCodepointShape(const stbtt_fontinfo *info, int unicode_codepoint, stbtt_vertex **vertices); STBTT_DEF int stbtt_GetGlyphShape(const stbtt_fontinfo *info, int glyph_index, stbtt_vertex **vertices); /* returns # of vertices and fills *vertices with the pointer to them */ /* these are expressed in "unscaled" coordinates */ /* */ /* The shape is a series of contours. Each one starts with */ /* a STBTT_moveto, then consists of a series of mixed */ /* STBTT_lineto and STBTT_curveto segments. A lineto */ /* draws a line from previous endpoint to its x,y; a curveto */ /* draws a quadratic bezier from previous endpoint to */ /* its x,y, using cx,cy as the bezier control point. */ STBTT_DEF void stbtt_FreeShape(const stbtt_fontinfo *info, stbtt_vertex *vertices); /* frees the data allocated above */ STBTT_DEF unsigned char *stbtt_FindSVGDoc(const stbtt_fontinfo *info, int gl); STBTT_DEF int stbtt_GetCodepointSVG(const stbtt_fontinfo *info, int unicode_codepoint, const char **svg); STBTT_DEF int stbtt_GetGlyphSVG(const stbtt_fontinfo *info, int gl, const char **svg); /* fills svg with the character's SVG data. */ /* returns data size or 0 if SVG not found. */ /* //////////////////////////////////////////////////////////////////////////// */ /* */ /* BITMAP RENDERING */ /* */ STBTT_DEF void stbtt_FreeBitmap(unsigned char *bitmap, void *userdata); /* frees the bitmap allocated below */ STBTT_DEF unsigned char *stbtt_GetCodepointBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int codepoint, int *width, int *height, int *xoff, int *yoff); /* allocates a large-enough single-channel 8bpp bitmap and renders the */ /* specified character/glyph at the specified scale into it, with */ /* antialiasing. 0 is no coverage (transparent), 255 is fully covered (opaque). */ /* *width & *height are filled out with the width & height of the bitmap, */ /* which is stored left-to-right, top-to-bottom. */ /* */ /* xoff/yoff are the offset it pixel space from the glyph origin to the top-left of the bitmap */ STBTT_DEF unsigned char *stbtt_GetCodepointBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint, int *width, int *height, int *xoff, int *yoff); /* the same as stbtt_GetCodepoitnBitmap, but you can specify a subpixel */ /* shift for the character */ STBTT_DEF void stbtt_MakeCodepointBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int codepoint); /* the same as stbtt_GetCodepointBitmap, but you pass in storage for the bitmap */ /* in the form of 'output', with row spacing of 'out_stride' bytes. the bitmap */ /* is clipped to out_w/out_h bytes. Call stbtt_GetCodepointBitmapBox to get the */ /* width and height and positioning info for it first. */ STBTT_DEF void stbtt_MakeCodepointBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint); /* same as stbtt_MakeCodepointBitmap, but you can specify a subpixel */ /* shift for the character */ STBTT_DEF void stbtt_MakeCodepointBitmapSubpixelPrefilter(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int oversample_x, int oversample_y, float *sub_x, float *sub_y, int codepoint); /* same as stbtt_MakeCodepointBitmapSubpixel, but prefiltering */ /* is performed (see stbtt_PackSetOversampling) */ STBTT_DEF void stbtt_GetCodepointBitmapBox(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1); /* get the bbox of the bitmap centered around the glyph origin; so the */ /* bitmap width is ix1-ix0, height is iy1-iy0, and location to place */ /* the bitmap top left is (leftSideBearing*scale,iy0). */ /* (Note that the bitmap uses y-increases-down, but the shape uses */ /* y-increases-up, so CodepointBitmapBox and CodepointBox are inverted.) */ STBTT_DEF void stbtt_GetCodepointBitmapBoxSubpixel(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1); /* same as stbtt_GetCodepointBitmapBox, but you can specify a subpixel */ /* shift for the character */ /* the following functions are equivalent to the above functions, but operate */ /* on glyph indices instead of Unicode codepoints (for efficiency) */ STBTT_DEF unsigned char *stbtt_GetGlyphBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int glyph, int *width, int *height, int *xoff, int *yoff); STBTT_DEF unsigned char *stbtt_GetGlyphBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int glyph, int *width, int *height, int *xoff, int *yoff); STBTT_DEF void stbtt_MakeGlyphBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int glyph); STBTT_DEF void stbtt_MakeGlyphBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int glyph); STBTT_DEF void stbtt_MakeGlyphBitmapSubpixelPrefilter(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int oversample_x, int oversample_y, float *sub_x, float *sub_y, int glyph); STBTT_DEF void stbtt_GetGlyphBitmapBox(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1); STBTT_DEF void stbtt_GetGlyphBitmapBoxSubpixel(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y,float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1); /* @TODO: don't expose this structure */ typedef struct { int w,h,stride; unsigned char *pixels; } stbtt__bitmap; /* rasterize a shape with quadratic beziers into a bitmap */ STBTT_DEF void stbtt_Rasterize(stbtt__bitmap *result, /* 1-channel bitmap to draw into */ float flatness_in_pixels, /* allowable error of curve in pixels */ stbtt_vertex *vertices, /* array of vertices defining shape */ int num_verts, /* number of vertices in above array */ float scale_x, float scale_y, /* scale applied to input vertices */ float shift_x, float shift_y, /* translation applied to input vertices */ int x_off, int y_off, /* another translation applied to input */ int invert, /* if non-zero, vertically flip shape */ void *userdata); /* context for to STBTT_MALLOC */ /* //////////////////////////////////////////////////////////////////////////// */ /* */ /* Signed Distance Function (or Field) rendering */ STBTT_DEF void stbtt_FreeSDF(unsigned char *bitmap, void *userdata); /* frees the SDF bitmap allocated below */ STBTT_DEF unsigned char * stbtt_GetGlyphSDF(const stbtt_fontinfo *info, float scale, int glyph, int padding, unsigned char onedge_value, float pixel_dist_scale, int *width, int *height, int *xoff, int *yoff); STBTT_DEF unsigned char * stbtt_GetCodepointSDF(const stbtt_fontinfo *info, float scale, int codepoint, int padding, unsigned char onedge_value, float pixel_dist_scale, int *width, int *height, int *xoff, int *yoff); /* These functions compute a discretized SDF field for a single character, suitable for storing */ /* in a single-channel texture, sampling with bilinear filtering, and testing against */ /* larger than some threshold to produce scalable fonts. */ /* info -- the font */ /* scale -- controls the size of the resulting SDF bitmap, same as it would be creating a regular bitmap */ /* glyph/codepoint -- the character to generate the SDF for */ /* padding -- extra "pixels" around the character which are filled with the distance to the character (not 0), */ /* which allows effects like bit outlines */ /* onedge_value -- value 0-255 to test the SDF against to reconstruct the character (i.e. the isocontour of the character) */ /* pixel_dist_scale -- what value the SDF should increase by when moving one SDF "pixel" away from the edge (on the 0..255 scale) */ /* if positive, > onedge_value is inside; if negative, < onedge_value is inside */ /* width,height -- output height & width of the SDF bitmap (including padding) */ /* xoff,yoff -- output origin of the character */ /* return value -- a 2D array of bytes 0..255, width*height in size */ /* */ /* pixel_dist_scale & onedge_value are a scale & bias that allows you to make */ /* optimal use of the limited 0..255 for your application, trading off precision */ /* and special effects. SDF values outside the range 0..255 are clamped to 0..255. */ /* */ /* Example: */ /* scale = stbtt_ScaleForPixelHeight(22) */ /* padding = 5 */ /* onedge_value = 180 */ /* pixel_dist_scale = 180/5.0 = 36.0 */ /* */ /* This will create an SDF bitmap in which the character is about 22 pixels */ /* high but the whole bitmap is about 22+5+5=32 pixels high. To produce a filled */ /* shape, sample the SDF at each pixel and fill the pixel if the SDF value */ /* is greater than or equal to 180/255. (You'll actually want to antialias, */ /* which is beyond the scope of this example.) Additionally, you can compute */ /* offset outlines (e.g. to stroke the character border inside & outside, */ /* or only outside). For example, to fill outside the character up to 3 SDF */ /* pixels, you would compare against (180-36.0*3)/255 = 72/255. The above */ /* choice of variables maps a range from 5 pixels outside the shape to */ /* 2 pixels inside the shape to 0..255; this is intended primarily for apply */ /* outside effects only (the interior range is needed to allow proper */ /* antialiasing of the font at *smaller* sizes) */ /* */ /* The function computes the SDF analytically at each SDF pixel, not by e.g. */ /* building a higher-res bitmap and approximating it. In theory the quality */ /* should be as high as possible for an SDF of this size & representation, but */ /* unclear if this is true in practice (perhaps building a higher-res bitmap */ /* and computing from that can allow drop-out prevention). */ /* */ /* The algorithm has not been optimized at all, so expect it to be slow */ /* if computing lots of characters or very large sizes. */ /* //////////////////////////////////////////////////////////////////////////// */ /* */ /* Finding the right font... */ /* */ /* You should really just solve this offline, keep your own tables */ /* of what font is what, and don't try to get it out of the .ttf file. */ /* That's because getting it out of the .ttf file is really hard, because */ /* the names in the file can appear in many possible encodings, in many */ /* possible languages, and e.g. if you need a case-insensitive comparison, */ /* the details of that depend on the encoding & language in a complex way */ /* (actually underspecified in truetype, but also gigantic). */ /* */ /* But you can use the provided functions in two possible ways: */ /* stbtt_FindMatchingFont() will use *case-sensitive* comparisons on */ /* unicode-encoded names to try to find the font you want; */ /* you can run this before calling stbtt_InitFont() */ /* */ /* stbtt_GetFontNameString() lets you get any of the various strings */ /* from the file yourself and do your own comparisons on them. */ /* You have to have called stbtt_InitFont() first. */ STBTT_DEF int stbtt_FindMatchingFont(const unsigned char *fontdata, const char *name, int flags); /* returns the offset (not index) of the font that matches, or -1 if none */ /* if you use STBTT_MACSTYLE_DONTCARE, use a font name like "Arial Bold". */ /* if you use any other flag, use a font name like "Arial"; this checks */ /* the 'macStyle' header field; i don't know if fonts set this consistently */ #define STBTT_MACSTYLE_DONTCARE 0 #define STBTT_MACSTYLE_BOLD 1 #define STBTT_MACSTYLE_ITALIC 2 #define STBTT_MACSTYLE_UNDERSCORE 4 #define STBTT_MACSTYLE_NONE 8 /* <= not same as 0, this makes us check the bitfield is 0 */ STBTT_DEF int stbtt_CompareUTF8toUTF16_bigendian(const char *s1, int len1, const char *s2, int len2); /* returns 1/0 whether the first string interpreted as utf8 is identical to */ /* the second string interpreted as big-endian utf16... useful for strings from next func */ STBTT_DEF const char *stbtt_GetFontNameString(const stbtt_fontinfo *font, int *length, int platformID, int encodingID, int languageID, int nameID); /* returns the string (which may be big-endian double byte, e.g. for unicode) */ /* and puts the length in bytes in *length. */ /* */ /* some of the values for the IDs are below; for more see the truetype spec: */ /* http://developer.apple.com/textfonts/TTRefMan/RM06/Chap6name.html */ /* http://www.microsoft.com/typography/otspec/name.htm */ enum { /* platformID */ STBTT_PLATFORM_ID_UNICODE =0, STBTT_PLATFORM_ID_MAC =1, STBTT_PLATFORM_ID_ISO =2, STBTT_PLATFORM_ID_MICROSOFT =3 }; enum { /* encodingID for STBTT_PLATFORM_ID_UNICODE */ STBTT_UNICODE_EID_UNICODE_1_0 =0, STBTT_UNICODE_EID_UNICODE_1_1 =1, STBTT_UNICODE_EID_ISO_10646 =2, STBTT_UNICODE_EID_UNICODE_2_0_BMP=3, STBTT_UNICODE_EID_UNICODE_2_0_FULL=4 }; enum { /* encodingID for STBTT_PLATFORM_ID_MICROSOFT */ STBTT_MS_EID_SYMBOL =0, STBTT_MS_EID_UNICODE_BMP =1, STBTT_MS_EID_SHIFTJIS =2, STBTT_MS_EID_UNICODE_FULL =10 }; enum { /* encodingID for STBTT_PLATFORM_ID_MAC; same as Script Manager codes */ STBTT_MAC_EID_ROMAN =0, STBTT_MAC_EID_ARABIC =4, STBTT_MAC_EID_JAPANESE =1, STBTT_MAC_EID_HEBREW =5, STBTT_MAC_EID_CHINESE_TRAD =2, STBTT_MAC_EID_GREEK =6, STBTT_MAC_EID_KOREAN =3, STBTT_MAC_EID_RUSSIAN =7 }; enum { /* languageID for STBTT_PLATFORM_ID_MICROSOFT; same as LCID... */ /* problematic because there are e.g. 16 english LCIDs and 16 arabic LCIDs */ STBTT_MS_LANG_ENGLISH =0x0409, STBTT_MS_LANG_ITALIAN =0x0410, STBTT_MS_LANG_CHINESE =0x0804, STBTT_MS_LANG_JAPANESE =0x0411, STBTT_MS_LANG_DUTCH =0x0413, STBTT_MS_LANG_KOREAN =0x0412, STBTT_MS_LANG_FRENCH =0x040c, STBTT_MS_LANG_RUSSIAN =0x0419, STBTT_MS_LANG_GERMAN =0x0407, STBTT_MS_LANG_SPANISH =0x0409, STBTT_MS_LANG_HEBREW =0x040d, STBTT_MS_LANG_SWEDISH =0x041D }; enum { /* languageID for STBTT_PLATFORM_ID_MAC */ STBTT_MAC_LANG_ENGLISH =0 , STBTT_MAC_LANG_JAPANESE =11, STBTT_MAC_LANG_ARABIC =12, STBTT_MAC_LANG_KOREAN =23, STBTT_MAC_LANG_DUTCH =4 , STBTT_MAC_LANG_RUSSIAN =32, STBTT_MAC_LANG_FRENCH =1 , STBTT_MAC_LANG_SPANISH =6 , STBTT_MAC_LANG_GERMAN =2 , STBTT_MAC_LANG_SWEDISH =5 , STBTT_MAC_LANG_HEBREW =10, STBTT_MAC_LANG_CHINESE_SIMPLIFIED =33, STBTT_MAC_LANG_ITALIAN =3 , STBTT_MAC_LANG_CHINESE_TRAD =19 }; #ifdef __cplusplus } #endif #endif /* __STB_INCLUDE_STB_TRUETYPE_H__ */ /* ///////////////////////////////////////////////////////////////////////////// */ /* ///////////////////////////////////////////////////////////////////////////// */ /* // */ /* // IMPLEMENTATION */ /* // */ /* // */ #ifdef STB_TRUETYPE_IMPLEMENTATION #ifndef STBTT_MAX_OVERSAMPLE #define STBTT_MAX_OVERSAMPLE 8 #endif #if STBTT_MAX_OVERSAMPLE > 255 #error "STBTT_MAX_OVERSAMPLE cannot be > 255" #endif typedef int stbtt__test_oversample_pow2[(STBTT_MAX_OVERSAMPLE & (STBTT_MAX_OVERSAMPLE-1)) == 0 ? 1 : -1]; #ifndef STBTT_RASTERIZER_VERSION #define STBTT_RASTERIZER_VERSION 2 #endif #ifdef _MSC_VER #define STBTT__NOTUSED(v) (void)(v) #else #define STBTT__NOTUSED(v) (void)sizeof(v) #endif /* //////////////////////////////////////////////////////////////////////// */ /* */ /* stbtt__buf helpers to parse data from file */ /* */ static stbtt_uint8 stbtt__buf_get8(stbtt__buf *b) { if (b->cursor >= b->size) return 0; return b->data[b->cursor++]; } static stbtt_uint8 stbtt__buf_peek8(stbtt__buf *b) { if (b->cursor >= b->size) return 0; return b->data[b->cursor]; } static void stbtt__buf_seek(stbtt__buf *b, int o) { STBTT_assert(!(o > b->size || o < 0)); b->cursor = (o > b->size || o < 0) ? b->size : o; } static void stbtt__buf_skip(stbtt__buf *b, int o) { stbtt__buf_seek(b, b->cursor + o); } static stbtt_uint32 stbtt__buf_get(stbtt__buf *b, int n) { stbtt_uint32 v = 0; int i; STBTT_assert(n >= 1 && n <= 4); for (i = 0; i < n; i++) v = (v << 8) | stbtt__buf_get8(b); return v; } static stbtt__buf stbtt__new_buf(const void *p, size_t size) { stbtt__buf r; STBTT_assert(size < 0x40000000); r.data = (stbtt_uint8*) p; r.size = (int) size; r.cursor = 0; return r; } #define stbtt__buf_get16(b) stbtt__buf_get((b), 2) #define stbtt__buf_get32(b) stbtt__buf_get((b), 4) static stbtt__buf stbtt__buf_range(const stbtt__buf *b, int o, int s) { stbtt__buf r = stbtt__new_buf(NULL, 0); if (o < 0 || s < 0 || o > b->size || s > b->size - o) return r; r.data = b->data + o; r.size = s; return r; } static stbtt__buf stbtt__cff_get_index(stbtt__buf *b) { int count, start, offsize; start = b->cursor; count = stbtt__buf_get16(b); if (count) { offsize = stbtt__buf_get8(b); STBTT_assert(offsize >= 1 && offsize <= 4); stbtt__buf_skip(b, offsize * count); stbtt__buf_skip(b, stbtt__buf_get(b, offsize) - 1); } return stbtt__buf_range(b, start, b->cursor - start); } static stbtt_uint32 stbtt__cff_int(stbtt__buf *b) { int b0 = stbtt__buf_get8(b); if (b0 >= 32 && b0 <= 246) return b0 - 139; else if (b0 >= 247 && b0 <= 250) return (b0 - 247)*256 + stbtt__buf_get8(b) + 108; else if (b0 >= 251 && b0 <= 254) return -(b0 - 251)*256 - stbtt__buf_get8(b) - 108; else if (b0 == 28) return stbtt__buf_get16(b); else if (b0 == 29) return stbtt__buf_get32(b); STBTT_assert(0); return 0; } static void stbtt__cff_skip_operand(stbtt__buf *b) { int v, b0 = stbtt__buf_peek8(b); STBTT_assert(b0 >= 28); if (b0 == 30) { stbtt__buf_skip(b, 1); while (b->cursor < b->size) { v = stbtt__buf_get8(b); if ((v & 0xF) == 0xF || (v >> 4) == 0xF) break; } } else { stbtt__cff_int(b); } } static stbtt__buf stbtt__dict_get(stbtt__buf *b, int key) { stbtt__buf_seek(b, 0); while (b->cursor < b->size) { int start = b->cursor, end, op; while (stbtt__buf_peek8(b) >= 28) stbtt__cff_skip_operand(b); end = b->cursor; op = stbtt__buf_get8(b); if (op == 12) op = stbtt__buf_get8(b) | 0x100; if (op == key) return stbtt__buf_range(b, start, end-start); } return stbtt__buf_range(b, 0, 0); } static void stbtt__dict_get_ints(stbtt__buf *b, int key, int outcount, stbtt_uint32 *out) { int i; stbtt__buf operands = stbtt__dict_get(b, key); for (i = 0; i < outcount && operands.cursor < operands.size; i++) out[i] = stbtt__cff_int(&operands); } static int stbtt__cff_index_count(stbtt__buf *b) { stbtt__buf_seek(b, 0); return stbtt__buf_get16(b); } static stbtt__buf stbtt__cff_index_get(stbtt__buf b, int i) { int count, offsize, start, end; stbtt__buf_seek(&b, 0); count = stbtt__buf_get16(&b); offsize = stbtt__buf_get8(&b); STBTT_assert(i >= 0 && i < count); STBTT_assert(offsize >= 1 && offsize <= 4); stbtt__buf_skip(&b, i*offsize); start = stbtt__buf_get(&b, offsize); end = stbtt__buf_get(&b, offsize); return stbtt__buf_range(&b, 2+(count+1)*offsize+start, end - start); } /* //////////////////////////////////////////////////////////////////////// */ /* */ /* accessors to parse data from file */ /* */ /* on platforms that don't allow misaligned reads, if we want to allow */ /* truetype fonts that aren't padded to alignment, define ALLOW_UNALIGNED_TRUETYPE */ #define ttBYTE(p) (* (stbtt_uint8 *) (p)) #define ttCHAR(p) (* (stbtt_int8 *) (p)) #define ttFixed(p) ttLONG(p) static stbtt_uint16 ttUSHORT(stbtt_uint8 *p) { return p[0]*256 + p[1]; } static stbtt_int16 ttSHORT(stbtt_uint8 *p) { return p[0]*256 + p[1]; } static stbtt_uint32 ttULONG(stbtt_uint8 *p) { return (p[0]<<24) + (p[1]<<16) + (p[2]<<8) + p[3]; } static stbtt_int32 ttLONG(stbtt_uint8 *p) { return (p[0]<<24) + (p[1]<<16) + (p[2]<<8) + p[3]; } #define stbtt_tag4(p,c0,c1,c2,c3) ((p)[0] == (c0) && (p)[1] == (c1) && (p)[2] == (c2) && (p)[3] == (c3)) #define stbtt_tag(p,str) stbtt_tag4(p,str[0],str[1],str[2],str[3]) static int stbtt__isfont(stbtt_uint8 *font) { /* check the version number */ if (stbtt_tag4(font, '1',0,0,0)) return 1; /* TrueType 1 */ if (stbtt_tag(font, "typ1")) return 1; /* TrueType with type 1 font -- we don't support this! */ if (stbtt_tag(font, "OTTO")) return 1; /* OpenType with CFF */ if (stbtt_tag4(font, 0,1,0,0)) return 1; /* OpenType 1.0 */ if (stbtt_tag(font, "true")) return 1; /* Apple specification for TrueType fonts */ return 0; } /* @OPTIMIZE: binary search */ static stbtt_uint32 stbtt__find_table(stbtt_uint8 *data, stbtt_uint32 fontstart, const char *tag) { stbtt_int32 num_tables = ttUSHORT(data+fontstart+4); stbtt_uint32 tabledir = fontstart + 12; stbtt_int32 i; for (i=0; i < num_tables; ++i) { stbtt_uint32 loc = tabledir + 16*i; if (stbtt_tag(data+loc+0, tag)) return ttULONG(data+loc+8); } return 0; } static int stbtt_GetFontOffsetForIndex_internal(unsigned char *font_collection, int index) { /* if it's just a font, there's only one valid index */ if (stbtt__isfont(font_collection)) return index == 0 ? 0 : -1; /* check if it's a TTC */ if (stbtt_tag(font_collection, "ttcf")) { /* version 1? */ if (ttULONG(font_collection+4) == 0x00010000 || ttULONG(font_collection+4) == 0x00020000) { stbtt_int32 n = ttLONG(font_collection+8); if (index >= n) return -1; return ttULONG(font_collection+12+index*4); } } return -1; } static int stbtt_GetNumberOfFonts_internal(unsigned char *font_collection) { /* if it's just a font, there's only one valid font */ if (stbtt__isfont(font_collection)) return 1; /* check if it's a TTC */ if (stbtt_tag(font_collection, "ttcf")) { /* version 1? */ if (ttULONG(font_collection+4) == 0x00010000 || ttULONG(font_collection+4) == 0x00020000) { return ttLONG(font_collection+8); } } return 0; } static stbtt__buf stbtt__get_subrs(stbtt__buf cff, stbtt__buf fontdict) { stbtt_uint32 subrsoff = 0, private_loc[2] = { 0, 0 }; stbtt__buf pdict; stbtt__dict_get_ints(&fontdict, 18, 2, private_loc); if (!private_loc[1] || !private_loc[0]) return stbtt__new_buf(NULL, 0); pdict = stbtt__buf_range(&cff, private_loc[1], private_loc[0]); stbtt__dict_get_ints(&pdict, 19, 1, &subrsoff); if (!subrsoff) return stbtt__new_buf(NULL, 0); stbtt__buf_seek(&cff, private_loc[1]+subrsoff); return stbtt__cff_get_index(&cff); } /* since most people won't use this, find this table the first time it's needed */ static int stbtt__get_svg(stbtt_fontinfo *info) { stbtt_uint32 t; if (info->svg < 0) { t = stbtt__find_table(info->data, info->fontstart, "SVG "); if (t) { stbtt_uint32 offset = ttULONG(info->data + t + 2); info->svg = t + offset; } else { info->svg = 0; } } return info->svg; } static int stbtt_InitFont_internal(stbtt_fontinfo *info, unsigned char *data, int fontstart) { stbtt_uint32 cmap, t; stbtt_int32 i,numTables; info->data = data; info->fontstart = fontstart; info->cff = stbtt__new_buf(NULL, 0); cmap = stbtt__find_table(data, fontstart, "cmap"); /* required */ info->loca = stbtt__find_table(data, fontstart, "loca"); /* required */ info->head = stbtt__find_table(data, fontstart, "head"); /* required */ info->glyf = stbtt__find_table(data, fontstart, "glyf"); /* required */ info->hhea = stbtt__find_table(data, fontstart, "hhea"); /* required */ info->hmtx = stbtt__find_table(data, fontstart, "hmtx"); /* required */ info->kern = stbtt__find_table(data, fontstart, "kern"); /* not required */ info->gpos = stbtt__find_table(data, fontstart, "GPOS"); /* not required */ if (!cmap || !info->head || !info->hhea || !info->hmtx) return 0; if (info->glyf) { /* required for truetype */ if (!info->loca) return 0; } else { /* initialization for CFF / Type2 fonts (OTF) */ stbtt__buf b, topdict, topdictidx; stbtt_uint32 cstype = 2, charstrings = 0, fdarrayoff = 0, fdselectoff = 0; stbtt_uint32 cff; cff = stbtt__find_table(data, fontstart, "CFF "); if (!cff) return 0; info->fontdicts = stbtt__new_buf(NULL, 0); info->fdselect = stbtt__new_buf(NULL, 0); /* @TODO this should use size from table (not 512MB) */ info->cff = stbtt__new_buf(data+cff, 512*1024*1024); b = info->cff; /* read the header */ stbtt__buf_skip(&b, 2); stbtt__buf_seek(&b, stbtt__buf_get8(&b)); /* hdrsize */ /* @TODO the name INDEX could list multiple fonts, */ /* but we just use the first one. */ stbtt__cff_get_index(&b); /* name INDEX */ topdictidx = stbtt__cff_get_index(&b); topdict = stbtt__cff_index_get(topdictidx, 0); stbtt__cff_get_index(&b); /* string INDEX */ info->gsubrs = stbtt__cff_get_index(&b); stbtt__dict_get_ints(&topdict, 17, 1, &charstrings); stbtt__dict_get_ints(&topdict, 0x100 | 6, 1, &cstype); stbtt__dict_get_ints(&topdict, 0x100 | 36, 1, &fdarrayoff); stbtt__dict_get_ints(&topdict, 0x100 | 37, 1, &fdselectoff); info->subrs = stbtt__get_subrs(b, topdict); /* we only support Type 2 charstrings */ if (cstype != 2) return 0; if (charstrings == 0) return 0; if (fdarrayoff) { /* looks like a CID font */ if (!fdselectoff) return 0; stbtt__buf_seek(&b, fdarrayoff); info->fontdicts = stbtt__cff_get_index(&b); info->fdselect = stbtt__buf_range(&b, fdselectoff, b.size-fdselectoff); } stbtt__buf_seek(&b, charstrings); info->charstrings = stbtt__cff_get_index(&b); } t = stbtt__find_table(data, fontstart, "maxp"); if (t) info->numGlyphs = ttUSHORT(data+t+4); else info->numGlyphs = 0xffff; info->svg = -1; /* find a cmap encoding table we understand *now* to avoid searching */ /* later. (todo: could make this installable) */ /* the same regardless of glyph. */ numTables = ttUSHORT(data + cmap + 2); info->index_map = 0; for (i=0; i < numTables; ++i) { stbtt_uint32 encoding_record = cmap + 4 + 8 * i; /* find an encoding we understand: */ switch(ttUSHORT(data+encoding_record)) { case STBTT_PLATFORM_ID_MICROSOFT: switch (ttUSHORT(data+encoding_record+2)) { case STBTT_MS_EID_UNICODE_BMP: case STBTT_MS_EID_UNICODE_FULL: /* MS/Unicode */ info->index_map = cmap + ttULONG(data+encoding_record+4); break; } break; case STBTT_PLATFORM_ID_UNICODE: /* Mac/iOS has these */ /* all the encodingIDs are unicode, so we don't bother to check it */ info->index_map = cmap + ttULONG(data+encoding_record+4); break; } } if (info->index_map == 0) return 0; info->indexToLocFormat = ttUSHORT(data+info->head + 50); return 1; } STBTT_DEF int stbtt_FindGlyphIndex(const stbtt_fontinfo *info, int unicode_codepoint) { stbtt_uint8 *data = info->data; stbtt_uint32 index_map = info->index_map; stbtt_uint16 format = ttUSHORT(data + index_map + 0); if (format == 0) { /* apple byte encoding */ stbtt_int32 bytes = ttUSHORT(data + index_map + 2); if (unicode_codepoint < bytes-6) return ttBYTE(data + index_map + 6 + unicode_codepoint); return 0; } else if (format == 6) { stbtt_uint32 first = ttUSHORT(data + index_map + 6); stbtt_uint32 count = ttUSHORT(data + index_map + 8); if ((stbtt_uint32) unicode_codepoint >= first && (stbtt_uint32) unicode_codepoint < first+count) return ttUSHORT(data + index_map + 10 + (unicode_codepoint - first)*2); return 0; } else if (format == 2) { STBTT_assert(0); /* @TODO: high-byte mapping for japanese/chinese/korean */ return 0; } else if (format == 4) { /* standard mapping for windows fonts: binary search collection of ranges */ stbtt_uint16 segcount = ttUSHORT(data+index_map+6) >> 1; stbtt_uint16 searchRange = ttUSHORT(data+index_map+8) >> 1; stbtt_uint16 entrySelector = ttUSHORT(data+index_map+10); stbtt_uint16 rangeShift = ttUSHORT(data+index_map+12) >> 1; /* do a binary search of the segments */ stbtt_uint32 endCount = index_map + 14; stbtt_uint32 search = endCount; if (unicode_codepoint > 0xffff) return 0; /* they lie from endCount .. endCount + segCount */ /* but searchRange is the nearest power of two, so... */ if (unicode_codepoint >= ttUSHORT(data + search + rangeShift*2)) search += rangeShift*2; /* now decrement to bias correctly to find smallest */ search -= 2; while (entrySelector) { stbtt_uint16 end; searchRange >>= 1; end = ttUSHORT(data + search + searchRange*2); if (unicode_codepoint > end) search += searchRange*2; --entrySelector; } search += 2; { stbtt_uint16 offset, start, last; stbtt_uint16 item = (stbtt_uint16) ((search - endCount) >> 1); start = ttUSHORT(data + index_map + 14 + segcount*2 + 2 + 2*item); last = ttUSHORT(data + endCount + 2*item); if (unicode_codepoint < start || unicode_codepoint > last) return 0; offset = ttUSHORT(data + index_map + 14 + segcount*6 + 2 + 2*item); if (offset == 0) return (stbtt_uint16) (unicode_codepoint + ttSHORT(data + index_map + 14 + segcount*4 + 2 + 2*item)); return ttUSHORT(data + offset + (unicode_codepoint-start)*2 + index_map + 14 + segcount*6 + 2 + 2*item); } } else if (format == 12 || format == 13) { stbtt_uint32 ngroups = ttULONG(data+index_map+12); stbtt_int32 low,high; low = 0; high = (stbtt_int32)ngroups; /* Binary search the right group. */ while (low < high) { stbtt_int32 mid = low + ((high-low) >> 1); /* rounds down, so low <= mid < high */ stbtt_uint32 start_char = ttULONG(data+index_map+16+mid*12); stbtt_uint32 end_char = ttULONG(data+index_map+16+mid*12+4); if ((stbtt_uint32) unicode_codepoint < start_char) high = mid; else if ((stbtt_uint32) unicode_codepoint > end_char) low = mid+1; else { stbtt_uint32 start_glyph = ttULONG(data+index_map+16+mid*12+8); if (format == 12) return start_glyph + unicode_codepoint-start_char; else /* format == 13 */ return start_glyph; } } return 0; /* not found */ } /* @TODO */ STBTT_assert(0); return 0; } STBTT_DEF int stbtt_GetCodepointShape(const stbtt_fontinfo *info, int unicode_codepoint, stbtt_vertex **vertices) { return stbtt_GetGlyphShape(info, stbtt_FindGlyphIndex(info, unicode_codepoint), vertices); } static void stbtt_setvertex(stbtt_vertex *v, stbtt_uint8 type, stbtt_int32 x, stbtt_int32 y, stbtt_int32 cx, stbtt_int32 cy) { v->type = type; v->x = (stbtt_int16) x; v->y = (stbtt_int16) y; v->cx = (stbtt_int16) cx; v->cy = (stbtt_int16) cy; } static int stbtt__GetGlyfOffset(const stbtt_fontinfo *info, int glyph_index) { int g1,g2; STBTT_assert(!info->cff.size); if (glyph_index >= info->numGlyphs) return -1; /* glyph index out of range */ if (info->indexToLocFormat >= 2) return -1; /* unknown index->glyph map format */ if (info->indexToLocFormat == 0) { g1 = info->glyf + ttUSHORT(info->data + info->loca + glyph_index * 2) * 2; g2 = info->glyf + ttUSHORT(info->data + info->loca + glyph_index * 2 + 2) * 2; } else { g1 = info->glyf + ttULONG (info->data + info->loca + glyph_index * 4); g2 = info->glyf + ttULONG (info->data + info->loca + glyph_index * 4 + 4); } return g1==g2 ? -1 : g1; /* if length is 0, return -1 */ } static int stbtt__GetGlyphInfoT2(const stbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1); STBTT_DEF int stbtt_GetGlyphBox(const stbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1) { if (info->cff.size) { stbtt__GetGlyphInfoT2(info, glyph_index, x0, y0, x1, y1); } else { int g = stbtt__GetGlyfOffset(info, glyph_index); if (g < 0) return 0; if (x0) *x0 = ttSHORT(info->data + g + 2); if (y0) *y0 = ttSHORT(info->data + g + 4); if (x1) *x1 = ttSHORT(info->data + g + 6); if (y1) *y1 = ttSHORT(info->data + g + 8); } return 1; } STBTT_DEF int stbtt_GetCodepointBox(const stbtt_fontinfo *info, int codepoint, int *x0, int *y0, int *x1, int *y1) { return stbtt_GetGlyphBox(info, stbtt_FindGlyphIndex(info,codepoint), x0,y0,x1,y1); } STBTT_DEF int stbtt_IsGlyphEmpty(const stbtt_fontinfo *info, int glyph_index) { stbtt_int16 numberOfContours; int g; if (info->cff.size) return stbtt__GetGlyphInfoT2(info, glyph_index, NULL, NULL, NULL, NULL) == 0; g = stbtt__GetGlyfOffset(info, glyph_index); if (g < 0) return 1; numberOfContours = ttSHORT(info->data + g); return numberOfContours == 0; } static int stbtt__close_shape(stbtt_vertex *vertices, int num_vertices, int was_off, int start_off, stbtt_int32 sx, stbtt_int32 sy, stbtt_int32 scx, stbtt_int32 scy, stbtt_int32 cx, stbtt_int32 cy) { if (start_off) { if (was_off) stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, (cx+scx)>>1, (cy+scy)>>1, cx,cy); stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, sx,sy,scx,scy); } else { if (was_off) stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve,sx,sy,cx,cy); else stbtt_setvertex(&vertices[num_vertices++], STBTT_vline,sx,sy,0,0); } return num_vertices; } static int stbtt__GetGlyphShapeTT(const stbtt_fontinfo *info, int glyph_index, stbtt_vertex **pvertices) { stbtt_int16 numberOfContours; stbtt_uint8 *endPtsOfContours; stbtt_uint8 *data = info->data; stbtt_vertex *vertices=0; int num_vertices=0; int g = stbtt__GetGlyfOffset(info, glyph_index); *pvertices = NULL; if (g < 0) return 0; numberOfContours = ttSHORT(data + g); if (numberOfContours > 0) { stbtt_uint8 flags=0,flagcount; stbtt_int32 ins, i,j=0,m,n, next_move, was_off=0, off, start_off=0; stbtt_int32 x,y,cx,cy,sx,sy, scx,scy; stbtt_uint8 *points; endPtsOfContours = (data + g + 10); ins = ttUSHORT(data + g + 10 + numberOfContours * 2); points = data + g + 10 + numberOfContours * 2 + 2 + ins; n = 1+ttUSHORT(endPtsOfContours + numberOfContours*2-2); m = n + 2*numberOfContours; /* a loose bound on how many vertices we might need */ vertices = (stbtt_vertex *) STBTT_malloc(m * sizeof(vertices[0]), info->userdata); if (vertices == 0) return 0; next_move = 0; flagcount=0; /* in first pass, we load uninterpreted data into the allocated array */ /* above, shifted to the end of the array so we won't overwrite it when */ /* we create our final data starting from the front */ off = m - n; /* starting offset for uninterpreted data, regardless of how m ends up being calculated */ /* first load flags */ for (i=0; i < n; ++i) { if (flagcount == 0) { flags = *points++; if (flags & 8) flagcount = *points++; } else --flagcount; vertices[off+i].type = flags; } /* now load x coordinates */ x=0; for (i=0; i < n; ++i) { flags = vertices[off+i].type; if (flags & 2) { stbtt_int16 dx = *points++; x += (flags & 16) ? dx : -dx; /* ??? */ } else { if (!(flags & 16)) { x = x + (stbtt_int16) (points[0]*256 + points[1]); points += 2; } } vertices[off+i].x = (stbtt_int16) x; } /* now load y coordinates */ y=0; for (i=0; i < n; ++i) { flags = vertices[off+i].type; if (flags & 4) { stbtt_int16 dy = *points++; y += (flags & 32) ? dy : -dy; /* ??? */ } else { if (!(flags & 32)) { y = y + (stbtt_int16) (points[0]*256 + points[1]); points += 2; } } vertices[off+i].y = (stbtt_int16) y; } /* now convert them to our format */ num_vertices=0; sx = sy = cx = cy = scx = scy = 0; for (i=0; i < n; ++i) { flags = vertices[off+i].type; x = (stbtt_int16) vertices[off+i].x; y = (stbtt_int16) vertices[off+i].y; if (next_move == i) { if (i != 0) num_vertices = stbtt__close_shape(vertices, num_vertices, was_off, start_off, sx,sy,scx,scy,cx,cy); /* now start the new one */ start_off = !(flags & 1); if (start_off) { /* if we start off with an off-curve point, then when we need to find a point on the curve */ /* where we can start, and we need to save some state for when we wraparound. */ scx = x; scy = y; if (!(vertices[off+i+1].type & 1)) { /* next point is also a curve point, so interpolate an on-point curve */ sx = (x + (stbtt_int32) vertices[off+i+1].x) >> 1; sy = (y + (stbtt_int32) vertices[off+i+1].y) >> 1; } else { /* otherwise just use the next point as our start point */ sx = (stbtt_int32) vertices[off+i+1].x; sy = (stbtt_int32) vertices[off+i+1].y; ++i; /* we're using point i+1 as the starting point, so skip it */ } } else { sx = x; sy = y; } stbtt_setvertex(&vertices[num_vertices++], STBTT_vmove,sx,sy,0,0); was_off = 0; next_move = 1 + ttUSHORT(endPtsOfContours+j*2); ++j; } else { if (!(flags & 1)) { /* if it's a curve */ if (was_off) /* two off-curve control points in a row means interpolate an on-curve midpoint */ stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, (cx+x)>>1, (cy+y)>>1, cx, cy); cx = x; cy = y; was_off = 1; } else { if (was_off) stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, x,y, cx, cy); else stbtt_setvertex(&vertices[num_vertices++], STBTT_vline, x,y,0,0); was_off = 0; } } } num_vertices = stbtt__close_shape(vertices, num_vertices, was_off, start_off, sx,sy,scx,scy,cx,cy); } else if (numberOfContours < 0) { /* Compound shapes. */ int more = 1; stbtt_uint8 *comp = data + g + 10; num_vertices = 0; vertices = 0; while (more) { stbtt_uint16 flags, gidx; int comp_num_verts = 0, i; stbtt_vertex *comp_verts = 0, *tmp = 0; float mtx[6] = {1,0,0,1,0,0}, m, n; flags = ttSHORT(comp); comp+=2; gidx = ttSHORT(comp); comp+=2; if (flags & 2) { /* XY values */ if (flags & 1) { /* shorts */ mtx[4] = ttSHORT(comp); comp+=2; mtx[5] = ttSHORT(comp); comp+=2; } else { mtx[4] = ttCHAR(comp); comp+=1; mtx[5] = ttCHAR(comp); comp+=1; } } else { /* @TODO handle matching point */ STBTT_assert(0); } if (flags & (1<<3)) { /* WE_HAVE_A_SCALE */ mtx[0] = mtx[3] = ttSHORT(comp)/16384.0f; comp+=2; mtx[1] = mtx[2] = 0; } else if (flags & (1<<6)) { /* WE_HAVE_AN_X_AND_YSCALE */ mtx[0] = ttSHORT(comp)/16384.0f; comp+=2; mtx[1] = mtx[2] = 0; mtx[3] = ttSHORT(comp)/16384.0f; comp+=2; } else if (flags & (1<<7)) { /* WE_HAVE_A_TWO_BY_TWO */ mtx[0] = ttSHORT(comp)/16384.0f; comp+=2; mtx[1] = ttSHORT(comp)/16384.0f; comp+=2; mtx[2] = ttSHORT(comp)/16384.0f; comp+=2; mtx[3] = ttSHORT(comp)/16384.0f; comp+=2; } /* Find transformation scales. */ m = (float) STBTT_sqrt(mtx[0]*mtx[0] + mtx[1]*mtx[1]); n = (float) STBTT_sqrt(mtx[2]*mtx[2] + mtx[3]*mtx[3]); /* Get indexed glyph. */ comp_num_verts = stbtt_GetGlyphShape(info, gidx, &comp_verts); if (comp_num_verts > 0) { /* Transform vertices. */ for (i = 0; i < comp_num_verts; ++i) { stbtt_vertex* v = &comp_verts[i]; stbtt_vertex_type x,y; x=v->x; y=v->y; v->x = (stbtt_vertex_type)(m * (mtx[0]*x + mtx[2]*y + mtx[4])); v->y = (stbtt_vertex_type)(n * (mtx[1]*x + mtx[3]*y + mtx[5])); x=v->cx; y=v->cy; v->cx = (stbtt_vertex_type)(m * (mtx[0]*x + mtx[2]*y + mtx[4])); v->cy = (stbtt_vertex_type)(n * (mtx[1]*x + mtx[3]*y + mtx[5])); } /* Append vertices. */ tmp = (stbtt_vertex*)STBTT_malloc((num_vertices+comp_num_verts)*sizeof(stbtt_vertex), info->userdata); if (!tmp) { if (vertices) STBTT_free(vertices, info->userdata); if (comp_verts) STBTT_free(comp_verts, info->userdata); return 0; } if (num_vertices > 0 && vertices) STBTT_memcpy(tmp, vertices, num_vertices*sizeof(stbtt_vertex)); STBTT_memcpy(tmp+num_vertices, comp_verts, comp_num_verts*sizeof(stbtt_vertex)); if (vertices) STBTT_free(vertices, info->userdata); vertices = tmp; STBTT_free(comp_verts, info->userdata); num_vertices += comp_num_verts; } /* More components ? */ more = flags & (1<<5); } } else { /* numberOfCounters == 0, do nothing */ } *pvertices = vertices; return num_vertices; } typedef struct { int bounds; int started; float first_x, first_y; float x, y; stbtt_int32 min_x, max_x, min_y, max_y; stbtt_vertex *pvertices; int num_vertices; } stbtt__csctx; #define STBTT__CSCTX_INIT(bounds) {bounds,0, 0,0, 0,0, 0,0,0,0, NULL, 0} static void stbtt__track_vertex(stbtt__csctx *c, stbtt_int32 x, stbtt_int32 y) { if (x > c->max_x || !c->started) c->max_x = x; if (y > c->max_y || !c->started) c->max_y = y; if (x < c->min_x || !c->started) c->min_x = x; if (y < c->min_y || !c->started) c->min_y = y; c->started = 1; } static void stbtt__csctx_v(stbtt__csctx *c, stbtt_uint8 type, stbtt_int32 x, stbtt_int32 y, stbtt_int32 cx, stbtt_int32 cy, stbtt_int32 cx1, stbtt_int32 cy1) { if (c->bounds) { stbtt__track_vertex(c, x, y); if (type == STBTT_vcubic) { stbtt__track_vertex(c, cx, cy); stbtt__track_vertex(c, cx1, cy1); } } else { stbtt_setvertex(&c->pvertices[c->num_vertices], type, x, y, cx, cy); c->pvertices[c->num_vertices].cx1 = (stbtt_int16) cx1; c->pvertices[c->num_vertices].cy1 = (stbtt_int16) cy1; } c->num_vertices++; } static void stbtt__csctx_close_shape(stbtt__csctx *ctx) { if (ctx->first_x != ctx->x || ctx->first_y != ctx->y) stbtt__csctx_v(ctx, STBTT_vline, (int)ctx->first_x, (int)ctx->first_y, 0, 0, 0, 0); } static void stbtt__csctx_rmove_to(stbtt__csctx *ctx, float dx, float dy) { stbtt__csctx_close_shape(ctx); ctx->first_x = ctx->x = ctx->x + dx; ctx->first_y = ctx->y = ctx->y + dy; stbtt__csctx_v(ctx, STBTT_vmove, (int)ctx->x, (int)ctx->y, 0, 0, 0, 0); } static void stbtt__csctx_rline_to(stbtt__csctx *ctx, float dx, float dy) { ctx->x += dx; ctx->y += dy; stbtt__csctx_v(ctx, STBTT_vline, (int)ctx->x, (int)ctx->y, 0, 0, 0, 0); } static void stbtt__csctx_rccurve_to(stbtt__csctx *ctx, float dx1, float dy1, float dx2, float dy2, float dx3, float dy3) { float cx1 = ctx->x + dx1; float cy1 = ctx->y + dy1; float cx2 = cx1 + dx2; float cy2 = cy1 + dy2; ctx->x = cx2 + dx3; ctx->y = cy2 + dy3; stbtt__csctx_v(ctx, STBTT_vcubic, (int)ctx->x, (int)ctx->y, (int)cx1, (int)cy1, (int)cx2, (int)cy2); } static stbtt__buf stbtt__get_subr(stbtt__buf idx, int n) { int count = stbtt__cff_index_count(&idx); int bias = 107; if (count >= 33900) bias = 32768; else if (count >= 1240) bias = 1131; n += bias; if (n < 0 || n >= count) return stbtt__new_buf(NULL, 0); return stbtt__cff_index_get(idx, n); } static stbtt__buf stbtt__cid_get_glyph_subrs(const stbtt_fontinfo *info, int glyph_index) { stbtt__buf fdselect = info->fdselect; int nranges, start, end, v, fmt, fdselector = -1, i; stbtt__buf_seek(&fdselect, 0); fmt = stbtt__buf_get8(&fdselect); if (fmt == 0) { /* untested */ stbtt__buf_skip(&fdselect, glyph_index); fdselector = stbtt__buf_get8(&fdselect); } else if (fmt == 3) { nranges = stbtt__buf_get16(&fdselect); start = stbtt__buf_get16(&fdselect); for (i = 0; i < nranges; i++) { v = stbtt__buf_get8(&fdselect); end = stbtt__buf_get16(&fdselect); if (glyph_index >= start && glyph_index < end) { fdselector = v; break; } start = end; } } if (fdselector == -1) stbtt__new_buf(NULL, 0); return stbtt__get_subrs(info->cff, stbtt__cff_index_get(info->fontdicts, fdselector)); } static int stbtt__run_charstring(const stbtt_fontinfo *info, int glyph_index, stbtt__csctx *c) { int in_header = 1, maskbits = 0, subr_stack_height = 0, sp = 0, v, i, b0; int has_subrs = 0, clear_stack; float s[48]; stbtt__buf subr_stack[10], subrs = info->subrs, b; float f; #define STBTT__CSERR(s) (0) /* this currently ignores the initial width value, which isn't needed if we have hmtx */ b = stbtt__cff_index_get(info->charstrings, glyph_index); while (b.cursor < b.size) { i = 0; clear_stack = 1; b0 = stbtt__buf_get8(&b); switch (b0) { /* @TODO implement hinting */ case 0x13: /* hintmask */ case 0x14: /* cntrmask */ if (in_header) maskbits += (sp / 2); /* implicit "vstem" */ in_header = 0; stbtt__buf_skip(&b, (maskbits + 7) / 8); break; case 0x01: /* hstem */ case 0x03: /* vstem */ case 0x12: /* hstemhm */ case 0x17: /* vstemhm */ maskbits += (sp / 2); break; case 0x15: /* rmoveto */ in_header = 0; if (sp < 2) return STBTT__CSERR("rmoveto stack"); stbtt__csctx_rmove_to(c, s[sp-2], s[sp-1]); break; case 0x04: /* vmoveto */ in_header = 0; if (sp < 1) return STBTT__CSERR("vmoveto stack"); stbtt__csctx_rmove_to(c, 0, s[sp-1]); break; case 0x16: /* hmoveto */ in_header = 0; if (sp < 1) return STBTT__CSERR("hmoveto stack"); stbtt__csctx_rmove_to(c, s[sp-1], 0); break; case 0x05: /* rlineto */ if (sp < 2) return STBTT__CSERR("rlineto stack"); for (; i + 1 < sp; i += 2) stbtt__csctx_rline_to(c, s[i], s[i+1]); break; /* hlineto/vlineto and vhcurveto/hvcurveto alternate horizontal and vertical */ /* starting from a different place. */ case 0x07: /* vlineto */ if (sp < 1) return STBTT__CSERR("vlineto stack"); goto vlineto; case 0x06: /* hlineto */ if (sp < 1) return STBTT__CSERR("hlineto stack"); for (;;) { if (i >= sp) break; stbtt__csctx_rline_to(c, s[i], 0); i++; vlineto: if (i >= sp) break; stbtt__csctx_rline_to(c, 0, s[i]); i++; } break; case 0x1F: /* hvcurveto */ if (sp < 4) return STBTT__CSERR("hvcurveto stack"); goto hvcurveto; case 0x1E: /* vhcurveto */ if (sp < 4) return STBTT__CSERR("vhcurveto stack"); for (;;) { if (i + 3 >= sp) break; stbtt__csctx_rccurve_to(c, 0, s[i], s[i+1], s[i+2], s[i+3], (sp - i == 5) ? s[i + 4] : 0.0f); i += 4; hvcurveto: if (i + 3 >= sp) break; stbtt__csctx_rccurve_to(c, s[i], 0, s[i+1], s[i+2], (sp - i == 5) ? s[i+4] : 0.0f, s[i+3]); i += 4; } break; case 0x08: /* rrcurveto */ if (sp < 6) return STBTT__CSERR("rcurveline stack"); for (; i + 5 < sp; i += 6) stbtt__csctx_rccurve_to(c, s[i], s[i+1], s[i+2], s[i+3], s[i+4], s[i+5]); break; case 0x18: /* rcurveline */ if (sp < 8) return STBTT__CSERR("rcurveline stack"); for (; i + 5 < sp - 2; i += 6) stbtt__csctx_rccurve_to(c, s[i], s[i+1], s[i+2], s[i+3], s[i+4], s[i+5]); if (i + 1 >= sp) return STBTT__CSERR("rcurveline stack"); stbtt__csctx_rline_to(c, s[i], s[i+1]); break; case 0x19: /* rlinecurve */ if (sp < 8) return STBTT__CSERR("rlinecurve stack"); for (; i + 1 < sp - 6; i += 2) stbtt__csctx_rline_to(c, s[i], s[i+1]); if (i + 5 >= sp) return STBTT__CSERR("rlinecurve stack"); stbtt__csctx_rccurve_to(c, s[i], s[i+1], s[i+2], s[i+3], s[i+4], s[i+5]); break; case 0x1A: /* vvcurveto */ case 0x1B: /* hhcurveto */ if (sp < 4) return STBTT__CSERR("(vv|hh)curveto stack"); f = 0.0; if (sp & 1) { f = s[i]; i++; } for (; i + 3 < sp; i += 4) { if (b0 == 0x1B) stbtt__csctx_rccurve_to(c, s[i], f, s[i+1], s[i+2], s[i+3], 0.0); else stbtt__csctx_rccurve_to(c, f, s[i], s[i+1], s[i+2], 0.0, s[i+3]); f = 0.0; } break; case 0x0A: /* callsubr */ if (!has_subrs) { if (info->fdselect.size) subrs = stbtt__cid_get_glyph_subrs(info, glyph_index); has_subrs = 1; } /* FALLTHROUGH */ case 0x1D: /* callgsubr */ if (sp < 1) return STBTT__CSERR("call(g|)subr stack"); v = (int) s[--sp]; if (subr_stack_height >= 10) return STBTT__CSERR("recursion limit"); subr_stack[subr_stack_height++] = b; b = stbtt__get_subr(b0 == 0x0A ? subrs : info->gsubrs, v); if (b.size == 0) return STBTT__CSERR("subr not found"); b.cursor = 0; clear_stack = 0; break; case 0x0B: /* return */ if (subr_stack_height <= 0) return STBTT__CSERR("return outside subr"); b = subr_stack[--subr_stack_height]; clear_stack = 0; break; case 0x0E: /* endchar */ stbtt__csctx_close_shape(c); return 1; case 0x0C: { /* two-byte escape */ float dx1, dx2, dx3, dx4, dx5, dx6, dy1, dy2, dy3, dy4, dy5, dy6; float dx, dy; int b1 = stbtt__buf_get8(&b); switch (b1) { /* @TODO These "flex" implementations ignore the flex-depth and resolution, */ /* and always draw beziers. */ case 0x22: /* hflex */ if (sp < 7) return STBTT__CSERR("hflex stack"); dx1 = s[0]; dx2 = s[1]; dy2 = s[2]; dx3 = s[3]; dx4 = s[4]; dx5 = s[5]; dx6 = s[6]; stbtt__csctx_rccurve_to(c, dx1, 0, dx2, dy2, dx3, 0); stbtt__csctx_rccurve_to(c, dx4, 0, dx5, -dy2, dx6, 0); break; case 0x23: /* flex */ if (sp < 13) return STBTT__CSERR("flex stack"); dx1 = s[0]; dy1 = s[1]; dx2 = s[2]; dy2 = s[3]; dx3 = s[4]; dy3 = s[5]; dx4 = s[6]; dy4 = s[7]; dx5 = s[8]; dy5 = s[9]; dx6 = s[10]; dy6 = s[11]; /* fd is s[12] */ stbtt__csctx_rccurve_to(c, dx1, dy1, dx2, dy2, dx3, dy3); stbtt__csctx_rccurve_to(c, dx4, dy4, dx5, dy5, dx6, dy6); break; case 0x24: /* hflex1 */ if (sp < 9) return STBTT__CSERR("hflex1 stack"); dx1 = s[0]; dy1 = s[1]; dx2 = s[2]; dy2 = s[3]; dx3 = s[4]; dx4 = s[5]; dx5 = s[6]; dy5 = s[7]; dx6 = s[8]; stbtt__csctx_rccurve_to(c, dx1, dy1, dx2, dy2, dx3, 0); stbtt__csctx_rccurve_to(c, dx4, 0, dx5, dy5, dx6, -(dy1+dy2+dy5)); break; case 0x25: /* flex1 */ if (sp < 11) return STBTT__CSERR("flex1 stack"); dx1 = s[0]; dy1 = s[1]; dx2 = s[2]; dy2 = s[3]; dx3 = s[4]; dy3 = s[5]; dx4 = s[6]; dy4 = s[7]; dx5 = s[8]; dy5 = s[9]; dx6 = dy6 = s[10]; dx = dx1+dx2+dx3+dx4+dx5; dy = dy1+dy2+dy3+dy4+dy5; if (STBTT_fabs(dx) > STBTT_fabs(dy)) dy6 = -dy; else dx6 = -dx; stbtt__csctx_rccurve_to(c, dx1, dy1, dx2, dy2, dx3, dy3); stbtt__csctx_rccurve_to(c, dx4, dy4, dx5, dy5, dx6, dy6); break; default: return STBTT__CSERR("unimplemented"); } } break; default: if (b0 != 255 && b0 != 28 && b0 < 32) return STBTT__CSERR("reserved operator"); /* push immediate */ if (b0 == 255) { f = (float)(stbtt_int32)stbtt__buf_get32(&b) / 0x10000; } else { stbtt__buf_skip(&b, -1); f = (float)(stbtt_int16)stbtt__cff_int(&b); } if (sp >= 48) return STBTT__CSERR("push stack overflow"); s[sp++] = f; clear_stack = 0; break; } if (clear_stack) sp = 0; } return STBTT__CSERR("no endchar"); #undef STBTT__CSERR } static int stbtt__GetGlyphShapeT2(const stbtt_fontinfo *info, int glyph_index, stbtt_vertex **pvertices) { /* runs the charstring twice, once to count and once to output (to avoid realloc) */ stbtt__csctx count_ctx = STBTT__CSCTX_INIT(1); stbtt__csctx output_ctx = STBTT__CSCTX_INIT(0); if (stbtt__run_charstring(info, glyph_index, &count_ctx)) { *pvertices = (stbtt_vertex*)STBTT_malloc(count_ctx.num_vertices*sizeof(stbtt_vertex), info->userdata); output_ctx.pvertices = *pvertices; if (stbtt__run_charstring(info, glyph_index, &output_ctx)) { STBTT_assert(output_ctx.num_vertices == count_ctx.num_vertices); return output_ctx.num_vertices; } } *pvertices = NULL; return 0; } static int stbtt__GetGlyphInfoT2(const stbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1) { stbtt__csctx c = STBTT__CSCTX_INIT(1); int r = stbtt__run_charstring(info, glyph_index, &c); if (x0) *x0 = r ? c.min_x : 0; if (y0) *y0 = r ? c.min_y : 0; if (x1) *x1 = r ? c.max_x : 0; if (y1) *y1 = r ? c.max_y : 0; return r ? c.num_vertices : 0; } STBTT_DEF int stbtt_GetGlyphShape(const stbtt_fontinfo *info, int glyph_index, stbtt_vertex **pvertices) { if (!info->cff.size) return stbtt__GetGlyphShapeTT(info, glyph_index, pvertices); else return stbtt__GetGlyphShapeT2(info, glyph_index, pvertices); } STBTT_DEF void stbtt_GetGlyphHMetrics(const stbtt_fontinfo *info, int glyph_index, int *advanceWidth, int *leftSideBearing) { stbtt_uint16 numOfLongHorMetrics = ttUSHORT(info->data+info->hhea + 34); if (glyph_index < numOfLongHorMetrics) { if (advanceWidth) *advanceWidth = ttSHORT(info->data + info->hmtx + 4*glyph_index); if (leftSideBearing) *leftSideBearing = ttSHORT(info->data + info->hmtx + 4*glyph_index + 2); } else { if (advanceWidth) *advanceWidth = ttSHORT(info->data + info->hmtx + 4*(numOfLongHorMetrics-1)); if (leftSideBearing) *leftSideBearing = ttSHORT(info->data + info->hmtx + 4*numOfLongHorMetrics + 2*(glyph_index - numOfLongHorMetrics)); } } STBTT_DEF int stbtt_GetKerningTableLength(const stbtt_fontinfo *info) { stbtt_uint8 *data = info->data + info->kern; /* we only look at the first table. it must be 'horizontal' and format 0. */ if (!info->kern) return 0; if (ttUSHORT(data+2) < 1) /* number of tables, need at least 1 */ return 0; if (ttUSHORT(data+8) != 1) /* horizontal flag must be set in format */ return 0; return ttUSHORT(data+10); } STBTT_DEF int stbtt_GetKerningTable(const stbtt_fontinfo *info, stbtt_kerningentry* table, int table_length) { stbtt_uint8 *data = info->data + info->kern; int k, length; /* we only look at the first table. it must be 'horizontal' and format 0. */ if (!info->kern) return 0; if (ttUSHORT(data+2) < 1) /* number of tables, need at least 1 */ return 0; if (ttUSHORT(data+8) != 1) /* horizontal flag must be set in format */ return 0; length = ttUSHORT(data+10); if (table_length < length) length = table_length; for (k = 0; k < length; k++) { table[k].glyph1 = ttUSHORT(data+18+(k*6)); table[k].glyph2 = ttUSHORT(data+20+(k*6)); table[k].advance = ttSHORT(data+22+(k*6)); } return length; } static int stbtt__GetGlyphKernInfoAdvance(const stbtt_fontinfo *info, int glyph1, int glyph2) { stbtt_uint8 *data = info->data + info->kern; stbtt_uint32 needle, straw; int l, r, m; /* we only look at the first table. it must be 'horizontal' and format 0. */ if (!info->kern) return 0; if (ttUSHORT(data+2) < 1) /* number of tables, need at least 1 */ return 0; if (ttUSHORT(data+8) != 1) /* horizontal flag must be set in format */ return 0; l = 0; r = ttUSHORT(data+10) - 1; needle = glyph1 << 16 | glyph2; while (l <= r) { m = (l + r) >> 1; straw = ttULONG(data+18+(m*6)); /* note: unaligned read */ if (needle < straw) r = m - 1; else if (needle > straw) l = m + 1; else return ttSHORT(data+22+(m*6)); } return 0; } static stbtt_int32 stbtt__GetCoverageIndex(stbtt_uint8 *coverageTable, int glyph) { stbtt_uint16 coverageFormat = ttUSHORT(coverageTable); switch (coverageFormat) { case 1: { stbtt_uint16 glyphCount = ttUSHORT(coverageTable + 2); /* Binary search. */ stbtt_int32 l=0, r=glyphCount-1, m; int straw, needle=glyph; while (l <= r) { stbtt_uint8 *glyphArray = coverageTable + 4; stbtt_uint16 glyphID; m = (l + r) >> 1; glyphID = ttUSHORT(glyphArray + 2 * m); straw = glyphID; if (needle < straw) r = m - 1; else if (needle > straw) l = m + 1; else { return m; } } break; } case 2: { stbtt_uint16 rangeCount = ttUSHORT(coverageTable + 2); stbtt_uint8 *rangeArray = coverageTable + 4; /* Binary search. */ stbtt_int32 l=0, r=rangeCount-1, m; int strawStart, strawEnd, needle=glyph; while (l <= r) { stbtt_uint8 *rangeRecord; m = (l + r) >> 1; rangeRecord = rangeArray + 6 * m; strawStart = ttUSHORT(rangeRecord); strawEnd = ttUSHORT(rangeRecord + 2); if (needle < strawStart) r = m - 1; else if (needle > strawEnd) l = m + 1; else { stbtt_uint16 startCoverageIndex = ttUSHORT(rangeRecord + 4); return startCoverageIndex + glyph - strawStart; } } break; } default: return -1; /* unsupported */ } return -1; } static stbtt_int32 stbtt__GetGlyphClass(stbtt_uint8 *classDefTable, int glyph) { stbtt_uint16 classDefFormat = ttUSHORT(classDefTable); switch (classDefFormat) { case 1: { stbtt_uint16 startGlyphID = ttUSHORT(classDefTable + 2); stbtt_uint16 glyphCount = ttUSHORT(classDefTable + 4); stbtt_uint8 *classDef1ValueArray = classDefTable + 6; if (glyph >= startGlyphID && glyph < startGlyphID + glyphCount) return (stbtt_int32)ttUSHORT(classDef1ValueArray + 2 * (glyph - startGlyphID)); break; } case 2: { stbtt_uint16 classRangeCount = ttUSHORT(classDefTable + 2); stbtt_uint8 *classRangeRecords = classDefTable + 4; /* Binary search. */ stbtt_int32 l=0, r=classRangeCount-1, m; int strawStart, strawEnd, needle=glyph; while (l <= r) { stbtt_uint8 *classRangeRecord; m = (l + r) >> 1; classRangeRecord = classRangeRecords + 6 * m; strawStart = ttUSHORT(classRangeRecord); strawEnd = ttUSHORT(classRangeRecord + 2); if (needle < strawStart) r = m - 1; else if (needle > strawEnd) l = m + 1; else return (stbtt_int32)ttUSHORT(classRangeRecord + 4); } break; } default: return -1; /* Unsupported definition type, return an error. */ } /* "All glyphs not assigned to a class fall into class 0". (OpenType spec) */ return 0; } /* Define to STBTT_assert(x) if you want to break on unimplemented formats. */ #define STBTT_GPOS_TODO_assert(x) static stbtt_int32 stbtt__GetGlyphGPOSInfoAdvance(const stbtt_fontinfo *info, int glyph1, int glyph2) { stbtt_uint16 lookupListOffset; stbtt_uint8 *lookupList; stbtt_uint16 lookupCount; stbtt_uint8 *data; stbtt_int32 i, sti; if (!info->gpos) return 0; data = info->data + info->gpos; if (ttUSHORT(data+0) != 1) return 0; /* Major version 1 */ if (ttUSHORT(data+2) != 0) return 0; /* Minor version 0 */ lookupListOffset = ttUSHORT(data+8); lookupList = data + lookupListOffset; lookupCount = ttUSHORT(lookupList); for (i=0; i= pairSetCount) return 0; needle=glyph2; r=pairValueCount-1; l=0; /* Binary search. */ while (l <= r) { stbtt_uint16 secondGlyph; stbtt_uint8 *pairValue; m = (l + r) >> 1; pairValue = pairValueArray + (2 + valueRecordPairSizeInBytes) * m; secondGlyph = ttUSHORT(pairValue); straw = secondGlyph; if (needle < straw) r = m - 1; else if (needle > straw) l = m + 1; else { stbtt_int16 xAdvance = ttSHORT(pairValue + 2); return xAdvance; } } } else return 0; break; } case 2: { stbtt_uint16 valueFormat1 = ttUSHORT(table + 4); stbtt_uint16 valueFormat2 = ttUSHORT(table + 6); if (valueFormat1 == 4 && valueFormat2 == 0) { /* Support more formats? */ stbtt_uint16 classDef1Offset = ttUSHORT(table + 8); stbtt_uint16 classDef2Offset = ttUSHORT(table + 10); int glyph1class = stbtt__GetGlyphClass(table + classDef1Offset, glyph1); int glyph2class = stbtt__GetGlyphClass(table + classDef2Offset, glyph2); stbtt_uint16 class1Count = ttUSHORT(table + 12); stbtt_uint16 class2Count = ttUSHORT(table + 14); stbtt_uint8 *class1Records, *class2Records; stbtt_int16 xAdvance; if (glyph1class < 0 || glyph1class >= class1Count) return 0; /* malformed */ if (glyph2class < 0 || glyph2class >= class2Count) return 0; /* malformed */ class1Records = table + 16; class2Records = class1Records + 2 * (glyph1class * class2Count); xAdvance = ttSHORT(class2Records + 2 * glyph2class); return xAdvance; } else return 0; break; } default: return 0; /* Unsupported position format */ } } } return 0; } STBTT_DEF int stbtt_GetGlyphKernAdvance(const stbtt_fontinfo *info, int g1, int g2) { int xAdvance = 0; if (info->gpos) xAdvance += stbtt__GetGlyphGPOSInfoAdvance(info, g1, g2); else if (info->kern) xAdvance += stbtt__GetGlyphKernInfoAdvance(info, g1, g2); return xAdvance; } STBTT_DEF int stbtt_GetCodepointKernAdvance(const stbtt_fontinfo *info, int ch1, int ch2) { if (!info->kern && !info->gpos) /* if no kerning table, don't waste time looking up both codepoint->glyphs */ return 0; return stbtt_GetGlyphKernAdvance(info, stbtt_FindGlyphIndex(info,ch1), stbtt_FindGlyphIndex(info,ch2)); } STBTT_DEF void stbtt_GetCodepointHMetrics(const stbtt_fontinfo *info, int codepoint, int *advanceWidth, int *leftSideBearing) { stbtt_GetGlyphHMetrics(info, stbtt_FindGlyphIndex(info,codepoint), advanceWidth, leftSideBearing); } STBTT_DEF void stbtt_GetFontVMetrics(const stbtt_fontinfo *info, int *ascent, int *descent, int *lineGap) { if (ascent ) *ascent = ttSHORT(info->data+info->hhea + 4); if (descent) *descent = ttSHORT(info->data+info->hhea + 6); if (lineGap) *lineGap = ttSHORT(info->data+info->hhea + 8); } STBTT_DEF int stbtt_GetFontVMetricsOS2(const stbtt_fontinfo *info, int *typoAscent, int *typoDescent, int *typoLineGap) { int tab = stbtt__find_table(info->data, info->fontstart, "OS/2"); if (!tab) return 0; if (typoAscent ) *typoAscent = ttSHORT(info->data+tab + 68); if (typoDescent) *typoDescent = ttSHORT(info->data+tab + 70); if (typoLineGap) *typoLineGap = ttSHORT(info->data+tab + 72); return 1; } STBTT_DEF void stbtt_GetFontBoundingBox(const stbtt_fontinfo *info, int *x0, int *y0, int *x1, int *y1) { *x0 = ttSHORT(info->data + info->head + 36); *y0 = ttSHORT(info->data + info->head + 38); *x1 = ttSHORT(info->data + info->head + 40); *y1 = ttSHORT(info->data + info->head + 42); } STBTT_DEF float stbtt_ScaleForPixelHeight(const stbtt_fontinfo *info, float height) { int fheight = ttSHORT(info->data + info->hhea + 4) - ttSHORT(info->data + info->hhea + 6); return (float) height / fheight; } STBTT_DEF float stbtt_ScaleForMappingEmToPixels(const stbtt_fontinfo *info, float pixels) { int unitsPerEm = ttUSHORT(info->data + info->head + 18); return pixels / unitsPerEm; } STBTT_DEF void stbtt_FreeShape(const stbtt_fontinfo *info, stbtt_vertex *v) { STBTT_free(v, info->userdata); } STBTT_DEF stbtt_uint8 *stbtt_FindSVGDoc(const stbtt_fontinfo *info, int gl) { int i; stbtt_uint8 *data = info->data; stbtt_uint8 *svg_doc_list = data + stbtt__get_svg((stbtt_fontinfo *) info); int numEntries = ttUSHORT(svg_doc_list); stbtt_uint8 *svg_docs = svg_doc_list + 2; for(i=0; i= ttUSHORT(svg_doc)) && (gl <= ttUSHORT(svg_doc + 2))) return svg_doc; } return 0; } STBTT_DEF int stbtt_GetGlyphSVG(const stbtt_fontinfo *info, int gl, const char **svg) { stbtt_uint8 *data = info->data; stbtt_uint8 *svg_doc; if (info->svg == 0) return 0; svg_doc = stbtt_FindSVGDoc(info, gl); if (svg_doc != NULL) { *svg = (char *) data + info->svg + ttULONG(svg_doc + 4); return ttULONG(svg_doc + 8); } else { return 0; } } STBTT_DEF int stbtt_GetCodepointSVG(const stbtt_fontinfo *info, int unicode_codepoint, const char **svg) { return stbtt_GetGlyphSVG(info, stbtt_FindGlyphIndex(info, unicode_codepoint), svg); } /* //////////////////////////////////////////////////////////////////////////// */ /* */ /* antialiasing software rasterizer */ /* */ STBTT_DEF void stbtt_GetGlyphBitmapBoxSubpixel(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y,float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1) { int x0=0,y0=0,x1,y1; /* =0 suppresses compiler warning */ if (!stbtt_GetGlyphBox(font, glyph, &x0,&y0,&x1,&y1)) { /* e.g. space character */ if (ix0) *ix0 = 0; if (iy0) *iy0 = 0; if (ix1) *ix1 = 0; if (iy1) *iy1 = 0; } else { /* move to integral bboxes (treating pixels as little squares, what pixels get touched)? */ if (ix0) *ix0 = STBTT_ifloor( x0 * scale_x + shift_x); if (iy0) *iy0 = STBTT_ifloor(-y1 * scale_y + shift_y); if (ix1) *ix1 = STBTT_iceil ( x1 * scale_x + shift_x); if (iy1) *iy1 = STBTT_iceil (-y0 * scale_y + shift_y); } } STBTT_DEF void stbtt_GetGlyphBitmapBox(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1) { stbtt_GetGlyphBitmapBoxSubpixel(font, glyph, scale_x, scale_y,0.0f,0.0f, ix0, iy0, ix1, iy1); } STBTT_DEF void stbtt_GetCodepointBitmapBoxSubpixel(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1) { stbtt_GetGlyphBitmapBoxSubpixel(font, stbtt_FindGlyphIndex(font,codepoint), scale_x, scale_y,shift_x,shift_y, ix0,iy0,ix1,iy1); } STBTT_DEF void stbtt_GetCodepointBitmapBox(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1) { stbtt_GetCodepointBitmapBoxSubpixel(font, codepoint, scale_x, scale_y,0.0f,0.0f, ix0,iy0,ix1,iy1); } /* //////////////////////////////////////////////////////////////////////////// */ /* */ /* Rasterizer */ typedef struct stbtt__hheap_chunk { struct stbtt__hheap_chunk *next; } stbtt__hheap_chunk; typedef struct stbtt__hheap { struct stbtt__hheap_chunk *head; void *first_free; int num_remaining_in_head_chunk; } stbtt__hheap; static void *stbtt__hheap_alloc(stbtt__hheap *hh, size_t size, void *userdata) { if (hh->first_free) { void *p = hh->first_free; hh->first_free = * (void **) p; return p; } else { if (hh->num_remaining_in_head_chunk == 0) { int count = (size < 32 ? 2000 : size < 128 ? 800 : 100); stbtt__hheap_chunk *c = (stbtt__hheap_chunk *) STBTT_malloc(sizeof(stbtt__hheap_chunk) + size * count, userdata); if (c == NULL) return NULL; c->next = hh->head; hh->head = c; hh->num_remaining_in_head_chunk = count; } --hh->num_remaining_in_head_chunk; return (char *) (hh->head) + sizeof(stbtt__hheap_chunk) + size * hh->num_remaining_in_head_chunk; } } static void stbtt__hheap_free(stbtt__hheap *hh, void *p) { *(void **) p = hh->first_free; hh->first_free = p; } static void stbtt__hheap_cleanup(stbtt__hheap *hh, void *userdata) { stbtt__hheap_chunk *c = hh->head; while (c) { stbtt__hheap_chunk *n = c->next; STBTT_free(c, userdata); c = n; } } typedef struct stbtt__edge { float x0,y0, x1,y1; int invert; } stbtt__edge; typedef struct stbtt__active_edge { struct stbtt__active_edge *next; #if STBTT_RASTERIZER_VERSION==1 int x,dx; float ey; int direction; #elif STBTT_RASTERIZER_VERSION==2 float fx,fdx,fdy; float direction; float sy; float ey; #else #error "Unrecognized value of STBTT_RASTERIZER_VERSION" #endif } stbtt__active_edge; #if STBTT_RASTERIZER_VERSION == 1 #define STBTT_FIXSHIFT 10 #define STBTT_FIX (1 << STBTT_FIXSHIFT) #define STBTT_FIXMASK (STBTT_FIX-1) static stbtt__active_edge *stbtt__new_active(stbtt__hheap *hh, stbtt__edge *e, int off_x, float start_point, void *userdata) { stbtt__active_edge *z = (stbtt__active_edge *) stbtt__hheap_alloc(hh, sizeof(*z), userdata); float dxdy = (e->x1 - e->x0) / (e->y1 - e->y0); STBTT_assert(z != NULL); if (!z) return z; /* round dx down to avoid overshooting */ if (dxdy < 0) z->dx = -STBTT_ifloor(STBTT_FIX * -dxdy); else z->dx = STBTT_ifloor(STBTT_FIX * dxdy); z->x = STBTT_ifloor(STBTT_FIX * e->x0 + z->dx * (start_point - e->y0)); /* use z->dx so when we offset later it's by the same amount */ z->x -= off_x * STBTT_FIX; z->ey = e->y1; z->next = 0; z->direction = e->invert ? 1 : -1; return z; } #elif STBTT_RASTERIZER_VERSION == 2 static stbtt__active_edge *stbtt__new_active(stbtt__hheap *hh, stbtt__edge *e, int off_x, float start_point, void *userdata) { stbtt__active_edge *z = (stbtt__active_edge *) stbtt__hheap_alloc(hh, sizeof(*z), userdata); float dxdy = (e->x1 - e->x0) / (e->y1 - e->y0); STBTT_assert(z != NULL); /* STBTT_assert(e->y0 <= start_point); */ if (!z) return z; z->fdx = dxdy; z->fdy = dxdy != 0.0f ? (1.0f/dxdy) : 0.0f; z->fx = e->x0 + dxdy * (start_point - e->y0); z->fx -= off_x; z->direction = e->invert ? 1.0f : -1.0f; z->sy = e->y0; z->ey = e->y1; z->next = 0; return z; } #else #error "Unrecognized value of STBTT_RASTERIZER_VERSION" #endif #if STBTT_RASTERIZER_VERSION == 1 /* note: this routine clips fills that extend off the edges... ideally this */ /* wouldn't happen, but it could happen if the truetype glyph bounding boxes */ /* are wrong, or if the user supplies a too-small bitmap */ static void stbtt__fill_active_edges(unsigned char *scanline, int len, stbtt__active_edge *e, int max_weight) { /* non-zero winding fill */ int x0=0, w=0; while (e) { if (w == 0) { /* if we're currently at zero, we need to record the edge start point */ x0 = e->x; w += e->direction; } else { int x1 = e->x; w += e->direction; /* if we went to zero, we need to draw */ if (w == 0) { int i = x0 >> STBTT_FIXSHIFT; int j = x1 >> STBTT_FIXSHIFT; if (i < len && j >= 0) { if (i == j) { /* x0,x1 are the same pixel, so compute combined coverage */ scanline[i] = scanline[i] + (stbtt_uint8) ((x1 - x0) * max_weight >> STBTT_FIXSHIFT); } else { if (i >= 0) /* add antialiasing for x0 */ scanline[i] = scanline[i] + (stbtt_uint8) (((STBTT_FIX - (x0 & STBTT_FIXMASK)) * max_weight) >> STBTT_FIXSHIFT); else i = -1; /* clip */ if (j < len) /* add antialiasing for x1 */ scanline[j] = scanline[j] + (stbtt_uint8) (((x1 & STBTT_FIXMASK) * max_weight) >> STBTT_FIXSHIFT); else j = len; /* clip */ for (++i; i < j; ++i) /* fill pixels between x0 and x1 */ scanline[i] = scanline[i] + (stbtt_uint8) max_weight; } } } } e = e->next; } } static void stbtt__rasterize_sorted_edges(stbtt__bitmap *result, stbtt__edge *e, int n, int vsubsample, int off_x, int off_y, void *userdata) { stbtt__hheap hh = { 0, 0, 0 }; stbtt__active_edge *active = NULL; int y,j=0; int max_weight = (255 / vsubsample); /* weight per vertical scanline */ int s; /* vertical subsample index */ unsigned char scanline_data[512], *scanline; if (result->w > 512) scanline = (unsigned char *) STBTT_malloc(result->w, userdata); else scanline = scanline_data; y = off_y * vsubsample; e[n].y0 = (off_y + result->h) * (float) vsubsample + 1; while (j < result->h) { STBTT_memset(scanline, 0, result->w); for (s=0; s < vsubsample; ++s) { /* find center of pixel for this scanline */ float scan_y = y + 0.5f; stbtt__active_edge **step = &active; /* update all active edges; */ /* remove all active edges that terminate before the center of this scanline */ while (*step) { stbtt__active_edge * z = *step; if (z->ey <= scan_y) { *step = z->next; /* delete from list */ STBTT_assert(z->direction); z->direction = 0; stbtt__hheap_free(&hh, z); } else { z->x += z->dx; /* advance to position for current scanline */ step = &((*step)->next); /* advance through list */ } } /* resort the list if needed */ for(;;) { int changed=0; step = &active; while (*step && (*step)->next) { if ((*step)->x > (*step)->next->x) { stbtt__active_edge *t = *step; stbtt__active_edge *q = t->next; t->next = q->next; q->next = t; *step = q; changed = 1; } step = &(*step)->next; } if (!changed) break; } /* insert all edges that start before the center of this scanline -- omit ones that also end on this scanline */ while (e->y0 <= scan_y) { if (e->y1 > scan_y) { stbtt__active_edge *z = stbtt__new_active(&hh, e, off_x, scan_y, userdata); if (z != NULL) { /* find insertion point */ if (active == NULL) active = z; else if (z->x < active->x) { /* insert at front */ z->next = active; active = z; } else { /* find thing to insert AFTER */ stbtt__active_edge *p = active; while (p->next && p->next->x < z->x) p = p->next; /* at this point, p->next->x is NOT < z->x */ z->next = p->next; p->next = z; } } } ++e; } /* now process all active edges in XOR fashion */ if (active) stbtt__fill_active_edges(scanline, result->w, active, max_weight); ++y; } STBTT_memcpy(result->pixels + j * result->stride, scanline, result->w); ++j; } stbtt__hheap_cleanup(&hh, userdata); if (scanline != scanline_data) STBTT_free(scanline, userdata); } #elif STBTT_RASTERIZER_VERSION == 2 /* the edge passed in here does not cross the vertical line at x or the vertical line at x+1 */ /* (i.e. it has already been clipped to those) */ static void stbtt__handle_clipped_edge(float *scanline, int x, stbtt__active_edge *e, float x0, float y0, float x1, float y1) { if (y0 == y1) return; STBTT_assert(y0 < y1); STBTT_assert(e->sy <= e->ey); if (y0 > e->ey) return; if (y1 < e->sy) return; if (y0 < e->sy) { x0 += (x1-x0) * (e->sy - y0) / (y1-y0); y0 = e->sy; } if (y1 > e->ey) { x1 += (x1-x0) * (e->ey - y1) / (y1-y0); y1 = e->ey; } if (x0 == x) STBTT_assert(x1 <= x+1); else if (x0 == x+1) STBTT_assert(x1 >= x); else if (x0 <= x) STBTT_assert(x1 <= x); else if (x0 >= x+1) STBTT_assert(x1 >= x+1); else STBTT_assert(x1 >= x && x1 <= x+1); if (x0 <= x && x1 <= x) scanline[x] += e->direction * (y1-y0); else if (x0 >= x+1 && x1 >= x+1) ; else { STBTT_assert(x0 >= x && x0 <= x+1 && x1 >= x && x1 <= x+1); scanline[x] += e->direction * (y1-y0) * (1-((x0-x)+(x1-x))/2); /* coverage = 1 - average x position */ } } static float stbtt__sized_trapezoid_area(float height, float top_width, float bottom_width) { STBTT_assert(top_width >= 0); STBTT_assert(bottom_width >= 0); return (top_width + bottom_width) / 2.0f * height; } static float stbtt__position_trapezoid_area(float height, float tx0, float tx1, float bx0, float bx1) { return stbtt__sized_trapezoid_area(height, tx1 - tx0, bx1 - bx0); } static float stbtt__sized_triangle_area(float height, float width) { return height * width / 2; } static void stbtt__fill_active_edges_new(float *scanline, float *scanline_fill, int len, stbtt__active_edge *e, float y_top) { float y_bottom = y_top+1; while (e) { /* brute force every pixel */ /* compute intersection points with top & bottom */ STBTT_assert(e->ey >= y_top); if (e->fdx == 0) { float x0 = e->fx; if (x0 < len) { if (x0 >= 0) { stbtt__handle_clipped_edge(scanline,(int) x0,e, x0,y_top, x0,y_bottom); stbtt__handle_clipped_edge(scanline_fill-1,(int) x0+1,e, x0,y_top, x0,y_bottom); } else { stbtt__handle_clipped_edge(scanline_fill-1,0,e, x0,y_top, x0,y_bottom); } } } else { float x0 = e->fx; float dx = e->fdx; float xb = x0 + dx; float x_top, x_bottom; float sy0,sy1; float dy = e->fdy; STBTT_assert(e->sy <= y_bottom && e->ey >= y_top); /* compute endpoints of line segment clipped to this scanline (if the */ /* line segment starts on this scanline. x0 is the intersection of the */ /* line with y_top, but that may be off the line segment. */ if (e->sy > y_top) { x_top = x0 + dx * (e->sy - y_top); sy0 = e->sy; } else { x_top = x0; sy0 = y_top; } if (e->ey < y_bottom) { x_bottom = x0 + dx * (e->ey - y_top); sy1 = e->ey; } else { x_bottom = xb; sy1 = y_bottom; } if (x_top >= 0 && x_bottom >= 0 && x_top < len && x_bottom < len) { /* from here on, we don't have to range check x values */ if ((int) x_top == (int) x_bottom) { float height; /* simple case, only spans one pixel */ int x = (int) x_top; height = (sy1 - sy0) * e->direction; STBTT_assert(x >= 0 && x < len); scanline[x] += stbtt__position_trapezoid_area(height, x_top, x+1.0f, x_bottom, x+1.0f); scanline_fill[x] += height; /* everything right of this pixel is filled */ } else { int x,x1,x2; float y_crossing, y_final, step, sign, area; /* covers 2+ pixels */ if (x_top > x_bottom) { /* flip scanline vertically; signed area is the same */ float t; sy0 = y_bottom - (sy0 - y_top); sy1 = y_bottom - (sy1 - y_top); t = sy0, sy0 = sy1, sy1 = t; t = x_bottom, x_bottom = x_top, x_top = t; dx = -dx; dy = -dy; t = x0, x0 = xb, xb = t; } STBTT_assert(dy >= 0); STBTT_assert(dx >= 0); x1 = (int) x_top; x2 = (int) x_bottom; /* compute intersection with y axis at x1+1 */ y_crossing = y_top + dy * (x1+1 - x0); /* compute intersection with y axis at x2 */ y_final = y_top + dy * (x2 - x0); /* x1 x_top x2 x_bottom */ /* y_top +------|-----+------------+------------+--------|---+------------+ */ /* | | | | | | */ /* | | | | | | */ /* sy0 | Txxxxx|............|............|............|............| */ /* y_crossing | *xxxxx.......|............|............|............| */ /* | | xxxxx..|............|............|............| */ /* | | /- xx*xxxx........|............|............| */ /* | | dy < | xxxxxx..|............|............| */ /* y_final | | \- | xx*xxx.........|............| */ /* sy1 | | | | xxxxxB...|............| */ /* | | | | | | */ /* | | | | | | */ /* y_bottom +------------+------------+------------+------------+------------+ */ /* */ /* goal is to measure the area covered by '.' in each pixel */ /* if x2 is right at the right edge of x1, y_crossing can blow up, github #1057 */ /* @TODO: maybe test against sy1 rather than y_bottom? */ if (y_crossing > y_bottom) y_crossing = y_bottom; sign = e->direction; /* area of the rectangle covered from sy0..y_crossing */ area = sign * (y_crossing-sy0); /* area of the triangle (x_top,sy0), (x1+1,sy0), (x1+1,y_crossing) */ scanline[x1] += stbtt__sized_triangle_area(area, x1+1 - x_top); /* check if final y_crossing is blown up; no test case for this */ if (y_final > y_bottom) { y_final = y_bottom; dy = (x2 - (x1+1)), dy = (y_final - y_crossing ) / (dy+!dy); /* if denom=0, y_final = y_crossing, so y_final <= y_bottom */ //< @r-lyeh dix divide by zero } /* in second pixel, area covered by line segment found in first pixel */ /* is always a rectangle 1 wide * the height of that line segment; this */ /* is exactly what the variable 'area' stores. it also gets a contribution */ /* from the line segment within it. the THIRD pixel will get the first */ /* pixel's rectangle contribution, the second pixel's rectangle contribution, */ /* and its own contribution. the 'own contribution' is the same in every pixel except */ /* the leftmost and rightmost, a trapezoid that slides down in each pixel. */ /* the second pixel's contribution to the third pixel will be the */ /* rectangle 1 wide times the height change in the second pixel, which is dy. */ step = sign * dy * 1; /* dy is dy/dx, change in y for every 1 change in x, */ /* which multiplied by 1-pixel-width is how much pixel area changes for each step in x */ /* so the area advances by 'step' every time */ for (x = x1+1; x < x2; ++x) { scanline[x] += area + step/2; /* area of trapezoid is 1*step/2 */ area += step; } STBTT_assert(STBTT_fabs(area) <= 1.01f); /* accumulated error from area += step unless we round step down */ STBTT_assert(sy1 > y_final-0.01f); /* area covered in the last pixel is the rectangle from all the pixels to the left, */ /* plus the trapezoid filled by the line segment in this pixel all the way to the right edge */ scanline[x2] += area + sign * stbtt__position_trapezoid_area(sy1-y_final, (float) x2, x2+1.0f, x_bottom, x2+1.0f); /* the rest of the line is filled based on the total height of the line segment in this pixel */ scanline_fill[x2] += sign * (sy1-sy0); } } else { /* if edge goes outside of box we're drawing, we require */ /* clipping logic. since this does not match the intended use */ /* of this library, we use a different, very slow brute */ /* force implementation */ /* note though that this does happen some of the time because */ /* x_top and x_bottom can be extrapolated at the top & bottom of */ /* the shape and actually lie outside the bounding box */ int x; for (x=0; x < len; ++x) { /* cases: */ /* */ /* there can be up to two intersections with the pixel. any intersection */ /* with left or right edges can be handled by splitting into two (or three) */ /* regions. intersections with top & bottom do not necessitate case-wise logic. */ /* */ /* the old way of doing this found the intersections with the left & right edges, */ /* then used some simple logic to produce up to three segments in sorted order */ /* from top-to-bottom. however, this had a problem: if an x edge was epsilon */ /* across the x border, then the corresponding y position might not be distinct */ /* from the other y segment, and it might ignored as an empty segment. to avoid */ /* that, we need to explicitly produce segments based on x positions. */ /* rename variables to clearly-defined pairs */ float y0 = y_top; float x1 = (float) (x); float x2 = (float) (x+1); float x3 = xb; float y3 = y_bottom; /* x = e->x + e->dx * (y-y_top) */ /* (y-y_top) = (x - e->x) / e->dx */ /* y = (x - e->x) / e->dx + y_top */ float y1 = (x - x0) / dx + y_top; float y2 = (x+1 - x0) / dx + y_top; if (x0 < x1 && x3 > x2) { /* three segments descending down-right */ stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x1,y1); stbtt__handle_clipped_edge(scanline,x,e, x1,y1, x2,y2); stbtt__handle_clipped_edge(scanline,x,e, x2,y2, x3,y3); } else if (x3 < x1 && x0 > x2) { /* three segments descending down-left */ stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x2,y2); stbtt__handle_clipped_edge(scanline,x,e, x2,y2, x1,y1); stbtt__handle_clipped_edge(scanline,x,e, x1,y1, x3,y3); } else if (x0 < x1 && x3 > x1) { /* two segments across x, down-right */ stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x1,y1); stbtt__handle_clipped_edge(scanline,x,e, x1,y1, x3,y3); } else if (x3 < x1 && x0 > x1) { /* two segments across x, down-left */ stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x1,y1); stbtt__handle_clipped_edge(scanline,x,e, x1,y1, x3,y3); } else if (x0 < x2 && x3 > x2) { /* two segments across x+1, down-right */ stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x2,y2); stbtt__handle_clipped_edge(scanline,x,e, x2,y2, x3,y3); } else if (x3 < x2 && x0 > x2) { /* two segments across x+1, down-left */ stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x2,y2); stbtt__handle_clipped_edge(scanline,x,e, x2,y2, x3,y3); } else { /* one segment */ stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x3,y3); } } } } e = e->next; } } /* directly AA rasterize edges w/o supersampling */ static void stbtt__rasterize_sorted_edges(stbtt__bitmap *result, stbtt__edge *e, int n, int vsubsample, int off_x, int off_y, void *userdata) { stbtt__hheap hh = { 0, 0, 0 }; stbtt__active_edge *active = NULL; int y,j=0, i; float scanline_data[129], *scanline, *scanline2; STBTT__NOTUSED(vsubsample); if (result->w > 64) scanline = (float *) STBTT_malloc((result->w*2+1) * sizeof(float), userdata); else scanline = scanline_data; scanline2 = scanline + result->w; y = off_y; e[n].y0 = (float) (off_y + result->h) + 1; while (j < result->h) { /* find center of pixel for this scanline */ float scan_y_top = y + 0.0f; float scan_y_bottom = y + 1.0f; stbtt__active_edge **step = &active; STBTT_memset(scanline , 0, result->w*sizeof(scanline[0])); STBTT_memset(scanline2, 0, (result->w+1)*sizeof(scanline[0])); /* update all active edges; */ /* remove all active edges that terminate before the top of this scanline */ while (*step) { stbtt__active_edge * z = *step; if (z->ey <= scan_y_top) { *step = z->next; /* delete from list */ STBTT_assert(z->direction); z->direction = 0; stbtt__hheap_free(&hh, z); } else { step = &((*step)->next); /* advance through list */ } } /* insert all edges that start before the bottom of this scanline */ while (e->y0 <= scan_y_bottom) { if (e->y0 != e->y1) { stbtt__active_edge *z = stbtt__new_active(&hh, e, off_x, scan_y_top, userdata); if (z != NULL) { if (j == 0 && off_y != 0) { if (z->ey < scan_y_top) { /* this can happen due to subpixel positioning and some kind of fp rounding error i think */ z->ey = scan_y_top; } } STBTT_assert(z->ey >= scan_y_top); /* if we get really unlucky a tiny bit of an edge can be out of bounds */ /* insert at front */ z->next = active; active = z; } } ++e; } /* now process all active edges */ if (active) stbtt__fill_active_edges_new(scanline, scanline2+1, result->w, active, scan_y_top); { float sum = 0; for (i=0; i < result->w; ++i) { float k; int m; sum += scanline2[i]; k = scanline[i] + sum; k = (float) STBTT_fabs(k)*255 + 0.5f; m = (int) k; if (m > 255) m = 255; result->pixels[j*result->stride + i] = (unsigned char) m; } } /* advance all the edges */ step = &active; while (*step) { stbtt__active_edge *z = *step; z->fx += z->fdx; /* advance to position for current scanline */ step = &((*step)->next); /* advance through list */ } ++y; ++j; } stbtt__hheap_cleanup(&hh, userdata); if (scanline != scanline_data) STBTT_free(scanline, userdata); } #else #error "Unrecognized value of STBTT_RASTERIZER_VERSION" #endif #define STBTT__COMPARE(a,b) ((a)->y0 < (b)->y0) static void stbtt__sort_edges_ins_sort(stbtt__edge *p, int n) { int i,j; for (i=1; i < n; ++i) { stbtt__edge t = p[i], *a = &t; j = i; while (j > 0) { stbtt__edge *b = &p[j-1]; int c = STBTT__COMPARE(a,b); if (!c) break; p[j] = p[j-1]; --j; } if (i != j) p[j] = t; } } static void stbtt__sort_edges_quicksort(stbtt__edge *p, int n) { /* threshold for transitioning to insertion sort */ while (n > 12) { stbtt__edge t; int c01,c12,c,m,i,j; /* compute median of three */ m = n >> 1; c01 = STBTT__COMPARE(&p[0],&p[m]); c12 = STBTT__COMPARE(&p[m],&p[n-1]); /* if 0 >= mid >= end, or 0 < mid < end, then use mid */ if (c01 != c12) { /* otherwise, we'll need to swap something else to middle */ int z; c = STBTT__COMPARE(&p[0],&p[n-1]); /* 0>mid && midn => n; 0 0 */ /* 0n: 0>n => 0; 0 n */ z = (c == c12) ? 0 : n-1; t = p[z]; p[z] = p[m]; p[m] = t; } /* now p[m] is the median-of-three */ /* swap it to the beginning so it won't move around */ t = p[0]; p[0] = p[m]; p[m] = t; /* partition loop */ i=1; j=n-1; for(;;) { /* handling of equality is crucial here */ /* for sentinels & efficiency with duplicates */ for (;;++i) { if (!STBTT__COMPARE(&p[i], &p[0])) break; } for (;;--j) { if (!STBTT__COMPARE(&p[0], &p[j])) break; } /* make sure we haven't crossed */ if (i >= j) break; t = p[i]; p[i] = p[j]; p[j] = t; ++i; --j; } /* recurse on smaller side, iterate on larger */ if (j < (n-i)) { stbtt__sort_edges_quicksort(p,j); p = p+i; n = n-i; } else { stbtt__sort_edges_quicksort(p+i, n-i); n = j; } } } static void stbtt__sort_edges(stbtt__edge *p, int n) { stbtt__sort_edges_quicksort(p, n); stbtt__sort_edges_ins_sort(p, n); } typedef struct { float x,y; } stbtt__point; static void stbtt__rasterize(stbtt__bitmap *result, stbtt__point *pts, int *wcount, int windings, float scale_x, float scale_y, float shift_x, float shift_y, int off_x, int off_y, int invert, void *userdata) { float y_scale_inv = invert ? -scale_y : scale_y; stbtt__edge *e; int n,i,j,k,m; #if STBTT_RASTERIZER_VERSION == 1 int vsubsample = result->h < 8 ? 15 : 5; #elif STBTT_RASTERIZER_VERSION == 2 int vsubsample = 1; #else #error "Unrecognized value of STBTT_RASTERIZER_VERSION" #endif /* vsubsample should divide 255 evenly; otherwise we won't reach full opacity */ /* now we have to blow out the windings into explicit edge lists */ n = 0; for (i=0; i < windings; ++i) n += wcount[i]; e = (stbtt__edge *) STBTT_malloc(sizeof(*e) * (n+1), userdata); /* add an extra one as a sentinel */ if (e == 0) return; n = 0; m=0; for (i=0; i < windings; ++i) { stbtt__point *p = pts + m; m += wcount[i]; j = wcount[i]-1; for (k=0; k < wcount[i]; j=k++) { int a=k,b=j; /* skip the edge if horizontal */ if (p[j].y == p[k].y) continue; /* add edge from j to k to the list */ e[n].invert = 0; if (invert ? p[j].y > p[k].y : p[j].y < p[k].y) { e[n].invert = 1; a=j,b=k; } e[n].x0 = p[a].x * scale_x + shift_x; e[n].y0 = (p[a].y * y_scale_inv + shift_y) * vsubsample; e[n].x1 = p[b].x * scale_x + shift_x; e[n].y1 = (p[b].y * y_scale_inv + shift_y) * vsubsample; ++n; } } /* now sort the edges by their highest point (should snap to integer, and then by x) */ /* STBTT_sort(e, n, sizeof(e[0]), stbtt__edge_compare); */ stbtt__sort_edges(e, n); /* now, traverse the scanlines and find the intersections on each scanline, use xor winding rule */ stbtt__rasterize_sorted_edges(result, e, n, vsubsample, off_x, off_y, userdata); STBTT_free(e, userdata); } static void stbtt__add_point(stbtt__point *points, int n, float x, float y) { if (!points) return; /* during first pass, it's unallocated */ points[n].x = x; points[n].y = y; } /* tessellate until threshold p is happy... @TODO warped to compensate for non-linear stretching */ static int stbtt__tesselate_curve(stbtt__point *points, int *num_points, float x0, float y0, float x1, float y1, float x2, float y2, float objspace_flatness_squared, int n) { /* midpoint */ float mx = (x0 + 2*x1 + x2)/4; float my = (y0 + 2*y1 + y2)/4; /* versus directly drawn line */ float dx = (x0+x2)/2 - mx; float dy = (y0+y2)/2 - my; if (n > 16) /* 65536 segments on one curve better be enough! */ return 1; if (dx*dx+dy*dy > objspace_flatness_squared) { /* half-pixel error allowed... need to be smaller if AA */ stbtt__tesselate_curve(points, num_points, x0,y0, (x0+x1)/2.0f,(y0+y1)/2.0f, mx,my, objspace_flatness_squared,n+1); stbtt__tesselate_curve(points, num_points, mx,my, (x1+x2)/2.0f,(y1+y2)/2.0f, x2,y2, objspace_flatness_squared,n+1); } else { stbtt__add_point(points, *num_points,x2,y2); *num_points = *num_points+1; } return 1; } static void stbtt__tesselate_cubic(stbtt__point *points, int *num_points, float x0, float y0, float x1, float y1, float x2, float y2, float x3, float y3, float objspace_flatness_squared, int n) { /* @TODO this "flatness" calculation is just made-up nonsense that seems to work well enough */ float dx0 = x1-x0; float dy0 = y1-y0; float dx1 = x2-x1; float dy1 = y2-y1; float dx2 = x3-x2; float dy2 = y3-y2; float dx = x3-x0; float dy = y3-y0; float longlen = (float) (STBTT_sqrt(dx0*dx0+dy0*dy0)+STBTT_sqrt(dx1*dx1+dy1*dy1)+STBTT_sqrt(dx2*dx2+dy2*dy2)); float shortlen = (float) STBTT_sqrt(dx*dx+dy*dy); float flatness_squared = longlen*longlen-shortlen*shortlen; if (n > 16) /* 65536 segments on one curve better be enough! */ return; if (flatness_squared > objspace_flatness_squared) { float x01 = (x0+x1)/2; float y01 = (y0+y1)/2; float x12 = (x1+x2)/2; float y12 = (y1+y2)/2; float x23 = (x2+x3)/2; float y23 = (y2+y3)/2; float xa = (x01+x12)/2; float ya = (y01+y12)/2; float xb = (x12+x23)/2; float yb = (y12+y23)/2; float mx = (xa+xb)/2; float my = (ya+yb)/2; stbtt__tesselate_cubic(points, num_points, x0,y0, x01,y01, xa,ya, mx,my, objspace_flatness_squared,n+1); stbtt__tesselate_cubic(points, num_points, mx,my, xb,yb, x23,y23, x3,y3, objspace_flatness_squared,n+1); } else { stbtt__add_point(points, *num_points,x3,y3); *num_points = *num_points+1; } } /* returns number of contours */ static stbtt__point *stbtt_FlattenCurves(stbtt_vertex *vertices, int num_verts, float objspace_flatness, int **contour_lengths, int *num_contours, void *userdata) { stbtt__point *points=0; int num_points=0; float objspace_flatness_squared = objspace_flatness * objspace_flatness; int i,n=0,start=0, pass; /* count how many "moves" there are to get the contour count */ for (i=0; i < num_verts; ++i) if (vertices[i].type == STBTT_vmove) ++n; *num_contours = n; if (n == 0) return 0; *contour_lengths = (int *) STBTT_malloc(sizeof(**contour_lengths) * n, userdata); if (*contour_lengths == 0) { *num_contours = 0; return 0; } /* make two passes through the points so we don't need to realloc */ for (pass=0; pass < 2; ++pass) { float x=0,y=0; if (pass == 1) { points = (stbtt__point *) STBTT_malloc(num_points * sizeof(points[0]), userdata); if (points == NULL) goto error; } num_points = 0; n= -1; for (i=0; i < num_verts; ++i) { switch (vertices[i].type) { case STBTT_vmove: /* start the next contour */ if (n >= 0) (*contour_lengths)[n] = num_points - start; ++n; start = num_points; x = vertices[i].x, y = vertices[i].y; stbtt__add_point(points, num_points++, x,y); break; case STBTT_vline: x = vertices[i].x, y = vertices[i].y; stbtt__add_point(points, num_points++, x, y); break; case STBTT_vcurve: stbtt__tesselate_curve(points, &num_points, x,y, vertices[i].cx, vertices[i].cy, vertices[i].x, vertices[i].y, objspace_flatness_squared, 0); x = vertices[i].x, y = vertices[i].y; break; case STBTT_vcubic: stbtt__tesselate_cubic(points, &num_points, x,y, vertices[i].cx, vertices[i].cy, vertices[i].cx1, vertices[i].cy1, vertices[i].x, vertices[i].y, objspace_flatness_squared, 0); x = vertices[i].x, y = vertices[i].y; break; } } (*contour_lengths)[n] = num_points - start; } return points; error: STBTT_free(points, userdata); STBTT_free(*contour_lengths, userdata); *contour_lengths = 0; *num_contours = 0; return NULL; } STBTT_DEF void stbtt_Rasterize(stbtt__bitmap *result, float flatness_in_pixels, stbtt_vertex *vertices, int num_verts, float scale_x, float scale_y, float shift_x, float shift_y, int x_off, int y_off, int invert, void *userdata) { float scale = scale_x > scale_y ? scale_y : scale_x; int winding_count = 0; int *winding_lengths = NULL; stbtt__point *windings = stbtt_FlattenCurves(vertices, num_verts, flatness_in_pixels / scale, &winding_lengths, &winding_count, userdata); if (windings) { stbtt__rasterize(result, windings, winding_lengths, winding_count, scale_x, scale_y, shift_x, shift_y, x_off, y_off, invert, userdata); STBTT_free(winding_lengths, userdata); STBTT_free(windings, userdata); } } STBTT_DEF void stbtt_FreeBitmap(unsigned char *bitmap, void *userdata) { STBTT_free(bitmap, userdata); } STBTT_DEF unsigned char *stbtt_GetGlyphBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int glyph, int *width, int *height, int *xoff, int *yoff) { int ix0,iy0,ix1,iy1; stbtt__bitmap gbm; stbtt_vertex *vertices; int num_verts = stbtt_GetGlyphShape(info, glyph, &vertices); if (scale_x == 0) scale_x = scale_y; if (scale_y == 0) { if (scale_x == 0) { STBTT_free(vertices, info->userdata); return NULL; } scale_y = scale_x; } stbtt_GetGlyphBitmapBoxSubpixel(info, glyph, scale_x, scale_y, shift_x, shift_y, &ix0,&iy0,&ix1,&iy1); /* now we get the size */ gbm.w = (ix1 - ix0); gbm.h = (iy1 - iy0); gbm.pixels = NULL; /* in case we error */ if (width ) *width = gbm.w; if (height) *height = gbm.h; if (xoff ) *xoff = ix0; if (yoff ) *yoff = iy0; if (gbm.w && gbm.h) { gbm.pixels = (unsigned char *) STBTT_malloc(gbm.w * gbm.h, info->userdata); if (gbm.pixels) { gbm.stride = gbm.w; stbtt_Rasterize(&gbm, 0.35f, vertices, num_verts, scale_x, scale_y, shift_x, shift_y, ix0, iy0, 1, info->userdata); } } STBTT_free(vertices, info->userdata); return gbm.pixels; } STBTT_DEF unsigned char *stbtt_GetGlyphBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int glyph, int *width, int *height, int *xoff, int *yoff) { return stbtt_GetGlyphBitmapSubpixel(info, scale_x, scale_y, 0.0f, 0.0f, glyph, width, height, xoff, yoff); } STBTT_DEF void stbtt_MakeGlyphBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int glyph) { int ix0,iy0; stbtt_vertex *vertices; int num_verts = stbtt_GetGlyphShape(info, glyph, &vertices); stbtt__bitmap gbm; stbtt_GetGlyphBitmapBoxSubpixel(info, glyph, scale_x, scale_y, shift_x, shift_y, &ix0,&iy0,0,0); gbm.pixels = output; gbm.w = out_w; gbm.h = out_h; gbm.stride = out_stride; if (gbm.w && gbm.h) stbtt_Rasterize(&gbm, 0.35f, vertices, num_verts, scale_x, scale_y, shift_x, shift_y, ix0,iy0, 1, info->userdata); STBTT_free(vertices, info->userdata); } STBTT_DEF void stbtt_MakeGlyphBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int glyph) { stbtt_MakeGlyphBitmapSubpixel(info, output, out_w, out_h, out_stride, scale_x, scale_y, 0.0f,0.0f, glyph); } STBTT_DEF unsigned char *stbtt_GetCodepointBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint, int *width, int *height, int *xoff, int *yoff) { return stbtt_GetGlyphBitmapSubpixel(info, scale_x, scale_y,shift_x,shift_y, stbtt_FindGlyphIndex(info,codepoint), width,height,xoff,yoff); } STBTT_DEF void stbtt_MakeCodepointBitmapSubpixelPrefilter(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int oversample_x, int oversample_y, float *sub_x, float *sub_y, int codepoint) { stbtt_MakeGlyphBitmapSubpixelPrefilter(info, output, out_w, out_h, out_stride, scale_x, scale_y, shift_x, shift_y, oversample_x, oversample_y, sub_x, sub_y, stbtt_FindGlyphIndex(info,codepoint)); } STBTT_DEF void stbtt_MakeCodepointBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint) { stbtt_MakeGlyphBitmapSubpixel(info, output, out_w, out_h, out_stride, scale_x, scale_y, shift_x, shift_y, stbtt_FindGlyphIndex(info,codepoint)); } STBTT_DEF unsigned char *stbtt_GetCodepointBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int codepoint, int *width, int *height, int *xoff, int *yoff) { return stbtt_GetCodepointBitmapSubpixel(info, scale_x, scale_y, 0.0f,0.0f, codepoint, width,height,xoff,yoff); } STBTT_DEF void stbtt_MakeCodepointBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int codepoint) { stbtt_MakeCodepointBitmapSubpixel(info, output, out_w, out_h, out_stride, scale_x, scale_y, 0.0f,0.0f, codepoint); } /* //////////////////////////////////////////////////////////////////////////// */ /* */ /* bitmap baking */ /* */ /* This is SUPER-CRAPPY packing to keep source code small */ static int stbtt_BakeFontBitmap_internal(unsigned char *data, int offset, /* font location (use offset=0 for plain .ttf) */ float pixel_height, /* height of font in pixels */ unsigned char *pixels, int pw, int ph, /* bitmap to be filled in */ int first_char, int num_chars, /* characters to bake */ stbtt_bakedchar *chardata) { float scale; int x,y,bottom_y, i; stbtt_fontinfo f; f.userdata = NULL; if (!stbtt_InitFont(&f, data, offset)) return -1; STBTT_memset(pixels, 0, pw*ph); /* background of 0 around pixels */ x=y=1; bottom_y = 1; scale = stbtt_ScaleForPixelHeight(&f, pixel_height); for (i=0; i < num_chars; ++i) { int advance, lsb, x0,y0,x1,y1,gw,gh; int g = stbtt_FindGlyphIndex(&f, first_char + i); stbtt_GetGlyphHMetrics(&f, g, &advance, &lsb); stbtt_GetGlyphBitmapBox(&f, g, scale,scale, &x0,&y0,&x1,&y1); gw = x1-x0; gh = y1-y0; if (x + gw + 1 >= pw) y = bottom_y, x = 1; /* advance to next row */ if (y + gh + 1 >= ph) /* check if it fits vertically AFTER potentially moving to next row */ return -i; STBTT_assert(x+gw < pw); STBTT_assert(y+gh < ph); stbtt_MakeGlyphBitmap(&f, pixels+x+y*pw, gw,gh,pw, scale,scale, g); chardata[i].x0 = (stbtt_int16) x; chardata[i].y0 = (stbtt_int16) y; chardata[i].x1 = (stbtt_int16) (x + gw); chardata[i].y1 = (stbtt_int16) (y + gh); chardata[i].xadvance = scale * advance; chardata[i].xoff = (float) x0; chardata[i].yoff = (float) y0; x = x + gw + 1; if (y+gh+1 > bottom_y) bottom_y = y+gh+1; } return bottom_y; } STBTT_DEF void stbtt_GetBakedQuad(const stbtt_bakedchar *chardata, int pw, int ph, int char_index, float *xpos, float *ypos, stbtt_aligned_quad *q, int opengl_fillrule) { float d3d_bias = opengl_fillrule ? 0 : -0.5f; float ipw = 1.0f / pw, iph = 1.0f / ph; const stbtt_bakedchar *b = chardata + char_index; int round_x = STBTT_ifloor((*xpos + b->xoff) + 0.5f); int round_y = STBTT_ifloor((*ypos + b->yoff) + 0.5f); q->x0 = round_x + d3d_bias; q->y0 = round_y + d3d_bias; q->x1 = round_x + b->x1 - b->x0 + d3d_bias; q->y1 = round_y + b->y1 - b->y0 + d3d_bias; q->s0 = b->x0 * ipw; q->t0 = b->y0 * iph; q->s1 = b->x1 * ipw; q->t1 = b->y1 * iph; *xpos += b->xadvance; } /* //////////////////////////////////////////////////////////////////////////// */ /* */ /* rectangle packing replacement routines if you don't have stb_rect_pack.h */ /* */ #ifndef STB_RECT_PACK_VERSION typedef int stbrp_coord; /* ////////////////////////////////////////////////////////////////////////////////// */ /* // */ /* // */ /* COMPILER WARNING ?!?!? // */ /* // */ /* // */ /* if you get a compile warning due to these symbols being defined more than // */ /* once, move #include "stb_rect_pack.h" before #include "stb_truetype.h" // */ /* // */ /* ////////////////////////////////////////////////////////////////////////////////// */ typedef struct { int width,height; int x,y,bottom_y; } stbrp_context; typedef struct { unsigned char x; } stbrp_node; struct stbrp_rect { stbrp_coord x,y; int id,w,h,was_packed; }; static void stbrp_init_target(stbrp_context *con, int pw, int ph, stbrp_node *nodes, int num_nodes) { con->width = pw; con->height = ph; con->x = 0; con->y = 0; con->bottom_y = 0; STBTT__NOTUSED(nodes); STBTT__NOTUSED(num_nodes); } static void stbrp_pack_rects(stbrp_context *con, stbrp_rect *rects, int num_rects) { int i; for (i=0; i < num_rects; ++i) { if (con->x + rects[i].w > con->width) { con->x = 0; con->y = con->bottom_y; } if (con->y + rects[i].h > con->height) break; rects[i].x = con->x; rects[i].y = con->y; rects[i].was_packed = 1; con->x += rects[i].w; if (con->y + rects[i].h > con->bottom_y) con->bottom_y = con->y + rects[i].h; } for ( ; i < num_rects; ++i) rects[i].was_packed = 0; } #endif /* //////////////////////////////////////////////////////////////////////////// */ /* */ /* bitmap baking */ /* */ /* This is SUPER-AWESOME (tm Ryan Gordon) packing using stb_rect_pack.h. If */ /* stb_rect_pack.h isn't available, it uses the BakeFontBitmap strategy. */ STBTT_DEF int stbtt_PackBegin(stbtt_pack_context *spc, unsigned char *pixels, int pw, int ph, int stride_in_bytes, int padding, void *alloc_context) { stbrp_context *context = (stbrp_context *) STBTT_malloc(sizeof(*context) ,alloc_context); int num_nodes = pw - padding; stbrp_node *nodes = (stbrp_node *) STBTT_malloc(sizeof(*nodes ) * num_nodes,alloc_context); if (context == NULL || nodes == NULL) { if (context != NULL) STBTT_free(context, alloc_context); if (nodes != NULL) STBTT_free(nodes , alloc_context); return 0; } spc->user_allocator_context = alloc_context; spc->width = pw; spc->height = ph; spc->pixels = pixels; spc->pack_info = context; spc->nodes = nodes; spc->padding = padding; spc->stride_in_bytes = stride_in_bytes != 0 ? stride_in_bytes : pw; spc->h_oversample = 1; spc->v_oversample = 1; spc->skip_missing = 0; stbrp_init_target(context, pw-padding, ph-padding, nodes, num_nodes); if (pixels) STBTT_memset(pixels, 0, pw*ph); /* background of 0 around pixels */ return 1; } STBTT_DEF void stbtt_PackEnd (stbtt_pack_context *spc) { STBTT_free(spc->nodes , spc->user_allocator_context); STBTT_free(spc->pack_info, spc->user_allocator_context); } STBTT_DEF void stbtt_PackSetOversampling(stbtt_pack_context *spc, unsigned int h_oversample, unsigned int v_oversample) { STBTT_assert(h_oversample <= STBTT_MAX_OVERSAMPLE); STBTT_assert(v_oversample <= STBTT_MAX_OVERSAMPLE); if (h_oversample <= STBTT_MAX_OVERSAMPLE) spc->h_oversample = h_oversample; if (v_oversample <= STBTT_MAX_OVERSAMPLE) spc->v_oversample = v_oversample; } STBTT_DEF void stbtt_PackSetSkipMissingCodepoints(stbtt_pack_context *spc, int skip) { spc->skip_missing = skip; } #define STBTT__OVER_MASK (STBTT_MAX_OVERSAMPLE-1) static void stbtt__h_prefilter(unsigned char *pixels, int w, int h, int stride_in_bytes, unsigned int kernel_width) { unsigned char buffer[STBTT_MAX_OVERSAMPLE]; int safe_w = w - kernel_width; int j; STBTT_memset(buffer, 0, STBTT_MAX_OVERSAMPLE); /* suppress bogus warning from VS2013 -analyze */ for (j=0; j < h; ++j) { int i; unsigned int total; STBTT_memset(buffer, 0, kernel_width); total = 0; /* make kernel_width a constant in common cases so compiler can optimize out the divide */ switch (kernel_width) { case 2: for (i=0; i <= safe_w; ++i) { total += pixels[i] - buffer[i & STBTT__OVER_MASK]; buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i]; pixels[i] = (unsigned char) (total / 2); } break; case 3: for (i=0; i <= safe_w; ++i) { total += pixels[i] - buffer[i & STBTT__OVER_MASK]; buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i]; pixels[i] = (unsigned char) (total / 3); } break; case 4: for (i=0; i <= safe_w; ++i) { total += pixels[i] - buffer[i & STBTT__OVER_MASK]; buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i]; pixels[i] = (unsigned char) (total / 4); } break; case 5: for (i=0; i <= safe_w; ++i) { total += pixels[i] - buffer[i & STBTT__OVER_MASK]; buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i]; pixels[i] = (unsigned char) (total / 5); } break; default: for (i=0; i <= safe_w; ++i) { total += pixels[i] - buffer[i & STBTT__OVER_MASK]; buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i]; pixels[i] = (unsigned char) (total / kernel_width); } break; } for (; i < w; ++i) { STBTT_assert(pixels[i] == 0); total -= buffer[i & STBTT__OVER_MASK]; pixels[i] = (unsigned char) (total / kernel_width); } pixels += stride_in_bytes; } } static void stbtt__v_prefilter(unsigned char *pixels, int w, int h, int stride_in_bytes, unsigned int kernel_width) { unsigned char buffer[STBTT_MAX_OVERSAMPLE]; int safe_h = h - kernel_width; int j; STBTT_memset(buffer, 0, STBTT_MAX_OVERSAMPLE); /* suppress bogus warning from VS2013 -analyze */ for (j=0; j < w; ++j) { int i; unsigned int total; STBTT_memset(buffer, 0, kernel_width); total = 0; /* make kernel_width a constant in common cases so compiler can optimize out the divide */ switch (kernel_width) { case 2: for (i=0; i <= safe_h; ++i) { total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK]; buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes]; pixels[i*stride_in_bytes] = (unsigned char) (total / 2); } break; case 3: for (i=0; i <= safe_h; ++i) { total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK]; buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes]; pixels[i*stride_in_bytes] = (unsigned char) (total / 3); } break; case 4: for (i=0; i <= safe_h; ++i) { total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK]; buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes]; pixels[i*stride_in_bytes] = (unsigned char) (total / 4); } break; case 5: for (i=0; i <= safe_h; ++i) { total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK]; buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes]; pixels[i*stride_in_bytes] = (unsigned char) (total / 5); } break; default: for (i=0; i <= safe_h; ++i) { total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK]; buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes]; pixels[i*stride_in_bytes] = (unsigned char) (total / kernel_width); } break; } for (; i < h; ++i) { STBTT_assert(pixels[i*stride_in_bytes] == 0); total -= buffer[i & STBTT__OVER_MASK]; pixels[i*stride_in_bytes] = (unsigned char) (total / kernel_width); } pixels += 1; } } static float stbtt__oversample_shift(int oversample) { if (!oversample) return 0.0f; /* The prefilter is a box filter of width "oversample", */ /* which shifts phase by (oversample - 1)/2 pixels in */ /* oversampled space. We want to shift in the opposite */ /* direction to counter this. */ return (float)-(oversample - 1) / (2.0f * (float)oversample); } /* rects array must be big enough to accommodate all characters in the given ranges */ STBTT_DEF int stbtt_PackFontRangesGatherRects(stbtt_pack_context *spc, const stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects) { int i,j,k; int missing_glyph_added = 0; k=0; for (i=0; i < num_ranges; ++i) { float fh = ranges[i].font_size; float scale = fh > 0 ? stbtt_ScaleForPixelHeight(info, fh) : stbtt_ScaleForMappingEmToPixels(info, -fh); ranges[i].h_oversample = (unsigned char) spc->h_oversample; ranges[i].v_oversample = (unsigned char) spc->v_oversample; for (j=0; j < ranges[i].num_chars; ++j) { int x0,y0,x1,y1; int codepoint = ranges[i].array_of_unicode_codepoints == NULL ? ranges[i].first_unicode_codepoint_in_range + j : ranges[i].array_of_unicode_codepoints[j]; int glyph = stbtt_FindGlyphIndex(info, codepoint); if (glyph == 0 && (spc->skip_missing || missing_glyph_added)) { rects[k].w = rects[k].h = 0; } else { stbtt_GetGlyphBitmapBoxSubpixel(info,glyph, scale * spc->h_oversample, scale * spc->v_oversample, 0,0, &x0,&y0,&x1,&y1); rects[k].w = (stbrp_coord) (x1-x0 + spc->padding + spc->h_oversample-1); rects[k].h = (stbrp_coord) (y1-y0 + spc->padding + spc->v_oversample-1); if (glyph == 0) missing_glyph_added = 1; } ++k; } } return k; } STBTT_DEF void stbtt_MakeGlyphBitmapSubpixelPrefilter(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int prefilter_x, int prefilter_y, float *sub_x, float *sub_y, int glyph) { stbtt_MakeGlyphBitmapSubpixel(info, output, out_w - (prefilter_x - 1), out_h - (prefilter_y - 1), out_stride, scale_x, scale_y, shift_x, shift_y, glyph); if (prefilter_x > 1) stbtt__h_prefilter(output, out_w, out_h, out_stride, prefilter_x); if (prefilter_y > 1) stbtt__v_prefilter(output, out_w, out_h, out_stride, prefilter_y); *sub_x = stbtt__oversample_shift(prefilter_x); *sub_y = stbtt__oversample_shift(prefilter_y); } /* rects array must be big enough to accommodate all characters in the given ranges */ STBTT_DEF int stbtt_PackFontRangesRenderIntoRects(stbtt_pack_context *spc, const stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects) { int i,j,k, missing_glyph = -1, return_value = 1; /* save current values */ int old_h_over = spc->h_oversample; int old_v_over = spc->v_oversample; k = 0; for (i=0; i < num_ranges; ++i) { float fh = ranges[i].font_size; float scale = fh > 0 ? stbtt_ScaleForPixelHeight(info, fh) : stbtt_ScaleForMappingEmToPixels(info, -fh); float recip_h,recip_v,sub_x,sub_y; spc->h_oversample = ranges[i].h_oversample; spc->v_oversample = ranges[i].v_oversample; recip_h = 1.0f / spc->h_oversample; recip_v = 1.0f / spc->v_oversample; sub_x = stbtt__oversample_shift(spc->h_oversample); sub_y = stbtt__oversample_shift(spc->v_oversample); for (j=0; j < ranges[i].num_chars; ++j) { stbrp_rect *r = &rects[k]; if (r->was_packed && r->w != 0 && r->h != 0) { stbtt_packedchar *bc = &ranges[i].chardata_for_range[j]; int advance, lsb, x0,y0,x1,y1; int codepoint = ranges[i].array_of_unicode_codepoints == NULL ? ranges[i].first_unicode_codepoint_in_range + j : ranges[i].array_of_unicode_codepoints[j]; int glyph = stbtt_FindGlyphIndex(info, codepoint); stbrp_coord pad = (stbrp_coord) spc->padding; /* pad on left and top */ r->x += pad; r->y += pad; r->w -= pad; r->h -= pad; stbtt_GetGlyphHMetrics(info, glyph, &advance, &lsb); stbtt_GetGlyphBitmapBox(info, glyph, scale * spc->h_oversample, scale * spc->v_oversample, &x0,&y0,&x1,&y1); stbtt_MakeGlyphBitmapSubpixel(info, spc->pixels + r->x + r->y*spc->stride_in_bytes, r->w - spc->h_oversample+1, r->h - spc->v_oversample+1, spc->stride_in_bytes, scale * spc->h_oversample, scale * spc->v_oversample, 0,0, glyph); if (spc->h_oversample > 1) stbtt__h_prefilter(spc->pixels + r->x + r->y*spc->stride_in_bytes, r->w, r->h, spc->stride_in_bytes, spc->h_oversample); if (spc->v_oversample > 1) stbtt__v_prefilter(spc->pixels + r->x + r->y*spc->stride_in_bytes, r->w, r->h, spc->stride_in_bytes, spc->v_oversample); bc->x0 = (stbtt_int16) r->x; bc->y0 = (stbtt_int16) r->y; bc->x1 = (stbtt_int16) (r->x + r->w); bc->y1 = (stbtt_int16) (r->y + r->h); bc->xadvance = scale * advance; bc->xoff = (float) x0 * recip_h + sub_x; bc->yoff = (float) y0 * recip_v + sub_y; bc->xoff2 = (x0 + r->w) * recip_h + sub_x; bc->yoff2 = (y0 + r->h) * recip_v + sub_y; if (glyph == 0) missing_glyph = j; } else if (spc->skip_missing) { return_value = 0; } else if (r->was_packed && r->w == 0 && r->h == 0 && missing_glyph >= 0) { ranges[i].chardata_for_range[j] = ranges[i].chardata_for_range[missing_glyph]; } else { return_value = 0; /* if any fail, report failure */ } ++k; } } /* restore original values */ spc->h_oversample = old_h_over; spc->v_oversample = old_v_over; return return_value; } STBTT_DEF void stbtt_PackFontRangesPackRects(stbtt_pack_context *spc, stbrp_rect *rects, int num_rects) { stbrp_pack_rects((stbrp_context *) spc->pack_info, rects, num_rects); } STBTT_DEF int stbtt_PackFontRanges(stbtt_pack_context *spc, const unsigned char *fontdata, int font_index, stbtt_pack_range *ranges, int num_ranges) { stbtt_fontinfo info; int i,j,n, return_value = 1; /* stbrp_context *context = (stbrp_context *) spc->pack_info; */ stbrp_rect *rects; /* flag all characters as NOT packed */ for (i=0; i < num_ranges; ++i) for (j=0; j < ranges[i].num_chars; ++j) ranges[i].chardata_for_range[j].x0 = ranges[i].chardata_for_range[j].y0 = ranges[i].chardata_for_range[j].x1 = ranges[i].chardata_for_range[j].y1 = 0; n = 0; for (i=0; i < num_ranges; ++i) n += ranges[i].num_chars; rects = (stbrp_rect *) STBTT_malloc(sizeof(*rects) * n, spc->user_allocator_context); if (rects == NULL) return 0; info.userdata = spc->user_allocator_context; stbtt_InitFont(&info, fontdata, stbtt_GetFontOffsetForIndex(fontdata,font_index)); n = stbtt_PackFontRangesGatherRects(spc, &info, ranges, num_ranges, rects); stbtt_PackFontRangesPackRects(spc, rects, n); return_value = stbtt_PackFontRangesRenderIntoRects(spc, &info, ranges, num_ranges, rects); STBTT_free(rects, spc->user_allocator_context); return return_value; } STBTT_DEF int stbtt_PackFontRange(stbtt_pack_context *spc, const unsigned char *fontdata, int font_index, float font_size, int first_unicode_codepoint_in_range, int num_chars_in_range, stbtt_packedchar *chardata_for_range) { stbtt_pack_range range; range.first_unicode_codepoint_in_range = first_unicode_codepoint_in_range; range.array_of_unicode_codepoints = NULL; range.num_chars = num_chars_in_range; range.chardata_for_range = chardata_for_range; range.font_size = font_size; return stbtt_PackFontRanges(spc, fontdata, font_index, &range, 1); } STBTT_DEF void stbtt_GetScaledFontVMetrics(const unsigned char *fontdata, int index, float size, float *ascent, float *descent, float *lineGap) { int i_ascent, i_descent, i_lineGap; float scale; stbtt_fontinfo info; stbtt_InitFont(&info, fontdata, stbtt_GetFontOffsetForIndex(fontdata, index)); scale = size > 0 ? stbtt_ScaleForPixelHeight(&info, size) : stbtt_ScaleForMappingEmToPixels(&info, -size); stbtt_GetFontVMetrics(&info, &i_ascent, &i_descent, &i_lineGap); *ascent = (float) i_ascent * scale; *descent = (float) i_descent * scale; *lineGap = (float) i_lineGap * scale; } STBTT_DEF void stbtt_GetPackedQuad(const stbtt_packedchar *chardata, int pw, int ph, int char_index, float *xpos, float *ypos, stbtt_aligned_quad *q, int align_to_integer) { float ipw = 1.0f / pw, iph = 1.0f / ph; const stbtt_packedchar *b = chardata + char_index; if (align_to_integer) { float x = (float) STBTT_ifloor((*xpos + b->xoff) + 0.5f); float y = (float) STBTT_ifloor((*ypos + b->yoff) + 0.5f); q->x0 = x; q->y0 = y; q->x1 = x + b->xoff2 - b->xoff; q->y1 = y + b->yoff2 - b->yoff; } else { q->x0 = *xpos + b->xoff; q->y0 = *ypos + b->yoff; q->x1 = *xpos + b->xoff2; q->y1 = *ypos + b->yoff2; } q->s0 = b->x0 * ipw; q->t0 = b->y0 * iph; q->s1 = b->x1 * ipw; q->t1 = b->y1 * iph; *xpos += b->xadvance; } /* //////////////////////////////////////////////////////////////////////////// */ /* */ /* sdf computation */ /* */ #define STBTT_min(a,b) ((a) < (b) ? (a) : (b)) #define STBTT_max(a,b) ((a) < (b) ? (b) : (a)) static int stbtt__ray_intersect_bezier(float orig[2], float ray[2], float q0[2], float q1[2], float q2[2], float hits[2][2]) { float q0perp = q0[1]*ray[0] - q0[0]*ray[1]; float q1perp = q1[1]*ray[0] - q1[0]*ray[1]; float q2perp = q2[1]*ray[0] - q2[0]*ray[1]; float roperp = orig[1]*ray[0] - orig[0]*ray[1]; float a = q0perp - 2*q1perp + q2perp; float b = q1perp - q0perp; float c = q0perp - roperp; float s0 = 0., s1 = 0.; int num_s = 0; if (a != 0.0) { float discr = b*b - a*c; if (discr > 0.0) { float rcpna = -1 / a; float d = (float) STBTT_sqrt(discr); s0 = (b+d) * rcpna; s1 = (b-d) * rcpna; if (s0 >= 0.0 && s0 <= 1.0) num_s = 1; if (d > 0.0 && s1 >= 0.0 && s1 <= 1.0) { if (num_s == 0) s0 = s1; ++num_s; } } } else { /* 2*b*s + c = 0 */ /* s = -c / (2*b) */ s0 = c / (-2 * b); if (s0 >= 0.0 && s0 <= 1.0) num_s = 1; } if (num_s == 0) return 0; else { float rcp_len2 = 1 / (ray[0]*ray[0] + ray[1]*ray[1]); float rayn_x = ray[0] * rcp_len2, rayn_y = ray[1] * rcp_len2; float q0d = q0[0]*rayn_x + q0[1]*rayn_y; float q1d = q1[0]*rayn_x + q1[1]*rayn_y; float q2d = q2[0]*rayn_x + q2[1]*rayn_y; float rod = orig[0]*rayn_x + orig[1]*rayn_y; float q10d = q1d - q0d; float q20d = q2d - q0d; float q0rd = q0d - rod; hits[0][0] = q0rd + s0*(2.0f - 2.0f*s0)*q10d + s0*s0*q20d; hits[0][1] = a*s0+b; if (num_s > 1) { hits[1][0] = q0rd + s1*(2.0f - 2.0f*s1)*q10d + s1*s1*q20d; hits[1][1] = a*s1+b; return 2; } else { return 1; } } } static int equal(float *a, float *b) { return (a[0] == b[0] && a[1] == b[1]); } static int stbtt__compute_crossings_x(float x, float y, int nverts, stbtt_vertex *verts) { int i; float orig[2], ray[2] = { 1, 0 }; float y_frac; int winding = 0; /* make sure y never passes through a vertex of the shape */ y_frac = (float) STBTT_fmod(y, 1.0f); if (y_frac < 0.01f) y += 0.01f; else if (y_frac > 0.99f) y -= 0.01f; orig[0] = x; orig[1] = y; /* test a ray from (-infinity,y) to (x,y) */ for (i=0; i < nverts; ++i) { if (verts[i].type == STBTT_vline) { int x0 = (int) verts[i-1].x, y0 = (int) verts[i-1].y; int x1 = (int) verts[i ].x, y1 = (int) verts[i ].y; if (y > STBTT_min(y0,y1) && y < STBTT_max(y0,y1) && x > STBTT_min(x0,x1)) { float x_inter = (y - y0) / (y1 - y0) * (x1-x0) + x0; if (x_inter < x) winding += (y0 < y1) ? 1 : -1; } } if (verts[i].type == STBTT_vcurve) { int x0 = (int) verts[i-1].x , y0 = (int) verts[i-1].y ; int x1 = (int) verts[i ].cx, y1 = (int) verts[i ].cy; int x2 = (int) verts[i ].x , y2 = (int) verts[i ].y ; int ax = STBTT_min(x0,STBTT_min(x1,x2)), ay = STBTT_min(y0,STBTT_min(y1,y2)); int by = STBTT_max(y0,STBTT_max(y1,y2)); if (y > ay && y < by && x > ax) { float q0[2],q1[2],q2[2]; float hits[2][2]; q0[0] = (float)x0; q0[1] = (float)y0; q1[0] = (float)x1; q1[1] = (float)y1; q2[0] = (float)x2; q2[1] = (float)y2; if (equal(q0,q1) || equal(q1,q2)) { x0 = (int)verts[i-1].x; y0 = (int)verts[i-1].y; x1 = (int)verts[i ].x; y1 = (int)verts[i ].y; if (y > STBTT_min(y0,y1) && y < STBTT_max(y0,y1) && x > STBTT_min(x0,x1)) { float x_inter = (y - y0) / (y1 - y0) * (x1-x0) + x0; if (x_inter < x) winding += (y0 < y1) ? 1 : -1; } } else { int num_hits = stbtt__ray_intersect_bezier(orig, ray, q0, q1, q2, hits); if (num_hits >= 1) if (hits[0][0] < 0) winding += (hits[0][1] < 0 ? -1 : 1); if (num_hits >= 2) if (hits[1][0] < 0) winding += (hits[1][1] < 0 ? -1 : 1); } } } } return winding; } static float stbtt__cuberoot( float x ) { if (x<0) return -(float) STBTT_pow(-x,1.0f/3.0f); else return (float) STBTT_pow( x,1.0f/3.0f); } /* x^3 + a*x^2 + b*x + c = 0 */ static int stbtt__solve_cubic(float a, float b, float c, float* r) { float s = -a / 3; float p = b - a*a / 3; float q = a * (2*a*a - 9*b) / 27 + c; float p3 = p*p*p; float d = q*q + 4*p3 / 27; if (d >= 0) { float z = (float) STBTT_sqrt(d); float u = (-q + z) / 2; float v = (-q - z) / 2; u = stbtt__cuberoot(u); v = stbtt__cuberoot(v); r[0] = s + u + v; return 1; } else { float u = (float) STBTT_sqrt(-p/3); float v = (float) STBTT_acos(-STBTT_sqrt(-27/p3) * q / 2) / 3; /* p3 must be negative, since d is negative */ float m = (float) STBTT_cos(v); float n = (float) STBTT_cos(v-3.141592/2)*1.732050808f; r[0] = s + u * 2 * m; r[1] = s - u * (m + n); r[2] = s - u * (m - n); /* STBTT_assert( STBTT_fabs(((r[0]+a)*r[0]+b)*r[0]+c) < 0.05f); // these asserts may not be safe at all scales, though they're in bezier t parameter units so maybe? */ /* STBTT_assert( STBTT_fabs(((r[1]+a)*r[1]+b)*r[1]+c) < 0.05f); */ /* STBTT_assert( STBTT_fabs(((r[2]+a)*r[2]+b)*r[2]+c) < 0.05f); */ return 3; } } STBTT_DEF unsigned char * stbtt_GetGlyphSDF(const stbtt_fontinfo *info, float scale, int glyph, int padding, unsigned char onedge_value, float pixel_dist_scale, int *width, int *height, int *xoff, int *yoff) { float scale_x = scale, scale_y = scale; int ix0,iy0,ix1,iy1; int w,h; unsigned char *data; if (scale == 0) return NULL; stbtt_GetGlyphBitmapBoxSubpixel(info, glyph, scale, scale, 0.0f,0.0f, &ix0,&iy0,&ix1,&iy1); /* if empty, return NULL */ if (ix0 == ix1 || iy0 == iy1) return NULL; ix0 -= padding; iy0 -= padding; ix1 += padding; iy1 += padding; w = (ix1 - ix0); h = (iy1 - iy0); if (width ) *width = w; if (height) *height = h; if (xoff ) *xoff = ix0; if (yoff ) *yoff = iy0; /* invert for y-downwards bitmaps */ scale_y = -scale_y; { int x,y,i,j; float *precompute; stbtt_vertex *verts; int num_verts = stbtt_GetGlyphShape(info, glyph, &verts); data = (unsigned char *) STBTT_malloc(w * h, info->userdata); precompute = (float *) STBTT_malloc(num_verts * sizeof(float), info->userdata); for (i=0,j=num_verts-1; i < num_verts; j=i++) { if (verts[i].type == STBTT_vline) { float x0 = verts[i].x*scale_x, y0 = verts[i].y*scale_y; float x1 = verts[j].x*scale_x, y1 = verts[j].y*scale_y; float dist = (float) STBTT_sqrt((x1-x0)*(x1-x0) + (y1-y0)*(y1-y0)); precompute[i] = (dist == 0) ? 0.0f : 1.0f / dist; } else if (verts[i].type == STBTT_vcurve) { float x2 = verts[j].x *scale_x, y2 = verts[j].y *scale_y; float x1 = verts[i].cx*scale_x, y1 = verts[i].cy*scale_y; float x0 = verts[i].x *scale_x, y0 = verts[i].y *scale_y; float bx = x0 - 2*x1 + x2, by = y0 - 2*y1 + y2; float len2 = bx*bx + by*by; if (len2 != 0.0f) precompute[i] = 1.0f / (bx*bx + by*by); else precompute[i] = 0.0f; } else precompute[i] = 0.0f; } for (y=iy0; y < iy1; ++y) { for (x=ix0; x < ix1; ++x) { float val; float min_dist = 999999.0f; float sx = (float) x + 0.5f; float sy = (float) y + 0.5f; float x_gspace = (sx / scale_x); float y_gspace = (sy / scale_y); int winding = stbtt__compute_crossings_x(x_gspace, y_gspace, num_verts, verts); /* @OPTIMIZE: this could just be a rasterization, but needs to be line vs. non-tesselated curves so a new path */ for (i=0; i < num_verts; ++i) { float x0 = verts[i].x*scale_x, y0 = verts[i].y*scale_y; if (verts[i].type == STBTT_vline && precompute[i] != 0.0f) { float x1 = verts[i-1].x*scale_x, y1 = verts[i-1].y*scale_y; float dist,dist2 = (x0-sx)*(x0-sx) + (y0-sy)*(y0-sy); if (dist2 < min_dist*min_dist) min_dist = (float) STBTT_sqrt(dist2); /* coarse culling against bbox */ /* if (sx > STBTT_min(x0,x1)-min_dist && sx < STBTT_max(x0,x1)+min_dist && */ /* sy > STBTT_min(y0,y1)-min_dist && sy < STBTT_max(y0,y1)+min_dist) */ dist = (float) STBTT_fabs((x1-x0)*(y0-sy) - (y1-y0)*(x0-sx)) * precompute[i]; STBTT_assert(i != 0); if (dist < min_dist) { /* check position along line */ /* x' = x0 + t*(x1-x0), y' = y0 + t*(y1-y0) */ /* minimize (x'-sx)*(x'-sx)+(y'-sy)*(y'-sy) */ float dx = x1-x0, dy = y1-y0; float px = x0-sx, py = y0-sy; /* minimize (px+t*dx)^2 + (py+t*dy)^2 = px*px + 2*px*dx*t + t^2*dx*dx + py*py + 2*py*dy*t + t^2*dy*dy */ /* derivative: 2*px*dx + 2*py*dy + (2*dx*dx+2*dy*dy)*t, set to 0 and solve */ float t = -(px*dx + py*dy) / (dx*dx + dy*dy); if (t >= 0.0f && t <= 1.0f) min_dist = dist; } } else if (verts[i].type == STBTT_vcurve) { float x2 = verts[i-1].x *scale_x, y2 = verts[i-1].y *scale_y; float x1 = verts[i ].cx*scale_x, y1 = verts[i ].cy*scale_y; float box_x0 = STBTT_min(STBTT_min(x0,x1),x2); float box_y0 = STBTT_min(STBTT_min(y0,y1),y2); float box_x1 = STBTT_max(STBTT_max(x0,x1),x2); float box_y1 = STBTT_max(STBTT_max(y0,y1),y2); /* coarse culling against bbox to avoid computing cubic unnecessarily */ if (sx > box_x0-min_dist && sx < box_x1+min_dist && sy > box_y0-min_dist && sy < box_y1+min_dist) { int num=0; float ax = x1-x0, ay = y1-y0; float bx = x0 - 2*x1 + x2, by = y0 - 2*y1 + y2; float mx = x0 - sx, my = y0 - sy; float res[3] = {0.f,0.f,0.f}; float px,py,t,it,dist2; float a_inv = precompute[i]; if (a_inv == 0.0) { /* if a_inv is 0, it's 2nd degree so use quadratic formula */ float a = 3*(ax*bx + ay*by); float b = 2*(ax*ax + ay*ay) + (mx*bx+my*by); float c = mx*ax+my*ay; if (a == 0.0) { /* if a is 0, it's linear */ if (b != 0.0) { res[num++] = -c/b; } } else { float discriminant = b*b - 4*a*c; if (discriminant < 0) num = 0; else { float root = (float) STBTT_sqrt(discriminant); res[0] = (-b - root)/(2*a); res[1] = (-b + root)/(2*a); num = 2; /* don't bother distinguishing 1-solution case, as code below will still work */ } } } else { float b = 3*(ax*bx + ay*by) * a_inv; /* could precompute this as it doesn't depend on sample point */ float c = (2*(ax*ax + ay*ay) + (mx*bx+my*by)) * a_inv; float d = (mx*ax+my*ay) * a_inv; num = stbtt__solve_cubic(b, c, d, res); } dist2 = (x0-sx)*(x0-sx) + (y0-sy)*(y0-sy); if (dist2 < min_dist*min_dist) min_dist = (float) STBTT_sqrt(dist2); if (num >= 1 && res[0] >= 0.0f && res[0] <= 1.0f) { t = res[0], it = 1.0f - t; px = it*it*x0 + 2*t*it*x1 + t*t*x2; py = it*it*y0 + 2*t*it*y1 + t*t*y2; dist2 = (px-sx)*(px-sx) + (py-sy)*(py-sy); if (dist2 < min_dist * min_dist) min_dist = (float) STBTT_sqrt(dist2); } if (num >= 2 && res[1] >= 0.0f && res[1] <= 1.0f) { t = res[1], it = 1.0f - t; px = it*it*x0 + 2*t*it*x1 + t*t*x2; py = it*it*y0 + 2*t*it*y1 + t*t*y2; dist2 = (px-sx)*(px-sx) + (py-sy)*(py-sy); if (dist2 < min_dist * min_dist) min_dist = (float) STBTT_sqrt(dist2); } if (num >= 3 && res[2] >= 0.0f && res[2] <= 1.0f) { t = res[2], it = 1.0f - t; px = it*it*x0 + 2*t*it*x1 + t*t*x2; py = it*it*y0 + 2*t*it*y1 + t*t*y2; dist2 = (px-sx)*(px-sx) + (py-sy)*(py-sy); if (dist2 < min_dist * min_dist) min_dist = (float) STBTT_sqrt(dist2); } } } } if (winding == 0) min_dist = -min_dist; /* if outside the shape, value is negative */ val = onedge_value + pixel_dist_scale * min_dist; if (val < 0) val = 0; else if (val > 255) val = 255; data[(y-iy0)*w+(x-ix0)] = (unsigned char) val; } } STBTT_free(precompute, info->userdata); STBTT_free(verts, info->userdata); } return data; } STBTT_DEF unsigned char * stbtt_GetCodepointSDF(const stbtt_fontinfo *info, float scale, int codepoint, int padding, unsigned char onedge_value, float pixel_dist_scale, int *width, int *height, int *xoff, int *yoff) { return stbtt_GetGlyphSDF(info, scale, stbtt_FindGlyphIndex(info, codepoint), padding, onedge_value, pixel_dist_scale, width, height, xoff, yoff); } STBTT_DEF void stbtt_FreeSDF(unsigned char *bitmap, void *userdata) { STBTT_free(bitmap, userdata); } /* //////////////////////////////////////////////////////////////////////////// */ /* */ /* font name matching -- recommended not to use this */ /* */ /* check if a utf8 string contains a prefix which is the utf16 string; if so return length of matching utf8 string */ static stbtt_int32 stbtt__CompareUTF8toUTF16_bigendian_prefix(stbtt_uint8 *s1, stbtt_int32 len1, stbtt_uint8 *s2, stbtt_int32 len2) { stbtt_int32 i=0; /* convert utf16 to utf8 and compare the results while converting */ while (len2) { stbtt_uint16 ch = s2[0]*256 + s2[1]; if (ch < 0x80) { if (i >= len1) return -1; if (s1[i++] != ch) return -1; } else if (ch < 0x800) { if (i+1 >= len1) return -1; if (s1[i++] != 0xc0 + (ch >> 6)) return -1; if (s1[i++] != 0x80 + (ch & 0x3f)) return -1; } else if (ch >= 0xd800 && ch < 0xdc00) { stbtt_uint32 c; stbtt_uint16 ch2 = s2[2]*256 + s2[3]; if (i+3 >= len1) return -1; c = ((ch - 0xd800) << 10) + (ch2 - 0xdc00) + 0x10000; if (s1[i++] != 0xf0 + (c >> 18)) return -1; if (s1[i++] != 0x80 + ((c >> 12) & 0x3f)) return -1; if (s1[i++] != 0x80 + ((c >> 6) & 0x3f)) return -1; if (s1[i++] != 0x80 + ((c ) & 0x3f)) return -1; s2 += 2; /* plus another 2 below */ len2 -= 2; } else if (ch >= 0xdc00 && ch < 0xe000) { return -1; } else { if (i+2 >= len1) return -1; if (s1[i++] != 0xe0 + (ch >> 12)) return -1; if (s1[i++] != 0x80 + ((ch >> 6) & 0x3f)) return -1; if (s1[i++] != 0x80 + ((ch ) & 0x3f)) return -1; } s2 += 2; len2 -= 2; } return i; } static int stbtt_CompareUTF8toUTF16_bigendian_internal(char *s1, int len1, char *s2, int len2) { return len1 == stbtt__CompareUTF8toUTF16_bigendian_prefix((stbtt_uint8*) s1, len1, (stbtt_uint8*) s2, len2); } /* returns results in whatever encoding you request... but note that 2-byte encodings */ /* will be BIG-ENDIAN... use stbtt_CompareUTF8toUTF16_bigendian() to compare */ STBTT_DEF const char *stbtt_GetFontNameString(const stbtt_fontinfo *font, int *length, int platformID, int encodingID, int languageID, int nameID) { stbtt_int32 i,count,stringOffset; stbtt_uint8 *fc = font->data; stbtt_uint32 offset = font->fontstart; stbtt_uint32 nm = stbtt__find_table(fc, offset, "name"); if (!nm) return NULL; count = ttUSHORT(fc+nm+2); stringOffset = nm + ttUSHORT(fc+nm+4); for (i=0; i < count; ++i) { stbtt_uint32 loc = nm + 6 + 12 * i; if (platformID == ttUSHORT(fc+loc+0) && encodingID == ttUSHORT(fc+loc+2) && languageID == ttUSHORT(fc+loc+4) && nameID == ttUSHORT(fc+loc+6)) { *length = ttUSHORT(fc+loc+8); return (const char *) (fc+stringOffset+ttUSHORT(fc+loc+10)); } } return NULL; } static int stbtt__matchpair(stbtt_uint8 *fc, stbtt_uint32 nm, stbtt_uint8 *name, stbtt_int32 nlen, stbtt_int32 target_id, stbtt_int32 next_id) { stbtt_int32 i; stbtt_int32 count = ttUSHORT(fc+nm+2); stbtt_int32 stringOffset = nm + ttUSHORT(fc+nm+4); for (i=0; i < count; ++i) { stbtt_uint32 loc = nm + 6 + 12 * i; stbtt_int32 id = ttUSHORT(fc+loc+6); if (id == target_id) { /* find the encoding */ stbtt_int32 platform = ttUSHORT(fc+loc+0), encoding = ttUSHORT(fc+loc+2), language = ttUSHORT(fc+loc+4); /* is this a Unicode encoding? */ if (platform == 0 || (platform == 3 && encoding == 1) || (platform == 3 && encoding == 10)) { stbtt_int32 slen = ttUSHORT(fc+loc+8); stbtt_int32 off = ttUSHORT(fc+loc+10); /* check if there's a prefix match */ stbtt_int32 matchlen = stbtt__CompareUTF8toUTF16_bigendian_prefix(name, nlen, fc+stringOffset+off,slen); if (matchlen >= 0) { /* check for target_id+1 immediately following, with same encoding & language */ if (i+1 < count && ttUSHORT(fc+loc+12+6) == next_id && ttUSHORT(fc+loc+12) == platform && ttUSHORT(fc+loc+12+2) == encoding && ttUSHORT(fc+loc+12+4) == language) { slen = ttUSHORT(fc+loc+12+8); off = ttUSHORT(fc+loc+12+10); if (slen == 0) { if (matchlen == nlen) return 1; } else if (matchlen < nlen && name[matchlen] == ' ') { ++matchlen; if (stbtt_CompareUTF8toUTF16_bigendian_internal((char*) (name+matchlen), nlen-matchlen, (char*)(fc+stringOffset+off),slen)) return 1; } } else { /* if nothing immediately following */ if (matchlen == nlen) return 1; } } } /* @TODO handle other encodings */ } } return 0; } static int stbtt__matches(stbtt_uint8 *fc, stbtt_uint32 offset, stbtt_uint8 *name, stbtt_int32 flags) { stbtt_int32 nlen = (stbtt_int32) STBTT_strlen((char *) name); stbtt_uint32 nm,hd; if (!stbtt__isfont(fc+offset)) return 0; /* check italics/bold/underline flags in macStyle... */ if (flags) { hd = stbtt__find_table(fc, offset, "head"); if ((ttUSHORT(fc+hd+44) & 7) != (flags & 7)) return 0; } nm = stbtt__find_table(fc, offset, "name"); if (!nm) return 0; if (flags) { /* if we checked the macStyle flags, then just check the family and ignore the subfamily */ if (stbtt__matchpair(fc, nm, name, nlen, 16, -1)) return 1; if (stbtt__matchpair(fc, nm, name, nlen, 1, -1)) return 1; if (stbtt__matchpair(fc, nm, name, nlen, 3, -1)) return 1; } else { if (stbtt__matchpair(fc, nm, name, nlen, 16, 17)) return 1; if (stbtt__matchpair(fc, nm, name, nlen, 1, 2)) return 1; if (stbtt__matchpair(fc, nm, name, nlen, 3, -1)) return 1; } return 0; } static int stbtt_FindMatchingFont_internal(unsigned char *font_collection, char *name_utf8, stbtt_int32 flags) { stbtt_int32 i; for (i=0;;++i) { stbtt_int32 off = stbtt_GetFontOffsetForIndex(font_collection, i); if (off < 0) return off; if (stbtt__matches((stbtt_uint8 *) font_collection, off, (stbtt_uint8*) name_utf8, flags)) return off; } } #if defined(__GNUC__) || defined(__clang__) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wcast-qual" #endif STBTT_DEF int stbtt_BakeFontBitmap(const unsigned char *data, int offset, float pixel_height, unsigned char *pixels, int pw, int ph, int first_char, int num_chars, stbtt_bakedchar *chardata) { return stbtt_BakeFontBitmap_internal((unsigned char *) data, offset, pixel_height, pixels, pw, ph, first_char, num_chars, chardata); } STBTT_DEF int stbtt_GetFontOffsetForIndex(const unsigned char *data, int index) { return stbtt_GetFontOffsetForIndex_internal((unsigned char *) data, index); } STBTT_DEF int stbtt_GetNumberOfFonts(const unsigned char *data) { return stbtt_GetNumberOfFonts_internal((unsigned char *) data); } STBTT_DEF int stbtt_InitFont(stbtt_fontinfo *info, const unsigned char *data, int offset) { return stbtt_InitFont_internal(info, (unsigned char *) data, offset); } STBTT_DEF int stbtt_FindMatchingFont(const unsigned char *fontdata, const char *name, int flags) { return stbtt_FindMatchingFont_internal((unsigned char *) fontdata, (char *) name, flags); } STBTT_DEF int stbtt_CompareUTF8toUTF16_bigendian(const char *s1, int len1, const char *s2, int len2) { return stbtt_CompareUTF8toUTF16_bigendian_internal((char *) s1, len1, (char *) s2, len2); } #if defined(__GNUC__) || defined(__clang__) #pragma GCC diagnostic pop #endif #endif /* STB_TRUETYPE_IMPLEMENTATION */ /* FULL VERSION HISTORY */ /* */ /* 1.25 (2021-07-11) many fixes */ /* 1.24 (2020-02-05) fix warning */ /* 1.23 (2020-02-02) query SVG data for glyphs; query whole kerning table (but only kern not GPOS) */ /* 1.22 (2019-08-11) minimize missing-glyph duplication; fix kerning if both 'GPOS' and 'kern' are defined */ /* 1.21 (2019-02-25) fix warning */ /* 1.20 (2019-02-07) PackFontRange skips missing codepoints; GetScaleFontVMetrics() */ /* 1.19 (2018-02-11) OpenType GPOS kerning (horizontal only), STBTT_fmod */ /* 1.18 (2018-01-29) add missing function */ /* 1.17 (2017-07-23) make more arguments const; doc fix */ /* 1.16 (2017-07-12) SDF support */ /* 1.15 (2017-03-03) make more arguments const */ /* 1.14 (2017-01-16) num-fonts-in-TTC function */ /* 1.13 (2017-01-02) support OpenType fonts, certain Apple fonts */ /* 1.12 (2016-10-25) suppress warnings about casting away const with -Wcast-qual */ /* 1.11 (2016-04-02) fix unused-variable warning */ /* 1.10 (2016-04-02) allow user-defined fabs() replacement */ /* fix memory leak if fontsize=0.0 */ /* fix warning from duplicate typedef */ /* 1.09 (2016-01-16) warning fix; avoid crash on outofmem; use alloc userdata for PackFontRanges */ /* 1.08 (2015-09-13) document stbtt_Rasterize(); fixes for vertical & horizontal edges */ /* 1.07 (2015-08-01) allow PackFontRanges to accept arrays of sparse codepoints; */ /* allow PackFontRanges to pack and render in separate phases; */ /* fix stbtt_GetFontOFfsetForIndex (never worked for non-0 input?); */ /* fixed an assert() bug in the new rasterizer */ /* replace assert() with STBTT_assert() in new rasterizer */ /* 1.06 (2015-07-14) performance improvements (~35% faster on x86 and x64 on test machine) */ /* also more precise AA rasterizer, except if shapes overlap */ /* remove need for STBTT_sort */ /* 1.05 (2015-04-15) fix misplaced definitions for STBTT_STATIC */ /* 1.04 (2015-04-15) typo in example */ /* 1.03 (2015-04-12) STBTT_STATIC, fix memory leak in new packing, various fixes */ /* 1.02 (2014-12-10) fix various warnings & compile issues w/ stb_rect_pack, C++ */ /* 1.01 (2014-12-08) fix subpixel position when oversampling to exactly match */ /* non-oversampled; STBTT_POINT_SIZE for packed case only */ /* 1.00 (2014-12-06) add new PackBegin etc. API, w/ support for oversampling */ /* 0.99 (2014-09-18) fix multiple bugs with subpixel rendering (ryg) */ /* 0.9 (2014-08-07) support certain mac/iOS fonts without an MS platformID */ /* 0.8b (2014-07-07) fix a warning */ /* 0.8 (2014-05-25) fix a few more warnings */ /* 0.7 (2013-09-25) bugfix: subpixel glyph bug fixed in 0.5 had come back */ /* 0.6c (2012-07-24) improve documentation */ /* 0.6b (2012-07-20) fix a few more warnings */ /* 0.6 (2012-07-17) fix warnings; added stbtt_ScaleForMappingEmToPixels, */ /* stbtt_GetFontBoundingBox, stbtt_IsGlyphEmpty */ /* 0.5 (2011-12-09) bugfixes: */ /* subpixel glyph renderer computed wrong bounding box */ /* first vertex of shape can be off-curve (FreeSans) */ /* 0.4b (2011-12-03) fixed an error in the font baking example */ /* 0.4 (2011-12-01) kerning, subpixel rendering (tor) */ /* bugfixes for: */ /* codepoint-to-glyph conversion using table fmt=12 */ /* codepoint-to-glyph conversion using table fmt=4 */ /* stbtt_GetBakedQuad with non-square texture (Zer) */ /* updated Hello World! sample to use kerning and subpixel */ /* fixed some warnings */ /* 0.3 (2009-06-24) cmap fmt=12, compound shapes (MM) */ /* userdata, malloc-from-userdata, non-zero fill (stb) */ /* 0.2 (2009-03-11) Fix unsigned/signed char warnings */ /* 0.1 (2009-03-09) First public release */ /* */ /* ------------------------------------------------------------------------------ This software is available under 2 licenses -- choose whichever you prefer. ------------------------------------------------------------------------------ ALTERNATIVE A - MIT License Copyright (c) 2017 Sean Barrett 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. ------------------------------------------------------------------------------ ALTERNATIVE B - Public Domain (www.unlicense.org) This is free and unencumbered software released into the public domain. Anyone is free to copy, modify, publish, use, compile, sell, or distribute this software, either in source code form or as a compiled binary, for any purpose, commercial or non-commercial, and by any means. In jurisdictions that recognize copyright laws, the author or authors of this software dedicate any and all copyright interest in the software to the public domain. We make this dedication for the benefit of the public at large and to the detriment of our heirs and successors. We intend this dedication to be an overt act of relinquishment in perpetuity of all present and future rights to this software under copyright law. 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 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. ------------------------------------------------------------------------------ */ #ifdef NK_INCLUDE_FONT_BAKING /* ------------------------------------------------------------- * * RECT PACK * * --------------------------------------------------------------*/ /* * ============================================================== * * TRUETYPE * * =============================================================== */ #define STBTT_MAX_OVERSAMPLE 8 /* ------------------------------------------------------------- * * FONT BAKING * * --------------------------------------------------------------*/ struct nk_font_bake_data { struct stbtt_fontinfo info; struct stbrp_rect *rects; stbtt_pack_range *ranges; nk_rune range_count; }; struct nk_font_baker { struct nk_allocator alloc; struct stbtt_pack_context spc; struct nk_font_bake_data *build; stbtt_packedchar *packed_chars; struct stbrp_rect *rects; stbtt_pack_range *ranges; }; NK_GLOBAL const nk_size nk_rect_align = NK_ALIGNOF(struct stbrp_rect); NK_GLOBAL const nk_size nk_range_align = NK_ALIGNOF(stbtt_pack_range); NK_GLOBAL const nk_size nk_char_align = NK_ALIGNOF(stbtt_packedchar); NK_GLOBAL const nk_size nk_build_align = NK_ALIGNOF(struct nk_font_bake_data); NK_GLOBAL const nk_size nk_baker_align = NK_ALIGNOF(struct nk_font_baker); NK_INTERN int nk_range_count(const nk_rune *range) { const nk_rune *iter = range; NK_ASSERT(range); if (!range) return 0; while (*(iter++) != 0); return (iter == range) ? 0 : (int)((iter - range)/2); } NK_INTERN int nk_range_glyph_count(const nk_rune *range, int count) { int i = 0; int total_glyphs = 0; for (i = 0; i < count; ++i) { int diff; nk_rune f = range[(i*2)+0]; nk_rune t = range[(i*2)+1]; NK_ASSERT(t >= f); diff = (int)((t - f) + 1); total_glyphs += diff; } return total_glyphs; } NK_API const nk_rune* nk_font_default_glyph_ranges(void) { NK_STORAGE const nk_rune ranges[] = {0x0020, 0x00FF, 0}; return ranges; } NK_API const nk_rune* nk_font_chinese_glyph_ranges(void) { NK_STORAGE const nk_rune ranges[] = { 0x0020, 0x00FF, 0x3000, 0x30FF, 0x31F0, 0x31FF, 0xFF00, 0xFFEF, 0x4e00, 0x9FAF, 0 }; return ranges; } NK_API const nk_rune* nk_font_cyrillic_glyph_ranges(void) { NK_STORAGE const nk_rune ranges[] = { 0x0020, 0x00FF, 0x0400, 0x052F, 0x2DE0, 0x2DFF, 0xA640, 0xA69F, 0 }; return ranges; } NK_API const nk_rune* nk_font_korean_glyph_ranges(void) { NK_STORAGE const nk_rune ranges[] = { 0x0020, 0x00FF, 0x3131, 0x3163, 0xAC00, 0xD79D, 0 }; return ranges; } NK_INTERN void nk_font_baker_memory(nk_size *temp, int *glyph_count, struct nk_font_config *config_list, int count) { int range_count = 0; int total_range_count = 0; struct nk_font_config *iter, *i; NK_ASSERT(config_list); NK_ASSERT(glyph_count); if (!config_list) { *temp = 0; *glyph_count = 0; return; } *glyph_count = 0; for (iter = config_list; iter; iter = iter->next) { i = iter; do {if (!i->range) iter->range = nk_font_default_glyph_ranges(); range_count = nk_range_count(i->range); total_range_count += range_count; *glyph_count += nk_range_glyph_count(i->range, range_count); } while ((i = i->n) != iter); } *temp = (nk_size)*glyph_count * sizeof(struct stbrp_rect); *temp += (nk_size)total_range_count * sizeof(stbtt_pack_range); *temp += (nk_size)*glyph_count * sizeof(stbtt_packedchar); *temp += (nk_size)count * sizeof(struct nk_font_bake_data); *temp += sizeof(struct nk_font_baker); *temp += nk_rect_align + nk_range_align + nk_char_align; *temp += nk_build_align + nk_baker_align; } NK_INTERN struct nk_font_baker* nk_font_baker(void *memory, int glyph_count, int count, struct nk_allocator *alloc) { struct nk_font_baker *baker; if (!memory) return 0; /* setup baker inside a memory block */ baker = (struct nk_font_baker*)NK_ALIGN_PTR(memory, nk_baker_align); baker->build = (struct nk_font_bake_data*)NK_ALIGN_PTR((baker + 1), nk_build_align); baker->packed_chars = (stbtt_packedchar*)NK_ALIGN_PTR((baker->build + count), nk_char_align); baker->rects = (struct stbrp_rect*)NK_ALIGN_PTR((baker->packed_chars + glyph_count), nk_rect_align); baker->ranges = (stbtt_pack_range*)NK_ALIGN_PTR((baker->rects + glyph_count), nk_range_align); baker->alloc = *alloc; return baker; } NK_INTERN int nk_font_bake_pack(struct nk_font_baker *baker, nk_size *image_memory, int *width, int *height, struct nk_recti *custom, const struct nk_font_config *config_list, int count, struct nk_allocator *alloc) { NK_STORAGE const nk_size max_height = 1024 * 32; const struct nk_font_config *config_iter, *it; int total_glyph_count = 0; int total_range_count = 0; int range_count = 0; int i = 0; NK_ASSERT(image_memory); NK_ASSERT(width); NK_ASSERT(height); NK_ASSERT(config_list); NK_ASSERT(count); NK_ASSERT(alloc); if (!image_memory || !width || !height || !config_list || !count) return nk_false; for (config_iter = config_list; config_iter; config_iter = config_iter->next) { it = config_iter; do {range_count = nk_range_count(it->range); total_range_count += range_count; total_glyph_count += nk_range_glyph_count(it->range, range_count); } while ((it = it->n) != config_iter); } /* setup font baker from temporary memory */ for (config_iter = config_list; config_iter; config_iter = config_iter->next) { it = config_iter; do { struct stbtt_fontinfo *font_info = &baker->build[i++].info; font_info->userdata = alloc; if (!stbtt_InitFont(font_info, (const unsigned char*)it->ttf_blob, 0)) return nk_false; } while ((it = it->n) != config_iter); } *height = 0; *width = (total_glyph_count > 1000) ? 1024 : 512; stbtt_PackBegin(&baker->spc, 0, (int)*width, (int)max_height, 0, 1, alloc); { int input_i = 0; int range_n = 0; int rect_n = 0; int char_n = 0; if (custom) { /* pack custom user data first so it will be in the upper left corner*/ struct stbrp_rect custom_space; nk_zero(&custom_space, sizeof(custom_space)); custom_space.w = (stbrp_coord)(custom->w); custom_space.h = (stbrp_coord)(custom->h); stbtt_PackSetOversampling(&baker->spc, 1, 1); stbrp_pack_rects((struct stbrp_context*)baker->spc.pack_info, &custom_space, 1); *height = NK_MAX(*height, (int)(custom_space.y + custom_space.h)); custom->x = (short)custom_space.x; custom->y = (short)custom_space.y; custom->w = (short)custom_space.w; custom->h = (short)custom_space.h; } /* first font pass: pack all glyphs */ for (input_i = 0, config_iter = config_list; input_i < count && config_iter; config_iter = config_iter->next) { it = config_iter; do {int n = 0; int glyph_count; const nk_rune *in_range; const struct nk_font_config *cfg = it; struct nk_font_bake_data *tmp = &baker->build[input_i++]; /* count glyphs + ranges in current font */ glyph_count = 0; range_count = 0; for (in_range = cfg->range; in_range[0] && in_range[1]; in_range += 2) { glyph_count += (int)(in_range[1] - in_range[0]) + 1; range_count++; } /* setup ranges */ tmp->ranges = baker->ranges + range_n; tmp->range_count = (nk_rune)range_count; range_n += range_count; for (i = 0; i < range_count; ++i) { in_range = &cfg->range[i * 2]; tmp->ranges[i].font_size = cfg->size; tmp->ranges[i].first_unicode_codepoint_in_range = (int)in_range[0]; tmp->ranges[i].num_chars = (int)(in_range[1]- in_range[0]) + 1; tmp->ranges[i].chardata_for_range = baker->packed_chars + char_n; char_n += tmp->ranges[i].num_chars; } /* pack */ tmp->rects = baker->rects + rect_n; rect_n += glyph_count; stbtt_PackSetOversampling(&baker->spc, cfg->oversample_h, cfg->oversample_v); n = stbtt_PackFontRangesGatherRects(&baker->spc, &tmp->info, tmp->ranges, (int)tmp->range_count, tmp->rects); stbrp_pack_rects((struct stbrp_context*)baker->spc.pack_info, tmp->rects, (int)n); /* texture height */ for (i = 0; i < n; ++i) { if (tmp->rects[i].was_packed) *height = NK_MAX(*height, tmp->rects[i].y + tmp->rects[i].h); } } while ((it = it->n) != config_iter); } NK_ASSERT(rect_n == total_glyph_count); NK_ASSERT(char_n == total_glyph_count); NK_ASSERT(range_n == total_range_count); } *height = (int)nk_round_up_pow2((nk_uint)*height); *image_memory = (nk_size)(*width) * (nk_size)(*height); return nk_true; } NK_INTERN void nk_font_bake(struct nk_font_baker *baker, void *image_memory, int width, int height, struct nk_font_glyph *glyphs, int glyphs_count, const struct nk_font_config *config_list, int font_count) { int input_i = 0; nk_rune glyph_n = 0; const struct nk_font_config *config_iter; const struct nk_font_config *it; NK_ASSERT(image_memory); NK_ASSERT(width); NK_ASSERT(height); NK_ASSERT(config_list); NK_ASSERT(baker); NK_ASSERT(font_count); NK_ASSERT(glyphs_count); if (!image_memory || !width || !height || !config_list || !font_count || !glyphs || !glyphs_count) return; /* second font pass: render glyphs */ nk_zero(image_memory, (nk_size)((nk_size)width * (nk_size)height)); baker->spc.pixels = (unsigned char*)image_memory; baker->spc.height = (int)height; for (input_i = 0, config_iter = config_list; input_i < font_count && config_iter; config_iter = config_iter->next) { it = config_iter; do {const struct nk_font_config *cfg = it; struct nk_font_bake_data *tmp = &baker->build[input_i++]; stbtt_PackSetOversampling(&baker->spc, cfg->oversample_h, cfg->oversample_v); stbtt_PackFontRangesRenderIntoRects(&baker->spc, &tmp->info, tmp->ranges, (int)tmp->range_count, tmp->rects); } while ((it = it->n) != config_iter); } stbtt_PackEnd(&baker->spc); /* third pass: setup font and glyphs */ for (input_i = 0, config_iter = config_list; input_i < font_count && config_iter; config_iter = config_iter->next) { it = config_iter; do {nk_size i = 0; int char_idx = 0; nk_rune glyph_count = 0; const struct nk_font_config *cfg = it; struct nk_font_bake_data *tmp = &baker->build[input_i++]; struct nk_baked_font *dst_font = cfg->font; float font_scale = stbtt_ScaleForPixelHeight(&tmp->info, cfg->size); int unscaled_ascent, unscaled_descent, unscaled_line_gap; stbtt_GetFontVMetrics(&tmp->info, &unscaled_ascent, &unscaled_descent, &unscaled_line_gap); /* fill baked font */ if (!cfg->merge_mode) { dst_font->ranges = cfg->range; dst_font->height = cfg->size; dst_font->ascent = ((float)unscaled_ascent * font_scale); dst_font->descent = ((float)unscaled_descent * font_scale); dst_font->glyph_offset = glyph_n; /* Need to zero this, or it will carry over from a previous bake, and cause a segfault when accessing glyphs[]. */ dst_font->glyph_count = 0; } /* fill own baked font glyph array */ for (i = 0; i < tmp->range_count; ++i) { stbtt_pack_range *range = &tmp->ranges[i]; for (char_idx = 0; char_idx < range->num_chars; char_idx++) { nk_rune codepoint = 0; float dummy_x = 0, dummy_y = 0; stbtt_aligned_quad q; struct nk_font_glyph *glyph; /* query glyph bounds from stb_truetype */ const stbtt_packedchar *pc = &range->chardata_for_range[char_idx]; if (!pc->x0 && !pc->x1 && !pc->y0 && !pc->y1) continue; codepoint = (nk_rune)(range->first_unicode_codepoint_in_range + char_idx); stbtt_GetPackedQuad(range->chardata_for_range, (int)width, (int)height, char_idx, &dummy_x, &dummy_y, &q, 0); /* fill own glyph type with data */ glyph = &glyphs[dst_font->glyph_offset + dst_font->glyph_count + (unsigned int)glyph_count]; glyph->codepoint = codepoint; glyph->x0 = q.x0; glyph->y0 = q.y0; glyph->x1 = q.x1; glyph->y1 = q.y1; glyph->y0 += (dst_font->ascent + 0.5f); glyph->y1 += (dst_font->ascent + 0.5f); glyph->w = glyph->x1 - glyph->x0 + 0.5f; glyph->h = glyph->y1 - glyph->y0; if (cfg->coord_type == NK_COORD_PIXEL) { glyph->u0 = q.s0 * (float)width; glyph->v0 = q.t0 * (float)height; glyph->u1 = q.s1 * (float)width; glyph->v1 = q.t1 * (float)height; } else { glyph->u0 = q.s0; glyph->v0 = q.t0; glyph->u1 = q.s1; glyph->v1 = q.t1; } glyph->xadvance = (pc->xadvance + cfg->spacing.x); glyph->yoffset = cfg->spacing.y; //< @r-lyeh if (cfg->pixel_snap) glyph->xadvance = (float)(int)(glyph->xadvance + 0.5f); glyph_count++; } } dst_font->glyph_count += glyph_count; glyph_n += glyph_count; } while ((it = it->n) != config_iter); } } NK_INTERN void nk_font_bake_custom_data(void *img_memory, int img_width, int img_height, struct nk_recti img_dst, const char *texture_data_mask, int tex_width, int tex_height, char white, char black) { nk_byte *pixels; int y = 0; int x = 0; int n = 0; NK_ASSERT(img_memory); NK_ASSERT(img_width); NK_ASSERT(img_height); NK_ASSERT(texture_data_mask); NK_UNUSED(tex_height); if (!img_memory || !img_width || !img_height || !texture_data_mask) return; pixels = (nk_byte*)img_memory; for (y = 0, n = 0; y < tex_height; ++y) { for (x = 0; x < tex_width; ++x, ++n) { const int off0 = ((img_dst.x + x) + (img_dst.y + y) * img_width); const int off1 = off0 + 1 + tex_width; pixels[off0] = (texture_data_mask[n] == white) ? 0xFF : 0x00; pixels[off1] = (texture_data_mask[n] == black) ? 0xFF : 0x00; } } } NK_INTERN void nk_font_bake_convert(void *out_memory, int img_width, int img_height, const void *in_memory) { int n = 0; nk_rune *dst; const nk_byte *src; NK_ASSERT(out_memory); NK_ASSERT(in_memory); NK_ASSERT(img_width); NK_ASSERT(img_height); if (!out_memory || !in_memory || !img_height || !img_width) return; dst = (nk_rune*)out_memory; src = (const nk_byte*)in_memory; for (n = (int)(img_width * img_height); n > 0; n--) *dst++ = ((nk_rune)(*src++) << 24) | 0x00FFFFFF; } /* ------------------------------------------------------------- * * FONT * * --------------------------------------------------------------*/ NK_INTERN float nk_font_text_width(nk_handle handle, float height, const char *text, int len) { nk_rune unicode; int text_len = 0; float text_width = 0; int glyph_len = 0; float scale = 0; struct nk_font *font = (struct nk_font*)handle.ptr; NK_ASSERT(font); NK_ASSERT(font->glyphs); if (!font || !text || !len) return 0; scale = height/font->info.height; glyph_len = text_len = nk_utf_decode(text, &unicode, (int)len); if (!glyph_len) return 0; while (text_len <= (int)len && glyph_len) { const struct nk_font_glyph *g; if (unicode == NK_UTF_INVALID) break; /* query currently drawn glyph information */ g = nk_font_find_glyph(font, unicode); text_width += g->xadvance * scale; /* offset next glyph */ glyph_len = nk_utf_decode(text + text_len, &unicode, (int)len - text_len); text_len += glyph_len; } return text_width; } #ifdef NK_INCLUDE_VERTEX_BUFFER_OUTPUT NK_INTERN void nk_font_query_font_glyph(nk_handle handle, float height, struct nk_user_font_glyph *glyph, nk_rune codepoint, nk_rune next_codepoint) { float scale; const struct nk_font_glyph *g; struct nk_font *font; NK_ASSERT(glyph); NK_UNUSED(next_codepoint); font = (struct nk_font*)handle.ptr; NK_ASSERT(font); NK_ASSERT(font->glyphs); if (!font || !glyph) return; scale = height/font->info.height; g = nk_font_find_glyph(font, codepoint); glyph->width = (g->x1 - g->x0) * scale; glyph->height = (g->y1 - g->y0) * scale; glyph->offset = nk_vec2(g->x0 * scale, g->y0 * scale); glyph->xadvance = (g->xadvance * scale); glyph->uv[0] = nk_vec2(g->u0, g->v0); glyph->uv[1] = nk_vec2(g->u1, g->v1); #if 1 //< @r-lyeh: hack to align MaterialIcons glyph->offset.y += g->yoffset; #endif } #endif NK_API const struct nk_font_glyph* nk_font_find_glyph(struct nk_font *font, nk_rune unicode) { int i = 0; int count; int total_glyphs = 0; const struct nk_font_glyph *glyph = 0; const struct nk_font_config *iter = 0; NK_ASSERT(font); NK_ASSERT(font->glyphs); NK_ASSERT(font->info.ranges); if (!font || !font->glyphs) return 0; glyph = font->fallback; iter = font->config; do {count = nk_range_count(iter->range); for (i = 0; i < count; ++i) { nk_rune f = iter->range[(i*2)+0]; nk_rune t = iter->range[(i*2)+1]; int diff = (int)((t - f) + 1); if (unicode >= f && unicode <= t) return &font->glyphs[((nk_rune)total_glyphs + (unicode - f))]; total_glyphs += diff; } } while ((iter = iter->n) != font->config); return glyph; } NK_INTERN void nk_font_init(struct nk_font *font, float pixel_height, nk_rune fallback_codepoint, struct nk_font_glyph *glyphs, const struct nk_baked_font *baked_font, nk_handle atlas) { struct nk_baked_font baked; NK_ASSERT(font); NK_ASSERT(glyphs); NK_ASSERT(baked_font); if (!font || !glyphs || !baked_font) return; baked = *baked_font; font->fallback = 0; font->info = baked; font->scale = (float)pixel_height / (float)font->info.height; font->glyphs = &glyphs[baked_font->glyph_offset]; font->texture = atlas; font->fallback_codepoint = fallback_codepoint; font->fallback = nk_font_find_glyph(font, fallback_codepoint); font->handle.height = font->info.height * font->scale; font->handle.width = nk_font_text_width; font->handle.userdata.ptr = font; #ifdef NK_INCLUDE_VERTEX_BUFFER_OUTPUT font->handle.query = nk_font_query_font_glyph; font->handle.texture = font->texture; #endif } /* --------------------------------------------------------------------------- * * DEFAULT FONT * * ProggyClean.ttf * Copyright (c) 2004, 2005 Tristan Grimmer * MIT license (see License.txt in http://www.upperbounds.net/download/ProggyClean.ttf.zip) * Download and more information at http://upperbounds.net *-----------------------------------------------------------------------------*/ #ifdef __clang__ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Woverlength-strings" #elif defined(__GNUC__) || defined(__GNUG__) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Woverlength-strings" #endif #ifdef NK_INCLUDE_DEFAULT_FONT NK_GLOBAL const char nk_proggy_clean_ttf_compressed_data_base85[11980+1] = "7])#######hV0qs'/###[),##/l:$#Q6>##5[n42>c-TH`->>#/e>11NNV=Bv(*:.F?uu#(gRU.o0XGH`$vhLG1hxt9?W`#,5LsCp#-i>.r$<$6pD>Lb';9Crc6tgXmKVeU2cD4Eo3R/" "2*>]b(MC;$jPfY.;h^`IWM9Qo#t'X#(v#Y9w0#1D$CIf;W'#pWUPXOuxXuU(H9M(1=Ke$$'5F%)]0^#0X@U.a$FBjVQTSDgEKnIS7EM9>ZY9w0#L;>>#Mx&4Mvt//L[MkA#W@lK.N'[0#7RL_&#w+F%HtG9M#XL`N&.,GM4Pg;--VsM.M0rJfLH2eTM`*oJMHRC`N" "kfimM2J,W-jXS:)r0wK#@Fge$U>`w'N7G#$#fB#$E^$#:9:hk+eOe--6x)F7*E%?76%^GMHePW-Z5l'&GiF#$956:rS?dA#fiK:)Yr+`�j@'DbG&#^$PG.Ll+DNa&VZ>1i%h1S9u5o@YaaW$e+bROPOpxTO7Stwi1::iB1q)C_=dV26J;2,]7op$]uQr@_V7$q^%lQwtuHY]=DX,n3L#0PHDO4f9>dC@O>HBuKPpP*E,N+b3L#lpR/MrTEH.IAQk.a>D[.e;mc." "x]Ip.PH^'/aqUO/$1WxLoW0[iLAw=4h(9.`G" "CRUxHPeR`5Mjol(dUWxZa(>STrPkrJiWx`5U7F#.g*jrohGg`cg:lSTvEY/EV_7H4Q9[Z%cnv;JQYZ5q.l7Zeas:HOIZOB?Ggv:[7MI2k).'2($5FNP&EQ(,)" "U]W]+fh18.vsai00);D3@4ku5P?DP8aJt+;qUM]=+b'8@;mViBKx0DE[-auGl8:PJ&Dj+M6OC]O^((##]`0i)drT;-7X`=-H3[igUnPG-NZlo.#k@h#=Ork$m>a>$-?Tm$UV(?#P6YY#" "'/###xe7q.73rI3*pP/$1>s9)W,JrM7SN]'/4C#v$U`0#V.[0>xQsH$fEmPMgY2u7Kh(G%siIfLSoS+MK2eTM$=5,M8p`A.;_R%#u[K#$x4AG8.kK/HSB==-'Ie/QTtG?-.*^N-4B/ZM" "_3YlQC7(p7q)&](`6_c)$/*JL(L-^(]$wIM`dPtOdGA,U3:w2M-0+WomX2u7lqM2iEumMTcsF?-aT=Z-97UEnXglEn1K-bnEO`gu" "Ft(c%=;Am_Qs@jLooI&NX;]0#j4#F14;gl8-GQpgwhrq8'=l_f-b49'UOqkLu7-##oDY2L(te+Mch&gLYtJ,MEtJfLh'x'M=$CS-ZZ%P]8bZ>#S?YY#%Q&q'3^Fw&?D)UDNrocM3A76/" "/oL?#h7gl85[qW/NDOk%16ij;+:1a'iNIdb-ou8.P*w,v5#EI$TWS>Pot-R*H'-SEpA:g)f+O$%%`kA#G=8RMmG1&O`>to8bC]T&$,n.LoO>29sp3dt-52U%VM#q7'DHpg+#Z9%H[Ket`e;)f#Km8&+DC$I46>#Kr]]u-[=99tts1.qb#q72g1WJO81q+eN'03'eM>&1XxY-caEnO" "j%2n8)),?ILR5^.Ibn<-X-Mq7[a82Lq:F&#ce+S9wsCK*x`569E8ew'He]h:sI[2LM$[guka3ZRd6:t%IG:;$%YiJ:Nq=?eAw;/:nnDq0(CYcMpG)qLN4$##&J-XTt,%OVU4)S1+R-#dg0/Nn?Ku1^0f$B*P:Rowwm-`0PKjYDDM'3]d39VZHEl4,.j']Pk-M.h^&:0FACm$maq-&sgw0t7/6(^xtk%" "LuH88Fj-ekm>GA#_>568x6(OFRl-IZp`&b,_P'$MhLbxfc$mj`,O;&%W2m`Zh:/)Uetw:aJ%]K9h:TcF]u_-Sj9,VK3M.*'&0D[Ca]J9gp8,kAW]" "%(?A%R$f<->Zts'^kn=-^@c4%-pY6qI%J%1IGxfLU9CP8cbPlXv);C=b),<2mOvP8up,UVf3839acAWAW-W?#ao/^#%KYo8fRULNd2.>%m]UK:n%r$'sw]J;5pAoO_#2mO3n,'=H5(et" "Hg*`+RLgv>=4U8guD$I%D:W>-r5V*%j*W:Kvej.Lp$'?;++O'>()jLR-^u68PHm8ZFWe+ej8h:9r6L*0//c&iH&R8pRbA#Kjm%upV1g:" "a_#Ur7FuA#(tRh#.Y5K+@?3<-8m0$PEn;J:rh6?I6uG<-`wMU'ircp0LaE_OtlMb&1#6T.#FDKu#1Lw%u%+GM+X'e?YLfjM[VO0MbuFp7;>Q&#WIo)0@F%q7c#4XAXN-U&VBpqB>0ie&jhZ[?iLR@@_AvA-iQC(=ksRZRVp7`.=+NpBC%rh&3]R:8XDmE5^V8O(x<-+k?'(^](H.aREZSi,#1:[IXaZFOm<-ui#qUq2$##Ri;u75OK#(RtaW-K-F`S+cF]uN`-KMQ%rP/Xri.LRcB##=YL3BgM/3M" "D?@f&1'BW-)Ju#bmmWCMkk&#TR`C,5d>g)F;t,4:@_l8G/5h4vUd%&%950:VXD'QdWoY-F$BtUwmfe$YqL'8(PWX(" "P?^@Po3$##`MSs?DWBZ/S>+4%>fX,VWv/w'KD`LP5IbH;rTV>n3cEK8U#bX]l-/V+^lj3;vlMb&[5YQ8#pekX9JP3XUC72L,,?+Ni&co7ApnO*5NK,((W-i:$,kp'UDAO(G0Sq7MVjJs" "bIu)'Z,*[>br5fX^:FPAWr-m2KgLQ_nN6'8uTGT5g)uLv:873UpTLgH+#FgpH'_o1780Ph8KmxQJ8#H72L4@768@Tm&Q" "h4CB/5OvmA&,Q&QbUoi$a_%3M01H)4x7I^&KQVgtFnV+;[Pc>[m4k//,]1?#`VY[Jr*3&&slRfLiVZJ:]?=K3Sw=[$=uRB?3xk48@aege0jT6'N#(q%.O=?2S]u*(m<-" "V8J'(1)G][68hW$5'q[GC&5j`TE?m'esFGNRM)j,ffZ?-qx8;->g4t*:CIP/[Qap7/9'#(1sao7w-.qNUdkJ)tCF&#B^;xGvn2r9FEPFFFcL@.iFNkTve$m%#QvQS8U@)2Z+3K:AKM5i" "sZ88+dKQ)W6>J%CL`.d*(B`-n8D9oK-XV1q['-5k'cAZ69e;D_?$ZPP&s^+7])$*$#@QYi9,5P r+$%CE=68>K8r0=dSC%%(@p7" ".m7jilQ02'0-VWAgTlGW'b)Tq7VT9q^*^$$.:&N@@" "$&)WHtPm*5_rO0&e%K&#-30j(E4#'Zb.o/(Tpm$>K'f@[PvFl,hfINTNU6u'0pao7%XUp9]5.>%h`8_=VYbxuel.NTSsJfLacFu3B'lQSu/m6-Oqem8T+oE--$0a/k]uj9EwsG>%veR*" "hv^BFpQj:K'#SJ,sB-'#](j.Lg92rTw-*n%@/;39rrJF,l#qV%OrtBeC6/,;qB3ebNW[?,Hqj2L.1NP&GjUR=1D8QaS3Up&@*9wP?+lo7b?@%'k4`p0Z$22%K3+iCZj?XJN4Nm&+YF]u" "@-W$U%VEQ/,,>>#)D#%8cY#YZ?=,`Wdxu/ae&#" "w6)R89tI#6@s'(6Bf7a&?S=^ZI_kS&ai`&=tE72L_D,;^R)7[$so8lKN%5/$(vdfq7+ebA#" "u1p]ovUKW&Y%q]'>$1@-[xfn$7ZTp7mM,G,Ko7a&Gu%G[RMxJs[0MM%wci.LFDK)(%:_i2B5CsR8&9Z&#=mPEnm0f`<&c)QL5uJ#%u%lJj+D-r;BoFDoS97h5g)E#o:&S4weDF,9^Hoe`h*L+_a*NrLW-1pG_&2UdB8" "6e%B/:=>)N4xeW.*wft-;$'58-ESqr#U`'6AQ]m&6/`Z>#S?YY#Vc;r7U2&326d=w&H####?TZ`*4?&.MK?LP8Vxg>$[QXc%QJv92.(Db*B)gb*BM9dM*hJMAo*c&#" "b0v=Pjer]$gG&JXDf->'StvU7505l9$AFvgYRI^&<^b68?j#q9QX4SM'RO#&sL1IM.rJfLUAj221]d##DW=m83u5;'bYx,*Sl0hL(W;;$doB&O/TQ:(Z^xBdLjLV#*8U_72Lh+2Q8Cj0i:6hp&$C/:p(HK>T8Y[gHQ4`4)'$Ab(Nof%V'8hL&#SfD07&6D@M.*J:;$-rv29'M]8qMv-tLp,'886iaC=Hb*YJoKJ,(j%K=H`K.v9HggqBIiZu'QvBT.#=)0ukruV&.)3=(^1`o*Pj4<-#MJ+gLq9-##@HuZPN0]u:h7.T..G:;$/Usj(T7`Q8tT72LnYl<-qx8;-HV7Q-&Xdx%1a,hC=0u+HlsV>nuIQL-5" "_>@kXQtMacfD.m-VAb8;IReM3$wf0''hra*so568'Ip&vRs849'MRYSp%:t:h5qSgwpEr$B>Q,;s(C#$)`svQuF$##-D,##,g68@2[T;.XSdN9Qe)rpt._K-#5wF)sP'##p#C0c%-Gb%" "hd+<-j'Ai*x&&HMkT]C'OSl##5RG[JXaHN;d'uA#x._U;.`PU@(Z3dt4r152@:v,'R.Sj'w#0<-;kPI)FfJ&#AYJ&#//)>-k=m=*XnK$>=)72L]0I%>.G690a:$##<,);?;72#?x9+d;" "^V'9;jY@;)br#q^YQpx:X#Te$Z^'=-=bGhLf:D6&bNwZ9-ZD#n^9HhLMr5G;']d&6'wYmTFmLq9wI>P(9mI[>kC-ekLC/R&CH+s'B;K-M6$EB%is00:" "+A4[7xks.LrNk0&E)wILYF@2L'0Nb$+pv<(2.768/FrY&h$^3i&@+G%JT'<-,v`3;_)I9M^AE]CN?Cl2AZg+%4iTpT3$U4O]GKx'm9)b@p7YsvK3w^YR-" "CdQ*:Ir<($u&)#(&?L9Rg3H)4fiEp^iI9O8KnTj,]H?D*r7'M;PwZ9K0E^k&-cpI;.p/6_vwoFMV<->#%Xi.LxVnrU(4&8/P+:hLSKj$#U%]49t'I:rgMi'FL@a:0Y-uA[39',(vbma*" "hU%<-SRF`Tt:542R_VV$p@[p8DV[A,?1839FWdFTi1O*H&#(AL8[_P%.M>v^-))qOT*F5Cq0`Ye%+$B6i:7@0IXSsDiWP,##P`%/L-" "S(qw%sf/@%#B6;/U7K]uZbi^Oc^2n%t<)'mEVE''n`WnJra$^TKvX5B>;_aSEK',(hwa0:i4G?.Bci.(X[?b*($,=-n<.Q%`(X=?+@Am*Js0&=3bh8K]mL69=Lb,OcZV/);TTm8VI;?%OtJ<(b4mq7M6:u?KRdFl*:xP?Yb.5)%w_I?7uk5JC+FS(m#i'k.'a0i)9<7b'fs'59hq$*5Uhv##pi^8+hIEBF`nvo`;'l0.^S1<-wUK2/Coh58KKhLj" "M=SO*rfO`+qC`W-On.=AJ56>>i2@2LH6A:&5q`?9I3@@'04&p2/LVa*T-4<-i3;M9UvZd+N7>b*eIwg:CC)c<>nO&#$(>.Z-I&J(Q0Hd5Q%7Co-b`-cP)hI;*_F]u`Rb[.j8_Q/<&>uu+VsH$sM9TA%?)(vmJ80),P7E>)tjD%2L=-t#fK[%`v=Q8WlA2);Sa" ">gXm8YB`1d@K#n]76-a$U,mF%Ul:#/'xoFM9QX-$.QN'>" "[%$Z$uF6pA6Ki2O5:8w*vP1<-1`[G,)-m#>0`P&#eb#.3i)rtB61(o'$?X3B2Qft^ae_5tKL9MUe9b*sLEQ95C&`=G?@Mj=wh*'3E>=-<)Gt*Iw)'QG:`@I" "wOf7&]1i'S01B+Ev/Nac#9S;=;YQpg_6U`*kVY39xK,[/6Aj7:'1Bm-_1EYfa1+o&o4hp7KN_Q(OlIo@S%;jVdn0'1h19w,WQhLI)3S#f$2(eb,jr*b;3Vw]*7NH%$c4Vs,eD9>XW8?N]o+(*pgC%/72LV-uW%iewS8W6m2rtCpo'RS1R84=@paTKt)>=%&1[)*vp'u+x,VrwN;&]kuO9JDbg=pO$J*.jVe;u'm0dr9l,<*wMK*Oe=g8lV_KEBFkO'oU]^=[-792#ok,)" "i]lR8qQ2oA8wcRCZ^7w/Njh;?.stX?Q1>S1q4Bn$)K1<-rGdO'$Wr.Lc.CG)$/*JL4tNR/,SVO3,aUw'DJN:)Ss;wGn9A32ijw%FL+Z0Fn.U9;reSq)bmI32U==5ALuG&#Vf1398/pVo" "1*c-(aY168o<`JsSbk-,1N;$>0:OUas(3:8Z972LSfF8eb=c-;>SPw7.6hn3m`9^Xkn(r.qS[0;T%&Qc=+STRxX'q1BNk3&*eu2;&8q$&x>Q#Q7^Tf+6<(d%ZVmj2bDi%.3L2n+4W'$P" "iDDG)g,r%+?,$@?uou5tSe2aN_AQU*'IAO" "URQ##V^Fv-XFbGM7Fl(N<3DhLGF%q.1rC$#:T__&Pi68%0xi_&[qFJ(77j_&JWoF.V735&T,[R*:xFR*K5>>#`bW-?4Ne_&6Ne_&6Ne_&n`kr-#GJcM6X;uM6X;uM(.a..^2TkL%oR(#" ";u.T%fAr%4tJ8&><1=GHZ_+m9/#H1F^R#SC#*N=BA9(D?v[UiFY>>^8p,KKF.W]L29uLkLlu/+4T" "w$)F./^n3+rlo+DB;5sIYGNk+i1t-69Jg--0pao7Sm#K)pdHW&;LuDNH@H>#/X-TI(;P>#,Gc>#0Su>#4`1?#8lC?#xL$#B.`$#F:r$#JF.%#NR@%#R_R%#Vke%#Zww%#_-4^Rh%Sflr-k'MS.o?.5/sWel/wpEM0%3'/1)K^f1-d>G21&v(35>V`39V7A4=onx4" "A1OY5EI0;6Ibgr6M$HS7Q<)58C5w,;WoA*#[%T*#`1g*#d=#+#hI5+#lUG+#pbY+#tnl+#x$),#&1;,#*=M,#.I`,#2Ur,#6b.-#;w[H#iQtA#m^0B#qjBB#uvTB##-hB#'9$C#+E6C#" "/QHC#3^ZC#7jmC#;v)D#?,)4kMYD4lVu`4m`:&5niUA5@(A5BA1]PBB:xlBCC=2CDLXMCEUtiCf&0g2'tN?PGT4CPGT4CPGT4CPGT4CPGT4CPGT4CPGT4CP" "GT4CPGT4CPGT4CPGT4CPGT4CPGT4CP-qekC`.9kEg^+F$kwViFJTB&5KTB&5KTB&5KTB&5KTB&5KTB&5KTB&5KTB&5KTB&5KTB&5KTB&5KTB&5KTB&5KTB&5KTB&5o,^<-28ZI'O?;xp" "O?;xpO?;xpO?;xpO?;xpO?;xpO?;xpO?;xpO?;xpO?;xpO?;xpO?;xpO?;xpO?;xp;7q-#lLYI:xvD=#"; #endif /* NK_INCLUDE_DEFAULT_FONT */ #define NK_CURSOR_DATA_W 90 #define NK_CURSOR_DATA_H 27 NK_GLOBAL const char nk_custom_cursor_data[NK_CURSOR_DATA_W * NK_CURSOR_DATA_H + 1] = { "..- -XXXXXXX- X - X -XXXXXXX - XXXXXXX" "..- -X.....X- X.X - X.X -X.....X - X.....X" "--- -XXX.XXX- X...X - X...X -X....X - X....X" "X - X.X - X.....X - X.....X -X...X - X...X" "XX - X.X -X.......X- X.......X -X..X.X - X.X..X" "X.X - X.X -XXXX.XXXX- XXXX.XXXX -X.X X.X - X.X X.X" "X..X - X.X - X.X - X.X -XX X.X - X.X XX" "X...X - X.X - X.X - XX X.X XX - X.X - X.X " "X....X - X.X - X.X - X.X X.X X.X - X.X - X.X " "X.....X - X.X - X.X - X..X X.X X..X - X.X - X.X " "X......X - X.X - X.X - X...XXXXXX.XXXXXX...X - X.X XX-XX X.X " "X.......X - X.X - X.X -X.....................X- X.X X.X-X.X X.X " "X........X - X.X - X.X - X...XXXXXX.XXXXXX...X - X.X..X-X..X.X " "X.........X -XXX.XXX- X.X - X..X X.X X..X - X...X-X...X " "X..........X-X.....X- X.X - X.X X.X X.X - X....X-X....X " "X......XXXXX-XXXXXXX- X.X - XX X.X XX - X.....X-X.....X " "X...X..X --------- X.X - X.X - XXXXXXX-XXXXXXX " "X..X X..X - -XXXX.XXXX- XXXX.XXXX ------------------------------------" "X.X X..X - -X.......X- X.......X - XX XX - " "XX X..X - - X.....X - X.....X - X.X X.X - " " X..X - X...X - X...X - X..X X..X - " " XX - X.X - X.X - X...XXXXXXXXXXXXX...X - " "------------ - X - X -X.....................X- " " ----------------------------------- X...XXXXXXXXXXXXX...X - " " - X..X X..X - " " - X.X X.X - " " - XX XX - " }; #ifdef __clang__ #pragma clang diagnostic pop #elif defined(__GNUC__) || defined(__GNUG__) #pragma GCC diagnostic pop #endif NK_GLOBAL unsigned char *nk__barrier; NK_GLOBAL unsigned char *nk__barrier2; NK_GLOBAL unsigned char *nk__barrier3; NK_GLOBAL unsigned char *nk__barrier4; NK_GLOBAL unsigned char *nk__dout; NK_INTERN unsigned int nk_decompress_length(unsigned char *input) { return (unsigned int)((input[8] << 24) + (input[9] << 16) + (input[10] << 8) + input[11]); } NK_INTERN void nk__match(unsigned char *data, unsigned int length) { /* INVERSE of memmove... write each byte before copying the next...*/ NK_ASSERT (nk__dout + length <= nk__barrier); if (nk__dout + length > nk__barrier) { nk__dout += length; return; } if (data < nk__barrier4) { nk__dout = nk__barrier+1; return; } while (length--) *nk__dout++ = *data++; } NK_INTERN void nk__lit(unsigned char *data, unsigned int length) { NK_ASSERT (nk__dout + length <= nk__barrier); if (nk__dout + length > nk__barrier) { nk__dout += length; return; } if (data < nk__barrier2) { nk__dout = nk__barrier+1; return; } NK_MEMCPY(nk__dout, data, length); nk__dout += length; } NK_INTERN unsigned char* nk_decompress_token(unsigned char *i) { #define nk__in2(x) ((i[x] << 8) + i[(x)+1]) #define nk__in3(x) ((i[x] << 16) + nk__in2((x)+1)) #define nk__in4(x) ((i[x] << 24) + nk__in3((x)+1)) if (*i >= 0x20) { /* use fewer if's for cases that expand small */ if (*i >= 0x80) nk__match(nk__dout-i[1]-1, (unsigned int)i[0] - 0x80 + 1), i += 2; else if (*i >= 0x40) nk__match(nk__dout-(nk__in2(0) - 0x4000 + 1), (unsigned int)i[2]+1), i += 3; else /* *i >= 0x20 */ nk__lit(i+1, (unsigned int)i[0] - 0x20 + 1), i += 1 + (i[0] - 0x20 + 1); } else { /* more ifs for cases that expand large, since overhead is amortized */ if (*i >= 0x18) nk__match(nk__dout-(unsigned int)(nk__in3(0) - 0x180000 + 1), (unsigned int)i[3]+1), i += 4; else if (*i >= 0x10) nk__match(nk__dout-(unsigned int)(nk__in3(0) - 0x100000 + 1), (unsigned int)nk__in2(3)+1), i += 5; else if (*i >= 0x08) nk__lit(i+2, (unsigned int)nk__in2(0) - 0x0800 + 1), i += 2 + (nk__in2(0) - 0x0800 + 1); else if (*i == 0x07) nk__lit(i+3, (unsigned int)nk__in2(1) + 1), i += 3 + (nk__in2(1) + 1); else if (*i == 0x06) nk__match(nk__dout-(unsigned int)(nk__in3(1)+1), i[4]+1u), i += 5; else if (*i == 0x04) nk__match(nk__dout-(unsigned int)(nk__in3(1)+1), (unsigned int)nk__in2(4)+1u), i += 6; } return i; } NK_INTERN unsigned int nk_adler32(unsigned int adler32, unsigned char *buffer, unsigned int buflen) { const unsigned long ADLER_MOD = 65521; unsigned long s1 = adler32 & 0xffff, s2 = adler32 >> 16; unsigned long blocklen, i; blocklen = buflen % 5552; while (buflen) { for (i=0; i + 7 < blocklen; i += 8) { s1 += buffer[0]; s2 += s1; s1 += buffer[1]; s2 += s1; s1 += buffer[2]; s2 += s1; s1 += buffer[3]; s2 += s1; s1 += buffer[4]; s2 += s1; s1 += buffer[5]; s2 += s1; s1 += buffer[6]; s2 += s1; s1 += buffer[7]; s2 += s1; buffer += 8; } for (; i < blocklen; ++i) { s1 += *buffer++; s2 += s1; } s1 %= ADLER_MOD; s2 %= ADLER_MOD; buflen -= (unsigned int)blocklen; blocklen = 5552; } return (unsigned int)(s2 << 16) + (unsigned int)s1; } NK_INTERN unsigned int nk_decompress(unsigned char *output, unsigned char *i, unsigned int length) { unsigned int olen; if (nk__in4(0) != 0x57bC0000) return 0; if (nk__in4(4) != 0) return 0; /* error! stream is > 4GB */ olen = nk_decompress_length(i); nk__barrier2 = i; nk__barrier3 = i+length; nk__barrier = output + olen; nk__barrier4 = output; i += 16; nk__dout = output; for (;;) { unsigned char *old_i = i; i = nk_decompress_token(i); if (i == old_i) { if (*i == 0x05 && i[1] == 0xfa) { NK_ASSERT(nk__dout == output + olen); if (nk__dout != output + olen) return 0; if (nk_adler32(1, output, olen) != (unsigned int) nk__in4(2)) return 0; return olen; } else { NK_ASSERT(0); /* NOTREACHED */ return 0; } } NK_ASSERT(nk__dout <= output + olen); if (nk__dout > output + olen) return 0; } } NK_INTERN unsigned int nk_decode_85_byte(char c) { return (unsigned int)((c >= '\\') ? c-36 : c-35); } NK_INTERN void nk_decode_85(unsigned char* dst, const unsigned char* src) { while (*src) { unsigned int tmp = nk_decode_85_byte((char)src[0]) + 85 * (nk_decode_85_byte((char)src[1]) + 85 * (nk_decode_85_byte((char)src[2]) + 85 * (nk_decode_85_byte((char)src[3]) + 85 * nk_decode_85_byte((char)src[4])))); /* we can't assume little-endianess. */ dst[0] = (unsigned char)((tmp >> 0) & 0xFF); dst[1] = (unsigned char)((tmp >> 8) & 0xFF); dst[2] = (unsigned char)((tmp >> 16) & 0xFF); dst[3] = (unsigned char)((tmp >> 24) & 0xFF); src += 5; dst += 4; } } /* ------------------------------------------------------------- * * FONT ATLAS * * --------------------------------------------------------------*/ NK_API struct nk_font_config nk_font_config(float pixel_height) { struct nk_font_config cfg; nk_zero_struct(cfg); cfg.ttf_blob = 0; cfg.ttf_size = 0; cfg.ttf_data_owned_by_atlas = 0; cfg.size = pixel_height*1; //< @r-lyeh : *1 cfg.oversample_h = 3; //< @r-lyeh : 3 cfg.oversample_v = 2; //< @r-lyeh : 1 cfg.pixel_snap = 1; //< @r-lyeh : 0. according to docs, if true set oversample to (1,1) cfg.coord_type = NK_COORD_UV; cfg.spacing = nk_vec2(0,0); cfg.range = nk_font_default_glyph_ranges(); cfg.merge_mode = 0; cfg.fallback_glyph = '?'; cfg.font = 0; cfg.n = 0; return cfg; } #ifdef NK_INCLUDE_DEFAULT_ALLOCATOR NK_API void nk_font_atlas_init_default(struct nk_font_atlas *atlas) { NK_ASSERT(atlas); if (!atlas) return; nk_zero_struct(*atlas); atlas->temporary.userdata.ptr = 0; atlas->temporary.alloc = nk_malloc; atlas->temporary.free = nk_mfree; atlas->permanent.userdata.ptr = 0; atlas->permanent.alloc = nk_malloc; atlas->permanent.free = nk_mfree; } #endif NK_API void nk_font_atlas_init(struct nk_font_atlas *atlas, struct nk_allocator *alloc) { NK_ASSERT(atlas); NK_ASSERT(alloc); if (!atlas || !alloc) return; nk_zero_struct(*atlas); atlas->permanent = *alloc; atlas->temporary = *alloc; } NK_API void nk_font_atlas_init_custom(struct nk_font_atlas *atlas, struct nk_allocator *permanent, struct nk_allocator *temporary) { NK_ASSERT(atlas); NK_ASSERT(permanent); NK_ASSERT(temporary); if (!atlas || !permanent || !temporary) return; nk_zero_struct(*atlas); atlas->permanent = *permanent; atlas->temporary = *temporary; } NK_API void nk_font_atlas_begin(struct nk_font_atlas *atlas) { NK_ASSERT(atlas); NK_ASSERT(atlas->temporary.alloc && atlas->temporary.free); NK_ASSERT(atlas->permanent.alloc && atlas->permanent.free); if (!atlas || !atlas->permanent.alloc || !atlas->permanent.free || !atlas->temporary.alloc || !atlas->temporary.free) return; if (atlas->glyphs) { atlas->permanent.free(atlas->permanent.userdata, atlas->glyphs); atlas->glyphs = 0; } if (atlas->pixel) { atlas->permanent.free(atlas->permanent.userdata, atlas->pixel); atlas->pixel = 0; } } NK_API struct nk_font* nk_font_atlas_add(struct nk_font_atlas *atlas, const struct nk_font_config *config) { struct nk_font *font = 0; struct nk_font_config *cfg; NK_ASSERT(atlas); NK_ASSERT(atlas->permanent.alloc); NK_ASSERT(atlas->permanent.free); NK_ASSERT(atlas->temporary.alloc); NK_ASSERT(atlas->temporary.free); NK_ASSERT(config); NK_ASSERT(config->ttf_blob); NK_ASSERT(config->ttf_size); NK_ASSERT(config->size > 0.0f); if (!atlas || !config || !config->ttf_blob || !config->ttf_size || config->size <= 0.0f|| !atlas->permanent.alloc || !atlas->permanent.free || !atlas->temporary.alloc || !atlas->temporary.free) return 0; /* allocate font config */ cfg = (struct nk_font_config*) atlas->permanent.alloc(atlas->permanent.userdata,0, sizeof(struct nk_font_config)); NK_MEMCPY(cfg, config, sizeof(*config)); cfg->n = cfg; cfg->p = cfg; if (!config->merge_mode) { /* insert font config into list */ if (!atlas->config) { atlas->config = cfg; cfg->next = 0; } else { struct nk_font_config *i = atlas->config; while (i->next) i = i->next; i->next = cfg; cfg->next = 0; } /* allocate new font */ font = (struct nk_font*) atlas->permanent.alloc(atlas->permanent.userdata,0, sizeof(struct nk_font)); NK_ASSERT(font); nk_zero(font, sizeof(*font)); if (!font) return 0; font->config = cfg; /* insert font into list */ if (!atlas->fonts) { atlas->fonts = font; font->next = 0; } else { struct nk_font *i = atlas->fonts; while (i->next) i = i->next; i->next = font; font->next = 0; } cfg->font = &font->info; } else { /* extend previously added font */ struct nk_font *f = 0; struct nk_font_config *c = 0; NK_ASSERT(atlas->font_num); f = atlas->fonts; c = f->config; cfg->font = &f->info; cfg->n = c; cfg->p = c->p; c->p->n = cfg; c->p = cfg; } /* create own copy of .TTF font blob */ if (!config->ttf_data_owned_by_atlas) { cfg->ttf_blob = atlas->permanent.alloc(atlas->permanent.userdata,0, cfg->ttf_size); NK_ASSERT(cfg->ttf_blob); if (!cfg->ttf_blob) { atlas->font_num++; return 0; } NK_MEMCPY(cfg->ttf_blob, config->ttf_blob, cfg->ttf_size); cfg->ttf_data_owned_by_atlas = 1; } atlas->font_num++; return font; } NK_API struct nk_font* nk_font_atlas_add_from_memory(struct nk_font_atlas *atlas, void *memory, nk_size size, float height, const struct nk_font_config *config) { struct nk_font_config cfg; NK_ASSERT(memory); NK_ASSERT(size); NK_ASSERT(atlas); NK_ASSERT(atlas->temporary.alloc); NK_ASSERT(atlas->temporary.free); NK_ASSERT(atlas->permanent.alloc); NK_ASSERT(atlas->permanent.free); if (!atlas || !atlas->temporary.alloc || !atlas->temporary.free || !memory || !size || !atlas->permanent.alloc || !atlas->permanent.free) return 0; cfg = (config) ? *config: nk_font_config(height); cfg.ttf_blob = memory; cfg.ttf_size = size; cfg.size = height; cfg.ttf_data_owned_by_atlas = 0; return nk_font_atlas_add(atlas, &cfg); } #ifdef NK_INCLUDE_STANDARD_IO NK_API struct nk_font* nk_font_atlas_add_from_file(struct nk_font_atlas *atlas, const char *file_path, float height, const struct nk_font_config *config) { nk_size size; char *memory; struct nk_font_config cfg; NK_ASSERT(atlas); NK_ASSERT(atlas->temporary.alloc); NK_ASSERT(atlas->temporary.free); NK_ASSERT(atlas->permanent.alloc); NK_ASSERT(atlas->permanent.free); if (!atlas || !file_path) return 0; memory = nk_file_load(file_path, &size, &atlas->permanent); if (!memory) return 0; cfg = (config) ? *config: nk_font_config(height); cfg.ttf_blob = memory; cfg.ttf_size = size; cfg.size = height; cfg.ttf_data_owned_by_atlas = 1; return nk_font_atlas_add(atlas, &cfg); } #endif NK_API struct nk_font* nk_font_atlas_add_compressed(struct nk_font_atlas *atlas, void *compressed_data, nk_size compressed_size, float height, const struct nk_font_config *config) { unsigned int decompressed_size; void *decompressed_data; struct nk_font_config cfg; NK_ASSERT(atlas); NK_ASSERT(atlas->temporary.alloc); NK_ASSERT(atlas->temporary.free); NK_ASSERT(atlas->permanent.alloc); NK_ASSERT(atlas->permanent.free); NK_ASSERT(compressed_data); NK_ASSERT(compressed_size); if (!atlas || !compressed_data || !atlas->temporary.alloc || !atlas->temporary.free || !atlas->permanent.alloc || !atlas->permanent.free) return 0; decompressed_size = nk_decompress_length((unsigned char*)compressed_data); decompressed_data = atlas->permanent.alloc(atlas->permanent.userdata,0,decompressed_size); NK_ASSERT(decompressed_data); if (!decompressed_data) return 0; nk_decompress((unsigned char*)decompressed_data, (unsigned char*)compressed_data, (unsigned int)compressed_size); cfg = (config) ? *config: nk_font_config(height); cfg.ttf_blob = decompressed_data; cfg.ttf_size = decompressed_size; cfg.size = height; cfg.ttf_data_owned_by_atlas = 1; return nk_font_atlas_add(atlas, &cfg); } NK_API struct nk_font* nk_font_atlas_add_compressed_base85(struct nk_font_atlas *atlas, const char *data_base85, float height, const struct nk_font_config *config) { int compressed_size; void *compressed_data; struct nk_font *font; NK_ASSERT(atlas); NK_ASSERT(atlas->temporary.alloc); NK_ASSERT(atlas->temporary.free); NK_ASSERT(atlas->permanent.alloc); NK_ASSERT(atlas->permanent.free); NK_ASSERT(data_base85); if (!atlas || !data_base85 || !atlas->temporary.alloc || !atlas->temporary.free || !atlas->permanent.alloc || !atlas->permanent.free) return 0; compressed_size = (((int)nk_strlen(data_base85) + 4) / 5) * 4; compressed_data = atlas->temporary.alloc(atlas->temporary.userdata,0, (nk_size)compressed_size); NK_ASSERT(compressed_data); if (!compressed_data) return 0; nk_decode_85((unsigned char*)compressed_data, (const unsigned char*)data_base85); font = nk_font_atlas_add_compressed(atlas, compressed_data, (nk_size)compressed_size, height, config); atlas->temporary.free(atlas->temporary.userdata, compressed_data); return font; } #ifdef NK_INCLUDE_DEFAULT_FONT NK_API struct nk_font* nk_font_atlas_add_default(struct nk_font_atlas *atlas, float pixel_height, const struct nk_font_config *config) { NK_ASSERT(atlas); NK_ASSERT(atlas->temporary.alloc); NK_ASSERT(atlas->temporary.free); NK_ASSERT(atlas->permanent.alloc); NK_ASSERT(atlas->permanent.free); return nk_font_atlas_add_compressed_base85(atlas, nk_proggy_clean_ttf_compressed_data_base85, pixel_height, config); } #endif NK_API const void* nk_font_atlas_bake(struct nk_font_atlas *atlas, int *width, int *height, enum nk_font_atlas_format fmt) { int i = 0; void *tmp = 0; nk_size tmp_size, img_size; struct nk_font *font_iter; struct nk_font_baker *baker; NK_ASSERT(atlas); NK_ASSERT(atlas->temporary.alloc); NK_ASSERT(atlas->temporary.free); NK_ASSERT(atlas->permanent.alloc); NK_ASSERT(atlas->permanent.free); NK_ASSERT(width); NK_ASSERT(height); if (!atlas || !width || !height || !atlas->temporary.alloc || !atlas->temporary.free || !atlas->permanent.alloc || !atlas->permanent.free) return 0; #ifdef NK_INCLUDE_DEFAULT_FONT /* no font added so just use default font */ if (!atlas->font_num) atlas->default_font = nk_font_atlas_add_default(atlas, 13.0f, 0); #endif NK_ASSERT(atlas->font_num); if (!atlas->font_num) return 0; /* allocate temporary baker memory required for the baking process */ nk_font_baker_memory(&tmp_size, &atlas->glyph_count, atlas->config, atlas->font_num); tmp = atlas->temporary.alloc(atlas->temporary.userdata,0, tmp_size); NK_ASSERT(tmp); if (!tmp) goto failed; NK_MEMSET(tmp,0,tmp_size); /* allocate glyph memory for all fonts */ baker = nk_font_baker(tmp, atlas->glyph_count, atlas->font_num, &atlas->temporary); atlas->glyphs = (struct nk_font_glyph*)atlas->permanent.alloc( atlas->permanent.userdata,0, sizeof(struct nk_font_glyph)*(nk_size)atlas->glyph_count); NK_ASSERT(atlas->glyphs); if (!atlas->glyphs) goto failed; /* pack all glyphs into a tight fit space */ atlas->custom.w = (NK_CURSOR_DATA_W*2)+1; atlas->custom.h = NK_CURSOR_DATA_H + 1; if (!nk_font_bake_pack(baker, &img_size, width, height, &atlas->custom, atlas->config, atlas->font_num, &atlas->temporary)) goto failed; /* allocate memory for the baked image font atlas */ atlas->pixel = atlas->temporary.alloc(atlas->temporary.userdata,0, img_size); NK_ASSERT(atlas->pixel); if (!atlas->pixel) goto failed; /* bake glyphs and custom white pixel into image */ nk_font_bake(baker, atlas->pixel, *width, *height, atlas->glyphs, atlas->glyph_count, atlas->config, atlas->font_num); nk_font_bake_custom_data(atlas->pixel, *width, *height, atlas->custom, nk_custom_cursor_data, NK_CURSOR_DATA_W, NK_CURSOR_DATA_H, '.', 'X'); if (fmt == NK_FONT_ATLAS_RGBA32) { /* convert alpha8 image into rgba32 image */ void *img_rgba = atlas->temporary.alloc(atlas->temporary.userdata,0, (nk_size)(*width * *height * 4)); NK_ASSERT(img_rgba); if (!img_rgba) goto failed; nk_font_bake_convert(img_rgba, *width, *height, atlas->pixel); atlas->temporary.free(atlas->temporary.userdata, atlas->pixel); atlas->pixel = img_rgba; } atlas->tex_width = *width; atlas->tex_height = *height; /* initialize each font */ for (font_iter = atlas->fonts; font_iter; font_iter = font_iter->next) { struct nk_font *font = font_iter; struct nk_font_config *config = font->config; nk_font_init(font, config->size, config->fallback_glyph, atlas->glyphs, config->font, nk_handle_ptr(0)); } /* initialize each cursor */ {NK_STORAGE const struct nk_vec2 nk_cursor_data[NK_CURSOR_COUNT][3] = { /* Pos Size Offset */ {{ 0, 3}, {12,19}, { 0, 0}}, {{13, 0}, { 7,16}, { 4, 8}}, {{31, 0}, {23,23}, {11,11}}, {{21, 0}, { 9, 23}, { 5,11}}, {{55,18}, {23, 9}, {11, 5}}, {{73, 0}, {17,17}, { 9, 9}}, {{55, 0}, {17,17}, { 9, 9}} }; for (i = 0; i < NK_CURSOR_COUNT; ++i) { struct nk_cursor *cursor = &atlas->cursors[i]; cursor->img.w = (unsigned short)*width; cursor->img.h = (unsigned short)*height; cursor->img.region[0] = (unsigned short)(atlas->custom.x + nk_cursor_data[i][0].x); cursor->img.region[1] = (unsigned short)(atlas->custom.y + nk_cursor_data[i][0].y); cursor->img.region[2] = (unsigned short)nk_cursor_data[i][1].x; cursor->img.region[3] = (unsigned short)nk_cursor_data[i][1].y; cursor->size = nk_cursor_data[i][1]; cursor->offset = nk_cursor_data[i][2]; }} /* free temporary memory */ atlas->temporary.free(atlas->temporary.userdata, tmp); return atlas->pixel; failed: /* error so cleanup all memory */ if (tmp) atlas->temporary.free(atlas->temporary.userdata, tmp); if (atlas->glyphs) { atlas->permanent.free(atlas->permanent.userdata, atlas->glyphs); atlas->glyphs = 0; } if (atlas->pixel) { atlas->temporary.free(atlas->temporary.userdata, atlas->pixel); atlas->pixel = 0; } return 0; } NK_API void nk_font_atlas_end(struct nk_font_atlas *atlas, nk_handle texture, struct nk_draw_null_texture *null) { int i = 0; struct nk_font *font_iter; NK_ASSERT(atlas); if (!atlas) { if (!null) return; null->texture = texture; null->uv = nk_vec2(0.5f,0.5f); } if (null) { null->texture = texture; null->uv.x = (atlas->custom.x + 0.5f)/(float)atlas->tex_width; null->uv.y = (atlas->custom.y + 0.5f)/(float)atlas->tex_height; } for (font_iter = atlas->fonts; font_iter; font_iter = font_iter->next) { font_iter->texture = texture; #ifdef NK_INCLUDE_VERTEX_BUFFER_OUTPUT font_iter->handle.texture = texture; #endif } for (i = 0; i < NK_CURSOR_COUNT; ++i) atlas->cursors[i].img.handle = texture; atlas->temporary.free(atlas->temporary.userdata, atlas->pixel); atlas->pixel = 0; atlas->tex_width = 0; atlas->tex_height = 0; atlas->custom.x = 0; atlas->custom.y = 0; atlas->custom.w = 0; atlas->custom.h = 0; } NK_API void nk_font_atlas_cleanup(struct nk_font_atlas *atlas) { NK_ASSERT(atlas); NK_ASSERT(atlas->temporary.alloc); NK_ASSERT(atlas->temporary.free); NK_ASSERT(atlas->permanent.alloc); NK_ASSERT(atlas->permanent.free); if (!atlas || !atlas->permanent.alloc || !atlas->permanent.free) return; if (atlas->config) { struct nk_font_config *iter; for (iter = atlas->config; iter; iter = iter->next) { struct nk_font_config *i; for (i = iter->n; i != iter; i = i->n) { atlas->permanent.free(atlas->permanent.userdata, i->ttf_blob); i->ttf_blob = 0; } atlas->permanent.free(atlas->permanent.userdata, iter->ttf_blob); iter->ttf_blob = 0; } } } NK_API void nk_font_atlas_clear(struct nk_font_atlas *atlas) { NK_ASSERT(atlas); NK_ASSERT(atlas->temporary.alloc); NK_ASSERT(atlas->temporary.free); NK_ASSERT(atlas->permanent.alloc); NK_ASSERT(atlas->permanent.free); if (!atlas || !atlas->permanent.alloc || !atlas->permanent.free) return; if (atlas->config) { struct nk_font_config *iter, *next; for (iter = atlas->config; iter; iter = next) { struct nk_font_config *i, *n; for (i = iter->n; i != iter; i = n) { n = i->n; if (i->ttf_blob) atlas->permanent.free(atlas->permanent.userdata, i->ttf_blob); atlas->permanent.free(atlas->permanent.userdata, i); } next = iter->next; if (i->ttf_blob) atlas->permanent.free(atlas->permanent.userdata, iter->ttf_blob); atlas->permanent.free(atlas->permanent.userdata, iter); } atlas->config = 0; } if (atlas->fonts) { struct nk_font *iter, *next; for (iter = atlas->fonts; iter; iter = next) { next = iter->next; atlas->permanent.free(atlas->permanent.userdata, iter); } atlas->fonts = 0; } if (atlas->glyphs) atlas->permanent.free(atlas->permanent.userdata, atlas->glyphs); nk_zero_struct(*atlas); } #endif /* =============================================================== * * INPUT * * ===============================================================*/ NK_API void nk_input_begin(struct nk_context *ctx) { int i; struct nk_input *in; NK_ASSERT(ctx); if (!ctx) return; in = &ctx->input; for (i = 0; i < NK_BUTTON_MAX; ++i) in->mouse.buttons[i].clicked = 0; in->keyboard.text_len = 0; in->mouse.scroll_delta = nk_vec2(0,0); in->mouse.prev.x = in->mouse.pos.x; in->mouse.prev.y = in->mouse.pos.y; in->mouse.delta.x = 0; in->mouse.delta.y = 0; for (i = 0; i < NK_KEY_MAX; i++) in->keyboard.keys[i].clicked = 0; } NK_API void nk_input_end(struct nk_context *ctx) { struct nk_input *in; NK_ASSERT(ctx); if (!ctx) return; in = &ctx->input; if (in->mouse.grab) in->mouse.grab = 0; if (in->mouse.ungrab) { in->mouse.grabbed = 0; in->mouse.ungrab = 0; in->mouse.grab = 0; } } NK_API void nk_input_motion(struct nk_context *ctx, int x, int y) { struct nk_input *in; NK_ASSERT(ctx); if (!ctx) return; in = &ctx->input; in->mouse.pos.x = (float)x; in->mouse.pos.y = (float)y; in->mouse.delta.x = in->mouse.pos.x - in->mouse.prev.x; in->mouse.delta.y = in->mouse.pos.y - in->mouse.prev.y; } NK_API void nk_input_key(struct nk_context *ctx, enum nk_keys key, nk_bool down) { struct nk_input *in; NK_ASSERT(ctx); if (!ctx) return; in = &ctx->input; #ifdef NK_KEYSTATE_BASED_INPUT if (in->keyboard.keys[key].down != down) in->keyboard.keys[key].clicked++; #else in->keyboard.keys[key].clicked++; #endif in->keyboard.keys[key].down = down; } NK_API void nk_input_button(struct nk_context *ctx, enum nk_buttons id, int x, int y, nk_bool down) { struct nk_mouse_button *btn; struct nk_input *in; NK_ASSERT(ctx); if (!ctx) return; in = &ctx->input; if (in->mouse.buttons[id].down == down) return; btn = &in->mouse.buttons[id]; btn->clicked_pos.x = (float)x; btn->clicked_pos.y = (float)y; btn->down = down; btn->clicked++; } NK_API void nk_input_scroll(struct nk_context *ctx, struct nk_vec2 val) { NK_ASSERT(ctx); if (!ctx) return; ctx->input.mouse.scroll_delta.x += val.x; ctx->input.mouse.scroll_delta.y += val.y; } NK_API void nk_input_glyph(struct nk_context *ctx, const nk_glyph glyph) { int len = 0; nk_rune unicode; struct nk_input *in; NK_ASSERT(ctx); if (!ctx) return; in = &ctx->input; len = nk_utf_decode(glyph, &unicode, NK_UTF_SIZE); if (len && ((in->keyboard.text_len + len) < NK_INPUT_MAX)) { nk_utf_encode(unicode, &in->keyboard.text[in->keyboard.text_len], NK_INPUT_MAX - in->keyboard.text_len); in->keyboard.text_len += len; } } NK_API void nk_input_char(struct nk_context *ctx, char c) { nk_glyph glyph; NK_ASSERT(ctx); if (!ctx) return; glyph[0] = c; nk_input_glyph(ctx, glyph); } NK_API void nk_input_unicode(struct nk_context *ctx, nk_rune unicode) { nk_glyph rune; NK_ASSERT(ctx); if (!ctx) return; nk_utf_encode(unicode, rune, NK_UTF_SIZE); nk_input_glyph(ctx, rune); } NK_API nk_bool nk_input_has_mouse_click(const struct nk_input *i, enum nk_buttons id) { const struct nk_mouse_button *btn; if (!i) return nk_false; btn = &i->mouse.buttons[id]; return (btn->clicked && btn->down == nk_false) ? nk_true : nk_false; } NK_API nk_bool nk_input_has_mouse_click_in_rect(const struct nk_input *i, enum nk_buttons id, struct nk_rect b) { const struct nk_mouse_button *btn; if (!i) return nk_false; btn = &i->mouse.buttons[id]; if (!NK_INBOX(btn->clicked_pos.x,btn->clicked_pos.y,b.x,b.y,b.w,b.h)) return nk_false; return nk_true; } NK_API nk_bool nk_input_has_mouse_click_down_in_rect(const struct nk_input *i, enum nk_buttons id, struct nk_rect b, nk_bool down) { const struct nk_mouse_button *btn; if (!i) return nk_false; btn = &i->mouse.buttons[id]; return nk_input_has_mouse_click_in_rect(i, id, b) && (btn->down == down); } NK_API nk_bool nk_input_is_mouse_click_in_rect(const struct nk_input *i, enum nk_buttons id, struct nk_rect b) { const struct nk_mouse_button *btn; if (!i) return nk_false; btn = &i->mouse.buttons[id]; return (nk_input_has_mouse_click_down_in_rect(i, id, b, nk_false) && btn->clicked) ? nk_true : nk_false; } NK_API nk_bool nk_input_is_mouse_click_down_in_rect(const struct nk_input *i, enum nk_buttons id, struct nk_rect b, nk_bool down) { const struct nk_mouse_button *btn; if (!i) return nk_false; btn = &i->mouse.buttons[id]; return (nk_input_has_mouse_click_down_in_rect(i, id, b, down) && btn->clicked) ? nk_true : nk_false; } NK_API nk_bool nk_input_any_mouse_click_in_rect(const struct nk_input *in, struct nk_rect b) { int i, down = 0; for (i = 0; i < NK_BUTTON_MAX; ++i) down = down || nk_input_is_mouse_click_in_rect(in, (enum nk_buttons)i, b); return down; } NK_API nk_bool nk_input_is_mouse_hovering_rect(const struct nk_input *i, struct nk_rect rect) { if (!i) return nk_false; return NK_INBOX(i->mouse.pos.x, i->mouse.pos.y, rect.x, rect.y, rect.w, rect.h); } NK_API nk_bool nk_input_is_mouse_prev_hovering_rect(const struct nk_input *i, struct nk_rect rect) { if (!i) return nk_false; return NK_INBOX(i->mouse.prev.x, i->mouse.prev.y, rect.x, rect.y, rect.w, rect.h); } NK_API nk_bool nk_input_mouse_clicked(const struct nk_input *i, enum nk_buttons id, struct nk_rect rect) { if (!i) return nk_false; if (!nk_input_is_mouse_hovering_rect(i, rect)) return nk_false; return nk_input_is_mouse_click_in_rect(i, id, rect); } NK_API nk_bool nk_input_is_mouse_down(const struct nk_input *i, enum nk_buttons id) { if (!i) return nk_false; return i->mouse.buttons[id].down; } NK_API nk_bool nk_input_is_mouse_pressed(const struct nk_input *i, enum nk_buttons id) { const struct nk_mouse_button *b; if (!i) return nk_false; b = &i->mouse.buttons[id]; if (b->down && b->clicked) return nk_true; return nk_false; } NK_API nk_bool nk_input_is_mouse_released(const struct nk_input *i, enum nk_buttons id) { if (!i) return nk_false; return (!i->mouse.buttons[id].down && i->mouse.buttons[id].clicked); } NK_API nk_bool nk_input_is_key_pressed(const struct nk_input *i, enum nk_keys key) { const struct nk_key *k; if (!i) return nk_false; k = &i->keyboard.keys[key]; if ((k->down && k->clicked) || (!k->down && k->clicked >= 2)) return nk_true; return nk_false; } NK_API nk_bool nk_input_is_key_released(const struct nk_input *i, enum nk_keys key) { const struct nk_key *k; if (!i) return nk_false; k = &i->keyboard.keys[key]; if ((!k->down && k->clicked) || (k->down && k->clicked >= 2)) return nk_true; return nk_false; } NK_API nk_bool nk_input_is_key_down(const struct nk_input *i, enum nk_keys key) { const struct nk_key *k; if (!i) return nk_false; k = &i->keyboard.keys[key]; if (k->down) return nk_true; return nk_false; } /* =============================================================== * * STYLE * * ===============================================================*/ NK_API void nk_style_default(struct nk_context *ctx){nk_style_from_table(ctx, 0);} #define NK_COLOR_MAP(NK_COLOR)\ NK_COLOR(NK_COLOR_TEXT, 175,175,175,255) \ NK_COLOR(NK_COLOR_WINDOW, 45, 45, 45, 255) \ NK_COLOR(NK_COLOR_HEADER, 40, 40, 40, 255) \ NK_COLOR(NK_COLOR_BORDER, 65, 65, 65, 255) \ NK_COLOR(NK_COLOR_BUTTON, 50, 50, 50, 255) \ NK_COLOR(NK_COLOR_BUTTON_HOVER, 40, 40, 40, 255) \ NK_COLOR(NK_COLOR_BUTTON_ACTIVE, 35, 35, 35, 255) \ NK_COLOR(NK_COLOR_TOGGLE, 100,100,100,255) \ NK_COLOR(NK_COLOR_TOGGLE_HOVER, 120,120,120,255) \ NK_COLOR(NK_COLOR_TOGGLE_CURSOR, 45, 45, 45, 255) \ NK_COLOR(NK_COLOR_SELECT, 45, 45, 45, 255) \ NK_COLOR(NK_COLOR_SELECT_ACTIVE, 35, 35, 35,255) \ NK_COLOR(NK_COLOR_SLIDER, 38, 38, 38, 255) \ NK_COLOR(NK_COLOR_SLIDER_CURSOR, 100,100,100,255) \ NK_COLOR(NK_COLOR_SLIDER_CURSOR_HOVER, 120,120,120,255) \ NK_COLOR(NK_COLOR_SLIDER_CURSOR_ACTIVE, 150,150,150,255) \ NK_COLOR(NK_COLOR_PROPERTY, 38, 38, 38, 255) \ NK_COLOR(NK_COLOR_EDIT, 38, 38, 38, 255) \ NK_COLOR(NK_COLOR_EDIT_CURSOR, 175,175,175,255) \ NK_COLOR(NK_COLOR_COMBO, 45, 45, 45, 255) \ NK_COLOR(NK_COLOR_CHART, 120,120,120,255) \ NK_COLOR(NK_COLOR_CHART_COLOR, 45, 45, 45, 255) \ NK_COLOR(NK_COLOR_CHART_COLOR_HIGHLIGHT, 255, 0, 0, 255) \ NK_COLOR(NK_COLOR_SCROLLBAR, 40, 40, 40, 255) \ NK_COLOR(NK_COLOR_SCROLLBAR_CURSOR, 100,100,100,255) \ NK_COLOR(NK_COLOR_SCROLLBAR_CURSOR_HOVER, 120,120,120,255) \ NK_COLOR(NK_COLOR_SCROLLBAR_CURSOR_ACTIVE, 150,150,150,255) \ NK_COLOR(NK_COLOR_TAB_HEADER, 40, 40, 40,255) NK_GLOBAL const struct nk_color nk_default_color_style[NK_COLOR_COUNT] = { #define NK_COLOR(a,b,c,d,e) {b,c,d,e}, NK_COLOR_MAP(NK_COLOR) #undef NK_COLOR }; NK_GLOBAL const char *nk_color_names[NK_COLOR_COUNT] = { #define NK_COLOR(a,b,c,d,e) #a, NK_COLOR_MAP(NK_COLOR) #undef NK_COLOR }; NK_API const char* nk_style_get_color_by_name(enum nk_style_colors c) { return nk_color_names[c]; } NK_API struct nk_style_item nk_style_item_color(struct nk_color col) { struct nk_style_item i; i.type = NK_STYLE_ITEM_COLOR; i.data.color = col; return i; } NK_API struct nk_style_item nk_style_item_image(struct nk_image img) { struct nk_style_item i; i.type = NK_STYLE_ITEM_IMAGE; i.data.image = img; return i; } NK_API struct nk_style_item nk_style_item_nine_slice(struct nk_nine_slice slice) { struct nk_style_item i; i.type = NK_STYLE_ITEM_NINE_SLICE; i.data.slice = slice; return i; } NK_API struct nk_style_item nk_style_item_hide(void) { struct nk_style_item i; i.type = NK_STYLE_ITEM_COLOR; i.data.color = nk_rgba(0,0,0,0); return i; } NK_API void nk_style_from_table(struct nk_context *ctx, const struct nk_color *table) { struct nk_style *style; struct nk_style_text *text; struct nk_style_button *button; struct nk_style_toggle *toggle; struct nk_style_selectable *select; struct nk_style_slider *slider; struct nk_style_progress *prog; struct nk_style_scrollbar *scroll; struct nk_style_edit *edit; struct nk_style_property *property; struct nk_style_combo *combo; struct nk_style_chart *chart; struct nk_style_tab *tab; struct nk_style_window *win; NK_ASSERT(ctx); if (!ctx) return; style = &ctx->style; table = (!table) ? nk_default_color_style: table; /* default text */ text = &style->text; text->color = table[NK_COLOR_TEXT]; text->padding = nk_vec2(0,0); /* default button */ button = &style->button; nk_zero_struct(*button); button->normal = nk_style_item_color(table[NK_COLOR_BUTTON]); button->hover = nk_style_item_color(table[NK_COLOR_BUTTON_HOVER]); button->active = nk_style_item_color(table[NK_COLOR_BUTTON_ACTIVE]); button->border_color = table[NK_COLOR_BORDER]; button->text_background = table[NK_COLOR_BUTTON]; button->text_normal = table[NK_COLOR_TEXT]; button->text_hover = table[NK_COLOR_TEXT]; button->text_active = table[NK_COLOR_TEXT]; button->padding = nk_vec2(2.0f,2.0f); button->image_padding = nk_vec2(0.0f,0.0f); button->touch_padding = nk_vec2(0.0f, 0.0f); button->userdata = nk_handle_ptr(0); button->text_alignment = NK_TEXT_CENTERED; button->border = 1.0f; button->rounding = 4.0f; button->draw_begin = 0; button->draw_end = 0; /* contextual button */ button = &style->contextual_button; nk_zero_struct(*button); button->normal = nk_style_item_color(table[NK_COLOR_WINDOW]); button->hover = nk_style_item_color(table[NK_COLOR_BUTTON_HOVER]); button->active = nk_style_item_color(table[NK_COLOR_BUTTON_ACTIVE]); button->border_color = table[NK_COLOR_WINDOW]; button->text_background = table[NK_COLOR_WINDOW]; button->text_normal = table[NK_COLOR_TEXT]; button->text_hover = table[NK_COLOR_TEXT]; button->text_active = table[NK_COLOR_TEXT]; button->padding = nk_vec2(2.0f,2.0f); button->touch_padding = nk_vec2(0.0f,0.0f); button->userdata = nk_handle_ptr(0); button->text_alignment = NK_TEXT_CENTERED; button->border = 0.0f; button->rounding = 0.0f; button->draw_begin = 0; button->draw_end = 0; /* menu button */ button = &style->menu_button; nk_zero_struct(*button); button->normal = nk_style_item_color(table[NK_COLOR_WINDOW]); button->hover = nk_style_item_color(table[NK_COLOR_WINDOW]); button->active = nk_style_item_color(table[NK_COLOR_WINDOW]); button->border_color = table[NK_COLOR_WINDOW]; button->text_background = table[NK_COLOR_WINDOW]; button->text_normal = table[NK_COLOR_TEXT]; button->text_hover = table[NK_COLOR_TEXT]; button->text_active = table[NK_COLOR_TEXT]; button->padding = nk_vec2(2.0f,2.0f); button->touch_padding = nk_vec2(0.0f,0.0f); button->userdata = nk_handle_ptr(0); button->text_alignment = NK_TEXT_CENTERED; button->border = 0.0f; button->rounding = 1.0f; button->draw_begin = 0; button->draw_end = 0; /* checkbox toggle */ toggle = &style->checkbox; nk_zero_struct(*toggle); toggle->normal = nk_style_item_color(table[NK_COLOR_TOGGLE]); toggle->hover = nk_style_item_color(table[NK_COLOR_TOGGLE_HOVER]); toggle->active = nk_style_item_color(table[NK_COLOR_TOGGLE_HOVER]); toggle->cursor_normal = nk_style_item_color(table[NK_COLOR_TOGGLE_CURSOR]); toggle->cursor_hover = nk_style_item_color(table[NK_COLOR_TOGGLE_CURSOR]); toggle->userdata = nk_handle_ptr(0); toggle->text_background = table[NK_COLOR_WINDOW]; toggle->text_normal = table[NK_COLOR_TEXT]; toggle->text_hover = table[NK_COLOR_TEXT]; toggle->text_active = table[NK_COLOR_TEXT]; toggle->padding = nk_vec2(2.0f, 2.0f); toggle->touch_padding = nk_vec2(0,0); toggle->border_color = nk_rgba(0,0,0,0); toggle->border = 0.0f; toggle->spacing = 4; /* option toggle */ toggle = &style->option; nk_zero_struct(*toggle); toggle->normal = nk_style_item_color(table[NK_COLOR_TOGGLE]); toggle->hover = nk_style_item_color(table[NK_COLOR_TOGGLE_HOVER]); toggle->active = nk_style_item_color(table[NK_COLOR_TOGGLE_HOVER]); toggle->cursor_normal = nk_style_item_color(table[NK_COLOR_TOGGLE_CURSOR]); toggle->cursor_hover = nk_style_item_color(table[NK_COLOR_TOGGLE_CURSOR]); toggle->userdata = nk_handle_ptr(0); toggle->text_background = table[NK_COLOR_WINDOW]; toggle->text_normal = table[NK_COLOR_TEXT]; toggle->text_hover = table[NK_COLOR_TEXT]; toggle->text_active = table[NK_COLOR_TEXT]; toggle->padding = nk_vec2(3.0f, 3.0f); toggle->touch_padding = nk_vec2(0,0); toggle->border_color = nk_rgba(0,0,0,0); toggle->border = 0.0f; toggle->spacing = 4; /* selectable */ select = &style->selectable; nk_zero_struct(*select); select->normal = nk_style_item_color(table[NK_COLOR_SELECT]); select->hover = nk_style_item_color(table[NK_COLOR_SELECT]); select->pressed = nk_style_item_color(table[NK_COLOR_SELECT]); select->normal_active = nk_style_item_color(table[NK_COLOR_SELECT_ACTIVE]); select->hover_active = nk_style_item_color(table[NK_COLOR_SELECT_ACTIVE]); select->pressed_active = nk_style_item_color(table[NK_COLOR_SELECT_ACTIVE]); select->text_normal = table[NK_COLOR_TEXT]; select->text_hover = table[NK_COLOR_TEXT]; select->text_pressed = table[NK_COLOR_TEXT]; select->text_normal_active = table[NK_COLOR_TEXT]; select->text_hover_active = table[NK_COLOR_TEXT]; select->text_pressed_active = table[NK_COLOR_TEXT]; select->padding = nk_vec2(2.0f,2.0f); select->image_padding = nk_vec2(2.0f,2.0f); select->touch_padding = nk_vec2(0,0); select->userdata = nk_handle_ptr(0); select->rounding = 0.0f; select->draw_begin = 0; select->draw_end = 0; /* slider */ slider = &style->slider; nk_zero_struct(*slider); slider->normal = nk_style_item_hide(); slider->hover = nk_style_item_hide(); slider->active = nk_style_item_hide(); slider->bar_normal = table[NK_COLOR_SLIDER]; slider->bar_hover = table[NK_COLOR_SLIDER]; slider->bar_active = table[NK_COLOR_SLIDER]; slider->bar_filled = table[NK_COLOR_SLIDER_CURSOR]; slider->cursor_normal = nk_style_item_color(table[NK_COLOR_SLIDER_CURSOR]); slider->cursor_hover = nk_style_item_color(table[NK_COLOR_SLIDER_CURSOR_HOVER]); slider->cursor_active = nk_style_item_color(table[NK_COLOR_SLIDER_CURSOR_ACTIVE]); slider->inc_symbol = NK_SYMBOL_TRIANGLE_RIGHT; slider->dec_symbol = NK_SYMBOL_TRIANGLE_LEFT; slider->cursor_size = nk_vec2(16,16); slider->padding = nk_vec2(2,2); slider->spacing = nk_vec2(2,2); slider->userdata = nk_handle_ptr(0); slider->show_buttons = nk_false; slider->bar_height = 8; slider->rounding = 0; slider->draw_begin = 0; slider->draw_end = 0; /* slider buttons */ button = &style->slider.inc_button; button->normal = nk_style_item_color(nk_rgb(40,40,40)); button->hover = nk_style_item_color(nk_rgb(42,42,42)); button->active = nk_style_item_color(nk_rgb(44,44,44)); button->border_color = nk_rgb(65,65,65); button->text_background = nk_rgb(40,40,40); button->text_normal = nk_rgb(175,175,175); button->text_hover = nk_rgb(175,175,175); button->text_active = nk_rgb(175,175,175); button->padding = nk_vec2(8.0f,8.0f); button->touch_padding = nk_vec2(0.0f,0.0f); button->userdata = nk_handle_ptr(0); button->text_alignment = NK_TEXT_CENTERED; button->border = 1.0f; button->rounding = 0.0f; button->draw_begin = 0; button->draw_end = 0; style->slider.dec_button = style->slider.inc_button; /* progressbar */ prog = &style->progress; nk_zero_struct(*prog); prog->normal = nk_style_item_color(table[NK_COLOR_SLIDER]); prog->hover = nk_style_item_color(table[NK_COLOR_SLIDER]); prog->active = nk_style_item_color(table[NK_COLOR_SLIDER]); prog->cursor_normal = nk_style_item_color(table[NK_COLOR_SLIDER_CURSOR]); prog->cursor_hover = nk_style_item_color(table[NK_COLOR_SLIDER_CURSOR_HOVER]); prog->cursor_active = nk_style_item_color(table[NK_COLOR_SLIDER_CURSOR_ACTIVE]); prog->border_color = nk_rgba(0,0,0,0); prog->cursor_border_color = nk_rgba(0,0,0,0); prog->userdata = nk_handle_ptr(0); prog->padding = nk_vec2(4,4); prog->rounding = 0; prog->border = 0; prog->cursor_rounding = 0; prog->cursor_border = 0; prog->draw_begin = 0; prog->draw_end = 0; /* scrollbars */ scroll = &style->scrollh; nk_zero_struct(*scroll); scroll->normal = nk_style_item_color(table[NK_COLOR_SCROLLBAR]); scroll->hover = nk_style_item_color(table[NK_COLOR_SCROLLBAR]); scroll->active = nk_style_item_color(table[NK_COLOR_SCROLLBAR]); scroll->cursor_normal = nk_style_item_color(table[NK_COLOR_SCROLLBAR_CURSOR]); scroll->cursor_hover = nk_style_item_color(table[NK_COLOR_SCROLLBAR_CURSOR_HOVER]); scroll->cursor_active = nk_style_item_color(table[NK_COLOR_SCROLLBAR_CURSOR_ACTIVE]); scroll->dec_symbol = NK_SYMBOL_CIRCLE_SOLID; scroll->inc_symbol = NK_SYMBOL_CIRCLE_SOLID; scroll->userdata = nk_handle_ptr(0); scroll->border_color = table[NK_COLOR_SCROLLBAR]; scroll->cursor_border_color = table[NK_COLOR_SCROLLBAR]; scroll->padding = nk_vec2(0,0); scroll->show_buttons = nk_false; scroll->border = 0; scroll->rounding = 0; scroll->border_cursor = 0; scroll->rounding_cursor = 0; scroll->draw_begin = 0; scroll->draw_end = 0; style->scrollv = style->scrollh; /* scrollbars buttons */ button = &style->scrollh.inc_button; button->normal = nk_style_item_color(nk_rgb(40,40,40)); button->hover = nk_style_item_color(nk_rgb(42,42,42)); button->active = nk_style_item_color(nk_rgb(44,44,44)); button->border_color = nk_rgb(65,65,65); button->text_background = nk_rgb(40,40,40); button->text_normal = nk_rgb(175,175,175); button->text_hover = nk_rgb(175,175,175); button->text_active = nk_rgb(175,175,175); button->padding = nk_vec2(4.0f,4.0f); button->touch_padding = nk_vec2(0.0f,0.0f); button->userdata = nk_handle_ptr(0); button->text_alignment = NK_TEXT_CENTERED; button->border = 1.0f; button->rounding = 0.0f; button->draw_begin = 0; button->draw_end = 0; style->scrollh.dec_button = style->scrollh.inc_button; style->scrollv.inc_button = style->scrollh.inc_button; style->scrollv.dec_button = style->scrollh.inc_button; /* edit */ edit = &style->edit; nk_zero_struct(*edit); edit->normal = nk_style_item_color(table[NK_COLOR_EDIT]); edit->hover = nk_style_item_color(table[NK_COLOR_EDIT]); edit->active = nk_style_item_color(table[NK_COLOR_EDIT]); edit->cursor_normal = table[NK_COLOR_TEXT]; edit->cursor_hover = table[NK_COLOR_TEXT]; edit->cursor_text_normal= table[NK_COLOR_EDIT]; edit->cursor_text_hover = table[NK_COLOR_EDIT]; edit->border_color = table[NK_COLOR_BORDER]; edit->text_normal = table[NK_COLOR_TEXT]; edit->text_hover = table[NK_COLOR_TEXT]; edit->text_active = table[NK_COLOR_TEXT]; edit->selected_normal = table[NK_COLOR_TEXT]; edit->selected_hover = table[NK_COLOR_TEXT]; edit->selected_text_normal = table[NK_COLOR_EDIT]; edit->selected_text_hover = table[NK_COLOR_EDIT]; edit->scrollbar_size = nk_vec2(10,10); edit->scrollbar = style->scrollv; edit->padding = nk_vec2(4,4); edit->row_padding = 2; edit->cursor_size = 4; edit->border = 1; edit->rounding = 0; /* property */ property = &style->property; nk_zero_struct(*property); property->normal = nk_style_item_color(table[NK_COLOR_PROPERTY]); property->hover = nk_style_item_color(table[NK_COLOR_PROPERTY]); property->active = nk_style_item_color(table[NK_COLOR_PROPERTY]); property->border_color = table[NK_COLOR_BORDER]; property->label_normal = table[NK_COLOR_TEXT]; property->label_hover = table[NK_COLOR_TEXT]; property->label_active = table[NK_COLOR_TEXT]; property->sym_left = NK_SYMBOL_TRIANGLE_LEFT; property->sym_right = NK_SYMBOL_TRIANGLE_RIGHT; property->userdata = nk_handle_ptr(0); property->padding = nk_vec2(4,4); property->border = 1; property->rounding = 10; property->draw_begin = 0; property->draw_end = 0; /* property buttons */ button = &style->property.dec_button; nk_zero_struct(*button); button->normal = nk_style_item_color(table[NK_COLOR_PROPERTY]); button->hover = nk_style_item_color(table[NK_COLOR_PROPERTY]); button->active = nk_style_item_color(table[NK_COLOR_PROPERTY]); button->border_color = nk_rgba(0,0,0,0); button->text_background = table[NK_COLOR_PROPERTY]; button->text_normal = table[NK_COLOR_TEXT]; button->text_hover = table[NK_COLOR_TEXT]; button->text_active = table[NK_COLOR_TEXT]; button->padding = nk_vec2(0.0f,0.0f); button->touch_padding = nk_vec2(0.0f,0.0f); button->userdata = nk_handle_ptr(0); button->text_alignment = NK_TEXT_CENTERED; button->border = 0.0f; button->rounding = 0.0f; button->draw_begin = 0; button->draw_end = 0; style->property.inc_button = style->property.dec_button; /* property edit */ edit = &style->property.edit; nk_zero_struct(*edit); edit->normal = nk_style_item_color(table[NK_COLOR_PROPERTY]); edit->hover = nk_style_item_color(table[NK_COLOR_PROPERTY]); edit->active = nk_style_item_color(table[NK_COLOR_PROPERTY]); edit->border_color = nk_rgba(0,0,0,0); edit->cursor_normal = table[NK_COLOR_TEXT]; edit->cursor_hover = table[NK_COLOR_TEXT]; edit->cursor_text_normal= table[NK_COLOR_EDIT]; edit->cursor_text_hover = table[NK_COLOR_EDIT]; edit->text_normal = table[NK_COLOR_TEXT]; edit->text_hover = table[NK_COLOR_TEXT]; edit->text_active = table[NK_COLOR_TEXT]; edit->selected_normal = table[NK_COLOR_TEXT]; edit->selected_hover = table[NK_COLOR_TEXT]; edit->selected_text_normal = table[NK_COLOR_EDIT]; edit->selected_text_hover = table[NK_COLOR_EDIT]; edit->padding = nk_vec2(0,0); edit->cursor_size = 8; edit->border = 0; edit->rounding = 0; /* chart */ chart = &style->chart; nk_zero_struct(*chart); chart->background = nk_style_item_color(table[NK_COLOR_CHART]); chart->border_color = table[NK_COLOR_BORDER]; chart->selected_color = table[NK_COLOR_CHART_COLOR_HIGHLIGHT]; chart->color = table[NK_COLOR_CHART_COLOR]; chart->padding = nk_vec2(4,4); chart->border = 0; chart->rounding = 0; /* combo */ combo = &style->combo; combo->normal = nk_style_item_color(table[NK_COLOR_COMBO]); combo->hover = nk_style_item_color(table[NK_COLOR_COMBO]); combo->active = nk_style_item_color(table[NK_COLOR_COMBO]); combo->border_color = table[NK_COLOR_BORDER]; combo->label_normal = table[NK_COLOR_TEXT]; combo->label_hover = table[NK_COLOR_TEXT]; combo->label_active = table[NK_COLOR_TEXT]; combo->sym_normal = NK_SYMBOL_TRIANGLE_DOWN; combo->sym_hover = NK_SYMBOL_TRIANGLE_DOWN; combo->sym_active = NK_SYMBOL_TRIANGLE_DOWN; combo->content_padding = nk_vec2(4,4); combo->button_padding = nk_vec2(0,4); combo->spacing = nk_vec2(4,0); combo->border = 1; combo->rounding = 0; /* combo button */ button = &style->combo.button; nk_zero_struct(*button); button->normal = nk_style_item_color(table[NK_COLOR_COMBO]); button->hover = nk_style_item_color(table[NK_COLOR_COMBO]); button->active = nk_style_item_color(table[NK_COLOR_COMBO]); button->border_color = nk_rgba(0,0,0,0); button->text_background = table[NK_COLOR_COMBO]; button->text_normal = table[NK_COLOR_TEXT]; button->text_hover = table[NK_COLOR_TEXT]; button->text_active = table[NK_COLOR_TEXT]; button->padding = nk_vec2(2.0f,2.0f); button->touch_padding = nk_vec2(0.0f,0.0f); button->userdata = nk_handle_ptr(0); button->text_alignment = NK_TEXT_CENTERED; button->border = 0.0f; button->rounding = 0.0f; button->draw_begin = 0; button->draw_end = 0; /* tab */ tab = &style->tab; tab->background = nk_style_item_color(table[NK_COLOR_TAB_HEADER]); tab->border_color = table[NK_COLOR_BORDER]; tab->text = table[NK_COLOR_TEXT]; tab->sym_minimize = NK_SYMBOL_TRIANGLE_RIGHT; tab->sym_maximize = NK_SYMBOL_TRIANGLE_DOWN; tab->padding = nk_vec2(4,4); tab->spacing = nk_vec2(4,4); tab->indent = 10.0f; tab->border = 1; tab->rounding = 0; /* tab button */ button = &style->tab.tab_minimize_button; nk_zero_struct(*button); button->normal = nk_style_item_color(table[NK_COLOR_TAB_HEADER]); button->hover = nk_style_item_color(table[NK_COLOR_TAB_HEADER]); button->active = nk_style_item_color(table[NK_COLOR_TAB_HEADER]); button->border_color = nk_rgba(0,0,0,0); button->text_background = table[NK_COLOR_TAB_HEADER]; button->text_normal = table[NK_COLOR_TEXT]; button->text_hover = table[NK_COLOR_TEXT]; button->text_active = table[NK_COLOR_TEXT]; button->padding = nk_vec2(2.0f,2.0f); button->touch_padding = nk_vec2(0.0f,0.0f); button->userdata = nk_handle_ptr(0); button->text_alignment = NK_TEXT_CENTERED; button->border = 0.0f; button->rounding = 0.0f; button->draw_begin = 0; button->draw_end = 0; style->tab.tab_maximize_button =*button; /* node button */ button = &style->tab.node_minimize_button; nk_zero_struct(*button); button->normal = nk_style_item_color(table[NK_COLOR_WINDOW]); button->hover = nk_style_item_color(table[NK_COLOR_WINDOW]); button->active = nk_style_item_color(table[NK_COLOR_WINDOW]); button->border_color = nk_rgba(0,0,0,0); button->text_background = table[NK_COLOR_TAB_HEADER]; button->text_normal = table[NK_COLOR_TEXT]; button->text_hover = table[NK_COLOR_TEXT]; button->text_active = table[NK_COLOR_TEXT]; button->padding = nk_vec2(2.0f,2.0f); button->touch_padding = nk_vec2(0.0f,0.0f); button->userdata = nk_handle_ptr(0); button->text_alignment = NK_TEXT_CENTERED; button->border = 0.0f; button->rounding = 0.0f; button->draw_begin = 0; button->draw_end = 0; style->tab.node_maximize_button =*button; /* window header */ win = &style->window; win->header.align = NK_HEADER_LEFT; //< @r-lyeh NK_HEADER_RIGHT > LEFT win->header.close_symbol = NK_SYMBOL_X; win->header.minimize_symbol = NK_SYMBOL_MINUS; win->header.maximize_symbol = NK_SYMBOL_PLUS; win->header.normal = nk_style_item_color(table[NK_COLOR_HEADER]); win->header.hover = nk_style_item_color(table[NK_COLOR_HEADER]); win->header.active = nk_style_item_color(table[NK_COLOR_HEADER]); win->header.label_normal = table[NK_COLOR_TEXT]; win->header.label_hover = table[NK_COLOR_TEXT]; win->header.label_active = table[NK_COLOR_TEXT]; win->header.label_padding = nk_vec2(4,4); win->header.padding = nk_vec2(4,4); win->header.spacing = nk_vec2(0,0); /* window header close button */ button = &style->window.header.close_button; nk_zero_struct(*button); #if 0 //< @r-lyeh button->normal = nk_style_item_color(table[NK_COLOR_HEADER]); button->hover = nk_style_item_color(table[NK_COLOR_HEADER]); button->active = nk_style_item_color(table[NK_COLOR_HEADER]); button->border_color = nk_rgba(0,0,0,0); button->text_background = table[NK_COLOR_HEADER]; button->text_normal = table[NK_COLOR_TEXT]; button->text_hover = table[NK_COLOR_TEXT]; button->text_active = table[NK_COLOR_TEXT]; button->padding = nk_vec2(0.0f,0.0f); button->touch_padding = nk_vec2(0.0f,0.0f); button->userdata = nk_handle_ptr(0); button->text_alignment = NK_TEXT_CENTERED; button->border = 0.0f; button->rounding = 0.0f; button->draw_begin = 0; button->draw_end = 0; #endif /* window header minimize button */ button = &style->window.header.minimize_button; nk_zero_struct(*button); #if 0 //< @r-lyeh button->normal = nk_style_item_color(table[NK_COLOR_HEADER]); button->hover = nk_style_item_color(table[NK_COLOR_HEADER]); button->active = nk_style_item_color(table[NK_COLOR_HEADER]); button->border_color = nk_rgba(0,0,0,0); button->text_background = table[NK_COLOR_HEADER]; button->text_normal = table[NK_COLOR_TEXT]; button->text_hover = table[NK_COLOR_TEXT]; button->text_active = table[NK_COLOR_TEXT]; button->padding = nk_vec2(0.0f,0.0f); button->touch_padding = nk_vec2(0.0f,0.0f); button->userdata = nk_handle_ptr(0); button->text_alignment = NK_TEXT_CENTERED; button->border = 0.0f; button->rounding = 0.0f; button->draw_begin = 0; button->draw_end = 0; #endif /* window */ win->background = table[NK_COLOR_WINDOW]; win->fixed_background = nk_style_item_color(table[NK_COLOR_WINDOW]); win->border_color = table[NK_COLOR_BORDER]; win->popup_border_color = table[NK_COLOR_BORDER]; win->combo_border_color = table[NK_COLOR_BORDER]; win->contextual_border_color = table[NK_COLOR_BORDER]; win->menu_border_color = table[NK_COLOR_BORDER]; win->group_border_color = table[NK_COLOR_BORDER]; win->tooltip_border_color = table[NK_COLOR_BORDER]; win->scaler = nk_style_item_color(table[NK_COLOR_TEXT]); win->rounding = 0.0f; win->spacing = nk_vec2(4,4); win->scrollbar_size = nk_vec2(10,10); win->min_size = nk_vec2(64,64); win->combo_border = 1.0f; win->contextual_border = 1.0f; win->menu_border = 1.0f; win->group_border = 1.0f; win->tooltip_border = 1.0f; win->popup_border = 1.0f; win->border = 2.0f; win->min_row_height_padding = 8; win->padding = nk_vec2(4,4); win->group_padding = nk_vec2(4,4); win->popup_padding = nk_vec2(4,4); win->combo_padding = nk_vec2(4,4); win->contextual_padding = nk_vec2(4,4); win->menu_padding = nk_vec2(4,4); win->tooltip_padding = nk_vec2(4,4); } NK_API void nk_style_set_font(struct nk_context *ctx, const struct nk_user_font *font) { struct nk_style *style; NK_ASSERT(ctx); if (!ctx) return; style = &ctx->style; style->font = font; ctx->stacks.fonts.head = 0; if (ctx->current) nk_layout_reset_min_row_height(ctx); } NK_API nk_bool nk_style_push_font(struct nk_context *ctx, const struct nk_user_font *font) { struct nk_config_stack_user_font *font_stack; struct nk_config_stack_user_font_element *element; NK_ASSERT(ctx); if (!ctx) return 0; font_stack = &ctx->stacks.fonts; NK_ASSERT(font_stack->head < (int)NK_LEN(font_stack->elements)); if (font_stack->head >= (int)NK_LEN(font_stack->elements)) return 0; element = &font_stack->elements[font_stack->head++]; element->address = &ctx->style.font; element->old_value = ctx->style.font; ctx->style.font = font; return 1; } NK_API nk_bool nk_style_pop_font(struct nk_context *ctx) { struct nk_config_stack_user_font *font_stack; struct nk_config_stack_user_font_element *element; NK_ASSERT(ctx); if (!ctx) return 0; font_stack = &ctx->stacks.fonts; NK_ASSERT(font_stack->head > 0); if (font_stack->head < 1) return 0; element = &font_stack->elements[--font_stack->head]; *element->address = element->old_value; return 1; } #define NK_STYLE_PUSH_IMPLEMENATION(prefix, type, stack) \ nk_style_push_##type(struct nk_context *ctx, prefix##_##type *address, prefix##_##type value)\ {\ struct nk_config_stack_##type * type_stack;\ struct nk_config_stack_##type##_element *element;\ NK_ASSERT(ctx);\ if (!ctx) return 0;\ type_stack = &ctx->stacks.stack;\ NK_ASSERT(type_stack->head < (int)NK_LEN(type_stack->elements));\ if (type_stack->head >= (int)NK_LEN(type_stack->elements))\ return 0;\ element = &type_stack->elements[type_stack->head++];\ element->address = address;\ element->old_value = *address;\ *address = value;\ return 1;\ } #define NK_STYLE_POP_IMPLEMENATION(type, stack) \ nk_style_pop_##type(struct nk_context *ctx)\ {\ struct nk_config_stack_##type *type_stack;\ struct nk_config_stack_##type##_element *element;\ NK_ASSERT(ctx);\ if (!ctx) return 0;\ type_stack = &ctx->stacks.stack;\ NK_ASSERT(type_stack->head > 0);\ if (type_stack->head < 1)\ return 0;\ element = &type_stack->elements[--type_stack->head];\ *element->address = element->old_value;\ return 1;\ } NK_API nk_bool NK_STYLE_PUSH_IMPLEMENATION(struct nk, style_item, style_items) NK_API nk_bool NK_STYLE_PUSH_IMPLEMENATION(nk,float, floats) NK_API nk_bool NK_STYLE_PUSH_IMPLEMENATION(struct nk, vec2, vectors) NK_API nk_bool NK_STYLE_PUSH_IMPLEMENATION(nk,flags, flags) NK_API nk_bool NK_STYLE_PUSH_IMPLEMENATION(struct nk,color, colors) NK_API nk_bool NK_STYLE_POP_IMPLEMENATION(style_item, style_items) NK_API nk_bool NK_STYLE_POP_IMPLEMENATION(float,floats) NK_API nk_bool NK_STYLE_POP_IMPLEMENATION(vec2, vectors) NK_API nk_bool NK_STYLE_POP_IMPLEMENATION(flags,flags) NK_API nk_bool NK_STYLE_POP_IMPLEMENATION(color,colors) NK_API nk_bool nk_style_set_cursor(struct nk_context *ctx, enum nk_style_cursor c) { struct nk_style *style; NK_ASSERT(ctx); if (!ctx) return 0; style = &ctx->style; if (style->cursors[c]) { style->cursor_active = style->cursors[c]; return 1; } return 0; } NK_API void nk_style_show_cursor(struct nk_context *ctx) { ctx->style.cursor_visible = nk_true; } NK_API void nk_style_hide_cursor(struct nk_context *ctx) { ctx->style.cursor_visible = nk_false; } NK_API void nk_style_load_cursor(struct nk_context *ctx, enum nk_style_cursor cursor, const struct nk_cursor *c) { struct nk_style *style; NK_ASSERT(ctx); if (!ctx) return; style = &ctx->style; style->cursors[cursor] = c; } NK_API void nk_style_load_all_cursors(struct nk_context *ctx, struct nk_cursor *cursors) { int i = 0; struct nk_style *style; NK_ASSERT(ctx); if (!ctx) return; style = &ctx->style; for (i = 0; i < NK_CURSOR_COUNT; ++i) style->cursors[i] = &cursors[i]; style->cursor_visible = nk_true; } /* ============================================================== * * CONTEXT * * ===============================================================*/ NK_INTERN void nk_setup(struct nk_context *ctx, const struct nk_user_font *font) { NK_ASSERT(ctx); if (!ctx) return; nk_zero_struct(*ctx); nk_style_default(ctx); ctx->seq = 1; if (font) ctx->style.font = font; #ifdef NK_INCLUDE_VERTEX_BUFFER_OUTPUT nk_draw_list_init(&ctx->draw_list); #endif } #ifdef NK_INCLUDE_DEFAULT_ALLOCATOR NK_API nk_bool nk_init_default(struct nk_context *ctx, const struct nk_user_font *font) { struct nk_allocator alloc; alloc.userdata.ptr = 0; alloc.alloc = nk_malloc; alloc.free = nk_mfree; return nk_init(ctx, &alloc, font); } #endif NK_API nk_bool nk_init_fixed(struct nk_context *ctx, void *memory, nk_size size, const struct nk_user_font *font) { NK_ASSERT(memory); if (!memory) return 0; nk_setup(ctx, font); nk_buffer_init_fixed(&ctx->memory, memory, size); ctx->use_pool = nk_false; return 1; } NK_API nk_bool nk_init_custom(struct nk_context *ctx, struct nk_buffer *cmds, struct nk_buffer *pool, const struct nk_user_font *font) { NK_ASSERT(cmds); NK_ASSERT(pool); if (!cmds || !pool) return 0; nk_setup(ctx, font); ctx->memory = *cmds; if (pool->type == NK_BUFFER_FIXED) { /* take memory from buffer and alloc fixed pool */ nk_pool_init_fixed(&ctx->pool, pool->memory.ptr, pool->memory.size); } else { /* create dynamic pool from buffer allocator */ struct nk_allocator *alloc = &pool->pool; nk_pool_init(&ctx->pool, alloc, NK_POOL_DEFAULT_CAPACITY); } ctx->use_pool = nk_true; return 1; } NK_API nk_bool nk_init(struct nk_context *ctx, struct nk_allocator *alloc, const struct nk_user_font *font) { NK_ASSERT(alloc); if (!alloc) return 0; nk_setup(ctx, font); nk_buffer_init(&ctx->memory, alloc, NK_DEFAULT_COMMAND_BUFFER_SIZE); nk_pool_init(&ctx->pool, alloc, NK_POOL_DEFAULT_CAPACITY); ctx->use_pool = nk_true; return 1; } #ifdef NK_INCLUDE_COMMAND_USERDATA NK_API void nk_set_user_data(struct nk_context *ctx, nk_handle handle) { if (!ctx) return; ctx->userdata = handle; if (ctx->current) ctx->current->buffer.userdata = handle; } #endif NK_API void nk_free(struct nk_context *ctx) { NK_ASSERT(ctx); if (!ctx) return; nk_buffer_free(&ctx->memory); if (ctx->use_pool) nk_pool_free(&ctx->pool); nk_zero(&ctx->input, sizeof(ctx->input)); nk_zero(&ctx->style, sizeof(ctx->style)); nk_zero(&ctx->memory, sizeof(ctx->memory)); ctx->seq = 0; ctx->build = 0; ctx->begin = 0; ctx->end = 0; ctx->active = 0; ctx->current = 0; ctx->freelist = 0; ctx->count = 0; } NK_API void nk_clear(struct nk_context *ctx) { struct nk_window *iter; struct nk_window *next; NK_ASSERT(ctx); if (!ctx) return; if (ctx->use_pool) nk_buffer_clear(&ctx->memory); else nk_buffer_reset(&ctx->memory, NK_BUFFER_FRONT); ctx->build = 0; ctx->memory.calls = 0; ctx->last_widget_state = 0; ctx->style.cursor_active = ctx->style.cursors[NK_CURSOR_ARROW]; NK_MEMSET(&ctx->overlay, 0, sizeof(ctx->overlay)); /* garbage collector */ iter = ctx->begin; while (iter) { /* make sure valid minimized windows do not get removed */ if ((iter->flags & NK_WINDOW_MINIMIZED) && !(iter->flags & NK_WINDOW_CLOSED) && iter->seq == ctx->seq) { iter = iter->next; continue; } /* remove hotness from hidden or closed windows*/ if (((iter->flags & NK_WINDOW_HIDDEN) || (iter->flags & NK_WINDOW_CLOSED)) && iter == ctx->active) { ctx->active = iter->prev; ctx->end = iter->prev; if (!ctx->end) ctx->begin = 0; if (ctx->active) ctx->active->flags &= ~(unsigned)NK_WINDOW_ROM; } /* free unused popup windows */ if (iter->popup.win && iter->popup.win->seq != ctx->seq) { nk_free_window(ctx, iter->popup.win); iter->popup.win = 0; } /* remove unused window state tables */ {struct nk_table *n, *it = iter->tables; while (it) { n = it->next; if (it->seq != ctx->seq) { nk_remove_table(iter, it); nk_zero(it, sizeof(union nk_page_data)); nk_free_table(ctx, it); if (it == iter->tables) iter->tables = n; } it = n; }} /* window itself is not used anymore so free */ if (iter->seq != ctx->seq || iter->flags & NK_WINDOW_CLOSED) { next = iter->next; nk_remove_window(ctx, iter); nk_free_window(ctx, iter); iter = next; } else iter = iter->next; } ctx->seq++; } NK_LIB void nk_start_buffer(struct nk_context *ctx, struct nk_command_buffer *buffer) { NK_ASSERT(ctx); NK_ASSERT(buffer); if (!ctx || !buffer) return; buffer->begin = ctx->memory.allocated; buffer->end = buffer->begin; buffer->last = buffer->begin; buffer->clip = nk_null_rect; } NK_LIB void nk_start(struct nk_context *ctx, struct nk_window *win) { NK_ASSERT(ctx); NK_ASSERT(win); nk_start_buffer(ctx, &win->buffer); } NK_LIB void nk_start_popup(struct nk_context *ctx, struct nk_window *win) { struct nk_popup_buffer *buf; NK_ASSERT(ctx); NK_ASSERT(win); if (!ctx || !win) return; /* save buffer fill state for popup */ buf = &win->popup.buf; buf->begin = win->buffer.end; buf->end = win->buffer.end; buf->parent = win->buffer.last; buf->last = buf->begin; buf->active = nk_true; } NK_LIB void nk_finish_popup(struct nk_context *ctx, struct nk_window *win) { struct nk_popup_buffer *buf; NK_ASSERT(ctx); NK_ASSERT(win); if (!ctx || !win) return; buf = &win->popup.buf; buf->last = win->buffer.last; buf->end = win->buffer.end; } NK_LIB void nk_finish_buffer(struct nk_context *ctx, struct nk_command_buffer *buffer) { NK_ASSERT(ctx); NK_ASSERT(buffer); if (!ctx || !buffer) return; buffer->end = ctx->memory.allocated; } NK_LIB void nk_finish(struct nk_context *ctx, struct nk_window *win) { struct nk_popup_buffer *buf; struct nk_command *parent_last; void *memory; NK_ASSERT(ctx); NK_ASSERT(win); if (!ctx || !win) return; nk_finish_buffer(ctx, &win->buffer); if (!win->popup.buf.active) return; buf = &win->popup.buf; memory = ctx->memory.memory.ptr; parent_last = nk_ptr_add(struct nk_command, memory, buf->parent); parent_last->next = buf->end; } NK_LIB void nk_build(struct nk_context *ctx) { struct nk_window *it = 0; struct nk_command *cmd = 0; nk_byte *buffer = 0; /* draw cursor overlay */ if (!ctx->style.cursor_active) ctx->style.cursor_active = ctx->style.cursors[NK_CURSOR_ARROW]; if (ctx->style.cursor_active && !ctx->input.mouse.grabbed && ctx->style.cursor_visible) { struct nk_rect mouse_bounds; const struct nk_cursor *cursor = ctx->style.cursor_active; nk_command_buffer_init(&ctx->overlay, &ctx->memory, NK_CLIPPING_OFF); nk_start_buffer(ctx, &ctx->overlay); mouse_bounds.x = ctx->input.mouse.pos.x - cursor->offset.x; mouse_bounds.y = ctx->input.mouse.pos.y - cursor->offset.y; mouse_bounds.w = cursor->size.x; mouse_bounds.h = cursor->size.y; nk_draw_image(&ctx->overlay, mouse_bounds, &cursor->img, nk_white); nk_finish_buffer(ctx, &ctx->overlay); } /* build one big draw command list out of all window buffers */ it = ctx->begin; buffer = (nk_byte*)ctx->memory.memory.ptr; while (it != 0) { struct nk_window *next = it->next; if (it->buffer.last == it->buffer.begin || (it->flags & NK_WINDOW_HIDDEN)|| it->seq != ctx->seq) goto cont; cmd = nk_ptr_add(struct nk_command, buffer, it->buffer.last); while (next && ((next->buffer.last == next->buffer.begin) || (next->flags & NK_WINDOW_HIDDEN) || next->seq != ctx->seq)) next = next->next; /* skip empty command buffers */ if (next) cmd->next = next->buffer.begin; cont: it = next; } /* append all popup draw commands into lists */ it = ctx->begin; while (it != 0) { struct nk_window *next = it->next; struct nk_popup_buffer *buf; if (!it->popup.buf.active) goto skip; buf = &it->popup.buf; cmd->next = buf->begin; cmd = nk_ptr_add(struct nk_command, buffer, buf->last); buf->active = nk_false; skip: it = next; } if (cmd) { /* append overlay commands */ if (ctx->overlay.end != ctx->overlay.begin) cmd->next = ctx->overlay.begin; else cmd->next = ctx->memory.allocated; } } NK_API const struct nk_command* nk__begin(struct nk_context *ctx) { struct nk_window *iter; nk_byte *buffer; NK_ASSERT(ctx); if (!ctx) return 0; if (!ctx->count) return 0; buffer = (nk_byte*)ctx->memory.memory.ptr; if (!ctx->build) { nk_build(ctx); ctx->build = nk_true; } iter = ctx->begin; while (iter && ((iter->buffer.begin == iter->buffer.end) || (iter->flags & NK_WINDOW_HIDDEN) || iter->seq != ctx->seq)) iter = iter->next; if (!iter) return 0; return nk_ptr_add_const(struct nk_command, buffer, iter->buffer.begin); } NK_API const struct nk_command* nk__next(struct nk_context *ctx, const struct nk_command *cmd) { nk_byte *buffer; const struct nk_command *next; NK_ASSERT(ctx); if (!ctx || !cmd || !ctx->count) return 0; if (cmd->next >= ctx->memory.allocated) return 0; buffer = (nk_byte*)ctx->memory.memory.ptr; next = nk_ptr_add_const(struct nk_command, buffer, cmd->next); return next; } /* =============================================================== * * POOL * * ===============================================================*/ NK_LIB void nk_pool_init(struct nk_pool *pool, struct nk_allocator *alloc, unsigned int capacity) { NK_ASSERT(capacity >= 1); nk_zero(pool, sizeof(*pool)); pool->alloc = *alloc; pool->capacity = capacity; pool->type = NK_BUFFER_DYNAMIC; pool->pages = 0; } NK_LIB void nk_pool_free(struct nk_pool *pool) { struct nk_page *iter; if (!pool) return; iter = pool->pages; if (pool->type == NK_BUFFER_FIXED) return; while (iter) { struct nk_page *next = iter->next; pool->alloc.free(pool->alloc.userdata, iter); iter = next; } } NK_LIB void nk_pool_init_fixed(struct nk_pool *pool, void *memory, nk_size size) { nk_zero(pool, sizeof(*pool)); NK_ASSERT(size >= sizeof(struct nk_page)); if (size < sizeof(struct nk_page)) return; /* first nk_page_element is embedded in nk_page, additional elements follow in adjacent space */ pool->capacity = (unsigned)(1 + (size - sizeof(struct nk_page)) / sizeof(struct nk_page_element)); pool->pages = (struct nk_page*)memory; pool->type = NK_BUFFER_FIXED; pool->size = size; } NK_LIB struct nk_page_element* nk_pool_alloc(struct nk_pool *pool) { if (!pool->pages || pool->pages->size >= pool->capacity) { /* allocate new page */ struct nk_page *page; if (pool->type == NK_BUFFER_FIXED) { NK_ASSERT(pool->pages); if (!pool->pages) return 0; NK_ASSERT(pool->pages->size < pool->capacity); return 0; } else { nk_size size = sizeof(struct nk_page); size += (pool->capacity - 1) * sizeof(struct nk_page_element); page = (struct nk_page*)pool->alloc.alloc(pool->alloc.userdata,0, size); page->next = pool->pages; pool->pages = page; page->size = 0; } } return &pool->pages->win[pool->pages->size++]; } /* =============================================================== * * PAGE ELEMENT * * ===============================================================*/ NK_LIB struct nk_page_element* nk_create_page_element(struct nk_context *ctx) { struct nk_page_element *elem; if (ctx->freelist) { /* unlink page element from free list */ elem = ctx->freelist; ctx->freelist = elem->next; } else if (ctx->use_pool) { /* allocate page element from memory pool */ elem = nk_pool_alloc(&ctx->pool); NK_ASSERT(elem); if (!elem) return 0; } else { /* allocate new page element from back of fixed size memory buffer */ NK_STORAGE const nk_size size = sizeof(struct nk_page_element); NK_STORAGE const nk_size align = NK_ALIGNOF(struct nk_page_element); elem = (struct nk_page_element*)nk_buffer_alloc(&ctx->memory, NK_BUFFER_BACK, size, align); NK_ASSERT(elem); if (!elem) return 0; } nk_zero_struct(*elem); elem->next = 0; elem->prev = 0; return elem; } NK_LIB void nk_link_page_element_into_freelist(struct nk_context *ctx, struct nk_page_element *elem) { /* link table into freelist */ if (!ctx->freelist) { ctx->freelist = elem; } else { elem->next = ctx->freelist; ctx->freelist = elem; } } NK_LIB void nk_free_page_element(struct nk_context *ctx, struct nk_page_element *elem) { /* we have a pool so just add to free list */ if (ctx->use_pool) { nk_link_page_element_into_freelist(ctx, elem); return; } /* if possible remove last element from back of fixed memory buffer */ {void *elem_end = (void*)(elem + 1); void *buffer_end = (nk_byte*)ctx->memory.memory.ptr + ctx->memory.size; if (elem_end == buffer_end) ctx->memory.size -= sizeof(struct nk_page_element); else nk_link_page_element_into_freelist(ctx, elem);} } /* =============================================================== * * TABLE * * ===============================================================*/ NK_LIB struct nk_table* nk_create_table(struct nk_context *ctx) { struct nk_page_element *elem; elem = nk_create_page_element(ctx); if (!elem) return 0; nk_zero_struct(*elem); return &elem->data.tbl; } NK_LIB void nk_free_table(struct nk_context *ctx, struct nk_table *tbl) { union nk_page_data *pd = NK_CONTAINER_OF(tbl, union nk_page_data, tbl); struct nk_page_element *pe = NK_CONTAINER_OF(pd, struct nk_page_element, data); nk_free_page_element(ctx, pe); } NK_LIB void nk_push_table(struct nk_window *win, struct nk_table *tbl) { if (!win->tables) { win->tables = tbl; tbl->next = 0; tbl->prev = 0; tbl->size = 0; win->table_count = 1; return; } win->tables->prev = tbl; tbl->next = win->tables; tbl->prev = 0; tbl->size = 0; win->tables = tbl; win->table_count++; } NK_LIB void nk_remove_table(struct nk_window *win, struct nk_table *tbl) { if (win->tables == tbl) win->tables = tbl->next; if (tbl->next) tbl->next->prev = tbl->prev; if (tbl->prev) tbl->prev->next = tbl->next; tbl->next = 0; tbl->prev = 0; } NK_LIB nk_uint* nk_add_value(struct nk_context *ctx, struct nk_window *win, nk_hash name, nk_uint value) { NK_ASSERT(ctx); NK_ASSERT(win); if (!win || !ctx) return 0; if (!win->tables || win->tables->size >= NK_VALUE_PAGE_CAPACITY) { struct nk_table *tbl = nk_create_table(ctx); NK_ASSERT(tbl); if (!tbl) return 0; nk_push_table(win, tbl); } win->tables->seq = win->seq; win->tables->keys[win->tables->size] = name; win->tables->values[win->tables->size] = value; return &win->tables->values[win->tables->size++]; } NK_LIB nk_uint* nk_find_value(struct nk_window *win, nk_hash name) { struct nk_table *iter = win->tables; while (iter) { unsigned int i = 0; unsigned int size = iter->size; for (i = 0; i < size; ++i) { if (iter->keys[i] == name) { iter->seq = win->seq; return &iter->values[i]; } } size = NK_VALUE_PAGE_CAPACITY; iter = iter->next; } return 0; } /* =============================================================== * * PANEL * * ===============================================================*/ NK_LIB void* nk_create_panel(struct nk_context *ctx) { struct nk_page_element *elem; elem = nk_create_page_element(ctx); if (!elem) return 0; nk_zero_struct(*elem); return &elem->data.pan; } NK_LIB void nk_free_panel(struct nk_context *ctx, struct nk_panel *pan) { union nk_page_data *pd = NK_CONTAINER_OF(pan, union nk_page_data, pan); struct nk_page_element *pe = NK_CONTAINER_OF(pd, struct nk_page_element, data); nk_free_page_element(ctx, pe); } NK_LIB nk_bool nk_panel_has_header(nk_flags flags, const char *title) { nk_bool active = 0; active = (flags & (NK_WINDOW_CLOSABLE|NK_WINDOW_MINIMIZABLE)); active = active || (flags & NK_WINDOW_TITLE); active = active && !(flags & NK_WINDOW_HIDDEN) && title; return active; } NK_LIB struct nk_vec2 nk_panel_get_padding(const struct nk_style *style, enum nk_panel_type type) { switch (type) { default: case NK_PANEL_WINDOW: return style->window.padding; case NK_PANEL_GROUP: return style->window.group_padding; case NK_PANEL_POPUP: return style->window.popup_padding; case NK_PANEL_CONTEXTUAL: return style->window.contextual_padding; case NK_PANEL_COMBO: return style->window.combo_padding; case NK_PANEL_MENU: return style->window.menu_padding; case NK_PANEL_TOOLTIP: return style->window.menu_padding;} } NK_LIB float nk_panel_get_border(const struct nk_style *style, nk_flags flags, enum nk_panel_type type) { if (flags & NK_WINDOW_BORDER) { switch (type) { default: case NK_PANEL_WINDOW: return style->window.border; case NK_PANEL_GROUP: return style->window.group_border; case NK_PANEL_POPUP: return style->window.popup_border; case NK_PANEL_CONTEXTUAL: return style->window.contextual_border; case NK_PANEL_COMBO: return style->window.combo_border; case NK_PANEL_MENU: return style->window.menu_border; case NK_PANEL_TOOLTIP: return style->window.menu_border; }} else return 0; } NK_LIB struct nk_color nk_panel_get_border_color(const struct nk_style *style, enum nk_panel_type type) { switch (type) { default: case NK_PANEL_WINDOW: return style->window.border_color; case NK_PANEL_GROUP: return style->window.group_border_color; case NK_PANEL_POPUP: return style->window.popup_border_color; case NK_PANEL_CONTEXTUAL: return style->window.contextual_border_color; case NK_PANEL_COMBO: return style->window.combo_border_color; case NK_PANEL_MENU: return style->window.menu_border_color; case NK_PANEL_TOOLTIP: return style->window.menu_border_color;} } NK_LIB nk_bool nk_panel_is_sub(enum nk_panel_type type) { return (type & NK_PANEL_SET_SUB)?1:0; } NK_LIB nk_bool nk_panel_is_nonblock(enum nk_panel_type type) { return (type & NK_PANEL_SET_NONBLOCK)?1:0; } NK_LIB nk_bool nk_panel_begin(struct nk_context *ctx, const char *title, enum nk_panel_type panel_type) { struct nk_input *in; struct nk_window *win; struct nk_panel *layout; struct nk_command_buffer *out; const struct nk_style *style; const struct nk_user_font *font; struct nk_vec2 scrollbar_size; struct nk_vec2 panel_padding; NK_ASSERT(ctx); NK_ASSERT(ctx->current); NK_ASSERT(ctx->current->layout); if (!ctx || !ctx->current || !ctx->current->layout) return 0; nk_zero(ctx->current->layout, sizeof(*ctx->current->layout)); if ((ctx->current->flags & NK_WINDOW_HIDDEN) || (ctx->current->flags & NK_WINDOW_CLOSED)) { nk_zero(ctx->current->layout, sizeof(struct nk_panel)); ctx->current->layout->type = panel_type; return 0; } /* pull state into local stack */ style = &ctx->style; font = style->font; win = ctx->current; layout = win->layout; out = &win->buffer; in = (win->flags & NK_WINDOW_NO_INPUT) ? 0: &ctx->input; #ifdef NK_INCLUDE_COMMAND_USERDATA win->buffer.userdata = ctx->userdata; #endif /* pull style configuration into local stack */ scrollbar_size = style->window.scrollbar_size; panel_padding = nk_panel_get_padding(style, panel_type); /* window movement */ if ((win->flags & NK_WINDOW_MOVABLE) && !(win->flags & NK_WINDOW_ROM)) { nk_bool left_mouse_down; unsigned int left_mouse_clicked; int left_mouse_click_in_cursor; /* calculate draggable window space */ struct nk_rect header; header.x = win->bounds.x; header.y = win->bounds.y; header.w = win->bounds.w; if (nk_panel_has_header(win->flags, title)) { header.h = font->height + 2.0f * style->window.header.padding.y; header.h += 2.0f * style->window.header.label_padding.y; } else header.h = panel_padding.y; /* window movement by dragging */ left_mouse_down = in->mouse.buttons[NK_BUTTON_LEFT].down; left_mouse_clicked = in->mouse.buttons[NK_BUTTON_LEFT].clicked; left_mouse_click_in_cursor = nk_input_has_mouse_click_down_in_rect(in, NK_BUTTON_LEFT, header, nk_true); if (left_mouse_down && left_mouse_click_in_cursor && !left_mouse_clicked) { win->bounds.x = win->bounds.x + in->mouse.delta.x; win->bounds.y = win->bounds.y + in->mouse.delta.y; in->mouse.buttons[NK_BUTTON_LEFT].clicked_pos.x += in->mouse.delta.x; in->mouse.buttons[NK_BUTTON_LEFT].clicked_pos.y += in->mouse.delta.y; ctx->style.cursor_active = ctx->style.cursors[NK_CURSOR_MOVE]; } } /* setup panel */ layout->type = panel_type; layout->flags = win->flags; layout->bounds = win->bounds; layout->bounds.x += panel_padding.x; layout->bounds.w -= 2*panel_padding.x; if (win->flags & NK_WINDOW_BORDER) { layout->border = nk_panel_get_border(style, win->flags, panel_type); layout->bounds = nk_shrink_rect(layout->bounds, layout->border); } else layout->border = 0; layout->at_y = layout->bounds.y; layout->at_x = layout->bounds.x; layout->max_x = 0; layout->header_height = 0; layout->footer_height = 0; nk_layout_reset_min_row_height(ctx); layout->row.index = 0; layout->row.columns = 0; layout->row.ratio = 0; layout->row.item_width = 0; layout->row.tree_depth = 0; layout->row.height = panel_padding.y; layout->has_scrolling = nk_true; if (!(win->flags & NK_WINDOW_NO_SCROLLBAR_Y)) //< @r-lyeh layout->bounds.w -= scrollbar_size.x; if (!nk_panel_is_nonblock(panel_type)) { layout->footer_height = 0; if (!(win->flags & NK_WINDOW_NO_SCROLLBAR_X) || win->flags & NK_WINDOW_SCALABLE) //< @r-lyeh layout->footer_height = scrollbar_size.y; layout->bounds.h -= layout->footer_height; } /* panel header */ if (nk_panel_has_header(win->flags, title)) { struct nk_text text; struct nk_rect header; const struct nk_style_item *background = 0; /* calculate header bounds */ header.x = win->bounds.x; header.y = win->bounds.y; header.w = win->bounds.w; header.h = font->height + 2.0f * style->window.header.padding.y; header.h += (2.0f * style->window.header.label_padding.y); /* shrink panel by header */ layout->header_height = header.h; layout->bounds.y += header.h; layout->bounds.h -= header.h; layout->at_y += header.h; /* select correct header background and text color */ if (ctx->active == win) { background = &style->window.header.active; text.text = style->window.header.label_active; } else if (nk_input_is_mouse_hovering_rect(&ctx->input, header)) { background = &style->window.header.hover; text.text = style->window.header.label_hover; } else { background = &style->window.header.normal; text.text = style->window.header.label_normal; } /* draw header background */ header.h += 1.0f; switch(background->type) { case NK_STYLE_ITEM_IMAGE: text.background = nk_rgba(0,0,0,0); nk_draw_image(&win->buffer, header, &background->data.image, nk_white); break; case NK_STYLE_ITEM_NINE_SLICE: text.background = nk_rgba(0, 0, 0, 0); nk_draw_nine_slice(&win->buffer, header, &background->data.slice, nk_white); break; case NK_STYLE_ITEM_COLOR: text.background = background->data.color; nk_fill_rect(out, header, 0, background->data.color); break; } /* window close button */ {struct nk_rect button; button.y = header.y + style->window.header.padding.y; button.h = header.h - 2 * style->window.header.padding.y; button.w = button.h; if (win->flags & NK_WINDOW_CLOSABLE) { nk_flags ws = 0; if (style->window.header.align == NK_HEADER_RIGHT || 1) { //< @r-lyeh: ||1 button.x = (header.w + header.x) - (button.w + style->window.header.padding.x); header.w -= button.w + style->window.header.spacing.x + style->window.header.padding.x; } else { button.x = header.x + style->window.header.padding.x; header.x += button.w + style->window.header.spacing.x + style->window.header.padding.x; } if (nk_do_button_symbol(&ws, &win->buffer, button, style->window.header.close_symbol, NK_BUTTON_DEFAULT, &style->window.header.close_button, in, style->font) && !(win->flags & NK_WINDOW_ROM)) { layout->flags |= NK_WINDOW_HIDDEN; layout->flags &= (nk_flags)~NK_WINDOW_MINIMIZED; } } #if 1 //< @r-lyeh /* window pushpin button */ if (win->flags & NK_WINDOW_PINNABLE) { nk_flags ws = 0; if (style->window.header.align == NK_HEADER_RIGHT || 1) { //< @r-lyeh: ||1 button.x = (header.w + header.x) - button.w; if (!(win->flags & NK_WINDOW_CLOSABLE)) { button.x -= style->window.header.padding.x; header.w -= style->window.header.padding.x; } header.w -= button.w + style->window.header.spacing.x; } else { button.x = header.x; button.x -= button.w * 0.25, button.w *= 0.75; //< small align hack to fit minimize+pushpin buttons more closely together header.x += button.w + style->window.header.spacing.x + style->window.header.padding.x; } if (nk_do_button_symbol(&ws, &win->buffer, button, (layout->flags & NK_WINDOW_PINNED)? NK_SYMBOL_PIN : NK_SYMBOL_FLOATING, NK_BUTTON_DEFAULT, &style->window.header.minimize_button, in, style->font) && !(win->flags & NK_WINDOW_ROM)) layout->flags = (layout->flags & NK_WINDOW_PINNED) ? (layout->flags & (nk_flags)~NK_WINDOW_PINNED): layout->flags | NK_WINDOW_PINNED; } #endif #if 1 //< @r-lyeh /* window maximize button */ if (win->flags & NK_WINDOW_MAXIMIZABLE) { nk_flags ws = 0; if (style->window.header.align == NK_HEADER_RIGHT || 1) { //< @r-lyeh: ||1 button.x = (header.w + header.x) - button.w; if (!(win->flags & NK_WINDOW_CLOSABLE)) { button.x -= style->window.header.padding.x; header.w -= style->window.header.padding.x; } header.w -= button.w + style->window.header.spacing.x; } else { button.x = header.x; button.x -= button.w * 0.25, button.w *= 0.75; //< small align hack to fit minimize+pushpin buttons more closely together header.x += button.w + style->window.header.spacing.x + style->window.header.padding.x; } if( win->is_window_resizing && layout->flags & NK_WINDOW_FULLSCREEN ) layout->flags &= (nk_flags)~NK_WINDOW_FULLSCREEN; if (nk_do_button_symbol(&ws, &win->buffer, button, (layout->flags & NK_WINDOW_FULLSCREEN)? NK_SYMBOL_RESTORE : NK_SYMBOL_FULLSCREEN, NK_BUTTON_DEFAULT, &style->window.header.minimize_button, in, style->font) && !(win->flags & NK_WINDOW_ROM) ) layout->flags = (layout->flags & NK_WINDOW_FULLSCREEN) ? (layout->flags & (nk_flags)~NK_WINDOW_FULLSCREEN): layout->flags | NK_WINDOW_FULLSCREEN, win->is_window_restoring = !(layout->flags & NK_WINDOW_FULLSCREEN); } #endif /* window minimize button */ if (win->flags & NK_WINDOW_MINIMIZABLE) { nk_flags ws = 0; if (style->window.header.align == NK_HEADER_RIGHT) { button.x = (header.w + header.x) - button.w; if (!(win->flags & NK_WINDOW_CLOSABLE)) { button.x -= style->window.header.padding.x; header.w -= style->window.header.padding.x; } header.w -= button.w + style->window.header.spacing.x; } else { button.x = header.x; header.x += button.w + style->window.header.spacing.x + style->window.header.padding.x; } if (nk_do_button_symbol(&ws, &win->buffer, button, (layout->flags & NK_WINDOW_MINIMIZED)? style->window.header.maximize_symbol: style->window.header.minimize_symbol, NK_BUTTON_DEFAULT, &style->window.header.minimize_button, in, style->font) && !(win->flags & NK_WINDOW_ROM)) layout->flags = (layout->flags & NK_WINDOW_MINIMIZED) ? (layout->flags & (nk_flags)~NK_WINDOW_MINIMIZED): (layout->flags | NK_WINDOW_MINIMIZED); } } {/* window header title */ int text_len = nk_strlen(title); struct nk_rect label = {0,0,0,0}; float t = font->width(font->userdata, font->height, title, text_len); text.padding = nk_vec2(0,0); label.x = header.x - style->window.header.padding.x; //< @r-lyeh: + -> - // label.x += style->window.header.label_padding.x; //< @r-lyeh: disabled label.y = header.y + style->window.header.label_padding.y; label.h = font->height + 2 * style->window.header.label_padding.y; label.w = t + 2 * style->window.header.spacing.x; label.w = NK_CLAMP(0, label.w, header.x + header.w - label.x); nk_widget_text(out, label, (const char*)title, text_len, &text, NK_TEXT_LEFT, font);} } /* draw window background */ if (!(layout->flags & NK_WINDOW_MINIMIZED) && !(layout->flags & NK_WINDOW_DYNAMIC)) { struct nk_rect body; body.x = win->bounds.x; body.w = win->bounds.w; body.y = (win->bounds.y + layout->header_height); body.h = (win->bounds.h - layout->header_height); switch(style->window.fixed_background.type) { case NK_STYLE_ITEM_IMAGE: nk_draw_image(out, body, &style->window.fixed_background.data.image, nk_white); break; case NK_STYLE_ITEM_NINE_SLICE: nk_draw_nine_slice(out, body, &style->window.fixed_background.data.slice, nk_white); break; case NK_STYLE_ITEM_COLOR: nk_fill_rect(out, body, 0, style->window.fixed_background.data.color); break; } } /* set clipping rectangle */ {struct nk_rect clip; layout->clip = layout->bounds; nk_unify(&clip, &win->buffer.clip, layout->clip.x, layout->clip.y, layout->clip.x + layout->clip.w, layout->clip.y + layout->clip.h); nk_push_scissor(out, clip); layout->clip = clip;} return !(layout->flags & NK_WINDOW_HIDDEN) && !(layout->flags & NK_WINDOW_MINIMIZED); } NK_LIB void nk_panel_end(struct nk_context *ctx) { struct nk_input *in; struct nk_window *window; struct nk_panel *layout; const struct nk_style *style; struct nk_command_buffer *out; struct nk_vec2 scrollbar_size; struct nk_vec2 panel_padding; NK_ASSERT(ctx); NK_ASSERT(ctx->current); NK_ASSERT(ctx->current->layout); if (!ctx || !ctx->current || !ctx->current->layout) return; #if 1 //< @r-lyeh ctx->current->is_window_resizing = 0; #endif window = ctx->current; layout = window->layout; style = &ctx->style; out = &window->buffer; in = (layout->flags & NK_WINDOW_ROM || layout->flags & NK_WINDOW_NO_INPUT) ? 0 :&ctx->input; if (!nk_panel_is_sub(layout->type)) nk_push_scissor(out, nk_null_rect); /* cache configuration data */ scrollbar_size = style->window.scrollbar_size; panel_padding = nk_panel_get_padding(style, layout->type); /* update the current cursor Y-position to point over the last added widget */ layout->at_y += layout->row.height; /* dynamic panels */ if (layout->flags & NK_WINDOW_DYNAMIC && !(layout->flags & NK_WINDOW_MINIMIZED)) { /* update panel height to fit dynamic growth */ struct nk_rect empty_space; if (layout->at_y < (layout->bounds.y + layout->bounds.h)) layout->bounds.h = layout->at_y - layout->bounds.y; /* fill top empty space */ empty_space.x = window->bounds.x; empty_space.y = layout->bounds.y; empty_space.h = panel_padding.y; empty_space.w = window->bounds.w; nk_fill_rect(out, empty_space, 0, style->window.background); /* fill left empty space */ empty_space.x = window->bounds.x; empty_space.y = layout->bounds.y; empty_space.w = panel_padding.x + layout->border; empty_space.h = layout->bounds.h; nk_fill_rect(out, empty_space, 0, style->window.background); /* fill right empty space */ empty_space.x = layout->bounds.x + layout->bounds.w; empty_space.y = layout->bounds.y; empty_space.w = panel_padding.x + layout->border; empty_space.h = layout->bounds.h; if (*layout->offset_y == 0 && !(layout->flags & NK_WINDOW_NO_SCROLLBAR_Y)) //< @r-lyeh empty_space.w += scrollbar_size.x; nk_fill_rect(out, empty_space, 0, style->window.background); /* fill bottom empty space */ if (layout->footer_height > 0) { empty_space.x = window->bounds.x; empty_space.y = layout->bounds.y + layout->bounds.h; empty_space.w = window->bounds.w; empty_space.h = layout->footer_height; nk_fill_rect(out, empty_space, 0, style->window.background); } } /* scrollbars */ if ( 1 && //< @r-lyeh !(layout->flags & NK_WINDOW_MINIMIZED) && window->scrollbar_hiding_timer < NK_SCROLLBAR_HIDING_TIMEOUT) { struct nk_rect scroll; int scroll_has_scrolling; float scroll_target; float scroll_offset; float scroll_step; float scroll_inc; /* mouse wheel scrolling */ if (nk_panel_is_sub(layout->type)) { /* sub-window mouse wheel scrolling */ struct nk_window *root_window = window; struct nk_panel *root_panel = window->layout; while (root_panel->parent) root_panel = root_panel->parent; while (root_window->parent) root_window = root_window->parent; /* only allow scrolling if parent window is active */ scroll_has_scrolling = 0; if ((root_window == ctx->active) && layout->has_scrolling) { /* and panel is being hovered and inside clip rect*/ if (nk_input_is_mouse_hovering_rect(in, layout->bounds) && NK_INTERSECT(layout->bounds.x, layout->bounds.y, layout->bounds.w, layout->bounds.h, root_panel->clip.x, root_panel->clip.y, root_panel->clip.w, root_panel->clip.h)) { /* deactivate all parent scrolling */ root_panel = window->layout; while (root_panel->parent) { root_panel->has_scrolling = nk_false; root_panel = root_panel->parent; } root_panel->has_scrolling = nk_false; scroll_has_scrolling = nk_true; } } } else if (!nk_panel_is_sub(layout->type)) { /* window mouse wheel scrolling */ scroll_has_scrolling = (window == ctx->active) && layout->has_scrolling; if (in && (in->mouse.scroll_delta.y > 0 || in->mouse.scroll_delta.x > 0) && scroll_has_scrolling) window->scrolled = nk_true; else window->scrolled = nk_false; } else scroll_has_scrolling = nk_false; if(!(layout->flags & NK_WINDOW_NO_SCROLLBAR_Y)) //< @r-lyeh { /* vertical scrollbar */ nk_flags state = 0; scroll.x = layout->bounds.x + layout->bounds.w + panel_padding.x; scroll.y = layout->bounds.y; scroll.w = scrollbar_size.x; scroll.h = layout->bounds.h; scroll_offset = (float)*layout->offset_y; scroll_step = scroll.h * 0.10f; scroll_inc = scroll.h * 0.01f; scroll_target = (float)(int)(layout->at_y - scroll.y); scroll_offset = nk_do_scrollbarv(&state, out, scroll, scroll_has_scrolling, scroll_offset, scroll_target, scroll_step, scroll_inc, &ctx->style.scrollv, in, style->font); *layout->offset_y = (nk_uint)scroll_offset; if (in && scroll_has_scrolling) in->mouse.scroll_delta.y = 0; } if(!(layout->flags & NK_WINDOW_NO_SCROLLBAR_X)) //< @r-lyeh { /* horizontal scrollbar */ nk_flags state = 0; scroll.x = layout->bounds.x; scroll.y = layout->bounds.y + layout->bounds.h; scroll.w = layout->bounds.w; scroll.h = scrollbar_size.y; scroll_offset = (float)*layout->offset_x; scroll_target = (float)(int)(layout->max_x - scroll.x); scroll_step = layout->max_x * 0.05f; scroll_inc = layout->max_x * 0.005f; scroll_offset = nk_do_scrollbarh(&state, out, scroll, scroll_has_scrolling, scroll_offset, scroll_target, scroll_step, scroll_inc, &ctx->style.scrollh, in, style->font); *layout->offset_x = (nk_uint)scroll_offset; } } /* hide scroll if no user input */ if (window->flags & NK_WINDOW_SCROLL_AUTO_HIDE) { int has_input = ctx->input.mouse.delta.x != 0 || ctx->input.mouse.delta.y != 0 || ctx->input.mouse.scroll_delta.y != 0; int is_window_hovered = nk_window_is_hovered(ctx); int any_item_active = (ctx->last_widget_state & NK_WIDGET_STATE_MODIFIED); if ((!has_input && is_window_hovered) || (!is_window_hovered && !any_item_active)) window->scrollbar_hiding_timer += ctx->delta_time_seconds; else window->scrollbar_hiding_timer = 0; } else window->scrollbar_hiding_timer = 0; /* window border */ if (layout->flags & NK_WINDOW_BORDER) { struct nk_color border_color = nk_panel_get_border_color(style, layout->type); const float padding_y = (layout->flags & NK_WINDOW_MINIMIZED) ? (style->window.border + window->bounds.y + layout->header_height) : ((layout->flags & NK_WINDOW_DYNAMIC) ? (layout->bounds.y + layout->bounds.h + layout->footer_height) : (window->bounds.y + window->bounds.h)); struct nk_rect b = window->bounds; b.h = padding_y - window->bounds.y; nk_stroke_rect(out, b, 0, layout->border, border_color); } /* scaler */ if ((layout->flags & NK_WINDOW_SCALABLE) && in && !(layout->flags & NK_WINDOW_MINIMIZED)) { /* calculate scaler bounds */ struct nk_rect scaler; scaler.w = scrollbar_size.x * 2; //< @r-lyeh x2 easier grabbing scaler.h = scrollbar_size.y * 2; //< @r-lyeh x2 easier grabbing #if 1 //< @r-lyeh: pixel perfect adjustments int scroll_has_scrolling = (window == ctx->active) && layout->has_scrolling; scaler.y = layout->bounds.y + layout->bounds.h - scaler.h + panel_padding.y; if (layout->flags & NK_WINDOW_SCALE_LEFT) scaler.x = layout->bounds.x - panel_padding.x; // + scaler.w * !!(window->flags & NK_WINDOW_NO_SCROLLBAR_Y); //< @r-lyeh else scaler.x = layout->bounds.x + layout->bounds.w - panel_padding.x - (scrollbar_size.x/2) * !(window->flags & NK_WINDOW_NO_SCROLLBAR_Y); if (layout->flags & NK_WINDOW_SCALE_TOP) scaler.y = layout->bounds.y; //< @r-lyeh: 32==title height #if 0 //< @r-lyeh: @fixme: 32 can be guessed from here if (nk_panel_has_header(win->flags, title)) { header.h = font->height + 2.0f * style->window.header.padding.y; header.h += 2.0f * style->window.header.label_padding.y; } else header.h = panel_padding.y; #endif #endif if (layout->flags & NK_WINDOW_NO_SCROLLBAR_Y) //< @r-lyeh scaler.x -= scaler.w; /* draw scaler */ {const struct nk_style_item *item = &style->window.scaler; if (item->type == NK_STYLE_ITEM_IMAGE) nk_draw_image(out, scaler, &item->data.image, nk_white); else { #if 1 //< @r-lyeh if (layout->flags & NK_WINDOW_SCALE_TOP) { if (layout->flags & NK_WINDOW_SCALE_LEFT) { nk_fill_triangle(out, scaler.x, scaler.y + scaler.h, scaler.x + scaler.w, scaler.y, scaler.x, scaler.y, item->data.color); } else { // RIGHT nk_fill_triangle(out, scaler.x, scaler.y, scaler.x + scaler.w, scaler.y, scaler.x + scaler.w, scaler.y + scaler.h, item->data.color); } } else #endif if (layout->flags & NK_WINDOW_SCALE_LEFT) { nk_fill_triangle(out, scaler.x, scaler.y, scaler.x, scaler.y + scaler.h, scaler.x + scaler.w, scaler.y + scaler.h, item->data.color); } else { nk_fill_triangle(out, scaler.x + scaler.w, scaler.y, scaler.x + scaler.w, scaler.y + scaler.h, scaler.x, scaler.y + scaler.h, item->data.color); } }} /* do window scaling */ if (!(window->flags & NK_WINDOW_ROM)) { struct nk_vec2 window_size = style->window.min_size; int left_mouse_down = in->mouse.buttons[NK_BUTTON_LEFT].down; int left_mouse_click_in_scaler = nk_input_has_mouse_click_down_in_rect(in, NK_BUTTON_LEFT, scaler, nk_true); if (left_mouse_down && left_mouse_click_in_scaler) { #if 1 //< @r-lyeh window->is_window_resizing = layout->flags & NK_WINDOW_SCALE_LEFT ? NK_WINDOW_SCALE_LEFT : 1; window->is_window_resizing |= layout->flags & NK_WINDOW_SCALE_TOP ? NK_WINDOW_SCALE_TOP : 0; #endif float delta_x = in->mouse.delta.x; float delta_y = in->mouse.delta.y; //< @r-lyeh if (layout->flags & NK_WINDOW_SCALE_LEFT) { #if 1 //< @r-lyeh: fix broken dragging on rapid mouse movements /* dragging in x-direction */ if( (window->bounds.w - delta_x) > window_size.x * 2 ) { window->bounds.x += delta_x; scaler.x += delta_x; window->bounds.w -= delta_x; } } else #endif #if 1 //< @r-lyeh if (1 /*NK_WINDOW_SCALE_RIGHT*/) { /* dragging in x-direction */ if( (window->bounds.w + delta_x) > window_size.x * 2) { window->bounds.w += delta_x; scaler.x += delta_x; } } #endif /* dragging in y-direction (only possible if static window) */ if (!(layout->flags & NK_WINDOW_DYNAMIC)) { #if 1 //< @r-lyeh if (layout->flags & NK_WINDOW_SCALE_TOP) { /* dragging in y-direction */ if (window_size.y < window->bounds.h + in->mouse.delta.y) { if ((in->mouse.delta.y < 0) || (in->mouse.delta.y > 0 && in->mouse.pos.y >= scaler.y)) { window->bounds.y += delta_y; window->bounds.h -= delta_y; scaler.y += delta_y; } } } else #endif if (window_size.y < window->bounds.h + in->mouse.delta.y) { if ((in->mouse.delta.y < 0) || (in->mouse.delta.y > 0 && in->mouse.pos.y >= scaler.y)) { window->bounds.h = window->bounds.h + in->mouse.delta.y; scaler.y += in->mouse.delta.y; } } } ctx->style.cursor_active = ctx->style.cursors[NK_CURSOR_RESIZE_TOP_RIGHT_DOWN_LEFT]; in->mouse.buttons[NK_BUTTON_LEFT].clicked_pos.x = scaler.x + scaler.w/2.0f; in->mouse.buttons[NK_BUTTON_LEFT].clicked_pos.y = scaler.y + scaler.h/2.0f; } } } if (!nk_panel_is_sub(layout->type)) { /* window is hidden so clear command buffer */ if (layout->flags & NK_WINDOW_HIDDEN) nk_command_buffer_reset(&window->buffer); /* window is visible and not tab */ else nk_finish(ctx, window); } /* NK_WINDOW_REMOVE_ROM flag was set so remove NK_WINDOW_ROM */ if (layout->flags & NK_WINDOW_REMOVE_ROM) { layout->flags &= ~(nk_flags)NK_WINDOW_ROM; layout->flags &= ~(nk_flags)NK_WINDOW_REMOVE_ROM; } window->flags = layout->flags; /* property garbage collector */ if (window->property.active && window->property.old != window->property.seq && window->property.active == window->property.prev) { nk_zero(&window->property, sizeof(window->property)); } else { window->property.old = window->property.seq; window->property.prev = window->property.active; window->property.seq = 0; } /* edit garbage collector */ if (window->edit.active && window->edit.old != window->edit.seq && window->edit.active == window->edit.prev) { nk_zero(&window->edit, sizeof(window->edit)); } else { window->edit.old = window->edit.seq; window->edit.prev = window->edit.active; window->edit.seq = 0; } /* contextual garbage collector */ if (window->popup.active_con && window->popup.con_old != window->popup.con_count) { window->popup.con_count = 0; window->popup.con_old = 0; window->popup.active_con = 0; } else { window->popup.con_old = window->popup.con_count; window->popup.con_count = 0; } window->popup.combo_count = 0; /* helper to make sure you have a 'nk_tree_push' for every 'nk_tree_pop' */ NK_ASSERT(!layout->row.tree_depth); } /* =============================================================== * * WINDOW * * ===============================================================*/ NK_LIB void* nk_create_window(struct nk_context *ctx) { struct nk_page_element *elem; elem = nk_create_page_element(ctx); if (!elem) return 0; elem->data.win.seq = ctx->seq; return &elem->data.win; } NK_LIB void nk_free_window(struct nk_context *ctx, struct nk_window *win) { /* unlink windows from list */ struct nk_table *it = win->tables; if (win->popup.win) { nk_free_window(ctx, win->popup.win); win->popup.win = 0; } win->next = 0; win->prev = 0; while (it) { /*free window state tables */ struct nk_table *n = it->next; nk_remove_table(win, it); nk_free_table(ctx, it); if (it == win->tables) win->tables = n; it = n; } /* link windows into freelist */ {union nk_page_data *pd = NK_CONTAINER_OF(win, union nk_page_data, win); struct nk_page_element *pe = NK_CONTAINER_OF(pd, struct nk_page_element, data); nk_free_page_element(ctx, pe);} } NK_LIB struct nk_window* nk_find_window(struct nk_context *ctx, nk_hash hash, const char *name) { struct nk_window *iter; iter = ctx->begin; while (iter) { NK_ASSERT(iter != iter->next); if (iter->name == hash) { int max_len = nk_strlen(iter->name_string); if (!nk_stricmpn(iter->name_string, name, max_len)) return iter; } iter = iter->next; } return 0; } NK_LIB void nk_insert_window(struct nk_context *ctx, struct nk_window *win, enum nk_window_insert_location loc) { const struct nk_window *iter; NK_ASSERT(ctx); NK_ASSERT(win); if (!win || !ctx) return; iter = ctx->begin; while (iter) { NK_ASSERT(iter != iter->next); NK_ASSERT(iter != win); if (iter == win) return; iter = iter->next; } if (!ctx->begin) { win->next = 0; win->prev = 0; ctx->begin = win; ctx->end = win; ctx->count = 1; return; } if (loc == NK_INSERT_BACK) { struct nk_window *end; end = ctx->end; end->flags |= NK_WINDOW_ROM; end->next = win; win->prev = ctx->end; win->next = 0; ctx->end = win; ctx->active = ctx->end; ctx->end->flags &= ~(nk_flags)NK_WINDOW_ROM; } else { /*ctx->end->flags |= NK_WINDOW_ROM;*/ ctx->begin->prev = win; win->next = ctx->begin; win->prev = 0; ctx->begin = win; ctx->begin->flags &= ~(nk_flags)NK_WINDOW_ROM; } ctx->count++; } NK_LIB void nk_remove_window(struct nk_context *ctx, struct nk_window *win) { if (win == ctx->begin || win == ctx->end) { if (win == ctx->begin) { ctx->begin = win->next; if (win->next) win->next->prev = 0; } if (win == ctx->end) { ctx->end = win->prev; if (win->prev) win->prev->next = 0; } } else { if (win->next) win->next->prev = win->prev; if (win->prev) win->prev->next = win->next; } if (win == ctx->active || !ctx->active) { ctx->active = ctx->end; if (ctx->end) ctx->end->flags &= ~(nk_flags)NK_WINDOW_ROM; } win->next = 0; win->prev = 0; ctx->count--; } NK_API nk_bool nk_begin(struct nk_context *ctx, const char *title, struct nk_rect bounds, nk_flags flags) { return nk_begin_titled(ctx, title, title, bounds, flags); } NK_API nk_bool nk_begin_titled(struct nk_context *ctx, const char *name, const char *title, struct nk_rect bounds, nk_flags flags) { struct nk_window *win; struct nk_style *style; nk_hash name_hash; int name_len; int ret = 0; NK_ASSERT(ctx); NK_ASSERT(name); NK_ASSERT(title); NK_ASSERT(ctx->style.font && ctx->style.font->width && "if this triggers you forgot to add a font"); NK_ASSERT(!ctx->current && "if this triggers you missed a `nk_end` call"); if (!ctx || ctx->current || !title || !name) return 0; /* find or create window */ style = &ctx->style; name_len = (int)nk_strlen(name); name_hash = nk_murmur_hash(name, (int)name_len, NK_WINDOW_TITLE); win = nk_find_window(ctx, name_hash, name); if (!win) { /* create new window */ nk_size name_length = (nk_size)name_len; win = (struct nk_window*)nk_create_window(ctx); NK_ASSERT(win); if (!win) return 0; if (flags & NK_WINDOW_BACKGROUND) nk_insert_window(ctx, win, NK_INSERT_FRONT); else nk_insert_window(ctx, win, NK_INSERT_BACK); nk_command_buffer_init(&win->buffer, &ctx->memory, NK_CLIPPING_ON); win->flags = flags; win->bounds = bounds; win->name = name_hash; name_length = NK_MIN(name_length, NK_WINDOW_MAX_NAME-1); NK_MEMCPY(win->name_string, name, name_length); win->name_string[name_length] = 0; win->popup.win = 0; if (!ctx->active) ctx->active = win; } else { /* update window */ win->flags &= ~(nk_flags)(NK_WINDOW_PRIVATE-1); win->flags |= flags; if (!(win->flags & (NK_WINDOW_MOVABLE | NK_WINDOW_SCALABLE))) win->bounds = bounds; /* If this assert triggers you either: * * I.) Have more than one window with the same name or * II.) You forgot to actually draw the window. * More specific you did not call `nk_clear` (nk_clear will be * automatically called for you if you are using one of the * provided demo backends). */ NK_ASSERT(win->seq != ctx->seq); win->seq = ctx->seq; if (!ctx->active && !(win->flags & NK_WINDOW_HIDDEN)) { ctx->active = win; ctx->end = win; } } if (win->flags & NK_WINDOW_HIDDEN) { ctx->current = win; win->layout = 0; return 0; } else nk_start(ctx, win); /* window overlapping */ if (!(win->flags & NK_WINDOW_HIDDEN) && !(win->flags & NK_WINDOW_NO_INPUT)) { int inpanel, ishovered; struct nk_window *iter = win; float h = ctx->style.font->height + 2.0f * style->window.header.padding.y + (2.0f * style->window.header.label_padding.y); struct nk_rect win_bounds = (!(win->flags & NK_WINDOW_MINIMIZED))? win->bounds: nk_rect(win->bounds.x, win->bounds.y, win->bounds.w, h); /* activate window if hovered and no other window is overlapping this window */ inpanel = nk_input_has_mouse_click_down_in_rect(&ctx->input, NK_BUTTON_LEFT, win_bounds, nk_true); inpanel = inpanel && ctx->input.mouse.buttons[NK_BUTTON_LEFT].clicked; ishovered = nk_input_is_mouse_hovering_rect(&ctx->input, win_bounds); if ((win != ctx->active) && ishovered && !ctx->input.mouse.buttons[NK_BUTTON_LEFT].down) { iter = win->next; while (iter) { struct nk_rect iter_bounds = (!(iter->flags & NK_WINDOW_MINIMIZED))? iter->bounds: nk_rect(iter->bounds.x, iter->bounds.y, iter->bounds.w, h); if (NK_INTERSECT(win_bounds.x, win_bounds.y, win_bounds.w, win_bounds.h, iter_bounds.x, iter_bounds.y, iter_bounds.w, iter_bounds.h) && (!(iter->flags & NK_WINDOW_HIDDEN))) break; if (iter->popup.win && iter->popup.active && !(iter->flags & NK_WINDOW_HIDDEN) && NK_INTERSECT(win->bounds.x, win_bounds.y, win_bounds.w, win_bounds.h, iter->popup.win->bounds.x, iter->popup.win->bounds.y, iter->popup.win->bounds.w, iter->popup.win->bounds.h)) break; iter = iter->next; } } /* activate window if clicked */ if (iter && inpanel && (win != ctx->end)) { iter = win->next; while (iter) { /* try to find a panel with higher priority in the same position */ struct nk_rect iter_bounds = (!(iter->flags & NK_WINDOW_MINIMIZED))? iter->bounds: nk_rect(iter->bounds.x, iter->bounds.y, iter->bounds.w, h); if (NK_INBOX(ctx->input.mouse.pos.x, ctx->input.mouse.pos.y, iter_bounds.x, iter_bounds.y, iter_bounds.w, iter_bounds.h) && !(iter->flags & NK_WINDOW_HIDDEN)) break; if (iter->popup.win && iter->popup.active && !(iter->flags & NK_WINDOW_HIDDEN) && NK_INTERSECT(win_bounds.x, win_bounds.y, win_bounds.w, win_bounds.h, iter->popup.win->bounds.x, iter->popup.win->bounds.y, iter->popup.win->bounds.w, iter->popup.win->bounds.h)) break; iter = iter->next; } } if (iter && !(win->flags & NK_WINDOW_ROM) && (win->flags & NK_WINDOW_BACKGROUND)) { win->flags |= (nk_flags)NK_WINDOW_ROM; iter->flags &= ~(nk_flags)NK_WINDOW_ROM; ctx->active = iter; if (!(iter->flags & NK_WINDOW_BACKGROUND)) { /* current window is active in that position so transfer to top * at the highest priority in stack */ nk_remove_window(ctx, iter); nk_insert_window(ctx, iter, NK_INSERT_BACK); } } else { if (!iter && ctx->end != win) { if (!(win->flags & NK_WINDOW_BACKGROUND)) { /* current window is active in that position so transfer to top * at the highest priority in stack */ nk_remove_window(ctx, win); nk_insert_window(ctx, win, NK_INSERT_BACK); } win->flags &= ~(nk_flags)NK_WINDOW_ROM; ctx->active = win; } if (ctx->end != win && !(win->flags & NK_WINDOW_BACKGROUND)) win->flags |= NK_WINDOW_ROM; } } win->layout = (struct nk_panel*)nk_create_panel(ctx); ctx->current = win; ret = nk_panel_begin(ctx, title, NK_PANEL_WINDOW); win->layout->offset_x = &win->scrollbar.x; win->layout->offset_y = &win->scrollbar.y; return ret; } NK_API void nk_end(struct nk_context *ctx) { struct nk_panel *layout; NK_ASSERT(ctx); NK_ASSERT(ctx->current && "if this triggers you forgot to call `nk_begin`"); if (!ctx || !ctx->current) return; layout = ctx->current->layout; if (!layout || (layout->type == NK_PANEL_WINDOW && (ctx->current->flags & NK_WINDOW_HIDDEN))) { ctx->current = 0; return; } nk_panel_end(ctx); nk_free_panel(ctx, ctx->current->layout); ctx->current = 0; } NK_API struct nk_rect nk_window_get_bounds(const struct nk_context *ctx) { NK_ASSERT(ctx); NK_ASSERT(ctx->current); if (!ctx || !ctx->current) return nk_rect(0,0,0,0); return ctx->current->bounds; } NK_API struct nk_vec2 nk_window_get_position(const struct nk_context *ctx) { NK_ASSERT(ctx); NK_ASSERT(ctx->current); if (!ctx || !ctx->current) return nk_vec2(0,0); return nk_vec2(ctx->current->bounds.x, ctx->current->bounds.y); } NK_API struct nk_vec2 nk_window_get_size(const struct nk_context *ctx) { NK_ASSERT(ctx); NK_ASSERT(ctx->current); if (!ctx || !ctx->current) return nk_vec2(0,0); return nk_vec2(ctx->current->bounds.w, ctx->current->bounds.h); } NK_API float nk_window_get_width(const struct nk_context *ctx) { NK_ASSERT(ctx); NK_ASSERT(ctx->current); if (!ctx || !ctx->current) return 0; return ctx->current->bounds.w; } NK_API float nk_window_get_height(const struct nk_context *ctx) { NK_ASSERT(ctx); NK_ASSERT(ctx->current); if (!ctx || !ctx->current) return 0; return ctx->current->bounds.h; } NK_API struct nk_rect nk_window_get_content_region(struct nk_context *ctx) { NK_ASSERT(ctx); NK_ASSERT(ctx->current); if (!ctx || !ctx->current) return nk_rect(0,0,0,0); return ctx->current->layout->clip; } NK_API struct nk_vec2 nk_window_get_content_region_min(struct nk_context *ctx) { NK_ASSERT(ctx); NK_ASSERT(ctx->current); NK_ASSERT(ctx->current->layout); if (!ctx || !ctx->current) return nk_vec2(0,0); return nk_vec2(ctx->current->layout->clip.x, ctx->current->layout->clip.y); } NK_API struct nk_vec2 nk_window_get_content_region_max(struct nk_context *ctx) { NK_ASSERT(ctx); NK_ASSERT(ctx->current); NK_ASSERT(ctx->current->layout); if (!ctx || !ctx->current) return nk_vec2(0,0); return nk_vec2(ctx->current->layout->clip.x + ctx->current->layout->clip.w, ctx->current->layout->clip.y + ctx->current->layout->clip.h); } NK_API struct nk_vec2 nk_window_get_content_region_size(struct nk_context *ctx) { NK_ASSERT(ctx); NK_ASSERT(ctx->current); NK_ASSERT(ctx->current->layout); if (!ctx || !ctx->current) return nk_vec2(0,0); return nk_vec2(ctx->current->layout->clip.w, ctx->current->layout->clip.h); } NK_API struct nk_command_buffer* nk_window_get_canvas(struct nk_context *ctx) { NK_ASSERT(ctx); NK_ASSERT(ctx->current); NK_ASSERT(ctx->current->layout); if (!ctx || !ctx->current) return 0; return &ctx->current->buffer; } NK_API struct nk_panel* nk_window_get_panel(struct nk_context *ctx) { NK_ASSERT(ctx); NK_ASSERT(ctx->current); if (!ctx || !ctx->current) return 0; return ctx->current->layout; } NK_API void nk_window_get_scroll(struct nk_context *ctx, nk_uint *offset_x, nk_uint *offset_y) { struct nk_window *win; NK_ASSERT(ctx); NK_ASSERT(ctx->current); if (!ctx || !ctx->current) return ; win = ctx->current; if (offset_x) *offset_x = win->scrollbar.x; if (offset_y) *offset_y = win->scrollbar.y; } NK_API nk_bool nk_window_has_focus(const struct nk_context *ctx) { NK_ASSERT(ctx); NK_ASSERT(ctx->current); NK_ASSERT(ctx->current->layout); if (!ctx || !ctx->current) return 0; return ctx->current == ctx->active; } NK_API nk_bool nk_window_is_hovered(struct nk_context *ctx) { NK_ASSERT(ctx); NK_ASSERT(ctx->current); if (!ctx || !ctx->current || (ctx->current->flags & NK_WINDOW_HIDDEN)) return 0; else { struct nk_rect actual_bounds = ctx->current->bounds; if (ctx->begin->flags & NK_WINDOW_MINIMIZED) { actual_bounds.h = ctx->current->layout->header_height; } return nk_input_is_mouse_hovering_rect(&ctx->input, actual_bounds); } } NK_API nk_bool nk_window_is_any_hovered(struct nk_context *ctx) { struct nk_window *iter; NK_ASSERT(ctx); if (!ctx) return 0; iter = ctx->begin; while (iter) { /* check if window is being hovered */ if(!(iter->flags & NK_WINDOW_HIDDEN)) { /* check if window popup is being hovered */ if (iter->popup.active && iter->popup.win && nk_input_is_mouse_hovering_rect(&ctx->input, iter->popup.win->bounds)) return 1; if (iter->flags & NK_WINDOW_MINIMIZED) { struct nk_rect header = iter->bounds; header.h = ctx->style.font->height + 2 * ctx->style.window.header.padding.y; if (nk_input_is_mouse_hovering_rect(&ctx->input, header)) return 1; } else if (nk_input_is_mouse_hovering_rect(&ctx->input, iter->bounds)) { return 1; } } iter = iter->next; } return 0; } NK_API nk_bool nk_item_is_any_active(struct nk_context *ctx) { int any_hovered = nk_window_is_any_hovered(ctx); int any_active = (ctx->last_widget_state & NK_WIDGET_STATE_MODIFIED); return any_hovered || any_active; } NK_API nk_bool nk_window_is_collapsed(struct nk_context *ctx, const char *name) { int title_len; nk_hash title_hash; struct nk_window *win; NK_ASSERT(ctx); if (!ctx) return 0; title_len = (int)nk_strlen(name); title_hash = nk_murmur_hash(name, (int)title_len, NK_WINDOW_TITLE); win = nk_find_window(ctx, title_hash, name); if (!win) return 0; return win->flags & NK_WINDOW_MINIMIZED; } NK_API nk_bool nk_window_is_closed(struct nk_context *ctx, const char *name) { int title_len; nk_hash title_hash; struct nk_window *win; NK_ASSERT(ctx); if (!ctx) return 1; title_len = (int)nk_strlen(name); title_hash = nk_murmur_hash(name, (int)title_len, NK_WINDOW_TITLE); win = nk_find_window(ctx, title_hash, name); if (!win) return 1; return (win->flags & NK_WINDOW_CLOSED); } NK_API nk_bool nk_window_is_hidden(struct nk_context *ctx, const char *name) { int title_len; nk_hash title_hash; struct nk_window *win; NK_ASSERT(ctx); if (!ctx) return 1; title_len = (int)nk_strlen(name); title_hash = nk_murmur_hash(name, (int)title_len, NK_WINDOW_TITLE); win = nk_find_window(ctx, title_hash, name); if (!win) return 1; return (win->flags & NK_WINDOW_HIDDEN); } NK_API nk_bool nk_window_is_active(struct nk_context *ctx, const char *name) { int title_len; nk_hash title_hash; struct nk_window *win; NK_ASSERT(ctx); if (!ctx) return 0; title_len = (int)nk_strlen(name); title_hash = nk_murmur_hash(name, (int)title_len, NK_WINDOW_TITLE); win = nk_find_window(ctx, title_hash, name); if (!win) return 0; return win == ctx->active; } NK_API struct nk_window* nk_window_find(struct nk_context *ctx, const char *name) { int title_len; nk_hash title_hash; title_len = (int)nk_strlen(name); title_hash = nk_murmur_hash(name, (int)title_len, NK_WINDOW_TITLE); return nk_find_window(ctx, title_hash, name); } NK_API void nk_window_close(struct nk_context *ctx, const char *name) { struct nk_window *win; NK_ASSERT(ctx); if (!ctx) return; win = nk_window_find(ctx, name); if (!win) return; NK_ASSERT(ctx->current != win && "You cannot close a currently active window"); if (ctx->current == win) return; win->flags |= NK_WINDOW_HIDDEN; win->flags |= NK_WINDOW_CLOSED; } NK_API void nk_window_set_bounds(struct nk_context *ctx, const char *name, struct nk_rect bounds) { struct nk_window *win; NK_ASSERT(ctx); if (!ctx) return; win = nk_window_find(ctx, name); if (!win) return; NK_ASSERT(ctx->current != win && "You cannot update a currently in procecss window"); win->bounds = bounds; } NK_API void nk_window_set_position(struct nk_context *ctx, const char *name, struct nk_vec2 pos) { struct nk_window *win = nk_window_find(ctx, name); if (!win) return; win->bounds.x = pos.x; win->bounds.y = pos.y; } NK_API void nk_window_set_size(struct nk_context *ctx, const char *name, struct nk_vec2 size) { struct nk_window *win = nk_window_find(ctx, name); if (!win) return; win->bounds.w = size.x; win->bounds.h = size.y; } NK_API void nk_window_set_scroll(struct nk_context *ctx, nk_uint offset_x, nk_uint offset_y) { struct nk_window *win; NK_ASSERT(ctx); NK_ASSERT(ctx->current); if (!ctx || !ctx->current) return; win = ctx->current; win->scrollbar.x = offset_x; win->scrollbar.y = offset_y; } NK_API void nk_window_collapse(struct nk_context *ctx, const char *name, enum nk_collapse_states c) { int title_len; nk_hash title_hash; struct nk_window *win; NK_ASSERT(ctx); if (!ctx) return; title_len = (int)nk_strlen(name); title_hash = nk_murmur_hash(name, (int)title_len, NK_WINDOW_TITLE); win = nk_find_window(ctx, title_hash, name); if (!win) return; if (c == NK_MINIMIZED) win->flags |= NK_WINDOW_MINIMIZED; else win->flags &= ~(nk_flags)NK_WINDOW_MINIMIZED; } NK_API void nk_window_collapse_if(struct nk_context *ctx, const char *name, enum nk_collapse_states c, int cond) { NK_ASSERT(ctx); if (!ctx || !cond) return; nk_window_collapse(ctx, name, c); } NK_API void nk_window_show(struct nk_context *ctx, const char *name, enum nk_show_states s) { int title_len; nk_hash title_hash; struct nk_window *win; NK_ASSERT(ctx); if (!ctx) return; title_len = (int)nk_strlen(name); title_hash = nk_murmur_hash(name, (int)title_len, NK_WINDOW_TITLE); win = nk_find_window(ctx, title_hash, name); if (!win) return; if (s == NK_HIDDEN) { win->flags |= NK_WINDOW_HIDDEN; } else win->flags &= ~(nk_flags)NK_WINDOW_HIDDEN; } NK_API void nk_window_show_if(struct nk_context *ctx, const char *name, enum nk_show_states s, int cond) { NK_ASSERT(ctx); if (!ctx || !cond) return; nk_window_show(ctx, name, s); } NK_API void nk_window_set_focus(struct nk_context *ctx, const char *name) { int title_len; nk_hash title_hash; struct nk_window *win; NK_ASSERT(ctx); if (!ctx) return; title_len = (int)nk_strlen(name); title_hash = nk_murmur_hash(name, (int)title_len, NK_WINDOW_TITLE); win = nk_find_window(ctx, title_hash, name); if (win && ctx->end != win) { nk_remove_window(ctx, win); nk_insert_window(ctx, win, NK_INSERT_BACK); } ctx->active = win; } /* =============================================================== * * POPUP * * ===============================================================*/ NK_API nk_bool nk_popup_begin(struct nk_context *ctx, enum nk_popup_type type, const char *title, nk_flags flags, struct nk_rect rect) { struct nk_window *popup; struct nk_window *win; struct nk_panel *panel; int title_len; nk_hash title_hash; nk_size allocated; NK_ASSERT(ctx); NK_ASSERT(title); NK_ASSERT(ctx->current); NK_ASSERT(ctx->current->layout); if (!ctx || !ctx->current || !ctx->current->layout) return 0; win = ctx->current; panel = win->layout; NK_ASSERT(!(panel->type & NK_PANEL_SET_POPUP) && "popups are not allowed to have popups"); (void)panel; title_len = (int)nk_strlen(title); title_hash = nk_murmur_hash(title, (int)title_len, NK_PANEL_POPUP); popup = win->popup.win; if (!popup) { popup = (struct nk_window*)nk_create_window(ctx); popup->parent = win; win->popup.win = popup; win->popup.active = 0; win->popup.type = NK_PANEL_POPUP; } /* make sure we have correct popup */ if (win->popup.name != title_hash) { if (!win->popup.active) { nk_zero(popup, sizeof(*popup)); win->popup.name = title_hash; win->popup.active = 1; win->popup.type = NK_PANEL_POPUP; } else return 0; } /* popup position is local to window */ ctx->current = popup; rect.x += win->layout->clip.x; rect.y += win->layout->clip.y; /* setup popup data */ popup->parent = win; popup->bounds = rect; popup->seq = ctx->seq; popup->layout = (struct nk_panel*)nk_create_panel(ctx); popup->flags = flags; popup->flags |= NK_WINDOW_BORDER; if (type == NK_POPUP_DYNAMIC) popup->flags |= NK_WINDOW_DYNAMIC; popup->buffer = win->buffer; nk_start_popup(ctx, win); allocated = ctx->memory.allocated; nk_push_scissor(&popup->buffer, nk_null_rect); if (nk_panel_begin(ctx, title, NK_PANEL_POPUP)) { /* popup is running therefore invalidate parent panels */ struct nk_panel *root; root = win->layout; while (root) { root->flags |= NK_WINDOW_ROM; root->flags &= ~(nk_flags)NK_WINDOW_REMOVE_ROM; root = root->parent; } win->popup.active = 1; popup->layout->offset_x = &popup->scrollbar.x; popup->layout->offset_y = &popup->scrollbar.y; popup->layout->parent = win->layout; return 1; } else { /* popup was closed/is invalid so cleanup */ struct nk_panel *root; root = win->layout; while (root) { root->flags |= NK_WINDOW_REMOVE_ROM; root = root->parent; } win->popup.buf.active = 0; win->popup.active = 0; ctx->memory.allocated = allocated; ctx->current = win; nk_free_panel(ctx, popup->layout); popup->layout = 0; return 0; } } NK_LIB nk_bool nk_nonblock_begin(struct nk_context *ctx, nk_flags flags, struct nk_rect body, struct nk_rect header, enum nk_panel_type panel_type) { struct nk_window *popup; struct nk_window *win; struct nk_panel *panel; int is_active = nk_true; NK_ASSERT(ctx); NK_ASSERT(ctx->current); NK_ASSERT(ctx->current->layout); if (!ctx || !ctx->current || !ctx->current->layout) return 0; /* popups cannot have popups */ win = ctx->current; panel = win->layout; NK_ASSERT(!(panel->type & NK_PANEL_SET_POPUP)); (void)panel; popup = win->popup.win; if (!popup) { /* create window for nonblocking popup */ popup = (struct nk_window*)nk_create_window(ctx); popup->parent = win; win->popup.win = popup; win->popup.type = panel_type; nk_command_buffer_init(&popup->buffer, &ctx->memory, NK_CLIPPING_ON); } else { /* close the popup if user pressed outside or in the header */ int pressed, in_body, in_header; #ifdef NK_BUTTON_TRIGGER_ON_RELEASE pressed = nk_input_is_mouse_released(&ctx->input, NK_BUTTON_LEFT); #else pressed = nk_input_is_mouse_pressed(&ctx->input, NK_BUTTON_LEFT); #endif in_body = nk_input_is_mouse_hovering_rect(&ctx->input, body); in_header = nk_input_is_mouse_hovering_rect(&ctx->input, header); if (pressed && (!in_body || in_header)) is_active = nk_false; } win->popup.header = header; if (!is_active) { /* remove read only mode from all parent panels */ struct nk_panel *root = win->layout; while (root) { root->flags |= NK_WINDOW_REMOVE_ROM; root = root->parent; } return is_active; } popup->bounds = body; popup->parent = win; popup->layout = (struct nk_panel*)nk_create_panel(ctx); popup->flags = flags; popup->flags |= NK_WINDOW_BORDER; popup->flags |= NK_WINDOW_DYNAMIC; popup->seq = ctx->seq; win->popup.active = 1; NK_ASSERT(popup->layout); nk_start_popup(ctx, win); popup->buffer = win->buffer; nk_push_scissor(&popup->buffer, nk_null_rect); ctx->current = popup; nk_panel_begin(ctx, 0, panel_type); win->buffer = popup->buffer; popup->layout->parent = win->layout; popup->layout->offset_x = &popup->scrollbar.x; popup->layout->offset_y = &popup->scrollbar.y; /* set read only mode to all parent panels */ {struct nk_panel *root; root = win->layout; while (root) { root->flags |= NK_WINDOW_ROM; root = root->parent; }} return is_active; } NK_API void nk_popup_close(struct nk_context *ctx) { struct nk_window *popup; NK_ASSERT(ctx); if (!ctx || !ctx->current) return; popup = ctx->current; NK_ASSERT(popup->parent); NK_ASSERT(popup->layout->type & NK_PANEL_SET_POPUP); popup->flags |= NK_WINDOW_HIDDEN; } NK_API void nk_popup_end(struct nk_context *ctx) { struct nk_window *win; struct nk_window *popup; NK_ASSERT(ctx); NK_ASSERT(ctx->current); NK_ASSERT(ctx->current->layout); if (!ctx || !ctx->current || !ctx->current->layout) return; popup = ctx->current; if (!popup->parent) return; win = popup->parent; if (popup->flags & NK_WINDOW_HIDDEN) { struct nk_panel *root; root = win->layout; while (root) { root->flags |= NK_WINDOW_REMOVE_ROM; root = root->parent; } win->popup.active = 0; } nk_push_scissor(&popup->buffer, nk_null_rect); nk_end(ctx); win->buffer = popup->buffer; nk_finish_popup(ctx, win); ctx->current = win; nk_push_scissor(&win->buffer, win->layout->clip); } NK_API void nk_popup_get_scroll(struct nk_context *ctx, nk_uint *offset_x, nk_uint *offset_y) { struct nk_window *popup; NK_ASSERT(ctx); NK_ASSERT(ctx->current); NK_ASSERT(ctx->current->layout); if (!ctx || !ctx->current || !ctx->current->layout) return; popup = ctx->current; if (offset_x) *offset_x = popup->scrollbar.x; if (offset_y) *offset_y = popup->scrollbar.y; } NK_API void nk_popup_set_scroll(struct nk_context *ctx, nk_uint offset_x, nk_uint offset_y) { struct nk_window *popup; NK_ASSERT(ctx); NK_ASSERT(ctx->current); NK_ASSERT(ctx->current->layout); if (!ctx || !ctx->current || !ctx->current->layout) return; popup = ctx->current; popup->scrollbar.x = offset_x; popup->scrollbar.y = offset_y; } /* ============================================================== * * CONTEXTUAL * * ===============================================================*/ NK_API nk_bool nk_contextual_begin(struct nk_context *ctx, nk_flags flags, struct nk_vec2 size, struct nk_rect trigger_bounds) { struct nk_window *win; struct nk_window *popup; struct nk_rect body; NK_STORAGE const struct nk_rect null_rect = {-1,-1,0,0}; int is_clicked = 0; int is_open = 0; int ret = 0; NK_ASSERT(ctx); NK_ASSERT(ctx->current); NK_ASSERT(ctx->current->layout); if (!ctx || !ctx->current || !ctx->current->layout) return 0; win = ctx->current; ++win->popup.con_count; if (ctx->current != ctx->active) return 0; /* check if currently active contextual is active */ popup = win->popup.win; is_open = (popup && win->popup.type == NK_PANEL_CONTEXTUAL); is_clicked = nk_input_mouse_clicked(&ctx->input, NK_BUTTON_RIGHT, trigger_bounds); if (win->popup.active_con && win->popup.con_count != win->popup.active_con) return 0; if (!is_open && win->popup.active_con) win->popup.active_con = 0; if ((!is_open && !is_clicked)) return 0; /* calculate contextual position on click */ win->popup.active_con = win->popup.con_count; if (is_clicked) { body.x = ctx->input.mouse.pos.x; body.y = ctx->input.mouse.pos.y; } else { body.x = popup->bounds.x; body.y = popup->bounds.y; } body.w = size.x; body.h = size.y; /* start nonblocking contextual popup */ ret = nk_nonblock_begin(ctx, flags|NK_WINDOW_NO_SCROLLBAR, body, null_rect, NK_PANEL_CONTEXTUAL); if (ret) win->popup.type = NK_PANEL_CONTEXTUAL; else { win->popup.active_con = 0; win->popup.type = NK_PANEL_NONE; if (win->popup.win) win->popup.win->flags = 0; } return ret; } NK_API nk_bool nk_contextual_item_text(struct nk_context *ctx, const char *text, int len, nk_flags alignment) { struct nk_window *win; const struct nk_input *in; const struct nk_style *style; struct nk_rect bounds; enum nk_widget_layout_states state; NK_ASSERT(ctx); NK_ASSERT(ctx->current); NK_ASSERT(ctx->current->layout); if (!ctx || !ctx->current || !ctx->current->layout) return 0; win = ctx->current; style = &ctx->style; state = nk_widget_fitting(&bounds, ctx, style->contextual_button.padding); if (!state) return nk_false; in = (state == NK_WIDGET_ROM || win->layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input; if (nk_do_button_text(&ctx->last_widget_state, &win->buffer, bounds, text, len, alignment, NK_BUTTON_DEFAULT, &style->contextual_button, in, style->font)) { nk_contextual_close(ctx); return nk_true; } return nk_false; } NK_API nk_bool nk_contextual_item_label(struct nk_context *ctx, const char *label, nk_flags align) { return nk_contextual_item_text(ctx, label, nk_strlen(label), align); } NK_API nk_bool nk_contextual_item_image_text(struct nk_context *ctx, struct nk_image img, const char *text, int len, nk_flags align) { struct nk_window *win; const struct nk_input *in; const struct nk_style *style; struct nk_rect bounds; enum nk_widget_layout_states state; NK_ASSERT(ctx); NK_ASSERT(ctx->current); NK_ASSERT(ctx->current->layout); if (!ctx || !ctx->current || !ctx->current->layout) return 0; win = ctx->current; style = &ctx->style; state = nk_widget_fitting(&bounds, ctx, style->contextual_button.padding); if (!state) return nk_false; in = (state == NK_WIDGET_ROM || win->layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input; if (nk_do_button_text_image(&ctx->last_widget_state, &win->buffer, bounds, img, text, len, align, NK_BUTTON_DEFAULT, &style->contextual_button, style->font, in)){ nk_contextual_close(ctx); return nk_true; } return nk_false; } NK_API nk_bool nk_contextual_item_image_label(struct nk_context *ctx, struct nk_image img, const char *label, nk_flags align) { return nk_contextual_item_image_text(ctx, img, label, nk_strlen(label), align); } NK_API nk_bool nk_contextual_item_symbol_text(struct nk_context *ctx, enum nk_symbol_type symbol, const char *text, int len, nk_flags align) { struct nk_window *win; const struct nk_input *in; const struct nk_style *style; struct nk_rect bounds; enum nk_widget_layout_states state; NK_ASSERT(ctx); NK_ASSERT(ctx->current); NK_ASSERT(ctx->current->layout); if (!ctx || !ctx->current || !ctx->current->layout) return 0; win = ctx->current; style = &ctx->style; state = nk_widget_fitting(&bounds, ctx, style->contextual_button.padding); if (!state) return nk_false; in = (state == NK_WIDGET_ROM || win->layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input; if (nk_do_button_text_symbol(&ctx->last_widget_state, &win->buffer, bounds, symbol, text, len, align, NK_BUTTON_DEFAULT, &style->contextual_button, style->font, in)) { nk_contextual_close(ctx); return nk_true; } return nk_false; } NK_API nk_bool nk_contextual_item_symbol_label(struct nk_context *ctx, enum nk_symbol_type symbol, const char *text, nk_flags align) { return nk_contextual_item_symbol_text(ctx, symbol, text, nk_strlen(text), align); } NK_API void nk_contextual_close(struct nk_context *ctx) { NK_ASSERT(ctx); NK_ASSERT(ctx->current); NK_ASSERT(ctx->current->layout); if (!ctx || !ctx->current || !ctx->current->layout) return; nk_popup_close(ctx); } NK_API void nk_contextual_end(struct nk_context *ctx) { struct nk_window *popup; struct nk_panel *panel; NK_ASSERT(ctx); NK_ASSERT(ctx->current); if (!ctx || !ctx->current) return; popup = ctx->current; panel = popup->layout; NK_ASSERT(popup->parent); NK_ASSERT(panel->type & NK_PANEL_SET_POPUP); if (panel->flags & NK_WINDOW_DYNAMIC) { /* Close behavior This is a bit of a hack solution since we do not know before we end our popup how big it will be. We therefore do not directly know when a click outside the non-blocking popup must close it at that direct frame. Instead it will be closed in the next frame.*/ struct nk_rect body = {0,0,0,0}; if (panel->at_y < (panel->bounds.y + panel->bounds.h)) { struct nk_vec2 padding = nk_panel_get_padding(&ctx->style, panel->type); body = panel->bounds; body.y = (panel->at_y + panel->footer_height + panel->border + padding.y + panel->row.height); body.h = (panel->bounds.y + panel->bounds.h) - body.y; } {int pressed = nk_input_is_mouse_pressed(&ctx->input, NK_BUTTON_LEFT); int in_body = nk_input_is_mouse_hovering_rect(&ctx->input, body); if (pressed && in_body) popup->flags |= NK_WINDOW_HIDDEN; } } if (popup->flags & NK_WINDOW_HIDDEN) popup->seq = 0; nk_popup_end(ctx); return; } /* =============================================================== * * MENU * * ===============================================================*/ NK_API void nk_menubar_begin(struct nk_context *ctx) { struct nk_panel *layout; NK_ASSERT(ctx); NK_ASSERT(ctx->current); NK_ASSERT(ctx->current->layout); if (!ctx || !ctx->current || !ctx->current->layout) return; layout = ctx->current->layout; NK_ASSERT(layout->at_y == layout->bounds.y); /* if this assert triggers you allocated space between nk_begin and nk_menubar_begin. If you want a menubar the first nuklear function after `nk_begin` has to be a `nk_menubar_begin` call. Inside the menubar you then have to allocate space for widgets (also supports multiple rows). Example: if (nk_begin(...)) { nk_menubar_begin(...); nk_layout_xxxx(...); nk_button(...); nk_layout_xxxx(...); nk_button(...); nk_menubar_end(...); } nk_end(...); */ if (layout->flags & NK_WINDOW_HIDDEN || layout->flags & NK_WINDOW_MINIMIZED) return; layout->menu.x = layout->at_x; layout->menu.y = layout->at_y + layout->row.height; layout->menu.w = layout->bounds.w; layout->menu.offset.x = *layout->offset_x; layout->menu.offset.y = *layout->offset_y; *layout->offset_y = 0; } NK_API void nk_menubar_end(struct nk_context *ctx) { struct nk_window *win; struct nk_panel *layout; struct nk_command_buffer *out; NK_ASSERT(ctx); NK_ASSERT(ctx->current); NK_ASSERT(ctx->current->layout); if (!ctx || !ctx->current || !ctx->current->layout) return; win = ctx->current; out = &win->buffer; layout = win->layout; if (layout->flags & NK_WINDOW_HIDDEN || layout->flags & NK_WINDOW_MINIMIZED) return; layout->menu.h = layout->at_y - layout->menu.y; layout->menu.h += layout->row.height + ctx->style.window.spacing.y; layout->bounds.y += layout->menu.h; layout->bounds.h -= layout->menu.h; *layout->offset_x = layout->menu.offset.x; *layout->offset_y = layout->menu.offset.y; layout->at_y = layout->bounds.y - layout->row.height; layout->clip.y = layout->bounds.y; layout->clip.h = layout->bounds.h; nk_push_scissor(out, layout->clip); } NK_INTERN int nk_menu_begin(struct nk_context *ctx, struct nk_window *win, const char *id, int is_clicked, struct nk_rect header, struct nk_vec2 size) { int is_open = 0; int is_active = 0; struct nk_rect body; struct nk_window *popup; nk_hash hash = nk_murmur_hash(id, (int)nk_strlen(id), NK_PANEL_MENU); NK_ASSERT(ctx); NK_ASSERT(ctx->current); NK_ASSERT(ctx->current->layout); if (!ctx || !ctx->current || !ctx->current->layout) return 0; body.x = header.x; body.w = size.x; body.y = header.y + header.h; body.h = size.y; popup = win->popup.win; is_open = popup ? nk_true : nk_false; is_active = (popup && (win->popup.name == hash) && win->popup.type == NK_PANEL_MENU); if ((is_clicked && is_open && !is_active) || (is_open && !is_active) || (!is_open && !is_active && !is_clicked)) return 0; if (!nk_nonblock_begin(ctx, NK_WINDOW_NO_SCROLLBAR, body, header, NK_PANEL_MENU)) return 0; win->popup.type = NK_PANEL_MENU; win->popup.name = hash; return 1; } NK_API nk_bool nk_menu_begin_text(struct nk_context *ctx, const char *title, int len, nk_flags align, struct nk_vec2 size) { struct nk_window *win; const struct nk_input *in; struct nk_rect header; int is_clicked = nk_false; nk_flags state; NK_ASSERT(ctx); NK_ASSERT(ctx->current); NK_ASSERT(ctx->current->layout); if (!ctx || !ctx->current || !ctx->current->layout) return 0; win = ctx->current; state = nk_widget(&header, ctx); if (!state) return 0; in = (state == NK_WIDGET_ROM || win->flags & NK_WINDOW_ROM) ? 0 : &ctx->input; if (nk_do_button_text(&ctx->last_widget_state, &win->buffer, header, title, len, align, NK_BUTTON_DEFAULT, &ctx->style.menu_button, in, ctx->style.font)) is_clicked = nk_true; return nk_menu_begin(ctx, win, title, is_clicked, header, size); } NK_API nk_bool nk_menu_begin_label(struct nk_context *ctx, const char *text, nk_flags align, struct nk_vec2 size) { return nk_menu_begin_text(ctx, text, nk_strlen(text), align, size); } NK_API nk_bool nk_menu_begin_image(struct nk_context *ctx, const char *id, struct nk_image img, struct nk_vec2 size) { struct nk_window *win; struct nk_rect header; const struct nk_input *in; int is_clicked = nk_false; nk_flags state; NK_ASSERT(ctx); NK_ASSERT(ctx->current); NK_ASSERT(ctx->current->layout); if (!ctx || !ctx->current || !ctx->current->layout) return 0; win = ctx->current; state = nk_widget(&header, ctx); if (!state) return 0; in = (state == NK_WIDGET_ROM || win->layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input; if (nk_do_button_image(&ctx->last_widget_state, &win->buffer, header, img, NK_BUTTON_DEFAULT, &ctx->style.menu_button, in)) is_clicked = nk_true; return nk_menu_begin(ctx, win, id, is_clicked, header, size); } NK_API nk_bool nk_menu_begin_symbol(struct nk_context *ctx, const char *id, enum nk_symbol_type sym, struct nk_vec2 size) { struct nk_window *win; const struct nk_input *in; struct nk_rect header; int is_clicked = nk_false; nk_flags state; NK_ASSERT(ctx); NK_ASSERT(ctx->current); NK_ASSERT(ctx->current->layout); if (!ctx || !ctx->current || !ctx->current->layout) return 0; win = ctx->current; state = nk_widget(&header, ctx); if (!state) return 0; in = (state == NK_WIDGET_ROM || win->layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input; if (nk_do_button_symbol(&ctx->last_widget_state, &win->buffer, header, sym, NK_BUTTON_DEFAULT, &ctx->style.menu_button, in, ctx->style.font)) is_clicked = nk_true; return nk_menu_begin(ctx, win, id, is_clicked, header, size); } NK_API nk_bool nk_menu_begin_image_text(struct nk_context *ctx, const char *title, int len, nk_flags align, struct nk_image img, struct nk_vec2 size) { struct nk_window *win; struct nk_rect header; const struct nk_input *in; int is_clicked = nk_false; nk_flags state; NK_ASSERT(ctx); NK_ASSERT(ctx->current); NK_ASSERT(ctx->current->layout); if (!ctx || !ctx->current || !ctx->current->layout) return 0; win = ctx->current; state = nk_widget(&header, ctx); if (!state) return 0; in = (state == NK_WIDGET_ROM || win->layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input; if (nk_do_button_text_image(&ctx->last_widget_state, &win->buffer, header, img, title, len, align, NK_BUTTON_DEFAULT, &ctx->style.menu_button, ctx->style.font, in)) is_clicked = nk_true; return nk_menu_begin(ctx, win, title, is_clicked, header, size); } NK_API nk_bool nk_menu_begin_image_label(struct nk_context *ctx, const char *title, nk_flags align, struct nk_image img, struct nk_vec2 size) { return nk_menu_begin_image_text(ctx, title, nk_strlen(title), align, img, size); } NK_API nk_bool nk_menu_begin_symbol_text(struct nk_context *ctx, const char *title, int len, nk_flags align, enum nk_symbol_type sym, struct nk_vec2 size) { struct nk_window *win; struct nk_rect header; const struct nk_input *in; int is_clicked = nk_false; nk_flags state; NK_ASSERT(ctx); NK_ASSERT(ctx->current); NK_ASSERT(ctx->current->layout); if (!ctx || !ctx->current || !ctx->current->layout) return 0; win = ctx->current; state = nk_widget(&header, ctx); if (!state) return 0; in = (state == NK_WIDGET_ROM || win->layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input; if (nk_do_button_text_symbol(&ctx->last_widget_state, &win->buffer, header, sym, title, len, align, NK_BUTTON_DEFAULT, &ctx->style.menu_button, ctx->style.font, in)) is_clicked = nk_true; return nk_menu_begin(ctx, win, title, is_clicked, header, size); } NK_API nk_bool nk_menu_begin_symbol_label(struct nk_context *ctx, const char *title, nk_flags align, enum nk_symbol_type sym, struct nk_vec2 size ) { return nk_menu_begin_symbol_text(ctx, title, nk_strlen(title), align,sym,size); } NK_API nk_bool nk_menu_item_text(struct nk_context *ctx, const char *title, int len, nk_flags align) { return nk_contextual_item_text(ctx, title, len, align); } NK_API nk_bool nk_menu_item_label(struct nk_context *ctx, const char *label, nk_flags align) { return nk_contextual_item_label(ctx, label, align); } NK_API nk_bool nk_menu_item_image_label(struct nk_context *ctx, struct nk_image img, const char *label, nk_flags align) { return nk_contextual_item_image_label(ctx, img, label, align); } NK_API nk_bool nk_menu_item_image_text(struct nk_context *ctx, struct nk_image img, const char *text, int len, nk_flags align) { return nk_contextual_item_image_text(ctx, img, text, len, align); } NK_API nk_bool nk_menu_item_symbol_text(struct nk_context *ctx, enum nk_symbol_type sym, const char *text, int len, nk_flags align) { return nk_contextual_item_symbol_text(ctx, sym, text, len, align); } NK_API nk_bool nk_menu_item_symbol_label(struct nk_context *ctx, enum nk_symbol_type sym, const char *label, nk_flags align) { return nk_contextual_item_symbol_label(ctx, sym, label, align); } NK_API void nk_menu_close(struct nk_context *ctx) { nk_contextual_close(ctx); } NK_API void nk_menu_end(struct nk_context *ctx) { nk_contextual_end(ctx); } /* =============================================================== * * LAYOUT * * ===============================================================*/ NK_API void nk_layout_set_min_row_height(struct nk_context *ctx, float height) { struct nk_window *win; struct nk_panel *layout; NK_ASSERT(ctx); NK_ASSERT(ctx->current); NK_ASSERT(ctx->current->layout); if (!ctx || !ctx->current || !ctx->current->layout) return; win = ctx->current; layout = win->layout; layout->row.min_height = height; } NK_API void nk_layout_reset_min_row_height(struct nk_context *ctx) { struct nk_window *win; struct nk_panel *layout; NK_ASSERT(ctx); NK_ASSERT(ctx->current); NK_ASSERT(ctx->current->layout); if (!ctx || !ctx->current || !ctx->current->layout) return; win = ctx->current; layout = win->layout; layout->row.min_height = ctx->style.font->height; layout->row.min_height += ctx->style.text.padding.y*2; layout->row.min_height += ctx->style.window.min_row_height_padding*2; } NK_LIB float nk_layout_row_calculate_usable_space(const struct nk_style *style, enum nk_panel_type type, float total_space, int columns) { float panel_spacing; float panel_space; struct nk_vec2 spacing; NK_UNUSED(type); spacing = style->window.spacing; /* calculate the usable panel space */ panel_spacing = (float)NK_MAX(columns - 1, 0) * spacing.x; panel_space = total_space - panel_spacing; return panel_space; } NK_LIB void nk_panel_layout(const struct nk_context *ctx, struct nk_window *win, float height, int cols) { struct nk_panel *layout; const struct nk_style *style; struct nk_command_buffer *out; struct nk_vec2 item_spacing; struct nk_color color; NK_ASSERT(ctx); NK_ASSERT(ctx->current); NK_ASSERT(ctx->current->layout); if (!ctx || !ctx->current || !ctx->current->layout) return; /* prefetch some configuration data */ layout = win->layout; style = &ctx->style; out = &win->buffer; color = style->window.background; item_spacing = style->window.spacing; /* if one of these triggers you forgot to add an `if` condition around either a window, group, popup, combobox or contextual menu `begin` and `end` block. Example: if (nk_begin(...) {...} nk_end(...); or if (nk_group_begin(...) { nk_group_end(...);} */ NK_ASSERT(!(layout->flags & NK_WINDOW_MINIMIZED)); NK_ASSERT(!(layout->flags & NK_WINDOW_HIDDEN)); NK_ASSERT(!(layout->flags & NK_WINDOW_CLOSED)); /* update the current row and set the current row layout */ layout->row.index = 0; layout->at_y += layout->row.height; layout->row.columns = cols; if (height == 0.0f) layout->row.height = NK_MAX(height, layout->row.min_height) + item_spacing.y; else layout->row.height = height + item_spacing.y; layout->row.item_offset = 0; if (layout->flags & NK_WINDOW_DYNAMIC) { /* draw background for dynamic panels */ struct nk_rect background; background.x = win->bounds.x; background.w = win->bounds.w; background.y = layout->at_y - 1.0f; background.h = layout->row.height + 1.0f; nk_fill_rect(out, background, 0, color); } } NK_LIB void nk_row_layout(struct nk_context *ctx, enum nk_layout_format fmt, float height, int cols, int width) { /* update the current row and set the current row layout */ struct nk_window *win; NK_ASSERT(ctx); NK_ASSERT(ctx->current); NK_ASSERT(ctx->current->layout); if (!ctx || !ctx->current || !ctx->current->layout) return; win = ctx->current; nk_panel_layout(ctx, win, height, cols); if (fmt == NK_DYNAMIC) win->layout->row.type = NK_LAYOUT_DYNAMIC_FIXED; else win->layout->row.type = NK_LAYOUT_STATIC_FIXED; win->layout->row.ratio = 0; win->layout->row.filled = 0; win->layout->row.item_offset = 0; win->layout->row.item_width = (float)width; } NK_API float nk_layout_ratio_from_pixel(struct nk_context *ctx, float pixel_width) { struct nk_window *win; NK_ASSERT(ctx); NK_ASSERT(pixel_width); if (!ctx || !ctx->current || !ctx->current->layout) return 0; win = ctx->current; return NK_CLAMP(0.0f, pixel_width/win->bounds.x, 1.0f); } NK_API void nk_layout_row_dynamic(struct nk_context *ctx, float height, int cols) { nk_row_layout(ctx, NK_DYNAMIC, height, cols, 0); } NK_API void nk_layout_row_static(struct nk_context *ctx, float height, int item_width, int cols) { nk_row_layout(ctx, NK_STATIC, height, cols, item_width); } NK_API void nk_layout_row_begin(struct nk_context *ctx, enum nk_layout_format fmt, float row_height, int cols) { struct nk_window *win; struct nk_panel *layout; NK_ASSERT(ctx); NK_ASSERT(ctx->current); NK_ASSERT(ctx->current->layout); if (!ctx || !ctx->current || !ctx->current->layout) return; win = ctx->current; layout = win->layout; nk_panel_layout(ctx, win, row_height, cols); if (fmt == NK_DYNAMIC) layout->row.type = NK_LAYOUT_DYNAMIC_ROW; else layout->row.type = NK_LAYOUT_STATIC_ROW; layout->row.ratio = 0; layout->row.filled = 0; layout->row.item_width = 0; layout->row.item_offset = 0; layout->row.columns = cols; } NK_API void nk_layout_row_push(struct nk_context *ctx, float ratio_or_width) { struct nk_window *win; struct nk_panel *layout; NK_ASSERT(ctx); NK_ASSERT(ctx->current); NK_ASSERT(ctx->current->layout); if (!ctx || !ctx->current || !ctx->current->layout) return; win = ctx->current; layout = win->layout; NK_ASSERT(layout->row.type == NK_LAYOUT_STATIC_ROW || layout->row.type == NK_LAYOUT_DYNAMIC_ROW); if (layout->row.type != NK_LAYOUT_STATIC_ROW && layout->row.type != NK_LAYOUT_DYNAMIC_ROW) return; if (layout->row.type == NK_LAYOUT_DYNAMIC_ROW) { float ratio = ratio_or_width; if ((ratio + layout->row.filled) > 1.0f) return; if (ratio > 0.0f) layout->row.item_width = NK_SATURATE(ratio); else layout->row.item_width = 1.0f - layout->row.filled; } else layout->row.item_width = ratio_or_width; } NK_API void nk_layout_row_end(struct nk_context *ctx) { struct nk_window *win; struct nk_panel *layout; NK_ASSERT(ctx); NK_ASSERT(ctx->current); NK_ASSERT(ctx->current->layout); if (!ctx || !ctx->current || !ctx->current->layout) return; win = ctx->current; layout = win->layout; NK_ASSERT(layout->row.type == NK_LAYOUT_STATIC_ROW || layout->row.type == NK_LAYOUT_DYNAMIC_ROW); if (layout->row.type != NK_LAYOUT_STATIC_ROW && layout->row.type != NK_LAYOUT_DYNAMIC_ROW) return; layout->row.item_width = 0; layout->row.item_offset = 0; } NK_API void nk_layout_row(struct nk_context *ctx, enum nk_layout_format fmt, float height, int cols, const float *ratio) { int i; int n_undef = 0; struct nk_window *win; struct nk_panel *layout; NK_ASSERT(ctx); NK_ASSERT(ctx->current); NK_ASSERT(ctx->current->layout); if (!ctx || !ctx->current || !ctx->current->layout) return; win = ctx->current; layout = win->layout; nk_panel_layout(ctx, win, height, cols); if (fmt == NK_DYNAMIC) { /* calculate width of undefined widget ratios */ float r = 0; layout->row.ratio = ratio; for (i = 0; i < cols; ++i) { if (ratio[i] < 0.0f) n_undef++; else r += ratio[i]; } r = NK_SATURATE(1.0f - r); layout->row.type = NK_LAYOUT_DYNAMIC; layout->row.item_width = (r > 0 && n_undef > 0) ? (r / (float)n_undef):0; } else { layout->row.ratio = ratio; layout->row.type = NK_LAYOUT_STATIC; layout->row.item_width = 0; layout->row.item_offset = 0; } layout->row.item_offset = 0; layout->row.filled = 0; } NK_API void nk_layout_row_template_begin(struct nk_context *ctx, float height) { struct nk_window *win; struct nk_panel *layout; NK_ASSERT(ctx); NK_ASSERT(ctx->current); NK_ASSERT(ctx->current->layout); if (!ctx || !ctx->current || !ctx->current->layout) return; win = ctx->current; layout = win->layout; nk_panel_layout(ctx, win, height, 1); layout->row.type = NK_LAYOUT_TEMPLATE; layout->row.columns = 0; layout->row.ratio = 0; layout->row.item_width = 0; layout->row.item_height = 0; layout->row.item_offset = 0; layout->row.filled = 0; layout->row.item.x = 0; layout->row.item.y = 0; layout->row.item.w = 0; layout->row.item.h = 0; } NK_API void nk_layout_row_template_push_dynamic(struct nk_context *ctx) { struct nk_window *win; struct nk_panel *layout; NK_ASSERT(ctx); NK_ASSERT(ctx->current); NK_ASSERT(ctx->current->layout); if (!ctx || !ctx->current || !ctx->current->layout) return; win = ctx->current; layout = win->layout; NK_ASSERT(layout->row.type == NK_LAYOUT_TEMPLATE); NK_ASSERT(layout->row.columns < NK_MAX_LAYOUT_ROW_TEMPLATE_COLUMNS); if (layout->row.type != NK_LAYOUT_TEMPLATE) return; if (layout->row.columns >= NK_MAX_LAYOUT_ROW_TEMPLATE_COLUMNS) return; layout->row.templates[layout->row.columns++] = -1.0f; } NK_API void nk_layout_row_template_push_variable(struct nk_context *ctx, float min_width) { struct nk_window *win; struct nk_panel *layout; NK_ASSERT(ctx); NK_ASSERT(ctx->current); NK_ASSERT(ctx->current->layout); if (!ctx || !ctx->current || !ctx->current->layout) return; win = ctx->current; layout = win->layout; NK_ASSERT(layout->row.type == NK_LAYOUT_TEMPLATE); NK_ASSERT(layout->row.columns < NK_MAX_LAYOUT_ROW_TEMPLATE_COLUMNS); if (layout->row.type != NK_LAYOUT_TEMPLATE) return; if (layout->row.columns >= NK_MAX_LAYOUT_ROW_TEMPLATE_COLUMNS) return; layout->row.templates[layout->row.columns++] = -min_width; } NK_API void nk_layout_row_template_push_static(struct nk_context *ctx, float width) { struct nk_window *win; struct nk_panel *layout; NK_ASSERT(ctx); NK_ASSERT(ctx->current); NK_ASSERT(ctx->current->layout); if (!ctx || !ctx->current || !ctx->current->layout) return; win = ctx->current; layout = win->layout; NK_ASSERT(layout->row.type == NK_LAYOUT_TEMPLATE); NK_ASSERT(layout->row.columns < NK_MAX_LAYOUT_ROW_TEMPLATE_COLUMNS); if (layout->row.type != NK_LAYOUT_TEMPLATE) return; if (layout->row.columns >= NK_MAX_LAYOUT_ROW_TEMPLATE_COLUMNS) return; layout->row.templates[layout->row.columns++] = width; } NK_API void nk_layout_row_template_end(struct nk_context *ctx) { struct nk_window *win; struct nk_panel *layout; int i = 0; int variable_count = 0; int min_variable_count = 0; float min_fixed_width = 0.0f; float total_fixed_width = 0.0f; float max_variable_width = 0.0f; NK_ASSERT(ctx); NK_ASSERT(ctx->current); NK_ASSERT(ctx->current->layout); if (!ctx || !ctx->current || !ctx->current->layout) return; win = ctx->current; layout = win->layout; NK_ASSERT(layout->row.type == NK_LAYOUT_TEMPLATE); if (layout->row.type != NK_LAYOUT_TEMPLATE) return; for (i = 0; i < layout->row.columns; ++i) { float width = layout->row.templates[i]; if (width >= 0.0f) { total_fixed_width += width; min_fixed_width += width; } else if (width < -1.0f) { width = -width; total_fixed_width += width; max_variable_width = NK_MAX(max_variable_width, width); variable_count++; } else { min_variable_count++; variable_count++; } } if (variable_count) { float space = nk_layout_row_calculate_usable_space(&ctx->style, layout->type, layout->bounds.w, layout->row.columns); float var_width = (NK_MAX(space-min_fixed_width,0.0f)) / (float)variable_count; int enough_space = var_width >= max_variable_width; if (!enough_space) var_width = (NK_MAX(space-total_fixed_width,0)) / (float)min_variable_count; for (i = 0; i < layout->row.columns; ++i) { float *width = &layout->row.templates[i]; *width = (*width >= 0.0f)? *width: (*width < -1.0f && !enough_space)? -(*width): var_width; } } } NK_API void nk_layout_space_begin(struct nk_context *ctx, enum nk_layout_format fmt, float height, int widget_count) { struct nk_window *win; struct nk_panel *layout; NK_ASSERT(ctx); NK_ASSERT(ctx->current); NK_ASSERT(ctx->current->layout); if (!ctx || !ctx->current || !ctx->current->layout) return; win = ctx->current; layout = win->layout; nk_panel_layout(ctx, win, height, widget_count); if (fmt == NK_STATIC) layout->row.type = NK_LAYOUT_STATIC_FREE; else layout->row.type = NK_LAYOUT_DYNAMIC_FREE; layout->row.ratio = 0; layout->row.filled = 0; layout->row.item_width = 0; layout->row.item_offset = 0; } NK_API void nk_layout_space_end(struct nk_context *ctx) { struct nk_window *win; struct nk_panel *layout; NK_ASSERT(ctx); NK_ASSERT(ctx->current); NK_ASSERT(ctx->current->layout); if (!ctx || !ctx->current || !ctx->current->layout) return; win = ctx->current; layout = win->layout; layout->row.item_width = 0; layout->row.item_height = 0; layout->row.item_offset = 0; nk_zero(&layout->row.item, sizeof(layout->row.item)); } NK_API void nk_layout_space_push(struct nk_context *ctx, struct nk_rect rect) { struct nk_window *win; struct nk_panel *layout; NK_ASSERT(ctx); NK_ASSERT(ctx->current); NK_ASSERT(ctx->current->layout); if (!ctx || !ctx->current || !ctx->current->layout) return; win = ctx->current; layout = win->layout; layout->row.item = rect; } NK_API struct nk_rect nk_layout_space_bounds(struct nk_context *ctx) { struct nk_rect ret; struct nk_window *win; struct nk_panel *layout; NK_ASSERT(ctx); NK_ASSERT(ctx->current); NK_ASSERT(ctx->current->layout); win = ctx->current; layout = win->layout; ret.x = layout->clip.x; ret.y = layout->clip.y; ret.w = layout->clip.w; ret.h = layout->row.height; return ret; } NK_API struct nk_rect nk_layout_widget_bounds(struct nk_context *ctx) { struct nk_rect ret; struct nk_window *win; struct nk_panel *layout; NK_ASSERT(ctx); NK_ASSERT(ctx->current); NK_ASSERT(ctx->current->layout); win = ctx->current; layout = win->layout; ret.x = layout->at_x; ret.y = layout->at_y; ret.w = layout->bounds.w - NK_MAX(layout->at_x - layout->bounds.x,0); ret.h = layout->row.height; return ret; } NK_API struct nk_vec2 nk_layout_space_to_screen(struct nk_context *ctx, struct nk_vec2 ret) { struct nk_window *win; struct nk_panel *layout; NK_ASSERT(ctx); NK_ASSERT(ctx->current); NK_ASSERT(ctx->current->layout); win = ctx->current; layout = win->layout; ret.x += layout->at_x - (float)*layout->offset_x; ret.y += layout->at_y - (float)*layout->offset_y; return ret; } NK_API struct nk_vec2 nk_layout_space_to_local(struct nk_context *ctx, struct nk_vec2 ret) { struct nk_window *win; struct nk_panel *layout; NK_ASSERT(ctx); NK_ASSERT(ctx->current); NK_ASSERT(ctx->current->layout); win = ctx->current; layout = win->layout; ret.x += -layout->at_x + (float)*layout->offset_x; ret.y += -layout->at_y + (float)*layout->offset_y; return ret; } NK_API struct nk_rect nk_layout_space_rect_to_screen(struct nk_context *ctx, struct nk_rect ret) { struct nk_window *win; struct nk_panel *layout; NK_ASSERT(ctx); NK_ASSERT(ctx->current); NK_ASSERT(ctx->current->layout); win = ctx->current; layout = win->layout; ret.x += layout->at_x - (float)*layout->offset_x; ret.y += layout->at_y - (float)*layout->offset_y; return ret; } NK_API struct nk_rect nk_layout_space_rect_to_local(struct nk_context *ctx, struct nk_rect ret) { struct nk_window *win; struct nk_panel *layout; NK_ASSERT(ctx); NK_ASSERT(ctx->current); NK_ASSERT(ctx->current->layout); win = ctx->current; layout = win->layout; ret.x += -layout->at_x + (float)*layout->offset_x; ret.y += -layout->at_y + (float)*layout->offset_y; return ret; } NK_LIB void nk_panel_alloc_row(const struct nk_context *ctx, struct nk_window *win) { struct nk_panel *layout = win->layout; struct nk_vec2 spacing = ctx->style.window.spacing; const float row_height = layout->row.height - spacing.y; nk_panel_layout(ctx, win, row_height, layout->row.columns); } NK_LIB void nk_layout_widget_space(struct nk_rect *bounds, const struct nk_context *ctx, struct nk_window *win, int modify) { struct nk_panel *layout; const struct nk_style *style; struct nk_vec2 spacing; float item_offset = 0; float item_width = 0; float item_spacing = 0; float panel_space = 0; NK_ASSERT(ctx); NK_ASSERT(ctx->current); NK_ASSERT(ctx->current->layout); if (!ctx || !ctx->current || !ctx->current->layout) return; win = ctx->current; layout = win->layout; style = &ctx->style; NK_ASSERT(bounds); spacing = style->window.spacing; panel_space = nk_layout_row_calculate_usable_space(&ctx->style, layout->type, layout->bounds.w, layout->row.columns); #define NK_FRAC(x) (x - (float)(int)x) /* will be used to remove fookin gaps */ /* calculate the width of one item inside the current layout space */ switch (layout->row.type) { case NK_LAYOUT_DYNAMIC_FIXED: { /* scaling fixed size widgets item width */ float w = NK_MAX(1.0f,panel_space) / (float)layout->row.columns; item_offset = (float)layout->row.index * w; item_width = w + NK_FRAC(item_offset); item_spacing = (float)layout->row.index * spacing.x; } break; case NK_LAYOUT_DYNAMIC_ROW: { /* scaling single ratio widget width */ float w = layout->row.item_width * panel_space; item_offset = layout->row.item_offset; item_width = w + NK_FRAC(item_offset); item_spacing = 0; if (modify) { layout->row.item_offset += w + spacing.x; layout->row.filled += layout->row.item_width; layout->row.index = 0; } } break; case NK_LAYOUT_DYNAMIC_FREE: { /* panel width depended free widget placing */ bounds->x = layout->at_x + (layout->bounds.w * layout->row.item.x); bounds->x -= (float)*layout->offset_x; bounds->y = layout->at_y + (layout->row.height * layout->row.item.y); bounds->y -= (float)*layout->offset_y; bounds->w = layout->bounds.w * layout->row.item.w + NK_FRAC(bounds->x); bounds->h = layout->row.height * layout->row.item.h + NK_FRAC(bounds->y); return; } case NK_LAYOUT_DYNAMIC: { /* scaling arrays of panel width ratios for every widget */ float ratio, w; NK_ASSERT(layout->row.ratio); ratio = (layout->row.ratio[layout->row.index] < 0) ? layout->row.item_width : layout->row.ratio[layout->row.index]; w = (ratio * panel_space); item_spacing = (float)layout->row.index * spacing.x; item_offset = layout->row.item_offset; item_width = w + NK_FRAC(item_offset); if (modify) { layout->row.item_offset += w; layout->row.filled += ratio; } } break; case NK_LAYOUT_STATIC_FIXED: { /* non-scaling fixed widgets item width */ item_width = layout->row.item_width; item_offset = (float)layout->row.index * item_width; item_spacing = (float)layout->row.index * spacing.x; } break; case NK_LAYOUT_STATIC_ROW: { /* scaling single ratio widget width */ item_width = layout->row.item_width; item_offset = layout->row.item_offset; item_spacing = (float)layout->row.index * spacing.x; if (modify) layout->row.item_offset += item_width; } break; case NK_LAYOUT_STATIC_FREE: { /* free widget placing */ bounds->x = layout->at_x + layout->row.item.x; bounds->w = layout->row.item.w; if (((bounds->x + bounds->w) > layout->max_x) && modify) layout->max_x = (bounds->x + bounds->w); bounds->x -= (float)*layout->offset_x; bounds->y = layout->at_y + layout->row.item.y; bounds->y -= (float)*layout->offset_y; bounds->h = layout->row.item.h; return; } case NK_LAYOUT_STATIC: { /* non-scaling array of panel pixel width for every widget */ item_spacing = (float)layout->row.index * spacing.x; item_width = layout->row.ratio[layout->row.index]; item_offset = layout->row.item_offset; if (modify) layout->row.item_offset += item_width; } break; case NK_LAYOUT_TEMPLATE: { /* stretchy row layout with combined dynamic/static widget width*/ float w; NK_ASSERT(layout->row.index < layout->row.columns); NK_ASSERT(layout->row.index < NK_MAX_LAYOUT_ROW_TEMPLATE_COLUMNS); w = layout->row.templates[layout->row.index]; item_offset = layout->row.item_offset; item_width = w + NK_FRAC(item_offset); item_spacing = (float)layout->row.index * spacing.x; if (modify) layout->row.item_offset += w; } break; #undef NK_FRAC default: NK_ASSERT(0); break; }; /* set the bounds of the newly allocated widget */ bounds->w = item_width; bounds->h = layout->row.height - spacing.y; bounds->y = layout->at_y - (float)*layout->offset_y; bounds->x = layout->at_x + item_offset + item_spacing; if (((bounds->x + bounds->w) > layout->max_x) && modify) layout->max_x = bounds->x + bounds->w; bounds->x -= (float)*layout->offset_x; } NK_LIB void nk_panel_alloc_space(struct nk_rect *bounds, const struct nk_context *ctx) { struct nk_window *win; struct nk_panel *layout; NK_ASSERT(ctx); NK_ASSERT(ctx->current); NK_ASSERT(ctx->current->layout); if (!ctx || !ctx->current || !ctx->current->layout) return; /* check if the end of the row has been hit and begin new row if so */ win = ctx->current; layout = win->layout; if (layout->row.index >= layout->row.columns) nk_panel_alloc_row(ctx, win); /* calculate widget position and size */ nk_layout_widget_space(bounds, ctx, win, nk_true); layout->row.index++; } NK_LIB void nk_layout_peek(struct nk_rect *bounds, struct nk_context *ctx) { float y; int index; struct nk_window *win; struct nk_panel *layout; NK_ASSERT(ctx); NK_ASSERT(ctx->current); NK_ASSERT(ctx->current->layout); if (!ctx || !ctx->current || !ctx->current->layout) { *bounds = nk_rect(0,0,0,0); return; } win = ctx->current; layout = win->layout; y = layout->at_y; index = layout->row.index; if (layout->row.index >= layout->row.columns) { layout->at_y += layout->row.height; layout->row.index = 0; } nk_layout_widget_space(bounds, ctx, win, nk_false); if (!layout->row.index) { bounds->x -= layout->row.item_offset; } layout->at_y = y; layout->row.index = index; } NK_API void nk_spacer(struct nk_context *ctx ) { struct nk_rect dummy_rect = { 0, 0, 0, 0 }; nk_panel_alloc_space( &dummy_rect, ctx ); } /* =============================================================== * * TREE * * ===============================================================*/ NK_INTERN int nk_tree_state_base(struct nk_context *ctx, enum nk_tree_type type, struct nk_image *img, const char *title, enum nk_collapse_states *state) { struct nk_window *win; struct nk_panel *layout; const struct nk_style *style; struct nk_command_buffer *out; const struct nk_input *in; const struct nk_style_button *button; enum nk_symbol_type symbol; float row_height; struct nk_vec2 item_spacing; struct nk_rect header = {0,0,0,0}; struct nk_rect sym = {0,0,0,0}; struct nk_text text; nk_flags ws = 0; enum nk_widget_layout_states widget_state; NK_ASSERT(ctx); NK_ASSERT(ctx->current); NK_ASSERT(ctx->current->layout); if (!ctx || !ctx->current || !ctx->current->layout) return 0; /* cache some data */ win = ctx->current; layout = win->layout; out = &win->buffer; style = &ctx->style; item_spacing = style->window.spacing; /* calculate header bounds and draw background */ row_height = style->font->height + 2 * style->tab.padding.y; nk_layout_set_min_row_height(ctx, row_height); nk_layout_row_dynamic(ctx, row_height, 1); nk_layout_reset_min_row_height(ctx); widget_state = nk_widget(&header, ctx); if (type == NK_TREE_TAB) { const struct nk_style_item *background = &style->tab.background; switch(background->type) { case NK_STYLE_ITEM_IMAGE: nk_draw_image(out, header, &background->data.image, nk_white); break; case NK_STYLE_ITEM_NINE_SLICE: nk_draw_nine_slice(out, header, &background->data.slice, nk_white); break; case NK_STYLE_ITEM_COLOR: nk_fill_rect(out, header, 0, style->tab.border_color); nk_fill_rect(out, nk_shrink_rect(header, style->tab.border), style->tab.rounding, background->data.color); break; } } else text.background = style->window.background; /* update node state */ in = (!(layout->flags & NK_WINDOW_ROM)) ? &ctx->input: 0; in = (in && widget_state == NK_WIDGET_VALID) ? &ctx->input : 0; if (nk_button_behavior(&ws, header, in, NK_BUTTON_DEFAULT)) *state = (*state == NK_MAXIMIZED) ? NK_MINIMIZED : NK_MAXIMIZED; /* select correct button style */ if (*state == NK_MAXIMIZED) { symbol = style->tab.sym_maximize; if (type == NK_TREE_TAB) button = &style->tab.tab_maximize_button; else button = &style->tab.node_maximize_button; } else { symbol = style->tab.sym_minimize; if (type == NK_TREE_TAB) button = &style->tab.tab_minimize_button; else button = &style->tab.node_minimize_button; } {/* draw triangle button */ sym.w = sym.h = style->font->height; sym.y = header.y + style->tab.padding.y; sym.x = header.x + style->tab.padding.x; for( nk_byte *a = &((struct nk_style_button *)button)->normal.data.color.a; a && ((*a = 0), 1); a = 0) //< @r-lyeh A=0 nk_do_button_symbol(&ws, &win->buffer, sym, symbol, NK_BUTTON_DEFAULT, button, 0, style->font); if (img) { /* draw optional image icon */ sym.x = sym.x + sym.w + 4 * item_spacing.x; nk_draw_image(&win->buffer, sym, img, nk_white); sym.w = style->font->height + style->tab.spacing.x;} } {/* draw label */ struct nk_rect label; header.w = NK_MAX(header.w, sym.w + item_spacing.x); label.x = sym.x + sym.w + item_spacing.x; label.y = sym.y; label.w = header.w - (sym.w + item_spacing.y + style->tab.indent); label.h = style->font->height; text.text = style->tab.text; text.padding = nk_vec2(0,0); nk_widget_text(out, label, title, nk_strlen(title), &text, NK_TEXT_LEFT, style->font);} /* increase x-axis cursor widget position pointer */ if (*state == NK_MAXIMIZED) { layout->at_x = header.x + (float)*layout->offset_x + style->tab.indent; layout->bounds.w = NK_MAX(layout->bounds.w, style->tab.indent); layout->bounds.w -= (style->tab.indent + style->window.padding.x); layout->row.tree_depth++; return nk_true; } else return nk_false; } NK_INTERN int nk_tree_base(struct nk_context *ctx, enum nk_tree_type type, struct nk_image *img, const char *title, enum nk_collapse_states initial_state, const char *hash, int len, int line) { struct nk_window *win = ctx->current; int title_len = 0; nk_hash tree_hash = 0; nk_uint *state = 0; /* retrieve tree state from internal widget state tables */ if (!hash) { title_len = (int)nk_strlen(title); tree_hash = nk_murmur_hash(title, (int)title_len, (nk_hash)line); } else tree_hash = nk_murmur_hash(hash, len, (nk_hash)line); state = nk_find_value(win, tree_hash); if (!state) { state = nk_add_value(ctx, win, tree_hash, 0); *state = initial_state; } return nk_tree_state_base(ctx, type, img, title, (enum nk_collapse_states*)state); } #if 1 //< @r-lyeh NK_INTERN int nk_tree_state_base_(struct nk_context *ctx, enum nk_tree_type type, struct nk_image *img, const char *title, enum nk_collapse_states *state) { struct nk_window *win; struct nk_panel *layout; const struct nk_style *style; struct nk_command_buffer *out; const struct nk_input *in; const struct nk_style_button *button; enum nk_symbol_type symbol; float row_height; struct nk_vec2 item_spacing; struct nk_rect header = {0,0,0,0}; struct nk_rect sym = {0,0,0,0}; struct nk_text text; nk_flags ws = 0; enum nk_widget_layout_states widget_state; NK_ASSERT(ctx); NK_ASSERT(ctx->current); NK_ASSERT(ctx->current->layout); if (!ctx || !ctx->current || !ctx->current->layout) return 0; /* cache some data */ win = ctx->current; layout = win->layout; out = &win->buffer; style = &ctx->style; item_spacing = style->window.spacing; /* calculate header bounds and draw background */ row_height = style->font->height + 2 * style->tab.padding.y; nk_layout_set_min_row_height(ctx, row_height); nk_layout_row_dynamic(ctx, row_height, 1); nk_layout_reset_min_row_height(ctx); widget_state = nk_widget(&header, ctx); if (type == NK_TREE_TAB) { const struct nk_style_item *background = &style->tab.background; switch(background->type) { case NK_STYLE_ITEM_IMAGE: nk_draw_image(out, header, &background->data.image, nk_white); break; case NK_STYLE_ITEM_NINE_SLICE: nk_draw_nine_slice(out, header, &background->data.slice, nk_white); break; case NK_STYLE_ITEM_COLOR: nk_fill_rect(out, header, 0, style->tab.border_color); nk_fill_rect(out, nk_shrink_rect(header, style->tab.border), style->tab.rounding, background->data.color); break; } } else text.background = style->window.background; int clicked = 0; //< @r-lyeh /* update node state */ in = (!(layout->flags & NK_WINDOW_ROM)) ? &ctx->input: 0; in = (in && widget_state == NK_WIDGET_VALID) ? &ctx->input : 0; if (nk_button_behavior(&ws, header, in, NK_BUTTON_DEFAULT)) { clicked |= 2; if( !input(KEY_LCTRL) && !input(KEY_RCTRL) ) //< @r-lyeh *state = (*state == NK_MAXIMIZED) ? NK_MINIMIZED : NK_MAXIMIZED, clicked |= 4; } /* select correct button style */ if (*state == NK_MAXIMIZED) { symbol = style->tab.sym_maximize; if (type == NK_TREE_TAB) button = &style->tab.tab_maximize_button; else button = &style->tab.node_maximize_button; } else { symbol = style->tab.sym_minimize; if (type == NK_TREE_TAB) button = &style->tab.tab_minimize_button; else button = &style->tab.node_minimize_button; } {/* draw triangle button */ sym.w = sym.h = style->font->height; sym.y = header.y + style->tab.padding.y; sym.x = header.x + style->tab.padding.x; for( nk_byte *a = &((struct nk_style_button *)button)->normal.data.color.a; a && ((*a = 0), 1); a = 0) //< @r-lyeh A=0 nk_do_button_symbol(&ws, &win->buffer, sym, symbol, NK_BUTTON_DEFAULT, button, 0, style->font); if (img) { /* draw optional image icon */ sym.x = sym.x + sym.w + 4 * item_spacing.x; nk_draw_image(&win->buffer, sym, img, nk_white); sym.w = style->font->height + style->tab.spacing.x;} } {/* draw label */ struct nk_rect label; header.w = NK_MAX(header.w, sym.w + item_spacing.x); label.x = sym.x + sym.w + item_spacing.x; label.y = sym.y; label.w = header.w - (sym.w + item_spacing.y + style->tab.indent); label.h = style->font->height; text.text = style->tab.text; text.padding = nk_vec2(0,0); nk_widget_text(out, label, title, nk_strlen(title), &text, NK_TEXT_LEFT, style->font);} /* increase x-axis cursor widget position pointer */ if (*state == NK_MAXIMIZED) { layout->at_x = header.x + (float)*layout->offset_x + style->tab.indent; layout->bounds.w = NK_MAX(layout->bounds.w, style->tab.indent); layout->bounds.w -= (style->tab.indent + style->window.padding.x); layout->row.tree_depth++; return clicked | nk_true; } else return clicked; } NK_INTERN int nk_tree_base_(struct nk_context *ctx, enum nk_tree_type type, struct nk_image *img, const char *title, enum nk_collapse_states initial_state, const char *hash, int len, int line) { struct nk_window *win = ctx->current; int title_len = 0; nk_hash tree_hash = 0; nk_uint *state = 0; /* retrieve tree state from internal widget state tables */ if (!hash) { title_len = (int)nk_strlen(title); tree_hash = nk_murmur_hash(title, (int)title_len, (nk_hash)line); } else tree_hash = nk_murmur_hash(hash, len, (nk_hash)line); state = nk_find_value(win, tree_hash); if (!state) { state = nk_add_value(ctx, win, tree_hash, 0); *state = initial_state; } return nk_tree_state_base_(ctx, type, img, title, (enum nk_collapse_states*)state); } #endif NK_API nk_bool nk_tree_state_push(struct nk_context *ctx, enum nk_tree_type type, const char *title, enum nk_collapse_states *state) { return nk_tree_state_base(ctx, type, 0, title, state); } NK_API nk_bool nk_tree_state_image_push(struct nk_context *ctx, enum nk_tree_type type, struct nk_image img, const char *title, enum nk_collapse_states *state) { return nk_tree_state_base(ctx, type, &img, title, state); } NK_API void nk_tree_state_pop(struct nk_context *ctx) { struct nk_window *win = 0; struct nk_panel *layout = 0; NK_ASSERT(ctx); NK_ASSERT(ctx->current); NK_ASSERT(ctx->current->layout); if (!ctx || !ctx->current || !ctx->current->layout) return; win = ctx->current; layout = win->layout; layout->at_x -= ctx->style.tab.indent + (float)*layout->offset_x; layout->bounds.w += ctx->style.tab.indent + ctx->style.window.padding.x; NK_ASSERT(layout->row.tree_depth); layout->row.tree_depth--; } NK_API nk_bool nk_tree_push_hashed(struct nk_context *ctx, enum nk_tree_type type, const char *title, enum nk_collapse_states initial_state, const char *hash, int len, int line) { return nk_tree_base(ctx, type, 0, title, initial_state, hash, len, line); } NK_API nk_bool nk_tree_image_push_hashed(struct nk_context *ctx, enum nk_tree_type type, struct nk_image img, const char *title, enum nk_collapse_states initial_state, const char *hash, int len,int seed) { return nk_tree_base(ctx, type, &img, title, initial_state, hash, len, seed); } NK_API void nk_tree_pop(struct nk_context *ctx) { nk_tree_state_pop(ctx); } NK_INTERN int nk_tree_element_image_push_hashed_base(struct nk_context *ctx, enum nk_tree_type type, struct nk_image *img, const char *title, int title_len, enum nk_collapse_states *state, nk_bool *selected) { struct nk_window *win; struct nk_panel *layout; const struct nk_style *style; struct nk_command_buffer *out; const struct nk_input *in; const struct nk_style_button *button; enum nk_symbol_type symbol; float row_height; struct nk_vec2 padding; int text_len; float text_width; struct nk_vec2 item_spacing; struct nk_rect header = {0,0,0,0}; struct nk_rect sym = {0,0,0,0}; nk_flags ws = 0; enum nk_widget_layout_states widget_state; NK_ASSERT(ctx); NK_ASSERT(ctx->current); NK_ASSERT(ctx->current->layout); if (!ctx || !ctx->current || !ctx->current->layout) return 0; /* cache some data */ win = ctx->current; layout = win->layout; out = &win->buffer; style = &ctx->style; item_spacing = style->window.spacing; padding = style->selectable.padding; /* calculate header bounds and draw background */ row_height = style->font->height + 2 * style->tab.padding.y; nk_layout_set_min_row_height(ctx, row_height); nk_layout_row_dynamic(ctx, row_height, 1); nk_layout_reset_min_row_height(ctx); widget_state = nk_widget(&header, ctx); if (type == NK_TREE_TAB) { const struct nk_style_item *background = &style->tab.background; switch (background->type) { case NK_STYLE_ITEM_IMAGE: nk_draw_image(out, header, &background->data.image, nk_white); break; case NK_STYLE_ITEM_NINE_SLICE: nk_draw_nine_slice(out, header, &background->data.slice, nk_white); break; case NK_STYLE_ITEM_COLOR: nk_fill_rect(out, header, 0, style->tab.border_color); nk_fill_rect(out, nk_shrink_rect(header, style->tab.border), style->tab.rounding, background->data.color); break; } } in = (!(layout->flags & NK_WINDOW_ROM)) ? &ctx->input: 0; in = (in && widget_state == NK_WIDGET_VALID) ? &ctx->input : 0; /* select correct button style */ if (*state == NK_MAXIMIZED) { symbol = style->tab.sym_maximize; if (type == NK_TREE_TAB) button = &style->tab.tab_maximize_button; else button = &style->tab.node_maximize_button; } else { symbol = style->tab.sym_minimize; if (type == NK_TREE_TAB) button = &style->tab.tab_minimize_button; else button = &style->tab.node_minimize_button; } {/* draw triangle button */ sym.w = sym.h = style->font->height; sym.y = header.y + style->tab.padding.y; sym.x = header.x + style->tab.padding.x; if (nk_do_button_symbol(&ws, &win->buffer, sym, symbol, NK_BUTTON_DEFAULT, button, in, style->font)) *state = (*state == NK_MAXIMIZED) ? NK_MINIMIZED : NK_MAXIMIZED;} /* draw label */ {nk_flags dummy = 0; struct nk_rect label; /* calculate size of the text and tooltip */ text_len = nk_strlen(title); text_width = style->font->width(style->font->userdata, style->font->height, title, text_len); text_width += (4 * padding.x); header.w = NK_MAX(header.w, sym.w + item_spacing.x); label.x = sym.x + sym.w + item_spacing.x; label.y = sym.y; label.w = NK_MIN(header.w - (sym.w + item_spacing.y + style->tab.indent), text_width); label.h = style->font->height; if (img) { nk_do_selectable_image(&dummy, &win->buffer, label, title, title_len, NK_TEXT_LEFT, selected, img, &style->selectable, in, style->font); } else nk_do_selectable(&dummy, &win->buffer, label, title, title_len, NK_TEXT_LEFT, selected, &style->selectable, in, style->font); } /* increase x-axis cursor widget position pointer */ if (*state == NK_MAXIMIZED) { layout->at_x = header.x + (float)*layout->offset_x + style->tab.indent; layout->bounds.w = NK_MAX(layout->bounds.w, style->tab.indent); layout->bounds.w -= (style->tab.indent + style->window.padding.x); layout->row.tree_depth++; return nk_true; } else return nk_false; } NK_INTERN int nk_tree_element_base(struct nk_context *ctx, enum nk_tree_type type, struct nk_image *img, const char *title, enum nk_collapse_states initial_state, nk_bool *selected, const char *hash, int len, int line) { struct nk_window *win = ctx->current; int title_len = 0; nk_hash tree_hash = 0; nk_uint *state = 0; /* retrieve tree state from internal widget state tables */ if (!hash) { title_len = (int)nk_strlen(title); tree_hash = nk_murmur_hash(title, (int)title_len, (nk_hash)line); } else tree_hash = nk_murmur_hash(hash, len, (nk_hash)line); state = nk_find_value(win, tree_hash); if (!state) { state = nk_add_value(ctx, win, tree_hash, 0); *state = initial_state; } return nk_tree_element_image_push_hashed_base(ctx, type, img, title, nk_strlen(title), (enum nk_collapse_states*)state, selected); } NK_API nk_bool nk_tree_element_push_hashed(struct nk_context *ctx, enum nk_tree_type type, const char *title, enum nk_collapse_states initial_state, nk_bool *selected, const char *hash, int len, int seed) { return nk_tree_element_base(ctx, type, 0, title, initial_state, selected, hash, len, seed); } NK_API nk_bool nk_tree_element_image_push_hashed(struct nk_context *ctx, enum nk_tree_type type, struct nk_image img, const char *title, enum nk_collapse_states initial_state, nk_bool *selected, const char *hash, int len,int seed) { return nk_tree_element_base(ctx, type, &img, title, initial_state, selected, hash, len, seed); } NK_API void nk_tree_element_pop(struct nk_context *ctx) { nk_tree_state_pop(ctx); } /* =============================================================== * * GROUP * * ===============================================================*/ NK_API nk_bool nk_group_scrolled_offset_begin(struct nk_context *ctx, nk_uint *x_offset, nk_uint *y_offset, const char *title, nk_flags flags) { struct nk_rect bounds; struct nk_window panel; struct nk_window *win; win = ctx->current; nk_panel_alloc_space(&bounds, ctx); {const struct nk_rect *c = &win->layout->clip; if (!NK_INTERSECT(c->x, c->y, c->w, c->h, bounds.x, bounds.y, bounds.w, bounds.h) && !(flags & NK_WINDOW_MOVABLE)) { return 0; }} if (win->flags & NK_WINDOW_ROM) flags |= NK_WINDOW_ROM; /* initialize a fake window to create the panel from */ nk_zero(&panel, sizeof(panel)); panel.bounds = bounds; panel.flags = flags; panel.scrollbar.x = *x_offset; panel.scrollbar.y = *y_offset; panel.buffer = win->buffer; panel.layout = (struct nk_panel*)nk_create_panel(ctx); ctx->current = &panel; nk_panel_begin(ctx, (flags & NK_WINDOW_TITLE) ? title: 0, NK_PANEL_GROUP); win->buffer = panel.buffer; win->buffer.clip = panel.layout->clip; panel.layout->offset_x = x_offset; panel.layout->offset_y = y_offset; panel.layout->parent = win->layout; win->layout = panel.layout; ctx->current = win; if ((panel.layout->flags & NK_WINDOW_CLOSED) || (panel.layout->flags & NK_WINDOW_MINIMIZED)) { nk_flags f = panel.layout->flags; nk_group_scrolled_end(ctx); if (f & NK_WINDOW_CLOSED) return NK_WINDOW_CLOSED; if (f & NK_WINDOW_MINIMIZED) return NK_WINDOW_MINIMIZED; } return 1; } NK_API void nk_group_scrolled_end(struct nk_context *ctx) { struct nk_window *win; struct nk_panel *parent; struct nk_panel *g; struct nk_rect clip; struct nk_window pan; struct nk_vec2 panel_padding; NK_ASSERT(ctx); NK_ASSERT(ctx->current); if (!ctx || !ctx->current) return; /* make sure nk_group_begin was called correctly */ NK_ASSERT(ctx->current); win = ctx->current; NK_ASSERT(win->layout); g = win->layout; NK_ASSERT(g->parent); parent = g->parent; /* dummy window */ nk_zero_struct(pan); panel_padding = nk_panel_get_padding(&ctx->style, NK_PANEL_GROUP); pan.bounds.y = g->bounds.y - (g->header_height + g->menu.h); pan.bounds.x = g->bounds.x - panel_padding.x; pan.bounds.w = g->bounds.w + 2 * panel_padding.x; pan.bounds.h = g->bounds.h + g->header_height + g->menu.h; if (g->flags & NK_WINDOW_BORDER) { pan.bounds.x -= g->border; pan.bounds.y -= g->border; pan.bounds.w += 2*g->border; pan.bounds.h += 2*g->border; } if (!(g->flags & NK_WINDOW_NO_SCROLLBAR_Y)) { //< @r-lyeh pan.bounds.w += ctx->style.window.scrollbar_size.x; } if (!(g->flags & NK_WINDOW_NO_SCROLLBAR_X)) { //< @r-lyeh pan.bounds.h += ctx->style.window.scrollbar_size.y; } pan.scrollbar.x = *g->offset_x; pan.scrollbar.y = *g->offset_y; pan.flags = g->flags; pan.buffer = win->buffer; pan.layout = g; pan.parent = win; ctx->current = &pan; /* make sure group has correct clipping rectangle */ nk_unify(&clip, &parent->clip, pan.bounds.x, pan.bounds.y, pan.bounds.x + pan.bounds.w, pan.bounds.y + pan.bounds.h + panel_padding.x); nk_push_scissor(&pan.buffer, clip); nk_end(ctx); win->buffer = pan.buffer; nk_push_scissor(&win->buffer, parent->clip); ctx->current = win; win->layout = parent; g->bounds = pan.bounds; return; } NK_API nk_bool nk_group_scrolled_begin(struct nk_context *ctx, struct nk_scroll *scroll, const char *title, nk_flags flags) { return nk_group_scrolled_offset_begin(ctx, &scroll->x, &scroll->y, title, flags); } NK_API nk_bool nk_group_begin_titled(struct nk_context *ctx, const char *id, const char *title, nk_flags flags) { int id_len; nk_hash id_hash; struct nk_window *win; nk_uint *x_offset; nk_uint *y_offset; NK_ASSERT(ctx); NK_ASSERT(id); NK_ASSERT(ctx->current); NK_ASSERT(ctx->current->layout); if (!ctx || !ctx->current || !ctx->current->layout || !id) return 0; /* find persistent group scrollbar value */ win = ctx->current; id_len = (int)nk_strlen(id); id_hash = nk_murmur_hash(id, (int)id_len, NK_PANEL_GROUP); x_offset = nk_find_value(win, id_hash); if (!x_offset) { x_offset = nk_add_value(ctx, win, id_hash, 0); y_offset = nk_add_value(ctx, win, id_hash+1, 0); NK_ASSERT(x_offset); NK_ASSERT(y_offset); if (!x_offset || !y_offset) return 0; *x_offset = *y_offset = 0; } else y_offset = nk_find_value(win, id_hash+1); return nk_group_scrolled_offset_begin(ctx, x_offset, y_offset, title, flags); } NK_API nk_bool nk_group_begin(struct nk_context *ctx, const char *title, nk_flags flags) { return nk_group_begin_titled(ctx, title, title, flags); } NK_API void nk_group_end(struct nk_context *ctx) { nk_group_scrolled_end(ctx); } NK_API void nk_group_get_scroll(struct nk_context *ctx, const char *id, nk_uint *x_offset, nk_uint *y_offset) { int id_len; nk_hash id_hash; struct nk_window *win; nk_uint *x_offset_ptr; nk_uint *y_offset_ptr; NK_ASSERT(ctx); NK_ASSERT(id); NK_ASSERT(ctx->current); NK_ASSERT(ctx->current->layout); if (!ctx || !ctx->current || !ctx->current->layout || !id) return; /* find persistent group scrollbar value */ win = ctx->current; id_len = (int)nk_strlen(id); id_hash = nk_murmur_hash(id, (int)id_len, NK_PANEL_GROUP); x_offset_ptr = nk_find_value(win, id_hash); if (!x_offset_ptr) { x_offset_ptr = nk_add_value(ctx, win, id_hash, 0); y_offset_ptr = nk_add_value(ctx, win, id_hash+1, 0); NK_ASSERT(x_offset_ptr); NK_ASSERT(y_offset_ptr); if (!x_offset_ptr || !y_offset_ptr) return; *x_offset_ptr = *y_offset_ptr = 0; } else y_offset_ptr = nk_find_value(win, id_hash+1); if (x_offset) *x_offset = *x_offset_ptr; if (y_offset) *y_offset = *y_offset_ptr; } NK_API void nk_group_set_scroll(struct nk_context *ctx, const char *id, nk_uint x_offset, nk_uint y_offset) { int id_len; nk_hash id_hash; struct nk_window *win; nk_uint *x_offset_ptr; nk_uint *y_offset_ptr; NK_ASSERT(ctx); NK_ASSERT(id); NK_ASSERT(ctx->current); NK_ASSERT(ctx->current->layout); if (!ctx || !ctx->current || !ctx->current->layout || !id) return; /* find persistent group scrollbar value */ win = ctx->current; id_len = (int)nk_strlen(id); id_hash = nk_murmur_hash(id, (int)id_len, NK_PANEL_GROUP); x_offset_ptr = nk_find_value(win, id_hash); if (!x_offset_ptr) { x_offset_ptr = nk_add_value(ctx, win, id_hash, 0); y_offset_ptr = nk_add_value(ctx, win, id_hash+1, 0); NK_ASSERT(x_offset_ptr); NK_ASSERT(y_offset_ptr); if (!x_offset_ptr || !y_offset_ptr) return; *x_offset_ptr = *y_offset_ptr = 0; } else y_offset_ptr = nk_find_value(win, id_hash+1); *x_offset_ptr = x_offset; *y_offset_ptr = y_offset; } /* =============================================================== * * LIST VIEW * * ===============================================================*/ NK_API nk_bool nk_list_view_begin(struct nk_context *ctx, struct nk_list_view *view, const char *title, nk_flags flags, int row_height, int row_count) { int title_len; nk_hash title_hash; nk_uint *x_offset; nk_uint *y_offset; int result; struct nk_window *win; struct nk_panel *layout; const struct nk_style *style; struct nk_vec2 item_spacing; NK_ASSERT(ctx); NK_ASSERT(view); NK_ASSERT(title); if (!ctx || !view || !title) return 0; win = ctx->current; style = &ctx->style; item_spacing = style->window.spacing; row_height += NK_MAX(0, (int)item_spacing.y); /* find persistent list view scrollbar offset */ title_len = (int)nk_strlen(title); title_hash = nk_murmur_hash(title, (int)title_len, NK_PANEL_GROUP); x_offset = nk_find_value(win, title_hash); if (!x_offset) { x_offset = nk_add_value(ctx, win, title_hash, 0); y_offset = nk_add_value(ctx, win, title_hash+1, 0); NK_ASSERT(x_offset); NK_ASSERT(y_offset); if (!x_offset || !y_offset) return 0; *x_offset = *y_offset = 0; } else y_offset = nk_find_value(win, title_hash+1); view->scroll_value = *y_offset; view->scroll_pointer = y_offset; *y_offset = 0; result = nk_group_scrolled_offset_begin(ctx, x_offset, y_offset, title, flags); win = ctx->current; layout = win->layout; view->total_height = row_height * NK_MAX(row_count,1); view->begin = (int)NK_MAX(((float)view->scroll_value / (float)row_height), 0.0f); view->count = (int)NK_MAX(nk_iceilf((layout->clip.h)/(float)row_height),0); view->count = NK_MIN(view->count, row_count - view->begin); view->end = view->begin + view->count; view->ctx = ctx; return result; } NK_API void nk_list_view_end(struct nk_list_view *view) { struct nk_context *ctx; struct nk_window *win; struct nk_panel *layout; NK_ASSERT(view); NK_ASSERT(view->ctx); NK_ASSERT(view->scroll_pointer); if (!view || !view->ctx) return; ctx = view->ctx; win = ctx->current; layout = win->layout; layout->at_y = layout->bounds.y + (float)view->total_height; *view->scroll_pointer = *view->scroll_pointer + view->scroll_value; nk_group_end(view->ctx); } /* =============================================================== * * WIDGET * * ===============================================================*/ NK_API struct nk_rect nk_widget_bounds(struct nk_context *ctx) { struct nk_rect bounds; NK_ASSERT(ctx); NK_ASSERT(ctx->current); if (!ctx || !ctx->current) return nk_rect(0,0,0,0); nk_layout_peek(&bounds, ctx); return bounds; } NK_API struct nk_vec2 nk_widget_position(struct nk_context *ctx) { struct nk_rect bounds; NK_ASSERT(ctx); NK_ASSERT(ctx->current); if (!ctx || !ctx->current) return nk_vec2(0,0); nk_layout_peek(&bounds, ctx); return nk_vec2(bounds.x, bounds.y); } NK_API struct nk_vec2 nk_widget_size(struct nk_context *ctx) { struct nk_rect bounds; NK_ASSERT(ctx); NK_ASSERT(ctx->current); if (!ctx || !ctx->current) return nk_vec2(0,0); nk_layout_peek(&bounds, ctx); return nk_vec2(bounds.w, bounds.h); } NK_API float nk_widget_width(struct nk_context *ctx) { struct nk_rect bounds; NK_ASSERT(ctx); NK_ASSERT(ctx->current); if (!ctx || !ctx->current) return 0; nk_layout_peek(&bounds, ctx); return bounds.w; } NK_API float nk_widget_height(struct nk_context *ctx) { struct nk_rect bounds; NK_ASSERT(ctx); NK_ASSERT(ctx->current); if (!ctx || !ctx->current) return 0; nk_layout_peek(&bounds, ctx); return bounds.h; } NK_API nk_bool nk_widget_is_hovered(struct nk_context *ctx) { struct nk_rect c, v; struct nk_rect bounds; NK_ASSERT(ctx); NK_ASSERT(ctx->current); if (!ctx || !ctx->current || ctx->active != ctx->current) return 0; c = ctx->current->layout->clip; c.x = (float)((int)c.x); c.y = (float)((int)c.y); c.w = (float)((int)c.w); c.h = (float)((int)c.h); nk_layout_peek(&bounds, ctx); nk_unify(&v, &c, bounds.x, bounds.y, bounds.x + bounds.w, bounds.y + bounds.h); if (!NK_INTERSECT(c.x, c.y, c.w, c.h, bounds.x, bounds.y, bounds.w, bounds.h)) return 0; return nk_input_is_mouse_hovering_rect(&ctx->input, bounds); } NK_API nk_bool nk_widget_is_mouse_clicked(struct nk_context *ctx, enum nk_buttons btn) { struct nk_rect c, v; struct nk_rect bounds; NK_ASSERT(ctx); NK_ASSERT(ctx->current); if (!ctx || !ctx->current || ctx->active != ctx->current) return 0; c = ctx->current->layout->clip; c.x = (float)((int)c.x); c.y = (float)((int)c.y); c.w = (float)((int)c.w); c.h = (float)((int)c.h); nk_layout_peek(&bounds, ctx); nk_unify(&v, &c, bounds.x, bounds.y, bounds.x + bounds.w, bounds.y + bounds.h); if (!NK_INTERSECT(c.x, c.y, c.w, c.h, bounds.x, bounds.y, bounds.w, bounds.h)) return 0; return nk_input_mouse_clicked(&ctx->input, btn, bounds); } NK_API nk_bool nk_widget_has_mouse_click_down(struct nk_context *ctx, enum nk_buttons btn, nk_bool down) { struct nk_rect c, v; struct nk_rect bounds; NK_ASSERT(ctx); NK_ASSERT(ctx->current); if (!ctx || !ctx->current || ctx->active != ctx->current) return 0; c = ctx->current->layout->clip; c.x = (float)((int)c.x); c.y = (float)((int)c.y); c.w = (float)((int)c.w); c.h = (float)((int)c.h); nk_layout_peek(&bounds, ctx); nk_unify(&v, &c, bounds.x, bounds.y, bounds.x + bounds.w, bounds.y + bounds.h); if (!NK_INTERSECT(c.x, c.y, c.w, c.h, bounds.x, bounds.y, bounds.w, bounds.h)) return 0; return nk_input_has_mouse_click_down_in_rect(&ctx->input, btn, bounds, down); } NK_API enum nk_widget_layout_states nk_widget(struct nk_rect *bounds, const struct nk_context *ctx) { struct nk_rect c, v; struct nk_window *win; struct nk_panel *layout; const struct nk_input *in; NK_ASSERT(ctx); NK_ASSERT(ctx->current); NK_ASSERT(ctx->current->layout); if (!ctx || !ctx->current || !ctx->current->layout) return NK_WIDGET_INVALID; /* allocate space and check if the widget needs to be updated and drawn */ nk_panel_alloc_space(bounds, ctx); win = ctx->current; layout = win->layout; in = &ctx->input; c = layout->clip; /* if one of these triggers you forgot to add an `if` condition around either a window, group, popup, combobox or contextual menu `begin` and `end` block. Example: if (nk_begin(...) {...} nk_end(...); or if (nk_group_begin(...) { nk_group_end(...);} */ NK_ASSERT(!(layout->flags & NK_WINDOW_MINIMIZED)); NK_ASSERT(!(layout->flags & NK_WINDOW_HIDDEN)); NK_ASSERT(!(layout->flags & NK_WINDOW_CLOSED)); /* need to convert to int here to remove floating point errors */ bounds->x = (float)((int)bounds->x); bounds->y = (float)((int)bounds->y); bounds->w = (float)((int)bounds->w); bounds->h = (float)((int)bounds->h); c.x = (float)((int)c.x); c.y = (float)((int)c.y); c.w = (float)((int)c.w); c.h = (float)((int)c.h); nk_unify(&v, &c, bounds->x, bounds->y, bounds->x + bounds->w, bounds->y + bounds->h); if (!NK_INTERSECT(c.x, c.y, c.w, c.h, bounds->x, bounds->y, bounds->w, bounds->h)) return NK_WIDGET_INVALID; if (!NK_INBOX(in->mouse.pos.x, in->mouse.pos.y, v.x, v.y, v.w, v.h)) return NK_WIDGET_ROM; return NK_WIDGET_VALID; } NK_API enum nk_widget_layout_states nk_widget_fitting(struct nk_rect *bounds, struct nk_context *ctx, struct nk_vec2 item_padding) { /* update the bounds to stand without padding */ enum nk_widget_layout_states state; NK_UNUSED(item_padding); NK_ASSERT(ctx); NK_ASSERT(ctx->current); NK_ASSERT(ctx->current->layout); if (!ctx || !ctx->current || !ctx->current->layout) return NK_WIDGET_INVALID; state = nk_widget(bounds, ctx); return state; } NK_API void nk_spacing(struct nk_context *ctx, int cols) { struct nk_window *win; struct nk_panel *layout; struct nk_rect none; int i, index, rows; NK_ASSERT(ctx); NK_ASSERT(ctx->current); NK_ASSERT(ctx->current->layout); if (!ctx || !ctx->current || !ctx->current->layout) return; /* spacing over row boundaries */ win = ctx->current; layout = win->layout; index = (layout->row.index + cols) % layout->row.columns; rows = (layout->row.index + cols) / layout->row.columns; if (rows) { for (i = 0; i < rows; ++i) nk_panel_alloc_row(ctx, win); cols = index; } /* non table layout need to allocate space */ if (layout->row.type != NK_LAYOUT_DYNAMIC_FIXED && layout->row.type != NK_LAYOUT_STATIC_FIXED) { for (i = 0; i < cols; ++i) nk_panel_alloc_space(&none, ctx); } layout->row.index = index; } /* =============================================================== * * TEXT * * ===============================================================*/ NK_LIB void nk_widget_text(struct nk_command_buffer *o, struct nk_rect b, const char *string, int len, const struct nk_text *t, nk_flags a, const struct nk_user_font *f) { struct nk_rect label; float text_width; NK_ASSERT(o); NK_ASSERT(t); if (!o || !t) return; b.h = NK_MAX(b.h, 2 * t->padding.y); label.x = 0; label.w = 0; label.y = b.y + t->padding.y; label.h = NK_MIN(f->height, b.h - 2 * t->padding.y); text_width = f->width(f->userdata, f->height, (const char*)string, len); text_width += (2.0f * t->padding.x); /* align in x-axis */ if (a & NK_TEXT_ALIGN_LEFT) { label.x = b.x + t->padding.x; label.w = NK_MAX(0, b.w - 2 * t->padding.x); } else if (a & NK_TEXT_ALIGN_CENTERED) { label.w = NK_MAX(1, 2 * t->padding.x + (float)text_width); label.x = (b.x + t->padding.x + ((b.w - 2 * t->padding.x) - label.w) / 2); label.x = NK_MAX(b.x + t->padding.x, label.x); label.w = NK_MIN(b.x + b.w, label.x + label.w); if (label.w >= label.x) label.w -= label.x; } else if (a & NK_TEXT_ALIGN_RIGHT) { label.x = NK_MAX(b.x + t->padding.x, (b.x + b.w) - (2 * t->padding.x + (float)text_width)); label.w = (float)text_width + 2 * t->padding.x; } else return; /* align in y-axis */ if (a & NK_TEXT_ALIGN_MIDDLE) { label.y = b.y + b.h/2.0f - (float)f->height/2.0f; label.y += t->padding.y; //< @r-lyeh label.h = NK_MAX(b.h/2.0f, b.h - (b.h/2.0f + f->height/2.0f)); } else if (a & NK_TEXT_ALIGN_BOTTOM) { label.y = b.y + b.h - f->height; label.y += t->padding.y; //< @r-lyeh label.h = f->height; } nk_draw_text(o, label, (const char*)string, len, f, t->background, t->text); } NK_LIB void nk_widget_text_wrap(struct nk_command_buffer *o, struct nk_rect b, const char *string, int len, const struct nk_text *t, const struct nk_user_font *f) { float width; int glyphs = 0; int fitting = 0; int done = 0; struct nk_rect line; struct nk_text text; NK_INTERN nk_rune seperator[] = {' '}; NK_ASSERT(o); NK_ASSERT(t); if (!o || !t) return; text.padding = nk_vec2(0,0); text.background = t->background; text.text = t->text; b.w = NK_MAX(b.w, 2 * t->padding.x); b.h = NK_MAX(b.h, 2 * t->padding.y); b.h = b.h - 2 * t->padding.y; line.x = b.x + t->padding.x; line.y = b.y + t->padding.y; line.w = b.w - 2 * t->padding.x; line.h = 2 * t->padding.y + f->height; fitting = nk_text_clamp(f, string, len, line.w, &glyphs, &width, seperator,NK_LEN(seperator)); while (done < len) { if (!fitting || line.y + line.h >= (b.y + b.h)) break; nk_widget_text(o, line, &string[done], fitting, &text, NK_TEXT_LEFT, f); done += fitting; line.y += f->height + 2 * t->padding.y; fitting = nk_text_clamp(f, &string[done], len - done, line.w, &glyphs, &width, seperator,NK_LEN(seperator)); } } NK_API void nk_text_colored(struct nk_context *ctx, const char *str, int len, nk_flags alignment, struct nk_color color) { struct nk_window *win; const struct nk_style *style; struct nk_vec2 item_padding; struct nk_rect bounds; struct nk_text text; NK_ASSERT(ctx); NK_ASSERT(ctx->current); NK_ASSERT(ctx->current->layout); if (!ctx || !ctx->current || !ctx->current->layout) return; win = ctx->current; style = &ctx->style; nk_panel_alloc_space(&bounds, ctx); item_padding = style->text.padding; text.padding.x = item_padding.x; text.padding.y = item_padding.y; text.background = style->window.background; text.text = color; nk_widget_text(&win->buffer, bounds, str, len, &text, alignment, style->font); } NK_API void nk_text_wrap_colored(struct nk_context *ctx, const char *str, int len, struct nk_color color) { struct nk_window *win; const struct nk_style *style; struct nk_vec2 item_padding; struct nk_rect bounds; struct nk_text text; NK_ASSERT(ctx); NK_ASSERT(ctx->current); NK_ASSERT(ctx->current->layout); if (!ctx || !ctx->current || !ctx->current->layout) return; win = ctx->current; style = &ctx->style; nk_panel_alloc_space(&bounds, ctx); item_padding = style->text.padding; text.padding.x = item_padding.x; text.padding.y = item_padding.y; text.background = style->window.background; text.text = color; nk_widget_text_wrap(&win->buffer, bounds, str, len, &text, style->font); } #ifdef NK_INCLUDE_STANDARD_VARARGS NK_API void nk_labelf_colored(struct nk_context *ctx, nk_flags flags, struct nk_color color, const char *fmt, ...) { va_list args; va_start(args, fmt); nk_labelfv_colored(ctx, flags, color, fmt, args); va_end(args); } NK_API void nk_labelf_colored_wrap(struct nk_context *ctx, struct nk_color color, const char *fmt, ...) { va_list args; va_start(args, fmt); nk_labelfv_colored_wrap(ctx, color, fmt, args); va_end(args); } NK_API void nk_labelf(struct nk_context *ctx, nk_flags flags, const char *fmt, ...) { va_list args; va_start(args, fmt); nk_labelfv(ctx, flags, fmt, args); va_end(args); } NK_API void nk_labelf_wrap(struct nk_context *ctx, const char *fmt,...) { va_list args; va_start(args, fmt); nk_labelfv_wrap(ctx, fmt, args); va_end(args); } NK_API void nk_labelfv_colored(struct nk_context *ctx, nk_flags flags, struct nk_color color, const char *fmt, va_list args) { char buf[256]; nk_strfmt(buf, NK_LEN(buf), fmt, args); nk_label_colored(ctx, buf, flags, color); } NK_API void nk_labelfv_colored_wrap(struct nk_context *ctx, struct nk_color color, const char *fmt, va_list args) { char buf[256]; nk_strfmt(buf, NK_LEN(buf), fmt, args); nk_label_colored_wrap(ctx, buf, color); } NK_API void nk_labelfv(struct nk_context *ctx, nk_flags flags, const char *fmt, va_list args) { char buf[256]; nk_strfmt(buf, NK_LEN(buf), fmt, args); nk_label(ctx, buf, flags); } NK_API void nk_labelfv_wrap(struct nk_context *ctx, const char *fmt, va_list args) { char buf[256]; nk_strfmt(buf, NK_LEN(buf), fmt, args); nk_label_wrap(ctx, buf); } NK_API void nk_value_bool(struct nk_context *ctx, const char *prefix, int value) { nk_labelf(ctx, NK_TEXT_LEFT, "%s: %s", prefix, ((value) ? "true": "false")); } NK_API void nk_value_int(struct nk_context *ctx, const char *prefix, int value) { nk_labelf(ctx, NK_TEXT_LEFT, "%s: %d", prefix, value); } NK_API void nk_value_uint(struct nk_context *ctx, const char *prefix, unsigned int value) { nk_labelf(ctx, NK_TEXT_LEFT, "%s: %u", prefix, value); } NK_API void nk_value_float(struct nk_context *ctx, const char *prefix, float value) { double double_value = (double)value; nk_labelf(ctx, NK_TEXT_LEFT, "%s: %.3f", prefix, double_value); } NK_API void nk_value_color_byte(struct nk_context *ctx, const char *p, struct nk_color c) { nk_labelf(ctx, NK_TEXT_LEFT, "%s: (%d, %d, %d, %d)", p, c.r, c.g, c.b, c.a); } NK_API void nk_value_color_float(struct nk_context *ctx, const char *p, struct nk_color color) { double c[4]; nk_color_dv(c, color); nk_labelf(ctx, NK_TEXT_LEFT, "%s: (%.2f, %.2f, %.2f, %.2f)", p, c[0], c[1], c[2], c[3]); } NK_API void nk_value_color_hex(struct nk_context *ctx, const char *prefix, struct nk_color color) { char hex[16]; nk_color_hex_rgba(hex, color); nk_labelf(ctx, NK_TEXT_LEFT, "%s: %s", prefix, hex); } #endif NK_API void nk_text(struct nk_context *ctx, const char *str, int len, nk_flags alignment) { NK_ASSERT(ctx); if (!ctx) return; nk_text_colored(ctx, str, len, alignment, ctx->style.text.color); } NK_API void nk_text_wrap(struct nk_context *ctx, const char *str, int len) { NK_ASSERT(ctx); if (!ctx) return; nk_text_wrap_colored(ctx, str, len, ctx->style.text.color); } NK_API void nk_label(struct nk_context *ctx, const char *str, nk_flags alignment) { nk_text(ctx, str, nk_strlen(str), alignment); } NK_API void nk_label_colored(struct nk_context *ctx, const char *str, nk_flags align, struct nk_color color) { nk_text_colored(ctx, str, nk_strlen(str), align, color); } NK_API void nk_label_wrap(struct nk_context *ctx, const char *str) { nk_text_wrap(ctx, str, nk_strlen(str)); } NK_API void nk_label_colored_wrap(struct nk_context *ctx, const char *str, struct nk_color color) { nk_text_wrap_colored(ctx, str, nk_strlen(str), color); } /* =============================================================== * * IMAGE * * ===============================================================*/ NK_API nk_handle nk_handle_ptr(void *ptr) { nk_handle handle = {0}; handle.ptr = ptr; return handle; } NK_API nk_handle nk_handle_id(int id) { nk_handle handle; nk_zero_struct(handle); handle.id = id; return handle; } NK_API struct nk_image nk_subimage_ptr(void *ptr, nk_ushort w, nk_ushort h, struct nk_rect r) { struct nk_image s; nk_zero(&s, sizeof(s)); s.handle.ptr = ptr; s.w = w; s.h = h; s.region[0] = (nk_ushort)r.x; s.region[1] = (nk_ushort)r.y; s.region[2] = (nk_ushort)r.w; s.region[3] = (nk_ushort)r.h; return s; } NK_API struct nk_image nk_subimage_id(int id, nk_ushort w, nk_ushort h, struct nk_rect r) { struct nk_image s; nk_zero(&s, sizeof(s)); s.handle.id = id; s.w = w; s.h = h; s.region[0] = (nk_ushort)r.x; s.region[1] = (nk_ushort)r.y; s.region[2] = (nk_ushort)r.w; s.region[3] = (nk_ushort)r.h; return s; } NK_API struct nk_image nk_subimage_handle(nk_handle handle, nk_ushort w, nk_ushort h, struct nk_rect r) { struct nk_image s; nk_zero(&s, sizeof(s)); s.handle = handle; s.w = w; s.h = h; s.region[0] = (nk_ushort)r.x; s.region[1] = (nk_ushort)r.y; s.region[2] = (nk_ushort)r.w; s.region[3] = (nk_ushort)r.h; return s; } NK_API struct nk_image nk_image_handle(nk_handle handle) { struct nk_image s; nk_zero(&s, sizeof(s)); s.handle = handle; s.w = 0; s.h = 0; s.region[0] = 0; s.region[1] = 0; s.region[2] = 0; s.region[3] = 0; return s; } NK_API struct nk_image nk_image_ptr(void *ptr) { struct nk_image s; nk_zero(&s, sizeof(s)); NK_ASSERT(ptr); s.handle.ptr = ptr; s.w = 0; s.h = 0; s.region[0] = 0; s.region[1] = 0; s.region[2] = 0; s.region[3] = 0; return s; } NK_API struct nk_image nk_image_id(int id) { struct nk_image s; nk_zero(&s, sizeof(s)); s.handle.id = id; s.w = 0; s.h = 0; s.region[0] = 0; s.region[1] = 0; s.region[2] = 0; s.region[3] = 0; return s; } NK_API nk_bool nk_image_is_subimage(const struct nk_image* img) { NK_ASSERT(img); return !(img->w == 0 && img->h == 0); } NK_API void nk_image(struct nk_context *ctx, struct nk_image img) { struct nk_window *win; struct nk_rect bounds; NK_ASSERT(ctx); NK_ASSERT(ctx->current); NK_ASSERT(ctx->current->layout); if (!ctx || !ctx->current || !ctx->current->layout) return; win = ctx->current; if (!nk_widget(&bounds, ctx)) return; nk_draw_image(&win->buffer, bounds, &img, nk_white); } NK_API void nk_image_color(struct nk_context *ctx, struct nk_image img, struct nk_color col) { struct nk_window *win; struct nk_rect bounds; NK_ASSERT(ctx); NK_ASSERT(ctx->current); NK_ASSERT(ctx->current->layout); if (!ctx || !ctx->current || !ctx->current->layout) return; win = ctx->current; if (!nk_widget(&bounds, ctx)) return; nk_draw_image(&win->buffer, bounds, &img, col); } /* =============================================================== * * 9-SLICE * * ===============================================================*/ NK_API struct nk_nine_slice nk_sub9slice_ptr(void *ptr, nk_ushort w, nk_ushort h, struct nk_rect rgn, nk_ushort l, nk_ushort t, nk_ushort r, nk_ushort b) { struct nk_nine_slice s; struct nk_image *i = &s.img; nk_zero(&s, sizeof(s)); i->handle.ptr = ptr; i->w = w; i->h = h; i->region[0] = (nk_ushort)rgn.x; i->region[1] = (nk_ushort)rgn.y; i->region[2] = (nk_ushort)rgn.w; i->region[3] = (nk_ushort)rgn.h; s.l = l; s.t = t; s.r = r; s.b = b; return s; } NK_API struct nk_nine_slice nk_sub9slice_id(int id, nk_ushort w, nk_ushort h, struct nk_rect rgn, nk_ushort l, nk_ushort t, nk_ushort r, nk_ushort b) { struct nk_nine_slice s; struct nk_image *i = &s.img; nk_zero(&s, sizeof(s)); i->handle.id = id; i->w = w; i->h = h; i->region[0] = (nk_ushort)rgn.x; i->region[1] = (nk_ushort)rgn.y; i->region[2] = (nk_ushort)rgn.w; i->region[3] = (nk_ushort)rgn.h; s.l = l; s.t = t; s.r = r; s.b = b; return s; } NK_API struct nk_nine_slice nk_sub9slice_handle(nk_handle handle, nk_ushort w, nk_ushort h, struct nk_rect rgn, nk_ushort l, nk_ushort t, nk_ushort r, nk_ushort b) { struct nk_nine_slice s; struct nk_image *i = &s.img; nk_zero(&s, sizeof(s)); i->handle = handle; i->w = w; i->h = h; i->region[0] = (nk_ushort)rgn.x; i->region[1] = (nk_ushort)rgn.y; i->region[2] = (nk_ushort)rgn.w; i->region[3] = (nk_ushort)rgn.h; s.l = l; s.t = t; s.r = r; s.b = b; return s; } NK_API struct nk_nine_slice nk_nine_slice_handle(nk_handle handle, nk_ushort l, nk_ushort t, nk_ushort r, nk_ushort b) { struct nk_nine_slice s; struct nk_image *i = &s.img; nk_zero(&s, sizeof(s)); i->handle = handle; i->w = 0; i->h = 0; i->region[0] = 0; i->region[1] = 0; i->region[2] = 0; i->region[3] = 0; s.l = l; s.t = t; s.r = r; s.b = b; return s; } NK_API struct nk_nine_slice nk_nine_slice_ptr(void *ptr, nk_ushort l, nk_ushort t, nk_ushort r, nk_ushort b) { struct nk_nine_slice s; struct nk_image *i = &s.img; nk_zero(&s, sizeof(s)); NK_ASSERT(ptr); i->handle.ptr = ptr; i->w = 0; i->h = 0; i->region[0] = 0; i->region[1] = 0; i->region[2] = 0; i->region[3] = 0; s.l = l; s.t = t; s.r = r; s.b = b; return s; } NK_API struct nk_nine_slice nk_nine_slice_id(int id, nk_ushort l, nk_ushort t, nk_ushort r, nk_ushort b) { struct nk_nine_slice s; struct nk_image *i = &s.img; nk_zero(&s, sizeof(s)); i->handle.id = id; i->w = 0; i->h = 0; i->region[0] = 0; i->region[1] = 0; i->region[2] = 0; i->region[3] = 0; s.l = l; s.t = t; s.r = r; s.b = b; return s; } NK_API int nk_nine_slice_is_sub9slice(const struct nk_nine_slice* slice) { NK_ASSERT(slice); return !(slice->img.w == 0 && slice->img.h == 0); } #define NK_GLYPH_CHEVRON_DOWN_ ICON_MD_EXPAND_MORE //< @r-lyeh #define NK_GLYPH_CHEVRON_LEFT_ ICON_MD_KEYBOARD_ARROW_LEFT // NAVIGATE_BEFORE // ARROW_BACKWARD_IOS // CHEVRON_LEFT //< @r-lyeh #define NK_GLYPH_CHEVRON_RIGHT_ ICON_MD_KEYBOARD_ARROW_RIGHT // NAVIGATE_NEXT // ARROW_FORWARD_IOS // CHEVRON_RIGHT //< @r-lyeh #define NK_GLYPH_CHEVRON_UP_ ICON_MD_EXPAND_LESS //< @r-lyeh #define NK_GLYPH_SQUARE_XMARK_ ICON_MD_DISABLED_BY_DEFAULT //< @r-lyeh #define NK_GLYPH_CIRCLE_XMARK_ ICON_MD_HIGHLIGHT_OFF // CANCEL //< @r-lyeh #define NK_GLYPH_XMARK_ ICON_MD_CLOSE //< @r-lyeh #define NK_GLYPH_CARET_DOWN_ ICON_MD_ARROW_DROP_DOWN //< @r-lyeh #define NK_GLYPH_CARET_LEFT_ ICON_MD_ARROW_LEFT //< @r-lyeh #define NK_GLYPH_CARET_RIGHT_ ICON_MD_ARROW_RIGHT //< @r-lyeh #define NK_GLYPH_CARET_UP_ ICON_MD_ARROW_DROP_UP //< @r-lyeh /* ============================================================== * * BUTTON * * ===============================================================*/ NK_LIB void nk_draw_symbol(struct nk_command_buffer *out, enum nk_symbol_type type, struct nk_rect content, struct nk_color background, struct nk_color foreground, float border_width, const struct nk_user_font *font) { switch (type) { case NK_SYMBOL_FULLSCREEN: //< @r-lyeh case NK_SYMBOL_RESTORE: //< @r-lyeh case NK_SYMBOL_PIN: //< @r-lyeh case NK_SYMBOL_FLOATING: //< @r-lyeh case NK_SYMBOL_X: case NK_SYMBOL_UNDERSCORE: case NK_SYMBOL_PLUS: case NK_SYMBOL_MINUS: { /* single character text symbol */ const char *X = (type == NK_SYMBOL_X) ? ICON_MD_CLOSE /*NK_GLYPH_CIRCLE_XMARK_*/: //< @r-lyeh: utf8 (type == NK_SYMBOL_UNDERSCORE) ? "_": //< @r-lyeh: utf8 (type == NK_SYMBOL_FLOATING) ? ICON_MD_PUSH_PIN /*ICON_MD_CROP*/: //< @r-lyeh: utf8 (type == NK_SYMBOL_PIN) ? ICON_MD_PUSH_PIN: //< @r-lyeh: utf8 (type == NK_SYMBOL_FULLSCREEN) ? ICON_MD_FULLSCREEN : //< @r-lyeh: utf8 (type == NK_SYMBOL_RESTORE) ? ICON_MD_FULLSCREEN_EXIT : //< @r-lyeh: utf8 (type == NK_SYMBOL_PLUS) ? NK_GLYPH_CHEVRON_RIGHT_ : NK_GLYPH_CHEVRON_DOWN_; //< @r-lyeh: utf8 struct nk_text text = {0}; // text.background = background; //< @r-lyeh: not used text.text.r = text.text.g = text.text.b = 255; text.text.a = type == NK_SYMBOL_PIN /*|| type == NK_SYMBOL_MINUS*/ ? 192 : 96; //< @r-lyeh: color+alpha nk_widget_text(out, content, X, strlen(X), &text, NK_TEXT_CENTERED, font); //< @r-lyeh: strlen } break; case NK_SYMBOL_CIRCLE_SOLID: case NK_SYMBOL_CIRCLE_OUTLINE: case NK_SYMBOL_RECT_SOLID: case NK_SYMBOL_RECT_OUTLINE: { /* simple empty/filled shapes */ if (type == NK_SYMBOL_RECT_SOLID || type == NK_SYMBOL_RECT_OUTLINE) { nk_fill_rect(out, content, 0, foreground); if (type == NK_SYMBOL_RECT_OUTLINE) nk_fill_rect(out, nk_shrink_rect(content, border_width), 0, background); } else { nk_fill_circle(out, content, foreground); if (type == NK_SYMBOL_CIRCLE_OUTLINE) nk_fill_circle(out, nk_shrink_rect(content, 1), background); } } break; case NK_SYMBOL_TRIANGLE_UP: case NK_SYMBOL_TRIANGLE_DOWN: case NK_SYMBOL_TRIANGLE_LEFT: case NK_SYMBOL_TRIANGLE_RIGHT: { /* single character text symbol */ //< @r-lyeh const char *X = (type == NK_SYMBOL_TRIANGLE_UP) ? NK_GLYPH_CARET_UP_: //< @r-lyeh (type == NK_SYMBOL_TRIANGLE_DOWN) ? NK_GLYPH_CARET_DOWN_: //< @r-lyeh (type == NK_SYMBOL_TRIANGLE_LEFT) ? NK_GLYPH_CARET_LEFT_ : NK_GLYPH_CARET_RIGHT_; //< @r-lyeh struct nk_text text; //< @r-lyeh text.padding = nk_vec2(-6,-2); //< @r-lyeh: 0,0 for FA text.background = background; //< @r-lyeh text.text = foreground; //< @r-lyeh nk_widget_text(out, content, X, strlen(X), &text, NK_TEXT_LEFT|NK_TEXT_ALIGN_BOTTOM, font); //< @r-lyeh } break; default: case NK_SYMBOL_NONE: case NK_SYMBOL_MAX: break; } } NK_LIB nk_bool nk_button_behavior(nk_flags *state, struct nk_rect r, const struct nk_input *i, enum nk_button_behavior behavior) { int ret = 0; nk_widget_state_reset(state); if (!i) return 0; if (nk_input_is_mouse_hovering_rect(i, r)) { *state = NK_WIDGET_STATE_HOVERED; if (nk_input_is_mouse_down(i, NK_BUTTON_LEFT)) *state = NK_WIDGET_STATE_ACTIVE; if (nk_input_has_mouse_click_in_rect(i, NK_BUTTON_LEFT, r)) { ret = (behavior != NK_BUTTON_DEFAULT) ? nk_input_is_mouse_down(i, NK_BUTTON_LEFT): #ifdef NK_BUTTON_TRIGGER_ON_RELEASE nk_input_is_mouse_released(i, NK_BUTTON_LEFT); #else nk_input_is_mouse_pressed(i, NK_BUTTON_LEFT); #endif } } if (*state & NK_WIDGET_STATE_HOVER && !nk_input_is_mouse_prev_hovering_rect(i, r)) *state |= NK_WIDGET_STATE_ENTERED; else if (nk_input_is_mouse_prev_hovering_rect(i, r)) *state |= NK_WIDGET_STATE_LEFT; return ret; } NK_LIB const struct nk_style_item* nk_draw_button(struct nk_command_buffer *out, const struct nk_rect *bounds, nk_flags state, const struct nk_style_button *style) { const struct nk_style_item *background; if (state & NK_WIDGET_STATE_HOVER) background = &style->hover; else if (state & NK_WIDGET_STATE_ACTIVED) background = &style->active; else background = &style->normal; switch(background->type) { case NK_STYLE_ITEM_IMAGE: nk_draw_image(out, *bounds, &background->data.image, nk_white); break; case NK_STYLE_ITEM_NINE_SLICE: nk_draw_nine_slice(out, *bounds, &background->data.slice, nk_white); break; case NK_STYLE_ITEM_COLOR: nk_fill_rect(out, *bounds, style->rounding, background->data.color); nk_stroke_rect(out, *bounds, style->rounding, style->border, style->border_color); break; } return background; } NK_LIB nk_bool nk_do_button(nk_flags *state, struct nk_command_buffer *out, struct nk_rect r, const struct nk_style_button *style, const struct nk_input *in, enum nk_button_behavior behavior, struct nk_rect *content) { struct nk_rect bounds; NK_ASSERT(style); NK_ASSERT(state); NK_ASSERT(out); if (!out || !style) return nk_false; /* calculate button content space */ content->x = r.x + style->padding.x + style->border + style->rounding; content->y = r.y + style->padding.y + style->border + style->rounding; content->w = r.w - (2 * style->padding.x + style->border + style->rounding*2); content->h = r.h - (2 * style->padding.y + style->border + style->rounding*2); /* execute button behavior */ bounds.x = r.x - style->touch_padding.x; bounds.y = r.y - style->touch_padding.y; bounds.w = r.w + 2 * style->touch_padding.x; bounds.h = r.h + 2 * style->touch_padding.y; return nk_button_behavior(state, bounds, in, behavior); } NK_LIB void nk_draw_button_text(struct nk_command_buffer *out, const struct nk_rect *bounds, const struct nk_rect *content, nk_flags state, const struct nk_style_button *style, const char *txt, int len, nk_flags text_alignment, const struct nk_user_font *font) { struct nk_text text; const struct nk_style_item *background; background = nk_draw_button(out, bounds, state, style); /* select correct colors/images */ if (background->type == NK_STYLE_ITEM_COLOR) text.background = background->data.color; else text.background = style->text_background; if (state & NK_WIDGET_STATE_HOVER) text.text = style->text_hover; else if (state & NK_WIDGET_STATE_ACTIVED) text.text = style->text_active; else text.text = style->text_normal; text.padding = nk_vec2(0,0); nk_widget_text(out, *content, txt, len, &text, text_alignment, font); } NK_LIB nk_bool nk_do_button_text(nk_flags *state, struct nk_command_buffer *out, struct nk_rect bounds, const char *string, int len, nk_flags align, enum nk_button_behavior behavior, const struct nk_style_button *style, const struct nk_input *in, const struct nk_user_font *font) { struct nk_rect content; int ret = nk_false; NK_ASSERT(state); NK_ASSERT(style); NK_ASSERT(out); NK_ASSERT(string); NK_ASSERT(font); if (!out || !style || !font || !string) return nk_false; ret = nk_do_button(state, out, bounds, style, in, behavior, &content); if (style->draw_begin) style->draw_begin(out, style->userdata); nk_draw_button_text(out, &bounds, &content, *state, style, string, len, align, font); if (style->draw_end) style->draw_end(out, style->userdata); return ret; } NK_LIB void nk_draw_button_symbol(struct nk_command_buffer *out, const struct nk_rect *bounds, const struct nk_rect *content, nk_flags state, const struct nk_style_button *style, enum nk_symbol_type type, const struct nk_user_font *font) { struct nk_color sym, bg; const struct nk_style_item *background; /* select correct colors/images */ background = nk_draw_button(out, bounds, state, style); if (background->type == NK_STYLE_ITEM_COLOR) bg = background->data.color; else bg = style->text_background; if (state & NK_WIDGET_STATE_HOVER) sym = style->text_hover; else if (state & NK_WIDGET_STATE_ACTIVED) sym = style->text_active; else sym = style->text_normal; nk_draw_symbol(out, type, *content, bg, sym, 1, font); } NK_LIB nk_bool nk_do_button_symbol(nk_flags *state, struct nk_command_buffer *out, struct nk_rect bounds, enum nk_symbol_type symbol, enum nk_button_behavior behavior, const struct nk_style_button *style, const struct nk_input *in, const struct nk_user_font *font) { int ret; struct nk_rect content; NK_ASSERT(state); NK_ASSERT(style); NK_ASSERT(font); NK_ASSERT(out); if (!out || !style || !font || !state) return nk_false; ret = nk_do_button(state, out, bounds, style, in, behavior, &content); if (style->draw_begin) style->draw_begin(out, style->userdata); nk_draw_button_symbol(out, &bounds, &content, *state, style, symbol, font); if (style->draw_end) style->draw_end(out, style->userdata); return ret; } NK_LIB void nk_draw_button_image(struct nk_command_buffer *out, const struct nk_rect *bounds, const struct nk_rect *content, nk_flags state, const struct nk_style_button *style, const struct nk_image *img) { float a = style->text_normal.a / 255.f; nk_draw_button(out, bounds, state, style); nk_draw_image(out, *content, img, nk_rgba_f(a,a,a,1)); // @r-lyeh: nk_white -> nk_rgba_f } NK_LIB nk_bool nk_do_button_image(nk_flags *state, struct nk_command_buffer *out, struct nk_rect bounds, struct nk_image img, enum nk_button_behavior b, const struct nk_style_button *style, const struct nk_input *in) { int ret; struct nk_rect content; NK_ASSERT(state); NK_ASSERT(style); NK_ASSERT(out); if (!out || !style || !state) return nk_false; ret = nk_do_button(state, out, bounds, style, in, b, &content); content.x += style->image_padding.x; content.y += style->image_padding.y; content.w -= 2 * style->image_padding.x; content.h -= 2 * style->image_padding.y; if (style->draw_begin) style->draw_begin(out, style->userdata); nk_draw_button_image(out, &bounds, &content, *state, style, &img); if (style->draw_end) style->draw_end(out, style->userdata); return ret; } NK_LIB void nk_draw_button_text_symbol(struct nk_command_buffer *out, const struct nk_rect *bounds, const struct nk_rect *label, const struct nk_rect *symbol, nk_flags state, const struct nk_style_button *style, const char *str, int len, enum nk_symbol_type type, const struct nk_user_font *font) { struct nk_color sym; struct nk_text text; const struct nk_style_item *background; /* select correct background colors/images */ background = nk_draw_button(out, bounds, state, style); if (background->type == NK_STYLE_ITEM_COLOR) text.background = background->data.color; else text.background = style->text_background; /* select correct text colors */ if (state & NK_WIDGET_STATE_HOVER) { sym = style->text_hover; text.text = style->text_hover; } else if (state & NK_WIDGET_STATE_ACTIVED) { sym = style->text_active; text.text = style->text_active; } else { sym = style->text_normal; text.text = style->text_normal; } text.padding = nk_vec2(0,0); nk_draw_symbol(out, type, *symbol, style->text_background, sym, 0, font); nk_widget_text(out, *label, str, len, &text, NK_TEXT_CENTERED, font); } NK_LIB nk_bool nk_do_button_text_symbol(nk_flags *state, struct nk_command_buffer *out, struct nk_rect bounds, enum nk_symbol_type symbol, const char *str, int len, nk_flags align, enum nk_button_behavior behavior, const struct nk_style_button *style, const struct nk_user_font *font, const struct nk_input *in) { int ret; struct nk_rect tri = {0,0,0,0}; struct nk_rect content; NK_ASSERT(style); NK_ASSERT(out); NK_ASSERT(font); if (!out || !style || !font) return nk_false; ret = nk_do_button(state, out, bounds, style, in, behavior, &content); tri.y = content.y + (content.h/2) - font->height/2; tri.w = font->height; tri.h = font->height; if (align & NK_TEXT_ALIGN_LEFT) { tri.x = (content.x + content.w) - (2 * style->padding.x + tri.w); tri.x = NK_MAX(tri.x, 0); } else tri.x = content.x + 2 * style->padding.x; /* draw button */ if (style->draw_begin) style->draw_begin(out, style->userdata); nk_draw_button_text_symbol(out, &bounds, &content, &tri, *state, style, str, len, symbol, font); if (style->draw_end) style->draw_end(out, style->userdata); return ret; } NK_LIB void nk_draw_button_text_image(struct nk_command_buffer *out, const struct nk_rect *bounds, const struct nk_rect *label, const struct nk_rect *image, nk_flags state, const struct nk_style_button *style, const char *str, int len, const struct nk_user_font *font, const struct nk_image *img, nk_flags align) //< @r-lyeh: add align param { struct nk_text text; const struct nk_style_item *background; background = nk_draw_button(out, bounds, state, style); /* select correct colors */ if (background->type == NK_STYLE_ITEM_COLOR) text.background = background->data.color; else text.background = style->text_background; if (state & NK_WIDGET_STATE_HOVER) text.text = style->text_hover; else if (state & NK_WIDGET_STATE_ACTIVED) text.text = style->text_active; else text.text = style->text_normal; text.padding = nk_vec2(0,0); nk_widget_text(out, *label, str, len, &text, align/*NK_TEXT_CENTERED*/, font); //< @r-lyeh: add align param nk_draw_image(out, *image, img, nk_white); } NK_LIB nk_bool nk_do_button_text_image(nk_flags *state, struct nk_command_buffer *out, struct nk_rect bounds, struct nk_image img, const char* str, int len, nk_flags align, enum nk_button_behavior behavior, const struct nk_style_button *style, const struct nk_user_font *font, const struct nk_input *in) { int ret; struct nk_rect icon; struct nk_rect content; NK_ASSERT(style); NK_ASSERT(state); NK_ASSERT(font); NK_ASSERT(out); if (!out || !font || !style || !str) return nk_false; ret = nk_do_button(state, out, bounds, style, in, behavior, &content); icon.y = bounds.y + style->padding.y; icon.w = icon.h = bounds.h - 2 * style->padding.y; #if 1 //< @r-lyeh reworked whole block: counterpart image align: img right when text_align_left, img left when text_align_right if (align & NK_TEXT_ALIGN_LEFT) { icon.x = (bounds.x + bounds.w) - (2 * style->padding.x) - icon.w; } else { // CENTERED and RIGHT icon.x = bounds.x + 2 * style->padding.x; } #endif icon.x += style->image_padding.x; icon.y += style->image_padding.y; icon.w -= 2 * style->image_padding.x; icon.h -= 2 * style->image_padding.y; if (style->draw_begin) style->draw_begin(out, style->userdata); nk_draw_button_text_image(out, &bounds, &content, &icon, *state, style, str, len, font, &img, align); //< @r-lyeh: add align param if (style->draw_end) style->draw_end(out, style->userdata); return ret; } NK_API void nk_button_set_behavior(struct nk_context *ctx, enum nk_button_behavior behavior) { NK_ASSERT(ctx); if (!ctx) return; ctx->button_behavior = behavior; } NK_API nk_bool nk_button_push_behavior(struct nk_context *ctx, enum nk_button_behavior behavior) { struct nk_config_stack_button_behavior *button_stack; struct nk_config_stack_button_behavior_element *element; NK_ASSERT(ctx); if (!ctx) return 0; button_stack = &ctx->stacks.button_behaviors; NK_ASSERT(button_stack->head < (int)NK_LEN(button_stack->elements)); if (button_stack->head >= (int)NK_LEN(button_stack->elements)) return 0; element = &button_stack->elements[button_stack->head++]; element->address = &ctx->button_behavior; element->old_value = ctx->button_behavior; ctx->button_behavior = behavior; return 1; } NK_API nk_bool nk_button_pop_behavior(struct nk_context *ctx) { struct nk_config_stack_button_behavior *button_stack; struct nk_config_stack_button_behavior_element *element; NK_ASSERT(ctx); if (!ctx) return 0; button_stack = &ctx->stacks.button_behaviors; NK_ASSERT(button_stack->head > 0); if (button_stack->head < 1) return 0; element = &button_stack->elements[--button_stack->head]; *element->address = element->old_value; return 1; } NK_API nk_bool nk_button_text_styled(struct nk_context *ctx, const struct nk_style_button *style, const char *title, int len) { struct nk_window *win; struct nk_panel *layout; const struct nk_input *in; struct nk_rect bounds; enum nk_widget_layout_states state; NK_ASSERT(ctx); NK_ASSERT(style); NK_ASSERT(ctx->current); NK_ASSERT(ctx->current->layout); if (!style || !ctx || !ctx->current || !ctx->current->layout) return 0; win = ctx->current; layout = win->layout; state = nk_widget(&bounds, ctx); if (!state) return 0; in = (state == NK_WIDGET_ROM || layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input; return nk_do_button_text(&ctx->last_widget_state, &win->buffer, bounds, title, len, style->text_alignment, ctx->button_behavior, style, in, ctx->style.font); } NK_API nk_bool nk_button_text(struct nk_context *ctx, const char *title, int len) { NK_ASSERT(ctx); if (!ctx) return 0; return nk_button_text_styled(ctx, &ctx->style.button, title, len); } NK_API nk_bool nk_button_label_styled(struct nk_context *ctx, const struct nk_style_button *style, const char *title) { return nk_button_text_styled(ctx, style, title, nk_strlen(title)); } NK_API nk_bool nk_button_label(struct nk_context *ctx, const char *title) { return nk_button_text(ctx, title, nk_strlen(title)); } NK_API nk_bool nk_button_color(struct nk_context *ctx, struct nk_color color) { struct nk_window *win; struct nk_panel *layout; const struct nk_input *in; struct nk_style_button button; int ret = 0; struct nk_rect bounds; struct nk_rect content; enum nk_widget_layout_states state; NK_ASSERT(ctx); NK_ASSERT(ctx->current); NK_ASSERT(ctx->current->layout); if (!ctx || !ctx->current || !ctx->current->layout) return 0; win = ctx->current; layout = win->layout; state = nk_widget(&bounds, ctx); if (!state) return 0; in = (state == NK_WIDGET_ROM || layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input; button = ctx->style.button; button.normal = nk_style_item_color(color); button.hover = nk_style_item_color(color); button.active = nk_style_item_color(color); ret = nk_do_button(&ctx->last_widget_state, &win->buffer, bounds, &button, in, ctx->button_behavior, &content); nk_draw_button(&win->buffer, &bounds, ctx->last_widget_state, &button); return ret; } NK_API nk_bool nk_button_symbol_styled(struct nk_context *ctx, const struct nk_style_button *style, enum nk_symbol_type symbol) { struct nk_window *win; struct nk_panel *layout; const struct nk_input *in; struct nk_rect bounds; enum nk_widget_layout_states state; NK_ASSERT(ctx); NK_ASSERT(ctx->current); NK_ASSERT(ctx->current->layout); if (!ctx || !ctx->current || !ctx->current->layout) return 0; win = ctx->current; layout = win->layout; state = nk_widget(&bounds, ctx); if (!state) return 0; in = (state == NK_WIDGET_ROM || layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input; return nk_do_button_symbol(&ctx->last_widget_state, &win->buffer, bounds, symbol, ctx->button_behavior, style, in, ctx->style.font); } NK_API nk_bool nk_button_symbol(struct nk_context *ctx, enum nk_symbol_type symbol) { NK_ASSERT(ctx); if (!ctx) return 0; return nk_button_symbol_styled(ctx, &ctx->style.button, symbol); } NK_API nk_bool nk_button_image_styled(struct nk_context *ctx, const struct nk_style_button *style, struct nk_image img) { struct nk_window *win; struct nk_panel *layout; const struct nk_input *in; struct nk_rect bounds; enum nk_widget_layout_states state; NK_ASSERT(ctx); NK_ASSERT(ctx->current); NK_ASSERT(ctx->current->layout); if (!ctx || !ctx->current || !ctx->current->layout) return 0; win = ctx->current; layout = win->layout; state = nk_widget(&bounds, ctx); if (!state) return 0; in = (state == NK_WIDGET_ROM || layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input; return nk_do_button_image(&ctx->last_widget_state, &win->buffer, bounds, img, ctx->button_behavior, style, in); } NK_API nk_bool nk_button_image(struct nk_context *ctx, struct nk_image img) { NK_ASSERT(ctx); if (!ctx) return 0; return nk_button_image_styled(ctx, &ctx->style.button, img); } NK_API nk_bool nk_button_symbol_text_styled(struct nk_context *ctx, const struct nk_style_button *style, enum nk_symbol_type symbol, const char *text, int len, nk_flags align) { struct nk_window *win; struct nk_panel *layout; const struct nk_input *in; struct nk_rect bounds; enum nk_widget_layout_states state; NK_ASSERT(ctx); NK_ASSERT(ctx->current); NK_ASSERT(ctx->current->layout); if (!ctx || !ctx->current || !ctx->current->layout) return 0; win = ctx->current; layout = win->layout; state = nk_widget(&bounds, ctx); if (!state) return 0; in = (state == NK_WIDGET_ROM || layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input; return nk_do_button_text_symbol(&ctx->last_widget_state, &win->buffer, bounds, symbol, text, len, align, ctx->button_behavior, style, ctx->style.font, in); } NK_API nk_bool nk_button_symbol_text(struct nk_context *ctx, enum nk_symbol_type symbol, const char* text, int len, nk_flags align) { NK_ASSERT(ctx); if (!ctx) return 0; return nk_button_symbol_text_styled(ctx, &ctx->style.button, symbol, text, len, align); } NK_API nk_bool nk_button_symbol_label(struct nk_context *ctx, enum nk_symbol_type symbol, const char *label, nk_flags align) { return nk_button_symbol_text(ctx, symbol, label, nk_strlen(label), align); } NK_API nk_bool nk_button_symbol_label_styled(struct nk_context *ctx, const struct nk_style_button *style, enum nk_symbol_type symbol, const char *title, nk_flags align) { return nk_button_symbol_text_styled(ctx, style, symbol, title, nk_strlen(title), align); } NK_API nk_bool nk_button_image_text_styled(struct nk_context *ctx, const struct nk_style_button *style, struct nk_image img, const char *text, int len, nk_flags align) { struct nk_window *win; struct nk_panel *layout; const struct nk_input *in; struct nk_rect bounds; enum nk_widget_layout_states state; NK_ASSERT(ctx); NK_ASSERT(ctx->current); NK_ASSERT(ctx->current->layout); if (!ctx || !ctx->current || !ctx->current->layout) return 0; win = ctx->current; layout = win->layout; state = nk_widget(&bounds, ctx); if (!state) return 0; in = (state == NK_WIDGET_ROM || layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input; return nk_do_button_text_image(&ctx->last_widget_state, &win->buffer, bounds, img, text, len, align, ctx->button_behavior, style, ctx->style.font, in); } NK_API nk_bool nk_button_image_text(struct nk_context *ctx, struct nk_image img, const char *text, int len, nk_flags align) { return nk_button_image_text_styled(ctx, &ctx->style.button,img, text, len, align); } NK_API nk_bool nk_button_image_label(struct nk_context *ctx, struct nk_image img, const char *label, nk_flags align) { return nk_button_image_text(ctx, img, label, nk_strlen(label), align); } NK_API nk_bool nk_button_image_label_styled(struct nk_context *ctx, const struct nk_style_button *style, struct nk_image img, const char *label, nk_flags text_alignment) { return nk_button_image_text_styled(ctx, style, img, label, nk_strlen(label), text_alignment); } /* =============================================================== * * TOGGLE * * ===============================================================*/ NK_LIB nk_bool nk_toggle_behavior(const struct nk_input *in, struct nk_rect select, nk_flags *state, nk_bool active) { nk_widget_state_reset(state); if (nk_button_behavior(state, select, in, NK_BUTTON_DEFAULT)) { *state = NK_WIDGET_STATE_ACTIVE; active = !active; } if (*state & NK_WIDGET_STATE_HOVER && !nk_input_is_mouse_prev_hovering_rect(in, select)) *state |= NK_WIDGET_STATE_ENTERED; else if (nk_input_is_mouse_prev_hovering_rect(in, select)) *state |= NK_WIDGET_STATE_LEFT; return active; } NK_LIB void nk_draw_checkbox(struct nk_command_buffer *out, nk_flags state, const struct nk_style_toggle *style, nk_bool active, const struct nk_rect *label, const struct nk_rect *selector, const struct nk_rect *cursors, const char *string, int len, const struct nk_user_font *font) { const struct nk_style_item *background; const struct nk_style_item *cursor; struct nk_text text; /* select correct colors/images */ if (state & NK_WIDGET_STATE_HOVER) { background = &style->hover; cursor = &style->cursor_hover; text.text = style->text_hover; } else if (state & NK_WIDGET_STATE_ACTIVED) { background = &style->hover; cursor = &style->cursor_hover; text.text = style->text_active; } else { background = &style->normal; cursor = &style->cursor_normal; text.text = style->text_normal; } /* draw background and cursor */ if (background->type == NK_STYLE_ITEM_COLOR) { nk_fill_rect(out, *selector, 0, style->border_color); nk_fill_rect(out, nk_shrink_rect(*selector, style->border), 0, background->data.color); } else nk_draw_image(out, *selector, &background->data.image, nk_white); if (active) { if (cursor->type == NK_STYLE_ITEM_IMAGE) nk_draw_image(out, *cursors, &cursor->data.image, nk_white); else nk_fill_rect(out, *cursors, 0, cursor->data.color); } text.padding.x = 0; text.padding.y = 0; text.background = style->text_background; nk_widget_text(out, *label, string, len, &text, NK_TEXT_LEFT, font); } NK_LIB void nk_draw_option(struct nk_command_buffer *out, nk_flags state, const struct nk_style_toggle *style, nk_bool active, const struct nk_rect *label, const struct nk_rect *selector, const struct nk_rect *cursors, const char *string, int len, const struct nk_user_font *font) { const struct nk_style_item *background; const struct nk_style_item *cursor; struct nk_text text; /* select correct colors/images */ if (state & NK_WIDGET_STATE_HOVER) { background = &style->hover; cursor = &style->cursor_hover; text.text = style->text_hover; } else if (state & NK_WIDGET_STATE_ACTIVED) { background = &style->hover; cursor = &style->cursor_hover; text.text = style->text_active; } else { background = &style->normal; cursor = &style->cursor_normal; text.text = style->text_normal; } /* draw background and cursor */ if (background->type == NK_STYLE_ITEM_COLOR) { nk_fill_circle(out, *selector, style->border_color); nk_fill_circle(out, nk_shrink_rect(*selector, style->border), background->data.color); } else nk_draw_image(out, *selector, &background->data.image, nk_white); if (active) { if (cursor->type == NK_STYLE_ITEM_IMAGE) nk_draw_image(out, *cursors, &cursor->data.image, nk_white); else nk_fill_circle(out, *cursors, cursor->data.color); } text.padding.x = 0; text.padding.y = 0; text.background = style->text_background; nk_widget_text(out, *label, string, len, &text, NK_TEXT_LEFT, font); } NK_LIB nk_bool nk_do_toggle(nk_flags *state, struct nk_command_buffer *out, struct nk_rect r, nk_bool *active, const char *str, int len, enum nk_toggle_type type, const struct nk_style_toggle *style, const struct nk_input *in, const struct nk_user_font *font) { int was_active; struct nk_rect bounds; struct nk_rect select; struct nk_rect cursor; struct nk_rect label; NK_ASSERT(style); NK_ASSERT(out); NK_ASSERT(font); if (!out || !style || !font || !active) return 0; r.w = NK_MAX(r.w, font->height + 2 * style->padding.x); r.h = NK_MAX(r.h, font->height + 2 * style->padding.y); /* add additional touch padding for touch screen devices */ bounds.x = r.x - style->touch_padding.x; bounds.y = r.y - style->touch_padding.y; bounds.w = r.w + 2 * style->touch_padding.x; bounds.h = r.h + 2 * style->touch_padding.y; /* calculate the selector space */ select.w = font->height; select.h = select.w; select.y = r.y + r.h/2.0f - select.h/2.0f; select.x = r.x; /* calculate the bounds of the cursor inside the selector */ cursor.x = select.x + style->padding.x + style->border; cursor.y = select.y + style->padding.y + style->border; cursor.w = select.w - (2 * style->padding.x + 2 * style->border); cursor.h = select.h - (2 * style->padding.y + 2 * style->border); /* label behind the selector */ label.x = select.x + select.w + style->spacing; label.y = select.y; label.w = NK_MAX(r.x + r.w, label.x) - label.x; label.h = select.w; /* update selector */ was_active = *active; *active = nk_toggle_behavior(in, bounds, state, *active); /* draw selector */ if (style->draw_begin) style->draw_begin(out, style->userdata); if (type == NK_TOGGLE_CHECK) { nk_draw_checkbox(out, *state, style, *active, &label, &select, &cursor, str, len, font); } else { nk_draw_option(out, *state, style, *active, &label, &select, &cursor, str, len, font); } if (style->draw_end) style->draw_end(out, style->userdata); return (was_active != *active); } /*---------------------------------------------------------------- * * CHECKBOX * * --------------------------------------------------------------*/ NK_API nk_bool nk_check_text(struct nk_context *ctx, const char *text, int len, nk_bool active) { struct nk_window *win; struct nk_panel *layout; const struct nk_input *in; const struct nk_style *style; struct nk_rect bounds; enum nk_widget_layout_states state; NK_ASSERT(ctx); NK_ASSERT(ctx->current); NK_ASSERT(ctx->current->layout); if (!ctx || !ctx->current || !ctx->current->layout) return active; win = ctx->current; style = &ctx->style; layout = win->layout; state = nk_widget(&bounds, ctx); if (!state) return active; in = (state == NK_WIDGET_ROM || layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input; nk_do_toggle(&ctx->last_widget_state, &win->buffer, bounds, &active, text, len, NK_TOGGLE_CHECK, &style->checkbox, in, style->font); return active; } NK_API unsigned int nk_check_flags_text(struct nk_context *ctx, const char *text, int len, unsigned int flags, unsigned int value) { int old_active; NK_ASSERT(ctx); NK_ASSERT(text); if (!ctx || !text) return flags; old_active = (int)((flags & value) & value); if (nk_check_text(ctx, text, len, old_active)) flags |= value; else flags &= ~value; return flags; } NK_API nk_bool nk_checkbox_text(struct nk_context *ctx, const char *text, int len, nk_bool *active) { int old_val; NK_ASSERT(ctx); NK_ASSERT(text); NK_ASSERT(active); if (!ctx || !text || !active) return 0; old_val = *active; *active = nk_check_text(ctx, text, len, *active); return old_val != *active; } NK_API nk_bool nk_checkbox_flags_text(struct nk_context *ctx, const char *text, int len, unsigned int *flags, unsigned int value) { nk_bool active; NK_ASSERT(ctx); NK_ASSERT(text); NK_ASSERT(flags); if (!ctx || !text || !flags) return 0; active = (int)((*flags & value) & value); if (nk_checkbox_text(ctx, text, len, &active)) { if (active) *flags |= value; else *flags &= ~value; return 1; } return 0; } NK_API nk_bool nk_check_label(struct nk_context *ctx, const char *label, nk_bool active) { return nk_check_text(ctx, label, nk_strlen(label), active); } NK_API unsigned int nk_check_flags_label(struct nk_context *ctx, const char *label, unsigned int flags, unsigned int value) { return nk_check_flags_text(ctx, label, nk_strlen(label), flags, value); } NK_API nk_bool nk_checkbox_label(struct nk_context *ctx, const char *label, nk_bool *active) { return nk_checkbox_text(ctx, label, nk_strlen(label), active); } NK_API nk_bool nk_checkbox_flags_label(struct nk_context *ctx, const char *label, unsigned int *flags, unsigned int value) { return nk_checkbox_flags_text(ctx, label, nk_strlen(label), flags, value); } /*---------------------------------------------------------------- * * OPTION * * --------------------------------------------------------------*/ NK_API nk_bool nk_option_text(struct nk_context *ctx, const char *text, int len, nk_bool is_active) { struct nk_window *win; struct nk_panel *layout; const struct nk_input *in; const struct nk_style *style; struct nk_rect bounds; enum nk_widget_layout_states state; NK_ASSERT(ctx); NK_ASSERT(ctx->current); NK_ASSERT(ctx->current->layout); if (!ctx || !ctx->current || !ctx->current->layout) return is_active; win = ctx->current; style = &ctx->style; layout = win->layout; state = nk_widget(&bounds, ctx); if (!state) return (int)state; in = (state == NK_WIDGET_ROM || layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input; nk_do_toggle(&ctx->last_widget_state, &win->buffer, bounds, &is_active, text, len, NK_TOGGLE_OPTION, &style->option, in, style->font); return is_active; } NK_API nk_bool nk_radio_text(struct nk_context *ctx, const char *text, int len, nk_bool *active) { int old_value; NK_ASSERT(ctx); NK_ASSERT(text); NK_ASSERT(active); if (!ctx || !text || !active) return 0; old_value = *active; *active = nk_option_text(ctx, text, len, old_value); return old_value != *active; } NK_API nk_bool nk_option_label(struct nk_context *ctx, const char *label, nk_bool active) { return nk_option_text(ctx, label, nk_strlen(label), active); } NK_API nk_bool nk_radio_label(struct nk_context *ctx, const char *label, nk_bool *active) { return nk_radio_text(ctx, label, nk_strlen(label), active); } /* =============================================================== * * SELECTABLE * * ===============================================================*/ NK_LIB void nk_draw_selectable(struct nk_command_buffer *out, nk_flags state, const struct nk_style_selectable *style, nk_bool active, const struct nk_rect *bounds, const struct nk_rect *icon, const struct nk_image *img, enum nk_symbol_type sym, const char *string, int len, nk_flags align, const struct nk_user_font *font) { const struct nk_style_item *background; struct nk_text text; text.padding = style->padding; /* select correct colors/images */ if (!active) { if (state & NK_WIDGET_STATE_ACTIVED) { background = &style->pressed; text.text = style->text_pressed; } else if (state & NK_WIDGET_STATE_HOVER) { background = &style->hover; text.text = style->text_hover; } else { background = &style->normal; text.text = style->text_normal; } } else { if (state & NK_WIDGET_STATE_ACTIVED) { background = &style->pressed_active; text.text = style->text_pressed_active; } else if (state & NK_WIDGET_STATE_HOVER) { background = &style->hover_active; text.text = style->text_hover_active; } else { background = &style->normal_active; text.text = style->text_normal_active; } } /* draw selectable background and text */ switch (background->type) { case NK_STYLE_ITEM_IMAGE: text.background = nk_rgba(0, 0, 0, 0); nk_draw_image(out, *bounds, &background->data.image, nk_white); break; case NK_STYLE_ITEM_NINE_SLICE: text.background = nk_rgba(0, 0, 0, 0); nk_draw_nine_slice(out, *bounds, &background->data.slice, nk_white); break; case NK_STYLE_ITEM_COLOR: text.background = background->data.color; nk_fill_rect(out, *bounds, style->rounding, background->data.color); break; } if (icon) { if (img) nk_draw_image(out, *icon, img, nk_white); else nk_draw_symbol(out, sym, *icon, text.background, text.text, 1, font); } nk_widget_text(out, *bounds, string, len, &text, align, font); } NK_LIB nk_bool nk_do_selectable(nk_flags *state, struct nk_command_buffer *out, struct nk_rect bounds, const char *str, int len, nk_flags align, nk_bool *value, const struct nk_style_selectable *style, const struct nk_input *in, const struct nk_user_font *font) { int old_value; struct nk_rect touch; NK_ASSERT(state); NK_ASSERT(out); NK_ASSERT(str); NK_ASSERT(len); NK_ASSERT(value); NK_ASSERT(style); NK_ASSERT(font); if (!state || !out || !str || !len || !value || !style || !font) return 0; old_value = *value; /* remove padding */ touch.x = bounds.x - style->touch_padding.x; touch.y = bounds.y - style->touch_padding.y; touch.w = bounds.w + style->touch_padding.x * 2; touch.h = bounds.h + style->touch_padding.y * 2; /* update button */ if (nk_button_behavior(state, touch, in, NK_BUTTON_DEFAULT)) *value = !(*value); /* draw selectable */ if (style->draw_begin) style->draw_begin(out, style->userdata); nk_draw_selectable(out, *state, style, *value, &bounds, 0,0,NK_SYMBOL_NONE, str, len, align, font); if (style->draw_end) style->draw_end(out, style->userdata); return old_value != *value; } NK_LIB nk_bool nk_do_selectable_image(nk_flags *state, struct nk_command_buffer *out, struct nk_rect bounds, const char *str, int len, nk_flags align, nk_bool *value, const struct nk_image *img, const struct nk_style_selectable *style, const struct nk_input *in, const struct nk_user_font *font) { nk_bool old_value; struct nk_rect touch; struct nk_rect icon; NK_ASSERT(state); NK_ASSERT(out); NK_ASSERT(str); NK_ASSERT(len); NK_ASSERT(value); NK_ASSERT(style); NK_ASSERT(font); if (!state || !out || !str || !len || !value || !style || !font) return 0; old_value = *value; /* toggle behavior */ touch.x = bounds.x - style->touch_padding.x; touch.y = bounds.y - style->touch_padding.y; touch.w = bounds.w + style->touch_padding.x * 2; touch.h = bounds.h + style->touch_padding.y * 2; if (nk_button_behavior(state, touch, in, NK_BUTTON_DEFAULT)) *value = !(*value); icon.y = bounds.y + style->padding.y; icon.w = icon.h = bounds.h - 2 * style->padding.y; if (align & NK_TEXT_ALIGN_LEFT) { icon.x = (bounds.x + bounds.w) - (2 * style->padding.x + icon.w); icon.x = NK_MAX(icon.x, 0); } else icon.x = bounds.x + 2 * style->padding.x; icon.x += style->image_padding.x; icon.y += style->image_padding.y; icon.w -= 2 * style->image_padding.x; icon.h -= 2 * style->image_padding.y; /* draw selectable */ if (style->draw_begin) style->draw_begin(out, style->userdata); nk_draw_selectable(out, *state, style, *value, &bounds, &icon, img, NK_SYMBOL_NONE, str, len, align, font); if (style->draw_end) style->draw_end(out, style->userdata); return old_value != *value; } NK_LIB nk_bool nk_do_selectable_symbol(nk_flags *state, struct nk_command_buffer *out, struct nk_rect bounds, const char *str, int len, nk_flags align, nk_bool *value, enum nk_symbol_type sym, const struct nk_style_selectable *style, const struct nk_input *in, const struct nk_user_font *font) { int old_value; struct nk_rect touch; struct nk_rect icon; NK_ASSERT(state); NK_ASSERT(out); NK_ASSERT(str); NK_ASSERT(len); NK_ASSERT(value); NK_ASSERT(style); NK_ASSERT(font); if (!state || !out || !str || !len || !value || !style || !font) return 0; old_value = *value; /* toggle behavior */ touch.x = bounds.x - style->touch_padding.x; touch.y = bounds.y - style->touch_padding.y; touch.w = bounds.w + style->touch_padding.x * 2; touch.h = bounds.h + style->touch_padding.y * 2; if (nk_button_behavior(state, touch, in, NK_BUTTON_DEFAULT)) *value = !(*value); icon.y = bounds.y + style->padding.y; icon.w = icon.h = bounds.h - 2 * style->padding.y; if (align & NK_TEXT_ALIGN_LEFT) { icon.x = (bounds.x + bounds.w) - (2 * style->padding.x + icon.w); icon.x = NK_MAX(icon.x, 0); } else icon.x = bounds.x + 2 * style->padding.x; icon.x += style->image_padding.x; icon.y += style->image_padding.y; icon.w -= 2 * style->image_padding.x; icon.h -= 2 * style->image_padding.y; /* draw selectable */ if (style->draw_begin) style->draw_begin(out, style->userdata); nk_draw_selectable(out, *state, style, *value, &bounds, &icon, 0, sym, str, len, align, font); if (style->draw_end) style->draw_end(out, style->userdata); return old_value != *value; } NK_API nk_bool nk_selectable_text(struct nk_context *ctx, const char *str, int len, nk_flags align, nk_bool *value) { struct nk_window *win; struct nk_panel *layout; const struct nk_input *in; const struct nk_style *style; enum nk_widget_layout_states state; struct nk_rect bounds; NK_ASSERT(ctx); NK_ASSERT(value); NK_ASSERT(ctx->current); NK_ASSERT(ctx->current->layout); if (!ctx || !ctx->current || !ctx->current->layout || !value) return 0; win = ctx->current; layout = win->layout; style = &ctx->style; state = nk_widget(&bounds, ctx); if (!state) return 0; in = (state == NK_WIDGET_ROM || layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input; return nk_do_selectable(&ctx->last_widget_state, &win->buffer, bounds, str, len, align, value, &style->selectable, in, style->font); } NK_API nk_bool nk_selectable_image_text(struct nk_context *ctx, struct nk_image img, const char *str, int len, nk_flags align, nk_bool *value) { struct nk_window *win; struct nk_panel *layout; const struct nk_input *in; const struct nk_style *style; enum nk_widget_layout_states state; struct nk_rect bounds; NK_ASSERT(ctx); NK_ASSERT(value); NK_ASSERT(ctx->current); NK_ASSERT(ctx->current->layout); if (!ctx || !ctx->current || !ctx->current->layout || !value) return 0; win = ctx->current; layout = win->layout; style = &ctx->style; state = nk_widget(&bounds, ctx); if (!state) return 0; in = (state == NK_WIDGET_ROM || layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input; return nk_do_selectable_image(&ctx->last_widget_state, &win->buffer, bounds, str, len, align, value, &img, &style->selectable, in, style->font); } NK_API nk_bool nk_selectable_symbol_text(struct nk_context *ctx, enum nk_symbol_type sym, const char *str, int len, nk_flags align, nk_bool *value) { struct nk_window *win; struct nk_panel *layout; const struct nk_input *in; const struct nk_style *style; enum nk_widget_layout_states state; struct nk_rect bounds; NK_ASSERT(ctx); NK_ASSERT(value); NK_ASSERT(ctx->current); NK_ASSERT(ctx->current->layout); if (!ctx || !ctx->current || !ctx->current->layout || !value) return 0; win = ctx->current; layout = win->layout; style = &ctx->style; state = nk_widget(&bounds, ctx); if (!state) return 0; in = (state == NK_WIDGET_ROM || layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input; return nk_do_selectable_symbol(&ctx->last_widget_state, &win->buffer, bounds, str, len, align, value, sym, &style->selectable, in, style->font); } NK_API nk_bool nk_selectable_symbol_label(struct nk_context *ctx, enum nk_symbol_type sym, const char *title, nk_flags align, nk_bool *value) { return nk_selectable_symbol_text(ctx, sym, title, nk_strlen(title), align, value); } NK_API nk_bool nk_select_text(struct nk_context *ctx, const char *str, int len, nk_flags align, nk_bool value) { nk_selectable_text(ctx, str, len, align, &value);return value; } NK_API nk_bool nk_selectable_label(struct nk_context *ctx, const char *str, nk_flags align, nk_bool *value) { return nk_selectable_text(ctx, str, nk_strlen(str), align, value); } NK_API nk_bool nk_selectable_image_label(struct nk_context *ctx,struct nk_image img, const char *str, nk_flags align, nk_bool *value) { return nk_selectable_image_text(ctx, img, str, nk_strlen(str), align, value); } NK_API nk_bool nk_select_label(struct nk_context *ctx, const char *str, nk_flags align, nk_bool value) { nk_selectable_text(ctx, str, nk_strlen(str), align, &value);return value; } NK_API nk_bool nk_select_image_label(struct nk_context *ctx, struct nk_image img, const char *str, nk_flags align, nk_bool value) { nk_selectable_image_text(ctx, img, str, nk_strlen(str), align, &value);return value; } NK_API nk_bool nk_select_image_text(struct nk_context *ctx, struct nk_image img, const char *str, int len, nk_flags align, nk_bool value) { nk_selectable_image_text(ctx, img, str, len, align, &value);return value; } NK_API nk_bool nk_select_symbol_text(struct nk_context *ctx, enum nk_symbol_type sym, const char *title, int title_len, nk_flags align, nk_bool value) { nk_selectable_symbol_text(ctx, sym, title, title_len, align, &value);return value; } NK_API nk_bool nk_select_symbol_label(struct nk_context *ctx, enum nk_symbol_type sym, const char *title, nk_flags align, nk_bool value) { return nk_select_symbol_text(ctx, sym, title, nk_strlen(title), align, value); } /* =============================================================== * * SLIDER * * ===============================================================*/ NK_LIB float nk_slider_behavior(nk_flags *state, struct nk_rect *logical_cursor, struct nk_rect *visual_cursor, struct nk_input *in, struct nk_rect bounds, float slider_min, float slider_max, float slider_value, float slider_step, float slider_steps) { int left_mouse_down; int left_mouse_click_in_cursor; /* check if visual cursor is being dragged */ nk_widget_state_reset(state); left_mouse_down = in && in->mouse.buttons[NK_BUTTON_LEFT].down; left_mouse_click_in_cursor = in && nk_input_has_mouse_click_down_in_rect(in, NK_BUTTON_LEFT, *visual_cursor, nk_true); if (left_mouse_down && left_mouse_click_in_cursor) { float ratio = 0; const float d = in->mouse.pos.x - (visual_cursor->x+visual_cursor->w*0.5f); const float pxstep = bounds.w / slider_steps; /* only update value if the next slider step is reached */ *state = NK_WIDGET_STATE_ACTIVE; if (NK_ABS(d) >= pxstep) { const float steps = (float)((int)(NK_ABS(d) / pxstep)); slider_value += (d > 0) ? (slider_step*steps) : -(slider_step*steps); slider_value = NK_CLAMP(slider_min, slider_value, slider_max); ratio = (slider_value - slider_min)/slider_step; logical_cursor->x = bounds.x + (logical_cursor->w * ratio); in->mouse.buttons[NK_BUTTON_LEFT].clicked_pos.x = logical_cursor->x; } } /* slider widget state */ if (nk_input_is_mouse_hovering_rect(in, bounds)) *state = NK_WIDGET_STATE_HOVERED; if (*state & NK_WIDGET_STATE_HOVER && !nk_input_is_mouse_prev_hovering_rect(in, bounds)) *state |= NK_WIDGET_STATE_ENTERED; else if (nk_input_is_mouse_prev_hovering_rect(in, bounds)) *state |= NK_WIDGET_STATE_LEFT; return slider_value; } NK_LIB void nk_draw_slider(struct nk_command_buffer *out, nk_flags state, const struct nk_style_slider *style, const struct nk_rect *bounds, const struct nk_rect *visual_cursor, float min, float value, float max) { struct nk_rect fill; struct nk_rect bar; const struct nk_style_item *background; /* select correct slider images/colors */ struct nk_color bar_color; const struct nk_style_item *cursor; NK_UNUSED(min); NK_UNUSED(max); NK_UNUSED(value); if (state & NK_WIDGET_STATE_ACTIVED) { background = &style->active; bar_color = style->bar_active; cursor = &style->cursor_active; } else if (state & NK_WIDGET_STATE_HOVER) { background = &style->hover; bar_color = style->bar_hover; cursor = &style->cursor_hover; } else { background = &style->normal; bar_color = style->bar_normal; cursor = &style->cursor_normal; } /* calculate slider background bar */ bar.x = bounds->x; bar.y = (visual_cursor->y + visual_cursor->h/2) - bounds->h/12; bar.w = bounds->w; bar.h = bounds->h/6; /* filled background bar style */ fill.w = (visual_cursor->x + (visual_cursor->w/2.0f)) - bar.x; fill.x = bar.x; fill.y = bar.y; fill.h = bar.h; /* draw background */ switch(background->type) { case NK_STYLE_ITEM_IMAGE: nk_draw_image(out, *bounds, &background->data.image, nk_white); break; case NK_STYLE_ITEM_NINE_SLICE: nk_draw_nine_slice(out, *bounds, &background->data.slice, nk_white); break; case NK_STYLE_ITEM_COLOR: nk_fill_rect(out, *bounds, style->rounding, background->data.color); nk_stroke_rect(out, *bounds, style->rounding, style->border, style->border_color); break; } /* draw slider bar */ nk_fill_rect(out, bar, style->rounding, bar_color); nk_fill_rect(out, fill, style->rounding, style->bar_filled); /* draw cursor */ if (cursor->type == NK_STYLE_ITEM_IMAGE) nk_draw_image(out, *visual_cursor, &cursor->data.image, nk_white); else nk_fill_circle(out, *visual_cursor, cursor->data.color); } NK_LIB float nk_do_slider(nk_flags *state, struct nk_command_buffer *out, struct nk_rect bounds, float min, float val, float max, float step, const struct nk_style_slider *style, struct nk_input *in, const struct nk_user_font *font) { float slider_range; float slider_min; float slider_max; float slider_value; float slider_steps; float cursor_offset; struct nk_rect visual_cursor; struct nk_rect logical_cursor; NK_ASSERT(style); NK_ASSERT(out); if (!out || !style) return 0; /* remove padding from slider bounds */ bounds.x = bounds.x + style->padding.x; bounds.y = bounds.y + style->padding.y; bounds.h = NK_MAX(bounds.h, 2*style->padding.y); bounds.w = NK_MAX(bounds.w, 2*style->padding.x + style->cursor_size.x); bounds.w -= 2 * style->padding.x; bounds.h -= 2 * style->padding.y; /* optional buttons */ if (style->show_buttons) { nk_flags ws; struct nk_rect button; button.y = bounds.y; button.w = bounds.h; button.h = bounds.h; /* decrement button */ button.x = bounds.x; if (nk_do_button_symbol(&ws, out, button, style->dec_symbol, NK_BUTTON_DEFAULT, &style->dec_button, in, font)) val -= step; /* increment button */ button.x = (bounds.x + bounds.w) - button.w; if (nk_do_button_symbol(&ws, out, button, style->inc_symbol, NK_BUTTON_DEFAULT, &style->inc_button, in, font)) val += step; bounds.x = bounds.x + button.w + style->spacing.x; bounds.w = bounds.w - (2*button.w + 2*style->spacing.x); } /* remove one cursor size to support visual cursor */ bounds.x += style->cursor_size.x*0.5f; bounds.w -= style->cursor_size.x; /* make sure the provided values are correct */ slider_max = NK_MAX(min, max); slider_min = NK_MIN(min, max); slider_value = NK_CLAMP(slider_min, val, slider_max); slider_range = slider_max - slider_min; slider_steps = slider_range / step; cursor_offset = (slider_value - slider_min) / step; /* calculate cursor Basically you have two cursors. One for visual representation and interaction and one for updating the actual cursor value. */ logical_cursor.h = bounds.h; logical_cursor.w = bounds.w / slider_steps; logical_cursor.x = bounds.x + (logical_cursor.w * cursor_offset); logical_cursor.y = bounds.y; visual_cursor.h = style->cursor_size.y; visual_cursor.w = style->cursor_size.x; visual_cursor.y = (bounds.y + bounds.h*0.5f) - visual_cursor.h*0.5f; visual_cursor.x = logical_cursor.x - visual_cursor.w*0.5f; slider_value = nk_slider_behavior(state, &logical_cursor, &visual_cursor, in, bounds, slider_min, slider_max, slider_value, step, slider_steps); visual_cursor.x = logical_cursor.x - visual_cursor.w*0.5f; /* draw slider */ if (style->draw_begin) style->draw_begin(out, style->userdata); nk_draw_slider(out, *state, style, &bounds, &visual_cursor, slider_min, slider_value, slider_max); if (style->draw_end) style->draw_end(out, style->userdata); return slider_value; } NK_API nk_bool nk_slider_float(struct nk_context *ctx, float min_value, float *value, float max_value, float value_step) { struct nk_window *win; struct nk_panel *layout; struct nk_input *in; const struct nk_style *style; int ret = 0; float old_value; struct nk_rect bounds; enum nk_widget_layout_states state; NK_ASSERT(ctx); NK_ASSERT(ctx->current); NK_ASSERT(ctx->current->layout); NK_ASSERT(value); if (!ctx || !ctx->current || !ctx->current->layout || !value) return ret; win = ctx->current; style = &ctx->style; layout = win->layout; state = nk_widget(&bounds, ctx); if (!state) return ret; in = (/*state == NK_WIDGET_ROM || */ layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input; old_value = *value; *value = nk_do_slider(&ctx->last_widget_state, &win->buffer, bounds, min_value, old_value, max_value, value_step, &style->slider, in, style->font); return (old_value > *value || old_value < *value); } NK_API float nk_slide_float(struct nk_context *ctx, float min, float val, float max, float step) { nk_slider_float(ctx, min, &val, max, step); return val; } NK_API int nk_slide_int(struct nk_context *ctx, int min, int val, int max, int step) { float value = (float)val; nk_slider_float(ctx, (float)min, &value, (float)max, (float)step); return (int)value; } NK_API nk_bool nk_slider_int(struct nk_context *ctx, int min, int *val, int max, int step) { int ret; float value = (float)*val; ret = nk_slider_float(ctx, (float)min, &value, (float)max, (float)step); *val = (int)value; return ret; } /* =============================================================== * * PROGRESS * * ===============================================================*/ NK_LIB nk_size nk_progress_behavior(nk_flags *state, struct nk_input *in, struct nk_rect r, struct nk_rect cursor, nk_size max, nk_size value, nk_bool modifiable) { int left_mouse_down = 0; int left_mouse_click_in_cursor = 0; nk_widget_state_reset(state); if (!in || !modifiable) return value; left_mouse_down = in && in->mouse.buttons[NK_BUTTON_LEFT].down; left_mouse_click_in_cursor = in && nk_input_has_mouse_click_down_in_rect(in, NK_BUTTON_LEFT, cursor, nk_true); if (nk_input_is_mouse_hovering_rect(in, r)) *state = NK_WIDGET_STATE_HOVERED; if (in && left_mouse_down && left_mouse_click_in_cursor) { if (left_mouse_down && left_mouse_click_in_cursor) { float ratio = NK_MAX(0, (float)(in->mouse.pos.x - cursor.x)) / (float)cursor.w; value = (nk_size)NK_CLAMP(0, (float)max * ratio, (float)max); in->mouse.buttons[NK_BUTTON_LEFT].clicked_pos.x = cursor.x + cursor.w/2.0f; *state |= NK_WIDGET_STATE_ACTIVE; } } /* set progressbar widget state */ if (*state & NK_WIDGET_STATE_HOVER && !nk_input_is_mouse_prev_hovering_rect(in, r)) *state |= NK_WIDGET_STATE_ENTERED; else if (nk_input_is_mouse_prev_hovering_rect(in, r)) *state |= NK_WIDGET_STATE_LEFT; return value; } NK_LIB void nk_draw_progress(struct nk_command_buffer *out, nk_flags state, const struct nk_style_progress *style, const struct nk_rect *bounds, const struct nk_rect *scursor, nk_size value, nk_size max) { const struct nk_style_item *background; const struct nk_style_item *cursor; NK_UNUSED(max); NK_UNUSED(value); /* select correct colors/images to draw */ if (state & NK_WIDGET_STATE_ACTIVED) { background = &style->active; cursor = &style->cursor_active; } else if (state & NK_WIDGET_STATE_HOVER){ background = &style->hover; cursor = &style->cursor_hover; } else { background = &style->normal; cursor = &style->cursor_normal; } /* draw background */ switch(background->type) { case NK_STYLE_ITEM_IMAGE: nk_draw_image(out, *bounds, &background->data.image, nk_white); break; case NK_STYLE_ITEM_NINE_SLICE: nk_draw_nine_slice(out, *bounds, &background->data.slice, nk_white); break; case NK_STYLE_ITEM_COLOR: nk_fill_rect(out, *bounds, style->rounding, background->data.color); nk_stroke_rect(out, *bounds, style->rounding, style->border, style->border_color); break; } /* draw cursor */ switch(cursor->type) { case NK_STYLE_ITEM_IMAGE: nk_draw_image(out, *scursor, &cursor->data.image, nk_white); break; case NK_STYLE_ITEM_NINE_SLICE: nk_draw_nine_slice(out, *scursor, &cursor->data.slice, nk_white); break; case NK_STYLE_ITEM_COLOR: nk_fill_rect(out, *scursor, style->rounding, cursor->data.color); nk_stroke_rect(out, *scursor, style->rounding, style->border, style->border_color); break; } } NK_LIB nk_size nk_do_progress(nk_flags *state, struct nk_command_buffer *out, struct nk_rect bounds, nk_size value, nk_size max, nk_bool modifiable, const struct nk_style_progress *style, struct nk_input *in) { float prog_scale; nk_size prog_value; struct nk_rect cursor; NK_ASSERT(style); NK_ASSERT(out); if (!out || !style) return 0; /* calculate progressbar cursor */ cursor.w = NK_MAX(bounds.w, 2 * style->padding.x + 2 * style->border); cursor.h = NK_MAX(bounds.h, 2 * style->padding.y + 2 * style->border); cursor = nk_pad_rect(bounds, nk_vec2(style->padding.x + style->border, style->padding.y + style->border)); prog_scale = (float)value / (float)max; /* update progressbar */ prog_value = NK_MIN(value, max); prog_value = nk_progress_behavior(state, in, bounds, cursor,max, prog_value, modifiable); cursor.w = cursor.w * prog_scale; /* draw progressbar */ if (style->draw_begin) style->draw_begin(out, style->userdata); nk_draw_progress(out, *state, style, &bounds, &cursor, value, max); if (style->draw_end) style->draw_end(out, style->userdata); return prog_value; } NK_API nk_bool nk_progress(struct nk_context *ctx, nk_size *cur, nk_size max, nk_bool is_modifyable) { struct nk_window *win; struct nk_panel *layout; const struct nk_style *style; struct nk_input *in; struct nk_rect bounds; enum nk_widget_layout_states state; nk_size old_value; NK_ASSERT(ctx); NK_ASSERT(cur); NK_ASSERT(ctx->current); NK_ASSERT(ctx->current->layout); if (!ctx || !ctx->current || !ctx->current->layout || !cur) return 0; win = ctx->current; style = &ctx->style; layout = win->layout; state = nk_widget(&bounds, ctx); if (!state) return 0; in = (state == NK_WIDGET_ROM || layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input; old_value = *cur; *cur = nk_do_progress(&ctx->last_widget_state, &win->buffer, bounds, *cur, max, is_modifyable, &style->progress, in); return (*cur != old_value); } NK_API nk_size nk_prog(struct nk_context *ctx, nk_size cur, nk_size max, nk_bool modifyable) { nk_progress(ctx, &cur, max, modifyable); return cur; } /* =============================================================== * * SCROLLBAR * * ===============================================================*/ NK_LIB float nk_scrollbar_behavior(nk_flags *state, struct nk_input *in, int has_scrolling, const struct nk_rect *scroll, const struct nk_rect *cursor, const struct nk_rect *empty0, const struct nk_rect *empty1, float scroll_offset, float target, float scroll_step, enum nk_orientation o) { nk_flags ws = 0; int left_mouse_down; unsigned int left_mouse_clicked; int left_mouse_click_in_cursor; float scroll_delta; nk_widget_state_reset(state); if (!in) return scroll_offset; left_mouse_down = in->mouse.buttons[NK_BUTTON_LEFT].down; left_mouse_clicked = in->mouse.buttons[NK_BUTTON_LEFT].clicked; left_mouse_click_in_cursor = nk_input_has_mouse_click_down_in_rect(in, NK_BUTTON_LEFT, *cursor, nk_true); if (nk_input_is_mouse_hovering_rect(in, *scroll)) *state = NK_WIDGET_STATE_HOVERED; scroll_delta = (o == NK_VERTICAL) ? in->mouse.scroll_delta.y: in->mouse.scroll_delta.x; if (left_mouse_down && left_mouse_click_in_cursor && !left_mouse_clicked) { /* update cursor by mouse dragging */ float pixel, delta; *state = NK_WIDGET_STATE_ACTIVE; if (o == NK_VERTICAL) { float cursor_y; pixel = in->mouse.delta.y; delta = (pixel / scroll->h) * target; scroll_offset = NK_CLAMP(0, scroll_offset + delta, target - scroll->h); cursor_y = scroll->y + ((scroll_offset/target) * scroll->h); in->mouse.buttons[NK_BUTTON_LEFT].clicked_pos.y = cursor_y + cursor->h/2.0f; } else { float cursor_x; pixel = in->mouse.delta.x; delta = (pixel / scroll->w) * target; scroll_offset = NK_CLAMP(0, scroll_offset + delta, target - scroll->w); cursor_x = scroll->x + ((scroll_offset/target) * scroll->w); in->mouse.buttons[NK_BUTTON_LEFT].clicked_pos.x = cursor_x + cursor->w/2.0f; } } else if ((nk_input_is_key_pressed(in, NK_KEY_SCROLL_UP) && o == NK_VERTICAL && has_scrolling)|| nk_button_behavior(&ws, *empty0, in, NK_BUTTON_DEFAULT)) { /* scroll page up by click on empty space or shortcut */ if (o == NK_VERTICAL) scroll_offset = NK_MAX(0, scroll_offset - scroll->h); else scroll_offset = NK_MAX(0, scroll_offset - scroll->w); } else if ((nk_input_is_key_pressed(in, NK_KEY_SCROLL_DOWN) && o == NK_VERTICAL && has_scrolling) || nk_button_behavior(&ws, *empty1, in, NK_BUTTON_DEFAULT)) { /* scroll page down by click on empty space or shortcut */ if (o == NK_VERTICAL) scroll_offset = NK_MIN(scroll_offset + scroll->h, target - scroll->h); else scroll_offset = NK_MIN(scroll_offset + scroll->w, target - scroll->w); } else if (has_scrolling) { if ((scroll_delta < 0 || (scroll_delta > 0))) { /* update cursor by mouse scrolling */ scroll_offset = scroll_offset + scroll_step * (-scroll_delta); if (o == NK_VERTICAL) scroll_offset = NK_CLAMP(0, scroll_offset, target - scroll->h); else scroll_offset = NK_CLAMP(0, scroll_offset, target - scroll->w); } else if (nk_input_is_key_pressed(in, NK_KEY_SCROLL_START)) { /* update cursor to the beginning */ if (o == NK_VERTICAL) scroll_offset = 0; } else if (nk_input_is_key_pressed(in, NK_KEY_SCROLL_END)) { /* update cursor to the end */ if (o == NK_VERTICAL) scroll_offset = target - scroll->h; } } if (*state & NK_WIDGET_STATE_HOVER && !nk_input_is_mouse_prev_hovering_rect(in, *scroll)) *state |= NK_WIDGET_STATE_ENTERED; else if (nk_input_is_mouse_prev_hovering_rect(in, *scroll)) *state |= NK_WIDGET_STATE_LEFT; return scroll_offset; } NK_LIB void nk_draw_scrollbar(struct nk_command_buffer *out, nk_flags state, const struct nk_style_scrollbar *style, const struct nk_rect *bounds, const struct nk_rect *scroll) { const struct nk_style_item *background; const struct nk_style_item *cursor; /* select correct colors/images to draw */ if (state & NK_WIDGET_STATE_ACTIVED) { background = &style->active; cursor = &style->cursor_active; } else if (state & NK_WIDGET_STATE_HOVER) { background = &style->hover; cursor = &style->cursor_hover; } else { background = &style->normal; cursor = &style->cursor_normal; } /* draw background */ switch (background->type) { case NK_STYLE_ITEM_IMAGE: nk_draw_image(out, *bounds, &background->data.image, nk_white); break; case NK_STYLE_ITEM_NINE_SLICE: nk_draw_nine_slice(out, *bounds, &background->data.slice, nk_white); break; case NK_STYLE_ITEM_COLOR: nk_fill_rect(out, *bounds, style->rounding, background->data.color); nk_stroke_rect(out, *bounds, style->rounding, style->border, style->border_color); break; } /* draw cursor */ switch (cursor->type) { case NK_STYLE_ITEM_IMAGE: nk_draw_image(out, *scroll, &cursor->data.image, nk_white); break; case NK_STYLE_ITEM_NINE_SLICE: nk_draw_nine_slice(out, *scroll, &cursor->data.slice, nk_white); break; case NK_STYLE_ITEM_COLOR: nk_fill_rect(out, *scroll, style->rounding_cursor, cursor->data.color); nk_stroke_rect(out, *scroll, style->rounding_cursor, style->border_cursor, style->cursor_border_color); break; } } NK_LIB float nk_do_scrollbarv(nk_flags *state, struct nk_command_buffer *out, struct nk_rect scroll, int has_scrolling, float offset, float target, float step, float button_pixel_inc, const struct nk_style_scrollbar *style, struct nk_input *in, const struct nk_user_font *font) { struct nk_rect empty_north; struct nk_rect empty_south; struct nk_rect cursor; float scroll_step; float scroll_offset; float scroll_off; float scroll_ratio; NK_ASSERT(out); NK_ASSERT(style); NK_ASSERT(state); if (!out || !style) return 0; scroll.w = NK_MAX(scroll.w, 1); scroll.h = NK_MAX(scroll.h, 0); if (target <= scroll.h) return 0; /* optional scrollbar buttons */ if (style->show_buttons) { nk_flags ws; float scroll_h; struct nk_rect button; button.x = scroll.x; button.w = scroll.w; button.h = scroll.w; scroll_h = NK_MAX(scroll.h - 2 * button.h,0); scroll_step = NK_MIN(step, button_pixel_inc); /* decrement button */ button.y = scroll.y; if (nk_do_button_symbol(&ws, out, button, style->dec_symbol, NK_BUTTON_REPEATER, &style->dec_button, in, font)) offset = offset - scroll_step; /* increment button */ button.y = scroll.y + scroll.h - button.h; if (nk_do_button_symbol(&ws, out, button, style->inc_symbol, NK_BUTTON_REPEATER, &style->inc_button, in, font)) offset = offset + scroll_step; scroll.y = scroll.y + button.h; scroll.h = scroll_h; } /* calculate scrollbar constants */ scroll_step = NK_MIN(step, scroll.h); scroll_offset = NK_CLAMP(0, offset, target - scroll.h); scroll_ratio = scroll.h / target; scroll_off = scroll_offset / target; /* calculate scrollbar cursor bounds */ cursor.h = NK_MAX((scroll_ratio * scroll.h) - (2*style->border + 2*style->padding.y), 0); cursor.y = scroll.y + (scroll_off * scroll.h) + style->border + style->padding.y; cursor.w = scroll.w - (2 * style->border + 2 * style->padding.x); cursor.x = scroll.x + style->border + style->padding.x; /* calculate empty space around cursor */ empty_north.x = scroll.x; empty_north.y = scroll.y; empty_north.w = scroll.w; empty_north.h = NK_MAX(cursor.y - scroll.y, 0); empty_south.x = scroll.x; empty_south.y = cursor.y + cursor.h; empty_south.w = scroll.w; empty_south.h = NK_MAX((scroll.y + scroll.h) - (cursor.y + cursor.h), 0); /* update scrollbar */ scroll_offset = nk_scrollbar_behavior(state, in, has_scrolling, &scroll, &cursor, &empty_north, &empty_south, scroll_offset, target, scroll_step, NK_VERTICAL); scroll_off = scroll_offset / target; cursor.y = scroll.y + (scroll_off * scroll.h) + style->border_cursor + style->padding.y; /* draw scrollbar */ if (style->draw_begin) style->draw_begin(out, style->userdata); nk_draw_scrollbar(out, *state, style, &scroll, &cursor); if (style->draw_end) style->draw_end(out, style->userdata); return scroll_offset; } NK_LIB float nk_do_scrollbarh(nk_flags *state, struct nk_command_buffer *out, struct nk_rect scroll, int has_scrolling, float offset, float target, float step, float button_pixel_inc, const struct nk_style_scrollbar *style, struct nk_input *in, const struct nk_user_font *font) { struct nk_rect cursor; struct nk_rect empty_west; struct nk_rect empty_east; float scroll_step; float scroll_offset; float scroll_off; float scroll_ratio; NK_ASSERT(out); NK_ASSERT(style); if (!out || !style) return 0; /* scrollbar background */ scroll.h = NK_MAX(scroll.h, 1); scroll.w = NK_MAX(scroll.w, 2 * scroll.h); if (target <= scroll.w) return 0; /* optional scrollbar buttons */ if (style->show_buttons) { nk_flags ws; float scroll_w; struct nk_rect button; button.y = scroll.y; button.w = scroll.h; button.h = scroll.h; scroll_w = scroll.w - 2 * button.w; scroll_step = NK_MIN(step, button_pixel_inc); /* decrement button */ button.x = scroll.x; if (nk_do_button_symbol(&ws, out, button, style->dec_symbol, NK_BUTTON_REPEATER, &style->dec_button, in, font)) offset = offset - scroll_step; /* increment button */ button.x = scroll.x + scroll.w - button.w; if (nk_do_button_symbol(&ws, out, button, style->inc_symbol, NK_BUTTON_REPEATER, &style->inc_button, in, font)) offset = offset + scroll_step; scroll.x = scroll.x + button.w; scroll.w = scroll_w; } /* calculate scrollbar constants */ scroll_step = NK_MIN(step, scroll.w); scroll_offset = NK_CLAMP(0, offset, target - scroll.w); scroll_ratio = scroll.w / target; scroll_off = scroll_offset / target; /* calculate cursor bounds */ cursor.w = (scroll_ratio * scroll.w) - (2*style->border + 2*style->padding.x); cursor.x = scroll.x + (scroll_off * scroll.w) + style->border + style->padding.x; cursor.h = scroll.h - (2 * style->border + 2 * style->padding.y); cursor.y = scroll.y + style->border + style->padding.y; /* calculate empty space around cursor */ empty_west.x = scroll.x; empty_west.y = scroll.y; empty_west.w = cursor.x - scroll.x; empty_west.h = scroll.h; empty_east.x = cursor.x + cursor.w; empty_east.y = scroll.y; empty_east.w = (scroll.x + scroll.w) - (cursor.x + cursor.w); empty_east.h = scroll.h; /* update scrollbar */ scroll_offset = nk_scrollbar_behavior(state, in, has_scrolling, &scroll, &cursor, &empty_west, &empty_east, scroll_offset, target, scroll_step, NK_HORIZONTAL); scroll_off = scroll_offset / target; cursor.x = scroll.x + (scroll_off * scroll.w); /* draw scrollbar */ if (style->draw_begin) style->draw_begin(out, style->userdata); nk_draw_scrollbar(out, *state, style, &scroll, &cursor); if (style->draw_end) style->draw_end(out, style->userdata); return scroll_offset; } /* =============================================================== * * TEXT EDITOR * * ===============================================================*/ /* stb_textedit.h - v1.8 - public domain - Sean Barrett */ struct nk_text_find { float x,y; /* position of n'th character */ float height; /* height of line */ int first_char, length; /* first char of row, and length */ int prev_first; /*_ first char of previous row */ }; struct nk_text_edit_row { float x0,x1; /* starting x location, end x location (allows for align=right, etc) */ float baseline_y_delta; /* position of baseline relative to previous row's baseline*/ float ymin,ymax; /* height of row above and below baseline */ int num_chars; }; /* forward declarations */ NK_INTERN void nk_textedit_makeundo_delete(struct nk_text_edit*, int, int); NK_INTERN void nk_textedit_makeundo_insert(struct nk_text_edit*, int, int); NK_INTERN void nk_textedit_makeundo_replace(struct nk_text_edit*, int, int, int); #define NK_TEXT_HAS_SELECTION(s) ((s)->select_start != (s)->select_end) NK_INTERN float nk_textedit_get_width(const struct nk_text_edit *edit, int line_start, int char_id, const struct nk_user_font *font) { int len = 0; nk_rune unicode = 0; const char *str = nk_str_at_const(&edit->string, line_start + char_id, &unicode, &len); return font->width(font->userdata, font->height, str, len); } NK_INTERN void nk_textedit_layout_row(struct nk_text_edit_row *r, struct nk_text_edit *edit, int line_start_id, float row_height, const struct nk_user_font *font) { int l; int glyphs = 0; nk_rune unicode; const char *remaining; int len = nk_str_len_char(&edit->string); const char *end = nk_str_get_const(&edit->string) + len; const char *text = nk_str_at_const(&edit->string, line_start_id, &unicode, &l); const struct nk_vec2 size = nk_text_calculate_text_bounds(font, text, (int)(end - text), row_height, &remaining, 0, &glyphs, NK_STOP_ON_NEW_LINE); r->x0 = 0.0f; r->x1 = size.x; r->baseline_y_delta = size.y; r->ymin = 0.0f; r->ymax = size.y; r->num_chars = glyphs; } NK_INTERN int nk_textedit_locate_coord(struct nk_text_edit *edit, float x, float y, const struct nk_user_font *font, float row_height) { struct nk_text_edit_row r; int n = edit->string.len; float base_y = 0, prev_x; int i=0, k; r.x0 = r.x1 = 0; r.ymin = r.ymax = 0; r.num_chars = 0; /* search rows to find one that straddles 'y' */ while (i < n) { nk_textedit_layout_row(&r, edit, i, row_height, font); if (r.num_chars <= 0) return n; if (i==0 && y < base_y + r.ymin) return 0; if (y < base_y + r.ymax) break; i += r.num_chars; base_y += r.baseline_y_delta; } /* below all text, return 'after' last character */ if (i >= n) return n; /* check if it's before the beginning of the line */ if (x < r.x0) return i; /* check if it's before the end of the line */ if (x < r.x1) { /* search characters in row for one that straddles 'x' */ k = i; prev_x = r.x0; for (i=0; i < r.num_chars; ++i) { float w = nk_textedit_get_width(edit, k, i, font); if (x < prev_x+w) { if (x < prev_x+w/2) return k+i; else return k+i+1; } prev_x += w; } /* shouldn't happen, but if it does, fall through to end-of-line case */ } /* if the last character is a newline, return that. * otherwise return 'after' the last character */ if (nk_str_rune_at(&edit->string, i+r.num_chars-1) == '\n') return i+r.num_chars-1; else return i+r.num_chars; } NK_LIB void nk_textedit_click(struct nk_text_edit *state, float x, float y, const struct nk_user_font *font, float row_height) { /* API click: on mouse down, move the cursor to the clicked location, * and reset the selection */ state->cursor = nk_textedit_locate_coord(state, x, y, font, row_height); state->select_start = state->cursor; state->select_end = state->cursor; state->has_preferred_x = 0; } NK_LIB void nk_textedit_drag(struct nk_text_edit *state, float x, float y, const struct nk_user_font *font, float row_height) { /* API drag: on mouse drag, move the cursor and selection endpoint * to the clicked location */ int p = nk_textedit_locate_coord(state, x, y, font, row_height); if (state->select_start == state->select_end) state->select_start = state->cursor; state->cursor = state->select_end = p; } NK_INTERN void nk_textedit_find_charpos(struct nk_text_find *find, struct nk_text_edit *state, int n, int single_line, const struct nk_user_font *font, float row_height) { /* find the x/y location of a character, and remember info about the previous * row in case we get a move-up event (for page up, we'll have to rescan) */ struct nk_text_edit_row r; int prev_start = 0; int z = state->string.len; int i=0, first; nk_zero_struct(r); if (n == z) { /* if it's at the end, then find the last line -- simpler than trying to explicitly handle this case in the regular code */ nk_textedit_layout_row(&r, state, 0, row_height, font); if (single_line) { find->first_char = 0; find->length = z; } else { while (i < z) { prev_start = i; i += r.num_chars; nk_textedit_layout_row(&r, state, i, row_height, font); } find->first_char = i; find->length = r.num_chars; } find->x = r.x1; find->y = r.ymin; find->height = r.ymax - r.ymin; find->prev_first = prev_start; return; } /* search rows to find the one that straddles character n */ find->y = 0; for(;;) { nk_textedit_layout_row(&r, state, i, row_height, font); if (n < i + r.num_chars) break; prev_start = i; i += r.num_chars; find->y += r.baseline_y_delta; } find->first_char = first = i; find->length = r.num_chars; find->height = r.ymax - r.ymin; find->prev_first = prev_start; /* now scan to find xpos */ find->x = r.x0; for (i=0; first+i < n; ++i) find->x += nk_textedit_get_width(state, first, i, font); } NK_INTERN void nk_textedit_clamp(struct nk_text_edit *state) { /* make the selection/cursor state valid if client altered the string */ int n = state->string.len; if (NK_TEXT_HAS_SELECTION(state)) { if (state->select_start > n) state->select_start = n; if (state->select_end > n) state->select_end = n; /* if clamping forced them to be equal, move the cursor to match */ if (state->select_start == state->select_end) state->cursor = state->select_start; } if (state->cursor > n) state->cursor = n; } NK_API void nk_textedit_delete(struct nk_text_edit *state, int where, int len) { /* delete characters while updating undo */ nk_textedit_makeundo_delete(state, where, len); nk_str_delete_runes(&state->string, where, len); state->has_preferred_x = 0; } NK_API void nk_textedit_delete_selection(struct nk_text_edit *state) { /* delete the section */ nk_textedit_clamp(state); if (NK_TEXT_HAS_SELECTION(state)) { if (state->select_start < state->select_end) { nk_textedit_delete(state, state->select_start, state->select_end - state->select_start); state->select_end = state->cursor = state->select_start; } else { nk_textedit_delete(state, state->select_end, state->select_start - state->select_end); state->select_start = state->cursor = state->select_end; } state->has_preferred_x = 0; } } NK_INTERN void nk_textedit_sortselection(struct nk_text_edit *state) { /* canonicalize the selection so start <= end */ if (state->select_end < state->select_start) { int temp = state->select_end; state->select_end = state->select_start; state->select_start = temp; } } NK_INTERN void nk_textedit_move_to_first(struct nk_text_edit *state) { /* move cursor to first character of selection */ if (NK_TEXT_HAS_SELECTION(state)) { nk_textedit_sortselection(state); state->cursor = state->select_start; state->select_end = state->select_start; state->has_preferred_x = 0; } } NK_INTERN void nk_textedit_move_to_last(struct nk_text_edit *state) { /* move cursor to last character of selection */ if (NK_TEXT_HAS_SELECTION(state)) { nk_textedit_sortselection(state); nk_textedit_clamp(state); state->cursor = state->select_end; state->select_start = state->select_end; state->has_preferred_x = 0; } } NK_INTERN int nk_is_word_boundary( struct nk_text_edit *state, int idx) { int len; nk_rune c; if (idx <= 0) return 1; if (!nk_str_at_rune(&state->string, idx, &c, &len)) return 1; return (c == ' ' || c == '\t' ||c == 0x3000 || c == ',' || c == ';' || c == '(' || c == ')' || c == '{' || c == '}' || c == '[' || c == ']' || c == '|'); } NK_INTERN int nk_textedit_move_to_word_previous(struct nk_text_edit *state) { int c = state->cursor - 1; while( c >= 0 && !nk_is_word_boundary(state, c)) --c; if( c < 0 ) c = 0; return c; } NK_INTERN int nk_textedit_move_to_word_next(struct nk_text_edit *state) { const int len = state->string.len; int c = state->cursor+1; while( c < len && !nk_is_word_boundary(state, c)) ++c; if( c > len ) c = len; return c; } NK_INTERN void nk_textedit_prep_selection_at_cursor(struct nk_text_edit *state) { /* update selection and cursor to match each other */ if (!NK_TEXT_HAS_SELECTION(state)) state->select_start = state->select_end = state->cursor; else state->cursor = state->select_end; } NK_API nk_bool nk_textedit_cut(struct nk_text_edit *state) { /* API cut: delete selection */ if (state->mode == NK_TEXT_EDIT_MODE_VIEW) return 0; if (NK_TEXT_HAS_SELECTION(state)) { nk_textedit_delete_selection(state); /* implicitly clamps */ state->has_preferred_x = 0; return 1; } return 0; } NK_API nk_bool nk_textedit_paste(struct nk_text_edit *state, char const *ctext, int len) { /* API paste: replace existing selection with passed-in text */ int glyphs; const char *text = (const char *) ctext; if (state->mode == NK_TEXT_EDIT_MODE_VIEW) return 0; /* if there's a selection, the paste should delete it */ nk_textedit_clamp(state); nk_textedit_delete_selection(state); /* try to insert the characters */ glyphs = nk_utf_len(ctext, len); if (nk_str_insert_text_char(&state->string, state->cursor, text, len)) { nk_textedit_makeundo_insert(state, state->cursor, glyphs); state->cursor += len; state->has_preferred_x = 0; return 1; } /* remove the undo since we didn't actually insert the characters */ if (state->undo.undo_point) --state->undo.undo_point; return 0; } NK_API void nk_textedit_text(struct nk_text_edit *state, const char *text, int total_len) { nk_rune unicode; int glyph_len; int text_len = 0; NK_ASSERT(state); NK_ASSERT(text); if (!text || !total_len || state->mode == NK_TEXT_EDIT_MODE_VIEW) return; glyph_len = nk_utf_decode(text, &unicode, total_len); while ((text_len < total_len) && glyph_len) { /* don't insert a backward delete, just process the event */ if (unicode == 127) goto next; /* can't add newline in single-line mode */ if (unicode == '\n' && state->single_line) goto next; /* filter incoming text */ if (state->filter && !state->filter(state, unicode)) goto next; if (!NK_TEXT_HAS_SELECTION(state) && state->cursor < state->string.len) { if (state->mode == NK_TEXT_EDIT_MODE_REPLACE) { nk_textedit_makeundo_replace(state, state->cursor, 1, 1); nk_str_delete_runes(&state->string, state->cursor, 1); } if (nk_str_insert_text_utf8(&state->string, state->cursor, text+text_len, 1)) { ++state->cursor; state->has_preferred_x = 0; } } else { nk_textedit_delete_selection(state); /* implicitly clamps */ if (nk_str_insert_text_utf8(&state->string, state->cursor, text+text_len, 1)) { nk_textedit_makeundo_insert(state, state->cursor, 1); ++state->cursor; state->has_preferred_x = 0; } } next: text_len += glyph_len; glyph_len = nk_utf_decode(text + text_len, &unicode, total_len-text_len); } } NK_LIB void nk_textedit_key(struct nk_text_edit *state, enum nk_keys key, int shift_mod, const struct nk_user_font *font, float row_height) { retry: switch (key) { case NK_KEY_NONE: case NK_KEY_CTRL: case NK_KEY_ENTER: case NK_KEY_SHIFT: case NK_KEY_TAB: case NK_KEY_COPY: case NK_KEY_CUT: case NK_KEY_PASTE: case NK_KEY_MAX: default: break; case NK_KEY_TEXT_UNDO: nk_textedit_undo(state); state->has_preferred_x = 0; break; case NK_KEY_TEXT_REDO: nk_textedit_redo(state); state->has_preferred_x = 0; break; case NK_KEY_TEXT_SELECT_ALL: nk_textedit_select_all(state); state->has_preferred_x = 0; break; case NK_KEY_TEXT_INSERT_MODE: if (state->mode == NK_TEXT_EDIT_MODE_VIEW) state->mode = NK_TEXT_EDIT_MODE_INSERT; break; case NK_KEY_TEXT_REPLACE_MODE: if (state->mode == NK_TEXT_EDIT_MODE_VIEW) state->mode = NK_TEXT_EDIT_MODE_REPLACE; break; case NK_KEY_TEXT_RESET_MODE: if (state->mode == NK_TEXT_EDIT_MODE_INSERT || state->mode == NK_TEXT_EDIT_MODE_REPLACE) state->mode = NK_TEXT_EDIT_MODE_VIEW; break; case NK_KEY_LEFT: if (shift_mod) { nk_textedit_clamp(state); nk_textedit_prep_selection_at_cursor(state); /* move selection left */ if (state->select_end > 0) --state->select_end; state->cursor = state->select_end; state->has_preferred_x = 0; } else { /* if currently there's a selection, * move cursor to start of selection */ if (NK_TEXT_HAS_SELECTION(state)) nk_textedit_move_to_first(state); else if (state->cursor > 0) --state->cursor; state->has_preferred_x = 0; } break; case NK_KEY_RIGHT: if (shift_mod) { nk_textedit_prep_selection_at_cursor(state); /* move selection right */ ++state->select_end; nk_textedit_clamp(state); state->cursor = state->select_end; state->has_preferred_x = 0; } else { /* if currently there's a selection, * move cursor to end of selection */ if (NK_TEXT_HAS_SELECTION(state)) nk_textedit_move_to_last(state); else ++state->cursor; nk_textedit_clamp(state); state->has_preferred_x = 0; } break; case NK_KEY_TEXT_WORD_LEFT: if (shift_mod) { if( !NK_TEXT_HAS_SELECTION( state ) ) nk_textedit_prep_selection_at_cursor(state); state->cursor = nk_textedit_move_to_word_previous(state); state->select_end = state->cursor; nk_textedit_clamp(state ); } else { if (NK_TEXT_HAS_SELECTION(state)) nk_textedit_move_to_first(state); else { state->cursor = nk_textedit_move_to_word_previous(state); nk_textedit_clamp(state ); } } break; case NK_KEY_TEXT_WORD_RIGHT: if (shift_mod) { if( !NK_TEXT_HAS_SELECTION( state ) ) nk_textedit_prep_selection_at_cursor(state); state->cursor = nk_textedit_move_to_word_next(state); state->select_end = state->cursor; nk_textedit_clamp(state); } else { if (NK_TEXT_HAS_SELECTION(state)) nk_textedit_move_to_last(state); else { state->cursor = nk_textedit_move_to_word_next(state); nk_textedit_clamp(state ); } } break; case NK_KEY_DOWN: { struct nk_text_find find; struct nk_text_edit_row row; int i, sel = shift_mod; if (state->single_line) { /* on windows, up&down in single-line behave like left&right */ key = NK_KEY_RIGHT; goto retry; } if (sel) nk_textedit_prep_selection_at_cursor(state); else if (NK_TEXT_HAS_SELECTION(state)) nk_textedit_move_to_last(state); /* compute current position of cursor point */ nk_textedit_clamp(state); nk_textedit_find_charpos(&find, state, state->cursor, state->single_line, font, row_height); /* now find character position down a row */ if (find.length) { float x; float goal_x = state->has_preferred_x ? state->preferred_x : find.x; int start = find.first_char + find.length; state->cursor = start; nk_textedit_layout_row(&row, state, state->cursor, row_height, font); x = row.x0; for (i=0; i < row.num_chars && x < row.x1; ++i) { float dx = nk_textedit_get_width(state, start, i, font); x += dx; if (x > goal_x) break; ++state->cursor; } nk_textedit_clamp(state); state->has_preferred_x = 1; state->preferred_x = goal_x; if (sel) state->select_end = state->cursor; } } break; case NK_KEY_UP: { struct nk_text_find find; struct nk_text_edit_row row; int i, sel = shift_mod; if (state->single_line) { /* on windows, up&down become left&right */ key = NK_KEY_LEFT; goto retry; } if (sel) nk_textedit_prep_selection_at_cursor(state); else if (NK_TEXT_HAS_SELECTION(state)) nk_textedit_move_to_first(state); /* compute current position of cursor point */ nk_textedit_clamp(state); nk_textedit_find_charpos(&find, state, state->cursor, state->single_line, font, row_height); /* can only go up if there's a previous row */ if (find.prev_first != find.first_char) { /* now find character position up a row */ float x; float goal_x = state->has_preferred_x ? state->preferred_x : find.x; state->cursor = find.prev_first; nk_textedit_layout_row(&row, state, state->cursor, row_height, font); x = row.x0; for (i=0; i < row.num_chars && x < row.x1; ++i) { float dx = nk_textedit_get_width(state, find.prev_first, i, font); x += dx; if (x > goal_x) break; ++state->cursor; } nk_textedit_clamp(state); state->has_preferred_x = 1; state->preferred_x = goal_x; if (sel) state->select_end = state->cursor; } } break; case NK_KEY_DEL: if (state->mode == NK_TEXT_EDIT_MODE_VIEW) break; if (NK_TEXT_HAS_SELECTION(state)) nk_textedit_delete_selection(state); else { int n = state->string.len; if (state->cursor < n) nk_textedit_delete(state, state->cursor, 1); } state->has_preferred_x = 0; break; case NK_KEY_BACKSPACE: if (state->mode == NK_TEXT_EDIT_MODE_VIEW) break; if (NK_TEXT_HAS_SELECTION(state)) nk_textedit_delete_selection(state); else { nk_textedit_clamp(state); if (state->cursor > 0) { nk_textedit_delete(state, state->cursor-1, 1); --state->cursor; } } state->has_preferred_x = 0; break; case NK_KEY_TEXT_START: if (shift_mod) { nk_textedit_prep_selection_at_cursor(state); state->cursor = state->select_end = 0; state->has_preferred_x = 0; } else { state->cursor = state->select_start = state->select_end = 0; state->has_preferred_x = 0; } break; case NK_KEY_TEXT_END: if (shift_mod) { nk_textedit_prep_selection_at_cursor(state); state->cursor = state->select_end = state->string.len; state->has_preferred_x = 0; } else { state->cursor = state->string.len; state->select_start = state->select_end = 0; state->has_preferred_x = 0; } break; case NK_KEY_TEXT_LINE_START: { if (shift_mod) { struct nk_text_find find; nk_textedit_clamp(state); nk_textedit_prep_selection_at_cursor(state); if (state->string.len && state->cursor == state->string.len) --state->cursor; nk_textedit_find_charpos(&find, state,state->cursor, state->single_line, font, row_height); state->cursor = state->select_end = find.first_char; state->has_preferred_x = 0; } else { struct nk_text_find find; if (state->string.len && state->cursor == state->string.len) --state->cursor; nk_textedit_clamp(state); nk_textedit_move_to_first(state); nk_textedit_find_charpos(&find, state, state->cursor, state->single_line, font, row_height); state->cursor = find.first_char; state->has_preferred_x = 0; } } break; case NK_KEY_TEXT_LINE_END: { if (shift_mod) { struct nk_text_find find; nk_textedit_clamp(state); nk_textedit_prep_selection_at_cursor(state); nk_textedit_find_charpos(&find, state, state->cursor, state->single_line, font, row_height); state->has_preferred_x = 0; state->cursor = find.first_char + find.length; if (find.length > 0 && nk_str_rune_at(&state->string, state->cursor-1) == '\n') --state->cursor; state->select_end = state->cursor; } else { struct nk_text_find find; nk_textedit_clamp(state); nk_textedit_move_to_first(state); nk_textedit_find_charpos(&find, state, state->cursor, state->single_line, font, row_height); state->has_preferred_x = 0; state->cursor = find.first_char + find.length; if (find.length > 0 && nk_str_rune_at(&state->string, state->cursor-1) == '\n') --state->cursor; }} break; } } NK_INTERN void nk_textedit_flush_redo(struct nk_text_undo_state *state) { state->redo_point = NK_TEXTEDIT_UNDOSTATECOUNT; state->redo_char_point = NK_TEXTEDIT_UNDOCHARCOUNT; } NK_INTERN void nk_textedit_discard_undo(struct nk_text_undo_state *state) { /* discard the oldest entry in the undo list */ if (state->undo_point > 0) { /* if the 0th undo state has characters, clean those up */ if (state->undo_rec[0].char_storage >= 0) { int n = state->undo_rec[0].insert_length, i; /* delete n characters from all other records */ state->undo_char_point = (short)(state->undo_char_point - n); NK_MEMCPY(state->undo_char, state->undo_char + n, (nk_size)state->undo_char_point*sizeof(nk_rune)); for (i=0; i < state->undo_point; ++i) { if (state->undo_rec[i].char_storage >= 0) state->undo_rec[i].char_storage = (short) (state->undo_rec[i].char_storage - n); } } --state->undo_point; NK_MEMCPY(state->undo_rec, state->undo_rec+1, (nk_size)((nk_size)state->undo_point * sizeof(state->undo_rec[0]))); } } NK_INTERN void nk_textedit_discard_redo(struct nk_text_undo_state *state) { /* discard the oldest entry in the redo list--it's bad if this ever happens, but because undo & redo have to store the actual characters in different cases, the redo character buffer can fill up even though the undo buffer didn't */ nk_size num; int k = NK_TEXTEDIT_UNDOSTATECOUNT-1; if (state->redo_point <= k) { /* if the k'th undo state has characters, clean those up */ if (state->undo_rec[k].char_storage >= 0) { int n = state->undo_rec[k].insert_length, i; /* delete n characters from all other records */ state->redo_char_point = (short)(state->redo_char_point + n); num = (nk_size)(NK_TEXTEDIT_UNDOCHARCOUNT - state->redo_char_point); NK_MEMCPY(state->undo_char + state->redo_char_point, state->undo_char + state->redo_char_point-n, num * sizeof(char)); for (i = state->redo_point; i < k; ++i) { if (state->undo_rec[i].char_storage >= 0) { state->undo_rec[i].char_storage = (short) (state->undo_rec[i].char_storage + n); } } } ++state->redo_point; num = (nk_size)(NK_TEXTEDIT_UNDOSTATECOUNT - state->redo_point); if (num) NK_MEMCPY(state->undo_rec + state->redo_point-1, state->undo_rec + state->redo_point, num * sizeof(state->undo_rec[0])); } } NK_INTERN struct nk_text_undo_record* nk_textedit_create_undo_record(struct nk_text_undo_state *state, int numchars) { /* any time we create a new undo record, we discard redo*/ nk_textedit_flush_redo(state); /* if we have no free records, we have to make room, * by sliding the existing records down */ if (state->undo_point == NK_TEXTEDIT_UNDOSTATECOUNT) nk_textedit_discard_undo(state); /* if the characters to store won't possibly fit in the buffer, * we can't undo */ if (numchars > NK_TEXTEDIT_UNDOCHARCOUNT) { state->undo_point = 0; state->undo_char_point = 0; return 0; } /* if we don't have enough free characters in the buffer, * we have to make room */ while (state->undo_char_point + numchars > NK_TEXTEDIT_UNDOCHARCOUNT) nk_textedit_discard_undo(state); return &state->undo_rec[state->undo_point++]; } NK_INTERN nk_rune* nk_textedit_createundo(struct nk_text_undo_state *state, int pos, int insert_len, int delete_len) { struct nk_text_undo_record *r = nk_textedit_create_undo_record(state, insert_len); if (r == 0) return 0; r->where = pos; r->insert_length = (short) insert_len; r->delete_length = (short) delete_len; if (insert_len == 0) { r->char_storage = -1; return 0; } else { r->char_storage = state->undo_char_point; state->undo_char_point = (short)(state->undo_char_point + insert_len); return &state->undo_char[r->char_storage]; } } NK_API void nk_textedit_undo(struct nk_text_edit *state) { struct nk_text_undo_state *s = &state->undo; struct nk_text_undo_record u, *r; if (s->undo_point == 0) return; /* we need to do two things: apply the undo record, and create a redo record */ u = s->undo_rec[s->undo_point-1]; r = &s->undo_rec[s->redo_point-1]; r->char_storage = -1; r->insert_length = u.delete_length; r->delete_length = u.insert_length; r->where = u.where; if (u.delete_length) { /* if the undo record says to delete characters, then the redo record will need to re-insert the characters that get deleted, so we need to store them. there are three cases: - there's enough room to store the characters - characters stored for *redoing* don't leave room for redo - characters stored for *undoing* don't leave room for redo if the last is true, we have to bail */ if (s->undo_char_point + u.delete_length >= NK_TEXTEDIT_UNDOCHARCOUNT) { /* the undo records take up too much character space; there's no space * to store the redo characters */ r->insert_length = 0; } else { int i; /* there's definitely room to store the characters eventually */ while (s->undo_char_point + u.delete_length > s->redo_char_point) { /* there's currently not enough room, so discard a redo record */ nk_textedit_discard_redo(s); /* should never happen: */ if (s->redo_point == NK_TEXTEDIT_UNDOSTATECOUNT) return; } r = &s->undo_rec[s->redo_point-1]; r->char_storage = (short)(s->redo_char_point - u.delete_length); s->redo_char_point = (short)(s->redo_char_point - u.delete_length); /* now save the characters */ for (i=0; i < u.delete_length; ++i) s->undo_char[r->char_storage + i] = nk_str_rune_at(&state->string, u.where + i); } /* now we can carry out the deletion */ nk_str_delete_runes(&state->string, u.where, u.delete_length); } /* check type of recorded action: */ if (u.insert_length) { /* easy case: was a deletion, so we need to insert n characters */ nk_str_insert_text_runes(&state->string, u.where, &s->undo_char[u.char_storage], u.insert_length); s->undo_char_point = (short)(s->undo_char_point - u.insert_length); } state->cursor = (short)(u.where + u.insert_length); s->undo_point--; s->redo_point--; } NK_API void nk_textedit_redo(struct nk_text_edit *state) { struct nk_text_undo_state *s = &state->undo; struct nk_text_undo_record *u, r; if (s->redo_point == NK_TEXTEDIT_UNDOSTATECOUNT) return; /* we need to do two things: apply the redo record, and create an undo record */ u = &s->undo_rec[s->undo_point]; r = s->undo_rec[s->redo_point]; /* we KNOW there must be room for the undo record, because the redo record was derived from an undo record */ u->delete_length = r.insert_length; u->insert_length = r.delete_length; u->where = r.where; u->char_storage = -1; if (r.delete_length) { /* the redo record requires us to delete characters, so the undo record needs to store the characters */ if (s->undo_char_point + u->insert_length > s->redo_char_point) { u->insert_length = 0; u->delete_length = 0; } else { int i; u->char_storage = s->undo_char_point; s->undo_char_point = (short)(s->undo_char_point + u->insert_length); /* now save the characters */ for (i=0; i < u->insert_length; ++i) { s->undo_char[u->char_storage + i] = nk_str_rune_at(&state->string, u->where + i); } } nk_str_delete_runes(&state->string, r.where, r.delete_length); } if (r.insert_length) { /* easy case: need to insert n characters */ nk_str_insert_text_runes(&state->string, r.where, &s->undo_char[r.char_storage], r.insert_length); } state->cursor = r.where + r.insert_length; s->undo_point++; s->redo_point++; } NK_INTERN void nk_textedit_makeundo_insert(struct nk_text_edit *state, int where, int length) { nk_textedit_createundo(&state->undo, where, 0, length); } NK_INTERN void nk_textedit_makeundo_delete(struct nk_text_edit *state, int where, int length) { int i; nk_rune *p = nk_textedit_createundo(&state->undo, where, length, 0); if (p) { for (i=0; i < length; ++i) p[i] = nk_str_rune_at(&state->string, where+i); } } NK_INTERN void nk_textedit_makeundo_replace(struct nk_text_edit *state, int where, int old_length, int new_length) { int i; nk_rune *p = nk_textedit_createundo(&state->undo, where, old_length, new_length); if (p) { for (i=0; i < old_length; ++i) p[i] = nk_str_rune_at(&state->string, where+i); } } NK_LIB void nk_textedit_clear_state(struct nk_text_edit *state, enum nk_text_edit_type type, nk_plugin_filter filter) { /* reset the state to default */ state->undo.undo_point = 0; state->undo.undo_char_point = 0; state->undo.redo_point = NK_TEXTEDIT_UNDOSTATECOUNT; state->undo.redo_char_point = NK_TEXTEDIT_UNDOCHARCOUNT; state->select_end = state->select_start = 0; state->cursor = 0; state->has_preferred_x = 0; state->preferred_x = 0; state->cursor_at_end_of_line = 0; state->initialized = 1; state->single_line = (unsigned char)(type == NK_TEXT_EDIT_SINGLE_LINE); state->mode = NK_TEXT_EDIT_MODE_VIEW; state->filter = filter; state->scrollbar = nk_vec2(0,0); } NK_API void nk_textedit_init_fixed(struct nk_text_edit *state, void *memory, nk_size size) { NK_ASSERT(state); NK_ASSERT(memory); if (!state || !memory || !size) return; NK_MEMSET(state, 0, sizeof(struct nk_text_edit)); nk_textedit_clear_state(state, NK_TEXT_EDIT_SINGLE_LINE, 0); nk_str_init_fixed(&state->string, memory, size); } NK_API void nk_textedit_init(struct nk_text_edit *state, struct nk_allocator *alloc, nk_size size) { NK_ASSERT(state); NK_ASSERT(alloc); if (!state || !alloc) return; NK_MEMSET(state, 0, sizeof(struct nk_text_edit)); nk_textedit_clear_state(state, NK_TEXT_EDIT_SINGLE_LINE, 0); nk_str_init(&state->string, alloc, size); } #ifdef NK_INCLUDE_DEFAULT_ALLOCATOR NK_API void nk_textedit_init_default(struct nk_text_edit *state) { NK_ASSERT(state); if (!state) return; NK_MEMSET(state, 0, sizeof(struct nk_text_edit)); nk_textedit_clear_state(state, NK_TEXT_EDIT_SINGLE_LINE, 0); nk_str_init_default(&state->string); } #endif NK_API void nk_textedit_select_all(struct nk_text_edit *state) { NK_ASSERT(state); state->select_start = 0; state->select_end = state->string.len; } NK_API void nk_textedit_free(struct nk_text_edit *state) { NK_ASSERT(state); if (!state) return; nk_str_free(&state->string); } /* =============================================================== * * FILTER * * ===============================================================*/ NK_API nk_bool nk_filter_default(const struct nk_text_edit *box, nk_rune unicode) { NK_UNUSED(unicode); NK_UNUSED(box); return nk_true; } NK_API nk_bool nk_filter_ascii(const struct nk_text_edit *box, nk_rune unicode) { NK_UNUSED(box); if (unicode > 128) return nk_false; else return nk_true; } NK_API nk_bool nk_filter_float(const struct nk_text_edit *box, nk_rune unicode) { NK_UNUSED(box); if ((unicode < '0' || unicode > '9') && unicode != '.' && unicode != '-') return nk_false; else return nk_true; } NK_API nk_bool nk_filter_decimal(const struct nk_text_edit *box, nk_rune unicode) { NK_UNUSED(box); if ((unicode < '0' || unicode > '9') && unicode != '-') return nk_false; else return nk_true; } NK_API nk_bool nk_filter_hex(const struct nk_text_edit *box, nk_rune unicode) { NK_UNUSED(box); if ((unicode < '0' || unicode > '9') && (unicode < 'a' || unicode > 'f') && (unicode < 'A' || unicode > 'F')) return nk_false; else return nk_true; } NK_API nk_bool nk_filter_oct(const struct nk_text_edit *box, nk_rune unicode) { NK_UNUSED(box); if (unicode < '0' || unicode > '7') return nk_false; else return nk_true; } NK_API nk_bool nk_filter_binary(const struct nk_text_edit *box, nk_rune unicode) { NK_UNUSED(box); if (unicode != '0' && unicode != '1') return nk_false; else return nk_true; } /* =============================================================== * * EDIT * * ===============================================================*/ NK_LIB void nk_edit_draw_text(struct nk_command_buffer *out, const struct nk_style_edit *style, float pos_x, float pos_y, float x_offset, const char *text, int byte_len, float row_height, const struct nk_user_font *font, struct nk_color background, struct nk_color foreground, nk_bool is_selected) { NK_ASSERT(out); NK_ASSERT(font); NK_ASSERT(style); if (!text || !byte_len || !out || !style) return; {int glyph_len = 0; nk_rune unicode = 0; int text_len = 0; float line_width = 0; float glyph_width; const char *line = text; float line_offset = 0; int line_count = 0; struct nk_text txt; txt.padding = nk_vec2(0,0); txt.background = background; txt.text = foreground; glyph_len = nk_utf_decode(text+text_len, &unicode, byte_len-text_len); if (!glyph_len) return; while ((text_len < byte_len) && glyph_len) { if (unicode == '\n') { /* new line separator so draw previous line */ struct nk_rect label; label.y = pos_y + line_offset; label.h = row_height; label.w = line_width; label.x = pos_x; if (!line_count) label.x += x_offset; if (is_selected) /* selection needs to draw different background color */ nk_fill_rect(out, label, 0, background); nk_widget_text(out, label, line, (int)((text + text_len) - line), &txt, NK_TEXT_CENTERED, font); text_len++; line_count++; line_width = 0; line = text + text_len; line_offset += row_height; glyph_len = nk_utf_decode(text + text_len, &unicode, (int)(byte_len-text_len)); continue; } if (unicode == '\r') { text_len++; glyph_len = nk_utf_decode(text + text_len, &unicode, byte_len-text_len); continue; } glyph_width = font->width(font->userdata, font->height, text+text_len, glyph_len); line_width += (float)glyph_width; text_len += glyph_len; glyph_len = nk_utf_decode(text + text_len, &unicode, byte_len-text_len); continue; } if (line_width > 0) { /* draw last line */ struct nk_rect label; label.y = pos_y + line_offset; label.h = row_height; label.w = line_width; label.x = pos_x; if (!line_count) label.x += x_offset; if (is_selected) nk_fill_rect(out, label, 0, background); nk_widget_text(out, label, line, (int)((text + text_len) - line), &txt, NK_TEXT_LEFT, font); }} } NK_LIB nk_flags nk_do_edit(nk_flags *state, struct nk_command_buffer *out, struct nk_rect bounds, nk_flags flags, nk_plugin_filter filter, struct nk_text_edit *edit, const struct nk_style_edit *style, struct nk_input *in, const struct nk_user_font *font) { struct nk_rect area; nk_flags ret = 0; float row_height; char prev_state = 0; char is_hovered = 0; char select_all = 0; char cursor_follow = 0; struct nk_rect old_clip; struct nk_rect clip; NK_ASSERT(state); NK_ASSERT(out); NK_ASSERT(style); if (!state || !out || !style) return ret; /* visible text area calculation */ area.x = bounds.x + style->padding.x + style->border; area.y = bounds.y + style->padding.y + style->border; area.w = bounds.w - (2.0f * style->padding.x + 2 * style->border); area.h = bounds.h - (2.0f * style->padding.y + 2 * style->border); if (flags & NK_EDIT_MULTILINE) area.w = NK_MAX(0, area.w - style->scrollbar_size.x); row_height = (flags & NK_EDIT_MULTILINE)? font->height + style->row_padding: area.h; /* calculate clipping rectangle */ old_clip = out->clip; nk_unify(&clip, &old_clip, area.x, area.y, area.x + area.w, area.y + area.h); /* update edit state */ prev_state = (char)edit->active; is_hovered = (char)nk_input_is_mouse_hovering_rect(in, bounds); if (in && in->mouse.buttons[NK_BUTTON_LEFT].clicked && in->mouse.buttons[NK_BUTTON_LEFT].down) { edit->active = NK_INBOX(in->mouse.pos.x, in->mouse.pos.y, bounds.x, bounds.y, bounds.w, bounds.h); } /* (de)activate text editor */ if (!prev_state && edit->active) { const enum nk_text_edit_type type = (flags & NK_EDIT_MULTILINE) ? NK_TEXT_EDIT_MULTI_LINE: NK_TEXT_EDIT_SINGLE_LINE; /* keep scroll position when re-activating edit widget */ struct nk_vec2 oldscrollbar = edit->scrollbar; nk_textedit_clear_state(edit, type, filter); edit->scrollbar = oldscrollbar; if (flags & NK_EDIT_AUTO_SELECT) select_all = nk_true; if (flags & NK_EDIT_GOTO_END_ON_ACTIVATE) { edit->cursor = edit->string.len; in = 0; } } else if (!edit->active) edit->mode = NK_TEXT_EDIT_MODE_VIEW; if (flags & NK_EDIT_READ_ONLY) edit->mode = NK_TEXT_EDIT_MODE_VIEW; else if (flags & NK_EDIT_ALWAYS_INSERT_MODE) edit->mode = NK_TEXT_EDIT_MODE_INSERT; ret = (edit->active) ? NK_EDIT_ACTIVE: NK_EDIT_INACTIVE; if (prev_state != edit->active) ret |= (edit->active) ? NK_EDIT_ACTIVATED: NK_EDIT_DEACTIVATED; /* handle user input */ if (edit->active && in) { int shift_mod = in->keyboard.keys[NK_KEY_SHIFT].down; const float mouse_x = (in->mouse.pos.x - area.x) + edit->scrollbar.x; const float mouse_y = (in->mouse.pos.y - area.y) + edit->scrollbar.y; /* mouse click handler */ is_hovered = (char)nk_input_is_mouse_hovering_rect(in, area); if (select_all) { nk_textedit_select_all(edit); } else if (is_hovered && in->mouse.buttons[NK_BUTTON_LEFT].down && in->mouse.buttons[NK_BUTTON_LEFT].clicked) { nk_textedit_click(edit, mouse_x, mouse_y, font, row_height); } else if (is_hovered && in->mouse.buttons[NK_BUTTON_LEFT].down && (in->mouse.delta.x != 0.0f || in->mouse.delta.y != 0.0f)) { nk_textedit_drag(edit, mouse_x, mouse_y, font, row_height); cursor_follow = nk_true; } else if (is_hovered && in->mouse.buttons[NK_BUTTON_RIGHT].clicked && in->mouse.buttons[NK_BUTTON_RIGHT].down) { nk_textedit_key(edit, NK_KEY_TEXT_WORD_LEFT, nk_false, font, row_height); nk_textedit_key(edit, NK_KEY_TEXT_WORD_RIGHT, nk_true, font, row_height); cursor_follow = nk_true; } {int i; /* keyboard input */ int old_mode = edit->mode; for (i = 0; i < NK_KEY_MAX; ++i) { if (i == NK_KEY_ENTER || i == NK_KEY_TAB) continue; /* special case */ if (nk_input_is_key_pressed(in, (enum nk_keys)i)) { nk_textedit_key(edit, (enum nk_keys)i, shift_mod, font, row_height); cursor_follow = nk_true; } } if (old_mode != edit->mode) { in->keyboard.text_len = 0; }} /* text input */ edit->filter = filter; if (in->keyboard.text_len) { nk_textedit_text(edit, in->keyboard.text, in->keyboard.text_len); cursor_follow = nk_true; in->keyboard.text_len = 0; } /* enter key handler */ if (nk_input_is_key_pressed(in, NK_KEY_ENTER)) { cursor_follow = nk_true; if (flags & NK_EDIT_CTRL_ENTER_NEWLINE && shift_mod) nk_textedit_text(edit, "\n", 1); else if (flags & NK_EDIT_SIG_ENTER) ret |= NK_EDIT_COMMITED; else nk_textedit_text(edit, "\n", 1); } /* cut & copy handler */ {int copy= nk_input_is_key_pressed(in, NK_KEY_COPY); int cut = nk_input_is_key_pressed(in, NK_KEY_CUT); if ((copy || cut) && (flags & NK_EDIT_CLIPBOARD)) { int glyph_len; nk_rune unicode; const char *text; int b = edit->select_start; int e = edit->select_end; int begin = NK_MIN(b, e); int end = NK_MAX(b, e); text = nk_str_at_const(&edit->string, begin, &unicode, &glyph_len); if (edit->clip.copy) edit->clip.copy(edit->clip.userdata, text, end - begin); if (cut && !(flags & NK_EDIT_READ_ONLY)){ nk_textedit_cut(edit); cursor_follow = nk_true; } }} /* paste handler */ {int paste = nk_input_is_key_pressed(in, NK_KEY_PASTE); if (paste && (flags & NK_EDIT_CLIPBOARD) && edit->clip.paste) { edit->clip.paste(edit->clip.userdata, edit); cursor_follow = nk_true; }} /* tab handler */ {int tab = nk_input_is_key_pressed(in, NK_KEY_TAB); if (tab && (flags & NK_EDIT_ALLOW_TAB)) { nk_textedit_text(edit, " ", 4); cursor_follow = nk_true; }} } /* set widget state */ if (edit->active) *state = NK_WIDGET_STATE_ACTIVE; else nk_widget_state_reset(state); if (is_hovered) *state |= NK_WIDGET_STATE_HOVERED; /* DRAW EDIT */ {const char *text = nk_str_get_const(&edit->string); int len = nk_str_len_char(&edit->string); {/* select background colors/images */ const struct nk_style_item *background; if (*state & NK_WIDGET_STATE_ACTIVED) background = &style->active; else if (*state & NK_WIDGET_STATE_HOVER) background = &style->hover; else background = &style->normal; /* draw background frame */ switch(background->type) { case NK_STYLE_ITEM_IMAGE: nk_draw_image(out, bounds, &background->data.image, nk_white); break; case NK_STYLE_ITEM_NINE_SLICE: nk_draw_nine_slice(out, bounds, &background->data.slice, nk_white); break; case NK_STYLE_ITEM_COLOR: nk_fill_rect(out, bounds, style->rounding, background->data.color); nk_stroke_rect(out, bounds, style->rounding, style->border, style->border_color); break; }} area.w = NK_MAX(0, area.w - style->cursor_size); if (edit->active) { int total_lines = 1; struct nk_vec2 text_size = nk_vec2(0,0); /* text pointer positions */ const char *cursor_ptr = 0; const char *select_begin_ptr = 0; const char *select_end_ptr = 0; /* 2D pixel positions */ struct nk_vec2 cursor_pos = nk_vec2(0,0); struct nk_vec2 selection_offset_start = nk_vec2(0,0); struct nk_vec2 selection_offset_end = nk_vec2(0,0); int selection_begin = NK_MIN(edit->select_start, edit->select_end); int selection_end = NK_MAX(edit->select_start, edit->select_end); /* calculate total line count + total space + cursor/selection position */ float line_width = 0.0f; if (text && len) { /* utf8 encoding */ float glyph_width; int glyph_len = 0; nk_rune unicode = 0; int text_len = 0; int glyphs = 0; int row_begin = 0; glyph_len = nk_utf_decode(text, &unicode, len); glyph_width = font->width(font->userdata, font->height, text, glyph_len); line_width = 0; /* iterate all lines */ while ((text_len < len) && glyph_len) { /* set cursor 2D position and line */ if (!cursor_ptr && glyphs == edit->cursor) { int glyph_offset; struct nk_vec2 out_offset; struct nk_vec2 row_size; const char *remaining; /* calculate 2d position */ cursor_pos.y = (float)(total_lines-1) * row_height; row_size = nk_text_calculate_text_bounds(font, text+row_begin, text_len-row_begin, row_height, &remaining, &out_offset, &glyph_offset, NK_STOP_ON_NEW_LINE); cursor_pos.x = row_size.x; cursor_ptr = text + text_len; } /* set start selection 2D position and line */ if (!select_begin_ptr && edit->select_start != edit->select_end && glyphs == selection_begin) { int glyph_offset; struct nk_vec2 out_offset; struct nk_vec2 row_size; const char *remaining; /* calculate 2d position */ selection_offset_start.y = (float)(NK_MAX(total_lines-1,0)) * row_height; row_size = nk_text_calculate_text_bounds(font, text+row_begin, text_len-row_begin, row_height, &remaining, &out_offset, &glyph_offset, NK_STOP_ON_NEW_LINE); selection_offset_start.x = row_size.x; select_begin_ptr = text + text_len; } /* set end selection 2D position and line */ if (!select_end_ptr && edit->select_start != edit->select_end && glyphs == selection_end) { int glyph_offset; struct nk_vec2 out_offset; struct nk_vec2 row_size; const char *remaining; /* calculate 2d position */ selection_offset_end.y = (float)(total_lines-1) * row_height; row_size = nk_text_calculate_text_bounds(font, text+row_begin, text_len-row_begin, row_height, &remaining, &out_offset, &glyph_offset, NK_STOP_ON_NEW_LINE); selection_offset_end.x = row_size.x; select_end_ptr = text + text_len; } if (unicode == '\n') { text_size.x = NK_MAX(text_size.x, line_width); total_lines++; line_width = 0; text_len++; glyphs++; row_begin = text_len; glyph_len = nk_utf_decode(text + text_len, &unicode, len-text_len); glyph_width = font->width(font->userdata, font->height, text+text_len, glyph_len); continue; } glyphs++; text_len += glyph_len; line_width += (float)glyph_width; glyph_len = nk_utf_decode(text + text_len, &unicode, len-text_len); glyph_width = font->width(font->userdata, font->height, text+text_len, glyph_len); continue; } text_size.y = (float)total_lines * row_height; #if 1 //< @r-lyeh if (flags & NK_EDIT_GOTO_END_ON_ACTIVATE) { edit->cursor = edit->string.len; // in = 0; } #endif /* handle case when cursor is at end of text buffer */ if (!cursor_ptr && edit->cursor == edit->string.len) { cursor_pos.x = line_width; cursor_pos.y = text_size.y - row_height; } } { /* scrollbar */ if (cursor_follow) { /* update scrollbar to follow cursor */ if (!(flags & NK_EDIT_NO_HORIZONTAL_SCROLL)) { /* horizontal scroll */ const float scroll_increment = area.w * 0.25f; if (cursor_pos.x < edit->scrollbar.x) edit->scrollbar.x = (float)(int)NK_MAX(0.0f, cursor_pos.x - scroll_increment); if (cursor_pos.x >= edit->scrollbar.x + area.w) edit->scrollbar.x = (float)(int)NK_MAX(0.0f, cursor_pos.x - area.w + scroll_increment); } else edit->scrollbar.x = 0; if (flags & NK_EDIT_MULTILINE) { /* vertical scroll */ if (cursor_pos.y < edit->scrollbar.y) edit->scrollbar.y = NK_MAX(0.0f, cursor_pos.y - row_height); if (cursor_pos.y >= edit->scrollbar.y + row_height) edit->scrollbar.y = edit->scrollbar.y + row_height; } else edit->scrollbar.y = 0; } /* scrollbar widget */ if (flags & NK_EDIT_MULTILINE) { nk_flags ws; struct nk_rect scroll; float scroll_target; float scroll_offset; float scroll_step; float scroll_inc; scroll = area; scroll.x = (bounds.x + bounds.w - style->border) - style->scrollbar_size.x; scroll.w = style->scrollbar_size.x; scroll_offset = edit->scrollbar.y; scroll_step = scroll.h * 0.10f; scroll_inc = scroll.h * 0.01f; scroll_target = text_size.y; edit->scrollbar.y = nk_do_scrollbarv(&ws, out, scroll, 0, scroll_offset, scroll_target, scroll_step, scroll_inc, &style->scrollbar, in, font); } } /* draw text */ {struct nk_color background_color; struct nk_color text_color; struct nk_color sel_background_color; struct nk_color sel_text_color; struct nk_color cursor_color; struct nk_color cursor_text_color; const struct nk_style_item *background; nk_push_scissor(out, clip); /* select correct colors to draw */ if (*state & NK_WIDGET_STATE_ACTIVED) { background = &style->active; text_color = style->text_active; sel_text_color = style->selected_text_hover; sel_background_color = style->selected_hover; cursor_color = style->cursor_hover; cursor_text_color = style->cursor_text_hover; } else if (*state & NK_WIDGET_STATE_HOVER) { background = &style->hover; text_color = style->text_hover; sel_text_color = style->selected_text_hover; sel_background_color = style->selected_hover; cursor_text_color = style->cursor_text_hover; cursor_color = style->cursor_hover; } else { background = &style->normal; text_color = style->text_normal; sel_text_color = style->selected_text_normal; sel_background_color = style->selected_normal; cursor_color = style->cursor_normal; cursor_text_color = style->cursor_text_normal; } if (background->type == NK_STYLE_ITEM_IMAGE) background_color = nk_rgba(0,0,0,0); else background_color = background->data.color; if (edit->select_start == edit->select_end) { /* no selection so just draw the complete text */ const char *begin = nk_str_get_const(&edit->string); int l = nk_str_len_char(&edit->string); nk_edit_draw_text(out, style, area.x - edit->scrollbar.x, area.y - edit->scrollbar.y, 0, begin, l, row_height, font, background_color, text_color, nk_false); } else { /* edit has selection so draw 1-3 text chunks */ if (edit->select_start != edit->select_end && selection_begin > 0){ /* draw unselected text before selection */ const char *begin = nk_str_get_const(&edit->string); NK_ASSERT(select_begin_ptr); nk_edit_draw_text(out, style, area.x - edit->scrollbar.x, area.y - edit->scrollbar.y, 0, begin, (int)(select_begin_ptr - begin), row_height, font, background_color, text_color, nk_false); } if (edit->select_start != edit->select_end) { /* draw selected text */ NK_ASSERT(select_begin_ptr); if (!select_end_ptr) { const char *begin = nk_str_get_const(&edit->string); select_end_ptr = begin + nk_str_len_char(&edit->string); } nk_edit_draw_text(out, style, area.x - edit->scrollbar.x, area.y + selection_offset_start.y - edit->scrollbar.y, selection_offset_start.x, select_begin_ptr, (int)(select_end_ptr - select_begin_ptr), row_height, font, sel_background_color, sel_text_color, nk_true); } if ((edit->select_start != edit->select_end && selection_end < edit->string.len)) { /* draw unselected text after selected text */ const char *begin = select_end_ptr; const char *end = nk_str_get_const(&edit->string) + nk_str_len_char(&edit->string); NK_ASSERT(select_end_ptr); nk_edit_draw_text(out, style, area.x - edit->scrollbar.x, area.y + selection_offset_end.y - edit->scrollbar.y, selection_offset_end.x, begin, (int)(end - begin), row_height, font, background_color, text_color, nk_true); } } /* cursor */ if (edit->select_start == edit->select_end) { if (edit->cursor >= nk_str_len(&edit->string) || (cursor_ptr && *cursor_ptr == '\n')) { /* draw cursor at end of line */ struct nk_rect cursor; cursor.w = style->cursor_size; cursor.h = font->height; cursor.x = area.x + cursor_pos.x - edit->scrollbar.x; cursor.y = area.y + cursor_pos.y + row_height/2.0f - cursor.h/2.0f; cursor.y -= edit->scrollbar.y; nk_fill_rect(out, cursor, 0, cursor_color); } else { /* draw cursor inside text */ int glyph_len; struct nk_rect label; struct nk_text txt; nk_rune unicode; NK_ASSERT(cursor_ptr); glyph_len = nk_utf_decode(cursor_ptr, &unicode, 4); label.x = area.x + cursor_pos.x - edit->scrollbar.x; label.y = area.y + cursor_pos.y - edit->scrollbar.y; label.w = font->width(font->userdata, font->height, cursor_ptr, glyph_len); label.h = row_height; txt.padding = nk_vec2(0,0); txt.background = cursor_color;; txt.text = cursor_text_color; nk_fill_rect(out, label, 0, cursor_color); nk_widget_text(out, label, cursor_ptr, glyph_len, &txt, NK_TEXT_LEFT, font); } }} } else { /* not active so just draw text */ int l = nk_str_len_char(&edit->string); const char *begin = nk_str_get_const(&edit->string); const struct nk_style_item *background; struct nk_color background_color; struct nk_color text_color; nk_push_scissor(out, clip); if (*state & NK_WIDGET_STATE_ACTIVED) { background = &style->active; text_color = style->text_active; } else if (*state & NK_WIDGET_STATE_HOVER) { background = &style->hover; text_color = style->text_hover; } else { background = &style->normal; text_color = style->text_normal; } if (background->type == NK_STYLE_ITEM_IMAGE) background_color = nk_rgba(0,0,0,0); else background_color = background->data.color; nk_edit_draw_text(out, style, area.x - edit->scrollbar.x, area.y - edit->scrollbar.y, 0, begin, l, row_height, font, background_color, text_color, nk_false); } nk_push_scissor(out, old_clip);} return ret; } NK_API void nk_edit_focus(struct nk_context *ctx, nk_flags flags) { nk_hash hash; struct nk_window *win; NK_ASSERT(ctx); NK_ASSERT(ctx->current); if (!ctx || !ctx->current) return; win = ctx->current; hash = win->edit.seq; win->edit.active = nk_true; win->edit.name = hash; if (flags & NK_EDIT_ALWAYS_INSERT_MODE) win->edit.mode = NK_TEXT_EDIT_MODE_INSERT; } NK_API void nk_edit_unfocus(struct nk_context *ctx) { struct nk_window *win; NK_ASSERT(ctx); NK_ASSERT(ctx->current); if (!ctx || !ctx->current) return; win = ctx->current; win->edit.active = nk_false; win->edit.name = 0; } NK_API nk_flags nk_edit_string(struct nk_context *ctx, nk_flags flags, char *memory, int *len, int max, nk_plugin_filter filter) { nk_hash hash; nk_flags state; struct nk_text_edit *edit; struct nk_window *win; NK_ASSERT(ctx); NK_ASSERT(memory); NK_ASSERT(len); if (!ctx || !memory || !len) return 0; filter = (!filter) ? nk_filter_default: filter; win = ctx->current; hash = win->edit.seq; edit = &ctx->text_edit; nk_textedit_clear_state(&ctx->text_edit, (flags & NK_EDIT_MULTILINE)? NK_TEXT_EDIT_MULTI_LINE: NK_TEXT_EDIT_SINGLE_LINE, filter); if (win->edit.active && hash == win->edit.name) { if (flags & NK_EDIT_NO_CURSOR) edit->cursor = nk_utf_len(memory, *len); else edit->cursor = win->edit.cursor; if (!(flags & NK_EDIT_SELECTABLE)) { edit->select_start = win->edit.cursor; edit->select_end = win->edit.cursor; } else { edit->select_start = win->edit.sel_start; edit->select_end = win->edit.sel_end; } edit->mode = win->edit.mode; edit->scrollbar.x = (float)win->edit.scrollbar.x; edit->scrollbar.y = (float)win->edit.scrollbar.y; edit->active = nk_true; } else edit->active = nk_false; max = NK_MAX(1, max); *len = NK_MIN(*len, max-1); nk_str_init_fixed(&edit->string, memory, (nk_size)max); edit->string.buffer.allocated = (nk_size)*len; edit->string.len = nk_utf_len(memory, *len); state = nk_edit_buffer(ctx, flags, edit, filter); *len = (int)edit->string.buffer.allocated; if (edit->active) { win->edit.cursor = edit->cursor; win->edit.sel_start = edit->select_start; win->edit.sel_end = edit->select_end; win->edit.mode = edit->mode; win->edit.scrollbar.x = (nk_uint)edit->scrollbar.x; win->edit.scrollbar.y = (nk_uint)edit->scrollbar.y; } return state; } NK_API nk_flags nk_edit_buffer(struct nk_context *ctx, nk_flags flags, struct nk_text_edit *edit, nk_plugin_filter filter) { struct nk_window *win; struct nk_style *style; struct nk_input *in; enum nk_widget_layout_states state; struct nk_rect bounds; nk_flags ret_flags = 0; unsigned char prev_state; nk_hash hash; /* make sure correct values */ NK_ASSERT(ctx); NK_ASSERT(edit); NK_ASSERT(ctx->current); NK_ASSERT(ctx->current->layout); if (!ctx || !ctx->current || !ctx->current->layout) return 0; win = ctx->current; style = &ctx->style; state = nk_widget(&bounds, ctx); if (!state) return state; in = (win->layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input; /* check if edit is currently hot item */ hash = win->edit.seq++; if (win->edit.active && hash == win->edit.name) { if (flags & NK_EDIT_NO_CURSOR) edit->cursor = edit->string.len; if (!(flags & NK_EDIT_SELECTABLE)) { edit->select_start = edit->cursor; edit->select_end = edit->cursor; } if (flags & NK_EDIT_CLIPBOARD) edit->clip = ctx->clip; edit->active = (unsigned char)win->edit.active; } else edit->active = nk_false; edit->mode = win->edit.mode; filter = (!filter) ? nk_filter_default: filter; prev_state = (unsigned char)edit->active; in = (flags & NK_EDIT_READ_ONLY) ? 0: in; ret_flags = nk_do_edit(&ctx->last_widget_state, &win->buffer, bounds, flags, filter, edit, &style->edit, in, style->font); if (ctx->last_widget_state & NK_WIDGET_STATE_HOVER) ctx->style.cursor_active = ctx->style.cursors[NK_CURSOR_TEXT]; if (edit->active && prev_state != edit->active) { /* current edit is now hot */ win->edit.active = nk_true; win->edit.name = hash; } else if (prev_state && !edit->active) { /* current edit is now cold */ win->edit.active = nk_false; } return ret_flags; } NK_API nk_flags nk_edit_string_zero_terminated(struct nk_context *ctx, nk_flags flags, char *buffer, int max, nk_plugin_filter filter) { nk_flags result; int len = nk_strlen(buffer); result = nk_edit_string(ctx, flags, buffer, &len, max, filter); buffer[NK_MIN(NK_MAX(max-1,0), len)] = '\0'; return result; } /* =============================================================== * * PROPERTY * * ===============================================================*/ NK_LIB void nk_drag_behavior(nk_flags *state, const struct nk_input *in, struct nk_rect drag, struct nk_property_variant *variant, float inc_per_pixel) { int left_mouse_down = in && in->mouse.buttons[NK_BUTTON_LEFT].down; int left_mouse_click_in_cursor = in && nk_input_has_mouse_click_down_in_rect(in, NK_BUTTON_LEFT, drag, nk_true); nk_widget_state_reset(state); if (nk_input_is_mouse_hovering_rect(in, drag)) *state = NK_WIDGET_STATE_HOVERED; if (left_mouse_down && left_mouse_click_in_cursor) { float delta, pixels; pixels = in->mouse.delta.x; delta = pixels * inc_per_pixel; switch (variant->kind) { default: break; case NK_PROPERTY_INT: variant->value.i = variant->value.i + (int)delta; variant->value.i = NK_CLAMP(variant->min_value.i, variant->value.i, variant->max_value.i); break; case NK_PROPERTY_FLOAT: variant->value.f = variant->value.f + (float)delta; variant->value.f = NK_CLAMP(variant->min_value.f, variant->value.f, variant->max_value.f); break; case NK_PROPERTY_DOUBLE: variant->value.d = variant->value.d + (double)delta; variant->value.d = NK_CLAMP(variant->min_value.d, variant->value.d, variant->max_value.d); break; } *state = NK_WIDGET_STATE_ACTIVE; } if (*state & NK_WIDGET_STATE_HOVER && !nk_input_is_mouse_prev_hovering_rect(in, drag)) *state |= NK_WIDGET_STATE_ENTERED; else if (nk_input_is_mouse_prev_hovering_rect(in, drag)) *state |= NK_WIDGET_STATE_LEFT; } NK_LIB void nk_property_behavior(nk_flags *ws, const struct nk_input *in, struct nk_rect property, struct nk_rect label, struct nk_rect edit, struct nk_rect empty, int *state, struct nk_property_variant *variant, float inc_per_pixel) { nk_widget_state_reset(ws); if (in && *state == NK_PROPERTY_DEFAULT) { if (nk_button_behavior(ws, edit, in, NK_BUTTON_DEFAULT)) *state = NK_PROPERTY_EDIT; else if (nk_input_is_mouse_click_down_in_rect(in, NK_BUTTON_LEFT, label, nk_true)) *state = NK_PROPERTY_DRAG; else if (nk_input_is_mouse_click_down_in_rect(in, NK_BUTTON_LEFT, empty, nk_true)) *state = NK_PROPERTY_DRAG; } if (*state == NK_PROPERTY_DRAG) { nk_drag_behavior(ws, in, property, variant, inc_per_pixel); if (!(*ws & NK_WIDGET_STATE_ACTIVED)) *state = NK_PROPERTY_DEFAULT; } } NK_LIB void nk_draw_property(struct nk_command_buffer *out, const struct nk_style_property *style, const struct nk_rect *bounds, const struct nk_rect *label, nk_flags state, const char *name, int len, const struct nk_user_font *font) { struct nk_text text; const struct nk_style_item *background; /* select correct background and text color */ if (state & NK_WIDGET_STATE_ACTIVED) { background = &style->active; text.text = style->label_active; } else if (state & NK_WIDGET_STATE_HOVER) { background = &style->hover; text.text = style->label_hover; } else { background = &style->normal; text.text = style->label_normal; } /* draw background */ switch(background->type) { case NK_STYLE_ITEM_IMAGE: text.background = nk_rgba(0, 0, 0, 0); nk_draw_image(out, *bounds, &background->data.image, nk_white); break; case NK_STYLE_ITEM_NINE_SLICE: text.background = nk_rgba(0, 0, 0, 0); nk_draw_nine_slice(out, *bounds, &background->data.slice, nk_white); break; case NK_STYLE_ITEM_COLOR: text.background = background->data.color; nk_fill_rect(out, *bounds, style->rounding, background->data.color); nk_stroke_rect(out, *bounds, style->rounding, style->border, background->data.color); break; } /* draw label */ text.padding = nk_vec2(0,0); nk_widget_text(out, *label, name, len, &text, NK_TEXT_CENTERED, font); } NK_LIB void nk_do_property(nk_flags *ws, struct nk_command_buffer *out, struct nk_rect property, const char *name, struct nk_property_variant *variant, float inc_per_pixel, char *buffer, int *len, int *state, int *cursor, int *select_begin, int *select_end, const struct nk_style_property *style, enum nk_property_filter filter, struct nk_input *in, const struct nk_user_font *font, struct nk_text_edit *text_edit, enum nk_button_behavior behavior) { const nk_plugin_filter filters[] = { nk_filter_decimal, nk_filter_float }; nk_bool active, old; int num_len = 0, name_len; char string[NK_MAX_NUMBER_BUFFER]; float size; char *dst = 0; int *length; struct nk_rect left; struct nk_rect right; struct nk_rect label; struct nk_rect edit; struct nk_rect empty; /* left decrement button */ left.h = font->height/2; left.w = left.h; left.x = property.x + style->border + style->padding.x; left.y = property.y + style->border + property.h/2.0f - left.h/2; /* text label */ name_len = nk_strlen(name); size = font->width(font->userdata, font->height, name, name_len); label.x = left.x + left.w + style->padding.x; label.w = (float)size + 2 * style->padding.x; label.y = property.y + style->border + style->padding.y; label.h = property.h - (2 * style->border + 2 * style->padding.y); /* right increment button */ right.y = left.y; right.w = left.w; right.h = left.h; right.x = property.x + property.w - (right.w + style->padding.x); /* edit */ if (*state == NK_PROPERTY_EDIT) { size = font->width(font->userdata, font->height, buffer, *len); size += style->edit.cursor_size; length = len; dst = buffer; } else { switch (variant->kind) { default: break; case NK_PROPERTY_INT: nk_itoa(string, variant->value.i); num_len = nk_strlen(string); break; case NK_PROPERTY_FLOAT: NK_DTOA(string, (double)variant->value.f); num_len = nk_string_float_limit(string, NK_MAX_FLOAT_PRECISION); break; case NK_PROPERTY_DOUBLE: NK_DTOA(string, variant->value.d); num_len = nk_string_float_limit(string, NK_MAX_FLOAT_PRECISION); break; } size = font->width(font->userdata, font->height, string, num_len); dst = string; length = &num_len; } edit.w = (float)size + 2 * style->padding.x; edit.w = NK_MIN(edit.w, right.x - (label.x + label.w)); edit.x = right.x - (edit.w + style->padding.x); edit.y = property.y + style->border; edit.h = property.h - (2 * style->border); /* empty left space activator */ empty.w = edit.x - (label.x + label.w); empty.x = label.x + label.w; empty.y = property.y; empty.h = property.h; /* update property */ old = (*state == NK_PROPERTY_EDIT); nk_property_behavior(ws, in, property, label, edit, empty, state, variant, inc_per_pixel); /* draw property */ if (style->draw_begin) style->draw_begin(out, style->userdata); nk_draw_property(out, style, &property, &label, *ws, name, name_len, font); if (style->draw_end) style->draw_end(out, style->userdata); /* execute right button */ if (nk_do_button_symbol(ws, out, left, style->sym_left, behavior, &style->dec_button, in, font)) { switch (variant->kind) { default: break; case NK_PROPERTY_INT: variant->value.i = NK_CLAMP(variant->min_value.i, variant->value.i - variant->step.i, variant->max_value.i); break; case NK_PROPERTY_FLOAT: variant->value.f = NK_CLAMP(variant->min_value.f, variant->value.f - variant->step.f, variant->max_value.f); break; case NK_PROPERTY_DOUBLE: variant->value.d = NK_CLAMP(variant->min_value.d, variant->value.d - variant->step.d, variant->max_value.d); break; } } /* execute left button */ if (nk_do_button_symbol(ws, out, right, style->sym_right, behavior, &style->inc_button, in, font)) { switch (variant->kind) { default: break; case NK_PROPERTY_INT: variant->value.i = NK_CLAMP(variant->min_value.i, variant->value.i + variant->step.i, variant->max_value.i); break; case NK_PROPERTY_FLOAT: variant->value.f = NK_CLAMP(variant->min_value.f, variant->value.f + variant->step.f, variant->max_value.f); break; case NK_PROPERTY_DOUBLE: variant->value.d = NK_CLAMP(variant->min_value.d, variant->value.d + variant->step.d, variant->max_value.d); break; } } if (old != NK_PROPERTY_EDIT && (*state == NK_PROPERTY_EDIT)) { /* property has been activated so setup buffer */ NK_MEMCPY(buffer, dst, (nk_size)*length); *cursor = nk_utf_len(buffer, *length); *len = *length; length = len; dst = buffer; active = 0; } else active = (*state == NK_PROPERTY_EDIT); /* execute and run text edit field */ nk_textedit_clear_state(text_edit, NK_TEXT_EDIT_SINGLE_LINE, filters[filter]); text_edit->active = (unsigned char)active; text_edit->string.len = *length; text_edit->cursor = NK_CLAMP(0, *cursor, *length); text_edit->select_start = NK_CLAMP(0,*select_begin, *length); text_edit->select_end = NK_CLAMP(0,*select_end, *length); text_edit->string.buffer.allocated = (nk_size)*length; text_edit->string.buffer.memory.size = NK_MAX_NUMBER_BUFFER; text_edit->string.buffer.memory.ptr = dst; text_edit->string.buffer.size = NK_MAX_NUMBER_BUFFER; text_edit->mode = NK_TEXT_EDIT_MODE_INSERT; nk_do_edit(ws, out, edit, NK_EDIT_FIELD|NK_EDIT_AUTO_SELECT, filters[filter], text_edit, &style->edit, (*state == NK_PROPERTY_EDIT) ? in: 0, font); *length = text_edit->string.len; *cursor = text_edit->cursor; *select_begin = text_edit->select_start; *select_end = text_edit->select_end; if (text_edit->active && nk_input_is_key_pressed(in, NK_KEY_ENTER)) text_edit->active = nk_false; if (active && !text_edit->active) { /* property is now not active so convert edit text to value*/ *state = NK_PROPERTY_DEFAULT; buffer[*len] = '\0'; switch (variant->kind) { default: break; case NK_PROPERTY_INT: variant->value.i = nk_strtoi(buffer, 0); variant->value.i = NK_CLAMP(variant->min_value.i, variant->value.i, variant->max_value.i); break; case NK_PROPERTY_FLOAT: nk_string_float_limit(buffer, NK_MAX_FLOAT_PRECISION); variant->value.f = nk_strtof(buffer, 0); variant->value.f = NK_CLAMP(variant->min_value.f, variant->value.f, variant->max_value.f); break; case NK_PROPERTY_DOUBLE: nk_string_float_limit(buffer, NK_MAX_FLOAT_PRECISION); variant->value.d = nk_strtod(buffer, 0); variant->value.d = NK_CLAMP(variant->min_value.d, variant->value.d, variant->max_value.d); break; } } } NK_LIB struct nk_property_variant nk_property_variant_int(int value, int min_value, int max_value, int step) { struct nk_property_variant result; result.kind = NK_PROPERTY_INT; result.value.i = value; result.min_value.i = min_value; result.max_value.i = max_value; result.step.i = step; return result; } NK_LIB struct nk_property_variant nk_property_variant_float(float value, float min_value, float max_value, float step) { struct nk_property_variant result; result.kind = NK_PROPERTY_FLOAT; result.value.f = value; result.min_value.f = min_value; result.max_value.f = max_value; result.step.f = step; return result; } NK_LIB struct nk_property_variant nk_property_variant_double(double value, double min_value, double max_value, double step) { struct nk_property_variant result; result.kind = NK_PROPERTY_DOUBLE; result.value.d = value; result.min_value.d = min_value; result.max_value.d = max_value; result.step.d = step; return result; } NK_LIB void nk_property(struct nk_context *ctx, const char *name, struct nk_property_variant *variant, float inc_per_pixel, const enum nk_property_filter filter) { struct nk_window *win; struct nk_panel *layout; struct nk_input *in; const struct nk_style *style; struct nk_rect bounds; enum nk_widget_layout_states s; int *state = 0; nk_hash hash = 0; char *buffer = 0; int *len = 0; int *cursor = 0; int *select_begin = 0; int *select_end = 0; int old_state; char dummy_buffer[NK_MAX_NUMBER_BUFFER]; int dummy_state = NK_PROPERTY_DEFAULT; int dummy_length = 0; int dummy_cursor = 0; int dummy_select_begin = 0; int dummy_select_end = 0; NK_ASSERT(ctx); NK_ASSERT(ctx->current); NK_ASSERT(ctx->current->layout); if (!ctx || !ctx->current || !ctx->current->layout) return; win = ctx->current; layout = win->layout; style = &ctx->style; s = nk_widget(&bounds, ctx); if (!s) return; /* calculate hash from name */ if (name[0] == '#') { hash = nk_murmur_hash(name, (int)nk_strlen(name), win->property.seq++); name++; /* special number hash */ } else hash = nk_murmur_hash(name, (int)nk_strlen(name), 42); /* check if property is currently hot item */ if (win->property.active && hash == win->property.name) { buffer = win->property.buffer; len = &win->property.length; cursor = &win->property.cursor; state = &win->property.state; select_begin = &win->property.select_start; select_end = &win->property.select_end; } else { buffer = dummy_buffer; len = &dummy_length; cursor = &dummy_cursor; state = &dummy_state; select_begin = &dummy_select_begin; select_end = &dummy_select_end; } /* execute property widget */ old_state = *state; ctx->text_edit.clip = ctx->clip; in = ((s == NK_WIDGET_ROM && !win->property.active) || layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input; nk_do_property(&ctx->last_widget_state, &win->buffer, bounds, name, variant, inc_per_pixel, buffer, len, state, cursor, select_begin, select_end, &style->property, filter, in, style->font, &ctx->text_edit, ctx->button_behavior); if (in && *state != NK_PROPERTY_DEFAULT && !win->property.active) { /* current property is now hot */ win->property.active = 1; NK_MEMCPY(win->property.buffer, buffer, (nk_size)*len); win->property.length = *len; win->property.cursor = *cursor; win->property.state = *state; win->property.name = hash; win->property.select_start = *select_begin; win->property.select_end = *select_end; if (*state == NK_PROPERTY_DRAG) { ctx->input.mouse.grab = nk_true; ctx->input.mouse.grabbed = nk_true; } } /* check if previously active property is now inactive */ if (*state == NK_PROPERTY_DEFAULT && old_state != NK_PROPERTY_DEFAULT) { if (old_state == NK_PROPERTY_DRAG) { ctx->input.mouse.grab = nk_false; ctx->input.mouse.grabbed = nk_false; ctx->input.mouse.ungrab = nk_true; } win->property.select_start = 0; win->property.select_end = 0; win->property.active = 0; } } NK_API void nk_property_int(struct nk_context *ctx, const char *name, int min, int *val, int max, int step, float inc_per_pixel) { struct nk_property_variant variant; NK_ASSERT(ctx); NK_ASSERT(name); NK_ASSERT(val); if (!ctx || !ctx->current || !name || !val) return; variant = nk_property_variant_int(*val, min, max, step); nk_property(ctx, name, &variant, inc_per_pixel, NK_FILTER_INT); *val = variant.value.i; } NK_API void nk_property_float(struct nk_context *ctx, const char *name, float min, float *val, float max, float step, float inc_per_pixel) { struct nk_property_variant variant; NK_ASSERT(ctx); NK_ASSERT(name); NK_ASSERT(val); if (!ctx || !ctx->current || !name || !val) return; variant = nk_property_variant_float(*val, min, max, step); nk_property(ctx, name, &variant, inc_per_pixel, NK_FILTER_FLOAT); *val = variant.value.f; } NK_API void nk_property_double(struct nk_context *ctx, const char *name, double min, double *val, double max, double step, float inc_per_pixel) { struct nk_property_variant variant; NK_ASSERT(ctx); NK_ASSERT(name); NK_ASSERT(val); if (!ctx || !ctx->current || !name || !val) return; variant = nk_property_variant_double(*val, min, max, step); nk_property(ctx, name, &variant, inc_per_pixel, NK_FILTER_FLOAT); *val = variant.value.d; } NK_API int nk_propertyi(struct nk_context *ctx, const char *name, int min, int val, int max, int step, float inc_per_pixel) { struct nk_property_variant variant; NK_ASSERT(ctx); NK_ASSERT(name); if (!ctx || !ctx->current || !name) return val; variant = nk_property_variant_int(val, min, max, step); nk_property(ctx, name, &variant, inc_per_pixel, NK_FILTER_INT); val = variant.value.i; return val; } NK_API float nk_propertyf(struct nk_context *ctx, const char *name, float min, float val, float max, float step, float inc_per_pixel) { struct nk_property_variant variant; NK_ASSERT(ctx); NK_ASSERT(name); if (!ctx || !ctx->current || !name) return val; variant = nk_property_variant_float(val, min, max, step); nk_property(ctx, name, &variant, inc_per_pixel, NK_FILTER_FLOAT); val = variant.value.f; return val; } NK_API double nk_propertyd(struct nk_context *ctx, const char *name, double min, double val, double max, double step, float inc_per_pixel) { struct nk_property_variant variant; NK_ASSERT(ctx); NK_ASSERT(name); if (!ctx || !ctx->current || !name) return val; variant = nk_property_variant_double(val, min, max, step); nk_property(ctx, name, &variant, inc_per_pixel, NK_FILTER_FLOAT); val = variant.value.d; return val; } /* ============================================================== * * CHART * * ===============================================================*/ NK_API nk_bool nk_chart_begin_colored(struct nk_context *ctx, enum nk_chart_type type, struct nk_color color, struct nk_color highlight, int count, float min_value, float max_value) { struct nk_window *win; struct nk_chart *chart; const struct nk_style *config; const struct nk_style_chart *style; const struct nk_style_item *background; struct nk_rect bounds = {0, 0, 0, 0}; NK_ASSERT(ctx); NK_ASSERT(ctx->current); NK_ASSERT(ctx->current->layout); if (!ctx || !ctx->current || !ctx->current->layout) return 0; if (!nk_widget(&bounds, ctx)) { chart = &ctx->current->layout->chart; nk_zero(chart, sizeof(*chart)); return 0; } win = ctx->current; config = &ctx->style; chart = &win->layout->chart; style = &config->chart; /* setup basic generic chart */ nk_zero(chart, sizeof(*chart)); chart->x = bounds.x + style->padding.x; chart->y = bounds.y + style->padding.y; chart->w = bounds.w - 2 * style->padding.x; chart->h = bounds.h - 2 * style->padding.y; chart->w = NK_MAX(chart->w, 2 * style->padding.x); chart->h = NK_MAX(chart->h, 2 * style->padding.y); /* add first slot into chart */ {struct nk_chart_slot *slot = &chart->slots[chart->slot++]; slot->type = type; slot->count = count; slot->color = color; slot->highlight = highlight; slot->min = NK_MIN(min_value, max_value); slot->max = NK_MAX(min_value, max_value); slot->range = slot->max - slot->min;} /* draw chart background */ background = &style->background; switch(background->type) { case NK_STYLE_ITEM_IMAGE: nk_draw_image(&win->buffer, bounds, &background->data.image, nk_white); break; case NK_STYLE_ITEM_NINE_SLICE: nk_draw_nine_slice(&win->buffer, bounds, &background->data.slice, nk_white); break; case NK_STYLE_ITEM_COLOR: nk_fill_rect(&win->buffer, bounds, style->rounding, style->border_color); nk_fill_rect(&win->buffer, nk_shrink_rect(bounds, style->border), style->rounding, style->background.data.color); break; } return 1; } NK_API nk_bool nk_chart_begin(struct nk_context *ctx, const enum nk_chart_type type, int count, float min_value, float max_value) { return nk_chart_begin_colored(ctx, type, ctx->style.chart.color, ctx->style.chart.selected_color, count, min_value, max_value); } NK_API void nk_chart_add_slot_colored(struct nk_context *ctx, const enum nk_chart_type type, struct nk_color color, struct nk_color highlight, int count, float min_value, float max_value) { NK_ASSERT(ctx); NK_ASSERT(ctx->current); NK_ASSERT(ctx->current->layout); NK_ASSERT(ctx->current->layout->chart.slot < NK_CHART_MAX_SLOT); if (!ctx || !ctx->current || !ctx->current->layout) return; if (ctx->current->layout->chart.slot >= NK_CHART_MAX_SLOT) return; /* add another slot into the graph */ {struct nk_chart *chart = &ctx->current->layout->chart; struct nk_chart_slot *slot = &chart->slots[chart->slot++]; slot->type = type; slot->count = count; slot->color = color; slot->highlight = highlight; slot->min = NK_MIN(min_value, max_value); slot->max = NK_MAX(min_value, max_value); slot->range = slot->max - slot->min;} } NK_API void nk_chart_add_slot(struct nk_context *ctx, const enum nk_chart_type type, int count, float min_value, float max_value) { nk_chart_add_slot_colored(ctx, type, ctx->style.chart.color, ctx->style.chart.selected_color, count, min_value, max_value); } NK_INTERN nk_flags nk_chart_push_line(struct nk_context *ctx, struct nk_window *win, struct nk_chart *g, float value, int slot) { struct nk_panel *layout = win->layout; const struct nk_input *i = &ctx->input; struct nk_command_buffer *out = &win->buffer; nk_flags ret = 0; struct nk_vec2 cur; struct nk_rect bounds; struct nk_color color; float step; float range; float ratio; NK_ASSERT(slot >= 0 && slot < NK_CHART_MAX_SLOT); step = g->w / (float)g->slots[slot].count; range = g->slots[slot].max - g->slots[slot].min; ratio = (value - g->slots[slot].min) / range; if (g->slots[slot].index == 0) { /* first data point does not have a connection */ g->slots[slot].last.x = g->x; g->slots[slot].last.y = (g->y + g->h) - ratio * (float)g->h; bounds.x = g->slots[slot].last.x - 2; bounds.y = g->slots[slot].last.y - 2; bounds.w = bounds.h = 4; color = g->slots[slot].color; if (!(layout->flags & NK_WINDOW_ROM) && NK_INBOX(i->mouse.pos.x,i->mouse.pos.y, g->slots[slot].last.x-3, g->slots[slot].last.y-3, 6, 6)){ ret = nk_input_is_mouse_hovering_rect(i, bounds) ? NK_CHART_HOVERING : 0; ret |= (i->mouse.buttons[NK_BUTTON_LEFT].down && i->mouse.buttons[NK_BUTTON_LEFT].clicked) ? NK_CHART_CLICKED: 0; color = g->slots[slot].highlight; } nk_fill_rect(out, bounds, 0, color); g->slots[slot].index += 1; return ret; } /* draw a line between the last data point and the new one */ color = g->slots[slot].color; cur.x = g->x + (float)(step * (float)g->slots[slot].index); cur.y = (g->y + g->h) - (ratio * (float)g->h); nk_stroke_line(out, g->slots[slot].last.x, g->slots[slot].last.y, cur.x, cur.y, 1.0f, color); bounds.x = cur.x - 3; bounds.y = cur.y - 3; bounds.w = bounds.h = 6; /* user selection of current data point */ if (!(layout->flags & NK_WINDOW_ROM)) { if (nk_input_is_mouse_hovering_rect(i, bounds)) { ret = NK_CHART_HOVERING; ret |= (!i->mouse.buttons[NK_BUTTON_LEFT].down && i->mouse.buttons[NK_BUTTON_LEFT].clicked) ? NK_CHART_CLICKED: 0; color = g->slots[slot].highlight; } } nk_fill_rect(out, nk_rect(cur.x - 2, cur.y - 2, 4, 4), 0, color); /* save current data point position */ g->slots[slot].last.x = cur.x; g->slots[slot].last.y = cur.y; g->slots[slot].index += 1; return ret; } NK_INTERN nk_flags nk_chart_push_column(const struct nk_context *ctx, struct nk_window *win, struct nk_chart *chart, float value, int slot) { struct nk_command_buffer *out = &win->buffer; const struct nk_input *in = &ctx->input; struct nk_panel *layout = win->layout; float ratio; nk_flags ret = 0; struct nk_color color; struct nk_rect item = {0,0,0,0}; NK_ASSERT(slot >= 0 && slot < NK_CHART_MAX_SLOT); if (chart->slots[slot].index >= chart->slots[slot].count) return nk_false; if (chart->slots[slot].count) { float padding = (float)(chart->slots[slot].count-1); item.w = (chart->w - padding) / (float)(chart->slots[slot].count); } /* calculate bounds of current bar chart entry */ color = chart->slots[slot].color;; item.h = chart->h * NK_ABS((value/chart->slots[slot].range)); if (value >= 0) { ratio = (value + NK_ABS(chart->slots[slot].min)) / NK_ABS(chart->slots[slot].range); item.y = (chart->y + chart->h) - chart->h * ratio; } else { ratio = (value - chart->slots[slot].max) / chart->slots[slot].range; item.y = chart->y + (chart->h * NK_ABS(ratio)) - item.h; } item.x = chart->x + ((float)chart->slots[slot].index * item.w); item.x = item.x + ((float)chart->slots[slot].index); /* user chart bar selection */ if (!(layout->flags & NK_WINDOW_ROM) && NK_INBOX(in->mouse.pos.x,in->mouse.pos.y,item.x,item.y,item.w,item.h)) { ret = NK_CHART_HOVERING; ret |= (!in->mouse.buttons[NK_BUTTON_LEFT].down && in->mouse.buttons[NK_BUTTON_LEFT].clicked) ? NK_CHART_CLICKED: 0; color = chart->slots[slot].highlight; } nk_fill_rect(out, item, 0, color); chart->slots[slot].index += 1; return ret; } NK_API nk_flags nk_chart_push_slot(struct nk_context *ctx, float value, int slot) { nk_flags flags; struct nk_window *win; NK_ASSERT(ctx); NK_ASSERT(ctx->current); NK_ASSERT(slot >= 0 && slot < NK_CHART_MAX_SLOT); NK_ASSERT(slot < ctx->current->layout->chart.slot); if (!ctx || !ctx->current || slot >= NK_CHART_MAX_SLOT) return nk_false; if (slot >= ctx->current->layout->chart.slot) return nk_false; win = ctx->current; if (win->layout->chart.slot < slot) return nk_false; switch (win->layout->chart.slots[slot].type) { case NK_CHART_LINES: flags = nk_chart_push_line(ctx, win, &win->layout->chart, value, slot); break; case NK_CHART_COLUMN: flags = nk_chart_push_column(ctx, win, &win->layout->chart, value, slot); break; default: case NK_CHART_MAX: flags = 0; } return flags; } NK_API nk_flags nk_chart_push(struct nk_context *ctx, float value) { return nk_chart_push_slot(ctx, value, 0); } NK_API void nk_chart_end(struct nk_context *ctx) { struct nk_window *win; struct nk_chart *chart; NK_ASSERT(ctx); NK_ASSERT(ctx->current); if (!ctx || !ctx->current) return; win = ctx->current; chart = &win->layout->chart; NK_MEMSET(chart, 0, sizeof(*chart)); return; } NK_API void nk_plot(struct nk_context *ctx, enum nk_chart_type type, const float *values, int count, int offset) { int i = 0; float min_value; float max_value; NK_ASSERT(ctx); NK_ASSERT(values); if (!ctx || !values || !count) return; min_value = values[offset]; max_value = values[offset]; for (i = 0; i < count; ++i) { min_value = NK_MIN(values[i + offset], min_value); max_value = NK_MAX(values[i + offset], max_value); } if (nk_chart_begin(ctx, type, count, min_value, max_value)) { for (i = 0; i < count; ++i) nk_chart_push(ctx, values[i + offset]); nk_chart_end(ctx); } } NK_API void nk_plot_function(struct nk_context *ctx, enum nk_chart_type type, void *userdata, float(*value_getter)(void* user, int index), int count, int offset) { int i = 0; float min_value; float max_value; NK_ASSERT(ctx); NK_ASSERT(value_getter); if (!ctx || !value_getter || !count) return; max_value = min_value = value_getter(userdata, offset); for (i = 0; i < count; ++i) { float value = value_getter(userdata, i + offset); min_value = NK_MIN(value, min_value); max_value = NK_MAX(value, max_value); } if (nk_chart_begin(ctx, type, count, min_value, max_value)) { for (i = 0; i < count; ++i) nk_chart_push(ctx, value_getter(userdata, i + offset)); nk_chart_end(ctx); } } /* ============================================================== * * COLOR PICKER * * ===============================================================*/ NK_LIB nk_bool nk_color_picker_behavior(nk_flags *state, const struct nk_rect *bounds, const struct nk_rect *matrix, const struct nk_rect *hue_bar, const struct nk_rect *alpha_bar, struct nk_colorf *color, const struct nk_input *in) { float hsva[4]; nk_bool value_changed = 0; nk_bool hsv_changed = 0; NK_ASSERT(state); NK_ASSERT(matrix); NK_ASSERT(hue_bar); NK_ASSERT(color); /* color matrix */ nk_colorf_hsva_fv(hsva, *color); if (nk_button_behavior(state, *matrix, in, NK_BUTTON_REPEATER)) { hsva[1] = NK_SATURATE((in->mouse.pos.x - matrix->x) / (matrix->w-1)); hsva[2] = 1.0f - NK_SATURATE((in->mouse.pos.y - matrix->y) / (matrix->h-1)); value_changed = hsv_changed = 1; } /* hue bar */ if (nk_button_behavior(state, *hue_bar, in, NK_BUTTON_REPEATER)) { hsva[0] = NK_SATURATE((in->mouse.pos.y - hue_bar->y) / (hue_bar->h-1)); value_changed = hsv_changed = 1; } /* alpha bar */ if (alpha_bar) { if (nk_button_behavior(state, *alpha_bar, in, NK_BUTTON_REPEATER)) { hsva[3] = 1.0f - NK_SATURATE((in->mouse.pos.y - alpha_bar->y) / (alpha_bar->h-1)); value_changed = 1; } } nk_widget_state_reset(state); if (hsv_changed) { *color = nk_hsva_colorfv(hsva); *state = NK_WIDGET_STATE_ACTIVE; } if (value_changed) { color->a = hsva[3]; *state = NK_WIDGET_STATE_ACTIVE; } /* set color picker widget state */ if (nk_input_is_mouse_hovering_rect(in, *bounds)) *state = NK_WIDGET_STATE_HOVERED; if (*state & NK_WIDGET_STATE_HOVER && !nk_input_is_mouse_prev_hovering_rect(in, *bounds)) *state |= NK_WIDGET_STATE_ENTERED; else if (nk_input_is_mouse_prev_hovering_rect(in, *bounds)) *state |= NK_WIDGET_STATE_LEFT; return value_changed; } NK_LIB void nk_draw_color_picker(struct nk_command_buffer *o, const struct nk_rect *matrix, const struct nk_rect *hue_bar, const struct nk_rect *alpha_bar, struct nk_colorf col) { NK_STORAGE const struct nk_color black = {0,0,0,255}; NK_STORAGE const struct nk_color white = {255, 255, 255, 255}; NK_STORAGE const struct nk_color black_trans = {0,0,0,0}; const float crosshair_size = 7.0f; struct nk_color temp; float hsva[4]; float line_y; int i; NK_ASSERT(o); NK_ASSERT(matrix); NK_ASSERT(hue_bar); /* draw hue bar */ nk_colorf_hsva_fv(hsva, col); for (i = 0; i < 6; ++i) { NK_GLOBAL const struct nk_color hue_colors[] = { {255, 0, 0, 255}, {255,255,0,255}, {0,255,0,255}, {0, 255,255,255}, {0,0,255,255}, {255, 0, 255, 255}, {255, 0, 0, 255} }; nk_fill_rect_multi_color(o, nk_rect(hue_bar->x, hue_bar->y + (float)i * (hue_bar->h/6.0f) + 0.5f, hue_bar->w, (hue_bar->h/6.0f) + 0.5f), hue_colors[i], hue_colors[i], hue_colors[i+1], hue_colors[i+1]); } line_y = (float)(int)(hue_bar->y + hsva[0] * matrix->h + 0.5f); nk_stroke_line(o, hue_bar->x-1, line_y, hue_bar->x + hue_bar->w + 2, line_y, 1, nk_rgb(255,255,255)); /* draw alpha bar */ if (alpha_bar) { float alpha = NK_SATURATE(col.a); line_y = (float)(int)(alpha_bar->y + (1.0f - alpha) * matrix->h + 0.5f); nk_fill_rect_multi_color(o, *alpha_bar, white, white, black, black); nk_stroke_line(o, alpha_bar->x-1, line_y, alpha_bar->x + alpha_bar->w + 2, line_y, 1, nk_rgb(255,255,255)); } /* draw color matrix */ temp = nk_hsv_f(hsva[0], 1.0f, 1.0f); nk_fill_rect_multi_color(o, *matrix, white, temp, temp, white); nk_fill_rect_multi_color(o, *matrix, black_trans, black_trans, black, black); /* draw cross-hair */ {struct nk_vec2 p; float S = hsva[1]; float V = hsva[2]; p.x = (float)(int)(matrix->x + S * matrix->w); p.y = (float)(int)(matrix->y + (1.0f - V) * matrix->h); nk_stroke_line(o, p.x - crosshair_size, p.y, p.x-2, p.y, 1.0f, white); nk_stroke_line(o, p.x + crosshair_size + 1, p.y, p.x+3, p.y, 1.0f, white); nk_stroke_line(o, p.x, p.y + crosshair_size + 1, p.x, p.y+3, 1.0f, white); nk_stroke_line(o, p.x, p.y - crosshair_size, p.x, p.y-2, 1.0f, white);} } NK_LIB nk_bool nk_do_color_picker(nk_flags *state, struct nk_command_buffer *out, struct nk_colorf *col, enum nk_color_format fmt, struct nk_rect bounds, struct nk_vec2 padding, const struct nk_input *in, const struct nk_user_font *font) { int ret = 0; struct nk_rect matrix; struct nk_rect hue_bar; struct nk_rect alpha_bar; float bar_w; NK_ASSERT(out); NK_ASSERT(col); NK_ASSERT(state); NK_ASSERT(font); if (!out || !col || !state || !font) return ret; bar_w = font->height; bounds.x += padding.x; bounds.y += padding.x; bounds.w -= 2 * padding.x; bounds.h -= 2 * padding.y; matrix.x = bounds.x; matrix.y = bounds.y; matrix.h = bounds.h; matrix.w = bounds.w - (3 * padding.x + 2 * bar_w); hue_bar.w = bar_w; hue_bar.y = bounds.y; hue_bar.h = matrix.h; hue_bar.x = matrix.x + matrix.w + padding.x; alpha_bar.x = hue_bar.x + hue_bar.w + padding.x; alpha_bar.y = bounds.y; alpha_bar.w = bar_w; alpha_bar.h = matrix.h; ret = nk_color_picker_behavior(state, &bounds, &matrix, &hue_bar, (fmt == NK_RGBA) ? &alpha_bar:0, col, in); nk_draw_color_picker(out, &matrix, &hue_bar, (fmt == NK_RGBA) ? &alpha_bar:0, *col); return ret; } NK_API nk_bool nk_color_pick(struct nk_context * ctx, struct nk_colorf *color, enum nk_color_format fmt) { struct nk_window *win; struct nk_panel *layout; const struct nk_style *config; const struct nk_input *in; enum nk_widget_layout_states state; struct nk_rect bounds; NK_ASSERT(ctx); NK_ASSERT(color); NK_ASSERT(ctx->current); NK_ASSERT(ctx->current->layout); if (!ctx || !ctx->current || !ctx->current->layout || !color) return 0; win = ctx->current; config = &ctx->style; layout = win->layout; state = nk_widget(&bounds, ctx); if (!state) return 0; in = (state == NK_WIDGET_ROM || layout->flags & NK_WINDOW_ROM) ? 0 : &ctx->input; return nk_do_color_picker(&ctx->last_widget_state, &win->buffer, color, fmt, bounds, nk_vec2(0,0), in, config->font); } NK_API struct nk_colorf nk_color_picker(struct nk_context *ctx, struct nk_colorf color, enum nk_color_format fmt) { nk_color_pick(ctx, &color, fmt); return color; } /* ============================================================== * * COMBO * * ===============================================================*/ NK_INTERN nk_bool nk_combo_begin(struct nk_context *ctx, struct nk_window *win, struct nk_vec2 size, nk_bool is_clicked, struct nk_rect header) { struct nk_window *popup; int is_open = 0; int is_active = 0; struct nk_rect body; nk_hash hash; NK_ASSERT(ctx); NK_ASSERT(ctx->current); NK_ASSERT(ctx->current->layout); if (!ctx || !ctx->current || !ctx->current->layout) return 0; popup = win->popup.win; body.x = header.x; body.w = size.x; body.y = header.y + header.h-ctx->style.window.combo_border; body.h = size.y; hash = win->popup.combo_count++; is_open = (popup) ? nk_true:nk_false; is_active = (popup && (win->popup.name == hash) && win->popup.type == NK_PANEL_COMBO); if ((is_clicked && is_open && !is_active) || (is_open && !is_active) || (!is_open && !is_active && !is_clicked)) return 0; if (!nk_nonblock_begin(ctx, 0, body, (is_clicked && is_open)?nk_rect(0,0,0,0):header, NK_PANEL_COMBO)) return 0; win->popup.type = NK_PANEL_COMBO; win->popup.name = hash; return 1; } NK_API nk_bool nk_combo_begin_text(struct nk_context *ctx, const char *selected, int len, struct nk_vec2 size) { const struct nk_input *in; struct nk_window *win; struct nk_style *style; enum nk_widget_layout_states s; int is_clicked = nk_false; struct nk_rect header; const struct nk_style_item *background; struct nk_text text; NK_ASSERT(ctx); NK_ASSERT(selected); NK_ASSERT(ctx->current); NK_ASSERT(ctx->current->layout); if (!ctx || !ctx->current || !ctx->current->layout || !selected) return 0; win = ctx->current; style = &ctx->style; s = nk_widget(&header, ctx); if (s == NK_WIDGET_INVALID) return 0; in = (win->layout->flags & NK_WINDOW_ROM || s == NK_WIDGET_ROM)? 0: &ctx->input; if (nk_button_behavior(&ctx->last_widget_state, header, in, NK_BUTTON_DEFAULT)) is_clicked = nk_true; /* draw combo box header background and border */ if (ctx->last_widget_state & NK_WIDGET_STATE_ACTIVED) { background = &style->combo.active; text.text = style->combo.label_active; } else if (ctx->last_widget_state & NK_WIDGET_STATE_HOVER) { background = &style->combo.hover; text.text = style->combo.label_hover; } else { background = &style->combo.normal; text.text = style->combo.label_normal; } switch(background->type) { case NK_STYLE_ITEM_IMAGE: text.background = nk_rgba(0, 0, 0, 0); nk_draw_image(&win->buffer, header, &background->data.image, nk_white); break; case NK_STYLE_ITEM_NINE_SLICE: text.background = nk_rgba(0, 0, 0, 0); nk_draw_nine_slice(&win->buffer, header, &background->data.slice, nk_white); break; case NK_STYLE_ITEM_COLOR: text.background = background->data.color; nk_fill_rect(&win->buffer, header, style->combo.rounding, background->data.color); nk_stroke_rect(&win->buffer, header, style->combo.rounding, style->combo.border, style->combo.border_color); break; } { /* print currently selected text item */ struct nk_rect label; struct nk_rect button; struct nk_rect content; int draw_button_symbol; enum nk_symbol_type sym; if (ctx->last_widget_state & NK_WIDGET_STATE_HOVER) sym = style->combo.sym_hover; else if (is_clicked) sym = style->combo.sym_active; else sym = style->combo.sym_normal; /* represents whether or not the combo's button symbol should be drawn */ draw_button_symbol = sym != NK_SYMBOL_NONE; /* calculate button */ button.w = header.h - 2 * style->combo.button_padding.y; button.x = (header.x + header.w - header.h) - style->combo.button_padding.x; button.y = header.y + style->combo.button_padding.y; button.h = button.w; content.x = button.x + style->combo.button.padding.x; content.y = button.y + style->combo.button.padding.y; content.w = button.w - 2 * style->combo.button.padding.x; content.h = button.h - 2 * style->combo.button.padding.y; /* draw selected label */ text.padding = nk_vec2(0,0); label.x = header.x + style->combo.content_padding.x; label.y = header.y + style->combo.content_padding.y; label.h = header.h - 2 * style->combo.content_padding.y; if (draw_button_symbol) label.w = button.x - (style->combo.content_padding.x + style->combo.spacing.x) - label.x; else label.w = header.w - 2 * style->combo.content_padding.x; nk_widget_text(&win->buffer, label, selected, len, &text, NK_TEXT_LEFT, ctx->style.font); /* draw open/close button */ if (draw_button_symbol) nk_draw_button_symbol(&win->buffer, &button, &content, ctx->last_widget_state, &ctx->style.combo.button, sym, style->font); } return nk_combo_begin(ctx, win, size, is_clicked, header); } NK_API nk_bool nk_combo_begin_label(struct nk_context *ctx, const char *selected, struct nk_vec2 size) { return nk_combo_begin_text(ctx, selected, nk_strlen(selected), size); } NK_API nk_bool nk_combo_begin_color(struct nk_context *ctx, struct nk_color color, struct nk_vec2 size) { struct nk_window *win; struct nk_style *style; const struct nk_input *in; struct nk_rect header; int is_clicked = nk_false; enum nk_widget_layout_states s; const struct nk_style_item *background; NK_ASSERT(ctx); NK_ASSERT(ctx->current); NK_ASSERT(ctx->current->layout); if (!ctx || !ctx->current || !ctx->current->layout) return 0; win = ctx->current; style = &ctx->style; s = nk_widget(&header, ctx); if (s == NK_WIDGET_INVALID) return 0; in = (win->layout->flags & NK_WINDOW_ROM || s == NK_WIDGET_ROM)? 0: &ctx->input; if (nk_button_behavior(&ctx->last_widget_state, header, in, NK_BUTTON_DEFAULT)) is_clicked = nk_true; /* draw combo box header background and border */ if (ctx->last_widget_state & NK_WIDGET_STATE_ACTIVED) background = &style->combo.active; else if (ctx->last_widget_state & NK_WIDGET_STATE_HOVER) background = &style->combo.hover; else background = &style->combo.normal; switch(background->type) { case NK_STYLE_ITEM_IMAGE: nk_draw_image(&win->buffer, header, &background->data.image, nk_white); break; case NK_STYLE_ITEM_NINE_SLICE: nk_draw_nine_slice(&win->buffer, header, &background->data.slice, nk_white); break; case NK_STYLE_ITEM_COLOR: nk_fill_rect(&win->buffer, header, style->combo.rounding, background->data.color); nk_stroke_rect(&win->buffer, header, style->combo.rounding, style->combo.border, style->combo.border_color); break; } { struct nk_rect content; struct nk_rect button; struct nk_rect bounds; int draw_button_symbol; enum nk_symbol_type sym; if (ctx->last_widget_state & NK_WIDGET_STATE_HOVER) sym = style->combo.sym_hover; else if (is_clicked) sym = style->combo.sym_active; else sym = style->combo.sym_normal; /* represents whether or not the combo's button symbol should be drawn */ draw_button_symbol = sym != NK_SYMBOL_NONE; /* calculate button */ button.w = header.h - 2 * style->combo.button_padding.y; button.x = (header.x + header.w - header.h) - style->combo.button_padding.x; button.y = header.y + style->combo.button_padding.y; button.h = button.w; content.x = button.x + style->combo.button.padding.x; content.y = button.y + style->combo.button.padding.y; content.w = button.w - 2 * style->combo.button.padding.x; content.h = button.h - 2 * style->combo.button.padding.y; /* draw color */ bounds.h = header.h - 4 * style->combo.content_padding.y; bounds.y = header.y + 2 * style->combo.content_padding.y; bounds.x = header.x + 2 * style->combo.content_padding.x; if (draw_button_symbol) bounds.w = (button.x - (style->combo.content_padding.x + style->combo.spacing.x)) - bounds.x; else bounds.w = header.w - 4 * style->combo.content_padding.x; nk_fill_rect(&win->buffer, bounds, 0, color); /* draw open/close button */ if (draw_button_symbol) nk_draw_button_symbol(&win->buffer, &button, &content, ctx->last_widget_state, &ctx->style.combo.button, sym, style->font); } return nk_combo_begin(ctx, win, size, is_clicked, header); } NK_API nk_bool nk_combo_begin_symbol(struct nk_context *ctx, enum nk_symbol_type symbol, struct nk_vec2 size) { struct nk_window *win; struct nk_style *style; const struct nk_input *in; struct nk_rect header; int is_clicked = nk_false; enum nk_widget_layout_states s; const struct nk_style_item *background; struct nk_color sym_background; struct nk_color symbol_color; NK_ASSERT(ctx); NK_ASSERT(ctx->current); NK_ASSERT(ctx->current->layout); if (!ctx || !ctx->current || !ctx->current->layout) return 0; win = ctx->current; style = &ctx->style; s = nk_widget(&header, ctx); if (s == NK_WIDGET_INVALID) return 0; in = (win->layout->flags & NK_WINDOW_ROM || s == NK_WIDGET_ROM)? 0: &ctx->input; if (nk_button_behavior(&ctx->last_widget_state, header, in, NK_BUTTON_DEFAULT)) is_clicked = nk_true; /* draw combo box header background and border */ if (ctx->last_widget_state & NK_WIDGET_STATE_ACTIVED) { background = &style->combo.active; symbol_color = style->combo.symbol_active; } else if (ctx->last_widget_state & NK_WIDGET_STATE_HOVER) { background = &style->combo.hover; symbol_color = style->combo.symbol_hover; } else { background = &style->combo.normal; symbol_color = style->combo.symbol_hover; } switch(background->type) { case NK_STYLE_ITEM_IMAGE: sym_background = nk_rgba(0, 0, 0, 0); nk_draw_image(&win->buffer, header, &background->data.image, nk_white); break; case NK_STYLE_ITEM_NINE_SLICE: sym_background = nk_rgba(0, 0, 0, 0); nk_draw_nine_slice(&win->buffer, header, &background->data.slice, nk_white); break; case NK_STYLE_ITEM_COLOR: sym_background = background->data.color; nk_fill_rect(&win->buffer, header, style->combo.rounding, background->data.color); nk_stroke_rect(&win->buffer, header, style->combo.rounding, style->combo.border, style->combo.border_color); break; } { struct nk_rect bounds = {0,0,0,0}; struct nk_rect content; struct nk_rect button; enum nk_symbol_type sym; if (ctx->last_widget_state & NK_WIDGET_STATE_HOVER) sym = style->combo.sym_hover; else if (is_clicked) sym = style->combo.sym_active; else sym = style->combo.sym_normal; /* calculate button */ button.w = header.h - 2 * style->combo.button_padding.y; button.x = (header.x + header.w - header.h) - style->combo.button_padding.y; button.y = header.y + style->combo.button_padding.y; button.h = button.w; content.x = button.x + style->combo.button.padding.x; content.y = button.y + style->combo.button.padding.y; content.w = button.w - 2 * style->combo.button.padding.x; content.h = button.h - 2 * style->combo.button.padding.y; /* draw symbol */ bounds.h = header.h - 2 * style->combo.content_padding.y; bounds.y = header.y + style->combo.content_padding.y; bounds.x = header.x + style->combo.content_padding.x; bounds.w = (button.x - style->combo.content_padding.y) - bounds.x; nk_draw_symbol(&win->buffer, symbol, bounds, sym_background, symbol_color, 1.0f, style->font); /* draw open/close button */ nk_draw_button_symbol(&win->buffer, &bounds, &content, ctx->last_widget_state, &ctx->style.combo.button, sym, style->font); } return nk_combo_begin(ctx, win, size, is_clicked, header); } NK_API nk_bool nk_combo_begin_symbol_text(struct nk_context *ctx, const char *selected, int len, enum nk_symbol_type symbol, struct nk_vec2 size) { struct nk_window *win; struct nk_style *style; struct nk_input *in; struct nk_rect header; int is_clicked = nk_false; enum nk_widget_layout_states s; const struct nk_style_item *background; struct nk_color symbol_color; struct nk_text text; NK_ASSERT(ctx); NK_ASSERT(ctx->current); NK_ASSERT(ctx->current->layout); if (!ctx || !ctx->current || !ctx->current->layout) return 0; win = ctx->current; style = &ctx->style; s = nk_widget(&header, ctx); if (!s) return 0; in = (win->layout->flags & NK_WINDOW_ROM || s == NK_WIDGET_ROM)? 0: &ctx->input; if (nk_button_behavior(&ctx->last_widget_state, header, in, NK_BUTTON_DEFAULT)) is_clicked = nk_true; /* draw combo box header background and border */ if (ctx->last_widget_state & NK_WIDGET_STATE_ACTIVED) { background = &style->combo.active; symbol_color = style->combo.symbol_active; text.text = style->combo.label_active; } else if (ctx->last_widget_state & NK_WIDGET_STATE_HOVER) { background = &style->combo.hover; symbol_color = style->combo.symbol_hover; text.text = style->combo.label_hover; } else { background = &style->combo.normal; symbol_color = style->combo.symbol_normal; text.text = style->combo.label_normal; } switch(background->type) { case NK_STYLE_ITEM_IMAGE: text.background = nk_rgba(0, 0, 0, 0); nk_draw_image(&win->buffer, header, &background->data.image, nk_white); break; case NK_STYLE_ITEM_NINE_SLICE: text.background = nk_rgba(0, 0, 0, 0); nk_draw_nine_slice(&win->buffer, header, &background->data.slice, nk_white); break; case NK_STYLE_ITEM_COLOR: text.background = background->data.color; nk_fill_rect(&win->buffer, header, style->combo.rounding, background->data.color); nk_stroke_rect(&win->buffer, header, style->combo.rounding, style->combo.border, style->combo.border_color); break; } { struct nk_rect content; struct nk_rect button; struct nk_rect label; struct nk_rect image; enum nk_symbol_type sym; if (ctx->last_widget_state & NK_WIDGET_STATE_HOVER) sym = style->combo.sym_hover; else if (is_clicked) sym = style->combo.sym_active; else sym = style->combo.sym_normal; /* calculate button */ button.w = header.h - 2 * style->combo.button_padding.y; button.x = (header.x + header.w - header.h) - style->combo.button_padding.x; button.y = header.y + style->combo.button_padding.y; button.h = button.w; content.x = button.x + style->combo.button.padding.x; content.y = button.y + style->combo.button.padding.y; content.w = button.w - 2 * style->combo.button.padding.x; content.h = button.h - 2 * style->combo.button.padding.y; nk_draw_button_symbol(&win->buffer, &button, &content, ctx->last_widget_state, &ctx->style.combo.button, sym, style->font); /* draw symbol */ image.x = header.x + style->combo.content_padding.x; image.y = header.y + style->combo.content_padding.y; image.h = header.h - 2 * style->combo.content_padding.y; image.w = image.h; nk_draw_symbol(&win->buffer, symbol, image, text.background, symbol_color, 1.0f, style->font); /* draw label */ text.padding = nk_vec2(0,0); label.x = image.x + image.w + style->combo.spacing.x + style->combo.content_padding.x; label.y = header.y + style->combo.content_padding.y; label.w = (button.x - style->combo.content_padding.x) - label.x; label.h = header.h - 2 * style->combo.content_padding.y; nk_widget_text(&win->buffer, label, selected, len, &text, NK_TEXT_LEFT, style->font); } return nk_combo_begin(ctx, win, size, is_clicked, header); } NK_API nk_bool nk_combo_begin_image(struct nk_context *ctx, struct nk_image img, struct nk_vec2 size) { struct nk_window *win; struct nk_style *style; const struct nk_input *in; struct nk_rect header; int is_clicked = nk_false; enum nk_widget_layout_states s; const struct nk_style_item *background; NK_ASSERT(ctx); NK_ASSERT(ctx->current); NK_ASSERT(ctx->current->layout); if (!ctx || !ctx->current || !ctx->current->layout) return 0; win = ctx->current; style = &ctx->style; s = nk_widget(&header, ctx); if (s == NK_WIDGET_INVALID) return 0; in = (win->layout->flags & NK_WINDOW_ROM || s == NK_WIDGET_ROM)? 0: &ctx->input; if (nk_button_behavior(&ctx->last_widget_state, header, in, NK_BUTTON_DEFAULT)) is_clicked = nk_true; /* draw combo box header background and border */ if (ctx->last_widget_state & NK_WIDGET_STATE_ACTIVED) background = &style->combo.active; else if (ctx->last_widget_state & NK_WIDGET_STATE_HOVER) background = &style->combo.hover; else background = &style->combo.normal; switch (background->type) { case NK_STYLE_ITEM_IMAGE: nk_draw_image(&win->buffer, header, &background->data.image, nk_white); break; case NK_STYLE_ITEM_NINE_SLICE: nk_draw_nine_slice(&win->buffer, header, &background->data.slice, nk_white); break; case NK_STYLE_ITEM_COLOR: nk_fill_rect(&win->buffer, header, style->combo.rounding, background->data.color); nk_stroke_rect(&win->buffer, header, style->combo.rounding, style->combo.border, style->combo.border_color); break; } { struct nk_rect bounds = {0,0,0,0}; struct nk_rect content; struct nk_rect button; int draw_button_symbol; enum nk_symbol_type sym; if (ctx->last_widget_state & NK_WIDGET_STATE_HOVER) sym = style->combo.sym_hover; else if (is_clicked) sym = style->combo.sym_active; else sym = style->combo.sym_normal; /* represents whether or not the combo's button symbol should be drawn */ draw_button_symbol = sym != NK_SYMBOL_NONE; /* calculate button */ button.w = header.h - 2 * style->combo.button_padding.y; button.x = (header.x + header.w - header.h) - style->combo.button_padding.y; button.y = header.y + style->combo.button_padding.y; button.h = button.w; content.x = button.x + style->combo.button.padding.x; content.y = button.y + style->combo.button.padding.y; content.w = button.w - 2 * style->combo.button.padding.x; content.h = button.h - 2 * style->combo.button.padding.y; /* draw image */ bounds.h = header.h - 2 * style->combo.content_padding.y; bounds.y = header.y + style->combo.content_padding.y; bounds.x = header.x + style->combo.content_padding.x; if (draw_button_symbol) bounds.w = (button.x - style->combo.content_padding.y) - bounds.x; else bounds.w = header.w - 2 * style->combo.content_padding.x; nk_draw_image(&win->buffer, bounds, &img, nk_white); /* draw open/close button */ if (draw_button_symbol) nk_draw_button_symbol(&win->buffer, &bounds, &content, ctx->last_widget_state, &ctx->style.combo.button, sym, style->font); } return nk_combo_begin(ctx, win, size, is_clicked, header); } NK_API nk_bool nk_combo_begin_image_text(struct nk_context *ctx, const char *selected, int len, struct nk_image img, struct nk_vec2 size) { struct nk_window *win; struct nk_style *style; struct nk_input *in; struct nk_rect header; int is_clicked = nk_false; enum nk_widget_layout_states s; const struct nk_style_item *background; struct nk_text text; NK_ASSERT(ctx); NK_ASSERT(ctx->current); NK_ASSERT(ctx->current->layout); if (!ctx || !ctx->current || !ctx->current->layout) return 0; win = ctx->current; style = &ctx->style; s = nk_widget(&header, ctx); if (!s) return 0; in = (win->layout->flags & NK_WINDOW_ROM || s == NK_WIDGET_ROM)? 0: &ctx->input; if (nk_button_behavior(&ctx->last_widget_state, header, in, NK_BUTTON_DEFAULT)) is_clicked = nk_true; /* draw combo box header background and border */ if (ctx->last_widget_state & NK_WIDGET_STATE_ACTIVED) { background = &style->combo.active; text.text = style->combo.label_active; } else if (ctx->last_widget_state & NK_WIDGET_STATE_HOVER) { background = &style->combo.hover; text.text = style->combo.label_hover; } else { background = &style->combo.normal; text.text = style->combo.label_normal; } switch(background->type) { case NK_STYLE_ITEM_IMAGE: text.background = nk_rgba(0, 0, 0, 0); nk_draw_image(&win->buffer, header, &background->data.image, nk_white); break; case NK_STYLE_ITEM_NINE_SLICE: text.background = nk_rgba(0, 0, 0, 0); nk_draw_nine_slice(&win->buffer, header, &background->data.slice, nk_white); break; case NK_STYLE_ITEM_COLOR: text.background = background->data.color; nk_fill_rect(&win->buffer, header, style->combo.rounding, background->data.color); nk_stroke_rect(&win->buffer, header, style->combo.rounding, style->combo.border, style->combo.border_color); break; } { struct nk_rect content; struct nk_rect button; struct nk_rect label; struct nk_rect image; int draw_button_symbol; enum nk_symbol_type sym; if (ctx->last_widget_state & NK_WIDGET_STATE_HOVER) sym = style->combo.sym_hover; else if (is_clicked) sym = style->combo.sym_active; else sym = style->combo.sym_normal; /* represents whether or not the combo's button symbol should be drawn */ draw_button_symbol = sym != NK_SYMBOL_NONE; /* calculate button */ button.w = header.h - 2 * style->combo.button_padding.y; button.x = (header.x + header.w - header.h) - style->combo.button_padding.x; button.y = header.y + style->combo.button_padding.y; button.h = button.w; content.x = button.x + style->combo.button.padding.x; content.y = button.y + style->combo.button.padding.y; content.w = button.w - 2 * style->combo.button.padding.x; content.h = button.h - 2 * style->combo.button.padding.y; if (draw_button_symbol) nk_draw_button_symbol(&win->buffer, &button, &content, ctx->last_widget_state, &ctx->style.combo.button, sym, style->font); /* draw image */ image.x = header.x + style->combo.content_padding.x; image.y = header.y + style->combo.content_padding.y; image.h = header.h - 2 * style->combo.content_padding.y; image.w = image.h; nk_draw_image(&win->buffer, image, &img, nk_white); /* draw label */ text.padding = nk_vec2(0,0); label.x = image.x + image.w + style->combo.spacing.x + style->combo.content_padding.x; label.y = header.y + style->combo.content_padding.y; label.h = header.h - 2 * style->combo.content_padding.y; if (draw_button_symbol) label.w = (button.x - style->combo.content_padding.x) - label.x; else label.w = (header.x + header.w - style->combo.content_padding.x) - label.x; nk_widget_text(&win->buffer, label, selected, len, &text, NK_TEXT_LEFT, style->font); } return nk_combo_begin(ctx, win, size, is_clicked, header); } NK_API nk_bool nk_combo_begin_symbol_label(struct nk_context *ctx, const char *selected, enum nk_symbol_type type, struct nk_vec2 size) { return nk_combo_begin_symbol_text(ctx, selected, nk_strlen(selected), type, size); } NK_API nk_bool nk_combo_begin_image_label(struct nk_context *ctx, const char *selected, struct nk_image img, struct nk_vec2 size) { return nk_combo_begin_image_text(ctx, selected, nk_strlen(selected), img, size); } NK_API nk_bool nk_combo_item_text(struct nk_context *ctx, const char *text, int len,nk_flags align) { return nk_contextual_item_text(ctx, text, len, align); } NK_API nk_bool nk_combo_item_label(struct nk_context *ctx, const char *label, nk_flags align) { return nk_contextual_item_label(ctx, label, align); } NK_API nk_bool nk_combo_item_image_text(struct nk_context *ctx, struct nk_image img, const char *text, int len, nk_flags alignment) { return nk_contextual_item_image_text(ctx, img, text, len, alignment); } NK_API nk_bool nk_combo_item_image_label(struct nk_context *ctx, struct nk_image img, const char *text, nk_flags alignment) { return nk_contextual_item_image_label(ctx, img, text, alignment); } NK_API nk_bool nk_combo_item_symbol_text(struct nk_context *ctx, enum nk_symbol_type sym, const char *text, int len, nk_flags alignment) { return nk_contextual_item_symbol_text(ctx, sym, text, len, alignment); } NK_API nk_bool nk_combo_item_symbol_label(struct nk_context *ctx, enum nk_symbol_type sym, const char *label, nk_flags alignment) { return nk_contextual_item_symbol_label(ctx, sym, label, alignment); } NK_API void nk_combo_end(struct nk_context *ctx) { nk_contextual_end(ctx); } NK_API void nk_combo_close(struct nk_context *ctx) { nk_contextual_close(ctx); } NK_API int nk_combo(struct nk_context *ctx, const char **items, int count, int selected, int item_height, struct nk_vec2 size) { int i = 0; int max_height; struct nk_vec2 item_spacing; struct nk_vec2 window_padding; NK_ASSERT(ctx); NK_ASSERT(items); NK_ASSERT(ctx->current); if (!ctx || !items ||!count) return selected; item_spacing = ctx->style.window.spacing; window_padding = nk_panel_get_padding(&ctx->style, ctx->current->layout->type); max_height = count * item_height + count * (int)item_spacing.y; max_height += (int)item_spacing.y * 2 + (int)window_padding.y * 2; size.y = NK_MIN(size.y, (float)max_height); if (nk_combo_begin_label(ctx, items[selected], size)) { nk_layout_row_dynamic(ctx, (float)item_height, 1); for (i = 0; i < count; ++i) { if (nk_combo_item_label(ctx, items[i], NK_TEXT_LEFT)) selected = i; } nk_combo_end(ctx); } return selected; } NK_API int nk_combo_separator(struct nk_context *ctx, const char *items_separated_by_separator, int separator, int selected, int count, int item_height, struct nk_vec2 size) { int i; int max_height; struct nk_vec2 item_spacing; struct nk_vec2 window_padding; const char *current_item; const char *iter; int length = 0; NK_ASSERT(ctx); NK_ASSERT(items_separated_by_separator); if (!ctx || !items_separated_by_separator) return selected; /* calculate popup window */ item_spacing = ctx->style.window.spacing; window_padding = nk_panel_get_padding(&ctx->style, ctx->current->layout->type); max_height = count * item_height + count * (int)item_spacing.y; max_height += (int)item_spacing.y * 2 + (int)window_padding.y * 2; size.y = NK_MIN(size.y, (float)max_height); /* find selected item */ current_item = items_separated_by_separator; for (i = 0; i < count; ++i) { iter = current_item; while (*iter && *iter != separator) iter++; length = (int)(iter - current_item); if (i == selected) break; current_item = iter + 1; } if (nk_combo_begin_text(ctx, current_item, length, size)) { current_item = items_separated_by_separator; nk_layout_row_dynamic(ctx, (float)item_height, 1); for (i = 0; i < count; ++i) { iter = current_item; while (*iter && *iter != separator) iter++; length = (int)(iter - current_item); if (nk_combo_item_text(ctx, current_item, length, NK_TEXT_LEFT)) selected = i; current_item = current_item + length + 1; } nk_combo_end(ctx); } return selected; } NK_API int nk_combo_string(struct nk_context *ctx, const char *items_separated_by_zeros, int selected, int count, int item_height, struct nk_vec2 size) { return nk_combo_separator(ctx, items_separated_by_zeros, '\0', selected, count, item_height, size); } NK_API int nk_combo_callback(struct nk_context *ctx, void(*item_getter)(void*, int, const char**), void *userdata, int selected, int count, int item_height, struct nk_vec2 size) { int i; int max_height; struct nk_vec2 item_spacing; struct nk_vec2 window_padding; const char *item; NK_ASSERT(ctx); NK_ASSERT(item_getter); if (!ctx || !item_getter) return selected; /* calculate popup window */ item_spacing = ctx->style.window.spacing; window_padding = nk_panel_get_padding(&ctx->style, ctx->current->layout->type); max_height = count * item_height + count * (int)item_spacing.y; max_height += (int)item_spacing.y * 2 + (int)window_padding.y * 2; size.y = NK_MIN(size.y, (float)max_height); item_getter(userdata, selected, &item); if (nk_combo_begin_label(ctx, item, size)) { nk_layout_row_dynamic(ctx, (float)item_height, 1); for (i = 0; i < count; ++i) { item_getter(userdata, i, &item); if (nk_combo_item_label(ctx, item, NK_TEXT_LEFT)) selected = i; } nk_combo_end(ctx); } return selected; } NK_API void nk_combobox(struct nk_context *ctx, const char **items, int count, int *selected, int item_height, struct nk_vec2 size) { *selected = nk_combo(ctx, items, count, *selected, item_height, size); } NK_API void nk_combobox_string(struct nk_context *ctx, const char *items_separated_by_zeros, int *selected, int count, int item_height, struct nk_vec2 size) { *selected = nk_combo_string(ctx, items_separated_by_zeros, *selected, count, item_height, size); } NK_API void nk_combobox_separator(struct nk_context *ctx, const char *items_separated_by_separator, int separator, int *selected, int count, int item_height, struct nk_vec2 size) { *selected = nk_combo_separator(ctx, items_separated_by_separator, separator, *selected, count, item_height, size); } NK_API void nk_combobox_callback(struct nk_context *ctx, void(*item_getter)(void* data, int id, const char **out_text), void *userdata, int *selected, int count, int item_height, struct nk_vec2 size) { *selected = nk_combo_callback(ctx, item_getter, userdata, *selected, count, item_height, size); } /* =============================================================== * * TOOLTIP * * ===============================================================*/ NK_API nk_bool nk_tooltip_begin(struct nk_context *ctx, float width) { int x,y,w,h; struct nk_window *win; const struct nk_input *in; struct nk_rect bounds; int ret; NK_ASSERT(ctx); NK_ASSERT(ctx->current); NK_ASSERT(ctx->current->layout); if (!ctx || !ctx->current || !ctx->current->layout) return 0; /* make sure that no nonblocking popup is currently active */ win = ctx->current; in = &ctx->input; if (win->popup.win && (win->popup.type & NK_PANEL_SET_NONBLOCK)) return 0; w = nk_iceilf(width); h = nk_iceilf(nk_null_rect.h); x = nk_ifloorf(in->mouse.pos.x + 1) - (int)win->layout->clip.x; y = nk_ifloorf(in->mouse.pos.y + 1) - (int)win->layout->clip.y; #if 1 //< @r-lyeh avoid tooltip truncation against app borders if( (x+(int)win->layout->clip.x+w) > window_width()/*(win->layout->clip.x+win->layout->clip.w)*/ ) x-=w+1; // if( (y+(int)win->layout->clip.y+h) > (win->layout->clip.y+win->layout->clip.h) ) y-=h+1; #endif bounds.x = (float)x; bounds.y = (float)y; bounds.w = (float)w; bounds.h = (float)h; ret = nk_popup_begin(ctx, NK_POPUP_DYNAMIC, "__##Tooltip##__", NK_WINDOW_NO_SCROLLBAR|NK_WINDOW_BORDER, bounds); if (ret) win->layout->flags &= ~(nk_flags)NK_WINDOW_ROM; win->popup.type = NK_PANEL_TOOLTIP; ctx->current->layout->type = NK_PANEL_TOOLTIP; return ret; } NK_API void nk_tooltip_end(struct nk_context *ctx) { NK_ASSERT(ctx); NK_ASSERT(ctx->current); if (!ctx || !ctx->current) return; ctx->current->seq--; nk_popup_close(ctx); nk_popup_end(ctx); } NK_API void nk_tooltip(struct nk_context *ctx, const char *text) { const struct nk_style *style; struct nk_vec2 padding; int text_len; float text_width; float text_height; NK_ASSERT(ctx); NK_ASSERT(ctx->current); NK_ASSERT(ctx->current->layout); NK_ASSERT(text); if (!ctx || !ctx->current || !ctx->current->layout || !text) return; /* fetch configuration data */ style = &ctx->style; padding = style->window.padding; padding.x += 10; //< @r-lyeh /* calculate size of the text and tooltip */ text_len = nk_strlen(text); text_width = style->font->width(style->font->userdata, style->font->height, text, text_len); text_width += (4 * padding.x); text_height = (style->font->height + 2 * padding.y); /* execute tooltip and fill with text */ if (nk_tooltip_begin(ctx, (float)text_width)) { nk_layout_row_dynamic(ctx, (float)text_height, 1); nk_text(ctx, text, text_len, NK_TEXT_CENTERED); //< @r-lyeh LEFT->CENTERED nk_tooltip_end(ctx); } } #ifdef NK_INCLUDE_STANDARD_VARARGS NK_API void nk_tooltipf(struct nk_context *ctx, const char *fmt, ...) { va_list args; va_start(args, fmt); nk_tooltipfv(ctx, fmt, args); va_end(args); } NK_API void nk_tooltipfv(struct nk_context *ctx, const char *fmt, va_list args) { char buf[256]; nk_strfmt(buf, NK_LEN(buf), fmt, args); nk_tooltip(ctx, buf); } #endif #endif /* NK_IMPLEMENTATION */ /* /// ## License /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~none /// ------------------------------------------------------------------------------ /// This software is available under 2 licenses -- choose whichever you prefer. /// ------------------------------------------------------------------------------ /// ALTERNATIVE A - MIT License /// Copyright (c) 2016-2018 Micha Mettke /// 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. /// ------------------------------------------------------------------------------ /// ALTERNATIVE B - Public Domain (www.unlicense.org) /// This is free and unencumbered software released into the public domain. /// Anyone is free to copy, modify, publish, use, compile, sell, or distribute this /// software, either in source code form or as a compiled binary, for any purpose, /// commercial or non-commercial, and by any means. /// In jurisdictions that recognize copyright laws, the author or authors of this /// software dedicate any and all copyright interest in the software to the public /// domain. We make this dedication for the benefit of the public at large and to /// the detriment of our heirs and successors. We intend this dedication to be an /// overt act of relinquishment in perpetuity of all present and future rights to /// this software under copyright law. /// 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 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. /// ------------------------------------------------------------------------------ /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /// ## Changelog /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~none /// [date] ([x.y.z]) - [description] /// - [date]: date on which the change has been pushed /// - [x.y.z]: Version string, represented in Semantic Versioning format /// - [x]: Major version with API and library breaking changes /// - [y]: Minor version with non-breaking API and library changes /// - [z]: Patch version with no direct changes to the API /// /// - 2022/02/03 (4.9.6) - Allow overriding the NK_INV_SQRT function, similar to NK_SIN and NK_COS /// - 2021/12/22 (4.9.5) - Revert layout bounds not accounting for padding due to regressions /// - 2021/12/22 (4.9.4) - Fix checking hovering when window is minimized /// - 2021/12/22 (4.09.3) - Fix layout bounds not accounting for padding /// - 2021/12/19 (4.09.2) - Update to stb_rect_pack.h v1.01 and stb_truetype.h v1.26 /// - 2021/12/16 (4.09.1) - Fix the majority of GCC warnings /// - 2021/10/16 (4.09.0) - Added nk_spacer() widget /// - 2021/09/22 (4.08.6) - Fix "may be used uninitialized" warnings in nk_widget /// - 2021/09/22 (4.08.5) - GCC __builtin_offsetof only exists in version 4 and later /// - 2021/09/15 (4.08.4) - Fix "'num_len' may be used uninitialized" in nk_do_property /// - 2021/09/15 (4.08.3) - Fix "Templates cannot be declared to have 'C' Linkage" /// - 2021/09/08 (4.08.2) - Fix warnings in C89 builds /// - 2021/09/08 (4.08.1) - Use compiler builtins for NK_OFFSETOF when possible /// - 2021/08/17 (4.08.0) - Implemented 9-slice scaling support for widget styles /// - 2021/08/16 (4.07.5) - Replace usage of memset in nk_font_atlas_bake with NK_MEMSET /// - 2021/08/15 (4.07.4) - Fix conversion and sign conversion warnings /// - 2021/08/08 (4.07.3) - Fix crash when baking merged fonts /// - 2021/08/08 (4.07.2) - Fix Multiline Edit wrong offset /// - 2021/03/17 (4.07.1) - Fix warning about unused parameter /// - 2021/03/17 (4.07.0) - Fix nk_property hover bug /// - 2021/03/15 (4.06.4) - Change nk_propertyi back to int /// - 2021/03/15 (4.06.3) - Update documentation for functions that now return nk_bool /// - 2020/12/19 (4.06.2) - Fix additional C++ style comments which are not allowed in ISO C90. /// - 2020/10/11 (4.06.1) - Fix C++ style comments which are not allowed in ISO C90. /// - 2020/10/07 (4.06.0) - Fix nk_combo return type wrongly changed to nk_bool /// - 2020/09/05 (4.05.0) - Use the nk_font_atlas allocator for stb_truetype memory management. /// - 2020/09/04 (4.04.1) - Replace every boolean int by nk_bool /// - 2020/09/04 (4.04.0) - Add nk_bool with NK_INCLUDE_STANDARD_BOOL /// - 2020/06/13 (4.03.1) - Fix nk_pool allocation sizes. /// - 2020/06/04 (4.03.0) - Made nk_combo header symbols optional. /// - 2020/05/27 (4.02.5) - Fix nk_do_edit: Keep scroll position when re-activating edit widget. /// - 2020/05/09 (4.02.4) - Fix nk_menubar height calculation bug /// - 2020/05/08 (4.02.3) - Fix missing stdarg.h with NK_INCLUDE_STANDARD_VARARGS /// - 2020/04/30 (4.02.2) - Fix nk_edit border drawing bug /// - 2020/04/09 (4.02.1) - Removed unused nk_sqrt function to fix compiler warnings /// - Fixed compiler warnings if you bring your own methods for /// nk_cos/nk_sin/nk_strtod/nk_memset/nk_memcopy/nk_dtoa /// - 2020/04/06 (4.01.10) - Fix bug: Do not use pool before checking for NULL /// - 2020/03/22 (4.01.9) - Fix bug where layout state wasn't restored correctly after /// popping a tree. /// - 2020/03/11 (4.01.8) - Fix bug where padding is subtracted from widget /// - 2020/03/06 (4.01.7) - Fix bug where width padding was applied twice /// - 2020/02/06 (4.01.6) - Update stb_truetype.h and stb_rect_pack.h and separate them /// - 2019/12/10 (4.01.5) - Fix off-by-one error in NK_INTERSECT /// - 2019/10/09 (4.01.4) - Fix bug for autoscrolling in nk_do_edit /// - 2019/09/20 (4.01.3) - Fixed a bug wherein combobox cannot be closed by clicking the header /// when NK_BUTTON_TRIGGER_ON_RELEASE is defined. /// - 2019/09/10 (4.01.2) - Fixed the nk_cos function, which deviated significantly. /// - 2019/09/08 (4.01.1) - Fixed a bug wherein re-baking of fonts caused a segmentation /// fault due to dst_font->glyph_count not being zeroed on subsequent /// bakes of the same set of fonts. /// - 2019/06/23 (4.01.0) - Added nk_***_get_scroll and nk_***_set_scroll for groups, windows, and popups. /// - 2019/06/12 (4.00.3) - Fix panel background drawing bug. /// - 2018/10/31 (4.00.2) - Added NK_KEYSTATE_BASED_INPUT to "fix" state based backends /// like GLFW without breaking key repeat behavior on event based. /// - 2018/04/01 (4.00.1) - Fixed calling `nk_convert` multiple time per single frame. /// - 2018/04/01 (4.00.0) - BREAKING CHANGE: nk_draw_list_clear no longer tries to /// clear provided buffers. So make sure to either free /// or clear each passed buffer after calling nk_convert. /// - 2018/02/23 (3.00.6) - Fixed slider dragging behavior. /// - 2018/01/31 (3.00.5) - Fixed overcalculation of cursor data in font baking process. /// - 2018/01/31 (3.00.4) - Removed name collision with stb_truetype. /// - 2018/01/28 (3.00.3) - Fixed panel window border drawing bug. /// - 2018/01/12 (3.00.2) - Added `nk_group_begin_titled` for separated group identifier and title. /// - 2018/01/07 (3.00.1) - Started to change documentation style. /// - 2018/01/05 (3.00.0) - BREAKING CHANGE: The previous color picker API was broken /// because of conversions between float and byte color representation. /// Color pickers now use floating point values to represent /// HSV values. To get back the old behavior I added some additional /// color conversion functions to cast between nk_color and /// nk_colorf. /// - 2017/12/23 (2.00.7) - Fixed small warning. /// - 2017/12/23 (2.00.7) - Fixed `nk_edit_buffer` behavior if activated to allow input. /// - 2017/12/23 (2.00.7) - Fixed modifyable progressbar dragging visuals and input behavior. /// - 2017/12/04 (2.00.6) - Added formatted string tooltip widget. /// - 2017/11/18 (2.00.5) - Fixed window becoming hidden with flag `NK_WINDOW_NO_INPUT`. /// - 2017/11/15 (2.00.4) - Fixed font merging. /// - 2017/11/07 (2.00.3) - Fixed window size and position modifier functions. /// - 2017/09/14 (2.00.2) - Fixed `nk_edit_buffer` and `nk_edit_focus` behavior. /// - 2017/09/14 (2.00.1) - Fixed window closing behavior. /// - 2017/09/14 (2.00.0) - BREAKING CHANGE: Modifying window position and size functions now /// require the name of the window and must happen outside the window /// building process (between function call nk_begin and nk_end). /// - 2017/09/11 (1.40.9) - Fixed window background flag if background window is declared last. /// - 2017/08/27 (1.40.8) - Fixed `nk_item_is_any_active` for hidden windows. /// - 2017/08/27 (1.40.7) - Fixed window background flag. /// - 2017/07/07 (1.40.6) - Fixed missing clipping rect check for hovering/clicked /// query for widgets. /// - 2017/07/07 (1.40.5) - Fixed drawing bug for vertex output for lines and stroked /// and filled rectangles. /// - 2017/07/07 (1.40.4) - Fixed bug in nk_convert trying to add windows that are in /// process of being destroyed. /// - 2017/07/07 (1.40.3) - Fixed table internal bug caused by storing table size in /// window instead of directly in table. /// - 2017/06/30 (1.40.2) - Removed unneeded semicolon in C++ NK_ALIGNOF macro. /// - 2017/06/30 (1.40.1) - Fixed drawing lines smaller or equal zero. /// - 2017/06/08 (1.40.0) - Removed the breaking part of last commit. Auto layout now only /// comes in effect if you pass in zero was row height argument. /// - 2017/06/08 (1.40.0) - BREAKING CHANGE: while not directly API breaking it will change /// how layouting works. From now there will be an internal minimum /// row height derived from font height. If you need a row smaller than /// that you can directly set it by `nk_layout_set_min_row_height` and /// reset the value back by calling `nk_layout_reset_min_row_height. /// - 2017/06/08 (1.39.1) - Fixed property text edit handling bug caused by past `nk_widget` fix. /// - 2017/06/08 (1.39.0) - Added function to retrieve window space without calling a `nk_layout_xxx` function. /// - 2017/06/06 (1.38.5) - Fixed `nk_convert` return flag for command buffer. /// - 2017/05/23 (1.38.4) - Fixed activation behavior for widgets partially clipped. /// - 2017/05/10 (1.38.3) - Fixed wrong min window size mouse scaling over boundaries. /// - 2017/05/09 (1.38.2) - Fixed vertical scrollbar drawing with not enough space. /// - 2017/05/09 (1.38.1) - Fixed scaler dragging behavior if window size hits minimum size. /// - 2017/05/06 (1.38.0) - Added platform double-click support. /// - 2017/04/20 (1.37.1) - Fixed key repeat found inside glfw demo backends. /// - 2017/04/20 (1.37.0) - Extended properties with selection and clipboard support. /// - 2017/04/20 (1.36.2) - Fixed #405 overlapping rows with zero padding and spacing. /// - 2017/04/09 (1.36.1) - Fixed #403 with another widget float error. /// - 2017/04/09 (1.36.0) - Added window `NK_WINDOW_NO_INPUT` and `NK_WINDOW_NOT_INTERACTIVE` flags. /// - 2017/04/09 (1.35.3) - Fixed buffer heap corruption. /// - 2017/03/25 (1.35.2) - Fixed popup overlapping for `NK_WINDOW_BACKGROUND` windows. /// - 2017/03/25 (1.35.1) - Fixed windows closing behavior. /// - 2017/03/18 (1.35.0) - Added horizontal scroll requested in #377. /// - 2017/03/18 (1.34.3) - Fixed long window header titles. /// - 2017/03/04 (1.34.2) - Fixed text edit filtering. /// - 2017/03/04 (1.34.1) - Fixed group closable flag. /// - 2017/02/25 (1.34.0) - Added custom draw command for better language binding support. /// - 2017/01/24 (1.33.0) - Added programmatic way to remove edit focus. /// - 2017/01/24 (1.32.3) - Fixed wrong define for basic type definitions for windows. /// - 2017/01/21 (1.32.2) - Fixed input capture from hidden or closed windows. /// - 2017/01/21 (1.32.1) - Fixed slider behavior and drawing. /// - 2017/01/13 (1.32.0) - Added flag to put scaler into the bottom left corner. /// - 2017/01/13 (1.31.0) - Added additional row layouting method to combine both /// dynamic and static widgets. /// - 2016/12/31 (1.30.0) - Extended scrollbar offset from 16-bit to 32-bit. /// - 2016/12/31 (1.29.2) - Fixed closing window bug of minimized windows. /// - 2016/12/03 (1.29.1) - Fixed wrapped text with no seperator and C89 error. /// - 2016/12/03 (1.29.0) - Changed text wrapping to process words not characters. /// - 2016/11/22 (1.28.6) - Fixed window minimized closing bug. /// - 2016/11/19 (1.28.5) - Fixed abstract combo box closing behavior. /// - 2016/11/19 (1.28.4) - Fixed tooltip flickering. /// - 2016/11/19 (1.28.3) - Fixed memory leak caused by popup repeated closing. /// - 2016/11/18 (1.28.2) - Fixed memory leak caused by popup panel allocation. /// - 2016/11/10 (1.28.1) - Fixed some warnings and C++ error. /// - 2016/11/10 (1.28.0) - Added additional `nk_button` versions which allows to directly /// pass in a style struct to change buttons visual. /// - 2016/11/10 (1.27.0) - Added additional `nk_tree` versions to support external state /// storage. Just like last the `nk_group` commit the main /// advantage is that you optionally can minimize nuklears runtime /// memory consumption or handle hash collisions. /// - 2016/11/09 (1.26.0) - Added additional `nk_group` version to support external scrollbar /// offset storage. Main advantage is that you can externalize /// the memory management for the offset. It could also be helpful /// if you have a hash collision in `nk_group_begin` but really /// want the name. In addition I added `nk_list_view` which allows /// to draw big lists inside a group without actually having to /// commit the whole list to nuklear (issue #269). /// - 2016/10/30 (1.25.1) - Fixed clipping rectangle bug inside `nk_draw_list`. /// - 2016/10/29 (1.25.0) - Pulled `nk_panel` memory management into nuklear and out of /// the hands of the user. From now on users don't have to care /// about panels unless they care about some information. If you /// still need the panel just call `nk_window_get_panel`. /// - 2016/10/21 (1.24.0) - Changed widget border drawing to stroked rectangle from filled /// rectangle for less overdraw and widget background transparency. /// - 2016/10/18 (1.23.0) - Added `nk_edit_focus` for manually edit widget focus control. /// - 2016/09/29 (1.22.7) - Fixed deduction of basic type in non `` compilation. /// - 2016/09/29 (1.22.6) - Fixed edit widget UTF-8 text cursor drawing bug. /// - 2016/09/28 (1.22.5) - Fixed edit widget UTF-8 text appending/inserting/removing. /// - 2016/09/28 (1.22.4) - Fixed drawing bug inside edit widgets which offset all text /// text in every edit widget if one of them is scrolled. /// - 2016/09/28 (1.22.3) - Fixed small bug in edit widgets if not active. The wrong /// text length is passed. It should have been in bytes but /// was passed as glyphs. /// - 2016/09/20 (1.22.2) - Fixed color button size calculation. /// - 2016/09/20 (1.22.1) - Fixed some `nk_vsnprintf` behavior bugs and removed `` /// again from `NK_INCLUDE_STANDARD_VARARGS`. /// - 2016/09/18 (1.22.0) - C89 does not support vsnprintf only C99 and newer as well /// as C++11 and newer. In addition to use vsnprintf you have /// to include . So just defining `NK_INCLUDE_STD_VAR_ARGS` /// is not enough. That behavior is now fixed. By default if /// both varargs as well as stdio is selected I try to use /// vsnprintf if not possible I will revert to vsprintf. If /// varargs but not stdio was defined I will use my own function. /// - 2016/09/15 (1.21.2) - Fixed panel `close` behavior for deeper panel levels. /// - 2016/09/15 (1.21.1) - Fixed C++ errors and wrong argument to `nk_panel_get_xxxx`. /// - 2016/09/13 (1.21.0) - !BREAKING! Fixed nonblocking popup behavior in menu, combo, /// and contextual which prevented closing in y-direction if /// popup did not reach max height. /// In addition the height parameter was changed into vec2 /// for width and height to have more control over the popup size. /// - 2016/09/13 (1.20.3) - Cleaned up and extended type selection. /// - 2016/09/13 (1.20.2) - Fixed slider behavior hopefully for the last time. This time /// all calculation are correct so no more hackery. /// - 2016/09/13 (1.20.1) - Internal change to divide window/panel flags into panel flags and types. /// Suprisinly spend years in C and still happened to confuse types /// with flags. Probably something to take note. /// - 2016/09/08 (1.20.0) - Added additional helper function to make it easier to just /// take the produced buffers from `nk_convert` and unplug the /// iteration process from `nk_context`. So now you can /// just use the vertex,element and command buffer + two pointer /// inside the command buffer retrieved by calls `nk__draw_begin` /// and `nk__draw_end` and macro `nk_draw_foreach_bounded`. /// - 2016/09/08 (1.19.0) - Added additional asserts to make sure every `nk_xxx_begin` call /// for windows, popups, combobox, menu and contextual is guarded by /// `if` condition and does not produce false drawing output. /// - 2016/09/08 (1.18.0) - Changed confusing name for `NK_SYMBOL_RECT_FILLED`, `NK_SYMBOL_RECT` /// to hopefully easier to understand `NK_SYMBOL_RECT_FILLED` and /// `NK_SYMBOL_RECT_OUTLINE`. /// - 2016/09/08 (1.17.0) - Changed confusing name for `NK_SYMBOL_CIRLCE_FILLED`, `NK_SYMBOL_CIRCLE` /// to hopefully easier to understand `NK_SYMBOL_CIRCLE_FILLED` and /// `NK_SYMBOL_CIRCLE_OUTLINE`. /// - 2016/09/08 (1.16.0) - Added additional checks to select correct types if `NK_INCLUDE_FIXED_TYPES` /// is not defined by supporting the biggest compiler GCC, clang and MSVC. /// - 2016/09/07 (1.15.3) - Fixed `NK_INCLUDE_COMMAND_USERDATA` define to not cause an error. /// - 2016/09/04 (1.15.2) - Fixed wrong combobox height calculation. /// - 2016/09/03 (1.15.1) - Fixed gaps inside combo boxes in OpenGL. /// - 2016/09/02 (1.15.0) - Changed nuklear to not have any default vertex layout and /// instead made it user provided. The range of types to convert /// to is quite limited at the moment, but I would be more than /// happy to accept PRs to add additional. /// - 2016/08/30 (1.14.2) - Removed unused variables. /// - 2016/08/30 (1.14.1) - Fixed C++ build errors. /// - 2016/08/30 (1.14.0) - Removed mouse dragging from SDL demo since it does not work correctly. /// - 2016/08/30 (1.13.4) - Tweaked some default styling variables. /// - 2016/08/30 (1.13.3) - Hopefully fixed drawing bug in slider, in general I would /// refrain from using slider with a big number of steps. /// - 2016/08/30 (1.13.2) - Fixed close and minimize button which would fire even if the /// window was in Read Only Mode. /// - 2016/08/30 (1.13.1) - Fixed popup panel padding handling which was previously just /// a hack for combo box and menu. /// - 2016/08/30 (1.13.0) - Removed `NK_WINDOW_DYNAMIC` flag from public API since /// it is bugged and causes issues in window selection. /// - 2016/08/30 (1.12.0) - Removed scaler size. The size of the scaler is now /// determined by the scrollbar size. /// - 2016/08/30 (1.11.2) - Fixed some drawing bugs caused by changes from 1.11.0. /// - 2016/08/30 (1.11.1) - Fixed overlapping minimized window selection. /// - 2016/08/30 (1.11.0) - Removed some internal complexity and overly complex code /// handling panel padding and panel border. /// - 2016/08/29 (1.10.0) - Added additional height parameter to `nk_combobox_xxx`. /// - 2016/08/29 (1.10.0) - Fixed drawing bug in dynamic popups. /// - 2016/08/29 (1.10.0) - Added experimental mouse scrolling to popups, menus and comboboxes. /// - 2016/08/26 (1.10.0) - Added window name string prepresentation to account for /// hash collisions. Currently limited to `NK_WINDOW_MAX_NAME` /// which in term can be redefined if not big enough. /// - 2016/08/26 (1.10.0) - Added stacks for temporary style/UI changes in code. /// - 2016/08/25 (1.10.0) - Changed `nk_input_is_key_pressed` and 'nk_input_is_key_released' /// to account for key press and release happening in one frame. /// - 2016/08/25 (1.10.0) - Added additional nk_edit flag to directly jump to the end on activate. /// - 2016/08/17 (1.09.6) - Removed invalid check for value zero in `nk_propertyx`. /// - 2016/08/16 (1.09.5) - Fixed ROM mode for deeper levels of popup windows parents. /// - 2016/08/15 (1.09.4) - Editbox are now still active if enter was pressed with flag /// `NK_EDIT_SIG_ENTER`. Main reasoning is to be able to keep /// typing after committing. /// - 2016/08/15 (1.09.4) - Removed redundant code. /// - 2016/08/15 (1.09.4) - Fixed negative numbers in `nk_strtoi` and remove unused variable. /// - 2016/08/15 (1.09.3) - Fixed `NK_WINDOW_BACKGROUND` flag behavior to select a background /// window only as selected by hovering and not by clicking. /// - 2016/08/14 (1.09.2) - Fixed a bug in font atlas which caused wrong loading /// of glyphs for font with multiple ranges. /// - 2016/08/12 (1.09.1) - Added additional function to check if window is currently /// hidden and therefore not visible. /// - 2016/08/12 (1.09.1) - nk_window_is_closed now queries the correct flag `NK_WINDOW_CLOSED` /// instead of the old flag `NK_WINDOW_HIDDEN`. /// - 2016/08/09 (1.09.0) - Added additional double version to nk_property and changed /// the underlying implementation to not cast to float and instead /// work directly on the given values. /// - 2016/08/09 (1.08.0) - Added additional define to overwrite library internal /// floating pointer number to string conversion for additional /// precision. /// - 2016/08/09 (1.08.0) - Added additional define to overwrite library internal /// string to floating point number conversion for additional /// precision. /// - 2016/08/08 (1.07.2) - Fixed compiling error without define `NK_INCLUDE_FIXED_TYPE`. /// - 2016/08/08 (1.07.1) - Fixed possible floating point error inside `nk_widget` leading /// to wrong wiget width calculation which results in widgets falsely /// becoming tagged as not inside window and cannot be accessed. /// - 2016/08/08 (1.07.0) - Nuklear now differentiates between hiding a window (NK_WINDOW_HIDDEN) and /// closing a window (NK_WINDOW_CLOSED). A window can be hidden/shown /// by using `nk_window_show` and closed by either clicking the close /// icon in a window or by calling `nk_window_close`. Only closed /// windows get removed at the end of the frame while hidden windows /// remain. /// - 2016/08/08 (1.06.0) - Added `nk_edit_string_zero_terminated` as a second option to /// `nk_edit_string` which takes, edits and outputs a '\0' terminated string. /// - 2016/08/08 (1.05.4) - Fixed scrollbar auto hiding behavior. /// - 2016/08/08 (1.05.3) - Fixed wrong panel padding selection in `nk_layout_widget_space`. /// - 2016/08/07 (1.05.2) - Fixed old bug in dynamic immediate mode layout API, calculating /// wrong item spacing and panel width. /// - 2016/08/07 (1.05.1) - Hopefully finally fixed combobox popup drawing bug. /// - 2016/08/07 (1.05.0) - Split varargs away from `NK_INCLUDE_STANDARD_IO` into own /// define `NK_INCLUDE_STANDARD_VARARGS` to allow more fine /// grained controlled over library includes. /// - 2016/08/06 (1.04.5) - Changed memset calls to `NK_MEMSET`. /// - 2016/08/04 (1.04.4) - Fixed fast window scaling behavior. /// - 2016/08/04 (1.04.3) - Fixed window scaling, movement bug which appears if you /// move/scale a window and another window is behind it. /// If you are fast enough then the window behind gets activated /// and the operation is blocked. I now require activating /// by hovering only if mouse is not pressed. /// - 2016/08/04 (1.04.2) - Fixed changing fonts. /// - 2016/08/03 (1.04.1) - Fixed `NK_WINDOW_BACKGROUND` behavior. /// - 2016/08/03 (1.04.0) - Added color parameter to `nk_draw_image`. /// - 2016/08/03 (1.04.0) - Added additional window padding style attributes for /// sub windows (combo, menu, ...). /// - 2016/08/03 (1.04.0) - Added functions to show/hide software cursor. /// - 2016/08/03 (1.04.0) - Added `NK_WINDOW_BACKGROUND` flag to force a window /// to be always in the background of the screen. /// - 2016/08/03 (1.03.2) - Removed invalid assert macro for NK_RGB color picker. /// - 2016/08/01 (1.03.1) - Added helper macros into header include guard. /// - 2016/07/29 (1.03.0) - Moved the window/table pool into the header part to /// simplify memory management by removing the need to /// allocate the pool. /// - 2016/07/29 (1.02.0) - Added auto scrollbar hiding window flag which if enabled /// will hide the window scrollbar after NK_SCROLLBAR_HIDING_TIMEOUT /// seconds without window interaction. To make it work /// you have to also set a delta time inside the `nk_context`. /// - 2016/07/25 (1.01.1) - Fixed small panel and panel border drawing bugs. /// - 2016/07/15 (1.01.0) - Added software cursor to `nk_style` and `nk_context`. /// - 2016/07/15 (1.01.0) - Added const correctness to `nk_buffer_push' data argument. /// - 2016/07/15 (1.01.0) - Removed internal font baking API and simplified /// font atlas memory management by converting pointer /// arrays for fonts and font configurations to lists. /// - 2016/07/15 (1.00.0) - Changed button API to use context dependent button /// behavior instead of passing it for every function call. /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /// ## Gallery /// ![Figure [blue]: Feature overview with blue color styling](https://cloud.githubusercontent.com/assets/8057201/13538240/acd96876-e249-11e5-9547-5ac0b19667a0.png) /// ![Figure [red]: Feature overview with red color styling](https://cloud.githubusercontent.com/assets/8057201/13538243/b04acd4c-e249-11e5-8fd2-ad7744a5b446.png) /// ![Figure [widgets]: Widget overview](https://cloud.githubusercontent.com/assets/8057201/11282359/3325e3c6-8eff-11e5-86cb-cf02b0596087.png) /// ![Figure [blackwhite]: Black and white](https://cloud.githubusercontent.com/assets/8057201/11033668/59ab5d04-86e5-11e5-8091-c56f16411565.png) /// ![Figure [filexp]: File explorer](https://cloud.githubusercontent.com/assets/8057201/10718115/02a9ba08-7b6b-11e5-950f-adacdd637739.png) /// ![Figure [opengl]: OpenGL Editor](https://cloud.githubusercontent.com/assets/8057201/12779619/2a20d72c-ca69-11e5-95fe-4edecf820d5c.png) /// ![Figure [nodedit]: Node Editor](https://cloud.githubusercontent.com/assets/8057201/9976995/e81ac04a-5ef7-11e5-872b-acd54fbeee03.gif) /// ![Figure [skinning]: Using skinning in Nuklear](https://cloud.githubusercontent.com/assets/8057201/15991632/76494854-30b8-11e6-9555-a69840d0d50b.png) /// ![Figure [bf]: Heavy modified version](https://cloud.githubusercontent.com/assets/8057201/14902576/339926a8-0d9c-11e6-9fee-a8b73af04473.png) /// /// ## Credits /// Developed by Micha Mettke and every direct or indirect github contributor.

/// /// Embeds [stb_texedit](https://github.com/nothings/stb/blob/master/stb_textedit.h), [stb_truetype](https://github.com/nothings/stb/blob/master/stb_truetype.h) and [stb_rectpack](https://github.com/nothings/stb/blob/master/stb_rect_pack.h) by Sean Barret (public domain)
/// Uses [stddoc.c](https://github.com/r-lyeh/stddoc.c) from r-lyeh@github.com for documentation generation

/// Embeds ProggyClean.ttf font by Tristan Grimmer (MIT license).
/// /// Big thank you to Omar Cornut (ocornut@github) for his [imgui library](https://github.com/ocornut/imgui) and /// giving me the inspiration for this library, Casey Muratori for handmade hero /// and his original immediate mode graphical user interface idea and Sean /// Barret for his amazing single header libraries which restored my faith /// in libraries and brought me to create some of my own. Finally Apoorva Joshi /// for his single header file packer. */ #line 0 #line 1 "3rd_nuklear_glfw_gl3.h" /* * Nuklear - 1.32.0 - public domain * no warrenty implied; use at your own risk. * authored from 2015-2016 by Micha Mettke */ /* * ============================================================== * * API * * =============================================================== */ #ifndef NK_GLFW_GL3_H_ #define NK_GLFW_GL3_H_ //#include enum nk_glfw_init_state{ NK_GLFW3_DEFAULT=0, NK_GLFW3_INSTALL_CALLBACKS }; #ifndef NK_GLFW_TEXT_MAX #define NK_GLFW_TEXT_MAX 256 #endif struct nk_glfw_device { struct nk_buffer cmds; struct nk_draw_null_texture null; GLuint vbo, vao, ebo; GLuint prog; GLuint vert_shdr; GLuint frag_shdr; GLint attrib_pos; GLint attrib_uv; GLint attrib_col; GLint uniform_tex; GLint uniform_proj; GLuint font_tex; }; struct nk_glfw { GLFWwindow *win; int width, height; int display_width, display_height; struct nk_glfw_device ogl; struct nk_context ctx; struct nk_font_atlas atlas; struct nk_vec2 fb_scale; unsigned int text[NK_GLFW_TEXT_MAX]; int text_len; struct nk_vec2 scroll, scroll_bak; //< @r-lyeh, added scroll_bak double last_button_click; int is_double_click_down; struct nk_vec2 double_click_pos; }; NK_API struct nk_context* nk_glfw3_init(struct nk_glfw* glfw, GLFWwindow *win, enum nk_glfw_init_state); NK_API void nk_glfw3_shutdown(struct nk_glfw* glfw); NK_API void nk_glfw3_font_stash_begin(struct nk_glfw* glfw, struct nk_font_atlas **atlas); NK_API void nk_glfw3_font_stash_end(struct nk_glfw* glfw); NK_API void nk_glfw3_new_frame(struct nk_glfw* glfw); NK_API void nk_glfw3_render(struct nk_glfw* glfw, enum nk_anti_aliasing, int max_vertex_buffer, int max_element_buffer); NK_API void nk_glfw3_device_destroy(struct nk_glfw* glfw); NK_API void nk_glfw3_device_create(struct nk_glfw* glfw); NK_API void nk_glfw3_char_callback(GLFWwindow *win, unsigned int codepoint); NK_API void nk_gflw3_scroll_callback(GLFWwindow *win, double xoff, double yoff); NK_API void nk_glfw3_mouse_button_callback(GLFWwindow *win, int button, int action, int mods); /* * ============================================================== * * IMPLEMENTATION * * =============================================================== */ #ifdef NK_GLFW_GL3_IMPLEMENTATION #ifndef NK_GLFW_DOUBLE_CLICK_LO #define NK_GLFW_DOUBLE_CLICK_LO 0.02 #endif #ifndef NK_GLFW_DOUBLE_CLICK_HI #define NK_GLFW_DOUBLE_CLICK_HI 0.2 #endif struct nk_glfw_vertex { float position[2]; float uv[2]; nk_byte col[4]; }; #ifdef __EMSCRIPTEN__ #define NK_SHADER_VERSION "#version 100\n" #else #ifdef __APPLE__ #define NK_SHADER_VERSION "#version 150\n" #else #define NK_SHADER_VERSION "#version 300 es\n" #endif #endif NK_API void nk_glfw3_device_create(struct nk_glfw* glfw) { GLint status; static const GLchar *vertex_shader = NK_SHADER_VERSION "uniform mat4 ProjMtx;\n" #ifdef __EMSCRIPTEN__ "attribute vec2 Position;\n" "attribute vec2 TexCoord;\n" "attribute vec4 Color;\n" "varying vec2 Frag_UV;\n" "varying vec4 Frag_Color;\n" #else "in vec2 Position;\n" "in vec2 TexCoord;\n" "in vec4 Color;\n" "out vec2 Frag_UV;\n" "out vec4 Frag_Color;\n" #endif "void main() {\n" " Frag_UV = TexCoord;\n" " Frag_Color = Color;\n" " gl_Position = ProjMtx * vec4(Position.xy, 0, 1);\n" "}\n"; static const GLchar *fragment_shader = NK_SHADER_VERSION "precision mediump float;\n" "uniform sampler2D Texture;\n" #ifdef __EMSCRIPTEN__ "varying vec2 Frag_UV;\n" "varying vec4 Frag_Color;\n" #else "in vec2 Frag_UV;\n" "in vec4 Frag_Color;\n" "out vec4 Out_Color;\n" #endif "void main(){\n" #ifdef __EMSCRIPTEN__ " gl_FragColor = Frag_Color * texture2D(Texture, Frag_UV);\n" #else " Out_Color = Frag_Color * texture(Texture, Frag_UV.st);\n" #endif "}\n"; struct nk_glfw_device *dev = &glfw->ogl; nk_buffer_init_default(&dev->cmds); dev->prog = glCreateProgram(); dev->vert_shdr = glCreateShader(GL_VERTEX_SHADER); dev->frag_shdr = glCreateShader(GL_FRAGMENT_SHADER); glShaderSource(dev->vert_shdr, 1, &vertex_shader, 0); glShaderSource(dev->frag_shdr, 1, &fragment_shader, 0); glCompileShader(dev->vert_shdr); glCompileShader(dev->frag_shdr); glGetShaderiv(dev->vert_shdr, GL_COMPILE_STATUS, &status); assert(status == GL_TRUE); glGetShaderiv(dev->frag_shdr, GL_COMPILE_STATUS, &status); assert(status == GL_TRUE); glAttachShader(dev->prog, dev->vert_shdr); glAttachShader(dev->prog, dev->frag_shdr); glLinkProgram(dev->prog); glGetProgramiv(dev->prog, GL_LINK_STATUS, &status); assert(status == GL_TRUE); dev->uniform_tex = glGetUniformLocation(dev->prog, "Texture"); dev->uniform_proj = glGetUniformLocation(dev->prog, "ProjMtx"); dev->attrib_pos = glGetAttribLocation(dev->prog, "Position"); dev->attrib_uv = glGetAttribLocation(dev->prog, "TexCoord"); dev->attrib_col = glGetAttribLocation(dev->prog, "Color"); { /* buffer setup */ GLsizei vs = sizeof(struct nk_glfw_vertex); size_t vp = offsetof(struct nk_glfw_vertex, position); size_t vt = offsetof(struct nk_glfw_vertex, uv); size_t vc = offsetof(struct nk_glfw_vertex, col); glGenBuffers(1, &dev->vbo); glGenBuffers(1, &dev->ebo); glGenVertexArrays(1, &dev->vao); glBindVertexArray(dev->vao); glBindBuffer(GL_ARRAY_BUFFER, dev->vbo); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, dev->ebo); glEnableVertexAttribArray((GLuint)dev->attrib_pos); glEnableVertexAttribArray((GLuint)dev->attrib_uv); glEnableVertexAttribArray((GLuint)dev->attrib_col); glVertexAttribPointer((GLuint)dev->attrib_pos, 2, GL_FLOAT, GL_FALSE, vs, (void*)vp); glVertexAttribPointer((GLuint)dev->attrib_uv, 2, GL_FLOAT, GL_FALSE, vs, (void*)vt); glVertexAttribPointer((GLuint)dev->attrib_col, 4, GL_UNSIGNED_BYTE, GL_TRUE, vs, (void*)vc); } glBindTexture(GL_TEXTURE_2D, 0); glBindBuffer(GL_ARRAY_BUFFER, 0); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); #ifndef __EMSCRIPTEN__ glBindVertexArray(0); #endif } NK_INTERN void nk_glfw3_device_upload_atlas(struct nk_glfw* glfw, const void *image, int width, int height) { struct nk_glfw_device *dev = &glfw->ogl; glGenTextures(1, &dev->font_tex); glBindTexture(GL_TEXTURE_2D, dev->font_tex); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (GLsizei)width, (GLsizei)height, 0, GL_RGBA, GL_UNSIGNED_BYTE, image); } NK_API void nk_glfw3_device_destroy(struct nk_glfw* glfw) { struct nk_glfw_device *dev = &glfw->ogl; glDetachShader(dev->prog, dev->vert_shdr); glDetachShader(dev->prog, dev->frag_shdr); glDeleteShader(dev->vert_shdr); glDeleteShader(dev->frag_shdr); glDeleteProgram(dev->prog); glDeleteTextures(1, &dev->font_tex); glDeleteBuffers(1, &dev->vbo); glDeleteBuffers(1, &dev->ebo); nk_buffer_free(&dev->cmds); } NK_API void nk_glfw3_render(struct nk_glfw* glfw, enum nk_anti_aliasing AA, int max_vertex_buffer, int max_element_buffer) { struct nk_glfw_device *dev = &glfw->ogl; GLfloat ortho[4][4] = { {2.0f, 0.0f, 0.0f, 0.0f}, {0.0f,-2.0f, 0.0f, 0.0f}, {0.0f, 0.0f,-1.0f, 0.0f}, {-1.0f,1.0f, 0.0f, 1.0f}, }; ortho[0][0] /= (GLfloat)glfw->width; ortho[1][1] /= (GLfloat)glfw->height; /* setup global state */ glEnable(GL_BLEND); glBlendEquation(GL_FUNC_ADD); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glDisable(GL_CULL_FACE); glDisable(GL_DEPTH_TEST); glEnable(GL_SCISSOR_TEST); glActiveTexture(GL_TEXTURE0); /* setup program */ glUseProgram(dev->prog); glUniform1i(dev->uniform_tex, 0); glUniformMatrix4fv(dev->uniform_proj, 1, GL_FALSE, &ortho[0][0]); glViewport(0,0,(GLsizei)glfw->display_width,(GLsizei)glfw->display_height); { /* convert from command queue into draw list and draw to screen */ const struct nk_draw_command *cmd = NULL; void *vertices, *elements; const nk_draw_index *offset = NULL; /* allocate vertex and element buffer */ glBindVertexArray(dev->vao); glBindBuffer(GL_ARRAY_BUFFER, dev->vbo); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, dev->ebo); glBufferData(GL_ARRAY_BUFFER, max_vertex_buffer, NULL, GL_STREAM_DRAW); glBufferData(GL_ELEMENT_ARRAY_BUFFER, max_element_buffer, NULL, GL_STREAM_DRAW); /* load draw vertices & elements directly into vertex + element buffer */ #ifndef __EMSCRIPTEN__ vertices = glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY); elements = glMapBuffer(GL_ELEMENT_ARRAY_BUFFER, GL_WRITE_ONLY); #else vertices = malloc((size_t)max_vertex_buffer); elements = malloc((size_t)max_element_buffer); #endif { /* fill convert configuration */ struct nk_convert_config config; static const struct nk_draw_vertex_layout_element vertex_layout[] = { {NK_VERTEX_POSITION, NK_FORMAT_FLOAT, NK_OFFSETOF(struct nk_glfw_vertex, position)}, {NK_VERTEX_TEXCOORD, NK_FORMAT_FLOAT, NK_OFFSETOF(struct nk_glfw_vertex, uv)}, {NK_VERTEX_COLOR, NK_FORMAT_R8G8B8A8, NK_OFFSETOF(struct nk_glfw_vertex, col)}, {NK_VERTEX_LAYOUT_END} }; NK_MEMSET(&config, 0, sizeof(config)); config.vertex_layout = vertex_layout; config.vertex_size = sizeof(struct nk_glfw_vertex); config.vertex_alignment = NK_ALIGNOF(struct nk_glfw_vertex); config.null = dev->null; config.circle_segment_count = 22; config.curve_segment_count = 22; config.arc_segment_count = 22; config.global_alpha = 1.0f; config.shape_AA = AA; config.line_AA = AA; /* setup buffers to load vertices and elements */ {struct nk_buffer vbuf, ebuf; nk_buffer_init_fixed(&vbuf, vertices, (size_t)max_vertex_buffer); nk_buffer_init_fixed(&ebuf, elements, (size_t)max_element_buffer); nk_convert(&glfw->ctx, &dev->cmds, &vbuf, &ebuf, &config);} } #ifdef __EMSCRIPTEN__ glBufferSubData(GL_ARRAY_BUFFER, 0, (size_t)max_vertex_buffer, vertices); glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, (size_t)max_element_buffer, elements); free(vertices); free(elements); #else glUnmapBuffer(GL_ARRAY_BUFFER); glUnmapBuffer(GL_ELEMENT_ARRAY_BUFFER); #endif /* iterate over and execute each draw command */ nk_draw_foreach(cmd, &glfw->ctx, &dev->cmds) { if (!cmd->elem_count) continue; glBindTexture(GL_TEXTURE_2D, (GLuint)cmd->texture.id); glScissor( (GLint)(cmd->clip_rect.x * glfw->fb_scale.x), (GLint)((glfw->height - (GLint)(cmd->clip_rect.y + cmd->clip_rect.h)) * glfw->fb_scale.y), (GLint)(cmd->clip_rect.w * glfw->fb_scale.x), (GLint)(cmd->clip_rect.h * glfw->fb_scale.y)); glDrawElements(GL_TRIANGLES, (GLsizei)cmd->elem_count, GL_UNSIGNED_SHORT, offset); offset += cmd->elem_count; } nk_clear(&glfw->ctx); } /* default OpenGL state */ glUseProgram(0); glBindBuffer(GL_ARRAY_BUFFER, 0); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); #ifndef __EMSCRIPTEN__ glBindVertexArray(0); #endif glDisable(GL_BLEND); glDisable(GL_SCISSOR_TEST); } NK_API void nk_glfw3_char_callback(GLFWwindow *win, unsigned int codepoint) { if(glfwGetInputMode(win, GLFW_CURSOR) != GLFW_CURSOR_NORMAL) return; //< @r-lyeh: do not grab input when mouse is hidden (fps cam) struct nk_glfw* glfw = glfwGetWindowUserPointer(win); if (glfw->text_len < NK_GLFW_TEXT_MAX) glfw->text[glfw->text_len++] = codepoint; } NK_API void nk_gflw3_scroll_callback(GLFWwindow *win, double xoff, double yoff) { if(glfwGetInputMode(win, GLFW_CURSOR) != GLFW_CURSOR_NORMAL) return; //< @r-lyeh: do not grab input when mouse is hidden (fps cam) struct nk_glfw* glfw = glfwGetWindowUserPointer(win); glfw->scroll.x += (float)xoff; glfw->scroll.y += (float)yoff; glfw->scroll_bak.x += (float)xoff; //< @r-lyeh glfw->scroll_bak.y += (float)yoff; //< @r-lyeh } NK_API void nk_glfw3_mouse_button_callback(GLFWwindow* win, int button, int action, int mods) { if(glfwGetInputMode(win, GLFW_CURSOR) != GLFW_CURSOR_NORMAL) return; //< @r-lyeh: do not grab input when mouse is hidden (fps cam) double x, y; if (button != GLFW_MOUSE_BUTTON_LEFT) return; struct nk_glfw* glfw = glfwGetWindowUserPointer(win); glfwGetCursorPos(win, &x, &y); if (action == GLFW_PRESS) { double dt = glfwGetTime() - glfw->last_button_click; if (dt > NK_GLFW_DOUBLE_CLICK_LO && dt < NK_GLFW_DOUBLE_CLICK_HI) { glfw->is_double_click_down = nk_true; glfw->double_click_pos = nk_vec2((float)x, (float)y); } glfw->last_button_click = glfwGetTime(); } else glfw->is_double_click_down = nk_false; } NK_INTERN void nk_glfw3_clipboard_paste(nk_handle usr, struct nk_text_edit *edit) { (void)usr; struct nk_glfw* glfw = glfwGetWindowUserPointer(window_handle()); // @rlyeh < struct nk_glfw* glfw = usr.ptr; const char *text = glfwGetClipboardString(glfw->win); if (text) nk_textedit_paste(edit, text, nk_strlen(text)); } NK_INTERN void nk_glfw3_clipboard_copy(nk_handle usr, const char *text, int len) { (void)usr; struct nk_glfw* glfw = glfwGetWindowUserPointer(window_handle()); // @rlyeh < struct nk_glfw* glfw = usr.ptr; char *str = 0; if (!len) return; str = (char*)malloc((size_t)len+1); if (!str) return; memcpy(str, text, (size_t)len); str[len] = '\0'; glfwSetClipboardString(glfw->win, str); free(str); } NK_API struct nk_context* nk_glfw3_init(struct nk_glfw* glfw, GLFWwindow *win, enum nk_glfw_init_state init_state) { glfwSetWindowUserPointer(win, glfw); glfw->win = win; if (init_state == NK_GLFW3_INSTALL_CALLBACKS) { glfwSetScrollCallback(win, nk_gflw3_scroll_callback); glfwSetCharCallback(win, nk_glfw3_char_callback); glfwSetMouseButtonCallback(win, nk_glfw3_mouse_button_callback); } nk_init_default(&glfw->ctx, 0); glfw->ctx.clip.copy = nk_glfw3_clipboard_copy; glfw->ctx.clip.paste = nk_glfw3_clipboard_paste; glfw->ctx.clip.userdata = nk_handle_ptr(0); glfw->last_button_click = 0; nk_glfw3_device_create(glfw); glfw->is_double_click_down = nk_false; glfw->double_click_pos = nk_vec2(0, 0); return &glfw->ctx; } NK_API void nk_glfw3_font_stash_begin(struct nk_glfw* glfw, struct nk_font_atlas **atlas) { nk_font_atlas_init_default(&glfw->atlas); nk_font_atlas_begin(&glfw->atlas); *atlas = &glfw->atlas; } NK_API void nk_glfw3_font_stash_end(struct nk_glfw* glfw) { const void *image; int w, h; image = nk_font_atlas_bake(&glfw->atlas, &w, &h, NK_FONT_ATLAS_RGBA32); nk_glfw3_device_upload_atlas(glfw, image, w, h); nk_font_atlas_end(&glfw->atlas, nk_handle_id((int)glfw->ogl.font_tex), &glfw->ogl.null); if (glfw->atlas.default_font) nk_style_set_font(&glfw->ctx, &glfw->atlas.default_font->handle); nk_style_load_all_cursors(&glfw->ctx, glfw->atlas.cursors); //< @r-lyeh nk_style_hide_cursor(&glfw->ctx); //< @r-lyeh } NK_API void nk_glfw3_new_frame(struct nk_glfw* glfw) { int i; double x, y; struct nk_context *ctx = &glfw->ctx; struct GLFWwindow *win = glfw->win; glfwGetWindowSize(win, &glfw->width, &glfw->height); glfwGetFramebufferSize(win, &glfw->display_width, &glfw->display_height); glfw->fb_scale.x = (float)glfw->display_width/(float)glfw->width; glfw->fb_scale.y = (float)glfw->display_height/(float)glfw->height; nk_input_begin(ctx); for (i = 0; i < glfw->text_len; ++i) nk_input_unicode(ctx, glfw->text[i]); #if NK_GLFW_GL3_MOUSE_GRABBING /* optional grabbing behavior */ if (ctx->input.mouse.grab) glfwSetInputMode(glfw->win, GLFW_CURSOR, GLFW_CURSOR_HIDDEN); else if (ctx->input.mouse.ungrab) glfwSetInputMode(glfw->win, GLFW_CURSOR, GLFW_CURSOR_NORMAL); #endif nk_input_key(ctx, NK_KEY_DEL, glfwGetKey(win, GLFW_KEY_DELETE) == GLFW_PRESS); nk_input_key(ctx, NK_KEY_ENTER, glfwGetKey(win, GLFW_KEY_ENTER) == GLFW_PRESS); nk_input_key(ctx, NK_KEY_TAB, glfwGetKey(win, GLFW_KEY_TAB) == GLFW_PRESS); nk_input_key(ctx, NK_KEY_BACKSPACE, glfwGetKey(win, GLFW_KEY_BACKSPACE) == GLFW_PRESS); nk_input_key(ctx, NK_KEY_UP, glfwGetKey(win, GLFW_KEY_UP) == GLFW_PRESS); nk_input_key(ctx, NK_KEY_DOWN, glfwGetKey(win, GLFW_KEY_DOWN) == GLFW_PRESS); nk_input_key(ctx, NK_KEY_TEXT_START, glfwGetKey(win, GLFW_KEY_HOME) == GLFW_PRESS); nk_input_key(ctx, NK_KEY_TEXT_END, glfwGetKey(win, GLFW_KEY_END) == GLFW_PRESS); nk_input_key(ctx, NK_KEY_SCROLL_START, glfwGetKey(win, GLFW_KEY_HOME) == GLFW_PRESS); nk_input_key(ctx, NK_KEY_SCROLL_END, glfwGetKey(win, GLFW_KEY_END) == GLFW_PRESS); nk_input_key(ctx, NK_KEY_SCROLL_DOWN, glfwGetKey(win, GLFW_KEY_PAGE_DOWN) == GLFW_PRESS); nk_input_key(ctx, NK_KEY_SCROLL_UP, glfwGetKey(win, GLFW_KEY_PAGE_UP) == GLFW_PRESS); nk_input_key(ctx, NK_KEY_SHIFT, glfwGetKey(win, GLFW_KEY_LEFT_SHIFT) == GLFW_PRESS|| glfwGetKey(win, GLFW_KEY_RIGHT_SHIFT) == GLFW_PRESS); if (glfwGetKey(win, GLFW_KEY_LEFT_CONTROL) == GLFW_PRESS || glfwGetKey(win, GLFW_KEY_RIGHT_CONTROL) == GLFW_PRESS) { nk_input_key(ctx, NK_KEY_COPY, glfwGetKey(win, GLFW_KEY_C) == GLFW_PRESS); nk_input_key(ctx, NK_KEY_PASTE, glfwGetKey(win, GLFW_KEY_V) == GLFW_PRESS); nk_input_key(ctx, NK_KEY_CUT, glfwGetKey(win, GLFW_KEY_X) == GLFW_PRESS); nk_input_key(ctx, NK_KEY_TEXT_UNDO, glfwGetKey(win, GLFW_KEY_Z) == GLFW_PRESS); nk_input_key(ctx, NK_KEY_TEXT_REDO, glfwGetKey(win, GLFW_KEY_R) == GLFW_PRESS); nk_input_key(ctx, NK_KEY_TEXT_WORD_LEFT, glfwGetKey(win, GLFW_KEY_LEFT) == GLFW_PRESS); nk_input_key(ctx, NK_KEY_TEXT_WORD_RIGHT, glfwGetKey(win, GLFW_KEY_RIGHT) == GLFW_PRESS); nk_input_key(ctx, NK_KEY_TEXT_LINE_START, glfwGetKey(win, GLFW_KEY_B) == GLFW_PRESS); nk_input_key(ctx, NK_KEY_TEXT_LINE_END, glfwGetKey(win, GLFW_KEY_E) == GLFW_PRESS); } else { nk_input_key(ctx, NK_KEY_LEFT, glfwGetKey(win, GLFW_KEY_LEFT) == GLFW_PRESS); nk_input_key(ctx, NK_KEY_RIGHT, glfwGetKey(win, GLFW_KEY_RIGHT) == GLFW_PRESS); nk_input_key(ctx, NK_KEY_COPY, 0); nk_input_key(ctx, NK_KEY_PASTE, 0); nk_input_key(ctx, NK_KEY_CUT, 0); nk_input_key(ctx, NK_KEY_SHIFT, 0); } glfwGetCursorPos(win, &x, &y); nk_input_motion(ctx, (int)x, (int)y); #if NK_GLFW_GL3_MOUSE_GRABBING if (ctx->input.mouse.grabbed) { glfwSetCursorPos(glfw->win, ctx->input.mouse.prev.x, ctx->input.mouse.prev.y); ctx->input.mouse.pos.x = ctx->input.mouse.prev.x; ctx->input.mouse.pos.y = ctx->input.mouse.prev.y; } #endif nk_input_button(ctx, NK_BUTTON_LEFT, (int)x, (int)y, glfwGetMouseButton(win, GLFW_MOUSE_BUTTON_LEFT) == GLFW_PRESS); nk_input_button(ctx, NK_BUTTON_MIDDLE, (int)x, (int)y, glfwGetMouseButton(win, GLFW_MOUSE_BUTTON_MIDDLE) == GLFW_PRESS); nk_input_button(ctx, NK_BUTTON_RIGHT, (int)x, (int)y, glfwGetMouseButton(win, GLFW_MOUSE_BUTTON_RIGHT) == GLFW_PRESS); nk_input_button(ctx, NK_BUTTON_DOUBLE, glfw->double_click_pos.x, glfw->double_click_pos.y, glfw->is_double_click_down); nk_input_scroll(ctx, glfw->scroll); nk_input_end(&glfw->ctx); glfw->text_len = 0; glfw->scroll = nk_vec2(0,0); } NK_API void nk_glfw3_shutdown(struct nk_glfw* glfw) { nk_font_atlas_clear(&glfw->atlas); nk_free(&glfw->ctx); nk_glfw3_device_destroy(glfw); memset(glfw, 0, sizeof(*glfw)); } #endif #endif #line 0 #line 1 "3rd_nuklear_filebrowser.h" // file browser for nuklear, based on https://github.com/vurtun/nuklear/blob/master/example/file_browser.c (public domain) // - rlyeh, public domain // // changelog: // - ported to V4K api // - namespaced symbols // - diverse win32 fixes // - adaptive cols/rows // - removed nk_begin()/nk_end() pairs // - dangling nk_group_begin/end() pairs // - simplified file<->media_group concept // - minor cosmetics #ifdef _WIN32 #include // _getcwd() #else #include // getcwd() #include // getpwuid() #endif #if 1 #define BROWSER_PRINTF(...) do {} while(0) #else #define BROWSER_PRINTF printf #endif enum browser_groups { BROWSER_FOLDER, BROWSER_HOME, BROWSER_DESKTOP, BROWSER_COMPUTER, BROWSER_PROJECT, BROWSER_MAXFOLDERS, BROWSER_MAXTYPES = 64, }; struct browser_media_group { unsigned icon; const char *extensions; }; struct browser_media { int font; int icon_sheet; struct nk_image custom_folders[BROWSER_MAXFOLDERS]; struct nk_image custom_files[BROWSER_MAXTYPES]; struct browser_media_group group[BROWSER_MAXTYPES]; } media = {0}; void browser_config_dir(struct nk_image icon, unsigned counter) { if( counter < BROWSER_MAXFOLDERS ) { media.custom_folders[ counter ] = icon; } } void browser_config_type(struct nk_image icon, const char *extensions) { static int counter = 0; if( counter < BROWSER_MAXTYPES ) { media.custom_files[ counter ] = icon; media.group[ counter ].icon = counter; media.group[ counter ].extensions = extensions; ++counter; } } #define BROWSER_MAX_PATH 512 struct browser { /* path */ char file[BROWSER_MAX_PATH]; // selection char directory[BROWSER_MAX_PATH]; // current cwd while browsing char home[BROWSER_MAX_PATH]; char desktop[BROWSER_MAX_PATH]; char computer[BROWSER_MAX_PATH]; char project[BROWSER_MAX_PATH]; // cwd when first invoked /* directory content */ array(char*) files; array(char*) directories; size_t file_count; size_t dir_count; /* view mode */ bool listing; float zooming; }; static struct nk_image* media_icon_for_file(const char *file) { /* extract extension .xxx from file */ char *ext = strrchr(file, '.'); if( ext && strlen(ext) < 16 ) { char ext_dot[16+1]; snprintf(ext_dot, 16, "%s.", ext); /* check for all file definition of all groups for fitting extension. skip first group (default) */ for (int i = 1; i < BROWSER_MAXTYPES && media.group[i].extensions; ++i) { if( strstri(media.group[i].extensions, ext_dot) ) { return &media.custom_files[ media.group[i].icon ]; } } } // return first (default) group return &media.custom_files[0]; } static void browser_reload_directory_content(struct browser *browser, const char *path) { if(path[0] == '\0') path = va("./"); if(!strend(path, "/")) path = va("%s/", path); for(int i = 0; i < array_count(browser->files); ++i) FREE(browser->files[i]); for(int i = 0; i < array_count(browser->directories); ++i) FREE(browser->directories[i]); array_resize(browser->files, 0); array_resize(browser->directories, 0); BROWSER_PRINTF("searching at %s\n", path); const char **list = file_list(path, "*"); for( int i = 0; list[i]; ++i ) { char *absolute = file_pathabs(ifndef(win32, list[i], va("%s/%s", path, list[i]))); // ../dir/./file.ext -> c:/prj/dir/file.ext BROWSER_PRINTF("%s->%s %d->", list[i], absolute, file_directory(absolute) ); if( file_directory(absolute) ) { // remove last '/' if present. ok to overwrite absolute var, file_*() API returns writeable strings. char *dir = absolute; if( dir[ strlen(dir) - 1 ] == '/' ) dir[ strlen(dir) - 1 ] = '\0'; dir = file_name(dir); // /home/rlyeh/prj/v4k/art -> art BROWSER_PRINTF("%s\n", dir); if( dir[0] != '.' ) // skip special files, folders and internal files like .git or .art.zip array_push(browser->directories, STRDUP(dir)); } else { const char *fname = file_name(absolute); BROWSER_PRINTF("%s\n", fname); if( fname[0] != '.' ) // skip special files, folders and internal files like .git or .art.zip array_push(browser->files, STRDUP(fname)); } } browser->file_count = array_count(browser->files); browser->dir_count = array_count(browser->directories); } static void browser_chdir_and_reload_directory_content(struct browser *browser, const char *path) { if( path != browser->directory ) strncpy(browser->directory, path, BROWSER_MAX_PATH); browser_reload_directory_content(browser, path); } static void browser_init(struct browser *browser) { memset(browser, 0, sizeof(*browser)); { /* load files and sub-directory list */ const char *home = getenv("HOME"); #ifdef _WIN32 if (!home) home = getenv("USERPROFILE"); #else if (!home) home = getpwuid(getuid())->pw_dir; #endif snprintf(browser->home, BROWSER_MAX_PATH, "%s/", home); snprintf(browser->desktop, BROWSER_MAX_PATH, "%s/Desktop/", home); snprintf(browser->computer, BROWSER_MAX_PATH, "%s", ifdef(win32, va("%.*s", 3, getenv("windir")), "/")); { ifdef(win32, _getcwd, getcwd)(browser->project, sizeof(browser->project) - 1); // -1 == room for '/' strcat(browser->project, "/"); } BROWSER_PRINTF("%s\n", browser->home); BROWSER_PRINTF("%s\n", browser->desktop); BROWSER_PRINTF("%s\n", browser->computer); BROWSER_PRINTF("%s\n", browser->project); browser_chdir_and_reload_directory_content(browser, browser->project); } } static void browser_free(struct browser *browser) { for(int i = 0; i < array_count(browser->files); ++i) FREE(browser->files[i]); for(int i = 0; i < array_count(browser->directories); ++i) FREE(browser->directories[i]); array_free(browser->files); array_free(browser->directories); memset(browser, 0, sizeof(*browser)); } static int browser_run(struct nk_context *ctx, struct browser *browser, int windowed, struct nk_rect total_space) { int clicked = 0; static float ratio[] = {0.25f, NK_UNDEFINED}; float spacing_x = ctx->style.window.spacing.x; /* output path directory selector in the menubar */ ctx->style.window.spacing.x = 0; if( windowed ) nk_menubar_begin(ctx); { char *d = browser->directory; #ifdef _WIN32 char *begin = d; #else char *begin = d + 1; #endif nk_layout_row_template_begin(ctx, 25); nk_layout_row_template_push_variable(ctx, 40); nk_layout_row_template_push_variable(ctx, 40); nk_layout_row_template_push_variable(ctx, 40); nk_layout_row_template_end(ctx); if (nk_button_label(ctx, !browser->listing ? ICON_MD_LIST : ICON_MD_GRID_VIEW)) { browser->listing ^= 1; } while (*d++) { if (*d == '/') { *d = '\0'; if (nk_button_label(ctx, va("%s" ICON_MD_ARROW_RIGHT, file_name(begin)))) { *d++ = '/'; *d = '\0'; browser_chdir_and_reload_directory_content(browser, browser->directory); break; } *d = '/'; begin = d + 1; } } } if( windowed ) nk_menubar_end(ctx); ctx->style.window.spacing.x = spacing_x; if(nk_window_has_focus(ctx)) browser->zooming = clampf( browser->zooming + input_diff(MOUSE_W) * 0.1, 1, 3); bool compact = 0, tiny = browser->listing; // compact, no left panel. tiny, no large icons size_t cols = total_space.w / (100 * browser->zooming); int icon_height = (67 * browser->zooming) * (tiny ? 0.33 : 1.); // icon height (96) + button padding (??). originally: 135 /**/ if( tiny ) cols = (int)cols+1.5, cols /= 2, compact = total_space.w < 500; // cols <= 2; else cols = (int)cols+1, compact = total_space.w < 500; // cols <= 5; if( cols < 1 ) cols=1; /* window layout */ nk_layout_row(ctx, NK_DYNAMIC, total_space.h, compact ? 1 : 2, compact ? ratio+1 : ratio); if( !compact ) if( nk_group_begin(ctx, "Special", NK_WINDOW_NO_SCROLLBAR) ) { nk_layout_row_dynamic(ctx, 40, 1); if (nk_button_image_label(ctx,media.custom_folders[BROWSER_HOME],"Home",NK_TEXT_RIGHT)) browser_chdir_and_reload_directory_content(browser, browser->home); if (nk_button_image_label(ctx,media.custom_folders[BROWSER_DESKTOP],"Desktop",NK_TEXT_RIGHT)) browser_chdir_and_reload_directory_content(browser, browser->desktop); if (nk_button_image_label(ctx,media.custom_folders[BROWSER_COMPUTER],"Computer",NK_TEXT_RIGHT)) browser_chdir_and_reload_directory_content(browser, browser->computer); if (nk_button_image_label(ctx,media.custom_folders[BROWSER_PROJECT],"Project",NK_TEXT_RIGHT)) browser_chdir_and_reload_directory_content(browser, browser->project); nk_group_end(ctx); } /* output directory content window */ if(nk_group_begin(ctx, "Content", windowed ? NK_WINDOW_NO_SCROLLBAR : 0)) { int index = -1; size_t i = 0, j = 0, k = 0; size_t rows = 0; size_t count = browser->dir_count + browser->file_count; rows = count / cols; for (i = 0; i <= rows; i += 1) { if(!tiny) {size_t n = j + cols; nk_layout_row_dynamic(ctx, icon_height, (int)cols); for (; j < count && j < n; ++j) { /* draw one row of icons */ if (j < browser->dir_count) { /* draw and execute directory buttons */ if (nk_button_image(ctx,media.custom_folders[BROWSER_FOLDER])) index = (int)j; } else { /* draw and execute files buttons */ struct nk_image *icon; size_t fileIndex = ((size_t)j - browser->dir_count); icon = media_icon_for_file(browser->files[fileIndex]); if (nk_button_image(ctx, *icon)) { snprintf(browser->file, BROWSER_MAX_PATH, "%s%s", browser->directory, browser->files[fileIndex]); clicked = 1; } } }} if(!tiny) {size_t n = k + cols; nk_layout_row_dynamic(ctx, 20, (int)cols); for (; k < count && k < n; k++) { /* draw one row of labels */ if (k < browser->dir_count) { nk_label(ctx, browser->directories[k], NK_TEXT_CENTERED); } else { size_t t = k-browser->dir_count; nk_label(ctx,browser->files[t],NK_TEXT_CENTERED); } }} if(tiny) {size_t n = j + cols; nk_layout_row_dynamic(ctx, icon_height, (int)cols); for (; j < count && j < n; ++j) { /* draw one row of icons */ if (j < browser->dir_count) { /* draw and execute directory buttons */ if (nk_button_image_label(ctx,media.custom_folders[BROWSER_FOLDER], browser->directories[j],NK_TEXT_RIGHT)) index = (int)j; } else { /* draw and execute files buttons */ struct nk_image *icon; size_t fileIndex = ((size_t)j - browser->dir_count); icon = media_icon_for_file(browser->files[fileIndex]); size_t t = j-browser->dir_count; if (nk_button_image_label(ctx, *icon, browser->files[t],NK_TEXT_RIGHT)) { snprintf(browser->file, BROWSER_MAX_PATH, "%s%s", browser->directory, browser->files[fileIndex]); clicked = 1; } } #if 0 bool has_focus = nk_window_has_focus(ctx); // @fixme: move out of loop bool has_popups = ui_popups(); // @fixme: move out of loop if( !has_popups && has_focus ) { struct nk_rect bounds = nk_widget_bounds(ctx); if (nk_input_is_mouse_hovering_rect(&ctx->input, bounds) ) { char *name = j < browser->dir_count ? browser->directories[j] : browser->files[j-browser->dir_count]; char fullpath[PATH_MAX]; snprintf(fullpath, PATH_MAX, "%s%s", browser->directory, name); struct stat t = {0}; if( stat( fullpath, &t ) != -1 ) { char tooltip[256]; snprintf(tooltip, 256, "Path: %s\n" "Type: %lld\n" // file type and mode "Size: %lld\n" // file size "Owner: %lld\n" // user ID of file owner "Modified: %s (%lld)", // last modification date name, (int64_t)t.st_mode, (int64_t)t.st_size, (int64_t)t.st_uid, ctime(&t.st_mtime), (int64_t)t.st_mtime ); nk_tooltip(ctx, tooltip); } } } #endif }} } if (index != -1) { BROWSER_PRINTF("%s + %s = ", browser->directory, browser->directories[index]); size_t n = strlen(browser->directory); snprintf(browser->directory + n, BROWSER_MAX_PATH - n, "%s/", browser->directories[index]); BROWSER_PRINTF("%s\n", browser->directory); browser_chdir_and_reload_directory_content(browser, browser->directory); } nk_group_end(ctx); } return clicked; } static struct nk_image icon_load(const char *filename) { texture_t t = texture(filename, 0); return nk_image_id((int)t.id); } static struct nk_image icon_load_rect(unsigned id, unsigned w, unsigned h, unsigned wcell, unsigned hcell, unsigned col, unsigned row) { return nk_subimage_id((int)id, w, h, (struct nk_rect){ wcell * col, hcell * row, wcell, hcell }); } /* demo: struct browser browser = {0}; browser_init(&browser); browser_config_dir(nk_image, BROWSER_HOME); browser_config_dir(nk_image, BROWSER_PROJECT); // [...] browser_config_type(nk_image, ".ext1.ext2.ext3."); browser_config_type(nk_image, ".ext1.ext2.ext3."); browser_config_type(nk_image, ".ext1.ext2.ext3."); // [...] [...] if( nk_begin(ctx, "window", ...) ) { struct nk_rect total_space = nk_window_get_content_region(ctx); if( browser_run(ctx, &browser, 0, total_space) ) { puts( browser->directory ); puts( browser->file ); } } nk_end(); */ #line 0 //--- #ifdef WITH_ASSIMP //{{FILE/*:*/3rd_assimp.h}} //#include "3rd_assimp/cimport.h" //#include "3rd_assimp/scene.h" //#include "3rd_assimp/postprocess.h" //#pragma comment(lib, "3rd/3rd_assimp/x64/assimp") #endif #line 1 "3rd_json5.h" // JSON5 + SJSON parser module // // License: // This software is dual-licensed to the public domain and under the following // license: you are granted a perpetual, irrevocable license to copy, modify, // publish, and distribute this file as you see fit. // No warranty is implied, use at your own risk. // // Credits: // r-lyeh (fork) // Dominik Madarasz (@zaklaus) (original code) #ifndef JSON5_H #define JSON5_H #ifndef JSON5_ASSERT #define JSON5_ASSERT do { printf("JSON5: Error L%d while parsing '%c' in '%.16s'\n", __LINE__, p[0], p); assert(0); } while(0) #endif #include #include typedef enum json5_type { JSON5_UNDEFINED, // 0 JSON5_NULL, // 1 JSON5_BOOL, // 2 JSON5_OBJECT, // 3 JSON5_STRING, // 4 JSON5_ARRAY, // 5 JSON5_INTEGER, // 6 JSON5_REAL, // 7 } json5_type; typedef struct json5 { char* name; #ifdef NDEBUG unsigned type : 3; #else json5_type type; #endif unsigned count : 29; union { struct json5* array; struct json5* nodes; int64_t integer; double real; char* string; int boolean; }; } json5; char* json5_parse(json5 *root, char *source, int flags); void json5_write(FILE *fp, const json5 *root); void json5_free(json5 *root); #endif // JSON5_H // json5 ---------------------------------------------------------------------- #ifdef JSON5_C //#pragma once #include #include #include #include #include char *json5__trim(char *p) { while (*p) { /**/ if( isspace(*p) ) ++p; else if( p[0] == '/' && p[1] == '*' ) { // skip C comment for( p += 2; *p && !(p[0] == '*' && p[1] == '/'); ++p) {} if( *p ) p += 2; } else if( p[0] == '/' && p[1] == '/' ) { // skip C++ comment for( p += 2; *p && p[0] != '\n'; ++p) {} if( *p ) ++p; } else break; } return p; } char *json5__parse_value(json5 *obj, char *p, char **err_code); char *json5__parse_string(json5 *obj, char *p, char **err_code) { assert(obj && p); if( *p == '"' || *p == '\'' || *p == '`' ) { obj->type = JSON5_STRING; obj->string = p + 1; char eos_char = *p, *b = obj->string, *e = b; while (*e) { /**/ if( *e == '\\' && (e[1] == eos_char) ) ++e; else if( *e == '\\' && (e[1] == '\r' || e[1] == '\n') ) *e = ' '; else if( *e == eos_char ) break; ++e; } *e = '\0'; return p = e + 1; } //JSON5_ASSERT; *err_code = "json5_error_invalid_value"; return NULL; } char *json5__parse_object(json5 *obj, char *p, char **err_code) { assert(obj && p); if( 1 /* *p == '{' */ ) { /* <-- for SJSON */ int skip = *p == '{'; /* <-- for SJSON */ obj->type = JSON5_OBJECT; obj->nodes = 0; obj->count = 0; while (*p) { json5 node = { 0 }; do { p = json5__trim(p + skip); skip = 1; } while( *p == ',' ); if( *p == '}' ) { ++p; break; } // @todo: is_unicode() (s[0] == '\\' && isxdigit(s[1]) && isxdigit(s[2]) && isxdigit(s[3]) && isxdigit(s[4]))) { else if( isalnum(*p) || *p == '_' || *p == '$' || *p == '.' ) { // also || is_unicode(p) node.name = p; do { ++p; } while (*p && (isalnum(*p) || *p == '_' || *p == '$' || *p == '.') ); // also || is_unicode(p) char *e = p; p = json5__trim(p); *e = '\0'; } else { //if( *p == '"' || *p == '\'' || *p == '`' ) { char *ps = json5__parse_string(&node, p, err_code); if( !ps ) { return NULL; } p = ps; node.name = node.string; p = json5__trim(p); } // @todo: https://www.ecma-international.org/ecma-262/5.1/#sec-7.6 if( !(node.name && node.name[0]) ) { // !json5__validate_name(node.name) ) { JSON5_ASSERT; *err_code = "json5_error_invalid_name"; return NULL; } if( !p || (*p && (*p != ':' && *p != '=' /* <-- for SJSON */)) ) { JSON5_ASSERT; *err_code = "json5_error_invalid_name"; return NULL; } p = json5__trim(p + 1); p = json5__parse_value(&node, p, err_code); if( *err_code[0] ) { return NULL; } if( node.type != JSON5_UNDEFINED ) { array_push(obj->nodes, node); ++obj->count; } if( *p == '}') { ++p; break; } } return p; } JSON5_ASSERT; *err_code = "json5_error_invalid_value"; return NULL; } char *json5__parse_value(json5 *obj, char *p, char **err_code) { assert(obj && p); p = json5__trim(p); char *is_string = json5__parse_string(obj, p, err_code); if( is_string ) { p = is_string; if( *err_code[0] ) { return NULL; } } else if( *p == '{' ) { p = json5__parse_object( obj, p, err_code ); if( *err_code[0] ) { return NULL; } } else if( *p == '[' ) { obj->type = JSON5_ARRAY; obj->array = 0; obj->count = 0; while (*p) { json5 elem = { 0 }; do { p = json5__trim(p + 1); } while( *p == ',' ); if( *p == ']') { ++p; break; } p = json5__parse_value(&elem, p, err_code); if( *err_code[0] ) { return NULL; } if( elem.type != JSON5_UNDEFINED ) { array_push(obj->array, elem); ++obj->count; } if (*p == ']') { ++p; break; } } } else if( isalpha(*p) || (*p == '-' && !isdigit(p[1])) ) { const char *labels[] = { "null", "on","true", "off","false", "nan","NaN", "-nan","-NaN", "inf","Infinity", "-inf","-Infinity", 0 }; const int lenghts[] = { 4, 2,4, 3,5, 3,3, 4,4, 3,8, 4,9 }; for( int i = 0; labels[i]; ++i ) { if( !strncmp(p, labels[i], lenghts[i] ) ) { p += lenghts[i]; #ifdef _MSC_VER // somehow, NaN is apparently signed in MSC /**/ if( i >= 5 ) obj->type = JSON5_REAL, obj->real = i >= 11 ? -INFINITY : i >= 9 ? INFINITY : i >= 7 ? NAN :-NAN; #else /**/ if( i >= 5 ) obj->type = JSON5_REAL, obj->real = i >= 11 ? -INFINITY : i >= 9 ? INFINITY : i >= 7 ? -NAN : NAN; #endif else if( i >= 1 ) obj->type = JSON5_BOOL, obj->boolean = i <= 2; else obj->type = JSON5_NULL; break; } } if( obj->type == JSON5_UNDEFINED ) { JSON5_ASSERT; *err_code = "json5_error_invalid_value"; return NULL; } } else if( isdigit(*p) || *p == '+' || *p == '-' || *p == '.' ) { char buffer[32] = {0}, *buf = buffer, is_hex = 0, is_dbl = 0; while( *p && strchr("+-.xX0123456789aAbBcCdDeEfF", *p)) { is_hex |= (*p | 32) == 'x'; is_dbl |= *p == '.'; *buf++ = *p++; } obj->type = is_dbl ? JSON5_REAL : JSON5_INTEGER; /**/ if( is_dbl ) sscanf( buffer, "%lf", &obj->real ); else if( is_hex ) sscanf( buffer, "%llx", &obj->integer ); // SCNx64 -> inttypes.h else sscanf( buffer, "%lld", &obj->integer ); // SCNd64 -> inttypes.h } else { return NULL; } return p; } char *json5_parse(json5 *root, char *p, int flags) { char *err_code = ""; *root = (json5) {0}; if( p && p[0] ) { p = json5__trim(p); if( *p == '[' ) { /* <-- for SJSON */ json5__parse_value(root, p, &err_code); } else { json5__parse_object(root, p, &err_code); /* <-- for SJSON */ } } else { root->type = JSON5_OBJECT; } return err_code[0] ? err_code : 0; } void json5_free(json5 *root) { if( root->type == JSON5_ARRAY && root->array ) { for( int i = 0, cnt = array_count(root->array); i < cnt; ++i ) { json5_free(root->array + i); } array_free(root->array); } if( root->type == JSON5_OBJECT && root->nodes ) { for( int i = 0, cnt = array_count(root->nodes); i < cnt; ++i ) { json5_free(root->nodes + i); } array_free(root->nodes); } *root = (json5) {0}; // needed? } void json5_write(FILE *fp, const json5 *o) { static __thread int indent = 0; int tabs = 1; // 0,1,2,4,8 if( o->name ) { fprintf(fp, "%*.s\"%s\"%s", indent * tabs, "", o->name, tabs ? ": " : ":"); } /**/ if( o->type == JSON5_NULL ) fprintf(fp, "%s", "null"); else if( o->type == JSON5_BOOL ) fprintf(fp, "%s", o->boolean ? "true" : "false"); else if( o->type == JSON5_INTEGER ) fprintf(fp, "%lld", o->integer); else if( o->type == JSON5_REAL ) { /**/ if( isnan(o->real) ) fprintf(fp, "%s", signbit(o->real) ? "-nan" : "nan" ); else if( isinf(o->real) ) fprintf(fp, "%s", signbit(o->real) ? "-inf" : "inf" ); else fprintf(fp, "%1.8e", o->real); // %1.8e from google:"randomascii 100 digits" ; %.4llf for compactness } #if 0 else if( o->type == JSON5_STRING ) { // write (escaped) string char chars[] = "\\\"\n\r\b\f\v", remap[] = "\\\"nrbfv", esc[256]; for( int i = 0; chars[i]; ++i ) esc[ chars[i] ] = remap[i]; const char *b = o->string, *e = strpbrk(b, chars), *sep = "\""; while( e ) { fprintf(fp, "%s%.*s\\%c", sep, (int)(e - b), b, esc[(unsigned char)*e] ); e = strpbrk( b = e + 1, chars); sep = ""; } fprintf(fp, "%s%s\"", sep, b); } #else else if( o->type == JSON5_STRING ) { // write string fprintf(fp, "\"%s\"", o->string); } #endif else if( o->type == JSON5_ARRAY ) { const char *sep = ""; fprintf(fp, "%s", tabs ? "[ " : "["); for( int i = 0, cnt = o->count; i < cnt; ++i ) { fprintf(fp, "%s", sep); sep = tabs ? ", " : ","; json5_write(fp, o->array + i); } fprintf(fp, "%s", tabs ? " ]" : "]"); } else if( o->type == JSON5_OBJECT ) { const char *sep = ""; fprintf(fp, "%*.s{%s", 0 * (++indent) * tabs, "", tabs ? "\n":""); for( int i = 0, cnt = o->count; i < cnt; ++i ) { fprintf(fp, "%s", sep); sep = tabs ? ",\n" : ","; json5_write(fp, o->nodes + i); } fprintf(fp, "%s%*.s}", tabs ? "\n":"", (--indent) * tabs, ""); } else { char p[16] = {0}; JSON5_ASSERT; /* "json5_error_invalid_value"; */ } } #ifdef JSON5_BENCH #include int main() { // https://www.reddit.com/r/datasets/comments/1uyd0t/200000_jeopardy_questions_in_a_json_file/ char *content = 0; for( FILE *fp = fopen("jeopardy.json", "rb"); fp; fclose(fp), fp = 0 ) { fseek(fp, 0L, SEEK_END); size_t pos = ftell(fp); fseek(fp, 0L, SEEK_SET); content = (char*)malloc( pos + 1 ); fread(content, 1, pos, fp); content[pos] = 0; } if( content ) { clock_t start = clock(); json5 root = {0}; char *error = json5_parse(&root, content, 0); clock_t end = clock(); double delta = ( end - start ) / (double)CLOCKS_PER_SEC; if( !error ) { printf("Parsing time: %.3fms\n", delta*1000); printf("Total nodes: %d\n", array_count(root.array)); printf("Category: %s, air date: %s\nQuestion: %s\n", root.array[0].nodes[0].string, root.array[0].nodes[1].string, root.array[0].nodes[2].string); } else { printf("Error: %s\n", error); } json5_free(&root); free(content); } } #define main main__ #endif #ifdef JSON5_DEMO int main() { char source5[] = " // comments\n" /* json5 sample */ " unquoted: 'and you can quote me on that',\n" " singleQuotes: 'I can use \"double quotes\" here',\n" " lineBreaks : \"Look, Mom! \\\n" "No \\n's!\",\n" " hexadecimal: 0x100,\n" " leadingDecimalPoint: .8675309, andTrailing: 8675309.,\n" " positiveSign: +1,\n" " trailingComma: 'in objects', andIn: ['arrays', ],\n" " \"backwardsCompatible\": \"with JSON\",\n" "" " ip = \"127.0.0.1\"\n" /* sjson sample */ " port = 8888\n" "" " /* comment //nested comment*/\n" /* tests */ " // comment /*nested comment*/\n" " nil: null," " \"+lšctžýáíé=:\": true,,,," " huge: 2.2239333e5, " " array: [+1,2,-3,4,5], " " hello: 'world /*comment in string*/ //again', " " abc: 42.67, def: false, " " children : { a: 1, b: 2, }," " invalids : [ nan, NaN, -nan, -NaN, inf, Infinity, -inf, -Infinity ]," "" " multiline: `this is\n" "a multiline string\n" "yeah`" "}\n"; json5 root = { 0 }; char *error = json5_parse(&root, source5, 0); if( error ) { printf("Error: %s\n", error); } else { json5_write(stdout, &root); } json5_free(&root); } #define main main__ #endif #endif // JSON5_C #line 0 #line 1 "3rd_gjk.h" // GJK distance algorithm. original code by @vurtun and @randygaul, public domain. // [src] https://gist.github.com/vurtun/29727217c269a2fbf4c0ed9a1d11cb40 // - rlyeh, public domain. /* Gilbert–Johnson–Keerthi (GJK) 3D distance algorithm The Gilbert–Johnson–Keerthi (GJK) distance algorithm is a method of determining the minimum distance between two convex sets. The algorithm's stability, speed which operates in near-constant time, and small storage footprint make it popular for realtime collision detection. Unlike many other distance algorithms, it has no requirments on geometry data to be stored in any specific format, but instead relies solely on a support function to iteratively generate closer simplices to the correct answer using the Minkowski sum (CSO) of two convex shapes. GJK algorithms are used incrementally. In this mode, the final simplex from a previous solution is used as the initial guess in the next iteration. If the positions in the new frame are close to those in the old frame, the algorithm will converge in one or two iterations. */ #ifndef GJK_H #define GJK_H #define GJK_MAX_ITERATIONS 20 typedef struct gjk_support { int aid, bid; vec3 a; vec3 b; } gjk_support; typedef struct gjk_vertex { vec3 a; vec3 b; vec3 p; int aid, bid; } gjk_vertex; typedef struct gjk_simplex { int max_iter, iter; int hit, cnt; gjk_vertex v[4]; float bc[4], D; } gjk_simplex; typedef struct gjk_result { int hit; vec3 p0; vec3 p1; float distance_squared; int iterations; } gjk_result; int gjk(gjk_simplex *s, const gjk_support *sup, vec3 *dv); gjk_result gjk_analyze(const gjk_simplex *s); gjk_result gjk_quad(float a_radius, float b_radius); #endif #ifdef GJK_C //#pragma once #include #include #define GJK_FLT_MAX FLT_MAX // 3.40282347E+38F #define GJK_EPSILON FLT_EPSILON // 1.19209290E-07F float gjk_inv_sqrt(float n) { union {unsigned u; float f;} conv; conv.f = n; conv.u = 0x5f375A84 - (conv.u >> 1); conv.f = conv.f * (1.5f - (n * 0.5f * conv.f * conv.f)); return conv.f; } int gjk(gjk_simplex *s, const gjk_support *sup, vec3 *dv) { assert(s); assert(dv); assert(sup); if (!s || !sup || !dv) return 0; if (s->max_iter > 0 && s->iter >= s->max_iter) return 0; /* I.) Initialize */ if (s->cnt == 0) { s->D = GJK_FLT_MAX; s->max_iter = !s->max_iter ? GJK_MAX_ITERATIONS: s->max_iter; } /* II.) Check for duplications */ for (int i = 0; i < s->cnt; ++i) { if (sup->aid != s->v[i].aid) continue; if (sup->bid != s->v[i].bid) continue; return 0; } /* III.) Add vertex into simplex */ gjk_vertex *vert = &s->v[s->cnt]; vert->a = sup->a; vert->b = sup->b; vert->p = *dv; vert->aid = sup->aid; vert->bid = sup->bid; s->bc[s->cnt++] = 1.0f; /* IV.) Find closest simplex point */ switch (s->cnt) { case 1: break; case 2: { /* -------------------- Line ----------------------- */ vec3 a = s->v[0].p; vec3 b = s->v[1].p; /* compute barycentric coordinates */ vec3 ab = sub3(a, b); vec3 ba = sub3(b, a); float u = dot3(b, ba); float v = dot3(a, ab); if (v <= 0.0f) { /* region A */ s->bc[0] = 1.0f; s->cnt = 1; break; } if (u <= 0.0f) { /* region B */ s->v[0] = s->v[1]; s->bc[0] = 1.0f; s->cnt = 1; break; } /* region AB */ s->bc[0] = u; s->bc[1] = v; s->cnt = 2; } break; case 3: { /* -------------------- Triangle ----------------------- */ vec3 a = s->v[0].p; vec3 b = s->v[1].p; vec3 c = s->v[2].p; vec3 ab = sub3(a, b); vec3 ba = sub3(b, a); vec3 bc = sub3(b, c); vec3 cb = sub3(c, b); vec3 ca = sub3(c, a); vec3 ac = sub3(a, c); /* compute barycentric coordinates */ float u_ab = dot3(b, ba); float v_ab = dot3(a, ab); float u_bc = dot3(c, cb); float v_bc = dot3(b, bc); float u_ca = dot3(a, ac); float v_ca = dot3(c, ca); if (v_ab <= 0.0f && u_ca <= 0.0f) { /* region A */ s->bc[0] = 1.0f; s->cnt = 1; break; } if (u_ab <= 0.0f && v_bc <= 0.0f) { /* region B */ s->v[0] = s->v[1]; s->bc[0] = 1.0f; s->cnt = 1; break; } if (u_bc <= 0.0f && v_ca <= 0.0f) { /* region C */ s->v[0] = s->v[2]; s->bc[0] = 1.0f; s->cnt = 1; break; } /* calculate fractional area */ vec3 n; n = cross3(ba, ca); vec3 n1; n1 = cross3(b, c); vec3 n2; n2 = cross3(c, a); vec3 n3; n3 = cross3(a, b); float u_abc = dot3(n1, n); float v_abc = dot3(n2, n); float w_abc = dot3(n3, n); if (u_ab > 0.0f && v_ab > 0.0f && w_abc <= 0.0f) { /* region AB */ s->bc[0] = u_ab; s->bc[1] = v_ab; s->cnt = 2; break; } if (u_bc > 0.0f && v_bc > 0.0f && u_abc <= 0.0f) { /* region BC */ s->v[0] = s->v[1]; s->v[1] = s->v[2]; s->bc[0] = u_bc; s->bc[1] = v_bc; s->cnt = 2; break; } if (u_ca > 0.0f && v_ca > 0.0f && v_abc <= 0.0f) { /* region CA */ s->v[1] = s->v[0]; s->v[0] = s->v[2]; s->bc[0] = u_ca; s->bc[1] = v_ca; s->cnt = 2; break; } /* region ABC */ assert(u_abc > 0.0f && v_abc > 0.0f && w_abc > 0.0f); s->bc[0] = u_abc; s->bc[1] = v_abc; s->bc[2] = w_abc; s->cnt = 3; } break; case 4: { /* -------------------- Tetrahedron ----------------------- */ vec3 a = s->v[0].p; vec3 b = s->v[1].p; vec3 c = s->v[2].p; vec3 d = s->v[3].p; vec3 ab = sub3(a, b); vec3 ba = sub3(b, a); vec3 bc = sub3(b, c); vec3 cb = sub3(c, b); vec3 ca = sub3(c, a); vec3 ac = sub3(a, c); vec3 db = sub3(d, b); vec3 bd = sub3(b, d); vec3 dc = sub3(d, c); vec3 cd = sub3(c, d); vec3 da = sub3(d, a); vec3 ad = sub3(a, d); /* compute barycentric coordinates */ float u_ab = dot3(b, ba); float v_ab = dot3(a, ab); float u_bc = dot3(c, cb); float v_bc = dot3(b, bc); float u_ca = dot3(a, ac); float v_ca = dot3(c, ca); float u_bd = dot3(d, db); float v_bd = dot3(b, bd); float u_dc = dot3(c, cd); float v_dc = dot3(d, dc); float u_ad = dot3(d, da); float v_ad = dot3(a, ad); /* check verticies for closest point */ if (v_ab <= 0.0f && u_ca <= 0.0f && v_ad <= 0.0f) { /* region A */ s->bc[0] = 1.0f; s->cnt = 1; break; } if (u_ab <= 0.0f && v_bc <= 0.0f && v_bd <= 0.0f) { /* region B */ s->v[0] = s->v[1]; s->bc[0] = 1.0f; s->cnt = 1; break; } if (u_bc <= 0.0f && v_ca <= 0.0f && u_dc <= 0.0f) { /* region C */ s->v[0] = s->v[2]; s->bc[0] = 1.0f; s->cnt = 1; break; } if (u_bd <= 0.0f && v_dc <= 0.0f && u_ad <= 0.0f) { /* region D */ s->v[0] = s->v[3]; s->bc[0] = 1.0f; s->cnt = 1; break; } /* calculate fractional area */ vec3 n; n = cross3(da, ba); vec3 n1; n1 = cross3(d, b); vec3 n2; n2 = cross3(b, a); vec3 n3; n3 = cross3(a, d); float u_adb = dot3(n1, n); float v_adb = dot3(n2, n); float w_adb = dot3(n3, n); n = cross3(ca, da); n1 = cross3(c, d); n2 = cross3(d, a); n3 = cross3(a, c); float u_acd = dot3(n1, n); float v_acd = dot3(n2, n); float w_acd = dot3(n3, n); n = cross3(bc, dc); n1 = cross3(b, d); n2 = cross3(d, c); n3 = cross3(c, b); float u_cbd = dot3(n1, n); float v_cbd = dot3(n2, n); float w_cbd = dot3(n3, n); n = cross3(ba, ca); n1 = cross3(b, c); n2 = cross3(c, a); n3 = cross3(a, b); float u_abc = dot3(n1, n); float v_abc = dot3(n2, n); float w_abc = dot3(n3, n); /* check edges for closest point */ if (w_abc <= 0.0f && v_adb <= 0.0f && u_ab > 0.0f && v_ab > 0.0f) { /* region AB */ s->bc[0] = u_ab; s->bc[1] = v_ab; s->cnt = 2; break; } if (u_abc <= 0.0f && w_cbd <= 0.0f && u_bc > 0.0f && v_bc > 0.0f) { /* region BC */ s->v[0] = s->v[1]; s->v[1] = s->v[2]; s->bc[0] = u_bc; s->bc[1] = v_bc; s->cnt = 2; break; } if (v_abc <= 0.0f && w_acd <= 0.0f && u_ca > 0.0f && v_ca > 0.0f) { /* region CA */ s->v[1] = s->v[0]; s->v[0] = s->v[2]; s->bc[0] = u_ca; s->bc[1] = v_ca; s->cnt = 2; break; } if (v_cbd <= 0.0f && u_acd <= 0.0f && u_dc > 0.0f && v_dc > 0.0f) { /* region DC */ s->v[0] = s->v[3]; s->v[1] = s->v[2]; s->bc[0] = u_dc; s->bc[1] = v_dc; s->cnt = 2; break; } if (v_acd <= 0.0f && w_adb <= 0.0f && u_ad > 0.0f && v_ad > 0.0f) { /* region AD */ s->v[1] = s->v[3]; s->bc[0] = u_ad; s->bc[1] = v_ad; s->cnt = 2; break; } if (u_cbd <= 0.0f && u_adb <= 0.0f && u_bd > 0.0f && v_bd > 0.0f) { /* region BD */ s->v[0] = s->v[1]; s->v[1] = s->v[3]; s->bc[0] = u_bd; s->bc[1] = v_bd; s->cnt = 2; break; } /* calculate fractional volume (volume can be negative!) */ float denom = dot3(cross3(cb, ab), db); // box3(cb, ab, db) float volume = (denom == 0) ? 1.0f: 1.0f/denom; float u_abcd = dot3(cross3(c, d), b) * volume; // box3(c, d, b) float v_abcd = dot3(cross3(c, a), d) * volume; // box3(c, a, d) float w_abcd = dot3(cross3(d, a), b) * volume; // box3(d, a, b) float x_abcd = dot3(cross3(b, a), c) * volume; // box3(b, a, c) /* check faces for closest point */ if (x_abcd <= 0.0f && u_abc > 0.0f && v_abc > 0.0f && w_abc > 0.0f) { /* region ABC */ s->bc[0] = u_abc; s->bc[1] = v_abc; s->bc[2] = w_abc; s->cnt = 3; break; } if (u_abcd <= 0.0f && u_cbd > 0.0f && v_cbd > 0.0f && w_cbd > 0.0f) { /* region CBD */ s->v[0] = s->v[2]; s->v[2] = s->v[3]; s->bc[0] = u_cbd; s->bc[1] = v_cbd; s->bc[2] = w_cbd; s->cnt = 3; break; } if (v_abcd <= 0.0f && u_acd > 0.0f && v_acd > 0.0f && w_acd > 0.0f) { /* region ACD */ s->v[1] = s->v[2]; s->v[2] = s->v[3]; s->bc[0] = u_acd; s->bc[1] = v_acd; s->bc[2] = w_acd; s->cnt = 3; break; } if (w_abcd <= 0.0f && u_adb > 0.0f && v_adb > 0.0f && w_adb > 0.0f) { /* region ADB */ s->v[2] = s->v[1]; s->v[1] = s->v[3]; s->bc[0] = u_adb; s->bc[1] = v_adb; s->bc[2] = w_adb; s->cnt = 3; break; } /* region ABCD */ // assert(u_abcd > 0.0f && v_abcd > 0.0f && w_abcd > 0.0f && x_abcd > 0.0f); // tcc+linux asserts in here: both u_abcd and v_abcd are negative s->bc[0] = u_abcd; s->bc[1] = v_abcd; s->bc[2] = w_abcd; s->bc[3] = x_abcd; s->cnt = 4; } break;} /* V.) Check if origin is enclosed by tetrahedron */ if (s->cnt == 4) { s->hit = 1; return 0; } /* VI.) Ensure closing in on origin to prevent multi-step cycling */ vec3 pnt; float denom = 0; for (int i = 0; i < s->cnt; ++i) denom += s->bc[i]; denom = 1.0f / denom; switch (s->cnt) { case 1: pnt = s->v[0].p; break; case 2: { /* --------- Line -------- */ vec3 a = scale3(s->v[0].p, denom * s->bc[0]); vec3 b = scale3(s->v[1].p, denom * s->bc[1]); pnt = add3(a, b); } break; case 3: { /* ------- Triangle ------ */ vec3 a = scale3(s->v[0].p, denom * s->bc[0]); vec3 b = scale3(s->v[1].p, denom * s->bc[1]); vec3 c = scale3(s->v[2].p, denom * s->bc[2]); pnt = add3(a, b); pnt = add3(pnt, c); } break; case 4: { /* ----- Tetrahedron ----- */ vec3 a = scale3(s->v[0].p, denom * s->bc[0]); vec3 b = scale3(s->v[1].p, denom * s->bc[1]); vec3 c = scale3(s->v[2].p, denom * s->bc[2]); vec3 d = scale3(s->v[3].p, denom * s->bc[3]); pnt = add3(a, b); pnt = add3(pnt, c); pnt = add3(pnt, d); } break;} float d2 = dot3(pnt, pnt); if (d2 >= s->D) return 0; s->D = d2; /* VII.) New search direction */ switch (s->cnt) { default: assert(0); break; case 1: { /* --------- Point -------- */ *dv = scale3(s->v[0].p, -1); } break; case 2: { /* ------ Line segment ---- */ vec3 ba = sub3(s->v[1].p, s->v[0].p); vec3 b0 = scale3(s->v[1].p, -1); vec3 t; t = cross3(ba, b0); *dv = cross3(t, ba); } break; case 3: { /* ------- Triangle ------- */ vec3 ab = sub3(s->v[1].p, s->v[0].p); vec3 ac = sub3(s->v[2].p, s->v[0].p); vec3 n; n = cross3(ab, ac); if (dot3(n, s->v[0].p) <= 0.0f) *dv = n; else *dv = scale3(n, -1); }} if (dot3(*dv,*dv) < GJK_EPSILON * GJK_EPSILON) return 0; return 1; } gjk_result gjk_analyze(const gjk_simplex *s) { gjk_result r = {0}, *res = &r; res->iterations = s->iter; res->hit = s->hit; /* calculate normalization denominator */ float denom = 0; for (int i = 0; i < s->cnt; ++i) denom += s->bc[i]; denom = 1.0f / denom; /* compute closest points */ switch (s->cnt) { default: assert(0); break; case 1: { /* Point */ res->p0 = s->v[0].a; res->p1 = s->v[0].b; } break; case 2: { /* Line */ float as = denom * s->bc[0]; float bs = denom * s->bc[1]; vec3 a = scale3(s->v[0].a, as); vec3 b = scale3(s->v[1].a, bs); vec3 c = scale3(s->v[0].b, as); vec3 d = scale3(s->v[1].b, bs); res->p0 = add3(a, b); res->p1 = add3(c, d); } break; case 3: { /* Triangle */ float as = denom * s->bc[0]; float bs = denom * s->bc[1]; float cs = denom * s->bc[2]; vec3 a = scale3(s->v[0].a, as); vec3 b = scale3(s->v[1].a, bs); vec3 c = scale3(s->v[2].a, cs); vec3 d = scale3(s->v[0].b, as); vec3 e = scale3(s->v[1].b, bs); vec3 f = scale3(s->v[2].b, cs); res->p0 = add3(a, b); res->p0 = add3(res->p0, c); res->p1 = add3(d, e); res->p1 = add3(res->p1, f); } break; case 4: { /* Tetrahedron */ vec3 a = scale3(s->v[0].a, denom * s->bc[0]); vec3 b = scale3(s->v[1].a, denom * s->bc[1]); vec3 c = scale3(s->v[2].a, denom * s->bc[2]); vec3 d = scale3(s->v[3].a, denom * s->bc[3]); res->p0 = add3(a, b); res->p0 = add3(res->p0, c); res->p0 = add3(res->p0, d); res->p1 = res->p0; } break;} if (!res->hit) { /* compute distance */ vec3 d= sub3(res->p1, res->p0); res->distance_squared = dot3(d, d); } else res->distance_squared = 0; return r; } gjk_result gjk_quad(float a_radius, float b_radius) { gjk_result r = {0}, *res = &r; float radius = a_radius + b_radius; float radius_squared = radius * radius; if (res->distance_squared > GJK_EPSILON && res->distance_squared > radius_squared) { res->distance_squared -= radius_squared; /* calculate normal */ vec3 n = sub3(res->p1, res->p0); float l2 = dot3(n, n); if (l2 != 0.0f) { float il = gjk_inv_sqrt(l2); n = scale3(n,il); } vec3 da = scale3(n, a_radius); vec3 db = scale3(n, b_radius); /* calculate new collision points */ res->p0 = add3(res->p0, da); res->p1 = sub3(res->p1, db); } else { vec3 p = add3(res->p0, res->p1); res->p0 = scale3(p, 0.5f); res->p1 = res->p0; res->distance_squared = 0; res->hit = 1; } return r; } #endif #line 0 #line 1 "3rd_compress.h" // compress.c de/compressors into a single-file header // - rlyeh, public domain // // current file format: // header : [1< #ifndef REALLOC #define REALLOC realloc #endif // compressor type [0..15]: high nibble // compression level/flags [0..15]: low hibble // compressor_type << 4 + compression_level = 1 byte enum { RAW = 0, PPP = (1<<4), ULZ = (2<<4), LZ4X = (3<<4), CRSH = (4<<4), DEFL = (5<<4), LZP1 = (6<<4), LZMA = (7<<4), BALZ = (8<<4), LZW3 = (9<<4), LZSS = (10<<4), BCM = (11<<4), NUM_COMPRESSORS = 13 }; // mem de/encoder unsigned mem_bounds(unsigned inlen, unsigned compressor); unsigned mem_encode(const void *in, unsigned inlen, void *out, unsigned outlen, unsigned compressor); unsigned mem_excess(unsigned compressor); unsigned mem_decode(const void *in, unsigned inlen, void *out, unsigned outlen); // file de/encoder unsigned file_encode(FILE* in, FILE* out, FILE *logfile, unsigned cnum, unsigned *clist); unsigned file_decode(FILE* in, FILE* out, FILE *logfile); #endif // COMPRESS_H #ifdef COMPRESS_C //#pragma once #define RAW_C #define PPP_C #define ULZ_C #define LZ4X_C #define CRUSH_C #define DEFLATE_C #define LZP1_C #define LZMA_C #define BALZ_C #define LZRW3A_C #define LZSS_C #define BCM_C #endif //#line 1 "amalgamated_balz.c" // balz.cpp is written and placed in the public domain by Ilya Muravyov // additional code by @r-lyeh (public domain) unsigned balz_encode(const void *in, unsigned inlen, void *out, unsigned outlen, unsigned flags /*[0..1]*/); unsigned balz_decode(const void *in, unsigned inlen, void *out, unsigned outlen); unsigned balz_bounds(unsigned inlen, unsigned flags); unsigned balz_excess(unsigned flags); #ifdef BALZ_C //#pragma once #define _CRT_SECURE_NO_WARNINGS #define _CRT_DISABLE_PERFCRIT_LOCKS #include #include #include typedef struct mfile { uint8_t *begin, *seek, *end; } mfile; int minit(mfile *f, const void *ptr, int len) { f->begin = f->seek = f->end = (uint8_t*)ptr; f->end += len; return 0; } int mread(mfile *m, void *buf, int len) { if( len >= (m->end - m->seek) ) len = (m->end - m->seek); memcpy(buf,m->seek,len); m->seek += len; return len; } int mwrite(mfile *m, const void *buf, int len) { if( len >= (m->end - m->seek) ) len = (m->end - m->seek); memcpy(m->seek,buf,len); m->seek += len; return len; } int mtell(mfile *m) { return m->seek - m->begin; } int mavail(mfile *m) { return m->end - m->seek; } int mputc(mfile *m, int i) { uint8_t ch = i; return mwrite(m, &ch, 1); } int mgetc(mfile *m) { if( mavail(m) <= 0 ) return -1; uint8_t ch; mread(m, &ch, 1); return ch; } typedef struct Counter { uint16_t p1; uint16_t p2; } Counter; void CounterCtor(Counter *c) { c->p1 = 1<<15; c->p2 = 1<<15; } uint32_t CounterP(const Counter *c) { return c->p1+c->p2; } void CounterUpdate0(Counter *c) { c->p1-=c->p1>>3; c->p2-=c->p2>>6; } void CounterUpdate1(Counter *c) { c->p1+=(c->p1^65535)>>3; c->p2+=(c->p2^65535)>>6; } typedef struct Encoder { uint32_t code; uint32_t low; uint32_t high; mfile *in, *out; } Encoder; void EncoderCtor(Encoder *e, mfile *in, mfile *out) { e->code = e->low = 0; e->high = -1; e->in = in; e->out = out; } void EncoderEncode(Encoder *e, int bit, Counter *c) { const uint32_t mid=e->low+((((uint64_t)e->high-e->low)*(CounterP(c)<<15))>>32); if (bit) { e->high=mid; CounterUpdate1(c); } else { e->low=mid+1; CounterUpdate0(c); } while ((e->low^e->high)<(1<<24)) { mputc(e->out, e->low>>24); e->low<<=8; e->high=(e->high<<8)|255; } } void EncoderFlush(Encoder *e) { for (int i=0; i<4; ++i) { mputc(e->out, e->low>>24); e->low<<=8; } } void EncoderInit(Encoder *e) { for (int i=0; i<4; ++i) e->code=(e->code<<8)|mgetc(e->in); } int EncoderDecode(Encoder *e, Counter *c) { const uint32_t mid=e->low+((((uint64_t)e->high-e->low)*(CounterP(c)<<15))>>32); const int bit=(e->code<=mid); if (bit) { e->high=mid; CounterUpdate1(c); } else { e->low=mid+1; CounterUpdate0(c); } while ((e->low^e->high)<(1<<24)) { e->code=(e->code<<8)|mgetc(e->in); e->low<<=8; e->high=(e->high<<8)|255; } return bit; } enum { BALZ_TAB_BITS=7 }; enum { BALZ_TAB_SIZE=1<encoder, in, out); for( int i = 0; i < 256; ++i) for( int j = 0; j < 512; ++j) CounterCtor(&cm->counter1[i][j]); for( int i = 0; i < 256; ++i) for( int j = 0; j < BALZ_TAB_SIZE; ++j) CounterCtor(&cm->counter2[i][j]); } void CMInit(CM *cm) { EncoderInit(&cm->encoder); } void CMEncode(CM *cm, int t, int c1) { int ctx=1; while (ctx<512) { const int bit=((t&256)!=0); t+=t; EncoderEncode(&cm->encoder, bit, &cm->counter1[c1][ctx]); ctx+=ctx+bit; } } void CMEncodeIdx(CM *cm, int x, int c2) { int ctx=1; while (ctx>1))!=0); x+=x; EncoderEncode(&cm->encoder, bit, &cm->counter2[c2][ctx]); ctx+=ctx+bit; } } int CMDecode(CM *cm, int c1) { int ctx=1; while (ctx<512) ctx+=ctx+EncoderDecode(&cm->encoder, &cm->counter1[c1][ctx]); return ctx-512; } int CMDecodeIdx(CM *cm, int c2) { int ctx=1; while (ctxencoder, &cm->counter2[c2][ctx]); return ctx-BALZ_TAB_SIZE; } enum { BALZ_MIN_MATCH=3 }; enum { BALZ_MAX_MATCH=255+BALZ_MIN_MATCH }; enum { BALZ_BUF_BITS=25 }; enum { BALZ_BUF_SIZE=1<=-p)&&(*addr<(n-p))) \ *addr+=p; \ else if ((*addr>0)&&(*addr=0) \ *addr+=n; \ } \ else if (*addr=BALZ_MIN_MATCH?(len<BALZ_MAX_MATCH) max_match=BALZ_MAX_MATCH; for (int x=0; xlen) { idx=x; len=l; if (l==max_match) break; } } return get_pts(len, idx); } int balz_compress(const uint8_t *in, unsigned inlen, uint8_t *out, unsigned outlen, unsigned is_max) { balz_init(); *out++ = (inlen >> 24) & 255; *out++ = (inlen >> 16) & 255; *out++ = (inlen >> 8) & 255; *out++ = (inlen >> 0) & 255; outlen -= 4; mfile inf, outf; minit(&inf, in, inlen); minit(&outf, out, outlen); CM cm; CMCtor(&cm, &inf, &outf); int best_idx[BALZ_MAX_MATCH+1]; int n; while ((n=mread(&inf, buf, BALZ_BUF_SIZE))>0) { //e8e9_transform(1,n); memset(tab, 0, sizeof(tab)); int p=0; while ((p<2)&&(pBALZ_MAX_MATCH) max_match=BALZ_MAX_MATCH; for (int x=0; xlen) { for (int i=l; i>len; --i) best_idx[i]=x; idx=x; len=l; if (l==max_match) break; } } if ((is_max)&&(len>=BALZ_MIN_MATCH)) { int sum=get_pts(len, idx)+get_pts_at(p+len, n); if (sumsum) { sum=tmp; len=i; } } idx=best_idx[len]; } } tab[c2][++cnt[c2]&BALZ_TAB_MASK]=hash|p; if (len>=BALZ_MIN_MATCH) { CMEncode(&cm, (256-BALZ_MIN_MATCH)+len, buf[p-1]); CMEncodeIdx(&cm, idx, buf[p-2]); p+=len; } else { CMEncode(&cm, buf[p], buf[p-1]); ++p; } } } EncoderFlush(&cm.encoder); if ( (inf.seek - inf.begin) != inlen) { return 0; // size mismatch error } return (int)(outf.seek - outf.begin) + 4; } int balz_decompress(const uint8_t *in, unsigned inlen, uint8_t *out, unsigned outlen) { balz_init(); uint32_t flen32 = 0; flen32 |= ((uint32_t)*in++) << 24; flen32 |= ((uint32_t)*in++) << 16; flen32 |= ((uint32_t)*in++) << 8; flen32 |= ((uint32_t)*in++) << 0; outlen = flen32; int flen = flen32; inlen -= 4; mfile inf, outf; minit(&inf, in, inlen); minit(&outf, out, outlen); CM cm; CMCtor(&cm, &inf, &outf); CMInit(&cm); #define balz_src_avail ((int)(inf.end - inf.seek)) #define balz_dst_avail ((int)(outf.end - outf.seek)) #define balz_dst_written ((int)(outf.seek - outf.begin)) while(/*(balz_src_avail > 0) &&*/ (balz_dst_written != flen)) { int p=0; while ((p<2) && ((p+balz_dst_written)=256) { return 0; // corrupt file error } buf[p++]=t; } while ((p < BALZ_BUF_SIZE) && (p+balz_dst_written 0)) { const int tmp=p; const int c2=*(uint16_t*)(&buf[p-2]); // unaligned const int t=CMDecode(&cm, buf[p-1]); if (t>=256) { int len=t-256; int s=tab[c2][(cnt[c2]-CMDecodeIdx(&cm, buf[p-2]))&BALZ_TAB_MASK]; buf[p++]=buf[s++]; buf[p++]=buf[s++]; buf[p++]=buf[s++]; while (len--) buf[p++]=buf[s++]; } else buf[p++]=t; tab[c2][++cnt[c2]&BALZ_TAB_MASK]=tmp; } //e8e9_transform(0,p); mwrite(&outf, buf, p); } return (int)(outf.seek - outf.begin); } unsigned balz_encode(const void *in, unsigned inlen, void *out, unsigned outlen, unsigned flags /*[0..1]*/) { unsigned level = flags > 0 ? 1 : 0; return (unsigned)balz_compress((const uint8_t *)in, inlen, (uint8_t*)out, outlen, level); } unsigned balz_decode(const void *in, unsigned inlen, void *out, unsigned outlen) { return (unsigned)balz_decompress((const uint8_t *)in, inlen, (uint8_t*)out, outlen); } unsigned balz_bounds(unsigned inlen, unsigned flags) { return (unsigned)(inlen * 1.1) + 16; // @todo: check src } unsigned balz_excess(unsigned flags) { return (unsigned)0; } #endif // BALZ_C #ifdef BALZ_DEMO //#pragma once #include int main() { const char *longcopy = "Hello world! Hello world! Hello world! Hello world!"; int level=1; char out[128]; unsigned outlen = balz_encode(longcopy, strlen(longcopy)+1, out, 128, level); printf("%s %d->%d\n", outlen ? "ok" : "fail", (int)strlen(longcopy)+1, (int)outlen); char redo[128]; unsigned unpacked = balz_decode(out, outlen, redo, 128); printf("%d->%d %s\n", (int)outlen, (int)unpacked, redo); } #define main main__ #endif // BALZ_DEMO //#line 1 "amalgamated_bcm_bwt.c" #ifndef BCM_C // do nothing #elif defined BCM_NO_ENCODER // dummy int bcm_divbwt(const unsigned char *T, unsigned char *U, int *A, int n) { return -1; } #else /* * divsufsort.h for libdivsufsort-lite * Copyright (c) 2003-2008 Yuta Mori All Rights Reserved. * * 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 _DIVSUFSORT_H #define _DIVSUFSORT_H 1 #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ /*- Prototypes -*/ /** * Constructs the suffix array of a given string. * @param T[0..n-1] The input string. * @param SA[0..n-1] The output array of suffixes. * @param n The length of the given string. * @return 0 if no error occurred, -1 or -2 otherwise. */ int bcm_divsufsort(const unsigned char *T, int *SA, int n); /** * Constructs the burrows-wheeler transformed string of a given string. * @param T[0..n-1] The input string. * @param U[0..n-1] The output string. (can be T) * @param A[0..n-1] The temporary array. (can be NULL) * @param n The length of the given string. * @return The primary index if no error occurred, -1 or -2 otherwise. */ int bcm_divbwt(const unsigned char *T, unsigned char *U, int *A, int n); #ifdef __cplusplus } /* extern "C" */ #endif /* __cplusplus */ #endif /* _DIVSUFSORT_H */ /* * divsufsort.c for libdivsufsort-lite * Copyright (c) 2003-2008 Yuta Mori All Rights Reserved. * * 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 #include #include #ifdef _OPENMP # include #endif //#include "bcm_divsufsort.h" /*- Constants -*/ #ifndef INLINE #define INLINE __inline #endif #if defined(ALPHABET_SIZE) && (ALPHABET_SIZE < 1) # undef ALPHABET_SIZE #endif #if !defined(ALPHABET_SIZE) # define ALPHABET_SIZE (256) #endif #define BUCKET_A_SIZE (ALPHABET_SIZE) #define BUCKET_B_SIZE (ALPHABET_SIZE * ALPHABET_SIZE) #if defined(SS_INSERTIONSORT_THRESHOLD) # if SS_INSERTIONSORT_THRESHOLD < 1 # undef SS_INSERTIONSORT_THRESHOLD # define SS_INSERTIONSORT_THRESHOLD (1) # endif #else # define SS_INSERTIONSORT_THRESHOLD (8) #endif #if defined(SS_BLOCKSIZE) # if SS_BLOCKSIZE < 0 # undef SS_BLOCKSIZE # define SS_BLOCKSIZE (0) # elif 32768 <= SS_BLOCKSIZE # undef SS_BLOCKSIZE # define SS_BLOCKSIZE (32767) # endif #else # define SS_BLOCKSIZE (1024) #endif /* minstacksize = log(SS_BLOCKSIZE) / log(3) * 2 */ #if SS_BLOCKSIZE == 0 # define SS_MISORT_STACKSIZE (96) #elif SS_BLOCKSIZE <= 4096 # define SS_MISORT_STACKSIZE (16) #else # define SS_MISORT_STACKSIZE (24) #endif #define SS_SMERGE_STACKSIZE (32) #define TR_INSERTIONSORT_THRESHOLD (8) #define TR_STACKSIZE (64) /*- Macros -*/ #ifndef SWAP # define SWAP(_a, _b) do { t = (_a); (_a) = (_b); (_b) = t; } while(0) #endif /* SWAP */ #ifndef MIN # define MIN(_a, _b) (((_a) < (_b)) ? (_a) : (_b)) #endif /* MIN */ #ifndef MAX # define MAX(_a, _b) (((_a) > (_b)) ? (_a) : (_b)) #endif /* MAX */ #define STACK_PUSH(_a, _b, _c, _d)\ do {\ assert(ssize < STACK_SIZE);\ stack[ssize].a = (_a), stack[ssize].b = (_b),\ stack[ssize].c = (_c), stack[ssize++].d = (_d);\ } while(0) #define STACK_PUSH5(_a, _b, _c, _d, _e)\ do {\ assert(ssize < STACK_SIZE);\ stack[ssize].a = (_a), stack[ssize].b = (_b),\ stack[ssize].c = (_c), stack[ssize].d = (_d), stack[ssize++].e = (_e);\ } while(0) #define STACK_POP(_a, _b, _c, _d)\ do {\ assert(0 <= ssize);\ if(ssize == 0) { return; }\ (_a) = stack[--ssize].a, (_b) = stack[ssize].b,\ (_c) = stack[ssize].c, (_d) = stack[ssize].d;\ } while(0) #define STACK_POP5(_a, _b, _c, _d, _e)\ do {\ assert(0 <= ssize);\ if(ssize == 0) { return; }\ (_a) = stack[--ssize].a, (_b) = stack[ssize].b,\ (_c) = stack[ssize].c, (_d) = stack[ssize].d, (_e) = stack[ssize].e;\ } while(0) #define BUCKET_A(_c0) bucket_A[(_c0)] #if ALPHABET_SIZE == 256 #define BUCKET_B(_c0, _c1) (bucket_B[((_c1) << 8) | (_c0)]) #define BUCKET_BSTAR(_c0, _c1) (bucket_B[((_c0) << 8) | (_c1)]) #else #define BUCKET_B(_c0, _c1) (bucket_B[(_c1) * ALPHABET_SIZE + (_c0)]) #define BUCKET_BSTAR(_c0, _c1) (bucket_B[(_c0) * ALPHABET_SIZE + (_c1)]) #endif /*- Private Functions -*/ static const int lg_table[256]= { -1,0,1,1,2,2,2,2,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7 }; #if (SS_BLOCKSIZE == 0) || (SS_INSERTIONSORT_THRESHOLD < SS_BLOCKSIZE) static INLINE int ss_ilg(int n) { #if SS_BLOCKSIZE == 0 return (n & 0xffff0000) ? ((n & 0xff000000) ? 24 + lg_table[(n >> 24) & 0xff] : 16 + lg_table[(n >> 16) & 0xff]) : ((n & 0x0000ff00) ? 8 + lg_table[(n >> 8) & 0xff] : 0 + lg_table[(n >> 0) & 0xff]); #elif SS_BLOCKSIZE < 256 return lg_table[n]; #else return (n & 0xff00) ? 8 + lg_table[(n >> 8) & 0xff] : 0 + lg_table[(n >> 0) & 0xff]; #endif } #endif /* (SS_BLOCKSIZE == 0) || (SS_INSERTIONSORT_THRESHOLD < SS_BLOCKSIZE) */ #if SS_BLOCKSIZE != 0 static const int sqq_table[256] = { 0, 16, 22, 27, 32, 35, 39, 42, 45, 48, 50, 53, 55, 57, 59, 61, 64, 65, 67, 69, 71, 73, 75, 76, 78, 80, 81, 83, 84, 86, 87, 89, 90, 91, 93, 94, 96, 97, 98, 99, 101, 102, 103, 104, 106, 107, 108, 109, 110, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 128, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 144, 145, 146, 147, 148, 149, 150, 150, 151, 152, 153, 154, 155, 155, 156, 157, 158, 159, 160, 160, 161, 162, 163, 163, 164, 165, 166, 167, 167, 168, 169, 170, 170, 171, 172, 173, 173, 174, 175, 176, 176, 177, 178, 178, 179, 180, 181, 181, 182, 183, 183, 184, 185, 185, 186, 187, 187, 188, 189, 189, 190, 191, 192, 192, 193, 193, 194, 195, 195, 196, 197, 197, 198, 199, 199, 200, 201, 201, 202, 203, 203, 204, 204, 205, 206, 206, 207, 208, 208, 209, 209, 210, 211, 211, 212, 212, 213, 214, 214, 215, 215, 216, 217, 217, 218, 218, 219, 219, 220, 221, 221, 222, 222, 223, 224, 224, 225, 225, 226, 226, 227, 227, 228, 229, 229, 230, 230, 231, 231, 232, 232, 233, 234, 234, 235, 235, 236, 236, 237, 237, 238, 238, 239, 240, 240, 241, 241, 242, 242, 243, 243, 244, 244, 245, 245, 246, 246, 247, 247, 248, 248, 249, 249, 250, 250, 251, 251, 252, 252, 253, 253, 254, 254, 255 }; static INLINE int ss_isqrt(int x) { int y, e; if(x >= (SS_BLOCKSIZE * SS_BLOCKSIZE)) { return SS_BLOCKSIZE; } e = (x & 0xffff0000) ? ((x & 0xff000000) ? 24 + lg_table[(x >> 24) & 0xff] : 16 + lg_table[(x >> 16) & 0xff]) : ((x & 0x0000ff00) ? 8 + lg_table[(x >> 8) & 0xff] : 0 + lg_table[(x >> 0) & 0xff]); if(e >= 16) { y = sqq_table[x >> ((e - 6) - (e & 1))] << ((e >> 1) - 7); if(e >= 24) { y = (y + 1 + x / y) >> 1; } y = (y + 1 + x / y) >> 1; } else if(e >= 8) { y = (sqq_table[x >> ((e - 6) - (e & 1))] >> (7 - (e >> 1))) + 1; } else { return sqq_table[x] >> 4; } return (x < (y * y)) ? y - 1 : y; } #endif /* SS_BLOCKSIZE != 0 */ /*---------------------------------------------------------------------------*/ /* Compares two suffixes. */ static INLINE int ss_compare(const unsigned char *T, const int *p1, const int *p2, int depth) { const unsigned char *U1, *U2, *U1n, *U2n; for(U1 = T + depth + *p1, U2 = T + depth + *p2, U1n = T + *(p1 + 1) + 2, U2n = T + *(p2 + 1) + 2; (U1 < U1n) && (U2 < U2n) && (*U1 == *U2); ++U1, ++U2) { } return U1 < U1n ? (U2 < U2n ? *U1 - *U2 : 1) : (U2 < U2n ? -1 : 0); } /*---------------------------------------------------------------------------*/ #if (SS_BLOCKSIZE != 1) && (SS_INSERTIONSORT_THRESHOLD != 1) /* Insertionsort for small size groups */ static void ss_insertionsort(const unsigned char *T, const int *PA, int *first, int *last, int depth) { int *i, *j; int t; int r; for(i = last - 2; first <= i; --i) { for(t = *i, j = i + 1; 0 < (r = ss_compare(T, PA + t, PA + *j, depth));) { do { *(j - 1) = *j; } while((++j < last) && (*j < 0)); if(last <= j) { break; } } if(r == 0) { *j = ~*j; } *(j - 1) = t; } } #endif /* (SS_BLOCKSIZE != 1) && (SS_INSERTIONSORT_THRESHOLD != 1) */ /*---------------------------------------------------------------------------*/ #if (SS_BLOCKSIZE == 0) || (SS_INSERTIONSORT_THRESHOLD < SS_BLOCKSIZE) static INLINE void ss_fixdown(const unsigned char *Td, const int *PA, int *SA, int i, int size) { int j, k; int v; int c, d, e; for(v = SA[i], c = Td[PA[v]]; (j = 2 * i + 1) < size; SA[i] = SA[k], i = k) { d = Td[PA[SA[k = j++]]]; if(d < (e = Td[PA[SA[j]]])) { k = j; d = e; } if(d <= c) { break; } } SA[i] = v; } /* Simple top-down heapsort. */ static void ss_heapsort(const unsigned char *Td, const int *PA, int *SA, int size) { int i, m; int t; m = size; if((size % 2) == 0) { m--; if(Td[PA[SA[m / 2]]] < Td[PA[SA[m]]]) { SWAP(SA[m], SA[m / 2]); } } for(i = m / 2 - 1; 0 <= i; --i) { ss_fixdown(Td, PA, SA, i, m); } if((size % 2) == 0) { SWAP(SA[0], SA[m]); ss_fixdown(Td, PA, SA, 0, m); } for(i = m - 1; 0 < i; --i) { t = SA[0], SA[0] = SA[i]; ss_fixdown(Td, PA, SA, 0, i); SA[i] = t; } } /*---------------------------------------------------------------------------*/ /* Returns the median of three elements. */ static INLINE int * ss_median3(const unsigned char *Td, const int *PA, int *v1, int *v2, int *v3) { int *t; if(Td[PA[*v1]] > Td[PA[*v2]]) { SWAP(v1, v2); } if(Td[PA[*v2]] > Td[PA[*v3]]) { if(Td[PA[*v1]] > Td[PA[*v3]]) { return v1; } else { return v3; } } return v2; } /* Returns the median of five elements. */ static INLINE int * ss_median5(const unsigned char *Td, const int *PA, int *v1, int *v2, int *v3, int *v4, int *v5) { int *t; if(Td[PA[*v2]] > Td[PA[*v3]]) { SWAP(v2, v3); } if(Td[PA[*v4]] > Td[PA[*v5]]) { SWAP(v4, v5); } if(Td[PA[*v2]] > Td[PA[*v4]]) { SWAP(v2, v4); SWAP(v3, v5); } if(Td[PA[*v1]] > Td[PA[*v3]]) { SWAP(v1, v3); } if(Td[PA[*v1]] > Td[PA[*v4]]) { SWAP(v1, v4); SWAP(v3, v5); } if(Td[PA[*v3]] > Td[PA[*v4]]) { return v4; } return v3; } /* Returns the pivot element. */ static INLINE int * ss_pivot(const unsigned char *Td, const int *PA, int *first, int *last) { int *middle; int t; t = last - first; middle = first + t / 2; if(t <= 512) { if(t <= 32) { return ss_median3(Td, PA, first, middle, last - 1); } else { t >>= 2; return ss_median5(Td, PA, first, first + t, middle, last - 1 - t, last - 1); } } t >>= 3; first = ss_median3(Td, PA, first, first + t, first + (t << 1)); middle = ss_median3(Td, PA, middle - t, middle, middle + t); last = ss_median3(Td, PA, last - 1 - (t << 1), last - 1 - t, last - 1); return ss_median3(Td, PA, first, middle, last); } /*---------------------------------------------------------------------------*/ /* Binary partition for substrings. */ static INLINE int * ss_partition(const int *PA, int *first, int *last, int depth) { int *a, *b; int t; for(a = first - 1, b = last;;) { for(; (++a < b) && ((PA[*a] + depth) >= (PA[*a + 1] + 1));) { *a = ~*a; } for(; (a < --b) && ((PA[*b] + depth) < (PA[*b + 1] + 1));) { } if(b <= a) { break; } t = ~*b; *b = *a; *a = t; } if(first < a) { *first = ~*first; } return a; } /* Multikey introsort for medium size groups. */ static void ss_mintrosort(const unsigned char *T, const int *PA, int *first, int *last, int depth) { #define STACK_SIZE SS_MISORT_STACKSIZE struct { int *a, *b, c; int d; } stack[STACK_SIZE]; const unsigned char *Td; int *a, *b, *c, *d, *e, *f; int s, t; int ssize; int limit; int v, x = 0; for(ssize = 0, limit = ss_ilg(last - first);;) { if((last - first) <= SS_INSERTIONSORT_THRESHOLD) { #if 1 < SS_INSERTIONSORT_THRESHOLD if(1 < (last - first)) { ss_insertionsort(T, PA, first, last, depth); } #endif STACK_POP(first, last, depth, limit); continue; } Td = T + depth; if(limit-- == 0) { ss_heapsort(Td, PA, first, last - first); } if(limit < 0) { for(a = first + 1, v = Td[PA[*first]]; a < last; ++a) { if((x = Td[PA[*a]]) != v) { if(1 < (a - first)) { break; } v = x; first = a; } } if(Td[PA[*first] - 1] < v) { first = ss_partition(PA, first, a, depth); } if((a - first) <= (last - a)) { if(1 < (a - first)) { STACK_PUSH(a, last, depth, -1); last = a, depth += 1, limit = ss_ilg(a - first); } else { first = a, limit = -1; } } else { if(1 < (last - a)) { STACK_PUSH(first, a, depth + 1, ss_ilg(a - first)); first = a, limit = -1; } else { last = a, depth += 1, limit = ss_ilg(a - first); } } continue; } /* choose pivot */ a = ss_pivot(Td, PA, first, last); v = Td[PA[*a]]; SWAP(*first, *a); /* partition */ for(b = first; (++b < last) && ((x = Td[PA[*b]]) == v);) { } if(((a = b) < last) && (x < v)) { for(; (++b < last) && ((x = Td[PA[*b]]) <= v);) { if(x == v) { SWAP(*b, *a); ++a; } } } for(c = last; (b < --c) && ((x = Td[PA[*c]]) == v);) { } if((b < (d = c)) && (x > v)) { for(; (b < --c) && ((x = Td[PA[*c]]) >= v);) { if(x == v) { SWAP(*c, *d); --d; } } } for(; b < c;) { SWAP(*b, *c); for(; (++b < c) && ((x = Td[PA[*b]]) <= v);) { if(x == v) { SWAP(*b, *a); ++a; } } for(; (b < --c) && ((x = Td[PA[*c]]) >= v);) { if(x == v) { SWAP(*c, *d); --d; } } } if(a <= d) { c = b - 1; if((s = a - first) > (t = b - a)) { s = t; } for(e = first, f = b - s; 0 < s; --s, ++e, ++f) { SWAP(*e, *f); } if((s = d - c) > (t = last - d - 1)) { s = t; } for(e = b, f = last - s; 0 < s; --s, ++e, ++f) { SWAP(*e, *f); } a = first + (b - a), c = last - (d - c); b = (v <= Td[PA[*a] - 1]) ? a : ss_partition(PA, a, c, depth); if((a - first) <= (last - c)) { if((last - c) <= (c - b)) { STACK_PUSH(b, c, depth + 1, ss_ilg(c - b)); STACK_PUSH(c, last, depth, limit); last = a; } else if((a - first) <= (c - b)) { STACK_PUSH(c, last, depth, limit); STACK_PUSH(b, c, depth + 1, ss_ilg(c - b)); last = a; } else { STACK_PUSH(c, last, depth, limit); STACK_PUSH(first, a, depth, limit); first = b, last = c, depth += 1, limit = ss_ilg(c - b); } } else { if((a - first) <= (c - b)) { STACK_PUSH(b, c, depth + 1, ss_ilg(c - b)); STACK_PUSH(first, a, depth, limit); first = c; } else if((last - c) <= (c - b)) { STACK_PUSH(first, a, depth, limit); STACK_PUSH(b, c, depth + 1, ss_ilg(c - b)); first = c; } else { STACK_PUSH(first, a, depth, limit); STACK_PUSH(c, last, depth, limit); first = b, last = c, depth += 1, limit = ss_ilg(c - b); } } } else { limit += 1; if(Td[PA[*first] - 1] < v) { first = ss_partition(PA, first, last, depth); limit = ss_ilg(last - first); } depth += 1; } } #undef STACK_SIZE } #endif /* (SS_BLOCKSIZE == 0) || (SS_INSERTIONSORT_THRESHOLD < SS_BLOCKSIZE) */ /*---------------------------------------------------------------------------*/ #if SS_BLOCKSIZE != 0 static INLINE void ss_blockswap(int *a, int *b, int n) { int t; for(; 0 < n; --n, ++a, ++b) { t = *a, *a = *b, *b = t; } } static INLINE void ss_rotate(int *first, int *middle, int *last) { int *a, *b, t; int l, r; l = middle - first, r = last - middle; for(; (0 < l) && (0 < r);) { if(l == r) { ss_blockswap(first, middle, l); break; } if(l < r) { a = last - 1, b = middle - 1; t = *a; do { *a-- = *b, *b-- = *a; if(b < first) { *a = t; last = a; if((r -= l + 1) <= l) { break; } a -= 1, b = middle - 1; t = *a; } } while(1); } else { a = first, b = middle; t = *a; do { *a++ = *b, *b++ = *a; if(last <= b) { *a = t; first = a + 1; if((l -= r + 1) <= r) { break; } a += 1, b = middle; t = *a; } } while(1); } } } /*---------------------------------------------------------------------------*/ static void ss_inplacemerge(const unsigned char *T, const int *PA, int *first, int *middle, int *last, int depth) { const int *p; int *a, *b; int len, half; int q, r; int x; for(;;) { if(*(last - 1) < 0) { x = 1; p = PA + ~*(last - 1); } else { x = 0; p = PA + *(last - 1); } for(a = first, len = middle - first, half = len >> 1, r = -1; 0 < len; len = half, half >>= 1) { b = a + half; q = ss_compare(T, PA + ((0 <= *b) ? *b : ~*b), p, depth); if(q < 0) { a = b + 1; half -= (len & 1) ^ 1; } else { r = q; } } if(a < middle) { if(r == 0) { *a = ~*a; } ss_rotate(a, middle, last); last -= middle - a; middle = a; if(first == middle) { break; } } --last; if(x != 0) { while(*--last < 0) { } } if(middle == last) { break; } } } /*---------------------------------------------------------------------------*/ /* Merge-forward with internal buffer. */ static void ss_mergeforward(const unsigned char *T, const int *PA, int *first, int *middle, int *last, int *buf, int depth) { int *a, *b, *c, *bufend; int t; int r; bufend = buf + (middle - first) - 1; ss_blockswap(buf, first, middle - first); for(t = *(a = first), b = buf, c = middle;;) { r = ss_compare(T, PA + *b, PA + *c, depth); if(r < 0) { do { *a++ = *b; if(bufend <= b) { *bufend = t; return; } *b++ = *a; } while(*b < 0); } else if(r > 0) { do { *a++ = *c, *c++ = *a; if(last <= c) { while(b < bufend) { *a++ = *b, *b++ = *a; } *a = *b, *b = t; return; } } while(*c < 0); } else { *c = ~*c; do { *a++ = *b; if(bufend <= b) { *bufend = t; return; } *b++ = *a; } while(*b < 0); do { *a++ = *c, *c++ = *a; if(last <= c) { while(b < bufend) { *a++ = *b, *b++ = *a; } *a = *b, *b = t; return; } } while(*c < 0); } } } /* Merge-backward with internal buffer. */ static void ss_mergebackward(const unsigned char *T, const int *PA, int *first, int *middle, int *last, int *buf, int depth) { const int *p1, *p2; int *a, *b, *c, *bufend; int t; int r; int x; bufend = buf + (last - middle) - 1; ss_blockswap(buf, middle, last - middle); x = 0; if(*bufend < 0) { p1 = PA + ~*bufend; x |= 1; } else { p1 = PA + *bufend; } if(*(middle - 1) < 0) { p2 = PA + ~*(middle - 1); x |= 2; } else { p2 = PA + *(middle - 1); } for(t = *(a = last - 1), b = bufend, c = middle - 1;;) { r = ss_compare(T, p1, p2, depth); if(0 < r) { if(x & 1) { do { *a-- = *b, *b-- = *a; } while(*b < 0); x ^= 1; } *a-- = *b; if(b <= buf) { *buf = t; break; } *b-- = *a; if(*b < 0) { p1 = PA + ~*b; x |= 1; } else { p1 = PA + *b; } } else if(r < 0) { if(x & 2) { do { *a-- = *c, *c-- = *a; } while(*c < 0); x ^= 2; } *a-- = *c, *c-- = *a; if(c < first) { while(buf < b) { *a-- = *b, *b-- = *a; } *a = *b, *b = t; break; } if(*c < 0) { p2 = PA + ~*c; x |= 2; } else { p2 = PA + *c; } } else { if(x & 1) { do { *a-- = *b, *b-- = *a; } while(*b < 0); x ^= 1; } *a-- = ~*b; if(b <= buf) { *buf = t; break; } *b-- = *a; if(x & 2) { do { *a-- = *c, *c-- = *a; } while(*c < 0); x ^= 2; } *a-- = *c, *c-- = *a; if(c < first) { while(buf < b) { *a-- = *b, *b-- = *a; } *a = *b, *b = t; break; } if(*b < 0) { p1 = PA + ~*b; x |= 1; } else { p1 = PA + *b; } if(*c < 0) { p2 = PA + ~*c; x |= 2; } else { p2 = PA + *c; } } } } /* D&C based merge. */ static void ss_swapmerge(const unsigned char *T, const int *PA, int *first, int *middle, int *last, int *buf, int bufsize, int depth) { #define STACK_SIZE SS_SMERGE_STACKSIZE #define GETIDX(a) ((0 <= (a)) ? (a) : (~(a))) #define MERGE_CHECK(a, b, c)\ do {\ if(((c) & 1) ||\ (((c) & 2) && (ss_compare(T, PA + GETIDX(*((a) - 1)), PA + *(a), depth) == 0))) {\ *(a) = ~*(a);\ }\ if(((c) & 4) && ((ss_compare(T, PA + GETIDX(*((b) - 1)), PA + *(b), depth) == 0))) {\ *(b) = ~*(b);\ }\ } while(0) struct { int *a, *b, *c; int d; } stack[STACK_SIZE]; int *l, *r, *lm, *rm; int m, len, half; int ssize; int check, next; for(check = 0, ssize = 0;;) { if((last - middle) <= bufsize) { if((first < middle) && (middle < last)) { ss_mergebackward(T, PA, first, middle, last, buf, depth); } MERGE_CHECK(first, last, check); STACK_POP(first, middle, last, check); continue; } if((middle - first) <= bufsize) { if(first < middle) { ss_mergeforward(T, PA, first, middle, last, buf, depth); } MERGE_CHECK(first, last, check); STACK_POP(first, middle, last, check); continue; } for(m = 0, len = MIN(middle - first, last - middle), half = len >> 1; 0 < len; len = half, half >>= 1) { if(ss_compare(T, PA + GETIDX(*(middle + m + half)), PA + GETIDX(*(middle - m - half - 1)), depth) < 0) { m += half + 1; half -= (len & 1) ^ 1; } } if(0 < m) { lm = middle - m, rm = middle + m; ss_blockswap(lm, middle, m); l = r = middle, next = 0; if(rm < last) { if(*rm < 0) { *rm = ~*rm; if(first < lm) { for(; *--l < 0;) { } next |= 4; } next |= 1; } else if(first < lm) { for(; *r < 0; ++r) { } next |= 2; } } if((l - first) <= (last - r)) { STACK_PUSH(r, rm, last, (next & 3) | (check & 4)); middle = lm, last = l, check = (check & 3) | (next & 4); } else { if((next & 2) && (r == middle)) { next ^= 6; } STACK_PUSH(first, lm, l, (check & 3) | (next & 4)); first = r, middle = rm, check = (next & 3) | (check & 4); } } else { if(ss_compare(T, PA + GETIDX(*(middle - 1)), PA + *middle, depth) == 0) { *middle = ~*middle; } MERGE_CHECK(first, last, check); STACK_POP(first, middle, last, check); } } #undef STACK_SIZE } #endif /* SS_BLOCKSIZE != 0 */ /*---------------------------------------------------------------------------*/ /* Substring sort */ static void sssort(const unsigned char *T, const int *PA, int *first, int *last, int *buf, int bufsize, int depth, int n, int lastsuffix) { int *a; #if SS_BLOCKSIZE != 0 int *b, *middle, *curbuf; int j, k, curbufsize, limit; #endif int i; if(lastsuffix != 0) { ++first; } #if SS_BLOCKSIZE == 0 ss_mintrosort(T, PA, first, last, depth); #else if((bufsize < SS_BLOCKSIZE) && (bufsize < (last - first)) && (bufsize < (limit = ss_isqrt(last - first)))) { if(SS_BLOCKSIZE < limit) { limit = SS_BLOCKSIZE; } buf = middle = last - limit, bufsize = limit; } else { middle = last, limit = 0; } for(a = first, i = 0; SS_BLOCKSIZE < (middle - a); a += SS_BLOCKSIZE, ++i) { #if SS_INSERTIONSORT_THRESHOLD < SS_BLOCKSIZE ss_mintrosort(T, PA, a, a + SS_BLOCKSIZE, depth); #elif 1 < SS_BLOCKSIZE ss_insertionsort(T, PA, a, a + SS_BLOCKSIZE, depth); #endif curbufsize = last - (a + SS_BLOCKSIZE); curbuf = a + SS_BLOCKSIZE; if(curbufsize <= bufsize) { curbufsize = bufsize, curbuf = buf; } for(b = a, k = SS_BLOCKSIZE, j = i; j & 1; b -= k, k <<= 1, j >>= 1) { ss_swapmerge(T, PA, b - k, b, b + k, curbuf, curbufsize, depth); } } #if SS_INSERTIONSORT_THRESHOLD < SS_BLOCKSIZE ss_mintrosort(T, PA, a, middle, depth); #elif 1 < SS_BLOCKSIZE ss_insertionsort(T, PA, a, middle, depth); #endif for(k = SS_BLOCKSIZE; i != 0; k <<= 1, i >>= 1) { if(i & 1) { ss_swapmerge(T, PA, a - k, a, middle, buf, bufsize, depth); a -= k; } } if(limit != 0) { #if SS_INSERTIONSORT_THRESHOLD < SS_BLOCKSIZE ss_mintrosort(T, PA, middle, last, depth); #elif 1 < SS_BLOCKSIZE ss_insertionsort(T, PA, middle, last, depth); #endif ss_inplacemerge(T, PA, first, middle, last, depth); } #endif if(lastsuffix != 0) { /* Insert last type B* suffix. */ int PAi[2]; PAi[0] = PA[*(first - 1)], PAi[1] = n - 2; for(a = first, i = *(first - 1); (a < last) && ((*a < 0) || (0 < ss_compare(T, &(PAi[0]), PA + *a, depth))); ++a) { *(a - 1) = *a; } *(a - 1) = i; } } /*---------------------------------------------------------------------------*/ static INLINE int tr_ilg(int n) { return (n & 0xffff0000) ? ((n & 0xff000000) ? 24 + lg_table[(n >> 24) & 0xff] : 16 + lg_table[(n >> 16) & 0xff]) : ((n & 0x0000ff00) ? 8 + lg_table[(n >> 8) & 0xff] : 0 + lg_table[(n >> 0) & 0xff]); } /*---------------------------------------------------------------------------*/ /* Simple insertionsort for small size groups. */ static void tr_insertionsort(const int *ISAd, int *first, int *last) { int *a, *b; int t, r; for(a = first + 1; a < last; ++a) { for(t = *a, b = a - 1; 0 > (r = ISAd[t] - ISAd[*b]);) { do { *(b + 1) = *b; } while((first <= --b) && (*b < 0)); if(b < first) { break; } } if(r == 0) { *b = ~*b; } *(b + 1) = t; } } /*---------------------------------------------------------------------------*/ static INLINE void tr_fixdown(const int *ISAd, int *SA, int i, int size) { int j, k; int v; int c, d, e; for(v = SA[i], c = ISAd[v]; (j = 2 * i + 1) < size; SA[i] = SA[k], i = k) { d = ISAd[SA[k = j++]]; if(d < (e = ISAd[SA[j]])) { k = j; d = e; } if(d <= c) { break; } } SA[i] = v; } /* Simple top-down heapsort. */ static void tr_heapsort(const int *ISAd, int *SA, int size) { int i, m; int t; m = size; if((size % 2) == 0) { m--; if(ISAd[SA[m / 2]] < ISAd[SA[m]]) { SWAP(SA[m], SA[m / 2]); } } for(i = m / 2 - 1; 0 <= i; --i) { tr_fixdown(ISAd, SA, i, m); } if((size % 2) == 0) { SWAP(SA[0], SA[m]); tr_fixdown(ISAd, SA, 0, m); } for(i = m - 1; 0 < i; --i) { t = SA[0], SA[0] = SA[i]; tr_fixdown(ISAd, SA, 0, i); SA[i] = t; } } /*---------------------------------------------------------------------------*/ /* Returns the median of three elements. */ static INLINE int * tr_median3(const int *ISAd, int *v1, int *v2, int *v3) { int *t; if(ISAd[*v1] > ISAd[*v2]) { SWAP(v1, v2); } if(ISAd[*v2] > ISAd[*v3]) { if(ISAd[*v1] > ISAd[*v3]) { return v1; } else { return v3; } } return v2; } /* Returns the median of five elements. */ static INLINE int * tr_median5(const int *ISAd, int *v1, int *v2, int *v3, int *v4, int *v5) { int *t; if(ISAd[*v2] > ISAd[*v3]) { SWAP(v2, v3); } if(ISAd[*v4] > ISAd[*v5]) { SWAP(v4, v5); } if(ISAd[*v2] > ISAd[*v4]) { SWAP(v2, v4); SWAP(v3, v5); } if(ISAd[*v1] > ISAd[*v3]) { SWAP(v1, v3); } if(ISAd[*v1] > ISAd[*v4]) { SWAP(v1, v4); SWAP(v3, v5); } if(ISAd[*v3] > ISAd[*v4]) { return v4; } return v3; } /* Returns the pivot element. */ static INLINE int * tr_pivot(const int *ISAd, int *first, int *last) { int *middle; int t; t = last - first; middle = first + t / 2; if(t <= 512) { if(t <= 32) { return tr_median3(ISAd, first, middle, last - 1); } else { t >>= 2; return tr_median5(ISAd, first, first + t, middle, last - 1 - t, last - 1); } } t >>= 3; first = tr_median3(ISAd, first, first + t, first + (t << 1)); middle = tr_median3(ISAd, middle - t, middle, middle + t); last = tr_median3(ISAd, last - 1 - (t << 1), last - 1 - t, last - 1); return tr_median3(ISAd, first, middle, last); } /*---------------------------------------------------------------------------*/ typedef struct _trbudget_t trbudget_t; struct _trbudget_t { int chance; int remain; int incval; int count; }; static INLINE void trbudget_init(trbudget_t *budget, int chance, int incval) { budget->chance = chance; budget->remain = budget->incval = incval; } static INLINE int trbudget_check(trbudget_t *budget, int size) { if(size <= budget->remain) { budget->remain -= size; return 1; } if(budget->chance == 0) { budget->count += size; return 0; } budget->remain += budget->incval - size; budget->chance -= 1; return 1; } /*---------------------------------------------------------------------------*/ static INLINE void tr_partition(const int *ISAd, int *first, int *middle, int *last, int **pa, int **pb, int v) { int *a, *b, *c, *d, *e, *f; int t, s; int x = 0; for(b = middle - 1; (++b < last) && ((x = ISAd[*b]) == v);) { } if(((a = b) < last) && (x < v)) { for(; (++b < last) && ((x = ISAd[*b]) <= v);) { if(x == v) { SWAP(*b, *a); ++a; } } } for(c = last; (b < --c) && ((x = ISAd[*c]) == v);) { } if((b < (d = c)) && (x > v)) { for(; (b < --c) && ((x = ISAd[*c]) >= v);) { if(x == v) { SWAP(*c, *d); --d; } } } for(; b < c;) { SWAP(*b, *c); for(; (++b < c) && ((x = ISAd[*b]) <= v);) { if(x == v) { SWAP(*b, *a); ++a; } } for(; (b < --c) && ((x = ISAd[*c]) >= v);) { if(x == v) { SWAP(*c, *d); --d; } } } if(a <= d) { c = b - 1; if((s = a - first) > (t = b - a)) { s = t; } for(e = first, f = b - s; 0 < s; --s, ++e, ++f) { SWAP(*e, *f); } if((s = d - c) > (t = last - d - 1)) { s = t; } for(e = b, f = last - s; 0 < s; --s, ++e, ++f) { SWAP(*e, *f); } first += (b - a), last -= (d - c); } *pa = first, *pb = last; } static void tr_copy(int *ISA, const int *SA, int *first, int *a, int *b, int *last, int depth) { /* sort suffixes of middle partition by using sorted order of suffixes of left and right partition. */ int *c, *d, *e; int s, v; v = b - SA - 1; for(c = first, d = a - 1; c <= d; ++c) { if((0 <= (s = *c - depth)) && (ISA[s] == v)) { *++d = s; ISA[s] = d - SA; } } for(c = last - 1, e = d + 1, d = b; e < d; --c) { if((0 <= (s = *c - depth)) && (ISA[s] == v)) { *--d = s; ISA[s] = d - SA; } } } static void tr_partialcopy(int *ISA, const int *SA, int *first, int *a, int *b, int *last, int depth) { int *c, *d, *e; int s, v; int rank, lastrank, newrank = -1; v = b - SA - 1; lastrank = -1; for(c = first, d = a - 1; c <= d; ++c) { if((0 <= (s = *c - depth)) && (ISA[s] == v)) { *++d = s; rank = ISA[s + depth]; if(lastrank != rank) { lastrank = rank; newrank = d - SA; } ISA[s] = newrank; } } lastrank = -1; for(e = d; first <= e; --e) { rank = ISA[*e]; if(lastrank != rank) { lastrank = rank; newrank = e - SA; } if(newrank != rank) { ISA[*e] = newrank; } } lastrank = -1; for(c = last - 1, e = d + 1, d = b; e < d; --c) { if((0 <= (s = *c - depth)) && (ISA[s] == v)) { *--d = s; rank = ISA[s + depth]; if(lastrank != rank) { lastrank = rank; newrank = d - SA; } ISA[s] = newrank; } } } static void tr_introsort(int *ISA, const int *ISAd, int *SA, int *first, int *last, trbudget_t *budget) { #define STACK_SIZE TR_STACKSIZE struct { const int *a; int *b, *c; int d, e; }stack[STACK_SIZE]; int *a, *b, *c; int t; int v, x = 0; int incr = ISAd - ISA; int limit, next; int ssize, trlink = -1; for(ssize = 0, limit = tr_ilg(last - first);;) { if(limit < 0) { if(limit == -1) { /* tandem repeat partition */ tr_partition(ISAd - incr, first, first, last, &a, &b, last - SA - 1); /* update ranks */ if(a < last) { for(c = first, v = a - SA - 1; c < a; ++c) { ISA[*c] = v; } } if(b < last) { for(c = a, v = b - SA - 1; c < b; ++c) { ISA[*c] = v; } } /* push */ if(1 < (b - a)) { STACK_PUSH5(NULL, a, b, 0, 0); STACK_PUSH5(ISAd - incr, first, last, -2, trlink); trlink = ssize - 2; } if((a - first) <= (last - b)) { if(1 < (a - first)) { STACK_PUSH5(ISAd, b, last, tr_ilg(last - b), trlink); last = a, limit = tr_ilg(a - first); } else if(1 < (last - b)) { first = b, limit = tr_ilg(last - b); } else { STACK_POP5(ISAd, first, last, limit, trlink); } } else { if(1 < (last - b)) { STACK_PUSH5(ISAd, first, a, tr_ilg(a - first), trlink); first = b, limit = tr_ilg(last - b); } else if(1 < (a - first)) { last = a, limit = tr_ilg(a - first); } else { STACK_POP5(ISAd, first, last, limit, trlink); } } } else if(limit == -2) { /* tandem repeat copy */ a = stack[--ssize].b, b = stack[ssize].c; if(stack[ssize].d == 0) { tr_copy(ISA, SA, first, a, b, last, ISAd - ISA); } else { if(0 <= trlink) { stack[trlink].d = -1; } tr_partialcopy(ISA, SA, first, a, b, last, ISAd - ISA); } STACK_POP5(ISAd, first, last, limit, trlink); } else { /* sorted partition */ if(0 <= *first) { a = first; do { ISA[*a] = a - SA; } while((++a < last) && (0 <= *a)); first = a; } if(first < last) { a = first; do { *a = ~*a; } while(*++a < 0); next = (ISA[*a] != ISAd[*a]) ? tr_ilg(a - first + 1) : -1; if(++a < last) { for(b = first, v = a - SA - 1; b < a; ++b) { ISA[*b] = v; } } /* push */ if(trbudget_check(budget, a - first)) { if((a - first) <= (last - a)) { STACK_PUSH5(ISAd, a, last, -3, trlink); ISAd += incr, last = a, limit = next; } else { if(1 < (last - a)) { STACK_PUSH5(ISAd + incr, first, a, next, trlink); first = a, limit = -3; } else { ISAd += incr, last = a, limit = next; } } } else { if(0 <= trlink) { stack[trlink].d = -1; } if(1 < (last - a)) { first = a, limit = -3; } else { STACK_POP5(ISAd, first, last, limit, trlink); } } } else { STACK_POP5(ISAd, first, last, limit, trlink); } } continue; } if((last - first) <= TR_INSERTIONSORT_THRESHOLD) { tr_insertionsort(ISAd, first, last); limit = -3; continue; } if(limit-- == 0) { tr_heapsort(ISAd, first, last - first); for(a = last - 1; first < a; a = b) { for(x = ISAd[*a], b = a - 1; (first <= b) && (ISAd[*b] == x); --b) { *b = ~*b; } } limit = -3; continue; } /* choose pivot */ a = tr_pivot(ISAd, first, last); SWAP(*first, *a); v = ISAd[*first]; /* partition */ tr_partition(ISAd, first, first + 1, last, &a, &b, v); if((last - first) != (b - a)) { next = (ISA[*a] != v) ? tr_ilg(b - a) : -1; /* update ranks */ for(c = first, v = a - SA - 1; c < a; ++c) { ISA[*c] = v; } if(b < last) { for(c = a, v = b - SA - 1; c < b; ++c) { ISA[*c] = v; } } /* push */ if((1 < (b - a)) && (trbudget_check(budget, b - a))) { if((a - first) <= (last - b)) { if((last - b) <= (b - a)) { if(1 < (a - first)) { STACK_PUSH5(ISAd + incr, a, b, next, trlink); STACK_PUSH5(ISAd, b, last, limit, trlink); last = a; } else if(1 < (last - b)) { STACK_PUSH5(ISAd + incr, a, b, next, trlink); first = b; } else { ISAd += incr, first = a, last = b, limit = next; } } else if((a - first) <= (b - a)) { if(1 < (a - first)) { STACK_PUSH5(ISAd, b, last, limit, trlink); STACK_PUSH5(ISAd + incr, a, b, next, trlink); last = a; } else { STACK_PUSH5(ISAd, b, last, limit, trlink); ISAd += incr, first = a, last = b, limit = next; } } else { STACK_PUSH5(ISAd, b, last, limit, trlink); STACK_PUSH5(ISAd, first, a, limit, trlink); ISAd += incr, first = a, last = b, limit = next; } } else { if((a - first) <= (b - a)) { if(1 < (last - b)) { STACK_PUSH5(ISAd + incr, a, b, next, trlink); STACK_PUSH5(ISAd, first, a, limit, trlink); first = b; } else if(1 < (a - first)) { STACK_PUSH5(ISAd + incr, a, b, next, trlink); last = a; } else { ISAd += incr, first = a, last = b, limit = next; } } else if((last - b) <= (b - a)) { if(1 < (last - b)) { STACK_PUSH5(ISAd, first, a, limit, trlink); STACK_PUSH5(ISAd + incr, a, b, next, trlink); first = b; } else { STACK_PUSH5(ISAd, first, a, limit, trlink); ISAd += incr, first = a, last = b, limit = next; } } else { STACK_PUSH5(ISAd, first, a, limit, trlink); STACK_PUSH5(ISAd, b, last, limit, trlink); ISAd += incr, first = a, last = b, limit = next; } } } else { if((1 < (b - a)) && (0 <= trlink)) { stack[trlink].d = -1; } if((a - first) <= (last - b)) { if(1 < (a - first)) { STACK_PUSH5(ISAd, b, last, limit, trlink); last = a; } else if(1 < (last - b)) { first = b; } else { STACK_POP5(ISAd, first, last, limit, trlink); } } else { if(1 < (last - b)) { STACK_PUSH5(ISAd, first, a, limit, trlink); first = b; } else if(1 < (a - first)) { last = a; } else { STACK_POP5(ISAd, first, last, limit, trlink); } } } } else { if(trbudget_check(budget, last - first)) { limit = tr_ilg(last - first), ISAd += incr; } else { if(0 <= trlink) { stack[trlink].d = -1; } STACK_POP5(ISAd, first, last, limit, trlink); } } } #undef STACK_SIZE } /*---------------------------------------------------------------------------*/ /* Tandem repeat sort */ static void trsort(int *ISA, int *SA, int n, int depth) { int *ISAd; int *first, *last; trbudget_t budget; int t, skip, unsorted; trbudget_init(&budget, tr_ilg(n) * 2 / 3, n); /* trbudget_init(&budget, tr_ilg(n) * 3 / 4, n); */ for(ISAd = ISA + depth; -n < *SA; ISAd += ISAd - ISA) { first = SA; skip = 0; unsorted = 0; do { if((t = *first) < 0) { first -= t; skip += t; } else { if(skip != 0) { *(first + skip) = skip; skip = 0; } last = SA + ISA[t] + 1; if(1 < (last - first)) { budget.count = 0; tr_introsort(ISA, ISAd, SA, first, last, &budget); if(budget.count != 0) { unsorted += budget.count; } else { skip = first - last; } } else if((last - first) == 1) { skip = -1; } first = last; } } while(first < (SA + n)); if(skip != 0) { *(first + skip) = skip; } if(unsorted == 0) { break; } } } /*---------------------------------------------------------------------------*/ /* Sorts suffixes of type B*. */ static int sort_typeBstar(const unsigned char *T, int *SA, int *bucket_A, int *bucket_B, int n) { int *PAb, *ISAb, *buf; #ifdef _OPENMP int *curbuf; int l; #endif int i, j, k, t, m, bufsize; int c0, c1; #ifdef _OPENMP int d0, d1; int tmp; #endif /* Initialize bucket arrays. */ for(i = 0; i < BUCKET_A_SIZE; ++i) { bucket_A[i] = 0; } for(i = 0; i < BUCKET_B_SIZE; ++i) { bucket_B[i] = 0; } /* Count the number of occurrences of the first one or two characters of each type A, B and B* suffix. Moreover, store the beginning position of all type B* suffixes into the array SA. */ for(i = n - 1, m = n, c0 = T[n - 1]; 0 <= i;) { /* type A suffix. */ do { ++BUCKET_A(c1 = c0); } while((0 <= --i) && ((c0 = T[i]) >= c1)); if(0 <= i) { /* type B* suffix. */ ++BUCKET_BSTAR(c0, c1); SA[--m] = i; /* type B suffix. */ for(--i, c1 = c0; (0 <= i) && ((c0 = T[i]) <= c1); --i, c1 = c0) { ++BUCKET_B(c0, c1); } } } m = n - m; /* note: A type B* suffix is lexicographically smaller than a type B suffix that begins with the same first two characters. */ /* Calculate the index of start/end point of each bucket. */ for(c0 = 0, i = 0, j = 0; c0 < ALPHABET_SIZE; ++c0) { t = i + BUCKET_A(c0); BUCKET_A(c0) = i + j; /* start point */ i = t + BUCKET_B(c0, c0); for(c1 = c0 + 1; c1 < ALPHABET_SIZE; ++c1) { j += BUCKET_BSTAR(c0, c1); BUCKET_BSTAR(c0, c1) = j; /* end point */ i += BUCKET_B(c0, c1); } } if(0 < m) { /* Sort the type B* suffixes by their first two characters. */ PAb = SA + n - m; ISAb = SA + m; for(i = m - 2; 0 <= i; --i) { t = PAb[i], c0 = T[t], c1 = T[t + 1]; SA[--BUCKET_BSTAR(c0, c1)] = i; } t = PAb[m - 1], c0 = T[t], c1 = T[t + 1]; SA[--BUCKET_BSTAR(c0, c1)] = m - 1; /* Sort the type B* substrings using sssort. */ #ifdef _OPENMP tmp = omp_get_max_threads(); buf = SA + m, bufsize = (n - (2 * m)) / tmp; c0 = ALPHABET_SIZE - 2, c1 = ALPHABET_SIZE - 1, j = m; #pragma omp parallel default(shared) private(curbuf, k, l, d0, d1, tmp) { tmp = omp_get_thread_num(); curbuf = buf + tmp * bufsize; k = 0; for(;;) { #pragma omp critical(sssort_lock) { if(0 < (l = j)) { d0 = c0, d1 = c1; do { k = BUCKET_BSTAR(d0, d1); if(--d1 <= d0) { d1 = ALPHABET_SIZE - 1; if(--d0 < 0) { break; } } } while(((l - k) <= 1) && (0 < (l = k))); c0 = d0, c1 = d1, j = k; } } if(l == 0) { break; } sssort(T, PAb, SA + k, SA + l, curbuf, bufsize, 2, n, *(SA + k) == (m - 1)); } } #else buf = SA + m, bufsize = n - (2 * m); for(c0 = ALPHABET_SIZE - 2, j = m; 0 < j; --c0) { for(c1 = ALPHABET_SIZE - 1; c0 < c1; j = i, --c1) { i = BUCKET_BSTAR(c0, c1); if(1 < (j - i)) { sssort(T, PAb, SA + i, SA + j, buf, bufsize, 2, n, *(SA + i) == (m - 1)); } } } #endif /* Compute ranks of type B* substrings. */ for(i = m - 1; 0 <= i; --i) { if(0 <= SA[i]) { j = i; do { ISAb[SA[i]] = i; } while((0 <= --i) && (0 <= SA[i])); SA[i + 1] = i - j; if(i <= 0) { break; } } j = i; do { ISAb[SA[i] = ~SA[i]] = j; } while(SA[--i] < 0); ISAb[SA[i]] = j; } /* Construct the inverse suffix array of type B* suffixes using trsort. */ trsort(ISAb, SA, m, 1); /* Set the sorted order of tyoe B* suffixes. */ for(i = n - 1, j = m, c0 = T[n - 1]; 0 <= i;) { for(--i, c1 = c0; (0 <= i) && ((c0 = T[i]) >= c1); --i, c1 = c0) { } if(0 <= i) { t = i; for(--i, c1 = c0; (0 <= i) && ((c0 = T[i]) <= c1); --i, c1 = c0) { } SA[ISAb[--j]] = ((t == 0) || (1 < (t - i))) ? t : ~t; } } /* Calculate the index of start/end point of each bucket. */ BUCKET_B(ALPHABET_SIZE - 1, ALPHABET_SIZE - 1) = n; /* end point */ for(c0 = ALPHABET_SIZE - 2, k = m - 1; 0 <= c0; --c0) { i = BUCKET_A(c0 + 1) - 1; for(c1 = ALPHABET_SIZE - 1; c0 < c1; --c1) { t = i - BUCKET_B(c0, c1); BUCKET_B(c0, c1) = i; /* end point */ /* Move all type B* suffixes to the correct position. */ for(i = t, j = BUCKET_BSTAR(c0, c1); j <= k; --i, --k) { SA[i] = SA[k]; } } BUCKET_BSTAR(c0, c0 + 1) = i - BUCKET_B(c0, c0) + 1; /* start point */ BUCKET_B(c0, c0) = i; /* end point */ } } return m; } /* Constructs the suffix array by using the sorted order of type B* suffixes. */ static void construct_SA(const unsigned char *T, int *SA, int *bucket_A, int *bucket_B, int n, int m) { int *i, *j, *k; int s; int c0, c1, c2; if(0 < m) { /* Construct the sorted order of type B suffixes by using the sorted order of type B* suffixes. */ for(c1 = ALPHABET_SIZE - 2; 0 <= c1; --c1) { /* Scan the suffix array from right to left. */ for(i = SA + BUCKET_BSTAR(c1, c1 + 1), j = SA + BUCKET_A(c1 + 1) - 1, k = NULL, c2 = -1; i <= j; --j) { if(0 < (s = *j)) { assert(T[s] == c1); assert(((s + 1) < n) && (T[s] <= T[s + 1])); assert(T[s - 1] <= T[s]); *j = ~s; c0 = T[--s]; if((0 < s) && (T[s - 1] > c0)) { s = ~s; } if(c0 != c2) { if(0 <= c2) { BUCKET_B(c2, c1) = k - SA; } k = SA + BUCKET_B(c2 = c0, c1); } assert(k < j); *k-- = s; } else { assert(((s == 0) && (T[s] == c1)) || (s < 0)); *j = ~s; } } } } /* Construct the suffix array by using the sorted order of type B suffixes. */ k = SA + BUCKET_A(c2 = T[n - 1]); *k++ = (T[n - 2] < c2) ? ~(n - 1) : (n - 1); /* Scan the suffix array from left to right. */ for(i = SA, j = SA + n; i < j; ++i) { if(0 < (s = *i)) { assert(T[s - 1] >= T[s]); c0 = T[--s]; if((s == 0) || (T[s - 1] < c0)) { s = ~s; } if(c0 != c2) { BUCKET_A(c2) = k - SA; k = SA + BUCKET_A(c2 = c0); } assert(i < k); *k++ = s; } else { assert(s < 0); *i = ~s; } } } /* Constructs the burrows-wheeler transformed string directly by using the sorted order of type B* suffixes. */ static int construct_BWT(const unsigned char *T, int *SA, int *bucket_A, int *bucket_B, int n, int m) { int *i, *j, *k, *orig; int s; int c0, c1, c2; if(0 < m) { /* Construct the sorted order of type B suffixes by using the sorted order of type B* suffixes. */ for(c1 = ALPHABET_SIZE - 2; 0 <= c1; --c1) { /* Scan the suffix array from right to left. */ for(i = SA + BUCKET_BSTAR(c1, c1 + 1), j = SA + BUCKET_A(c1 + 1) - 1, k = NULL, c2 = -1; i <= j; --j) { if(0 < (s = *j)) { assert(T[s] == c1); assert(((s + 1) < n) && (T[s] <= T[s + 1])); assert(T[s - 1] <= T[s]); c0 = T[--s]; *j = ~((int)c0); if((0 < s) && (T[s - 1] > c0)) { s = ~s; } if(c0 != c2) { if(0 <= c2) { BUCKET_B(c2, c1) = k - SA; } k = SA + BUCKET_B(c2 = c0, c1); } assert(k < j); *k-- = s; } else if(s != 0) { *j = ~s; #ifndef NDEBUG } else { assert(T[s] == c1); #endif } } } } /* Construct the BWTed string by using the sorted order of type B suffixes. */ k = SA + BUCKET_A(c2 = T[n - 1]); *k++ = (T[n - 2] < c2) ? ~((int)T[n - 2]) : (n - 1); /* Scan the suffix array from left to right. */ for(i = SA, j = SA + n, orig = SA; i < j; ++i) { if(0 < (s = *i)) { assert(T[s - 1] >= T[s]); c0 = T[--s]; *i = c0; if((0 < s) && (T[s - 1] < c0)) { s = ~((int)T[s - 1]); } if(c0 != c2) { BUCKET_A(c2) = k - SA; k = SA + BUCKET_A(c2 = c0); } assert(i < k); *k++ = s; } else if(s != 0) { *i = ~s; } else { orig = i; } } return orig - SA; } /*---------------------------------------------------------------------------*/ /*- Function -*/ int bcm_divsufsort(const unsigned char *T, int *SA, int n) { int *bucket_A, *bucket_B; int m; int err = 0; /* Check arguments. */ if((T == NULL) || (SA == NULL) || (n < 0)) { return -1; } else if(n == 0) { return 0; } else if(n == 1) { SA[0] = 0; return 0; } else if(n == 2) { m = (T[0] < T[1]); SA[m ^ 1] = 0, SA[m] = 1; return 0; } bucket_A = (int *)malloc(BUCKET_A_SIZE * sizeof(int)); bucket_B = (int *)malloc(BUCKET_B_SIZE * sizeof(int)); /* Suffixsort. */ if((bucket_A != NULL) && (bucket_B != NULL)) { m = sort_typeBstar(T, SA, bucket_A, bucket_B, n); construct_SA(T, SA, bucket_A, bucket_B, n, m); } else { err = -2; } free(bucket_B); free(bucket_A); return err; } int bcm_divbwt(const unsigned char *T, unsigned char *U, int *A, int n) { int *B; int *bucket_A, *bucket_B; int m, pidx, i; /* Check arguments. */ if((T == NULL) || (U == NULL) || (n < 0)) { return -1; } else if(n <= 1) { if(n == 1) { U[0] = T[0]; } return n; } if((B = A) == NULL) { B = (int *)malloc((size_t)(n + 1) * sizeof(int)); } bucket_A = (int *)malloc(BUCKET_A_SIZE * sizeof(int)); bucket_B = (int *)malloc(BUCKET_B_SIZE * sizeof(int)); /* Burrows-Wheeler Transform. */ if((B != NULL) && (bucket_A != NULL) && (bucket_B != NULL)) { m = sort_typeBstar(T, B, bucket_A, bucket_B, n); pidx = construct_BWT(T, B, bucket_A, bucket_B, n, m); /* Copy to output string. */ U[0] = T[n - 1]; for(i = 0; i < pidx; ++i) { U[i + 1] = (unsigned char)B[i]; } for(i += 1; i < n; ++i) { U[i] = (unsigned char)B[i]; } pidx += 1; } else { pidx = -2; } free(bucket_B); free(bucket_A); if(A == NULL) { free(B); } return pidx; } #endif // BCM_C //#line 1 "amalgamated_bcm.c" // BCM 1.40 - A BWT-based file compressor // Written and placed in the public domain by Ilya Muravyov (UNLICENSE) // Additional code by @r-lyeh (UNLICENSE) // // Notes: // - BCM decoder has no dependencies. // - BCM encoder requires libdivsufsort, which is MIT licensed. // - #define BCM_NO_ENCODER if you want to exclude libdivsufsort from linkage. unsigned bcm_encode(const void *in, unsigned inlen, void *out, unsigned outlen, unsigned flags/*[0..(4)..9]*/); unsigned bcm_decode(const void *in, unsigned inlen, void *out, unsigned outlen); unsigned bcm_bounds(unsigned inlen, unsigned flags); unsigned bcm_excess(unsigned flags); // --- #ifdef BCM_C //#pragma once #include #include #include #include #if INTPTR_MAX >= INT64_MAX #define BCM_64BITS 1 #else #define BCM_64BITS 0 #endif #ifndef BCM_REALLOC #define BCM_REALLOC REALLOC #endif # if defined _MSC_VER && !defined __thread #define __thread __declspec(thread) #elif defined __TINYC__ && !defined __thread #define __thread __declspec(thread) #endif #ifndef BALZ_C typedef struct mfile { uint8_t *begin, *seek, *end; } mfile; int minit(mfile *f, const void *ptr, int len) { f->begin = f->seek = f->end = (uint8_t*)ptr; f->end += len; return 0; } int mread(mfile *m, void *buf, int len) { if( len >= (m->end - m->seek) ) len = (m->end - m->seek); memcpy(buf,m->seek,len); m->seek += len; return len; } int mwrite(mfile *m, const void *buf, int len) { if( len >= (m->end - m->seek) ) len = (m->end - m->seek); memcpy(m->seek,buf,len); m->seek += len; return len; } int mtell(mfile *m) { return m->seek - m->begin; } int mavail(mfile *m) { return m->end - m->seek; } int mputc(mfile *m, int i) { uint8_t ch = i; return mwrite(m, &ch, 1); } int mgetc(mfile *m) { if( mavail(m) <= 0 ) return -1; uint8_t ch; mread(m, &ch, 1); return ch; } #endif int bcm_divbwt(const unsigned char *T, unsigned char *U, int *A, int n); // Globals static __thread mfile* g_in; static __thread mfile* g_out; typedef struct bcmEncode { uint32_t low; uint32_t high; uint32_t code; } bcmEncoder; void bcmeCtor(bcmEncoder *e) { e->low=0; e->high=0xFFFFFFFF; e->code=0; } void bcmeFlush(bcmEncoder *e) { for (int i=0; i<4; ++i) { mputc(g_out, e->low>>24); e->low<<=8; } } void bcmeInit(bcmEncoder *e) { for (int i=0; i<4; ++i) e->code=(e->code<<8)+mgetc(g_in); } void bcmeEncodeDirectBits(bcmEncoder *e, int N, uint32_t x) { for (uint32_t i=1<<(N-1); i!=0; i>>=1) { if (x&i) e->high=e->low+((e->high-e->low)>>1); else e->low+=((e->high-e->low)>>1)+1; if ((e->low^e->high)<(1<<24)) { mputc(g_out, e->low>>24); e->low<<=8; e->high=(e->high<<8)+255; } } } void bcmeEncodeBit1(bcmEncoder *e, uint32_t p) { #if BCM_64BITS e->high=e->low+(((uint64_t)(e->high-e->low)*p)>>18); #else e->high=e->low+(((uint64_t)(e->high-e->low)*(p<<(32-18)))>>32); #endif while ((e->low^e->high)<(1<<24)) { mputc(g_out, e->low>>24); e->low<<=8; e->high=(e->high<<8)+255; } } void bcmeEncodeBit0(bcmEncoder *e, uint32_t p) { #if BCM_64BITS e->low+=(((uint64_t)(e->high-e->low)*p)>>18)+1; #else e->low+=(((uint64_t)(e->high-e->low)*(p<<(32-18)))>>32)+1; #endif while ((e->low^e->high)<(1<<24)) { mputc(g_out, e->low>>24); e->low<<=8; e->high=(e->high<<8)+255; } } uint32_t bcmeDecodeDirectBits(bcmEncoder *e, int N) { uint32_t x=0; for (int i=0; ilow+((e->high-e->low)>>1); if (e->code<=mid) { e->high=mid; x+=x+1; } else { e->low=mid+1; x+=x; } if ((e->low^e->high)<(1<<24)) { e->low<<=8; e->high=(e->high<<8)+255; e->code=(e->code<<8)+mgetc(g_in); } } return x; } int bcmeDecodeBit(bcmEncoder *e, uint32_t p) { #if BCM_64BITS const uint32_t mid=e->low+(((uint64_t)(e->high-e->low)*p)>>18); #else const uint32_t mid=e->low+(((uint64_t)(e->high-e->low)*(p<<(32-18)))>>32); #endif const int bit=(e->code<=mid); if (bit) e->high=mid; else e->low=mid+1; while ((e->low^e->high)<(1<<24)) { e->low<<=8; e->high=(e->high<<8)+255; e->code=(e->code<<8)+mgetc(g_in); } return bit; } #define BCM_COUNTER_TEMPLATE(RATE) \ typedef struct bcmCounter##RATE { uint16_t p; } bcmCounter##RATE; \ void bcmCounter##RATE##Ctor(bcmCounter##RATE *c) { c->p=1<<15; /* 0.5 */ } \ void bcmCounter##RATE##UpdateBit0(bcmCounter##RATE *c) { c->p-=c->p>>RATE; } \ void bcmCounter##RATE##UpdateBit1(bcmCounter##RATE *c) { c->p+=(c->p^0xFFFF)>>RATE; } BCM_COUNTER_TEMPLATE(2); BCM_COUNTER_TEMPLATE(4); BCM_COUNTER_TEMPLATE(6); typedef struct bcmCM { bcmEncoder enc; bcmCounter2 counter0[256]; bcmCounter4 counter1[256][256]; bcmCounter6 counter2[2][256][17]; int c1; int c2; int run; } bcmCM; void bcmCMCtor(bcmCM *c) { bcmeCtor(&c->enc); for(int i = 0; i < 256; ++i) { bcmCounter2Ctor(&c->counter0[i]); for(int j = 0; j < 256; ++j) { bcmCounter4Ctor(&c->counter1[i][j]); } for(int k = 0; k < 17; ++k) { bcmCounter6Ctor(&c->counter2[0][i][k]); bcmCounter6Ctor(&c->counter2[1][i][k]); } } c->c1=0; c->c2=0; c->run=0; for (int i=0; i<2; ++i) { for (int j=0; j<256; ++j) { for (int k=0; k<17; ++k) c->counter2[i][j][k].p=(k<<12)-(k==16); } } } void bcmCMEncode(bcmCM *c, int ch) { if (c->c1==c->c2) ++c->run; else c->run=0; const int f=(c->run>2); int ctx=1; while (ctx<256) { const int p0=c->counter0[ctx].p; const int p1=c->counter1[c->c1][ctx].p; const int p2=c->counter1[c->c2][ctx].p; const int p=(((p0+p1)*7)+p2+p2)>>4; const int j=p>>12; const int x1=c->counter2[f][ctx][j].p; const int x2=c->counter2[f][ctx][j+1].p; const int ssep=x1+(((x2-x1)*(p&4095))>>12); if (ch&128) { bcmeEncodeBit1(&c->enc, (ssep*3)+p); bcmCounter2UpdateBit1(&c->counter0[ctx]); bcmCounter4UpdateBit1(&c->counter1[c->c1][ctx]); bcmCounter6UpdateBit1(&c->counter2[f][ctx][j]); bcmCounter6UpdateBit1(&c->counter2[f][ctx][j+1]); ctx+=ctx+1; } else { bcmeEncodeBit0(&c->enc, (ssep*3)+p); bcmCounter2UpdateBit0(&c->counter0[ctx]); bcmCounter4UpdateBit0(&c->counter1[c->c1][ctx]); bcmCounter6UpdateBit0(&c->counter2[f][ctx][j]); bcmCounter6UpdateBit0(&c->counter2[f][ctx][j+1]); ctx+=ctx; } ch+=ch; } c->c2=c->c1; c->c1=ctx-256; } int bcmCMDecode(bcmCM *c) { if (c->c1==c->c2) ++c->run; else c->run=0; const int f=(c->run>2); int ctx=1; while (ctx<256) { const int p0=c->counter0[ctx].p; const int p1=c->counter1[c->c1][ctx].p; const int p2=c->counter1[c->c2][ctx].p; const int p=(((p0+p1)*7)+p2+p2)>>4; const int j=p>>12; const int x1=c->counter2[f][ctx][j].p; const int x2=c->counter2[f][ctx][j+1].p; const int ssep=x1+(((x2-x1)*(p&4095))>>12); if (bcmeDecodeBit(&c->enc, (ssep*3)+p)) { bcmCounter2UpdateBit1(&c->counter0[ctx]); bcmCounter4UpdateBit1(&c->counter1[c->c1][ctx]); bcmCounter6UpdateBit1(&c->counter2[f][ctx][j]); bcmCounter6UpdateBit1(&c->counter2[f][ctx][j+1]); ctx+=ctx+1; } else { bcmCounter2UpdateBit0(&c->counter0[ctx]); bcmCounter4UpdateBit0(&c->counter1[c->c1][ctx]); bcmCounter6UpdateBit0(&c->counter2[f][ctx][j]); bcmCounter6UpdateBit0(&c->counter2[f][ctx][j+1]); ctx+=ctx; } } c->c2=c->c1; return c->c1=ctx-256; } unsigned bcm_encode(const void *in, unsigned inlen, void *out, unsigned outlen, unsigned level) { mfile infile; minit(&infile, in, inlen); g_in = &infile; mfile outfile; minit(&outfile, out, outlen); g_out = &outfile; bcmCM cm; bcmCMCtor(&cm); const int config_tab[10]= { 1<<19, // -0 - 512KiB, @rlyeh: originally was: 0 1<<20, // -1 - 1 MB 1<<22, // -2 - 4 MB 1<<23, // -3 - 8 MB 0x00FFFFFF, // -4 - ~16 MB (Default) 1<<25, // -5 - 32 MB 1<<26, // -6 - 64 MB 1<<27, // -7 - 128 MB 1<<28, // -8 - 256 MB 0x7FFFFFFF, // -9 - ~2 GB }; int block_size=config_tab[level]; int64_t file_size = (int64_t)inlen; if (file_size>0 && block_size>file_size) block_size=(int)(file_size); uint8_t* buf=(uint8_t*)BCM_REALLOC(0, sizeof(uint8_t) * block_size); int* ptr=(int*)BCM_REALLOC(0, sizeof(int) * block_size); int n; while ((n=mread(g_in, buf, block_size))>0) { const int idx=bcm_divbwt(buf, buf, ptr, n); if (idx<1) return 0; // divbwt() failed bcmeEncodeDirectBits(&cm.enc, 32, n); bcmeEncodeDirectBits(&cm.enc, 32, idx); for (int i=0; i0) { if (block_size==0) { if ((block_size=n)>=(1<<24)) // 5*N buf=(uint8_t*)BCM_REALLOC(0, sizeof(uint8_t) * block_size); ptr=(uint32_t*)BCM_REALLOC(0, sizeof(uint32_t) * block_size); } const int idx=bcmeDecodeDirectBits(&cm.enc, 32); if (n>block_size || idx<1 || idx>n) return 0; // corrupt input // Inverse BW-transform if (n>=(1<<24)) // 5*N { int t[257]={0}; for (int i=0; i=idx); for (int p=idx; p;) { p=ptr[p-1]; const int c=buf[p-(p>=idx)]; mputc(g_out, c); } } else // 4*N { int t[257]={0}; for (int i=0; i=idx))<<8; for (int p=idx; p;) { p=ptr[p-1]>>8; const int c=ptr[p-(p>=idx)]&255; mputc(g_out, c); } } } // if (bcmeDecodeDirectBits(&cm.enc, 32)!=crc32) return 0; // crc error BCM_REALLOC(buf, 0); // free BCM_REALLOC(ptr, 0); // free return mtell(g_out); } unsigned bcm_bounds(unsigned inlen, unsigned flags) { return (unsigned)(inlen * 2); // @todo: check src } unsigned bcm_excess(unsigned flags) { return (unsigned)0; } #endif // BCM_C //#line 1 "amalgamated_crush.c" // crush.cpp // Written and placed in the public domain by Ilya Muravyov // Additional code by @r-lyeh (public domain). @todo: honor unused args inlen/outlen unsigned crush_encode(const void* in, unsigned inlen, void* out, unsigned outlen, unsigned flags); // [0..(4)..10] unsigned crush_decode(const void* in, unsigned inlen, void* out, unsigned outlen); unsigned crush_bounds(unsigned inlen, unsigned flags); unsigned crush_excess(unsigned flags); #ifdef CRUSH_C //#pragma once #ifdef _MSC_VER #define _CRT_SECECURE_NO_WARNINGS #define _CRT_DISABLE_PERFCRIT_LOCKS #endif #include #include // Bit I/O // typedef struct bits { const uint8_t* g_inbuf; uint8_t* g_outbuf; int g_inbuf_pos; int g_outbuf_pos; int bit_buf; int bit_count; } bits; void bits_init(bits *b, const uint8_t* inbuf, uint8_t* outbuf) { b->bit_count=b->bit_buf=b->g_inbuf_pos=b->g_outbuf_pos=0; b->g_inbuf = inbuf; b->g_outbuf = outbuf; } void bits_put(bits *b, int n, int x) { b->bit_buf|=x<bit_count; b->bit_count+=n; while (b->bit_count>=8) { b->g_outbuf[b->g_outbuf_pos++] = b->bit_buf; b->bit_buf>>=8; b->bit_count-=8; } } void bits_flush(bits *b) { bits_put(b, 7, 0); b->bit_count=b->bit_buf=0; } int bits_get(bits *b, int n) { while (b->bit_countbit_buf|=b->g_inbuf[b->g_inbuf_pos++]<bit_count; b->bit_count+=8; } const int x=b->bit_buf&((1<bit_buf>>=n; b->bit_count-=n; return x; } // LZ77 // enum { W_BITS=21 }; // Window size (17..23) enum { W_SIZE=1<b?a:b; } static inline int get_penalty(int a, int b) { int p=0; while (a>b) { a>>=3; ++p; } return p; } static size_t crush_compress(const uint8_t* buf, size_t size, uint8_t* outbuf, size_t outlen, size_t level) { static int head[HASH1_SIZE+HASH2_SIZE]; static int prev[W_SIZE]; //const int max_chain[]={4, 256, 1<<12}; // original [0fast..2uber] const int max_chain[11] = { 0, 1, 2, 4, 8, 16, 32, 64, 128, 256, 1<<12 }; //[0fastest..10uber] const int max_level = sizeof(max_chain)/sizeof(max_chain[0]); level = level > max_level ? max_level : level; bits bits; { for (int i=0; i=limit) { int s=head[h1]; if (buf[s]==buf[p]) { int l=0; while (++llen) { len=l; offset=p-s; } } } if (len=limit)) { if ((buf[s+len]==buf[p+len])&&(buf[s]==buf[p])) { int l=0; while (++llen+get_penalty((p-s)>>4, offset)) { len=l; offset=p-s; } if (l==max_match) break; } s=prev[s&W_MASK]; } } if ((len==MIN_MATCH)&&(offset>TOO_FAR)) len=0; if ((level>=2)&&(len>=MIN_MATCH)&&(len=limit)) { if ((buf[s+len]==buf[next_p+len])&&(buf[s]==buf[next_p])) { int l=0; while (++llen+get_penalty(next_p-s, offset)) { len=0; break; } if (l==max_lazy) break; } s=prev[s&W_MASK]; } } if (len>=MIN_MATCH) // Match { bits_put(&bits, 1, 1); const int l=len-MIN_MATCH; if (l=(2<(W_BITS-NUM_SLOTS)) bits_put(&bits, log, offset-(1<(W_BITS-NUM_SLOTS) ?bits_get(&bits, log)+(1< 10 ? 10 : flags; return crush_compress((const uint8_t*)in, (size_t)inlen, (uint8_t*)out, (size_t)outlen, (size_t)level); } unsigned crush_decode(const void* in, unsigned inlen, void* out, unsigned outlen) { return crush_decompress((const uint8_t*)in, (size_t)inlen, (uint8_t*)out, (size_t)outlen); } unsigned crush_bounds(unsigned inlen, unsigned flags) { return (unsigned)(inlen * 1.1) + 16; // @todo: check src } unsigned crush_excess(unsigned flags) { return (unsigned)0; } #endif // CRUSH_C #ifdef CRUSH_DEMO //#pragma once int main() { const char *longcopy = "Hello world! Hello world! Hello world! Hello world!"; int level = 1; char out[128]; size_t outlen = crush_encode(longcopy, strlen(longcopy)+1, out, 128, level ); printf("%s %d->%d\n", outlen ? "ok" : "fail", (int)strlen(longcopy)+1, (int)outlen); char redo[128]; size_t unpacked = crush_decode(out, outlen, redo, 128); printf("%d->%d %s\n", (int)outlen, (int)unpacked, redo); } #define main main__ #endif // CRUSH_DEMO //#line 1 "amalgamated_deflate.c" // miniz.c v1.15 r4 - public domain de/inflate. See "unlicense" statement at http://unlicense.org/ // Rich Geldreich , last updated Oct. 13, 2013. Then stripped down by @r-lyeh. // Implements RFC 1950: http://www.ietf.org/rfc/rfc1950.txt and RFC 1951: http://www.ietf.org/rfc/rfc1951.txt // without zlib headers unsigned deflate_encode(const void *in, unsigned inlen, void *out, unsigned outlen, unsigned flags); // [0..(6)..9][10 (uber)] unsigned deflate_decode(const void *in, unsigned inlen, void *out, unsigned outlen); // with zlib headers unsigned deflatez_encode(const void *in, unsigned inlen, void *out, unsigned outlen, unsigned flags); // [0..(6)..9][10 (uber)] unsigned deflatez_decode(const void *in, unsigned inlen, void *out, unsigned outlen); // both options unsigned deflate_bounds(unsigned inlen, unsigned flags); unsigned deflate_excess(unsigned flags); #ifdef DEFLATE_C //#pragma once #include // assert() #include // types #include // realloc() #include // Set to 1 on CPU's that permit efficient integer loads and stores from unaligned addresses. #ifndef MINIZ_USE_UNALIGNED_LOADS_AND_STORES #if defined(_M_X64) || defined(_M_IX86) #define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 1 #else #define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 0 #endif #endif // Set to 1 if the processor is little endian. #ifndef MINIZ_LITTLE_ENDIAN #if defined(_M_X64) || defined(_M_IX86) || __BYTE_ORDER__==__ORDER_LITTLE_ENDIAN__ #define MINIZ_LITTLE_ENDIAN 1 #else #define MINIZ_LITTLE_ENDIAN 0 #endif #endif // Set to 1 if operations on 64-bit integers are reasonably fast (and don't involve compiler generated calls to helper functions). #ifndef MINIZ_HAS_64BIT_REGISTERS #if UINTPTR_MAX > 0xffffffff // defined(_M_X64) || defined(_WIN64) || defined(__MINGW64__) || defined(_LP64) || defined(__LP64__) || defined(__ia64__) || defined(__x86_64__) #define MINIZ_HAS_64BIT_REGISTERS 1 #else #define MINIZ_HAS_64BIT_REGISTERS 0 #endif #endif // ------------------- Types and macros typedef uint32_t mz_uint; // An attempt to work around MSVC's spammy "warning C4127: conditional expression is constant" message. #ifdef _MSC_VER #define MZ_MACRO_END while (0, 0) #else #define MZ_MACRO_END while (0) #endif #define MZ_ASSERT(x) assert(x) #define MZ_MAX(a,b) (((a)>(b))?(a):(b)) #define MZ_MIN(a,b) (((a)<(b))?(a):(b)) #define MZ_CLEAR_OBJ(obj) memset(&(obj), 0, sizeof(obj)) #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN #define MZ_READ_LE16(p) *((const uint16_t *)(p)) #define MZ_READ_LE32(p) *((const uint32_t *)(p)) #else #define MZ_READ_LE16(p) ((uint32_t)(((const uint8_t *)(p))[0]) | ((uint32_t)(((const uint8_t *)(p))[1]) << 8U)) #define MZ_READ_LE32(p) ((uint32_t)(((const uint8_t *)(p))[0]) | ((uint32_t)(((const uint8_t *)(p))[1]) << 8U) | ((uint32_t)(((const uint8_t *)(p))[2]) << 16U) | ((uint32_t)(((const uint8_t *)(p))[3]) << 24U)) #endif // Return status. typedef enum { TINFL_STATUS_BAD_PARAM = -3, TINFL_STATUS_ADLER32_MISMATCH = -2, TINFL_STATUS_FAILED = -1, TINFL_STATUS_DONE = 0, TINFL_STATUS_NEEDS_MORE_INPUT = 1, TINFL_STATUS_HAS_MORE_OUTPUT = 2 } tinfl_status; struct tinfl_decompressor_tag; typedef struct tinfl_decompressor_tag tinfl_decompressor; // ------------------- Low-level Decompression (completely independent from all compression API's) // Decompression flags used by tinfl_decompress(). // TINFL_FLAG_PARSE_ZLIB_HEADER: If set, the input has a valid zlib header and ends with an adler32 checksum (it's a valid zlib stream). Otherwise, the input is a raw deflate stream. // TINFL_FLAG_HAS_MORE_INPUT: If set, there are more input bytes available beyond the end of the supplied input buffer. If clear, the input buffer contains all remaining input. // TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF: If set, the output buffer is large enough to hold the entire decompressed stream. If clear, the output buffer is at least the size of the dictionary (typically 32KB). // TINFL_FLAG_COMPUTE_ADLER32: Force adler-32 checksum computation of the decompressed bytes. enum { TINFL_FLAG_PARSE_ZLIB_HEADER = 1, TINFL_FLAG_HAS_MORE_INPUT = 2, TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF = 4, TINFL_FLAG_COMPUTE_ADLER32 = 8 }; #define TINFL_MEMCPY memcpy #define TINFL_MEMSET memset #define TINFL_CR_BEGIN switch(r->m_state) { case 0: #define TINFL_CR_RETURN(state_index, result) do { status = result; r->m_state = state_index; /*printf("L%d\n", __LINE__);*/ goto common_exit; case state_index:; } MZ_MACRO_END #define TINFL_CR_RETURN_FOREVER(state_index, result) do { for ( ; ; ) { TINFL_CR_RETURN(state_index, result); } } MZ_MACRO_END #define TINFL_CR_FINISH } // TODO: If the caller has indicated that there's no more input, and we attempt to read beyond the input buf, then something is wrong with the input because the inflator never // reads ahead more than it needs to. Currently TINFL_GET_BYTE() pads the end of the stream with 0's in this scenario. #define TINFL_GET_BYTE(state_index, c) do { \ if (pIn_buf_cur >= pIn_buf_end) { \ for ( ; ; ) { \ if (decomp_flags & TINFL_FLAG_HAS_MORE_INPUT) { \ TINFL_CR_RETURN(state_index, TINFL_STATUS_NEEDS_MORE_INPUT); \ if (pIn_buf_cur < pIn_buf_end) { \ c = *pIn_buf_cur++; \ break; \ } \ } else { \ c = 0; \ break; \ } \ } \ } else c = *pIn_buf_cur++; } MZ_MACRO_END #define TINFL_NEED_BITS(state_index, n) do { mz_uint c; TINFL_GET_BYTE(state_index, c); bit_buf |= (((tinfl_bit_buf_t)c) << num_bits); num_bits += 8; } while (num_bits < (mz_uint)(n)) #define TINFL_SKIP_BITS(state_index, n) do { if (num_bits < (mz_uint)(n)) { TINFL_NEED_BITS(state_index, n); } bit_buf >>= (n); num_bits -= (n); } MZ_MACRO_END #define TINFL_GET_BITS(state_index, b, n) do { if (num_bits < (mz_uint)(n)) { TINFL_NEED_BITS(state_index, n); } b = bit_buf & ((1 << (n)) - 1); bit_buf >>= (n); num_bits -= (n); } MZ_MACRO_END // TINFL_HUFF_BITBUF_FILL() is only used rarely, when the number of bytes remaining in the input buffer falls below 2. // It reads just enough bytes from the input stream that are needed to decode the next Huffman code (and absolutely no more). It works by trying to fully decode a // Huffman code by using whatever bits are currently present in the bit buffer. If this fails, it reads another byte, and tries again until it succeeds or until the // bit buffer contains >=15 bits (deflate's max. Huffman code size). #define TINFL_HUFF_BITBUF_FILL(state_index, pHuff) \ do { \ temp = (pHuff)->m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]; \ if (temp >= 0) { \ code_len = temp >> 9; \ if ((code_len) && (num_bits >= code_len)) \ break; \ } else if (num_bits > TINFL_FAST_LOOKUP_BITS) { \ code_len = TINFL_FAST_LOOKUP_BITS; \ do { \ temp = (pHuff)->m_tree[~temp + ((bit_buf >> code_len++) & 1)]; \ } while ((temp < 0) && (num_bits >= (code_len + 1))); if (temp >= 0) break; \ } TINFL_GET_BYTE(state_index, c); bit_buf |= (((tinfl_bit_buf_t)c) << num_bits); num_bits += 8; \ } while (num_bits < 15); // TINFL_HUFF_DECODE() decodes the next Huffman coded symbol. It's more complex than you would initially expect because the zlib API expects the decompressor to never read // beyond the final byte of the deflate stream. (In other words, when this macro wants to read another byte from the input, it REALLY needs another byte in order to fully // decode the next Huffman code.) Handling this properly is particularly important on raw deflate (non-zlib) streams, which aren't followed by a byte aligned adler-32. // The slow path is only executed at the very end of the input buffer. #define TINFL_HUFF_DECODE(state_index, sym, pHuff) do { \ int temp; mz_uint code_len, c; \ if (num_bits < 15) { \ if ((pIn_buf_end - pIn_buf_cur) < 2) { \ TINFL_HUFF_BITBUF_FILL(state_index, pHuff); \ } else { \ bit_buf |= (((tinfl_bit_buf_t)pIn_buf_cur[0]) << num_bits) | (((tinfl_bit_buf_t)pIn_buf_cur[1]) << (num_bits + 8)); pIn_buf_cur += 2; num_bits += 16; \ } \ } \ if ((temp = (pHuff)->m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0) \ code_len = temp >> 9, temp &= 511; \ else { \ code_len = TINFL_FAST_LOOKUP_BITS; do { temp = (pHuff)->m_tree[~temp + ((bit_buf >> code_len++) & 1)]; } while (temp < 0); \ } sym = temp; bit_buf >>= code_len; num_bits -= code_len; } MZ_MACRO_END // Internal/private bits follow. enum { TINFL_MAX_HUFF_TABLES = 3, TINFL_MAX_HUFF_SYMBOLS_0 = 288, TINFL_MAX_HUFF_SYMBOLS_1 = 32, TINFL_MAX_HUFF_SYMBOLS_2 = 19, TINFL_FAST_LOOKUP_BITS = 10, TINFL_FAST_LOOKUP_SIZE = 1 << TINFL_FAST_LOOKUP_BITS }; typedef struct { uint8_t m_code_size[TINFL_MAX_HUFF_SYMBOLS_0]; int16_t m_look_up[TINFL_FAST_LOOKUP_SIZE], m_tree[TINFL_MAX_HUFF_SYMBOLS_0 * 2]; } tinfl_huff_table; #if MINIZ_HAS_64BIT_REGISTERS typedef uint64_t tinfl_bit_buf_t; #define TINFL_BITBUF_SIZE (64) #else typedef uint32_t tinfl_bit_buf_t; #define TINFL_BITBUF_SIZE (32) #endif struct tinfl_decompressor_tag { uint32_t m_state, m_num_bits, m_zhdr0, m_zhdr1, m_z_adler32, m_final, m_type, m_check_adler32, m_dist, m_counter, m_num_extra, m_table_sizes[TINFL_MAX_HUFF_TABLES]; tinfl_bit_buf_t m_bit_buf; size_t m_dist_from_out_buf_start; tinfl_huff_table m_tables[TINFL_MAX_HUFF_TABLES]; uint8_t m_raw_header[4], m_len_codes[TINFL_MAX_HUFF_SYMBOLS_0 + TINFL_MAX_HUFF_SYMBOLS_1 + 137]; }; tinfl_status tinfl_decompress(tinfl_decompressor *r, const uint8_t *pIn_buf_next, size_t *pIn_buf_size, uint8_t *pOut_buf_start, uint8_t *pOut_buf_next, size_t *pOut_buf_size, const uint32_t decomp_flags) { static const int s_length_base[31] = { 3,4,5,6,7,8,9,10,11,13, 15,17,19,23,27,31,35,43,51,59, 67,83,99,115,131,163,195,227,258,0,0 }; static const int s_length_extra[31]= { 0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0 }; static const int s_dist_base[32] = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193, 257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577,0,0}; static const int s_dist_extra[32] = { 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13}; static const uint8_t s_length_dezigzag[19] = { 16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15 }; static const int s_min_table_sizes[3] = { 257, 1, 4 }; tinfl_status status = TINFL_STATUS_FAILED; uint32_t num_bits, dist, counter, num_extra; tinfl_bit_buf_t bit_buf; const uint8_t *pIn_buf_cur = pIn_buf_next, *const pIn_buf_end = pIn_buf_next + *pIn_buf_size; uint8_t *pOut_buf_cur = pOut_buf_next, *const pOut_buf_end = pOut_buf_next + *pOut_buf_size; size_t out_buf_size_mask = (decomp_flags & TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF) ? (size_t)-1 : ((pOut_buf_next - pOut_buf_start) + *pOut_buf_size) - 1, dist_from_out_buf_start; // Ensure the output buffer's size is a power of 2, unless the output buffer is large enough to hold the entire output file (in which case it doesn't matter). if (((out_buf_size_mask + 1) & out_buf_size_mask) || (pOut_buf_next < pOut_buf_start)) { *pIn_buf_size = *pOut_buf_size = 0; return TINFL_STATUS_BAD_PARAM; } num_bits = r->m_num_bits; bit_buf = r->m_bit_buf; dist = r->m_dist; counter = r->m_counter; num_extra = r->m_num_extra; dist_from_out_buf_start = r->m_dist_from_out_buf_start; TINFL_CR_BEGIN bit_buf = num_bits = dist = counter = num_extra = r->m_zhdr0 = r->m_zhdr1 = 0; r->m_z_adler32 = r->m_check_adler32 = 1; if (decomp_flags & TINFL_FLAG_PARSE_ZLIB_HEADER) { TINFL_GET_BYTE(1, r->m_zhdr0); TINFL_GET_BYTE(2, r->m_zhdr1); counter = (((r->m_zhdr0 * 256 + r->m_zhdr1) % 31 != 0) || (r->m_zhdr1 & 32) || ((r->m_zhdr0 & 15) != 8)); if (!(decomp_flags & TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF)) counter |= (((1U << (8U + (r->m_zhdr0 >> 4))) > 32768U) || ((out_buf_size_mask + 1) < (size_t)(1U << (8U + (r->m_zhdr0 >> 4))))); if (counter) { TINFL_CR_RETURN_FOREVER(36, TINFL_STATUS_FAILED); } } do { TINFL_GET_BITS(3, r->m_final, 3); r->m_type = r->m_final >> 1; if (r->m_type == 0) { TINFL_SKIP_BITS(5, num_bits & 7); for (counter = 0; counter < 4; ++counter) { if (num_bits) TINFL_GET_BITS(6, r->m_raw_header[counter], 8); else TINFL_GET_BYTE(7, r->m_raw_header[counter]); } if ((counter = (r->m_raw_header[0] | (r->m_raw_header[1] << 8))) != (mz_uint)(0xFFFF ^ (r->m_raw_header[2] | (r->m_raw_header[3] << 8)))) { TINFL_CR_RETURN_FOREVER(39, TINFL_STATUS_FAILED); } while ((counter) && (num_bits)) { TINFL_GET_BITS(51, dist, 8); while (pOut_buf_cur >= pOut_buf_end) { TINFL_CR_RETURN(52, TINFL_STATUS_HAS_MORE_OUTPUT); } *pOut_buf_cur++ = (uint8_t)dist; counter--; } while (counter) { size_t n; while (pOut_buf_cur >= pOut_buf_end) { TINFL_CR_RETURN(9, TINFL_STATUS_HAS_MORE_OUTPUT); } while (pIn_buf_cur >= pIn_buf_end) { if (decomp_flags & TINFL_FLAG_HAS_MORE_INPUT) { TINFL_CR_RETURN(38, TINFL_STATUS_NEEDS_MORE_INPUT); } else { TINFL_CR_RETURN_FOREVER(40, TINFL_STATUS_FAILED); } } n = MZ_MIN(MZ_MIN((size_t)(pOut_buf_end - pOut_buf_cur), (size_t)(pIn_buf_end - pIn_buf_cur)), counter); TINFL_MEMCPY(pOut_buf_cur, pIn_buf_cur, n); pIn_buf_cur += n; pOut_buf_cur += n; counter -= (mz_uint)n; } } else if (r->m_type == 3) { TINFL_CR_RETURN_FOREVER(10, TINFL_STATUS_FAILED); } else { if (r->m_type == 1) { uint8_t *p = r->m_tables[0].m_code_size; mz_uint i; r->m_table_sizes[0] = 288; r->m_table_sizes[1] = 32; TINFL_MEMSET(r->m_tables[1].m_code_size, 5, 32); for ( i = 0; i <= 143; ++i) *p++ = 8; for ( ; i <= 255; ++i) *p++ = 9; for ( ; i <= 279; ++i) *p++ = 7; for ( ; i <= 287; ++i) *p++ = 8; } else { for (counter = 0; counter < 3; counter++) { TINFL_GET_BITS(11, r->m_table_sizes[counter], "\05\05\04"[counter]); r->m_table_sizes[counter] += s_min_table_sizes[counter]; } MZ_CLEAR_OBJ(r->m_tables[2].m_code_size); for (counter = 0; counter < r->m_table_sizes[2]; counter++) { mz_uint s; TINFL_GET_BITS(14, s, 3); r->m_tables[2].m_code_size[s_length_dezigzag[counter]] = (uint8_t)s; } r->m_table_sizes[2] = 19; } for ( ; (int)r->m_type >= 0; r->m_type--) { int tree_next, tree_cur; tinfl_huff_table *pTable; mz_uint i, j, used_syms, total, sym_index, next_code[17], total_syms[16]; pTable = &r->m_tables[r->m_type]; MZ_CLEAR_OBJ(total_syms); MZ_CLEAR_OBJ(pTable->m_look_up); MZ_CLEAR_OBJ(pTable->m_tree); for (i = 0; i < r->m_table_sizes[r->m_type]; ++i) total_syms[pTable->m_code_size[i]]++; used_syms = 0, total = 0; next_code[0] = next_code[1] = 0; for (i = 1; i <= 15; ++i) { used_syms += total_syms[i]; next_code[i + 1] = (total = ((total + total_syms[i]) << 1)); } if ((65536 != total) && (used_syms > 1)) { TINFL_CR_RETURN_FOREVER(35, TINFL_STATUS_FAILED); } for (tree_next = -1, sym_index = 0; sym_index < r->m_table_sizes[r->m_type]; ++sym_index) { mz_uint rev_code = 0, l, cur_code, code_size = pTable->m_code_size[sym_index]; if (!code_size) continue; cur_code = next_code[code_size]++; for (l = code_size; l > 0; l--, cur_code >>= 1) rev_code = (rev_code << 1) | (cur_code & 1); if (code_size <= TINFL_FAST_LOOKUP_BITS) { int16_t k = (int16_t)((code_size << 9) | sym_index); while (rev_code < TINFL_FAST_LOOKUP_SIZE) { pTable->m_look_up[rev_code] = k; rev_code += (1 << code_size); } continue; } if (0 == (tree_cur = pTable->m_look_up[rev_code & (TINFL_FAST_LOOKUP_SIZE - 1)])) { pTable->m_look_up[rev_code & (TINFL_FAST_LOOKUP_SIZE - 1)] = (int16_t)tree_next; tree_cur = tree_next; tree_next -= 2; } rev_code >>= (TINFL_FAST_LOOKUP_BITS - 1); for (j = code_size; j > (TINFL_FAST_LOOKUP_BITS + 1); j--) { tree_cur -= ((rev_code >>= 1) & 1); if (!pTable->m_tree[-tree_cur - 1]) { pTable->m_tree[-tree_cur - 1] = (int16_t)tree_next; tree_cur = tree_next; tree_next -= 2; } else tree_cur = pTable->m_tree[-tree_cur - 1]; } tree_cur -= ((rev_code >>= 1) & 1); pTable->m_tree[-tree_cur - 1] = (int16_t)sym_index; } if (r->m_type == 2) { for (counter = 0; counter < (r->m_table_sizes[0] + r->m_table_sizes[1]); ) { mz_uint s; TINFL_HUFF_DECODE(16, dist, &r->m_tables[2]); if (dist < 16) { r->m_len_codes[counter++] = (uint8_t)dist; continue; } if ((dist == 16) && (!counter)) { TINFL_CR_RETURN_FOREVER(17, TINFL_STATUS_FAILED); } num_extra = "\02\03\07"[dist - 16]; TINFL_GET_BITS(18, s, num_extra); s += "\03\03\013"[dist - 16]; TINFL_MEMSET(r->m_len_codes + counter, (dist == 16) ? r->m_len_codes[counter - 1] : 0, s); counter += s; } if ((r->m_table_sizes[0] + r->m_table_sizes[1]) != counter) { TINFL_CR_RETURN_FOREVER(21, TINFL_STATUS_FAILED); } TINFL_MEMCPY(r->m_tables[0].m_code_size, r->m_len_codes, r->m_table_sizes[0]); TINFL_MEMCPY(r->m_tables[1].m_code_size, r->m_len_codes + r->m_table_sizes[0], r->m_table_sizes[1]); } } for ( ; ; ) { uint8_t *pSrc; for ( ; ; ) { if (((pIn_buf_end - pIn_buf_cur) < 4) || ((pOut_buf_end - pOut_buf_cur) < 2)) { TINFL_HUFF_DECODE(23, counter, &r->m_tables[0]); if (counter >= 256) break; while (pOut_buf_cur >= pOut_buf_end) { TINFL_CR_RETURN(24, TINFL_STATUS_HAS_MORE_OUTPUT); } *pOut_buf_cur++ = (uint8_t)counter; } else { int sym2; mz_uint code_len; #if MINIZ_HAS_64BIT_REGISTERS if (num_bits < 30) { bit_buf |= (((tinfl_bit_buf_t)MZ_READ_LE32(pIn_buf_cur)) << num_bits); pIn_buf_cur += 4; num_bits += 32; } #else if (num_bits < 15) { bit_buf |= (((tinfl_bit_buf_t)MZ_READ_LE16(pIn_buf_cur)) << num_bits); pIn_buf_cur += 2; num_bits += 16; } #endif if ((sym2 = r->m_tables[0].m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0) code_len = sym2 >> 9; else { code_len = TINFL_FAST_LOOKUP_BITS; do { sym2 = r->m_tables[0].m_tree[~sym2 + ((bit_buf >> code_len++) & 1)]; } while (sym2 < 0); } counter = sym2; bit_buf >>= code_len; num_bits -= code_len; if (counter & 256) break; #if !MINIZ_HAS_64BIT_REGISTERS if (num_bits < 15) { bit_buf |= (((tinfl_bit_buf_t)MZ_READ_LE16(pIn_buf_cur)) << num_bits); pIn_buf_cur += 2; num_bits += 16; } #endif if ((sym2 = r->m_tables[0].m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0) code_len = sym2 >> 9; else { code_len = TINFL_FAST_LOOKUP_BITS; do { sym2 = r->m_tables[0].m_tree[~sym2 + ((bit_buf >> code_len++) & 1)]; } while (sym2 < 0); } bit_buf >>= code_len; num_bits -= code_len; pOut_buf_cur[0] = (uint8_t)counter; if (sym2 & 256) { pOut_buf_cur++; counter = sym2; break; } pOut_buf_cur[1] = (uint8_t)sym2; pOut_buf_cur += 2; } } if ((counter &= 511) == 256) break; num_extra = s_length_extra[counter - 257]; counter = s_length_base[counter - 257]; if (num_extra) { mz_uint extra_bits; TINFL_GET_BITS(25, extra_bits, num_extra); counter += extra_bits; } TINFL_HUFF_DECODE(26, dist, &r->m_tables[1]); num_extra = s_dist_extra[dist]; dist = s_dist_base[dist]; if (num_extra) { mz_uint extra_bits; TINFL_GET_BITS(27, extra_bits, num_extra); dist += extra_bits; } dist_from_out_buf_start = pOut_buf_cur - pOut_buf_start; if ((dist > dist_from_out_buf_start) && (decomp_flags & TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF)) { TINFL_CR_RETURN_FOREVER(37, TINFL_STATUS_FAILED); } pSrc = pOut_buf_start + ((dist_from_out_buf_start - dist) & out_buf_size_mask); if ((MZ_MAX(pOut_buf_cur, pSrc) + counter) > pOut_buf_end) { while (counter--) { while (pOut_buf_cur >= pOut_buf_end) { TINFL_CR_RETURN(53, TINFL_STATUS_HAS_MORE_OUTPUT); } *pOut_buf_cur++ = pOut_buf_start[(dist_from_out_buf_start++ - dist) & out_buf_size_mask]; } continue; } #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES else if ((counter >= 9) && (counter <= dist)) { const uint8_t *pSrc_end = pSrc + (counter & ~7); do { ((uint32_t *)pOut_buf_cur)[0] = ((const uint32_t *)pSrc)[0]; ((uint32_t *)pOut_buf_cur)[1] = ((const uint32_t *)pSrc)[1]; pOut_buf_cur += 8; } while ((pSrc += 8) < pSrc_end); if ((counter &= 7) < 3) { if (counter) { pOut_buf_cur[0] = pSrc[0]; if (counter > 1) pOut_buf_cur[1] = pSrc[1]; pOut_buf_cur += counter; } continue; } } #endif do { pOut_buf_cur[0] = pSrc[0]; pOut_buf_cur[1] = pSrc[1]; pOut_buf_cur[2] = pSrc[2]; pOut_buf_cur += 3; pSrc += 3; } while ((int)(counter -= 3) > 2); if ((int)counter > 0) { pOut_buf_cur[0] = pSrc[0]; if ((int)counter > 1) pOut_buf_cur[1] = pSrc[1]; pOut_buf_cur += counter; } } } } while (!(r->m_final & 1)); if (decomp_flags & TINFL_FLAG_PARSE_ZLIB_HEADER) { TINFL_SKIP_BITS(32, num_bits & 7); for (counter = 0; counter < 4; ++counter) { mz_uint s; if (num_bits) TINFL_GET_BITS(41, s, 8); else TINFL_GET_BYTE(42, s); r->m_z_adler32 = (r->m_z_adler32 << 8) | s; } } TINFL_CR_RETURN_FOREVER(34, TINFL_STATUS_DONE); TINFL_CR_FINISH common_exit: r->m_num_bits = num_bits; r->m_bit_buf = bit_buf; r->m_dist = dist; r->m_counter = counter; r->m_num_extra = num_extra; r->m_dist_from_out_buf_start = dist_from_out_buf_start; *pIn_buf_size = pIn_buf_cur - pIn_buf_next; *pOut_buf_size = pOut_buf_cur - pOut_buf_next; if ((decomp_flags & (TINFL_FLAG_PARSE_ZLIB_HEADER | TINFL_FLAG_COMPUTE_ADLER32)) && (status >= 0)) { const uint8_t *ptr = pOut_buf_next; size_t buf_len = *pOut_buf_size; uint32_t i, s1 = r->m_check_adler32 & 0xffff, s2 = r->m_check_adler32 >> 16; size_t block_len = buf_len % 5552; while (buf_len) { for (i = 0; i + 7 < block_len; i += 8, ptr += 8) { s1 += ptr[0], s2 += s1; s1 += ptr[1], s2 += s1; s1 += ptr[2], s2 += s1; s1 += ptr[3], s2 += s1; s1 += ptr[4], s2 += s1; s1 += ptr[5], s2 += s1; s1 += ptr[6], s2 += s1; s1 += ptr[7], s2 += s1; } for ( ; i < block_len; ++i) s1 += *ptr++, s2 += s1; s1 %= 65521U, s2 %= 65521U; buf_len -= block_len; block_len = 5552; } r->m_check_adler32 = (s2 << 16) + s1; if ((status == TINFL_STATUS_DONE) && (decomp_flags & TINFL_FLAG_PARSE_ZLIB_HEADER) && (r->m_check_adler32 != r->m_z_adler32)) status = TINFL_STATUS_ADLER32_MISMATCH; } return status; } // end of inflate.c // begin of deflate.c // Set TDEFL_LESS_MEMORY to 1 to use less memory (compression will be slightly slower, and raw/dynamic blocks will be output more frequently). #define TDEFL_LESS_MEMORY 0 #ifndef MZ_REALLOC #define MZ_REALLOC REALLOC #endif #ifndef MZ_FORCEINLINE #ifdef _MSC_VER #define MZ_FORCEINLINE __forceinline #elif defined(__GNUC__) #define MZ_FORCEINLINE inline __attribute__((__always_inline__)) #else #define MZ_FORCEINLINE inline #endif #endif // ------------------- Types and macros typedef int32_t mz_bool; #define MZ_FALSE (0) #define MZ_TRUE (1) // ------------------- Low-level Compression API Definitions // tdefl_init() compression flags logically OR'd together (low 12 bits contain the max. number of probes per dictionary search): // TDEFL_DEFAULT_MAX_PROBES: The compressor defaults to 128 dictionary probes per dictionary search. 0=Huffman only, 1=Huffman+LZ (fastest/crap compression), 4095=Huffman+LZ (slowest/best compression). enum { TDEFL_HUFFMAN_ONLY = 0, TDEFL_DEFAULT_MAX_PROBES = 128, TDEFL_MAX_PROBES_MASK = 0xFFF }; // TDEFL_WRITE_ZLIB_HEADER: If set, the compressor outputs a zlib header before the deflate data, and the Adler-32 of the source data at the end. Otherwise, you'll get raw deflate data. // TDEFL_COMPUTE_ADLER32: Always compute the adler-32 of the input data (even when not writing zlib headers). // TDEFL_GREEDY_PARSING_FLAG: Set to use faster greedy parsing, instead of more efficient lazy parsing. // TDEFL_NONDETERMINISTIC_PARSING_FLAG: Enable to decrease the compressor's initialization time to the minimum, but the output may vary from run to run given the same input (depending on the contents of memory). // TDEFL_RLE_MATCHES: Only look for RLE matches (matches with a distance of 1) // TDEFL_FILTER_MATCHES: Discards matches <= 5 chars if enabled. // TDEFL_FORCE_ALL_STATIC_BLOCKS: Disable usage of optimized Huffman tables. // TDEFL_FORCE_ALL_RAW_BLOCKS: Only use raw (uncompressed) deflate blocks. // The low 12 bits are reserved to control the max # of hash probes per dictionary lookup (see TDEFL_MAX_PROBES_MASK). enum { TDEFL_WRITE_ZLIB_HEADER = 0x01000, TDEFL_COMPUTE_ADLER32 = 0x02000, TDEFL_GREEDY_PARSING_FLAG = 0x04000, TDEFL_NONDETERMINISTIC_PARSING_FLAG = 0x08000, TDEFL_RLE_MATCHES = 0x10000, TDEFL_FILTER_MATCHES = 0x20000, TDEFL_FORCE_ALL_STATIC_BLOCKS = 0x40000, TDEFL_FORCE_ALL_RAW_BLOCKS = 0x80000 }; // Output stream interface. The compressor uses this interface to write compressed data. It'll typically be called TDEFL_OUT_BUF_SIZE at a time. typedef mz_bool (*tdefl_callback)(const void* pBuf, int len, void *pUser); enum { TDEFL_MAX_HUFF_TABLES = 3, TDEFL_MAX_HUFF_SYMBOLS_0 = 288, TDEFL_MAX_HUFF_SYMBOLS_1 = 32, TDEFL_MAX_HUFF_SYMBOLS_2 = 19, TDEFL_LZ_DICT_SIZE = 32768, TDEFL_LZ_DICT_SIZE_MASK = TDEFL_LZ_DICT_SIZE - 1, TDEFL_MIN_MATCH_LEN = 3, TDEFL_MAX_MATCH_LEN = 258 }; // TDEFL_OUT_BUF_SIZE MUST be large enough to hold a single entire compressed output block (using static/fixed Huffman codes). #if TDEFL_LESS_MEMORY enum { TDEFL_LZ_CODE_BUF_SIZE = 24 * 1024, TDEFL_OUT_BUF_SIZE = (TDEFL_LZ_CODE_BUF_SIZE * 13 ) / 10, TDEFL_MAX_HUFF_SYMBOLS = 288, TDEFL_LZ_HASH_BITS = 12, TDEFL_LEVEL1_HASH_SIZE_MASK = 4095, TDEFL_LZ_HASH_SHIFT = (TDEFL_LZ_HASH_BITS + 2) / 3, TDEFL_LZ_HASH_SIZE = 1 << TDEFL_LZ_HASH_BITS }; #else enum { TDEFL_LZ_CODE_BUF_SIZE = 64 * 1024, TDEFL_OUT_BUF_SIZE = (TDEFL_LZ_CODE_BUF_SIZE * 13 ) / 10, TDEFL_MAX_HUFF_SYMBOLS = 288, TDEFL_LZ_HASH_BITS = 15, TDEFL_LEVEL1_HASH_SIZE_MASK = 4095, TDEFL_LZ_HASH_SHIFT = (TDEFL_LZ_HASH_BITS + 2) / 3, TDEFL_LZ_HASH_SIZE = 1 << TDEFL_LZ_HASH_BITS }; #endif // The low-level tdefl functions below may be used directly if the above helper functions aren't flexible enough. The low-level functions don't make any heap allocations, unlike the above helper functions. typedef enum { TDEFL_STATUS_BAD_PARAM = -2, TDEFL_STATUS_PUT_BUF_FAILED = -1, TDEFL_STATUS_OKAY = 0, TDEFL_STATUS_DONE = 1, } tdefl_status; // Must map to MZ_NO_FLUSH, MZ_SYNC_FLUSH, etc. enums typedef enum { TDEFL_NO_FLUSH = 0, TDEFL_SYNC_FLUSH = 2, TDEFL_FULL_FLUSH = 3, TDEFL_FINISH = 4 } tdefl_flush; // tdefl's compression state structure. typedef struct { char *m_outbuffer[3]; // start,seek,end mz_uint m_flags, m_max_probes[2]; int m_greedy_parsing; mz_uint m_adler32, m_lookahead_pos, m_lookahead_size, m_dict_size; uint8_t *m_pLZ_code_buf, *m_pLZ_flags, *m_pOutput_buf, *m_pOutput_buf_end; mz_uint m_num_flags_left, m_total_lz_bytes, m_lz_code_buf_dict_pos, m_bits_in, m_bit_buffer; mz_uint m_saved_match_dist, m_saved_match_len, m_saved_lit, m_output_flush_ofs, m_output_flush_remaining, m_finished, m_block_index, m_wants_to_finish; tdefl_status m_prev_return_status; const void *m_pIn_buf; void *m_pOut_buf; size_t *m_pIn_buf_size, *m_pOut_buf_size; tdefl_flush m_flush; const uint8_t *m_pSrc; size_t m_src_buf_left, m_out_buf_ofs; uint8_t m_dict[TDEFL_LZ_DICT_SIZE + TDEFL_MAX_MATCH_LEN - 1]; uint16_t m_huff_count[TDEFL_MAX_HUFF_TABLES][TDEFL_MAX_HUFF_SYMBOLS]; uint16_t m_huff_codes[TDEFL_MAX_HUFF_TABLES][TDEFL_MAX_HUFF_SYMBOLS]; uint8_t m_huff_code_sizes[TDEFL_MAX_HUFF_TABLES][TDEFL_MAX_HUFF_SYMBOLS]; uint8_t m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE]; uint16_t m_next[TDEFL_LZ_DICT_SIZE]; uint16_t m_hash[TDEFL_LZ_HASH_SIZE]; uint8_t m_output_buf[TDEFL_OUT_BUF_SIZE]; } tdefl_compressor; // ------------------- zlib-style API's // mz_adler32() returns the initial adler-32 value to use when called with ptr==NULL. uint32_t mz_adler32(uint32_t adler, const unsigned char *ptr, size_t buf_len) { uint32_t i, s1 = (adler & 0xffff), s2 = (adler >> 16); size_t block_len = buf_len % 5552; if (!ptr) return 1; // MZ_ADLER32_INIT; while (buf_len) { for (i = 0; i + 7 < block_len; i += 8, ptr += 8) { s1 += ptr[0], s2 += s1; s1 += ptr[1], s2 += s1; s1 += ptr[2], s2 += s1; s1 += ptr[3], s2 += s1; s1 += ptr[4], s2 += s1; s1 += ptr[5], s2 += s1; s1 += ptr[6], s2 += s1; s1 += ptr[7], s2 += s1; } for ( ; i < block_len; ++i) s1 += *ptr++, s2 += s1; s1 %= 65521U, s2 %= 65521U; buf_len -= block_len; block_len = 5552; } return (s2 << 16) + s1; } // ------------------- Low-level Compression (independent from all decompression API's) // Purposely making these tables static for faster init and thread safety. static const uint16_t s_tdefl_len_sym[256] = { 257,258,259,260,261,262,263,264,265,265,266,266,267,267,268,268,269,269,269,269,270,270,270,270,271,271,271,271,272,272,272,272, 273,273,273,273,273,273,273,273,274,274,274,274,274,274,274,274,275,275,275,275,275,275,275,275,276,276,276,276,276,276,276,276, 277,277,277,277,277,277,277,277,277,277,277,277,277,277,277,277,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278, 279,279,279,279,279,279,279,279,279,279,279,279,279,279,279,279,280,280,280,280,280,280,280,280,280,280,280,280,280,280,280,280, 281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281, 282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282, 283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283, 284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,285 }; static const uint8_t s_tdefl_len_extra[256] = { 0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3, 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,0 }; static const uint8_t s_tdefl_small_dist_sym[512] = { 0,1,2,3,4,4,5,5,6,6,6,6,7,7,7,7,8,8,8,8,8,8,8,8,9,9,9,9,9,9,9,9,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,11,11,11,11,11,11, 11,11,11,11,11,11,11,11,11,11,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,13, 13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,14,14,14,14,14,14,14,14,14,14,14,14, 14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14, 14,14,14,14,14,14,14,14,14,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15, 15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,16,16,16,16,16,16,16,16,16,16,16,16,16, 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,17,17,17,17,17,17,17,17,17,17,17,17,17,17, 17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17, 17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17, 17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17 }; static const uint8_t s_tdefl_small_dist_extra[512] = { 0,0,0,0,1,1,1,1,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5, 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7 }; static const uint8_t s_tdefl_large_dist_sym[128] = { 0,0,18,19,20,20,21,21,22,22,22,22,23,23,23,23,24,24,24,24,24,24,24,24,25,25,25,25,25,25,25,25,26,26,26,26,26,26,26,26,26,26,26,26, 26,26,26,26,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28, 28,28,28,28,28,28,28,28,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29 }; static const uint8_t s_tdefl_large_dist_extra[128] = { 0,0,8,8,9,9,9,9,10,10,10,10,10,10,10,10,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, 12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13, 13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13 }; // Radix sorts tdefl_sym_freq[] array by 16-bit key m_key. Returns ptr to sorted values. typedef struct { uint16_t m_key, m_sym_index; } tdefl_sym_freq; static tdefl_sym_freq* tdefl_radix_sort_syms(mz_uint num_syms, tdefl_sym_freq* pSyms0, tdefl_sym_freq* pSyms1) { uint32_t total_passes = 2, pass_shift, pass, i, hist[256 * 2]; tdefl_sym_freq* pCur_syms = pSyms0, *pNew_syms = pSyms1; MZ_CLEAR_OBJ(hist); for (i = 0; i < num_syms; i++) { mz_uint freq = pSyms0[i].m_key; hist[freq & 0xFF]++; hist[256 + ((freq >> 8) & 0xFF)]++; } while ((total_passes > 1) && (num_syms == hist[(total_passes - 1) * 256])) total_passes--; for (pass_shift = 0, pass = 0; pass < total_passes; pass++, pass_shift += 8) { const uint32_t* pHist = &hist[pass << 8]; mz_uint offsets[256], cur_ofs = 0; for (i = 0; i < 256; i++) { offsets[i] = cur_ofs; cur_ofs += pHist[i]; } for (i = 0; i < num_syms; i++) pNew_syms[offsets[(pCur_syms[i].m_key >> pass_shift) & 0xFF]++] = pCur_syms[i]; { tdefl_sym_freq* t = pCur_syms; pCur_syms = pNew_syms; pNew_syms = t; } } return pCur_syms; } // tdefl_calculate_minimum_redundancy() originally written by: Alistair Moffat, alistair@cs.mu.oz.au, Jyrki Katajainen, jyrki@diku.dk, November 1996. static void tdefl_calculate_minimum_redundancy(tdefl_sym_freq *A, int n) { int root, leaf, next, avbl, used, dpth; if (n==0) return; else if (n==1) { A[0].m_key = 1; return; } A[0].m_key += A[1].m_key; root = 0; leaf = 2; for (next=1; next < n-1; next++) { if (leaf>=n || A[root].m_key=n || (root=0; next--) A[next].m_key = A[A[next].m_key].m_key+1; avbl = 1; used = dpth = 0; root = n-2; next = n-1; while (avbl>0) { while (root>=0 && (int)A[root].m_key==dpth) { used++; root--; } while (avbl>used) { A[next--].m_key = (uint16_t)(dpth); avbl--; } avbl = 2*used; dpth++; used = 0; } } // Limits canonical Huffman code table's max code size. enum { TDEFL_MAX_SUPPORTED_HUFF_CODESIZE = 32 }; static void tdefl_huffman_enforce_max_code_size(int *pNum_codes, int code_list_len, int max_code_size) { int i; uint32_t total = 0; if (code_list_len <= 1) return; for (i = max_code_size + 1; i <= TDEFL_MAX_SUPPORTED_HUFF_CODESIZE; i++) pNum_codes[max_code_size] += pNum_codes[i]; for (i = max_code_size; i > 0; i--) total += (((uint32_t)pNum_codes[i]) << (max_code_size - i)); while (total != (1UL << max_code_size)) { pNum_codes[max_code_size]--; for (i = max_code_size - 1; i > 0; i--) if (pNum_codes[i]) { pNum_codes[i]--; pNum_codes[i + 1] += 2; break; } total--; } } static void tdefl_optimize_huffman_table(tdefl_compressor *d, int table_num, int table_len, int code_size_limit, int static_table) { int i, j, l, num_codes[1 + TDEFL_MAX_SUPPORTED_HUFF_CODESIZE]; mz_uint next_code[TDEFL_MAX_SUPPORTED_HUFF_CODESIZE + 1]; MZ_CLEAR_OBJ(num_codes); if (static_table) { for (i = 0; i < table_len; i++) num_codes[d->m_huff_code_sizes[table_num][i]]++; } else { tdefl_sym_freq syms0[TDEFL_MAX_HUFF_SYMBOLS], syms1[TDEFL_MAX_HUFF_SYMBOLS], *pSyms; int num_used_syms = 0; const uint16_t *pSym_count = &d->m_huff_count[table_num][0]; for (i = 0; i < table_len; i++) if (pSym_count[i]) { syms0[num_used_syms].m_key = (uint16_t)pSym_count[i]; syms0[num_used_syms++].m_sym_index = (uint16_t)i; } pSyms = tdefl_radix_sort_syms(num_used_syms, syms0, syms1); tdefl_calculate_minimum_redundancy(pSyms, num_used_syms); for (i = 0; i < num_used_syms; i++) num_codes[pSyms[i].m_key]++; tdefl_huffman_enforce_max_code_size(num_codes, num_used_syms, code_size_limit); MZ_CLEAR_OBJ(d->m_huff_code_sizes[table_num]); MZ_CLEAR_OBJ(d->m_huff_codes[table_num]); for (i = 1, j = num_used_syms; i <= code_size_limit; i++) for (l = num_codes[i]; l > 0; l--) d->m_huff_code_sizes[table_num][pSyms[--j].m_sym_index] = (uint8_t)(i); } next_code[1] = 0; for (j = 0, i = 2; i <= code_size_limit; i++) next_code[i] = j = ((j + num_codes[i - 1]) << 1); for (i = 0; i < table_len; i++) { mz_uint rev_code = 0, code, code_size; if ((code_size = d->m_huff_code_sizes[table_num][i]) == 0) continue; code = next_code[code_size]++; for (l = code_size; l > 0; l--, code >>= 1) rev_code = (rev_code << 1) | (code & 1); d->m_huff_codes[table_num][i] = (uint16_t)rev_code; } } #define TDEFL_PUT_BITS(b, l) do { \ mz_uint bits = b; mz_uint len = l; MZ_ASSERT(bits <= ((1U << len) - 1U)); \ d->m_bit_buffer |= (bits << d->m_bits_in); d->m_bits_in += len; \ while (d->m_bits_in >= 8) { \ if (d->m_pOutput_buf < d->m_pOutput_buf_end) \ *d->m_pOutput_buf++ = (uint8_t)(d->m_bit_buffer); \ d->m_bit_buffer >>= 8; \ d->m_bits_in -= 8; \ } \ } MZ_MACRO_END #define TDEFL_RLE_PREV_CODE_SIZE() { if (rle_repeat_count) { \ if (rle_repeat_count < 3) { \ d->m_huff_count[2][prev_code_size] = (uint16_t)(d->m_huff_count[2][prev_code_size] + rle_repeat_count); \ while (rle_repeat_count--) packed_code_sizes[num_packed_code_sizes++] = prev_code_size; \ } else { \ d->m_huff_count[2][16] = (uint16_t)(d->m_huff_count[2][16] + 1); packed_code_sizes[num_packed_code_sizes++] = 16; packed_code_sizes[num_packed_code_sizes++] = (uint8_t)(rle_repeat_count - 3); \ } rle_repeat_count = 0; } } #define TDEFL_RLE_ZERO_CODE_SIZE() { if (rle_z_count) { \ if (rle_z_count < 3) { \ d->m_huff_count[2][0] = (uint16_t)(d->m_huff_count[2][0] + rle_z_count); while (rle_z_count--) packed_code_sizes[num_packed_code_sizes++] = 0; \ } else if (rle_z_count <= 10) { \ d->m_huff_count[2][17] = (uint16_t)(d->m_huff_count[2][17] + 1); packed_code_sizes[num_packed_code_sizes++] = 17; packed_code_sizes[num_packed_code_sizes++] = (uint8_t)(rle_z_count - 3); \ } else { \ d->m_huff_count[2][18] = (uint16_t)(d->m_huff_count[2][18] + 1); packed_code_sizes[num_packed_code_sizes++] = 18; packed_code_sizes[num_packed_code_sizes++] = (uint8_t)(rle_z_count - 11); \ } rle_z_count = 0; } } static uint8_t s_tdefl_packed_code_size_syms_swizzle[] = { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 }; static void tdefl_start_dynamic_block(tdefl_compressor *d) { int num_lit_codes, num_dist_codes, num_bit_lengths; mz_uint i, total_code_sizes_to_pack, num_packed_code_sizes, rle_z_count, rle_repeat_count, packed_code_sizes_index; uint8_t code_sizes_to_pack[TDEFL_MAX_HUFF_SYMBOLS_0 + TDEFL_MAX_HUFF_SYMBOLS_1], packed_code_sizes[TDEFL_MAX_HUFF_SYMBOLS_0 + TDEFL_MAX_HUFF_SYMBOLS_1], prev_code_size = 0xFF; d->m_huff_count[0][256] = 1; tdefl_optimize_huffman_table(d, 0, TDEFL_MAX_HUFF_SYMBOLS_0, 15, MZ_FALSE); tdefl_optimize_huffman_table(d, 1, TDEFL_MAX_HUFF_SYMBOLS_1, 15, MZ_FALSE); for (num_lit_codes = 286; num_lit_codes > 257; num_lit_codes--) if (d->m_huff_code_sizes[0][num_lit_codes - 1]) break; for (num_dist_codes = 30; num_dist_codes > 1; num_dist_codes--) if (d->m_huff_code_sizes[1][num_dist_codes - 1]) break; memcpy(code_sizes_to_pack, &d->m_huff_code_sizes[0][0], num_lit_codes); memcpy(code_sizes_to_pack + num_lit_codes, &d->m_huff_code_sizes[1][0], num_dist_codes); total_code_sizes_to_pack = num_lit_codes + num_dist_codes; num_packed_code_sizes = 0; rle_z_count = 0; rle_repeat_count = 0; memset(&d->m_huff_count[2][0], 0, sizeof(d->m_huff_count[2][0]) * TDEFL_MAX_HUFF_SYMBOLS_2); for (i = 0; i < total_code_sizes_to_pack; i++) { uint8_t code_size = code_sizes_to_pack[i]; if (!code_size) { TDEFL_RLE_PREV_CODE_SIZE(); if (++rle_z_count == 138) { TDEFL_RLE_ZERO_CODE_SIZE(); } } else { TDEFL_RLE_ZERO_CODE_SIZE(); if (code_size != prev_code_size) { TDEFL_RLE_PREV_CODE_SIZE(); d->m_huff_count[2][code_size] = (uint16_t)(d->m_huff_count[2][code_size] + 1); packed_code_sizes[num_packed_code_sizes++] = code_size; } else if (++rle_repeat_count == 6) { TDEFL_RLE_PREV_CODE_SIZE(); } } prev_code_size = code_size; } if (rle_repeat_count) { TDEFL_RLE_PREV_CODE_SIZE(); } else { TDEFL_RLE_ZERO_CODE_SIZE(); } tdefl_optimize_huffman_table(d, 2, TDEFL_MAX_HUFF_SYMBOLS_2, 7, MZ_FALSE); TDEFL_PUT_BITS(2, 2); TDEFL_PUT_BITS(num_lit_codes - 257, 5); TDEFL_PUT_BITS(num_dist_codes - 1, 5); for (num_bit_lengths = 18; num_bit_lengths >= 0; num_bit_lengths--) if (d->m_huff_code_sizes[2][s_tdefl_packed_code_size_syms_swizzle[num_bit_lengths]]) break; num_bit_lengths = MZ_MAX(4, (num_bit_lengths + 1)); TDEFL_PUT_BITS(num_bit_lengths - 4, 4); for (i = 0; (int)i < num_bit_lengths; i++) TDEFL_PUT_BITS(d->m_huff_code_sizes[2][s_tdefl_packed_code_size_syms_swizzle[i]], 3); for (packed_code_sizes_index = 0; packed_code_sizes_index < num_packed_code_sizes; ) { mz_uint code = packed_code_sizes[packed_code_sizes_index++]; MZ_ASSERT(code < TDEFL_MAX_HUFF_SYMBOLS_2); TDEFL_PUT_BITS(d->m_huff_codes[2][code], d->m_huff_code_sizes[2][code]); if (code >= 16) TDEFL_PUT_BITS(packed_code_sizes[packed_code_sizes_index++], "\02\03\07"[code - 16]); } } static void tdefl_start_static_block(tdefl_compressor *d) { mz_uint i; uint8_t *p = &d->m_huff_code_sizes[0][0]; for (i = 0; i <= 143; ++i) *p++ = 8; for ( ; i <= 255; ++i) *p++ = 9; for ( ; i <= 279; ++i) *p++ = 7; for ( ; i <= 287; ++i) *p++ = 8; memset(d->m_huff_code_sizes[1], 5, 32); tdefl_optimize_huffman_table(d, 0, 288, 15, MZ_TRUE); tdefl_optimize_huffman_table(d, 1, 32, 15, MZ_TRUE); TDEFL_PUT_BITS(1, 2); } static const mz_uint mz_bitmasks[17] = { 0x0000, 0x0001, 0x0003, 0x0007, 0x000F, 0x001F, 0x003F, 0x007F, 0x00FF, 0x01FF, 0x03FF, 0x07FF, 0x0FFF, 0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF }; #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN && MINIZ_HAS_64BIT_REGISTERS static mz_bool tdefl_compress_lz_codes(tdefl_compressor *d) { mz_uint flags; uint8_t *pLZ_codes; uint8_t *pOutput_buf = d->m_pOutput_buf; uint8_t *pLZ_code_buf_end = d->m_pLZ_code_buf; uint64_t bit_buffer = d->m_bit_buffer; mz_uint bits_in = d->m_bits_in; #define TDEFL_PUT_BITS_FAST(b, l) { bit_buffer |= (((uint64_t)(b)) << bits_in); bits_in += (l); } flags = 1; for (pLZ_codes = d->m_lz_code_buf; pLZ_codes < pLZ_code_buf_end; flags >>= 1) { if (flags == 1) flags = *pLZ_codes++ | 0x100; if (flags & 1) { mz_uint s0, s1, n0, n1, sym, num_extra_bits; mz_uint match_len = pLZ_codes[0], match_dist = *(const uint16_t *)(pLZ_codes + 1); pLZ_codes += 3; MZ_ASSERT(d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]); TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][s_tdefl_len_sym[match_len]], d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]); TDEFL_PUT_BITS_FAST(match_len & mz_bitmasks[s_tdefl_len_extra[match_len]], s_tdefl_len_extra[match_len]); // This sequence coaxes MSVC into using cmov's vs. jmp's. s0 = s_tdefl_small_dist_sym[match_dist & 511]; n0 = s_tdefl_small_dist_extra[match_dist & 511]; s1 = s_tdefl_large_dist_sym[match_dist >> 8]; n1 = s_tdefl_large_dist_extra[match_dist >> 8]; sym = (match_dist < 512) ? s0 : s1; num_extra_bits = (match_dist < 512) ? n0 : n1; MZ_ASSERT(d->m_huff_code_sizes[1][sym]); TDEFL_PUT_BITS_FAST(d->m_huff_codes[1][sym], d->m_huff_code_sizes[1][sym]); TDEFL_PUT_BITS_FAST(match_dist & mz_bitmasks[num_extra_bits], num_extra_bits); } else { mz_uint lit = *pLZ_codes++; MZ_ASSERT(d->m_huff_code_sizes[0][lit]); TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]); if (((flags & 2) == 0) && (pLZ_codes < pLZ_code_buf_end)) { flags >>= 1; lit = *pLZ_codes++; MZ_ASSERT(d->m_huff_code_sizes[0][lit]); TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]); if (((flags & 2) == 0) && (pLZ_codes < pLZ_code_buf_end)) { flags >>= 1; lit = *pLZ_codes++; MZ_ASSERT(d->m_huff_code_sizes[0][lit]); TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]); } } } if (pOutput_buf >= d->m_pOutput_buf_end) return MZ_FALSE; *(uint64_t*)pOutput_buf = bit_buffer; pOutput_buf += (bits_in >> 3); bit_buffer >>= (bits_in & ~7); bits_in &= 7; } #undef TDEFL_PUT_BITS_FAST d->m_pOutput_buf = pOutput_buf; d->m_bits_in = 0; d->m_bit_buffer = 0; while (bits_in) { uint32_t n = MZ_MIN(bits_in, 16); TDEFL_PUT_BITS((mz_uint)bit_buffer & mz_bitmasks[n], n); bit_buffer >>= n; bits_in -= n; } TDEFL_PUT_BITS(d->m_huff_codes[0][256], d->m_huff_code_sizes[0][256]); return (d->m_pOutput_buf < d->m_pOutput_buf_end); } #else static mz_bool tdefl_compress_lz_codes(tdefl_compressor *d) { mz_uint flags; uint8_t *pLZ_codes; flags = 1; for (pLZ_codes = d->m_lz_code_buf; pLZ_codes < d->m_pLZ_code_buf; flags >>= 1) { if (flags == 1) flags = *pLZ_codes++ | 0x100; if (flags & 1) { mz_uint sym, num_extra_bits; mz_uint match_len = pLZ_codes[0], match_dist = (pLZ_codes[1] | (pLZ_codes[2] << 8)); pLZ_codes += 3; MZ_ASSERT(d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]); TDEFL_PUT_BITS(d->m_huff_codes[0][s_tdefl_len_sym[match_len]], d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]); TDEFL_PUT_BITS(match_len & mz_bitmasks[s_tdefl_len_extra[match_len]], s_tdefl_len_extra[match_len]); if (match_dist < 512) { sym = s_tdefl_small_dist_sym[match_dist]; num_extra_bits = s_tdefl_small_dist_extra[match_dist]; } else { sym = s_tdefl_large_dist_sym[match_dist >> 8]; num_extra_bits = s_tdefl_large_dist_extra[match_dist >> 8]; } MZ_ASSERT(d->m_huff_code_sizes[1][sym]); TDEFL_PUT_BITS(d->m_huff_codes[1][sym], d->m_huff_code_sizes[1][sym]); TDEFL_PUT_BITS(match_dist & mz_bitmasks[num_extra_bits], num_extra_bits); } else { mz_uint lit = *pLZ_codes++; MZ_ASSERT(d->m_huff_code_sizes[0][lit]); TDEFL_PUT_BITS(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]); } } TDEFL_PUT_BITS(d->m_huff_codes[0][256], d->m_huff_code_sizes[0][256]); return (d->m_pOutput_buf < d->m_pOutput_buf_end); } #endif // MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN && MINIZ_HAS_64BIT_REGISTERS static mz_bool tdefl_compress_block(tdefl_compressor *d, mz_bool static_block) { if (static_block) tdefl_start_static_block(d); else tdefl_start_dynamic_block(d); return tdefl_compress_lz_codes(d); } static int tdefl_flush_block(tdefl_compressor *d, int flush) { mz_uint saved_bit_buf, saved_bits_in; uint8_t *pSaved_output_buf; mz_bool comp_block_succeeded = MZ_FALSE; int n, use_raw_block = ((d->m_flags & TDEFL_FORCE_ALL_RAW_BLOCKS) != 0) && (d->m_lookahead_pos - d->m_lz_code_buf_dict_pos) <= d->m_dict_size; uint8_t *pOutput_buf_start = ((d->m_outbuffer[0] == NULL) && ((*d->m_pOut_buf_size - d->m_out_buf_ofs) >= TDEFL_OUT_BUF_SIZE)) ? ((uint8_t *)d->m_pOut_buf + d->m_out_buf_ofs) : d->m_output_buf; d->m_pOutput_buf = pOutput_buf_start; d->m_pOutput_buf_end = d->m_pOutput_buf + TDEFL_OUT_BUF_SIZE - 16; MZ_ASSERT(!d->m_output_flush_remaining); d->m_output_flush_ofs = 0; d->m_output_flush_remaining = 0; *d->m_pLZ_flags = (uint8_t)(*d->m_pLZ_flags >> d->m_num_flags_left); d->m_pLZ_code_buf -= (d->m_num_flags_left == 8); if ((d->m_flags & TDEFL_WRITE_ZLIB_HEADER) && (!d->m_block_index)) { TDEFL_PUT_BITS(0x78, 8); TDEFL_PUT_BITS(0x01, 8); } TDEFL_PUT_BITS(flush == TDEFL_FINISH, 1); pSaved_output_buf = d->m_pOutput_buf; saved_bit_buf = d->m_bit_buffer; saved_bits_in = d->m_bits_in; if (!use_raw_block) comp_block_succeeded = tdefl_compress_block(d, (d->m_flags & TDEFL_FORCE_ALL_STATIC_BLOCKS) || (d->m_total_lz_bytes < 48)); // If the block gets expanded, forget the current contents of the output buffer and send a raw block instead. if ( ((use_raw_block) || ((d->m_total_lz_bytes) && ((d->m_pOutput_buf - pSaved_output_buf + 1U) >= d->m_total_lz_bytes))) && ((d->m_lookahead_pos - d->m_lz_code_buf_dict_pos) <= d->m_dict_size) ) { mz_uint i; d->m_pOutput_buf = pSaved_output_buf; d->m_bit_buffer = saved_bit_buf, d->m_bits_in = saved_bits_in; TDEFL_PUT_BITS(0, 2); if (d->m_bits_in) { TDEFL_PUT_BITS(0, 8 - d->m_bits_in); } for (i = 2; i; --i, d->m_total_lz_bytes ^= 0xFFFF) { TDEFL_PUT_BITS(d->m_total_lz_bytes & 0xFFFF, 16); } for (i = 0; i < d->m_total_lz_bytes; ++i) { TDEFL_PUT_BITS(d->m_dict[(d->m_lz_code_buf_dict_pos + i) & TDEFL_LZ_DICT_SIZE_MASK], 8); } } // Check for the extremely unlikely (if not impossible) case of the compressed block not fitting into the output buffer when using dynamic codes. else if (!comp_block_succeeded) { d->m_pOutput_buf = pSaved_output_buf; d->m_bit_buffer = saved_bit_buf, d->m_bits_in = saved_bits_in; tdefl_compress_block(d, MZ_TRUE); } if (flush) { if (flush == TDEFL_FINISH) { if (d->m_bits_in) { TDEFL_PUT_BITS(0, 8 - d->m_bits_in); } if (d->m_flags & TDEFL_WRITE_ZLIB_HEADER) { mz_uint i, a = d->m_adler32; for (i = 0; i < 4; i++) { TDEFL_PUT_BITS((a >> 24) & 0xFF, 8); a <<= 8; } } } else { mz_uint i, z = 0; TDEFL_PUT_BITS(0, 3); if (d->m_bits_in) { TDEFL_PUT_BITS(0, 8 - d->m_bits_in); } for (i = 2; i; --i, z ^= 0xFFFF) { TDEFL_PUT_BITS(z & 0xFFFF, 16); } } } MZ_ASSERT(d->m_pOutput_buf < d->m_pOutput_buf_end); memset(&d->m_huff_count[0][0], 0, sizeof(d->m_huff_count[0][0]) * TDEFL_MAX_HUFF_SYMBOLS_0); memset(&d->m_huff_count[1][0], 0, sizeof(d->m_huff_count[1][0]) * TDEFL_MAX_HUFF_SYMBOLS_1); d->m_pLZ_code_buf = d->m_lz_code_buf + 1; d->m_pLZ_flags = d->m_lz_code_buf; d->m_num_flags_left = 8; d->m_lz_code_buf_dict_pos += d->m_total_lz_bytes; d->m_total_lz_bytes = 0; d->m_block_index++; if ((n = (int)(d->m_pOutput_buf - pOutput_buf_start)) != 0) { if (d->m_outbuffer[0]) { *d->m_pIn_buf_size = d->m_pSrc - (const uint8_t *)d->m_pIn_buf; uintptr_t capacity = (uintptr_t)d->m_outbuffer[2] - (uintptr_t)d->m_outbuffer[1]; if( n > capacity ) return (d->m_prev_return_status = TDEFL_STATUS_PUT_BUF_FAILED); memcpy(d->m_outbuffer[1], d->m_output_buf, n); d->m_outbuffer[1] += n; } else if (pOutput_buf_start == d->m_output_buf) { int bytes_to_copy = (int)MZ_MIN((size_t)n, (size_t)(*d->m_pOut_buf_size - d->m_out_buf_ofs)); memcpy((uint8_t *)d->m_pOut_buf + d->m_out_buf_ofs, d->m_output_buf, bytes_to_copy); d->m_out_buf_ofs += bytes_to_copy; if ((n -= bytes_to_copy) != 0) { d->m_output_flush_ofs = bytes_to_copy; d->m_output_flush_remaining = n; } } else { d->m_out_buf_ofs += n; } } return d->m_output_flush_remaining; } #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES #define TDEFL_READ_UNALIGNED_WORD(p) *(const uint16_t*)(p) static MZ_FORCEINLINE void tdefl_find_match(tdefl_compressor *d, mz_uint lookahead_pos, mz_uint max_dist, mz_uint max_match_len, mz_uint *pMatch_dist, mz_uint *pMatch_len) { mz_uint dist, pos = lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK, match_len = *pMatch_len, probe_pos = pos, next_probe_pos, probe_len; mz_uint num_probes_left = d->m_max_probes[match_len >= 32]; const uint16_t *s = (const uint16_t*)(d->m_dict + pos), *p, *q; uint16_t c01 = TDEFL_READ_UNALIGNED_WORD(&d->m_dict[pos + match_len - 1]), s01 = TDEFL_READ_UNALIGNED_WORD(s); MZ_ASSERT(max_match_len <= TDEFL_MAX_MATCH_LEN); if (max_match_len <= match_len) return; for ( ; ; ) { for ( ; ; ) { if (--num_probes_left == 0) return; #define TDEFL_PROBE \ next_probe_pos = d->m_next[probe_pos]; \ if ((!next_probe_pos) || ((dist = (uint16_t)(lookahead_pos - next_probe_pos)) > max_dist)) return; \ probe_pos = next_probe_pos & TDEFL_LZ_DICT_SIZE_MASK; \ if (TDEFL_READ_UNALIGNED_WORD(&d->m_dict[probe_pos + match_len - 1]) == c01) break; TDEFL_PROBE; TDEFL_PROBE; TDEFL_PROBE; } if (!dist) break; q = (const uint16_t*)(d->m_dict + probe_pos); if (TDEFL_READ_UNALIGNED_WORD(q) != s01) continue; p = s; probe_len = 32; do { } while ( (TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) && (TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) && (TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) && (TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) && (--probe_len > 0) ); if (!probe_len) { *pMatch_dist = dist; *pMatch_len = MZ_MIN(max_match_len, TDEFL_MAX_MATCH_LEN); break; } else if ((probe_len = ((mz_uint)(p - s) * 2) + (mz_uint)(*(const uint8_t*)p == *(const uint8_t*)q)) > match_len) { *pMatch_dist = dist; if ((*pMatch_len = match_len = MZ_MIN(max_match_len, probe_len)) == max_match_len) break; c01 = TDEFL_READ_UNALIGNED_WORD(&d->m_dict[pos + match_len - 1]); } } } #else static MZ_FORCEINLINE void tdefl_find_match(tdefl_compressor *d, mz_uint lookahead_pos, mz_uint max_dist, mz_uint max_match_len, mz_uint *pMatch_dist, mz_uint *pMatch_len) { mz_uint dist, pos = lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK, match_len = *pMatch_len, probe_pos = pos, next_probe_pos, probe_len; mz_uint num_probes_left = d->m_max_probes[match_len >= 32]; const uint8_t *s = d->m_dict + pos, *p, *q; uint8_t c0 = d->m_dict[pos + match_len], c1 = d->m_dict[pos + match_len - 1]; MZ_ASSERT(max_match_len <= TDEFL_MAX_MATCH_LEN); if (max_match_len <= match_len) return; for ( ; ; ) { for ( ; ; ) { if (--num_probes_left == 0) return; #define TDEFL_PROBE \ next_probe_pos = d->m_next[probe_pos]; \ if ((!next_probe_pos) || ((dist = (uint16_t)(lookahead_pos - next_probe_pos)) > max_dist)) return; \ probe_pos = next_probe_pos & TDEFL_LZ_DICT_SIZE_MASK; \ if ((d->m_dict[probe_pos + match_len] == c0) && (d->m_dict[probe_pos + match_len - 1] == c1)) break; TDEFL_PROBE; TDEFL_PROBE; TDEFL_PROBE; } if (!dist) break; p = s; q = d->m_dict + probe_pos; for (probe_len = 0; probe_len < max_match_len; probe_len++) if (*p++ != *q++) break; if (probe_len > match_len) { *pMatch_dist = dist; if ((*pMatch_len = match_len = probe_len) == max_match_len) return; c0 = d->m_dict[pos + match_len]; c1 = d->m_dict[pos + match_len - 1]; } } } #endif // #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN static mz_bool tdefl_compress_fast(tdefl_compressor *d) { // Faster, minimally featured LZRW1-style match+parse loop with better register utilization. Intended for applications where raw throughput is valued more highly than ratio. mz_uint lookahead_pos = d->m_lookahead_pos, lookahead_size = d->m_lookahead_size, dict_size = d->m_dict_size, total_lz_bytes = d->m_total_lz_bytes, num_flags_left = d->m_num_flags_left; uint8_t *pLZ_code_buf = d->m_pLZ_code_buf, *pLZ_flags = d->m_pLZ_flags; mz_uint cur_pos = lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK; while ((d->m_src_buf_left) || ((d->m_flush) && (lookahead_size))) { const mz_uint TDEFL_COMP_FAST_LOOKAHEAD_SIZE = 4096; mz_uint dst_pos = (lookahead_pos + lookahead_size) & TDEFL_LZ_DICT_SIZE_MASK; mz_uint num_bytes_to_process = (mz_uint)MZ_MIN(d->m_src_buf_left, TDEFL_COMP_FAST_LOOKAHEAD_SIZE - lookahead_size); d->m_src_buf_left -= num_bytes_to_process; lookahead_size += num_bytes_to_process; while (num_bytes_to_process) { uint32_t n = MZ_MIN(TDEFL_LZ_DICT_SIZE - dst_pos, num_bytes_to_process); memcpy(d->m_dict + dst_pos, d->m_pSrc, n); if (dst_pos < (TDEFL_MAX_MATCH_LEN - 1)) memcpy(d->m_dict + TDEFL_LZ_DICT_SIZE + dst_pos, d->m_pSrc, MZ_MIN(n, (TDEFL_MAX_MATCH_LEN - 1) - dst_pos)); d->m_pSrc += n; dst_pos = (dst_pos + n) & TDEFL_LZ_DICT_SIZE_MASK; num_bytes_to_process -= n; } dict_size = MZ_MIN(TDEFL_LZ_DICT_SIZE - lookahead_size, dict_size); if ((!d->m_flush) && (lookahead_size < TDEFL_COMP_FAST_LOOKAHEAD_SIZE)) break; while (lookahead_size >= 4) { mz_uint cur_match_dist, cur_match_len = 1; uint8_t *pCur_dict = d->m_dict + cur_pos; mz_uint first_trigram = (*(const uint32_t *)pCur_dict) & 0xFFFFFF; mz_uint hash = (first_trigram ^ (first_trigram >> (24 - (TDEFL_LZ_HASH_BITS - 8)))) & TDEFL_LEVEL1_HASH_SIZE_MASK; mz_uint probe_pos = d->m_hash[hash]; d->m_hash[hash] = (uint16_t)lookahead_pos; if (((cur_match_dist = (uint16_t)(lookahead_pos - probe_pos)) <= dict_size) && ((*(const uint32_t *)(d->m_dict + (probe_pos &= TDEFL_LZ_DICT_SIZE_MASK)) & 0xFFFFFF) == first_trigram)) { const uint16_t *p = (const uint16_t *)pCur_dict; const uint16_t *q = (const uint16_t *)(d->m_dict + probe_pos); uint32_t probe_len = 32; do { } while ( (TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) && (TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) && (TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) && (TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) && (--probe_len > 0) ); cur_match_len = ((mz_uint)(p - (const uint16_t *)pCur_dict) * 2) + (mz_uint)(*(const uint8_t *)p == *(const uint8_t *)q); if (!probe_len) cur_match_len = cur_match_dist ? TDEFL_MAX_MATCH_LEN : 0; if ((cur_match_len < TDEFL_MIN_MATCH_LEN) || ((cur_match_len == TDEFL_MIN_MATCH_LEN) && (cur_match_dist >= 8U*1024U))) { cur_match_len = 1; *pLZ_code_buf++ = (uint8_t)first_trigram; *pLZ_flags = (uint8_t)(*pLZ_flags >> 1); d->m_huff_count[0][(uint8_t)first_trigram]++; } else { uint32_t s0, s1; cur_match_len = MZ_MIN(cur_match_len, lookahead_size); MZ_ASSERT((cur_match_len >= TDEFL_MIN_MATCH_LEN) && (cur_match_dist >= 1) && (cur_match_dist <= TDEFL_LZ_DICT_SIZE)); cur_match_dist--; pLZ_code_buf[0] = (uint8_t)(cur_match_len - TDEFL_MIN_MATCH_LEN); *(uint16_t *)(&pLZ_code_buf[1]) = (uint16_t)cur_match_dist; pLZ_code_buf += 3; *pLZ_flags = (uint8_t)((*pLZ_flags >> 1) | 0x80); s0 = s_tdefl_small_dist_sym[cur_match_dist & 511]; s1 = s_tdefl_large_dist_sym[cur_match_dist >> 8]; d->m_huff_count[1][(cur_match_dist < 512) ? s0 : s1]++; d->m_huff_count[0][s_tdefl_len_sym[cur_match_len - TDEFL_MIN_MATCH_LEN]]++; } } else { *pLZ_code_buf++ = (uint8_t)first_trigram; *pLZ_flags = (uint8_t)(*pLZ_flags >> 1); d->m_huff_count[0][(uint8_t)first_trigram]++; } if (--num_flags_left == 0) { num_flags_left = 8; pLZ_flags = pLZ_code_buf++; } total_lz_bytes += cur_match_len; lookahead_pos += cur_match_len; dict_size = MZ_MIN(dict_size + cur_match_len, TDEFL_LZ_DICT_SIZE); cur_pos = (cur_pos + cur_match_len) & TDEFL_LZ_DICT_SIZE_MASK; MZ_ASSERT(lookahead_size >= cur_match_len); lookahead_size -= cur_match_len; if (pLZ_code_buf > &d->m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE - 8]) { int n; d->m_lookahead_pos = lookahead_pos; d->m_lookahead_size = lookahead_size; d->m_dict_size = dict_size; d->m_total_lz_bytes = total_lz_bytes; d->m_pLZ_code_buf = pLZ_code_buf; d->m_pLZ_flags = pLZ_flags; d->m_num_flags_left = num_flags_left; if ((n = tdefl_flush_block(d, 0)) != 0) return (n < 0) ? MZ_FALSE : MZ_TRUE; total_lz_bytes = d->m_total_lz_bytes; pLZ_code_buf = d->m_pLZ_code_buf; pLZ_flags = d->m_pLZ_flags; num_flags_left = d->m_num_flags_left; } } while (lookahead_size) { uint8_t lit = d->m_dict[cur_pos]; total_lz_bytes++; *pLZ_code_buf++ = lit; *pLZ_flags = (uint8_t)(*pLZ_flags >> 1); if (--num_flags_left == 0) { num_flags_left = 8; pLZ_flags = pLZ_code_buf++; } d->m_huff_count[0][lit]++; lookahead_pos++; dict_size = MZ_MIN(dict_size + 1, TDEFL_LZ_DICT_SIZE); cur_pos = (cur_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK; lookahead_size--; if (pLZ_code_buf > &d->m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE - 8]) { int n; d->m_lookahead_pos = lookahead_pos; d->m_lookahead_size = lookahead_size; d->m_dict_size = dict_size; d->m_total_lz_bytes = total_lz_bytes; d->m_pLZ_code_buf = pLZ_code_buf; d->m_pLZ_flags = pLZ_flags; d->m_num_flags_left = num_flags_left; if ((n = tdefl_flush_block(d, 0)) != 0) return (n < 0) ? MZ_FALSE : MZ_TRUE; total_lz_bytes = d->m_total_lz_bytes; pLZ_code_buf = d->m_pLZ_code_buf; pLZ_flags = d->m_pLZ_flags; num_flags_left = d->m_num_flags_left; } } } d->m_lookahead_pos = lookahead_pos; d->m_lookahead_size = lookahead_size; d->m_dict_size = dict_size; d->m_total_lz_bytes = total_lz_bytes; d->m_pLZ_code_buf = pLZ_code_buf; d->m_pLZ_flags = pLZ_flags; d->m_num_flags_left = num_flags_left; return MZ_TRUE; } #endif // MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN static MZ_FORCEINLINE void tdefl_record_literal(tdefl_compressor *d, uint8_t lit) { d->m_total_lz_bytes++; *d->m_pLZ_code_buf++ = lit; *d->m_pLZ_flags = (uint8_t)(*d->m_pLZ_flags >> 1); if (--d->m_num_flags_left == 0) { d->m_num_flags_left = 8; d->m_pLZ_flags = d->m_pLZ_code_buf++; } d->m_huff_count[0][lit]++; } static MZ_FORCEINLINE void tdefl_record_match(tdefl_compressor *d, mz_uint match_len, mz_uint match_dist) { uint32_t s0, s1; MZ_ASSERT((match_len >= TDEFL_MIN_MATCH_LEN) && (match_dist >= 1) && (match_dist <= TDEFL_LZ_DICT_SIZE)); d->m_total_lz_bytes += match_len; d->m_pLZ_code_buf[0] = (uint8_t)(match_len - TDEFL_MIN_MATCH_LEN); match_dist -= 1; d->m_pLZ_code_buf[1] = (uint8_t)(match_dist & 0xFF); d->m_pLZ_code_buf[2] = (uint8_t)(match_dist >> 8); d->m_pLZ_code_buf += 3; *d->m_pLZ_flags = (uint8_t)((*d->m_pLZ_flags >> 1) | 0x80); if (--d->m_num_flags_left == 0) { d->m_num_flags_left = 8; d->m_pLZ_flags = d->m_pLZ_code_buf++; } s0 = s_tdefl_small_dist_sym[match_dist & 511]; s1 = s_tdefl_large_dist_sym[(match_dist >> 8) & 127]; d->m_huff_count[1][(match_dist < 512) ? s0 : s1]++; if (match_len >= TDEFL_MIN_MATCH_LEN) d->m_huff_count[0][s_tdefl_len_sym[match_len - TDEFL_MIN_MATCH_LEN]]++; } static mz_bool tdefl_compress_normal(tdefl_compressor *d) { const uint8_t *pSrc = d->m_pSrc; size_t src_buf_left = d->m_src_buf_left; tdefl_flush flush = d->m_flush; while ((src_buf_left) || ((flush) && (d->m_lookahead_size))) { mz_uint len_to_move, cur_match_dist, cur_match_len, cur_pos; // Update dictionary and hash chains. Keeps the lookahead size equal to TDEFL_MAX_MATCH_LEN. if ((d->m_lookahead_size + d->m_dict_size) >= (TDEFL_MIN_MATCH_LEN - 1)) { mz_uint dst_pos = (d->m_lookahead_pos + d->m_lookahead_size) & TDEFL_LZ_DICT_SIZE_MASK, ins_pos = d->m_lookahead_pos + d->m_lookahead_size - 2; mz_uint hash = (d->m_dict[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] << TDEFL_LZ_HASH_SHIFT) ^ d->m_dict[(ins_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK]; mz_uint num_bytes_to_process = (mz_uint)MZ_MIN(src_buf_left, TDEFL_MAX_MATCH_LEN - d->m_lookahead_size); const uint8_t *pSrc_end = pSrc + num_bytes_to_process; src_buf_left -= num_bytes_to_process; d->m_lookahead_size += num_bytes_to_process; while (pSrc != pSrc_end) { uint8_t c = *pSrc++; d->m_dict[dst_pos] = c; if (dst_pos < (TDEFL_MAX_MATCH_LEN - 1)) d->m_dict[TDEFL_LZ_DICT_SIZE + dst_pos] = c; hash = ((hash << TDEFL_LZ_HASH_SHIFT) ^ c) & (TDEFL_LZ_HASH_SIZE - 1); d->m_next[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] = d->m_hash[hash]; d->m_hash[hash] = (uint16_t)(ins_pos); dst_pos = (dst_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK; ins_pos++; } } else { while ((src_buf_left) && (d->m_lookahead_size < TDEFL_MAX_MATCH_LEN)) { uint8_t c = *pSrc++; mz_uint dst_pos = (d->m_lookahead_pos + d->m_lookahead_size) & TDEFL_LZ_DICT_SIZE_MASK; src_buf_left--; d->m_dict[dst_pos] = c; if (dst_pos < (TDEFL_MAX_MATCH_LEN - 1)) d->m_dict[TDEFL_LZ_DICT_SIZE + dst_pos] = c; if ((++d->m_lookahead_size + d->m_dict_size) >= TDEFL_MIN_MATCH_LEN) { mz_uint ins_pos = d->m_lookahead_pos + (d->m_lookahead_size - 1) - 2; mz_uint hash = ((d->m_dict[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] << (TDEFL_LZ_HASH_SHIFT * 2)) ^ (d->m_dict[(ins_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK] << TDEFL_LZ_HASH_SHIFT) ^ c) & (TDEFL_LZ_HASH_SIZE - 1); d->m_next[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] = d->m_hash[hash]; d->m_hash[hash] = (uint16_t)(ins_pos); } } } d->m_dict_size = MZ_MIN(TDEFL_LZ_DICT_SIZE - d->m_lookahead_size, d->m_dict_size); if ((!flush) && (d->m_lookahead_size < TDEFL_MAX_MATCH_LEN)) break; // Simple lazy/greedy parsing state machine. len_to_move = 1; cur_match_dist = 0; cur_match_len = d->m_saved_match_len ? d->m_saved_match_len : (TDEFL_MIN_MATCH_LEN - 1); cur_pos = d->m_lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK; if (d->m_flags & (TDEFL_RLE_MATCHES | TDEFL_FORCE_ALL_RAW_BLOCKS)) { if ((d->m_dict_size) && (!(d->m_flags & TDEFL_FORCE_ALL_RAW_BLOCKS))) { uint8_t c = d->m_dict[(cur_pos - 1) & TDEFL_LZ_DICT_SIZE_MASK]; cur_match_len = 0; while (cur_match_len < d->m_lookahead_size) { if (d->m_dict[cur_pos + cur_match_len] != c) break; cur_match_len++; } if (cur_match_len < TDEFL_MIN_MATCH_LEN) cur_match_len = 0; else cur_match_dist = 1; } } else { tdefl_find_match(d, d->m_lookahead_pos, d->m_dict_size, d->m_lookahead_size, &cur_match_dist, &cur_match_len); } if (((cur_match_len == TDEFL_MIN_MATCH_LEN) && (cur_match_dist >= 8U*1024U)) || (cur_pos == cur_match_dist) || ((d->m_flags & TDEFL_FILTER_MATCHES) && (cur_match_len <= 5))) { cur_match_dist = cur_match_len = 0; } if (d->m_saved_match_len) { if (cur_match_len > d->m_saved_match_len) { tdefl_record_literal(d, (uint8_t)d->m_saved_lit); if (cur_match_len >= 128) { tdefl_record_match(d, cur_match_len, cur_match_dist); d->m_saved_match_len = 0; len_to_move = cur_match_len; } else { d->m_saved_lit = d->m_dict[cur_pos]; d->m_saved_match_dist = cur_match_dist; d->m_saved_match_len = cur_match_len; } } else { tdefl_record_match(d, d->m_saved_match_len, d->m_saved_match_dist); len_to_move = d->m_saved_match_len - 1; d->m_saved_match_len = 0; } } else if (!cur_match_dist) tdefl_record_literal(d, d->m_dict[MZ_MIN(cur_pos, sizeof(d->m_dict) - 1)]); else if ((d->m_greedy_parsing) || (d->m_flags & TDEFL_RLE_MATCHES) || (cur_match_len >= 128)) { tdefl_record_match(d, cur_match_len, cur_match_dist); len_to_move = cur_match_len; } else { d->m_saved_lit = d->m_dict[MZ_MIN(cur_pos, sizeof(d->m_dict) - 1)]; d->m_saved_match_dist = cur_match_dist; d->m_saved_match_len = cur_match_len; } // Move the lookahead forward by len_to_move bytes. d->m_lookahead_pos += len_to_move; MZ_ASSERT(d->m_lookahead_size >= len_to_move); d->m_lookahead_size -= len_to_move; d->m_dict_size = MZ_MIN(d->m_dict_size + len_to_move, TDEFL_LZ_DICT_SIZE); // Check if it's time to flush the current LZ codes to the internal output buffer. if ( (d->m_pLZ_code_buf > &d->m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE - 8]) || ( (d->m_total_lz_bytes > 31*1024) && (((((mz_uint)(d->m_pLZ_code_buf - d->m_lz_code_buf) * 115) >> 7) >= d->m_total_lz_bytes) || (d->m_flags & TDEFL_FORCE_ALL_RAW_BLOCKS))) ) { int n; d->m_pSrc = pSrc; d->m_src_buf_left = src_buf_left; if ((n = tdefl_flush_block(d, 0)) != 0) return (n < 0) ? MZ_FALSE : MZ_TRUE; } } d->m_pSrc = pSrc; d->m_src_buf_left = src_buf_left; return MZ_TRUE; } static tdefl_status tdefl_flush_output_buffer(tdefl_compressor *d) { if (d->m_pIn_buf_size) { *d->m_pIn_buf_size = d->m_pSrc - (const uint8_t *)d->m_pIn_buf; } if (d->m_pOut_buf_size) { size_t n = MZ_MIN(*d->m_pOut_buf_size - d->m_out_buf_ofs, d->m_output_flush_remaining); memcpy((uint8_t *)d->m_pOut_buf + d->m_out_buf_ofs, d->m_output_buf + d->m_output_flush_ofs, n); d->m_output_flush_ofs += (mz_uint)n; d->m_output_flush_remaining -= (mz_uint)n; d->m_out_buf_ofs += n; *d->m_pOut_buf_size = d->m_out_buf_ofs; } return (d->m_finished && !d->m_output_flush_remaining) ? TDEFL_STATUS_DONE : TDEFL_STATUS_OKAY; } tdefl_status tdefl_compress(tdefl_compressor *d, const void *pIn_buf, size_t *pIn_buf_size, void *pOut_buf, size_t *pOut_buf_size, tdefl_flush flush) { if (!d) { if (pIn_buf_size) *pIn_buf_size = 0; if (pOut_buf_size) *pOut_buf_size = 0; return TDEFL_STATUS_BAD_PARAM; } d->m_pIn_buf = pIn_buf; d->m_pIn_buf_size = pIn_buf_size; d->m_pOut_buf = pOut_buf; d->m_pOut_buf_size = pOut_buf_size; d->m_pSrc = (const uint8_t *)(pIn_buf); d->m_src_buf_left = pIn_buf_size ? *pIn_buf_size : 0; d->m_out_buf_ofs = 0; d->m_flush = flush; if ( ((d->m_outbuffer[0] != NULL) == ((pOut_buf != NULL) || (pOut_buf_size != NULL))) || (d->m_prev_return_status != TDEFL_STATUS_OKAY) || (d->m_wants_to_finish && (flush != TDEFL_FINISH)) || (pIn_buf_size && *pIn_buf_size && !pIn_buf) || (pOut_buf_size && *pOut_buf_size && !pOut_buf) ) { if (pIn_buf_size) *pIn_buf_size = 0; if (pOut_buf_size) *pOut_buf_size = 0; return (d->m_prev_return_status = TDEFL_STATUS_BAD_PARAM); } d->m_wants_to_finish |= (flush == TDEFL_FINISH); if ((d->m_output_flush_remaining) || (d->m_finished)) return (d->m_prev_return_status = tdefl_flush_output_buffer(d)); #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN if (((d->m_flags & TDEFL_MAX_PROBES_MASK) == 1) && ((d->m_flags & TDEFL_GREEDY_PARSING_FLAG) != 0) && ((d->m_flags & (TDEFL_FILTER_MATCHES | TDEFL_FORCE_ALL_RAW_BLOCKS | TDEFL_RLE_MATCHES)) == 0)) { if (!tdefl_compress_fast(d)) return d->m_prev_return_status; } else #endif // #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN { if (!tdefl_compress_normal(d)) return d->m_prev_return_status; } if ((d->m_flags & (TDEFL_WRITE_ZLIB_HEADER | TDEFL_COMPUTE_ADLER32)) && (pIn_buf)) d->m_adler32 = (uint32_t)mz_adler32(d->m_adler32, (const uint8_t *)pIn_buf, d->m_pSrc - (const uint8_t *)pIn_buf); if ((flush) && (!d->m_lookahead_size) && (!d->m_src_buf_left) && (!d->m_output_flush_remaining)) { if (tdefl_flush_block(d, flush) < 0) return d->m_prev_return_status; d->m_finished = (flush == TDEFL_FINISH); if (flush == TDEFL_FULL_FLUSH) { MZ_CLEAR_OBJ(d->m_hash); MZ_CLEAR_OBJ(d->m_next); d->m_dict_size = 0; } } return (d->m_prev_return_status = tdefl_flush_output_buffer(d)); } tdefl_status tdefl_compress_buffer(tdefl_compressor *d, const void *pIn_buf, size_t in_buf_size, tdefl_flush flush) { MZ_ASSERT(d->m_outbuffer[0]); MZ_ASSERT(d->m_outbuffer[1]); MZ_ASSERT(d->m_outbuffer[2]); return tdefl_compress(d, pIn_buf, &in_buf_size, NULL, NULL, flush); } tdefl_status tdefl_init(tdefl_compressor *d, void *out, size_t outlen, int flags) { #if 0 d->m_putbuf_callback = putbuf_callback; d->m_pPut_buf_user = pPut_buf_user; d->m_flags = (mz_uint)(flags); d->m_max_probes[0] = 1 + ((flags & 0xFFF) + 2) / 3; d->m_greedy_parsing = (flags & TDEFL_GREEDY_PARSING_FLAG) != 0; d->m_max_probes[1] = 1 + (((flags & 0xFFF) >> 2) + 2) / 3; if (!(flags & TDEFL_NONDETERMINISTIC_PARSING_FLAG)) MZ_CLEAR_OBJ(d->m_hash); d->m_lookahead_pos = d->m_lookahead_size = d->m_dict_size = d->m_total_lz_bytes = d->m_lz_code_buf_dict_pos = d->m_bits_in = 0; d->m_output_flush_ofs = d->m_output_flush_remaining = d->m_finished = d->m_block_index = d->m_bit_buffer = d->m_wants_to_finish = 0; d->m_pLZ_code_buf = d->m_lz_code_buf + 1; d->m_pLZ_flags = d->m_lz_code_buf; d->m_num_flags_left = 8; d->m_pOutput_buf = d->m_output_buf; d->m_pOutput_buf_end = d->m_output_buf; d->m_prev_return_status = TDEFL_STATUS_OKAY; d->m_saved_match_dist = d->m_saved_match_len = d->m_saved_lit = 0; d->m_adler32 = 1; d->m_pIn_buf = NULL; d->m_pOut_buf = NULL; d->m_pIn_buf_size = NULL; d->m_pOut_buf_size = NULL; d->m_flush = TDEFL_NO_FLUSH; d->m_pSrc = NULL; d->m_src_buf_left = 0; d->m_out_buf_ofs = 0; memset(&d->m_huff_count[0][0], 0, sizeof(d->m_huff_count[0][0]) * TDEFL_MAX_HUFF_SYMBOLS_0); memset(&d->m_huff_count[1][0], 0, sizeof(d->m_huff_count[1][0]) * TDEFL_MAX_HUFF_SYMBOLS_1); #else tdefl_compressor zero = {0}; *d = zero; // invalidated TDEFL_NONDETERMINISTIC_PARSING_FLAG option here d->m_outbuffer[0] = d->m_outbuffer[1] = (char*)out; d->m_outbuffer[2] = d->m_outbuffer[0] + outlen; d->m_flags = (mz_uint)(flags); d->m_max_probes[0] = 1 + ((flags & 0xFFF) + 2) / 3; d->m_greedy_parsing = (flags & TDEFL_GREEDY_PARSING_FLAG) != 0; d->m_max_probes[1] = 1 + (((flags & 0xFFF) >> 2) + 2) / 3; d->m_pLZ_code_buf = d->m_lz_code_buf + 1; d->m_pLZ_flags = d->m_lz_code_buf; d->m_num_flags_left = 8; d->m_pOutput_buf = d->m_output_buf; d->m_pOutput_buf_end = d->m_output_buf; d->m_prev_return_status = TDEFL_STATUS_OKAY; d->m_adler32 = 1; d->m_flush = TDEFL_NO_FLUSH; //memset(&d->m_huff_count[0][0], 0, sizeof(d->m_huff_count[0][0]) * TDEFL_MAX_HUFF_SYMBOLS_0); //memset(&d->m_huff_count[1][0], 0, sizeof(d->m_huff_count[1][0]) * TDEFL_MAX_HUFF_SYMBOLS_1); #endif return TDEFL_STATUS_OKAY; } // end of deflate.c static unsigned deflate_decode_(const void *in, unsigned inlen_, void *out, unsigned outlen_, unsigned zlib) { size_t inlen = inlen_, outlen = outlen_; tinfl_decompressor decomp = {0}; tinfl_status status; // tinfl_init(&decomp); status = tinfl_decompress(&decomp, (const uint8_t*)in, &inlen, (uint8_t*)out, (uint8_t*)out, &outlen, zlib|TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF); return (unsigned)((status != TINFL_STATUS_DONE) ? 0 : outlen); } static unsigned deflate_encode_(const void *in, unsigned inlen, void *out, unsigned outlen, unsigned flags /*[0..9|10]*/, unsigned zlib_flags) { size_t bytes = 0; if(in && inlen && out && outlen) { int level = flags > 10 ? 10 : flags; const mz_uint tdefl_num_probes[11] = { 0, 1, 6, 32, 16, 32, 128, 256, 512, 768, 1500 }; mz_uint comp_flags = zlib_flags | tdefl_num_probes[level] | (level <= 3 ? TDEFL_GREEDY_PARSING_FLAG : 0); tdefl_compressor *pComp = (tdefl_compressor*)MZ_REALLOC(0,sizeof(tdefl_compressor)); if (!pComp) return MZ_FALSE; if(tdefl_init(pComp, out, outlen, (int)comp_flags) == TDEFL_STATUS_OKAY) { if(tdefl_compress_buffer(pComp, in, inlen, TDEFL_FINISH) == TDEFL_STATUS_DONE) { bytes = pComp->m_outbuffer[1] - pComp->m_outbuffer[0]; } } MZ_REALLOC(pComp, 0); } return (unsigned)bytes; } unsigned deflate_decode(const void *in, unsigned inlen_, void *out, unsigned outlen_) { return deflate_decode_(in, inlen_, out, outlen_, 0); } unsigned deflate_encode(const void *in, unsigned inlen, void *out, unsigned outlen, unsigned flags /*[0..9|10]*/) { return deflate_encode_(in, inlen, out, outlen, flags, 0); } unsigned deflatez_decode(const void *in, unsigned inlen_, void *out, unsigned outlen_) { return deflate_decode_(in, inlen_, out, outlen_, TINFL_FLAG_PARSE_ZLIB_HEADER); } unsigned deflatez_encode(const void *in, unsigned inlen, void *out, unsigned outlen, unsigned flags /*[0..9|10]*/) { return deflate_encode_(in, inlen, out, outlen, flags, TDEFL_WRITE_ZLIB_HEADER); } unsigned deflate_bounds(unsigned inlen, unsigned flags) { return (unsigned)MZ_MAX(128 + (inlen * 110) / 100, 128 + inlen + ((inlen / (31 * 1024)) + 1) * 5); } unsigned deflate_excess(unsigned flags) { return (unsigned)0; } #endif // DEFLATE_C #ifdef DEFLATE_DEMO //#pragma once int main() { const char *longcopy = "Hello world! Hello world! Hello world! Hello world!"; int level=1; char out[128]; unsigned outlen = deflate_encode(longcopy, strlen(longcopy)+1, out, 128, level); printf("%s %d->%d\n", outlen ? "ok" : "fail", (int)strlen(longcopy)+1, (int)outlen); char redo[128]; unsigned unpacked = deflate_decode(out, outlen, redo, 128); printf("%d->%d %s\n", (int)outlen, (int)unpacked, redo); } #define main main__ #endif // DEFLATE_DEMO //#line 1 "amalgamated_lz4x.c" // LZ4X - An optimized LZ4 compressor // Written and placed in the public domain by Ilya Muravyov (UNLICENSED) // MemBased+ThreadSafe by @r-lyeh. unsigned lz4x_encode(const void *in, unsigned inlen, void *out, unsigned outlen, unsigned flags); //[1(fastest)..(6)..15(uber)] unsigned lz4x_decode(const void *in, unsigned inlen, void *out, unsigned outlen); unsigned lz4x_bounds(unsigned inlen, unsigned flags); unsigned lz4x_excess(unsigned flags); #ifdef LZ4X_C //#pragma once #define _CRT_SECURE_NO_WARNINGS #define _CRT_DISABLE_PERFCRIT_LOCKS #include #include #include #include #include #include #ifndef LZ4X_REALLOC #define LZ4X_REALLOC REALLOC #endif #define LZ4X_BLOCK_SIZE (8<<20) // 8 MB #define LZ4X_PADDING_LITERALS 5 #define LZ4X_WINDOW_BITS 16 #define LZ4X_WINDOW_SIZE (1<(b))?(a):(b)) #define LZ4X_LOAD_16(p) (*(const uint16_t*)(p)) #define LZ4X_LOAD_32(p) (*(const uint32_t*)(p)) #define LZ4X_STORE_16(p, x) (*(uint16_t*)(p)=(x)) #define LZ4X_COPY_32(d, s) (*(uint32_t*)(d)=LZ4X_LOAD_32(s)) #define LZ4X_HASH_BITS 18 #define LZ4X_HASH_SIZE (1<>(32-LZ4X_HASH_BITS)) //< @r-lyeh macro below crashes often with /fsanitize=address #define lz4x_wild_copy(d,s,n) do { \ LZ4X_COPY_32(d, s); \ LZ4X_COPY_32(d+4, s+4); \ for (int i_=8; i_=LZ4X_MAX(12-LZ4X_PADDING_LITERALS, LZ4X_MIN_MATCH)) { const int limit=LZ4X_MAX(p-LZ4X_WINDOW_SIZE, LZ4X_NIL); int* left=&nodes[p&LZ4X_WINDOW_MASK][1]; int* right=&nodes[p&LZ4X_WINDOW_MASK][0]; int left_len=0; int right_len=0; const uint32_t h=LZ4X_HASH_32(&in[p]); int s=head[h]; head[h]=p; while (s>limit) { int len=LZ4X_MIN(left_len, right_len); if (in[s+len]==in[p+len]) { while (++lenbest_len) { best_len=len; dist=p-s; if (len==max_match || len>=(1<<16)) break; } } if (in[s+len]0; --p) { int c0=path[p+1].cum+1; if (--count==0) { count=255; ++c0; } int len=path[p].len; if (len>=LZ4X_MIN_MATCH) { int c1=1<<30; const int j=LZ4X_MAX(len-255, LZ4X_MIN_MATCH); for (int i=len; i>=j; --i) { int tmp=path[p+i].cum+3; if (i>=(15+LZ4X_MIN_MATCH)) tmp+=1+((i-(15+LZ4X_MIN_MATCH))/255); if (tmp=LZ4X_MIN_MATCH) { int len=path[p].len-LZ4X_MIN_MATCH; const int nib=LZ4X_MIN(len, 15); if (pp!=p) { const int run=p-pp; if (run>=15) { out[op++]=(15<<4)+nib; int j=run-15; for (; j>=255; j-=255) out[op++]=255; out[op++]=j; } else out[op++]=(run<<4)+nib; lz4x_wild_copy(&out[op], &in[pp], run); op+=run; } else out[op++]=nib; LZ4X_STORE_16(&out[op], path[p].dist); op+=2; if (len>=15) { len-=15; for (; len>=255; len-=255) out[op++]=255; out[op++]=len; } p+=path[p].len; pp=p; } else ++p; } if (pp!=p) { const int run=p-pp; if (run>=15) { out[op++]=15<<4; int j=run-15; for (; j>=255; j-=255) out[op++]=255; out[op++]=j; } else out[op++]=run<<4; lz4x_wild_copy(&out[op], &in[pp], run); op+=run; } LZ4X_REALLOC(path, 0); const int comp_len=op; return comp_len; } int lz4x_compress(const uint8_t *in, size_t inlen, uint8_t *out, size_t outlen, unsigned max_chain) { static __thread int head[LZ4X_HASH_SIZE]; static __thread int tail[LZ4X_WINDOW_SIZE]; int n = (int)inlen; for (int i=0; i=LZ4X_MAX(12-LZ4X_PADDING_LITERALS, LZ4X_MIN_MATCH)) { const int limit=LZ4X_MAX(p-LZ4X_WINDOW_SIZE, LZ4X_NIL); int chain_len=max_chain; int s=head[LZ4X_HASH_32(&in[p])]; while (s>limit) { if (in[s+best_len]==in[p+best_len] && LZ4X_LOAD_32(&in[s])==LZ4X_LOAD_32(&in[p])) { int len=LZ4X_MIN_MATCH; while (lenbest_len) { best_len=len; dist=p-s; if (len==max_match) break; } } if (--chain_len<=0) break; s=tail[s&LZ4X_WINDOW_MASK]; } } if (best_len>=LZ4X_MIN_MATCH) { int len=best_len-LZ4X_MIN_MATCH; const int nib=LZ4X_MIN(len, 15); if (pp!=p) { const int run=p-pp; if (run>=15) { out[op++]=(15<<4)+nib; int j=run-15; for (; j>=255; j-=255) out[op++]=255; out[op++]=j; } else out[op++]=(run<<4)+nib; lz4x_wild_copy(&out[op], &in[pp], run); op+=run; } else out[op++]=nib; LZ4X_STORE_16(&out[op], dist); op+=2; if (len>=15) { len-=15; for (; len>=255; len-=255) out[op++]=255; out[op++]=len; } pp=p+best_len; while (p=15) { out[op++]=15<<4; int j=run-15; for (; j>=255; j-=255) out[op++]=255; out[op++]=j; } else out[op++]=run<<4; lz4x_wild_copy(&out[op], &in[pp], run); op+=run; } const int comp_len=op; return comp_len; } int lz4x_decompress(const uint8_t *in, size_t inlen, uint8_t *out, size_t outlen) { int n = (int)inlen; int p=0; int ip=0; const int ip_end=ip+n; for (;;) { const int token=in[ip++]; if (token>=16) { int run=token>>4; if (run==15) { for (;;) { const int c=in[ip++]; run+=c; if (c!=255) break; } } if ((p+run)>outlen) return 0; // -1 lz4x_wild_copy(&out[p], &in[ip], run); p+=run; ip+=run; if (ip>=ip_end) break; } int s=p-LZ4X_LOAD_16(&in[ip]); ip+=2; if (s<0) return 0; // -1 int len=(token&15)+LZ4X_MIN_MATCH; if (len==(15+LZ4X_MIN_MATCH)) { for (;;) { const int c=in[ip++]; len+=c; if (c!=255) break; } } if ((p+len)>outlen) return 0; // -1 if ((p-s)>=4) { lz4x_wild_copy(&out[p], &out[s], len); p+=len; } else { while (len--!=0) out[p++]=out[s++]; } } return p; } unsigned lz4x_encode(const void *in, unsigned inlen, void *out, unsigned outlen, unsigned flags/*[1..15*/) { unsigned level = (unsigned)(flags > 15 ? 15 : flags < 1 ? 1 : flags); if(level >= 15) return lz4x_compress_optimal((const uint8_t*)in, inlen, (uint8_t*)out, outlen); return (unsigned)lz4x_compress((const uint8_t*)in, inlen, (uint8_t*)out, outlen, level); } unsigned lz4x_decode(const void *in, unsigned inlen, void *out, unsigned outlen) { return (unsigned)lz4x_decompress((const uint8_t*)in, (size_t)inlen, (uint8_t*)out, (size_t)outlen); } unsigned lz4x_bounds(unsigned inlen, unsigned flags) { return (unsigned)(inlen * 2 + (inlen/255) + 16); // (inlen + (inlen/255) + 16); } unsigned lz4x_excess(unsigned flags) { return (unsigned)(LZ4X_EXCESS); } #endif // LZ4X_C #ifdef LZ4X_DEMO //#pragma once int main() { const char *longcopy = "Hello world! Hello world! Hello world! Hello world!"; int level=1; char out[128]; unsigned outlen = lz4x_encode(longcopy, strlen(longcopy)+1, out, 128, level); printf("%s %d->%d\n", outlen ? "ok" : "fail", (int)strlen(longcopy)+1, (int)outlen); char redo[128]; unsigned unpacked = lz4x_decode(out, outlen, redo, 128); printf("%d->%d %s\n", (int)outlen, (int)unpacked, redo); } #define main main__ #endif // LZ4X_DEMO //#line 1 "amalgamated_lzma.c" // LzFind.c -- Match finder for LZ algorithms 2009-04-22 : Igor Pavlov : Public domain // LzmaDec.c -- LZMA Decoder 2009-09-20 : Igor Pavlov : Public domain // LzmaEnc.c -- LZMA Encoder 2009-11-24 : Igor Pavlov : Public domain // Additional code by @r-lyeh, public domain. TOC: glue.h+lzfind.h/c+lzmaenc.h/c+lzmadec.h/c+glue.c unsigned lzma_encode(const void *in, unsigned inlen, void *out, unsigned outlen, unsigned flags); // [0..(7)..9] unsigned lzma_decode(const void *in, unsigned inlen, void *out, unsigned outlen); unsigned lzma_bounds(unsigned inlen, unsigned flags); unsigned lzma_excess(unsigned flags); #ifdef LZMA_C //#pragma once // glue.h #ifndef LZMA_REALLOC #define LZMA_REALLOC REALLOC #endif #define LZMA_MALLOC(s) LZMA_REALLOC(0, s) #define LZMA_FREE(p) LZMA_REALLOC(p, 0) #define _FILE_OFFSET_BITS 64 #include #include #include #include #include #include #include #ifndef max #define max(x,y) ((x) >= (y) ? (x) : (y)) #endif #ifndef min #define min(x,y) ((x) <= (y) ? (x) : (y)) #endif #ifdef _WIN32 //#include #else //#include #endif /* #define SHOW_STAT */ /* #define SHOW_STAT2 */ typedef int State; enum { min_dictionary_bits = 12, min_dictionary_size = 1 << min_dictionary_bits, max_dictionary_bits = 29, max_dictionary_size = 1 << max_dictionary_bits, max_dictionary_bits_c = 27, /* kDicLogSizeMaxCompress */ max_dictionary_size_c = 1 << max_dictionary_bits_c, literal_context_bits = 3, literal_pos_state_bits = 0, /* not used */ pos_state_bits = 2, len_low_bits = 3, len_mid_bits = 3, len_high_bits = 8, len_low_symbols = 1 << len_low_bits, len_mid_symbols = 1 << len_mid_bits, len_high_symbols = 1 << len_high_bits, max_len_symbols = len_low_symbols + len_mid_symbols + len_high_symbols, min_match_len = 2, /* must be 2 */ max_match_len = min_match_len + max_len_symbols - 1, /* 273 */ min_match_len_limit = 5 }; enum { SZ_OK = 0, SZ_ERROR_READ = 8, SZ_ERROR_WRITE = 9, }; // io interface static int readblock( const int fd, uint8_t *buf,int size ); static int writeblock( const int fd, const uint8_t *buf, int size ); /* LzFind.h -- Match finder for LZ algorithms 2009-04-22 : Igor Pavlov : Public domain */ typedef uint32_t CLzRef; typedef struct { uint8_t *bufferBase; uint8_t *buffer; CLzRef *hash; CLzRef *son; uint32_t pos; uint32_t posLimit; uint32_t streamPos; uint32_t lenLimit; uint32_t cyclicBufferPos; uint32_t cyclicBufferSize; /* it must be = (historySize + 1) */ uint32_t matchMaxLen; uint32_t hashMask; uint32_t cutValue; uint32_t blockSize; uint32_t keepSizeBefore; uint32_t keepSizeAfter; uint32_t numHashBytes; uint32_t historySize; uint32_t hashSizeSum; uint32_t numSons; int infd; int result; uint32_t crc; bool btMode; bool streamEndWasReached; } CMatchFinder; /* Conditions: historySize <= 3 GB keepAddBufferBefore + matchMaxLen + keepAddBufferAfter < 511MB */ int Mf_Init(CMatchFinder *p, const int ifd, const int mc, uint32_t historySize, uint32_t keepAddBufferBefore, uint32_t matchMaxLen, uint32_t keepAddBufferAfter); void Mf_Free(CMatchFinder *p); /* Conditions: Mf_GetNumAvailableBytes_Func must be called before each Mf_GetMatchLen_Func. Mf_GetPointerToCurrentPos_Func's result must be used only before any other function */ typedef uint32_t (*Mf_GetMatches_Func)(void *object, uint32_t *distances); typedef void (*Mf_Skip_Func)(void *object, uint32_t); typedef struct _IMatchFinder { Mf_GetMatches_Func GetMatches; Mf_Skip_Func Skip; } IMatchFinder; void Mf_CreateVTable(CMatchFinder *p, IMatchFinder *vTable); static inline uint32_t Mf_GetNumAvailableBytes(CMatchFinder *p) { return p->streamPos - p->pos; } static inline uint8_t Mf_GetIndexByte(CMatchFinder *p, int index) { return p->buffer[index]; } static inline uint8_t * Mf_GetPointerToCurrentPos(CMatchFinder *p) { return p->buffer; } /* LzFind.c -- Match finder for LZ algorithms 2009-04-22 : Igor Pavlov : Public domain */ static uint32_t crc32[256]; /* Table of CRCs of all 8-bit messages. */ static inline void CRC32_init(void) { for( unsigned n = 0; n < 256; ++n ) { unsigned c = n; for( int k = 0; k < 8; ++k ) { if( c & 1 ) c = 0xEDB88320U ^ ( c >> 1 ); else c >>= 1; } crc32[n] = c; } } static inline void CRC32_update_buf(uint32_t* const crc, const uint8_t* const buffer, const int size) { uint32_t c = *crc; for( int i = 0; i < size; ++i ) c = crc32[(c^buffer[i])&0xFF] ^ ( c >> 8 ); *crc = c; } #define kHash2Size (1 << 10) #define kHash3Size (1 << 16) #define kHash4Size (1 << 20) #define kFix3HashSize (kHash2Size) #define kFix4HashSize (kHash2Size + kHash3Size) #define HASH2_CALC hashValue = cur[0] | ((uint32_t)cur[1] << 8); #define HASH3_CALC { \ uint32_t temp = crc32[cur[0]] ^ cur[1]; \ hash2Value = temp & (kHash2Size - 1); \ hashValue = (temp ^ ((uint32_t)cur[2] << 8)) & p->hashMask; } #define HASH4_CALC { \ uint32_t temp = crc32[cur[0]] ^ cur[1]; \ hash2Value = temp & (kHash2Size - 1); \ hash3Value = (temp ^ ((uint32_t)cur[2] << 8)) & (kHash3Size - 1); \ hashValue = (temp ^ ((uint32_t)cur[2] << 8) ^ (crc32[cur[3]] << 5)) & p->hashMask; } #define kEmptyHashValue 0 #define kMaxValForNormalize ((uint32_t)0xFFFFFFFF) #define kNormalizeStepMin (1 << 10) /* it must be power of 2 */ #define kNormalizeMask (~(kNormalizeStepMin - 1)) #define kStartMaxLen 3 static void Mf_ReadBlock(CMatchFinder *p) { if (p->streamEndWasReached || p->result != SZ_OK) return; for (;;) { uint8_t * const dest = p->buffer + (p->streamPos - p->pos); const int size = (p->bufferBase + p->blockSize - dest); int rd; if (size == 0) return; rd = readblock( p->infd, dest, size ); if (rd != size && errno) { p->result = SZ_ERROR_READ; return; } if (rd == 0) { p->streamEndWasReached = true; return; } CRC32_update_buf( &p->crc, dest, rd ); p->streamPos += rd; if (p->streamPos - p->pos > p->keepSizeAfter) return; } } static void Mf_CheckAndMoveAndRead(CMatchFinder *p) { if ((uint32_t)(p->bufferBase + p->blockSize - p->buffer) <= p->keepSizeAfter) { memmove(p->bufferBase, p->buffer - p->keepSizeBefore, p->streamPos - p->pos + p->keepSizeBefore); p->buffer = p->bufferBase + p->keepSizeBefore; } Mf_ReadBlock(p); } void Mf_Free(CMatchFinder *p) { LZMA_FREE(p->hash); p->hash = 0; LZMA_FREE(p->bufferBase); p->bufferBase = 0; } static CLzRef* AllocRefs(uint32_t num) { uint32_t sizeInBytes = num * sizeof(CLzRef); if (sizeInBytes / sizeof(CLzRef) != num) return 0; return (CLzRef *)LZMA_MALLOC(sizeInBytes); } static void Mf_SetLimits(CMatchFinder *p) { uint32_t limit = kMaxValForNormalize - p->pos; uint32_t limit2 = p->cyclicBufferSize - p->cyclicBufferPos; if (limit2 < limit) limit = limit2; limit2 = p->streamPos - p->pos; if (limit2 <= p->keepSizeAfter) { if (limit2 > 0) limit2 = 1; } else limit2 -= p->keepSizeAfter; if (limit2 < limit) limit = limit2; { uint32_t lenLimit = p->streamPos - p->pos; if (lenLimit > p->matchMaxLen) lenLimit = p->matchMaxLen; p->lenLimit = lenLimit; } p->posLimit = p->pos + limit; } int Mf_Init(CMatchFinder *p, const int ifd, const int mc, uint32_t historySize, uint32_t keepAddBufferBefore, uint32_t matchMaxLen, uint32_t keepAddBufferAfter) { const uint32_t sizeReserv = ( historySize >> 1 ) + (keepAddBufferBefore + matchMaxLen + keepAddBufferAfter) / 2 + (1 << 19); p->hash = 0; p->cutValue = mc; p->infd = ifd; p->btMode = true; p->numHashBytes = 4; p->crc = 0xFFFFFFFFU; p->keepSizeBefore = historySize + keepAddBufferBefore + 1; p->keepSizeAfter = matchMaxLen + keepAddBufferAfter; /* we need one additional byte, since we use MoveBlock after pos++ and before dictionary using */ /* keepSizeBefore + keepSizeAfter + sizeReserv must be < 4G) */ p->blockSize = p->keepSizeBefore + p->keepSizeAfter + sizeReserv; p->buffer = p->bufferBase = (uint8_t *)LZMA_MALLOC(p->blockSize); if( p->bufferBase ) { uint32_t newCyclicBufferSize = historySize + 1; uint32_t hs; p->matchMaxLen = matchMaxLen; { if (p->numHashBytes == 2) hs = (1 << 16) - 1; else { hs = historySize - 1; hs |= (hs >> 1); hs |= (hs >> 2); hs |= (hs >> 4); hs |= (hs >> 8); hs >>= 1; hs |= 0xFFFF; /* don't change it! It's required for Deflate */ if (hs > (1 << 24)) { if (p->numHashBytes == 3) hs = (1 << 24) - 1; else hs >>= 1; } } p->hashMask = hs; hs++; if (p->numHashBytes > 2) hs += kHash2Size; if (p->numHashBytes > 3) hs += kHash3Size; if (p->numHashBytes > 4) hs += kHash4Size; } { uint32_t newSize; p->historySize = historySize; p->hashSizeSum = hs; p->cyclicBufferSize = newCyclicBufferSize; p->numSons = (p->btMode ? newCyclicBufferSize * 2 : newCyclicBufferSize); newSize = p->hashSizeSum + p->numSons; p->hash = AllocRefs(newSize); if (p->hash != 0) { uint32_t i; p->son = p->hash + p->hashSizeSum; for (i = 0; i < p->hashSizeSum; i++) p->hash[i] = kEmptyHashValue; p->cyclicBufferPos = 0; p->pos = p->streamPos = p->cyclicBufferSize; p->result = SZ_OK; p->streamEndWasReached = false; Mf_ReadBlock(p); Mf_SetLimits(p); return 1; } } } Mf_Free(p); return 0; } static void Mf_Normalize3(uint32_t subValue, CLzRef *items, uint32_t numItems) { uint32_t i; for (i = 0; i < numItems; i++) { uint32_t value = items[i]; if (value <= subValue) value = kEmptyHashValue; else value -= subValue; items[i] = value; } } static void Mf_Normalize(CMatchFinder *p) { uint32_t subValue = (p->pos - p->historySize - 1) & kNormalizeMask; Mf_Normalize3(subValue, p->hash, p->hashSizeSum + p->numSons); p->posLimit -= subValue; p->pos -= subValue; p->streamPos -= subValue; } static void Mf_CheckLimits(CMatchFinder *p) { if (p->pos == kMaxValForNormalize) Mf_Normalize(p); if (!p->streamEndWasReached && p->keepSizeAfter == p->streamPos - p->pos) Mf_CheckAndMoveAndRead(p); if (p->cyclicBufferPos == p->cyclicBufferSize) p->cyclicBufferPos = 0; Mf_SetLimits(p); } static uint32_t * Hc_GetMatchesSpec(uint32_t lenLimit, uint32_t curMatch, uint32_t pos, const uint8_t *cur, CLzRef *son, uint32_t _cyclicBufferPos, uint32_t _cyclicBufferSize, uint32_t cutValue, uint32_t *distances, uint32_t maxLen) { son[_cyclicBufferPos] = curMatch; for (;;) { uint32_t delta = pos - curMatch; if (cutValue-- == 0 || delta >= _cyclicBufferSize) return distances; { const uint8_t *pb = cur - delta; curMatch = son[_cyclicBufferPos - delta + ((delta > _cyclicBufferPos) ? _cyclicBufferSize : 0)]; if (pb[maxLen] == cur[maxLen] && *pb == *cur) { uint32_t len = 0; while (++len != lenLimit) if (pb[len] != cur[len]) break; if (maxLen < len) { *distances++ = maxLen = len; *distances++ = delta - 1; if (len == lenLimit) return distances; } } } } } static uint32_t * GetMatchesSpec1( uint32_t lenLimit, uint32_t curMatch, uint32_t pos, const uint8_t *cur, CLzRef *son, uint32_t _cyclicBufferPos, uint32_t _cyclicBufferSize, uint32_t cutValue, uint32_t *distances, uint32_t maxLen ) { CLzRef *ptr0 = son + (_cyclicBufferPos << 1) + 1; CLzRef *ptr1 = son + (_cyclicBufferPos << 1); uint32_t len0 = 0, len1 = 0; for (;;) { uint32_t delta = pos - curMatch; if (cutValue-- == 0 || delta >= _cyclicBufferSize) { *ptr0 = *ptr1 = kEmptyHashValue; return distances; } { CLzRef *pair = son + ((_cyclicBufferPos - delta + ((delta > _cyclicBufferPos) ? _cyclicBufferSize : 0)) << 1); const uint8_t *pb = cur - delta; uint32_t len = (len0 < len1 ? len0 : len1); if (pb[len] == cur[len]) { if (++len != lenLimit && pb[len] == cur[len]) while (++len != lenLimit) if (pb[len] != cur[len]) break; if (maxLen < len) { *distances++ = maxLen = len; *distances++ = delta - 1; if (len == lenLimit) { *ptr1 = pair[0]; *ptr0 = pair[1]; return distances; } } } if (pb[len] < cur[len]) { *ptr1 = curMatch; ptr1 = pair + 1; curMatch = *ptr1; len1 = len; } else { *ptr0 = curMatch; ptr0 = pair; curMatch = *ptr0; len0 = len; } } } } static void SkipMatchesSpec(uint32_t lenLimit, uint32_t curMatch, uint32_t pos, const uint8_t *cur, CLzRef *son, uint32_t _cyclicBufferPos, uint32_t _cyclicBufferSize, uint32_t cutValue) { CLzRef *ptr0 = son + (_cyclicBufferPos << 1) + 1; CLzRef *ptr1 = son + (_cyclicBufferPos << 1); uint32_t len0 = 0, len1 = 0; for (;;) { uint32_t delta = pos - curMatch; if (cutValue-- == 0 || delta >= _cyclicBufferSize) { *ptr0 = *ptr1 = kEmptyHashValue; return; } { CLzRef *pair = son + ((_cyclicBufferPos - delta + ((delta > _cyclicBufferPos) ? _cyclicBufferSize : 0)) << 1); const uint8_t *pb = cur - delta; uint32_t len = (len0 < len1 ? len0 : len1); if (pb[len] == cur[len]) { while (++len != lenLimit) if (pb[len] != cur[len]) break; { if (len == lenLimit) { *ptr1 = pair[0]; *ptr0 = pair[1]; return; } } } if (pb[len] < cur[len]) { *ptr1 = curMatch; ptr1 = pair + 1; curMatch = *ptr1; len1 = len; } else { *ptr0 = curMatch; ptr0 = pair; curMatch = *ptr0; len0 = len; } } } } #define MOVE_POS \ ++p->cyclicBufferPos; \ p->buffer++; \ if (++p->pos == p->posLimit) Mf_CheckLimits(p); #define MOVE_POS_RET MOVE_POS return offset; static void Mf_MovePos(CMatchFinder *p) { MOVE_POS; } #define GET_MATCHES_HEADER2(minLen, ret_op) \ uint32_t lenLimit; uint32_t hashValue; const uint8_t *cur; uint32_t curMatch; \ lenLimit = p->lenLimit; { if (lenLimit < minLen) { Mf_MovePos(p); ret_op; }} \ cur = p->buffer; #define GET_MATCHES_HEADER(minLen) GET_MATCHES_HEADER2(minLen, return 0) #define SKIP_HEADER(minLen) GET_MATCHES_HEADER2(minLen, continue) #define MF_PARAMS(p) p->pos, p->buffer, p->son, p->cyclicBufferPos, p->cyclicBufferSize, p->cutValue #define GET_MATCHES_FOOTER(offset, maxLen) \ offset = (uint32_t)(GetMatchesSpec1(lenLimit, curMatch, MF_PARAMS(p), \ distances + offset, maxLen) - distances); MOVE_POS_RET; #define SKIP_FOOTER \ SkipMatchesSpec(lenLimit, curMatch, MF_PARAMS(p)); MOVE_POS; static uint32_t Bt2_MatchFinder_GetMatches(CMatchFinder *p, uint32_t *distances) { uint32_t offset; GET_MATCHES_HEADER(2) HASH2_CALC; curMatch = p->hash[hashValue]; p->hash[hashValue] = p->pos; offset = 0; GET_MATCHES_FOOTER(offset, 1) } static uint32_t Bt3_MatchFinder_GetMatches(CMatchFinder *p, uint32_t *distances) { uint32_t hash2Value, delta2, maxLen, offset; GET_MATCHES_HEADER(3) HASH3_CALC; delta2 = p->pos - p->hash[hash2Value]; curMatch = p->hash[kFix3HashSize + hashValue]; p->hash[hash2Value] = p->hash[kFix3HashSize + hashValue] = p->pos; maxLen = 2; offset = 0; if (delta2 < p->cyclicBufferSize && *(cur - delta2) == *cur) { for (; maxLen != lenLimit; maxLen++) if (cur[(ptrdiff_t)maxLen - delta2] != cur[maxLen]) break; distances[0] = maxLen; distances[1] = delta2 - 1; offset = 2; if (maxLen == lenLimit) { SkipMatchesSpec(lenLimit, curMatch, MF_PARAMS(p)); MOVE_POS_RET; } } GET_MATCHES_FOOTER(offset, maxLen) } static uint32_t Bt4_MatchFinder_GetMatches(CMatchFinder *p, uint32_t *distances) { uint32_t hash2Value, hash3Value, delta2, delta3, maxLen, offset; GET_MATCHES_HEADER(4) HASH4_CALC; delta2 = p->pos - p->hash[ hash2Value]; delta3 = p->pos - p->hash[kFix3HashSize + hash3Value]; curMatch = p->hash[kFix4HashSize + hashValue]; p->hash[ hash2Value] = p->hash[kFix3HashSize + hash3Value] = p->hash[kFix4HashSize + hashValue] = p->pos; maxLen = 1; offset = 0; if (delta2 < p->cyclicBufferSize && *(cur - delta2) == *cur) { distances[0] = maxLen = 2; distances[1] = delta2 - 1; offset = 2; } if (delta2 != delta3 && delta3 < p->cyclicBufferSize && *(cur - delta3) == *cur) { maxLen = 3; distances[offset + 1] = delta3 - 1; offset += 2; delta2 = delta3; } if (offset != 0) { for (; maxLen != lenLimit; maxLen++) if (cur[(ptrdiff_t)maxLen - delta2] != cur[maxLen]) break; distances[offset - 2] = maxLen; if (maxLen == lenLimit) { SkipMatchesSpec(lenLimit, curMatch, MF_PARAMS(p)); MOVE_POS_RET; } } if (maxLen < 3) maxLen = 3; GET_MATCHES_FOOTER(offset, maxLen) } static uint32_t Hc4_MatchFinder_GetMatches(CMatchFinder *p, uint32_t *distances) { uint32_t hash2Value, hash3Value, delta2, delta3, maxLen, offset; GET_MATCHES_HEADER(4) HASH4_CALC; delta2 = p->pos - p->hash[ hash2Value]; delta3 = p->pos - p->hash[kFix3HashSize + hash3Value]; curMatch = p->hash[kFix4HashSize + hashValue]; p->hash[ hash2Value] = p->hash[kFix3HashSize + hash3Value] = p->hash[kFix4HashSize + hashValue] = p->pos; maxLen = 1; offset = 0; if (delta2 < p->cyclicBufferSize && *(cur - delta2) == *cur) { distances[0] = maxLen = 2; distances[1] = delta2 - 1; offset = 2; } if (delta2 != delta3 && delta3 < p->cyclicBufferSize && *(cur - delta3) == *cur) { maxLen = 3; distances[offset + 1] = delta3 - 1; offset += 2; delta2 = delta3; } if (offset != 0) { for (; maxLen != lenLimit; maxLen++) if (cur[(ptrdiff_t)maxLen - delta2] != cur[maxLen]) break; distances[offset - 2] = maxLen; if (maxLen == lenLimit) { p->son[p->cyclicBufferPos] = curMatch; MOVE_POS_RET; } } if (maxLen < 3) maxLen = 3; offset = (uint32_t)(Hc_GetMatchesSpec(lenLimit, curMatch, MF_PARAMS(p), distances + offset, maxLen) - (distances)); MOVE_POS_RET } static void Bt2_MatchFinder_Skip(CMatchFinder *p, uint32_t num) { do { SKIP_HEADER(2) HASH2_CALC; curMatch = p->hash[hashValue]; p->hash[hashValue] = p->pos; SKIP_FOOTER } while (--num != 0); } static void Bt3_MatchFinder_Skip(CMatchFinder *p, uint32_t num) { do { uint32_t hash2Value; SKIP_HEADER(3) HASH3_CALC; curMatch = p->hash[kFix3HashSize + hashValue]; p->hash[hash2Value] = p->hash[kFix3HashSize + hashValue] = p->pos; SKIP_FOOTER } while (--num != 0); } static void Bt4_MatchFinder_Skip(CMatchFinder *p, uint32_t num) { do { uint32_t hash2Value, hash3Value; SKIP_HEADER(4) HASH4_CALC; curMatch = p->hash[kFix4HashSize + hashValue]; p->hash[ hash2Value] = p->hash[kFix3HashSize + hash3Value] = p->pos; p->hash[kFix4HashSize + hashValue] = p->pos; SKIP_FOOTER } while (--num != 0); } static void Hc4_MatchFinder_Skip(CMatchFinder *p, uint32_t num) { do { uint32_t hash2Value, hash3Value; SKIP_HEADER(4) HASH4_CALC; curMatch = p->hash[kFix4HashSize + hashValue]; p->hash[ hash2Value] = p->hash[kFix3HashSize + hash3Value] = p->hash[kFix4HashSize + hashValue] = p->pos; p->son[p->cyclicBufferPos] = curMatch; MOVE_POS } while (--num != 0); } void Mf_CreateVTable(CMatchFinder *p, IMatchFinder *vTable) { if (!p->btMode) { vTable->GetMatches = (Mf_GetMatches_Func)Hc4_MatchFinder_GetMatches; vTable->Skip = (Mf_Skip_Func)Hc4_MatchFinder_Skip; } else if (p->numHashBytes == 2) { vTable->GetMatches = (Mf_GetMatches_Func)Bt2_MatchFinder_GetMatches; vTable->Skip = (Mf_Skip_Func)Bt2_MatchFinder_Skip; } else if (p->numHashBytes == 3) { vTable->GetMatches = (Mf_GetMatches_Func)Bt3_MatchFinder_GetMatches; vTable->Skip = (Mf_Skip_Func)Bt3_MatchFinder_Skip; } else { vTable->GetMatches = (Mf_GetMatches_Func)Bt4_MatchFinder_GetMatches; vTable->Skip = (Mf_Skip_Func)Bt4_MatchFinder_Skip; } } /* LzmaEnc.h -- LZMA Encoder 2009-02-07 : Igor Pavlov : Public domain */ /* ---------- CLzmaEncHandle Interface ---------- */ /* LzmaEnc_* functions can return the following exit codes: Returns: SZ_OK - OK SZ_ERROR_WRITE - Write callback error. */ typedef void * CLzmaEncHandle; CLzmaEncHandle LzmaEnc_Init( const int dict_size, const int match_len_limit, const int infd, const int outfd ); void LzmaEnc_Free(CLzmaEncHandle p); int LzmaEnc_Encode(CLzmaEncHandle p); /* LzmaEnc.c -- LZMA Encoder 2009-11-24 : Igor Pavlov : Public domain */ #ifdef SHOW_STAT static int ttt = 0; #endif static int verbosity = 0; enum { Fh_size = 6, // file header size Ft_size = 20, // file trailer size /* 0-3 CRC32 of the uncompressed data */ /* 4-11 size of the uncompressed data */ /* 12-19 member size including header and trailer */ }; typedef uint8_t File_trailer[Ft_size]; static inline void Ft_set_data_crc( File_trailer data, unsigned crc ) { for( int i = 0; i <= 3; ++i ) { data[i] = (uint8_t)crc; crc >>= 8; } } static inline void Ft_set_data_size( File_trailer data, unsigned long long sz ) { for( int i = 4; i <= 11; ++i ) { data[i] = (uint8_t)sz; sz >>= 8; } } static inline void Ft_set_member_size( File_trailer data, unsigned long long sz ) { for( int i = 12; i <= 19; ++i ) { data[i] = (uint8_t)sz; sz >>= 8; } } #define kNumTopBits 24 #define kTopValue ((uint32_t)1 << kNumTopBits) #define kNumBitModelTotalBits 11 #define kBitModelTotal (1 << kNumBitModelTotalBits) #define kNumMoveBits 5 #define kProbInitValue (kBitModelTotal >> 1) #define kNumMoveReducingBits 4 #define kNumBitPriceShiftBits 4 #define kNumLogBits (9 + (int)sizeof(uint32_t) / 2) #define kDicLogSizeMaxCompress ((kNumLogBits - 1) * 2 + 7) static void LzmaEnc_FastPosInit(uint8_t *g_FastPos) { int c = 2, slotFast; g_FastPos[0] = 0; g_FastPos[1] = 1; for (slotFast = 2; slotFast < kNumLogBits * 2; slotFast++) { uint32_t k = (1 << ((slotFast >> 1) - 1)); uint32_t j; for (j = 0; j < k; j++, c++) g_FastPos[c] = (uint8_t)slotFast; } } #define BSR2_RET(pos, res) { uint32_t i = 6 + ((kNumLogBits - 1) & \ (0 - (((((uint32_t)1 << (kNumLogBits + 6)) - 1) - pos) >> 31))); \ res = p->g_FastPos[pos >> i] + (i * 2); } /* #define BSR2_RET(pos, res) { res = (pos < (1 << (kNumLogBits + 6))) ? \ p->g_FastPos[pos >> 6] + 12 : \ p->g_FastPos[pos >> (6 + kNumLogBits - 1)] + (6 + (kNumLogBits - 1)) * 2; } */ #define GetPosSlot1(pos) p->g_FastPos[pos] #define GetPosSlot2(pos, res) { BSR2_RET(pos, res); } #define GetPosSlot(pos, res) { if (pos < kNumFullDistances) res = p->g_FastPos[pos]; else BSR2_RET(pos, res); } #define LZMA_NUM_REPS 4 typedef struct { uint32_t price; State state; uint32_t posPrev2; uint32_t backPrev2; uint32_t posPrev; uint32_t backPrev; uint32_t backs[LZMA_NUM_REPS]; bool prev1IsChar; bool prev2; } COptimal; #define kNumOpts (1 << 12) #define kNumLenToPosStates 4 #define kNumPosSlotBits 6 #define kDicLogSizeMin 0 #define kDicLogSizeMax 32 #define kDistTableSizeMax (kDicLogSizeMax * 2) #define kNumAlignBits 4 #define kAlignTableSize (1 << kNumAlignBits) #define kAlignMask (kAlignTableSize - 1) #define kStartPosModelIndex 4 #define kEndPosModelIndex 14 #define kNumPosModels (kEndPosModelIndex - kStartPosModelIndex) #define kNumFullDistances (1 << (kEndPosModelIndex >> 1)) #define LZMA_LC_MAX 8 #define LZMA_LP_MAX 4 #define LZMA_PB_MAX 4 #define LZMA_NUM_PB_STATES_MAX (1 << LZMA_PB_MAX) #define kLenNumLowBits 3 #define kLenNumLowSymbols (1 << kLenNumLowBits) #define kLenNumMidBits 3 #define kLenNumMidSymbols (1 << kLenNumMidBits) #define kLenNumHighBits 8 #define kLenNumHighSymbols (1 << kLenNumHighBits) #define kLenNumSymbolsTotal (kLenNumLowSymbols + kLenNumMidSymbols + kLenNumHighSymbols) #define LZMA_MATCH_LEN_MIN 2 #define LZMA_MATCH_LEN_MAX (LZMA_MATCH_LEN_MIN + kLenNumSymbolsTotal - 1) #define kNumStates 12 typedef struct { int choice; int choice2; int low[LZMA_NUM_PB_STATES_MAX << kLenNumLowBits]; int mid[LZMA_NUM_PB_STATES_MAX << kLenNumMidBits]; int high[kLenNumHighSymbols]; } CLenEnc; typedef struct { CLenEnc p; uint32_t prices[LZMA_NUM_PB_STATES_MAX][kLenNumSymbolsTotal]; uint32_t tableSize; uint32_t counters[LZMA_NUM_PB_STATES_MAX]; } CLenPriceEnc; typedef struct { uint64_t low; uint64_t processed; uint8_t *bufBase; uint8_t *buf; uint8_t *bufLim; uint32_t range; uint32_t cacheSize; int outfd; int res; uint8_t cache; } CRangeEnc; typedef struct { uint64_t nowPos64; int *litProbs; IMatchFinder matchFinder; CMatchFinder matchFinderBase; uint32_t optimumEndIndex; uint32_t optimumCurrentIndex; uint32_t longestMatchLength; uint32_t numPairs; uint32_t numAvail; COptimal opt[kNumOpts]; uint8_t g_FastPos[1 << kNumLogBits]; uint32_t ProbPrices[kBitModelTotal >> kNumMoveReducingBits]; uint32_t matches[LZMA_MATCH_LEN_MAX * 2 + 2 + 1]; uint32_t numFastBytes; uint32_t additionalOffset; uint32_t reps[LZMA_NUM_REPS]; State state; uint32_t posSlotPrices[kNumLenToPosStates][kDistTableSizeMax]; uint32_t distancesPrices[kNumLenToPosStates][kNumFullDistances]; uint32_t alignPrices[kAlignTableSize]; uint32_t alignPriceCount; uint32_t distTableSize; unsigned lc, lp, pb; unsigned lpMask, pbMask; int isMatch[kNumStates][LZMA_NUM_PB_STATES_MAX]; int isRep[kNumStates]; int isRepG0[kNumStates]; int isRepG1[kNumStates]; int isRepG2[kNumStates]; int isRep0Long[kNumStates][LZMA_NUM_PB_STATES_MAX]; int posSlotEncoder[kNumLenToPosStates][1 << kNumPosSlotBits]; int posEncoders[kNumFullDistances - kEndPosModelIndex]; int posAlignEncoder[1 << kNumAlignBits]; CLenPriceEnc lenEnc; CLenPriceEnc repLenEnc; CRangeEnc rc; uint32_t matchPriceCount; int result; uint32_t dictSize; bool fastMode; bool finished; } CLzmaEnc; static const int kLiteralNextStates[kNumStates] = {0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 4, 5}; static const int kMatchNextStates[kNumStates] = {7, 7, 7, 7, 7, 7, 7, 10, 10, 10, 10, 10}; static const int kRepNextStates[kNumStates] = {8, 8, 8, 8, 8, 8, 8, 11, 11, 11, 11, 11}; static const int kShortRepNextStates[kNumStates]= {9, 9, 9, 9, 9, 9, 9, 11, 11, 11, 11, 11}; #define IsCharState(s) ((s) < 7) #define GetLenToPosState(len) (((len) < kNumLenToPosStates + 1) ? (len) - 2 : kNumLenToPosStates - 1) #define kInfinityPrice (1 << 30) #define RC_BUF_SIZE (1 << 16) static int RangeEnc_Init( CRangeEnc *p, const int outfd ) { p->low = 0; p->processed = 0; p->range = 0xFFFFFFFF; p->cacheSize = 1; p->outfd = outfd; p->res = SZ_OK; p->cache = 0; p->buf = p->bufBase = (uint8_t *)LZMA_MALLOC( RC_BUF_SIZE ); if( !p->bufBase ) return 0; p->bufLim = p->bufBase + RC_BUF_SIZE; return 1; } static void RangeEnc_Free(CRangeEnc *p) { LZMA_FREE(p->bufBase); p->bufBase = 0; } static void RangeEnc_FlushStream(CRangeEnc *p) { int num; if (p->res != SZ_OK) return; num = p->buf - p->bufBase; if (num != writeblock(p->outfd, p->bufBase, num)) p->res = SZ_ERROR_WRITE; p->processed += num; p->buf = p->bufBase; } static void RangeEnc_ShiftLow(CRangeEnc *p) { if ((uint32_t)p->low < (uint32_t)0xFF000000 || (int)(p->low >> 32) != 0) { uint8_t temp = p->cache; do { uint8_t *buf = p->buf; *buf++ = (uint8_t)(temp + (uint8_t)(p->low >> 32)); p->buf = buf; if (buf == p->bufLim) RangeEnc_FlushStream(p); temp = 0xFF; } while (--p->cacheSize != 0); p->cache = (uint8_t)((uint32_t)p->low >> 24); } p->cacheSize++; p->low = (uint32_t)p->low << 8; } static void RangeEnc_FlushData(CRangeEnc *p) { int i; for (i = 0; i < 5; i++) RangeEnc_ShiftLow(p); } static void RangeEnc_EncodeDirectBits(CRangeEnc *p, uint32_t value, int numBits) { do { p->range >>= 1; p->low += p->range & (0 - ((value >> --numBits) & 1)); if (p->range < kTopValue) { p->range <<= 8; RangeEnc_ShiftLow(p); } } while (numBits != 0); } static void RangeEnc_EncodeBit(CRangeEnc *p, int *prob, uint32_t symbol) { uint32_t ttt = *prob; uint32_t newBound = (p->range >> kNumBitModelTotalBits) * ttt; if (symbol == 0) { p->range = newBound; ttt += (kBitModelTotal - ttt) >> kNumMoveBits; } else { p->low += newBound; p->range -= newBound; ttt -= ttt >> kNumMoveBits; } *prob = (int)ttt; if (p->range < kTopValue) { p->range <<= 8; RangeEnc_ShiftLow(p); } } static void LitEnc_Encode(CRangeEnc *p, int *probs, uint32_t symbol) { symbol |= 0x100; do { RangeEnc_EncodeBit(p, probs + (symbol >> 8), (symbol >> 7) & 1); symbol <<= 1; } while (symbol < 0x10000); } static void LitEnc_EncodeMatched(CRangeEnc *p, int *probs, uint32_t symbol, uint32_t matchByte) { uint32_t offs = 0x100; symbol |= 0x100; do { matchByte <<= 1; RangeEnc_EncodeBit(p, probs + (offs + (matchByte & offs) + (symbol >> 8)), (symbol >> 7) & 1); symbol <<= 1; offs &= ~(matchByte ^ symbol); } while (symbol < 0x10000); } static void LzmaEnc_InitPriceTables(uint32_t *ProbPrices) { uint32_t i; for (i = (1 << kNumMoveReducingBits) / 2; i < kBitModelTotal; i += (1 << kNumMoveReducingBits)) { const int kCyclesBits = kNumBitPriceShiftBits; uint32_t w = i; uint32_t bitCount = 0; int j; for (j = 0; j < kCyclesBits; j++) { w = w * w; bitCount <<= 1; while (w >= ((uint32_t)1 << 16)) { w >>= 1; bitCount++; } } ProbPrices[i >> kNumMoveReducingBits] = ((kNumBitModelTotalBits << kCyclesBits) - 15 - bitCount); } } #define GET_PRICE(prob, symbol) \ p->ProbPrices[((prob) ^ (((-(int)(symbol))) & (kBitModelTotal - 1))) >> kNumMoveReducingBits]; #define GET_PRICEa(prob, symbol) \ ProbPrices[((prob) ^ ((-((int)(symbol))) & (kBitModelTotal - 1))) >> kNumMoveReducingBits]; #define GET_PRICE_0(prob) p->ProbPrices[(prob) >> kNumMoveReducingBits] #define GET_PRICE_1(prob) p->ProbPrices[((prob) ^ (kBitModelTotal - 1)) >> kNumMoveReducingBits] #define GET_PRICE_0a(prob) ProbPrices[(prob) >> kNumMoveReducingBits] #define GET_PRICE_1a(prob) ProbPrices[((prob) ^ (kBitModelTotal - 1)) >> kNumMoveReducingBits] static uint32_t LitEnc_GetPrice(const int *probs, uint32_t symbol, uint32_t *ProbPrices) { uint32_t price = 0; symbol |= 0x100; do { price += GET_PRICEa(probs[symbol >> 8], (symbol >> 7) & 1); symbol <<= 1; } while (symbol < 0x10000); return price; } static uint32_t LitEnc_GetPriceMatched(const int *probs, uint32_t symbol, uint32_t matchByte, uint32_t *ProbPrices) { uint32_t price = 0; uint32_t offs = 0x100; symbol |= 0x100; do { matchByte <<= 1; price += GET_PRICEa(probs[offs + (matchByte & offs) + (symbol >> 8)], (symbol >> 7) & 1); symbol <<= 1; offs &= ~(matchByte ^ symbol); } while (symbol < 0x10000); return price; } static void RcTree_Encode(CRangeEnc *rc, int *probs, int numBitLevels, uint32_t symbol) { uint32_t m = 1; int i; for (i = numBitLevels; i != 0;) { uint32_t bit; i--; bit = (symbol >> i) & 1; RangeEnc_EncodeBit(rc, probs + m, bit); m = (m << 1) | bit; } } static void RcTree_ReverseEncode(CRangeEnc *rc, int *probs, int numBitLevels, uint32_t symbol) { uint32_t m = 1; int i; for (i = 0; i < numBitLevels; i++) { uint32_t bit = symbol & 1; RangeEnc_EncodeBit(rc, probs + m, bit); m = (m << 1) | bit; symbol >>= 1; } } static uint32_t RcTree_GetPrice(const int *probs, int numBitLevels, uint32_t symbol, uint32_t *ProbPrices) { uint32_t price = 0; symbol |= (1 << numBitLevels); while (symbol != 1) { price += GET_PRICEa(probs[symbol >> 1], symbol & 1); symbol >>= 1; } return price; } static uint32_t RcTree_ReverseGetPrice(const int *probs, int numBitLevels, uint32_t symbol, uint32_t *ProbPrices) { uint32_t price = 0; uint32_t m = 1; int i; for (i = numBitLevels; i != 0; i--) { uint32_t bit = symbol & 1; symbol >>= 1; price += GET_PRICEa(probs[m], bit); m = (m << 1) | bit; } return price; } static void LenEnc_Init(CLenEnc *p) { unsigned i; p->choice = p->choice2 = kProbInitValue; for (i = 0; i < (LZMA_NUM_PB_STATES_MAX << kLenNumLowBits); i++) p->low[i] = kProbInitValue; for (i = 0; i < (LZMA_NUM_PB_STATES_MAX << kLenNumMidBits); i++) p->mid[i] = kProbInitValue; for (i = 0; i < kLenNumHighSymbols; i++) p->high[i] = kProbInitValue; } static void LenEnc_Encode(CLenEnc *p, CRangeEnc *rc, uint32_t symbol, uint32_t posState) { if (symbol < kLenNumLowSymbols) { RangeEnc_EncodeBit(rc, &p->choice, 0); RcTree_Encode(rc, p->low + (posState << kLenNumLowBits), kLenNumLowBits, symbol); } else { RangeEnc_EncodeBit(rc, &p->choice, 1); if (symbol < kLenNumLowSymbols + kLenNumMidSymbols) { RangeEnc_EncodeBit(rc, &p->choice2, 0); RcTree_Encode(rc, p->mid + (posState << kLenNumMidBits), kLenNumMidBits, symbol - kLenNumLowSymbols); } else { RangeEnc_EncodeBit(rc, &p->choice2, 1); RcTree_Encode(rc, p->high, kLenNumHighBits, symbol - kLenNumLowSymbols - kLenNumMidSymbols); } } } static void LenEnc_SetPrices(CLenEnc *p, uint32_t posState, uint32_t numSymbols, uint32_t *prices, uint32_t *ProbPrices) { uint32_t a0 = GET_PRICE_0a(p->choice); uint32_t a1 = GET_PRICE_1a(p->choice); uint32_t b0 = a1 + GET_PRICE_0a(p->choice2); uint32_t b1 = a1 + GET_PRICE_1a(p->choice2); uint32_t i = 0; for (i = 0; i < kLenNumLowSymbols; i++) { if (i >= numSymbols) return; prices[i] = a0 + RcTree_GetPrice(p->low + (posState << kLenNumLowBits), kLenNumLowBits, i, ProbPrices); } for (; i < kLenNumLowSymbols + kLenNumMidSymbols; i++) { if (i >= numSymbols) return; prices[i] = b0 + RcTree_GetPrice(p->mid + (posState << kLenNumMidBits), kLenNumMidBits, i - kLenNumLowSymbols, ProbPrices); } for (; i < numSymbols; i++) prices[i] = b1 + RcTree_GetPrice(p->high, kLenNumHighBits, i - kLenNumLowSymbols - kLenNumMidSymbols, ProbPrices); } static void LenPriceEnc_UpdateTable(CLenPriceEnc *p, uint32_t posState, uint32_t *ProbPrices) { LenEnc_SetPrices(&p->p, posState, p->tableSize, p->prices[posState], ProbPrices); p->counters[posState] = p->tableSize; } static void LenPriceEnc_UpdateTables(CLenPriceEnc *p, uint32_t numPosStates, uint32_t *ProbPrices) { uint32_t posState; for (posState = 0; posState < numPosStates; posState++) LenPriceEnc_UpdateTable(p, posState, ProbPrices); } static void LenEnc_Encode2(CLenPriceEnc *p, CRangeEnc *rc, uint32_t symbol, uint32_t posState, bool updatePrice, uint32_t *ProbPrices) { LenEnc_Encode(&p->p, rc, symbol, posState); if (updatePrice) if (--p->counters[posState] == 0) LenPriceEnc_UpdateTable(p, posState, ProbPrices); } static void MovePos(CLzmaEnc *p, uint32_t num) { #ifdef SHOW_STAT ttt += num; printf("\n MovePos %d", num); #endif if (num != 0) { p->additionalOffset += num; p->matchFinder.Skip(&p->matchFinderBase, num); } } static uint32_t ReadMatchDistances(CLzmaEnc *p, uint32_t *numDistancePairsRes) { uint32_t lenRes = 0, numPairs; p->numAvail = Mf_GetNumAvailableBytes(&p->matchFinderBase); numPairs = p->matchFinder.GetMatches(&p->matchFinderBase, p->matches); #ifdef SHOW_STAT printf("\n i = %d numPairs = %d ", ttt, numPairs / 2); ttt++; { uint32_t i; for (i = 0; i < numPairs; i += 2) printf("%2d %6d | ", p->matches[i], p->matches[i + 1]); } #endif if (numPairs > 0) { lenRes = p->matches[numPairs - 2]; if (lenRes == p->numFastBytes) { const uint8_t *pby = Mf_GetPointerToCurrentPos(&p->matchFinderBase) - 1; uint32_t distance = p->matches[numPairs - 1] + 1; uint32_t numAvail = p->numAvail; if (numAvail > LZMA_MATCH_LEN_MAX) numAvail = LZMA_MATCH_LEN_MAX; { const uint8_t *pby2 = pby - distance; for (; lenRes < numAvail && pby[lenRes] == pby2[lenRes]; lenRes++) ; } } } p->additionalOffset++; *numDistancePairsRes = numPairs; return lenRes; } #define MakeAsChar(p) (p)->backPrev = (uint32_t)(-1); (p)->prev1IsChar = false; #define MakeAsShortRep(p) (p)->backPrev = 0; (p)->prev1IsChar = false; #define IsShortRep(p) ((p)->backPrev == 0) static uint32_t GetRepLen1Price(CLzmaEnc *p, State state, uint32_t posState) { return GET_PRICE_0(p->isRepG0[state]) + GET_PRICE_0(p->isRep0Long[state][posState]); } static uint32_t GetPureRepPrice(CLzmaEnc *p, uint32_t repIndex, State state, uint32_t posState) { uint32_t price; if (repIndex == 0) { price = GET_PRICE_0(p->isRepG0[state]); price += GET_PRICE_1(p->isRep0Long[state][posState]); } else { price = GET_PRICE_1(p->isRepG0[state]); if (repIndex == 1) price += GET_PRICE_0(p->isRepG1[state]); else { price += GET_PRICE_1(p->isRepG1[state]); price += GET_PRICE(p->isRepG2[state], repIndex - 2); } } return price; } static uint32_t GetRepPrice(CLzmaEnc *p, uint32_t repIndex, uint32_t len, State state, uint32_t posState) { return p->repLenEnc.prices[posState][len - LZMA_MATCH_LEN_MIN] + GetPureRepPrice(p, repIndex, state, posState); } static uint32_t Backward(CLzmaEnc *p, uint32_t *backRes, uint32_t cur) { uint32_t posMem = p->opt[cur].posPrev; uint32_t backMem = p->opt[cur].backPrev; p->optimumEndIndex = cur; do { if (p->opt[cur].prev1IsChar) { MakeAsChar(&p->opt[posMem]) p->opt[posMem].posPrev = posMem - 1; if (p->opt[cur].prev2) { p->opt[posMem - 1].prev1IsChar = false; p->opt[posMem - 1].posPrev = p->opt[cur].posPrev2; p->opt[posMem - 1].backPrev = p->opt[cur].backPrev2; } } { uint32_t posPrev = posMem; uint32_t backCur = backMem; backMem = p->opt[posPrev].backPrev; posMem = p->opt[posPrev].posPrev; p->opt[posPrev].backPrev = backCur; p->opt[posPrev].posPrev = cur; cur = posPrev; } } while (cur != 0); *backRes = p->opt[0].backPrev; p->optimumCurrentIndex = p->opt[0].posPrev; return p->optimumCurrentIndex; } #define LIT_PROBS(pos, prevByte) (p->litProbs + ((((pos) & p->lpMask) << p->lc) + ((prevByte) >> (8 - p->lc))) * 0x300) static uint32_t GetOptimum(CLzmaEnc *p, uint32_t position, uint32_t *backRes) { uint32_t numAvail, mainLen, numPairs, repMaxIndex, i, posState, lenEnd, len, cur; uint32_t matchPrice, repMatchPrice, normalMatchPrice; uint32_t reps[LZMA_NUM_REPS], repLens[LZMA_NUM_REPS]; uint32_t *matches; const uint8_t *data; uint8_t curByte, matchByte; if (p->optimumEndIndex != p->optimumCurrentIndex) { const COptimal *opt = &p->opt[p->optimumCurrentIndex]; uint32_t lenRes = opt->posPrev - p->optimumCurrentIndex; *backRes = opt->backPrev; p->optimumCurrentIndex = opt->posPrev; return lenRes; } p->optimumCurrentIndex = p->optimumEndIndex = 0; if (p->additionalOffset == 0) mainLen = ReadMatchDistances(p, &numPairs); else { mainLen = p->longestMatchLength; numPairs = p->numPairs; } numAvail = p->numAvail; if (numAvail < 2) { *backRes = (uint32_t)(-1); return 1; } if (numAvail > LZMA_MATCH_LEN_MAX) numAvail = LZMA_MATCH_LEN_MAX; data = Mf_GetPointerToCurrentPos(&p->matchFinderBase) - 1; repMaxIndex = 0; for (i = 0; i < LZMA_NUM_REPS; i++) { uint32_t lenTest; const uint8_t *data2; reps[i] = p->reps[i]; data2 = data - (reps[i] + 1); if (data[0] != data2[0] || data[1] != data2[1]) { repLens[i] = 0; continue; } for (lenTest = 2; lenTest < numAvail && data[lenTest] == data2[lenTest]; lenTest++) ; repLens[i] = lenTest; if (lenTest > repLens[repMaxIndex]) repMaxIndex = i; } if (repLens[repMaxIndex] >= p->numFastBytes) { uint32_t lenRes; *backRes = repMaxIndex; lenRes = repLens[repMaxIndex]; MovePos(p, lenRes - 1); return lenRes; } matches = p->matches; if (mainLen >= p->numFastBytes) { *backRes = matches[numPairs - 1] + LZMA_NUM_REPS; MovePos(p, mainLen - 1); return mainLen; } curByte = *data; matchByte = *(data - (reps[0] + 1)); if (mainLen < 2 && curByte != matchByte && repLens[repMaxIndex] < 2) { *backRes = (uint32_t)-1; return 1; } p->opt[0].state = p->state; posState = (position & p->pbMask); { const int *probs = LIT_PROBS(position, *(data - 1)); p->opt[1].price = GET_PRICE_0(p->isMatch[p->state][posState]) + (!IsCharState(p->state) ? LitEnc_GetPriceMatched(probs, curByte, matchByte, p->ProbPrices) : LitEnc_GetPrice(probs, curByte, p->ProbPrices)); } MakeAsChar(&p->opt[1]); matchPrice = GET_PRICE_1(p->isMatch[p->state][posState]); repMatchPrice = matchPrice + GET_PRICE_1(p->isRep[p->state]); if (matchByte == curByte) { uint32_t shortRepPrice = repMatchPrice + GetRepLen1Price(p, p->state, posState); if (shortRepPrice < p->opt[1].price) { p->opt[1].price = shortRepPrice; MakeAsShortRep(&p->opt[1]); } } lenEnd = ((mainLen >= repLens[repMaxIndex]) ? mainLen : repLens[repMaxIndex]); if (lenEnd < 2) { *backRes = p->opt[1].backPrev; return 1; } p->opt[1].posPrev = 0; for (i = 0; i < LZMA_NUM_REPS; i++) p->opt[0].backs[i] = reps[i]; len = lenEnd; do p->opt[len--].price = kInfinityPrice; while (len >= 2); for (i = 0; i < LZMA_NUM_REPS; i++) { uint32_t repLen = repLens[i]; uint32_t price; if (repLen < 2) continue; price = repMatchPrice + GetPureRepPrice(p, i, p->state, posState); do { uint32_t curAndLenPrice = price + p->repLenEnc.prices[posState][repLen - 2]; COptimal *opt = &p->opt[repLen]; if (curAndLenPrice < opt->price) { opt->price = curAndLenPrice; opt->posPrev = 0; opt->backPrev = i; opt->prev1IsChar = false; } } while (--repLen >= 2); } normalMatchPrice = matchPrice + GET_PRICE_0(p->isRep[p->state]); len = ((repLens[0] >= 2) ? repLens[0] + 1 : 2); if (len <= mainLen) { uint32_t offs = 0; while (len > matches[offs]) offs += 2; for (; ; len++) { COptimal *opt; uint32_t distance = matches[offs + 1]; uint32_t curAndLenPrice = normalMatchPrice + p->lenEnc.prices[posState][len - LZMA_MATCH_LEN_MIN]; uint32_t lenToPosState = GetLenToPosState(len); if (distance < kNumFullDistances) curAndLenPrice += p->distancesPrices[lenToPosState][distance]; else { uint32_t slot; GetPosSlot2(distance, slot); curAndLenPrice += p->alignPrices[distance & kAlignMask] + p->posSlotPrices[lenToPosState][slot]; } opt = &p->opt[len]; if (curAndLenPrice < opt->price) { opt->price = curAndLenPrice; opt->posPrev = 0; opt->backPrev = distance + LZMA_NUM_REPS; opt->prev1IsChar = false; } if (len == matches[offs]) { offs += 2; if (offs == numPairs) break; } } } cur = 0; #ifdef SHOW_STAT2 if (position >= 0) { unsigned i; printf("\n pos = %4X", position); for (i = cur; i <= lenEnd; i++) printf("\nprice[%4X] = %d", position - cur + i, p->opt[i].price); } #endif for (;;) { uint32_t numAvailFull, newLen, numPairs, posPrev, state, posState, startLen; uint32_t curPrice, curAnd1Price, matchPrice, repMatchPrice; bool nextIsChar; uint8_t curByte, matchByte; const uint8_t *data; COptimal *curOpt; COptimal *nextOpt; cur++; if (cur == lenEnd) return Backward(p, backRes, cur); newLen = ReadMatchDistances(p, &numPairs); if (newLen >= p->numFastBytes) { p->numPairs = numPairs; p->longestMatchLength = newLen; return Backward(p, backRes, cur); } position++; curOpt = &p->opt[cur]; posPrev = curOpt->posPrev; if (curOpt->prev1IsChar) { posPrev--; if (curOpt->prev2) { state = p->opt[curOpt->posPrev2].state; if (curOpt->backPrev2 < LZMA_NUM_REPS) state = kRepNextStates[state]; else state = kMatchNextStates[state]; } else state = p->opt[posPrev].state; state = kLiteralNextStates[state]; } else state = p->opt[posPrev].state; if (posPrev == cur - 1) { if (IsShortRep(curOpt)) state = kShortRepNextStates[state]; else state = kLiteralNextStates[state]; } else { uint32_t pos; const COptimal *prevOpt; if (curOpt->prev1IsChar && curOpt->prev2) { posPrev = curOpt->posPrev2; pos = curOpt->backPrev2; state = kRepNextStates[state]; } else { pos = curOpt->backPrev; if (pos < LZMA_NUM_REPS) state = kRepNextStates[state]; else state = kMatchNextStates[state]; } prevOpt = &p->opt[posPrev]; if (pos < LZMA_NUM_REPS) { uint32_t i; reps[0] = prevOpt->backs[pos]; for (i = 1; i <= pos; i++) reps[i] = prevOpt->backs[i - 1]; for (; i < LZMA_NUM_REPS; i++) reps[i] = prevOpt->backs[i]; } else { uint32_t i; reps[0] = (pos - LZMA_NUM_REPS); for (i = 1; i < LZMA_NUM_REPS; i++) reps[i] = prevOpt->backs[i - 1]; } } curOpt->state = state; curOpt->backs[0] = reps[0]; curOpt->backs[1] = reps[1]; curOpt->backs[2] = reps[2]; curOpt->backs[3] = reps[3]; curPrice = curOpt->price; nextIsChar = false; data = Mf_GetPointerToCurrentPos(&p->matchFinderBase) - 1; curByte = *data; matchByte = *(data - (reps[0] + 1)); posState = (position & p->pbMask); curAnd1Price = curPrice + GET_PRICE_0(p->isMatch[state][posState]); { const int *probs = LIT_PROBS(position, *(data - 1)); curAnd1Price += (!IsCharState(state) ? LitEnc_GetPriceMatched(probs, curByte, matchByte, p->ProbPrices) : LitEnc_GetPrice(probs, curByte, p->ProbPrices)); } nextOpt = &p->opt[cur + 1]; if (curAnd1Price < nextOpt->price) { nextOpt->price = curAnd1Price; nextOpt->posPrev = cur; MakeAsChar(nextOpt); nextIsChar = true; } matchPrice = curPrice + GET_PRICE_1(p->isMatch[state][posState]); repMatchPrice = matchPrice + GET_PRICE_1(p->isRep[state]); if (matchByte == curByte && !(nextOpt->posPrev < cur && nextOpt->backPrev == 0)) { uint32_t shortRepPrice = repMatchPrice + GetRepLen1Price(p, state, posState); if (shortRepPrice <= nextOpt->price) { nextOpt->price = shortRepPrice; nextOpt->posPrev = cur; MakeAsShortRep(nextOpt); nextIsChar = true; } } numAvailFull = p->numAvail; { uint32_t temp = kNumOpts - 1 - cur; if (temp < numAvailFull) numAvailFull = temp; } if (numAvailFull < 2) continue; numAvail = (numAvailFull <= p->numFastBytes ? numAvailFull : p->numFastBytes); if (!nextIsChar && matchByte != curByte) /* speed optimization */ { /* try Literal + rep0 */ uint32_t temp; uint32_t lenTest2; const uint8_t *data2 = data - (reps[0] + 1); uint32_t limit = p->numFastBytes + 1; if (limit > numAvailFull) limit = numAvailFull; for (temp = 1; temp < limit && data[temp] == data2[temp]; temp++) ; lenTest2 = temp - 1; if (lenTest2 >= 2) { State state2 = kLiteralNextStates[state]; uint32_t posStateNext = (position + 1) & p->pbMask; uint32_t nextRepMatchPrice = curAnd1Price + GET_PRICE_1(p->isMatch[state2][posStateNext]) + GET_PRICE_1(p->isRep[state2]); /* for (; lenTest2 >= 2; lenTest2--) */ { uint32_t curAndLenPrice; COptimal *opt; uint32_t offset = cur + 1 + lenTest2; while (lenEnd < offset) p->opt[++lenEnd].price = kInfinityPrice; curAndLenPrice = nextRepMatchPrice + GetRepPrice(p, 0, lenTest2, state2, posStateNext); opt = &p->opt[offset]; if (curAndLenPrice < opt->price) { opt->price = curAndLenPrice; opt->posPrev = cur + 1; opt->backPrev = 0; opt->prev1IsChar = true; opt->prev2 = false; } } } } startLen = 2; /* speed optimization */ { uint32_t repIndex; for (repIndex = 0; repIndex < LZMA_NUM_REPS; repIndex++) { uint32_t lenTest; uint32_t lenTestTemp; uint32_t price; const uint8_t *data2 = data - (reps[repIndex] + 1); if (data[0] != data2[0] || data[1] != data2[1]) continue; for (lenTest = 2; lenTest < numAvail && data[lenTest] == data2[lenTest]; lenTest++) ; while (lenEnd < cur + lenTest) p->opt[++lenEnd].price = kInfinityPrice; lenTestTemp = lenTest; price = repMatchPrice + GetPureRepPrice(p, repIndex, state, posState); do { uint32_t curAndLenPrice = price + p->repLenEnc.prices[posState][lenTest - 2]; COptimal *opt = &p->opt[cur + lenTest]; if (curAndLenPrice < opt->price) { opt->price = curAndLenPrice; opt->posPrev = cur; opt->backPrev = repIndex; opt->prev1IsChar = false; } } while (--lenTest >= 2); lenTest = lenTestTemp; if (repIndex == 0) startLen = lenTest + 1; /* if (_maxMode) */ { uint32_t lenTest2 = lenTest + 1; uint32_t limit = lenTest2 + p->numFastBytes; uint32_t nextRepMatchPrice; if (limit > numAvailFull) limit = numAvailFull; for (; lenTest2 < limit && data[lenTest2] == data2[lenTest2]; lenTest2++) ; lenTest2 -= lenTest + 1; if (lenTest2 >= 2) { State state2 = kRepNextStates[state]; uint32_t posStateNext = (position + lenTest) & p->pbMask; uint32_t curAndLenCharPrice = price + p->repLenEnc.prices[posState][lenTest - 2] + GET_PRICE_0(p->isMatch[state2][posStateNext]) + LitEnc_GetPriceMatched(LIT_PROBS(position + lenTest, data[lenTest - 1]), data[lenTest], data2[lenTest], p->ProbPrices); state2 = kLiteralNextStates[state2]; posStateNext = (position + lenTest + 1) & p->pbMask; nextRepMatchPrice = curAndLenCharPrice + GET_PRICE_1(p->isMatch[state2][posStateNext]) + GET_PRICE_1(p->isRep[state2]); /* for (; lenTest2 >= 2; lenTest2--) */ { uint32_t curAndLenPrice; COptimal *opt; uint32_t offset = cur + lenTest + 1 + lenTest2; while (lenEnd < offset) p->opt[++lenEnd].price = kInfinityPrice; curAndLenPrice = nextRepMatchPrice + GetRepPrice(p, 0, lenTest2, state2, posStateNext); opt = &p->opt[offset]; if (curAndLenPrice < opt->price) { opt->price = curAndLenPrice; opt->posPrev = cur + lenTest + 1; opt->backPrev = 0; opt->prev1IsChar = true; opt->prev2 = true; opt->posPrev2 = cur; opt->backPrev2 = repIndex; } } } } } } /* for (uint32_t lenTest = 2; lenTest <= newLen; lenTest++) */ if (newLen > numAvail) { newLen = numAvail; for (numPairs = 0; newLen > matches[numPairs]; numPairs += 2) ; matches[numPairs] = newLen; numPairs += 2; } if (newLen >= startLen) { uint32_t normalMatchPrice = matchPrice + GET_PRICE_0(p->isRep[state]); uint32_t offs, curBack, posSlot; uint32_t lenTest; while (lenEnd < cur + newLen) p->opt[++lenEnd].price = kInfinityPrice; offs = 0; while (startLen > matches[offs]) offs += 2; curBack = matches[offs + 1]; GetPosSlot2(curBack, posSlot); for (lenTest = /*2*/ startLen; ; lenTest++) { uint32_t curAndLenPrice = normalMatchPrice + p->lenEnc.prices[posState][lenTest - LZMA_MATCH_LEN_MIN]; uint32_t lenToPosState = GetLenToPosState(lenTest); COptimal *opt; if (curBack < kNumFullDistances) curAndLenPrice += p->distancesPrices[lenToPosState][curBack]; else curAndLenPrice += p->posSlotPrices[lenToPosState][posSlot] + p->alignPrices[curBack & kAlignMask]; opt = &p->opt[cur + lenTest]; if (curAndLenPrice < opt->price) { opt->price = curAndLenPrice; opt->posPrev = cur; opt->backPrev = curBack + LZMA_NUM_REPS; opt->prev1IsChar = false; } if (/*_maxMode && */lenTest == matches[offs]) { /* Try Match + Literal + Rep0 */ const uint8_t *data2 = data - (curBack + 1); uint32_t lenTest2 = lenTest + 1; uint32_t limit = lenTest2 + p->numFastBytes; uint32_t nextRepMatchPrice; if (limit > numAvailFull) limit = numAvailFull; for (; lenTest2 < limit && data[lenTest2] == data2[lenTest2]; lenTest2++) ; lenTest2 -= lenTest + 1; if (lenTest2 >= 2) { State state2 = kMatchNextStates[state]; uint32_t posStateNext = (position + lenTest) & p->pbMask; uint32_t curAndLenCharPrice = curAndLenPrice + GET_PRICE_0(p->isMatch[state2][posStateNext]) + LitEnc_GetPriceMatched(LIT_PROBS(position + lenTest, data[lenTest - 1]), data[lenTest], data2[lenTest], p->ProbPrices); state2 = kLiteralNextStates[state2]; posStateNext = (posStateNext + 1) & p->pbMask; nextRepMatchPrice = curAndLenCharPrice + GET_PRICE_1(p->isMatch[state2][posStateNext]) + GET_PRICE_1(p->isRep[state2]); /* for (; lenTest2 >= 2; lenTest2--) */ { uint32_t offset = cur + lenTest + 1 + lenTest2; uint32_t curAndLenPrice; COptimal *opt; while (lenEnd < offset) p->opt[++lenEnd].price = kInfinityPrice; curAndLenPrice = nextRepMatchPrice + GetRepPrice(p, 0, lenTest2, state2, posStateNext); opt = &p->opt[offset]; if (curAndLenPrice < opt->price) { opt->price = curAndLenPrice; opt->posPrev = cur + lenTest + 1; opt->backPrev = 0; opt->prev1IsChar = true; opt->prev2 = true; opt->posPrev2 = cur; opt->backPrev2 = curBack + LZMA_NUM_REPS; } } } offs += 2; if (offs == numPairs) break; curBack = matches[offs + 1]; if (curBack >= kNumFullDistances) GetPosSlot2(curBack, posSlot); } } } } } #define ChangePair(smallDist, bigDist) (((bigDist) >> 7) > (smallDist)) static uint32_t GetOptimumFast(CLzmaEnc *p, uint32_t *backRes) { uint32_t numAvail, mainLen, mainDist, numPairs, repIndex, repLen, i; const uint8_t *data; const uint32_t *matches; if (p->additionalOffset == 0) mainLen = ReadMatchDistances(p, &numPairs); else { mainLen = p->longestMatchLength; numPairs = p->numPairs; } numAvail = p->numAvail; *backRes = (uint32_t)-1; if (numAvail < 2) return 1; if (numAvail > LZMA_MATCH_LEN_MAX) numAvail = LZMA_MATCH_LEN_MAX; data = Mf_GetPointerToCurrentPos(&p->matchFinderBase) - 1; repLen = repIndex = 0; for (i = 0; i < LZMA_NUM_REPS; i++) { uint32_t len; const uint8_t *data2 = data - (p->reps[i] + 1); if (data[0] != data2[0] || data[1] != data2[1]) continue; for (len = 2; len < numAvail && data[len] == data2[len]; len++) ; if (len >= p->numFastBytes) { *backRes = i; MovePos(p, len - 1); return len; } if (len > repLen) { repIndex = i; repLen = len; } } matches = p->matches; if (mainLen >= p->numFastBytes) { *backRes = matches[numPairs - 1] + LZMA_NUM_REPS; MovePos(p, mainLen - 1); return mainLen; } mainDist = 0; /* for GCC */ if (mainLen >= 2) { mainDist = matches[numPairs - 1]; while (numPairs > 2 && mainLen == matches[numPairs - 4] + 1) { if (!ChangePair(matches[numPairs - 3], mainDist)) break; numPairs -= 2; mainLen = matches[numPairs - 2]; mainDist = matches[numPairs - 1]; } if (mainLen == 2 && mainDist >= 0x80) mainLen = 1; } if (repLen >= 2 && ( (repLen + 1 >= mainLen) || (repLen + 2 >= mainLen && mainDist >= (1 << 9)) || (repLen + 3 >= mainLen && mainDist >= (1 << 15)))) { *backRes = repIndex; MovePos(p, repLen - 1); return repLen; } if (mainLen < 2 || numAvail <= 2) return 1; p->longestMatchLength = ReadMatchDistances(p, &p->numPairs); if (p->longestMatchLength >= 2) { uint32_t newDistance = matches[p->numPairs - 1]; if ((p->longestMatchLength >= mainLen && newDistance < mainDist) || (p->longestMatchLength == mainLen + 1 && !ChangePair(mainDist, newDistance)) || (p->longestMatchLength > mainLen + 1) || (p->longestMatchLength + 1 >= mainLen && mainLen >= 3 && ChangePair(newDistance, mainDist))) return 1; } data = Mf_GetPointerToCurrentPos(&p->matchFinderBase) - 1; for (i = 0; i < LZMA_NUM_REPS; i++) { uint32_t len, limit; const uint8_t *data2 = data - (p->reps[i] + 1); if (data[0] != data2[0] || data[1] != data2[1]) continue; limit = mainLen - 1; for (len = 2; len < limit && data[len] == data2[len]; len++) ; if (len >= limit) return 1; } *backRes = mainDist + LZMA_NUM_REPS; MovePos(p, mainLen - 2); return mainLen; } static void LZe_full_flush(CLzmaEnc *p, uint32_t posState) { const uint32_t len = LZMA_MATCH_LEN_MIN; File_trailer trailer; RangeEnc_EncodeBit(&p->rc, &p->isMatch[p->state][posState], 1); RangeEnc_EncodeBit(&p->rc, &p->isRep[p->state], 0); p->state = kMatchNextStates[p->state]; LenEnc_Encode2(&p->lenEnc, &p->rc, len - LZMA_MATCH_LEN_MIN, posState, !p->fastMode, p->ProbPrices); RcTree_Encode(&p->rc, p->posSlotEncoder[GetLenToPosState(len)], kNumPosSlotBits, (1 << kNumPosSlotBits) - 1); RangeEnc_EncodeDirectBits(&p->rc, (((uint32_t)1 << 30) - 1) >> kNumAlignBits, 30 - kNumAlignBits); RcTree_ReverseEncode(&p->rc, p->posAlignEncoder, kNumAlignBits, kAlignMask); RangeEnc_FlushData(&p->rc); RangeEnc_FlushStream(&p->rc); Ft_set_data_crc( trailer, p->matchFinderBase.crc ^ 0xFFFFFFFFU ); Ft_set_data_size( trailer, p->nowPos64 ); Ft_set_member_size( trailer, p->rc.processed + Fh_size + Ft_size ); if( writeblock( p->rc.outfd, trailer, Ft_size ) != Ft_size ) p->rc.res = SZ_ERROR_WRITE; if( verbosity >= 1 ) { unsigned long long in_size = p->nowPos64; unsigned long long out_size = p->rc.processed + Fh_size + Ft_size; if( in_size == 0 || out_size == 0 ) fputs( " no data compressed.\n", stderr ); else fprintf( stderr, "%6.3f:1, %5.2f%% ratio, %5.2f%% saved, " "%llu in, %llu out.\n", (double)in_size / out_size, ( 100.0 * out_size ) / in_size, 100.0 - ( ( 100.0 * out_size ) / in_size ), in_size, out_size ); } } static int CheckErrors(CLzmaEnc *p) { if (p->result != SZ_OK) return p->result; if (p->rc.res != SZ_OK) p->result = SZ_ERROR_WRITE; if (p->matchFinderBase.result != SZ_OK) p->result = SZ_ERROR_READ; if (p->result != SZ_OK) p->finished = true; return p->result; } static int Flush(CLzmaEnc *p, uint32_t nowPos) { /* ReleaseMFStream(); */ p->finished = true; LZe_full_flush(p, nowPos & p->pbMask); return CheckErrors(p); } static void FillAlignPrices(CLzmaEnc *p) { uint32_t i; for (i = 0; i < kAlignTableSize; i++) p->alignPrices[i] = RcTree_ReverseGetPrice(p->posAlignEncoder, kNumAlignBits, i, p->ProbPrices); p->alignPriceCount = 0; } static void FillDistancesPrices(CLzmaEnc *p) { uint32_t tempPrices[kNumFullDistances]; uint32_t i, lenToPosState; for (i = kStartPosModelIndex; i < kNumFullDistances; i++) { uint32_t posSlot = GetPosSlot1(i); uint32_t footerBits = ((posSlot >> 1) - 1); uint32_t base = ((2 | (posSlot & 1)) << footerBits); tempPrices[i] = RcTree_ReverseGetPrice(p->posEncoders + base - posSlot - 1, footerBits, i - base, p->ProbPrices); } for (lenToPosState = 0; lenToPosState < kNumLenToPosStates; lenToPosState++) { uint32_t posSlot; const int *encoder = p->posSlotEncoder[lenToPosState]; uint32_t *posSlotPrices = p->posSlotPrices[lenToPosState]; for (posSlot = 0; posSlot < p->distTableSize; posSlot++) posSlotPrices[posSlot] = RcTree_GetPrice(encoder, kNumPosSlotBits, posSlot, p->ProbPrices); for (posSlot = kEndPosModelIndex; posSlot < p->distTableSize; posSlot++) posSlotPrices[posSlot] += ((((posSlot >> 1) - 1) - kNumAlignBits) << kNumBitPriceShiftBits); { uint32_t *distancesPrices = p->distancesPrices[lenToPosState]; uint32_t i; for (i = 0; i < kStartPosModelIndex; i++) distancesPrices[i] = posSlotPrices[i]; for (; i < kNumFullDistances; i++) distancesPrices[i] = posSlotPrices[GetPosSlot1(i)] + tempPrices[i]; } } p->matchPriceCount = 0; } static int LzmaEnc_CodeOneBlock(CLzmaEnc *p) { uint32_t nowPos32, startPos32; if (p->finished) return p->result; if( CheckErrors(p) != 0 ) return p->result; nowPos32 = (uint32_t)p->nowPos64; startPos32 = nowPos32; if (p->nowPos64 == 0) { uint32_t numPairs; uint8_t curByte; if (Mf_GetNumAvailableBytes(&p->matchFinderBase) == 0) return Flush(p, nowPos32); ReadMatchDistances(p, &numPairs); RangeEnc_EncodeBit(&p->rc, &p->isMatch[p->state][0], 0); p->state = kLiteralNextStates[p->state]; curByte = Mf_GetIndexByte(&p->matchFinderBase, 0 - p->additionalOffset); LitEnc_Encode(&p->rc, p->litProbs, curByte); p->additionalOffset--; nowPos32++; } if (Mf_GetNumAvailableBytes(&p->matchFinderBase) != 0) for (;;) { uint32_t pos, len, posState; if (p->fastMode) len = GetOptimumFast(p, &pos); else len = GetOptimum(p, nowPos32, &pos); #ifdef SHOW_STAT2 printf("\n pos = %4X, len = %d pos = %d", nowPos32, len, pos); #endif posState = nowPos32 & p->pbMask; if (len == 1 && pos == (uint32_t)-1) { uint8_t curByte; int *probs; const uint8_t *data; RangeEnc_EncodeBit(&p->rc, &p->isMatch[p->state][posState], 0); data = Mf_GetPointerToCurrentPos(&p->matchFinderBase) - p->additionalOffset; curByte = *data; probs = LIT_PROBS(nowPos32, *(data - 1)); if (IsCharState(p->state)) LitEnc_Encode(&p->rc, probs, curByte); else LitEnc_EncodeMatched(&p->rc, probs, curByte, *(data - p->reps[0] - 1)); p->state = kLiteralNextStates[p->state]; } else { RangeEnc_EncodeBit(&p->rc, &p->isMatch[p->state][posState], 1); if (pos < LZMA_NUM_REPS) { RangeEnc_EncodeBit(&p->rc, &p->isRep[p->state], 1); if (pos == 0) { RangeEnc_EncodeBit(&p->rc, &p->isRepG0[p->state], 0); RangeEnc_EncodeBit(&p->rc, &p->isRep0Long[p->state][posState], ((len == 1) ? 0 : 1)); } else { uint32_t distance = p->reps[pos]; RangeEnc_EncodeBit(&p->rc, &p->isRepG0[p->state], 1); if (pos == 1) RangeEnc_EncodeBit(&p->rc, &p->isRepG1[p->state], 0); else { RangeEnc_EncodeBit(&p->rc, &p->isRepG1[p->state], 1); RangeEnc_EncodeBit(&p->rc, &p->isRepG2[p->state], pos - 2); if (pos == 3) p->reps[3] = p->reps[2]; p->reps[2] = p->reps[1]; } p->reps[1] = p->reps[0]; p->reps[0] = distance; } if (len == 1) p->state = kShortRepNextStates[p->state]; else { LenEnc_Encode2(&p->repLenEnc, &p->rc, len - LZMA_MATCH_LEN_MIN, posState, !p->fastMode, p->ProbPrices); p->state = kRepNextStates[p->state]; } } else { uint32_t posSlot; RangeEnc_EncodeBit(&p->rc, &p->isRep[p->state], 0); p->state = kMatchNextStates[p->state]; LenEnc_Encode2(&p->lenEnc, &p->rc, len - LZMA_MATCH_LEN_MIN, posState, !p->fastMode, p->ProbPrices); pos -= LZMA_NUM_REPS; GetPosSlot(pos, posSlot); RcTree_Encode(&p->rc, p->posSlotEncoder[GetLenToPosState(len)], kNumPosSlotBits, posSlot); if (posSlot >= kStartPosModelIndex) { uint32_t footerBits = ((posSlot >> 1) - 1); uint32_t base = ((2 | (posSlot & 1)) << footerBits); uint32_t posReduced = pos - base; if (posSlot < kEndPosModelIndex) RcTree_ReverseEncode(&p->rc, p->posEncoders + base - posSlot - 1, footerBits, posReduced); else { RangeEnc_EncodeDirectBits(&p->rc, posReduced >> kNumAlignBits, footerBits - kNumAlignBits); RcTree_ReverseEncode(&p->rc, p->posAlignEncoder, kNumAlignBits, posReduced & kAlignMask); p->alignPriceCount++; } } p->reps[3] = p->reps[2]; p->reps[2] = p->reps[1]; p->reps[1] = p->reps[0]; p->reps[0] = pos; p->matchPriceCount++; } } p->additionalOffset -= len; nowPos32 += len; if (p->additionalOffset == 0) { uint32_t processed; if (!p->fastMode) { if (p->matchPriceCount >= (1 << 7)) FillDistancesPrices(p); if (p->alignPriceCount >= kAlignTableSize) FillAlignPrices(p); } if (Mf_GetNumAvailableBytes(&p->matchFinderBase) == 0) break; processed = nowPos32 - startPos32; if (processed >= (1 << 15)) { p->nowPos64 += nowPos32 - startPos32; return CheckErrors(p); } } } p->nowPos64 += nowPos32 - startPos32; return Flush(p, nowPos32); } CLzmaEncHandle LzmaEnc_Init( const int dict_size, const int match_len_limit, const int infd, const int outfd ) { int i; const uint32_t beforeSize = kNumOpts; CLzmaEnc * const p = (CLzmaEnc *)LZMA_MALLOC(sizeof(CLzmaEnc)); if( !p ) return 0; p->nowPos64 = 0; p->dictSize = dict_size; p->numFastBytes = match_len_limit; p->lc = literal_context_bits; p->lp = 0; p->pb = pos_state_bits; p->optimumEndIndex = 0; p->optimumCurrentIndex = 0; p->additionalOffset = 0; p->state = 0; p->result = SZ_OK; p->fastMode = false; p->finished = false; if (!Mf_Init(&p->matchFinderBase, infd, 16 + ( match_len_limit / 2 ), p->dictSize, beforeSize, p->numFastBytes, LZMA_MATCH_LEN_MAX)) { LZMA_FREE( p ); return 0; } Mf_CreateVTable(&p->matchFinderBase, &p->matchFinder); LzmaEnc_FastPosInit(p->g_FastPos); LzmaEnc_InitPriceTables(p->ProbPrices); for (i = 0; i < kDicLogSizeMaxCompress; i++) if (p->dictSize <= ((uint32_t)1 << i)) break; p->distTableSize = i * 2; if( !RangeEnc_Init( &p->rc, outfd ) ) { LZMA_FREE( p ); return 0; } p->litProbs = (int *)LZMA_MALLOC((0x300 << (p->lc + p->lp)) * sizeof(int)); if( !p->litProbs ) { LZMA_FREE( p ); return 0; } for (i = 0 ; i < LZMA_NUM_REPS; i++) p->reps[i] = 0; for (i = 0; i < kNumStates; i++) { int j; for (j = 0; j < LZMA_NUM_PB_STATES_MAX; j++) { p->isMatch[i][j] = kProbInitValue; p->isRep0Long[i][j] = kProbInitValue; } p->isRep[i] = kProbInitValue; p->isRepG0[i] = kProbInitValue; p->isRepG1[i] = kProbInitValue; p->isRepG2[i] = kProbInitValue; } { const int num = 0x300 << (p->lp + p->lc); for (i = 0; i < num; i++) p->litProbs[i] = kProbInitValue; } for (i = 0; i < kNumLenToPosStates; i++) { int *probs = p->posSlotEncoder[i]; uint32_t j; for (j = 0; j < (1 << kNumPosSlotBits); j++) probs[j] = kProbInitValue; } for (i = 0; i < kNumFullDistances - kEndPosModelIndex; i++) p->posEncoders[i] = kProbInitValue; LenEnc_Init(&p->lenEnc.p); LenEnc_Init(&p->repLenEnc.p); for (i = 0; i < (1 << kNumAlignBits); i++) p->posAlignEncoder[i] = kProbInitValue; p->pbMask = (1 << p->pb) - 1; p->lpMask = (1 << p->lp) - 1; if (!p->fastMode) { FillDistancesPrices(p); FillAlignPrices(p); } p->lenEnc.tableSize = p->repLenEnc.tableSize = p->numFastBytes + 1 - LZMA_MATCH_LEN_MIN; LenPriceEnc_UpdateTables(&p->lenEnc, 1 << p->pb, p->ProbPrices); LenPriceEnc_UpdateTables(&p->repLenEnc, 1 << p->pb, p->ProbPrices); return p; } void LzmaEnc_Free(CLzmaEncHandle pp) { CLzmaEnc *p = (CLzmaEnc *)pp; Mf_Free(&p->matchFinderBase); LZMA_FREE(p->litProbs); p->litProbs = 0; RangeEnc_Free(&p->rc); LZMA_FREE(p); } int LzmaEnc_Encode(CLzmaEncHandle pp) { int res = SZ_OK; CLzmaEnc *p = (CLzmaEnc *)pp; for (;;) { res = LzmaEnc_CodeOneBlock(p); if( res != SZ_OK || p->finished ) break; } return res; } /* LzmaDec.h -- LZMA Decoder 2009-02-07 : Igor Pavlov : Public domain */ /* ---------- LZMA Properties ---------- */ #define LZMA_PROPS_SIZE 5 /* ---------- LZMA Decoder state ---------- */ /* LZMA_REQUIRED_INPUT_MAX = number of required input bytes for worst case. Num bits = log2((2^11 / 31) ^ 22) + 26 < 134 + 26 = 160; */ #define LZMA_REQUIRED_INPUT_MAX 20 typedef struct { int *probs; uint8_t *dic; const uint8_t *buf; uint32_t range, code; uint32_t dicPos; uint32_t dicBufSize; uint32_t processedPos; uint32_t checkDicSize; unsigned lc, lp, pb; State state; uint32_t reps[4]; unsigned remainLen; uint32_t numProbs; unsigned tempBufSize; bool needFlush; uint8_t tempBuf[LZMA_REQUIRED_INPUT_MAX]; } CLzmaDec; /* There are two types of LZMA streams: 0) Stream with end mark. That end mark adds about 6 bytes to compressed size. 1) Stream without end mark. You must know exact uncompressed size to decompress such stream. */ typedef enum { LZMA_FINISH_ANY, /* finish at any point */ LZMA_FINISH_END /* block must be finished at the end */ } ELzmaFinishMode; /* ELzmaFinishMode has meaning only if the decoding reaches output limit !!! You must use LZMA_FINISH_END, when you know that current output buffer covers last bytes of block. In other cases you must use LZMA_FINISH_ANY. If LZMA decoder sees end marker before reaching output limit, it returns SZ_OK, and output value of destLen will be less than output buffer size limit. You can check status result also. You can use multiple checks to test data integrity after full decompression: 1) Check Result and "status" variable. 2) Check that output(destLen) = uncompressedSize, if you know real uncompressedSize. 3) Check that output(srcLen) = compressedSize, if you know real compressedSize. You must use correct finish mode in that case. */ typedef enum { LZMA_STATUS_NOT_SPECIFIED, /* use main error code instead */ LZMA_STATUS_FINISHED_WITH_MARK, /* stream was finished with end mark. */ LZMA_STATUS_NOT_FINISHED, /* stream was not finished */ LZMA_STATUS_NEEDS_MORE_INPUT, /* you must provide more input bytes */ LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK /* there is probability that stream was finished without end mark */ } ELzmaStatus; /* ELzmaStatus is used only as output value for function call */ static bool LzmaDec_Init(CLzmaDec *p, const uint8_t *raw_props); static void LzmaDec_Free(CLzmaDec *p); /* ---------- Buffer Interface ---------- */ /* It's zlib-like interface. finishMode: It has meaning only if the decoding reaches output limit (*destLen). LZMA_FINISH_ANY - Decode just destLen bytes. LZMA_FINISH_END - Stream must be finished after (*destLen). */ static bool LzmaDec_DecodeToBuf( CLzmaDec *p, uint8_t *dest, uint32_t *destLen, const uint8_t *src, uint32_t *srcLen, ELzmaFinishMode finishMode, ELzmaStatus *status ); /* LzmaDec.c -- LZMA Decoder 2009-09-20 : Igor Pavlov : Public domain */ #define kNumTopBits 24 #define kTopValue ((uint32_t)1 << kNumTopBits) #define kNumBitModelTotalBits 11 #define kBitModelTotal (1 << kNumBitModelTotalBits) #define kNumMoveBits 5 #define RC_INIT_SIZE 5 #define NORMALIZE if (range < kTopValue) { range <<= 8; code = (code << 8) | (*buf++); } #define IF_BIT_0(p) ttt = *(p); NORMALIZE; bound = (range >> kNumBitModelTotalBits) * ttt; if (code < bound) #define UPDATE_0(p) range = bound; *(p) = (int)(ttt + ((kBitModelTotal - ttt) >> kNumMoveBits)); #define UPDATE_1(p) range -= bound; code -= bound; *(p) = (int)(ttt - (ttt >> kNumMoveBits)); #define GET_BIT2(p, i, A0, A1) IF_BIT_0(p) \ { UPDATE_0(p); i = (i + i); A0; } else \ { UPDATE_1(p); i = (i + i) + 1; A1; } #define GET_BIT(p, i) GET_BIT2(p, i, ; , ;) #define TREE_GET_BIT(probs, i) { GET_BIT((probs + i), i); } #define TREE_DECODE(probs, limit, i) \ { i = 1; do { TREE_GET_BIT(probs, i); } while (i < limit); i -= limit; } /* #define _LZMA_SIZE_OPT */ #ifdef _LZMA_SIZE_OPT #define TREE_6_DECODE(probs, i) TREE_DECODE(probs, (1 << 6), i) #else #define TREE_6_DECODE(probs, i) \ { i = 1; \ TREE_GET_BIT(probs, i); \ TREE_GET_BIT(probs, i); \ TREE_GET_BIT(probs, i); \ TREE_GET_BIT(probs, i); \ TREE_GET_BIT(probs, i); \ TREE_GET_BIT(probs, i); \ i -= 0x40; } #endif #define NORMALIZE_CHECK if (range < kTopValue) { if (buf >= bufLimit) return DUMMY_ERROR; range <<= 8; code = (code << 8) | (*buf++); } #define IF_BIT_0_CHECK(p) ttt = *(p); NORMALIZE_CHECK; bound = (range >> kNumBitModelTotalBits) * ttt; if (code < bound) #define UPDATE_0_CHECK range = bound; #define UPDATE_1_CHECK range -= bound; code -= bound; #define GET_BIT2_CHECK(p, i, A0, A1) IF_BIT_0_CHECK(p) \ { UPDATE_0_CHECK; i = (i + i); A0; } else \ { UPDATE_1_CHECK; i = (i + i) + 1; A1; } #define GET_BIT_CHECK(p, i) GET_BIT2_CHECK(p, i, ; , ;) #define TREE_DECODE_CHECK(probs, limit, i) \ { i = 1; do { GET_BIT_CHECK(probs + i, i) } while (i < limit); i -= limit; } #define kNumPosBitsMax 4 #define kNumPosStatesMax (1 << kNumPosBitsMax) #define kLenNumLowBits 3 #define kLenNumLowSymbols (1 << kLenNumLowBits) #define kLenNumMidBits 3 #define kLenNumMidSymbols (1 << kLenNumMidBits) #define kLenNumHighBits 8 #define kLenNumHighSymbols (1 << kLenNumHighBits) #define LenChoice 0 #define LenChoice2 (LenChoice + 1) #define LenLow (LenChoice2 + 1) #define LenMid (LenLow + (kNumPosStatesMax << kLenNumLowBits)) #define LenHigh (LenMid + (kNumPosStatesMax << kLenNumMidBits)) #define kNumLenProbs (LenHigh + kLenNumHighSymbols) #define kNumStates 12 #define kNumLitStates 7 #define kStartPosModelIndex 4 #define kEndPosModelIndex 14 #define kNumFullDistances (1 << (kEndPosModelIndex >> 1)) #define kNumPosSlotBits 6 #define kNumLenToPosStates 4 #define kNumAlignBits 4 #define kAlignTableSize (1 << kNumAlignBits) #define kMatchMinLen 2 #define kMatchSpecLenStart (kMatchMinLen + kLenNumLowSymbols + kLenNumMidSymbols + kLenNumHighSymbols) #define IsMatch 0 #define IsRep (IsMatch + (kNumStates << kNumPosBitsMax)) #define IsRepG0 (IsRep + kNumStates) #define IsRepG1 (IsRepG0 + kNumStates) #define IsRepG2 (IsRepG1 + kNumStates) #define IsRep0Long (IsRepG2 + kNumStates) #define PosSlot (IsRep0Long + (kNumStates << kNumPosBitsMax)) #define SpecPos (PosSlot + (kNumLenToPosStates << kNumPosSlotBits)) #define Align (SpecPos + kNumFullDistances - kEndPosModelIndex) #define LenCoder (Align + kAlignTableSize) #define RepLenCoder (LenCoder + kNumLenProbs) #define Literal (RepLenCoder + kNumLenProbs) #define LZMA_BASE_SIZE 1846 #define LZMA_LIT_SIZE 768 #define LzmaProps_GetNumProbs(p) ((uint32_t)LZMA_BASE_SIZE + (LZMA_LIT_SIZE << ((p)->lc + (p)->lp))) #if Literal != LZMA_BASE_SIZE StopCompilingDueBUG #endif /* First LZMA-symbol is always decoded. And it decodes new LZMA-symbols while (buf < bufLimit), but "buf" is without last normalization Out: Result: true - OK false - Error p->remainLen: < kMatchSpecLenStart : normal remain = kMatchSpecLenStart : finished = kMatchSpecLenStart + 1 : Flush marker = kMatchSpecLenStart + 2 : State Init Marker */ static bool LzmaDec_DecodeReal(CLzmaDec *p, uint32_t limit, const uint8_t *bufLimit) { int *probs = p->probs; State state = p->state; uint32_t rep0 = p->reps[0], rep1 = p->reps[1], rep2 = p->reps[2], rep3 = p->reps[3]; unsigned pbMask = ((unsigned)1 << (p->pb)) - 1; unsigned lpMask = ((unsigned)1 << (p->lp)) - 1; const unsigned lc = p->lc; uint8_t *dic = p->dic; const uint32_t dicBufSize = p->dicBufSize; uint32_t dicPos = p->dicPos; uint32_t processedPos = p->processedPos; uint32_t checkDicSize = p->checkDicSize; unsigned len = 0; const uint8_t *buf = p->buf; uint32_t range = p->range; uint32_t code = p->code; do { int *prob; uint32_t bound; unsigned ttt; unsigned posState = processedPos & pbMask; prob = probs + IsMatch + (state << kNumPosBitsMax) + posState; IF_BIT_0(prob) { unsigned symbol; UPDATE_0(prob); prob = probs + Literal; if (checkDicSize != 0 || processedPos != 0) prob += (LZMA_LIT_SIZE * (((processedPos & lpMask) << lc) + (dic[(dicPos == 0 ? dicBufSize : dicPos) - 1] >> (8 - lc)))); if (state < kNumLitStates) { state -= (state < 4) ? state : 3; symbol = 1; do { GET_BIT(prob + symbol, symbol) } while (symbol < 0x100); } else { unsigned matchByte = p->dic[(dicPos - rep0) + ((dicPos < rep0) ? dicBufSize : 0)]; unsigned offs = 0x100; state -= (state < 10) ? 3 : 6; symbol = 1; do { unsigned bit; int *probLit; matchByte <<= 1; bit = (matchByte & offs); probLit = prob + offs + bit + symbol; GET_BIT2(probLit, symbol, offs &= ~bit, offs &= bit) } while (symbol < 0x100); } dic[dicPos++] = (uint8_t)symbol; processedPos++; continue; } else { UPDATE_1(prob); prob = probs + IsRep + state; IF_BIT_0(prob) { UPDATE_0(prob); state += kNumStates; prob = probs + LenCoder; } else { UPDATE_1(prob); if (checkDicSize == 0 && processedPos == 0) return false; prob = probs + IsRepG0 + state; IF_BIT_0(prob) { UPDATE_0(prob); prob = probs + IsRep0Long + (state << kNumPosBitsMax) + posState; IF_BIT_0(prob) { UPDATE_0(prob); dic[dicPos] = dic[(dicPos - rep0) + ((dicPos < rep0) ? dicBufSize : 0)]; dicPos++; processedPos++; state = state < kNumLitStates ? 9 : 11; continue; } UPDATE_1(prob); } else { uint32_t distance; UPDATE_1(prob); prob = probs + IsRepG1 + state; IF_BIT_0(prob) { UPDATE_0(prob); distance = rep1; } else { UPDATE_1(prob); prob = probs + IsRepG2 + state; IF_BIT_0(prob) { UPDATE_0(prob); distance = rep2; } else { UPDATE_1(prob); distance = rep3; rep3 = rep2; } rep2 = rep1; } rep1 = rep0; rep0 = distance; } state = state < kNumLitStates ? 8 : 11; prob = probs + RepLenCoder; } { unsigned limit, offset; int *probLen = prob + LenChoice; IF_BIT_0(probLen) { UPDATE_0(probLen); probLen = prob + LenLow + (posState << kLenNumLowBits); offset = 0; limit = (1 << kLenNumLowBits); } else { UPDATE_1(probLen); probLen = prob + LenChoice2; IF_BIT_0(probLen) { UPDATE_0(probLen); probLen = prob + LenMid + (posState << kLenNumMidBits); offset = kLenNumLowSymbols; limit = (1 << kLenNumMidBits); } else { UPDATE_1(probLen); probLen = prob + LenHigh; offset = kLenNumLowSymbols + kLenNumMidSymbols; limit = (1 << kLenNumHighBits); } } TREE_DECODE(probLen, limit, len); len += offset; } if (state >= kNumStates) { uint32_t distance; prob = probs + PosSlot + ((len < kNumLenToPosStates ? len : kNumLenToPosStates - 1) << kNumPosSlotBits); TREE_6_DECODE(prob, distance); if (distance >= kStartPosModelIndex) { unsigned posSlot = (unsigned)distance; int numDirectBits = (int)(((distance >> 1) - 1)); distance = (2 | (distance & 1)); if (posSlot < kEndPosModelIndex) { distance <<= numDirectBits; prob = probs + SpecPos + distance - posSlot - 1; { uint32_t mask = 1; unsigned i = 1; do { GET_BIT2(prob + i, i, ; , distance |= mask); mask <<= 1; } while (--numDirectBits != 0); } } else { numDirectBits -= kNumAlignBits; do { NORMALIZE range >>= 1; { uint32_t t; code -= range; t = (0 - ((uint32_t)code >> 31)); /* (uint32_t)((int)code >> 31) */ distance = (distance << 1) + (t + 1); code += range & t; } /* distance <<= 1; if (code >= range) { code -= range; distance |= 1; } */ } while (--numDirectBits != 0); prob = probs + Align; distance <<= kNumAlignBits; { unsigned i = 1; GET_BIT2(prob + i, i, ; , distance |= 1); GET_BIT2(prob + i, i, ; , distance |= 2); GET_BIT2(prob + i, i, ; , distance |= 4); GET_BIT2(prob + i, i, ; , distance |= 8); } if (distance == (uint32_t)0xFFFFFFFF) { len += kMatchSpecLenStart; state -= kNumStates; break; } } } rep3 = rep2; rep2 = rep1; rep1 = rep0; rep0 = distance + 1; if (checkDicSize == 0) { if (distance >= processedPos) return false; } else if (distance >= checkDicSize) return false; state = (state < kNumStates + kNumLitStates) ? kNumLitStates : kNumLitStates + 3; } len += kMatchMinLen; if (limit == dicPos) return false; { uint32_t rem = limit - dicPos; unsigned curLen = ((rem < len) ? (unsigned)rem : len); uint32_t pos = (dicPos - rep0) + ((dicPos < rep0) ? dicBufSize : 0); processedPos += curLen; len -= curLen; if (pos + curLen <= dicBufSize) { uint8_t *dest = dic + dicPos; ptrdiff_t src = (ptrdiff_t)pos - (ptrdiff_t)dicPos; const uint8_t *lim = dest + curLen; dicPos += curLen; do *(dest) = (uint8_t)*(dest + src); while (++dest != lim); } else { do { dic[dicPos++] = dic[pos]; if (++pos == dicBufSize) pos = 0; } while (--curLen != 0); } } } } while (dicPos < limit && buf < bufLimit); NORMALIZE; p->buf = buf; p->range = range; p->code = code; p->remainLen = len; p->dicPos = dicPos; p->processedPos = processedPos; p->reps[0] = rep0; p->reps[1] = rep1; p->reps[2] = rep2; p->reps[3] = rep3; p->state = state; return true; } static void LzmaDec_WriteRem(CLzmaDec *p, uint32_t limit) { if (p->remainLen != 0 && p->remainLen < kMatchSpecLenStart) { uint8_t *dic = p->dic; uint32_t dicPos = p->dicPos; const uint32_t dicBufSize = p->dicBufSize; unsigned len = p->remainLen; uint32_t rep0 = p->reps[0]; if (limit - dicPos < len) len = (unsigned)(limit - dicPos); if (p->checkDicSize == 0 && dicBufSize - p->processedPos <= len) p->checkDicSize = dicBufSize; p->processedPos += len; p->remainLen -= len; while (len-- != 0) { dic[dicPos] = dic[(dicPos - rep0) + ((dicPos < rep0) ? dicBufSize : 0)]; dicPos++; } p->dicPos = dicPos; } } static int LzmaDec_DecodeReal2(CLzmaDec *p, uint32_t limit, const uint8_t *bufLimit) { const uint32_t dicBufSize = p->dicBufSize; do { uint32_t limit2 = limit; if (p->checkDicSize == 0) { uint32_t rem = dicBufSize - p->processedPos; if (limit - p->dicPos > rem) limit2 = p->dicPos + rem; } if( !LzmaDec_DecodeReal(p, limit2, bufLimit) ) return false; if (p->processedPos >= dicBufSize) p->checkDicSize = dicBufSize; LzmaDec_WriteRem(p, limit); } while (p->dicPos < limit && p->buf < bufLimit && p->remainLen < kMatchSpecLenStart); if (p->remainLen > kMatchSpecLenStart) { p->remainLen = kMatchSpecLenStart; } return true; } typedef enum { DUMMY_ERROR, /* unexpected end of input stream */ DUMMY_LIT, DUMMY_MATCH, DUMMY_REP } ELzmaDummy; static ELzmaDummy LzmaDec_TryDummy(const CLzmaDec *p, const uint8_t *buf, uint32_t inSize) { uint32_t range = p->range; uint32_t code = p->code; const uint8_t *bufLimit = buf + inSize; int *probs = p->probs; State state = p->state; ELzmaDummy res; { int *prob; uint32_t bound; unsigned ttt; unsigned posState = (p->processedPos) & ((1 << p->pb) - 1); prob = probs + IsMatch + (state << kNumPosBitsMax) + posState; IF_BIT_0_CHECK(prob) { UPDATE_0_CHECK /* if (bufLimit - buf >= 7) return DUMMY_LIT; */ prob = probs + Literal; if (p->checkDicSize != 0 || p->processedPos != 0) prob += (LZMA_LIT_SIZE * ((((p->processedPos) & ((1 << (p->lp)) - 1)) << p->lc) + (p->dic[(p->dicPos == 0 ? p->dicBufSize : p->dicPos) - 1] >> (8 - p->lc)))); if (state < kNumLitStates) { unsigned symbol = 1; do { GET_BIT_CHECK(prob + symbol, symbol) } while (symbol < 0x100); } else { unsigned matchByte = p->dic[p->dicPos - p->reps[0] + ((p->dicPos < p->reps[0]) ? p->dicBufSize : 0)]; unsigned offs = 0x100; unsigned symbol = 1; do { unsigned bit; int *probLit; matchByte <<= 1; bit = (matchByte & offs); probLit = prob + offs + bit + symbol; GET_BIT2_CHECK(probLit, symbol, offs &= ~bit, offs &= bit) } while (symbol < 0x100); } res = DUMMY_LIT; } else { unsigned len; UPDATE_1_CHECK; prob = probs + IsRep + state; IF_BIT_0_CHECK(prob) { UPDATE_0_CHECK; state = 0; prob = probs + LenCoder; res = DUMMY_MATCH; } else { UPDATE_1_CHECK; res = DUMMY_REP; prob = probs + IsRepG0 + state; IF_BIT_0_CHECK(prob) { UPDATE_0_CHECK; prob = probs + IsRep0Long + (state << kNumPosBitsMax) + posState; IF_BIT_0_CHECK(prob) { UPDATE_0_CHECK; NORMALIZE_CHECK; return DUMMY_REP; } else { UPDATE_1_CHECK; } } else { UPDATE_1_CHECK; prob = probs + IsRepG1 + state; IF_BIT_0_CHECK(prob) { UPDATE_0_CHECK; } else { UPDATE_1_CHECK; prob = probs + IsRepG2 + state; IF_BIT_0_CHECK(prob) { UPDATE_0_CHECK; } else { UPDATE_1_CHECK; } } } state = kNumStates; prob = probs + RepLenCoder; } { unsigned limit, offset; int *probLen = prob + LenChoice; IF_BIT_0_CHECK(probLen) { UPDATE_0_CHECK; probLen = prob + LenLow + (posState << kLenNumLowBits); offset = 0; limit = 1 << kLenNumLowBits; } else { UPDATE_1_CHECK; probLen = prob + LenChoice2; IF_BIT_0_CHECK(probLen) { UPDATE_0_CHECK; probLen = prob + LenMid + (posState << kLenNumMidBits); offset = kLenNumLowSymbols; limit = 1 << kLenNumMidBits; } else { UPDATE_1_CHECK; probLen = prob + LenHigh; offset = kLenNumLowSymbols + kLenNumMidSymbols; limit = 1 << kLenNumHighBits; } } TREE_DECODE_CHECK(probLen, limit, len); len += offset; } if (state < 4) { unsigned posSlot; prob = probs + PosSlot + ((len < kNumLenToPosStates ? len : kNumLenToPosStates - 1) << kNumPosSlotBits); TREE_DECODE_CHECK(prob, 1 << kNumPosSlotBits, posSlot); if (posSlot >= kStartPosModelIndex) { int numDirectBits = ((posSlot >> 1) - 1); /* if (bufLimit - buf >= 8) return DUMMY_MATCH; */ if (posSlot < kEndPosModelIndex) { prob = probs + SpecPos + ((2 | (posSlot & 1)) << numDirectBits) - posSlot - 1; } else { numDirectBits -= kNumAlignBits; do { NORMALIZE_CHECK range >>= 1; code -= range & (((code - range) >> 31) - 1); /* if (code >= range) code -= range; */ } while (--numDirectBits != 0); prob = probs + Align; numDirectBits = kNumAlignBits; } { unsigned i = 1; do { GET_BIT_CHECK(prob + i, i); } while (--numDirectBits != 0); } } } } } NORMALIZE_CHECK; return res; } static void LzmaDec_InitRc(CLzmaDec *p, const uint8_t *data) { p->code = ((uint32_t)data[1] << 24) | ((uint32_t)data[2] << 16) | ((uint32_t)data[3] << 8) | ((uint32_t)data[4]); p->range = 0xFFFFFFFF; p->needFlush = false; } static bool LzmaDec_DecodeToDic(CLzmaDec *p, uint32_t dicLimit, const uint8_t *src, uint32_t *srcLen, ELzmaFinishMode finishMode, ELzmaStatus *status) { uint32_t inSize = *srcLen; (*srcLen) = 0; LzmaDec_WriteRem(p, dicLimit); *status = LZMA_STATUS_NOT_SPECIFIED; while (p->remainLen != kMatchSpecLenStart) { int checkEndMarkNow; if( p->needFlush ) { for (; inSize > 0 && p->tempBufSize < RC_INIT_SIZE; (*srcLen)++, inSize--) p->tempBuf[p->tempBufSize++] = *src++; if (p->tempBufSize < RC_INIT_SIZE) { *status = LZMA_STATUS_NEEDS_MORE_INPUT; return true; } if (p->tempBuf[0] != 0) return false; LzmaDec_InitRc(p, p->tempBuf); p->tempBufSize = 0; } checkEndMarkNow = 0; if (p->dicPos >= dicLimit) { if (p->remainLen == 0 && p->code == 0) { *status = LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK; return true; } if (finishMode == LZMA_FINISH_ANY) { *status = LZMA_STATUS_NOT_FINISHED; return true; } if (p->remainLen != 0) { *status = LZMA_STATUS_NOT_FINISHED; return false; } checkEndMarkNow = 1; } if (p->tempBufSize == 0) { uint32_t processed; const uint8_t *bufLimit; if (inSize < LZMA_REQUIRED_INPUT_MAX || checkEndMarkNow) { int dummyRes = LzmaDec_TryDummy(p, src, inSize); if (dummyRes == DUMMY_ERROR) { memcpy(p->tempBuf, src, inSize); p->tempBufSize = (unsigned)inSize; (*srcLen) += inSize; *status = LZMA_STATUS_NEEDS_MORE_INPUT; return true; } if (checkEndMarkNow && dummyRes != DUMMY_MATCH) { *status = LZMA_STATUS_NOT_FINISHED; return false; } bufLimit = src; } else bufLimit = src + inSize - LZMA_REQUIRED_INPUT_MAX; p->buf = src; if( !LzmaDec_DecodeReal2(p, dicLimit, bufLimit) ) return false; processed = (uint32_t)(p->buf - src); (*srcLen) += processed; src += processed; inSize -= processed; } else { unsigned rem = p->tempBufSize, lookAhead = 0; while (rem < LZMA_REQUIRED_INPUT_MAX && lookAhead < inSize) p->tempBuf[rem++] = src[lookAhead++]; p->tempBufSize = rem; if (rem < LZMA_REQUIRED_INPUT_MAX || checkEndMarkNow) { int dummyRes = LzmaDec_TryDummy(p, p->tempBuf, rem); if (dummyRes == DUMMY_ERROR) { (*srcLen) += lookAhead; *status = LZMA_STATUS_NEEDS_MORE_INPUT; return true; } if (checkEndMarkNow && dummyRes != DUMMY_MATCH) { *status = LZMA_STATUS_NOT_FINISHED; return false; } } p->buf = p->tempBuf; if( !LzmaDec_DecodeReal2(p, dicLimit, p->buf) ) return false; lookAhead -= (rem - (unsigned)(p->buf - p->tempBuf)); (*srcLen) += lookAhead; src += lookAhead; inSize -= lookAhead; p->tempBufSize = 0; } } if (p->code == 0) *status = LZMA_STATUS_FINISHED_WITH_MARK; return (p->code == 0); } static bool LzmaDec_DecodeToBuf( CLzmaDec *p, uint8_t *dest, uint32_t *destLen, const uint8_t *src, uint32_t *srcLen, ELzmaFinishMode finishMode, ELzmaStatus *status ) { uint32_t outSize = *destLen; uint32_t inSize = *srcLen; *srcLen = *destLen = 0; for (;;) { uint32_t inSizeCur = inSize, outSizeCur, dicPos; ELzmaFinishMode curFinishMode; bool res; if (p->dicPos == p->dicBufSize) p->dicPos = 0; dicPos = p->dicPos; if (outSize > p->dicBufSize - dicPos) { outSizeCur = p->dicBufSize; curFinishMode = LZMA_FINISH_ANY; } else { outSizeCur = dicPos + outSize; curFinishMode = finishMode; } res = LzmaDec_DecodeToDic(p, outSizeCur, src, &inSizeCur, curFinishMode, status); src += inSizeCur; inSize -= inSizeCur; *srcLen += inSizeCur; outSizeCur = p->dicPos - dicPos; memcpy(dest, p->dic + dicPos, outSizeCur); dest += outSizeCur; outSize -= outSizeCur; *destLen += outSizeCur; if( !res ) return false; if (outSizeCur == 0 || outSize == 0) return true; } } static void LzmaDec_Free(CLzmaDec *p) { LZMA_FREE( p->dic ); LZMA_FREE( p->probs ); } static bool LzmaDec_Init(CLzmaDec *p, const uint8_t *raw_props) { uint32_t i; uint8_t d = raw_props[0]; p->lc = d % 9; d /= 9; p->pb = d / 5; p->lp = d % 5; p->dicBufSize = raw_props[1] | ((uint32_t)raw_props[2] << 8) | ((uint32_t)raw_props[3] << 16) | ((uint32_t)raw_props[4] << 24); if (p->dicBufSize < min_dictionary_size) p->dicBufSize = min_dictionary_size; p->numProbs = LzmaProps_GetNumProbs(p); p->probs = (int *)LZMA_MALLOC(p->numProbs * sizeof(int)); if( !p->probs ) return false; p->dic = (uint8_t *)LZMA_MALLOC(p->dicBufSize); if (p->dic == 0) { LZMA_FREE( p->probs ); return false; } p->dicPos = 0; p->needFlush = true; p->remainLen = 0; p->tempBufSize = 0; p->processedPos = 0; p->checkDicSize = 0; for( i = 0; i < p->numProbs; ++i ) p->probs[i] = kBitModelTotal >> 1; p->reps[0] = p->reps[1] = p->reps[2] = p->reps[3] = 1; p->state = 0; return true; } // glue.c static #ifdef _MSC_VER __declspec(thread) #else __thread #endif struct { uint8_t *begin, *seek, *end; } memfd[2]; /* Returns the number of bytes really read. If (returned value < size) and (errno == 0), means EOF was reached. */ static int readblock( const int fd, uint8_t * buf, int size ) { int avail = (memfd[fd].end - memfd[fd].seek); if( size > avail ) size = avail; memcpy(buf, memfd[fd].seek, size); memfd[fd].seek += size; errno = 0; return size; } /* Returns the number of bytes really written. If (returned value < size), it is always an error. */ static int writeblock( const int fd, const uint8_t *buf, int size ) { int avail = (memfd[fd].end - memfd[fd].seek); if( size > avail ) size = avail; memcpy(memfd[fd].seek, buf, size); memfd[fd].seek += size; errno = 0; return size; } // Customized compression modes. // Lower modes are optimized for low-mem devices. Uber modes A-B-C require *lots of RAM*. static const struct lzma_options { int dictionary_size; /* [4 KiB .. 512 MiB] */ int match_len_limit; /* [5 .. 273] */ } lzma_mappings[] = { // lowmem+fastest modes { 1 << 12, 5 }, // 0 - 39973598 lzma 39.97% c:13.635s d:2.909s { 1 << 16, 6 }, // 1 - 34979790 lzma 34.98% c:19.151s d:2.427s { 1 << 19, 7 }, // 2 - 32881806 lzma 32.88% c:25.592s d:1.907s { 1 << 20, 8 }, // 3 - 31908622 lzma 31.91% c:32.189s d:1.827s { 3 << 19, 10 }, // 4 - 30704458 lzma 30.70% c:40.736s d:1.747s { 1 << 21, 16 }, // 5 - 28807777 lzma 28.81% c:55.690s d:1.645s { 3 << 20, 20 }, // 6 - 28100304 lzma 28.10% c:63.734s d:1.614s { 1 << 22, 28 }, // 7 - 27594705 lzma 27.59% c:72.234s d:1.604s { 1 << 23, 36 }, // 8 - 27051139 lzma 27.05% c:79.418s d:1.586s { 1 << 24, 68 }, // 9 - 26702913 lzma 26.70% c:87.800s d:1.573s { 3 << 23, 132 }, // A - 26667550 lzma 26.67% c:89.020s d:1.581s { 1 << 25, 273 }, // B - 26656366 lzma 26.66% c:89.586s d:1.607s { 1 << 26, 273 }, // C - 26656366 lzma 26.66% c:90.004s d:1.586s // himem+slowest modes }; unsigned lzma_encode(const void *in, unsigned inlen, void *out, unsigned outlen, unsigned flags /*[0..9]*/) { uint8_t level = (uint8_t)(flags > 9 ? 9 : flags); int i = 0; memfd[i].begin = memfd[i].seek = memfd[i].end = (uint8_t*)in; memfd[i].end += inlen; int o = 1; memfd[o].begin = memfd[o].seek = memfd[o].end = (uint8_t*)out; memfd[o].end += outlen; writeblock(o, &level, 1); // write 1-byte header struct lzma_options encoder_options = lzma_mappings[level]; CLzmaEncHandle handle = LzmaEnc_Init( encoder_options.dictionary_size, encoder_options.match_len_limit, i, o ); int ok = SZ_OK == LzmaEnc_Encode(handle); LzmaEnc_Free(handle); return ok ? (int)(memfd[o].seek - memfd[o].begin) : 0; } unsigned lzma_decode(const void *in_, unsigned inlen, void *out, unsigned outlen) { const uint8_t *in = (const uint8_t*)in_; // parse 1-byte header uint8_t level = *in++; --inlen; // -d{N}: set dictionary size - [12, 30], default: 23 (8MB) // -fb{N}: set number of fast bytes - [5, 273], default: 128 // -mc{N}: set number of cycles for match finder // -lc{N}: set number of literal context bits - [0, 8], default: 3 // -lp{N}: set number of literal pos bits - [0, 4], default: 0 // -pb{N}: set number of pos bits - [0, 4], default: 2 // -mf{MF_ID}: set Match Finder: [bt2, bt3, bt4, hc4], default: bt4 #pragma pack(push,1) struct { uint8_t d /*d=lc/pb/lp*/; uint32_t dsize; uint64_t rawsize; } props = {0}; #pragma pack(pop) props.d = 0x5D; props.dsize = lzma_mappings[level].dictionary_size; CLzmaDec dec; ELzmaStatus status; LzmaDec_Init(&dec, &props.d); uint32_t srcLen = (uint32_t)inlen, destLen = (uint32_t)outlen; bool ok = LzmaDec_DecodeToBuf(&dec, (uint8_t*)out, &destLen, in, &srcLen, LZMA_FINISH_ANY, &status); LzmaDec_Free(&dec); return (unsigned)(ok ? destLen : 0); } unsigned lzma_bounds(unsigned inlen, unsigned flags) { return (unsigned)(inlen * 1.1) + 16; // @todo: check src } unsigned lzma_excess(unsigned flags) { return (unsigned)(0); } #endif // LZMA_C #ifdef LZMA_DEMO //#pragma once int main() { const char *longcopy = "Hello world! Hello world! Hello world! Hello world!"; int level = 1; char out[128]; unsigned outlen = lzma_encode(longcopy, strlen(longcopy)+1, out, 128, level ); printf("%s %d->%d\n", outlen ? "ok" : "fail", (int)strlen(longcopy)+1, (int)outlen); char redo[128]; unsigned unpacked = lzma_decode(out, outlen, redo, 128); printf("%d->%d %s\n", (int)outlen, (int)unpacked, redo); } #define main main__ #endif // LZMA_DEMO //#line 1 "amalgamated_lzp1.c" /*********** Direct port of the old lzp1.c code to a single file header. This is not the best way to make fast compressors on modern hardware and this is by no means a modern competitive compressor. Also, zlib licensed is not strictly public domain, but pretty close terms :o) ----------- Copyright (c) 2019, @r-lyeh Copyright (c) 1998-2012, Charles Bloom This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. *******************/ unsigned lzp1_encode(const void* in, unsigned inlen, void* out, unsigned outlen, unsigned flags); unsigned lzp1_decode(const void* in, unsigned inlen, void* out, unsigned outlen); unsigned lzp1_bounds(unsigned inlen, unsigned flags); unsigned lzp1_excess(unsigned flags); #ifdef LZP1_C //#pragma once #include #define LZP1_BOUNDS(sz) ((sz)+((sz)/8)+256) #define LZP1_EXCESS 256 #define LZP1_HASH_SIZE (1<<16) #define LZP1_HASH(x,y,z) ((x ^ (y << 7) ^ (z<<11)) & 0xFFFF) static int lzp1_encode_(const uint8_t *raw,int rawLen,uint8_t * comp,int compLen) { uint8_t const *table[LZP1_HASH_SIZE]; for(int ix=0;ix= 0xFF ) { *cp++ = 0xFF; ml -= 0xFF; } *cp++ = (uint8_t)ml; } else { // match 10 ENC_SHIFT_CONTROL(1); ENC_SHIFT_CONTROL(1); ENC_SHIFT_CONTROL(0); } } else { // match 9 ENC_SHIFT_CONTROL(1); ENC_SHIFT_CONTROL(0); ENC_SHIFT_CONTROL(1); } } else { // match 8 ENC_SHIFT_CONTROL(1); ENC_SHIFT_CONTROL(0); ENC_SHIFT_CONTROL(0); } } else { // match 7 ENC_SHIFT_CONTROL(0); ENC_SHIFT_CONTROL(1); ENC_SHIFT_CONTROL(1); } } else { // match 6 ENC_SHIFT_CONTROL(0); ENC_SHIFT_CONTROL(1); ENC_SHIFT_CONTROL(0); } } else { // match 5 ENC_SHIFT_CONTROL(0); ENC_SHIFT_CONTROL(0); ENC_SHIFT_CONTROL(1); } } else { // match 4 ENC_SHIFT_CONTROL(0); ENC_SHIFT_CONTROL(0); ENC_SHIFT_CONTROL(0); } } else { // match 3 ENC_SHIFT_CONTROL(1); ENC_SHIFT_CONTROL(0); } } else { // match 2 ENC_SHIFT_CONTROL(0); ENC_SHIFT_CONTROL(1); } } else { //match 1 ENC_SHIFT_CONTROL(0); ENC_SHIFT_CONTROL(0); } } } } //flush the control while( controlb > 0 ) { control += control; controlb--; } *controlp = (uint8_t)control; return (int)(cp - comp); } static int lzp1_decode_(const uint8_t * comp,int compLen,uint8_t * raw,int rawLen) { uint8_t const *table[LZP1_HASH_SIZE]; for(int ix=0;ix> 8) << 8) | excess; } unsigned lzp1_encode(const void* in, unsigned inlen, void* out, unsigned outlen, unsigned flags) { return (unsigned)lzp1_encode_((const uint8_t*)in, (int)inlen, (uint8_t*)out, (int)outlen); } unsigned lzp1_decode(const void* in, unsigned inlen, void* out, unsigned outlen) { return (unsigned)lzp1_decode_((const uint8_t*)in, (int)inlen, (uint8_t*)out, (int)outlen); } unsigned lzp1_bounds(unsigned inlen, unsigned flags) { return (unsigned)LZP1_BOUNDS(inlen); } unsigned lzp1_excess(unsigned flags) { return (unsigned)LZP1_EXCESS; } #endif // LZP1_C #ifdef LZP1_DEMO //#pragma once int main(int argc, char** argv) { const char *longcopy = "Hello world! Hello world! Hello world! Hello world!"; char out[128]; int outlen = lzp1_encode(longcopy, strlen(longcopy)+1, out, 128); printf("%s %d->%d\n", outlen ? "ok" : "fail", (int)strlen(longcopy)+1, outlen); char redo[128 + 256]; int unpacked = lzp1_decode(out, outlen, redo, 128); printf("%d->%d %s\n", outlen, unpacked, redo); } #define main main__ #endif // LZP1_DEMO //#line 1 "amalgamated_lzrw3a.c" // Author : Ross Williams. Date : 15-Jul-1991. Release : 1. // Modified by @r-lyeh. // // This file contains an implementation of the LZRW3-A data compression // algorithm in the C programming language. // 1 Algorithm is free of patent problems. The algorithm has not been // patented (nor will it be) and is of the LZ77 class which is fairly // clear of patents. // 2 This implementation in C is in the public domain. unsigned lzrw3a_encode(const void* in, unsigned inlen, void* out, unsigned outlen, unsigned flags); unsigned lzrw3a_decode(const void* in, unsigned inlen, void* out, unsigned outlen); unsigned lzrw3a_bounds(unsigned inlen, unsigned flags); unsigned lzrw3a_excess(unsigned flags); #ifdef LZRW3A_C //#pragma once #include #include #define MEM_REQ ( HASH_TABLE_LENGTH*sizeof(uint8_t *) + 16 ) // 16 = ALIGNMENT_FUDGE #define FLAG_BYTES 4 #define FLAG_PACKESS 0 #define FLAG_COPY 1 #define ALIGN_UP(X) ((((uintptr_t)X)+3)&~3) #define MAX_RAW_ITEM (18) #define MAX_RAW_GROUP (16*MAX_RAW_ITEM) #define MAX_CMP_GROUP (2+16*2) #define HASH_TABLE_LENGTH (4096) #define HASH_TABLE_DEPTH_BITS (3) #define PARTITION_LENGTH_BITS (12-HASH_TABLE_DEPTH_BITS) #define PARTITION_LENGTH (1<>4) & HASH_MASK) \ << HASH_TABLE_DEPTH_BITS \ ) #define UPDATE_P(P_BASE,NEWPTR) \ {(P_BASE)[cycle++]=(NEWPTR); cycle&=DEPTH_MASK;} #define UPDATE_I(I_BASE,NEWPTR) \ {hash[(I_BASE)+cycle++]=(NEWPTR); cycle&=DEPTH_MASK;} #define ANY_HASH_INDEX (0) static void lzrw3a_compress(uint8_t* p_wrk_mem, uint8_t* p_src_first, uint32_t src_len, uint8_t* p_dst_first, size_t* p_dst_len) { uint8_t* p_src = p_src_first; uint8_t* p_dst = p_dst_first; uint8_t* p_src_post = p_src_first + src_len; uint8_t* p_dst_post = p_dst_first + src_len; uint8_t* p_src_max1 = p_src_first + src_len - MAX_RAW_ITEM; uint8_t* p_src_max16 = p_src_first + src_len - MAX_RAW_ITEM * 16; #define TOPWORD 0xFFFF0000 uint8_t* p_control; uint32_t control = TOPWORD; uint8_t** hash = (uint8_t**)ALIGN_UP(p_wrk_mem); uint8_t** p_h1 = 0; uint8_t** p_h2 = 0; unsigned cycle = 0; *p_dst++ = FLAG_PACKESS; {unsigned i; for (i = 2; i <= FLAG_BYTES; i++) *p_dst++ = 0; } p_control = p_dst; p_dst += 2; {unsigned i; uint8_t** p_h = hash; #define ZH *p_h++=START_STRING_18 for (i = 0; i < 256; i++) { ZH; ZH; ZH; ZH; ZH; ZH; ZH; ZH; ZH; ZH; ZH; ZH; ZH; ZH; ZH; ZH; } } while (1) { uint8_t* p_ziv = 0; unsigned unroll; unsigned index; uint8_t** p_h0; register unsigned d; register unsigned bestlen; register unsigned bestpos; if (p_dst > p_dst_post) goto overrun; unroll = 16; if (p_src > p_src_max16) { unroll = 1; if (p_src > p_src_max1) { if (p_src == p_src_post) break; else { p_h0 = &hash[ANY_HASH_INDEX]; goto literal; } } } begin_unrolled_loop: p_ziv = p_src; index = HASH(p_src); p_h0 = &hash[index]; bestlen = 0; bestpos = 0; for (d = 0; d < HASH_TABLE_DEPTH; d++) { register uint8_t* s = p_src; register uint8_t* p = p_h0[d]; register unsigned len; if (s[bestlen] == p[bestlen]) { #define PS *p++!=*s++ PS || PS || PS || PS || PS || PS || PS || PS || PS || PS || PS || PS || PS || PS || PS || PS || PS || PS || s++; len = s - p_src - 1; if (len > bestlen) { bestpos = d; bestlen = len; } } } if (bestlen < 3) { literal: *p_dst++ = *p_src++; control &= 0xFFFEFFFF; if (p_h2 != 0) { UPDATE_P(p_h2, p_ziv - 2); } p_h2 = p_h1; p_h1 = p_h0; } else { index += bestpos; *p_dst++ = ((index & 0xF00) >> 4) | (bestlen - 3); *p_dst++ = index & 0xFF; p_src += bestlen; if (p_h1 != 0) { if (p_h2 != 0) { UPDATE_P(p_h2, p_ziv - 2); p_h2 = 0; } UPDATE_P(p_h1, p_ziv - 1); p_h1 = 0; } UPDATE_P(p_h0, p_ziv); } control >>= 1; if (--unroll) goto begin_unrolled_loop; if ((control & TOPWORD) == 0) { *p_control++ = control & 0xFF; *p_control = (control >> 8) & 0xFF; p_control = p_dst; p_dst += 2; control = TOPWORD; } } while (control & TOPWORD) control >>= 1; *p_control++ = control & 0xFF; *p_control++ = (control >> 8) & 0xFF; if (p_control == p_dst) p_dst -= 2; *p_dst_len = p_dst - p_dst_first; return; overrun: *p_dst_first = FLAG_COPY; memcpy(p_dst_first + FLAG_BYTES, p_src_first, src_len); *p_dst_len = src_len + FLAG_BYTES; } static void lzrw3a_decompress(uint8_t* p_wrk_mem, uint8_t* p_src_first, uint32_t src_len, uint8_t* p_dst_first, size_t* p_dst_len) { register uint8_t* p_src = p_src_first + FLAG_BYTES; register uint8_t* p_dst = p_dst_first; uint8_t* p_src_post = p_src_first + src_len; uint8_t* p_src_max16 = p_src_first + src_len - (MAX_CMP_GROUP - 2); uint8_t** hash = (uint8_t**)ALIGN_UP(p_wrk_mem); register uint32_t control = 1; register unsigned literals = 0; unsigned cycle = 0; if (*p_src_first == FLAG_COPY) { memcpy(p_dst_first, p_src_first + FLAG_BYTES, src_len - FLAG_BYTES); *p_dst_len = src_len - FLAG_BYTES; return; } {unsigned i; uint8_t** p_h = hash; #define ZJ *p_h++=START_STRING_18 for (i = 0; i < 256; i++) { ZJ; ZJ; ZJ; ZJ; ZJ; ZJ; ZJ; ZJ; ZJ; ZJ; ZJ; ZJ; ZJ; ZJ; ZJ; ZJ; } } while (p_src != p_src_post) { register unsigned unroll; if (control == 1) { control = 0x10000 | *p_src++; control |= (*p_src++) << 8; } unroll = p_src <= p_src_max16 ? 16 : 1; while (unroll--) { if (control & 1) { register uint8_t* p; register unsigned lenmt; register uint8_t* p_ziv = p_dst; register unsigned index; lenmt = *p_src++; index = ((lenmt & 0xF0) << 4) | *p_src++; p = hash[index]; lenmt &= 0xF; *p_dst++ = *p++; *p_dst++ = *p++; *p_dst++ = *p++; while (lenmt--) *p_dst++ = *p++; if (literals > 0) { register uint8_t* r = p_ziv - literals;; UPDATE_I(HASH(r), r); if (literals == 2) { r++; UPDATE_I(HASH(r), r); } literals = 0; } UPDATE_I(index & (~DEPTH_MASK), p_ziv); } else { *p_dst++ = *p_src++; if (++literals == 3) { register uint8_t* p = p_dst - 3; UPDATE_I(HASH(p), p); literals = 2; } } control >>= 1; } } *p_dst_len = p_dst - p_dst_first; } unsigned lzrw3a_encode(const void* in, unsigned inlen, void* out, unsigned outlen, unsigned flags) { uint8_t workmem[MEM_REQ]; size_t outlen_ = outlen; lzrw3a_compress(workmem, (uint8_t*)in, inlen, (uint8_t*)out, &outlen_); return (unsigned)outlen_; } unsigned lzrw3a_decode(const void* in, unsigned inlen, void* out, unsigned outlen) { uint8_t workmem[MEM_REQ]; size_t outlen_ = outlen; lzrw3a_decompress(workmem, (uint8_t*)in, inlen, (uint8_t*)out, &outlen_); return (unsigned)outlen_; } unsigned lzrw3a_bounds(unsigned inlen, unsigned flags) { return (unsigned)(inlen * 1.1) + 16; // @todo: check src } unsigned lzrw3a_excess(unsigned flags) { return (unsigned)0; } #endif // LZRW3A_C #ifdef LZRW3A_DEMO //#pragma once #include int main() { const char* longcopy = "Hello world! Hello world! Hello world! Hello world!"; int level = 1; char out[128]; size_t outlen = lzrw3a_encode(longcopy, strlen(longcopy) + 1, out, 128, level); printf("%s %d->%d\n", outlen ? "ok" : "fail", (int)strlen(longcopy) + 1, (int)outlen); char redo[128]; size_t unpacked = lzrw3a_decode(out, outlen, redo, 128); printf("%d->%d %s\n", (int)outlen, (int)unpacked, redo); } #define main main__ #endif // LZRW3A_DEMO //#line 1 "amalgamated_lzss.c" /************************************************************** LZSS.C -- A Data Compression Program *************************************************************** 4/ 6/1989 Haruhiko Okumura 30/12/2019 @r-lyeh Use, distribute, and modify this program freely. **************************************************************/ unsigned lzss_encode(const void *in, unsigned inlen, void *out, unsigned outlen, unsigned flags); unsigned lzss_decode(const void *in, unsigned inlen, void *out, unsigned outlen); unsigned lzss_bounds(unsigned bytes, unsigned flags); unsigned lzss_excess(unsigned flags); #ifdef LZSS_C //#pragma once #include #include #include #define N 4096 /* size of ring buffer */ #define F 18 /* upper limit for match_length */ #define THRESHOLD 2 /* encode string into position and length if match_length is greater than this */ #define NIL N /* index for root of binary search trees */ /* of longest match. These are set by the InsertNode() procedure. */ static int match_position; static int match_length; static void InsertNode(unsigned char* text_buf, int* lson, int* rson, int* dad, int r) /* Inserts string of length F, text_buf[r..r+F-1], into one of the trees (text_buf[r]'th tree) and returns the longest-match position and length via the global variables match_position and match_length. If match_length = F, then removes the old node in favor of the new one, because the old one will be deleted sooner. Note r plays double role, as tree node and position in buffer. */ { int i, p, cmp; unsigned char *key; cmp = 1; key = &text_buf[r]; p = N + 1 + key[0]; rson[r] = lson[r] = NIL; match_length = 0; for ( ; ; ) { if (cmp >= 0) { if (rson[p] != NIL) p = rson[p]; else { rson[p] = r; dad[r] = p; return; } } else { if (lson[p] != NIL) p = lson[p]; else { lson[p] = r; dad[r] = p; return; } } for (i = 1; i < F; i++) if ((cmp = key[i] - text_buf[p + i]) != 0) break; if (i > match_length) { match_position = p; if ((match_length = i) >= F) break; } } dad[r] = dad[p]; lson[r] = lson[p]; rson[r] = rson[p]; dad[lson[p]] = r; dad[rson[p]] = r; if (rson[dad[p]] == p) rson[dad[p]] = r; else lson[dad[p]] = r; dad[p] = NIL; /* remove p */ } static void DeleteNode(int* lson, int* rson, int* dad, int p) /* deletes node p from tree */ { int q; if (dad[p] == NIL) return; /* not in tree */ if (rson[p] == NIL) q = lson[p]; else if (lson[p] == NIL) q = rson[p]; else { q = lson[p]; if (rson[q] != NIL) { do { q = rson[q]; } while (rson[q] != NIL); rson[dad[q]] = lson[q]; dad[lson[q]] = dad[q]; lson[q] = lson[p]; dad[lson[p]] = q; } rson[q] = rson[p]; dad[rson[p]] = q; } dad[q] = dad[p]; if (rson[dad[p]] == p) rson[dad[p]] = q; else lson[dad[p]] = q; dad[p] = NIL; } #define _get(c) \ if (! ilen) {\ c = -1; /*EOF;*/ \ break;\ }\ c = *istr;\ ++istr;\ --ilen #define _put(c) \ *ostr = c;\ ++ostr;\ --olen size_t LzssEncode(const char* istr, size_t ilen, char* ostr, size_t olen) { int i, c, len, r, s, last_match_length, code_buf_ptr; unsigned char code_buf[17], mask; size_t codesize = 0; int lson[N + 1], rson[N + 257], dad[N + 1]; /* left & right children & parents -- These constitute binary search trees. */ unsigned char text_buf[N + F - 1]; /* ring buffer of size N, with extra F-1 bytes to facilitate string comparison */ match_position = 0; match_length = 0; if (ilen == 0) return 0; /* initialize trees */ /* For i = 0 to N - 1, rson[i] and lson[i] will be the right and left children of node i. These nodes need not be initialized. Also, dad[i] is the parent of node i. These are initialized to NIL (= N), which stands for 'not used.' For i = 0 to 255, rson[N + i + 1] is the root of the tree for strings that begin with character i. These are initialized to NIL. Note there are 256 trees. */ for (i = N + 1; i <= N + 256; i++) rson[i] = NIL; for (i = 0; i < N; i++) dad[i] = NIL; code_buf[0] = 0; /* code_buf[1..16] saves eight units of code, and code_buf[0] works as eight flags, "1" representing that the unit is an unencoded letter (1 byte), "0" a position-and-length pair (2 bytes). Thus, eight units require at most 16 bytes of code. */ code_buf_ptr = mask = 1; s = 0; r = N - F; for (i = s; i < r; i++) text_buf[i] = 0; /* Clear the buffer with any character that will appear often. */ for (len = 0; len < F && ilen; len++) { _get(c); text_buf[r + len] = c; /* Read F bytes into the last F bytes of the buffer */ } for (i = 1; i <= F; i++) InsertNode(text_buf, lson, rson, dad, r - i); /* Insert the F strings, each of which begins with one or more 'space' characters. Note the order in which these strings are inserted. This way, degenerate trees will be less likely to occur. */ InsertNode(text_buf, lson, rson, dad, r); /* Finally, insert the whole string just read. The global variables match_length and match_position are set. */ do { if (match_length > len) match_length = len; /* match_length may be spuriously long near the end of text. */ if (match_length <= THRESHOLD) { match_length = 1; /* Not long enough match. Send one byte. */ code_buf[0] |= mask; /* 'send one byte' flag */ code_buf[code_buf_ptr++] = text_buf[r]; /* Send uncoded. */ } else { code_buf[code_buf_ptr++] = (unsigned char) match_position; code_buf[code_buf_ptr++] = (unsigned char) (((match_position >> 4) & 0xf0) | (match_length - (THRESHOLD + 1))); /* Send position and length pair. Note match_length > THRESHOLD. */ } if ((mask <<= 1) == 0) { /* Shift mask left one bit. */ for (i = 0; i < code_buf_ptr; i++) { /* Send at most 8 units of */ _put(code_buf[i]); /* code together */ } codesize += code_buf_ptr; code_buf[0] = 0; code_buf_ptr = mask = 1; } last_match_length = match_length; for (i = 0; i < last_match_length && ilen; i++) { _get(c); DeleteNode(lson, rson, dad, s); /* Delete old strings and */ text_buf[s] = c; /* read new bytes */ if (s < F - 1) text_buf[s + N] = c; /* If the position is near the end of buffer, extend the buffer to make string comparison easier. */ s = (s + 1) & (N - 1); r = (r + 1) & (N - 1); /* Since this is a ring buffer, increment the position modulo N. */ InsertNode(text_buf, lson, rson, dad, r); /* Register the string in text_buf[r..r+F-1] */ } while (i++ < last_match_length) { /* After the end of text, */ DeleteNode(lson, rson, dad, s); /* no need to read, but */ s = (s + 1) & (N - 1); r = (r + 1) & (N - 1); if (--len) InsertNode(text_buf, lson, rson, dad, r); /* buffer may not be empty. */ } } while (len > 0); /* until length of string to be processed is zero */ if (code_buf_ptr > 1) { /* Send remaining code. */ for (i = 0; i < code_buf_ptr; i++) { _put(code_buf[i]); } codesize += code_buf_ptr; } return codesize; } #undef _put #define _put(c) \ *ostr++ = c; size_t LzssDecode(const unsigned char* istr, size_t ilen, char *ostr, size_t olen) /* Just the reverse of Encode(). */ { unsigned char text_buf[N + F - 1]; /* ring buffer of size N, with extra F-1 bytes to facilitate string comparison */ int i, j, k, r, c; unsigned int flags; int limit = ilen; char *obak = ostr; for (i = 0; i < N - F; i++) text_buf[i] = 0; r = N - F; flags = 0; for ( ; ; ) { if (((flags >>= 1) & 256) == 0) { _get(c); flags = c | 0xff00; /* uses higher byte cleverly */ } /* to count eight */ if (flags & 1) { _get(c); _put(c); text_buf[r++] = c; r &= (N - 1); } else { _get(i); _get(j); i |= ((j & 0xf0) << 4); j = (j & 0x0f) + THRESHOLD; for (k = 0; k <= j; k++) { c = text_buf[(i + k) & (N - 1)]; _put(c); text_buf[r++] = c; r &= (N - 1); } } } return (size_t)(ostr - obak); } #undef _get #undef _put #undef N #undef F #undef THRESHOLD #undef NIL unsigned lzss_encode(const void *in, unsigned inlen, void *out, unsigned outlen, unsigned flags) { size_t rc = LzssEncode((const char*)in, (size_t)inlen, (char*)out, (size_t)outlen); return (unsigned)rc; } unsigned lzss_decode(const void *in, unsigned inlen, void *out, unsigned outlen) { size_t rc = LzssDecode((const unsigned char*)in, (size_t)inlen, (char*)out, (size_t)outlen); return (unsigned)rc; } unsigned lzss_bounds(unsigned bytes, unsigned flags) { return (unsigned)(bytes * 1.5) + 16; // @todo: check src } unsigned lzss_excess(unsigned flags) { return (unsigned)0; } #endif // LZSS_C #ifdef LZSS_DEMO //#pragma once #include int main() { const char *longcopy = "Hello world! Hello world! Hello world! Hello world!"; int level=1; char out[128]; size_t outlen = lzss_encode(longcopy, strlen(longcopy)+1, out, 128, level); printf("%s %d->%d\n", outlen ? "ok" : "fail", (int)strlen(longcopy)+1, (int)outlen); char redo[128]; size_t unpacked = lzss_decode(out, outlen, redo, 128); printf("%d->%d %s\n", (int)outlen, (int)unpacked, redo); } #define main main__ #endif // LZSS_DEMO //#line 1 "amalgamated_ppp.c" // pred.c -- Original code by Dave Rand's rendition of the predictor algorithm. // Updated by: Ian Donaldson, Carsten Bormann. Additional modifications by @r-lyeh. // // There are no license fees or costs associated with using the Predictor algorithm. // Use the following code at your own risk. unsigned ppp_encode(const void *in, unsigned inlen, void *out, unsigned outlen, unsigned flags); unsigned ppp_decode(const void *in, unsigned inlen, void *out, unsigned outlen); unsigned ppp_bounds(unsigned inlen, unsigned flags); unsigned ppp_excess(unsigned flags); #ifdef PPP_C //#pragma once #include #include #include /* The following hash code is the heart of the algorithm: * It builds a sliding hash sum of the previous 3-and-a-bit * characters which will be used to index the guess table. * A better hash function would result in additional compression, * at the expense of time. */ // original. enwik8: 61.730.508 c:0.729s d:0.453s //#define PPP_HASH_TYPE unsigned short //#define PPP_HASH_TABLE (65536) //#define PPP_HASH(x) Hash = (Hash << 4) ^ (x) // // improved. enwik8: 58.769.363 c:0.772s d:0.490s #define PPP_HASH_TYPE unsigned int #define PPP_HASH_TABLE (1<<18) // 256K #define PPP_HASH(x) Hash = ((Hash * 160) ^ (x)) & (PPP_HASH_TABLE-1) // see: https://encode.su/threads/1025-PREDICTOR-algorithm static int ppp_compress(const unsigned char *source, int slen, unsigned char *dest, int dlen) { PPP_HASH_TYPE Hash = 0; unsigned char GuessTable[PPP_HASH_TABLE] = {0}; unsigned char *orgdest = dest; while (slen) { unsigned char *flagdest = dest++, flags = 0; /* All guess wrong initially */ for (int bitmask=1, i=0; i < 8 && slen; i++, bitmask <<= 1) { if (GuessTable[Hash] != *source) { GuessTable[Hash] = *source; *dest++ = *source; /* Guess wrong, output char */ } else { flags |= bitmask; /* Guess was right - don't output */ } PPP_HASH(*source++);slen--; } *flagdest = flags; } return(dest - orgdest); } static int ppp_decompress(const unsigned char *source, int slen, unsigned char *dest, int dlen) { int final = 1; PPP_HASH_TYPE Hash = 0; unsigned char GuessTable[PPP_HASH_TABLE] = {0}; unsigned char *orgdest = dest; while (slen >= 9) { unsigned char flags = *source++; for (int i=0, bitmask = 1; i < 8; i++, bitmask <<= 1) { if (!(flags & bitmask)) { GuessTable[Hash] = *source; /* Guess wrong */ *dest = *source++; /* Read from source */ slen--; } else { *dest = GuessTable[Hash]; /* Guess correct */ } PPP_HASH(*dest++); } slen--; } while (final && slen > 0) { unsigned char flags = *source++; slen--; for (int i=0, bitmask = 1; i < 8; i++, bitmask <<= 1) { if (!(flags & bitmask)) { if (!slen) break; /* we seem to be really done -- cabo */ GuessTable[Hash] = *source; /* Guess wrong */ *dest = *source++; /* Read from source */ slen--; } else { *dest = GuessTable[Hash]; /* Guess correct */ } PPP_HASH(*dest++); } } return (dest - orgdest); // len } unsigned ppp_encode(const void *in, unsigned inlen, void *out, unsigned outlen, unsigned flags) { return (unsigned)ppp_compress((const unsigned char *)in, (int)inlen, (unsigned char *)out, (int)outlen); } unsigned ppp_decode(const void *in, unsigned inlen, void *out, unsigned outlen) { return (unsigned)ppp_decompress((const unsigned char *)in, (int)inlen, (unsigned char *)out, (int)outlen); } unsigned ppp_bounds(unsigned inlen, unsigned flags) { return (unsigned)(inlen/8*9+9); } unsigned ppp_excess(unsigned flags) { return (unsigned)0; } #endif // PPP_C #ifdef PPP_DEMO //#pragma once #include int main() { const char *longcopy = "Hello world! Hello world! Hello world! Hello world!"; int level = 0; char out[128]; unsigned outlen = ppp_encode(longcopy, strlen(longcopy)+1, out, 128, level); printf("%s %d->%d\n", outlen ? "ok" : "fail", (int)strlen(longcopy)+1, (int)outlen); char redo[128]; unsigned unpacked = ppp_decode(out, outlen, redo, 128); printf("%d->%d %s\n", outlen, unpacked, redo); } #define main main__ #endif // PPP_DEMO //#line 1 "amalgamated_raw.c" // raw memcpy de/encoder // - rlyeh, public domain #ifndef RAW_H #define RAW_H unsigned raw_encode(const void *in, unsigned inlen, void *out, unsigned outcap, unsigned flags); unsigned raw_decode(const void *in, unsigned inlen, void *out, unsigned outcap); unsigned raw_bounds(unsigned bytes, unsigned flags); unsigned raw_excess(unsigned flags); #endif #ifdef RAW_C //#pragma once #include unsigned raw_encode(const void *in, unsigned inlen, void *out, unsigned outcap, unsigned flags) { return memcpy(out, in, inlen), inlen; } unsigned raw_decode(const void *in, unsigned inlen, void *out, unsigned outcap) { return memcpy(out, in, inlen), inlen; } unsigned raw_bounds(unsigned bytes, unsigned flags) { return (unsigned)bytes; } unsigned raw_excess(unsigned flags) { return (unsigned)0; } #endif //#line 1 "amalgamated_ulz.c" // ULZ.HPP - An ultra-fast LZ77 compressor // Original C++ code written and placed in the public domain by Ilya Muravyov (UNLICENSED) // Modified by r-lyeh (UNLICENSED) unsigned ulz_encode(const void *in, unsigned inlen, void *out, unsigned outlen, unsigned flags); // [0..(6)..9] unsigned ulz_decode(const void *in, unsigned inlen, void *out, unsigned outlen); unsigned ulz_bounds(unsigned inlen, unsigned flags); unsigned ulz_excess(unsigned flags); #ifdef ULZ_C //#pragma once #include #include #ifndef ULZ_REALLOC #define ULZ_REALLOC REALLOC #endif enum { ULZ_EXCESS=16, ULZ_WINDOW_BITS=17, // Hard-coded ULZ_WINDOW_SIZE=1<>(32-ULZ_HASH_BITS); } static inline void EncodeMod(uint8_t** p, uint32_t x) { while (x>=128) { x-=128; *(*p)++=128+(x&127); x>>=7; } *(*p)++=x; } static inline uint32_t DecodeMod(const uint8_t** p) { uint32_t x=0; for (int i=0; i<=21; i+=7) { const uint32_t c=*(*p)++; x+=c<HashTable[i]=ULZ_NIL; uint8_t* op=out; int anchor=0; int p=0; while (p=ULZ_MIN_MATCH) { const int limit=(p-ULZ_WINDOW_SIZE) > ULZ_NIL ? (p-ULZ_WINDOW_SIZE) : ULZ_NIL; const uint32_t h=Hash32(&in[p]); int s=u->HashTable[h]; u->HashTable[h]=p; if (s>limit && UnalignedLoad32(&in[s])==UnalignedLoad32(&in[p])) { int len=ULZ_MIN_MATCH; while (len=(7+128)) best_len=0; if (best_len>=ULZ_MIN_MATCH) { const int len=best_len-ULZ_MIN_MATCH; const int token=((dist>>12)&16)+(len < 15 ? len : 15); if (anchor!=p) { const int run=p-anchor; if (run>=7) { *op++=(7<<5)+token; EncodeMod(&op, run-7); } else *op++=(run<<5)+token; WildCopy(op, &in[anchor], run); op+=run; } else *op++=token; if (len>=15) EncodeMod(&op, len-15); UnalignedStore16(op, dist); op+=2; anchor=p+best_len; ++p; u->HashTable[Hash32(&in[p])]=p; ++p; u->HashTable[Hash32(&in[p])]=p; ++p; u->HashTable[Hash32(&in[p])]=p; ++p; p=anchor; } else ++p; } if (anchor!=p) { const int run=p-anchor; if (run>=7) { *op++=7<<5; EncodeMod(&op, run-7); } else *op++=run<<5; WildCopy(op, &in[anchor], run); op+=run; } ULZ_REALLOC(u, 0); return op-out; } static int UlzCompress(const uint8_t* in, int inlen, uint8_t* out, int outlen, int level) { if (level<1 || level>9) return 0; const int max_chain=(level<9)?1<HashTable[i]=ULZ_NIL; uint8_t* op=out; int anchor=0; int p=0; while (p=ULZ_MIN_MATCH) { const int limit=(p-ULZ_WINDOW_SIZE) > ULZ_NIL ? (p-ULZ_WINDOW_SIZE) : ULZ_NIL; int chainlen=max_chain; int s=u->HashTable[Hash32(&in[p])]; while (s>limit) { if (in[s+best_len]==in[p+best_len] && UnalignedLoad32(&in[s])==UnalignedLoad32(&in[p])) { int len=ULZ_MIN_MATCH; while (lenbest_len) { best_len=len; dist=p-s; if (len==max_match) break; } } if (--chainlen==0) break; s=u->Prev[s&ULZ_WINDOW_MASK]; } } if (best_len==ULZ_MIN_MATCH && (p-anchor)>=(7+128)) best_len=0; if (level>=5 && best_len>=ULZ_MIN_MATCH && best_len ULZ_NIL ? (x-ULZ_WINDOW_SIZE) : ULZ_NIL; int chainlen=max_chain; int s=u->HashTable[Hash32(&in[x])]; while (s>limit) { if (in[s+best_len]==in[x+best_len] && UnalignedLoad32(&in[s])==UnalignedLoad32(&in[x])) { int len=ULZ_MIN_MATCH; while (lenPrev[s&ULZ_WINDOW_MASK]; } } if (best_len>=ULZ_MIN_MATCH) { const int len=best_len-ULZ_MIN_MATCH; const int token=((dist>>12)&16)+(len < 15 ? len : 15); if (anchor!=p) { const int run=p-anchor; if (run>=7) { *op++=(7<<5)+token; EncodeMod(&op, run-7); } else *op++=(run<<5)+token; WildCopy(op, &in[anchor], run); op+=run; } else *op++=token; if (len>=15) EncodeMod(&op, len-15); UnalignedStore16(op, dist); op+=2; while (best_len--!=0) { const uint32_t h=Hash32(&in[p]); u->Prev[p&ULZ_WINDOW_MASK]=u->HashTable[h]; u->HashTable[h]=p++; } anchor=p; } else { const uint32_t h=Hash32(&in[p]); u->Prev[p&ULZ_WINDOW_MASK]=u->HashTable[h]; u->HashTable[h]=p++; } } if (anchor!=p) { const int run=p-anchor; if (run>=7) { *op++=7<<5; EncodeMod(&op, run-7); } else *op++=run<<5; WildCopy(op, &in[anchor], run); op+=run; } ULZ_REALLOC(u, 0); return op-out; } static int UlzDecompress(const uint8_t* in, int inlen, uint8_t* out, int outlen) { uint8_t* op=out; const uint8_t* ip=in; const uint8_t* ip_end=ip+inlen; const uint8_t* op_end=op+outlen; while (ip=32) { int run=token>>5; if (run==7) run+=DecodeMod(&ip); if ((op_end-op)=ip_end) break; } int len=(token&15)+ULZ_MIN_MATCH; if (len==(15+ULZ_MIN_MATCH)) len+=DecodeMod(&ip); if ((op_end-op)=8) { WildCopy(op, cp, len); op+=len; } else { *op++=*cp++; *op++=*cp++; *op++=*cp++; *op++=*cp++; while (len--!=4) *op++=*cp++; } } return (ip==ip_end)?op-out:0; } unsigned ulz_encode(const void *in, unsigned inlen, void *out, unsigned outlen, unsigned flags) { int level = flags > 9 ? 9 : flags; // [0..(6)..9] int rc = level ? UlzCompress((uint8_t *)in, (int)inlen, (uint8_t *)out, (int)outlen, level) : UlzCompressFast((uint8_t *)in, (int)inlen, (uint8_t *)out, (int)outlen); return (unsigned)rc; } unsigned ulz_decode(const void *in, unsigned inlen, void *out, unsigned outlen) { return (unsigned)UlzDecompress((uint8_t *)in, (int)inlen, (uint8_t *)out, (int)outlen); } unsigned ulz_bounds(unsigned inlen, unsigned flags) { return (unsigned)(inlen + inlen/255 + 16); } unsigned ulz_excess(unsigned flags) { return (unsigned)(ULZ_EXCESS); } #endif // ULZ_C #ifdef ULZ_DEMO //#pragma once #include int main() { const char *longcopy = "Hello world! Hello world! Hello world! Hello world!"; int level=1; char out[128]; size_t outlen = ulz_encode(longcopy, strlen(longcopy)+1, out, 128, level); printf("%s %d->%d\n", outlen ? "ok" : "fail", (int)strlen(longcopy)+1, (int)outlen); char redo[128]; size_t unpacked = ulz_decode(out, outlen, redo, 128); printf("%d->%d %s\n", (int)outlen, (int)unpacked, redo); } #define main main__ #endif // ULZ_DEMO //#line 1 "amalgamated_pack.c" #ifdef COMPRESS_C //#pragma once #include #ifdef _MSC_VER # define ftello64 _ftelli64 #elif !defined __GNUC__ # define ftello64 ftell #endif #include #include #include static struct compressor { // id of compressor unsigned enumerator; // name of compressor const char name1, *name4, *name; // returns worst case compression estimation for selected flags unsigned (*bounds)(unsigned bytes, unsigned flags); // returns number of bytes written. 0 if error. unsigned (*encode)(const void *in, unsigned inlen, void *out, unsigned outcap, unsigned flags); // returns number of excess bytes that will be overwritten when decoding. unsigned (*excess)(unsigned flags); // returns number of bytes written. 0 if error. unsigned (*decode)(const void *in, unsigned inlen, void *out, unsigned outcap); } list[] = { { RAW, '0', "raw", "raw", raw_bounds, raw_encode, raw_excess, raw_decode }, { PPP, 'p', "ppp", "ppp", ppp_bounds, ppp_encode, ppp_excess, ppp_decode }, { ULZ, 'u', "ulz", "ulz", ulz_bounds, ulz_encode, ulz_excess, ulz_decode }, { LZ4X, '4', "lz4x", "lz4x", lz4x_bounds, lz4x_encode, lz4x_excess, lz4x_decode }, { CRSH, 'c', "crsh", "crush", crush_bounds, crush_encode, crush_excess, crush_decode }, { DEFL, 'd', "defl", "deflate", deflate_bounds, deflate_encode, deflate_excess, deflate_decode }, { LZP1, '1', "lzp1", "lzp1", lzp1_bounds, lzp1_encode, lzp1_excess, lzp1_decode }, { LZMA, 'm', "lzma", "lzma", lzma_bounds, lzma_encode, lzma_excess, lzma_decode }, { BALZ, 'b', "balz", "balz", balz_bounds, balz_encode, balz_excess, balz_decode }, { LZW3, 'w', "lzw3", "lzrw3a", lzrw3a_bounds, lzrw3a_encode, lzrw3a_excess, lzrw3a_decode }, { LZSS, 's', "lzss", "lzss", lzss_bounds, lzss_encode, lzss_excess, lzss_decode }, { BCM, 'B', "bcm", "bcm", bcm_bounds, bcm_encode, bcm_excess, bcm_decode }, }; char *arc_nameof(unsigned flags) { static __thread char buf[16]; snprintf(buf, 16, "%4s.%c", list[(flags>>4)&0x0F].name4, "0123456789ABCDEF"[flags&0xF]); return buf; } unsigned mem_encode(const void *in, unsigned inlen, void *out, unsigned outlen, unsigned compressor) { *(uint8_t*)out = compressor & 0xff; unsigned ret = list[(compressor >> 4) % NUM_COMPRESSORS].encode(in, inlen, (uint8_t*)out+1, outlen-1, compressor & 0x0F); return ret ? ret+1 : 0; } unsigned mem_decode(const void *in, unsigned inlen, void *out, unsigned outlen) { unsigned compressor = *(uint8_t*)in; return list[(compressor >> 4) % NUM_COMPRESSORS].decode((uint8_t*)in+1, inlen-1, out, outlen); } unsigned mem_bounds(unsigned inlen, unsigned compressor) { return 1 + list[(compressor >> 4) % NUM_COMPRESSORS].bounds(inlen, compressor & 0x0F); } unsigned mem_excess(unsigned compressor) { return list[(compressor >> 4) % NUM_COMPRESSORS].excess(compressor & 0x0F); } // --- // file options static uint8_t COMPRESS_FILE_BLOCK_SIZE = 23; // 2<<(BS+12) = { 8K..256M } static uint8_t COMPRESS_FILE_BLOCK_EXCESS = 0; // 16<> 4) & 3); // BLSIZE = 1ull << ((MAGIC & 15) + 13); #else if( fwrite(&COMPRESS_FILE_BLOCK_SIZE, 1,1, out) < 1) return 0; if( fwrite(&COMPRESS_FILE_BLOCK_EXCESS, 1,1, out) < 1) return 0; uint64_t BS_BYTES = 1ull << COMPRESS_FILE_BLOCK_SIZE; uint64_t BE_BYTES = 1ull << COMPRESS_FILE_BLOCK_EXCESS; #endif uint64_t total_in = 0, total_out = 0; uint8_t *inbuf, *outbuf[2]; inbuf=(uint8_t*)REALLOC(0, BS_BYTES+BE_BYTES); outbuf[0]=(uint8_t*)REALLOC(0, BS_BYTES*1.1+BE_BYTES); outbuf[1]=(uint8_t*)(cnum > 1 ? REALLOC(0, BS_BYTES*1.1+BE_BYTES) : 0); enum { BLOCK_PREVIEW_CHARS = 8 }; char best_compressors_history[BLOCK_PREVIEW_CHARS+1] = {0}, best_compressors_index = BLOCK_PREVIEW_CHARS-1; uint8_t best = 0; clock_t tm = {0}; double enctime = 0; if( logfile ) tm = clock(); { for( uint32_t inlen; (inlen=fread(inbuf, 1, BS_BYTES, in)) > 0 ; ) { uint32_t outlen[2] = {0}; best = clist[0]; for(unsigned i = 0; i < cnum; ++i) { unsigned compr = clist[i] >> 4; unsigned flags = clist[i] & 15; if(logfile) fprintf(logfile, "\r%11lld -> %11lld %4s.%c %s", (long long)(total_in+inlen), (long long)outlen[0], list[compr].name4, "0123456789ABCDEF"[flags], best_compressors_history); outlen[!!i] = list[compr].encode(inbuf, (unsigned)inlen, outbuf[!!i], BS_BYTES, flags); if(!outlen[!!i]) goto fail; if( i && outlen[1] < outlen[0]) { best = clist[i]; outlen[0] = outlen[1]; uint8_t *swap = outbuf[0]; outbuf[0] = outbuf[1]; outbuf[1] = swap; } if(logfile) fprintf(logfile, "\r%11lld -> %11lld %4s.%c %s", (long long)(total_in+inlen), (long long)outlen[0], list[compr].name4, "0123456789ABCDEF"[flags], best_compressors_history); } uint64_t final = 4 + 1 + outlen[0]; // sizeof(outlen[0]) + sizeof(compressor) + compr data double ratio = final * 100.0 / (inlen ? inlen : 1); if(!(ratio < 97 /* && ((outlen[0] - inlen) >= 64*1024) */ )) best = 0; unsigned compr = best >> 4; unsigned flags = best & 15; if( compr ) { uint8_t packer = (compr << 4) | flags; // store block length + compressor + compr data if( fwrite(&outlen[0], 1, 4, out) != 4 ) goto fail; if( fwrite(&packer, 1, 1, out) != 1 ) goto fail; if( fwrite(outbuf[0], 1, outlen[0], out) != outlen[0] ) goto fail; } else { uint8_t packer = 0; // store block length + no-compressor + raw data if( fwrite(&inlen, 1, 4, out) != 4 ) goto fail; if( fwrite(&packer, 1, 1, out) != 1 ) goto fail; if( fwrite(inbuf, 1, inlen, out) != inlen ) goto fail; } total_in += inlen; total_out += 4 + 1 + (best ? outlen[0] : inlen); best_compressors_index = (best_compressors_index+1) % BLOCK_PREVIEW_CHARS; best_compressors_history[best_compressors_index] = list[compr].name1; best_compressors_history[best_compressors_index+1] = 0; } } if( logfile ) enctime = (clock() - tm) / (double)CLOCKS_PER_SEC; if( logfile ) { double ratio = (total_out - 4 - 1) * 100.0 / (total_in ? total_in : 1); fprintf(logfile, "\r%11lld -> %11lld %4s.%c %5.*f%% c:%.*fs ", (long long)total_in, (long long)total_out - 4 - 1, list[best>>4].name4, "0123456789ABCDEF"[best&15], ratio >= 100 ? 1 : 2, ratio, enctime > 99 ? 1 : enctime > 9 ? 2 : 3, enctime); } pass: goto next; fail: total_out = 0; next: REALLOC( outbuf[1], 0 ); REALLOC( outbuf[0], 0 ); REALLOC( inbuf, 0 ); return (unsigned)total_out; } unsigned file_decode(FILE* in, FILE* out, FILE *logfile) { // multi decoder uint8_t block8; if( fread(&block8, 1,1, in ) < 1 ) return 0; uint8_t excess8; if( fread(&excess8, 1,1, in ) < 1 ) return 0; uint64_t BLOCK_SIZE = 1ull << block8; uint64_t EXCESS = 1ull << excess8; unsigned total = 0, outlen; uint8_t* inbuf=(uint8_t*)REALLOC(0, BLOCK_SIZE+EXCESS); uint8_t* outbuf=(uint8_t*)REALLOC(0, BLOCK_SIZE+EXCESS); clock_t tm = {0}; double dectime = 0; if(logfile) tm = clock(); { for(uint32_t inlen=0, loop=0;fread(&inlen, 1, sizeof(inlen), in)>0;++loop) { if (inlen>(BLOCK_SIZE+EXCESS)) goto fail; uint8_t packer; if( fread(&packer, 1,sizeof(packer), in) <= 0 ) goto fail; if(packer) { // read compressed if (fread(inbuf, 1, inlen, in)!=inlen) goto fail; // decompress uint8_t compressor = packer >> 4; outlen=list[compressor % NUM_COMPRESSORS].decode(inbuf, (unsigned)inlen, outbuf, BLOCK_SIZE); if (!outlen) goto fail; } else { // read raw if (fread(outbuf, 1, inlen, in)!=inlen) goto fail; outlen=inlen; } if (fwrite(outbuf, 1, outlen, out) != outlen) { perror("fwrite() failed"); goto fail; } total += outlen; if( logfile ) fprintf(logfile, "%c\b", "\\|/-"[loop&3] ); } } if( logfile ) dectime = (clock() - tm) / (double)CLOCKS_PER_SEC; if( logfile ) fprintf(logfile, "d:%.*fs ", dectime > 99 ? 1 : dectime > 9 ? 2 : 3, dectime ); pass: goto next; fail: total = 0; next: REALLOC( outbuf, 0 ); REALLOC( inbuf, 0 ); return total; } #endif // COMPRESS_C #line 0 #line 1 "3rd_archive.h" // archive.c pak/zip/tar/dir archivers // - rlyeh, public domain #ifndef ARCHIVE_H #define ARCHIVE_H #define ARCHIVE_VERSION "v1.0.1" #endif // ARCHIVE_H #ifdef ARCHIVE_C //#pragma once #define PAK_C #define ZIP_C #define TAR_C #define DIR_C #endif // ARCHIVE_C //#line 1 "src/zip.c" // zip un/packer. based on JUnzip library by Joonas Pihlajamaa (UNLICENSE) // - rlyeh, public domain. // // notes about compression_level: // - plain integers use DEFLATE. Levels are [0(store)..6(default)..9(max)] // - compress.c compression flags are also supported. Just use LZMA|5, ULZ|9, LZ4X|3, etc. // - see zip_put.c for more info. #ifndef ZIP_H #define ZIP_H #include #include typedef struct zip zip; zip* zip_open(const char *file, const char *mode /*r,w,a*/); // only for (w)rite or (a)ppend mode bool zip_append_file(zip*, const char *entryname, const char *comment, FILE *in, unsigned compress_level); bool zip_append_file_timeinfo(zip*, const char *entryname, const char *comment, FILE *in, unsigned compress_level, struct tm *); bool zip_append_mem(zip*, const char *entryname, const char *comment, const void *in, unsigned inlen, unsigned compress_level); bool zip_append_mem_timeinfo(zip*, const char *entryname, const char *comment, const void *in, unsigned inlen, unsigned compress_level, struct tm *); // only for (r)ead mode int zip_find(zip*, const char *entryname); // convert entry to index. returns <0 if not found. unsigned zip_count(zip*); char* zip_name(zip*, unsigned index); char* zip_modt(zip*, unsigned index); unsigned zip_size(zip*, unsigned index); unsigned zip_hash(zip*, unsigned index); bool zip_file(zip*, unsigned index); // is_file? (dir if name ends with '/'; file otherwise) bool zip_test(zip*, unsigned index); char* zip_comment(zip*, unsigned index); unsigned zip_codec(zip*, unsigned index); unsigned zip_offset(zip*, unsigned index); unsigned zip_excess(zip*, unsigned index); void* zip_extract(zip*, unsigned index); // must free() after use bool zip_extract_file(zip*, unsigned index, FILE *out); unsigned zip_extract_inplace(zip*, unsigned index, void *out, unsigned outlen_with_excess); void zip_close(zip*); #endif // ZIP_H // ----------------------------------------------------------------------------- #ifdef ZIP_C //#pragma once #include #include #include #include #include #ifdef _WIN32 #include // _chsize_s #endif #ifndef REALLOC #define REALLOC realloc #endif #ifndef STRDUP #define STRDUP strdup #endif #ifndef FPRINTF #define FPRINTF(...) ((void)0) // printf for error logging #endif #ifndef ERR #define ERR(NUM, ...) (FPRINTF(stderr, "" __VA_ARGS__), FPRINTF(stderr, "(%s:%d) %s\n", __FILE__, __LINE__, strerror(errno)), /*fflush(stderr),*/ (NUM)) // (NUM) #endif #ifndef COMPRESS #define COMPRESS(...) ((unsigned)0) #endif #ifndef DECOMPRESS #define DECOMPRESS(...) ((unsigned)0) #endif #ifndef BOUNDS #define BOUNDS(...) ((unsigned)0) #endif #ifndef EXCESS #define EXCESS(...) ((unsigned)0) #endif #ifdef COMPRESS_H #undef COMPRESS #undef DECOMPRESS #undef BOUNDS #undef EXCESS static unsigned COMPRESS(const void *in, unsigned inlen, void *out, unsigned outlen, unsigned flags /*[0..1]*/) { return ( flags > 10 ? mem_encode : deflate_encode )(in, inlen, out, outlen, flags); } static unsigned DECOMPRESS(const void *in, unsigned inlen, void *out, unsigned outlen, unsigned flags) { return ( flags > 10 ? mem_decode : deflate_decode )(in, inlen, out, outlen); } static unsigned BOUNDS(unsigned inlen, unsigned flags) { return ( flags > 10 ? mem_bounds : deflate_bounds )(inlen, flags); } static unsigned EXCESS(unsigned flags) { return ( flags > 10 ? mem_excess : deflate_excess )(flags); } #elif defined DEFLATE_H #undef COMPRESS #undef DECOMPRESS #undef BOUNDS #undef EXCESS static unsigned COMPRESS(const void *in, unsigned inlen, void *out, unsigned outlen, unsigned flags /*[0..1]*/) { return deflate_encode(in, inlen, out, outlen, flags); } static unsigned DECOMPRESS(const void *in, unsigned inlen, void *out, unsigned outlen, unsigned flags) { return deflate_decode(in, inlen, out, outlen); } static unsigned BOUNDS(unsigned inlen, unsigned flags) { return deflate_bounds(inlen, flags); } static unsigned EXCESS(unsigned flags) { return deflate_excess(flags); } #endif #pragma pack(push, 1) typedef struct { uint32_t signature; // 0x02014B50 uint16_t versionMadeBy; // unsupported uint16_t versionNeededToExtract; // unsupported uint16_t generalPurposeBitFlag; // unsupported uint16_t compressionMethod; // 0-store,8-deflate uint16_t lastModFileTime; uint16_t lastModFileDate; uint32_t crc32; uint32_t compressedSize; uint32_t uncompressedSize; uint16_t fileNameLength; uint16_t extraFieldLength; // unsupported uint16_t fileCommentLength; // unsupported uint16_t diskNumberStart; // unsupported uint16_t internalFileAttributes; // unsupported uint32_t externalFileAttributes; // unsupported uint32_t relativeOffsetOflocalHeader; } JZGlobalFileHeader; typedef struct { uint32_t signature; // 0x06054b50 uint16_t diskNumber; // unsupported uint16_t centralDirectoryDiskNumber; // unsupported uint16_t numEntriesThisDisk; // unsupported uint16_t numEntries; uint32_t centralDirectorySize; uint32_t centralDirectoryOffset; uint16_t zipCommentLength; // Followed by .ZIP file comment (variable size) } JZEndRecord; #pragma pack(pop) // Verifying that structs are correct sized... typedef int static_assert_sizeof_JZGlobalFileHeader[sizeof(JZGlobalFileHeader) == 46 ? 1:-1]; typedef int static_assert_sizeof_JZEndRecord[sizeof(JZEndRecord) == 22 ? 1:-1]; enum { sizeof_JZLocalFileHeader = 30 }; // Constants enum { JZ_OK = 0, JZ_ERRNO = -1, JZ_BUFFER_SIZE = 65536 }; // Callback prototype for central reading function. Returns Z_OK while done. typedef int (*JZRecordCallback)(FILE *fp, int index, JZGlobalFileHeader *header, char *filename, void *extra, char *comment, void *user_data); // Read ZIP file end record. Will move within file. Returns Z_OK, or error code int jzReadEndRecord(FILE *fp, JZEndRecord *endRecord) { long fileSize, readBytes, i; JZEndRecord *er = 0; if(fseek(fp, 0, SEEK_END)) { return ERR(JZ_ERRNO, "Couldn't go to end of zip file!"); } if((fileSize = ftell(fp)) <= sizeof(JZEndRecord)) { return ERR(JZ_ERRNO, "Too small file to be a zip!"); } unsigned char jzBuffer[JZ_BUFFER_SIZE]; // maximum zip descriptor size readBytes = (fileSize < sizeof(jzBuffer)) ? fileSize : sizeof(jzBuffer); if(fseek(fp, fileSize - readBytes, SEEK_SET)) { return ERR(JZ_ERRNO, "Cannot seek in zip file!"); } if(fread(jzBuffer, 1, readBytes, fp) < readBytes) { return ERR(JZ_ERRNO, "Couldn't read end of zip file!"); } // Naively assume signature can only be found in one place... for(i = readBytes - sizeof(JZEndRecord); i >= 0; i--) { er = (JZEndRecord *)(jzBuffer + i); if(er->signature == 0x06054B50) break; } if(i < 0 || !er) { return ERR(JZ_ERRNO, "End record signature not found in zip!"); } memcpy(endRecord, er, sizeof(JZEndRecord)); JZEndRecord *e = endRecord; FPRINTF(stdout, "end)\n\tsignature: 0x%X\n", e->signature ); // 0x06054b50 FPRINTF(stdout, "\tdiskNumber: %d\n", e->diskNumber ); // unsupported FPRINTF(stdout, "\tcentralDirectoryDiskNumber: %d\n", e->centralDirectoryDiskNumber ); // unsupported FPRINTF(stdout, "\tnumEntriesThisDisk: %d\n", e->numEntriesThisDisk ); // unsupported FPRINTF(stdout, "\tnumEntries: %d\n", e->numEntries ); FPRINTF(stdout, "\tcentralDirectorySize: %u %#x\n", e->centralDirectorySize, e->centralDirectorySize ); FPRINTF(stdout, "\tcentralDirectoryOffset: %u %#x\n", e->centralDirectoryOffset, e->centralDirectoryOffset ); FPRINTF(stdout, "\tzipCommentLength: %d\n---\n", e->zipCommentLength ); if(endRecord->diskNumber || endRecord->centralDirectoryDiskNumber || endRecord->numEntries != endRecord->numEntriesThisDisk) { return ERR(JZ_ERRNO, "Multifile zips not supported!"); } return JZ_OK; } // Read ZIP file global directory. Will move within file. Returns Z_OK, or error code // Callback is called for each record, until callback returns zero int jzReadCentralDirectory(FILE *fp, JZEndRecord *endRecord, JZRecordCallback callback, void *user_data) { JZGlobalFileHeader fileHeader; if(fseek(fp, endRecord->centralDirectoryOffset, SEEK_SET)) { return ERR(JZ_ERRNO, "Cannot seek in zip file!"); } for(int i=0; inumEntries; i++) { FPRINTF(stdout, "%d)\n@-> %lu %#lx\n", i+1, (unsigned long)ftell(fp), (unsigned long)ftell(fp)); long offset = ftell(fp); // store current position if(fread(&fileHeader, 1, sizeof(JZGlobalFileHeader), fp) < sizeof(JZGlobalFileHeader)) { return ERR(JZ_ERRNO, "Couldn't read file header #%d!", i); } JZGlobalFileHeader *g = &fileHeader, copy = *g; FPRINTF(stdout, "\tsignature: %u %#x\n", g->signature, g->signature); // 0x02014B50 FPRINTF(stdout, "\tversionMadeBy: %u %#x\n", g->versionMadeBy, g->versionMadeBy); // unsupported FPRINTF(stdout, "\tversionNeededToExtract: %u %#x\n", g->versionNeededToExtract, g->versionNeededToExtract); // unsupported FPRINTF(stdout, "\tgeneralPurposeBitFlag: %u %#x\n", g->generalPurposeBitFlag, g->generalPurposeBitFlag); // unsupported FPRINTF(stdout, "\tcompressionMethod: %u %#x\n", g->compressionMethod, g->compressionMethod); // 0-store,8-deflate FPRINTF(stdout, "\tlastModFileTime: %u %#x\n", g->lastModFileTime, g->lastModFileTime); FPRINTF(stdout, "\tlastModFileDate: %u %#x\n", g->lastModFileDate, g->lastModFileDate); FPRINTF(stdout, "\tcrc32: %#x\n", g->crc32); FPRINTF(stdout, "\tcompressedSize: %u\n", g->compressedSize); FPRINTF(stdout, "\tuncompressedSize: %u\n", g->uncompressedSize); FPRINTF(stdout, "\tfileNameLength: %u\n", g->fileNameLength); FPRINTF(stdout, "\textraFieldLength: %u\n", g->extraFieldLength); // unsupported FPRINTF(stdout, "\tfileCommentLength: %u\n", g->fileCommentLength); // unsupported FPRINTF(stdout, "\tdiskNumberStart: %u\n", g->diskNumberStart); // unsupported FPRINTF(stdout, "\tinternalFileAttributes: %#x\n", g->internalFileAttributes); // unsupported FPRINTF(stdout, "\texternalFileAttributes: %#x\n", g->externalFileAttributes); // unsupported FPRINTF(stdout, "\trelativeOffsetOflocalHeader: %u %#x\n", g->relativeOffsetOflocalHeader, g->relativeOffsetOflocalHeader); if(fileHeader.signature != 0x02014B50) { return ERR(JZ_ERRNO, "Invalid file header signature %#x #%d!", fileHeader.signature, i); } if(fileHeader.fileNameLength + 1 >= JZ_BUFFER_SIZE) { return ERR(JZ_ERRNO, "Too long file name %u #%d!", fileHeader.fileNameLength, i); } // filename char jzFilename[JZ_BUFFER_SIZE/3]; if(fread(jzFilename, 1, fileHeader.fileNameLength, fp) < fileHeader.fileNameLength) { return ERR(JZ_ERRNO, "Couldn't read filename #%d!", i); } jzFilename[fileHeader.fileNameLength] = '\0'; // NULL terminate // extra block unsigned char jzExtra[JZ_BUFFER_SIZE/3]; if(fread(jzExtra, 1, fileHeader.extraFieldLength, fp) < fileHeader.extraFieldLength) { return ERR(JZ_ERRNO, "Couldn't read extra block #%d!", i); } // comment block char jzComment[JZ_BUFFER_SIZE/3]; if(fread(jzComment, 1, fileHeader.fileCommentLength, fp) < fileHeader.fileCommentLength) { return ERR(JZ_ERRNO, "Couldn't read comment block #%d!", i); } jzComment[fileHeader.fileCommentLength] = '\0'; // NULL terminate // seek to local file header, then skip file header + filename + extra field length if(fseek(fp, fileHeader.relativeOffsetOflocalHeader + sizeof_JZLocalFileHeader - 2 - 2, SEEK_SET)) { return ERR(JZ_ERRNO, "Cannot seek in file!"); } if(fread(&fileHeader.fileNameLength, 1, 2, fp) < 2) { return ERR(JZ_ERRNO, "Couldn't read local filename #%d!", i); } if(fread(&fileHeader.extraFieldLength, 1, 2, fp) < 2) { return ERR(JZ_ERRNO, "Couldn't read local extrafield #%d!", i); } if(fseek(fp, fileHeader.relativeOffsetOflocalHeader + sizeof_JZLocalFileHeader + fileHeader.fileNameLength + fileHeader.extraFieldLength, SEEK_SET)) { return ERR(JZ_ERRNO, "Cannot seek in file!"); } FPRINTF(stdout, "@-> %lu %#lx\n---\n", (unsigned long)ftell(fp), (unsigned long)ftell(fp)); if( JZ_OK != callback(fp, i, &fileHeader, jzFilename, jzExtra, jzComment, user_data) ) break; // keep going while callback returns ok fseek(fp, offset, SEEK_SET); // return to position fseek(fp, sizeof(JZGlobalFileHeader) + copy.fileNameLength, SEEK_CUR); // skip entry fseek(fp, copy.extraFieldLength + copy.fileCommentLength, SEEK_CUR); // skip entry } return JZ_OK; } // Read data from file stream, described by header, to preallocated buffer. Returns Z_OK, or error code int jzReadData(FILE *fp, JZGlobalFileHeader *header, void *out) { if(header->compressionMethod == 0) { // Store - just read it if(fread(out, 1, header->uncompressedSize, fp) < header->uncompressedSize || ferror(fp)) return JZ_ERRNO; } else if((header->compressionMethod & 255) == 8) { // Deflate uint16_t level = header->compressionMethod >> 8; unsigned outlen = header->uncompressedSize; unsigned inlen = header->compressedSize; void *in = REALLOC(0, inlen + 8); // small excess as some decompressors are really wild with output buffers (lz4x) if(in == NULL) return ERR(JZ_ERRNO, "Could not allocate mem for decompress"); unsigned read = fread(in, 1, inlen, fp); if(read != inlen) return ERR(JZ_ERRNO, "Could not read file"); // TODO: more robust read loop unsigned ret = DECOMPRESS(in, inlen, out, outlen, level); REALLOC(in, 0); if(!ret) return ERR(JZ_ERRNO, "Could not decompress"); } else { return JZ_ERRNO; } return JZ_OK; } #define JZHOUR(t) ((t)>>11) #define JZMINUTE(t) (((t)>>5) & 63) #define JZSECOND(t) (((t) & 31) * 2) #define JZTIME(h,m,s) (((h)<<11) + ((m)<<5) + (s)/2) #define JZYEAR(t) (((t)>>9) + 1980) #define JZMONTH(t) (((t)>>5) & 15) #define JZDAY(t) ((t) & 31) #define JZDATE(y,m,d) ((((y)-1980)<<9) + ((m)<<5) + (d)) // end of junzip.c --- struct zip { FILE *in, *out; struct zip_entry { JZGlobalFileHeader header; char timestamp[40]; char *filename; uint64_t offset; void *extra; char *comment; } *entries; unsigned count; }; uint32_t zip__crc32(uint32_t crc, const void *data, size_t n_bytes) { // CRC32 routine is from Björn Samuelsson's public domain implementation at http://home.thep.lu.se/~bjorn/crc/ static uint32_t table[256] = {0}; if(!*table) for(uint32_t i = 0; i < 0x100; ++i) { uint32_t r = i; for(int j = 0; j < 8; ++j) r = (r & 1 ? 0 : (uint32_t)0xEDB88320L) ^ r >> 1; table[i] = r ^ (uint32_t)0xFF000000L; } for(size_t i = 0; i < n_bytes; ++i) { crc = table[(uint8_t)crc ^ ((uint8_t*)data)[i]] ^ crc >> 8; } return crc; } int zip__callback(FILE *fp, int idx, JZGlobalFileHeader *header, char *filename, void *extra, char *comment, void *user_data) { zip *z = user_data; unsigned index = z->count; z->entries = REALLOC(z->entries, (++z->count) * sizeof(struct zip_entry) ); struct zip_entry *e = &z->entries[index]; e->header = *header; e->filename = STRDUP(filename); e->offset = ftell(fp); e->extra = REALLOC(0, header->extraFieldLength); memcpy(e->extra, extra, header->extraFieldLength); e->comment = STRDUP(comment); snprintf(e->timestamp, sizeof(e->timestamp), "%04d/%02d/%02d %02d:%02d:%02d" "%c" "%04d%02d%02d%02d%02d%02d", JZYEAR(header->lastModFileDate), JZMONTH(header->lastModFileDate), JZDAY(header->lastModFileDate), JZHOUR(header->lastModFileTime), JZMINUTE(header->lastModFileTime), JZSECOND(header->lastModFileTime), '\0', // hidden date in base10 JZYEAR(header->lastModFileDate), JZMONTH(header->lastModFileDate), JZDAY(header->lastModFileDate), JZHOUR(header->lastModFileTime), JZMINUTE(header->lastModFileTime), JZSECOND(header->lastModFileTime) ); return JZ_OK; } // zip read int ZIP_DEBUG = 0; int zip_find(zip *z, const char *entryname) { int zip_debug = ZIP_DEBUG; ZIP_DEBUG = 0; if(zip_debug) FPRINTF(stdout, "zip_find(%s)\n", entryname); if( z->in ) for( int i = z->count; --i >= 0; ) { // in case of several copies, grab most recent file (last coincidence) if(zip_debug) FPRINTF(stdout, "\t%d) %s\n", i, z->entries[i].filename); if( 0 == strcmp(entryname, z->entries[i].filename)) return i; } return -1; } bool zip_file(zip *z, unsigned index) { // is_file? (dir if attrib&15 or name ends with '/'; file otherwise) if( z->in && index < z->count ) { char *name = zip_name(z, index); return (name[ strlen(name) ] != '/') && !(z->entries[index].header.externalFileAttributes & 0x10); } return 0; } unsigned zip_count(zip *z) { return z->in ? z->count : 0; } unsigned zip_hash(zip *z, unsigned index) { return z->in && index < z->count ? z->entries[index].header.crc32 : 0; } char *zip_modt(zip *z, unsigned index) { return z->in && index < z->count ? z->entries[index].timestamp : 0; } char *zip_name(zip *z, unsigned index) { return z->in && index < z->count ? z->entries[index].filename : NULL; } char *zip_comment(zip *z, unsigned index) { return z->in && index < z->count ? z->entries[index].comment : NULL; } unsigned zip_size(zip *z, unsigned index) { return z->in && index < z->count ? z->entries[index].header.uncompressedSize : 0; } unsigned zip_offset(zip *z, unsigned index) { return z->in && index < z->count ? z->entries[index].offset : 0; } unsigned zip_codec(zip *z, unsigned index) { if( z->in && index < z->count ) { unsigned cm = z->entries[index].header.compressionMethod; return cm < 255 ? cm : cm >> 8; } return 0; } unsigned zip_excess(zip *z, unsigned index) { if( z->in && index < z->count ) { unsigned level = z->entries[index].header.compressionMethod; unsigned flags = level >> 8; return EXCESS(flags); } return 0; } unsigned zip_extract_inplace(zip *z, unsigned index, void *out, unsigned outlen) { if( z->in && index < z->count ) { JZGlobalFileHeader *header = &(z->entries[index].header); if( outlen >= header->uncompressedSize ) { fseek(z->in, z->entries[index].offset, SEEK_SET); int ret = jzReadData(z->in, header, (char*)out); return ret == JZ_OK ? header->uncompressedSize : 0; } } return 0; } void *zip_extract(zip *z, unsigned index) { // must free() if( z->in && index < z->count ) { unsigned outlen = (unsigned)z->entries[index].header.uncompressedSize; char *out = (char*)REALLOC(0, outlen + 1 + zip_excess(z, index)); unsigned ret = zip_extract_inplace(z, index, out, outlen); return ret ? (out[outlen] = '\0', out) : (REALLOC(out, 0), out = 0); } return NULL; } bool zip_extract_file(zip *z, unsigned index, FILE *out) { void *data = zip_extract(z, index); if( !data ) return false; unsigned datalen = (unsigned)z->entries[index].header.uncompressedSize; bool ok = fwrite(data, 1, datalen, out) == datalen; REALLOC( data, 0 ); return ok; } bool zip_test(zip *z, unsigned index) { void *ret = zip_extract(z, index); bool ok = !!ret; REALLOC(ret, 0); return ok; } // zip append/write bool zip_append_file(zip *z, const char *entryname, const char *comment, FILE *in, unsigned compress_level) { if( !entryname ) return ERR(false, "No filename provided"); struct stat st; struct tm *timeinfo; stat(entryname, &st); timeinfo = localtime(&st.st_mtime); return zip_append_file_timeinfo(z, entryname, comment, in, compress_level, timeinfo); } bool zip_append_file_timeinfo(zip *z, const char *entryname, const char *comment, FILE *in, unsigned compress_level, struct tm* timeinfo) { if( !in ) return ERR(false, "No input file provided"); if( !entryname ) return ERR(false, "No filename provided"); if( !timeinfo ) return ERR(false, "No timeinfo provided"); // @fixme: calc whole crc contents uint32_t crc = 0; unsigned char buf[1<<15]; while(!feof(in) && !ferror(in)) crc = zip__crc32(crc, buf, fread(buf, 1, sizeof(buf), in)); if(ferror(in)) return ERR(false, "Error while calculating CRC, skipping store."); unsigned index = z->count; z->entries = REALLOC(z->entries, (++z->count) * sizeof(struct zip_entry)); if(z->entries == NULL) return ERR(false, "Failed to allocate new entry!"); struct zip_entry *e = &z->entries[index], zero = {0}; *e = zero; e->filename = STRDUP(entryname); e->comment = comment ? STRDUP(comment) : 0; e->header.signature = 0x02014B50; e->header.versionMadeBy = 10; // random stuff e->header.versionNeededToExtract = 10; e->header.generalPurposeBitFlag = 0; e->header.lastModFileTime = JZTIME(timeinfo->tm_hour, timeinfo->tm_min, timeinfo->tm_sec); e->header.lastModFileDate = JZDATE(timeinfo->tm_year+1900,timeinfo->tm_mon+1,timeinfo->tm_mday); e->header.crc32 = crc; e->header.uncompressedSize = ftell(in); e->header.fileNameLength = strlen(entryname); e->header.extraFieldLength = 0; e->header.fileCommentLength = comment ? strlen(comment) : 0; e->header.diskNumberStart = 0; e->header.internalFileAttributes = 0; e->header.externalFileAttributes = 0x20; // whatever this is e->header.relativeOffsetOflocalHeader = ftell(z->out); #if defined _MSC_VER || (defined __TINYC__ && defined _WIN32) static __declspec(thread) #else static __thread #endif void* comp = 0, *data = 0; // if(comp) comp = REALLOC(comp, 1); // re-entry optimization: hopefully the allocator will optimize this out (N>1-byte) // if(data) data = REALLOC(data, 1); // re-entry optimization: hopefully the allocator will optimize this out (N>1-byte) if(!compress_level) goto dont_compress; // Read whole file and and use compress(). Simple but won't handle GB files well. unsigned dataSize = e->header.uncompressedSize, compSize = BOUNDS(e->header.uncompressedSize, compress_level); comp = REALLOC(0, compSize); if(comp == NULL) goto cant_compress; data = REALLOC(0, dataSize + 8); // small excess as some compressors are really wild when reading from buffers (lz4x) if(data == NULL) goto cant_compress; else memset((char*)data + dataSize, 0, 8); fseek(in, 0, SEEK_SET); // rewind size_t bytes = fread(data, 1, dataSize, in); if(bytes != dataSize) { return ERR(false, "Failed to read file in full (%lu vs. %ld bytes)", (unsigned long)bytes, dataSize); } compSize = COMPRESS(data, (unsigned)dataSize, comp, (unsigned)compSize, compress_level); if(!compSize) goto cant_compress; if(compSize >= (dataSize * 0.98) ) goto dont_compress; uint16_t cl = 8 | (compress_level > 10 ? compress_level << 8 : 0); e->header.compressedSize = compSize; e->header.compressionMethod = cl; goto common; cant_compress: dont_compress:; e->header.compressedSize = ftell(in); e->header.compressionMethod = 0; // store method common:; // write local header uint32_t signature = 0x04034B50; fwrite(&signature, 1, sizeof(signature), z->out); fwrite(&(e->header.versionNeededToExtract), 1, sizeof_JZLocalFileHeader - sizeof(signature), z->out); // write filename fwrite(entryname, 1, strlen(entryname), z->out); // write comment // if( comment ) fwrite(comment, 1, strlen(comment), z->out); if(e->header.compressionMethod) { // store compressed blob fwrite(comp, compSize, 1, z->out); } else { // store uncompressed blob fseek(in, 0, SEEK_SET); while(!feof(in) && !ferror(in)) { size_t bytes = fread(buf, 1, sizeof(buf), in); fwrite(buf, 1, bytes, z->out); } } // REALLOC(comp, 0); // see re-entry optimization above // REALLOC(data, 0); // see re-entry optimization above return true; } bool zip_append_mem(zip *z, const char *entryname, const char *comment, const void *in, unsigned inlen, unsigned compress_level) { if( !entryname ) return ERR(false, "No filename provided"); struct stat st; struct tm *timeinfo; stat(entryname, &st); timeinfo = localtime(&st.st_mtime); return zip_append_mem_timeinfo(z, entryname, comment, in, inlen, compress_level, timeinfo); } bool zip_append_mem_timeinfo(zip *z, const char *entryname, const char *comment, const void *in, unsigned inlen, unsigned compress_level, struct tm* timeinfo) { if( !in ) return ERR(false, "No input file provided"); if( !entryname ) return ERR(false, "No filename provided"); if( !timeinfo ) return ERR(false, "No timeinfo provided"); uint32_t crc = zip__crc32(0, in, inlen); unsigned index = z->count; z->entries = REALLOC(z->entries, (++z->count) * sizeof(struct zip_entry)); if(z->entries == NULL) return ERR(false, "Failed to allocate new entry!"); struct zip_entry *e = &z->entries[index], zero = {0}; *e = zero; e->filename = STRDUP(entryname); e->comment = comment ? STRDUP(comment) : 0; e->header.signature = 0x02014B50; e->header.versionMadeBy = 10; // random stuff e->header.versionNeededToExtract = 10; e->header.generalPurposeBitFlag = 0; e->header.lastModFileTime = JZTIME(timeinfo->tm_hour, timeinfo->tm_min, timeinfo->tm_sec); e->header.lastModFileDate = JZDATE(timeinfo->tm_year+1900,timeinfo->tm_mon+1,timeinfo->tm_mday); e->header.crc32 = crc; e->header.uncompressedSize = inlen; e->header.fileNameLength = strlen(entryname); e->header.extraFieldLength = 0; e->header.fileCommentLength = comment ? strlen(comment) : 0; e->header.diskNumberStart = 0; e->header.internalFileAttributes = 0; e->header.externalFileAttributes = 0x20; // whatever this is e->header.relativeOffsetOflocalHeader = ftell(z->out); #if defined _MSC_VER || (defined __TINYC__ && defined _WIN32) static __declspec(thread) #else static __thread #endif void* comp = 0, *data = 0; // if(comp) comp = REALLOC(comp, 1); // re-entry optimization: hopefully the allocator will optimize this out (N>1-byte) // if(data) data = REALLOC(data, 1); // re-entry optimization: hopefully the allocator will optimize this out (N>1-byte) if(!compress_level) goto dont_compress; // Read whole file and and use compress(). Simple but won't handle GB files well. unsigned dataSize = e->header.uncompressedSize, compSize = BOUNDS(e->header.uncompressedSize, compress_level); comp = REALLOC(0, compSize); if(comp == NULL) goto cant_compress; data = REALLOC(0, dataSize + 8); // small excess as some compressors are really wild when reading from buffers (lz4x) if(data == NULL) goto cant_compress; else memset((char*)data + dataSize, 0, 8); size_t bytes = inlen; memcpy(data, in, inlen); if(bytes != dataSize) { return ERR(false, "Failed to read file in full (%lu vs. %ld bytes)", (unsigned long)bytes, dataSize); } compSize = COMPRESS(data, (unsigned)dataSize, comp, (unsigned)compSize, compress_level); if(!compSize) goto cant_compress; if(compSize >= (dataSize * 0.98) ) goto dont_compress; uint16_t cl = 8 | (compress_level > 10 ? compress_level << 8 : 0); e->header.compressedSize = compSize; e->header.compressionMethod = cl; goto common; cant_compress: dont_compress:; e->header.compressedSize = inlen; e->header.compressionMethod = 0; // store method common:; // write local header uint32_t signature = 0x04034B50; fwrite(&signature, 1, sizeof(signature), z->out); fwrite(&(e->header.versionNeededToExtract), 1, sizeof_JZLocalFileHeader - sizeof(signature), z->out); // write filename fwrite(entryname, 1, strlen(entryname), z->out); // write comment // if( comment ) fwrite(comment, 1, strlen(comment), z->out); if(e->header.compressionMethod) { // store compressed blob fwrite(comp, compSize, 1, z->out); } else { // store uncompressed blob fwrite(in, 1, inlen, z->out); } // REALLOC(comp, 0); // see re-entry optimization above // REALLOC(data, 0); // see re-entry optimization above return true; } // zip common zip* zip_open(const char *file, const char *mode /*r,w,a*/) { struct stat buffer; int exists = (stat(file, &buffer) == 0); if( mode[0] == 'a' && !exists ) mode = "wb"; FILE *fp = fopen(file, mode[0] == 'w' ? "wb" : mode[0] == 'a' ? "a+b" : "rb"); if( !fp ) return ERR(NULL, "cannot open file for %s mode", mode); zip zero = {0}, *z = (zip*)REALLOC(0, sizeof(zip)); if( !z ) return fclose(fp), ERR(NULL, "out of mem"); else *z = zero; if( mode[0] == 'w' ) { z->out = fp; return z; } if( mode[0] == 'r' || mode[0] == 'a' ) { z->in = fp; JZEndRecord jzEndRecord = {0}; if(jzReadEndRecord(fp, &jzEndRecord) != JZ_OK) { REALLOC(z, 0); return fclose(fp), ERR(NULL, "Couldn't read ZIP file end record."); } if(jzReadCentralDirectory(fp, &jzEndRecord, zip__callback, z) != JZ_OK) { REALLOC(z, 0); return fclose(fp), ERR(NULL, "Couldn't read ZIP file central directory."); } if( mode[0] == 'a' ) { // resize (by truncation) size_t resize = jzEndRecord.centralDirectoryOffset; int fd = fileno(fp); if( fd != -1 ) { #ifdef _WIN32 int ok = 0 == _chsize_s( fd, resize ); #else int ok = 0 == ftruncate( fd, (off_t)resize ); #endif fflush(fp); fseek( fp, 0L, SEEK_END ); } z->in = NULL; z->out = fp; } return z; } REALLOC(z, 0); return fclose(fp), ERR(NULL, "Unknown open mode %s", mode); } void zip_close(zip* z) { if( z->out && z->count ) { // prepare end record JZEndRecord end = {0}; end.signature = 0x06054b50; end.diskNumber = 0; end.centralDirectoryDiskNumber = 0; end.numEntriesThisDisk = z->count; end.numEntries = z->count; end.centralDirectoryOffset = ftell(z->out); // flush global directory: global file+filename each for(unsigned i = 0; i < z->count; i++) { struct zip_entry *h = &z->entries[i]; JZGlobalFileHeader *g = &h->header; fwrite(g, 1, sizeof(JZGlobalFileHeader), z->out); fwrite(h->filename, 1, g->fileNameLength, z->out); fwrite(h->extra, 1, g->extraFieldLength, z->out); fwrite(h->comment, 1, g->fileCommentLength, z->out); } end.centralDirectorySize = ftell(z->out) - end.centralDirectoryOffset; end.zipCommentLength = 0; // flush end record fwrite(&end, 1, sizeof(end), z->out); } if( z->out ) fclose(z->out); if( z->in ) fclose(z->in); // clean up for(unsigned i = 0; i < z->count; ++i ) { REALLOC(z->entries[i].filename, 0); if(z->entries[i].extra) REALLOC(z->entries[i].extra, 0); if(z->entries[i].comment) REALLOC(z->entries[i].comment, 0); } if(z->entries) REALLOC(z->entries, 0); zip zero = {0}; *z = zero; REALLOC(z, 0); } #endif // ZIP_C //#line 1 "src/tar.c" // gnu tar and ustar extraction // - rlyeh, public domain. #ifndef TAR_H #define TAR_H typedef struct tar tar; tar *tar_open(const char *filename, const char *mode); int tar_find(tar*, const char *entryname); // returns entry number; or <0 if not found. unsigned tar_count(tar*); char* tar_name(tar*, unsigned index); unsigned tar_size(tar*, unsigned index); unsigned tar_offset(tar*, unsigned index); void* tar_extract(tar*, unsigned index); // must free() after use void tar_close(tar *t); #endif // ----------------------------------------------------------------------------- #ifdef TAR_C //#pragma once #include #include #include #include #ifndef STRDUP #define STRDUP strdup #endif #ifndef REALLOC #define REALLOC realloc #endif #ifndef ERR #define ERR(NUM, ...) (fprintf(stderr, "" __VA_ARGS__), fprintf(stderr, "(%s:%d)\n", __FILE__, __LINE__), fflush(stderr), (NUM)) // (NUM) #endif struct tar { FILE *in; unsigned count; struct tar_entry { char *filename; unsigned size; size_t offset; } *entries; }; // equivalent to sscanf(buf, 8, "%.7o", &size); or (12, "%.11o", &modtime) // ignores everything after first null or space, including trailing bytes uint64_t tar__octal( const char *src, const char *eof ) { uint64_t sum = 0, mul = 1; const char *ptr = eof; while( ptr-- >= src ) eof = ( 0 != ptr[1] && 32 != ptr[1] ) ? eof : ptr; while( eof-- >= src ) sum += (uint8_t)(eof[1] - '0') * mul, mul *= 8; return sum; } typedef int (*tar_callback)(const char *filename, unsigned inlen, size_t offset, void *userdata); int tar__push_entry(const char *filename, unsigned inlen, size_t offset, void *userdata) { tar *t = (tar *)userdata; unsigned index = t->count; t->entries = REALLOC(t->entries, (++t->count) * sizeof(struct tar_entry)); struct tar_entry *e = &t->entries[index]; e->filename = STRDUP(filename); e->size = inlen; e->offset = offset; return 1; } int tar__parse( FILE *in, tar_callback yield, void *userdata ) { enum { name = 0, // (null terminated) mode = 100, // (octal) uid = 108, // (octal) gid = 116, // (octal) size = 124, // (octal) modtime = 136, // (octal) checksum = 148, // (octal) type = 156, // \0|'0':file,1:hardlink,2:symlink,3:chardev,4:blockdev,5:dir,6:fifo,L:longnameblocknext linkname = 157, // if !ustar link indicator magic = 257, // if ustar "ustar" -- 6th character may be space or null, else zero version = 263, // if ustar "00", else zero uname = 265, // if ustar owner username, else zero gname = 297, // if ustar owner groupname, else zero devmajor = 329, // if ustar device major number, else zero devminor = 337, // if ustar device minor number , else zero path = 345, // if ustar filename prefix, else zero padding = 500, // if ustar relevant for checksum, else zero total = 512 }; // handle both regular tar and ustar tar filenames until end of tar is found char header[512], entry[512], blank[512] = {0}; while( !ferror(in) ) { if( 512 != fread(header, 1, 512, in ) ) break; if( memcmp( header, blank, 512 ) ) { // if not end of tar if( !memcmp( header+magic, "ustar", 5 ) ) { // if valid ustar int namelen = strlen(header+name), pathlen = strlen(header+path); // read filename snprintf(entry, 512, "%.*s" "%s" "%.*s", pathlen < 155 ? pathlen : 155, header+path, pathlen ? "/" : "", namelen < 100 ? namelen : 100, header+name ); switch( header[type] ) { default: // unsupported file type break; case '5': //yield(entry.back()!='/'?entry+'/':entry,0);// directory break; case 'L': strcpy(entry, header+name); fread(header,1,512,in); // gnu tar long filename break; case '0': case 0: { // regular file uint64_t len = tar__octal(header+size, header+modtime); // decode octal size int cont = yield(entry, len, ftell(in), userdata); // yield entry fseek(in,len,SEEK_CUR); // skip blob fseek(in,(512 - (len & 511)) & 511,SEEK_CUR); // skip padding } } } else return ERR(0, "not a .tar file"); } else return ferror(in) ? ERR(0, "file error") : 1; } return ERR(0, "read error"); } // --- tar *tar_open(const char *filename, const char *mode) { if(mode[0] != 'r') return ERR(NULL, "(w) and (a) not supported for now"); FILE *in = fopen(filename, "rb"); if(!in) return ERR(NULL, "cant open file '%s' for reading", filename); tar zero = {0}, *t = REALLOC(0, sizeof(tar)); if( !t ) { fclose(in); return ERR(NULL, "out of mem"); } *t = zero; t->in = in; tar__parse(in, tar__push_entry, t); return t; } int tar_find(tar *t, const char *entryname) { if( t->in ) for( int i = t->count; --i >= 0; ) { // in case of several copies, grab most recent file (last coincidence) if( 0 == strcmp(entryname, t->entries[i].filename)) return i; } return -1; } unsigned tar_count(tar *t) { return t ? t->count : 0; } char* tar_name(tar *t, unsigned index) { return t && index < t->count ? t->entries[index].filename : 0; } unsigned tar_size(tar *t, unsigned index) { return t && index < t->count ? t->entries[index].size : 0; } unsigned tar_offset(tar *t, unsigned index) { return t && index < t->count ? (unsigned)t->entries[index].offset : 0; } void *tar_extract(tar *t, unsigned index) { if( t && index < t->count ) { fseek(t->in, t->entries[index].offset, SEEK_SET); size_t len = t->entries[index].size; void *data = REALLOC(0, t->entries[index].size); fread(data, 1, len, t->in); return data; } return 0; } void tar_close(tar *t) { fclose(t->in); for( int i = 0; i < t->count; ++i) { REALLOC(t->entries[i].filename, 0); } tar zero = {0}; *t = zero; REALLOC(t, 0); } #ifdef TAR_DEMO int main( int argc, char **argv ) { if(argc <= 1) exit(printf("%s file.tar [file_to_view]\n", argv[0])); tar *t = tar_open(argv[1], "rb"); if( t ) { for( int i = 0; i < tar_count(t); ++i ) { printf("%d) %s (%u bytes)\n", i+1, tar_name(t,i), tar_size(t,i)); char *data = tar_extract(t,i); if(argc>2) if(0==strcmp(argv[2],tar_name(t,i))) printf("%.*s\n", tar_size(t,i), data); free(data); } tar_close(t); } } #define main main__ #endif //TAR_DEMO #endif //TAR_C //#line 1 "src/pak.c" // pak file reading/writing/appending. // - rlyeh, public domain. // // ## PAK // - [ref] https://quakewiki.org/wiki/.pak (public domain). // - Header: 12 bytes // - "PACK" 4-byte // - directory offset uint32 // - directory size uint32 (number of files by dividing this by 64, which is sizeof(pak_entry)) // // - File Directory Entry (Num files * 64 bytes) // - Each Directory Entry: 64 bytes // - file name 56-byte null-terminated string. Includes path. Example: "maps/e1m1.bsp". // - file offset uint32 from beginning of pak file. // - file size uint32 #ifndef PAK_H #define PAK_H typedef struct pak pak; pak* pak_open(const char *fname, const char *mode /*a,r,w*/); // (w)rite or (a)ppend modes only int pak_append_file(pak*, const char *filename, FILE *in); int pak_append_data(pak*, const char *filename, const void *in, unsigned inlen); // (r)ead only mode int pak_find(pak*,const char *fname); // return <0 if error; index otherwise. unsigned pak_count(pak*); unsigned pak_size(pak*,unsigned index); unsigned pak_offset(pak*, unsigned index); char *pak_name(pak*,unsigned index); void *pak_extract(pak*, unsigned index); // must free() after use void pak_close(pak*); #endif // --- #ifdef PAK_C //#pragma once #include #include #include #include #ifndef REALLOC #define REALLOC realloc #endif #ifndef ERR #define ERR(NUM, ...) (fprintf(stderr, "" __VA_ARGS__), fprintf(stderr, "(%s:%d)\n", __FILE__, __LINE__), fflush(stderr), (NUM)) // (NUM) #endif #include static inline uint32_t pak_swap32( uint32_t t ) { return (t >> 24) | (t << 24) | ((t >> 8) & 0xff00) | ((t & 0xff00) << 8); } #if defined(_M_IX86) || defined(_M_X64) // #ifdef LITTLE #define htob32(x) pak_swap32(x) #define btoh32(x) pak_swap32(x) #define htol32(x) (x) #define ltoh32(x) (x) #else #define htob32(x) (x) #define btoh32(x) (x) #define htol32(x) pak_swap32(x) #define ltoh32(x) pak_swap32(x) #endif #pragma pack(push, 1) typedef struct pak_header { char id[4]; uint32_t offset; uint32_t size; } pak_header; typedef struct pak_file { char name[56]; uint32_t offset; uint32_t size; } pak_file; #pragma pack(pop) typedef int static_assert_sizeof_pak_header[sizeof(pak_header) == 12 ? 1:-1]; typedef int static_assert_sizeof_pak_file[sizeof(pak_file) == 64 ? 1:-1]; typedef struct pak { FILE *in, *out; int dummy; pak_file *entries; unsigned count; } pak; pak *pak_open(const char *fname, const char *mode) { struct stat buffer; int exists = (stat(fname, &buffer) == 0); if(mode[0] == 'a' && !exists ) mode = "wb"; if(mode[0] != 'w' && mode[0] != 'r' && mode[0] != 'a') return NULL; FILE *fp = fopen(fname, mode[0] == 'w' ? "wb" : mode[0] == 'r' ? "rb" : "r+b"); if(!fp) return ERR(NULL, "cant open file '%s' in '%s' mode", fname, mode); pak *p = malloc(sizeof(pak)), zero = {0}; if(!p) return fclose(fp), ERR(NULL, "out of mem"); *p = zero; if( mode[0] == 'r' || mode[0] == 'a' ) { pak_header header = {0}; if( fread(&header, 1, sizeof(pak_header), fp) != sizeof(pak_header) ) { return fclose(fp), ERR(NULL, "read error"); } if( memcmp(header.id, "PACK", 4) ) { return fclose(fp), ERR(NULL, "not a .pak file"); } header.offset = ltoh32(header.offset); header.size = ltoh32(header.size); unsigned num_files = header.size / sizeof(pak_file); if( fseek(fp, header.offset, SEEK_SET) != 0 ) { return fclose(fp), ERR(NULL, "read error"); } p->count = num_files; p->entries = REALLOC(0, num_files * sizeof(pak_file)); if( fread(p->entries, num_files, sizeof(pak_file), fp) != sizeof(pak_file) ) { goto fail; } for( unsigned i = 0; i < num_files; ++i ) { pak_file *e = &p->entries[i]; e->offset = ltoh32(e->offset); e->size = ltoh32(e->size); } if( mode[0] == 'a' ) { // resize (by truncation) size_t resize = header.offset; int fd = fileno(fp); if( fd != -1 ) { #ifdef _WIN32 int ok = 0 == _chsize_s( fd, resize ); #else int ok = 0 == ftruncate( fd, (off_t)resize ); #endif fflush(fp); fseek( fp, 0L, SEEK_END ); } p->out = fp; p->in = NULL; } else { p->in = fp; } return p; } if(mode[0] == 'w') { p->out = fp; // write temporary header char header[12] = {0}; if( fwrite(header, 1,12, p->out) != 12) goto fail; return p; } fail:; if(fp) fclose(fp); if(p->entries) REALLOC(p->entries, 0); if(p) REALLOC(p, 0); return NULL; } int pak_append_data(pak *p, const char *filename, const void *in, unsigned inlen) { if(!p->out) return ERR(0, "read-only pak file"); // index meta unsigned index = p->count++; p->entries = REALLOC(p->entries, p->count * sizeof(pak_file)); pak_file *e = &p->entries[index], zero = {0}; *e = zero; snprintf(e->name, 55, "%s", filename); // @todo: verify 56 chars limit e->size = inlen; e->offset = ftell(p->out); // write blob fwrite(in, 1, inlen, p->out); return !ferror(p->out); } int pak_append_file(pak *p, const char *filename, FILE *in) { // index meta unsigned index = p->count++; p->entries = REALLOC(p->entries, p->count * sizeof(pak_file)); pak_file *e = &p->entries[index], zero = {0}; *e = zero; snprintf(e->name, 55, "%s", filename); // @todo: verify 56 chars limit e->offset = ftell(p->out); char buf[1<<15]; while(!feof(in) && !ferror(in)) { size_t bytes = fread(buf, 1, sizeof(buf), in); fwrite(buf, 1, bytes, p->out); } e->size = ftell(p->out) - e->offset; return !ferror(p->out); } void pak_close(pak *p) { if(p->out) { // write toc uint32_t seek = 0 + 12, dirpos = (uint32_t)ftell(p->out), dirlen = p->count * 64; for(unsigned i = 0; i < p->count; ++i) { pak_file *e = &p->entries[i]; // write name (truncated if needed), and trailing zeros char zero[56] = {0}; int namelen = strlen(e->name); fwrite( e->name, 1, namelen >= 56 ? 55 : namelen, p->out ); fwrite( zero, 1, namelen >= 56 ? 1 : 56 - namelen, p->out ); // write offset + length pair uint32_t pseek = htol32(seek); fwrite( &pseek, 1,4, p->out ); uint32_t psize = htol32(e->size); fwrite( &psize, 1,4, p->out ); seek += e->size; } // patch header fseek(p->out, 0L, SEEK_SET); fwrite("PACK", 1,4, p->out); dirpos = htol32(dirpos); fwrite( &dirpos, 1,4, p->out ); dirlen = htol32(dirlen); fwrite( &dirlen, 1,4, p->out ); } // close streams if(p->in) fclose(p->in); if(p->out) fclose(p->out); // clean up for(unsigned i = 0; i < p->count; ++i) { pak_file *e = &p->entries[i]; } REALLOC(p->entries, 0); // delete pak zero = {0}; *p = zero; REALLOC(p, 0); } int pak_find(pak *p, const char *filename) { if( p->in ) { for( int i = p->count; --i >= 0; ) { if(!strcmp(p->entries[i].name, filename)) return i; } } return -1; } unsigned pak_count(pak *p) { return p->in ? p->count : 0; } unsigned pak_offset(pak *p, unsigned index) { return p->in && index < p->count ? p->entries[index].offset : 0; } unsigned pak_size(pak *p, unsigned index) { return p->in && index < p->count ? p->entries[index].size : 0; } char *pak_name(pak *p, unsigned index) { return p->in && index < p->count ? p->entries[index].name : NULL; } void *pak_extract(pak *p, unsigned index) { if( p->in && index < p->count ) { pak_file *e = &p->entries[index]; if( fseek(p->in, e->offset, SEEK_SET) != 0 ) { return ERR(NULL, "cant seek"); } void *buffer = REALLOC(0, e->size); if( !buffer ) { return ERR(NULL, "out of mem"); } if( fread(buffer, 1, e->size, p->in) != e->size ) { REALLOC(buffer, 0); return ERR(NULL, "cant read"); } return buffer; } return NULL; } #ifdef PAK_DEMO int main(int argc, char **argv) { puts("creating test.pak archive (3) ..."); pak *p = pak_open("test.pak", "wb"); if( p ) { pak_append_data(p, "/index.txt", "just a file", strlen("just a file")); pak_append_data(p, "/file/name1.txt", "just another file #1", strlen("just another file #1")); pak_append_data(p, "/file/name2.txt", "just another file #2", strlen("just another file #2")); pak_close(p); } puts("appending file to test.pak (1) ..."); p = pak_open("test.pak", "a+b"); if( p ) { pak_append_data(p, "/new/file", "this is an appended file", strlen("this is an appended file")); pak_close(p); } const char *fname = argc > 1 ? argv[1] : "test.pak"; printf("listing %s archive ...\n", fname); p = pak_open(fname, "rb"); if( p ) { for( unsigned i = 0; i < pak_count(p); ++i ) { printf(" %d) @%08x %11u %s ", i+1, pak_offset(p,i), pak_size(p,i), pak_name(p,i)); void *data = pak_extract(p,i); printf("\r%c\n", data ? 'Y':'N'); if(argc > 2 && data) if(i == pak_find(p,argv[2])) printf("%.*s\n", (int)pak_size(p,i), (char*)data); free(data); } pak_close(p); } puts("ok"); } #endif // PAK_DEMO #endif // PAK_C //#line 1 "src/dir.c" // directory iteration. // - rlyeh, public domain. #ifndef DIR_H #define DIR_H typedef struct dir dir; dir *dir_open(const char *filename, const char *mode); // recursive 'r' int dir_find(dir*, const char *entryname); // returns entry number; or <0 if not found. unsigned dir_count(dir*); char* dir_name(dir*, unsigned index); unsigned dir_size(dir*, unsigned index); unsigned dir_file(dir*, unsigned index); // dir_isfile? bool? void* dir_read(dir*, unsigned index); // must free() after use void dir_close(dir*); #endif // ----------------------------------------------------------------------------- #ifdef DIR_C //#pragma once #include #include #include #include #include # if defined _WIN32 && defined(__TINYC__) #include // tcc #elif defined _WIN32 #include // msc+gcc #else #include #endif #ifndef STRDUP #define STRDUP strdup #endif #ifndef REALLOC #define REALLOC realloc #endif #ifndef ERR #define ERR(NUM, ...) (fprintf(stderr, "" __VA_ARGS__), fprintf(stderr, "(%s:%d)\n", __FILE__, __LINE__), fflush(stderr), (NUM)) // (NUM) #endif typedef struct dir_entry { char *filename; size_t size; size_t is_dir : 1; } dir_entry; struct dir { dir_entry *entry; unsigned count; bool recursive; }; // --- #if !defined(S_ISDIR) # define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR) #endif int dir_yield(dir *d, const char *pathfile, char *name, int namelen) { int ok = 0; #ifdef _WIN32 WIN32_FIND_DATAA fdata = { 0 }; snprintf(name, namelen, "%s/*", pathfile); for( HANDLE h = FindFirstFileA(name, &fdata ); h != INVALID_HANDLE_VALUE; ok = (FindClose( h ), h = INVALID_HANDLE_VALUE, 1)) { for( int next = 1; next; next = FindNextFileA(h, &fdata) != 0 ) { if( fdata.cFileName[0] == '.' ) continue; int is_dir = (fdata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) > 0; snprintf(name, namelen, "%s/%s%s", pathfile, fdata.cFileName, is_dir ? "/" : ""); struct stat st; if( !is_dir ) if(stat(name, &st) < 0) continue; // add dir_entry de = { STRDUP(name), is_dir ? 0 : st.st_size, is_dir }; d->entry = (dir_entry*)REALLOC(d->entry, ++d->count * sizeof(dir_entry)); d->entry[d->count-1] = de; // recurse if (is_dir && d->recursive) { char pf[512]; snprintf(pf, 512, "%.*s", (int)strlen(name) - 1, name); name[0] = 0; dir_yield(d, pf, name, namelen); } } } #else snprintf(name, namelen, "%s/", pathfile); for( DIR *dir = opendir(name); dir; ok = (closedir(dir), dir = 0, 1)) { for( struct dirent *ep; (ep = readdir(dir)) != NULL; ) { if( ep->d_name[0] == '.' ) continue; snprintf(name, namelen, "%s/%s", pathfile, ep->d_name); struct stat st; if( stat(name, &st) < 0 ) continue; DIR *tmp = opendir(/*ep->d_*/name); int is_dir = !!tmp; if(tmp) closedir(tmp); // add dir_entry de = { STRDUP(name), is_dir ? 0 : st.st_size, is_dir }; d->entry = (dir_entry*)REALLOC(d->entry, ++d->count * sizeof(dir_entry)); d->entry[d->count-1] = de; // recurse if (is_dir && d->recursive) { char pf[512]; snprintf(pf, 512, "%s", name); name[0] = 0; dir_yield(d, pf, name, namelen); } } } #endif return ok; } dir *dir_open(const char *pathfile, const char *mode) { dir *d = (dir*)REALLOC(0, sizeof(dir)), zero = {0}; *d = zero; d->recursive = (mode[0] == 'R' || mode[0] == 'r'); char *clean = STRDUP( pathfile ); for( int i = 0; clean[i]; ++i ) if(clean[i] == '\\') clean[i] = '/'; for( int len = strlen(clean); clean[--len] == '/'; ) clean[len] = '\0'; char buffer[2048]; dir_yield(d, clean, buffer, 2048); REALLOC(clean, 0); return d; } int dir_find(dir *d, const char *entryname) { for( int i = d->count; --i >= 0; ) { // in case of several copies, grab most recent file (last coincidence) if( 0 == strcmp(entryname, d->entry[i].filename)) return i; } return -1; } unsigned dir_count(dir *d) { return d ? d->count : 0; } char* dir_name(dir *d, unsigned index) { return d && index < d->count ? d->entry[index].filename : 0; } unsigned dir_size(dir *d, unsigned index) { return d && index < d->count ? (unsigned)d->entry[index].size : 0; } unsigned dir_file(dir *d, unsigned index) { return d && index < d->count ? (unsigned)!d->entry[index].is_dir : 0; } void *dir_read(dir *d, unsigned index) { if( d && index < d->count ) { void *data = 0; for( FILE *fp = fopen(d->entry[index].filename, "rb"); fp; fclose(fp), fp = 0) { size_t len = d->entry[index].size; data = REALLOC(0, len); if( data && fread(data, 1, len, fp) != len ) { data = REALLOC(data, 0); } } return data; } return 0; } void dir_close(dir *d) { for( int i = 0; i < d->count; ++i) { REALLOC(d->entry[i].filename, 0); } dir zero = {0}; *d = zero; REALLOC(d, 0); } #ifdef DIR_DEMO int main( int argc, char **argv ) { dir *d = dir_open(argc > 1 ? argv[1] : "./", "rb"); if( d ) { for( int i = 0; i < dir_count(d); ++i ) { if( dir_file(d,i) ) printf("%3d) %11d %s\n", i + 1, dir_size(d,i), dir_name(d,i)); else printf("%3d) %11s %s\n", i + 1, "

", dir_name(d,i)); char *data = dir_read(d,i); if(argc > 2 && !strcmp(argv[2],dir_name(d,i))) printf("%.*s\n", dir_size(d,i), data); free(data); } dir_close(d); } } #define main main__ #endif //DIR_DEMO #endif //DIR_C #line 0 #line 1 "3rd_thread.h" /* ------------------------------------------------------------------------------ Licensing information can be found at the end of the file. ------------------------------------------------------------------------------ thread.h - v0.31 - Cross platform threading functions for C/C++. Do this: #define THREAD_IMPLEMENTATION before you include this file in *one* C/C++ file to create the implementation. */ #ifndef thread_h #define thread_h #ifndef THREAD_U64 #define THREAD_U64 unsigned long long #endif #define THREAD_HAS_ATOMIC 1 // ref: https://github.com/ufbx/ufbx/blob/master/ufbx.c // ref: https://github.com/gingerBill/gb/blob/master/gb.h #if defined __TINYC__ && !defined _WIN32 #undef THREAD_HAS_ATOMIC #define THREAD_HAS_ATOMIC 0 #if defined(__x86_64__) || defined(_AMD64_) static size_t tcc_atomic_add(volatile size_t *dst, size_t value) { __asm__ __volatile__("lock; xaddq %0, %1;" : "+r" (value), "=m" (*dst) : "m" (dst)); return value; } static size_t tcc_atomic_compare_exchange(volatile size_t *dst, size_t expected, size_t desired) { size_t original; __asm__ __volatile__( "lock; cmpxchgq %2, %1" : "=a"(original), "+m"(*dst) : "q"(desired), "0"(expected) ); return original; } static size_t tcc_atomic_exchanged(volatile size_t *dst, size_t desired) { size_t original; __asm__ __volatile__( "xchgq %0, %1" : "=r"(original), "+m"(*dst) : "0"(desired) ); return original; } #elif defined(__i386__) || defined(_X86_) static size_t tcc_atomic_add(volatile size_t *dst, size_t value) { __asm__ __volatile__("lock; xaddl %0, %1;" : "+r" (value), "=m" (*dst) : "m" (dst)); return value; } static size_t tcc_atomic_compare_exchange(volatile size_t *a, size_t expected, size_t desired) { size_t original; __asm__ __volatile__( "lock; cmpxchgl %2, %1" : "=a"(original), "+m"(*dst) : "q"(desired), "0"(expected) ); return original; } static size_t tcc_atomic_exchanged(volatile size_t *a, size_t desired) { size_t original; __asm__ __volatile__( "xchgl %0, %1" : "=r"(original), "+m"(*dst) : "0"(desired) ); return original; } #else #error Unexpected TCC architecture #endif typedef volatile size_t thread_atomic_int_t; #define thread_atomic_int_inc(ptr) tcc_atomic_add((ptr), 1) #define thread_atomic_int_dec(ptr) tcc_atomic_add((ptr), SIZE_MAX) //#define thread_atomic_int_add(ptr,v) tcc_atomic_add((ptr), (v)) //#define thread_atomic_int_sub(ptr,v) tcc_atomic_add((ptr), -(v)) // meh #define thread_atomic_int_load(ptr) (*(ptr) = 0) // meh #define thread_atomic_int_store(ptr,v) (*(ptr) = (v)) // meh //#define thread_atomic_int_swap(ptr,desired) tcc_atomic_exchanged((ptr),desired) #define thread_atomic_int_compare_and_swap(ptr,expected,desired) tcc_atomic_compare_exchange((ptr),(expected),(desired)) #elif defined __TINYC__ && defined _WIN32 #define THREAD_USE_MCMP 1 #endif #define THREAD_STACK_SIZE_DEFAULT ( 0 ) #define THREAD_SIGNAL_WAIT_INFINITE ( -1 ) #define THREAD_QUEUE_WAIT_INFINITE ( -1 ) typedef void* thread_id_t; thread_id_t thread_current_thread_id( void ); void thread_yield( void ); void thread_set_high_priority( void ); void thread_exit( int return_code ); typedef void* thread_ptr_t; thread_ptr_t thread_init( int (*thread_proc)( void* ), void* user_data, char const* name, int stack_size ); //< @r-lyeh thread_create -> thread_init void thread_term( thread_ptr_t thread ); //< @r-lyeh: renamed thread_destroy -> thread_term int thread_join( thread_ptr_t thread ); int thread_detach( thread_ptr_t thread ); typedef union thread_mutex_t thread_mutex_t; void thread_mutex_init( thread_mutex_t* mutex ); void thread_mutex_term( thread_mutex_t* mutex ); void thread_mutex_lock( thread_mutex_t* mutex ); void thread_mutex_unlock( thread_mutex_t* mutex ); typedef union thread_signal_t thread_signal_t; void thread_signal_init( thread_signal_t* signal ); void thread_signal_term( thread_signal_t* signal ); void thread_signal_raise( thread_signal_t* signal ); int thread_signal_wait( thread_signal_t* signal, int timeout_ms ); #if THREAD_HAS_ATOMIC typedef union thread_atomic_int_t thread_atomic_int_t; int thread_atomic_int_load( thread_atomic_int_t* atomic ); void thread_atomic_int_store( thread_atomic_int_t* atomic, int desired ); int thread_atomic_int_inc( thread_atomic_int_t* atomic ); int thread_atomic_int_dec( thread_atomic_int_t* atomic ); int thread_atomic_int_add( thread_atomic_int_t* atomic, int value ); int thread_atomic_int_sub( thread_atomic_int_t* atomic, int value ); int thread_atomic_int_swap( thread_atomic_int_t* atomic, int desired ); int thread_atomic_int_compare_and_swap( thread_atomic_int_t* atomic, int expected, int desired ); typedef union thread_atomic_ptr_t thread_atomic_ptr_t; void* thread_atomic_ptr_load( thread_atomic_ptr_t* atomic ); void thread_atomic_ptr_store( thread_atomic_ptr_t* atomic, void* desired ); void* thread_atomic_ptr_swap( thread_atomic_ptr_t* atomic, void* desired ); void* thread_atomic_ptr_compare_and_swap( thread_atomic_ptr_t* atomic, void* expected, void* desired ); #endif typedef union thread_timer_t thread_timer_t; void thread_timer_init( thread_timer_t* timer ); void thread_timer_term( thread_timer_t* timer ); void thread_timer_wait( thread_timer_t* timer, THREAD_U64 nanoseconds ); typedef void* thread_tls_t; thread_tls_t thread_tls_create( void ); void thread_tls_destroy( thread_tls_t tls ); void thread_tls_set( thread_tls_t tls, void* value ); void* thread_tls_get( thread_tls_t tls ); typedef struct thread_queue_t thread_queue_t; void thread_queue_init( thread_queue_t* queue, int size, void** values, int count ); void thread_queue_term( thread_queue_t* queue ); int thread_queue_produce( thread_queue_t* queue, void* value, int timeout_ms ); void* thread_queue_consume( thread_queue_t* queue, int timeout_ms ); int thread_queue_count( thread_queue_t* queue ); #if THREAD_USE_MCMP struct mcmp; int mcmp_new(struct mcmp *ctx); int mcmp_del(struct mcmp *ctx); int mcmp_add(struct mcmp *ctx, void *data); void *mcmp_pop(struct mcmp *ctx ); #define thread_queue_t struct mcmp #define thread_queue_init(t,a,b,c) mcmp_new(t) #define thread_queue_produce(t,v,a) mcmp_add(t,v) #define thread_queue_consume(t,a) mcmp_pop(t) #define thread_queue_term(t) mcmp_del(t) #define thread_queue_count(t) exit(-123) #endif #endif /* thread_h */ /** Example ======= Here's a basic sample program which starts a second thread which just waits and prints a message. #define THREAD_IMPLEMENTATION #include "thread.h" #include // for printf int thread_proc( void* user_data) { thread_timer_t timer; thread_timer_init( &timer ); int count = 0; thread_atomic_int_t* exit_flag = (thread_atomic_int_t*) user_data; while( thread_atomic_int_load( exit_flag ) == 0 ) { printf( "Thread... " ); thread_timer_wait( &timer, 1000000000 ); // sleep for a second ++count; } thread_timer_term( &timer ); printf( "Done\n" ); return count; } int main( int argc, char** argv ) { (void) argc, argv; thread_atomic_int_t exit_flag; thread_atomic_int_store( &exit_flag, 0 ); thread_ptr_t thread = thread_init( thread_proc, &exit_flag, "Example thread", THREAD_STACK_SIZE_DEFAULT ); thread_timer_t timer; thread_timer_init( &timer ); for( int i = 0; i < 5; ++i ) { printf( "Main... " ); thread_timer_wait( &timer, 2000000000 ); // sleep for two seconds } thread_timer_term( &timer ); thread_atomic_int_store( &exit_flag, 1 ); // signal thread to exit int retval = thread_join( thread ); printf( "Count: %d\n", retval ); thread_term( thread ); return retval; } API Documentation ================= thread.h is a single-header library, and does not need any .lib files or other binaries, or any build scripts. To use it, you just include thread.h to get the API declarations. To get the definitions, you must include thread.h from *one* single C or C++ file, and #define the symbol `THREAD_IMPLEMENTATION` before you do. Customization ------------- thread.h allows for specifying the exact type of 64-bit unsigned integer to be used in its API. By default, it is defined as `unsigned long long`, but as this is not a standard type on all compilers, you can redefine it by #defining THREAD_U64 before including thread.h. This is useful if you, for example, use the types from `` in the rest of your program, and you want thread.h to use compatible types. In this case, you would include thread.h using the following code: #define THREAD_U64 uint64_t #include "thread.h" Note that when customizing this data type, you need to use the same definition in every place where you include thread.h, as it affect the declarations as well as the definitions. thread_current_thread_id ------------------------ thread_id_t thread_current_thread_id( void ) Returns a unique identifier for the calling thread. After the thread terminates, the id might be reused for new threads. thread_yield ------------ void thread_yield( void ) Makes the calling thread yield execution to another thread. The operating system controls which thread is switched to. thread_set_high_priority ------------------------ void thread_set_high_priority( void ) When created, threads are set to run at normal priority. In some rare cases, such as a sound buffer update loop, it can be necessary to have one thread of your application run on a higher priority than the rest. Calling `thread_set_high_priority` will raise the priority of the calling thread, giving it a chance to be run more often. Do not increase the priority of a thread unless you absolutely have to, as it can negatively affect performance if used without care. thread_exit ----------- void thread_exit( int return_code ) Exits the calling thread, as if you had done `return return_code;` from the main body of the thread function. thread_init ------------- thread_ptr_t thread_init( int (*thread_proc)( void* ), void* user_data, char const* name, int stack_size ) Creates a new thread running the `thread_proc` function, passing the `user_data` through to it. The thread will be given the debug name given in the `name` parameter, if supported on the platform, and it will have the stack size specified in the `stack_size` parameter. To get the operating system default stack size, use the defined constant `THREAD_STACK_SIZE_DEFAULT`. When returning from the thread_proc function, the value you return can be received in another thread by calling thread_join. `thread_init` returns a pointer to the thread instance, which can be used as a parameter to the functions `thread_term` and `thread_join`. thread_term -------------- void thread_term( thread_ptr_t thread ) Destroys a thread that was created by calling `thread_init`. Make sure the thread has exited before you attempt to destroy it. This can be accomplished by calling `thread_join`. It is not possible for force termination of a thread by calling `thread_term`. thread_join ----------- int thread_join( thread_ptr_t thread ) Waits for the specified thread to exit. Returns the value which the thread returned when exiting. thread_detach ------------- int thread_detach( thread_ptr_t thread ) Marks the thread as detached. When a detached thread terminates, its resources are automatically released back to the system without the need for another thread to join with the terminated thread. thread_mutex_init ----------------- void thread_mutex_init( thread_mutex_t* mutex ) Initializes the specified mutex instance, preparing it for use. A mutex can be used to lock sections of code, such that it can only be run by one thread at a time. thread_mutex_term ----------------- void thread_mutex_term( thread_mutex_t* mutex ) Terminates the specified mutex instance, releasing any system resources held by it. thread_mutex_lock ----------------- void thread_mutex_lock( thread_mutex_t* mutex ) Takes an exclusive lock on a mutex. If the lock is already taken by another thread, `thread_mutex_lock` will yield the calling thread and wait for the lock to become available before returning. The mutex must be initialized by calling `thread_mutex_init` before it can be locked. thread_mutex_unlock ------------------- void thread_mutex_unlock( thread_mutex_t* mutex ) Releases a lock taken by calling `thread_mutex_lock`. thread_signal_init ------------------ void thread_signal_init( thread_signal_t* signal ) Initializes the specified signal instance, preparing it for use. A signal works like a flag, which can be waited on by one thread, until it is raised from another thread. thread_signal_term ------------------ void thread_signal_term( thread_signal_t* signal ) Terminates the specified signal instance, releasing any system resources held by it. thread_signal_raise ------------------- void thread_signal_raise( thread_signal_t* signal ) Raise the specified signal. Other threads waiting for the signal will proceed. thread_signal_wait ------------------ int thread_signal_wait( thread_signal_t* signal, int timeout_ms ) Waits for a signal to be raised, or until `timeout_ms` milliseconds have passed. If the wait timed out, a value of 0 is returned, otherwise a non-zero value is returned. If the `timeout_ms` parameter is THREAD_SIGNAL_WAIT_INFINITE, `thread_signal_wait` waits indefinitely. thread_atomic_int_load ---------------------- int thread_atomic_int_load( thread_atomic_int_t* atomic ) Returns the value of `atomic` as an atomic operation. thread_atomic_int_store ----------------------- void thread_atomic_int_store( thread_atomic_int_t* atomic, int desired ) Sets the value of `atomic` as an atomic operation. thread_atomic_int_inc --------------------- int thread_atomic_int_inc( thread_atomic_int_t* atomic ) Increments the value of `atomic` by one, as an atomic operation. Returns the value `atomic` had before the operation. thread_atomic_int_dec --------------------- int thread_atomic_int_dec( thread_atomic_int_t* atomic ) Decrements the value of `atomic` by one, as an atomic operation. Returns the value `atomic` had before the operation. thread_atomic_int_add --------------------- int thread_atomic_int_add( thread_atomic_int_t* atomic, int value ) Adds the specified value to `atomic`, as an atomic operation. Returns the value `atomic` had before the operation. thread_atomic_int_sub --------------------- int thread_atomic_int_sub( thread_atomic_int_t* atomic, int value ) Subtracts the specified value to `atomic`, as an atomic operation. Returns the value `atomic` had before the operation. thread_atomic_int_swap ---------------------- int thread_atomic_int_swap( thread_atomic_int_t* atomic, int desired ) Sets the value of `atomic` as an atomic operation. Returns the value `atomic` had before the operation. thread_atomic_int_compare_and_swap ---------------------------------- int thread_atomic_int_compare_and_swap( thread_atomic_int_t* atomic, int expected, int desired ) Compares the value of `atomic` to the value of `expected`, and if they match, sets the vale of `atomic` to `desired`, all as an atomic operation. Returns the value `atomic` had before the operation. thread_atomic_ptr_load ---------------------- void* thread_atomic_ptr_load( thread_atomic_ptr_t* atomic ) Returns the value of `atomic` as an atomic operation. thread_atomic_ptr_store ----------------------- void thread_atomic_ptr_store( thread_atomic_ptr_t* atomic, void* desired ) Sets the value of `atomic` as an atomic operation. thread_atomic_ptr_swap ---------------------- void* thread_atomic_ptr_swap( thread_atomic_ptr_t* atomic, void* desired ) Sets the value of `atomic` as an atomic operation. Returns the value `atomic` had before the operation. thread_atomic_ptr_compare_and_swap ---------------------------------- void* thread_atomic_ptr_compare_and_swap( thread_atomic_ptr_t* atomic, void* expected, void* desired ) Compares the value of `atomic` to the value of `expected`, and if they match, sets the vale of `atomic` to `desired`, all as an atomic operation. Returns the value `atomic` had before the operation. thread_timer_init ----------------- void thread_timer_init( thread_timer_t* timer ) Initializes the specified timer instance, preparing it for use. A timer can be used to sleep a thread for a high precision duration. thread_timer_term ----------------- void thread_timer_term( thread_timer_t* timer ) Terminates the specified timer instance, releasing any system resources held by it. thread_timer_wait ----------------- void thread_timer_wait( thread_timer_t* timer, THREAD_U64 nanoseconds ) Waits until `nanoseconds` amount of time have passed, before returning. thread_tls_create ----------------- thread_tls_t thread_tls_create( void ) Creates a thread local storage (TLS) index. Once created, each thread has its own value for that TLS index, which can be set or retrieved individually. thread_tls_destroy ------------------ void thread_tls_destroy( thread_tls_t tls ) Destroys the specified TLS index. No further calls to `thread_tls_set` or `thread_tls_get` are valid after this. thread_tls_set -------------- void thread_tls_set( thread_tls_t tls, void* value ) Stores a value in the calling thread's slot for the specified TLS index. Each thread has its own value for each TLS index. thread_tls_get -------------- void* thread_tls_get( thread_tls_t tls ) Retrieves the value from the calling thread's slot for the specified TLS index. Each thread has its own value for each TLS index. thread_queue_init ----------------- void thread_queue_init( thread_queue_t* queue, int size, void** values, int count ) Initializes the specified queue instance, preparing it for use. The queue is a lock-free (but not wait-free) single-producer/single-consumer queue - it will not acquire any locks as long as there is space for adding or items to be consume, but will lock and wait when there is not. The `size` parameter specifies the number of elements in the queue. The `values` parameter is an array of queue slots (`size` elements in length), each being of type `void*`. If the queue is initially empty, the `count` parameter should be 0, otherwise it indicates the number of entires, from the start of the `values` array, that the queue is initialized with. The `values` array is not copied, and must remain valid until `thread_queue_term` is called. thread_queue_term ----------------- void thread_queue_term( thread_queue_t* queue ) Terminates the specified queue instance, releasing any system resources held by it. thread_queue_produce -------------------- int thread_queue_produce( thread_queue_t* queue, void* value, int timeout_ms ) Adds an element to a single-producer/single-consumer queue. If there is space in the queue to add another element, no lock will be taken. If the queue is full, calling thread will sleep until an element is consumed from another thread, before adding the element, or until `timeout_ms` milliseconds have passed. If the wait timed out, a value of 0 is returned, otherwise a non-zero value is returned. If the `timeout_ms` parameter is THREAD_QUEUE_WAIT_INFINITE, `thread_queue_produce` waits indefinitely. thread_queue_consume -------------------- void* thread_queue_consume( thread_queue_t* queue, int timeout_ms ) Removes an element from a single-producer/single-consumer queue. If the queue contains at least one element, no lock will be taken. If the queue is empty, the calling thread will sleep until an element is added from another thread, or until `timeout_ms` milliseconds have passed. If the wait timed out, a value of NULL is returned, otherwise `thread_queue_consume` returns the value that was removed from the queue. If the `timeout_ms` parameter is THREAD_QUEUE_WAIT_INFINITE, `thread_queue_consume` waits indefinitely. thread_queue_count ------------------ int thread_queue_count( thread_queue_t* queue ) Returns the number of elements currently held in a single-producer/single-consumer queue. Be aware that by the time you get the count, it might have changed by another thread calling consume or produce, so use with care. **/ /* ---------------------- IMPLEMENTATION ---------------------- */ #ifndef thread_impl #define thread_impl union thread_mutex_t { void* align; char data[ 64 ]; }; union thread_signal_t { void* align; char data[ 116 ]; }; union thread_atomic_int_t { void* align; long i; }; union thread_atomic_ptr_t { void* ptr; }; union thread_timer_t { void* data; char d[ 8 ]; }; #if !THREAD_USE_MCMP struct thread_queue_t { thread_signal_t data_ready; thread_signal_t space_open; thread_atomic_int_t count; thread_atomic_int_t head; thread_atomic_int_t tail; void** values; int size; #ifndef NDEBUG thread_atomic_int_t id_produce_is_set; thread_id_t id_produce; thread_atomic_int_t id_consume_is_set; thread_id_t id_consume; #endif }; #endif #endif /* thread_impl */ #ifdef THREAD_IMPLEMENTATION #undef THREAD_IMPLEMENTATION #if defined( _WIN32 ) #pragma comment( lib, "winmm" ) //< @r-lyeh, tcc support (remove .lib) #define _CRT_NONSTDC_NO_DEPRECATE #define _CRT_SECURE_NO_WARNINGS #if !defined( _WIN32_WINNT ) || _WIN32_WINNT < 0x0501 #undef _WIN32_WINNT #define _WIN32_WINNT 0x501// requires Windows XP minimum #endif #define _WINSOCKAPI_ #pragma warning( push ) #pragma warning( disable: 4668 ) // 'symbol' is not defined as a preprocessor macro, replacing with '0' for 'directives' #pragma warning( disable: 4255 ) #include #pragma warning( pop ) // To set thread name const DWORD MS_VC_EXCEPTION = 0x406D1388; #pragma pack( push, 8 ) typedef struct tagTHREADNAME_INFO { DWORD dwType; LPCSTR szName; DWORD dwThreadID; DWORD dwFlags; } THREADNAME_INFO; #pragma pack(pop) #elif defined( __linux__ ) || defined( __APPLE__ ) || defined( __ANDROID__ ) || defined( __EMSCRIPTEN__ ) //< @r-lyeh, ems #ifndef _GNU_SOURCE #define _GNU_SOURCE //< @r-lyeh: pthread_setname_np() #endif #include #include #include #else #error Unknown platform. #endif #ifndef NDEBUG #include #endif thread_id_t thread_current_thread_id( void ) { #if defined( _WIN32 ) return (void*) (uintptr_t)GetCurrentThreadId(); #elif defined( __linux__ ) || defined( __APPLE__ ) || defined( __ANDROID__ ) || defined( __EMSCRIPTEN__ ) //< @r-lyeh, ems return (void*) pthread_self(); #else #error Unknown platform. #endif } void thread_yield( void ) { #if defined( _WIN32 ) SwitchToThread(); #elif defined( __linux__ ) || defined( __APPLE__ ) || defined( __ANDROID__ ) || defined( __EMSCRIPTEN__ ) //< @r-lyeh, ems sched_yield(); #else #error Unknown platform. #endif } void thread_exit( int return_code ) { #if defined( _WIN32 ) ExitThread( (DWORD) return_code ); #elif defined( __linux__ ) || defined( __APPLE__ ) || defined( __ANDROID__ ) || defined( __EMSCRIPTEN__ ) //< @r-lyeh, ems pthread_exit( (void*)(uintptr_t) return_code ); #else #error Unknown platform. #endif } thread_ptr_t thread_init( int (*thread_proc)( void* ), void* user_data, char const* name, int stack_size ) { #if defined( _WIN32 ) DWORD thread_id; HANDLE handle = CreateThread( NULL, stack_size > 0 ? (size_t)stack_size : 0U, (LPTHREAD_START_ROUTINE)(uintptr_t) thread_proc, user_data, 0, &thread_id ); if( !handle ) return NULL; #ifdef _MSC_VER //< @r-lyeh: fix mingw64 // Yes, this crazy construct with __try and RaiseException is how you name a thread in Visual Studio :S if( name && IsDebuggerPresent() ) { THREADNAME_INFO info; info.dwType = 0x1000; info.szName = name; info.dwThreadID = thread_id; info.dwFlags = 0; __try { RaiseException( MS_VC_EXCEPTION, 0, sizeof( info ) / sizeof( ULONG_PTR ), (ULONG_PTR*) &info ); } __except( EXCEPTION_EXECUTE_HANDLER ) { } } #endif return (thread_ptr_t) handle; #elif defined( __linux__ ) || defined( __APPLE__ ) || defined( __ANDROID__ ) || defined( __EMSCRIPTEN__ ) //< @r-lyeh, ems pthread_t thread; if( 0 != pthread_create( &thread, NULL, ( void* (*)( void * ) ) thread_proc, user_data ) ) return NULL; #if !defined( __APPLE__ ) && !defined( __EMSCRIPTEN__ ) // max doesn't support pthread_setname_np. alternatives? //< @r-lyeh, ems if( name ) pthread_setname_np( thread, name ); #endif return (thread_ptr_t) thread; #else #error Unknown platform. #endif } void thread_term( thread_ptr_t thread ) { #if defined( _WIN32 ) WaitForSingleObject( (HANDLE) thread, INFINITE ); CloseHandle( (HANDLE) thread ); #elif defined( __linux__ ) || defined( __APPLE__ ) || defined( __ANDROID__ ) || defined( __EMSCRIPTEN__ ) //< @r-lyeh, ems pthread_join( (pthread_t) thread, NULL ); #else #error Unknown platform. #endif } int thread_join( thread_ptr_t thread ) { #if defined( _WIN32 ) WaitForSingleObject( (HANDLE) thread, INFINITE ); DWORD retval; GetExitCodeThread( (HANDLE) thread, &retval ); return (int) retval; #elif defined( __linux__ ) || defined( __APPLE__ ) || defined( __ANDROID__ ) || defined( __EMSCRIPTEN__ ) //< @r-lyeh, ems void* retval; pthread_join( (pthread_t) thread, &retval ); return (int)(uintptr_t) retval; #else #error Unknown platform. #endif } int thread_detach( thread_ptr_t thread ) { #if defined( _WIN32 ) return CloseHandle( (HANDLE) thread ) != 0; #elif defined( __linux__ ) || defined( __APPLE__ ) || defined( __ANDROID__ ) || defined( __EMSCRIPTEN__ ) //< @r-lyeh, ems return pthread_detach( (pthread_t) thread ) == 0; #else #error Unknown platform. #endif } void thread_set_high_priority( void ) { #if defined( _WIN32 ) SetThreadPriority( GetCurrentThread(), THREAD_PRIORITY_HIGHEST ); #elif defined( __linux__ ) || defined( __APPLE__ ) || defined( __ANDROID__ ) || defined( __EMSCRIPTEN__ ) //< @r-lyeh, ems struct sched_param sp; memset( &sp, 0, sizeof( sp ) ); sp.sched_priority = sched_get_priority_min( SCHED_RR ); pthread_setschedparam( pthread_self(), SCHED_RR, &sp); #else #error Unknown platform. #endif } void thread_mutex_init( thread_mutex_t* mutex ) { #if defined( _WIN32 ) // Compile-time size check #pragma warning( push ) #pragma warning( disable: 4214 ) // nonstandard extension used: bit field types other than int struct x { char thread_mutex_type_too_small : ( sizeof( thread_mutex_t ) < sizeof( CRITICAL_SECTION ) ? 0 : 1 ); }; #pragma warning( pop ) InitializeCriticalSectionAndSpinCount( (CRITICAL_SECTION*) mutex, 32 ); #elif defined( __linux__ ) || defined( __APPLE__ ) || defined( __ANDROID__ ) || defined( __EMSCRIPTEN__ ) //< @r-lyeh, ems // Compile-time size check struct x { char thread_mutex_type_too_small : ( sizeof( thread_mutex_t ) < sizeof( pthread_mutex_t ) ? 0 : 1 ); }; pthread_mutex_init( (pthread_mutex_t*) mutex, NULL ); #else #error Unknown platform. #endif } void thread_mutex_term( thread_mutex_t* mutex ) { #if defined( _WIN32 ) DeleteCriticalSection( (CRITICAL_SECTION*) mutex ); #elif defined( __linux__ ) || defined( __APPLE__ ) || defined( __ANDROID__ ) || defined( __EMSCRIPTEN__ ) //< @r-lyeh, ems pthread_mutex_destroy( (pthread_mutex_t*) mutex ); #else #error Unknown platform. #endif } void thread_mutex_lock( thread_mutex_t* mutex ) { #if defined( _WIN32 ) EnterCriticalSection( (CRITICAL_SECTION*) mutex ); #elif defined( __linux__ ) || defined( __APPLE__ ) || defined( __ANDROID__ ) || defined( __EMSCRIPTEN__ ) //< @r-lyeh, ems pthread_mutex_lock( (pthread_mutex_t*) mutex ); #else #error Unknown platform. #endif } void thread_mutex_unlock( thread_mutex_t* mutex ) { #if defined( _WIN32 ) LeaveCriticalSection( (CRITICAL_SECTION*) mutex ); #elif defined( __linux__ ) || defined( __APPLE__ ) || defined( __ANDROID__ ) || defined( __EMSCRIPTEN__ ) //< @r-lyeh, ems pthread_mutex_unlock( (pthread_mutex_t*) mutex ); #else #error Unknown platform. #endif } struct thread_internal_signal_t { #if defined( _WIN32 ) #if _WIN32_WINNT >= 0x0600 CRITICAL_SECTION mutex; CONDITION_VARIABLE condition; int value; #else HANDLE event; #endif #elif defined( __linux__ ) || defined( __APPLE__ ) || defined( __ANDROID__ ) || defined( __EMSCRIPTEN__ ) //< @r-lyeh, ems pthread_mutex_t mutex; pthread_cond_t condition; int value; #else #error Unknown platform. #endif }; void thread_signal_init( thread_signal_t* signal ) { // Compile-time size check #pragma warning( push ) #pragma warning( disable: 4214 ) // nonstandard extension used: bit field types other than int struct x { char thread_signal_type_too_small : ( sizeof( thread_signal_t ) < sizeof( struct thread_internal_signal_t ) ? 0 : 1 ); }; #pragma warning( pop ) struct thread_internal_signal_t* internal = (struct thread_internal_signal_t*) signal; #if defined( _WIN32 ) #if _WIN32_WINNT >= 0x0600 InitializeCriticalSectionAndSpinCount( &internal->mutex, 32 ); InitializeConditionVariable( &internal->condition ); internal->value = 0; #else internal->event = CreateEvent( NULL, FALSE, FALSE, NULL ); #endif #elif defined( __linux__ ) || defined( __APPLE__ ) || defined( __ANDROID__ ) || defined( __EMSCRIPTEN__ ) //< @r-lyeh, ems pthread_mutex_init( &internal->mutex, NULL ); pthread_cond_init( &internal->condition, NULL ); internal->value = 0; #else #error Unknown platform. #endif } void thread_signal_term( thread_signal_t* signal ) { struct thread_internal_signal_t* internal = (struct thread_internal_signal_t*) signal; #if defined( _WIN32 ) #if _WIN32_WINNT >= 0x0600 DeleteCriticalSection( &internal->mutex ); #else CloseHandle( internal->event ); #endif #elif defined( __linux__ ) || defined( __APPLE__ ) || defined( __ANDROID__ ) || defined( __EMSCRIPTEN__ ) //< @r-lyeh, ems pthread_mutex_destroy( &internal->mutex ); pthread_cond_destroy( &internal->condition ); #else #error Unknown platform. #endif } void thread_signal_raise( thread_signal_t* signal ) { struct thread_internal_signal_t* internal = (struct thread_internal_signal_t*) signal; #if defined( _WIN32 ) #if _WIN32_WINNT >= 0x0600 EnterCriticalSection( &internal->mutex ); internal->value = 1; LeaveCriticalSection( &internal->mutex ); WakeConditionVariable( &internal->condition ); #else SetEvent( internal->event ); #endif #elif defined( __linux__ ) || defined( __APPLE__ ) || defined( __ANDROID__ ) || defined( __EMSCRIPTEN__ ) //< @r-lyeh, ems pthread_mutex_lock( &internal->mutex ); internal->value = 1; pthread_mutex_unlock( &internal->mutex ); pthread_cond_signal( &internal->condition ); #else #error Unknown platform. #endif } int thread_signal_wait( thread_signal_t* signal, int timeout_ms ) { struct thread_internal_signal_t* internal = (struct thread_internal_signal_t*) signal; #if defined( _WIN32 ) #if _WIN32_WINNT >= 0x0600 int timed_out = 0; EnterCriticalSection( &internal->mutex ); while( internal->value == 0 ) { BOOL res = SleepConditionVariableCS( &internal->condition, &internal->mutex, timeout_ms < 0 ? INFINITE : timeout_ms ); if( !res && GetLastError() == ERROR_TIMEOUT ) { timed_out = 1; break; } } internal->value = 0; LeaveCriticalSection( &internal->mutex ); return !timed_out; #else int failed = WAIT_OBJECT_0 != WaitForSingleObject( internal->event, timeout_ms < 0 ? INFINITE : timeout_ms ); return !failed; #endif #elif defined( __linux__ ) || defined( __APPLE__ ) || defined( __ANDROID__ ) || defined( __EMSCRIPTEN__ ) //< @r-lyeh, ems struct timespec ts; if( timeout_ms >= 0 ) { struct timeval tv; gettimeofday( &tv, NULL ); ts.tv_sec = time( NULL ) + timeout_ms / 1000; ts.tv_nsec = tv.tv_usec * 1000 + 1000 * 1000 * ( timeout_ms % 1000 ); ts.tv_sec += ts.tv_nsec / ( 1000 * 1000 * 1000 ); ts.tv_nsec %= ( 1000 * 1000 * 1000 ); } int timed_out = 0; pthread_mutex_lock( &internal->mutex ); while( internal->value == 0 ) { if( timeout_ms < 0 ) pthread_cond_wait( &internal->condition, &internal->mutex ); else if( pthread_cond_timedwait( &internal->condition, &internal->mutex, &ts ) == ETIMEDOUT ) { timed_out = 1; break; } } if( !timed_out ) internal->value = 0; pthread_mutex_unlock( &internal->mutex ); return !timed_out; #else #error Unknown platform. #endif } #if THREAD_HAS_ATOMIC int thread_atomic_int_load( thread_atomic_int_t* atomic ) { #if defined( _WIN32 ) return InterlockedCompareExchange( &atomic->i, 0, 0 ); #elif defined( __linux__ ) || defined( __APPLE__ ) || defined( __ANDROID__ ) || defined( __EMSCRIPTEN__ ) //< @r-lyeh, ems return (int)__sync_fetch_and_add( &atomic->i, 0 ); #else #error Unknown platform. #endif } void thread_atomic_int_store( thread_atomic_int_t* atomic, int desired ) { #if defined( _WIN32 ) InterlockedExchange( &atomic->i, desired ); #elif defined( __linux__ ) || defined( __APPLE__ ) || defined( __ANDROID__ ) || defined( __EMSCRIPTEN__ ) //< @r-lyeh, ems __sync_fetch_and_and( &atomic->i, 0 ); __sync_fetch_and_or( &atomic->i, desired ); #else #error Unknown platform. #endif } int thread_atomic_int_inc( thread_atomic_int_t* atomic ) { #if defined( _WIN32 ) return InterlockedIncrement( &atomic->i ) - 1; #elif defined( __linux__ ) || defined( __APPLE__ ) || defined( __ANDROID__ ) || defined( __EMSCRIPTEN__ ) //< @r-lyeh, ems return (int)__sync_fetch_and_add( &atomic->i, 1 ); #else #error Unknown platform. #endif } int thread_atomic_int_dec( thread_atomic_int_t* atomic ) { #if defined( _WIN32 ) return InterlockedDecrement( &atomic->i ) + 1; #elif defined( __linux__ ) || defined( __APPLE__ ) || defined( __ANDROID__ ) || defined( __EMSCRIPTEN__ ) //< @r-lyeh, ems return (int)__sync_fetch_and_sub( &atomic->i, 1 ); #else #error Unknown platform. #endif } int thread_atomic_int_add( thread_atomic_int_t* atomic, int value ) { #if defined( _WIN32 ) return InterlockedExchangeAdd ( &atomic->i, value ); #elif defined( __linux__ ) || defined( __APPLE__ ) || defined( __ANDROID__ ) || defined( __EMSCRIPTEN__ ) //< @r-lyeh, ems return (int)__sync_fetch_and_add( &atomic->i, value ); #else #error Unknown platform. #endif } int thread_atomic_int_sub( thread_atomic_int_t* atomic, int value ) { #if defined( _WIN32 ) return InterlockedExchangeAdd( &atomic->i, -value ); #elif defined( __linux__ ) || defined( __APPLE__ ) || defined( __ANDROID__ ) || defined( __EMSCRIPTEN__ ) //< @r-lyeh, ems return (int)__sync_fetch_and_sub( &atomic->i, value ); #else #error Unknown platform. #endif } int thread_atomic_int_swap( thread_atomic_int_t* atomic, int desired ) { #if defined( _WIN32 ) return InterlockedExchange( &atomic->i, desired ); #elif defined( __linux__ ) || defined( __APPLE__ ) || defined( __ANDROID__ ) || defined( __EMSCRIPTEN__ ) //< @r-lyeh, ems int old = (int)__sync_lock_test_and_set( &atomic->i, desired ); __sync_lock_release( &atomic->i ); return old; #else #error Unknown platform. #endif } int thread_atomic_int_compare_and_swap( thread_atomic_int_t* atomic, int expected, int desired ) { #if defined( _WIN32 ) return InterlockedCompareExchange( &atomic->i, desired, expected ); #elif defined( __linux__ ) || defined( __APPLE__ ) || defined( __ANDROID__ ) || defined( __EMSCRIPTEN__ ) //< @r-lyeh, ems return (int)__sync_val_compare_and_swap( &atomic->i, expected, desired ); #else #error Unknown platform. #endif } void* thread_atomic_ptr_load( thread_atomic_ptr_t* atomic ) { #if defined( _WIN32 ) return InterlockedCompareExchangePointer( &atomic->ptr, 0, 0 ); #elif defined( __linux__ ) || defined( __APPLE__ ) || defined( __ANDROID__ ) || defined( __EMSCRIPTEN__ ) //< @r-lyeh, ems return __sync_fetch_and_add( &atomic->ptr, 0 ); #else #error Unknown platform. #endif } void thread_atomic_ptr_store( thread_atomic_ptr_t* atomic, void* desired ) { #if defined( _WIN32 ) #pragma warning( push ) #pragma warning( disable: 4302 ) // 'type cast' : truncation from 'void *' to 'LONG' #pragma warning( disable: 4311 ) // pointer truncation from 'void *' to 'LONG' #pragma warning( disable: 4312 ) // conversion from 'LONG' to 'PVOID' of greater size InterlockedExchangePointer( &atomic->ptr, desired ); #pragma warning( pop ) #elif defined( __linux__ ) || defined( __APPLE__ ) || defined( __ANDROID__ ) || defined( __EMSCRIPTEN__ ) //< @r-lyeh, ems __sync_lock_test_and_set( &atomic->ptr, desired ); __sync_lock_release( &atomic->ptr ); #else #error Unknown platform. #endif } void* thread_atomic_ptr_swap( thread_atomic_ptr_t* atomic, void* desired ) { #if defined( _WIN32 ) #pragma warning( push ) #pragma warning( disable: 4302 ) // 'type cast' : truncation from 'void *' to 'LONG' #pragma warning( disable: 4311 ) // pointer truncation from 'void *' to 'LONG' #pragma warning( disable: 4312 ) // conversion from 'LONG' to 'PVOID' of greater size return InterlockedExchangePointer( &atomic->ptr, desired ); #pragma warning( pop ) #elif defined( __linux__ ) || defined( __APPLE__ ) || defined( __ANDROID__ ) || defined( __EMSCRIPTEN__ ) //< @r-lyeh, ems void* old = __sync_lock_test_and_set( &atomic->ptr, desired ); __sync_lock_release( &atomic->ptr ); return old; #else #error Unknown platform. #endif } void* thread_atomic_ptr_compare_and_swap( thread_atomic_ptr_t* atomic, void* expected, void* desired ) { #if defined( _WIN32 ) return InterlockedCompareExchangePointer( &atomic->ptr, desired, expected ); #elif defined( __linux__ ) || defined( __APPLE__ ) || defined( __ANDROID__ ) || defined( __EMSCRIPTEN__ ) //< @r-lyeh, ems return __sync_val_compare_and_swap( &atomic->ptr, expected, desired ); #else #error Unknown platform. #endif } #endif // THREAD_HAS_ATOMIC void thread_timer_init( thread_timer_t* timer ) { #if defined( _WIN32 ) // Compile-time size check #pragma warning( push ) #pragma warning( disable: 4214 ) // nonstandard extension used: bit field types other than int struct x { char thread_timer_type_too_small : ( sizeof( thread_mutex_t ) < sizeof( HANDLE ) ? 0 : 1 ); }; #pragma warning( pop ) TIMECAPS tc; if( timeGetDevCaps( &tc, sizeof( TIMECAPS ) ) == TIMERR_NOERROR ) timeBeginPeriod( tc.wPeriodMin ); *(HANDLE*)timer = CreateWaitableTimer( NULL, TRUE, NULL ); #elif defined( __linux__ ) || defined( __APPLE__ ) || defined( __ANDROID__ ) || defined( __EMSCRIPTEN__ ) //< @r-lyeh, ems // Nothing #else #error Unknown platform. #endif } void thread_timer_term( thread_timer_t* timer ) { #if defined( _WIN32 ) CloseHandle( *(HANDLE*)timer ); TIMECAPS tc; if( timeGetDevCaps( &tc, sizeof( TIMECAPS ) ) == TIMERR_NOERROR ) timeEndPeriod( tc.wPeriodMin ); #elif defined( __linux__ ) || defined( __APPLE__ ) || defined( __ANDROID__ ) || defined( __EMSCRIPTEN__ ) //< @r-lyeh, ems // Nothing #else #error Unknown platform. #endif } void thread_timer_wait( thread_timer_t* timer, THREAD_U64 nanoseconds ) { #if defined( _WIN32 ) LARGE_INTEGER due_time; due_time.QuadPart = - (LONGLONG) ( nanoseconds / 100 ); BOOL b = SetWaitableTimer( *(HANDLE*)timer, &due_time, 0, 0, 0, FALSE ); (void) b; WaitForSingleObject( *(HANDLE*)timer, INFINITE ); #elif defined( __linux__ ) || defined( __APPLE__ ) || defined( __ANDROID__ ) || defined( __EMSCRIPTEN__ ) //< @r-lyeh, ems struct timespec rem; struct timespec req; req.tv_sec = nanoseconds / 1000000000ULL; req.tv_nsec = nanoseconds - req.tv_sec * 1000000000ULL; while( nanosleep( &req, &rem ) ) req = rem; #else #error Unknown platform. #endif } thread_tls_t thread_tls_create( void ) { #if defined( _WIN32 ) DWORD tls = TlsAlloc(); if( tls == TLS_OUT_OF_INDEXES ) return NULL; else return (thread_tls_t) (uintptr_t) tls; #elif defined( __linux__ ) || defined( __APPLE__ ) || defined( __ANDROID__ ) || defined( __EMSCRIPTEN__ ) //< @r-lyeh, ems pthread_key_t tls; if( pthread_key_create( &tls, NULL ) == 0 ) return (thread_tls_t) (uintptr_t) tls; //< @r-lyeh: uintptr_t else return NULL; #else #error Unknown platform. #endif } void thread_tls_destroy( thread_tls_t tls ) { #if defined( _WIN32 ) TlsFree( (DWORD) (uintptr_t) tls ); #elif defined( __linux__ ) || defined( __APPLE__ ) || defined( __ANDROID__ ) || defined( __EMSCRIPTEN__ ) //< @r-lyeh, ems pthread_key_delete( (pthread_key_t) (uintptr_t) tls ); //< @r-lyeh: uintptr_t #else #error Unknown platform. #endif } void thread_tls_set( thread_tls_t tls, void* value ) { #if defined( _WIN32 ) TlsSetValue( (DWORD) (uintptr_t) tls, value ); #elif defined( __linux__ ) || defined( __APPLE__ ) || defined( __ANDROID__ ) || defined( __EMSCRIPTEN__ ) //< @r-lyeh, ems pthread_setspecific( (pthread_key_t) (uintptr_t) tls, value ); //< @r-lyeh: uintptr_t #else #error Unknown platform. #endif } void* thread_tls_get( thread_tls_t tls ) { #if defined( _WIN32 ) return TlsGetValue( (DWORD) (uintptr_t) tls ); #elif defined( __linux__ ) || defined( __APPLE__ ) || defined( __ANDROID__ ) || defined( __EMSCRIPTEN__ ) //< @r-lyeh, ems return pthread_getspecific( (pthread_key_t) (uintptr_t) tls ); //< @r-lyeh: uintptr_t #else #error Unknown platform. #endif } #if !THREAD_USE_MCMP void thread_queue_init( thread_queue_t* queue, int size, void** values, int count ) { queue->values = values; thread_signal_init( &queue->data_ready ); thread_signal_init( &queue->space_open ); thread_atomic_int_store( &queue->head, 0 ); thread_atomic_int_store( &queue->tail, count > size ? size : count ); thread_atomic_int_store( &queue->count, count > size ? size : count ); queue->size = size; #ifndef NDEBUG thread_atomic_int_store( &queue->id_produce_is_set, 0 ); thread_atomic_int_store( &queue->id_consume_is_set, 0 ); #endif } void thread_queue_term( thread_queue_t* queue ) { thread_signal_term( &queue->space_open ); thread_signal_term( &queue->data_ready ); } int thread_queue_produce( thread_queue_t* queue, void* value, int timeout_ms ) { #ifndef NDEBUG if( thread_atomic_int_compare_and_swap( &queue->id_produce_is_set, 0, 1 ) == 0 ) queue->id_produce = thread_current_thread_id(); assert( thread_current_thread_id() == queue->id_produce && "thread_queue_produce called from multiple threads" ); #endif while( thread_atomic_int_load( &queue->count ) == queue->size ) // TODO: fix signal so that this can be an "if" instead of "while" { if( timeout_ms == 0 ) return 0; if( thread_signal_wait( &queue->space_open, timeout_ms == THREAD_QUEUE_WAIT_INFINITE ? THREAD_SIGNAL_WAIT_INFINITE : timeout_ms ) == 0 ) return 0; } int tail = thread_atomic_int_inc( &queue->tail ); queue->values[ tail % queue->size ] = value; if( thread_atomic_int_inc( &queue->count ) == 0 ) thread_signal_raise( &queue->data_ready ); return 1; } void* thread_queue_consume( thread_queue_t* queue, int timeout_ms ) { #ifndef NDEBUG if( thread_atomic_int_compare_and_swap( &queue->id_consume_is_set, 0, 1 ) == 0 ) queue->id_consume = thread_current_thread_id(); assert( thread_current_thread_id() == queue->id_consume && "thread_queue_consume called from multiple threads" ); #endif while( thread_atomic_int_load( &queue->count ) == 0 ) // TODO: fix signal so that this can be an "if" instead of "while" { if( timeout_ms == 0 ) return NULL; if( thread_signal_wait( &queue->data_ready, timeout_ms == THREAD_QUEUE_WAIT_INFINITE ? THREAD_SIGNAL_WAIT_INFINITE : timeout_ms ) == 0 ) return NULL; } int head = thread_atomic_int_inc( &queue->head ); void* retval = queue->values[ head % queue->size ]; if( thread_atomic_int_dec( &queue->count ) == queue->size ) thread_signal_raise( &queue->space_open ); return retval; } int thread_queue_count( thread_queue_t* queue ) { return thread_atomic_int_load( &queue->count ); } #else // THREAD_USE_MCMP // # lockfree queues (multiple consumer-multiple producer) ##################### // License: WTFPL. https://github.com/darkautism/lfqueue // Use -O0 flag to compile (needed?). struct mcmp; int mcmp_new(struct mcmp *ctx); int mcmp_del(struct mcmp *ctx); int mcmp_add(struct mcmp *ctx, void *data); void *mcmp_pop(struct mcmp *ctx ); #ifdef _WIN32 # include # define __sync_add_and_fetch(p,x) (_InterlockedExchangeAdd64((__int64 volatile *)(p), (x)) + (x)) # define __sync_bool_compare_and_swap(p, c, s) (_InterlockedCompareExchange64((__int64 volatile *)(p), (__int64)(s), (__int64)(c)) == (__int64)(c)) # define __sync_lock_test_and_set(p,v) (_InterlockedExchange64( (__int64 volatile *)(p), (__int64)(v) )) #endif struct mcmp_node { void * data; struct mcmp_node *next; }; struct mcmp { struct mcmp_node *head; struct mcmp_node *tail; size_t count; // int }; int mcmp_new(struct mcmp *ctx) { struct mcmp_node * tmpnode = memset( (char*)REALLOC(0,sizeof(struct mcmp_node)), 0, sizeof(struct mcmp_node)); if (!tmpnode) return -errno; memset(ctx,0,sizeof(struct mcmp)); ctx->head=ctx->tail=tmpnode; return 1; } int mcmp_del(struct mcmp *ctx){ if ( ctx->tail && ctx->head ) { // if have data in queue struct mcmp_node * walker = ctx->head, *tmp; while ( walker != ctx->tail ) { // while still have node tmp = walker->next; REALLOC(walker, 0); walker=tmp; } REALLOC(ctx->head, 0); // free the empty node memset(ctx,0,sizeof(struct mcmp)); } return 1; } int mcmp_add(struct mcmp *ctx, void * data) { struct mcmp_node * p; struct mcmp_node * tmpnode = memset( (char*)REALLOC(0,sizeof(struct mcmp_node)), 0, sizeof(struct mcmp_node)); tmpnode->data=data; do { p = ctx->tail; if ( __sync_bool_compare_and_swap(&ctx->tail,p,tmpnode)) { p->next=tmpnode; break; } } while(1); __sync_add_and_fetch( &ctx->count, 1); return 1; } void * mcmp_pop(struct mcmp *ctx ) { void * ret=0; struct mcmp_node * p; do { p = ctx->head; } while(p==0 || !__sync_bool_compare_and_swap(&ctx->head,p,0)); if( p->next==0) { ctx->head=p; return 0; } ret=p->next->data; ctx->head=p->next; __sync_add_and_fetch( &ctx->count, -1); REALLOC(p, 0); return ret; } #endif // mcmp.h #endif /* THREAD_IMPLEMENTATION */ /* revision history: 0.31 add THREAD_HAS_ATOMIC (@r-lyeh) add THREAD_USE_MCMP tcc atomics tcc fixes emscripten fix 0.3 set_high_priority API change. Fixed spurious wakeup bug in signal. Added timeout param to queue produce/consume. Various cleanup and trivial fixes. 0.2 first publicly released version */ /* ------------------------------------------------------------------------------ This software is available under 2 licenses - you may choose the one you like. ------------------------------------------------------------------------------ ALTERNATIVE A - MIT License Copyright (c) 2015 Mattias Gustavsson 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. ------------------------------------------------------------------------------ ALTERNATIVE B - Public Domain (www.unlicense.org) This is free and unencumbered software released into the public domain. Anyone is free to copy, modify, publish, use, compile, sell, or distribute this software, either in source code form or as a compiled binary, for any purpose, commercial or non-commercial, and by any means. In jurisdictions that recognize copyright laws, the author or authors of this software dedicate any and all copyright interest in the software to the public domain. We make this dedication for the benefit of the public at large and to the detriment of our heirs and successors. We intend this dedication to be an overt act of relinquishment in perpetuity of all present and future rights to this software under copyright law. 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 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. ------------------------------------------------------------------------------ */ #line 0 #line 1 "3rd_plmpeg.h" /* PL_MPEG - MPEG1 Video decoder, MP2 Audio decoder, MPEG-PS demuxer Dominic Szablewski - https://phoboslab.org -- LICENSE: The MIT License(MIT) Copyright(c) 2019 Dominic Szablewski 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. -- Synopsis // Define `PL_MPEG_IMPLEMENTATION` in *one* C/C++ file before including this // library to create the implementation. #define PL_MPEG_IMPLEMENTATION #include "plmpeg.h" // This function gets called for each decoded video frame void my_video_callback(plm_t *plm, plm_frame_t *frame, void *user) { // Do something with frame->y.data, frame->cr.data, frame->cb.data } // This function gets called for each decoded audio frame void my_audio_callback(plm_t *plm, plm_samples_t *frame, void *user) { // Do something with samples->interleaved } // Load a .mpg (MPEG Program Stream) file plm_t *plm = plm_create_with_filename("some-file.mpg"); // Install the video & audio decode callbacks plm_set_video_decode_callback(plm, my_video_callback, my_data); plm_set_audio_decode_callback(plm, my_audio_callback, my_data); // Decode do { plm_decode(plm, time_since_last_call); } while (!plm_has_ended(plm)); // All done plm_destroy(plm); -- Documentation This library provides several interfaces to load, demux and decode MPEG video and audio data. A high-level API combines the demuxer, video & audio decoders in an easy to use wrapper. Lower-level APIs for accessing the demuxer, video decoder and audio decoder, as well as providing different data sources are also available. Interfaces are written in an object orientet style, meaning you create object instances via various different constructor functions (plm_*create()), do some work on them and later dispose them via plm_*destroy(). plm_* ......... the high-level interface, combining demuxer and decoders plm_buffer_* .. the data source used by all interfaces plm_demux_* ... the MPEG-PS demuxer plm_video_* ... the MPEG1 Video ("mpeg1") decoder plm_audio_* ... the MPEG1 Audio Layer II ("mp2") decoder With the high-level interface you have two options to decode video & audio: 1. Use plm_decode() and just hand over the delta time since the last call. It will decode everything needed and call your callbacks (specified through plm_set_{video|audio}_decode_callback()) any number of times. 2. Use plm_decode_video() and plm_decode_audio() to decode exactly one frame of video or audio data at a time. How you handle the synchronization of both streams is up to you. If you only want to decode video *or* audio through these functions, you should disable the other stream (plm_set_{video|audio}_enabled(FALSE)) Video data is decoded into a struct with all 3 planes (Y, Cr, Cb) stored in separate buffers. You can either convert this to RGB on the CPU (slow) via the plm_frame_to_rgb() function or do it on the GPU with the following matrix: mat4 bt601 = mat4( 1.16438, 0.00000, 1.59603, -0.87079, 1.16438, -0.39176, -0.81297, 0.52959, 1.16438, 2.01723, 0.00000, -1.08139, 0, 0, 0, 1 ); gl_FragColor = vec4(y, cb, cr, 1.0) * bt601; Audio data is decoded into a struct with either one single float array with the samples for the left and right channel interleaved, or if the PLM_AUDIO_SEPARATE_CHANNELS is defined *before* including this library, into two separate float arrays - one for each channel. Data can be supplied to the high level interface, the demuxer and the decoders in three different ways: 1. Using plm_create_from_filename() or with a file handle with plm_create_from_file(). 2. Using plm_create_with_memory() and supplying a pointer to memory that contains the whole file. 3. Using plm_create_with_buffer(), supplying your own plm_buffer_t instance and periodically writing to this buffer. When using your own plm_buffer_t instance, you can fill this buffer using plm_buffer_write(). You can either monitor plm_buffer_get_remaining() and push data when appropriate, or install a callback on the buffer with plm_buffer_set_load_callback() that gets called whenever the buffer needs more data. A buffer created with plm_buffer_create_with_capacity() is treated as a ring buffer, meaning that data that has already been read, will be discarded. In contrast, a buffer created with plm_buffer_create_for_appending() will keep all data written to it in memory. This enables seeking in the already loaded data. There should be no need to use the lower level plm_demux_*, plm_video_* and plm_audio_* functions, if all you want to do is read/decode an MPEG-PS file. However, if you get raw mpeg1video data or raw mp2 audio data from a different source, these functions can be used to decode the raw data directly. Similarly, if you only want to analyze an MPEG-PS file or extract raw video or audio packets from it, you can use the plm_demux_* functions. This library uses malloc(), realloc() and free() to manage memory. Typically all allocation happens up-front when creating the interface. However, the default buffer size may be too small for certain inputs. In these cases plmpeg will realloc() the buffer with a larger size whenever needed. You can configure the default buffer size by defining PLM_BUFFER_DEFAULT_SIZE *before* including this library. See below for detailed the API documentation. */ #ifndef PL_MPEG_H #define PL_MPEG_H #include #include #ifdef __cplusplus extern "C" { #endif // ----------------------------------------------------------------------------- // Public Data Types // Object types for the various interfaces typedef struct plm_t plm_t; typedef struct plm_buffer_t plm_buffer_t; typedef struct plm_demux_t plm_demux_t; typedef struct plm_video_t plm_video_t; typedef struct plm_audio_t plm_audio_t; // Demuxed MPEG PS packet // The type maps directly to the various MPEG-PES start codes. PTS is the // presentation time stamp of the packet in seconds. Note that not all packets // have a PTS value, indicated by PLM_PACKET_INVALID_TS. #define PLM_PACKET_INVALID_TS -1 typedef struct { int type; double pts; size_t length; uint8_t *data; } plm_packet_t; // Decoded Video Plane // The byte length of the data is width * height. Note that different planes // have different sizes: the Luma plane (Y) is double the size of each of // the two Chroma planes (Cr, Cb) - i.e. 4 times the byte length. // Also note that the size of the plane does *not* denote the size of the // displayed frame. The sizes of planes are always rounded up to the nearest // macroblock (16px). typedef struct { unsigned int width; unsigned int height; uint8_t *data; } plm_plane_t; // Decoded Video Frame // width and height denote the desired display size of the frame. This may be // different from the internal size of the 3 planes. typedef struct { double time; unsigned int width; unsigned int height; plm_plane_t y; plm_plane_t cr; plm_plane_t cb; } plm_frame_t; // Callback function type for decoded video frames used by the high-level // plm_* interface typedef void(*plm_video_decode_callback) (plm_t *self, plm_frame_t *frame, void *user); // Decoded Audio Samples // Samples are stored as normalized (-1, 1) float either interleaved, or if // PLM_AUDIO_SEPARATE_CHANNELS is defined, in two separate arrays. // The `count` is always PLM_AUDIO_SAMPLES_PER_FRAME and just there for // convenience. #define PLM_AUDIO_SAMPLES_PER_FRAME 1152 typedef struct { double time; unsigned int count; #ifdef PLM_AUDIO_SEPARATE_CHANNELS float left[PLM_AUDIO_SAMPLES_PER_FRAME]; float right[PLM_AUDIO_SAMPLES_PER_FRAME]; #else float interleaved[PLM_AUDIO_SAMPLES_PER_FRAME * 2]; #endif } plm_samples_t; // Callback function type for decoded audio samples used by the high-level // plm_* interface typedef void(*plm_audio_decode_callback) (plm_t *self, plm_samples_t *samples, void *user); // Callback function for plm_buffer when it needs more data typedef void(*plm_buffer_load_callback)(plm_buffer_t *self, void *user); // ----------------------------------------------------------------------------- // plm_* public API // High-Level API for loading/demuxing/decoding MPEG-PS data // Create a plmpeg instance with a filename. Returns NULL if the file could not // be opened. plm_t *plm_create_with_filename(const char *filename); // Create a plmpeg instance with a file handle. Pass TRUE to close_when_done to // let plmpeg call fclose() on the handle when plm_destroy() is called. plm_t *plm_create_with_file(FILE *fh, int close_when_done); // Create a plmpeg instance with a pointer to memory as source. This assumes the // whole file is in memory. The memory is not copied. Pass TRUE to // free_when_done to let plmpeg call free() on the pointer when plm_destroy() // is called. plm_t *plm_create_with_memory(uint8_t *bytes, size_t length, int free_when_done); // Create a plmpeg instance with a plm_buffer as source. Pass TRUE to // destroy_when_done to let plmpeg call plm_buffer_destroy() on the buffer when // plm_destroy() is called. plm_t *plm_create_with_buffer(plm_buffer_t *buffer, int destroy_when_done); // Destroy a plmpeg instance and free all data. void plm_destroy(plm_t *self); // Get whether we have headers on all available streams and we can accurately // report the number of video/audio streams, video dimensions, framerate and // audio samplerate. // This returns FALSE if the file is not an MPEG-PS file or - when not using a // file as source - when not enough data is available yet. int plm_has_headers(plm_t *self); // Get or set whether video decoding is enabled. Default TRUE. int plm_get_video_enabled(plm_t *self); void plm_set_video_enabled(plm_t *self, int enabled); // Get the number of video streams (0--1) reported in the system header. int plm_get_num_video_streams(plm_t *self); // Get the display width/height of the video stream. int plm_get_width(plm_t *self); int plm_get_height(plm_t *self); // Get the framerate of the video stream in frames per second. double plm_get_framerate(plm_t *self); // Get or set whether audio decoding is enabled. Default TRUE. int plm_get_audio_enabled(plm_t *self); void plm_set_audio_enabled(plm_t *self, int enabled); // Get the number of audio streams (0--4) reported in the system header. int plm_get_num_audio_streams(plm_t *self); // Set the desired audio stream (0--3). Default 0. void plm_set_audio_stream(plm_t *self, int stream_index); // Get the samplerate of the audio stream in samples per second. int plm_get_samplerate(plm_t *self); // Get or set the audio lead time in seconds - the time in which audio samples // are decoded in advance (or behind) the video decode time. Typically this // should be set to the duration of the buffer of the audio API that you use // for output. E.g. for SDL2: (SDL_AudioSpec.samples / samplerate) double plm_get_audio_lead_time(plm_t *self); void plm_set_audio_lead_time(plm_t *self, double lead_time); // Get the current internal time in seconds. double plm_get_time(plm_t *self); // Get the video duration of the underlying source in seconds. double plm_get_duration(plm_t *self); // Rewind all buffers back to the beginning. void plm_rewind(plm_t *self); // Get or set looping. Default FALSE. int plm_get_loop(plm_t *self); void plm_set_loop(plm_t *self, int loop); // Get whether the file has ended. If looping is enabled, this will always // return FALSE. int plm_has_ended(plm_t *self); // Set the callback for decoded video frames used with plm_decode(). If no // callback is set, video data will be ignored and not be decoded. The *user // Parameter will be passed to your callback. void plm_set_video_decode_callback(plm_t *self, plm_video_decode_callback fp, void *user); // Set the callback for decoded audio samples used with plm_decode(). If no // callback is set, audio data will be ignored and not be decoded. The *user // Parameter will be passed to your callback. void plm_set_audio_decode_callback(plm_t *self, plm_audio_decode_callback fp, void *user); // Advance the internal timer by seconds and decode video/audio up to this time. // This will call the video_decode_callback and audio_decode_callback any number // of times. A frame-skip is not implemented, i.e. everything up to current time // will be decoded. void plm_decode(plm_t *self, double seconds); // Decode and return one video frame. Returns NULL if no frame could be decoded // (either because the source ended or data is corrupt). If you only want to // decode video, you should disable audio via plm_set_audio_enabled(). // The returned plm_frame_t is valid until the next call to plm_decode_video() // or until plm_destroy() is called. plm_frame_t *plm_decode_video(plm_t *self); // Decode and return one audio frame. Returns NULL if no frame could be decoded // (either because the source ended or data is corrupt). If you only want to // decode audio, you should disable video via plm_set_video_enabled(). // The returned plm_samples_t is valid until the next call to plm_decode_audio() // or until plm_destroy() is called. plm_samples_t *plm_decode_audio(plm_t *self); // Seek to the specified time, clamped between 0 -- duration. This can only be // used when the underlying plm_buffer is seekable, i.e. for files, fixed // memory buffers or _for_appending buffers. // If seek_exact is TRUE this will seek to the exact time, otherwise it will // seek to the last intra frame just before the desired time. Exact seeking can // be slow, because all frames up to the seeked one have to be decoded on top of // the previous intra frame. // If seeking succeeds, this function will call the video_decode_callback // exactly once with the target frame. If audio is enabled, it will also call // the audio_decode_callback any number of times, until the audio_lead_time is // satisfied. // Returns TRUE if seeking succeeded or FALSE if no frame could be found. int plm_seek(plm_t *self, double time, int seek_exact); // Similar to plm_seek(), but will not call the video_decode_callback, // audio_decode_callback or make any attempts to sync audio. // Returns the found frame or NULL if no frame could be found. plm_frame_t *plm_seek_frame(plm_t *self, double time, int seek_exact); // ----------------------------------------------------------------------------- // plm_buffer public API // Provides the data source for all other plm_* interfaces // The default size for buffers created from files or by the high-level API #ifndef PLM_BUFFER_DEFAULT_SIZE #define PLM_BUFFER_DEFAULT_SIZE (128 * 1024) #endif // Create a buffer instance with a filename. Returns NULL if the file could not // be opened. plm_buffer_t *plm_buffer_create_with_filename(const char *filename); // Create a buffer instance with a file handle. Pass TRUE to close_when_done // to let plmpeg call fclose() on the handle when plm_destroy() is called. plm_buffer_t *plm_buffer_create_with_file(FILE *fh, int close_when_done); // Create a buffer instance with a pointer to memory as source. This assumes // the whole file is in memory. The bytes are not copied. Pass 1 to // free_when_done to let plmpeg call free() on the pointer when plm_destroy() // is called. plm_buffer_t *plm_buffer_create_with_memory(uint8_t *bytes, size_t length, int free_when_done); // Create an empty buffer with an initial capacity. The buffer will grow // as needed. Data that has already been read, will be discarded. plm_buffer_t *plm_buffer_create_with_capacity(size_t capacity); // Create an empty buffer with an initial capacity. The buffer will grow // as needed. Decoded data will *not* be discarded. This can be used when // loading a file over the network, without needing to throttle the download. // It also allows for seeking in the already loaded data. plm_buffer_t *plm_buffer_create_for_appending(size_t initial_capacity); // Destroy a buffer instance and free all data void plm_buffer_destroy(plm_buffer_t *self); // Copy data into the buffer. If the data to be written is larger than the // available space, the buffer will realloc() with a larger capacity. // Returns the number of bytes written. This will always be the same as the // passed in length, except when the buffer was created _with_memory() for // which _write() is forbidden. size_t plm_buffer_write(plm_buffer_t *self, uint8_t *bytes, size_t length); // Mark the current byte length as the end of this buffer and signal that no // more data is expected to be written to it. This function should be called // just after the last plm_buffer_write(). // For _with_capacity buffers, this is cleared on a plm_buffer_rewind(). void plm_buffer_signal_end(plm_buffer_t *self); // Set a callback that is called whenever the buffer needs more data void plm_buffer_set_load_callback(plm_buffer_t *self, plm_buffer_load_callback fp, void *user); // Rewind the buffer back to the beginning. When loading from a file handle, // this also seeks to the beginning of the file. void plm_buffer_rewind(plm_buffer_t *self); // Get the total size. For files, this returns the file size. For all other // types it returns the number of bytes currently in the buffer. size_t plm_buffer_get_size(plm_buffer_t *self); // Get the number of remaining (yet unread) bytes in the buffer. This can be // useful to throttle writing. size_t plm_buffer_get_remaining(plm_buffer_t *self); // Get whether the read position of the buffer is at the end and no more data // is expected. int plm_buffer_has_ended(plm_buffer_t *self); // ----------------------------------------------------------------------------- // plm_demux public API // Demux an MPEG Program Stream (PS) data into separate packages // Various Packet Types static const int PLM_DEMUX_PACKET_PRIVATE = 0xBD; static const int PLM_DEMUX_PACKET_AUDIO_1 = 0xC0; static const int PLM_DEMUX_PACKET_AUDIO_2 = 0xC1; static const int PLM_DEMUX_PACKET_AUDIO_3 = 0xC2; static const int PLM_DEMUX_PACKET_AUDIO_4 = 0xC2; static const int PLM_DEMUX_PACKET_VIDEO_1 = 0xE0; // Create a demuxer with a plm_buffer as source. This will also attempt to read // the pack and system headers from the buffer. plm_demux_t *plm_demux_create(plm_buffer_t *buffer, int destroy_when_done); // Destroy a demuxer and free all data. void plm_demux_destroy(plm_demux_t *self); // Returns TRUE/FALSE whether pack and system headers have been found. This will // attempt to read the headers if non are present yet. int plm_demux_has_headers(plm_demux_t *self); // Returns the number of video streams found in the system header. This will // attempt to read the system header if non is present yet. int plm_demux_get_num_video_streams(plm_demux_t *self); // Returns the number of audio streams found in the system header. This will // attempt to read the system header if non is present yet. int plm_demux_get_num_audio_streams(plm_demux_t *self); // Rewind the internal buffer. See plm_buffer_rewind(). void plm_demux_rewind(plm_demux_t *self); // Get whether the file has ended. This will be cleared on seeking or rewind. int plm_demux_has_ended(plm_demux_t *self); // Seek to a packet of the specified type with a PTS just before specified time. // If force_intra is TRUE, only packets containing an intra frame will be // considered - this only makes sense when the type is PLM_DEMUX_PACKET_VIDEO_1. // Note that the specified time is considered 0-based, regardless of the first // PTS in the data source. plm_packet_t *plm_demux_seek(plm_demux_t *self, double time, int type, int force_intra); // Get the PTS of the first packet of this type. Returns PLM_PACKET_INVALID_TS // if not packet of this packet type can be found. double plm_demux_get_start_time(plm_demux_t *self, int type); // Get the duration for the specified packet type - i.e. the span between the // the first PTS and the last PTS in the data source. This only makes sense when // the underlying data source is a file or fixed memory. double plm_demux_get_duration(plm_demux_t *self, int type); // Decode and return the next packet. The returned packet_t is valid until // the next call to plm_demux_decode() or until the demuxer is destroyed. plm_packet_t *plm_demux_decode(plm_demux_t *self); // ----------------------------------------------------------------------------- // plm_video public API // Decode MPEG1 Video ("mpeg1") data into raw YCrCb frames // Create a video decoder with a plm_buffer as source. plm_video_t *plm_video_create_with_buffer(plm_buffer_t *buffer, int destroy_when_done); // Destroy a video decoder and free all data. void plm_video_destroy(plm_video_t *self); // Get whether a sequence header was found and we can accurately report on // dimensions and framerate. int plm_video_has_header(plm_video_t *self); // Get the framerate in frames per second. double plm_video_get_framerate(plm_video_t *self); // Get the display width/height. int plm_video_get_width(plm_video_t *self); int plm_video_get_height(plm_video_t *self); // Set "no delay" mode. When enabled, the decoder assumes that the video does // *not* contain any B-Frames. This is useful for reducing lag when streaming. // The default is FALSE. void plm_video_set_no_delay(plm_video_t *self, int no_delay); // Get the current internal time in seconds. double plm_video_get_time(plm_video_t *self); // Set the current internal time in seconds. This is only useful when you // manipulate the underlying video buffer and want to enforce a correct // timestamps. void plm_video_set_time(plm_video_t *self, double time); // Rewind the internal buffer. See plm_buffer_rewind(). void plm_video_rewind(plm_video_t *self); // Get whether the file has ended. This will be cleared on rewind. int plm_video_has_ended(plm_video_t *self); // Decode and return one frame of video and advance the internal time by // 1/framerate seconds. The returned frame_t is valid until the next call of // plm_video_decode() or until the video decoder is destroyed. plm_frame_t *plm_video_decode(plm_video_t *self); // Convert the YCrCb data of a frame into interleaved R G B data. The stride // specifies the width in bytes of the destination buffer. I.e. the number of // bytes from one line to the next. The stride must be at least // (frame->width * bytes_per_pixel). The buffer pointed to by *dest must have a // size of at least (stride * frame->height). // Note that the alpha component of the dest buffer is always left untouched. void plm_frame_to_rgb(plm_frame_t *frame, uint8_t *dest, int stride); void plm_frame_to_bgr(plm_frame_t *frame, uint8_t *dest, int stride); void plm_frame_to_rgba(plm_frame_t *frame, uint8_t *dest, int stride); void plm_frame_to_bgra(plm_frame_t *frame, uint8_t *dest, int stride); void plm_frame_to_argb(plm_frame_t *frame, uint8_t *dest, int stride); void plm_frame_to_abgr(plm_frame_t *frame, uint8_t *dest, int stride); // ----------------------------------------------------------------------------- // plm_audio public API // Decode MPEG-1 Audio Layer II ("mp2") data into raw samples // Create an audio decoder with a plm_buffer as source. plm_audio_t *plm_audio_create_with_buffer(plm_buffer_t *buffer, int destroy_when_done); // Destroy an audio decoder and free all data. void plm_audio_destroy(plm_audio_t *self); // Get whether a frame header was found and we can accurately report on // samplerate. int plm_audio_has_header(plm_audio_t *self); // Get the samplerate in samples per second. int plm_audio_get_samplerate(plm_audio_t *self); // Get the current internal time in seconds. double plm_audio_get_time(plm_audio_t *self); // Set the current internal time in seconds. This is only useful when you // manipulate the underlying video buffer and want to enforce a correct // timestamps. void plm_audio_set_time(plm_audio_t *self, double time); // Rewind the internal buffer. See plm_buffer_rewind(). void plm_audio_rewind(plm_audio_t *self); // Get whether the file has ended. This will be cleared on rewind. int plm_audio_has_ended(plm_audio_t *self); // Decode and return one "frame" of audio and advance the internal time by // (PLM_AUDIO_SAMPLES_PER_FRAME/samplerate) seconds. The returned samples_t // is valid until the next call of plm_audio_decode() or until the audio // decoder is destroyed. plm_samples_t *plm_audio_decode(plm_audio_t *self); #ifdef __cplusplus } #endif #endif // PL_MPEG_H // ----------------------------------------------------------------------------- // ----------------------------------------------------------------------------- // IMPLEMENTATION #ifdef PL_MPEG_IMPLEMENTATION #include #include #ifndef TRUE #define TRUE 1 #define FALSE 0 #endif #define PLM_UNUSED(expr) (void)(expr) // ----------------------------------------------------------------------------- // plm (high-level interface) implementation typedef struct plm_t { plm_demux_t *demux; double time; int has_ended; int loop; int has_decoders; int video_enabled; int video_packet_type; plm_buffer_t *video_buffer; plm_video_t *video_decoder; int audio_enabled; int audio_stream_index; int audio_packet_type; double audio_lead_time; plm_buffer_t *audio_buffer; plm_audio_t *audio_decoder; plm_video_decode_callback video_decode_callback; void *video_decode_callback_user_data; plm_audio_decode_callback audio_decode_callback; void *audio_decode_callback_user_data; } plm_t; int plm_init_decoders(plm_t *self); void plm_handle_end(plm_t *self); void plm_read_video_packet(plm_buffer_t *buffer, void *user); void plm_read_audio_packet(plm_buffer_t *buffer, void *user); void plm_read_packets(plm_t *self, int requested_type); plm_t *plm_create_with_filename(const char *filename) { plm_buffer_t *buffer = plm_buffer_create_with_filename(filename); if (!buffer) { return NULL; } return plm_create_with_buffer(buffer, TRUE); } plm_t *plm_create_with_file(FILE *fh, int close_when_done) { plm_buffer_t *buffer = plm_buffer_create_with_file(fh, close_when_done); return plm_create_with_buffer(buffer, TRUE); } plm_t *plm_create_with_memory(uint8_t *bytes, size_t length, int free_when_done) { plm_buffer_t *buffer = plm_buffer_create_with_memory(bytes, length, free_when_done); return plm_create_with_buffer(buffer, TRUE); } plm_t *plm_create_with_buffer(plm_buffer_t *buffer, int destroy_when_done) { plm_t *self = (plm_t *)malloc(sizeof(plm_t)); memset(self, 0, sizeof(plm_t)); self->demux = plm_demux_create(buffer, destroy_when_done); self->video_enabled = TRUE; self->audio_enabled = TRUE; plm_init_decoders(self); return self; } int plm_init_decoders(plm_t *self) { if (self->has_decoders) { return TRUE; } if (!plm_demux_has_headers(self->demux)) { return FALSE; } if (plm_demux_get_num_video_streams(self->demux) > 0) { if (self->video_enabled) { self->video_packet_type = PLM_DEMUX_PACKET_VIDEO_1; } self->video_buffer = plm_buffer_create_with_capacity(PLM_BUFFER_DEFAULT_SIZE); plm_buffer_set_load_callback(self->video_buffer, plm_read_video_packet, self); } if (plm_demux_get_num_audio_streams(self->demux) > 0) { if (self->audio_enabled) { self->audio_packet_type = PLM_DEMUX_PACKET_AUDIO_1 + self->audio_stream_index; } self->audio_buffer = plm_buffer_create_with_capacity(PLM_BUFFER_DEFAULT_SIZE); plm_buffer_set_load_callback(self->audio_buffer, plm_read_audio_packet, self); } if (self->video_buffer) { self->video_decoder = plm_video_create_with_buffer(self->video_buffer, TRUE); } if (self->audio_buffer) { self->audio_decoder = plm_audio_create_with_buffer(self->audio_buffer, TRUE); } self->has_decoders = TRUE; return TRUE; } void plm_destroy(plm_t *self) { if (self->video_decoder) { plm_video_destroy(self->video_decoder); } if (self->audio_decoder) { plm_audio_destroy(self->audio_decoder); } plm_demux_destroy(self->demux); free(self); } int plm_get_audio_enabled(plm_t *self) { return self->audio_enabled; } int plm_has_headers(plm_t *self) { if (!plm_demux_has_headers(self->demux)) { return FALSE; } if (!plm_init_decoders(self)) { return FALSE; } if ( (self->video_decoder && !plm_video_has_header(self->video_decoder)) || (self->audio_decoder && !plm_audio_has_header(self->audio_decoder)) ) { return FALSE; } return TRUE; } void plm_set_audio_enabled(plm_t *self, int enabled) { self->audio_enabled = enabled; if (!enabled) { self->audio_packet_type = 0; return; } self->audio_packet_type = (plm_init_decoders(self) && self->audio_decoder) ? PLM_DEMUX_PACKET_AUDIO_1 + self->audio_stream_index : 0; } void plm_set_audio_stream(plm_t *self, int stream_index) { if (stream_index < 0 || stream_index > 3) { return; } self->audio_stream_index = stream_index; // Set the correct audio_packet_type plm_set_audio_enabled(self, self->audio_enabled); } int plm_get_video_enabled(plm_t *self) { return self->video_enabled; } void plm_set_video_enabled(plm_t *self, int enabled) { self->video_enabled = enabled; if (!enabled) { self->video_packet_type = 0; return; } self->video_packet_type = (plm_init_decoders(self) && self->video_decoder) ? PLM_DEMUX_PACKET_VIDEO_1 : 0; } int plm_get_num_video_streams(plm_t *self) { return plm_demux_get_num_video_streams(self->demux); } int plm_get_width(plm_t *self) { return (plm_init_decoders(self) && self->video_decoder) ? plm_video_get_width(self->video_decoder) : 0; } int plm_get_height(plm_t *self) { return (plm_init_decoders(self) && self->video_decoder) ? plm_video_get_height(self->video_decoder) : 0; } double plm_get_framerate(plm_t *self) { return (plm_init_decoders(self) && self->video_decoder) ? plm_video_get_framerate(self->video_decoder) : 0; } int plm_get_num_audio_streams(plm_t *self) { return plm_demux_get_num_audio_streams(self->demux); } int plm_get_samplerate(plm_t *self) { return (plm_init_decoders(self) && self->audio_decoder) ? plm_audio_get_samplerate(self->audio_decoder) : 0; } double plm_get_audio_lead_time(plm_t *self) { return self->audio_lead_time; } void plm_set_audio_lead_time(plm_t *self, double lead_time) { self->audio_lead_time = lead_time; } double plm_get_time(plm_t *self) { return self->time; } double plm_get_duration(plm_t *self) { return plm_demux_get_duration(self->demux, PLM_DEMUX_PACKET_VIDEO_1); } void plm_rewind(plm_t *self) { if (self->video_decoder) { plm_video_rewind(self->video_decoder); } if (self->audio_decoder) { plm_audio_rewind(self->audio_decoder); } plm_demux_rewind(self->demux); self->time = 0; } int plm_get_loop(plm_t *self) { return self->loop; } void plm_set_loop(plm_t *self, int loop) { self->loop = loop; } int plm_has_ended(plm_t *self) { return self->has_ended; } void plm_set_video_decode_callback(plm_t *self, plm_video_decode_callback fp, void *user) { self->video_decode_callback = fp; self->video_decode_callback_user_data = user; } void plm_set_audio_decode_callback(plm_t *self, plm_audio_decode_callback fp, void *user) { self->audio_decode_callback = fp; self->audio_decode_callback_user_data = user; } void plm_decode(plm_t *self, double tick) { if (!plm_init_decoders(self)) { return; } int decode_video = (self->video_decode_callback && self->video_packet_type); int decode_audio = (self->audio_decode_callback && self->audio_packet_type); if (!decode_video && !decode_audio) { // Nothing to do here return; } int did_decode = FALSE; int decode_video_failed = FALSE; int decode_audio_failed = FALSE; double video_target_time = self->time + tick; double audio_target_time = self->time + tick + self->audio_lead_time; do { did_decode = FALSE; if (decode_video && plm_video_get_time(self->video_decoder) < video_target_time) { plm_frame_t *frame = plm_video_decode(self->video_decoder); if (frame) { self->video_decode_callback(self, frame, self->video_decode_callback_user_data); did_decode = TRUE; } else { decode_video_failed = TRUE; } } if (decode_audio && plm_audio_get_time(self->audio_decoder) < audio_target_time) { plm_samples_t *samples = plm_audio_decode(self->audio_decoder); if (samples) { self->audio_decode_callback(self, samples, self->audio_decode_callback_user_data); did_decode = TRUE; } else { decode_audio_failed = TRUE; } } } while (did_decode); // Did all sources we wanted to decode fail and the demuxer is at the end? if ( (!decode_video || decode_video_failed) && (!decode_audio || decode_audio_failed) && plm_demux_has_ended(self->demux) ) { plm_handle_end(self); return; } self->time += tick; } plm_frame_t *plm_decode_video(plm_t *self) { if (!plm_init_decoders(self)) { return NULL; } if (!self->video_packet_type) { return NULL; } plm_frame_t *frame = plm_video_decode(self->video_decoder); if (frame) { self->time = frame->time; } else if (plm_demux_has_ended(self->demux)) { plm_handle_end(self); } return frame; } plm_samples_t *plm_decode_audio(plm_t *self) { if (!plm_init_decoders(self)) { return NULL; } if (!self->audio_packet_type) { return NULL; } plm_samples_t *samples = plm_audio_decode(self->audio_decoder); if (samples) { self->time = samples->time; } else if (plm_demux_has_ended(self->demux)) { plm_handle_end(self); } return samples; } void plm_handle_end(plm_t *self) { if (self->loop) { plm_rewind(self); } else { self->has_ended = TRUE; } } void plm_read_video_packet(plm_buffer_t *buffer, void *user) { PLM_UNUSED(buffer); plm_t *self = (plm_t *)user; plm_read_packets(self, self->video_packet_type); } void plm_read_audio_packet(plm_buffer_t *buffer, void *user) { PLM_UNUSED(buffer); plm_t *self = (plm_t *)user; plm_read_packets(self, self->audio_packet_type); } void plm_read_packets(plm_t *self, int requested_type) { plm_packet_t *packet; while ((packet = plm_demux_decode(self->demux))) { if (packet->type == self->video_packet_type) { plm_buffer_write(self->video_buffer, packet->data, packet->length); } else if (packet->type == self->audio_packet_type) { plm_buffer_write(self->audio_buffer, packet->data, packet->length); } if (packet->type == requested_type) { return; } } if (plm_demux_has_ended(self->demux)) { if (self->video_buffer) { plm_buffer_signal_end(self->video_buffer); } if (self->audio_buffer) { plm_buffer_signal_end(self->audio_buffer); } } } plm_frame_t *plm_seek_frame(plm_t *self, double time, int seek_exact) { if (!plm_init_decoders(self)) { return NULL; } if (!self->video_packet_type) { return NULL; } int type = self->video_packet_type; double start_time = plm_demux_get_start_time(self->demux, type); double duration = plm_demux_get_duration(self->demux, type); if (time < 0) { time = 0; } else if (time > duration) { time = duration; } plm_packet_t *packet = plm_demux_seek(self->demux, time, type, TRUE); if (!packet) { return NULL; } // Disable writing to the audio buffer while decoding video int previous_audio_packet_type = self->audio_packet_type; self->audio_packet_type = 0; // Clear video buffer and decode the found packet plm_video_rewind(self->video_decoder); plm_video_set_time(self->video_decoder, packet->pts - start_time); plm_buffer_write(self->video_buffer, packet->data, packet->length); plm_frame_t *frame = plm_video_decode(self->video_decoder); // If we want to seek to an exact frame, we have to decode all frames // on top of the intra frame we just jumped to. if (seek_exact) { while (frame && frame->time < time) { frame = plm_video_decode(self->video_decoder); } } // Enable writing to the audio buffer again? self->audio_packet_type = previous_audio_packet_type; if (frame) { self->time = frame->time; } self->has_ended = FALSE; return frame; } int plm_seek(plm_t *self, double time, int seek_exact) { plm_frame_t *frame = plm_seek_frame(self, time, seek_exact); if (!frame) { return FALSE; } if (self->video_decode_callback) { self->video_decode_callback(self, frame, self->video_decode_callback_user_data); } // If audio is not enabled we are done here. if (!self->audio_packet_type) { return TRUE; } // Sync up Audio. This demuxes more packets until the first audio packet // with a PTS greater than the current time is found. plm_decode() is then // called to decode enough audio data to satisfy the audio_lead_time. double start_time = plm_demux_get_start_time(self->demux, self->video_packet_type); plm_audio_rewind(self->audio_decoder); plm_packet_t *packet = NULL; while ((packet = plm_demux_decode(self->demux))) { if (packet->type == self->video_packet_type) { plm_buffer_write(self->video_buffer, packet->data, packet->length); } else if ( packet->type == self->audio_packet_type && packet->pts - start_time > self->time ) { plm_audio_set_time(self->audio_decoder, packet->pts - start_time); plm_buffer_write(self->audio_buffer, packet->data, packet->length); plm_decode(self, 0); break; } } return TRUE; } // ----------------------------------------------------------------------------- // plm_buffer implementation enum plm_buffer_mode { PLM_BUFFER_MODE_FILE, PLM_BUFFER_MODE_FIXED_MEM, PLM_BUFFER_MODE_RING, PLM_BUFFER_MODE_APPEND }; typedef struct plm_buffer_t { size_t bit_index; size_t capacity; size_t length; size_t total_size; int discard_read_bytes; int has_ended; int free_when_done; int close_when_done; FILE *fh; plm_buffer_load_callback load_callback; void *load_callback_user_data; uint8_t *bytes; enum plm_buffer_mode mode; } plm_buffer_t; typedef struct { int16_t index; int16_t value; } plm_vlc_t; typedef struct { int16_t index; uint16_t value; } plm_vlc_uint_t; void plm_buffer_seek(plm_buffer_t *self, size_t pos); size_t plm_buffer_tell(plm_buffer_t *self); void plm_buffer_discard_read_bytes(plm_buffer_t *self); void plm_buffer_load_file_callback(plm_buffer_t *self, void *user); int plm_buffer_has(plm_buffer_t *self, size_t count); int plm_buffer_read(plm_buffer_t *self, int count); void plm_buffer_align(plm_buffer_t *self); void plm_buffer_skip(plm_buffer_t *self, size_t count); int plm_buffer_skip_bytes(plm_buffer_t *self, uint8_t v); int plm_buffer_next_start_code(plm_buffer_t *self); int plm_buffer_find_start_code(plm_buffer_t *self, int code); int plm_buffer_no_start_code(plm_buffer_t *self); int16_t plm_buffer_read_vlc(plm_buffer_t *self, const plm_vlc_t *table); uint16_t plm_buffer_read_vlc_uint(plm_buffer_t *self, const plm_vlc_uint_t *table); plm_buffer_t *plm_buffer_create_with_filename(const char *filename) { FILE *fh = fopen(filename, "rb"); if (!fh) { return NULL; } return plm_buffer_create_with_file(fh, TRUE); } plm_buffer_t *plm_buffer_create_with_file(FILE *fh, int close_when_done) { plm_buffer_t *self = plm_buffer_create_with_capacity(PLM_BUFFER_DEFAULT_SIZE); self->fh = fh; self->close_when_done = close_when_done; self->mode = PLM_BUFFER_MODE_FILE; self->discard_read_bytes = TRUE; fseek(self->fh, 0, SEEK_END); self->total_size = ftell(self->fh); fseek(self->fh, 0, SEEK_SET); plm_buffer_set_load_callback(self, plm_buffer_load_file_callback, NULL); return self; } plm_buffer_t *plm_buffer_create_with_memory(uint8_t *bytes, size_t length, int free_when_done) { plm_buffer_t *self = (plm_buffer_t *)malloc(sizeof(plm_buffer_t)); memset(self, 0, sizeof(plm_buffer_t)); self->capacity = length; self->length = length; self->total_size = length; self->free_when_done = free_when_done; self->bytes = bytes; self->mode = PLM_BUFFER_MODE_FIXED_MEM; self->discard_read_bytes = FALSE; return self; } plm_buffer_t *plm_buffer_create_with_capacity(size_t capacity) { plm_buffer_t *self = (plm_buffer_t *)malloc(sizeof(plm_buffer_t)); memset(self, 0, sizeof(plm_buffer_t)); self->capacity = capacity; self->free_when_done = TRUE; self->bytes = (uint8_t *)malloc(capacity); self->mode = PLM_BUFFER_MODE_RING; self->discard_read_bytes = TRUE; return self; } plm_buffer_t *plm_buffer_create_for_appending(size_t initial_capacity) { plm_buffer_t *self = plm_buffer_create_with_capacity(initial_capacity); self->mode = PLM_BUFFER_MODE_APPEND; self->discard_read_bytes = FALSE; return self; } void plm_buffer_destroy(plm_buffer_t *self) { if (self->fh && self->close_when_done) { fclose(self->fh); } if (self->free_when_done) { free(self->bytes); } free(self); } size_t plm_buffer_get_size(plm_buffer_t *self) { return (self->mode == PLM_BUFFER_MODE_FILE) ? self->total_size : self->length; } size_t plm_buffer_get_remaining(plm_buffer_t *self) { return self->length - (self->bit_index >> 3); } size_t plm_buffer_write(plm_buffer_t *self, uint8_t *bytes, size_t length) { if (self->mode == PLM_BUFFER_MODE_FIXED_MEM) { return 0; } if (self->discard_read_bytes) { // This should be a ring buffer, but instead it just shifts all unread // data to the beginning of the buffer and appends new data at the end. // Seems to be good enough. plm_buffer_discard_read_bytes(self); if (self->mode == PLM_BUFFER_MODE_RING) { self->total_size = 0; } } // Do we have to resize to fit the new data? size_t bytes_available = self->capacity - self->length; if (bytes_available < length) { size_t new_size = self->capacity; do { new_size *= 2; } while (new_size - self->length < length); self->bytes = (uint8_t *)realloc(self->bytes, new_size); self->capacity = new_size; } memcpy(self->bytes + self->length, bytes, length); self->length += length; self->has_ended = FALSE; return length; } void plm_buffer_signal_end(plm_buffer_t *self) { self->total_size = self->length; } void plm_buffer_set_load_callback(plm_buffer_t *self, plm_buffer_load_callback fp, void *user) { self->load_callback = fp; self->load_callback_user_data = user; } void plm_buffer_rewind(plm_buffer_t *self) { plm_buffer_seek(self, 0); } void plm_buffer_seek(plm_buffer_t *self, size_t pos) { self->has_ended = FALSE; if (self->mode == PLM_BUFFER_MODE_FILE) { fseek(self->fh, pos, SEEK_SET); self->bit_index = 0; self->length = 0; } else if (self->mode == PLM_BUFFER_MODE_RING) { if (pos != 0) { // Seeking to non-0 is forbidden for dynamic-mem buffers return; } self->bit_index = 0; self->length = 0; self->total_size = 0; } else if (pos < self->length) { self->bit_index = pos << 3; } } size_t plm_buffer_tell(plm_buffer_t *self) { return self->mode == PLM_BUFFER_MODE_FILE ? ftell(self->fh) + (self->bit_index >> 3) - self->length : self->bit_index >> 3; } void plm_buffer_discard_read_bytes(plm_buffer_t *self) { size_t byte_pos = self->bit_index >> 3; if (byte_pos == self->length) { self->bit_index = 0; self->length = 0; } else if (byte_pos > 0) { memmove(self->bytes, self->bytes + byte_pos, self->length - byte_pos); self->bit_index -= byte_pos << 3; self->length -= byte_pos; } } void plm_buffer_load_file_callback(plm_buffer_t *self, void *user) { PLM_UNUSED(user); if (self->discard_read_bytes) { plm_buffer_discard_read_bytes(self); } size_t bytes_available = self->capacity - self->length; size_t bytes_read = fread(self->bytes + self->length, 1, bytes_available, self->fh); self->length += bytes_read; if (bytes_read == 0) { self->has_ended = TRUE; } } int plm_buffer_has_ended(plm_buffer_t *self) { return self->has_ended; } int plm_buffer_has(plm_buffer_t *self, size_t count) { if (((self->length << 3) - self->bit_index) >= count) { return TRUE; } if (self->load_callback) { self->load_callback(self, self->load_callback_user_data); } if (((self->length << 3) - self->bit_index) >= count) { return TRUE; } if (self->total_size != 0 && self->length == self->total_size) { self->has_ended = TRUE; } return FALSE; } int plm_buffer_read(plm_buffer_t *self, int count) { if (!plm_buffer_has(self, count)) { return 0; } int value = 0; while (count) { int current_byte = self->bytes[self->bit_index >> 3]; int remaining = 8 - (self->bit_index & 7); // Remaining bits in byte int read = remaining < count ? remaining : count; // Bits in self run int shift = remaining - read; int mask = (0xff >> (8 - read)); value = (value << read) | ((current_byte & (mask << shift)) >> shift); self->bit_index += read; count -= read; } return value; } void plm_buffer_align(plm_buffer_t *self) { self->bit_index = ((self->bit_index + 7) >> 3) << 3; // Align to next byte } void plm_buffer_skip(plm_buffer_t *self, size_t count) { if (plm_buffer_has(self, count)) { self->bit_index += count; } } int plm_buffer_skip_bytes(plm_buffer_t *self, uint8_t v) { plm_buffer_align(self); int skipped = 0; while (plm_buffer_has(self, 8) && self->bytes[self->bit_index >> 3] == v) { self->bit_index += 8; skipped++; } return skipped; } int plm_buffer_next_start_code(plm_buffer_t *self) { plm_buffer_align(self); while (plm_buffer_has(self, (5 << 3))) { size_t byte_index = (self->bit_index) >> 3; if ( self->bytes[byte_index] == 0x00 && self->bytes[byte_index + 1] == 0x00 && self->bytes[byte_index + 2] == 0x01 ) { self->bit_index = (byte_index + 4) << 3; return self->bytes[byte_index + 3]; } self->bit_index += 8; } return -1; } int plm_buffer_find_start_code(plm_buffer_t *self, int code) { int current = 0; while (TRUE) { current = plm_buffer_next_start_code(self); if (current == code || current == -1) { return current; } } return -1; } int plm_buffer_has_start_code(plm_buffer_t *self, int code) { size_t previous_bit_index = self->bit_index; int previous_discard_read_bytes = self->discard_read_bytes; self->discard_read_bytes = FALSE; int current = plm_buffer_find_start_code(self, code); self->bit_index = previous_bit_index; self->discard_read_bytes = previous_discard_read_bytes; return current; } int plm_buffer_no_start_code(plm_buffer_t *self) { if (!plm_buffer_has(self, (5 << 3))) { return FALSE; } size_t byte_index = ((self->bit_index + 7) >> 3); return !( self->bytes[byte_index] == 0x00 && self->bytes[byte_index + 1] == 0x00 && self->bytes[byte_index + 2] == 0x01 ); } int16_t plm_buffer_read_vlc(plm_buffer_t *self, const plm_vlc_t *table) { plm_vlc_t state = {0, 0}; do { state = table[state.index + plm_buffer_read(self, 1)]; } while (state.index > 0); return state.value; } uint16_t plm_buffer_read_vlc_uint(plm_buffer_t *self, const plm_vlc_uint_t *table) { return (uint16_t)plm_buffer_read_vlc(self, (const plm_vlc_t *)table); } // ---------------------------------------------------------------------------- // plm_demux implementation static const int PLM_START_PACK = 0xBA; static const int PLM_START_END = 0xB9; static const int PLM_START_SYSTEM = 0xBB; typedef struct plm_demux_t { plm_buffer_t *buffer; int destroy_buffer_when_done; double system_clock_ref; size_t last_file_size; double last_decoded_pts; double start_time; double duration; int start_code; int has_pack_header; int has_system_header; int has_headers; int num_audio_streams; int num_video_streams; plm_packet_t current_packet; plm_packet_t next_packet; } plm_demux_t; void plm_demux_buffer_seek(plm_demux_t *self, size_t pos); double plm_demux_decode_time(plm_demux_t *self); plm_packet_t *plm_demux_decode_packet(plm_demux_t *self, int type); plm_packet_t *plm_demux_get_packet(plm_demux_t *self); plm_demux_t *plm_demux_create(plm_buffer_t *buffer, int destroy_when_done) { plm_demux_t *self = (plm_demux_t *)malloc(sizeof(plm_demux_t)); memset(self, 0, sizeof(plm_demux_t)); self->buffer = buffer; self->destroy_buffer_when_done = destroy_when_done; self->start_time = PLM_PACKET_INVALID_TS; self->duration = PLM_PACKET_INVALID_TS; self->start_code = -1; plm_demux_has_headers(self); return self; } void plm_demux_destroy(plm_demux_t *self) { if (self->destroy_buffer_when_done) { plm_buffer_destroy(self->buffer); } free(self); } int plm_demux_has_headers(plm_demux_t *self) { if (self->has_headers) { return TRUE; } // Decode pack header if (!self->has_pack_header) { if ( self->start_code != PLM_START_PACK && plm_buffer_find_start_code(self->buffer, PLM_START_PACK) == -1 ) { return FALSE; } self->start_code = PLM_START_PACK; if (!plm_buffer_has(self->buffer, 64)) { return FALSE; } self->start_code = -1; if (plm_buffer_read(self->buffer, 4) != 0x02) { return FALSE; } self->system_clock_ref = plm_demux_decode_time(self); plm_buffer_skip(self->buffer, 1); plm_buffer_skip(self->buffer, 22); // mux_rate * 50 plm_buffer_skip(self->buffer, 1); self->has_pack_header = TRUE; } // Decode system header if (!self->has_system_header) { if ( self->start_code != PLM_START_SYSTEM && plm_buffer_find_start_code(self->buffer, PLM_START_SYSTEM) == -1 ) { return FALSE; } self->start_code = PLM_START_SYSTEM; if (!plm_buffer_has(self->buffer, 56)) { return FALSE; } self->start_code = -1; plm_buffer_skip(self->buffer, 16); // header_length plm_buffer_skip(self->buffer, 24); // rate bound self->num_audio_streams = plm_buffer_read(self->buffer, 6); plm_buffer_skip(self->buffer, 5); // misc flags self->num_video_streams = plm_buffer_read(self->buffer, 5); self->has_system_header = TRUE; } self->has_headers = TRUE; return TRUE; } int plm_demux_get_num_video_streams(plm_demux_t *self) { return plm_demux_has_headers(self) ? self->num_video_streams : 0; } int plm_demux_get_num_audio_streams(plm_demux_t *self) { return plm_demux_has_headers(self) ? self->num_audio_streams : 0; } void plm_demux_rewind(plm_demux_t *self) { plm_buffer_rewind(self->buffer); self->current_packet.length = 0; self->next_packet.length = 0; self->start_code = -1; } int plm_demux_has_ended(plm_demux_t *self) { return plm_buffer_has_ended(self->buffer); } void plm_demux_buffer_seek(plm_demux_t *self, size_t pos) { plm_buffer_seek(self->buffer, pos); self->current_packet.length = 0; self->next_packet.length = 0; self->start_code = -1; } double plm_demux_get_start_time(plm_demux_t *self, int type) { if (self->start_time != PLM_PACKET_INVALID_TS) { return self->start_time; } int previous_pos = plm_buffer_tell(self->buffer); int previous_start_code = self->start_code; // Find first video PTS plm_demux_rewind(self); do { plm_packet_t *packet = plm_demux_decode(self); if (!packet) { break; } if (packet->type == type) { self->start_time = packet->pts; } } while (self->start_time == PLM_PACKET_INVALID_TS); plm_demux_buffer_seek(self, previous_pos); self->start_code = previous_start_code; return self->start_time; } double plm_demux_get_duration(plm_demux_t *self, int type) { size_t file_size = plm_buffer_get_size(self->buffer); if ( self->duration != PLM_PACKET_INVALID_TS && self->last_file_size == file_size ) { return self->duration; } size_t previous_pos = plm_buffer_tell(self->buffer); int previous_start_code = self->start_code; // Find last video PTS. Start searching 64kb from the end and go further // back if needed. long start_range = 64 * 1024; long max_range = 4096 * 1024; for (long range = start_range; range <= max_range; range *= 2) { long seek_pos = file_size - range; if (seek_pos < 0) { seek_pos = 0; range = max_range; // Make sure to bail after this round } plm_demux_buffer_seek(self, seek_pos); self->current_packet.length = 0; double last_pts = PLM_PACKET_INVALID_TS; plm_packet_t *packet = NULL; while ((packet = plm_demux_decode(self))) { if (packet->pts != PLM_PACKET_INVALID_TS && packet->type == type) { last_pts = packet->pts; } } if (last_pts != PLM_PACKET_INVALID_TS) { self->duration = last_pts - plm_demux_get_start_time(self, type); break; } } plm_demux_buffer_seek(self, previous_pos); self->start_code = previous_start_code; self->last_file_size = file_size; return self->duration; } plm_packet_t *plm_demux_seek(plm_demux_t *self, double seek_time, int type, int force_intra) { if (!plm_demux_has_headers(self)) { return NULL; } // Using the current time, current byte position and the average bytes per // second for this file, try to jump to a byte position that hopefully has // packets containing timestamps within one second before to the desired // seek_time. // If we hit close to the seek_time scan through all packets to find the // last one (just before the seek_time) containing an intra frame. // Otherwise we should at least be closer than before. Calculate the bytes // per second for the jumped range and jump again. // The number of retries here is hard-limited to a generous amount. Usually // the correct range is found after 1--5 jumps, even for files with very // variable bitrates. If significantly more jumps are needed, there's // probably something wrong with the file and we just avoid getting into an // infinite loop. 32 retries should be enough for anybody. double duration = plm_demux_get_duration(self, type); long file_size = plm_buffer_get_size(self->buffer); long byterate = file_size / duration; double cur_time = self->last_decoded_pts; double scan_span = 1; if (seek_time > duration) { seek_time = duration; } else if (seek_time < 0) { seek_time = 0; } seek_time += self->start_time; for (int retry = 0; retry < 32; retry++) { int found_packet_with_pts = FALSE; int found_packet_in_range = FALSE; long last_valid_packet_start = -1; double first_packet_time = PLM_PACKET_INVALID_TS; long cur_pos = plm_buffer_tell(self->buffer); // Estimate byte offset and jump to it. long offset = (seek_time - cur_time - scan_span) * byterate; long seek_pos = cur_pos + offset; if (seek_pos < 0) { seek_pos = 0; } else if (seek_pos > file_size - 256) { seek_pos = file_size - 256; } plm_demux_buffer_seek(self, seek_pos); // Scan through all packets up to the seek_time to find the last packet // containing an intra frame. while (plm_buffer_find_start_code(self->buffer, type) != -1) { long packet_start = plm_buffer_tell(self->buffer); plm_packet_t *packet = plm_demux_decode_packet(self, type); // Skip packet if it has no PTS if (!packet || packet->pts == PLM_PACKET_INVALID_TS) { continue; } // Bail scanning through packets if we hit one that is outside // seek_time - scan_span. // We also adjust the cur_time and byterate values here so the next // iteration can be a bit more precise. if (packet->pts > seek_time || packet->pts < seek_time - scan_span) { found_packet_with_pts = TRUE; byterate = (seek_pos - cur_pos) / (packet->pts - cur_time); cur_time = packet->pts; break; } // If we are still here, it means this packet is in close range to // the seek_time. If this is the first packet for this jump position // record the PTS. If we later have to back off, when there was no // intra frame in this range, we can lower the seek_time to not scan // this range again. if (!found_packet_in_range) { found_packet_in_range = TRUE; first_packet_time = packet->pts; } // Check if this is an intra frame packet. If so, record the buffer // position of the start of this packet. We want to jump back to it // later, when we know it's the last intra frame before desired // seek time. if (force_intra) { for (size_t i = 0; i < packet->length - 6; i++) { // Find the START_PICTURE code if ( packet->data[i] == 0x00 && packet->data[i + 1] == 0x00 && packet->data[i + 2] == 0x01 && packet->data[i + 3] == 0x00 ) { // Bits 11--13 in the picture header contain the frame // type, where 1=Intra if ((packet->data[i + 5] & 0x38) == 8) { last_valid_packet_start = packet_start; } break; } } } // If we don't want intra frames, just use the last PTS found. else { last_valid_packet_start = packet_start; } } // If there was at least one intra frame in the range scanned above, // our search is over. Jump back to the packet and decode it again. if (last_valid_packet_start != -1) { plm_demux_buffer_seek(self, last_valid_packet_start); return plm_demux_decode_packet(self, type); } // If we hit the right range, but still found no intra frame, we have // to increases the scan_span. This is done exponentially to also handle // video files with very few intra frames. else if (found_packet_in_range) { scan_span *= 2; seek_time = first_packet_time; } // If we didn't find any packet with a PTS, it probably means we reached // the end of the file. Estimate byterate and cur_time accordingly. else if (!found_packet_with_pts) { byterate = (seek_pos - cur_pos) / (duration - cur_time); cur_time = duration; } } return NULL; } plm_packet_t *plm_demux_decode(plm_demux_t *self) { if (!plm_demux_has_headers(self)) { return NULL; } if (self->current_packet.length) { size_t bits_till_next_packet = self->current_packet.length << 3; if (!plm_buffer_has(self->buffer, bits_till_next_packet)) { return NULL; } plm_buffer_skip(self->buffer, bits_till_next_packet); self->current_packet.length = 0; } // Pending packet waiting for data? if (self->next_packet.length) { return plm_demux_get_packet(self); } // Pending packet waiting for header? if (self->start_code != -1) { return plm_demux_decode_packet(self, self->start_code); } do { self->start_code = plm_buffer_next_start_code(self->buffer); if ( self->start_code == PLM_DEMUX_PACKET_VIDEO_1 || self->start_code == PLM_DEMUX_PACKET_PRIVATE || ( self->start_code >= PLM_DEMUX_PACKET_AUDIO_1 && self->start_code <= PLM_DEMUX_PACKET_AUDIO_4 ) ) { return plm_demux_decode_packet(self, self->start_code); } } while (self->start_code != -1); return NULL; } double plm_demux_decode_time(plm_demux_t *self) { int64_t clock = plm_buffer_read(self->buffer, 3) << 30; plm_buffer_skip(self->buffer, 1); clock |= plm_buffer_read(self->buffer, 15) << 15; plm_buffer_skip(self->buffer, 1); clock |= plm_buffer_read(self->buffer, 15); plm_buffer_skip(self->buffer, 1); return (double)clock / 90000.0; } plm_packet_t *plm_demux_decode_packet(plm_demux_t *self, int type) { if (!plm_buffer_has(self->buffer, 16 << 3)) { return NULL; } self->start_code = -1; self->next_packet.type = type; self->next_packet.length = plm_buffer_read(self->buffer, 16); self->next_packet.length -= plm_buffer_skip_bytes(self->buffer, 0xff); // stuffing // skip P-STD if (plm_buffer_read(self->buffer, 2) == 0x01) { plm_buffer_skip(self->buffer, 16); self->next_packet.length -= 2; } int pts_dts_marker = plm_buffer_read(self->buffer, 2); if (pts_dts_marker == 0x03) { self->next_packet.pts = plm_demux_decode_time(self); self->last_decoded_pts = self->next_packet.pts; plm_buffer_skip(self->buffer, 40); // skip dts self->next_packet.length -= 10; } else if (pts_dts_marker == 0x02) { self->next_packet.pts = plm_demux_decode_time(self); self->last_decoded_pts = self->next_packet.pts; self->next_packet.length -= 5; } else if (pts_dts_marker == 0x00) { self->next_packet.pts = PLM_PACKET_INVALID_TS; plm_buffer_skip(self->buffer, 4); self->next_packet.length -= 1; } else { return NULL; // invalid } return plm_demux_get_packet(self); } plm_packet_t *plm_demux_get_packet(plm_demux_t *self) { if (!plm_buffer_has(self->buffer, self->next_packet.length << 3)) { return NULL; } self->current_packet.data = self->buffer->bytes + (self->buffer->bit_index >> 3); self->current_packet.length = self->next_packet.length; self->current_packet.type = self->next_packet.type; self->current_packet.pts = self->next_packet.pts; self->next_packet.length = 0; return &self->current_packet; } // ----------------------------------------------------------------------------- // plm_video implementation // Inspired by Java MPEG-1 Video Decoder and Player by Zoltan Korandi // https://sourceforge.net/projects/javampeg1video/ static const int PLM_VIDEO_PICTURE_TYPE_INTRA = 1; static const int PLM_VIDEO_PICTURE_TYPE_PREDICTIVE = 2; static const int PLM_VIDEO_PICTURE_TYPE_B = 3; static const int PLM_START_SEQUENCE = 0xB3; static const int PLM_START_SLICE_FIRST = 0x01; static const int PLM_START_SLICE_LAST = 0xAF; static const int PLM_START_PICTURE = 0x00; static const int PLM_START_EXTENSION = 0xB5; static const int PLM_START_USER_DATA = 0xB2; #define PLM_START_IS_SLICE(c) \ (c >= PLM_START_SLICE_FIRST && c <= PLM_START_SLICE_LAST) static const double PLM_VIDEO_PICTURE_RATE[] = { 0.000, 23.976, 24.000, 25.000, 29.970, 30.000, 50.000, 59.940, 60.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000 }; static const uint8_t PLM_VIDEO_ZIG_ZAG[] = { 0, 1, 8, 16, 9, 2, 3, 10, 17, 24, 32, 25, 18, 11, 4, 5, 12, 19, 26, 33, 40, 48, 41, 34, 27, 20, 13, 6, 7, 14, 21, 28, 35, 42, 49, 56, 57, 50, 43, 36, 29, 22, 15, 23, 30, 37, 44, 51, 58, 59, 52, 45, 38, 31, 39, 46, 53, 60, 61, 54, 47, 55, 62, 63 }; static const uint8_t PLM_VIDEO_INTRA_QUANT_MATRIX[] = { 8, 16, 19, 22, 26, 27, 29, 34, 16, 16, 22, 24, 27, 29, 34, 37, 19, 22, 26, 27, 29, 34, 34, 38, 22, 22, 26, 27, 29, 34, 37, 40, 22, 26, 27, 29, 32, 35, 40, 48, 26, 27, 29, 32, 35, 40, 48, 58, 26, 27, 29, 34, 38, 46, 56, 69, 27, 29, 35, 38, 46, 56, 69, 83 }; static const uint8_t PLM_VIDEO_NON_INTRA_QUANT_MATRIX[] = { 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16 }; static const uint8_t PLM_VIDEO_PREMULTIPLIER_MATRIX[] = { 32, 44, 42, 38, 32, 25, 17, 9, 44, 62, 58, 52, 44, 35, 24, 12, 42, 58, 55, 49, 42, 33, 23, 12, 38, 52, 49, 44, 38, 30, 20, 10, 32, 44, 42, 38, 32, 25, 17, 9, 25, 35, 33, 30, 25, 20, 14, 7, 17, 24, 23, 20, 17, 14, 9, 5, 9, 12, 12, 10, 9, 7, 5, 2 }; static const plm_vlc_t PLM_VIDEO_MACROBLOCK_ADDRESS_INCREMENT[] = { { 1 << 1, 0}, { 0, 1}, // 0: x { 2 << 1, 0}, { 3 << 1, 0}, // 1: 0x { 4 << 1, 0}, { 5 << 1, 0}, // 2: 00x { 0, 3}, { 0, 2}, // 3: 01x { 6 << 1, 0}, { 7 << 1, 0}, // 4: 000x { 0, 5}, { 0, 4}, // 5: 001x { 8 << 1, 0}, { 9 << 1, 0}, // 6: 0000x { 0, 7}, { 0, 6}, // 7: 0001x { 10 << 1, 0}, { 11 << 1, 0}, // 8: 0000 0x { 12 << 1, 0}, { 13 << 1, 0}, // 9: 0000 1x { 14 << 1, 0}, { 15 << 1, 0}, // 10: 0000 00x { 16 << 1, 0}, { 17 << 1, 0}, // 11: 0000 01x { 18 << 1, 0}, { 19 << 1, 0}, // 12: 0000 10x { 0, 9}, { 0, 8}, // 13: 0000 11x { -1, 0}, { 20 << 1, 0}, // 14: 0000 000x { -1, 0}, { 21 << 1, 0}, // 15: 0000 001x { 22 << 1, 0}, { 23 << 1, 0}, // 16: 0000 010x { 0, 15}, { 0, 14}, // 17: 0000 011x { 0, 13}, { 0, 12}, // 18: 0000 100x { 0, 11}, { 0, 10}, // 19: 0000 101x { 24 << 1, 0}, { 25 << 1, 0}, // 20: 0000 0001x { 26 << 1, 0}, { 27 << 1, 0}, // 21: 0000 0011x { 28 << 1, 0}, { 29 << 1, 0}, // 22: 0000 0100x { 30 << 1, 0}, { 31 << 1, 0}, // 23: 0000 0101x { 32 << 1, 0}, { -1, 0}, // 24: 0000 0001 0x { -1, 0}, { 33 << 1, 0}, // 25: 0000 0001 1x { 34 << 1, 0}, { 35 << 1, 0}, // 26: 0000 0011 0x { 36 << 1, 0}, { 37 << 1, 0}, // 27: 0000 0011 1x { 38 << 1, 0}, { 39 << 1, 0}, // 28: 0000 0100 0x { 0, 21}, { 0, 20}, // 29: 0000 0100 1x { 0, 19}, { 0, 18}, // 30: 0000 0101 0x { 0, 17}, { 0, 16}, // 31: 0000 0101 1x { 0, 35}, { -1, 0}, // 32: 0000 0001 00x { -1, 0}, { 0, 34}, // 33: 0000 0001 11x { 0, 33}, { 0, 32}, // 34: 0000 0011 00x { 0, 31}, { 0, 30}, // 35: 0000 0011 01x { 0, 29}, { 0, 28}, // 36: 0000 0011 10x { 0, 27}, { 0, 26}, // 37: 0000 0011 11x { 0, 25}, { 0, 24}, // 38: 0000 0100 00x { 0, 23}, { 0, 22}, // 39: 0000 0100 01x }; static const plm_vlc_t PLM_VIDEO_MACROBLOCK_TYPE_INTRA[] = { { 1 << 1, 0}, { 0, 0x01}, // 0: x { -1, 0}, { 0, 0x11}, // 1: 0x }; static const plm_vlc_t PLM_VIDEO_MACROBLOCK_TYPE_PREDICTIVE[] = { { 1 << 1, 0}, { 0, 0x0a}, // 0: x { 2 << 1, 0}, { 0, 0x02}, // 1: 0x { 3 << 1, 0}, { 0, 0x08}, // 2: 00x { 4 << 1, 0}, { 5 << 1, 0}, // 3: 000x { 6 << 1, 0}, { 0, 0x12}, // 4: 0000x { 0, 0x1a}, { 0, 0x01}, // 5: 0001x { -1, 0}, { 0, 0x11}, // 6: 0000 0x }; static const plm_vlc_t PLM_VIDEO_MACROBLOCK_TYPE_B[] = { { 1 << 1, 0}, { 2 << 1, 0}, // 0: x { 3 << 1, 0}, { 4 << 1, 0}, // 1: 0x { 0, 0x0c}, { 0, 0x0e}, // 2: 1x { 5 << 1, 0}, { 6 << 1, 0}, // 3: 00x { 0, 0x04}, { 0, 0x06}, // 4: 01x { 7 << 1, 0}, { 8 << 1, 0}, // 5: 000x { 0, 0x08}, { 0, 0x0a}, // 6: 001x { 9 << 1, 0}, { 10 << 1, 0}, // 7: 0000x { 0, 0x1e}, { 0, 0x01}, // 8: 0001x { -1, 0}, { 0, 0x11}, // 9: 0000 0x { 0, 0x16}, { 0, 0x1a}, // 10: 0000 1x }; static const plm_vlc_t *PLM_VIDEO_MACROBLOCK_TYPE[] = { NULL, PLM_VIDEO_MACROBLOCK_TYPE_INTRA, PLM_VIDEO_MACROBLOCK_TYPE_PREDICTIVE, PLM_VIDEO_MACROBLOCK_TYPE_B }; static const plm_vlc_t PLM_VIDEO_CODE_BLOCK_PATTERN[] = { { 1 << 1, 0}, { 2 << 1, 0}, // 0: x { 3 << 1, 0}, { 4 << 1, 0}, // 1: 0x { 5 << 1, 0}, { 6 << 1, 0}, // 2: 1x { 7 << 1, 0}, { 8 << 1, 0}, // 3: 00x { 9 << 1, 0}, { 10 << 1, 0}, // 4: 01x { 11 << 1, 0}, { 12 << 1, 0}, // 5: 10x { 13 << 1, 0}, { 0, 60}, // 6: 11x { 14 << 1, 0}, { 15 << 1, 0}, // 7: 000x { 16 << 1, 0}, { 17 << 1, 0}, // 8: 001x { 18 << 1, 0}, { 19 << 1, 0}, // 9: 010x { 20 << 1, 0}, { 21 << 1, 0}, // 10: 011x { 22 << 1, 0}, { 23 << 1, 0}, // 11: 100x { 0, 32}, { 0, 16}, // 12: 101x { 0, 8}, { 0, 4}, // 13: 110x { 24 << 1, 0}, { 25 << 1, 0}, // 14: 0000x { 26 << 1, 0}, { 27 << 1, 0}, // 15: 0001x { 28 << 1, 0}, { 29 << 1, 0}, // 16: 0010x { 30 << 1, 0}, { 31 << 1, 0}, // 17: 0011x { 0, 62}, { 0, 2}, // 18: 0100x { 0, 61}, { 0, 1}, // 19: 0101x { 0, 56}, { 0, 52}, // 20: 0110x { 0, 44}, { 0, 28}, // 21: 0111x { 0, 40}, { 0, 20}, // 22: 1000x { 0, 48}, { 0, 12}, // 23: 1001x { 32 << 1, 0}, { 33 << 1, 0}, // 24: 0000 0x { 34 << 1, 0}, { 35 << 1, 0}, // 25: 0000 1x { 36 << 1, 0}, { 37 << 1, 0}, // 26: 0001 0x { 38 << 1, 0}, { 39 << 1, 0}, // 27: 0001 1x { 40 << 1, 0}, { 41 << 1, 0}, // 28: 0010 0x { 42 << 1, 0}, { 43 << 1, 0}, // 29: 0010 1x { 0, 63}, { 0, 3}, // 30: 0011 0x { 0, 36}, { 0, 24}, // 31: 0011 1x { 44 << 1, 0}, { 45 << 1, 0}, // 32: 0000 00x { 46 << 1, 0}, { 47 << 1, 0}, // 33: 0000 01x { 48 << 1, 0}, { 49 << 1, 0}, // 34: 0000 10x { 50 << 1, 0}, { 51 << 1, 0}, // 35: 0000 11x { 52 << 1, 0}, { 53 << 1, 0}, // 36: 0001 00x { 54 << 1, 0}, { 55 << 1, 0}, // 37: 0001 01x { 56 << 1, 0}, { 57 << 1, 0}, // 38: 0001 10x { 58 << 1, 0}, { 59 << 1, 0}, // 39: 0001 11x { 0, 34}, { 0, 18}, // 40: 0010 00x { 0, 10}, { 0, 6}, // 41: 0010 01x { 0, 33}, { 0, 17}, // 42: 0010 10x { 0, 9}, { 0, 5}, // 43: 0010 11x { -1, 0}, { 60 << 1, 0}, // 44: 0000 000x { 61 << 1, 0}, { 62 << 1, 0}, // 45: 0000 001x { 0, 58}, { 0, 54}, // 46: 0000 010x { 0, 46}, { 0, 30}, // 47: 0000 011x { 0, 57}, { 0, 53}, // 48: 0000 100x { 0, 45}, { 0, 29}, // 49: 0000 101x { 0, 38}, { 0, 26}, // 50: 0000 110x { 0, 37}, { 0, 25}, // 51: 0000 111x { 0, 43}, { 0, 23}, // 52: 0001 000x { 0, 51}, { 0, 15}, // 53: 0001 001x { 0, 42}, { 0, 22}, // 54: 0001 010x { 0, 50}, { 0, 14}, // 55: 0001 011x { 0, 41}, { 0, 21}, // 56: 0001 100x { 0, 49}, { 0, 13}, // 57: 0001 101x { 0, 35}, { 0, 19}, // 58: 0001 110x { 0, 11}, { 0, 7}, // 59: 0001 111x { 0, 39}, { 0, 27}, // 60: 0000 0001x { 0, 59}, { 0, 55}, // 61: 0000 0010x { 0, 47}, { 0, 31}, // 62: 0000 0011x }; static const plm_vlc_t PLM_VIDEO_MOTION[] = { { 1 << 1, 0}, { 0, 0}, // 0: x { 2 << 1, 0}, { 3 << 1, 0}, // 1: 0x { 4 << 1, 0}, { 5 << 1, 0}, // 2: 00x { 0, 1}, { 0, -1}, // 3: 01x { 6 << 1, 0}, { 7 << 1, 0}, // 4: 000x { 0, 2}, { 0, -2}, // 5: 001x { 8 << 1, 0}, { 9 << 1, 0}, // 6: 0000x { 0, 3}, { 0, -3}, // 7: 0001x { 10 << 1, 0}, { 11 << 1, 0}, // 8: 0000 0x { 12 << 1, 0}, { 13 << 1, 0}, // 9: 0000 1x { -1, 0}, { 14 << 1, 0}, // 10: 0000 00x { 15 << 1, 0}, { 16 << 1, 0}, // 11: 0000 01x { 17 << 1, 0}, { 18 << 1, 0}, // 12: 0000 10x { 0, 4}, { 0, -4}, // 13: 0000 11x { -1, 0}, { 19 << 1, 0}, // 14: 0000 001x { 20 << 1, 0}, { 21 << 1, 0}, // 15: 0000 010x { 0, 7}, { 0, -7}, // 16: 0000 011x { 0, 6}, { 0, -6}, // 17: 0000 100x { 0, 5}, { 0, -5}, // 18: 0000 101x { 22 << 1, 0}, { 23 << 1, 0}, // 19: 0000 0011x { 24 << 1, 0}, { 25 << 1, 0}, // 20: 0000 0100x { 26 << 1, 0}, { 27 << 1, 0}, // 21: 0000 0101x { 28 << 1, 0}, { 29 << 1, 0}, // 22: 0000 0011 0x { 30 << 1, 0}, { 31 << 1, 0}, // 23: 0000 0011 1x { 32 << 1, 0}, { 33 << 1, 0}, // 24: 0000 0100 0x { 0, 10}, { 0, -10}, // 25: 0000 0100 1x { 0, 9}, { 0, -9}, // 26: 0000 0101 0x { 0, 8}, { 0, -8}, // 27: 0000 0101 1x { 0, 16}, { 0, -16}, // 28: 0000 0011 00x { 0, 15}, { 0, -15}, // 29: 0000 0011 01x { 0, 14}, { 0, -14}, // 30: 0000 0011 10x { 0, 13}, { 0, -13}, // 31: 0000 0011 11x { 0, 12}, { 0, -12}, // 32: 0000 0100 00x { 0, 11}, { 0, -11}, // 33: 0000 0100 01x }; static const plm_vlc_t PLM_VIDEO_DCT_SIZE_LUMINANCE[] = { { 1 << 1, 0}, { 2 << 1, 0}, // 0: x { 0, 1}, { 0, 2}, // 1: 0x { 3 << 1, 0}, { 4 << 1, 0}, // 2: 1x { 0, 0}, { 0, 3}, // 3: 10x { 0, 4}, { 5 << 1, 0}, // 4: 11x { 0, 5}, { 6 << 1, 0}, // 5: 111x { 0, 6}, { 7 << 1, 0}, // 6: 1111x { 0, 7}, { 8 << 1, 0}, // 7: 1111 1x { 0, 8}, { -1, 0}, // 8: 1111 11x }; static const plm_vlc_t PLM_VIDEO_DCT_SIZE_CHROMINANCE[] = { { 1 << 1, 0}, { 2 << 1, 0}, // 0: x { 0, 0}, { 0, 1}, // 1: 0x { 0, 2}, { 3 << 1, 0}, // 2: 1x { 0, 3}, { 4 << 1, 0}, // 3: 11x { 0, 4}, { 5 << 1, 0}, // 4: 111x { 0, 5}, { 6 << 1, 0}, // 5: 1111x { 0, 6}, { 7 << 1, 0}, // 6: 1111 1x { 0, 7}, { 8 << 1, 0}, // 7: 1111 11x { 0, 8}, { -1, 0}, // 8: 1111 111x }; static const plm_vlc_t *PLM_VIDEO_DCT_SIZE[] = { PLM_VIDEO_DCT_SIZE_LUMINANCE, PLM_VIDEO_DCT_SIZE_CHROMINANCE, PLM_VIDEO_DCT_SIZE_CHROMINANCE }; // dct_coeff bitmap: // 0xff00 run // 0x00ff level // Decoded values are unsigned. Sign bit follows in the stream. static const plm_vlc_uint_t PLM_VIDEO_DCT_COEFF[] = { { 1 << 1, 0}, { 0, 0x0001}, // 0: x { 2 << 1, 0}, { 3 << 1, 0}, // 1: 0x { 4 << 1, 0}, { 5 << 1, 0}, // 2: 00x { 6 << 1, 0}, { 0, 0x0101}, // 3: 01x { 7 << 1, 0}, { 8 << 1, 0}, // 4: 000x { 9 << 1, 0}, { 10 << 1, 0}, // 5: 001x { 0, 0x0002}, { 0, 0x0201}, // 6: 010x { 11 << 1, 0}, { 12 << 1, 0}, // 7: 0000x { 13 << 1, 0}, { 14 << 1, 0}, // 8: 0001x { 15 << 1, 0}, { 0, 0x0003}, // 9: 0010x { 0, 0x0401}, { 0, 0x0301}, // 10: 0011x { 16 << 1, 0}, { 0, 0xffff}, // 11: 0000 0x { 17 << 1, 0}, { 18 << 1, 0}, // 12: 0000 1x { 0, 0x0701}, { 0, 0x0601}, // 13: 0001 0x { 0, 0x0102}, { 0, 0x0501}, // 14: 0001 1x { 19 << 1, 0}, { 20 << 1, 0}, // 15: 0010 0x { 21 << 1, 0}, { 22 << 1, 0}, // 16: 0000 00x { 0, 0x0202}, { 0, 0x0901}, // 17: 0000 10x { 0, 0x0004}, { 0, 0x0801}, // 18: 0000 11x { 23 << 1, 0}, { 24 << 1, 0}, // 19: 0010 00x { 25 << 1, 0}, { 26 << 1, 0}, // 20: 0010 01x { 27 << 1, 0}, { 28 << 1, 0}, // 21: 0000 000x { 29 << 1, 0}, { 30 << 1, 0}, // 22: 0000 001x { 0, 0x0d01}, { 0, 0x0006}, // 23: 0010 000x { 0, 0x0c01}, { 0, 0x0b01}, // 24: 0010 001x { 0, 0x0302}, { 0, 0x0103}, // 25: 0010 010x { 0, 0x0005}, { 0, 0x0a01}, // 26: 0010 011x { 31 << 1, 0}, { 32 << 1, 0}, // 27: 0000 0000x { 33 << 1, 0}, { 34 << 1, 0}, // 28: 0000 0001x { 35 << 1, 0}, { 36 << 1, 0}, // 29: 0000 0010x { 37 << 1, 0}, { 38 << 1, 0}, // 30: 0000 0011x { 39 << 1, 0}, { 40 << 1, 0}, // 31: 0000 0000 0x { 41 << 1, 0}, { 42 << 1, 0}, // 32: 0000 0000 1x { 43 << 1, 0}, { 44 << 1, 0}, // 33: 0000 0001 0x { 45 << 1, 0}, { 46 << 1, 0}, // 34: 0000 0001 1x { 0, 0x1001}, { 0, 0x0502}, // 35: 0000 0010 0x { 0, 0x0007}, { 0, 0x0203}, // 36: 0000 0010 1x { 0, 0x0104}, { 0, 0x0f01}, // 37: 0000 0011 0x { 0, 0x0e01}, { 0, 0x0402}, // 38: 0000 0011 1x { 47 << 1, 0}, { 48 << 1, 0}, // 39: 0000 0000 00x { 49 << 1, 0}, { 50 << 1, 0}, // 40: 0000 0000 01x { 51 << 1, 0}, { 52 << 1, 0}, // 41: 0000 0000 10x { 53 << 1, 0}, { 54 << 1, 0}, // 42: 0000 0000 11x { 55 << 1, 0}, { 56 << 1, 0}, // 43: 0000 0001 00x { 57 << 1, 0}, { 58 << 1, 0}, // 44: 0000 0001 01x { 59 << 1, 0}, { 60 << 1, 0}, // 45: 0000 0001 10x { 61 << 1, 0}, { 62 << 1, 0}, // 46: 0000 0001 11x { -1, 0}, { 63 << 1, 0}, // 47: 0000 0000 000x { 64 << 1, 0}, { 65 << 1, 0}, // 48: 0000 0000 001x { 66 << 1, 0}, { 67 << 1, 0}, // 49: 0000 0000 010x { 68 << 1, 0}, { 69 << 1, 0}, // 50: 0000 0000 011x { 70 << 1, 0}, { 71 << 1, 0}, // 51: 0000 0000 100x { 72 << 1, 0}, { 73 << 1, 0}, // 52: 0000 0000 101x { 74 << 1, 0}, { 75 << 1, 0}, // 53: 0000 0000 110x { 76 << 1, 0}, { 77 << 1, 0}, // 54: 0000 0000 111x { 0, 0x000b}, { 0, 0x0802}, // 55: 0000 0001 000x { 0, 0x0403}, { 0, 0x000a}, // 56: 0000 0001 001x { 0, 0x0204}, { 0, 0x0702}, // 57: 0000 0001 010x { 0, 0x1501}, { 0, 0x1401}, // 58: 0000 0001 011x { 0, 0x0009}, { 0, 0x1301}, // 59: 0000 0001 100x { 0, 0x1201}, { 0, 0x0105}, // 60: 0000 0001 101x { 0, 0x0303}, { 0, 0x0008}, // 61: 0000 0001 110x { 0, 0x0602}, { 0, 0x1101}, // 62: 0000 0001 111x { 78 << 1, 0}, { 79 << 1, 0}, // 63: 0000 0000 0001x { 80 << 1, 0}, { 81 << 1, 0}, // 64: 0000 0000 0010x { 82 << 1, 0}, { 83 << 1, 0}, // 65: 0000 0000 0011x { 84 << 1, 0}, { 85 << 1, 0}, // 66: 0000 0000 0100x { 86 << 1, 0}, { 87 << 1, 0}, // 67: 0000 0000 0101x { 88 << 1, 0}, { 89 << 1, 0}, // 68: 0000 0000 0110x { 90 << 1, 0}, { 91 << 1, 0}, // 69: 0000 0000 0111x { 0, 0x0a02}, { 0, 0x0902}, // 70: 0000 0000 1000x { 0, 0x0503}, { 0, 0x0304}, // 71: 0000 0000 1001x { 0, 0x0205}, { 0, 0x0107}, // 72: 0000 0000 1010x { 0, 0x0106}, { 0, 0x000f}, // 73: 0000 0000 1011x { 0, 0x000e}, { 0, 0x000d}, // 74: 0000 0000 1100x { 0, 0x000c}, { 0, 0x1a01}, // 75: 0000 0000 1101x { 0, 0x1901}, { 0, 0x1801}, // 76: 0000 0000 1110x { 0, 0x1701}, { 0, 0x1601}, // 77: 0000 0000 1111x { 92 << 1, 0}, { 93 << 1, 0}, // 78: 0000 0000 0001 0x { 94 << 1, 0}, { 95 << 1, 0}, // 79: 0000 0000 0001 1x { 96 << 1, 0}, { 97 << 1, 0}, // 80: 0000 0000 0010 0x { 98 << 1, 0}, { 99 << 1, 0}, // 81: 0000 0000 0010 1x {100 << 1, 0}, {101 << 1, 0}, // 82: 0000 0000 0011 0x {102 << 1, 0}, {103 << 1, 0}, // 83: 0000 0000 0011 1x { 0, 0x001f}, { 0, 0x001e}, // 84: 0000 0000 0100 0x { 0, 0x001d}, { 0, 0x001c}, // 85: 0000 0000 0100 1x { 0, 0x001b}, { 0, 0x001a}, // 86: 0000 0000 0101 0x { 0, 0x0019}, { 0, 0x0018}, // 87: 0000 0000 0101 1x { 0, 0x0017}, { 0, 0x0016}, // 88: 0000 0000 0110 0x { 0, 0x0015}, { 0, 0x0014}, // 89: 0000 0000 0110 1x { 0, 0x0013}, { 0, 0x0012}, // 90: 0000 0000 0111 0x { 0, 0x0011}, { 0, 0x0010}, // 91: 0000 0000 0111 1x {104 << 1, 0}, {105 << 1, 0}, // 92: 0000 0000 0001 00x {106 << 1, 0}, {107 << 1, 0}, // 93: 0000 0000 0001 01x {108 << 1, 0}, {109 << 1, 0}, // 94: 0000 0000 0001 10x {110 << 1, 0}, {111 << 1, 0}, // 95: 0000 0000 0001 11x { 0, 0x0028}, { 0, 0x0027}, // 96: 0000 0000 0010 00x { 0, 0x0026}, { 0, 0x0025}, // 97: 0000 0000 0010 01x { 0, 0x0024}, { 0, 0x0023}, // 98: 0000 0000 0010 10x { 0, 0x0022}, { 0, 0x0021}, // 99: 0000 0000 0010 11x { 0, 0x0020}, { 0, 0x010e}, // 100: 0000 0000 0011 00x { 0, 0x010d}, { 0, 0x010c}, // 101: 0000 0000 0011 01x { 0, 0x010b}, { 0, 0x010a}, // 102: 0000 0000 0011 10x { 0, 0x0109}, { 0, 0x0108}, // 103: 0000 0000 0011 11x { 0, 0x0112}, { 0, 0x0111}, // 104: 0000 0000 0001 000x { 0, 0x0110}, { 0, 0x010f}, // 105: 0000 0000 0001 001x { 0, 0x0603}, { 0, 0x1002}, // 106: 0000 0000 0001 010x { 0, 0x0f02}, { 0, 0x0e02}, // 107: 0000 0000 0001 011x { 0, 0x0d02}, { 0, 0x0c02}, // 108: 0000 0000 0001 100x { 0, 0x0b02}, { 0, 0x1f01}, // 109: 0000 0000 0001 101x { 0, 0x1e01}, { 0, 0x1d01}, // 110: 0000 0000 0001 110x { 0, 0x1c01}, { 0, 0x1b01}, // 111: 0000 0000 0001 111x }; typedef struct { int full_px; int is_set; int r_size; int h; int v; } plm_video_motion_t; typedef struct plm_video_t { double framerate; double time; int frames_decoded; int width; int height; int mb_width; int mb_height; int mb_size; int luma_width; int luma_height; int chroma_width; int chroma_height; int start_code; int picture_type; plm_video_motion_t motion_forward; plm_video_motion_t motion_backward; int has_sequence_header; int quantizer_scale; int slice_begin; int macroblock_address; int mb_row; int mb_col; int macroblock_type; int macroblock_intra; int dc_predictor[3]; plm_buffer_t *buffer; int destroy_buffer_when_done; plm_frame_t frame_current; plm_frame_t frame_forward; plm_frame_t frame_backward; uint8_t *frames_data; int block_data[64]; uint8_t intra_quant_matrix[64]; uint8_t non_intra_quant_matrix[64]; int has_reference_frame; int assume_no_b_frames; } plm_video_t; static inline uint8_t plm_clamp(int n) { if (n > 255) { n = 255; } else if (n < 0) { n = 0; } return n; } int plm_video_decode_sequence_header(plm_video_t *self); void plm_video_init_frame(plm_video_t *self, plm_frame_t *frame, uint8_t *base); void plm_video_decode_picture(plm_video_t *self); void plm_video_decode_slice(plm_video_t *self, int slice); void plm_video_decode_macroblock(plm_video_t *self); void plm_video_decode_motion_vectors(plm_video_t *self); int plm_video_decode_motion_vector(plm_video_t *self, int r_size, int motion); void plm_video_predict_macroblock(plm_video_t *self); void plm_video_copy_macroblock(plm_video_t *self, int motion_h, int motion_v, plm_frame_t *d); void plm_video_interpolate_macroblock(plm_video_t *self, int motion_h, int motion_v, plm_frame_t *d); void plm_video_process_macroblock(plm_video_t *self, uint8_t *d, uint8_t *s, int mh, int mb, int bs, int interp); void plm_video_decode_block(plm_video_t *self, int block); void plm_video_idct(int *block); plm_video_t * plm_video_create_with_buffer(plm_buffer_t *buffer, int destroy_when_done) { plm_video_t *self = (plm_video_t *)malloc(sizeof(plm_video_t)); memset(self, 0, sizeof(plm_video_t)); self->buffer = buffer; self->destroy_buffer_when_done = destroy_when_done; // Attempt to decode the sequence header self->start_code = plm_buffer_find_start_code(self->buffer, PLM_START_SEQUENCE); if (self->start_code != -1) { plm_video_decode_sequence_header(self); } return self; } void plm_video_destroy(plm_video_t *self) { if (self->destroy_buffer_when_done) { plm_buffer_destroy(self->buffer); } if (self->has_sequence_header) { free(self->frames_data); } free(self); } double plm_video_get_framerate(plm_video_t *self) { return plm_video_has_header(self) ? self->framerate : 0; } int plm_video_get_width(plm_video_t *self) { return plm_video_has_header(self) ? self->width : 0; } int plm_video_get_height(plm_video_t *self) { return plm_video_has_header(self) ? self->height : 0; } void plm_video_set_no_delay(plm_video_t *self, int no_delay) { self->assume_no_b_frames = no_delay; } double plm_video_get_time(plm_video_t *self) { return self->time; } void plm_video_set_time(plm_video_t *self, double time) { self->frames_decoded = self->framerate * time; self->time = time; } void plm_video_rewind(plm_video_t *self) { plm_buffer_rewind(self->buffer); self->time = 0; self->frames_decoded = 0; self->has_reference_frame = FALSE; self->start_code = -1; } int plm_video_has_ended(plm_video_t *self) { return plm_buffer_has_ended(self->buffer); } plm_frame_t *plm_video_decode(plm_video_t *self) { if (!plm_video_has_header(self)) { return NULL; } plm_frame_t *frame = NULL; do { if (self->start_code != PLM_START_PICTURE) { self->start_code = plm_buffer_find_start_code(self->buffer, PLM_START_PICTURE); if (self->start_code == -1) { // If we reached the end of the file and the previously decoded // frame was a reference frame, we still have to return it. if ( self->has_reference_frame && !self->assume_no_b_frames && plm_buffer_has_ended(self->buffer) && ( self->picture_type == PLM_VIDEO_PICTURE_TYPE_INTRA || self->picture_type == PLM_VIDEO_PICTURE_TYPE_PREDICTIVE ) ) { self->has_reference_frame = FALSE; frame = &self->frame_backward; break; } return NULL; } } // Make sure we have a full picture in the buffer before attempting to // decode it. Sadly, this can only be done by seeking for the start code // of the next picture. Also, if we didn't find the start code for the // next picture, but the source has ended, we assume that this last // picture is in the buffer. if ( plm_buffer_has_start_code(self->buffer, PLM_START_PICTURE) == -1 && !plm_buffer_has_ended(self->buffer) ) { return NULL; } plm_video_decode_picture(self); if (self->assume_no_b_frames) { frame = &self->frame_backward; } else if (self->picture_type == PLM_VIDEO_PICTURE_TYPE_B) { frame = &self->frame_current; } else if (self->has_reference_frame) { frame = &self->frame_forward; } else { self->has_reference_frame = TRUE; } } while (!frame); frame->time = self->time; self->frames_decoded++; self->time = (double)self->frames_decoded / self->framerate; return frame; } int plm_video_has_header(plm_video_t *self) { if (self->has_sequence_header) { return TRUE; } if (self->start_code != PLM_START_SEQUENCE) { self->start_code = plm_buffer_find_start_code(self->buffer, PLM_START_SEQUENCE); } if (self->start_code == -1) { return FALSE; } if (!plm_video_decode_sequence_header(self)) { return FALSE; } return TRUE; } int plm_video_decode_sequence_header(plm_video_t *self) { int max_header_size = 64 + 2 * 64 * 8; // 64 bit header + 2x 64 byte matrix if (!plm_buffer_has(self->buffer, max_header_size)) { return FALSE; } self->width = plm_buffer_read(self->buffer, 12); self->height = plm_buffer_read(self->buffer, 12); if (self->width <= 0 || self->height <= 0) { return FALSE; } // Skip pixel aspect ratio plm_buffer_skip(self->buffer, 4); self->framerate = PLM_VIDEO_PICTURE_RATE[plm_buffer_read(self->buffer, 4)]; // Skip bit_rate, marker, buffer_size and constrained bit plm_buffer_skip(self->buffer, 18 + 1 + 10 + 1); // Load custom intra quant matrix? if (plm_buffer_read(self->buffer, 1)) { for (int i = 0; i < 64; i++) { int idx = PLM_VIDEO_ZIG_ZAG[i]; self->intra_quant_matrix[idx] = plm_buffer_read(self->buffer, 8); } } else { memcpy(self->intra_quant_matrix, PLM_VIDEO_INTRA_QUANT_MATRIX, 64); } // Load custom non intra quant matrix? if (plm_buffer_read(self->buffer, 1)) { for (int i = 0; i < 64; i++) { int idx = PLM_VIDEO_ZIG_ZAG[i]; self->non_intra_quant_matrix[idx] = plm_buffer_read(self->buffer, 8); } } else { memcpy(self->non_intra_quant_matrix, PLM_VIDEO_NON_INTRA_QUANT_MATRIX, 64); } self->mb_width = (self->width + 15) >> 4; self->mb_height = (self->height + 15) >> 4; self->mb_size = self->mb_width * self->mb_height; self->luma_width = self->mb_width << 4; self->luma_height = self->mb_height << 4; self->chroma_width = self->mb_width << 3; self->chroma_height = self->mb_height << 3; // Allocate one big chunk of data for all 3 frames = 9 planes size_t luma_plane_size = self->luma_width * self->luma_height; size_t chroma_plane_size = self->chroma_width * self->chroma_height; size_t frame_data_size = (luma_plane_size + 2 * chroma_plane_size); self->frames_data = (uint8_t*)malloc(frame_data_size * 3); plm_video_init_frame(self, &self->frame_current, self->frames_data + frame_data_size * 0); plm_video_init_frame(self, &self->frame_forward, self->frames_data + frame_data_size * 1); plm_video_init_frame(self, &self->frame_backward, self->frames_data + frame_data_size * 2); self->has_sequence_header = TRUE; return TRUE; } void plm_video_init_frame(plm_video_t *self, plm_frame_t *frame, uint8_t *base) { size_t luma_plane_size = self->luma_width * self->luma_height; size_t chroma_plane_size = self->chroma_width * self->chroma_height; frame->width = self->width; frame->height = self->height; frame->y.width = self->luma_width; frame->y.height = self->luma_height; frame->y.data = base; frame->cr.width = self->chroma_width; frame->cr.height = self->chroma_height; frame->cr.data = base + luma_plane_size; frame->cb.width = self->chroma_width; frame->cb.height = self->chroma_height; frame->cb.data = base + luma_plane_size + chroma_plane_size; } void plm_video_decode_picture(plm_video_t *self) { plm_buffer_skip(self->buffer, 10); // skip temporalReference self->picture_type = plm_buffer_read(self->buffer, 3); plm_buffer_skip(self->buffer, 16); // skip vbv_delay // D frames or unknown coding type if (self->picture_type <= 0 || self->picture_type > PLM_VIDEO_PICTURE_TYPE_B) { return; } // Forward full_px, f_code if ( self->picture_type == PLM_VIDEO_PICTURE_TYPE_PREDICTIVE || self->picture_type == PLM_VIDEO_PICTURE_TYPE_B ) { self->motion_forward.full_px = plm_buffer_read(self->buffer, 1); int f_code = plm_buffer_read(self->buffer, 3); if (f_code == 0) { // Ignore picture with zero f_code return; } self->motion_forward.r_size = f_code - 1; } // Backward full_px, f_code if (self->picture_type == PLM_VIDEO_PICTURE_TYPE_B) { self->motion_backward.full_px = plm_buffer_read(self->buffer, 1); int f_code = plm_buffer_read(self->buffer, 3); if (f_code == 0) { // Ignore picture with zero f_code return; } self->motion_backward.r_size = f_code - 1; } plm_frame_t frame_temp = self->frame_forward; if ( self->picture_type == PLM_VIDEO_PICTURE_TYPE_INTRA || self->picture_type == PLM_VIDEO_PICTURE_TYPE_PREDICTIVE ) { self->frame_forward = self->frame_backward; } // Find the first slice; this skips extension and user data do { self->start_code = plm_buffer_next_start_code(self->buffer); } while (!PLM_START_IS_SLICE(self->start_code)); // Decode all slices do { plm_video_decode_slice(self, self->start_code & 0x000000FF); if (self->macroblock_address == self->mb_size - 1) { break; } self->start_code = plm_buffer_next_start_code(self->buffer); } while (PLM_START_IS_SLICE(self->start_code)); // If this is a reference picture rotate the prediction pointers if ( self->picture_type == PLM_VIDEO_PICTURE_TYPE_INTRA || self->picture_type == PLM_VIDEO_PICTURE_TYPE_PREDICTIVE ) { self->frame_backward = self->frame_current; self->frame_current = frame_temp; } } void plm_video_decode_slice(plm_video_t *self, int slice) { self->slice_begin = TRUE; self->macroblock_address = (slice - 1) * self->mb_width - 1; // Reset motion vectors and DC predictors self->motion_backward.h = self->motion_forward.h = 0; self->motion_backward.v = self->motion_forward.v = 0; self->dc_predictor[0] = 128; self->dc_predictor[1] = 128; self->dc_predictor[2] = 128; self->quantizer_scale = plm_buffer_read(self->buffer, 5); // Skip extra while (plm_buffer_read(self->buffer, 1)) { plm_buffer_skip(self->buffer, 8); } do { plm_video_decode_macroblock(self); } while ( self->macroblock_address < self->mb_size - 1 && plm_buffer_no_start_code(self->buffer) ); } void plm_video_decode_macroblock(plm_video_t *self) { // Decode self->macroblock_address_increment int increment = 0; int t = plm_buffer_read_vlc(self->buffer, PLM_VIDEO_MACROBLOCK_ADDRESS_INCREMENT); while (t == 34) { // macroblock_stuffing t = plm_buffer_read_vlc(self->buffer, PLM_VIDEO_MACROBLOCK_ADDRESS_INCREMENT); } while (t == 35) { // macroblock_escape increment += 33; t = plm_buffer_read_vlc(self->buffer, PLM_VIDEO_MACROBLOCK_ADDRESS_INCREMENT); } increment += t; // Process any skipped macroblocks if (self->slice_begin) { // The first self->macroblock_address_increment of each slice is relative // to beginning of the preverious row, not the preverious macroblock self->slice_begin = FALSE; self->macroblock_address += increment; } else { if (self->macroblock_address + increment >= self->mb_size) { return; // invalid } if (increment > 1) { // Skipped macroblocks reset DC predictors self->dc_predictor[0] = 128; self->dc_predictor[1] = 128; self->dc_predictor[2] = 128; // Skipped macroblocks in P-pictures reset motion vectors if (self->picture_type == PLM_VIDEO_PICTURE_TYPE_PREDICTIVE) { self->motion_forward.h = 0; self->motion_forward.v = 0; } } // Predict skipped macroblocks while (increment > 1) { self->macroblock_address++; self->mb_row = self->macroblock_address / self->mb_width; self->mb_col = self->macroblock_address % self->mb_width; plm_video_predict_macroblock(self); increment--; } self->macroblock_address++; } self->mb_row = self->macroblock_address / self->mb_width; self->mb_col = self->macroblock_address % self->mb_width; if (self->mb_col >= self->mb_width || self->mb_row >= self->mb_height) { return; // corrupt stream; } // Process the current macroblock const plm_vlc_t *table = PLM_VIDEO_MACROBLOCK_TYPE[self->picture_type]; self->macroblock_type = plm_buffer_read_vlc(self->buffer, table); self->macroblock_intra = (self->macroblock_type & 0x01); self->motion_forward.is_set = (self->macroblock_type & 0x08); self->motion_backward.is_set = (self->macroblock_type & 0x04); // Quantizer scale if ((self->macroblock_type & 0x10) != 0) { self->quantizer_scale = plm_buffer_read(self->buffer, 5); } if (self->macroblock_intra) { // Intra-coded macroblocks reset motion vectors self->motion_backward.h = self->motion_forward.h = 0; self->motion_backward.v = self->motion_forward.v = 0; } else { // Non-intra macroblocks reset DC predictors self->dc_predictor[0] = 128; self->dc_predictor[1] = 128; self->dc_predictor[2] = 128; plm_video_decode_motion_vectors(self); plm_video_predict_macroblock(self); } // Decode blocks int cbp = ((self->macroblock_type & 0x02) != 0) ? plm_buffer_read_vlc(self->buffer, PLM_VIDEO_CODE_BLOCK_PATTERN) : (self->macroblock_intra ? 0x3f : 0); for (int block = 0, mask = 0x20; block < 6; block++) { if ((cbp & mask) != 0) { plm_video_decode_block(self, block); } mask >>= 1; } } void plm_video_decode_motion_vectors(plm_video_t *self) { // Forward if (self->motion_forward.is_set) { int r_size = self->motion_forward.r_size; self->motion_forward.h = plm_video_decode_motion_vector(self, r_size, self->motion_forward.h); self->motion_forward.v = plm_video_decode_motion_vector(self, r_size, self->motion_forward.v); } else if (self->picture_type == PLM_VIDEO_PICTURE_TYPE_PREDICTIVE) { // No motion information in P-picture, reset vectors self->motion_forward.h = 0; self->motion_forward.v = 0; } if (self->motion_backward.is_set) { int r_size = self->motion_backward.r_size; self->motion_backward.h = plm_video_decode_motion_vector(self, r_size, self->motion_backward.h); self->motion_backward.v = plm_video_decode_motion_vector(self, r_size, self->motion_backward.v); } } int plm_video_decode_motion_vector(plm_video_t *self, int r_size, int motion) { int fscale = 1 << r_size; int m_code = plm_buffer_read_vlc(self->buffer, PLM_VIDEO_MOTION); int r = 0; int d; if ((m_code != 0) && (fscale != 1)) { r = plm_buffer_read(self->buffer, r_size); d = ((abs(m_code) - 1) << r_size) + r + 1; if (m_code < 0) { d = -d; } } else { d = m_code; } motion += d; if (motion >(fscale << 4) - 1) { motion -= fscale << 5; } else if (motion < ((-fscale) << 4)) { motion += fscale << 5; } return motion; } void plm_video_predict_macroblock(plm_video_t *self) { int fw_h = self->motion_forward.h; int fw_v = self->motion_forward.v; if (self->motion_forward.full_px) { fw_h <<= 1; fw_v <<= 1; } if (self->picture_type == PLM_VIDEO_PICTURE_TYPE_B) { int bw_h = self->motion_backward.h; int bw_v = self->motion_backward.v; if (self->motion_backward.full_px) { bw_h <<= 1; bw_v <<= 1; } if (self->motion_forward.is_set) { plm_video_copy_macroblock(self, fw_h, fw_v, &self->frame_forward); if (self->motion_backward.is_set) { plm_video_interpolate_macroblock(self, bw_h, bw_v, &self->frame_backward); } } else { plm_video_copy_macroblock(self, bw_h, bw_v, &self->frame_backward); } } else { plm_video_copy_macroblock(self, fw_h, fw_v, &self->frame_forward); } } void plm_video_copy_macroblock(plm_video_t *self, int motion_h, int motion_v, plm_frame_t *d) { plm_frame_t *s = &self->frame_current; plm_video_process_macroblock(self, s->y.data, d->y.data, motion_h, motion_v, 16, FALSE); plm_video_process_macroblock(self, s->cr.data, d->cr.data, motion_h / 2, motion_v / 2, 8, FALSE); plm_video_process_macroblock(self, s->cb.data, d->cb.data, motion_h / 2, motion_v / 2, 8, FALSE); } void plm_video_interpolate_macroblock(plm_video_t *self, int motion_h, int motion_v, plm_frame_t *d) { plm_frame_t *s = &self->frame_current; plm_video_process_macroblock(self, s->y.data, d->y.data, motion_h, motion_v, 16, TRUE); plm_video_process_macroblock(self, s->cr.data, d->cr.data, motion_h / 2, motion_v / 2, 8, TRUE); plm_video_process_macroblock(self, s->cb.data, d->cb.data, motion_h / 2, motion_v / 2, 8, TRUE); } #define PLM_BLOCK_SET(DEST, DEST_INDEX, DEST_WIDTH, SOURCE_INDEX, SOURCE_WIDTH, BLOCK_SIZE, OP) do { \ int dest_scan = DEST_WIDTH - BLOCK_SIZE; \ int source_scan = SOURCE_WIDTH - BLOCK_SIZE; \ for (int y = 0; y < BLOCK_SIZE; y++) { \ for (int x = 0; x < BLOCK_SIZE; x++) { \ DEST[DEST_INDEX] = OP; \ SOURCE_INDEX++; DEST_INDEX++; \ } \ SOURCE_INDEX += source_scan; \ DEST_INDEX += dest_scan; \ }} while(FALSE) void plm_video_process_macroblock( plm_video_t *self, uint8_t *d, uint8_t *s, int motion_h, int motion_v, int block_size, int interpolate ) { int dw = self->mb_width * block_size; int hp = motion_h >> 1; int vp = motion_v >> 1; int odd_h = (motion_h & 1) == 1; int odd_v = (motion_v & 1) == 1; unsigned int si = ((self->mb_row * block_size) + vp) * dw + (self->mb_col * block_size) + hp; unsigned int di = (self->mb_row * dw + self->mb_col) * block_size; unsigned int max_address = (dw * (self->mb_height * block_size - block_size + 1) - block_size); if (si > max_address || di > max_address) { return; // corrupt video } #define PLM_MB_CASE(INTERPOLATE, ODD_H, ODD_V, OP) \ case ((INTERPOLATE << 2) | (ODD_H << 1) | (ODD_V)): \ PLM_BLOCK_SET(d, di, dw, si, dw, block_size, OP); \ break switch ((interpolate << 2) | (odd_h << 1) | (odd_v)) { PLM_MB_CASE(0, 0, 0, (s[si])); PLM_MB_CASE(0, 0, 1, (s[si] + s[si + dw] + 1) >> 1); PLM_MB_CASE(0, 1, 0, (s[si] + s[si + 1] + 1) >> 1); PLM_MB_CASE(0, 1, 1, (s[si] + s[si + 1] + s[si + dw] + s[si + dw + 1] + 2) >> 2); PLM_MB_CASE(1, 0, 0, (d[di] + (s[si]) + 1) >> 1); PLM_MB_CASE(1, 0, 1, (d[di] + ((s[si] + s[si + dw] + 1) >> 1) + 1) >> 1); PLM_MB_CASE(1, 1, 0, (d[di] + ((s[si] + s[si + 1] + 1) >> 1) + 1) >> 1); PLM_MB_CASE(1, 1, 1, (d[di] + ((s[si] + s[si + 1] + s[si + dw] + s[si + dw + 1] + 2) >> 2) + 1) >> 1); } #undef PLM_MB_CASE } void plm_video_decode_block(plm_video_t *self, int block) { int n = 0; uint8_t *quant_matrix; // Decode DC coefficient of intra-coded blocks if (self->macroblock_intra) { int predictor; int dct_size; // DC prediction int plane_index = block > 3 ? block - 3 : 0; predictor = self->dc_predictor[plane_index]; dct_size = plm_buffer_read_vlc(self->buffer, PLM_VIDEO_DCT_SIZE[plane_index]); // Read DC coeff if (dct_size > 0) { int differential = plm_buffer_read(self->buffer, dct_size); if ((differential & (1 << (dct_size - 1))) != 0) { self->block_data[0] = predictor + differential; } else { self->block_data[0] = predictor + (-(1 << dct_size) | (differential + 1)); } } else { self->block_data[0] = predictor; } // Save predictor value self->dc_predictor[plane_index] = self->block_data[0]; // Dequantize + premultiply self->block_data[0] <<= (3 + 5); quant_matrix = self->intra_quant_matrix; n = 1; } else { quant_matrix = self->non_intra_quant_matrix; } // Decode AC coefficients (+DC for non-intra) int level = 0; while (TRUE) { int run = 0; uint16_t coeff = plm_buffer_read_vlc_uint(self->buffer, PLM_VIDEO_DCT_COEFF); if ((coeff == 0x0001) && (n > 0) && (plm_buffer_read(self->buffer, 1) == 0)) { // end_of_block break; } if (coeff == 0xffff) { // escape run = plm_buffer_read(self->buffer, 6); level = plm_buffer_read(self->buffer, 8); if (level == 0) { level = plm_buffer_read(self->buffer, 8); } else if (level == 128) { level = plm_buffer_read(self->buffer, 8) - 256; } else if (level > 128) { level = level - 256; } } else { run = coeff >> 8; level = coeff & 0xff; if (plm_buffer_read(self->buffer, 1)) { level = -level; } } n += run; if (n < 0 || n >= 64) { return; // invalid } int de_zig_zagged = PLM_VIDEO_ZIG_ZAG[n]; n++; // Dequantize, oddify, clip level <<= 1; if (!self->macroblock_intra) { level += (level < 0 ? -1 : 1); } level = (level * self->quantizer_scale * quant_matrix[de_zig_zagged]) >> 4; if ((level & 1) == 0) { level -= level > 0 ? 1 : -1; } if (level > 2047) { level = 2047; } else if (level < -2048) { level = -2048; } // Save premultiplied coefficient self->block_data[de_zig_zagged] = level * PLM_VIDEO_PREMULTIPLIER_MATRIX[de_zig_zagged]; } // Move block to its place uint8_t *d; int dw; int di; if (block < 4) { d = self->frame_current.y.data; dw = self->luma_width; di = (self->mb_row * self->luma_width + self->mb_col) << 4; if ((block & 1) != 0) { di += 8; } if ((block & 2) != 0) { di += self->luma_width << 3; } } else { d = (block == 4) ? self->frame_current.cb.data : self->frame_current.cr.data; dw = self->chroma_width; di = ((self->mb_row * self->luma_width) << 2) + (self->mb_col << 3); } int *s = self->block_data; int si = 0; if (self->macroblock_intra) { // Overwrite (no prediction) if (n == 1) { int clamped = plm_clamp((s[0] + 128) >> 8); PLM_BLOCK_SET(d, di, dw, si, 8, 8, clamped); s[0] = 0; } else { plm_video_idct(s); PLM_BLOCK_SET(d, di, dw, si, 8, 8, plm_clamp(s[si])); memset(self->block_data, 0, sizeof(self->block_data)); } } else { // Add data to the predicted macroblock if (n == 1) { int value = (s[0] + 128) >> 8; PLM_BLOCK_SET(d, di, dw, si, 8, 8, plm_clamp(d[di] + value)); s[0] = 0; } else { plm_video_idct(s); PLM_BLOCK_SET(d, di, dw, si, 8, 8, plm_clamp(d[di] + s[si])); memset(self->block_data, 0, sizeof(self->block_data)); } } } void plm_video_idct(int *block) { int b1, b3, b4, b6, b7, tmp1, tmp2, m0, x0, x1, x2, x3, x4, y3, y4, y5, y6, y7; // Transform columns for (int i = 0; i < 8; ++i) { b1 = block[4 * 8 + i]; b3 = block[2 * 8 + i] + block[6 * 8 + i]; b4 = block[5 * 8 + i] - block[3 * 8 + i]; tmp1 = block[1 * 8 + i] + block[7 * 8 + i]; tmp2 = block[3 * 8 + i] + block[5 * 8 + i]; b6 = block[1 * 8 + i] - block[7 * 8 + i]; b7 = tmp1 + tmp2; m0 = block[0 * 8 + i]; x4 = ((b6 * 473 - b4 * 196 + 128) >> 8) - b7; x0 = x4 - (((tmp1 - tmp2) * 362 + 128) >> 8); x1 = m0 - b1; x2 = (((block[2 * 8 + i] - block[6 * 8 + i]) * 362 + 128) >> 8) - b3; x3 = m0 + b1; y3 = x1 + x2; y4 = x3 + b3; y5 = x1 - x2; y6 = x3 - b3; y7 = -x0 - ((b4 * 473 + b6 * 196 + 128) >> 8); block[0 * 8 + i] = b7 + y4; block[1 * 8 + i] = x4 + y3; block[2 * 8 + i] = y5 - x0; block[3 * 8 + i] = y6 - y7; block[4 * 8 + i] = y6 + y7; block[5 * 8 + i] = x0 + y5; block[6 * 8 + i] = y3 - x4; block[7 * 8 + i] = y4 - b7; } // Transform rows for (int i = 0; i < 64; i += 8) { b1 = block[4 + i]; b3 = block[2 + i] + block[6 + i]; b4 = block[5 + i] - block[3 + i]; tmp1 = block[1 + i] + block[7 + i]; tmp2 = block[3 + i] + block[5 + i]; b6 = block[1 + i] - block[7 + i]; b7 = tmp1 + tmp2; m0 = block[0 + i]; x4 = ((b6 * 473 - b4 * 196 + 128) >> 8) - b7; x0 = x4 - (((tmp1 - tmp2) * 362 + 128) >> 8); x1 = m0 - b1; x2 = (((block[2 + i] - block[6 + i]) * 362 + 128) >> 8) - b3; x3 = m0 + b1; y3 = x1 + x2; y4 = x3 + b3; y5 = x1 - x2; y6 = x3 - b3; y7 = -x0 - ((b4 * 473 + b6 * 196 + 128) >> 8); block[0 + i] = (b7 + y4 + 128) >> 8; block[1 + i] = (x4 + y3 + 128) >> 8; block[2 + i] = (y5 - x0 + 128) >> 8; block[3 + i] = (y6 - y7 + 128) >> 8; block[4 + i] = (y6 + y7 + 128) >> 8; block[5 + i] = (x0 + y5 + 128) >> 8; block[6 + i] = (y3 - x4 + 128) >> 8; block[7 + i] = (y4 - b7 + 128) >> 8; } } // YCbCr conversion following the BT.601 standard: // https://infogalactic.com/info/YCbCr#ITU-R_BT.601_conversion #define PLM_PUT_PIXEL(RI, GI, BI, Y_OFFSET, DEST_OFFSET) \ y = ((frame->y.data[y_index + Y_OFFSET]-16) * 76309) >> 16; \ dest[d_index + DEST_OFFSET + RI] = plm_clamp(y + r); \ dest[d_index + DEST_OFFSET + GI] = plm_clamp(y - g); \ dest[d_index + DEST_OFFSET + BI] = plm_clamp(y + b); #define PLM_DEFINE_FRAME_CONVERT_FUNCTION(NAME, BYTES_PER_PIXEL, RI, GI, BI) \ void NAME(plm_frame_t *frame, uint8_t *dest, int stride) { \ int cols = frame->width >> 1; \ int rows = frame->height >> 1; \ int yw = frame->y.width; \ int cw = frame->cb.width; \ for (int row = 0; row < rows; row++) { \ int c_index = row * cw; \ int y_index = row * 2 * yw; \ int d_index = row * 2 * stride; \ for (int col = 0; col < cols; col++) { \ int y; \ int cr = frame->cr.data[c_index] - 128; \ int cb = frame->cb.data[c_index] - 128; \ int r = (cr * 104597) >> 16; \ int g = (cb * 25674 + cr * 53278) >> 16; \ int b = (cb * 132201) >> 16; \ PLM_PUT_PIXEL(RI, GI, BI, 0, 0); \ PLM_PUT_PIXEL(RI, GI, BI, 1, BYTES_PER_PIXEL); \ PLM_PUT_PIXEL(RI, GI, BI, yw, stride); \ PLM_PUT_PIXEL(RI, GI, BI, yw + 1, stride + BYTES_PER_PIXEL); \ c_index += 1; \ y_index += 2; \ d_index += 2 * BYTES_PER_PIXEL; \ } \ } \ } PLM_DEFINE_FRAME_CONVERT_FUNCTION(plm_frame_to_rgb, 3, 0, 1, 2) PLM_DEFINE_FRAME_CONVERT_FUNCTION(plm_frame_to_bgr, 3, 2, 1, 0) PLM_DEFINE_FRAME_CONVERT_FUNCTION(plm_frame_to_rgba, 4, 0, 1, 2) PLM_DEFINE_FRAME_CONVERT_FUNCTION(plm_frame_to_bgra, 4, 2, 1, 0) PLM_DEFINE_FRAME_CONVERT_FUNCTION(plm_frame_to_argb, 4, 1, 2, 3) PLM_DEFINE_FRAME_CONVERT_FUNCTION(plm_frame_to_abgr, 4, 3, 2, 1) #undef PLM_PUT_PIXEL #undef PLM_DEFINE_FRAME_CONVERT_FUNCTION // ----------------------------------------------------------------------------- // plm_audio implementation // Based on kjmp2 by Martin J. Fiedler // http://keyj.emphy.de/kjmp2/ static const int PLM_AUDIO_FRAME_SYNC = 0x7ff; static const int PLM_AUDIO_MPEG_2_5 = 0x0; static const int PLM_AUDIO_MPEG_2 = 0x2; static const int PLM_AUDIO_MPEG_1 = 0x3; static const int PLM_AUDIO_LAYER_III = 0x1; static const int PLM_AUDIO_LAYER_II = 0x2; static const int PLM_AUDIO_LAYER_I = 0x3; static const int PLM_AUDIO_MODE_STEREO = 0x0; static const int PLM_AUDIO_MODE_JOINT_STEREO = 0x1; static const int PLM_AUDIO_MODE_DUAL_CHANNEL = 0x2; static const int PLM_AUDIO_MODE_MONO = 0x3; static const unsigned short PLM_AUDIO_SAMPLE_RATE[] = { 44100, 48000, 32000, 0, // MPEG-1 22050, 24000, 16000, 0 // MPEG-2 }; static const short PLM_AUDIO_BIT_RATE[] = { 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384, // MPEG-1 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160 // MPEG-2 }; static const int PLM_AUDIO_SCALEFACTOR_BASE[] = { 0x02000000, 0x01965FEA, 0x01428A30 }; static const float PLM_AUDIO_SYNTHESIS_WINDOW[] = { 0.0, -0.5, -0.5, -0.5, -0.5, -0.5, -0.5, -1.0, -1.0, -1.0, -1.0, -1.5, -1.5, -2.0, -2.0, -2.5, -2.5, -3.0, -3.5, -3.5, -4.0, -4.5, -5.0, -5.5, -6.5, -7.0, -8.0, -8.5, -9.5, -10.5, -12.0, -13.0, -14.5, -15.5, -17.5, -19.0, -20.5, -22.5, -24.5, -26.5, -29.0, -31.5, -34.0, -36.5, -39.5, -42.5, -45.5, -48.5, -52.0, -55.5, -58.5, -62.5, -66.0, -69.5, -73.5, -77.0, -80.5, -84.5, -88.0, -91.5, -95.0, -98.0, -101.0, -104.0, 106.5, 109.0, 111.0, 112.5, 113.5, 114.0, 114.0, 113.5, 112.0, 110.5, 107.5, 104.0, 100.0, 94.5, 88.5, 81.5, 73.0, 63.5, 53.0, 41.5, 28.5, 14.5, -1.0, -18.0, -36.0, -55.5, -76.5, -98.5, -122.0, -147.0, -173.5, -200.5, -229.5, -259.5, -290.5, -322.5, -355.5, -389.5, -424.0, -459.5, -495.5, -532.0, -568.5, -605.0, -641.5, -678.0, -714.0, -749.0, -783.5, -817.0, -849.0, -879.5, -908.5, -935.0, -959.5, -981.0, -1000.5, -1016.0, -1028.5, -1037.5, -1042.5, -1043.5, -1040.0, -1031.5, 1018.5, 1000.0, 976.0, 946.5, 911.0, 869.5, 822.0, 767.5, 707.0, 640.0, 565.5, 485.0, 397.0, 302.5, 201.0, 92.5, -22.5, -144.0, -272.5, -407.0, -547.5, -694.0, -846.0, -1003.0, -1165.0, -1331.5, -1502.0, -1675.5, -1852.5, -2031.5, -2212.5, -2394.0, -2576.5, -2758.5, -2939.5, -3118.5, -3294.5, -3467.5, -3635.5, -3798.5, -3955.0, -4104.5, -4245.5, -4377.5, -4499.0, -4609.5, -4708.0, -4792.5, -4863.5, -4919.0, -4958.0, -4979.5, -4983.0, -4967.5, -4931.5, -4875.0, -4796.0, -4694.5, -4569.5, -4420.0, -4246.0, -4046.0, -3820.0, -3567.0, 3287.0, 2979.5, 2644.0, 2280.5, 1888.0, 1467.5, 1018.5, 541.0, 35.0, -499.0, -1061.0, -1650.0, -2266.5, -2909.0, -3577.0, -4270.0, -4987.5, -5727.5, -6490.0, -7274.0, -8077.5, -8899.5, -9739.0, -10594.5, -11464.5, -12347.0, -13241.0, -14144.5, -15056.0, -15973.5, -16895.5, -17820.0, -18744.5, -19668.0, -20588.0, -21503.0, -22410.5, -23308.5, -24195.0, -25068.5, -25926.5, -26767.0, -27589.0, -28389.0, -29166.5, -29919.0, -30644.5, -31342.0, -32009.5, -32645.0, -33247.0, -33814.5, -34346.0, -34839.5, -35295.0, -35710.0, -36084.5, -36417.5, -36707.5, -36954.0, -37156.5, -37315.0, -37428.0, -37496.0, 37519.0, 37496.0, 37428.0, 37315.0, 37156.5, 36954.0, 36707.5, 36417.5, 36084.5, 35710.0, 35295.0, 34839.5, 34346.0, 33814.5, 33247.0, 32645.0, 32009.5, 31342.0, 30644.5, 29919.0, 29166.5, 28389.0, 27589.0, 26767.0, 25926.5, 25068.5, 24195.0, 23308.5, 22410.5, 21503.0, 20588.0, 19668.0, 18744.5, 17820.0, 16895.5, 15973.5, 15056.0, 14144.5, 13241.0, 12347.0, 11464.5, 10594.5, 9739.0, 8899.5, 8077.5, 7274.0, 6490.0, 5727.5, 4987.5, 4270.0, 3577.0, 2909.0, 2266.5, 1650.0, 1061.0, 499.0, -35.0, -541.0, -1018.5, -1467.5, -1888.0, -2280.5, -2644.0, -2979.5, 3287.0, 3567.0, 3820.0, 4046.0, 4246.0, 4420.0, 4569.5, 4694.5, 4796.0, 4875.0, 4931.5, 4967.5, 4983.0, 4979.5, 4958.0, 4919.0, 4863.5, 4792.5, 4708.0, 4609.5, 4499.0, 4377.5, 4245.5, 4104.5, 3955.0, 3798.5, 3635.5, 3467.5, 3294.5, 3118.5, 2939.5, 2758.5, 2576.5, 2394.0, 2212.5, 2031.5, 1852.5, 1675.5, 1502.0, 1331.5, 1165.0, 1003.0, 846.0, 694.0, 547.5, 407.0, 272.5, 144.0, 22.5, -92.5, -201.0, -302.5, -397.0, -485.0, -565.5, -640.0, -707.0, -767.5, -822.0, -869.5, -911.0, -946.5, -976.0, -1000.0, 1018.5, 1031.5, 1040.0, 1043.5, 1042.5, 1037.5, 1028.5, 1016.0, 1000.5, 981.0, 959.5, 935.0, 908.5, 879.5, 849.0, 817.0, 783.5, 749.0, 714.0, 678.0, 641.5, 605.0, 568.5, 532.0, 495.5, 459.5, 424.0, 389.5, 355.5, 322.5, 290.5, 259.5, 229.5, 200.5, 173.5, 147.0, 122.0, 98.5, 76.5, 55.5, 36.0, 18.0, 1.0, -14.5, -28.5, -41.5, -53.0, -63.5, -73.0, -81.5, -88.5, -94.5, -100.0, -104.0, -107.5, -110.5, -112.0, -113.5, -114.0, -114.0, -113.5, -112.5, -111.0, -109.0, 106.5, 104.0, 101.0, 98.0, 95.0, 91.5, 88.0, 84.5, 80.5, 77.0, 73.5, 69.5, 66.0, 62.5, 58.5, 55.5, 52.0, 48.5, 45.5, 42.5, 39.5, 36.5, 34.0, 31.5, 29.0, 26.5, 24.5, 22.5, 20.5, 19.0, 17.5, 15.5, 14.5, 13.0, 12.0, 10.5, 9.5, 8.5, 8.0, 7.0, 6.5, 5.5, 5.0, 4.5, 4.0, 3.5, 3.5, 3.0, 2.5, 2.5, 2.0, 2.0, 1.5, 1.5, 1.0, 1.0, 1.0, 1.0, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5 }; // Quantizer lookup, step 1: bitrate classes static const uint8_t PLM_AUDIO_QUANT_LUT_STEP_1[2][16] = { // 32, 48, 56, 64, 80, 96,112,128,160,192,224,256,320,384 <- bitrate { 0, 0, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2 }, // mono // 16, 24, 28, 32, 40, 48, 56, 64, 80, 96,112,128,160,192 <- bitrate / chan { 0, 0, 0, 0, 0, 0, 1, 1, 1, 2, 2, 2, 2, 2 } // stereo }; // Quantizer lookup, step 2: bitrate class, sample rate -> B2 table idx, sblimit enum { PLM_AUDIO_QUANT_TAB_A = (27 | 64) }; // Table 3-B.2a: high-rate, sblimit = 27 enum { PLM_AUDIO_QUANT_TAB_B = (30 | 64) }; // Table 3-B.2b: high-rate, sblimit = 30 enum { PLM_AUDIO_QUANT_TAB_C = 8 }; // Table 3-B.2c: low-rate, sblimit = 8 enum { PLM_AUDIO_QUANT_TAB_D = 12 }; // Table 3-B.2d: low-rate, sblimit = 12 static const uint8_t QUANT_LUT_STEP_2[3][3] = { //44.1 kHz, 48 kHz, 32 kHz { PLM_AUDIO_QUANT_TAB_C, PLM_AUDIO_QUANT_TAB_C, PLM_AUDIO_QUANT_TAB_D }, // 32 - 48 kbit/sec/ch { PLM_AUDIO_QUANT_TAB_A, PLM_AUDIO_QUANT_TAB_A, PLM_AUDIO_QUANT_TAB_A }, // 56 - 80 kbit/sec/ch { PLM_AUDIO_QUANT_TAB_B, PLM_AUDIO_QUANT_TAB_A, PLM_AUDIO_QUANT_TAB_B } // 96+ kbit/sec/ch }; // Quantizer lookup, step 3: B2 table, subband -> nbal, row index // (upper 4 bits: nbal, lower 4 bits: row index) static const uint8_t PLM_AUDIO_QUANT_LUT_STEP_3[3][32] = { // Low-rate table (3-B.2c and 3-B.2d) { 0x44,0x44, 0x34,0x34,0x34,0x34,0x34,0x34,0x34,0x34,0x34,0x34 }, // High-rate table (3-B.2a and 3-B.2b) { 0x43,0x43,0x43, 0x42,0x42,0x42,0x42,0x42,0x42,0x42,0x42, 0x31,0x31,0x31,0x31,0x31,0x31,0x31,0x31,0x31,0x31,0x31,0x31, 0x20,0x20,0x20,0x20,0x20,0x20,0x20 }, // MPEG-2 LSR table (B.2 in ISO 13818-3) { 0x45,0x45,0x45,0x45, 0x34,0x34,0x34,0x34,0x34,0x34,0x34, 0x24,0x24,0x24,0x24,0x24,0x24,0x24,0x24,0x24,0x24, 0x24,0x24,0x24,0x24,0x24,0x24,0x24,0x24,0x24 } }; // Quantizer lookup, step 4: table row, allocation[] value -> quant table index static const uint8_t PLM_AUDIO_QUANT_LUT_STEP4[6][16] = { { 0, 1, 2, 17 }, { 0, 1, 2, 3, 4, 5, 6, 17 }, { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 17 }, { 0, 1, 3, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17 }, { 0, 1, 2, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 17 }, { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 } }; typedef struct plm_quantizer_spec_t { unsigned short levels; unsigned char group; unsigned char bits; } plm_quantizer_spec_t; static const plm_quantizer_spec_t PLM_AUDIO_QUANT_TAB[] = { { 3, 1, 5 }, // 1 { 5, 1, 7 }, // 2 { 7, 0, 3 }, // 3 { 9, 1, 10 }, // 4 { 15, 0, 4 }, // 5 { 31, 0, 5 }, // 6 { 63, 0, 6 }, // 7 { 127, 0, 7 }, // 8 { 255, 0, 8 }, // 9 { 511, 0, 9 }, // 10 { 1023, 0, 10 }, // 11 { 2047, 0, 11 }, // 12 { 4095, 0, 12 }, // 13 { 8191, 0, 13 }, // 14 { 16383, 0, 14 }, // 15 { 32767, 0, 15 }, // 16 { 65535, 0, 16 } // 17 }; typedef struct plm_audio_t { double time; int samples_decoded; int samplerate_index; int bitrate_index; int version; int layer; int mode; int bound; int v_pos; int next_frame_data_size; int has_header; plm_buffer_t *buffer; int destroy_buffer_when_done; const plm_quantizer_spec_t *allocation[2][32]; uint8_t scale_factor_info[2][32]; int scale_factor[2][32][3]; int sample[2][32][3]; plm_samples_t samples; float D[1024]; float V[2][1024]; float U[32]; } plm_audio_t; int plm_audio_find_frame_sync(plm_audio_t *self); int plm_audio_decode_header(plm_audio_t *self); void plm_audio_decode_frame(plm_audio_t *self); const plm_quantizer_spec_t *plm_audio_read_allocation(plm_audio_t *self, int sb, int tab3); void plm_audio_read_samples(plm_audio_t *self, int ch, int sb, int part); void plm_audio_matrix_transform(int s[32][3], int ss, float *d, int dp); plm_audio_t *plm_audio_create_with_buffer(plm_buffer_t *buffer, int destroy_when_done) { plm_audio_t *self = (plm_audio_t *)malloc(sizeof(plm_audio_t)); memset(self, 0, sizeof(plm_audio_t)); self->samples.count = PLM_AUDIO_SAMPLES_PER_FRAME; self->buffer = buffer; self->destroy_buffer_when_done = destroy_when_done; self->samplerate_index = 3; // Indicates 0 memcpy(self->D, PLM_AUDIO_SYNTHESIS_WINDOW, 512 * sizeof(float)); memcpy(self->D + 512, PLM_AUDIO_SYNTHESIS_WINDOW, 512 * sizeof(float)); // Attempt to decode first header self->next_frame_data_size = plm_audio_decode_header(self); return self; } void plm_audio_destroy(plm_audio_t *self) { if (self->destroy_buffer_when_done) { plm_buffer_destroy(self->buffer); } free(self); } int plm_audio_has_header(plm_audio_t *self) { if (self->has_header) { return TRUE; } self->next_frame_data_size = plm_audio_decode_header(self); return self->has_header; } int plm_audio_get_samplerate(plm_audio_t *self) { return plm_audio_has_header(self) ? PLM_AUDIO_SAMPLE_RATE[self->samplerate_index] : 0; } double plm_audio_get_time(plm_audio_t *self) { return self->time; } void plm_audio_set_time(plm_audio_t *self, double time) { self->samples_decoded = time * (double)PLM_AUDIO_SAMPLE_RATE[self->samplerate_index]; self->time = time; } void plm_audio_rewind(plm_audio_t *self) { plm_buffer_rewind(self->buffer); self->time = 0; self->samples_decoded = 0; self->next_frame_data_size = 0; } int plm_audio_has_ended(plm_audio_t *self) { return plm_buffer_has_ended(self->buffer); } plm_samples_t *plm_audio_decode(plm_audio_t *self) { // Do we have at least enough information to decode the frame header? if (!self->next_frame_data_size) { if (!plm_buffer_has(self->buffer, 48)) { return NULL; } self->next_frame_data_size = plm_audio_decode_header(self); } if ( self->next_frame_data_size == 0 || !plm_buffer_has(self->buffer, self->next_frame_data_size << 3) ) { return NULL; } plm_audio_decode_frame(self); self->next_frame_data_size = 0; self->samples.time = self->time; self->samples_decoded += PLM_AUDIO_SAMPLES_PER_FRAME; self->time = (double)self->samples_decoded / (double)PLM_AUDIO_SAMPLE_RATE[self->samplerate_index]; return &self->samples; } int plm_audio_find_frame_sync(plm_audio_t *self) { size_t i; for (i = self->buffer->bit_index >> 3; i < self->buffer->length-1; i++) { if ( self->buffer->bytes[i] == 0xFF && (self->buffer->bytes[i+1] & 0xFE) == 0xFC ) { self->buffer->bit_index = ((i+1) << 3) + 3; return TRUE; } } self->buffer->bit_index = (i + 1) << 3; return FALSE; } int plm_audio_decode_header(plm_audio_t *self) { if (!plm_buffer_has(self->buffer, 48)) { return 0; } plm_buffer_skip_bytes(self->buffer, 0x00); int sync = plm_buffer_read(self->buffer, 11); // Attempt to resync if no syncword was found. This sucks balls. The MP2 // stream contains a syncword just before every frame (11 bits set to 1). // However, this syncword is not guaranteed to not occur elswhere in the // stream. So, if we have to resync, we also have to check if the header // (samplerate, bitrate) differs from the one we had before. This all // may still lead to garbage data being decoded :/ if (sync != PLM_AUDIO_FRAME_SYNC && !plm_audio_find_frame_sync(self)) { return 0; } self->version = plm_buffer_read(self->buffer, 2); self->layer = plm_buffer_read(self->buffer, 2); int hasCRC = !plm_buffer_read(self->buffer, 1); if ( self->version != PLM_AUDIO_MPEG_1 || self->layer != PLM_AUDIO_LAYER_II ) { return 0; } int bitrate_index = plm_buffer_read(self->buffer, 4) - 1; if (bitrate_index > 13) { return 0; } int samplerate_index = plm_buffer_read(self->buffer, 2); if (samplerate_index == 3) { return 0; } int padding = plm_buffer_read(self->buffer, 1); plm_buffer_skip(self->buffer, 1); // f_private int mode = plm_buffer_read(self->buffer, 2); // If we already have a header, make sure the samplerate, bitrate and mode // are still the same, otherwise we might have missed sync. if ( self->has_header && ( self->bitrate_index != bitrate_index || self->samplerate_index != samplerate_index || self->mode != mode ) ) { return 0; } self->bitrate_index = bitrate_index; self->samplerate_index = samplerate_index; self->mode = mode; self->has_header = TRUE; // Parse the mode_extension, set up the stereo bound if (mode == PLM_AUDIO_MODE_JOINT_STEREO) { self->bound = (plm_buffer_read(self->buffer, 2) + 1) << 2; } else { plm_buffer_skip(self->buffer, 2); self->bound = (mode == PLM_AUDIO_MODE_MONO) ? 0 : 32; } // Discard the last 4 bits of the header and the CRC value, if present plm_buffer_skip(self->buffer, 4); if (hasCRC) { plm_buffer_skip(self->buffer, 16); } // Compute frame size, check if we have enough data to decode the whole // frame. int bitrate = PLM_AUDIO_BIT_RATE[self->bitrate_index]; int samplerate = PLM_AUDIO_SAMPLE_RATE[self->samplerate_index]; int frame_size = (144000 * bitrate / samplerate) + padding; return frame_size - (hasCRC ? 6 : 4); } void plm_audio_decode_frame(plm_audio_t *self) { // Prepare the quantizer table lookups int tab3 = 0; int sblimit = 0; int tab1 = (self->mode == PLM_AUDIO_MODE_MONO) ? 0 : 1; int tab2 = PLM_AUDIO_QUANT_LUT_STEP_1[tab1][self->bitrate_index]; tab3 = QUANT_LUT_STEP_2[tab2][self->samplerate_index]; sblimit = tab3 & 63; tab3 >>= 6; if (self->bound > sblimit) { self->bound = sblimit; } // Read the allocation information for (int sb = 0; sb < self->bound; sb++) { self->allocation[0][sb] = plm_audio_read_allocation(self, sb, tab3); self->allocation[1][sb] = plm_audio_read_allocation(self, sb, tab3); } for (int sb = self->bound; sb < sblimit; sb++) { self->allocation[0][sb] = self->allocation[1][sb] = plm_audio_read_allocation(self, sb, tab3); } // Read scale factor selector information int channels = (self->mode == PLM_AUDIO_MODE_MONO) ? 1 : 2; for (int sb = 0; sb < sblimit; sb++) { for (int ch = 0; ch < channels; ch++) { if (self->allocation[ch][sb]) { self->scale_factor_info[ch][sb] = plm_buffer_read(self->buffer, 2); } } if (self->mode == PLM_AUDIO_MODE_MONO) { self->scale_factor_info[1][sb] = self->scale_factor_info[0][sb]; } } // Read scale factors for (int sb = 0; sb < sblimit; sb++) { for (int ch = 0; ch < channels; ch++) { if (self->allocation[ch][sb]) { int *sf = self->scale_factor[ch][sb]; switch (self->scale_factor_info[ch][sb]) { case 0: sf[0] = plm_buffer_read(self->buffer, 6); sf[1] = plm_buffer_read(self->buffer, 6); sf[2] = plm_buffer_read(self->buffer, 6); break; case 1: sf[0] = sf[1] = plm_buffer_read(self->buffer, 6); sf[2] = plm_buffer_read(self->buffer, 6); break; case 2: sf[0] = sf[1] = sf[2] = plm_buffer_read(self->buffer, 6); break; case 3: sf[0] = plm_buffer_read(self->buffer, 6); sf[1] = sf[2] = plm_buffer_read(self->buffer, 6); break; } } } if (self->mode == PLM_AUDIO_MODE_MONO) { self->scale_factor[1][sb][0] = self->scale_factor[0][sb][0]; self->scale_factor[1][sb][1] = self->scale_factor[0][sb][1]; self->scale_factor[1][sb][2] = self->scale_factor[0][sb][2]; } } // Coefficient input and reconstruction int out_pos = 0; for (int part = 0; part < 3; part++) { for (int granule = 0; granule < 4; granule++) { // Read the samples for (int sb = 0; sb < self->bound; sb++) { plm_audio_read_samples(self, 0, sb, part); plm_audio_read_samples(self, 1, sb, part); } for (int sb = self->bound; sb < sblimit; sb++) { plm_audio_read_samples(self, 0, sb, part); self->sample[1][sb][0] = self->sample[0][sb][0]; self->sample[1][sb][1] = self->sample[0][sb][1]; self->sample[1][sb][2] = self->sample[0][sb][2]; } for (int sb = sblimit; sb < 32; sb++) { self->sample[0][sb][0] = 0; self->sample[0][sb][1] = 0; self->sample[0][sb][2] = 0; self->sample[1][sb][0] = 0; self->sample[1][sb][1] = 0; self->sample[1][sb][2] = 0; } // Synthesis loop for (int p = 0; p < 3; p++) { // Shifting step self->v_pos = (self->v_pos - 64) & 1023; for (int ch = 0; ch < 2; ch++) { plm_audio_matrix_transform(self->sample[ch], p, self->V[ch], self->v_pos); // Build U, windowing, calculate output memset(self->U, 0, sizeof(self->U)); int d_index = 512 - (self->v_pos >> 1); int v_index = (self->v_pos % 128) >> 1; while (v_index < 1024) { for (int i = 0; i < 32; ++i) { self->U[i] += self->D[d_index++] * self->V[ch][v_index++]; } v_index += 128 - 32; d_index += 64 - 32; } d_index -= (512 - 32); v_index = (128 - 32 + 1024) - v_index; while (v_index < 1024) { for (int i = 0; i < 32; ++i) { self->U[i] += self->D[d_index++] * self->V[ch][v_index++]; } v_index += 128 - 32; d_index += 64 - 32; } // Output samples #ifdef PLM_AUDIO_SEPARATE_CHANNELS float *out_channel = ch == 0 ? self->samples.left : self->samples.right; for (int j = 0; j < 32; j++) { out_channel[out_pos + j] = self->U[j] / 2147418112.0f; } #else for (int j = 0; j < 32; j++) { self->samples.interleaved[((out_pos + j) << 1) + ch] = self->U[j] / 2147418112.0f; } #endif } // End of synthesis channel loop out_pos += 32; } // End of synthesis sub-block loop } // Decoding of the granule finished } plm_buffer_align(self->buffer); } const plm_quantizer_spec_t *plm_audio_read_allocation(plm_audio_t *self, int sb, int tab3) { int tab4 = PLM_AUDIO_QUANT_LUT_STEP_3[tab3][sb]; int qtab = PLM_AUDIO_QUANT_LUT_STEP4[tab4 & 15][plm_buffer_read(self->buffer, tab4 >> 4)]; return qtab ? (&PLM_AUDIO_QUANT_TAB[qtab - 1]) : 0; } void plm_audio_read_samples(plm_audio_t *self, int ch, int sb, int part) { const plm_quantizer_spec_t *q = self->allocation[ch][sb]; int sf = self->scale_factor[ch][sb][part]; int *sample = self->sample[ch][sb]; int val = 0; if (!q) { // No bits allocated for this subband sample[0] = sample[1] = sample[2] = 0; return; } // Resolve scalefactor if (sf == 63) { sf = 0; } else { int shift = (sf / 3) | 0; sf = (PLM_AUDIO_SCALEFACTOR_BASE[sf % 3] + ((1 << shift) >> 1)) >> shift; } // Decode samples int adj = q->levels; if (q->group) { // Decode grouped samples val = plm_buffer_read(self->buffer, q->bits); sample[0] = val % adj; val /= adj; sample[1] = val % adj; sample[2] = val / adj; } else { // Decode direct samples sample[0] = plm_buffer_read(self->buffer, q->bits); sample[1] = plm_buffer_read(self->buffer, q->bits); sample[2] = plm_buffer_read(self->buffer, q->bits); } // Postmultiply samples int scale = 65536 / (adj + 1); adj = ((adj + 1) >> 1) - 1; val = (adj - sample[0]) * scale; sample[0] = (val * (sf >> 12) + ((val * (sf & 4095) + 2048) >> 12)) >> 12; val = (adj - sample[1]) * scale; sample[1] = (val * (sf >> 12) + ((val * (sf & 4095) + 2048) >> 12)) >> 12; val = (adj - sample[2]) * scale; sample[2] = (val * (sf >> 12) + ((val * (sf & 4095) + 2048) >> 12)) >> 12; } void plm_audio_matrix_transform(int s[32][3], int ss, float *d, int dp) { float t01, t02, t03, t04, t05, t06, t07, t08, t09, t10, t11, t12, t13, t14, t15, t16, t17, t18, t19, t20, t21, t22, t23, t24, t25, t26, t27, t28, t29, t30, t31, t32, t33; t01 = (float)(s[0][ss] + s[31][ss]); t02 = (float)(s[0][ss] - s[31][ss]) * 0.500602998235f; t03 = (float)(s[1][ss] + s[30][ss]); t04 = (float)(s[1][ss] - s[30][ss]) * 0.505470959898f; t05 = (float)(s[2][ss] + s[29][ss]); t06 = (float)(s[2][ss] - s[29][ss]) * 0.515447309923f; t07 = (float)(s[3][ss] + s[28][ss]); t08 = (float)(s[3][ss] - s[28][ss]) * 0.53104259109f; t09 = (float)(s[4][ss] + s[27][ss]); t10 = (float)(s[4][ss] - s[27][ss]) * 0.553103896034f; t11 = (float)(s[5][ss] + s[26][ss]); t12 = (float)(s[5][ss] - s[26][ss]) * 0.582934968206f; t13 = (float)(s[6][ss] + s[25][ss]); t14 = (float)(s[6][ss] - s[25][ss]) * 0.622504123036f; t15 = (float)(s[7][ss] + s[24][ss]); t16 = (float)(s[7][ss] - s[24][ss]) * 0.674808341455f; t17 = (float)(s[8][ss] + s[23][ss]); t18 = (float)(s[8][ss] - s[23][ss]) * 0.744536271002f; t19 = (float)(s[9][ss] + s[22][ss]); t20 = (float)(s[9][ss] - s[22][ss]) * 0.839349645416f; t21 = (float)(s[10][ss] + s[21][ss]); t22 = (float)(s[10][ss] - s[21][ss]) * 0.972568237862f; t23 = (float)(s[11][ss] + s[20][ss]); t24 = (float)(s[11][ss] - s[20][ss]) * 1.16943993343f; t25 = (float)(s[12][ss] + s[19][ss]); t26 = (float)(s[12][ss] - s[19][ss]) * 1.48416461631f; t27 = (float)(s[13][ss] + s[18][ss]); t28 = (float)(s[13][ss] - s[18][ss]) * 2.05778100995f; t29 = (float)(s[14][ss] + s[17][ss]); t30 = (float)(s[14][ss] - s[17][ss]) * 3.40760841847f; t31 = (float)(s[15][ss] + s[16][ss]); t32 = (float)(s[15][ss] - s[16][ss]) * 10.1900081235f; t33 = t01 + t31; t31 = (t01 - t31) * 0.502419286188f; t01 = t03 + t29; t29 = (t03 - t29) * 0.52249861494f; t03 = t05 + t27; t27 = (t05 - t27) * 0.566944034816f; t05 = t07 + t25; t25 = (t07 - t25) * 0.64682178336f; t07 = t09 + t23; t23 = (t09 - t23) * 0.788154623451f; t09 = t11 + t21; t21 = (t11 - t21) * 1.06067768599f; t11 = t13 + t19; t19 = (t13 - t19) * 1.72244709824f; t13 = t15 + t17; t17 = (t15 - t17) * 5.10114861869f; t15 = t33 + t13; t13 = (t33 - t13) * 0.509795579104f; t33 = t01 + t11; t01 = (t01 - t11) * 0.601344886935f; t11 = t03 + t09; t09 = (t03 - t09) * 0.899976223136f; t03 = t05 + t07; t07 = (t05 - t07) * 2.56291544774f; t05 = t15 + t03; t15 = (t15 - t03) * 0.541196100146f; t03 = t33 + t11; t11 = (t33 - t11) * 1.30656296488f; t33 = t05 + t03; t05 = (t05 - t03) * 0.707106781187f; t03 = t15 + t11; t15 = (t15 - t11) * 0.707106781187f; t03 += t15; t11 = t13 + t07; t13 = (t13 - t07) * 0.541196100146f; t07 = t01 + t09; t09 = (t01 - t09) * 1.30656296488f; t01 = t11 + t07; t07 = (t11 - t07) * 0.707106781187f; t11 = t13 + t09; t13 = (t13 - t09) * 0.707106781187f; t11 += t13; t01 += t11; t11 += t07; t07 += t13; t09 = t31 + t17; t31 = (t31 - t17) * 0.509795579104f; t17 = t29 + t19; t29 = (t29 - t19) * 0.601344886935f; t19 = t27 + t21; t21 = (t27 - t21) * 0.899976223136f; t27 = t25 + t23; t23 = (t25 - t23) * 2.56291544774f; t25 = t09 + t27; t09 = (t09 - t27) * 0.541196100146f; t27 = t17 + t19; t19 = (t17 - t19) * 1.30656296488f; t17 = t25 + t27; t27 = (t25 - t27) * 0.707106781187f; t25 = t09 + t19; t19 = (t09 - t19) * 0.707106781187f; t25 += t19; t09 = t31 + t23; t31 = (t31 - t23) * 0.541196100146f; t23 = t29 + t21; t21 = (t29 - t21) * 1.30656296488f; t29 = t09 + t23; t23 = (t09 - t23) * 0.707106781187f; t09 = t31 + t21; t31 = (t31 - t21) * 0.707106781187f; t09 += t31; t29 += t09; t09 += t23; t23 += t31; t17 += t29; t29 += t25; t25 += t09; t09 += t27; t27 += t23; t23 += t19; t19 += t31; t21 = t02 + t32; t02 = (t02 - t32) * 0.502419286188f; t32 = t04 + t30; t04 = (t04 - t30) * 0.52249861494f; t30 = t06 + t28; t28 = (t06 - t28) * 0.566944034816f; t06 = t08 + t26; t08 = (t08 - t26) * 0.64682178336f; t26 = t10 + t24; t10 = (t10 - t24) * 0.788154623451f; t24 = t12 + t22; t22 = (t12 - t22) * 1.06067768599f; t12 = t14 + t20; t20 = (t14 - t20) * 1.72244709824f; t14 = t16 + t18; t16 = (t16 - t18) * 5.10114861869f; t18 = t21 + t14; t14 = (t21 - t14) * 0.509795579104f; t21 = t32 + t12; t32 = (t32 - t12) * 0.601344886935f; t12 = t30 + t24; t24 = (t30 - t24) * 0.899976223136f; t30 = t06 + t26; t26 = (t06 - t26) * 2.56291544774f; t06 = t18 + t30; t18 = (t18 - t30) * 0.541196100146f; t30 = t21 + t12; t12 = (t21 - t12) * 1.30656296488f; t21 = t06 + t30; t30 = (t06 - t30) * 0.707106781187f; t06 = t18 + t12; t12 = (t18 - t12) * 0.707106781187f; t06 += t12; t18 = t14 + t26; t26 = (t14 - t26) * 0.541196100146f; t14 = t32 + t24; t24 = (t32 - t24) * 1.30656296488f; t32 = t18 + t14; t14 = (t18 - t14) * 0.707106781187f; t18 = t26 + t24; t24 = (t26 - t24) * 0.707106781187f; t18 += t24; t32 += t18; t18 += t14; t26 = t14 + t24; t14 = t02 + t16; t02 = (t02 - t16) * 0.509795579104f; t16 = t04 + t20; t04 = (t04 - t20) * 0.601344886935f; t20 = t28 + t22; t22 = (t28 - t22) * 0.899976223136f; t28 = t08 + t10; t10 = (t08 - t10) * 2.56291544774f; t08 = t14 + t28; t14 = (t14 - t28) * 0.541196100146f; t28 = t16 + t20; t20 = (t16 - t20) * 1.30656296488f; t16 = t08 + t28; t28 = (t08 - t28) * 0.707106781187f; t08 = t14 + t20; t20 = (t14 - t20) * 0.707106781187f; t08 += t20; t14 = t02 + t10; t02 = (t02 - t10) * 0.541196100146f; t10 = t04 + t22; t22 = (t04 - t22) * 1.30656296488f; t04 = t14 + t10; t10 = (t14 - t10) * 0.707106781187f; t14 = t02 + t22; t02 = (t02 - t22) * 0.707106781187f; t14 += t02; t04 += t14; t14 += t10; t10 += t02; t16 += t04; t04 += t08; t08 += t14; t14 += t28; t28 += t10; t10 += t20; t20 += t02; t21 += t16; t16 += t32; t32 += t04; t04 += t06; t06 += t08; t08 += t18; t18 += t14; t14 += t30; t30 += t28; t28 += t26; t26 += t10; t10 += t12; t12 += t20; t20 += t24; t24 += t02; d[dp + 48] = -t33; d[dp + 49] = d[dp + 47] = -t21; d[dp + 50] = d[dp + 46] = -t17; d[dp + 51] = d[dp + 45] = -t16; d[dp + 52] = d[dp + 44] = -t01; d[dp + 53] = d[dp + 43] = -t32; d[dp + 54] = d[dp + 42] = -t29; d[dp + 55] = d[dp + 41] = -t04; d[dp + 56] = d[dp + 40] = -t03; d[dp + 57] = d[dp + 39] = -t06; d[dp + 58] = d[dp + 38] = -t25; d[dp + 59] = d[dp + 37] = -t08; d[dp + 60] = d[dp + 36] = -t11; d[dp + 61] = d[dp + 35] = -t18; d[dp + 62] = d[dp + 34] = -t09; d[dp + 63] = d[dp + 33] = -t14; d[dp + 32] = -t05; d[dp + 0] = t05; d[dp + 31] = -t30; d[dp + 1] = t30; d[dp + 30] = -t27; d[dp + 2] = t27; d[dp + 29] = -t28; d[dp + 3] = t28; d[dp + 28] = -t07; d[dp + 4] = t07; d[dp + 27] = -t26; d[dp + 5] = t26; d[dp + 26] = -t23; d[dp + 6] = t23; d[dp + 25] = -t10; d[dp + 7] = t10; d[dp + 24] = -t15; d[dp + 8] = t15; d[dp + 23] = -t12; d[dp + 9] = t12; d[dp + 22] = -t19; d[dp + 10] = t19; d[dp + 21] = -t20; d[dp + 11] = t20; d[dp + 20] = -t13; d[dp + 12] = t13; d[dp + 19] = -t24; d[dp + 13] = t24; d[dp + 18] = -t31; d[dp + 14] = t31; d[dp + 17] = -t02; d[dp + 15] = t02; d[dp + 16] = 0.0; } #endif // PL_MPEG_IMPLEMENTATION #line 0 #line 1 "3rd_jo_mpeg.h" /* public domain Simple, Minimalistic, No Allocations MPEG writer - http://jonolick.com * * Latest revisions: * 1.02c rgbx -> bgrx channel swap && vertical image flip && userdef components (@r-lyeh) * 1.02 (22-03-2017) Fixed AC encoding bug. * Fixed color space bug (thx r- lyeh!) * 1.01 (18-10-2016) warning fixes * 1.00 (25-09-2016) initial release * * Basic usage: * char *frame = new char[width*height*4]; // 4 component. bgrx format, where X is unused * FILE *fp = fopen("foo.mpg", "wb"); * jo_write_mpeg(fp, frame, width, height, 60); // frame 0 * jo_write_mpeg(fp, frame, width, height, 60); // frame 1 * jo_write_mpeg(fp, frame, width, height, 60); // frame 2 * ... * fclose(fp); * * Notes: * Only supports 24, 25, 30, 50, or 60 fps * * I don't know if decoders support changing of fps, or dimensions for each frame. * Movie players *should* support it as the spec allows it, but ... * * MPEG-1/2 currently has no active patents as far as I am aware. * * http://dvd.sourceforge.net/dvdinfo/mpeghdrs.html * http://www.cs.cornell.edu/dali/api/mpegvideo-c.html * */ #ifndef JO_INCLUDE_MPEG_H #define JO_INCLUDE_MPEG_H #include // To get a header file for this, either cut and paste the header, // or create jo_mpeg.h, #define JO_MPEG_HEADER_FILE_ONLY, and // then include jo_mpeg.c from it. // Returns false on failure extern void jo_write_mpeg(FILE *fp, const unsigned char *bgrx, int width, int height, int fps); #endif // JO_INCLUDE_MPEG_H #ifndef JO_MPEG_HEADER_FILE_ONLY #ifndef JO_MPEG_COMPONENTS #define JO_MPEG_COMPONENTS 4 #endif #include #include #include // Huffman tables static const unsigned char s_jo_HTDC_Y[9][2] = {{4,3}, {0,2}, {1,2}, {5,3}, {6,3}, {14,4}, {30,5}, {62,6}, {126,7}}; static const unsigned char s_jo_HTDC_C[9][2] = {{0,2}, {1,2}, {2,2}, {6,3}, {14,4}, {30,5}, {62,6}, {126,7}, {254,8}}; static const unsigned char s_jo_HTAC[32][40][2] = { {{6,3},{8,5},{10,6},{12,8},{76,9},{66,9},{20,11},{58,13},{48,13},{38,13},{32,13},{52,14},{50,14},{48,14},{46,14},{62,15},{62,15},{58,15},{56,15},{54,15},{52,15},{50,15},{48,15},{46,15},{44,15},{42,15},{40,15},{38,15},{36,15},{34,15},{32,15},{48,16},{46,16},{44,16},{42,16},{40,16},{38,16},{36,16},{34,16},{32,16},}, {{6,4},{12,7},{74,9},{24,11},{54,13},{44,14},{42,14},{62,16},{60,16},{58,16},{56,16},{54,16},{52,16},{50,16},{38,17},{36,17},{34,17},{32,17}}, {{10,5},{8,8},{22,11},{40,13},{40,14}}, {{14,6},{72,9},{56,13},{38,14}}, {{12,6},{30,11},{36,13}}, {{14,7},{18,11},{36,14}}, {{10,7},{60,13},{40,17}}, {{8,7},{42,13}}, {{14,8},{34,13}}, {{10,8},{34,14}}, {{78,9},{32,14}}, {{70,9},{52,17}}, {{68,9},{50,17}}, {{64,9},{48,17}}, {{28,11},{46,17}}, {{26,11},{44,17}}, {{16,11},{42,17}}, {{62,13}}, {{52,13}}, {{50,13}}, {{46,13}}, {{44,13}}, {{62,14}}, {{60,14}}, {{58,14}}, {{56,14}}, {{54,14}}, {{62,17}}, {{60,17}}, {{58,17}}, {{56,17}}, {{54,17}}, }; static const float s_jo_quantTbl[64] = { 0.015625f,0.005632f,0.005035f,0.004832f,0.004808f,0.005892f,0.007964f,0.013325f, 0.005632f,0.004061f,0.003135f,0.003193f,0.003338f,0.003955f,0.004898f,0.008828f, 0.005035f,0.003135f,0.002816f,0.003013f,0.003299f,0.003581f,0.005199f,0.009125f, 0.004832f,0.003484f,0.003129f,0.003348f,0.003666f,0.003979f,0.005309f,0.009632f, 0.005682f,0.003466f,0.003543f,0.003666f,0.003906f,0.004546f,0.005774f,0.009439f, 0.006119f,0.004248f,0.004199f,0.004228f,0.004546f,0.005062f,0.006124f,0.009942f, 0.008883f,0.006167f,0.006096f,0.005777f,0.006078f,0.006391f,0.007621f,0.012133f, 0.016780f,0.011263f,0.009907f,0.010139f,0.009849f,0.010297f,0.012133f,0.019785f, }; static const unsigned char s_jo_ZigZag[] = { 0,1,5,6,14,15,27,28,2,4,7,13,16,26,29,42,3,8,12,17,25,30,41,43,9,11,18,24,31,40,44,53,10,19,23,32,39,45,52,54,20,22,33,38,46,51,55,60,21,34,37,47,50,56,59,61,35,36,48,49,57,58,62,63 }; typedef struct { FILE *fp; int buf, cnt; } jo_bits_t; static void jo_writeBits(jo_bits_t *b, int value, int count) { b->cnt += count; b->buf |= value << (24 - b->cnt); while(b->cnt >= 8) { unsigned char c = (b->buf >> 16) & 255; putc(c, b->fp); b->buf <<= 8; b->cnt -= 8; } } static void jo_DCT(float *d0, float *d1, float *d2, float *d3, float *d4, float *d5, float *d6, float *d7) { float tmp0 = *d0 + *d7; float tmp7 = *d0 - *d7; float tmp1 = *d1 + *d6; float tmp6 = *d1 - *d6; float tmp2 = *d2 + *d5; float tmp5 = *d2 - *d5; float tmp3 = *d3 + *d4; float tmp4 = *d3 - *d4; // Even part float tmp10 = tmp0 + tmp3; // phase 2 float tmp13 = tmp0 - tmp3; float tmp11 = tmp1 + tmp2; float tmp12 = tmp1 - tmp2; *d0 = tmp10 + tmp11; // phase 3 *d4 = tmp10 - tmp11; float z1 = (tmp12 + tmp13) * 0.707106781f; // c4 *d2 = tmp13 + z1; // phase 5 *d6 = tmp13 - z1; // Odd part tmp10 = tmp4 + tmp5; // phase 2 tmp11 = tmp5 + tmp6; tmp12 = tmp6 + tmp7; // The rotator is modified from fig 4-8 to avoid extra negations. float z5 = (tmp10 - tmp12) * 0.382683433f; // c6 float z2 = tmp10 * 0.541196100f + z5; // c2-c6 float z4 = tmp12 * 1.306562965f + z5; // c2+c6 float z3 = tmp11 * 0.707106781f; // c4 float z11 = tmp7 + z3; // phase 5 float z13 = tmp7 - z3; *d5 = z13 + z2; // phase 6 *d3 = z13 - z2; *d1 = z11 + z4; *d7 = z11 - z4; } static int jo_processDU(jo_bits_t *bits, float A[64], const unsigned char htdc[9][2], int DC) { for(int dataOff=0; dataOff<64; dataOff+=8) { jo_DCT(&A[dataOff], &A[dataOff+1], &A[dataOff+2], &A[dataOff+3], &A[dataOff+4], &A[dataOff+5], &A[dataOff+6], &A[dataOff+7]); } for(int dataOff=0; dataOff<8; ++dataOff) { jo_DCT(&A[dataOff], &A[dataOff+8], &A[dataOff+16], &A[dataOff+24], &A[dataOff+32], &A[dataOff+40], &A[dataOff+48], &A[dataOff+56]); } int Q[64]; for(int i=0; i<64; ++i) { float v = A[i]*s_jo_quantTbl[i]; Q[s_jo_ZigZag[i]] = (int)(v < 0 ? ceilf(v - 0.5f) : floorf(v + 0.5f)); } DC = Q[0] - DC; int aDC = DC < 0 ? -DC : DC; int size = 0; int tempval = aDC; while(tempval) { size++; tempval >>= 1; } jo_writeBits(bits, htdc[size][0], htdc[size][1]); if(DC < 0) aDC ^= (1 << size) - 1; jo_writeBits(bits, aDC, size); int endpos = 63; for(; (endpos>0)&&(Q[endpos]==0); --endpos) { /* do nothing */ } for(int i = 1; i <= endpos;) { int run = 0; while (Q[i]==0 && i 127) { jo_writeBits(bits, 0, 8); } code = AC&255; size = 8; } jo_writeBits(bits, code, size); } jo_writeBits(bits, 2, 2); return Q[0]; } void jo_write_mpeg(FILE *fp, const unsigned char *bgrx, int width, int height, int fps) { int lastDCY = 128, lastDCCR = 128, lastDCCB = 128; jo_bits_t bits = {fp}; // Sequence Header fwrite("\x00\x00\x01\xB3", 4, 1, fp); // 12 bits for width, height putc((width>>4)&0xFF, fp); putc(((width&0xF)<<4) | ((height>>8) & 0xF), fp); putc(height & 0xFF, fp); // aspect ratio, framerate if(fps <= 24) putc(0x12, fp); else if(fps <= 25) putc(0x13, fp); else if(fps <= 30) putc(0x15, fp); else if(fps <= 50) putc(0x16, fp); else putc(0x18, fp); // 60fps fwrite("\xFF\xFF\xE0\xA0", 4, 1, fp); fwrite("\x00\x00\x01\xB8\x80\x08\x00\x40", 8, 1, fp); // GOP header fwrite("\x00\x00\x01\x00\x00\x0C\x00\x00", 8, 1, fp); // PIC header fwrite("\x00\x00\x01\x01", 4, 1, fp); // Slice header jo_writeBits(&bits, 0x10, 6); for (int vblock=0; vblock<(height+15)/16; vblock++) { for (int hblock=0; hblock<(width+15)/16; hblock++) { jo_writeBits(&bits, 3, 2); float Y[256], CBx[256], CRx[256]; for (int i=0; i<256; ++i) { int y = vblock*16+(i/16); int x = hblock*16+(i&15); x = x >= width ? width-1 : x; y = y >= height ? height-1 : y; int _4 = JO_MPEG_COMPONENTS; // const unsigned char *c = bgrx + y*width*_4+x*_4; // original const unsigned char *c = bgrx + ((height-1)-y)*width*_4+x*_4; // flipped float b = c[0], g = c[1], r = c[2]; // channel swap Y[i] = ( 0.299f*r + 0.587f*g + 0.114f*b) * (219.f/255) + 16; CBx[i] = (-0.299f*r - 0.587f*g + 0.886f*b) * (224.f/255) + 128; CRx[i] = ( 0.701f*r - 0.587f*g - 0.114f*b) * (224.f/255) + 128; } // Downsample Cb,Cr (420 format) float CB[64], CR[64]; for (int i=0; i<64; ++i) { int j =(i&7)*2 + (i&56)*4; CB[i] = (CBx[j] + CBx[j+1] + CBx[j+16] + CBx[j+17]) * 0.25f; CR[i] = (CRx[j] + CRx[j+1] + CRx[j+16] + CRx[j+17]) * 0.25f; } for (int k1=0; k1<2; ++k1) { for (int k2=0; k2<2; ++k2) { float block[64]; for (int i=0; i<64; i+=8) { int j = (i&7)+(i&56)*2 + k1*8*16 + k2*8; memcpy(block+i, Y+j, 8*sizeof(Y[0])); } lastDCY = jo_processDU(&bits, block, s_jo_HTDC_Y, lastDCY); } } lastDCCB = jo_processDU(&bits, CB, s_jo_HTDC_C, lastDCCB); lastDCCR = jo_processDU(&bits, CR, s_jo_HTDC_C, lastDCCR); } } jo_writeBits(&bits, 0, 7); fwrite("\x00\x00\x01\xb7", 4, 1, fp); // End of Sequence } #endif #line 0 //#define _RTL_RUN_ONCE _RTL_RUN_ONCE2 // __MINGW64__ #line 1 "3rd_https.h" /* ------------------------------------------------------------------------------ Licensing information can be found at the end of the file. ------------------------------------------------------------------------------ https.h - v0.1 - Basic HTTP/HTTPS protocol implementation over sockets. Do this: #define HTTPS_IMPLEMENTATION before you include this file in *one* C/C++ file to create the implementation. ------------------------------------------------------------------------------ This is a HTTP protocol implementation including the TLSe library for SSL functionality (https://github.com/eduardsui/tlse),and the LibTomCrypt library for crypto functions (https://github.com/libtom/libtomcrypt). Both of these libraries are available under a public domain license, so I'm releasing this library under my usual dual license (MIT and Public Domain). As they are C libraries, I have modified them to compile under C++ as well (mostly fixing automatic casts from void*, which C++ doesn't support). / Mattias Gustavsson ( mattias@mattiasgustavsson.com ) */ #ifndef https_h #define https_h #define _CRT_NONSTDC_NO_DEPRECATE #define _CRT_SECURE_NO_WARNINGS #include // for size_t typedef enum https_status_t { HTTPS_STATUS_PENDING, HTTPS_STATUS_COMPLETED, HTTPS_STATUS_FAILED, } https_status_t; typedef struct https_t { https_status_t status; int status_code; char const* reason_phrase; char const* content_type; size_t response_size; void* response_data; } https_t; https_t* https_get( char const* url, void* memctx ); https_t* https_post( char const* url, void const* data, size_t size, void* memctx ); https_status_t https_process( https_t* https ); void https_release( https_t* https ); #endif /* https_h */ /** Example ======= #define HTTPS_IMPLEMENTATION #include "https.h" int main( int argc, char** argv ) { (void) argc, argv; return 0; } API Documentation ================= https.h is a small library for making https requests from a web server. It only supports GET and POST https commands, and is designed for when you just need a very basic way of communicating over https. https.h is a single-header library, and does not need any .lib files or other binaries, or any build scripts. To use it, you just include https.h to get the API declarations. To get the definitions, you must include https.h from *one* single C or C++ file, and #define the symbol `HTTPS_IMPLEMENTATION` before you do. Customization ------------- ### Custom memory allocators For working memory and to store the retrieved data, https.h needs to do dynamic allocation by calling `malloc`. Programs might want to keep track of allocations done, or use custom defined pools to allocate memory from. https.h allows for specifying custom memory allocation functions for `malloc` and `free`. This is done with the following code: #define HTTPS_IMPLEMENTATION #define HTTPS_MALLOC( ctx, size ) ( my_custom_malloc( ctx, size ) ) #define HTTPS_FREE( ctx, ptr ) ( my_custom_free( ctx, ptr ) ) #include "https.h" where `my_custom_malloc` and `my_custom_free` are your own memory allocation/deallocation functions. The `ctx` parameter is an optional parameter of type `void*`. When `https_get` or `https_post` is called, , you can pass in a `memctx` parameter, which can be a pointer to anything you like, and which will be passed through as the `ctx` parameter to every `HTTPS_MALLOC`/`HTTPS_FREE` call. For example, if you are doing memory tracking, you can pass a pointer to your tracking data as `memctx`, and in your custom allocation/deallocation function, you can cast the `ctx` param back to the right type, and access the tracking data. If no custom allocator is defined, https.h will default to `malloc` and `free` from the C runtime library. https_get --------- https_t* https_get( char const* url, void* memctx ) Initiates a https GET request with the specified url. `url` is a zero terminated string containing the request location, just like you would type it in a browser, for example `https://www.mattiasgustavsson.com:443/http_test.txt`. `memctx` is a pointer to user defined data which will be passed through to the custom HTTPS_MALLOC/HTTPS_FREE calls. It can be NULL if no user defined data is needed. Returns a `https_t` instance, which needs to be passed to `https_process` to process the request. When the request is finished (or have failed), the returned `https_t` instance needs to be released by calling `https_release`. If the request was invalid, `https_get` returns NULL. https_post ---------- https_t* https_post( char const* url, void const* data, size_t size, void* memctx ) Initiates a https POST request with the specified url. `url` is a zero terminated string containing the request location, just like you would type it in a browser, for example `https://www.mattiasgustavsson.com:443/http_test.txt`. `data` is a pointer to the data to be sent along as part of the request, and `size` is the number of bytes to send. `memctx` is a pointer to user defined data which will be passed through to the custom HTTPS_MALLOC/HTTPS_FREE calls. It can be NULL if no user defined data is needed. Returns a `https_t` instance, which needs to be passed to `https_process` to process the request. When the request is finished (or have failed), the returned `https_t` instance needs to be released by calling `https_release`. If the request was invalid, `https_post` returns NULL. https_process ------------- https_status_t https_process( https_t* https ) https.h uses non-blocking sockets, so after a request have been made by calling either `https_get` or `https_post`, you have to keep calling `https_process` for as long as it returns `HTTPS_STATUS_PENDING`. You can call it from a loop which does other work too, for example from inside a game loop or from a loop which calls `https_process` on multiple requests. If the request fails, `https_process` returns `HTTPS_STATUS_FAILED`, and the fields `status_code` and `reason_phrase` may contain more details (for example, status code can be 404 if the requested resource was not found on the server). If the request completes successfully, it returns `HTTPS_STATUS_COMPLETED`. In this case, the `https_t` instance will contain details about the result. `status_code` and `reason_phrase` contains the details about the result, as specified in the HTTPS protocol. `content_type` contains the MIME type for the returns resource, for example `text/html` for a normal web page. `response_data` is the pointer to the received data, and `resonse_size` is the number of bytes it contains. In the case when the response data is in text format, https.h ensures there is a zero terminator placed immediately after the response data block, so it is safe to interpret the resonse data as a `char*`. Note that the data size in this case will be the length of the data without the additional zero terminator. https_release ------------- void https_release( https_t* https ) Releases the resources acquired by `https_get` or `https_post`. Should be call when you are finished with the request. */ /* ---------------------- IMPLEMENTATION ---------------------- */ #ifdef HTTPS_IMPLEMENTATION #undef HTTPS_IMPLEMENTATION #if defined(_MSC_VER) && _MSC_VER < 1600 typedef __int8 int8_t; typedef unsigned __int8 uint8_t; typedef __int16 int16_t; typedef unsigned __int16 uint16_t; typedef __int32 int32_t; typedef unsigned __int32 uint32_t; typedef __int64 int64_t; typedef unsigned __int64 uint64_t; #else #include #endif #ifdef _WIN32 #undef _CRT_NONSTDC_NO_DEPRECATE #define _CRT_NONSTDC_NO_DEPRECATE #undef _CRT_SECURE_NO_WARNINGS #define _CRT_SECURE_NO_WARNINGS #if !defined( _WIN32_WINNT ) || _WIN32_WINNT < 0x0501 #undef _WIN32_WINNT #define _WIN32_WINNT 0x501// requires Windows XP minimum #endif #pragma warning( push ) #pragma warning( disable: 4619 ) // pragma warning : there is no warning number 'number' #pragma warning( disable: 4127 ) // conditional expression is constant #pragma warning( disable: 4255 ) #pragma warning( disable: 4365 ) // 'action' : conversion from 'type_1' to 'type_2', signed/unsigned mismatch #pragma warning( disable: 4574 ) // 'Identifier' is defined to be '0': did you mean to use '#if identifier'? #pragma warning( disable: 4668 ) // 'symbol' is not defined as a preprocessor macro, replacing with '0' for 'directive' #pragma warning( disable: 4706 ) // assignment within conditional expression #include #include #pragma warning( pop ) #pragma comment (lib, "Ws2_32") //< @r-lyeh removed .lib (tcc support) #include #include #define HTTPS_SOCKET SOCKET #define HTTPS_INVALID_SOCKET INVALID_SOCKET #else #include #include #include #include #include #include #include #include #include #include #define HTTPS_SOCKET int #define HTTPS_INVALID_SOCKET -1 #endif static int https_snprintf( char* s, size_t n, char const* format, ... ) { va_list args; va_start( args, format ); #ifdef _WIN32 int r = _vsnprintf( s, n, format, args ); #else int r = vsnprintf( s, n, format, args ); #endif va_end( args ); return r; } #ifndef HTTPS_ASSERT #undef _CRT_NONSTDC_NO_DEPRECATE #define _CRT_NONSTDC_NO_DEPRECATE #undef _CRT_SECURE_NO_WARNINGS #define _CRT_SECURE_NO_WARNINGS #include #define HTTPS_ASSERT( expression, message ) assert( ( expression ) && ( message ) ) #endif #ifndef HTTPS_MALLOC #undef _CRT_NONSTDC_NO_DEPRECATE #define _CRT_NONSTDC_NO_DEPRECATE #undef _CRT_SECURE_NO_WARNINGS #define _CRT_SECURE_NO_WARNINGS #include #define HTTPS_MALLOC( ctx, size ) memset( MALLOC( size ), 0, size ) //< @r-lyeh: unify #define HTTPS_FREE( ctx, ptr ) ( FREE( ptr ) ) //< @r-lyeh: unify #endif #define XMALLOC https_internal_xmalloc #define XREALLOC https_internal_xrealloc #define XCALLOC https_internal_xcalloc #define XFREE https_internal_xfree void tls_init(); void* https_internal_memctx( void* set ); #define HTTPS_NO_THREADING 0 #if HTTPS_NO_THREADING void* https_internal_memctx( void* set ) { return 0; } #else #if defined( _WIN32 ) #ifdef __cplusplus extern "C" { #endif #ifdef __TINYC__ //#include // PRTL_RUN_ONCE typedef struct _RTL_RUN_ONCE { PVOID Ptr; } RTL_RUN_ONCE, *PRTL_RUN_ONCE; typedef DWORD (WINAPI *PRTL_RUN_ONCE_INIT_FN)(PRTL_RUN_ONCE, PVOID, PVOID *); #endif #if is(tcc) // ndef __MINGW64__ //< @r-lyeh WINBASEAPI BOOL WINAPI InitOnceExecuteOnce( PRTL_RUN_ONCE InitOnce, BOOL (WINAPI*)( PRTL_RUN_ONCE, PVOID, PVOID* ), PVOID Parameter, LPVOID * Context ); #endif #ifdef __cplusplus } #endif static RTL_RUN_ONCE https_internal_one_time_init_instance = { 0 }; static DWORD https_internal_tls = TLS_OUT_OF_INDEXES; BOOL CALLBACK https_internal_one_time_init( PRTL_RUN_ONCE InitOnce, PVOID Parameter, PVOID *lpContext) { (void)InitOnce; (void)Parameter; (void)lpContext; // WSADATA wsa_data; // int result = WSAStartup( MAKEWORD( 1, 0 ), &wsa_data ); // HTTPS_ASSERT( result == 0, "Failed to initialize winsock" ); // (void) result; // do_once { WSADATA data; (void)WSAStartup(MAKEWORD(2,2), &data); // } https_internal_tls = TlsAlloc(); HTTPS_ASSERT( https_internal_tls != TLS_OUT_OF_INDEXES, "Failed to initialize thread local storage" ); tls_init(); return TRUE; } #else static pthread_once_t https_internal_one_time_init_instance = PTHREAD_ONCE_INIT; static pthread_key_t https_internal_tls; void https_internal_one_time_init( void ) { int result = pthread_key_create( &https_internal_tls, NULL ); HTTPS_ASSERT( result == 0, "Failed to initialize thread local storage" ); tls_init(); } #endif // _WIN32 void* https_internal_memctx( void* set ) { if( set ) { #if defined( _WIN32 ) TlsSetValue( https_internal_tls, set ); #else pthread_setspecific( https_internal_tls, set ); #endif return NULL; } else { #if defined( _WIN32 ) return TlsGetValue( https_internal_tls ); #else return pthread_getspecific( https_internal_tls ); #endif } } #endif // if 0 void* https_internal_xmalloc( size_t n ) { uint64_t* ptr = (uint64_t*) HTTPS_MALLOC( https_internal_memctx( NULL ), n + sizeof( uint64_t ) ); *ptr = (uint64_t) n; return (void*)( ptr + 1 ); } void* https_internal_xcalloc( size_t n, size_t s ) { void* ptr = https_internal_xmalloc( n * s ); memset( ptr, 0, n * s ); return ptr; } void https_internal_xfree( void* p ) { if( !p ) return; void* real_ptr = (void*)( ( (uint64_t*) p ) - 1 ); HTTPS_FREE( https_internal_memctx( NULL ), real_ptr ); } void* https_internal_xrealloc( void* p, size_t n ) { if( !p ) return https_internal_xmalloc( n ); void* real_ptr = (void*)( ( (uint64_t*) p ) - 1 ); size_t prev_size = (size_t)( *(uint64_t*) real_ptr ); if( prev_size >= n ) return p; // Never make smaller void* new_ptr = https_internal_xmalloc( n ); memcpy( new_ptr, p, prev_size ); https_internal_xfree( p ); return new_ptr; } #ifdef __cplusplus // MG: As there were hundreds of places where the C code relied on implicit casts from void* to some other pointer // type, I'm using this construct to make it easier to add the casts. It's a simple template which allows you to // to cast, via the template operator, to anything. In the rest of the code, I have just added the AUTO_CAST // macro where needed (which has no effect in C builds). template< typename T > struct void_cast_helper { void_cast_helper( T const& x_ ) : x( x_ ) { } template< typename U > operator U() { return (U)x; } void_cast_helper& operator=( void_cast_helper const& ); // no assignment operator as we're storing a const T const& x; }; template< typename T > void_cast_helper void_cast( T const& x ) { return void_cast_helper( x ); } #define AUTO_CAST( x ) void_cast( x ) #else #define AUTO_CAST( x ) x #endif #pragma warning( disable: 4619 ) #pragma warning( disable: 4005 ) #pragma warning( disable: 4018 ) #pragma warning( disable: 4055 ) #pragma warning( disable: 4047 ) #pragma warning( disable: 4100 ) #pragma warning( disable: 4127 ) #pragma warning( disable: 4242 ) #pragma warning( disable: 4244 ) #pragma warning( disable: 4245 ) #pragma warning( disable: 4255 ) #pragma warning( disable: 4267 ) #pragma warning( disable: 4302 ) #pragma warning( disable: 4311 ) #pragma warning( disable: 4333 ) #pragma warning( disable: 4334 ) #pragma warning( disable: 4365 ) #pragma warning( disable: 4388 ) #pragma warning( disable: 4431 ) #pragma warning( disable: 4668 ) #define LTC_NO_PROTOTYPES #ifndef HTTPS_NO_LIBTOMCRYPT #define HTTPS_NO_LIBTOMCRYPT /* ------------------------------------------------------------------------------ BEGIN libtomcrypt.c ------------------------------------------------------------------------------ */ #ifdef _WIN32 #pragma comment( lib, "Advapi32" ) //< @r-lyeh, tcc support (remove .lib) #endif #ifdef __GNUC__ #define LTC_NO_ASM //< @r-lyeh, gcc support (Error: incorrect register `%eax' used with `q' suffix) #endif #define CRYPT 0x0117 #define LTC_NO_ROLC /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://math.libtomcrypt.com */ #ifndef BN_H_ #define BN_H_ #include #include #include #if !(defined(LTM1) && defined(LTM2) && defined(LTM3)) #if defined(LTM2) #define LTM3 #endif #if defined(LTM1) #define LTM2 #endif #define LTM1 #if defined(LTM_ALL) #define BN_ERROR_C #define BN_FAST_MP_INVMOD_C #define BN_FAST_MP_MONTGOMERY_REDUCE_C #define BN_FAST_S_MP_MUL_DIGS_C #define BN_FAST_S_MP_MUL_HIGH_DIGS_C #define BN_FAST_S_MP_SQR_C #define BN_MP_2EXPT_C #define BN_MP_ABS_C #define BN_MP_ADD_C #define BN_MP_ADD_D_C #define BN_MP_ADDMOD_C #define BN_MP_AND_C #define BN_MP_CLAMP_C #define BN_MP_CLEAR_C #define BN_MP_CLEAR_MULTI_C #define BN_MP_CMP_C #define BN_MP_CMP_D_C #define BN_MP_CMP_MAG_C #define BN_MP_CNT_LSB_C #define BN_MP_COPY_C #define BN_MP_COUNT_BITS_C #define BN_MP_DIV_C #define BN_MP_DIV_2_C #define BN_MP_DIV_2D_C #define BN_MP_DIV_3_C #define BN_MP_DIV_D_C #define BN_MP_DR_IS_MODULUS_C #define BN_MP_DR_REDUCE_C #define BN_MP_DR_SETUP_C #define BN_MP_EXCH_C #define BN_MP_EXPORT_C #define BN_MP_EXPT_D_C #define BN_MP_EXPT_D_EX_C #define BN_MP_EXPTMOD_C #define BN_MP_EXPTMOD_FAST_C #define BN_MP_EXTEUCLID_C #define BN_MP_FREAD_C #define BN_MP_FWRITE_C #define BN_MP_GCD_C #define BN_MP_GET_INT_C #define BN_MP_GET_LONG_C #define BN_MP_GET_LONG_LONG_C #define BN_MP_GROW_C #define BN_MP_IMPORT_C #define BN_MP_INIT_C #define BN_MP_INIT_COPY_C #define BN_MP_INIT_MULTI_C #define BN_MP_INIT_SET_C #define BN_MP_INIT_SET_INT_C #define BN_MP_INIT_SIZE_C #define BN_MP_INVMOD_C #define BN_MP_INVMOD_SLOW_C #define BN_MP_IS_SQUARE_C #define BN_MP_JACOBI_C #define BN_MP_KARATSUBA_MUL_C #define BN_MP_KARATSUBA_SQR_C #define BN_MP_LCM_C #define BN_MP_LSHD_C #define BN_MP_MOD_C #define BN_MP_MOD_2D_C #define BN_MP_MOD_D_C #define BN_MP_MONTGOMERY_CALC_NORMALIZATION_C #define BN_MP_MONTGOMERY_REDUCE_C #define BN_MP_MONTGOMERY_SETUP_C #define BN_MP_MUL_C #define BN_MP_MUL_2_C #define BN_MP_MUL_2D_C #define BN_MP_MUL_D_C #define BN_MP_MULMOD_C #define BN_MP_N_ROOT_C #define BN_MP_N_ROOT_EX_C #define BN_MP_NEG_C #define BN_MP_OR_C #define BN_MP_PRIME_FERMAT_C #define BN_MP_PRIME_IS_DIVISIBLE_C #define BN_MP_PRIME_IS_PRIME_C #define BN_MP_PRIME_MILLER_RABIN_C #define BN_MP_PRIME_NEXT_PRIME_C #define BN_MP_PRIME_RABIN_MILLER_TRIALS_C #define BN_MP_PRIME_RANDOM_EX_C #define BN_MP_RADIX_SIZE_C #define BN_MP_RADIX_SMAP_C #define BN_MP_RAND_C #define BN_MP_READ_RADIX_C #define BN_MP_READ_SIGNED_BIN_C #define BN_MP_READ_UNSIGNED_BIN_C #define BN_MP_REDUCE_C #define BN_MP_REDUCE_2K_C #define BN_MP_REDUCE_2K_L_C #define BN_MP_REDUCE_2K_SETUP_C #define BN_MP_REDUCE_2K_SETUP_L_C #define BN_MP_REDUCE_IS_2K_C #define BN_MP_REDUCE_IS_2K_L_C #define BN_MP_REDUCE_SETUP_C #define BN_MP_RSHD_C #define BN_MP_SET_C #define BN_MP_SET_INT_C #define BN_MP_SET_LONG_C #define BN_MP_SET_LONG_LONG_C #define BN_MP_SHRINK_C #define BN_MP_SIGNED_BIN_SIZE_C #define BN_MP_SQR_C #define BN_MP_SQRMOD_C #define BN_MP_SQRT_C #define BN_MP_SQRTMOD_PRIME_C #define BN_MP_SUB_C #define BN_MP_SUB_D_C #define BN_MP_SUBMOD_C #define BN_MP_TO_SIGNED_BIN_C #define BN_MP_TO_SIGNED_BIN_N_C #define BN_MP_TO_UNSIGNED_BIN_C #define BN_MP_TO_UNSIGNED_BIN_N_C #define BN_MP_TOOM_MUL_C #define BN_MP_TOOM_SQR_C #define BN_MP_TORADIX_C #define BN_MP_TORADIX_N_C #define BN_MP_UNSIGNED_BIN_SIZE_C #define BN_MP_XOR_C #define BN_MP_ZERO_C #define BN_PRIME_TAB_C #define BN_REVERSE_C #define BN_S_MP_ADD_C #define BN_S_MP_EXPTMOD_C #define BN_S_MP_MUL_DIGS_C #define BN_S_MP_MUL_HIGH_DIGS_C #define BN_S_MP_SQR_C #define BN_S_MP_SUB_C #define BNCORE_C #endif #if defined(BN_ERROR_C) #define BN_MP_ERROR_TO_STRING_C #endif #if defined(BN_FAST_MP_INVMOD_C) #define BN_MP_ISEVEN_C #define BN_MP_INIT_MULTI_C #define BN_MP_COPY_C #define BN_MP_MOD_C #define BN_MP_SET_C #define BN_MP_DIV_2_C #define BN_MP_ISODD_C #define BN_MP_SUB_C #define BN_MP_CMP_C #define BN_MP_ISZERO_C #define BN_MP_CMP_D_C #define BN_MP_ADD_C #define BN_MP_EXCH_C #define BN_MP_CLEAR_MULTI_C #endif #if defined(BN_FAST_MP_MONTGOMERY_REDUCE_C) #define BN_MP_GROW_C #define BN_MP_RSHD_C #define BN_MP_CLAMP_C #define BN_MP_CMP_MAG_C #define BN_S_MP_SUB_C #endif #if defined(BN_FAST_S_MP_MUL_DIGS_C) #define BN_MP_GROW_C #define BN_MP_CLAMP_C #endif #if defined(BN_FAST_S_MP_MUL_HIGH_DIGS_C) #define BN_MP_GROW_C #define BN_MP_CLAMP_C #endif #if defined(BN_FAST_S_MP_SQR_C) #define BN_MP_GROW_C #define BN_MP_CLAMP_C #endif #if defined(BN_MP_2EXPT_C) #define BN_MP_ZERO_C #define BN_MP_GROW_C #endif #if defined(BN_MP_ABS_C) #define BN_MP_COPY_C #endif #if defined(BN_MP_ADD_C) #define BN_S_MP_ADD_C #define BN_MP_CMP_MAG_C #define BN_S_MP_SUB_C #endif #if defined(BN_MP_ADD_D_C) #define BN_MP_GROW_C #define BN_MP_SUB_D_C #define BN_MP_CLAMP_C #endif #if defined(BN_MP_ADDMOD_C) #define BN_MP_INIT_C #define BN_MP_ADD_C #define BN_MP_CLEAR_C #define BN_MP_MOD_C #endif #if defined(BN_MP_AND_C) #define BN_MP_INIT_COPY_C #define BN_MP_CLAMP_C #define BN_MP_EXCH_C #define BN_MP_CLEAR_C #endif #if defined(BN_MP_CLAMP_C) #endif #if defined(BN_MP_CLEAR_C) #endif #if defined(BN_MP_CLEAR_MULTI_C) #define BN_MP_CLEAR_C #endif #if defined(BN_MP_CMP_C) #define BN_MP_CMP_MAG_C #endif #if defined(BN_MP_CMP_D_C) #endif #if defined(BN_MP_CMP_MAG_C) #endif #if defined(BN_MP_CNT_LSB_C) #define BN_MP_ISZERO_C #endif #if defined(BN_MP_COPY_C) #define BN_MP_GROW_C #endif #if defined(BN_MP_COUNT_BITS_C) #endif #if defined(BN_MP_DIV_C) #define BN_MP_ISZERO_C #define BN_MP_CMP_MAG_C #define BN_MP_COPY_C #define BN_MP_ZERO_C #define BN_MP_INIT_MULTI_C #define BN_MP_SET_C #define BN_MP_COUNT_BITS_C #define BN_MP_ABS_C #define BN_MP_MUL_2D_C #define BN_MP_CMP_C #define BN_MP_SUB_C #define BN_MP_ADD_C #define BN_MP_DIV_2D_C #define BN_MP_EXCH_C #define BN_MP_CLEAR_MULTI_C #define BN_MP_INIT_SIZE_C #define BN_MP_INIT_C #define BN_MP_INIT_COPY_C #define BN_MP_LSHD_C #define BN_MP_RSHD_C #define BN_MP_MUL_D_C #define BN_MP_CLAMP_C #define BN_MP_CLEAR_C #endif #if defined(BN_MP_DIV_2_C) #define BN_MP_GROW_C #define BN_MP_CLAMP_C #endif #if defined(BN_MP_DIV_2D_C) #define BN_MP_COPY_C #define BN_MP_ZERO_C #define BN_MP_INIT_C #define BN_MP_MOD_2D_C #define BN_MP_CLEAR_C #define BN_MP_RSHD_C #define BN_MP_CLAMP_C #define BN_MP_EXCH_C #endif #if defined(BN_MP_DIV_3_C) #define BN_MP_INIT_SIZE_C #define BN_MP_CLAMP_C #define BN_MP_EXCH_C #define BN_MP_CLEAR_C #endif #if defined(BN_MP_DIV_D_C) #define BN_MP_ISZERO_C #define BN_MP_COPY_C #define BN_MP_DIV_2D_C #define BN_MP_DIV_3_C #define BN_MP_INIT_SIZE_C #define BN_MP_CLAMP_C #define BN_MP_EXCH_C #define BN_MP_CLEAR_C #endif #if defined(BN_MP_DR_IS_MODULUS_C) #endif #if defined(BN_MP_DR_REDUCE_C) #define BN_MP_GROW_C #define BN_MP_CLAMP_C #define BN_MP_CMP_MAG_C #define BN_S_MP_SUB_C #endif #if defined(BN_MP_DR_SETUP_C) #endif #if defined(BN_MP_EXCH_C) #endif #if defined(BN_MP_EXPORT_C) #define BN_MP_INIT_COPY_C #define BN_MP_COUNT_BITS_C #define BN_MP_DIV_2D_C #define BN_MP_CLEAR_C #endif #if defined(BN_MP_EXPT_D_C) #define BN_MP_EXPT_D_EX_C #endif #if defined(BN_MP_EXPT_D_EX_C) #define BN_MP_INIT_COPY_C #define BN_MP_SET_C #define BN_MP_MUL_C #define BN_MP_CLEAR_C #define BN_MP_SQR_C #endif #if defined(BN_MP_EXPTMOD_C) #define BN_MP_INIT_C #define BN_MP_INVMOD_C #define BN_MP_CLEAR_C #define BN_MP_ABS_C #define BN_MP_CLEAR_MULTI_C #define BN_MP_REDUCE_IS_2K_L_C #define BN_S_MP_EXPTMOD_C #define BN_MP_DR_IS_MODULUS_C #define BN_MP_REDUCE_IS_2K_C #define BN_MP_ISODD_C #define BN_MP_EXPTMOD_FAST_C #endif #if defined(BN_MP_EXPTMOD_FAST_C) #define BN_MP_COUNT_BITS_C #define BN_MP_INIT_C #define BN_MP_CLEAR_C #define BN_MP_MONTGOMERY_SETUP_C #define BN_FAST_MP_MONTGOMERY_REDUCE_C #define BN_MP_MONTGOMERY_REDUCE_C #define BN_MP_DR_SETUP_C #define BN_MP_DR_REDUCE_C #define BN_MP_REDUCE_2K_SETUP_C #define BN_MP_REDUCE_2K_C #define BN_MP_MONTGOMERY_CALC_NORMALIZATION_C #define BN_MP_MULMOD_C #define BN_MP_SET_C #define BN_MP_MOD_C #define BN_MP_COPY_C #define BN_MP_SQR_C #define BN_MP_MUL_C #define BN_MP_EXCH_C #endif #if defined(BN_MP_EXTEUCLID_C) #define BN_MP_INIT_MULTI_C #define BN_MP_SET_C #define BN_MP_COPY_C #define BN_MP_ISZERO_C #define BN_MP_DIV_C #define BN_MP_MUL_C #define BN_MP_SUB_C #define BN_MP_NEG_C #define BN_MP_EXCH_C #define BN_MP_CLEAR_MULTI_C #endif #if defined(BN_MP_FREAD_C) #define BN_MP_ZERO_C #define BN_MP_S_RMAP_C #define BN_MP_MUL_D_C #define BN_MP_ADD_D_C #define BN_MP_CMP_D_C #endif #if defined(BN_MP_FWRITE_C) #define BN_MP_RADIX_SIZE_C #define BN_MP_TORADIX_C #endif #if defined(BN_MP_GCD_C) #define BN_MP_ISZERO_C #define BN_MP_ABS_C #define BN_MP_INIT_COPY_C #define BN_MP_CNT_LSB_C #define BN_MP_DIV_2D_C #define BN_MP_CMP_MAG_C #define BN_MP_EXCH_C #define BN_S_MP_SUB_C #define BN_MP_MUL_2D_C #define BN_MP_CLEAR_C #endif #if defined(BN_MP_GET_INT_C) #endif #if defined(BN_MP_GET_LONG_C) #endif #if defined(BN_MP_GET_LONG_LONG_C) #endif #if defined(BN_MP_GROW_C) #endif #if defined(BN_MP_IMPORT_C) #define BN_MP_ZERO_C #define BN_MP_MUL_2D_C #define BN_MP_CLAMP_C #endif #if defined(BN_MP_INIT_C) #endif #if defined(BN_MP_INIT_COPY_C) #define BN_MP_INIT_SIZE_C #define BN_MP_COPY_C #endif #if defined(BN_MP_INIT_MULTI_C) #define BN_MP_ERR_C #define BN_MP_INIT_C #define BN_MP_CLEAR_C #endif #if defined(BN_MP_INIT_SET_C) #define BN_MP_INIT_C #define BN_MP_SET_C #endif #if defined(BN_MP_INIT_SET_INT_C) #define BN_MP_INIT_C #define BN_MP_SET_INT_C #endif #if defined(BN_MP_INIT_SIZE_C) #define BN_MP_INIT_C #endif #if defined(BN_MP_INVMOD_C) #define BN_MP_ISZERO_C #define BN_MP_ISODD_C #define BN_FAST_MP_INVMOD_C #define BN_MP_INVMOD_SLOW_C #endif #if defined(BN_MP_INVMOD_SLOW_C) #define BN_MP_ISZERO_C #define BN_MP_INIT_MULTI_C #define BN_MP_MOD_C #define BN_MP_COPY_C #define BN_MP_ISEVEN_C #define BN_MP_SET_C #define BN_MP_DIV_2_C #define BN_MP_ISODD_C #define BN_MP_ADD_C #define BN_MP_SUB_C #define BN_MP_CMP_C #define BN_MP_CMP_D_C #define BN_MP_CMP_MAG_C #define BN_MP_EXCH_C #define BN_MP_CLEAR_MULTI_C #endif #if defined(BN_MP_IS_SQUARE_C) #define BN_MP_MOD_D_C #define BN_MP_INIT_SET_INT_C #define BN_MP_MOD_C #define BN_MP_GET_INT_C #define BN_MP_SQRT_C #define BN_MP_SQR_C #define BN_MP_CMP_MAG_C #define BN_MP_CLEAR_C #endif #if defined(BN_MP_JACOBI_C) #define BN_MP_CMP_D_C #define BN_MP_ISZERO_C #define BN_MP_INIT_COPY_C #define BN_MP_CNT_LSB_C #define BN_MP_DIV_2D_C #define BN_MP_MOD_C #define BN_MP_CLEAR_C #endif #if defined(BN_MP_KARATSUBA_MUL_C) #define BN_MP_MUL_C #define BN_MP_INIT_SIZE_C #define BN_MP_CLAMP_C #define BN_S_MP_ADD_C #define BN_MP_ADD_C #define BN_S_MP_SUB_C #define BN_MP_LSHD_C #define BN_MP_CLEAR_C #endif #if defined(BN_MP_KARATSUBA_SQR_C) #define BN_MP_INIT_SIZE_C #define BN_MP_CLAMP_C #define BN_MP_SQR_C #define BN_S_MP_ADD_C #define BN_S_MP_SUB_C #define BN_MP_LSHD_C #define BN_MP_ADD_C #define BN_MP_CLEAR_C #endif #if defined(BN_MP_LCM_C) #define BN_MP_INIT_MULTI_C #define BN_MP_GCD_C #define BN_MP_CMP_MAG_C #define BN_MP_DIV_C #define BN_MP_MUL_C #define BN_MP_CLEAR_MULTI_C #endif #if defined(BN_MP_LSHD_C) #define BN_MP_GROW_C #define BN_MP_RSHD_C #endif #if defined(BN_MP_MOD_C) #define BN_MP_INIT_C #define BN_MP_DIV_C #define BN_MP_CLEAR_C #define BN_MP_ISZERO_C #define BN_MP_EXCH_C #define BN_MP_ADD_C #endif #if defined(BN_MP_MOD_2D_C) #define BN_MP_ZERO_C #define BN_MP_COPY_C #define BN_MP_CLAMP_C #endif #if defined(BN_MP_MOD_D_C) #define BN_MP_DIV_D_C #endif #if defined(BN_MP_MONTGOMERY_CALC_NORMALIZATION_C) #define BN_MP_COUNT_BITS_C #define BN_MP_2EXPT_C #define BN_MP_SET_C #define BN_MP_MUL_2_C #define BN_MP_CMP_MAG_C #define BN_S_MP_SUB_C #endif #if defined(BN_MP_MONTGOMERY_REDUCE_C) #define BN_FAST_MP_MONTGOMERY_REDUCE_C #define BN_MP_GROW_C #define BN_MP_CLAMP_C #define BN_MP_RSHD_C #define BN_MP_CMP_MAG_C #define BN_S_MP_SUB_C #endif #if defined(BN_MP_MONTGOMERY_SETUP_C) #endif #if defined(BN_MP_MUL_C) #define BN_MP_TOOM_MUL_C #define BN_MP_KARATSUBA_MUL_C #define BN_FAST_S_MP_MUL_DIGS_C #define BN_S_MP_MUL_C #define BN_S_MP_MUL_DIGS_C #endif #if defined(BN_MP_MUL_2_C) #define BN_MP_GROW_C #endif #if defined(BN_MP_MUL_2D_C) #define BN_MP_COPY_C #define BN_MP_GROW_C #define BN_MP_LSHD_C #define BN_MP_CLAMP_C #endif #if defined(BN_MP_MUL_D_C) #define BN_MP_GROW_C #define BN_MP_CLAMP_C #endif #if defined(BN_MP_MULMOD_C) #define BN_MP_INIT_C #define BN_MP_MUL_C #define BN_MP_CLEAR_C #define BN_MP_MOD_C #endif #if defined(BN_MP_N_ROOT_C) #define BN_MP_N_ROOT_EX_C #endif #if defined(BN_MP_N_ROOT_EX_C) #define BN_MP_INIT_C #define BN_MP_SET_C #define BN_MP_COPY_C #define BN_MP_EXPT_D_EX_C #define BN_MP_MUL_C #define BN_MP_SUB_C #define BN_MP_MUL_D_C #define BN_MP_DIV_C #define BN_MP_CMP_C #define BN_MP_SUB_D_C #define BN_MP_EXCH_C #define BN_MP_CLEAR_C #endif #if defined(BN_MP_NEG_C) #define BN_MP_COPY_C #define BN_MP_ISZERO_C #endif #if defined(BN_MP_OR_C) #define BN_MP_INIT_COPY_C #define BN_MP_CLAMP_C #define BN_MP_EXCH_C #define BN_MP_CLEAR_C #endif #if defined(BN_MP_PRIME_FERMAT_C) #define BN_MP_CMP_D_C #define BN_MP_INIT_C #define BN_MP_EXPTMOD_C #define BN_MP_CMP_C #define BN_MP_CLEAR_C #endif #if defined(BN_MP_PRIME_IS_DIVISIBLE_C) #define BN_MP_MOD_D_C #endif #if defined(BN_MP_PRIME_IS_PRIME_C) #define BN_MP_CMP_D_C #define BN_MP_PRIME_IS_DIVISIBLE_C #define BN_MP_INIT_C #define BN_MP_SET_C #define BN_MP_PRIME_MILLER_RABIN_C #define BN_MP_CLEAR_C #endif #if defined(BN_MP_PRIME_MILLER_RABIN_C) #define BN_MP_CMP_D_C #define BN_MP_INIT_COPY_C #define BN_MP_SUB_D_C #define BN_MP_CNT_LSB_C #define BN_MP_DIV_2D_C #define BN_MP_EXPTMOD_C #define BN_MP_CMP_C #define BN_MP_SQRMOD_C #define BN_MP_CLEAR_C #endif #if defined(BN_MP_PRIME_NEXT_PRIME_C) #define BN_MP_CMP_D_C #define BN_MP_SET_C #define BN_MP_SUB_D_C #define BN_MP_ISEVEN_C #define BN_MP_MOD_D_C #define BN_MP_INIT_C #define BN_MP_ADD_D_C #define BN_MP_PRIME_MILLER_RABIN_C #define BN_MP_CLEAR_C #endif #if defined(BN_MP_PRIME_RABIN_MILLER_TRIALS_C) #endif #if defined(BN_MP_PRIME_RANDOM_EX_C) #define BN_MP_READ_UNSIGNED_BIN_C #define BN_MP_PRIME_IS_PRIME_C #define BN_MP_SUB_D_C #define BN_MP_DIV_2_C #define BN_MP_MUL_2_C #define BN_MP_ADD_D_C #endif #if defined(BN_MP_RADIX_SIZE_C) #define BN_MP_ISZERO_C #define BN_MP_COUNT_BITS_C #define BN_MP_INIT_COPY_C #define BN_MP_DIV_D_C #define BN_MP_CLEAR_C #endif #if defined(BN_MP_RADIX_SMAP_C) #define BN_MP_S_RMAP_C #endif #if defined(BN_MP_RAND_C) #define BN_MP_ZERO_C #define BN_MP_ADD_D_C #define BN_MP_LSHD_C #endif #if defined(BN_MP_READ_RADIX_C) #define BN_MP_ZERO_C #define BN_MP_S_RMAP_C #define BN_MP_MUL_D_C #define BN_MP_ADD_D_C #define BN_MP_ISZERO_C #endif #if defined(BN_MP_READ_SIGNED_BIN_C) #define BN_MP_READ_UNSIGNED_BIN_C #endif #if defined(BN_MP_READ_UNSIGNED_BIN_C) #define BN_MP_GROW_C #define BN_MP_ZERO_C #define BN_MP_MUL_2D_C #define BN_MP_CLAMP_C #endif #if defined(BN_MP_REDUCE_C) #define BN_MP_REDUCE_SETUP_C #define BN_MP_INIT_COPY_C #define BN_MP_RSHD_C #define BN_MP_MUL_C #define BN_S_MP_MUL_HIGH_DIGS_C #define BN_FAST_S_MP_MUL_HIGH_DIGS_C #define BN_MP_MOD_2D_C #define BN_S_MP_MUL_DIGS_C #define BN_MP_SUB_C #define BN_MP_CMP_D_C #define BN_MP_SET_C #define BN_MP_LSHD_C #define BN_MP_ADD_C #define BN_MP_CMP_C #define BN_S_MP_SUB_C #define BN_MP_CLEAR_C #endif #if defined(BN_MP_REDUCE_2K_C) #define BN_MP_INIT_C #define BN_MP_COUNT_BITS_C #define BN_MP_DIV_2D_C #define BN_MP_MUL_D_C #define BN_S_MP_ADD_C #define BN_MP_CMP_MAG_C #define BN_S_MP_SUB_C #define BN_MP_CLEAR_C #endif #if defined(BN_MP_REDUCE_2K_L_C) #define BN_MP_INIT_C #define BN_MP_COUNT_BITS_C #define BN_MP_DIV_2D_C #define BN_MP_MUL_C #define BN_S_MP_ADD_C #define BN_MP_CMP_MAG_C #define BN_S_MP_SUB_C #define BN_MP_CLEAR_C #endif #if defined(BN_MP_REDUCE_2K_SETUP_C) #define BN_MP_INIT_C #define BN_MP_COUNT_BITS_C #define BN_MP_2EXPT_C #define BN_MP_CLEAR_C #define BN_S_MP_SUB_C #endif #if defined(BN_MP_REDUCE_2K_SETUP_L_C) #define BN_MP_INIT_C #define BN_MP_2EXPT_C #define BN_MP_COUNT_BITS_C #define BN_S_MP_SUB_C #define BN_MP_CLEAR_C #endif #if defined(BN_MP_REDUCE_IS_2K_C) #define BN_MP_REDUCE_2K_C #define BN_MP_COUNT_BITS_C #endif #if defined(BN_MP_REDUCE_IS_2K_L_C) #endif #if defined(BN_MP_REDUCE_SETUP_C) #define BN_MP_2EXPT_C #define BN_MP_DIV_C #endif #if defined(BN_MP_RSHD_C) #define BN_MP_ZERO_C #endif #if defined(BN_MP_SET_C) #define BN_MP_ZERO_C #endif #if defined(BN_MP_SET_INT_C) #define BN_MP_ZERO_C #define BN_MP_MUL_2D_C #define BN_MP_CLAMP_C #endif #if defined(BN_MP_SET_LONG_C) #endif #if defined(BN_MP_SET_LONG_LONG_C) #endif #if defined(BN_MP_SHRINK_C) #endif #if defined(BN_MP_SIGNED_BIN_SIZE_C) #define BN_MP_UNSIGNED_BIN_SIZE_C #endif #if defined(BN_MP_SQR_C) #define BN_MP_TOOM_SQR_C #define BN_MP_KARATSUBA_SQR_C #define BN_FAST_S_MP_SQR_C #define BN_S_MP_SQR_C #endif #if defined(BN_MP_SQRMOD_C) #define BN_MP_INIT_C #define BN_MP_SQR_C #define BN_MP_CLEAR_C #define BN_MP_MOD_C #endif #if defined(BN_MP_SQRT_C) #define BN_MP_N_ROOT_C #define BN_MP_ISZERO_C #define BN_MP_ZERO_C #define BN_MP_INIT_COPY_C #define BN_MP_RSHD_C #define BN_MP_DIV_C #define BN_MP_ADD_C #define BN_MP_DIV_2_C #define BN_MP_CMP_MAG_C #define BN_MP_EXCH_C #define BN_MP_CLEAR_C #endif #if defined(BN_MP_SQRTMOD_PRIME_C) #define BN_MP_CMP_D_C #define BN_MP_ZERO_C #define BN_MP_JACOBI_C #define BN_MP_INIT_MULTI_C #define BN_MP_MOD_D_C #define BN_MP_ADD_D_C #define BN_MP_DIV_2_C #define BN_MP_EXPTMOD_C #define BN_MP_COPY_C #define BN_MP_SUB_D_C #define BN_MP_ISEVEN_C #define BN_MP_SET_INT_C #define BN_MP_SQRMOD_C #define BN_MP_MULMOD_C #define BN_MP_SET_C #define BN_MP_CLEAR_MULTI_C #endif #if defined(BN_MP_SUB_C) #define BN_S_MP_ADD_C #define BN_MP_CMP_MAG_C #define BN_S_MP_SUB_C #endif #if defined(BN_MP_SUB_D_C) #define BN_MP_GROW_C #define BN_MP_ADD_D_C #define BN_MP_CLAMP_C #endif #if defined(BN_MP_SUBMOD_C) #define BN_MP_INIT_C #define BN_MP_SUB_C #define BN_MP_CLEAR_C #define BN_MP_MOD_C #endif #if defined(BN_MP_TO_SIGNED_BIN_C) #define BN_MP_TO_UNSIGNED_BIN_C #endif #if defined(BN_MP_TO_SIGNED_BIN_N_C) #define BN_MP_SIGNED_BIN_SIZE_C #define BN_MP_TO_SIGNED_BIN_C #endif #if defined(BN_MP_TO_UNSIGNED_BIN_C) #define BN_MP_INIT_COPY_C #define BN_MP_ISZERO_C #define BN_MP_DIV_2D_C #define BN_MP_CLEAR_C #endif #if defined(BN_MP_TO_UNSIGNED_BIN_N_C) #define BN_MP_UNSIGNED_BIN_SIZE_C #define BN_MP_TO_UNSIGNED_BIN_C #endif #if defined(BN_MP_TOOM_MUL_C) #define BN_MP_INIT_MULTI_C #define BN_MP_MOD_2D_C #define BN_MP_COPY_C #define BN_MP_RSHD_C #define BN_MP_MUL_C #define BN_MP_MUL_2_C #define BN_MP_ADD_C #define BN_MP_SUB_C #define BN_MP_DIV_2_C #define BN_MP_MUL_2D_C #define BN_MP_MUL_D_C #define BN_MP_DIV_3_C #define BN_MP_LSHD_C #define BN_MP_CLEAR_MULTI_C #endif #if defined(BN_MP_TOOM_SQR_C) #define BN_MP_INIT_MULTI_C #define BN_MP_MOD_2D_C #define BN_MP_COPY_C #define BN_MP_RSHD_C #define BN_MP_SQR_C #define BN_MP_MUL_2_C #define BN_MP_ADD_C #define BN_MP_SUB_C #define BN_MP_DIV_2_C #define BN_MP_MUL_2D_C #define BN_MP_MUL_D_C #define BN_MP_DIV_3_C #define BN_MP_LSHD_C #define BN_MP_CLEAR_MULTI_C #endif #if defined(BN_MP_TORADIX_C) #define BN_MP_ISZERO_C #define BN_MP_INIT_COPY_C #define BN_MP_DIV_D_C #define BN_MP_CLEAR_C #define BN_MP_S_RMAP_C #endif #if defined(BN_MP_TORADIX_N_C) #define BN_MP_ISZERO_C #define BN_MP_INIT_COPY_C #define BN_MP_DIV_D_C #define BN_MP_CLEAR_C #define BN_MP_S_RMAP_C #endif #if defined(BN_MP_UNSIGNED_BIN_SIZE_C) #define BN_MP_COUNT_BITS_C #endif #if defined(BN_MP_XOR_C) #define BN_MP_INIT_COPY_C #define BN_MP_CLAMP_C #define BN_MP_EXCH_C #define BN_MP_CLEAR_C #endif #if defined(BN_MP_ZERO_C) #endif #if defined(BN_PRIME_TAB_C) #endif #if defined(BN_REVERSE_C) #endif #if defined(BN_S_MP_ADD_C) #define BN_MP_GROW_C #define BN_MP_CLAMP_C #endif #if defined(BN_S_MP_EXPTMOD_C) #define BN_MP_COUNT_BITS_C #define BN_MP_INIT_C #define BN_MP_CLEAR_C #define BN_MP_REDUCE_SETUP_C #define BN_MP_REDUCE_C #define BN_MP_REDUCE_2K_SETUP_L_C #define BN_MP_REDUCE_2K_L_C #define BN_MP_MOD_C #define BN_MP_COPY_C #define BN_MP_SQR_C #define BN_MP_MUL_C #define BN_MP_SET_C #define BN_MP_EXCH_C #endif #if defined(BN_S_MP_MUL_DIGS_C) #define BN_FAST_S_MP_MUL_DIGS_C #define BN_MP_INIT_SIZE_C #define BN_MP_CLAMP_C #define BN_MP_EXCH_C #define BN_MP_CLEAR_C #endif #if defined(BN_S_MP_MUL_HIGH_DIGS_C) #define BN_FAST_S_MP_MUL_HIGH_DIGS_C #define BN_MP_INIT_SIZE_C #define BN_MP_CLAMP_C #define BN_MP_EXCH_C #define BN_MP_CLEAR_C #endif #if defined(BN_S_MP_SQR_C) #define BN_MP_INIT_SIZE_C #define BN_MP_CLAMP_C #define BN_MP_EXCH_C #define BN_MP_CLEAR_C #endif #if defined(BN_S_MP_SUB_C) #define BN_MP_GROW_C #define BN_MP_CLAMP_C #endif #if defined(BNCORE_C) #endif #ifdef LTM3 #define LTM_LAST #endif /* super class file for PK algos */ /* default ... include all MPI */ #define LTM_ALL /* RSA only (does not support DH/DSA/ECC) */ /* #define SC_RSA_1 */ /* For reference.... On an Athlon64 optimizing for speed... LTM's mpi.o with all functions [striped] is 142KiB in size. */ /* Works for RSA only, mpi.o is 68KiB */ #ifdef SC_RSA_1 #define BN_MP_SHRINK_C #define BN_MP_LCM_C #define BN_MP_PRIME_RANDOM_EX_C #define BN_MP_INVMOD_C #define BN_MP_GCD_C #define BN_MP_MOD_C #define BN_MP_MULMOD_C #define BN_MP_ADDMOD_C #define BN_MP_EXPTMOD_C #define BN_MP_SET_INT_C #define BN_MP_INIT_MULTI_C #define BN_MP_CLEAR_MULTI_C #define BN_MP_UNSIGNED_BIN_SIZE_C #define BN_MP_TO_UNSIGNED_BIN_C #define BN_MP_MOD_D_C #define BN_MP_PRIME_RABIN_MILLER_TRIALS_C #define BN_REVERSE_C #define BN_PRIME_TAB_C /* other modifiers */ #define BN_MP_DIV_SMALL /* Slower division, not critical */ /* here we are on the last pass so we turn things off. The functions classes are still there * but we remove them specifically from the build. This also invokes tweaks in functions * like removing support for even moduli, etc... */ #ifdef LTM_LAST #undef BN_MP_TOOM_MUL_C #undef BN_MP_TOOM_SQR_C #undef BN_MP_KARATSUBA_MUL_C #undef BN_MP_KARATSUBA_SQR_C #undef BN_MP_REDUCE_C #undef BN_MP_REDUCE_SETUP_C #undef BN_MP_DR_IS_MODULUS_C #undef BN_MP_DR_SETUP_C #undef BN_MP_DR_REDUCE_C #undef BN_MP_REDUCE_IS_2K_C #undef BN_MP_REDUCE_2K_SETUP_C #undef BN_MP_REDUCE_2K_C #undef BN_S_MP_EXPTMOD_C #undef BN_MP_DIV_3_C #undef BN_S_MP_MUL_HIGH_DIGS_C #undef BN_FAST_S_MP_MUL_HIGH_DIGS_C #undef BN_FAST_MP_INVMOD_C /* To safely undefine these you have to make sure your RSA key won't exceed the Comba threshold * which is roughly 255 digits [7140 bits for 32-bit machines, 15300 bits for 64-bit machines] * which means roughly speaking you can handle upto 2536-bit RSA keys with these defined without * trouble. */ #undef BN_S_MP_MUL_DIGS_C #undef BN_S_MP_SQR_C #undef BN_MP_MONTGOMERY_REDUCE_C #endif #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ #if !(defined(LTM1) && defined(LTM2) && defined(LTM3)) #if defined(LTM2) #define LTM3 #endif #if defined(LTM1) #define LTM2 #endif #define LTM1 #if defined(LTM_ALL) #define BN_ERROR_C #define BN_FAST_MP_INVMOD_C #define BN_FAST_MP_MONTGOMERY_REDUCE_C #define BN_FAST_S_MP_MUL_DIGS_C #define BN_FAST_S_MP_MUL_HIGH_DIGS_C #define BN_FAST_S_MP_SQR_C #define BN_MP_2EXPT_C #define BN_MP_ABS_C #define BN_MP_ADD_C #define BN_MP_ADD_D_C #define BN_MP_ADDMOD_C #define BN_MP_AND_C #define BN_MP_CLAMP_C #define BN_MP_CLEAR_C #define BN_MP_CLEAR_MULTI_C #define BN_MP_CMP_C #define BN_MP_CMP_D_C #define BN_MP_CMP_MAG_C #define BN_MP_CNT_LSB_C #define BN_MP_COPY_C #define BN_MP_COUNT_BITS_C #define BN_MP_DIV_C #define BN_MP_DIV_2_C #define BN_MP_DIV_2D_C #define BN_MP_DIV_3_C #define BN_MP_DIV_D_C #define BN_MP_DR_IS_MODULUS_C #define BN_MP_DR_REDUCE_C #define BN_MP_DR_SETUP_C #define BN_MP_EXCH_C #define BN_MP_EXPORT_C #define BN_MP_EXPT_D_C #define BN_MP_EXPT_D_EX_C #define BN_MP_EXPTMOD_C #define BN_MP_EXPTMOD_FAST_C #define BN_MP_EXTEUCLID_C #define BN_MP_FREAD_C #define BN_MP_FWRITE_C #define BN_MP_GCD_C #define BN_MP_GET_INT_C #define BN_MP_GET_LONG_C #define BN_MP_GET_LONG_LONG_C #define BN_MP_GROW_C #define BN_MP_IMPORT_C #define BN_MP_INIT_C #define BN_MP_INIT_COPY_C #define BN_MP_INIT_MULTI_C #define BN_MP_INIT_SET_C #define BN_MP_INIT_SET_INT_C #define BN_MP_INIT_SIZE_C #define BN_MP_INVMOD_C #define BN_MP_INVMOD_SLOW_C #define BN_MP_IS_SQUARE_C #define BN_MP_JACOBI_C #define BN_MP_KARATSUBA_MUL_C #define BN_MP_KARATSUBA_SQR_C #define BN_MP_LCM_C #define BN_MP_LSHD_C #define BN_MP_MOD_C #define BN_MP_MOD_2D_C #define BN_MP_MOD_D_C #define BN_MP_MONTGOMERY_CALC_NORMALIZATION_C #define BN_MP_MONTGOMERY_REDUCE_C #define BN_MP_MONTGOMERY_SETUP_C #define BN_MP_MUL_C #define BN_MP_MUL_2_C #define BN_MP_MUL_2D_C #define BN_MP_MUL_D_C #define BN_MP_MULMOD_C #define BN_MP_N_ROOT_C #define BN_MP_N_ROOT_EX_C #define BN_MP_NEG_C #define BN_MP_OR_C #define BN_MP_PRIME_FERMAT_C #define BN_MP_PRIME_IS_DIVISIBLE_C #define BN_MP_PRIME_IS_PRIME_C #define BN_MP_PRIME_MILLER_RABIN_C #define BN_MP_PRIME_NEXT_PRIME_C #define BN_MP_PRIME_RABIN_MILLER_TRIALS_C #define BN_MP_PRIME_RANDOM_EX_C #define BN_MP_RADIX_SIZE_C #define BN_MP_RADIX_SMAP_C #define BN_MP_RAND_C #define BN_MP_READ_RADIX_C #define BN_MP_READ_SIGNED_BIN_C #define BN_MP_READ_UNSIGNED_BIN_C #define BN_MP_REDUCE_C #define BN_MP_REDUCE_2K_C #define BN_MP_REDUCE_2K_L_C #define BN_MP_REDUCE_2K_SETUP_C #define BN_MP_REDUCE_2K_SETUP_L_C #define BN_MP_REDUCE_IS_2K_C #define BN_MP_REDUCE_IS_2K_L_C #define BN_MP_REDUCE_SETUP_C #define BN_MP_RSHD_C #define BN_MP_SET_C #define BN_MP_SET_INT_C #define BN_MP_SET_LONG_C #define BN_MP_SET_LONG_LONG_C #define BN_MP_SHRINK_C #define BN_MP_SIGNED_BIN_SIZE_C #define BN_MP_SQR_C #define BN_MP_SQRMOD_C #define BN_MP_SQRT_C #define BN_MP_SQRTMOD_PRIME_C #define BN_MP_SUB_C #define BN_MP_SUB_D_C #define BN_MP_SUBMOD_C #define BN_MP_TO_SIGNED_BIN_C #define BN_MP_TO_SIGNED_BIN_N_C #define BN_MP_TO_UNSIGNED_BIN_C #define BN_MP_TO_UNSIGNED_BIN_N_C #define BN_MP_TOOM_MUL_C #define BN_MP_TOOM_SQR_C #define BN_MP_TORADIX_C #define BN_MP_TORADIX_N_C #define BN_MP_UNSIGNED_BIN_SIZE_C #define BN_MP_XOR_C #define BN_MP_ZERO_C #define BN_PRIME_TAB_C #define BN_REVERSE_C #define BN_S_MP_ADD_C #define BN_S_MP_EXPTMOD_C #define BN_S_MP_MUL_DIGS_C #define BN_S_MP_MUL_HIGH_DIGS_C #define BN_S_MP_SQR_C #define BN_S_MP_SUB_C #define BNCORE_C #endif #if defined(BN_ERROR_C) #define BN_MP_ERROR_TO_STRING_C #endif #if defined(BN_FAST_MP_INVMOD_C) #define BN_MP_ISEVEN_C #define BN_MP_INIT_MULTI_C #define BN_MP_COPY_C #define BN_MP_MOD_C #define BN_MP_SET_C #define BN_MP_DIV_2_C #define BN_MP_ISODD_C #define BN_MP_SUB_C #define BN_MP_CMP_C #define BN_MP_ISZERO_C #define BN_MP_CMP_D_C #define BN_MP_ADD_C #define BN_MP_EXCH_C #define BN_MP_CLEAR_MULTI_C #endif #if defined(BN_FAST_MP_MONTGOMERY_REDUCE_C) #define BN_MP_GROW_C #define BN_MP_RSHD_C #define BN_MP_CLAMP_C #define BN_MP_CMP_MAG_C #define BN_S_MP_SUB_C #endif #if defined(BN_FAST_S_MP_MUL_DIGS_C) #define BN_MP_GROW_C #define BN_MP_CLAMP_C #endif #if defined(BN_FAST_S_MP_MUL_HIGH_DIGS_C) #define BN_MP_GROW_C #define BN_MP_CLAMP_C #endif #if defined(BN_FAST_S_MP_SQR_C) #define BN_MP_GROW_C #define BN_MP_CLAMP_C #endif #if defined(BN_MP_2EXPT_C) #define BN_MP_ZERO_C #define BN_MP_GROW_C #endif #if defined(BN_MP_ABS_C) #define BN_MP_COPY_C #endif #if defined(BN_MP_ADD_C) #define BN_S_MP_ADD_C #define BN_MP_CMP_MAG_C #define BN_S_MP_SUB_C #endif #if defined(BN_MP_ADD_D_C) #define BN_MP_GROW_C #define BN_MP_SUB_D_C #define BN_MP_CLAMP_C #endif #if defined(BN_MP_ADDMOD_C) #define BN_MP_INIT_C #define BN_MP_ADD_C #define BN_MP_CLEAR_C #define BN_MP_MOD_C #endif #if defined(BN_MP_AND_C) #define BN_MP_INIT_COPY_C #define BN_MP_CLAMP_C #define BN_MP_EXCH_C #define BN_MP_CLEAR_C #endif #if defined(BN_MP_CLAMP_C) #endif #if defined(BN_MP_CLEAR_C) #endif #if defined(BN_MP_CLEAR_MULTI_C) #define BN_MP_CLEAR_C #endif #if defined(BN_MP_CMP_C) #define BN_MP_CMP_MAG_C #endif #if defined(BN_MP_CMP_D_C) #endif #if defined(BN_MP_CMP_MAG_C) #endif #if defined(BN_MP_CNT_LSB_C) #define BN_MP_ISZERO_C #endif #if defined(BN_MP_COPY_C) #define BN_MP_GROW_C #endif #if defined(BN_MP_COUNT_BITS_C) #endif #if defined(BN_MP_DIV_C) #define BN_MP_ISZERO_C #define BN_MP_CMP_MAG_C #define BN_MP_COPY_C #define BN_MP_ZERO_C #define BN_MP_INIT_MULTI_C #define BN_MP_SET_C #define BN_MP_COUNT_BITS_C #define BN_MP_ABS_C #define BN_MP_MUL_2D_C #define BN_MP_CMP_C #define BN_MP_SUB_C #define BN_MP_ADD_C #define BN_MP_DIV_2D_C #define BN_MP_EXCH_C #define BN_MP_CLEAR_MULTI_C #define BN_MP_INIT_SIZE_C #define BN_MP_INIT_C #define BN_MP_INIT_COPY_C #define BN_MP_LSHD_C #define BN_MP_RSHD_C #define BN_MP_MUL_D_C #define BN_MP_CLAMP_C #define BN_MP_CLEAR_C #endif #if defined(BN_MP_DIV_2_C) #define BN_MP_GROW_C #define BN_MP_CLAMP_C #endif #if defined(BN_MP_DIV_2D_C) #define BN_MP_COPY_C #define BN_MP_ZERO_C #define BN_MP_INIT_C #define BN_MP_MOD_2D_C #define BN_MP_CLEAR_C #define BN_MP_RSHD_C #define BN_MP_CLAMP_C #define BN_MP_EXCH_C #endif #if defined(BN_MP_DIV_3_C) #define BN_MP_INIT_SIZE_C #define BN_MP_CLAMP_C #define BN_MP_EXCH_C #define BN_MP_CLEAR_C #endif #if defined(BN_MP_DIV_D_C) #define BN_MP_ISZERO_C #define BN_MP_COPY_C #define BN_MP_DIV_2D_C #define BN_MP_DIV_3_C #define BN_MP_INIT_SIZE_C #define BN_MP_CLAMP_C #define BN_MP_EXCH_C #define BN_MP_CLEAR_C #endif #if defined(BN_MP_DR_IS_MODULUS_C) #endif #if defined(BN_MP_DR_REDUCE_C) #define BN_MP_GROW_C #define BN_MP_CLAMP_C #define BN_MP_CMP_MAG_C #define BN_S_MP_SUB_C #endif #if defined(BN_MP_DR_SETUP_C) #endif #if defined(BN_MP_EXCH_C) #endif #if defined(BN_MP_EXPORT_C) #define BN_MP_INIT_COPY_C #define BN_MP_COUNT_BITS_C #define BN_MP_DIV_2D_C #define BN_MP_CLEAR_C #endif #if defined(BN_MP_EXPT_D_C) #define BN_MP_EXPT_D_EX_C #endif #if defined(BN_MP_EXPT_D_EX_C) #define BN_MP_INIT_COPY_C #define BN_MP_SET_C #define BN_MP_MUL_C #define BN_MP_CLEAR_C #define BN_MP_SQR_C #endif #if defined(BN_MP_EXPTMOD_C) #define BN_MP_INIT_C #define BN_MP_INVMOD_C #define BN_MP_CLEAR_C #define BN_MP_ABS_C #define BN_MP_CLEAR_MULTI_C #define BN_MP_REDUCE_IS_2K_L_C #define BN_S_MP_EXPTMOD_C #define BN_MP_DR_IS_MODULUS_C #define BN_MP_REDUCE_IS_2K_C #define BN_MP_ISODD_C #define BN_MP_EXPTMOD_FAST_C #endif #if defined(BN_MP_EXPTMOD_FAST_C) #define BN_MP_COUNT_BITS_C #define BN_MP_INIT_C #define BN_MP_CLEAR_C #define BN_MP_MONTGOMERY_SETUP_C #define BN_FAST_MP_MONTGOMERY_REDUCE_C #define BN_MP_MONTGOMERY_REDUCE_C #define BN_MP_DR_SETUP_C #define BN_MP_DR_REDUCE_C #define BN_MP_REDUCE_2K_SETUP_C #define BN_MP_REDUCE_2K_C #define BN_MP_MONTGOMERY_CALC_NORMALIZATION_C #define BN_MP_MULMOD_C #define BN_MP_SET_C #define BN_MP_MOD_C #define BN_MP_COPY_C #define BN_MP_SQR_C #define BN_MP_MUL_C #define BN_MP_EXCH_C #endif #if defined(BN_MP_EXTEUCLID_C) #define BN_MP_INIT_MULTI_C #define BN_MP_SET_C #define BN_MP_COPY_C #define BN_MP_ISZERO_C #define BN_MP_DIV_C #define BN_MP_MUL_C #define BN_MP_SUB_C #define BN_MP_NEG_C #define BN_MP_EXCH_C #define BN_MP_CLEAR_MULTI_C #endif #if defined(BN_MP_FREAD_C) #define BN_MP_ZERO_C #define BN_MP_S_RMAP_C #define BN_MP_MUL_D_C #define BN_MP_ADD_D_C #define BN_MP_CMP_D_C #endif #if defined(BN_MP_FWRITE_C) #define BN_MP_RADIX_SIZE_C #define BN_MP_TORADIX_C #endif #if defined(BN_MP_GCD_C) #define BN_MP_ISZERO_C #define BN_MP_ABS_C #define BN_MP_INIT_COPY_C #define BN_MP_CNT_LSB_C #define BN_MP_DIV_2D_C #define BN_MP_CMP_MAG_C #define BN_MP_EXCH_C #define BN_S_MP_SUB_C #define BN_MP_MUL_2D_C #define BN_MP_CLEAR_C #endif #if defined(BN_MP_GET_INT_C) #endif #if defined(BN_MP_GET_LONG_C) #endif #if defined(BN_MP_GET_LONG_LONG_C) #endif #if defined(BN_MP_GROW_C) #endif #if defined(BN_MP_IMPORT_C) #define BN_MP_ZERO_C #define BN_MP_MUL_2D_C #define BN_MP_CLAMP_C #endif #if defined(BN_MP_INIT_C) #endif #if defined(BN_MP_INIT_COPY_C) #define BN_MP_INIT_SIZE_C #define BN_MP_COPY_C #endif #if defined(BN_MP_INIT_MULTI_C) #define BN_MP_ERR_C #define BN_MP_INIT_C #define BN_MP_CLEAR_C #endif #if defined(BN_MP_INIT_SET_C) #define BN_MP_INIT_C #define BN_MP_SET_C #endif #if defined(BN_MP_INIT_SET_INT_C) #define BN_MP_INIT_C #define BN_MP_SET_INT_C #endif #if defined(BN_MP_INIT_SIZE_C) #define BN_MP_INIT_C #endif #if defined(BN_MP_INVMOD_C) #define BN_MP_ISZERO_C #define BN_MP_ISODD_C #define BN_FAST_MP_INVMOD_C #define BN_MP_INVMOD_SLOW_C #endif #if defined(BN_MP_INVMOD_SLOW_C) #define BN_MP_ISZERO_C #define BN_MP_INIT_MULTI_C #define BN_MP_MOD_C #define BN_MP_COPY_C #define BN_MP_ISEVEN_C #define BN_MP_SET_C #define BN_MP_DIV_2_C #define BN_MP_ISODD_C #define BN_MP_ADD_C #define BN_MP_SUB_C #define BN_MP_CMP_C #define BN_MP_CMP_D_C #define BN_MP_CMP_MAG_C #define BN_MP_EXCH_C #define BN_MP_CLEAR_MULTI_C #endif #if defined(BN_MP_IS_SQUARE_C) #define BN_MP_MOD_D_C #define BN_MP_INIT_SET_INT_C #define BN_MP_MOD_C #define BN_MP_GET_INT_C #define BN_MP_SQRT_C #define BN_MP_SQR_C #define BN_MP_CMP_MAG_C #define BN_MP_CLEAR_C #endif #if defined(BN_MP_JACOBI_C) #define BN_MP_CMP_D_C #define BN_MP_ISZERO_C #define BN_MP_INIT_COPY_C #define BN_MP_CNT_LSB_C #define BN_MP_DIV_2D_C #define BN_MP_MOD_C #define BN_MP_CLEAR_C #endif #if defined(BN_MP_KARATSUBA_MUL_C) #define BN_MP_MUL_C #define BN_MP_INIT_SIZE_C #define BN_MP_CLAMP_C #define BN_S_MP_ADD_C #define BN_MP_ADD_C #define BN_S_MP_SUB_C #define BN_MP_LSHD_C #define BN_MP_CLEAR_C #endif #if defined(BN_MP_KARATSUBA_SQR_C) #define BN_MP_INIT_SIZE_C #define BN_MP_CLAMP_C #define BN_MP_SQR_C #define BN_S_MP_ADD_C #define BN_S_MP_SUB_C #define BN_MP_LSHD_C #define BN_MP_ADD_C #define BN_MP_CLEAR_C #endif #if defined(BN_MP_LCM_C) #define BN_MP_INIT_MULTI_C #define BN_MP_GCD_C #define BN_MP_CMP_MAG_C #define BN_MP_DIV_C #define BN_MP_MUL_C #define BN_MP_CLEAR_MULTI_C #endif #if defined(BN_MP_LSHD_C) #define BN_MP_GROW_C #define BN_MP_RSHD_C #endif #if defined(BN_MP_MOD_C) #define BN_MP_INIT_C #define BN_MP_DIV_C #define BN_MP_CLEAR_C #define BN_MP_ISZERO_C #define BN_MP_EXCH_C #define BN_MP_ADD_C #endif #if defined(BN_MP_MOD_2D_C) #define BN_MP_ZERO_C #define BN_MP_COPY_C #define BN_MP_CLAMP_C #endif #if defined(BN_MP_MOD_D_C) #define BN_MP_DIV_D_C #endif #if defined(BN_MP_MONTGOMERY_CALC_NORMALIZATION_C) #define BN_MP_COUNT_BITS_C #define BN_MP_2EXPT_C #define BN_MP_SET_C #define BN_MP_MUL_2_C #define BN_MP_CMP_MAG_C #define BN_S_MP_SUB_C #endif #if defined(BN_MP_MONTGOMERY_REDUCE_C) #define BN_FAST_MP_MONTGOMERY_REDUCE_C #define BN_MP_GROW_C #define BN_MP_CLAMP_C #define BN_MP_RSHD_C #define BN_MP_CMP_MAG_C #define BN_S_MP_SUB_C #endif #if defined(BN_MP_MONTGOMERY_SETUP_C) #endif #if defined(BN_MP_MUL_C) #define BN_MP_TOOM_MUL_C #define BN_MP_KARATSUBA_MUL_C #define BN_FAST_S_MP_MUL_DIGS_C #define BN_S_MP_MUL_C #define BN_S_MP_MUL_DIGS_C #endif #if defined(BN_MP_MUL_2_C) #define BN_MP_GROW_C #endif #if defined(BN_MP_MUL_2D_C) #define BN_MP_COPY_C #define BN_MP_GROW_C #define BN_MP_LSHD_C #define BN_MP_CLAMP_C #endif #if defined(BN_MP_MUL_D_C) #define BN_MP_GROW_C #define BN_MP_CLAMP_C #endif #if defined(BN_MP_MULMOD_C) #define BN_MP_INIT_C #define BN_MP_MUL_C #define BN_MP_CLEAR_C #define BN_MP_MOD_C #endif #if defined(BN_MP_N_ROOT_C) #define BN_MP_N_ROOT_EX_C #endif #if defined(BN_MP_N_ROOT_EX_C) #define BN_MP_INIT_C #define BN_MP_SET_C #define BN_MP_COPY_C #define BN_MP_EXPT_D_EX_C #define BN_MP_MUL_C #define BN_MP_SUB_C #define BN_MP_MUL_D_C #define BN_MP_DIV_C #define BN_MP_CMP_C #define BN_MP_SUB_D_C #define BN_MP_EXCH_C #define BN_MP_CLEAR_C #endif #if defined(BN_MP_NEG_C) #define BN_MP_COPY_C #define BN_MP_ISZERO_C #endif #if defined(BN_MP_OR_C) #define BN_MP_INIT_COPY_C #define BN_MP_CLAMP_C #define BN_MP_EXCH_C #define BN_MP_CLEAR_C #endif #if defined(BN_MP_PRIME_FERMAT_C) #define BN_MP_CMP_D_C #define BN_MP_INIT_C #define BN_MP_EXPTMOD_C #define BN_MP_CMP_C #define BN_MP_CLEAR_C #endif #if defined(BN_MP_PRIME_IS_DIVISIBLE_C) #define BN_MP_MOD_D_C #endif #if defined(BN_MP_PRIME_IS_PRIME_C) #define BN_MP_CMP_D_C #define BN_MP_PRIME_IS_DIVISIBLE_C #define BN_MP_INIT_C #define BN_MP_SET_C #define BN_MP_PRIME_MILLER_RABIN_C #define BN_MP_CLEAR_C #endif #if defined(BN_MP_PRIME_MILLER_RABIN_C) #define BN_MP_CMP_D_C #define BN_MP_INIT_COPY_C #define BN_MP_SUB_D_C #define BN_MP_CNT_LSB_C #define BN_MP_DIV_2D_C #define BN_MP_EXPTMOD_C #define BN_MP_CMP_C #define BN_MP_SQRMOD_C #define BN_MP_CLEAR_C #endif #if defined(BN_MP_PRIME_NEXT_PRIME_C) #define BN_MP_CMP_D_C #define BN_MP_SET_C #define BN_MP_SUB_D_C #define BN_MP_ISEVEN_C #define BN_MP_MOD_D_C #define BN_MP_INIT_C #define BN_MP_ADD_D_C #define BN_MP_PRIME_MILLER_RABIN_C #define BN_MP_CLEAR_C #endif #if defined(BN_MP_PRIME_RABIN_MILLER_TRIALS_C) #endif #if defined(BN_MP_PRIME_RANDOM_EX_C) #define BN_MP_READ_UNSIGNED_BIN_C #define BN_MP_PRIME_IS_PRIME_C #define BN_MP_SUB_D_C #define BN_MP_DIV_2_C #define BN_MP_MUL_2_C #define BN_MP_ADD_D_C #endif #if defined(BN_MP_RADIX_SIZE_C) #define BN_MP_ISZERO_C #define BN_MP_COUNT_BITS_C #define BN_MP_INIT_COPY_C #define BN_MP_DIV_D_C #define BN_MP_CLEAR_C #endif #if defined(BN_MP_RADIX_SMAP_C) #define BN_MP_S_RMAP_C #endif #if defined(BN_MP_RAND_C) #define BN_MP_ZERO_C #define BN_MP_ADD_D_C #define BN_MP_LSHD_C #endif #if defined(BN_MP_READ_RADIX_C) #define BN_MP_ZERO_C #define BN_MP_S_RMAP_C #define BN_MP_MUL_D_C #define BN_MP_ADD_D_C #define BN_MP_ISZERO_C #endif #if defined(BN_MP_READ_SIGNED_BIN_C) #define BN_MP_READ_UNSIGNED_BIN_C #endif #if defined(BN_MP_READ_UNSIGNED_BIN_C) #define BN_MP_GROW_C #define BN_MP_ZERO_C #define BN_MP_MUL_2D_C #define BN_MP_CLAMP_C #endif #if defined(BN_MP_REDUCE_C) #define BN_MP_REDUCE_SETUP_C #define BN_MP_INIT_COPY_C #define BN_MP_RSHD_C #define BN_MP_MUL_C #define BN_S_MP_MUL_HIGH_DIGS_C #define BN_FAST_S_MP_MUL_HIGH_DIGS_C #define BN_MP_MOD_2D_C #define BN_S_MP_MUL_DIGS_C #define BN_MP_SUB_C #define BN_MP_CMP_D_C #define BN_MP_SET_C #define BN_MP_LSHD_C #define BN_MP_ADD_C #define BN_MP_CMP_C #define BN_S_MP_SUB_C #define BN_MP_CLEAR_C #endif #if defined(BN_MP_REDUCE_2K_C) #define BN_MP_INIT_C #define BN_MP_COUNT_BITS_C #define BN_MP_DIV_2D_C #define BN_MP_MUL_D_C #define BN_S_MP_ADD_C #define BN_MP_CMP_MAG_C #define BN_S_MP_SUB_C #define BN_MP_CLEAR_C #endif #if defined(BN_MP_REDUCE_2K_L_C) #define BN_MP_INIT_C #define BN_MP_COUNT_BITS_C #define BN_MP_DIV_2D_C #define BN_MP_MUL_C #define BN_S_MP_ADD_C #define BN_MP_CMP_MAG_C #define BN_S_MP_SUB_C #define BN_MP_CLEAR_C #endif #if defined(BN_MP_REDUCE_2K_SETUP_C) #define BN_MP_INIT_C #define BN_MP_COUNT_BITS_C #define BN_MP_2EXPT_C #define BN_MP_CLEAR_C #define BN_S_MP_SUB_C #endif #if defined(BN_MP_REDUCE_2K_SETUP_L_C) #define BN_MP_INIT_C #define BN_MP_2EXPT_C #define BN_MP_COUNT_BITS_C #define BN_S_MP_SUB_C #define BN_MP_CLEAR_C #endif #if defined(BN_MP_REDUCE_IS_2K_C) #define BN_MP_REDUCE_2K_C #define BN_MP_COUNT_BITS_C #endif #if defined(BN_MP_REDUCE_IS_2K_L_C) #endif #if defined(BN_MP_REDUCE_SETUP_C) #define BN_MP_2EXPT_C #define BN_MP_DIV_C #endif #if defined(BN_MP_RSHD_C) #define BN_MP_ZERO_C #endif #if defined(BN_MP_SET_C) #define BN_MP_ZERO_C #endif #if defined(BN_MP_SET_INT_C) #define BN_MP_ZERO_C #define BN_MP_MUL_2D_C #define BN_MP_CLAMP_C #endif #if defined(BN_MP_SET_LONG_C) #endif #if defined(BN_MP_SET_LONG_LONG_C) #endif #if defined(BN_MP_SHRINK_C) #endif #if defined(BN_MP_SIGNED_BIN_SIZE_C) #define BN_MP_UNSIGNED_BIN_SIZE_C #endif #if defined(BN_MP_SQR_C) #define BN_MP_TOOM_SQR_C #define BN_MP_KARATSUBA_SQR_C #define BN_FAST_S_MP_SQR_C #define BN_S_MP_SQR_C #endif #if defined(BN_MP_SQRMOD_C) #define BN_MP_INIT_C #define BN_MP_SQR_C #define BN_MP_CLEAR_C #define BN_MP_MOD_C #endif #if defined(BN_MP_SQRT_C) #define BN_MP_N_ROOT_C #define BN_MP_ISZERO_C #define BN_MP_ZERO_C #define BN_MP_INIT_COPY_C #define BN_MP_RSHD_C #define BN_MP_DIV_C #define BN_MP_ADD_C #define BN_MP_DIV_2_C #define BN_MP_CMP_MAG_C #define BN_MP_EXCH_C #define BN_MP_CLEAR_C #endif #if defined(BN_MP_SQRTMOD_PRIME_C) #define BN_MP_CMP_D_C #define BN_MP_ZERO_C #define BN_MP_JACOBI_C #define BN_MP_INIT_MULTI_C #define BN_MP_MOD_D_C #define BN_MP_ADD_D_C #define BN_MP_DIV_2_C #define BN_MP_EXPTMOD_C #define BN_MP_COPY_C #define BN_MP_SUB_D_C #define BN_MP_ISEVEN_C #define BN_MP_SET_INT_C #define BN_MP_SQRMOD_C #define BN_MP_MULMOD_C #define BN_MP_SET_C #define BN_MP_CLEAR_MULTI_C #endif #if defined(BN_MP_SUB_C) #define BN_S_MP_ADD_C #define BN_MP_CMP_MAG_C #define BN_S_MP_SUB_C #endif #if defined(BN_MP_SUB_D_C) #define BN_MP_GROW_C #define BN_MP_ADD_D_C #define BN_MP_CLAMP_C #endif #if defined(BN_MP_SUBMOD_C) #define BN_MP_INIT_C #define BN_MP_SUB_C #define BN_MP_CLEAR_C #define BN_MP_MOD_C #endif #if defined(BN_MP_TO_SIGNED_BIN_C) #define BN_MP_TO_UNSIGNED_BIN_C #endif #if defined(BN_MP_TO_SIGNED_BIN_N_C) #define BN_MP_SIGNED_BIN_SIZE_C #define BN_MP_TO_SIGNED_BIN_C #endif #if defined(BN_MP_TO_UNSIGNED_BIN_C) #define BN_MP_INIT_COPY_C #define BN_MP_ISZERO_C #define BN_MP_DIV_2D_C #define BN_MP_CLEAR_C #endif #if defined(BN_MP_TO_UNSIGNED_BIN_N_C) #define BN_MP_UNSIGNED_BIN_SIZE_C #define BN_MP_TO_UNSIGNED_BIN_C #endif #if defined(BN_MP_TOOM_MUL_C) #define BN_MP_INIT_MULTI_C #define BN_MP_MOD_2D_C #define BN_MP_COPY_C #define BN_MP_RSHD_C #define BN_MP_MUL_C #define BN_MP_MUL_2_C #define BN_MP_ADD_C #define BN_MP_SUB_C #define BN_MP_DIV_2_C #define BN_MP_MUL_2D_C #define BN_MP_MUL_D_C #define BN_MP_DIV_3_C #define BN_MP_LSHD_C #define BN_MP_CLEAR_MULTI_C #endif #if defined(BN_MP_TOOM_SQR_C) #define BN_MP_INIT_MULTI_C #define BN_MP_MOD_2D_C #define BN_MP_COPY_C #define BN_MP_RSHD_C #define BN_MP_SQR_C #define BN_MP_MUL_2_C #define BN_MP_ADD_C #define BN_MP_SUB_C #define BN_MP_DIV_2_C #define BN_MP_MUL_2D_C #define BN_MP_MUL_D_C #define BN_MP_DIV_3_C #define BN_MP_LSHD_C #define BN_MP_CLEAR_MULTI_C #endif #if defined(BN_MP_TORADIX_C) #define BN_MP_ISZERO_C #define BN_MP_INIT_COPY_C #define BN_MP_DIV_D_C #define BN_MP_CLEAR_C #define BN_MP_S_RMAP_C #endif #if defined(BN_MP_TORADIX_N_C) #define BN_MP_ISZERO_C #define BN_MP_INIT_COPY_C #define BN_MP_DIV_D_C #define BN_MP_CLEAR_C #define BN_MP_S_RMAP_C #endif #if defined(BN_MP_UNSIGNED_BIN_SIZE_C) #define BN_MP_COUNT_BITS_C #endif #if defined(BN_MP_XOR_C) #define BN_MP_INIT_COPY_C #define BN_MP_CLAMP_C #define BN_MP_EXCH_C #define BN_MP_CLEAR_C #endif #if defined(BN_MP_ZERO_C) #endif #if defined(BN_PRIME_TAB_C) #endif #if defined(BN_REVERSE_C) #endif #if defined(BN_S_MP_ADD_C) #define BN_MP_GROW_C #define BN_MP_CLAMP_C #endif #if defined(BN_S_MP_EXPTMOD_C) #define BN_MP_COUNT_BITS_C #define BN_MP_INIT_C #define BN_MP_CLEAR_C #define BN_MP_REDUCE_SETUP_C #define BN_MP_REDUCE_C #define BN_MP_REDUCE_2K_SETUP_L_C #define BN_MP_REDUCE_2K_L_C #define BN_MP_MOD_C #define BN_MP_COPY_C #define BN_MP_SQR_C #define BN_MP_MUL_C #define BN_MP_SET_C #define BN_MP_EXCH_C #endif #if defined(BN_S_MP_MUL_DIGS_C) #define BN_FAST_S_MP_MUL_DIGS_C #define BN_MP_INIT_SIZE_C #define BN_MP_CLAMP_C #define BN_MP_EXCH_C #define BN_MP_CLEAR_C #endif #if defined(BN_S_MP_MUL_HIGH_DIGS_C) #define BN_FAST_S_MP_MUL_HIGH_DIGS_C #define BN_MP_INIT_SIZE_C #define BN_MP_CLAMP_C #define BN_MP_EXCH_C #define BN_MP_CLEAR_C #endif #if defined(BN_S_MP_SQR_C) #define BN_MP_INIT_SIZE_C #define BN_MP_CLAMP_C #define BN_MP_EXCH_C #define BN_MP_CLEAR_C #endif #if defined(BN_S_MP_SUB_C) #define BN_MP_GROW_C #define BN_MP_CLAMP_C #endif #if defined(BNCORE_C) #endif #ifdef LTM3 #define LTM_LAST #endif #else #define LTM_LAST #endif #else #define LTM_LAST #endif /* detect 64-bit mode if possible */ #if defined(__x86_64__) #if !(defined(MP_32BIT) || defined(MP_16BIT) || defined(MP_8BIT)) #define MP_64BIT #endif #endif /* some default configurations. * * A "mp_digit" must be able to hold DIGIT_BIT + 1 bits * A "mp_word" must be able to hold 2*DIGIT_BIT + 1 bits * * At the very least a mp_digit must be able to hold 7 bits * [any size beyond that is ok provided it doesn't overflow the data type] */ #ifdef MP_8BIT typedef uint8_t mp_digit; typedef uint16_t mp_word; #define MP_SIZEOF_MP_DIGIT 1 #ifdef DIGIT_BIT #error You must not define DIGIT_BIT when using MP_8BIT #endif #elif defined(MP_16BIT) typedef uint16_t mp_digit; typedef uint32_t mp_word; #define MP_SIZEOF_MP_DIGIT 2 #ifdef DIGIT_BIT #error You must not define DIGIT_BIT when using MP_16BIT #endif #elif defined(MP_64BIT) && !defined(__TINYC__) && !(defined(_WIN32) && defined(__clang__)) //< @r-lyeh bypass __int128 on clang-cl (unresolved external symbol __udivti3 otherwise) /* for GCC only on supported platforms */ #ifndef CRYPT typedef unsigned long long ulong64; typedef signed long long long64; #endif typedef uint64_t mp_digit; #if defined(_WIN32) typedef unsigned __int128 mp_word; #elif defined(__GNUC__) typedef unsigned long mp_word __attribute__ ((mode(TI))); #else /* it seems you have a problem * but we assume you can somewhere define your own uint128_t */ typedef uint128_t mp_word; #endif #define DIGIT_BIT 60 #else /* this is the default case, 28-bit digits */ /* this is to make porting into LibTomCrypt easier :-) */ #ifndef CRYPT typedef unsigned long long ulong64; typedef signed long long long64; #endif typedef uint32_t mp_digit; typedef uint64_t mp_word; #ifdef MP_31BIT /* this is an extension that uses 31-bit digits */ #define DIGIT_BIT 31 #else /* default case is 28-bit digits, defines MP_28BIT as a handy macro to test */ #define DIGIT_BIT 28 #define MP_28BIT #endif #endif /* otherwise the bits per digit is calculated automatically from the size of a mp_digit */ #ifndef DIGIT_BIT #define DIGIT_BIT (((CHAR_BIT * MP_SIZEOF_MP_DIGIT) - 1)) /* bits per digit */ typedef uint_least32_t mp_min_u32; #else typedef mp_digit mp_min_u32; #endif /* platforms that can use a better rand function */ #if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__DragonFly__) #define MP_USE_ALT_RAND 1 #endif /* use arc4random on platforms that support it */ #ifdef MP_USE_ALT_RAND #define MP_GEN_RANDOM() arc4random() #else #define MP_GEN_RANDOM() rand() #endif #define MP_DIGIT_BIT DIGIT_BIT #define MP_MASK ((((mp_digit)1) << ((mp_digit)DIGIT_BIT)) - ((mp_digit)1)) #define MP_DIGIT_MAX MP_MASK /* equalities */ #define MP_LT -1 /* less than */ #define MP_EQ 0 /* equal to */ #define MP_GT 1 /* greater than */ #define MP_ZPOS 0 /* positive integer */ #define MP_NEG 1 /* negative */ #define MP_OKAY 0 /* ok result */ #define MP_MEM -2 /* out of mem */ #define MP_VAL -3 /* invalid input */ #define MP_RANGE MP_VAL #define MP_YES 1 /* yes response */ #define MP_NO 0 /* no response */ /* Primality generation flags */ #define LTM_PRIME_BBS 0x0001 /* BBS style prime */ #define LTM_PRIME_SAFE 0x0002 /* Safe prime (p-1)/2 == prime */ #define LTM_PRIME_2MSB_ON 0x0008 /* force 2nd MSB to 1 */ typedef int mp_err; /* you'll have to tune these... */ extern int KARATSUBA_MUL_CUTOFF, KARATSUBA_SQR_CUTOFF, TOOM_MUL_CUTOFF, TOOM_SQR_CUTOFF; /* define this to use lower memory usage routines (exptmods mostly) */ /* #define MP_LOW_MEM */ /* default precision */ #ifndef MP_PREC #ifndef MP_LOW_MEM #define MP_PREC 32 /* default digits of precision */ #else #define MP_PREC 8 /* default digits of precision */ #endif #endif /* size of comba arrays, should be at least 2 * 2**(BITS_PER_WORD - BITS_PER_DIGIT*2) */ #define MP_WARRAY (1 << (((sizeof(mp_word) * CHAR_BIT) - (2 * DIGIT_BIT)) + 1)) /* the infamous mp_int structure */ typedef struct { int used, alloc, sign; mp_digit *dp; } mp_int; /* callback for mp_prime_random, should fill dst with random bytes and return how many read [upto len] */ typedef int ltm_prime_callback (unsigned char *dst, int len, void *dat); #define USED(m) ((m)->used) #define DIGIT(m, k) ((m)->dp[(k)]) #define SIGN(m) ((m)->sign) /* error code to char* string */ const char *mp_error_to_string(int code); /* ---> init and deinit bignum functions <--- */ /* init a bignum */ int mp_init(mp_int *a); /* free a bignum */ void mp_clear(mp_int *a); /* init a null terminated series of arguments */ int mp_init_multi(mp_int *mp, ...); /* clear a null terminated series of arguments */ void mp_clear_multi(mp_int *mp, ...); /* exchange two ints */ void mp_exch(mp_int *a, mp_int *b); /* shrink ram required for a bignum */ int mp_shrink(mp_int *a); /* grow an int to a given size */ int mp_grow(mp_int *a, int size); /* init to a given number of digits */ int mp_init_size(mp_int *a, int size); /* ---> Basic Manipulations <--- */ #define mp_iszero(a) (((a)->used == 0) ? MP_YES : MP_NO) #define mp_iseven(a) ((((a)->used > 0) && (((a)->dp[0] & 1u) == 0u)) ? MP_YES : MP_NO) #define mp_isodd(a) ((((a)->used > 0) && (((a)->dp[0] & 1u) == 1u)) ? MP_YES : MP_NO) #define mp_isneg(a) (((a)->sign != MP_ZPOS) ? MP_YES : MP_NO) /* set to zero */ void mp_zero(mp_int *a); /* set to a digit */ void mp_set(mp_int *a, mp_digit b); /* set a 32-bit const */ int mp_set_int(mp_int *a, unsigned long b); /* set a platform dependent unsigned long value */ int mp_set_long(mp_int *a, unsigned long b); /* set a platform dependent unsigned long long value */ int mp_set_long_long(mp_int *a, unsigned long long b); /* get a 32-bit value */ unsigned long mp_get_int(mp_int *a); /* get a platform dependent unsigned long value */ unsigned long mp_get_long(mp_int *a); /* get a platform dependent unsigned long long value */ unsigned long long mp_get_long_long(mp_int *a); /* initialize and set a digit */ int mp_init_set(mp_int *a, mp_digit b); /* initialize and set 32-bit value */ int mp_init_set_int(mp_int *a, unsigned long b); /* copy, b = a */ int mp_copy(mp_int *a, mp_int *b); /* inits and copies, a = b */ int mp_init_copy(mp_int *a, mp_int *b); /* trim unused digits */ void mp_clamp(mp_int *a); /* import binary data */ int mp_import(mp_int *rop, size_t count, int order, size_t size, int endian, size_t nails, const void *op); /* export binary data */ int mp_export(void *rop, size_t *countp, int order, size_t size, int endian, size_t nails, mp_int *op); /* ---> digit manipulation <--- */ /* right shift by "b" digits */ void mp_rshd(mp_int *a, int b); /* left shift by "b" digits */ int mp_lshd(mp_int *a, int b); /* c = a / 2**b, implemented as c = a >> b */ int mp_div_2d(mp_int *a, int b, mp_int *c, mp_int *d); /* b = a/2 */ int mp_div_2(mp_int *a, mp_int *b); /* c = a * 2**b, implemented as c = a << b */ int mp_mul_2d(mp_int *a, int b, mp_int *c); /* b = a*2 */ int mp_mul_2(mp_int *a, mp_int *b); /* c = a mod 2**b */ int mp_mod_2d(mp_int *a, int b, mp_int *c); /* computes a = 2**b */ int mp_2expt(mp_int *a, int b); /* Counts the number of lsbs which are zero before the first zero bit */ int mp_cnt_lsb(mp_int *a); /* I Love Earth! */ /* makes a pseudo-random int of a given size */ int mp_rand(mp_int *a, int digits); /* ---> binary operations <--- */ /* c = a XOR b */ int mp_xor(mp_int *a, mp_int *b, mp_int *c); /* c = a OR b */ int mp_or(mp_int *a, mp_int *b, mp_int *c); /* c = a AND b */ int mp_and(mp_int *a, mp_int *b, mp_int *c); /* ---> Basic arithmetic <--- */ /* b = -a */ int mp_neg(mp_int *a, mp_int *b); /* b = |a| */ int mp_abs(mp_int *a, mp_int *b); /* compare a to b */ int mp_cmp(mp_int *a, mp_int *b); /* compare |a| to |b| */ int mp_cmp_mag(mp_int *a, mp_int *b); /* c = a + b */ int mp_add(mp_int *a, mp_int *b, mp_int *c); /* c = a - b */ int mp_sub(mp_int *a, mp_int *b, mp_int *c); /* c = a * b */ int mp_mul(mp_int *a, mp_int *b, mp_int *c); /* b = a*a */ int mp_sqr(mp_int *a, mp_int *b); /* a/b => cb + d == a */ int mp_div(mp_int *a, mp_int *b, mp_int *c, mp_int *d); /* c = a mod b, 0 <= c < b */ int mp_mod(mp_int *a, mp_int *b, mp_int *c); /* ---> single digit functions <--- */ /* compare against a single digit */ int mp_cmp_d(mp_int *a, mp_digit b); /* c = a + b */ int mp_add_d(mp_int *a, mp_digit b, mp_int *c); /* c = a - b */ int mp_sub_d(mp_int *a, mp_digit b, mp_int *c); /* c = a * b */ int mp_mul_d(mp_int *a, mp_digit b, mp_int *c); /* a/b => cb + d == a */ int mp_div_d(mp_int *a, mp_digit b, mp_int *c, mp_digit *d); /* a/3 => 3c + d == a */ int mp_div_3(mp_int *a, mp_int *c, mp_digit *d); /* c = a**b */ int mp_expt_d(mp_int *a, mp_digit b, mp_int *c); int mp_expt_d_ex(mp_int *a, mp_digit b, mp_int *c, int fast); /* c = a mod b, 0 <= c < b */ int mp_mod_d(mp_int *a, mp_digit b, mp_digit *c); /* ---> number theory <--- */ /* d = a + b (mod c) */ int mp_addmod(mp_int *a, mp_int *b, mp_int *c, mp_int *d); /* d = a - b (mod c) */ int mp_submod(mp_int *a, mp_int *b, mp_int *c, mp_int *d); /* d = a * b (mod c) */ int mp_mulmod(mp_int *a, mp_int *b, mp_int *c, mp_int *d); /* c = a * a (mod b) */ int mp_sqrmod(mp_int *a, mp_int *b, mp_int *c); /* c = 1/a (mod b) */ int mp_invmod(mp_int *a, mp_int *b, mp_int *c); /* c = (a, b) */ int mp_gcd(mp_int *a, mp_int *b, mp_int *c); /* produces value such that U1*a + U2*b = U3 */ int mp_exteuclid(mp_int *a, mp_int *b, mp_int *U1, mp_int *U2, mp_int *U3); /* c = [a, b] or (a*b)/(a, b) */ int mp_lcm(mp_int *a, mp_int *b, mp_int *c); /* finds one of the b'th root of a, such that |c|**b <= |a| * * returns error if a < 0 and b is even */ int mp_n_root(mp_int *a, mp_digit b, mp_int *c); int mp_n_root_ex(mp_int *a, mp_digit b, mp_int *c, int fast); /* special sqrt algo */ int mp_sqrt(mp_int *arg, mp_int *ret); /* special sqrt (mod prime) */ int mp_sqrtmod_prime(mp_int *arg, mp_int *prime, mp_int *ret); /* is number a square? */ int mp_is_square(mp_int *arg, int *ret); /* computes the jacobi c = (a | n) (or Legendre if b is prime) */ int mp_jacobi(mp_int *a, mp_int *n, int *c); /* used to setup the Barrett reduction for a given modulus b */ int mp_reduce_setup(mp_int *a, mp_int *b); /* Barrett Reduction, computes a (mod b) with a precomputed value c * * Assumes that 0 < a <= b*b, note if 0 > a > -(b*b) then you can merely * compute the reduction as -1 * mp_reduce(mp_abs(a)) [pseudo code]. */ int mp_reduce(mp_int *a, mp_int *b, mp_int *c); /* setups the montgomery reduction */ int mp_montgomery_setup(mp_int *a, mp_digit *mp); /* computes a = B**n mod b without division or multiplication useful for * normalizing numbers in a Montgomery system. */ int mp_montgomery_calc_normalization(mp_int *a, mp_int *b); /* computes x/R == x (mod N) via Montgomery Reduction */ int mp_montgomery_reduce(mp_int *a, mp_int *m, mp_digit mp); /* returns 1 if a is a valid DR modulus */ int mp_dr_is_modulus(mp_int *a); /* sets the value of "d" required for mp_dr_reduce */ void mp_dr_setup(mp_int *a, mp_digit *d); /* reduces a modulo b using the Diminished Radix method */ int mp_dr_reduce(mp_int *a, mp_int *b, mp_digit mp); /* returns true if a can be reduced with mp_reduce_2k */ int mp_reduce_is_2k(mp_int *a); /* determines k value for 2k reduction */ int mp_reduce_2k_setup(mp_int *a, mp_digit *d); /* reduces a modulo b where b is of the form 2**p - k [0 <= a] */ int mp_reduce_2k(mp_int *a, mp_int *n, mp_digit d); /* returns true if a can be reduced with mp_reduce_2k_l */ int mp_reduce_is_2k_l(mp_int *a); /* determines k value for 2k reduction */ int mp_reduce_2k_setup_l(mp_int *a, mp_int *d); /* reduces a modulo b where b is of the form 2**p - k [0 <= a] */ int mp_reduce_2k_l(mp_int *a, mp_int *n, mp_int *d); /* d = a**b (mod c) */ int mp_exptmod(mp_int *a, mp_int *b, mp_int *c, mp_int *d); /* ---> Primes <--- */ /* number of primes */ #ifdef MP_8BIT #define PRIME_SIZE 31 #else #define PRIME_SIZE 256 #endif /* table of first PRIME_SIZE primes */ extern const mp_digit ltm_prime_tab[PRIME_SIZE]; /* result=1 if a is divisible by one of the first PRIME_SIZE primes */ int mp_prime_is_divisible(mp_int *a, int *result); /* performs one Fermat test of "a" using base "b". * Sets result to 0 if composite or 1 if probable prime */ int mp_prime_fermat(mp_int *a, mp_int *b, int *result); /* performs one Miller-Rabin test of "a" using base "b". * Sets result to 0 if composite or 1 if probable prime */ int mp_prime_miller_rabin(mp_int *a, mp_int *b, int *result); /* This gives [for a given bit size] the number of trials required * such that Miller-Rabin gives a prob of failure lower than 2^-96 */ int mp_prime_rabin_miller_trials(int size); /* performs t rounds of Miller-Rabin on "a" using the first * t prime bases. Also performs an initial sieve of trial * division. Determines if "a" is prime with probability * of error no more than (1/4)**t. * * Sets result to 1 if probably prime, 0 otherwise */ int mp_prime_is_prime(mp_int *a, int t, int *result); /* finds the next prime after the number "a" using "t" trials * of Miller-Rabin. * * bbs_style = 1 means the prime must be congruent to 3 mod 4 */ int mp_prime_next_prime(mp_int *a, int t, int bbs_style); /* makes a truly random prime of a given size (bytes), * call with bbs = 1 if you want it to be congruent to 3 mod 4 * * You have to supply a callback which fills in a buffer with random bytes. "dat" is a parameter you can * have passed to the callback (e.g. a state or something). This function doesn't use "dat" itself * so it can be NULL * * The prime generated will be larger than 2^(8*size). */ #define mp_prime_random(a, t, size, bbs, cb, dat) mp_prime_random_ex(a, t, ((size) * 8) + 1, (bbs == 1) ? LTM_PRIME_BBS : 0, cb, dat) /* makes a truly random prime of a given size (bits), * * Flags are as follows: * * LTM_PRIME_BBS - make prime congruent to 3 mod 4 * LTM_PRIME_SAFE - make sure (p-1)/2 is prime as well (implies LTM_PRIME_BBS) * LTM_PRIME_2MSB_ON - make the 2nd highest bit one * * You have to supply a callback which fills in a buffer with random bytes. "dat" is a parameter you can * have passed to the callback (e.g. a state or something). This function doesn't use "dat" itself * so it can be NULL * */ int mp_prime_random_ex(mp_int *a, int t, int size, int flags, ltm_prime_callback cb, void *dat); /* ---> radix conversion <--- */ int mp_count_bits(mp_int *a); int mp_unsigned_bin_size(mp_int *a); int mp_read_unsigned_bin(mp_int *a, const unsigned char *b, int c); int mp_to_unsigned_bin(mp_int *a, unsigned char *b); int mp_to_unsigned_bin_n(mp_int *a, unsigned char *b, unsigned long *outlen); int mp_signed_bin_size(mp_int *a); int mp_read_signed_bin(mp_int *a, const unsigned char *b, int c); int mp_to_signed_bin(mp_int *a, unsigned char *b); int mp_to_signed_bin_n(mp_int *a, unsigned char *b, unsigned long *outlen); int mp_read_radix(mp_int *a, const char *str, int radix); int mp_toradix(mp_int *a, char *str, int radix); int mp_toradix_n(mp_int *a, char *str, int radix, int maxlen); int mp_radix_size(mp_int *a, int radix, int *size); #ifndef LTM_NO_FILE int mp_fread(mp_int *a, int radix, FILE *stream); int mp_fwrite(mp_int *a, int radix, FILE *stream); #endif #define mp_read_raw(mp, str, len) mp_read_signed_bin((mp), (str), (len)) #define mp_raw_size(mp) mp_signed_bin_size(mp) #define mp_toraw(mp, str) mp_to_signed_bin((mp), (str)) #define mp_read_mag(mp, str, len) mp_read_unsigned_bin((mp), (str), (len)) #define mp_mag_size(mp) mp_unsigned_bin_size(mp) #define mp_tomag(mp, str) mp_to_unsigned_bin((mp), (str)) #define mp_tobinary(M, S) mp_toradix((M), (S), 2) #define mp_tooctal(M, S) mp_toradix((M), (S), 8) #define mp_todecimal(M, S) mp_toradix((M), (S), 10) #define mp_tohex(M, S) mp_toradix((M), (S), 16) #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://math.libtomcrypt.com */ #ifndef TOMMATH_PRIV_H_ #define TOMMATH_PRIV_H_ #include #ifndef MIN #define MIN(x, y) (((x) < (y)) ? (x) : (y)) #endif #ifndef MAX #define MAX(x, y) (((x) > (y)) ? (x) : (y)) #endif /* C++ compilers don't like assigning void * to mp_digit * */ #define OPT_CAST(x) (x *) /* define heap macros */ #ifndef XMALLOC /* default to libc stuff */ #define XMALLOC malloc #define XFREE free #define XREALLOC realloc #define XCALLOC calloc #elif 0 //< @r-lyeh /* prototypes for our heap functions */ extern void *XMALLOC(size_t n); extern void *XREALLOC(void *p, size_t n); extern void *XCALLOC(size_t n, size_t s); extern void XFREE(void *p); #endif /* lowlevel functions, do not call! */ int s_mp_add(mp_int *a, mp_int *b, mp_int *c); int s_mp_sub(mp_int *a, mp_int *b, mp_int *c); #define s_mp_mul(a, b, c) s_mp_mul_digs(a, b, c, (a)->used + (b)->used + 1) int fast_s_mp_mul_digs(mp_int *a, mp_int *b, mp_int *c, int digs); int s_mp_mul_digs(mp_int *a, mp_int *b, mp_int *c, int digs); int fast_s_mp_mul_high_digs(mp_int *a, mp_int *b, mp_int *c, int digs); int s_mp_mul_high_digs(mp_int *a, mp_int *b, mp_int *c, int digs); int fast_s_mp_sqr(mp_int *a, mp_int *b); int s_mp_sqr(mp_int *a, mp_int *b); int mp_karatsuba_mul(mp_int *a, mp_int *b, mp_int *c); int mp_toom_mul(mp_int *a, mp_int *b, mp_int *c); int mp_karatsuba_sqr(mp_int *a, mp_int *b); int mp_toom_sqr(mp_int *a, mp_int *b); int fast_mp_invmod(mp_int *a, mp_int *b, mp_int *c); int mp_invmod_slow(mp_int *a, mp_int *b, mp_int *c); int fast_mp_montgomery_reduce(mp_int *x, mp_int *n, mp_digit rho); int mp_exptmod_fast(mp_int *G, mp_int *X, mp_int *P, mp_int *Y, int redmode); int s_mp_exptmod(mp_int *G, mp_int *X, mp_int *P, mp_int *Y, int redmode); void bn_reverse(unsigned char *s, int len); extern const char *mp_s_rmap; /* Fancy macro to set an MPI from another type. * There are several things assumed: * x is the counter and unsigned * a is the pointer to the MPI * b is the original value that should be set in the MPI. */ #define MP_SET_XLONG(func_name, type) \ int func_name(mp_int * a, type b) \ { \ unsigned int x; \ int res; \ \ mp_zero(a); \ \ /* set four bits at a time */ \ for (x = 0; x < (sizeof(type) * 2u); x++) { \ /* shift the number up four bits */ \ if ((res = mp_mul_2d(a, 4, a)) != MP_OKAY) { \ return res; \ } \ \ /* OR in the top four bits of the source */ \ a->dp[0] |= (b >> ((sizeof(type) * 8u) - 4u)) & 15u; \ \ /* shift the source up to the next four bits */ \ b <<= 4; \ \ /* ensure that digits are not clamped off */ \ a->used += 1; \ } \ mp_clamp(a); \ return MP_OKAY; \ } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ #define BN_FAST_MP_INVMOD_C #ifdef BN_FAST_MP_INVMOD_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* computes the modular inverse via binary extended euclidean algorithm, * that is c = 1/a mod b * * Based on slow invmod except this is optimized for the case where b is * odd as per HAC Note 14.64 on pp. 610 */ int fast_mp_invmod(mp_int *a, mp_int *b, mp_int *c) { mp_int x, y, u, v, B, D; int res, neg; /* 2. [modified] b must be odd */ if (mp_iseven(b) == MP_YES) { return MP_VAL; } /* init all our temps */ if ((res = mp_init_multi(&x, &y, &u, &v, &B, &D, NULL)) != MP_OKAY) { return res; } /* x == modulus, y == value to invert */ if ((res = mp_copy(b, &x)) != MP_OKAY) { goto LBL_ERR; } /* we need y = |a| */ if ((res = mp_mod(a, b, &y)) != MP_OKAY) { goto LBL_ERR; } /* 3. u=x, v=y, A=1, B=0, C=0,D=1 */ if ((res = mp_copy(&x, &u)) != MP_OKAY) { goto LBL_ERR; } if ((res = mp_copy(&y, &v)) != MP_OKAY) { goto LBL_ERR; } mp_set(&D, 1); top: /* 4. while u is even do */ while (mp_iseven(&u) == MP_YES) { /* 4.1 u = u/2 */ if ((res = mp_div_2(&u, &u)) != MP_OKAY) { goto LBL_ERR; } /* 4.2 if B is odd then */ if (mp_isodd(&B) == MP_YES) { if ((res = mp_sub(&B, &x, &B)) != MP_OKAY) { goto LBL_ERR; } } /* B = B/2 */ if ((res = mp_div_2(&B, &B)) != MP_OKAY) { goto LBL_ERR; } } /* 5. while v is even do */ while (mp_iseven(&v) == MP_YES) { /* 5.1 v = v/2 */ if ((res = mp_div_2(&v, &v)) != MP_OKAY) { goto LBL_ERR; } /* 5.2 if D is odd then */ if (mp_isodd(&D) == MP_YES) { /* D = (D-x)/2 */ if ((res = mp_sub(&D, &x, &D)) != MP_OKAY) { goto LBL_ERR; } } /* D = D/2 */ if ((res = mp_div_2(&D, &D)) != MP_OKAY) { goto LBL_ERR; } } /* 6. if u >= v then */ if (mp_cmp(&u, &v) != MP_LT) { /* u = u - v, B = B - D */ if ((res = mp_sub(&u, &v, &u)) != MP_OKAY) { goto LBL_ERR; } if ((res = mp_sub(&B, &D, &B)) != MP_OKAY) { goto LBL_ERR; } } else { /* v - v - u, D = D - B */ if ((res = mp_sub(&v, &u, &v)) != MP_OKAY) { goto LBL_ERR; } if ((res = mp_sub(&D, &B, &D)) != MP_OKAY) { goto LBL_ERR; } } /* if not zero goto step 4 */ if (mp_iszero(&u) == MP_NO) { goto top; } /* now a = C, b = D, gcd == g*v */ /* if v != 1 then there is no inverse */ if (mp_cmp_d(&v, 1) != MP_EQ) { res = MP_VAL; goto LBL_ERR; } /* b is now the inverse */ neg = a->sign; while (D.sign == MP_NEG) { if ((res = mp_add(&D, b, &D)) != MP_OKAY) { goto LBL_ERR; } } mp_exch(&D, c); c->sign = neg; res = MP_OKAY; LBL_ERR: mp_clear_multi(&x, &y, &u, &v, &B, &D, NULL); return res; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ #ifdef BN_FAST_MP_MONTGOMERY_REDUCE_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* computes xR**-1 == x (mod N) via Montgomery Reduction * * This is an optimized implementation of montgomery_reduce * which uses the comba method to quickly calculate the columns of the * reduction. * * Based on Algorithm 14.32 on pp.601 of HAC. */ int fast_mp_montgomery_reduce(mp_int *x, mp_int *n, mp_digit rho) { int ix, res, olduse; mp_word W[MP_WARRAY]; /* get old used count */ olduse = x->used; /* grow a as required */ if (x->alloc < (n->used + 1)) { if ((res = mp_grow(x, n->used + 1)) != MP_OKAY) { return res; } } /* first we have to get the digits of the input into * an array of double precision words W[...] */ { mp_word *_W; mp_digit *tmpx; /* alias for the W[] array */ _W = W; /* alias for the digits of x*/ tmpx = x->dp; /* copy the digits of a into W[0..a->used-1] */ for (ix = 0; ix < x->used; ix++) { *_W++ = *tmpx++; } /* zero the high words of W[a->used..m->used*2] */ for ( ; ix < ((n->used * 2) + 1); ix++) { *_W++ = 0; } } /* now we proceed to zero successive digits * from the least significant upwards */ for (ix = 0; ix < n->used; ix++) { /* mu = ai * m' mod b * * We avoid a double precision multiplication (which isn't required) * by casting the value down to a mp_digit. Note this requires * that W[ix-1] have the carry cleared (see after the inner loop) */ mp_digit mu; mu = (mp_digit)(((W[ix] & MP_MASK) * rho) & MP_MASK); /* a = a + mu * m * b**i * * This is computed in place and on the fly. The multiplication * by b**i is handled by offseting which columns the results * are added to. * * Note the comba method normally doesn't handle carries in the * inner loop In this case we fix the carry from the previous * column since the Montgomery reduction requires digits of the * result (so far) [see above] to work. This is * handled by fixing up one carry after the inner loop. The * carry fixups are done in order so after these loops the * first m->used words of W[] have the carries fixed */ { int iy; mp_digit *tmpn; mp_word *_W; /* alias for the digits of the modulus */ tmpn = n->dp; /* Alias for the columns set by an offset of ix */ _W = W + ix; /* inner loop */ for (iy = 0; iy < n->used; iy++) { *_W++ += ((mp_word)mu) * ((mp_word) * tmpn++); } } /* now fix carry for next digit, W[ix+1] */ W[ix + 1] += W[ix] >> ((mp_word)DIGIT_BIT); } /* now we have to propagate the carries and * shift the words downward [all those least * significant digits we zeroed]. */ { mp_digit *tmpx; mp_word *_W, *_W1; /* nox fix rest of carries */ /* alias for current word */ _W1 = W + ix; /* alias for next word, where the carry goes */ _W = W + ++ix; for ( ; ix <= ((n->used * 2) + 1); ix++) { *_W++ += *_W1++ >> ((mp_word)DIGIT_BIT); } /* copy out, A = A/b**n * * The result is A/b**n but instead of converting from an * array of mp_word to mp_digit than calling mp_rshd * we just copy them in the right order */ /* alias for destination word */ tmpx = x->dp; /* alias for shifted double precision result */ _W = W + n->used; for (ix = 0; ix < (n->used + 1); ix++) { *tmpx++ = (mp_digit)(*_W++ & ((mp_word)MP_MASK)); } /* zero oldused digits, if the input a was larger than * m->used+1 we'll have to clear the digits */ for ( ; ix < olduse; ix++) { *tmpx++ = 0; } } /* set the max used and clamp */ x->used = n->used + 1; mp_clamp(x); /* if A >= m then A = A - m */ if (mp_cmp_mag(x, n) != MP_LT) { return s_mp_sub(x, n, x); } return MP_OKAY; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ #ifdef BN_FAST_S_MP_MUL_DIGS_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* Fast (comba) multiplier * * This is the fast column-array [comba] multiplier. It is * designed to compute the columns of the product first * then handle the carries afterwards. This has the effect * of making the nested loops that compute the columns very * simple and schedulable on super-scalar processors. * * This has been modified to produce a variable number of * digits of output so if say only a half-product is required * you don't have to compute the upper half (a feature * required for fast Barrett reduction). * * Based on Algorithm 14.12 on pp.595 of HAC. * */ int fast_s_mp_mul_digs(mp_int *a, mp_int *b, mp_int *c, int digs) { int olduse, res, pa, ix, iz; mp_digit W[MP_WARRAY]; mp_word _W; /* grow the destination as required */ if (c->alloc < digs) { if ((res = mp_grow(c, digs)) != MP_OKAY) { return res; } } /* number of output digits to produce */ pa = MIN(digs, a->used + b->used); /* clear the carry */ _W = 0; for (ix = 0; ix < pa; ix++) { int tx, ty; int iy; mp_digit *tmpx, *tmpy; /* get offsets into the two bignums */ ty = MIN(b->used - 1, ix); tx = ix - ty; /* setup temp aliases */ tmpx = a->dp + tx; tmpy = b->dp + ty; /* this is the number of times the loop will iterrate, essentially while (tx++ < a->used && ty-- >= 0) { ... } */ iy = MIN(a->used - tx, ty + 1); /* execute loop */ for (iz = 0; iz < iy; ++iz) { _W += ((mp_word) * tmpx++) * ((mp_word) * tmpy--); } /* store term */ W[ix] = ((mp_digit)_W) & MP_MASK; /* make next carry */ _W = _W >> ((mp_word)DIGIT_BIT); } /* setup dest */ olduse = c->used; c->used = pa; { mp_digit *tmpc; tmpc = c->dp; for (ix = 0; ix < (pa + 1); ix++) { /* now extract the previous digit [below the carry] */ *tmpc++ = W[ix]; } /* clear unused digits [that existed in the old copy of c] */ for ( ; ix < olduse; ix++) { *tmpc++ = 0; } } mp_clamp(c); return MP_OKAY; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ #ifdef BN_FAST_S_MP_MUL_HIGH_DIGS_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* this is a modified version of fast_s_mul_digs that only produces * output digits *above* digs. See the comments for fast_s_mul_digs * to see how it works. * * This is used in the Barrett reduction since for one of the multiplications * only the higher digits were needed. This essentially halves the work. * * Based on Algorithm 14.12 on pp.595 of HAC. */ int fast_s_mp_mul_high_digs(mp_int *a, mp_int *b, mp_int *c, int digs) { int olduse, res, pa, ix, iz; mp_digit W[MP_WARRAY]; mp_word _W; /* grow the destination as required */ pa = a->used + b->used; if (c->alloc < pa) { if ((res = mp_grow(c, pa)) != MP_OKAY) { return res; } } /* number of output digits to produce */ pa = a->used + b->used; _W = 0; for (ix = digs; ix < pa; ix++) { int tx, ty, iy; mp_digit *tmpx, *tmpy; /* get offsets into the two bignums */ ty = MIN(b->used - 1, ix); tx = ix - ty; /* setup temp aliases */ tmpx = a->dp + tx; tmpy = b->dp + ty; /* this is the number of times the loop will iterrate, essentially its while (tx++ < a->used && ty-- >= 0) { ... } */ iy = MIN(a->used - tx, ty + 1); /* execute loop */ for (iz = 0; iz < iy; iz++) { _W += ((mp_word) * tmpx++) * ((mp_word) * tmpy--); } /* store term */ W[ix] = ((mp_digit)_W) & MP_MASK; /* make next carry */ _W = _W >> ((mp_word)DIGIT_BIT); } /* setup dest */ olduse = c->used; c->used = pa; { mp_digit *tmpc; tmpc = c->dp + digs; for (ix = digs; ix < pa; ix++) { /* now extract the previous digit [below the carry] */ *tmpc++ = W[ix]; } /* clear unused digits [that existed in the old copy of c] */ for ( ; ix < olduse; ix++) { *tmpc++ = 0; } } mp_clamp(c); return MP_OKAY; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ #ifdef BN_FAST_S_MP_SQR_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* the jist of squaring... * you do like mult except the offset of the tmpx [one that * starts closer to zero] can't equal the offset of tmpy. * So basically you set up iy like before then you min it with * (ty-tx) so that it never happens. You double all those * you add in the inner loop After that loop you do the squares and add them in. */ int fast_s_mp_sqr(mp_int *a, mp_int *b) { int olduse, res, pa, ix, iz; mp_digit W[MP_WARRAY], *tmpx; mp_word W1; /* grow the destination as required */ pa = a->used + a->used; if (b->alloc < pa) { if ((res = mp_grow(b, pa)) != MP_OKAY) { return res; } } /* number of output digits to produce */ W1 = 0; for (ix = 0; ix < pa; ix++) { int tx, ty, iy; mp_word _W; mp_digit *tmpy; /* clear counter */ _W = 0; /* get offsets into the two bignums */ ty = MIN(a->used - 1, ix); tx = ix - ty; /* setup temp aliases */ tmpx = a->dp + tx; tmpy = a->dp + ty; /* this is the number of times the loop will iterrate, essentially while (tx++ < a->used && ty-- >= 0) { ... } */ iy = MIN(a->used - tx, ty + 1); /* now for squaring tx can never equal ty * we halve the distance since they approach at a rate of 2x * and we have to round because odd cases need to be executed */ iy = MIN(iy, ((ty - tx) + 1) >> 1); /* execute loop */ for (iz = 0; iz < iy; iz++) { _W += ((mp_word) * tmpx++) * ((mp_word) * tmpy--); } /* double the inner product and add carry */ _W = _W + _W + W1; /* even columns have the square term in them */ if ((ix & 1) == 0) { _W += ((mp_word)a->dp[ix >> 1]) * ((mp_word)a->dp[ix >> 1]); } /* store it */ W[ix] = (mp_digit)(_W & MP_MASK); /* make next carry */ W1 = _W >> ((mp_word)DIGIT_BIT); } /* setup dest */ olduse = b->used; b->used = a->used + a->used; { mp_digit *tmpb; tmpb = b->dp; for (ix = 0; ix < pa; ix++) { *tmpb++ = W[ix] & MP_MASK; } /* clear unused digits [that existed in the old copy of c] */ for ( ; ix < olduse; ix++) { *tmpb++ = 0; } } mp_clamp(b); return MP_OKAY; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ #ifdef BN_MP_2EXPT_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* computes a = 2**b * * Simple algorithm which zeroes the int, grows it then just sets one bit * as required. */ int mp_2expt(mp_int *a, int b) { int res; /* zero a as per default */ mp_zero(a); /* grow a to accomodate the single bit */ if ((res = mp_grow(a, (b / DIGIT_BIT) + 1)) != MP_OKAY) { return res; } /* set the used count of where the bit will go */ a->used = (b / DIGIT_BIT) + 1; /* put the single bit in its place */ a->dp[b / DIGIT_BIT] = ((mp_digit)1) << (b % DIGIT_BIT); return MP_OKAY; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ #ifdef BN_MP_ABS_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* b = |a| * * Simple function copies the input and fixes the sign to positive */ int mp_abs(mp_int *a, mp_int *b) { int res; /* copy a to b */ if (a != b) { if ((res = mp_copy(a, b)) != MP_OKAY) { return res; } } /* force the sign of b to positive */ b->sign = MP_ZPOS; return MP_OKAY; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ #ifdef BN_MP_ADD_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* high level addition (handles signs) */ int mp_add(mp_int *a, mp_int *b, mp_int *c) { int sa, sb, res; /* get sign of both inputs */ sa = a->sign; sb = b->sign; /* handle two cases, not four */ if (sa == sb) { /* both positive or both negative */ /* add their magnitudes, copy the sign */ c->sign = sa; res = s_mp_add(a, b, c); } else { /* one positive, the other negative */ /* subtract the one with the greater magnitude from */ /* the one of the lesser magnitude. The result gets */ /* the sign of the one with the greater magnitude. */ if (mp_cmp_mag(a, b) == MP_LT) { c->sign = sb; res = s_mp_sub(b, a, c); } else { c->sign = sa; res = s_mp_sub(a, b, c); } } return res; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ #ifdef BN_MP_ADD_D_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* single digit addition */ int mp_add_d(mp_int *a, mp_digit b, mp_int *c) { int res, ix, oldused; mp_digit *tmpa, *tmpc, mu; /* grow c as required */ if (c->alloc < (a->used + 1)) { if ((res = mp_grow(c, a->used + 1)) != MP_OKAY) { return res; } } /* if a is negative and |a| >= b, call c = |a| - b */ if ((a->sign == MP_NEG) && ((a->used > 1) || (a->dp[0] >= b))) { /* temporarily fix sign of a */ a->sign = MP_ZPOS; /* c = |a| - b */ res = mp_sub_d(a, b, c); /* fix sign */ a->sign = c->sign = MP_NEG; /* clamp */ mp_clamp(c); return res; } /* old number of used digits in c */ oldused = c->used; /* sign always positive */ c->sign = MP_ZPOS; /* source alias */ tmpa = a->dp; /* destination alias */ tmpc = c->dp; /* if a is positive */ if (a->sign == MP_ZPOS) { /* add digit, after this we're propagating * the carry. */ *tmpc = *tmpa++ + b; mu = *tmpc >> DIGIT_BIT; *tmpc++ &= MP_MASK; /* now handle rest of the digits */ for (ix = 1; ix < a->used; ix++) { *tmpc = *tmpa++ + mu; mu = *tmpc >> DIGIT_BIT; *tmpc++ &= MP_MASK; } /* set final carry */ ix++; *tmpc++ = mu; /* setup size */ c->used = a->used + 1; } else { /* a was negative and |a| < b */ c->used = 1; /* the result is a single digit */ if (a->used == 1) { *tmpc++ = b - a->dp[0]; } else { *tmpc++ = b; } /* setup count so the clearing of oldused * can fall through correctly */ ix = 1; } /* now zero to oldused */ while (ix++ < oldused) { *tmpc++ = 0; } mp_clamp(c); return MP_OKAY; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ #ifdef BN_MP_ADDMOD_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* d = a + b (mod c) */ int mp_addmod(mp_int *a, mp_int *b, mp_int *c, mp_int *d) { int res; mp_int t; if ((res = mp_init(&t)) != MP_OKAY) { return res; } if ((res = mp_add(a, b, &t)) != MP_OKAY) { mp_clear(&t); return res; } res = mp_mod(&t, c, d); mp_clear(&t); return res; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ #ifdef BN_MP_AND_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* AND two ints together */ int mp_and(mp_int *a, mp_int *b, mp_int *c) { int res, ix, px; mp_int t, *x; if (a->used > b->used) { if ((res = mp_init_copy(&t, a)) != MP_OKAY) { return res; } px = b->used; x = b; } else { if ((res = mp_init_copy(&t, b)) != MP_OKAY) { return res; } px = a->used; x = a; } for (ix = 0; ix < px; ix++) { t.dp[ix] &= x->dp[ix]; } /* zero digits above the last from the smallest mp_int */ for ( ; ix < t.used; ix++) { t.dp[ix] = 0; } mp_clamp(&t); mp_exch(c, &t); mp_clear(&t); return MP_OKAY; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ #ifdef BN_MP_CLAMP_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* trim unused digits * * This is used to ensure that leading zero digits are * trimed and the leading "used" digit will be non-zero * Typically very fast. Also fixes the sign if there * are no more leading digits */ void mp_clamp(mp_int *a) { /* decrease used while the most significant digit is * zero. */ while ((a->used > 0) && (a->dp[a->used - 1] == 0)) { --(a->used); } /* reset the sign flag if used == 0 */ if (a->used == 0) { a->sign = MP_ZPOS; } } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ #ifdef BN_MP_CLEAR_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* clear one (frees) */ void mp_clear(mp_int *a) { int i; /* only do anything if a hasn't been freed previously */ if (a->dp != NULL) { /* first zero the digits */ for (i = 0; i < a->used; i++) { a->dp[i] = 0; } /* free ram */ XFREE(a->dp); /* reset members to make debugging easier */ a->dp = NULL; a->alloc = a->used = 0; a->sign = MP_ZPOS; } } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ #ifdef BN_MP_CLEAR_MULTI_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ #include void mp_clear_multi(mp_int *mp, ...) { mp_int *next_mp = mp; va_list args; va_start(args, mp); while (next_mp != NULL) { mp_clear(next_mp); next_mp = va_arg(args, mp_int *); } va_end(args); } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ #ifdef BN_MP_CMP_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* compare two ints (signed)*/ int mp_cmp(mp_int *a, mp_int *b) { /* compare based on sign */ if (a->sign != b->sign) { if (a->sign == MP_NEG) { return MP_LT; } else { return MP_GT; } } /* compare digits */ if (a->sign == MP_NEG) { /* if negative compare opposite direction */ return mp_cmp_mag(b, a); } else { return mp_cmp_mag(a, b); } } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ #ifdef BN_MP_CMP_D_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* compare a digit */ int mp_cmp_d(mp_int *a, mp_digit b) { /* compare based on sign */ if (a->sign == MP_NEG) { return MP_LT; } /* compare based on magnitude */ if (a->used > 1) { return MP_GT; } /* compare the only digit of a to b */ if (a->dp[0] > b) { return MP_GT; } else if (a->dp[0] < b) { return MP_LT; } else { return MP_EQ; } } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ #ifdef BN_MP_CMP_MAG_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* compare maginitude of two ints (unsigned) */ int mp_cmp_mag(mp_int *a, mp_int *b) { int n; mp_digit *tmpa, *tmpb; /* compare based on # of non-zero digits */ if (a->used > b->used) { return MP_GT; } if (a->used < b->used) { return MP_LT; } /* alias for a */ tmpa = a->dp + (a->used - 1); /* alias for b */ tmpb = b->dp + (a->used - 1); /* compare based on digits */ for (n = 0; n < a->used; ++n, --tmpa, --tmpb) { if (*tmpa > *tmpb) { return MP_GT; } if (*tmpa < *tmpb) { return MP_LT; } } return MP_EQ; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ #ifdef BN_MP_CNT_LSB_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ static const int lnz[16] = { 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0 }; /* Counts the number of lsbs which are zero before the first zero bit */ int mp_cnt_lsb(mp_int *a) { int x; mp_digit q, qq; /* easy out */ if (mp_iszero(a) == MP_YES) { return 0; } /* scan lower digits until non-zero */ for (x = 0; (x < a->used) && (a->dp[x] == 0); x++) { } q = a->dp[x]; x *= DIGIT_BIT; /* now scan this digit until a 1 is found */ if ((q & 1) == 0) { do { qq = q & 15; x += lnz[qq]; q >>= 4; } while (qq == 0); } return x; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ #ifdef BN_MP_COPY_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* copy, b = a */ int mp_copy(mp_int *a, mp_int *b) { int res, n; /* if dst == src do nothing */ if (a == b) { return MP_OKAY; } /* grow dest */ if (b->alloc < a->used) { if ((res = mp_grow(b, a->used)) != MP_OKAY) { return res; } } /* zero b and copy the parameters over */ { mp_digit *tmpa, *tmpb; /* pointer aliases */ /* source */ tmpa = a->dp; /* destination */ tmpb = b->dp; /* copy all the digits */ for (n = 0; n < a->used; n++) { *tmpb++ = *tmpa++; } /* clear high digits */ for ( ; n < b->used; n++) { *tmpb++ = 0; } } /* copy used count and sign */ b->used = a->used; b->sign = a->sign; return MP_OKAY; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ #ifdef BN_MP_COUNT_BITS_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* returns the number of bits in an int */ int mp_count_bits(mp_int *a) { int r; mp_digit q; /* shortcut */ if (a->used == 0) { return 0; } /* get number of digits and add that */ r = (a->used - 1) * DIGIT_BIT; /* take the last digit and count the bits in it */ q = a->dp[a->used - 1]; while (q > ((mp_digit)0)) { ++r; q >>= ((mp_digit)1); } return r; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ #ifdef BN_MP_DIV_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ #ifdef BN_MP_DIV_SMALL /* slower bit-bang division... also smaller */ int mp_div(mp_int *a, mp_int *b, mp_int *c, mp_int *d) { mp_int ta, tb, tq, q; int res, n, n2; /* is divisor zero ? */ if (mp_iszero(b) == MP_YES) { return MP_VAL; } /* if a < b then q=0, r = a */ if (mp_cmp_mag(a, b) == MP_LT) { if (d != NULL) { res = mp_copy(a, d); } else { res = MP_OKAY; } if (c != NULL) { mp_zero(c); } return res; } /* init our temps */ if ((res = mp_init_multi(&ta, &tb, &tq, &q, NULL)) != MP_OKAY) { return res; } mp_set(&tq, 1); n = mp_count_bits(a) - mp_count_bits(b); if (((res = mp_abs(a, &ta)) != MP_OKAY) || ((res = mp_abs(b, &tb)) != MP_OKAY) || ((res = mp_mul_2d(&tb, n, &tb)) != MP_OKAY) || ((res = mp_mul_2d(&tq, n, &tq)) != MP_OKAY)) { goto LBL_ERR; } while (n-- >= 0) { if (mp_cmp(&tb, &ta) != MP_GT) { if (((res = mp_sub(&ta, &tb, &ta)) != MP_OKAY) || ((res = mp_add(&q, &tq, &q)) != MP_OKAY)) { goto LBL_ERR; } } if (((res = mp_div_2d(&tb, 1, &tb, NULL)) != MP_OKAY) || ((res = mp_div_2d(&tq, 1, &tq, NULL)) != MP_OKAY)) { goto LBL_ERR; } } /* now q == quotient and ta == remainder */ n = a->sign; n2 = (a->sign == b->sign) ? MP_ZPOS : MP_NEG; if (c != NULL) { mp_exch(c, &q); c->sign = (mp_iszero(c) == MP_YES) ? MP_ZPOS : n2; } if (d != NULL) { mp_exch(d, &ta); d->sign = (mp_iszero(d) == MP_YES) ? MP_ZPOS : n; } LBL_ERR: mp_clear_multi(&ta, &tb, &tq, &q, NULL); return res; } #else /* integer signed division. * c*b + d == a [e.g. a/b, c=quotient, d=remainder] * HAC pp.598 Algorithm 14.20 * * Note that the description in HAC is horribly * incomplete. For example, it doesn't consider * the case where digits are removed from 'x' in * the inner loop. It also doesn't consider the * case that y has fewer than three digits, etc.. * * The overall algorithm is as described as * 14.20 from HAC but fixed to treat these cases. */ int mp_div(mp_int *a, mp_int *b, mp_int *c, mp_int *d) { mp_int q, x, y, t1, t2; int res, n, t, i, norm, neg; /* is divisor zero ? */ if (mp_iszero(b) == MP_YES) { return MP_VAL; } /* if a < b then q=0, r = a */ if (mp_cmp_mag(a, b) == MP_LT) { if (d != NULL) { res = mp_copy(a, d); } else { res = MP_OKAY; } if (c != NULL) { mp_zero(c); } return res; } if ((res = mp_init_size(&q, a->used + 2)) != MP_OKAY) { return res; } q.used = a->used + 2; if ((res = mp_init(&t1)) != MP_OKAY) { goto LBL_Q; } if ((res = mp_init(&t2)) != MP_OKAY) { goto LBL_T1; } if ((res = mp_init_copy(&x, a)) != MP_OKAY) { goto LBL_T2; } if ((res = mp_init_copy(&y, b)) != MP_OKAY) { goto LBL_X; } /* fix the sign */ neg = (a->sign == b->sign) ? MP_ZPOS : MP_NEG; x.sign = y.sign = MP_ZPOS; /* normalize both x and y, ensure that y >= b/2, [b == 2**DIGIT_BIT] */ norm = mp_count_bits(&y) % DIGIT_BIT; if (norm < (int)(DIGIT_BIT - 1)) { norm = (DIGIT_BIT - 1) - norm; if ((res = mp_mul_2d(&x, norm, &x)) != MP_OKAY) { goto LBL_Y; } if ((res = mp_mul_2d(&y, norm, &y)) != MP_OKAY) { goto LBL_Y; } } else { norm = 0; } /* note hac does 0 based, so if used==5 then its 0,1,2,3,4, e.g. use 4 */ n = x.used - 1; t = y.used - 1; /* while (x >= y*b**n-t) do { q[n-t] += 1; x -= y*b**{n-t} } */ if ((res = mp_lshd(&y, n - t)) != MP_OKAY) { /* y = y*b**{n-t} */ goto LBL_Y; } while (mp_cmp(&x, &y) != MP_LT) { ++(q.dp[n - t]); if ((res = mp_sub(&x, &y, &x)) != MP_OKAY) { goto LBL_Y; } } /* reset y by shifting it back down */ mp_rshd(&y, n - t); /* step 3. for i from n down to (t + 1) */ for (i = n; i >= (t + 1); i--) { if (i > x.used) { continue; } /* step 3.1 if xi == yt then set q{i-t-1} to b-1, * otherwise set q{i-t-1} to (xi*b + x{i-1})/yt */ if (x.dp[i] == y.dp[t]) { q.dp[(i - t) - 1] = ((((mp_digit)1) << DIGIT_BIT) - 1); } else { mp_word tmp; tmp = ((mp_word)x.dp[i]) << ((mp_word)DIGIT_BIT); tmp |= ((mp_word)x.dp[i - 1]); tmp /= ((mp_word)y.dp[t]); if (tmp > (mp_word)MP_MASK) { tmp = MP_MASK; } q.dp[(i - t) - 1] = (mp_digit)(tmp & (mp_word)(MP_MASK)); } /* while (q{i-t-1} * (yt * b + y{t-1})) > xi * b**2 + xi-1 * b + xi-2 do q{i-t-1} -= 1; */ q.dp[(i - t) - 1] = (q.dp[(i - t) - 1] + 1) & MP_MASK; do { q.dp[(i - t) - 1] = (q.dp[(i - t) - 1] - 1) & MP_MASK; /* find left hand */ mp_zero(&t1); t1.dp[0] = ((t - 1) < 0) ? 0 : y.dp[t - 1]; t1.dp[1] = y.dp[t]; t1.used = 2; if ((res = mp_mul_d(&t1, q.dp[(i - t) - 1], &t1)) != MP_OKAY) { goto LBL_Y; } /* find right hand */ t2.dp[0] = ((i - 2) < 0) ? 0 : x.dp[i - 2]; t2.dp[1] = ((i - 1) < 0) ? 0 : x.dp[i - 1]; t2.dp[2] = x.dp[i]; t2.used = 3; } while (mp_cmp_mag(&t1, &t2) == MP_GT); /* step 3.3 x = x - q{i-t-1} * y * b**{i-t-1} */ if ((res = mp_mul_d(&y, q.dp[(i - t) - 1], &t1)) != MP_OKAY) { goto LBL_Y; } if ((res = mp_lshd(&t1, (i - t) - 1)) != MP_OKAY) { goto LBL_Y; } if ((res = mp_sub(&x, &t1, &x)) != MP_OKAY) { goto LBL_Y; } /* if x < 0 then { x = x + y*b**{i-t-1}; q{i-t-1} -= 1; } */ if (x.sign == MP_NEG) { if ((res = mp_copy(&y, &t1)) != MP_OKAY) { goto LBL_Y; } if ((res = mp_lshd(&t1, (i - t) - 1)) != MP_OKAY) { goto LBL_Y; } if ((res = mp_add(&x, &t1, &x)) != MP_OKAY) { goto LBL_Y; } q.dp[(i - t) - 1] = (q.dp[(i - t) - 1] - 1UL) & MP_MASK; } } /* now q is the quotient and x is the remainder * [which we have to normalize] */ /* get sign before writing to c */ x.sign = (x.used == 0) ? MP_ZPOS : a->sign; if (c != NULL) { mp_clamp(&q); mp_exch(&q, c); c->sign = neg; } if (d != NULL) { if ((res = mp_div_2d(&x, norm, &x, NULL)) != MP_OKAY) { goto LBL_Y; } mp_exch(&x, d); } res = MP_OKAY; LBL_Y: mp_clear(&y); LBL_X: mp_clear(&x); LBL_T2: mp_clear(&t2); LBL_T1: mp_clear(&t1); LBL_Q: mp_clear(&q); return res; } #endif #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ #ifdef BN_MP_DIV_2_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* b = a/2 */ int mp_div_2(mp_int *a, mp_int *b) { int x, res, oldused; /* copy */ if (b->alloc < a->used) { if ((res = mp_grow(b, a->used)) != MP_OKAY) { return res; } } oldused = b->used; b->used = a->used; { mp_digit r, rr, *tmpa, *tmpb; /* source alias */ tmpa = a->dp + b->used - 1; /* dest alias */ tmpb = b->dp + b->used - 1; /* carry */ r = 0; for (x = b->used - 1; x >= 0; x--) { /* get the carry for the next iteration */ rr = *tmpa & 1; /* shift the current digit, add in carry and store */ *tmpb-- = (*tmpa-- >> 1) | (r << (DIGIT_BIT - 1)); /* forward carry to next iteration */ r = rr; } /* zero excess digits */ tmpb = b->dp + b->used; for (x = b->used; x < oldused; x++) { *tmpb++ = 0; } } b->sign = a->sign; mp_clamp(b); return MP_OKAY; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ #ifdef BN_MP_DIV_2D_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* shift right by a certain bit count (store quotient in c, optional remainder in d) */ int mp_div_2d(mp_int *a, int b, mp_int *c, mp_int *d) { mp_digit D, r, rr; int x, res; mp_int t; /* if the shift count is <= 0 then we do no work */ if (b <= 0) { res = mp_copy(a, c); if (d != NULL) { mp_zero(d); } return res; } if ((res = mp_init(&t)) != MP_OKAY) { return res; } /* get the remainder */ if (d != NULL) { if ((res = mp_mod_2d(a, b, &t)) != MP_OKAY) { mp_clear(&t); return res; } } /* copy */ if ((res = mp_copy(a, c)) != MP_OKAY) { mp_clear(&t); return res; } /* shift by as many digits in the bit count */ if (b >= (int)DIGIT_BIT) { mp_rshd(c, b / DIGIT_BIT); } /* shift any bit count < DIGIT_BIT */ D = (mp_digit)(b % DIGIT_BIT); if (D != 0) { mp_digit *tmpc, mask, shift; /* mask */ mask = (((mp_digit)1) << D) - 1; /* shift for lsb */ shift = DIGIT_BIT - D; /* alias */ tmpc = c->dp + (c->used - 1); /* carry */ r = 0; for (x = c->used - 1; x >= 0; x--) { /* get the lower bits of this word in a temp */ rr = *tmpc & mask; /* shift the current word and mix in the carry bits from the previous word */ *tmpc = (*tmpc >> D) | (r << shift); --tmpc; /* set the carry to the carry bits of the current word found above */ r = rr; } } mp_clamp(c); if (d != NULL) { mp_exch(&t, d); } mp_clear(&t); return MP_OKAY; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ #ifdef BN_MP_DIV_3_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* divide by three (based on routine from MPI and the GMP manual) */ int mp_div_3(mp_int *a, mp_int *c, mp_digit *d) { mp_int q; mp_word w, t; mp_digit b; int res, ix; /* b = 2**DIGIT_BIT / 3 */ b = (((mp_word)1) << ((mp_word)DIGIT_BIT)) / ((mp_word)3); if ((res = mp_init_size(&q, a->used)) != MP_OKAY) { return res; } q.used = a->used; q.sign = a->sign; w = 0; for (ix = a->used - 1; ix >= 0; ix--) { w = (w << ((mp_word)DIGIT_BIT)) | ((mp_word)a->dp[ix]); if (w >= 3) { /* multiply w by [1/3] */ t = (w * ((mp_word)b)) >> ((mp_word)DIGIT_BIT); /* now subtract 3 * [w/3] from w, to get the remainder */ w -= t + t + t; /* fixup the remainder as required since * the optimization is not exact. */ while (w >= 3) { t += 1; w -= 3; } } else { t = 0; } q.dp[ix] = (mp_digit)t; } /* [optional] store the remainder */ if (d != NULL) { *d = (mp_digit)w; } /* [optional] store the quotient */ if (c != NULL) { mp_clamp(&q); mp_exch(&q, c); } mp_clear(&q); return res; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ #ifdef BN_MP_DIV_D_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ static int s_is_power_of_two(mp_digit b, int *p) { int x; /* fast return if no power of two */ if ((b == 0) || ((b & (b - 1)) != 0)) { return 0; } for (x = 0; x < DIGIT_BIT; x++) { if (b == (((mp_digit)1) << x)) { *p = x; return 1; } } return 0; } /* single digit division (based on routine from MPI) */ int mp_div_d(mp_int *a, mp_digit b, mp_int *c, mp_digit *d) { mp_int q; mp_word w; mp_digit t; int res, ix; /* cannot divide by zero */ if (b == 0) { return MP_VAL; } /* quick outs */ if ((b == 1) || (mp_iszero(a) == MP_YES)) { if (d != NULL) { *d = 0; } if (c != NULL) { return mp_copy(a, c); } return MP_OKAY; } /* power of two ? */ if (s_is_power_of_two(b, &ix) == 1) { if (d != NULL) { *d = a->dp[0] & ((((mp_digit)1) << ix) - 1); } if (c != NULL) { return mp_div_2d(a, ix, c, NULL); } return MP_OKAY; } #ifdef BN_MP_DIV_3_C /* three? */ if (b == 3) { return mp_div_3(a, c, d); } #endif /* no easy answer [c'est la vie]. Just division */ if ((res = mp_init_size(&q, a->used)) != MP_OKAY) { return res; } q.used = a->used; q.sign = a->sign; w = 0; for (ix = a->used - 1; ix >= 0; ix--) { w = (w << ((mp_word)DIGIT_BIT)) | ((mp_word)a->dp[ix]); if (w >= b) { t = (mp_digit)(w / b); w -= ((mp_word)t) * ((mp_word)b); } else { t = 0; } q.dp[ix] = (mp_digit)t; } if (d != NULL) { *d = (mp_digit)w; } if (c != NULL) { mp_clamp(&q); mp_exch(&q, c); } mp_clear(&q); return res; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ #ifdef BN_MP_DR_IS_MODULUS_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* determines if a number is a valid DR modulus */ int mp_dr_is_modulus(mp_int *a) { int ix; /* must be at least two digits */ if (a->used < 2) { return 0; } /* must be of the form b**k - a [a <= b] so all * but the first digit must be equal to -1 (mod b). */ for (ix = 1; ix < a->used; ix++) { if (a->dp[ix] != MP_MASK) { return 0; } } return 1; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ #ifdef BN_MP_DR_REDUCE_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* reduce "x" in place modulo "n" using the Diminished Radix algorithm. * * Based on algorithm from the paper * * "Generating Efficient Primes for Discrete Log Cryptosystems" * Chae Hoon Lim, Pil Joong Lee, * POSTECH Information Research Laboratories * * The modulus must be of a special format [see manual] * * Has been modified to use algorithm 7.10 from the LTM book instead * * Input x must be in the range 0 <= x <= (n-1)**2 */ int mp_dr_reduce(mp_int *x, mp_int *n, mp_digit k) { int err, i, m; mp_word r; mp_digit mu, *tmpx1, *tmpx2; /* m = digits in modulus */ m = n->used; /* ensure that "x" has at least 2m digits */ if (x->alloc < (m + m)) { if ((err = mp_grow(x, m + m)) != MP_OKAY) { return err; } } /* top of loop, this is where the code resumes if * another reduction pass is required. */ top: /* aliases for digits */ /* alias for lower half of x */ tmpx1 = x->dp; /* alias for upper half of x, or x/B**m */ tmpx2 = x->dp + m; /* set carry to zero */ mu = 0; /* compute (x mod B**m) + k * [x/B**m] inline and inplace */ for (i = 0; i < m; i++) { r = (((mp_word) * tmpx2++) * (mp_word)k) + *tmpx1 + mu; *tmpx1++ = (mp_digit)(r & MP_MASK); mu = (mp_digit)(r >> ((mp_word)DIGIT_BIT)); } /* set final carry */ *tmpx1++ = mu; /* zero words above m */ for (i = m + 1; i < x->used; i++) { *tmpx1++ = 0; } /* clamp, sub and return */ mp_clamp(x); /* if x >= n then subtract and reduce again * Each successive "recursion" makes the input smaller and smaller. */ if (mp_cmp_mag(x, n) != MP_LT) { if ((err = s_mp_sub(x, n, x)) != MP_OKAY) { return err; } goto top; } return MP_OKAY; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ #ifdef BN_MP_DR_SETUP_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* determines the setup value */ void mp_dr_setup(mp_int *a, mp_digit *d) { /* the casts are required if DIGIT_BIT is one less than * the number of bits in a mp_digit [e.g. DIGIT_BIT==31] */ *d = (mp_digit)((((mp_word)1) << ((mp_word)DIGIT_BIT)) - ((mp_word)a->dp[0])); } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ #ifdef BN_MP_EXCH_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* swap the elements of two integers, for cases where you can't simply swap the * mp_int pointers around */ void mp_exch(mp_int *a, mp_int *b) { mp_int t; t = *a; *a = *b; *b = t; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ #ifdef BN_MP_EXPORT_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* based on gmp's mpz_export. * see http://gmplib.org/manual/Integer-Import-and-Export.html */ int mp_export(void *rop, size_t *countp, int order, size_t size, int endian, size_t nails, mp_int *op) { int result; size_t odd_nails, nail_bytes, i, j, bits, count; unsigned char odd_nail_mask; mp_int t; if ((result = mp_init_copy(&t, op)) != MP_OKAY) { return result; } if (endian == 0) { union { unsigned int i; char c[4]; } lint; lint.i = 0x01020304; endian = (lint.c[0] == 4) ? -1 : 1; } odd_nails = (nails % 8); odd_nail_mask = 0xff; for (i = 0; i < odd_nails; ++i) { odd_nail_mask ^= (1 << (7 - i)); } nail_bytes = nails / 8; bits = mp_count_bits(&t); count = (bits / ((size * 8) - nails)) + (((bits % ((size * 8) - nails)) != 0) ? 1 : 0); for (i = 0; i < count; ++i) { for (j = 0; j < size; ++j) { unsigned char *byte = ( (unsigned char *)rop + (((order == -1) ? i : ((count - 1) - i)) * size) + ((endian == -1) ? j : ((size - 1) - j)) ); if (j >= (size - nail_bytes)) { *byte = 0; continue; } *byte = (unsigned char)((j == ((size - nail_bytes) - 1)) ? (t.dp[0] & odd_nail_mask) : (t.dp[0] & 0xFF)); if ((result = mp_div_2d(&t, ((j == ((size - nail_bytes) - 1)) ? (8 - odd_nails) : 8), &t, NULL)) != MP_OKAY) { mp_clear(&t); return result; } } } mp_clear(&t); if (countp != NULL) { *countp = count; } return MP_OKAY; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ #ifdef BN_MP_EXPT_D_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* wrapper function for mp_expt_d_ex() */ int mp_expt_d(mp_int *a, mp_digit b, mp_int *c) { return mp_expt_d_ex(a, b, c, 0); } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ #ifdef BN_MP_EXPT_D_EX_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* calculate c = a**b using a square-multiply algorithm */ int mp_expt_d_ex(mp_int *a, mp_digit b, mp_int *c, int fast) { int res; unsigned int x; mp_int g; if ((res = mp_init_copy(&g, a)) != MP_OKAY) { return res; } /* set initial result */ mp_set(c, 1); if (fast != 0) { while (b > 0) { /* if the bit is set multiply */ if ((b & 1) != 0) { if ((res = mp_mul(c, &g, c)) != MP_OKAY) { mp_clear(&g); return res; } } /* square */ if (b > 1) { if ((res = mp_sqr(&g, &g)) != MP_OKAY) { mp_clear(&g); return res; } } /* shift to next bit */ b >>= 1; } } else { for (x = 0; x < DIGIT_BIT; x++) { /* square */ if ((res = mp_sqr(c, c)) != MP_OKAY) { mp_clear(&g); return res; } /* if the bit is set multiply */ if ((b & (mp_digit)(((mp_digit)1) << (DIGIT_BIT - 1))) != 0) { if ((res = mp_mul(c, &g, c)) != MP_OKAY) { mp_clear(&g); return res; } } /* shift to next bit */ b <<= 1; } } /* if ... else */ mp_clear(&g); return MP_OKAY; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ #ifdef BN_MP_EXPTMOD_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* this is a shell function that calls either the normal or Montgomery * exptmod functions. Originally the call to the montgomery code was * embedded in the normal function but that wasted alot of stack space * for nothing (since 99% of the time the Montgomery code would be called) */ int mp_exptmod(mp_int *G, mp_int *X, mp_int *P, mp_int *Y) { int dr; /* modulus P must be positive */ if (P->sign == MP_NEG) { return MP_VAL; } /* if exponent X is negative we have to recurse */ if (X->sign == MP_NEG) { #ifdef BN_MP_INVMOD_C mp_int tmpG, tmpX; int err; /* first compute 1/G mod P */ if ((err = mp_init(&tmpG)) != MP_OKAY) { return err; } if ((err = mp_invmod(G, P, &tmpG)) != MP_OKAY) { mp_clear(&tmpG); return err; } /* now get |X| */ if ((err = mp_init(&tmpX)) != MP_OKAY) { mp_clear(&tmpG); return err; } if ((err = mp_abs(X, &tmpX)) != MP_OKAY) { mp_clear_multi(&tmpG, &tmpX, NULL); return err; } /* and now compute (1/G)**|X| instead of G**X [X < 0] */ err = mp_exptmod(&tmpG, &tmpX, P, Y); mp_clear_multi(&tmpG, &tmpX, NULL); return err; #else /* no invmod */ return MP_VAL; #endif } /* modified diminished radix reduction */ #if defined(BN_MP_REDUCE_IS_2K_L_C) && defined(BN_MP_REDUCE_2K_L_C) && defined(BN_S_MP_EXPTMOD_C) if (mp_reduce_is_2k_l(P) == MP_YES) { return s_mp_exptmod(G, X, P, Y, 1); } #endif #ifdef BN_MP_DR_IS_MODULUS_C /* is it a DR modulus? */ dr = mp_dr_is_modulus(P); #else /* default to no */ dr = 0; #endif #ifdef BN_MP_REDUCE_IS_2K_C /* if not, is it a unrestricted DR modulus? */ if (dr == 0) { dr = mp_reduce_is_2k(P) << 1; } #endif /* if the modulus is odd or dr != 0 use the montgomery method */ #ifdef BN_MP_EXPTMOD_FAST_C if ((mp_isodd(P) == MP_YES) || (dr != 0)) { return mp_exptmod_fast(G, X, P, Y, dr); } else { #endif #ifdef BN_S_MP_EXPTMOD_C /* otherwise use the generic Barrett reduction technique */ return s_mp_exptmod(G, X, P, Y, 0); #else /* no exptmod for evens */ return MP_VAL; #endif #ifdef BN_MP_EXPTMOD_FAST_C } #endif } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ #ifdef BN_MP_EXPTMOD_FAST_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* computes Y == G**X mod P, HAC pp.616, Algorithm 14.85 * * Uses a left-to-right k-ary sliding window to compute the modular exponentiation. * The value of k changes based on the size of the exponent. * * Uses Montgomery or Diminished Radix reduction [whichever appropriate] */ #ifdef MP_LOW_MEM #define TAB_SIZE 32 #else #define TAB_SIZE 256 #endif int mp_exptmod_fast(mp_int *G, mp_int *X, mp_int *P, mp_int *Y, int redmode) { mp_int M[TAB_SIZE], res; mp_digit buf, mp; int err, bitbuf, bitcpy, bitcnt, mode, digidx, x, y, winsize; /* use a pointer to the reduction algorithm. This allows us to use * one of many reduction algorithms without modding the guts of * the code with if statements everywhere. */ int (*redux)(mp_int *, mp_int *, mp_digit); /* find window size */ x = mp_count_bits(X); if (x <= 7) { winsize = 2; } else if (x <= 36) { winsize = 3; } else if (x <= 140) { winsize = 4; } else if (x <= 450) { winsize = 5; } else if (x <= 1303) { winsize = 6; } else if (x <= 3529) { winsize = 7; } else { winsize = 8; } #ifdef MP_LOW_MEM if (winsize > 5) { winsize = 5; } #endif /* init M array */ /* init first cell */ if ((err = mp_init(&M[1])) != MP_OKAY) { return err; } /* now init the second half of the array */ for (x = 1 << (winsize - 1); x < (1 << winsize); x++) { if ((err = mp_init(&M[x])) != MP_OKAY) { for (y = 1 << (winsize - 1); y < x; y++) { mp_clear(&M[y]); } mp_clear(&M[1]); return err; } } /* determine and setup reduction code */ if (redmode == 0) { #ifdef BN_MP_MONTGOMERY_SETUP_C /* now setup montgomery */ if ((err = mp_montgomery_setup(P, &mp)) != MP_OKAY) { goto LBL_M; } #else err = MP_VAL; goto LBL_M; #endif /* automatically pick the comba one if available (saves quite a few calls/ifs) */ #ifdef BN_FAST_MP_MONTGOMERY_REDUCE_C if ((((P->used * 2) + 1) < MP_WARRAY) && (P->used < (1 << ((CHAR_BIT * sizeof(mp_word)) - (2 * DIGIT_BIT))))) { redux = fast_mp_montgomery_reduce; } else #endif { #ifdef BN_MP_MONTGOMERY_REDUCE_C /* use slower baseline Montgomery method */ redux = mp_montgomery_reduce; #else err = MP_VAL; goto LBL_M; #endif } } else if (redmode == 1) { #if defined(BN_MP_DR_SETUP_C) && defined(BN_MP_DR_REDUCE_C) /* setup DR reduction for moduli of the form B**k - b */ mp_dr_setup(P, &mp); redux = mp_dr_reduce; #else err = MP_VAL; goto LBL_M; #endif } else { #if defined(BN_MP_REDUCE_2K_SETUP_C) && defined(BN_MP_REDUCE_2K_C) /* setup DR reduction for moduli of the form 2**k - b */ if ((err = mp_reduce_2k_setup(P, &mp)) != MP_OKAY) { goto LBL_M; } redux = mp_reduce_2k; #else err = MP_VAL; goto LBL_M; #endif } /* setup result */ if ((err = mp_init(&res)) != MP_OKAY) { goto LBL_M; } /* create M table * * * The first half of the table is not computed though accept for M[0] and M[1] */ if (redmode == 0) { #ifdef BN_MP_MONTGOMERY_CALC_NORMALIZATION_C /* now we need R mod m */ if ((err = mp_montgomery_calc_normalization(&res, P)) != MP_OKAY) { goto LBL_RES; } #else err = MP_VAL; goto LBL_RES; #endif /* now set M[1] to G * R mod m */ if ((err = mp_mulmod(G, &res, P, &M[1])) != MP_OKAY) { goto LBL_RES; } } else { mp_set(&res, 1); if ((err = mp_mod(G, P, &M[1])) != MP_OKAY) { goto LBL_RES; } } /* compute the value at M[1<<(winsize-1)] by squaring M[1] (winsize-1) times */ if ((err = mp_copy(&M[1], &M[1 << (winsize - 1)])) != MP_OKAY) { goto LBL_RES; } for (x = 0; x < (winsize - 1); x++) { if ((err = mp_sqr(&M[1 << (winsize - 1)], &M[1 << (winsize - 1)])) != MP_OKAY) { goto LBL_RES; } if ((err = redux(&M[1 << (winsize - 1)], P, mp)) != MP_OKAY) { goto LBL_RES; } } /* create upper table */ for (x = (1 << (winsize - 1)) + 1; x < (1 << winsize); x++) { if ((err = mp_mul(&M[x - 1], &M[1], &M[x])) != MP_OKAY) { goto LBL_RES; } if ((err = redux(&M[x], P, mp)) != MP_OKAY) { goto LBL_RES; } } /* set initial mode and bit cnt */ mode = 0; bitcnt = 1; buf = 0; digidx = X->used - 1; bitcpy = 0; bitbuf = 0; for ( ; ; ) { /* grab next digit as required */ if (--bitcnt == 0) { /* if digidx == -1 we are out of digits so break */ if (digidx == -1) { break; } /* read next digit and reset bitcnt */ buf = X->dp[digidx--]; bitcnt = (int)DIGIT_BIT; } /* grab the next msb from the exponent */ y = (mp_digit)(buf >> (DIGIT_BIT - 1)) & 1; buf <<= (mp_digit)1; /* if the bit is zero and mode == 0 then we ignore it * These represent the leading zero bits before the first 1 bit * in the exponent. Technically this opt is not required but it * does lower the # of trivial squaring/reductions used */ if ((mode == 0) && (y == 0)) { continue; } /* if the bit is zero and mode == 1 then we square */ if ((mode == 1) && (y == 0)) { if ((err = mp_sqr(&res, &res)) != MP_OKAY) { goto LBL_RES; } if ((err = redux(&res, P, mp)) != MP_OKAY) { goto LBL_RES; } continue; } /* else we add it to the window */ bitbuf |= (y << (winsize - ++bitcpy)); mode = 2; if (bitcpy == winsize) { /* ok window is filled so square as required and multiply */ /* square first */ for (x = 0; x < winsize; x++) { if ((err = mp_sqr(&res, &res)) != MP_OKAY) { goto LBL_RES; } if ((err = redux(&res, P, mp)) != MP_OKAY) { goto LBL_RES; } } /* then multiply */ if ((err = mp_mul(&res, &M[bitbuf], &res)) != MP_OKAY) { goto LBL_RES; } if ((err = redux(&res, P, mp)) != MP_OKAY) { goto LBL_RES; } /* empty window and reset */ bitcpy = 0; bitbuf = 0; mode = 1; } } /* if bits remain then square/multiply */ if ((mode == 2) && (bitcpy > 0)) { /* square then multiply if the bit is set */ for (x = 0; x < bitcpy; x++) { if ((err = mp_sqr(&res, &res)) != MP_OKAY) { goto LBL_RES; } if ((err = redux(&res, P, mp)) != MP_OKAY) { goto LBL_RES; } /* get next bit of the window */ bitbuf <<= 1; if ((bitbuf & (1 << winsize)) != 0) { /* then multiply */ if ((err = mp_mul(&res, &M[1], &res)) != MP_OKAY) { goto LBL_RES; } if ((err = redux(&res, P, mp)) != MP_OKAY) { goto LBL_RES; } } } } if (redmode == 0) { /* fixup result if Montgomery reduction is used * recall that any value in a Montgomery system is * actually multiplied by R mod n. So we have * to reduce one more time to cancel out the factor * of R. */ if ((err = redux(&res, P, mp)) != MP_OKAY) { goto LBL_RES; } } /* swap res with Y */ mp_exch(&res, Y); err = MP_OKAY; LBL_RES: mp_clear(&res); LBL_M: mp_clear(&M[1]); for (x = 1 << (winsize - 1); x < (1 << winsize); x++) { mp_clear(&M[x]); } return err; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ #ifdef BN_MP_EXTEUCLID_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* Extended euclidean algorithm of (a, b) produces a*u1 + b*u2 = u3 */ int mp_exteuclid(mp_int *a, mp_int *b, mp_int *U1, mp_int *U2, mp_int *U3) { mp_int u1, u2, u3, v1, v2, v3, t1, t2, t3, q, tmp; int err; if ((err = mp_init_multi(&u1, &u2, &u3, &v1, &v2, &v3, &t1, &t2, &t3, &q, &tmp, NULL)) != MP_OKAY) { return err; } /* initialize, (u1,u2,u3) = (1,0,a) */ mp_set(&u1, 1); if ((err = mp_copy(a, &u3)) != MP_OKAY) { goto _ERR; } /* initialize, (v1,v2,v3) = (0,1,b) */ mp_set(&v2, 1); if ((err = mp_copy(b, &v3)) != MP_OKAY) { goto _ERR; } /* loop while v3 != 0 */ while (mp_iszero(&v3) == MP_NO) { /* q = u3/v3 */ if ((err = mp_div(&u3, &v3, &q, NULL)) != MP_OKAY) { goto _ERR; } /* (t1,t2,t3) = (u1,u2,u3) - (v1,v2,v3)q */ if ((err = mp_mul(&v1, &q, &tmp)) != MP_OKAY) { goto _ERR; } if ((err = mp_sub(&u1, &tmp, &t1)) != MP_OKAY) { goto _ERR; } if ((err = mp_mul(&v2, &q, &tmp)) != MP_OKAY) { goto _ERR; } if ((err = mp_sub(&u2, &tmp, &t2)) != MP_OKAY) { goto _ERR; } if ((err = mp_mul(&v3, &q, &tmp)) != MP_OKAY) { goto _ERR; } if ((err = mp_sub(&u3, &tmp, &t3)) != MP_OKAY) { goto _ERR; } /* (u1,u2,u3) = (v1,v2,v3) */ if ((err = mp_copy(&v1, &u1)) != MP_OKAY) { goto _ERR; } if ((err = mp_copy(&v2, &u2)) != MP_OKAY) { goto _ERR; } if ((err = mp_copy(&v3, &u3)) != MP_OKAY) { goto _ERR; } /* (v1,v2,v3) = (t1,t2,t3) */ if ((err = mp_copy(&t1, &v1)) != MP_OKAY) { goto _ERR; } if ((err = mp_copy(&t2, &v2)) != MP_OKAY) { goto _ERR; } if ((err = mp_copy(&t3, &v3)) != MP_OKAY) { goto _ERR; } } /* make sure U3 >= 0 */ if (u3.sign == MP_NEG) { if ((err = mp_neg(&u1, &u1)) != MP_OKAY) { goto _ERR; } if ((err = mp_neg(&u2, &u2)) != MP_OKAY) { goto _ERR; } if ((err = mp_neg(&u3, &u3)) != MP_OKAY) { goto _ERR; } } /* copy result out */ if (U1 != NULL) { mp_exch(U1, &u1); } if (U2 != NULL) { mp_exch(U2, &u2); } if (U3 != NULL) { mp_exch(U3, &u3); } err = MP_OKAY; _ERR: mp_clear_multi(&u1, &u2, &u3, &v1, &v2, &v3, &t1, &t2, &t3, &q, &tmp, NULL); return err; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ #ifdef BN_MP_FREAD_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* read a bigint from a file stream in ASCII */ int mp_fread(mp_int *a, int radix, FILE *stream) { int err, ch, neg, y; /* clear a */ mp_zero(a); /* if first digit is - then set negative */ ch = fgetc(stream); if (ch == '-') { neg = MP_NEG; ch = fgetc(stream); } else { neg = MP_ZPOS; } for ( ; ; ) { /* find y in the radix map */ for (y = 0; y < radix; y++) { if (mp_s_rmap[y] == ch) { break; } } if (y == radix) { break; } /* shift up and add */ if ((err = mp_mul_d(a, radix, a)) != MP_OKAY) { return err; } if ((err = mp_add_d(a, y, a)) != MP_OKAY) { return err; } ch = fgetc(stream); } if (mp_cmp_d(a, 0) != MP_EQ) { a->sign = neg; } return MP_OKAY; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ #ifdef BN_MP_FWRITE_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ int mp_fwrite(mp_int *a, int radix, FILE *stream) { char *buf; int err, len, x; if ((err = mp_radix_size(a, radix, &len)) != MP_OKAY) { return err; } buf = OPT_CAST(char) XMALLOC(len); if (buf == NULL) { return MP_MEM; } if ((err = mp_toradix(a, buf, radix)) != MP_OKAY) { XFREE(buf); return err; } for (x = 0; x < len; x++) { if (fputc(buf[x], stream) == EOF) { XFREE(buf); return MP_VAL; } } XFREE(buf); return MP_OKAY; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ #ifdef BN_MP_GCD_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* Greatest Common Divisor using the binary method */ int mp_gcd(mp_int *a, mp_int *b, mp_int *c) { mp_int u, v; int k, u_lsb, v_lsb, res; /* either zero than gcd is the largest */ if (mp_iszero(a) == MP_YES) { return mp_abs(b, c); } if (mp_iszero(b) == MP_YES) { return mp_abs(a, c); } /* get copies of a and b we can modify */ if ((res = mp_init_copy(&u, a)) != MP_OKAY) { return res; } if ((res = mp_init_copy(&v, b)) != MP_OKAY) { goto LBL_U; } /* must be positive for the remainder of the algorithm */ u.sign = v.sign = MP_ZPOS; /* B1. Find the common power of two for u and v */ u_lsb = mp_cnt_lsb(&u); v_lsb = mp_cnt_lsb(&v); k = MIN(u_lsb, v_lsb); if (k > 0) { /* divide the power of two out */ if ((res = mp_div_2d(&u, k, &u, NULL)) != MP_OKAY) { goto LBL_V; } if ((res = mp_div_2d(&v, k, &v, NULL)) != MP_OKAY) { goto LBL_V; } } /* divide any remaining factors of two out */ if (u_lsb != k) { if ((res = mp_div_2d(&u, u_lsb - k, &u, NULL)) != MP_OKAY) { goto LBL_V; } } if (v_lsb != k) { if ((res = mp_div_2d(&v, v_lsb - k, &v, NULL)) != MP_OKAY) { goto LBL_V; } } while (mp_iszero(&v) == MP_NO) { /* make sure v is the largest */ if (mp_cmp_mag(&u, &v) == MP_GT) { /* swap u and v to make sure v is >= u */ mp_exch(&u, &v); } /* subtract smallest from largest */ if ((res = s_mp_sub(&v, &u, &v)) != MP_OKAY) { goto LBL_V; } /* Divide out all factors of two */ if ((res = mp_div_2d(&v, mp_cnt_lsb(&v), &v, NULL)) != MP_OKAY) { goto LBL_V; } } /* multiply by 2**k which we divided out at the beginning */ if ((res = mp_mul_2d(&u, k, c)) != MP_OKAY) { goto LBL_V; } c->sign = MP_ZPOS; res = MP_OKAY; LBL_V: mp_clear(&u); LBL_U: mp_clear(&v); return res; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ #ifdef BN_MP_GET_INT_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* get the lower 32-bits of an mp_int */ unsigned long mp_get_int(mp_int *a) { int i; mp_min_u32 res; if (a->used == 0) { return 0; } /* get number of digits of the lsb we have to read */ i = MIN(a->used, (int)(((sizeof(unsigned long) * CHAR_BIT) + DIGIT_BIT - 1) / DIGIT_BIT)) - 1; /* get most significant digit of result */ res = DIGIT(a, i); while (--i >= 0) { res = (res << DIGIT_BIT) | DIGIT(a, i); } /* force result to 32-bits always so it is consistent on non 32-bit platforms */ return res & 0xFFFFFFFFUL; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ #ifdef BN_MP_GET_LONG_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* get the lower unsigned long of an mp_int, platform dependent */ unsigned long mp_get_long(mp_int *a) { int i; unsigned long res; if (a->used == 0) { return 0; } /* get number of digits of the lsb we have to read */ i = MIN(a->used, (int)(((sizeof(unsigned long) * CHAR_BIT) + DIGIT_BIT - 1) / DIGIT_BIT)) - 1; /* get most significant digit of result */ res = DIGIT(a, i); #if (ULONG_MAX != 0xffffffffuL) || (DIGIT_BIT < 32) while (--i >= 0) { res = (res << DIGIT_BIT) | DIGIT(a, i); } #endif return res; } #endif #ifdef BN_MP_GET_LONG_LONG_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* get the lower unsigned long long of an mp_int, platform dependent */ unsigned long long mp_get_long_long(mp_int *a) { int i; unsigned long long res; if (a->used == 0) { return 0; } /* get number of digits of the lsb we have to read */ i = MIN(a->used, (int)(((sizeof(unsigned long long) * CHAR_BIT) + DIGIT_BIT - 1) / DIGIT_BIT)) - 1; /* get most significant digit of result */ res = DIGIT(a, i); #if DIGIT_BIT < 64 while (--i >= 0) { res = (res << DIGIT_BIT) | DIGIT(a, i); } #endif return res; } #endif #ifdef BN_MP_GROW_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* grow as required */ int mp_grow(mp_int *a, int size) { int i; mp_digit *tmp; /* if the alloc size is smaller alloc more ram */ if (a->alloc < size) { /* ensure there are always at least MP_PREC digits extra on top */ size += (MP_PREC * 2) - (size % MP_PREC); /* reallocate the array a->dp * * We store the return in a temporary variable * in case the operation failed we don't want * to overwrite the dp member of a. */ tmp = OPT_CAST(mp_digit) XREALLOC(a->dp, sizeof(mp_digit) * size); if (tmp == NULL) { /* reallocation failed but "a" is still valid [can be freed] */ return MP_MEM; } /* reallocation succeeded so set a->dp */ a->dp = tmp; /* zero excess digits */ i = a->alloc; a->alloc = size; for ( ; i < a->alloc; i++) { a->dp[i] = 0; } } return MP_OKAY; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ #ifdef BN_MP_IMPORT_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* based on gmp's mpz_import. * see http://gmplib.org/manual/Integer-Import-and-Export.html */ int mp_import(mp_int *rop, size_t count, int order, size_t size, int endian, size_t nails, const void *op) { int result; size_t odd_nails, nail_bytes, i, j; unsigned char odd_nail_mask; mp_zero(rop); if (endian == 0) { union { unsigned int i; char c[4]; } lint; lint.i = 0x01020304; endian = (lint.c[0] == 4) ? -1 : 1; } odd_nails = (nails % 8); odd_nail_mask = 0xff; for (i = 0; i < odd_nails; ++i) { odd_nail_mask ^= (1 << (7 - i)); } nail_bytes = nails / 8; for (i = 0; i < count; ++i) { for (j = 0; j < (size - nail_bytes); ++j) { unsigned char byte = *( (unsigned char *)op + (((order == 1) ? i : ((count - 1) - i)) * size) + ((endian == 1) ? (j + nail_bytes) : (((size - 1) - j) - nail_bytes)) ); if ( (result = mp_mul_2d(rop, ((j == 0) ? (8 - odd_nails) : 8), rop)) != MP_OKAY) { return result; } rop->dp[0] |= (j == 0) ? (byte & odd_nail_mask) : byte; rop->used += 1; } } mp_clamp(rop); return MP_OKAY; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ #ifdef BN_MP_INIT_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* init a new mp_int */ int mp_init(mp_int *a) { int i; /* allocate memory required and clear it */ a->dp = OPT_CAST(mp_digit) XMALLOC(sizeof(mp_digit) * MP_PREC); if (a->dp == NULL) { return MP_MEM; } /* set the digits to zero */ for (i = 0; i < MP_PREC; i++) { a->dp[i] = 0; } /* set the used to zero, allocated digits to the default precision * and sign to positive */ a->used = 0; a->alloc = MP_PREC; a->sign = MP_ZPOS; return MP_OKAY; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ #ifdef BN_MP_INIT_COPY_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* creates "a" then copies b into it */ int mp_init_copy(mp_int *a, mp_int *b) { int res; if ((res = mp_init_size(a, b->used)) != MP_OKAY) { return res; } return mp_copy(b, a); } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ #ifdef BN_MP_INIT_MULTI_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ #include int mp_init_multi(mp_int *mp, ...) { mp_err res = MP_OKAY; /* Assume ok until proven otherwise */ int n = 0; /* Number of ok inits */ mp_int *cur_arg = mp; va_list args; va_start(args, mp); /* init args to next argument from caller */ while (cur_arg != NULL) { if (mp_init(cur_arg) != MP_OKAY) { /* Oops - error! Back-track and mp_clear what we already succeeded in init-ing, then return error. */ va_list clean_args; /* end the current list */ va_end(args); /* now start cleaning up */ cur_arg = mp; va_start(clean_args, mp); while (n-- != 0) { mp_clear(cur_arg); cur_arg = va_arg(clean_args, mp_int *); } va_end(clean_args); res = MP_MEM; break; } n++; cur_arg = va_arg(args, mp_int *); } va_end(args); return res; /* Assumed ok, if error flagged above. */ } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ #ifdef BN_MP_INIT_SET_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* initialize and set a digit */ int mp_init_set(mp_int *a, mp_digit b) { int err; if ((err = mp_init(a)) != MP_OKAY) { return err; } mp_set(a, b); return err; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ #ifdef BN_MP_INIT_SET_INT_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* initialize and set a digit */ int mp_init_set_int(mp_int *a, unsigned long b) { int err; if ((err = mp_init(a)) != MP_OKAY) { return err; } return mp_set_int(a, b); } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ #ifdef BN_MP_INIT_SIZE_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* init an mp_init for a given size */ int mp_init_size(mp_int *a, int size) { int x; /* pad size so there are always extra digits */ size += (MP_PREC * 2) - (size % MP_PREC); /* alloc mem */ a->dp = OPT_CAST(mp_digit) XMALLOC(sizeof(mp_digit) * size); if (a->dp == NULL) { return MP_MEM; } /* set the members */ a->used = 0; a->alloc = size; a->sign = MP_ZPOS; /* zero the digits */ for (x = 0; x < size; x++) { a->dp[x] = 0; } return MP_OKAY; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ #ifdef BN_MP_INVMOD_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* hac 14.61, pp608 */ int mp_invmod(mp_int *a, mp_int *b, mp_int *c) { /* b cannot be negative */ if ((b->sign == MP_NEG) || (mp_iszero(b) == MP_YES)) { return MP_VAL; } #ifdef BN_FAST_MP_INVMOD_C /* if the modulus is odd we can use a faster routine instead */ if (mp_isodd(b) == MP_YES) { return fast_mp_invmod(a, b, c); } #endif #ifdef BN_MP_INVMOD_SLOW_C return mp_invmod_slow(a, b, c); #else return MP_VAL; #endif } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ #ifdef BN_MP_INVMOD_SLOW_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* hac 14.61, pp608 */ int mp_invmod_slow(mp_int *a, mp_int *b, mp_int *c) { mp_int x, y, u, v, A, B, C, D; int res; /* b cannot be negative */ if ((b->sign == MP_NEG) || (mp_iszero(b) == MP_YES)) { return MP_VAL; } /* init temps */ if ((res = mp_init_multi(&x, &y, &u, &v, &A, &B, &C, &D, NULL)) != MP_OKAY) { return res; } /* x = a, y = b */ if ((res = mp_mod(a, b, &x)) != MP_OKAY) { goto LBL_ERR; } if ((res = mp_copy(b, &y)) != MP_OKAY) { goto LBL_ERR; } /* 2. [modified] if x,y are both even then return an error! */ if ((mp_iseven(&x) == MP_YES) && (mp_iseven(&y) == MP_YES)) { res = MP_VAL; goto LBL_ERR; } /* 3. u=x, v=y, A=1, B=0, C=0,D=1 */ if ((res = mp_copy(&x, &u)) != MP_OKAY) { goto LBL_ERR; } if ((res = mp_copy(&y, &v)) != MP_OKAY) { goto LBL_ERR; } mp_set(&A, 1); mp_set(&D, 1); top: /* 4. while u is even do */ while (mp_iseven(&u) == MP_YES) { /* 4.1 u = u/2 */ if ((res = mp_div_2(&u, &u)) != MP_OKAY) { goto LBL_ERR; } /* 4.2 if A or B is odd then */ if ((mp_isodd(&A) == MP_YES) || (mp_isodd(&B) == MP_YES)) { /* A = (A+y)/2, B = (B-x)/2 */ if ((res = mp_add(&A, &y, &A)) != MP_OKAY) { goto LBL_ERR; } if ((res = mp_sub(&B, &x, &B)) != MP_OKAY) { goto LBL_ERR; } } /* A = A/2, B = B/2 */ if ((res = mp_div_2(&A, &A)) != MP_OKAY) { goto LBL_ERR; } if ((res = mp_div_2(&B, &B)) != MP_OKAY) { goto LBL_ERR; } } /* 5. while v is even do */ while (mp_iseven(&v) == MP_YES) { /* 5.1 v = v/2 */ if ((res = mp_div_2(&v, &v)) != MP_OKAY) { goto LBL_ERR; } /* 5.2 if C or D is odd then */ if ((mp_isodd(&C) == MP_YES) || (mp_isodd(&D) == MP_YES)) { /* C = (C+y)/2, D = (D-x)/2 */ if ((res = mp_add(&C, &y, &C)) != MP_OKAY) { goto LBL_ERR; } if ((res = mp_sub(&D, &x, &D)) != MP_OKAY) { goto LBL_ERR; } } /* C = C/2, D = D/2 */ if ((res = mp_div_2(&C, &C)) != MP_OKAY) { goto LBL_ERR; } if ((res = mp_div_2(&D, &D)) != MP_OKAY) { goto LBL_ERR; } } /* 6. if u >= v then */ if (mp_cmp(&u, &v) != MP_LT) { /* u = u - v, A = A - C, B = B - D */ if ((res = mp_sub(&u, &v, &u)) != MP_OKAY) { goto LBL_ERR; } if ((res = mp_sub(&A, &C, &A)) != MP_OKAY) { goto LBL_ERR; } if ((res = mp_sub(&B, &D, &B)) != MP_OKAY) { goto LBL_ERR; } } else { /* v - v - u, C = C - A, D = D - B */ if ((res = mp_sub(&v, &u, &v)) != MP_OKAY) { goto LBL_ERR; } if ((res = mp_sub(&C, &A, &C)) != MP_OKAY) { goto LBL_ERR; } if ((res = mp_sub(&D, &B, &D)) != MP_OKAY) { goto LBL_ERR; } } /* if not zero goto step 4 */ if (mp_iszero(&u) == MP_NO) goto top; /* now a = C, b = D, gcd == g*v */ /* if v != 1 then there is no inverse */ if (mp_cmp_d(&v, 1) != MP_EQ) { res = MP_VAL; goto LBL_ERR; } /* if its too low */ while (mp_cmp_d(&C, 0) == MP_LT) { if ((res = mp_add(&C, b, &C)) != MP_OKAY) { goto LBL_ERR; } } /* too big */ while (mp_cmp_mag(&C, b) != MP_LT) { if ((res = mp_sub(&C, b, &C)) != MP_OKAY) { goto LBL_ERR; } } /* C is now the inverse */ mp_exch(&C, c); res = MP_OKAY; LBL_ERR: mp_clear_multi(&x, &y, &u, &v, &A, &B, &C, &D, NULL); return res; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ #ifdef BN_MP_IS_SQUARE_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* Check if remainders are possible squares - fast exclude non-squares */ static const char rem_128[128] = { 0, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1 }; static const char rem_105[105] = { 0, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1 }; /* Store non-zero to ret if arg is square, and zero if not */ int mp_is_square(mp_int *arg, int *ret) { int res; mp_digit c; mp_int t; unsigned long r; /* Default to Non-square :) */ *ret = MP_NO; if (arg->sign == MP_NEG) { return MP_VAL; } /* digits used? (TSD) */ if (arg->used == 0) { return MP_OKAY; } /* First check mod 128 (suppose that DIGIT_BIT is at least 7) */ if (rem_128[127 & DIGIT(arg, 0)] == 1) { return MP_OKAY; } /* Next check mod 105 (3*5*7) */ if ((res = mp_mod_d(arg, 105, &c)) != MP_OKAY) { return res; } if (rem_105[c] == 1) { return MP_OKAY; } if ((res = mp_init_set_int(&t, 11L * 13L * 17L * 19L * 23L * 29L * 31L)) != MP_OKAY) { return res; } if ((res = mp_mod(arg, &t, &t)) != MP_OKAY) { goto ERR; } r = mp_get_int(&t); /* Check for other prime modules, note it's not an ERROR but we must * free "t" so the easiest way is to goto ERR. We know that res * is already equal to MP_OKAY from the mp_mod call */ if (((1L << (r % 11)) & 0x5C4L) != 0L) goto ERR; if (((1L << (r % 13)) & 0x9E4L) != 0L) goto ERR; if (((1L << (r % 17)) & 0x5CE8L) != 0L) goto ERR; if (((1L << (r % 19)) & 0x4F50CL) != 0L) goto ERR; if (((1L << (r % 23)) & 0x7ACCA0L) != 0L) goto ERR; if (((1L << (r % 29)) & 0xC2EDD0CL) != 0L) goto ERR; if (((1L << (r % 31)) & 0x6DE2B848L) != 0L) goto ERR; /* Final check - is sqr(sqrt(arg)) == arg ? */ if ((res = mp_sqrt(arg, &t)) != MP_OKAY) { goto ERR; } if ((res = mp_sqr(&t, &t)) != MP_OKAY) { goto ERR; } *ret = (mp_cmp_mag(&t, arg) == MP_EQ) ? MP_YES : MP_NO; ERR: mp_clear(&t); return res; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ #ifdef BN_MP_JACOBI_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* computes the jacobi c = (a | n) (or Legendre if n is prime) * HAC pp. 73 Algorithm 2.149 * HAC is wrong here, as the special case of (0 | 1) is not * handled correctly. */ int mp_jacobi(mp_int *a, mp_int *n, int *c) { mp_int a1, p1; int k, s, r, res; mp_digit residue; /* if n <= 0 return MP_VAL */ if (mp_cmp_d(n, 0) != MP_GT) { return MP_VAL; } /* step 1. handle case of a == 0 */ if (mp_iszero(a) == MP_YES) { /* special case of a == 0 and n == 1 */ if (mp_cmp_d(n, 1) == MP_EQ) { *c = 1; } else { *c = 0; } return MP_OKAY; } /* step 2. if a == 1, return 1 */ if (mp_cmp_d(a, 1) == MP_EQ) { *c = 1; return MP_OKAY; } /* default */ s = 0; /* step 3. write a = a1 * 2**k */ if ((res = mp_init_copy(&a1, a)) != MP_OKAY) { return res; } if ((res = mp_init(&p1)) != MP_OKAY) { goto LBL_A1; } /* divide out larger power of two */ k = mp_cnt_lsb(&a1); if ((res = mp_div_2d(&a1, k, &a1, NULL)) != MP_OKAY) { goto LBL_P1; } /* step 4. if e is even set s=1 */ if ((k & 1) == 0) { s = 1; } else { /* else set s=1 if p = 1/7 (mod 8) or s=-1 if p = 3/5 (mod 8) */ residue = n->dp[0] & 7; if ((residue == 1) || (residue == 7)) { s = 1; } else if ((residue == 3) || (residue == 5)) { s = -1; } } /* step 5. if p == 3 (mod 4) *and* a1 == 3 (mod 4) then s = -s */ if (((n->dp[0] & 3) == 3) && ((a1.dp[0] & 3) == 3)) { s = -s; } /* if a1 == 1 we're done */ if (mp_cmp_d(&a1, 1) == MP_EQ) { *c = s; } else { /* n1 = n mod a1 */ if ((res = mp_mod(n, &a1, &p1)) != MP_OKAY) { goto LBL_P1; } if ((res = mp_jacobi(&p1, &a1, &r)) != MP_OKAY) { goto LBL_P1; } *c = s * r; } /* done */ res = MP_OKAY; LBL_P1: mp_clear(&p1); LBL_A1: mp_clear(&a1); return res; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ #ifdef BN_MP_KARATSUBA_MUL_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* c = |a| * |b| using Karatsuba Multiplication using * three half size multiplications * * Let B represent the radix [e.g. 2**DIGIT_BIT] and * let n represent half of the number of digits in * the min(a,b) * * a = a1 * B**n + a0 * b = b1 * B**n + b0 * * Then, a * b => a1b1 * B**2n + ((a1 + a0)(b1 + b0) - (a0b0 + a1b1)) * B + a0b0 * * Note that a1b1 and a0b0 are used twice and only need to be * computed once. So in total three half size (half # of * digit) multiplications are performed, a0b0, a1b1 and * (a1+b1)(a0+b0) * * Note that a multiplication of half the digits requires * 1/4th the number of single precision multiplications so in * total after one call 25% of the single precision multiplications * are saved. Note also that the call to mp_mul can end up back * in this function if the a0, a1, b0, or b1 are above the threshold. * This is known as divide-and-conquer and leads to the famous * O(N**lg(3)) or O(N**1.584) work which is asymptopically lower than * the standard O(N**2) that the baseline/comba methods use. * Generally though the overhead of this method doesn't pay off * until a certain size (N ~ 80) is reached. */ int mp_karatsuba_mul(mp_int *a, mp_int *b, mp_int *c) { mp_int x0, x1, y0, y1, t1, x0y0, x1y1; int B, err; /* default the return code to an error */ err = MP_MEM; /* min # of digits */ B = MIN(a->used, b->used); /* now divide in two */ B = B >> 1; /* init copy all the temps */ if (mp_init_size(&x0, B) != MP_OKAY) goto ERR; if (mp_init_size(&x1, a->used - B) != MP_OKAY) goto X0; if (mp_init_size(&y0, B) != MP_OKAY) goto X1; if (mp_init_size(&y1, b->used - B) != MP_OKAY) goto Y0; /* init temps */ if (mp_init_size(&t1, B * 2) != MP_OKAY) goto Y1; if (mp_init_size(&x0y0, B * 2) != MP_OKAY) goto T1; if (mp_init_size(&x1y1, B * 2) != MP_OKAY) goto X0Y0; /* now shift the digits */ x0.used = y0.used = B; x1.used = a->used - B; y1.used = b->used - B; { int x; mp_digit *tmpa, *tmpb, *tmpx, *tmpy; /* we copy the digits directly instead of using higher level functions * since we also need to shift the digits */ tmpa = a->dp; tmpb = b->dp; tmpx = x0.dp; tmpy = y0.dp; for (x = 0; x < B; x++) { *tmpx++ = *tmpa++; *tmpy++ = *tmpb++; } tmpx = x1.dp; for (x = B; x < a->used; x++) { *tmpx++ = *tmpa++; } tmpy = y1.dp; for (x = B; x < b->used; x++) { *tmpy++ = *tmpb++; } } /* only need to clamp the lower words since by definition the * upper words x1/y1 must have a known number of digits */ mp_clamp(&x0); mp_clamp(&y0); /* now calc the products x0y0 and x1y1 */ /* after this x0 is no longer required, free temp [x0==t2]! */ if (mp_mul(&x0, &y0, &x0y0) != MP_OKAY) goto X1Y1; /* x0y0 = x0*y0 */ if (mp_mul(&x1, &y1, &x1y1) != MP_OKAY) goto X1Y1; /* x1y1 = x1*y1 */ /* now calc x1+x0 and y1+y0 */ if (s_mp_add(&x1, &x0, &t1) != MP_OKAY) goto X1Y1; /* t1 = x1 - x0 */ if (s_mp_add(&y1, &y0, &x0) != MP_OKAY) goto X1Y1; /* t2 = y1 - y0 */ if (mp_mul(&t1, &x0, &t1) != MP_OKAY) goto X1Y1; /* t1 = (x1 + x0) * (y1 + y0) */ /* add x0y0 */ if (mp_add(&x0y0, &x1y1, &x0) != MP_OKAY) goto X1Y1; /* t2 = x0y0 + x1y1 */ if (s_mp_sub(&t1, &x0, &t1) != MP_OKAY) goto X1Y1; /* t1 = (x1+x0)*(y1+y0) - (x1y1 + x0y0) */ /* shift by B */ if (mp_lshd(&t1, B) != MP_OKAY) goto X1Y1; /* t1 = (x0y0 + x1y1 - (x1-x0)*(y1-y0))<used; /* now divide in two */ B = B >> 1; /* init copy all the temps */ if (mp_init_size(&x0, B) != MP_OKAY) goto ERR; if (mp_init_size(&x1, a->used - B) != MP_OKAY) goto X0; /* init temps */ if (mp_init_size(&t1, a->used * 2) != MP_OKAY) goto X1; if (mp_init_size(&t2, a->used * 2) != MP_OKAY) goto T1; if (mp_init_size(&x0x0, B * 2) != MP_OKAY) goto T2; if (mp_init_size(&x1x1, (a->used - B) * 2) != MP_OKAY) goto X0X0; { int x; mp_digit *dst, *src; src = a->dp; /* now shift the digits */ dst = x0.dp; for (x = 0; x < B; x++) { *dst++ = *src++; } dst = x1.dp; for (x = B; x < a->used; x++) { *dst++ = *src++; } } x0.used = B; x1.used = a->used - B; mp_clamp(&x0); /* now calc the products x0*x0 and x1*x1 */ if (mp_sqr(&x0, &x0x0) != MP_OKAY) goto X1X1; /* x0x0 = x0*x0 */ if (mp_sqr(&x1, &x1x1) != MP_OKAY) goto X1X1; /* x1x1 = x1*x1 */ /* now calc (x1+x0)**2 */ if (s_mp_add(&x1, &x0, &t1) != MP_OKAY) goto X1X1; /* t1 = x1 - x0 */ if (mp_sqr(&t1, &t1) != MP_OKAY) goto X1X1; /* t1 = (x1 - x0) * (x1 - x0) */ /* add x0y0 */ if (s_mp_add(&x0x0, &x1x1, &t2) != MP_OKAY) goto X1X1; /* t2 = x0x0 + x1x1 */ if (s_mp_sub(&t1, &t2, &t1) != MP_OKAY) goto X1X1; /* t1 = (x1+x0)**2 - (x0x0 + x1x1) */ /* shift by B */ if (mp_lshd(&t1, B) != MP_OKAY) goto X1X1; /* t1 = (x0x0 + x1x1 - (x1-x0)*(x1-x0))<sign = MP_ZPOS; LBL_T: mp_clear_multi(&t1, &t2, NULL); return res; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ #ifdef BN_MP_LSHD_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* shift left a certain amount of digits */ int mp_lshd(mp_int *a, int b) { int x, res; /* if its less than zero return */ if (b <= 0) { return MP_OKAY; } /* grow to fit the new digits */ if (a->alloc < (a->used + b)) { if ((res = mp_grow(a, a->used + b)) != MP_OKAY) { return res; } } { mp_digit *top, *bottom; /* increment the used by the shift amount then copy upwards */ a->used += b; /* top */ top = a->dp + a->used - 1; /* base */ bottom = (a->dp + a->used - 1) - b; /* much like mp_rshd this is implemented using a sliding window * except the window goes the otherway around. Copying from * the bottom to the top. see bn_mp_rshd.c for more info. */ for (x = a->used - 1; x >= b; x--) { *top-- = *bottom--; } /* zero the lower digits */ top = a->dp; for (x = 0; x < b; x++) { *top++ = 0; } } return MP_OKAY; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ #ifdef BN_MP_MOD_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* c = a mod b, 0 <= c < b if b > 0, b < c <= 0 if b < 0 */ int mp_mod(mp_int *a, mp_int *b, mp_int *c) { mp_int t; int res; if ((res = mp_init(&t)) != MP_OKAY) { return res; } if ((res = mp_div(a, b, NULL, &t)) != MP_OKAY) { mp_clear(&t); return res; } if ((mp_iszero(&t) != MP_NO) || (t.sign == b->sign)) { res = MP_OKAY; mp_exch(&t, c); } else { res = mp_add(b, &t, c); } mp_clear(&t); return res; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ #ifdef BN_MP_MOD_2D_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* calc a value mod 2**b */ int mp_mod_2d(mp_int *a, int b, mp_int *c) { int x, res; /* if b is <= 0 then zero the int */ if (b <= 0) { mp_zero(c); return MP_OKAY; } /* if the modulus is larger than the value than return */ if (b >= (int)(a->used * DIGIT_BIT)) { res = mp_copy(a, c); return res; } /* copy */ if ((res = mp_copy(a, c)) != MP_OKAY) { return res; } /* zero digits above the last digit of the modulus */ for (x = (b / DIGIT_BIT) + (((b % DIGIT_BIT) == 0) ? 0 : 1); x < c->used; x++) { c->dp[x] = 0; } /* clear the digit that is not completely outside/inside the modulus */ c->dp[b / DIGIT_BIT] &= (mp_digit)((((mp_digit)1) << (((mp_digit)b) % DIGIT_BIT)) - ((mp_digit)1)); mp_clamp(c); return MP_OKAY; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ #ifdef BN_MP_MOD_D_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ int mp_mod_d(mp_int *a, mp_digit b, mp_digit *c) { return mp_div_d(a, b, NULL, c); } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ #ifdef BN_MP_MONTGOMERY_CALC_NORMALIZATION_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* * shifts with subtractions when the result is greater than b. * * The method is slightly modified to shift B unconditionally upto just under * the leading bit of b. This saves alot of multiple precision shifting. */ int mp_montgomery_calc_normalization(mp_int *a, mp_int *b) { int x, bits, res; /* how many bits of last digit does b use */ bits = mp_count_bits(b) % DIGIT_BIT; if (b->used > 1) { if ((res = mp_2expt(a, ((b->used - 1) * DIGIT_BIT) + bits - 1)) != MP_OKAY) { return res; } } else { mp_set(a, 1); bits = 1; } /* now compute C = A * B mod b */ for (x = bits - 1; x < (int)DIGIT_BIT; x++) { if ((res = mp_mul_2(a, a)) != MP_OKAY) { return res; } if (mp_cmp_mag(a, b) != MP_LT) { if ((res = s_mp_sub(a, b, a)) != MP_OKAY) { return res; } } } return MP_OKAY; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ #ifdef BN_MP_MONTGOMERY_REDUCE_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* computes xR**-1 == x (mod N) via Montgomery Reduction */ int mp_montgomery_reduce(mp_int *x, mp_int *n, mp_digit rho) { int ix, res, digs; mp_digit mu; /* can the fast reduction [comba] method be used? * * Note that unlike in mul you're safely allowed *less* * than the available columns [255 per default] since carries * are fixed up in the inner loop. */ digs = (n->used * 2) + 1; if ((digs < MP_WARRAY) && (n->used < (1 << ((CHAR_BIT * sizeof(mp_word)) - (2 * DIGIT_BIT))))) { return fast_mp_montgomery_reduce(x, n, rho); } /* grow the input as required */ if (x->alloc < digs) { if ((res = mp_grow(x, digs)) != MP_OKAY) { return res; } } x->used = digs; for (ix = 0; ix < n->used; ix++) { /* mu = ai * rho mod b * * The value of rho must be precalculated via * montgomery_setup() such that * it equals -1/n0 mod b this allows the * following inner loop to reduce the * input one digit at a time */ mu = (mp_digit)(((mp_word)x->dp[ix] * (mp_word)rho) & MP_MASK); /* a = a + mu * m * b**i */ { int iy; mp_digit *tmpn, *tmpx, u; mp_word r; /* alias for digits of the modulus */ tmpn = n->dp; /* alias for the digits of x [the input] */ tmpx = x->dp + ix; /* set the carry to zero */ u = 0; /* Multiply and add in place */ for (iy = 0; iy < n->used; iy++) { /* compute product and sum */ r = ((mp_word)mu * (mp_word) * tmpn++) + (mp_word)u + (mp_word) * tmpx; /* get carry */ u = (mp_digit)(r >> ((mp_word)DIGIT_BIT)); /* fix digit */ *tmpx++ = (mp_digit)(r & ((mp_word)MP_MASK)); } /* At this point the ix'th digit of x should be zero */ /* propagate carries upwards as required*/ while (u != 0) { *tmpx += u; u = *tmpx >> DIGIT_BIT; *tmpx++ &= MP_MASK; } } } /* at this point the n.used'th least * significant digits of x are all zero * which means we can shift x to the * right by n.used digits and the * residue is unchanged. */ /* x = x/b**n.used */ mp_clamp(x); mp_rshd(x, n->used); /* if x >= n then x = x - n */ if (mp_cmp_mag(x, n) != MP_LT) { return s_mp_sub(x, n, x); } return MP_OKAY; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ #ifdef BN_MP_MONTGOMERY_SETUP_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* setups the montgomery reduction stuff */ int mp_montgomery_setup(mp_int *n, mp_digit *rho) { mp_digit x, b; /* fast inversion mod 2**k * * Based on the fact that * * XA = 1 (mod 2**n) => (X(2-XA)) A = 1 (mod 2**2n) * => 2*X*A - X*X*A*A = 1 * => 2*(1) - (1) = 1 */ b = n->dp[0]; if ((b & 1) == 0) { return MP_VAL; } x = (((b + 2) & 4) << 1) + b; /* here x*a==1 mod 2**4 */ x *= 2 - (b * x); /* here x*a==1 mod 2**8 */ #if !defined(MP_8BIT) x *= 2 - (b * x); /* here x*a==1 mod 2**16 */ #endif #if defined(MP_64BIT) || !(defined(MP_8BIT) || defined(MP_16BIT)) x *= 2 - (b * x); /* here x*a==1 mod 2**32 */ #endif #ifdef MP_64BIT x *= 2 - (b * x); /* here x*a==1 mod 2**64 */ #endif /* rho = -1/m mod b */ *rho = (mp_digit)(((mp_word)1 << ((mp_word)DIGIT_BIT)) - x) & MP_MASK; return MP_OKAY; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ #ifdef BN_MP_MUL_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* high level multiplication (handles sign) */ int mp_mul(mp_int *a, mp_int *b, mp_int *c) { int res, neg; neg = (a->sign == b->sign) ? MP_ZPOS : MP_NEG; /* use Toom-Cook? */ #ifdef BN_MP_TOOM_MUL_C if (MIN(a->used, b->used) >= TOOM_MUL_CUTOFF) { res = mp_toom_mul(a, b, c); } else #endif #ifdef BN_MP_KARATSUBA_MUL_C /* use Karatsuba? */ if (MIN(a->used, b->used) >= KARATSUBA_MUL_CUTOFF) { res = mp_karatsuba_mul(a, b, c); } else #endif { /* can we use the fast multiplier? * * The fast multiplier can be used if the output will * have less than MP_WARRAY digits and the number of * digits won't affect carry propagation */ int digs = a->used + b->used + 1; #ifdef BN_FAST_S_MP_MUL_DIGS_C if ((digs < MP_WARRAY) && (MIN(a->used, b->used) <= (1 << ((CHAR_BIT * sizeof(mp_word)) - (2 * DIGIT_BIT))))) { res = fast_s_mp_mul_digs(a, b, c, digs); } else #endif { #ifdef BN_S_MP_MUL_DIGS_C res = s_mp_mul(a, b, c); /* uses s_mp_mul_digs */ #else res = MP_VAL; #endif } } c->sign = (c->used > 0) ? neg : MP_ZPOS; return res; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ #ifdef BN_MP_MUL_2_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* b = a*2 */ int mp_mul_2(mp_int *a, mp_int *b) { int x, res, oldused; /* grow to accomodate result */ if (b->alloc < (a->used + 1)) { if ((res = mp_grow(b, a->used + 1)) != MP_OKAY) { return res; } } oldused = b->used; b->used = a->used; { mp_digit r, rr, *tmpa, *tmpb; /* alias for source */ tmpa = a->dp; /* alias for dest */ tmpb = b->dp; /* carry */ r = 0; for (x = 0; x < a->used; x++) { /* get what will be the *next* carry bit from the * MSB of the current digit */ rr = *tmpa >> ((mp_digit)(DIGIT_BIT - 1)); /* now shift up this digit, add in the carry [from the previous] */ *tmpb++ = ((*tmpa++ << ((mp_digit)1)) | r) & MP_MASK; /* copy the carry that would be from the source * digit into the next iteration */ r = rr; } /* new leading digit? */ if (r != 0) { /* add a MSB which is always 1 at this point */ *tmpb = 1; ++(b->used); } /* now zero any excess digits on the destination * that we didn't write to */ tmpb = b->dp + b->used; for (x = b->used; x < oldused; x++) { *tmpb++ = 0; } } b->sign = a->sign; return MP_OKAY; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ #ifdef BN_MP_MUL_2D_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* shift left by a certain bit count */ int mp_mul_2d(mp_int *a, int b, mp_int *c) { mp_digit d; int res; /* copy */ if (a != c) { if ((res = mp_copy(a, c)) != MP_OKAY) { return res; } } if (c->alloc < (int)(c->used + (b / DIGIT_BIT) + 1)) { if ((res = mp_grow(c, c->used + (b / DIGIT_BIT) + 1)) != MP_OKAY) { return res; } } /* shift by as many digits in the bit count */ if (b >= (int)DIGIT_BIT) { if ((res = mp_lshd(c, b / DIGIT_BIT)) != MP_OKAY) { return res; } } /* shift any bit count < DIGIT_BIT */ d = (mp_digit)(b % DIGIT_BIT); if (d != 0) { mp_digit *tmpc, shift, mask, r, rr; int x; /* bitmask for carries */ mask = (((mp_digit)1) << d) - 1; /* shift for msbs */ shift = DIGIT_BIT - d; /* alias */ tmpc = c->dp; /* carry */ r = 0; for (x = 0; x < c->used; x++) { /* get the higher bits of the current word */ rr = (*tmpc >> shift) & mask; /* shift the current word and OR in the carry */ *tmpc = ((*tmpc << d) | r) & MP_MASK; ++tmpc; /* set the carry to the carry bits of the current word */ r = rr; } /* set final carry */ if (r != 0) { c->dp[(c->used)++] = r; } } mp_clamp(c); return MP_OKAY; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ #ifdef BN_MP_MUL_D_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* multiply by a digit */ int mp_mul_d(mp_int *a, mp_digit b, mp_int *c) { mp_digit u, *tmpa, *tmpc; mp_word r; int ix, res, olduse; /* make sure c is big enough to hold a*b */ if (c->alloc < (a->used + 1)) { if ((res = mp_grow(c, a->used + 1)) != MP_OKAY) { return res; } } /* get the original destinations used count */ olduse = c->used; /* set the sign */ c->sign = a->sign; /* alias for a->dp [source] */ tmpa = a->dp; /* alias for c->dp [dest] */ tmpc = c->dp; /* zero carry */ u = 0; /* compute columns */ for (ix = 0; ix < a->used; ix++) { /* compute product and carry sum for this term */ r = (mp_word)u + ((mp_word) * tmpa++ *(mp_word)b); /* mask off higher bits to get a single digit */ *tmpc++ = (mp_digit)(r & ((mp_word)MP_MASK)); /* send carry into next iteration */ u = (mp_digit)(r >> ((mp_word)DIGIT_BIT)); } /* store final carry [if any] and increment ix offset */ *tmpc++ = u; ++ix; /* now zero digits above the top */ while (ix++ < olduse) { *tmpc++ = 0; } /* set used count */ c->used = a->used + 1; mp_clamp(c); return MP_OKAY; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ #ifdef BN_MP_MULMOD_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* d = a * b (mod c) */ int mp_mulmod(mp_int *a, mp_int *b, mp_int *c, mp_int *d) { int res; mp_int t; if ((res = mp_init(&t)) != MP_OKAY) { return res; } if ((res = mp_mul(a, b, &t)) != MP_OKAY) { mp_clear(&t); return res; } res = mp_mod(&t, c, d); mp_clear(&t); return res; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ #ifdef BN_MP_N_ROOT_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* wrapper function for mp_n_root_ex() * computes c = (a)**(1/b) such that (c)**b <= a and (c+1)**b > a */ int mp_n_root(mp_int *a, mp_digit b, mp_int *c) { return mp_n_root_ex(a, b, c, 0); } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ #ifdef BN_MP_N_ROOT_EX_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* find the n'th root of an integer * * Result found such that (c)**b <= a and (c+1)**b > a * * This algorithm uses Newton's approximation * x[i+1] = x[i] - f(x[i])/f'(x[i]) * which will find the root in log(N) time where * each step involves a fair bit. This is not meant to * find huge roots [square and cube, etc]. */ int mp_n_root_ex(mp_int *a, mp_digit b, mp_int *c, int fast) { mp_int t1, t2, t3; int res, neg; /* input must be positive if b is even */ if (((b & 1) == 0) && (a->sign == MP_NEG)) { return MP_VAL; } if ((res = mp_init(&t1)) != MP_OKAY) { return res; } if ((res = mp_init(&t2)) != MP_OKAY) { goto LBL_T1; } if ((res = mp_init(&t3)) != MP_OKAY) { goto LBL_T2; } /* if a is negative fudge the sign but keep track */ neg = a->sign; a->sign = MP_ZPOS; /* t2 = 2 */ mp_set(&t2, 2); do { /* t1 = t2 */ if ((res = mp_copy(&t2, &t1)) != MP_OKAY) { goto LBL_T3; } /* t2 = t1 - ((t1**b - a) / (b * t1**(b-1))) */ /* t3 = t1**(b-1) */ if ((res = mp_expt_d_ex(&t1, b - 1, &t3, fast)) != MP_OKAY) { goto LBL_T3; } /* numerator */ /* t2 = t1**b */ if ((res = mp_mul(&t3, &t1, &t2)) != MP_OKAY) { goto LBL_T3; } /* t2 = t1**b - a */ if ((res = mp_sub(&t2, a, &t2)) != MP_OKAY) { goto LBL_T3; } /* denominator */ /* t3 = t1**(b-1) * b */ if ((res = mp_mul_d(&t3, b, &t3)) != MP_OKAY) { goto LBL_T3; } /* t3 = (t1**b - a)/(b * t1**(b-1)) */ if ((res = mp_div(&t2, &t3, &t3, NULL)) != MP_OKAY) { goto LBL_T3; } if ((res = mp_sub(&t1, &t3, &t2)) != MP_OKAY) { goto LBL_T3; } } while (mp_cmp(&t1, &t2) != MP_EQ); /* result can be off by a few so check */ for ( ; ; ) { if ((res = mp_expt_d_ex(&t1, b, &t2, fast)) != MP_OKAY) { goto LBL_T3; } if (mp_cmp(&t2, a) == MP_GT) { if ((res = mp_sub_d(&t1, 1, &t1)) != MP_OKAY) { goto LBL_T3; } } else { break; } } /* reset the sign of a first */ a->sign = neg; /* set the result */ mp_exch(&t1, c); /* set the sign of the result */ c->sign = neg; res = MP_OKAY; LBL_T3: mp_clear(&t3); LBL_T2: mp_clear(&t2); LBL_T1: mp_clear(&t1); return res; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ #ifdef BN_MP_NEG_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* b = -a */ int mp_neg(mp_int *a, mp_int *b) { int res; if (a != b) { if ((res = mp_copy(a, b)) != MP_OKAY) { return res; } } if (mp_iszero(b) != MP_YES) { b->sign = (a->sign == MP_ZPOS) ? MP_NEG : MP_ZPOS; } else { b->sign = MP_ZPOS; } return MP_OKAY; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ #ifdef BN_MP_OR_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* OR two ints together */ int mp_or(mp_int *a, mp_int *b, mp_int *c) { int res, ix, px; mp_int t, *x; if (a->used > b->used) { if ((res = mp_init_copy(&t, a)) != MP_OKAY) { return res; } px = b->used; x = b; } else { if ((res = mp_init_copy(&t, b)) != MP_OKAY) { return res; } px = a->used; x = a; } for (ix = 0; ix < px; ix++) { t.dp[ix] |= x->dp[ix]; } mp_clamp(&t); mp_exch(c, &t); mp_clear(&t); return MP_OKAY; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ #ifdef BN_MP_PRIME_FERMAT_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* performs one Fermat test. * * If "a" were prime then b**a == b (mod a) since the order of * the multiplicative sub-group would be phi(a) = a-1. That means * it would be the same as b**(a mod (a-1)) == b**1 == b (mod a). * * Sets result to 1 if the congruence holds, or zero otherwise. */ int mp_prime_fermat(mp_int *a, mp_int *b, int *result) { mp_int t; int err; /* default to composite */ *result = MP_NO; /* ensure b > 1 */ if (mp_cmp_d(b, 1) != MP_GT) { return MP_VAL; } /* init t */ if ((err = mp_init(&t)) != MP_OKAY) { return err; } /* compute t = b**a mod a */ if ((err = mp_exptmod(b, a, a, &t)) != MP_OKAY) { goto LBL_T; } /* is it equal to b? */ if (mp_cmp(&t, b) == MP_EQ) { *result = MP_YES; } err = MP_OKAY; LBL_T: mp_clear(&t); return err; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ #ifdef BN_MP_PRIME_IS_DIVISIBLE_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* determines if an integers is divisible by one * of the first PRIME_SIZE primes or not * * sets result to 0 if not, 1 if yes */ int mp_prime_is_divisible(mp_int *a, int *result) { int err, ix; mp_digit res; /* default to not */ *result = MP_NO; for (ix = 0; ix < PRIME_SIZE; ix++) { /* what is a mod LBL_prime_tab[ix] */ if ((err = mp_mod_d(a, ltm_prime_tab[ix], &res)) != MP_OKAY) { return err; } /* is the residue zero? */ if (res == 0) { *result = MP_YES; return MP_OKAY; } } return MP_OKAY; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ #ifdef BN_MP_PRIME_IS_PRIME_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* performs a variable number of rounds of Miller-Rabin * * Probability of error after t rounds is no more than * * Sets result to 1 if probably prime, 0 otherwise */ int mp_prime_is_prime(mp_int *a, int t, int *result) { mp_int b; int ix, err, res; /* default to no */ *result = MP_NO; /* valid value of t? */ if ((t <= 0) || (t > PRIME_SIZE)) { return MP_VAL; } /* is the input equal to one of the primes in the table? */ for (ix = 0; ix < PRIME_SIZE; ix++) { if (mp_cmp_d(a, ltm_prime_tab[ix]) == MP_EQ) { *result = 1; return MP_OKAY; } } /* first perform trial division */ if ((err = mp_prime_is_divisible(a, &res)) != MP_OKAY) { return err; } /* return if it was trivially divisible */ if (res == MP_YES) { return MP_OKAY; } /* now perform the miller-rabin rounds */ if ((err = mp_init(&b)) != MP_OKAY) { return err; } for (ix = 0; ix < t; ix++) { /* set the prime */ mp_set(&b, ltm_prime_tab[ix]); if ((err = mp_prime_miller_rabin(a, &b, &res)) != MP_OKAY) { goto LBL_B; } if (res == MP_NO) { goto LBL_B; } } /* passed the test */ *result = MP_YES; LBL_B: mp_clear(&b); return err; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ #ifdef BN_MP_PRIME_MILLER_RABIN_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* Miller-Rabin test of "a" to the base of "b" as described in * HAC pp. 139 Algorithm 4.24 * * Sets result to 0 if definitely composite or 1 if probably prime. * Randomly the chance of error is no more than 1/4 and often * very much lower. */ int mp_prime_miller_rabin(mp_int *a, mp_int *b, int *result) { mp_int n1, y, r; int s, j, err; /* default */ *result = MP_NO; /* ensure b > 1 */ if (mp_cmp_d(b, 1) != MP_GT) { return MP_VAL; } /* get n1 = a - 1 */ if ((err = mp_init_copy(&n1, a)) != MP_OKAY) { return err; } if ((err = mp_sub_d(&n1, 1, &n1)) != MP_OKAY) { goto LBL_N1; } /* set 2**s * r = n1 */ if ((err = mp_init_copy(&r, &n1)) != MP_OKAY) { goto LBL_N1; } /* count the number of least significant bits * which are zero */ s = mp_cnt_lsb(&r); /* now divide n - 1 by 2**s */ if ((err = mp_div_2d(&r, s, &r, NULL)) != MP_OKAY) { goto LBL_R; } /* compute y = b**r mod a */ if ((err = mp_init(&y)) != MP_OKAY) { goto LBL_R; } if ((err = mp_exptmod(b, &r, a, &y)) != MP_OKAY) { goto LBL_Y; } /* if y != 1 and y != n1 do */ if ((mp_cmp_d(&y, 1) != MP_EQ) && (mp_cmp(&y, &n1) != MP_EQ)) { j = 1; /* while j <= s-1 and y != n1 */ while ((j <= (s - 1)) && (mp_cmp(&y, &n1) != MP_EQ)) { if ((err = mp_sqrmod(&y, a, &y)) != MP_OKAY) { goto LBL_Y; } /* if y == 1 then composite */ if (mp_cmp_d(&y, 1) == MP_EQ) { goto LBL_Y; } ++j; } /* if y != n1 then composite */ if (mp_cmp(&y, &n1) != MP_EQ) { goto LBL_Y; } } /* probably prime now */ *result = MP_YES; LBL_Y: mp_clear(&y); LBL_R: mp_clear(&r); LBL_N1: mp_clear(&n1); return err; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ #ifdef BN_MP_PRIME_NEXT_PRIME_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* finds the next prime after the number "a" using "t" trials * of Miller-Rabin. * * bbs_style = 1 means the prime must be congruent to 3 mod 4 */ int mp_prime_next_prime(mp_int *a, int t, int bbs_style) { int err, res = MP_NO, x, y; mp_digit res_tab[PRIME_SIZE], step, kstep; mp_int b; /* ensure t is valid */ if ((t <= 0) || (t > PRIME_SIZE)) { return MP_VAL; } /* force positive */ a->sign = MP_ZPOS; /* simple algo if a is less than the largest prime in the table */ if (mp_cmp_d(a, ltm_prime_tab[PRIME_SIZE - 1]) == MP_LT) { /* find which prime it is bigger than */ for (x = PRIME_SIZE - 2; x >= 0; x--) { if (mp_cmp_d(a, ltm_prime_tab[x]) != MP_LT) { if (bbs_style == 1) { /* ok we found a prime smaller or * equal [so the next is larger] * * however, the prime must be * congruent to 3 mod 4 */ if ((ltm_prime_tab[x + 1] & 3) != 3) { /* scan upwards for a prime congruent to 3 mod 4 */ for (y = x + 1; y < PRIME_SIZE; y++) { if ((ltm_prime_tab[y] & 3) == 3) { mp_set(a, ltm_prime_tab[y]); return MP_OKAY; } } } } else { mp_set(a, ltm_prime_tab[x + 1]); return MP_OKAY; } } } /* at this point a maybe 1 */ if (mp_cmp_d(a, 1) == MP_EQ) { mp_set(a, 2); return MP_OKAY; } /* fall through to the sieve */ } /* generate a prime congruent to 3 mod 4 or 1/3 mod 4? */ if (bbs_style == 1) { kstep = 4; } else { kstep = 2; } /* at this point we will use a combination of a sieve and Miller-Rabin */ if (bbs_style == 1) { /* if a mod 4 != 3 subtract the correct value to make it so */ if ((a->dp[0] & 3) != 3) { if ((err = mp_sub_d(a, (a->dp[0] & 3) + 1, a)) != MP_OKAY) { return err; } } } else { if (mp_iseven(a) == MP_YES) { /* force odd */ if ((err = mp_sub_d(a, 1, a)) != MP_OKAY) { return err; } } } /* generate the restable */ for (x = 1; x < PRIME_SIZE; x++) { if ((err = mp_mod_d(a, ltm_prime_tab[x], res_tab + x)) != MP_OKAY) { return err; } } /* init temp used for Miller-Rabin Testing */ if ((err = mp_init(&b)) != MP_OKAY) { return err; } for ( ; ; ) { /* skip to the next non-trivially divisible candidate */ step = 0; do { /* y == 1 if any residue was zero [e.g. cannot be prime] */ y = 0; /* increase step to next candidate */ step += kstep; /* compute the new residue without using division */ for (x = 1; x < PRIME_SIZE; x++) { /* add the step to each residue */ res_tab[x] += kstep; /* subtract the modulus [instead of using division] */ if (res_tab[x] >= ltm_prime_tab[x]) { res_tab[x] -= ltm_prime_tab[x]; } /* set flag if zero */ if (res_tab[x] == 0) { y = 1; } } } while ((y == 1) && (step < ((((mp_digit)1) << DIGIT_BIT) - kstep))); /* add the step */ if ((err = mp_add_d(a, step, a)) != MP_OKAY) { goto LBL_ERR; } /* if didn't pass sieve and step == MAX then skip test */ if ((y == 1) && (step >= ((((mp_digit)1) << DIGIT_BIT) - kstep))) { continue; } /* is this prime? */ for (x = 0; x < t; x++) { mp_set(&b, ltm_prime_tab[x]); if ((err = mp_prime_miller_rabin(a, &b, &res)) != MP_OKAY) { goto LBL_ERR; } if (res == MP_NO) { break; } } if (res == MP_YES) { break; } } err = MP_OKAY; LBL_ERR: mp_clear(&b); return err; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ #ifdef BN_MP_PRIME_RABIN_MILLER_TRIALS_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ static const struct { int k, t; } https_internal_sizes[] = { { 128, 28 }, { 256, 16 }, { 384, 10 }, { 512, 7 }, { 640, 6 }, { 768, 5 }, { 896, 4 }, { 1024, 4 } }; /* returns # of RM trials required for a given bit size */ int mp_prime_rabin_miller_trials(int size) { int x; for (x = 0; x < (int)(sizeof(https_internal_sizes) / (sizeof(https_internal_sizes[0]))); x++) { if (https_internal_sizes[x].k == size) { return https_internal_sizes[x].t; } else if (https_internal_sizes[x].k > size) { return (x == 0) ? https_internal_sizes[0].t : https_internal_sizes[x - 1].t; } } return https_internal_sizes[x - 1].t + 1; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ #ifdef BN_MP_PRIME_RANDOM_EX_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* makes a truly random prime of a given size (bits), * * Flags are as follows: * * LTM_PRIME_BBS - make prime congruent to 3 mod 4 * LTM_PRIME_SAFE - make sure (p-1)/2 is prime as well (implies LTM_PRIME_BBS) * LTM_PRIME_2MSB_ON - make the 2nd highest bit one * * You have to supply a callback which fills in a buffer with random bytes. "dat" is a parameter you can * have passed to the callback (e.g. a state or something). This function doesn't use "dat" itself * so it can be NULL * */ /* This is possibly the mother of all prime generation functions, muahahahahaha! */ int mp_prime_random_ex(mp_int *a, int t, int size, int flags, ltm_prime_callback cb, void *dat) { unsigned char *tmp, maskAND, maskOR_msb, maskOR_lsb; int res, err, bsize, maskOR_msb_offset; /* sanity check the input */ if ((size <= 1) || (t <= 0)) { return MP_VAL; } /* LTM_PRIME_SAFE implies LTM_PRIME_BBS */ if ((flags & LTM_PRIME_SAFE) != 0) { flags |= LTM_PRIME_BBS; } /* calc the byte size */ bsize = (size >> 3) + ((size & 7) ? 1 : 0); /* we need a buffer of bsize bytes */ tmp = OPT_CAST(unsigned char) XMALLOC(bsize); if (tmp == NULL) { return MP_MEM; } /* calc the maskAND value for the MSbyte*/ maskAND = ((size & 7) == 0) ? 0xFF : (0xFF >> (8 - (size & 7))); /* calc the maskOR_msb */ maskOR_msb = 0; maskOR_msb_offset = ((size & 7) == 1) ? 1 : 0; if ((flags & LTM_PRIME_2MSB_ON) != 0) { maskOR_msb |= 0x80 >> ((9 - size) & 7); } /* get the maskOR_lsb */ maskOR_lsb = 1; if ((flags & LTM_PRIME_BBS) != 0) { maskOR_lsb |= 3; } do { /* read the bytes */ if (cb(tmp, bsize, dat) != bsize) { err = MP_VAL; goto error; } /* work over the MSbyte */ tmp[0] &= maskAND; tmp[0] |= 1 << ((size - 1) & 7); /* mix in the maskORs */ tmp[maskOR_msb_offset] |= maskOR_msb; tmp[bsize - 1] |= maskOR_lsb; /* read it in */ if ((err = mp_read_unsigned_bin(a, tmp, bsize)) != MP_OKAY) { goto error; } /* is it prime? */ if ((err = mp_prime_is_prime(a, t, &res)) != MP_OKAY) { goto error; } if (res == MP_NO) { continue; } if ((flags & LTM_PRIME_SAFE) != 0) { /* see if (a-1)/2 is prime */ if ((err = mp_sub_d(a, 1, a)) != MP_OKAY) { goto error; } if ((err = mp_div_2(a, a)) != MP_OKAY) { goto error; } /* is it prime? */ if ((err = mp_prime_is_prime(a, t, &res)) != MP_OKAY) { goto error; } } } while (res == MP_NO); if ((flags & LTM_PRIME_SAFE) != 0) { /* restore a to the original value */ if ((err = mp_mul_2(a, a)) != MP_OKAY) { goto error; } if ((err = mp_add_d(a, 1, a)) != MP_OKAY) { goto error; } } err = MP_OKAY; error: XFREE(tmp); return err; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ #ifdef BN_MP_RADIX_SIZE_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* returns size of ASCII reprensentation */ int mp_radix_size(mp_int *a, int radix, int *size) { int res, digs; mp_int t; mp_digit d; *size = 0; /* make sure the radix is in range */ if ((radix < 2) || (radix > 64)) { return MP_VAL; } if (mp_iszero(a) == MP_YES) { *size = 2; return MP_OKAY; } /* special case for binary */ if (radix == 2) { *size = mp_count_bits(a) + ((a->sign == MP_NEG) ? 1 : 0) + 1; return MP_OKAY; } /* digs is the digit count */ digs = 0; /* if it's negative add one for the sign */ if (a->sign == MP_NEG) { ++digs; } /* init a copy of the input */ if ((res = mp_init_copy(&t, a)) != MP_OKAY) { return res; } /* force temp to positive */ t.sign = MP_ZPOS; /* fetch out all of the digits */ while (mp_iszero(&t) == MP_NO) { if ((res = mp_div_d(&t, (mp_digit)radix, &t, &d)) != MP_OKAY) { mp_clear(&t); return res; } ++digs; } mp_clear(&t); /* return digs + 1, the 1 is for the NULL byte that would be required. */ *size = digs + 1; return MP_OKAY; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ #ifdef BN_MP_RADIX_SMAP_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* chars used in radix conversions */ const char *mp_s_rmap = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz+/"; #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ #ifdef BN_MP_RAND_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* makes a pseudo-random int of a given size */ int mp_rand(mp_int *a, int digits) { int res; mp_digit d; mp_zero(a); if (digits <= 0) { return MP_OKAY; } /* first place a random non-zero digit */ do { d = ((mp_digit)abs(MP_GEN_RANDOM())) & MP_MASK; } while (d == 0); if ((res = mp_add_d(a, d, a)) != MP_OKAY) { return res; } while (--digits > 0) { if ((res = mp_lshd(a, 1)) != MP_OKAY) { return res; } if ((res = mp_add_d(a, ((mp_digit)abs(MP_GEN_RANDOM())), a)) != MP_OKAY) { return res; } } return MP_OKAY; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ #ifdef BN_MP_READ_RADIX_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* read a string [ASCII] in a given radix */ int mp_read_radix(mp_int *a, const char *str, int radix) { int y, res, neg; char ch; /* zero the digit bignum */ mp_zero(a); /* make sure the radix is ok */ if ((radix < 2) || (radix > 64)) { return MP_VAL; } /* if the leading digit is a * minus set the sign to negative. */ if (*str == '-') { ++str; neg = MP_NEG; } else { neg = MP_ZPOS; } /* set the integer to the default of zero */ mp_zero(a); /* process each digit of the string */ while (*str != '\0') { /* if the radix <= 36 the conversion is case insensitive * this allows numbers like 1AB and 1ab to represent the same value * [e.g. in hex] */ ch = (radix <= 36) ? (char)toupper((int)*str) : *str; for (y = 0; y < 64; y++) { if (ch == mp_s_rmap[y]) { break; } } /* if the char was found in the map * and is less than the given radix add it * to the number, otherwise exit the loop. */ if (y < radix) { if ((res = mp_mul_d(a, (mp_digit)radix, a)) != MP_OKAY) { return res; } if ((res = mp_add_d(a, (mp_digit)y, a)) != MP_OKAY) { return res; } } else { break; } ++str; } /* set the sign only if a != 0 */ if (mp_iszero(a) != MP_YES) { a->sign = neg; } return MP_OKAY; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ #ifdef BN_MP_READ_SIGNED_BIN_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* read signed bin, big endian, first byte is 0==positive or 1==negative */ int mp_read_signed_bin(mp_int *a, const unsigned char *b, int c) { int res; /* read magnitude */ if ((res = mp_read_unsigned_bin(a, b + 1, c - 1)) != MP_OKAY) { return res; } /* first byte is 0 for positive, non-zero for negative */ if (b[0] == 0) { a->sign = MP_ZPOS; } else { a->sign = MP_NEG; } return MP_OKAY; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ #ifdef BN_MP_READ_UNSIGNED_BIN_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* reads a unsigned char array, assumes the msb is stored first [big endian] */ int mp_read_unsigned_bin(mp_int *a, const unsigned char *b, int c) { int res; /* make sure there are at least two digits */ if (a->alloc < 2) { if ((res = mp_grow(a, 2)) != MP_OKAY) { return res; } } /* zero the int */ mp_zero(a); /* read the bytes in */ while (c-- > 0) { if ((res = mp_mul_2d(a, 8, a)) != MP_OKAY) { return res; } #ifndef MP_8BIT a->dp[0] |= *b++; a->used += 1; #else a->dp[0] = (*b & MP_MASK); a->dp[1] |= ((*b++ >> 7U) & 1); a->used += 2; #endif } mp_clamp(a); return MP_OKAY; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ #ifdef BN_MP_REDUCE_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* reduces x mod m, assumes 0 < x < m**2, mu is * precomputed via mp_reduce_setup. * From HAC pp.604 Algorithm 14.42 */ int mp_reduce(mp_int *x, mp_int *m, mp_int *mu) { mp_int q; int res, um = m->used; /* q = x */ if ((res = mp_init_copy(&q, x)) != MP_OKAY) { return res; } /* q1 = x / b**(k-1) */ mp_rshd(&q, um - 1); /* according to HAC this optimization is ok */ if (((mp_digit)um) > (((mp_digit)1) << (DIGIT_BIT - 1))) { if ((res = mp_mul(&q, mu, &q)) != MP_OKAY) { goto CLEANUP; } } else { #ifdef BN_S_MP_MUL_HIGH_DIGS_C if ((res = s_mp_mul_high_digs(&q, mu, &q, um)) != MP_OKAY) { goto CLEANUP; } #elif defined(BN_FAST_S_MP_MUL_HIGH_DIGS_C) if ((res = fast_s_mp_mul_high_digs(&q, mu, &q, um)) != MP_OKAY) { goto CLEANUP; } #else { res = MP_VAL; goto CLEANUP; } #endif } /* q3 = q2 / b**(k+1) */ mp_rshd(&q, um + 1); /* x = x mod b**(k+1), quick (no division) */ if ((res = mp_mod_2d(x, DIGIT_BIT * (um + 1), x)) != MP_OKAY) { goto CLEANUP; } /* q = q * m mod b**(k+1), quick (no division) */ if ((res = s_mp_mul_digs(&q, m, &q, um + 1)) != MP_OKAY) { goto CLEANUP; } /* x = x - q */ if ((res = mp_sub(x, &q, x)) != MP_OKAY) { goto CLEANUP; } /* If x < 0, add b**(k+1) to it */ if (mp_cmp_d(x, 0) == MP_LT) { mp_set(&q, 1); if ((res = mp_lshd(&q, um + 1)) != MP_OKAY) goto CLEANUP; if ((res = mp_add(x, &q, x)) != MP_OKAY) goto CLEANUP; } /* Back off if it's too big */ while (mp_cmp(x, m) != MP_LT) { if ((res = s_mp_sub(x, m, x)) != MP_OKAY) { goto CLEANUP; } } CLEANUP: mp_clear(&q); return res; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ #ifdef BN_MP_REDUCE_2K_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* reduces a modulo n where n is of the form 2**p - d */ int mp_reduce_2k(mp_int *a, mp_int *n, mp_digit d) { mp_int q; int p, res; if ((res = mp_init(&q)) != MP_OKAY) { return res; } p = mp_count_bits(n); top: /* q = a/2**p, a = a mod 2**p */ if ((res = mp_div_2d(a, p, &q, a)) != MP_OKAY) { goto ERR; } if (d != 1) { /* q = q * d */ if ((res = mp_mul_d(&q, d, &q)) != MP_OKAY) { goto ERR; } } /* a = a + q */ if ((res = s_mp_add(a, &q, a)) != MP_OKAY) { goto ERR; } if (mp_cmp_mag(a, n) != MP_LT) { if ((res = s_mp_sub(a, n, a)) != MP_OKAY) { goto ERR; } goto top; } ERR: mp_clear(&q); return res; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ #ifdef BN_MP_REDUCE_2K_L_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* reduces a modulo n where n is of the form 2**p - d This differs from reduce_2k since "d" can be larger than a single digit. */ int mp_reduce_2k_l(mp_int *a, mp_int *n, mp_int *d) { mp_int q; int p, res; if ((res = mp_init(&q)) != MP_OKAY) { return res; } p = mp_count_bits(n); top: /* q = a/2**p, a = a mod 2**p */ if ((res = mp_div_2d(a, p, &q, a)) != MP_OKAY) { goto ERR; } /* q = q * d */ if ((res = mp_mul(&q, d, &q)) != MP_OKAY) { goto ERR; } /* a = a + q */ if ((res = s_mp_add(a, &q, a)) != MP_OKAY) { goto ERR; } if (mp_cmp_mag(a, n) != MP_LT) { if ((res = s_mp_sub(a, n, a)) != MP_OKAY) { goto ERR; } goto top; } ERR: mp_clear(&q); return res; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ #ifdef BN_MP_REDUCE_2K_SETUP_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* determines the setup value */ int mp_reduce_2k_setup(mp_int *a, mp_digit *d) { int res, p; mp_int tmp; if ((res = mp_init(&tmp)) != MP_OKAY) { return res; } p = mp_count_bits(a); if ((res = mp_2expt(&tmp, p)) != MP_OKAY) { mp_clear(&tmp); return res; } if ((res = s_mp_sub(&tmp, a, &tmp)) != MP_OKAY) { mp_clear(&tmp); return res; } *d = tmp.dp[0]; mp_clear(&tmp); return MP_OKAY; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ #ifdef BN_MP_REDUCE_2K_SETUP_L_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* determines the setup value */ int mp_reduce_2k_setup_l(mp_int *a, mp_int *d) { int res; mp_int tmp; if ((res = mp_init(&tmp)) != MP_OKAY) { return res; } if ((res = mp_2expt(&tmp, mp_count_bits(a))) != MP_OKAY) { goto ERR; } if ((res = s_mp_sub(&tmp, a, d)) != MP_OKAY) { goto ERR; } ERR: mp_clear(&tmp); return res; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ #ifdef BN_MP_REDUCE_IS_2K_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* determines if mp_reduce_2k can be used */ int mp_reduce_is_2k(mp_int *a) { int ix, iy, iw; mp_digit iz; if (a->used == 0) { return MP_NO; } else if (a->used == 1) { return MP_YES; } else if (a->used > 1) { iy = mp_count_bits(a); iz = 1; iw = 1; /* Test every bit from the second digit up, must be 1 */ for (ix = DIGIT_BIT; ix < iy; ix++) { if ((a->dp[iw] & iz) == 0) { return MP_NO; } iz <<= 1; if (iz > (mp_digit)MP_MASK) { ++iw; iz = 1; } } } return MP_YES; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ #ifdef BN_MP_REDUCE_IS_2K_L_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* determines if reduce_2k_l can be used */ int mp_reduce_is_2k_l(mp_int *a) { int ix, iy; if (a->used == 0) { return MP_NO; } else if (a->used == 1) { return MP_YES; } else if (a->used > 1) { /* if more than half of the digits are -1 we're sold */ for (iy = ix = 0; ix < a->used; ix++) { if (a->dp[ix] == MP_MASK) { ++iy; } } return (iy >= (a->used / 2)) ? MP_YES : MP_NO; } return MP_NO; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ #ifdef BN_MP_REDUCE_SETUP_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* pre-calculate the value required for Barrett reduction * For a given modulus "b" it calulates the value required in "a" */ int mp_reduce_setup(mp_int *a, mp_int *b) { int res; if ((res = mp_2expt(a, b->used * 2 * DIGIT_BIT)) != MP_OKAY) { return res; } return mp_div(a, b, a, NULL); } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ #ifdef BN_MP_RSHD_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* shift right a certain amount of digits */ void mp_rshd(mp_int *a, int b) { int x; /* if b <= 0 then ignore it */ if (b <= 0) { return; } /* if b > used then simply zero it and return */ if (a->used <= b) { mp_zero(a); return; } { mp_digit *bottom, *top; /* shift the digits down */ /* bottom */ bottom = a->dp; /* top [offset into digits] */ top = a->dp + b; /* this is implemented as a sliding window where * the window is b-digits long and digits from * the top of the window are copied to the bottom * * e.g. b-2 | b-1 | b0 | b1 | b2 | ... | bb | ----> /\ | ----> **\-------------------/ ----> */ for (x = 0; x < (a->used - b); x++) { *bottom++ = *top++; } /* zero the top digits */ for ( ; x < a->used; x++) { *bottom++ = 0; } } /* remove excess digits */ a->used -= b; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ #ifdef BN_MP_SET_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* set to a digit */ void mp_set(mp_int *a, mp_digit b) { mp_zero(a); a->dp[0] = b & MP_MASK; a->used = (a->dp[0] != 0) ? 1 : 0; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ #ifdef BN_MP_SET_INT_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* set a 32-bit const */ int mp_set_int(mp_int *a, unsigned long b) { int x, res; mp_zero(a); /* set four bits at a time */ for (x = 0; x < 8; x++) { /* shift the number up four bits */ if ((res = mp_mul_2d(a, 4, a)) != MP_OKAY) { return res; } /* OR in the top four bits of the source */ a->dp[0] |= (b >> 28) & 15; /* shift the source up to the next four bits */ b <<= 4; /* ensure that digits are not clamped off */ a->used += 1; } mp_clamp(a); return MP_OKAY; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ #ifdef BN_MP_SET_LONG_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* set a platform dependent unsigned long int */ MP_SET_XLONG(mp_set_long, unsigned long) #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ #ifdef BN_MP_SET_LONG_LONG_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* set a platform dependent unsigned long long int */ MP_SET_XLONG(mp_set_long_long, unsigned long long) #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ #ifdef BN_MP_SHRINK_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* shrink a bignum */ int mp_shrink(mp_int *a) { mp_digit *tmp; int used = 1; if (a->used > 0) { used = a->used; } if (a->alloc != used) { if ((tmp = OPT_CAST(mp_digit) XREALLOC(a->dp, sizeof(mp_digit) * used)) == NULL) { return MP_MEM; } a->dp = tmp; a->alloc = used; } return MP_OKAY; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ #ifdef BN_MP_SIGNED_BIN_SIZE_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* get the size for an signed equivalent */ int mp_signed_bin_size(mp_int *a) { return 1 + mp_unsigned_bin_size(a); } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ #ifdef BN_MP_SQR_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* computes b = a*a */ int mp_sqr(mp_int *a, mp_int *b) { int res; #ifdef BN_MP_TOOM_SQR_C /* use Toom-Cook? */ if (a->used >= TOOM_SQR_CUTOFF) { res = mp_toom_sqr(a, b); /* Karatsuba? */ } else #endif #ifdef BN_MP_KARATSUBA_SQR_C if (a->used >= KARATSUBA_SQR_CUTOFF) { res = mp_karatsuba_sqr(a, b); } else #endif { #ifdef BN_FAST_S_MP_SQR_C /* can we use the fast comba multiplier? */ if ((((a->used * 2) + 1) < MP_WARRAY) && (a->used < (1 << (((sizeof(mp_word) * CHAR_BIT) - (2 * DIGIT_BIT)) - 1)))) { res = fast_s_mp_sqr(a, b); } else #endif { #ifdef BN_S_MP_SQR_C res = s_mp_sqr(a, b); #else res = MP_VAL; #endif } } b->sign = MP_ZPOS; return res; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ #ifdef BN_MP_SQRMOD_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* c = a * a (mod b) */ int mp_sqrmod(mp_int *a, mp_int *b, mp_int *c) { int res; mp_int t; if ((res = mp_init(&t)) != MP_OKAY) { return res; } if ((res = mp_sqr(a, &t)) != MP_OKAY) { mp_clear(&t); return res; } res = mp_mod(&t, b, c); mp_clear(&t); return res; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ #ifdef BN_MP_SQRT_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* this function is less generic than mp_n_root, simpler and faster */ int mp_sqrt(mp_int *arg, mp_int *ret) { int res; mp_int t1, t2; /* must be positive */ if (arg->sign == MP_NEG) { return MP_VAL; } /* easy out */ if (mp_iszero(arg) == MP_YES) { mp_zero(ret); return MP_OKAY; } if ((res = mp_init_copy(&t1, arg)) != MP_OKAY) { return res; } if ((res = mp_init(&t2)) != MP_OKAY) { goto E2; } /* First approx. (not very bad for large arg) */ mp_rshd(&t1, t1.used / 2); /* t1 > 0 */ if ((res = mp_div(arg, &t1, &t2, NULL)) != MP_OKAY) { goto E1; } if ((res = mp_add(&t1, &t2, &t1)) != MP_OKAY) { goto E1; } if ((res = mp_div_2(&t1, &t1)) != MP_OKAY) { goto E1; } /* And now t1 > sqrt(arg) */ do { if ((res = mp_div(arg, &t1, &t2, NULL)) != MP_OKAY) { goto E1; } if ((res = mp_add(&t1, &t2, &t1)) != MP_OKAY) { goto E1; } if ((res = mp_div_2(&t1, &t1)) != MP_OKAY) { goto E1; } /* t1 >= sqrt(arg) >= t2 at this point */ } while (mp_cmp_mag(&t1, &t2) == MP_GT); mp_exch(&t1, ret); E1: mp_clear(&t2); E2: mp_clear(&t1); return res; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ #ifdef BN_MP_SQRTMOD_PRIME_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library is free for all purposes without any express * guarantee it works. */ /* Tonelli-Shanks algorithm * https://en.wikipedia.org/wiki/Tonelli%E2%80%93Shanks_algorithm * https://gmplib.org/list-archives/gmp-discuss/2013-April/005300.html * */ int mp_sqrtmod_prime(mp_int *n, mp_int *prime, mp_int *ret) { int res, legendre; mp_int t1, C, Q, S, Z, M, T, R, two; mp_digit i; /* first handle the simple cases */ if (mp_cmp_d(n, 0) == MP_EQ) { mp_zero(ret); return MP_OKAY; } if (mp_cmp_d(prime, 2) == MP_EQ) return MP_VAL; /* prime must be odd */ if ((res = mp_jacobi(n, prime, &legendre)) != MP_OKAY) return res; if (legendre == -1) return MP_VAL; /* quadratic non-residue mod prime */ if ((res = mp_init_multi(&t1, &C, &Q, &S, &Z, &M, &T, &R, &two, NULL)) != MP_OKAY) { return res; } /* SPECIAL CASE: if prime mod 4 == 3 * compute directly: res = n^(prime+1)/4 mod prime * Handbook of Applied Cryptography algorithm 3.36 */ if ((res = mp_mod_d(prime, 4, &i)) != MP_OKAY) goto cleanup; if (i == 3) { if ((res = mp_add_d(prime, 1, &t1)) != MP_OKAY) goto cleanup; if ((res = mp_div_2(&t1, &t1)) != MP_OKAY) goto cleanup; if ((res = mp_div_2(&t1, &t1)) != MP_OKAY) goto cleanup; if ((res = mp_exptmod(n, &t1, prime, ret)) != MP_OKAY) goto cleanup; res = MP_OKAY; goto cleanup; } /* NOW: Tonelli-Shanks algorithm */ /* factor out powers of 2 from prime-1, defining Q and S as: prime-1 = Q*2^S */ if ((res = mp_copy(prime, &Q)) != MP_OKAY) goto cleanup; if ((res = mp_sub_d(&Q, 1, &Q)) != MP_OKAY) goto cleanup; /* Q = prime - 1 */ mp_zero(&S); /* S = 0 */ while (mp_iseven(&Q) != MP_NO) { if ((res = mp_div_2(&Q, &Q)) != MP_OKAY) goto cleanup; /* Q = Q / 2 */ if ((res = mp_add_d(&S, 1, &S)) != MP_OKAY) goto cleanup; /* S = S + 1 */ } /* find a Z such that the Legendre symbol (Z|prime) == -1 */ if ((res = mp_set_int(&Z, 2)) != MP_OKAY) goto cleanup; /* Z = 2 */ while (1) { if ((res = mp_jacobi(&Z, prime, &legendre)) != MP_OKAY) goto cleanup; if (legendre == -1) break; if ((res = mp_add_d(&Z, 1, &Z)) != MP_OKAY) goto cleanup; /* Z = Z + 1 */ } if ((res = mp_exptmod(&Z, &Q, prime, &C)) != MP_OKAY) goto cleanup; /* C = Z ^ Q mod prime */ if ((res = mp_add_d(&Q, 1, &t1)) != MP_OKAY) goto cleanup; if ((res = mp_div_2(&t1, &t1)) != MP_OKAY) goto cleanup; /* t1 = (Q + 1) / 2 */ if ((res = mp_exptmod(n, &t1, prime, &R)) != MP_OKAY) goto cleanup; /* R = n ^ ((Q + 1) / 2) mod prime */ if ((res = mp_exptmod(n, &Q, prime, &T)) != MP_OKAY) goto cleanup; /* T = n ^ Q mod prime */ if ((res = mp_copy(&S, &M)) != MP_OKAY) goto cleanup; /* M = S */ if ((res = mp_set_int(&two, 2)) != MP_OKAY) goto cleanup; res = MP_VAL; while (1) { if ((res = mp_copy(&T, &t1)) != MP_OKAY) goto cleanup; i = 0; while (1) { if (mp_cmp_d(&t1, 1) == MP_EQ) break; if ((res = mp_exptmod(&t1, &two, prime, &t1)) != MP_OKAY) goto cleanup; i++; } if (i == 0) { if ((res = mp_copy(&R, ret)) != MP_OKAY) goto cleanup; res = MP_OKAY; goto cleanup; } if ((res = mp_sub_d(&M, i, &t1)) != MP_OKAY) goto cleanup; if ((res = mp_sub_d(&t1, 1, &t1)) != MP_OKAY) goto cleanup; if ((res = mp_exptmod(&two, &t1, prime, &t1)) != MP_OKAY) goto cleanup; /* t1 = 2 ^ (M - i - 1) */ if ((res = mp_exptmod(&C, &t1, prime, &t1)) != MP_OKAY) goto cleanup; /* t1 = C ^ (2 ^ (M - i - 1)) mod prime */ if ((res = mp_sqrmod(&t1, prime, &C)) != MP_OKAY) goto cleanup; /* C = (t1 * t1) mod prime */ if ((res = mp_mulmod(&R, &t1, prime, &R)) != MP_OKAY) goto cleanup; /* R = (R * t1) mod prime */ if ((res = mp_mulmod(&T, &C, prime, &T)) != MP_OKAY) goto cleanup; /* T = (T * C) mod prime */ mp_set(&M, i); /* M = i */ } cleanup: mp_clear_multi(&t1, &C, &Q, &S, &Z, &M, &T, &R, &two, NULL); return res; } #endif #ifdef BN_MP_SUB_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* high level subtraction (handles signs) */ int mp_sub(mp_int *a, mp_int *b, mp_int *c) { int sa, sb, res; sa = a->sign; sb = b->sign; if (sa != sb) { /* subtract a negative from a positive, OR */ /* subtract a positive from a negative. */ /* In either case, ADD their magnitudes, */ /* and use the sign of the first number. */ c->sign = sa; res = s_mp_add(a, b, c); } else { /* subtract a positive from a positive, OR */ /* subtract a negative from a negative. */ /* First, take the difference between their */ /* magnitudes, then... */ if (mp_cmp_mag(a, b) != MP_LT) { /* Copy the sign from the first */ c->sign = sa; /* The first has a larger or equal magnitude */ res = s_mp_sub(a, b, c); } else { /* The result has the *opposite* sign from */ /* the first number. */ c->sign = (sa == MP_ZPOS) ? MP_NEG : MP_ZPOS; /* The second has a larger magnitude */ res = s_mp_sub(b, a, c); } } return res; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ #ifdef BN_MP_SUB_D_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* single digit subtraction */ int mp_sub_d(mp_int *a, mp_digit b, mp_int *c) { mp_digit *tmpa, *tmpc, mu; int res, ix, oldused; /* grow c as required */ if (c->alloc < (a->used + 1)) { if ((res = mp_grow(c, a->used + 1)) != MP_OKAY) { return res; } } /* if a is negative just do an unsigned * addition [with fudged signs] */ if (a->sign == MP_NEG) { a->sign = MP_ZPOS; res = mp_add_d(a, b, c); a->sign = c->sign = MP_NEG; /* clamp */ mp_clamp(c); return res; } /* setup regs */ oldused = c->used; tmpa = a->dp; tmpc = c->dp; /* if a <= b simply fix the single digit */ if (((a->used == 1) && (a->dp[0] <= b)) || (a->used == 0)) { if (a->used == 1) { *tmpc++ = b - *tmpa; } else { *tmpc++ = b; } ix = 1; /* negative/1digit */ c->sign = MP_NEG; c->used = 1; } else { /* positive/size */ c->sign = MP_ZPOS; c->used = a->used; /* subtract first digit */ *tmpc = *tmpa++ - b; mu = *tmpc >> ((sizeof(mp_digit) * CHAR_BIT) - 1); *tmpc++ &= MP_MASK; /* handle rest of the digits */ for (ix = 1; ix < a->used; ix++) { *tmpc = *tmpa++ - mu; mu = *tmpc >> ((sizeof(mp_digit) * CHAR_BIT) - 1); *tmpc++ &= MP_MASK; } } /* zero excess digits */ while (ix++ < oldused) { *tmpc++ = 0; } mp_clamp(c); return MP_OKAY; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ #ifdef BN_MP_SUBMOD_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* d = a - b (mod c) */ int mp_submod(mp_int *a, mp_int *b, mp_int *c, mp_int *d) { int res; mp_int t; if ((res = mp_init(&t)) != MP_OKAY) { return res; } if ((res = mp_sub(a, b, &t)) != MP_OKAY) { mp_clear(&t); return res; } res = mp_mod(&t, c, d); mp_clear(&t); return res; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ #ifdef BN_MP_TO_SIGNED_BIN_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* store in signed [big endian] format */ int mp_to_signed_bin(mp_int *a, unsigned char *b) { int res; if ((res = mp_to_unsigned_bin(a, b + 1)) != MP_OKAY) { return res; } b[0] = (a->sign == MP_ZPOS) ? (unsigned char)0 : (unsigned char)1; return MP_OKAY; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ #ifdef BN_MP_TO_SIGNED_BIN_N_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* store in signed [big endian] format */ int mp_to_signed_bin_n(mp_int *a, unsigned char *b, unsigned long *outlen) { if (*outlen < (unsigned long)mp_signed_bin_size(a)) { return MP_VAL; } *outlen = mp_signed_bin_size(a); return mp_to_signed_bin(a, b); } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ #ifdef BN_MP_TO_UNSIGNED_BIN_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* store in unsigned [big endian] format */ int mp_to_unsigned_bin(mp_int *a, unsigned char *b) { int x, res; mp_int t; if ((res = mp_init_copy(&t, a)) != MP_OKAY) { return res; } x = 0; while (mp_iszero(&t) == MP_NO) { #ifndef MP_8BIT b[x++] = (unsigned char)(t.dp[0] & 255); #else b[x++] = (unsigned char)(t.dp[0] | ((t.dp[1] & 0x01) << 7)); #endif if ((res = mp_div_2d(&t, 8, &t, NULL)) != MP_OKAY) { mp_clear(&t); return res; } } bn_reverse(b, x); mp_clear(&t); return MP_OKAY; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ #ifdef BN_MP_TOOM_MUL_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* multiplication using the Toom-Cook 3-way algorithm * * Much more complicated than Karatsuba but has a lower * asymptotic running time of O(N**1.464). This algorithm is * only particularly useful on VERY large inputs * (we're talking 1000s of digits here...). */ int mp_toom_mul(mp_int *a, mp_int *b, mp_int *c) { mp_int w0, w1, w2, w3, w4, tmp1, tmp2, a0, a1, a2, b0, b1, b2; int res, B; /* init temps */ if ((res = mp_init_multi(&w0, &w1, &w2, &w3, &w4, &a0, &a1, &a2, &b0, &b1, &b2, &tmp1, &tmp2, NULL)) != MP_OKAY) { return res; } /* B */ B = MIN(a->used, b->used) / 3; /* a = a2 * B**2 + a1 * B + a0 */ if ((res = mp_mod_2d(a, DIGIT_BIT * B, &a0)) != MP_OKAY) { goto ERR; } if ((res = mp_copy(a, &a1)) != MP_OKAY) { goto ERR; } mp_rshd(&a1, B); if ((res = mp_mod_2d(&a1, DIGIT_BIT * B, &a1)) != MP_OKAY) { goto ERR; } if ((res = mp_copy(a, &a2)) != MP_OKAY) { goto ERR; } mp_rshd(&a2, B * 2); /* b = b2 * B**2 + b1 * B + b0 */ if ((res = mp_mod_2d(b, DIGIT_BIT * B, &b0)) != MP_OKAY) { goto ERR; } if ((res = mp_copy(b, &b1)) != MP_OKAY) { goto ERR; } mp_rshd(&b1, B); (void)mp_mod_2d(&b1, DIGIT_BIT * B, &b1); if ((res = mp_copy(b, &b2)) != MP_OKAY) { goto ERR; } mp_rshd(&b2, B * 2); /* w0 = a0*b0 */ if ((res = mp_mul(&a0, &b0, &w0)) != MP_OKAY) { goto ERR; } /* w4 = a2 * b2 */ if ((res = mp_mul(&a2, &b2, &w4)) != MP_OKAY) { goto ERR; } /* w1 = (a2 + 2(a1 + 2a0))(b2 + 2(b1 + 2b0)) */ if ((res = mp_mul_2(&a0, &tmp1)) != MP_OKAY) { goto ERR; } if ((res = mp_add(&tmp1, &a1, &tmp1)) != MP_OKAY) { goto ERR; } if ((res = mp_mul_2(&tmp1, &tmp1)) != MP_OKAY) { goto ERR; } if ((res = mp_add(&tmp1, &a2, &tmp1)) != MP_OKAY) { goto ERR; } if ((res = mp_mul_2(&b0, &tmp2)) != MP_OKAY) { goto ERR; } if ((res = mp_add(&tmp2, &b1, &tmp2)) != MP_OKAY) { goto ERR; } if ((res = mp_mul_2(&tmp2, &tmp2)) != MP_OKAY) { goto ERR; } if ((res = mp_add(&tmp2, &b2, &tmp2)) != MP_OKAY) { goto ERR; } if ((res = mp_mul(&tmp1, &tmp2, &w1)) != MP_OKAY) { goto ERR; } /* w3 = (a0 + 2(a1 + 2a2))(b0 + 2(b1 + 2b2)) */ if ((res = mp_mul_2(&a2, &tmp1)) != MP_OKAY) { goto ERR; } if ((res = mp_add(&tmp1, &a1, &tmp1)) != MP_OKAY) { goto ERR; } if ((res = mp_mul_2(&tmp1, &tmp1)) != MP_OKAY) { goto ERR; } if ((res = mp_add(&tmp1, &a0, &tmp1)) != MP_OKAY) { goto ERR; } if ((res = mp_mul_2(&b2, &tmp2)) != MP_OKAY) { goto ERR; } if ((res = mp_add(&tmp2, &b1, &tmp2)) != MP_OKAY) { goto ERR; } if ((res = mp_mul_2(&tmp2, &tmp2)) != MP_OKAY) { goto ERR; } if ((res = mp_add(&tmp2, &b0, &tmp2)) != MP_OKAY) { goto ERR; } if ((res = mp_mul(&tmp1, &tmp2, &w3)) != MP_OKAY) { goto ERR; } /* w2 = (a2 + a1 + a0)(b2 + b1 + b0) */ if ((res = mp_add(&a2, &a1, &tmp1)) != MP_OKAY) { goto ERR; } if ((res = mp_add(&tmp1, &a0, &tmp1)) != MP_OKAY) { goto ERR; } if ((res = mp_add(&b2, &b1, &tmp2)) != MP_OKAY) { goto ERR; } if ((res = mp_add(&tmp2, &b0, &tmp2)) != MP_OKAY) { goto ERR; } if ((res = mp_mul(&tmp1, &tmp2, &w2)) != MP_OKAY) { goto ERR; } /* now solve the matrix 0 0 0 0 1 1 2 4 8 16 1 1 1 1 1 16 8 4 2 1 1 0 0 0 0 using 12 subtractions, 4 shifts, 2 small divisions and 1 small multiplication */ /* r1 - r4 */ if ((res = mp_sub(&w1, &w4, &w1)) != MP_OKAY) { goto ERR; } /* r3 - r0 */ if ((res = mp_sub(&w3, &w0, &w3)) != MP_OKAY) { goto ERR; } /* r1/2 */ if ((res = mp_div_2(&w1, &w1)) != MP_OKAY) { goto ERR; } /* r3/2 */ if ((res = mp_div_2(&w3, &w3)) != MP_OKAY) { goto ERR; } /* r2 - r0 - r4 */ if ((res = mp_sub(&w2, &w0, &w2)) != MP_OKAY) { goto ERR; } if ((res = mp_sub(&w2, &w4, &w2)) != MP_OKAY) { goto ERR; } /* r1 - r2 */ if ((res = mp_sub(&w1, &w2, &w1)) != MP_OKAY) { goto ERR; } /* r3 - r2 */ if ((res = mp_sub(&w3, &w2, &w3)) != MP_OKAY) { goto ERR; } /* r1 - 8r0 */ if ((res = mp_mul_2d(&w0, 3, &tmp1)) != MP_OKAY) { goto ERR; } if ((res = mp_sub(&w1, &tmp1, &w1)) != MP_OKAY) { goto ERR; } /* r3 - 8r4 */ if ((res = mp_mul_2d(&w4, 3, &tmp1)) != MP_OKAY) { goto ERR; } if ((res = mp_sub(&w3, &tmp1, &w3)) != MP_OKAY) { goto ERR; } /* 3r2 - r1 - r3 */ if ((res = mp_mul_d(&w2, 3, &w2)) != MP_OKAY) { goto ERR; } if ((res = mp_sub(&w2, &w1, &w2)) != MP_OKAY) { goto ERR; } if ((res = mp_sub(&w2, &w3, &w2)) != MP_OKAY) { goto ERR; } /* r1 - r2 */ if ((res = mp_sub(&w1, &w2, &w1)) != MP_OKAY) { goto ERR; } /* r3 - r2 */ if ((res = mp_sub(&w3, &w2, &w3)) != MP_OKAY) { goto ERR; } /* r1/3 */ if ((res = mp_div_3(&w1, &w1, NULL)) != MP_OKAY) { goto ERR; } /* r3/3 */ if ((res = mp_div_3(&w3, &w3, NULL)) != MP_OKAY) { goto ERR; } /* at this point shift W[n] by B*n */ if ((res = mp_lshd(&w1, 1 * B)) != MP_OKAY) { goto ERR; } if ((res = mp_lshd(&w2, 2 * B)) != MP_OKAY) { goto ERR; } if ((res = mp_lshd(&w3, 3 * B)) != MP_OKAY) { goto ERR; } if ((res = mp_lshd(&w4, 4 * B)) != MP_OKAY) { goto ERR; } if ((res = mp_add(&w0, &w1, c)) != MP_OKAY) { goto ERR; } if ((res = mp_add(&w2, &w3, &tmp1)) != MP_OKAY) { goto ERR; } if ((res = mp_add(&w4, &tmp1, &tmp1)) != MP_OKAY) { goto ERR; } if ((res = mp_add(&tmp1, c, c)) != MP_OKAY) { goto ERR; } ERR: mp_clear_multi(&w0, &w1, &w2, &w3, &w4, &a0, &a1, &a2, &b0, &b1, &b2, &tmp1, &tmp2, NULL); return res; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ #ifdef BN_MP_TOOM_SQR_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* squaring using Toom-Cook 3-way algorithm */ int mp_toom_sqr(mp_int *a, mp_int *b) { mp_int w0, w1, w2, w3, w4, tmp1, a0, a1, a2; int res, B; /* init temps */ if ((res = mp_init_multi(&w0, &w1, &w2, &w3, &w4, &a0, &a1, &a2, &tmp1, NULL)) != MP_OKAY) { return res; } /* B */ B = a->used / 3; /* a = a2 * B**2 + a1 * B + a0 */ if ((res = mp_mod_2d(a, DIGIT_BIT * B, &a0)) != MP_OKAY) { goto ERR; } if ((res = mp_copy(a, &a1)) != MP_OKAY) { goto ERR; } mp_rshd(&a1, B); if ((res = mp_mod_2d(&a1, DIGIT_BIT * B, &a1)) != MP_OKAY) { goto ERR; } if ((res = mp_copy(a, &a2)) != MP_OKAY) { goto ERR; } mp_rshd(&a2, B * 2); /* w0 = a0*a0 */ if ((res = mp_sqr(&a0, &w0)) != MP_OKAY) { goto ERR; } /* w4 = a2 * a2 */ if ((res = mp_sqr(&a2, &w4)) != MP_OKAY) { goto ERR; } /* w1 = (a2 + 2(a1 + 2a0))**2 */ if ((res = mp_mul_2(&a0, &tmp1)) != MP_OKAY) { goto ERR; } if ((res = mp_add(&tmp1, &a1, &tmp1)) != MP_OKAY) { goto ERR; } if ((res = mp_mul_2(&tmp1, &tmp1)) != MP_OKAY) { goto ERR; } if ((res = mp_add(&tmp1, &a2, &tmp1)) != MP_OKAY) { goto ERR; } if ((res = mp_sqr(&tmp1, &w1)) != MP_OKAY) { goto ERR; } /* w3 = (a0 + 2(a1 + 2a2))**2 */ if ((res = mp_mul_2(&a2, &tmp1)) != MP_OKAY) { goto ERR; } if ((res = mp_add(&tmp1, &a1, &tmp1)) != MP_OKAY) { goto ERR; } if ((res = mp_mul_2(&tmp1, &tmp1)) != MP_OKAY) { goto ERR; } if ((res = mp_add(&tmp1, &a0, &tmp1)) != MP_OKAY) { goto ERR; } if ((res = mp_sqr(&tmp1, &w3)) != MP_OKAY) { goto ERR; } /* w2 = (a2 + a1 + a0)**2 */ if ((res = mp_add(&a2, &a1, &tmp1)) != MP_OKAY) { goto ERR; } if ((res = mp_add(&tmp1, &a0, &tmp1)) != MP_OKAY) { goto ERR; } if ((res = mp_sqr(&tmp1, &w2)) != MP_OKAY) { goto ERR; } /* now solve the matrix 0 0 0 0 1 1 2 4 8 16 1 1 1 1 1 16 8 4 2 1 1 0 0 0 0 using 12 subtractions, 4 shifts, 2 small divisions and 1 small multiplication. */ /* r1 - r4 */ if ((res = mp_sub(&w1, &w4, &w1)) != MP_OKAY) { goto ERR; } /* r3 - r0 */ if ((res = mp_sub(&w3, &w0, &w3)) != MP_OKAY) { goto ERR; } /* r1/2 */ if ((res = mp_div_2(&w1, &w1)) != MP_OKAY) { goto ERR; } /* r3/2 */ if ((res = mp_div_2(&w3, &w3)) != MP_OKAY) { goto ERR; } /* r2 - r0 - r4 */ if ((res = mp_sub(&w2, &w0, &w2)) != MP_OKAY) { goto ERR; } if ((res = mp_sub(&w2, &w4, &w2)) != MP_OKAY) { goto ERR; } /* r1 - r2 */ if ((res = mp_sub(&w1, &w2, &w1)) != MP_OKAY) { goto ERR; } /* r3 - r2 */ if ((res = mp_sub(&w3, &w2, &w3)) != MP_OKAY) { goto ERR; } /* r1 - 8r0 */ if ((res = mp_mul_2d(&w0, 3, &tmp1)) != MP_OKAY) { goto ERR; } if ((res = mp_sub(&w1, &tmp1, &w1)) != MP_OKAY) { goto ERR; } /* r3 - 8r4 */ if ((res = mp_mul_2d(&w4, 3, &tmp1)) != MP_OKAY) { goto ERR; } if ((res = mp_sub(&w3, &tmp1, &w3)) != MP_OKAY) { goto ERR; } /* 3r2 - r1 - r3 */ if ((res = mp_mul_d(&w2, 3, &w2)) != MP_OKAY) { goto ERR; } if ((res = mp_sub(&w2, &w1, &w2)) != MP_OKAY) { goto ERR; } if ((res = mp_sub(&w2, &w3, &w2)) != MP_OKAY) { goto ERR; } /* r1 - r2 */ if ((res = mp_sub(&w1, &w2, &w1)) != MP_OKAY) { goto ERR; } /* r3 - r2 */ if ((res = mp_sub(&w3, &w2, &w3)) != MP_OKAY) { goto ERR; } /* r1/3 */ if ((res = mp_div_3(&w1, &w1, NULL)) != MP_OKAY) { goto ERR; } /* r3/3 */ if ((res = mp_div_3(&w3, &w3, NULL)) != MP_OKAY) { goto ERR; } /* at this point shift W[n] by B*n */ if ((res = mp_lshd(&w1, 1 * B)) != MP_OKAY) { goto ERR; } if ((res = mp_lshd(&w2, 2 * B)) != MP_OKAY) { goto ERR; } if ((res = mp_lshd(&w3, 3 * B)) != MP_OKAY) { goto ERR; } if ((res = mp_lshd(&w4, 4 * B)) != MP_OKAY) { goto ERR; } if ((res = mp_add(&w0, &w1, b)) != MP_OKAY) { goto ERR; } if ((res = mp_add(&w2, &w3, &tmp1)) != MP_OKAY) { goto ERR; } if ((res = mp_add(&w4, &tmp1, &tmp1)) != MP_OKAY) { goto ERR; } if ((res = mp_add(&tmp1, b, b)) != MP_OKAY) { goto ERR; } ERR: mp_clear_multi(&w0, &w1, &w2, &w3, &w4, &a0, &a1, &a2, &tmp1, NULL); return res; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ #ifdef BN_MP_TORADIX_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* stores a bignum as a ASCII string in a given radix (2..64) */ int mp_toradix(mp_int *a, char *str, int radix) { int res, digs; mp_int t; mp_digit d; char *_s = str; /* check range of the radix */ if ((radix < 2) || (radix > 64)) { return MP_VAL; } /* quick out if its zero */ if (mp_iszero(a) == MP_YES) { *str++ = '0'; *str = '\0'; return MP_OKAY; } if ((res = mp_init_copy(&t, a)) != MP_OKAY) { return res; } /* if it is negative output a - */ if (t.sign == MP_NEG) { ++_s; *str++ = '-'; t.sign = MP_ZPOS; } digs = 0; while (mp_iszero(&t) == MP_NO) { if ((res = mp_div_d(&t, (mp_digit)radix, &t, &d)) != MP_OKAY) { mp_clear(&t); return res; } *str++ = mp_s_rmap[d]; ++digs; } /* reverse the digits of the string. In this case _s points * to the first digit [exluding the sign] of the number] */ bn_reverse((unsigned char *)_s, digs); /* append a NULL so the string is properly terminated */ *str = '\0'; mp_clear(&t); return MP_OKAY; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ #ifdef BN_MP_UNSIGNED_BIN_SIZE_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* get the size for an unsigned equivalent */ int mp_unsigned_bin_size(mp_int *a) { int size = mp_count_bits(a); return (size / 8) + (((size & 7) != 0) ? 1 : 0); } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ #ifdef BN_MP_XOR_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* XOR two ints together */ int mp_xor(mp_int *a, mp_int *b, mp_int *c) { int res, ix, px; mp_int t, *x; if (a->used > b->used) { if ((res = mp_init_copy(&t, a)) != MP_OKAY) { return res; } px = b->used; x = b; } else { if ((res = mp_init_copy(&t, b)) != MP_OKAY) { return res; } px = a->used; x = a; } for (ix = 0; ix < px; ix++) { t.dp[ix] ^= x->dp[ix]; } mp_clamp(&t); mp_exch(c, &t); mp_clear(&t); return MP_OKAY; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ #ifdef BN_MP_ZERO_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* set to zero */ void mp_zero(mp_int *a) { int n; mp_digit *tmp; a->sign = MP_ZPOS; a->used = 0; tmp = a->dp; for (n = 0; n < a->alloc; n++) { *tmp++ = 0; } } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ #ifdef BN_PRIME_TAB_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ const mp_digit ltm_prime_tab[] = { 0x0002, 0x0003, 0x0005, 0x0007, 0x000B, 0x000D, 0x0011, 0x0013, 0x0017, 0x001D, 0x001F, 0x0025, 0x0029, 0x002B, 0x002F, 0x0035, 0x003B, 0x003D, 0x0043, 0x0047, 0x0049, 0x004F, 0x0053, 0x0059, 0x0061, 0x0065, 0x0067, 0x006B, 0x006D, 0x0071, 0x007F, #ifndef MP_8BIT 0x0083, 0x0089, 0x008B, 0x0095, 0x0097, 0x009D, 0x00A3, 0x00A7, 0x00AD, 0x00B3, 0x00B5, 0x00BF, 0x00C1, 0x00C5, 0x00C7, 0x00D3, 0x00DF, 0x00E3, 0x00E5, 0x00E9, 0x00EF, 0x00F1, 0x00FB, 0x0101, 0x0107, 0x010D, 0x010F, 0x0115, 0x0119, 0x011B, 0x0125, 0x0133, 0x0137, 0x0139, 0x013D, 0x014B, 0x0151, 0x015B, 0x015D, 0x0161, 0x0167, 0x016F, 0x0175, 0x017B, 0x017F, 0x0185, 0x018D, 0x0191, 0x0199, 0x01A3, 0x01A5, 0x01AF, 0x01B1, 0x01B7, 0x01BB, 0x01C1, 0x01C9, 0x01CD, 0x01CF, 0x01D3, 0x01DF, 0x01E7, 0x01EB, 0x01F3, 0x01F7, 0x01FD, 0x0209, 0x020B, 0x021D, 0x0223, 0x022D, 0x0233, 0x0239, 0x023B, 0x0241, 0x024B, 0x0251, 0x0257, 0x0259, 0x025F, 0x0265, 0x0269, 0x026B, 0x0277, 0x0281, 0x0283, 0x0287, 0x028D, 0x0293, 0x0295, 0x02A1, 0x02A5, 0x02AB, 0x02B3, 0x02BD, 0x02C5, 0x02CF, 0x02D7, 0x02DD, 0x02E3, 0x02E7, 0x02EF, 0x02F5, 0x02F9, 0x0301, 0x0305, 0x0313, 0x031D, 0x0329, 0x032B, 0x0335, 0x0337, 0x033B, 0x033D, 0x0347, 0x0355, 0x0359, 0x035B, 0x035F, 0x036D, 0x0371, 0x0373, 0x0377, 0x038B, 0x038F, 0x0397, 0x03A1, 0x03A9, 0x03AD, 0x03B3, 0x03B9, 0x03C7, 0x03CB, 0x03D1, 0x03D7, 0x03DF, 0x03E5, 0x03F1, 0x03F5, 0x03FB, 0x03FD, 0x0407, 0x0409, 0x040F, 0x0419, 0x041B, 0x0425, 0x0427, 0x042D, 0x043F, 0x0443, 0x0445, 0x0449, 0x044F, 0x0455, 0x045D, 0x0463, 0x0469, 0x047F, 0x0481, 0x048B, 0x0493, 0x049D, 0x04A3, 0x04A9, 0x04B1, 0x04BD, 0x04C1, 0x04C7, 0x04CD, 0x04CF, 0x04D5, 0x04E1, 0x04EB, 0x04FD, 0x04FF, 0x0503, 0x0509, 0x050B, 0x0511, 0x0515, 0x0517, 0x051B, 0x0527, 0x0529, 0x052F, 0x0551, 0x0557, 0x055D, 0x0565, 0x0577, 0x0581, 0x058F, 0x0593, 0x0595, 0x0599, 0x059F, 0x05A7, 0x05AB, 0x05AD, 0x05B3, 0x05BF, 0x05C9, 0x05CB, 0x05CF, 0x05D1, 0x05D5, 0x05DB, 0x05E7, 0x05F3, 0x05FB, 0x0607, 0x060D, 0x0611, 0x0617, 0x061F, 0x0623, 0x062B, 0x062F, 0x063D, 0x0641, 0x0647, 0x0649, 0x064D, 0x0653 #endif }; #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ #ifdef BN_REVERSE_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* reverse an array, used for radix code */ void bn_reverse(unsigned char *s, int len) { int ix, iy; unsigned char t; ix = 0; iy = len - 1; while (ix < iy) { t = s[ix]; s[ix] = s[iy]; s[iy] = t; ++ix; --iy; } } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ #ifdef BN_S_MP_ADD_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* low level addition, based on HAC pp.594, Algorithm 14.7 */ int s_mp_add(mp_int *a, mp_int *b, mp_int *c) { mp_int *x; int olduse, res, min, max; /* find sizes, we let |a| <= |b| which means we have to sort * them. "x" will point to the input with the most digits */ if (a->used > b->used) { min = b->used; max = a->used; x = a; } else { min = a->used; max = b->used; x = b; } /* init result */ if (c->alloc < (max + 1)) { if ((res = mp_grow(c, max + 1)) != MP_OKAY) { return res; } } /* get old used digit count and set new one */ olduse = c->used; c->used = max + 1; { mp_digit u, *tmpa, *tmpb, *tmpc; int i; /* alias for digit pointers */ /* first input */ tmpa = a->dp; /* second input */ tmpb = b->dp; /* destination */ tmpc = c->dp; /* zero the carry */ u = 0; for (i = 0; i < min; i++) { /* Compute the sum at one digit, T[i] = A[i] + B[i] + U */ *tmpc = *tmpa++ + *tmpb++ + u; /* U = carry bit of T[i] */ u = *tmpc >> ((mp_digit)DIGIT_BIT); /* take away carry bit from T[i] */ *tmpc++ &= MP_MASK; } /* now copy higher words if any, that is in A+B * if A or B has more digits add those in */ if (min != max) { for ( ; i < max; i++) { /* T[i] = X[i] + U */ *tmpc = x->dp[i] + u; /* U = carry bit of T[i] */ u = *tmpc >> ((mp_digit)DIGIT_BIT); /* take away carry bit from T[i] */ *tmpc++ &= MP_MASK; } } /* add carry */ *tmpc++ = u; /* clear digits above oldused */ for (i = c->used; i < olduse; i++) { *tmpc++ = 0; } } mp_clamp(c); return MP_OKAY; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ #ifdef BN_S_MP_EXPTMOD_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ int s_mp_exptmod(mp_int *G, mp_int *X, mp_int *P, mp_int *Y, int redmode) { mp_int M[TAB_SIZE], res, mu; mp_digit buf; int err, bitbuf, bitcpy, bitcnt, mode, digidx, x, y, winsize; int (*redux)(mp_int *, mp_int *, mp_int *); /* find window size */ x = mp_count_bits(X); if (x <= 7) { winsize = 2; } else if (x <= 36) { winsize = 3; } else if (x <= 140) { winsize = 4; } else if (x <= 450) { winsize = 5; } else if (x <= 1303) { winsize = 6; } else if (x <= 3529) { winsize = 7; } else { winsize = 8; } #ifdef MP_LOW_MEM if (winsize > 5) { winsize = 5; } #endif /* init M array */ /* init first cell */ if ((err = mp_init(&M[1])) != MP_OKAY) { return err; } /* now init the second half of the array */ for (x = 1 << (winsize - 1); x < (1 << winsize); x++) { if ((err = mp_init(&M[x])) != MP_OKAY) { for (y = 1 << (winsize - 1); y < x; y++) { mp_clear(&M[y]); } mp_clear(&M[1]); return err; } } /* create mu, used for Barrett reduction */ if ((err = mp_init(&mu)) != MP_OKAY) { goto LBL_M; } if (redmode == 0) { if ((err = mp_reduce_setup(&mu, P)) != MP_OKAY) { goto LBL_MU; } redux = mp_reduce; } else { if ((err = mp_reduce_2k_setup_l(P, &mu)) != MP_OKAY) { goto LBL_MU; } redux = mp_reduce_2k_l; } /* create M table * * The M table contains powers of the base, * e.g. M[x] = G**x mod P * * The first half of the table is not * computed though accept for M[0] and M[1] */ if ((err = mp_mod(G, P, &M[1])) != MP_OKAY) { goto LBL_MU; } /* compute the value at M[1<<(winsize-1)] by squaring * M[1] (winsize-1) times */ if ((err = mp_copy(&M[1], &M[1 << (winsize - 1)])) != MP_OKAY) { goto LBL_MU; } for (x = 0; x < (winsize - 1); x++) { /* square it */ if ((err = mp_sqr(&M[1 << (winsize - 1)], &M[1 << (winsize - 1)])) != MP_OKAY) { goto LBL_MU; } /* reduce modulo P */ if ((err = redux(&M[1 << (winsize - 1)], P, &mu)) != MP_OKAY) { goto LBL_MU; } } /* create upper table, that is M[x] = M[x-1] * M[1] (mod P) * for x = (2**(winsize - 1) + 1) to (2**winsize - 1) */ for (x = (1 << (winsize - 1)) + 1; x < (1 << winsize); x++) { if ((err = mp_mul(&M[x - 1], &M[1], &M[x])) != MP_OKAY) { goto LBL_MU; } if ((err = redux(&M[x], P, &mu)) != MP_OKAY) { goto LBL_MU; } } /* setup result */ if ((err = mp_init(&res)) != MP_OKAY) { goto LBL_MU; } mp_set(&res, 1); /* set initial mode and bit cnt */ mode = 0; bitcnt = 1; buf = 0; digidx = X->used - 1; bitcpy = 0; bitbuf = 0; for ( ; ; ) { /* grab next digit as required */ if (--bitcnt == 0) { /* if digidx == -1 we are out of digits */ if (digidx == -1) { break; } /* read next digit and reset the bitcnt */ buf = X->dp[digidx--]; bitcnt = (int)DIGIT_BIT; } /* grab the next msb from the exponent */ y = (buf >> (mp_digit)(DIGIT_BIT - 1)) & 1; buf <<= (mp_digit)1; /* if the bit is zero and mode == 0 then we ignore it * These represent the leading zero bits before the first 1 bit * in the exponent. Technically this opt is not required but it * does lower the # of trivial squaring/reductions used */ if ((mode == 0) && (y == 0)) { continue; } /* if the bit is zero and mode == 1 then we square */ if ((mode == 1) && (y == 0)) { if ((err = mp_sqr(&res, &res)) != MP_OKAY) { goto LBL_RES; } if ((err = redux(&res, P, &mu)) != MP_OKAY) { goto LBL_RES; } continue; } /* else we add it to the window */ bitbuf |= (y << (winsize - ++bitcpy)); mode = 2; if (bitcpy == winsize) { /* ok window is filled so square as required and multiply */ /* square first */ for (x = 0; x < winsize; x++) { if ((err = mp_sqr(&res, &res)) != MP_OKAY) { goto LBL_RES; } if ((err = redux(&res, P, &mu)) != MP_OKAY) { goto LBL_RES; } } /* then multiply */ if ((err = mp_mul(&res, &M[bitbuf], &res)) != MP_OKAY) { goto LBL_RES; } if ((err = redux(&res, P, &mu)) != MP_OKAY) { goto LBL_RES; } /* empty window and reset */ bitcpy = 0; bitbuf = 0; mode = 1; } } /* if bits remain then square/multiply */ if ((mode == 2) && (bitcpy > 0)) { /* square then multiply if the bit is set */ for (x = 0; x < bitcpy; x++) { if ((err = mp_sqr(&res, &res)) != MP_OKAY) { goto LBL_RES; } if ((err = redux(&res, P, &mu)) != MP_OKAY) { goto LBL_RES; } bitbuf <<= 1; if ((bitbuf & (1 << winsize)) != 0) { /* then multiply */ if ((err = mp_mul(&res, &M[1], &res)) != MP_OKAY) { goto LBL_RES; } if ((err = redux(&res, P, &mu)) != MP_OKAY) { goto LBL_RES; } } } } mp_exch(&res, Y); err = MP_OKAY; LBL_RES: mp_clear(&res); LBL_MU: mp_clear(&mu); LBL_M: mp_clear(&M[1]); for (x = 1 << (winsize - 1); x < (1 << winsize); x++) { mp_clear(&M[x]); } return err; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ #ifdef BN_S_MP_MUL_DIGS_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* multiplies |a| * |b| and only computes upto digs digits of result * HAC pp. 595, Algorithm 14.12 Modified so you can control how * many digits of output are created. */ int s_mp_mul_digs(mp_int *a, mp_int *b, mp_int *c, int digs) { mp_int t; int res, pa, pb, ix, iy; mp_digit u; mp_word r; mp_digit tmpx, *tmpt, *tmpy; /* can we use the fast multiplier? */ if (((digs) < MP_WARRAY) && (MIN(a->used, b->used) < (1 << ((CHAR_BIT * sizeof(mp_word)) - (2 * DIGIT_BIT))))) { return fast_s_mp_mul_digs(a, b, c, digs); } if ((res = mp_init_size(&t, digs)) != MP_OKAY) { return res; } t.used = digs; /* compute the digits of the product directly */ pa = a->used; for (ix = 0; ix < pa; ix++) { /* set the carry to zero */ u = 0; /* limit ourselves to making digs digits of output */ pb = MIN(b->used, digs - ix); /* setup some aliases */ /* copy of the digit from a used within the nested loop */ tmpx = a->dp[ix]; /* an alias for the destination shifted ix places */ tmpt = t.dp + ix; /* an alias for the digits of b */ tmpy = b->dp; /* compute the columns of the output and propagate the carry */ for (iy = 0; iy < pb; iy++) { /* compute the column as a mp_word */ r = (mp_word) * tmpt + ((mp_word)tmpx * (mp_word) * tmpy++) + (mp_word)u; /* the new column is the lower part of the result */ *tmpt++ = (mp_digit)(r & ((mp_word)MP_MASK)); /* get the carry word from the result */ u = (mp_digit)(r >> ((mp_word)DIGIT_BIT)); } /* set carry if it is placed below digs */ if ((ix + iy) < digs) { *tmpt = u; } } mp_clamp(&t); mp_exch(&t, c); mp_clear(&t); return MP_OKAY; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ #ifdef BN_S_MP_MUL_HIGH_DIGS_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* multiplies |a| * |b| and does not compute the lower digs digits * [meant to get the higher part of the product] */ int s_mp_mul_high_digs(mp_int *a, mp_int *b, mp_int *c, int digs) { mp_int t; int res, pa, pb, ix, iy; mp_digit u; mp_word r; mp_digit tmpx, *tmpt, *tmpy; /* can we use the fast multiplier? */ #ifdef BN_FAST_S_MP_MUL_HIGH_DIGS_C if (((a->used + b->used + 1) < MP_WARRAY) && (MIN(a->used, b->used) < (1 << ((CHAR_BIT * sizeof(mp_word)) - (2 * DIGIT_BIT))))) { return fast_s_mp_mul_high_digs(a, b, c, digs); } #endif if ((res = mp_init_size(&t, a->used + b->used + 1)) != MP_OKAY) { return res; } t.used = a->used + b->used + 1; pa = a->used; pb = b->used; for (ix = 0; ix < pa; ix++) { /* clear the carry */ u = 0; /* left hand side of A[ix] * B[iy] */ tmpx = a->dp[ix]; /* alias to the address of where the digits will be stored */ tmpt = &(t.dp[digs]); /* alias for where to read the right hand side from */ tmpy = b->dp + (digs - ix); for (iy = digs - ix; iy < pb; iy++) { /* calculate the double precision result */ r = (mp_word) * tmpt + ((mp_word)tmpx * (mp_word) * tmpy++) + (mp_word)u; /* get the lower part */ *tmpt++ = (mp_digit)(r & ((mp_word)MP_MASK)); /* carry the carry */ u = (mp_digit)(r >> ((mp_word)DIGIT_BIT)); } *tmpt = u; } mp_clamp(&t); mp_exch(&t, c); mp_clear(&t); return MP_OKAY; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ #ifdef BN_S_MP_SQR_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* low level squaring, b = a*a, HAC pp.596-597, Algorithm 14.16 */ int s_mp_sqr(mp_int *a, mp_int *b) { mp_int t; int res, ix, iy, pa; mp_word r; mp_digit u, tmpx, *tmpt; pa = a->used; if ((res = mp_init_size(&t, (2 * pa) + 1)) != MP_OKAY) { return res; } /* default used is maximum possible size */ t.used = (2 * pa) + 1; for (ix = 0; ix < pa; ix++) { /* first calculate the digit at 2*ix */ /* calculate double precision result */ r = (mp_word)t.dp[2 * ix] + ((mp_word)a->dp[ix] * (mp_word)a->dp[ix]); /* store lower part in result */ t.dp[ix + ix] = (mp_digit)(r & ((mp_word)MP_MASK)); /* get the carry */ u = (mp_digit)(r >> ((mp_word)DIGIT_BIT)); /* left hand side of A[ix] * A[iy] */ tmpx = a->dp[ix]; /* alias for where to store the results */ tmpt = t.dp + ((2 * ix) + 1); for (iy = ix + 1; iy < pa; iy++) { /* first calculate the product */ r = ((mp_word)tmpx) * ((mp_word)a->dp[iy]); /* now calculate the double precision result, note we use * addition instead of *2 since it's easier to optimize */ r = ((mp_word) * tmpt) + r + r + ((mp_word)u); /* store lower part */ *tmpt++ = (mp_digit)(r & ((mp_word)MP_MASK)); /* get carry */ u = (mp_digit)(r >> ((mp_word)DIGIT_BIT)); } /* propagate upwards */ while (u != ((mp_digit)0)) { r = ((mp_word) * tmpt) + ((mp_word)u); *tmpt++ = (mp_digit)(r & ((mp_word)MP_MASK)); u = (mp_digit)(r >> ((mp_word)DIGIT_BIT)); } } mp_clamp(&t); mp_exch(&t, b); mp_clear(&t); return MP_OKAY; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ #ifdef BN_S_MP_SUB_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* low level subtraction (assumes |a| > |b|), HAC pp.595 Algorithm 14.9 */ int s_mp_sub(mp_int *a, mp_int *b, mp_int *c) { int olduse, res, min, max; /* find sizes */ min = b->used; max = a->used; /* init result */ if (c->alloc < max) { if ((res = mp_grow(c, max)) != MP_OKAY) { return res; } } olduse = c->used; c->used = max; { mp_digit u, *tmpa, *tmpb, *tmpc; int i; /* alias for digit pointers */ tmpa = a->dp; tmpb = b->dp; tmpc = c->dp; /* set carry to zero */ u = 0; for (i = 0; i < min; i++) { /* T[i] = A[i] - B[i] - U */ *tmpc = (*tmpa++ - *tmpb++) - u; /* U = carry bit of T[i] * Note this saves performing an AND operation since * if a carry does occur it will propagate all the way to the * MSB. As a result a single shift is enough to get the carry */ u = *tmpc >> ((mp_digit)((CHAR_BIT * sizeof(mp_digit)) - 1)); /* Clear carry from T[i] */ *tmpc++ &= MP_MASK; } /* now copy higher words if any, e.g. if A has more digits than B */ for ( ; i < max; i++) { /* T[i] = A[i] - U */ *tmpc = *tmpa++ - u; /* U = carry bit of T[i] */ u = *tmpc >> ((mp_digit)((CHAR_BIT * sizeof(mp_digit)) - 1)); /* Clear carry from T[i] */ *tmpc++ &= MP_MASK; } /* clear digits above used (since we may not have grown result above) */ for (i = c->used; i < olduse; i++) { *tmpc++ = 0; } } mp_clamp(c); return MP_OKAY; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ #ifdef BNCORE_C /* LibTomMath, multiple-precision integer library -- Tom St Denis * * LibTomMath is a library that provides multiple-precision * integer arithmetic as well as number theoretic functionality. * * The library was designed directly after the MPI library by * Michael Fromberger but has been written from scratch with * additional optimizations in place. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tstdenis82@gmail.com, http://libtom.org */ /* Known optimal configurations CPU /Compiler /MUL CUTOFF/SQR CUTOFF ------------------------------------------------------------- Intel P4 Northwood /GCC v3.4.1 / 88/ 128/LTM 0.32 ;-) AMD Athlon64 /GCC v3.4.4 / 80/ 120/LTM 0.35 */ int KARATSUBA_MUL_CUTOFF = 80, /* Min. number of digits before Karatsuba multiplication is used. */ KARATSUBA_SQR_CUTOFF = 120, /* Min. number of digits before Karatsuba squaring is used. */ TOOM_MUL_CUTOFF = 350, /* no optimal values of these are known yet so set em high */ TOOM_SQR_CUTOFF = 400; #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ /* LibTomCrypt, modular cryptographic library -- Tom St Denis * * LibTomCrypt is a library that provides various cryptographic * algorithms in a highly modular and flexible manner. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tomstdenis@gmail.com, http://libtom.org */ /** @file crypt.c Build strings, Tom St Denis */ const char *crypt_build_settings = "LibTomCrypt ""1.17"" (Tom St Denis, tomstdenis@gmail.com)\n" "LibTomCrypt is public domain software.\n" "Built on " __DATE__ " at " __TIME__ "\n\n\n" "Endianess: " #if defined(ENDIAN_NEUTRAL) "neutral\n" #elif defined(ENDIAN_LITTLE) "little" #if defined(ENDIAN_32BITWORD) " (32-bit words)\n" #else " (64-bit words)\n" #endif #elif defined(ENDIAN_BIG) "big" #if defined(ENDIAN_32BITWORD) " (32-bit words)\n" #else " (64-bit words)\n" #endif #endif "Clean stack: " #if defined(LTC_CLEAN_STACK) "enabled\n" #else "disabled\n" #endif "Ciphers built-in:\n" #if defined(LTC_BLOWFISH) " Blowfish\n" #endif #if defined(LTC_RC2) " LTC_RC2\n" #endif #if defined(LTC_RC5) " LTC_RC5\n" #endif #if defined(LTC_RC6) " LTC_RC6\n" #endif #if defined(LTC_SAFERP) " Safer+\n" #endif #if defined(LTC_SAFER) " Safer\n" #endif #if defined(LTC_RIJNDAEL) " Rijndael\n" #endif #if defined(LTC_XTEA) " LTC_XTEA\n" #endif #if defined(LTC_TWOFISH) " Twofish " #if defined(LTC_TWOFISH_SMALL) && defined(LTC_TWOFISH_TABLES) && defined(LTC_TWOFISH_ALL_TABLES) "(small, tables, all_tables)\n" #elif defined(LTC_TWOFISH_SMALL) && defined(LTC_TWOFISH_TABLES) "(small, tables)\n" #elif defined(LTC_TWOFISH_SMALL) && defined(LTC_TWOFISH_ALL_TABLES) "(small, all_tables)\n" #elif defined(LTC_TWOFISH_TABLES) && defined(LTC_TWOFISH_ALL_TABLES) "(tables, all_tables)\n" #elif defined(LTC_TWOFISH_SMALL) "(small)\n" #elif defined(LTC_TWOFISH_TABLES) "(tables)\n" #elif defined(LTC_TWOFISH_ALL_TABLES) "(all_tables)\n" #else "\n" #endif #endif #if defined(LTC_DES) " LTC_DES\n" #endif #if defined(LTC_CAST5) " LTC_CAST5\n" #endif #if defined(LTC_NOEKEON) " Noekeon\n" #endif #if defined(LTC_SKIPJACK) " Skipjack\n" #endif #if defined(LTC_KHAZAD) " Khazad\n" #endif #if defined(LTC_ANUBIS) " Anubis " #endif #if defined(LTC_ANUBIS_TWEAK) " (tweaked)" #endif "\n" #if defined(LTC_KSEED) " LTC_KSEED\n" #endif #if defined(LTC_KASUMI) " KASUMI\n" #endif "\nHashes built-in:\n" #if defined(LTC_SHA512) " LTC_SHA-512\n" #endif #if defined(LTC_SHA384) " LTC_SHA-384\n" #endif #if defined(LTC_SHA256) " LTC_SHA-256\n" #endif #if defined(LTC_SHA224) " LTC_SHA-224\n" #endif #if defined(LTC_TIGER) " LTC_TIGER\n" #endif #if defined(LTC_SHA1) " LTC_SHA1\n" #endif #if defined(LTC_MD5) " LTC_MD5\n" #endif #if defined(LTC_MD4) " LTC_MD4\n" #endif #if defined(LTC_MD2) " LTC_MD2\n" #endif #if defined(LTC_RIPEMD128) " LTC_RIPEMD128\n" #endif #if defined(LTC_RIPEMD160) " LTC_RIPEMD160\n" #endif #if defined(LTC_RIPEMD256) " LTC_RIPEMD256\n" #endif #if defined(LTC_RIPEMD320) " LTC_RIPEMD320\n" #endif #if defined(LTC_WHIRLPOOL) " LTC_WHIRLPOOL\n" #endif #if defined(LTC_CHC_HASH) " LTC_CHC_HASH \n" #endif "\nBlock Chaining Modes:\n" #if defined(LTC_CFB_MODE) " CFB\n" #endif #if defined(LTC_OFB_MODE) " OFB\n" #endif #if defined(LTC_ECB_MODE) " ECB\n" #endif #if defined(LTC_CBC_MODE) " CBC\n" #endif #if defined(LTC_CTR_MODE) " CTR " #endif #if defined(LTC_CTR_OLD) " (CTR_OLD) " #endif "\n" #if defined(LRW_MODE) " LRW_MODE" #if defined(LRW_TABLES) " (LRW_TABLES) " #endif "\n" #endif #if defined(LTC_F8_MODE) " F8 MODE\n" #endif #if defined(LTC_XTS_MODE) " LTC_XTS_MODE\n" #endif "\nMACs:\n" #if defined(LTC_HMAC) " LTC_HMAC\n" #endif #if defined(LTC_OMAC) " LTC_OMAC\n" #endif #if defined(LTC_PMAC) " PMAC\n" #endif #if defined(LTC_PELICAN) " LTC_PELICAN\n" #endif #if defined(LTC_XCBC) " XCBC-MAC\n" #endif #if defined(LTC_F9_MODE) " F9-MAC\n" #endif "\nENC + AUTH modes:\n" #if defined(LTC_EAX_MODE) " LTC_EAX_MODE\n" #endif #if defined(LTC_OCB_MODE) " LTC_OCB_MODE\n" #endif #if defined(LTC_CCM_MODE) " LTC_CCM_MODE\n" #endif #if defined(LTC_GCM_MODE) " LTC_GCM_MODE " #endif #if defined(LTC_GCM_TABLES) " (LTC_GCM_TABLES) " #endif "\n" "\nPRNG:\n" #if defined(LTC_YARROW) " Yarrow\n" #endif #if defined(LTC_SPRNG) " LTC_SPRNG\n" #endif #if defined(LTC_RC4) " LTC_RC4\n" #endif #if defined(LTC_FORTUNA) " Fortuna\n" #endif #if defined(LTC_SOBER128) " LTC_SOBER128\n" #endif "\nPK Algs:\n" #if defined(LTC_MRSA) " RSA \n" #endif #if defined(LTC_MECC) " ECC\n" #endif #if defined(LTC_MDSA) " DSA\n" #endif #if defined(MKAT) " Katja\n" #endif "\nCompiler:\n" #if defined(WIN32) " WIN32 platform detected.\n" #endif #if defined(__CYGWIN__) " CYGWIN Detected.\n" #endif #if defined(__DJGPP__) " DJGPP Detected.\n" #endif #if defined(_MSC_VER) " MSVC compiler detected.\n" #endif #if defined(__GNUC__) " GCC compiler detected.\n" #endif #if defined(INTEL_CC) " Intel C Compiler detected.\n" #endif #if defined(__x86_64__) " x86-64 detected.\n" #endif #if defined(LTC_PPC32) " LTC_PPC32 defined \n" #endif "\nVarious others: " #if defined(LTC_BASE64) " LTC_BASE64 " #endif #if defined(MPI) " MPI " #endif #if defined(TRY_UNRANDOM_FIRST) " TRY_UNRANDOM_FIRST " #endif #if defined(LTC_TEST) " LTC_TEST " #endif #if defined(LTC_PKCS_1) " LTC_PKCS#1 " #endif #if defined(LTC_PKCS_5) " LTC_PKCS#5 " #endif #if defined(LTC_SMALL_CODE) " LTC_SMALL_CODE " #endif #if defined(LTC_NO_FILE) " LTC_NO_FILE " #endif #if defined(LTC_DER) " LTC_DER " #endif #if defined(LTC_FAST) " LTC_FAST " #endif #if defined(LTC_NO_FAST) " LTC_NO_FAST " #endif #if defined(LTC_NO_BSWAP) " LTC_NO_BSWAP " #endif #if defined(LTC_NO_ASM) " LTC_NO_ASM " #endif #if defined(LTC_NO_TEST) " LTC_NO_TEST " #endif #if defined(LTC_NO_TABLES) " LTC_NO_TABLES " #endif #if defined(LTC_PTHREAD) " LTC_PTHREAD " #endif #if defined(LTM_LTC_DESC) " LTM_DESC " #endif #if defined(TFM_LTC_DESC) " TFM_DESC " #endif #if defined(LTC_MECC_ACCEL) " LTC_MECC_ACCEL " #endif #if defined(GMP_LTC_DESC) " GMP_DESC " #endif #if defined(LTC_EASY) " (easy) " #endif #if defined(LTC_MECC_FP) " LTC_MECC_FP " #endif #if defined(LTC_ECC_SHAMIR) " LTC_ECC_SHAMIR " #endif "\n" "\n\n\n" ; /* $Source: /cvs/libtom/libtomcrypt/src/misc/crypt/crypt.c,v $ */ /* $Revision: 1.36 $ */ /* $Date: 2007/05/12 14:46:12 $ */ /* LibTomCrypt, modular cryptographic library -- Tom St Denis * * LibTomCrypt is a library that provides various cryptographic * algorithms in a highly modular and flexible manner. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tomstdenis@gmail.com, http://libtom.org */ #include /** @file crypt_argchk.c Perform argument checking, Tom St Denis */ #if (ARGTYPE == 0) void crypt_argchk(char *v, char *s, int d) { fprintf(stderr, "LTC_ARGCHK '%s' failure on line %d of file %s\n", v, d, s); (void)raise(SIGABRT); } #endif #ifndef TOMCRYPT_H_ #define TOMCRYPT_H_ #define USE_LTM #define LTM_DESC #define LTC_SHA1 //#include #include #include #include #include #include #include /* use configuration data */ #ifndef TOMCRYPT_CUSTOM_H_ #define TOMCRYPT_CUSTOM_H_ /* macros for various libc functions you can change for embedded targets */ #ifndef XMALLOC #ifdef malloc #define LTC_NO_PROTOTYPES #endif #define XMALLOC malloc #endif #ifndef XREALLOC #ifdef realloc #define LTC_NO_PROTOTYPES #endif #define XREALLOC realloc #endif #ifndef XCALLOC #ifdef calloc #define LTC_NO_PROTOTYPES #endif #define XCALLOC calloc #endif #ifndef XFREE #ifdef free #define LTC_NO_PROTOTYPES #endif #define XFREE free #endif #ifndef XMEMSET #ifdef memset #define LTC_NO_PROTOTYPES #endif #define XMEMSET memset #endif #ifndef XMEMCPY #ifdef memcpy #define LTC_NO_PROTOTYPES #endif #define XMEMCPY memcpy #endif #ifndef XMEMCMP #ifdef memcmp #define LTC_NO_PROTOTYPES #endif #define XMEMCMP memcmp #endif #ifndef XSTRCMP #ifdef strcmp #define LTC_NO_PROTOTYPES #endif #define XSTRCMP strcmp #endif #ifndef XCLOCK #define XCLOCK clock #endif #ifndef XCLOCKS_PER_SEC #define XCLOCKS_PER_SEC CLOCKS_PER_SEC #endif #ifndef XQSORT #ifdef qsort #define LTC_NO_PROTOTYPES #endif #define XQSORT qsort #endif /* Easy button? */ #ifdef LTC_EASY #define LTC_NO_CIPHERS #define LTC_RIJNDAEL #define LTC_BLOWFISH #define LTC_DES #define LTC_CAST5 #define LTC_NO_MODES #define LTC_ECB_MODE #define LTC_CBC_MODE #define LTC_CTR_MODE #define LTC_NO_HASHES #define LTC_SHA1 #define LTC_SHA512 #define LTC_SHA384 #define LTC_SHA256 #define LTC_SHA224 #define LTC_NO_MACS #define LTC_HMAC #define LTC_OMAC #define LTC_CCM_MODE #define LTC_NO_PRNGS #define LTC_SPRNG #define LTC_YARROW #define LTC_DEVRANDOM #define TRY_URANDOM_FIRST #define LTC_NO_PK #define LTC_MRSA #define LTC_MECC #endif /* Use small code where possible */ /* #define LTC_SMALL_CODE */ /* Enable self-test test vector checking */ #ifndef LTC_NO_TEST #define LTC_TEST #endif /* clean the stack of functions which put private information on stack */ /* #define LTC_CLEAN_STACK */ /* disable all file related functions */ /* #define LTC_NO_FILE */ /* disable all forms of ASM */ /* #define LTC_NO_ASM */ /* disable FAST mode */ /* #define LTC_NO_FAST */ /* disable BSWAP on x86 */ /* #define LTC_NO_BSWAP */ /* ---> Symmetric Block Ciphers <--- */ #ifndef LTC_NO_CIPHERS #define LTC_BLOWFISH #define LTC_RC2 #define LTC_RC5 #define LTC_RC6 #define LTC_SAFERP #define LTC_RIJNDAEL #define LTC_XTEA /* _TABLES tells it to use tables during setup, _SMALL means to use the smaller scheduled key format * (saves 4KB of ram), _ALL_TABLES enables all tables during setup */ #define LTC_TWOFISH #ifndef LTC_NO_TABLES #define LTC_TWOFISH_TABLES /* #define LTC_TWOFISH_ALL_TABLES */ #else #define LTC_TWOFISH_SMALL #endif /* #define LTC_TWOFISH_SMALL */ /* LTC_DES includes EDE triple-LTC_DES */ #define LTC_DES #define LTC_CAST5 #define LTC_NOEKEON #define LTC_SKIPJACK #define LTC_SAFER #define LTC_KHAZAD #define LTC_ANUBIS #define LTC_ANUBIS_TWEAK #define LTC_KSEED #define LTC_KASUMI #endif /* LTC_NO_CIPHERS */ /* ---> Block Cipher Modes of Operation <--- */ #ifndef LTC_NO_MODES #define LTC_CFB_MODE #define LTC_OFB_MODE #define LTC_ECB_MODE #define LTC_CBC_MODE #define LTC_CTR_MODE /* F8 chaining mode */ #define LTC_F8_MODE /* LRW mode */ #define LTC_LRW_MODE #ifndef LTC_NO_TABLES /* like GCM mode this will enable 16 8x128 tables [64KB] that make * seeking very fast. */ #define LRW_TABLES #endif /* XTS mode */ #define LTC_XTS_MODE #endif /* LTC_NO_MODES */ /* ---> One-Way Hash Functions <--- */ #ifndef LTC_NO_HASHES #define LTC_CHC_HASH #define LTC_WHIRLPOOL #define LTC_SHA512 #define LTC_SHA384 #define LTC_SHA256 #define LTC_SHA224 #define LTC_TIGER #define LTC_SHA1 #define LTC_MD5 #define LTC_MD4 #define LTC_MD2 #define LTC_RIPEMD128 #define LTC_RIPEMD160 #define LTC_RIPEMD256 #define LTC_RIPEMD320 #endif /* LTC_NO_HASHES */ /* ---> MAC functions <--- */ #ifndef LTC_NO_MACS #define LTC_HMAC #define LTC_OMAC #define LTC_PMAC #define LTC_XCBC #define LTC_F9_MODE #define LTC_PELICAN #if defined(LTC_PELICAN) && !defined(LTC_RIJNDAEL) #error Pelican-MAC requires LTC_RIJNDAEL #endif /* ---> Encrypt + Authenticate Modes <--- */ #define LTC_EAX_MODE #if defined(LTC_EAX_MODE) && !(defined(LTC_CTR_MODE) && defined(LTC_OMAC)) #error LTC_EAX_MODE requires CTR and LTC_OMAC mode #endif #define LTC_OCB_MODE #define LTC_CCM_MODE #define LTC_GCM_MODE /* Use 64KiB tables */ #ifndef LTC_NO_TABLES #define LTC_GCM_TABLES #endif /* USE SSE2? requires GCC works on x86_32 and x86_64*/ #ifdef LTC_GCM_TABLES /* #define LTC_GCM_TABLES_SSE2 */ #endif #endif /* LTC_NO_MACS */ /* Various tidbits of modern neatoness */ #define LTC_BASE64 /* --> Pseudo Random Number Generators <--- */ #ifndef LTC_NO_PRNGS /* Yarrow */ #define LTC_YARROW /* which descriptor of AES to use? */ /* 0 = rijndael_enc 1 = aes_enc, 2 = rijndael [full], 3 = aes [full] */ #define LTC_YARROW_AES 0 #if defined(LTC_YARROW) && !defined(LTC_CTR_MODE) #error LTC_YARROW requires LTC_CTR_MODE chaining mode to be defined! #endif /* a PRNG that simply reads from an available system source */ #define LTC_SPRNG /* The LTC_RC4 stream cipher */ #define LTC_RC4 /* Fortuna PRNG */ #define LTC_FORTUNA /* reseed every N calls to the read function */ #define LTC_FORTUNA_WD 10 /* number of pools (4..32) can save a bit of ram by lowering the count */ #define LTC_FORTUNA_POOLS 32 /* Greg's LTC_SOBER128 PRNG ;-0 */ #define LTC_SOBER128 /* the *nix style /dev/random device */ #define LTC_DEVRANDOM /* try /dev/urandom before trying /dev/random */ #define TRY_URANDOM_FIRST #endif /* LTC_NO_PRNGS */ /* ---> math provider? <--- */ #ifndef LTC_NO_MATH /* LibTomMath */ /* #define LTM_LTC_DESC */ /* TomsFastMath */ /* #define TFM_LTC_DESC */ #endif /* LTC_NO_MATH */ /* ---> Public Key Crypto <--- */ #ifndef LTC_NO_PK /* Include RSA support */ #define LTC_MRSA /* Include Katja (a Rabin variant like RSA) */ /* #define MKAT */ /* Digital Signature Algorithm */ #define LTC_MDSA /* ECC */ #define LTC_MECC /* use Shamir's trick for point mul (speeds up signature verification) */ #define LTC_ECC_SHAMIR #if defined(TFM_LTC_DESC) && defined(LTC_MECC) #define LTC_MECC_ACCEL #endif /* do we want fixed point ECC */ /* #define LTC_MECC_FP */ /* Timing Resistant? */ /* #define LTC_ECC_TIMING_RESISTANT */ #endif /* LTC_NO_PK */ /* LTC_PKCS #1 (RSA) and #5 (Password Handling) stuff */ #ifndef LTC_NO_PKCS #define LTC_PKCS_1 #define LTC_PKCS_5 /* Include ASN.1 DER (required by DSA/RSA) */ #define LTC_DER #endif /* LTC_NO_PKCS */ /* cleanup */ #ifdef LTC_MECC /* Supported ECC Key Sizes */ #ifndef LTC_NO_CURVES #define ECC112 #define ECC128 #define ECC160 #define ECC192 #define ECC224 #define ECC256 #define ECC384 #define ECC521 #endif #endif #if defined(LTC_MECC) || defined(LTC_MRSA) || defined(LTC_MDSA) || defined(MKATJA) /* Include the MPI functionality? (required by the PK algorithms) */ #define MPI #endif #ifdef LTC_MRSA #define LTC_PKCS_1 #endif #if defined(LTC_DER) && !defined(MPI) #error ASN.1 DER requires MPI functionality #endif #if (defined(LTC_MDSA) || defined(LTC_MRSA) || defined(LTC_MECC) || defined(MKATJA)) && !defined(LTC_DER) #error PK requires ASN.1 DER functionality, make sure LTC_DER is enabled #endif /* THREAD management */ #ifdef LTC_PTHREAD #include #define LTC_MUTEX_GLOBAL(x) pthread_mutex_t x = PTHREAD_MUTEX_INITIALIZER; #define LTC_MUTEX_PROTO(x) extern pthread_mutex_t x; #define LTC_MUTEX_TYPE(x) pthread_mutex_t x; #define LTC_MUTEX_INIT(x) pthread_mutex_init(x, NULL); #define LTC_MUTEX_LOCK(x) pthread_mutex_lock(x); #define LTC_MUTEX_UNLOCK(x) pthread_mutex_unlock(x); #else /* default no functions */ #define LTC_MUTEX_GLOBAL(x) #define LTC_MUTEX_PROTO(x) #define LTC_MUTEX_TYPE(x) #define LTC_MUTEX_INIT(x) #define LTC_MUTEX_LOCK(x) #define LTC_MUTEX_UNLOCK(x) #endif /* Debuggers */ /* define this if you use Valgrind, note: it CHANGES the way SOBER-128 and LTC_RC4 work (see the code) */ /* #define LTC_VALGRIND */ #endif /* $Source: /cvs/libtom/libtomcrypt/src/headers/tomcrypt_custom.h,v $ */ /* $Revision: 1.73 $ */ /* $Date: 2007/05/12 14:37:41 $ */ /* version */ #define CRYPT 0x0117 #define SCRYPT "1.17" /* max size of either a cipher/hash block or symmetric key [largest of the two] */ #define MAXBLOCKSIZE 128 /* descriptor table size */ /* error codes [will be expanded in future releases] */ enum { CRYPT_OK=0, /* Result OK */ CRYPT_ERROR, /* Generic Error */ CRYPT_NOP, /* Not a failure but no operation was performed */ CRYPT_INVALID_KEYSIZE, /* Invalid key size given */ CRYPT_INVALID_ROUNDS, /* Invalid number of rounds */ CRYPT_FAIL_TESTVECTOR, /* Algorithm failed test vectors */ CRYPT_BUFFER_OVERFLOW, /* Not enough space for output */ CRYPT_INVALID_PACKET, /* Invalid input packet given */ CRYPT_INVALID_PRNGSIZE, /* Invalid number of bits for a PRNG */ CRYPT_ERROR_READPRNG, /* Could not read enough from PRNG */ CRYPT_INVALID_CIPHER, /* Invalid cipher specified */ CRYPT_INVALID_HASH, /* Invalid hash specified */ CRYPT_INVALID_PRNG, /* Invalid PRNG specified */ CRYPT_MEM, /* Out of memory */ CRYPT_PK_TYPE_MISMATCH, /* Not equivalent types of PK keys */ CRYPT_PK_NOT_PRIVATE, /* Requires a private PK key */ CRYPT_INVALID_ARG, /* Generic invalid argument */ CRYPT_FILE_NOTFOUND, /* File Not Found */ CRYPT_PK_INVALID_TYPE, /* Invalid type of PK key */ CRYPT_PK_INVALID_SYSTEM, /* Invalid PK system specified */ CRYPT_PK_DUP, /* Duplicate key already in key ring */ CRYPT_PK_NOT_FOUND, /* Key not found in keyring */ CRYPT_PK_INVALID_SIZE, /* Invalid size input for PK parameters */ CRYPT_INVALID_PRIME_SIZE, /* Invalid size of prime requested */ CRYPT_PK_INVALID_PADDING /* Invalid padding on input */ }; /* This is the build config file. * * With this you can setup what to inlcude/exclude automatically during any build. Just comment * out the line that #define's the word for the thing you want to remove. phew! */ #ifndef TOMCRYPT_CFG_H #define TOMCRYPT_CFG_H #if defined(_WIN32) || defined(_MSC_VER) #define LTC_CALL __cdecl #else #ifndef LTC_CALL #define LTC_CALL #endif #endif #ifndef LTC_EXPORT #define LTC_EXPORT #endif /* certain platforms use macros for these, making the prototypes broken */ #ifndef LTC_NO_PROTOTYPES /* you can change how memory allocation works ... */ LTC_EXPORT void *LTC_CALL XMALLOC(size_t n); LTC_EXPORT void *LTC_CALL XREALLOC(void *p, size_t n); LTC_EXPORT void *LTC_CALL XCALLOC(size_t n, size_t s); LTC_EXPORT void LTC_CALL XFREE(void *p); LTC_EXPORT void LTC_CALL XQSORT(void *base, size_t nmemb, size_t size, int (*compar)(const void *, const void *)); /* change the clock function too */ LTC_EXPORT clock_t LTC_CALL XCLOCK(void); /* various other functions */ LTC_EXPORT void *LTC_CALL XMEMCPY(void *dest, const void *src, size_t n); LTC_EXPORT int LTC_CALL XMEMCMP(const void *s1, const void *s2, size_t n); LTC_EXPORT void *LTC_CALL XMEMSET(void *s, int c, size_t n); LTC_EXPORT int LTC_CALL XSTRCMP(const char *s1, const char *s2); #endif /* type of argument checking, 0=default, 1=fatal and 2=error+continue, 3=nothing */ #ifndef ARGTYPE #define ARGTYPE 0 #endif /* Controls endianess and size of registers. Leave uncommented to get platform neutral [slower] code * * Note: in order to use the optimized macros your platform must support unaligned 32 and 64 bit read/writes. * The x86 platforms allow this but some others [ARM for instance] do not. On those platforms you **MUST** * use the portable [slower] macros. */ /* detect x86-32 machines somewhat */ #if !defined(__STRICT_ANSI__) && (defined(INTEL_CC) || (defined(_MSC_VER) && defined(WIN32)) || (defined(__GNUC__) && (defined(__DJGPP__) || defined(__CYGWIN__) || defined(__MINGW32__) || defined(__i386__)))) #define ENDIAN_LITTLE #define ENDIAN_32BITWORD #define LTC_FAST #define LTC_FAST_TYPE unsigned long #endif /* detects MIPS R5900 processors (PS2) */ #if (defined(__R5900) || defined(R5900) || defined(__R5900__)) && (defined(_mips) || defined(__mips__) || defined(mips)) #define ENDIAN_LITTLE #define ENDIAN_64BITWORD #endif /* detect amd64 */ #if !defined(__STRICT_ANSI__) && defined(__x86_64__) #define ENDIAN_LITTLE #define ENDIAN_64BITWORD #define LTC_FAST #define LTC_FAST_TYPE unsigned long #endif /* detect PPC32 */ #if !defined(__STRICT_ANSI__) && defined(LTC_PPC32) #define ENDIAN_BIG #define ENDIAN_32BITWORD #define LTC_FAST #define LTC_FAST_TYPE unsigned long #endif /* detect sparc and sparc64 */ #if defined(__sparc__) #define ENDIAN_BIG #if defined(__arch64__) #define ENDIAN_64BITWORD #else #define ENDIAN_32BITWORD #endif #endif #ifdef LTC_NO_FAST #ifdef LTC_FAST #undef LTC_FAST #endif #endif /* No asm is a quick way to disable anything "not portable" */ #ifdef LTC_NO_ASM #undef ENDIAN_LITTLE #undef ENDIAN_BIG #undef ENDIAN_32BITWORD #undef ENDIAN_64BITWORD #undef LTC_FAST #undef LTC_FAST_TYPE #define LTC_NO_ROLC #define LTC_NO_BSWAP #endif /* #define ENDIAN_LITTLE */ /* #define ENDIAN_BIG */ /* #define ENDIAN_32BITWORD */ /* #define ENDIAN_64BITWORD */ #if (defined(ENDIAN_BIG) || defined(ENDIAN_LITTLE)) && !(defined(ENDIAN_32BITWORD) || defined(ENDIAN_64BITWORD)) #error You must specify a word size as well as endianess in tomcrypt_cfg.h #endif #if !(defined(ENDIAN_BIG) || defined(ENDIAN_LITTLE)) #define ENDIAN_NEUTRAL #endif #endif /* $Source: /cvs/libtom/libtomcrypt/src/headers/tomcrypt_cfg.h,v $ */ /* $Revision: 1.19 $ */ /* $Date: 2006/12/04 02:19:48 $ */ /* fix for MSVC ...evil! */ #ifdef _MSC_VER #define CONST64(n) n ## ui64 typedef unsigned __int64 ulong64; #else #define CONST64(n) n ## ULL typedef unsigned long long ulong64; #endif /* this is the "32-bit at least" data type * Re-define it to suit your platform but it must be at least 32-bits */ #if defined(__x86_64__) || (defined(__sparc__) && defined(__arch64__)) typedef unsigned ulong32; #else typedef unsigned long ulong32; #endif /* ---- HELPER MACROS ---- */ #ifdef ENDIAN_NEUTRAL #define STORE32L(x, y) \ { (y)[3] = (unsigned char)(((x) >> 24) & 255); (y)[2] = (unsigned char)(((x) >> 16) & 255); \ (y)[1] = (unsigned char)(((x) >> 8) & 255); (y)[0] = (unsigned char)((x) & 255); } #define LOAD32L(x, y) \ { x = ((unsigned long)((y)[3] & 255) << 24) | \ ((unsigned long)((y)[2] & 255) << 16) | \ ((unsigned long)((y)[1] & 255) << 8) | \ ((unsigned long)((y)[0] & 255)); } #define STORE64L(x, y) \ { (y)[7] = (unsigned char)(((x) >> 56) & 255); (y)[6] = (unsigned char)(((x) >> 48) & 255); \ (y)[5] = (unsigned char)(((x) >> 40) & 255); (y)[4] = (unsigned char)(((x) >> 32) & 255); \ (y)[3] = (unsigned char)(((x) >> 24) & 255); (y)[2] = (unsigned char)(((x) >> 16) & 255); \ (y)[1] = (unsigned char)(((x) >> 8) & 255); (y)[0] = (unsigned char)((x) & 255); } #define LOAD64L(x, y) \ { x = (((ulong64)((y)[7] & 255)) << 56) | (((ulong64)((y)[6] & 255)) << 48) | \ (((ulong64)((y)[5] & 255)) << 40) | (((ulong64)((y)[4] & 255)) << 32) | \ (((ulong64)((y)[3] & 255)) << 24) | (((ulong64)((y)[2] & 255)) << 16) | \ (((ulong64)((y)[1] & 255)) << 8) | (((ulong64)((y)[0] & 255))); } #define STORE32H(x, y) \ { (y)[0] = (unsigned char)(((x) >> 24) & 255); (y)[1] = (unsigned char)(((x) >> 16) & 255); \ (y)[2] = (unsigned char)(((x) >> 8) & 255); (y)[3] = (unsigned char)((x) & 255); } #define LOAD32H(x, y) \ { x = ((unsigned long)((y)[0] & 255) << 24) | \ ((unsigned long)((y)[1] & 255) << 16) | \ ((unsigned long)((y)[2] & 255) << 8) | \ ((unsigned long)((y)[3] & 255)); } #define STORE64H(x, y) \ { (y)[0] = (unsigned char)(((x) >> 56) & 255); (y)[1] = (unsigned char)(((x) >> 48) & 255); \ (y)[2] = (unsigned char)(((x) >> 40) & 255); (y)[3] = (unsigned char)(((x) >> 32) & 255); \ (y)[4] = (unsigned char)(((x) >> 24) & 255); (y)[5] = (unsigned char)(((x) >> 16) & 255); \ (y)[6] = (unsigned char)(((x) >> 8) & 255); (y)[7] = (unsigned char)((x) & 255); } #define LOAD64H(x, y) \ { x = (((ulong64)((y)[0] & 255)) << 56) | (((ulong64)((y)[1] & 255)) << 48) | \ (((ulong64)((y)[2] & 255)) << 40) | (((ulong64)((y)[3] & 255)) << 32) | \ (((ulong64)((y)[4] & 255)) << 24) | (((ulong64)((y)[5] & 255)) << 16) | \ (((ulong64)((y)[6] & 255)) << 8) | (((ulong64)((y)[7] & 255))); } #endif /* ENDIAN_NEUTRAL */ #ifdef ENDIAN_LITTLE #if !defined(LTC_NO_BSWAP) && (defined(INTEL_CC) || (defined(__GNUC__) && (defined(__DJGPP__) || defined(__CYGWIN__) || defined(__MINGW32__) || defined(__i386__) || defined(__x86_64__)))) #define STORE32H(x, y) \ asm __volatile__ ( \ "bswapl %0 \n\t" \ "movl %0,(%1)\n\t" \ "bswapl %0 \n\t" \ ::"r" (x), "r" (y)); #define LOAD32H(x, y) \ asm __volatile__ ( \ "movl (%1),%0\n\t" \ "bswapl %0\n\t" \ : "=r" (x) : "r" (y)); #else #define STORE32H(x, y) \ { (y)[0] = (unsigned char)(((x) >> 24) & 255); (y)[1] = (unsigned char)(((x) >> 16) & 255); \ (y)[2] = (unsigned char)(((x) >> 8) & 255); (y)[3] = (unsigned char)((x) & 255); } #define LOAD32H(x, y) \ { x = ((unsigned long)((y)[0] & 255) << 24) | \ ((unsigned long)((y)[1] & 255) << 16) | \ ((unsigned long)((y)[2] & 255) << 8) | \ ((unsigned long)((y)[3] & 255)); } #endif /* x86_64 processor */ #if !defined(LTC_NO_BSWAP) && (defined(__GNUC__) && defined(__x86_64__)) #define STORE64H(x, y) \ asm __volatile__ ( \ "bswapq %0 \n\t" \ "movq %0,(%1)\n\t" \ "bswapq %0 \n\t" \ ::"r" (x), "r" (y)); #define LOAD64H(x, y) \ asm __volatile__ ( \ "movq (%1),%0\n\t" \ "bswapq %0\n\t" \ : "=r" (x) : "r" (y)); #else #define STORE64H(x, y) \ { (y)[0] = (unsigned char)(((x) >> 56) & 255); (y)[1] = (unsigned char)(((x) >> 48) & 255); \ (y)[2] = (unsigned char)(((x) >> 40) & 255); (y)[3] = (unsigned char)(((x) >> 32) & 255); \ (y)[4] = (unsigned char)(((x) >> 24) & 255); (y)[5] = (unsigned char)(((x) >> 16) & 255); \ (y)[6] = (unsigned char)(((x) >> 8) & 255); (y)[7] = (unsigned char)((x) & 255); } #define LOAD64H(x, y) \ { x = (((ulong64)((y)[0] & 255)) << 56) | (((ulong64)((y)[1] & 255)) << 48) | \ (((ulong64)((y)[2] & 255)) << 40) | (((ulong64)((y)[3] & 255)) << 32) | \ (((ulong64)((y)[4] & 255)) << 24) | (((ulong64)((y)[5] & 255)) << 16) | \ (((ulong64)((y)[6] & 255)) << 8) | (((ulong64)((y)[7] & 255))); } #endif #ifdef ENDIAN_32BITWORD #define STORE32L(x, y) \ { ulong32 __t = (x); XMEMCPY(y, &__t, 4); } #define LOAD32L(x, y) \ XMEMCPY(&(x), y, 4); #define STORE64L(x, y) \ { (y)[7] = (unsigned char)(((x) >> 56) & 255); (y)[6] = (unsigned char)(((x) >> 48) & 255); \ (y)[5] = (unsigned char)(((x) >> 40) & 255); (y)[4] = (unsigned char)(((x) >> 32) & 255); \ (y)[3] = (unsigned char)(((x) >> 24) & 255); (y)[2] = (unsigned char)(((x) >> 16) & 255); \ (y)[1] = (unsigned char)(((x) >> 8) & 255); (y)[0] = (unsigned char)((x) & 255); } #define LOAD64L(x, y) \ { x = (((ulong64)((y)[7] & 255)) << 56) | (((ulong64)((y)[6] & 255)) << 48) | \ (((ulong64)((y)[5] & 255)) << 40) | (((ulong64)((y)[4] & 255)) << 32) | \ (((ulong64)((y)[3] & 255)) << 24) | (((ulong64)((y)[2] & 255)) << 16) | \ (((ulong64)((y)[1] & 255)) << 8) | (((ulong64)((y)[0] & 255))); } #else /* 64-bit words then */ #define STORE32L(x, y) \ { ulong32 __t = (x); XMEMCPY(y, &__t, 4); } #define LOAD32L(x, y) \ { XMEMCPY(&(x), y, 4); x &= 0xFFFFFFFF; } #define STORE64L(x, y) \ { ulong64 __t = (x); XMEMCPY(y, &__t, 8); } #define LOAD64L(x, y) \ { XMEMCPY(&(x), y, 8); } #endif /* ENDIAN_64BITWORD */ #endif /* ENDIAN_LITTLE */ #ifdef ENDIAN_BIG #define STORE32L(x, y) \ { (y)[3] = (unsigned char)(((x) >> 24) & 255); (y)[2] = (unsigned char)(((x) >> 16) & 255); \ (y)[1] = (unsigned char)(((x) >> 8) & 255); (y)[0] = (unsigned char)((x) & 255); } #define LOAD32L(x, y) \ { x = ((unsigned long)((y)[3] & 255) << 24) | \ ((unsigned long)((y)[2] & 255) << 16) | \ ((unsigned long)((y)[1] & 255) << 8) | \ ((unsigned long)((y)[0] & 255)); } #define STORE64L(x, y) \ { (y)[7] = (unsigned char)(((x) >> 56) & 255); (y)[6] = (unsigned char)(((x) >> 48) & 255); \ (y)[5] = (unsigned char)(((x) >> 40) & 255); (y)[4] = (unsigned char)(((x) >> 32) & 255); \ (y)[3] = (unsigned char)(((x) >> 24) & 255); (y)[2] = (unsigned char)(((x) >> 16) & 255); \ (y)[1] = (unsigned char)(((x) >> 8) & 255); (y)[0] = (unsigned char)((x) & 255); } #define LOAD64L(x, y) \ { x = (((ulong64)((y)[7] & 255)) << 56) | (((ulong64)((y)[6] & 255)) << 48) | \ (((ulong64)((y)[5] & 255)) << 40) | (((ulong64)((y)[4] & 255)) << 32) | \ (((ulong64)((y)[3] & 255)) << 24) | (((ulong64)((y)[2] & 255)) << 16) | \ (((ulong64)((y)[1] & 255)) << 8) | (((ulong64)((y)[0] & 255))); } #ifdef ENDIAN_32BITWORD #define STORE32H(x, y) \ { ulong32 __t = (x); XMEMCPY(y, &__t, 4); } #define LOAD32H(x, y) \ XMEMCPY(&(x), y, 4); #define STORE64H(x, y) \ { (y)[0] = (unsigned char)(((x) >> 56) & 255); (y)[1] = (unsigned char)(((x) >> 48) & 255); \ (y)[2] = (unsigned char)(((x) >> 40) & 255); (y)[3] = (unsigned char)(((x) >> 32) & 255); \ (y)[4] = (unsigned char)(((x) >> 24) & 255); (y)[5] = (unsigned char)(((x) >> 16) & 255); \ (y)[6] = (unsigned char)(((x) >> 8) & 255); (y)[7] = (unsigned char)((x) & 255); } #define LOAD64H(x, y) \ { x = (((ulong64)((y)[0] & 255)) << 56) | (((ulong64)((y)[1] & 255)) << 48) | \ (((ulong64)((y)[2] & 255)) << 40) | (((ulong64)((y)[3] & 255)) << 32) | \ (((ulong64)((y)[4] & 255)) << 24) | (((ulong64)((y)[5] & 255)) << 16) | \ (((ulong64)((y)[6] & 255)) << 8) | (((ulong64)((y)[7] & 255))); } #else /* 64-bit words then */ #define STORE32H(x, y) \ { ulong32 __t = (x); XMEMCPY(y, &__t, 4); } #define LOAD32H(x, y) \ { XMEMCPY(&(x), y, 4); x &= 0xFFFFFFFF; } #define STORE64H(x, y) \ { ulong64 __t = (x); XMEMCPY(y, &__t, 8); } #define LOAD64H(x, y) \ { XMEMCPY(&(x), y, 8); } #endif /* ENDIAN_64BITWORD */ #endif /* ENDIAN_BIG */ #define BSWAP(x) \ (((x >> 24) & 0x000000FFUL) | ((x << 24) & 0xFF000000UL) | \ ((x >> 8) & 0x0000FF00UL) | ((x << 8) & 0x00FF0000UL)) /* 32-bit Rotates */ #if defined(_MSC_VER) /* instrinsic rotate */ #include #pragma intrinsic(_lrotr,_lrotl) #define ROR(x, n) _lrotr(x, n) #define ROL(x, n) _lrotl(x, n) #define RORc(x, n) _lrotr(x, n) #define ROLc(x, n) _lrotl(x, n) #elif !defined(__STRICT_ANSI__) && defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__)) && !defined(INTEL_CC) && !defined(LTC_NO_ASM) static inline unsigned ROL(unsigned word, int i) { asm ("roll %%cl,%0" : "=r" (word) : "0" (word), "c" (i)); return word; } static inline unsigned ROR(unsigned word, int i) { asm ("rorl %%cl,%0" : "=r" (word) : "0" (word), "c" (i)); return word; } #ifndef LTC_NO_ROLC static inline unsigned ROLc(unsigned word, const int i) { asm ("roll %2,%0" : "=r" (word) : "0" (word), "I" (i)); return word; } static inline unsigned RORc(unsigned word, const int i) { asm ("rorl %2,%0" : "=r" (word) : "0" (word), "I" (i)); return word; } #else #define ROLc ROL #define RORc ROR #endif #elif !defined(__STRICT_ANSI__) && defined(LTC_PPC32) static inline unsigned ROL(unsigned word, int i) { asm ("rotlw %0,%0,%2" : "=r" (word) : "0" (word), "r" (i)); return word; } static inline unsigned ROR(unsigned word, int i) { asm ("rotlw %0,%0,%2" : "=r" (word) : "0" (word), "r" (32 - i)); return word; } #ifndef LTC_NO_ROLC static inline unsigned ROLc(unsigned word, const int i) { asm ("rotlwi %0,%0,%2" : "=r" (word) : "0" (word), "I" (i)); return word; } static inline unsigned RORc(unsigned word, const int i) { asm ("rotrwi %0,%0,%2" : "=r" (word) : "0" (word), "I" (i)); return word; } #else #define ROLc ROL #define RORc ROR #endif #else /* rotates the hard way */ #define ROL(x, y) ((((unsigned long)(x) << (unsigned long)((y) & 31)) | (((unsigned long)(x) & 0xFFFFFFFFUL) >> (unsigned long)(32 - ((y) & 31)))) & 0xFFFFFFFFUL) #define ROR(x, y) (((((unsigned long)(x) & 0xFFFFFFFFUL) >> (unsigned long)((y) & 31)) | ((unsigned long)(x) << (unsigned long)(32 - ((y) & 31)))) & 0xFFFFFFFFUL) #define ROLc(x, y) ((((unsigned long)(x) << (unsigned long)((y) & 31)) | (((unsigned long)(x) & 0xFFFFFFFFUL) >> (unsigned long)(32 - ((y) & 31)))) & 0xFFFFFFFFUL) #define RORc(x, y) (((((unsigned long)(x) & 0xFFFFFFFFUL) >> (unsigned long)((y) & 31)) | ((unsigned long)(x) << (unsigned long)(32 - ((y) & 31)))) & 0xFFFFFFFFUL) #endif /* 64-bit Rotates */ #if !defined(__STRICT_ANSI__) && defined(__GNUC__) && defined(__x86_64__) && !defined(LTC_NO_ASM) static inline unsigned long ROL64(unsigned long word, int i) { asm ("rolq %%cl,%0" : "=r" (word) : "0" (word), "c" (i)); return word; } static inline unsigned long ROR64(unsigned long word, int i) { asm ("rorq %%cl,%0" : "=r" (word) : "0" (word), "c" (i)); return word; } #ifndef LTC_NO_ROLC static inline unsigned long ROL64c(unsigned long word, const int i) { asm ("rolq %2,%0" : "=r" (word) : "0" (word), "J" (i)); return word; } static inline unsigned long ROR64c(unsigned long word, const int i) { asm ("rorq %2,%0" : "=r" (word) : "0" (word), "J" (i)); return word; } #else /* LTC_NO_ROLC */ #define ROL64c ROL64 #define ROR64c ROR64 #endif #else /* Not x86_64 */ #define ROL64(x, y) \ ((((x) << ((ulong64)(y) & 63)) | \ (((x) & CONST64(0xFFFFFFFFFFFFFFFF)) >> ((ulong64)64 - ((y) & 63)))) & CONST64(0xFFFFFFFFFFFFFFFF)) #define ROR64(x, y) \ (((((x) & CONST64(0xFFFFFFFFFFFFFFFF)) >> ((ulong64)(y) & CONST64(63))) | \ ((x) << ((ulong64)(64 - ((y) & CONST64(63)))))) & CONST64(0xFFFFFFFFFFFFFFFF)) #define ROL64c(x, y) \ ((((x) << ((ulong64)(y) & 63)) | \ (((x) & CONST64(0xFFFFFFFFFFFFFFFF)) >> ((ulong64)64 - ((y) & 63)))) & CONST64(0xFFFFFFFFFFFFFFFF)) #define ROR64c(x, y) \ (((((x) & CONST64(0xFFFFFFFFFFFFFFFF)) >> ((ulong64)(y) & CONST64(63))) | \ ((x) << ((ulong64)(64 - ((y) & CONST64(63)))))) & CONST64(0xFFFFFFFFFFFFFFFF)) #endif #ifndef MAX #define MAX(x, y) (((x) > (y)) ? (x) : (y)) #endif #ifndef MIN #define MIN(x, y) (((x) < (y)) ? (x) : (y)) #endif /* extract a byte portably */ #ifdef _MSC_VER #define byte(x, n) ((unsigned char)((x) >> (8 * (n)))) #else #define byte(x, n) (((x) >> (8 * (n))) & 255) #endif /* $Source: /cvs/libtom/libtomcrypt/src/headers/tomcrypt_macros.h,v $ */ /* $Revision: 1.15 $ */ /* $Date: 2006/11/29 23:43:57 $ */ /* ---- SYMMETRIC KEY STUFF ----- * * We put each of the ciphers scheduled keys in their own structs then we put all of * the key formats in one union. This makes the function prototypes easier to use. */ #ifdef LTC_BLOWFISH struct blowfish_key { ulong32 S[4][256]; ulong32 K[18]; }; #endif #ifdef LTC_RC5 struct rc5_key { int rounds; ulong32 K[50]; }; #endif #ifdef LTC_RC6 struct rc6_key { ulong32 K[44]; }; #endif #ifdef LTC_SAFERP struct saferp_key { unsigned char K[33][16]; long rounds; }; #endif #ifdef LTC_RIJNDAEL struct rijndael_key { ulong32 eK[60], dK[60]; int Nr; }; #endif #ifdef LTC_KSEED struct kseed_key { ulong32 K[32], dK[32]; }; #endif #ifdef LTC_KASUMI struct kasumi_key { ulong32 KLi1[8], KLi2[8], KOi1[8], KOi2[8], KOi3[8], KIi1[8], KIi2[8], KIi3[8]; }; #endif #ifdef LTC_XTEA struct xtea_key { unsigned long A[32], B[32]; }; #endif #ifdef LTC_TWOFISH #ifndef LTC_TWOFISH_SMALL struct twofish_key { ulong32 S[4][256], K[40]; }; #else struct twofish_key { ulong32 K[40]; unsigned char S[32], start; }; #endif #endif #ifdef LTC_SAFER #define LTC_SAFER_K64_DEFAULT_NOF_ROUNDS 6 #define LTC_SAFER_K128_DEFAULT_NOF_ROUNDS 10 #define LTC_SAFER_SK64_DEFAULT_NOF_ROUNDS 8 #define LTC_SAFER_SK128_DEFAULT_NOF_ROUNDS 10 #define LTC_SAFER_MAX_NOF_ROUNDS 13 #define LTC_SAFER_BLOCK_LEN 8 #define LTC_SAFER_KEY_LEN (1 + LTC_SAFER_BLOCK_LEN * (1 + 2 * LTC_SAFER_MAX_NOF_ROUNDS)) typedef unsigned char safer_block_t[LTC_SAFER_BLOCK_LEN]; typedef unsigned char safer_key_t[LTC_SAFER_KEY_LEN]; struct safer_key { safer_key_t key; }; #endif #ifdef LTC_RC2 struct rc2_key { unsigned xkey[64]; }; #endif #ifdef LTC_DES struct des_key { ulong32 ek[32], dk[32]; }; struct des3_key { ulong32 ek[3][32], dk[3][32]; }; #endif #ifdef LTC_CAST5 struct cast5_key { ulong32 K[32], keylen; }; #endif #ifdef LTC_NOEKEON struct noekeon_key { ulong32 K[4], dK[4]; }; #endif #ifdef LTC_SKIPJACK struct skipjack_key { unsigned char key[10]; }; #endif #ifdef LTC_KHAZAD struct khazad_key { ulong64 roundKeyEnc[8 + 1]; ulong64 roundKeyDec[8 + 1]; }; #endif #ifdef LTC_ANUBIS struct anubis_key { int keyBits; int R; ulong32 roundKeyEnc[18 + 1][4]; ulong32 roundKeyDec[18 + 1][4]; }; #endif #ifdef LTC_MULTI2 struct multi2_key { int N; ulong32 uk[8]; }; #endif typedef union Symmetric_key { #ifdef LTC_DES struct des_key des; struct des3_key des3; #endif #ifdef LTC_RC2 struct rc2_key rc2; #endif #ifdef LTC_SAFER struct safer_key safer; #endif #ifdef LTC_TWOFISH struct twofish_key twofish; #endif #ifdef LTC_BLOWFISH struct blowfish_key blowfish; #endif #ifdef LTC_RC5 struct rc5_key rc5; #endif #ifdef LTC_RC6 struct rc6_key rc6; #endif #ifdef LTC_SAFERP struct saferp_key saferp; #endif #ifdef LTC_RIJNDAEL struct rijndael_key rijndael; #endif #ifdef LTC_XTEA struct xtea_key xtea; #endif #ifdef LTC_CAST5 struct cast5_key cast5; #endif #ifdef LTC_NOEKEON struct noekeon_key noekeon; #endif #ifdef LTC_SKIPJACK struct skipjack_key skipjack; #endif #ifdef LTC_KHAZAD struct khazad_key khazad; #endif #ifdef LTC_ANUBIS struct anubis_key anubis; #endif #ifdef LTC_KSEED struct kseed_key kseed; #endif #ifdef LTC_KASUMI struct kasumi_key kasumi; #endif #ifdef LTC_MULTI2 struct multi2_key multi2; #endif void *data; } symmetric_key; #ifdef LTC_ECB_MODE /** A block cipher ECB structure */ typedef struct { /** The index of the cipher chosen */ int cipher, /** The block size of the given cipher */ blocklen; /** The scheduled key */ symmetric_key key; } symmetric_ECB; #endif #ifdef LTC_CFB_MODE /** A block cipher CFB structure */ typedef struct { /** The index of the cipher chosen */ int cipher, /** The block size of the given cipher */ blocklen, /** The padding offset */ padlen; /** The current IV */ unsigned char IV[MAXBLOCKSIZE], /** The pad used to encrypt/decrypt */ pad[MAXBLOCKSIZE]; /** The scheduled key */ symmetric_key key; } symmetric_CFB; #endif #ifdef LTC_OFB_MODE /** A block cipher OFB structure */ typedef struct { /** The index of the cipher chosen */ int cipher, /** The block size of the given cipher */ blocklen, /** The padding offset */ padlen; /** The current IV */ unsigned char IV[MAXBLOCKSIZE]; /** The scheduled key */ symmetric_key key; } symmetric_OFB; #endif #ifdef LTC_CBC_MODE /** A block cipher CBC structure */ typedef struct { /** The index of the cipher chosen */ int cipher, /** The block size of the given cipher */ blocklen; /** The current IV */ unsigned char IV[MAXBLOCKSIZE]; /** The scheduled key */ symmetric_key key; } symmetric_CBC; #endif #ifdef LTC_CTR_MODE /** A block cipher CTR structure */ typedef struct { /** The index of the cipher chosen */ int cipher, /** The block size of the given cipher */ blocklen, /** The padding offset */ padlen, /** The mode (endianess) of the CTR, 0==little, 1==big */ mode, /** counter width */ ctrlen; /** The counter */ unsigned char ctr[MAXBLOCKSIZE], /** The pad used to encrypt/decrypt */ pad[MAXBLOCKSIZE]; /** The scheduled key */ symmetric_key key; } symmetric_CTR; #endif #ifdef LTC_LRW_MODE /** A LRW structure */ typedef struct { /** The index of the cipher chosen (must be a 128-bit block cipher) */ int cipher; /** The current IV */ unsigned char IV[16], /** the tweak key */ tweak[16], /** The current pad, it's the product of the first 15 bytes against the tweak key */ pad[16]; /** The scheduled symmetric key */ symmetric_key key; #ifdef LRW_TABLES /** The pre-computed multiplication table */ unsigned char PC[16][256][16]; #endif } symmetric_LRW; #endif #ifdef LTC_F8_MODE /** A block cipher F8 structure */ typedef struct { /** The index of the cipher chosen */ int cipher, /** The block size of the given cipher */ blocklen, /** The padding offset */ padlen; /** The current IV */ unsigned char IV[MAXBLOCKSIZE], MIV[MAXBLOCKSIZE]; /** Current block count */ ulong32 blockcnt; /** The scheduled key */ symmetric_key key; } symmetric_F8; #endif /** cipher descriptor table, last entry has "name == NULL" to mark the end of table */ extern struct ltc_cipher_descriptor { /** name of cipher */ char *name; /** internal ID */ unsigned char ID; /** min keysize (octets) */ int min_key_length, /** max keysize (octets) */ max_key_length, /** block size (octets) */ block_length, /** default number of rounds */ default_rounds; /** Setup the cipher @param key The input symmetric key @param keylen The length of the input key (octets) @param num_rounds The requested number of rounds (0==default) @param skey [out] The destination of the scheduled key @return CRYPT_OK if successful */ int (*setup)(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey); /** Encrypt a block @param pt The plaintext @param ct [out] The ciphertext @param skey The scheduled key @return CRYPT_OK if successful */ int (*ecb_encrypt)(const unsigned char *pt, unsigned char *ct, symmetric_key *skey); /** Decrypt a block @param ct The ciphertext @param pt [out] The plaintext @param skey The scheduled key @return CRYPT_OK if successful */ int (*ecb_decrypt)(const unsigned char *ct, unsigned char *pt, symmetric_key *skey); /** Test the block cipher @return CRYPT_OK if successful, CRYPT_NOP if self-testing has been disabled */ int (*test)(void); /** Terminate the context @param skey The scheduled key */ void (*done)(symmetric_key *skey); /** Determine a key size @param keysize [in/out] The size of the key desired and the suggested size @return CRYPT_OK if successful */ int (*keysize)(int *keysize); /** Accelerators **/ /** Accelerated ECB encryption @param pt Plaintext @param ct Ciphertext @param blocks The number of complete blocks to process @param skey The scheduled key context @return CRYPT_OK if successful */ int (*accel_ecb_encrypt)(const unsigned char *pt, unsigned char *ct, unsigned long blocks, symmetric_key *skey); /** Accelerated ECB decryption @param pt Plaintext @param ct Ciphertext @param blocks The number of complete blocks to process @param skey The scheduled key context @return CRYPT_OK if successful */ int (*accel_ecb_decrypt)(const unsigned char *ct, unsigned char *pt, unsigned long blocks, symmetric_key *skey); /** Accelerated CBC encryption @param pt Plaintext @param ct Ciphertext @param blocks The number of complete blocks to process @param IV The initial value (input/output) @param skey The scheduled key context @return CRYPT_OK if successful */ int (*accel_cbc_encrypt)(const unsigned char *pt, unsigned char *ct, unsigned long blocks, unsigned char *IV, symmetric_key *skey); /** Accelerated CBC decryption @param pt Plaintext @param ct Ciphertext @param blocks The number of complete blocks to process @param IV The initial value (input/output) @param skey The scheduled key context @return CRYPT_OK if successful */ int (*accel_cbc_decrypt)(const unsigned char *ct, unsigned char *pt, unsigned long blocks, unsigned char *IV, symmetric_key *skey); /** Accelerated CTR encryption @param pt Plaintext @param ct Ciphertext @param blocks The number of complete blocks to process @param IV The initial value (input/output) @param mode little or big endian counter (mode=0 or mode=1) @param skey The scheduled key context @return CRYPT_OK if successful */ int (*accel_ctr_encrypt)(const unsigned char *pt, unsigned char *ct, unsigned long blocks, unsigned char *IV, int mode, symmetric_key *skey); /** Accelerated LRW @param pt Plaintext @param ct Ciphertext @param blocks The number of complete blocks to process @param IV The initial value (input/output) @param tweak The LRW tweak @param skey The scheduled key context @return CRYPT_OK if successful */ int (*accel_lrw_encrypt)(const unsigned char *pt, unsigned char *ct, unsigned long blocks, unsigned char *IV, const unsigned char *tweak, symmetric_key *skey); /** Accelerated LRW @param ct Ciphertext @param pt Plaintext @param blocks The number of complete blocks to process @param IV The initial value (input/output) @param tweak The LRW tweak @param skey The scheduled key context @return CRYPT_OK if successful */ int (*accel_lrw_decrypt)(const unsigned char *ct, unsigned char *pt, unsigned long blocks, unsigned char *IV, const unsigned char *tweak, symmetric_key *skey); /** Accelerated CCM packet (one-shot) @param key The secret key to use @param keylen The length of the secret key (octets) @param uskey A previously scheduled key [optional can be NULL] @param nonce The session nonce [use once] @param noncelen The length of the nonce @param header The header for the session @param headerlen The length of the header (octets) @param pt [out] The plaintext @param ptlen The length of the plaintext (octets) @param ct [out] The ciphertext @param tag [out] The destination tag @param taglen [in/out] The max size and resulting size of the authentication tag @param direction Encrypt or Decrypt direction (0 or 1) @return CRYPT_OK if successful */ int (*accel_ccm_memory)( const unsigned char *key, unsigned long keylen, symmetric_key *uskey, const unsigned char *nonce, unsigned long noncelen, const unsigned char *header, unsigned long headerlen, unsigned char *pt, unsigned long ptlen, unsigned char *ct, unsigned char *tag, unsigned long *taglen, int direction); /** Accelerated GCM packet (one shot) @param key The secret key @param keylen The length of the secret key @param IV The initial vector @param IVlen The length of the initial vector @param adata The additional authentication data (header) @param adatalen The length of the adata @param pt The plaintext @param ptlen The length of the plaintext (ciphertext length is the same) @param ct The ciphertext @param tag [out] The MAC tag @param taglen [in/out] The MAC tag length @param direction Encrypt or Decrypt mode (GCM_ENCRYPT or GCM_DECRYPT) @return CRYPT_OK on success */ int (*accel_gcm_memory)( const unsigned char *key, unsigned long keylen, const unsigned char *IV, unsigned long IVlen, const unsigned char *adata, unsigned long adatalen, unsigned char *pt, unsigned long ptlen, unsigned char *ct, unsigned char *tag, unsigned long *taglen, int direction); /** Accelerated one shot LTC_OMAC @param key The secret key @param keylen The key length (octets) @param in The message @param inlen Length of message (octets) @param out [out] Destination for tag @param outlen [in/out] Initial and final size of out @return CRYPT_OK on success */ int (*omac_memory)( const unsigned char *key, unsigned long keylen, const unsigned char *in, unsigned long inlen, unsigned char *out, unsigned long *outlen); /** Accelerated one shot XCBC @param key The secret key @param keylen The key length (octets) @param in The message @param inlen Length of message (octets) @param out [out] Destination for tag @param outlen [in/out] Initial and final size of out @return CRYPT_OK on success */ int (*xcbc_memory)( const unsigned char *key, unsigned long keylen, const unsigned char *in, unsigned long inlen, unsigned char *out, unsigned long *outlen); /** Accelerated one shot F9 @param key The secret key @param keylen The key length (octets) @param in The message @param inlen Length of message (octets) @param out [out] Destination for tag @param outlen [in/out] Initial and final size of out @return CRYPT_OK on success @remark Requires manual padding */ int (*f9_memory)( const unsigned char *key, unsigned long keylen, const unsigned char *in, unsigned long inlen, unsigned char *out, unsigned long *outlen); } cipher_descriptor[]; #ifdef LTC_BLOWFISH int blowfish_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey); int blowfish_ecb_encrypt(const unsigned char *pt, unsigned char *ct, symmetric_key *skey); int blowfish_ecb_decrypt(const unsigned char *ct, unsigned char *pt, symmetric_key *skey); int blowfish_test(void); void blowfish_done(symmetric_key *skey); int blowfish_keysize(int *keysize); extern const struct ltc_cipher_descriptor blowfish_desc; #endif #ifdef LTC_RC5 int rc5_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey); int rc5_ecb_encrypt(const unsigned char *pt, unsigned char *ct, symmetric_key *skey); int rc5_ecb_decrypt(const unsigned char *ct, unsigned char *pt, symmetric_key *skey); int rc5_test(void); void rc5_done(symmetric_key *skey); int rc5_keysize(int *keysize); extern const struct ltc_cipher_descriptor rc5_desc; #endif #ifdef LTC_RC6 int rc6_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey); int rc6_ecb_encrypt(const unsigned char *pt, unsigned char *ct, symmetric_key *skey); int rc6_ecb_decrypt(const unsigned char *ct, unsigned char *pt, symmetric_key *skey); int rc6_test(void); void rc6_done(symmetric_key *skey); int rc6_keysize(int *keysize); extern const struct ltc_cipher_descriptor rc6_desc; #endif #ifdef LTC_RC2 int rc2_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey); int rc2_ecb_encrypt(const unsigned char *pt, unsigned char *ct, symmetric_key *skey); int rc2_ecb_decrypt(const unsigned char *ct, unsigned char *pt, symmetric_key *skey); int rc2_test(void); void rc2_done(symmetric_key *skey); int rc2_keysize(int *keysize); extern const struct ltc_cipher_descriptor rc2_desc; #endif #ifdef LTC_SAFERP int saferp_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey); int saferp_ecb_encrypt(const unsigned char *pt, unsigned char *ct, symmetric_key *skey); int saferp_ecb_decrypt(const unsigned char *ct, unsigned char *pt, symmetric_key *skey); int saferp_test(void); void saferp_done(symmetric_key *skey); int saferp_keysize(int *keysize); extern const struct ltc_cipher_descriptor saferp_desc; #endif #ifdef LTC_SAFER int safer_k64_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey); int safer_sk64_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey); int safer_k128_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey); int safer_sk128_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey); int safer_ecb_encrypt(const unsigned char *pt, unsigned char *ct, symmetric_key *key); int safer_ecb_decrypt(const unsigned char *ct, unsigned char *pt, symmetric_key *key); int safer_k64_test(void); int safer_sk64_test(void); int safer_sk128_test(void); void safer_done(symmetric_key *skey); int safer_64_keysize(int *keysize); int safer_128_keysize(int *keysize); extern const struct ltc_cipher_descriptor safer_k64_desc, safer_k128_desc, safer_sk64_desc, safer_sk128_desc; #endif #ifdef LTC_RIJNDAEL /* make aes an alias */ #define aes_setup rijndael_setup #define aes_ecb_encrypt rijndael_ecb_encrypt #define aes_ecb_decrypt rijndael_ecb_decrypt #define aes_test rijndael_test #define aes_done rijndael_done #define aes_keysize rijndael_keysize #define aes_enc_setup rijndael_enc_setup #define aes_enc_ecb_encrypt rijndael_enc_ecb_encrypt #define aes_enc_keysize rijndael_enc_keysize int rijndael_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey); int rijndael_ecb_encrypt(const unsigned char *pt, unsigned char *ct, symmetric_key *skey); int rijndael_ecb_decrypt(const unsigned char *ct, unsigned char *pt, symmetric_key *skey); int rijndael_test(void); void rijndael_done(symmetric_key *skey); int rijndael_keysize(int *keysize); int rijndael_enc_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey); int rijndael_enc_ecb_encrypt(const unsigned char *pt, unsigned char *ct, symmetric_key *skey); void rijndael_enc_done(symmetric_key *skey); int rijndael_enc_keysize(int *keysize); extern const struct ltc_cipher_descriptor rijndael_desc, aes_desc; extern const struct ltc_cipher_descriptor rijndael_enc_desc, aes_enc_desc; #endif #ifdef LTC_XTEA int xtea_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey); int xtea_ecb_encrypt(const unsigned char *pt, unsigned char *ct, symmetric_key *skey); int xtea_ecb_decrypt(const unsigned char *ct, unsigned char *pt, symmetric_key *skey); int xtea_test(void); void xtea_done(symmetric_key *skey); int xtea_keysize(int *keysize); extern const struct ltc_cipher_descriptor xtea_desc; #endif #ifdef LTC_TWOFISH int twofish_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey); int twofish_ecb_encrypt(const unsigned char *pt, unsigned char *ct, symmetric_key *skey); int twofish_ecb_decrypt(const unsigned char *ct, unsigned char *pt, symmetric_key *skey); int twofish_test(void); void twofish_done(symmetric_key *skey); int twofish_keysize(int *keysize); extern const struct ltc_cipher_descriptor twofish_desc; #endif #ifdef LTC_DES int des_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey); int des_ecb_encrypt(const unsigned char *pt, unsigned char *ct, symmetric_key *skey); int des_ecb_decrypt(const unsigned char *ct, unsigned char *pt, symmetric_key *skey); int des_test(void); void des_done(symmetric_key *skey); int des_keysize(int *keysize); int des3_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey); int des3_ecb_encrypt(const unsigned char *pt, unsigned char *ct, symmetric_key *skey); int des3_ecb_decrypt(const unsigned char *ct, unsigned char *pt, symmetric_key *skey); int des3_test(void); void des3_done(symmetric_key *skey); int des3_keysize(int *keysize); extern const struct ltc_cipher_descriptor des_desc, des3_desc; #endif #ifdef LTC_CAST5 int cast5_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey); int cast5_ecb_encrypt(const unsigned char *pt, unsigned char *ct, symmetric_key *skey); int cast5_ecb_decrypt(const unsigned char *ct, unsigned char *pt, symmetric_key *skey); int cast5_test(void); void cast5_done(symmetric_key *skey); int cast5_keysize(int *keysize); extern const struct ltc_cipher_descriptor cast5_desc; #endif #ifdef LTC_NOEKEON int noekeon_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey); int noekeon_ecb_encrypt(const unsigned char *pt, unsigned char *ct, symmetric_key *skey); int noekeon_ecb_decrypt(const unsigned char *ct, unsigned char *pt, symmetric_key *skey); int noekeon_test(void); void noekeon_done(symmetric_key *skey); int noekeon_keysize(int *keysize); extern const struct ltc_cipher_descriptor noekeon_desc; #endif #ifdef LTC_SKIPJACK int skipjack_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey); int skipjack_ecb_encrypt(const unsigned char *pt, unsigned char *ct, symmetric_key *skey); int skipjack_ecb_decrypt(const unsigned char *ct, unsigned char *pt, symmetric_key *skey); int skipjack_test(void); void skipjack_done(symmetric_key *skey); int skipjack_keysize(int *keysize); extern const struct ltc_cipher_descriptor skipjack_desc; #endif #ifdef LTC_KHAZAD int khazad_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey); int khazad_ecb_encrypt(const unsigned char *pt, unsigned char *ct, symmetric_key *skey); int khazad_ecb_decrypt(const unsigned char *ct, unsigned char *pt, symmetric_key *skey); int khazad_test(void); void khazad_done(symmetric_key *skey); int khazad_keysize(int *keysize); extern const struct ltc_cipher_descriptor khazad_desc; #endif #ifdef LTC_ANUBIS int anubis_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey); int anubis_ecb_encrypt(const unsigned char *pt, unsigned char *ct, symmetric_key *skey); int anubis_ecb_decrypt(const unsigned char *ct, unsigned char *pt, symmetric_key *skey); int anubis_test(void); void anubis_done(symmetric_key *skey); int anubis_keysize(int *keysize); extern const struct ltc_cipher_descriptor anubis_desc; #endif #ifdef LTC_KSEED int kseed_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey); int kseed_ecb_encrypt(const unsigned char *pt, unsigned char *ct, symmetric_key *skey); int kseed_ecb_decrypt(const unsigned char *ct, unsigned char *pt, symmetric_key *skey); int kseed_test(void); void kseed_done(symmetric_key *skey); int kseed_keysize(int *keysize); extern const struct ltc_cipher_descriptor kseed_desc; #endif #ifdef LTC_KASUMI int kasumi_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey); int kasumi_ecb_encrypt(const unsigned char *pt, unsigned char *ct, symmetric_key *skey); int kasumi_ecb_decrypt(const unsigned char *ct, unsigned char *pt, symmetric_key *skey); int kasumi_test(void); void kasumi_done(symmetric_key *skey); int kasumi_keysize(int *keysize); extern const struct ltc_cipher_descriptor kasumi_desc; #endif #ifdef LTC_MULTI2 int multi2_setup(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey); int multi2_ecb_encrypt(const unsigned char *pt, unsigned char *ct, symmetric_key *skey); int multi2_ecb_decrypt(const unsigned char *ct, unsigned char *pt, symmetric_key *skey); int multi2_test(void); void multi2_done(symmetric_key *skey); int multi2_keysize(int *keysize); extern const struct ltc_cipher_descriptor multi2_desc; #endif #ifdef LTC_ECB_MODE int ecb_start(int cipher, const unsigned char *key, int keylen, int num_rounds, symmetric_ECB *ecb); int ecb_encrypt(const unsigned char *pt, unsigned char *ct, unsigned long len, symmetric_ECB *ecb); int ecb_decrypt(const unsigned char *ct, unsigned char *pt, unsigned long len, symmetric_ECB *ecb); int ecb_done(symmetric_ECB *ecb); #endif #ifdef LTC_CFB_MODE int cfb_start(int cipher, const unsigned char *IV, const unsigned char *key, int keylen, int num_rounds, symmetric_CFB *cfb); int cfb_encrypt(const unsigned char *pt, unsigned char *ct, unsigned long len, symmetric_CFB *cfb); int cfb_decrypt(const unsigned char *ct, unsigned char *pt, unsigned long len, symmetric_CFB *cfb); int cfb_getiv(unsigned char *IV, unsigned long *len, symmetric_CFB *cfb); int cfb_setiv(const unsigned char *IV, unsigned long len, symmetric_CFB *cfb); int cfb_done(symmetric_CFB *cfb); #endif #ifdef LTC_OFB_MODE int ofb_start(int cipher, const unsigned char *IV, const unsigned char *key, int keylen, int num_rounds, symmetric_OFB *ofb); int ofb_encrypt(const unsigned char *pt, unsigned char *ct, unsigned long len, symmetric_OFB *ofb); int ofb_decrypt(const unsigned char *ct, unsigned char *pt, unsigned long len, symmetric_OFB *ofb); int ofb_getiv(unsigned char *IV, unsigned long *len, symmetric_OFB *ofb); int ofb_setiv(const unsigned char *IV, unsigned long len, symmetric_OFB *ofb); int ofb_done(symmetric_OFB *ofb); #endif #ifdef LTC_CBC_MODE int cbc_start(int cipher, const unsigned char *IV, const unsigned char *key, int keylen, int num_rounds, symmetric_CBC *cbc); int cbc_encrypt(const unsigned char *pt, unsigned char *ct, unsigned long len, symmetric_CBC *cbc); int cbc_decrypt(const unsigned char *ct, unsigned char *pt, unsigned long len, symmetric_CBC *cbc); int cbc_getiv(unsigned char *IV, unsigned long *len, symmetric_CBC *cbc); int cbc_setiv(const unsigned char *IV, unsigned long len, symmetric_CBC *cbc); int cbc_done(symmetric_CBC *cbc); #endif #ifdef LTC_CTR_MODE #define CTR_COUNTER_LITTLE_ENDIAN 0x0000 #define CTR_COUNTER_BIG_ENDIAN 0x1000 #define LTC_CTR_RFC3686 0x2000 int ctr_start(int cipher, const unsigned char *IV, const unsigned char *key, int keylen, int num_rounds, int ctr_mode, symmetric_CTR *ctr); int ctr_encrypt(const unsigned char *pt, unsigned char *ct, unsigned long len, symmetric_CTR *ctr); int ctr_decrypt(const unsigned char *ct, unsigned char *pt, unsigned long len, symmetric_CTR *ctr); int ctr_getiv(unsigned char *IV, unsigned long *len, symmetric_CTR *ctr); int ctr_setiv(const unsigned char *IV, unsigned long len, symmetric_CTR *ctr); int ctr_done(symmetric_CTR *ctr); int ctr_test(void); #endif #ifdef LTC_LRW_MODE #define LRW_ENCRYPT 0 #define LRW_DECRYPT 1 int lrw_start(int cipher, const unsigned char *IV, const unsigned char *key, int keylen, const unsigned char *tweak, int num_rounds, symmetric_LRW *lrw); int lrw_encrypt(const unsigned char *pt, unsigned char *ct, unsigned long len, symmetric_LRW *lrw); int lrw_decrypt(const unsigned char *ct, unsigned char *pt, unsigned long len, symmetric_LRW *lrw); int lrw_getiv(unsigned char *IV, unsigned long *len, symmetric_LRW *lrw); int lrw_setiv(const unsigned char *IV, unsigned long len, symmetric_LRW *lrw); int lrw_done(symmetric_LRW *lrw); int lrw_test(void); /* don't call */ int lrw_process(const unsigned char *pt, unsigned char *ct, unsigned long len, int mode, symmetric_LRW *lrw); #endif #ifdef LTC_F8_MODE int f8_start(int cipher, const unsigned char *IV, const unsigned char *key, int keylen, const unsigned char *salt_key, int skeylen, int num_rounds, symmetric_F8 *f8); int f8_encrypt(const unsigned char *pt, unsigned char *ct, unsigned long len, symmetric_F8 *f8); int f8_decrypt(const unsigned char *ct, unsigned char *pt, unsigned long len, symmetric_F8 *f8); int f8_getiv(unsigned char *IV, unsigned long *len, symmetric_F8 *f8); int f8_setiv(const unsigned char *IV, unsigned long len, symmetric_F8 *f8); int f8_done(symmetric_F8 *f8); int f8_test_mode(void); #endif #ifdef LTC_XTS_MODE typedef struct { symmetric_key key1, key2; int cipher; } symmetric_xts; int xts_start(int cipher, const unsigned char *key1, const unsigned char *key2, unsigned long keylen, int num_rounds, symmetric_xts *xts); int xts_encrypt( const unsigned char *pt, unsigned long ptlen, unsigned char *ct, const unsigned char *tweak, symmetric_xts *xts); int xts_decrypt( const unsigned char *ct, unsigned long ptlen, unsigned char *pt, const unsigned char *tweak, symmetric_xts *xts); void xts_done(symmetric_xts *xts); int xts_test(void); void xts_mult_x(unsigned char *I); #endif int find_cipher(const char *name); int find_cipher_any(const char *name, int blocklen, int keylen); int find_cipher_id(unsigned char ID); int register_cipher(const struct ltc_cipher_descriptor *cipher); int unregister_cipher(const struct ltc_cipher_descriptor *cipher); int cipher_is_valid(int idx); LTC_MUTEX_PROTO(ltc_cipher_mutex) /* $Source: /cvs/libtom/libtomcrypt/src/headers/tomcrypt_cipher.h,v $ */ /* $Revision: 1.54 $ */ /* $Date: 2007/05/12 14:37:41 $ */ #define LTC_SHA1 /* ---- HASH FUNCTIONS ---- */ #ifdef LTC_SHA512 struct sha512_state { ulong64 length, state[8]; unsigned long curlen; unsigned char buf[128]; }; #endif #ifdef LTC_SHA256 struct sha256_state { ulong64 length; ulong32 state[8], curlen; unsigned char buf[64]; }; #endif #ifdef LTC_SHA1 struct sha1_state { ulong64 length; ulong32 state[5], curlen; unsigned char buf[64]; }; #endif #ifdef LTC_MD5 struct md5_state { ulong64 length; ulong32 state[4], curlen; unsigned char buf[64]; }; #endif #ifdef LTC_MD4 struct md4_state { ulong64 length; ulong32 state[4], curlen; unsigned char buf[64]; }; #endif #ifdef LTC_TIGER struct tiger_state { ulong64 state[3], length; unsigned long curlen; unsigned char buf[64]; }; #endif #ifdef LTC_MD2 struct md2_state { unsigned char chksum[16], X[48], buf[16]; unsigned long curlen; }; #endif #ifdef LTC_RIPEMD128 struct rmd128_state { ulong64 length; unsigned char buf[64]; ulong32 curlen, state[4]; }; #endif #ifdef LTC_RIPEMD160 struct rmd160_state { ulong64 length; unsigned char buf[64]; ulong32 curlen, state[5]; }; #endif #ifdef LTC_RIPEMD256 struct rmd256_state { ulong64 length; unsigned char buf[64]; ulong32 curlen, state[8]; }; #endif #ifdef LTC_RIPEMD320 struct rmd320_state { ulong64 length; unsigned char buf[64]; ulong32 curlen, state[10]; }; #endif #ifdef LTC_WHIRLPOOL struct whirlpool_state { ulong64 length, state[8]; unsigned char buf[64]; ulong32 curlen; }; #endif #ifdef LTC_CHC_HASH struct chc_state { ulong64 length; unsigned char state[MAXBLOCKSIZE], buf[MAXBLOCKSIZE]; ulong32 curlen; }; #endif typedef union Hash_state { char dummy[1]; #ifdef LTC_CHC_HASH struct chc_state chc; #endif #ifdef LTC_WHIRLPOOL struct whirlpool_state whirlpool; #endif #ifdef LTC_SHA512 struct sha512_state sha512; #endif #ifdef LTC_SHA256 struct sha256_state sha256; #endif #ifdef LTC_SHA1 struct sha1_state sha1; #endif #ifdef LTC_MD5 struct md5_state md5; #endif #ifdef LTC_MD4 struct md4_state md4; #endif #ifdef LTC_MD2 struct md2_state md2; #endif #ifdef LTC_TIGER struct tiger_state tiger; #endif #ifdef LTC_RIPEMD128 struct rmd128_state rmd128; #endif #ifdef LTC_RIPEMD160 struct rmd160_state rmd160; #endif #ifdef LTC_RIPEMD256 struct rmd256_state rmd256; #endif #ifdef LTC_RIPEMD320 struct rmd320_state rmd320; #endif void *data; } hash_state; /** hash descriptor */ extern struct ltc_hash_descriptor { /** name of hash */ char *name; /** internal ID */ unsigned char ID; /** Size of digest in octets */ unsigned long hashsize; /** Input block size in octets */ unsigned long blocksize; /** ASN.1 OID */ unsigned long OID[16]; /** Length of DER encoding */ unsigned long OIDlen; /** Init a hash state @param hash The hash to initialize @return CRYPT_OK if successful */ int (*init)(hash_state *hash); /** Process a block of data @param hash The hash state @param in The data to hash @param inlen The length of the data (octets) @return CRYPT_OK if successful */ int (*process)(hash_state *hash, const unsigned char *in, unsigned long inlen); /** Produce the digest and store it @param hash The hash state @param out [out] The destination of the digest @return CRYPT_OK if successful */ int (*done)(hash_state *hash, unsigned char *out); /** Self-test @return CRYPT_OK if successful, CRYPT_NOP if self-tests have been disabled */ int (*test)(void); /* accelerated hmac callback: if you need to-do multiple packets just use the generic hmac_memory and provide a hash callback */ int (*hmac_block)(const unsigned char *key, unsigned long keylen, const unsigned char *in, unsigned long inlen, unsigned char *out, unsigned long *outlen); } hash_descriptor[]; #ifdef LTC_CHC_HASH int chc_register(int cipher); int chc_init(hash_state *md); int chc_process(hash_state *md, const unsigned char *in, unsigned long inlen); int chc_done(hash_state *md, unsigned char *hash); int chc_test(void); extern const struct ltc_hash_descriptor chc_desc; #endif #ifdef LTC_WHIRLPOOL int whirlpool_init(hash_state *md); int whirlpool_process(hash_state *md, const unsigned char *in, unsigned long inlen); int whirlpool_done(hash_state *md, unsigned char *hash); int whirlpool_test(void); extern const struct ltc_hash_descriptor whirlpool_desc; #endif #ifdef LTC_SHA512 int sha512_init(hash_state *md); int sha512_process(hash_state *md, const unsigned char *in, unsigned long inlen); int sha512_done(hash_state *md, unsigned char *hash); int sha512_test(void); extern const struct ltc_hash_descriptor sha512_desc; #endif #ifdef LTC_SHA384 #ifndef LTC_SHA512 #error LTC_SHA512 is required for LTC_SHA384 #endif int sha384_init(hash_state *md); #define sha384_process sha512_process int sha384_done(hash_state *md, unsigned char *hash); int sha384_test(void); extern const struct ltc_hash_descriptor sha384_desc; #endif #ifdef LTC_SHA256 int sha256_init(hash_state *md); int sha256_process(hash_state *md, const unsigned char *in, unsigned long inlen); int sha256_done(hash_state *md, unsigned char *hash); int sha256_test(void); extern const struct ltc_hash_descriptor sha256_desc; #ifdef LTC_SHA224 #ifndef LTC_SHA256 #error LTC_SHA256 is required for LTC_SHA224 #endif int sha224_init(hash_state *md); #define sha224_process sha256_process int sha224_done(hash_state *md, unsigned char *hash); int sha224_test(void); extern const struct ltc_hash_descriptor sha224_desc; #endif #endif #ifdef LTC_SHA1 int sha1_init(hash_state *md); int sha1_process(hash_state *md, const unsigned char *in, unsigned long inlen); int sha1_done(hash_state *md, unsigned char *hash); int sha1_test(void); extern const struct ltc_hash_descriptor sha1_desc; #endif #ifdef LTC_MD5 int md5_init(hash_state *md); int md5_process(hash_state *md, const unsigned char *in, unsigned long inlen); int md5_done(hash_state *md, unsigned char *hash); int md5_test(void); extern const struct ltc_hash_descriptor md5_desc; #endif #ifdef LTC_MD4 int md4_init(hash_state *md); int md4_process(hash_state *md, const unsigned char *in, unsigned long inlen); int md4_done(hash_state *md, unsigned char *hash); int md4_test(void); extern const struct ltc_hash_descriptor md4_desc; #endif #ifdef LTC_MD2 int md2_init(hash_state *md); int md2_process(hash_state *md, const unsigned char *in, unsigned long inlen); int md2_done(hash_state *md, unsigned char *hash); int md2_test(void); extern const struct ltc_hash_descriptor md2_desc; #endif #ifdef LTC_TIGER int tiger_init(hash_state *md); int tiger_process(hash_state *md, const unsigned char *in, unsigned long inlen); int tiger_done(hash_state *md, unsigned char *hash); int tiger_test(void); extern const struct ltc_hash_descriptor tiger_desc; #endif #ifdef LTC_RIPEMD128 int rmd128_init(hash_state *md); int rmd128_process(hash_state *md, const unsigned char *in, unsigned long inlen); int rmd128_done(hash_state *md, unsigned char *hash); int rmd128_test(void); extern const struct ltc_hash_descriptor rmd128_desc; #endif #ifdef LTC_RIPEMD160 int rmd160_init(hash_state *md); int rmd160_process(hash_state *md, const unsigned char *in, unsigned long inlen); int rmd160_done(hash_state *md, unsigned char *hash); int rmd160_test(void); extern const struct ltc_hash_descriptor rmd160_desc; #endif #ifdef LTC_RIPEMD256 int rmd256_init(hash_state *md); int rmd256_process(hash_state *md, const unsigned char *in, unsigned long inlen); int rmd256_done(hash_state *md, unsigned char *hash); int rmd256_test(void); extern const struct ltc_hash_descriptor rmd256_desc; #endif #ifdef LTC_RIPEMD320 int rmd320_init(hash_state *md); int rmd320_process(hash_state *md, const unsigned char *in, unsigned long inlen); int rmd320_done(hash_state *md, unsigned char *hash); int rmd320_test(void); extern const struct ltc_hash_descriptor rmd320_desc; #endif int find_hash(const char *name); int find_hash_id(unsigned char ID); int find_hash_oid(const unsigned long *ID, unsigned long IDlen); int find_hash_any(const char *name, int digestlen); int register_hash(const struct ltc_hash_descriptor *hash); int unregister_hash(const struct ltc_hash_descriptor *hash); int hash_is_valid(int idx); LTC_MUTEX_PROTO(ltc_hash_mutex) int hash_memory(int hash, const unsigned char *in, unsigned long inlen, unsigned char *out, unsigned long *outlen); int hash_memory_multi(int hash, unsigned char *out, unsigned long *outlen, const unsigned char *in, unsigned long inlen, ...); int hash_filehandle(int hash, FILE *in, unsigned char *out, unsigned long *outlen); int hash_file(int hash, const char *fname, unsigned char *out, unsigned long *outlen); /* a simple macro for making hash "process" functions */ #define HASH_PROCESS(func_name, compress_name, state_var, block_size) \ int func_name(hash_state * md, const unsigned char *in, unsigned long inlen) \ { \ unsigned long n; \ int err; \ LTC_ARGCHK(md != NULL); \ LTC_ARGCHK(in != NULL); \ if (md->state_var.curlen > sizeof(md->state_var.buf)) { \ return CRYPT_INVALID_ARG; \ } \ while (inlen > 0) { \ if (md->state_var.curlen == 0 && inlen >= block_size) { \ if ((err = compress_name(md, (unsigned char *)in)) != CRYPT_OK) { \ return err; \ } \ md->state_var.length += block_size * 8; \ in += block_size; \ inlen -= block_size; \ } else { \ n = MIN(inlen, (block_size - md->state_var.curlen)); \ memcpy(md->state_var.buf + md->state_var.curlen, in, (size_t)n); \ md->state_var.curlen += n; \ in += n; \ inlen -= n; \ if (md->state_var.curlen == block_size) { \ if ((err = compress_name(md, md->state_var.buf)) != CRYPT_OK) { \ return err; \ } \ md->state_var.length += 8 * block_size; \ md->state_var.curlen = 0; \ } \ } \ } \ return CRYPT_OK; \ } /* $Source: /cvs/libtom/libtomcrypt/src/headers/tomcrypt_hash.h,v $ */ /* $Revision: 1.22 $ */ /* $Date: 2007/05/12 14:32:35 $ */ #ifdef LTC_HMAC typedef struct Hmac_state { hash_state md; int hash; hash_state hashstate; unsigned char *key; } hmac_state; int hmac_init(hmac_state *hmac, int hash, const unsigned char *key, unsigned long keylen); int hmac_process(hmac_state *hmac, const unsigned char *in, unsigned long inlen); int hmac_done(hmac_state *hmac, unsigned char *out, unsigned long *outlen); int hmac_test(void); int hmac_memory(int hash, const unsigned char *key, unsigned long keylen, const unsigned char *in, unsigned long inlen, unsigned char *out, unsigned long *outlen); int hmac_memory_multi(int hash, const unsigned char *key, unsigned long keylen, unsigned char *out, unsigned long *outlen, const unsigned char *in, unsigned long inlen, ...); int hmac_file(int hash, const char *fname, const unsigned char *key, unsigned long keylen, unsigned char *dst, unsigned long *dstlen); #endif #ifdef LTC_OMAC typedef struct { int cipher_idx, buflen, blklen; unsigned char block[MAXBLOCKSIZE], prev[MAXBLOCKSIZE], Lu[2][MAXBLOCKSIZE]; symmetric_key key; } omac_state; int omac_init(omac_state *omac, int cipher, const unsigned char *key, unsigned long keylen); int omac_process(omac_state *omac, const unsigned char *in, unsigned long inlen); int omac_done(omac_state *omac, unsigned char *out, unsigned long *outlen); int omac_memory(int cipher, const unsigned char *key, unsigned long keylen, const unsigned char *in, unsigned long inlen, unsigned char *out, unsigned long *outlen); int omac_memory_multi(int cipher, const unsigned char *key, unsigned long keylen, unsigned char *out, unsigned long *outlen, const unsigned char *in, unsigned long inlen, ...); int omac_file(int cipher, const unsigned char *key, unsigned long keylen, const char *filename, unsigned char *out, unsigned long *outlen); int omac_test(void); #endif /* LTC_OMAC */ #ifdef LTC_PMAC typedef struct { unsigned char Ls[32][MAXBLOCKSIZE], /* L shifted by i bits to the left */ Li[MAXBLOCKSIZE], /* value of Li [current value, we calc from previous recall] */ Lr[MAXBLOCKSIZE], /* L * x^-1 */ block[MAXBLOCKSIZE], /* currently accumulated block */ checksum[MAXBLOCKSIZE]; /* current checksum */ symmetric_key key; /* scheduled key for cipher */ unsigned long block_index; /* index # for current block */ int cipher_idx, /* cipher idx */ block_len, /* length of block */ buflen; /* number of bytes in the buffer */ } pmac_state; int pmac_init(pmac_state *pmac, int cipher, const unsigned char *key, unsigned long keylen); int pmac_process(pmac_state *pmac, const unsigned char *in, unsigned long inlen); int pmac_done(pmac_state *pmac, unsigned char *out, unsigned long *outlen); int pmac_memory(int cipher, const unsigned char *key, unsigned long keylen, const unsigned char *msg, unsigned long msglen, unsigned char *out, unsigned long *outlen); int pmac_memory_multi(int cipher, const unsigned char *key, unsigned long keylen, unsigned char *out, unsigned long *outlen, const unsigned char *in, unsigned long inlen, ...); int pmac_file(int cipher, const unsigned char *key, unsigned long keylen, const char *filename, unsigned char *out, unsigned long *outlen); int pmac_test(void); /* internal functions */ int pmac_ntz(unsigned long x); void pmac_shift_xor(pmac_state *pmac); #endif /* PMAC */ #ifdef LTC_EAX_MODE #if !(defined(LTC_OMAC) && defined(LTC_CTR_MODE)) #error LTC_EAX_MODE requires LTC_OMAC and CTR #endif typedef struct { unsigned char N[MAXBLOCKSIZE]; symmetric_CTR ctr; omac_state headeromac, ctomac; } eax_state; int eax_init(eax_state *eax, int cipher, const unsigned char *key, unsigned long keylen, const unsigned char *nonce, unsigned long noncelen, const unsigned char *header, unsigned long headerlen); int eax_encrypt(eax_state *eax, const unsigned char *pt, unsigned char *ct, unsigned long length); int eax_decrypt(eax_state *eax, const unsigned char *ct, unsigned char *pt, unsigned long length); int eax_addheader(eax_state *eax, const unsigned char *header, unsigned long length); int eax_done(eax_state *eax, unsigned char *tag, unsigned long *taglen); int eax_encrypt_authenticate_memory(int cipher, const unsigned char *key, unsigned long keylen, const unsigned char *nonce, unsigned long noncelen, const unsigned char *header, unsigned long headerlen, const unsigned char *pt, unsigned long ptlen, unsigned char *ct, unsigned char *tag, unsigned long *taglen); int eax_decrypt_verify_memory(int cipher, const unsigned char *key, unsigned long keylen, const unsigned char *nonce, unsigned long noncelen, const unsigned char *header, unsigned long headerlen, const unsigned char *ct, unsigned long ctlen, unsigned char *pt, unsigned char *tag, unsigned long taglen, int *stat); int eax_test(void); #endif /* EAX MODE */ #ifdef LTC_OCB_MODE typedef struct { unsigned char L[MAXBLOCKSIZE], /* L value */ Ls[32][MAXBLOCKSIZE], /* L shifted by i bits to the left */ Li[MAXBLOCKSIZE], /* value of Li [current value, we calc from previous recall] */ Lr[MAXBLOCKSIZE], /* L * x^-1 */ R[MAXBLOCKSIZE], /* R value */ checksum[MAXBLOCKSIZE]; /* current checksum */ symmetric_key key; /* scheduled key for cipher */ unsigned long block_index; /* index # for current block */ int cipher, /* cipher idx */ block_len; /* length of block */ } ocb_state; int ocb_init(ocb_state *ocb, int cipher, const unsigned char *key, unsigned long keylen, const unsigned char *nonce); int ocb_encrypt(ocb_state *ocb, const unsigned char *pt, unsigned char *ct); int ocb_decrypt(ocb_state *ocb, const unsigned char *ct, unsigned char *pt); int ocb_done_encrypt(ocb_state *ocb, const unsigned char *pt, unsigned long ptlen, unsigned char *ct, unsigned char *tag, unsigned long *taglen); int ocb_done_decrypt(ocb_state *ocb, const unsigned char *ct, unsigned long ctlen, unsigned char *pt, const unsigned char *tag, unsigned long taglen, int *stat); int ocb_encrypt_authenticate_memory(int cipher, const unsigned char *key, unsigned long keylen, const unsigned char *nonce, const unsigned char *pt, unsigned long ptlen, unsigned char *ct, unsigned char *tag, unsigned long *taglen); int ocb_decrypt_verify_memory(int cipher, const unsigned char *key, unsigned long keylen, const unsigned char *nonce, const unsigned char *ct, unsigned long ctlen, unsigned char *pt, const unsigned char *tag, unsigned long taglen, int *stat); int ocb_test(void); /* internal functions */ void ocb_shift_xor(ocb_state *ocb, unsigned char *Z); int ocb_ntz(unsigned long x); int s_ocb_done(ocb_state *ocb, const unsigned char *pt, unsigned long ptlen, unsigned char *ct, unsigned char *tag, unsigned long *taglen, int mode); #endif /* LTC_OCB_MODE */ #ifdef LTC_CCM_MODE #define CCM_ENCRYPT 0 #define CCM_DECRYPT 1 int ccm_memory(int cipher, const unsigned char *key, unsigned long keylen, symmetric_key *uskey, const unsigned char *nonce, unsigned long noncelen, const unsigned char *header, unsigned long headerlen, unsigned char *pt, unsigned long ptlen, unsigned char *ct, unsigned char *tag, unsigned long *taglen, int direction); int ccm_test(void); #endif /* LTC_CCM_MODE */ #if defined(LRW_MODE) || defined(LTC_GCM_MODE) void gcm_gf_mult(const unsigned char *a, const unsigned char *b, unsigned char *c); #endif /* table shared between GCM and LRW */ #if defined(LTC_GCM_TABLES) || defined(LRW_TABLES) || ((defined(LTC_GCM_MODE) || defined(LTC_GCM_MODE)) && defined(LTC_FAST)) extern const unsigned char gcm_shift_table[]; #endif #ifdef LTC_GCM_MODE #define GCM_ENCRYPT 0 #define GCM_DECRYPT 1 #define LTC_GCM_MODE_IV 0 #define LTC_GCM_MODE_AAD 1 #define LTC_GCM_MODE_TEXT 2 typedef struct { symmetric_key K; unsigned char H[16], /* multiplier */ X[16], /* accumulator */ Y[16], /* counter */ Y_0[16], /* initial counter */ buf[16]; /* buffer for stuff */ int cipher, /* which cipher */ ivmode, /* Which mode is the IV in? */ mode, /* mode the GCM code is in */ buflen; /* length of data in buf */ ulong64 totlen, /* 64-bit counter used for IV and AAD */ pttotlen; /* 64-bit counter for the PT */ #ifdef LTC_GCM_TABLES unsigned char PC[16][256][16] /* 16 tables of 8x128 */ #ifdef LTC_GCM_TABLES_SSE2 __attribute__ ((aligned(16))) #endif ; #endif } gcm_state; void gcm_mult_h(gcm_state *gcm, unsigned char *I); int gcm_init(gcm_state *gcm, int cipher, const unsigned char *key, int keylen); int gcm_reset(gcm_state *gcm); int gcm_add_iv(gcm_state *gcm, const unsigned char *IV, unsigned long IVlen); int gcm_add_aad(gcm_state *gcm, const unsigned char *adata, unsigned long adatalen); int gcm_process(gcm_state *gcm, unsigned char *pt, unsigned long ptlen, unsigned char *ct, int direction); int gcm_done(gcm_state *gcm, unsigned char *tag, unsigned long *taglen); int gcm_memory(int cipher, const unsigned char *key, unsigned long keylen, const unsigned char *IV, unsigned long IVlen, const unsigned char *adata, unsigned long adatalen, unsigned char *pt, unsigned long ptlen, unsigned char *ct, unsigned char *tag, unsigned long *taglen, int direction); int gcm_test(void); #endif /* LTC_GCM_MODE */ #ifdef LTC_PELICAN typedef struct pelican_state { symmetric_key K; unsigned char state[16]; int buflen; } pelican_state; int pelican_init(pelican_state *pelmac, const unsigned char *key, unsigned long keylen); int pelican_process(pelican_state *pelmac, const unsigned char *in, unsigned long inlen); int pelican_done(pelican_state *pelmac, unsigned char *out); int pelican_test(void); int pelican_memory(const unsigned char *key, unsigned long keylen, const unsigned char *in, unsigned long inlen, unsigned char *out); #endif #ifdef LTC_XCBC /* add this to "keylen" to xcbc_init to use a pure three-key XCBC MAC */ #define LTC_XCBC_PURE 0x8000UL typedef struct { unsigned char K[3][MAXBLOCKSIZE], IV[MAXBLOCKSIZE]; symmetric_key key; int cipher, buflen, blocksize; } xcbc_state; int xcbc_init(xcbc_state *xcbc, int cipher, const unsigned char *key, unsigned long keylen); int xcbc_process(xcbc_state *xcbc, const unsigned char *in, unsigned long inlen); int xcbc_done(xcbc_state *xcbc, unsigned char *out, unsigned long *outlen); int xcbc_memory(int cipher, const unsigned char *key, unsigned long keylen, const unsigned char *in, unsigned long inlen, unsigned char *out, unsigned long *outlen); int xcbc_memory_multi(int cipher, const unsigned char *key, unsigned long keylen, unsigned char *out, unsigned long *outlen, const unsigned char *in, unsigned long inlen, ...); int xcbc_file(int cipher, const unsigned char *key, unsigned long keylen, const char *filename, unsigned char *out, unsigned long *outlen); int xcbc_test(void); #endif #ifdef LTC_F9_MODE typedef struct { unsigned char akey[MAXBLOCKSIZE], ACC[MAXBLOCKSIZE], IV[MAXBLOCKSIZE]; symmetric_key key; int cipher, buflen, keylen, blocksize; } f9_state; int f9_init(f9_state *f9, int cipher, const unsigned char *key, unsigned long keylen); int f9_process(f9_state *f9, const unsigned char *in, unsigned long inlen); int f9_done(f9_state *f9, unsigned char *out, unsigned long *outlen); int f9_memory(int cipher, const unsigned char *key, unsigned long keylen, const unsigned char *in, unsigned long inlen, unsigned char *out, unsigned long *outlen); int f9_memory_multi(int cipher, const unsigned char *key, unsigned long keylen, unsigned char *out, unsigned long *outlen, const unsigned char *in, unsigned long inlen, ...); int f9_file(int cipher, const unsigned char *key, unsigned long keylen, const char *filename, unsigned char *out, unsigned long *outlen); int f9_test(void); #endif /* $Source: /cvs/libtom/libtomcrypt/src/headers/tomcrypt_mac.h,v $ */ /* $Revision: 1.23 $ */ /* $Date: 2007/05/12 14:37:41 $ */ /* ---- PRNG Stuff ---- */ #ifdef LTC_YARROW struct yarrow_prng { int cipher, hash; unsigned char pool[MAXBLOCKSIZE]; symmetric_CTR ctr; LTC_MUTEX_TYPE(prng_lock) }; #endif #ifdef LTC_RC4 struct rc4_prng { int x, y; unsigned char buf[256]; }; #endif #ifdef LTC_FORTUNA struct fortuna_prng { hash_state pool[LTC_FORTUNA_POOLS]; /* the pools */ symmetric_key skey; unsigned char K[32], /* the current key */ IV[16]; /* IV for CTR mode */ unsigned long pool_idx, /* current pool we will add to */ pool0_len, /* length of 0'th pool */ wd; ulong64 reset_cnt; /* number of times we have reset */ LTC_MUTEX_TYPE(prng_lock) }; #endif #ifdef LTC_SOBER128 struct sober128_prng { ulong32 R[17], /* Working storage for the shift register */ initR[17], /* saved register contents */ konst, /* key dependent constant */ sbuf; /* partial word encryption buffer */ int nbuf, /* number of part-word stream bits buffered */ flag, /* first add_entropy call or not? */ set; /* did we call add_entropy to set key? */ }; #endif typedef union Prng_state { char dummy[1]; #ifdef LTC_YARROW struct yarrow_prng yarrow; #endif #ifdef LTC_RC4 struct rc4_prng rc4; #endif #ifdef LTC_FORTUNA struct fortuna_prng fortuna; #endif #ifdef LTC_SOBER128 struct sober128_prng sober128; #endif } prng_state; /** PRNG descriptor */ extern struct ltc_prng_descriptor { /** Name of the PRNG */ char *name; /** size in bytes of exported state */ int export_size; /** Start a PRNG state @param prng [out] The state to initialize @return CRYPT_OK if successful */ int (*start)(prng_state *prng); /** Add entropy to the PRNG @param in The entropy @param inlen Length of the entropy (octets)\ @param prng The PRNG state @return CRYPT_OK if successful */ int (*add_entropy)(const unsigned char *in, unsigned long inlen, prng_state *prng); /** Ready a PRNG state to read from @param prng The PRNG state to ready @return CRYPT_OK if successful */ int (*ready)(prng_state *prng); /** Read from the PRNG @param out [out] Where to store the data @param outlen Length of data desired (octets) @param prng The PRNG state to read from @return Number of octets read */ unsigned long (*read)(unsigned char *out, unsigned long outlen, prng_state *prng); /** Terminate a PRNG state @param prng The PRNG state to terminate @return CRYPT_OK if successful */ int (*done)(prng_state *prng); /** Export a PRNG state @param out [out] The destination for the state @param outlen [in/out] The max size and resulting size of the PRNG state @param prng The PRNG to export @return CRYPT_OK if successful */ int (*pexport)(unsigned char *out, unsigned long *outlen, prng_state *prng); /** Import a PRNG state @param in The data to import @param inlen The length of the data to import (octets) @param prng The PRNG to initialize/import @return CRYPT_OK if successful */ int (*pimport)(const unsigned char *in, unsigned long inlen, prng_state *prng); /** Self-test the PRNG @return CRYPT_OK if successful, CRYPT_NOP if self-testing has been disabled */ int (*test)(void); } prng_descriptor[]; #ifdef LTC_YARROW int yarrow_start(prng_state *prng); int yarrow_add_entropy(const unsigned char *in, unsigned long inlen, prng_state *prng); int yarrow_ready(prng_state *prng); unsigned long yarrow_read(unsigned char *out, unsigned long outlen, prng_state *prng); int yarrow_done(prng_state *prng); int yarrow_export(unsigned char *out, unsigned long *outlen, prng_state *prng); int yarrow_import(const unsigned char *in, unsigned long inlen, prng_state *prng); int yarrow_test(void); extern const struct ltc_prng_descriptor yarrow_desc; #endif #ifdef LTC_FORTUNA int fortuna_start(prng_state *prng); int fortuna_add_entropy(const unsigned char *in, unsigned long inlen, prng_state *prng); int fortuna_ready(prng_state *prng); unsigned long fortuna_read(unsigned char *out, unsigned long outlen, prng_state *prng); int fortuna_done(prng_state *prng); int fortuna_export(unsigned char *out, unsigned long *outlen, prng_state *prng); int fortuna_import(const unsigned char *in, unsigned long inlen, prng_state *prng); int fortuna_test(void); extern const struct ltc_prng_descriptor fortuna_desc; #endif #ifdef LTC_RC4 int rc4_start(prng_state *prng); int rc4_add_entropy(const unsigned char *in, unsigned long inlen, prng_state *prng); int rc4_ready(prng_state *prng); unsigned long rc4_read(unsigned char *out, unsigned long outlen, prng_state *prng); int rc4_done(prng_state *prng); int rc4_export(unsigned char *out, unsigned long *outlen, prng_state *prng); int rc4_import(const unsigned char *in, unsigned long inlen, prng_state *prng); int rc4_test(void); extern const struct ltc_prng_descriptor rc4_desc; #endif #ifdef LTC_SPRNG int sprng_start(prng_state *prng); int sprng_add_entropy(const unsigned char *in, unsigned long inlen, prng_state *prng); int sprng_ready(prng_state *prng); unsigned long sprng_read(unsigned char *out, unsigned long outlen, prng_state *prng); int sprng_done(prng_state *prng); int sprng_export(unsigned char *out, unsigned long *outlen, prng_state *prng); int sprng_import(const unsigned char *in, unsigned long inlen, prng_state *prng); int sprng_test(void); extern const struct ltc_prng_descriptor sprng_desc; #endif #ifdef LTC_SOBER128 int sober128_start(prng_state *prng); int sober128_add_entropy(const unsigned char *in, unsigned long inlen, prng_state *prng); int sober128_ready(prng_state *prng); unsigned long sober128_read(unsigned char *out, unsigned long outlen, prng_state *prng); int sober128_done(prng_state *prng); int sober128_export(unsigned char *out, unsigned long *outlen, prng_state *prng); int sober128_import(const unsigned char *in, unsigned long inlen, prng_state *prng); int sober128_test(void); extern const struct ltc_prng_descriptor sober128_desc; #endif int find_prng(const char *name); int register_prng(const struct ltc_prng_descriptor *prng); int unregister_prng(const struct ltc_prng_descriptor *prng); int prng_is_valid(int idx); LTC_MUTEX_PROTO(ltc_prng_mutex) /* Slow RNG you **might** be able to use to seed a PRNG with. Be careful as this * might not work on all platforms as planned */ unsigned long rng_get_bytes(unsigned char *out, unsigned long outlen, void ( *callback)(void)); int rng_make_prng(int bits, int wprng, prng_state *prng, void (*callback)(void)); /* $Source: /cvs/libtom/libtomcrypt/src/headers/tomcrypt_prng.h,v $ */ /* $Revision: 1.9 $ */ /* $Date: 2007/05/12 14:32:35 $ */ /* ---- NUMBER THEORY ---- */ enum { PK_PUBLIC =0, PK_PRIVATE=1 }; int rand_prime(void *N, long len, prng_state *prng, int wprng); /* ---- RSA ---- */ #ifdef LTC_MRSA /* Min and Max RSA key sizes (in bits) */ #define MIN_RSA_SIZE 1024 #define MAX_RSA_SIZE 4096 /** RSA LTC_PKCS style key */ typedef struct Rsa_key { /** Type of key, PK_PRIVATE or PK_PUBLIC */ int type; /** The public exponent */ void *e; /** The private exponent */ void *d; /** The modulus */ void *N; /** The p factor of N */ void *p; /** The q factor of N */ void *q; /** The 1/q mod p CRT param */ void *qP; /** The d mod (p - 1) CRT param */ void *dP; /** The d mod (q - 1) CRT param */ void *dQ; } rsa_key; int rsa_make_key(prng_state *prng, int wprng, int size, long e, rsa_key *key); int rsa_exptmod(const unsigned char *in, unsigned long inlen, unsigned char *out, unsigned long *outlen, int which, rsa_key *key); void rsa_free(rsa_key *key); /* These use LTC_PKCS #1 v2.0 padding */ #define rsa_encrypt_key(_in, _inlen, _out, _outlen, _lparam, _lparamlen, _prng, _prng_idx, _hash_idx, _key) \ rsa_encrypt_key_ex(_in, _inlen, _out, _outlen, _lparam, _lparamlen, _prng, _prng_idx, _hash_idx, LTC_LTC_PKCS_1_OAEP, _key) #define rsa_decrypt_key(_in, _inlen, _out, _outlen, _lparam, _lparamlen, _hash_idx, _stat, _key) \ rsa_decrypt_key_ex(_in, _inlen, _out, _outlen, _lparam, _lparamlen, _hash_idx, LTC_LTC_PKCS_1_OAEP, _stat, _key) #define rsa_sign_hash(_in, _inlen, _out, _outlen, _prng, _prng_idx, _hash_idx, _saltlen, _key) \ rsa_sign_hash_ex(_in, _inlen, _out, _outlen, LTC_LTC_PKCS_1_PSS, _prng, _prng_idx, _hash_idx, _saltlen, _key) #define rsa_verify_hash(_sig, _siglen, _hash, _hashlen, _hash_idx, _saltlen, _stat, _key) \ rsa_verify_hash_ex(_sig, _siglen, _hash, _hashlen, LTC_LTC_PKCS_1_PSS, _hash_idx, _saltlen, _stat, _key) /* These can be switched between LTC_PKCS #1 v2.x and LTC_PKCS #1 v1.5 paddings */ int rsa_encrypt_key_ex(const unsigned char *in, unsigned long inlen, unsigned char *out, unsigned long *outlen, const unsigned char *lparam, unsigned long lparamlen, prng_state *prng, int prng_idx, int hash_idx, int padding, rsa_key *key); int rsa_decrypt_key_ex(const unsigned char *in, unsigned long inlen, unsigned char *out, unsigned long *outlen, const unsigned char *lparam, unsigned long lparamlen, int hash_idx, int padding, int *stat, rsa_key *key); int rsa_sign_hash_ex(const unsigned char *in, unsigned long inlen, unsigned char *out, unsigned long *outlen, int padding, prng_state *prng, int prng_idx, int hash_idx, unsigned long saltlen, rsa_key *key); int rsa_verify_hash_ex(const unsigned char *sig, unsigned long siglen, const unsigned char *hash, unsigned long hashlen, int padding, int hash_idx, unsigned long saltlen, int *stat, rsa_key *key); /* LTC_PKCS #1 import/export */ int rsa_export(unsigned char *out, unsigned long *outlen, int type, rsa_key *key); int rsa_import(const unsigned char *in, unsigned long inlen, rsa_key *key); #endif /* ---- Katja ---- */ #ifdef MKAT /* Min and Max KAT key sizes (in bits) */ #define MIN_KAT_SIZE 1024 #define MAX_KAT_SIZE 4096 /** Katja LTC_PKCS style key */ typedef struct KAT_key { /** Type of key, PK_PRIVATE or PK_PUBLIC */ int type; /** The private exponent */ void *d; /** The modulus */ void *N; /** The p factor of N */ void *p; /** The q factor of N */ void *q; /** The 1/q mod p CRT param */ void *qP; /** The d mod (p - 1) CRT param */ void *dP; /** The d mod (q - 1) CRT param */ void *dQ; /** The pq param */ void *pq; } katja_key; int katja_make_key(prng_state *prng, int wprng, int size, katja_key *key); int katja_exptmod(const unsigned char *in, unsigned long inlen, unsigned char *out, unsigned long *outlen, int which, katja_key *key); void katja_free(katja_key *key); /* These use LTC_PKCS #1 v2.0 padding */ int katja_encrypt_key(const unsigned char *in, unsigned long inlen, unsigned char *out, unsigned long *outlen, const unsigned char *lparam, unsigned long lparamlen, prng_state *prng, int prng_idx, int hash_idx, katja_key *key); int katja_decrypt_key(const unsigned char *in, unsigned long inlen, unsigned char *out, unsigned long *outlen, const unsigned char *lparam, unsigned long lparamlen, int hash_idx, int *stat, katja_key *key); /* LTC_PKCS #1 import/export */ int katja_export(unsigned char *out, unsigned long *outlen, int type, katja_key *key); int katja_import(const unsigned char *in, unsigned long inlen, katja_key *key); #endif /* ---- ECC Routines ---- */ #ifdef LTC_MECC /* size of our temp buffers for exported keys */ #define ECC_BUF_SIZE 256 /* max private key size */ #define ECC_MAXSIZE 66 /** Structure defines a NIST GF(p) curve */ typedef struct { /** The size of the curve in octets */ int size; /** name of curve */ char *name; /** The prime that defines the field the curve is in (encoded in hex) */ char *prime; /** The fields B param (hex) */ char *B; /** The order of the curve (hex) */ char *order; /** The x co-ordinate of the base point on the curve (hex) */ char *Gx; /** The y co-ordinate of the base point on the curve (hex) */ char *Gy; } ltc_ecc_set_type; /** A point on a ECC curve, stored in Jacbobian format such that (x,y,z) => (x/z^2, y/z^3, 1) when interpretted as affine */ typedef struct { /** The x co-ordinate */ void *x; /** The y co-ordinate */ void *y; /** The z co-ordinate */ void *z; } ecc_point; /** An ECC key */ typedef struct { /** Type of key, PK_PRIVATE or PK_PUBLIC */ int type; /** Index into the ltc_ecc_sets[] for the parameters of this curve; if -1, then this key is using user supplied curve in dp */ int idx; /** pointer to domain parameters; either points to NIST curves (identified by idx >= 0) or user supplied curve */ const ltc_ecc_set_type *dp; /** The public key */ ecc_point pubkey; /** The private key */ void *k; } ecc_key; /** the ECC params provided */ extern const ltc_ecc_set_type ltc_ecc_sets[]; int ecc_test(void); void ecc_sizes(int *low, int *high); int ecc_get_size(ecc_key *key); int ecc_make_key(prng_state *prng, int wprng, int keysize, ecc_key *key); int ecc_make_key_ex(prng_state *prng, int wprng, ecc_key *key, const ltc_ecc_set_type *dp); void ecc_free(ecc_key *key); int ecc_export(unsigned char *out, unsigned long *outlen, int type, ecc_key *key); int ecc_import(const unsigned char *in, unsigned long inlen, ecc_key *key); int ecc_import_ex(const unsigned char *in, unsigned long inlen, ecc_key *key, const ltc_ecc_set_type *dp); int ecc_ansi_x963_export(ecc_key *key, unsigned char *out, unsigned long *outlen); int ecc_ansi_x963_import(const unsigned char *in, unsigned long inlen, ecc_key *key); int ecc_ansi_x963_import_ex(const unsigned char *in, unsigned long inlen, ecc_key *key, ltc_ecc_set_type *dp); int ecc_shared_secret(ecc_key *private_key, ecc_key *public_key, unsigned char *out, unsigned long *outlen); int ecc_encrypt_key(const unsigned char *in, unsigned long inlen, unsigned char *out, unsigned long *outlen, prng_state *prng, int wprng, int hash, ecc_key *key); int ecc_decrypt_key(const unsigned char *in, unsigned long inlen, unsigned char *out, unsigned long *outlen, ecc_key *key); int ecc_sign_hash(const unsigned char *in, unsigned long inlen, unsigned char *out, unsigned long *outlen, prng_state *prng, int wprng, ecc_key *key); int ecc_verify_hash(const unsigned char *sig, unsigned long siglen, const unsigned char *hash, unsigned long hashlen, int *stat, ecc_key *key); /* low level functions */ ecc_point *ltc_ecc_new_point(void); void ltc_ecc_del_point(ecc_point *p); int ltc_ecc_is_valid_idx(int n); /* point ops (mp == montgomery digit) */ #if !defined(LTC_MECC_ACCEL) || defined(LTM_LTC_DESC) || defined(GMP_LTC_DESC) /* R = 2P */ int ltc_ecc_projective_dbl_point(ecc_point *P, ecc_point *R, void *modulus, void *mp); /* R = P + Q */ int ltc_ecc_projective_add_point(ecc_point *P, ecc_point *Q, ecc_point *R, void *modulus, void *mp); #endif #if defined(LTC_MECC_FP) /* optimized point multiplication using fixed point cache (HAC algorithm 14.117) */ int ltc_ecc_fp_mulmod(void *k, ecc_point *G, ecc_point *R, void *modulus, int map); /* functions for saving/loading/freeing/adding to fixed point cache */ int ltc_ecc_fp_save_state(unsigned char **out, unsigned long *outlen); int ltc_ecc_fp_restore_state(unsigned char *in, unsigned long inlen); void ltc_ecc_fp_free(void); int ltc_ecc_fp_add_point(ecc_point *g, void *modulus, int lock); /* lock/unlock all points currently in fixed point cache */ void ltc_ecc_fp_tablelock(int lock); #endif /* R = kG */ int ltc_ecc_mulmod(void *k, ecc_point *G, ecc_point *R, void *modulus, int map); #ifdef LTC_ECC_SHAMIR /* kA*A + kB*B = C */ int ltc_ecc_mul2add(ecc_point *A, void *kA, ecc_point *B, void *kB, ecc_point *C, void *modulus); #ifdef LTC_MECC_FP /* Shamir's trick with optimized point multiplication using fixed point cache */ int ltc_ecc_fp_mul2add(ecc_point *A, void *kA, ecc_point *B, void *kB, ecc_point *C, void *modulus); #endif #endif /* map P to affine from projective */ int ltc_ecc_map(ecc_point *P, void *modulus, void *mp); #endif #ifdef LTC_MDSA /* Max diff between group and modulus size in bytes */ #define LTC_MDSA_DELTA 512 /* Max DSA group size in bytes (default allows 4k-bit groups) */ #define LTC_MDSA_MAX_GROUP 512 /** DSA key structure */ typedef struct { /** The key type, PK_PRIVATE or PK_PUBLIC */ int type; /** The order of the sub-group used in octets */ int qord; /** The generator */ void *g; /** The prime used to generate the sub-group */ void *q; /** The large prime that generats the field the contains the sub-group */ void *p; /** The private key */ void *x; /** The public key */ void *y; } dsa_key; int dsa_make_key(prng_state *prng, int wprng, int group_size, int modulus_size, dsa_key *key); void dsa_free(dsa_key *key); int dsa_sign_hash_raw(const unsigned char *in, unsigned long inlen, void *r, void *s, prng_state *prng, int wprng, dsa_key *key); int dsa_sign_hash(const unsigned char *in, unsigned long inlen, unsigned char *out, unsigned long *outlen, prng_state *prng, int wprng, dsa_key *key); int dsa_verify_hash_raw(void *r, void *s, const unsigned char *hash, unsigned long hashlen, int *stat, dsa_key *key); int dsa_verify_hash(const unsigned char *sig, unsigned long siglen, const unsigned char *hash, unsigned long hashlen, int *stat, dsa_key *key); int dsa_encrypt_key(const unsigned char *in, unsigned long inlen, unsigned char *out, unsigned long *outlen, prng_state *prng, int wprng, int hash, dsa_key *key); int dsa_decrypt_key(const unsigned char *in, unsigned long inlen, unsigned char *out, unsigned long *outlen, dsa_key *key); int dsa_import(const unsigned char *in, unsigned long inlen, dsa_key *key); int dsa_export(unsigned char *out, unsigned long *outlen, int type, dsa_key *key); int dsa_verify_key(dsa_key *key, int *stat); int dsa_shared_secret(void *private_key, void *base, dsa_key *public_key, unsigned char *out, unsigned long *outlen); #endif #ifdef LTC_DER /* DER handling */ enum { LTC_ASN1_EOL, LTC_ASN1_BOOLEAN, LTC_ASN1_INTEGER, LTC_ASN1_SHORT_INTEGER, LTC_ASN1_BIT_STRING, LTC_ASN1_OCTET_STRING, LTC_ASN1_NULL, LTC_ASN1_OBJECT_IDENTIFIER, LTC_ASN1_IA5_STRING, LTC_ASN1_PRINTABLE_STRING, LTC_ASN1_UTF8_STRING, LTC_ASN1_UTCTIME, LTC_ASN1_CHOICE, LTC_ASN1_SEQUENCE, LTC_ASN1_SET, LTC_ASN1_SETOF }; /** A LTC ASN.1 list type */ typedef struct ltc_asn1_list_ { /** The LTC ASN.1 enumerated type identifier */ int type; /** The data to encode or place for decoding */ void *data; /** The size of the input or resulting output */ unsigned long size; /** The used flag, this is used by the CHOICE ASN.1 type to indicate which choice was made */ int used; /** prev/next entry in the list */ struct ltc_asn1_list_ *prev, *next, *child, *parent; } ltc_asn1_list; #define LTC_SET_ASN1(list, index, Type, Data, Size) \ do { \ int LTC_MACRO_temp = (index); \ ltc_asn1_list *LTC_MACRO_list = (list); \ LTC_MACRO_list[LTC_MACRO_temp].type = (Type); \ LTC_MACRO_list[LTC_MACRO_temp].data = (void *)(Data); \ LTC_MACRO_list[LTC_MACRO_temp].size = (Size); \ LTC_MACRO_list[LTC_MACRO_temp].used = 0; \ } while (0); /* SEQUENCE */ int der_encode_sequence_ex(ltc_asn1_list *list, unsigned long inlen, unsigned char *out, unsigned long *outlen, int type_of); #define der_encode_sequence(list, inlen, out, outlen) der_encode_sequence_ex(list, inlen, out, outlen, LTC_ASN1_SEQUENCE) int der_decode_sequence_ex(const unsigned char *in, unsigned long inlen, ltc_asn1_list *list, unsigned long outlen, int ordered); #define der_decode_sequence(in, inlen, list, outlen) der_decode_sequence_ex(in, inlen, list, outlen, 1) int der_length_sequence(ltc_asn1_list *list, unsigned long inlen, unsigned long *outlen); /* SET */ #define der_decode_set(in, inlen, list, outlen) der_decode_sequence_ex(in, inlen, list, outlen, 0) #define der_length_set der_length_sequence int der_encode_set(ltc_asn1_list *list, unsigned long inlen, unsigned char *out, unsigned long *outlen); int der_encode_setof(ltc_asn1_list *list, unsigned long inlen, unsigned char *out, unsigned long *outlen); /* VA list handy helpers with triplets of */ int der_encode_sequence_multi(unsigned char *out, unsigned long *outlen, ...); int der_decode_sequence_multi(const unsigned char *in, unsigned long inlen, ...); /* FLEXI DECODER handle unknown list decoder */ int der_decode_sequence_flexi(const unsigned char *in, unsigned long *inlen, ltc_asn1_list **out); void der_free_sequence_flexi(ltc_asn1_list *list); void der_sequence_free(ltc_asn1_list *in); /* BOOLEAN */ int der_length_boolean(unsigned long *outlen); int der_encode_boolean(int in, unsigned char *out, unsigned long *outlen); int der_decode_boolean(const unsigned char *in, unsigned long inlen, int *out); /* INTEGER */ int der_encode_integer(void *num, unsigned char *out, unsigned long *outlen); int der_decode_integer(const unsigned char *in, unsigned long inlen, void *num); int der_length_integer(void *num, unsigned long *len); /* INTEGER -- handy for 0..2^32-1 values */ int der_decode_short_integer(const unsigned char *in, unsigned long inlen, unsigned long *num); int der_encode_short_integer(unsigned long num, unsigned char *out, unsigned long *outlen); int der_length_short_integer(unsigned long num, unsigned long *outlen); /* BIT STRING */ int der_encode_bit_string(const unsigned char *in, unsigned long inlen, unsigned char *out, unsigned long *outlen); int der_decode_bit_string(const unsigned char *in, unsigned long inlen, unsigned char *out, unsigned long *outlen); int der_length_bit_string(unsigned long nbits, unsigned long *outlen); /* OCTET STRING */ int der_encode_octet_string(const unsigned char *in, unsigned long inlen, unsigned char *out, unsigned long *outlen); int der_decode_octet_string(const unsigned char *in, unsigned long inlen, unsigned char *out, unsigned long *outlen); int der_length_octet_string(unsigned long noctets, unsigned long *outlen); /* OBJECT IDENTIFIER */ int der_encode_object_identifier(unsigned long *words, unsigned long nwords, unsigned char *out, unsigned long *outlen); int der_decode_object_identifier(const unsigned char *in, unsigned long inlen, unsigned long *words, unsigned long *outlen); int der_length_object_identifier(unsigned long *words, unsigned long nwords, unsigned long *outlen); unsigned long der_object_identifier_bits(unsigned long x); /* IA5 STRING */ int der_encode_ia5_string(const unsigned char *in, unsigned long inlen, unsigned char *out, unsigned long *outlen); int der_decode_ia5_string(const unsigned char *in, unsigned long inlen, unsigned char *out, unsigned long *outlen); int der_length_ia5_string(const unsigned char *octets, unsigned long noctets, unsigned long *outlen); int der_ia5_char_encode(int c); int der_ia5_value_decode(int v); /* Printable STRING */ int der_encode_printable_string(const unsigned char *in, unsigned long inlen, unsigned char *out, unsigned long *outlen); int der_decode_printable_string(const unsigned char *in, unsigned long inlen, unsigned char *out, unsigned long *outlen); int der_length_printable_string(const unsigned char *octets, unsigned long noctets, unsigned long *outlen); int der_printable_char_encode(int c); int der_printable_value_decode(int v); /* UTF-8 */ #if (defined(SIZE_MAX) || __STDC_VERSION__ >= 199901L || defined(WCHAR_MAX) || defined(_WCHAR_T) || defined(_WCHAR_T_DEFINED) || defined (__WCHAR_TYPE__)) && !defined(LTC_NO_WCHAR) #include #else typedef ulong32 wchar_t; #endif int der_encode_utf8_string(const wchar_t *in, unsigned long inlen, unsigned char *out, unsigned long *outlen); int der_decode_utf8_string(const unsigned char *in, unsigned long inlen, wchar_t *out, unsigned long *outlen); unsigned long der_utf8_charsize(const wchar_t c); int der_length_utf8_string(const wchar_t *in, unsigned long noctets, unsigned long *outlen); /* CHOICE */ int der_decode_choice(const unsigned char *in, unsigned long *inlen, ltc_asn1_list *list, unsigned long outlen); /* UTCTime */ typedef struct { unsigned YY, /* year */ MM, /* month */ DD, /* day */ hh, /* hour */ mm, /* minute */ ss, /* second */ off_dir, /* timezone offset direction 0 == +, 1 == - */ off_hh, /* timezone offset hours */ off_mm; /* timezone offset minutes */ } ltc_utctime; int der_encode_utctime(ltc_utctime *utctime, unsigned char *out, unsigned long *outlen); int der_decode_utctime(const unsigned char *in, unsigned long *inlen, ltc_utctime *out); int der_length_utctime(ltc_utctime *utctime, unsigned long *outlen); #endif /* $Source: /cvs/libtom/libtomcrypt/src/headers/tomcrypt_pk.h,v $ */ /* $Revision: 1.81 $ */ /* $Date: 2007/05/12 14:32:35 $ */ /** math functions **/ #define LTC_SOURCE #define LTC_MP_LT -1 #define LTC_MP_EQ 0 #define LTC_MP_GT 1 #define LTC_MP_NO 0 #define LTC_MP_YES 1 #ifndef LTC_MECC typedef void ecc_point; #endif #ifndef LTC_MRSA typedef void rsa_key; #endif /** math descriptor */ typedef struct { /** Name of the math provider */ char *name; /** Bits per digit, amount of bits must fit in an unsigned long */ int bits_per_digit; /* ---- init/deinit functions ---- */ /** initialize a bignum @param a The number to initialize @return CRYPT_OK on success */ int (*init)(void **a); /** init copy @param dst The number to initialize and write to @param src The number to copy from @return CRYPT_OK on success */ int (*init_copy)(void **dst, void *src); /** deinit @param a The number to free @return CRYPT_OK on success */ void (*deinit)(void *a); /* ---- data movement ---- */ /** negate @param src The number to negate @param dst The destination @return CRYPT_OK on success */ int (*neg)(void *src, void *dst); /** copy @param src The number to copy from @param dst The number to write to @return CRYPT_OK on success */ int (*copy)(void *src, void *dst); /* ---- trivial low level functions ---- */ /** set small constant @param a Number to write to @param n Source upto bits_per_digit (actually meant for very small constants) @return CRYPT_OK on succcess */ int (*set_int)(void *a, unsigned long n); /** get small constant @param a Number to read, only fetches upto bits_per_digit from the number @return The lower bits_per_digit of the integer (unsigned) */ unsigned long (*get_int)(void *a); /** get digit n @param a The number to read from @param n The number of the digit to fetch @return The bits_per_digit sized n'th digit of a */ unsigned long (*get_digit)(void *a, int n); /** Get the number of digits that represent the number @param a The number to count @return The number of digits used to represent the number */ int (*get_digit_count)(void *a); /** compare two integers @param a The left side integer @param b The right side integer @return LTC_MP_LT if a < b, LTC_MP_GT if a > b and LTC_MP_EQ otherwise. (signed comparison) */ int (*compare)(void *a, void *b); /** compare against int @param a The left side integer @param b The right side integer (upto bits_per_digit) @return LTC_MP_LT if a < b, LTC_MP_GT if a > b and LTC_MP_EQ otherwise. (signed comparison) */ int (*compare_d)(void *a, unsigned long n); /** Count the number of bits used to represent the integer @param a The integer to count @return The number of bits required to represent the integer */ int (*count_bits)(void *a); /** Count the number of LSB bits which are zero @param a The integer to count @return The number of contiguous zero LSB bits */ int (*count_lsb_bits)(void *a); /** Compute a power of two @param a The integer to store the power in @param n The power of two you want to store (a = 2^n) @return CRYPT_OK on success */ int (*twoexpt)(void *a, int n); /* ---- radix conversions ---- */ /** read ascii string @param a The integer to store into @param str The string to read @param radix The radix the integer has been represented in (2-64) @return CRYPT_OK on success */ int (*read_radix)(void *a, const char *str, int radix); /** write number to string @param a The integer to store @param str The destination for the string @param radix The radix the integer is to be represented in (2-64) @return CRYPT_OK on success */ int (*write_radix)(void *a, char *str, int radix); /** get size as unsigned char string @param a The integer to get the size (when stored in array of octets) @return The length of the integer */ unsigned long (*unsigned_size)(void *a); /** store an integer as an array of octets @param src The integer to store @param dst The buffer to store the integer in @return CRYPT_OK on success */ int (*unsigned_write)(void *src, unsigned char *dst); /** read an array of octets and store as integer @param dst The integer to load @param src The array of octets @param len The number of octets @return CRYPT_OK on success */ int (*unsigned_read)(void *dst, unsigned char *src, unsigned long len); /* ---- basic math ---- */ /** add two integers @param a The first source integer @param b The second source integer @param c The destination of "a + b" @return CRYPT_OK on success */ int (*add)(void *a, void *b, void *c); /** add two integers @param a The first source integer @param b The second source integer (single digit of upto bits_per_digit in length) @param c The destination of "a + b" @return CRYPT_OK on success */ int (*addi)(void *a, unsigned long b, void *c); /** subtract two integers @param a The first source integer @param b The second source integer @param c The destination of "a - b" @return CRYPT_OK on success */ int (*sub)(void *a, void *b, void *c); /** subtract two integers @param a The first source integer @param b The second source integer (single digit of upto bits_per_digit in length) @param c The destination of "a - b" @return CRYPT_OK on success */ int (*subi)(void *a, unsigned long b, void *c); /** multiply two integers @param a The first source integer @param b The second source integer (single digit of upto bits_per_digit in length) @param c The destination of "a * b" @return CRYPT_OK on success */ int (*mul)(void *a, void *b, void *c); /** multiply two integers @param a The first source integer @param b The second source integer (single digit of upto bits_per_digit in length) @param c The destination of "a * b" @return CRYPT_OK on success */ int (*muli)(void *a, unsigned long b, void *c); /** Square an integer @param a The integer to square @param b The destination @return CRYPT_OK on success */ int (*sqr)(void *a, void *b); /** Divide an integer @param a The dividend @param b The divisor @param c The quotient (can be NULL to signify don't care) @param d The remainder (can be NULL to signify don't care) @return CRYPT_OK on success */ int (*mpdiv)(void *a, void *b, void *c, void *d); /** divide by two @param a The integer to divide (shift right) @param b The destination @return CRYPT_OK on success */ int (*div_2)(void *a, void *b); /** Get remainder (small value) @param a The integer to reduce @param b The modulus (upto bits_per_digit in length) @param c The destination for the residue @return CRYPT_OK on success */ int (*modi)(void *a, unsigned long b, unsigned long *c); /** gcd @param a The first integer @param b The second integer @param c The destination for (a, b) @return CRYPT_OK on success */ int (*gcd)(void *a, void *b, void *c); /** lcm @param a The first integer @param b The second integer @param c The destination for [a, b] @return CRYPT_OK on success */ int (*lcm)(void *a, void *b, void *c); /** Modular multiplication @param a The first source @param b The second source @param c The modulus @param d The destination (a*b mod c) @return CRYPT_OK on success */ int (*mulmod)(void *a, void *b, void *c, void *d); /** Modular squaring @param a The first source @param b The modulus @param c The destination (a*a mod b) @return CRYPT_OK on success */ int (*sqrmod)(void *a, void *b, void *c); /** Modular inversion @param a The value to invert @param b The modulus @param c The destination (1/a mod b) @return CRYPT_OK on success */ int (*invmod)(void *, void *, void *); /* ---- reduction ---- */ /** setup montgomery @param a The modulus @param b The destination for the reduction digit @return CRYPT_OK on success */ int (*montgomery_setup)(void *a, void **b); /** get normalization value @param a The destination for the normalization value @param b The modulus @return CRYPT_OK on success */ int (*montgomery_normalization)(void *a, void *b); /** reduce a number @param a The number [and dest] to reduce @param b The modulus @param c The value "b" from montgomery_setup() @return CRYPT_OK on success */ int (*montgomery_reduce)(void *a, void *b, void *c); /** clean up (frees memory) @param a The value "b" from montgomery_setup() @return CRYPT_OK on success */ void (*montgomery_deinit)(void *a); /* ---- exponentiation ---- */ /** Modular exponentiation @param a The base integer @param b The power (can be negative) integer @param c The modulus integer @param d The destination @return CRYPT_OK on success */ int (*exptmod)(void *a, void *b, void *c, void *d); /** Primality testing @param a The integer to test @param b The destination of the result (FP_YES if prime) @return CRYPT_OK on success */ int (*isprime)(void *a, int *b); /* ---- (optional) ecc point math ---- */ /** ECC GF(p) point multiplication (from the NIST curves) @param k The integer to multiply the point by @param G The point to multiply @param R The destination for kG @param modulus The modulus for the field @param map Boolean indicated whether to map back to affine or not (can be ignored if you work in affine only) @return CRYPT_OK on success */ int (*ecc_ptmul)(void *k, ecc_point *G, ecc_point *R, void *modulus, int map); /** ECC GF(p) point addition @param P The first point @param Q The second point @param R The destination of P + Q @param modulus The modulus @param mp The "b" value from montgomery_setup() @return CRYPT_OK on success */ int (*ecc_ptadd)(ecc_point *P, ecc_point *Q, ecc_point *R, void *modulus, void *mp); /** ECC GF(p) point double @param P The first point @param R The destination of 2P @param modulus The modulus @param mp The "b" value from montgomery_setup() @return CRYPT_OK on success */ int (*ecc_ptdbl)(ecc_point *P, ecc_point *R, void *modulus, void *mp); /** ECC mapping from projective to affine, currently uses (x,y,z) => (x/z^2, y/z^3, 1) @param P The point to map @param modulus The modulus @param mp The "b" value from montgomery_setup() @return CRYPT_OK on success @remark The mapping can be different but keep in mind a ecc_point only has three integers (x,y,z) so if you use a different mapping you have to make it fit. */ int (*ecc_map)(ecc_point *P, void *modulus, void *mp); /** Computes kA*A + kB*B = C using Shamir's Trick @param A First point to multiply @param kA What to multiple A by @param B Second point to multiply @param kB What to multiple B by @param C [out] Destination point (can overlap with A or B @param modulus Modulus for curve @return CRYPT_OK on success */ int (*ecc_mul2add)(ecc_point *A, void *kA, ecc_point *B, void *kB, ecc_point *C, void *modulus); /* ---- (optional) rsa optimized math (for internal CRT) ---- */ /** RSA Key Generation @param prng An active PRNG state @param wprng The index of the PRNG desired @param size The size of the modulus (key size) desired (octets) @param e The "e" value (public key). e==65537 is a good choice @param key [out] Destination of a newly created private key pair @return CRYPT_OK if successful, upon error all allocated ram is freed */ int (*rsa_keygen)(prng_state *prng, int wprng, int size, long e, rsa_key *key); /** RSA exponentiation @param in The octet array representing the base @param inlen The length of the input @param out The destination (to be stored in an octet array format) @param outlen The length of the output buffer and the resulting size (zero padded to the size of the modulus) @param which PK_PUBLIC for public RSA and PK_PRIVATE for private RSA @param key The RSA key to use @return CRYPT_OK on success */ int (*rsa_me)(const unsigned char *in, unsigned long inlen, unsigned char *out, unsigned long *outlen, int which, rsa_key *key); } ltc_math_descriptor; extern ltc_math_descriptor ltc_mp; int ltc_init_multi(void **a, ...); void ltc_deinit_multi(void *a, ...); #ifdef LTM_DESC extern const ltc_math_descriptor ltm_desc; #endif #ifdef TFM_DESC extern const ltc_math_descriptor tfm_desc; #endif #ifdef GMP_DESC extern const ltc_math_descriptor gmp_desc; #endif #if !defined(DESC_DEF_ONLY) && defined(LTC_SOURCE) #undef MP_DIGIT_BIT #undef mp_iszero #undef mp_isodd #undef mp_tohex #define MP_DIGIT_BIT ltc_mp.bits_per_digit /* some handy macros */ #define mp_init(a) ltc_mp.init(a) #define mp_init_multi ltc_init_multi #define mp_clear(a) ltc_mp.deinit(a) #define mp_clear_multi ltc_deinit_multi #define mp_init_copy(a, b) ltc_mp.init_copy(a, b) #define mp_neg(a, b) ltc_mp.neg(a, b) #define mp_copy(a, b) ltc_mp.copy(a, b) #define mp_set(a, b) ltc_mp.set_int(a, b) #define mp_set_int(a, b) ltc_mp.set_int(a, b) #define mp_get_int(a) ltc_mp.get_int(a) #define mp_get_digit(a, n) ltc_mp.get_digit(a, n) #define mp_get_digit_count(a) ltc_mp.get_digit_count(a) #define mp_cmp(a, b) ltc_mp.compare(a, b) #define mp_cmp_d(a, b) ltc_mp.compare_d(a, b) #define mp_count_bits(a) ltc_mp.count_bits(a) #define mp_cnt_lsb(a) ltc_mp.count_lsb_bits(a) #define mp_2expt(a, b) ltc_mp.twoexpt(a, b) #define mp_read_radix(a, b, c) ltc_mp.read_radix(a, b, c) #define mp_toradix(a, b, c) ltc_mp.write_radix(a, b, c) #define mp_unsigned_bin_size(a) ltc_mp.unsigned_size(a) #define mp_to_unsigned_bin(a, b) ltc_mp.unsigned_write(a, b) #define mp_read_unsigned_bin(a, b, c) ltc_mp.unsigned_read(a, b, c) #define mp_add(a, b, c) ltc_mp.add(a, b, c) #define mp_add_d(a, b, c) ltc_mp.addi(a, b, c) #define mp_sub(a, b, c) ltc_mp.sub(a, b, c) #define mp_sub_d(a, b, c) ltc_mp.subi(a, b, c) #define mp_mul(a, b, c) ltc_mp.mul(a, b, c) #define mp_mul_d(a, b, c) ltc_mp.muli(a, b, c) #define mp_sqr(a, b) ltc_mp.sqr(a, b) #define mp_div(a, b, c, d) ltc_mp.mpdiv(a, b, c, d) #define mp_div_2(a, b) ltc_mp.div_2(a, b) #define mp_mod(a, b, c) ltc_mp.mpdiv(a, b, NULL, c) #define mp_mod_d(a, b, c) ltc_mp.modi(a, b, c) #define mp_gcd(a, b, c) ltc_mp.gcd(a, b, c) #define mp_lcm(a, b, c) ltc_mp.lcm(a, b, c) #define mp_mulmod(a, b, c, d) ltc_mp.mulmod(a, b, c, d) #define mp_sqrmod(a, b, c) ltc_mp.sqrmod(a, b, c) #define mp_invmod(a, b, c) ltc_mp.invmod(a, b, c) #define mp_montgomery_setup(a, b) ltc_mp.montgomery_setup(a, b) #define mp_montgomery_normalization(a, b) ltc_mp.montgomery_normalization(a, b) #define mp_montgomery_reduce(a, b, c) ltc_mp.montgomery_reduce(a, b, c) #define mp_montgomery_free(a) ltc_mp.montgomery_deinit(a) #define mp_exptmod(a, b, c, d) ltc_mp.exptmod(a, b, c, d) #define mp_prime_is_prime(a, b, c) ltc_mp.isprime(a, c) #define mp_iszero(a) (mp_cmp_d(a, 0) == LTC_MP_EQ ? LTC_MP_YES : LTC_MP_NO) #define mp_isodd(a) (mp_get_digit_count(a) > 0 ? (mp_get_digit(a, 0) & 1 ? LTC_MP_YES : LTC_MP_NO) : LTC_MP_NO) #define mp_exch(a, b) do { void *ABC__tmp = a; a = b; b = ABC__tmp; } while (0); #define mp_tohex(a, b) mp_toradix(a, b, 16) #endif /* $Source: /cvs/libtom/libtomcrypt/src/headers/tomcrypt_math.h,v $ */ /* $Revision: 1.44 $ */ /* $Date: 2007/05/12 14:32:35 $ */ /* ---- LTC_BASE64 Routines ---- */ #ifdef LTC_BASE64 int base64_encode(const unsigned char *in, unsigned long len, unsigned char *out, unsigned long *outlen); int base64_decode(const unsigned char *in, unsigned long len, unsigned char *out, unsigned long *outlen); #endif /* ---- MEM routines ---- */ void zeromem(void *dst, size_t len); void burn_stack(unsigned long len); const char *error_to_string(int err); extern const char *crypt_build_settings; /* ---- HMM ---- */ int crypt_fsa(void *mp, ...); /* $Source: /cvs/libtom/libtomcrypt/src/headers/tomcrypt_misc.h,v $ */ /* $Revision: 1.5 $ */ /* $Date: 2007/05/12 14:32:35 $ */ /* Defines the LTC_ARGCHK macro used within the library */ /* ARGTYPE is defined in mycrypt_cfg.h */ #if ARGTYPE == 0 #include /* this is the default LibTomCrypt macro */ void crypt_argchk(char *v, char *s, int d); #define LTC_ARGCHK(x) if (!(x)) { crypt_argchk(#x, __FILE__, __LINE__); } #define LTC_ARGCHKVD(x) LTC_ARGCHK(x) #elif ARGTYPE == 1 /* fatal type of error */ #define LTC_ARGCHK(x) HTTPS_ASSERT((x, "LibTomCrypt LTC_ARGCHK error")) #define LTC_ARGCHKVD(x) LTC_ARGCHK(x) #elif ARGTYPE == 2 #define LTC_ARGCHK(x) if (!(x)) { fprintf(stderr, "\nwarning: ARGCHK failed at %s:%d\n", __FILE__, __LINE__); } #define LTC_ARGCHKVD(x) LTC_ARGCHK(x) #elif ARGTYPE == 3 #define LTC_ARGCHK(x) #define LTC_ARGCHKVD(x) LTC_ARGCHK(x) #elif ARGTYPE == 4 #define LTC_ARGCHK(x) if (!(x)) return CRYPT_INVALID_ARG; #define LTC_ARGCHKVD(x) if (!(x)) return; #endif /* $Source: /cvs/libtom/libtomcrypt/src/headers/tomcrypt_argchk.h,v $ */ /* $Revision: 1.5 $ */ /* $Date: 2006/08/27 20:50:21 $ */ /* LTC_PKCS Header Info */ /* ===> LTC_PKCS #1 -- RSA Cryptography <=== */ #ifdef LTC_PKCS_1 enum ltc_pkcs_1_v1_5_blocks { LTC_LTC_PKCS_1_EMSA = 1, /* Block type 1 (LTC_PKCS #1 v1.5 signature padding) */ LTC_LTC_PKCS_1_EME = 2 /* Block type 2 (LTC_PKCS #1 v1.5 encryption padding) */ }; enum ltc_pkcs_1_paddings { LTC_LTC_PKCS_1_V1_5 = 1, /* LTC_PKCS #1 v1.5 padding (\sa ltc_pkcs_1_v1_5_blocks) */ LTC_LTC_PKCS_1_OAEP = 2, /* LTC_PKCS #1 v2.0 encryption padding */ LTC_LTC_PKCS_1_PSS = 3 /* LTC_PKCS #1 v2.1 signature padding */ }; int pkcs_1_mgf1(int hash_idx, const unsigned char *seed, unsigned long seedlen, unsigned char *mask, unsigned long masklen); int pkcs_1_i2osp(void *n, unsigned long modulus_len, unsigned char *out); int pkcs_1_os2ip(void *n, unsigned char *in, unsigned long inlen); /* *** v1.5 padding */ int pkcs_1_v1_5_encode(const unsigned char *msg, unsigned long msglen, int block_type, unsigned long modulus_bitlen, prng_state *prng, int prng_idx, unsigned char *out, unsigned long *outlen); int pkcs_1_v1_5_decode(const unsigned char *msg, unsigned long msglen, int block_type, unsigned long modulus_bitlen, unsigned char *out, unsigned long *outlen, int *is_valid); /* *** v2.1 padding */ int pkcs_1_oaep_encode(const unsigned char *msg, unsigned long msglen, const unsigned char *lparam, unsigned long lparamlen, unsigned long modulus_bitlen, prng_state *prng, int prng_idx, int hash_idx, unsigned char *out, unsigned long *outlen); int pkcs_1_oaep_decode(const unsigned char *msg, unsigned long msglen, const unsigned char *lparam, unsigned long lparamlen, unsigned long modulus_bitlen, int hash_idx, unsigned char *out, unsigned long *outlen, int *res); int pkcs_1_pss_encode(const unsigned char *msghash, unsigned long msghashlen, unsigned long saltlen, prng_state *prng, int prng_idx, int hash_idx, unsigned long modulus_bitlen, unsigned char *out, unsigned long *outlen); int pkcs_1_pss_decode(const unsigned char *msghash, unsigned long msghashlen, const unsigned char *sig, unsigned long siglen, unsigned long saltlen, int hash_idx, unsigned long modulus_bitlen, int *res); #endif /* LTC_PKCS_1 */ /* ===> LTC_PKCS #5 -- Password Based Cryptography <=== */ #ifdef LTC_PKCS_5 /* Algorithm #1 (old) */ int pkcs_5_alg1(const unsigned char *password, unsigned long password_len, const unsigned char *salt, int iteration_count, int hash_idx, unsigned char *out, unsigned long *outlen); /* Algorithm #2 (new) */ int pkcs_5_alg2(const unsigned char *password, unsigned long password_len, const unsigned char *salt, unsigned long salt_len, int iteration_count, int hash_idx, unsigned char *out, unsigned long *outlen); #endif /* LTC_PKCS_5 */ /* $Source: /cvs/libtom/libtomcrypt/src/headers/tomcrypt_pkcs.h,v $ */ /* $Revision: 1.8 $ */ /* $Date: 2007/05/12 14:32:35 $ */ #endif /* TOMCRYPT_H_ */ /* $Source: /cvs/libtom/libtomcrypt/src/headers/tomcrypt.h,v $ */ /* $Revision: 1.21 $ */ /* $Date: 2006/12/16 19:34:05 $ */ /* $Source: /cvs/libtom/libtomcrypt/src/misc/crypt/crypt_argchk.c,v $ */ /* $Revision: 1.5 $ */ /* $Date: 2006/12/28 01:27:24 $ */ /* LibTomCrypt, modular cryptographic library -- Tom St Denis * * LibTomCrypt is a library that provides various cryptographic * algorithms in a highly modular and flexible manner. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tomstdenis@gmail.com, http://libtom.org */ /** @file crypt_cipher_descriptor.c Stores the cipher descriptor table, Tom St Denis */ struct ltc_cipher_descriptor cipher_descriptor[TAB_SIZE] = { { NULL, 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL } }; LTC_MUTEX_GLOBAL(ltc_cipher_mutex) /* $Source: /cvs/libtom/libtomcrypt/src/misc/crypt/crypt_cipher_descriptor.c,v $ */ /* $Revision: 1.13 $ */ /* $Date: 2006/12/28 01:27:24 $ */ /* LibTomCrypt, modular cryptographic library -- Tom St Denis * * LibTomCrypt is a library that provides various cryptographic * algorithms in a highly modular and flexible manner. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tomstdenis@gmail.com, http://libtom.org */ /** @file crypt_cipher_is_valid.c Determine if cipher is valid, Tom St Denis */ /* Test if a cipher index is valid @param idx The index of the cipher to search for @return CRYPT_OK if valid */ int cipher_is_valid(int idx) { LTC_MUTEX_LOCK(<c_cipher_mutex); if ((idx < 0) || (idx >= TAB_SIZE) || (cipher_descriptor[idx].name == NULL)) { LTC_MUTEX_UNLOCK(<c_cipher_mutex); return CRYPT_INVALID_CIPHER; } LTC_MUTEX_UNLOCK(<c_cipher_mutex); return CRYPT_OK; } /* $Source: /cvs/libtom/libtomcrypt/src/misc/crypt/crypt_cipher_is_valid.c,v $ */ /* $Revision: 1.6 $ */ /* $Date: 2006/12/28 01:27:24 $ */ /* LibTomCrypt, modular cryptographic library -- Tom St Denis * * LibTomCrypt is a library that provides various cryptographic * algorithms in a highly modular and flexible manner. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tomstdenis@gmail.com, http://libtom.org */ /** @file crypt_find_cipher.c Find a cipher in the descriptor tables, Tom St Denis */ /** Find a registered cipher by name @param name The name of the cipher to look for @return >= 0 if found, -1 if not present */ int find_cipher(const char *name) { int x; LTC_ARGCHK(name != NULL); LTC_MUTEX_LOCK(<c_cipher_mutex); for (x = 0; x < TAB_SIZE; x++) { if ((cipher_descriptor[x].name != NULL) && !XSTRCMP(cipher_descriptor[x].name, name)) { LTC_MUTEX_UNLOCK(<c_cipher_mutex); return x; } } LTC_MUTEX_UNLOCK(<c_cipher_mutex); return -1; } /* $Source: /cvs/libtom/libtomcrypt/src/misc/crypt/crypt_find_cipher.c,v $ */ /* $Revision: 1.7 $ */ /* $Date: 2006/12/28 01:27:24 $ */ /* LibTomCrypt, modular cryptographic library -- Tom St Denis * * LibTomCrypt is a library that provides various cryptographic * algorithms in a highly modular and flexible manner. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tomstdenis@gmail.com, http://libtom.org */ /** @file crypt_find_cipher_any.c Find a cipher in the descriptor tables, Tom St Denis */ /** Find a cipher flexibly. First by name then if not present by block and key size @param name The name of the cipher desired @param blocklen The minimum length of the block cipher desired (octets) @param keylen The minimum length of the key size desired (octets) @return >= 0 if found, -1 if not present */ int find_cipher_any(const char *name, int blocklen, int keylen) { int x; LTC_ARGCHK(name != NULL); x = find_cipher(name); if (x != -1) return x; LTC_MUTEX_LOCK(<c_cipher_mutex); for (x = 0; x < TAB_SIZE; x++) { if (cipher_descriptor[x].name == NULL) { continue; } if ((blocklen <= (int)cipher_descriptor[x].block_length) && (keylen <= (int)cipher_descriptor[x].max_key_length)) { LTC_MUTEX_UNLOCK(<c_cipher_mutex); return x; } } LTC_MUTEX_UNLOCK(<c_cipher_mutex); return -1; } /* $Source: /cvs/libtom/libtomcrypt/src/misc/crypt/crypt_find_cipher_any.c,v $ */ /* $Revision: 1.6 $ */ /* $Date: 2006/12/28 01:27:24 $ */ /* LibTomCrypt, modular cryptographic library -- Tom St Denis * * LibTomCrypt is a library that provides various cryptographic * algorithms in a highly modular and flexible manner. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tomstdenis@gmail.com, http://libtom.org */ /** @file crypt_find_cipher_id.c Find cipher by ID, Tom St Denis */ /** Find a cipher by ID number @param ID The ID (not same as index) of the cipher to find @return >= 0 if found, -1 if not present */ int find_cipher_id(unsigned char ID) { int x; LTC_MUTEX_LOCK(<c_cipher_mutex); for (x = 0; x < TAB_SIZE; x++) { if (cipher_descriptor[x].ID == ID) { x = (cipher_descriptor[x].name == NULL) ? -1 : x; LTC_MUTEX_UNLOCK(<c_cipher_mutex); return x; } } LTC_MUTEX_UNLOCK(<c_cipher_mutex); return -1; } /* $Source: /cvs/libtom/libtomcrypt/src/misc/crypt/crypt_find_cipher_id.c,v $ */ /* $Revision: 1.6 $ */ /* $Date: 2006/12/28 01:27:24 $ */ /* LibTomCrypt, modular cryptographic library -- Tom St Denis * * LibTomCrypt is a library that provides various cryptographic * algorithms in a highly modular and flexible manner. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tomstdenis@gmail.com, http://libtom.org */ /** @file crypt_find_hash.c Find a hash, Tom St Denis */ /** Find a registered hash by name @param name The name of the hash to look for @return >= 0 if found, -1 if not present */ int find_hash(const char *name) { int x; LTC_ARGCHK(name != NULL); LTC_MUTEX_LOCK(<c_hash_mutex); for (x = 0; x < TAB_SIZE; x++) { if ((hash_descriptor[x].name != NULL) && (XSTRCMP(hash_descriptor[x].name, name) == 0)) { LTC_MUTEX_UNLOCK(<c_hash_mutex); return x; } } LTC_MUTEX_UNLOCK(<c_hash_mutex); return -1; } /* $Source: /cvs/libtom/libtomcrypt/src/misc/crypt/crypt_find_hash.c,v $ */ /* $Revision: 1.7 $ */ /* $Date: 2006/12/28 01:27:24 $ */ /* LibTomCrypt, modular cryptographic library -- Tom St Denis * * LibTomCrypt is a library that provides various cryptographic * algorithms in a highly modular and flexible manner. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tomstdenis@gmail.com, http://libtom.org */ /** @file crypt_find_hash_any.c Find a hash, Tom St Denis */ /** Find a hash flexibly. First by name then if not present by digest size @param name The name of the hash desired @param digestlen The minimum length of the digest size (octets) @return >= 0 if found, -1 if not present */int find_hash_any(const char *name, int digestlen) { int x, y, z; LTC_ARGCHK(name != NULL); x = find_hash(name); if (x != -1) return x; LTC_MUTEX_LOCK(<c_hash_mutex); y = MAXBLOCKSIZE + 1; z = -1; for (x = 0; x < TAB_SIZE; x++) { if (hash_descriptor[x].name == NULL) { continue; } if (((int)hash_descriptor[x].hashsize >= digestlen) && ((int)hash_descriptor[x].hashsize < y)) { z = x; y = hash_descriptor[x].hashsize; } } LTC_MUTEX_UNLOCK(<c_hash_mutex); return z; } /* $Source: /cvs/libtom/libtomcrypt/src/misc/crypt/crypt_find_hash_any.c,v $ */ /* $Revision: 1.6 $ */ /* $Date: 2006/12/28 01:27:24 $ */ /* LibTomCrypt, modular cryptographic library -- Tom St Denis * * LibTomCrypt is a library that provides various cryptographic * algorithms in a highly modular and flexible manner. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tomstdenis@gmail.com, http://libtom.org */ /** @file crypt_find_hash_id.c Find hash by ID, Tom St Denis */ /** Find a hash by ID number @param ID The ID (not same as index) of the hash to find @return >= 0 if found, -1 if not present */ int find_hash_id(unsigned char ID) { int x; LTC_MUTEX_LOCK(<c_hash_mutex); for (x = 0; x < TAB_SIZE; x++) { if (hash_descriptor[x].ID == ID) { x = (hash_descriptor[x].name == NULL) ? -1 : x; LTC_MUTEX_UNLOCK(<c_hash_mutex); return x; } } LTC_MUTEX_UNLOCK(<c_hash_mutex); return -1; } /* $Source: /cvs/libtom/libtomcrypt/src/misc/crypt/crypt_find_hash_id.c,v $ */ /* $Revision: 1.7 $ */ /* $Date: 2006/12/28 01:27:24 $ */ /* LibTomCrypt, modular cryptographic library -- Tom St Denis * * LibTomCrypt is a library that provides various cryptographic * algorithms in a highly modular and flexible manner. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tomstdenis@gmail.com, http://libtom.org */ /** @file crypt_find_hash_oid.c Find a hash, Tom St Denis */ int find_hash_oid(const unsigned long *ID, unsigned long IDlen) { int x; LTC_ARGCHK(ID != NULL); LTC_MUTEX_LOCK(<c_hash_mutex); for (x = 0; x < TAB_SIZE; x++) { if ((hash_descriptor[x].name != NULL) && (hash_descriptor[x].OIDlen == IDlen) && !XMEMCMP(hash_descriptor[x].OID, ID, sizeof(unsigned long) * IDlen)) { LTC_MUTEX_UNLOCK(<c_hash_mutex); return x; } } LTC_MUTEX_UNLOCK(<c_hash_mutex); return -1; } /* $Source: /cvs/libtom/libtomcrypt/src/misc/crypt/crypt_find_hash_oid.c,v $ */ /* $Revision: 1.5 $ */ /* $Date: 2006/12/28 01:27:24 $ */ /* LibTomCrypt, modular cryptographic library -- Tom St Denis * * LibTomCrypt is a library that provides various cryptographic * algorithms in a highly modular and flexible manner. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tomstdenis@gmail.com, http://libtom.org */ /** @file crypt_find_prng.c Find a PRNG, Tom St Denis */ /** Find a registered PRNG by name @param name The name of the PRNG to look for @return >= 0 if found, -1 if not present */ int find_prng(const char *name) { int x; LTC_ARGCHK(name != NULL); LTC_MUTEX_LOCK(<c_prng_mutex); for (x = 0; x < TAB_SIZE; x++) { if ((prng_descriptor[x].name != NULL) && (XSTRCMP(prng_descriptor[x].name, name) == 0)) { LTC_MUTEX_UNLOCK(<c_prng_mutex); return x; } } LTC_MUTEX_UNLOCK(<c_prng_mutex); return -1; } /* $Source: /cvs/libtom/libtomcrypt/src/misc/crypt/crypt_find_prng.c,v $ */ /* $Revision: 1.7 $ */ /* $Date: 2006/12/28 01:27:24 $ */ /* LibTomCrypt, modular cryptographic library -- Tom St Denis * * LibTomCrypt is a library that provides various cryptographic * algorithms in a highly modular and flexible manner. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tomstdenis@gmail.com, http://libtom.org */ #include /** @file crypt_fsa.c LibTomCrypt FULL SPEED AHEAD!, Tom St Denis */ /* format is ltc_mp, cipher_desc, [cipher_desc], NULL, hash_desc, [hash_desc], NULL, prng_desc, [prng_desc], NULL */ int crypt_fsa(void *mp, ...) { int err; va_list args; void *p; va_start(args, mp); if (mp != NULL) { XMEMCPY(<c_mp, mp, sizeof(ltc_mp)); } while ((p = va_arg(args, void *)) != NULL) { if ((err = register_cipher(AUTO_CAST(p))) != CRYPT_OK) { va_end(args); return err; } } while ((p = va_arg(args, void *)) != NULL) { if ((err = register_hash(AUTO_CAST(p))) != CRYPT_OK) { va_end(args); return err; } } while ((p = va_arg(args, void *)) != NULL) { if ((err = register_prng(AUTO_CAST(p))) != CRYPT_OK) { va_end(args); return err; } } va_end(args); return CRYPT_OK; } /* $Source: /cvs/libtom/libtomcrypt/src/misc/crypt/crypt_fsa.c,v $ */ /* $Revision: 1.5 $ */ /* $Date: 2006/12/28 01:27:24 $ */ /* LibTomCrypt, modular cryptographic library -- Tom St Denis * * LibTomCrypt is a library that provides various cryptographic * algorithms in a highly modular and flexible manner. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tomstdenis@gmail.com, http://libtom.org */ /** @file crypt_hash_descriptor.c Stores the hash descriptor table, Tom St Denis */ struct ltc_hash_descriptor hash_descriptor[TAB_SIZE] = { { NULL, 0, 0, 0, { 0 }, 0, NULL, NULL, NULL, NULL, NULL } }; LTC_MUTEX_GLOBAL(ltc_hash_mutex) /* $Source: /cvs/libtom/libtomcrypt/src/misc/crypt/crypt_hash_descriptor.c,v $ */ /* $Revision: 1.10 $ */ /* $Date: 2006/12/28 01:27:24 $ */ /* LibTomCrypt, modular cryptographic library -- Tom St Denis * * LibTomCrypt is a library that provides various cryptographic * algorithms in a highly modular and flexible manner. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tomstdenis@gmail.com, http://libtom.org */ /** @file crypt_hash_is_valid.c Determine if hash is valid, Tom St Denis */ /* Test if a hash index is valid @param idx The index of the hash to search for @return CRYPT_OK if valid */ int hash_is_valid(int idx) { LTC_MUTEX_LOCK(<c_hash_mutex); if ((idx < 0) || (idx >= TAB_SIZE) || (hash_descriptor[idx].name == NULL)) { LTC_MUTEX_UNLOCK(<c_hash_mutex); return CRYPT_INVALID_HASH; } LTC_MUTEX_UNLOCK(<c_hash_mutex); return CRYPT_OK; } /* $Source: /cvs/libtom/libtomcrypt/src/misc/crypt/crypt_hash_is_valid.c,v $ */ /* $Revision: 1.6 $ */ /* $Date: 2006/12/28 01:27:24 $ */ /* LibTomCrypt, modular cryptographic library -- Tom St Denis * * LibTomCrypt is a library that provides various cryptographic * algorithms in a highly modular and flexible manner. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tomstdenis@gmail.com, http://libtom.org */ ltc_math_descriptor ltc_mp; /* LibTomCrypt, modular cryptographic library -- Tom St Denis * * LibTomCrypt is a library that provides various cryptographic * algorithms in a highly modular and flexible manner. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tomstdenis@gmail.com, http://libtom.org */ /** @file crypt_prng_descriptor.c Stores the PRNG descriptors, Tom St Denis */ struct ltc_prng_descriptor prng_descriptor[TAB_SIZE] = { { NULL, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL } }; LTC_MUTEX_GLOBAL(ltc_prng_mutex) /* $Source: /cvs/libtom/libtomcrypt/src/misc/crypt/crypt_prng_descriptor.c,v $ */ /* $Revision: 1.8 $ */ /* $Date: 2006/12/28 01:27:24 $ */ /* LibTomCrypt, modular cryptographic library -- Tom St Denis * * LibTomCrypt is a library that provides various cryptographic * algorithms in a highly modular and flexible manner. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tomstdenis@gmail.com, http://libtom.org */ /** @file crypt_prng_is_valid.c Determine if PRNG is valid, Tom St Denis */ /* Test if a PRNG index is valid @param idx The index of the PRNG to search for @return CRYPT_OK if valid */ int prng_is_valid(int idx) { LTC_MUTEX_LOCK(<c_prng_mutex); if ((idx < 0) || (idx >= TAB_SIZE) || (prng_descriptor[idx].name == NULL)) { LTC_MUTEX_UNLOCK(<c_prng_mutex); return CRYPT_INVALID_PRNG; } LTC_MUTEX_UNLOCK(<c_prng_mutex); return CRYPT_OK; } /* $Source: /cvs/libtom/libtomcrypt/src/misc/crypt/crypt_prng_is_valid.c,v $ */ /* $Revision: 1.6 $ */ /* $Date: 2006/12/28 01:27:24 $ */ /* LibTomCrypt, modular cryptographic library -- Tom St Denis * * LibTomCrypt is a library that provides various cryptographic * algorithms in a highly modular and flexible manner. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tomstdenis@gmail.com, http://libtom.org */ /** @file crypt_register_cipher.c Register a cipher, Tom St Denis */ /** Register a cipher with the descriptor table @param cipher The cipher you wish to register @return value >= 0 if successfully added (or already present), -1 if unsuccessful */ int register_cipher(const struct ltc_cipher_descriptor *cipher) { int x; LTC_ARGCHK(cipher != NULL); /* is it already registered? */ LTC_MUTEX_LOCK(<c_cipher_mutex); for (x = 0; x < TAB_SIZE; x++) { if ((cipher_descriptor[x].name != NULL) && (cipher_descriptor[x].ID == cipher->ID)) { LTC_MUTEX_UNLOCK(<c_cipher_mutex); return x; } } /* find a blank spot */ for (x = 0; x < TAB_SIZE; x++) { if (cipher_descriptor[x].name == NULL) { XMEMCPY(&cipher_descriptor[x], cipher, sizeof(struct ltc_cipher_descriptor)); LTC_MUTEX_UNLOCK(<c_cipher_mutex); return x; } } /* no spot */ LTC_MUTEX_UNLOCK(<c_cipher_mutex); return -1; } /* $Source: /cvs/libtom/libtomcrypt/src/misc/crypt/crypt_register_cipher.c,v $ */ /* $Revision: 1.6 $ */ /* $Date: 2006/12/28 01:27:24 $ */ /* LibTomCrypt, modular cryptographic library -- Tom St Denis * * LibTomCrypt is a library that provides various cryptographic * algorithms in a highly modular and flexible manner. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tomstdenis@gmail.com, http://libtom.org */ /** @file crypt_register_hash.c Register a HASH, Tom St Denis */ /** Register a hash with the descriptor table @param hash The hash you wish to register @return value >= 0 if successfully added (or already present), -1 if unsuccessful */ int register_hash(const struct ltc_hash_descriptor *hash) { int x; LTC_ARGCHK(hash != NULL); /* is it already registered? */ LTC_MUTEX_LOCK(<c_hash_mutex); for (x = 0; x < TAB_SIZE; x++) { if (XMEMCMP(&hash_descriptor[x], hash, sizeof(struct ltc_hash_descriptor)) == 0) { LTC_MUTEX_UNLOCK(<c_hash_mutex); return x; } } /* find a blank spot */ for (x = 0; x < TAB_SIZE; x++) { if (hash_descriptor[x].name == NULL) { XMEMCPY(&hash_descriptor[x], hash, sizeof(struct ltc_hash_descriptor)); LTC_MUTEX_UNLOCK(<c_hash_mutex); return x; } } /* no spot */ LTC_MUTEX_UNLOCK(<c_hash_mutex); return -1; } /* $Source: /cvs/libtom/libtomcrypt/src/misc/crypt/crypt_register_hash.c,v $ */ /* $Revision: 1.7 $ */ /* $Date: 2006/12/28 01:27:24 $ */ /* LibTomCrypt, modular cryptographic library -- Tom St Denis * * LibTomCrypt is a library that provides various cryptographic * algorithms in a highly modular and flexible manner. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tomstdenis@gmail.com, http://libtom.org */ /** @file crypt_register_prng.c Register a PRNG, Tom St Denis */ /** Register a PRNG with the descriptor table @param prng The PRNG you wish to register @return value >= 0 if successfully added (or already present), -1 if unsuccessful */ int register_prng(const struct ltc_prng_descriptor *prng) { int x; LTC_ARGCHK(prng != NULL); /* is it already registered? */ LTC_MUTEX_LOCK(<c_prng_mutex); for (x = 0; x < TAB_SIZE; x++) { if (XMEMCMP(&prng_descriptor[x], prng, sizeof(struct ltc_prng_descriptor)) == 0) { LTC_MUTEX_UNLOCK(<c_prng_mutex); return x; } } /* find a blank spot */ for (x = 0; x < TAB_SIZE; x++) { if (prng_descriptor[x].name == NULL) { XMEMCPY(&prng_descriptor[x], prng, sizeof(struct ltc_prng_descriptor)); LTC_MUTEX_UNLOCK(<c_prng_mutex); return x; } } /* no spot */ LTC_MUTEX_UNLOCK(<c_prng_mutex); return -1; } /* $Source: /cvs/libtom/libtomcrypt/src/misc/crypt/crypt_register_prng.c,v $ */ /* $Revision: 1.8 $ */ /* $Date: 2006/12/28 01:27:24 $ */ /* LibTomCrypt, modular cryptographic library -- Tom St Denis * * LibTomCrypt is a library that provides various cryptographic * algorithms in a highly modular and flexible manner. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tomstdenis@gmail.com, http://libtom.org */ /** @file crypt_unregister_cipher.c Unregister a cipher, Tom St Denis */ /** Unregister a cipher from the descriptor table @param cipher The cipher descriptor to remove @return CRYPT_OK on success */ int unregister_cipher(const struct ltc_cipher_descriptor *cipher) { int x; LTC_ARGCHK(cipher != NULL); /* is it already registered? */ LTC_MUTEX_LOCK(<c_cipher_mutex); for (x = 0; x < TAB_SIZE; x++) { if (XMEMCMP(&cipher_descriptor[x], cipher, sizeof(struct ltc_cipher_descriptor)) == 0) { cipher_descriptor[x].name = NULL; cipher_descriptor[x].ID = 255; LTC_MUTEX_UNLOCK(<c_cipher_mutex); return CRYPT_OK; } } LTC_MUTEX_UNLOCK(<c_cipher_mutex); return CRYPT_ERROR; } /* $Source: /cvs/libtom/libtomcrypt/src/misc/crypt/crypt_unregister_cipher.c,v $ */ /* $Revision: 1.7 $ */ /* $Date: 2006/12/28 01:27:24 $ */ /* LibTomCrypt, modular cryptographic library -- Tom St Denis * * LibTomCrypt is a library that provides various cryptographic * algorithms in a highly modular and flexible manner. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tomstdenis@gmail.com, http://libtom.org */ /** @file crypt_unregister_hash.c Unregister a hash, Tom St Denis */ /** Unregister a hash from the descriptor table @param hash The hash descriptor to remove @return CRYPT_OK on success */ int unregister_hash(const struct ltc_hash_descriptor *hash) { int x; LTC_ARGCHK(hash != NULL); /* is it already registered? */ LTC_MUTEX_LOCK(<c_hash_mutex); for (x = 0; x < TAB_SIZE; x++) { if (XMEMCMP(&hash_descriptor[x], hash, sizeof(struct ltc_hash_descriptor)) == 0) { hash_descriptor[x].name = NULL; LTC_MUTEX_UNLOCK(<c_hash_mutex); return CRYPT_OK; } } LTC_MUTEX_UNLOCK(<c_hash_mutex); return CRYPT_ERROR; } /* $Source: /cvs/libtom/libtomcrypt/src/misc/crypt/crypt_unregister_hash.c,v $ */ /* $Revision: 1.7 $ */ /* $Date: 2006/12/28 01:27:24 $ */ /* LibTomCrypt, modular cryptographic library -- Tom St Denis * * LibTomCrypt is a library that provides various cryptographic * algorithms in a highly modular and flexible manner. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tomstdenis@gmail.com, http://libtom.org */ /** @file crypt_unregister_prng.c Unregister a PRNG, Tom St Denis */ /** Unregister a PRNG from the descriptor table @param prng The PRNG descriptor to remove @return CRYPT_OK on success */ int unregister_prng(const struct ltc_prng_descriptor *prng) { int x; LTC_ARGCHK(prng != NULL); /* is it already registered? */ LTC_MUTEX_LOCK(<c_prng_mutex); for (x = 0; x < TAB_SIZE; x++) { if (XMEMCMP(&prng_descriptor[x], prng, sizeof(struct ltc_prng_descriptor)) != 0) { prng_descriptor[x].name = NULL; LTC_MUTEX_UNLOCK(<c_prng_mutex); return CRYPT_OK; } } LTC_MUTEX_UNLOCK(<c_prng_mutex); return CRYPT_ERROR; } /* $Source: /cvs/libtom/libtomcrypt/src/misc/crypt/crypt_unregister_prng.c,v $ */ /* $Revision: 1.7 $ */ /* $Date: 2006/12/28 01:27:24 $ */ /* LibTomCrypt, modular cryptographic library -- Tom St Denis * * LibTomCrypt is a library that provides various cryptographic * algorithms in a highly modular and flexible manner. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tomstdenis@gmail.com, http://libtom.org */ /** @file der_decode_bit_string.c ASN.1 DER, encode a BIT STRING, Tom St Denis */ #ifdef LTC_DER /** Store a BIT STRING @param in The DER encoded BIT STRING @param inlen The size of the DER BIT STRING @param out [out] The array of bits stored (one per char) @param outlen [in/out] The number of bits stored @return CRYPT_OK if successful */ int der_decode_bit_string(const unsigned char *in, unsigned long inlen, unsigned char *out, unsigned long *outlen) { unsigned long dlen, blen, x, y; LTC_ARGCHK(in != NULL); LTC_ARGCHK(out != NULL); LTC_ARGCHK(outlen != NULL); /* packet must be at least 4 bytes */ if (inlen < 4) { return CRYPT_INVALID_ARG; } /* check for 0x03 */ if ((in[0] & 0x1F) != 0x03) { return CRYPT_INVALID_PACKET; } /* offset in the data */ x = 1; /* get the length of the data */ if (in[x] & 0x80) { /* long format get number of length bytes */ y = in[x++] & 0x7F; /* invalid if 0 or > 2 */ if ((y == 0) || (y > 2)) { return CRYPT_INVALID_PACKET; } /* read the data len */ dlen = 0; while (y--) { dlen = (dlen << 8) | (unsigned long)in[x++]; } } else { /* short format */ dlen = in[x++] & 0x7F; } /* is the data len too long or too short? */ if ((dlen == 0) || (dlen + x > inlen)) { return CRYPT_INVALID_PACKET; } /* get padding count */ blen = ((dlen - 1) << 3) - (in[x++] & 7); /* too many bits? */ if (blen > *outlen) { *outlen = blen; return CRYPT_BUFFER_OVERFLOW; } /* decode/store the bits */ for (y = 0; y < blen; y++) { out[y] = (in[x] & (1 << (7 - (y & 7)))) ? 1 : 0; if ((y & 7) == 7) { ++x; } } /* we done */ *outlen = blen; return CRYPT_OK; } #endif /* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/bit/der_decode_bit_string.c,v $ */ /* $Revision: 1.5 $ */ /* $Date: 2006/12/28 01:27:24 $ */ /* LibTomCrypt, modular cryptographic library -- Tom St Denis * * LibTomCrypt is a library that provides various cryptographic * algorithms in a highly modular and flexible manner. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tomstdenis@gmail.com, http://libtom.org */ /** @file der_decode_boolean.c ASN.1 DER, decode a BOOLEAN, Tom St Denis */ #ifdef LTC_DER /** Read a BOOLEAN @param in The destination for the DER encoded BOOLEAN @param inlen The size of the DER BOOLEAN @param out [out] The boolean to decode @return CRYPT_OK if successful */ int der_decode_boolean(const unsigned char *in, unsigned long inlen, int *out) { LTC_ARGCHK(in != NULL); LTC_ARGCHK(out != NULL); if ((inlen != 3) || (in[0] != 0x01) || (in[1] != 0x01) || ((in[2] != 0x00) && (in[2] != 0xFF))) { return CRYPT_INVALID_ARG; } *out = (in[2] == 0xFF) ? 1 : 0; return CRYPT_OK; } #endif /* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/boolean/der_decode_boolean.c,v $ */ /* $Revision: 1.2 $ */ /* $Date: 2006/12/28 01:27:24 $ */ /* LibTomCrypt, modular cryptographic library -- Tom St Denis * * LibTomCrypt is a library that provides various cryptographic * algorithms in a highly modular and flexible manner. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tomstdenis@gmail.com, http://libtom.org */ /** @file der_decode_choice.c ASN.1 DER, decode a CHOICE, Tom St Denis */ #ifdef LTC_DER /** Decode a CHOICE @param in The DER encoded input @param inlen [in/out] The size of the input and resulting size of read type @param list The list of items to decode @param outlen The number of items in the list @return CRYPT_OK on success */ int der_decode_choice(const unsigned char *in, unsigned long *inlen, ltc_asn1_list *list, unsigned long outlen) { unsigned long size, x, z; void *data; LTC_ARGCHK(in != NULL); LTC_ARGCHK(inlen != NULL); LTC_ARGCHK(list != NULL); /* get blk size */ if (*inlen < 2) { return CRYPT_INVALID_PACKET; } /* set all of the "used" flags to zero */ for (x = 0; x < outlen; x++) { list[x].used = 0; } /* now scan until we have a winner */ for (x = 0; x < outlen; x++) { size = list[x].size; data = list[x].data; switch (list[x].type) { case LTC_ASN1_INTEGER: if (der_decode_integer(in, *inlen, data) == CRYPT_OK) { if (der_length_integer(data, &z) == CRYPT_OK) { list[x].used = 1; *inlen = z; return CRYPT_OK; } } break; case LTC_ASN1_SHORT_INTEGER: if (der_decode_short_integer(in, *inlen, (unsigned long *)data) == CRYPT_OK) { if (der_length_short_integer(size, &z) == CRYPT_OK) { list[x].used = 1; *inlen = z; return CRYPT_OK; } } break; case LTC_ASN1_BIT_STRING: if (der_decode_bit_string(in, *inlen, (unsigned char *)data, &size) == CRYPT_OK) { if (der_length_bit_string(size, &z) == CRYPT_OK) { list[x].used = 1; list[x].size = size; *inlen = z; return CRYPT_OK; } } break; case LTC_ASN1_OCTET_STRING: if (der_decode_octet_string(in, *inlen, AUTO_CAST(data), &size) == CRYPT_OK) { if (der_length_octet_string(size, &z) == CRYPT_OK) { list[x].used = 1; list[x].size = size; *inlen = z; return CRYPT_OK; } } break; case LTC_ASN1_NULL: if ((*inlen == 2) && (in[x] == 0x05) && (in[x + 1] == 0x00)) { *inlen = 2; list[x].used = 1; return CRYPT_OK; } break; case LTC_ASN1_OBJECT_IDENTIFIER: if (der_decode_object_identifier(in, *inlen, AUTO_CAST(data), &size) == CRYPT_OK) { if (der_length_object_identifier(AUTO_CAST(data), size, &z) == CRYPT_OK) { list[x].used = 1; list[x].size = size; *inlen = z; return CRYPT_OK; } } break; case LTC_ASN1_IA5_STRING: if (der_decode_ia5_string(in, *inlen, AUTO_CAST(data), &size) == CRYPT_OK) { if (der_length_ia5_string(AUTO_CAST(data), size, &z) == CRYPT_OK) { list[x].used = 1; list[x].size = size; *inlen = z; return CRYPT_OK; } } break; case LTC_ASN1_PRINTABLE_STRING: if (der_decode_printable_string(in, *inlen, AUTO_CAST(data), &size) == CRYPT_OK) { if (der_length_printable_string(AUTO_CAST(data), size, &z) == CRYPT_OK) { list[x].used = 1; list[x].size = size; *inlen = z; return CRYPT_OK; } } break; case LTC_ASN1_UTF8_STRING: if (der_decode_utf8_string(in, *inlen, AUTO_CAST(data), &size) == CRYPT_OK) { if (der_length_utf8_string(AUTO_CAST(data), size, &z) == CRYPT_OK) { list[x].used = 1; list[x].size = size; *inlen = z; return CRYPT_OK; } } break; case LTC_ASN1_UTCTIME: z = *inlen; if (der_decode_utctime(in, &z, AUTO_CAST(data)) == CRYPT_OK) { list[x].used = 1; *inlen = z; return CRYPT_OK; } break; case LTC_ASN1_SET: case LTC_ASN1_SETOF: case LTC_ASN1_SEQUENCE: if (der_decode_sequence(in, *inlen, AUTO_CAST(data), size) == CRYPT_OK) { if (der_length_sequence(AUTO_CAST(data), size, &z) == CRYPT_OK) { list[x].used = 1; *inlen = z; return CRYPT_OK; } } break; default: return CRYPT_INVALID_ARG; } } return CRYPT_INVALID_PACKET; } #endif /* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/choice/der_decode_choice.c,v $ */ /* $Revision: 1.9 $ */ /* $Date: 2006/12/28 01:27:24 $ */ /* LibTomCrypt, modular cryptographic library -- Tom St Denis * * LibTomCrypt is a library that provides various cryptographic * algorithms in a highly modular and flexible manner. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tomstdenis@gmail.com, http://libtom.org */ /** @file der_decode_ia5_string.c ASN.1 DER, encode a IA5 STRING, Tom St Denis */ #ifdef LTC_DER /** Store a IA5 STRING @param in The DER encoded IA5 STRING @param inlen The size of the DER IA5 STRING @param out [out] The array of octets stored (one per char) @param outlen [in/out] The number of octets stored @return CRYPT_OK if successful */ int der_decode_ia5_string(const unsigned char *in, unsigned long inlen, unsigned char *out, unsigned long *outlen) { unsigned long x, y, len; int t; LTC_ARGCHK(in != NULL); LTC_ARGCHK(out != NULL); LTC_ARGCHK(outlen != NULL); /* must have header at least */ if (inlen < 2) { return CRYPT_INVALID_PACKET; } /* check for 0x16 */ if ((in[0] & 0x1F) != 0x16) { return CRYPT_INVALID_PACKET; } x = 1; /* decode the length */ if (in[x] & 0x80) { /* valid # of bytes in length are 1,2,3 */ y = in[x] & 0x7F; if ((y == 0) || (y > 3) || ((x + y) > inlen)) { return CRYPT_INVALID_PACKET; } /* read the length in */ len = 0; ++x; while (y--) { len = (len << 8) | in[x++]; } } else { len = in[x++] & 0x7F; } /* is it too long? */ if (len > *outlen) { *outlen = len; return CRYPT_BUFFER_OVERFLOW; } if (len + x > inlen) { return CRYPT_INVALID_PACKET; } /* read the data */ for (y = 0; y < len; y++) { t = der_ia5_value_decode(in[x++]); if (t == -1) { return CRYPT_INVALID_ARG; } out[y] = t; } *outlen = y; return CRYPT_OK; } #endif /* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/ia5/der_decode_ia5_string.c,v $ */ /* $Revision: 1.4 $ */ /* $Date: 2006/12/28 01:27:24 $ */ /* LibTomCrypt, modular cryptographic library -- Tom St Denis * * LibTomCrypt is a library that provides various cryptographic * algorithms in a highly modular and flexible manner. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tomstdenis@gmail.com, http://libtom.org */ /** @file der_decode_integer.c ASN.1 DER, decode an integer, Tom St Denis */ #ifdef LTC_DER /** Read a mp_int integer @param in The DER encoded data @param inlen Size of DER encoded data @param num The first mp_int to decode @return CRYPT_OK if successful */ int der_decode_integer(const unsigned char *in, unsigned long inlen, void *num) { unsigned long x, y, z; int err; LTC_ARGCHK(num != NULL); LTC_ARGCHK(in != NULL); /* min DER INTEGER is 0x02 01 00 == 0 */ if (inlen < (1 + 1 + 1)) { return CRYPT_INVALID_PACKET; } /* ok expect 0x02 when we AND with 0001 1111 [1F] */ x = 0; if ((in[x++] & 0x1F) != 0x02) { return CRYPT_INVALID_PACKET; } /* now decode the len stuff */ z = in[x++]; if ((z & 0x80) == 0x00) { /* short form */ /* will it overflow? */ if (x + z > inlen) { return CRYPT_INVALID_PACKET; } /* no so read it */ if ((err = mp_read_unsigned_bin(num, (unsigned char *)in + x, z)) != CRYPT_OK) { return err; } } else { /* long form */ z &= 0x7F; /* will number of length bytes overflow? (or > 4) */ if (((x + z) > inlen) || (z > 4) || (z == 0)) { return CRYPT_INVALID_PACKET; } /* now read it in */ y = 0; while (z--) { y = ((unsigned long)(in[x++])) | (y << 8); } /* now will reading y bytes overrun? */ if ((x + y) > inlen) { return CRYPT_INVALID_PACKET; } /* no so read it */ if ((err = mp_read_unsigned_bin(num, (unsigned char *)in + x, y)) != CRYPT_OK) { return err; } } /* see if it's negative */ if (in[x] & 0x80) { void *tmp; if (mp_init(&tmp) != CRYPT_OK) { return CRYPT_MEM; } if ((mp_2expt(tmp, mp_count_bits(num)) != CRYPT_OK) || (mp_sub(num, tmp, num) != CRYPT_OK)) { mp_clear(tmp); return CRYPT_MEM; } mp_clear(tmp); } return CRYPT_OK; } #endif /* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/integer/der_decode_integer.c,v $ */ /* $Revision: 1.5 $ */ /* $Date: 2006/12/28 01:27:24 $ */ /* LibTomCrypt, modular cryptographic library -- Tom St Denis * * LibTomCrypt is a library that provides various cryptographic * algorithms in a highly modular and flexible manner. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tomstdenis@gmail.com, http://libtom.org */ /** @file der_decode_object_identifier.c ASN.1 DER, Decode Object Identifier, Tom St Denis */ #ifdef LTC_DER /** Decode OID data and store the array of integers in words @param in The OID DER encoded data @param inlen The length of the OID data @param words [out] The destination of the OID words @param outlen [in/out] The number of OID words @return CRYPT_OK if successful */ int der_decode_object_identifier(const unsigned char *in, unsigned long inlen, unsigned long *words, unsigned long *outlen) { unsigned long x, y, t, len; LTC_ARGCHK(in != NULL); LTC_ARGCHK(words != NULL); LTC_ARGCHK(outlen != NULL); /* header is at least 3 bytes */ if (inlen < 3) { return CRYPT_INVALID_PACKET; } /* must be room for at least two words */ if (*outlen < 2) { return CRYPT_BUFFER_OVERFLOW; } /* decode the packet header */ x = 0; if ((in[x++] & 0x1F) != 0x06) { return CRYPT_INVALID_PACKET; } /* get the length */ if (in[x] < 128) { len = in[x++]; } else { if ((in[x] < 0x81) || (in[x] > 0x82)) { return CRYPT_INVALID_PACKET; } y = in[x++] & 0x7F; len = 0; while (y--) { len = (len << 8) | (unsigned long)in[x++]; } } if ((len < 1) || ((len + x) > inlen)) { return CRYPT_INVALID_PACKET; } /* decode words */ y = 0; t = 0; while (len--) { t = (t << 7) | (in[x] & 0x7F); if (!(in[x++] & 0x80)) { /* store t */ if (y >= *outlen) { return CRYPT_BUFFER_OVERFLOW; } if (y == 0) { words[0] = t / 40; words[1] = t % 40; y = 2; } else { words[y++] = t; } t = 0; } } *outlen = y; return CRYPT_OK; } #endif /* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/object_identifier/der_decode_object_identifier.c,v $ */ /* $Revision: 1.6 $ */ /* $Date: 2006/12/28 01:27:24 $ */ /* LibTomCrypt, modular cryptographic library -- Tom St Denis * * LibTomCrypt is a library that provides various cryptographic * algorithms in a highly modular and flexible manner. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tomstdenis@gmail.com, http://libtom.org */ /** @file der_decode_octet_string.c ASN.1 DER, encode a OCTET STRING, Tom St Denis */ #ifdef LTC_DER /** Store a OCTET STRING @param in The DER encoded OCTET STRING @param inlen The size of the DER OCTET STRING @param out [out] The array of octets stored (one per char) @param outlen [in/out] The number of octets stored @return CRYPT_OK if successful */ int der_decode_octet_string(const unsigned char *in, unsigned long inlen, unsigned char *out, unsigned long *outlen) { unsigned long x, y, len; LTC_ARGCHK(in != NULL); LTC_ARGCHK(out != NULL); LTC_ARGCHK(outlen != NULL); /* must have header at least */ if (inlen < 2) { return CRYPT_INVALID_PACKET; } /* check for 0x04 */ if ((in[0] & 0x1F) != 0x04) { return CRYPT_INVALID_PACKET; } x = 1; /* decode the length */ if (in[x] & 0x80) { /* valid # of bytes in length are 1,2,3 */ y = in[x] & 0x7F; if ((y == 0) || (y > 3) || ((x + y) > inlen)) { return CRYPT_INVALID_PACKET; } /* read the length in */ len = 0; ++x; while (y--) { len = (len << 8) | in[x++]; } } else { len = in[x++] & 0x7F; } /* is it too long? */ if (len > *outlen) { *outlen = len; return CRYPT_BUFFER_OVERFLOW; } if (len + x > inlen) { return CRYPT_INVALID_PACKET; } /* read the data */ for (y = 0; y < len; y++) { out[y] = in[x++]; } *outlen = y; return CRYPT_OK; } #endif /* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/octet/der_decode_octet_string.c,v $ */ /* $Revision: 1.4 $ */ /* $Date: 2006/12/28 01:27:24 $ */ /* LibTomCrypt, modular cryptographic library -- Tom St Denis * * LibTomCrypt is a library that provides various cryptographic * algorithms in a highly modular and flexible manner. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tomstdenis@gmail.com, http://libtom.org */ /** @file der_decode_printable_string.c ASN.1 DER, encode a printable STRING, Tom St Denis */ #ifdef LTC_DER /** Store a printable STRING @param in The DER encoded printable STRING @param inlen The size of the DER printable STRING @param out [out] The array of octets stored (one per char) @param outlen [in/out] The number of octets stored @return CRYPT_OK if successful */ int der_decode_printable_string(const unsigned char *in, unsigned long inlen, unsigned char *out, unsigned long *outlen) { unsigned long x, y, len; int t; LTC_ARGCHK(in != NULL); LTC_ARGCHK(out != NULL); LTC_ARGCHK(outlen != NULL); /* must have header at least */ if (inlen < 2) { return CRYPT_INVALID_PACKET; } /* check for 0x13 */ if ((in[0] & 0x1F) != 0x13) { return CRYPT_INVALID_PACKET; } x = 1; /* decode the length */ if (in[x] & 0x80) { /* valid # of bytes in length are 1,2,3 */ y = in[x] & 0x7F; if ((y == 0) || (y > 3) || ((x + y) > inlen)) { return CRYPT_INVALID_PACKET; } /* read the length in */ len = 0; ++x; while (y--) { len = (len << 8) | in[x++]; } } else { len = in[x++] & 0x7F; } /* is it too long? */ if (len > *outlen) { *outlen = len; return CRYPT_BUFFER_OVERFLOW; } if (len + x > inlen) { return CRYPT_INVALID_PACKET; } /* read the data */ for (y = 0; y < len; y++) { t = der_printable_value_decode(in[x++]); if (t == -1) { return CRYPT_INVALID_ARG; } out[y] = t; } *outlen = y; return CRYPT_OK; } #endif /* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/printable_string/der_decode_printable_string.c,v $ */ /* $Revision: 1.4 $ */ /* $Date: 2006/12/28 01:27:24 $ */ /* LibTomCrypt, modular cryptographic library -- Tom St Denis * * LibTomCrypt is a library that provides various cryptographic * algorithms in a highly modular and flexible manner. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tomstdenis@gmail.com, http://libtom.org */ #include /** @file der_decode_sequence_ex.c ASN.1 DER, decode a SEQUENCE, Tom St Denis */ #ifdef LTC_DER /** Decode a SEQUENCE @param in The DER encoded input @param inlen The size of the input @param list The list of items to decode @param outlen The number of items in the list @param ordered Search an unordeded or ordered list @return CRYPT_OK on success */ int der_decode_sequence_ex(const unsigned char *in, unsigned long inlen, ltc_asn1_list *list, unsigned long outlen, int ordered) { int err, type; unsigned long size, x, y, z, i, blksize; void *data; LTC_ARGCHK(in != NULL); LTC_ARGCHK(list != NULL); /* get blk size */ if (inlen < 2) { return CRYPT_INVALID_PACKET; } blksize = 0; /* sequence type? We allow 0x30 SEQUENCE and 0x31 SET since fundamentally they're the same structure */ x = 0; if ((in[x] != 0x30) && (in[x] != 0x31)) { return CRYPT_INVALID_PACKET; } ++x; if (in[x] < 128) { blksize = in[x++]; } else if (in[x] & 0x80) { if ((in[x] < 0x81) || (in[x] > 0x83)) { return CRYPT_INVALID_PACKET; } y = in[x++] & 0x7F; /* would reading the len bytes overrun? */ if (x + y > inlen) { return CRYPT_INVALID_PACKET; } /* read len */ blksize = 0; while (y--) { blksize = (blksize << 8) | (unsigned long)in[x++]; } } /* would this blksize overflow? */ if (x + blksize > inlen) { return CRYPT_INVALID_PACKET; } /* mark all as unused */ for (i = 0; i < outlen; i++) { list[i].used = 0; } /* ok read data */ inlen = blksize; for (i = 0; i < outlen; i++) { z = 0; type = list[i].type; size = list[i].size; data = list[i].data; if (!ordered && (list[i].used == 1)) { continue; } if (type == LTC_ASN1_EOL) { break; } switch (type) { case LTC_ASN1_BOOLEAN: z = inlen; if ((err = der_decode_boolean(in + x, z, ((int *)data))) != CRYPT_OK) { goto LBL_ERR; } if ((err = der_length_boolean(&z)) != CRYPT_OK) { goto LBL_ERR; } break; case LTC_ASN1_INTEGER: z = inlen; if ((err = der_decode_integer(in + x, z, data)) != CRYPT_OK) { if (!ordered) { continue; } goto LBL_ERR; } if ((err = der_length_integer(data, &z)) != CRYPT_OK) { goto LBL_ERR; } break; case LTC_ASN1_SHORT_INTEGER: z = inlen; if ((err = der_decode_short_integer(in + x, z, AUTO_CAST(data))) != CRYPT_OK) { if (!ordered) { continue; } goto LBL_ERR; } if ((err = der_length_short_integer(((unsigned long *)data)[0], &z)) != CRYPT_OK) { goto LBL_ERR; } break; case LTC_ASN1_BIT_STRING: z = inlen; if ((err = der_decode_bit_string(in + x, z, AUTO_CAST(data), &size)) != CRYPT_OK) { if (!ordered) { continue; } goto LBL_ERR; } list[i].size = size; if ((err = der_length_bit_string(size, &z)) != CRYPT_OK) { goto LBL_ERR; } break; case LTC_ASN1_OCTET_STRING: z = inlen; if ((err = der_decode_octet_string(in + x, z, AUTO_CAST(data), &size)) != CRYPT_OK) { if (!ordered) { continue; } goto LBL_ERR; } list[i].size = size; if ((err = der_length_octet_string(size, &z)) != CRYPT_OK) { goto LBL_ERR; } break; case LTC_ASN1_NULL: if ((inlen < 2) || (in[x] != 0x05) || (in[x + 1] != 0x00)) { if (!ordered) { continue; } err = CRYPT_INVALID_PACKET; goto LBL_ERR; } z = 2; break; case LTC_ASN1_OBJECT_IDENTIFIER: z = inlen; if ((err = der_decode_object_identifier(in + x, z, AUTO_CAST(data), &size)) != CRYPT_OK) { if (!ordered) { continue; } goto LBL_ERR; } list[i].size = size; if ((err = der_length_object_identifier(AUTO_CAST(data), size, &z)) != CRYPT_OK) { goto LBL_ERR; } break; case LTC_ASN1_IA5_STRING: z = inlen; if ((err = der_decode_ia5_string(in + x, z, AUTO_CAST(data), &size)) != CRYPT_OK) { if (!ordered) { continue; } goto LBL_ERR; } list[i].size = size; if ((err = der_length_ia5_string(AUTO_CAST(data), size, &z)) != CRYPT_OK) { goto LBL_ERR; } break; case LTC_ASN1_PRINTABLE_STRING: z = inlen; if ((err = der_decode_printable_string(in + x, z, AUTO_CAST(data), &size)) != CRYPT_OK) { if (!ordered) { continue; } goto LBL_ERR; } list[i].size = size; if ((err = der_length_printable_string(AUTO_CAST(data), size, &z)) != CRYPT_OK) { goto LBL_ERR; } break; case LTC_ASN1_UTF8_STRING: z = inlen; if ((err = der_decode_utf8_string(in + x, z, AUTO_CAST(data), &size)) != CRYPT_OK) { if (!ordered) { continue; } goto LBL_ERR; } list[i].size = size; if ((err = der_length_utf8_string(AUTO_CAST(data), size, &z)) != CRYPT_OK) { goto LBL_ERR; } break; case LTC_ASN1_UTCTIME: z = inlen; if ((err = der_decode_utctime(in + x, &z, AUTO_CAST(data))) != CRYPT_OK) { if (!ordered) { continue; } goto LBL_ERR; } break; case LTC_ASN1_SET: z = inlen; if ((err = der_decode_set(in + x, z, AUTO_CAST(data), size)) != CRYPT_OK) { if (!ordered) { continue; } goto LBL_ERR; } if ((err = der_length_sequence(AUTO_CAST(data), size, &z)) != CRYPT_OK) { goto LBL_ERR; } break; case LTC_ASN1_SETOF: case LTC_ASN1_SEQUENCE: /* detect if we have the right type */ if (((type == LTC_ASN1_SETOF) && ((in[x] & 0x3F) != 0x31)) || ((type == LTC_ASN1_SEQUENCE) && ((in[x] & 0x3F) != 0x30))) { err = CRYPT_INVALID_PACKET; goto LBL_ERR; } z = inlen; if ((err = der_decode_sequence(in + x, z, AUTO_CAST(data), size)) != CRYPT_OK) { if (!ordered) { continue; } goto LBL_ERR; } if ((err = der_length_sequence(AUTO_CAST(data), size, &z)) != CRYPT_OK) { goto LBL_ERR; } break; case LTC_ASN1_CHOICE: z = inlen; if ((err = der_decode_choice(in + x, &z, AUTO_CAST(data), size)) != CRYPT_OK) { if (!ordered) { continue; } goto LBL_ERR; } break; default: err = CRYPT_INVALID_ARG; goto LBL_ERR; } x += z; inlen -= z; list[i].used = 1; if (!ordered) { /* restart the decoder */ i = -1; } } for (i = 0; i < outlen; i++) { if (list[i].used == 0) { err = CRYPT_INVALID_PACKET; goto LBL_ERR; } } err = CRYPT_OK; LBL_ERR: return err; } #endif /* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/sequence/der_decode_sequence_ex.c,v $ */ /* $Revision: 1.16 $ */ /* $Date: 2006/12/28 01:27:24 $ */ /* LibTomCrypt, modular cryptographic library -- Tom St Denis * * LibTomCrypt is a library that provides various cryptographic * algorithms in a highly modular and flexible manner. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tomstdenis@gmail.com, http://libtom.org */ /** @file der_decode_sequence_flexi.c ASN.1 DER, decode an array of ASN.1 types with a flexi parser, Tom St Denis */ #ifdef LTC_DER static unsigned long fetch_length(const unsigned char *in, unsigned long inlen) { unsigned long x, y, z; y = 0; /* skip type and read len */ if (inlen < 2) { return 0xFFFFFFFF; } ++in; ++y; /* read len */ x = *in++; ++y; /* <128 means literal */ if (x < 128) { return x + y; } x &= 0x7F; /* the lower 7 bits are the length of the length */ inlen -= 2; /* len means len of len! */ if ((x == 0) || (x > 4) || (x > inlen)) { return 0xFFFFFFFF; } y += x; z = 0; while (x--) { z = (z << 8) | ((unsigned long)*in); ++in; } return z + y; } /** ASN.1 DER Flexi(ble) decoder will decode arbitrary DER packets and create a linked list of the decoded elements. @param in The input buffer @param inlen [in/out] The length of the input buffer and on output the amount of decoded data @param out [out] A pointer to the linked list @return CRYPT_OK on success. */ int der_decode_sequence_flexi(const unsigned char *in, unsigned long *inlen, ltc_asn1_list **out) { ltc_asn1_list *l; unsigned long err, type, len, totlen, x, y; void *realloc_tmp; LTC_ARGCHK(in != NULL); LTC_ARGCHK(inlen != NULL); LTC_ARGCHK(out != NULL); l = NULL; totlen = 0; /* scan the input and and get lengths and what not */ while (*inlen) { /* read the type byte */ type = *in; /* fetch length */ len = fetch_length(in, *inlen); if (len > *inlen) { err = CRYPT_INVALID_PACKET; goto error; } /* alloc new link */ if (l == NULL) { l = AUTO_CAST(XCALLOC(1, sizeof(*l))); if (l == NULL) { err = CRYPT_MEM; goto error; } } else { l->next = AUTO_CAST(XCALLOC(1, sizeof(*l))); if (l->next == NULL) { err = CRYPT_MEM; goto error; } l->next->prev = l; l = l->next; } /* now switch on type */ switch (type) { case 0x01: /* BOOLEAN */ l->type = LTC_ASN1_BOOLEAN; l->size = 1; l->data = XCALLOC(1, sizeof(int)); if ((err = der_decode_boolean(in, *inlen, AUTO_CAST(l->data))) != CRYPT_OK) { goto error; } if ((err = der_length_boolean(&len)) != CRYPT_OK) { goto error; } break; case 0x02: /* INTEGER */ /* init field */ l->type = LTC_ASN1_INTEGER; l->size = 1; if ((err = mp_init(&l->data)) != CRYPT_OK) { goto error; } /* decode field */ if ((err = der_decode_integer(in, *inlen, l->data)) != CRYPT_OK) { goto error; } /* calc length of object */ if ((err = der_length_integer(l->data, &len)) != CRYPT_OK) { goto error; } break; case 0x03: /* BIT */ /* init field */ l->type = LTC_ASN1_BIT_STRING; l->size = len * 8; /* *8 because we store decoded bits one per char and they are encoded 8 per char. */ if ((l->data = XCALLOC(1, l->size)) == NULL) { err = CRYPT_MEM; goto error; } if ((err = der_decode_bit_string(in, *inlen, AUTO_CAST(l->data), &l->size)) != CRYPT_OK) { goto error; } if ((err = der_length_bit_string(l->size, &len)) != CRYPT_OK) { goto error; } break; case 0x04: /* OCTET */ /* init field */ l->type = LTC_ASN1_OCTET_STRING; l->size = len; if ((l->data = XCALLOC(1, l->size)) == NULL) { err = CRYPT_MEM; goto error; } if ((err = der_decode_octet_string(in, *inlen, AUTO_CAST(l->data), &l->size)) != CRYPT_OK) { goto error; } if ((err = der_length_octet_string(l->size, &len)) != CRYPT_OK) { goto error; } break; case 0x05: /* NULL */ /* valid NULL is 0x05 0x00 */ if ((in[0] != 0x05) || (in[1] != 0x00)) { err = CRYPT_INVALID_PACKET; goto error; } /* simple to store ;-) */ l->type = LTC_ASN1_NULL; l->data = NULL; l->size = 0; len = 2; break; case 0x06: /* OID */ /* init field */ l->type = LTC_ASN1_OBJECT_IDENTIFIER; l->size = len; if ((l->data = XCALLOC(len, sizeof(unsigned long))) == NULL) { err = CRYPT_MEM; goto error; } if ((err = der_decode_object_identifier(in, *inlen, AUTO_CAST(l->data), &l->size)) != CRYPT_OK) { goto error; } if ((err = der_length_object_identifier(AUTO_CAST(l->data), l->size, &len)) != CRYPT_OK) { goto error; } /* resize it to save a bunch of mem */ if ((realloc_tmp = XREALLOC(l->data, l->size * sizeof(unsigned long))) == NULL) { /* out of heap but this is not an error */ break; } l->data = realloc_tmp; break; case 0x0C: /* UTF8 */ /* init field */ l->type = LTC_ASN1_UTF8_STRING; l->size = len; if ((l->data = XCALLOC(sizeof(wchar_t), l->size)) == NULL) { err = CRYPT_MEM; goto error; } if ((err = der_decode_utf8_string(in, *inlen, AUTO_CAST(l->data), &l->size)) != CRYPT_OK) { goto error; } if ((err = der_length_utf8_string(AUTO_CAST(l->data), l->size, &len)) != CRYPT_OK) { goto error; } break; case 0x13: /* PRINTABLE */ /* init field */ l->type = LTC_ASN1_PRINTABLE_STRING; l->size = len; if ((l->data = XCALLOC(1, l->size)) == NULL) { err = CRYPT_MEM; goto error; } if ((err = der_decode_printable_string(in, *inlen, AUTO_CAST(l->data), &l->size)) != CRYPT_OK) { goto error; } if ((err = der_length_printable_string(AUTO_CAST(l->data), l->size, &len)) != CRYPT_OK) { goto error; } break; case 0x16: /* IA5 */ /* init field */ l->type = LTC_ASN1_IA5_STRING; l->size = len; if ((l->data = XCALLOC(1, l->size)) == NULL) { err = CRYPT_MEM; goto error; } if ((err = der_decode_ia5_string(in, *inlen, AUTO_CAST(l->data), &l->size)) != CRYPT_OK) { goto error; } if ((err = der_length_ia5_string(AUTO_CAST(l->data), l->size, &len)) != CRYPT_OK) { goto error; } break; case 0x17: /* UTC TIME */ /* init field */ l->type = LTC_ASN1_UTCTIME; l->size = 1; if ((l->data = XCALLOC(1, sizeof(ltc_utctime))) == NULL) { err = CRYPT_MEM; goto error; } len = *inlen; if ((err = der_decode_utctime(in, &len, AUTO_CAST(l->data))) != CRYPT_OK) { goto error; } if ((err = der_length_utctime(AUTO_CAST(l->data), &len)) != CRYPT_OK) { goto error; } break; case 0x30: /* SEQUENCE */ case 0x31: /* SET */ /* init field */ l->type = (type == 0x30) ? LTC_ASN1_SEQUENCE : LTC_ASN1_SET; /* we have to decode the SEQUENCE header and get it's length */ /* move past type */ ++in; --(*inlen); /* read length byte */ x = *in++; --(*inlen); /* smallest SEQUENCE/SET header */ y = 2; /* now if it's > 127 the next bytes are the length of the length */ if (x > 128) { x &= 0x7F; in += x; *inlen -= x; /* update sequence header len */ y += x; } /* Sequence elements go as child */ len = len - y; if ((err = der_decode_sequence_flexi(in, &len, &(l->child))) != CRYPT_OK) { goto error; } /* len update */ totlen += y; /* link them up y0 */ l->child->parent = l; break; default: /* invalid byte ... this is a soft error */ /* remove link */ l = l->prev; XFREE(l->next); l->next = NULL; goto outside; } /* advance pointers */ totlen += len; in += len; *inlen -= len; } outside: /* rewind l please */ while (l->prev != NULL || l->parent != NULL) { if (l->parent != NULL) { l = l->parent; } else { l = l->prev; } } /* return */ *out = l; *inlen = totlen; return CRYPT_OK; error: /* free list */ der_sequence_free(l); return err; } #endif /* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/sequence/der_decode_sequence_flexi.c,v $ */ /* $Revision: 1.26 $ */ /* $Date: 2006/12/28 01:27:24 $ */ /* LibTomCrypt, modular cryptographic library -- Tom St Denis * * LibTomCrypt is a library that provides various cryptographic * algorithms in a highly modular and flexible manner. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tomstdenis@gmail.com, http://libtom.org */ #include /** @file der_decode_sequence_multi.c ASN.1 DER, decode a SEQUENCE, Tom St Denis */ #ifdef LTC_DER /** Decode a SEQUENCE type using a VA list @param in Input buffer @param inlen Length of input in octets @remark <...> is of the form (int, unsigned long, void*) @return CRYPT_OK on success */ int der_decode_sequence_multi(const unsigned char *in, unsigned long inlen, ...) { int err, type; unsigned long size, x; void *data; va_list args; ltc_asn1_list *list; LTC_ARGCHK(in != NULL); /* get size of output that will be required */ va_start(args, inlen); x = 0; for ( ; ; ) { type = va_arg(args, int); size = va_arg(args, unsigned long); data = va_arg(args, void *); if (type == LTC_ASN1_EOL) { break; } switch (type) { case LTC_ASN1_BOOLEAN: case LTC_ASN1_INTEGER: case LTC_ASN1_SHORT_INTEGER: case LTC_ASN1_BIT_STRING: case LTC_ASN1_OCTET_STRING: case LTC_ASN1_NULL: case LTC_ASN1_OBJECT_IDENTIFIER: case LTC_ASN1_IA5_STRING: case LTC_ASN1_PRINTABLE_STRING: case LTC_ASN1_UTF8_STRING: case LTC_ASN1_UTCTIME: case LTC_ASN1_SET: case LTC_ASN1_SETOF: case LTC_ASN1_SEQUENCE: case LTC_ASN1_CHOICE: ++x; break; default: va_end(args); return CRYPT_INVALID_ARG; } } va_end(args); /* allocate structure for x elements */ if (x == 0) { return CRYPT_NOP; } list = AUTO_CAST(XCALLOC(sizeof(*list), x)); if (list == NULL) { return CRYPT_MEM; } /* fill in the structure */ va_start(args, inlen); x = 0; for ( ; ; ) { type = va_arg(args, int); size = va_arg(args, unsigned long); data = va_arg(args, void *); if (type == LTC_ASN1_EOL) { break; } switch (type) { case LTC_ASN1_BOOLEAN: case LTC_ASN1_INTEGER: case LTC_ASN1_SHORT_INTEGER: case LTC_ASN1_BIT_STRING: case LTC_ASN1_OCTET_STRING: case LTC_ASN1_NULL: case LTC_ASN1_OBJECT_IDENTIFIER: case LTC_ASN1_IA5_STRING: case LTC_ASN1_PRINTABLE_STRING: case LTC_ASN1_UTF8_STRING: case LTC_ASN1_UTCTIME: case LTC_ASN1_SEQUENCE: case LTC_ASN1_SET: case LTC_ASN1_SETOF: case LTC_ASN1_CHOICE: list[x].type = type; list[x].size = size; list[x++].data = data; break; default: va_end(args); err = CRYPT_INVALID_ARG; goto LBL_ERR; } } va_end(args); err = der_decode_sequence(in, inlen, list, x); LBL_ERR: XFREE(list); return err; } #endif /* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/sequence/der_decode_sequence_multi.c,v $ */ /* $Revision: 1.13 $ */ /* $Date: 2006/12/28 01:27:24 $ */ /* LibTomCrypt, modular cryptographic library -- Tom St Denis * * LibTomCrypt is a library that provides various cryptographic * algorithms in a highly modular and flexible manner. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tomstdenis@gmail.com, http://libtom.org */ /** @file der_decode_short_integer.c ASN.1 DER, decode an integer, Tom St Denis */ #ifdef LTC_DER /** Read a short integer @param in The DER encoded data @param inlen Size of data @param num [out] The integer to decode @return CRYPT_OK if successful */ int der_decode_short_integer(const unsigned char *in, unsigned long inlen, unsigned long *num) { unsigned long len, x, y; LTC_ARGCHK(num != NULL); LTC_ARGCHK(in != NULL); /* check length */ if (inlen < 2) { return CRYPT_INVALID_PACKET; } /* check header */ x = 0; if ((in[x++] & 0x1F) != 0x02) { return CRYPT_INVALID_PACKET; } /* get the packet len */ len = in[x++]; if (x + len > inlen) { return CRYPT_INVALID_PACKET; } /* read number */ y = 0; while (len--) { y = (y << 8) | (unsigned long)in[x++]; } *num = y; return CRYPT_OK; } #endif /* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/short_integer/der_decode_short_integer.c,v $ */ /* $Revision: 1.7 $ */ /* $Date: 2006/12/28 01:27:24 $ */ /* LibTomCrypt, modular cryptographic library -- Tom St Denis * * LibTomCrypt is a library that provides various cryptographic * algorithms in a highly modular and flexible manner. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tomstdenis@gmail.com, http://libtom.org */ /** @file der_decode_utctime.c ASN.1 DER, decode a UTCTIME, Tom St Denis */ #ifdef LTC_DER static int char_to_int(unsigned char x) { switch (x) { case '0': return 0; case '1': return 1; case '2': return 2; case '3': return 3; case '4': return 4; case '5': return 5; case '6': return 6; case '7': return 7; case '8': return 8; case '9': return 9; } return 100; } #define DECODE_V(y, max) \ y = char_to_int(buf[x]) * 10 + char_to_int(buf[x + 1]); \ if (y >= max) return CRYPT_INVALID_PACKET; \ x += 2; /** Decodes a UTC time structure in DER format (reads all 6 valid encoding formats) @param in Input buffer @param inlen Length of input buffer in octets @param out [out] Destination of UTC time structure @return CRYPT_OK if successful */ int der_decode_utctime(const unsigned char *in, unsigned long *inlen, ltc_utctime *out) { unsigned char buf[32]; unsigned long x; int y; LTC_ARGCHK(in != NULL); LTC_ARGCHK(inlen != NULL); LTC_ARGCHK(out != NULL); /* check header */ if ((*inlen < 2UL) || (in[1] >= sizeof(buf)) || ((in[1] + 2UL) > *inlen)) { return CRYPT_INVALID_PACKET; } /* decode the string */ for (x = 0; x < in[1]; x++) { y = der_ia5_value_decode(in[x + 2]); if (y == -1) { return CRYPT_INVALID_PACKET; } buf[x] = y; } *inlen = 2 + x; /* possible encodings are YYMMDDhhmmZ YYMMDDhhmm+hh'mm' YYMMDDhhmm-hh'mm' YYMMDDhhmmssZ YYMMDDhhmmss+hh'mm' YYMMDDhhmmss-hh'mm' So let's do a trivial decode upto [including] mm */ x = 0; DECODE_V(out->YY, 100); DECODE_V(out->MM, 13); DECODE_V(out->DD, 32); DECODE_V(out->hh, 24); DECODE_V(out->mm, 60); /* clear timezone and seconds info */ out->off_dir = out->off_hh = out->off_mm = out->ss = 0; /* now is it Z, +, - or 0-9 */ if (buf[x] == 'Z') { return CRYPT_OK; } else if ((buf[x] == '+') || (buf[x] == '-')) { out->off_dir = (buf[x++] == '+') ? 0 : 1; DECODE_V(out->off_hh, 24); DECODE_V(out->off_mm, 60); return CRYPT_OK; } /* decode seconds */ DECODE_V(out->ss, 60); /* now is it Z, +, - */ if (buf[x] == 'Z') { return CRYPT_OK; } else if ((buf[x] == '+') || (buf[x] == '-')) { out->off_dir = (buf[x++] == '+') ? 0 : 1; DECODE_V(out->off_hh, 24); DECODE_V(out->off_mm, 60); return CRYPT_OK; } else { return CRYPT_INVALID_PACKET; } } #endif /* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/utctime/der_decode_utctime.c,v $ */ /* $Revision: 1.9 $ */ /* $Date: 2006/12/28 01:27:24 $ */ /* LibTomCrypt, modular cryptographic library -- Tom St Denis * * LibTomCrypt is a library that provides various cryptographic * algorithms in a highly modular and flexible manner. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tomstdenis@gmail.com, http://libtom.org */ /** @file der_decode_utf8_string.c ASN.1 DER, encode a UTF8 STRING, Tom St Denis */ #ifdef LTC_DER /** Store a UTF8 STRING @param in The DER encoded UTF8 STRING @param inlen The size of the DER UTF8 STRING @param out [out] The array of utf8s stored (one per char) @param outlen [in/out] The number of utf8s stored @return CRYPT_OK if successful */ int der_decode_utf8_string(const unsigned char *in, unsigned long inlen, wchar_t *out, unsigned long *outlen) { wchar_t tmp; unsigned long x, y, z, len; LTC_ARGCHK(in != NULL); LTC_ARGCHK(out != NULL); LTC_ARGCHK(outlen != NULL); /* must have header at least */ if (inlen < 2) { return CRYPT_INVALID_PACKET; } /* check for 0x0C */ if ((in[0] & 0x1F) != 0x0C) { return CRYPT_INVALID_PACKET; } x = 1; /* decode the length */ if (in[x] & 0x80) { /* valid # of bytes in length are 1,2,3 */ y = in[x] & 0x7F; if ((y == 0) || (y > 3) || ((x + y) > inlen)) { return CRYPT_INVALID_PACKET; } /* read the length in */ len = 0; ++x; while (y--) { len = (len << 8) | in[x++]; } } else { len = in[x++] & 0x7F; } if (len + x > inlen) { return CRYPT_INVALID_PACKET; } /* proceed to decode */ for (y = 0; x < inlen; ) { /* get first byte */ tmp = in[x++]; /* count number of bytes */ for (z = 0; (tmp & 0x80) && (z <= 4); z++, tmp = (tmp << 1) & 0xFF); if ((z > 4) || (x + (z - 1) > inlen)) { return CRYPT_INVALID_PACKET; } /* decode, grab upper bits */ tmp >>= z; /* grab remaining bytes */ if (z > 1) { --z; } while (z-- != 0) { if ((in[x] & 0xC0) != 0x80) { return CRYPT_INVALID_PACKET; } tmp = (tmp << 6) | ((wchar_t)in[x++] & 0x3F); } if (y > *outlen) { *outlen = y; return CRYPT_BUFFER_OVERFLOW; } out[y++] = tmp; } *outlen = y; return CRYPT_OK; } #endif /* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/utf8/der_decode_utf8_string.c,v $ */ /* $Revision: 1.8 $ */ /* $Date: 2006/12/28 01:27:24 $ */ /* LibTomCrypt, modular cryptographic library -- Tom St Denis * * LibTomCrypt is a library that provides various cryptographic * algorithms in a highly modular and flexible manner. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tomstdenis@gmail.com, http://libtom.org */ /** @file der_encode_bit_string.c ASN.1 DER, encode a BIT STRING, Tom St Denis */ #ifdef LTC_DER /** Store a BIT STRING @param in The array of bits to store (one per char) @param inlen The number of bits tostore @param out [out] The destination for the DER encoded BIT STRING @param outlen [in/out] The max size and resulting size of the DER BIT STRING @return CRYPT_OK if successful */ int der_encode_bit_string(const unsigned char *in, unsigned long inlen, unsigned char *out, unsigned long *outlen) { unsigned long len, x, y; unsigned char buf; int err; LTC_ARGCHK(in != NULL); LTC_ARGCHK(out != NULL); LTC_ARGCHK(outlen != NULL); /* avoid overflows */ if ((err = der_length_bit_string(inlen, &len)) != CRYPT_OK) { return err; } if (len > *outlen) { *outlen = len; return CRYPT_BUFFER_OVERFLOW; } /* store header (include bit padding count in length) */ x = 0; y = (inlen >> 3) + ((inlen & 7) ? 1 : 0) + 1; out[x++] = 0x03; if (y < 128) { out[x++] = (unsigned char)y; } else if (y < 256) { out[x++] = 0x81; out[x++] = (unsigned char)y; } else if (y < 65536) { out[x++] = 0x82; out[x++] = (unsigned char)((y >> 8) & 255); out[x++] = (unsigned char)(y & 255); } /* store number of zero padding bits */ out[x++] = (unsigned char)((8 - inlen) & 7); /* store the bits in big endian format */ for (y = buf = 0; y < inlen; y++) { buf |= (in[y] ? 1 : 0) << (7 - (y & 7)); if ((y & 7) == 7) { out[x++] = buf; buf = 0; } } /* store last byte */ if (inlen & 7) { out[x++] = buf; } *outlen = x; return CRYPT_OK; } #endif /* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/bit/der_encode_bit_string.c,v $ */ /* $Revision: 1.5 $ */ /* $Date: 2006/12/28 01:27:24 $ */ /* LibTomCrypt, modular cryptographic library -- Tom St Denis * * LibTomCrypt is a library that provides various cryptographic * algorithms in a highly modular and flexible manner. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tomstdenis@gmail.com, http://libtom.org */ /** @file der_encode_boolean.c ASN.1 DER, encode a BOOLEAN, Tom St Denis */ #ifdef LTC_DER /** Store a BOOLEAN @param in The boolean to encode @param out [out] The destination for the DER encoded BOOLEAN @param outlen [in/out] The max size and resulting size of the DER BOOLEAN @return CRYPT_OK if successful */ int der_encode_boolean(int in, unsigned char *out, unsigned long *outlen) { LTC_ARGCHK(outlen != NULL); LTC_ARGCHK(out != NULL); if (*outlen < 3) { *outlen = 3; return CRYPT_BUFFER_OVERFLOW; } *outlen = 3; out[0] = 0x01; out[1] = 0x01; out[2] = in ? 0xFF : 0x00; return CRYPT_OK; } #endif /* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/boolean/der_encode_boolean.c,v $ */ /* $Revision: 1.4 $ */ /* $Date: 2006/12/28 01:27:24 $ */ /* LibTomCrypt, modular cryptographic library -- Tom St Denis * * LibTomCrypt is a library that provides various cryptographic * algorithms in a highly modular and flexible manner. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tomstdenis@gmail.com, http://libtom.org */ /** @file der_encode_ia5_string.c ASN.1 DER, encode a IA5 STRING, Tom St Denis */ #ifdef LTC_DER /** Store an IA5 STRING @param in The array of IA5 to store (one per char) @param inlen The number of IA5 to store @param out [out] The destination for the DER encoded IA5 STRING @param outlen [in/out] The max size and resulting size of the DER IA5 STRING @return CRYPT_OK if successful */ int der_encode_ia5_string(const unsigned char *in, unsigned long inlen, unsigned char *out, unsigned long *outlen) { unsigned long x, y, len; int err; LTC_ARGCHK(in != NULL); LTC_ARGCHK(out != NULL); LTC_ARGCHK(outlen != NULL); /* get the size */ if ((err = der_length_ia5_string(in, inlen, &len)) != CRYPT_OK) { return err; } /* too big? */ if (len > *outlen) { *outlen = len; return CRYPT_BUFFER_OVERFLOW; } /* encode the header+len */ x = 0; out[x++] = 0x16; if (inlen < 128) { out[x++] = (unsigned char)inlen; } else if (inlen < 256) { out[x++] = 0x81; out[x++] = (unsigned char)inlen; } else if (inlen < 65536UL) { out[x++] = 0x82; out[x++] = (unsigned char)((inlen >> 8) & 255); out[x++] = (unsigned char)(inlen & 255); } else if (inlen < 16777216UL) { out[x++] = 0x83; out[x++] = (unsigned char)((inlen >> 16) & 255); out[x++] = (unsigned char)((inlen >> 8) & 255); out[x++] = (unsigned char)(inlen & 255); } else { return CRYPT_INVALID_ARG; } /* store octets */ for (y = 0; y < inlen; y++) { out[x++] = der_ia5_char_encode(in[y]); } /* retun length */ *outlen = x; return CRYPT_OK; } #endif /* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/ia5/der_encode_ia5_string.c,v $ */ /* $Revision: 1.5 $ */ /* $Date: 2006/12/28 01:27:24 $ */ /* LibTomCrypt, modular cryptographic library -- Tom St Denis * * LibTomCrypt is a library that provides various cryptographic * algorithms in a highly modular and flexible manner. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tomstdenis@gmail.com, http://libtom.org */ /** @file der_encode_integer.c ASN.1 DER, encode an integer, Tom St Denis */ #ifdef LTC_DER /* Exports a positive bignum as DER format (upto 2^32 bytes in size) */ /** Store a mp_int integer @param num The first mp_int to encode @param out [out] The destination for the DER encoded integers @param outlen [in/out] The max size and resulting size of the DER encoded integers @return CRYPT_OK if successful */ int der_encode_integer(void *num, unsigned char *out, unsigned long *outlen) { unsigned long tmplen, y; int err, leading_zero; LTC_ARGCHK(num != NULL); LTC_ARGCHK(out != NULL); LTC_ARGCHK(outlen != NULL); /* find out how big this will be */ if ((err = der_length_integer(num, &tmplen)) != CRYPT_OK) { return err; } if (*outlen < tmplen) { *outlen = tmplen; return CRYPT_BUFFER_OVERFLOW; } if (mp_cmp_d(num, 0) != LTC_MP_LT) { /* we only need a leading zero if the msb of the first byte is one */ if (((mp_count_bits(num) & 7) == 0) || (mp_iszero(num) == LTC_MP_YES)) { leading_zero = 1; } else { leading_zero = 0; } /* get length of num in bytes (plus 1 since we force the msbyte to zero) */ y = mp_unsigned_bin_size(num) + leading_zero; } else { leading_zero = 0; y = mp_count_bits(num); y = y + (8 - (y & 7)); y = y >> 3; if (((mp_cnt_lsb(num) + 1) == mp_count_bits(num)) && ((mp_count_bits(num) & 7) == 0)) --y; } /* now store initial data */ *out++ = 0x02; if (y < 128) { /* short form */ *out++ = (unsigned char)y; } else if (y < 256) { *out++ = 0x81; *out++ = (unsigned char)y; } else if (y < 65536UL) { *out++ = 0x82; *out++ = (unsigned char)((y >> 8) & 255); *out++ = (unsigned char)y; } else if (y < 16777216UL) { *out++ = 0x83; *out++ = (unsigned char)((y >> 16) & 255); *out++ = (unsigned char)((y >> 8) & 255); *out++ = (unsigned char)y; } else { return CRYPT_INVALID_ARG; } /* now store msbyte of zero if num is non-zero */ if (leading_zero) { *out++ = 0x00; } /* if it's not zero store it as big endian */ if (mp_cmp_d(num, 0) == LTC_MP_GT) { /* now store the mpint */ if ((err = mp_to_unsigned_bin(num, out)) != CRYPT_OK) { return err; } } else if (mp_iszero(num) != LTC_MP_YES) { void *tmp; /* negative */ if (mp_init(&tmp) != CRYPT_OK) { return CRYPT_MEM; } /* 2^roundup and subtract */ y = mp_count_bits(num); y = y + (8 - (y & 7)); if (((mp_cnt_lsb(num) + 1) == mp_count_bits(num)) && ((mp_count_bits(num) & 7) == 0)) y -= 8; if ((mp_2expt(tmp, y) != CRYPT_OK) || (mp_add(tmp, num, tmp) != CRYPT_OK)) { mp_clear(tmp); return CRYPT_MEM; } if ((err = mp_to_unsigned_bin(tmp, out)) != CRYPT_OK) { mp_clear(tmp); return err; } mp_clear(tmp); } /* we good */ *outlen = tmplen; return CRYPT_OK; } #endif /* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/integer/der_encode_integer.c,v $ */ /* $Revision: 1.9 $ */ /* $Date: 2006/12/28 01:27:24 $ */ /* LibTomCrypt, modular cryptographic library -- Tom St Denis * * LibTomCrypt is a library that provides various cryptographic * algorithms in a highly modular and flexible manner. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tomstdenis@gmail.com, http://libtom.org */ /** @file der_encode_object_identifier.c ASN.1 DER, Encode Object Identifier, Tom St Denis */ #ifdef LTC_DER /** Encode an OID @param words The words to encode (upto 32-bits each) @param nwords The number of words in the OID @param out [out] Destination of OID data @param outlen [in/out] The max and resulting size of the OID @return CRYPT_OK if successful */ int der_encode_object_identifier(unsigned long *words, unsigned long nwords, unsigned char *out, unsigned long *outlen) { unsigned long i, x, y, z, t, mask, wordbuf; int err; LTC_ARGCHK(words != NULL); LTC_ARGCHK(out != NULL); LTC_ARGCHK(outlen != NULL); /* check length */ if ((err = der_length_object_identifier(words, nwords, &x)) != CRYPT_OK) { return err; } if (x > *outlen) { *outlen = x; return CRYPT_BUFFER_OVERFLOW; } /* compute length to store OID data */ z = 0; wordbuf = words[0] * 40 + words[1]; for (y = 1; y < nwords; y++) { t = der_object_identifier_bits(wordbuf); z += t / 7 + ((t % 7) ? 1 : 0) + (wordbuf == 0 ? 1 : 0); if (y < nwords - 1) { wordbuf = words[y + 1]; } } /* store header + length */ x = 0; out[x++] = 0x06; if (z < 128) { out[x++] = (unsigned char)z; } else if (z < 256) { out[x++] = 0x81; out[x++] = (unsigned char)z; } else if (z < 65536UL) { out[x++] = 0x82; out[x++] = (unsigned char)((z >> 8) & 255); out[x++] = (unsigned char)(z & 255); } else { return CRYPT_INVALID_ARG; } /* store first byte */ wordbuf = words[0] * 40 + words[1]; for (i = 1; i < nwords; i++) { /* store 7 bit words in little endian */ t = wordbuf & 0xFFFFFFFF; if (t) { y = x; mask = 0; while (t) { out[x++] = (unsigned char)((t & 0x7F) | mask); t >>= 7; mask |= 0x80; /* upper bit is set on all but the last byte */ } /* now swap bytes y...x-1 */ z = x - 1; while (y < z) { t = out[y]; out[y] = out[z]; out[z] = (unsigned char)t; ++y; --z; } } else { /* zero word */ out[x++] = 0x00; } if (i < nwords - 1) { wordbuf = words[i + 1]; } } *outlen = x; return CRYPT_OK; } #endif /* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/object_identifier/der_encode_object_identifier.c,v $ */ /* $Revision: 1.7 $ */ /* $Date: 2006/12/28 01:27:24 $ */ /* LibTomCrypt, modular cryptographic library -- Tom St Denis * * LibTomCrypt is a library that provides various cryptographic * algorithms in a highly modular and flexible manner. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tomstdenis@gmail.com, http://libtom.org */ /** @file der_encode_octet_string.c ASN.1 DER, encode a OCTET STRING, Tom St Denis */ #ifdef LTC_DER /** Store an OCTET STRING @param in The array of OCTETS to store (one per char) @param inlen The number of OCTETS to store @param out [out] The destination for the DER encoded OCTET STRING @param outlen [in/out] The max size and resulting size of the DER OCTET STRING @return CRYPT_OK if successful */ int der_encode_octet_string(const unsigned char *in, unsigned long inlen, unsigned char *out, unsigned long *outlen) { unsigned long x, y, len; int err; LTC_ARGCHK(in != NULL); LTC_ARGCHK(out != NULL); LTC_ARGCHK(outlen != NULL); /* get the size */ if ((err = der_length_octet_string(inlen, &len)) != CRYPT_OK) { return err; } /* too big? */ if (len > *outlen) { *outlen = len; return CRYPT_BUFFER_OVERFLOW; } /* encode the header+len */ x = 0; out[x++] = 0x04; if (inlen < 128) { out[x++] = (unsigned char)inlen; } else if (inlen < 256) { out[x++] = 0x81; out[x++] = (unsigned char)inlen; } else if (inlen < 65536UL) { out[x++] = 0x82; out[x++] = (unsigned char)((inlen >> 8) & 255); out[x++] = (unsigned char)(inlen & 255); } else if (inlen < 16777216UL) { out[x++] = 0x83; out[x++] = (unsigned char)((inlen >> 16) & 255); out[x++] = (unsigned char)((inlen >> 8) & 255); out[x++] = (unsigned char)(inlen & 255); } else { return CRYPT_INVALID_ARG; } /* store octets */ for (y = 0; y < inlen; y++) { out[x++] = in[y]; } /* retun length */ *outlen = x; return CRYPT_OK; } #endif /* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/octet/der_encode_octet_string.c,v $ */ /* $Revision: 1.5 $ */ /* $Date: 2006/12/28 01:27:24 $ */ /* LibTomCrypt, modular cryptographic library -- Tom St Denis * * LibTomCrypt is a library that provides various cryptographic * algorithms in a highly modular and flexible manner. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tomstdenis@gmail.com, http://libtom.org */ /** @file der_encode_printable_string.c ASN.1 DER, encode a printable STRING, Tom St Denis */ #ifdef LTC_DER /** Store an printable STRING @param in The array of printable to store (one per char) @param inlen The number of printable to store @param out [out] The destination for the DER encoded printable STRING @param outlen [in/out] The max size and resulting size of the DER printable STRING @return CRYPT_OK if successful */ int der_encode_printable_string(const unsigned char *in, unsigned long inlen, unsigned char *out, unsigned long *outlen) { unsigned long x, y, len; int err; LTC_ARGCHK(in != NULL); LTC_ARGCHK(out != NULL); LTC_ARGCHK(outlen != NULL); /* get the size */ if ((err = der_length_printable_string(in, inlen, &len)) != CRYPT_OK) { return err; } /* too big? */ if (len > *outlen) { *outlen = len; return CRYPT_BUFFER_OVERFLOW; } /* encode the header+len */ x = 0; out[x++] = 0x13; if (inlen < 128) { out[x++] = (unsigned char)inlen; } else if (inlen < 256) { out[x++] = 0x81; out[x++] = (unsigned char)inlen; } else if (inlen < 65536UL) { out[x++] = 0x82; out[x++] = (unsigned char)((inlen >> 8) & 255); out[x++] = (unsigned char)(inlen & 255); } else if (inlen < 16777216UL) { out[x++] = 0x83; out[x++] = (unsigned char)((inlen >> 16) & 255); out[x++] = (unsigned char)((inlen >> 8) & 255); out[x++] = (unsigned char)(inlen & 255); } else { return CRYPT_INVALID_ARG; } /* store octets */ for (y = 0; y < inlen; y++) { out[x++] = der_printable_char_encode(in[y]); } /* retun length */ *outlen = x; return CRYPT_OK; } #endif /* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/printable_string/der_encode_printable_string.c,v $ */ /* $Revision: 1.5 $ */ /* $Date: 2006/12/28 01:27:24 $ */ /* LibTomCrypt, modular cryptographic library -- Tom St Denis * * LibTomCrypt is a library that provides various cryptographic * algorithms in a highly modular and flexible manner. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tomstdenis@gmail.com, http://libtom.org */ #include /** @file der_encode_sequence_ex.c ASN.1 DER, encode a SEQUENCE, Tom St Denis */ #ifdef LTC_DER /** Encode a SEQUENCE @param list The list of items to encode @param inlen The number of items in the list @param out [out] The destination @param outlen [in/out] The size of the output @param type_of LTC_ASN1_SEQUENCE or LTC_ASN1_SET/LTC_ASN1_SETOF @return CRYPT_OK on success */ int der_encode_sequence_ex(ltc_asn1_list *list, unsigned long inlen, unsigned char *out, unsigned long *outlen, int type_of) { int err, type; unsigned long size, x, y, z, i; void *data; LTC_ARGCHK(list != NULL); LTC_ARGCHK(out != NULL); LTC_ARGCHK(outlen != NULL); /* get size of output that will be required */ y = 0; for (i = 0; i < inlen; i++) { type = list[i].type; size = list[i].size; data = list[i].data; if (type == LTC_ASN1_EOL) { break; } switch (type) { case LTC_ASN1_BOOLEAN: if ((err = der_length_boolean(&x)) != CRYPT_OK) { goto LBL_ERR; } y += x; break; case LTC_ASN1_INTEGER: if ((err = der_length_integer(data, &x)) != CRYPT_OK) { goto LBL_ERR; } y += x; break; case LTC_ASN1_SHORT_INTEGER: if ((err = der_length_short_integer(*((unsigned long *)data), &x)) != CRYPT_OK) { goto LBL_ERR; } y += x; break; case LTC_ASN1_BIT_STRING: if ((err = der_length_bit_string(size, &x)) != CRYPT_OK) { goto LBL_ERR; } y += x; break; case LTC_ASN1_OCTET_STRING: if ((err = der_length_octet_string(size, &x)) != CRYPT_OK) { goto LBL_ERR; } y += x; break; case LTC_ASN1_NULL: y += 2; break; case LTC_ASN1_OBJECT_IDENTIFIER: if ((err = der_length_object_identifier(AUTO_CAST(data), size, &x)) != CRYPT_OK) { goto LBL_ERR; } y += x; break; case LTC_ASN1_IA5_STRING: if ((err = der_length_ia5_string(AUTO_CAST(data), size, &x)) != CRYPT_OK) { goto LBL_ERR; } y += x; break; case LTC_ASN1_PRINTABLE_STRING: if ((err = der_length_printable_string(AUTO_CAST(data), size, &x)) != CRYPT_OK) { goto LBL_ERR; } y += x; break; case LTC_ASN1_UTF8_STRING: if ((err = der_length_utf8_string(AUTO_CAST(data), size, &x)) != CRYPT_OK) { goto LBL_ERR; } y += x; break; case LTC_ASN1_UTCTIME: if ((err = der_length_utctime(AUTO_CAST(data), &x)) != CRYPT_OK) { goto LBL_ERR; } y += x; break; case LTC_ASN1_SET: case LTC_ASN1_SETOF: case LTC_ASN1_SEQUENCE: if ((err = der_length_sequence(AUTO_CAST(data), size, &x)) != CRYPT_OK) { goto LBL_ERR; } y += x; break; default: err = CRYPT_INVALID_ARG; goto LBL_ERR; } } /* calc header size */ z = y; if (y < 128) { y += 2; } else if (y < 256) { /* 0x30 0x81 LL */ y += 3; } else if (y < 65536UL) { /* 0x30 0x82 LL LL */ y += 4; } else if (y < 16777216UL) { /* 0x30 0x83 LL LL LL */ y += 5; } else { err = CRYPT_INVALID_ARG; goto LBL_ERR; } /* too big ? */ if (*outlen < y) { *outlen = y; err = CRYPT_BUFFER_OVERFLOW; goto LBL_ERR; } /* store header */ x = 0; out[x++] = (type_of == LTC_ASN1_SEQUENCE) ? 0x30 : 0x31; if (z < 128) { out[x++] = (unsigned char)z; } else if (z < 256) { out[x++] = 0x81; out[x++] = (unsigned char)z; } else if (z < 65536UL) { out[x++] = 0x82; out[x++] = (unsigned char)((z >> 8UL) & 255); out[x++] = (unsigned char)(z & 255); } else if (z < 16777216UL) { out[x++] = 0x83; out[x++] = (unsigned char)((z >> 16UL) & 255); out[x++] = (unsigned char)((z >> 8UL) & 255); out[x++] = (unsigned char)(z & 255); } /* store data */ *outlen -= x; for (i = 0; i < inlen; i++) { type = list[i].type; size = list[i].size; data = list[i].data; if (type == LTC_ASN1_EOL) { break; } switch (type) { case LTC_ASN1_BOOLEAN: z = *outlen; if ((err = der_encode_boolean(*((int *)data), out + x, &z)) != CRYPT_OK) { goto LBL_ERR; } x += z; *outlen -= z; break; case LTC_ASN1_INTEGER: z = *outlen; if ((err = der_encode_integer(data, out + x, &z)) != CRYPT_OK) { goto LBL_ERR; } x += z; *outlen -= z; break; case LTC_ASN1_SHORT_INTEGER: z = *outlen; if ((err = der_encode_short_integer(*((unsigned long *)data), out + x, &z)) != CRYPT_OK) { goto LBL_ERR; } x += z; *outlen -= z; break; case LTC_ASN1_BIT_STRING: z = *outlen; if ((err = der_encode_bit_string(AUTO_CAST(data), size, out + x, &z)) != CRYPT_OK) { goto LBL_ERR; } x += z; *outlen -= z; break; case LTC_ASN1_OCTET_STRING: z = *outlen; if ((err = der_encode_octet_string(AUTO_CAST(data), size, out + x, &z)) != CRYPT_OK) { goto LBL_ERR; } x += z; *outlen -= z; break; case LTC_ASN1_NULL: out[x++] = 0x05; out[x++] = 0x00; *outlen -= 2; break; case LTC_ASN1_OBJECT_IDENTIFIER: z = *outlen; if ((err = der_encode_object_identifier(AUTO_CAST(data), size, out + x, &z)) != CRYPT_OK) { goto LBL_ERR; } x += z; *outlen -= z; break; case LTC_ASN1_IA5_STRING: z = *outlen; if ((err = der_encode_ia5_string(AUTO_CAST(data), size, out + x, &z)) != CRYPT_OK) { goto LBL_ERR; } x += z; *outlen -= z; break; case LTC_ASN1_PRINTABLE_STRING: z = *outlen; if ((err = der_encode_printable_string(AUTO_CAST(data), size, out + x, &z)) != CRYPT_OK) { goto LBL_ERR; } x += z; *outlen -= z; break; case LTC_ASN1_UTF8_STRING: z = *outlen; if ((err = der_encode_utf8_string(AUTO_CAST(data), size, out + x, &z)) != CRYPT_OK) { goto LBL_ERR; } x += z; *outlen -= z; break; case LTC_ASN1_UTCTIME: z = *outlen; if ((err = der_encode_utctime(AUTO_CAST(data), out + x, &z)) != CRYPT_OK) { goto LBL_ERR; } x += z; *outlen -= z; break; case LTC_ASN1_SET: z = *outlen; if ((err = der_encode_set(AUTO_CAST(data), size, out + x, &z)) != CRYPT_OK) { goto LBL_ERR; } x += z; *outlen -= z; break; case LTC_ASN1_SETOF: z = *outlen; if ((err = der_encode_setof(AUTO_CAST(data), size, out + x, &z)) != CRYPT_OK) { goto LBL_ERR; } x += z; *outlen -= z; break; case LTC_ASN1_SEQUENCE: z = *outlen; if ((err = der_encode_sequence_ex(AUTO_CAST(data), size, out + x, &z, type)) != CRYPT_OK) { goto LBL_ERR; } x += z; *outlen -= z; break; default: err = CRYPT_INVALID_ARG; goto LBL_ERR; } } *outlen = x; err = CRYPT_OK; LBL_ERR: return err; } #endif /* LibTomCrypt, modular cryptographic library -- Tom St Denis * * LibTomCrypt is a library that provides various cryptographic * algorithms in a highly modular and flexible manner. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tomstdenis@gmail.com, http://libtom.org */ #include /** @file der_encode_sequence_multi.c ASN.1 DER, encode a SEQUENCE, Tom St Denis */ #ifdef LTC_DER /** Encode a SEQUENCE type using a VA list @param out [out] Destination for data @param outlen [in/out] Length of buffer and resulting length of output @remark <...> is of the form (int, unsigned long, void*) @return CRYPT_OK on success */ int der_encode_sequence_multi(unsigned char *out, unsigned long *outlen, ...) { int err, type; unsigned long size, x; void *data; va_list args; ltc_asn1_list *list; LTC_ARGCHK(out != NULL); LTC_ARGCHK(outlen != NULL); /* get size of output that will be required */ va_start(args, outlen); x = 0; for ( ; ; ) { type = va_arg(args, int); size = va_arg(args, unsigned long); data = va_arg(args, void *); if (type == LTC_ASN1_EOL) { break; } switch (type) { case LTC_ASN1_BOOLEAN: case LTC_ASN1_INTEGER: case LTC_ASN1_SHORT_INTEGER: case LTC_ASN1_BIT_STRING: case LTC_ASN1_OCTET_STRING: case LTC_ASN1_NULL: case LTC_ASN1_OBJECT_IDENTIFIER: case LTC_ASN1_IA5_STRING: case LTC_ASN1_PRINTABLE_STRING: case LTC_ASN1_UTF8_STRING: case LTC_ASN1_UTCTIME: case LTC_ASN1_SEQUENCE: case LTC_ASN1_SET: case LTC_ASN1_SETOF: ++x; break; default: va_end(args); return CRYPT_INVALID_ARG; } } va_end(args); /* allocate structure for x elements */ if (x == 0) { return CRYPT_NOP; } list = AUTO_CAST(XCALLOC(sizeof(*list), x)); if (list == NULL) { return CRYPT_MEM; } /* fill in the structure */ va_start(args, outlen); x = 0; for ( ; ; ) { type = va_arg(args, int); size = va_arg(args, unsigned long); data = va_arg(args, void *); if (type == LTC_ASN1_EOL) { break; } switch (type) { case LTC_ASN1_BOOLEAN: case LTC_ASN1_INTEGER: case LTC_ASN1_SHORT_INTEGER: case LTC_ASN1_BIT_STRING: case LTC_ASN1_OCTET_STRING: case LTC_ASN1_NULL: case LTC_ASN1_OBJECT_IDENTIFIER: case LTC_ASN1_IA5_STRING: case LTC_ASN1_PRINTABLE_STRING: case LTC_ASN1_UTF8_STRING: case LTC_ASN1_UTCTIME: case LTC_ASN1_SEQUENCE: case LTC_ASN1_SET: case LTC_ASN1_SETOF: list[x].type = type; list[x].size = size; list[x++].data = data; break; default: va_end(args); err = CRYPT_INVALID_ARG; goto LBL_ERR; } } va_end(args); err = der_encode_sequence(list, x, out, outlen); LBL_ERR: XFREE(list); return err; } #endif /* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/sequence/der_encode_sequence_multi.c,v $ */ /* $Revision: 1.12 $ */ /* $Date: 2006/12/28 01:27:24 $ */ /* LibTomCrypt, modular cryptographic library -- Tom St Denis * * LibTomCrypt is a library that provides various cryptographic * algorithms in a highly modular and flexible manner. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tomstdenis@gmail.com, http://libtom.org */ /** @file der_encode_set.c ASN.1 DER, Encode a SET, Tom St Denis */ #ifdef LTC_DER /* LTC define to ASN.1 TAG */ static int ltc_to_asn1(int v) { switch (v) { case LTC_ASN1_BOOLEAN: return 0x01; case LTC_ASN1_INTEGER: case LTC_ASN1_SHORT_INTEGER: return 0x02; case LTC_ASN1_BIT_STRING: return 0x03; case LTC_ASN1_OCTET_STRING: return 0x04; case LTC_ASN1_NULL: return 0x05; case LTC_ASN1_OBJECT_IDENTIFIER: return 0x06; case LTC_ASN1_UTF8_STRING: return 0x0C; case LTC_ASN1_PRINTABLE_STRING: return 0x13; case LTC_ASN1_IA5_STRING: return 0x16; case LTC_ASN1_UTCTIME: return 0x17; case LTC_ASN1_SEQUENCE: return 0x30; case LTC_ASN1_SET: case LTC_ASN1_SETOF: return 0x31; default: return -1; } } static int qsort_helper_set(const void *a, const void *b) { ltc_asn1_list *A = (ltc_asn1_list *)a, *B = (ltc_asn1_list *)b; int r; r = ltc_to_asn1(A->type) - ltc_to_asn1(B->type); /* for QSORT the order is UNDEFINED if they are "equal" which means it is NOT DETERMINISTIC. So we force it to be :-) */ if (r == 0) { /* their order in the original list now determines the position */ return A->used - B->used; } else { return r; } } /* Encode a SET type @param list The list of items to encode @param inlen The number of items in the list @param out [out] The destination @param outlen [in/out] The size of the output @return CRYPT_OK on success */ int der_encode_set(ltc_asn1_list *list, unsigned long inlen, unsigned char *out, unsigned long *outlen) { ltc_asn1_list *copy; unsigned long x; int err; /* make copy of list */ copy = AUTO_CAST(XCALLOC(inlen, sizeof(*copy))); if (copy == NULL) { return CRYPT_MEM; } /* fill in used member with index so we can fully sort it */ for (x = 0; x < inlen; x++) { copy[x] = list[x]; copy[x].used = x; } /* sort it by the "type" field */ XQSORT(copy, inlen, sizeof(*copy), &qsort_helper_set); /* call der_encode_sequence_ex() */ err = der_encode_sequence_ex(copy, inlen, out, outlen, LTC_ASN1_SET); /* free list */ XFREE(copy); return err; } #endif /* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/set/der_encode_set.c,v $ */ /* $Revision: 1.12 $ */ /* $Date: 2006/12/28 01:27:24 $ */ /* LibTomCrypt, modular cryptographic library -- Tom St Denis * * LibTomCrypt is a library that provides various cryptographic * algorithms in a highly modular and flexible manner. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tomstdenis@gmail.com, http://libtom.org */ /** @file der_encode_setof.c ASN.1 DER, Encode SET OF, Tom St Denis */ #ifdef LTC_DER struct edge { unsigned char *start; unsigned long size; }; static int qsort_helper(const void *a, const void *b) { struct edge *A = (struct edge *)a, *B = (struct edge *)b; int r; unsigned long x; /* compare min length */ r = XMEMCMP(A->start, B->start, MIN(A->size, B->size)); if ((r == 0) && (A->size != B->size)) { if (A->size > B->size) { for (x = B->size; x < A->size; x++) { if (A->start[x]) { return 1; } } } else { for (x = A->size; x < B->size; x++) { if (B->start[x]) { return -1; } } } } return r; } /** Encode a SETOF stucture @param list The list of items to encode @param inlen The number of items in the list @param out [out] The destination @param outlen [in/out] The size of the output @return CRYPT_OK on success */ int der_encode_setof(ltc_asn1_list *list, unsigned long inlen, unsigned char *out, unsigned long *outlen) { unsigned long x, y, z, hdrlen; int err; struct edge *edges; unsigned char *ptr, *buf; /* check that they're all the same type */ for (x = 1; x < inlen; x++) { if (list[x].type != list[x - 1].type) { return CRYPT_INVALID_ARG; } } /* alloc buffer to store copy of output */ buf = AUTO_CAST(XCALLOC(1, *outlen)); if (buf == NULL) { return CRYPT_MEM; } /* encode list */ if ((err = der_encode_sequence_ex(list, inlen, buf, outlen, LTC_ASN1_SETOF)) != CRYPT_OK) { XFREE(buf); return err; } /* allocate edges */ edges = AUTO_CAST(XCALLOC(inlen, sizeof(*edges))); if (edges == NULL) { XFREE(buf); return CRYPT_MEM; } /* skip header */ ptr = buf + 1; /* now skip length data */ x = *ptr++; if (x >= 0x80) { ptr += (x & 0x7F); } /* get the size of the static header */ hdrlen = ((uintptr_t)ptr) - ((uintptr_t)buf); //<@r-lyeh /* scan for edges */ x = 0; while (ptr < (buf + *outlen)) { /* store start */ edges[x].start = ptr; /* skip type */ z = 1; /* parse length */ y = ptr[z++]; if (y < 128) { edges[x].size = y; } else { y &= 0x7F; edges[x].size = 0; while (y--) { edges[x].size = (edges[x].size << 8) | ((unsigned long)ptr[z++]); } } /* skip content */ edges[x].size += z; ptr += edges[x].size; ++x; } /* sort based on contents (using edges) */ XQSORT(edges, inlen, sizeof(*edges), &qsort_helper); /* copy static header */ XMEMCPY(out, buf, hdrlen); /* copy+sort using edges+indecies to output from buffer */ for (y = hdrlen, x = 0; x < inlen; x++) { XMEMCPY(out + y, edges[x].start, edges[x].size); y += edges[x].size; } #ifdef LTC_CLEAN_STACK zeromem(buf, *outlen); #endif /* free buffers */ XFREE(edges); XFREE(buf); return CRYPT_OK; } #endif /* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/set/der_encode_setof.c,v $ */ /* $Revision: 1.12 $ */ /* $Date: 2006/12/28 01:27:24 $ */ /* LibTomCrypt, modular cryptographic library -- Tom St Denis * * LibTomCrypt is a library that provides various cryptographic * algorithms in a highly modular and flexible manner. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tomstdenis@gmail.com, http://libtom.org */ /** @file der_encode_short_integer.c ASN.1 DER, encode an integer, Tom St Denis */ #ifdef LTC_DER /** Store a short integer in the range (0,2^32-1) @param num The integer to encode @param out [out] The destination for the DER encoded integers @param outlen [in/out] The max size and resulting size of the DER encoded integers @return CRYPT_OK if successful */ int der_encode_short_integer(unsigned long num, unsigned char *out, unsigned long *outlen) { unsigned long len, x, y, z; int err; LTC_ARGCHK(out != NULL); LTC_ARGCHK(outlen != NULL); /* force to 32 bits */ num &= 0xFFFFFFFFUL; /* find out how big this will be */ if ((err = der_length_short_integer(num, &len)) != CRYPT_OK) { return err; } if (*outlen < len) { *outlen = len; return CRYPT_BUFFER_OVERFLOW; } /* get len of output */ z = 0; y = num; while (y) { ++z; y >>= 8; } /* handle zero */ if (z == 0) { z = 1; } /* see if msb is set */ z += (num & (1UL << ((z << 3) - 1))) ? 1 : 0; /* adjust the number so the msB is non-zero */ for (x = 0; (z <= 4) && (x < (4 - z)); x++) { num <<= 8; } /* store header */ x = 0; out[x++] = 0x02; out[x++] = (unsigned char)z; /* if 31st bit is set output a leading zero and decrement count */ if (z == 5) { out[x++] = 0; --z; } /* store values */ for (y = 0; y < z; y++) { out[x++] = (unsigned char)((num >> 24) & 0xFF); num <<= 8; } /* we good */ *outlen = x; return CRYPT_OK; } #endif /* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/short_integer/der_encode_short_integer.c,v $ */ /* $Revision: 1.8 $ */ /* $Date: 2006/12/28 01:27:24 $ */ /* LibTomCrypt, modular cryptographic library -- Tom St Denis * * LibTomCrypt is a library that provides various cryptographic * algorithms in a highly modular and flexible manner. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tomstdenis@gmail.com, http://libtom.org */ /** @file der_encode_utctime.c ASN.1 DER, encode a UTCTIME, Tom St Denis */ #ifdef LTC_DER static const char baseten[] = "0123456789"; #define STORE_V(y) \ out[x++] = der_ia5_char_encode(baseten[(y / 10) % 10]); \ out[x++] = der_ia5_char_encode(baseten[y % 10]); /** Encodes a UTC time structure in DER format @param utctime The UTC time structure to encode @param out The destination of the DER encoding of the UTC time structure @param outlen [in/out] The length of the DER encoding @return CRYPT_OK if successful */ int der_encode_utctime(ltc_utctime *utctime, unsigned char *out, unsigned long *outlen) { unsigned long x, tmplen; int err; LTC_ARGCHK(utctime != NULL); LTC_ARGCHK(out != NULL); LTC_ARGCHK(outlen != NULL); if ((err = der_length_utctime(utctime, &tmplen)) != CRYPT_OK) { return err; } if (tmplen > *outlen) { *outlen = tmplen; return CRYPT_BUFFER_OVERFLOW; } /* store header */ out[0] = 0x17; /* store values */ x = 2; STORE_V(utctime->YY); STORE_V(utctime->MM); STORE_V(utctime->DD); STORE_V(utctime->hh); STORE_V(utctime->mm); STORE_V(utctime->ss); if (utctime->off_mm || utctime->off_hh) { out[x++] = der_ia5_char_encode(utctime->off_dir ? '-' : '+'); STORE_V(utctime->off_hh); STORE_V(utctime->off_mm); } else { out[x++] = der_ia5_char_encode('Z'); } /* store length */ out[1] = (unsigned char)(x - 2); /* all good let's return */ *outlen = x; return CRYPT_OK; } #endif /* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/utctime/der_encode_utctime.c,v $ */ /* $Revision: 1.10 $ */ /* $Date: 2006/12/28 01:27:24 $ */ /* LibTomCrypt, modular cryptographic library -- Tom St Denis * * LibTomCrypt is a library that provides various cryptographic * algorithms in a highly modular and flexible manner. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tomstdenis@gmail.com, http://libtom.org */ /** @file der_encode_utf8_string.c ASN.1 DER, encode a UTF8 STRING, Tom St Denis */ #ifdef LTC_DER /** Store an UTF8 STRING @param in The array of UTF8 to store (one per wchar_t) @param inlen The number of UTF8 to store @param out [out] The destination for the DER encoded UTF8 STRING @param outlen [in/out] The max size and resulting size of the DER UTF8 STRING @return CRYPT_OK if successful */ int der_encode_utf8_string(const wchar_t *in, unsigned long inlen, unsigned char *out, unsigned long *outlen) { unsigned long x, y, len; LTC_ARGCHK(in != NULL); LTC_ARGCHK(out != NULL); LTC_ARGCHK(outlen != NULL); /* get the size */ for (x = len = 0; x < inlen; x++) { if ((in[x] < 0) || (in[x] > 0x1FFFF)) { return CRYPT_INVALID_ARG; } len += der_utf8_charsize(in[x]); } if (len < 128) { y = 2 + len; } else if (len < 256) { y = 3 + len; } else if (len < 65536UL) { y = 4 + len; } else if (len < 16777216UL) { y = 5 + len; } else { return CRYPT_INVALID_ARG; } /* too big? */ if (y > *outlen) { *outlen = len; return CRYPT_BUFFER_OVERFLOW; } /* encode the header+len */ x = 0; out[x++] = 0x0C; if (len < 128) { out[x++] = (unsigned char)len; } else if (len < 256) { out[x++] = 0x81; out[x++] = (unsigned char)len; } else if (len < 65536UL) { out[x++] = 0x82; out[x++] = (unsigned char)((len >> 8) & 255); out[x++] = (unsigned char)(len & 255); } else if (len < 16777216UL) { out[x++] = 0x83; out[x++] = (unsigned char)((len >> 16) & 255); out[x++] = (unsigned char)((len >> 8) & 255); out[x++] = (unsigned char)(len & 255); } else { return CRYPT_INVALID_ARG; } /* store UTF8 */ for (y = 0; y < inlen; y++) { switch (der_utf8_charsize(in[y])) { case 1: out[x++] = (unsigned char)in[y]; break; case 2: out[x++] = 0xC0 | ((in[y] >> 6) & 0x1F); out[x++] = 0x80 | (in[y] & 0x3F); break; case 3: out[x++] = 0xE0 | ((in[y] >> 12) & 0x0F); out[x++] = 0x80 | ((in[y] >> 6) & 0x3F); out[x++] = 0x80 | (in[y] & 0x3F); break; case 4: out[x++] = 0xF0 | ((in[y] >> 18) & 0x07); out[x++] = 0x80 | ((in[y] >> 12) & 0x3F); out[x++] = 0x80 | ((in[y] >> 6) & 0x3F); out[x++] = 0x80 | (in[y] & 0x3F); break; } } /* retun length */ *outlen = x; return CRYPT_OK; } #endif /* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/utf8/der_encode_utf8_string.c,v $ */ /* $Revision: 1.9 $ */ /* $Date: 2006/12/28 01:27:24 $ */ /* LibTomCrypt, modular cryptographic library -- Tom St Denis * * LibTomCrypt is a library that provides various cryptographic * algorithms in a highly modular and flexible manner. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tomstdenis@gmail.com, http://libtom.org */ /** @file der_length_bit_string.c ASN.1 DER, get length of BIT STRING, Tom St Denis */ #ifdef LTC_DER /** Gets length of DER encoding of BIT STRING @param nbits The number of bits in the string to encode @param outlen [out] The length of the DER encoding for the given string @return CRYPT_OK if successful */ int der_length_bit_string(unsigned long nbits, unsigned long *outlen) { unsigned long nbytes; LTC_ARGCHK(outlen != NULL); /* get the number of the bytes */ nbytes = (nbits >> 3) + ((nbits & 7) ? 1 : 0) + 1; if (nbytes < 128) { /* 03 LL PP DD DD DD ... */ *outlen = 2 + nbytes; } else if (nbytes < 256) { /* 03 81 LL PP DD DD DD ... */ *outlen = 3 + nbytes; } else if (nbytes < 65536) { /* 03 82 LL LL PP DD DD DD ... */ *outlen = 4 + nbytes; } else { return CRYPT_INVALID_ARG; } return CRYPT_OK; } #endif /* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/bit/der_length_bit_string.c,v $ */ /* $Revision: 1.3 $ */ /* $Date: 2006/12/28 01:27:24 $ */ /* LibTomCrypt, modular cryptographic library -- Tom St Denis * * LibTomCrypt is a library that provides various cryptographic * algorithms in a highly modular and flexible manner. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tomstdenis@gmail.com, http://libtom.org */ /** @file der_length_boolean.c ASN.1 DER, get length of a BOOLEAN, Tom St Denis */ #ifdef LTC_DER /** Gets length of DER encoding of a BOOLEAN @param outlen [out] The length of the DER encoding @return CRYPT_OK if successful */ int der_length_boolean(unsigned long *outlen) { LTC_ARGCHK(outlen != NULL); *outlen = 3; return CRYPT_OK; } #endif /* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/boolean/der_length_boolean.c,v $ */ /* $Revision: 1.3 $ */ /* $Date: 2006/12/28 01:27:24 $ */ /* LibTomCrypt, modular cryptographic library -- Tom St Denis * * LibTomCrypt is a library that provides various cryptographic * algorithms in a highly modular and flexible manner. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tomstdenis@gmail.com, http://libtom.org */ /** @file der_length_ia5_string.c ASN.1 DER, get length of IA5 STRING, Tom St Denis */ #ifdef LTC_DER static const struct { int code, value; } ia5_table[] = { { '\0', 0 }, { '\a', 7 }, { '\b', 8 }, { '\t', 9 }, { '\n', 10 }, { '\f', 12 }, { '\r', 13 }, { ' ', 32 }, { '!', 33 }, { '"', 34 }, { '#', 35 }, { '$', 36 }, { '%', 37 }, { '&', 38 }, { '\'', 39 }, { '(', 40 }, { ')', 41 }, { '*', 42 }, { '+', 43 }, { ',', 44 }, { '-', 45 }, { '.', 46 }, { '/', 47 }, { '0', 48 }, { '1', 49 }, { '2', 50 }, { '3', 51 }, { '4', 52 }, { '5', 53 }, { '6', 54 }, { '7', 55 }, { '8', 56 }, { '9', 57 }, { ':', 58 }, { ';', 59 }, { '<', 60 }, { '=', 61 }, { '>', 62 }, { '?', 63 }, { '@', 64 }, { 'A', 65 }, { 'B', 66 }, { 'C', 67 }, { 'D', 68 }, { 'E', 69 }, { 'F', 70 }, { 'G', 71 }, { 'H', 72 }, { 'I', 73 }, { 'J', 74 }, { 'K', 75 }, { 'L', 76 }, { 'M', 77 }, { 'N', 78 }, { 'O', 79 }, { 'P', 80 }, { 'Q', 81 }, { 'R', 82 }, { 'S', 83 }, { 'T', 84 }, { 'U', 85 }, { 'V', 86 }, { 'W', 87 }, { 'X', 88 }, { 'Y', 89 }, { 'Z', 90 }, { '[', 91 }, { '\\', 92 }, { ']', 93 }, { '^', 94 }, { '_', 95 }, { '`', 96 }, { 'a', 97 }, { 'b', 98 }, { 'c', 99 }, { 'd', 100 }, { 'e', 101 }, { 'f', 102 }, { 'g', 103 }, { 'h', 104 }, { 'i', 105 }, { 'j', 106 }, { 'k', 107 }, { 'l', 108 }, { 'm', 109 }, { 'n', 110 }, { 'o', 111 }, { 'p', 112 }, { 'q', 113 }, { 'r', 114 }, { 's', 115 }, { 't', 116 }, { 'u', 117 }, { 'v', 118 }, { 'w', 119 }, { 'x', 120 }, { 'y', 121 }, { 'z', 122 }, { '{', 123 }, { '|', 124 }, { '}', 125 }, { '~', 126 } }; int der_ia5_char_encode(int c) { int x; for (x = 0; x < (int)(sizeof(ia5_table) / sizeof(ia5_table[0])); x++) { if (ia5_table[x].code == c) { return ia5_table[x].value; } } return -1; } int der_ia5_value_decode(int v) { int x; for (x = 0; x < (int)(sizeof(ia5_table) / sizeof(ia5_table[0])); x++) { if (ia5_table[x].value == v) { return ia5_table[x].code; } } return -1; } /** Gets length of DER encoding of IA5 STRING @param octets The values you want to encode @param noctets The number of octets in the string to encode @param outlen [out] The length of the DER encoding for the given string @return CRYPT_OK if successful */ int der_length_ia5_string(const unsigned char *octets, unsigned long noctets, unsigned long *outlen) { unsigned long x; LTC_ARGCHK(outlen != NULL); LTC_ARGCHK(octets != NULL); /* scan string for validity */ for (x = 0; x < noctets; x++) { if (der_ia5_char_encode(octets[x]) == -1) { return CRYPT_INVALID_ARG; } } if (noctets < 128) { /* 16 LL DD DD DD ... */ *outlen = 2 + noctets; } else if (noctets < 256) { /* 16 81 LL DD DD DD ... */ *outlen = 3 + noctets; } else if (noctets < 65536UL) { /* 16 82 LL LL DD DD DD ... */ *outlen = 4 + noctets; } else if (noctets < 16777216UL) { /* 16 83 LL LL LL DD DD DD ... */ *outlen = 5 + noctets; } else { return CRYPT_INVALID_ARG; } return CRYPT_OK; } #endif /* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/ia5/der_length_ia5_string.c,v $ */ /* $Revision: 1.3 $ */ /* $Date: 2006/12/28 01:27:24 $ */ /* LibTomCrypt, modular cryptographic library -- Tom St Denis * * LibTomCrypt is a library that provides various cryptographic * algorithms in a highly modular and flexible manner. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tomstdenis@gmail.com, http://libtom.org */ /** @file der_length_integer.c ASN.1 DER, get length of encoding, Tom St Denis */ #ifdef LTC_DER /** Gets length of DER encoding of num @param num The int to get the size of @param outlen [out] The length of the DER encoding for the given integer @return CRYPT_OK if successful */ int der_length_integer(void *num, unsigned long *outlen) { unsigned long z, len; int leading_zero; LTC_ARGCHK(num != NULL); LTC_ARGCHK(outlen != NULL); if (mp_cmp_d(num, 0) != LTC_MP_LT) { /* positive */ /* we only need a leading zero if the msb of the first byte is one */ if (((mp_count_bits(num) & 7) == 0) || (mp_iszero(num) == LTC_MP_YES)) { leading_zero = 1; } else { leading_zero = 0; } /* size for bignum */ z = len = leading_zero + mp_unsigned_bin_size(num); } else { /* it's negative */ /* find power of 2 that is a multiple of eight and greater than count bits */ leading_zero = 0; z = mp_count_bits(num); z = z + (8 - (z & 7)); if (((mp_cnt_lsb(num) + 1) == mp_count_bits(num)) && ((mp_count_bits(num) & 7) == 0)) --z; len = z = z >> 3; } /* now we need a length */ if (z < 128) { /* short form */ ++len; } else { /* long form (relies on z != 0), assumes length bytes < 128 */ ++len; while (z) { ++len; z >>= 8; } } /* we need a 0x02 to indicate it's INTEGER */ ++len; /* return length */ *outlen = len; return CRYPT_OK; } #endif /* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/integer/der_length_integer.c,v $ */ /* $Revision: 1.5 $ */ /* $Date: 2006/12/28 01:27:24 $ */ /* LibTomCrypt, modular cryptographic library -- Tom St Denis * * LibTomCrypt is a library that provides various cryptographic * algorithms in a highly modular and flexible manner. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tomstdenis@gmail.com, http://libtom.org */ /** @file der_length_object_identifier.c ASN.1 DER, get length of Object Identifier, Tom St Denis */ #ifdef LTC_DER unsigned long der_object_identifier_bits(unsigned long x) { unsigned long c; x &= 0xFFFFFFFF; c = 0; while (x) { ++c; x >>= 1; } return c; } /** Gets length of DER encoding of Object Identifier @param nwords The number of OID words @param words The actual OID words to get the size of @param outlen [out] The length of the DER encoding for the given string @return CRYPT_OK if successful */ int der_length_object_identifier(unsigned long *words, unsigned long nwords, unsigned long *outlen) { unsigned long y, z, t, wordbuf; LTC_ARGCHK(words != NULL); LTC_ARGCHK(outlen != NULL); /* must be >= 2 words */ if (nwords < 2) { return CRYPT_INVALID_ARG; } /* word1 = 0,1,2,3 and word2 0..39 */ if ((words[0] > 3) || ((words[0] < 2) && (words[1] > 39))) { return CRYPT_INVALID_ARG; } /* leading word is the first two */ z = 0; wordbuf = words[0] * 40 + words[1]; for (y = 1; y < nwords; y++) { t = der_object_identifier_bits(wordbuf); z += t / 7 + ((t % 7) ? 1 : 0) + (wordbuf == 0 ? 1 : 0); if (y < nwords - 1) { /* grab next word */ wordbuf = words[y + 1]; } } /* now depending on the length our length encoding changes */ if (z < 128) { z += 2; } else if (z < 256) { z += 3; } else if (z < 65536UL) { z += 4; } else { return CRYPT_INVALID_ARG; } *outlen = z; return CRYPT_OK; } #endif /* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/object_identifier/der_length_object_identifier.c,v $ */ /* $Revision: 1.5 $ */ /* $Date: 2006/12/28 01:27:24 $ */ /* LibTomCrypt, modular cryptographic library -- Tom St Denis * * LibTomCrypt is a library that provides various cryptographic * algorithms in a highly modular and flexible manner. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tomstdenis@gmail.com, http://libtom.org */ /** @file der_length_octet_string.c ASN.1 DER, get length of OCTET STRING, Tom St Denis */ #ifdef LTC_DER /** Gets length of DER encoding of OCTET STRING @param noctets The number of octets in the string to encode @param outlen [out] The length of the DER encoding for the given string @return CRYPT_OK if successful */ int der_length_octet_string(unsigned long noctets, unsigned long *outlen) { LTC_ARGCHK(outlen != NULL); if (noctets < 128) { /* 04 LL DD DD DD ... */ *outlen = 2 + noctets; } else if (noctets < 256) { /* 04 81 LL DD DD DD ... */ *outlen = 3 + noctets; } else if (noctets < 65536UL) { /* 04 82 LL LL DD DD DD ... */ *outlen = 4 + noctets; } else if (noctets < 16777216UL) { /* 04 83 LL LL LL DD DD DD ... */ *outlen = 5 + noctets; } else { return CRYPT_INVALID_ARG; } return CRYPT_OK; } #endif /* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/octet/der_length_octet_string.c,v $ */ /* $Revision: 1.3 $ */ /* $Date: 2006/12/28 01:27:24 $ */ /* LibTomCrypt, modular cryptographic library -- Tom St Denis * * LibTomCrypt is a library that provides various cryptographic * algorithms in a highly modular and flexible manner. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tomstdenis@gmail.com, http://libtom.org */ /** @file der_length_printable_string.c ASN.1 DER, get length of Printable STRING, Tom St Denis */ #ifdef LTC_DER static const struct { int code, value; } printable_table[] = { { ' ', 32 }, { '\'', 39 }, { '(', 40 }, { ')', 41 }, { '+', 43 }, { ',', 44 }, { '-', 45 }, { '.', 46 }, { '/', 47 }, { '0', 48 }, { '1', 49 }, { '2', 50 }, { '3', 51 }, { '4', 52 }, { '5', 53 }, { '6', 54 }, { '7', 55 }, { '8', 56 }, { '9', 57 }, { ':', 58 }, { '=', 61 }, { '?', 63 }, { 'A', 65 }, { 'B', 66 }, { 'C', 67 }, { 'D', 68 }, { 'E', 69 }, { 'F', 70 }, { 'G', 71 }, { 'H', 72 }, { 'I', 73 }, { 'J', 74 }, { 'K', 75 }, { 'L', 76 }, { 'M', 77 }, { 'N', 78 }, { 'O', 79 }, { 'P', 80 }, { 'Q', 81 }, { 'R', 82 }, { 'S', 83 }, { 'T', 84 }, { 'U', 85 }, { 'V', 86 }, { 'W', 87 }, { 'X', 88 }, { 'Y', 89 }, { 'Z', 90 }, { 'a', 97 }, { 'b', 98 }, { 'c', 99 }, { 'd', 100 }, { 'e', 101 }, { 'f', 102 }, { 'g', 103 }, { 'h', 104 }, { 'i', 105 }, { 'j', 106 }, { 'k', 107 }, { 'l', 108 }, { 'm', 109 }, { 'n', 110 }, { 'o', 111 }, { 'p', 112 }, { 'q', 113 }, { 'r', 114 }, { 's', 115 }, { 't', 116 }, { 'u', 117 }, { 'v', 118 }, { 'w', 119 }, { 'x', 120 }, { 'y', 121 }, { 'z', 122 }, }; int der_printable_char_encode(int c) { int x; for (x = 0; x < (int)(sizeof(printable_table) / sizeof(printable_table[0])); x++) { if (printable_table[x].code == c) { return printable_table[x].value; } } return -1; } int der_printable_value_decode(int v) { int x; for (x = 0; x < (int)(sizeof(printable_table) / sizeof(printable_table[0])); x++) { if (printable_table[x].value == v) { return printable_table[x].code; } } return -1; } /** Gets length of DER encoding of Printable STRING @param octets The values you want to encode @param noctets The number of octets in the string to encode @param outlen [out] The length of the DER encoding for the given string @return CRYPT_OK if successful */ int der_length_printable_string(const unsigned char *octets, unsigned long noctets, unsigned long *outlen) { unsigned long x; LTC_ARGCHK(outlen != NULL); LTC_ARGCHK(octets != NULL); /* scan string for validity */ for (x = 0; x < noctets; x++) { if (der_printable_char_encode(octets[x]) == -1) { return CRYPT_INVALID_ARG; } } if (noctets < 128) { /* 16 LL DD DD DD ... */ *outlen = 2 + noctets; } else if (noctets < 256) { /* 16 81 LL DD DD DD ... */ *outlen = 3 + noctets; } else if (noctets < 65536UL) { /* 16 82 LL LL DD DD DD ... */ *outlen = 4 + noctets; } else if (noctets < 16777216UL) { /* 16 83 LL LL LL DD DD DD ... */ *outlen = 5 + noctets; } else { return CRYPT_INVALID_ARG; } return CRYPT_OK; } #endif /* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/printable_string/der_length_printable_string.c,v $ */ /* $Revision: 1.3 $ */ /* $Date: 2006/12/28 01:27:24 $ */ /* LibTomCrypt, modular cryptographic library -- Tom St Denis * * LibTomCrypt is a library that provides various cryptographic * algorithms in a highly modular and flexible manner. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tomstdenis@gmail.com, http://libtom.org */ /** @file der_length_sequence.c ASN.1 DER, length a SEQUENCE, Tom St Denis */ #ifdef LTC_DER /** Get the length of a DER sequence @param list The sequences of items in the SEQUENCE @param inlen The number of items @param outlen [out] The length required in octets to store it @return CRYPT_OK on success */ int der_length_sequence(ltc_asn1_list *list, unsigned long inlen, unsigned long *outlen) { int err, type; unsigned long size, x, y, z, i; void *data; LTC_ARGCHK(list != NULL); LTC_ARGCHK(outlen != NULL); /* get size of output that will be required */ y = 0; for (i = 0; i < inlen; i++) { type = list[i].type; size = list[i].size; data = list[i].data; if (type == LTC_ASN1_EOL) { break; } switch (type) { case LTC_ASN1_BOOLEAN: if ((err = der_length_boolean(&x)) != CRYPT_OK) { goto LBL_ERR; } y += x; break; case LTC_ASN1_INTEGER: if ((err = der_length_integer(data, &x)) != CRYPT_OK) { goto LBL_ERR; } y += x; break; case LTC_ASN1_SHORT_INTEGER: if ((err = der_length_short_integer(*((unsigned long *)data), &x)) != CRYPT_OK) { goto LBL_ERR; } y += x; break; case LTC_ASN1_BIT_STRING: if ((err = der_length_bit_string(size, &x)) != CRYPT_OK) { goto LBL_ERR; } y += x; break; case LTC_ASN1_OCTET_STRING: if ((err = der_length_octet_string(size, &x)) != CRYPT_OK) { goto LBL_ERR; } y += x; break; case LTC_ASN1_NULL: y += 2; break; case LTC_ASN1_OBJECT_IDENTIFIER: if ((err = der_length_object_identifier(AUTO_CAST(data), size, &x)) != CRYPT_OK) { goto LBL_ERR; } y += x; break; case LTC_ASN1_IA5_STRING: if ((err = der_length_ia5_string(AUTO_CAST(data), size, &x)) != CRYPT_OK) { goto LBL_ERR; } y += x; break; case LTC_ASN1_PRINTABLE_STRING: if ((err = der_length_printable_string(AUTO_CAST(data), size, &x)) != CRYPT_OK) { goto LBL_ERR; } y += x; break; case LTC_ASN1_UTCTIME: if ((err = der_length_utctime(AUTO_CAST(data), &x)) != CRYPT_OK) { goto LBL_ERR; } y += x; break; case LTC_ASN1_UTF8_STRING: if ((err = der_length_utf8_string(AUTO_CAST(data), size, &x)) != CRYPT_OK) { goto LBL_ERR; } y += x; break; case LTC_ASN1_SET: case LTC_ASN1_SETOF: case LTC_ASN1_SEQUENCE: if ((err = der_length_sequence(AUTO_CAST(data), size, &x)) != CRYPT_OK) { goto LBL_ERR; } y += x; break; default: err = CRYPT_INVALID_ARG; goto LBL_ERR; } } /* calc header size */ z = y; if (y < 128) { y += 2; } else if (y < 256) { /* 0x30 0x81 LL */ y += 3; } else if (y < 65536UL) { /* 0x30 0x82 LL LL */ y += 4; } else if (y < 16777216UL) { /* 0x30 0x83 LL LL LL */ y += 5; } else { err = CRYPT_INVALID_ARG; goto LBL_ERR; } /* store size */ *outlen = y; err = CRYPT_OK; LBL_ERR: return err; } #endif /* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/sequence/der_length_sequence.c,v $ */ /* $Revision: 1.14 $ */ /* $Date: 2006/12/28 01:27:24 $ */ /* LibTomCrypt, modular cryptographic library -- Tom St Denis * * LibTomCrypt is a library that provides various cryptographic * algorithms in a highly modular and flexible manner. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tomstdenis@gmail.com, http://libtom.org */ /** @file der_length_short_integer.c ASN.1 DER, get length of encoding, Tom St Denis */ #ifdef LTC_DER /** Gets length of DER encoding of num @param num The integer to get the size of @param outlen [out] The length of the DER encoding for the given integer @return CRYPT_OK if successful */ int der_length_short_integer(unsigned long num, unsigned long *outlen) { unsigned long z, y, len; LTC_ARGCHK(outlen != NULL); /* force to 32 bits */ num &= 0xFFFFFFFFUL; /* get the number of bytes */ z = 0; y = num; while (y) { ++z; y >>= 8; } /* handle zero */ if (z == 0) { z = 1; } /* we need a 0x02 to indicate it's INTEGER */ len = 1; /* length byte */ ++len; /* bytes in value */ len += z; /* see if msb is set */ len += (num & (1UL << ((z << 3) - 1))) ? 1 : 0; /* return length */ *outlen = len; return CRYPT_OK; } #endif /* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/short_integer/der_length_short_integer.c,v $ */ /* $Revision: 1.6 $ */ /* $Date: 2006/12/28 01:27:24 $ */ /* LibTomCrypt, modular cryptographic library -- Tom St Denis * * LibTomCrypt is a library that provides various cryptographic * algorithms in a highly modular and flexible manner. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tomstdenis@gmail.com, http://libtom.org */ /** @file der_length_utctime.c ASN.1 DER, get length of UTCTIME, Tom St Denis */ #ifdef LTC_DER /** Gets length of DER encoding of UTCTIME @param utctime The UTC time structure to get the size of @param outlen [out] The length of the DER encoding @return CRYPT_OK if successful */ int der_length_utctime(ltc_utctime *utctime, unsigned long *outlen) { LTC_ARGCHK(outlen != NULL); LTC_ARGCHK(utctime != NULL); if ((utctime->off_hh == 0) && (utctime->off_mm == 0)) { /* we encode as YYMMDDhhmmssZ */ *outlen = 2 + 13; } else { /* we encode as YYMMDDhhmmss{+|-}hh'mm' */ *outlen = 2 + 17; } return CRYPT_OK; } #endif /* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/utctime/der_length_utctime.c,v $ */ /* $Revision: 1.5 $ */ /* $Date: 2006/12/28 01:27:24 $ */ /* LibTomCrypt, modular cryptographic library -- Tom St Denis * * LibTomCrypt is a library that provides various cryptographic * algorithms in a highly modular and flexible manner. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tomstdenis@gmail.com, http://libtom.org */ /** @file der_length_utf8_string.c ASN.1 DER, get length of UTF8 STRING, Tom St Denis */ #ifdef LTC_DER /** Return the size in bytes of a UTF-8 character @param c The UTF-8 character to measure @return The size in bytes */ unsigned long der_utf8_charsize(const wchar_t c) { if (c <= 0x7F) { return 1; } else if (c <= 0x7FF) { return 2; } else if (c <= 0xFFFF) { return 3; } else { return 4; } } /** Gets length of DER encoding of UTF8 STRING @param in The characters to measure the length of @param noctets The number of octets in the string to encode @param outlen [out] The length of the DER encoding for the given string @return CRYPT_OK if successful */ int der_length_utf8_string(const wchar_t *in, unsigned long noctets, unsigned long *outlen) { unsigned long x, len; LTC_ARGCHK(in != NULL); LTC_ARGCHK(outlen != NULL); len = 0; for (x = 0; x < noctets; x++) { if ((in[x] < 0) || (in[x] > 0x10FFFF)) { return CRYPT_INVALID_ARG; } len += der_utf8_charsize(in[x]); } if (len < 128) { /* 0C LL DD DD DD ... */ *outlen = 2 + len; } else if (len < 256) { /* 0C 81 LL DD DD DD ... */ *outlen = 3 + len; } else if (len < 65536UL) { /* 0C 82 LL LL DD DD DD ... */ *outlen = 4 + len; } else if (len < 16777216UL) { /* 0C 83 LL LL LL DD DD DD ... */ *outlen = 5 + len; } else { return CRYPT_INVALID_ARG; } return CRYPT_OK; } #endif /* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/utf8/der_length_utf8_string.c,v $ */ /* $Revision: 1.6 $ */ /* $Date: 2006/12/28 01:27:24 $ */ /* LibTomCrypt, modular cryptographic library -- Tom St Denis * * LibTomCrypt is a library that provides various cryptographic * algorithms in a highly modular and flexible manner. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tomstdenis@gmail.com, http://libtom.org */ /** @file der_sequence_free.c ASN.1 DER, free's a structure allocated by der_decode_sequence_flexi(), Tom St Denis */ #ifdef LTC_DER /** Free memory allocated by der_decode_sequence_flexi() @param in The list to free */ void der_sequence_free(ltc_asn1_list *in) { ltc_asn1_list *l; /* walk to the start of the chain */ while (in->prev != NULL || in->parent != NULL) { if (in->parent != NULL) { in = in->parent; } else { in = in->prev; } } /* now walk the list and free stuff */ while (in != NULL) { /* is there a child? */ if (in->child) { /* disconnect */ in->child->parent = NULL; der_sequence_free(in->child); } switch (in->type) { case LTC_ASN1_SET: case LTC_ASN1_SETOF: case LTC_ASN1_SEQUENCE: break; case LTC_ASN1_INTEGER: if (in->data != NULL) { mp_clear(in->data); } break; default: if (in->data != NULL) { XFREE(in->data); } } /* move to next and free current */ l = in->next; free(in); in = l; } } #endif /* $Source: /cvs/libtom/libtomcrypt/src/pk/asn1/der/sequence/der_sequence_free.c,v $ */ /* $Revision: 1.4 $ */ /* $Date: 2006/12/28 01:27:24 $ */ /* LibTomCrypt, modular cryptographic library -- Tom St Denis * * LibTomCrypt is a library that provides various cryptographic * algorithms in a highly modular and flexible manner. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tomstdenis@gmail.com, http://libtom.org */ /* Implements ECC over Z/pZ for curve y^2 = x^3 - 3x + b * * All curves taken from NIST recommendation paper of July 1999 * Available at http://csrc.nist.gov/cryptval/dss.htm */ /** @file ecc.c ECC Crypto, Tom St Denis */ #ifdef LTC_MECC /* This holds the key settings. ***MUST*** be organized by size from smallest to largest. */ const ltc_ecc_set_type ltc_ecc_sets[] = { #ifdef ECC112 { 14, "SECP112R1", "DB7C2ABF62E35E668076BEAD208B", "659EF8BA043916EEDE8911702B22", "DB7C2ABF62E35E7628DFAC6561C5", "09487239995A5EE76B55F9C2F098", "A89CE5AF8724C0A23E0E0FF77500" }, #endif #ifdef ECC128 { 16, "SECP128R1", "FFFFFFFDFFFFFFFFFFFFFFFFFFFFFFFF", "E87579C11079F43DD824993C2CEE5ED3", "FFFFFFFE0000000075A30D1B9038A115", "161FF7528B899B2D0C28607CA52C5B86", "CF5AC8395BAFEB13C02DA292DDED7A83", }, #endif #ifdef ECC160 { 20, "SECP160R1", "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7FFFFFFF", "1C97BEFC54BD7A8B65ACF89F81D4D4ADC565FA45", "0100000000000000000001F4C8F927AED3CA752257", "4A96B5688EF573284664698968C38BB913CBFC82", "23A628553168947D59DCC912042351377AC5FB32", }, #endif #ifdef ECC192 { 24, "ECC-192", "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFF", "64210519E59C80E70FA7E9AB72243049FEB8DEECC146B9B1", "FFFFFFFFFFFFFFFFFFFFFFFF99DEF836146BC9B1B4D22831", "188DA80EB03090F67CBF20EB43A18800F4FF0AFD82FF1012", "7192B95FFC8DA78631011ED6B24CDD573F977A11E794811", }, #endif #ifdef ECC224 { 28, "ECC-224", "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000000000000001", "B4050A850C04B3ABF54132565044B0B7D7BFD8BA270B39432355FFB4", "FFFFFFFFFFFFFFFFFFFFFFFFFFFF16A2E0B8F03E13DD29455C5C2A3D", "B70E0CBD6BB4BF7F321390B94A03C1D356C21122343280D6115C1D21", "BD376388B5F723FB4C22DFE6CD4375A05A07476444D5819985007E34", }, #endif #ifdef ECC256 { 32, "ECC-256", "FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF", "5AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B", "FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551", "6B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296", "4FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5", }, #endif #ifdef ECC384 { 48, "ECC-384", "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFF0000000000000000FFFFFFFF", "B3312FA7E23EE7E4988E056BE3F82D19181D9C6EFE8141120314088F5013875AC656398D8A2ED19D2A85C8EDD3EC2AEF", "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC7634D81F4372DDF581A0DB248B0A77AECEC196ACCC52973", "AA87CA22BE8B05378EB1C71EF320AD746E1D3B628BA79B9859F741E082542A385502F25DBF55296C3A545E3872760AB7", "3617DE4A96262C6F5D9E98BF9292DC29F8F41DBD289A147CE9DA3113B5F0B8C00A60B1CE1D7E819D7A431D7C90EA0E5F", }, #endif #ifdef ECC521 { 66, "ECC-521", "1FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", "51953EB9618E1C9A1F929A21A0B68540EEA2DA725B99B315F3B8B489918EF109E156193951EC7E937B1652C0BD3BB1BF073573DF883D2C34F1EF451FD46B503F00", "1FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA51868783BF2F966B7FCC0148F709A5D03BB5C9B8899C47AEBB6FB71E91386409", "C6858E06B70404E9CD9E3ECB662395B4429C648139053FB521F828AF606B4D3DBAA14B5E77EFE75928FE1DC127A2FFA8DE3348B3C1856A429BF97E7E31C2E5BD66", "11839296A789A3BC0045C8A5FB42C7D1BD998F54449579B446817AFBD17273E662C97EE72995EF42640C550B9013FAD0761353C7086A272C24088BE94769FD16650", }, #endif { 0, NULL, NULL, NULL, NULL, NULL, NULL } }; #endif /* $Source: /cvs/libtom/libtomcrypt/src/pk/ecc/ecc.c,v $ */ /* $Revision: 1.40 $ */ /* $Date: 2007/05/12 14:32:35 $ */ /* LibTomCrypt, modular cryptographic library -- Tom St Denis * * LibTomCrypt is a library that provides various cryptographic * algorithms in a highly modular and flexible manner. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tomstdenis@gmail.com, http://libtom.org */ /* Implements ECC over Z/pZ for curve y^2 = x^3 - 3x + b * * All curves taken from NIST recommendation paper of July 1999 * Available at http://csrc.nist.gov/cryptval/dss.htm */ /** @file ecc_ansi_x963_export.c ECC Crypto, Tom St Denis */ #ifdef LTC_MECC /** ECC X9.63 (Sec. 4.3.6) uncompressed export @param key Key to export @param out [out] destination of export @param outlen [in/out] Length of destination and final output size Return CRYPT_OK on success */ int ecc_ansi_x963_export(ecc_key *key, unsigned char *out, unsigned long *outlen) { unsigned char buf[ECC_BUF_SIZE]; unsigned long numlen; LTC_ARGCHK(key != NULL); LTC_ARGCHK(out != NULL); LTC_ARGCHK(outlen != NULL); if (ltc_ecc_is_valid_idx(key->idx) == 0) { return CRYPT_INVALID_ARG; } numlen = key->dp->size; if (*outlen < (1 + 2 * numlen)) { *outlen = 1 + 2 * numlen; return CRYPT_BUFFER_OVERFLOW; } /* store byte 0x04 */ out[0] = 0x04; /* pad and store x */ zeromem(buf, sizeof(buf)); mp_to_unsigned_bin(key->pubkey.x, buf + (numlen - mp_unsigned_bin_size(key->pubkey.x))); XMEMCPY(out + 1, buf, numlen); /* pad and store y */ zeromem(buf, sizeof(buf)); mp_to_unsigned_bin(key->pubkey.y, buf + (numlen - mp_unsigned_bin_size(key->pubkey.y))); XMEMCPY(out + 1 + numlen, buf, numlen); *outlen = 1 + 2 * numlen; return CRYPT_OK; } #endif /* $Source: /cvs/libtom/libtomcrypt/src/pk/ecc/ecc_ansi_x963_export.c,v $ */ /* $Revision: 1.6 $ */ /* $Date: 2007/05/12 14:32:35 $ */ /* LibTomCrypt, modular cryptographic library -- Tom St Denis * * LibTomCrypt is a library that provides various cryptographic * algorithms in a highly modular and flexible manner. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tomstdenis@gmail.com, http://libtom.org */ /* Implements ECC over Z/pZ for curve y^2 = x^3 - 3x + b * * All curves taken from NIST recommendation paper of July 1999 * Available at http://csrc.nist.gov/cryptval/dss.htm */ /** @file ecc_ansi_x963_import.c ECC Crypto, Tom St Denis */ #ifdef LTC_MECC /** Import an ANSI X9.63 format public key @param in The input data to read @param inlen The length of the input data @param key [out] destination to store imported key \ */ int ecc_ansi_x963_import(const unsigned char *in, unsigned long inlen, ecc_key *key) { return ecc_ansi_x963_import_ex(in, inlen, key, NULL); } int ecc_ansi_x963_import_ex(const unsigned char *in, unsigned long inlen, ecc_key *key, ltc_ecc_set_type *dp) { int x, err; LTC_ARGCHK(in != NULL); LTC_ARGCHK(key != NULL); /* must be odd */ if ((inlen & 1) == 0) { return CRYPT_INVALID_ARG; } /* init key */ if (mp_init_multi(&key->pubkey.x, &key->pubkey.y, &key->pubkey.z, &key->k, NULL) != CRYPT_OK) { return CRYPT_MEM; } /* check for 4, 6 or 7 */ if ((in[0] != 4) && (in[0] != 6) && (in[0] != 7)) { err = CRYPT_INVALID_PACKET; goto error; } /* read data */ if ((err = mp_read_unsigned_bin(key->pubkey.x, (unsigned char *)in + 1, (inlen - 1) >> 1)) != CRYPT_OK) { goto error; } if ((err = mp_read_unsigned_bin(key->pubkey.y, (unsigned char *)in + 1 + ((inlen - 1) >> 1), (inlen - 1) >> 1)) != CRYPT_OK) { goto error; } if ((err = mp_set(key->pubkey.z, 1)) != CRYPT_OK) { goto error; } if (dp == NULL) { /* determine the idx */ for (x = 0; ltc_ecc_sets[x].size != 0; x++) { if ((unsigned)ltc_ecc_sets[x].size >= ((inlen - 1) >> 1)) { break; } } if (ltc_ecc_sets[x].size == 0) { err = CRYPT_INVALID_PACKET; goto error; } /* set the idx */ key->idx = x; key->dp = <c_ecc_sets[x]; } else { if (((inlen - 1) >> 1) != (unsigned long)dp->size) { err = CRYPT_INVALID_PACKET; goto error; } key->idx = -1; key->dp = dp; } key->type = PK_PUBLIC; /* we're done */ return CRYPT_OK; error: mp_clear_multi(key->pubkey.x, key->pubkey.y, key->pubkey.z, key->k, NULL); return err; } #endif /* $Source: /cvs/libtom/libtomcrypt/src/pk/ecc/ecc_ansi_x963_import.c,v $ */ /* $Revision: 1.11 $ */ /* $Date: 2007/05/12 14:32:35 $ */ /* LibTomCrypt, modular cryptographic library -- Tom St Denis * * LibTomCrypt is a library that provides various cryptographic * algorithms in a highly modular and flexible manner. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tomstdenis@gmail.com, http://libtom.org */ /* Implements ECC over Z/pZ for curve y^2 = x^3 - 3x + b * * All curves taken from NIST recommendation paper of July 1999 * Available at http://csrc.nist.gov/cryptval/dss.htm */ /** @file ecc_decrypt_key.c ECC Crypto, Tom St Denis */ #ifdef LTC_MECC /** Decrypt an ECC encrypted key @param in The ciphertext @param inlen The length of the ciphertext (octets) @param out [out] The plaintext @param outlen [in/out] The max size and resulting size of the plaintext @param key The corresponding private ECC key @return CRYPT_OK if successful */ int ecc_decrypt_key(const unsigned char *in, unsigned long inlen, unsigned char *out, unsigned long *outlen, ecc_key *key) { unsigned char *ecc_shared, *skey, *pub_expt; unsigned long x, y, hashOID[32]; int hash, err; ecc_key pubkey; ltc_asn1_list decode[3]; LTC_ARGCHK(in != NULL); LTC_ARGCHK(out != NULL); LTC_ARGCHK(outlen != NULL); LTC_ARGCHK(key != NULL); /* right key type? */ if (key->type != PK_PRIVATE) { return CRYPT_PK_NOT_PRIVATE; } /* decode to find out hash */ LTC_SET_ASN1(decode, 0, LTC_ASN1_OBJECT_IDENTIFIER, hashOID, sizeof(hashOID) / sizeof(hashOID[0])); if ((err = der_decode_sequence(in, inlen, decode, 1)) != CRYPT_OK) { return err; } hash = find_hash_oid(hashOID, decode[0].size); if (hash_is_valid(hash) != CRYPT_OK) { return CRYPT_INVALID_PACKET; } /* we now have the hash! */ /* allocate memory */ pub_expt = AUTO_CAST(XMALLOC(ECC_BUF_SIZE)); ecc_shared = AUTO_CAST(XMALLOC(ECC_BUF_SIZE)); skey = AUTO_CAST(XMALLOC(MAXBLOCKSIZE)); if ((pub_expt == NULL) || (ecc_shared == NULL) || (skey == NULL)) { if (pub_expt != NULL) { XFREE(pub_expt); } if (ecc_shared != NULL) { XFREE(ecc_shared); } if (skey != NULL) { XFREE(skey); } return CRYPT_MEM; } LTC_SET_ASN1(decode, 1, LTC_ASN1_OCTET_STRING, pub_expt, ECC_BUF_SIZE); LTC_SET_ASN1(decode, 2, LTC_ASN1_OCTET_STRING, skey, MAXBLOCKSIZE); /* read the structure in now */ if ((err = der_decode_sequence(in, inlen, decode, 3)) != CRYPT_OK) { goto LBL_ERR; } /* import ECC key from packet */ if ((err = ecc_import(AUTO_CAST(decode[1].data), decode[1].size, &pubkey)) != CRYPT_OK) { goto LBL_ERR; } /* make shared key */ x = ECC_BUF_SIZE; if ((err = ecc_shared_secret(key, &pubkey, ecc_shared, &x)) != CRYPT_OK) { ecc_free(&pubkey); goto LBL_ERR; } ecc_free(&pubkey); y = MIN(ECC_BUF_SIZE, MAXBLOCKSIZE); if ((err = hash_memory(hash, ecc_shared, x, ecc_shared, &y)) != CRYPT_OK) { goto LBL_ERR; } /* ensure the hash of the shared secret is at least as big as the encrypt itself */ if (decode[2].size > y) { err = CRYPT_INVALID_PACKET; goto LBL_ERR; } /* avoid buffer overflow */ if (*outlen < decode[2].size) { *outlen = decode[2].size; err = CRYPT_BUFFER_OVERFLOW; goto LBL_ERR; } /* Decrypt the key */ for (x = 0; x < decode[2].size; x++) { out[x] = skey[x] ^ ecc_shared[x]; } *outlen = x; err = CRYPT_OK; LBL_ERR: #ifdef LTC_CLEAN_STACK zeromem(pub_expt, ECC_BUF_SIZE); zeromem(ecc_shared, ECC_BUF_SIZE); zeromem(skey, MAXBLOCKSIZE); #endif XFREE(pub_expt); XFREE(ecc_shared); XFREE(skey); return err; } #endif /* $Source: /cvs/libtom/libtomcrypt/src/pk/ecc/ecc_decrypt_key.c,v $ */ /* $Revision: 1.7 $ */ /* $Date: 2007/05/12 14:32:35 $ */ /* LibTomCrypt, modular cryptographic library -- Tom St Denis * * LibTomCrypt is a library that provides various cryptographic * algorithms in a highly modular and flexible manner. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tomstdenis@gmail.com, http://libtom.org */ /* Implements ECC over Z/pZ for curve y^2 = x^3 - 3x + b * * All curves taken from NIST recommendation paper of July 1999 * Available at http://csrc.nist.gov/cryptval/dss.htm */ /** @file ecc_encrypt_key.c ECC Crypto, Tom St Denis */ #ifdef LTC_MECC /** Encrypt a symmetric key with ECC @param in The symmetric key you want to encrypt @param inlen The length of the key to encrypt (octets) @param out [out] The destination for the ciphertext @param outlen [in/out] The max size and resulting size of the ciphertext @param prng An active PRNG state @param wprng The index of the PRNG you wish to use @param hash The index of the hash you want to use @param key The ECC key you want to encrypt to @return CRYPT_OK if successful */ int ecc_encrypt_key(const unsigned char *in, unsigned long inlen, unsigned char *out, unsigned long *outlen, prng_state *prng, int wprng, int hash, ecc_key *key) { unsigned char *pub_expt, *ecc_shared, *skey; ecc_key pubkey; unsigned long x, y, pubkeysize; int err; LTC_ARGCHK(in != NULL); LTC_ARGCHK(out != NULL); LTC_ARGCHK(outlen != NULL); LTC_ARGCHK(key != NULL); /* check that wprng/cipher/hash are not invalid */ if ((err = prng_is_valid(wprng)) != CRYPT_OK) { return err; } if ((err = hash_is_valid(hash)) != CRYPT_OK) { return err; } if (inlen > hash_descriptor[hash].hashsize) { return CRYPT_INVALID_HASH; } /* make a random key and export the public copy */ if ((err = ecc_make_key_ex(prng, wprng, &pubkey, key->dp)) != CRYPT_OK) { return err; } pub_expt = AUTO_CAST(XMALLOC(ECC_BUF_SIZE)); ecc_shared = AUTO_CAST(XMALLOC(ECC_BUF_SIZE)); skey = AUTO_CAST(XMALLOC(MAXBLOCKSIZE)); if ((pub_expt == NULL) || (ecc_shared == NULL) || (skey == NULL)) { if (pub_expt != NULL) { XFREE(pub_expt); } if (ecc_shared != NULL) { XFREE(ecc_shared); } if (skey != NULL) { XFREE(skey); } ecc_free(&pubkey); return CRYPT_MEM; } pubkeysize = ECC_BUF_SIZE; if ((err = ecc_export(pub_expt, &pubkeysize, PK_PUBLIC, &pubkey)) != CRYPT_OK) { ecc_free(&pubkey); goto LBL_ERR; } /* make random key */ x = ECC_BUF_SIZE; if ((err = ecc_shared_secret(&pubkey, key, ecc_shared, &x)) != CRYPT_OK) { ecc_free(&pubkey); goto LBL_ERR; } ecc_free(&pubkey); y = MAXBLOCKSIZE; if ((err = hash_memory(hash, ecc_shared, x, skey, &y)) != CRYPT_OK) { goto LBL_ERR; } /* Encrypt key */ for (x = 0; x < inlen; x++) { skey[x] ^= in[x]; } err = der_encode_sequence_multi(out, outlen, LTC_ASN1_OBJECT_IDENTIFIER, hash_descriptor[hash].OIDlen, hash_descriptor[hash].OID, LTC_ASN1_OCTET_STRING, pubkeysize, pub_expt, LTC_ASN1_OCTET_STRING, inlen, skey, LTC_ASN1_EOL, 0UL, NULL); LBL_ERR: #ifdef LTC_CLEAN_STACK /* clean up */ zeromem(pub_expt, ECC_BUF_SIZE); zeromem(ecc_shared, ECC_BUF_SIZE); zeromem(skey, MAXBLOCKSIZE); #endif XFREE(skey); XFREE(ecc_shared); XFREE(pub_expt); return err; } #endif /* $Source: /cvs/libtom/libtomcrypt/src/pk/ecc/ecc_encrypt_key.c,v $ */ /* $Revision: 1.6 $ */ /* $Date: 2007/05/12 14:32:35 $ */ /* LibTomCrypt, modular cryptographic library -- Tom St Denis * * LibTomCrypt is a library that provides various cryptographic * algorithms in a highly modular and flexible manner. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tomstdenis@gmail.com, http://libtom.org */ /* Implements ECC over Z/pZ for curve y^2 = x^3 - 3x + b * * All curves taken from NIST recommendation paper of July 1999 * Available at http://csrc.nist.gov/cryptval/dss.htm */ /** @file ecc_export.c ECC Crypto, Tom St Denis */ #ifdef LTC_MECC /** Export an ECC key as a binary packet @param out [out] Destination for the key @param outlen [in/out] Max size and resulting size of the exported key @param type The type of key you want to export (PK_PRIVATE or PK_PUBLIC) @param key The key to export @return CRYPT_OK if successful */ int ecc_export(unsigned char *out, unsigned long *outlen, int type, ecc_key *key) { int err; unsigned char flags[1]; unsigned long key_size; LTC_ARGCHK(out != NULL); LTC_ARGCHK(outlen != NULL); LTC_ARGCHK(key != NULL); /* type valid? */ if ((key->type != PK_PRIVATE) && (type == PK_PRIVATE)) { return CRYPT_PK_TYPE_MISMATCH; } if (ltc_ecc_is_valid_idx(key->idx) == 0) { return CRYPT_INVALID_ARG; } /* we store the NIST byte size */ key_size = key->dp->size; if (type == PK_PRIVATE) { flags[0] = 1; err = der_encode_sequence_multi(out, outlen, LTC_ASN1_BIT_STRING, 1UL, flags, LTC_ASN1_SHORT_INTEGER, 1UL, &key_size, LTC_ASN1_INTEGER, 1UL, key->pubkey.x, LTC_ASN1_INTEGER, 1UL, key->pubkey.y, LTC_ASN1_INTEGER, 1UL, key->k, LTC_ASN1_EOL, 0UL, NULL); } else { flags[0] = 0; err = der_encode_sequence_multi(out, outlen, LTC_ASN1_BIT_STRING, 1UL, flags, LTC_ASN1_SHORT_INTEGER, 1UL, &key_size, LTC_ASN1_INTEGER, 1UL, key->pubkey.x, LTC_ASN1_INTEGER, 1UL, key->pubkey.y, LTC_ASN1_EOL, 0UL, NULL); } return err; } #endif /* $Source: /cvs/libtom/libtomcrypt/src/pk/ecc/ecc_export.c,v $ */ /* $Revision: 1.6 $ */ /* $Date: 2007/05/12 14:32:35 $ */ /* LibTomCrypt, modular cryptographic library -- Tom St Denis * * LibTomCrypt is a library that provides various cryptographic * algorithms in a highly modular and flexible manner. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tomstdenis@gmail.com, http://libtom.org */ /* Implements ECC over Z/pZ for curve y^2 = x^3 - 3x + b * * All curves taken from NIST recommendation paper of July 1999 * Available at http://csrc.nist.gov/cryptval/dss.htm */ /** @file ecc_free.c ECC Crypto, Tom St Denis */ #ifdef LTC_MECC /** Free an ECC key from memory @param key The key you wish to free */ void ecc_free(ecc_key *key) { LTC_ARGCHKVD(key != NULL); mp_clear_multi(key->pubkey.x, key->pubkey.y, key->pubkey.z, key->k, NULL); } #endif /* $Source: /cvs/libtom/libtomcrypt/src/pk/ecc/ecc_free.c,v $ */ /* $Revision: 1.6 $ */ /* $Date: 2007/05/12 14:32:35 $ */ /* LibTomCrypt, modular cryptographic library -- Tom St Denis * * LibTomCrypt is a library that provides various cryptographic * algorithms in a highly modular and flexible manner. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tomstdenis@gmail.com, http://libtom.org */ /* Implements ECC over Z/pZ for curve y^2 = x^3 - 3x + b * * All curves taken from NIST recommendation paper of July 1999 * Available at http://csrc.nist.gov/cryptval/dss.htm */ /** @file ecc_get_size.c ECC Crypto, Tom St Denis */ #ifdef LTC_MECC /** Get the size of an ECC key @param key The key to get the size of @return The size (octets) of the key or INT_MAX on error */ int ecc_get_size(ecc_key *key) { LTC_ARGCHK(key != NULL); if (ltc_ecc_is_valid_idx(key->idx)) return key->dp->size; else return INT_MAX; /* large value known to cause it to fail when passed to ecc_make_key() */ } #endif /* $Source: /cvs/libtom/libtomcrypt/src/pk/ecc/ecc_get_size.c,v $ */ /* $Revision: 1.6 $ */ /* $Date: 2007/05/12 14:32:35 $ */ /* LibTomCrypt, modular cryptographic library -- Tom St Denis * * LibTomCrypt is a library that provides various cryptographic * algorithms in a highly modular and flexible manner. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tomstdenis@gmail.com, http://libtom.org */ /* Implements ECC over Z/pZ for curve y^2 = x^3 - 3x + b * * All curves taken from NIST recommendation paper of July 1999 * Available at http://csrc.nist.gov/cryptval/dss.htm */ /** @file ecc_import.c ECC Crypto, Tom St Denis */ #ifdef LTC_MECC static int is_point(ecc_key *key) { void *prime, *b, *t1, *t2; int err; if ((err = mp_init_multi(&prime, &b, &t1, &t2, NULL)) != CRYPT_OK) { return err; } /* load prime and b */ if ((err = mp_read_radix(prime, key->dp->prime, 16)) != CRYPT_OK) { goto error; } if ((err = mp_read_radix(b, key->dp->B, 16)) != CRYPT_OK) { goto error; } /* compute y^2 */ if ((err = mp_sqr(key->pubkey.y, t1)) != CRYPT_OK) { goto error; } /* compute x^3 */ if ((err = mp_sqr(key->pubkey.x, t2)) != CRYPT_OK) { goto error; } if ((err = mp_mod(t2, prime, t2)) != CRYPT_OK) { goto error; } if ((err = mp_mul(key->pubkey.x, t2, t2)) != CRYPT_OK) { goto error; } /* compute y^2 - x^3 */ if ((err = mp_sub(t1, t2, t1)) != CRYPT_OK) { goto error; } /* compute y^2 - x^3 + 3x */ if ((err = mp_add(t1, key->pubkey.x, t1)) != CRYPT_OK) { goto error; } if ((err = mp_add(t1, key->pubkey.x, t1)) != CRYPT_OK) { goto error; } if ((err = mp_add(t1, key->pubkey.x, t1)) != CRYPT_OK) { goto error; } if ((err = mp_mod(t1, prime, t1)) != CRYPT_OK) { goto error; } while (mp_cmp_d(t1, 0) == LTC_MP_LT) { if ((err = mp_add(t1, prime, t1)) != CRYPT_OK) { goto error; } } while (mp_cmp(t1, prime) != LTC_MP_LT) { if ((err = mp_sub(t1, prime, t1)) != CRYPT_OK) { goto error; } } /* compare to b */ if (mp_cmp(t1, b) != LTC_MP_EQ) { err = CRYPT_INVALID_PACKET; } else { err = CRYPT_OK; } error: mp_clear_multi(prime, b, t1, t2, NULL); return err; } /** Import an ECC key from a binary packet @param in The packet to import @param inlen The length of the packet @param key [out] The destination of the import @return CRYPT_OK if successful, upon error all allocated memory will be freed */ int ecc_import(const unsigned char *in, unsigned long inlen, ecc_key *key) { return ecc_import_ex(in, inlen, key, NULL); } /** Import an ECC key from a binary packet, using user supplied domain params rather than one of the NIST ones @param in The packet to import @param inlen The length of the packet @param key [out] The destination of the import @param dp pointer to user supplied params; must be the same as the params used when exporting @return CRYPT_OK if successful, upon error all allocated memory will be freed */ int ecc_import_ex(const unsigned char *in, unsigned long inlen, ecc_key *key, const ltc_ecc_set_type *dp) { unsigned long key_size; unsigned char flags[1]; int err; LTC_ARGCHK(in != NULL); LTC_ARGCHK(key != NULL); LTC_ARGCHK(ltc_mp.name != NULL); /* init key */ if (mp_init_multi(&key->pubkey.x, &key->pubkey.y, &key->pubkey.z, &key->k, NULL) != CRYPT_OK) { return CRYPT_MEM; } /* find out what type of key it is */ if ((err = der_decode_sequence_multi(in, inlen, LTC_ASN1_BIT_STRING, 1UL, &flags, LTC_ASN1_EOL, 0UL, NULL)) != CRYPT_OK) { goto done; } if (flags[0] == 1) { /* private key */ key->type = PK_PRIVATE; if ((err = der_decode_sequence_multi(in, inlen, LTC_ASN1_BIT_STRING, 1UL, flags, LTC_ASN1_SHORT_INTEGER, 1UL, &key_size, LTC_ASN1_INTEGER, 1UL, key->pubkey.x, LTC_ASN1_INTEGER, 1UL, key->pubkey.y, LTC_ASN1_INTEGER, 1UL, key->k, LTC_ASN1_EOL, 0UL, NULL)) != CRYPT_OK) { goto done; } } else { /* public key */ key->type = PK_PUBLIC; if ((err = der_decode_sequence_multi(in, inlen, LTC_ASN1_BIT_STRING, 1UL, flags, LTC_ASN1_SHORT_INTEGER, 1UL, &key_size, LTC_ASN1_INTEGER, 1UL, key->pubkey.x, LTC_ASN1_INTEGER, 1UL, key->pubkey.y, LTC_ASN1_EOL, 0UL, NULL)) != CRYPT_OK) { goto done; } } if (dp == NULL) { /* find the idx */ for (key->idx = 0; ltc_ecc_sets[key->idx].size && (unsigned long)ltc_ecc_sets[key->idx].size != key_size; ++key->idx); if (ltc_ecc_sets[key->idx].size == 0) { err = CRYPT_INVALID_PACKET; goto done; } key->dp = <c_ecc_sets[key->idx]; } else { key->idx = -1; key->dp = dp; } /* set z */ if ((err = mp_set(key->pubkey.z, 1)) != CRYPT_OK) { goto done; } /* is it a point on the curve? */ if ((err = is_point(key)) != CRYPT_OK) { goto done; } /* we're good */ return CRYPT_OK; done: mp_clear_multi(key->pubkey.x, key->pubkey.y, key->pubkey.z, key->k, NULL); return err; } #endif /* $Source: /cvs/libtom/libtomcrypt/src/pk/ecc/ecc_import.c,v $ */ /* $Revision: 1.13 $ */ /* $Date: 2007/05/12 14:32:35 $ */ /* LibTomCrypt, modular cryptographic library -- Tom St Denis * * LibTomCrypt is a library that provides various cryptographic * algorithms in a highly modular and flexible manner. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tomstdenis@gmail.com, http://libtom.org */ /* Implements ECC over Z/pZ for curve y^2 = x^3 - 3x + b * * All curves taken from NIST recommendation paper of July 1999 * Available at http://csrc.nist.gov/cryptval/dss.htm */ /** @file ecc_make_key.c ECC Crypto, Tom St Denis */ #ifdef LTC_MECC /** Make a new ECC key @param prng An active PRNG state @param wprng The index of the PRNG you wish to use @param keysize The keysize for the new key (in octets from 20 to 65 bytes) @param key [out] Destination of the newly created key @return CRYPT_OK if successful, upon error all allocated memory will be freed */ int ecc_make_key(prng_state *prng, int wprng, int keysize, ecc_key *key) { int x, err; /* find key size */ for (x = 0; (keysize > ltc_ecc_sets[x].size) && (ltc_ecc_sets[x].size != 0); x++); keysize = ltc_ecc_sets[x].size; if ((keysize > ECC_MAXSIZE) || (ltc_ecc_sets[x].size == 0)) { return CRYPT_INVALID_KEYSIZE; } err = ecc_make_key_ex(prng, wprng, key, <c_ecc_sets[x]); key->idx = x; return err; } int ecc_make_key_ex(prng_state *prng, int wprng, ecc_key *key, const ltc_ecc_set_type *dp) { int err; ecc_point *base; void *prime, *order; unsigned char *buf; int keysize; LTC_ARGCHK(key != NULL); LTC_ARGCHK(ltc_mp.name != NULL); LTC_ARGCHK(dp != NULL); /* good prng? */ if ((err = prng_is_valid(wprng)) != CRYPT_OK) { return err; } key->idx = -1; key->dp = dp; keysize = dp->size; /* allocate ram */ base = NULL; buf = AUTO_CAST(XMALLOC(ECC_MAXSIZE)); if (buf == NULL) { return CRYPT_MEM; } /* make up random string */ if (prng_descriptor[wprng].read(buf, (unsigned long)keysize, prng) != (unsigned long)keysize) { err = CRYPT_ERROR_READPRNG; goto ERR_BUF; } /* setup the key variables */ if ((err = mp_init_multi(&key->pubkey.x, &key->pubkey.y, &key->pubkey.z, &key->k, &prime, &order, NULL)) != CRYPT_OK) { goto ERR_BUF; } base = ltc_ecc_new_point(); if (base == NULL) { err = CRYPT_MEM; goto errkey; } /* read in the specs for this key */ if ((err = mp_read_radix(prime, (char *)key->dp->prime, 16)) != CRYPT_OK) { goto errkey; } if ((err = mp_read_radix(order, (char *)key->dp->order, 16)) != CRYPT_OK) { goto errkey; } if ((err = mp_read_radix(base->x, (char *)key->dp->Gx, 16)) != CRYPT_OK) { goto errkey; } if ((err = mp_read_radix(base->y, (char *)key->dp->Gy, 16)) != CRYPT_OK) { goto errkey; } if ((err = mp_set(base->z, 1)) != CRYPT_OK) { goto errkey; } if ((err = mp_read_unsigned_bin(key->k, (unsigned char *)buf, keysize)) != CRYPT_OK) { goto errkey; } /* the key should be smaller than the order of base point */ if (mp_cmp(key->k, order) != LTC_MP_LT) { if ((err = mp_mod(key->k, order, key->k)) != CRYPT_OK) { goto errkey; } } /* make the public key */ if ((err = ltc_mp.ecc_ptmul(key->k, base, &key->pubkey, prime, 1)) != CRYPT_OK) { goto errkey; } key->type = PK_PRIVATE; /* free up ram */ err = CRYPT_OK; goto cleanup; errkey: mp_clear_multi(key->pubkey.x, key->pubkey.y, key->pubkey.z, key->k, NULL); cleanup: ltc_ecc_del_point(base); mp_clear_multi(prime, order, NULL); ERR_BUF: #ifdef LTC_CLEAN_STACK zeromem(buf, ECC_MAXSIZE); #endif XFREE(buf); return err; } #endif /* $Source: /cvs/libtom/libtomcrypt/src/pk/ecc/ecc_make_key.c,v $ */ /* $Revision: 1.13 $ */ /* $Date: 2007/05/12 14:32:35 $ */ /* LibTomCrypt, modular cryptographic library -- Tom St Denis * * LibTomCrypt is a library that provides various cryptographic * algorithms in a highly modular and flexible manner. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tomstdenis@gmail.com, http://libtom.org */ /* Implements ECC over Z/pZ for curve y^2 = x^3 - 3x + b * * All curves taken from NIST recommendation paper of July 1999 * Available at http://csrc.nist.gov/cryptval/dss.htm */ /** @file ecc_shared_secret.c ECC Crypto, Tom St Denis */ #ifdef LTC_MECC /** Create an ECC shared secret between two keys @param private_key The private ECC key @param public_key The public key @param out [out] Destination of the shared secret (Conforms to EC-DH from ANSI X9.63) @param outlen [in/out] The max size and resulting size of the shared secret @return CRYPT_OK if successful */ int ecc_shared_secret(ecc_key *private_key, ecc_key *public_key, unsigned char *out, unsigned long *outlen) { unsigned long x; ecc_point *result; void *prime; int err; LTC_ARGCHK(private_key != NULL); LTC_ARGCHK(public_key != NULL); LTC_ARGCHK(out != NULL); LTC_ARGCHK(outlen != NULL); /* type valid? */ if (private_key->type != PK_PRIVATE) { return CRYPT_PK_NOT_PRIVATE; } if ((ltc_ecc_is_valid_idx(private_key->idx) == 0) || (ltc_ecc_is_valid_idx(public_key->idx) == 0)) { return CRYPT_INVALID_ARG; } if (XSTRCMP(private_key->dp->name, public_key->dp->name) != 0) { return CRYPT_PK_TYPE_MISMATCH; } /* make new point */ result = ltc_ecc_new_point(); if (result == NULL) { return CRYPT_MEM; } if ((err = mp_init(&prime)) != CRYPT_OK) { ltc_ecc_del_point(result); return err; } if ((err = mp_read_radix(prime, (char *)private_key->dp->prime, 16)) != CRYPT_OK) { goto done; } if ((err = ltc_mp.ecc_ptmul(private_key->k, &public_key->pubkey, result, prime, 1)) != CRYPT_OK) { goto done; } x = (unsigned long)mp_unsigned_bin_size(prime); if (*outlen < x) { *outlen = x; err = CRYPT_BUFFER_OVERFLOW; goto done; } zeromem(out, x); if ((err = mp_to_unsigned_bin(result->x, out + (x - mp_unsigned_bin_size(result->x)))) != CRYPT_OK) { goto done; } err = CRYPT_OK; *outlen = x; done: mp_clear(prime); ltc_ecc_del_point(result); return err; } #endif /* $Source: /cvs/libtom/libtomcrypt/src/pk/ecc/ecc_shared_secret.c,v $ */ /* $Revision: 1.10 $ */ /* $Date: 2007/05/12 14:32:35 $ */ /* LibTomCrypt, modular cryptographic library -- Tom St Denis * * LibTomCrypt is a library that provides various cryptographic * algorithms in a highly modular and flexible manner. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tomstdenis@gmail.com, http://libtom.org */ /* Implements ECC over Z/pZ for curve y^2 = x^3 - 3x + b * * All curves taken from NIST recommendation paper of July 1999 * Available at http://csrc.nist.gov/cryptval/dss.htm */ /** @file ecc_sign_hash.c ECC Crypto, Tom St Denis */ #ifdef LTC_MECC /** Sign a message digest @param in The message digest to sign @param inlen The length of the digest @param out [out] The destination for the signature @param outlen [in/out] The max size and resulting size of the signature @param prng An active PRNG state @param wprng The index of the PRNG you wish to use @param key A private ECC key @return CRYPT_OK if successful */ int ecc_sign_hash(const unsigned char *in, unsigned long inlen, unsigned char *out, unsigned long *outlen, prng_state *prng, int wprng, ecc_key *key) { ecc_key pubkey; void *r, *s, *e, *p; int err; LTC_ARGCHK(in != NULL); LTC_ARGCHK(out != NULL); LTC_ARGCHK(outlen != NULL); LTC_ARGCHK(key != NULL); /* is this a private key? */ if (key->type != PK_PRIVATE) { return CRYPT_PK_NOT_PRIVATE; } /* is the IDX valid ? */ if (ltc_ecc_is_valid_idx(key->idx) != 1) { return CRYPT_PK_INVALID_TYPE; } if ((err = prng_is_valid(wprng)) != CRYPT_OK) { return err; } /* get the hash and load it as a bignum into 'e' */ /* init the bignums */ if ((err = mp_init_multi(&r, &s, &p, &e, NULL)) != CRYPT_OK) { return err; } if ((err = mp_read_radix(p, (char *)key->dp->order, 16)) != CRYPT_OK) { goto errnokey; } if ((err = mp_read_unsigned_bin(e, (unsigned char *)in, (int)inlen)) != CRYPT_OK) { goto errnokey; } /* make up a key and export the public copy */ for ( ; ; ) { if ((err = ecc_make_key_ex(prng, wprng, &pubkey, key->dp)) != CRYPT_OK) { goto errnokey; } /* find r = x1 mod n */ if ((err = mp_mod(pubkey.pubkey.x, p, r)) != CRYPT_OK) { goto error; } if (mp_iszero(r) == LTC_MP_YES) { ecc_free(&pubkey); } else { /* find s = (e + xr)/k */ if ((err = mp_invmod(pubkey.k, p, pubkey.k)) != CRYPT_OK) { goto error; } /* k = 1/k */ if ((err = mp_mulmod(key->k, r, p, s)) != CRYPT_OK) { goto error; } /* s = xr */ if ((err = mp_add(e, s, s)) != CRYPT_OK) { goto error; } /* s = e + xr */ if ((err = mp_mod(s, p, s)) != CRYPT_OK) { goto error; } /* s = e + xr */ if ((err = mp_mulmod(s, pubkey.k, p, s)) != CRYPT_OK) { goto error; } /* s = (e + xr)/k */ ecc_free(&pubkey); if (mp_iszero(s) == LTC_MP_NO) { break; } } } /* store as SEQUENCE { r, s -- integer } */ err = der_encode_sequence_multi(out, outlen, LTC_ASN1_INTEGER, 1UL, r, LTC_ASN1_INTEGER, 1UL, s, LTC_ASN1_EOL, 0UL, NULL); goto errnokey; error: ecc_free(&pubkey); errnokey: mp_clear_multi(r, s, p, e, NULL); return err; } #endif /* $Source: /cvs/libtom/libtomcrypt/src/pk/ecc/ecc_sign_hash.c,v $ */ /* $Revision: 1.11 $ */ /* $Date: 2007/05/12 14:32:35 $ */ /* LibTomCrypt, modular cryptographic library -- Tom St Denis * * LibTomCrypt is a library that provides various cryptographic * algorithms in a highly modular and flexible manner. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tomstdenis@gmail.com, http://libtom.org */ /* Implements ECC over Z/pZ for curve y^2 = x^3 - 3x + b * * All curves taken from NIST recommendation paper of July 1999 * Available at http://csrc.nist.gov/cryptval/dss.htm */ /** @file ecc_sizes.c ECC Crypto, Tom St Denis */ #ifdef LTC_MECC void ecc_sizes(int *low, int *high) { int i; LTC_ARGCHKVD(low != NULL); LTC_ARGCHKVD(high != NULL); *low = INT_MAX; *high = 0; for (i = 0; ltc_ecc_sets[i].size != 0; i++) { if (ltc_ecc_sets[i].size < *low) { *low = ltc_ecc_sets[i].size; } if (ltc_ecc_sets[i].size > *high) { *high = ltc_ecc_sets[i].size; } } } #endif /* $Source: /cvs/libtom/libtomcrypt/src/pk/ecc/ecc_sizes.c,v $ */ /* $Revision: 1.6 $ */ /* $Date: 2007/05/12 14:32:35 $ */ /* LibTomCrypt, modular cryptographic library -- Tom St Denis * * LibTomCrypt is a library that provides various cryptographic * algorithms in a highly modular and flexible manner. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tomstdenis@gmail.com, http://libtom.org */ /* Implements ECC over Z/pZ for curve y^2 = x^3 - 3x + b * * All curves taken from NIST recommendation paper of July 1999 * Available at http://csrc.nist.gov/cryptval/dss.htm */ /** @file ecc_test.c ECC Crypto, Tom St Denis */ #ifdef LTC_MECC /** Perform on the ECC system @return CRYPT_OK if successful */ int ecc_test(void) { void *modulus, *order; ecc_point *G, *GG; int i, err, primality; if ((err = mp_init_multi(&modulus, &order, NULL)) != CRYPT_OK) { return err; } G = ltc_ecc_new_point(); GG = ltc_ecc_new_point(); if ((G == NULL) || (GG == NULL)) { mp_clear_multi(modulus, order, NULL); ltc_ecc_del_point(G); ltc_ecc_del_point(GG); return CRYPT_MEM; } for (i = 0; ltc_ecc_sets[i].size; i++) { #if 0 printf("Testing %d\n", ltc_ecc_sets[i].size); #endif if ((err = mp_read_radix(modulus, (char *)ltc_ecc_sets[i].prime, 16)) != CRYPT_OK) { goto done; } if ((err = mp_read_radix(order, (char *)ltc_ecc_sets[i].order, 16)) != CRYPT_OK) { goto done; } /* is prime actually prime? */ if ((err = mp_prime_is_prime(modulus, 8, &primality)) != CRYPT_OK) { goto done; } if (primality == 0) { err = CRYPT_FAIL_TESTVECTOR; goto done; } /* is order prime ? */ if ((err = mp_prime_is_prime(order, 8, &primality)) != CRYPT_OK) { goto done; } if (primality == 0) { err = CRYPT_FAIL_TESTVECTOR; goto done; } if ((err = mp_read_radix(G->x, (char *)ltc_ecc_sets[i].Gx, 16)) != CRYPT_OK) { goto done; } if ((err = mp_read_radix(G->y, (char *)ltc_ecc_sets[i].Gy, 16)) != CRYPT_OK) { goto done; } mp_set(G->z, 1); /* then we should have G == (order + 1)G */ if ((err = mp_add_d(order, 1, order)) != CRYPT_OK) { goto done; } if ((err = ltc_mp.ecc_ptmul(order, G, GG, modulus, 1)) != CRYPT_OK) { goto done; } if ((mp_cmp(G->x, GG->x) != LTC_MP_EQ) || (mp_cmp(G->y, GG->y) != LTC_MP_EQ)) { err = CRYPT_FAIL_TESTVECTOR; goto done; } } err = CRYPT_OK; done: ltc_ecc_del_point(GG); ltc_ecc_del_point(G); mp_clear_multi(order, modulus, NULL); return err; } #endif /* $Source: /cvs/libtom/libtomcrypt/src/pk/ecc/ecc_test.c,v $ */ /* $Revision: 1.12 $ */ /* $Date: 2007/05/12 14:32:35 $ */ /* LibTomCrypt, modular cryptographic library -- Tom St Denis * * LibTomCrypt is a library that provides various cryptographic * algorithms in a highly modular and flexible manner. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tomstdenis@gmail.com, http://libtom.org */ /* Implements ECC over Z/pZ for curve y^2 = x^3 - 3x + b * * All curves taken from NIST recommendation paper of July 1999 * Available at http://csrc.nist.gov/cryptval/dss.htm */ /** @file ecc_verify_hash.c ECC Crypto, Tom St Denis */ #ifdef LTC_MECC /* verify * * w = s^-1 mod n * u1 = xw * u2 = rw * X = u1*G + u2*Q * v = X_x1 mod n * accept if v == r */ /** Verify an ECC signature @param sig The signature to verify @param siglen The length of the signature (octets) @param hash The hash (message digest) that was signed @param hashlen The length of the hash (octets) @param stat Result of signature, 1==valid, 0==invalid @param key The corresponding public ECC key @return CRYPT_OK if successful (even if the signature is not valid) */ int ecc_verify_hash(const unsigned char *sig, unsigned long siglen, const unsigned char *hash, unsigned long hashlen, int *stat, ecc_key *key) { ecc_point *mG, *mQ; void *r, *s, *v, *w, *u1, *u2, *e, *p, *m; void *mp; int err; LTC_ARGCHK(sig != NULL); LTC_ARGCHK(hash != NULL); LTC_ARGCHK(stat != NULL); LTC_ARGCHK(key != NULL); /* default to invalid signature */ *stat = 0; mp = NULL; /* is the IDX valid ? */ if (ltc_ecc_is_valid_idx(key->idx) != 1) { return CRYPT_PK_INVALID_TYPE; } /* allocate ints */ if ((err = mp_init_multi(&r, &s, &v, &w, &u1, &u2, &p, &e, &m, NULL)) != CRYPT_OK) { return CRYPT_MEM; } /* allocate points */ mG = ltc_ecc_new_point(); mQ = ltc_ecc_new_point(); if ((mQ == NULL) || (mG == NULL)) { err = CRYPT_MEM; goto error; } /* parse header */ if ((err = der_decode_sequence_multi(sig, siglen, LTC_ASN1_INTEGER, 1UL, r, LTC_ASN1_INTEGER, 1UL, s, LTC_ASN1_EOL, 0UL, NULL)) != CRYPT_OK) { goto error; } /* get the order */ if ((err = mp_read_radix(p, (char *)key->dp->order, 16)) != CRYPT_OK) { goto error; } /* get the modulus */ if ((err = mp_read_radix(m, (char *)key->dp->prime, 16)) != CRYPT_OK) { goto error; } /* check for zero */ if (mp_iszero(r) || mp_iszero(s) || (mp_cmp(r, p) != LTC_MP_LT) || (mp_cmp(s, p) != LTC_MP_LT)) { err = CRYPT_INVALID_PACKET; goto error; } /* read hash */ if ((err = mp_read_unsigned_bin(e, (unsigned char *)hash, (int)hashlen)) != CRYPT_OK) { goto error; } /* w = s^-1 mod n */ if ((err = mp_invmod(s, p, w)) != CRYPT_OK) { goto error; } /* u1 = ew */ if ((err = mp_mulmod(e, w, p, u1)) != CRYPT_OK) { goto error; } /* u2 = rw */ if ((err = mp_mulmod(r, w, p, u2)) != CRYPT_OK) { goto error; } /* find mG and mQ */ if ((err = mp_read_radix(mG->x, (char *)key->dp->Gx, 16)) != CRYPT_OK) { goto error; } if ((err = mp_read_radix(mG->y, (char *)key->dp->Gy, 16)) != CRYPT_OK) { goto error; } if ((err = mp_set(mG->z, 1)) != CRYPT_OK) { goto error; } if ((err = mp_copy(key->pubkey.x, mQ->x)) != CRYPT_OK) { goto error; } if ((err = mp_copy(key->pubkey.y, mQ->y)) != CRYPT_OK) { goto error; } if ((err = mp_copy(key->pubkey.z, mQ->z)) != CRYPT_OK) { goto error; } /* compute u1*mG + u2*mQ = mG */ if (ltc_mp.ecc_mul2add == NULL) { if ((err = ltc_mp.ecc_ptmul(u1, mG, mG, m, 0)) != CRYPT_OK) { goto error; } if ((err = ltc_mp.ecc_ptmul(u2, mQ, mQ, m, 0)) != CRYPT_OK) { goto error; } /* find the montgomery mp */ if ((err = mp_montgomery_setup(m, &mp)) != CRYPT_OK) { goto error; } /* add them */ if ((err = ltc_mp.ecc_ptadd(mQ, mG, mG, m, mp)) != CRYPT_OK) { goto error; } /* reduce */ if ((err = ltc_mp.ecc_map(mG, m, mp)) != CRYPT_OK) { goto error; } } else { /* use Shamir's trick to compute u1*mG + u2*mQ using half of the doubles */ if ((err = ltc_mp.ecc_mul2add(mG, u1, mQ, u2, mG, m)) != CRYPT_OK) { goto error; } } /* v = X_x1 mod n */ if ((err = mp_mod(mG->x, p, v)) != CRYPT_OK) { goto error; } /* does v == r */ if (mp_cmp(v, r) == LTC_MP_EQ) { *stat = 1; } /* clear up and return */ err = CRYPT_OK; error: ltc_ecc_del_point(mG); ltc_ecc_del_point(mQ); mp_clear_multi(r, s, v, w, u1, u2, p, e, m, NULL); if (mp != NULL) { mp_montgomery_free(mp); } return err; } #endif /* $Source: /cvs/libtom/libtomcrypt/src/pk/ecc/ecc_verify_hash.c,v $ */ /* $Revision: 1.14 $ */ /* $Date: 2007/05/12 14:32:35 $ */ /* LibTomCrypt, modular cryptographic library -- Tom St Denis * * LibTomCrypt is a library that provides various cryptographic * algorithms in a highly modular and flexible manner. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tomstdenis@gmail.com, http://libtom.org */ /** @file error_to_string.c Convert error codes to ASCII strings, Tom St Denis */ static const char * const err_2_str[] = { "CRYPT_OK", "CRYPT_ERROR", "Non-fatal 'no-operation' requested.", "Invalid keysize for block cipher.", "Invalid number of rounds for block cipher.", "Algorithm failed test vectors.", "Buffer overflow.", "Invalid input packet.", "Invalid number of bits for a PRNG.", "Error reading the PRNG.", "Invalid cipher specified.", "Invalid hash specified.", "Invalid PRNG specified.", "Out of memory.", "Invalid PK key or key type specified for function.", "A private PK key is required.", "Invalid argument provided.", "File Not Found", "Invalid PK type.", "Invalid PK system.", "Duplicate PK key found on keyring.", "Key not found in keyring.", "Invalid sized parameter.", "Invalid size for prime.", }; /** Convert an LTC error code to ASCII @param err The error code @return A pointer to the ASCII NUL terminated string for the error or "Invalid error code." if the err code was not valid. */ const char *error_to_string(int err) { if ((err < 0) || (err >= (int)(sizeof(err_2_str) / sizeof(err_2_str[0])))) { return "Invalid error code."; } else { return err_2_str[err]; } } /* $Source: /cvs/libtom/libtomcrypt/src/misc/error_to_string.c,v $ */ /* $Revision: 1.5 $ */ /* $Date: 2006/12/28 01:27:24 $ */ /* LibTomCrypt, modular cryptographic library -- Tom St Denis * * LibTomCrypt is a library that provides various cryptographic * algorithms in a highly modular and flexible manner. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tomstdenis@gmail.com, http://libtom.org */ #define DESC_DEF_ONLY /* $Source: /cvs/libtom/libtomcrypt/src/math/gmp_desc.c,v $ */ /* $Revision: 1.16 $ */ /* $Date: 2007/05/12 14:32:35 $ */ /* LibTomCrypt, modular cryptographic library -- Tom St Denis * * LibTomCrypt is a library that provides various cryptographic * algorithms in a highly modular and flexible manner. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tomstdenis@gmail.com, http://libtom.org */ /** @file hash_file.c Hash a file, Tom St Denis */ /** @param hash The index of the hash desired @param fname The name of the file you wish to hash @param out [out] The destination of the digest @param outlen [in/out] The max size and resulting size of the message digest @result CRYPT_OK if successful */ int hash_file(int hash, const char *fname, unsigned char *out, unsigned long *outlen) { #ifdef LTC_NO_FILE return CRYPT_NOP; #else FILE *in; int err; LTC_ARGCHK(fname != NULL); LTC_ARGCHK(out != NULL); LTC_ARGCHK(outlen != NULL); if ((err = hash_is_valid(hash)) != CRYPT_OK) { return err; } in = fopen(fname, "rb"); if (in == NULL) { return CRYPT_FILE_NOTFOUND; } err = hash_filehandle(hash, in, out, outlen); if (fclose(in) != 0) { return CRYPT_ERROR; } return err; #endif } /* $Source: /cvs/libtom/libtomcrypt/src/hashes/helper/hash_file.c,v $ */ /* $Revision: 1.5 $ */ /* $Date: 2006/12/28 01:27:23 $ */ /* LibTomCrypt, modular cryptographic library -- Tom St Denis * * LibTomCrypt is a library that provides various cryptographic * algorithms in a highly modular and flexible manner. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tomstdenis@gmail.com, http://libtom.org */ /** @file hash_filehandle.c Hash open files, Tom St Denis */ /** Hash data from an open file handle. @param hash The index of the hash you want to use @param in The FILE* handle of the file you want to hash @param out [out] The destination of the digest @param outlen [in/out] The max size and resulting size of the digest @result CRYPT_OK if successful */ int hash_filehandle(int hash, FILE *in, unsigned char *out, unsigned long *outlen) { #ifdef LTC_NO_FILE return CRYPT_NOP; #else hash_state md; unsigned char buf[512]; size_t x; int err; LTC_ARGCHK(out != NULL); LTC_ARGCHK(outlen != NULL); LTC_ARGCHK(in != NULL); if ((err = hash_is_valid(hash)) != CRYPT_OK) { return err; } if (*outlen < hash_descriptor[hash].hashsize) { *outlen = hash_descriptor[hash].hashsize; return CRYPT_BUFFER_OVERFLOW; } if ((err = hash_descriptor[hash].init(&md)) != CRYPT_OK) { return err; } *outlen = hash_descriptor[hash].hashsize; do { x = fread(buf, 1, sizeof(buf), in); if ((err = hash_descriptor[hash].process(&md, buf, x)) != CRYPT_OK) { return err; } } while (x == sizeof(buf)); err = hash_descriptor[hash].done(&md, out); #ifdef LTC_CLEAN_STACK zeromem(buf, sizeof(buf)); #endif return err; #endif } /* $Source: /cvs/libtom/libtomcrypt/src/hashes/helper/hash_filehandle.c,v $ */ /* $Revision: 1.6 $ */ /* $Date: 2006/12/28 01:27:23 $ */ /* LibTomCrypt, modular cryptographic library -- Tom St Denis * * LibTomCrypt is a library that provides various cryptographic * algorithms in a highly modular and flexible manner. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tomstdenis@gmail.com, http://libtom.org */ /** @file hash_memory.c Hash memory helper, Tom St Denis */ /** Hash a block of memory and store the digest. @param hash The index of the hash you wish to use @param in The data you wish to hash @param inlen The length of the data to hash (octets) @param out [out] Where to store the digest @param outlen [in/out] Max size and resulting size of the digest @return CRYPT_OK if successful */ int hash_memory(int hash, const unsigned char *in, unsigned long inlen, unsigned char *out, unsigned long *outlen) { hash_state *md; int err; LTC_ARGCHK(in != NULL); LTC_ARGCHK(out != NULL); LTC_ARGCHK(outlen != NULL); if ((err = hash_is_valid(hash)) != CRYPT_OK) { return err; } if (*outlen < hash_descriptor[hash].hashsize) { *outlen = hash_descriptor[hash].hashsize; return CRYPT_BUFFER_OVERFLOW; } md = AUTO_CAST(XMALLOC(sizeof(hash_state))); if (md == NULL) { return CRYPT_MEM; } if ((err = hash_descriptor[hash].init(md)) != CRYPT_OK) { goto LBL_ERR; } if ((err = hash_descriptor[hash].process(md, in, inlen)) != CRYPT_OK) { goto LBL_ERR; } err = hash_descriptor[hash].done(md, out); *outlen = hash_descriptor[hash].hashsize; LBL_ERR: #ifdef LTC_CLEAN_STACK zeromem(md, sizeof(hash_state)); #endif XFREE(md); return err; } /* $Source: /cvs/libtom/libtomcrypt/src/hashes/helper/hash_memory.c,v $ */ /* $Revision: 1.6 $ */ /* $Date: 2006/12/28 01:27:23 $ */ /* LibTomCrypt, modular cryptographic library -- Tom St Denis * * LibTomCrypt is a library that provides various cryptographic * algorithms in a highly modular and flexible manner. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tomstdenis@gmail.com, http://libtom.org */ #include /** @file hash_memory_multi.c Hash (multiple buffers) memory helper, Tom St Denis */ /** Hash multiple (non-adjacent) blocks of memory at once. @param hash The index of the hash you wish to use @param out [out] Where to store the digest @param outlen [in/out] Max size and resulting size of the digest @param in The data you wish to hash @param inlen The length of the data to hash (octets) @param ... tuples of (data,len) pairs to hash, terminated with a (NULL,x) (x=don't care) @return CRYPT_OK if successful */ int hash_memory_multi(int hash, unsigned char *out, unsigned long *outlen, const unsigned char *in, unsigned long inlen, ...) { hash_state *md; int err; va_list args; const unsigned char *curptr; unsigned long curlen; LTC_ARGCHK(in != NULL); LTC_ARGCHK(out != NULL); LTC_ARGCHK(outlen != NULL); if ((err = hash_is_valid(hash)) != CRYPT_OK) { return err; } if (*outlen < hash_descriptor[hash].hashsize) { *outlen = hash_descriptor[hash].hashsize; return CRYPT_BUFFER_OVERFLOW; } md = AUTO_CAST(XMALLOC(sizeof(hash_state))); if (md == NULL) { return CRYPT_MEM; } if ((err = hash_descriptor[hash].init(md)) != CRYPT_OK) { goto LBL_ERR; } va_start(args, inlen); curptr = in; curlen = inlen; for ( ; ; ) { /* process buf */ if ((err = hash_descriptor[hash].process(md, curptr, curlen)) != CRYPT_OK) { goto LBL_ERR; } /* step to next */ curptr = va_arg(args, const unsigned char *); if (curptr == NULL) { break; } curlen = va_arg(args, unsigned long); } err = hash_descriptor[hash].done(md, out); *outlen = hash_descriptor[hash].hashsize; LBL_ERR: #ifdef LTC_CLEAN_STACK zeromem(md, sizeof(hash_state)); #endif XFREE(md); va_end(args); return err; } /* $Source: /cvs/libtom/libtomcrypt/src/hashes/helper/hash_memory_multi.c,v $ */ /* $Revision: 1.6 $ */ /* $Date: 2006/12/28 01:27:23 $ */ /* LibTomCrypt, modular cryptographic library -- Tom St Denis * * LibTomCrypt is a library that provides various cryptographic * algorithms in a highly modular and flexible manner. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tomstdenis@gmail.com, http://libtom.org */ /* Implements ECC over Z/pZ for curve y^2 = x^3 - 3x + b * * All curves taken from NIST recommendation paper of July 1999 * Available at http://csrc.nist.gov/cryptval/dss.htm */ /** @file ltc_ecc_is_valid_idx.c ECC Crypto, Tom St Denis */ #ifdef LTC_MECC /** Returns whether an ECC idx is valid or not @param n The idx number to check @return 1 if valid, 0 if not */ int ltc_ecc_is_valid_idx(int n) { int x; for (x = 0; ltc_ecc_sets[x].size != 0; x++); /* -1 is a valid index --- indicating that the domain params were supplied by the user */ if ((n >= -1) && (n < x)) { return 1; } return 0; } #endif /* $Source: /cvs/libtom/libtomcrypt/src/pk/ecc/ltc_ecc_is_valid_idx.c,v $ */ /* $Revision: 1.7 $ */ /* $Date: 2007/05/12 14:32:35 $ */ /* LibTomCrypt, modular cryptographic library -- Tom St Denis * * LibTomCrypt is a library that provides various cryptographic * algorithms in a highly modular and flexible manner. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tomstdenis@gmail.com, http://libtom.org */ /* Implements ECC over Z/pZ for curve y^2 = x^3 - 3x + b * * All curves taken from NIST recommendation paper of July 1999 * Available at http://csrc.nist.gov/cryptval/dss.htm */ /** @file ltc_ecc_map.c ECC Crypto, Tom St Denis */ #ifdef LTC_MECC /** Map a projective jacbobian point back to affine space @param P [in/out] The point to map @param modulus The modulus of the field the ECC curve is in @param mp The "b" value from montgomery_setup() @return CRYPT_OK on success */ int ltc_ecc_map(ecc_point *P, void *modulus, void *mp) { void *t1, *t2; int err; LTC_ARGCHK(P != NULL); LTC_ARGCHK(modulus != NULL); LTC_ARGCHK(mp != NULL); if ((err = mp_init_multi(&t1, &t2, NULL)) != CRYPT_OK) { return CRYPT_MEM; } /* first map z back to normal */ if ((err = mp_montgomery_reduce(P->z, modulus, mp)) != CRYPT_OK) { goto done; } /* get 1/z */ if ((err = mp_invmod(P->z, modulus, t1)) != CRYPT_OK) { goto done; } /* get 1/z^2 and 1/z^3 */ if ((err = mp_sqr(t1, t2)) != CRYPT_OK) { goto done; } if ((err = mp_mod(t2, modulus, t2)) != CRYPT_OK) { goto done; } if ((err = mp_mul(t1, t2, t1)) != CRYPT_OK) { goto done; } if ((err = mp_mod(t1, modulus, t1)) != CRYPT_OK) { goto done; } /* multiply against x/y */ if ((err = mp_mul(P->x, t2, P->x)) != CRYPT_OK) { goto done; } if ((err = mp_montgomery_reduce(P->x, modulus, mp)) != CRYPT_OK) { goto done; } if ((err = mp_mul(P->y, t1, P->y)) != CRYPT_OK) { goto done; } if ((err = mp_montgomery_reduce(P->y, modulus, mp)) != CRYPT_OK) { goto done; } if ((err = mp_set(P->z, 1)) != CRYPT_OK) { goto done; } err = CRYPT_OK; done: mp_clear_multi(t1, t2, NULL); return err; } #endif /* $Source: /cvs/libtom/libtomcrypt/src/pk/ecc/ltc_ecc_map.c,v $ */ /* $Revision: 1.7 $ */ /* $Date: 2007/05/12 14:32:35 $ */ /* LibTomCrypt, modular cryptographic library -- Tom St Denis * * LibTomCrypt is a library that provides various cryptographic * algorithms in a highly modular and flexible manner. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tomstdenis@gmail.com, http://libtom.org */ /* Implements ECC over Z/pZ for curve y^2 = x^3 - 3x + b * * All curves taken from NIST recommendation paper of July 1999 * Available at http://csrc.nist.gov/cryptval/dss.htm */ /** @file ltc_ecc_mul2add.c ECC Crypto, Shamir's Trick, Tom St Denis */ #ifdef LTC_MECC #ifdef LTC_ECC_SHAMIR /** Computes kA*A + kB*B = C using Shamir's Trick @param A First point to multiply @param kA What to multiple A by @param B Second point to multiply @param kB What to multiple B by @param C [out] Destination point (can overlap with A or B @param modulus Modulus for curve @return CRYPT_OK on success */ int ltc_ecc_mul2add(ecc_point *A, void *kA, ecc_point *B, void *kB, ecc_point *C, void *modulus) { ecc_point *precomp[16]; unsigned bitbufA, bitbufB, lenA, lenB, len, x, y, nA, nB, nibble; unsigned char *tA, *tB; int err, first; void *mp, *mu; /* argchks */ LTC_ARGCHK(A != NULL); LTC_ARGCHK(B != NULL); LTC_ARGCHK(C != NULL); LTC_ARGCHK(kA != NULL); LTC_ARGCHK(kB != NULL); LTC_ARGCHK(modulus != NULL); /* allocate memory */ tA = AUTO_CAST(XCALLOC(1, ECC_BUF_SIZE)); if (tA == NULL) { return CRYPT_MEM; } tB = AUTO_CAST(XCALLOC(1, ECC_BUF_SIZE)); if (tB == NULL) { XFREE(tA); return CRYPT_MEM; } /* get sizes */ lenA = mp_unsigned_bin_size(kA); lenB = mp_unsigned_bin_size(kB); len = MAX(lenA, lenB); /* sanity check */ if ((lenA > ECC_BUF_SIZE) || (lenB > ECC_BUF_SIZE)) { err = CRYPT_INVALID_ARG; goto ERR_T; } /* extract and justify kA */ mp_to_unsigned_bin(kA, (len - lenA) + tA); /* extract and justify kB */ mp_to_unsigned_bin(kB, (len - lenB) + tB); /* allocate the table */ for (x = 0; x < 16; x++) { precomp[x] = ltc_ecc_new_point(); if (precomp[x] == NULL) { for (y = 0; y < x; ++y) { ltc_ecc_del_point(precomp[y]); } err = CRYPT_MEM; goto ERR_T; } } /* init montgomery reduction */ if ((err = mp_montgomery_setup(modulus, &mp)) != CRYPT_OK) { goto ERR_P; } if ((err = mp_init(&mu)) != CRYPT_OK) { goto ERR_MP; } if ((err = mp_montgomery_normalization(mu, modulus)) != CRYPT_OK) { goto ERR_MU; } /* copy ones ... */ if ((err = mp_mulmod(A->x, mu, modulus, precomp[1]->x)) != CRYPT_OK) { goto ERR_MU; } if ((err = mp_mulmod(A->y, mu, modulus, precomp[1]->y)) != CRYPT_OK) { goto ERR_MU; } if ((err = mp_mulmod(A->z, mu, modulus, precomp[1]->z)) != CRYPT_OK) { goto ERR_MU; } if ((err = mp_mulmod(B->x, mu, modulus, precomp[1 << 2]->x)) != CRYPT_OK) { goto ERR_MU; } if ((err = mp_mulmod(B->y, mu, modulus, precomp[1 << 2]->y)) != CRYPT_OK) { goto ERR_MU; } if ((err = mp_mulmod(B->z, mu, modulus, precomp[1 << 2]->z)) != CRYPT_OK) { goto ERR_MU; } /* precomp [i,0](A + B) table */ if ((err = ltc_mp.ecc_ptdbl(precomp[1], precomp[2], modulus, mp)) != CRYPT_OK) { goto ERR_MU; } if ((err = ltc_mp.ecc_ptadd(precomp[1], precomp[2], precomp[3], modulus, mp)) != CRYPT_OK) { goto ERR_MU; } /* precomp [0,i](A + B) table */ if ((err = ltc_mp.ecc_ptdbl(precomp[1 << 2], precomp[2 << 2], modulus, mp)) != CRYPT_OK) { goto ERR_MU; } if ((err = ltc_mp.ecc_ptadd(precomp[1 << 2], precomp[2 << 2], precomp[3 << 2], modulus, mp)) != CRYPT_OK) { goto ERR_MU; } /* precomp [i,j](A + B) table (i != 0, j != 0) */ for (x = 1; x < 4; x++) { for (y = 1; y < 4; y++) { if ((err = ltc_mp.ecc_ptadd(precomp[x], precomp[(y << 2)], precomp[x + (y << 2)], modulus, mp)) != CRYPT_OK) { goto ERR_MU; } } } nibble = 3; first = 1; bitbufA = tA[0]; bitbufB = tB[0]; /* for every byte of the multiplicands */ for (x = -1; ; ) { /* grab a nibble */ if (++nibble == 4) { ++x; if (x == len) break; bitbufA = tA[x]; bitbufB = tB[x]; nibble = 0; } /* extract two bits from both, shift/update */ nA = (bitbufA >> 6) & 0x03; nB = (bitbufB >> 6) & 0x03; bitbufA = (bitbufA << 2) & 0xFF; bitbufB = (bitbufB << 2) & 0xFF; /* if both zero, if first, continue */ if ((nA == 0) && (nB == 0) && (first == 1)) { continue; } /* double twice, only if this isn't the first */ if (first == 0) { /* double twice */ if ((err = ltc_mp.ecc_ptdbl(C, C, modulus, mp)) != CRYPT_OK) { goto ERR_MU; } if ((err = ltc_mp.ecc_ptdbl(C, C, modulus, mp)) != CRYPT_OK) { goto ERR_MU; } } /* if not both zero */ if ((nA != 0) || (nB != 0)) { if (first == 1) { /* if first, copy from table */ first = 0; if ((err = mp_copy(precomp[nA + (nB << 2)]->x, C->x)) != CRYPT_OK) { goto ERR_MU; } if ((err = mp_copy(precomp[nA + (nB << 2)]->y, C->y)) != CRYPT_OK) { goto ERR_MU; } if ((err = mp_copy(precomp[nA + (nB << 2)]->z, C->z)) != CRYPT_OK) { goto ERR_MU; } } else { /* if not first, add from table */ if ((err = ltc_mp.ecc_ptadd(C, precomp[nA + (nB << 2)], C, modulus, mp)) != CRYPT_OK) { goto ERR_MU; } } } } /* reduce to affine */ err = ltc_ecc_map(C, modulus, mp); /* clean up */ ERR_MU: mp_clear(mu); ERR_MP: mp_montgomery_free(mp); ERR_P: for (x = 0; x < 16; x++) { ltc_ecc_del_point(precomp[x]); } ERR_T: #ifdef LTC_CLEAN_STACK zeromem(tA, ECC_BUF_SIZE); zeromem(tB, ECC_BUF_SIZE); #endif XFREE(tA); XFREE(tB); return err; } #endif #endif /* $Source: /cvs/libtom/libtomcrypt/src/pk/ecc/ltc_ecc_mul2add.c,v $ */ /* $Revision: 1.8 $ */ /* $Date: 2007/05/12 14:32:35 $ */ /* LibTomCrypt, modular cryptographic library -- Tom St Denis * * LibTomCrypt is a library that provides various cryptographic * algorithms in a highly modular and flexible manner. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tomstdenis@gmail.com, http://libtom.org */ /* Implements ECC over Z/pZ for curve y^2 = x^3 - 3x + b * * All curves taken from NIST recommendation paper of July 1999 * Available at http://csrc.nist.gov/cryptval/dss.htm */ /** @file ltc_ecc_mulmod.c ECC Crypto, Tom St Denis */ #ifdef LTC_MECC #ifndef LTC_ECC_TIMING_RESISTANT /* size of sliding window, don't change this! */ #define WINSIZE 4 /** Perform a point multiplication @param k The scalar to multiply by @param G The base point @param R [out] Destination for kG @param modulus The modulus of the field the ECC curve is in @param map Boolean whether to map back to affine or not (1==map, 0 == leave in projective) @return CRYPT_OK on success */ int ltc_ecc_mulmod(void *k, ecc_point *G, ecc_point *R, void *modulus, int map) { ecc_point *tG, *M[8]; int i, j, err; void *mu, *mp; unsigned long buf; int first, bitbuf, bitcpy, bitcnt, mode, digidx; LTC_ARGCHK(k != NULL); LTC_ARGCHK(G != NULL); LTC_ARGCHK(R != NULL); LTC_ARGCHK(modulus != NULL); /* init montgomery reduction */ if ((err = mp_montgomery_setup(modulus, &mp)) != CRYPT_OK) { return err; } if ((err = mp_init(&mu)) != CRYPT_OK) { mp_montgomery_free(mp); return err; } if ((err = mp_montgomery_normalization(mu, modulus)) != CRYPT_OK) { mp_montgomery_free(mp); mp_clear(mu); return err; } /* alloc ram for window temps */ for (i = 0; i < 8; i++) { M[i] = ltc_ecc_new_point(); if (M[i] == NULL) { for (j = 0; j < i; j++) { ltc_ecc_del_point(M[j]); } mp_montgomery_free(mp); mp_clear(mu); return CRYPT_MEM; } } /* make a copy of G incase R==G */ tG = ltc_ecc_new_point(); if (tG == NULL) { err = CRYPT_MEM; goto done; } /* tG = G and convert to montgomery */ if (mp_cmp_d(mu, 1) == LTC_MP_EQ) { if ((err = mp_copy(G->x, tG->x)) != CRYPT_OK) { goto done; } if ((err = mp_copy(G->y, tG->y)) != CRYPT_OK) { goto done; } if ((err = mp_copy(G->z, tG->z)) != CRYPT_OK) { goto done; } } else { if ((err = mp_mulmod(G->x, mu, modulus, tG->x)) != CRYPT_OK) { goto done; } if ((err = mp_mulmod(G->y, mu, modulus, tG->y)) != CRYPT_OK) { goto done; } if ((err = mp_mulmod(G->z, mu, modulus, tG->z)) != CRYPT_OK) { goto done; } } mp_clear(mu); mu = NULL; /* calc the M tab, which holds kG for k==8..15 */ /* M[0] == 8G */ if ((err = ltc_mp.ecc_ptdbl(tG, M[0], modulus, mp)) != CRYPT_OK) { goto done; } if ((err = ltc_mp.ecc_ptdbl(M[0], M[0], modulus, mp)) != CRYPT_OK) { goto done; } if ((err = ltc_mp.ecc_ptdbl(M[0], M[0], modulus, mp)) != CRYPT_OK) { goto done; } /* now find (8+k)G for k=1..7 */ for (j = 9; j < 16; j++) { if ((err = ltc_mp.ecc_ptadd(M[j - 9], tG, M[j - 8], modulus, mp)) != CRYPT_OK) { goto done; } } /* setup sliding window */ mode = 0; bitcnt = 1; buf = 0; digidx = mp_get_digit_count(k) - 1; bitcpy = bitbuf = 0; first = 1; /* perform ops */ for ( ; ; ) { /* grab next digit as required */ if (--bitcnt == 0) { if (digidx == -1) { break; } buf = mp_get_digit(k, digidx); bitcnt = (int)ltc_mp.bits_per_digit; --digidx; } /* grab the next msb from the ltiplicand */ i = (buf >> (ltc_mp.bits_per_digit - 1)) & 1; buf <<= 1; /* skip leading zero bits */ if ((mode == 0) && (i == 0)) { continue; } /* if the bit is zero and mode == 1 then we double */ if ((mode == 1) && (i == 0)) { if ((err = ltc_mp.ecc_ptdbl(R, R, modulus, mp)) != CRYPT_OK) { goto done; } continue; } /* else we add it to the window */ bitbuf |= (i << (WINSIZE - ++bitcpy)); mode = 2; if (bitcpy == WINSIZE) { /* if this is the first window we do a simple copy */ if (first == 1) { /* R = kG [k = first window] */ if ((err = mp_copy(M[bitbuf - 8]->x, R->x)) != CRYPT_OK) { goto done; } if ((err = mp_copy(M[bitbuf - 8]->y, R->y)) != CRYPT_OK) { goto done; } if ((err = mp_copy(M[bitbuf - 8]->z, R->z)) != CRYPT_OK) { goto done; } first = 0; } else { /* normal window */ /* ok window is filled so double as required and add */ /* double first */ for (j = 0; j < WINSIZE; j++) { if ((err = ltc_mp.ecc_ptdbl(R, R, modulus, mp)) != CRYPT_OK) { goto done; } } /* then add, bitbuf will be 8..15 [8..2^WINSIZE] guaranteed */ if ((err = ltc_mp.ecc_ptadd(R, M[bitbuf - 8], R, modulus, mp)) != CRYPT_OK) { goto done; } } /* empty window and reset */ bitcpy = bitbuf = 0; mode = 1; } } /* if bits remain then double/add */ if ((mode == 2) && (bitcpy > 0)) { /* double then add */ for (j = 0; j < bitcpy; j++) { /* only double if we have had at least one add first */ if (first == 0) { if ((err = ltc_mp.ecc_ptdbl(R, R, modulus, mp)) != CRYPT_OK) { goto done; } } bitbuf <<= 1; if ((bitbuf & (1 << WINSIZE)) != 0) { if (first == 1) { /* first add, so copy */ if ((err = mp_copy(tG->x, R->x)) != CRYPT_OK) { goto done; } if ((err = mp_copy(tG->y, R->y)) != CRYPT_OK) { goto done; } if ((err = mp_copy(tG->z, R->z)) != CRYPT_OK) { goto done; } first = 0; } else { /* then add */ if ((err = ltc_mp.ecc_ptadd(R, tG, R, modulus, mp)) != CRYPT_OK) { goto done; } } } } } /* map R back from projective space */ if (map) { err = ltc_ecc_map(R, modulus, mp); } else { err = CRYPT_OK; } done: if (mu != NULL) { mp_clear(mu); } mp_montgomery_free(mp); ltc_ecc_del_point(tG); for (i = 0; i < 8; i++) { ltc_ecc_del_point(M[i]); } return err; } #endif #undef WINSIZE #endif /* $Source: /cvs/libtom/libtomcrypt/src/pk/ecc/ltc_ecc_mulmod.c,v $ */ /* $Revision: 1.26 $ */ /* $Date: 2007/05/12 14:32:35 $ */ /* LibTomCrypt, modular cryptographic library -- Tom St Denis * * LibTomCrypt is a library that provides various cryptographic * algorithms in a highly modular and flexible manner. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tomstdenis@gmail.com, http://libtom.org */ /* Implements ECC over Z/pZ for curve y^2 = x^3 - 3x + b * * All curves taken from NIST recommendation paper of July 1999 * Available at http://csrc.nist.gov/cryptval/dss.htm */ /** @file ltc_ecc_mulmod_timing.c ECC Crypto, Tom St Denis */ #ifdef LTC_MECC #ifdef LTC_ECC_TIMING_RESISTANT /** Perform a point multiplication (timing resistant) @param k The scalar to multiply by @param G The base point @param R [out] Destination for kG @param modulus The modulus of the field the ECC curve is in @param map Boolean whether to map back to affine or not (1==map, 0 == leave in projective) @return CRYPT_OK on success */ int ltc_ecc_mulmod(void *k, ecc_point *G, ecc_point *R, void *modulus, int map) { ecc_point *tG, *M[3]; int i, j, err; void *mu, *mp; unsigned long buf; int first, bitbuf, bitcpy, bitcnt, mode, digidx; LTC_ARGCHK(k != NULL); LTC_ARGCHK(G != NULL); LTC_ARGCHK(R != NULL); LTC_ARGCHK(modulus != NULL); /* init montgomery reduction */ if ((err = mp_montgomery_setup(modulus, &mp)) != CRYPT_OK) { return err; } if ((err = mp_init(&mu)) != CRYPT_OK) { mp_montgomery_free(mp); return err; } if ((err = mp_montgomery_normalization(mu, modulus)) != CRYPT_OK) { mp_clear(mu); mp_montgomery_free(mp); return err; } /* alloc ram for window temps */ for (i = 0; i < 3; i++) { M[i] = ltc_ecc_new_point(); if (M[i] == NULL) { for (j = 0; j < i; j++) { ltc_ecc_del_point(M[j]); } mp_clear(mu); mp_montgomery_free(mp); return CRYPT_MEM; } } /* make a copy of G incase R==G */ tG = ltc_ecc_new_point(); if (tG == NULL) { err = CRYPT_MEM; goto done; } /* tG = G and convert to montgomery */ if ((err = mp_mulmod(G->x, mu, modulus, tG->x)) != CRYPT_OK) { goto done; } if ((err = mp_mulmod(G->y, mu, modulus, tG->y)) != CRYPT_OK) { goto done; } if ((err = mp_mulmod(G->z, mu, modulus, tG->z)) != CRYPT_OK) { goto done; } mp_clear(mu); mu = NULL; /* calc the M tab */ /* M[0] == G */ if ((err = mp_copy(tG->x, M[0]->x)) != CRYPT_OK) { goto done; } if ((err = mp_copy(tG->y, M[0]->y)) != CRYPT_OK) { goto done; } if ((err = mp_copy(tG->z, M[0]->z)) != CRYPT_OK) { goto done; } /* M[1] == 2G */ if ((err = ltc_mp.ecc_ptdbl(tG, M[1], modulus, mp)) != CRYPT_OK) { goto done; } /* setup sliding window */ mode = 0; bitcnt = 1; buf = 0; digidx = mp_get_digit_count(k) - 1; bitcpy = bitbuf = 0; first = 1; /* perform ops */ for ( ; ; ) { /* grab next digit as required */ if (--bitcnt == 0) { if (digidx == -1) { break; } buf = mp_get_digit(k, digidx); bitcnt = (int)MP_DIGIT_BIT; --digidx; } /* grab the next msb from the ltiplicand */ i = (buf >> (MP_DIGIT_BIT - 1)) & 1; buf <<= 1; if ((mode == 0) && (i == 0)) { /* dummy operations */ if ((err = ltc_mp.ecc_ptadd(M[0], M[1], M[2], modulus, mp)) != CRYPT_OK) { goto done; } if ((err = ltc_mp.ecc_ptdbl(M[1], M[2], modulus, mp)) != CRYPT_OK) { goto done; } continue; } if ((mode == 0) && (i == 1)) { mode = 1; /* dummy operations */ if ((err = ltc_mp.ecc_ptadd(M[0], M[1], M[2], modulus, mp)) != CRYPT_OK) { goto done; } if ((err = ltc_mp.ecc_ptdbl(M[1], M[2], modulus, mp)) != CRYPT_OK) { goto done; } continue; } if ((err = ltc_mp.ecc_ptadd(M[0], M[1], M[i ^ 1], modulus, mp)) != CRYPT_OK) { goto done; } if ((err = ltc_mp.ecc_ptdbl(M[i], M[i], modulus, mp)) != CRYPT_OK) { goto done; } } /* copy result out */ if ((err = mp_copy(M[0]->x, R->x)) != CRYPT_OK) { goto done; } if ((err = mp_copy(M[0]->y, R->y)) != CRYPT_OK) { goto done; } if ((err = mp_copy(M[0]->z, R->z)) != CRYPT_OK) { goto done; } /* map R back from projective space */ if (map) { err = ltc_ecc_map(R, modulus, mp); } else { err = CRYPT_OK; } done: if (mu != NULL) { mp_clear(mu); } mp_montgomery_free(mp); ltc_ecc_del_point(tG); for (i = 0; i < 3; i++) { ltc_ecc_del_point(M[i]); } return err; } #endif #endif /* $Source: /cvs/libtom/libtomcrypt/src/pk/ecc/ltc_ecc_mulmod_timing.c,v $ */ /* $Revision: 1.13 $ */ /* $Date: 2007/05/12 14:32:35 $ */ /* LibTomCrypt, modular cryptographic library -- Tom St Denis * * LibTomCrypt is a library that provides various cryptographic * algorithms in a highly modular and flexible manner. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tomstdenis@gmail.com, http://libtom.org */ /* Implements ECC over Z/pZ for curve y^2 = x^3 - 3x + b * * All curves taken from NIST recommendation paper of July 1999 * Available at http://csrc.nist.gov/cryptval/dss.htm */ /** @file ltc_ecc_points.c ECC Crypto, Tom St Denis */ #ifdef LTC_MECC /** Allocate a new ECC point @return A newly allocated point or NULL on error */ ecc_point *ltc_ecc_new_point(void) { ecc_point *p; p = AUTO_CAST(XCALLOC(1, sizeof(*p))); if (p == NULL) { return NULL; } if (mp_init_multi(&p->x, &p->y, &p->z, NULL) != CRYPT_OK) { XFREE(p); return NULL; } return p; } /** Free an ECC point from memory @param p The point to free */ void ltc_ecc_del_point(ecc_point *p) { /* prevents free'ing null arguments */ if (p != NULL) { mp_clear_multi(p->x, p->y, p->z, NULL); /* note: p->z may be NULL but that's ok with this function anyways */ XFREE(p); } } #endif /* $Source: /cvs/libtom/libtomcrypt/src/pk/ecc/ltc_ecc_points.c,v $ */ /* $Revision: 1.7 $ */ /* $Date: 2007/05/12 14:32:35 $ */ /* LibTomCrypt, modular cryptographic library -- Tom St Denis * * LibTomCrypt is a library that provides various cryptographic * algorithms in a highly modular and flexible manner. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tomstdenis@gmail.com, http://libtom.org */ /* Implements ECC over Z/pZ for curve y^2 = x^3 - 3x + b * * All curves taken from NIST recommendation paper of July 1999 * Available at http://csrc.nist.gov/cryptval/dss.htm */ /** @file ltc_ecc_projective_add_point.c ECC Crypto, Tom St Denis */ #if defined(LTC_MECC) && (!defined(LTC_MECC_ACCEL) || defined(LTM_LTC_DESC)) /** Add two ECC points @param P The point to add @param Q The point to add @param R [out] The destination of the double @param modulus The modulus of the field the ECC curve is in @param mp The "b" value from montgomery_setup() @return CRYPT_OK on success */ int ltc_ecc_projective_add_point(ecc_point *P, ecc_point *Q, ecc_point *R, void *modulus, void *mp) { void *t1, *t2, *x, *y, *z; int err; LTC_ARGCHK(P != NULL); LTC_ARGCHK(Q != NULL); LTC_ARGCHK(R != NULL); LTC_ARGCHK(modulus != NULL); LTC_ARGCHK(mp != NULL); if ((err = mp_init_multi(&t1, &t2, &x, &y, &z, NULL)) != CRYPT_OK) { return err; } /* should we dbl instead? */ if ((err = mp_sub(modulus, Q->y, t1)) != CRYPT_OK) { goto done; } if ((mp_cmp(P->x, Q->x) == LTC_MP_EQ) && ((Q->z != NULL) && (mp_cmp(P->z, Q->z) == LTC_MP_EQ)) && ((mp_cmp(P->y, Q->y) == LTC_MP_EQ) || (mp_cmp(P->y, t1) == LTC_MP_EQ))) { mp_clear_multi(t1, t2, x, y, z, NULL); return ltc_ecc_projective_dbl_point(P, R, modulus, mp); } if ((err = mp_copy(P->x, x)) != CRYPT_OK) { goto done; } if ((err = mp_copy(P->y, y)) != CRYPT_OK) { goto done; } if ((err = mp_copy(P->z, z)) != CRYPT_OK) { goto done; } /* if Z is one then these are no-operations */ if (Q->z != NULL) { /* T1 = Z' * Z' */ if ((err = mp_sqr(Q->z, t1)) != CRYPT_OK) { goto done; } if ((err = mp_montgomery_reduce(t1, modulus, mp)) != CRYPT_OK) { goto done; } /* X = X * T1 */ if ((err = mp_mul(t1, x, x)) != CRYPT_OK) { goto done; } if ((err = mp_montgomery_reduce(x, modulus, mp)) != CRYPT_OK) { goto done; } /* T1 = Z' * T1 */ if ((err = mp_mul(Q->z, t1, t1)) != CRYPT_OK) { goto done; } if ((err = mp_montgomery_reduce(t1, modulus, mp)) != CRYPT_OK) { goto done; } /* Y = Y * T1 */ if ((err = mp_mul(t1, y, y)) != CRYPT_OK) { goto done; } if ((err = mp_montgomery_reduce(y, modulus, mp)) != CRYPT_OK) { goto done; } } /* T1 = Z*Z */ if ((err = mp_sqr(z, t1)) != CRYPT_OK) { goto done; } if ((err = mp_montgomery_reduce(t1, modulus, mp)) != CRYPT_OK) { goto done; } /* T2 = X' * T1 */ if ((err = mp_mul(Q->x, t1, t2)) != CRYPT_OK) { goto done; } if ((err = mp_montgomery_reduce(t2, modulus, mp)) != CRYPT_OK) { goto done; } /* T1 = Z * T1 */ if ((err = mp_mul(z, t1, t1)) != CRYPT_OK) { goto done; } if ((err = mp_montgomery_reduce(t1, modulus, mp)) != CRYPT_OK) { goto done; } /* T1 = Y' * T1 */ if ((err = mp_mul(Q->y, t1, t1)) != CRYPT_OK) { goto done; } if ((err = mp_montgomery_reduce(t1, modulus, mp)) != CRYPT_OK) { goto done; } /* Y = Y - T1 */ if ((err = mp_sub(y, t1, y)) != CRYPT_OK) { goto done; } if (mp_cmp_d(y, 0) == LTC_MP_LT) { if ((err = mp_add(y, modulus, y)) != CRYPT_OK) { goto done; } } /* T1 = 2T1 */ if ((err = mp_add(t1, t1, t1)) != CRYPT_OK) { goto done; } if (mp_cmp(t1, modulus) != LTC_MP_LT) { if ((err = mp_sub(t1, modulus, t1)) != CRYPT_OK) { goto done; } } /* T1 = Y + T1 */ if ((err = mp_add(t1, y, t1)) != CRYPT_OK) { goto done; } if (mp_cmp(t1, modulus) != LTC_MP_LT) { if ((err = mp_sub(t1, modulus, t1)) != CRYPT_OK) { goto done; } } /* X = X - T2 */ if ((err = mp_sub(x, t2, x)) != CRYPT_OK) { goto done; } if (mp_cmp_d(x, 0) == LTC_MP_LT) { if ((err = mp_add(x, modulus, x)) != CRYPT_OK) { goto done; } } /* T2 = 2T2 */ if ((err = mp_add(t2, t2, t2)) != CRYPT_OK) { goto done; } if (mp_cmp(t2, modulus) != LTC_MP_LT) { if ((err = mp_sub(t2, modulus, t2)) != CRYPT_OK) { goto done; } } /* T2 = X + T2 */ if ((err = mp_add(t2, x, t2)) != CRYPT_OK) { goto done; } if (mp_cmp(t2, modulus) != LTC_MP_LT) { if ((err = mp_sub(t2, modulus, t2)) != CRYPT_OK) { goto done; } } /* if Z' != 1 */ if (Q->z != NULL) { /* Z = Z * Z' */ if ((err = mp_mul(z, Q->z, z)) != CRYPT_OK) { goto done; } if ((err = mp_montgomery_reduce(z, modulus, mp)) != CRYPT_OK) { goto done; } } /* Z = Z * X */ if ((err = mp_mul(z, x, z)) != CRYPT_OK) { goto done; } if ((err = mp_montgomery_reduce(z, modulus, mp)) != CRYPT_OK) { goto done; } /* T1 = T1 * X */ if ((err = mp_mul(t1, x, t1)) != CRYPT_OK) { goto done; } if ((err = mp_montgomery_reduce(t1, modulus, mp)) != CRYPT_OK) { goto done; } /* X = X * X */ if ((err = mp_sqr(x, x)) != CRYPT_OK) { goto done; } if ((err = mp_montgomery_reduce(x, modulus, mp)) != CRYPT_OK) { goto done; } /* T2 = T2 * x */ if ((err = mp_mul(t2, x, t2)) != CRYPT_OK) { goto done; } if ((err = mp_montgomery_reduce(t2, modulus, mp)) != CRYPT_OK) { goto done; } /* T1 = T1 * X */ if ((err = mp_mul(t1, x, t1)) != CRYPT_OK) { goto done; } if ((err = mp_montgomery_reduce(t1, modulus, mp)) != CRYPT_OK) { goto done; } /* X = Y*Y */ if ((err = mp_sqr(y, x)) != CRYPT_OK) { goto done; } if ((err = mp_montgomery_reduce(x, modulus, mp)) != CRYPT_OK) { goto done; } /* X = X - T2 */ if ((err = mp_sub(x, t2, x)) != CRYPT_OK) { goto done; } if (mp_cmp_d(x, 0) == LTC_MP_LT) { if ((err = mp_add(x, modulus, x)) != CRYPT_OK) { goto done; } } /* T2 = T2 - X */ if ((err = mp_sub(t2, x, t2)) != CRYPT_OK) { goto done; } if (mp_cmp_d(t2, 0) == LTC_MP_LT) { if ((err = mp_add(t2, modulus, t2)) != CRYPT_OK) { goto done; } } /* T2 = T2 - X */ if ((err = mp_sub(t2, x, t2)) != CRYPT_OK) { goto done; } if (mp_cmp_d(t2, 0) == LTC_MP_LT) { if ((err = mp_add(t2, modulus, t2)) != CRYPT_OK) { goto done; } } /* T2 = T2 * Y */ if ((err = mp_mul(t2, y, t2)) != CRYPT_OK) { goto done; } if ((err = mp_montgomery_reduce(t2, modulus, mp)) != CRYPT_OK) { goto done; } /* Y = T2 - T1 */ if ((err = mp_sub(t2, t1, y)) != CRYPT_OK) { goto done; } if (mp_cmp_d(y, 0) == LTC_MP_LT) { if ((err = mp_add(y, modulus, y)) != CRYPT_OK) { goto done; } } /* Y = Y/2 */ if (mp_isodd(y)) { if ((err = mp_add(y, modulus, y)) != CRYPT_OK) { goto done; } } if ((err = mp_div_2(y, y)) != CRYPT_OK) { goto done; } if ((err = mp_copy(x, R->x)) != CRYPT_OK) { goto done; } if ((err = mp_copy(y, R->y)) != CRYPT_OK) { goto done; } if ((err = mp_copy(z, R->z)) != CRYPT_OK) { goto done; } err = CRYPT_OK; done: mp_clear_multi(t1, t2, x, y, z, NULL); return err; } #endif /* $Source: /cvs/libtom/libtomcrypt/src/pk/ecc/ltc_ecc_projective_add_point.c,v $ */ /* $Revision: 1.16 $ */ /* $Date: 2007/05/12 14:32:35 $ */ /* LibTomCrypt, modular cryptographic library -- Tom St Denis * * LibTomCrypt is a library that provides various cryptographic * algorithms in a highly modular and flexible manner. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tomstdenis@gmail.com, http://libtom.org */ /* Implements ECC over Z/pZ for curve y^2 = x^3 - 3x + b * * All curves taken from NIST recommendation paper of July 1999 * Available at http://csrc.nist.gov/cryptval/dss.htm */ /** @file ltc_ecc_projective_dbl_point.c ECC Crypto, Tom St Denis */ #if defined(LTC_MECC) && (!defined(LTC_MECC_ACCEL) || defined(LTM_LTC_DESC)) /** Double an ECC point @param P The point to double @param R [out] The destination of the double @param modulus The modulus of the field the ECC curve is in @param mp The "b" value from montgomery_setup() @return CRYPT_OK on success */ int ltc_ecc_projective_dbl_point(ecc_point *P, ecc_point *R, void *modulus, void *mp) { void *t1, *t2; int err; LTC_ARGCHK(P != NULL); LTC_ARGCHK(R != NULL); LTC_ARGCHK(modulus != NULL); LTC_ARGCHK(mp != NULL); if ((err = mp_init_multi(&t1, &t2, NULL)) != CRYPT_OK) { return err; } if (P != R) { if ((err = mp_copy(P->x, R->x)) != CRYPT_OK) { goto done; } if ((err = mp_copy(P->y, R->y)) != CRYPT_OK) { goto done; } if ((err = mp_copy(P->z, R->z)) != CRYPT_OK) { goto done; } } /* t1 = Z * Z */ if ((err = mp_sqr(R->z, t1)) != CRYPT_OK) { goto done; } if ((err = mp_montgomery_reduce(t1, modulus, mp)) != CRYPT_OK) { goto done; } /* Z = Y * Z */ if ((err = mp_mul(R->z, R->y, R->z)) != CRYPT_OK) { goto done; } if ((err = mp_montgomery_reduce(R->z, modulus, mp)) != CRYPT_OK) { goto done; } /* Z = 2Z */ if ((err = mp_add(R->z, R->z, R->z)) != CRYPT_OK) { goto done; } if (mp_cmp(R->z, modulus) != LTC_MP_LT) { if ((err = mp_sub(R->z, modulus, R->z)) != CRYPT_OK) { goto done; } } /* T2 = X - T1 */ if ((err = mp_sub(R->x, t1, t2)) != CRYPT_OK) { goto done; } if (mp_cmp_d(t2, 0) == LTC_MP_LT) { if ((err = mp_add(t2, modulus, t2)) != CRYPT_OK) { goto done; } } /* T1 = X + T1 */ if ((err = mp_add(t1, R->x, t1)) != CRYPT_OK) { goto done; } if (mp_cmp(t1, modulus) != LTC_MP_LT) { if ((err = mp_sub(t1, modulus, t1)) != CRYPT_OK) { goto done; } } /* T2 = T1 * T2 */ if ((err = mp_mul(t1, t2, t2)) != CRYPT_OK) { goto done; } if ((err = mp_montgomery_reduce(t2, modulus, mp)) != CRYPT_OK) { goto done; } /* T1 = 2T2 */ if ((err = mp_add(t2, t2, t1)) != CRYPT_OK) { goto done; } if (mp_cmp(t1, modulus) != LTC_MP_LT) { if ((err = mp_sub(t1, modulus, t1)) != CRYPT_OK) { goto done; } } /* T1 = T1 + T2 */ if ((err = mp_add(t1, t2, t1)) != CRYPT_OK) { goto done; } if (mp_cmp(t1, modulus) != LTC_MP_LT) { if ((err = mp_sub(t1, modulus, t1)) != CRYPT_OK) { goto done; } } /* Y = 2Y */ if ((err = mp_add(R->y, R->y, R->y)) != CRYPT_OK) { goto done; } if (mp_cmp(R->y, modulus) != LTC_MP_LT) { if ((err = mp_sub(R->y, modulus, R->y)) != CRYPT_OK) { goto done; } } /* Y = Y * Y */ if ((err = mp_sqr(R->y, R->y)) != CRYPT_OK) { goto done; } if ((err = mp_montgomery_reduce(R->y, modulus, mp)) != CRYPT_OK) { goto done; } /* T2 = Y * Y */ if ((err = mp_sqr(R->y, t2)) != CRYPT_OK) { goto done; } if ((err = mp_montgomery_reduce(t2, modulus, mp)) != CRYPT_OK) { goto done; } /* T2 = T2/2 */ if (mp_isodd(t2)) { if ((err = mp_add(t2, modulus, t2)) != CRYPT_OK) { goto done; } } if ((err = mp_div_2(t2, t2)) != CRYPT_OK) { goto done; } /* Y = Y * X */ if ((err = mp_mul(R->y, R->x, R->y)) != CRYPT_OK) { goto done; } if ((err = mp_montgomery_reduce(R->y, modulus, mp)) != CRYPT_OK) { goto done; } /* X = T1 * T1 */ if ((err = mp_sqr(t1, R->x)) != CRYPT_OK) { goto done; } if ((err = mp_montgomery_reduce(R->x, modulus, mp)) != CRYPT_OK) { goto done; } /* X = X - Y */ if ((err = mp_sub(R->x, R->y, R->x)) != CRYPT_OK) { goto done; } if (mp_cmp_d(R->x, 0) == LTC_MP_LT) { if ((err = mp_add(R->x, modulus, R->x)) != CRYPT_OK) { goto done; } } /* X = X - Y */ if ((err = mp_sub(R->x, R->y, R->x)) != CRYPT_OK) { goto done; } if (mp_cmp_d(R->x, 0) == LTC_MP_LT) { if ((err = mp_add(R->x, modulus, R->x)) != CRYPT_OK) { goto done; } } /* Y = Y - X */ if ((err = mp_sub(R->y, R->x, R->y)) != CRYPT_OK) { goto done; } if (mp_cmp_d(R->y, 0) == LTC_MP_LT) { if ((err = mp_add(R->y, modulus, R->y)) != CRYPT_OK) { goto done; } } /* Y = Y * T1 */ if ((err = mp_mul(R->y, t1, R->y)) != CRYPT_OK) { goto done; } if ((err = mp_montgomery_reduce(R->y, modulus, mp)) != CRYPT_OK) { goto done; } /* Y = Y - T2 */ if ((err = mp_sub(R->y, t2, R->y)) != CRYPT_OK) { goto done; } if (mp_cmp_d(R->y, 0) == LTC_MP_LT) { if ((err = mp_add(R->y, modulus, R->y)) != CRYPT_OK) { goto done; } } err = CRYPT_OK; done: mp_clear_multi(t1, t2, NULL); return err; } #endif /* $Source: /cvs/libtom/libtomcrypt/src/pk/ecc/ltc_ecc_projective_dbl_point.c,v $ */ /* $Revision: 1.11 $ */ /* $Date: 2007/05/12 14:32:35 $ */ /* LibTomCrypt, modular cryptographic library -- Tom St Denis * * LibTomCrypt is a library that provides various cryptographic * algorithms in a highly modular and flexible manner. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tomstdenis@gmail.com, http://libtom.org */ #define DESC_DEF_ONLY #ifdef LTM_DESC #undef mp_init #undef mp_init_multi #undef mp_clear #undef mp_clear_multi #undef mp_init_copy #undef mp_neg #undef mp_copy #undef mp_set #undef mp_set_int #undef mp_get_int #undef mp_get_digit #undef mp_get_digit_count #undef mp_cmp #undef mp_cmp_d #undef mp_count_bits #undef mp_cnt_lsb #undef mp_2expt #undef mp_read_radix #undef mp_toradix #undef mp_unsigned_bin_size #undef mp_to_unsigned_bin #undef mp_read_unsigned_bin #undef mp_add #undef mp_add_d #undef mp_sub #undef mp_sub_d #undef mp_mul #undef mp_mul_d #undef mp_sqr #undef mp_div #undef mp_div_2 #undef mp_mod #undef mp_mod_d #undef mp_gcd #undef mp_lcm #undef mp_mulmod #undef mp_sqrmod #undef mp_invmod #undef mp_montgomery_setup #undef mp_montgomery_normalization #undef mp_montgomery_reduce #undef mp_montgomery_free #undef mp_exptmod #undef mp_prime_is_prime #undef mp_iszero #undef mp_isodd #undef mp_exch #undef mp_tohex static const struct { int mpi_code, ltc_code; } mpi_to_ltc_codes[] = { { MP_OKAY, CRYPT_OK }, { MP_MEM, CRYPT_MEM }, { MP_VAL, CRYPT_INVALID_ARG }, }; /** Convert a MPI error to a LTC error (Possibly the most powerful function ever! Oh wait... no) @param err The error to convert @return The equivalent LTC error code or CRYPT_ERROR if none found */ static int mpi_to_ltc_error(int err) { int x; for (x = 0; x < (int)(sizeof(mpi_to_ltc_codes) / sizeof(mpi_to_ltc_codes[0])); x++) { if (err == mpi_to_ltc_codes[x].mpi_code) { return mpi_to_ltc_codes[x].ltc_code; } } return CRYPT_ERROR; } static int init(void **a) { int err; LTC_ARGCHK(a != NULL); *a = XCALLOC(1, sizeof(mp_int)); if (*a == NULL) { return CRYPT_MEM; } if ((err = mpi_to_ltc_error(mp_init(AUTO_CAST(*a)))) != CRYPT_OK) { XFREE(*a); } return err; } static void deinit(void *a) { LTC_ARGCHKVD(a != NULL); mp_clear(AUTO_CAST(a)); XFREE(a); } static int neg(void *a, void *b) { LTC_ARGCHK(a != NULL); LTC_ARGCHK(b != NULL); return mpi_to_ltc_error(mp_neg(AUTO_CAST(a), AUTO_CAST(b))); } static int copy(void *a, void *b) { LTC_ARGCHK(a != NULL); LTC_ARGCHK(b != NULL); return mpi_to_ltc_error(mp_copy(AUTO_CAST(a), AUTO_CAST(b))); } static int init_copy(void **a, void *b) { if (init(a) != CRYPT_OK) { return CRYPT_MEM; } return copy(b, *a); } /* ---- trivial ---- */ static int set_int(void *a, unsigned long b) { LTC_ARGCHK(a != NULL); return mpi_to_ltc_error(mp_set_int(AUTO_CAST(a), AUTO_CAST(b))); } static unsigned long get_int(void *a) { LTC_ARGCHK(a != NULL); return mp_get_int(AUTO_CAST(a)); } static unsigned long get_digit(void *a, int n) { mp_int *A; LTC_ARGCHK(a != NULL); A = AUTO_CAST(a); return (n >= A->used || n < 0) ? 0 : A->dp[n]; } static int get_digit_count(void *a) { mp_int *A; LTC_ARGCHK(a != NULL); A = AUTO_CAST(a); return A->used; } static int compare(void *a, void *b) { int ret; LTC_ARGCHK(a != NULL); LTC_ARGCHK(b != NULL); ret = mp_cmp(AUTO_CAST(a), AUTO_CAST(b)); switch (ret) { case MP_LT: return LTC_MP_LT; case MP_EQ: return LTC_MP_EQ; case MP_GT: return LTC_MP_GT; } return 0; } static int compare_d(void *a, unsigned long b) { int ret; LTC_ARGCHK(a != NULL); ret = mp_cmp_d(AUTO_CAST(a), AUTO_CAST(b)); switch (ret) { case MP_LT: return LTC_MP_LT; case MP_EQ: return LTC_MP_EQ; case MP_GT: return LTC_MP_GT; } return 0; } static int count_bits(void *a) { LTC_ARGCHK(a != NULL); return mp_count_bits(AUTO_CAST(a)); } static int count_lsb_bits(void *a) { LTC_ARGCHK(a != NULL); return mp_cnt_lsb(AUTO_CAST(a)); } static int twoexpt(void *a, int n) { LTC_ARGCHK(a != NULL); return mpi_to_ltc_error(mp_2expt(AUTO_CAST(a), n)); } /* ---- conversions ---- */ /* read ascii string */ static int read_radix(void *a, const char *b, int radix) { LTC_ARGCHK(a != NULL); LTC_ARGCHK(b != NULL); return mpi_to_ltc_error(mp_read_radix(AUTO_CAST(a), AUTO_CAST(b), radix)); } /* write one */ static int write_radix(void *a, char *b, int radix) { LTC_ARGCHK(a != NULL); LTC_ARGCHK(b != NULL); return mpi_to_ltc_error(mp_toradix(AUTO_CAST(a), b, radix)); } /* get size as unsigned char string */ static unsigned long unsigned_size(void *a) { LTC_ARGCHK(a != NULL); return mp_unsigned_bin_size(AUTO_CAST(a)); } /* store */ static int unsigned_write(void *a, unsigned char *b) { LTC_ARGCHK(a != NULL); LTC_ARGCHK(b != NULL); return mpi_to_ltc_error(mp_to_unsigned_bin(AUTO_CAST(a), b)); } /* read */ static int unsigned_read(void *a, unsigned char *b, unsigned long len) { LTC_ARGCHK(a != NULL); LTC_ARGCHK(b != NULL); return mpi_to_ltc_error(mp_read_unsigned_bin(AUTO_CAST(a), b, len)); } /* add */ static int add(void *a, void *b, void *c) { LTC_ARGCHK(a != NULL); LTC_ARGCHK(b != NULL); LTC_ARGCHK(c != NULL); return mpi_to_ltc_error(mp_add(AUTO_CAST(a), AUTO_CAST(b), AUTO_CAST(c))); } static int addi(void *a, unsigned long b, void *c) { LTC_ARGCHK(a != NULL); LTC_ARGCHK(c != NULL); return mpi_to_ltc_error(mp_add_d(AUTO_CAST(a), b, AUTO_CAST(c))); } /* sub */ static int sub(void *a, void *b, void *c) { LTC_ARGCHK(a != NULL); LTC_ARGCHK(b != NULL); LTC_ARGCHK(c != NULL); return mpi_to_ltc_error(mp_sub(AUTO_CAST(a), AUTO_CAST(b), AUTO_CAST(c))); } static int subi(void *a, unsigned long b, void *c) { LTC_ARGCHK(a != NULL); LTC_ARGCHK(c != NULL); return mpi_to_ltc_error(mp_sub_d(AUTO_CAST(a), b, AUTO_CAST(c))); } /* mul */ static int mul(void *a, void *b, void *c) { LTC_ARGCHK(a != NULL); LTC_ARGCHK(b != NULL); LTC_ARGCHK(c != NULL); return mpi_to_ltc_error(mp_mul(AUTO_CAST(a), AUTO_CAST(b), AUTO_CAST(c))); } static int muli(void *a, unsigned long b, void *c) { LTC_ARGCHK(a != NULL); LTC_ARGCHK(c != NULL); return mpi_to_ltc_error(mp_mul_d(AUTO_CAST(a), b, AUTO_CAST(c))); } /* sqr */ static int sqr(void *a, void *b) { LTC_ARGCHK(a != NULL); LTC_ARGCHK(b != NULL); return mpi_to_ltc_error(mp_sqr(AUTO_CAST(a), AUTO_CAST(b))); } /* div */ static int divide(void *a, void *b, void *c, void *d) { LTC_ARGCHK(a != NULL); LTC_ARGCHK(b != NULL); return mpi_to_ltc_error(mp_div(AUTO_CAST(a), AUTO_CAST(b), AUTO_CAST(c), AUTO_CAST(d))); } static int div_2(void *a, void *b) { LTC_ARGCHK(a != NULL); LTC_ARGCHK(b != NULL); return mpi_to_ltc_error(mp_div_2(AUTO_CAST(a), AUTO_CAST(b))); } /* modi */ static int modi(void *a, unsigned long b, unsigned long *c) { mp_digit tmp; int err; LTC_ARGCHK(a != NULL); LTC_ARGCHK(c != NULL); if ((err = mpi_to_ltc_error(mp_mod_d(AUTO_CAST(a), b, &tmp))) != CRYPT_OK) { return err; } *c = tmp; return CRYPT_OK; } /* gcd */ static int gcd(void *a, void *b, void *c) { LTC_ARGCHK(a != NULL); LTC_ARGCHK(b != NULL); LTC_ARGCHK(c != NULL); return mpi_to_ltc_error(mp_gcd(AUTO_CAST(a), AUTO_CAST(b), AUTO_CAST(c))); } /* lcm */ static int lcm(void *a, void *b, void *c) { LTC_ARGCHK(a != NULL); LTC_ARGCHK(b != NULL); LTC_ARGCHK(c != NULL); return mpi_to_ltc_error(mp_lcm(AUTO_CAST(a), AUTO_CAST(b), AUTO_CAST(c))); } static int mulmod(void *a, void *b, void *c, void *d) { LTC_ARGCHK(a != NULL); LTC_ARGCHK(b != NULL); LTC_ARGCHK(c != NULL); LTC_ARGCHK(d != NULL); return mpi_to_ltc_error(mp_mulmod(AUTO_CAST(a), AUTO_CAST(b), AUTO_CAST(c), AUTO_CAST(d))); } static int sqrmod(void *a, void *b, void *c) { LTC_ARGCHK(a != NULL); LTC_ARGCHK(b != NULL); LTC_ARGCHK(c != NULL); return mpi_to_ltc_error(mp_sqrmod(AUTO_CAST(a), AUTO_CAST(b), AUTO_CAST(c))); } /* invmod */ static int invmod(void *a, void *b, void *c) { LTC_ARGCHK(a != NULL); LTC_ARGCHK(b != NULL); LTC_ARGCHK(c != NULL); return mpi_to_ltc_error(mp_invmod(AUTO_CAST(a), AUTO_CAST(b), AUTO_CAST(c))); } /* setup */ static int montgomery_setup(void *a, void **b) { int err; LTC_ARGCHK(a != NULL); LTC_ARGCHK(b != NULL); *b = XCALLOC(1, sizeof(mp_digit)); if (*b == NULL) { return CRYPT_MEM; } if ((err = mpi_to_ltc_error(mp_montgomery_setup(AUTO_CAST(a), (mp_digit *)*b))) != CRYPT_OK) { XFREE(*b); } return err; } /* get normalization value */ static int montgomery_normalization(void *a, void *b) { LTC_ARGCHK(a != NULL); LTC_ARGCHK(b != NULL); return mpi_to_ltc_error(mp_montgomery_calc_normalization(AUTO_CAST(a), AUTO_CAST(b))); } /* reduce */ static int montgomery_reduce(void *a, void *b, void *c) { LTC_ARGCHK(a != NULL); LTC_ARGCHK(b != NULL); LTC_ARGCHK(c != NULL); return mpi_to_ltc_error(mp_montgomery_reduce(AUTO_CAST(a), AUTO_CAST(b), *((mp_digit *)c))); } /* clean up */ static void montgomery_deinit(void *a) { XFREE(a); } static int exptmod(void *a, void *b, void *c, void *d) { LTC_ARGCHK(a != NULL); LTC_ARGCHK(b != NULL); LTC_ARGCHK(c != NULL); LTC_ARGCHK(d != NULL); return mpi_to_ltc_error(mp_exptmod(AUTO_CAST(a), AUTO_CAST(b), AUTO_CAST(c), AUTO_CAST(d))); } static int isprime(void *a, int *b) { int err; LTC_ARGCHK(a != NULL); LTC_ARGCHK(b != NULL); err = mpi_to_ltc_error(mp_prime_is_prime(AUTO_CAST(a), 8, AUTO_CAST(b))); *b = (*b == MP_YES) ? LTC_MP_YES : LTC_MP_NO; return err; } const ltc_math_descriptor ltm_desc = { "LibTomMath", (int)DIGIT_BIT, &init, &init_copy, &deinit, &neg, ©, &set_int, &get_int, &get_digit, &get_digit_count, &compare, &compare_d, &count_bits, &count_lsb_bits, &twoexpt, &read_radix, &write_radix, &unsigned_size, &unsigned_write, &unsigned_read, &add, &addi, &sub, &subi, &mul, &muli, &sqr, ÷, &div_2, &modi, &gcd, &lcm, &mulmod, &sqrmod, &invmod, &montgomery_setup, &montgomery_normalization, &montgomery_reduce, &montgomery_deinit, &exptmod, &isprime, #ifdef LTC_MECC #ifdef LTC_MECC_FP <c_ecc_fp_mulmod, #else <c_ecc_mulmod, #endif <c_ecc_projective_add_point, <c_ecc_projective_dbl_point, <c_ecc_map, #ifdef LTC_ECC_SHAMIR #ifdef LTC_MECC_FP <c_ecc_fp_mul2add, #else <c_ecc_mul2add, #endif /* LTC_MECC_FP */ #else NULL, #endif /* LTC_ECC_SHAMIR */ #else NULL, NULL,NULL, NULL, NULL, #endif /* LTC_MECC */ #ifdef LTC_MRSA &rsa_make_key, &rsa_exptmod, #else NULL, NULL #endif }; #define mp_init(a) ltc_mp.init(a) #define mp_init_multi ltc_init_multi #define mp_clear(a) ltc_mp.deinit(a) #define mp_clear_multi ltc_deinit_multi #define mp_init_copy(a, b) ltc_mp.init_copy(a, b) #define mp_neg(a, b) ltc_mp.neg(a, b) #define mp_copy(a, b) ltc_mp.copy(a, b) #define mp_set(a, b) ltc_mp.set_int(a, b) #define mp_set_int(a, b) ltc_mp.set_int(a, b) #define mp_get_int(a) ltc_mp.get_int(a) #define mp_get_digit(a, n) ltc_mp.get_digit(a, n) #define mp_get_digit_count(a) ltc_mp.get_digit_count(a) #define mp_cmp(a, b) ltc_mp.compare(a, b) #define mp_cmp_d(a, b) ltc_mp.compare_d(a, b) #define mp_count_bits(a) ltc_mp.count_bits(a) #define mp_cnt_lsb(a) ltc_mp.count_lsb_bits(a) #define mp_2expt(a, b) ltc_mp.twoexpt(a, b) #define mp_read_radix(a, b, c) ltc_mp.read_radix(a, b, c) #define mp_toradix(a, b, c) ltc_mp.write_radix(a, b, c) #define mp_unsigned_bin_size(a) ltc_mp.unsigned_size(a) #define mp_to_unsigned_bin(a, b) ltc_mp.unsigned_write(a, b) #define mp_read_unsigned_bin(a, b, c) ltc_mp.unsigned_read(a, b, c) #define mp_add(a, b, c) ltc_mp.add(a, b, c) #define mp_add_d(a, b, c) ltc_mp.addi(a, b, c) #define mp_sub(a, b, c) ltc_mp.sub(a, b, c) #define mp_sub_d(a, b, c) ltc_mp.subi(a, b, c) #define mp_mul(a, b, c) ltc_mp.mul(a, b, c) #define mp_mul_d(a, b, c) ltc_mp.muli(a, b, c) #define mp_sqr(a, b) ltc_mp.sqr(a, b) #define mp_div(a, b, c, d) ltc_mp.mpdiv(a, b, c, d) #define mp_div_2(a, b) ltc_mp.div_2(a, b) #define mp_mod(a, b, c) ltc_mp.mpdiv(a, b, NULL, c) #define mp_mod_d(a, b, c) ltc_mp.modi(a, b, c) #define mp_gcd(a, b, c) ltc_mp.gcd(a, b, c) #define mp_lcm(a, b, c) ltc_mp.lcm(a, b, c) #define mp_mulmod(a, b, c, d) ltc_mp.mulmod(a, b, c, d) #define mp_sqrmod(a, b, c) ltc_mp.sqrmod(a, b, c) #define mp_invmod(a, b, c) ltc_mp.invmod(a, b, c) #define mp_montgomery_setup(a, b) ltc_mp.montgomery_setup(a, b) #define mp_montgomery_normalization(a, b) ltc_mp.montgomery_normalization(a, b) #define mp_montgomery_reduce(a, b, c) ltc_mp.montgomery_reduce(a, b, c) #define mp_montgomery_free(a) ltc_mp.montgomery_deinit(a) #define mp_exptmod(a, b, c, d) ltc_mp.exptmod(a, b, c, d) #define mp_prime_is_prime(a, b, c) ltc_mp.isprime(a, c) #define mp_iszero(a) (mp_cmp_d(a, 0) == LTC_MP_EQ ? LTC_MP_YES : LTC_MP_NO) #define mp_isodd(a) (mp_get_digit_count(a) > 0 ? (mp_get_digit(a, 0) & 1 ? LTC_MP_YES : LTC_MP_NO) : LTC_MP_NO) #define mp_exch(a, b) do { void *ABC__tmp = a; a = b; b = ABC__tmp; } while (0); #define mp_tohex(a, b) mp_toradix(a, b, 16) #endif /* $Source: /cvs/libtom/libtomcrypt/src/math/ltm_desc.c,v $ */ /* $Revision: 1.31 $ */ /* $Date: 2007/05/12 14:32:35 $ */ /* LibTomCrypt, modular cryptographic library -- Tom St Denis * * LibTomCrypt is a library that provides various cryptographic * algorithms in a highly modular and flexible manner. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tomstdenis@gmail.com, http://libtom.org */ #ifdef MPI #include int ltc_init_multi(void **a, ...) { void **cur = a; int np = 0; va_list args; va_start(args, a); while (cur != NULL) { if (mp_init(cur) != CRYPT_OK) { /* failed */ va_list clean_list; va_start(clean_list, a); cur = a; while (np--) { mp_clear(*cur); cur = va_arg(clean_list, void **); } va_end(clean_list); return CRYPT_MEM; } ++np; cur = va_arg(args, void **); } va_end(args); return CRYPT_OK; } void ltc_deinit_multi(void *a, ...) { void *cur = a; va_list args; va_start(args, a); while (cur != NULL) { mp_clear(cur); cur = va_arg(args, void *); } va_end(args); } #endif /* $Source: /cvs/libtom/libtomcrypt/src/math/multi.c,v $ */ /* $Revision: 1.6 $ */ /* $Date: 2006/12/28 01:27:23 $ */ /* LibTomCrypt, modular cryptographic library -- Tom St Denis * * LibTomCrypt is a library that provides various cryptographic * algorithms in a highly modular and flexible manner. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tomstdenis@gmail.com, http://libtom.org */ /** @file pkcs_1_i2osp.c Integer to Octet I2OSP, Tom St Denis */ #ifdef LTC_PKCS_1 /* always stores the same # of bytes, pads with leading zero bytes as required */ /** LTC_PKCS #1 Integer to binary @param n The integer to store @param modulus_len The length of the RSA modulus @param out [out] The destination for the integer @return CRYPT_OK if successful */ int pkcs_1_i2osp(void *n, unsigned long modulus_len, unsigned char *out) { unsigned long size; size = mp_unsigned_bin_size(n); if (size > modulus_len) { return CRYPT_BUFFER_OVERFLOW; } /* store it */ zeromem(out, modulus_len); return mp_to_unsigned_bin(n, out + (modulus_len - size)); } #endif /* LTC_PKCS_1 */ /* $Source: /cvs/libtom/libtomcrypt/src/pk/pkcs1/pkcs_1_i2osp.c,v $ */ /* $Revision: 1.7 $ */ /* $Date: 2007/05/12 14:32:35 $ */ /* LibTomCrypt, modular cryptographic library -- Tom St Denis * * LibTomCrypt is a library that provides various cryptographic * algorithms in a highly modular and flexible manner. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tomstdenis@gmail.com, http://libtom.org */ /** @file pkcs_1_mgf1.c The Mask Generation Function (MGF1) for LTC_PKCS #1, Tom St Denis */ #ifdef LTC_PKCS_1 /** Perform LTC_PKCS #1 MGF1 (internal) @param seed The seed for MGF1 @param seedlen The length of the seed @param hash_idx The index of the hash desired @param mask [out] The destination @param masklen The length of the mask desired @return CRYPT_OK if successful */ int pkcs_1_mgf1(int hash_idx, const unsigned char *seed, unsigned long seedlen, unsigned char *mask, unsigned long masklen) { unsigned long hLen, x; ulong32 counter; int err; hash_state *md; unsigned char *buf; LTC_ARGCHK(seed != NULL); LTC_ARGCHK(mask != NULL); /* ensure valid hash */ if ((err = hash_is_valid(hash_idx)) != CRYPT_OK) { return err; } /* get hash output size */ hLen = hash_descriptor[hash_idx].hashsize; /* allocate memory */ md = AUTO_CAST(XMALLOC(sizeof(hash_state))); buf = AUTO_CAST(XMALLOC(hLen)); if ((md == NULL) || (buf == NULL)) { if (md != NULL) { XFREE(md); } if (buf != NULL) { XFREE(buf); } return CRYPT_MEM; } /* start counter */ counter = 0; while (masklen > 0) { /* handle counter */ STORE32H(counter, buf); ++counter; /* get hash of seed || counter */ if ((err = hash_descriptor[hash_idx].init(md)) != CRYPT_OK) { goto LBL_ERR; } if ((err = hash_descriptor[hash_idx].process(md, seed, seedlen)) != CRYPT_OK) { goto LBL_ERR; } if ((err = hash_descriptor[hash_idx].process(md, buf, 4)) != CRYPT_OK) { goto LBL_ERR; } if ((err = hash_descriptor[hash_idx].done(md, buf)) != CRYPT_OK) { goto LBL_ERR; } /* store it */ for (x = 0; x < hLen && masklen > 0; x++, masklen--) { *mask++ = buf[x]; } } err = CRYPT_OK; LBL_ERR: #ifdef LTC_CLEAN_STACK zeromem(buf, hLen); zeromem(md, sizeof(hash_state)); #endif XFREE(buf); XFREE(md); return err; } #endif /* LTC_PKCS_1 */ /* $Source: /cvs/libtom/libtomcrypt/src/pk/pkcs1/pkcs_1_mgf1.c,v $ */ /* $Revision: 1.8 $ */ /* $Date: 2007/05/12 14:32:35 $ */ /* LibTomCrypt, modular cryptographic library -- Tom St Denis * * LibTomCrypt is a library that provides various cryptographic * algorithms in a highly modular and flexible manner. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tomstdenis@gmail.com, http://libtom.org */ /** @file pkcs_1_oaep_decode.c OAEP Padding for LTC_PKCS #1, Tom St Denis */ #ifdef LTC_PKCS_1 /** LTC_PKCS #1 v2.00 OAEP decode @param msg The encoded data to decode @param msglen The length of the encoded data (octets) @param lparam The session or system data (can be NULL) @param lparamlen The length of the lparam @param modulus_bitlen The bit length of the RSA modulus @param hash_idx The index of the hash desired @param out [out] Destination of decoding @param outlen [in/out] The max size and resulting size of the decoding @param res [out] Result of decoding, 1==valid, 0==invalid @return CRYPT_OK if successful (even if invalid) */ int pkcs_1_oaep_decode(const unsigned char *msg, unsigned long msglen, const unsigned char *lparam, unsigned long lparamlen, unsigned long modulus_bitlen, int hash_idx, unsigned char *out, unsigned long *outlen, int *res) { unsigned char *DB, *seed, *mask; unsigned long hLen, x, y, modulus_len; int err; LTC_ARGCHK(msg != NULL); LTC_ARGCHK(out != NULL); LTC_ARGCHK(outlen != NULL); LTC_ARGCHK(res != NULL); /* default to invalid packet */ *res = 0; /* test valid hash */ if ((err = hash_is_valid(hash_idx)) != CRYPT_OK) { return err; } hLen = hash_descriptor[hash_idx].hashsize; modulus_len = (modulus_bitlen >> 3) + (modulus_bitlen & 7 ? 1 : 0); /* test hash/message size */ if ((2 * hLen >= (modulus_len - 2)) || (msglen != modulus_len)) { return CRYPT_PK_INVALID_SIZE; } /* allocate ram for DB/mask/salt of size modulus_len */ DB = AUTO_CAST(XMALLOC(modulus_len)); mask = AUTO_CAST(XMALLOC(modulus_len)); seed = AUTO_CAST(XMALLOC(hLen)); if ((DB == NULL) || (mask == NULL) || (seed == NULL)) { if (DB != NULL) { XFREE(DB); } if (mask != NULL) { XFREE(mask); } if (seed != NULL) { XFREE(seed); } return CRYPT_MEM; } /* ok so it's now in the form 0x00 || maskedseed || maskedDB 1 || hLen || modulus_len - hLen - 1 */ /* must have leading 0x00 byte */ if (msg[0] != 0x00) { err = CRYPT_OK; goto LBL_ERR; } /* now read the masked seed */ x = 1; XMEMCPY(seed, msg + x, hLen); x += hLen; /* now read the masked DB */ XMEMCPY(DB, msg + x, modulus_len - hLen - 1); x += modulus_len - hLen - 1; /* compute MGF1 of maskedDB (hLen) */ if ((err = pkcs_1_mgf1(hash_idx, DB, modulus_len - hLen - 1, mask, hLen)) != CRYPT_OK) { goto LBL_ERR; } /* XOR against seed */ for (y = 0; y < hLen; y++) { seed[y] ^= mask[y]; } /* compute MGF1 of seed (k - hlen - 1) */ if ((err = pkcs_1_mgf1(hash_idx, seed, hLen, mask, modulus_len - hLen - 1)) != CRYPT_OK) { goto LBL_ERR; } /* xor against DB */ for (y = 0; y < (modulus_len - hLen - 1); y++) { DB[y] ^= mask[y]; } /* now DB == lhash || PS || 0x01 || M, PS == k - mlen - 2hlen - 2 zeroes */ /* compute lhash and store it in seed [reuse temps!] */ x = modulus_len; if (lparam != NULL) { if ((err = hash_memory(hash_idx, lparam, lparamlen, seed, &x)) != CRYPT_OK) { goto LBL_ERR; } } else { /* can't pass hash_memory a NULL so use DB with zero length */ if ((err = hash_memory(hash_idx, DB, 0, seed, &x)) != CRYPT_OK) { goto LBL_ERR; } } /* compare the lhash'es */ if (XMEMCMP(seed, DB, hLen) != 0) { err = CRYPT_OK; goto LBL_ERR; } /* now zeroes before a 0x01 */ for (x = hLen; x < (modulus_len - hLen - 1) && DB[x] == 0x00; x++) { /* step... */ } /* error out if wasn't 0x01 */ if ((x == (modulus_len - hLen - 1)) || (DB[x] != 0x01)) { err = CRYPT_INVALID_PACKET; goto LBL_ERR; } /* rest is the message (and skip 0x01) */ if ((modulus_len - hLen - 1 - ++x) > *outlen) { *outlen = modulus_len - hLen - 1 - x; err = CRYPT_BUFFER_OVERFLOW; goto LBL_ERR; } /* copy message */ *outlen = modulus_len - hLen - 1 - x; XMEMCPY(out, DB + x, modulus_len - hLen - 1 - x); x += modulus_len - hLen - 1; /* valid packet */ *res = 1; err = CRYPT_OK; LBL_ERR: #ifdef LTC_CLEAN_STACK zeromem(DB, modulus_len); zeromem(seed, hLen); zeromem(mask, modulus_len); #endif XFREE(seed); XFREE(mask); XFREE(DB); return err; } #endif /* LTC_PKCS_1 */ /* $Source: /cvs/libtom/libtomcrypt/src/pk/pkcs1/pkcs_1_oaep_decode.c,v $ */ /* $Revision: 1.13 $ */ /* $Date: 2007/05/12 14:32:35 $ */ /* LibTomCrypt, modular cryptographic library -- Tom St Denis * * LibTomCrypt is a library that provides various cryptographic * algorithms in a highly modular and flexible manner. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tomstdenis@gmail.com, http://libtom.org */ /** @file pkcs_1_oaep_encode.c OAEP Padding for LTC_PKCS #1, Tom St Denis */ #ifdef LTC_PKCS_1 /** LTC_PKCS #1 v2.00 OAEP encode @param msg The data to encode @param msglen The length of the data to encode (octets) @param lparam A session or system parameter (can be NULL) @param lparamlen The length of the lparam data @param modulus_bitlen The bit length of the RSA modulus @param prng An active PRNG state @param prng_idx The index of the PRNG desired @param hash_idx The index of the hash desired @param out [out] The destination for the encoded data @param outlen [in/out] The max size and resulting size of the encoded data @return CRYPT_OK if successful */ int pkcs_1_oaep_encode(const unsigned char *msg, unsigned long msglen, const unsigned char *lparam, unsigned long lparamlen, unsigned long modulus_bitlen, prng_state *prng, int prng_idx, int hash_idx, unsigned char *out, unsigned long *outlen) { unsigned char *DB, *seed, *mask; unsigned long hLen, x, y, modulus_len; int err; LTC_ARGCHK(msg != NULL); LTC_ARGCHK(out != NULL); LTC_ARGCHK(outlen != NULL); /* test valid hash */ if ((err = hash_is_valid(hash_idx)) != CRYPT_OK) { return err; } /* valid prng */ if ((err = prng_is_valid(prng_idx)) != CRYPT_OK) { return err; } hLen = hash_descriptor[hash_idx].hashsize; modulus_len = (modulus_bitlen >> 3) + (modulus_bitlen & 7 ? 1 : 0); /* test message size */ if ((2 * hLen >= (modulus_len - 2)) || (msglen > (modulus_len - 2 * hLen - 2))) { return CRYPT_PK_INVALID_SIZE; } /* allocate ram for DB/mask/salt of size modulus_len */ DB = AUTO_CAST(XMALLOC(modulus_len)); mask = AUTO_CAST(XMALLOC(modulus_len)); seed = AUTO_CAST(XMALLOC(hLen)); if ((DB == NULL) || (mask == NULL) || (seed == NULL)) { if (DB != NULL) { XFREE(DB); } if (mask != NULL) { XFREE(mask); } if (seed != NULL) { XFREE(seed); } return CRYPT_MEM; } /* get lhash */ /* DB == lhash || PS || 0x01 || M, PS == k - mlen - 2hlen - 2 zeroes */ x = modulus_len; if (lparam != NULL) { if ((err = hash_memory(hash_idx, lparam, lparamlen, DB, &x)) != CRYPT_OK) { goto LBL_ERR; } } else { /* can't pass hash_memory a NULL so use DB with zero length */ if ((err = hash_memory(hash_idx, DB, 0, DB, &x)) != CRYPT_OK) { goto LBL_ERR; } } /* append PS then 0x01 (to lhash) */ x = hLen; y = modulus_len - msglen - 2 * hLen - 2; XMEMSET(DB + x, 0, y); x += y; /* 0x01 byte */ DB[x++] = 0x01; /* message (length = msglen) */ XMEMCPY(DB + x, msg, msglen); x += msglen; /* now choose a random seed */ if (prng_descriptor[prng_idx].read(seed, hLen, prng) != hLen) { err = CRYPT_ERROR_READPRNG; goto LBL_ERR; } /* compute MGF1 of seed (k - hlen - 1) */ if ((err = pkcs_1_mgf1(hash_idx, seed, hLen, mask, modulus_len - hLen - 1)) != CRYPT_OK) { goto LBL_ERR; } /* xor against DB */ for (y = 0; y < (modulus_len - hLen - 1); y++) { DB[y] ^= mask[y]; } /* compute MGF1 of maskedDB (hLen) */ if ((err = pkcs_1_mgf1(hash_idx, DB, modulus_len - hLen - 1, mask, hLen)) != CRYPT_OK) { goto LBL_ERR; } /* XOR against seed */ for (y = 0; y < hLen; y++) { seed[y] ^= mask[y]; } /* create string of length modulus_len */ if (*outlen < modulus_len) { *outlen = modulus_len; err = CRYPT_BUFFER_OVERFLOW; goto LBL_ERR; } /* start output which is 0x00 || maskedSeed || maskedDB */ x = 0; out[x++] = 0x00; XMEMCPY(out + x, seed, hLen); x += hLen; XMEMCPY(out + x, DB, modulus_len - hLen - 1); x += modulus_len - hLen - 1; *outlen = x; err = CRYPT_OK; LBL_ERR: #ifdef LTC_CLEAN_STACK zeromem(DB, modulus_len); zeromem(seed, hLen); zeromem(mask, modulus_len); #endif XFREE(seed); XFREE(mask); XFREE(DB); return err; } #endif /* LTC_PKCS_1 */ /* $Source: /cvs/libtom/libtomcrypt/src/pk/pkcs1/pkcs_1_oaep_encode.c,v $ */ /* $Revision: 1.9 $ */ /* $Date: 2007/05/12 14:32:35 $ */ /* LibTomCrypt, modular cryptographic library -- Tom St Denis * * LibTomCrypt is a library that provides various cryptographic * algorithms in a highly modular and flexible manner. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tomstdenis@gmail.com, http://libtom.org */ /** @file pkcs_1_os2ip.c Octet to Integer OS2IP, Tom St Denis */ #ifdef LTC_PKCS_1 /** Read a binary string into an mp_int @param n [out] The mp_int destination @param in The binary string to read @param inlen The length of the binary string @return CRYPT_OK if successful */ int pkcs_1_os2ip(void *n, unsigned char *in, unsigned long inlen) { return mp_read_unsigned_bin(n, in, inlen); } #endif /* LTC_PKCS_1 */ /* $Source: /cvs/libtom/libtomcrypt/src/pk/pkcs1/pkcs_1_os2ip.c,v $ */ /* $Revision: 1.7 $ */ /* $Date: 2007/05/12 14:32:35 $ */ /* LibTomCrypt, modular cryptographic library -- Tom St Denis * * LibTomCrypt is a library that provides various cryptographic * algorithms in a highly modular and flexible manner. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tomstdenis@gmail.com, http://libtom.org */ /** @file pkcs_1_pss_decode.c LTC_PKCS #1 PSS Signature Padding, Tom St Denis */ #ifdef LTC_PKCS_1 /** LTC_PKCS #1 v2.00 PSS decode @param msghash The hash to verify @param msghashlen The length of the hash (octets) @param sig The signature data (encoded data) @param siglen The length of the signature data (octets) @param saltlen The length of the salt used (octets) @param hash_idx The index of the hash desired @param modulus_bitlen The bit length of the RSA modulus @param res [out] The result of the comparison, 1==valid, 0==invalid @return CRYPT_OK if successful (even if the comparison failed) */ int pkcs_1_pss_decode(const unsigned char *msghash, unsigned long msghashlen, const unsigned char *sig, unsigned long siglen, unsigned long saltlen, int hash_idx, unsigned long modulus_bitlen, int *res) { unsigned char *DB, *mask, *salt, *hash; unsigned long x, y, hLen, modulus_len; int err; hash_state md; LTC_ARGCHK(msghash != NULL); LTC_ARGCHK(res != NULL); /* default to invalid */ *res = 0; /* ensure hash is valid */ if ((err = hash_is_valid(hash_idx)) != CRYPT_OK) { return err; } hLen = hash_descriptor[hash_idx].hashsize; modulus_len = (modulus_bitlen >> 3) + (modulus_bitlen & 7 ? 1 : 0); /* check sizes */ if ((saltlen > modulus_len) || (modulus_len < hLen + saltlen + 2) || (siglen != modulus_len)) { return CRYPT_PK_INVALID_SIZE; } /* allocate ram for DB/mask/salt/hash of size modulus_len */ DB = AUTO_CAST(XMALLOC(modulus_len)); mask = AUTO_CAST(XMALLOC(modulus_len)); salt = AUTO_CAST(XMALLOC(modulus_len)); hash = AUTO_CAST(XMALLOC(modulus_len)); if ((DB == NULL) || (mask == NULL) || (salt == NULL) || (hash == NULL)) { if (DB != NULL) { XFREE(DB); } if (mask != NULL) { XFREE(mask); } if (salt != NULL) { XFREE(salt); } if (hash != NULL) { XFREE(hash); } return CRYPT_MEM; } /* ensure the 0xBC byte */ if (sig[siglen - 1] != 0xBC) { err = CRYPT_INVALID_PACKET; goto LBL_ERR; } /* copy out the DB */ x = 0; XMEMCPY(DB, sig + x, modulus_len - hLen - 1); x += modulus_len - hLen - 1; /* copy out the hash */ XMEMCPY(hash, sig + x, hLen); x += hLen; /* check the MSB */ if ((sig[0] & ~(0xFF >> ((modulus_len << 3) - (modulus_bitlen - 1)))) != 0) { err = CRYPT_INVALID_PACKET; goto LBL_ERR; } /* generate mask of length modulus_len - hLen - 1 from hash */ if ((err = pkcs_1_mgf1(hash_idx, hash, hLen, mask, modulus_len - hLen - 1)) != CRYPT_OK) { goto LBL_ERR; } /* xor against DB */ for (y = 0; y < (modulus_len - hLen - 1); y++) { DB[y] ^= mask[y]; } /* now clear the first byte [make sure smaller than modulus] */ DB[0] &= 0xFF >> ((modulus_len << 3) - (modulus_bitlen - 1)); /* DB = PS || 0x01 || salt, PS == modulus_len - saltlen - hLen - 2 zero bytes */ /* check for zeroes and 0x01 */ for (x = 0; x < modulus_len - saltlen - hLen - 2; x++) { if (DB[x] != 0x00) { err = CRYPT_INVALID_PACKET; goto LBL_ERR; } } /* check for the 0x01 */ if (DB[x++] != 0x01) { err = CRYPT_INVALID_PACKET; goto LBL_ERR; } /* M = (eight) 0x00 || msghash || salt, mask = H(M) */ if ((err = hash_descriptor[hash_idx].init(&md)) != CRYPT_OK) { goto LBL_ERR; } zeromem(mask, 8); if ((err = hash_descriptor[hash_idx].process(&md, mask, 8)) != CRYPT_OK) { goto LBL_ERR; } if ((err = hash_descriptor[hash_idx].process(&md, msghash, msghashlen)) != CRYPT_OK) { goto LBL_ERR; } if ((err = hash_descriptor[hash_idx].process(&md, DB + x, saltlen)) != CRYPT_OK) { goto LBL_ERR; } if ((err = hash_descriptor[hash_idx].done(&md, mask)) != CRYPT_OK) { goto LBL_ERR; } /* mask == hash means valid signature */ if (XMEMCMP(mask, hash, hLen) == 0) { *res = 1; } err = CRYPT_OK; LBL_ERR: #ifdef LTC_CLEAN_STACK zeromem(DB, modulus_len); zeromem(mask, modulus_len); zeromem(salt, modulus_len); zeromem(hash, modulus_len); #endif XFREE(hash); XFREE(salt); XFREE(mask); XFREE(DB); return err; } #endif /* LTC_PKCS_1 */ /* $Source: /cvs/libtom/libtomcrypt/src/pk/pkcs1/pkcs_1_pss_decode.c,v $ */ /* $Revision: 1.11 $ */ /* $Date: 2007/05/12 14:32:35 $ */ /* LibTomCrypt, modular cryptographic library -- Tom St Denis * * LibTomCrypt is a library that provides various cryptographic * algorithms in a highly modular and flexible manner. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tomstdenis@gmail.com, http://libtom.org */ /** @file pkcs_1_pss_encode.c LTC_PKCS #1 PSS Signature Padding, Tom St Denis */ #ifdef LTC_PKCS_1 /** LTC_PKCS #1 v2.00 Signature Encoding @param msghash The hash to encode @param msghashlen The length of the hash (octets) @param saltlen The length of the salt desired (octets) @param prng An active PRNG context @param prng_idx The index of the PRNG desired @param hash_idx The index of the hash desired @param modulus_bitlen The bit length of the RSA modulus @param out [out] The destination of the encoding @param outlen [in/out] The max size and resulting size of the encoded data @return CRYPT_OK if successful */ int pkcs_1_pss_encode(const unsigned char *msghash, unsigned long msghashlen, unsigned long saltlen, prng_state *prng, int prng_idx, int hash_idx, unsigned long modulus_bitlen, unsigned char *out, unsigned long *outlen) { unsigned char *DB, *mask, *salt, *hash; unsigned long x, y, hLen, modulus_len; int err; hash_state md; LTC_ARGCHK(msghash != NULL); LTC_ARGCHK(out != NULL); LTC_ARGCHK(outlen != NULL); /* ensure hash and PRNG are valid */ if ((err = hash_is_valid(hash_idx)) != CRYPT_OK) { return err; } if ((err = prng_is_valid(prng_idx)) != CRYPT_OK) { return err; } hLen = hash_descriptor[hash_idx].hashsize; modulus_len = (modulus_bitlen >> 3) + (modulus_bitlen & 7 ? 1 : 0); /* check sizes */ if ((saltlen > modulus_len) || (modulus_len < hLen + saltlen + 2)) { return CRYPT_PK_INVALID_SIZE; } /* allocate ram for DB/mask/salt/hash of size modulus_len */ DB = AUTO_CAST(XMALLOC(modulus_len)); mask = AUTO_CAST(XMALLOC(modulus_len)); salt = AUTO_CAST(XMALLOC(modulus_len)); hash = AUTO_CAST(XMALLOC(modulus_len)); if ((DB == NULL) || (mask == NULL) || (salt == NULL) || (hash == NULL)) { if (DB != NULL) { XFREE(DB); } if (mask != NULL) { XFREE(mask); } if (salt != NULL) { XFREE(salt); } if (hash != NULL) { XFREE(hash); } return CRYPT_MEM; } /* generate random salt */ if (saltlen > 0) { if (prng_descriptor[prng_idx].read(salt, saltlen, prng) != saltlen) { err = CRYPT_ERROR_READPRNG; goto LBL_ERR; } } /* M = (eight) 0x00 || msghash || salt, hash = H(M) */ if ((err = hash_descriptor[hash_idx].init(&md)) != CRYPT_OK) { goto LBL_ERR; } zeromem(DB, 8); if ((err = hash_descriptor[hash_idx].process(&md, DB, 8)) != CRYPT_OK) { goto LBL_ERR; } if ((err = hash_descriptor[hash_idx].process(&md, msghash, msghashlen)) != CRYPT_OK) { goto LBL_ERR; } if ((err = hash_descriptor[hash_idx].process(&md, salt, saltlen)) != CRYPT_OK) { goto LBL_ERR; } if ((err = hash_descriptor[hash_idx].done(&md, hash)) != CRYPT_OK) { goto LBL_ERR; } /* generate DB = PS || 0x01 || salt, PS == modulus_len - saltlen - hLen - 2 zero bytes */ x = 0; XMEMSET(DB + x, 0, modulus_len - saltlen - hLen - 2); x += modulus_len - saltlen - hLen - 2; DB[x++] = 0x01; XMEMCPY(DB + x, salt, saltlen); x += saltlen; /* generate mask of length modulus_len - hLen - 1 from hash */ if ((err = pkcs_1_mgf1(hash_idx, hash, hLen, mask, modulus_len - hLen - 1)) != CRYPT_OK) { goto LBL_ERR; } /* xor against DB */ for (y = 0; y < (modulus_len - hLen - 1); y++) { DB[y] ^= mask[y]; } /* output is DB || hash || 0xBC */ if (*outlen < modulus_len) { *outlen = modulus_len; err = CRYPT_BUFFER_OVERFLOW; goto LBL_ERR; } /* DB len = modulus_len - hLen - 1 */ y = 0; XMEMCPY(out + y, DB, modulus_len - hLen - 1); y += modulus_len - hLen - 1; /* hash */ XMEMCPY(out + y, hash, hLen); y += hLen; /* 0xBC */ out[y] = 0xBC; /* now clear the 8*modulus_len - modulus_bitlen most significant bits */ out[0] &= 0xFF >> ((modulus_len << 3) - (modulus_bitlen - 1)); /* store output size */ *outlen = modulus_len; err = CRYPT_OK; LBL_ERR: #ifdef LTC_CLEAN_STACK zeromem(DB, modulus_len); zeromem(mask, modulus_len); zeromem(salt, modulus_len); zeromem(hash, modulus_len); #endif XFREE(hash); XFREE(salt); XFREE(mask); XFREE(DB); return err; } #endif /* LTC_PKCS_1 */ /* $Source: /cvs/libtom/libtomcrypt/src/pk/pkcs1/pkcs_1_pss_encode.c,v $ */ /* $Revision: 1.9 $ */ /* $Date: 2007/05/12 14:32:35 $ */ /* LibTomCrypt, modular cryptographic library -- Tom St Denis * * LibTomCrypt is a library that provides various cryptographic * algorithms in a highly modular and flexible manner. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tomstdenis@gmail.com, http://libtom.org */ /** @file pkcs_1_v1_5_decode.c * * LTC_PKCS #1 v1.5 Padding. (Andreas Lange) */ #ifdef LTC_PKCS_1 /** @brief LTC_PKCS #1 v1.5 decode. * * @param msg The encoded data to decode * @param msglen The length of the encoded data (octets) * @param block_type Block type to use in padding (\sa ltc_pkcs_1_v1_5_blocks) * @param modulus_bitlen The bit length of the RSA modulus * @param out [out] Destination of decoding * @param outlen [in/out] The max size and resulting size of the decoding * @param is_valid [out] Boolean whether the padding was valid * * @return CRYPT_OK if successful (even if invalid) */ int pkcs_1_v1_5_decode(const unsigned char *msg, unsigned long msglen, int block_type, unsigned long modulus_bitlen, unsigned char *out, unsigned long *outlen, int *is_valid) { unsigned long modulus_len, ps_len, i; int result; /* default to invalid packet */ *is_valid = 0; modulus_len = (modulus_bitlen >> 3) + (modulus_bitlen & 7 ? 1 : 0); /* test message size */ if ((msglen > modulus_len) || (modulus_len < 11)) { return CRYPT_PK_INVALID_SIZE; } /* separate encoded message */ if ((msg[0] != 0x00) || (msg[1] != (unsigned char)block_type)) { result = CRYPT_INVALID_PACKET; goto bail; } if (block_type == LTC_LTC_PKCS_1_EME) { for (i = 2; i < modulus_len; i++) { /* separator */ if (msg[i] == 0x00) { break; } } ps_len = i++ - 2; if ((i >= modulus_len) || (ps_len < 8)) { /* There was no octet with hexadecimal value 0x00 to separate ps from m, * or the length of ps is less than 8 octets. */ result = CRYPT_INVALID_PACKET; goto bail; } } else { for (i = 2; i < modulus_len - 1; i++) { if (msg[i] != 0xFF) { break; } } /* separator check */ if (msg[i] != 0) { /* There was no octet with hexadecimal value 0x00 to separate ps from m. */ result = CRYPT_INVALID_PACKET; goto bail; } ps_len = i - 2; } if (*outlen < (msglen - (2 + ps_len + 1))) { *outlen = msglen - (2 + ps_len + 1); result = CRYPT_BUFFER_OVERFLOW; goto bail; } *outlen = (msglen - (2 + ps_len + 1)); XMEMCPY(out, &msg[2 + ps_len + 1], *outlen); /* valid packet */ *is_valid = 1; result = CRYPT_OK; bail: return result; } /* pkcs_1_v1_5_decode */ #endif /* #ifdef LTC_PKCS_1 */ /* $Source: /cvs/libtom/libtomcrypt/src/pk/pkcs1/pkcs_1_v1_5_decode.c,v $ */ /* $Revision: 1.7 $ */ /* $Date: 2007/05/12 14:32:35 $ */ /* LibTomCrypt, modular cryptographic library -- Tom St Denis * * LibTomCrypt is a library that provides various cryptographic * algorithms in a highly modular and flexible manner. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tomstdenis@gmail.com, http://libtom.org */ /*! \file pkcs_1_v1_5_encode.c * * LTC_PKCS #1 v1.5 Padding (Andreas Lange) */ #ifdef LTC_PKCS_1 /*! \brief LTC_PKCS #1 v1.5 encode. * * \param msg The data to encode * \param msglen The length of the data to encode (octets) * \param block_type Block type to use in padding (\sa ltc_pkcs_1_v1_5_blocks) * \param modulus_bitlen The bit length of the RSA modulus * \param prng An active PRNG state (only for LTC_LTC_PKCS_1_EME) * \param prng_idx The index of the PRNG desired (only for LTC_LTC_PKCS_1_EME) * \param out [out] The destination for the encoded data * \param outlen [in/out] The max size and resulting size of the encoded data * * \return CRYPT_OK if successful */ int pkcs_1_v1_5_encode(const unsigned char *msg, unsigned long msglen, int block_type, unsigned long modulus_bitlen, prng_state *prng, int prng_idx, unsigned char *out, unsigned long *outlen) { unsigned long modulus_len, ps_len, i; unsigned char *ps; int result; /* valid block_type? */ if ((block_type != LTC_LTC_PKCS_1_EMSA) && (block_type != LTC_LTC_PKCS_1_EME)) { return CRYPT_PK_INVALID_PADDING; } if (block_type == LTC_LTC_PKCS_1_EME) { /* encryption padding, we need a valid PRNG */ if ((result = prng_is_valid(prng_idx)) != CRYPT_OK) { return result; } } modulus_len = (modulus_bitlen >> 3) + (modulus_bitlen & 7 ? 1 : 0); /* test message size */ if ((msglen + 11) > modulus_len) { return CRYPT_PK_INVALID_SIZE; } if (*outlen < modulus_len) { *outlen = modulus_len; result = CRYPT_BUFFER_OVERFLOW; goto bail; } /* generate an octets string PS */ ps = &out[2]; ps_len = modulus_len - msglen - 3; if (block_type == LTC_LTC_PKCS_1_EME) { /* now choose a random ps */ if (prng_descriptor[prng_idx].read(ps, ps_len, prng) != ps_len) { result = CRYPT_ERROR_READPRNG; goto bail; } /* transform zero bytes (if any) to non-zero random bytes */ for (i = 0; i < ps_len; i++) { while (ps[i] == 0) { if (prng_descriptor[prng_idx].read(&ps[i], 1, prng) != 1) { result = CRYPT_ERROR_READPRNG; goto bail; } } } } else { XMEMSET(ps, 0xFF, ps_len); } /* create string of length modulus_len */ out[0] = 0x00; out[1] = (unsigned char)block_type;/* block_type 1 or 2 */ out[2 + ps_len] = 0x00; XMEMCPY(&out[2 + ps_len + 1], msg, msglen); *outlen = modulus_len; result = CRYPT_OK; bail: return result; } /* pkcs_1_v1_5_encode */ #endif /* #ifdef LTC_PKCS_1 */ /* $Source: /cvs/libtom/libtomcrypt/src/pk/pkcs1/pkcs_1_v1_5_encode.c,v $ */ /* $Revision: 1.4 $ */ /* $Date: 2007/05/12 14:32:35 $ */ /* LibTomCrypt, modular cryptographic library -- Tom St Denis * * LibTomCrypt is a library that provides various cryptographic * algorithms in a highly modular and flexible manner. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tomstdenis@gmail.com, http://libtom.org */ /** @file rand_prime.c Generate a random prime, Tom St Denis */ #define USE_BBS 1 int rand_prime(void *N, long len, prng_state *prng, int wprng) { int err, res, type; unsigned char *buf; LTC_ARGCHK(N != NULL); /* get type */ if (len < 0) { type = USE_BBS; len = -len; } else { type = 0; } /* allow sizes between 2 and 512 bytes for a prime size */ if ((len < 2) || (len > 512)) { return CRYPT_INVALID_PRIME_SIZE; } /* valid PRNG? Better be! */ if ((err = prng_is_valid(wprng)) != CRYPT_OK) { return err; } /* allocate buffer to work with */ buf = AUTO_CAST(XCALLOC(1, len)); if (buf == NULL) { return CRYPT_MEM; } do { /* generate value */ if (prng_descriptor[wprng].read(buf, len, prng) != (unsigned long)len) { XFREE(buf); return CRYPT_ERROR_READPRNG; } /* munge bits */ buf[0] |= 0x80 | 0x40; buf[len - 1] |= 0x01 | ((type & USE_BBS) ? 0x02 : 0x00); /* load value */ if ((err = mp_read_unsigned_bin(N, buf, len)) != CRYPT_OK) { XFREE(buf); return err; } /* test */ if ((err = mp_prime_is_prime(N, 8, &res)) != CRYPT_OK) { XFREE(buf); return err; } } while (res == LTC_MP_NO); #ifdef LTC_CLEAN_STACK zeromem(buf, len); #endif XFREE(buf); return CRYPT_OK; } /* $Source: /cvs/libtom/libtomcrypt/src/math/rand_prime.c,v $ */ /* $Revision: 1.7 $ */ /* $Date: 2006/12/28 01:27:23 $ */ /* LibTomCrypt, modular cryptographic library -- Tom St Denis * * LibTomCrypt is a library that provides various cryptographic * algorithms in a highly modular and flexible manner. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tomstdenis@gmail.com, http://libtom.org */ /** @file rng_get_bytes.c portable way to get secure random bits to feed a PRNG (Tom St Denis) */ #ifdef LTC_DEVRANDOM /* on *NIX read /dev/random */ static unsigned long rng_nix(unsigned char *buf, unsigned long len, void (*callback)(void)) { #ifdef LTC_NO_FILE return 0; #else FILE *f; unsigned long x; #ifdef TRY_URANDOM_FIRST f = fopen("/dev/urandom", "rb"); if (f == NULL) #endif /* TRY_URANDOM_FIRST */ f = fopen("/dev/random", "rb"); if (f == NULL) { return 0; } /* disable buffering */ if (setvbuf(f, NULL, _IONBF, 0) != 0) { fclose(f); return 0; } x = (unsigned long)fread(buf, 1, (size_t)len, f); fclose(f); return x; #endif /* LTC_NO_FILE */ } #endif /* LTC_DEVRANDOM */ /* on ANSI C platforms with 100 < CLOCKS_PER_SEC < 10000 */ #if defined(CLOCKS_PER_SEC) && !defined(WINCE) #define ANSI_RNG static unsigned long rng_ansic(unsigned char *buf, unsigned long len, void (*callback)(void)) { clock_t t1; int l, acc, bits, a, b; if ((XCLOCKS_PER_SEC < 100) || (XCLOCKS_PER_SEC > 10000)) { return 0; } l = len; bits = 8; acc = a = b = 0; while (len--) { if (callback != NULL) callback(); while (bits--) { do { t1 = XCLOCK(); while (t1 == XCLOCK()) a ^= 1; t1 = XCLOCK(); while (t1 == XCLOCK()) b ^= 1; } while (a == b); acc = (acc << 1) | a; } *buf++ = acc; acc = 0; bits = 8; } acc = bits = a = b = 0; return l; } #endif /* Try the Microsoft CSP */ #if defined(WIN32) || defined(WINCE) #if !defined( _WIN32_WINNT ) || _WIN32_WINNT < 0x0501 #undef _WIN32_WINNT #define _WIN32_WINNT 0x0501 // requires Windows XP minimum #endif #ifdef WINCE #define UNDER_CE #define ARM #endif #include #include static unsigned long rng_win32(unsigned char *buf, unsigned long len, void (*callback)(void)) { HCRYPTPROV hProv = 0; if (!CryptAcquireContext(&hProv, NULL, MS_DEF_PROV, PROV_RSA_FULL, (CRYPT_VERIFYCONTEXT | CRYPT_MACHINE_KEYSET)) && !CryptAcquireContext(&hProv, NULL, MS_DEF_PROV, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT | CRYPT_MACHINE_KEYSET | CRYPT_NEWKEYSET)) return 0; if (CryptGenRandom(hProv, len, buf) == TRUE) { CryptReleaseContext(hProv, 0); return len; } else { CryptReleaseContext(hProv, 0); return 0; } } #endif /* WIN32 */ /** Read the system RNG @param out Destination @param outlen Length desired (octets) @param callback Pointer to void function to act as "callback" when RNG is slow. This can be NULL @return Number of octets read */ unsigned long rng_get_bytes(unsigned char *out, unsigned long outlen, void (*callback)(void)) { unsigned long x; LTC_ARGCHK(out != NULL); #if defined(LTC_DEVRANDOM) x = rng_nix(out, outlen, callback); if (x != 0) { return x; } #endif #ifdef WIN32 x = rng_win32(out, outlen, callback); if (x != 0) { return x; } #endif #ifdef ANSI_RNG x = rng_ansic(out, outlen, callback); if (x != 0) { return x; } #endif return 0; } /* $Source: /cvs/libtom/libtomcrypt/src/prngs/rng_get_bytes.c,v $ */ /* $Revision: 1.7 $ */ /* $Date: 2007/05/12 14:32:35 $ */ /* LibTomCrypt, modular cryptographic library -- Tom St Denis * * LibTomCrypt is a library that provides various cryptographic * algorithms in a highly modular and flexible manner. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tomstdenis@gmail.com, http://libtom.org */ /** @file rng_make_prng.c portable way to get secure random bits to feed a PRNG (Tom St Denis) */ /** Create a PRNG from a RNG @param bits Number of bits of entropy desired (64 ... 1024) @param wprng Index of which PRNG to setup @param prng [out] PRNG state to initialize @param callback A pointer to a void function for when the RNG is slow, this can be NULL @return CRYPT_OK if successful */ int rng_make_prng(int bits, int wprng, prng_state *prng, void (*callback)(void)) { unsigned char buf[256]; int err; LTC_ARGCHK(prng != NULL); /* check parameter */ if ((err = prng_is_valid(wprng)) != CRYPT_OK) { return err; } if ((bits < 64) || (bits > 1024)) { return CRYPT_INVALID_PRNGSIZE; } if ((err = prng_descriptor[wprng].start(prng)) != CRYPT_OK) { return err; } bits = ((bits / 8) + ((bits & 7) != 0 ? 1 : 0)) * 2; if (rng_get_bytes(buf, (unsigned long)bits, callback) != (unsigned long)bits) { return CRYPT_ERROR_READPRNG; } if ((err = prng_descriptor[wprng].add_entropy(buf, (unsigned long)bits, prng)) != CRYPT_OK) { return err; } if ((err = prng_descriptor[wprng].ready(prng)) != CRYPT_OK) { return err; } #ifdef LTC_CLEAN_STACK zeromem(buf, sizeof(buf)); #endif return CRYPT_OK; } /* $Source: /cvs/libtom/libtomcrypt/src/prngs/rng_make_prng.c,v $ */ /* $Revision: 1.5 $ */ /* $Date: 2006/12/28 01:27:24 $ */ /* LibTomCrypt, modular cryptographic library -- Tom St Denis * * LibTomCrypt is a library that provides various cryptographic * algorithms in a highly modular and flexible manner. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tomstdenis@gmail.com, http://libtom.org */ /** @file rsa_decrypt_key.c RSA LTC_PKCS #1 Decryption, Tom St Denis and Andreas Lange */ #ifdef LTC_MRSA /** LTC_PKCS #1 decrypt then v1.5 or OAEP depad @param in The ciphertext @param inlen The length of the ciphertext (octets) @param out [out] The plaintext @param outlen [in/out] The max size and resulting size of the plaintext (octets) @param lparam The system "lparam" value @param lparamlen The length of the lparam value (octets) @param hash_idx The index of the hash desired @param padding Type of padding (LTC_LTC_PKCS_1_OAEP or LTC_LTC_PKCS_1_V1_5) @param stat [out] Result of the decryption, 1==valid, 0==invalid @param key The corresponding private RSA key @return CRYPT_OK if succcessul (even if invalid) */ int rsa_decrypt_key_ex(const unsigned char *in, unsigned long inlen, unsigned char *out, unsigned long *outlen, const unsigned char *lparam, unsigned long lparamlen, int hash_idx, int padding, int *stat, rsa_key *key) { unsigned long modulus_bitlen, modulus_bytelen, x; int err; unsigned char *tmp; LTC_ARGCHK(out != NULL); LTC_ARGCHK(outlen != NULL); LTC_ARGCHK(key != NULL); LTC_ARGCHK(stat != NULL); /* default to invalid */ *stat = 0; /* valid padding? */ if ((padding != LTC_LTC_PKCS_1_V1_5) && (padding != LTC_LTC_PKCS_1_OAEP)) { return CRYPT_PK_INVALID_PADDING; } if (padding == LTC_LTC_PKCS_1_OAEP) { /* valid hash ? */ if ((err = hash_is_valid(hash_idx)) != CRYPT_OK) { return err; } } /* get modulus len in bits */ modulus_bitlen = mp_count_bits((key->N)); /* outlen must be at least the size of the modulus */ modulus_bytelen = mp_unsigned_bin_size((key->N)); if (modulus_bytelen != inlen) { return CRYPT_INVALID_PACKET; } /* allocate ram */ tmp = AUTO_CAST(XMALLOC(inlen)); if (tmp == NULL) { return CRYPT_MEM; } /* rsa decode the packet */ x = inlen; if ((err = ltc_mp.rsa_me(in, inlen, tmp, &x, PK_PRIVATE, key)) != CRYPT_OK) { XFREE(tmp); return err; } if (padding == LTC_LTC_PKCS_1_OAEP) { /* now OAEP decode the packet */ err = pkcs_1_oaep_decode(tmp, x, lparam, lparamlen, modulus_bitlen, hash_idx, out, outlen, stat); } else { /* now LTC_PKCS #1 v1.5 depad the packet */ err = pkcs_1_v1_5_decode(tmp, x, LTC_LTC_PKCS_1_EME, modulus_bitlen, out, outlen, stat); } XFREE(tmp); return err; } #endif /* LTC_MRSA */ /* $Source: /cvs/libtom/libtomcrypt/src/pk/rsa/rsa_decrypt_key.c,v $ */ /* $Revision: 1.10 $ */ /* $Date: 2007/05/12 14:32:35 $ */ /* LibTomCrypt, modular cryptographic library -- Tom St Denis * * LibTomCrypt is a library that provides various cryptographic * algorithms in a highly modular and flexible manner. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tomstdenis@gmail.com, http://libtom.org */ /** @file rsa_encrypt_key.c RSA LTC_PKCS #1 encryption, Tom St Denis and Andreas Lange */ #ifdef LTC_MRSA /** (LTC_PKCS #1 v2.0) OAEP pad then encrypt @param in The plaintext @param inlen The length of the plaintext (octets) @param out [out] The ciphertext @param outlen [in/out] The max size and resulting size of the ciphertext @param lparam The system "lparam" for the encryption @param lparamlen The length of lparam (octets) @param prng An active PRNG @param prng_idx The index of the desired prng @param hash_idx The index of the desired hash @param padding Type of padding (LTC_LTC_PKCS_1_OAEP or LTC_LTC_PKCS_1_V1_5) @param key The RSA key to encrypt to @return CRYPT_OK if successful */ int rsa_encrypt_key_ex(const unsigned char *in, unsigned long inlen, unsigned char *out, unsigned long *outlen, const unsigned char *lparam, unsigned long lparamlen, prng_state *prng, int prng_idx, int hash_idx, int padding, rsa_key *key) { unsigned long modulus_bitlen, modulus_bytelen, x; int err; LTC_ARGCHK(in != NULL); LTC_ARGCHK(out != NULL); LTC_ARGCHK(outlen != NULL); LTC_ARGCHK(key != NULL); /* valid padding? */ if ((padding != LTC_LTC_PKCS_1_V1_5) && (padding != LTC_LTC_PKCS_1_OAEP)) { return CRYPT_PK_INVALID_PADDING; } /* valid prng? */ if ((err = prng_is_valid(prng_idx)) != CRYPT_OK) { return err; } if (padding == LTC_LTC_PKCS_1_OAEP) { /* valid hash? */ if ((err = hash_is_valid(hash_idx)) != CRYPT_OK) { return err; } } /* get modulus len in bits */ modulus_bitlen = mp_count_bits((key->N)); /* outlen must be at least the size of the modulus */ modulus_bytelen = mp_unsigned_bin_size((key->N)); if (modulus_bytelen > *outlen) { *outlen = modulus_bytelen; return CRYPT_BUFFER_OVERFLOW; } if (padding == LTC_LTC_PKCS_1_OAEP) { /* OAEP pad the key */ x = *outlen; if ((err = pkcs_1_oaep_encode(in, inlen, lparam, lparamlen, modulus_bitlen, prng, prng_idx, hash_idx, out, &x)) != CRYPT_OK) { return err; } } else { /* LTC_PKCS #1 v1.5 pad the key */ x = *outlen; if ((err = pkcs_1_v1_5_encode(in, inlen, LTC_LTC_PKCS_1_EME, modulus_bitlen, prng, prng_idx, out, &x)) != CRYPT_OK) { return err; } } /* rsa exptmod the OAEP or LTC_PKCS #1 v1.5 pad */ return ltc_mp.rsa_me(out, x, out, outlen, PK_PUBLIC, key); } #endif /* LTC_MRSA */ /* $Source: /cvs/libtom/libtomcrypt/src/pk/rsa/rsa_encrypt_key.c,v $ */ /* $Revision: 1.10 $ */ /* $Date: 2007/05/12 14:32:35 $ */ /* LibTomCrypt, modular cryptographic library -- Tom St Denis * * LibTomCrypt is a library that provides various cryptographic * algorithms in a highly modular and flexible manner. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tomstdenis@gmail.com, http://libtom.org */ /** @file rsa_exptmod.c RSA LTC_PKCS exptmod, Tom St Denis */ #ifdef LTC_MRSA /** Compute an RSA modular exponentiation @param in The input data to send into RSA @param inlen The length of the input (octets) @param out [out] The destination @param outlen [in/out] The max size and resulting size of the output @param which Which exponent to use, e.g. PK_PRIVATE or PK_PUBLIC @param key The RSA key to use @return CRYPT_OK if successful */ int rsa_exptmod(const unsigned char *in, unsigned long inlen, unsigned char *out, unsigned long *outlen, int which, rsa_key *key) { void *tmp, *tmpa, *tmpb; unsigned long x; int err; LTC_ARGCHK(in != NULL); LTC_ARGCHK(out != NULL); LTC_ARGCHK(outlen != NULL); LTC_ARGCHK(key != NULL); /* is the key of the right type for the operation? */ if ((which == PK_PRIVATE) && (key->type != PK_PRIVATE)) { return CRYPT_PK_NOT_PRIVATE; } /* must be a private or public operation */ if ((which != PK_PRIVATE) && (which != PK_PUBLIC)) { return CRYPT_PK_INVALID_TYPE; } /* init and copy into tmp */ if ((err = mp_init_multi(&tmp, &tmpa, &tmpb, NULL)) != CRYPT_OK) { return err; } if ((err = mp_read_unsigned_bin(tmp, (unsigned char *)in, (int)inlen)) != CRYPT_OK) { goto error; } /* sanity check on the input */ if (mp_cmp(key->N, tmp) == LTC_MP_LT) { err = CRYPT_PK_INVALID_SIZE; goto error; } /* are we using the private exponent and is the key optimized? */ if (which == PK_PRIVATE) { /* tmpa = tmp^dP mod p */ if ((err = mp_exptmod(tmp, key->dP, key->p, tmpa)) != CRYPT_OK) { goto error; } /* tmpb = tmp^dQ mod q */ if ((err = mp_exptmod(tmp, key->dQ, key->q, tmpb)) != CRYPT_OK) { goto error; } /* tmp = (tmpa - tmpb) * qInv (mod p) */ if ((err = mp_sub(tmpa, tmpb, tmp)) != CRYPT_OK) { goto error; } if ((err = mp_mulmod(tmp, key->qP, key->p, tmp)) != CRYPT_OK) { goto error; } /* tmp = tmpb + q * tmp */ if ((err = mp_mul(tmp, key->q, tmp)) != CRYPT_OK) { goto error; } if ((err = mp_add(tmp, tmpb, tmp)) != CRYPT_OK) { goto error; } } else { /* exptmod it */ if ((err = mp_exptmod(tmp, key->e, key->N, tmp)) != CRYPT_OK) { goto error; } } /* read it back */ x = (unsigned long)mp_unsigned_bin_size(key->N); if (x > *outlen) { *outlen = x; err = CRYPT_BUFFER_OVERFLOW; goto error; } /* this should never happen ... */ if (mp_unsigned_bin_size(tmp) > mp_unsigned_bin_size(key->N)) { err = CRYPT_ERROR; goto error; } *outlen = x; /* convert it */ zeromem(out, x); if ((err = mp_to_unsigned_bin(tmp, out + (x - mp_unsigned_bin_size(tmp)))) != CRYPT_OK) { goto error; } /* clean up and return */ err = CRYPT_OK; error: mp_clear_multi(tmp, tmpa, tmpb, NULL); return err; } #endif /* $Source: /cvs/libtom/libtomcrypt/src/pk/rsa/rsa_exptmod.c,v $ */ /* $Revision: 1.18 $ */ /* $Date: 2007/05/12 14:32:35 $ */ /* LibTomCrypt, modular cryptographic library -- Tom St Denis * * LibTomCrypt is a library that provides various cryptographic * algorithms in a highly modular and flexible manner. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tomstdenis@gmail.com, http://libtom.org */ /** @file rsa_free.c Free an RSA key, Tom St Denis */ #ifdef LTC_MRSA /** Free an RSA key from memory @param key The RSA key to free */ void rsa_free(rsa_key *key) { LTC_ARGCHKVD(key != NULL); mp_clear_multi(key->e, key->d, key->N, key->dQ, key->dP, key->qP, key->p, key->q, NULL); } #endif /* $Source: /cvs/libtom/libtomcrypt/src/pk/rsa/rsa_free.c,v $ */ /* $Revision: 1.10 $ */ /* $Date: 2007/05/12 14:32:35 $ */ /* LibTomCrypt, modular cryptographic library -- Tom St Denis * * LibTomCrypt is a library that provides various cryptographic * algorithms in a highly modular and flexible manner. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tomstdenis@gmail.com, http://libtom.org */ /** @file rsa_import.c Import a LTC_PKCS RSA key, Tom St Denis */ #ifdef LTC_MRSA /** Import an RSAPublicKey or RSAPrivateKey [two-prime only, only support >= 1024-bit keys, defined in LTC_PKCS #1 v2.1] @param in The packet to import from @param inlen It's length (octets) @param key [out] Destination for newly imported key @return CRYPT_OK if successful, upon error allocated memory is freed */ int rsa_import(const unsigned char *in, unsigned long inlen, rsa_key *key) { int err; void *zero; unsigned char *tmpbuf; unsigned long t, x, y, z, tmpoid[16]; ltc_asn1_list ssl_pubkey_hashoid[2]; ltc_asn1_list ssl_pubkey[2]; LTC_ARGCHK(in != NULL); LTC_ARGCHK(key != NULL); LTC_ARGCHK(ltc_mp.name != NULL); /* init key */ if ((err = mp_init_multi(&key->e, &key->d, &key->N, &key->dQ, &key->dP, &key->qP, &key->p, &key->q, NULL)) != CRYPT_OK) { return err; } /* see if the OpenSSL DER format RSA public key will work */ tmpbuf = AUTO_CAST(XCALLOC(1, MAX_RSA_SIZE * 8)); if (tmpbuf == NULL) { err = CRYPT_MEM; goto LBL_ERR; } /* this includes the internal hash ID and optional params (NULL in this case) */ LTC_SET_ASN1(ssl_pubkey_hashoid, 0, LTC_ASN1_OBJECT_IDENTIFIER, tmpoid, sizeof(tmpoid) / sizeof(tmpoid[0])); LTC_SET_ASN1(ssl_pubkey_hashoid, 1, LTC_ASN1_NULL, NULL, 0); /* the actual format of the SSL DER key is odd, it stores a RSAPublicKey in a **BIT** string ... so we have to extract it then proceed to convert bit to octet */ LTC_SET_ASN1(ssl_pubkey, 0, LTC_ASN1_SEQUENCE, &ssl_pubkey_hashoid, 2); LTC_SET_ASN1(ssl_pubkey, 1, LTC_ASN1_BIT_STRING, tmpbuf, MAX_RSA_SIZE * 8); if (der_decode_sequence(in, inlen, ssl_pubkey, 2UL) == CRYPT_OK) { /* ok now we have to reassemble the BIT STRING to an OCTET STRING. Thanks OpenSSL... */ for (t = y = z = x = 0; x < ssl_pubkey[1].size; x++) { y = (y << 1) | tmpbuf[x]; if (++z == 8) { tmpbuf[t++] = (unsigned char)y; y = 0; z = 0; } } /* now it should be SEQUENCE { INTEGER, INTEGER } */ if ((err = der_decode_sequence_multi(tmpbuf, t, LTC_ASN1_INTEGER, 1UL, key->N, LTC_ASN1_INTEGER, 1UL, key->e, LTC_ASN1_EOL, 0UL, NULL)) != CRYPT_OK) { XFREE(tmpbuf); goto LBL_ERR; } XFREE(tmpbuf); key->type = PK_PUBLIC; return CRYPT_OK; } XFREE(tmpbuf); /* not SSL public key, try to match against LTC_PKCS #1 standards */ if ((err = der_decode_sequence_multi(in, inlen, LTC_ASN1_INTEGER, 1UL, key->N, LTC_ASN1_EOL, 0UL, NULL)) != CRYPT_OK) { goto LBL_ERR; } if (mp_cmp_d(key->N, 0) == LTC_MP_EQ) { if ((err = mp_init(&zero)) != CRYPT_OK) { goto LBL_ERR; } /* it's a private key */ if ((err = der_decode_sequence_multi(in, inlen, LTC_ASN1_INTEGER, 1UL, zero, LTC_ASN1_INTEGER, 1UL, key->N, LTC_ASN1_INTEGER, 1UL, key->e, LTC_ASN1_INTEGER, 1UL, key->d, LTC_ASN1_INTEGER, 1UL, key->p, LTC_ASN1_INTEGER, 1UL, key->q, LTC_ASN1_INTEGER, 1UL, key->dP, LTC_ASN1_INTEGER, 1UL, key->dQ, LTC_ASN1_INTEGER, 1UL, key->qP, LTC_ASN1_EOL, 0UL, NULL)) != CRYPT_OK) { mp_clear(zero); goto LBL_ERR; } mp_clear(zero); key->type = PK_PRIVATE; } else if (mp_cmp_d(key->N, 1) == LTC_MP_EQ) { /* we don't support multi-prime RSA */ err = CRYPT_PK_INVALID_TYPE; goto LBL_ERR; } else { /* it's a public key and we lack e */ if ((err = der_decode_sequence_multi(in, inlen, LTC_ASN1_INTEGER, 1UL, key->N, LTC_ASN1_INTEGER, 1UL, key->e, LTC_ASN1_EOL, 0UL, NULL)) != CRYPT_OK) { goto LBL_ERR; } key->type = PK_PUBLIC; } return CRYPT_OK; LBL_ERR: mp_clear_multi(key->d, key->e, key->N, key->dQ, key->dP, key->qP, key->p, key->q, NULL); return err; } #endif /* LTC_MRSA */ /* $Source: /cvs/libtom/libtomcrypt/src/pk/rsa/rsa_import.c,v $ */ /* $Revision: 1.23 $ */ /* $Date: 2007/05/12 14:32:35 $ */ /* LibTomCrypt, modular cryptographic library -- Tom St Denis * * LibTomCrypt is a library that provides various cryptographic * algorithms in a highly modular and flexible manner. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tomstdenis@gmail.com, http://libtom.org */ /** @file rsa_make_key.c RSA key generation, Tom St Denis */ #ifdef LTC_MRSA /** Create an RSA key @param prng An active PRNG state @param wprng The index of the PRNG desired @param size The size of the modulus (key size) desired (octets) @param e The "e" value (public key). e==65537 is a good choice @param key [out] Destination of a newly created private key pair @return CRYPT_OK if successful, upon error all allocated ram is freed */ int rsa_make_key(prng_state *prng, int wprng, int size, long e, rsa_key *key) { void *p, *q, *tmp1, *tmp2, *tmp3; int err; LTC_ARGCHK(ltc_mp.name != NULL); LTC_ARGCHK(key != NULL); if ((size < (MIN_RSA_SIZE / 8)) || (size > (MAX_RSA_SIZE / 8))) { return CRYPT_INVALID_KEYSIZE; } if ((e < 3) || ((e & 1) == 0)) { return CRYPT_INVALID_ARG; } if ((err = prng_is_valid(wprng)) != CRYPT_OK) { return err; } if ((err = mp_init_multi(&p, &q, &tmp1, &tmp2, &tmp3, NULL)) != CRYPT_OK) { return err; } /* make primes p and q (optimization provided by Wayne Scott) */ if ((err = mp_set_int(tmp3, e)) != CRYPT_OK) { goto errkey; } /* tmp3 = e */ /* make prime "p" */ do { if ((err = rand_prime(p, size / 2, prng, wprng)) != CRYPT_OK) { goto errkey; } if ((err = mp_sub_d(p, 1, tmp1)) != CRYPT_OK) { goto errkey; } /* tmp1 = p-1 */ if ((err = mp_gcd(tmp1, tmp3, tmp2)) != CRYPT_OK) { goto errkey; } /* tmp2 = gcd(p-1, e) */ } while (mp_cmp_d(tmp2, 1) != 0); /* while e divides p-1 */ /* make prime "q" */ do { if ((err = rand_prime(q, size / 2, prng, wprng)) != CRYPT_OK) { goto errkey; } if ((err = mp_sub_d(q, 1, tmp1)) != CRYPT_OK) { goto errkey; } /* tmp1 = q-1 */ if ((err = mp_gcd(tmp1, tmp3, tmp2)) != CRYPT_OK) { goto errkey; } /* tmp2 = gcd(q-1, e) */ } while (mp_cmp_d(tmp2, 1) != 0); /* while e divides q-1 */ /* tmp1 = lcm(p-1, q-1) */ if ((err = mp_sub_d(p, 1, tmp2)) != CRYPT_OK) { goto errkey; } /* tmp2 = p-1 */ /* tmp1 = q-1 (previous do/while loop) */ if ((err = mp_lcm(tmp1, tmp2, tmp1)) != CRYPT_OK) { goto errkey; } /* tmp1 = lcm(p-1, q-1) */ /* make key */ if ((err = mp_init_multi(&key->e, &key->d, &key->N, &key->dQ, &key->dP, &key->qP, &key->p, &key->q, NULL)) != CRYPT_OK) { goto errkey; } if ((err = mp_set_int(key->e, e)) != CRYPT_OK) { goto errkey; } /* key->e = e */ if ((err = mp_invmod(key->e, tmp1, key->d)) != CRYPT_OK) { goto errkey; } /* key->d = 1/e mod lcm(p-1,q-1) */ if ((err = mp_mul(p, q, key->N)) != CRYPT_OK) { goto errkey; } /* key->N = pq */ /* optimize for CRT now */ /* find d mod q-1 and d mod p-1 */ if ((err = mp_sub_d(p, 1, tmp1)) != CRYPT_OK) { goto errkey; } /* tmp1 = q-1 */ if ((err = mp_sub_d(q, 1, tmp2)) != CRYPT_OK) { goto errkey; } /* tmp2 = p-1 */ if ((err = mp_mod(key->d, tmp1, key->dP)) != CRYPT_OK) { goto errkey; } /* dP = d mod p-1 */ if ((err = mp_mod(key->d, tmp2, key->dQ)) != CRYPT_OK) { goto errkey; } /* dQ = d mod q-1 */ if ((err = mp_invmod(q, p, key->qP)) != CRYPT_OK) { goto errkey; } /* qP = 1/q mod p */ if ((err = mp_copy(p, key->p)) != CRYPT_OK) { goto errkey; } if ((err = mp_copy(q, key->q)) != CRYPT_OK) { goto errkey; } /* set key type (in this case it's CRT optimized) */ key->type = PK_PRIVATE; /* return ok and free temps */ err = CRYPT_OK; goto cleanup; errkey: mp_clear_multi(key->d, key->e, key->N, key->dQ, key->dP, key->qP, key->p, key->q, NULL); cleanup: mp_clear_multi(tmp3, tmp2, tmp1, p, q, NULL); return err; } #endif /* $Source: /cvs/libtom/libtomcrypt/src/pk/rsa/rsa_make_key.c,v $ */ /* $Revision: 1.16 $ */ /* $Date: 2007/05/12 14:32:35 $ */ /* LibTomCrypt, modular cryptographic library -- Tom St Denis * * LibTomCrypt is a library that provides various cryptographic * algorithms in a highly modular and flexible manner. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tomstdenis@gmail.com, http://libtom.org */ /** @file rsa_sign_hash.c RSA LTC_PKCS #1 v1.5 and v2 PSS sign hash, Tom St Denis and Andreas Lange */ #ifdef LTC_MRSA /** LTC_PKCS #1 pad then sign @param in The hash to sign @param inlen The length of the hash to sign (octets) @param out [out] The signature @param outlen [in/out] The max size and resulting size of the signature @param padding Type of padding (LTC_LTC_PKCS_1_PSS or LTC_LTC_PKCS_1_V1_5) @param prng An active PRNG state @param prng_idx The index of the PRNG desired @param hash_idx The index of the hash desired @param saltlen The length of the salt desired (octets) @param key The private RSA key to use @return CRYPT_OK if successful */ int rsa_sign_hash_ex(const unsigned char *in, unsigned long inlen, unsigned char *out, unsigned long *outlen, int padding, prng_state *prng, int prng_idx, int hash_idx, unsigned long saltlen, rsa_key *key) { unsigned long modulus_bitlen, modulus_bytelen, x, y; int err; LTC_ARGCHK(in != NULL); LTC_ARGCHK(out != NULL); LTC_ARGCHK(outlen != NULL); LTC_ARGCHK(key != NULL); /* valid padding? */ if ((padding != LTC_LTC_PKCS_1_V1_5) && (padding != LTC_LTC_PKCS_1_PSS)) { return CRYPT_PK_INVALID_PADDING; } if (padding == LTC_LTC_PKCS_1_PSS) { /* valid prng and hash ? */ if ((err = prng_is_valid(prng_idx)) != CRYPT_OK) { return err; } if ((err = hash_is_valid(hash_idx)) != CRYPT_OK) { return err; } } /* get modulus len in bits */ modulus_bitlen = mp_count_bits((key->N)); /* outlen must be at least the size of the modulus */ modulus_bytelen = mp_unsigned_bin_size((key->N)); if (modulus_bytelen > *outlen) { *outlen = modulus_bytelen; return CRYPT_BUFFER_OVERFLOW; } if (padding == LTC_LTC_PKCS_1_PSS) { /* PSS pad the key */ x = *outlen; if ((err = pkcs_1_pss_encode(in, inlen, saltlen, prng, prng_idx, hash_idx, modulus_bitlen, out, &x)) != CRYPT_OK) { return err; } } else { /* LTC_PKCS #1 v1.5 pad the hash */ unsigned char *tmpin; ltc_asn1_list digestinfo[2], siginfo[2]; /* not all hashes have OIDs... so sad */ if (hash_descriptor[hash_idx].OIDlen == 0) { return CRYPT_INVALID_ARG; } /* construct the SEQUENCE SEQUENCE { SEQUENCE {hashoid OID blah NULL } hash OCTET STRING } */ LTC_SET_ASN1(digestinfo, 0, LTC_ASN1_OBJECT_IDENTIFIER, hash_descriptor[hash_idx].OID, hash_descriptor[hash_idx].OIDlen); LTC_SET_ASN1(digestinfo, 1, LTC_ASN1_NULL, NULL, 0); LTC_SET_ASN1(siginfo, 0, LTC_ASN1_SEQUENCE, digestinfo, 2); LTC_SET_ASN1(siginfo, 1, LTC_ASN1_OCTET_STRING, in, inlen); /* allocate memory for the encoding */ y = mp_unsigned_bin_size(key->N); tmpin = AUTO_CAST(XMALLOC(y)); if (tmpin == NULL) { return CRYPT_MEM; } if ((err = der_encode_sequence(siginfo, 2, tmpin, &y)) != CRYPT_OK) { XFREE(tmpin); return err; } x = *outlen; if ((err = pkcs_1_v1_5_encode(tmpin, y, LTC_LTC_PKCS_1_EMSA, modulus_bitlen, NULL, 0, out, &x)) != CRYPT_OK) { XFREE(tmpin); return err; } XFREE(tmpin); } /* RSA encode it */ return ltc_mp.rsa_me(out, x, out, outlen, PK_PRIVATE, key); } #endif /* LTC_MRSA */ /* $Source: /cvs/libtom/libtomcrypt/src/pk/rsa/rsa_sign_hash.c,v $ */ /* $Revision: 1.11 $ */ /* $Date: 2007/05/12 14:32:35 $ */ /* LibTomCrypt, modular cryptographic library -- Tom St Denis * * LibTomCrypt is a library that provides various cryptographic * algorithms in a highly modular and flexible manner. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tomstdenis@gmail.com, http://libtom.org */ /** @file rsa_verify_hash.c RSA LTC_PKCS #1 v1.5 or v2 PSS signature verification, Tom St Denis and Andreas Lange */ #ifdef LTC_MRSA /** LTC_PKCS #1 de-sign then v1.5 or PSS depad @param sig The signature data @param siglen The length of the signature data (octets) @param hash The hash of the message that was signed @param hashlen The length of the hash of the message that was signed (octets) @param padding Type of padding (LTC_LTC_PKCS_1_PSS or LTC_LTC_PKCS_1_V1_5) @param hash_idx The index of the desired hash @param saltlen The length of the salt used during signature @param stat [out] The result of the signature comparison, 1==valid, 0==invalid @param key The public RSA key corresponding to the key that performed the signature @return CRYPT_OK on success (even if the signature is invalid) */ int rsa_verify_hash_ex(const unsigned char *sig, unsigned long siglen, const unsigned char *hash, unsigned long hashlen, int padding, int hash_idx, unsigned long saltlen, int *stat, rsa_key *key) { unsigned long modulus_bitlen, modulus_bytelen, x; int err; unsigned char *tmpbuf; LTC_ARGCHK(hash != NULL); LTC_ARGCHK(sig != NULL); LTC_ARGCHK(stat != NULL); LTC_ARGCHK(key != NULL); /* default to invalid */ *stat = 0; /* valid padding? */ if ((padding != LTC_LTC_PKCS_1_V1_5) && (padding != LTC_LTC_PKCS_1_PSS)) { return CRYPT_PK_INVALID_PADDING; } if (padding == LTC_LTC_PKCS_1_PSS) { /* valid hash ? */ if ((err = hash_is_valid(hash_idx)) != CRYPT_OK) { return err; } } /* get modulus len in bits */ modulus_bitlen = mp_count_bits((key->N)); /* outlen must be at least the size of the modulus */ modulus_bytelen = mp_unsigned_bin_size((key->N)); if (modulus_bytelen != siglen) { return CRYPT_INVALID_PACKET; } /* allocate temp buffer for decoded sig */ tmpbuf = AUTO_CAST(XMALLOC(siglen)); if (tmpbuf == NULL) { return CRYPT_MEM; } /* RSA decode it */ x = siglen; if ((err = ltc_mp.rsa_me(sig, siglen, tmpbuf, &x, PK_PUBLIC, key)) != CRYPT_OK) { XFREE(tmpbuf); return err; } /* make sure the output is the right size */ if (x != siglen) { XFREE(tmpbuf); return CRYPT_INVALID_PACKET; } if (padding == LTC_LTC_PKCS_1_PSS) { /* PSS decode and verify it */ err = pkcs_1_pss_decode(hash, hashlen, tmpbuf, x, saltlen, hash_idx, modulus_bitlen, stat); } else { /* LTC_PKCS #1 v1.5 decode it */ unsigned char *out; unsigned long outlen, loid[16]; int decoded; ltc_asn1_list digestinfo[2], siginfo[2]; /* not all hashes have OIDs... so sad */ if (hash_descriptor[hash_idx].OIDlen == 0) { err = CRYPT_INVALID_ARG; goto bail_2; } /* allocate temp buffer for decoded hash */ outlen = ((modulus_bitlen >> 3) + (modulus_bitlen & 7 ? 1 : 0)) - 3; out = AUTO_CAST(XMALLOC(outlen)); if (out == NULL) { err = CRYPT_MEM; goto bail_2; } if ((err = pkcs_1_v1_5_decode(tmpbuf, x, LTC_LTC_PKCS_1_EMSA, modulus_bitlen, out, &outlen, &decoded)) != CRYPT_OK) { XFREE(out); goto bail_2; } /* now we must decode out[0...outlen-1] using ASN.1, test the OID and then test the hash */ /* construct the SEQUENCE SEQUENCE { SEQUENCE {hashoid OID blah NULL } hash OCTET STRING } */ LTC_SET_ASN1(digestinfo, 0, LTC_ASN1_OBJECT_IDENTIFIER, loid, sizeof(loid) / sizeof(loid[0])); LTC_SET_ASN1(digestinfo, 1, LTC_ASN1_NULL, NULL, 0); LTC_SET_ASN1(siginfo, 0, LTC_ASN1_SEQUENCE, digestinfo, 2); LTC_SET_ASN1(siginfo, 1, LTC_ASN1_OCTET_STRING, tmpbuf, siglen); if ((err = der_decode_sequence(out, outlen, siginfo, 2)) != CRYPT_OK) { XFREE(out); goto bail_2; } /* test OID */ if ((digestinfo[0].size == hash_descriptor[hash_idx].OIDlen) && (XMEMCMP(digestinfo[0].data, hash_descriptor[hash_idx].OID, sizeof(unsigned long) * hash_descriptor[hash_idx].OIDlen) == 0) && (siginfo[1].size == hashlen) && (XMEMCMP(siginfo[1].data, hash, hashlen) == 0)) { *stat = 1; } #ifdef LTC_CLEAN_STACK zeromem(out, outlen); #endif XFREE(out); } bail_2: #ifdef LTC_CLEAN_STACK zeromem(tmpbuf, siglen); #endif XFREE(tmpbuf); return err; } #endif /* LTC_MRSA */ /* $Source: /cvs/libtom/libtomcrypt/src/pk/rsa/rsa_verify_hash.c,v $ */ /* $Revision: 1.13 $ */ /* $Date: 2007/05/12 14:32:35 $ */ /* LibTomCrypt, modular cryptographic library -- Tom St Denis * * LibTomCrypt is a library that provides various cryptographic * algorithms in a highly modular and flexible manner. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tomstdenis@gmail.com, http://libtom.org */ /** @file sprng.c Secure PRNG, Tom St Denis */ /* A secure PRNG using the RNG functions. Basically this is a * wrapper that allows you to use a secure RNG as a PRNG * in the various other functions. */ #ifdef LTC_SPRNG const struct ltc_prng_descriptor sprng_desc = { "sprng", 0, &sprng_start, &sprng_add_entropy, &sprng_ready, &sprng_read, &sprng_done, &sprng_export, &sprng_import, &sprng_test }; /** Start the PRNG @param prng [out] The PRNG state to initialize @return CRYPT_OK if successful */ int sprng_start(prng_state *prng) { return CRYPT_OK; } /** Add entropy to the PRNG state @param in The data to add @param inlen Length of the data to add @param prng PRNG state to update @return CRYPT_OK if successful */ int sprng_add_entropy(const unsigned char *in, unsigned long inlen, prng_state *prng) { return CRYPT_OK; } /** Make the PRNG ready to read from @param prng The PRNG to make active @return CRYPT_OK if successful */ int sprng_ready(prng_state *prng) { return CRYPT_OK; } /** Read from the PRNG @param out Destination @param outlen Length of output @param prng The active PRNG to read from @return Number of octets read */ unsigned long sprng_read(unsigned char *out, unsigned long outlen, prng_state *prng) { LTC_ARGCHK(out != NULL); return rng_get_bytes(out, outlen, NULL); } /** Terminate the PRNG @param prng The PRNG to terminate @return CRYPT_OK if successful */ int sprng_done(prng_state *prng) { return CRYPT_OK; } /** Export the PRNG state @param out [out] Destination @param outlen [in/out] Max size and resulting size of the state @param prng The PRNG to export @return CRYPT_OK if successful */ int sprng_export(unsigned char *out, unsigned long *outlen, prng_state *prng) { LTC_ARGCHK(outlen != NULL); *outlen = 0; return CRYPT_OK; } /** Import a PRNG state @param in The PRNG state @param inlen Size of the state @param prng The PRNG to import @return CRYPT_OK if successful */ int sprng_import(const unsigned char *in, unsigned long inlen, prng_state *prng) { return CRYPT_OK; } /** PRNG self-test @return CRYPT_OK if successful, CRYPT_NOP if self-testing has been disabled */ int sprng_test(void) { return CRYPT_OK; } #endif /* $Source: /cvs/libtom/libtomcrypt/src/prngs/sprng.c,v $ */ /* $Revision: 1.6 $ */ /* $Date: 2007/05/12 14:32:35 $ */ /* LibTomCrypt, modular cryptographic library -- Tom St Denis * * LibTomCrypt is a library that provides various cryptographic * algorithms in a highly modular and flexible manner. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tomstdenis@gmail.com, http://libtom.org */ /** @file base64_decode.c Compliant base64 code donated by Wayne Scott (wscott@bitmover.com) base64 URL Safe variant (RFC 4648 section 5) by Karel Miko */ #if defined(LTC_BASE64) || defined (LTC_BASE64_URL) #if defined(LTC_BASE64) static const unsigned char map_base64[256] = { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 62, 255, 255, 255, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 255, 255, 255, 254, 255, 255, 255, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 255, 255, 255, 255, 255, 255, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }; #endif /* LTC_BASE64 */ #if defined(LTC_BASE64_URL) static const unsigned char map_base64url[] = { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 62, 255, 255, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 255, 255, 255, 254, 255, 255, 255, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 255, 255, 255, 255, 63, 255, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }; #endif /* LTC_BASE64_URL */ enum { relaxed = 0, strict = 1 }; static int _base64_decode_internal(const unsigned char *in, unsigned long inlen, unsigned char *out, unsigned long *outlen, const unsigned char *map, int is_strict) { unsigned long t, x, y, z; unsigned char c; int g; LTC_ARGCHK(in != NULL); LTC_ARGCHK(out != NULL); LTC_ARGCHK(outlen != NULL); g = 0; /* '=' counter */ for (x = y = z = t = 0; x < inlen; x++) { c = map[in[x]&0xFF]; if (c == 254) { g++; continue; } else if (is_strict && g > 0) { /* we only allow '=' to be at the end */ return CRYPT_INVALID_PACKET; } if (c == 255) { if (is_strict) return CRYPT_INVALID_PACKET; else continue; } t = (t<<6)|c; if (++y == 4) { if (z + 3 > *outlen) return CRYPT_BUFFER_OVERFLOW; out[z++] = (unsigned char)((t>>16)&255); out[z++] = (unsigned char)((t>>8)&255); out[z++] = (unsigned char)(t&255); y = t = 0; } } if (y != 0) { int allow_b64url = 0; #ifdef LTC_BASE64_URL if (map == map_base64url) { allow_b64url = 1; } #endif if (y == 1) return CRYPT_INVALID_PACKET; if ((y + g) != 4 && is_strict && !allow_b64url) return CRYPT_INVALID_PACKET; t = t << (6 * (4 - y)); if (z + y - 1 > *outlen) return CRYPT_BUFFER_OVERFLOW; if (y >= 2) out[z++] = (unsigned char) ((t >> 16) & 255); if (y == 3) out[z++] = (unsigned char) ((t >> 8) & 255); } *outlen = z; return CRYPT_OK; } #if defined(LTC_BASE64) /** Relaxed base64 decode a block of memory @param in The base64 data to decode @param inlen The length of the base64 data @param out [out] The destination of the binary decoded data @param outlen [in/out] The max size and resulting size of the decoded data @return CRYPT_OK if successful */ int base64_decode(const unsigned char *in, unsigned long inlen, unsigned char *out, unsigned long *outlen) { return _base64_decode_internal(in, inlen, out, outlen, map_base64, relaxed); } /** Strict base64 decode a block of memory @param in The base64 data to decode @param inlen The length of the base64 data @param out [out] The destination of the binary decoded data @param outlen [in/out] The max size and resulting size of the decoded data @return CRYPT_OK if successful */ int base64_strict_decode(const unsigned char *in, unsigned long inlen, unsigned char *out, unsigned long *outlen) { return _base64_decode_internal(in, inlen, out, outlen, map_base64, strict); } #endif /* LTC_BASE64 */ #if defined(LTC_BASE64_URL) /** Relaxed base64 (URL Safe, RFC 4648 section 5) decode a block of memory @param in The base64 data to decode @param inlen The length of the base64 data @param out [out] The destination of the binary decoded data @param outlen [in/out] The max size and resulting size of the decoded data @return CRYPT_OK if successful */ int base64url_decode(const unsigned char *in, unsigned long inlen, unsigned char *out, unsigned long *outlen) { return _base64_decode_internal(in, inlen, out, outlen, map_base64url, relaxed); } /** Strict base64 (URL Safe, RFC 4648 section 5) decode a block of memory @param in The base64 data to decode @param inlen The length of the base64 data @param out [out] The destination of the binary decoded data @param outlen [in/out] The max size and resulting size of the decoded data @return CRYPT_OK if successful */ int base64url_strict_decode(const unsigned char *in, unsigned long inlen, unsigned char *out, unsigned long *outlen) { return _base64_decode_internal(in, inlen, out, outlen, map_base64url, strict); } #endif /* LTC_BASE64_URL */ #endif /** @file zeromem.c Zero a block of memory, Tom St Denis */ /** Zero a block of memory @param out The destination of the area to zero @param outlen The length of the area to zero (octets) */ void zeromem(void *out, size_t outlen) { unsigned char *mem = AUTO_CAST(out); LTC_ARGCHKVD(out != NULL); while (outlen-- > 0) { *mem++ = 0; } } /* $Source: /cvs/libtom/libtomcrypt/src/misc/zeromem.c,v $ */ /* $Revision: 1.7 $ */ /* $Date: 2006/12/28 01:27:24 $ */ /* LibTomCrypt, modular cryptographic library -- Tom St Denis * * LibTomCrypt is a library that provides various cryptographic * algorithms in a highly modular and flexible manner. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tomstdenis@gmail.com, http://libtom.org */ /** @file sha1.c LTC_SHA1 code by Tom St Denis */ #ifdef LTC_SHA1 const struct ltc_hash_descriptor sha1_desc = { "sha1", 2, 20, 64, /* OID */ { 1, 3, 14, 3, 2, 26, }, 6, &sha1_init, &sha1_process, &sha1_done, &sha1_test, NULL }; #define F0(x, y, z) (z ^ (x & (y ^ z))) #define F1(x, y, z) (x ^ y ^ z) #define F2(x, y, z) ((x & y) | (z & (x | y))) #define F3(x, y, z) (x ^ y ^ z) #ifdef LTC_CLEAN_STACK static int _sha1_compress(hash_state *md, unsigned char *buf) #else static int sha1_compress(hash_state *md, unsigned char *buf) #endif { ulong32 a, b, c, d, e, W[80], i; #ifdef LTC_SMALL_CODE ulong32 t; #endif /* copy the state into 512-bits into W[0..15] */ for (i = 0; i < 16; i++) { LOAD32H(W[i], buf + (4 * i)); } /* copy state */ a = md->sha1.state[0]; b = md->sha1.state[1]; c = md->sha1.state[2]; d = md->sha1.state[3]; e = md->sha1.state[4]; /* expand it */ for (i = 16; i < 80; i++) { W[i] = ROL(W[i - 3] ^ W[i - 8] ^ W[i - 14] ^ W[i - 16], 1); } /* compress */ /* round one */ #define FF0(a, b, c, d, e, i) e = (ROLc(a, 5) + F0(b, c, d) + e + W[i] + 0x5a827999UL); b = ROLc(b, 30); #define FF1(a, b, c, d, e, i) e = (ROLc(a, 5) + F1(b, c, d) + e + W[i] + 0x6ed9eba1UL); b = ROLc(b, 30); #define FF2(a, b, c, d, e, i) e = (ROLc(a, 5) + F2(b, c, d) + e + W[i] + 0x8f1bbcdcUL); b = ROLc(b, 30); #define FF3(a, b, c, d, e, i) e = (ROLc(a, 5) + F3(b, c, d) + e + W[i] + 0xca62c1d6UL); b = ROLc(b, 30); #ifdef LTC_SMALL_CODE for (i = 0; i < 20; ) { FF0(a, b, c, d, e, i++); t = e; e = d; d = c; c = b; b = a; a = t; } for ( ; i < 40; ) { FF1(a, b, c, d, e, i++); t = e; e = d; d = c; c = b; b = a; a = t; } for ( ; i < 60; ) { FF2(a, b, c, d, e, i++); t = e; e = d; d = c; c = b; b = a; a = t; } for ( ; i < 80; ) { FF3(a, b, c, d, e, i++); t = e; e = d; d = c; c = b; b = a; a = t; } #else for (i = 0; i < 20; ) { FF0(a, b, c, d, e, i++); FF0(e, a, b, c, d, i++); FF0(d, e, a, b, c, i++); FF0(c, d, e, a, b, i++); FF0(b, c, d, e, a, i++); } /* round two */ for ( ; i < 40; ) { FF1(a, b, c, d, e, i++); FF1(e, a, b, c, d, i++); FF1(d, e, a, b, c, i++); FF1(c, d, e, a, b, i++); FF1(b, c, d, e, a, i++); } /* round three */ for ( ; i < 60; ) { FF2(a, b, c, d, e, i++); FF2(e, a, b, c, d, i++); FF2(d, e, a, b, c, i++); FF2(c, d, e, a, b, i++); FF2(b, c, d, e, a, i++); } /* round four */ for ( ; i < 80; ) { FF3(a, b, c, d, e, i++); FF3(e, a, b, c, d, i++); FF3(d, e, a, b, c, i++); FF3(c, d, e, a, b, i++); FF3(b, c, d, e, a, i++); } #endif #undef FF0 #undef FF1 #undef FF2 #undef FF3 /* store */ md->sha1.state[0] = md->sha1.state[0] + a; md->sha1.state[1] = md->sha1.state[1] + b; md->sha1.state[2] = md->sha1.state[2] + c; md->sha1.state[3] = md->sha1.state[3] + d; md->sha1.state[4] = md->sha1.state[4] + e; return CRYPT_OK; } #ifdef LTC_CLEAN_STACK static int sha1_compress(hash_state *md, unsigned char *buf) { int err; err = _sha1_compress(md, buf); burn_stack(sizeof(ulong32) * 87); return err; } #endif /** Initialize the hash state @param md The hash state you wish to initialize @return CRYPT_OK if successful */ int sha1_init(hash_state *md) { LTC_ARGCHK(md != NULL); md->sha1.state[0] = 0x67452301UL; md->sha1.state[1] = 0xefcdab89UL; md->sha1.state[2] = 0x98badcfeUL; md->sha1.state[3] = 0x10325476UL; md->sha1.state[4] = 0xc3d2e1f0UL; md->sha1.curlen = 0; md->sha1.length = 0; return CRYPT_OK; } /** Process a block of memory though the hash @param md The hash state @param in The data to hash @param inlen The length of the data (octets) @return CRYPT_OK if successful */ HASH_PROCESS(sha1_process, sha1_compress, sha1, 64) /** Terminate the hash to get the digest @param md The hash state @param out [out] The destination of the hash (20 bytes) @return CRYPT_OK if successful */ int sha1_done(hash_state *md, unsigned char *out) { int i; LTC_ARGCHK(md != NULL); LTC_ARGCHK(out != NULL); if (md->sha1.curlen >= sizeof(md->sha1.buf)) { return CRYPT_INVALID_ARG; } /* increase the length of the message */ md->sha1.length += md->sha1.curlen * 8; /* append the '1' bit */ md->sha1.buf[md->sha1.curlen++] = (unsigned char)0x80; /* if the length is currently above 56 bytes we append zeros * then compress. Then we can fall back to padding zeros and length * encoding like normal. */ if (md->sha1.curlen > 56) { while (md->sha1.curlen < 64) { md->sha1.buf[md->sha1.curlen++] = (unsigned char)0; } sha1_compress(md, md->sha1.buf); md->sha1.curlen = 0; } /* pad upto 56 bytes of zeroes */ while (md->sha1.curlen < 56) { md->sha1.buf[md->sha1.curlen++] = (unsigned char)0; } /* store length */ STORE64H(md->sha1.length, md->sha1.buf + 56); sha1_compress(md, md->sha1.buf); /* copy output */ for (i = 0; i < 5; i++) { STORE32H(md->sha1.state[i], out + (4 * i)); } #ifdef LTC_CLEAN_STACK zeromem(md, sizeof(hash_state)); #endif return CRYPT_OK; } /** Self-test the hash @return CRYPT_OK if successful, CRYPT_NOP if self-tests have been disabled */ int sha1_test(void) { #ifndef LTC_TEST return CRYPT_NOP; #else static const struct { char *msg; unsigned char hash[20]; } tests[] = { { "abc", { 0xa9, 0x99, 0x3e, 0x36, 0x47, 0x06, 0x81, 0x6a, 0xba, 0x3e, 0x25, 0x71, 0x78, 0x50, 0xc2, 0x6c, 0x9c, 0xd0, 0xd8, 0x9d } }, { "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", { 0x84, 0x98, 0x3E, 0x44, 0x1C, 0x3B, 0xD2, 0x6E, 0xBA, 0xAE, 0x4A, 0xA1, 0xF9, 0x51, 0x29, 0xE5, 0xE5, 0x46, 0x70, 0xF1 } } }; int i; unsigned char tmp[20]; hash_state md; for (i = 0; i < (int)(sizeof(tests) / sizeof(tests[0])); i++) { sha1_init(&md); sha1_process(&md, (unsigned char *)tests[i].msg, (unsigned long)strlen(tests[i].msg)); sha1_done(&md, tmp); if (XMEMCMP(tmp, tests[i].hash, 20) != 0) { return CRYPT_FAIL_TESTVECTOR; } } return CRYPT_OK; #endif } #endif /* $Source: /cvs/libtom/libtomcrypt/src/hashes/sha1.c,v $ */ /* $Revision: 1.10 $ */ /* $Date: 2007/05/12 14:25:28 $ */ /* LibTomCrypt, modular cryptographic library -- Tom St Denis * * LibTomCrypt is a library that provides various cryptographic * algorithms in a highly modular and flexible manner. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tomstdenis@gmail.com, http://libtom.org */ /** @file sha256.c LTC_SHA256 by Tom St Denis */ #ifdef LTC_SHA256 const struct ltc_hash_descriptor sha256_desc = { "sha256", 0, 32, 64, /* OID */ { 2, 16, 840, 1, 101, 3, 4, 2, 1, }, 9, &sha256_init, &sha256_process, &sha256_done, &sha256_test, NULL }; #ifdef LTC_SMALL_CODE /* the K array */ static const ulong32 K[64] = { 0x428a2f98UL, 0x71374491UL, 0xb5c0fbcfUL, 0xe9b5dba5UL, 0x3956c25bUL, 0x59f111f1UL, 0x923f82a4UL, 0xab1c5ed5UL, 0xd807aa98UL, 0x12835b01UL, 0x243185beUL, 0x550c7dc3UL, 0x72be5d74UL, 0x80deb1feUL, 0x9bdc06a7UL, 0xc19bf174UL, 0xe49b69c1UL, 0xefbe4786UL, 0x0fc19dc6UL, 0x240ca1ccUL, 0x2de92c6fUL, 0x4a7484aaUL, 0x5cb0a9dcUL, 0x76f988daUL, 0x983e5152UL, 0xa831c66dUL, 0xb00327c8UL, 0xbf597fc7UL, 0xc6e00bf3UL, 0xd5a79147UL, 0x06ca6351UL, 0x14292967UL, 0x27b70a85UL, 0x2e1b2138UL, 0x4d2c6dfcUL, 0x53380d13UL, 0x650a7354UL, 0x766a0abbUL, 0x81c2c92eUL, 0x92722c85UL, 0xa2bfe8a1UL, 0xa81a664bUL, 0xc24b8b70UL, 0xc76c51a3UL, 0xd192e819UL, 0xd6990624UL, 0xf40e3585UL, 0x106aa070UL, 0x19a4c116UL, 0x1e376c08UL, 0x2748774cUL, 0x34b0bcb5UL, 0x391c0cb3UL, 0x4ed8aa4aUL, 0x5b9cca4fUL, 0x682e6ff3UL, 0x748f82eeUL, 0x78a5636fUL, 0x84c87814UL, 0x8cc70208UL, 0x90befffaUL, 0xa4506cebUL, 0xbef9a3f7UL, 0xc67178f2UL }; #endif /* Various logical functions */ #define Ch(x,y,z) (z ^ (x & (y ^ z))) #define Maj(x,y,z) (((x | y) & z) | (x & y)) #define S(x, n) RORc((x),(n)) #define R(x, n) (((x)&0xFFFFFFFFUL)>>(n)) #define Sigma0(x) (S(x, 2) ^ S(x, 13) ^ S(x, 22)) #define Sigma1(x) (S(x, 6) ^ S(x, 11) ^ S(x, 25)) #define Gamma0(x) (S(x, 7) ^ S(x, 18) ^ R(x, 3)) #define Gamma1(x) (S(x, 17) ^ S(x, 19) ^ R(x, 10)) /* compress 512-bits */ #ifdef LTC_CLEAN_STACK static int _sha256_compress(hash_state * md, unsigned char *buf) #else static int sha256_compress(hash_state * md, unsigned char *buf) #endif { ulong32 S[8], W[64], t0, t1; #ifdef LTC_SMALL_CODE ulong32 t; #endif int i; /* copy state into S */ for (i = 0; i < 8; i++) { S[i] = md->sha256.state[i]; } /* copy the state into 512-bits into W[0..15] */ for (i = 0; i < 16; i++) { LOAD32H(W[i], buf + (4*i)); } /* fill W[16..63] */ for (i = 16; i < 64; i++) { W[i] = Gamma1(W[i - 2]) + W[i - 7] + Gamma0(W[i - 15]) + W[i - 16]; } /* Compress */ #ifdef LTC_SMALL_CODE #define RND(a,b,c,d,e,f,g,h,i) \ t0 = h + Sigma1(e) + Ch(e, f, g) + K[i] + W[i]; \ t1 = Sigma0(a) + Maj(a, b, c); \ d += t0; \ h = t0 + t1; for (i = 0; i < 64; ++i) { RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],i); t = S[7]; S[7] = S[6]; S[6] = S[5]; S[5] = S[4]; S[4] = S[3]; S[3] = S[2]; S[2] = S[1]; S[1] = S[0]; S[0] = t; } #else #define RND(a,b,c,d,e,f,g,h,i,ki) \ t0 = h + Sigma1(e) + Ch(e, f, g) + ki + W[i]; \ t1 = Sigma0(a) + Maj(a, b, c); \ d += t0; \ h = t0 + t1; RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],0,0x428a2f98); RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],1,0x71374491); RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],2,0xb5c0fbcf); RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],3,0xe9b5dba5); RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],4,0x3956c25b); RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],5,0x59f111f1); RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],6,0x923f82a4); RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],7,0xab1c5ed5); RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],8,0xd807aa98); RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],9,0x12835b01); RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],10,0x243185be); RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],11,0x550c7dc3); RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],12,0x72be5d74); RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],13,0x80deb1fe); RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],14,0x9bdc06a7); RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],15,0xc19bf174); RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],16,0xe49b69c1); RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],17,0xefbe4786); RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],18,0x0fc19dc6); RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],19,0x240ca1cc); RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],20,0x2de92c6f); RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],21,0x4a7484aa); RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],22,0x5cb0a9dc); RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],23,0x76f988da); RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],24,0x983e5152); RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],25,0xa831c66d); RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],26,0xb00327c8); RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],27,0xbf597fc7); RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],28,0xc6e00bf3); RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],29,0xd5a79147); RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],30,0x06ca6351); RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],31,0x14292967); RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],32,0x27b70a85); RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],33,0x2e1b2138); RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],34,0x4d2c6dfc); RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],35,0x53380d13); RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],36,0x650a7354); RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],37,0x766a0abb); RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],38,0x81c2c92e); RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],39,0x92722c85); RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],40,0xa2bfe8a1); RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],41,0xa81a664b); RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],42,0xc24b8b70); RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],43,0xc76c51a3); RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],44,0xd192e819); RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],45,0xd6990624); RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],46,0xf40e3585); RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],47,0x106aa070); RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],48,0x19a4c116); RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],49,0x1e376c08); RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],50,0x2748774c); RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],51,0x34b0bcb5); RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],52,0x391c0cb3); RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],53,0x4ed8aa4a); RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],54,0x5b9cca4f); RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],55,0x682e6ff3); RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],56,0x748f82ee); RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],57,0x78a5636f); RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],58,0x84c87814); RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],59,0x8cc70208); RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],60,0x90befffa); RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],61,0xa4506ceb); RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],62,0xbef9a3f7); RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],63,0xc67178f2); #undef RND #endif /* feedback */ for (i = 0; i < 8; i++) { md->sha256.state[i] = md->sha256.state[i] + S[i]; } return CRYPT_OK; } #ifdef LTC_CLEAN_STACK static int sha256_compress(hash_state * md, unsigned char *buf) { int err; err = _sha256_compress(md, buf); burn_stack(sizeof(ulong32) * 74); return err; } #endif /** Initialize the hash state @param md The hash state you wish to initialize @return CRYPT_OK if successful */ int sha256_init(hash_state * md) { LTC_ARGCHK(md != NULL); md->sha256.curlen = 0; md->sha256.length = 0; md->sha256.state[0] = 0x6A09E667UL; md->sha256.state[1] = 0xBB67AE85UL; md->sha256.state[2] = 0x3C6EF372UL; md->sha256.state[3] = 0xA54FF53AUL; md->sha256.state[4] = 0x510E527FUL; md->sha256.state[5] = 0x9B05688CUL; md->sha256.state[6] = 0x1F83D9ABUL; md->sha256.state[7] = 0x5BE0CD19UL; return CRYPT_OK; } /** Process a block of memory though the hash @param md The hash state @param in The data to hash @param inlen The length of the data (octets) @return CRYPT_OK if successful */ HASH_PROCESS(sha256_process, sha256_compress, sha256, 64) /** Terminate the hash to get the digest @param md The hash state @param out [out] The destination of the hash (32 bytes) @return CRYPT_OK if successful */ int sha256_done(hash_state * md, unsigned char *out) { int i; LTC_ARGCHK(md != NULL); LTC_ARGCHK(out != NULL); if (md->sha256.curlen >= sizeof(md->sha256.buf)) { return CRYPT_INVALID_ARG; } /* increase the length of the message */ md->sha256.length += md->sha256.curlen * 8; /* append the '1' bit */ md->sha256.buf[md->sha256.curlen++] = (unsigned char)0x80; /* if the length is currently above 56 bytes we append zeros * then compress. Then we can fall back to padding zeros and length * encoding like normal. */ if (md->sha256.curlen > 56) { while (md->sha256.curlen < 64) { md->sha256.buf[md->sha256.curlen++] = (unsigned char)0; } sha256_compress(md, md->sha256.buf); md->sha256.curlen = 0; } /* pad upto 56 bytes of zeroes */ while (md->sha256.curlen < 56) { md->sha256.buf[md->sha256.curlen++] = (unsigned char)0; } /* store length */ STORE64H(md->sha256.length, md->sha256.buf+56); sha256_compress(md, md->sha256.buf); /* copy output */ for (i = 0; i < 8; i++) { STORE32H(md->sha256.state[i], out+(4*i)); } #ifdef LTC_CLEAN_STACK zeromem(md, sizeof(hash_state)); #endif return CRYPT_OK; } /** Self-test the hash @return CRYPT_OK if successful, CRYPT_NOP if self-tests have been disabled */ int sha256_test(void) { #ifndef LTC_TEST return CRYPT_NOP; #else static const struct { char *msg; unsigned char hash[32]; } tests[] = { { "abc", { 0xba, 0x78, 0x16, 0xbf, 0x8f, 0x01, 0xcf, 0xea, 0x41, 0x41, 0x40, 0xde, 0x5d, 0xae, 0x22, 0x23, 0xb0, 0x03, 0x61, 0xa3, 0x96, 0x17, 0x7a, 0x9c, 0xb4, 0x10, 0xff, 0x61, 0xf2, 0x00, 0x15, 0xad } }, { "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", { 0x24, 0x8d, 0x6a, 0x61, 0xd2, 0x06, 0x38, 0xb8, 0xe5, 0xc0, 0x26, 0x93, 0x0c, 0x3e, 0x60, 0x39, 0xa3, 0x3c, 0xe4, 0x59, 0x64, 0xff, 0x21, 0x67, 0xf6, 0xec, 0xed, 0xd4, 0x19, 0xdb, 0x06, 0xc1 } }, }; int i; unsigned char tmp[32]; hash_state md; for (i = 0; i < (int)(sizeof(tests) / sizeof(tests[0])); i++) { sha256_init(&md); sha256_process(&md, (unsigned char*)tests[i].msg, (unsigned long)strlen(tests[i].msg)); sha256_done(&md, tmp); if (XMEMCMP(tmp, tests[i].hash, 32) != 0) { return CRYPT_FAIL_TESTVECTOR; } } return CRYPT_OK; #endif } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ /* LibTomCrypt, modular cryptographic library -- Tom St Denis * * LibTomCrypt is a library that provides various cryptographic * algorithms in a highly modular and flexible manner. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tomstdenis@gmail.com, http://libtom.org */ /** @param sha384.c LTC_SHA384 hash included in sha512.c, Tom St Denis */ #if defined(LTC_SHA384) && defined(LTC_SHA512) const struct ltc_hash_descriptor sha384_desc = { "sha384", 4, 48, 128, /* OID */ { 2, 16, 840, 1, 101, 3, 4, 2, 2, }, 9, &sha384_init, &sha512_process, &sha384_done, &sha384_test, NULL }; /** Initialize the hash state @param md The hash state you wish to initialize @return CRYPT_OK if successful */ int sha384_init(hash_state * md) { LTC_ARGCHK(md != NULL); md->sha512.curlen = 0; md->sha512.length = 0; md->sha512.state[0] = CONST64(0xcbbb9d5dc1059ed8); md->sha512.state[1] = CONST64(0x629a292a367cd507); md->sha512.state[2] = CONST64(0x9159015a3070dd17); md->sha512.state[3] = CONST64(0x152fecd8f70e5939); md->sha512.state[4] = CONST64(0x67332667ffc00b31); md->sha512.state[5] = CONST64(0x8eb44a8768581511); md->sha512.state[6] = CONST64(0xdb0c2e0d64f98fa7); md->sha512.state[7] = CONST64(0x47b5481dbefa4fa4); return CRYPT_OK; } /** Terminate the hash to get the digest @param md The hash state @param out [out] The destination of the hash (48 bytes) @return CRYPT_OK if successful */ int sha384_done(hash_state * md, unsigned char *out) { unsigned char buf[64]; LTC_ARGCHK(md != NULL); LTC_ARGCHK(out != NULL); if (md->sha512.curlen >= sizeof(md->sha512.buf)) { return CRYPT_INVALID_ARG; } sha512_done(md, buf); XMEMCPY(out, buf, 48); #ifdef LTC_CLEAN_STACK zeromem(buf, sizeof(buf)); #endif return CRYPT_OK; } /** Self-test the hash @return CRYPT_OK if successful, CRYPT_NOP if self-tests have been disabled */ int sha384_test(void) { #ifndef LTC_TEST return CRYPT_NOP; #else static const struct { char *msg; unsigned char hash[48]; } tests[] = { { "abc", { 0xcb, 0x00, 0x75, 0x3f, 0x45, 0xa3, 0x5e, 0x8b, 0xb5, 0xa0, 0x3d, 0x69, 0x9a, 0xc6, 0x50, 0x07, 0x27, 0x2c, 0x32, 0xab, 0x0e, 0xde, 0xd1, 0x63, 0x1a, 0x8b, 0x60, 0x5a, 0x43, 0xff, 0x5b, 0xed, 0x80, 0x86, 0x07, 0x2b, 0xa1, 0xe7, 0xcc, 0x23, 0x58, 0xba, 0xec, 0xa1, 0x34, 0xc8, 0x25, 0xa7 } }, { "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu", { 0x09, 0x33, 0x0c, 0x33, 0xf7, 0x11, 0x47, 0xe8, 0x3d, 0x19, 0x2f, 0xc7, 0x82, 0xcd, 0x1b, 0x47, 0x53, 0x11, 0x1b, 0x17, 0x3b, 0x3b, 0x05, 0xd2, 0x2f, 0xa0, 0x80, 0x86, 0xe3, 0xb0, 0xf7, 0x12, 0xfc, 0xc7, 0xc7, 0x1a, 0x55, 0x7e, 0x2d, 0xb9, 0x66, 0xc3, 0xe9, 0xfa, 0x91, 0x74, 0x60, 0x39 } }, }; int i; unsigned char tmp[48]; hash_state md; for (i = 0; i < (int)(sizeof(tests) / sizeof(tests[0])); i++) { sha384_init(&md); sha384_process(&md, (unsigned char*)tests[i].msg, (unsigned long)strlen(tests[i].msg)); sha384_done(&md, tmp); if (XMEMCMP(tmp, tests[i].hash, 48) != 0) { return CRYPT_FAIL_TESTVECTOR; } } return CRYPT_OK; #endif } #endif /* defined(LTC_SHA384) && defined(LTC_SHA512) */ /* $Source$ */ /* $Revision$ */ /* $Date$ */ /* LibTomCrypt, modular cryptographic library -- Tom St Denis * * LibTomCrypt is a library that provides various cryptographic * algorithms in a highly modular and flexible manner. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tomstdenis@gmail.com, http://libtom.org */ /** @param sha512.c LTC_SHA512 by Tom St Denis */ #ifdef LTC_SHA512 const struct ltc_hash_descriptor sha512_desc = { "sha512", 5, 64, 128, /* OID */ { 2, 16, 840, 1, 101, 3, 4, 2, 3, }, 9, &sha512_init, &sha512_process, &sha512_done, &sha512_test, NULL }; /* the K array */ static const ulong64 K[80] = { CONST64(0x428a2f98d728ae22), CONST64(0x7137449123ef65cd), CONST64(0xb5c0fbcfec4d3b2f), CONST64(0xe9b5dba58189dbbc), CONST64(0x3956c25bf348b538), CONST64(0x59f111f1b605d019), CONST64(0x923f82a4af194f9b), CONST64(0xab1c5ed5da6d8118), CONST64(0xd807aa98a3030242), CONST64(0x12835b0145706fbe), CONST64(0x243185be4ee4b28c), CONST64(0x550c7dc3d5ffb4e2), CONST64(0x72be5d74f27b896f), CONST64(0x80deb1fe3b1696b1), CONST64(0x9bdc06a725c71235), CONST64(0xc19bf174cf692694), CONST64(0xe49b69c19ef14ad2), CONST64(0xefbe4786384f25e3), CONST64(0x0fc19dc68b8cd5b5), CONST64(0x240ca1cc77ac9c65), CONST64(0x2de92c6f592b0275), CONST64(0x4a7484aa6ea6e483), CONST64(0x5cb0a9dcbd41fbd4), CONST64(0x76f988da831153b5), CONST64(0x983e5152ee66dfab), CONST64(0xa831c66d2db43210), CONST64(0xb00327c898fb213f), CONST64(0xbf597fc7beef0ee4), CONST64(0xc6e00bf33da88fc2), CONST64(0xd5a79147930aa725), CONST64(0x06ca6351e003826f), CONST64(0x142929670a0e6e70), CONST64(0x27b70a8546d22ffc), CONST64(0x2e1b21385c26c926), CONST64(0x4d2c6dfc5ac42aed), CONST64(0x53380d139d95b3df), CONST64(0x650a73548baf63de), CONST64(0x766a0abb3c77b2a8), CONST64(0x81c2c92e47edaee6), CONST64(0x92722c851482353b), CONST64(0xa2bfe8a14cf10364), CONST64(0xa81a664bbc423001), CONST64(0xc24b8b70d0f89791), CONST64(0xc76c51a30654be30), CONST64(0xd192e819d6ef5218), CONST64(0xd69906245565a910), CONST64(0xf40e35855771202a), CONST64(0x106aa07032bbd1b8), CONST64(0x19a4c116b8d2d0c8), CONST64(0x1e376c085141ab53), CONST64(0x2748774cdf8eeb99), CONST64(0x34b0bcb5e19b48a8), CONST64(0x391c0cb3c5c95a63), CONST64(0x4ed8aa4ae3418acb), CONST64(0x5b9cca4f7763e373), CONST64(0x682e6ff3d6b2b8a3), CONST64(0x748f82ee5defb2fc), CONST64(0x78a5636f43172f60), CONST64(0x84c87814a1f0ab72), CONST64(0x8cc702081a6439ec), CONST64(0x90befffa23631e28), CONST64(0xa4506cebde82bde9), CONST64(0xbef9a3f7b2c67915), CONST64(0xc67178f2e372532b), CONST64(0xca273eceea26619c), CONST64(0xd186b8c721c0c207), CONST64(0xeada7dd6cde0eb1e), CONST64(0xf57d4f7fee6ed178), CONST64(0x06f067aa72176fba), CONST64(0x0a637dc5a2c898a6), CONST64(0x113f9804bef90dae), CONST64(0x1b710b35131c471b), CONST64(0x28db77f523047d84), CONST64(0x32caab7b40c72493), CONST64(0x3c9ebe0a15c9bebc), CONST64(0x431d67c49c100d4c), CONST64(0x4cc5d4becb3e42b6), CONST64(0x597f299cfc657e2a), CONST64(0x5fcb6fab3ad6faec), CONST64(0x6c44198c4a475817) }; /* Various logical functions */ #undef S #undef R #undef Sigma0 #undef Sigma1 #undef Gamma0 #undef Gamma1 #define Ch(x,y,z) (z ^ (x & (y ^ z))) #define Maj(x,y,z) (((x | y) & z) | (x & y)) #define S(x, n) ROR64c(x, n) #define R(x, n) (((x)&CONST64(0xFFFFFFFFFFFFFFFF))>>((ulong64)n)) #define Sigma0(x) (S(x, 28) ^ S(x, 34) ^ S(x, 39)) #define Sigma1(x) (S(x, 14) ^ S(x, 18) ^ S(x, 41)) #define Gamma0(x) (S(x, 1) ^ S(x, 8) ^ R(x, 7)) #define Gamma1(x) (S(x, 19) ^ S(x, 61) ^ R(x, 6)) /* compress 1024-bits */ #ifdef LTC_CLEAN_STACK static int _sha512_compress(hash_state * md, unsigned char *buf) #else static int sha512_compress(hash_state * md, unsigned char *buf) #endif { ulong64 S[8], W[80], t0, t1; int i; /* copy state into S */ for (i = 0; i < 8; i++) { S[i] = md->sha512.state[i]; } /* copy the state into 1024-bits into W[0..15] */ for (i = 0; i < 16; i++) { LOAD64H(W[i], buf + (8*i)); } /* fill W[16..79] */ for (i = 16; i < 80; i++) { W[i] = Gamma1(W[i - 2]) + W[i - 7] + Gamma0(W[i - 15]) + W[i - 16]; } /* Compress */ #ifdef LTC_SMALL_CODE for (i = 0; i < 80; i++) { t0 = S[7] + Sigma1(S[4]) + Ch(S[4], S[5], S[6]) + K[i] + W[i]; t1 = Sigma0(S[0]) + Maj(S[0], S[1], S[2]); S[7] = S[6]; S[6] = S[5]; S[5] = S[4]; S[4] = S[3] + t0; S[3] = S[2]; S[2] = S[1]; S[1] = S[0]; S[0] = t0 + t1; } #else #define RND(a,b,c,d,e,f,g,h,i) \ t0 = h + Sigma1(e) + Ch(e, f, g) + K[i] + W[i]; \ t1 = Sigma0(a) + Maj(a, b, c); \ d += t0; \ h = t0 + t1; for (i = 0; i < 80; i += 8) { RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],i+0); RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],i+1); RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],i+2); RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],i+3); RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],i+4); RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],i+5); RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],i+6); RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],i+7); } #endif /* feedback */ for (i = 0; i < 8; i++) { md->sha512.state[i] = md->sha512.state[i] + S[i]; } return CRYPT_OK; } /* compress 1024-bits */ #ifdef LTC_CLEAN_STACK static int sha512_compress(hash_state * md, unsigned char *buf) { int err; err = _sha512_compress(md, buf); burn_stack(sizeof(ulong64) * 90 + sizeof(int)); return err; } #endif /** Initialize the hash state @param md The hash state you wish to initialize @return CRYPT_OK if successful */ int sha512_init(hash_state * md) { LTC_ARGCHK(md != NULL); md->sha512.curlen = 0; md->sha512.length = 0; md->sha512.state[0] = CONST64(0x6a09e667f3bcc908); md->sha512.state[1] = CONST64(0xbb67ae8584caa73b); md->sha512.state[2] = CONST64(0x3c6ef372fe94f82b); md->sha512.state[3] = CONST64(0xa54ff53a5f1d36f1); md->sha512.state[4] = CONST64(0x510e527fade682d1); md->sha512.state[5] = CONST64(0x9b05688c2b3e6c1f); md->sha512.state[6] = CONST64(0x1f83d9abfb41bd6b); md->sha512.state[7] = CONST64(0x5be0cd19137e2179); return CRYPT_OK; } /** Process a block of memory though the hash @param md The hash state @param in The data to hash @param inlen The length of the data (octets) @return CRYPT_OK if successful */ HASH_PROCESS(sha512_process, sha512_compress, sha512, 128) /** Terminate the hash to get the digest @param md The hash state @param out [out] The destination of the hash (64 bytes) @return CRYPT_OK if successful */ int sha512_done(hash_state * md, unsigned char *out) { int i; LTC_ARGCHK(md != NULL); LTC_ARGCHK(out != NULL); if (md->sha512.curlen >= sizeof(md->sha512.buf)) { return CRYPT_INVALID_ARG; } /* increase the length of the message */ md->sha512.length += md->sha512.curlen * CONST64(8); /* append the '1' bit */ md->sha512.buf[md->sha512.curlen++] = (unsigned char)0x80; /* if the length is currently above 112 bytes we append zeros * then compress. Then we can fall back to padding zeros and length * encoding like normal. */ if (md->sha512.curlen > 112) { while (md->sha512.curlen < 128) { md->sha512.buf[md->sha512.curlen++] = (unsigned char)0; } sha512_compress(md, md->sha512.buf); md->sha512.curlen = 0; } /* pad upto 120 bytes of zeroes * note: that from 112 to 120 is the 64 MSB of the length. We assume that you won't hash * > 2^64 bits of data... :-) */ while (md->sha512.curlen < 120) { md->sha512.buf[md->sha512.curlen++] = (unsigned char)0; } /* store length */ STORE64H(md->sha512.length, md->sha512.buf+120); sha512_compress(md, md->sha512.buf); /* copy output */ for (i = 0; i < 8; i++) { STORE64H(md->sha512.state[i], out+(8*i)); } #ifdef LTC_CLEAN_STACK zeromem(md, sizeof(hash_state)); #endif return CRYPT_OK; } /** Self-test the hash @return CRYPT_OK if successful, CRYPT_NOP if self-tests have been disabled */ int sha512_test(void) { #ifndef LTC_TEST return CRYPT_NOP; #else static const struct { char *msg; unsigned char hash[64]; } tests[] = { { "abc", { 0xdd, 0xaf, 0x35, 0xa1, 0x93, 0x61, 0x7a, 0xba, 0xcc, 0x41, 0x73, 0x49, 0xae, 0x20, 0x41, 0x31, 0x12, 0xe6, 0xfa, 0x4e, 0x89, 0xa9, 0x7e, 0xa2, 0x0a, 0x9e, 0xee, 0xe6, 0x4b, 0x55, 0xd3, 0x9a, 0x21, 0x92, 0x99, 0x2a, 0x27, 0x4f, 0xc1, 0xa8, 0x36, 0xba, 0x3c, 0x23, 0xa3, 0xfe, 0xeb, 0xbd, 0x45, 0x4d, 0x44, 0x23, 0x64, 0x3c, 0xe8, 0x0e, 0x2a, 0x9a, 0xc9, 0x4f, 0xa5, 0x4c, 0xa4, 0x9f } }, { "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu", { 0x8e, 0x95, 0x9b, 0x75, 0xda, 0xe3, 0x13, 0xda, 0x8c, 0xf4, 0xf7, 0x28, 0x14, 0xfc, 0x14, 0x3f, 0x8f, 0x77, 0x79, 0xc6, 0xeb, 0x9f, 0x7f, 0xa1, 0x72, 0x99, 0xae, 0xad, 0xb6, 0x88, 0x90, 0x18, 0x50, 0x1d, 0x28, 0x9e, 0x49, 0x00, 0xf7, 0xe4, 0x33, 0x1b, 0x99, 0xde, 0xc4, 0xb5, 0x43, 0x3a, 0xc7, 0xd3, 0x29, 0xee, 0xb6, 0xdd, 0x26, 0x54, 0x5e, 0x96, 0xe5, 0x5b, 0x87, 0x4b, 0xe9, 0x09 } }, }; int i; unsigned char tmp[64]; hash_state md; for (i = 0; i < (int)(sizeof(tests) / sizeof(tests[0])); i++) { sha512_init(&md); sha512_process(&md, (unsigned char *)tests[i].msg, (unsigned long)strlen(tests[i].msg)); sha512_done(&md, tmp); if (XMEMCMP(tmp, tests[i].hash, 64) != 0) { return CRYPT_FAIL_TESTVECTOR; } } return CRYPT_OK; #endif } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ /* LibTomCrypt, modular cryptographic library -- Tom St Denis * * LibTomCrypt is a library that provides various cryptographic * algorithms in a highly modular and flexible manner. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tomstdenis@gmail.com, http://libtom.org */ /** @file hmac_init.c HMAC support, initialize state, Tom St Denis/Dobes Vandermeer */ #ifdef LTC_HMAC #define LTC_HMAC_BLOCKSIZE hash_descriptor[hash].blocksize /** Initialize an HMAC context. @param hmac The HMAC state @param hash The index of the hash you want to use @param key The secret key @param keylen The length of the secret key (octets) @return CRYPT_OK if successful */ int hmac_init(hmac_state *hmac, int hash, const unsigned char *key, unsigned long keylen) { unsigned char *buf; unsigned long hashsize; unsigned long i, z; int err; LTC_ARGCHK(hmac != NULL); LTC_ARGCHK(key != NULL); /* valid hash? */ if ((err = hash_is_valid(hash)) != CRYPT_OK) { return err; } hmac->hash = hash; hashsize = hash_descriptor[hash].hashsize; /* valid key length? */ if (keylen == 0) { return CRYPT_INVALID_KEYSIZE; } /* allocate ram for buf */ buf = AUTO_CAST(XMALLOC(LTC_HMAC_BLOCKSIZE)); if (buf == NULL) { return CRYPT_MEM; } /* allocate memory for key */ hmac->key = AUTO_CAST(XMALLOC(LTC_HMAC_BLOCKSIZE)); if (hmac->key == NULL) { XFREE(buf); return CRYPT_MEM; } /* (1) make sure we have a large enough key */ if(keylen > LTC_HMAC_BLOCKSIZE) { z = LTC_HMAC_BLOCKSIZE; if ((err = hash_memory(hash, key, keylen, hmac->key, &z)) != CRYPT_OK) { goto LBL_ERR; } keylen = hashsize; } else { XMEMCPY(hmac->key, key, (size_t)keylen); } if(keylen < LTC_HMAC_BLOCKSIZE) { zeromem((hmac->key) + keylen, (size_t)(LTC_HMAC_BLOCKSIZE - keylen)); } /* Create the initial vector for step (3) */ for(i=0; i < LTC_HMAC_BLOCKSIZE; i++) { buf[i] = hmac->key[i] ^ 0x36; } /* Pre-pend that to the hash data */ if ((err = hash_descriptor[hash].init(&hmac->md)) != CRYPT_OK) { goto LBL_ERR; } if ((err = hash_descriptor[hash].process(&hmac->md, buf, LTC_HMAC_BLOCKSIZE)) != CRYPT_OK) { goto LBL_ERR; } goto done; LBL_ERR: /* free the key since we failed */ XFREE(hmac->key); done: #ifdef LTC_CLEAN_STACK zeromem(buf, LTC_HMAC_BLOCKSIZE); #endif XFREE(buf); return err; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ /* LibTomCrypt, modular cryptographic library -- Tom St Denis * * LibTomCrypt is a library that provides various cryptographic * algorithms in a highly modular and flexible manner. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tomstdenis@gmail.com, http://libtom.org */ /** @file hmac_process.c HMAC support, process data, Tom St Denis/Dobes Vandermeer */ #ifdef LTC_HMAC /** Process data through HMAC @param hmac The hmac state @param in The data to send through HMAC @param inlen The length of the data to HMAC (octets) @return CRYPT_OK if successful */ int hmac_process(hmac_state *hmac, const unsigned char *in, unsigned long inlen) { int err; LTC_ARGCHK(hmac != NULL); LTC_ARGCHK(in != NULL); if ((err = hash_is_valid(hmac->hash)) != CRYPT_OK) { return err; } return hash_descriptor[hmac->hash].process(&hmac->md, in, inlen); } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ /* LibTomCrypt, modular cryptographic library -- Tom St Denis * * LibTomCrypt is a library that provides various cryptographic * algorithms in a highly modular and flexible manner. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tomstdenis@gmail.com, http://libtom.org */ /** @file hmac_done.c HMAC support, terminate stream, Tom St Denis/Dobes Vandermeer */ #ifdef LTC_HMAC #define LTC_HMAC_BLOCKSIZE hash_descriptor[hash].blocksize /** Terminate an HMAC session @param hmac The HMAC state @param out [out] The destination of the HMAC authentication tag @param outlen [in/out] The max size and resulting size of the HMAC authentication tag @return CRYPT_OK if successful */ int hmac_done(hmac_state *hmac, unsigned char *out, unsigned long *outlen) { unsigned char *buf, *isha; unsigned long hashsize, i; int hash, err; LTC_ARGCHK(hmac != NULL); LTC_ARGCHK(out != NULL); /* test hash */ hash = hmac->hash; if((err = hash_is_valid(hash)) != CRYPT_OK) { return err; } /* get the hash message digest size */ hashsize = hash_descriptor[hash].hashsize; /* allocate buffers */ buf = AUTO_CAST(XMALLOC(LTC_HMAC_BLOCKSIZE)); isha = AUTO_CAST(XMALLOC(hashsize)); if (buf == NULL || isha == NULL) { if (buf != NULL) { XFREE(buf); } if (isha != NULL) { XFREE(isha); } return CRYPT_MEM; } /* Get the hash of the first HMAC vector plus the data */ if ((err = hash_descriptor[hash].done(&hmac->md, isha)) != CRYPT_OK) { goto LBL_ERR; } /* Create the second HMAC vector vector for step (3) */ for(i=0; i < LTC_HMAC_BLOCKSIZE; i++) { buf[i] = hmac->key[i] ^ 0x5C; } /* Now calculate the "outer" hash for step (5), (6), and (7) */ if ((err = hash_descriptor[hash].init(&hmac->md)) != CRYPT_OK) { goto LBL_ERR; } if ((err = hash_descriptor[hash].process(&hmac->md, buf, LTC_HMAC_BLOCKSIZE)) != CRYPT_OK) { goto LBL_ERR; } if ((err = hash_descriptor[hash].process(&hmac->md, isha, hashsize)) != CRYPT_OK) { goto LBL_ERR; } if ((err = hash_descriptor[hash].done(&hmac->md, buf)) != CRYPT_OK) { goto LBL_ERR; } /* copy to output */ for (i = 0; i < hashsize && i < *outlen; i++) { out[i] = buf[i]; } *outlen = i; err = CRYPT_OK; LBL_ERR: XFREE(hmac->key); #ifdef LTC_CLEAN_STACK zeromem(isha, hashsize); zeromem(buf, hashsize); zeromem(hmac, sizeof(*hmac)); #endif XFREE(isha); XFREE(buf); return err; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ #define __LTC_AES_TAB_C__ /* LibTomCrypt, modular cryptographic library -- Tom St Denis * * LibTomCrypt is a library that provides various cryptographic * algorithms in a highly modular and flexible manner. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tomstdenis@gmail.com, http://libtom.org */ /* The precomputed tables for AES */ /* Te0[x] = S [x].[02, 01, 01, 03]; Te1[x] = S [x].[03, 02, 01, 01]; Te2[x] = S [x].[01, 03, 02, 01]; Te3[x] = S [x].[01, 01, 03, 02]; Te4[x] = S [x].[01, 01, 01, 01]; Td0[x] = Si[x].[0e, 09, 0d, 0b]; Td1[x] = Si[x].[0b, 0e, 09, 0d]; Td2[x] = Si[x].[0d, 0b, 0e, 09]; Td3[x] = Si[x].[09, 0d, 0b, 0e]; Td4[x] = Si[x].[01, 01, 01, 01]; */ #ifdef __LTC_AES_TAB_C__ /** @file aes_tab.c AES tables */ static const ulong32 TE0[256] = { 0xc66363a5UL, 0xf87c7c84UL, 0xee777799UL, 0xf67b7b8dUL, 0xfff2f20dUL, 0xd66b6bbdUL, 0xde6f6fb1UL, 0x91c5c554UL, 0x60303050UL, 0x02010103UL, 0xce6767a9UL, 0x562b2b7dUL, 0xe7fefe19UL, 0xb5d7d762UL, 0x4dababe6UL, 0xec76769aUL, 0x8fcaca45UL, 0x1f82829dUL, 0x89c9c940UL, 0xfa7d7d87UL, 0xeffafa15UL, 0xb25959ebUL, 0x8e4747c9UL, 0xfbf0f00bUL, 0x41adadecUL, 0xb3d4d467UL, 0x5fa2a2fdUL, 0x45afafeaUL, 0x239c9cbfUL, 0x53a4a4f7UL, 0xe4727296UL, 0x9bc0c05bUL, 0x75b7b7c2UL, 0xe1fdfd1cUL, 0x3d9393aeUL, 0x4c26266aUL, 0x6c36365aUL, 0x7e3f3f41UL, 0xf5f7f702UL, 0x83cccc4fUL, 0x6834345cUL, 0x51a5a5f4UL, 0xd1e5e534UL, 0xf9f1f108UL, 0xe2717193UL, 0xabd8d873UL, 0x62313153UL, 0x2a15153fUL, 0x0804040cUL, 0x95c7c752UL, 0x46232365UL, 0x9dc3c35eUL, 0x30181828UL, 0x379696a1UL, 0x0a05050fUL, 0x2f9a9ab5UL, 0x0e070709UL, 0x24121236UL, 0x1b80809bUL, 0xdfe2e23dUL, 0xcdebeb26UL, 0x4e272769UL, 0x7fb2b2cdUL, 0xea75759fUL, 0x1209091bUL, 0x1d83839eUL, 0x582c2c74UL, 0x341a1a2eUL, 0x361b1b2dUL, 0xdc6e6eb2UL, 0xb45a5aeeUL, 0x5ba0a0fbUL, 0xa45252f6UL, 0x763b3b4dUL, 0xb7d6d661UL, 0x7db3b3ceUL, 0x5229297bUL, 0xdde3e33eUL, 0x5e2f2f71UL, 0x13848497UL, 0xa65353f5UL, 0xb9d1d168UL, 0x00000000UL, 0xc1eded2cUL, 0x40202060UL, 0xe3fcfc1fUL, 0x79b1b1c8UL, 0xb65b5bedUL, 0xd46a6abeUL, 0x8dcbcb46UL, 0x67bebed9UL, 0x7239394bUL, 0x944a4adeUL, 0x984c4cd4UL, 0xb05858e8UL, 0x85cfcf4aUL, 0xbbd0d06bUL, 0xc5efef2aUL, 0x4faaaae5UL, 0xedfbfb16UL, 0x864343c5UL, 0x9a4d4dd7UL, 0x66333355UL, 0x11858594UL, 0x8a4545cfUL, 0xe9f9f910UL, 0x04020206UL, 0xfe7f7f81UL, 0xa05050f0UL, 0x783c3c44UL, 0x259f9fbaUL, 0x4ba8a8e3UL, 0xa25151f3UL, 0x5da3a3feUL, 0x804040c0UL, 0x058f8f8aUL, 0x3f9292adUL, 0x219d9dbcUL, 0x70383848UL, 0xf1f5f504UL, 0x63bcbcdfUL, 0x77b6b6c1UL, 0xafdada75UL, 0x42212163UL, 0x20101030UL, 0xe5ffff1aUL, 0xfdf3f30eUL, 0xbfd2d26dUL, 0x81cdcd4cUL, 0x180c0c14UL, 0x26131335UL, 0xc3ecec2fUL, 0xbe5f5fe1UL, 0x359797a2UL, 0x884444ccUL, 0x2e171739UL, 0x93c4c457UL, 0x55a7a7f2UL, 0xfc7e7e82UL, 0x7a3d3d47UL, 0xc86464acUL, 0xba5d5de7UL, 0x3219192bUL, 0xe6737395UL, 0xc06060a0UL, 0x19818198UL, 0x9e4f4fd1UL, 0xa3dcdc7fUL, 0x44222266UL, 0x542a2a7eUL, 0x3b9090abUL, 0x0b888883UL, 0x8c4646caUL, 0xc7eeee29UL, 0x6bb8b8d3UL, 0x2814143cUL, 0xa7dede79UL, 0xbc5e5ee2UL, 0x160b0b1dUL, 0xaddbdb76UL, 0xdbe0e03bUL, 0x64323256UL, 0x743a3a4eUL, 0x140a0a1eUL, 0x924949dbUL, 0x0c06060aUL, 0x4824246cUL, 0xb85c5ce4UL, 0x9fc2c25dUL, 0xbdd3d36eUL, 0x43acacefUL, 0xc46262a6UL, 0x399191a8UL, 0x319595a4UL, 0xd3e4e437UL, 0xf279798bUL, 0xd5e7e732UL, 0x8bc8c843UL, 0x6e373759UL, 0xda6d6db7UL, 0x018d8d8cUL, 0xb1d5d564UL, 0x9c4e4ed2UL, 0x49a9a9e0UL, 0xd86c6cb4UL, 0xac5656faUL, 0xf3f4f407UL, 0xcfeaea25UL, 0xca6565afUL, 0xf47a7a8eUL, 0x47aeaee9UL, 0x10080818UL, 0x6fbabad5UL, 0xf0787888UL, 0x4a25256fUL, 0x5c2e2e72UL, 0x381c1c24UL, 0x57a6a6f1UL, 0x73b4b4c7UL, 0x97c6c651UL, 0xcbe8e823UL, 0xa1dddd7cUL, 0xe874749cUL, 0x3e1f1f21UL, 0x964b4bddUL, 0x61bdbddcUL, 0x0d8b8b86UL, 0x0f8a8a85UL, 0xe0707090UL, 0x7c3e3e42UL, 0x71b5b5c4UL, 0xcc6666aaUL, 0x904848d8UL, 0x06030305UL, 0xf7f6f601UL, 0x1c0e0e12UL, 0xc26161a3UL, 0x6a35355fUL, 0xae5757f9UL, 0x69b9b9d0UL, 0x17868691UL, 0x99c1c158UL, 0x3a1d1d27UL, 0x279e9eb9UL, 0xd9e1e138UL, 0xebf8f813UL, 0x2b9898b3UL, 0x22111133UL, 0xd26969bbUL, 0xa9d9d970UL, 0x078e8e89UL, 0x339494a7UL, 0x2d9b9bb6UL, 0x3c1e1e22UL, 0x15878792UL, 0xc9e9e920UL, 0x87cece49UL, 0xaa5555ffUL, 0x50282878UL, 0xa5dfdf7aUL, 0x038c8c8fUL, 0x59a1a1f8UL, 0x09898980UL, 0x1a0d0d17UL, 0x65bfbfdaUL, 0xd7e6e631UL, 0x844242c6UL, 0xd06868b8UL, 0x824141c3UL, 0x299999b0UL, 0x5a2d2d77UL, 0x1e0f0f11UL, 0x7bb0b0cbUL, 0xa85454fcUL, 0x6dbbbbd6UL, 0x2c16163aUL, }; #ifndef PELI_TAB static const ulong32 Te4[256] = { 0x63636363UL, 0x7c7c7c7cUL, 0x77777777UL, 0x7b7b7b7bUL, 0xf2f2f2f2UL, 0x6b6b6b6bUL, 0x6f6f6f6fUL, 0xc5c5c5c5UL, 0x30303030UL, 0x01010101UL, 0x67676767UL, 0x2b2b2b2bUL, 0xfefefefeUL, 0xd7d7d7d7UL, 0xababababUL, 0x76767676UL, 0xcacacacaUL, 0x82828282UL, 0xc9c9c9c9UL, 0x7d7d7d7dUL, 0xfafafafaUL, 0x59595959UL, 0x47474747UL, 0xf0f0f0f0UL, 0xadadadadUL, 0xd4d4d4d4UL, 0xa2a2a2a2UL, 0xafafafafUL, 0x9c9c9c9cUL, 0xa4a4a4a4UL, 0x72727272UL, 0xc0c0c0c0UL, 0xb7b7b7b7UL, 0xfdfdfdfdUL, 0x93939393UL, 0x26262626UL, 0x36363636UL, 0x3f3f3f3fUL, 0xf7f7f7f7UL, 0xccccccccUL, 0x34343434UL, 0xa5a5a5a5UL, 0xe5e5e5e5UL, 0xf1f1f1f1UL, 0x71717171UL, 0xd8d8d8d8UL, 0x31313131UL, 0x15151515UL, 0x04040404UL, 0xc7c7c7c7UL, 0x23232323UL, 0xc3c3c3c3UL, 0x18181818UL, 0x96969696UL, 0x05050505UL, 0x9a9a9a9aUL, 0x07070707UL, 0x12121212UL, 0x80808080UL, 0xe2e2e2e2UL, 0xebebebebUL, 0x27272727UL, 0xb2b2b2b2UL, 0x75757575UL, 0x09090909UL, 0x83838383UL, 0x2c2c2c2cUL, 0x1a1a1a1aUL, 0x1b1b1b1bUL, 0x6e6e6e6eUL, 0x5a5a5a5aUL, 0xa0a0a0a0UL, 0x52525252UL, 0x3b3b3b3bUL, 0xd6d6d6d6UL, 0xb3b3b3b3UL, 0x29292929UL, 0xe3e3e3e3UL, 0x2f2f2f2fUL, 0x84848484UL, 0x53535353UL, 0xd1d1d1d1UL, 0x00000000UL, 0xededededUL, 0x20202020UL, 0xfcfcfcfcUL, 0xb1b1b1b1UL, 0x5b5b5b5bUL, 0x6a6a6a6aUL, 0xcbcbcbcbUL, 0xbebebebeUL, 0x39393939UL, 0x4a4a4a4aUL, 0x4c4c4c4cUL, 0x58585858UL, 0xcfcfcfcfUL, 0xd0d0d0d0UL, 0xefefefefUL, 0xaaaaaaaaUL, 0xfbfbfbfbUL, 0x43434343UL, 0x4d4d4d4dUL, 0x33333333UL, 0x85858585UL, 0x45454545UL, 0xf9f9f9f9UL, 0x02020202UL, 0x7f7f7f7fUL, 0x50505050UL, 0x3c3c3c3cUL, 0x9f9f9f9fUL, 0xa8a8a8a8UL, 0x51515151UL, 0xa3a3a3a3UL, 0x40404040UL, 0x8f8f8f8fUL, 0x92929292UL, 0x9d9d9d9dUL, 0x38383838UL, 0xf5f5f5f5UL, 0xbcbcbcbcUL, 0xb6b6b6b6UL, 0xdadadadaUL, 0x21212121UL, 0x10101010UL, 0xffffffffUL, 0xf3f3f3f3UL, 0xd2d2d2d2UL, 0xcdcdcdcdUL, 0x0c0c0c0cUL, 0x13131313UL, 0xececececUL, 0x5f5f5f5fUL, 0x97979797UL, 0x44444444UL, 0x17171717UL, 0xc4c4c4c4UL, 0xa7a7a7a7UL, 0x7e7e7e7eUL, 0x3d3d3d3dUL, 0x64646464UL, 0x5d5d5d5dUL, 0x19191919UL, 0x73737373UL, 0x60606060UL, 0x81818181UL, 0x4f4f4f4fUL, 0xdcdcdcdcUL, 0x22222222UL, 0x2a2a2a2aUL, 0x90909090UL, 0x88888888UL, 0x46464646UL, 0xeeeeeeeeUL, 0xb8b8b8b8UL, 0x14141414UL, 0xdedededeUL, 0x5e5e5e5eUL, 0x0b0b0b0bUL, 0xdbdbdbdbUL, 0xe0e0e0e0UL, 0x32323232UL, 0x3a3a3a3aUL, 0x0a0a0a0aUL, 0x49494949UL, 0x06060606UL, 0x24242424UL, 0x5c5c5c5cUL, 0xc2c2c2c2UL, 0xd3d3d3d3UL, 0xacacacacUL, 0x62626262UL, 0x91919191UL, 0x95959595UL, 0xe4e4e4e4UL, 0x79797979UL, 0xe7e7e7e7UL, 0xc8c8c8c8UL, 0x37373737UL, 0x6d6d6d6dUL, 0x8d8d8d8dUL, 0xd5d5d5d5UL, 0x4e4e4e4eUL, 0xa9a9a9a9UL, 0x6c6c6c6cUL, 0x56565656UL, 0xf4f4f4f4UL, 0xeaeaeaeaUL, 0x65656565UL, 0x7a7a7a7aUL, 0xaeaeaeaeUL, 0x08080808UL, 0xbabababaUL, 0x78787878UL, 0x25252525UL, 0x2e2e2e2eUL, 0x1c1c1c1cUL, 0xa6a6a6a6UL, 0xb4b4b4b4UL, 0xc6c6c6c6UL, 0xe8e8e8e8UL, 0xddddddddUL, 0x74747474UL, 0x1f1f1f1fUL, 0x4b4b4b4bUL, 0xbdbdbdbdUL, 0x8b8b8b8bUL, 0x8a8a8a8aUL, 0x70707070UL, 0x3e3e3e3eUL, 0xb5b5b5b5UL, 0x66666666UL, 0x48484848UL, 0x03030303UL, 0xf6f6f6f6UL, 0x0e0e0e0eUL, 0x61616161UL, 0x35353535UL, 0x57575757UL, 0xb9b9b9b9UL, 0x86868686UL, 0xc1c1c1c1UL, 0x1d1d1d1dUL, 0x9e9e9e9eUL, 0xe1e1e1e1UL, 0xf8f8f8f8UL, 0x98989898UL, 0x11111111UL, 0x69696969UL, 0xd9d9d9d9UL, 0x8e8e8e8eUL, 0x94949494UL, 0x9b9b9b9bUL, 0x1e1e1e1eUL, 0x87878787UL, 0xe9e9e9e9UL, 0xcecececeUL, 0x55555555UL, 0x28282828UL, 0xdfdfdfdfUL, 0x8c8c8c8cUL, 0xa1a1a1a1UL, 0x89898989UL, 0x0d0d0d0dUL, 0xbfbfbfbfUL, 0xe6e6e6e6UL, 0x42424242UL, 0x68686868UL, 0x41414141UL, 0x99999999UL, 0x2d2d2d2dUL, 0x0f0f0f0fUL, 0xb0b0b0b0UL, 0x54545454UL, 0xbbbbbbbbUL, 0x16161616UL, }; #endif #ifndef ENCRYPT_ONLY static const ulong32 TD0[256] = { 0x51f4a750UL, 0x7e416553UL, 0x1a17a4c3UL, 0x3a275e96UL, 0x3bab6bcbUL, 0x1f9d45f1UL, 0xacfa58abUL, 0x4be30393UL, 0x2030fa55UL, 0xad766df6UL, 0x88cc7691UL, 0xf5024c25UL, 0x4fe5d7fcUL, 0xc52acbd7UL, 0x26354480UL, 0xb562a38fUL, 0xdeb15a49UL, 0x25ba1b67UL, 0x45ea0e98UL, 0x5dfec0e1UL, 0xc32f7502UL, 0x814cf012UL, 0x8d4697a3UL, 0x6bd3f9c6UL, 0x038f5fe7UL, 0x15929c95UL, 0xbf6d7aebUL, 0x955259daUL, 0xd4be832dUL, 0x587421d3UL, 0x49e06929UL, 0x8ec9c844UL, 0x75c2896aUL, 0xf48e7978UL, 0x99583e6bUL, 0x27b971ddUL, 0xbee14fb6UL, 0xf088ad17UL, 0xc920ac66UL, 0x7dce3ab4UL, 0x63df4a18UL, 0xe51a3182UL, 0x97513360UL, 0x62537f45UL, 0xb16477e0UL, 0xbb6bae84UL, 0xfe81a01cUL, 0xf9082b94UL, 0x70486858UL, 0x8f45fd19UL, 0x94de6c87UL, 0x527bf8b7UL, 0xab73d323UL, 0x724b02e2UL, 0xe31f8f57UL, 0x6655ab2aUL, 0xb2eb2807UL, 0x2fb5c203UL, 0x86c57b9aUL, 0xd33708a5UL, 0x302887f2UL, 0x23bfa5b2UL, 0x02036abaUL, 0xed16825cUL, 0x8acf1c2bUL, 0xa779b492UL, 0xf307f2f0UL, 0x4e69e2a1UL, 0x65daf4cdUL, 0x0605bed5UL, 0xd134621fUL, 0xc4a6fe8aUL, 0x342e539dUL, 0xa2f355a0UL, 0x058ae132UL, 0xa4f6eb75UL, 0x0b83ec39UL, 0x4060efaaUL, 0x5e719f06UL, 0xbd6e1051UL, 0x3e218af9UL, 0x96dd063dUL, 0xdd3e05aeUL, 0x4de6bd46UL, 0x91548db5UL, 0x71c45d05UL, 0x0406d46fUL, 0x605015ffUL, 0x1998fb24UL, 0xd6bde997UL, 0x894043ccUL, 0x67d99e77UL, 0xb0e842bdUL, 0x07898b88UL, 0xe7195b38UL, 0x79c8eedbUL, 0xa17c0a47UL, 0x7c420fe9UL, 0xf8841ec9UL, 0x00000000UL, 0x09808683UL, 0x322bed48UL, 0x1e1170acUL, 0x6c5a724eUL, 0xfd0efffbUL, 0x0f853856UL, 0x3daed51eUL, 0x362d3927UL, 0x0a0fd964UL, 0x685ca621UL, 0x9b5b54d1UL, 0x24362e3aUL, 0x0c0a67b1UL, 0x9357e70fUL, 0xb4ee96d2UL, 0x1b9b919eUL, 0x80c0c54fUL, 0x61dc20a2UL, 0x5a774b69UL, 0x1c121a16UL, 0xe293ba0aUL, 0xc0a02ae5UL, 0x3c22e043UL, 0x121b171dUL, 0x0e090d0bUL, 0xf28bc7adUL, 0x2db6a8b9UL, 0x141ea9c8UL, 0x57f11985UL, 0xaf75074cUL, 0xee99ddbbUL, 0xa37f60fdUL, 0xf701269fUL, 0x5c72f5bcUL, 0x44663bc5UL, 0x5bfb7e34UL, 0x8b432976UL, 0xcb23c6dcUL, 0xb6edfc68UL, 0xb8e4f163UL, 0xd731dccaUL, 0x42638510UL, 0x13972240UL, 0x84c61120UL, 0x854a247dUL, 0xd2bb3df8UL, 0xaef93211UL, 0xc729a16dUL, 0x1d9e2f4bUL, 0xdcb230f3UL, 0x0d8652ecUL, 0x77c1e3d0UL, 0x2bb3166cUL, 0xa970b999UL, 0x119448faUL, 0x47e96422UL, 0xa8fc8cc4UL, 0xa0f03f1aUL, 0x567d2cd8UL, 0x223390efUL, 0x87494ec7UL, 0xd938d1c1UL, 0x8ccaa2feUL, 0x98d40b36UL, 0xa6f581cfUL, 0xa57ade28UL, 0xdab78e26UL, 0x3fadbfa4UL, 0x2c3a9de4UL, 0x5078920dUL, 0x6a5fcc9bUL, 0x547e4662UL, 0xf68d13c2UL, 0x90d8b8e8UL, 0x2e39f75eUL, 0x82c3aff5UL, 0x9f5d80beUL, 0x69d0937cUL, 0x6fd52da9UL, 0xcf2512b3UL, 0xc8ac993bUL, 0x10187da7UL, 0xe89c636eUL, 0xdb3bbb7bUL, 0xcd267809UL, 0x6e5918f4UL, 0xec9ab701UL, 0x834f9aa8UL, 0xe6956e65UL, 0xaaffe67eUL, 0x21bccf08UL, 0xef15e8e6UL, 0xbae79bd9UL, 0x4a6f36ceUL, 0xea9f09d4UL, 0x29b07cd6UL, 0x31a4b2afUL, 0x2a3f2331UL, 0xc6a59430UL, 0x35a266c0UL, 0x744ebc37UL, 0xfc82caa6UL, 0xe090d0b0UL, 0x33a7d815UL, 0xf104984aUL, 0x41ecdaf7UL, 0x7fcd500eUL, 0x1791f62fUL, 0x764dd68dUL, 0x43efb04dUL, 0xccaa4d54UL, 0xe49604dfUL, 0x9ed1b5e3UL, 0x4c6a881bUL, 0xc12c1fb8UL, 0x4665517fUL, 0x9d5eea04UL, 0x018c355dUL, 0xfa877473UL, 0xfb0b412eUL, 0xb3671d5aUL, 0x92dbd252UL, 0xe9105633UL, 0x6dd64713UL, 0x9ad7618cUL, 0x37a10c7aUL, 0x59f8148eUL, 0xeb133c89UL, 0xcea927eeUL, 0xb761c935UL, 0xe11ce5edUL, 0x7a47b13cUL, 0x9cd2df59UL, 0x55f2733fUL, 0x1814ce79UL, 0x73c737bfUL, 0x53f7cdeaUL, 0x5ffdaa5bUL, 0xdf3d6f14UL, 0x7844db86UL, 0xcaaff381UL, 0xb968c43eUL, 0x3824342cUL, 0xc2a3405fUL, 0x161dc372UL, 0xbce2250cUL, 0x283c498bUL, 0xff0d9541UL, 0x39a80171UL, 0x080cb3deUL, 0xd8b4e49cUL, 0x6456c190UL, 0x7bcb8461UL, 0xd532b670UL, 0x486c5c74UL, 0xd0b85742UL, }; static const ulong32 Td4[256] = { 0x52525252UL, 0x09090909UL, 0x6a6a6a6aUL, 0xd5d5d5d5UL, 0x30303030UL, 0x36363636UL, 0xa5a5a5a5UL, 0x38383838UL, 0xbfbfbfbfUL, 0x40404040UL, 0xa3a3a3a3UL, 0x9e9e9e9eUL, 0x81818181UL, 0xf3f3f3f3UL, 0xd7d7d7d7UL, 0xfbfbfbfbUL, 0x7c7c7c7cUL, 0xe3e3e3e3UL, 0x39393939UL, 0x82828282UL, 0x9b9b9b9bUL, 0x2f2f2f2fUL, 0xffffffffUL, 0x87878787UL, 0x34343434UL, 0x8e8e8e8eUL, 0x43434343UL, 0x44444444UL, 0xc4c4c4c4UL, 0xdedededeUL, 0xe9e9e9e9UL, 0xcbcbcbcbUL, 0x54545454UL, 0x7b7b7b7bUL, 0x94949494UL, 0x32323232UL, 0xa6a6a6a6UL, 0xc2c2c2c2UL, 0x23232323UL, 0x3d3d3d3dUL, 0xeeeeeeeeUL, 0x4c4c4c4cUL, 0x95959595UL, 0x0b0b0b0bUL, 0x42424242UL, 0xfafafafaUL, 0xc3c3c3c3UL, 0x4e4e4e4eUL, 0x08080808UL, 0x2e2e2e2eUL, 0xa1a1a1a1UL, 0x66666666UL, 0x28282828UL, 0xd9d9d9d9UL, 0x24242424UL, 0xb2b2b2b2UL, 0x76767676UL, 0x5b5b5b5bUL, 0xa2a2a2a2UL, 0x49494949UL, 0x6d6d6d6dUL, 0x8b8b8b8bUL, 0xd1d1d1d1UL, 0x25252525UL, 0x72727272UL, 0xf8f8f8f8UL, 0xf6f6f6f6UL, 0x64646464UL, 0x86868686UL, 0x68686868UL, 0x98989898UL, 0x16161616UL, 0xd4d4d4d4UL, 0xa4a4a4a4UL, 0x5c5c5c5cUL, 0xccccccccUL, 0x5d5d5d5dUL, 0x65656565UL, 0xb6b6b6b6UL, 0x92929292UL, 0x6c6c6c6cUL, 0x70707070UL, 0x48484848UL, 0x50505050UL, 0xfdfdfdfdUL, 0xededededUL, 0xb9b9b9b9UL, 0xdadadadaUL, 0x5e5e5e5eUL, 0x15151515UL, 0x46464646UL, 0x57575757UL, 0xa7a7a7a7UL, 0x8d8d8d8dUL, 0x9d9d9d9dUL, 0x84848484UL, 0x90909090UL, 0xd8d8d8d8UL, 0xababababUL, 0x00000000UL, 0x8c8c8c8cUL, 0xbcbcbcbcUL, 0xd3d3d3d3UL, 0x0a0a0a0aUL, 0xf7f7f7f7UL, 0xe4e4e4e4UL, 0x58585858UL, 0x05050505UL, 0xb8b8b8b8UL, 0xb3b3b3b3UL, 0x45454545UL, 0x06060606UL, 0xd0d0d0d0UL, 0x2c2c2c2cUL, 0x1e1e1e1eUL, 0x8f8f8f8fUL, 0xcacacacaUL, 0x3f3f3f3fUL, 0x0f0f0f0fUL, 0x02020202UL, 0xc1c1c1c1UL, 0xafafafafUL, 0xbdbdbdbdUL, 0x03030303UL, 0x01010101UL, 0x13131313UL, 0x8a8a8a8aUL, 0x6b6b6b6bUL, 0x3a3a3a3aUL, 0x91919191UL, 0x11111111UL, 0x41414141UL, 0x4f4f4f4fUL, 0x67676767UL, 0xdcdcdcdcUL, 0xeaeaeaeaUL, 0x97979797UL, 0xf2f2f2f2UL, 0xcfcfcfcfUL, 0xcecececeUL, 0xf0f0f0f0UL, 0xb4b4b4b4UL, 0xe6e6e6e6UL, 0x73737373UL, 0x96969696UL, 0xacacacacUL, 0x74747474UL, 0x22222222UL, 0xe7e7e7e7UL, 0xadadadadUL, 0x35353535UL, 0x85858585UL, 0xe2e2e2e2UL, 0xf9f9f9f9UL, 0x37373737UL, 0xe8e8e8e8UL, 0x1c1c1c1cUL, 0x75757575UL, 0xdfdfdfdfUL, 0x6e6e6e6eUL, 0x47474747UL, 0xf1f1f1f1UL, 0x1a1a1a1aUL, 0x71717171UL, 0x1d1d1d1dUL, 0x29292929UL, 0xc5c5c5c5UL, 0x89898989UL, 0x6f6f6f6fUL, 0xb7b7b7b7UL, 0x62626262UL, 0x0e0e0e0eUL, 0xaaaaaaaaUL, 0x18181818UL, 0xbebebebeUL, 0x1b1b1b1bUL, 0xfcfcfcfcUL, 0x56565656UL, 0x3e3e3e3eUL, 0x4b4b4b4bUL, 0xc6c6c6c6UL, 0xd2d2d2d2UL, 0x79797979UL, 0x20202020UL, 0x9a9a9a9aUL, 0xdbdbdbdbUL, 0xc0c0c0c0UL, 0xfefefefeUL, 0x78787878UL, 0xcdcdcdcdUL, 0x5a5a5a5aUL, 0xf4f4f4f4UL, 0x1f1f1f1fUL, 0xddddddddUL, 0xa8a8a8a8UL, 0x33333333UL, 0x88888888UL, 0x07070707UL, 0xc7c7c7c7UL, 0x31313131UL, 0xb1b1b1b1UL, 0x12121212UL, 0x10101010UL, 0x59595959UL, 0x27272727UL, 0x80808080UL, 0xececececUL, 0x5f5f5f5fUL, 0x60606060UL, 0x51515151UL, 0x7f7f7f7fUL, 0xa9a9a9a9UL, 0x19191919UL, 0xb5b5b5b5UL, 0x4a4a4a4aUL, 0x0d0d0d0dUL, 0x2d2d2d2dUL, 0xe5e5e5e5UL, 0x7a7a7a7aUL, 0x9f9f9f9fUL, 0x93939393UL, 0xc9c9c9c9UL, 0x9c9c9c9cUL, 0xefefefefUL, 0xa0a0a0a0UL, 0xe0e0e0e0UL, 0x3b3b3b3bUL, 0x4d4d4d4dUL, 0xaeaeaeaeUL, 0x2a2a2a2aUL, 0xf5f5f5f5UL, 0xb0b0b0b0UL, 0xc8c8c8c8UL, 0xebebebebUL, 0xbbbbbbbbUL, 0x3c3c3c3cUL, 0x83838383UL, 0x53535353UL, 0x99999999UL, 0x61616161UL, 0x17171717UL, 0x2b2b2b2bUL, 0x04040404UL, 0x7e7e7e7eUL, 0xbabababaUL, 0x77777777UL, 0xd6d6d6d6UL, 0x26262626UL, 0xe1e1e1e1UL, 0x69696969UL, 0x14141414UL, 0x63636363UL, 0x55555555UL, 0x21212121UL, 0x0c0c0c0cUL, 0x7d7d7d7dUL, }; #endif /* ENCRYPT_ONLY */ #ifdef LTC_SMALL_CODE #define Te0(x) TE0[x] #define Te1(x) RORc(TE0[x], 8) #define Te2(x) RORc(TE0[x], 16) #define Te3(x) RORc(TE0[x], 24) #define Td0(x) TD0[x] #define Td1(x) RORc(TD0[x], 8) #define Td2(x) RORc(TD0[x], 16) #define Td3(x) RORc(TD0[x], 24) #define Te4_0 0x000000FF & Te4 #define Te4_1 0x0000FF00 & Te4 #define Te4_2 0x00FF0000 & Te4 #define Te4_3 0xFF000000 & Te4 #else #define Te0(x) TE0[x] #define Te1(x) TE1[x] #define Te2(x) TE2[x] #define Te3(x) TE3[x] #define Td0(x) TD0[x] #define Td1(x) TD1[x] #define Td2(x) TD2[x] #define Td3(x) TD3[x] static const ulong32 TE1[256] = { 0xa5c66363UL, 0x84f87c7cUL, 0x99ee7777UL, 0x8df67b7bUL, 0x0dfff2f2UL, 0xbdd66b6bUL, 0xb1de6f6fUL, 0x5491c5c5UL, 0x50603030UL, 0x03020101UL, 0xa9ce6767UL, 0x7d562b2bUL, 0x19e7fefeUL, 0x62b5d7d7UL, 0xe64dababUL, 0x9aec7676UL, 0x458fcacaUL, 0x9d1f8282UL, 0x4089c9c9UL, 0x87fa7d7dUL, 0x15effafaUL, 0xebb25959UL, 0xc98e4747UL, 0x0bfbf0f0UL, 0xec41adadUL, 0x67b3d4d4UL, 0xfd5fa2a2UL, 0xea45afafUL, 0xbf239c9cUL, 0xf753a4a4UL, 0x96e47272UL, 0x5b9bc0c0UL, 0xc275b7b7UL, 0x1ce1fdfdUL, 0xae3d9393UL, 0x6a4c2626UL, 0x5a6c3636UL, 0x417e3f3fUL, 0x02f5f7f7UL, 0x4f83ccccUL, 0x5c683434UL, 0xf451a5a5UL, 0x34d1e5e5UL, 0x08f9f1f1UL, 0x93e27171UL, 0x73abd8d8UL, 0x53623131UL, 0x3f2a1515UL, 0x0c080404UL, 0x5295c7c7UL, 0x65462323UL, 0x5e9dc3c3UL, 0x28301818UL, 0xa1379696UL, 0x0f0a0505UL, 0xb52f9a9aUL, 0x090e0707UL, 0x36241212UL, 0x9b1b8080UL, 0x3ddfe2e2UL, 0x26cdebebUL, 0x694e2727UL, 0xcd7fb2b2UL, 0x9fea7575UL, 0x1b120909UL, 0x9e1d8383UL, 0x74582c2cUL, 0x2e341a1aUL, 0x2d361b1bUL, 0xb2dc6e6eUL, 0xeeb45a5aUL, 0xfb5ba0a0UL, 0xf6a45252UL, 0x4d763b3bUL, 0x61b7d6d6UL, 0xce7db3b3UL, 0x7b522929UL, 0x3edde3e3UL, 0x715e2f2fUL, 0x97138484UL, 0xf5a65353UL, 0x68b9d1d1UL, 0x00000000UL, 0x2cc1ededUL, 0x60402020UL, 0x1fe3fcfcUL, 0xc879b1b1UL, 0xedb65b5bUL, 0xbed46a6aUL, 0x468dcbcbUL, 0xd967bebeUL, 0x4b723939UL, 0xde944a4aUL, 0xd4984c4cUL, 0xe8b05858UL, 0x4a85cfcfUL, 0x6bbbd0d0UL, 0x2ac5efefUL, 0xe54faaaaUL, 0x16edfbfbUL, 0xc5864343UL, 0xd79a4d4dUL, 0x55663333UL, 0x94118585UL, 0xcf8a4545UL, 0x10e9f9f9UL, 0x06040202UL, 0x81fe7f7fUL, 0xf0a05050UL, 0x44783c3cUL, 0xba259f9fUL, 0xe34ba8a8UL, 0xf3a25151UL, 0xfe5da3a3UL, 0xc0804040UL, 0x8a058f8fUL, 0xad3f9292UL, 0xbc219d9dUL, 0x48703838UL, 0x04f1f5f5UL, 0xdf63bcbcUL, 0xc177b6b6UL, 0x75afdadaUL, 0x63422121UL, 0x30201010UL, 0x1ae5ffffUL, 0x0efdf3f3UL, 0x6dbfd2d2UL, 0x4c81cdcdUL, 0x14180c0cUL, 0x35261313UL, 0x2fc3ececUL, 0xe1be5f5fUL, 0xa2359797UL, 0xcc884444UL, 0x392e1717UL, 0x5793c4c4UL, 0xf255a7a7UL, 0x82fc7e7eUL, 0x477a3d3dUL, 0xacc86464UL, 0xe7ba5d5dUL, 0x2b321919UL, 0x95e67373UL, 0xa0c06060UL, 0x98198181UL, 0xd19e4f4fUL, 0x7fa3dcdcUL, 0x66442222UL, 0x7e542a2aUL, 0xab3b9090UL, 0x830b8888UL, 0xca8c4646UL, 0x29c7eeeeUL, 0xd36bb8b8UL, 0x3c281414UL, 0x79a7dedeUL, 0xe2bc5e5eUL, 0x1d160b0bUL, 0x76addbdbUL, 0x3bdbe0e0UL, 0x56643232UL, 0x4e743a3aUL, 0x1e140a0aUL, 0xdb924949UL, 0x0a0c0606UL, 0x6c482424UL, 0xe4b85c5cUL, 0x5d9fc2c2UL, 0x6ebdd3d3UL, 0xef43acacUL, 0xa6c46262UL, 0xa8399191UL, 0xa4319595UL, 0x37d3e4e4UL, 0x8bf27979UL, 0x32d5e7e7UL, 0x438bc8c8UL, 0x596e3737UL, 0xb7da6d6dUL, 0x8c018d8dUL, 0x64b1d5d5UL, 0xd29c4e4eUL, 0xe049a9a9UL, 0xb4d86c6cUL, 0xfaac5656UL, 0x07f3f4f4UL, 0x25cfeaeaUL, 0xafca6565UL, 0x8ef47a7aUL, 0xe947aeaeUL, 0x18100808UL, 0xd56fbabaUL, 0x88f07878UL, 0x6f4a2525UL, 0x725c2e2eUL, 0x24381c1cUL, 0xf157a6a6UL, 0xc773b4b4UL, 0x5197c6c6UL, 0x23cbe8e8UL, 0x7ca1ddddUL, 0x9ce87474UL, 0x213e1f1fUL, 0xdd964b4bUL, 0xdc61bdbdUL, 0x860d8b8bUL, 0x850f8a8aUL, 0x90e07070UL, 0x427c3e3eUL, 0xc471b5b5UL, 0xaacc6666UL, 0xd8904848UL, 0x05060303UL, 0x01f7f6f6UL, 0x121c0e0eUL, 0xa3c26161UL, 0x5f6a3535UL, 0xf9ae5757UL, 0xd069b9b9UL, 0x91178686UL, 0x5899c1c1UL, 0x273a1d1dUL, 0xb9279e9eUL, 0x38d9e1e1UL, 0x13ebf8f8UL, 0xb32b9898UL, 0x33221111UL, 0xbbd26969UL, 0x70a9d9d9UL, 0x89078e8eUL, 0xa7339494UL, 0xb62d9b9bUL, 0x223c1e1eUL, 0x92158787UL, 0x20c9e9e9UL, 0x4987ceceUL, 0xffaa5555UL, 0x78502828UL, 0x7aa5dfdfUL, 0x8f038c8cUL, 0xf859a1a1UL, 0x80098989UL, 0x171a0d0dUL, 0xda65bfbfUL, 0x31d7e6e6UL, 0xc6844242UL, 0xb8d06868UL, 0xc3824141UL, 0xb0299999UL, 0x775a2d2dUL, 0x111e0f0fUL, 0xcb7bb0b0UL, 0xfca85454UL, 0xd66dbbbbUL, 0x3a2c1616UL, }; static const ulong32 TE2[256] = { 0x63a5c663UL, 0x7c84f87cUL, 0x7799ee77UL, 0x7b8df67bUL, 0xf20dfff2UL, 0x6bbdd66bUL, 0x6fb1de6fUL, 0xc55491c5UL, 0x30506030UL, 0x01030201UL, 0x67a9ce67UL, 0x2b7d562bUL, 0xfe19e7feUL, 0xd762b5d7UL, 0xabe64dabUL, 0x769aec76UL, 0xca458fcaUL, 0x829d1f82UL, 0xc94089c9UL, 0x7d87fa7dUL, 0xfa15effaUL, 0x59ebb259UL, 0x47c98e47UL, 0xf00bfbf0UL, 0xadec41adUL, 0xd467b3d4UL, 0xa2fd5fa2UL, 0xafea45afUL, 0x9cbf239cUL, 0xa4f753a4UL, 0x7296e472UL, 0xc05b9bc0UL, 0xb7c275b7UL, 0xfd1ce1fdUL, 0x93ae3d93UL, 0x266a4c26UL, 0x365a6c36UL, 0x3f417e3fUL, 0xf702f5f7UL, 0xcc4f83ccUL, 0x345c6834UL, 0xa5f451a5UL, 0xe534d1e5UL, 0xf108f9f1UL, 0x7193e271UL, 0xd873abd8UL, 0x31536231UL, 0x153f2a15UL, 0x040c0804UL, 0xc75295c7UL, 0x23654623UL, 0xc35e9dc3UL, 0x18283018UL, 0x96a13796UL, 0x050f0a05UL, 0x9ab52f9aUL, 0x07090e07UL, 0x12362412UL, 0x809b1b80UL, 0xe23ddfe2UL, 0xeb26cdebUL, 0x27694e27UL, 0xb2cd7fb2UL, 0x759fea75UL, 0x091b1209UL, 0x839e1d83UL, 0x2c74582cUL, 0x1a2e341aUL, 0x1b2d361bUL, 0x6eb2dc6eUL, 0x5aeeb45aUL, 0xa0fb5ba0UL, 0x52f6a452UL, 0x3b4d763bUL, 0xd661b7d6UL, 0xb3ce7db3UL, 0x297b5229UL, 0xe33edde3UL, 0x2f715e2fUL, 0x84971384UL, 0x53f5a653UL, 0xd168b9d1UL, 0x00000000UL, 0xed2cc1edUL, 0x20604020UL, 0xfc1fe3fcUL, 0xb1c879b1UL, 0x5bedb65bUL, 0x6abed46aUL, 0xcb468dcbUL, 0xbed967beUL, 0x394b7239UL, 0x4ade944aUL, 0x4cd4984cUL, 0x58e8b058UL, 0xcf4a85cfUL, 0xd06bbbd0UL, 0xef2ac5efUL, 0xaae54faaUL, 0xfb16edfbUL, 0x43c58643UL, 0x4dd79a4dUL, 0x33556633UL, 0x85941185UL, 0x45cf8a45UL, 0xf910e9f9UL, 0x02060402UL, 0x7f81fe7fUL, 0x50f0a050UL, 0x3c44783cUL, 0x9fba259fUL, 0xa8e34ba8UL, 0x51f3a251UL, 0xa3fe5da3UL, 0x40c08040UL, 0x8f8a058fUL, 0x92ad3f92UL, 0x9dbc219dUL, 0x38487038UL, 0xf504f1f5UL, 0xbcdf63bcUL, 0xb6c177b6UL, 0xda75afdaUL, 0x21634221UL, 0x10302010UL, 0xff1ae5ffUL, 0xf30efdf3UL, 0xd26dbfd2UL, 0xcd4c81cdUL, 0x0c14180cUL, 0x13352613UL, 0xec2fc3ecUL, 0x5fe1be5fUL, 0x97a23597UL, 0x44cc8844UL, 0x17392e17UL, 0xc45793c4UL, 0xa7f255a7UL, 0x7e82fc7eUL, 0x3d477a3dUL, 0x64acc864UL, 0x5de7ba5dUL, 0x192b3219UL, 0x7395e673UL, 0x60a0c060UL, 0x81981981UL, 0x4fd19e4fUL, 0xdc7fa3dcUL, 0x22664422UL, 0x2a7e542aUL, 0x90ab3b90UL, 0x88830b88UL, 0x46ca8c46UL, 0xee29c7eeUL, 0xb8d36bb8UL, 0x143c2814UL, 0xde79a7deUL, 0x5ee2bc5eUL, 0x0b1d160bUL, 0xdb76addbUL, 0xe03bdbe0UL, 0x32566432UL, 0x3a4e743aUL, 0x0a1e140aUL, 0x49db9249UL, 0x060a0c06UL, 0x246c4824UL, 0x5ce4b85cUL, 0xc25d9fc2UL, 0xd36ebdd3UL, 0xacef43acUL, 0x62a6c462UL, 0x91a83991UL, 0x95a43195UL, 0xe437d3e4UL, 0x798bf279UL, 0xe732d5e7UL, 0xc8438bc8UL, 0x37596e37UL, 0x6db7da6dUL, 0x8d8c018dUL, 0xd564b1d5UL, 0x4ed29c4eUL, 0xa9e049a9UL, 0x6cb4d86cUL, 0x56faac56UL, 0xf407f3f4UL, 0xea25cfeaUL, 0x65afca65UL, 0x7a8ef47aUL, 0xaee947aeUL, 0x08181008UL, 0xbad56fbaUL, 0x7888f078UL, 0x256f4a25UL, 0x2e725c2eUL, 0x1c24381cUL, 0xa6f157a6UL, 0xb4c773b4UL, 0xc65197c6UL, 0xe823cbe8UL, 0xdd7ca1ddUL, 0x749ce874UL, 0x1f213e1fUL, 0x4bdd964bUL, 0xbddc61bdUL, 0x8b860d8bUL, 0x8a850f8aUL, 0x7090e070UL, 0x3e427c3eUL, 0xb5c471b5UL, 0x66aacc66UL, 0x48d89048UL, 0x03050603UL, 0xf601f7f6UL, 0x0e121c0eUL, 0x61a3c261UL, 0x355f6a35UL, 0x57f9ae57UL, 0xb9d069b9UL, 0x86911786UL, 0xc15899c1UL, 0x1d273a1dUL, 0x9eb9279eUL, 0xe138d9e1UL, 0xf813ebf8UL, 0x98b32b98UL, 0x11332211UL, 0x69bbd269UL, 0xd970a9d9UL, 0x8e89078eUL, 0x94a73394UL, 0x9bb62d9bUL, 0x1e223c1eUL, 0x87921587UL, 0xe920c9e9UL, 0xce4987ceUL, 0x55ffaa55UL, 0x28785028UL, 0xdf7aa5dfUL, 0x8c8f038cUL, 0xa1f859a1UL, 0x89800989UL, 0x0d171a0dUL, 0xbfda65bfUL, 0xe631d7e6UL, 0x42c68442UL, 0x68b8d068UL, 0x41c38241UL, 0x99b02999UL, 0x2d775a2dUL, 0x0f111e0fUL, 0xb0cb7bb0UL, 0x54fca854UL, 0xbbd66dbbUL, 0x163a2c16UL, }; static const ulong32 TE3[256] = { 0x6363a5c6UL, 0x7c7c84f8UL, 0x777799eeUL, 0x7b7b8df6UL, 0xf2f20dffUL, 0x6b6bbdd6UL, 0x6f6fb1deUL, 0xc5c55491UL, 0x30305060UL, 0x01010302UL, 0x6767a9ceUL, 0x2b2b7d56UL, 0xfefe19e7UL, 0xd7d762b5UL, 0xababe64dUL, 0x76769aecUL, 0xcaca458fUL, 0x82829d1fUL, 0xc9c94089UL, 0x7d7d87faUL, 0xfafa15efUL, 0x5959ebb2UL, 0x4747c98eUL, 0xf0f00bfbUL, 0xadadec41UL, 0xd4d467b3UL, 0xa2a2fd5fUL, 0xafafea45UL, 0x9c9cbf23UL, 0xa4a4f753UL, 0x727296e4UL, 0xc0c05b9bUL, 0xb7b7c275UL, 0xfdfd1ce1UL, 0x9393ae3dUL, 0x26266a4cUL, 0x36365a6cUL, 0x3f3f417eUL, 0xf7f702f5UL, 0xcccc4f83UL, 0x34345c68UL, 0xa5a5f451UL, 0xe5e534d1UL, 0xf1f108f9UL, 0x717193e2UL, 0xd8d873abUL, 0x31315362UL, 0x15153f2aUL, 0x04040c08UL, 0xc7c75295UL, 0x23236546UL, 0xc3c35e9dUL, 0x18182830UL, 0x9696a137UL, 0x05050f0aUL, 0x9a9ab52fUL, 0x0707090eUL, 0x12123624UL, 0x80809b1bUL, 0xe2e23ddfUL, 0xebeb26cdUL, 0x2727694eUL, 0xb2b2cd7fUL, 0x75759feaUL, 0x09091b12UL, 0x83839e1dUL, 0x2c2c7458UL, 0x1a1a2e34UL, 0x1b1b2d36UL, 0x6e6eb2dcUL, 0x5a5aeeb4UL, 0xa0a0fb5bUL, 0x5252f6a4UL, 0x3b3b4d76UL, 0xd6d661b7UL, 0xb3b3ce7dUL, 0x29297b52UL, 0xe3e33eddUL, 0x2f2f715eUL, 0x84849713UL, 0x5353f5a6UL, 0xd1d168b9UL, 0x00000000UL, 0xeded2cc1UL, 0x20206040UL, 0xfcfc1fe3UL, 0xb1b1c879UL, 0x5b5bedb6UL, 0x6a6abed4UL, 0xcbcb468dUL, 0xbebed967UL, 0x39394b72UL, 0x4a4ade94UL, 0x4c4cd498UL, 0x5858e8b0UL, 0xcfcf4a85UL, 0xd0d06bbbUL, 0xefef2ac5UL, 0xaaaae54fUL, 0xfbfb16edUL, 0x4343c586UL, 0x4d4dd79aUL, 0x33335566UL, 0x85859411UL, 0x4545cf8aUL, 0xf9f910e9UL, 0x02020604UL, 0x7f7f81feUL, 0x5050f0a0UL, 0x3c3c4478UL, 0x9f9fba25UL, 0xa8a8e34bUL, 0x5151f3a2UL, 0xa3a3fe5dUL, 0x4040c080UL, 0x8f8f8a05UL, 0x9292ad3fUL, 0x9d9dbc21UL, 0x38384870UL, 0xf5f504f1UL, 0xbcbcdf63UL, 0xb6b6c177UL, 0xdada75afUL, 0x21216342UL, 0x10103020UL, 0xffff1ae5UL, 0xf3f30efdUL, 0xd2d26dbfUL, 0xcdcd4c81UL, 0x0c0c1418UL, 0x13133526UL, 0xecec2fc3UL, 0x5f5fe1beUL, 0x9797a235UL, 0x4444cc88UL, 0x1717392eUL, 0xc4c45793UL, 0xa7a7f255UL, 0x7e7e82fcUL, 0x3d3d477aUL, 0x6464acc8UL, 0x5d5de7baUL, 0x19192b32UL, 0x737395e6UL, 0x6060a0c0UL, 0x81819819UL, 0x4f4fd19eUL, 0xdcdc7fa3UL, 0x22226644UL, 0x2a2a7e54UL, 0x9090ab3bUL, 0x8888830bUL, 0x4646ca8cUL, 0xeeee29c7UL, 0xb8b8d36bUL, 0x14143c28UL, 0xdede79a7UL, 0x5e5ee2bcUL, 0x0b0b1d16UL, 0xdbdb76adUL, 0xe0e03bdbUL, 0x32325664UL, 0x3a3a4e74UL, 0x0a0a1e14UL, 0x4949db92UL, 0x06060a0cUL, 0x24246c48UL, 0x5c5ce4b8UL, 0xc2c25d9fUL, 0xd3d36ebdUL, 0xacacef43UL, 0x6262a6c4UL, 0x9191a839UL, 0x9595a431UL, 0xe4e437d3UL, 0x79798bf2UL, 0xe7e732d5UL, 0xc8c8438bUL, 0x3737596eUL, 0x6d6db7daUL, 0x8d8d8c01UL, 0xd5d564b1UL, 0x4e4ed29cUL, 0xa9a9e049UL, 0x6c6cb4d8UL, 0x5656faacUL, 0xf4f407f3UL, 0xeaea25cfUL, 0x6565afcaUL, 0x7a7a8ef4UL, 0xaeaee947UL, 0x08081810UL, 0xbabad56fUL, 0x787888f0UL, 0x25256f4aUL, 0x2e2e725cUL, 0x1c1c2438UL, 0xa6a6f157UL, 0xb4b4c773UL, 0xc6c65197UL, 0xe8e823cbUL, 0xdddd7ca1UL, 0x74749ce8UL, 0x1f1f213eUL, 0x4b4bdd96UL, 0xbdbddc61UL, 0x8b8b860dUL, 0x8a8a850fUL, 0x707090e0UL, 0x3e3e427cUL, 0xb5b5c471UL, 0x6666aaccUL, 0x4848d890UL, 0x03030506UL, 0xf6f601f7UL, 0x0e0e121cUL, 0x6161a3c2UL, 0x35355f6aUL, 0x5757f9aeUL, 0xb9b9d069UL, 0x86869117UL, 0xc1c15899UL, 0x1d1d273aUL, 0x9e9eb927UL, 0xe1e138d9UL, 0xf8f813ebUL, 0x9898b32bUL, 0x11113322UL, 0x6969bbd2UL, 0xd9d970a9UL, 0x8e8e8907UL, 0x9494a733UL, 0x9b9bb62dUL, 0x1e1e223cUL, 0x87879215UL, 0xe9e920c9UL, 0xcece4987UL, 0x5555ffaaUL, 0x28287850UL, 0xdfdf7aa5UL, 0x8c8c8f03UL, 0xa1a1f859UL, 0x89898009UL, 0x0d0d171aUL, 0xbfbfda65UL, 0xe6e631d7UL, 0x4242c684UL, 0x6868b8d0UL, 0x4141c382UL, 0x9999b029UL, 0x2d2d775aUL, 0x0f0f111eUL, 0xb0b0cb7bUL, 0x5454fca8UL, 0xbbbbd66dUL, 0x16163a2cUL, }; #ifndef PELI_TAB static const ulong32 Te4_0[] = { 0x00000063UL, 0x0000007cUL, 0x00000077UL, 0x0000007bUL, 0x000000f2UL, 0x0000006bUL, 0x0000006fUL, 0x000000c5UL, 0x00000030UL, 0x00000001UL, 0x00000067UL, 0x0000002bUL, 0x000000feUL, 0x000000d7UL, 0x000000abUL, 0x00000076UL, 0x000000caUL, 0x00000082UL, 0x000000c9UL, 0x0000007dUL, 0x000000faUL, 0x00000059UL, 0x00000047UL, 0x000000f0UL, 0x000000adUL, 0x000000d4UL, 0x000000a2UL, 0x000000afUL, 0x0000009cUL, 0x000000a4UL, 0x00000072UL, 0x000000c0UL, 0x000000b7UL, 0x000000fdUL, 0x00000093UL, 0x00000026UL, 0x00000036UL, 0x0000003fUL, 0x000000f7UL, 0x000000ccUL, 0x00000034UL, 0x000000a5UL, 0x000000e5UL, 0x000000f1UL, 0x00000071UL, 0x000000d8UL, 0x00000031UL, 0x00000015UL, 0x00000004UL, 0x000000c7UL, 0x00000023UL, 0x000000c3UL, 0x00000018UL, 0x00000096UL, 0x00000005UL, 0x0000009aUL, 0x00000007UL, 0x00000012UL, 0x00000080UL, 0x000000e2UL, 0x000000ebUL, 0x00000027UL, 0x000000b2UL, 0x00000075UL, 0x00000009UL, 0x00000083UL, 0x0000002cUL, 0x0000001aUL, 0x0000001bUL, 0x0000006eUL, 0x0000005aUL, 0x000000a0UL, 0x00000052UL, 0x0000003bUL, 0x000000d6UL, 0x000000b3UL, 0x00000029UL, 0x000000e3UL, 0x0000002fUL, 0x00000084UL, 0x00000053UL, 0x000000d1UL, 0x00000000UL, 0x000000edUL, 0x00000020UL, 0x000000fcUL, 0x000000b1UL, 0x0000005bUL, 0x0000006aUL, 0x000000cbUL, 0x000000beUL, 0x00000039UL, 0x0000004aUL, 0x0000004cUL, 0x00000058UL, 0x000000cfUL, 0x000000d0UL, 0x000000efUL, 0x000000aaUL, 0x000000fbUL, 0x00000043UL, 0x0000004dUL, 0x00000033UL, 0x00000085UL, 0x00000045UL, 0x000000f9UL, 0x00000002UL, 0x0000007fUL, 0x00000050UL, 0x0000003cUL, 0x0000009fUL, 0x000000a8UL, 0x00000051UL, 0x000000a3UL, 0x00000040UL, 0x0000008fUL, 0x00000092UL, 0x0000009dUL, 0x00000038UL, 0x000000f5UL, 0x000000bcUL, 0x000000b6UL, 0x000000daUL, 0x00000021UL, 0x00000010UL, 0x000000ffUL, 0x000000f3UL, 0x000000d2UL, 0x000000cdUL, 0x0000000cUL, 0x00000013UL, 0x000000ecUL, 0x0000005fUL, 0x00000097UL, 0x00000044UL, 0x00000017UL, 0x000000c4UL, 0x000000a7UL, 0x0000007eUL, 0x0000003dUL, 0x00000064UL, 0x0000005dUL, 0x00000019UL, 0x00000073UL, 0x00000060UL, 0x00000081UL, 0x0000004fUL, 0x000000dcUL, 0x00000022UL, 0x0000002aUL, 0x00000090UL, 0x00000088UL, 0x00000046UL, 0x000000eeUL, 0x000000b8UL, 0x00000014UL, 0x000000deUL, 0x0000005eUL, 0x0000000bUL, 0x000000dbUL, 0x000000e0UL, 0x00000032UL, 0x0000003aUL, 0x0000000aUL, 0x00000049UL, 0x00000006UL, 0x00000024UL, 0x0000005cUL, 0x000000c2UL, 0x000000d3UL, 0x000000acUL, 0x00000062UL, 0x00000091UL, 0x00000095UL, 0x000000e4UL, 0x00000079UL, 0x000000e7UL, 0x000000c8UL, 0x00000037UL, 0x0000006dUL, 0x0000008dUL, 0x000000d5UL, 0x0000004eUL, 0x000000a9UL, 0x0000006cUL, 0x00000056UL, 0x000000f4UL, 0x000000eaUL, 0x00000065UL, 0x0000007aUL, 0x000000aeUL, 0x00000008UL, 0x000000baUL, 0x00000078UL, 0x00000025UL, 0x0000002eUL, 0x0000001cUL, 0x000000a6UL, 0x000000b4UL, 0x000000c6UL, 0x000000e8UL, 0x000000ddUL, 0x00000074UL, 0x0000001fUL, 0x0000004bUL, 0x000000bdUL, 0x0000008bUL, 0x0000008aUL, 0x00000070UL, 0x0000003eUL, 0x000000b5UL, 0x00000066UL, 0x00000048UL, 0x00000003UL, 0x000000f6UL, 0x0000000eUL, 0x00000061UL, 0x00000035UL, 0x00000057UL, 0x000000b9UL, 0x00000086UL, 0x000000c1UL, 0x0000001dUL, 0x0000009eUL, 0x000000e1UL, 0x000000f8UL, 0x00000098UL, 0x00000011UL, 0x00000069UL, 0x000000d9UL, 0x0000008eUL, 0x00000094UL, 0x0000009bUL, 0x0000001eUL, 0x00000087UL, 0x000000e9UL, 0x000000ceUL, 0x00000055UL, 0x00000028UL, 0x000000dfUL, 0x0000008cUL, 0x000000a1UL, 0x00000089UL, 0x0000000dUL, 0x000000bfUL, 0x000000e6UL, 0x00000042UL, 0x00000068UL, 0x00000041UL, 0x00000099UL, 0x0000002dUL, 0x0000000fUL, 0x000000b0UL, 0x00000054UL, 0x000000bbUL, 0x00000016UL }; static const ulong32 Te4_1[] = { 0x00006300UL, 0x00007c00UL, 0x00007700UL, 0x00007b00UL, 0x0000f200UL, 0x00006b00UL, 0x00006f00UL, 0x0000c500UL, 0x00003000UL, 0x00000100UL, 0x00006700UL, 0x00002b00UL, 0x0000fe00UL, 0x0000d700UL, 0x0000ab00UL, 0x00007600UL, 0x0000ca00UL, 0x00008200UL, 0x0000c900UL, 0x00007d00UL, 0x0000fa00UL, 0x00005900UL, 0x00004700UL, 0x0000f000UL, 0x0000ad00UL, 0x0000d400UL, 0x0000a200UL, 0x0000af00UL, 0x00009c00UL, 0x0000a400UL, 0x00007200UL, 0x0000c000UL, 0x0000b700UL, 0x0000fd00UL, 0x00009300UL, 0x00002600UL, 0x00003600UL, 0x00003f00UL, 0x0000f700UL, 0x0000cc00UL, 0x00003400UL, 0x0000a500UL, 0x0000e500UL, 0x0000f100UL, 0x00007100UL, 0x0000d800UL, 0x00003100UL, 0x00001500UL, 0x00000400UL, 0x0000c700UL, 0x00002300UL, 0x0000c300UL, 0x00001800UL, 0x00009600UL, 0x00000500UL, 0x00009a00UL, 0x00000700UL, 0x00001200UL, 0x00008000UL, 0x0000e200UL, 0x0000eb00UL, 0x00002700UL, 0x0000b200UL, 0x00007500UL, 0x00000900UL, 0x00008300UL, 0x00002c00UL, 0x00001a00UL, 0x00001b00UL, 0x00006e00UL, 0x00005a00UL, 0x0000a000UL, 0x00005200UL, 0x00003b00UL, 0x0000d600UL, 0x0000b300UL, 0x00002900UL, 0x0000e300UL, 0x00002f00UL, 0x00008400UL, 0x00005300UL, 0x0000d100UL, 0x00000000UL, 0x0000ed00UL, 0x00002000UL, 0x0000fc00UL, 0x0000b100UL, 0x00005b00UL, 0x00006a00UL, 0x0000cb00UL, 0x0000be00UL, 0x00003900UL, 0x00004a00UL, 0x00004c00UL, 0x00005800UL, 0x0000cf00UL, 0x0000d000UL, 0x0000ef00UL, 0x0000aa00UL, 0x0000fb00UL, 0x00004300UL, 0x00004d00UL, 0x00003300UL, 0x00008500UL, 0x00004500UL, 0x0000f900UL, 0x00000200UL, 0x00007f00UL, 0x00005000UL, 0x00003c00UL, 0x00009f00UL, 0x0000a800UL, 0x00005100UL, 0x0000a300UL, 0x00004000UL, 0x00008f00UL, 0x00009200UL, 0x00009d00UL, 0x00003800UL, 0x0000f500UL, 0x0000bc00UL, 0x0000b600UL, 0x0000da00UL, 0x00002100UL, 0x00001000UL, 0x0000ff00UL, 0x0000f300UL, 0x0000d200UL, 0x0000cd00UL, 0x00000c00UL, 0x00001300UL, 0x0000ec00UL, 0x00005f00UL, 0x00009700UL, 0x00004400UL, 0x00001700UL, 0x0000c400UL, 0x0000a700UL, 0x00007e00UL, 0x00003d00UL, 0x00006400UL, 0x00005d00UL, 0x00001900UL, 0x00007300UL, 0x00006000UL, 0x00008100UL, 0x00004f00UL, 0x0000dc00UL, 0x00002200UL, 0x00002a00UL, 0x00009000UL, 0x00008800UL, 0x00004600UL, 0x0000ee00UL, 0x0000b800UL, 0x00001400UL, 0x0000de00UL, 0x00005e00UL, 0x00000b00UL, 0x0000db00UL, 0x0000e000UL, 0x00003200UL, 0x00003a00UL, 0x00000a00UL, 0x00004900UL, 0x00000600UL, 0x00002400UL, 0x00005c00UL, 0x0000c200UL, 0x0000d300UL, 0x0000ac00UL, 0x00006200UL, 0x00009100UL, 0x00009500UL, 0x0000e400UL, 0x00007900UL, 0x0000e700UL, 0x0000c800UL, 0x00003700UL, 0x00006d00UL, 0x00008d00UL, 0x0000d500UL, 0x00004e00UL, 0x0000a900UL, 0x00006c00UL, 0x00005600UL, 0x0000f400UL, 0x0000ea00UL, 0x00006500UL, 0x00007a00UL, 0x0000ae00UL, 0x00000800UL, 0x0000ba00UL, 0x00007800UL, 0x00002500UL, 0x00002e00UL, 0x00001c00UL, 0x0000a600UL, 0x0000b400UL, 0x0000c600UL, 0x0000e800UL, 0x0000dd00UL, 0x00007400UL, 0x00001f00UL, 0x00004b00UL, 0x0000bd00UL, 0x00008b00UL, 0x00008a00UL, 0x00007000UL, 0x00003e00UL, 0x0000b500UL, 0x00006600UL, 0x00004800UL, 0x00000300UL, 0x0000f600UL, 0x00000e00UL, 0x00006100UL, 0x00003500UL, 0x00005700UL, 0x0000b900UL, 0x00008600UL, 0x0000c100UL, 0x00001d00UL, 0x00009e00UL, 0x0000e100UL, 0x0000f800UL, 0x00009800UL, 0x00001100UL, 0x00006900UL, 0x0000d900UL, 0x00008e00UL, 0x00009400UL, 0x00009b00UL, 0x00001e00UL, 0x00008700UL, 0x0000e900UL, 0x0000ce00UL, 0x00005500UL, 0x00002800UL, 0x0000df00UL, 0x00008c00UL, 0x0000a100UL, 0x00008900UL, 0x00000d00UL, 0x0000bf00UL, 0x0000e600UL, 0x00004200UL, 0x00006800UL, 0x00004100UL, 0x00009900UL, 0x00002d00UL, 0x00000f00UL, 0x0000b000UL, 0x00005400UL, 0x0000bb00UL, 0x00001600UL }; static const ulong32 Te4_2[] = { 0x00630000UL, 0x007c0000UL, 0x00770000UL, 0x007b0000UL, 0x00f20000UL, 0x006b0000UL, 0x006f0000UL, 0x00c50000UL, 0x00300000UL, 0x00010000UL, 0x00670000UL, 0x002b0000UL, 0x00fe0000UL, 0x00d70000UL, 0x00ab0000UL, 0x00760000UL, 0x00ca0000UL, 0x00820000UL, 0x00c90000UL, 0x007d0000UL, 0x00fa0000UL, 0x00590000UL, 0x00470000UL, 0x00f00000UL, 0x00ad0000UL, 0x00d40000UL, 0x00a20000UL, 0x00af0000UL, 0x009c0000UL, 0x00a40000UL, 0x00720000UL, 0x00c00000UL, 0x00b70000UL, 0x00fd0000UL, 0x00930000UL, 0x00260000UL, 0x00360000UL, 0x003f0000UL, 0x00f70000UL, 0x00cc0000UL, 0x00340000UL, 0x00a50000UL, 0x00e50000UL, 0x00f10000UL, 0x00710000UL, 0x00d80000UL, 0x00310000UL, 0x00150000UL, 0x00040000UL, 0x00c70000UL, 0x00230000UL, 0x00c30000UL, 0x00180000UL, 0x00960000UL, 0x00050000UL, 0x009a0000UL, 0x00070000UL, 0x00120000UL, 0x00800000UL, 0x00e20000UL, 0x00eb0000UL, 0x00270000UL, 0x00b20000UL, 0x00750000UL, 0x00090000UL, 0x00830000UL, 0x002c0000UL, 0x001a0000UL, 0x001b0000UL, 0x006e0000UL, 0x005a0000UL, 0x00a00000UL, 0x00520000UL, 0x003b0000UL, 0x00d60000UL, 0x00b30000UL, 0x00290000UL, 0x00e30000UL, 0x002f0000UL, 0x00840000UL, 0x00530000UL, 0x00d10000UL, 0x00000000UL, 0x00ed0000UL, 0x00200000UL, 0x00fc0000UL, 0x00b10000UL, 0x005b0000UL, 0x006a0000UL, 0x00cb0000UL, 0x00be0000UL, 0x00390000UL, 0x004a0000UL, 0x004c0000UL, 0x00580000UL, 0x00cf0000UL, 0x00d00000UL, 0x00ef0000UL, 0x00aa0000UL, 0x00fb0000UL, 0x00430000UL, 0x004d0000UL, 0x00330000UL, 0x00850000UL, 0x00450000UL, 0x00f90000UL, 0x00020000UL, 0x007f0000UL, 0x00500000UL, 0x003c0000UL, 0x009f0000UL, 0x00a80000UL, 0x00510000UL, 0x00a30000UL, 0x00400000UL, 0x008f0000UL, 0x00920000UL, 0x009d0000UL, 0x00380000UL, 0x00f50000UL, 0x00bc0000UL, 0x00b60000UL, 0x00da0000UL, 0x00210000UL, 0x00100000UL, 0x00ff0000UL, 0x00f30000UL, 0x00d20000UL, 0x00cd0000UL, 0x000c0000UL, 0x00130000UL, 0x00ec0000UL, 0x005f0000UL, 0x00970000UL, 0x00440000UL, 0x00170000UL, 0x00c40000UL, 0x00a70000UL, 0x007e0000UL, 0x003d0000UL, 0x00640000UL, 0x005d0000UL, 0x00190000UL, 0x00730000UL, 0x00600000UL, 0x00810000UL, 0x004f0000UL, 0x00dc0000UL, 0x00220000UL, 0x002a0000UL, 0x00900000UL, 0x00880000UL, 0x00460000UL, 0x00ee0000UL, 0x00b80000UL, 0x00140000UL, 0x00de0000UL, 0x005e0000UL, 0x000b0000UL, 0x00db0000UL, 0x00e00000UL, 0x00320000UL, 0x003a0000UL, 0x000a0000UL, 0x00490000UL, 0x00060000UL, 0x00240000UL, 0x005c0000UL, 0x00c20000UL, 0x00d30000UL, 0x00ac0000UL, 0x00620000UL, 0x00910000UL, 0x00950000UL, 0x00e40000UL, 0x00790000UL, 0x00e70000UL, 0x00c80000UL, 0x00370000UL, 0x006d0000UL, 0x008d0000UL, 0x00d50000UL, 0x004e0000UL, 0x00a90000UL, 0x006c0000UL, 0x00560000UL, 0x00f40000UL, 0x00ea0000UL, 0x00650000UL, 0x007a0000UL, 0x00ae0000UL, 0x00080000UL, 0x00ba0000UL, 0x00780000UL, 0x00250000UL, 0x002e0000UL, 0x001c0000UL, 0x00a60000UL, 0x00b40000UL, 0x00c60000UL, 0x00e80000UL, 0x00dd0000UL, 0x00740000UL, 0x001f0000UL, 0x004b0000UL, 0x00bd0000UL, 0x008b0000UL, 0x008a0000UL, 0x00700000UL, 0x003e0000UL, 0x00b50000UL, 0x00660000UL, 0x00480000UL, 0x00030000UL, 0x00f60000UL, 0x000e0000UL, 0x00610000UL, 0x00350000UL, 0x00570000UL, 0x00b90000UL, 0x00860000UL, 0x00c10000UL, 0x001d0000UL, 0x009e0000UL, 0x00e10000UL, 0x00f80000UL, 0x00980000UL, 0x00110000UL, 0x00690000UL, 0x00d90000UL, 0x008e0000UL, 0x00940000UL, 0x009b0000UL, 0x001e0000UL, 0x00870000UL, 0x00e90000UL, 0x00ce0000UL, 0x00550000UL, 0x00280000UL, 0x00df0000UL, 0x008c0000UL, 0x00a10000UL, 0x00890000UL, 0x000d0000UL, 0x00bf0000UL, 0x00e60000UL, 0x00420000UL, 0x00680000UL, 0x00410000UL, 0x00990000UL, 0x002d0000UL, 0x000f0000UL, 0x00b00000UL, 0x00540000UL, 0x00bb0000UL, 0x00160000UL }; static const ulong32 Te4_3[] = { 0x63000000UL, 0x7c000000UL, 0x77000000UL, 0x7b000000UL, 0xf2000000UL, 0x6b000000UL, 0x6f000000UL, 0xc5000000UL, 0x30000000UL, 0x01000000UL, 0x67000000UL, 0x2b000000UL, 0xfe000000UL, 0xd7000000UL, 0xab000000UL, 0x76000000UL, 0xca000000UL, 0x82000000UL, 0xc9000000UL, 0x7d000000UL, 0xfa000000UL, 0x59000000UL, 0x47000000UL, 0xf0000000UL, 0xad000000UL, 0xd4000000UL, 0xa2000000UL, 0xaf000000UL, 0x9c000000UL, 0xa4000000UL, 0x72000000UL, 0xc0000000UL, 0xb7000000UL, 0xfd000000UL, 0x93000000UL, 0x26000000UL, 0x36000000UL, 0x3f000000UL, 0xf7000000UL, 0xcc000000UL, 0x34000000UL, 0xa5000000UL, 0xe5000000UL, 0xf1000000UL, 0x71000000UL, 0xd8000000UL, 0x31000000UL, 0x15000000UL, 0x04000000UL, 0xc7000000UL, 0x23000000UL, 0xc3000000UL, 0x18000000UL, 0x96000000UL, 0x05000000UL, 0x9a000000UL, 0x07000000UL, 0x12000000UL, 0x80000000UL, 0xe2000000UL, 0xeb000000UL, 0x27000000UL, 0xb2000000UL, 0x75000000UL, 0x09000000UL, 0x83000000UL, 0x2c000000UL, 0x1a000000UL, 0x1b000000UL, 0x6e000000UL, 0x5a000000UL, 0xa0000000UL, 0x52000000UL, 0x3b000000UL, 0xd6000000UL, 0xb3000000UL, 0x29000000UL, 0xe3000000UL, 0x2f000000UL, 0x84000000UL, 0x53000000UL, 0xd1000000UL, 0x00000000UL, 0xed000000UL, 0x20000000UL, 0xfc000000UL, 0xb1000000UL, 0x5b000000UL, 0x6a000000UL, 0xcb000000UL, 0xbe000000UL, 0x39000000UL, 0x4a000000UL, 0x4c000000UL, 0x58000000UL, 0xcf000000UL, 0xd0000000UL, 0xef000000UL, 0xaa000000UL, 0xfb000000UL, 0x43000000UL, 0x4d000000UL, 0x33000000UL, 0x85000000UL, 0x45000000UL, 0xf9000000UL, 0x02000000UL, 0x7f000000UL, 0x50000000UL, 0x3c000000UL, 0x9f000000UL, 0xa8000000UL, 0x51000000UL, 0xa3000000UL, 0x40000000UL, 0x8f000000UL, 0x92000000UL, 0x9d000000UL, 0x38000000UL, 0xf5000000UL, 0xbc000000UL, 0xb6000000UL, 0xda000000UL, 0x21000000UL, 0x10000000UL, 0xff000000UL, 0xf3000000UL, 0xd2000000UL, 0xcd000000UL, 0x0c000000UL, 0x13000000UL, 0xec000000UL, 0x5f000000UL, 0x97000000UL, 0x44000000UL, 0x17000000UL, 0xc4000000UL, 0xa7000000UL, 0x7e000000UL, 0x3d000000UL, 0x64000000UL, 0x5d000000UL, 0x19000000UL, 0x73000000UL, 0x60000000UL, 0x81000000UL, 0x4f000000UL, 0xdc000000UL, 0x22000000UL, 0x2a000000UL, 0x90000000UL, 0x88000000UL, 0x46000000UL, 0xee000000UL, 0xb8000000UL, 0x14000000UL, 0xde000000UL, 0x5e000000UL, 0x0b000000UL, 0xdb000000UL, 0xe0000000UL, 0x32000000UL, 0x3a000000UL, 0x0a000000UL, 0x49000000UL, 0x06000000UL, 0x24000000UL, 0x5c000000UL, 0xc2000000UL, 0xd3000000UL, 0xac000000UL, 0x62000000UL, 0x91000000UL, 0x95000000UL, 0xe4000000UL, 0x79000000UL, 0xe7000000UL, 0xc8000000UL, 0x37000000UL, 0x6d000000UL, 0x8d000000UL, 0xd5000000UL, 0x4e000000UL, 0xa9000000UL, 0x6c000000UL, 0x56000000UL, 0xf4000000UL, 0xea000000UL, 0x65000000UL, 0x7a000000UL, 0xae000000UL, 0x08000000UL, 0xba000000UL, 0x78000000UL, 0x25000000UL, 0x2e000000UL, 0x1c000000UL, 0xa6000000UL, 0xb4000000UL, 0xc6000000UL, 0xe8000000UL, 0xdd000000UL, 0x74000000UL, 0x1f000000UL, 0x4b000000UL, 0xbd000000UL, 0x8b000000UL, 0x8a000000UL, 0x70000000UL, 0x3e000000UL, 0xb5000000UL, 0x66000000UL, 0x48000000UL, 0x03000000UL, 0xf6000000UL, 0x0e000000UL, 0x61000000UL, 0x35000000UL, 0x57000000UL, 0xb9000000UL, 0x86000000UL, 0xc1000000UL, 0x1d000000UL, 0x9e000000UL, 0xe1000000UL, 0xf8000000UL, 0x98000000UL, 0x11000000UL, 0x69000000UL, 0xd9000000UL, 0x8e000000UL, 0x94000000UL, 0x9b000000UL, 0x1e000000UL, 0x87000000UL, 0xe9000000UL, 0xce000000UL, 0x55000000UL, 0x28000000UL, 0xdf000000UL, 0x8c000000UL, 0xa1000000UL, 0x89000000UL, 0x0d000000UL, 0xbf000000UL, 0xe6000000UL, 0x42000000UL, 0x68000000UL, 0x41000000UL, 0x99000000UL, 0x2d000000UL, 0x0f000000UL, 0xb0000000UL, 0x54000000UL, 0xbb000000UL, 0x16000000UL }; #endif /* pelimac */ #ifndef ENCRYPT_ONLY static const ulong32 TD1[256] = { 0x5051f4a7UL, 0x537e4165UL, 0xc31a17a4UL, 0x963a275eUL, 0xcb3bab6bUL, 0xf11f9d45UL, 0xabacfa58UL, 0x934be303UL, 0x552030faUL, 0xf6ad766dUL, 0x9188cc76UL, 0x25f5024cUL, 0xfc4fe5d7UL, 0xd7c52acbUL, 0x80263544UL, 0x8fb562a3UL, 0x49deb15aUL, 0x6725ba1bUL, 0x9845ea0eUL, 0xe15dfec0UL, 0x02c32f75UL, 0x12814cf0UL, 0xa38d4697UL, 0xc66bd3f9UL, 0xe7038f5fUL, 0x9515929cUL, 0xebbf6d7aUL, 0xda955259UL, 0x2dd4be83UL, 0xd3587421UL, 0x2949e069UL, 0x448ec9c8UL, 0x6a75c289UL, 0x78f48e79UL, 0x6b99583eUL, 0xdd27b971UL, 0xb6bee14fUL, 0x17f088adUL, 0x66c920acUL, 0xb47dce3aUL, 0x1863df4aUL, 0x82e51a31UL, 0x60975133UL, 0x4562537fUL, 0xe0b16477UL, 0x84bb6baeUL, 0x1cfe81a0UL, 0x94f9082bUL, 0x58704868UL, 0x198f45fdUL, 0x8794de6cUL, 0xb7527bf8UL, 0x23ab73d3UL, 0xe2724b02UL, 0x57e31f8fUL, 0x2a6655abUL, 0x07b2eb28UL, 0x032fb5c2UL, 0x9a86c57bUL, 0xa5d33708UL, 0xf2302887UL, 0xb223bfa5UL, 0xba02036aUL, 0x5ced1682UL, 0x2b8acf1cUL, 0x92a779b4UL, 0xf0f307f2UL, 0xa14e69e2UL, 0xcd65daf4UL, 0xd50605beUL, 0x1fd13462UL, 0x8ac4a6feUL, 0x9d342e53UL, 0xa0a2f355UL, 0x32058ae1UL, 0x75a4f6ebUL, 0x390b83ecUL, 0xaa4060efUL, 0x065e719fUL, 0x51bd6e10UL, 0xf93e218aUL, 0x3d96dd06UL, 0xaedd3e05UL, 0x464de6bdUL, 0xb591548dUL, 0x0571c45dUL, 0x6f0406d4UL, 0xff605015UL, 0x241998fbUL, 0x97d6bde9UL, 0xcc894043UL, 0x7767d99eUL, 0xbdb0e842UL, 0x8807898bUL, 0x38e7195bUL, 0xdb79c8eeUL, 0x47a17c0aUL, 0xe97c420fUL, 0xc9f8841eUL, 0x00000000UL, 0x83098086UL, 0x48322bedUL, 0xac1e1170UL, 0x4e6c5a72UL, 0xfbfd0effUL, 0x560f8538UL, 0x1e3daed5UL, 0x27362d39UL, 0x640a0fd9UL, 0x21685ca6UL, 0xd19b5b54UL, 0x3a24362eUL, 0xb10c0a67UL, 0x0f9357e7UL, 0xd2b4ee96UL, 0x9e1b9b91UL, 0x4f80c0c5UL, 0xa261dc20UL, 0x695a774bUL, 0x161c121aUL, 0x0ae293baUL, 0xe5c0a02aUL, 0x433c22e0UL, 0x1d121b17UL, 0x0b0e090dUL, 0xadf28bc7UL, 0xb92db6a8UL, 0xc8141ea9UL, 0x8557f119UL, 0x4caf7507UL, 0xbbee99ddUL, 0xfda37f60UL, 0x9ff70126UL, 0xbc5c72f5UL, 0xc544663bUL, 0x345bfb7eUL, 0x768b4329UL, 0xdccb23c6UL, 0x68b6edfcUL, 0x63b8e4f1UL, 0xcad731dcUL, 0x10426385UL, 0x40139722UL, 0x2084c611UL, 0x7d854a24UL, 0xf8d2bb3dUL, 0x11aef932UL, 0x6dc729a1UL, 0x4b1d9e2fUL, 0xf3dcb230UL, 0xec0d8652UL, 0xd077c1e3UL, 0x6c2bb316UL, 0x99a970b9UL, 0xfa119448UL, 0x2247e964UL, 0xc4a8fc8cUL, 0x1aa0f03fUL, 0xd8567d2cUL, 0xef223390UL, 0xc787494eUL, 0xc1d938d1UL, 0xfe8ccaa2UL, 0x3698d40bUL, 0xcfa6f581UL, 0x28a57adeUL, 0x26dab78eUL, 0xa43fadbfUL, 0xe42c3a9dUL, 0x0d507892UL, 0x9b6a5fccUL, 0x62547e46UL, 0xc2f68d13UL, 0xe890d8b8UL, 0x5e2e39f7UL, 0xf582c3afUL, 0xbe9f5d80UL, 0x7c69d093UL, 0xa96fd52dUL, 0xb3cf2512UL, 0x3bc8ac99UL, 0xa710187dUL, 0x6ee89c63UL, 0x7bdb3bbbUL, 0x09cd2678UL, 0xf46e5918UL, 0x01ec9ab7UL, 0xa8834f9aUL, 0x65e6956eUL, 0x7eaaffe6UL, 0x0821bccfUL, 0xe6ef15e8UL, 0xd9bae79bUL, 0xce4a6f36UL, 0xd4ea9f09UL, 0xd629b07cUL, 0xaf31a4b2UL, 0x312a3f23UL, 0x30c6a594UL, 0xc035a266UL, 0x37744ebcUL, 0xa6fc82caUL, 0xb0e090d0UL, 0x1533a7d8UL, 0x4af10498UL, 0xf741ecdaUL, 0x0e7fcd50UL, 0x2f1791f6UL, 0x8d764dd6UL, 0x4d43efb0UL, 0x54ccaa4dUL, 0xdfe49604UL, 0xe39ed1b5UL, 0x1b4c6a88UL, 0xb8c12c1fUL, 0x7f466551UL, 0x049d5eeaUL, 0x5d018c35UL, 0x73fa8774UL, 0x2efb0b41UL, 0x5ab3671dUL, 0x5292dbd2UL, 0x33e91056UL, 0x136dd647UL, 0x8c9ad761UL, 0x7a37a10cUL, 0x8e59f814UL, 0x89eb133cUL, 0xeecea927UL, 0x35b761c9UL, 0xede11ce5UL, 0x3c7a47b1UL, 0x599cd2dfUL, 0x3f55f273UL, 0x791814ceUL, 0xbf73c737UL, 0xea53f7cdUL, 0x5b5ffdaaUL, 0x14df3d6fUL, 0x867844dbUL, 0x81caaff3UL, 0x3eb968c4UL, 0x2c382434UL, 0x5fc2a340UL, 0x72161dc3UL, 0x0cbce225UL, 0x8b283c49UL, 0x41ff0d95UL, 0x7139a801UL, 0xde080cb3UL, 0x9cd8b4e4UL, 0x906456c1UL, 0x617bcb84UL, 0x70d532b6UL, 0x74486c5cUL, 0x42d0b857UL, }; static const ulong32 TD2[256] = { 0xa75051f4UL, 0x65537e41UL, 0xa4c31a17UL, 0x5e963a27UL, 0x6bcb3babUL, 0x45f11f9dUL, 0x58abacfaUL, 0x03934be3UL, 0xfa552030UL, 0x6df6ad76UL, 0x769188ccUL, 0x4c25f502UL, 0xd7fc4fe5UL, 0xcbd7c52aUL, 0x44802635UL, 0xa38fb562UL, 0x5a49deb1UL, 0x1b6725baUL, 0x0e9845eaUL, 0xc0e15dfeUL, 0x7502c32fUL, 0xf012814cUL, 0x97a38d46UL, 0xf9c66bd3UL, 0x5fe7038fUL, 0x9c951592UL, 0x7aebbf6dUL, 0x59da9552UL, 0x832dd4beUL, 0x21d35874UL, 0x692949e0UL, 0xc8448ec9UL, 0x896a75c2UL, 0x7978f48eUL, 0x3e6b9958UL, 0x71dd27b9UL, 0x4fb6bee1UL, 0xad17f088UL, 0xac66c920UL, 0x3ab47dceUL, 0x4a1863dfUL, 0x3182e51aUL, 0x33609751UL, 0x7f456253UL, 0x77e0b164UL, 0xae84bb6bUL, 0xa01cfe81UL, 0x2b94f908UL, 0x68587048UL, 0xfd198f45UL, 0x6c8794deUL, 0xf8b7527bUL, 0xd323ab73UL, 0x02e2724bUL, 0x8f57e31fUL, 0xab2a6655UL, 0x2807b2ebUL, 0xc2032fb5UL, 0x7b9a86c5UL, 0x08a5d337UL, 0x87f23028UL, 0xa5b223bfUL, 0x6aba0203UL, 0x825ced16UL, 0x1c2b8acfUL, 0xb492a779UL, 0xf2f0f307UL, 0xe2a14e69UL, 0xf4cd65daUL, 0xbed50605UL, 0x621fd134UL, 0xfe8ac4a6UL, 0x539d342eUL, 0x55a0a2f3UL, 0xe132058aUL, 0xeb75a4f6UL, 0xec390b83UL, 0xefaa4060UL, 0x9f065e71UL, 0x1051bd6eUL, 0x8af93e21UL, 0x063d96ddUL, 0x05aedd3eUL, 0xbd464de6UL, 0x8db59154UL, 0x5d0571c4UL, 0xd46f0406UL, 0x15ff6050UL, 0xfb241998UL, 0xe997d6bdUL, 0x43cc8940UL, 0x9e7767d9UL, 0x42bdb0e8UL, 0x8b880789UL, 0x5b38e719UL, 0xeedb79c8UL, 0x0a47a17cUL, 0x0fe97c42UL, 0x1ec9f884UL, 0x00000000UL, 0x86830980UL, 0xed48322bUL, 0x70ac1e11UL, 0x724e6c5aUL, 0xfffbfd0eUL, 0x38560f85UL, 0xd51e3daeUL, 0x3927362dUL, 0xd9640a0fUL, 0xa621685cUL, 0x54d19b5bUL, 0x2e3a2436UL, 0x67b10c0aUL, 0xe70f9357UL, 0x96d2b4eeUL, 0x919e1b9bUL, 0xc54f80c0UL, 0x20a261dcUL, 0x4b695a77UL, 0x1a161c12UL, 0xba0ae293UL, 0x2ae5c0a0UL, 0xe0433c22UL, 0x171d121bUL, 0x0d0b0e09UL, 0xc7adf28bUL, 0xa8b92db6UL, 0xa9c8141eUL, 0x198557f1UL, 0x074caf75UL, 0xddbbee99UL, 0x60fda37fUL, 0x269ff701UL, 0xf5bc5c72UL, 0x3bc54466UL, 0x7e345bfbUL, 0x29768b43UL, 0xc6dccb23UL, 0xfc68b6edUL, 0xf163b8e4UL, 0xdccad731UL, 0x85104263UL, 0x22401397UL, 0x112084c6UL, 0x247d854aUL, 0x3df8d2bbUL, 0x3211aef9UL, 0xa16dc729UL, 0x2f4b1d9eUL, 0x30f3dcb2UL, 0x52ec0d86UL, 0xe3d077c1UL, 0x166c2bb3UL, 0xb999a970UL, 0x48fa1194UL, 0x642247e9UL, 0x8cc4a8fcUL, 0x3f1aa0f0UL, 0x2cd8567dUL, 0x90ef2233UL, 0x4ec78749UL, 0xd1c1d938UL, 0xa2fe8ccaUL, 0x0b3698d4UL, 0x81cfa6f5UL, 0xde28a57aUL, 0x8e26dab7UL, 0xbfa43fadUL, 0x9de42c3aUL, 0x920d5078UL, 0xcc9b6a5fUL, 0x4662547eUL, 0x13c2f68dUL, 0xb8e890d8UL, 0xf75e2e39UL, 0xaff582c3UL, 0x80be9f5dUL, 0x937c69d0UL, 0x2da96fd5UL, 0x12b3cf25UL, 0x993bc8acUL, 0x7da71018UL, 0x636ee89cUL, 0xbb7bdb3bUL, 0x7809cd26UL, 0x18f46e59UL, 0xb701ec9aUL, 0x9aa8834fUL, 0x6e65e695UL, 0xe67eaaffUL, 0xcf0821bcUL, 0xe8e6ef15UL, 0x9bd9bae7UL, 0x36ce4a6fUL, 0x09d4ea9fUL, 0x7cd629b0UL, 0xb2af31a4UL, 0x23312a3fUL, 0x9430c6a5UL, 0x66c035a2UL, 0xbc37744eUL, 0xcaa6fc82UL, 0xd0b0e090UL, 0xd81533a7UL, 0x984af104UL, 0xdaf741ecUL, 0x500e7fcdUL, 0xf62f1791UL, 0xd68d764dUL, 0xb04d43efUL, 0x4d54ccaaUL, 0x04dfe496UL, 0xb5e39ed1UL, 0x881b4c6aUL, 0x1fb8c12cUL, 0x517f4665UL, 0xea049d5eUL, 0x355d018cUL, 0x7473fa87UL, 0x412efb0bUL, 0x1d5ab367UL, 0xd25292dbUL, 0x5633e910UL, 0x47136dd6UL, 0x618c9ad7UL, 0x0c7a37a1UL, 0x148e59f8UL, 0x3c89eb13UL, 0x27eecea9UL, 0xc935b761UL, 0xe5ede11cUL, 0xb13c7a47UL, 0xdf599cd2UL, 0x733f55f2UL, 0xce791814UL, 0x37bf73c7UL, 0xcdea53f7UL, 0xaa5b5ffdUL, 0x6f14df3dUL, 0xdb867844UL, 0xf381caafUL, 0xc43eb968UL, 0x342c3824UL, 0x405fc2a3UL, 0xc372161dUL, 0x250cbce2UL, 0x498b283cUL, 0x9541ff0dUL, 0x017139a8UL, 0xb3de080cUL, 0xe49cd8b4UL, 0xc1906456UL, 0x84617bcbUL, 0xb670d532UL, 0x5c74486cUL, 0x5742d0b8UL, }; static const ulong32 TD3[256] = { 0xf4a75051UL, 0x4165537eUL, 0x17a4c31aUL, 0x275e963aUL, 0xab6bcb3bUL, 0x9d45f11fUL, 0xfa58abacUL, 0xe303934bUL, 0x30fa5520UL, 0x766df6adUL, 0xcc769188UL, 0x024c25f5UL, 0xe5d7fc4fUL, 0x2acbd7c5UL, 0x35448026UL, 0x62a38fb5UL, 0xb15a49deUL, 0xba1b6725UL, 0xea0e9845UL, 0xfec0e15dUL, 0x2f7502c3UL, 0x4cf01281UL, 0x4697a38dUL, 0xd3f9c66bUL, 0x8f5fe703UL, 0x929c9515UL, 0x6d7aebbfUL, 0x5259da95UL, 0xbe832dd4UL, 0x7421d358UL, 0xe0692949UL, 0xc9c8448eUL, 0xc2896a75UL, 0x8e7978f4UL, 0x583e6b99UL, 0xb971dd27UL, 0xe14fb6beUL, 0x88ad17f0UL, 0x20ac66c9UL, 0xce3ab47dUL, 0xdf4a1863UL, 0x1a3182e5UL, 0x51336097UL, 0x537f4562UL, 0x6477e0b1UL, 0x6bae84bbUL, 0x81a01cfeUL, 0x082b94f9UL, 0x48685870UL, 0x45fd198fUL, 0xde6c8794UL, 0x7bf8b752UL, 0x73d323abUL, 0x4b02e272UL, 0x1f8f57e3UL, 0x55ab2a66UL, 0xeb2807b2UL, 0xb5c2032fUL, 0xc57b9a86UL, 0x3708a5d3UL, 0x2887f230UL, 0xbfa5b223UL, 0x036aba02UL, 0x16825cedUL, 0xcf1c2b8aUL, 0x79b492a7UL, 0x07f2f0f3UL, 0x69e2a14eUL, 0xdaf4cd65UL, 0x05bed506UL, 0x34621fd1UL, 0xa6fe8ac4UL, 0x2e539d34UL, 0xf355a0a2UL, 0x8ae13205UL, 0xf6eb75a4UL, 0x83ec390bUL, 0x60efaa40UL, 0x719f065eUL, 0x6e1051bdUL, 0x218af93eUL, 0xdd063d96UL, 0x3e05aeddUL, 0xe6bd464dUL, 0x548db591UL, 0xc45d0571UL, 0x06d46f04UL, 0x5015ff60UL, 0x98fb2419UL, 0xbde997d6UL, 0x4043cc89UL, 0xd99e7767UL, 0xe842bdb0UL, 0x898b8807UL, 0x195b38e7UL, 0xc8eedb79UL, 0x7c0a47a1UL, 0x420fe97cUL, 0x841ec9f8UL, 0x00000000UL, 0x80868309UL, 0x2bed4832UL, 0x1170ac1eUL, 0x5a724e6cUL, 0x0efffbfdUL, 0x8538560fUL, 0xaed51e3dUL, 0x2d392736UL, 0x0fd9640aUL, 0x5ca62168UL, 0x5b54d19bUL, 0x362e3a24UL, 0x0a67b10cUL, 0x57e70f93UL, 0xee96d2b4UL, 0x9b919e1bUL, 0xc0c54f80UL, 0xdc20a261UL, 0x774b695aUL, 0x121a161cUL, 0x93ba0ae2UL, 0xa02ae5c0UL, 0x22e0433cUL, 0x1b171d12UL, 0x090d0b0eUL, 0x8bc7adf2UL, 0xb6a8b92dUL, 0x1ea9c814UL, 0xf1198557UL, 0x75074cafUL, 0x99ddbbeeUL, 0x7f60fda3UL, 0x01269ff7UL, 0x72f5bc5cUL, 0x663bc544UL, 0xfb7e345bUL, 0x4329768bUL, 0x23c6dccbUL, 0xedfc68b6UL, 0xe4f163b8UL, 0x31dccad7UL, 0x63851042UL, 0x97224013UL, 0xc6112084UL, 0x4a247d85UL, 0xbb3df8d2UL, 0xf93211aeUL, 0x29a16dc7UL, 0x9e2f4b1dUL, 0xb230f3dcUL, 0x8652ec0dUL, 0xc1e3d077UL, 0xb3166c2bUL, 0x70b999a9UL, 0x9448fa11UL, 0xe9642247UL, 0xfc8cc4a8UL, 0xf03f1aa0UL, 0x7d2cd856UL, 0x3390ef22UL, 0x494ec787UL, 0x38d1c1d9UL, 0xcaa2fe8cUL, 0xd40b3698UL, 0xf581cfa6UL, 0x7ade28a5UL, 0xb78e26daUL, 0xadbfa43fUL, 0x3a9de42cUL, 0x78920d50UL, 0x5fcc9b6aUL, 0x7e466254UL, 0x8d13c2f6UL, 0xd8b8e890UL, 0x39f75e2eUL, 0xc3aff582UL, 0x5d80be9fUL, 0xd0937c69UL, 0xd52da96fUL, 0x2512b3cfUL, 0xac993bc8UL, 0x187da710UL, 0x9c636ee8UL, 0x3bbb7bdbUL, 0x267809cdUL, 0x5918f46eUL, 0x9ab701ecUL, 0x4f9aa883UL, 0x956e65e6UL, 0xffe67eaaUL, 0xbccf0821UL, 0x15e8e6efUL, 0xe79bd9baUL, 0x6f36ce4aUL, 0x9f09d4eaUL, 0xb07cd629UL, 0xa4b2af31UL, 0x3f23312aUL, 0xa59430c6UL, 0xa266c035UL, 0x4ebc3774UL, 0x82caa6fcUL, 0x90d0b0e0UL, 0xa7d81533UL, 0x04984af1UL, 0xecdaf741UL, 0xcd500e7fUL, 0x91f62f17UL, 0x4dd68d76UL, 0xefb04d43UL, 0xaa4d54ccUL, 0x9604dfe4UL, 0xd1b5e39eUL, 0x6a881b4cUL, 0x2c1fb8c1UL, 0x65517f46UL, 0x5eea049dUL, 0x8c355d01UL, 0x877473faUL, 0x0b412efbUL, 0x671d5ab3UL, 0xdbd25292UL, 0x105633e9UL, 0xd647136dUL, 0xd7618c9aUL, 0xa10c7a37UL, 0xf8148e59UL, 0x133c89ebUL, 0xa927eeceUL, 0x61c935b7UL, 0x1ce5ede1UL, 0x47b13c7aUL, 0xd2df599cUL, 0xf2733f55UL, 0x14ce7918UL, 0xc737bf73UL, 0xf7cdea53UL, 0xfdaa5b5fUL, 0x3d6f14dfUL, 0x44db8678UL, 0xaff381caUL, 0x68c43eb9UL, 0x24342c38UL, 0xa3405fc2UL, 0x1dc37216UL, 0xe2250cbcUL, 0x3c498b28UL, 0x0d9541ffUL, 0xa8017139UL, 0x0cb3de08UL, 0xb4e49cd8UL, 0x56c19064UL, 0xcb84617bUL, 0x32b670d5UL, 0x6c5c7448UL, 0xb85742d0UL, }; static const ulong32 Tks0[] = { 0x00000000UL, 0x0e090d0bUL, 0x1c121a16UL, 0x121b171dUL, 0x3824342cUL, 0x362d3927UL, 0x24362e3aUL, 0x2a3f2331UL, 0x70486858UL, 0x7e416553UL, 0x6c5a724eUL, 0x62537f45UL, 0x486c5c74UL, 0x4665517fUL, 0x547e4662UL, 0x5a774b69UL, 0xe090d0b0UL, 0xee99ddbbUL, 0xfc82caa6UL, 0xf28bc7adUL, 0xd8b4e49cUL, 0xd6bde997UL, 0xc4a6fe8aUL, 0xcaaff381UL, 0x90d8b8e8UL, 0x9ed1b5e3UL, 0x8ccaa2feUL, 0x82c3aff5UL, 0xa8fc8cc4UL, 0xa6f581cfUL, 0xb4ee96d2UL, 0xbae79bd9UL, 0xdb3bbb7bUL, 0xd532b670UL, 0xc729a16dUL, 0xc920ac66UL, 0xe31f8f57UL, 0xed16825cUL, 0xff0d9541UL, 0xf104984aUL, 0xab73d323UL, 0xa57ade28UL, 0xb761c935UL, 0xb968c43eUL, 0x9357e70fUL, 0x9d5eea04UL, 0x8f45fd19UL, 0x814cf012UL, 0x3bab6bcbUL, 0x35a266c0UL, 0x27b971ddUL, 0x29b07cd6UL, 0x038f5fe7UL, 0x0d8652ecUL, 0x1f9d45f1UL, 0x119448faUL, 0x4be30393UL, 0x45ea0e98UL, 0x57f11985UL, 0x59f8148eUL, 0x73c737bfUL, 0x7dce3ab4UL, 0x6fd52da9UL, 0x61dc20a2UL, 0xad766df6UL, 0xa37f60fdUL, 0xb16477e0UL, 0xbf6d7aebUL, 0x955259daUL, 0x9b5b54d1UL, 0x894043ccUL, 0x87494ec7UL, 0xdd3e05aeUL, 0xd33708a5UL, 0xc12c1fb8UL, 0xcf2512b3UL, 0xe51a3182UL, 0xeb133c89UL, 0xf9082b94UL, 0xf701269fUL, 0x4de6bd46UL, 0x43efb04dUL, 0x51f4a750UL, 0x5ffdaa5bUL, 0x75c2896aUL, 0x7bcb8461UL, 0x69d0937cUL, 0x67d99e77UL, 0x3daed51eUL, 0x33a7d815UL, 0x21bccf08UL, 0x2fb5c203UL, 0x058ae132UL, 0x0b83ec39UL, 0x1998fb24UL, 0x1791f62fUL, 0x764dd68dUL, 0x7844db86UL, 0x6a5fcc9bUL, 0x6456c190UL, 0x4e69e2a1UL, 0x4060efaaUL, 0x527bf8b7UL, 0x5c72f5bcUL, 0x0605bed5UL, 0x080cb3deUL, 0x1a17a4c3UL, 0x141ea9c8UL, 0x3e218af9UL, 0x302887f2UL, 0x223390efUL, 0x2c3a9de4UL, 0x96dd063dUL, 0x98d40b36UL, 0x8acf1c2bUL, 0x84c61120UL, 0xaef93211UL, 0xa0f03f1aUL, 0xb2eb2807UL, 0xbce2250cUL, 0xe6956e65UL, 0xe89c636eUL, 0xfa877473UL, 0xf48e7978UL, 0xdeb15a49UL, 0xd0b85742UL, 0xc2a3405fUL, 0xccaa4d54UL, 0x41ecdaf7UL, 0x4fe5d7fcUL, 0x5dfec0e1UL, 0x53f7cdeaUL, 0x79c8eedbUL, 0x77c1e3d0UL, 0x65daf4cdUL, 0x6bd3f9c6UL, 0x31a4b2afUL, 0x3fadbfa4UL, 0x2db6a8b9UL, 0x23bfa5b2UL, 0x09808683UL, 0x07898b88UL, 0x15929c95UL, 0x1b9b919eUL, 0xa17c0a47UL, 0xaf75074cUL, 0xbd6e1051UL, 0xb3671d5aUL, 0x99583e6bUL, 0x97513360UL, 0x854a247dUL, 0x8b432976UL, 0xd134621fUL, 0xdf3d6f14UL, 0xcd267809UL, 0xc32f7502UL, 0xe9105633UL, 0xe7195b38UL, 0xf5024c25UL, 0xfb0b412eUL, 0x9ad7618cUL, 0x94de6c87UL, 0x86c57b9aUL, 0x88cc7691UL, 0xa2f355a0UL, 0xacfa58abUL, 0xbee14fb6UL, 0xb0e842bdUL, 0xea9f09d4UL, 0xe49604dfUL, 0xf68d13c2UL, 0xf8841ec9UL, 0xd2bb3df8UL, 0xdcb230f3UL, 0xcea927eeUL, 0xc0a02ae5UL, 0x7a47b13cUL, 0x744ebc37UL, 0x6655ab2aUL, 0x685ca621UL, 0x42638510UL, 0x4c6a881bUL, 0x5e719f06UL, 0x5078920dUL, 0x0a0fd964UL, 0x0406d46fUL, 0x161dc372UL, 0x1814ce79UL, 0x322bed48UL, 0x3c22e043UL, 0x2e39f75eUL, 0x2030fa55UL, 0xec9ab701UL, 0xe293ba0aUL, 0xf088ad17UL, 0xfe81a01cUL, 0xd4be832dUL, 0xdab78e26UL, 0xc8ac993bUL, 0xc6a59430UL, 0x9cd2df59UL, 0x92dbd252UL, 0x80c0c54fUL, 0x8ec9c844UL, 0xa4f6eb75UL, 0xaaffe67eUL, 0xb8e4f163UL, 0xb6edfc68UL, 0x0c0a67b1UL, 0x02036abaUL, 0x10187da7UL, 0x1e1170acUL, 0x342e539dUL, 0x3a275e96UL, 0x283c498bUL, 0x26354480UL, 0x7c420fe9UL, 0x724b02e2UL, 0x605015ffUL, 0x6e5918f4UL, 0x44663bc5UL, 0x4a6f36ceUL, 0x587421d3UL, 0x567d2cd8UL, 0x37a10c7aUL, 0x39a80171UL, 0x2bb3166cUL, 0x25ba1b67UL, 0x0f853856UL, 0x018c355dUL, 0x13972240UL, 0x1d9e2f4bUL, 0x47e96422UL, 0x49e06929UL, 0x5bfb7e34UL, 0x55f2733fUL, 0x7fcd500eUL, 0x71c45d05UL, 0x63df4a18UL, 0x6dd64713UL, 0xd731dccaUL, 0xd938d1c1UL, 0xcb23c6dcUL, 0xc52acbd7UL, 0xef15e8e6UL, 0xe11ce5edUL, 0xf307f2f0UL, 0xfd0efffbUL, 0xa779b492UL, 0xa970b999UL, 0xbb6bae84UL, 0xb562a38fUL, 0x9f5d80beUL, 0x91548db5UL, 0x834f9aa8UL, 0x8d4697a3UL }; static const ulong32 Tks1[] = { 0x00000000UL, 0x0b0e090dUL, 0x161c121aUL, 0x1d121b17UL, 0x2c382434UL, 0x27362d39UL, 0x3a24362eUL, 0x312a3f23UL, 0x58704868UL, 0x537e4165UL, 0x4e6c5a72UL, 0x4562537fUL, 0x74486c5cUL, 0x7f466551UL, 0x62547e46UL, 0x695a774bUL, 0xb0e090d0UL, 0xbbee99ddUL, 0xa6fc82caUL, 0xadf28bc7UL, 0x9cd8b4e4UL, 0x97d6bde9UL, 0x8ac4a6feUL, 0x81caaff3UL, 0xe890d8b8UL, 0xe39ed1b5UL, 0xfe8ccaa2UL, 0xf582c3afUL, 0xc4a8fc8cUL, 0xcfa6f581UL, 0xd2b4ee96UL, 0xd9bae79bUL, 0x7bdb3bbbUL, 0x70d532b6UL, 0x6dc729a1UL, 0x66c920acUL, 0x57e31f8fUL, 0x5ced1682UL, 0x41ff0d95UL, 0x4af10498UL, 0x23ab73d3UL, 0x28a57adeUL, 0x35b761c9UL, 0x3eb968c4UL, 0x0f9357e7UL, 0x049d5eeaUL, 0x198f45fdUL, 0x12814cf0UL, 0xcb3bab6bUL, 0xc035a266UL, 0xdd27b971UL, 0xd629b07cUL, 0xe7038f5fUL, 0xec0d8652UL, 0xf11f9d45UL, 0xfa119448UL, 0x934be303UL, 0x9845ea0eUL, 0x8557f119UL, 0x8e59f814UL, 0xbf73c737UL, 0xb47dce3aUL, 0xa96fd52dUL, 0xa261dc20UL, 0xf6ad766dUL, 0xfda37f60UL, 0xe0b16477UL, 0xebbf6d7aUL, 0xda955259UL, 0xd19b5b54UL, 0xcc894043UL, 0xc787494eUL, 0xaedd3e05UL, 0xa5d33708UL, 0xb8c12c1fUL, 0xb3cf2512UL, 0x82e51a31UL, 0x89eb133cUL, 0x94f9082bUL, 0x9ff70126UL, 0x464de6bdUL, 0x4d43efb0UL, 0x5051f4a7UL, 0x5b5ffdaaUL, 0x6a75c289UL, 0x617bcb84UL, 0x7c69d093UL, 0x7767d99eUL, 0x1e3daed5UL, 0x1533a7d8UL, 0x0821bccfUL, 0x032fb5c2UL, 0x32058ae1UL, 0x390b83ecUL, 0x241998fbUL, 0x2f1791f6UL, 0x8d764dd6UL, 0x867844dbUL, 0x9b6a5fccUL, 0x906456c1UL, 0xa14e69e2UL, 0xaa4060efUL, 0xb7527bf8UL, 0xbc5c72f5UL, 0xd50605beUL, 0xde080cb3UL, 0xc31a17a4UL, 0xc8141ea9UL, 0xf93e218aUL, 0xf2302887UL, 0xef223390UL, 0xe42c3a9dUL, 0x3d96dd06UL, 0x3698d40bUL, 0x2b8acf1cUL, 0x2084c611UL, 0x11aef932UL, 0x1aa0f03fUL, 0x07b2eb28UL, 0x0cbce225UL, 0x65e6956eUL, 0x6ee89c63UL, 0x73fa8774UL, 0x78f48e79UL, 0x49deb15aUL, 0x42d0b857UL, 0x5fc2a340UL, 0x54ccaa4dUL, 0xf741ecdaUL, 0xfc4fe5d7UL, 0xe15dfec0UL, 0xea53f7cdUL, 0xdb79c8eeUL, 0xd077c1e3UL, 0xcd65daf4UL, 0xc66bd3f9UL, 0xaf31a4b2UL, 0xa43fadbfUL, 0xb92db6a8UL, 0xb223bfa5UL, 0x83098086UL, 0x8807898bUL, 0x9515929cUL, 0x9e1b9b91UL, 0x47a17c0aUL, 0x4caf7507UL, 0x51bd6e10UL, 0x5ab3671dUL, 0x6b99583eUL, 0x60975133UL, 0x7d854a24UL, 0x768b4329UL, 0x1fd13462UL, 0x14df3d6fUL, 0x09cd2678UL, 0x02c32f75UL, 0x33e91056UL, 0x38e7195bUL, 0x25f5024cUL, 0x2efb0b41UL, 0x8c9ad761UL, 0x8794de6cUL, 0x9a86c57bUL, 0x9188cc76UL, 0xa0a2f355UL, 0xabacfa58UL, 0xb6bee14fUL, 0xbdb0e842UL, 0xd4ea9f09UL, 0xdfe49604UL, 0xc2f68d13UL, 0xc9f8841eUL, 0xf8d2bb3dUL, 0xf3dcb230UL, 0xeecea927UL, 0xe5c0a02aUL, 0x3c7a47b1UL, 0x37744ebcUL, 0x2a6655abUL, 0x21685ca6UL, 0x10426385UL, 0x1b4c6a88UL, 0x065e719fUL, 0x0d507892UL, 0x640a0fd9UL, 0x6f0406d4UL, 0x72161dc3UL, 0x791814ceUL, 0x48322bedUL, 0x433c22e0UL, 0x5e2e39f7UL, 0x552030faUL, 0x01ec9ab7UL, 0x0ae293baUL, 0x17f088adUL, 0x1cfe81a0UL, 0x2dd4be83UL, 0x26dab78eUL, 0x3bc8ac99UL, 0x30c6a594UL, 0x599cd2dfUL, 0x5292dbd2UL, 0x4f80c0c5UL, 0x448ec9c8UL, 0x75a4f6ebUL, 0x7eaaffe6UL, 0x63b8e4f1UL, 0x68b6edfcUL, 0xb10c0a67UL, 0xba02036aUL, 0xa710187dUL, 0xac1e1170UL, 0x9d342e53UL, 0x963a275eUL, 0x8b283c49UL, 0x80263544UL, 0xe97c420fUL, 0xe2724b02UL, 0xff605015UL, 0xf46e5918UL, 0xc544663bUL, 0xce4a6f36UL, 0xd3587421UL, 0xd8567d2cUL, 0x7a37a10cUL, 0x7139a801UL, 0x6c2bb316UL, 0x6725ba1bUL, 0x560f8538UL, 0x5d018c35UL, 0x40139722UL, 0x4b1d9e2fUL, 0x2247e964UL, 0x2949e069UL, 0x345bfb7eUL, 0x3f55f273UL, 0x0e7fcd50UL, 0x0571c45dUL, 0x1863df4aUL, 0x136dd647UL, 0xcad731dcUL, 0xc1d938d1UL, 0xdccb23c6UL, 0xd7c52acbUL, 0xe6ef15e8UL, 0xede11ce5UL, 0xf0f307f2UL, 0xfbfd0effUL, 0x92a779b4UL, 0x99a970b9UL, 0x84bb6baeUL, 0x8fb562a3UL, 0xbe9f5d80UL, 0xb591548dUL, 0xa8834f9aUL, 0xa38d4697UL }; static const ulong32 Tks2[] = { 0x00000000UL, 0x0d0b0e09UL, 0x1a161c12UL, 0x171d121bUL, 0x342c3824UL, 0x3927362dUL, 0x2e3a2436UL, 0x23312a3fUL, 0x68587048UL, 0x65537e41UL, 0x724e6c5aUL, 0x7f456253UL, 0x5c74486cUL, 0x517f4665UL, 0x4662547eUL, 0x4b695a77UL, 0xd0b0e090UL, 0xddbbee99UL, 0xcaa6fc82UL, 0xc7adf28bUL, 0xe49cd8b4UL, 0xe997d6bdUL, 0xfe8ac4a6UL, 0xf381caafUL, 0xb8e890d8UL, 0xb5e39ed1UL, 0xa2fe8ccaUL, 0xaff582c3UL, 0x8cc4a8fcUL, 0x81cfa6f5UL, 0x96d2b4eeUL, 0x9bd9bae7UL, 0xbb7bdb3bUL, 0xb670d532UL, 0xa16dc729UL, 0xac66c920UL, 0x8f57e31fUL, 0x825ced16UL, 0x9541ff0dUL, 0x984af104UL, 0xd323ab73UL, 0xde28a57aUL, 0xc935b761UL, 0xc43eb968UL, 0xe70f9357UL, 0xea049d5eUL, 0xfd198f45UL, 0xf012814cUL, 0x6bcb3babUL, 0x66c035a2UL, 0x71dd27b9UL, 0x7cd629b0UL, 0x5fe7038fUL, 0x52ec0d86UL, 0x45f11f9dUL, 0x48fa1194UL, 0x03934be3UL, 0x0e9845eaUL, 0x198557f1UL, 0x148e59f8UL, 0x37bf73c7UL, 0x3ab47dceUL, 0x2da96fd5UL, 0x20a261dcUL, 0x6df6ad76UL, 0x60fda37fUL, 0x77e0b164UL, 0x7aebbf6dUL, 0x59da9552UL, 0x54d19b5bUL, 0x43cc8940UL, 0x4ec78749UL, 0x05aedd3eUL, 0x08a5d337UL, 0x1fb8c12cUL, 0x12b3cf25UL, 0x3182e51aUL, 0x3c89eb13UL, 0x2b94f908UL, 0x269ff701UL, 0xbd464de6UL, 0xb04d43efUL, 0xa75051f4UL, 0xaa5b5ffdUL, 0x896a75c2UL, 0x84617bcbUL, 0x937c69d0UL, 0x9e7767d9UL, 0xd51e3daeUL, 0xd81533a7UL, 0xcf0821bcUL, 0xc2032fb5UL, 0xe132058aUL, 0xec390b83UL, 0xfb241998UL, 0xf62f1791UL, 0xd68d764dUL, 0xdb867844UL, 0xcc9b6a5fUL, 0xc1906456UL, 0xe2a14e69UL, 0xefaa4060UL, 0xf8b7527bUL, 0xf5bc5c72UL, 0xbed50605UL, 0xb3de080cUL, 0xa4c31a17UL, 0xa9c8141eUL, 0x8af93e21UL, 0x87f23028UL, 0x90ef2233UL, 0x9de42c3aUL, 0x063d96ddUL, 0x0b3698d4UL, 0x1c2b8acfUL, 0x112084c6UL, 0x3211aef9UL, 0x3f1aa0f0UL, 0x2807b2ebUL, 0x250cbce2UL, 0x6e65e695UL, 0x636ee89cUL, 0x7473fa87UL, 0x7978f48eUL, 0x5a49deb1UL, 0x5742d0b8UL, 0x405fc2a3UL, 0x4d54ccaaUL, 0xdaf741ecUL, 0xd7fc4fe5UL, 0xc0e15dfeUL, 0xcdea53f7UL, 0xeedb79c8UL, 0xe3d077c1UL, 0xf4cd65daUL, 0xf9c66bd3UL, 0xb2af31a4UL, 0xbfa43fadUL, 0xa8b92db6UL, 0xa5b223bfUL, 0x86830980UL, 0x8b880789UL, 0x9c951592UL, 0x919e1b9bUL, 0x0a47a17cUL, 0x074caf75UL, 0x1051bd6eUL, 0x1d5ab367UL, 0x3e6b9958UL, 0x33609751UL, 0x247d854aUL, 0x29768b43UL, 0x621fd134UL, 0x6f14df3dUL, 0x7809cd26UL, 0x7502c32fUL, 0x5633e910UL, 0x5b38e719UL, 0x4c25f502UL, 0x412efb0bUL, 0x618c9ad7UL, 0x6c8794deUL, 0x7b9a86c5UL, 0x769188ccUL, 0x55a0a2f3UL, 0x58abacfaUL, 0x4fb6bee1UL, 0x42bdb0e8UL, 0x09d4ea9fUL, 0x04dfe496UL, 0x13c2f68dUL, 0x1ec9f884UL, 0x3df8d2bbUL, 0x30f3dcb2UL, 0x27eecea9UL, 0x2ae5c0a0UL, 0xb13c7a47UL, 0xbc37744eUL, 0xab2a6655UL, 0xa621685cUL, 0x85104263UL, 0x881b4c6aUL, 0x9f065e71UL, 0x920d5078UL, 0xd9640a0fUL, 0xd46f0406UL, 0xc372161dUL, 0xce791814UL, 0xed48322bUL, 0xe0433c22UL, 0xf75e2e39UL, 0xfa552030UL, 0xb701ec9aUL, 0xba0ae293UL, 0xad17f088UL, 0xa01cfe81UL, 0x832dd4beUL, 0x8e26dab7UL, 0x993bc8acUL, 0x9430c6a5UL, 0xdf599cd2UL, 0xd25292dbUL, 0xc54f80c0UL, 0xc8448ec9UL, 0xeb75a4f6UL, 0xe67eaaffUL, 0xf163b8e4UL, 0xfc68b6edUL, 0x67b10c0aUL, 0x6aba0203UL, 0x7da71018UL, 0x70ac1e11UL, 0x539d342eUL, 0x5e963a27UL, 0x498b283cUL, 0x44802635UL, 0x0fe97c42UL, 0x02e2724bUL, 0x15ff6050UL, 0x18f46e59UL, 0x3bc54466UL, 0x36ce4a6fUL, 0x21d35874UL, 0x2cd8567dUL, 0x0c7a37a1UL, 0x017139a8UL, 0x166c2bb3UL, 0x1b6725baUL, 0x38560f85UL, 0x355d018cUL, 0x22401397UL, 0x2f4b1d9eUL, 0x642247e9UL, 0x692949e0UL, 0x7e345bfbUL, 0x733f55f2UL, 0x500e7fcdUL, 0x5d0571c4UL, 0x4a1863dfUL, 0x47136dd6UL, 0xdccad731UL, 0xd1c1d938UL, 0xc6dccb23UL, 0xcbd7c52aUL, 0xe8e6ef15UL, 0xe5ede11cUL, 0xf2f0f307UL, 0xfffbfd0eUL, 0xb492a779UL, 0xb999a970UL, 0xae84bb6bUL, 0xa38fb562UL, 0x80be9f5dUL, 0x8db59154UL, 0x9aa8834fUL, 0x97a38d46UL }; static const ulong32 Tks3[] = { 0x00000000UL, 0x090d0b0eUL, 0x121a161cUL, 0x1b171d12UL, 0x24342c38UL, 0x2d392736UL, 0x362e3a24UL, 0x3f23312aUL, 0x48685870UL, 0x4165537eUL, 0x5a724e6cUL, 0x537f4562UL, 0x6c5c7448UL, 0x65517f46UL, 0x7e466254UL, 0x774b695aUL, 0x90d0b0e0UL, 0x99ddbbeeUL, 0x82caa6fcUL, 0x8bc7adf2UL, 0xb4e49cd8UL, 0xbde997d6UL, 0xa6fe8ac4UL, 0xaff381caUL, 0xd8b8e890UL, 0xd1b5e39eUL, 0xcaa2fe8cUL, 0xc3aff582UL, 0xfc8cc4a8UL, 0xf581cfa6UL, 0xee96d2b4UL, 0xe79bd9baUL, 0x3bbb7bdbUL, 0x32b670d5UL, 0x29a16dc7UL, 0x20ac66c9UL, 0x1f8f57e3UL, 0x16825cedUL, 0x0d9541ffUL, 0x04984af1UL, 0x73d323abUL, 0x7ade28a5UL, 0x61c935b7UL, 0x68c43eb9UL, 0x57e70f93UL, 0x5eea049dUL, 0x45fd198fUL, 0x4cf01281UL, 0xab6bcb3bUL, 0xa266c035UL, 0xb971dd27UL, 0xb07cd629UL, 0x8f5fe703UL, 0x8652ec0dUL, 0x9d45f11fUL, 0x9448fa11UL, 0xe303934bUL, 0xea0e9845UL, 0xf1198557UL, 0xf8148e59UL, 0xc737bf73UL, 0xce3ab47dUL, 0xd52da96fUL, 0xdc20a261UL, 0x766df6adUL, 0x7f60fda3UL, 0x6477e0b1UL, 0x6d7aebbfUL, 0x5259da95UL, 0x5b54d19bUL, 0x4043cc89UL, 0x494ec787UL, 0x3e05aeddUL, 0x3708a5d3UL, 0x2c1fb8c1UL, 0x2512b3cfUL, 0x1a3182e5UL, 0x133c89ebUL, 0x082b94f9UL, 0x01269ff7UL, 0xe6bd464dUL, 0xefb04d43UL, 0xf4a75051UL, 0xfdaa5b5fUL, 0xc2896a75UL, 0xcb84617bUL, 0xd0937c69UL, 0xd99e7767UL, 0xaed51e3dUL, 0xa7d81533UL, 0xbccf0821UL, 0xb5c2032fUL, 0x8ae13205UL, 0x83ec390bUL, 0x98fb2419UL, 0x91f62f17UL, 0x4dd68d76UL, 0x44db8678UL, 0x5fcc9b6aUL, 0x56c19064UL, 0x69e2a14eUL, 0x60efaa40UL, 0x7bf8b752UL, 0x72f5bc5cUL, 0x05bed506UL, 0x0cb3de08UL, 0x17a4c31aUL, 0x1ea9c814UL, 0x218af93eUL, 0x2887f230UL, 0x3390ef22UL, 0x3a9de42cUL, 0xdd063d96UL, 0xd40b3698UL, 0xcf1c2b8aUL, 0xc6112084UL, 0xf93211aeUL, 0xf03f1aa0UL, 0xeb2807b2UL, 0xe2250cbcUL, 0x956e65e6UL, 0x9c636ee8UL, 0x877473faUL, 0x8e7978f4UL, 0xb15a49deUL, 0xb85742d0UL, 0xa3405fc2UL, 0xaa4d54ccUL, 0xecdaf741UL, 0xe5d7fc4fUL, 0xfec0e15dUL, 0xf7cdea53UL, 0xc8eedb79UL, 0xc1e3d077UL, 0xdaf4cd65UL, 0xd3f9c66bUL, 0xa4b2af31UL, 0xadbfa43fUL, 0xb6a8b92dUL, 0xbfa5b223UL, 0x80868309UL, 0x898b8807UL, 0x929c9515UL, 0x9b919e1bUL, 0x7c0a47a1UL, 0x75074cafUL, 0x6e1051bdUL, 0x671d5ab3UL, 0x583e6b99UL, 0x51336097UL, 0x4a247d85UL, 0x4329768bUL, 0x34621fd1UL, 0x3d6f14dfUL, 0x267809cdUL, 0x2f7502c3UL, 0x105633e9UL, 0x195b38e7UL, 0x024c25f5UL, 0x0b412efbUL, 0xd7618c9aUL, 0xde6c8794UL, 0xc57b9a86UL, 0xcc769188UL, 0xf355a0a2UL, 0xfa58abacUL, 0xe14fb6beUL, 0xe842bdb0UL, 0x9f09d4eaUL, 0x9604dfe4UL, 0x8d13c2f6UL, 0x841ec9f8UL, 0xbb3df8d2UL, 0xb230f3dcUL, 0xa927eeceUL, 0xa02ae5c0UL, 0x47b13c7aUL, 0x4ebc3774UL, 0x55ab2a66UL, 0x5ca62168UL, 0x63851042UL, 0x6a881b4cUL, 0x719f065eUL, 0x78920d50UL, 0x0fd9640aUL, 0x06d46f04UL, 0x1dc37216UL, 0x14ce7918UL, 0x2bed4832UL, 0x22e0433cUL, 0x39f75e2eUL, 0x30fa5520UL, 0x9ab701ecUL, 0x93ba0ae2UL, 0x88ad17f0UL, 0x81a01cfeUL, 0xbe832dd4UL, 0xb78e26daUL, 0xac993bc8UL, 0xa59430c6UL, 0xd2df599cUL, 0xdbd25292UL, 0xc0c54f80UL, 0xc9c8448eUL, 0xf6eb75a4UL, 0xffe67eaaUL, 0xe4f163b8UL, 0xedfc68b6UL, 0x0a67b10cUL, 0x036aba02UL, 0x187da710UL, 0x1170ac1eUL, 0x2e539d34UL, 0x275e963aUL, 0x3c498b28UL, 0x35448026UL, 0x420fe97cUL, 0x4b02e272UL, 0x5015ff60UL, 0x5918f46eUL, 0x663bc544UL, 0x6f36ce4aUL, 0x7421d358UL, 0x7d2cd856UL, 0xa10c7a37UL, 0xa8017139UL, 0xb3166c2bUL, 0xba1b6725UL, 0x8538560fUL, 0x8c355d01UL, 0x97224013UL, 0x9e2f4b1dUL, 0xe9642247UL, 0xe0692949UL, 0xfb7e345bUL, 0xf2733f55UL, 0xcd500e7fUL, 0xc45d0571UL, 0xdf4a1863UL, 0xd647136dUL, 0x31dccad7UL, 0x38d1c1d9UL, 0x23c6dccbUL, 0x2acbd7c5UL, 0x15e8e6efUL, 0x1ce5ede1UL, 0x07f2f0f3UL, 0x0efffbfdUL, 0x79b492a7UL, 0x70b999a9UL, 0x6bae84bbUL, 0x62a38fb5UL, 0x5d80be9fUL, 0x548db591UL, 0x4f9aa883UL, 0x4697a38dUL }; #endif /* ENCRYPT_ONLY */ #endif /* SMALL CODE */ static const ulong32 rcon[] = { 0x01000000UL, 0x02000000UL, 0x04000000UL, 0x08000000UL, 0x10000000UL, 0x20000000UL, 0x40000000UL, 0x80000000UL, 0x1B000000UL, 0x36000000UL, /* for 128-bit blocks, Rijndael never uses more than 10 rcon values */ }; #endif /* __LTC_AES_TAB_C__ */ /* $Source$ */ /* $Revision$ */ /* $Date$ */ /* LibTomCrypt, modular cryptographic library -- Tom St Denis * * LibTomCrypt is a library that provides various cryptographic * algorithms in a highly modular and flexible manner. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tomstdenis@gmail.com, http://libtom.org */ /* AES implementation by Tom St Denis * * Derived from the Public Domain source code by --- * rijndael-alg-fst.c * * @version 3.0 (December 2000) * * Optimised ANSI C code for the Rijndael cipher (now AES) * * @author Vincent Rijmen * @author Antoon Bosselaers * @author Paulo Barreto --- */ /** @file aes.c Implementation of AES */ #ifdef LTC_RIJNDAEL #ifndef ENCRYPT_ONLY #define SETUP rijndael_setup #define ECB_ENC rijndael_ecb_encrypt #define ECB_DEC rijndael_ecb_decrypt #define ECB_DONE rijndael_done #define ECB_TEST rijndael_test #define ECB_KS rijndael_keysize const struct ltc_cipher_descriptor rijndael_desc = { "rijndael", 6, 16, 32, 16, 10, SETUP, ECB_ENC, ECB_DEC, ECB_TEST, ECB_DONE, ECB_KS, // NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }; const struct ltc_cipher_descriptor aes_desc = { "aes", 6, 16, 32, 16, 10, SETUP, ECB_ENC, ECB_DEC, ECB_TEST, ECB_DONE, ECB_KS, // NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }; #else #define SETUP rijndael_enc_setup #define ECB_ENC rijndael_enc_ecb_encrypt #define ECB_KS rijndael_enc_keysize #define ECB_DONE rijndael_enc_done const struct ltc_cipher_descriptor rijndael_enc_desc = { "rijndael", 6, 16, 32, 16, 10, SETUP, ECB_ENC, NULL, NULL, ECB_DONE, ECB_KS, // NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }; const struct ltc_cipher_descriptor aes_enc_desc = { "aes", 6, 16, 32, 16, 10, SETUP, ECB_ENC, NULL, NULL, ECB_DONE, ECB_KS, // NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }; #endif #define __LTC_AES_TAB_C__ static ulong32 setup_mix(ulong32 temp) { return (Te4_3[byte(temp, 2)]) ^ (Te4_2[byte(temp, 1)]) ^ (Te4_1[byte(temp, 0)]) ^ (Te4_0[byte(temp, 3)]); } #ifndef ENCRYPT_ONLY #ifdef LTC_SMALL_CODE static ulong32 setup_mix2(ulong32 temp) { return Td0(255 & Te4[byte(temp, 3)]) ^ Td1(255 & Te4[byte(temp, 2)]) ^ Td2(255 & Te4[byte(temp, 1)]) ^ Td3(255 & Te4[byte(temp, 0)]); } #endif #endif /** Initialize the AES (Rijndael) block cipher @param key The symmetric key you wish to pass @param keylen The key length in bytes @param num_rounds The number of rounds desired (0 for default) @param skey The key in as scheduled by this function. @return CRYPT_OK if successful */ int SETUP(const unsigned char *key, int keylen, int num_rounds, symmetric_key *skey) { int i; ulong32 temp, *rk; #ifndef ENCRYPT_ONLY ulong32 *rrk; #endif LTC_ARGCHK(key != NULL); LTC_ARGCHK(skey != NULL); if (keylen != 16 && keylen != 24 && keylen != 32) { return CRYPT_INVALID_KEYSIZE; } if (num_rounds != 0 && num_rounds != (10 + ((keylen/8)-2)*2)) { return CRYPT_INVALID_ROUNDS; } skey->rijndael.Nr = 10 + ((keylen/8)-2)*2; /* setup the forward key */ i = 0; rk = skey->rijndael.eK; LOAD32H(rk[0], key ); LOAD32H(rk[1], key + 4); LOAD32H(rk[2], key + 8); LOAD32H(rk[3], key + 12); if (keylen == 16) { for (;;) { temp = rk[3]; rk[4] = rk[0] ^ setup_mix(temp) ^ rcon[i]; rk[5] = rk[1] ^ rk[4]; rk[6] = rk[2] ^ rk[5]; rk[7] = rk[3] ^ rk[6]; if (++i == 10) { break; } rk += 4; } } else if (keylen == 24) { LOAD32H(rk[4], key + 16); LOAD32H(rk[5], key + 20); for (;;) { #ifdef _MSC_VER temp = skey->rijndael.eK[rk - skey->rijndael.eK + 5]; #else temp = rk[5]; #endif rk[ 6] = rk[ 0] ^ setup_mix(temp) ^ rcon[i]; rk[ 7] = rk[ 1] ^ rk[ 6]; rk[ 8] = rk[ 2] ^ rk[ 7]; rk[ 9] = rk[ 3] ^ rk[ 8]; if (++i == 8) { break; } rk[10] = rk[ 4] ^ rk[ 9]; rk[11] = rk[ 5] ^ rk[10]; rk += 6; } } else if (keylen == 32) { LOAD32H(rk[4], key + 16); LOAD32H(rk[5], key + 20); LOAD32H(rk[6], key + 24); LOAD32H(rk[7], key + 28); for (;;) { #ifdef _MSC_VER temp = skey->rijndael.eK[rk - skey->rijndael.eK + 7]; #else temp = rk[7]; #endif rk[ 8] = rk[ 0] ^ setup_mix(temp) ^ rcon[i]; rk[ 9] = rk[ 1] ^ rk[ 8]; rk[10] = rk[ 2] ^ rk[ 9]; rk[11] = rk[ 3] ^ rk[10]; if (++i == 7) { break; } temp = rk[11]; rk[12] = rk[ 4] ^ setup_mix(RORc(temp, 8)); rk[13] = rk[ 5] ^ rk[12]; rk[14] = rk[ 6] ^ rk[13]; rk[15] = rk[ 7] ^ rk[14]; rk += 8; } } else { /* this can't happen */ /* coverity[dead_error_line] */ return CRYPT_ERROR; } #ifndef ENCRYPT_ONLY /* setup the inverse key now */ rk = skey->rijndael.dK; rrk = skey->rijndael.eK + (28 + keylen) - 4; /* apply the inverse MixColumn transform to all round keys but the first and the last: */ /* copy first */ *rk++ = *rrk++; *rk++ = *rrk++; *rk++ = *rrk++; *rk = *rrk; rk -= 3; rrk -= 3; for (i = 1; i < skey->rijndael.Nr; i++) { rrk -= 4; rk += 4; #ifdef LTC_SMALL_CODE temp = rrk[0]; rk[0] = setup_mix2(temp); temp = rrk[1]; rk[1] = setup_mix2(temp); temp = rrk[2]; rk[2] = setup_mix2(temp); temp = rrk[3]; rk[3] = setup_mix2(temp); #else temp = rrk[0]; rk[0] = Tks0[byte(temp, 3)] ^ Tks1[byte(temp, 2)] ^ Tks2[byte(temp, 1)] ^ Tks3[byte(temp, 0)]; temp = rrk[1]; rk[1] = Tks0[byte(temp, 3)] ^ Tks1[byte(temp, 2)] ^ Tks2[byte(temp, 1)] ^ Tks3[byte(temp, 0)]; temp = rrk[2]; rk[2] = Tks0[byte(temp, 3)] ^ Tks1[byte(temp, 2)] ^ Tks2[byte(temp, 1)] ^ Tks3[byte(temp, 0)]; temp = rrk[3]; rk[3] = Tks0[byte(temp, 3)] ^ Tks1[byte(temp, 2)] ^ Tks2[byte(temp, 1)] ^ Tks3[byte(temp, 0)]; #endif } /* copy last */ rrk -= 4; rk += 4; *rk++ = *rrk++; *rk++ = *rrk++; *rk++ = *rrk++; *rk = *rrk; #endif /* ENCRYPT_ONLY */ return CRYPT_OK; } /** Encrypts a block of text with AES @param pt The input plaintext (16 bytes) @param ct The output ciphertext (16 bytes) @param skey The key as scheduled @return CRYPT_OK if successful */ #ifdef LTC_CLEAN_STACK static int _rijndael_ecb_encrypt(const unsigned char *pt, unsigned char *ct, symmetric_key *skey) #else int ECB_ENC(const unsigned char *pt, unsigned char *ct, symmetric_key *skey) #endif { ulong32 s0, s1, s2, s3, t0, t1, t2, t3, *rk; int Nr, r; LTC_ARGCHK(pt != NULL); LTC_ARGCHK(ct != NULL); LTC_ARGCHK(skey != NULL); Nr = skey->rijndael.Nr; rk = skey->rijndael.eK; /* * map byte array block to cipher state * and add initial round key: */ LOAD32H(s0, pt ); s0 ^= rk[0]; LOAD32H(s1, pt + 4); s1 ^= rk[1]; LOAD32H(s2, pt + 8); s2 ^= rk[2]; LOAD32H(s3, pt + 12); s3 ^= rk[3]; #ifdef LTC_SMALL_CODE for (r = 0; ; r++) { rk += 4; t0 = Te0(byte(s0, 3)) ^ Te1(byte(s1, 2)) ^ Te2(byte(s2, 1)) ^ Te3(byte(s3, 0)) ^ rk[0]; t1 = Te0(byte(s1, 3)) ^ Te1(byte(s2, 2)) ^ Te2(byte(s3, 1)) ^ Te3(byte(s0, 0)) ^ rk[1]; t2 = Te0(byte(s2, 3)) ^ Te1(byte(s3, 2)) ^ Te2(byte(s0, 1)) ^ Te3(byte(s1, 0)) ^ rk[2]; t3 = Te0(byte(s3, 3)) ^ Te1(byte(s0, 2)) ^ Te2(byte(s1, 1)) ^ Te3(byte(s2, 0)) ^ rk[3]; if (r == Nr-2) { break; } s0 = t0; s1 = t1; s2 = t2; s3 = t3; } rk += 4; #else /* * Nr - 1 full rounds: */ r = Nr >> 1; for (;;) { t0 = Te0(byte(s0, 3)) ^ Te1(byte(s1, 2)) ^ Te2(byte(s2, 1)) ^ Te3(byte(s3, 0)) ^ rk[4]; t1 = Te0(byte(s1, 3)) ^ Te1(byte(s2, 2)) ^ Te2(byte(s3, 1)) ^ Te3(byte(s0, 0)) ^ rk[5]; t2 = Te0(byte(s2, 3)) ^ Te1(byte(s3, 2)) ^ Te2(byte(s0, 1)) ^ Te3(byte(s1, 0)) ^ rk[6]; t3 = Te0(byte(s3, 3)) ^ Te1(byte(s0, 2)) ^ Te2(byte(s1, 1)) ^ Te3(byte(s2, 0)) ^ rk[7]; rk += 8; if (--r == 0) { break; } s0 = Te0(byte(t0, 3)) ^ Te1(byte(t1, 2)) ^ Te2(byte(t2, 1)) ^ Te3(byte(t3, 0)) ^ rk[0]; s1 = Te0(byte(t1, 3)) ^ Te1(byte(t2, 2)) ^ Te2(byte(t3, 1)) ^ Te3(byte(t0, 0)) ^ rk[1]; s2 = Te0(byte(t2, 3)) ^ Te1(byte(t3, 2)) ^ Te2(byte(t0, 1)) ^ Te3(byte(t1, 0)) ^ rk[2]; s3 = Te0(byte(t3, 3)) ^ Te1(byte(t0, 2)) ^ Te2(byte(t1, 1)) ^ Te3(byte(t2, 0)) ^ rk[3]; } #endif /* * apply last round and * map cipher state to byte array block: */ s0 = (Te4_3[byte(t0, 3)]) ^ (Te4_2[byte(t1, 2)]) ^ (Te4_1[byte(t2, 1)]) ^ (Te4_0[byte(t3, 0)]) ^ rk[0]; STORE32H(s0, ct); s1 = (Te4_3[byte(t1, 3)]) ^ (Te4_2[byte(t2, 2)]) ^ (Te4_1[byte(t3, 1)]) ^ (Te4_0[byte(t0, 0)]) ^ rk[1]; STORE32H(s1, ct+4); s2 = (Te4_3[byte(t2, 3)]) ^ (Te4_2[byte(t3, 2)]) ^ (Te4_1[byte(t0, 1)]) ^ (Te4_0[byte(t1, 0)]) ^ rk[2]; STORE32H(s2, ct+8); s3 = (Te4_3[byte(t3, 3)]) ^ (Te4_2[byte(t0, 2)]) ^ (Te4_1[byte(t1, 1)]) ^ (Te4_0[byte(t2, 0)]) ^ rk[3]; STORE32H(s3, ct+12); return CRYPT_OK; } #ifdef LTC_CLEAN_STACK int ECB_ENC(const unsigned char *pt, unsigned char *ct, symmetric_key *skey) { int err = _rijndael_ecb_encrypt(pt, ct, skey); burn_stack(sizeof(unsigned long)*8 + sizeof(unsigned long*) + sizeof(int)*2); return err; } #endif #ifndef ENCRYPT_ONLY /** Decrypts a block of text with AES @param ct The input ciphertext (16 bytes) @param pt The output plaintext (16 bytes) @param skey The key as scheduled @return CRYPT_OK if successful */ #ifdef LTC_CLEAN_STACK static int _rijndael_ecb_decrypt(const unsigned char *ct, unsigned char *pt, symmetric_key *skey) #else int ECB_DEC(const unsigned char *ct, unsigned char *pt, symmetric_key *skey) #endif { ulong32 s0, s1, s2, s3, t0, t1, t2, t3, *rk; int Nr, r; LTC_ARGCHK(pt != NULL); LTC_ARGCHK(ct != NULL); LTC_ARGCHK(skey != NULL); Nr = skey->rijndael.Nr; rk = skey->rijndael.dK; /* * map byte array block to cipher state * and add initial round key: */ LOAD32H(s0, ct ); s0 ^= rk[0]; LOAD32H(s1, ct + 4); s1 ^= rk[1]; LOAD32H(s2, ct + 8); s2 ^= rk[2]; LOAD32H(s3, ct + 12); s3 ^= rk[3]; #ifdef LTC_SMALL_CODE for (r = 0; ; r++) { rk += 4; t0 = Td0(byte(s0, 3)) ^ Td1(byte(s3, 2)) ^ Td2(byte(s2, 1)) ^ Td3(byte(s1, 0)) ^ rk[0]; t1 = Td0(byte(s1, 3)) ^ Td1(byte(s0, 2)) ^ Td2(byte(s3, 1)) ^ Td3(byte(s2, 0)) ^ rk[1]; t2 = Td0(byte(s2, 3)) ^ Td1(byte(s1, 2)) ^ Td2(byte(s0, 1)) ^ Td3(byte(s3, 0)) ^ rk[2]; t3 = Td0(byte(s3, 3)) ^ Td1(byte(s2, 2)) ^ Td2(byte(s1, 1)) ^ Td3(byte(s0, 0)) ^ rk[3]; if (r == Nr-2) { break; } s0 = t0; s1 = t1; s2 = t2; s3 = t3; } rk += 4; #else /* * Nr - 1 full rounds: */ r = Nr >> 1; for (;;) { t0 = Td0(byte(s0, 3)) ^ Td1(byte(s3, 2)) ^ Td2(byte(s2, 1)) ^ Td3(byte(s1, 0)) ^ rk[4]; t1 = Td0(byte(s1, 3)) ^ Td1(byte(s0, 2)) ^ Td2(byte(s3, 1)) ^ Td3(byte(s2, 0)) ^ rk[5]; t2 = Td0(byte(s2, 3)) ^ Td1(byte(s1, 2)) ^ Td2(byte(s0, 1)) ^ Td3(byte(s3, 0)) ^ rk[6]; t3 = Td0(byte(s3, 3)) ^ Td1(byte(s2, 2)) ^ Td2(byte(s1, 1)) ^ Td3(byte(s0, 0)) ^ rk[7]; rk += 8; if (--r == 0) { break; } s0 = Td0(byte(t0, 3)) ^ Td1(byte(t3, 2)) ^ Td2(byte(t2, 1)) ^ Td3(byte(t1, 0)) ^ rk[0]; s1 = Td0(byte(t1, 3)) ^ Td1(byte(t0, 2)) ^ Td2(byte(t3, 1)) ^ Td3(byte(t2, 0)) ^ rk[1]; s2 = Td0(byte(t2, 3)) ^ Td1(byte(t1, 2)) ^ Td2(byte(t0, 1)) ^ Td3(byte(t3, 0)) ^ rk[2]; s3 = Td0(byte(t3, 3)) ^ Td1(byte(t2, 2)) ^ Td2(byte(t1, 1)) ^ Td3(byte(t0, 0)) ^ rk[3]; } #endif /* * apply last round and * map cipher state to byte array block: */ s0 = (Td4[byte(t0, 3)] & 0xff000000) ^ (Td4[byte(t3, 2)] & 0x00ff0000) ^ (Td4[byte(t2, 1)] & 0x0000ff00) ^ (Td4[byte(t1, 0)] & 0x000000ff) ^ rk[0]; STORE32H(s0, pt); s1 = (Td4[byte(t1, 3)] & 0xff000000) ^ (Td4[byte(t0, 2)] & 0x00ff0000) ^ (Td4[byte(t3, 1)] & 0x0000ff00) ^ (Td4[byte(t2, 0)] & 0x000000ff) ^ rk[1]; STORE32H(s1, pt+4); s2 = (Td4[byte(t2, 3)] & 0xff000000) ^ (Td4[byte(t1, 2)] & 0x00ff0000) ^ (Td4[byte(t0, 1)] & 0x0000ff00) ^ (Td4[byte(t3, 0)] & 0x000000ff) ^ rk[2]; STORE32H(s2, pt+8); s3 = (Td4[byte(t3, 3)] & 0xff000000) ^ (Td4[byte(t2, 2)] & 0x00ff0000) ^ (Td4[byte(t1, 1)] & 0x0000ff00) ^ (Td4[byte(t0, 0)] & 0x000000ff) ^ rk[3]; STORE32H(s3, pt+12); return CRYPT_OK; } #ifdef LTC_CLEAN_STACK int ECB_DEC(const unsigned char *ct, unsigned char *pt, symmetric_key *skey) { int err = _rijndael_ecb_decrypt(ct, pt, skey); burn_stack(sizeof(unsigned long)*8 + sizeof(unsigned long*) + sizeof(int)*2); return err; } #endif /** Performs a self-test of the AES block cipher @return CRYPT_OK if functional, CRYPT_NOP if self-test has been disabled */ int ECB_TEST(void) { #ifndef LTC_TEST return CRYPT_NOP; #else int err; static const struct { int keylen; unsigned char key[32], pt[16], ct[16]; } tests[] = { { 16, { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f }, { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff }, { 0x69, 0xc4, 0xe0, 0xd8, 0x6a, 0x7b, 0x04, 0x30, 0xd8, 0xcd, 0xb7, 0x80, 0x70, 0xb4, 0xc5, 0x5a } }, { 24, { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17 }, { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff }, { 0xdd, 0xa9, 0x7c, 0xa4, 0x86, 0x4c, 0xdf, 0xe0, 0x6e, 0xaf, 0x70, 0xa0, 0xec, 0x0d, 0x71, 0x91 } }, { 32, { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f }, { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff }, { 0x8e, 0xa2, 0xb7, 0xca, 0x51, 0x67, 0x45, 0xbf, 0xea, 0xfc, 0x49, 0x90, 0x4b, 0x49, 0x60, 0x89 } } }; symmetric_key key; unsigned char tmp[2][16]; int i, y; for (i = 0; i < (int)(sizeof(tests)/sizeof(tests[0])); i++) { zeromem(&key, sizeof(key)); if ((err = rijndael_setup(tests[i].key, tests[i].keylen, 0, &key)) != CRYPT_OK) { return err; } rijndael_ecb_encrypt(tests[i].pt, tmp[0], &key); rijndael_ecb_decrypt(tmp[0], tmp[1], &key); if (XMEMCMP(tmp[0], tests[i].ct, 16) || XMEMCMP(tmp[1], tests[i].pt, 16)) { #if 0 printf("\n\nTest %d failed\n", i); if (XMEMCMP(tmp[0], tests[i].ct, 16)) { printf("CT: "); for (i = 0; i < 16; i++) { printf("%02x ", tmp[0][i]); } printf("\n"); } else { printf("PT: "); for (i = 0; i < 16; i++) { printf("%02x ", tmp[1][i]); } printf("\n"); } #endif return CRYPT_FAIL_TESTVECTOR; } /* now see if we can encrypt all zero bytes 1000 times, decrypt and come back where we started */ for (y = 0; y < 16; y++) tmp[0][y] = 0; for (y = 0; y < 1000; y++) rijndael_ecb_encrypt(tmp[0], tmp[0], &key); for (y = 0; y < 1000; y++) rijndael_ecb_decrypt(tmp[0], tmp[0], &key); for (y = 0; y < 16; y++) if (tmp[0][y] != 0) return CRYPT_FAIL_TESTVECTOR; } return CRYPT_OK; #endif } #endif /* ENCRYPT_ONLY */ /** Terminate the context @param skey The scheduled key */ void ECB_DONE(symmetric_key *skey) { //LTC_UNUSED_PARAM(skey); } /** Gets suitable key size @param keysize [in/out] The length of the recommended key (in bytes). This function will store the suitable size back in this variable. @return CRYPT_OK if the input key size is acceptable. */ int ECB_KS(int *keysize) { LTC_ARGCHK(keysize != NULL); if (*keysize < 16) return CRYPT_INVALID_KEYSIZE; if (*keysize < 24) { *keysize = 16; return CRYPT_OK; } else if (*keysize < 32) { *keysize = 24; return CRYPT_OK; } else { *keysize = 32; return CRYPT_OK; } } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ /* LibTomCrypt, modular cryptographic library -- Tom St Denis * * LibTomCrypt is a library that provides various cryptographic * algorithms in a highly modular and flexible manner. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tomstdenis@gmail.com, http://libtom.org */ /** @file cbc_decrypt.c CBC implementation, encrypt block, Tom St Denis */ #ifdef LTC_CBC_MODE /** CBC decrypt @param ct Ciphertext @param pt [out] Plaintext @param len The number of bytes to process (must be multiple of block length) @param cbc CBC state @return CRYPT_OK if successful */ int cbc_decrypt(const unsigned char *ct, unsigned char *pt, unsigned long len, symmetric_CBC *cbc) { int x, err; unsigned char tmp[16]; #ifdef LTC_FAST LTC_FAST_TYPE tmpy; #else unsigned char tmpy; #endif LTC_ARGCHK(pt != NULL); LTC_ARGCHK(ct != NULL); LTC_ARGCHK(cbc != NULL); if ((err = cipher_is_valid(cbc->cipher)) != CRYPT_OK) { return err; } /* is blocklen valid? */ if (cbc->blocklen < 1 || cbc->blocklen > (int)sizeof(cbc->IV)) { return CRYPT_INVALID_ARG; } if (len % cbc->blocklen) { return CRYPT_INVALID_ARG; } #ifdef LTC_FAST if (cbc->blocklen % sizeof(LTC_FAST_TYPE)) { return CRYPT_INVALID_ARG; } #endif if (cipher_descriptor[cbc->cipher].accel_cbc_decrypt != NULL) { return cipher_descriptor[cbc->cipher].accel_cbc_decrypt(ct, pt, len / cbc->blocklen, cbc->IV, &cbc->key); } else { while (len) { /* decrypt */ if ((err = cipher_descriptor[cbc->cipher].ecb_decrypt(ct, tmp, &cbc->key)) != CRYPT_OK) { return err; } /* xor IV against plaintext */ #if defined(LTC_FAST) for (x = 0; x < cbc->blocklen; x += sizeof(LTC_FAST_TYPE)) { tmpy = *((LTC_FAST_TYPE*)((unsigned char *)cbc->IV + x)) ^ *((LTC_FAST_TYPE*)((unsigned char *)tmp + x)); *((LTC_FAST_TYPE*)((unsigned char *)cbc->IV + x)) = *((LTC_FAST_TYPE*)((unsigned char *)ct + x)); *((LTC_FAST_TYPE*)((unsigned char *)pt + x)) = tmpy; } #else for (x = 0; x < cbc->blocklen; x++) { tmpy = tmp[x] ^ cbc->IV[x]; cbc->IV[x] = ct[x]; pt[x] = tmpy; } #endif ct += cbc->blocklen; pt += cbc->blocklen; len -= cbc->blocklen; } } return CRYPT_OK; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ /* LibTomCrypt, modular cryptographic library -- Tom St Denis * * LibTomCrypt is a library that provides various cryptographic * algorithms in a highly modular and flexible manner. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tomstdenis@gmail.com, http://libtom.org */ /** @file cbc_done.c CBC implementation, finish chain, Tom St Denis */ #ifdef LTC_CBC_MODE /** Terminate the chain @param cbc The CBC chain to terminate @return CRYPT_OK on success */ int cbc_done(symmetric_CBC *cbc) { int err; LTC_ARGCHK(cbc != NULL); if ((err = cipher_is_valid(cbc->cipher)) != CRYPT_OK) { return err; } cipher_descriptor[cbc->cipher].done(&cbc->key); return CRYPT_OK; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ /* LibTomCrypt, modular cryptographic library -- Tom St Denis * * LibTomCrypt is a library that provides various cryptographic * algorithms in a highly modular and flexible manner. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tomstdenis@gmail.com, http://libtom.org */ /** @file cbc_encrypt.c CBC implementation, encrypt block, Tom St Denis */ #ifdef LTC_CBC_MODE /** CBC encrypt @param pt Plaintext @param ct [out] Ciphertext @param len The number of bytes to process (must be multiple of block length) @param cbc CBC state @return CRYPT_OK if successful */ int cbc_encrypt(const unsigned char *pt, unsigned char *ct, unsigned long len, symmetric_CBC *cbc) { int x, err; LTC_ARGCHK(pt != NULL); LTC_ARGCHK(ct != NULL); LTC_ARGCHK(cbc != NULL); if ((err = cipher_is_valid(cbc->cipher)) != CRYPT_OK) { return err; } /* is blocklen valid? */ if (cbc->blocklen < 1 || cbc->blocklen > (int)sizeof(cbc->IV)) { return CRYPT_INVALID_ARG; } if (len % cbc->blocklen) { return CRYPT_INVALID_ARG; } #ifdef LTC_FAST if (cbc->blocklen % sizeof(LTC_FAST_TYPE)) { return CRYPT_INVALID_ARG; } #endif if (cipher_descriptor[cbc->cipher].accel_cbc_encrypt != NULL) { return cipher_descriptor[cbc->cipher].accel_cbc_encrypt(pt, ct, len / cbc->blocklen, cbc->IV, &cbc->key); } else { while (len) { /* xor IV against plaintext */ #if defined(LTC_FAST) for (x = 0; x < cbc->blocklen; x += sizeof(LTC_FAST_TYPE)) { *((LTC_FAST_TYPE*)((unsigned char *)cbc->IV + x)) ^= *((LTC_FAST_TYPE*)((unsigned char *)pt + x)); } #else for (x = 0; x < cbc->blocklen; x++) { cbc->IV[x] ^= pt[x]; } #endif /* encrypt */ if ((err = cipher_descriptor[cbc->cipher].ecb_encrypt(cbc->IV, ct, &cbc->key)) != CRYPT_OK) { return err; } /* store IV [ciphertext] for a future block */ #if defined(LTC_FAST) for (x = 0; x < cbc->blocklen; x += sizeof(LTC_FAST_TYPE)) { *((LTC_FAST_TYPE*)((unsigned char *)cbc->IV + x)) = *((LTC_FAST_TYPE*)((unsigned char *)ct + x)); } #else for (x = 0; x < cbc->blocklen; x++) { cbc->IV[x] = ct[x]; } #endif ct += cbc->blocklen; pt += cbc->blocklen; len -= cbc->blocklen; } } return CRYPT_OK; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ /* LibTomCrypt, modular cryptographic library -- Tom St Denis * * LibTomCrypt is a library that provides various cryptographic * algorithms in a highly modular and flexible manner. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tomstdenis@gmail.com, http://libtom.org */ /** @file cbc_getiv.c CBC implementation, get IV, Tom St Denis */ #ifdef LTC_CBC_MODE /** Get the current initial vector @param IV [out] The destination of the initial vector @param len [in/out] The max size and resulting size of the initial vector @param cbc The CBC state @return CRYPT_OK if successful */ int cbc_getiv(unsigned char *IV, unsigned long *len, symmetric_CBC *cbc) { LTC_ARGCHK(IV != NULL); LTC_ARGCHK(len != NULL); LTC_ARGCHK(cbc != NULL); if ((unsigned long)cbc->blocklen > *len) { *len = cbc->blocklen; return CRYPT_BUFFER_OVERFLOW; } XMEMCPY(IV, cbc->IV, cbc->blocklen); *len = cbc->blocklen; return CRYPT_OK; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ /* LibTomCrypt, modular cryptographic library -- Tom St Denis * * LibTomCrypt is a library that provides various cryptographic * algorithms in a highly modular and flexible manner. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tomstdenis@gmail.com, http://libtom.org */ /** @file cbc_setiv.c CBC implementation, set IV, Tom St Denis */ #ifdef LTC_CBC_MODE /** Set an initial vector @param IV The initial vector @param len The length of the vector (in octets) @param cbc The CBC state @return CRYPT_OK if successful */ int cbc_setiv(const unsigned char *IV, unsigned long len, symmetric_CBC *cbc) { LTC_ARGCHK(IV != NULL); LTC_ARGCHK(cbc != NULL); if (len != (unsigned long)cbc->blocklen) { return CRYPT_INVALID_ARG; } XMEMCPY(cbc->IV, IV, len); return CRYPT_OK; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ /* LibTomCrypt, modular cryptographic library -- Tom St Denis * * LibTomCrypt is a library that provides various cryptographic * algorithms in a highly modular and flexible manner. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tomstdenis@gmail.com, http://libtom.org */ /** @file cbc_start.c CBC implementation, start chain, Tom St Denis */ #ifdef LTC_CBC_MODE /** Initialize a CBC context @param cipher The index of the cipher desired @param IV The initial vector @param key The secret key @param keylen The length of the secret key (octets) @param num_rounds Number of rounds in the cipher desired (0 for default) @param cbc The CBC state to initialize @return CRYPT_OK if successful */ int cbc_start(int cipher, const unsigned char *IV, const unsigned char *key, int keylen, int num_rounds, symmetric_CBC *cbc) { int x, err; LTC_ARGCHK(IV != NULL); LTC_ARGCHK(key != NULL); LTC_ARGCHK(cbc != NULL); /* bad param? */ if ((err = cipher_is_valid(cipher)) != CRYPT_OK) { return err; } /* setup cipher */ if ((err = cipher_descriptor[cipher].setup(key, keylen, num_rounds, &cbc->key)) != CRYPT_OK) { return err; } /* copy IV */ cbc->blocklen = cipher_descriptor[cipher].block_length; cbc->cipher = cipher; for (x = 0; x < cbc->blocklen; x++) { cbc->IV[x] = IV[x]; } return CRYPT_OK; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ /* LibTomCrypt, modular cryptographic library -- Tom St Denis * * LibTomCrypt is a library that provides various cryptographic * algorithms in a highly modular and flexible manner. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tomstdenis@gmail.com, http://libtom.org */ /** @file gcm_add_iv.c GCM implementation, add IV data to the state, by Tom St Denis */ #ifdef LTC_GCM_MODE /** Add IV data to the GCM state @param gcm The GCM state @param IV The initial value data to add @param IVlen The length of the IV @return CRYPT_OK on success */ int gcm_add_iv(gcm_state *gcm, const unsigned char *IV, unsigned long IVlen) { unsigned long x, y; int err; LTC_ARGCHK(gcm != NULL); if (IVlen > 0) { LTC_ARGCHK(IV != NULL); } /* must be in IV mode */ if (gcm->mode != LTC_GCM_MODE_IV) { return CRYPT_INVALID_ARG; } if (gcm->buflen >= 16 || gcm->buflen < 0) { return CRYPT_INVALID_ARG; } if ((err = cipher_is_valid(gcm->cipher)) != CRYPT_OK) { return err; } /* trip the ivmode flag */ if (IVlen + gcm->buflen > 12) { gcm->ivmode |= 1; } x = 0; #ifdef LTC_FAST if (gcm->buflen == 0) { for (x = 0; x < (IVlen & ~15); x += 16) { for (y = 0; y < 16; y += sizeof(LTC_FAST_TYPE)) { *((LTC_FAST_TYPE*)(&gcm->X[y])) ^= *((LTC_FAST_TYPE*)(&IV[x + y])); } gcm_mult_h(gcm, gcm->X); gcm->totlen += 128; } IV += x; } #endif /* start adding IV data to the state */ for (; x < IVlen; x++) { gcm->buf[gcm->buflen++] = *IV++; if (gcm->buflen == 16) { /* GF mult it */ for (y = 0; y < 16; y++) { gcm->X[y] ^= gcm->buf[y]; } gcm_mult_h(gcm, gcm->X); gcm->buflen = 0; gcm->totlen += 128; } } return CRYPT_OK; } #endif /* $Source: /cvs/libtom/libtomcrypt/src/encauth/gcm/gcm_add_iv.c,v $ */ /* $Revision: 1.9 $ */ /* $Date: 2007/05/12 14:32:35 $ */ /* LibTomCrypt, modular cryptographic library -- Tom St Denis * * LibTomCrypt is a library that provides various cryptographic * algorithms in a highly modular and flexible manner. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tomstdenis@gmail.com, http://libtom.org */ /** @file gcm_done.c GCM implementation, Terminate the stream, by Tom St Denis */ #ifdef LTC_GCM_MODE /** Terminate a GCM stream @param gcm The GCM state @param tag [out] The destination for the MAC tag @param taglen [in/out] The length of the MAC tag @return CRYPT_OK on success */ int gcm_done(gcm_state *gcm, unsigned char *tag, unsigned long *taglen) { unsigned long x; int err; LTC_ARGCHK(gcm != NULL); LTC_ARGCHK(tag != NULL); LTC_ARGCHK(taglen != NULL); if (gcm->buflen > 16 || gcm->buflen < 0) { return CRYPT_INVALID_ARG; } if ((err = cipher_is_valid(gcm->cipher)) != CRYPT_OK) { return err; } if (gcm->mode != LTC_GCM_MODE_TEXT) { return CRYPT_INVALID_ARG; } /* handle remaining ciphertext */ if (gcm->buflen) { gcm->pttotlen += gcm->buflen * CONST64(8); gcm_mult_h(gcm, gcm->X); } /* length */ STORE64H(gcm->totlen, gcm->buf); STORE64H(gcm->pttotlen, gcm->buf+8); for (x = 0; x < 16; x++) { gcm->X[x] ^= gcm->buf[x]; } gcm_mult_h(gcm, gcm->X); /* encrypt original counter */ if ((err = cipher_descriptor[gcm->cipher].ecb_encrypt(gcm->Y_0, gcm->buf, &gcm->K)) != CRYPT_OK) { return err; } for (x = 0; x < 16 && x < *taglen; x++) { tag[x] = gcm->buf[x] ^ gcm->X[x]; } *taglen = x; cipher_descriptor[gcm->cipher].done(&gcm->K); return CRYPT_OK; } #endif /* $Source: /cvs/libtom/libtomcrypt/src/encauth/gcm/gcm_done.c,v $ */ /* $Revision: 1.11 $ */ /* $Date: 2007/05/12 14:32:35 $ */ /* LibTomCrypt, modular cryptographic library -- Tom St Denis * * LibTomCrypt is a library that provides various cryptographic * algorithms in a highly modular and flexible manner. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tomstdenis@gmail.com, http://libtom.org */ /** @file gcm_init.c GCM implementation, initialize state, by Tom St Denis */ #ifdef LTC_GCM_MODE /** Initialize a GCM state @param gcm The GCM state to initialize @param cipher The index of the cipher to use @param key The secret key @param keylen The length of the secret key @return CRYPT_OK on success */ int gcm_init(gcm_state *gcm, int cipher, const unsigned char *key, int keylen) { int err; unsigned char B[16]; #ifdef LTC_GCM_TABLES int x, y, z, t; #endif LTC_ARGCHK(gcm != NULL); LTC_ARGCHK(key != NULL); #ifdef LTC_FAST if (16 % sizeof(LTC_FAST_TYPE)) { return CRYPT_INVALID_ARG; } #endif /* is cipher valid? */ if ((err = cipher_is_valid(cipher)) != CRYPT_OK) { return err; } if (cipher_descriptor[cipher].block_length != 16) { return CRYPT_INVALID_CIPHER; } /* schedule key */ if ((err = cipher_descriptor[cipher].setup(key, keylen, 0, &gcm->K)) != CRYPT_OK) { return err; } /* H = E(0) */ zeromem(B, 16); if ((err = cipher_descriptor[cipher].ecb_encrypt(B, gcm->H, &gcm->K)) != CRYPT_OK) { return err; } /* setup state */ zeromem(gcm->buf, sizeof(gcm->buf)); zeromem(gcm->X, sizeof(gcm->X)); gcm->cipher = cipher; gcm->mode = LTC_GCM_MODE_IV; gcm->ivmode = 0; gcm->buflen = 0; gcm->totlen = 0; gcm->pttotlen = 0; #ifdef LTC_GCM_TABLES /* setup tables */ /* generate the first table as it has no shifting (from which we make the other tables) */ zeromem(B, 16); for (y = 0; y < 256; y++) { B[0] = y; gcm_gf_mult(gcm->H, B, &gcm->PC[0][y][0]); } /* now generate the rest of the tables based the previous table */ for (x = 1; x < 16; x++) { for (y = 0; y < 256; y++) { /* now shift it right by 8 bits */ t = gcm->PC[x-1][y][15]; for (z = 15; z > 0; z--) { gcm->PC[x][y][z] = gcm->PC[x-1][y][z-1]; } gcm->PC[x][y][0] = gcm_shift_table[t<<1]; gcm->PC[x][y][1] ^= gcm_shift_table[(t<<1)+1]; } } #endif return CRYPT_OK; } #endif /* $Source: /cvs/libtom/libtomcrypt/src/encauth/gcm/gcm_init.c,v $ */ /* $Revision: 1.20 $ */ /* $Date: 2007/05/12 14:32:35 $ */ /* LibTomCrypt, modular cryptographic library -- Tom St Denis * * LibTomCrypt is a library that provides various cryptographic * algorithms in a highly modular and flexible manner. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tomstdenis@gmail.com, http://libtom.org */ /** @file gcm_process.c GCM implementation, process message data, by Tom St Denis */ #ifdef LTC_GCM_MODE /** Process plaintext/ciphertext through GCM @param gcm The GCM state @param pt The plaintext @param ptlen The plaintext length (ciphertext length is the same) @param ct The ciphertext @param direction Encrypt or Decrypt mode (GCM_ENCRYPT or GCM_DECRYPT) @return CRYPT_OK on success */ int gcm_process(gcm_state *gcm, unsigned char *pt, unsigned long ptlen, unsigned char *ct, int direction) { unsigned long x; int y, err; unsigned char b; LTC_ARGCHK(gcm != NULL); if (ptlen > 0) { LTC_ARGCHK(pt != NULL); LTC_ARGCHK(ct != NULL); } if (gcm->buflen > 16 || gcm->buflen < 0) { return CRYPT_INVALID_ARG; } if ((err = cipher_is_valid(gcm->cipher)) != CRYPT_OK) { return err; } /* in AAD mode? */ if (gcm->mode == LTC_GCM_MODE_AAD) { /* let's process the AAD */ if (gcm->buflen) { gcm->totlen += gcm->buflen * CONST64(8); gcm_mult_h(gcm, gcm->X); } /* increment counter */ for (y = 15; y >= 12; y--) { if (++gcm->Y[y] & 255) { break; } } /* encrypt the counter */ if ((err = cipher_descriptor[gcm->cipher].ecb_encrypt(gcm->Y, gcm->buf, &gcm->K)) != CRYPT_OK) { return err; } gcm->buflen = 0; gcm->mode = LTC_GCM_MODE_TEXT; } if (gcm->mode != LTC_GCM_MODE_TEXT) { return CRYPT_INVALID_ARG; } x = 0; #ifdef LTC_FAST if (gcm->buflen == 0) { if (direction == GCM_ENCRYPT) { for (x = 0; x < (ptlen & ~15); x += 16) { /* ctr encrypt */ for (y = 0; y < 16; y += sizeof(LTC_FAST_TYPE)) { *((LTC_FAST_TYPE*)(&ct[x + y])) = *((LTC_FAST_TYPE*)(&pt[x+y])) ^ *((LTC_FAST_TYPE*)(&gcm->buf[y])); *((LTC_FAST_TYPE*)(&gcm->X[y])) ^= *((LTC_FAST_TYPE*)(&ct[x+y])); } /* GMAC it */ gcm->pttotlen += 128; gcm_mult_h(gcm, gcm->X); /* increment counter */ for (y = 15; y >= 12; y--) { if (++gcm->Y[y] & 255) { break; } } if ((err = cipher_descriptor[gcm->cipher].ecb_encrypt(gcm->Y, gcm->buf, &gcm->K)) != CRYPT_OK) { return err; } } } else { for (x = 0; x < (ptlen & ~15); x += 16) { /* ctr encrypt */ for (y = 0; y < 16; y += sizeof(LTC_FAST_TYPE)) { *((LTC_FAST_TYPE*)(&gcm->X[y])) ^= *((LTC_FAST_TYPE*)(&ct[x+y])); *((LTC_FAST_TYPE*)(&pt[x + y])) = *((LTC_FAST_TYPE*)(&ct[x+y])) ^ *((LTC_FAST_TYPE*)(&gcm->buf[y])); } /* GMAC it */ gcm->pttotlen += 128; gcm_mult_h(gcm, gcm->X); /* increment counter */ for (y = 15; y >= 12; y--) { if (++gcm->Y[y] & 255) { break; } } if ((err = cipher_descriptor[gcm->cipher].ecb_encrypt(gcm->Y, gcm->buf, &gcm->K)) != CRYPT_OK) { return err; } } } } #endif /* process text */ for (; x < ptlen; x++) { if (gcm->buflen == 16) { gcm->pttotlen += 128; gcm_mult_h(gcm, gcm->X); /* increment counter */ for (y = 15; y >= 12; y--) { if (++gcm->Y[y] & 255) { break; } } if ((err = cipher_descriptor[gcm->cipher].ecb_encrypt(gcm->Y, gcm->buf, &gcm->K)) != CRYPT_OK) { return err; } gcm->buflen = 0; } if (direction == GCM_ENCRYPT) { b = ct[x] = pt[x] ^ gcm->buf[gcm->buflen]; } else { b = ct[x]; pt[x] = ct[x] ^ gcm->buf[gcm->buflen]; } gcm->X[gcm->buflen++] ^= b; } return CRYPT_OK; } #endif /* $Source: /cvs/libtom/libtomcrypt/src/encauth/gcm/gcm_process.c,v $ */ /* $Revision: 1.16 $ */ /* $Date: 2007/05/12 14:32:35 $ */ /* LibTomCrypt, modular cryptographic library -- Tom St Denis * * LibTomCrypt is a library that provides various cryptographic * algorithms in a highly modular and flexible manner. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tomstdenis@gmail.com, http://libtom.org */ /** @file gcm_mult_h.c GCM implementation, do the GF mult, by Tom St Denis */ #if defined(LTC_GCM_MODE) /** GCM multiply by H @param gcm The GCM state which holds the H value @param I The value to multiply H by */ void gcm_mult_h(gcm_state *gcm, unsigned char *I) { unsigned char T[16]; #ifdef LTC_GCM_TABLES int x, y; #ifdef LTC_GCM_TABLES_SSE2 asm("movdqa (%0),%%xmm0"::"r"(&gcm->PC[0][I[0]][0])); for (x = 1; x < 16; x++) { asm("pxor (%0),%%xmm0"::"r"(&gcm->PC[x][I[x]][0])); } asm("movdqa %%xmm0,(%0)"::"r"(&T)); #else XMEMCPY(T, &gcm->PC[0][I[0]][0], 16); for (x = 1; x < 16; x++) { #ifdef LTC_FAST for (y = 0; y < 16; y += sizeof(LTC_FAST_TYPE)) { *((LTC_FAST_TYPE *)(T + y)) ^= *((LTC_FAST_TYPE *)(&gcm->PC[x][I[x]][y])); } #else for (y = 0; y < 16; y++) { T[y] ^= gcm->PC[x][I[x]][y]; } #endif /* LTC_FAST */ } #endif /* LTC_GCM_TABLES_SSE2 */ #else gcm_gf_mult(gcm->H, I, T); #endif XMEMCPY(I, T, 16); } #endif /* $Source: /cvs/libtom/libtomcrypt/src/encauth/gcm/gcm_mult_h.c,v $ */ /* $Revision: 1.6 $ */ /* $Date: 2007/05/12 14:32:35 $ */ /* LibTomCrypt, modular cryptographic library -- Tom St Denis * * LibTomCrypt is a library that provides various cryptographic * algorithms in a highly modular and flexible manner. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tomstdenis@gmail.com, http://libtom.org */ /** @file gcm_gf_mult.c GCM implementation, do the GF mult, by Tom St Denis */ #if defined(LTC_GCM_TABLES) || defined(LRW_TABLES) || ((defined(LTC_GCM_MODE) || defined(LTC_GCM_MODE)) && defined(LTC_FAST)) /* this is x*2^128 mod p(x) ... the results are 16 bytes each stored in a packed format. Since only the * lower 16 bits are not zero'ed I removed the upper 14 bytes */ const unsigned char gcm_shift_table[256*2] = { 0x00, 0x00, 0x01, 0xc2, 0x03, 0x84, 0x02, 0x46, 0x07, 0x08, 0x06, 0xca, 0x04, 0x8c, 0x05, 0x4e, 0x0e, 0x10, 0x0f, 0xd2, 0x0d, 0x94, 0x0c, 0x56, 0x09, 0x18, 0x08, 0xda, 0x0a, 0x9c, 0x0b, 0x5e, 0x1c, 0x20, 0x1d, 0xe2, 0x1f, 0xa4, 0x1e, 0x66, 0x1b, 0x28, 0x1a, 0xea, 0x18, 0xac, 0x19, 0x6e, 0x12, 0x30, 0x13, 0xf2, 0x11, 0xb4, 0x10, 0x76, 0x15, 0x38, 0x14, 0xfa, 0x16, 0xbc, 0x17, 0x7e, 0x38, 0x40, 0x39, 0x82, 0x3b, 0xc4, 0x3a, 0x06, 0x3f, 0x48, 0x3e, 0x8a, 0x3c, 0xcc, 0x3d, 0x0e, 0x36, 0x50, 0x37, 0x92, 0x35, 0xd4, 0x34, 0x16, 0x31, 0x58, 0x30, 0x9a, 0x32, 0xdc, 0x33, 0x1e, 0x24, 0x60, 0x25, 0xa2, 0x27, 0xe4, 0x26, 0x26, 0x23, 0x68, 0x22, 0xaa, 0x20, 0xec, 0x21, 0x2e, 0x2a, 0x70, 0x2b, 0xb2, 0x29, 0xf4, 0x28, 0x36, 0x2d, 0x78, 0x2c, 0xba, 0x2e, 0xfc, 0x2f, 0x3e, 0x70, 0x80, 0x71, 0x42, 0x73, 0x04, 0x72, 0xc6, 0x77, 0x88, 0x76, 0x4a, 0x74, 0x0c, 0x75, 0xce, 0x7e, 0x90, 0x7f, 0x52, 0x7d, 0x14, 0x7c, 0xd6, 0x79, 0x98, 0x78, 0x5a, 0x7a, 0x1c, 0x7b, 0xde, 0x6c, 0xa0, 0x6d, 0x62, 0x6f, 0x24, 0x6e, 0xe6, 0x6b, 0xa8, 0x6a, 0x6a, 0x68, 0x2c, 0x69, 0xee, 0x62, 0xb0, 0x63, 0x72, 0x61, 0x34, 0x60, 0xf6, 0x65, 0xb8, 0x64, 0x7a, 0x66, 0x3c, 0x67, 0xfe, 0x48, 0xc0, 0x49, 0x02, 0x4b, 0x44, 0x4a, 0x86, 0x4f, 0xc8, 0x4e, 0x0a, 0x4c, 0x4c, 0x4d, 0x8e, 0x46, 0xd0, 0x47, 0x12, 0x45, 0x54, 0x44, 0x96, 0x41, 0xd8, 0x40, 0x1a, 0x42, 0x5c, 0x43, 0x9e, 0x54, 0xe0, 0x55, 0x22, 0x57, 0x64, 0x56, 0xa6, 0x53, 0xe8, 0x52, 0x2a, 0x50, 0x6c, 0x51, 0xae, 0x5a, 0xf0, 0x5b, 0x32, 0x59, 0x74, 0x58, 0xb6, 0x5d, 0xf8, 0x5c, 0x3a, 0x5e, 0x7c, 0x5f, 0xbe, 0xe1, 0x00, 0xe0, 0xc2, 0xe2, 0x84, 0xe3, 0x46, 0xe6, 0x08, 0xe7, 0xca, 0xe5, 0x8c, 0xe4, 0x4e, 0xef, 0x10, 0xee, 0xd2, 0xec, 0x94, 0xed, 0x56, 0xe8, 0x18, 0xe9, 0xda, 0xeb, 0x9c, 0xea, 0x5e, 0xfd, 0x20, 0xfc, 0xe2, 0xfe, 0xa4, 0xff, 0x66, 0xfa, 0x28, 0xfb, 0xea, 0xf9, 0xac, 0xf8, 0x6e, 0xf3, 0x30, 0xf2, 0xf2, 0xf0, 0xb4, 0xf1, 0x76, 0xf4, 0x38, 0xf5, 0xfa, 0xf7, 0xbc, 0xf6, 0x7e, 0xd9, 0x40, 0xd8, 0x82, 0xda, 0xc4, 0xdb, 0x06, 0xde, 0x48, 0xdf, 0x8a, 0xdd, 0xcc, 0xdc, 0x0e, 0xd7, 0x50, 0xd6, 0x92, 0xd4, 0xd4, 0xd5, 0x16, 0xd0, 0x58, 0xd1, 0x9a, 0xd3, 0xdc, 0xd2, 0x1e, 0xc5, 0x60, 0xc4, 0xa2, 0xc6, 0xe4, 0xc7, 0x26, 0xc2, 0x68, 0xc3, 0xaa, 0xc1, 0xec, 0xc0, 0x2e, 0xcb, 0x70, 0xca, 0xb2, 0xc8, 0xf4, 0xc9, 0x36, 0xcc, 0x78, 0xcd, 0xba, 0xcf, 0xfc, 0xce, 0x3e, 0x91, 0x80, 0x90, 0x42, 0x92, 0x04, 0x93, 0xc6, 0x96, 0x88, 0x97, 0x4a, 0x95, 0x0c, 0x94, 0xce, 0x9f, 0x90, 0x9e, 0x52, 0x9c, 0x14, 0x9d, 0xd6, 0x98, 0x98, 0x99, 0x5a, 0x9b, 0x1c, 0x9a, 0xde, 0x8d, 0xa0, 0x8c, 0x62, 0x8e, 0x24, 0x8f, 0xe6, 0x8a, 0xa8, 0x8b, 0x6a, 0x89, 0x2c, 0x88, 0xee, 0x83, 0xb0, 0x82, 0x72, 0x80, 0x34, 0x81, 0xf6, 0x84, 0xb8, 0x85, 0x7a, 0x87, 0x3c, 0x86, 0xfe, 0xa9, 0xc0, 0xa8, 0x02, 0xaa, 0x44, 0xab, 0x86, 0xae, 0xc8, 0xaf, 0x0a, 0xad, 0x4c, 0xac, 0x8e, 0xa7, 0xd0, 0xa6, 0x12, 0xa4, 0x54, 0xa5, 0x96, 0xa0, 0xd8, 0xa1, 0x1a, 0xa3, 0x5c, 0xa2, 0x9e, 0xb5, 0xe0, 0xb4, 0x22, 0xb6, 0x64, 0xb7, 0xa6, 0xb2, 0xe8, 0xb3, 0x2a, 0xb1, 0x6c, 0xb0, 0xae, 0xbb, 0xf0, 0xba, 0x32, 0xb8, 0x74, 0xb9, 0xb6, 0xbc, 0xf8, 0xbd, 0x3a, 0xbf, 0x7c, 0xbe, 0xbe }; #endif #if defined(LTC_GCM_MODE) || defined(LRW_MODE) #ifndef LTC_FAST /* right shift */ static void gcm_rightshift(unsigned char *a) { int x; for (x = 15; x > 0; x--) { a[x] = (a[x]>>1) | ((a[x-1]<<7)&0x80); } a[0] >>= 1; } /* c = b*a */ static const unsigned char mask[] = { 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01 }; static const unsigned char poly_[] = { 0x00, 0xE1 }; /** GCM GF multiplier (internal use only) bitserial @param a First value @param b Second value @param c Destination for a * b */ void gcm_gf_mult(const unsigned char *a, const unsigned char *b, unsigned char *c) { unsigned char Z[16], V[16]; unsigned char x, y, z; zeromem(Z, 16); XMEMCPY(V, a, 16); for (x = 0; x < 128; x++) { if (b[x>>3] & mask[x&7]) { for (y = 0; y < 16; y++) { Z[y] ^= V[y]; } } z = V[15] & 0x01; gcm_rightshift(V); V[0] ^= poly_[z]; } XMEMCPY(c, Z, 16); } #else /* map normal numbers to "ieee" way ... e.g. bit reversed */ #define M(x) ( ((x&8)>>3) | ((x&4)>>1) | ((x&2)<<1) | ((x&1)<<3) ) #define BPD (sizeof(LTC_FAST_TYPE) * 8) #define WPV (1 + (16 / sizeof(LTC_FAST_TYPE))) /** GCM GF multiplier (internal use only) word oriented @param a First value @param b Second value @param c Destination for a * b */ void gcm_gf_mult(const unsigned char *a, const unsigned char *b, unsigned char *c) { int i, j, k, u; LTC_FAST_TYPE B[16][WPV], tmp[32 / sizeof(LTC_FAST_TYPE)], pB[16 / sizeof(LTC_FAST_TYPE)], zz, z; unsigned char pTmp[32]; /* create simple tables */ zeromem(B[0], sizeof(B[0])); zeromem(B[M(1)], sizeof(B[M(1)])); #ifdef ENDIAN_32BITWORD for (i = 0; i < 4; i++) { LOAD32H(B[M(1)][i], a + (i<<2)); LOAD32L(pB[i], b + (i<<2)); } #else for (i = 0; i < 2; i++) { LOAD64H(B[M(1)][i], a + (i<<3)); LOAD64L(pB[i], b + (i<<3)); } #endif /* now create 2, 4 and 8 */ B[M(2)][0] = B[M(1)][0] >> 1; B[M(4)][0] = B[M(1)][0] >> 2; B[M(8)][0] = B[M(1)][0] >> 3; for (i = 1; i < (int)WPV; i++) { B[M(2)][i] = (B[M(1)][i-1] << (BPD-1)) | (B[M(1)][i] >> 1); B[M(4)][i] = (B[M(1)][i-1] << (BPD-2)) | (B[M(1)][i] >> 2); B[M(8)][i] = (B[M(1)][i-1] << (BPD-3)) | (B[M(1)][i] >> 3); } /* now all values with two bits which are 3, 5, 6, 9, 10, 12 */ for (i = 0; i < (int)WPV; i++) { B[M(3)][i] = B[M(1)][i] ^ B[M(2)][i]; B[M(5)][i] = B[M(1)][i] ^ B[M(4)][i]; B[M(6)][i] = B[M(2)][i] ^ B[M(4)][i]; B[M(9)][i] = B[M(1)][i] ^ B[M(8)][i]; B[M(10)][i] = B[M(2)][i] ^ B[M(8)][i]; B[M(12)][i] = B[M(8)][i] ^ B[M(4)][i]; /* now all 3 bit values and the only 4 bit value: 7, 11, 13, 14, 15 */ B[M(7)][i] = B[M(3)][i] ^ B[M(4)][i]; B[M(11)][i] = B[M(3)][i] ^ B[M(8)][i]; B[M(13)][i] = B[M(1)][i] ^ B[M(12)][i]; B[M(14)][i] = B[M(6)][i] ^ B[M(8)][i]; B[M(15)][i] = B[M(7)][i] ^ B[M(8)][i]; } zeromem(tmp, sizeof(tmp)); /* compute product four bits of each word at a time */ /* for each nibble */ for (i = (BPD/4)-1; i >= 0; i--) { /* for each word */ for (j = 0; j < (int)(WPV-1); j++) { /* grab the 4 bits recall the nibbles are backwards so it's a shift by (i^1)*4 */ u = (pB[j] >> ((i^1)<<2)) & 15; /* add offset by the word count the table looked up value to the result */ for (k = 0; k < (int)WPV; k++) { tmp[k+j] ^= B[u][k]; } } /* shift result up by 4 bits */ if (i != 0) { for (z = j = 0; j < (int)(32 / sizeof(LTC_FAST_TYPE)); j++) { zz = tmp[j] << (BPD-4); tmp[j] = (tmp[j] >> 4) | z; z = zz; } } } /* store product */ #ifdef ENDIAN_32BITWORD for (i = 0; i < 8; i++) { STORE32H(tmp[i], pTmp + (i<<2)); } #else for (i = 0; i < 4; i++) { STORE64H(tmp[i], pTmp + (i<<3)); } #endif /* reduce by taking most significant byte and adding the appropriate two byte sequence 16 bytes down */ for (i = 31; i >= 16; i--) { pTmp[i-16] ^= gcm_shift_table[((unsigned)pTmp[i]<<1)]; pTmp[i-15] ^= gcm_shift_table[((unsigned)pTmp[i]<<1)+1]; } for (i = 0; i < 16; i++) { c[i] = pTmp[i]; } } #endif #endif /* $Source: /cvs/libtom/libtomcrypt/src/encauth/gcm/gcm_gf_mult.c,v $ */ /* $Revision: 1.25 $ */ /* $Date: 2007/05/12 14:32:35 $ */ /* LibTomCrypt, modular cryptographic library -- Tom St Denis * * LibTomCrypt is a library that provides various cryptographic * algorithms in a highly modular and flexible manner. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tomstdenis@gmail.com, http://libtom.org */ /** @file gcm_add_aad.c GCM implementation, Add AAD data to the stream, by Tom St Denis */ #ifdef LTC_GCM_MODE /** Add AAD to the GCM state @param gcm The GCM state @param adata The additional authentication data to add to the GCM state @param adatalen The length of the AAD data. @return CRYPT_OK on success */ int gcm_add_aad(gcm_state *gcm, const unsigned char *adata, unsigned long adatalen) { unsigned long x; int err; #ifdef LTC_FAST unsigned long y; #endif LTC_ARGCHK(gcm != NULL); if (adatalen > 0) { LTC_ARGCHK(adata != NULL); } if (gcm->buflen > 16 || gcm->buflen < 0) { return CRYPT_INVALID_ARG; } if ((err = cipher_is_valid(gcm->cipher)) != CRYPT_OK) { return err; } /* in IV mode? */ if (gcm->mode == LTC_GCM_MODE_IV) { /* let's process the IV */ if (gcm->ivmode || gcm->buflen != 12) { for (x = 0; x < (unsigned long)gcm->buflen; x++) { gcm->X[x] ^= gcm->buf[x]; } if (gcm->buflen) { gcm->totlen += gcm->buflen * CONST64(8); gcm_mult_h(gcm, gcm->X); } /* mix in the length */ zeromem(gcm->buf, 8); STORE64H(gcm->totlen, gcm->buf+8); for (x = 0; x < 16; x++) { gcm->X[x] ^= gcm->buf[x]; } gcm_mult_h(gcm, gcm->X); /* copy counter out */ XMEMCPY(gcm->Y, gcm->X, 16); zeromem(gcm->X, 16); } else { XMEMCPY(gcm->Y, gcm->buf, 12); gcm->Y[12] = 0; gcm->Y[13] = 0; gcm->Y[14] = 0; gcm->Y[15] = 1; } XMEMCPY(gcm->Y_0, gcm->Y, 16); zeromem(gcm->buf, 16); gcm->buflen = 0; gcm->totlen = 0; gcm->mode = LTC_GCM_MODE_AAD; } if (gcm->mode != LTC_GCM_MODE_AAD || gcm->buflen >= 16) { return CRYPT_INVALID_ARG; } x = 0; #ifdef LTC_FAST if (gcm->buflen == 0) { for (x = 0; x < (adatalen & ~15); x += 16) { for (y = 0; y < 16; y += sizeof(LTC_FAST_TYPE)) { *((LTC_FAST_TYPE*)(&gcm->X[y])) ^= *((LTC_FAST_TYPE*)(&adata[x + y])); } gcm_mult_h(gcm, gcm->X); gcm->totlen += 128; } adata += x; } #endif /* start adding AAD data to the state */ for (; x < adatalen; x++) { gcm->X[gcm->buflen++] ^= *adata++; if (gcm->buflen == 16) { /* GF mult it */ gcm_mult_h(gcm, gcm->X); gcm->buflen = 0; gcm->totlen += 128; } } return CRYPT_OK; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ /* LibTomCrypt, modular cryptographic library -- Tom St Denis * * LibTomCrypt is a library that provides various cryptographic * algorithms in a highly modular and flexible manner. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tomstdenis@gmail.com, http://libtom.org */ /** @file gcm_reset.c GCM implementation, reset a used state so it can accept IV data, by Tom St Denis */ #ifdef LTC_GCM_MODE /** Reset a GCM state to as if you just called gcm_init(). This saves the initialization time. @param gcm The GCM state to reset @return CRYPT_OK on success */ int gcm_reset(gcm_state *gcm) { LTC_ARGCHK(gcm != NULL); zeromem(gcm->buf, sizeof(gcm->buf)); zeromem(gcm->X, sizeof(gcm->X)); gcm->mode = LTC_GCM_MODE_IV; gcm->ivmode = 0; gcm->buflen = 0; gcm->totlen = 0; gcm->pttotlen = 0; return CRYPT_OK; } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ /* LibTomCrypt, modular cryptographic library -- Tom St Denis * * LibTomCrypt is a library that provides various cryptographic * algorithms in a highly modular and flexible manner. * * The library is free for all purposes without any express * guarantee it works. * * Tom St Denis, tomstdenis@gmail.com, http://libtom.org */ /** @file md5.c LTC_MD5 hash function by Tom St Denis */ #ifdef LTC_MD5 const struct ltc_hash_descriptor md5_desc = { "md5", 3, 16, 64, /* OID */ { 1, 2, 840, 113549, 2, 5, }, 6, &md5_init, &md5_process, &md5_done, &md5_test, NULL }; #define F(x,y,z) (z ^ (x & (y ^ z))) #define G(x,y,z) (y ^ (z & (y ^ x))) #define H(x,y,z) (x^y^z) #define I(x,y,z) (y^(x|(~z))) #ifdef LTC_SMALL_CODE #define FF(a,b,c,d,M,s,t) \ a = (a + F(b,c,d) + M + t); a = ROL(a, s) + b; #define GG(a,b,c,d,M,s,t) \ a = (a + G(b,c,d) + M + t); a = ROL(a, s) + b; #define HH(a,b,c,d,M,s,t) \ a = (a + H(b,c,d) + M + t); a = ROL(a, s) + b; #define II(a,b,c,d,M,s,t) \ a = (a + I(b,c,d) + M + t); a = ROL(a, s) + b; static const unsigned char Worder[64] = { 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15, 1,6,11,0,5,10,15,4,9,14,3,8,13,2,7,12, 5,8,11,14,1,4,7,10,13,0,3,6,9,12,15,2, 0,7,14,5,12,3,10,1,8,15,6,13,4,11,2,9 }; static const unsigned char Rorder[64] = { 7,12,17,22,7,12,17,22,7,12,17,22,7,12,17,22, 5,9,14,20,5,9,14,20,5,9,14,20,5,9,14,20, 4,11,16,23,4,11,16,23,4,11,16,23,4,11,16,23, 6,10,15,21,6,10,15,21,6,10,15,21,6,10,15,21 }; static const ulong32 Korder[64] = { 0xd76aa478UL, 0xe8c7b756UL, 0x242070dbUL, 0xc1bdceeeUL, 0xf57c0fafUL, 0x4787c62aUL, 0xa8304613UL, 0xfd469501UL, 0x698098d8UL, 0x8b44f7afUL, 0xffff5bb1UL, 0x895cd7beUL, 0x6b901122UL, 0xfd987193UL, 0xa679438eUL, 0x49b40821UL, 0xf61e2562UL, 0xc040b340UL, 0x265e5a51UL, 0xe9b6c7aaUL, 0xd62f105dUL, 0x02441453UL, 0xd8a1e681UL, 0xe7d3fbc8UL, 0x21e1cde6UL, 0xc33707d6UL, 0xf4d50d87UL, 0x455a14edUL, 0xa9e3e905UL, 0xfcefa3f8UL, 0x676f02d9UL, 0x8d2a4c8aUL, 0xfffa3942UL, 0x8771f681UL, 0x6d9d6122UL, 0xfde5380cUL, 0xa4beea44UL, 0x4bdecfa9UL, 0xf6bb4b60UL, 0xbebfbc70UL, 0x289b7ec6UL, 0xeaa127faUL, 0xd4ef3085UL, 0x04881d05UL, 0xd9d4d039UL, 0xe6db99e5UL, 0x1fa27cf8UL, 0xc4ac5665UL, 0xf4292244UL, 0x432aff97UL, 0xab9423a7UL, 0xfc93a039UL, 0x655b59c3UL, 0x8f0ccc92UL, 0xffeff47dUL, 0x85845dd1UL, 0x6fa87e4fUL, 0xfe2ce6e0UL, 0xa3014314UL, 0x4e0811a1UL, 0xf7537e82UL, 0xbd3af235UL, 0x2ad7d2bbUL, 0xeb86d391UL }; #else #define FF(a,b,c,d,M,s,t) \ a = (a + F(b,c,d) + M + t); a = ROLc(a, s) + b; #define GG(a,b,c,d,M,s,t) \ a = (a + G(b,c,d) + M + t); a = ROLc(a, s) + b; #define HH(a,b,c,d,M,s,t) \ a = (a + H(b,c,d) + M + t); a = ROLc(a, s) + b; #define II(a,b,c,d,M,s,t) \ a = (a + I(b,c,d) + M + t); a = ROLc(a, s) + b; #endif #ifdef LTC_CLEAN_STACK static int _md5_compress(hash_state *md, unsigned char *buf) #else static int md5_compress(hash_state *md, unsigned char *buf) #endif { ulong32 i, W[16], a, b, c, d; #ifdef LTC_SMALL_CODE ulong32 t; #endif /* copy the state into 512-bits into W[0..15] */ for (i = 0; i < 16; i++) { LOAD32L(W[i], buf + (4*i)); } /* copy state */ a = md->md5.state[0]; b = md->md5.state[1]; c = md->md5.state[2]; d = md->md5.state[3]; #ifdef LTC_SMALL_CODE for (i = 0; i < 16; ++i) { FF(a,b,c,d,W[Worder[i]],Rorder[i],Korder[i]); t = d; d = c; c = b; b = a; a = t; } for (; i < 32; ++i) { GG(a,b,c,d,W[Worder[i]],Rorder[i],Korder[i]); t = d; d = c; c = b; b = a; a = t; } for (; i < 48; ++i) { HH(a,b,c,d,W[Worder[i]],Rorder[i],Korder[i]); t = d; d = c; c = b; b = a; a = t; } for (; i < 64; ++i) { II(a,b,c,d,W[Worder[i]],Rorder[i],Korder[i]); t = d; d = c; c = b; b = a; a = t; } #else FF(a,b,c,d,W[0],7,0xd76aa478UL) FF(d,a,b,c,W[1],12,0xe8c7b756UL) FF(c,d,a,b,W[2],17,0x242070dbUL) FF(b,c,d,a,W[3],22,0xc1bdceeeUL) FF(a,b,c,d,W[4],7,0xf57c0fafUL) FF(d,a,b,c,W[5],12,0x4787c62aUL) FF(c,d,a,b,W[6],17,0xa8304613UL) FF(b,c,d,a,W[7],22,0xfd469501UL) FF(a,b,c,d,W[8],7,0x698098d8UL) FF(d,a,b,c,W[9],12,0x8b44f7afUL) FF(c,d,a,b,W[10],17,0xffff5bb1UL) FF(b,c,d,a,W[11],22,0x895cd7beUL) FF(a,b,c,d,W[12],7,0x6b901122UL) FF(d,a,b,c,W[13],12,0xfd987193UL) FF(c,d,a,b,W[14],17,0xa679438eUL) FF(b,c,d,a,W[15],22,0x49b40821UL) GG(a,b,c,d,W[1],5,0xf61e2562UL) GG(d,a,b,c,W[6],9,0xc040b340UL) GG(c,d,a,b,W[11],14,0x265e5a51UL) GG(b,c,d,a,W[0],20,0xe9b6c7aaUL) GG(a,b,c,d,W[5],5,0xd62f105dUL) GG(d,a,b,c,W[10],9,0x02441453UL) GG(c,d,a,b,W[15],14,0xd8a1e681UL) GG(b,c,d,a,W[4],20,0xe7d3fbc8UL) GG(a,b,c,d,W[9],5,0x21e1cde6UL) GG(d,a,b,c,W[14],9,0xc33707d6UL) GG(c,d,a,b,W[3],14,0xf4d50d87UL) GG(b,c,d,a,W[8],20,0x455a14edUL) GG(a,b,c,d,W[13],5,0xa9e3e905UL) GG(d,a,b,c,W[2],9,0xfcefa3f8UL) GG(c,d,a,b,W[7],14,0x676f02d9UL) GG(b,c,d,a,W[12],20,0x8d2a4c8aUL) HH(a,b,c,d,W[5],4,0xfffa3942UL) HH(d,a,b,c,W[8],11,0x8771f681UL) HH(c,d,a,b,W[11],16,0x6d9d6122UL) HH(b,c,d,a,W[14],23,0xfde5380cUL) HH(a,b,c,d,W[1],4,0xa4beea44UL) HH(d,a,b,c,W[4],11,0x4bdecfa9UL) HH(c,d,a,b,W[7],16,0xf6bb4b60UL) HH(b,c,d,a,W[10],23,0xbebfbc70UL) HH(a,b,c,d,W[13],4,0x289b7ec6UL) HH(d,a,b,c,W[0],11,0xeaa127faUL) HH(c,d,a,b,W[3],16,0xd4ef3085UL) HH(b,c,d,a,W[6],23,0x04881d05UL) HH(a,b,c,d,W[9],4,0xd9d4d039UL) HH(d,a,b,c,W[12],11,0xe6db99e5UL) HH(c,d,a,b,W[15],16,0x1fa27cf8UL) HH(b,c,d,a,W[2],23,0xc4ac5665UL) II(a,b,c,d,W[0],6,0xf4292244UL) II(d,a,b,c,W[7],10,0x432aff97UL) II(c,d,a,b,W[14],15,0xab9423a7UL) II(b,c,d,a,W[5],21,0xfc93a039UL) II(a,b,c,d,W[12],6,0x655b59c3UL) II(d,a,b,c,W[3],10,0x8f0ccc92UL) II(c,d,a,b,W[10],15,0xffeff47dUL) II(b,c,d,a,W[1],21,0x85845dd1UL) II(a,b,c,d,W[8],6,0x6fa87e4fUL) II(d,a,b,c,W[15],10,0xfe2ce6e0UL) II(c,d,a,b,W[6],15,0xa3014314UL) II(b,c,d,a,W[13],21,0x4e0811a1UL) II(a,b,c,d,W[4],6,0xf7537e82UL) II(d,a,b,c,W[11],10,0xbd3af235UL) II(c,d,a,b,W[2],15,0x2ad7d2bbUL) II(b,c,d,a,W[9],21,0xeb86d391UL) #endif md->md5.state[0] = md->md5.state[0] + a; md->md5.state[1] = md->md5.state[1] + b; md->md5.state[2] = md->md5.state[2] + c; md->md5.state[3] = md->md5.state[3] + d; return CRYPT_OK; } #ifdef LTC_CLEAN_STACK static int md5_compress(hash_state *md, unsigned char *buf) { int err; err = _md5_compress(md, buf); burn_stack(sizeof(ulong32) * 21); return err; } #endif /** Initialize the hash state @param md The hash state you wish to initialize @return CRYPT_OK if successful */ int md5_init(hash_state * md) { LTC_ARGCHK(md != NULL); md->md5.state[0] = 0x67452301UL; md->md5.state[1] = 0xefcdab89UL; md->md5.state[2] = 0x98badcfeUL; md->md5.state[3] = 0x10325476UL; md->md5.curlen = 0; md->md5.length = 0; return CRYPT_OK; } /** Process a block of memory though the hash @param md The hash state @param in The data to hash @param inlen The length of the data (octets) @return CRYPT_OK if successful */ HASH_PROCESS(md5_process, md5_compress, md5, 64) /** Terminate the hash to get the digest @param md The hash state @param out [out] The destination of the hash (16 bytes) @return CRYPT_OK if successful */ int md5_done(hash_state * md, unsigned char *out) { int i; LTC_ARGCHK(md != NULL); LTC_ARGCHK(out != NULL); if (md->md5.curlen >= sizeof(md->md5.buf)) { return CRYPT_INVALID_ARG; } /* increase the length of the message */ md->md5.length += md->md5.curlen * 8; /* append the '1' bit */ md->md5.buf[md->md5.curlen++] = (unsigned char)0x80; /* if the length is currently above 56 bytes we append zeros * then compress. Then we can fall back to padding zeros and length * encoding like normal. */ if (md->md5.curlen > 56) { while (md->md5.curlen < 64) { md->md5.buf[md->md5.curlen++] = (unsigned char)0; } md5_compress(md, md->md5.buf); md->md5.curlen = 0; } /* pad upto 56 bytes of zeroes */ while (md->md5.curlen < 56) { md->md5.buf[md->md5.curlen++] = (unsigned char)0; } /* store length */ STORE64L(md->md5.length, md->md5.buf+56); md5_compress(md, md->md5.buf); /* copy output */ for (i = 0; i < 4; i++) { STORE32L(md->md5.state[i], out+(4*i)); } #ifdef LTC_CLEAN_STACK zeromem(md, sizeof(hash_state)); #endif return CRYPT_OK; } /** Self-test the hash @return CRYPT_OK if successful, CRYPT_NOP if self-tests have been disabled */ int md5_test(void) { #ifndef LTC_TEST return CRYPT_NOP; #else static const struct { char *msg; unsigned char hash[16]; } tests[] = { { "", { 0xd4, 0x1d, 0x8c, 0xd9, 0x8f, 0x00, 0xb2, 0x04, 0xe9, 0x80, 0x09, 0x98, 0xec, 0xf8, 0x42, 0x7e } }, { "a", {0x0c, 0xc1, 0x75, 0xb9, 0xc0, 0xf1, 0xb6, 0xa8, 0x31, 0xc3, 0x99, 0xe2, 0x69, 0x77, 0x26, 0x61 } }, { "abc", { 0x90, 0x01, 0x50, 0x98, 0x3c, 0xd2, 0x4f, 0xb0, 0xd6, 0x96, 0x3f, 0x7d, 0x28, 0xe1, 0x7f, 0x72 } }, { "message digest", { 0xf9, 0x6b, 0x69, 0x7d, 0x7c, 0xb7, 0x93, 0x8d, 0x52, 0x5a, 0x2f, 0x31, 0xaa, 0xf1, 0x61, 0xd0 } }, { "abcdefghijklmnopqrstuvwxyz", { 0xc3, 0xfc, 0xd3, 0xd7, 0x61, 0x92, 0xe4, 0x00, 0x7d, 0xfb, 0x49, 0x6c, 0xca, 0x67, 0xe1, 0x3b } }, { "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789", { 0xd1, 0x74, 0xab, 0x98, 0xd2, 0x77, 0xd9, 0xf5, 0xa5, 0x61, 0x1c, 0x2c, 0x9f, 0x41, 0x9d, 0x9f } }, { "12345678901234567890123456789012345678901234567890123456789012345678901234567890", { 0x57, 0xed, 0xf4, 0xa2, 0x2b, 0xe3, 0xc9, 0x55, 0xac, 0x49, 0xda, 0x2e, 0x21, 0x07, 0xb6, 0x7a } }, { NULL, { 0 } } }; int i; unsigned char tmp[16]; hash_state md; for (i = 0; tests[i].msg != NULL; i++) { md5_init(&md); md5_process(&md, (unsigned char *)tests[i].msg, (unsigned long)strlen(tests[i].msg)); md5_done(&md, tmp); if (XMEMCMP(tmp, tests[i].hash, 16) != 0) { return CRYPT_FAIL_TESTVECTOR; } } return CRYPT_OK; #endif } #endif /* $Source$ */ /* $Revision$ */ /* $Date$ */ /* ------------------------------------------------------------------------------ END libtomcrypt.c ------------------------------------------------------------------------------ */ #endif /* HTTPS_NO_LIBTOMCRYPT */ /* ------------------------------------------------------------------------------ BEGIN tlse readme ------------------------------------------------------------------------------ */ /* # TLSe Single C file TLS 1.3, 1.2, 1.1 and 1.0(without the weak ciphers) implementation, using [libtomcrypt](https://github.com/libtom/libtomcrypt "libtomcrypt") as crypto library. It also supports DTLS 1.2 and 1.0. Before using tlse.c you may want to download and compile tomcrypt; alternatively you may use libtomcrypt.c (see Compiling). I'm working at an alternative efficient RSA signing, DH and Curve25519 implementation, to allow the compilation, alternatively, without tomcrypt, on devices where memory and code size is an issue. **Note**: It does not implement 0-RTT. Client-side TLS 1.3 support is experimental. Like this project ? You may donate Bitcoin for this project at 14LqvMzFfaJ82C7wY5iavvTf9HPELYWsax ![](https://raw.githubusercontent.com/eduardsui/edwork/master/bwallet.png) Compiling ---------- Simple TLS client: `$ gcc tlshello.c -o tlshello -ltomcrypt -ltommath -DLTM_DESC` For debuging tls connections, the DEBUG flag must be set (-DDEBUG). Simple TLS server: `$ gcc tlsserverhello.c -o tlsserverhello -ltomcrypt -ltommath -DLTM_DESC` The entire library is a single c file that you just include in your source. The library may also use the libtomcrypt.c amalgamation. In this case, the client may be compiled: `$ gcc tlshello.c -o tlshello -DTLS_AMALGAMATION` and the server: `$ gcc tlsserverhello.c -o tlsserverhello -DTLS_AMALGAMATION` tlse.h is optional (is safe to just include tlse.c). Alternatively, you may include tlse.h and add tlse.c to your makefile (useful when linking against C++). If thread-safety is needed, you need to call `tls_init()` before letting any other threads in, and not use the same object from multiple threads without a mutex. Other than that, TLSe and libtomcrypt are thread-safe. Also, you may want to define LTC_PTHREAD if you're using libtomcrypt. TLSe supports KTLS on linux kernel 4.13 or higher. KTLS is a TLS implementation in the linux kernel. If TLS_RX is not defined, KTLS is send-only (you may use send/sendfile to send data, but you may not use recv). Also, the negotiation must be handled by TLSe. If KTLS support is needed, define WITH_KTLS (compile with -DWITH_KTLS). Note that is not clear which header should be included for linux structure, you may need to check these structures and constants: https://github.com/torvalds/linux/blob/master/Documentation/networking/tls.txt. Usage ---------- You just `#include "tlse.c"` in your code. Everything is a single file. Features ---------- The main feature of this implementation is the ability to serialize TLS context, via tls_export_context and re-import it, via tls_import_context in another pre-forked worker process (socket descriptor may be sent via sendmsg). For now it supports TLS 1.2, TLS 1.1 + 1.0 (when TLS_LEGACY_SUPPORT is defined / default is on), RSA, ECDSA, DHE, ECDHE ciphers: ``TLS_DHE_RSA_WITH_AES_128_CBC_SHA, TLS_DHE_RSA_WITH_AES_256_CBC_SHA, TLS_DHE_RSA_WITH_AES_128_CBC_SHA256, TLS_DHE_RSA_WITH_AES_256_CBC_SHA256, TLS_DHE_RSA_WITH_AES_128_GCM_SHA256, TLS_DHE_RSA_WITH_AES_256_GCM_SHA384, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384, TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256` and `TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384``. The following ciphers are supported but disabled by default: ``TLS_RSA_WITH_AES_128_CBC_SHA, TLS_RSA_WITH_AES_256_CBC_SHA, TLS_RSA_WITH_AES_128_CBC_SHA256, TLS_RSA_WITH_AES_256_CBC_SHA256, TLS_RSA_WITH_AES_128_GCM_SHA256, TLS_RSA_WITH_AES_256_GCM_SHA384``. To enable these ciphers, TLSe must be compiled with ``-DNO_TLS_ROBOT_MITIGATION``. ROBOT attack is mitigated by default, but it is recommended to disable RSA encryption to avoid future vulnerabilities. TLSe now supports ChaCha20/Poly1305 ciphers: `TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256`, `TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256` and `TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256`. These ciphers are enabled by default. It has a low level interface, efficient for non-blocking, asynchronous sockets, and a blocking, libssl-style interface. It implements all that is needed for the TLS protocol version 1.2 and a pem/der parser. From tomcrypt it uses RSA, ECDSA and AES(GCM and CBC) encryption/decryption, SHA1, SHA256, SHA384, SHA512 and HMAC functions. Now it supports client certificate. To request a client certificate, call ``tls_request_client_certificate(TLSContext *)`` following ``tls_accept(TLSContext *)``. It implements SNI extension (Server Name Indication). To get the SNI string call ``tls_sni(TLSContext *)``. It also implements SCSV and ALPN (see ``tls_add_alpn(struct TLSContext *, const char *)`` and ``const char *tls_alpn(struct TLSContext *)``. The library supports certificate validation by using ``tls_certificate_chain_is_valid``, ``tls_certificate_chain_is_valid_root``, ``tls_certificate_valid_subject`` and ``tls_certificate_is_valid``(checks not before/not after). Note that certificates fed to ``tls_certificate_chain_is_valid`` must be in correct order (certificate 2 signs certificate 1, certificate 3 signs certificate 2 and so on; also certificate 1 (first) is the certificate to be used in key exchange). This library was written to be used by my other projects [Concept Applications Server](https://github.com/Devronium/ConceptApplicationServer "Concept Application Server") and [Concept Native Client](https://github.com/Devronium/ConceptClientQT "Concept Client QT") Examples ---------- 1. [examples/tlsclienthello.c](https://github.com/eduardsui/tlslayer/blob/master/examples/tlsclienthello.c) simple client example 2. [examples/tlshelloworld.c](https://github.com/eduardsui/tlslayer/blob/master/examples/tlshelloworld.c) simple server example 3. [examples/tlssimple.c](https://github.com/eduardsui/tlslayer/blob/master/examples/tlssimple.c) simple blocking client using libssl-ish API 4. [examples/tlssimpleserver.c](https://github.com/eduardsui/tlslayer/blob/master/examples/tlssimpleserver.c) simple blocking server using libssl-ish API After compiling the examples, in the working directory, you should put fullchain.pem and privkey.pem in a directory called testcert for running the server examples. I've used [letsencrypt](https://github.com/letsencrypt/letsencrypt) for certificate generation (is free!). Important security note ---------- Note that for DTLS, it doesn't implement a state machine, so using this DTLS implementation with UDP (server) may expose your server to DoS attack. License ---------- Public domain, BSD, MIT. Choose one. */ /* ------------------------------------------------------------------------------ END tlse readme ------------------------------------------------------------------------------ */ /* ------------------------------------------------------------------------------ BEGIN tlse.h ------------------------------------------------------------------------------ */ #ifndef TLSE_H #define TLSE_H // #define DEBUG // define TLS_LEGACY_SUPPORT to support TLS 1.1/1.0 (legacy) // legacy support it will use an additional 272 bytes / context #ifndef NO_TLS_LEGACY_SUPPORT #define TLS_LEGACY_SUPPORT #endif // SSL_* style blocking APIs #ifndef NO_SSL_COMPATIBLE_INTERFACE #define SSL_COMPATIBLE_INTERFACE #endif // support ChaCha20/Poly1305 #if !defined(__BIG_ENDIAN__) && ((!defined(__BYTE_ORDER)) || (__BYTE_ORDER == __LITTLE_ENDIAN)) // not working on big endian machines #ifndef NO_TLS_WITH_CHACHA20_POLY1305 #define TLS_WITH_CHACHA20_POLY1305 #endif #endif #ifndef NO_TLS_13 #define WITH_TLS_13 #endif // support forward secrecy (Diffie-Hellman ephemeral) #ifndef NO_TLS_FORWARD_SECRECY #define TLS_FORWARD_SECRECY #endif // support client-side ECDHE #ifndef NO_TLS_CLIENT_ECDHE #define TLS_CLIENT_ECDHE #endif // suport ecdsa #ifndef NO_TLS_ECDSA_SUPPORTED #define TLS_ECDSA_SUPPORTED #endif // suport ecdsa client-side // #define TLS_CLIENT_ECDSA // TLS renegotiation is disabled by default (secured or not) // do not uncomment next line! // #define TLS_ACCEPT_SECURE_RENEGOTIATION // basic superficial X509v1 certificate support #ifndef NO_TLS_X509_V1_SUPPORT #define TLS_X509_V1_SUPPORT #endif // disable TLS_RSA_WITH_* ciphers #ifndef NO_TLS_ROBOT_MITIGATION #define TLS_ROBOT_MITIGATION #endif #define SSL_V30 0x0300 #define TLS_V10 0x0301 #define TLS_V11 0x0302 #define TLS_V12 0x0303 #define TLS_V13 0x0304 #define DTLS_V10 0xFEFF #define DTLS_V12 0xFEFD #define DTLS_V13 0xFEFC #define TLS_NEED_MORE_DATA 0 #define TLS_GENERIC_ERROR -1 #define TLS_BROKEN_PACKET -2 #define TLS_NOT_UNDERSTOOD -3 #define TLS_NOT_SAFE -4 #define TLS_NO_COMMON_CIPHER -5 #define TLS_UNEXPECTED_MESSAGE -6 #define TLS_CLOSE_CONNECTION -7 #define TLS_COMPRESSION_NOT_SUPPORTED -8 #define TLS_NO_MEMORY -9 #define TLS_NOT_VERIFIED -10 #define TLS_INTEGRITY_FAILED -11 #define TLS_ERROR_ALERT -12 #define TLS_BROKEN_CONNECTION -13 #define TLS_BAD_CERTIFICATE -14 #define TLS_UNSUPPORTED_CERTIFICATE -15 #define TLS_NO_RENEGOTIATION -16 #define TLS_FEATURE_NOT_SUPPORTED -17 #define TLS_DECRYPTION_FAILED -20 #define TLS_AES_128_GCM_SHA256 0x1301 #define TLS_AES_256_GCM_SHA384 0x1302 #define TLS_CHACHA20_POLY1305_SHA256 0x1303 #define TLS_AES_128_CCM_SHA256 0x1304 #define TLS_AES_128_CCM_8_SHA256 0x1305 #define TLS_RSA_WITH_AES_128_CBC_SHA 0x002F #define TLS_RSA_WITH_AES_256_CBC_SHA 0x0035 #define TLS_RSA_WITH_AES_128_CBC_SHA256 0x003C #define TLS_RSA_WITH_AES_256_CBC_SHA256 0x003D #define TLS_RSA_WITH_AES_128_GCM_SHA256 0x009C #define TLS_RSA_WITH_AES_256_GCM_SHA384 0x009D // forward secrecy #define TLS_DHE_RSA_WITH_AES_128_CBC_SHA 0x0033 #define TLS_DHE_RSA_WITH_AES_256_CBC_SHA 0x0039 #define TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 0x0067 #define TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 0x006B #define TLS_DHE_RSA_WITH_AES_128_GCM_SHA256 0x009E #define TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 0x009F #define TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA 0xC013 #define TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA 0xC014 #define TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 0xC027 #define TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 0xC02F #define TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 0xC030 #define TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA 0xC009 #define TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA 0xC00A #define TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 0xC023 #define TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384 0xC024 #define TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 0xC02B #define TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 0xC02C #define TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 0xCCA8 #define TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 0xCCA9 #define TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256 0xCCAA #define TLS_FALLBACK_SCSV 0x5600 #define TLS_UNSUPPORTED_ALGORITHM 0x00 #define TLS_RSA_SIGN_RSA 0x01 #define TLS_RSA_SIGN_MD5 0x04 #define TLS_RSA_SIGN_SHA1 0x05 #define TLS_RSA_SIGN_SHA256 0x0B #define TLS_RSA_SIGN_SHA384 0x0C #define TLS_RSA_SIGN_SHA512 0x0D #define TLS_ECDSA_SIGN_SHA256 0x0E #define TLS_EC_PUBLIC_KEY 0x11 #define TLS_EC_prime192v1 0x12 #define TLS_EC_prime192v2 0x13 #define TLS_EC_prime192v3 0x14 #define TLS_EC_prime239v1 0x15 #define TLS_EC_prime239v2 0x16 #define TLS_EC_prime239v3 0x17 #define TLS_EC_prime256v1 0x18 #define TLS_EC_secp224r1 21 #define TLS_EC_secp256r1 23 #define TLS_EC_secp384r1 24 #define TLS_EC_secp521r1 25 #define TLS_ALERT_WARNING 0x01 #define TLS_ALERT_CRITICAL 0x02 #ifdef TLS_ROBOT_MITIGATION #define TLS_CIPHERS_SIZE(n, mitigated) n * 2 #else #define TLS_CIPHERS_SIZE(n, mitigated) (n + mitigated) * 2 #endif #ifdef __cplusplus extern "C" { #endif typedef enum { close_notify = 0, unexpected_message = 10, bad_record_mac = 20, decryption_failed_RESERVED = 21, record_overflow = 22, decompression_failure = 30, handshake_failure = 40, no_certificate_RESERVED = 41, bad_certificate = 42, unsupported_certificate = 43, certificate_revoked = 44, certificate_expired = 45, certificate_unknown = 46, illegal_parameter = 47, unknown_ca = 48, access_denied = 49, decode_error = 50, decrypt_error = 51, export_restriction_RESERVED = 60, protocol_version = 70, insufficient_security = 71, internal_error = 80, inappropriate_fallback = 86, user_canceled = 90, no_renegotiation = 100, unsupported_extension = 110, no_error = 255 } TLSAlertDescription; // forward declarations struct TLSPacket; struct TLSCertificate; struct TLSContext; struct ECCCurveParameters; typedef struct TLSContext TLS; typedef struct TLSCertificate Certificate; typedef int (*tls_validation_function)(struct TLSContext *context, struct TLSCertificate **certificate_chain, int len); /* Global initialization. Optional, as it will be called automatically; however, the initialization is not thread-safe, so if you intend to use TLSe from multiple threads, you'll need to call tls_init() once, from a single thread, before using the library. */ void tls_init(); unsigned char *tls_pem_decode(const unsigned char *data_in, unsigned int input_length, int cert_index, unsigned int *output_len); struct TLSCertificate *tls_create_certificate(); int tls_certificate_valid_subject(struct TLSCertificate *cert, const char *subject); int tls_certificate_valid_subject_name(const unsigned char *cert_subject, const char *subject); int tls_certificate_is_valid(struct TLSCertificate *cert); void tls_certificate_set_copy(unsigned char **member, const unsigned char *val, int len); void tls_certificate_set_copy_date(unsigned char **member, const unsigned char *val, int len); void tls_certificate_set_key(struct TLSCertificate *cert, const unsigned char *val, int len); void tls_certificate_set_priv(struct TLSCertificate *cert, const unsigned char *val, int len); void tls_certificate_set_sign_key(struct TLSCertificate *cert, const unsigned char *val, int len); char *tls_certificate_to_string(struct TLSCertificate *cert, char *buffer, int len); void tls_certificate_set_exponent(struct TLSCertificate *cert, const unsigned char *val, int len); void tls_certificate_set_serial(struct TLSCertificate *cert, const unsigned char *val, int len); void tls_certificate_set_algorithm(struct TLSContext *context, unsigned int *algorithm, const unsigned char *val, int len); void tls_destroy_certificate(struct TLSCertificate *cert); struct TLSPacket *tls_create_packet(struct TLSContext *context, unsigned char type, unsigned short version, int payload_size_hint); void tls_destroy_packet(struct TLSPacket *packet); void tls_packet_update(struct TLSPacket *packet); int tls_packet_append(struct TLSPacket *packet, const unsigned char *buf, unsigned int len); int tls_packet_uint8(struct TLSPacket *packet, unsigned char i); int tls_packet_uint16(struct TLSPacket *packet, unsigned short i); int tls_packet_uint32(struct TLSPacket *packet, unsigned int i); int tls_packet_uint24(struct TLSPacket *packet, unsigned int i); int tls_random(unsigned char *key, int len); /* Get encrypted data to write, if any. Once you've sent all of it, call tls_buffer_clear(). */ const unsigned char *tls_get_write_buffer(struct TLSContext *context, unsigned int *outlen); void tls_buffer_clear(struct TLSContext *context); /* Returns 1 for established, 0 for not established yet, and -1 for a critical error. */ int tls_established(struct TLSContext *context); /* Discards any unread decrypted data not consumed by tls_read(). */ void tls_read_clear(struct TLSContext *context); /* Reads any unread decrypted data (see tls_consume_stream). If you don't read all of it, the remainder will be left in the internal buffers for next tls_read(). Returns -1 for fatal error, 0 for no more data, or otherwise the number of bytes copied into the buffer (up to a maximum of the given size). */ int tls_read(struct TLSContext *context, unsigned char *buf, unsigned int size); struct TLSContext *tls_create_context(unsigned char is_server, unsigned short version); const struct ECCCurveParameters *tls_set_curve(struct TLSContext *context, const struct ECCCurveParameters *curve); /* Create a context for a given client, from a server context. Returns NULL on error. */ struct TLSContext *tls_accept(struct TLSContext *context); int tls_set_default_dhe_pg(struct TLSContext *context, const char *p_hex_str, const char *g_hex_str); void tls_destroy_context(struct TLSContext *context); int tls_cipher_supported(struct TLSContext *context, unsigned short cipher); int tls_cipher_is_fs(struct TLSContext *context, unsigned short cipher); int tls_choose_cipher(struct TLSContext *context, const unsigned char *buf, int buf_len, int *scsv_set); int tls_cipher_is_ephemeral(struct TLSContext *context); const char *tls_cipher_name(struct TLSContext *context); int tls_is_ecdsa(struct TLSContext *context); struct TLSPacket *tls_build_client_key_exchange(struct TLSContext *context); struct TLSPacket *tls_build_server_key_exchange(struct TLSContext *context, int method); struct TLSPacket *tls_build_hello(struct TLSContext *context, int tls13_downgrade); struct TLSPacket *tls_certificate_request(struct TLSContext *context); struct TLSPacket *tls_build_verify_request(struct TLSContext *context); int tls_parse_hello(struct TLSContext *context, const unsigned char *buf, int buf_len, unsigned int *write_packets, unsigned int *dtls_verified); int tls_parse_certificate(struct TLSContext *context, const unsigned char *buf, int buf_len, int is_client); int tls_parse_server_key_exchange(struct TLSContext *context, const unsigned char *buf, int buf_len); int tls_parse_client_key_exchange(struct TLSContext *context, const unsigned char *buf, int buf_len); int tls_parse_server_hello_done(struct TLSContext *context, const unsigned char *buf, int buf_len); int tls_parse_finished(struct TLSContext *context, const unsigned char *buf, int buf_len, unsigned int *write_packets); int tls_parse_verify(struct TLSContext *context, const unsigned char *buf, int buf_len); int tls_parse_payload(struct TLSContext *context, const unsigned char *buf, int buf_len, tls_validation_function certificate_verify); int tls_parse_message(struct TLSContext *context, unsigned char *buf, int buf_len, tls_validation_function certificate_verify); int tls_certificate_verify_signature(struct TLSCertificate *cert, struct TLSCertificate *parent); int tls_certificate_chain_is_valid(struct TLSCertificate **certificates, int len); int tls_certificate_chain_is_valid_root(struct TLSContext *context, struct TLSCertificate **certificates, int len); /* Add a certificate or a certificate chain to the given context, in PEM form. Returns a negative value (TLS_GENERIC_ERROR etc.) on error, 0 if there were no certificates in the buffer, or the number of loaded certificates on success. */ int tls_load_certificates(struct TLSContext *context, const unsigned char *pem_buffer, int pem_size); /* Add a private key to the given context, in PEM form. Returns a negative value (TLS_GENERIC_ERROR etc.) on error, 0 if there was no private key in the buffer, or 1 on success. */ int tls_load_private_key(struct TLSContext *context, const unsigned char *pem_buffer, int pem_size); struct TLSPacket *tls_build_certificate(struct TLSContext *context); struct TLSPacket *tls_build_finished(struct TLSContext *context); struct TLSPacket *tls_build_change_cipher_spec(struct TLSContext *context); struct TLSPacket *tls_build_done(struct TLSContext *context); struct TLSPacket *tls_build_message(struct TLSContext *context, const unsigned char *data, unsigned int len); int tls_client_connect(struct TLSContext *context); int tls_write(struct TLSContext *context, const unsigned char *data, unsigned int len); struct TLSPacket *tls_build_alert(struct TLSContext *context, char critical, unsigned char code); /* Process a given number of input bytes from a socket. If the other side just presented a certificate and certificate_verify is not NULL, it will be called. Returns 0 if there's no data ready yet, a negative value (see TLS_GENERIC_ERROR etc.) for an error, or a positive value (the number of bytes used from buf) if one or more complete TLS messages were received. The data is copied into an internal buffer even if not all of it was consumed, so you should not re-send it the next time. Decrypted data, if any, should be read back with tls_read(). Can change the status of tls_established(). If the library has anything to send back on the socket (e.g. as part of the handshake), tls_get_write_buffer() will return non-NULL. */ int tls_consume_stream(struct TLSContext *context, const unsigned char *buf, int buf_len, tls_validation_function certificate_verify); void tls_close_notify(struct TLSContext *context); void tls_alert(struct TLSContext *context, unsigned char critical, int code); /* Whether tls_consume_stream() has data in its buffer that is not processed yet. */ int tls_pending(struct TLSContext *context); /* Set the context as serializable or not. Must be called before negotiation. Exportable contexts use a bit more memory, to be able to hold the keys. Note that imported keys are not reexportable unless TLS_REEXPORTABLE is set. */ void tls_make_exportable(struct TLSContext *context, unsigned char exportable_flag); int tls_export_context(struct TLSContext *context, unsigned char *buffer, unsigned int buf_len, unsigned char small_version); struct TLSContext *tls_import_context(const unsigned char *buffer, unsigned int buf_len); int tls_is_broken(struct TLSContext *context); int tls_request_client_certificate(struct TLSContext *context); int tls_client_verified(struct TLSContext *context); const char *tls_sni(struct TLSContext *context); int tls_sni_set(struct TLSContext *context, const char *sni); int tls_load_root_certificates(struct TLSContext *context, const unsigned char *pem_buffer, int pem_size); int tls_default_verify(struct TLSContext *context, struct TLSCertificate **certificate_chain, int len); void tls_print_certificate(const char *fname); int tls_add_alpn(struct TLSContext *context, const char *alpn); int tls_alpn_contains(struct TLSContext *context, const char *alpn, unsigned char alpn_size); const char *tls_alpn(struct TLSContext *context); // useful when renewing certificates for servers, without the need to restart the server int tls_clear_certificates(struct TLSContext *context); int tls_make_ktls(struct TLSContext *context, int socket); int tls_unmake_ktls(struct TLSContext *context, int socket); /* Creates a new DTLS random cookie secret to be used in HelloVerifyRequest (server-side). It is recommended to call this function from time to time, to protect against some DoS attacks. */ void dtls_reset_cookie_secret(); int tls_remote_error(struct TLSContext *context); #ifdef SSL_COMPATIBLE_INTERFACE #define SSL_SERVER_RSA_CERT 1 #define SSL_SERVER_RSA_KEY 2 typedef struct TLSContext SSL_CTX; typedef struct TLSContext SSL; #define SSL_FILETYPE_PEM 1 #define SSL_VERIFY_NONE 0 #define SSL_VERIFY_PEER 1 #define SSL_VERIFY_FAIL_IF_NO_PEER_CERT 2 #define SSL_VERIFY_CLIENT_ONCE 3 typedef struct { int fd; tls_validation_function certificate_verify; void *recv; void *send; void *user_data; } SSLUserData; int SSL_library_init(); void SSL_load_error_strings(); void OpenSSL_add_all_algorithms(); void OpenSSL_add_all_ciphers(); void OpenSSL_add_all_digests(); void EVP_cleanup(); int SSLv3_server_method(); int SSLv3_client_method(); struct TLSContext *SSL_new(struct TLSContext *context); int SSL_CTX_use_certificate_file(struct TLSContext *context, const char *filename, int dummy); int SSL_CTX_use_PrivateKey_file(struct TLSContext *context, const char *filename, int dummy); int SSL_CTX_check_private_key(struct TLSContext *context); struct TLSContext *SSL_CTX_new(int method); void SSL_free(struct TLSContext *context); void SSL_CTX_free(struct TLSContext *context); int SSL_get_error(struct TLSContext *context, int ret); int SSL_set_fd(struct TLSContext *context, int socket); void *SSL_set_userdata(struct TLSContext *context, void *data); void *SSL_userdata(struct TLSContext *context); int SSL_CTX_root_ca(struct TLSContext *context, const char *pem_filename); void SSL_CTX_set_verify(struct TLSContext *context, int mode, tls_validation_function verify_callback); int SSL_accept(struct TLSContext *context); int SSL_connect(struct TLSContext *context); int SSL_shutdown(struct TLSContext *context); int SSL_write(struct TLSContext *context, const void *buf, unsigned int len); int SSL_read(struct TLSContext *context, void *buf, unsigned int len); int SSL_pending(struct TLSContext *context); int SSL_set_io(struct TLSContext *context, void *recv, void *send); #endif #ifdef TLS_SRTP struct SRTPContext; #define SRTP_NULL 0 #define SRTP_AES_CM 1 #define SRTP_AUTH_NULL 0 #define SRTP_AUTH_HMAC_SHA1 1 struct SRTPContext *srtp_init(unsigned char mode, unsigned char auth_mode); int srtp_key(struct SRTPContext *context, const void *key, int keylen, const void *salt, int saltlen, int tag_bits); int srtp_inline(struct SRTPContext *context, const char *b64, int tag_bits); int srtp_encrypt(struct SRTPContext *context, const unsigned char *pt_header, int pt_len, const unsigned char *payload, unsigned int payload_len, unsigned char *out, int *out_buffer_len); int srtp_decrypt(struct SRTPContext *context, const unsigned char *pt_header, int pt_len, const unsigned char *payload, unsigned int payload_len, unsigned char *out, int *out_buffer_len); void srtp_destroy(struct SRTPContext *context); #endif #ifdef __cplusplus } // extern "C" #endif #endif /* ------------------------------------------------------------------------------ END tlse.h ------------------------------------------------------------------------------ */ #ifndef HTTPS_NO_TLSE /* ------------------------------------------------------------------------------ BEGIN tlse.c ------------------------------------------------------------------------------ */ /******************************************************************************** Copyright (c) 2016-2023, Eduard Suica All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ********************************************************************************/ #ifndef TLSE_C #define TLSE_C #include #include #include #include #include #ifdef _WIN32 #ifdef SSL_COMPATIBLE_INTERFACE #include #endif #include #include #ifndef strcasecmp #define strcasecmp stricmp #endif #else // hton* and ntoh* functions #include #include #include #endif #ifdef TLS_AMALGAMATION #ifdef I #pragma push_macro("I") #define TLS_I_MACRO #undef I #endif //#include "libtomcrypt.c" #ifdef TLS_I_MACRO #pragma pop_macro("I") #undef TLS_I_MACRO #endif #else //#include #endif #if (CRYPT <= 0x0117) #define LTC_PKCS_1_EMSA LTC_LTC_PKCS_1_EMSA #define LTC_PKCS_1_V1_5 LTC_LTC_PKCS_1_V1_5 #define LTC_PKCS_1_PSS LTC_LTC_PKCS_1_PSS #endif #ifdef WITH_KTLS #include #include #include // should get /usr/include/linux/tls.h (linux headers) // rename it to ktls.h and add it to your project #include "ktls.h" // or just include tls.h instead of ktls.h // #include "linux/tls.h" #endif //#include "tlse.h" #ifdef TLS_CURVE25519 #include "curve25519.c" #endif // using ChaCha20 implementation by D. J. Bernstein #ifndef TLS_FORWARD_SECRECY #undef TLS_ECDSA_SUPPORTED #endif #ifndef TLS_ECDSA_SUPPORTED // disable client ECDSA if not supported #undef TLS_CLIENT_ECDSA #endif #define TLS_DH_DEFAULT_P "87A8E61DB4B6663CFFBBD19C651959998CEEF608660DD0F25D2CEED4435E3B00E00DF8F1D61957D4FAF7DF4561B2AA3016C3D91134096FAA3BF4296D830E9A7C209E0C6497517ABD5A8A9D306BCF67ED91F9E6725B4758C022E0B1EF4275BF7B6C5BFC11D45F9088B941F54EB1E59BB8BC39A0BF12307F5C4FDB70C581B23F76B63ACAE1CAA6B7902D52526735488A0EF13C6D9A51BFA4AB3AD8347796524D8EF6A167B5A41825D967E144E5140564251CCACB83E6B486F6B3CA3F7971506026C0B857F689962856DED4010ABD0BE621C3A3960A54E710C375F26375D7014103A4B54330C198AF126116D2276E11715F693877FAD7EF09CADB094AE91E1A1597" #define TLS_DH_DEFAULT_G "3FB32C9B73134D0B2E77506660EDBD484CA7B18F21EF205407F4793A1A0BA12510DBC15077BE463FFF4FED4AAC0BB555BE3A6C1B0C6B47B1BC3773BF7E8C6F62901228F8C28CBB18A55AE31341000A650196F931C77A57F2DDF463E5E9EC144B777DE62AAAB8A8628AC376D282D6ED3864E67982428EBC831D14348F6F2F9193B5045AF2767164E1DFC967C1FB3F2E55A4BD1BFFE83B9C80D052B985D182EA0ADB2A3B7313D3FE14C8484B1E052588B9B7D2BBD2DF016199ECD06E1557CD0915B3353BBB64E0EC377FD028370DF92B52C7891428CDC67EB6184B523D1DB246C32F63078490F00EF8D647D148D47954515E2327CFEF98C582664B4C0F6CC41659" #define TLS_DHE_KEY_SIZE 2048 // you should never use weak DH groups (1024 bits) // but if you have old devices (like grandstream ip phones) // that can't handle 2048bit DHE, uncomment next lines // and define TLS_WEAK_DH_LEGACY_DEVICES // #ifdef TLS_WEAK_DH_LEGACY_DEVICES // #define TLS_DH_DEFAULT_P "B10B8F96A080E01DDE92DE5EAE5D54EC52C99FBCFB06A3C69A6A9DCA52D23B616073E28675A23D189838EF1E2EE652C013ECB4AEA906112324975C3CD49B83BFACCBDD7D90C4BD7098488E9C219A73724EFFD6FAE5644738FAA31A4FF55BCCC0A151AF5F0DC8B4BD45BF37DF365C1A65E68CFDA76D4DA708DF1FB2BC2E4A4371" // #define TLS_DH_DEFAULT_G "A4D1CBD5C3FD34126765A442EFB99905F8104DD258AC507FD6406CFF14266D31266FEA1E5C41564B777E690F5504F213160217B4B01B886A5E91547F9E2749F4D7FBD7D3B9A92EE1909D0D2263F80A76A6A24C087A091F531DBF0A0169B6A28AD662A4D18E73AFA32D779D5918D08BC8858F4DCEF97C2A24855E6EEB22B3B2E5" // #define TLS_DHE_KEY_SIZE 1024 // #endif #ifndef TLS_MALLOC #define TLS_MALLOC MALLOC //< @r-lyeh #endif #ifndef TLS_REALLOC #define TLS_REALLOC REALLOC //< @r-lyeh #endif #ifndef TLS_FREE #define TLS_FREE FREE //< @r-lyeh #endif #define TLS_ERROR(err, statement) if (err) statement; #ifdef DEBUG #define DEBUG_PRINT(...) fprintf(stderr, __VA_ARGS__) #define DEBUG_DUMP_HEX(buf, len) {if (buf) { int _i_; for (_i_ = 0; _i_ < len; _i_++) { DEBUG_PRINT("%02X ", (unsigned int)(buf)[_i_]); } } else { fprintf(stderr, "(null)"); } } #define DEBUG_INDEX(fields) print_index(fields) #define DEBUG_DUMP(buf, length) fwrite(buf, 1, length, stderr); #define DEBUG_DUMP_HEX_LABEL(title, buf, len) {fprintf(stderr, "%s (%i): ", title, (int)len); DEBUG_DUMP_HEX(buf, len); fprintf(stderr, "\n");} #else #define DEBUG_PRINT(...) { } #define DEBUG_DUMP_HEX(buf, len) { } #define DEBUG_INDEX(fields) { } #define DEBUG_DUMP(buf, length) { } #define DEBUG_DUMP_HEX_LABEL(title, buf, len) { } #endif #ifndef htonll #define htonll(x) ((1==htonl(1)) ? (x) : ((uint64_t)htonl((x) & 0xFFFFFFFF) << 32) | htonl((x) >> 32)) #endif #ifndef ntohll #define ntohll(x) ((1==ntohl(1)) ? (x) : ((uint64_t)ntohl((x) & 0xFFFFFFFF) << 32) | ntohl((x) >> 32)) #endif #define TLS_CHANGE_CIPHER 0x14 #define TLS_ALERT 0x15 #define TLS_HANDSHAKE 0x16 #define TLS_APPLICATION_DATA 0x17 #define TLS_SERIALIZED_OBJECT 0xFE #define TLS_CLIENT_HELLO_MINSIZE 41 #define TLS_CLIENT_RANDOM_SIZE 32 #define TLS_SERVER_RANDOM_SIZE 32 #define TLS_MAX_SESSION_ID 32 #define TLS_SHA256_MAC_SIZE 32 #define TLS_SHA1_MAC_SIZE 20 #define TLS_SHA384_MAC_SIZE 48 #define TLS_MAX_MAC_SIZE TLS_SHA384_MAC_SIZE // 160 #define TLS_MAX_KEY_EXPANSION_SIZE 192 // 512bits (sha256) = 64 bytes #define TLS_MAX_HASH_LEN 64 #define TLS_AES_IV_LENGTH 16 #define TLS_AES_BLOCK_SIZE 16 #define TLS_AES_GCM_IV_LENGTH 4 #define TLS_13_AES_GCM_IV_LENGTH 12 #define TLS_GCM_TAG_LEN 16 #define TLS_MAX_TAG_LEN 16 #define TLS_MIN_FINISHED_OPAQUE_LEN 12 #define TLS_BLOB_INCREMENT 0xFFF #define TLS_ASN1_MAXLEVEL 0xFF #define DTLS_COOKIE_SIZE 32 #define TLS_MAX_SHA_SIZE 48 // 16(md5) + 20(sha1) #define TLS_V11_HASH_SIZE 36 #define TLS_MAX_HASH_SIZE TLS_MAX_SHA_SIZE // 16(md5) + 20(sha1) #define TLS_MAX_RSA_KEY 2048 #define TLS_MAXTLS_APP_SIZE 0x4000 // max 1 second sleep #define TLS_MAX_ERROR_SLEEP_uS 1000000 // max 5 seconds context sleep #define TLS_MAX_ERROR_IDLE_S 5 #define TLS_V13_MAX_KEY_SIZE 32 #define TLS_V13_MAX_IV_SIZE 12 #define VERSION_SUPPORTED(version, err) if ((version != TLS_V13) && (version != TLS_V12) && (version != TLS_V11) && (version != TLS_V10) && (version != DTLS_V13) && (version != DTLS_V12) && (version != DTLS_V10)) { if ((version == SSL_V30) && (context->connection_status == 0)) { version = TLS_V12; } else { DEBUG_PRINT("UNSUPPORTED TLS VERSION %x\n", (int)version); return err;} } #define CHECK_SIZE(size, buf_size, err) if (((int)(size) > (int)(buf_size)) || ((int)(buf_size) < 0)) return err; #define TLS_IMPORT_CHECK_SIZE(buf_pos, size, buf_size) if (((int)size > (int)buf_size - buf_pos) || ((int)buf_pos > (int)buf_size)) { DEBUG_PRINT("IMPORT ELEMENT SIZE ERROR\n"); tls_destroy_context(context); return NULL; } #define CHECK_HANDSHAKE_STATE(context, n, limit) { if (context->hs_messages[n] >= limit) { DEBUG_PRINT("* UNEXPECTED MESSAGE (%i)\n", (int)n); payload_res = TLS_UNEXPECTED_MESSAGE; break; } context->hs_messages[n]++; } #if CRYPT > 0x0118 #define TLS_TOMCRYPT_PRIVATE_DP(key) ((key).dp) #define TLS_TOMCRYPT_PRIVATE_SET_INDEX(key, k_idx) #else #define TLS_TOMCRYPT_PRIVATE_DP(key) ((key)->dp) #define TLS_TOMCRYPT_PRIVATE_SET_INDEX(key, k_idx) key->idx = k_idx #endif #ifdef TLS_WITH_CHACHA20_POLY1305 #define TLS_CHACHA20_IV_LENGTH 12 // ChaCha20 implementation by D. J. Bernstein // Public domain. #define CHACHA_MINKEYLEN 16 #define CHACHA_NONCELEN 8 #define CHACHA_NONCELEN_96 12 #define CHACHA_CTRLEN 8 #define CHACHA_CTRLEN_96 4 #define CHACHA_STATELEN (CHACHA_NONCELEN+CHACHA_CTRLEN) #define CHACHA_BLOCKLEN 64 #define POLY1305_MAX_AAD 32 #define POLY1305_KEYLEN 32 #define POLY1305_TAGLEN 16 #define u_int unsigned int #define uint8_t unsigned char #define u_char unsigned char #ifndef NULL #define NULL (void *)0 #endif #if (CRYPT >= 0x0117) && (0) // to do: use ltc chacha/poly1305 implementation (working on big-endian machines) #define chacha_ctx chacha20poly1305_state #define poly1305_context poly1305_state #define _private_tls_poly1305_init(ctx, key, len) poly1305_init(ctx, key, len) #define _private_tls_poly1305_update(ctx, in, len) poly1305_process(ctx, in, len) #define _private_tls_poly1305_finish(ctx, mac) poly1305_done(ctx, mac, 16) #else struct chacha_ctx { u_int input[16]; uint8_t ks[CHACHA_BLOCKLEN]; uint8_t unused; }; static inline void chacha_keysetup(struct chacha_ctx *x, const u_char *k, u_int kbits); static inline void chacha_ivsetup(struct chacha_ctx *x, const u_char *iv, const u_char *ctr); static inline void chacha_ivsetup_96bitnonce(struct chacha_ctx *x, const u_char *iv, const u_char *ctr); static inline void chacha_encrypt_bytes(struct chacha_ctx *x, const u_char *m, u_char *c, u_int bytes); static inline int poly1305_generate_key(unsigned char *key256, unsigned char *nonce, unsigned int noncelen, unsigned char *poly_key, unsigned int counter); #define poly1305_block_size 16 #define poly1305_context poly1305_state_internal_t //========== ChaCha20 from D. J. Bernstein ========= // // Source available at https://cr.yp.to/chacha.html // typedef unsigned char u8; typedef unsigned int u32; typedef struct chacha_ctx chacha_ctx; #define U8C(v) (v##U) #define U32C(v) (v##U) #define U8V(v) ((u8)(v) & U8C(0xFF)) #define U32V(v) ((u32)(v) & U32C(0xFFFFFFFF)) #define ROTL32(v, n) \ (U32V((v) << (n)) | ((v) >> (32 - (n)))) #define _private_tls_U8TO32_LITTLE(p) \ (((u32)((p)[0])) | \ ((u32)((p)[1]) << 8) | \ ((u32)((p)[2]) << 16) | \ ((u32)((p)[3]) << 24)) #define _private_tls_U32TO8_LITTLE(p, v) \ do { \ (p)[0] = U8V((v)); \ (p)[1] = U8V((v) >> 8); \ (p)[2] = U8V((v) >> 16); \ (p)[3] = U8V((v) >> 24); \ } while (0) #define ROTATE(v,c) (ROTL32(v,c)) #define XOR(v,w) ((v) ^ (w)) #define PLUS(v,w) (U32V((v) + (w))) #define PLUSONE(v) (PLUS((v),1)) #define QUARTERROUND(a,b,c,d) \ a = PLUS(a,b); d = ROTATE(XOR(d,a),16); \ c = PLUS(c,d); b = ROTATE(XOR(b,c),12); \ a = PLUS(a,b); d = ROTATE(XOR(d,a), 8); \ c = PLUS(c,d); b = ROTATE(XOR(b,c), 7); static const char sigma[] = "expand 32-byte k"; static const char tau[] = "expand 16-byte k"; static inline void chacha_keysetup(chacha_ctx *x, const u8 *k, u32 kbits) { const char *constants; x->input[4] = _private_tls_U8TO32_LITTLE(k + 0); x->input[5] = _private_tls_U8TO32_LITTLE(k + 4); x->input[6] = _private_tls_U8TO32_LITTLE(k + 8); x->input[7] = _private_tls_U8TO32_LITTLE(k + 12); if (kbits == 256) { /* recommended */ k += 16; constants = sigma; } else { /* kbits == 128 */ constants = tau; } x->input[8] = _private_tls_U8TO32_LITTLE(k + 0); x->input[9] = _private_tls_U8TO32_LITTLE(k + 4); x->input[10] = _private_tls_U8TO32_LITTLE(k + 8); x->input[11] = _private_tls_U8TO32_LITTLE(k + 12); x->input[0] = _private_tls_U8TO32_LITTLE(constants + 0); x->input[1] = _private_tls_U8TO32_LITTLE(constants + 4); x->input[2] = _private_tls_U8TO32_LITTLE(constants + 8); x->input[3] = _private_tls_U8TO32_LITTLE(constants + 12); } static inline void chacha_key(chacha_ctx *x, u8 *k) { _private_tls_U32TO8_LITTLE(k, x->input[4]); _private_tls_U32TO8_LITTLE(k + 4, x->input[5]); _private_tls_U32TO8_LITTLE(k + 8, x->input[6]); _private_tls_U32TO8_LITTLE(k + 12, x->input[7]); _private_tls_U32TO8_LITTLE(k + 16, x->input[8]); _private_tls_U32TO8_LITTLE(k + 20, x->input[9]); _private_tls_U32TO8_LITTLE(k + 24, x->input[10]); _private_tls_U32TO8_LITTLE(k + 28, x->input[11]); } static inline void chacha_nonce(chacha_ctx *x, u8 *nonce) { _private_tls_U32TO8_LITTLE(nonce + 0, x->input[13]); _private_tls_U32TO8_LITTLE(nonce + 4, x->input[14]); _private_tls_U32TO8_LITTLE(nonce + 8, x->input[15]); } static inline void chacha_ivsetup(chacha_ctx *x, const u8 *iv, const u8 *counter) { x->input[12] = counter == NULL ? 0 : _private_tls_U8TO32_LITTLE(counter + 0); x->input[13] = counter == NULL ? 0 : _private_tls_U8TO32_LITTLE(counter + 4); if (iv) { x->input[14] = _private_tls_U8TO32_LITTLE(iv + 0); x->input[15] = _private_tls_U8TO32_LITTLE(iv + 4); } } static inline void chacha_ivsetup_96bitnonce(chacha_ctx *x, const u8 *iv, const u8 *counter) { x->input[12] = counter == NULL ? 0 : _private_tls_U8TO32_LITTLE(counter + 0); if (iv) { x->input[13] = _private_tls_U8TO32_LITTLE(iv + 0); x->input[14] = _private_tls_U8TO32_LITTLE(iv + 4); x->input[15] = _private_tls_U8TO32_LITTLE(iv + 8); } } static inline void chacha_ivupdate(chacha_ctx *x, const u8 *iv, const u8 *aad, const u8 *counter) { x->input[12] = counter == NULL ? 0 : _private_tls_U8TO32_LITTLE(counter + 0); x->input[13] = _private_tls_U8TO32_LITTLE(iv + 0); x->input[14] = _private_tls_U8TO32_LITTLE(iv + 4) ^ _private_tls_U8TO32_LITTLE(aad); x->input[15] = _private_tls_U8TO32_LITTLE(iv + 8) ^ _private_tls_U8TO32_LITTLE(aad + 4); } static inline void chacha_encrypt_bytes(chacha_ctx *x, const u8 *m, u8 *c, u32 bytes) { u32 x0, x1, x2, x3, x4, x5, x6, x7; u32 x8, x9, x10, x11, x12, x13, x14, x15; u32 j0, j1, j2, j3, j4, j5, j6, j7; u32 j8, j9, j10, j11, j12, j13, j14, j15; u8 *ctarget = NULL; u8 tmp[64]; u_int i; if (!bytes) return; j0 = x->input[0]; j1 = x->input[1]; j2 = x->input[2]; j3 = x->input[3]; j4 = x->input[4]; j5 = x->input[5]; j6 = x->input[6]; j7 = x->input[7]; j8 = x->input[8]; j9 = x->input[9]; j10 = x->input[10]; j11 = x->input[11]; j12 = x->input[12]; j13 = x->input[13]; j14 = x->input[14]; j15 = x->input[15]; for (;;) { if (bytes < 64) { for (i = 0; i < bytes; ++i) tmp[i] = m[i]; m = tmp; ctarget = c; c = tmp; } x0 = j0; x1 = j1; x2 = j2; x3 = j3; x4 = j4; x5 = j5; x6 = j6; x7 = j7; x8 = j8; x9 = j9; x10 = j10; x11 = j11; x12 = j12; x13 = j13; x14 = j14; x15 = j15; for (i = 20; i > 0; i -= 2) { QUARTERROUND(x0, x4, x8, x12) QUARTERROUND(x1, x5, x9, x13) QUARTERROUND(x2, x6, x10, x14) QUARTERROUND(x3, x7, x11, x15) QUARTERROUND(x0, x5, x10, x15) QUARTERROUND(x1, x6, x11, x12) QUARTERROUND(x2, x7, x8, x13) QUARTERROUND(x3, x4, x9, x14) } x0 = PLUS(x0, j0); x1 = PLUS(x1, j1); x2 = PLUS(x2, j2); x3 = PLUS(x3, j3); x4 = PLUS(x4, j4); x5 = PLUS(x5, j5); x6 = PLUS(x6, j6); x7 = PLUS(x7, j7); x8 = PLUS(x8, j8); x9 = PLUS(x9, j9); x10 = PLUS(x10, j10); x11 = PLUS(x11, j11); x12 = PLUS(x12, j12); x13 = PLUS(x13, j13); x14 = PLUS(x14, j14); x15 = PLUS(x15, j15); if (bytes < 64) { _private_tls_U32TO8_LITTLE(x->ks + 0, x0); _private_tls_U32TO8_LITTLE(x->ks + 4, x1); _private_tls_U32TO8_LITTLE(x->ks + 8, x2); _private_tls_U32TO8_LITTLE(x->ks + 12, x3); _private_tls_U32TO8_LITTLE(x->ks + 16, x4); _private_tls_U32TO8_LITTLE(x->ks + 20, x5); _private_tls_U32TO8_LITTLE(x->ks + 24, x6); _private_tls_U32TO8_LITTLE(x->ks + 28, x7); _private_tls_U32TO8_LITTLE(x->ks + 32, x8); _private_tls_U32TO8_LITTLE(x->ks + 36, x9); _private_tls_U32TO8_LITTLE(x->ks + 40, x10); _private_tls_U32TO8_LITTLE(x->ks + 44, x11); _private_tls_U32TO8_LITTLE(x->ks + 48, x12); _private_tls_U32TO8_LITTLE(x->ks + 52, x13); _private_tls_U32TO8_LITTLE(x->ks + 56, x14); _private_tls_U32TO8_LITTLE(x->ks + 60, x15); } x0 = XOR(x0, _private_tls_U8TO32_LITTLE(m + 0)); x1 = XOR(x1, _private_tls_U8TO32_LITTLE(m + 4)); x2 = XOR(x2, _private_tls_U8TO32_LITTLE(m + 8)); x3 = XOR(x3, _private_tls_U8TO32_LITTLE(m + 12)); x4 = XOR(x4, _private_tls_U8TO32_LITTLE(m + 16)); x5 = XOR(x5, _private_tls_U8TO32_LITTLE(m + 20)); x6 = XOR(x6, _private_tls_U8TO32_LITTLE(m + 24)); x7 = XOR(x7, _private_tls_U8TO32_LITTLE(m + 28)); x8 = XOR(x8, _private_tls_U8TO32_LITTLE(m + 32)); x9 = XOR(x9, _private_tls_U8TO32_LITTLE(m + 36)); x10 = XOR(x10, _private_tls_U8TO32_LITTLE(m + 40)); x11 = XOR(x11, _private_tls_U8TO32_LITTLE(m + 44)); x12 = XOR(x12, _private_tls_U8TO32_LITTLE(m + 48)); x13 = XOR(x13, _private_tls_U8TO32_LITTLE(m + 52)); x14 = XOR(x14, _private_tls_U8TO32_LITTLE(m + 56)); x15 = XOR(x15, _private_tls_U8TO32_LITTLE(m + 60)); j12 = PLUSONE(j12); if (!j12) { j13 = PLUSONE(j13); /* * Stopping at 2^70 bytes per nonce is the user's * responsibility. */ } _private_tls_U32TO8_LITTLE(c + 0, x0); _private_tls_U32TO8_LITTLE(c + 4, x1); _private_tls_U32TO8_LITTLE(c + 8, x2); _private_tls_U32TO8_LITTLE(c + 12, x3); _private_tls_U32TO8_LITTLE(c + 16, x4); _private_tls_U32TO8_LITTLE(c + 20, x5); _private_tls_U32TO8_LITTLE(c + 24, x6); _private_tls_U32TO8_LITTLE(c + 28, x7); _private_tls_U32TO8_LITTLE(c + 32, x8); _private_tls_U32TO8_LITTLE(c + 36, x9); _private_tls_U32TO8_LITTLE(c + 40, x10); _private_tls_U32TO8_LITTLE(c + 44, x11); _private_tls_U32TO8_LITTLE(c + 48, x12); _private_tls_U32TO8_LITTLE(c + 52, x13); _private_tls_U32TO8_LITTLE(c + 56, x14); _private_tls_U32TO8_LITTLE(c + 60, x15); if (bytes <= 64) { if (bytes < 64) { for (i = 0; i < bytes; ++i) ctarget[i] = c[i]; } x->input[12] = j12; x->input[13] = j13; x->unused = 64 - bytes; return; } bytes -= 64; c += 64; m += 64; } } static inline void chacha20_block(chacha_ctx *x, unsigned char *c, u_int len) { u_int i; unsigned int state[16]; for (i = 0; i < 16; i++) state[i] = x->input[i]; for (i = 20; i > 0; i -= 2) { QUARTERROUND(state[0], state[4], state[8], state[12]) QUARTERROUND(state[1], state[5], state[9], state[13]) QUARTERROUND(state[2], state[6], state[10], state[14]) QUARTERROUND(state[3], state[7], state[11], state[15]) QUARTERROUND(state[0], state[5], state[10], state[15]) QUARTERROUND(state[1], state[6], state[11], state[12]) QUARTERROUND(state[2], state[7], state[8], state[13]) QUARTERROUND(state[3], state[4], state[9], state[14]) } for (i = 0; i < 16; i++) x->input[i] = PLUS(x->input[i], state[i]); for (i = 0; i < len; i += 4) { _private_tls_U32TO8_LITTLE(c + i, x->input[i/4]); } } static inline int poly1305_generate_key(unsigned char *key256, unsigned char *nonce, unsigned int noncelen, unsigned char *poly_key, unsigned int counter) { struct chacha_ctx ctx; uint64_t ctr; memset(&ctx, 0, sizeof(ctx)); chacha_keysetup(&ctx, key256, 256); switch (noncelen) { case 8: ctr = counter; chacha_ivsetup(&ctx, nonce, (unsigned char *)&ctr); break; case 12: chacha_ivsetup_96bitnonce(&ctx, nonce, (unsigned char *)&counter); break; default: return -1; } chacha20_block(&ctx, poly_key, POLY1305_KEYLEN); return 0; } /* 17 + sizeof(size_t) + 14*sizeof(unsigned long) */ typedef struct poly1305_state_internal_t { unsigned long r[5]; unsigned long h[5]; unsigned long pad[4]; size_t leftover; unsigned char buffer[poly1305_block_size]; unsigned char final; } poly1305_state_internal_t; /* interpret four 8 bit unsigned integers as a 32 bit unsigned integer in little endian */ static unsigned long _private_tls_U8TO32(const unsigned char *p) { return (((unsigned long)(p[0] & 0xff) ) | ((unsigned long)(p[1] & 0xff) << 8) | ((unsigned long)(p[2] & 0xff) << 16) | ((unsigned long)(p[3] & 0xff) << 24)); } /* store a 32 bit unsigned integer as four 8 bit unsigned integers in little endian */ static void _private_tls_U32TO8(unsigned char *p, unsigned long v) { p[0] = (v ) & 0xff; p[1] = (v >> 8) & 0xff; p[2] = (v >> 16) & 0xff; p[3] = (v >> 24) & 0xff; } void _private_tls_poly1305_init(poly1305_context *ctx, const unsigned char key[32]) { poly1305_state_internal_t *st = (poly1305_state_internal_t *)ctx; /* r &= 0xffffffc0ffffffc0ffffffc0fffffff */ st->r[0] = (_private_tls_U8TO32(&key[ 0]) ) & 0x3ffffff; st->r[1] = (_private_tls_U8TO32(&key[ 3]) >> 2) & 0x3ffff03; st->r[2] = (_private_tls_U8TO32(&key[ 6]) >> 4) & 0x3ffc0ff; st->r[3] = (_private_tls_U8TO32(&key[ 9]) >> 6) & 0x3f03fff; st->r[4] = (_private_tls_U8TO32(&key[12]) >> 8) & 0x00fffff; /* h = 0 */ st->h[0] = 0; st->h[1] = 0; st->h[2] = 0; st->h[3] = 0; st->h[4] = 0; /* save pad for later */ st->pad[0] = _private_tls_U8TO32(&key[16]); st->pad[1] = _private_tls_U8TO32(&key[20]); st->pad[2] = _private_tls_U8TO32(&key[24]); st->pad[3] = _private_tls_U8TO32(&key[28]); st->leftover = 0; st->final = 0; } static void _private_tls_poly1305_blocks(poly1305_state_internal_t *st, const unsigned char *m, size_t bytes) { const unsigned long hibit = (st->final) ? 0 : (1UL << 24); /* 1 << 128 */ unsigned long r0,r1,r2,r3,r4; unsigned long s1,s2,s3,s4; unsigned long h0,h1,h2,h3,h4; unsigned long long d0,d1,d2,d3,d4; unsigned long c; r0 = st->r[0]; r1 = st->r[1]; r2 = st->r[2]; r3 = st->r[3]; r4 = st->r[4]; s1 = r1 * 5; s2 = r2 * 5; s3 = r3 * 5; s4 = r4 * 5; h0 = st->h[0]; h1 = st->h[1]; h2 = st->h[2]; h3 = st->h[3]; h4 = st->h[4]; while (bytes >= poly1305_block_size) { /* h += m[i] */ h0 += (_private_tls_U8TO32(m+ 0) ) & 0x3ffffff; h1 += (_private_tls_U8TO32(m+ 3) >> 2) & 0x3ffffff; h2 += (_private_tls_U8TO32(m+ 6) >> 4) & 0x3ffffff; h3 += (_private_tls_U8TO32(m+ 9) >> 6) & 0x3ffffff; h4 += (_private_tls_U8TO32(m+12) >> 8) | hibit; /* h *= r */ d0 = ((unsigned long long)h0 * r0) + ((unsigned long long)h1 * s4) + ((unsigned long long)h2 * s3) + ((unsigned long long)h3 * s2) + ((unsigned long long)h4 * s1); d1 = ((unsigned long long)h0 * r1) + ((unsigned long long)h1 * r0) + ((unsigned long long)h2 * s4) + ((unsigned long long)h3 * s3) + ((unsigned long long)h4 * s2); d2 = ((unsigned long long)h0 * r2) + ((unsigned long long)h1 * r1) + ((unsigned long long)h2 * r0) + ((unsigned long long)h3 * s4) + ((unsigned long long)h4 * s3); d3 = ((unsigned long long)h0 * r3) + ((unsigned long long)h1 * r2) + ((unsigned long long)h2 * r1) + ((unsigned long long)h3 * r0) + ((unsigned long long)h4 * s4); d4 = ((unsigned long long)h0 * r4) + ((unsigned long long)h1 * r3) + ((unsigned long long)h2 * r2) + ((unsigned long long)h3 * r1) + ((unsigned long long)h4 * r0); /* (partial) h %= p */ c = (unsigned long)(d0 >> 26); h0 = (unsigned long)d0 & 0x3ffffff; d1 += c; c = (unsigned long)(d1 >> 26); h1 = (unsigned long)d1 & 0x3ffffff; d2 += c; c = (unsigned long)(d2 >> 26); h2 = (unsigned long)d2 & 0x3ffffff; d3 += c; c = (unsigned long)(d3 >> 26); h3 = (unsigned long)d3 & 0x3ffffff; d4 += c; c = (unsigned long)(d4 >> 26); h4 = (unsigned long)d4 & 0x3ffffff; h0 += c * 5; c = (h0 >> 26); h0 = h0 & 0x3ffffff; h1 += c; m += poly1305_block_size; bytes -= poly1305_block_size; } st->h[0] = h0; st->h[1] = h1; st->h[2] = h2; st->h[3] = h3; st->h[4] = h4; } void _private_tls_poly1305_finish(poly1305_context *ctx, unsigned char mac[16]) { poly1305_state_internal_t *st = (poly1305_state_internal_t *)ctx; unsigned long h0,h1,h2,h3,h4,c; unsigned long g0,g1,g2,g3,g4; unsigned long long f; unsigned long mask; /* process the remaining block */ if (st->leftover) { size_t i = st->leftover; st->buffer[i++] = 1; for (; i < poly1305_block_size; i++) st->buffer[i] = 0; st->final = 1; _private_tls_poly1305_blocks(st, st->buffer, poly1305_block_size); } /* fully carry h */ h0 = st->h[0]; h1 = st->h[1]; h2 = st->h[2]; h3 = st->h[3]; h4 = st->h[4]; c = h1 >> 26; h1 = h1 & 0x3ffffff; h2 += c; c = h2 >> 26; h2 = h2 & 0x3ffffff; h3 += c; c = h3 >> 26; h3 = h3 & 0x3ffffff; h4 += c; c = h4 >> 26; h4 = h4 & 0x3ffffff; h0 += c * 5; c = h0 >> 26; h0 = h0 & 0x3ffffff; h1 += c; /* compute h + -p */ g0 = h0 + 5; c = g0 >> 26; g0 &= 0x3ffffff; g1 = h1 + c; c = g1 >> 26; g1 &= 0x3ffffff; g2 = h2 + c; c = g2 >> 26; g2 &= 0x3ffffff; g3 = h3 + c; c = g3 >> 26; g3 &= 0x3ffffff; g4 = h4 + c - (1UL << 26); /* select h if h < p, or h + -p if h >= p */ mask = (g4 >> ((sizeof(unsigned long) * 8) - 1)) - 1; g0 &= mask; g1 &= mask; g2 &= mask; g3 &= mask; g4 &= mask; mask = ~mask; h0 = (h0 & mask) | g0; h1 = (h1 & mask) | g1; h2 = (h2 & mask) | g2; h3 = (h3 & mask) | g3; h4 = (h4 & mask) | g4; /* h = h % (2^128) */ h0 = ((h0 ) | (h1 << 26)) & 0xffffffff; h1 = ((h1 >> 6) | (h2 << 20)) & 0xffffffff; h2 = ((h2 >> 12) | (h3 << 14)) & 0xffffffff; h3 = ((h3 >> 18) | (h4 << 8)) & 0xffffffff; /* mac = (h + pad) % (2^128) */ f = (unsigned long long)h0 + st->pad[0] ; h0 = (unsigned long)f; f = (unsigned long long)h1 + st->pad[1] + (f >> 32); h1 = (unsigned long)f; f = (unsigned long long)h2 + st->pad[2] + (f >> 32); h2 = (unsigned long)f; f = (unsigned long long)h3 + st->pad[3] + (f >> 32); h3 = (unsigned long)f; _private_tls_U32TO8(mac + 0, h0); _private_tls_U32TO8(mac + 4, h1); _private_tls_U32TO8(mac + 8, h2); _private_tls_U32TO8(mac + 12, h3); /* zero out the state */ st->h[0] = 0; st->h[1] = 0; st->h[2] = 0; st->h[3] = 0; st->h[4] = 0; st->r[0] = 0; st->r[1] = 0; st->r[2] = 0; st->r[3] = 0; st->r[4] = 0; st->pad[0] = 0; st->pad[1] = 0; st->pad[2] = 0; st->pad[3] = 0; } void _private_tls_poly1305_update(poly1305_context *ctx, const unsigned char *m, size_t bytes) { poly1305_state_internal_t *st = (poly1305_state_internal_t *)ctx; size_t i; /* handle leftover */ if (st->leftover) { size_t want = (poly1305_block_size - st->leftover); if (want > bytes) want = bytes; for (i = 0; i < want; i++) st->buffer[st->leftover + i] = m[i]; bytes -= want; m += want; st->leftover += want; if (st->leftover < poly1305_block_size) return; _private_tls_poly1305_blocks(st, st->buffer, poly1305_block_size); st->leftover = 0; } /* process full blocks */ if (bytes >= poly1305_block_size) { size_t want = (bytes & ~(poly1305_block_size - 1)); _private_tls_poly1305_blocks(st, m, want); m += want; bytes -= want; } /* store leftover */ if (bytes) { for (i = 0; i < bytes; i++) st->buffer[st->leftover + i] = m[i]; st->leftover += bytes; } } int poly1305_verify(const unsigned char mac1[16], const unsigned char mac2[16]) { size_t i; unsigned int dif = 0; for (i = 0; i < 16; i++) dif |= (mac1[i] ^ mac2[i]); dif = (dif - 1) >> ((sizeof(unsigned int) * 8) - 1); return (dif & 1); } void chacha20_poly1305_key(struct chacha_ctx *ctx, unsigned char *poly1305_key) { unsigned char key[32]; unsigned char nonce[12]; chacha_key(ctx, key); chacha_nonce(ctx, nonce); poly1305_generate_key(key, nonce, sizeof(nonce), poly1305_key, 0); } int chacha20_poly1305_aead(struct chacha_ctx *ctx, unsigned char *pt, unsigned int len, unsigned char *aad, unsigned int aad_len, unsigned char *poly_key, unsigned char *out) { static unsigned char zeropad[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; if (aad_len > POLY1305_MAX_AAD) return -1; unsigned int counter = 1; chacha_ivsetup_96bitnonce(ctx, NULL, (unsigned char *)&counter); chacha_encrypt_bytes(ctx, pt, out, len); poly1305_context aead_ctx; _private_tls_poly1305_init(&aead_ctx, poly_key); _private_tls_poly1305_update(&aead_ctx, aad, aad_len); int rem = aad_len % 16; if (rem) _private_tls_poly1305_update(&aead_ctx, zeropad, 16 - rem); _private_tls_poly1305_update(&aead_ctx, out, len); rem = len % 16; if (rem) _private_tls_poly1305_update(&aead_ctx, zeropad, 16 - rem); unsigned char trail[16]; _private_tls_U32TO8(trail, aad_len); *(int *)(trail + 4) = 0; _private_tls_U32TO8(trail + 8, len); *(int *)(trail + 12) = 0; _private_tls_poly1305_update(&aead_ctx, trail, 16); _private_tls_poly1305_finish(&aead_ctx, out + len); return len + POLY1305_TAGLEN; } #endif #endif typedef enum { KEA_dhe_dss, KEA_dhe_rsa, KEA_dh_anon, KEA_rsa, KEA_dh_dss, KEA_dh_rsa, KEA_ec_diffie_hellman } KeyExchangeAlgorithm; typedef enum { rsa_sign = 1, dss_sign = 2, rsa_fixed_dh = 3, dss_fixed_dh = 4, rsa_ephemeral_dh_RESERVED = 5, dss_ephemeral_dh_RESERVED = 6, fortezza_dms_RESERVED = 20, ecdsa_sign = 64, rsa_fixed_ecdh = 65, ecdsa_fixed_ecdh = 66 } TLSClientCertificateType; typedef enum { none = 0, md5 = 1, sha1 = 2, sha224 = 3, sha256 = 4, sha384 = 5, sha512 = 6, _md5_sha1 = 255 } TLSHashAlgorithm; typedef enum { anonymous = 0, rsa = 1, dsa = 2, ecdsa = 3 } TLSSignatureAlgorithm; struct _private_OID_chain { void *top; unsigned char *oid; }; struct TLSCertificate { unsigned short version; unsigned int algorithm; unsigned int key_algorithm; unsigned int ec_algorithm; unsigned char *exponent; unsigned int exponent_len; unsigned char *pk; unsigned int pk_len; unsigned char *priv; unsigned int priv_len; unsigned char *issuer_country; unsigned char *issuer_state; unsigned char *issuer_location; unsigned char *issuer_entity; unsigned char *issuer_subject; unsigned char *not_before; unsigned char *not_after; unsigned char *country; unsigned char *state; unsigned char *location; unsigned char *entity; unsigned char *subject; unsigned char **san; unsigned short san_length; unsigned char *ocsp; unsigned char *serial_number; unsigned int serial_len; unsigned char *sign_key; unsigned int sign_len; unsigned char *fingerprint; unsigned char *der_bytes; unsigned int der_len; unsigned char *bytes; unsigned int len; }; typedef struct { union { symmetric_CBC aes_local; gcm_state aes_gcm_local; #ifdef TLS_WITH_CHACHA20_POLY1305 chacha_ctx chacha_local; #endif } ctx_local; union { symmetric_CBC aes_remote; gcm_state aes_gcm_remote; #ifdef TLS_WITH_CHACHA20_POLY1305 chacha_ctx chacha_remote; #endif } ctx_remote; union { unsigned char local_mac[TLS_MAX_MAC_SIZE]; unsigned char local_aead_iv[TLS_AES_GCM_IV_LENGTH]; #ifdef WITH_TLS_13 unsigned char local_iv[TLS_13_AES_GCM_IV_LENGTH]; #endif #ifdef TLS_WITH_CHACHA20_POLY1305 unsigned char local_nonce[TLS_CHACHA20_IV_LENGTH]; #endif } ctx_local_mac; union { unsigned char remote_aead_iv[TLS_AES_GCM_IV_LENGTH]; unsigned char remote_mac[TLS_MAX_MAC_SIZE]; #ifdef WITH_TLS_13 unsigned char remote_iv[TLS_13_AES_GCM_IV_LENGTH]; #endif #ifdef TLS_WITH_CHACHA20_POLY1305 unsigned char remote_nonce[TLS_CHACHA20_IV_LENGTH]; #endif } ctx_remote_mac; unsigned char created; } TLSCipher; typedef struct { hash_state hash32; hash_state hash48; #ifdef TLS_LEGACY_SUPPORT hash_state hash2; #endif unsigned char created; } TLSHash; #ifdef TLS_FORWARD_SECRECY #define mp_init(a) ltc_mp.init(a) #define mp_init_multi ltc_init_multi #define mp_clear(a) ltc_mp.deinit(a) #define mp_clear_multi ltc_deinit_multi #define mp_count_bits(a) ltc_mp.count_bits(a) #define mp_read_radix(a, b, c) ltc_mp.read_radix(a, b, c) #define mp_unsigned_bin_size(a) ltc_mp.unsigned_size(a) #define mp_to_unsigned_bin(a, b) ltc_mp.unsigned_write(a, b) #define mp_read_unsigned_bin(a, b, c) ltc_mp.unsigned_read(a, b, c) #define mp_exptmod(a, b, c, d) ltc_mp.exptmod(a, b, c, d) #define mp_add(a, b, c) ltc_mp.add(a, b, c) #define mp_mul(a, b, c) ltc_mp.mul(a, b, c) #define mp_cmp(a, b) ltc_mp.compare(a, b) #define mp_cmp_d(a, b) ltc_mp.compare_d(a, b) #define mp_sqr(a, b) ltc_mp.sqr(a, b) #define mp_mod(a, b, c) ltc_mp.mpdiv(a, b, NULL, c) #define mp_sub(a, b, c) ltc_mp.sub(a, b, c) #define mp_set(a, b) ltc_mp.set_int(a, b) typedef struct { int iana; void *x; void *y; void *p; void *g; } DHKey; #ifdef WITH_TLS_13 static DHKey ffdhe2048 = { 0x0100, NULL, NULL, (void *)"FFFFFFFFFFFFFFFFADF85458A2BB4A9AAFDC5620273D3CF1D8B9C583CE2D3695A9E13641146433FBCC939DCE249B3EF97D2FE363630C75D8F681B202AEC4617AD3DF1ED5D5FD65612433F51F5F066ED0856365553DED1AF3B557135E7F57C935984F0C70E0E68B77E2A689DAF3EFE8721DF158A136ADE73530ACCA4F483A797ABC0AB182B324FB61D108A94BB2C8E3FBB96ADAB760D7F4681D4F42A3DE394DF4AE56EDE76372BB190B07A7C8EE0A6D709E02FCE1CDF7E2ECC03404CD28342F619172FE9CE98583FF8E4F1232EEF28183C3FE3B1B4C6FAD733BB5FCBC2EC22005C58EF1837D1683B2C6F34A26C1B2EFFA886B423861285C97FFFFFFFFFFFFFFFF", (void *)"00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002" }; static DHKey ffdhe3072 = { 0x0101, NULL, NULL, (void *)"FFFFFFFFFFFFFFFFADF85458A2BB4A9AAFDC5620273D3CF1D8B9C583CE2D3695A9E13641146433FBCC939DCE249B3EF97D2FE363630C75D8F681B202AEC4617AD3DF1ED5D5FD65612433F51F5F066ED0856365553DED1AF3B557135E7F57C935984F0C70E0E68B77E2A689DAF3EFE8721DF158A136ADE73530ACCA4F483A797ABC0AB182B324FB61D108A94BB2C8E3FBB96ADAB760D7F4681D4F42A3DE394DF4AE56EDE76372BB190B07A7C8EE0A6D709E02FCE1CDF7E2ECC03404CD28342F619172FE9CE98583FF8E4F1232EEF28183C3FE3B1B4C6FAD733BB5FCBC2EC22005C58EF1837D1683B2C6F34A26C1B2EFFA886B4238611FCFDCDE355B3B6519035BBC34F4DEF99C023861B46FC9D6E6C9077AD91D2691F7F7EE598CB0FAC186D91CAEFE130985139270B4130C93BC437944F4FD4452E2D74DD364F2E21E71F54BFF5CAE82AB9C9DF69EE86D2BC522363A0DABC521979B0DEADA1DBF9A42D5C4484E0ABCD06BFA53DDEF3C1B20EE3FD59D7C25E41D2B66C62E37FFFFFFFFFFFFFFFF", (void *)"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002" }; static DHKey ffdhe4096 = { 0x0102, NULL, NULL, (void *)"FFFFFFFFFFFFFFFFADF85458A2BB4A9AAFDC5620273D3CF1D8B9C583CE2D3695A9E13641146433FBCC939DCE249B3EF97D2FE363630C75D8F681B202AEC4617AD3DF1ED5D5FD65612433F51F5F066ED0856365553DED1AF3B557135E7F57C935984F0C70E0E68B77E2A689DAF3EFE8721DF158A136ADE73530ACCA4F483A797ABC0AB182B324FB61D108A94BB2C8E3FBB96ADAB760D7F4681D4F42A3DE394DF4AE56EDE76372BB190B07A7C8EE0A6D709E02FCE1CDF7E2ECC03404CD28342F619172FE9CE98583FF8E4F1232EEF28183C3FE3B1B4C6FAD733BB5FCBC2EC22005C58EF1837D1683B2C6F34A26C1B2EFFA886B4238611FCFDCDE355B3B6519035BBC34F4DEF99C023861B46FC9D6E6C9077AD91D2691F7F7EE598CB0FAC186D91CAEFE130985139270B4130C93BC437944F4FD4452E2D74DD364F2E21E71F54BFF5CAE82AB9C9DF69EE86D2BC522363A0DABC521979B0DEADA1DBF9A42D5C4484E0ABCD06BFA53DDEF3C1B20EE3FD59D7C25E41D2B669E1EF16E6F52C3164DF4FB7930E9E4E58857B6AC7D5F42D69F6D187763CF1D5503400487F55BA57E31CC7A7135C886EFB4318AED6A1E012D9E6832A907600A918130C46DC778F971AD0038092999A333CB8B7A1A1DB93D7140003C2A4ECEA9F98D0ACC0A8291CDCEC97DCF8EC9B55A7F88A46B4DB5A851F44182E1C68A007E5E655F6AFFFFFFFFFFFFFFFF", (void *)"0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002" }; static DHKey ffdhe6144 = { 0x0103, NULL, NULL, (void *)"FFFFFFFFFFFFFFFFADF85458A2BB4A9AAFDC5620273D3CF1D8B9C583CE2D3695A9E13641146433FBCC939DCE249B3EF97D2FE363630C75D8F681B202AEC4617AD3DF1ED5D5FD65612433F51F5F066ED0856365553DED1AF3B557135E7F57C935984F0C70E0E68B77E2A689DAF3EFE8721DF158A136ADE73530ACCA4F483A797ABC0AB182B324FB61D108A94BB2C8E3FBB96ADAB760D7F4681D4F42A3DE394DF4AE56EDE76372BB190B07A7C8EE0A6D709E02FCE1CDF7E2ECC03404CD28342F619172FE9CE98583FF8E4F1232EEF28183C3FE3B1B4C6FAD733BB5FCBC2EC22005C58EF1837D1683B2C6F34A26C1B2EFFA886B4238611FCFDCDE355B3B6519035BBC34F4DEF99C023861B46FC9D6E6C9077AD91D2691F7F7EE598CB0FAC186D91CAEFE130985139270B4130C93BC437944F4FD4452E2D74DD364F2E21E71F54BFF5CAE82AB9C9DF69EE86D2BC522363A0DABC521979B0DEADA1DBF9A42D5C4484E0ABCD06BFA53DDEF3C1B20EE3FD59D7C25E41D2B669E1EF16E6F52C3164DF4FB7930E9E4E58857B6AC7D5F42D69F6D187763CF1D5503400487F55BA57E31CC7A7135C886EFB4318AED6A1E012D9E6832A907600A918130C46DC778F971AD0038092999A333CB8B7A1A1DB93D7140003C2A4ECEA9F98D0ACC0A8291CDCEC97DCF8EC9B55A7F88A46B4DB5A851F44182E1C68A007E5E0DD9020BFD64B645036C7A4E677D2C38532A3A23BA4442CAF53EA63BB454329B7624C8917BDD64B1C0FD4CB38E8C334C701C3ACDAD0657FCCFEC719B1F5C3E4E46041F388147FB4CFDB477A52471F7A9A96910B855322EDB6340D8A00EF092350511E30ABEC1FFF9E3A26E7FB29F8C183023C3587E38DA0077D9B4763E4E4B94B2BBC194C6651E77CAF992EEAAC0232A281BF6B3A739C1226116820AE8DB5847A67CBEF9C9091B462D538CD72B03746AE77F5E62292C311562A846505DC82DB854338AE49F5235C95B91178CCF2DD5CACEF403EC9D1810C6272B045B3B71F9DC6B80D63FDD4A8E9ADB1E6962A69526D43161C1A41D570D7938DAD4A40E329CD0E40E65FFFFFFFFFFFFFFFF", (void *)"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002" }; static DHKey ffdhe8192 = { 0x0104, NULL, NULL, (void *)"FFFFFFFFFFFFFFFFADF85458A2BB4A9AAFDC5620273D3CF1D8B9C583CE2D3695A9E13641146433FBCC939DCE249B3EF97D2FE363630C75D8F681B202AEC4617AD3DF1ED5D5FD65612433F51F5F066ED0856365553DED1AF3B557135E7F57C935984F0C70E0E68B77E2A689DAF3EFE8721DF158A136ADE73530ACCA4F483A797ABC0AB182B324FB61D108A94BB2C8E3FBB96ADAB760D7F4681D4F42A3DE394DF4AE56EDE76372BB190B07A7C8EE0A6D709E02FCE1CDF7E2ECC03404CD28342F619172FE9CE98583FF8E4F1232EEF28183C3FE3B1B4C6FAD733BB5FCBC2EC22005C58EF1837D1683B2C6F34A26C1B2EFFA886B4238611FCFDCDE355B3B6519035BBC34F4DEF99C023861B46FC9D6E6C9077AD91D2691F7F7EE598CB0FAC186D91CAEFE130985139270B4130C93BC437944F4FD4452E2D74DD364F2E21E71F54BFF5CAE82AB9C9DF69EE86D2BC522363A0DABC521979B0DEADA1DBF9A42D5C4484E0ABCD06BFA53DDEF3C1B20EE3FD59D7C25E41D2B669E1EF16E6F52C3164DF4FB7930E9E4E58857B6AC7D5F42D69F6D187763CF1D5503400487F55BA57E31CC7A7135C886EFB4318AED6A1E012D9E6832A907600A918130C46DC778F971AD0038092999A333CB8B7A1A1DB93D7140003C2A4ECEA9F98D0ACC0A8291CDCEC97DCF8EC9B55A7F88A46B4DB5A851F44182E1C68A007E5E0DD9020BFD64B645036C7A4E677D2C38532A3A23BA4442CAF53EA63BB454329B7624C8917BDD64B1C0FD4CB38E8C334C701C3ACDAD0657FCCFEC719B1F5C3E4E46041F388147FB4CFDB477A52471F7A9A96910B855322EDB6340D8A00EF092350511E30ABEC1FFF9E3A26E7FB29F8C183023C3587E38DA0077D9B4763E4E4B94B2BBC194C6651E77CAF992EEAAC0232A281BF6B3A739C1226116820AE8DB5847A67CBEF9C9091B462D538CD72B03746AE77F5E62292C311562A846505DC82DB854338AE49F5235C95B91178CCF2DD5CACEF403EC9D1810C6272B045B3B71F9DC6B80D63FDD4A8E9ADB1E6962A69526D43161C1A41D570D7938DAD4A40E329CCFF46AAA36AD004CF600C8381E425A31D951AE64FDB23FCEC9509D43687FEB69EDD1CC5E0B8CC3BDF64B10EF86B63142A3AB8829555B2F747C932665CB2C0F1CC01BD70229388839D2AF05E454504AC78B7582822846C0BA35C35F5C59160CC046FD8251541FC68C9C86B022BB7099876A460E7451A8A93109703FEE1C217E6C3826E52C51AA691E0E423CFC99E9E31650C1217B624816CDAD9A95F9D5B8019488D9C0A0A1FE3075A577E23183F81D4A3F2FA4571EFC8CE0BA8A4FE8B6855DFE72B0A66EDED2FBABFBE58A30FAFABE1C5D71A87E2F741EF8C1FE86FEA6BBFDE530677F0D97D11D49F7A8443D0822E506A9F4614E011E2A94838FF88CD68C8BB7C5C6424CFFFFFFFFFFFFFFFF", (void *)"00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002" }; #endif struct ECCCurveParameters { int size; int iana; const char *name; const char *P; const char *A; const char *B; const char *Gx; const char *Gy; const char *order; ltc_ecc_set_type dp; }; static struct ECCCurveParameters secp192r1 = { 24, 19, "secp192r1", "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFF", // P "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFC", // A "64210519E59C80E70FA7E9AB72243049FEB8DEECC146B9B1", // B "188DA80EB03090F67CBF20EB43A18800F4FF0AFD82FF1012", // Gx "07192B95FFC8DA78631011ED6B24CDD573F977A11E794811", // Gy "FFFFFFFFFFFFFFFFFFFFFFFF99DEF836146BC9B1B4D22831" // order (n) }; static struct ECCCurveParameters secp224r1 = { 28, 21, "secp224r1", "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000000000000001", // P "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFE", // A "B4050A850C04B3ABF54132565044B0B7D7BFD8BA270B39432355FFB4", // B "B70E0CBD6BB4BF7F321390B94A03C1D356C21122343280D6115C1D21", // Gx "BD376388B5F723FB4C22DFE6CD4375A05A07476444D5819985007E34", // Gy "FFFFFFFFFFFFFFFFFFFFFFFFFFFF16A2E0B8F03E13DD29455C5C2A3D" // order (n) }; static struct ECCCurveParameters secp224k1 = { 28, 20, "secp224k1", "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFE56D", // P "00000000000000000000000000000000000000000000000000000000", // A "00000000000000000000000000000000000000000000000000000005", // B "A1455B334DF099DF30FC28A169A467E9E47075A90F7E650EB6B7A45C", // Gx "7E089FED7FBA344282CAFBD6F7E319F7C0B0BD59E2CA4BDB556D61A5", // Gy "0000000000000000000000000001DCE8D2EC6184CAF0A971769FB1F7" // order (n) }; static struct ECCCurveParameters secp256r1 = { 32, 23, "secp256r1", "FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF", // P "FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC", // A "5AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B", // B "6B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296", // Gx "4FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5", // Gy "FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551" // order (n) }; static struct ECCCurveParameters secp256k1 = { 32, 22, "secp256k1", "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F", // P "0000000000000000000000000000000000000000000000000000000000000000", // A "0000000000000000000000000000000000000000000000000000000000000007", // B "79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798", // Gx "483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8", // Gy "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141" // order (n) }; static struct ECCCurveParameters secp384r1 = { 48, 24, "secp384r1", "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFF0000000000000000FFFFFFFF", // P "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFF0000000000000000FFFFFFFC", // A "B3312FA7E23EE7E4988E056BE3F82D19181D9C6EFE8141120314088F5013875AC656398D8A2ED19D2A85C8EDD3EC2AEF", // B "AA87CA22BE8B05378EB1C71EF320AD746E1D3B628BA79B9859F741E082542A385502F25DBF55296C3A545E3872760AB7", // Gx "3617DE4A96262C6F5D9E98BF9292DC29F8F41DBD289A147CE9DA3113B5F0B8C00A60B1CE1D7E819D7A431D7C90EA0E5F", // Gy "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC7634D81F4372DDF581A0DB248B0A77AECEC196ACCC52973" // order (n) }; static struct ECCCurveParameters secp521r1 = { 66, 25, "secp521r1", "01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", // P "01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC", // A "0051953EB9618E1C9A1F929A21A0B68540EEA2DA725B99B315F3B8B489918EF109E156193951EC7E937B1652C0BD3BB1BF073573DF883D2C34F1EF451FD46B503F00", // B "00C6858E06B70404E9CD9E3ECB662395B4429C648139053FB521F828AF606B4D3DBAA14B5E77EFE75928FE1DC127A2FFA8DE3348B3C1856A429BF97E7E31C2E5BD66", // Gx "011839296A789A3BC0045C8A5FB42C7D1BD998F54449579B446817AFBD17273E662C97EE72995EF42640C550B9013FAD0761353C7086A272C24088BE94769FD16650", // Gy "01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA51868783BF2F966B7FCC0148F709A5D03BB5C9B8899C47AEBB6FB71E91386409" // order (n) }; #ifdef TLS_CURVE25519 // dummy static struct ECCCurveParameters x25519 = { 32, 29, "x25519", "7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFED", // P "0000000000000000000000000000000000000000000000000000000000076D06", // A "0000000000000000000000000000000000000000000000000000000000000000", // B "0000000000000000000000000000000000000000000000000000000000000009", // Gx "20AE19A1B8A086B4E01EDD2C7748D14C923D4D7E6D7C61B229E9C5A27ECED3D9", // Gy "1000000000000000000000000000000014DEF9DEA2F79CD65812631A5CF5D3ED" // order (n) }; #endif static struct ECCCurveParameters * const default_curve = &secp256r1; void init_curve(struct ECCCurveParameters *curve) { curve->dp.size = curve->size; curve->dp.name = (char *)curve->name; curve->dp.B = (char *)curve->B; curve->dp.prime = (char *)curve->P; curve->dp.Gx = (char *)curve->Gx; curve->dp.Gy = (char *)curve->Gy; curve->dp.order = (char *)curve->order; } void init_curves() { init_curve(&secp192r1); init_curve(&secp224r1); init_curve(&secp224k1); init_curve(&secp256r1); init_curve(&secp256k1); init_curve(&secp384r1); init_curve(&secp521r1); } #endif struct TLSContext { unsigned char remote_random[TLS_CLIENT_RANDOM_SIZE]; unsigned char local_random[TLS_SERVER_RANDOM_SIZE]; unsigned char session[TLS_MAX_SESSION_ID]; unsigned char session_size; unsigned short cipher; unsigned short version; unsigned char is_server; struct TLSCertificate **certificates; struct TLSCertificate *private_key; #ifdef TLS_ECDSA_SUPPORTED struct TLSCertificate *ec_private_key; #endif #ifdef TLS_FORWARD_SECRECY DHKey *dhe; ecc_key *ecc_dhe; char *default_dhe_p; char *default_dhe_g; const struct ECCCurveParameters *curve; #endif struct TLSCertificate **client_certificates; unsigned int certificates_count; unsigned int client_certificates_count; unsigned char *master_key; unsigned int master_key_len; unsigned char *premaster_key; unsigned int premaster_key_len; unsigned char cipher_spec_set; TLSCipher crypto; TLSHash *handshake_hash; unsigned char *message_buffer; unsigned int message_buffer_len; uint64_t remote_sequence_number; uint64_t local_sequence_number; unsigned char connection_status; unsigned char critical_error; unsigned char error_code; unsigned char *tls_buffer; unsigned int tls_buffer_len; unsigned char *application_buffer; unsigned int application_buffer_len; unsigned char is_child; unsigned char exportable; unsigned char *exportable_keys; unsigned char exportable_size; char *sni; unsigned char request_client_certificate; unsigned char dtls; unsigned short dtls_epoch_local; unsigned short dtls_epoch_remote; unsigned char *dtls_cookie; unsigned char dtls_cookie_len; unsigned char dtls_seq; unsigned char *cached_handshake; unsigned int cached_handshake_len; unsigned char client_verified; // handshake messages flags unsigned char hs_messages[11]; void *user_data; struct TLSCertificate **root_certificates; unsigned int root_count; #ifdef TLS_ACCEPT_SECURE_RENEGOTIATION unsigned char *verify_data; unsigned char verify_len; #endif #ifdef WITH_TLS_13 unsigned char *finished_key; unsigned char *remote_finished_key; unsigned char *server_finished_hash; #endif #ifdef TLS_CURVE25519 unsigned char *client_secret; #endif char **alpn; unsigned char alpn_count; char *negotiated_alpn; unsigned int sleep_until; unsigned short tls13_version; #ifdef TLS_12_FALSE_START unsigned char false_start; #endif }; struct TLSPacket { unsigned char *buf; unsigned int len; unsigned int size; unsigned char broken; struct TLSContext *context; }; #ifdef SSL_COMPATIBLE_INTERFACE typedef int (*SOCKET_RECV_CALLBACK)(int socket, void *buffer, size_t length, int flags); typedef int (*SOCKET_SEND_CALLBACK)(int socket, const void *buffer, size_t length, int flags); #ifndef _WIN32 #include #endif #endif static const unsigned int version_id[] = {1, 1, 1, 0}; static const unsigned int pk_id[] = {1, 1, 7, 0}; static const unsigned int serial_id[] = {1, 1, 2, 1, 0}; static const unsigned int issurer_id[] = {1, 1, 4, 0}; static const unsigned int owner_id[] = {1, 1, 6, 0}; static const unsigned int validity_id[] = {1, 1, 5, 0}; static const unsigned int algorithm_id[] = {1, 1, 3, 0}; static const unsigned int sign_id[] = {1, 3, 2, 1, 0}; static const unsigned int priv_id[] = {1, 4, 0}; static const unsigned int priv_der_id[] = {1, 3, 1, 0}; static const unsigned int ecc_priv_id[] = {1, 2, 0}; static const unsigned char country_oid[] = {0x55, 0x04, 0x06, 0x00}; static const unsigned char state_oid[] = {0x55, 0x04, 0x08, 0x00}; static const unsigned char location_oid[] = {0x55, 0x04, 0x07, 0x00}; static const unsigned char entity_oid[] = {0x55, 0x04, 0x0A, 0x00}; static const unsigned char subject_oid[] = {0x55, 0x04, 0x03, 0x00}; static const unsigned char san_oid[] = {0x55, 0x1D, 0x11, 0x00}; static const unsigned char ocsp_oid[] = {0x2B, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x00}; static const unsigned char TLS_RSA_SIGN_RSA_OID[] = {0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01, 0x00}; static const unsigned char TLS_RSA_SIGN_MD5_OID[] = {0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x04, 0x00}; static const unsigned char TLS_RSA_SIGN_SHA1_OID[] = {0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x05, 0x00}; static const unsigned char TLS_RSA_SIGN_SHA256_OID[] = {0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0B, 0x00}; static const unsigned char TLS_RSA_SIGN_SHA384_OID[] = {0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0C, 0x00}; static const unsigned char TLS_RSA_SIGN_SHA512_OID[] = {0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0D, 0x00}; // static const unsigned char TLS_ECDSA_SIGN_SHA1_OID[] = {0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x04, 0x01, 0x05, 0x00, 0x00}; // static const unsigned char TLS_ECDSA_SIGN_SHA224_OID[] = {0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x04, 0x03, 0x01, 0x05, 0x00, 0x00}; static const unsigned char TLS_ECDSA_SIGN_SHA256_OID[] = {0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x04, 0x03, 0x02, 0x05, 0x00, 0x00}; // static const unsigned char TLS_ECDSA_SIGN_SHA384_OID[] = {0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x04, 0x03, 0x03, 0x05, 0x00, 0x00}; // static const unsigned char TLS_ECDSA_SIGN_SHA512_OID[] = {0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x04, 0x03, 0x04, 0x05, 0x00, 0x00}; static const unsigned char TLS_EC_PUBLIC_KEY_OID[] = {0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x02, 0x01, 0x00}; static const unsigned char TLS_EC_prime192v1_OID[] = {0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x01, 0x00}; static const unsigned char TLS_EC_prime192v2_OID[] = {0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x02, 0x00}; static const unsigned char TLS_EC_prime192v3_OID[] = {0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x03, 0x00}; static const unsigned char TLS_EC_prime239v1_OID[] = {0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x04, 0x00}; static const unsigned char TLS_EC_prime239v2_OID[] = {0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x05, 0x00}; static const unsigned char TLS_EC_prime239v3_OID[] = {0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x06, 0x00}; static const unsigned char TLS_EC_prime256v1_OID[] = {0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x07, 0x00}; #define TLS_EC_secp256r1_OID TLS_EC_prime256v1_OID static const unsigned char TLS_EC_secp224r1_OID[] = {0x2B, 0x81, 0x04, 0x00, 0x21, 0x00}; static const unsigned char TLS_EC_secp384r1_OID[] = {0x2B, 0x81, 0x04, 0x00, 0x22, 0x00}; static const unsigned char TLS_EC_secp521r1_OID[] = {0x2B, 0x81, 0x04, 0x00, 0x23, 0x00}; struct TLSCertificate *asn1_parse(struct TLSContext *context, const unsigned char *buffer, unsigned int size, int client_cert); int _private_tls_update_hash(struct TLSContext *context, const unsigned char *in, unsigned int len); struct TLSPacket *tls_build_finished(struct TLSContext *context); unsigned int _private_tls_hmac_message(unsigned char local, struct TLSContext *context, const unsigned char *buf, int buf_len, const unsigned char *buf2, int buf_len2, unsigned char *out, unsigned int outlen, uint64_t remote_sequence_number); int tls_random(unsigned char *key, int len); void tls_destroy_packet(struct TLSPacket *packet); struct TLSPacket *tls_build_hello(struct TLSContext *context, int tls13_downgrade); struct TLSPacket *tls_build_certificate(struct TLSContext *context); struct TLSPacket *tls_build_done(struct TLSContext *context); struct TLSPacket *tls_build_alert(struct TLSContext *context, char critical, unsigned char code); struct TLSPacket *tls_build_change_cipher_spec(struct TLSContext *context); struct TLSPacket *tls_build_verify_request(struct TLSContext *context); int _private_tls_crypto_create(struct TLSContext *context, int key_length, unsigned char *localkey, unsigned char *localiv, unsigned char *remotekey, unsigned char *remoteiv); int _private_tls_get_hash(struct TLSContext *context, unsigned char *hout); int _private_tls_done_hash(struct TLSContext *context, unsigned char *hout); int _private_tls_get_hash_idx(struct TLSContext *context); int _private_tls_build_random(struct TLSPacket *packet); unsigned int _private_tls_mac_length(struct TLSContext *context); void _private_dtls_handshake_data(struct TLSContext *context, struct TLSPacket *packet, unsigned int dataframe); #ifdef TLS_FORWARD_SECRECY void _private_tls_dhe_free(struct TLSContext *context); void _private_tls_ecc_dhe_free(struct TLSContext *context); void _private_tls_dh_clear_key(DHKey *key); #endif #ifdef WITH_TLS_13 struct TLSPacket *tls_build_encrypted_extensions(struct TLSContext *context); struct TLSPacket *tls_build_certificate_verify(struct TLSContext *context); #endif // dtls base secret static unsigned char dtls_secret[32]; static unsigned char dependecies_loaded = 0; // not supported // static unsigned char TLS_DSA_SIGN_SHA1_OID[] = {0x2A, 0x86, 0x52, 0xCE, 0x38, 0x04, 0x03, 0x00}; // base64 stuff static const char cd64[] = "|$$$}rstuvwxyz{$$$$$$$>?@ABCDEFGHIJKLMNOPQRSTUVW$$$$$$XYZ[\\]^_`abcdefghijklmnopq"; void _private_b64_decodeblock(unsigned char in[4], unsigned char out[3]) { out[0] = (unsigned char )(in[0] << 2 | in[1] >> 4); out[1] = (unsigned char )(in[1] << 4 | in[2] >> 2); out[2] = (unsigned char )(((in[2] << 6) & 0xc0) | in[3]); } int _private_b64_decode(const char *in_buffer, int in_buffer_size, unsigned char *out_buffer) { unsigned char in[4], out[3], v; int i, len; const char *ptr = in_buffer; char *out_ptr = (char *)out_buffer; while (ptr <= in_buffer + in_buffer_size) { for (len = 0, i = 0; i < 4 && (ptr <= in_buffer + in_buffer_size); i++) { v = 0; while ((ptr <= in_buffer + in_buffer_size) && v == 0) { v = (unsigned char)ptr[0]; ptr++; v = (unsigned char)((v < 43 || v > 122) ? 0 : cd64[v - 43]); if (v) v = (unsigned char)((v == '$') ? 0 : v - 61); } if (ptr <= in_buffer + in_buffer_size) { len++; if (v) in[i] = (unsigned char)(v - 1); } else { in[i] = 0; } } if (len) { _private_b64_decodeblock(in, out); for (i = 0; i < len - 1; i++) { out_ptr[0] = out[i]; out_ptr++; } } } return (int)((intptr_t)out_ptr - (intptr_t)out_buffer); } void dtls_reset_cookie_secret() { tls_random(dtls_secret, sizeof(dtls_secret)); } void tls_init() { if (dependecies_loaded) return; DEBUG_PRINT("Initializing dependencies\n"); dependecies_loaded = 1; #ifdef LTM_DESC ltc_mp = ltm_desc; #else #ifdef TFM_DESC ltc_mp = tfm_desc; #else #ifdef GMP_DESC ltc_mp = gmp_desc; #endif #endif #endif register_prng(&sprng_desc); register_hash(&sha256_desc); register_hash(&sha1_desc); register_hash(&sha384_desc); register_hash(&sha512_desc); register_hash(&md5_desc); register_cipher(&aes_desc); #ifdef TLS_FORWARD_SECRECY init_curves(); #endif dtls_reset_cookie_secret(); } #ifdef TLS_FORWARD_SECRECY int _private_tls_dh_shared_secret(DHKey *private_key, DHKey *public_key, unsigned char *out, unsigned long *outlen) { void *tmp; unsigned long x; int err; if ((!private_key) || (!public_key) || (!out) || (!outlen)) return TLS_GENERIC_ERROR; /* compute y^x mod p */ if ((err = mp_init(&tmp)) != CRYPT_OK) return err; if ((err = mp_exptmod(public_key->y, private_key->x, private_key->p, tmp)) != CRYPT_OK) { mp_clear(tmp); return err; } x = (unsigned long)mp_unsigned_bin_size(tmp); if (*outlen < x) { err = CRYPT_BUFFER_OVERFLOW; mp_clear(tmp); return err; } if ((err = mp_to_unsigned_bin(tmp, out)) != CRYPT_OK) { mp_clear(tmp); return err; } *outlen = x; mp_clear(tmp); return 0; } unsigned char *_private_tls_decrypt_dhe(struct TLSContext *context, const unsigned char *buffer, unsigned int len, unsigned int *size, int clear_key) { *size = 0; if ((!len) || (!context) || (!context->dhe)) { DEBUG_PRINT("No private DHE key set\n"); return NULL; } unsigned long out_size = len; void *Yc = NULL; if (mp_init(&Yc)) { DEBUG_PRINT("ERROR CREATING Yc\n"); return NULL; } if (mp_read_unsigned_bin(Yc, (unsigned char *)buffer, len)) { DEBUG_PRINT("ERROR LOADING DHE Yc\n"); mp_clear(Yc); return NULL; } unsigned char *out = (unsigned char *)TLS_MALLOC(len); DHKey client_key; memset(&client_key, 0, sizeof(DHKey)); client_key.p = context->dhe->p; client_key.g = context->dhe->g; client_key.y = Yc; int err = _private_tls_dh_shared_secret(context->dhe, &client_key, out, &out_size); // don't delete p and g client_key.p = NULL; client_key.g = NULL; _private_tls_dh_clear_key(&client_key); // not needing the dhe key anymore if (clear_key) _private_tls_dhe_free(context); if (err) { DEBUG_PRINT("DHE DECRYPT ERROR %i\n", err); TLS_FREE(out); return NULL; } DEBUG_PRINT("OUT_SIZE: %lu\n", out_size); DEBUG_DUMP_HEX_LABEL("DHE", out, out_size); *size = (unsigned int)out_size; return out; } unsigned char *_private_tls_decrypt_ecc_dhe(struct TLSContext *context, const unsigned char *buffer, unsigned int len, unsigned int *size, int clear_key) { *size = 0; if ((!len) || (!context) || (!context->ecc_dhe)) { DEBUG_PRINT("No private ECC DHE key set\n"); return NULL; } const struct ECCCurveParameters *curve; if (context->curve) curve = context->curve; else curve = default_curve; ltc_ecc_set_type *dp = (ltc_ecc_set_type *)&curve->dp; ecc_key client_key; memset(&client_key, 0, sizeof(client_key)); if (ecc_ansi_x963_import_ex(buffer, len, &client_key, dp)) { DEBUG_PRINT("Error importing ECC DHE key\n"); return NULL; } unsigned char *out = (unsigned char *)TLS_MALLOC(len); unsigned long out_size = len; int err = ecc_shared_secret(context->ecc_dhe, &client_key, out, &out_size); ecc_free(&client_key); if (clear_key) _private_tls_ecc_dhe_free(context); if (err) { DEBUG_PRINT("ECC DHE DECRYPT ERROR %i\n", err); TLS_FREE(out); return NULL; } DEBUG_PRINT("OUT_SIZE: %lu\n", out_size); DEBUG_DUMP_HEX_LABEL("ECC DHE", out, out_size); *size = (unsigned int)out_size; return out; } #endif unsigned char *_private_tls_decrypt_rsa(struct TLSContext *context, const unsigned char *buffer, unsigned int len, unsigned int *size) { *size = 0; if ((!len) || (!context) || (!context->private_key) || (!context->private_key->der_bytes) || (!context->private_key->der_len)) { DEBUG_PRINT("No private key set\n"); return NULL; } tls_init(); rsa_key key; int err; err = rsa_import(context->private_key->der_bytes, context->private_key->der_len, &key); if (err) { DEBUG_PRINT("Error importing RSA key (code: %i)\n", err); return NULL; } unsigned char *out = (unsigned char *)TLS_MALLOC(len); unsigned long out_size = len; int res = 0; err = rsa_decrypt_key_ex(buffer, len, out, &out_size, NULL, 0, -1, LTC_PKCS_1_V1_5, &res, &key); rsa_free(&key); if ((err) || (out_size != 48) || (ntohs(*(unsigned short *)out) != context->version)) { // generate a random secret and continue (ROBOT fix) // silently ignore and generate a random secret out_size = 48; tls_random(out, out_size); *(unsigned short *)out = htons(context->version); } *size = (unsigned int)out_size; return out; } unsigned char *_private_tls_encrypt_rsa(struct TLSContext *context, const unsigned char *buffer, unsigned int len, unsigned int *size) { *size = 0; if ((!len) || (!context) || (!context->certificates) || (!context->certificates_count) || (!context->certificates[0]) || (!context->certificates[0]->der_bytes) || (!context->certificates[0]->der_len)) { DEBUG_PRINT("No certificate set\n"); return NULL; } tls_init(); rsa_key key; int err; err = rsa_import(context->certificates[0]->der_bytes, context->certificates[0]->der_len, &key); if (err) { DEBUG_PRINT("Error importing RSA certificate (code: %i)\n", err); return NULL; } unsigned long out_size = TLS_MAX_RSA_KEY; unsigned char *out = (unsigned char *)TLS_MALLOC(out_size); int hash_idx = find_hash("sha256"); int prng_idx = find_prng("sprng"); err = rsa_encrypt_key_ex(buffer, len, out, &out_size, (unsigned char *)"Concept", 7, NULL, prng_idx, hash_idx, LTC_PKCS_1_V1_5, &key); rsa_free(&key); if ((err) || (!out_size)) { TLS_FREE(out); return NULL; } *size = (unsigned int)out_size; return out; } #ifdef TLS_LEGACY_SUPPORT int _private_rsa_verify_hash_md5sha1(const unsigned char *sig, unsigned long siglen, unsigned char *hash, unsigned long hashlen, int *stat, rsa_key *key) { unsigned long modulus_bitlen, modulus_bytelen, x; int err; unsigned char *tmpbuf = NULL; if ((hash == NULL) || (sig == NULL) || (stat == NULL) || (key == NULL) || (!siglen) || (!hashlen)) return TLS_GENERIC_ERROR; *stat = 0; modulus_bitlen = mp_count_bits((key->N)); modulus_bytelen = mp_unsigned_bin_size((key->N)); if (modulus_bytelen != siglen) return TLS_GENERIC_ERROR; tmpbuf = (unsigned char *)TLS_MALLOC(siglen); if (!tmpbuf) return TLS_GENERIC_ERROR; x = siglen; if ((err = ltc_mp.rsa_me(sig, siglen, tmpbuf, &x, PK_PUBLIC, key)) != CRYPT_OK) { TLS_FREE(tmpbuf); return err; } if (x != siglen) { TLS_FREE(tmpbuf); return CRYPT_INVALID_PACKET; } unsigned long out_len = siglen; unsigned char *out = (unsigned char *)TLS_MALLOC(siglen); if (!out) { TLS_FREE(tmpbuf); return TLS_GENERIC_ERROR; } int decoded = 0; err = pkcs_1_v1_5_decode(tmpbuf, x, LTC_PKCS_1_EMSA, modulus_bitlen, out, &out_len, &decoded); if (decoded) { if (out_len == hashlen) { if (!memcmp(out, hash, hashlen)) *stat = 1; } } TLS_FREE(tmpbuf); TLS_FREE(out); return err; } #endif int _private_tls_verify_rsa(struct TLSContext *context, unsigned int hash_type, const unsigned char *buffer, unsigned int len, const unsigned char *message, unsigned int message_len) { tls_init(); rsa_key key; int err; if (context->is_server) { if ((!len) || (!context->client_certificates) || (!context->client_certificates_count) || (!context->client_certificates[0]) || (!context->client_certificates[0]->der_bytes) || (!context->client_certificates[0]->der_len)) { DEBUG_PRINT("No client certificate set\n"); return TLS_GENERIC_ERROR; } err = rsa_import(context->client_certificates[0]->der_bytes, context->client_certificates[0]->der_len, &key); } else { if ((!len) || (!context->certificates) || (!context->certificates_count) || (!context->certificates[0]) || (!context->certificates[0]->der_bytes) || (!context->certificates[0]->der_len)) { DEBUG_PRINT("No server certificate set\n"); return TLS_GENERIC_ERROR; } err = rsa_import(context->certificates[0]->der_bytes, context->certificates[0]->der_len, &key); } if (err) { DEBUG_PRINT("Error importing RSA certificate (code: %i)\n", err); return TLS_GENERIC_ERROR; } int hash_idx = -1; unsigned char hash[TLS_MAX_HASH_LEN]; unsigned int hash_len = 0; hash_state state; switch (hash_type) { case md5: hash_idx = find_hash("md5"); err = md5_init(&state); TLS_ERROR(err, break); err = md5_process(&state, message, message_len); TLS_ERROR(err, break); err = md5_done(&state, hash); TLS_ERROR(err, break); hash_len = 16; break; case sha1: hash_idx = find_hash("sha1"); err = sha1_init(&state); TLS_ERROR(err, break) err = sha1_process(&state, message, message_len); TLS_ERROR(err, break) err = sha1_done(&state, hash); TLS_ERROR(err, break) hash_len = 20; break; case sha256: hash_idx = find_hash("sha256"); err = sha256_init(&state); TLS_ERROR(err, break) err = sha256_process(&state, message, message_len); TLS_ERROR(err, break) err = sha256_done(&state, hash); TLS_ERROR(err, break) hash_len = 32; break; case sha384: hash_idx = find_hash("sha384"); err = sha384_init(&state); TLS_ERROR(err, break) err = sha384_process(&state, message, message_len); TLS_ERROR(err, break) err = sha384_done(&state, hash); TLS_ERROR(err, break) hash_len = 48; break; case sha512: hash_idx = find_hash("sha512"); err = sha512_init(&state); TLS_ERROR(err, break) err = sha512_process(&state, message, message_len); TLS_ERROR(err, break) err = sha512_done(&state, hash); TLS_ERROR(err, break) hash_len = 64; break; #ifdef TLS_LEGACY_SUPPORT case _md5_sha1: hash_idx = find_hash("md5"); err = md5_init(&state); TLS_ERROR(err, break) err = md5_process(&state, message, message_len); TLS_ERROR(err, break) err = md5_done(&state, hash); TLS_ERROR(err, break) hash_idx = find_hash("sha1"); err = sha1_init(&state); TLS_ERROR(err, break) err = sha1_process(&state, message, message_len); TLS_ERROR(err, break) err = sha1_done(&state, hash + 16); TLS_ERROR(err, break) hash_len = 36; err = sha1_init(&state); TLS_ERROR(err, break) err = sha1_process(&state, message, message_len); TLS_ERROR(err, break) err = sha1_done(&state, hash + 16); TLS_ERROR(err, break) hash_len = 36; break; #endif } if ((hash_idx < 0) || (err)) { DEBUG_PRINT("Unsupported hash type: %i\n", hash_type); return TLS_GENERIC_ERROR; } int rsa_stat = 0; #ifdef TLS_LEGACY_SUPPORT if (hash_type == _md5_sha1) err = _private_rsa_verify_hash_md5sha1(buffer, len, hash, hash_len, &rsa_stat, &key); else #endif #ifdef WITH_TLS_13 if ((context->version == TLS_V13) || (context->version == DTLS_V13)) err = rsa_verify_hash_ex(buffer, len, hash, hash_len, LTC_PKCS_1_PSS, hash_idx, 0, &rsa_stat, &key); else #endif err = rsa_verify_hash_ex(buffer, len, hash, hash_len, LTC_PKCS_1_V1_5, hash_idx, 0, &rsa_stat, &key); rsa_free(&key); if (err) return 0; return rsa_stat; } #ifdef TLS_LEGACY_SUPPORT int _private_rsa_sign_hash_md5sha1(const unsigned char *in, unsigned long inlen, unsigned char *out, unsigned long *outlen, rsa_key *key) { unsigned long modulus_bitlen, modulus_bytelen, x; int err; if ((in == NULL) || (out == NULL) || (outlen == NULL) || (key == NULL)) return TLS_GENERIC_ERROR; modulus_bitlen = mp_count_bits((key->N)); modulus_bytelen = mp_unsigned_bin_size((key->N)); if (modulus_bytelen > *outlen) { *outlen = modulus_bytelen; return CRYPT_BUFFER_OVERFLOW; } x = modulus_bytelen; err = pkcs_1_v1_5_encode(in, inlen, LTC_PKCS_1_EMSA, modulus_bitlen, NULL, 0, out, &x); if (err != CRYPT_OK) return err; return ltc_mp.rsa_me(out, x, out, outlen, PK_PRIVATE, key); } #endif int _private_tls_sign_rsa(struct TLSContext *context, unsigned int hash_type, const unsigned char *message, unsigned int message_len, unsigned char *out, unsigned long *outlen) { if ((!outlen) || (!context) || (!out) || (!outlen) || (!context->private_key) || (!context->private_key->der_bytes) || (!context->private_key->der_len)) { DEBUG_PRINT("No private key set\n"); return TLS_GENERIC_ERROR; } tls_init(); rsa_key key; int err; err = rsa_import(context->private_key->der_bytes, context->private_key->der_len, &key); if (err) { DEBUG_PRINT("Error importing RSA certificate (code: %i)\n", err); return TLS_GENERIC_ERROR; } int hash_idx = -1; unsigned char hash[TLS_MAX_HASH_LEN]; unsigned int hash_len = 0; hash_state state; switch (hash_type) { case md5: hash_idx = find_hash("md5"); err = md5_init(&state); TLS_ERROR(err, break) err = md5_process(&state, message, message_len); TLS_ERROR(err, break) err = md5_done(&state, hash); TLS_ERROR(err, break) hash_len = 16; break; case sha1: hash_idx = find_hash("sha1"); err = sha1_init(&state); TLS_ERROR(err, break) err = sha1_process(&state, message, message_len); TLS_ERROR(err, break) err = sha1_done(&state, hash); TLS_ERROR(err, break) hash_len = 20; break; case sha256: hash_idx = find_hash("sha256"); err = sha256_init(&state); TLS_ERROR(err, break) err = sha256_process(&state, message, message_len); TLS_ERROR(err, break) err = sha256_done(&state, hash); TLS_ERROR(err, break) hash_len = 32; break; case sha384: hash_idx = find_hash("sha384"); err = sha384_init(&state); TLS_ERROR(err, break) err = sha384_process(&state, message, message_len); TLS_ERROR(err, break) err = sha384_done(&state, hash); TLS_ERROR(err, break) hash_len = 48; break; case sha512: hash_idx = find_hash("sha512"); err = sha512_init(&state); TLS_ERROR(err, break) err = sha512_process(&state, message, message_len); TLS_ERROR(err, break) err = sha512_done(&state, hash); TLS_ERROR(err, break) hash_len = 64; break; case _md5_sha1: hash_idx = find_hash("md5"); err = md5_init(&state); TLS_ERROR(err, break) err = md5_process(&state, message, message_len); TLS_ERROR(err, break) err = md5_done(&state, hash); TLS_ERROR(err, break) hash_idx = find_hash("sha1"); err = sha1_init(&state); TLS_ERROR(err, break) err = sha1_process(&state, message, message_len); TLS_ERROR(err, break) err = sha1_done(&state, hash + 16); TLS_ERROR(err, break) hash_len = 36; err = sha1_init(&state); TLS_ERROR(err, break) err = sha1_process(&state, message, message_len); TLS_ERROR(err, break) err = sha1_done(&state, hash + 16); TLS_ERROR(err, break) hash_len = 36; break; } #ifdef TLS_LEGACY_SUPPORT if (hash_type == _md5_sha1) { if (err) { DEBUG_PRINT("Unsupported hash type: %i\n", hash_type); return TLS_GENERIC_ERROR; } err = _private_rsa_sign_hash_md5sha1(hash, hash_len, out, outlen, &key); } else #endif { if ((hash_idx < 0) || (err)) { DEBUG_PRINT("Unsupported hash type: %i\n", hash_type); return TLS_GENERIC_ERROR; } #ifdef WITH_TLS_13 if ((context->version == TLS_V13) || (context->version == DTLS_V13)) err = rsa_sign_hash_ex(hash, hash_len, out, outlen, LTC_PKCS_1_PSS, NULL, find_prng("sprng"), hash_idx, hash_type == sha256 ? 32 : 48, &key); else #endif err = rsa_sign_hash_ex(hash, hash_len, out, outlen, LTC_PKCS_1_V1_5, NULL, find_prng("sprng"), hash_idx, 0, &key); } rsa_free(&key); if (err) return 0; return 1; } #ifdef TLS_ECDSA_SUPPORTED static int _private_tls_is_point(ecc_key *key) { void *prime, *b, *t1, *t2; int err; if ((err = mp_init_multi(&prime, &b, &t1, &t2, NULL)) != CRYPT_OK) { return err; } /* load prime and b */ if ((err = mp_read_radix(prime, TLS_TOMCRYPT_PRIVATE_DP(key)->prime, 16)) != CRYPT_OK) { goto error; } if ((err = mp_read_radix(b, TLS_TOMCRYPT_PRIVATE_DP(key)->B, 16)) != CRYPT_OK) { goto error; } /* compute y^2 */ if ((err = mp_sqr(key->pubkey.y, t1)) != CRYPT_OK) { goto error; } /* compute x^3 */ if ((err = mp_sqr(key->pubkey.x, t2)) != CRYPT_OK) { goto error; } if ((err = mp_mod(t2, prime, t2)) != CRYPT_OK) { goto error; } if ((err = mp_mul(key->pubkey.x, t2, t2)) != CRYPT_OK) { goto error; } /* compute y^2 - x^3 */ if ((err = mp_sub(t1, t2, t1)) != CRYPT_OK) { goto error; } /* compute y^2 - x^3 + 3x */ if ((err = mp_add(t1, key->pubkey.x, t1)) != CRYPT_OK) { goto error; } if ((err = mp_add(t1, key->pubkey.x, t1)) != CRYPT_OK) { goto error; } if ((err = mp_add(t1, key->pubkey.x, t1)) != CRYPT_OK) { goto error; } if ((err = mp_mod(t1, prime, t1)) != CRYPT_OK) { goto error; } while (mp_cmp_d(t1, 0) == LTC_MP_LT) { if ((err = mp_add(t1, prime, t1)) != CRYPT_OK) { goto error; } } while (mp_cmp(t1, prime) != LTC_MP_LT) { if ((err = mp_sub(t1, prime, t1)) != CRYPT_OK) { goto error; } } /* compare to b */ if (mp_cmp(t1, b) != LTC_MP_EQ) { err = CRYPT_INVALID_PACKET; } else { err = CRYPT_OK; } error: mp_clear_multi(prime, b, t1, t2, NULL); return err; } int _private_tls_ecc_import_key(const unsigned char *private_key, int private_len, const unsigned char *public_key, int public_len, ecc_key *key, const ltc_ecc_set_type *dp) { int err; if ((!key) || (!ltc_mp.name)) return CRYPT_MEM; key->type = PK_PRIVATE; if (mp_init_multi(&key->pubkey.x, &key->pubkey.y, &key->pubkey.z, &key->k, NULL) != CRYPT_OK) return CRYPT_MEM; if ((public_len) && (!public_key[0])) { public_key++; public_len--; } if ((err = mp_read_unsigned_bin(key->pubkey.x, (unsigned char *)public_key + 1, (public_len - 1) >> 1)) != CRYPT_OK) { mp_clear_multi(key->pubkey.x, key->pubkey.y, key->pubkey.z, key->k, NULL); return err; } if ((err = mp_read_unsigned_bin(key->pubkey.y, (unsigned char *)public_key + 1 + ((public_len - 1) >> 1), (public_len - 1) >> 1)) != CRYPT_OK) { mp_clear_multi(key->pubkey.x, key->pubkey.y, key->pubkey.z, key->k, NULL); return err; } if ((err = mp_read_unsigned_bin(key->k, (unsigned char *)private_key, private_len)) != CRYPT_OK) { mp_clear_multi(key->pubkey.x, key->pubkey.y, key->pubkey.z, key->k, NULL); return err; } TLS_TOMCRYPT_PRIVATE_SET_INDEX(key, -1); TLS_TOMCRYPT_PRIVATE_DP(key) = dp; /* set z */ if ((err = mp_set(key->pubkey.z, 1)) != CRYPT_OK) { mp_clear_multi(key->pubkey.x, key->pubkey.y, key->pubkey.z, key->k, NULL); return err; } /* is it a point on the curve? */ if ((err = _private_tls_is_point(key)) != CRYPT_OK) { DEBUG_PRINT("KEY IS NOT ON CURVE\n"); mp_clear_multi(key->pubkey.x, key->pubkey.y, key->pubkey.z, key->k, NULL); return err; } /* we're good */ return CRYPT_OK; } int _private_tls_sign_ecdsa(struct TLSContext *context, unsigned int hash_type, const unsigned char *message, unsigned int message_len, unsigned char *out, unsigned long *outlen) { if ((!outlen) || (!context) || (!out) || (!outlen) || (!context->ec_private_key) || (!context->ec_private_key->priv) || (!context->ec_private_key->priv_len) || (!context->ec_private_key->pk) || (!context->ec_private_key->pk_len)) { DEBUG_PRINT("No private ECDSA key set\n"); return TLS_GENERIC_ERROR; } const struct ECCCurveParameters *curve = NULL; switch (context->ec_private_key->ec_algorithm) { case 19: curve = &secp192r1; break; case 20: curve = &secp224k1; break; case 21: curve = &secp224r1; break; case 22: curve = &secp256k1; break; case 23: curve = &secp256r1; break; case 24: curve = &secp384r1; break; case 25: curve = &secp521r1; break; default: DEBUG_PRINT("UNSUPPORTED CURVE\n"); } if (!curve) return TLS_GENERIC_ERROR; tls_init(); ecc_key key; int err; ltc_ecc_set_type *dp = (ltc_ecc_set_type *)&curve->dp; // broken ... fix this err = _private_tls_ecc_import_key(context->ec_private_key->priv, context->ec_private_key->priv_len, context->ec_private_key->pk, context->ec_private_key->pk_len, &key, dp); if (err) { DEBUG_PRINT("Error importing ECC certificate (code: %i)\n", (int)err); return TLS_GENERIC_ERROR; } unsigned char hash[TLS_MAX_HASH_LEN]; unsigned int hash_len = 0; hash_state state; switch (hash_type) { case md5: err = md5_init(&state); TLS_ERROR(err, break) err = md5_process(&state, message, message_len); TLS_ERROR(err, break) err = md5_done(&state, hash); TLS_ERROR(err, break) hash_len = 16; break; case sha1: err = sha1_init(&state); TLS_ERROR(err, break) err = sha1_process(&state, message, message_len); TLS_ERROR(err, break) err = sha1_done(&state, hash); TLS_ERROR(err, break) hash_len = 20; break; case sha256: err = sha256_init(&state); TLS_ERROR(err, break) err = sha256_process(&state, message, message_len); TLS_ERROR(err, break) err = sha256_done(&state, hash); TLS_ERROR(err, break) hash_len = 32; break; case sha384: err = sha384_init(&state); TLS_ERROR(err, break) err = sha384_process(&state, message, message_len); TLS_ERROR(err, break) err = sha384_done(&state, hash); TLS_ERROR(err, break) hash_len = 48; break; case sha512: err = sha512_init(&state); TLS_ERROR(err, break) err = sha512_process(&state, message, message_len); TLS_ERROR(err, break) err = sha512_done(&state, hash); TLS_ERROR(err, break) hash_len = 64; break; case _md5_sha1: err = md5_init(&state); TLS_ERROR(err, break) err = md5_process(&state, message, message_len); TLS_ERROR(err, break) err = md5_done(&state, hash); TLS_ERROR(err, break) err = sha1_init(&state); TLS_ERROR(err, break) err = sha1_process(&state, message, message_len); TLS_ERROR(err, break) err = sha1_done(&state, hash + 16); TLS_ERROR(err, break) hash_len = 36; err = sha1_init(&state); TLS_ERROR(err, break) err = sha1_process(&state, message, message_len); TLS_ERROR(err, break) err = sha1_done(&state, hash + 16); TLS_ERROR(err, break) hash_len = 36; break; } if (err) { DEBUG_PRINT("Unsupported hash type: %i\n", hash_type); return TLS_GENERIC_ERROR; } // "Let z be the Ln leftmost bits of e, where Ln is the bit length of the group order n." if (hash_len > (unsigned int)curve->size) hash_len = (unsigned int)curve->size; err = ecc_sign_hash(hash, hash_len, out, outlen, NULL, find_prng("sprng"), &key); DEBUG_DUMP_HEX_LABEL("ECC SIGNATURE", out, *outlen); ecc_free(&key); if (err) return 0; return 1; } #if defined(TLS_CLIENT_ECDSA) || defined(WITH_TLS_13) int _private_tls_ecc_import_pk(const unsigned char *public_key, int public_len, ecc_key *key, const ltc_ecc_set_type *dp) { int err; if ((!key) || (!ltc_mp.name)) return CRYPT_MEM; key->type = PK_PUBLIC; if (mp_init_multi(&key->pubkey.x, &key->pubkey.y, &key->pubkey.z, &key->k, NULL) != CRYPT_OK) return CRYPT_MEM; if ((public_len) && (!public_key[0])) { public_key++; public_len--; } if ((err = mp_read_unsigned_bin(key->pubkey.x, (unsigned char *)public_key + 1, (public_len - 1) >> 1)) != CRYPT_OK) { mp_clear_multi(key->pubkey.x, key->pubkey.y, key->pubkey.z, key->k, NULL); return err; } if ((err = mp_read_unsigned_bin(key->pubkey.y, (unsigned char *)public_key + 1 + ((public_len - 1) >> 1), (public_len - 1) >> 1)) != CRYPT_OK) { mp_clear_multi(key->pubkey.x, key->pubkey.y, key->pubkey.z, key->k, NULL); return err; } TLS_TOMCRYPT_PRIVATE_SET_INDEX(key, -1); TLS_TOMCRYPT_PRIVATE_DP(key) = dp; /* set z */ if ((err = mp_set(key->pubkey.z, 1)) != CRYPT_OK) { mp_clear_multi(key->pubkey.x, key->pubkey.y, key->pubkey.z, key->k, NULL); return err; } /* is it a point on the curve? */ if ((err = _private_tls_is_point(key)) != CRYPT_OK) { DEBUG_PRINT("KEY IS NOT ON CURVE\n"); mp_clear_multi(key->pubkey.x, key->pubkey.y, key->pubkey.z, key->k, NULL); return err; } /* we're good */ return CRYPT_OK; } int _private_tls_verify_ecdsa(struct TLSContext *context, unsigned int hash_type, const unsigned char *buffer, unsigned int len, const unsigned char *message, unsigned int message_len, const struct ECCCurveParameters *curve_hint) { tls_init(); ecc_key key; int err; if (!curve_hint) curve_hint = context->curve; if (context->is_server) { if ((!len) || (!context->client_certificates) || (!context->client_certificates_count) || (!context->client_certificates[0]) || (!context->client_certificates[0]->pk) || (!context->client_certificates[0]->pk_len) || (!curve_hint)) { DEBUG_PRINT("No client certificate set\n"); return TLS_GENERIC_ERROR; } err = _private_tls_ecc_import_pk(context->client_certificates[0]->pk, context->client_certificates[0]->pk_len, &key, (ltc_ecc_set_type *)&curve_hint->dp); } else { if ((!len) || (!context->certificates) || (!context->certificates_count) || (!context->certificates[0]) || (!context->certificates[0]->pk) || (!context->certificates[0]->pk_len) || (!curve_hint)) { DEBUG_PRINT("No server certificate set\n"); return TLS_GENERIC_ERROR; } err = _private_tls_ecc_import_pk(context->certificates[0]->pk, context->certificates[0]->pk_len, &key, (ltc_ecc_set_type *)&curve_hint->dp); } if (err) { DEBUG_PRINT("Error importing ECC certificate (code: %i)", err); return TLS_GENERIC_ERROR; } int hash_idx = -1; unsigned char hash[TLS_MAX_HASH_LEN]; unsigned int hash_len = 0; hash_state state; switch (hash_type) { case md5: hash_idx = find_hash("md5"); err = md5_init(&state); if (!err) { err = md5_process(&state, message, message_len); if (!err) err = md5_done(&state, hash); } hash_len = 16; break; case sha1: hash_idx = find_hash("sha1"); err = sha1_init(&state); if (!err) { err = sha1_process(&state, message, message_len); if (!err) err = sha1_done(&state, hash); } hash_len = 20; break; case sha256: hash_idx = find_hash("sha256"); err = sha256_init(&state); if (!err) { err = sha256_process(&state, message, message_len); if (!err) err = sha256_done(&state, hash); } hash_len = 32; break; case sha384: hash_idx = find_hash("sha384"); err = sha384_init(&state); if (!err) { err = sha384_process(&state, message, message_len); if (!err) err = sha384_done(&state, hash); } hash_len = 48; break; case sha512: hash_idx = find_hash("sha512"); err = sha512_init(&state); if (!err) { err = sha512_process(&state, message, message_len); if (!err) err = sha512_done(&state, hash); } hash_len = 64; break; #ifdef TLS_LEGACY_SUPPORT case _md5_sha1: hash_idx = find_hash("md5"); err = md5_init(&state); if (!err) { err = md5_process(&state, message, message_len); if (!err) err = md5_done(&state, hash); } hash_idx = find_hash("sha1"); err = sha1_init(&state); if (!err) { err = sha1_process(&state, message, message_len); if (!err) err = sha1_done(&state, hash + 16); } hash_len = 36; err = sha1_init(&state); if (!err) { err = sha1_process(&state, message, message_len); if (!err) err = sha1_done(&state, hash + 16); } hash_len = 36; break; #endif } if ((hash_idx < 0) || (err)) { DEBUG_PRINT("Unsupported hash type: %i\n", hash_type); return TLS_GENERIC_ERROR; } int ecc_stat = 0; err = ecc_verify_hash(buffer, len, hash, hash_len, &ecc_stat, &key); ecc_free(&key); if (err) return 0; return ecc_stat; } #endif #endif unsigned int _private_tls_random_int(int limit) { unsigned int res = 0; tls_random((unsigned char *)&res, sizeof(int)); if (limit) res %= limit; return res; } void _private_tls_sleep(unsigned int microseconds) { #ifdef _WIN32 Sleep(microseconds/1000); #else struct timespec ts; ts.tv_sec = (unsigned int) (microseconds / 1000000); ts.tv_nsec = (unsigned int) (microseconds % 1000000) * 1000ul; nanosleep(&ts, NULL); #endif } void _private_random_sleep(struct TLSContext *context, int max_microseconds) { if (context) context->sleep_until = (unsigned int)time(NULL) + _private_tls_random_int(max_microseconds/1000000 * TLS_MAX_ERROR_IDLE_S); else _private_tls_sleep(_private_tls_random_int(max_microseconds)); } void _private_tls_prf_helper(int hash_idx, unsigned long dlen, unsigned char *output, unsigned int outlen, const unsigned char *secret, const unsigned int secret_len, const unsigned char *label, unsigned int label_len, unsigned char *seed, unsigned int seed_len, unsigned char *seed_b, unsigned int seed_b_len) { unsigned char digest_out0[TLS_MAX_HASH_LEN]; unsigned char digest_out1[TLS_MAX_HASH_LEN]; unsigned int i; hmac_state hmac; hmac_init(&hmac, hash_idx, secret, secret_len); hmac_process(&hmac, label, label_len); hmac_process(&hmac, seed, seed_len); if ((seed_b) && (seed_b_len)) hmac_process(&hmac, seed_b, seed_b_len); hmac_done(&hmac, digest_out0, &dlen); int idx = 0; while (outlen) { hmac_init(&hmac, hash_idx, secret, secret_len); hmac_process(&hmac, digest_out0, dlen); hmac_process(&hmac, label, label_len); hmac_process(&hmac, seed, seed_len); if ((seed_b) && (seed_b_len)) hmac_process(&hmac, seed_b, seed_b_len); hmac_done(&hmac, digest_out1, &dlen); unsigned int copylen = outlen; if (copylen > dlen) copylen = dlen; for (i = 0; i < copylen; i++) { output[idx++] ^= digest_out1[i]; outlen--; } if (!outlen) break; hmac_init(&hmac, hash_idx, secret, secret_len); hmac_process(&hmac, digest_out0, dlen); hmac_done(&hmac, digest_out0, &dlen); } } #ifdef WITH_TLS_13 int _private_tls_hkdf_label(const char *label, unsigned char label_len, const unsigned char *data, unsigned char data_len, unsigned char *hkdflabel, unsigned short length, const char *prefix) { *(unsigned short *)hkdflabel = htons(length); int prefix_len; if (prefix) { prefix_len = (int)strlen(prefix); memcpy(&hkdflabel[3], prefix, prefix_len); } else { memcpy(&hkdflabel[3], "tls13 ", 6); prefix_len = 6; } hkdflabel[2] = (unsigned char)prefix_len + label_len; memcpy(&hkdflabel[3 + prefix_len], label, label_len); hkdflabel[3 + prefix_len + label_len] = (unsigned char)data_len; if (data_len) memcpy(&hkdflabel[4 + prefix_len + label_len], data, data_len); return 4 + prefix_len + label_len + data_len; } int _private_tls_hkdf_extract(unsigned int mac_length, unsigned char *output, unsigned int outlen, const unsigned char *salt, unsigned int salt_len, const unsigned char *ikm, unsigned char ikm_len) { unsigned long dlen = outlen; static unsigned char dummy_label[1] = { 0 }; if ((!salt) || (salt_len == 0)) { salt_len = 1; salt = dummy_label; } int hash_idx; if (mac_length == TLS_SHA384_MAC_SIZE) { hash_idx = find_hash("sha384"); dlen = mac_length; } else hash_idx = find_hash("sha256"); hmac_state hmac; hmac_init(&hmac, hash_idx, salt, salt_len); hmac_process(&hmac, ikm, ikm_len); hmac_done(&hmac, output, &dlen); DEBUG_DUMP_HEX_LABEL("EXTRACT", output, dlen); return dlen; } void _private_tls_hkdf_expand(unsigned int mac_length, unsigned char *output, unsigned int outlen, const unsigned char *secret, unsigned int secret_len, const unsigned char *info, unsigned char info_len) { unsigned char digest_out[TLS_MAX_HASH_LEN]; unsigned long dlen = 32; int hash_idx; if (mac_length == TLS_SHA384_MAC_SIZE) { hash_idx = find_hash("sha384"); dlen = mac_length; } else hash_idx = find_hash("sha256"); unsigned int i; unsigned int idx = 0; hmac_state hmac; unsigned char i2 = 0; while (outlen) { hmac_init(&hmac, hash_idx, secret, secret_len); if (i2) hmac_process(&hmac, digest_out, dlen); if ((info) && (info_len)) hmac_process(&hmac, info, info_len); i2++; hmac_process(&hmac, &i2, 1); hmac_done(&hmac, digest_out, &dlen); unsigned int copylen = outlen; if (copylen > dlen) copylen = (unsigned int)dlen; for (i = 0; i < copylen; i++) { output[idx++] = digest_out[i]; outlen--; } if (!outlen) break; } } void _private_tls_hkdf_expand_label(unsigned int mac_length, unsigned char *output, unsigned int outlen, const unsigned char *secret, unsigned int secret_len, const char *label, unsigned char label_len, const unsigned char *data, unsigned char data_len) { unsigned char hkdf_label[512]; int len = _private_tls_hkdf_label(label, label_len, data, data_len, hkdf_label, outlen, NULL); DEBUG_DUMP_HEX_LABEL("INFO", hkdf_label, len); _private_tls_hkdf_expand(mac_length, output, outlen, secret, secret_len, hkdf_label, len); } #endif void _private_tls_prf(struct TLSContext *context, unsigned char *output, unsigned int outlen, const unsigned char *secret, const unsigned int secret_len, const unsigned char *label, unsigned int label_len, unsigned char *seed, unsigned int seed_len, unsigned char *seed_b, unsigned int seed_b_len) { if ((!secret) || (!secret_len)) { DEBUG_PRINT("NULL SECRET\n"); return; } if ((context->version != TLS_V12) && (context->version != DTLS_V12)) { int md5_hash_idx = find_hash("md5"); int sha1_hash_idx = find_hash("sha1"); int half_secret = (secret_len + 1) / 2; memset(output, 0, outlen); _private_tls_prf_helper(md5_hash_idx, 16, output, outlen, secret, half_secret, label, label_len, seed, seed_len, seed_b, seed_b_len); _private_tls_prf_helper(sha1_hash_idx, 20, output, outlen, secret + (secret_len - half_secret), secret_len - half_secret, label, label_len, seed, seed_len, seed_b, seed_b_len); } else { // sha256_hmac unsigned char digest_out0[TLS_MAX_HASH_LEN]; unsigned char digest_out1[TLS_MAX_HASH_LEN]; unsigned long dlen = 32; int hash_idx; unsigned int mac_length = _private_tls_mac_length(context); if (mac_length == TLS_SHA384_MAC_SIZE) { hash_idx = find_hash("sha384"); dlen = mac_length; } else hash_idx = find_hash("sha256"); unsigned int i; hmac_state hmac; hmac_init(&hmac, hash_idx, secret, secret_len); hmac_process(&hmac, label, label_len); hmac_process(&hmac, seed, seed_len); if ((seed_b) && (seed_b_len)) hmac_process(&hmac, seed_b, seed_b_len); hmac_done(&hmac, digest_out0, &dlen); int idx = 0; while (outlen) { hmac_init(&hmac, hash_idx, secret, secret_len); hmac_process(&hmac, digest_out0, dlen); hmac_process(&hmac, label, label_len); hmac_process(&hmac, seed, seed_len); if ((seed_b) && (seed_b_len)) hmac_process(&hmac, seed_b, seed_b_len); hmac_done(&hmac, digest_out1, &dlen); unsigned int copylen = outlen; if (copylen > dlen) copylen = (unsigned int)dlen; for (i = 0; i < copylen; i++) { output[idx++] = digest_out1[i]; outlen--; } if (!outlen) break; hmac_init(&hmac, hash_idx, secret, secret_len); hmac_process(&hmac, digest_out0, dlen); hmac_done(&hmac, digest_out0, &dlen); } } } int _private_tls_key_length(struct TLSContext *context) { switch (context->cipher) { case TLS_RSA_WITH_AES_128_CBC_SHA: case TLS_RSA_WITH_AES_128_CBC_SHA256: case TLS_RSA_WITH_AES_128_GCM_SHA256: case TLS_DHE_RSA_WITH_AES_128_CBC_SHA: case TLS_DHE_RSA_WITH_AES_128_CBC_SHA256: case TLS_DHE_RSA_WITH_AES_128_GCM_SHA256: case TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA: case TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256: case TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256: case TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA: case TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256: case TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256: case TLS_AES_128_GCM_SHA256: return 16; case TLS_RSA_WITH_AES_256_CBC_SHA: case TLS_RSA_WITH_AES_256_CBC_SHA256: case TLS_RSA_WITH_AES_256_GCM_SHA384: case TLS_DHE_RSA_WITH_AES_256_CBC_SHA: case TLS_DHE_RSA_WITH_AES_256_CBC_SHA256: case TLS_DHE_RSA_WITH_AES_256_GCM_SHA384: case TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA: case TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384: case TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA: case TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384: case TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384: case TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256: case TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256: case TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256: case TLS_AES_256_GCM_SHA384: case TLS_CHACHA20_POLY1305_SHA256: return 32; } return 0; } int _private_tls_is_aead(struct TLSContext *context) { switch (context->cipher) { case TLS_RSA_WITH_AES_128_GCM_SHA256: case TLS_RSA_WITH_AES_256_GCM_SHA384: case TLS_DHE_RSA_WITH_AES_128_GCM_SHA256: case TLS_DHE_RSA_WITH_AES_256_GCM_SHA384: case TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256: case TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384: case TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256: case TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384: case TLS_AES_128_GCM_SHA256: case TLS_AES_256_GCM_SHA384: return 1; case TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256: case TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256: case TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256: case TLS_CHACHA20_POLY1305_SHA256: return 2; } return 0; } unsigned int _private_tls_mac_length(struct TLSContext *context) { switch (context->cipher) { case TLS_RSA_WITH_AES_128_CBC_SHA: case TLS_RSA_WITH_AES_256_CBC_SHA: case TLS_DHE_RSA_WITH_AES_128_CBC_SHA: case TLS_DHE_RSA_WITH_AES_256_CBC_SHA: case TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA: case TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA: case TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA: case TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA: return TLS_SHA1_MAC_SIZE; case TLS_RSA_WITH_AES_128_CBC_SHA256: case TLS_RSA_WITH_AES_256_CBC_SHA256: case TLS_RSA_WITH_AES_128_GCM_SHA256: case TLS_DHE_RSA_WITH_AES_128_CBC_SHA256: case TLS_DHE_RSA_WITH_AES_256_CBC_SHA256: case TLS_DHE_RSA_WITH_AES_128_GCM_SHA256: case TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256: case TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256: case TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256: case TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256: case TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256: case TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256: case TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256: #ifdef WITH_TLS_13 case TLS_AES_128_GCM_SHA256: case TLS_CHACHA20_POLY1305_SHA256: case TLS_AES_128_CCM_SHA256: case TLS_AES_128_CCM_8_SHA256: #endif return TLS_SHA256_MAC_SIZE; case TLS_RSA_WITH_AES_256_GCM_SHA384: case TLS_DHE_RSA_WITH_AES_256_GCM_SHA384: case TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384: case TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384: case TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384: #ifdef WITH_TLS_13 case TLS_AES_256_GCM_SHA384: #endif return TLS_SHA384_MAC_SIZE; } return 0; } #ifdef WITH_TLS_13 int _private_tls13_key(struct TLSContext *context, int handshake) { tls_init(); int key_length = _private_tls_key_length(context); unsigned int mac_length = _private_tls_mac_length(context); if ((!context->premaster_key) || (!context->premaster_key_len)) return 0; if ((!key_length) || (!mac_length)) { DEBUG_PRINT("KEY EXPANSION FAILED, KEY LENGTH: %i, MAC LENGTH: %i\n", key_length, mac_length); return 0; } unsigned char *clientkey = NULL; unsigned char *serverkey = NULL; unsigned char *clientiv = NULL; unsigned char *serveriv = NULL; int is_aead = _private_tls_is_aead(context); unsigned char local_keybuffer[TLS_V13_MAX_KEY_SIZE]; unsigned char local_ivbuffer[TLS_V13_MAX_IV_SIZE]; unsigned char remote_keybuffer[TLS_V13_MAX_KEY_SIZE]; unsigned char remote_ivbuffer[TLS_V13_MAX_IV_SIZE]; unsigned char prk[TLS_MAX_HASH_SIZE]; unsigned char hash[TLS_MAX_HASH_SIZE]; static unsigned char earlysecret[TLS_MAX_HASH_SIZE]; const char *server_key = "s ap traffic"; const char *client_key = "c ap traffic"; if (handshake) { server_key = "s hs traffic"; client_key = "c hs traffic"; } unsigned char salt[TLS_MAX_HASH_SIZE]; hash_state md; if (mac_length == TLS_SHA384_MAC_SIZE) { sha384_init(&md); sha384_done(&md, hash); } else { sha256_init(&md); sha256_done(&md, hash); } // extract secret "early" if ((context->master_key) && (context->master_key_len) && (!handshake)) { DEBUG_DUMP_HEX_LABEL("USING PREVIOUS SECRET", context->master_key, context->master_key_len); _private_tls_hkdf_expand_label(mac_length, salt, mac_length, context->master_key, context->master_key_len, "derived", 7, hash, mac_length); DEBUG_DUMP_HEX_LABEL("salt", salt, mac_length); _private_tls_hkdf_extract(mac_length, prk, mac_length, salt, mac_length, earlysecret, mac_length); } else { _private_tls_hkdf_extract(mac_length, prk, mac_length, NULL, 0, earlysecret, mac_length); // derive secret for handshake "tls13 derived": DEBUG_DUMP_HEX_LABEL("null hash", hash, mac_length); _private_tls_hkdf_expand_label(mac_length, salt, mac_length, prk, mac_length, "derived", 7, hash, mac_length); // extract secret "handshake": DEBUG_DUMP_HEX_LABEL("salt", salt, mac_length); _private_tls_hkdf_extract(mac_length, prk, mac_length, salt, mac_length, context->premaster_key, context->premaster_key_len); } if (!is_aead) { DEBUG_PRINT("KEY EXPANSION FAILED, NON AEAD CIPHER\n"); return 0; } unsigned char secret[TLS_MAX_MAC_SIZE]; unsigned char hs_secret[TLS_MAX_HASH_SIZE]; int hash_size; if (handshake) hash_size = _private_tls_get_hash(context, hash); else hash_size = _private_tls_done_hash(context, hash); DEBUG_DUMP_HEX_LABEL("messages hash", hash, hash_size); if (context->is_server) { _private_tls_hkdf_expand_label(mac_length, hs_secret, mac_length, prk, mac_length, server_key, 12, context->server_finished_hash ? context->server_finished_hash : hash, hash_size); DEBUG_DUMP_HEX_LABEL(server_key, hs_secret, mac_length); serverkey = local_keybuffer; serveriv = local_ivbuffer; clientkey = remote_keybuffer; clientiv = remote_ivbuffer; } else { _private_tls_hkdf_expand_label(mac_length, hs_secret, mac_length, prk, mac_length, client_key, 12, context->server_finished_hash ? context->server_finished_hash : hash, hash_size); DEBUG_DUMP_HEX_LABEL(client_key, hs_secret, mac_length); serverkey = remote_keybuffer; serveriv = remote_ivbuffer; clientkey = local_keybuffer; clientiv = local_ivbuffer; } int iv_length = TLS_13_AES_GCM_IV_LENGTH; #ifdef TLS_WITH_CHACHA20_POLY1305 if (is_aead == 2) iv_length = TLS_CHACHA20_IV_LENGTH; #endif _private_tls_hkdf_expand_label(mac_length, local_keybuffer, key_length, hs_secret, mac_length, "key", 3, NULL, 0); _private_tls_hkdf_expand_label(mac_length, local_ivbuffer, iv_length, hs_secret, mac_length, "iv", 2, NULL, 0); if (context->is_server) _private_tls_hkdf_expand_label(mac_length, secret, mac_length, prk, mac_length, client_key, 12, context->server_finished_hash ? context->server_finished_hash : hash, hash_size); else _private_tls_hkdf_expand_label(mac_length, secret, mac_length, prk, mac_length, server_key, 12, context->server_finished_hash ? context->server_finished_hash : hash, hash_size); _private_tls_hkdf_expand_label(mac_length, remote_keybuffer, key_length, secret, mac_length, "key", 3, NULL, 0); _private_tls_hkdf_expand_label(mac_length, remote_ivbuffer, iv_length, secret, mac_length, "iv", 2, NULL, 0); DEBUG_DUMP_HEX_LABEL("CLIENT KEY", clientkey, key_length) DEBUG_DUMP_HEX_LABEL("CLIENT IV", clientiv, iv_length) DEBUG_DUMP_HEX_LABEL("SERVER KEY", serverkey, key_length) DEBUG_DUMP_HEX_LABEL("SERVER IV", serveriv, iv_length) TLS_FREE(context->finished_key); TLS_FREE(context->remote_finished_key); if (handshake) { context->finished_key = (unsigned char *)TLS_MALLOC(mac_length); context->remote_finished_key = (unsigned char *)TLS_MALLOC(mac_length); if (context->finished_key) { _private_tls_hkdf_expand_label(mac_length, context->finished_key, mac_length, hs_secret, mac_length, "finished", 8, NULL, 0); DEBUG_DUMP_HEX_LABEL("FINISHED", context->finished_key, mac_length) } if (context->remote_finished_key) { _private_tls_hkdf_expand_label(mac_length, context->remote_finished_key, mac_length, secret, mac_length, "finished", 8, NULL, 0); DEBUG_DUMP_HEX_LABEL("REMOTE FINISHED", context->remote_finished_key, mac_length) } } else { context->finished_key = NULL; context->remote_finished_key = NULL; TLS_FREE(context->server_finished_hash); context->server_finished_hash = NULL; } if (context->is_server) { #ifdef TLS_WITH_CHACHA20_POLY1305 if (is_aead == 2) { memcpy(context->crypto.ctx_remote_mac.remote_nonce, clientiv, iv_length); memcpy(context->crypto.ctx_local_mac.local_nonce, serveriv, iv_length); } else #endif if (is_aead) { memcpy(context->crypto.ctx_remote_mac.remote_iv, clientiv, iv_length); memcpy(context->crypto.ctx_local_mac.local_iv, serveriv, iv_length); } if (_private_tls_crypto_create(context, key_length, serverkey, serveriv, clientkey, clientiv)) return 0; } else { #ifdef TLS_WITH_CHACHA20_POLY1305 if (is_aead == 2) { memcpy(context->crypto.ctx_local_mac.local_nonce, clientiv, iv_length); memcpy(context->crypto.ctx_remote_mac.remote_nonce, serveriv, iv_length); } else #endif if (is_aead) { memcpy(context->crypto.ctx_local_mac.local_iv, clientiv, iv_length); memcpy(context->crypto.ctx_remote_mac.remote_iv, serveriv, iv_length); } if (_private_tls_crypto_create(context, key_length, clientkey, clientiv, serverkey, serveriv)) return 0; } context->crypto.created = 1 + is_aead; if (context->exportable) { TLS_FREE(context->exportable_keys); context->exportable_keys = (unsigned char *)TLS_MALLOC(key_length * 2); if (context->exportable_keys) { if (context->is_server) { memcpy(context->exportable_keys, serverkey, key_length); memcpy(context->exportable_keys + key_length, clientkey, key_length); } else { memcpy(context->exportable_keys, clientkey, key_length); memcpy(context->exportable_keys + key_length, serverkey, key_length); } context->exportable_size = key_length * 2; } } TLS_FREE(context->master_key); context->master_key = (unsigned char *)TLS_MALLOC(mac_length); if (context->master_key) { memcpy(context->master_key, prk, mac_length); context->master_key_len = mac_length; } context->local_sequence_number = 0; context->remote_sequence_number = 0; // extract client_mac_key(mac_key_length) // extract server_mac_key(mac_key_length) // extract client_key(enc_key_length) // extract server_key(enc_key_length) // extract client_iv(fixed_iv_lengh) // extract server_iv(fixed_iv_length) return 1; } #endif int _private_tls_expand_key(struct TLSContext *context) { unsigned char key[TLS_MAX_KEY_EXPANSION_SIZE]; #ifdef WITH_TLS_13 if ((context->version == TLS_V13) || (context->version == DTLS_V13)) return 0; #endif if ((!context->master_key) || (!context->master_key_len)) return 0; int key_length = _private_tls_key_length(context); int mac_length = _private_tls_mac_length(context); if ((!key_length) || (!mac_length)) { DEBUG_PRINT("KEY EXPANSION FAILED, KEY LENGTH: %i, MAC LENGTH: %i\n", key_length, mac_length); return 0; } unsigned char *clientkey = NULL; unsigned char *serverkey = NULL; unsigned char *clientiv = NULL; unsigned char *serveriv = NULL; int iv_length = TLS_AES_IV_LENGTH; int is_aead = _private_tls_is_aead(context); if (context->is_server) _private_tls_prf(context, key, sizeof(key), context->master_key, context->master_key_len, (unsigned char *)"key expansion", 13, context->local_random, TLS_SERVER_RANDOM_SIZE, context->remote_random, TLS_CLIENT_RANDOM_SIZE); else _private_tls_prf(context, key, sizeof(key), context->master_key, context->master_key_len, (unsigned char *)"key expansion", 13, context->remote_random, TLS_SERVER_RANDOM_SIZE, context->local_random, TLS_CLIENT_RANDOM_SIZE); DEBUG_DUMP_HEX_LABEL("LOCAL RANDOM ", context->local_random, TLS_SERVER_RANDOM_SIZE); DEBUG_DUMP_HEX_LABEL("REMOTE RANDOM", context->remote_random, TLS_CLIENT_RANDOM_SIZE); DEBUG_PRINT("\n=========== EXPANSION ===========\n"); DEBUG_DUMP_HEX(key, TLS_MAX_KEY_EXPANSION_SIZE); DEBUG_PRINT("\n"); int pos = 0; #ifdef TLS_WITH_CHACHA20_POLY1305 if (is_aead == 2) { iv_length = TLS_CHACHA20_IV_LENGTH; } else #endif if (is_aead) { iv_length = TLS_AES_GCM_IV_LENGTH; } else { if (context->is_server) { memcpy(context->crypto.ctx_remote_mac.remote_mac, &key[pos], mac_length); pos += mac_length; memcpy(context->crypto.ctx_local_mac.local_mac, &key[pos], mac_length); pos += mac_length; } else { memcpy(context->crypto.ctx_local_mac.local_mac, &key[pos], mac_length); pos += mac_length; memcpy(context->crypto.ctx_remote_mac.remote_mac, &key[pos], mac_length); pos += mac_length; } } clientkey = &key[pos]; pos += key_length; serverkey = &key[pos]; pos += key_length; clientiv = &key[pos]; pos += iv_length; serveriv = &key[pos]; pos += iv_length; DEBUG_PRINT("EXPANSION %i/%i\n", (int)pos, (int)TLS_MAX_KEY_EXPANSION_SIZE); DEBUG_DUMP_HEX_LABEL("CLIENT KEY", clientkey, key_length) DEBUG_DUMP_HEX_LABEL("CLIENT IV", clientiv, iv_length) DEBUG_DUMP_HEX_LABEL("CLIENT MAC KEY", context->is_server ? context->crypto.ctx_remote_mac.remote_mac : context->crypto.ctx_local_mac.local_mac, mac_length) DEBUG_DUMP_HEX_LABEL("SERVER KEY", serverkey, key_length) DEBUG_DUMP_HEX_LABEL("SERVER IV", serveriv, iv_length) DEBUG_DUMP_HEX_LABEL("SERVER MAC KEY", context->is_server ? context->crypto.ctx_local_mac.local_mac : context->crypto.ctx_remote_mac.remote_mac, mac_length) if (context->is_server) { #ifdef TLS_WITH_CHACHA20_POLY1305 if (is_aead == 2) { memcpy(context->crypto.ctx_remote_mac.remote_nonce, clientiv, iv_length); memcpy(context->crypto.ctx_local_mac.local_nonce, serveriv, iv_length); } else #endif if (is_aead) { memcpy(context->crypto.ctx_remote_mac.remote_aead_iv, clientiv, iv_length); memcpy(context->crypto.ctx_local_mac.local_aead_iv, serveriv, iv_length); } if (_private_tls_crypto_create(context, key_length, serverkey, serveriv, clientkey, clientiv)) return 0; } else { #ifdef TLS_WITH_CHACHA20_POLY1305 if (is_aead == 2) { memcpy(context->crypto.ctx_local_mac.local_nonce, clientiv, iv_length); memcpy(context->crypto.ctx_remote_mac.remote_nonce, serveriv, iv_length); } else #endif if (is_aead) { memcpy(context->crypto.ctx_local_mac.local_aead_iv, clientiv, iv_length); memcpy(context->crypto.ctx_remote_mac.remote_aead_iv, serveriv, iv_length); } if (_private_tls_crypto_create(context, key_length, clientkey, clientiv, serverkey, serveriv)) return 0; } if (context->exportable) { TLS_FREE(context->exportable_keys); context->exportable_keys = (unsigned char *)TLS_MALLOC(key_length * 2); if (context->exportable_keys) { if (context->is_server) { memcpy(context->exportable_keys, serverkey, key_length); memcpy(context->exportable_keys + key_length, clientkey, key_length); } else { memcpy(context->exportable_keys, clientkey, key_length); memcpy(context->exportable_keys + key_length, serverkey, key_length); } context->exportable_size = key_length * 2; } } // extract client_mac_key(mac_key_length) // extract server_mac_key(mac_key_length) // extract client_key(enc_key_length) // extract server_key(enc_key_length) // extract client_iv(fixed_iv_lengh) // extract server_iv(fixed_iv_length) return 1; } int _private_tls_compute_key(struct TLSContext *context, unsigned int key_len) { #ifdef WITH_TLS_13 if ((context->version == TLS_V13) || (context->version == DTLS_V13)) return 0; #endif if ((!context->premaster_key) || (!context->premaster_key_len) || (key_len < 48)) { DEBUG_PRINT("CANNOT COMPUTE MASTER SECRET\n"); return 0; } unsigned char master_secret_label[] = "master secret"; #ifdef TLS_CHECK_PREMASTER_KEY if (!tls_cipher_is_ephemeral(context)) { unsigned short version = ntohs(*(unsigned short *)context->premaster_key); // this check is not true for DHE/ECDHE ciphers if (context->version > version) { DEBUG_PRINT("Mismatch protocol version 0x(%x)\n", version); return 0; } } #endif TLS_FREE(context->master_key); context->master_key_len = 0; context->master_key = NULL; if ((context->version == TLS_V13) || (context->version == TLS_V12) || (context->version == TLS_V11) || (context->version == TLS_V10) || (context->version == DTLS_V13) || (context->version == DTLS_V12) || (context->version == DTLS_V10)) { context->master_key = (unsigned char *)TLS_MALLOC(key_len); if (!context->master_key) return 0; context->master_key_len = key_len; if (context->is_server) { _private_tls_prf(context, context->master_key, context->master_key_len, context->premaster_key, context->premaster_key_len, master_secret_label, 13, context->remote_random, TLS_CLIENT_RANDOM_SIZE, context->local_random, TLS_SERVER_RANDOM_SIZE ); } else { _private_tls_prf(context, context->master_key, context->master_key_len, context->premaster_key, context->premaster_key_len, master_secret_label, 13, context->local_random, TLS_CLIENT_RANDOM_SIZE, context->remote_random, TLS_SERVER_RANDOM_SIZE ); } TLS_FREE(context->premaster_key); context->premaster_key = NULL; context->premaster_key_len = 0; DEBUG_PRINT("\n=========== Master key ===========\n"); DEBUG_DUMP_HEX(context->master_key, context->master_key_len); DEBUG_PRINT("\n"); _private_tls_expand_key(context); return 1; } return 0; } unsigned char *tls_pem_decode(const unsigned char *data_in, unsigned int input_length, int cert_index, unsigned int *output_len) { unsigned int i; *output_len = 0; int alloc_len = input_length / 4 * 3; unsigned char *output = (unsigned char *)TLS_MALLOC(alloc_len); if (!output) return NULL; unsigned int start_at = 0; unsigned int idx = 0; for (i = 0; i < input_length; i++) { if ((data_in[i] == '\n') || (data_in[i] == '\r')) continue; if (data_in[i] != '-') { // read entire line while ((i < input_length) && (data_in[i] != '\n')) i++; continue; } if (data_in[i] == '-') { unsigned int end_idx = i; //read until end of line while ((i < input_length) && (data_in[i] != '\n')) i++; if (start_at) { if (cert_index > 0) { cert_index--; start_at = 0; } else { idx = _private_b64_decode((const char *)&data_in[start_at], end_idx - start_at, output); break; } } else start_at = i + 1; } } *output_len = idx; if (!idx) { TLS_FREE(output); return NULL; } return output; } int _is_oid(const unsigned char *oid, const unsigned char *compare_to, int compare_to_len) { int i = 0; while ((oid[i]) && (i < compare_to_len)) { if (oid[i] != compare_to[i]) return 0; i++; } return 1; } int _is_oid2(const unsigned char *oid, const unsigned char *compare_to, int compare_to_len, int oid_len) { int i = 0; if (oid_len < compare_to_len) compare_to_len = oid_len; while (i < compare_to_len) { if (oid[i] != compare_to[i]) return 0; i++; } return 1; } struct TLSCertificate *tls_create_certificate() { struct TLSCertificate *cert = (struct TLSCertificate *)TLS_MALLOC(sizeof(struct TLSCertificate)); if (cert) memset(cert, 0, sizeof(struct TLSCertificate)); return cert; } int tls_certificate_valid_subject_name(const unsigned char *cert_subject, const char *subject) { // no subjects ... if (((!cert_subject) || (!cert_subject[0])) && ((!subject) || (!subject[0]))) return 0; if ((!subject) || (!subject[0])) return bad_certificate; if ((!cert_subject) || (!cert_subject[0])) return bad_certificate; // exact match if (!strcmp((const char *)cert_subject, subject)) return 0; const char *wildcard = strchr((const char *)cert_subject, '*'); if (wildcard) { // 6.4.3 (1) The client SHOULD NOT attempt to match a presented identifier in // which the wildcard character comprises a label other than the left-most label if (!wildcard[1]) { // subject is [*] // or // subject is [something*] .. invalid return bad_certificate; } wildcard++; const char *match = strstr(subject, wildcard); if ((!match) && (wildcard[0] == '.')) { // check *.domain.com agains domain.com wildcard++; if (!strcasecmp(subject, wildcard)) return 0; } if (match) { uintptr_t offset = (uintptr_t)match - (uintptr_t)subject; if (offset) { // check for foo.*.domain.com against *.domain.com (invalid) if (memchr(subject, '.', offset)) return bad_certificate; } // check if exact match if (!strcasecmp(match, wildcard)) return 0; } } return bad_certificate; } int tls_certificate_valid_subject(struct TLSCertificate *cert, const char *subject) { int i; if (!cert) return certificate_unknown; int err = tls_certificate_valid_subject_name(cert->subject, subject); if ((err) && (cert->san)) { for (i = 0; i < cert->san_length; i++) { err = tls_certificate_valid_subject_name(cert->san[i], subject); if (!err) return err; } } return err; } int tls_certificate_is_valid(struct TLSCertificate *cert) { if (!cert) return certificate_unknown; if (!cert->not_before) return certificate_unknown; if (!cert->not_after) return certificate_unknown; //20160224182300Z// char current_time[16]; time_t t = time(NULL); struct tm *utct = gmtime(&t); if (utct) { current_time[0] = 0; snprintf(current_time, sizeof(current_time), "%04d%02d%02d%02d%02d%02dZ", 1900 + utct->tm_year, utct->tm_mon + 1, utct->tm_mday, utct->tm_hour, utct->tm_min, utct->tm_sec); if (strcasecmp((char *)cert->not_before, current_time) > 0) { DEBUG_PRINT("Certificate is not yer valid, now: %s (validity: %s - %s)\n", current_time, cert->not_before, cert->not_after); return certificate_expired; } if (strcasecmp((char *)cert->not_after, current_time) < 0) { DEBUG_PRINT("Expired certificate, now: %s (validity: %s - %s)\n", current_time, cert->not_before, cert->not_after); return certificate_expired; } DEBUG_PRINT("Valid certificate, now: %s (validity: %s - %s)\n", current_time, cert->not_before, cert->not_after); } return 0; } void tls_certificate_set_copy(unsigned char **member, const unsigned char *val, int len) { if (!member) return; TLS_FREE(*member); if (len) { *member = (unsigned char *)TLS_MALLOC(len + 1); if (*member) { memcpy(*member, val, len); (*member)[len] = 0; } } else *member = NULL; } void tls_certificate_set_copy_date(unsigned char **member, const unsigned char *val, int len) { if (!member) return; TLS_FREE(*member); if (len > 4) { *member = (unsigned char *)TLS_MALLOC(len + 3); if (*member) { if (val[0] == '9') { (*member)[0]='1'; (*member)[1]='9'; } else { (*member)[0]='2'; (*member)[1]='0'; } memcpy(*member + 2, val, len); (*member)[len] = 0; } } else *member = NULL; } void tls_certificate_set_key(struct TLSCertificate *cert, const unsigned char *val, int len) { if ((!val[0]) && (len % 2)) { val++; len--; } tls_certificate_set_copy(&cert->pk, val, len); if (cert->pk) cert->pk_len = len; } void tls_certificate_set_priv(struct TLSCertificate *cert, const unsigned char *val, int len) { tls_certificate_set_copy(&cert->priv, val, len); if (cert->priv) cert->priv_len = len; } void tls_certificate_set_sign_key(struct TLSCertificate *cert, const unsigned char *val, int len) { if ((!val[0]) && (len % 2)) { val++; len--; } tls_certificate_set_copy(&cert->sign_key, val, len); if (cert->sign_key) cert->sign_len = len; } char *tls_certificate_to_string(struct TLSCertificate *cert, char *buffer, int len) { unsigned int i; if (!buffer) return NULL; buffer[0] = 0; if (cert->version) { int res = snprintf(buffer, len, "X.509v%i certificate\n Issued by: [%s]%s (%s)\n Issued to: [%s]%s (%s, %s)\n Subject: %s\n Validity: %s - %s\n OCSP: %s\n Serial number: ", (int)cert->version, cert->issuer_country, cert->issuer_entity, cert->issuer_subject, cert->country, cert->entity, cert->state, cert->location, cert->subject, cert->not_before, cert->not_after, cert->ocsp ); if (res > 0) { for (i = 0; i < cert->serial_len; i++) res += snprintf(buffer + res, len - res, "%02x", (int)cert->serial_number[i]); } if ((cert->san) && (cert->san_length)) { res += snprintf(buffer + res, len - res, "\n Alternative subjects: "); for (i = 0; i < cert->san_length; i++) { if (i) res += snprintf(buffer + res, len - res, ", %s", cert->san[i]); else res += snprintf(buffer + res, len - res, "%s", cert->san[i]); } } res += snprintf(buffer + res, len - res, "\n Key (%i bits, ", cert->pk_len * 8); if (res > 0) { switch (cert->key_algorithm) { case TLS_RSA_SIGN_RSA: res += snprintf(buffer + res, len - res, "RSA_SIGN_RSA"); break; case TLS_RSA_SIGN_MD5: res += snprintf(buffer + res, len - res, "RSA_SIGN_MD5"); break; case TLS_RSA_SIGN_SHA1: res += snprintf(buffer + res, len - res, "RSA_SIGN_SHA1"); break; case TLS_RSA_SIGN_SHA256: res += snprintf(buffer + res, len - res, "RSA_SIGN_SHA256"); break; case TLS_RSA_SIGN_SHA384: res += snprintf(buffer + res, len - res, "RSA_SIGN_SHA384"); break; case TLS_RSA_SIGN_SHA512: res += snprintf(buffer + res, len - res, "RSA_SIGN_SHA512"); break; case TLS_ECDSA_SIGN_SHA256: res += snprintf(buffer + res, len - res, "ECDSA_SIGN_SHA512"); break; case TLS_EC_PUBLIC_KEY: res += snprintf(buffer + res, len - res, "EC_PUBLIC_KEY"); break; default: res += snprintf(buffer + res, len - res, "not supported (%i)", (int)cert->key_algorithm); } } if ((res > 0) && (cert->ec_algorithm)) { switch (cert->ec_algorithm) { case TLS_EC_prime192v1: res += snprintf(buffer + res, len - res, " prime192v1"); break; case TLS_EC_prime192v2: res += snprintf(buffer + res, len - res, " prime192v2"); break; case TLS_EC_prime192v3: res += snprintf(buffer + res, len - res, " prime192v3"); break; case TLS_EC_prime239v2: res += snprintf(buffer + res, len - res, " prime239v2"); break; case TLS_EC_secp256r1: res += snprintf(buffer + res, len - res, " EC_secp256r1"); break; case TLS_EC_secp224r1: res += snprintf(buffer + res, len - res, " EC_secp224r1"); break; case TLS_EC_secp384r1: res += snprintf(buffer + res, len - res, " EC_secp384r1"); break; case TLS_EC_secp521r1: res += snprintf(buffer + res, len - res, " EC_secp521r1"); break; default: res += snprintf(buffer + res, len - res, " unknown(%i)", (int)cert->ec_algorithm); } } res += snprintf(buffer + res, len - res, "):\n"); if (res > 0) { for (i = 0; i < cert->pk_len; i++) res += snprintf(buffer + res, len - res, "%02x", (int)cert->pk[i]); res += snprintf(buffer + res, len - res, "\n Signature (%i bits, ", cert->sign_len * 8); switch (cert->algorithm) { case TLS_RSA_SIGN_RSA: res += snprintf(buffer + res, len - res, "RSA_SIGN_RSA):\n"); break; case TLS_RSA_SIGN_MD5: res += snprintf(buffer + res, len - res, "RSA_SIGN_MD5):\n"); break; case TLS_RSA_SIGN_SHA1: res += snprintf(buffer + res, len - res, "RSA_SIGN_SHA1):\n"); break; case TLS_RSA_SIGN_SHA256: res += snprintf(buffer + res, len - res, "RSA_SIGN_SHA256):\n"); break; case TLS_RSA_SIGN_SHA384: res += snprintf(buffer + res, len - res, "RSA_SIGN_SHA384):\n"); break; case TLS_RSA_SIGN_SHA512: res += snprintf(buffer + res, len - res, "RSA_SIGN_SHA512):\n"); break; case TLS_EC_PUBLIC_KEY: res += snprintf(buffer + res, len - res, "EC_PUBLIC_KEY):\n"); break; default: res += snprintf(buffer + res, len - res, "not supported):\n"); } for (i = 0; i < cert->sign_len; i++) res += snprintf(buffer + res, len - res, "%02x", (int)cert->sign_key[i]); } } else if ((cert->priv) && (cert->priv_len)) { int res = snprintf(buffer, len, "X.509 private key\n"); res += snprintf(buffer + res, len - res, " Private Key: "); if (res > 0) { for (i = 0; i < cert->priv_len; i++) res += snprintf(buffer + res, len - res, "%02x", (int)cert->priv[i]); } } else snprintf(buffer, len, "Empty ASN1 file"); return buffer; } void tls_certificate_set_exponent(struct TLSCertificate *cert, const unsigned char *val, int len) { tls_certificate_set_copy(&cert->exponent, val, len); if (cert->exponent) cert->exponent_len = len; } void tls_certificate_set_serial(struct TLSCertificate *cert, const unsigned char *val, int len) { tls_certificate_set_copy(&cert->serial_number, val, len); if (cert->serial_number) cert->serial_len = len; } void tls_certificate_set_algorithm(struct TLSContext *context, unsigned int *algorithm, const unsigned char *val, int len) { if ((len == 7) && (_is_oid(val, TLS_EC_PUBLIC_KEY_OID, 7))) { *algorithm = TLS_EC_PUBLIC_KEY; return; } if (len == 8) { if (_is_oid(val, TLS_EC_prime192v1_OID, len)) { *algorithm = TLS_EC_prime192v1; return; } if (_is_oid(val, TLS_EC_prime192v2_OID, len)) { *algorithm = TLS_EC_prime192v2; return; } if (_is_oid(val, TLS_EC_prime192v3_OID, len)) { *algorithm = TLS_EC_prime192v3; return; } if (_is_oid(val, TLS_EC_prime239v1_OID, len)) { *algorithm = TLS_EC_prime239v1; return; } if (_is_oid(val, TLS_EC_prime239v2_OID, len)) { *algorithm = TLS_EC_prime239v2; return; } if (_is_oid(val, TLS_EC_prime239v3_OID, len)) { *algorithm = TLS_EC_prime239v3; return; } if (_is_oid(val, TLS_EC_prime256v1_OID, len)) { *algorithm = TLS_EC_prime256v1; return; } } if (len == 5) { if (_is_oid2(val, TLS_EC_secp224r1_OID, len, sizeof(TLS_EC_secp224r1_OID) - 1)) { *algorithm = TLS_EC_secp224r1; return; } if (_is_oid2(val, TLS_EC_secp384r1_OID, len, sizeof(TLS_EC_secp384r1_OID) - 1)) { *algorithm = TLS_EC_secp384r1; return; } if (_is_oid2(val, TLS_EC_secp521r1_OID, len, sizeof(TLS_EC_secp521r1_OID) - 1)) { *algorithm = TLS_EC_secp521r1; return; } } if (len != 9) return; if (_is_oid(val, TLS_RSA_SIGN_SHA256_OID, 9)) { *algorithm = TLS_RSA_SIGN_SHA256; return; } if (_is_oid(val, TLS_RSA_SIGN_RSA_OID, 9)) { *algorithm = TLS_RSA_SIGN_RSA; return; } if (_is_oid(val, TLS_RSA_SIGN_SHA1_OID, 9)) { *algorithm = TLS_RSA_SIGN_SHA1; return; } if (_is_oid(val, TLS_RSA_SIGN_SHA512_OID, 9)) { *algorithm = TLS_RSA_SIGN_SHA512; return; } if (_is_oid(val, TLS_RSA_SIGN_SHA384_OID, 9)) { *algorithm = TLS_RSA_SIGN_SHA384; return; } if (_is_oid(val, TLS_RSA_SIGN_MD5_OID, 9)) { *algorithm = TLS_RSA_SIGN_MD5; return; } if (_is_oid(val, TLS_ECDSA_SIGN_SHA256_OID, 9)) { *algorithm = TLS_ECDSA_SIGN_SHA256; return; } // client should fail on unsupported signature if (!context->is_server) { DEBUG_PRINT("UNSUPPORTED SIGNATURE ALGORITHM\n"); context->critical_error = 1; } } void tls_destroy_certificate(struct TLSCertificate *cert) { if (cert) { int i; TLS_FREE(cert->exponent); TLS_FREE(cert->pk); TLS_FREE(cert->issuer_country); TLS_FREE(cert->issuer_state); TLS_FREE(cert->issuer_location); TLS_FREE(cert->issuer_entity); TLS_FREE(cert->issuer_subject); TLS_FREE(cert->country); TLS_FREE(cert->state); TLS_FREE(cert->location); TLS_FREE(cert->subject); for (i = 0; i < cert->san_length; i++) { TLS_FREE(cert->san[i]); } TLS_FREE(cert->san); TLS_FREE(cert->ocsp); TLS_FREE(cert->serial_number); TLS_FREE(cert->entity); TLS_FREE(cert->not_before); TLS_FREE(cert->not_after); TLS_FREE(cert->sign_key); TLS_FREE(cert->priv); TLS_FREE(cert->der_bytes); TLS_FREE(cert->bytes); TLS_FREE(cert->fingerprint); TLS_FREE(cert); } } struct TLSPacket *tls_create_packet(struct TLSContext *context, unsigned char type, unsigned short version, int payload_size_hint) { struct TLSPacket *packet = (struct TLSPacket *)TLS_MALLOC(sizeof(struct TLSPacket)); if (!packet) return NULL; packet->broken = 0; if (payload_size_hint > 0) packet->size = payload_size_hint + 10; else packet->size = TLS_BLOB_INCREMENT; packet->buf = (unsigned char *)TLS_MALLOC(packet->size); packet->context = context; if (!packet->buf) { TLS_FREE(packet); return NULL; } if ((context) && (context->dtls)) packet->len = 13; else packet->len = 5; packet->buf[0] = type; #ifdef WITH_TLS_13 switch (version) { case TLS_V13: // check if context is not null. If null, is a tls_export_context call if (context) *(unsigned short *)(packet->buf + 1) = 0x0303; // no need to reorder (same bytes) else *(unsigned short *)(packet->buf + 1) = htons(version); break; case DTLS_V13: *(unsigned short *)(packet->buf + 1) = htons(DTLS_V13); break; default: *(unsigned short *)(packet->buf + 1) = htons(version); } #else *(unsigned short *)(packet->buf + 1) = htons(version); #endif return packet; } void tls_destroy_packet(struct TLSPacket *packet) { if (packet) { if (packet->buf) TLS_FREE(packet->buf); TLS_FREE(packet); } } int _private_tls_crypto_create(struct TLSContext *context, int key_length, unsigned char *localkey, unsigned char *localiv, unsigned char *remotekey, unsigned char *remoteiv) { if (context->crypto.created) { if (context->crypto.created == 1) { cbc_done(&context->crypto.ctx_remote.aes_remote); cbc_done(&context->crypto.ctx_local.aes_local); } else { #ifdef TLS_WITH_CHACHA20_POLY1305 if (context->crypto.created == 2) { #endif unsigned char dummy_buffer[32]; unsigned long tag_len = 0; gcm_done(&context->crypto.ctx_remote.aes_gcm_remote, dummy_buffer, &tag_len); gcm_done(&context->crypto.ctx_local.aes_gcm_local, dummy_buffer, &tag_len); #ifdef TLS_WITH_CHACHA20_POLY1305 } #endif } context->crypto.created = 0; } tls_init(); int is_aead = _private_tls_is_aead(context); int cipherID = find_cipher("aes"); DEBUG_PRINT("Using cipher ID: %x\n", (int)context->cipher); #ifdef TLS_WITH_CHACHA20_POLY1305 if (is_aead == 2) { unsigned int counter = 1; chacha_keysetup(&context->crypto.ctx_local.chacha_local, localkey, key_length * 8); chacha_ivsetup_96bitnonce(&context->crypto.ctx_local.chacha_local, localiv, (unsigned char *)&counter); chacha_keysetup(&context->crypto.ctx_remote.chacha_remote, remotekey, key_length * 8); chacha_ivsetup_96bitnonce(&context->crypto.ctx_remote.chacha_remote, remoteiv, (unsigned char *)&counter); context->crypto.created = 3; } else #endif if (is_aead) { int res1 = gcm_init(&context->crypto.ctx_local.aes_gcm_local, cipherID, localkey, key_length); int res2 = gcm_init(&context->crypto.ctx_remote.aes_gcm_remote, cipherID, remotekey, key_length); if ((res1) || (res2)) return TLS_GENERIC_ERROR; context->crypto.created = 2; } else { int res1 = cbc_start(cipherID, localiv, localkey, key_length, 0, &context->crypto.ctx_local.aes_local); int res2 = cbc_start(cipherID, remoteiv, remotekey, key_length, 0, &context->crypto.ctx_remote.aes_remote); if ((res1) || (res2)) return TLS_GENERIC_ERROR; context->crypto.created = 1; } return 0; } int _private_tls_crypto_encrypt(struct TLSContext *context, unsigned char *buf, unsigned char *ct, unsigned int len) { if (context->crypto.created == 1) return cbc_encrypt(buf, ct, len, &context->crypto.ctx_local.aes_local); memset(ct, 0, len); return TLS_GENERIC_ERROR; } int _private_tls_crypto_decrypt(struct TLSContext *context, unsigned char *buf, unsigned char *pt, unsigned int len) { if (context->crypto.created == 1) return cbc_decrypt(buf, pt, len, &context->crypto.ctx_remote.aes_remote); memset(pt, 0, len); return TLS_GENERIC_ERROR; } void _private_tls_crypto_done(struct TLSContext *context) { unsigned char dummy_buffer[32]; unsigned long tag_len = 0; switch (context->crypto.created) { case 1: cbc_done(&context->crypto.ctx_remote.aes_remote); cbc_done(&context->crypto.ctx_local.aes_local); break; case 2: gcm_done(&context->crypto.ctx_remote.aes_gcm_remote, dummy_buffer, &tag_len); gcm_done(&context->crypto.ctx_local.aes_gcm_local, dummy_buffer, &tag_len); break; } context->crypto.created = 0; } void tls_packet_update(struct TLSPacket *packet) { if ((packet) && (!packet->broken)) { int footer_size = 0; #ifdef WITH_TLS_13 if ((packet->context) && ((packet->context->version == TLS_V13) || (packet->context->version == DTLS_V13)) && (packet->context->cipher_spec_set) && (packet->context->crypto.created)) { // type tls_packet_uint8(packet, packet->buf[0]); // no padding // tls_packet_uint8(packet, 0); footer_size = 1; } #endif unsigned int header_size = 5; if ((packet->context) && (packet->context->dtls)) { header_size = 13; *(unsigned short *)(packet->buf + 3) = htons(packet->context->dtls_epoch_local); uint64_t sequence_number = packet->context->local_sequence_number; packet->buf[5] = (unsigned char)(sequence_number / 0x10000000000LL); sequence_number %= 0x10000000000LL; packet->buf[6] = (unsigned char)(sequence_number / 0x100000000LL); sequence_number %= 0x100000000LL; packet->buf[7] = (unsigned char)(sequence_number / 0x1000000); sequence_number %= 0x1000000; packet->buf[8] = (unsigned char)(sequence_number / 0x10000); sequence_number %= 0x10000; packet->buf[9] = (unsigned char)(sequence_number / 0x100); sequence_number %= 0x100; packet->buf[10] = (unsigned char)sequence_number; *(unsigned short *)(packet->buf + 11) = htons(packet->len - header_size); } else *(unsigned short *)(packet->buf + 3) = htons(packet->len - header_size); if (packet->context) { if (packet->buf[0] != TLS_CHANGE_CIPHER) { if ((packet->buf[0] == TLS_HANDSHAKE) && (packet->len > header_size)) { unsigned char handshake_type = packet->buf[header_size]; if ((handshake_type != 0x00) && (handshake_type != 0x03)) _private_tls_update_hash(packet->context, packet->buf + header_size, packet->len - header_size - footer_size); } #ifdef TLS_12_FALSE_START if (((packet->context->cipher_spec_set) || (packet->context->false_start)) && (packet->context->crypto.created)) { #else if ((packet->context->cipher_spec_set) && (packet->context->crypto.created)) { #endif int block_size = TLS_AES_BLOCK_SIZE; int mac_size = 0; unsigned int length = 0; unsigned char padding = 0; unsigned int pt_length = packet->len - header_size; if (packet->context->crypto.created == 1) { mac_size = _private_tls_mac_length(packet->context); #ifdef TLS_LEGACY_SUPPORT if (packet->context->version == TLS_V10) length = packet->len - header_size + mac_size; else #endif length = packet->len - header_size + TLS_AES_IV_LENGTH + mac_size; padding = block_size - length % block_size; length += padding; #ifdef TLS_WITH_CHACHA20_POLY1305 } else if (packet->context->crypto.created == 3) { mac_size = POLY1305_TAGLEN; length = packet->len - header_size + mac_size; #endif } else { mac_size = TLS_GCM_TAG_LEN; length = packet->len - header_size + 8 + mac_size; } if (packet->context->crypto.created == 1) { unsigned char *buf = (unsigned char *)TLS_MALLOC(length); if (buf) { unsigned char *ct = (unsigned char *)TLS_MALLOC(length + header_size); if (ct) { unsigned int buf_pos = 0; memcpy(ct, packet->buf, header_size - 2); *(unsigned short *)&ct[header_size - 2] = htons(length); #ifdef TLS_LEGACY_SUPPORT if (packet->context->version != TLS_V10) #endif { tls_random(buf, TLS_AES_IV_LENGTH); buf_pos += TLS_AES_IV_LENGTH; } // copy payload memcpy(buf + buf_pos, packet->buf + header_size, packet->len - header_size); buf_pos += packet->len - header_size; if (packet->context->dtls) { unsigned char temp_buf[5]; memcpy(temp_buf, packet->buf, 3); *(unsigned short *)(temp_buf + 3) = *(unsigned short *)&packet->buf[header_size - 2]; uint64_t dtls_sequence_number = ntohll(*(uint64_t *)&packet->buf[3]); _private_tls_hmac_message(1, packet->context, temp_buf, 5, packet->buf + header_size, packet->len - header_size, buf + buf_pos, mac_size, dtls_sequence_number); } else _private_tls_hmac_message(1, packet->context, packet->buf, packet->len, NULL, 0, buf + buf_pos, mac_size, 0); buf_pos += mac_size; memset(buf + buf_pos, padding - 1, padding); buf_pos += padding; //DEBUG_DUMP_HEX_LABEL("PT BUFFER", buf, length); _private_tls_crypto_encrypt(packet->context, buf, ct + header_size, length); TLS_FREE(packet->buf); packet->buf = ct; packet->len = length + header_size; packet->size = packet->len; } else { // invalidate packet memset(packet->buf, 0, packet->len); } TLS_FREE(buf); } else { // invalidate packet memset(packet->buf, 0, packet->len); } } else #ifdef TLS_WITH_CHACHA20_POLY1305 if (packet->context->crypto.created >= 2) { #else if (packet->context->crypto.created == 2) { #endif // + 1 = type int ct_size = length + header_size + 12 + TLS_MAX_TAG_LEN + 1; unsigned char *ct = (unsigned char *)TLS_MALLOC(ct_size); if (ct) { memset(ct, 0, ct_size); // AEAD // sequence number (8 bytes) // content type (1 byte) // version (2 bytes) // length (2 bytes) unsigned char aad[13]; int aad_size = sizeof(aad); unsigned char *sequence = aad; #ifdef WITH_TLS_13 if ((packet->context->version == TLS_V13) || (packet->context->version == DTLS_V13)) { aad[0] = TLS_APPLICATION_DATA; aad[1] = packet->buf[1]; aad[2] = packet->buf[2]; #ifdef TLS_WITH_CHACHA20_POLY1305 if (packet->context->crypto.created == 3) *((unsigned short *)(aad + 3)) = htons(packet->len + POLY1305_TAGLEN - header_size); else #endif *((unsigned short *)(aad + 3)) = htons(packet->len + TLS_GCM_TAG_LEN - header_size); aad_size = 5; sequence = aad + 5; if (packet->context->dtls) *((uint64_t *)sequence) = *(uint64_t *)&packet->buf[3]; else *((uint64_t *)sequence) = htonll(packet->context->local_sequence_number); } else { #endif if (packet->context->dtls) *((uint64_t *)aad) = *(uint64_t *)&packet->buf[3]; else *((uint64_t *)aad) = htonll(packet->context->local_sequence_number); aad[8] = packet->buf[0]; aad[9] = packet->buf[1]; aad[10] = packet->buf[2]; *((unsigned short *)(aad + 11)) = htons(packet->len - header_size); #ifdef WITH_TLS_13 } #endif int ct_pos = header_size; #ifdef TLS_WITH_CHACHA20_POLY1305 if (packet->context->crypto.created == 3) { unsigned int counter = 1; unsigned char poly1305_key[POLY1305_KEYLEN]; chacha_ivupdate(&packet->context->crypto.ctx_local.chacha_local, packet->context->crypto.ctx_local_mac.local_aead_iv, sequence, (u8 *)&counter); chacha20_poly1305_key(&packet->context->crypto.ctx_local.chacha_local, poly1305_key); ct_pos += chacha20_poly1305_aead(&packet->context->crypto.ctx_local.chacha_local, packet->buf + header_size, pt_length, aad, aad_size, poly1305_key, ct + ct_pos); } else { #endif unsigned char iv[TLS_13_AES_GCM_IV_LENGTH]; #ifdef WITH_TLS_13 if ((packet->context->version == TLS_V13) || (packet->context->version == DTLS_V13)) { memcpy(iv, packet->context->crypto.ctx_local_mac.local_iv, TLS_13_AES_GCM_IV_LENGTH); int i; int offset = TLS_13_AES_GCM_IV_LENGTH - 8; for (i = 0; i < 8; i++) iv[offset + i] = packet->context->crypto.ctx_local_mac.local_iv[offset + i] ^ sequence[i]; } else { #endif memcpy(iv, packet->context->crypto.ctx_local_mac.local_aead_iv, TLS_AES_GCM_IV_LENGTH); tls_random(iv + TLS_AES_GCM_IV_LENGTH, 8); memcpy(ct + ct_pos, iv + TLS_AES_GCM_IV_LENGTH, 8); ct_pos += 8; #ifdef WITH_TLS_13 } #endif gcm_reset(&packet->context->crypto.ctx_local.aes_gcm_local); gcm_add_iv(&packet->context->crypto.ctx_local.aes_gcm_local, iv, 12); gcm_add_aad(&packet->context->crypto.ctx_local.aes_gcm_local, aad, aad_size); gcm_process(&packet->context->crypto.ctx_local.aes_gcm_local, packet->buf + header_size, pt_length, ct + ct_pos, GCM_ENCRYPT); ct_pos += pt_length; unsigned long taglen = TLS_GCM_TAG_LEN; gcm_done(&packet->context->crypto.ctx_local.aes_gcm_local, ct + ct_pos, &taglen); ct_pos += taglen; #ifdef TLS_WITH_CHACHA20_POLY1305 } #endif #ifdef WITH_TLS_13 if ((packet->context->version == TLS_V13) || (packet->context->version == DTLS_V13)) { ct[0] = TLS_APPLICATION_DATA; *(unsigned short *)&ct[1] = htons(packet->context->version == TLS_V13 ? TLS_V12 : DTLS_V12); // is dtls ? if (header_size != 5) memcpy(ct, packet->buf + 3, header_size - 2); } else #endif memcpy(ct, packet->buf, header_size - 2); *(unsigned short *)&ct[header_size - 2] = htons(ct_pos - header_size); TLS_FREE(packet->buf); packet->buf = ct; packet->len = ct_pos; packet->size = ct_pos; } else { // invalidate packet memset(packet->buf, 0, packet->len); } } else { // invalidate packet (never reached) memset(packet->buf, 0, packet->len); } } } else packet->context->dtls_epoch_local++; packet->context->local_sequence_number++; } } } int tls_packet_append(struct TLSPacket *packet, const unsigned char *buf, unsigned int len) { if ((!packet) || (packet->broken)) return -1; if (!len) return 0; unsigned int new_len = packet->len + len; if (new_len > packet->size) { packet->size = (new_len / TLS_BLOB_INCREMENT + 1) * TLS_BLOB_INCREMENT; packet->buf = (unsigned char *)TLS_REALLOC(packet->buf, packet->size); if (!packet->buf) { packet->size = 0; packet->len = 0; packet->broken = 1; return -1; } } memcpy(packet->buf + packet->len, buf, len); packet->len = new_len; return new_len; } int tls_packet_uint8(struct TLSPacket *packet, unsigned char i) { return tls_packet_append(packet, &i, 1); } int tls_packet_uint16(struct TLSPacket *packet, unsigned short i) { unsigned short ni = htons(i); return tls_packet_append(packet, (unsigned char *)&ni, 2); } int tls_packet_uint32(struct TLSPacket *packet, unsigned int i) { unsigned int ni = htonl(i); return tls_packet_append(packet, (unsigned char *)&ni, 4); } int tls_packet_uint24(struct TLSPacket *packet, unsigned int i) { unsigned char buf[3]; buf[0] = i / 0x10000; i %= 0x10000; buf[1] = i / 0x100; i %= 0x100; buf[2] = i; return tls_packet_append(packet, buf, 3); } int tls_random(unsigned char *key, int len) { #ifdef TLS_USE_RANDOM_SOURCE TLS_USE_RANDOM_SOURCE(key, len); #else #ifdef __APPLE__ for (int i = 0; i < len; i++) { unsigned int v = arc4random() % 0x100; key[i] = (char)v; } return 1; #else #ifdef _WIN32 HCRYPTPROV hProvider = 0; if (CryptAcquireContext(&hProvider, 0, 0, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT | CRYPT_SILENT)) { if (CryptGenRandom(hProvider, len, (BYTE *)key)) { CryptReleaseContext(hProvider, 0); return 1; } CryptReleaseContext(hProvider, 0); } #else FILE *fp = fopen("/dev/urandom", "r"); if (fp) { int key_len = fread(key, 1, len, fp); fclose(fp); if (key_len == len) return 1; } #endif #endif #endif return 0; } TLSHash *_private_tls_ensure_hash(struct TLSContext *context) { TLSHash *hash = context->handshake_hash; if (!hash) { hash = (TLSHash *)TLS_MALLOC(sizeof(TLSHash)); if (hash) memset(hash, 0, sizeof(TLSHash)); context->handshake_hash = hash; } return hash; } void _private_tls_destroy_hash(struct TLSContext *context) { if (context) { TLS_FREE(context->handshake_hash); context->handshake_hash = NULL; } } void _private_tls_create_hash(struct TLSContext *context) { if (!context) return; TLSHash *hash = _private_tls_ensure_hash(context); if ((context->version == TLS_V12) || (context->version == DTLS_V12) || (context->version == TLS_V13) || (context->version == DTLS_V13)) { int hash_size = _private_tls_mac_length(context); if (hash->created) { unsigned char temp[TLS_MAX_SHA_SIZE]; sha384_done(&hash->hash32, temp); sha256_done(&hash->hash48, temp); } sha384_init(&hash->hash48); sha256_init(&hash->hash32); hash->created = 1; } else { #ifdef TLS_LEGACY_SUPPORT // TLS_V11 if (hash->created) { unsigned char temp[TLS_V11_HASH_SIZE]; md5_done(&hash->hash32, temp); sha1_done(&hash->hash2, temp); } md5_init(&hash->hash32); sha1_init(&hash->hash2); hash->created = 1; #endif } } int _private_tls_update_hash(struct TLSContext *context, const unsigned char *in, unsigned int len) { if (!context) return 0; TLSHash *hash = _private_tls_ensure_hash(context); if ((context->version == TLS_V12) || (context->version == DTLS_V12) || (context->version == TLS_V13) || (context->version == DTLS_V13)) { if (!hash->created) { _private_tls_create_hash(context); #ifdef TLS_LEGACY_SUPPORT // cache first hello in case of protocol downgrade if ((!context->is_server) && (!context->cached_handshake) && (!context->request_client_certificate) && (len)) { context->cached_handshake = (unsigned char *)TLS_MALLOC(len); if (context->cached_handshake) { memcpy(context->cached_handshake, in, len); context->cached_handshake_len = len; } } #endif } int hash_size = _private_tls_mac_length(context); sha256_process(&hash->hash32, in, len); sha384_process(&hash->hash48, in, len); if (!hash_size) hash_size = TLS_SHA256_MAC_SIZE; } else { #ifdef TLS_LEGACY_SUPPORT if (!hash->created) _private_tls_create_hash(context); md5_process(&hash->hash32, in, len); sha1_process(&hash->hash2, in, len); #endif } if ((context->request_client_certificate) && (len)) { // cache all messages for verification int new_len = context->cached_handshake_len + len; context->cached_handshake = (unsigned char *)TLS_REALLOC(context->cached_handshake, new_len); if (context->cached_handshake) { memcpy(context->cached_handshake + context->cached_handshake_len, in, len); context->cached_handshake_len = new_len; } else context->cached_handshake_len = 0; } return 0; } #ifdef TLS_LEGACY_SUPPORT int _private_tls_change_hash_type(struct TLSContext *context) { if (!context) return 0; TLSHash *hash = _private_tls_ensure_hash(context); if ((hash) && (hash->created) && (context->cached_handshake) && (context->cached_handshake_len)) { _private_tls_destroy_hash(context); int res = _private_tls_update_hash(context, context->cached_handshake, context->cached_handshake_len); TLS_FREE(context->cached_handshake); context->cached_handshake = NULL; context->cached_handshake_len = 0; return res; } return 0; } #endif int _private_tls_done_hash(struct TLSContext *context, unsigned char *hout) { if (!context) return 0; TLSHash *hash = _private_tls_ensure_hash(context); if (!hash->created) return 0; int hash_size = 0; if ((context->version == TLS_V12) || (context->version == DTLS_V12) || (context->version == TLS_V13) || (context->version == DTLS_V13)) { unsigned char temp[TLS_MAX_SHA_SIZE]; if (!hout) hout = temp; //TLS_HASH_DONE(&hash->hash, hout); hash_size = _private_tls_mac_length(context); if (hash_size == TLS_SHA384_MAC_SIZE) { sha256_done(&hash->hash32, temp); sha384_done(&hash->hash48, hout); } else { sha256_done(&hash->hash32, hout); sha384_done(&hash->hash48, temp); hash_size = TLS_SHA256_MAC_SIZE; } } else { #ifdef TLS_LEGACY_SUPPORT // TLS_V11 unsigned char temp[TLS_V11_HASH_SIZE]; if (!hout) hout = temp; md5_done(&hash->hash32, hout); sha1_done(&hash->hash2, hout + 16); hash_size = TLS_V11_HASH_SIZE; #endif } hash->created = 0; if (context->cached_handshake) { // not needed anymore TLS_FREE(context->cached_handshake); context->cached_handshake = NULL; context->cached_handshake_len = 0; } return hash_size; } int _private_tls_get_hash_idx(struct TLSContext *context) { if (!context) return -1; switch (_private_tls_mac_length(context)) { case TLS_SHA256_MAC_SIZE: return find_hash("sha256"); case TLS_SHA384_MAC_SIZE: return find_hash("sha384"); case TLS_SHA1_MAC_SIZE: return find_hash("sha1"); } return -1; } int _private_tls_get_hash(struct TLSContext *context, unsigned char *hout) { if (!context) return 0; TLSHash *hash = _private_tls_ensure_hash(context); if (!hash->created) return 0; int hash_size = 0; if ((context->version == TLS_V12) || (context->version == DTLS_V12) || (context->version == TLS_V13) || (context->version == DTLS_V13)) { hash_size = _private_tls_mac_length(context); hash_state prec; if (hash_size == TLS_SHA384_MAC_SIZE) { memcpy(&prec, &hash->hash48, sizeof(hash_state)); sha384_done(&hash->hash48, hout); memcpy(&hash->hash48, &prec, sizeof(hash_state)); } else { memcpy(&prec, &hash->hash32, sizeof(hash_state)); hash_size = TLS_SHA256_MAC_SIZE; sha256_done(&hash->hash32, hout); memcpy(&hash->hash32, &prec, sizeof(hash_state)); } } else { #ifdef TLS_LEGACY_SUPPORT // TLS_V11 hash_state prec; memcpy(&prec, &hash->hash32, sizeof(hash_state)); md5_done(&hash->hash32, hout); memcpy(&hash->hash32, &prec, sizeof(hash_state)); memcpy(&prec, &hash->hash2, sizeof(hash_state)); sha1_done(&hash->hash2, hout + 16); memcpy(&hash->hash2, &prec, sizeof(hash_state)); hash_size = TLS_V11_HASH_SIZE; #endif } return hash_size; } int _private_tls_write_packet(struct TLSPacket *packet) { if (!packet) return -1; struct TLSContext *context = packet->context; if (!context) return -1; if (context->tls_buffer) { int len = context->tls_buffer_len + packet->len; context->tls_buffer = (unsigned char *)TLS_REALLOC(context->tls_buffer, len); if (!context->tls_buffer) { context->tls_buffer_len = 0; return -1; } memcpy(context->tls_buffer + context->tls_buffer_len, packet->buf, packet->len); context->tls_buffer_len = len; int written = packet->len; tls_destroy_packet(packet); return written; } context->tls_buffer_len = packet->len; context->tls_buffer = packet->buf; packet->buf = NULL; packet->len = 0; packet->size = 0; tls_destroy_packet(packet); return context->tls_buffer_len; } int _private_tls_write_app_data(struct TLSContext *context, const unsigned char *buf, unsigned int buf_len) { if (!context) return -1; if ((!buf) || (!buf_len)) return 0; int len = context->application_buffer_len + buf_len; context->application_buffer = (unsigned char *)TLS_REALLOC(context->application_buffer, len); if (!context->application_buffer) { context->application_buffer_len = 0; return -1; } memcpy(context->application_buffer + context->application_buffer_len, buf, buf_len); context->application_buffer_len = len; return buf_len; } const unsigned char *tls_get_write_buffer(struct TLSContext *context, unsigned int *outlen) { if (!outlen) return NULL; if (!context) { *outlen = 0; return NULL; } // check if any error if (context->sleep_until) { if (context->sleep_until < time(NULL)) { *outlen = 0; return NULL; } context->sleep_until = 0; } *outlen = context->tls_buffer_len; return context->tls_buffer; } const unsigned char *tls_get_message(struct TLSContext *context, unsigned int *outlen, unsigned int offset) { if (!outlen) return NULL; if ((!context) || (!context->tls_buffer)) { *outlen = 0; return NULL; } if (offset >= context->tls_buffer_len) { *outlen = 0; return NULL; } // check if any error if (context->sleep_until) { if (context->sleep_until < time(NULL)) { *outlen = 0; return NULL; } context->sleep_until = 0; } unsigned char *tls_buffer = &context->tls_buffer[offset]; unsigned int tls_buffer_len = context->tls_buffer_len - offset; unsigned int len = 0; if (context->dtls) { if (tls_buffer_len < 13) { *outlen = 0; return NULL; } len = ntohs(*(unsigned short *)&tls_buffer[11]) + 13; } else { if (tls_buffer_len < 5) { *outlen = 0; return NULL; } len = ntohs(*(unsigned short *)&tls_buffer[3]) + 5; } if (len > tls_buffer_len) { *outlen = 0; return NULL; } *outlen = len; return tls_buffer; } void tls_buffer_clear(struct TLSContext *context) { if ((context) && (context->tls_buffer)) { TLS_FREE(context->tls_buffer); context->tls_buffer = NULL; context->tls_buffer_len = 0; } } int tls_established(struct TLSContext *context) { if (context) { if (context->critical_error) return -1; if (context->connection_status == 0xFF) return 1; #ifdef TLS_12_FALSE_START // allow false start if ((!context->is_server) && (context->version == TLS_V12) && (context->false_start)) return 1; #endif } return 0; } void tls_read_clear(struct TLSContext *context) { if ((context) && (context->application_buffer)) { TLS_FREE(context->application_buffer); context->application_buffer = NULL; context->application_buffer_len = 0; } } int tls_read(struct TLSContext *context, unsigned char *buf, unsigned int size) { if (!context) return -1; if ((context->application_buffer) && (context->application_buffer_len)) { if (context->application_buffer_len < size) size = context->application_buffer_len; memcpy(buf, context->application_buffer, size); if (context->application_buffer_len == size) { TLS_FREE(context->application_buffer); context->application_buffer = NULL; context->application_buffer_len = 0; return size; } context->application_buffer_len -= size; memmove(context->application_buffer, context->application_buffer + size, context->application_buffer_len); return size; } return 0; } struct TLSContext *tls_create_context(unsigned char is_server, unsigned short version) { struct TLSContext *context = (struct TLSContext *)TLS_MALLOC(sizeof(struct TLSContext)); if (context) { memset(context, 0, sizeof(struct TLSContext)); context->is_server = is_server; if ((version == DTLS_V13) || (version == DTLS_V12) || (version == DTLS_V10)) context->dtls = 1; context->version = version; } return context; } #ifdef TLS_FORWARD_SECRECY const struct ECCCurveParameters *tls_set_curve(struct TLSContext *context, const struct ECCCurveParameters *curve) { if (!context->is_server) return NULL; const struct ECCCurveParameters *old_curve = context->curve; context->curve = curve; return old_curve; } #endif struct TLSContext *tls_accept(struct TLSContext *context) { if ((!context) || (!context->is_server)) return NULL; struct TLSContext *child = (struct TLSContext *)TLS_MALLOC(sizeof(struct TLSContext)); if (child) { memset(child, 0, sizeof(struct TLSContext)); child->is_server = 1; child->is_child = 1; child->dtls = context->dtls; child->version = context->version; child->certificates = context->certificates; child->certificates_count = context->certificates_count; child->private_key = context->private_key; #ifdef TLS_ECDSA_SUPPORTED child->ec_private_key = context->ec_private_key; #endif child->exportable = context->exportable; child->root_certificates = context->root_certificates; child->root_count = context->root_count; #ifdef TLS_FORWARD_SECRECY child->default_dhe_p = context->default_dhe_p; child->default_dhe_g = context->default_dhe_g; child->curve = context->curve; #endif child->alpn = context->alpn; child->alpn_count = context->alpn_count; } return child; } #ifdef TLS_FORWARD_SECRECY void _private_tls_dhe_free(struct TLSContext *context) { if (context->dhe) { _private_tls_dh_clear_key(context->dhe); TLS_FREE(context->dhe); context->dhe = NULL; } } void _private_tls_dhe_create(struct TLSContext *context) { _private_tls_dhe_free(context); context->dhe = (DHKey *)TLS_MALLOC(sizeof(DHKey)); if (context->dhe) memset(context->dhe, 0, sizeof(DHKey)); } void _private_tls_ecc_dhe_free(struct TLSContext *context) { if (context->ecc_dhe) { ecc_free(context->ecc_dhe); TLS_FREE(context->ecc_dhe); context->ecc_dhe = NULL; } } void _private_tls_ecc_dhe_create(struct TLSContext *context) { _private_tls_ecc_dhe_free(context); context->ecc_dhe = (ecc_key *)TLS_MALLOC(sizeof(ecc_key)); memset(context->ecc_dhe, 0, sizeof(ecc_key)); } int tls_set_default_dhe_pg(struct TLSContext *context, const char *p_hex_str, const char *g_hex_str) { if ((!context) || (context->is_child) || (!context->is_server) || (!p_hex_str) || (!g_hex_str)) return 0; TLS_FREE(context->default_dhe_p); TLS_FREE(context->default_dhe_g); context->default_dhe_p = NULL; context->default_dhe_g = NULL; size_t p_len = strlen(p_hex_str); size_t g_len = strlen(g_hex_str); if ((p_len <= 0) || (g_len <= 0)) return 0; context->default_dhe_p = (char *)TLS_MALLOC(p_len + 1); if (!context->default_dhe_p) return 0; context->default_dhe_g = (char *)TLS_MALLOC(g_len + 1); if (!context->default_dhe_g) return 0; memcpy(context->default_dhe_p, p_hex_str, p_len); context->default_dhe_p[p_len] = 0; memcpy(context->default_dhe_g, g_hex_str, g_len); context->default_dhe_g[g_len] = 0; return 1; } #endif const char *tls_alpn(struct TLSContext *context) { if (!context) return NULL; return context->negotiated_alpn; } int tls_add_alpn(struct TLSContext *context, const char *alpn) { if ((!context) || (!alpn) || (!alpn[0]) || ((context->is_server) && (context->is_child))) return TLS_GENERIC_ERROR; int len = strlen(alpn); if (tls_alpn_contains(context, alpn, len)) return 0; context->alpn = (char **)TLS_REALLOC(context->alpn, (context->alpn_count + 1) * sizeof(char *)); if (!context->alpn) { context->alpn_count = 0; return TLS_NO_MEMORY; } char *alpn_ref = (char *)TLS_MALLOC(len+1); context->alpn[context->alpn_count] = alpn_ref; if (alpn_ref) { memcpy(alpn_ref, alpn, len); alpn_ref[len] = 0; context->alpn_count++; } else return TLS_NO_MEMORY; return 0; } int tls_alpn_contains(struct TLSContext *context, const char *alpn, unsigned char alpn_size) { if ((!context) || (!alpn) || (!alpn_size)) return 0; if (context->alpn) { int i; for (i = 0; i < context->alpn_count; i++) { const char *alpn_local = context->alpn[i]; if (alpn_local) { int len = strlen(alpn_local); if (alpn_size == len) { if (!memcmp(alpn_local, alpn, alpn_size)) return 1; } } } } return 0; } void tls_destroy_context(struct TLSContext *context) { unsigned int i; if (!context) return; if (!context->is_child) { if (context->certificates) { for (i = 0; i < context->certificates_count; i++) tls_destroy_certificate(context->certificates[i]); } if (context->root_certificates) { for (i = 0; i < context->root_count; i++) tls_destroy_certificate(context->root_certificates[i]); TLS_FREE(context->root_certificates); context->root_certificates = NULL; } if (context->private_key) tls_destroy_certificate(context->private_key); #ifdef TLS_ECDSA_SUPPORTED if (context->ec_private_key) tls_destroy_certificate(context->ec_private_key); #endif TLS_FREE(context->certificates); #ifdef TLS_FORWARD_SECRECY TLS_FREE(context->default_dhe_p); TLS_FREE(context->default_dhe_g); #endif if (context->alpn) { for (i = 0; i < context->alpn_count; i++) TLS_FREE(context->alpn[i]); TLS_FREE(context->alpn); } } if (context->client_certificates) { for (i = 0; i < context->client_certificates_count; i++) tls_destroy_certificate(context->client_certificates[i]); TLS_FREE(context->client_certificates); } context->client_certificates = NULL; TLS_FREE(context->master_key); TLS_FREE(context->premaster_key); if (context->crypto.created) _private_tls_crypto_done(context); TLS_FREE(context->message_buffer); _private_tls_done_hash(context, NULL); _private_tls_destroy_hash(context); TLS_FREE(context->tls_buffer); TLS_FREE(context->application_buffer); // zero out the keys before free if ((context->exportable_keys) && (context->exportable_size)) memset(context->exportable_keys, 0, context->exportable_size); TLS_FREE(context->exportable_keys); TLS_FREE(context->sni); TLS_FREE(context->dtls_cookie); TLS_FREE(context->cached_handshake); #ifdef TLS_FORWARD_SECRECY _private_tls_dhe_free(context); _private_tls_ecc_dhe_free(context); #endif #ifdef TLS_ACCEPT_SECURE_RENEGOTIATION TLS_FREE(context->verify_data); #endif TLS_FREE(context->negotiated_alpn); #ifdef WITH_TLS_13 TLS_FREE(context->finished_key); TLS_FREE(context->remote_finished_key); TLS_FREE(context->server_finished_hash); #endif #ifdef TLS_CURVE25519 TLS_FREE(context->client_secret); #endif TLS_FREE(context); } #ifdef TLS_ACCEPT_SECURE_RENEGOTIATION void _private_tls_reset_context(struct TLSContext *context) { unsigned int i; if (!context) return; if (!context->is_child) { if (context->certificates) { for (i = 0; i < context->certificates_count; i++) tls_destroy_certificate(context->certificates[i]); } context->certificates = NULL; if (context->private_key) { tls_destroy_certificate(context->private_key); context->private_key = NULL; } #ifdef TLS_ECDSA_SUPPORTED if (context->ec_private_key) { tls_destroy_certificate(context->ec_private_key); context->ec_private_key = NULL; } #endif TLS_FREE(context->certificates); context->certificates = NULL; #ifdef TLS_FORWARD_SECRECY TLS_FREE(context->default_dhe_p); TLS_FREE(context->default_dhe_g); context->default_dhe_p = NULL; context->default_dhe_g = NULL; #endif } if (context->client_certificates) { for (i = 0; i < context->client_certificates_count; i++) tls_destroy_certificate(context->client_certificates[i]); TLS_FREE(context->client_certificates); } context->client_certificates = NULL; TLS_FREE(context->master_key); context->master_key = NULL; TLS_FREE(context->premaster_key); context->premaster_key = NULL; if (context->crypto.created) _private_tls_crypto_done(context); _private_tls_done_hash(context, NULL); _private_tls_destroy_hash(context); TLS_FREE(context->application_buffer); context->application_buffer = NULL; // zero out the keys before free if ((context->exportable_keys) && (context->exportable_size)) memset(context->exportable_keys, 0, context->exportable_size); TLS_FREE(context->exportable_keys); context->exportable_keys = NULL; TLS_FREE(context->sni); context->sni = NULL; TLS_FREE(context->dtls_cookie); context->dtls_cookie = NULL; TLS_FREE(context->cached_handshake); context->cached_handshake = NULL; context->connection_status = 0; #ifdef TLS_FORWARD_SECRECY _private_tls_dhe_free(context); _private_tls_ecc_dhe_free(context); #endif } #endif int tls_cipher_supported(struct TLSContext *context, unsigned short cipher) { if (!context) return 0; switch (cipher) { #ifdef WITH_TLS_13 case TLS_AES_128_GCM_SHA256: case TLS_AES_256_GCM_SHA384: case TLS_CHACHA20_POLY1305_SHA256: if ((context->version == TLS_V13) || (context->version == DTLS_V13)) return 1; return 0; #endif #ifdef TLS_FORWARD_SECRECY #ifdef TLS_ECDSA_SUPPORTED case TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA: case TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA: #ifdef TLS_CLIENT_ECDSA if ((context) && (((context->certificates) && (context->certificates_count) && (context->ec_private_key)) || (!context->is_server))) #else if ((context) && (context->certificates) && (context->certificates_count) && (context->ec_private_key)) #endif return 1; return 0; case TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256: case TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384: case TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256: case TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384: #ifdef TLS_WITH_CHACHA20_POLY1305 case TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256: #endif if ((context->version == TLS_V12) || (context->version == DTLS_V12)) { #ifdef TLS_CLIENT_ECDSA if ((context) && (((context->certificates) && (context->certificates_count) && (context->ec_private_key)) || (!context->is_server))) #else if ((context) && (context->certificates) && (context->certificates_count) && (context->ec_private_key)) #endif return 1; } return 0; #endif case TLS_DHE_RSA_WITH_AES_128_CBC_SHA: case TLS_DHE_RSA_WITH_AES_256_CBC_SHA: case TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA: case TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA: #endif case TLS_RSA_WITH_AES_128_CBC_SHA: case TLS_RSA_WITH_AES_256_CBC_SHA: return 1; #ifdef TLS_FORWARD_SECRECY case TLS_DHE_RSA_WITH_AES_128_CBC_SHA256: case TLS_DHE_RSA_WITH_AES_256_CBC_SHA256: case TLS_DHE_RSA_WITH_AES_128_GCM_SHA256: case TLS_DHE_RSA_WITH_AES_256_GCM_SHA384: case TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256: case TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256: case TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384: #ifdef TLS_WITH_CHACHA20_POLY1305 case TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256: case TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256: #endif #endif case TLS_RSA_WITH_AES_128_GCM_SHA256: case TLS_RSA_WITH_AES_128_CBC_SHA256: case TLS_RSA_WITH_AES_256_CBC_SHA256: case TLS_RSA_WITH_AES_256_GCM_SHA384: if ((context->version == TLS_V12) || (context->version == DTLS_V12)) return 1; return 0; } return 0; } int tls_cipher_is_fs(struct TLSContext *context, unsigned short cipher) { if (!context) return 0; #ifdef WITH_TLS_13 if ((context->version == TLS_V13) || (context->version == DTLS_V13)) { switch (cipher) { case TLS_AES_128_GCM_SHA256: case TLS_AES_256_GCM_SHA384: case TLS_CHACHA20_POLY1305_SHA256: return 1; } return 0; } #endif switch (cipher) { #ifdef TLS_ECDSA_SUPPORTED case TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA: case TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA: #ifdef TLS_WITH_CHACHA20_POLY1305 case TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256: #endif if ((context) && (context->certificates) && (context->certificates_count) && (context->ec_private_key)) return 1; return 0; case TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256: case TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384: case TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256: case TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384: if ((context->version == TLS_V12) || (context->version == DTLS_V12)) { if ((context) && (context->certificates) && (context->certificates_count) && (context->ec_private_key)) return 1; } return 0; #endif case TLS_DHE_RSA_WITH_AES_128_CBC_SHA: case TLS_DHE_RSA_WITH_AES_256_CBC_SHA: case TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA: case TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA: return 1; case TLS_DHE_RSA_WITH_AES_128_CBC_SHA256: case TLS_DHE_RSA_WITH_AES_256_CBC_SHA256: case TLS_DHE_RSA_WITH_AES_128_GCM_SHA256: case TLS_DHE_RSA_WITH_AES_256_GCM_SHA384: case TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256: case TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256: case TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384: #ifdef TLS_WITH_CHACHA20_POLY1305 case TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256: case TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256: #endif if ((context->version == TLS_V12) || (context->version == DTLS_V12)) return 1; break; } return 0; } #ifdef WITH_KTLS int _private_tls_prefer_ktls(struct TLSContext *context, unsigned short cipher) { if ((context->version == TLS_V13) || (context->version == DTLS_V13) || ((context->version != TLS_V12) && (context->version != DTLS_V12))) return 0; switch (cipher) { case TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256: if ((context->version == TLS_V13) || (context->version == DTLS_V13) || (context->version == TLS_V12) || (context->version == DTLS_V12)) { if ((context->certificates) && (context->certificates_count) && (context->ec_private_key)) return 1; } break; case TLS_DHE_RSA_WITH_AES_128_GCM_SHA256: case TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256: return 1; } return 0; } #endif int tls_choose_cipher(struct TLSContext *context, const unsigned char *buf, int buf_len, int *scsv_set) { int i; if (scsv_set) *scsv_set = 0; if (!context) return 0; int selected_cipher = TLS_NO_COMMON_CIPHER; #ifdef TLS_FORWARD_SECRECY #ifdef WITH_KTLS for (i = 0; i < buf_len; i+=2) { unsigned short cipher = ntohs(*(unsigned short *)&buf[i]); if (_private_tls_prefer_ktls(context, cipher)) { selected_cipher = cipher; break; } } #endif if (selected_cipher == TLS_NO_COMMON_CIPHER) { for (i = 0; i < buf_len; i+=2) { unsigned short cipher = ntohs(*(unsigned short *)&buf[i]); if (tls_cipher_is_fs(context, cipher)) { selected_cipher = cipher; break; } } } #endif for (i = 0; i < buf_len; i+=2) { unsigned short cipher = ntohs(*(unsigned short *)&buf[i]); if (cipher == TLS_FALLBACK_SCSV) { if (scsv_set) *scsv_set = 1; if (selected_cipher != TLS_NO_COMMON_CIPHER) break; } #ifndef TLS_ROBOT_MITIGATION else if ((selected_cipher == TLS_NO_COMMON_CIPHER) && (tls_cipher_supported(context, cipher))) selected_cipher = cipher; #endif } return selected_cipher; } int tls_cipher_is_ephemeral(struct TLSContext *context) { if (context) { switch (context->cipher) { case TLS_DHE_RSA_WITH_AES_128_CBC_SHA: case TLS_DHE_RSA_WITH_AES_256_CBC_SHA: case TLS_DHE_RSA_WITH_AES_128_CBC_SHA256: case TLS_DHE_RSA_WITH_AES_256_CBC_SHA256: case TLS_DHE_RSA_WITH_AES_128_GCM_SHA256: case TLS_DHE_RSA_WITH_AES_256_GCM_SHA384: case TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256: return 1; case TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA: case TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA: case TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256: case TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256: case TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384: case TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256: case TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA: case TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA: case TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256: case TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384: case TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256: case TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384: case TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256: return 2; #ifdef WITH_TLS_13 case TLS_AES_128_GCM_SHA256: case TLS_CHACHA20_POLY1305_SHA256: case TLS_AES_128_CCM_SHA256: case TLS_AES_128_CCM_8_SHA256: case TLS_AES_256_GCM_SHA384: if (context->dhe) return 1; return 2; #endif } } return 0; } const char *tls_cipher_name(struct TLSContext *context) { if (context) { switch (context->cipher) { case TLS_RSA_WITH_AES_128_CBC_SHA: return "RSA-AES128CBC-SHA"; case TLS_RSA_WITH_AES_256_CBC_SHA: return "RSA-AES256CBC-SHA"; case TLS_RSA_WITH_AES_128_CBC_SHA256: return "RSA-AES128CBC-SHA256"; case TLS_RSA_WITH_AES_256_CBC_SHA256: return "RSA-AES256CBC-SHA256"; case TLS_RSA_WITH_AES_128_GCM_SHA256: return "RSA-AES128GCM-SHA256"; case TLS_RSA_WITH_AES_256_GCM_SHA384: return "RSA-AES256GCM-SHA384"; case TLS_DHE_RSA_WITH_AES_128_CBC_SHA: return "DHE-RSA-AES128CBC-SHA"; case TLS_DHE_RSA_WITH_AES_256_CBC_SHA: return "DHE-RSA-AES256CBC-SHA"; case TLS_DHE_RSA_WITH_AES_128_CBC_SHA256: return "DHE-RSA-AES128CBC-SHA256"; case TLS_DHE_RSA_WITH_AES_256_CBC_SHA256: return "DHE-RSA-AES256CBC-SHA256"; case TLS_DHE_RSA_WITH_AES_128_GCM_SHA256: return "DHE-RSA-AES128GCM-SHA256"; case TLS_DHE_RSA_WITH_AES_256_GCM_SHA384: return "DHE-RSA-AES256GCM-SHA384"; case TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA: return "ECDHE-RSA-AES128CBC-SHA"; case TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA: return "ECDHE-RSA-AES256CBC-SHA"; case TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256: return "ECDHE-RSA-AES128CBC-SHA256"; case TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256: return "ECDHE-RSA-AES128GCM-SHA256"; case TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384: return "ECDHE-RSA-AES256GCM-SHA384"; case TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA: return "ECDHE-ECDSA-AES128CBC-SHA"; case TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA: return "ECDHE-ECDSA-AES256CBC-SHA"; case TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256: return "ECDHE-ECDSA-AES128CBC-SHA256"; case TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384: return "ECDHE-ECDSA-AES256CBC-SHA384"; case TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256: return "ECDHE-ECDSA-AES128GCM-SHA256"; case TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384: return "ECDHE-ECDSA-AES256GCM-SHA384"; case TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256: return "ECDHE-RSA-CHACHA20-POLY1305-SHA256"; case TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256: return "ECDHE-ECDSA-CHACHA20-POLY1305-SHA256"; case TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256: return "ECDHE-DHE-CHACHA20-POLY1305-SHA256"; case TLS_AES_128_GCM_SHA256: return "TLS-AES-128-GCM-SHA256"; case TLS_AES_256_GCM_SHA384: return "TLS-AES-256-GCM-SHA384"; case TLS_CHACHA20_POLY1305_SHA256: return "TLS-CHACHA20-POLY1305-SHA256"; case TLS_AES_128_CCM_SHA256: return "TLS-AES-128-CCM-SHA256"; case TLS_AES_128_CCM_8_SHA256: return "TLS-AES-128-CCM-8-SHA256"; } } return "UNKNOWN"; } #ifdef TLS_FORWARD_SECRECY int _private_tls_dh_export_Y(unsigned char *Ybuf, unsigned long *Ylen, DHKey *key) { unsigned long len; if ((Ybuf == NULL) || (Ylen == NULL) || (key == NULL)) return TLS_GENERIC_ERROR; len = mp_unsigned_bin_size(key->y); if (len > *Ylen) return TLS_GENERIC_ERROR; *Ylen = len; return 0; } int _private_tls_dh_export_pqY(unsigned char *pbuf, unsigned long *plen, unsigned char *gbuf, unsigned long *glen, unsigned char *Ybuf, unsigned long *Ylen, DHKey *key) { unsigned long len; int err; if ((pbuf == NULL) || (plen == NULL) || (gbuf == NULL) || (glen == NULL) || (Ybuf == NULL) || (Ylen == NULL) || (key == NULL)) return TLS_GENERIC_ERROR; len = mp_unsigned_bin_size(key->y); if (len > *Ylen) return TLS_GENERIC_ERROR; if ((err = mp_to_unsigned_bin(key->y, Ybuf)) != CRYPT_OK) return err; *Ylen = len; len = mp_unsigned_bin_size(key->p); if (len > *plen) return TLS_GENERIC_ERROR; if ((err = mp_to_unsigned_bin(key->p, pbuf)) != CRYPT_OK) return err; *plen = len; len = mp_unsigned_bin_size(key->g); if (len > *glen) return TLS_GENERIC_ERROR; if ((err = mp_to_unsigned_bin(key->g, gbuf)) != CRYPT_OK) return err; *glen = len; return 0; } void _private_tls_dh_clear_key(DHKey *key) { mp_clear_multi(key->g, key->p, key->x, key->y, NULL); key->g = NULL; key->p = NULL; key->x = NULL; key->y = NULL; } int _private_tls_dh_make_key(int keysize, DHKey *key, const char *pbuf, const char *gbuf, int pbuf_len, int gbuf_len) { unsigned char *buf; int err; if (!key) return TLS_GENERIC_ERROR; static prng_state prng; int wprng = find_prng("sprng"); if ((err = prng_is_valid(wprng)) != CRYPT_OK) return err; buf = (unsigned char *)TLS_MALLOC(keysize); if (!buf) return TLS_NO_MEMORY; if (rng_make_prng(keysize, wprng, &prng, NULL) != CRYPT_OK) { TLS_FREE(buf); return TLS_GENERIC_ERROR; } if (prng_descriptor[wprng].read(buf, keysize, &prng) != (unsigned long)keysize) { TLS_FREE(buf); return TLS_GENERIC_ERROR; } if ((err = mp_init_multi(&key->g, &key->p, &key->x, &key->y, NULL)) != CRYPT_OK) { TLS_FREE(buf); return TLS_GENERIC_ERROR; } if (gbuf_len <= 0) { if ((err = mp_read_radix(key->g, gbuf, 16)) != CRYPT_OK) { TLS_FREE(buf); _private_tls_dh_clear_key(key); return TLS_GENERIC_ERROR; } } else { if ((err = mp_read_unsigned_bin(key->g, (unsigned char *)gbuf, gbuf_len)) != CRYPT_OK) { TLS_FREE(buf); _private_tls_dh_clear_key(key); return TLS_GENERIC_ERROR; } } if (pbuf_len <= 0) { if ((err = mp_read_radix(key->p, pbuf, 16)) != CRYPT_OK) { TLS_FREE(buf); _private_tls_dh_clear_key(key); return TLS_GENERIC_ERROR; } } else { if ((err = mp_read_unsigned_bin(key->p, (unsigned char *)pbuf, pbuf_len)) != CRYPT_OK) { TLS_FREE(buf); _private_tls_dh_clear_key(key); return TLS_GENERIC_ERROR; } } if ((err = mp_read_unsigned_bin(key->x, buf, keysize)) != CRYPT_OK) { TLS_FREE(buf); _private_tls_dh_clear_key(key); return TLS_GENERIC_ERROR; } if ((err = mp_exptmod(key->g, key->x, key->p, key->y)) != CRYPT_OK) { TLS_FREE(buf); _private_tls_dh_clear_key(key); return TLS_GENERIC_ERROR; } TLS_FREE(buf); return 0; } #endif int tls_is_ecdsa(struct TLSContext *context) { if (!context) return 0; switch (context->cipher) { case TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA: case TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA: case TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256: case TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384: case TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256: case TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384: #ifdef TLS_WITH_CHACHA20_POLY1305 case TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256: #endif return 1; } #ifdef WITH_TLS_13 if (context->ec_private_key) return 1; #endif return 0; } struct TLSPacket *tls_build_client_key_exchange(struct TLSContext *context) { if (context->is_server) { DEBUG_PRINT("CANNOT BUILD CLIENT KEY EXCHANGE MESSAGE FOR SERVERS\n"); return NULL; } struct TLSPacket *packet = tls_create_packet(context, TLS_HANDSHAKE, context->version, 0); tls_packet_uint8(packet, 0x10); #ifdef TLS_FORWARD_SECRECY int ephemeral = tls_cipher_is_ephemeral(context); if ((ephemeral) && (context->premaster_key) && (context->premaster_key_len)) { if (ephemeral == 1) { unsigned char dh_Ys[0xFFF]; unsigned char dh_p[0xFFF]; unsigned char dh_g[0xFFF]; unsigned long dh_p_len = sizeof(dh_p); unsigned long dh_g_len = sizeof(dh_g); unsigned long dh_Ys_len = sizeof(dh_Ys); if (_private_tls_dh_export_pqY(dh_p, &dh_p_len, dh_g, &dh_g_len, dh_Ys, &dh_Ys_len, context->dhe)) { DEBUG_PRINT("ERROR EXPORTING DHE KEY %p\n", context->dhe); TLS_FREE(packet); _private_tls_dhe_free(context); return NULL; } _private_tls_dhe_free(context); DEBUG_DUMP_HEX_LABEL("Yc", dh_Ys, dh_Ys_len); tls_packet_uint24(packet, dh_Ys_len + 2); if (context->dtls) _private_dtls_handshake_data(context, packet, dh_Ys_len + 2); tls_packet_uint16(packet, dh_Ys_len); tls_packet_append(packet, dh_Ys, dh_Ys_len); } else if (context->ecc_dhe) { unsigned char out[TLS_MAX_RSA_KEY]; unsigned long out_len = TLS_MAX_RSA_KEY; if (ecc_ansi_x963_export(context->ecc_dhe, out, &out_len)) { DEBUG_PRINT("Error exporting ECC key\n"); TLS_FREE(packet); return NULL; } _private_tls_ecc_dhe_free(context); tls_packet_uint24(packet, out_len + 1); if (context->dtls) { _private_dtls_handshake_data(context, packet, out_len + 1); context->dtls_seq++; } tls_packet_uint8(packet, out_len); tls_packet_append(packet, out, out_len); } #ifdef TLS_CURVE25519 else if ((context->curve == &x25519) && (context->client_secret)) { static const unsigned char basepoint[32] = {9}; unsigned char shared_key[32]; curve25519(shared_key, context->client_secret, basepoint); tls_packet_uint24(packet, 32 + 1); tls_packet_uint8(packet, 32); tls_packet_append(packet, shared_key, 32); TLS_FREE(context->client_secret); context->client_secret = NULL; } #endif _private_tls_compute_key(context, 48); } else #endif _private_tls_build_random(packet); context->connection_status = 2; tls_packet_update(packet); return packet; } void _private_dtls_handshake_data(struct TLSContext *context, struct TLSPacket *packet, unsigned int framelength) { // message seq tls_packet_uint16(packet, context->dtls_seq); // fragment offset tls_packet_uint24(packet, 0); // fragment length tls_packet_uint24(packet, framelength); } void _private_dtls_handshake_copyframesize(struct TLSPacket *packet) { packet->buf[22] = packet->buf[14]; packet->buf[23] = packet->buf[15]; packet->buf[24] = packet->buf[16]; } struct TLSPacket *tls_build_server_key_exchange(struct TLSContext *context, int method) { if (!context->is_server) { DEBUG_PRINT("CANNOT BUILD SERVER KEY EXCHANGE MESSAGE FOR CLIENTS\n"); return NULL; } struct TLSPacket *packet = tls_create_packet(context, TLS_HANDSHAKE, context->version, 0); tls_packet_uint8(packet, 0x0C); unsigned char dummy[3]; tls_packet_append(packet, dummy, 3); if (context->dtls) _private_dtls_handshake_data(context, packet, 0); int start_len = packet->len; #ifdef TLS_FORWARD_SECRECY if (method == KEA_dhe_rsa) { tls_init(); _private_tls_dhe_create(context); const char *default_dhe_p = context->default_dhe_p; const char *default_dhe_g = context->default_dhe_g; int key_size; if ((!default_dhe_p) || (!default_dhe_g)) { default_dhe_p = TLS_DH_DEFAULT_P; default_dhe_g = TLS_DH_DEFAULT_G; key_size = TLS_DHE_KEY_SIZE / 8; } else { key_size = strlen(default_dhe_p); } if (_private_tls_dh_make_key(key_size, context->dhe, default_dhe_p, default_dhe_g, 0, 0)) { DEBUG_PRINT("ERROR CREATING DHE KEY\n"); TLS_FREE(packet); TLS_FREE(context->dhe); context->dhe = NULL; return NULL; } unsigned char dh_Ys[0xFFF]; unsigned char dh_p[0xFFF]; unsigned char dh_g[0xFFF]; unsigned long dh_p_len = sizeof(dh_p); unsigned long dh_g_len = sizeof(dh_g); unsigned long dh_Ys_len = sizeof(dh_Ys); if (_private_tls_dh_export_pqY(dh_p, &dh_p_len, dh_g, &dh_g_len, dh_Ys, &dh_Ys_len, context->dhe)) { DEBUG_PRINT("ERROR EXPORTING DHE KEY\n"); TLS_FREE(packet); return NULL; } DEBUG_PRINT("LEN: %lu (%lu, %lu)\n", dh_Ys_len, dh_p_len, dh_g_len); DEBUG_DUMP_HEX_LABEL("DHE PK", dh_Ys, dh_Ys_len); DEBUG_DUMP_HEX_LABEL("DHE P", dh_p, dh_p_len); DEBUG_DUMP_HEX_LABEL("DHE G", dh_g, dh_g_len); tls_packet_uint16(packet, dh_p_len); tls_packet_append(packet, dh_p, dh_p_len); tls_packet_uint16(packet, dh_g_len); tls_packet_append(packet, dh_g, dh_g_len); tls_packet_uint16(packet, dh_Ys_len); tls_packet_append(packet, dh_Ys, dh_Ys_len); //dh_p //dh_g //dh_Ys } else if (method == KEA_ec_diffie_hellman) { // 3 = named curve if (!context->curve) context->curve = default_curve; tls_packet_uint8(packet, 3); tls_packet_uint16(packet, context->curve->iana); tls_init(); _private_tls_ecc_dhe_create(context); ltc_ecc_set_type *dp = (ltc_ecc_set_type *)&context->curve->dp; if (ecc_make_key_ex(NULL, find_prng("sprng"), context->ecc_dhe, dp)) { TLS_FREE(context->ecc_dhe); context->ecc_dhe = NULL; DEBUG_PRINT("Error generating ECC key\n"); TLS_FREE(packet); return NULL; } unsigned char out[TLS_MAX_RSA_KEY]; unsigned long out_len = TLS_MAX_RSA_KEY; if (ecc_ansi_x963_export(context->ecc_dhe, out, &out_len)) { DEBUG_PRINT("Error exporting ECC key\n"); TLS_FREE(packet); return NULL; } tls_packet_uint8(packet, out_len); tls_packet_append(packet, out, out_len); } else #endif { TLS_FREE(packet); DEBUG_PRINT("Unsupported ephemeral method: %i\n", method); return NULL; } // signature unsigned int params_len = packet->len - start_len; unsigned int message_len = params_len + TLS_CLIENT_RANDOM_SIZE + TLS_SERVER_RANDOM_SIZE; unsigned char *message = (unsigned char *)TLS_MALLOC(message_len); if (message) { unsigned char out[TLS_MAX_RSA_KEY]; unsigned long out_len = TLS_MAX_RSA_KEY; int hash_algorithm; if ((context->version != TLS_V13) && (context->version != DTLS_V13) && (context->version != TLS_V12) && (context->version != DTLS_V12)) { hash_algorithm = _md5_sha1; } else { if ((context->version == TLS_V13) || (context->version == DTLS_V13) || (context->version == TLS_V12) || (context->version == DTLS_V12)) hash_algorithm = sha256; else hash_algorithm = sha1; #ifdef TLS_ECDSA_SUPPORTED if (tls_is_ecdsa(context)) { if ((context->version == TLS_V13) || (context->version == DTLS_V13) || (context->version == TLS_V12) || (context->version == DTLS_V12)) hash_algorithm = sha512; tls_packet_uint8(packet, hash_algorithm); tls_packet_uint8(packet, ecdsa); } else #endif { tls_packet_uint8(packet, hash_algorithm); tls_packet_uint8(packet, rsa_sign); } } memcpy(message, context->remote_random, TLS_CLIENT_RANDOM_SIZE); memcpy(message + TLS_CLIENT_RANDOM_SIZE, context->local_random, TLS_SERVER_RANDOM_SIZE); memcpy(message + TLS_CLIENT_RANDOM_SIZE + TLS_SERVER_RANDOM_SIZE, packet->buf + start_len, params_len); #ifdef TLS_ECDSA_SUPPORTED if (tls_is_ecdsa(context)) { if (_private_tls_sign_ecdsa(context, hash_algorithm, message, message_len, out, &out_len) == 1) { DEBUG_PRINT("Signing OK! (ECDSA, length %lu)\n", out_len); tls_packet_uint16(packet, out_len); tls_packet_append(packet, out, out_len); } } else #endif if (_private_tls_sign_rsa(context, hash_algorithm, message, message_len, out, &out_len) == 1) { DEBUG_PRINT("Signing OK! (length %lu)\n", out_len); tls_packet_uint16(packet, out_len); tls_packet_append(packet, out, out_len); } TLS_FREE(message); } if ((!packet->broken) && (packet->buf)) { int remaining = packet->len - start_len; int payload_pos = 6; if (context->dtls) payload_pos = 14; packet->buf[payload_pos] = remaining / 0x10000; remaining %= 0x10000; packet->buf[payload_pos + 1] = remaining / 0x100; remaining %= 0x100; packet->buf[payload_pos + 2] = remaining; if (context->dtls) { _private_dtls_handshake_copyframesize(packet); context->dtls_seq++; } } tls_packet_update(packet); return packet; } void _private_tls_set_session_id(struct TLSContext *context) { if (((context->version == TLS_V13) || (context->version == DTLS_V13)) && (context->session_size == TLS_MAX_SESSION_ID)) return; if (tls_random(context->session, TLS_MAX_SESSION_ID)) context->session_size = TLS_MAX_SESSION_ID; else context->session_size = 0; } struct TLSPacket *tls_build_hello(struct TLSContext *context, int tls13_downgrade) { tls_init(); #ifdef WITH_TLS_13 if (context->connection_status == 4) { static unsigned char sha256_helloretryrequest[] = {0xCF, 0x21, 0xAD, 0x74, 0xE5, 0x9A, 0x61, 0x11, 0xBE, 0x1D, 0x8C, 0x02, 0x1E, 0x65, 0xB8, 0x91, 0xC2, 0xA2, 0x11, 0x16, 0x7A, 0xBB, 0x8C, 0x5E, 0x07, 0x9E, 0x09, 0xE2, 0xC8, 0xA8, 0x33, 0x9C}; memcpy(context->local_random, sha256_helloretryrequest, 32); unsigned char header[4] = {0xFE, 0, 0, 0}; unsigned char hash[TLS_MAX_SHA_SIZE ]; int hash_len = _private_tls_done_hash(context, hash); header[3] = (unsigned char)hash_len; _private_tls_update_hash(context, header, sizeof(header)); _private_tls_update_hash(context, hash, hash_len); } else if ((!context->is_server) || ((context->version != TLS_V13) && (context->version != DTLS_V13))) #endif if (!tls_random(context->local_random, context->is_server ? TLS_SERVER_RANDOM_SIZE : TLS_CLIENT_RANDOM_SIZE)) return NULL; if (!context->is_server) *(unsigned int *)context->local_random = htonl((unsigned int)time(NULL)); if ((context->is_server) && (tls13_downgrade)) { if ((tls13_downgrade == TLS_V12) || (tls13_downgrade == DTLS_V12)) memcpy(context->local_random + TLS_SERVER_RANDOM_SIZE - 8, "DOWNGRD\x01", 8); else memcpy(context->local_random + TLS_SERVER_RANDOM_SIZE - 8, "DOWNGRD\x00", 8); } unsigned short packet_version = context->version; unsigned short version = context->version; #ifdef WITH_TLS_13 if (context->version == TLS_V13) version = TLS_V12; else if (context->version == DTLS_V13) version = DTLS_V12; #endif struct TLSPacket *packet = tls_create_packet(context, TLS_HANDSHAKE, version, 0); if (packet) { // hello if (context->is_server) tls_packet_uint8(packet, 0x02); else tls_packet_uint8(packet, 0x01); unsigned char dummy[3]; tls_packet_append(packet, dummy, 3); if (context->dtls) _private_dtls_handshake_data(context, packet, 0); int start_len = packet->len; tls_packet_uint16(packet, version); if (context->is_server) tls_packet_append(packet, context->local_random, TLS_SERVER_RANDOM_SIZE); else tls_packet_append(packet, context->local_random, TLS_CLIENT_RANDOM_SIZE); #ifdef IGNORE_SESSION_ID // session size tls_packet_uint8(packet, 0); #else _private_tls_set_session_id(context); // session size tls_packet_uint8(packet, context->session_size); if (context->session_size) tls_packet_append(packet, context->session, context->session_size); #endif int extension_len = 0; int alpn_len = 0; int alpn_negotiated_len = 0; int i; #ifdef WITH_TLS_13 unsigned char shared_key[TLS_MAX_RSA_KEY]; unsigned long shared_key_len = TLS_MAX_RSA_KEY; unsigned short shared_key_short = 0; int selected_group = 0; if ((context->version == TLS_V13) || (context->version == DTLS_V13)) { if (context->connection_status == 4) { // connection_status == 4 => hello retry request extension_len += 6; } else if (context->is_server) { #ifdef TLS_CURVE25519 if (context->curve == &x25519) { extension_len += 8 + 32; shared_key_short = (unsigned short)32; if (context->finished_key) { memcpy(shared_key, context->finished_key, 32); TLS_FREE(context->finished_key); context->finished_key = NULL; } selected_group = context->curve->iana; // make context->curve NULL (x25519 is a different implementation) context->curve = NULL; } else #endif if (context->ecc_dhe) { if (ecc_ansi_x963_export(context->ecc_dhe, shared_key, &shared_key_len)) { DEBUG_PRINT("Error exporting ECC DHE key\n"); tls_destroy_packet(packet); return tls_build_alert(context, 1, internal_error); } _private_tls_ecc_dhe_free(context); extension_len += 8 + shared_key_len; shared_key_short = (unsigned short)shared_key_len; if (context->curve) selected_group = context->curve->iana; } else if (context->dhe) { selected_group = context->dhe->iana; _private_tls_dh_export_Y(shared_key, &shared_key_len, context->dhe); _private_tls_dhe_free(context); extension_len += 8 + shared_key_len; shared_key_short = (unsigned short)shared_key_len; } } // supported versions if (context->is_server) extension_len += 6; else extension_len += 9; } if ((context->is_server) && (context->negotiated_alpn) && (context->version != TLS_V13) && (context->version != DTLS_V13)) { #else if ((context->is_server) && (context->negotiated_alpn)) { #endif alpn_negotiated_len = strlen(context->negotiated_alpn); alpn_len = alpn_negotiated_len + 1; extension_len += alpn_len + 6; } else if ((!context->is_server) && (context->alpn_count)) { for (i = 0; i < context->alpn_count;i++) { if (context->alpn[i]) { int len = strlen(context->alpn[i]); if (len) alpn_len += len + 1; } } if (alpn_len) extension_len += alpn_len + 6; } // ciphers if (context->is_server) { // fallback ... this should never happen if (!context->cipher) context->cipher = TLS_DHE_RSA_WITH_AES_128_CBC_SHA; tls_packet_uint16(packet, context->cipher); // no compression tls_packet_uint8(packet, 0); #ifndef STRICT_TLS if ((context->version == TLS_V13) || (context->version == DTLS_V13) || (context->version == TLS_V12) || (context->version == DTLS_V12)) { // extensions size #ifdef WITH_TLS_13 if ((context->version == TLS_V13) || (context->version == DTLS_V13)) { tls_packet_uint16(packet, extension_len); } else #endif { tls_packet_uint16(packet, 5 + extension_len); // secure renegotation // advertise it, but refuse renegotiation tls_packet_uint16(packet, 0xff01); #ifdef TLS_ACCEPT_SECURE_RENEGOTIATION // a little defensive if ((context->verify_len) && (!context->verify_data)) context->verify_len = 0; tls_packet_uint16(packet, context->verify_len + 1); tls_packet_uint8(packet, context->verify_len); if (context->verify_len) tls_packet_append(packet, (unsigned char *)context->verify_data, context->verify_len); #else tls_packet_uint16(packet, 1); tls_packet_uint8(packet, 0); #endif } if (alpn_len) { tls_packet_uint16(packet, 0x10); tls_packet_uint16(packet, alpn_len + 2); tls_packet_uint16(packet, alpn_len); tls_packet_uint8(packet, alpn_negotiated_len); tls_packet_append(packet, (unsigned char *)context->negotiated_alpn, alpn_negotiated_len); } } #endif } else { if (context->dtls) { tls_packet_uint8(packet, context->dtls_cookie_len); if (context->dtls_cookie_len) tls_packet_append(packet, context->dtls_cookie, context->dtls_cookie_len); } #ifndef STRICT_TLS #ifdef WITH_TLS_13 #ifdef TLS_FORWARD_SECRECY if ((context->version == TLS_V13) || (context->version == DTLS_V13)) { #ifdef TLS_WITH_CHACHA20_POLY1305 tls_packet_uint16(packet, TLS_CIPHERS_SIZE(9, 0)); tls_packet_uint16(packet, TLS_AES_128_GCM_SHA256); tls_packet_uint16(packet, TLS_AES_256_GCM_SHA384); tls_packet_uint16(packet, TLS_CHACHA20_POLY1305_SHA256); #else tls_packet_uint16(packet, TLS_CIPHERS_SIZE(8, 0)); tls_packet_uint16(packet, TLS_AES_128_GCM_SHA256); tls_packet_uint16(packet, TLS_AES_256_GCM_SHA384); #endif #ifdef TLS_PREFER_CHACHA20 tls_packet_uint16(packet, TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256); tls_packet_uint16(packet, TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256); tls_packet_uint16(packet, TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256); tls_packet_uint16(packet, TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256); tls_packet_uint16(packet, TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256); tls_packet_uint16(packet, TLS_DHE_RSA_WITH_AES_128_GCM_SHA256); #else tls_packet_uint16(packet, TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256); tls_packet_uint16(packet, TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256); tls_packet_uint16(packet, TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256); tls_packet_uint16(packet, TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256); tls_packet_uint16(packet, TLS_DHE_RSA_WITH_AES_128_GCM_SHA256); tls_packet_uint16(packet, TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256); #endif } else #endif #endif if ((context->version == TLS_V12) || (context->version == DTLS_V12)) { #endif #ifdef TLS_FORWARD_SECRECY #ifdef TLS_CLIENT_ECDHE #ifdef TLS_WITH_CHACHA20_POLY1305 #ifdef TLS_CLIENT_ECDSA tls_packet_uint16(packet, TLS_CIPHERS_SIZE(16, 5)); #ifdef TLS_PREFER_CHACHA20 tls_packet_uint16(packet, TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256); #endif tls_packet_uint16(packet, TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256); #ifndef TLS_PREFER_CHACHA20 tls_packet_uint16(packet, TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256); #endif tls_packet_uint16(packet, TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256); tls_packet_uint16(packet, TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA); tls_packet_uint16(packet, TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA); #else // sizeof ciphers (16 ciphers * 2 bytes) tls_packet_uint16(packet, TLS_CIPHERS_SIZE(11, 5)); #endif #else #ifdef TLS_CLIENT_ECDSA tls_packet_uint16(packet, TLS_CIPHERS_SIZE(13, 5)); tls_packet_uint16(packet, TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256); tls_packet_uint16(packet, TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256); tls_packet_uint16(packet, TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA); tls_packet_uint16(packet, TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA); #else // sizeof ciphers (14 ciphers * 2 bytes) tls_packet_uint16(packet, TLS_CIPHERS_SIZE(9, 5)); #endif #endif #ifdef TLS_WITH_CHACHA20_POLY1305 #ifdef TLS_PREFER_CHACHA20 tls_packet_uint16(packet, TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256); #endif #endif tls_packet_uint16(packet, TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256); tls_packet_uint16(packet, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA); tls_packet_uint16(packet, TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA); tls_packet_uint16(packet, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256); #ifdef TLS_WITH_CHACHA20_POLY1305 #ifndef TLS_PREFER_CHACHA20 tls_packet_uint16(packet, TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256); #endif #endif #else #ifdef TLS_WITH_CHACHA20_POLY1305 // sizeof ciphers (11 ciphers * 2 bytes) tls_packet_uint16(packet, TLS_CIPHERS_SIZE(6, 5)); #else // sizeof ciphers (10 ciphers * 2 bytes) tls_packet_uint16(packet, TLS_CIPHERS_SIZE(5, 5)); #endif #endif // not yet supported, because the first message sent (this one) // is already hashed by the client with sha256 (sha384 not yet supported client-side) // but is fully suported server-side // tls_packet_uint16(packet, TLS_DHE_RSA_WITH_AES_256_GCM_SHA384); tls_packet_uint16(packet, TLS_DHE_RSA_WITH_AES_128_GCM_SHA256); tls_packet_uint16(packet, TLS_DHE_RSA_WITH_AES_256_CBC_SHA256); tls_packet_uint16(packet, TLS_DHE_RSA_WITH_AES_128_CBC_SHA256); tls_packet_uint16(packet, TLS_DHE_RSA_WITH_AES_256_CBC_SHA); tls_packet_uint16(packet, TLS_DHE_RSA_WITH_AES_128_CBC_SHA); #ifdef TLS_WITH_CHACHA20_POLY1305 tls_packet_uint16(packet, TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256); #endif #else tls_packet_uint16(packet, TLS_CIPHERS_SIZE(0, 5)); #endif // tls_packet_uint16(packet, TLS_RSA_WITH_AES_256_GCM_SHA384); #ifndef TLS_ROBOT_MITIGATION tls_packet_uint16(packet, TLS_RSA_WITH_AES_128_GCM_SHA256); tls_packet_uint16(packet, TLS_RSA_WITH_AES_256_CBC_SHA256); tls_packet_uint16(packet, TLS_RSA_WITH_AES_128_CBC_SHA256); tls_packet_uint16(packet, TLS_RSA_WITH_AES_256_CBC_SHA); tls_packet_uint16(packet, TLS_RSA_WITH_AES_128_CBC_SHA); #endif #ifndef STRICT_TLS } else { #ifdef TLS_FORWARD_SECRECY #ifdef TLS_CLIENT_ECDHE tls_packet_uint16(packet, TLS_CIPHERS_SIZE(5, 2)); tls_packet_uint16(packet, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA); tls_packet_uint16(packet, TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA); #else tls_packet_uint16(packet, TLS_CIPHERS_SIZE(3, 2)); #endif tls_packet_uint16(packet, TLS_DHE_RSA_WITH_AES_256_CBC_SHA); tls_packet_uint16(packet, TLS_DHE_RSA_WITH_AES_256_CBC_SHA); tls_packet_uint16(packet, TLS_DHE_RSA_WITH_AES_128_CBC_SHA); #else tls_packet_uint16(packet, TLS_CIPHERS_SIZE(0, 2)); #endif #ifndef TLS_ROBOT_MITIGATION tls_packet_uint16(packet, TLS_RSA_WITH_AES_256_CBC_SHA); tls_packet_uint16(packet, TLS_RSA_WITH_AES_128_CBC_SHA); #endif } #endif // compression tls_packet_uint8(packet, 1); // no compression tls_packet_uint8(packet, 0); if ((context->version == TLS_V12) || (context->version == DTLS_V12) || (context->version == TLS_V13) || (context->version == DTLS_V13)) { int sni_len = 0; if (context->sni) sni_len = strlen(context->sni); #ifdef TLS_CLIENT_ECDHE extension_len += 12; #endif if (sni_len) extension_len += sni_len + 9; #ifdef WITH_TLS_13 if ((!context->is_server) && ((context->version == TLS_V13) || (context->version == DTLS_V13))) { #ifdef TLS_CURVE25519 extension_len += 70; #else // secp256r1 produces 65 bytes export extension_len += 103; #endif } #endif tls_packet_uint16(packet, extension_len); if (sni_len) { // sni extension tls_packet_uint16(packet, 0x00); // sni extension len tls_packet_uint16(packet, sni_len + 5); // sni len tls_packet_uint16(packet, sni_len + 3); // sni type tls_packet_uint8(packet, 0); // sni host len tls_packet_uint16(packet, sni_len); tls_packet_append(packet, (unsigned char *)context->sni, sni_len); } #ifdef TLS_FORWARD_SECRECY #ifdef TLS_CLIENT_ECDHE // supported groups tls_packet_uint16(packet, 0x0A); tls_packet_uint16(packet, 8); // 3 curves x 2 bytes tls_packet_uint16(packet, 6); tls_packet_uint16(packet, secp256r1.iana); tls_packet_uint16(packet, secp384r1.iana); #ifdef TLS_CURVE25519 tls_packet_uint16(packet, x25519.iana); #else tls_packet_uint16(packet, secp224r1.iana); #endif #endif #endif if (alpn_len) { tls_packet_uint16(packet, 0x10); tls_packet_uint16(packet, alpn_len + 2); tls_packet_uint16(packet, alpn_len); for (i = 0; i < context->alpn_count;i++) { if (context->alpn[i]) { int len = strlen(context->alpn[i]); if (len) { tls_packet_uint8(packet, len); tls_packet_append(packet, (unsigned char *)context->alpn[i], len); } } } } } } #ifdef WITH_TLS_13 if ((context->version == TLS_V13) || (context->version == DTLS_V13)) { // supported versions tls_packet_uint16(packet, 0x2B); if (context->is_server) { tls_packet_uint16(packet, 2); if (context->version == TLS_V13) tls_packet_uint16(packet, context->tls13_version ? context->tls13_version : TLS_V13); else tls_packet_uint16(packet, context->version); } else { tls_packet_uint16(packet, 5); tls_packet_uint8(packet, 4); tls_packet_uint16(packet, TLS_V13); tls_packet_uint16(packet, 0x7F1C); } if (context->connection_status == 4) { // fallback to the mandatory secp256r1 tls_packet_uint16(packet, 0x33); tls_packet_uint16(packet, 2); tls_packet_uint16(packet, (unsigned short)secp256r1.iana); } if (((shared_key_short) && (selected_group)) || (!context->is_server)) { // key share tls_packet_uint16(packet, 0x33); if (context->is_server) { tls_packet_uint16(packet, shared_key_short + 4); tls_packet_uint16(packet, (unsigned short)selected_group); tls_packet_uint16(packet, shared_key_short); tls_packet_append(packet, (unsigned char *)shared_key, shared_key_short); } else { #ifdef TLS_CURVE25519 // make key shared_key_short = 32; tls_packet_uint16(packet, shared_key_short + 6); tls_packet_uint16(packet, shared_key_short + 4); TLS_FREE(context->client_secret); context->client_secret = (unsigned char *)TLS_MALLOC(32); if (!context->client_secret) { DEBUG_PRINT("ERROR IN TLS_MALLOC"); TLS_FREE(packet); return NULL; } static const unsigned char basepoint[32] = {9}; tls_random(context->client_secret, 32); context->client_secret[0] &= 248; context->client_secret[31] &= 127; context->client_secret[31] |= 64; curve25519(shared_key, context->client_secret, basepoint); tls_packet_uint16(packet, (unsigned short)x25519.iana); tls_packet_uint16(packet, shared_key_short); tls_packet_append(packet, (unsigned char *)shared_key, shared_key_short); #else // make key shared_key_short = 65; tls_packet_uint16(packet, shared_key_short + 6); tls_packet_uint16(packet, shared_key_short + 4); _private_tls_ecc_dhe_create(context); ltc_ecc_set_type *dp = (ltc_ecc_set_type *)&secp256r1.dp; if (ecc_make_key_ex(NULL, find_prng("sprng"), context->ecc_dhe, dp)) { TLS_FREE(context->ecc_dhe); context->ecc_dhe = NULL; DEBUG_PRINT("Error generating ECC key\n"); TLS_FREE(packet); return NULL; } unsigned char out[TLS_MAX_RSA_KEY]; unsigned long out_len = shared_key_short; if (ecc_ansi_x963_export(context->ecc_dhe, out, &out_len)) { DEBUG_PRINT("Error exporting ECC key\n"); TLS_FREE(packet); return NULL; } tls_packet_uint16(packet, (unsigned short)secp256r1.iana); tls_packet_uint16(packet, out_len); tls_packet_append(packet, (unsigned char *)out, shared_key_short); #endif } } if (!context->is_server) { // signature algorithms tls_packet_uint16(packet, 0x0D); tls_packet_uint16(packet, 24); tls_packet_uint16(packet, 22); tls_packet_uint16(packet, 0x0403); tls_packet_uint16(packet, 0x0503); tls_packet_uint16(packet, 0x0603); tls_packet_uint16(packet, 0x0804); tls_packet_uint16(packet, 0x0805); tls_packet_uint16(packet, 0x0806); tls_packet_uint16(packet, 0x0401); tls_packet_uint16(packet, 0x0501); tls_packet_uint16(packet, 0x0601); tls_packet_uint16(packet, 0x0203); tls_packet_uint16(packet, 0x0201); } } #endif if ((!packet->broken) && (packet->buf)) { int remaining = packet->len - start_len; int payload_pos = 6; if (context->dtls) payload_pos = 14; packet->buf[payload_pos] = remaining / 0x10000; remaining %= 0x10000; packet->buf[payload_pos + 1] = remaining / 0x100; remaining %= 0x100; packet->buf[payload_pos + 2] = remaining; if (context->dtls) { _private_dtls_handshake_copyframesize(packet); context->dtls_seq++; } } tls_packet_update(packet); } return packet; } struct TLSPacket *tls_certificate_request(struct TLSContext *context) { if ((!context) || (!context->is_server)) return NULL; unsigned short packet_version = context->version; struct TLSPacket *packet = tls_create_packet(context, TLS_HANDSHAKE, packet_version, 0); if (packet) { // certificate request tls_packet_uint8(packet, 0x0D); unsigned char dummy[3]; tls_packet_append(packet, dummy, 3); if (context->dtls) _private_dtls_handshake_data(context, packet, 0); int start_len = packet->len; #ifdef WITH_TLS_13 if ((context->version == TLS_V13) || (context->version == DTLS_V13)) { // certificate request context tls_packet_uint8(packet, 0); // extensions tls_packet_uint16(packet, 18); // signature algorithms tls_packet_uint16(packet, 0x0D); tls_packet_uint16(packet, 14); tls_packet_uint16(packet, 12); // rsa_pkcs1_sha256 // tls_packet_uint16(packet, 0x0401); // rsa_pkcs1_sha384 // tls_packet_uint16(packet, 0x0501); // rsa_pkcs1_sha512 // tls_packet_uint16(packet, 0x0601); // ecdsa_secp256r1_sha256 tls_packet_uint16(packet, 0x0403); // ecdsa_secp384r1_sha384 tls_packet_uint16(packet, 0x0503); // ecdsa_secp521r1_sha512 tls_packet_uint16(packet, 0x0604); // rsa_pss_rsae_sha256 tls_packet_uint16(packet, 0x0804); // rsa_pss_rsae_sha384 tls_packet_uint16(packet, 0x0805); // rsa_pss_rsae_sha512 tls_packet_uint16(packet, 0x0806); } else #endif { tls_packet_uint8(packet, 1); tls_packet_uint8(packet, rsa_sign); if ((context->version == TLS_V12) || (context->version == DTLS_V12)) { // 10 pairs or 2 bytes tls_packet_uint16(packet, 10); tls_packet_uint8(packet, sha256); tls_packet_uint8(packet, rsa); tls_packet_uint8(packet, sha1); tls_packet_uint8(packet, rsa); tls_packet_uint8(packet, sha384); tls_packet_uint8(packet, rsa); tls_packet_uint8(packet, sha512); tls_packet_uint8(packet, rsa); tls_packet_uint8(packet, md5); tls_packet_uint8(packet, rsa); } // no DistinguishedName yet tls_packet_uint16(packet, 0); } if (!packet->broken) { int remaining = packet->len - start_len; int payload_pos = 6; if (context->dtls) payload_pos = 14; packet->buf[payload_pos] = remaining / 0x10000; remaining %= 0x10000; packet->buf[payload_pos + 1] = remaining / 0x100; remaining %= 0x100; packet->buf[payload_pos + 2] = remaining; if (context->dtls) { _private_dtls_handshake_copyframesize(packet); context->dtls_seq++; } } tls_packet_update(packet); } return packet; } int _private_dtls_build_cookie(struct TLSContext *context) { if ((!context->dtls_cookie) || (!context->dtls_cookie_len)) { context->dtls_cookie = (unsigned char *)TLS_MALLOC(DTLS_COOKIE_SIZE); if (!context->dtls_cookie) return 0; #ifdef WITH_RANDOM_DLTS_COOKIE if (!tls_random(context->dtls_cookie, DTLS_COOKIE_SIZE)) { TLS_FREE(context->dtls_cookie); context->dtls_cookie = NULL; return 0; } context->dtls_cookie_len = DTLS_COOKIE_SIZE; #else hmac_state hmac; hmac_init(&hmac, find_hash("sha256"), dtls_secret, sizeof(dtls_secret)); hmac_process(&hmac, context->remote_random, TLS_CLIENT_RANDOM_SIZE); unsigned long out_size = DTLS_COOKIE_SIZE; hmac_done(&hmac, context->dtls_cookie, &out_size); #endif } return 1; } struct TLSPacket *tls_build_verify_request(struct TLSContext *context) { if ((!context->is_server) || (!context->dtls)) return NULL; if ((!context->dtls_cookie) || (!context->dtls_cookie_len)) { if (!_private_dtls_build_cookie(context)) return NULL; } unsigned short packet_version = context->version; struct TLSPacket *packet = tls_create_packet(context, TLS_HANDSHAKE, packet_version, 0); if (packet) { // verify request tls_packet_uint8(packet, 0x03); // 24-bit length tls_packet_uint24(packet, context->dtls_cookie_len + 3); // 16-bit message_sequence tls_packet_uint16(packet, 0); // 24-bit fragment_offset tls_packet_uint24(packet, 0); // 24-bit fragment_offset tls_packet_uint24(packet, context->dtls_cookie_len + 3); // server_version tls_packet_uint16(packet, context->version); tls_packet_uint8(packet, context->dtls_cookie_len); tls_packet_append(packet, context->dtls_cookie, context->dtls_cookie_len); tls_packet_update(packet); } return packet; } int _private_dtls_check_packet(const unsigned char *buf, int buf_len) { CHECK_SIZE(11, buf_len, TLS_NEED_MORE_DATA) unsigned int bytes_to_follow = buf[0] * 0x10000 + buf[1] * 0x100 + buf[2]; // not used: unsigned short message_seq = ntohs(*(unsigned short *)&buf[3]); unsigned int fragment_offset = buf[5] * 0x10000 + buf[6] * 0x100 + buf[7]; unsigned int fragment_length = buf[8] * 0x10000 + buf[9] * 0x100 + buf[10]; if ((fragment_offset) || (fragment_length != bytes_to_follow)) { DEBUG_PRINT("FRAGMENTED PACKETS NOT SUPPORTED\n"); return TLS_FEATURE_NOT_SUPPORTED; } return bytes_to_follow; } void _private_dtls_reset(struct TLSContext *context) { context->dtls_epoch_local = 0; context->dtls_epoch_remote = 0; context->dtls_seq = 0; _private_tls_destroy_hash(context); context->connection_status = 0; } int tls_parse_verify_request(struct TLSContext *context, const unsigned char *buf, int buf_len, unsigned int *write_packets) { *write_packets = 0; if ((context->connection_status != 0) || (!context->dtls)) { DEBUG_PRINT("UNEXPECTED VERIFY REQUEST MESSAGE\n"); return TLS_UNEXPECTED_MESSAGE; } int res = 11; int bytes_to_follow = _private_dtls_check_packet(buf, buf_len); if (bytes_to_follow < 0) return bytes_to_follow; CHECK_SIZE(bytes_to_follow, buf_len - res, TLS_NEED_MORE_DATA) // not used: unsigned short version = ntohs(*(unsigned short *)&buf[res]); res += 2; unsigned char len = buf[res]; res++; TLS_FREE(context->dtls_cookie); context->dtls_cookie_len = 0; if (len) { CHECK_SIZE(len, buf_len - res, TLS_NEED_MORE_DATA) context->dtls_cookie = (unsigned char *)TLS_MALLOC(len); if (!context->dtls_cookie) return TLS_NO_MEMORY; context->dtls_cookie_len = len; memcpy(context->dtls_cookie, &buf[res], len); res += len; *write_packets = 4; } // reset context _private_dtls_reset(context); return res; } void _private_dtls_reset_cookie(struct TLSContext *context) { TLS_FREE(context->dtls_cookie); context->dtls_cookie = NULL; context->dtls_cookie_len = 0; } #ifdef WITH_TLS_13 int _private_tls_parse_key_share(struct TLSContext *context, const unsigned char *buf, int buf_len) { int i = 0; struct ECCCurveParameters *curve = 0; DHKey *dhkey = 0; int dhe_key_size = 0; const unsigned char *buffer = NULL; unsigned char *out2; unsigned long out_size; unsigned short key_size = 0; while (buf_len >= 4) { unsigned short named_group = ntohs(*(unsigned short *)&buf[i]); i += 2; buf_len -= 2; key_size = ntohs(*(unsigned short *)&buf[i]); i += 2; buf_len -= 2; if (key_size > buf_len) return TLS_BROKEN_PACKET; switch (named_group) { case 0x0017: curve = &secp256r1; buffer = &buf[i]; DEBUG_PRINT("KEY SHARE => secp256r1\n"); buf_len = 0; continue; case 0x0018: // secp384r1 curve = &secp384r1; buffer = &buf[i]; DEBUG_PRINT("KEY SHARE => secp384r1\n"); buf_len = 0; continue; case 0x0019: // secp521r1 break; case 0x001D: // x25519 #ifdef TLS_CURVE25519 if (key_size != 32) { DEBUG_PRINT("INVALID x25519 KEY SIZE (%i)\n", key_size); continue; } curve = &x25519; buffer = &buf[i]; DEBUG_PRINT("KEY SHARE => x25519\n"); buf_len = 0; continue; #endif break; case 0x001E: // x448 break; case 0x0100: dhkey = &ffdhe2048; dhe_key_size = 2048; break; case 0x0101: dhkey = &ffdhe3072; dhe_key_size = 3072; break; case 0x0102: dhkey = &ffdhe4096; dhe_key_size = 4096; break; case 0x0103: dhkey = &ffdhe6144; dhe_key_size = 6144; break; case 0x0104: dhkey = &ffdhe8192; dhe_key_size = 8192; break; } i += key_size; buf_len -= key_size; if (!context->is_server) break; } tls_init(); if (curve) { context->curve = curve; #ifdef TLS_CURVE25519 if (curve == &x25519) { if ((context->is_server) && (!tls_random(context->local_random, TLS_SERVER_RANDOM_SIZE))) return TLS_GENERIC_ERROR; unsigned char secret[32]; static const unsigned char basepoint[32] = {9}; if ((context->is_server) || (!context->client_secret)) { tls_random(secret, 32); secret[0] &= 248; secret[31] &= 127; secret[31] |= 64; // use finished key to store public key TLS_FREE(context->finished_key); context->finished_key = (unsigned char *)TLS_MALLOC(32); if (!context->finished_key) return TLS_GENERIC_ERROR; curve25519(context->finished_key, secret, basepoint); TLS_FREE(context->premaster_key); context->premaster_key = (unsigned char *)TLS_MALLOC(32); if (!context->premaster_key) return TLS_GENERIC_ERROR; curve25519(context->premaster_key, secret, buffer); context->premaster_key_len = 32; } else { TLS_FREE(context->premaster_key); context->premaster_key = (unsigned char *)TLS_MALLOC(32); if (!context->premaster_key) return TLS_GENERIC_ERROR; curve25519(context->premaster_key, context->client_secret, buffer); context->premaster_key_len = 32; TLS_FREE(context->client_secret); context->client_secret = NULL; } DEBUG_DUMP_HEX_LABEL("x25519 KEY", context->premaster_key, context->premaster_key_len); return 0; } #endif if (context->is_server) { _private_tls_ecc_dhe_create(context); if (ecc_make_key_ex(NULL, find_prng("sprng"), context->ecc_dhe, (ltc_ecc_set_type *)&context->curve->dp)) { TLS_FREE(context->ecc_dhe); context->ecc_dhe = NULL; DEBUG_PRINT("Error generating ECC DHE key\n"); return TLS_GENERIC_ERROR; } } ltc_ecc_set_type *dp = (ltc_ecc_set_type *)&context->curve->dp; if ((context->is_server) && (!tls_random(context->local_random, TLS_SERVER_RANDOM_SIZE))) return TLS_GENERIC_ERROR; ecc_key client_key; memset(&client_key, 0, sizeof(client_key)); if (ecc_ansi_x963_import_ex(buffer, key_size, &client_key, dp)) { DEBUG_PRINT("Error importing ECC DHE key\n"); return TLS_GENERIC_ERROR; } out2 = (unsigned char *)TLS_MALLOC(key_size); out_size = key_size; int err = ecc_shared_secret(context->ecc_dhe, &client_key, out2, &out_size); ecc_free(&client_key); if (err) { DEBUG_PRINT("ECC DHE DECRYPT ERROR %i\n", err); TLS_FREE(out2); return TLS_GENERIC_ERROR; } DEBUG_PRINT("OUT_SIZE: %lu\n", out_size); DEBUG_DUMP_HEX_LABEL("ECC DHE", out2, out_size); TLS_FREE(context->premaster_key); context->premaster_key = out2; context->premaster_key_len = out_size; return 0; } else if ((dhkey) && (buffer)) { _private_tls_dhe_create(context); if (!tls_random(context->local_random, TLS_SERVER_RANDOM_SIZE)) return TLS_GENERIC_ERROR; if (_private_tls_dh_make_key(dhe_key_size / 8, context->dhe, (const char *)dhkey->p, (const char *)dhkey->g, 0, 0)) { TLS_FREE(context->dhe); context->dhe = NULL; DEBUG_PRINT("Error generating DHE key\n"); return TLS_GENERIC_ERROR; } unsigned int dhe_out_size; out2 = _private_tls_decrypt_dhe(context, buffer, key_size, &dhe_out_size, 0); if (!out2) { DEBUG_PRINT("Error generating DHE shared key\n"); return TLS_GENERIC_ERROR; } TLS_FREE(context->premaster_key); context->premaster_key = out2; context->premaster_key_len = dhe_out_size; if (context->dhe) context->dhe->iana = dhkey->iana; return 0; } DEBUG_PRINT("NO COMMON KEY SHARE SUPPORTED\n"); return TLS_NO_COMMON_CIPHER; } #endif int tls_parse_hello(struct TLSContext *context, const unsigned char *buf, int buf_len, unsigned int *write_packets, unsigned int *dtls_verified) { *write_packets = 0; *dtls_verified = 0; if ((context->connection_status != 0) && (context->connection_status != 4)) { // ignore multiple hello on dtls if (context->dtls) { DEBUG_PRINT("RETRANSMITTED HELLO MESSAGE RECEIVED\n"); return 1; } DEBUG_PRINT("UNEXPECTED HELLO MESSAGE\n"); return TLS_UNEXPECTED_MESSAGE; } int res = 0; int downgraded = 0; int hello_min_size = context->dtls ? TLS_CLIENT_HELLO_MINSIZE + 8 : TLS_CLIENT_HELLO_MINSIZE; CHECK_SIZE(hello_min_size, buf_len, TLS_NEED_MORE_DATA) // big endian unsigned int bytes_to_follow = buf[0] * 0x10000 + buf[1] * 0x100 + buf[2]; res += 3; if (context->dtls) { int dtls_check = _private_dtls_check_packet(buf, buf_len); if (dtls_check < 0) return dtls_check; // 16 bit message seq + 24 bit fragment offset + 24 bit fragment length res += 8; } CHECK_SIZE(bytes_to_follow, buf_len - res, TLS_NEED_MORE_DATA) CHECK_SIZE(2, buf_len - res, TLS_NEED_MORE_DATA) unsigned short version = ntohs(*(unsigned short *)&buf[res]); unsigned short cipher = 0; res += 2; VERSION_SUPPORTED(version, TLS_NOT_SAFE) DEBUG_PRINT("VERSION REQUIRED BY REMOTE %x, VERSION NOW %x\n", (int)version, (int)context->version); #ifdef TLS_LEGACY_SUPPORT // when no legacy support, don't downgrade #ifndef TLS_FORCE_LOCAL_VERSION // downgrade ? if (context->dtls) { // for dlts, newer version has lower id (1.0 = FEFF, 1.2 = FEFD) if (context->version < version) downgraded = 1; } else { if (context->version > version) downgraded = 1; } if (downgraded) { context->version = version; if (!context->is_server) _private_tls_change_hash_type(context); } #endif #endif memcpy(context->remote_random, &buf[res], TLS_CLIENT_RANDOM_SIZE); res += TLS_CLIENT_RANDOM_SIZE; unsigned char session_len = buf[res++]; CHECK_SIZE(session_len, buf_len - res, TLS_NEED_MORE_DATA) if ((session_len) && (session_len <= TLS_MAX_SESSION_ID)) { memcpy(context->session, &buf[res], session_len); context->session_size = session_len; DEBUG_DUMP_HEX_LABEL("REMOTE SESSION ID: ", context->session, context->session_size); } else context->session_size = 0; res += session_len; const unsigned char *cipher_buffer = NULL; unsigned short cipher_len = 0; int scsv_set = 0; if (context->is_server) { if (context->dtls) { CHECK_SIZE(1, buf_len - res, TLS_NEED_MORE_DATA) unsigned char tls_cookie_len = buf[res++]; if (tls_cookie_len) { CHECK_SIZE(tls_cookie_len, buf_len - res, TLS_NEED_MORE_DATA) if ((!context->dtls_cookie_len) || (!context->dtls_cookie)) _private_dtls_build_cookie(context); if ((context->dtls_cookie_len != tls_cookie_len) || (!context->dtls_cookie)) { *dtls_verified = 2; _private_dtls_reset_cookie(context); DEBUG_PRINT("INVALID DTLS COOKIE\n"); return TLS_BROKEN_PACKET; } if (memcmp(context->dtls_cookie, &buf[res], tls_cookie_len)) { *dtls_verified = 3; _private_dtls_reset_cookie(context); DEBUG_PRINT("MISMATCH DTLS COOKIE\n"); return TLS_BROKEN_PACKET; } _private_dtls_reset_cookie(context); context->dtls_seq++; *dtls_verified = 1; res += tls_cookie_len; } else { *write_packets = 2; return buf_len; } } CHECK_SIZE(2, buf_len - res, TLS_NEED_MORE_DATA) cipher_len = ntohs(*(unsigned short *)&buf[res]); res += 2; CHECK_SIZE(cipher_len, buf_len - res, TLS_NEED_MORE_DATA) // faster than cipher_len % 2 if (cipher_len & 1) return TLS_BROKEN_PACKET; cipher_buffer = &buf[res]; res += cipher_len; CHECK_SIZE(1, buf_len - res, TLS_NEED_MORE_DATA) unsigned char compression_list_size = buf[res++]; CHECK_SIZE(compression_list_size, buf_len - res, TLS_NEED_MORE_DATA) // no compression support res += compression_list_size; } else { CHECK_SIZE(2, buf_len - res, TLS_NEED_MORE_DATA) cipher = ntohs(*(unsigned short *)&buf[res]); res += 2; context->cipher = cipher; #ifndef WITH_TLS_13 if (!tls_cipher_supported(context, cipher)) { context->cipher = 0; DEBUG_PRINT("NO CIPHER SUPPORTED\n"); return TLS_NO_COMMON_CIPHER; } DEBUG_PRINT("CIPHER: %s\n", tls_cipher_name(context)); #endif CHECK_SIZE(1, buf_len - res, TLS_NEED_MORE_DATA) unsigned char compression = buf[res++]; if (compression != 0) { DEBUG_PRINT("COMPRESSION NOT SUPPORTED\n"); return TLS_COMPRESSION_NOT_SUPPORTED; } } if (res > 0) { if (context->is_server) *write_packets = 2; if (context->connection_status != 4) context->connection_status = 1; } if (res > 2) res += 2; #ifdef WITH_TLS_13 const unsigned char *key_share = NULL; unsigned short key_size = 0; #endif while (buf_len - res >= 4) { // have extensions unsigned short extension_type = ntohs(*(unsigned short *)&buf[res]); res += 2; unsigned short extension_len = ntohs(*(unsigned short *)&buf[res]); res += 2; DEBUG_PRINT("Extension: 0x0%x (%i), len: %i\n", (int)extension_type, (int)extension_type, (int)extension_len); if (extension_len) { // SNI extension CHECK_SIZE(extension_len, buf_len - res, TLS_NEED_MORE_DATA) if (extension_type == 0x00) { // unsigned short sni_len = ntohs(*(unsigned short *)&buf[res]); // unsigned char sni_type = buf[res + 2]; unsigned short sni_host_len = ntohs(*(unsigned short *)&buf[res + 3]); CHECK_SIZE(sni_host_len, buf_len - res - 5, TLS_NEED_MORE_DATA) if (sni_host_len) { TLS_FREE(context->sni); context->sni = (char *)TLS_MALLOC(sni_host_len + 1); if (context->sni) { memcpy(context->sni, &buf[res + 5], sni_host_len); context->sni[sni_host_len] = 0; DEBUG_PRINT("SNI HOST INDICATOR: [%s]\n", context->sni); } } } else #ifdef TLS_FORWARD_SECRECY if (extension_type == 0x0A) { // supported groups if (buf_len - res > 2) { unsigned short group_len = ntohs(*(unsigned short *)&buf[res]); if (buf_len - res >= group_len + 2) { DEBUG_DUMP_HEX_LABEL("SUPPORTED GROUPS", &buf[res + 2], group_len); int i; int selected = 0; for (i = 0; i < group_len; i += 2) { unsigned short iana_n = ntohs(*(unsigned short *)&buf[res + 2 + i]); switch (iana_n) { case 23: context->curve = &secp256r1; selected = 1; break; case 24: context->curve = &secp384r1; selected = 1; break; #ifdef WITH_TLS_13 // needs different implementation // case 29: // context->curve = &x25519; // selected = 1; // break; #endif // do not use it anymore // case 25: // context->curve = &secp521r1; // selected = 1; // break; } if (selected) { DEBUG_PRINT("SELECTED CURVE %s\n", context->curve->name); break; } } } } } else #endif if ((extension_type == 0x10) && (context->alpn) && (context->alpn_count)) { if (buf_len - res > 2) { unsigned short alpn_len = ntohs(*(unsigned short *)&buf[res]); if ((alpn_len) && (alpn_len <= extension_len - 2)) { unsigned char *alpn = (unsigned char *)&buf[res + 2]; int alpn_pos = 0; while (alpn_pos < alpn_len) { unsigned char alpn_size = alpn[alpn_pos++]; if (alpn_size + alpn_pos >= extension_len) break; if ((alpn_size) && (tls_alpn_contains(context, (char *)&alpn[alpn_pos], alpn_size))) { TLS_FREE(context->negotiated_alpn); context->negotiated_alpn = (char *)TLS_MALLOC(alpn_size + 1); if (context->negotiated_alpn) { memcpy(context->negotiated_alpn, &alpn[alpn_pos], alpn_size); context->negotiated_alpn[alpn_size] = 0; DEBUG_PRINT("NEGOTIATED ALPN: %s\n", context->negotiated_alpn); } break; } alpn_pos += alpn_size; // ServerHello contains just one alpn if (!context->is_server) break; } } } } else if (extension_type == 0x0D) { // supported signatures DEBUG_DUMP_HEX_LABEL("SUPPORTED SIGNATURES", &buf[res], extension_len); } else if (extension_type == 0x0B) { // supported point formats DEBUG_DUMP_HEX_LABEL("SUPPORTED POINT FORMATS", &buf[res], extension_len); } #ifdef WITH_TLS_13 else if (extension_type == 0x2B) { // supported versions if ((context->is_server) && (buf[res] == extension_len - 1)) { if (extension_len > 2) { DEBUG_DUMP_HEX_LABEL("SUPPORTED VERSIONS", &buf[res], extension_len); int i; int limit = (int)buf[res]; if (limit == extension_len - 1) { for (i = 1; i < limit; i += 2) { if ((ntohs(*(unsigned short *)&buf[res + i]) == TLS_V13) || (ntohs(*(unsigned short *)&buf[res + i]) == 0x7F1C)) { context->version = TLS_V13; context->tls13_version = ntohs(*(unsigned short *)&buf[res + i]); DEBUG_PRINT("TLS 1.3 SUPPORTED\n"); break; } } } } } else if ((!context->is_server) && (extension_len == 2)) { if ((ntohs(*(unsigned short *)&buf[res]) == TLS_V13) || (ntohs(*(unsigned short *)&buf[res]) == 0x7F1C)) { context->version = TLS_V13; context->tls13_version = ntohs(*(unsigned short *)&buf[res]); DEBUG_PRINT("TLS 1.3 SUPPORTED\n"); } } } else if (extension_type == 0x2A) { // early data DEBUG_DUMP_HEX_LABEL("EXTENSION, EARLY DATA", &buf[res], extension_len); } else if (extension_type == 0x29) { // pre shared key DEBUG_DUMP_HEX_LABEL("EXTENSION, PRE SHARED KEY", &buf[res], extension_len); } else if (extension_type == 0x33) { // key share if (context->is_server) { key_size = ntohs(*(unsigned short *)&buf[res]); if ((context->is_server) && (key_size > extension_len - 2)) { DEBUG_PRINT("BROKEN KEY SHARE\n"); return TLS_BROKEN_PACKET; } } else { key_size = extension_len; } DEBUG_DUMP_HEX_LABEL("EXTENSION, KEY SHARE", &buf[res], extension_len); if (context->is_server) key_share = &buf[res + 2]; else key_share = &buf[res]; } else if (extension_type == 0x0D) { // signature algorithms DEBUG_DUMP_HEX_LABEL("EXTENSION, SIGNATURE ALGORITHMS", &buf[res], extension_len); } else if (extension_type == 0x2D) { // psk key exchange modes DEBUG_DUMP_HEX_LABEL("EXTENSION, PSK KEY EXCHANGE MODES", &buf[res], extension_len); } #endif res += extension_len; } } if (buf_len != res) return TLS_NEED_MORE_DATA; if ((context->is_server) && (cipher_buffer) && (cipher_len)) { int cipher = tls_choose_cipher(context, cipher_buffer, cipher_len, &scsv_set); if (cipher < 0) { DEBUG_PRINT("NO COMMON CIPHERS\n"); return cipher; } if ((downgraded) && (scsv_set)) { DEBUG_PRINT("NO DOWNGRADE (SCSV SET)\n"); _private_tls_write_packet(tls_build_alert(context, 1, inappropriate_fallback)); context->critical_error = 1; return TLS_NOT_SAFE; } context->cipher = cipher; } #ifdef WITH_TLS_13 if (!context->is_server) { if (!tls_cipher_supported(context, cipher)) { context->cipher = 0; DEBUG_PRINT("NO CIPHER SUPPORTED\n"); return TLS_NO_COMMON_CIPHER; } DEBUG_PRINT("CIPHER: %s\n", tls_cipher_name(context)); } if ((key_share) && (key_size) && ((context->version == TLS_V13) || (context->version == DTLS_V13))) { int key_share_err = _private_tls_parse_key_share(context, key_share, key_size); if (key_share_err) { // request hello retry if (context->connection_status != 4) { *write_packets = 5; context->hs_messages[1] = 0; context->connection_status = 4; return res; } else return key_share_err; } // we have key share if (context->is_server) context->connection_status = 3; else context->connection_status = 2; } #endif return res; } int tls_parse_certificate(struct TLSContext *context, const unsigned char *buf, int buf_len, int is_client) { int res = 0; CHECK_SIZE(3, buf_len, TLS_NEED_MORE_DATA) unsigned int size_of_all_certificates = buf[0] * 0x10000 + buf[1] * 0x100 + buf[2]; if (size_of_all_certificates <= 4) return 3 + size_of_all_certificates; res += 3; if (context->dtls) { int dtls_check = _private_dtls_check_packet(buf, buf_len); if (dtls_check < 0) return dtls_check; res += 8; } #ifdef WITH_TLS_13 if ((context->version == TLS_V13) || (context->version == DTLS_V13)) { int context_size = buf[res]; res++; // must be 0 if (context_size) res += context_size; } #endif CHECK_SIZE(size_of_all_certificates, buf_len - res, TLS_NEED_MORE_DATA); int size = size_of_all_certificates; int idx = 0; int valid_certificate = 0; while (size > 0) { idx++; CHECK_SIZE(3, buf_len - res, TLS_NEED_MORE_DATA); unsigned int certificate_size = buf[res] * 0x10000 + buf[res + 1] * 0x100 + buf[res + 2]; res += 3; CHECK_SIZE(certificate_size, buf_len - res, TLS_NEED_MORE_DATA) // load chain int certificates_in_chain = 0; int res2 = res; unsigned int remaining = certificate_size; do { if (remaining <= 3) break; certificates_in_chain++; unsigned int certificate_size2 = buf[res2] * 0x10000 + buf[res2 + 1] * 0x100 + buf[res2 + 2]; res2 += 3; remaining -= 3; if (certificate_size2 > remaining) { DEBUG_PRINT("Invalid certificate size (%i from %i bytes remaining)\n", certificate_size2, remaining); break; } remaining -= certificate_size2; struct TLSCertificate *cert = asn1_parse(context, &buf[res2], certificate_size2, is_client); if (cert) { if (certificate_size2) { cert->bytes = (unsigned char *)TLS_MALLOC(certificate_size2); if (cert->bytes) { cert->len = certificate_size2; memcpy(cert->bytes, &buf[res2], certificate_size2); } } // valid certificate if (is_client) { valid_certificate = 1; context->client_certificates = (struct TLSCertificate **)TLS_REALLOC(context->client_certificates, (context->client_certificates_count + 1) * sizeof(struct TLSCertificate *)); context->client_certificates[context->client_certificates_count] = cert; context->client_certificates_count++; } else { context->certificates = (struct TLSCertificate **)TLS_REALLOC(context->certificates, (context->certificates_count + 1) * sizeof(struct TLSCertificate *)); context->certificates[context->certificates_count] = cert; context->certificates_count++; if ((cert->pk) || (cert->priv)) valid_certificate = 1; else if (!context->is_server) valid_certificate = 1; } } res2 += certificate_size2; #ifdef WITH_TLS_13 // extension if ((context->version == TLS_V13) || (context->version == DTLS_V13)) { if (remaining >= 2) { // ignore extensions remaining -= 2; unsigned short size = ntohs(*(unsigned short *)&buf[res2]); if ((size) && (size >= remaining)) { res2 += size; remaining -= size; } } } #endif } while (remaining > 0); if (remaining) { DEBUG_PRINT("Extra %i bytes after certificate\n", remaining); } size -= certificate_size + 3; res += certificate_size; } if (!valid_certificate) return TLS_UNSUPPORTED_CERTIFICATE; if (res != buf_len) { DEBUG_PRINT("Warning: %i bytes read from %i byte buffer\n", (int)res, (int)buf_len); } return res; } int _private_tls_parse_dh(const unsigned char *buf, int buf_len, const unsigned char **out, int *out_size) { int res = 0; *out = NULL; *out_size = 0; CHECK_SIZE(2, buf_len, TLS_NEED_MORE_DATA) unsigned short size = ntohs(*(unsigned short *)buf); res += 2; CHECK_SIZE(size, buf_len - res, TLS_NEED_MORE_DATA) DEBUG_DUMP_HEX(&buf[res], size); *out = &buf[res]; *out_size = size; res += size; return res; } int _private_tls_parse_random(struct TLSContext *context, const unsigned char *buf, int buf_len) { int res = 0; int ephemeral = tls_cipher_is_ephemeral(context); unsigned short size; if (ephemeral == 2) { CHECK_SIZE(1, buf_len, TLS_NEED_MORE_DATA) size = buf[0]; res += 1; } else { CHECK_SIZE(2, buf_len, TLS_NEED_MORE_DATA) size = ntohs(*(unsigned short *)buf); res += 2; } CHECK_SIZE(size, buf_len - res, TLS_NEED_MORE_DATA) unsigned int out_len = 0; unsigned char *random = NULL; switch (ephemeral) { #ifdef TLS_FORWARD_SECRECY case 1: random = _private_tls_decrypt_dhe(context, &buf[res], size, &out_len, 1); break; case 2: random = _private_tls_decrypt_ecc_dhe(context, &buf[res], size, &out_len, 1); break; #endif default: random = _private_tls_decrypt_rsa(context, &buf[res], size, &out_len); } if ((random) && (out_len > 2)) { DEBUG_DUMP_HEX_LABEL("PRE MASTER KEY", random, out_len); TLS_FREE(context->premaster_key); context->premaster_key = random; context->premaster_key_len = out_len; _private_tls_compute_key(context, 48); } else { TLS_FREE(random); return 0; } res += size; return res; } int _private_tls_build_random(struct TLSPacket *packet) { int res = 0; unsigned char rand_bytes[48]; int bytes = 48; if (!tls_random(rand_bytes, bytes)) return TLS_GENERIC_ERROR; // max supported version if (packet->context->is_server) *(unsigned short *)rand_bytes = htons(packet->context->version); else if (packet->context->dtls) *(unsigned short *)rand_bytes = htons(DTLS_V12); else *(unsigned short *)rand_bytes = htons(TLS_V12); //DEBUG_DUMP_HEX_LABEL("PREMASTER KEY", rand_bytes, bytes); TLS_FREE(packet->context->premaster_key); packet->context->premaster_key = (unsigned char *)TLS_MALLOC(bytes); if (!packet->context->premaster_key) return TLS_NO_MEMORY; packet->context->premaster_key_len = bytes; memcpy(packet->context->premaster_key, rand_bytes, packet->context->premaster_key_len); unsigned int out_len; unsigned char *random = _private_tls_encrypt_rsa(packet->context, packet->context->premaster_key, packet->context->premaster_key_len, &out_len); _private_tls_compute_key(packet->context, bytes); if ((random) && (out_len > 2)) { tls_packet_uint24(packet, out_len + 2); if (packet->context->dtls) _private_dtls_handshake_data(packet->context, packet, out_len + 2); tls_packet_uint16(packet, out_len); tls_packet_append(packet, random, out_len); } else res = TLS_GENERIC_ERROR; TLS_FREE(random); if (res) return res; return out_len + 2; } const unsigned char *_private_tls_parse_signature(struct TLSContext *context, const unsigned char *buf, int buf_len, int *hash_algorithm, int *sign_algorithm, int *sig_size, int *offset) { int res = 0; CHECK_SIZE(2, buf_len, NULL) *hash_algorithm = _md5_sha1; *sign_algorithm = rsa_sign; *sig_size = 0; if ((context->version == TLS_V12) || (context->version == DTLS_V12) || (context->version == TLS_V13) || (context->version == DTLS_V13)) { *hash_algorithm = buf[res]; res++; *sign_algorithm = buf[res]; res++; } unsigned short size = ntohs(*(unsigned short *)&buf[res]); res += 2; CHECK_SIZE(size, buf_len - res, NULL) DEBUG_DUMP_HEX(&buf[res], size); *sig_size = size; *offset = res + size; return &buf[res]; } int tls_parse_server_key_exchange(struct TLSContext *context, const unsigned char *buf, int buf_len) { int res = 0; int dh_res = 0; CHECK_SIZE(3, buf_len, TLS_NEED_MORE_DATA) unsigned int size = buf[0] * 0x10000 + buf[1] * 0x100 + buf[2]; res += 3; if (context->dtls) { int dtls_check = _private_dtls_check_packet(buf, buf_len); if (dtls_check < 0) return dtls_check; res += 8; } const unsigned char *packet_ref = buf + res; CHECK_SIZE(size, buf_len - res, TLS_NEED_MORE_DATA); if (!size) return res; unsigned char has_ds_params = 0; unsigned int key_size = 0; #ifdef TLS_FORWARD_SECRECY const struct ECCCurveParameters *curve = NULL; const unsigned char *pk_key = NULL; int ephemeral = tls_cipher_is_ephemeral(context); if (ephemeral) { if (ephemeral == 1) { has_ds_params = 1; } else { if (buf[res++] != 3) { // named curve // any other method is not supported return 0; } CHECK_SIZE(3, buf_len - res, TLS_NEED_MORE_DATA); int iana_n = ntohs(*(unsigned short *)&buf[res]); res += 2; key_size = buf[res]; res++; CHECK_SIZE(key_size, buf_len - res, TLS_NEED_MORE_DATA); DEBUG_PRINT("IANA CURVE NUMBER: %i\n", iana_n); switch (iana_n) { case 19: curve = &secp192r1; break; case 20: curve = &secp224k1; break; case 21: curve = &secp224r1; break; case 22: curve = &secp256k1; break; case 23: curve = &secp256r1; break; case 24: curve = &secp384r1; break; case 25: curve = &secp521r1; break; #ifdef TLS_CURVE25519 case 29: curve = &x25519; break; #endif default: DEBUG_PRINT("UNSUPPORTED CURVE\n"); return TLS_GENERIC_ERROR; } pk_key = &buf[res]; res += key_size; context->curve = curve; } } #endif const unsigned char *dh_p = NULL; int dh_p_len = 0; const unsigned char *dh_g = NULL; int dh_g_len = 0; const unsigned char *dh_Ys = NULL; int dh_Ys_len = 0; if (has_ds_params) { DEBUG_PRINT(" dh_p: "); dh_res = _private_tls_parse_dh(&buf[res], buf_len - res, &dh_p, &dh_p_len); if (dh_res <= 0) return TLS_BROKEN_PACKET; res += dh_res; DEBUG_PRINT("\n"); DEBUG_PRINT(" dh_q: "); dh_res = _private_tls_parse_dh(&buf[res], buf_len - res, &dh_g, &dh_g_len); if (dh_res <= 0) return TLS_BROKEN_PACKET; res += dh_res; DEBUG_PRINT("\n"); DEBUG_PRINT(" dh_Ys: "); dh_res = _private_tls_parse_dh(&buf[res], buf_len - res, &dh_Ys, &dh_Ys_len); if (dh_res <= 0) return TLS_BROKEN_PACKET; res += dh_res; DEBUG_PRINT("\n"); } int sign_size; int hash_algorithm; int sign_algorithm; int packet_size = res - 3; if (context->dtls) packet_size -= 8; int offset = 0; DEBUG_PRINT(" SIGNATURE (%i/%i/%i): ", packet_size, dh_res, key_size); const unsigned char *signature = _private_tls_parse_signature(context, &buf[res], buf_len - res, &hash_algorithm, &sign_algorithm, &sign_size, &offset); DEBUG_PRINT("\n"); if ((sign_size <= 0) || (!signature)) return TLS_BROKEN_PACKET; res += offset; // check signature unsigned int message_len = packet_size + TLS_CLIENT_RANDOM_SIZE + TLS_SERVER_RANDOM_SIZE; unsigned char *message = (unsigned char *)TLS_MALLOC(message_len); if (message) { memcpy(message, context->local_random, TLS_CLIENT_RANDOM_SIZE); memcpy(message + TLS_CLIENT_RANDOM_SIZE, context->remote_random, TLS_SERVER_RANDOM_SIZE); memcpy(message + TLS_CLIENT_RANDOM_SIZE + TLS_SERVER_RANDOM_SIZE, packet_ref, packet_size); #ifdef TLS_CLIENT_ECDSA if (tls_is_ecdsa(context)) { if (_private_tls_verify_ecdsa(context, hash_algorithm, signature, sign_size, message, message_len, NULL) != 1) { DEBUG_PRINT("ECC Server signature FAILED!\n"); TLS_FREE(message); return TLS_BROKEN_PACKET; } } else #endif { if (_private_tls_verify_rsa(context, hash_algorithm, signature, sign_size, message, message_len) != 1) { DEBUG_PRINT("Server signature FAILED!\n"); TLS_FREE(message); return TLS_BROKEN_PACKET; } } TLS_FREE(message); } if (buf_len - res) { DEBUG_PRINT("EXTRA %i BYTES AT THE END OF MESSAGE\n", buf_len - res); DEBUG_DUMP_HEX(&buf[res], buf_len - res); DEBUG_PRINT("\n"); } #ifdef TLS_FORWARD_SECRECY if (ephemeral == 1) { _private_tls_dhe_create(context); DEBUG_DUMP_HEX_LABEL("DHP", dh_p, dh_p_len); DEBUG_DUMP_HEX_LABEL("DHG", dh_g, dh_g_len); int dhe_key_size = dh_p_len; if (dh_g_len > dh_p_len) dhe_key_size = dh_g_len; if (_private_tls_dh_make_key(dhe_key_size, context->dhe, (const char *)dh_p, (const char *)dh_g, dh_p_len, dh_g_len)) { DEBUG_PRINT("ERROR CREATING DHE KEY\n"); TLS_FREE(context->dhe); context->dhe = NULL; return TLS_GENERIC_ERROR; } unsigned int dh_key_size = 0; unsigned char *key = _private_tls_decrypt_dhe(context, dh_Ys, dh_Ys_len, &dh_key_size, 0); DEBUG_DUMP_HEX_LABEL("DH COMMON SECRET", key, dh_key_size); if ((key) && (dh_key_size)) { TLS_FREE(context->premaster_key); context->premaster_key = key; context->premaster_key_len = dh_key_size; } } else if ((ephemeral == 2) && (curve) && (pk_key) && (key_size)) { #ifdef TLS_CURVE25519 if (curve == &x25519) { if (key_size != 32) { DEBUG_PRINT("INVALID X25519 PUBLIC SIZE"); return TLS_GENERIC_ERROR; } TLS_FREE(context->client_secret); context->client_secret = (unsigned char *)TLS_MALLOC(32); if (!context->client_secret) { DEBUG_PRINT("ERROR IN TLS_MALLOC"); return TLS_GENERIC_ERROR; } tls_random(context->client_secret, 32); context->client_secret[0] &= 248; context->client_secret[31] &= 127; context->client_secret[31] |= 64; TLS_FREE(context->premaster_key); context->premaster_key = (unsigned char *)TLS_MALLOC(32); if (!context->premaster_key) return TLS_GENERIC_ERROR; curve25519(context->premaster_key, context->client_secret, pk_key); context->premaster_key_len = 32; } else #endif { tls_init(); _private_tls_ecc_dhe_create(context); ltc_ecc_set_type *dp = (ltc_ecc_set_type *)&curve->dp; if (ecc_make_key_ex(NULL, find_prng("sprng"), context->ecc_dhe, dp)) { TLS_FREE(context->ecc_dhe); context->ecc_dhe = NULL; DEBUG_PRINT("Error generating ECC key\n"); return TLS_GENERIC_ERROR; } TLS_FREE(context->premaster_key); context->premaster_key_len = 0; unsigned int out_len = 0; context->premaster_key = _private_tls_decrypt_ecc_dhe(context, pk_key, key_size, &out_len, 0); if (context->premaster_key) context->premaster_key_len = out_len; } } #endif return res; } int tls_parse_client_key_exchange(struct TLSContext *context, const unsigned char *buf, int buf_len) { if (context->connection_status != 1) { DEBUG_PRINT("UNEXPECTED CLIENT KEY EXCHANGE MESSAGE (connections status: %i)\n", (int)context->connection_status); return TLS_UNEXPECTED_MESSAGE; } int res = 0; int dh_res = 0; CHECK_SIZE(3, buf_len, TLS_NEED_MORE_DATA) unsigned int size = buf[0] * 0x10000 + buf[1] * 0x100 + buf[2]; res += 3; if (context->dtls) { int dtls_check = _private_dtls_check_packet(buf, buf_len); if (dtls_check < 0) return dtls_check; res += 8; } CHECK_SIZE(size, buf_len - res, TLS_NEED_MORE_DATA); if (!size) return res; dh_res = _private_tls_parse_random(context, &buf[res], size); if (dh_res <= 0) { DEBUG_PRINT("broken key\n"); return TLS_BROKEN_PACKET; } DEBUG_PRINT("\n"); res += size; context->connection_status = 2; return res; } int tls_parse_server_hello_done(struct TLSContext *context, const unsigned char *buf, int buf_len) { int res = 0; CHECK_SIZE(3, buf_len, TLS_NEED_MORE_DATA) unsigned int size = buf[0] * 0x10000 + buf[1] * 0x100 + buf[2]; res += 3; if (context->dtls) { int dtls_check = _private_dtls_check_packet(buf, buf_len); if (dtls_check < 0) return dtls_check; res += 8; } CHECK_SIZE(size, buf_len - res, TLS_NEED_MORE_DATA); res += size; return res; } int tls_parse_finished(struct TLSContext *context, const unsigned char *buf, int buf_len, unsigned int *write_packets) { if ((context->connection_status < 2) || (context->connection_status == 0xFF)) { DEBUG_PRINT("UNEXPECTED FINISHED MESSAGE\n"); return TLS_UNEXPECTED_MESSAGE; } int res = 0; *write_packets = 0; CHECK_SIZE(3, buf_len, TLS_NEED_MORE_DATA) unsigned int size = buf[0] * 0x10000 + buf[1] * 0x100 + buf[2]; res += 3; if (context->dtls) { int dtls_check = _private_dtls_check_packet(buf, buf_len); if (dtls_check < 0) return dtls_check; res += 8; } if (size < TLS_MIN_FINISHED_OPAQUE_LEN) { DEBUG_PRINT("Invalid finished pachet size: %i\n", size); return TLS_BROKEN_PACKET; } CHECK_SIZE(size, buf_len - res, TLS_NEED_MORE_DATA); unsigned char hash[TLS_MAX_SHA_SIZE]; unsigned int hash_len = _private_tls_get_hash(context, hash); #ifdef WITH_TLS_13 if ((context->version == TLS_V13) || (context->version == DTLS_V13)) { unsigned char hash_out[TLS_MAX_SHA_SIZE]; unsigned long out_size = TLS_MAX_SHA_SIZE; if ((!context->remote_finished_key) || (!hash_len)) { DEBUG_PRINT("NO FINISHED KEY COMPUTED OR NO HANDSHAKE HASH\n"); return TLS_NOT_VERIFIED; } DEBUG_DUMP_HEX_LABEL("HS HASH", hash, hash_len); DEBUG_DUMP_HEX_LABEL("HS FINISH", context->finished_key, hash_len); DEBUG_DUMP_HEX_LABEL("HS REMOTE FINISH", context->remote_finished_key, hash_len); out_size = hash_len; hmac_state hmac; hmac_init(&hmac, _private_tls_get_hash_idx(context), context->remote_finished_key, hash_len); hmac_process(&hmac, hash, hash_len); hmac_done(&hmac, hash_out, &out_size); if ((size != out_size) || (memcmp(hash_out, &buf[res], size))) { DEBUG_PRINT("Finished validation error (sequence number, local: %i, remote: %i)\n", (int)context->local_sequence_number, (int)context->remote_sequence_number); DEBUG_DUMP_HEX_LABEL("FINISHED OPAQUE", &buf[res], size); DEBUG_DUMP_HEX_LABEL("VERIFY", hash_out, out_size); return TLS_NOT_VERIFIED; } if (context->is_server) { context->connection_status = 0xFF; res += size; _private_tls13_key(context, 0); context->local_sequence_number = 0; context->remote_sequence_number = 0; return res; } } else #endif { // verify unsigned char *out = (unsigned char *)TLS_MALLOC(size); if (!out) { DEBUG_PRINT("Error in TLS_MALLOC (%i bytes)\n", (int)size); return TLS_NO_MEMORY; } // server verifies client's message if (context->is_server) _private_tls_prf(context, out, size, context->master_key, context->master_key_len, (unsigned char *)"client finished", 15, hash, hash_len, NULL, 0); else _private_tls_prf(context, out, size, context->master_key, context->master_key_len, (unsigned char *)"server finished", 15, hash, hash_len, NULL, 0); if (memcmp(out, &buf[res], size)) { TLS_FREE(out); DEBUG_PRINT("Finished validation error (sequence number, local: %i, remote: %i)\n", (int)context->local_sequence_number, (int)context->remote_sequence_number); DEBUG_DUMP_HEX_LABEL("FINISHED OPAQUE", &buf[res], size); DEBUG_DUMP_HEX_LABEL("VERIFY", out, size); return TLS_NOT_VERIFIED; } #ifdef TLS_ACCEPT_SECURE_RENEGOTIATION if (size) { if (context->is_server) { TLS_FREE(context->verify_data); context->verify_data = (unsigned char *)TLS_MALLOC(size); if (context->verify_data) { memcpy(context->verify_data, out, size); context->verify_len = size; } } else { // concatenate client verify and server verify context->verify_data = (unsigned char *)TLS_REALLOC(context->verify_data, size); if (context->verify_data) { memcpy(context->verify_data + context->verify_len, out, size); context->verify_len += size; } else context->verify_len = 0; } } #endif TLS_FREE(out); } if (context->is_server) *write_packets = 3; else context->connection_status = 0xFF; res += size; return res; } #ifdef WITH_TLS_13 int tls_parse_verify_tls13(struct TLSContext *context, const unsigned char *buf, int buf_len) { CHECK_SIZE(7, buf_len, TLS_NEED_MORE_DATA) unsigned int size = buf[0] * 0x10000 + buf[1] * 0x100 + buf[2]; if (size < 2) return buf_len; unsigned char signing_data[TLS_MAX_HASH_SIZE + 98]; int signing_data_len; // first 64 bytes to 0x20 (32) memset(signing_data, 0x20, 64); // context string 33 bytes if (context->is_server) memcpy(signing_data + 64, "TLS 1.3, server CertificateVerify", 33); else memcpy(signing_data + 64, "TLS 1.3, client CertificateVerify", 33); // a single 0 byte separator signing_data[97] = 0; signing_data_len = 98; signing_data_len += _private_tls_get_hash(context, signing_data + 98); DEBUG_DUMP_HEX_LABEL("signature data", signing_data, signing_data_len); unsigned short signature = ntohs(*(unsigned short *)&buf[3]); unsigned short signature_size = ntohs(*(unsigned short *)&buf[5]); int valid = 0; CHECK_SIZE(7 + signature_size, buf_len, TLS_NEED_MORE_DATA) switch (signature) { #ifdef TLS_ECDSA_SUPPORTED case 0x0403: // secp256r1 + sha256 valid = _private_tls_verify_ecdsa(context, sha256, buf + 7, signature_size, signing_data, signing_data_len, &secp256r1); break; case 0x0503: // secp384r1 + sha384 valid = _private_tls_verify_ecdsa(context, sha384, buf + 7, signature_size, signing_data, signing_data_len, &secp384r1); break; case 0x0603: // secp521r1 + sha512 valid = _private_tls_verify_ecdsa(context, sha512, buf + 7, signature_size, signing_data, signing_data_len, &secp521r1); break; #endif case 0x0804: valid = _private_tls_verify_rsa(context, sha256, buf + 7, signature_size, signing_data, signing_data_len); break; default: DEBUG_PRINT("Unsupported signature: %x\n", (int)signature); return TLS_UNSUPPORTED_CERTIFICATE; } if (valid != 1) { DEBUG_PRINT("Signature FAILED!\n"); return TLS_DECRYPTION_FAILED; } return buf_len; } #endif int tls_parse_verify(struct TLSContext *context, const unsigned char *buf, int buf_len) { #ifdef WITH_TLS_13 if ((context->version == TLS_V13) || (context->version == DTLS_V13)) return tls_parse_verify_tls13(context, buf, buf_len); #endif CHECK_SIZE(7, buf_len, TLS_BAD_CERTIFICATE) unsigned int bytes_to_follow = buf[0] * 0x10000 + buf[1] * 0x100 + buf[2]; CHECK_SIZE(bytes_to_follow, buf_len - 3, TLS_BAD_CERTIFICATE) int res = -1; if ((context->version == TLS_V12) || (context->version == DTLS_V12) || (context->version == TLS_V13) || (context->version == DTLS_V13)) { unsigned int hash = buf[3]; unsigned int algorithm = buf[4]; if (algorithm != rsa) return TLS_UNSUPPORTED_CERTIFICATE; unsigned short size = ntohs(*(unsigned short *)&buf[5]); CHECK_SIZE(size, bytes_to_follow - 4, TLS_BAD_CERTIFICATE) DEBUG_PRINT("ALGORITHM %i/%i (%i)\n", hash, algorithm, (int)size); DEBUG_DUMP_HEX_LABEL("VERIFY", &buf[7], bytes_to_follow - 7); res = _private_tls_verify_rsa(context, hash, &buf[7], size, context->cached_handshake, context->cached_handshake_len); } else { #ifdef TLS_LEGACY_SUPPORT unsigned short size = ntohs(*(unsigned short *)&buf[3]); CHECK_SIZE(size, bytes_to_follow - 2, TLS_BAD_CERTIFICATE) res = _private_tls_verify_rsa(context, md5, &buf[5], size, context->cached_handshake, context->cached_handshake_len); #endif } if (context->cached_handshake) { // not needed anymore TLS_FREE(context->cached_handshake); context->cached_handshake = NULL; context->cached_handshake_len = 0; } if (res == 1) { DEBUG_PRINT("Signature OK\n"); context->client_verified = 1; } else { DEBUG_PRINT("Signature FAILED\n"); context->client_verified = 0; } return 1; } int tls_parse_payload(struct TLSContext *context, const unsigned char *buf, int buf_len, tls_validation_function certificate_verify) { int orig_len = buf_len; if (context->connection_status == 0xFF) { #ifndef TLS_ACCEPT_SECURE_RENEGOTIATION // renegotiation disabled (emit warning alert) _private_tls_write_packet(tls_build_alert(context, 0, no_renegotiation)); return 1; #endif } while ((buf_len >= 4) && (!context->critical_error)) { int payload_res = 0; unsigned char update_hash = 1; CHECK_SIZE(1, buf_len, TLS_NEED_MORE_DATA) unsigned char type = buf[0]; unsigned int write_packets = 0; unsigned int dtls_cookie_verified = 0; int certificate_verify_alert = no_error; unsigned int payload_size = buf[1] * 0x10000 + buf[2] * 0x100 + buf[3] + 3; if (context->dtls) payload_size += 8; CHECK_SIZE(payload_size + 1, buf_len, TLS_NEED_MORE_DATA) switch (type) { // hello request case 0x00: CHECK_HANDSHAKE_STATE(context, 0, 1); DEBUG_PRINT(" => HELLO REQUEST (RENEGOTIATION?)\n"); if (context->dtls) context->dtls_seq = 0; if (context->is_server) payload_res = TLS_UNEXPECTED_MESSAGE; else { if (context->connection_status == 0xFF) { // renegotiation #ifdef TLS_ACCEPT_SECURE_RENEGOTIATION if (context->critical_error) payload_res = TLS_UNEXPECTED_MESSAGE; else { _private_tls_reset_context(context); _private_tls_write_packet(tls_build_hello(context, 0)); return 1; } #else payload_res = TLS_NO_RENEGOTIATION; #endif } else payload_res = TLS_UNEXPECTED_MESSAGE; } // no payload break; // client hello case 0x01: CHECK_HANDSHAKE_STATE(context, 1, (context->dtls ? 2 : 1)); DEBUG_PRINT(" => CLIENT HELLO\n"); if (context->is_server) { payload_res = tls_parse_hello(context, buf + 1, payload_size, &write_packets, &dtls_cookie_verified); DEBUG_PRINT(" => DTLS COOKIE VERIFIED: %i (%i)\n", dtls_cookie_verified, payload_res); if ((context->dtls) && (payload_res > 0) && (!dtls_cookie_verified) && (context->connection_status == 1)) { // wait client hello context->connection_status = 3; update_hash = 0; } } else payload_res = TLS_UNEXPECTED_MESSAGE; break; // server hello case 0x02: CHECK_HANDSHAKE_STATE(context, 2, 1); DEBUG_PRINT(" => SERVER HELLO\n"); if (context->is_server) payload_res = TLS_UNEXPECTED_MESSAGE; else payload_res = tls_parse_hello(context, buf + 1, payload_size, &write_packets, &dtls_cookie_verified); break; // hello verify request case 0x03: DEBUG_PRINT(" => VERIFY REQUEST\n"); CHECK_HANDSHAKE_STATE(context, 3, 1); if ((context->dtls) && (!context->is_server)) { payload_res = tls_parse_verify_request(context, buf + 1, payload_size, &write_packets); update_hash = 0; } else payload_res = TLS_UNEXPECTED_MESSAGE; break; // certificate case 0x0B: CHECK_HANDSHAKE_STATE(context, 4, 1); DEBUG_PRINT(" => CERTIFICATE\n"); #ifdef WITH_TLS_13 if ((context->version == TLS_V13) || (context->version == DTLS_V13)) { if (context->connection_status == 2) { payload_res = tls_parse_certificate(context, buf + 1, payload_size, context->is_server); if (context->is_server) { if ((certificate_verify) && (context->client_certificates_count)) certificate_verify_alert = certificate_verify(context, context->client_certificates, context->client_certificates_count); // empty certificates are permitted for client if (payload_res <= 0) payload_res = 1; } else { if ((certificate_verify) && (context->certificates_count)) certificate_verify_alert = certificate_verify(context, context->certificates, context->certificates_count); } } else payload_res = TLS_UNEXPECTED_MESSAGE; } else #endif if (context->connection_status == 1) { if (context->is_server) { // client certificate payload_res = tls_parse_certificate(context, buf + 1, payload_size, 1); if ((certificate_verify) && (context->client_certificates_count)) certificate_verify_alert = certificate_verify(context, context->client_certificates, context->client_certificates_count); // empty certificates are permitted for client if (payload_res <= 0) payload_res = 1; } else { payload_res = tls_parse_certificate(context, buf + 1, payload_size, 0); if ((certificate_verify) && (context->certificates_count)) certificate_verify_alert = certificate_verify(context, context->certificates, context->certificates_count); } } else payload_res = TLS_UNEXPECTED_MESSAGE; break; // server key exchange case 0x0C: CHECK_HANDSHAKE_STATE(context, 5, 1); DEBUG_PRINT(" => SERVER KEY EXCHANGE\n"); if (context->is_server) payload_res = TLS_UNEXPECTED_MESSAGE; else payload_res = tls_parse_server_key_exchange(context, buf + 1, payload_size); break; // certificate request case 0x0D: CHECK_HANDSHAKE_STATE(context, 6, 1); // server to client if (context->is_server) payload_res = TLS_UNEXPECTED_MESSAGE; else context->client_verified = 2; DEBUG_PRINT(" => CERTIFICATE REQUEST\n"); break; // server hello done case 0x0E: CHECK_HANDSHAKE_STATE(context, 7, 1); DEBUG_PRINT(" => SERVER HELLO DONE\n"); if (context->is_server) { payload_res = TLS_UNEXPECTED_MESSAGE; } else { payload_res = tls_parse_server_hello_done(context, buf + 1, payload_size); if (payload_res > 0) write_packets = 1; } break; // certificate verify case 0x0F: CHECK_HANDSHAKE_STATE(context, 8, 1); DEBUG_PRINT(" => CERTIFICATE VERIFY\n"); if (context->connection_status == 2) payload_res = tls_parse_verify(context, buf + 1, payload_size); else payload_res = TLS_UNEXPECTED_MESSAGE; break; // client key exchange case 0x10: CHECK_HANDSHAKE_STATE(context, 9, 1); DEBUG_PRINT(" => CLIENT KEY EXCHANGE\n"); if (context->is_server) payload_res = tls_parse_client_key_exchange(context, buf + 1, payload_size); else payload_res = TLS_UNEXPECTED_MESSAGE; break; // finished case 0x14: if (context->cached_handshake) { TLS_FREE(context->cached_handshake); context->cached_handshake = NULL; context->cached_handshake_len = 0; } CHECK_HANDSHAKE_STATE(context, 10, 1); DEBUG_PRINT(" => FINISHED\n"); payload_res = tls_parse_finished(context, buf + 1, payload_size, &write_packets); if (payload_res > 0) memset(context->hs_messages, 0, sizeof(context->hs_messages)); #ifdef WITH_TLS_13 if ((!context->is_server) && ((context->version == TLS_V13) || (context->version == DTLS_V13))) { update_hash = 0; DEBUG_PRINT("<= SENDING FINISHED\n"); _private_tls_update_hash(context, buf, payload_size + 1); _private_tls_write_packet(tls_build_finished(context)); _private_tls13_key(context, 0); context->connection_status = 0xFF; context->local_sequence_number = 0; context->remote_sequence_number = 0; } #endif break; #ifdef WITH_TLS_13 case 0x08: // encrypted extensions ... ignore it for now break; #endif default: DEBUG_PRINT(" => NOT UNDERSTOOD PAYLOAD TYPE: %x\n", (int)type); return TLS_NOT_UNDERSTOOD; } if ((type != 0x00) && (update_hash)) _private_tls_update_hash(context, buf, payload_size + 1); if (certificate_verify_alert != no_error) { _private_tls_write_packet(tls_build_alert(context, 1, certificate_verify_alert)); context->critical_error = 1; } if (payload_res < 0) { switch (payload_res) { case TLS_UNEXPECTED_MESSAGE: _private_tls_write_packet(tls_build_alert(context, 1, unexpected_message)); break; case TLS_COMPRESSION_NOT_SUPPORTED: _private_tls_write_packet(tls_build_alert(context, 1, decompression_failure)); break; case TLS_BROKEN_PACKET: _private_tls_write_packet(tls_build_alert(context, 1, decode_error)); break; case TLS_NO_MEMORY: _private_tls_write_packet(tls_build_alert(context, 1, internal_error)); break; case TLS_NOT_VERIFIED: _private_tls_write_packet(tls_build_alert(context, 1, bad_record_mac)); break; case TLS_BAD_CERTIFICATE: if (context->is_server) { // bad client certificate, continue _private_tls_write_packet(tls_build_alert(context, 0, bad_certificate)); payload_res = 0; } else _private_tls_write_packet(tls_build_alert(context, 1, bad_certificate)); break; case TLS_UNSUPPORTED_CERTIFICATE: _private_tls_write_packet(tls_build_alert(context, 1, unsupported_certificate)); break; case TLS_NO_COMMON_CIPHER: _private_tls_write_packet(tls_build_alert(context, 1, insufficient_security)); break; case TLS_NOT_UNDERSTOOD: _private_tls_write_packet(tls_build_alert(context, 1, internal_error)); break; case TLS_NO_RENEGOTIATION: _private_tls_write_packet(tls_build_alert(context, 0, no_renegotiation)); payload_res = 0; break; case TLS_DECRYPTION_FAILED: _private_tls_write_packet(tls_build_alert(context, 1, decryption_failed_RESERVED)); break; } if (payload_res < 0) return payload_res; } if (certificate_verify_alert != no_error) payload_res = TLS_BAD_CERTIFICATE; // except renegotiation switch (write_packets) { case 1: if (context->client_verified == 2) { DEBUG_PRINT("<= Building CERTIFICATE \n"); _private_tls_write_packet(tls_build_certificate(context)); context->client_verified = 0; } // client handshake DEBUG_PRINT("<= Building KEY EXCHANGE\n"); _private_tls_write_packet(tls_build_client_key_exchange(context)); DEBUG_PRINT("<= Building CHANGE CIPHER SPEC\n"); _private_tls_write_packet(tls_build_change_cipher_spec(context)); context->cipher_spec_set = 1; context->local_sequence_number = 0; DEBUG_PRINT("<= Building CLIENT FINISHED\n"); _private_tls_write_packet(tls_build_finished(context)); context->cipher_spec_set = 0; #ifdef TLS_12_FALSE_START if ((!context->is_server) && (context->version == TLS_V12)) { // https://tools.ietf.org/html/rfc7918 // 5.1. Symmetric Cipher // Clients MUST NOT use the False Start protocol modification in a // handshake unless the cipher suite uses a symmetric cipher that is // considered cryptographically strong. switch (context->cipher) { case TLS_RSA_WITH_AES_128_GCM_SHA256: case TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256: case TLS_DHE_RSA_WITH_AES_128_GCM_SHA256: case TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256: case TLS_RSA_WITH_AES_256_GCM_SHA384: case TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384: case TLS_DHE_RSA_WITH_AES_256_GCM_SHA384: case TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384: context->false_start = 1; break; } } #endif break; case 2: // server handshake if ((context->dtls) && (dtls_cookie_verified == 0)) { _private_tls_write_packet(tls_build_verify_request(context)); _private_dtls_reset(context); } else { DEBUG_PRINT("<= SENDING SERVER HELLO\n"); #ifdef WITH_TLS_13 if (context->connection_status == 3) { context->connection_status = 2; _private_tls_write_packet(tls_build_hello(context, 0)); _private_tls_write_packet(tls_build_change_cipher_spec(context)); _private_tls13_key(context, 1); context->cipher_spec_set = 1; DEBUG_PRINT("<= SENDING ENCRYPTED EXTENSIONS\n"); _private_tls_write_packet(tls_build_encrypted_extensions(context)); if (context->request_client_certificate) { DEBUG_PRINT("<= SENDING CERTIFICATE REQUEST\n"); _private_tls_write_packet(tls_certificate_request(context)); } DEBUG_PRINT("<= SENDING CERTIFICATE\n"); _private_tls_write_packet(tls_build_certificate(context)); DEBUG_PRINT("<= SENDING CERTIFICATE VERIFY\n"); _private_tls_write_packet(tls_build_certificate_verify(context)); DEBUG_PRINT("<= SENDING FINISHED\n"); _private_tls_write_packet(tls_build_finished(context)); // new key TLS_FREE(context->server_finished_hash); context->server_finished_hash = (unsigned char *)TLS_MALLOC(_private_tls_mac_length(context)); if (context->server_finished_hash) _private_tls_get_hash(context, context->server_finished_hash); break; } #endif _private_tls_write_packet(tls_build_hello(context, 0)); DEBUG_PRINT("<= SENDING CERTIFICATE\n"); _private_tls_write_packet(tls_build_certificate(context)); int ephemeral_cipher = tls_cipher_is_ephemeral(context); if (ephemeral_cipher) { DEBUG_PRINT("<= SENDING EPHEMERAL DH KEY\n"); _private_tls_write_packet(tls_build_server_key_exchange(context, ephemeral_cipher == 1 ? KEA_dhe_rsa : KEA_ec_diffie_hellman)); } if (context->request_client_certificate) { DEBUG_PRINT("<= SENDING CERTIFICATE REQUEST\n"); _private_tls_write_packet(tls_certificate_request(context)); } DEBUG_PRINT("<= SENDING DONE\n"); _private_tls_write_packet(tls_build_done(context)); } break; case 3: // finished _private_tls_write_packet(tls_build_change_cipher_spec(context)); _private_tls_write_packet(tls_build_finished(context)); context->connection_status = 0xFF; break; case 4: // dtls only context->dtls_seq = 1; _private_tls_write_packet(tls_build_hello(context, 0)); break; #ifdef WITH_TLS_13 case 5: // hello retry request DEBUG_PRINT("<= SENDING HELLO RETRY REQUEST\n"); _private_tls_write_packet(tls_build_hello(context, 0)); break; #endif } payload_size++; buf += payload_size; buf_len -= payload_size; } return orig_len; } unsigned int _private_tls_hmac_message(unsigned char local, struct TLSContext *context, const unsigned char *buf, int buf_len, const unsigned char *buf2, int buf_len2, unsigned char *out, unsigned int outlen, uint64_t remote_sequence_number) { hmac_state hash; int mac_size = outlen; int hash_idx; if (mac_size == TLS_SHA1_MAC_SIZE) hash_idx = find_hash("sha1"); else if (mac_size == TLS_SHA384_MAC_SIZE) hash_idx = find_hash("sha384"); else hash_idx = find_hash("sha256"); if (hmac_init(&hash, hash_idx, local ? context->crypto.ctx_local_mac.local_mac : context->crypto.ctx_remote_mac.remote_mac, mac_size)) return 0; uint64_t squence_number; if (context->dtls) squence_number = htonll(remote_sequence_number); else if (local) squence_number = htonll(context->local_sequence_number); else squence_number = htonll(context->remote_sequence_number); if (hmac_process(&hash, (unsigned char *)&squence_number, sizeof(uint64_t))) return 0; if (hmac_process(&hash, buf, buf_len)) return 0; if ((buf2) && (buf_len2)) { if (hmac_process(&hash, buf2, buf_len2)) return 0; } unsigned long ref_outlen = outlen; if (hmac_done(&hash, out, &ref_outlen)) return 0; return (unsigned int)ref_outlen; } int tls_parse_message(struct TLSContext *context, unsigned char *buf, int buf_len, tls_validation_function certificate_verify) { int res = 5; if (context->dtls) res = 13; int header_size = res; int payload_res = 0; CHECK_SIZE(res, buf_len, TLS_NEED_MORE_DATA) unsigned char type = *buf; int buf_pos = 1; unsigned short version = ntohs(*(unsigned short *)&buf[buf_pos]); buf_pos += 2; uint64_t dtls_sequence_number = 0; if (context->dtls) { CHECK_SIZE(buf_pos + 8, buf_len, TLS_NEED_MORE_DATA) dtls_sequence_number = ntohll(*(uint64_t *)&buf[buf_pos]); buf_pos += 8; } VERSION_SUPPORTED(version, TLS_NOT_SAFE) unsigned short length; length = ntohs(*(unsigned short *)&buf[buf_pos]); buf_pos += 2; unsigned char *pt = NULL; const unsigned char *ptr = buf + buf_pos; CHECK_SIZE(buf_pos + length, buf_len, TLS_NEED_MORE_DATA) DEBUG_PRINT("Message type: %0x, length: %i\n", (int)type, (int)length); if ((context->cipher_spec_set) && (type != TLS_CHANGE_CIPHER)) { DEBUG_DUMP_HEX_LABEL("encrypted", &buf[header_size], length); if (!context->crypto.created) { DEBUG_PRINT("Encryption context not created\n"); _private_random_sleep(context, TLS_MAX_ERROR_SLEEP_uS); return TLS_BROKEN_PACKET; } pt = (unsigned char *)TLS_MALLOC(length); if (!pt) { DEBUG_PRINT("Error in TLS_MALLOC (%i bytes)\n", (int)length); _private_random_sleep(context, TLS_MAX_ERROR_SLEEP_uS); return TLS_NO_MEMORY; } unsigned char aad[16]; int aad_size = sizeof(aad); unsigned char *sequence = aad; if (context->crypto.created == 2) { int delta = 8; int pt_length; unsigned char iv[TLS_13_AES_GCM_IV_LENGTH]; gcm_reset(&context->crypto.ctx_remote.aes_gcm_remote); #ifdef WITH_TLS_13 if ((context->version == TLS_V13) || (context->version == DTLS_V13)) { aad[0] = TLS_APPLICATION_DATA; aad[1] = 0x03; aad[2] = 0x03; *((unsigned short *)(aad + 3)) = htons(buf_len - header_size); aad_size = 5; sequence = aad + 5; if (context->dtls) *((uint64_t *)sequence) = *(uint64_t *)(buf + 3); else *((uint64_t *)sequence) = htonll(context->remote_sequence_number); memcpy(iv, context->crypto.ctx_remote_mac.remote_iv, TLS_13_AES_GCM_IV_LENGTH); int i; int offset = TLS_13_AES_GCM_IV_LENGTH - 8; for (i = 0; i < 8; i++) iv[offset + i] = context->crypto.ctx_remote_mac.remote_iv[offset + i] ^ sequence[i]; pt_length = buf_len - header_size - TLS_GCM_TAG_LEN; delta = 0; } else { #endif aad_size = 13; pt_length = length - 8 - TLS_GCM_TAG_LEN; // build aad and iv if (context->dtls) *((uint64_t *)aad) = htonll(dtls_sequence_number); else *((uint64_t *)aad) = htonll(context->remote_sequence_number); aad[8] = buf[0]; aad[9] = buf[1]; aad[10] = buf[2]; memcpy(iv, context->crypto.ctx_remote_mac.remote_aead_iv, 4); memcpy(iv + 4, buf + header_size, 8); *((unsigned short *)(aad + 11)) = htons((unsigned short)pt_length); #ifdef WITH_TLS_13 } #endif if (pt_length < 0) { DEBUG_PRINT("Invalid packet length"); TLS_FREE(pt); _private_random_sleep(context, TLS_MAX_ERROR_SLEEP_uS); return TLS_BROKEN_PACKET; } DEBUG_DUMP_HEX_LABEL("aad", aad, aad_size); DEBUG_DUMP_HEX_LABEL("aad iv", iv, 12); int res0 = gcm_add_iv(&context->crypto.ctx_remote.aes_gcm_remote, iv, 12); int res1 = gcm_add_aad(&context->crypto.ctx_remote.aes_gcm_remote, aad, aad_size); memset(pt, 0, length); DEBUG_PRINT("PT SIZE: %i\n", pt_length); int res2 = gcm_process(&context->crypto.ctx_remote.aes_gcm_remote, pt, pt_length, buf + header_size + delta, GCM_DECRYPT); unsigned char tag[32]; unsigned long taglen = 32; int res3 = gcm_done(&context->crypto.ctx_remote.aes_gcm_remote, tag, &taglen); if ((res0) || (res1) || (res2) || (res3) || (taglen != TLS_GCM_TAG_LEN)) { DEBUG_PRINT("ERROR: gcm_add_iv: %i, gcm_add_aad: %i, gcm_process: %i, gcm_done: %i\n", res0, res1, res2, res3); _private_random_sleep(context, TLS_MAX_ERROR_SLEEP_uS); return TLS_BROKEN_PACKET; } DEBUG_DUMP_HEX_LABEL("decrypted", pt, pt_length); DEBUG_DUMP_HEX_LABEL("tag", tag, taglen); // check tag if (memcmp(buf + header_size + delta + pt_length, tag, taglen)) { DEBUG_PRINT("INTEGRITY CHECK FAILED (msg length %i)\n", pt_length); DEBUG_DUMP_HEX_LABEL("TAG RECEIVED", buf + header_size + delta + pt_length, taglen); DEBUG_DUMP_HEX_LABEL("TAG COMPUTED", tag, taglen); TLS_FREE(pt); _private_random_sleep(context, TLS_MAX_ERROR_SLEEP_uS); _private_tls_write_packet(tls_build_alert(context, 1, bad_record_mac)); return TLS_INTEGRITY_FAILED; } ptr = pt; length = (unsigned short)pt_length; #ifdef TLS_WITH_CHACHA20_POLY1305 } else if (context->crypto.created == 3) { int pt_length = length - POLY1305_TAGLEN; unsigned int counter = 1; unsigned char poly1305_key[POLY1305_KEYLEN]; unsigned char trail[16]; unsigned char mac_tag[POLY1305_TAGLEN]; aad_size = 16; if (pt_length < 0) { DEBUG_PRINT("Invalid packet length"); TLS_FREE(pt); _private_random_sleep(context, TLS_MAX_ERROR_SLEEP_uS); return TLS_BROKEN_PACKET; } #ifdef WITH_TLS_13 if ((context->version == TLS_V13) || (context->version == DTLS_V13)) { aad[0] = TLS_APPLICATION_DATA; aad[1] = 0x03; aad[2] = 0x03; *((unsigned short *)(aad + 3)) = htons(buf_len - header_size); aad_size = 5; sequence = aad + 5; if (context->dtls) *((uint64_t *)sequence) = *(uint64_t *)(buf + 3); else *((uint64_t *)sequence) = htonll(context->remote_sequence_number); } else { #endif if (context->dtls) *((uint64_t *)aad) = htonll(dtls_sequence_number); else *((uint64_t *)aad) = htonll(context->remote_sequence_number); aad[8] = buf[0]; aad[9] = buf[1]; aad[10] = buf[2]; *((unsigned short *)(aad + 11)) = htons((unsigned short)pt_length); aad[13] = 0; aad[14] = 0; aad[15] = 0; #ifdef WITH_TLS_13 } #endif chacha_ivupdate(&context->crypto.ctx_remote.chacha_remote, context->crypto.ctx_remote_mac.remote_aead_iv, sequence, (unsigned char *)&counter); chacha_encrypt_bytes(&context->crypto.ctx_remote.chacha_remote, buf + header_size, pt, pt_length); DEBUG_DUMP_HEX_LABEL("decrypted", pt, pt_length); ptr = pt; length = (unsigned short)pt_length; chacha20_poly1305_key(&context->crypto.ctx_remote.chacha_remote, poly1305_key); poly1305_context ctx; _private_tls_poly1305_init(&ctx, poly1305_key); _private_tls_poly1305_update(&ctx, aad, aad_size); static unsigned char zeropad[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; int rem = aad_size % 16; if (rem) _private_tls_poly1305_update(&ctx, zeropad, 16 - rem); _private_tls_poly1305_update(&ctx, buf + header_size, pt_length); rem = pt_length % 16; if (rem) _private_tls_poly1305_update(&ctx, zeropad, 16 - rem); _private_tls_U32TO8(&trail[0], aad_size == 5 ? 5 : 13); *(int *)&trail[4] = 0; _private_tls_U32TO8(&trail[8], pt_length); *(int *)&trail[12] = 0; _private_tls_poly1305_update(&ctx, trail, 16); _private_tls_poly1305_finish(&ctx, mac_tag); if (memcmp(mac_tag, buf + header_size + pt_length, POLY1305_TAGLEN)) { DEBUG_PRINT("INTEGRITY CHECK FAILED (msg length %i)\n", length); DEBUG_DUMP_HEX_LABEL("POLY1305 TAG RECEIVED", buf + header_size + pt_length, POLY1305_TAGLEN); DEBUG_DUMP_HEX_LABEL("POLY1305 TAG COMPUTED", mac_tag, POLY1305_TAGLEN); TLS_FREE(pt); // silently ignore packet for DTLS if (context->dtls) return header_size + length; _private_random_sleep(context, TLS_MAX_ERROR_SLEEP_uS); _private_tls_write_packet(tls_build_alert(context, 1, bad_record_mac)); return TLS_INTEGRITY_FAILED; } #endif } else { int err = _private_tls_crypto_decrypt(context, buf + header_size, pt, length); if (err) { TLS_FREE(pt); DEBUG_PRINT("Decryption error %i\n", (int)err); _private_random_sleep(context, TLS_MAX_ERROR_SLEEP_uS); return TLS_BROKEN_PACKET; } unsigned char padding_byte = pt[length - 1]; unsigned char padding = padding_byte + 1; // poodle check int padding_index = length - padding; if (padding_index > 0) { int i; int limit = length - 1; for (i = length - padding; i < limit; i++) { if (pt[i] != padding_byte) { TLS_FREE(pt); DEBUG_PRINT("BROKEN PACKET (POODLE ?)\n"); _private_random_sleep(context, TLS_MAX_ERROR_SLEEP_uS); _private_tls_write_packet(tls_build_alert(context, 1, decrypt_error)); return TLS_BROKEN_PACKET; } } } unsigned int decrypted_length = length; if (padding < decrypted_length) decrypted_length -= padding; DEBUG_DUMP_HEX_LABEL("decrypted", pt, decrypted_length); ptr = pt; #ifdef TLS_LEGACY_SUPPORT if ((context->version != TLS_V10) && (decrypted_length > TLS_AES_IV_LENGTH)) { decrypted_length -= TLS_AES_IV_LENGTH; ptr += TLS_AES_IV_LENGTH; } #else if (decrypted_length > TLS_AES_IV_LENGTH) { decrypted_length -= TLS_AES_IV_LENGTH; ptr += TLS_AES_IV_LENGTH; } #endif length = decrypted_length; unsigned int mac_size = _private_tls_mac_length(context); if ((length < mac_size) || (!mac_size)) { TLS_FREE(pt); DEBUG_PRINT("BROKEN PACKET\n"); _private_random_sleep(context, TLS_MAX_ERROR_SLEEP_uS); _private_tls_write_packet(tls_build_alert(context, 1, decrypt_error)); return TLS_BROKEN_PACKET; } length -= mac_size; const unsigned char *message_hmac = &ptr[length]; unsigned char hmac_out[TLS_MAX_MAC_SIZE]; unsigned char temp_buf[5]; memcpy(temp_buf, buf, 3); *(unsigned short *)(temp_buf + 3) = htons(length); unsigned int hmac_out_len = _private_tls_hmac_message(0, context, temp_buf, 5, ptr, length, hmac_out, mac_size, dtls_sequence_number); if ((hmac_out_len != mac_size) || (memcmp(message_hmac, hmac_out, mac_size))) { DEBUG_PRINT("INTEGRITY CHECK FAILED (msg length %i)\n", length); DEBUG_DUMP_HEX_LABEL("HMAC RECEIVED", message_hmac, mac_size); DEBUG_DUMP_HEX_LABEL("HMAC COMPUTED", hmac_out, hmac_out_len); TLS_FREE(pt); // silently ignore packet for DTLS if (context->dtls) return header_size + length; _private_random_sleep(context, TLS_MAX_ERROR_SLEEP_uS); _private_tls_write_packet(tls_build_alert(context, 1, bad_record_mac)); return TLS_INTEGRITY_FAILED; } } } context->remote_sequence_number++; #ifdef WITH_TLS_13 if ((context->version == TLS_V13) || (context->version == DTLS_V13)) { if (/*(context->connection_status == 2) && */(type == TLS_APPLICATION_DATA) && (context->crypto.created)) { do { length--; type = ptr[length]; } while (!type); } } #endif switch (type) { // application data case TLS_APPLICATION_DATA: if (context->connection_status != 0xFF) { DEBUG_PRINT("UNEXPECTED APPLICATION DATA MESSAGE\n"); payload_res = TLS_UNEXPECTED_MESSAGE; _private_tls_write_packet(tls_build_alert(context, 1, unexpected_message)); } else { DEBUG_PRINT("APPLICATION DATA MESSAGE (TLS VERSION: %x):\n", (int)context->version); DEBUG_DUMP(ptr, length); DEBUG_PRINT("\n"); _private_tls_write_app_data(context, ptr, length); } break; // handshake case TLS_HANDSHAKE: DEBUG_PRINT("HANDSHAKE MESSAGE\n"); payload_res = tls_parse_payload(context, ptr, length, certificate_verify); break; // change cipher spec case TLS_CHANGE_CIPHER: context->dtls_epoch_remote++; if (context->connection_status != 2) { #ifdef WITH_TLS_13 if (context->connection_status == 4) { DEBUG_PRINT("IGNORING CHANGE CIPHER SPEC MESSAGE (HELLO RETRY REQUEST)\n"); break; } #endif DEBUG_PRINT("UNEXPECTED CHANGE CIPHER SPEC MESSAGE (%i)\n", context->connection_status); _private_tls_write_packet(tls_build_alert(context, 1, unexpected_message)); payload_res = TLS_UNEXPECTED_MESSAGE; } else { DEBUG_PRINT("CHANGE CIPHER SPEC MESSAGE\n"); context->cipher_spec_set = 1; // reset sequence numbers context->remote_sequence_number = 0; } #ifdef WITH_TLS_13 if (!context->is_server) _private_tls13_key(context, 1); #endif break; // alert case TLS_ALERT: DEBUG_PRINT("ALERT MESSAGE\n"); if (length >= 2) { DEBUG_DUMP_HEX(ptr, length); int level = ptr[0]; int code = ptr[1]; if (level == TLS_ALERT_CRITICAL) { context->critical_error = 1; res = TLS_ERROR_ALERT; } context->error_code = code; } break; default: DEBUG_PRINT("NOT UNDERSTOOD MESSAGE TYPE: %x\n", (int)type); TLS_FREE(pt); return TLS_NOT_UNDERSTOOD; } TLS_FREE(pt); if (payload_res < 0) return payload_res; if (res > 0) return header_size + length; return res; } unsigned int asn1_get_len(const unsigned char *buffer, int buf_len, unsigned int *octets) { *octets = 0; if (buf_len < 1) return 0; unsigned char size = buffer[0]; int i; if (size & 0x80) { *octets = size & 0x7F; if ((int)*octets > buf_len - 1) return 0; // max 32 bits unsigned int ref_octets = *octets; if (*octets > 4) ref_octets = 4; if ((int)*octets > buf_len -1) return 0; unsigned int long_size = 0; unsigned int coef = 1; for (i = ref_octets; i > 0; i--) { long_size += buffer[i] * coef; coef *= 0x100; } ++*octets; return long_size; } ++*octets; return size; } void print_index(const unsigned int *fields) { int i = 0; while (fields[i]) { if (i) DEBUG_PRINT("."); DEBUG_PRINT("%i", fields[i]); i++; } while (i < 6) { DEBUG_PRINT(" "); i++; } } int _is_field(const unsigned int *fields, const unsigned int *prefix) { int i = 0; while (prefix[i]) { if (fields[i] != prefix[i]) return 0; i++; } return 1; } int _private_tls_hash_len(int algorithm) { switch (algorithm) { case TLS_RSA_SIGN_MD5: return 16; case TLS_RSA_SIGN_SHA1: return 20; case TLS_RSA_SIGN_SHA256: case TLS_ECDSA_SIGN_SHA256: return 32; case TLS_RSA_SIGN_SHA384: return 48; case TLS_RSA_SIGN_SHA512: return 64; } return 0; } unsigned char *_private_tls_compute_hash(int algorithm, const unsigned char *message, unsigned int message_len) { unsigned char *hash = NULL; if ((!message) || (!message_len)) return hash; int err; hash_state state; switch (algorithm) { case TLS_RSA_SIGN_MD5: DEBUG_PRINT("SIGN MD5\n"); hash = (unsigned char *)TLS_MALLOC(16); if (!hash) return NULL; err = md5_init(&state); if (!err) { err = md5_process(&state, message, message_len); if (!err) err = md5_done(&state, hash); } break; case TLS_RSA_SIGN_SHA1: DEBUG_PRINT("SIGN SHA1\n"); hash = (unsigned char *)TLS_MALLOC(20); if (!hash) return NULL; err = sha1_init(&state); if (!err) { err = sha1_process(&state, message, message_len); if (!err) err = sha1_done(&state, hash); } break; case TLS_RSA_SIGN_SHA256: case TLS_ECDSA_SIGN_SHA256: DEBUG_PRINT("SIGN SHA256\n"); hash = (unsigned char *)TLS_MALLOC(32); if (!hash) return NULL; err = sha256_init(&state); if (!err) { err = sha256_process(&state, message, message_len); if (!err) err = sha256_done(&state, hash); } break; case TLS_RSA_SIGN_SHA384: DEBUG_PRINT("SIGN SHA384\n"); hash = (unsigned char *)TLS_MALLOC(48); if (!hash) return NULL; err = sha384_init(&state); if (!err) { err = sha384_process(&state, message, message_len); if (!err) err = sha384_done(&state, hash); } break; case TLS_RSA_SIGN_SHA512: DEBUG_PRINT("SIGN SHA512\n"); hash = (unsigned char *)TLS_MALLOC(64); if (!hash) return NULL; err = sha512_init(&state); if (!err) { err = sha512_process(&state, message, message_len); if (!err) err = sha512_done(&state, hash); } break; default: DEBUG_PRINT("UNKNOWN SIGNATURE ALGORITHM\n"); } return hash; } int tls_certificate_verify_signature(struct TLSCertificate *cert, struct TLSCertificate *parent) { if ((!cert) || (!parent) || (!cert->sign_key) || (!cert->fingerprint) || (!cert->sign_len) || (!parent->der_bytes) || (!parent->der_len)) { DEBUG_PRINT("CANNOT VERIFY SIGNATURE"); return 0; } tls_init(); int hash_len = _private_tls_hash_len(cert->algorithm); if (hash_len <= 0) return 0; int hash_index = -1; switch (cert->algorithm) { case TLS_RSA_SIGN_MD5: hash_index = find_hash("md5"); break; case TLS_RSA_SIGN_SHA1: hash_index = find_hash("sha1"); break; case TLS_RSA_SIGN_SHA256: case TLS_ECDSA_SIGN_SHA256: hash_index = find_hash("sha256"); break; case TLS_RSA_SIGN_SHA384: hash_index = find_hash("sha384"); break; case TLS_RSA_SIGN_SHA512: hash_index = find_hash("sha512"); break; default: DEBUG_PRINT("UNKNOWN SIGNATURE ALGORITHM\n"); return 0; } #ifdef TLS_ECDSA_SUPPORTED if (cert->algorithm == TLS_ECDSA_SIGN_SHA256) { ecc_key key; int err = ecc_import(parent->der_bytes, parent->der_len, &key); if (err) { DEBUG_PRINT("Error importing ECC certificate (code: %i)\n", err); DEBUG_DUMP_HEX_LABEL("CERTIFICATE", parent->der_bytes, parent->der_len); return 0; } int ecc_stat = 0; unsigned char *signature = cert->sign_key; int signature_len = cert->sign_len; if (!signature[0]) { signature++; signature_len--; } err = ecc_verify_hash(signature, signature_len, cert->fingerprint, hash_len, &ecc_stat, &key); ecc_free(&key); if (err) { DEBUG_PRINT("ECC HASH VERIFY ERROR %i\n", err); return 0; } DEBUG_PRINT("ECC CERTIFICATE VALIDATION: %i\n", ecc_stat); return ecc_stat; } #endif rsa_key key; int err = rsa_import(parent->der_bytes, parent->der_len, &key); if (err) { DEBUG_PRINT("Error importing RSA certificate (code: %i)\n", err); DEBUG_DUMP_HEX_LABEL("CERTIFICATE", parent->der_bytes, parent->der_len); return 0; } int rsa_stat = 0; unsigned char *signature = cert->sign_key; int signature_len = cert->sign_len; if (!signature[0]) { signature++; signature_len--; } err = rsa_verify_hash_ex(signature, signature_len, cert->fingerprint, hash_len, LTC_PKCS_1_V1_5, hash_index, 0, &rsa_stat, &key); rsa_free(&key); if (err) { DEBUG_PRINT("HASH VERIFY ERROR %i\n", err); return 0; } DEBUG_PRINT("CERTIFICATE VALIDATION: %i\n", rsa_stat); return rsa_stat; } int tls_certificate_chain_is_valid(struct TLSCertificate **certificates, int len) { if ((!certificates) || (!len)) return bad_certificate; int i; len--; // expired certificate or not yet valid ? if (tls_certificate_is_valid(certificates[0])) return bad_certificate; // check for (i = 0; i < len; i++) { // certificate in chain is expired ? if (tls_certificate_is_valid(certificates[i+1])) return bad_certificate; if (!tls_certificate_verify_signature(certificates[i], certificates[i+1])) return bad_certificate; } return 0; } int tls_certificate_chain_is_valid_root(struct TLSContext *context, struct TLSCertificate **certificates, int len) { if ((!certificates) || (!len) || (!context->root_certificates) || (!context->root_count)) return bad_certificate; int i; unsigned int j; for (i = 0; i < len; i++) { for (j = 0; j < context->root_count; j++) { // check if root certificate expired if (tls_certificate_is_valid(context->root_certificates[j])) continue; // if any root validates any certificate in the chain, then is root validated if (tls_certificate_verify_signature(certificates[i], context->root_certificates[j])) return 0; } } return bad_certificate; } int _private_is_oid(struct _private_OID_chain *ref_chain, const unsigned char *looked_oid, int looked_oid_len) { while (ref_chain) { if (ref_chain->oid) { if (_is_oid2(ref_chain->oid, looked_oid, 16, looked_oid_len)) return 1; } ref_chain = (struct _private_OID_chain *)ref_chain->top; } return 0; } int _private_asn1_parse(struct TLSContext *context, struct TLSCertificate *cert, const unsigned char *buffer, unsigned int size, int level, unsigned int *fields, unsigned char *has_key, int client_cert, unsigned char *top_oid, struct _private_OID_chain *chain) { struct _private_OID_chain local_chain; local_chain.top = chain; unsigned int pos = 0; // X.690 int idx = 0; unsigned char oid[16]; memset(oid, 0, 16); local_chain.oid = oid; if (has_key) *has_key = 0; unsigned char local_has_key = 0; const unsigned char *cert_data = NULL; unsigned int cert_len = 0; while (pos < size) { unsigned int start_pos = pos; CHECK_SIZE(2, size - pos, TLS_NEED_MORE_DATA) unsigned char first = buffer[pos++]; unsigned char type = first & 0x1F; unsigned char constructed = first & 0x20; unsigned char element_class = first >> 6; unsigned int octets = 0; unsigned int temp; idx++; if (level <= TLS_ASN1_MAXLEVEL) fields[level - 1] = idx; unsigned int length = asn1_get_len((unsigned char *)&buffer[pos], size - pos, &octets); if ((octets > 4) || (octets > size - pos)) { DEBUG_PRINT("CANNOT READ CERTIFICATE\n"); return pos; } pos += octets; CHECK_SIZE(length, size - pos, TLS_NEED_MORE_DATA) //DEBUG_PRINT("FIRST: %x => %x (%i)\n", (int)first, (int)type, length); // sequence //DEBUG_PRINT("%2i: ", level); #ifdef DEBUG DEBUG_INDEX(fields); int i1; for (i1 = 1; i1 < level; i1++) DEBUG_PRINT(" "); #endif if ((length) && (constructed)) { switch (type) { case 0x03: DEBUG_PRINT("CONSTRUCTED BITSTREAM\n"); break; case 0x10: DEBUG_PRINT("SEQUENCE\n"); if ((level == 2) && (idx == 1)) { cert_len = length + (pos - start_pos); cert_data = &buffer[start_pos]; } // private key on server or public key on client if ((!cert->version) && (_is_field(fields, priv_der_id))) { TLS_FREE(cert->der_bytes); temp = length + (pos - start_pos); cert->der_bytes = (unsigned char *)TLS_MALLOC(temp); if (cert->der_bytes) { memcpy(cert->der_bytes, &buffer[start_pos], temp); cert->der_len = temp; } else cert->der_len = 0; } break; case 0x11: DEBUG_PRINT("EMBEDDED PDV\n"); break; case 0x00: if (element_class == 0x02) { DEBUG_PRINT("CONTEXT-SPECIFIC\n"); break; } default: DEBUG_PRINT("CONSTRUCT TYPE %02X\n", (int)type); } local_has_key = 0; _private_asn1_parse(context, cert, &buffer[pos], length, level + 1, fields, &local_has_key, client_cert, top_oid, &local_chain); if ((((local_has_key) && (context) && ((!context->is_server) || (client_cert))) || (!context)) && (_is_field(fields, pk_id))) { TLS_FREE(cert->der_bytes); temp = length + (pos - start_pos); cert->der_bytes = (unsigned char *)TLS_MALLOC(temp); if (cert->der_bytes) { memcpy(cert->der_bytes, &buffer[start_pos], temp); cert->der_len = temp; } else cert->der_len = 0; } } else { switch (type) { case 0x00: // end of content DEBUG_PRINT("END OF CONTENT\n"); return pos; break; case 0x01: // boolean temp = buffer[pos]; DEBUG_PRINT("BOOLEAN: %i\n", temp); break; case 0x02: // integer if (_is_field(fields, pk_id)) { if (has_key) *has_key = 1; if (idx == 1) tls_certificate_set_key(cert, &buffer[pos], length); else if (idx == 2) tls_certificate_set_exponent(cert, &buffer[pos], length); } else if (_is_field(fields, serial_id)) tls_certificate_set_serial(cert, &buffer[pos], length); if (_is_field(fields, version_id)) { if (length == 1) cert->version = buffer[pos]; #ifdef TLS_X509_V1_SUPPORT else cert->version = 0; idx++; #endif } if (level >= 2) { unsigned int fields_temp[3]; fields_temp[0] = fields[level - 2]; fields_temp[1] = fields[level - 1]; fields_temp[2] = 0; if (_is_field(fields_temp, priv_id)) tls_certificate_set_priv(cert, &buffer[pos], length); } DEBUG_PRINT("INTEGER(%i): ", length); DEBUG_DUMP_HEX(&buffer[pos], length); if ((chain) && (length > 2)) { if (_private_is_oid(chain, san_oid, sizeof(san_oid) - 1)) { cert->san = (unsigned char **)TLS_REALLOC(cert->san, sizeof(unsigned char *) * (cert->san_length + 1)); if (cert->san) { cert->san[cert->san_length] = NULL; tls_certificate_set_copy(&cert->san[cert->san_length], &buffer[pos], length); DEBUG_PRINT(" => SUBJECT ALTERNATIVE NAME: %s", cert->san[cert->san_length ]); cert->san_length++; } else cert->san_length = 0; } } DEBUG_PRINT("\n"); break; case 0x03: if (_is_field(fields, pk_id)) { if (has_key) *has_key = 1; } // bitstream DEBUG_PRINT("BITSTREAM(%i): ", length); DEBUG_DUMP_HEX(&buffer[pos], length); DEBUG_PRINT("\n"); if (_is_field(fields, sign_id)) { tls_certificate_set_sign_key(cert, &buffer[pos], length); } else if ((cert->ec_algorithm) && (_is_field(fields, pk_id))) { tls_certificate_set_key(cert, &buffer[pos], length); } else { if ((buffer[pos] == 0x00) && (length > 256)) _private_asn1_parse(context, cert, &buffer[pos]+1, length - 1, level + 1, fields, &local_has_key, client_cert, top_oid, &local_chain); else _private_asn1_parse(context, cert, &buffer[pos], length, level + 1, fields, &local_has_key, client_cert, top_oid, &local_chain); #ifdef TLS_FORWARD_SECRECY #ifdef TLS_ECDSA_SUPPORTED if (top_oid) { if (_is_oid2(top_oid, TLS_EC_prime256v1_OID, sizeof(oid), sizeof(TLS_EC_prime256v1) - 1)) { cert->ec_algorithm = secp256r1.iana; } else if (_is_oid2(top_oid, TLS_EC_secp224r1_OID, sizeof(oid), sizeof(TLS_EC_secp224r1_OID) - 1)) { cert->ec_algorithm = secp224r1.iana; } else if (_is_oid2(top_oid, TLS_EC_secp384r1_OID, sizeof(oid), sizeof(TLS_EC_secp384r1_OID) - 1)) { cert->ec_algorithm = secp384r1.iana; } else if (_is_oid2(top_oid, TLS_EC_secp521r1_OID, sizeof(oid), sizeof(TLS_EC_secp521r1_OID) - 1)) { cert->ec_algorithm = secp521r1.iana; } if ((cert->ec_algorithm) && (!cert->pk)) tls_certificate_set_key(cert, &buffer[pos], length); } #endif #endif } break; case 0x04: if ((top_oid) && (_is_field(fields, ecc_priv_id)) && (!cert->priv)) { DEBUG_PRINT("BINARY STRING(%i): ", length); DEBUG_DUMP_HEX(&buffer[pos], length); DEBUG_PRINT("\n"); tls_certificate_set_priv(cert, &buffer[pos], length); } else _private_asn1_parse(context, cert, &buffer[pos], length, level + 1, fields, &local_has_key, client_cert, top_oid, &local_chain); break; case 0x05: DEBUG_PRINT("NULL\n"); break; case 0x06: // object identifier if (_is_field(fields, pk_id)) { #ifdef TLS_ECDSA_SUPPORTED if ((length == 8) || (length == 5)) tls_certificate_set_algorithm(context, &cert->ec_algorithm, &buffer[pos], length); else #endif tls_certificate_set_algorithm(context, &cert->key_algorithm, &buffer[pos], length); } if (_is_field(fields, algorithm_id)) tls_certificate_set_algorithm(context, &cert->algorithm, &buffer[pos], length); DEBUG_PRINT("OBJECT IDENTIFIER(%i): ", length); DEBUG_DUMP_HEX(&buffer[pos], length); DEBUG_PRINT("\n"); // check previous oid if (_is_oid2(oid, ocsp_oid, 16, sizeof(ocsp_oid) - 1)) tls_certificate_set_copy(&cert->ocsp, &buffer[pos], length); if (length < 16) memcpy(oid, &buffer[pos], length); else memcpy(oid, &buffer[pos], 16); if (top_oid) memcpy(top_oid, oid, 16); break; case 0x09: DEBUG_PRINT("REAL NUMBER(%i): ", length); DEBUG_DUMP_HEX(&buffer[pos], length); DEBUG_PRINT("\n"); break; case 0x17: // utc time DEBUG_PRINT("UTC TIME: ["); DEBUG_DUMP(&buffer[pos], length); DEBUG_PRINT("]\n"); if (_is_field(fields, validity_id)) { if (idx == 1) tls_certificate_set_copy_date(&cert->not_before, &buffer[pos], length); else tls_certificate_set_copy_date(&cert->not_after, &buffer[pos], length); } break; case 0x18: // generalized time DEBUG_PRINT("GENERALIZED TIME: ["); DEBUG_DUMP(&buffer[pos], length); DEBUG_PRINT("]\n"); break; case 0x13: // printable string case 0x0C: case 0x14: case 0x15: case 0x16: case 0x19: case 0x1A: case 0x1B: case 0x1C: case 0x1D: case 0x1E: if (_is_field(fields, issurer_id)) { if (_is_oid(oid, country_oid, 3)) tls_certificate_set_copy(&cert->issuer_country, &buffer[pos], length); else if (_is_oid(oid, state_oid, 3)) tls_certificate_set_copy(&cert->issuer_state, &buffer[pos], length); else if (_is_oid(oid, location_oid, 3)) tls_certificate_set_copy(&cert->issuer_location, &buffer[pos], length); else if (_is_oid(oid, entity_oid, 3)) tls_certificate_set_copy(&cert->issuer_entity, &buffer[pos], length); else if (_is_oid(oid, subject_oid, 3)) tls_certificate_set_copy(&cert->issuer_subject, &buffer[pos], length); } else if (_is_field(fields, owner_id)) { if (_is_oid(oid, country_oid, 3)) tls_certificate_set_copy(&cert->country, &buffer[pos], length); else if (_is_oid(oid, state_oid, 3)) tls_certificate_set_copy(&cert->state, &buffer[pos], length); else if (_is_oid(oid, location_oid, 3)) tls_certificate_set_copy(&cert->location, &buffer[pos], length); else if (_is_oid(oid, entity_oid, 3)) tls_certificate_set_copy(&cert->entity, &buffer[pos], length); else if (_is_oid(oid, subject_oid, 3)) tls_certificate_set_copy(&cert->subject, &buffer[pos], length); } DEBUG_PRINT("STR: ["); DEBUG_DUMP(&buffer[pos], length); DEBUG_PRINT("]\n"); break; case 0x10: DEBUG_PRINT("EMPTY SEQUENCE\n"); break; case 0xA: DEBUG_PRINT("ENUMERATED(%i): ", length); DEBUG_DUMP_HEX(&buffer[pos], length); DEBUG_PRINT("\n"); break; default: DEBUG_PRINT("========> NOT SUPPORTED %x\n", (int)type); // not supported / needed break; } } pos += length; } if ((level == 2) && (cert->sign_key) && (cert->sign_len) && (cert_len) && (cert_data)) { TLS_FREE(cert->fingerprint); cert->fingerprint = _private_tls_compute_hash(cert->algorithm, cert_data, cert_len); #ifdef DEBUG if (cert->fingerprint) { DEBUG_DUMP_HEX_LABEL("FINGERPRINT", cert->fingerprint, _private_tls_hash_len(cert->algorithm)); } #endif } return pos; } struct TLSCertificate *asn1_parse(struct TLSContext *context, const unsigned char *buffer, unsigned int size, int client_cert) { unsigned int fields[TLS_ASN1_MAXLEVEL]; memset(fields, 0, sizeof(int) * TLS_ASN1_MAXLEVEL); struct TLSCertificate *cert = tls_create_certificate(); if (cert) { if (client_cert < 0) { client_cert = 0; // private key unsigned char top_oid[16]; memset(top_oid, 0, sizeof(top_oid)); _private_asn1_parse(context, cert, buffer, size, 1, fields, NULL, client_cert, top_oid, NULL); } else _private_asn1_parse(context, cert, buffer, size, 1, fields, NULL, client_cert, NULL, NULL); } return cert; } int tls_load_certificates(struct TLSContext *context, const unsigned char *pem_buffer, int pem_size) { if (!context) return TLS_GENERIC_ERROR; unsigned int len; int idx = 0; do { unsigned char *data = tls_pem_decode(pem_buffer, pem_size, idx++, &len); if ((!data) || (!len)) break; struct TLSCertificate *cert = asn1_parse(context, data, len, 0); if (cert) { if ((cert->version == 2) #ifdef TLS_X509_V1_SUPPORT || (cert->version == 0) #endif ) { TLS_FREE(cert->der_bytes); cert->der_bytes = data; cert->der_len = len; data = NULL; if (cert->priv) { DEBUG_PRINT("WARNING - parse error (private key encountered in certificate)\n"); TLS_FREE(cert->priv); cert->priv = NULL; cert->priv_len = 0; } if (context->is_server) { context->certificates = (struct TLSCertificate **)TLS_REALLOC(context->certificates, (context->certificates_count + 1) * sizeof(struct TLSCertificate *)); context->certificates[context->certificates_count] = cert; context->certificates_count++; DEBUG_PRINT("Loaded certificate: %i\n", (int)context->certificates_count); } else { context->client_certificates = (struct TLSCertificate **)TLS_REALLOC(context->client_certificates, (context->client_certificates_count + 1) * sizeof(struct TLSCertificate *)); context->client_certificates[context->client_certificates_count] = cert; context->client_certificates_count++; DEBUG_PRINT("Loaded client certificate: %i\n", (int)context->client_certificates_count); } } else { DEBUG_PRINT("WARNING - certificate version error (v%i)\n", (int)cert->version); tls_destroy_certificate(cert); } } TLS_FREE(data); } while (1); return context->certificates_count; } int tls_load_private_key(struct TLSContext *context, const unsigned char *pem_buffer, int pem_size) { if (!context) return TLS_GENERIC_ERROR; unsigned int len; int idx = 0; do { unsigned char *data = tls_pem_decode(pem_buffer, pem_size, idx++, &len); if ((!data) || (!len)) break; struct TLSCertificate *cert = asn1_parse(context, data, len, -1); if (cert) { if (!cert->der_len) { TLS_FREE(cert->der_bytes); cert->der_bytes = data; cert->der_len = len; } else TLS_FREE(data); if ((cert) && (cert->priv) && (cert->priv_len)) { #ifdef TLS_ECDSA_SUPPORTED if (cert->ec_algorithm) { DEBUG_PRINT("Loaded ECC private key\n"); if (context->ec_private_key) tls_destroy_certificate(context->ec_private_key); context->ec_private_key = cert; return 1; } else #endif { DEBUG_PRINT("Loaded private key\n"); if (context->private_key) tls_destroy_certificate(context->private_key); context->private_key = cert; return 1; } } tls_destroy_certificate(cert); } else TLS_FREE(data); } while (1); return 0; } int tls_clear_certificates(struct TLSContext *context) { unsigned int i; if ((!context) || (!context->is_server) || (context->is_child)) return TLS_GENERIC_ERROR; if (context->root_certificates) { for (i = 0; i < context->root_count; i++) tls_destroy_certificate(context->root_certificates[i]); } context->root_certificates = NULL; context->root_count = 0; if (context->private_key) tls_destroy_certificate(context->private_key); context->private_key = NULL; #ifdef TLS_ECDSA_SUPPORTED if (context->ec_private_key) tls_destroy_certificate(context->ec_private_key); context->ec_private_key = NULL; #endif TLS_FREE(context->certificates); context->certificates = NULL; context->certificates_count = 0; return 0; } #ifdef WITH_TLS_13 struct TLSPacket *tls_build_certificate_verify(struct TLSContext *context) { struct TLSPacket *packet = tls_create_packet(context, TLS_HANDSHAKE, context->version, 0); //certificate verify tls_packet_uint8(packet, 0x0F); unsigned int size_offset = packet->len; tls_packet_uint24(packet, 0); unsigned char out[TLS_MAX_RSA_KEY]; #ifdef TLS_ECDSA_SUPPORTED unsigned long out_len = TLS_MAX_RSA_KEY; #endif unsigned char signing_data[TLS_MAX_HASH_SIZE + 98]; int signing_data_len; // first 64 bytes to 0x20 (32) memset(signing_data, 0x20, 64); // context string 33 bytes if (context->is_server) memcpy(signing_data + 64, "TLS 1.3, server CertificateVerify", 33); else memcpy(signing_data + 64, "TLS 1.3, client CertificateVerify", 33); // a single 0 byte separator signing_data[97] = 0; signing_data_len = 98; signing_data_len += _private_tls_get_hash(context, signing_data + 98); DEBUG_DUMP_HEX_LABEL("verify data", signing_data, signing_data_len); int hash_algorithm = sha256; #ifdef TLS_ECDSA_SUPPORTED if (tls_is_ecdsa(context)) { switch (context->ec_private_key->ec_algorithm) { case 23: // secp256r1 + sha256 tls_packet_uint16(packet, 0x0403); break; case 24: // secp384r1 + sha384 tls_packet_uint16(packet, 0x0503); hash_algorithm = sha384; break; case 25: // secp521r1 + sha512 tls_packet_uint16(packet, 0x0603); hash_algorithm = sha512; break; default: DEBUG_PRINT("UNSUPPORTED CURVE (SIGNING)\n"); packet->broken = 1; return packet; } } else #endif { tls_packet_uint16(packet, 0x0804); } int packet_size = 2; #ifdef TLS_ECDSA_SUPPORTED if (tls_is_ecdsa(context)) { if (_private_tls_sign_ecdsa(context, hash_algorithm, signing_data, signing_data_len, out, &out_len) == 1) { DEBUG_PRINT("ECDSA signing OK! (ECDSA, length %lu)\n", out_len); tls_packet_uint16(packet, out_len); tls_packet_append(packet, out, out_len); packet_size += out_len + 2; } } else #endif if (_private_tls_sign_rsa(context, hash_algorithm, signing_data, signing_data_len, out, &out_len) == 1) { DEBUG_PRINT("RSA signing OK! (length %lu)\n", out_len); tls_packet_uint16(packet, out_len); tls_packet_append(packet, out, out_len); packet_size += out_len + 2; } packet->buf[size_offset] = packet_size / 0x10000; packet_size %= 0x10000; packet->buf[size_offset + 1] = packet_size / 0x100; packet_size %= 0x100; packet->buf[size_offset + 2] = packet_size; tls_packet_update(packet); return packet; } #endif struct TLSPacket *tls_build_certificate(struct TLSContext *context) { int i; unsigned int all_certificate_size = 0; int certificates_count; struct TLSCertificate **certificates; if (context->is_server) { certificates_count = context->certificates_count; certificates = context->certificates; } else { certificates_count = context->client_certificates_count; certificates = context->client_certificates; } int delta = 3; #ifdef WITH_TLS_13 if ((context->version == TLS_V13) || (context->version == DTLS_V13)) delta = 5; #endif #ifdef TLS_ECDSA_SUPPORTED int is_ecdsa = tls_is_ecdsa(context); if (is_ecdsa) { for (i = 0; i < certificates_count; i++) { struct TLSCertificate *cert = certificates[i]; if ((cert) && (cert->der_len) && (cert->ec_algorithm)) all_certificate_size += cert->der_len + delta; } } else { for (i = 0; i < certificates_count; i++) { struct TLSCertificate *cert = certificates[i]; if ((cert) && (cert->der_len) && (!cert->ec_algorithm)) all_certificate_size += cert->der_len + delta; } } #else for (i = 0; i < certificates_count; i++) { struct TLSCertificate *cert = certificates[i]; if ((cert) && (cert->der_len)) all_certificate_size += cert->der_len + delta; } #endif if (!all_certificate_size) { DEBUG_PRINT("NO CERTIFICATE SET\n"); } struct TLSPacket *packet = tls_create_packet(context, TLS_HANDSHAKE, context->version, 0); tls_packet_uint8(packet, 0x0B); if (all_certificate_size) { #ifdef WITH_TLS_13 // context if ((context->version == TLS_V13) || (context->version == DTLS_V13)) { tls_packet_uint24(packet, all_certificate_size + 4); tls_packet_uint8(packet, 0); } else #endif tls_packet_uint24(packet, all_certificate_size + 3); if (context->dtls) _private_dtls_handshake_data(context, packet, all_certificate_size + 3); tls_packet_uint24(packet, all_certificate_size); for (i = 0; i < certificates_count; i++) { struct TLSCertificate *cert = certificates[i]; if ((cert) && (cert->der_len)) { #ifdef TLS_ECDSA_SUPPORTED // is RSA certificate ? if ((is_ecdsa) && (!cert->ec_algorithm)) continue; // is ECC certificate ? if ((!is_ecdsa) && (cert->ec_algorithm)) continue; #endif // 2 times -> one certificate tls_packet_uint24(packet, cert->der_len); tls_packet_append(packet, cert->der_bytes, cert->der_len); #ifdef WITH_TLS_13 // extension if ((context->version == TLS_V13) || (context->version == DTLS_V13)) tls_packet_uint16(packet, 0); #endif } } } else { tls_packet_uint24(packet, all_certificate_size); #ifdef WITH_TLS_13 if ((context->version == TLS_V13) || (context->version == DTLS_V13)) tls_packet_uint8(packet, 0); #endif if (context->dtls) _private_dtls_handshake_data(context, packet, all_certificate_size); } tls_packet_update(packet); if (context->dtls) context->dtls_seq++; return packet; } #ifdef WITH_TLS_13 struct TLSPacket *tls_build_encrypted_extensions(struct TLSContext *context) { struct TLSPacket *packet = tls_create_packet(context, TLS_HANDSHAKE, context->version, 3); tls_packet_uint8(packet, 0x08); if (context->negotiated_alpn) { int alpn_negotiated_len = strlen(context->negotiated_alpn); int alpn_len = alpn_negotiated_len + 1; tls_packet_uint24(packet, alpn_len + 8); tls_packet_uint16(packet, alpn_len + 6); tls_packet_uint16(packet, 0x10); tls_packet_uint16(packet, alpn_len + 2); tls_packet_uint16(packet, alpn_len); tls_packet_uint8(packet, alpn_negotiated_len); tls_packet_append(packet, (unsigned char *)context->negotiated_alpn, alpn_negotiated_len); } else { tls_packet_uint24(packet, 2); tls_packet_uint16(packet, 0); } tls_packet_update(packet); return packet; } #endif struct TLSPacket *tls_build_finished(struct TLSContext *context) { struct TLSPacket *packet = tls_create_packet(context, TLS_HANDSHAKE, context->version, TLS_MIN_FINISHED_OPAQUE_LEN + 64); tls_packet_uint8(packet, 0x14); #ifdef WITH_TLS_13 if ((context->version == TLS_V13) || (context->version == DTLS_V13)) tls_packet_uint24(packet, _private_tls_mac_length(context)); else #endif tls_packet_uint24(packet, TLS_MIN_FINISHED_OPAQUE_LEN); if (context->dtls) _private_dtls_handshake_data(context, packet, TLS_MIN_FINISHED_OPAQUE_LEN); // verify unsigned char hash[TLS_MAX_HASH_SIZE]; unsigned long out_size = TLS_MIN_FINISHED_OPAQUE_LEN; #ifdef WITH_TLS_13 unsigned char out[TLS_MAX_HASH_SIZE]; #else unsigned char out[TLS_MIN_FINISHED_OPAQUE_LEN]; #endif unsigned int hash_len; // server verifies client's message if (context->is_server) { #ifdef WITH_TLS_13 if ((context->version == TLS_V13) || (context->version == DTLS_V13)) { hash_len = _private_tls_get_hash(context, hash); if ((!context->finished_key) || (!hash_len)) { DEBUG_PRINT("NO FINISHED KEY COMPUTED OR NO HANDSHAKE HASH\n"); packet->broken = 1; return packet; } DEBUG_DUMP_HEX_LABEL("HS HASH", hash, hash_len); DEBUG_DUMP_HEX_LABEL("HS FINISH", context->finished_key, hash_len); DEBUG_DUMP_HEX_LABEL("HS REMOTE FINISH", context->remote_finished_key, hash_len); out_size = hash_len; hmac_state hmac; hmac_init(&hmac, _private_tls_get_hash_idx(context), context->finished_key, hash_len); hmac_process(&hmac, hash, hash_len); hmac_done(&hmac, out, &out_size); } else #endif { hash_len = _private_tls_done_hash(context, hash); _private_tls_prf(context, out, TLS_MIN_FINISHED_OPAQUE_LEN, context->master_key, context->master_key_len, (unsigned char *)"server finished", 15, hash, hash_len, NULL, 0); _private_tls_destroy_hash(context); } } else { #ifdef WITH_TLS_13 if ((context->version == TLS_V13) || (context->version == DTLS_V13)) { hash_len = _private_tls_get_hash(context, hash); if ((!context->finished_key) || (!hash_len)) { DEBUG_PRINT("NO FINISHED KEY COMPUTED OR NO HANDSHAKE HASH\n"); packet->broken = 1; return packet; } DEBUG_DUMP_HEX_LABEL("HS HASH", hash, hash_len); DEBUG_DUMP_HEX_LABEL("HS FINISH", context->finished_key, hash_len); DEBUG_DUMP_HEX_LABEL("HS REMOTE FINISH", context->remote_finished_key, hash_len); TLS_FREE(context->server_finished_hash); context->server_finished_hash = (unsigned char *)TLS_MALLOC(hash_len); if (context->server_finished_hash) memcpy(context->server_finished_hash, hash, hash_len); out_size = hash_len; hmac_state hmac; hmac_init(&hmac, _private_tls_get_hash_idx(context), context->finished_key, hash_len); hmac_process(&hmac, hash, hash_len); hmac_done(&hmac, out, &out_size); } else { #endif hash_len = _private_tls_get_hash(context, hash); _private_tls_prf(context, out, TLS_MIN_FINISHED_OPAQUE_LEN, context->master_key, context->master_key_len, (unsigned char *)"client finished", 15, hash, hash_len, NULL, 0); #ifdef WITH_TLS_13 } #endif } tls_packet_append(packet, out, out_size); tls_packet_update(packet); DEBUG_DUMP_HEX_LABEL("VERIFY DATA", out, out_size); #ifdef TLS_ACCEPT_SECURE_RENEGOTIATION if (context->is_server) { // concatenate client verify and server verify context->verify_data = (unsigned char *)TLS_REALLOC(context->verify_data, out_size); if (context->verify_data) { memcpy(context->verify_data + context->verify_len, out, out_size); context->verify_len += out_size; } else context->verify_len = 0; } else { TLS_FREE(context->verify_data); context->verify_data = (unsigned char *)TLS_MALLOC(out_size); if (context->verify_data) { memcpy(context->verify_data, out, out_size); context->verify_len = out_size; } } #endif return packet; } struct TLSPacket *tls_build_change_cipher_spec(struct TLSContext *context) { struct TLSPacket *packet = tls_create_packet(context, TLS_CHANGE_CIPHER, context->version, 64); tls_packet_uint8(packet, 1); tls_packet_update(packet); context->local_sequence_number = 0; return packet; } struct TLSPacket *tls_build_done(struct TLSContext *context) { struct TLSPacket *packet = tls_create_packet(context, TLS_HANDSHAKE, context->version, 0); tls_packet_uint8(packet, 0x0E); tls_packet_uint24(packet, 0); if (context->dtls) { _private_dtls_handshake_data(context, packet, 0); context->dtls_seq++; } tls_packet_update(packet); return packet; } struct TLSPacket *tls_build_message(struct TLSContext *context, const unsigned char *data, unsigned int len) { if ((!data) || (!len)) return 0; struct TLSPacket *packet = tls_create_packet(context, TLS_APPLICATION_DATA, context->version, len); tls_packet_append(packet, data, len); tls_packet_update(packet); return packet; } int tls_client_connect(struct TLSContext *context) { if ((context->is_server) || (context->critical_error)) return TLS_UNEXPECTED_MESSAGE; return _private_tls_write_packet(tls_build_hello(context, 0)); } int tls_write(struct TLSContext *context, const unsigned char *data, unsigned int len) { if (!context) return TLS_GENERIC_ERROR; #ifdef TLS_12_FALSE_START if ((context->connection_status != 0xFF) && ((context->is_server) || (context->version != TLS_V12) || (context->critical_error) || (!context->false_start))) return TLS_UNEXPECTED_MESSAGE; #else if (context->connection_status != 0xFF) return TLS_UNEXPECTED_MESSAGE; #endif if (len > TLS_MAXTLS_APP_SIZE) len = TLS_MAXTLS_APP_SIZE; int actually_written = _private_tls_write_packet(tls_build_message(context, data, len)); if (actually_written <= 0) return actually_written; return len; } struct TLSPacket *tls_build_alert(struct TLSContext *context, char critical, unsigned char code) { struct TLSPacket *packet = tls_create_packet(context, TLS_ALERT, context->version, 0); tls_packet_uint8(packet, critical ? TLS_ALERT_CRITICAL : TLS_ALERT_WARNING); if (critical) context->critical_error = 1; tls_packet_uint8(packet, code); tls_packet_update(packet); return packet; } int _private_tls_read_from_file(const char *fname, void *buf, int max_len) { FILE *f = fopen(fname, "rb"); if (f) { int size = (int)fread(buf, 1, max_len, f); fclose(f); return size; } return 0; } int tls_consume_stream(struct TLSContext *context, const unsigned char *buf, int buf_len, tls_validation_function certificate_verify) { if (!context) return TLS_GENERIC_ERROR; if (context->critical_error) return TLS_BROKEN_CONNECTION; if (buf_len <= 0) { DEBUG_PRINT("tls_consume_stream called with buf_len %i\n", buf_len); return 0; } if (!buf) { DEBUG_PRINT("tls_consume_stream called NULL buffer\n"); context->critical_error = 1; return TLS_NO_MEMORY; } unsigned int orig_len = context->message_buffer_len; context->message_buffer_len += buf_len; context->message_buffer = (unsigned char *)TLS_REALLOC(context->message_buffer, context->message_buffer_len); if (!context->message_buffer) { context->message_buffer_len = 0; return TLS_NO_MEMORY; } memcpy(context->message_buffer + orig_len, buf, buf_len); unsigned int index = 0; unsigned int tls_buffer_len = context->message_buffer_len; int err_flag = 0; int tls_header_size; int tls_size_offset; if (context->dtls) { tls_size_offset = 11; tls_header_size = 13; } else { tls_size_offset = 3; tls_header_size = 5; } while (tls_buffer_len >= 5) { unsigned int length = ntohs(*(unsigned short *)&context->message_buffer[index + tls_size_offset]) + tls_header_size; if (length > tls_buffer_len) { DEBUG_PRINT("NEED DATA: %i/%i\n", length, tls_buffer_len); break; } int consumed = tls_parse_message(context, &context->message_buffer[index], length, certificate_verify); DEBUG_PRINT("Consumed %i bytes\n", consumed); if (consumed < 0) { if (!context->critical_error) context->critical_error = 1; err_flag = consumed; break; } index += length; tls_buffer_len -= length; if (context->critical_error) { err_flag = TLS_BROKEN_CONNECTION; break; } } if (err_flag) { DEBUG_PRINT("ERROR IN CONSUME: %i\n", err_flag); context->message_buffer_len = 0; TLS_FREE(context->message_buffer); context->message_buffer = NULL; return err_flag; } if (index) { context->message_buffer_len -= index; if (context->message_buffer_len) { // no realloc here memmove(context->message_buffer, context->message_buffer + index, context->message_buffer_len); } else { TLS_FREE(context->message_buffer); context->message_buffer = NULL; } } return index; } void tls_close_notify(struct TLSContext *context) { if ((!context) || (context->critical_error)) return; context->critical_error = 1; DEBUG_PRINT("CLOSE\n"); _private_tls_write_packet(tls_build_alert(context, 0, close_notify)); } void tls_alert(struct TLSContext *context, unsigned char critical, int code) { if (!context) return; if ((!context->critical_error) && (critical)) context->critical_error = 1; DEBUG_PRINT("ALERT\n"); _private_tls_write_packet(tls_build_alert(context, critical, code)); } int tls_pending(struct TLSContext *context) { if (!context->message_buffer) return 0; return context->message_buffer_len; } void tls_make_exportable(struct TLSContext *context, unsigned char exportable_flag) { context->exportable = exportable_flag; if (!exportable_flag) { // zero the memory if ((context->exportable_keys) && (context->exportable_size)) memset(context->exportable_keys, 0, context->exportable_size); // free the memory, if alocated TLS_FREE(context->exportable_keys); context->exportable_size = 0; } } int tls_export_context(struct TLSContext *context, unsigned char *buffer, unsigned int buf_len, unsigned char small_version) { // only negotiated AND exportable connections may be exported if ((!context) || (context->critical_error) || (context->connection_status != 0xFF) || (!context->exportable) || (!context->exportable_keys) || (!context->exportable_size) || (!context->crypto.created)) { DEBUG_PRINT("CANNOT EXPORT CONTEXT %i\n", (int)context->connection_status); return 0; } struct TLSPacket *packet = tls_create_packet(NULL, TLS_SERIALIZED_OBJECT, context->version, 0); // export buffer version tls_packet_uint8(packet, 0x01); tls_packet_uint8(packet, context->connection_status); tls_packet_uint16(packet, context->cipher); if (context->is_child) tls_packet_uint8(packet, 2); else tls_packet_uint8(packet, context->is_server); if (context->crypto.created == 2) { // aead #ifdef WITH_TLS_13 if ((context->version == TLS_V13) || (context->version == DTLS_V13)) { tls_packet_uint8(packet, TLS_13_AES_GCM_IV_LENGTH); tls_packet_append(packet, context->crypto.ctx_local_mac.local_iv, TLS_13_AES_GCM_IV_LENGTH); tls_packet_append(packet, context->crypto.ctx_remote_mac.remote_iv, TLS_13_AES_GCM_IV_LENGTH); } else { #endif tls_packet_uint8(packet, TLS_AES_GCM_IV_LENGTH); tls_packet_append(packet, context->crypto.ctx_local_mac.local_aead_iv, TLS_AES_GCM_IV_LENGTH); tls_packet_append(packet, context->crypto.ctx_remote_mac.remote_aead_iv, TLS_AES_GCM_IV_LENGTH); #ifdef WITH_TLS_13 } #endif #ifdef TLS_WITH_CHACHA20_POLY1305 } else if (context->crypto.created == 3) { // ChaCha20 tls_packet_uint8(packet, TLS_CHACHA20_IV_LENGTH); tls_packet_append(packet, context->crypto.ctx_local_mac.local_nonce, TLS_CHACHA20_IV_LENGTH); tls_packet_append(packet, context->crypto.ctx_remote_mac.remote_nonce, TLS_CHACHA20_IV_LENGTH); #endif } else { unsigned char iv[TLS_AES_IV_LENGTH]; unsigned long len = TLS_AES_IV_LENGTH; memset(iv, 0, TLS_AES_IV_LENGTH); cbc_getiv(iv, &len, &context->crypto.ctx_local.aes_local); tls_packet_uint8(packet, TLS_AES_IV_LENGTH); tls_packet_append(packet, iv, len); memset(iv, 0, TLS_AES_IV_LENGTH); cbc_getiv(iv, &len, &context->crypto.ctx_remote.aes_remote); tls_packet_append(packet, iv, TLS_AES_IV_LENGTH); } tls_packet_uint8(packet, context->exportable_size); tls_packet_append(packet, context->exportable_keys, context->exportable_size); if (context->crypto.created == 2) { tls_packet_uint8(packet, 0); #ifdef TLS_WITH_CHACHA20_POLY1305 } else if (context->crypto.created == 3) { // ChaCha20 tls_packet_uint8(packet, 0); unsigned int i; for (i = 0; i < 16; i++) tls_packet_uint32(packet, context->crypto.ctx_local.chacha_local.input[i]); for (i = 0; i < 16; i++) tls_packet_uint32(packet, context->crypto.ctx_remote.chacha_remote.input[i]); tls_packet_append(packet, context->crypto.ctx_local.chacha_local.ks, CHACHA_BLOCKLEN); tls_packet_append(packet, context->crypto.ctx_remote.chacha_remote.ks, CHACHA_BLOCKLEN); #endif } else { unsigned char mac_length = (unsigned char)_private_tls_mac_length(context); tls_packet_uint8(packet, mac_length); tls_packet_append(packet, context->crypto.ctx_local_mac.local_mac, mac_length); tls_packet_append(packet, context->crypto.ctx_remote_mac.remote_mac, mac_length); } if (small_version) { tls_packet_uint16(packet, 0); } else { tls_packet_uint16(packet, context->master_key_len); tls_packet_append(packet, context->master_key, context->master_key_len); } uint64_t sequence_number = htonll(context->local_sequence_number); tls_packet_append(packet, (unsigned char *)&sequence_number, sizeof(uint64_t)); sequence_number = htonll(context->remote_sequence_number); tls_packet_append(packet, (unsigned char *)&sequence_number, sizeof(uint64_t)); tls_packet_uint32(packet, context->tls_buffer_len); tls_packet_append(packet, context->tls_buffer, context->tls_buffer_len); tls_packet_uint32(packet, context->message_buffer_len); tls_packet_append(packet, context->message_buffer, context->message_buffer_len); tls_packet_uint32(packet, context->application_buffer_len); tls_packet_append(packet, context->application_buffer, context->application_buffer_len); tls_packet_uint8(packet, context->dtls); if (context->dtls) { tls_packet_uint16(packet, context->dtls_epoch_local); tls_packet_uint16(packet, context->dtls_epoch_remote); } tls_packet_update(packet); unsigned int size = packet->len; if ((buffer) && (buf_len)) { if (size > buf_len) { tls_destroy_packet(packet); DEBUG_PRINT("EXPORT BUFFER TO SMALL\n"); return (int)buf_len - (int)size; } memcpy(buffer, packet->buf, size); } tls_destroy_packet(packet); return size; } struct TLSContext *tls_import_context(const unsigned char *buffer, unsigned int buf_len) { if ((!buffer) || (buf_len < 64) || (buffer[0] != TLS_SERIALIZED_OBJECT) || (buffer[5] != 0x01)) { DEBUG_PRINT("CANNOT IMPORT CONTEXT BUFFER\n"); return NULL; } // create a context object struct TLSContext *context = tls_create_context(0, TLS_V12); if (context) { unsigned char temp[0xFF]; context->version = ntohs(*(unsigned short *)&buffer[1]); unsigned short length = ntohs(*(unsigned short *)&buffer[3]); if (length != buf_len - 5) { DEBUG_PRINT("INVALID IMPORT BUFFER SIZE\n"); tls_destroy_context(context); return NULL; } context->connection_status = buffer[6]; context->cipher = ntohs(*(unsigned short *)&buffer[7]); unsigned char server = buffer[9]; if (server == 2) { context->is_server = 1; context->is_child = 1; } else context->is_server = server; unsigned char local_iv[TLS_AES_IV_LENGTH]; unsigned char remote_iv[TLS_AES_IV_LENGTH]; unsigned char iv_len = buffer[10]; if (iv_len > TLS_AES_IV_LENGTH) { DEBUG_PRINT("INVALID IV LENGTH\n"); tls_destroy_context(context); return NULL; } // get the initialization vectors int buf_pos = 11; memcpy(local_iv, &buffer[buf_pos], iv_len); buf_pos += iv_len; memcpy(remote_iv, &buffer[buf_pos], iv_len); buf_pos += iv_len; unsigned char key_lengths = buffer[buf_pos++]; TLS_IMPORT_CHECK_SIZE(buf_pos, key_lengths, buf_len) memcpy(temp, &buffer[buf_pos], key_lengths); buf_pos += key_lengths; #ifdef TLS_REEXPORTABLE context->exportable = 1; context->exportable_keys = (unsigned char *)TLS_MALLOC(key_lengths); memcpy(context->exportable_keys, temp, key_lengths); context->exportable_size = key_lengths; #else context->exportable = 0; #endif int is_aead = _private_tls_is_aead(context); #ifdef TLS_WITH_CHACHA20_POLY1305 if (is_aead == 2) { // ChaCha20 if (iv_len > TLS_CHACHA20_IV_LENGTH) iv_len = TLS_CHACHA20_IV_LENGTH; memcpy(context->crypto.ctx_local_mac.local_nonce, local_iv, iv_len); memcpy(context->crypto.ctx_remote_mac.remote_nonce, remote_iv, iv_len); } else #endif if (is_aead) { #ifdef WITH_TLS_13 if ((context->version == TLS_V13) || (context->version == DTLS_V13)) { if (iv_len > TLS_13_AES_GCM_IV_LENGTH) iv_len = TLS_13_AES_GCM_IV_LENGTH; memcpy(context->crypto.ctx_local_mac.local_iv, local_iv, iv_len); memcpy(context->crypto.ctx_remote_mac.remote_iv, remote_iv, iv_len); } else { #endif if (iv_len > TLS_AES_GCM_IV_LENGTH) iv_len = TLS_AES_GCM_IV_LENGTH; memcpy(context->crypto.ctx_local_mac.local_aead_iv, local_iv, iv_len); memcpy(context->crypto.ctx_remote_mac.remote_aead_iv, remote_iv, iv_len); #ifdef WITH_TLS_13 } #endif } if (context->is_server) { if (_private_tls_crypto_create(context, key_lengths / 2, temp, local_iv, temp + key_lengths / 2, remote_iv)) { DEBUG_PRINT("ERROR CREATING KEY CONTEXT\n"); tls_destroy_context(context); return NULL; } } else { if (_private_tls_crypto_create(context, key_lengths / 2, temp + key_lengths / 2, remote_iv, temp, local_iv)) { DEBUG_PRINT("ERROR CREATING KEY CONTEXT (CLIENT)\n"); tls_destroy_context(context); return NULL; } } memset(temp, 0, sizeof(temp)); unsigned char mac_length = buffer[buf_pos++]; if (mac_length > TLS_MAX_MAC_SIZE) { DEBUG_PRINT("INVALID MAC SIZE\n"); tls_destroy_context(context); return NULL; } if (mac_length) { TLS_IMPORT_CHECK_SIZE(buf_pos, mac_length, buf_len) memcpy(context->crypto.ctx_local_mac.local_mac, &buffer[buf_pos], mac_length); buf_pos += mac_length; TLS_IMPORT_CHECK_SIZE(buf_pos, mac_length, buf_len) memcpy(context->crypto.ctx_remote_mac.remote_mac, &buffer[buf_pos], mac_length); buf_pos += mac_length; } else #ifdef TLS_WITH_CHACHA20_POLY1305 if (is_aead == 2) { // ChaCha20 unsigned int i; TLS_IMPORT_CHECK_SIZE(buf_pos, 128 + CHACHA_BLOCKLEN * 2, buf_len) for (i = 0; i < 16; i++) { context->crypto.ctx_local.chacha_local.input[i] = ntohl(*(unsigned int *)(buffer + buf_pos)); buf_pos += sizeof(unsigned int); } for (i = 0; i < 16; i++) { context->crypto.ctx_remote.chacha_remote.input[i] = ntohl(*(unsigned int *)(buffer + buf_pos)); buf_pos += sizeof(unsigned int); } memcpy(context->crypto.ctx_local.chacha_local.ks, buffer + buf_pos, CHACHA_BLOCKLEN); buf_pos += CHACHA_BLOCKLEN; memcpy(context->crypto.ctx_remote.chacha_remote.ks, buffer + buf_pos, CHACHA_BLOCKLEN); buf_pos += CHACHA_BLOCKLEN; } #endif TLS_IMPORT_CHECK_SIZE(buf_pos, 2, buf_len) unsigned short master_key_len = ntohs(*(unsigned short *)(buffer + buf_pos)); buf_pos += 2; if (master_key_len) { TLS_IMPORT_CHECK_SIZE(buf_pos, master_key_len, buf_len) context->master_key = (unsigned char *)TLS_MALLOC(master_key_len); if (context->master_key) { memcpy(context->master_key, &buffer[buf_pos], master_key_len); context->master_key_len = master_key_len; } buf_pos += master_key_len; } TLS_IMPORT_CHECK_SIZE(buf_pos, 16, buf_len) context->local_sequence_number = ntohll(*(uint64_t *)&buffer[buf_pos]); buf_pos += 8; context->remote_sequence_number = ntohll(*(uint64_t *)&buffer[buf_pos]); buf_pos += 8; TLS_IMPORT_CHECK_SIZE(buf_pos, 4, buf_len) unsigned int tls_buffer_len = ntohl(*(unsigned int *)&buffer[buf_pos]); buf_pos += 4; TLS_IMPORT_CHECK_SIZE(buf_pos, tls_buffer_len, buf_len) if (tls_buffer_len) { context->tls_buffer = (unsigned char *)TLS_MALLOC(tls_buffer_len); if (context->tls_buffer) { memcpy(context->tls_buffer, &buffer[buf_pos], tls_buffer_len); context->tls_buffer_len = tls_buffer_len; } buf_pos += tls_buffer_len; } TLS_IMPORT_CHECK_SIZE(buf_pos, 4, buf_len) unsigned int message_buffer_len = ntohl(*(unsigned int *)&buffer[buf_pos]); buf_pos += 4; TLS_IMPORT_CHECK_SIZE(buf_pos, message_buffer_len, buf_len) if (message_buffer_len) { context->message_buffer = (unsigned char *)TLS_MALLOC(message_buffer_len); if (context->message_buffer) { memcpy(context->message_buffer, &buffer[buf_pos], message_buffer_len); context->message_buffer_len = message_buffer_len; } buf_pos += message_buffer_len; } TLS_IMPORT_CHECK_SIZE(buf_pos, 4, buf_len) unsigned int application_buffer_len = ntohl(*(unsigned int *)&buffer[buf_pos]); buf_pos += 4; context->cipher_spec_set = 1; TLS_IMPORT_CHECK_SIZE(buf_pos, application_buffer_len, buf_len) if (application_buffer_len) { context->application_buffer = (unsigned char *)TLS_MALLOC(application_buffer_len); if (context->application_buffer) { memcpy(context->application_buffer, &buffer[buf_pos], application_buffer_len); context->application_buffer_len = application_buffer_len; } buf_pos += application_buffer_len; } TLS_IMPORT_CHECK_SIZE(buf_pos, 1, buf_len) context->dtls = buffer[buf_pos]; buf_pos++; if (context->dtls) { TLS_IMPORT_CHECK_SIZE(buf_pos, 4, buf_len) context->dtls_epoch_local = ntohs(*(unsigned short *)&buffer[buf_pos]); buf_pos += 2; context->dtls_epoch_remote = ntohs(*(unsigned short *)&buffer[buf_pos]); } } return context; } int tls_is_broken(struct TLSContext *context) { if ((!context) || (context->critical_error)) return 1; return 0; } int tls_request_client_certificate(struct TLSContext *context) { if ((!context) || (!context->is_server)) return 0; context->request_client_certificate = 1; return 1; } int tls_client_verified(struct TLSContext *context) { if ((!context) || (context->critical_error)) return 0; return (context->client_verified == 1); } const char *tls_sni(struct TLSContext *context) { if (!context) return NULL; return context->sni; } int tls_sni_set(struct TLSContext *context, const char *sni) { if ((!context) || (context->is_server) || (context->critical_error) || (context->connection_status != 0)) return 0; TLS_FREE(context->sni); context->sni = NULL; if (sni) { size_t len = strlen(sni); if (len > 0) { context->sni = (char *)TLS_MALLOC(len + 1); if (context->sni) { context->sni[len] = 0; memcpy(context->sni, sni, len); return 1; } } } return 0; } int tls_load_root_certificates(struct TLSContext *context, const unsigned char *pem_buffer, int pem_size) { if (!context) return TLS_GENERIC_ERROR; unsigned int len; int idx = 0; do { unsigned char *data = tls_pem_decode(pem_buffer, pem_size, idx++, &len); if ((!data) || (!len)) break; struct TLSCertificate *cert = asn1_parse(NULL, data, len, 0); if (cert) { if ((cert->version == 2) #ifdef TLS_X509_V1_SUPPORT || (cert->version == 0) #endif ) { if (cert->priv) { DEBUG_PRINT("WARNING - parse error (private key encountered in certificate)\n"); TLS_FREE(cert->priv); cert->priv = NULL; cert->priv_len = 0; } context->root_certificates = (struct TLSCertificate **)TLS_REALLOC(context->root_certificates, (context->root_count + 1) * sizeof(struct TLSCertificate *)); if (!context->root_certificates) { context->root_count = 0; return TLS_GENERIC_ERROR; } context->root_certificates[context->root_count] = cert; context->root_count++; DEBUG_PRINT("Loaded certificate: %i\n", (int)context->root_count); } else { DEBUG_PRINT("WARNING - certificate version error (v%i)\n", (int)cert->version); tls_destroy_certificate(cert); } } TLS_FREE(data); } while (1); return context->root_count; } int tls_default_verify(struct TLSContext *context, struct TLSCertificate **certificate_chain, int len) { int i; int err; if (certificate_chain) { for (i = 0; i < len; i++) { struct TLSCertificate *certificate = certificate_chain[i]; // check validity date err = tls_certificate_is_valid(certificate); if (err) return err; } } // check if chain is valid err = tls_certificate_chain_is_valid(certificate_chain, len); if (err) return err; // check certificate subject if ((!context->is_server) && (context->sni) && (len > 0) && (certificate_chain)) { err = tls_certificate_valid_subject(certificate_chain[0], context->sni); if (err) return err; } err = tls_certificate_chain_is_valid_root(context, certificate_chain, len); if (err) return err; DEBUG_PRINT("Certificate OK\n"); return no_error; } int tls_unmake_ktls(struct TLSContext *context, int socket) { #ifdef WITH_KTLS struct tls12_crypto_info_aes_gcm_128 crypto_info; socklen_t crypt_info_size = sizeof(crypto_info); if (getsockopt(socket, SOL_TLS, TLS_TX, &crypto_info, &crypt_info_size)) { DEBUG_PRINT("ERROR IN getsockopt\n"); return TLS_GENERIC_ERROR; } memcpy(crypto_info.rec_seq, &context->local_sequence_number, TLS_CIPHER_AES_GCM_128_REC_SEQ_SIZE); context->local_sequence_number = ntohll(context->local_sequence_number); #ifdef TLS_RX crypt_info_size = sizeof(crypto_info); if (getsockopt(socket, SOL_TLS, TLS_RX, &crypto_info, &crypt_info_size)) { DEBUG_PRINT("ERROR IN getsockopt\n"); return TLS_GENERIC_ERROR; } memcpy(crypto_info.rec_seq, &context->remote_sequence_number, TLS_CIPHER_AES_GCM_128_REC_SEQ_SIZE); context->remote_sequence_number = ntohll(context->remote_sequence_number); #endif return 0; #endif DEBUG_PRINT("TLSe COMPILED WITHOUT kTLS SUPPORT\n"); return TLS_FEATURE_NOT_SUPPORTED; } int tls_make_ktls(struct TLSContext *context, int socket) { if ((!context) || (context->critical_error) || (context->connection_status != 0xFF) || (!context->crypto.created)) { DEBUG_PRINT("CANNOT SWITCH TO kTLS\n"); return TLS_GENERIC_ERROR; } if ((!context->exportable) || (!context->exportable_keys)) { DEBUG_PRINT("KEY MUST BE EXPORTABLE TO BE ABLE TO USE kTLS\n"); return TLS_GENERIC_ERROR; } if ((context->version != TLS_V12) && (context->version != DTLS_V12) && (context->version != TLS_V13) && (context->version != DTLS_V13)) { DEBUG_PRINT("kTLS IS SUPPORTED ONLY FOR TLS >= 1.2 AND DTLS >= 1.2\n"); return TLS_FEATURE_NOT_SUPPORTED; } switch (context->cipher) { case TLS_RSA_WITH_AES_128_GCM_SHA256: case TLS_DHE_RSA_WITH_AES_128_GCM_SHA256: case TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256: case TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256: case TLS_AES_128_GCM_SHA256: break; default: DEBUG_PRINT("CIPHER UNSUPPORTED: kTLS SUPPORTS ONLY AES 128 GCM CIPHERS\n"); return TLS_FEATURE_NOT_SUPPORTED; } #ifdef WITH_KTLS if (context->exportable_size < TLS_CIPHER_AES_GCM_128_KEY_SIZE * 2) { DEBUG_PRINT("INVALID KEY SIZE\n"); return TLS_GENERIC_ERROR; } int err; struct tls12_crypto_info_aes_gcm_128 crypto_info; crypto_info.info.cipher_type = TLS_CIPHER_AES_GCM_128; uint64_t local_sequence_number = htonll(context->local_sequence_number); if ((context->version == TLS_V12) || (context->version == DTLS_V12)) { crypto_info.info.version = TLS_1_2_VERSION; memcpy(crypto_info.iv, &local_sequence_number, TLS_CIPHER_AES_GCM_128_IV_SIZE); memcpy(crypto_info.rec_seq, &local_sequence_number, TLS_CIPHER_AES_GCM_128_REC_SEQ_SIZE); memcpy(crypto_info.key, context->exportable_keys, TLS_CIPHER_AES_GCM_128_KEY_SIZE); memcpy(crypto_info.salt, context->crypto.ctx_local_mac.local_aead_iv, TLS_CIPHER_AES_GCM_128_SALT_SIZE); } else if ((context->version == TLS_V13) || (context->version == DTLS_V13)) { crypto_info.info.version = TLS_1_3_VERSION; memcpy(crypto_info.iv, context->crypto.ctx_local_mac.local_iv + 4, TLS_CIPHER_AES_GCM_128_IV_SIZE); memcpy(crypto_info.rec_seq, &local_sequence_number, TLS_CIPHER_AES_GCM_128_REC_SEQ_SIZE); memcpy(crypto_info.key, context->exportable_keys, TLS_CIPHER_AES_GCM_128_KEY_SIZE); memcpy(crypto_info.salt, context->crypto.ctx_local_mac.local_iv, TLS_CIPHER_AES_GCM_128_SALT_SIZE); } err = setsockopt(socket, SOL_TCP, TCP_ULP, "tls", sizeof("tls")); if (err) return err; #ifdef TLS_RX // kernel 4.17 adds TLS_RX support struct tls12_crypto_info_aes_gcm_128 crypto_info_read; crypto_info_read.info.cipher_type = TLS_CIPHER_AES_GCM_128; uint64_t remote_sequence_number = htonll(context->remote_sequence_number); if ((context->version == TLS_V12) || (context->version == DTLS_V12)) { crypto_info_read.info.version = TLS_1_2_VERSION; memcpy(crypto_info_read.iv, &remote_sequence_number, TLS_CIPHER_AES_GCM_128_IV_SIZE); memcpy(crypto_info_read.rec_seq, &remote_sequence_number, TLS_CIPHER_AES_GCM_128_REC_SEQ_SIZE); memcpy(crypto_info_read.key, context->exportable_keys + TLS_CIPHER_AES_GCM_128_KEY_SIZE, TLS_CIPHER_AES_GCM_128_KEY_SIZE); memcpy(crypto_info_read.salt, context->crypto.ctx_remote_mac.remote_aead_iv, TLS_CIPHER_AES_GCM_128_SALT_SIZE); } else if ((context->version == TLS_V13) || (context->version == DTLS_V13)) { crypto_info_read.info.version = TLS_1_3_VERSION; memcpy(crypto_info_read.iv, context->crypto.ctx_remote_mac.remote_iv + 4, TLS_CIPHER_AES_GCM_128_IV_SIZE); memcpy(crypto_info_read.rec_seq, &remote_sequence_number, TLS_CIPHER_AES_GCM_128_REC_SEQ_SIZE); memcpy(crypto_info_read.key, context->exportable_keys + TLS_CIPHER_AES_GCM_128_KEY_SIZE, TLS_CIPHER_AES_GCM_128_KEY_SIZE); memcpy(crypto_info_read.salt, context->crypto.ctx_remote_mac.remote_iv, TLS_CIPHER_AES_GCM_128_SALT_SIZE); } err = setsockopt(socket, SOL_TLS, TLS_RX, &crypto_info_read, sizeof(crypto_info_read)); if (err) return err; #endif return setsockopt(socket, SOL_TLS, TLS_TX, &crypto_info, sizeof(crypto_info)); #else DEBUG_PRINT("TLSe COMPILED WITHOUT kTLS SUPPORT\n"); return TLS_FEATURE_NOT_SUPPORTED; #endif } #ifdef DEBUG void tls_print_certificate(const char *fname) { unsigned char buf[0xFFFF]; char out_buf[0xFFFF]; int size = _private_tls_read_from_file(fname, buf, 0xFFFF); if (size > 0) { int idx = 0; unsigned int len; do { unsigned char *data; if (buf[0] == '-') { data = tls_pem_decode(buf, size, idx++, &len); } else { data = buf; len = size; } if ((!data) || (!len)) return; struct TLSCertificate *cert = asn1_parse(NULL, data, len, -1); if (data != buf) TLS_FREE(data); if (cert) { fprintf(stderr, "%s", tls_certificate_to_string(cert, out_buf, 0xFFFF)); tls_destroy_certificate(cert); } if (data == buf) break; } while (1); } } #endif int tls_remote_error(struct TLSContext *context) { if (!context) return TLS_GENERIC_ERROR; return context->error_code; } #ifdef SSL_COMPATIBLE_INTERFACE int SSL_library_init() { // dummy function return 1; } void SSL_load_error_strings() { // dummy function } void OpenSSL_add_all_algorithms() { // dummy function } void OpenSSL_add_all_ciphers() { // dummy function } void OpenSSL_add_all_digests() { // dummy function } void EVP_cleanup() { // dummy function } int _tls_ssl_private_send_pending(int client_sock, struct TLSContext *context) { unsigned int out_buffer_len = 0; const unsigned char *out_buffer = tls_get_write_buffer(context, &out_buffer_len); unsigned int out_buffer_index = 0; int send_res = 0; SOCKET_SEND_CALLBACK write_cb = NULL; SSLUserData *ssl_data = (SSLUserData *)context->user_data; if (ssl_data) write_cb = (SOCKET_SEND_CALLBACK)ssl_data->send; while ((out_buffer) && (out_buffer_len > 0)) { int res; if (write_cb) res = write_cb(client_sock, (char *)&out_buffer[out_buffer_index], out_buffer_len, 0); else res = send(client_sock, (char *)&out_buffer[out_buffer_index], out_buffer_len, 0); if (res <= 0) { if ((!write_cb) && (res < 0)) { #ifdef _WIN32 if (WSAGetLastError() == WSAEWOULDBLOCK) { context->tls_buffer_len = out_buffer_len; memmove(context->tls_buffer, out_buffer + out_buffer_index, out_buffer_len); return res; } #else if ((errno == EAGAIN) || (errno == EINTR)) { context->tls_buffer_len = out_buffer_len; memmove(context->tls_buffer, out_buffer + out_buffer_index, out_buffer_len); return res; } #endif } send_res = res; break; } out_buffer_len -= res; out_buffer_index += res; send_res += res; } tls_buffer_clear(context); return send_res; } struct TLSContext *SSL_new(struct TLSContext *context) { return tls_accept(context); } int SSLv3_server_method() { return 1; } int SSLv3_client_method() { return 0; } int SSL_CTX_use_certificate_file(struct TLSContext *context, const char *filename, int dummy) { // max 64k buffer unsigned char buf[0xFFFF]; int size = _private_tls_read_from_file(filename, buf, sizeof(buf)); if (size > 0) return tls_load_certificates(context, buf, size); return size; } int SSL_CTX_use_PrivateKey_file(struct TLSContext *context, const char *filename, int dummy) { unsigned char buf[0xFFFF]; int size = _private_tls_read_from_file(filename, buf, sizeof(buf)); if (size > 0) return tls_load_private_key(context, buf, size); return size; } int SSL_CTX_check_private_key(struct TLSContext *context) { if ((!context) || (((!context->private_key) || (!context->private_key->der_bytes) || (!context->private_key->der_len)) #ifdef TLS_ECDSA_SUPPORTED && ((!context->ec_private_key) || (!context->ec_private_key->der_bytes) || (!context->ec_private_key->der_len)) #endif )) return 0; return 1; } struct TLSContext *SSL_CTX_new(int method) { #ifdef WITH_TLS_13 return tls_create_context(method, TLS_V13); #else return tls_create_context(method, TLS_V12); #endif } void SSL_free(struct TLSContext *context) { if (context) { TLS_FREE(context->user_data); tls_destroy_context(context); } } void SSL_CTX_free(struct TLSContext *context) { SSL_free(context); } int SSL_get_error(struct TLSContext *context, int ret) { if (!context) return TLS_GENERIC_ERROR; return context->critical_error; } int SSL_set_fd(struct TLSContext *context, int socket) { if (!context) return 0; SSLUserData *ssl_data = (SSLUserData *)context->user_data; if (!ssl_data) { ssl_data = (SSLUserData *)TLS_MALLOC(sizeof(SSLUserData)); if (!ssl_data) return TLS_NO_MEMORY; memset(ssl_data, 0, sizeof(SSLUserData)); context->user_data = ssl_data; } ssl_data->fd = socket; return 1; } void *SSL_set_userdata(struct TLSContext *context, void *data) { if (!context) return NULL; SSLUserData *ssl_data = (SSLUserData *)context->user_data; if (!ssl_data) { ssl_data = (SSLUserData *)TLS_MALLOC(sizeof(SSLUserData)); if (!ssl_data) return NULL; memset(ssl_data, 0, sizeof(SSLUserData)); context->user_data = ssl_data; } void *old_data = ssl_data->user_data; ssl_data->user_data = data; return old_data; } void *SSL_userdata(struct TLSContext *context) { if (!context) return NULL; SSLUserData *ssl_data = (SSLUserData *)context->user_data; if (!ssl_data) return NULL; return ssl_data->user_data; } int SSL_CTX_root_ca(struct TLSContext *context, const char *pem_filename) { if (!context) return TLS_GENERIC_ERROR; int count = TLS_GENERIC_ERROR; FILE *f = fopen(pem_filename, "rb"); if (f) { fseek(f, 0, SEEK_END); size_t size = (size_t)ftell(f); fseek(f, 0, SEEK_SET); if (size) { unsigned char *buf = (unsigned char *)TLS_MALLOC(size + 1); if (buf) { buf[size] = 1; if (fread(buf, 1, size, f) == size) { count = tls_load_root_certificates(context, buf, size); if (count > 0) { SSLUserData *ssl_data = (SSLUserData *)context->user_data; if (!ssl_data) { ssl_data = (SSLUserData *)TLS_MALLOC(sizeof(SSLUserData)); if (!ssl_data) { fclose(f); return TLS_NO_MEMORY; } memset(ssl_data, 0, sizeof(SSLUserData)); context->user_data = ssl_data; } if (!ssl_data->certificate_verify) ssl_data->certificate_verify = tls_default_verify; } } TLS_FREE(buf); } } fclose(f); } return count; } void SSL_CTX_set_verify(struct TLSContext *context, int mode, tls_validation_function verify_callback) { if (!context) return; SSLUserData *ssl_data = (SSLUserData *)context->user_data; if (!ssl_data) { ssl_data = (SSLUserData *)TLS_MALLOC(sizeof(SSLUserData)); if (!ssl_data) return; memset(ssl_data, 0, sizeof(SSLUserData)); context->user_data = ssl_data; } if (mode == SSL_VERIFY_NONE) ssl_data->certificate_verify = NULL; else ssl_data->certificate_verify = verify_callback; } int _private_tls_safe_read(struct TLSContext *context, void *buffer, int buf_size) { SSLUserData *ssl_data = (SSLUserData *)context->user_data; if ((!ssl_data) || (ssl_data->fd < 0)) return TLS_GENERIC_ERROR; SOCKET_RECV_CALLBACK read_cb = (SOCKET_RECV_CALLBACK)ssl_data->recv; if (read_cb) return read_cb(ssl_data->fd, (char *)buffer, buf_size, 0); return recv(ssl_data->fd, (char *)buffer, buf_size, 0); } int SSL_accept(struct TLSContext *context) { if (!context) return TLS_GENERIC_ERROR; SSLUserData *ssl_data = (SSLUserData *)context->user_data; if ((!ssl_data) || (ssl_data->fd < 0)) return TLS_GENERIC_ERROR; if (tls_established(context)) return 1; unsigned char client_message[0xFFFF]; // accept int read_size = 0; while ((read_size = _private_tls_safe_read(context, (char *)client_message, sizeof(client_message))) > 0) { if (tls_consume_stream(context, client_message, read_size, ssl_data->certificate_verify) >= 0) { int res = _tls_ssl_private_send_pending(ssl_data->fd, context); if (res < 0) return res; } if (tls_established(context)) return 1; } if (read_size <= 0) return TLS_BROKEN_CONNECTION; return 0; } int SSL_connect(struct TLSContext *context) { if (!context) return TLS_GENERIC_ERROR; SSLUserData *ssl_data = (SSLUserData *)context->user_data; if ((!ssl_data) || (ssl_data->fd < 0) || (context->critical_error)) return TLS_GENERIC_ERROR; int res = tls_client_connect(context); if (res < 0) return res; res = _tls_ssl_private_send_pending(ssl_data->fd, context); if (res < 0) return res; int read_size; unsigned char client_message[0xFFFF]; while ((read_size = _private_tls_safe_read(context, (char *)client_message, sizeof(client_message))) > 0) { if (tls_consume_stream(context, client_message, read_size, ssl_data->certificate_verify) >= 0) { res = _tls_ssl_private_send_pending(ssl_data->fd, context); if (res < 0) return res; } if (tls_established(context)) return 1; if (context->critical_error) return TLS_GENERIC_ERROR; } return read_size; } int SSL_shutdown(struct TLSContext *context) { if (!context) return TLS_GENERIC_ERROR; SSLUserData *ssl_data = (SSLUserData *)context->user_data; if ((!ssl_data) || (ssl_data->fd < 0)) return TLS_GENERIC_ERROR; tls_close_notify(context); return 0; } int SSL_write(struct TLSContext *context, const void *buf, unsigned int len) { if (!context) return TLS_GENERIC_ERROR; SSLUserData *ssl_data = (SSLUserData *)context->user_data; if ((!ssl_data) || (ssl_data->fd < 0)) return TLS_GENERIC_ERROR; int written_size = tls_write(context, (const unsigned char *)buf, len); if (written_size > 0) { int res = _tls_ssl_private_send_pending(ssl_data->fd, context); if (res <= 0) return res; } return written_size; } int SSL_read(struct TLSContext *context, void *buf, unsigned int len) { if (!context) return TLS_GENERIC_ERROR; if (context->application_buffer_len) return tls_read(context, (unsigned char *)buf, len); SSLUserData *ssl_data = (SSLUserData *)context->user_data; if ((!ssl_data) || (ssl_data->fd < 0) || (context->critical_error)) return TLS_GENERIC_ERROR; if (tls_established(context) != 1) return TLS_GENERIC_ERROR; unsigned char client_message[0xFFFF]; // accept int read_size; while ((!context->application_buffer_len) && ((read_size = _private_tls_safe_read(context, (char *)client_message, sizeof(client_message))) > 0)) { if (tls_consume_stream(context, client_message, read_size, ssl_data->certificate_verify) > 0) _tls_ssl_private_send_pending(ssl_data->fd, context); if ((context->critical_error) && (!context->application_buffer_len)) return TLS_GENERIC_ERROR; } if ((read_size <= 0) && (!context->application_buffer_len)) return read_size; return tls_read(context, (unsigned char *)buf, len); } int SSL_pending(struct TLSContext *context) { if (!context) return TLS_GENERIC_ERROR; return context->application_buffer_len; } int SSL_set_io(struct TLSContext *context, void *recv_cb, void *send_cb) { if (!context) return TLS_GENERIC_ERROR; SSLUserData *ssl_data = (SSLUserData *)context->user_data; if (!ssl_data) { ssl_data = (SSLUserData *)TLS_MALLOC(sizeof(SSLUserData)); if (!ssl_data) return TLS_NO_MEMORY; memset(ssl_data, 0, sizeof(SSLUserData)); context->user_data = ssl_data; } ssl_data->recv = recv_cb; ssl_data->send = send_cb; return 0; } #endif // SSL_COMPATIBLE_INTERFACE #ifdef TLS_SRTP struct SRTPContext { symmetric_CTR aes; unsigned int salt[4]; unsigned char mac[TLS_SHA1_MAC_SIZE]; unsigned int tag_size; unsigned int roc; unsigned short seq; unsigned char mode; unsigned char auth_mode; }; struct SRTPContext *srtp_init(unsigned char mode, unsigned char auth_mode) { struct SRTPContext *context = NULL; tls_init(); switch (mode) { case SRTP_NULL: break; case SRTP_AES_CM: break; default: return NULL; } switch (auth_mode) { case SRTP_AUTH_NULL: break; case SRTP_AUTH_HMAC_SHA1: break; default: return NULL; } context = (struct SRTPContext *)TLS_MALLOC(sizeof(struct SRTPContext)); if (context) { memset(context, 0, sizeof(struct SRTPContext)); context->mode = mode; context->auth_mode = auth_mode; } return context; } static int _private_tls_srtp_key_derive(const void *key, int keylen, const void *salt, unsigned char label, void *out, int outlen) { unsigned char iv[16]; memcpy(iv, salt, 14); iv[14] = iv[15] = 0; void *in = TLS_MALLOC(outlen); if (!in) return TLS_GENERIC_ERROR; memset(in, 0, outlen); iv[7] ^= label; symmetric_CTR aes; if (ctr_start(find_cipher("aes"), iv, (const unsigned char *)key, keylen, 0, CTR_COUNTER_BIG_ENDIAN, &aes)) return TLS_GENERIC_ERROR; ctr_encrypt((unsigned char *)in, (unsigned char *)out, outlen, &aes); TLS_FREE(in); ctr_done(&aes); return 0; } int srtp_key(struct SRTPContext *context, const void *key, int keylen, const void *salt, int saltlen, int tag_bits) { if (!context) return TLS_GENERIC_ERROR; if (context->mode == SRTP_AES_CM) { if ((saltlen < 14) || (keylen < 16)) return TLS_GENERIC_ERROR; // key unsigned char key_buf[16]; unsigned char iv[16]; memset(iv, 0, sizeof(iv)); if (_private_tls_srtp_key_derive(key, keylen, salt, 0, key_buf, sizeof(key_buf))) return TLS_GENERIC_ERROR; DEBUG_DUMP_HEX_LABEL("KEY", key_buf, 16) if (_private_tls_srtp_key_derive(key, keylen, salt, 1, context->mac, 20)) return TLS_GENERIC_ERROR; DEBUG_DUMP_HEX_LABEL("AUTH", context->mac, 20) memset(context->salt, 0, sizeof(context->salt)); if (_private_tls_srtp_key_derive(key, keylen, salt, 2, context->salt, 14)) return TLS_GENERIC_ERROR; DEBUG_DUMP_HEX_LABEL("SALT", ((unsigned char *)context->salt), 14) if (ctr_start(find_cipher("aes"), iv, key_buf, sizeof(key_buf), 0, CTR_COUNTER_BIG_ENDIAN, &context->aes)) return TLS_GENERIC_ERROR; } if (context->auth_mode) context->tag_size = tag_bits / 8; return 0; } int srtp_inline(struct SRTPContext *context, const char *b64, int tag_bits) { char out_buffer[1024]; if (!b64) return TLS_GENERIC_ERROR; int len = strlen(b64); if (len >= sizeof(out_buffer)) len = sizeof(out_buffer); int size = _private_b64_decode(b64, len, (unsigned char *)out_buffer); if (size <= 0) return TLS_GENERIC_ERROR; switch (context->mode) { case SRTP_AES_CM: if (size < 30) return TLS_BROKEN_PACKET; return srtp_key(context, out_buffer, 16, out_buffer + 16, 14, tag_bits); } return TLS_GENERIC_ERROR; } int srtp_encrypt(struct SRTPContext *context, const unsigned char *pt_header, int pt_len, const unsigned char *payload, unsigned int payload_len, unsigned char *out, int *out_buffer_len) { if ((!context) || (!out) || (!out_buffer_len) || (*out_buffer_len < payload_len)) return TLS_GENERIC_ERROR; int out_len = payload_len; unsigned short seq = 0; unsigned int roc = context->roc; unsigned int ssrc = 0; if ((pt_header) && (pt_len >= 12)) { seq = ntohs(*((unsigned short *)&pt_header[2])); ssrc = ntohl(*((unsigned long *)&pt_header[8])); } if (seq < context->seq) roc++; unsigned int roc_be = htonl(roc); if (context->mode) { if (*out_buffer_len < out_len) return TLS_NO_MEMORY; unsigned int counter[4]; counter[0] = context->salt[0]; counter[1] = context->salt[1] ^ htonl (ssrc); counter[2] = context->salt[2] ^ roc_be; counter[3] = context->salt[3] ^ htonl (seq << 16); ctr_setiv((unsigned char *)&counter, 16, &context->aes); if (ctr_encrypt(payload, out, payload_len, &context->aes)) return TLS_GENERIC_ERROR; } else { memcpy(out, payload, payload_len); } *out_buffer_len = out_len; if (context->auth_mode == SRTP_AUTH_HMAC_SHA1) { unsigned char digest_out[TLS_SHA1_MAC_SIZE]; unsigned long dlen = TLS_SHA1_MAC_SIZE; hmac_state hmac; int err = hmac_init(&hmac, find_hash("sha1"), context->mac, 20); if (!err) { if (pt_len) err = hmac_process(&hmac, pt_header, pt_len); if (out_len) err = hmac_process(&hmac, out, payload_len); err = hmac_process(&hmac, (unsigned char *)&roc_be, 4); if (!err) err = hmac_done(&hmac, digest_out, &dlen); } if (err) return TLS_GENERIC_ERROR; if (dlen > context->tag_size) dlen = context->tag_size; *out_buffer_len += dlen; memcpy(out + out_len, digest_out, dlen); } context->roc = roc; context->seq = seq; return 0; } int srtp_decrypt(struct SRTPContext *context, const unsigned char *pt_header, int pt_len, const unsigned char *payload, unsigned int payload_len, unsigned char *out, int *out_buffer_len) { if ((!context) || (!out) || (!out_buffer_len) || (*out_buffer_len < payload_len) || (payload_len < context->tag_size) || (!pt_header) || (pt_len < 12)) return TLS_GENERIC_ERROR; int out_len = payload_len; unsigned short seq = ntohs(*((unsigned short *)&pt_header[2])); unsigned int roc = context->roc; unsigned int ssrc = ntohl(*((unsigned long *)&pt_header[8])); if (seq < context->seq) roc++; unsigned int roc_be = htonl(roc); if (context->mode) { unsigned int counter[4]; counter[0] = context->salt[0]; counter[1] = context->salt[1] ^ htonl (ssrc); counter[2] = context->salt[2] ^ roc_be; counter[3] = context->salt[3] ^ htonl (seq << 16); ctr_setiv((unsigned char *)&counter, 16, &context->aes); if (ctr_decrypt(payload, out, payload_len - context->tag_size, &context->aes)) return TLS_GENERIC_ERROR; if (context->auth_mode == SRTP_AUTH_HMAC_SHA1) { unsigned char digest_out[TLS_SHA1_MAC_SIZE]; unsigned long dlen = TLS_SHA1_MAC_SIZE; hmac_state hmac; int err = hmac_init(&hmac, find_hash("sha1"), context->mac, 20); if (!err) { if (pt_len) err = hmac_process(&hmac, pt_header, pt_len); if (out_len) err = hmac_process(&hmac, payload, payload_len - context->tag_size); err = hmac_process(&hmac, (unsigned char *)&roc_be, 4); if (!err) err = hmac_done(&hmac, digest_out, &dlen); } if (err) return TLS_GENERIC_ERROR; if (dlen > context->tag_size) dlen = context->tag_size; if (memcmp(digest_out, payload + payload_len - context->tag_size, dlen)) return TLS_INTEGRITY_FAILED; } } else { memcpy(out, payload, payload_len - context->tag_size); } context->seq = seq; context->roc = roc; *out_buffer_len = payload_len - context->tag_size; return 0; } void srtp_destroy(struct SRTPContext *context) { if (context) { if (context->mode) ctr_done(&context->aes); TLS_FREE(context); } } #endif // TLS_SRTP #endif // TLSE_C /* ------------------------------------------------------------------------------ END tlse.c ------------------------------------------------------------------------------ */ #endif /* HTTPS_NO_TLSE */ /* ------------------------------------------------------------------------------ HTTPS IMPLEMENTATION ------------------------------------------------------------------------------ */ typedef struct { /* keep this at the top!*/ https_t https; /* because https_internal_t* can be cast to https_t*. */ void* memctx; struct TLSContext * tls_context; HTTPS_SOCKET socket; int connect_pending; int handshake_pending; int request_sent; char address[ 256 ]; char request_header[ 256 ]; char* request_header_large; void* request_data; size_t request_data_size; char reason_phrase[ 1024 ]; char content_type[ 256 ]; size_t data_size; size_t data_capacity; void* data; } https_internal_t; static int https_internal_parse_url( char const* url, char* address, size_t address_capacity, char* port, size_t port_capacity, char const** resource ) { // make sure url starts with https:// if( strncmp( url, "https://", 8 ) != 0 ) return 0; url += 8; // skip https:// part of url size_t url_len = strlen( url ); // find end of address part of url char const* port_pos = strchr( url, ':' ); char const* rsrc_pos = strchr( url, '/' ); char const* address_end = port_pos ? port_pos : rsrc_pos; if( port_pos && port_pos < address_end ) address_end = port_pos; if( rsrc_pos && rsrc_pos < address_end ) address_end = rsrc_pos; if( !address_end ) address_end = url + url_len; // extract address size_t address_len = (size_t)( address_end - url ); if( address_len >= address_capacity ) return 0; memcpy( address, url, address_len ); address[ address_len ] = 0; // check if there's a port defined char const* port_end = address_end; if( *address_end == ':' ) { ++address_end; port_end = strchr( address_end, '/' ); if( !port_end ) port_end = address_end + strlen( address_end ); size_t port_len = (size_t)( port_end - address_end ); if( port_len >= port_capacity ) return 0; memcpy( port, address_end, port_len ); port[ port_len ] = 0; } else { // use default port number 443 if( port_capacity <= 3 ) return 0; strcpy( port, "443" ); } *resource = port_end; return 1; } HTTPS_SOCKET https_internal_connect( char const* address, char const* port ) { // set up hints for getaddrinfo struct addrinfo hints; memset( &hints, 0, sizeof( hints ) ); hints.ai_family = AF_UNSPEC; // the Internet Protocol version 4 (IPv4) address family. hints.ai_flags = AI_PASSIVE; hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = IPPROTO_TCP; // Use Transmission Control Protocol (TCP). // resolve the server address and port struct addrinfo* addri = 0; int error = getaddrinfo( address, port, &hints, &addri ) ; if( error != 0 ) return HTTPS_INVALID_SOCKET; // create the socket HTTPS_SOCKET sock = socket( addri->ai_family, addri->ai_socktype, addri->ai_protocol ); if( sock == -1) { freeaddrinfo( addri ); return HTTPS_INVALID_SOCKET; } // set socket to nonblocking mode /* u_long nonblocking = 1; #ifdef _WIN32 int res = ioctlsocket( sock, FIONBIO, &nonblocking ); #else int flags = fcntl( sock, F_GETFL, 0 ); int res = fcntl( sock, F_SETFL, flags | O_NONBLOCK ); #endif if( res == -1 ) { freeaddrinfo( addri ); #ifdef _WIN32 closesocket( sock ); #else close( sock ); #endif return HTTPS_INVALID_SOCKET; }*/ // connect to server if( connect( sock, addri->ai_addr, (int)addri->ai_addrlen ) == -1 ) { #ifdef _WIN32 if( WSAGetLastError() != WSAEWOULDBLOCK && WSAGetLastError() != WSAEINPROGRESS ) { freeaddrinfo( addri ); closesocket( sock ); return HTTPS_INVALID_SOCKET; } #else if( errno != EWOULDBLOCK && errno != EINPROGRESS && errno != EAGAIN ) { freeaddrinfo( addri ); close( sock ); return HTTPS_INVALID_SOCKET; } #endif } freeaddrinfo( addri ); return sock; } static https_internal_t* https_internal_create( size_t request_data_size, void* memctx ) { https_internal_t* internal = (https_internal_t*) HTTPS_MALLOC( memctx, sizeof( https_internal_t ) + request_data_size ); internal->https.status = HTTPS_STATUS_PENDING; internal->https.status_code = 0; internal->https.response_size = 0; internal->https.response_data = NULL; internal->memctx = memctx; internal->connect_pending = 1; internal->request_sent = 0; strcpy( internal->reason_phrase, "" ); internal->https.reason_phrase = internal->reason_phrase; strcpy( internal->content_type, "" ); internal->https.content_type = internal->content_type; internal->data_size = 0; internal->data_capacity = 64 * 1024; internal->data = HTTPS_MALLOC( memctx, internal->data_capacity ); internal->request_data = NULL; internal->request_data_size = 0; internal->tls_context = tls_create_context( 0, TLS_V12 ); return internal; } https_t* https_get( char const* url, void* memctx ) { #if !HTTPS_NO_THREADING //< @r-lyeh, add option to disable threading #ifdef _WIN32 #if (defined(_MSC_VER) && _MSC_VER < 1600) typedef BOOL (WINAPI *InitOnceExecuteOnce_t)( PRTL_RUN_ONCE, BOOL (WINAPI*)( PRTL_RUN_ONCE, PVOID, PVOID* ), PVOID, LPVOID* ); HINSTANCE lib = LoadLibraryA( "kernel32.dll" ); InitOnceExecuteOnce_t InitOnceExecuteOnce_func = (InitOnceExecuteOnce_t) (uintptr_t) GetProcAddress( lib, "InitOnceExecuteOnce" ); ASSERT(InitOnceExecuteOnce_func); InitOnceExecuteOnce_func( &https_internal_one_time_init_instance, https_internal_one_time_init, NULL, NULL ); FreeLibrary( lib ); #else InitOnceExecuteOnce( &https_internal_one_time_init_instance, (void*)https_internal_one_time_init, NULL, NULL ); #endif #else pthread_once( &https_internal_one_time_init_instance, https_internal_one_time_init); #endif if( memctx ) https_internal_memctx( memctx ); #endif char address[ 256 ]; char port[ 16 ]; char const* resource; if( https_internal_parse_url( url, address, sizeof( address ), port, sizeof( port ), &resource ) == 0 ) return NULL; HTTPS_SOCKET socket = https_internal_connect( address, port ); if( socket == HTTPS_INVALID_SOCKET ) return NULL; https_internal_t* internal = https_internal_create( 0, memctx ); internal->socket = socket; char* request_header; size_t request_header_len = 256 + strlen( resource ) + strlen( address ) + strlen( port ); if( request_header_len < sizeof( internal->request_header ) ) { internal->request_header_large = NULL; request_header = internal->request_header; } else { internal->request_header_large = (char*) HTTPS_MALLOC( memctx, request_header_len + 1 ); request_header = internal->request_header_large; } sprintf( request_header, "GET %s HTTP/1.0\r\nHost: %s:%s\r\n\r\n", resource, address, port ); return &internal->https; } https_t* https_post( char const* url, void const* data, size_t size, void* memctx ) { #if !HTTPS_NO_THREADING //< @r-lyeh, add option to disable threading #ifdef _WIN32 #if (defined(_MSC_VER) && _MSC_VER < 1600) typedef BOOL (WINAPI *InitOnceExecuteOnce_t)( PRTL_RUN_ONCE, BOOL (WINAPI*)( PRTL_RUN_ONCE, PVOID, PVOID* ), PVOID, LPVOID* ); HINSTANCE lib = LoadLibraryA( "kernel32.dll" ); InitOnceExecuteOnce_t InitOnceExecuteOnce_func = (InitOnceExecuteOnce_t) (uintptr_t) GetProcAddress( lib, "InitOnceExecuteOnce" ); ASSERT(InitOnceExecuteOnce_func); InitOnceExecuteOnce_func( &https_internal_one_time_init_instance, https_internal_one_time_init, NULL, NULL ); FreeLibrary( lib ); #else InitOnceExecuteOnce( &https_internal_one_time_init_instance, (void*)https_internal_one_time_init, NULL, NULL ); #endif #else pthread_once( &https_internal_one_time_init_instance, https_internal_one_time_init); #endif if( memctx ) https_internal_memctx( memctx ); #endif char address[ 256 ]; char port[ 16 ]; char const* resource; if( https_internal_parse_url( url, address, sizeof( address ), port, sizeof( port ), &resource ) == 0 ) return NULL; HTTPS_SOCKET socket = https_internal_connect( address, port ); if( socket == HTTPS_INVALID_SOCKET ) return NULL; https_internal_t* internal = https_internal_create( size, memctx ); internal->socket = socket; char* request_header; size_t request_header_len = 256 + strlen( resource ) + strlen( address ) + strlen( port ); if( request_header_len < sizeof( internal->request_header ) ) { internal->request_header_large = NULL; request_header = internal->request_header; } else { internal->request_header_large = (char*) HTTPS_MALLOC( memctx, request_header_len + 1 ); request_header = internal->request_header_large; } sprintf( request_header, "POST %s HTTP/1.0\r\nHost: %s:%s\r\nContent-Length: %d\r\n\r\n", resource, address, port, (int) size ); internal->request_data_size = size; internal->request_data = ( internal + 1 ); memcpy( internal->request_data, data, size ); return &internal->https; } static int https_internal_send_pending( https_internal_t* internal ) { unsigned int send_size = 0; unsigned char const* send_buffer = tls_get_write_buffer( internal->tls_context, &send_size ); int res = 0; while( send_size > 0 ) { int bytes_sent = send( internal->socket, (char const*) send_buffer, send_size, 0 ); if( bytes_sent <= 0) { res = bytes_sent; break; } send_buffer += bytes_sent; send_size -= bytes_sent; res += bytes_sent; } tls_buffer_clear( internal->tls_context ); return res; } https_status_t https_process( https_t* https ) { https_internal_t* internal = (https_internal_t*) https; if( https->status > HTTPS_STATUS_COMPLETED ) return https->status; // == HTTPS_STATUS_FAILED ) //< @r-lyeh if( internal->connect_pending ) { fd_set sockets_to_check; FD_ZERO( &sockets_to_check ); #pragma warning( push ) #pragma warning( disable: 4548 ) // expression before comma has no effect; expected expression with side-effect FD_SET( internal->socket, &sockets_to_check ); #pragma warning( pop ) struct timeval timeout; timeout.tv_sec = 0; timeout.tv_usec = 0; // check if socket is ready for send if( select( (int)( internal->socket + 1 ), NULL, &sockets_to_check, NULL, &timeout ) == 1 ) { int opt = -1; socklen_t len = sizeof( opt ); if( getsockopt( internal->socket, SOL_SOCKET, SO_ERROR, (char*)( &opt ), &len) >= 0 && opt == 0 ) { internal->connect_pending = 0; // if it is, we're connected // do tls handshake tls_client_connect( internal->tls_context ); int res = https_internal_send_pending( internal ); if( res == -1 ) { https->status = __LINE__; // HTTPS_STATUS_FAILED; //< @r-lyeh return https->status; } internal->handshake_pending = 1; } } } if( internal->connect_pending ) return https->status; if( !internal->handshake_pending && !internal->request_sent ) { char const* request_header = internal->request_header_large ? internal->request_header_large : internal->request_header; tls_write( internal->tls_context, (unsigned char*) request_header, (int) strlen( request_header ) ); if( https_internal_send_pending( internal ) == -1 ) { https->status = __LINE__; // HTTPS_STATUS_FAILED; //< @r-lyeh return https->status; } if( internal->request_data_size ) { tls_write( internal->tls_context, (unsigned char*) internal->request_data, (int) internal->request_data_size ); int res = https_internal_send_pending( internal ); if( res == -1 ) { https->status = __LINE__; // HTTPS_STATUS_FAILED; //< @r-lyeh return https->status; } } internal->request_sent = 1; return https->status; } // check if socket is ready for recv fd_set sockets_to_check; FD_ZERO( &sockets_to_check ); #pragma warning( push ) #pragma warning( disable: 4548 ) // expression before comma has no effect; expected expression with side-effect FD_SET( internal->socket, &sockets_to_check ); #pragma warning( pop ) struct timeval timeout; while( (timeout.tv_sec = 1, timeout.tv_usec = 0), select( (int)( internal->socket + 1 ), &sockets_to_check, NULL, NULL, &timeout ) == 1 ) { enum { buflen = 4 * 1024 }; char buffer[ buflen ]; int size = recv( internal->socket, buffer, buflen, 0 ); if( size == -1 ) { https->status = __LINE__; // HTTPS_STATUS_FAILED; //< @r-lyeh return https->status; } #if is(tcc) && is(win32) // hexdump(buffer, size); //< @r-lyeh // printf(">+=%d,%d/%d\n", size,internal->data_size,internal->data_capacity); #endif tls_consume_stream( internal->tls_context, (unsigned char*) buffer, size, 0 ); int res = https_internal_send_pending( internal ); if( res == -1 ) { https->status = __LINE__; // HTTPS_STATUS_FAILED; //< @r-lyeh return https->status; } if( internal->handshake_pending ) { if( tls_established( internal->tls_context ) ) internal->handshake_pending = 0; else return https->status; continue; } if( size > 0 ) { size_t min_size = internal->data_size + size + 1; if( internal->data_capacity < min_size ) { internal->data_capacity *= 2; if( internal->data_capacity < min_size ) internal->data_capacity = min_size; void* new_data = HTTPS_MALLOC( internal->memctx, internal->data_capacity ); memcpy( new_data, internal->data, internal->data_size ); HTTPS_FREE( internal->memctx, internal->data ); internal->data = new_data; } int read_size = tls_read( internal->tls_context, (unsigned char*)( ( (uintptr_t) internal->data ) + internal->data_size ), size); internal->data_size += read_size; } else if( size == 0 ) { char const* status_line = (char const*) internal->data; int header_size = 0; char const* header_end = strstr( status_line, "\r\n\r\n" ); if( header_end ) { header_end += 4; header_size = (int)( header_end - status_line ); } else { https->status = __LINE__; // HTTPS_STATUS_FAILED; //< @r-lyeh return https->status; } char const* content_length = strstr( status_line, "Content-Length: " ); if( content_length && content_length < header_end ) { content_length += strlen( "Content-Length: " ); char const* content_length_end = strstr( content_length, "\r\n" ); if( content_length_end ) { char content_length_extracted[ 32 ]; int len = content_length_end - content_length; memcpy( content_length_extracted, content_length, len ); content_length_extracted[ len ] = '\0'; int content_length_num = atoi( content_length_extracted ); if( content_length_num > internal->data_size ) { https->status = __LINE__; // HTTPS_STATUS_FAILED; //< @r-lyeh return https->status; } } } // skip https version status_line = strchr( status_line, ' ' ); if( !status_line ) { https->status = __LINE__; // HTTPS_STATUS_FAILED; //< @r-lyeh return https->status; } ++status_line; // extract status code char status_code[ 16 ]; char const* status_code_end = strchr( status_line, ' ' ); if( !status_code_end ) { https->status = __LINE__; // HTTPS_STATUS_FAILED; //< @r-lyeh return https->status; } memcpy( status_code, status_line, (size_t)( status_code_end - status_line ) ); status_code[ status_code_end - status_line ] = 0; status_line = status_code_end + 1; https->status_code = atoi( status_code ); // extract reason phrase char const* reason_phrase_end = strstr( status_line, "\r\n" ); if( !reason_phrase_end ) { https->status = __LINE__; // HTTPS_STATUS_FAILED; //< @r-lyeh return https->status; } size_t reason_phrase_len = (size_t)( reason_phrase_end - status_line ); if( reason_phrase_len >= sizeof( internal->reason_phrase ) ) reason_phrase_len = sizeof( internal->reason_phrase ) - 1; memcpy( internal->reason_phrase, status_line, reason_phrase_len ); internal->reason_phrase[ reason_phrase_len ] = 0; status_line = reason_phrase_end + 1; // extract content type char const* content_type_start = strstr( status_line, "Content-Type: " ); if( content_type_start ) { content_type_start += strlen( "Content-Type: " ); char const* content_type_end = strstr( content_type_start, "\r\n" ); if( content_type_end ) { size_t content_type_len = (size_t)( content_type_end - content_type_start ); if( content_type_len >= sizeof( internal->content_type ) ) content_type_len = sizeof( internal->content_type ) - 1; memcpy( internal->content_type, content_type_start, content_type_len ); internal->content_type[ content_type_len ] = 0; } } https->status = https->status_code < 300 ? HTTPS_STATUS_COMPLETED : __LINE__; // HTTPS_STATUS_FAILED; //< @r-lyeh https->response_data = (void*)( ( (uintptr_t) internal->data ) + header_size ); https->response_size = internal->data_size - header_size; #if 1 //< @r-lyeh // add silently a trailing zero and do not resize response_size. that way, // response remains intact but it can also be used as a ascii C string when needed. ( (char*)https->response_data )[ https->response_size ] = 0; #endif return https->status; } } return https->status; } void https_release( https_t* https ) { https_internal_t* internal = (https_internal_t*) https; #ifdef _WIN32 closesocket( internal->socket ); #else close( internal->socket ); #endif tls_destroy_context( internal->tls_context ); if( internal->request_header_large) HTTPS_FREE( internal->memctx, internal->request_header_large ); HTTPS_FREE( internal->memctx, internal->data ); HTTPS_FREE( internal->memctx, internal ); } #endif /* HTTPS_IMPLEMENTATION */ /* revision history: 0.1 ... */ /* ------------------------------------------------------------------------------ This software is available under 2 licenses - you may choose the one you like. ------------------------------------------------------------------------------ ALTERNATIVE A - MIT License Copyright (c) 2018 Mattias Gustavsson 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. ------------------------------------------------------------------------------ ALTERNATIVE B - Public Domain (www.unlicense.org) This is free and unencumbered software released into the public domain. Anyone is free to copy, modify, publish, use, compile, sell, or distribute this software, either in source code form or as a compiled binary, for any purpose, commercial or non-commercial, and by any means. In jurisdictions that recognize copyright laws, the author or authors of this software dedicate any and all copyright interest in the software to the public domain. We make this dedication for the benefit of the public at large and to the detriment of our heirs and successors. We intend this dedication to be an overt act of relinquishment in perpetuity of all present and future rights to this software under copyright law. 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 BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ------------------------------------------------------------------------------ */ #if 0 #include int main() { https_t *h = https_get("https://www.google.com/", NULL); while (!https_process(h)) { Sleep(0); } printf("%d %s\n", h->status_code, h->content_type); //printf("%.*s\n", (int)h->response_size, (char*)h->response_data); FILE *f = tmpfile(); fwrite(h->response_data, 1, h->response_size, f); printf("%u bytes\n", (unsigned)ftell(f)); fclose(f); https_release(h); } #endif #line 0 #undef F2 #undef F3 #line 1 "3rd_enet.h" /** * include/enet.h - a Single-Header auto-generated variant of enet.h library. * * Usage: * #define ENET_IMPLEMENTATION exactly in ONE source file right BEFORE including the library, like: * * #define ENET_IMPLEMENTATION * #include * * License: * The MIT License (MIT) * * Copyright (c) 2002-2016 Lee Salzman * Copyright (c) 2017-2021 Vladyslav Hrytsenko, Dominik Madarász * * 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. * */ /* ## Disclaimer This is a fork of the original library [lsalzman/enet](https://github.com/lsalzman/enet). While original repo offers a stable, time-tested wonderful library, we are trying to change some things, things, which can't be reflected on the main repo, like: * integrated ipv6 support * added monotonic time * applied project-wide code style change * cleaned up project * single-header style code * NPM package distribution * removed a lot of older methods * and many other various changes */ #ifndef ENET_INCLUDE_H #define ENET_INCLUDE_H #include #include #include #include #define ENET_VERSION_MAJOR 2 #define ENET_VERSION_MINOR 3 #define ENET_VERSION_PATCH 0 #define ENET_VERSION_CREATE(major, minor, patch) (((major)<<16) | ((minor)<<8) | (patch)) #define ENET_VERSION_GET_MAJOR(version) (((version)>>16)&0xFF) #define ENET_VERSION_GET_MINOR(version) (((version)>>8)&0xFF) #define ENET_VERSION_GET_PATCH(version) ((version)&0xFF) #define ENET_VERSION ENET_VERSION_CREATE(ENET_VERSION_MAJOR, ENET_VERSION_MINOR, ENET_VERSION_PATCH) #define ENET_TIME_OVERFLOW 86400000 #define ENET_TIME_LESS(a, b) ((a) - (b) >= ENET_TIME_OVERFLOW) #define ENET_TIME_GREATER(a, b) ((b) - (a) >= ENET_TIME_OVERFLOW) #define ENET_TIME_LESS_EQUAL(a, b) (! ENET_TIME_GREATER (a, b)) #define ENET_TIME_GREATER_EQUAL(a, b) (! ENET_TIME_LESS (a, b)) #define ENET_TIME_DIFFERENCE(a, b) ((a) - (b) >= ENET_TIME_OVERFLOW ? (b) - (a) : (a) - (b)) // =======================================================================// // ! // ! System differences // ! // =======================================================================// #if defined(_WIN32) #if defined(_MSC_VER) && defined(ENET_IMPLEMENTATION) #pragma warning (disable: 4267) // size_t to int conversion #pragma warning (disable: 4244) // 64bit to 32bit int #pragma warning (disable: 4018) // signed/unsigned mismatch #pragma warning (disable: 4146) // unary minus operator applied to unsigned type #endif #ifndef ENET_NO_PRAGMA_LINK #pragma comment(lib, "ws2_32") //< @r-lyeh removed .lib (tcc support) #pragma comment(lib, "winmm") //< @r-lyeh removed .lib (tcc support) #endif #if _MSC_VER >= 1910 /* It looks like there were changes as of Visual Studio 2017 and there are no 32/64 bit versions of _InterlockedExchange[operation], only InterlockedExchange[operation] (without leading underscore), so we have to distinguish between compiler versions */ #define NOT_UNDERSCORED_INTERLOCKED_EXCHANGE #endif #ifdef __GNUC__ #if (_WIN32_WINNT < 0x0501) #undef _WIN32_WINNT #define _WIN32_WINNT 0x0501 #endif #endif #include #include #include #include #if defined(_MSC_VER) && _MSC_VER < 1900 //< @r-lyeh typedef struct timespec { long tv_sec; long tv_nsec; }; #endif #ifndef __MINGW32__ #define CLOCK_MONOTONIC 0 //< @r-lyeh, tcc support #endif typedef SOCKET ENetSocket; #define ENET_SOCKET_NULL INVALID_SOCKET #define ENET_HOST_TO_NET_16(value) (htons(value)) #define ENET_HOST_TO_NET_32(value) (htonl(value)) #define ENET_NET_TO_HOST_16(value) (ntohs(value)) #define ENET_NET_TO_HOST_32(value) (ntohl(value)) typedef struct { size_t dataLength; void * data; } ENetBuffer; #define ENET_CALLBACK __cdecl #ifdef ENET_DLL #ifdef ENET_IMPLEMENTATION #define ENET_API __declspec( dllexport ) #else #define ENET_API __declspec( dllimport ) #endif // ENET_IMPLEMENTATION #else #define ENET_API extern #endif // ENET_DLL typedef fd_set ENetSocketSet; #define ENET_SOCKETSET_EMPTY(sockset) FD_ZERO(&(sockset)) #define ENET_SOCKETSET_ADD(sockset, socket) FD_SET(socket, &(sockset)) #define ENET_SOCKETSET_REMOVE(sockset, socket) FD_CLR(socket, &(sockset)) #define ENET_SOCKETSET_CHECK(sockset, socket) FD_ISSET(socket, &(sockset)) #else #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef __APPLE__ #include #include #include #endif #ifndef MSG_NOSIGNAL #define MSG_NOSIGNAL 0 #endif #ifdef MSG_MAXIOVLEN #define ENET_BUFFER_MAXIMUM MSG_MAXIOVLEN #endif typedef int ENetSocket; #define ENET_SOCKET_NULL -1 #define ENET_HOST_TO_NET_16(value) (htons(value)) /**< macro that converts host to net byte-order of a 16-bit value */ #define ENET_HOST_TO_NET_32(value) (htonl(value)) /**< macro that converts host to net byte-order of a 32-bit value */ #define ENET_NET_TO_HOST_16(value) (ntohs(value)) /**< macro that converts net to host byte-order of a 16-bit value */ #define ENET_NET_TO_HOST_32(value) (ntohl(value)) /**< macro that converts net to host byte-order of a 32-bit value */ typedef struct { void * data; size_t dataLength; } ENetBuffer; #define ENET_CALLBACK #define ENET_API extern typedef fd_set ENetSocketSet; #define ENET_SOCKETSET_EMPTY(sockset) FD_ZERO(&(sockset)) #define ENET_SOCKETSET_ADD(sockset, socket) FD_SET(socket, &(sockset)) #define ENET_SOCKETSET_REMOVE(sockset, socket) FD_CLR(socket, &(sockset)) #define ENET_SOCKETSET_CHECK(sockset, socket) FD_ISSET(socket, &(sockset)) #endif #ifdef __GNUC__ #define ENET_DEPRECATED(func) func __attribute__ ((deprecated)) #elif defined(_MSC_VER) #define ENET_DEPRECATED(func) __declspec(deprecated) func #else #pragma message("WARNING: Please ENET_DEPRECATED for this compiler") #define ENET_DEPRECATED(func) func #endif #ifndef ENET_BUFFER_MAXIMUM #define ENET_BUFFER_MAXIMUM (1 + 2 * ENET_PROTOCOL_MAXIMUM_PACKET_COMMANDS) #endif #define ENET_UNUSED(x) (void)x; #define ENET_MAX(x, y) ((x) > (y) ? (x) : (y)) #define ENET_MIN(x, y) ((x) < (y) ? (x) : (y)) #define ENET_IPV6 1 const struct in6_addr enet_v4_anyaddr = {{{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00 }}}; const struct in6_addr enet_v4_noaddr = {{{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }}}; const struct in6_addr enet_v4_localhost = {{{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x7f, 0x00, 0x00, 0x01 }}}; const struct in6_addr enet_v6_anyaddr = {{{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }}}; const struct in6_addr enet_v6_noaddr = {{{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }}}; const struct in6_addr enet_v6_localhost = {{{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 }}}; #define ENET_HOST_ANY in6addr_any #define ENET_HOST_BROADCAST 0xFFFFFFFFU #define ENET_PORT_ANY 0 #ifdef __cplusplus extern "C" { #endif // =======================================================================// // ! // ! Basic stuff // ! // =======================================================================// typedef uint8_t enet_uint8; /**< unsigned 8-bit type */ typedef uint16_t enet_uint16; /**< unsigned 16-bit type */ typedef uint32_t enet_uint32; /**< unsigned 32-bit type */ typedef uint64_t enet_uint64; /**< unsigned 64-bit type */ typedef enet_uint32 ENetVersion; typedef struct _ENetPacket ENetPacket; typedef struct _ENetCallbacks { void *(ENET_CALLBACK *malloc) (size_t size); void (ENET_CALLBACK *free) (void *memory); void (ENET_CALLBACK *no_memory) (void); ENetPacket *(ENET_CALLBACK *packet_create) (const void *data, size_t dataLength, enet_uint32 flags); void (ENET_CALLBACK *packet_destroy) (ENetPacket *packet); } ENetCallbacks; extern void *enet_malloc(size_t); extern void enet_free(void *); extern ENetPacket* enet_packet_create(const void*,size_t,enet_uint32); extern ENetPacket* enet_packet_copy(ENetPacket*); extern void enet_packet_destroy(ENetPacket*); // =======================================================================// // ! // ! List // ! // =======================================================================// typedef struct _ENetListNode { struct _ENetListNode *next; struct _ENetListNode *previous; } ENetListNode; typedef ENetListNode *ENetListIterator; typedef struct _ENetList { ENetListNode sentinel; } ENetList; extern ENetListIterator enet_list_insert(ENetListIterator, void *); extern ENetListIterator enet_list_move(ENetListIterator, void *, void *); extern void *enet_list_remove(ENetListIterator); extern void enet_list_clear(ENetList *); extern size_t enet_list_size(ENetList *); #define enet_list_begin(list) ((list)->sentinel.next) #define enet_list_end(list) (&(list)->sentinel) #define enet_list_empty(list) (enet_list_begin(list) == enet_list_end(list)) #define enet_list_next(iterator) ((iterator)->next) #define enet_list_previous(iterator) ((iterator)->previous) #define enet_list_front(list) ((void *)(list)->sentinel.next) #define enet_list_back(list) ((void *)(list)->sentinel.previous) // =======================================================================// // ! // ! Protocol // ! // =======================================================================// enum { ENET_PROTOCOL_MINIMUM_MTU = 576, ENET_PROTOCOL_MAXIMUM_MTU = 4096, ENET_PROTOCOL_MAXIMUM_PACKET_COMMANDS = 32, ENET_PROTOCOL_MINIMUM_WINDOW_SIZE = 4096, ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE = 65536, ENET_PROTOCOL_MINIMUM_CHANNEL_COUNT = 1, ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT = 255, ENET_PROTOCOL_MAXIMUM_PEER_ID = 0xFFF, ENET_PROTOCOL_MAXIMUM_FRAGMENT_COUNT = 1024 * 1024 }; typedef enum _ENetProtocolCommand { ENET_PROTOCOL_COMMAND_NONE = 0, ENET_PROTOCOL_COMMAND_ACKNOWLEDGE = 1, ENET_PROTOCOL_COMMAND_CONNECT = 2, ENET_PROTOCOL_COMMAND_VERIFY_CONNECT = 3, ENET_PROTOCOL_COMMAND_DISCONNECT = 4, ENET_PROTOCOL_COMMAND_PING = 5, ENET_PROTOCOL_COMMAND_SEND_RELIABLE = 6, ENET_PROTOCOL_COMMAND_SEND_UNRELIABLE = 7, ENET_PROTOCOL_COMMAND_SEND_FRAGMENT = 8, ENET_PROTOCOL_COMMAND_SEND_UNSEQUENCED = 9, ENET_PROTOCOL_COMMAND_BANDWIDTH_LIMIT = 10, ENET_PROTOCOL_COMMAND_THROTTLE_CONFIGURE = 11, ENET_PROTOCOL_COMMAND_SEND_UNRELIABLE_FRAGMENT = 12, ENET_PROTOCOL_COMMAND_COUNT = 13, ENET_PROTOCOL_COMMAND_MASK = 0x0F } ENetProtocolCommand; typedef enum _ENetProtocolFlag { ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE = (1 << 7), ENET_PROTOCOL_COMMAND_FLAG_UNSEQUENCED = (1 << 6), ENET_PROTOCOL_HEADER_FLAG_COMPRESSED = (1 << 14), ENET_PROTOCOL_HEADER_FLAG_SENT_TIME = (1 << 15), ENET_PROTOCOL_HEADER_FLAG_MASK = ENET_PROTOCOL_HEADER_FLAG_COMPRESSED | ENET_PROTOCOL_HEADER_FLAG_SENT_TIME, ENET_PROTOCOL_HEADER_SESSION_MASK = (3 << 12), ENET_PROTOCOL_HEADER_SESSION_SHIFT = 12 } ENetProtocolFlag; #ifdef _MSC_VER #pragma pack(push, 1) #define ENET_PACKED #elif defined(__GNUC__) || defined(__clang__) #define ENET_PACKED __attribute__ ((packed)) #else #define ENET_PACKED #endif typedef struct _ENetProtocolHeader { enet_uint16 peerID; enet_uint16 sentTime; } ENET_PACKED ENetProtocolHeader; typedef struct _ENetProtocolCommandHeader { enet_uint8 command; enet_uint8 channelID; enet_uint16 reliableSequenceNumber; } ENET_PACKED ENetProtocolCommandHeader; typedef struct _ENetProtocolAcknowledge { ENetProtocolCommandHeader header; enet_uint16 receivedReliableSequenceNumber; enet_uint16 receivedSentTime; } ENET_PACKED ENetProtocolAcknowledge; typedef struct _ENetProtocolConnect { ENetProtocolCommandHeader header; enet_uint16 outgoingPeerID; enet_uint8 incomingSessionID; enet_uint8 outgoingSessionID; enet_uint32 mtu; enet_uint32 windowSize; enet_uint32 channelCount; enet_uint32 incomingBandwidth; enet_uint32 outgoingBandwidth; enet_uint32 packetThrottleInterval; enet_uint32 packetThrottleAcceleration; enet_uint32 packetThrottleDeceleration; enet_uint32 connectID; enet_uint32 data; } ENET_PACKED ENetProtocolConnect; typedef struct _ENetProtocolVerifyConnect { ENetProtocolCommandHeader header; enet_uint16 outgoingPeerID; enet_uint8 incomingSessionID; enet_uint8 outgoingSessionID; enet_uint32 mtu; enet_uint32 windowSize; enet_uint32 channelCount; enet_uint32 incomingBandwidth; enet_uint32 outgoingBandwidth; enet_uint32 packetThrottleInterval; enet_uint32 packetThrottleAcceleration; enet_uint32 packetThrottleDeceleration; enet_uint32 connectID; } ENET_PACKED ENetProtocolVerifyConnect; typedef struct _ENetProtocolBandwidthLimit { ENetProtocolCommandHeader header; enet_uint32 incomingBandwidth; enet_uint32 outgoingBandwidth; } ENET_PACKED ENetProtocolBandwidthLimit; typedef struct _ENetProtocolThrottleConfigure { ENetProtocolCommandHeader header; enet_uint32 packetThrottleInterval; enet_uint32 packetThrottleAcceleration; enet_uint32 packetThrottleDeceleration; } ENET_PACKED ENetProtocolThrottleConfigure; typedef struct _ENetProtocolDisconnect { ENetProtocolCommandHeader header; enet_uint32 data; } ENET_PACKED ENetProtocolDisconnect; typedef struct _ENetProtocolPing { ENetProtocolCommandHeader header; } ENET_PACKED ENetProtocolPing; typedef struct _ENetProtocolSendReliable { ENetProtocolCommandHeader header; enet_uint16 dataLength; } ENET_PACKED ENetProtocolSendReliable; typedef struct _ENetProtocolSendUnreliable { ENetProtocolCommandHeader header; enet_uint16 unreliableSequenceNumber; enet_uint16 dataLength; } ENET_PACKED ENetProtocolSendUnreliable; typedef struct _ENetProtocolSendUnsequenced { ENetProtocolCommandHeader header; enet_uint16 unsequencedGroup; enet_uint16 dataLength; } ENET_PACKED ENetProtocolSendUnsequenced; typedef struct _ENetProtocolSendFragment { ENetProtocolCommandHeader header; enet_uint16 startSequenceNumber; enet_uint16 dataLength; enet_uint32 fragmentCount; enet_uint32 fragmentNumber; enet_uint32 totalLength; enet_uint32 fragmentOffset; } ENET_PACKED ENetProtocolSendFragment; typedef union _ENetProtocol { ENetProtocolCommandHeader header; ENetProtocolAcknowledge acknowledge; ENetProtocolConnect connect; ENetProtocolVerifyConnect verifyConnect; ENetProtocolDisconnect disconnect; ENetProtocolPing ping; ENetProtocolSendReliable sendReliable; ENetProtocolSendUnreliable sendUnreliable; ENetProtocolSendUnsequenced sendUnsequenced; ENetProtocolSendFragment sendFragment; ENetProtocolBandwidthLimit bandwidthLimit; ENetProtocolThrottleConfigure throttleConfigure; } ENET_PACKED ENetProtocol; #ifdef _MSC_VER #pragma pack(pop) #endif // =======================================================================// // ! // ! General ENet structs/enums // ! // =======================================================================// typedef enum _ENetSocketType { ENET_SOCKET_TYPE_STREAM = 1, ENET_SOCKET_TYPE_DATAGRAM = 2 } ENetSocketType; typedef enum _ENetSocketWait { ENET_SOCKET_WAIT_NONE = 0, ENET_SOCKET_WAIT_SEND = (1 << 0), ENET_SOCKET_WAIT_RECEIVE = (1 << 1), ENET_SOCKET_WAIT_INTERRUPT = (1 << 2) } ENetSocketWait; typedef enum _ENetSocketOption { ENET_SOCKOPT_NONBLOCK = 1, ENET_SOCKOPT_BROADCAST = 2, ENET_SOCKOPT_RCVBUF = 3, ENET_SOCKOPT_SNDBUF = 4, ENET_SOCKOPT_REUSEADDR = 5, ENET_SOCKOPT_RCVTIMEO = 6, ENET_SOCKOPT_SNDTIMEO = 7, ENET_SOCKOPT_ERROR = 8, ENET_SOCKOPT_NODELAY = 9, ENET_SOCKOPT_IPV6_V6ONLY = 10, } ENetSocketOption; typedef enum _ENetSocketShutdown { ENET_SOCKET_SHUTDOWN_READ = 0, ENET_SOCKET_SHUTDOWN_WRITE = 1, ENET_SOCKET_SHUTDOWN_READ_WRITE = 2 } ENetSocketShutdown; /** * Portable internet address structure. * * The host must be specified in network byte-order, and the port must be in host * byte-order. The constant ENET_HOST_ANY may be used to specify the default * server host. The constant ENET_HOST_BROADCAST may be used to specify the * broadcast address (255.255.255.255). This makes sense for enet_host_connect, * but not for enet_host_create. Once a server responds to a broadcast, the * address is updated from ENET_HOST_BROADCAST to the server's actual IP address. */ typedef struct _ENetAddress { struct in6_addr host; enet_uint16 port; enet_uint16 sin6_scope_id; } ENetAddress; #define in6_equal(in6_addr_a, in6_addr_b) (memcmp(&in6_addr_a, &in6_addr_b, sizeof(struct in6_addr)) == 0) /** * Packet flag bit constants. * * The host must be specified in network byte-order, and the port must be in * host byte-order. The constant ENET_HOST_ANY may be used to specify the * default server host. * * @sa ENetPacket */ typedef enum _ENetPacketFlag { ENET_PACKET_FLAG_RELIABLE = (1 << 0), /** packet must be received by the target peer and resend attempts should be made until the packet is delivered */ ENET_PACKET_FLAG_UNSEQUENCED = (1 << 1), /** packet will not be sequenced with other packets not supported for reliable packets */ ENET_PACKET_FLAG_NO_ALLOCATE = (1 << 2), /** packet will not allocate data, and user must supply it instead */ ENET_PACKET_FLAG_UNRELIABLE_FRAGMENT = (1 << 3), /** packet will be fragmented using unreliable (instead of reliable) sends if it exceeds the MTU */ ENET_PACKET_FLAG_SENT = (1 << 8), /** whether the packet has been sent from all queues it has been entered into */ } ENetPacketFlag; typedef void (ENET_CALLBACK *ENetPacketFreeCallback)(void *); /** * ENet packet structure. * * An ENet data packet that may be sent to or received from a peer. The shown * fields should only be read and never modified. The data field contains the * allocated data for the packet. The dataLength fields specifies the length * of the allocated data. The flags field is either 0 (specifying no flags), * or a bitwise-or of any combination of the following flags: * * ENET_PACKET_FLAG_RELIABLE - packet must be received by the target peer and resend attempts should be made until the packet is delivered * ENET_PACKET_FLAG_UNSEQUENCED - packet will not be sequenced with other packets (not supported for reliable packets) * ENET_PACKET_FLAG_NO_ALLOCATE - packet will not allocate data, and user must supply it instead * ENET_PACKET_FLAG_UNRELIABLE_FRAGMENT - packet will be fragmented using unreliable (instead of reliable) sends if it exceeds the MTU * ENET_PACKET_FLAG_SENT - whether the packet has been sent from all queues it has been entered into * @sa ENetPacketFlag */ typedef struct _ENetPacket { size_t referenceCount; /**< internal use only */ enet_uint32 flags; /**< bitwise-or of ENetPacketFlag constants */ enet_uint8 * data; /**< allocated data for packet */ size_t dataLength; /**< length of data */ ENetPacketFreeCallback freeCallback; /**< function to be called when the packet is no longer in use */ void * userData; /**< application private data, may be freely modified */ } ENetPacket; typedef struct _ENetAcknowledgement { ENetListNode acknowledgementList; enet_uint32 sentTime; ENetProtocol command; } ENetAcknowledgement; typedef struct _ENetOutgoingCommand { ENetListNode outgoingCommandList; enet_uint16 reliableSequenceNumber; enet_uint16 unreliableSequenceNumber; enet_uint32 sentTime; enet_uint32 roundTripTimeout; enet_uint32 roundTripTimeoutLimit; enet_uint32 fragmentOffset; enet_uint16 fragmentLength; enet_uint16 sendAttempts; ENetProtocol command; ENetPacket * packet; } ENetOutgoingCommand; typedef struct _ENetIncomingCommand { ENetListNode incomingCommandList; enet_uint16 reliableSequenceNumber; enet_uint16 unreliableSequenceNumber; ENetProtocol command; enet_uint32 fragmentCount; enet_uint32 fragmentsRemaining; enet_uint32 *fragments; ENetPacket * packet; } ENetIncomingCommand; typedef enum _ENetPeerState { ENET_PEER_STATE_DISCONNECTED = 0, ENET_PEER_STATE_CONNECTING = 1, ENET_PEER_STATE_ACKNOWLEDGING_CONNECT = 2, ENET_PEER_STATE_CONNECTION_PENDING = 3, ENET_PEER_STATE_CONNECTION_SUCCEEDED = 4, ENET_PEER_STATE_CONNECTED = 5, ENET_PEER_STATE_DISCONNECT_LATER = 6, ENET_PEER_STATE_DISCONNECTING = 7, ENET_PEER_STATE_ACKNOWLEDGING_DISCONNECT = 8, ENET_PEER_STATE_ZOMBIE = 9 } ENetPeerState; enum { ENET_HOST_RECEIVE_BUFFER_SIZE = 256 * 1024, ENET_HOST_SEND_BUFFER_SIZE = 256 * 1024, ENET_HOST_BANDWIDTH_THROTTLE_INTERVAL = 1000, ENET_HOST_DEFAULT_MTU = 1400, ENET_HOST_DEFAULT_MAXIMUM_PACKET_SIZE = 32 * 1024 * 1024, ENET_HOST_DEFAULT_MAXIMUM_WAITING_DATA = 32 * 1024 * 1024, ENET_PEER_DEFAULT_ROUND_TRIP_TIME = 500, ENET_PEER_DEFAULT_PACKET_THROTTLE = 32, ENET_PEER_PACKET_THROTTLE_SCALE = 32, ENET_PEER_PACKET_THROTTLE_COUNTER = 7, ENET_PEER_PACKET_THROTTLE_ACCELERATION = 2, ENET_PEER_PACKET_THROTTLE_DECELERATION = 2, ENET_PEER_PACKET_THROTTLE_INTERVAL = 5000, ENET_PEER_PACKET_LOSS_SCALE = (1 << 16), ENET_PEER_PACKET_LOSS_INTERVAL = 10000, ENET_PEER_WINDOW_SIZE_SCALE = 64 * 1024, ENET_PEER_TIMEOUT_LIMIT = 32, ENET_PEER_TIMEOUT_MINIMUM = 5000, ENET_PEER_TIMEOUT_MAXIMUM = 30000, ENET_PEER_PING_INTERVAL = 500, ENET_PEER_UNSEQUENCED_WINDOWS = 64, ENET_PEER_UNSEQUENCED_WINDOW_SIZE = 1024, ENET_PEER_FREE_UNSEQUENCED_WINDOWS = 32, ENET_PEER_RELIABLE_WINDOWS = 16, ENET_PEER_RELIABLE_WINDOW_SIZE = 0x1000, ENET_PEER_FREE_RELIABLE_WINDOWS = 8 }; typedef struct _ENetChannel { enet_uint16 outgoingReliableSequenceNumber; enet_uint16 outgoingUnreliableSequenceNumber; enet_uint16 usedReliableWindows; enet_uint16 reliableWindows[ENET_PEER_RELIABLE_WINDOWS]; enet_uint16 incomingReliableSequenceNumber; enet_uint16 incomingUnreliableSequenceNumber; ENetList incomingReliableCommands; ENetList incomingUnreliableCommands; } ENetChannel; /** * An ENet peer which data packets may be sent or received from. * * No fields should be modified unless otherwise specified. */ typedef struct _ENetPeer { ENetListNode dispatchList; struct _ENetHost *host; enet_uint16 outgoingPeerID; enet_uint16 incomingPeerID; enet_uint32 connectID; enet_uint8 outgoingSessionID; enet_uint8 incomingSessionID; ENetAddress address; /**< Internet address of the peer */ void * data; /**< Application private data, may be freely modified */ ENetPeerState state; ENetChannel * channels; size_t channelCount; /**< Number of channels allocated for communication with peer */ enet_uint32 incomingBandwidth; /**< Downstream bandwidth of the client in bytes/second */ enet_uint32 outgoingBandwidth; /**< Upstream bandwidth of the client in bytes/second */ enet_uint32 incomingBandwidthThrottleEpoch; enet_uint32 outgoingBandwidthThrottleEpoch; enet_uint32 incomingDataTotal; enet_uint64 totalDataReceived; enet_uint32 outgoingDataTotal; enet_uint64 totalDataSent; enet_uint32 lastSendTime; enet_uint32 lastReceiveTime; enet_uint32 nextTimeout; enet_uint32 earliestTimeout; enet_uint32 packetLossEpoch; enet_uint32 packetsSent; enet_uint64 totalPacketsSent; /**< total number of packets sent during a session */ enet_uint32 packetsLost; enet_uint32 totalPacketsLost; /**< total number of packets lost during a session */ enet_uint32 packetLoss; /**< mean packet loss of reliable packets as a ratio with respect to the constant ENET_PEER_PACKET_LOSS_SCALE */ enet_uint32 packetLossVariance; enet_uint32 packetThrottle; enet_uint32 packetThrottleLimit; enet_uint32 packetThrottleCounter; enet_uint32 packetThrottleEpoch; enet_uint32 packetThrottleAcceleration; enet_uint32 packetThrottleDeceleration; enet_uint32 packetThrottleInterval; enet_uint32 pingInterval; enet_uint32 timeoutLimit; enet_uint32 timeoutMinimum; enet_uint32 timeoutMaximum; enet_uint32 lastRoundTripTime; enet_uint32 lowestRoundTripTime; enet_uint32 lastRoundTripTimeVariance; enet_uint32 highestRoundTripTimeVariance; enet_uint32 roundTripTime; /**< mean round trip time (RTT), in milliseconds, between sending a reliable packet and receiving its acknowledgement */ enet_uint32 roundTripTimeVariance; enet_uint32 mtu; enet_uint32 windowSize; enet_uint32 reliableDataInTransit; enet_uint16 outgoingReliableSequenceNumber; ENetList acknowledgements; ENetList sentReliableCommands; ENetList sentUnreliableCommands; ENetList outgoingReliableCommands; ENetList outgoingUnreliableCommands; ENetList dispatchedCommands; int needsDispatch; enet_uint16 incomingUnsequencedGroup; enet_uint16 outgoingUnsequencedGroup; enet_uint32 unsequencedWindow[ENET_PEER_UNSEQUENCED_WINDOW_SIZE / 32]; enet_uint32 eventData; size_t totalWaitingData; } ENetPeer; /** An ENet packet compressor for compressing UDP packets before socket sends or receives. */ typedef struct _ENetCompressor { /** Context data for the compressor. Must be non-NULL. */ void *context; /** Compresses from inBuffers[0:inBufferCount-1], containing inLimit bytes, to outData, outputting at most outLimit bytes. Should return 0 on failure. */ size_t(ENET_CALLBACK * compress) (void *context, const ENetBuffer * inBuffers, size_t inBufferCount, size_t inLimit, enet_uint8 * outData, size_t outLimit); /** Decompresses from inData, containing inLimit bytes, to outData, outputting at most outLimit bytes. Should return 0 on failure. */ size_t(ENET_CALLBACK * decompress) (void *context, const enet_uint8 * inData, size_t inLimit, enet_uint8 * outData, size_t outLimit); /** Destroys the context when compression is disabled or the host is destroyed. May be NULL. */ void (ENET_CALLBACK * destroy)(void *context); } ENetCompressor; /** Callback that computes the checksum of the data held in buffers[0:bufferCount-1] */ typedef enet_uint32 (ENET_CALLBACK * ENetChecksumCallback)(const ENetBuffer *buffers, size_t bufferCount); /** Callback for intercepting received raw UDP packets. Should return 1 to intercept, 0 to ignore, or -1 to propagate an error. */ typedef int (ENET_CALLBACK * ENetInterceptCallback)(struct _ENetHost *host, void *event); /** An ENet host for communicating with peers. * * No fields should be modified unless otherwise stated. * * @sa enet_host_create() * @sa enet_host_destroy() * @sa enet_host_connect() * @sa enet_host_service() * @sa enet_host_flush() * @sa enet_host_broadcast() * @sa enet_host_compress() * @sa enet_host_channel_limit() * @sa enet_host_bandwidth_limit() * @sa enet_host_bandwidth_throttle() */ typedef struct _ENetHost { ENetSocket socket; ENetAddress address; /**< Internet address of the host */ enet_uint32 incomingBandwidth; /**< downstream bandwidth of the host */ enet_uint32 outgoingBandwidth; /**< upstream bandwidth of the host */ enet_uint32 bandwidthThrottleEpoch; enet_uint32 mtu; enet_uint32 randomSeed; int recalculateBandwidthLimits; ENetPeer * peers; /**< array of peers allocated for this host */ size_t peerCount; /**< number of peers allocated for this host */ size_t channelLimit; /**< maximum number of channels allowed for connected peers */ enet_uint32 serviceTime; ENetList dispatchQueue; int continueSending; size_t packetSize; enet_uint16 headerFlags; ENetProtocol commands[ENET_PROTOCOL_MAXIMUM_PACKET_COMMANDS]; size_t commandCount; ENetBuffer buffers[ENET_BUFFER_MAXIMUM]; size_t bufferCount; ENetChecksumCallback checksum; /**< callback the user can set to enable packet checksums for this host */ ENetCompressor compressor; enet_uint8 packetData[2][ENET_PROTOCOL_MAXIMUM_MTU]; ENetAddress receivedAddress; enet_uint8 * receivedData; size_t receivedDataLength; enet_uint32 totalSentData; /**< total data sent, user should reset to 0 as needed to prevent overflow */ enet_uint32 totalSentPackets; /**< total UDP packets sent, user should reset to 0 as needed to prevent overflow */ enet_uint32 totalReceivedData; /**< total data received, user should reset to 0 as needed to prevent overflow */ enet_uint32 totalReceivedPackets; /**< total UDP packets received, user should reset to 0 as needed to prevent overflow */ ENetInterceptCallback intercept; /**< callback the user can set to intercept received raw UDP packets */ size_t connectedPeers; size_t bandwidthLimitedPeers; size_t duplicatePeers; /**< optional number of allowed peers from duplicate IPs, defaults to ENET_PROTOCOL_MAXIMUM_PEER_ID */ size_t maximumPacketSize; /**< the maximum allowable packet size that may be sent or received on a peer */ size_t maximumWaitingData; /**< the maximum aggregate amount of buffer space a peer may use waiting for packets to be delivered */ } ENetHost; /** * An ENet event type, as specified in @ref ENetEvent. */ typedef enum _ENetEventType { /** no event occurred within the specified time limit */ ENET_EVENT_TYPE_NONE = 0, /** a connection request initiated by enet_host_connect has completed. * The peer field contains the peer which successfully connected. */ ENET_EVENT_TYPE_CONNECT = 1, /** a peer has disconnected. This event is generated on a successful * completion of a disconnect initiated by enet_peer_disconnect, if * a peer has timed out. The peer field contains the peer * which disconnected. The data field contains user supplied data * describing the disconnection, or 0, if none is available. */ ENET_EVENT_TYPE_DISCONNECT = 2, /** a packet has been received from a peer. The peer field specifies the * peer which sent the packet. The channelID field specifies the channel * number upon which the packet was received. The packet field contains * the packet that was received; this packet must be destroyed with * enet_packet_destroy after use. */ ENET_EVENT_TYPE_RECEIVE = 3, /** a peer is disconnected because the host didn't receive the acknowledgment * packet within certain maximum time out. The reason could be because of bad * network connection or host crashed. */ ENET_EVENT_TYPE_DISCONNECT_TIMEOUT = 4, } ENetEventType; /** * An ENet event as returned by enet_host_service(). * * @sa enet_host_service */ typedef struct _ENetEvent { ENetEventType type; /**< type of the event */ ENetPeer * peer; /**< peer that generated a connect, disconnect or receive event */ enet_uint8 channelID; /**< channel on the peer that generated the event, if appropriate */ enet_uint32 data; /**< data associated with the event, if appropriate */ ENetPacket * packet; /**< packet associated with the event, if appropriate */ } ENetEvent; // =======================================================================// // ! // ! Public API // ! // =======================================================================// /** * Initializes ENet globally. Must be called prior to using any functions in ENet. * @returns 0 on success, < 0 on failure */ ENET_API int enet_initialize(void); /** * Initializes ENet globally and supplies user-overridden callbacks. Must be called prior to using any functions in ENet. Do not use enet_initialize() if you use this variant. Make sure the ENetCallbacks structure is zeroed out so that any additional callbacks added in future versions will be properly ignored. * * @param version the constant ENET_VERSION should be supplied so ENet knows which version of ENetCallbacks struct to use * @param inits user-overridden callbacks where any NULL callbacks will use ENet's defaults * @returns 0 on success, < 0 on failure */ ENET_API int enet_initialize_with_callbacks(ENetVersion version, const ENetCallbacks * inits); /** * Shuts down ENet globally. Should be called when a program that has initialized ENet exits. */ ENET_API void enet_deinitialize(void); /** * Gives the linked version of the ENet library. * @returns the version number */ ENET_API ENetVersion enet_linked_version(void); /** Returns the monotonic time in milliseconds. Its initial value is unspecified unless otherwise set. */ ENET_API enet_uint32 enet_time_get(void); /** ENet socket functions */ ENET_API ENetSocket enet_socket_create(ENetSocketType); ENET_API int enet_socket_bind(ENetSocket, const ENetAddress *); ENET_API int enet_socket_get_address(ENetSocket, ENetAddress *); ENET_API int enet_socket_listen(ENetSocket, int); ENET_API ENetSocket enet_socket_accept(ENetSocket, ENetAddress *); ENET_API int enet_socket_connect(ENetSocket, const ENetAddress *); ENET_API int enet_socket_send(ENetSocket, const ENetAddress *, const ENetBuffer *, size_t); ENET_API int enet_socket_receive(ENetSocket, ENetAddress *, ENetBuffer *, size_t); ENET_API int enet_socket_wait(ENetSocket, enet_uint32 *, enet_uint64); ENET_API int enet_socket_set_option(ENetSocket, ENetSocketOption, int); ENET_API int enet_socket_get_option(ENetSocket, ENetSocketOption, int *); ENET_API int enet_socket_shutdown(ENetSocket, ENetSocketShutdown); ENET_API void enet_socket_destroy(ENetSocket); ENET_API int enet_socketset_select(ENetSocket, ENetSocketSet *, ENetSocketSet *, enet_uint32); /** Attempts to parse the printable form of the IP address in the parameter hostName and sets the host field in the address parameter if successful. @param address destination to store the parsed IP address @param hostName IP address to parse @retval 0 on success @retval < 0 on failure @returns the address of the given hostName in address on success */ ENET_API int enet_address_set_host_ip_old(ENetAddress * address, const char * hostName); /** Attempts to resolve the host named by the parameter hostName and sets the host field in the address parameter if successful. @param address destination to store resolved address @param hostName host name to lookup @retval 0 on success @retval < 0 on failure @returns the address of the given hostName in address on success */ ENET_API int enet_address_set_host_old(ENetAddress * address, const char * hostName); /** Gives the printable form of the IP address specified in the address parameter. @param address address printed @param hostName destination for name, must not be NULL @param nameLength maximum length of hostName. @returns the null-terminated name of the host in hostName on success @retval 0 on success @retval < 0 on failure */ ENET_API int enet_address_get_host_ip_old(const ENetAddress * address, char * hostName, size_t nameLength); /** Attempts to do a reverse lookup of the host field in the address parameter. @param address address used for reverse lookup @param hostName destination for name, must not be NULL @param nameLength maximum length of hostName. @returns the null-terminated name of the host in hostName on success @retval 0 on success @retval < 0 on failure */ ENET_API int enet_address_get_host_old(const ENetAddress * address, char * hostName, size_t nameLength); ENET_API int enet_address_set_host_ip_new(ENetAddress * address, const char * hostName); ENET_API int enet_address_set_host_new(ENetAddress * address, const char * hostName); ENET_API int enet_address_get_host_ip_new(const ENetAddress * address, char * hostName, size_t nameLength); ENET_API int enet_address_get_host_new(const ENetAddress * address, char * hostName, size_t nameLength); #ifdef ENET_FEATURE_ADDRESS_MAPPING #define enet_address_set_host_ip enet_address_set_host_ip_new #define enet_address_set_host enet_address_set_host_new #define enet_address_get_host_ip enet_address_get_host_ip_new #define enet_address_get_host enet_address_get_host_new #else #define enet_address_set_host_ip enet_address_set_host_ip_old #define enet_address_set_host enet_address_set_host_old #define enet_address_get_host_ip enet_address_get_host_ip_old #define enet_address_get_host enet_address_get_host_old #endif ENET_API enet_uint32 enet_host_get_peers_count(ENetHost *); ENET_API enet_uint32 enet_host_get_packets_sent(ENetHost *); ENET_API enet_uint32 enet_host_get_packets_received(ENetHost *); ENET_API enet_uint32 enet_host_get_bytes_sent(ENetHost *); ENET_API enet_uint32 enet_host_get_bytes_received(ENetHost *); ENET_API enet_uint32 enet_host_get_received_data(ENetHost *, enet_uint8** data); ENET_API enet_uint32 enet_host_get_mtu(ENetHost *); ENET_API enet_uint32 enet_peer_get_id(ENetPeer *); ENET_API enet_uint32 enet_peer_get_ip(ENetPeer *, char * ip, size_t ipLength); ENET_API enet_uint16 enet_peer_get_port(ENetPeer *); ENET_API enet_uint32 enet_peer_get_rtt(ENetPeer *); ENET_API enet_uint64 enet_peer_get_packets_sent(ENetPeer *); ENET_API enet_uint32 enet_peer_get_packets_lost(ENetPeer *); ENET_API enet_uint64 enet_peer_get_bytes_sent(ENetPeer *); ENET_API enet_uint64 enet_peer_get_bytes_received(ENetPeer *); ENET_API ENetPeerState enet_peer_get_state(ENetPeer *); ENET_API void * enet_peer_get_data(ENetPeer *); ENET_API void enet_peer_set_data(ENetPeer *, const void *); ENET_API void * enet_packet_get_data(ENetPacket *); ENET_API enet_uint32 enet_packet_get_length(ENetPacket *); ENET_API void enet_packet_set_free_callback(ENetPacket *, void *); ENET_API ENetPacket * enet_packet_create_offset(const void *, size_t, size_t, enet_uint32); ENET_API enet_uint32 enet_crc32(const ENetBuffer *, size_t); ENET_API ENetHost * enet_host_create(const ENetAddress *, size_t, size_t, enet_uint32, enet_uint32); ENET_API void enet_host_destroy(ENetHost *); ENET_API ENetPeer * enet_host_connect(ENetHost *, const ENetAddress *, size_t, enet_uint32); ENET_API int enet_host_check_events(ENetHost *, ENetEvent *); ENET_API int enet_host_service(ENetHost *, ENetEvent *, enet_uint32); ENET_API int enet_host_send_raw(ENetHost *, const ENetAddress *, enet_uint8 *, size_t); ENET_API int enet_host_send_raw_ex(ENetHost *host, const ENetAddress* address, enet_uint8* data, size_t skipBytes, size_t bytesToSend); ENET_API void enet_host_set_intercept(ENetHost *, const ENetInterceptCallback); ENET_API void enet_host_flush(ENetHost *); ENET_API void enet_host_broadcast(ENetHost *, enet_uint8, ENetPacket *); ENET_API void enet_host_compress(ENetHost *, const ENetCompressor *); ENET_API void enet_host_channel_limit(ENetHost *, size_t); ENET_API void enet_host_bandwidth_limit(ENetHost *, enet_uint32, enet_uint32); extern void enet_host_bandwidth_throttle(ENetHost *); extern enet_uint64 enet_host_random_seed(void); ENET_API int enet_peer_send(ENetPeer *, enet_uint8, ENetPacket *); ENET_API ENetPacket * enet_peer_receive(ENetPeer *, enet_uint8 * channelID); ENET_API void enet_peer_ping(ENetPeer *); ENET_API void enet_peer_ping_interval(ENetPeer *, enet_uint32); ENET_API void enet_peer_timeout(ENetPeer *, enet_uint32, enet_uint32, enet_uint32); ENET_API void enet_peer_reset(ENetPeer *); ENET_API void enet_peer_disconnect(ENetPeer *, enet_uint32); ENET_API void enet_peer_disconnect_now(ENetPeer *, enet_uint32); ENET_API void enet_peer_disconnect_later(ENetPeer *, enet_uint32); ENET_API void enet_peer_throttle_configure(ENetPeer *, enet_uint32, enet_uint32, enet_uint32); extern int enet_peer_throttle(ENetPeer *, enet_uint32); extern void enet_peer_reset_queues(ENetPeer *); extern void enet_peer_setup_outgoing_command(ENetPeer *, ENetOutgoingCommand *); extern ENetOutgoingCommand * enet_peer_queue_outgoing_command(ENetPeer *, const ENetProtocol *, ENetPacket *, enet_uint32, enet_uint16); extern ENetIncomingCommand * enet_peer_queue_incoming_command(ENetPeer *, const ENetProtocol *, const void *, size_t, enet_uint32, enet_uint32); extern ENetAcknowledgement * enet_peer_queue_acknowledgement(ENetPeer *, const ENetProtocol *, enet_uint16); extern void enet_peer_dispatch_incoming_unreliable_commands(ENetPeer *, ENetChannel *); extern void enet_peer_dispatch_incoming_reliable_commands(ENetPeer *, ENetChannel *); extern void enet_peer_on_connect(ENetPeer *); extern void enet_peer_on_disconnect(ENetPeer *); extern size_t enet_protocol_command_size (enet_uint8); #ifdef __cplusplus } #endif #if defined(ENET_IMPLEMENTATION) && !defined(ENET_IMPLEMENTATION_DONE) #define ENET_IMPLEMENTATION_DONE 1 #ifdef __cplusplus extern "C" { #endif // =======================================================================// // ! // ! Atomics // ! // =======================================================================// #if defined(_MSC_VER) #define ENET_AT_CASSERT_PRED(predicate) sizeof(char[2 * !!(predicate)-1]) #define ENET_IS_SUPPORTED_ATOMIC(size) ENET_AT_CASSERT_PRED(size == 1 || size == 2 || size == 4 || size == 8) #define ENET_ATOMIC_SIZEOF(variable) (ENET_IS_SUPPORTED_ATOMIC(sizeof(*(variable))), sizeof(*(variable))) __inline int64_t enet_at_atomic_read(char *ptr, size_t size) { switch (size) { case 1: return _InterlockedExchangeAdd8((volatile char *)ptr, 0); case 2: return _InterlockedExchangeAdd16((volatile SHORT *)ptr, 0); case 4: #ifdef NOT_UNDERSCORED_INTERLOCKED_EXCHANGE return InterlockedExchangeAdd((volatile LONG *)ptr, 0); #else return _InterlockedExchangeAdd((volatile LONG *)ptr, 0); #endif case 8: #ifdef NOT_UNDERSCORED_INTERLOCKED_EXCHANGE return InterlockedExchangeAdd64((volatile LONGLONG *)ptr, 0); #else return _InterlockedExchangeAdd64((volatile LONGLONG *)ptr, 0); #endif default: return 0xbad13bad; /* never reached */ } } __inline int64_t enet_at_atomic_write(char *ptr, int64_t value, size_t size) { switch (size) { case 1: return _InterlockedExchange8((volatile char *)ptr, (char)value); case 2: return _InterlockedExchange16((volatile SHORT *)ptr, (SHORT)value); case 4: #ifdef NOT_UNDERSCORED_INTERLOCKED_EXCHANGE return InterlockedExchange((volatile LONG *)ptr, (LONG)value); #else return _InterlockedExchange((volatile LONG *)ptr, (LONG)value); #endif case 8: #ifdef NOT_UNDERSCORED_INTERLOCKED_EXCHANGE return InterlockedExchange64((volatile LONGLONG *)ptr, (LONGLONG)value); #else return _InterlockedExchange64((volatile LONGLONG *)ptr, (LONGLONG)value); #endif default: return 0xbad13bad; /* never reached */ } } __inline int64_t enet_at_atomic_cas(char *ptr, int64_t new_val, int64_t old_val, size_t size) { switch (size) { case 1: return _InterlockedCompareExchange8((volatile char *)ptr, (char)new_val, (char)old_val); case 2: return _InterlockedCompareExchange16((volatile SHORT *)ptr, (SHORT)new_val, (SHORT)old_val); case 4: #ifdef NOT_UNDERSCORED_INTERLOCKED_EXCHANGE return InterlockedCompareExchange((volatile LONG *)ptr, (LONG)new_val, (LONG)old_val); #else return _InterlockedCompareExchange((volatile LONG *)ptr, (LONG)new_val, (LONG)old_val); #endif case 8: #ifdef NOT_UNDERSCORED_INTERLOCKED_EXCHANGE return InterlockedCompareExchange64((volatile LONGLONG *)ptr, (LONGLONG)new_val, (LONGLONG)old_val); #else return _InterlockedCompareExchange64((volatile LONGLONG *)ptr, (LONGLONG)new_val, (LONGLONG)old_val); #endif default: return 0xbad13bad; /* never reached */ } } __inline int64_t enet_at_atomic_inc(char *ptr, int64_t delta, size_t data_size) { switch (data_size) { case 1: return _InterlockedExchangeAdd8((volatile char *)ptr, (char)delta); case 2: return _InterlockedExchangeAdd16((volatile SHORT *)ptr, (SHORT)delta); case 4: #ifdef NOT_UNDERSCORED_INTERLOCKED_EXCHANGE return InterlockedExchangeAdd((volatile LONG *)ptr, (LONG)delta); #else return _InterlockedExchangeAdd((volatile LONG *)ptr, (LONG)delta); #endif case 8: #ifdef NOT_UNDERSCORED_INTERLOCKED_EXCHANGE return InterlockedExchangeAdd64((volatile LONGLONG *)ptr, (LONGLONG)delta); #else return _InterlockedExchangeAdd64((volatile LONGLONG *)ptr, (LONGLONG)delta); #endif default: return 0xbad13bad; /* never reached */ } } #define ENET_ATOMIC_READ(variable) enet_at_atomic_read((char *)(variable), ENET_ATOMIC_SIZEOF(variable)) #define ENET_ATOMIC_WRITE(variable, new_val) \ enet_at_atomic_write((char *)(variable), (int64_t)(new_val), ENET_ATOMIC_SIZEOF(variable)) #define ENET_ATOMIC_CAS(variable, old_value, new_val) \ enet_at_atomic_cas((char *)(variable), (int64_t)(new_val), (int64_t)(old_value), \ ENET_ATOMIC_SIZEOF(variable)) #define ENET_ATOMIC_INC(variable) enet_at_atomic_inc((char *)(variable), 1, ENET_ATOMIC_SIZEOF(variable)) #define ENET_ATOMIC_DEC(variable) enet_at_atomic_inc((char *)(variable), -1, ENET_ATOMIC_SIZEOF(variable)) #define ENET_ATOMIC_INC_BY(variable, delta) \ enet_at_atomic_inc((char *)(variable), (delta), ENET_ATOMIC_SIZEOF(variable)) #define ENET_ATOMIC_DEC_BY(variable, delta) \ enet_at_atomic_inc((char *)(variable), -(delta), ENET_ATOMIC_SIZEOF(variable)) #elif defined(__GNUC__) || defined(__clang__) #if defined(__clang__) || (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7)) #define AT_HAVE_ATOMICS #endif /* We want to use __atomic built-ins if possible because the __sync primitives are deprecated, because the __atomic build-ins allow us to use ENET_ATOMIC_WRITE on uninitialized memory without running into undefined behavior, and because the __atomic versions generate more efficient code since we don't need to rely on CAS when we don't actually want it. Note that we use acquire-release memory order (like mutexes do). We could use sequentially consistent memory order but that has lower performance and is almost always unneeded. */ #ifdef AT_HAVE_ATOMICS #define ENET_ATOMIC_READ(ptr) __atomic_load_n((ptr), __ATOMIC_ACQUIRE) #define ENET_ATOMIC_WRITE(ptr, value) __atomic_store_n((ptr), (value), __ATOMIC_RELEASE) #ifndef typeof #define typeof __typeof__ #endif /* clang_analyzer doesn't know that CAS writes to memory so it complains about potentially lost data. Replace the code with the equivalent non-sync code. */ #ifdef __clang_analyzer__ #define ENET_ATOMIC_CAS(ptr, old_value, new_value) \ ({ \ typeof(*(ptr)) ENET_ATOMIC_CAS_old_actual_ = (*(ptr)); \ if (ATOMIC_CAS_old_actual_ == (old_value)) { \ *(ptr) = new_value; \ } \ ENET_ATOMIC_CAS_old_actual_; \ }) #else /* Could use __auto_type instead of typeof but that shouldn't work in C++. The ({ }) syntax is a GCC extension called statement expression. It lets us return a value out of the macro. TODO We should return bool here instead of the old value to avoid the ABA problem. */ #define ENET_ATOMIC_CAS(ptr, old_value, new_value) \ ({ \ typeof(*(ptr)) ENET_ATOMIC_CAS_expected_ = (old_value); \ __atomic_compare_exchange_n((ptr), &ENET_ATOMIC_CAS_expected_, (new_value), false, \ __ATOMIC_ACQ_REL, __ATOMIC_ACQUIRE); \ ENET_ATOMIC_CAS_expected_; \ }) #endif /* __clang_analyzer__ */ #define ENET_ATOMIC_INC(ptr) __atomic_fetch_add((ptr), 1, __ATOMIC_ACQ_REL) #define ENET_ATOMIC_DEC(ptr) __atomic_fetch_sub((ptr), 1, __ATOMIC_ACQ_REL) #define ENET_ATOMIC_INC_BY(ptr, delta) __atomic_fetch_add((ptr), (delta), __ATOMIC_ACQ_REL) #define ENET_ATOMIC_DEC_BY(ptr, delta) __atomic_fetch_sub((ptr), (delta), __ATOMIC_ACQ_REL) #else #define ENET_ATOMIC_READ(variable) __sync_fetch_and_add(variable, 0) #define ENET_ATOMIC_WRITE(variable, new_val) \ (void) __sync_val_compare_and_swap((variable), *(variable), (new_val)) #define ENET_ATOMIC_CAS(variable, old_value, new_val) \ __sync_val_compare_and_swap((variable), (old_value), (new_val)) #define ENET_ATOMIC_INC(variable) __sync_fetch_and_add((variable), 1) #define ENET_ATOMIC_DEC(variable) __sync_fetch_and_sub((variable), 1) #define ENET_ATOMIC_INC_BY(variable, delta) __sync_fetch_and_add((variable), (delta), 1) #define ENET_ATOMIC_DEC_BY(variable, delta) __sync_fetch_and_sub((variable), (delta), 1) #endif /* AT_HAVE_ATOMICS */ #undef AT_HAVE_ATOMICS #else //< @r-lyeh: add __TINYC__ stubs. not going to work. #define ENET_ATOMIC_READ(variable) (*(int64_t *)(variable)) #define ENET_ATOMIC_WRITE(variable, new_val) (*(int64_t *)(variable) = (int64_t)(new_val)) #define ENET_ATOMIC_CAS(variable, old_value, new_val) (*(int64_t *)(variable) = (int64_t)(new_val)) #define ENET_ATOMIC_INC(variable) ENET_ATOMIC_INC_BY(variable, 1) #define ENET_ATOMIC_DEC(variable) ENET_ATOMIC_DEC_BY(variable, 1) #define ENET_ATOMIC_INC_BY(variable, delta) ( *(int64_t*)(variable) = (int64_t*)(variable) + (delta)) #define ENET_ATOMIC_DEC_BY(variable, delta) ( *(int64_t*)(variable) = (int64_t*)(variable) - (delta)) #endif /* defined(_MSC_VER) */ // =======================================================================// // ! // ! Callbacks // ! // =======================================================================// ENetCallbacks callbacks = { malloc, free, abort, enet_packet_create, enet_packet_destroy }; int enet_initialize_with_callbacks(ENetVersion version, const ENetCallbacks *inits) { if (version < ENET_VERSION_CREATE(1, 3, 0)) { return -1; } if (inits->malloc != NULL || inits->free != NULL) { if (inits->malloc == NULL || inits->free == NULL) { return -1; } callbacks.malloc = inits->malloc; callbacks.free = inits->free; } if (inits->no_memory != NULL) { callbacks.no_memory = inits->no_memory; } if (inits->packet_create != NULL || inits->packet_destroy != NULL) { if (inits->packet_create == NULL || inits->packet_destroy == NULL) { return -1; } callbacks.packet_create = inits->packet_create; callbacks.packet_destroy = inits->packet_destroy; } return enet_initialize(); } ENetVersion enet_linked_version(void) { return ENET_VERSION; } void * enet_malloc(size_t size) { void *memory = callbacks.malloc(size); if (memory == NULL) { callbacks.no_memory(); } return memory; } void enet_free(void *memory) { callbacks.free(memory); } // =======================================================================// // ! // ! List // ! // =======================================================================// void enet_list_clear(ENetList *list) { list->sentinel.next = &list->sentinel; list->sentinel.previous = &list->sentinel; } ENetListIterator enet_list_insert(ENetListIterator position, void *data) { ENetListIterator result = (ENetListIterator)data; result->previous = position->previous; result->next = position; result->previous->next = result; position->previous = result; return result; } void *enet_list_remove(ENetListIterator position) { position->previous->next = position->next; position->next->previous = position->previous; return position; } ENetListIterator enet_list_move(ENetListIterator position, void *dataFirst, void *dataLast) { ENetListIterator first = (ENetListIterator)dataFirst; ENetListIterator last = (ENetListIterator)dataLast; first->previous->next = last->next; last->next->previous = first->previous; first->previous = position->previous; last->next = position; first->previous->next = first; position->previous = last; return first; } size_t enet_list_size(ENetList *list) { size_t size = 0; ENetListIterator position; for (position = enet_list_begin(list); position != enet_list_end(list); position = enet_list_next(position)) { ++size; } return size; } // =======================================================================// // ! // ! Packet // ! // =======================================================================// /** * Creates a packet that may be sent to a peer. * @param data initial contents of the packet's data; the packet's data will remain uninitialized if data is NULL. * @param dataLength size of the data allocated for this packet * @param flags flags for this packet as described for the ENetPacket structure. * @returns the packet on success, NULL on failure */ ENetPacket *enet_packet_create(const void *data, size_t dataLength, enet_uint32 flags) { ENetPacket *packet; if (flags & ENET_PACKET_FLAG_NO_ALLOCATE) { packet = (ENetPacket *)enet_malloc(sizeof (ENetPacket)); if (packet == NULL) { return NULL; } packet->data = (enet_uint8 *)data; } else { packet = (ENetPacket *)enet_malloc(sizeof (ENetPacket) + dataLength); if (packet == NULL) { return NULL; } packet->data = (enet_uint8 *)packet + sizeof(ENetPacket); if (data != NULL) { memcpy(packet->data, data, dataLength); } } packet->referenceCount = 0; packet->flags = flags; packet->dataLength = dataLength; packet->freeCallback = NULL; packet->userData = NULL; return packet; } ENetPacket *enet_packet_create_offset(const void *data, size_t dataLength, size_t dataOffset, enet_uint32 flags) { ENetPacket *packet; if (flags & ENET_PACKET_FLAG_NO_ALLOCATE) { packet = (ENetPacket *)enet_malloc(sizeof (ENetPacket)); if (packet == NULL) { return NULL; } packet->data = (enet_uint8 *)data; } else { packet = (ENetPacket *)enet_malloc(sizeof (ENetPacket) + dataLength + dataOffset); if (packet == NULL) { return NULL; } packet->data = (enet_uint8 *)packet + sizeof(ENetPacket); if (data != NULL) { memcpy(packet->data + dataOffset, data, dataLength); } } packet->referenceCount = 0; packet->flags = flags; packet->dataLength = dataLength + dataOffset; packet->freeCallback = NULL; packet->userData = NULL; return packet; } ENetPacket *enet_packet_copy(ENetPacket *packet) { return enet_packet_create(packet->data, packet->dataLength, packet->flags); } /** * Destroys the packet and deallocates its data. * @param packet packet to be destroyed */ void enet_packet_destroy(ENetPacket *packet) { if (packet == NULL) { return; } if (packet->freeCallback != NULL) { (*packet->freeCallback)((void *)packet); } enet_free(packet); } static int initializedCRC32 = 0; static enet_uint32 crcTable[256]; static enet_uint32 reflect_crc(int val, int bits) { int result = 0, bit; for (bit = 0; bit < bits; bit++) { if (val & 1) { result |= 1 << (bits - 1 - bit); } val >>= 1; } return result; } static void initialize_crc32(void) { int byte; for (byte = 0; byte < 256; ++byte) { enet_uint32 crc = reflect_crc(byte, 8) << 24; int offset; for (offset = 0; offset < 8; ++offset) { if (crc & 0x80000000) { crc = (crc << 1) ^ 0x04c11db7; } else { crc <<= 1; } } crcTable[byte] = reflect_crc(crc, 32); } initializedCRC32 = 1; } enet_uint32 enet_crc32(const ENetBuffer *buffers, size_t bufferCount) { enet_uint32 crc = 0xFFFFFFFF; if (!initializedCRC32) { initialize_crc32(); } while (bufferCount-- > 0) { const enet_uint8 *data = (const enet_uint8 *)buffers->data; const enet_uint8 *dataEnd = &data[buffers->dataLength]; while (data < dataEnd) { crc = (crc >> 8) ^ crcTable[(crc & 0xFF) ^ *data++]; } ++buffers; } return ENET_HOST_TO_NET_32(~crc); } // =======================================================================// // ! // ! Protocol // ! // =======================================================================// static size_t commandSizes[ENET_PROTOCOL_COMMAND_COUNT] = { 0, sizeof(ENetProtocolAcknowledge), sizeof(ENetProtocolConnect), sizeof(ENetProtocolVerifyConnect), sizeof(ENetProtocolDisconnect), sizeof(ENetProtocolPing), sizeof(ENetProtocolSendReliable), sizeof(ENetProtocolSendUnreliable), sizeof(ENetProtocolSendFragment), sizeof(ENetProtocolSendUnsequenced), sizeof(ENetProtocolBandwidthLimit), sizeof(ENetProtocolThrottleConfigure), sizeof(ENetProtocolSendFragment) }; size_t enet_protocol_command_size(enet_uint8 commandNumber) { return commandSizes[commandNumber & ENET_PROTOCOL_COMMAND_MASK]; } static void enet_protocol_change_state(ENetHost *host, ENetPeer *peer, ENetPeerState state) { ENET_UNUSED(host) if (state == ENET_PEER_STATE_CONNECTED || state == ENET_PEER_STATE_DISCONNECT_LATER) { enet_peer_on_connect(peer); } else { enet_peer_on_disconnect(peer); } peer->state = state; } static void enet_protocol_dispatch_state(ENetHost *host, ENetPeer *peer, ENetPeerState state) { enet_protocol_change_state(host, peer, state); if (!peer->needsDispatch) { enet_list_insert(enet_list_end(&host->dispatchQueue), &peer->dispatchList); peer->needsDispatch = 1; } } static int enet_protocol_dispatch_incoming_commands(ENetHost *host, ENetEvent *event) { while (!enet_list_empty(&host->dispatchQueue)) { ENetPeer *peer = (ENetPeer *) enet_list_remove(enet_list_begin(&host->dispatchQueue)); peer->needsDispatch = 0; switch (peer->state) { case ENET_PEER_STATE_CONNECTION_PENDING: case ENET_PEER_STATE_CONNECTION_SUCCEEDED: enet_protocol_change_state(host, peer, ENET_PEER_STATE_CONNECTED); event->type = ENET_EVENT_TYPE_CONNECT; event->peer = peer; event->data = peer->eventData; return 1; case ENET_PEER_STATE_ZOMBIE: host->recalculateBandwidthLimits = 1; event->type = ENET_EVENT_TYPE_DISCONNECT; event->peer = peer; event->data = peer->eventData; enet_peer_reset(peer); return 1; case ENET_PEER_STATE_CONNECTED: if (enet_list_empty(&peer->dispatchedCommands)) { continue; } event->packet = enet_peer_receive(peer, &event->channelID); if (event->packet == NULL) { continue; } event->type = ENET_EVENT_TYPE_RECEIVE; event->peer = peer; if (!enet_list_empty(&peer->dispatchedCommands)) { peer->needsDispatch = 1; enet_list_insert(enet_list_end(&host->dispatchQueue), &peer->dispatchList); } return 1; default: break; } } return 0; } /* enet_protocol_dispatch_incoming_commands */ static void enet_protocol_notify_connect(ENetHost *host, ENetPeer *peer, ENetEvent *event) { host->recalculateBandwidthLimits = 1; if (event != NULL) { enet_protocol_change_state(host, peer, ENET_PEER_STATE_CONNECTED); peer->totalDataSent = 0; peer->totalDataReceived = 0; peer->totalPacketsSent = 0; peer->totalPacketsLost = 0; event->type = ENET_EVENT_TYPE_CONNECT; event->peer = peer; event->data = peer->eventData; } else { enet_protocol_dispatch_state(host, peer, peer->state == ENET_PEER_STATE_CONNECTING ? ENET_PEER_STATE_CONNECTION_SUCCEEDED : ENET_PEER_STATE_CONNECTION_PENDING); } } static void enet_protocol_notify_disconnect(ENetHost *host, ENetPeer *peer, ENetEvent *event) { if (peer->state >= ENET_PEER_STATE_CONNECTION_PENDING) { host->recalculateBandwidthLimits = 1; } if (peer->state != ENET_PEER_STATE_CONNECTING && peer->state < ENET_PEER_STATE_CONNECTION_SUCCEEDED) { enet_peer_reset(peer); } else if (event != NULL) { event->type = ENET_EVENT_TYPE_DISCONNECT; event->peer = peer; event->data = 0; enet_peer_reset(peer); } else { peer->eventData = 0; enet_protocol_dispatch_state(host, peer, ENET_PEER_STATE_ZOMBIE); } } static void enet_protocol_notify_disconnect_timeout (ENetHost * host, ENetPeer * peer, ENetEvent * event) { if (peer->state >= ENET_PEER_STATE_CONNECTION_PENDING) { host->recalculateBandwidthLimits = 1; } if (peer->state != ENET_PEER_STATE_CONNECTING && peer->state < ENET_PEER_STATE_CONNECTION_SUCCEEDED) { enet_peer_reset (peer); } else if (event != NULL) { event->type = ENET_EVENT_TYPE_DISCONNECT_TIMEOUT; event->peer = peer; event->data = 0; enet_peer_reset(peer); } else { peer->eventData = 0; enet_protocol_dispatch_state(host, peer, ENET_PEER_STATE_ZOMBIE); } } static void enet_protocol_remove_sent_unreliable_commands(ENetPeer *peer) { ENetOutgoingCommand *outgoingCommand; while (!enet_list_empty(&peer->sentUnreliableCommands)) { outgoingCommand = (ENetOutgoingCommand *) enet_list_front(&peer->sentUnreliableCommands); enet_list_remove(&outgoingCommand->outgoingCommandList); if (outgoingCommand->packet != NULL) { --outgoingCommand->packet->referenceCount; if (outgoingCommand->packet->referenceCount == 0) { outgoingCommand->packet->flags |= ENET_PACKET_FLAG_SENT; callbacks.packet_destroy(outgoingCommand->packet); } } enet_free(outgoingCommand); } } static ENetProtocolCommand enet_protocol_remove_sent_reliable_command(ENetPeer *peer, enet_uint16 reliableSequenceNumber, enet_uint8 channelID) { ENetOutgoingCommand *outgoingCommand = NULL; ENetListIterator currentCommand; ENetProtocolCommand commandNumber; int wasSent = 1; for (currentCommand = enet_list_begin(&peer->sentReliableCommands); currentCommand != enet_list_end(&peer->sentReliableCommands); currentCommand = enet_list_next(currentCommand) ) { outgoingCommand = (ENetOutgoingCommand *) currentCommand; if (outgoingCommand->reliableSequenceNumber == reliableSequenceNumber && outgoingCommand->command.header.channelID == channelID) { break; } } if (currentCommand == enet_list_end(&peer->sentReliableCommands)) { for (currentCommand = enet_list_begin(&peer->outgoingReliableCommands); currentCommand != enet_list_end(&peer->outgoingReliableCommands); currentCommand = enet_list_next(currentCommand) ) { outgoingCommand = (ENetOutgoingCommand *) currentCommand; if (outgoingCommand->sendAttempts < 1) { return ENET_PROTOCOL_COMMAND_NONE; } if (outgoingCommand->reliableSequenceNumber == reliableSequenceNumber && outgoingCommand->command.header.channelID == channelID) { break; } } if (currentCommand == enet_list_end(&peer->outgoingReliableCommands)) { return ENET_PROTOCOL_COMMAND_NONE; } wasSent = 0; } if (outgoingCommand == NULL) { return ENET_PROTOCOL_COMMAND_NONE; } if (channelID < peer->channelCount) { ENetChannel *channel = &peer->channels[channelID]; enet_uint16 reliableWindow = reliableSequenceNumber / ENET_PEER_RELIABLE_WINDOW_SIZE; if (channel->reliableWindows[reliableWindow] > 0) { --channel->reliableWindows[reliableWindow]; if (!channel->reliableWindows[reliableWindow]) { channel->usedReliableWindows &= ~(1 << reliableWindow); } } } commandNumber = (ENetProtocolCommand) (outgoingCommand->command.header.command & ENET_PROTOCOL_COMMAND_MASK); enet_list_remove(&outgoingCommand->outgoingCommandList); if (outgoingCommand->packet != NULL) { if (wasSent) { peer->reliableDataInTransit -= outgoingCommand->fragmentLength; } --outgoingCommand->packet->referenceCount; if (outgoingCommand->packet->referenceCount == 0) { outgoingCommand->packet->flags |= ENET_PACKET_FLAG_SENT; callbacks.packet_destroy(outgoingCommand->packet); } } enet_free(outgoingCommand); if (enet_list_empty(&peer->sentReliableCommands)) { return commandNumber; } outgoingCommand = (ENetOutgoingCommand *) enet_list_front(&peer->sentReliableCommands); peer->nextTimeout = outgoingCommand->sentTime + outgoingCommand->roundTripTimeout; return commandNumber; } /* enet_protocol_remove_sent_reliable_command */ static ENetPeer * enet_protocol_handle_connect(ENetHost *host, ENetProtocolHeader *header, ENetProtocol *command) { ENET_UNUSED(header) enet_uint8 incomingSessionID, outgoingSessionID; enet_uint32 mtu, windowSize; ENetChannel *channel; size_t channelCount, duplicatePeers = 0; ENetPeer *currentPeer, *peer = NULL; ENetProtocol verifyCommand; channelCount = ENET_NET_TO_HOST_32(command->connect.channelCount); if (channelCount < ENET_PROTOCOL_MINIMUM_CHANNEL_COUNT || channelCount > ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT) { return NULL; } for (currentPeer = host->peers; currentPeer < &host->peers[host->peerCount]; ++currentPeer) { if (currentPeer->state == ENET_PEER_STATE_DISCONNECTED) { if (peer == NULL) { peer = currentPeer; } } else if (currentPeer->state != ENET_PEER_STATE_CONNECTING && in6_equal(currentPeer->address.host, host->receivedAddress.host)) { if (currentPeer->address.port == host->receivedAddress.port && currentPeer->connectID == command->connect.connectID) { return NULL; } ++duplicatePeers; } } if (peer == NULL || duplicatePeers >= host->duplicatePeers) { return NULL; } if (channelCount > host->channelLimit) { channelCount = host->channelLimit; } peer->channels = (ENetChannel *) enet_malloc(channelCount * sizeof(ENetChannel)); if (peer->channels == NULL) { return NULL; } peer->channelCount = channelCount; peer->state = ENET_PEER_STATE_ACKNOWLEDGING_CONNECT; peer->connectID = command->connect.connectID; peer->address = host->receivedAddress; peer->outgoingPeerID = ENET_NET_TO_HOST_16(command->connect.outgoingPeerID); peer->incomingBandwidth = ENET_NET_TO_HOST_32(command->connect.incomingBandwidth); peer->outgoingBandwidth = ENET_NET_TO_HOST_32(command->connect.outgoingBandwidth); peer->packetThrottleInterval = ENET_NET_TO_HOST_32(command->connect.packetThrottleInterval); peer->packetThrottleAcceleration = ENET_NET_TO_HOST_32(command->connect.packetThrottleAcceleration); peer->packetThrottleDeceleration = ENET_NET_TO_HOST_32(command->connect.packetThrottleDeceleration); peer->eventData = ENET_NET_TO_HOST_32(command->connect.data); incomingSessionID = command->connect.incomingSessionID == 0xFF ? peer->outgoingSessionID : command->connect.incomingSessionID; incomingSessionID = (incomingSessionID + 1) & (ENET_PROTOCOL_HEADER_SESSION_MASK >> ENET_PROTOCOL_HEADER_SESSION_SHIFT); if (incomingSessionID == peer->outgoingSessionID) { incomingSessionID = (incomingSessionID + 1) & (ENET_PROTOCOL_HEADER_SESSION_MASK >> ENET_PROTOCOL_HEADER_SESSION_SHIFT); } peer->outgoingSessionID = incomingSessionID; outgoingSessionID = command->connect.outgoingSessionID == 0xFF ? peer->incomingSessionID : command->connect.outgoingSessionID; outgoingSessionID = (outgoingSessionID + 1) & (ENET_PROTOCOL_HEADER_SESSION_MASK >> ENET_PROTOCOL_HEADER_SESSION_SHIFT); if (outgoingSessionID == peer->incomingSessionID) { outgoingSessionID = (outgoingSessionID + 1) & (ENET_PROTOCOL_HEADER_SESSION_MASK >> ENET_PROTOCOL_HEADER_SESSION_SHIFT); } peer->incomingSessionID = outgoingSessionID; for (channel = peer->channels; channel < &peer->channels[channelCount]; ++channel) { channel->outgoingReliableSequenceNumber = 0; channel->outgoingUnreliableSequenceNumber = 0; channel->incomingReliableSequenceNumber = 0; channel->incomingUnreliableSequenceNumber = 0; enet_list_clear(&channel->incomingReliableCommands); enet_list_clear(&channel->incomingUnreliableCommands); channel->usedReliableWindows = 0; memset(channel->reliableWindows, 0, sizeof(channel->reliableWindows)); } mtu = ENET_NET_TO_HOST_32(command->connect.mtu); if (mtu < ENET_PROTOCOL_MINIMUM_MTU) { mtu = ENET_PROTOCOL_MINIMUM_MTU; } else if (mtu > ENET_PROTOCOL_MAXIMUM_MTU) { mtu = ENET_PROTOCOL_MAXIMUM_MTU; } peer->mtu = mtu; if (host->outgoingBandwidth == 0 && peer->incomingBandwidth == 0) { peer->windowSize = ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE; } else if (host->outgoingBandwidth == 0 || peer->incomingBandwidth == 0) { peer->windowSize = (ENET_MAX(host->outgoingBandwidth, peer->incomingBandwidth) / ENET_PEER_WINDOW_SIZE_SCALE) * ENET_PROTOCOL_MINIMUM_WINDOW_SIZE; } else { peer->windowSize = (ENET_MIN(host->outgoingBandwidth, peer->incomingBandwidth) / ENET_PEER_WINDOW_SIZE_SCALE) * ENET_PROTOCOL_MINIMUM_WINDOW_SIZE; } if (peer->windowSize < ENET_PROTOCOL_MINIMUM_WINDOW_SIZE) { peer->windowSize = ENET_PROTOCOL_MINIMUM_WINDOW_SIZE; } else if (peer->windowSize > ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE) { peer->windowSize = ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE; } if (host->incomingBandwidth == 0) { windowSize = ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE; } else { windowSize = (host->incomingBandwidth / ENET_PEER_WINDOW_SIZE_SCALE) * ENET_PROTOCOL_MINIMUM_WINDOW_SIZE; } if (windowSize > ENET_NET_TO_HOST_32(command->connect.windowSize)) { windowSize = ENET_NET_TO_HOST_32(command->connect.windowSize); } if (windowSize < ENET_PROTOCOL_MINIMUM_WINDOW_SIZE) { windowSize = ENET_PROTOCOL_MINIMUM_WINDOW_SIZE; } else if (windowSize > ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE) { windowSize = ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE; } verifyCommand.header.command = ENET_PROTOCOL_COMMAND_VERIFY_CONNECT | ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE; verifyCommand.header.channelID = 0xFF; verifyCommand.verifyConnect.outgoingPeerID = ENET_HOST_TO_NET_16(peer->incomingPeerID); verifyCommand.verifyConnect.incomingSessionID = incomingSessionID; verifyCommand.verifyConnect.outgoingSessionID = outgoingSessionID; verifyCommand.verifyConnect.mtu = ENET_HOST_TO_NET_32(peer->mtu); verifyCommand.verifyConnect.windowSize = ENET_HOST_TO_NET_32(windowSize); verifyCommand.verifyConnect.channelCount = ENET_HOST_TO_NET_32(channelCount); verifyCommand.verifyConnect.incomingBandwidth = ENET_HOST_TO_NET_32(host->incomingBandwidth); verifyCommand.verifyConnect.outgoingBandwidth = ENET_HOST_TO_NET_32(host->outgoingBandwidth); verifyCommand.verifyConnect.packetThrottleInterval = ENET_HOST_TO_NET_32(peer->packetThrottleInterval); verifyCommand.verifyConnect.packetThrottleAcceleration = ENET_HOST_TO_NET_32(peer->packetThrottleAcceleration); verifyCommand.verifyConnect.packetThrottleDeceleration = ENET_HOST_TO_NET_32(peer->packetThrottleDeceleration); verifyCommand.verifyConnect.connectID = peer->connectID; enet_peer_queue_outgoing_command(peer, &verifyCommand, NULL, 0, 0); return peer; } /* enet_protocol_handle_connect */ static int enet_protocol_handle_send_reliable(ENetHost *host, ENetPeer *peer, const ENetProtocol *command, enet_uint8 **currentData) { size_t dataLength; if (command->header.channelID >= peer->channelCount || (peer->state != ENET_PEER_STATE_CONNECTED && peer->state != ENET_PEER_STATE_DISCONNECT_LATER)) { return -1; } dataLength = ENET_NET_TO_HOST_16(command->sendReliable.dataLength); *currentData += dataLength; if (dataLength > host->maximumPacketSize || *currentData < host->receivedData || *currentData > &host->receivedData[host->receivedDataLength]) { return -1; } if (enet_peer_queue_incoming_command(peer, command, (const enet_uint8 *) command + sizeof(ENetProtocolSendReliable), dataLength, ENET_PACKET_FLAG_RELIABLE, 0) == NULL) { return -1; } return 0; } static int enet_protocol_handle_send_unsequenced(ENetHost *host, ENetPeer *peer, const ENetProtocol *command, enet_uint8 **currentData) { enet_uint32 unsequencedGroup, index; size_t dataLength; if (command->header.channelID >= peer->channelCount || (peer->state != ENET_PEER_STATE_CONNECTED && peer->state != ENET_PEER_STATE_DISCONNECT_LATER)) { return -1; } dataLength = ENET_NET_TO_HOST_16(command->sendUnsequenced.dataLength); *currentData += dataLength; if (dataLength > host->maximumPacketSize || *currentData < host->receivedData || *currentData > &host->receivedData[host->receivedDataLength]) { return -1; } unsequencedGroup = ENET_NET_TO_HOST_16(command->sendUnsequenced.unsequencedGroup); index = unsequencedGroup % ENET_PEER_UNSEQUENCED_WINDOW_SIZE; if (unsequencedGroup < peer->incomingUnsequencedGroup) { unsequencedGroup += 0x10000; } if (unsequencedGroup >= (enet_uint32) peer->incomingUnsequencedGroup + ENET_PEER_FREE_UNSEQUENCED_WINDOWS * ENET_PEER_UNSEQUENCED_WINDOW_SIZE) { return 0; } unsequencedGroup &= 0xFFFF; if (unsequencedGroup - index != peer->incomingUnsequencedGroup) { peer->incomingUnsequencedGroup = unsequencedGroup - index; memset(peer->unsequencedWindow, 0, sizeof(peer->unsequencedWindow)); } else if (peer->unsequencedWindow[index / 32] & (1 << (index % 32))) { return 0; } if (enet_peer_queue_incoming_command(peer, command, (const enet_uint8 *) command + sizeof(ENetProtocolSendUnsequenced), dataLength, ENET_PACKET_FLAG_UNSEQUENCED,0) == NULL) { return -1; } peer->unsequencedWindow[index / 32] |= 1 << (index % 32); return 0; } /* enet_protocol_handle_send_unsequenced */ static int enet_protocol_handle_send_unreliable(ENetHost *host, ENetPeer *peer, const ENetProtocol *command, enet_uint8 **currentData) { size_t dataLength; if (command->header.channelID >= peer->channelCount || (peer->state != ENET_PEER_STATE_CONNECTED && peer->state != ENET_PEER_STATE_DISCONNECT_LATER)) { return -1; } dataLength = ENET_NET_TO_HOST_16(command->sendUnreliable.dataLength); *currentData += dataLength; if (dataLength > host->maximumPacketSize || *currentData < host->receivedData || *currentData > &host->receivedData[host->receivedDataLength]) { return -1; } if (enet_peer_queue_incoming_command(peer, command, (const enet_uint8 *) command + sizeof(ENetProtocolSendUnreliable), dataLength, 0, 0) == NULL) { return -1; } return 0; } static int enet_protocol_handle_send_fragment(ENetHost *host, ENetPeer *peer, const ENetProtocol *command, enet_uint8 **currentData) { enet_uint32 fragmentNumber, fragmentCount, fragmentOffset, fragmentLength, startSequenceNumber, totalLength; ENetChannel *channel; enet_uint16 startWindow, currentWindow; ENetListIterator currentCommand; ENetIncomingCommand *startCommand = NULL; if (command->header.channelID >= peer->channelCount || (peer->state != ENET_PEER_STATE_CONNECTED && peer->state != ENET_PEER_STATE_DISCONNECT_LATER)) { return -1; } fragmentLength = ENET_NET_TO_HOST_16(command->sendFragment.dataLength); *currentData += fragmentLength; if (fragmentLength > host->maximumPacketSize || *currentData < host->receivedData || *currentData > &host->receivedData[host->receivedDataLength]) { return -1; } channel = &peer->channels[command->header.channelID]; startSequenceNumber = ENET_NET_TO_HOST_16(command->sendFragment.startSequenceNumber); startWindow = startSequenceNumber / ENET_PEER_RELIABLE_WINDOW_SIZE; currentWindow = channel->incomingReliableSequenceNumber / ENET_PEER_RELIABLE_WINDOW_SIZE; if (startSequenceNumber < channel->incomingReliableSequenceNumber) { startWindow += ENET_PEER_RELIABLE_WINDOWS; } if (startWindow < currentWindow || startWindow >= currentWindow + ENET_PEER_FREE_RELIABLE_WINDOWS - 1) { return 0; } fragmentNumber = ENET_NET_TO_HOST_32(command->sendFragment.fragmentNumber); fragmentCount = ENET_NET_TO_HOST_32(command->sendFragment.fragmentCount); fragmentOffset = ENET_NET_TO_HOST_32(command->sendFragment.fragmentOffset); totalLength = ENET_NET_TO_HOST_32(command->sendFragment.totalLength); if (fragmentCount > ENET_PROTOCOL_MAXIMUM_FRAGMENT_COUNT || fragmentNumber >= fragmentCount || totalLength > host->maximumPacketSize || fragmentOffset >= totalLength || fragmentLength > totalLength - fragmentOffset ) { return -1; } for (currentCommand = enet_list_previous(enet_list_end(&channel->incomingReliableCommands)); currentCommand != enet_list_end(&channel->incomingReliableCommands); currentCommand = enet_list_previous(currentCommand) ) { ENetIncomingCommand *incomingCommand = (ENetIncomingCommand *) currentCommand; if (startSequenceNumber >= channel->incomingReliableSequenceNumber) { if (incomingCommand->reliableSequenceNumber < channel->incomingReliableSequenceNumber) { continue; } } else if (incomingCommand->reliableSequenceNumber >= channel->incomingReliableSequenceNumber) { break; } if (incomingCommand->reliableSequenceNumber <= startSequenceNumber) { if (incomingCommand->reliableSequenceNumber < startSequenceNumber) { break; } if ((incomingCommand->command.header.command & ENET_PROTOCOL_COMMAND_MASK) != ENET_PROTOCOL_COMMAND_SEND_FRAGMENT || totalLength != incomingCommand->packet->dataLength || fragmentCount != incomingCommand->fragmentCount ) { return -1; } startCommand = incomingCommand; break; } } if (startCommand == NULL) { ENetProtocol hostCommand = *command; hostCommand.header.reliableSequenceNumber = startSequenceNumber; startCommand = enet_peer_queue_incoming_command(peer, &hostCommand, NULL, totalLength, ENET_PACKET_FLAG_RELIABLE, fragmentCount); if (startCommand == NULL) { return -1; } } if ((startCommand->fragments[fragmentNumber / 32] & (1 << (fragmentNumber % 32))) == 0) { --startCommand->fragmentsRemaining; startCommand->fragments[fragmentNumber / 32] |= (1 << (fragmentNumber % 32)); if (fragmentOffset + fragmentLength > startCommand->packet->dataLength) { fragmentLength = startCommand->packet->dataLength - fragmentOffset; } memcpy(startCommand->packet->data + fragmentOffset, (enet_uint8 *) command + sizeof(ENetProtocolSendFragment), fragmentLength); if (startCommand->fragmentsRemaining <= 0) { enet_peer_dispatch_incoming_reliable_commands(peer, channel); } } return 0; } /* enet_protocol_handle_send_fragment */ static int enet_protocol_handle_send_unreliable_fragment(ENetHost *host, ENetPeer *peer, const ENetProtocol *command, enet_uint8 **currentData) { enet_uint32 fragmentNumber, fragmentCount, fragmentOffset, fragmentLength, reliableSequenceNumber, startSequenceNumber, totalLength; enet_uint16 reliableWindow, currentWindow; ENetChannel *channel; ENetListIterator currentCommand; ENetIncomingCommand *startCommand = NULL; if (command->header.channelID >= peer->channelCount || (peer->state != ENET_PEER_STATE_CONNECTED && peer->state != ENET_PEER_STATE_DISCONNECT_LATER)) { return -1; } fragmentLength = ENET_NET_TO_HOST_16(command->sendFragment.dataLength); *currentData += fragmentLength; if (fragmentLength > host->maximumPacketSize || *currentData < host->receivedData || *currentData > &host->receivedData[host->receivedDataLength]) { return -1; } channel = &peer->channels[command->header.channelID]; reliableSequenceNumber = command->header.reliableSequenceNumber; startSequenceNumber = ENET_NET_TO_HOST_16(command->sendFragment.startSequenceNumber); reliableWindow = reliableSequenceNumber / ENET_PEER_RELIABLE_WINDOW_SIZE; currentWindow = channel->incomingReliableSequenceNumber / ENET_PEER_RELIABLE_WINDOW_SIZE; if (reliableSequenceNumber < channel->incomingReliableSequenceNumber) { reliableWindow += ENET_PEER_RELIABLE_WINDOWS; } if (reliableWindow < currentWindow || reliableWindow >= currentWindow + ENET_PEER_FREE_RELIABLE_WINDOWS - 1) { return 0; } if (reliableSequenceNumber == channel->incomingReliableSequenceNumber && startSequenceNumber <= channel->incomingUnreliableSequenceNumber) { return 0; } fragmentNumber = ENET_NET_TO_HOST_32(command->sendFragment.fragmentNumber); fragmentCount = ENET_NET_TO_HOST_32(command->sendFragment.fragmentCount); fragmentOffset = ENET_NET_TO_HOST_32(command->sendFragment.fragmentOffset); totalLength = ENET_NET_TO_HOST_32(command->sendFragment.totalLength); if (fragmentCount > ENET_PROTOCOL_MAXIMUM_FRAGMENT_COUNT || fragmentNumber >= fragmentCount || totalLength > host->maximumPacketSize || fragmentOffset >= totalLength || fragmentLength > totalLength - fragmentOffset ) { return -1; } for (currentCommand = enet_list_previous(enet_list_end(&channel->incomingUnreliableCommands)); currentCommand != enet_list_end(&channel->incomingUnreliableCommands); currentCommand = enet_list_previous(currentCommand) ) { ENetIncomingCommand *incomingCommand = (ENetIncomingCommand *) currentCommand; if (reliableSequenceNumber >= channel->incomingReliableSequenceNumber) { if (incomingCommand->reliableSequenceNumber < channel->incomingReliableSequenceNumber) { continue; } } else if (incomingCommand->reliableSequenceNumber >= channel->incomingReliableSequenceNumber) { break; } if (incomingCommand->reliableSequenceNumber < reliableSequenceNumber) { break; } if (incomingCommand->reliableSequenceNumber > reliableSequenceNumber) { continue; } if (incomingCommand->unreliableSequenceNumber <= startSequenceNumber) { if (incomingCommand->unreliableSequenceNumber < startSequenceNumber) { break; } if ((incomingCommand->command.header.command & ENET_PROTOCOL_COMMAND_MASK) != ENET_PROTOCOL_COMMAND_SEND_UNRELIABLE_FRAGMENT || totalLength != incomingCommand->packet->dataLength || fragmentCount != incomingCommand->fragmentCount ) { return -1; } startCommand = incomingCommand; break; } } if (startCommand == NULL) { startCommand = enet_peer_queue_incoming_command(peer, command, NULL, totalLength, ENET_PACKET_FLAG_UNRELIABLE_FRAGMENT, fragmentCount); if (startCommand == NULL) { return -1; } } if ((startCommand->fragments[fragmentNumber / 32] & (1 << (fragmentNumber % 32))) == 0) { --startCommand->fragmentsRemaining; startCommand->fragments[fragmentNumber / 32] |= (1 << (fragmentNumber % 32)); if (fragmentOffset + fragmentLength > startCommand->packet->dataLength) { fragmentLength = startCommand->packet->dataLength - fragmentOffset; } memcpy(startCommand->packet->data + fragmentOffset, (enet_uint8 *) command + sizeof(ENetProtocolSendFragment), fragmentLength); if (startCommand->fragmentsRemaining <= 0) { enet_peer_dispatch_incoming_unreliable_commands(peer, channel); } } return 0; } /* enet_protocol_handle_send_unreliable_fragment */ static int enet_protocol_handle_ping(ENetHost *host, ENetPeer *peer, const ENetProtocol *command) { ENET_UNUSED(host) ENET_UNUSED(command) if (peer->state != ENET_PEER_STATE_CONNECTED && peer->state != ENET_PEER_STATE_DISCONNECT_LATER) { return -1; } return 0; } static int enet_protocol_handle_bandwidth_limit(ENetHost *host, ENetPeer *peer, const ENetProtocol *command) { if (peer->state != ENET_PEER_STATE_CONNECTED && peer->state != ENET_PEER_STATE_DISCONNECT_LATER) { return -1; } if (peer->incomingBandwidth != 0) { --host->bandwidthLimitedPeers; } peer->incomingBandwidth = ENET_NET_TO_HOST_32(command->bandwidthLimit.incomingBandwidth); peer->outgoingBandwidth = ENET_NET_TO_HOST_32(command->bandwidthLimit.outgoingBandwidth); if (peer->incomingBandwidth != 0) { ++host->bandwidthLimitedPeers; } if (peer->incomingBandwidth == 0 && host->outgoingBandwidth == 0) { peer->windowSize = ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE; } else if (peer->incomingBandwidth == 0 || host->outgoingBandwidth == 0) { peer->windowSize = (ENET_MAX(peer->incomingBandwidth, host->outgoingBandwidth) / ENET_PEER_WINDOW_SIZE_SCALE) * ENET_PROTOCOL_MINIMUM_WINDOW_SIZE; } else { peer->windowSize = (ENET_MIN(peer->incomingBandwidth, host->outgoingBandwidth) / ENET_PEER_WINDOW_SIZE_SCALE) * ENET_PROTOCOL_MINIMUM_WINDOW_SIZE; } if (peer->windowSize < ENET_PROTOCOL_MINIMUM_WINDOW_SIZE) { peer->windowSize = ENET_PROTOCOL_MINIMUM_WINDOW_SIZE; } else if (peer->windowSize > ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE) { peer->windowSize = ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE; } return 0; } /* enet_protocol_handle_bandwidth_limit */ static int enet_protocol_handle_throttle_configure(ENetHost *host, ENetPeer *peer, const ENetProtocol *command) { ENET_UNUSED(host) if (peer->state != ENET_PEER_STATE_CONNECTED && peer->state != ENET_PEER_STATE_DISCONNECT_LATER) { return -1; } peer->packetThrottleInterval = ENET_NET_TO_HOST_32(command->throttleConfigure.packetThrottleInterval); peer->packetThrottleAcceleration = ENET_NET_TO_HOST_32(command->throttleConfigure.packetThrottleAcceleration); peer->packetThrottleDeceleration = ENET_NET_TO_HOST_32(command->throttleConfigure.packetThrottleDeceleration); return 0; } static int enet_protocol_handle_disconnect(ENetHost *host, ENetPeer *peer, const ENetProtocol *command) { if (peer->state == ENET_PEER_STATE_DISCONNECTED || peer->state == ENET_PEER_STATE_ZOMBIE || peer->state == ENET_PEER_STATE_ACKNOWLEDGING_DISCONNECT ) { return 0; } enet_peer_reset_queues(peer); if (peer->state == ENET_PEER_STATE_CONNECTION_SUCCEEDED || peer->state == ENET_PEER_STATE_DISCONNECTING || peer->state == ENET_PEER_STATE_CONNECTING) { enet_protocol_dispatch_state(host, peer, ENET_PEER_STATE_ZOMBIE); } else if (peer->state != ENET_PEER_STATE_CONNECTED && peer->state != ENET_PEER_STATE_DISCONNECT_LATER) { if (peer->state == ENET_PEER_STATE_CONNECTION_PENDING) { host->recalculateBandwidthLimits = 1; } enet_peer_reset(peer); } else if (command->header.command & ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE) { enet_protocol_change_state(host, peer, ENET_PEER_STATE_ACKNOWLEDGING_DISCONNECT); } else { enet_protocol_dispatch_state(host, peer, ENET_PEER_STATE_ZOMBIE); } if (peer->state != ENET_PEER_STATE_DISCONNECTED) { peer->eventData = ENET_NET_TO_HOST_32(command->disconnect.data); } return 0; } static int enet_protocol_handle_acknowledge(ENetHost *host, ENetEvent *event, ENetPeer *peer, const ENetProtocol *command) { enet_uint32 roundTripTime, receivedSentTime, receivedReliableSequenceNumber; ENetProtocolCommand commandNumber; if (peer->state == ENET_PEER_STATE_DISCONNECTED || peer->state == ENET_PEER_STATE_ZOMBIE) { return 0; } receivedSentTime = ENET_NET_TO_HOST_16(command->acknowledge.receivedSentTime); receivedSentTime |= host->serviceTime & 0xFFFF0000; if ((receivedSentTime & 0x8000) > (host->serviceTime & 0x8000)) { receivedSentTime -= 0x10000; } if (ENET_TIME_LESS(host->serviceTime, receivedSentTime)) { return 0; } peer->lastReceiveTime = host->serviceTime; peer->earliestTimeout = 0; roundTripTime = ENET_TIME_DIFFERENCE(host->serviceTime, receivedSentTime); enet_peer_throttle(peer, roundTripTime); peer->roundTripTimeVariance -= peer->roundTripTimeVariance / 4; if (roundTripTime >= peer->roundTripTime) { peer->roundTripTime += (roundTripTime - peer->roundTripTime) / 8; peer->roundTripTimeVariance += (roundTripTime - peer->roundTripTime) / 4; } else { peer->roundTripTime -= (peer->roundTripTime - roundTripTime) / 8; peer->roundTripTimeVariance += (peer->roundTripTime - roundTripTime) / 4; } if (peer->roundTripTime < peer->lowestRoundTripTime) { peer->lowestRoundTripTime = peer->roundTripTime; } if (peer->roundTripTimeVariance > peer->highestRoundTripTimeVariance) { peer->highestRoundTripTimeVariance = peer->roundTripTimeVariance; } if (peer->packetThrottleEpoch == 0 || ENET_TIME_DIFFERENCE(host->serviceTime, peer->packetThrottleEpoch) >= peer->packetThrottleInterval ) { peer->lastRoundTripTime = peer->lowestRoundTripTime; peer->lastRoundTripTimeVariance = peer->highestRoundTripTimeVariance; peer->lowestRoundTripTime = peer->roundTripTime; peer->highestRoundTripTimeVariance = peer->roundTripTimeVariance; peer->packetThrottleEpoch = host->serviceTime; } receivedReliableSequenceNumber = ENET_NET_TO_HOST_16(command->acknowledge.receivedReliableSequenceNumber); commandNumber = enet_protocol_remove_sent_reliable_command(peer, receivedReliableSequenceNumber, command->header.channelID); switch (peer->state) { case ENET_PEER_STATE_ACKNOWLEDGING_CONNECT: if (commandNumber != ENET_PROTOCOL_COMMAND_VERIFY_CONNECT) { return -1; } enet_protocol_notify_connect(host, peer, event); break; case ENET_PEER_STATE_DISCONNECTING: if (commandNumber != ENET_PROTOCOL_COMMAND_DISCONNECT) { return -1; } enet_protocol_notify_disconnect(host, peer, event); break; case ENET_PEER_STATE_DISCONNECT_LATER: if (enet_list_empty(&peer->outgoingReliableCommands) && enet_list_empty(&peer->outgoingUnreliableCommands) && enet_list_empty(&peer->sentReliableCommands)) { enet_peer_disconnect(peer, peer->eventData); } break; default: break; } return 0; } /* enet_protocol_handle_acknowledge */ static int enet_protocol_handle_verify_connect(ENetHost *host, ENetEvent *event, ENetPeer *peer, const ENetProtocol *command) { enet_uint32 mtu, windowSize; size_t channelCount; if (peer->state != ENET_PEER_STATE_CONNECTING) { return 0; } channelCount = ENET_NET_TO_HOST_32(command->verifyConnect.channelCount); if (channelCount < ENET_PROTOCOL_MINIMUM_CHANNEL_COUNT || channelCount > ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT || ENET_NET_TO_HOST_32(command->verifyConnect.packetThrottleInterval) != peer->packetThrottleInterval || ENET_NET_TO_HOST_32(command->verifyConnect.packetThrottleAcceleration) != peer->packetThrottleAcceleration || ENET_NET_TO_HOST_32(command->verifyConnect.packetThrottleDeceleration) != peer->packetThrottleDeceleration || command->verifyConnect.connectID != peer->connectID ) { peer->eventData = 0; enet_protocol_dispatch_state(host, peer, ENET_PEER_STATE_ZOMBIE); return -1; } enet_protocol_remove_sent_reliable_command(peer, 1, 0xFF); if (channelCount < peer->channelCount) { peer->channelCount = channelCount; } peer->outgoingPeerID = ENET_NET_TO_HOST_16(command->verifyConnect.outgoingPeerID); peer->incomingSessionID = command->verifyConnect.incomingSessionID; peer->outgoingSessionID = command->verifyConnect.outgoingSessionID; mtu = ENET_NET_TO_HOST_32(command->verifyConnect.mtu); if (mtu < ENET_PROTOCOL_MINIMUM_MTU) { mtu = ENET_PROTOCOL_MINIMUM_MTU; } else if (mtu > ENET_PROTOCOL_MAXIMUM_MTU) { mtu = ENET_PROTOCOL_MAXIMUM_MTU; } if (mtu < peer->mtu) { peer->mtu = mtu; } windowSize = ENET_NET_TO_HOST_32(command->verifyConnect.windowSize); if (windowSize < ENET_PROTOCOL_MINIMUM_WINDOW_SIZE) { windowSize = ENET_PROTOCOL_MINIMUM_WINDOW_SIZE; } if (windowSize > ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE) { windowSize = ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE; } if (windowSize < peer->windowSize) { peer->windowSize = windowSize; } peer->incomingBandwidth = ENET_NET_TO_HOST_32(command->verifyConnect.incomingBandwidth); peer->outgoingBandwidth = ENET_NET_TO_HOST_32(command->verifyConnect.outgoingBandwidth); enet_protocol_notify_connect(host, peer, event); return 0; } /* enet_protocol_handle_verify_connect */ static int enet_protocol_handle_incoming_commands(ENetHost *host, ENetEvent *event) { ENetProtocolHeader *header; ENetProtocol *command; ENetPeer *peer; enet_uint8 *currentData; size_t headerSize; enet_uint16 peerID, flags; enet_uint8 sessionID; if (host->receivedDataLength < (size_t) &((ENetProtocolHeader *) 0)->sentTime) { return 0; } header = (ENetProtocolHeader *) host->receivedData; peerID = ENET_NET_TO_HOST_16(header->peerID); sessionID = (peerID & ENET_PROTOCOL_HEADER_SESSION_MASK) >> ENET_PROTOCOL_HEADER_SESSION_SHIFT; flags = peerID & ENET_PROTOCOL_HEADER_FLAG_MASK; peerID &= ~(ENET_PROTOCOL_HEADER_FLAG_MASK | ENET_PROTOCOL_HEADER_SESSION_MASK); headerSize = (flags & ENET_PROTOCOL_HEADER_FLAG_SENT_TIME ? sizeof(ENetProtocolHeader) : (size_t) &((ENetProtocolHeader *) 0)->sentTime); if (host->checksum != NULL) { headerSize += sizeof(enet_uint32); } if (peerID == ENET_PROTOCOL_MAXIMUM_PEER_ID) { peer = NULL; } else if (peerID >= host->peerCount) { return 0; } else { peer = &host->peers[peerID]; if (peer->state == ENET_PEER_STATE_DISCONNECTED || peer->state == ENET_PEER_STATE_ZOMBIE || ((!in6_equal(host->receivedAddress.host , peer->address.host) || host->receivedAddress.port != peer->address.port) && 1 /* no broadcast in ipv6 !in6_equal(peer->address.host , ENET_HOST_BROADCAST)*/) || (peer->outgoingPeerID < ENET_PROTOCOL_MAXIMUM_PEER_ID && sessionID != peer->incomingSessionID) ) { return 0; } } if (flags & ENET_PROTOCOL_HEADER_FLAG_COMPRESSED) { size_t originalSize; if (host->compressor.context == NULL || host->compressor.decompress == NULL) { return 0; } originalSize = host->compressor.decompress(host->compressor.context, host->receivedData + headerSize, host->receivedDataLength - headerSize, host->packetData[1] + headerSize, sizeof(host->packetData[1]) - headerSize ); if (originalSize <= 0 || originalSize > sizeof(host->packetData[1]) - headerSize) { return 0; } memcpy(host->packetData[1], header, headerSize); host->receivedData = host->packetData[1]; host->receivedDataLength = headerSize + originalSize; } if (host->checksum != NULL) { enet_uint32 *checksum = (enet_uint32 *) &host->receivedData[headerSize - sizeof(enet_uint32)]; enet_uint32 desiredChecksum = *checksum; ENetBuffer buffer; *checksum = peer != NULL ? peer->connectID : 0; buffer.data = host->receivedData; buffer.dataLength = host->receivedDataLength; if (host->checksum(&buffer, 1) != desiredChecksum) { return 0; } } if (peer != NULL) { peer->address.host = host->receivedAddress.host; peer->address.port = host->receivedAddress.port; peer->incomingDataTotal += host->receivedDataLength; peer->totalDataReceived += host->receivedDataLength; } currentData = host->receivedData + headerSize; while (currentData < &host->receivedData[host->receivedDataLength]) { enet_uint8 commandNumber; size_t commandSize; command = (ENetProtocol *) currentData; if (currentData + sizeof(ENetProtocolCommandHeader) > &host->receivedData[host->receivedDataLength]) { break; } commandNumber = command->header.command & ENET_PROTOCOL_COMMAND_MASK; if (commandNumber >= ENET_PROTOCOL_COMMAND_COUNT) { break; } commandSize = commandSizes[commandNumber]; if (commandSize == 0 || currentData + commandSize > &host->receivedData[host->receivedDataLength]) { break; } currentData += commandSize; if (peer == NULL && (commandNumber != ENET_PROTOCOL_COMMAND_CONNECT || currentData < &host->receivedData[host->receivedDataLength])) { break; } command->header.reliableSequenceNumber = ENET_NET_TO_HOST_16(command->header.reliableSequenceNumber); switch (commandNumber) { case ENET_PROTOCOL_COMMAND_ACKNOWLEDGE: if (enet_protocol_handle_acknowledge(host, event, peer, command)) { goto commandError; } break; case ENET_PROTOCOL_COMMAND_CONNECT: if (peer != NULL) { goto commandError; } peer = enet_protocol_handle_connect(host, header, command); if (peer == NULL) { goto commandError; } break; case ENET_PROTOCOL_COMMAND_VERIFY_CONNECT: if (enet_protocol_handle_verify_connect(host, event, peer, command)) { goto commandError; } break; case ENET_PROTOCOL_COMMAND_DISCONNECT: if (enet_protocol_handle_disconnect(host, peer, command)) { goto commandError; } break; case ENET_PROTOCOL_COMMAND_PING: if (enet_protocol_handle_ping(host, peer, command)) { goto commandError; } break; case ENET_PROTOCOL_COMMAND_SEND_RELIABLE: if (enet_protocol_handle_send_reliable(host, peer, command, ¤tData)) { goto commandError; } break; case ENET_PROTOCOL_COMMAND_SEND_UNRELIABLE: if (enet_protocol_handle_send_unreliable(host, peer, command, ¤tData)) { goto commandError; } break; case ENET_PROTOCOL_COMMAND_SEND_UNSEQUENCED: if (enet_protocol_handle_send_unsequenced(host, peer, command, ¤tData)) { goto commandError; } break; case ENET_PROTOCOL_COMMAND_SEND_FRAGMENT: if (enet_protocol_handle_send_fragment(host, peer, command, ¤tData)) { goto commandError; } break; case ENET_PROTOCOL_COMMAND_BANDWIDTH_LIMIT: if (enet_protocol_handle_bandwidth_limit(host, peer, command)) { goto commandError; } break; case ENET_PROTOCOL_COMMAND_THROTTLE_CONFIGURE: if (enet_protocol_handle_throttle_configure(host, peer, command)) { goto commandError; } break; case ENET_PROTOCOL_COMMAND_SEND_UNRELIABLE_FRAGMENT: if (enet_protocol_handle_send_unreliable_fragment(host, peer, command, ¤tData)) { goto commandError; } break; default: goto commandError; } if (peer != NULL && (command->header.command & ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE) != 0) { enet_uint16 sentTime; if (!(flags & ENET_PROTOCOL_HEADER_FLAG_SENT_TIME)) { break; } sentTime = ENET_NET_TO_HOST_16(header->sentTime); switch (peer->state) { case ENET_PEER_STATE_DISCONNECTING: case ENET_PEER_STATE_ACKNOWLEDGING_CONNECT: case ENET_PEER_STATE_DISCONNECTED: case ENET_PEER_STATE_ZOMBIE: break; case ENET_PEER_STATE_ACKNOWLEDGING_DISCONNECT: if ((command->header.command & ENET_PROTOCOL_COMMAND_MASK) == ENET_PROTOCOL_COMMAND_DISCONNECT) { enet_peer_queue_acknowledgement(peer, command, sentTime); } break; default: enet_peer_queue_acknowledgement(peer, command, sentTime); break; } } } commandError: if (event != NULL && event->type != ENET_EVENT_TYPE_NONE) { return 1; } return 0; } /* enet_protocol_handle_incoming_commands */ static int enet_protocol_receive_incoming_commands(ENetHost *host, ENetEvent *event) { int packets; for (packets = 0; packets < 256; ++packets) { int receivedLength; ENetBuffer buffer; buffer.data = host->packetData[0]; // buffer.dataLength = sizeof (host->packetData[0]); buffer.dataLength = host->mtu; receivedLength = enet_socket_receive(host->socket, &host->receivedAddress, &buffer, 1); if (receivedLength == -2) continue; if (receivedLength < 0) { return -1; } if (receivedLength == 0) { return 0; } host->receivedData = host->packetData[0]; host->receivedDataLength = receivedLength; host->totalReceivedData += receivedLength; host->totalReceivedPackets++; if (host->intercept != NULL) { switch (host->intercept(host, (void *)event)) { case 1: if (event != NULL && event->type != ENET_EVENT_TYPE_NONE) { return 1; } continue; case -1: return -1; default: break; } } switch (enet_protocol_handle_incoming_commands(host, event)) { case 1: return 1; case -1: return -1; default: break; } } return -1; } /* enet_protocol_receive_incoming_commands */ static void enet_protocol_send_acknowledgements(ENetHost *host, ENetPeer *peer) { ENetProtocol *command = &host->commands[host->commandCount]; ENetBuffer *buffer = &host->buffers[host->bufferCount]; ENetAcknowledgement *acknowledgement; ENetListIterator currentAcknowledgement; enet_uint16 reliableSequenceNumber; currentAcknowledgement = enet_list_begin(&peer->acknowledgements); while (currentAcknowledgement != enet_list_end(&peer->acknowledgements)) { if (command >= &host->commands[sizeof(host->commands) / sizeof(ENetProtocol)] || buffer >= &host->buffers[sizeof(host->buffers) / sizeof(ENetBuffer)] || peer->mtu - host->packetSize < sizeof(ENetProtocolAcknowledge) ) { host->continueSending = 1; break; } acknowledgement = (ENetAcknowledgement *) currentAcknowledgement; currentAcknowledgement = enet_list_next(currentAcknowledgement); buffer->data = command; buffer->dataLength = sizeof(ENetProtocolAcknowledge); host->packetSize += buffer->dataLength; reliableSequenceNumber = ENET_HOST_TO_NET_16(acknowledgement->command.header.reliableSequenceNumber); command->header.command = ENET_PROTOCOL_COMMAND_ACKNOWLEDGE; command->header.channelID = acknowledgement->command.header.channelID; command->header.reliableSequenceNumber = reliableSequenceNumber; command->acknowledge.receivedReliableSequenceNumber = reliableSequenceNumber; command->acknowledge.receivedSentTime = ENET_HOST_TO_NET_16(acknowledgement->sentTime); if ((acknowledgement->command.header.command & ENET_PROTOCOL_COMMAND_MASK) == ENET_PROTOCOL_COMMAND_DISCONNECT) { enet_protocol_dispatch_state(host, peer, ENET_PEER_STATE_ZOMBIE); } enet_list_remove(&acknowledgement->acknowledgementList); enet_free(acknowledgement); ++command; ++buffer; } host->commandCount = command - host->commands; host->bufferCount = buffer - host->buffers; } /* enet_protocol_send_acknowledgements */ static void enet_protocol_send_unreliable_outgoing_commands(ENetHost *host, ENetPeer *peer) { ENetProtocol *command = &host->commands[host->commandCount]; ENetBuffer *buffer = &host->buffers[host->bufferCount]; ENetOutgoingCommand *outgoingCommand; ENetListIterator currentCommand; currentCommand = enet_list_begin(&peer->outgoingUnreliableCommands); while (currentCommand != enet_list_end(&peer->outgoingUnreliableCommands)) { size_t commandSize; outgoingCommand = (ENetOutgoingCommand *) currentCommand; commandSize = commandSizes[outgoingCommand->command.header.command & ENET_PROTOCOL_COMMAND_MASK]; if (command >= &host->commands[sizeof(host->commands) / sizeof(ENetProtocol)] || buffer + 1 >= &host->buffers[sizeof(host->buffers) / sizeof(ENetBuffer)] || peer->mtu - host->packetSize < commandSize || (outgoingCommand->packet != NULL && peer->mtu - host->packetSize < commandSize + outgoingCommand->fragmentLength) ) { host->continueSending = 1; break; } currentCommand = enet_list_next(currentCommand); if (outgoingCommand->packet != NULL && outgoingCommand->fragmentOffset == 0) { peer->packetThrottleCounter += ENET_PEER_PACKET_THROTTLE_COUNTER; peer->packetThrottleCounter %= ENET_PEER_PACKET_THROTTLE_SCALE; if (peer->packetThrottleCounter > peer->packetThrottle) { enet_uint16 reliableSequenceNumber = outgoingCommand->reliableSequenceNumber; enet_uint16 unreliableSequenceNumber = outgoingCommand->unreliableSequenceNumber; for (;;) { --outgoingCommand->packet->referenceCount; if (outgoingCommand->packet->referenceCount == 0) { callbacks.packet_destroy(outgoingCommand->packet); } enet_list_remove(&outgoingCommand->outgoingCommandList); enet_free(outgoingCommand); if (currentCommand == enet_list_end(&peer->outgoingUnreliableCommands)) { break; } outgoingCommand = (ENetOutgoingCommand *) currentCommand; if (outgoingCommand->reliableSequenceNumber != reliableSequenceNumber || outgoingCommand->unreliableSequenceNumber != unreliableSequenceNumber) { break; } currentCommand = enet_list_next(currentCommand); } continue; } } buffer->data = command; buffer->dataLength = commandSize; host->packetSize += buffer->dataLength; *command = outgoingCommand->command; enet_list_remove(&outgoingCommand->outgoingCommandList); if (outgoingCommand->packet != NULL) { ++buffer; buffer->data = outgoingCommand->packet->data + outgoingCommand->fragmentOffset; buffer->dataLength = outgoingCommand->fragmentLength; host->packetSize += buffer->dataLength; enet_list_insert(enet_list_end(&peer->sentUnreliableCommands), outgoingCommand); } else { enet_free(outgoingCommand); } ++command; ++buffer; } host->commandCount = command - host->commands; host->bufferCount = buffer - host->buffers; if (peer->state == ENET_PEER_STATE_DISCONNECT_LATER && enet_list_empty(&peer->outgoingReliableCommands) && enet_list_empty(&peer->outgoingUnreliableCommands) && enet_list_empty(&peer->sentReliableCommands)) { enet_peer_disconnect(peer, peer->eventData); } } /* enet_protocol_send_unreliable_outgoing_commands */ static int enet_protocol_check_timeouts(ENetHost *host, ENetPeer *peer, ENetEvent *event) { ENetOutgoingCommand *outgoingCommand; ENetListIterator currentCommand, insertPosition; currentCommand = enet_list_begin(&peer->sentReliableCommands); insertPosition = enet_list_begin(&peer->outgoingReliableCommands); while (currentCommand != enet_list_end(&peer->sentReliableCommands)) { outgoingCommand = (ENetOutgoingCommand *) currentCommand; currentCommand = enet_list_next(currentCommand); if (ENET_TIME_DIFFERENCE(host->serviceTime, outgoingCommand->sentTime) < outgoingCommand->roundTripTimeout) { continue; } if (peer->earliestTimeout == 0 || ENET_TIME_LESS(outgoingCommand->sentTime, peer->earliestTimeout)) { peer->earliestTimeout = outgoingCommand->sentTime; } if (peer->earliestTimeout != 0 && (ENET_TIME_DIFFERENCE(host->serviceTime, peer->earliestTimeout) >= peer->timeoutMaximum || (outgoingCommand->roundTripTimeout >= outgoingCommand->roundTripTimeoutLimit && ENET_TIME_DIFFERENCE(host->serviceTime, peer->earliestTimeout) >= peer->timeoutMinimum)) ) { enet_protocol_notify_disconnect_timeout(host, peer, event); return 1; } if (outgoingCommand->packet != NULL) { peer->reliableDataInTransit -= outgoingCommand->fragmentLength; } ++peer->packetsLost; ++peer->totalPacketsLost; /* Replaced exponential backoff time with something more linear */ /* Source: http://lists.cubik.org/pipermail/enet-discuss/2014-May/002308.html */ outgoingCommand->roundTripTimeout = peer->roundTripTime + 4 * peer->roundTripTimeVariance; outgoingCommand->roundTripTimeoutLimit = peer->timeoutLimit * outgoingCommand->roundTripTimeout; enet_list_insert(insertPosition, enet_list_remove(&outgoingCommand->outgoingCommandList)); if (currentCommand == enet_list_begin(&peer->sentReliableCommands) && !enet_list_empty(&peer->sentReliableCommands)) { outgoingCommand = (ENetOutgoingCommand *) currentCommand; peer->nextTimeout = outgoingCommand->sentTime + outgoingCommand->roundTripTimeout; } } return 0; } /* enet_protocol_check_timeouts */ static int enet_protocol_send_reliable_outgoing_commands(ENetHost *host, ENetPeer *peer) { ENetProtocol *command = &host->commands[host->commandCount]; ENetBuffer *buffer = &host->buffers[host->bufferCount]; ENetOutgoingCommand *outgoingCommand; ENetListIterator currentCommand; ENetChannel *channel; enet_uint16 reliableWindow; size_t commandSize; int windowExceeded = 0, windowWrap = 0, canPing = 1; currentCommand = enet_list_begin(&peer->outgoingReliableCommands); while (currentCommand != enet_list_end(&peer->outgoingReliableCommands)) { outgoingCommand = (ENetOutgoingCommand *) currentCommand; channel = outgoingCommand->command.header.channelID < peer->channelCount ? &peer->channels[outgoingCommand->command.header.channelID] : NULL; reliableWindow = outgoingCommand->reliableSequenceNumber / ENET_PEER_RELIABLE_WINDOW_SIZE; if (channel != NULL) { if (!windowWrap && outgoingCommand->sendAttempts < 1 && !(outgoingCommand->reliableSequenceNumber % ENET_PEER_RELIABLE_WINDOW_SIZE) && (channel->reliableWindows[(reliableWindow + ENET_PEER_RELIABLE_WINDOWS - 1) % ENET_PEER_RELIABLE_WINDOWS] >= ENET_PEER_RELIABLE_WINDOW_SIZE || channel->usedReliableWindows & ((((1 << ENET_PEER_FREE_RELIABLE_WINDOWS) - 1) << reliableWindow) | (((1 << ENET_PEER_FREE_RELIABLE_WINDOWS) - 1) >> (ENET_PEER_RELIABLE_WINDOWS - reliableWindow)))) ) { windowWrap = 1; } if (windowWrap) { currentCommand = enet_list_next(currentCommand); continue; } } if (outgoingCommand->packet != NULL) { if (!windowExceeded) { enet_uint32 windowSize = (peer->packetThrottle * peer->windowSize) / ENET_PEER_PACKET_THROTTLE_SCALE; if (peer->reliableDataInTransit + outgoingCommand->fragmentLength > ENET_MAX(windowSize, peer->mtu)) { windowExceeded = 1; } } if (windowExceeded) { currentCommand = enet_list_next(currentCommand); continue; } } canPing = 0; commandSize = commandSizes[outgoingCommand->command.header.command & ENET_PROTOCOL_COMMAND_MASK]; if (command >= &host->commands[sizeof(host->commands) / sizeof(ENetProtocol)] || buffer + 1 >= &host->buffers[sizeof(host->buffers) / sizeof(ENetBuffer)] || peer->mtu - host->packetSize < commandSize || (outgoingCommand->packet != NULL && (enet_uint16) (peer->mtu - host->packetSize) < (enet_uint16) (commandSize + outgoingCommand->fragmentLength)) ) { host->continueSending = 1; break; } currentCommand = enet_list_next(currentCommand); if (channel != NULL && outgoingCommand->sendAttempts < 1) { channel->usedReliableWindows |= 1 << reliableWindow; ++channel->reliableWindows[reliableWindow]; } ++outgoingCommand->sendAttempts; if (outgoingCommand->roundTripTimeout == 0) { outgoingCommand->roundTripTimeout = peer->roundTripTime + 4 * peer->roundTripTimeVariance; outgoingCommand->roundTripTimeoutLimit = peer->timeoutLimit * outgoingCommand->roundTripTimeout; } if (enet_list_empty(&peer->sentReliableCommands)) { peer->nextTimeout = host->serviceTime + outgoingCommand->roundTripTimeout; } enet_list_insert(enet_list_end(&peer->sentReliableCommands), enet_list_remove(&outgoingCommand->outgoingCommandList)); outgoingCommand->sentTime = host->serviceTime; buffer->data = command; buffer->dataLength = commandSize; host->packetSize += buffer->dataLength; host->headerFlags |= ENET_PROTOCOL_HEADER_FLAG_SENT_TIME; *command = outgoingCommand->command; if (outgoingCommand->packet != NULL) { ++buffer; buffer->data = outgoingCommand->packet->data + outgoingCommand->fragmentOffset; buffer->dataLength = outgoingCommand->fragmentLength; host->packetSize += outgoingCommand->fragmentLength; peer->reliableDataInTransit += outgoingCommand->fragmentLength; } ++peer->packetsSent; ++peer->totalPacketsSent; ++command; ++buffer; } host->commandCount = command - host->commands; host->bufferCount = buffer - host->buffers; return canPing; } /* enet_protocol_send_reliable_outgoing_commands */ static int enet_protocol_send_outgoing_commands(ENetHost *host, ENetEvent *event, int checkForTimeouts) { enet_uint8 headerData[sizeof(ENetProtocolHeader) + sizeof(enet_uint32)]; ENetProtocolHeader *header = (ENetProtocolHeader *) headerData; ENetPeer *currentPeer; int sentLength; size_t shouldCompress = 0; host->continueSending = 1; while (host->continueSending) for (host->continueSending = 0, currentPeer = host->peers; currentPeer < &host->peers[host->peerCount]; ++currentPeer) { if (currentPeer->state == ENET_PEER_STATE_DISCONNECTED || currentPeer->state == ENET_PEER_STATE_ZOMBIE) { continue; } host->headerFlags = 0; host->commandCount = 0; host->bufferCount = 1; host->packetSize = sizeof(ENetProtocolHeader); if (!enet_list_empty(¤tPeer->acknowledgements)) { enet_protocol_send_acknowledgements(host, currentPeer); } if (checkForTimeouts != 0 && !enet_list_empty(¤tPeer->sentReliableCommands) && ENET_TIME_GREATER_EQUAL(host->serviceTime, currentPeer->nextTimeout) && enet_protocol_check_timeouts(host, currentPeer, event) == 1 ) { if (event != NULL && event->type != ENET_EVENT_TYPE_NONE) { return 1; } else { continue; } } if ((enet_list_empty(¤tPeer->outgoingReliableCommands) || enet_protocol_send_reliable_outgoing_commands(host, currentPeer)) && enet_list_empty(¤tPeer->sentReliableCommands) && ENET_TIME_DIFFERENCE(host->serviceTime, currentPeer->lastReceiveTime) >= currentPeer->pingInterval && currentPeer->mtu - host->packetSize >= sizeof(ENetProtocolPing) ) { enet_peer_ping(currentPeer); enet_protocol_send_reliable_outgoing_commands(host, currentPeer); } if (!enet_list_empty(¤tPeer->outgoingUnreliableCommands)) { enet_protocol_send_unreliable_outgoing_commands(host, currentPeer); } if (host->commandCount == 0) { continue; } if (currentPeer->packetLossEpoch == 0) { currentPeer->packetLossEpoch = host->serviceTime; } else if (ENET_TIME_DIFFERENCE(host->serviceTime, currentPeer->packetLossEpoch) >= ENET_PEER_PACKET_LOSS_INTERVAL && currentPeer->packetsSent > 0) { enet_uint32 packetLoss = currentPeer->packetsLost * ENET_PEER_PACKET_LOSS_SCALE / currentPeer->packetsSent; #ifdef ENET_DEBUG printf( "peer %u: %f%%+-%f%% packet loss, %u+-%u ms round trip time, %f%% throttle, %u/%u outgoing, %u/%u incoming\n", currentPeer->incomingPeerID, currentPeer->packetLoss / (float) ENET_PEER_PACKET_LOSS_SCALE, currentPeer->packetLossVariance / (float) ENET_PEER_PACKET_LOSS_SCALE, currentPeer->roundTripTime, currentPeer->roundTripTimeVariance, currentPeer->packetThrottle / (float) ENET_PEER_PACKET_THROTTLE_SCALE, enet_list_size(¤tPeer->outgoingReliableCommands), enet_list_size(¤tPeer->outgoingUnreliableCommands), currentPeer->channels != NULL ? enet_list_size( ¤tPeer->channels->incomingReliableCommands) : 0, currentPeer->channels != NULL ? enet_list_size(¤tPeer->channels->incomingUnreliableCommands) : 0 ); #endif currentPeer->packetLossVariance -= currentPeer->packetLossVariance / 4; if (packetLoss >= currentPeer->packetLoss) { currentPeer->packetLoss += (packetLoss - currentPeer->packetLoss) / 8; currentPeer->packetLossVariance += (packetLoss - currentPeer->packetLoss) / 4; } else { currentPeer->packetLoss -= (currentPeer->packetLoss - packetLoss) / 8; currentPeer->packetLossVariance += (currentPeer->packetLoss - packetLoss) / 4; } currentPeer->packetLossEpoch = host->serviceTime; currentPeer->packetsSent = 0; currentPeer->packetsLost = 0; } host->buffers->data = headerData; if (host->headerFlags & ENET_PROTOCOL_HEADER_FLAG_SENT_TIME) { header->sentTime = ENET_HOST_TO_NET_16(host->serviceTime & 0xFFFF); host->buffers->dataLength = sizeof(ENetProtocolHeader); } else { host->buffers->dataLength = (size_t) &((ENetProtocolHeader *) 0)->sentTime; } shouldCompress = 0; if (host->compressor.context != NULL && host->compressor.compress != NULL) { size_t originalSize = host->packetSize - sizeof(ENetProtocolHeader), compressedSize = host->compressor.compress(host->compressor.context, &host->buffers[1], host->bufferCount - 1, originalSize, host->packetData[1], originalSize); if (compressedSize > 0 && compressedSize < originalSize) { host->headerFlags |= ENET_PROTOCOL_HEADER_FLAG_COMPRESSED; shouldCompress = compressedSize; #ifdef ENET_DEBUG_COMPRESS printf("peer %u: compressed %u->%u (%u%%)\n", currentPeer->incomingPeerID, originalSize, compressedSize, (compressedSize * 100) / originalSize); #endif } } if (currentPeer->outgoingPeerID < ENET_PROTOCOL_MAXIMUM_PEER_ID) { host->headerFlags |= currentPeer->outgoingSessionID << ENET_PROTOCOL_HEADER_SESSION_SHIFT; } header->peerID = ENET_HOST_TO_NET_16(currentPeer->outgoingPeerID | host->headerFlags); if (host->checksum != NULL) { enet_uint32 *checksum = (enet_uint32 *) &headerData[host->buffers->dataLength]; *checksum = currentPeer->outgoingPeerID < ENET_PROTOCOL_MAXIMUM_PEER_ID ? currentPeer->connectID : 0; host->buffers->dataLength += sizeof(enet_uint32); *checksum = host->checksum(host->buffers, host->bufferCount); } if (shouldCompress > 0) { host->buffers[1].data = host->packetData[1]; host->buffers[1].dataLength = shouldCompress; host->bufferCount = 2; } currentPeer->lastSendTime = host->serviceTime; sentLength = enet_socket_send(host->socket, ¤tPeer->address, host->buffers, host->bufferCount); enet_protocol_remove_sent_unreliable_commands(currentPeer); if (sentLength < 0) { return -1; } host->totalSentData += sentLength; currentPeer->totalDataSent += sentLength; host->totalSentPackets++; } return 0; } /* enet_protocol_send_outgoing_commands */ /** Sends any queued packets on the host specified to its designated peers. * * @param host host to flush * @remarks this function need only be used in circumstances where one wishes to send queued packets earlier than in a call to enet_host_service(). * @ingroup host */ void enet_host_flush(ENetHost *host) { host->serviceTime = enet_time_get(); enet_protocol_send_outgoing_commands(host, NULL, 0); } /** Checks for any queued events on the host and dispatches one if available. * * @param host host to check for events * @param event an event structure where event details will be placed if available * @retval > 0 if an event was dispatched * @retval 0 if no events are available * @retval < 0 on failure * @ingroup host */ int enet_host_check_events(ENetHost *host, ENetEvent *event) { if (event == NULL) { return -1; } event->type = ENET_EVENT_TYPE_NONE; event->peer = NULL; event->packet = NULL; return enet_protocol_dispatch_incoming_commands(host, event); } /** Waits for events on the host specified and shuttles packets between * the host and its peers. * * @param host host to service * @param event an event structure where event details will be placed if one occurs * if event == NULL then no events will be delivered * @param timeout number of milliseconds that ENet should wait for events * @retval > 0 if an event occurred within the specified time limit * @retval 0 if no event occurred * @retval < 0 on failure * @remarks enet_host_service should be called fairly regularly for adequate performance * @ingroup host */ int enet_host_service(ENetHost *host, ENetEvent *event, enet_uint32 timeout) { enet_uint32 waitCondition; if (event != NULL) { event->type = ENET_EVENT_TYPE_NONE; event->peer = NULL; event->packet = NULL; switch (enet_protocol_dispatch_incoming_commands(host, event)) { case 1: return 1; case -1: #ifdef ENET_DEBUG perror("Error dispatching incoming packets"); #endif return -1; default: break; } } host->serviceTime = enet_time_get(); timeout += host->serviceTime; do { if (ENET_TIME_DIFFERENCE(host->serviceTime, host->bandwidthThrottleEpoch) >= ENET_HOST_BANDWIDTH_THROTTLE_INTERVAL) { enet_host_bandwidth_throttle(host); } switch (enet_protocol_send_outgoing_commands(host, event, 1)) { case 1: return 1; case -1: #ifdef ENET_DEBUG perror("Error sending outgoing packets"); #endif return -1; default: break; } switch (enet_protocol_receive_incoming_commands(host, event)) { case 1: return 1; case -1: #ifdef ENET_DEBUG perror("Error receiving incoming packets"); #endif return -1; default: break; } switch (enet_protocol_send_outgoing_commands(host, event, 1)) { case 1: return 1; case -1: #ifdef ENET_DEBUG perror("Error sending outgoing packets"); #endif return -1; default: break; } if (event != NULL) { switch (enet_protocol_dispatch_incoming_commands(host, event)) { case 1: return 1; case -1: #ifdef ENET_DEBUG perror("Error dispatching incoming packets"); #endif return -1; default: break; } } if (ENET_TIME_GREATER_EQUAL(host->serviceTime, timeout)) { return 0; } do { host->serviceTime = enet_time_get(); if (ENET_TIME_GREATER_EQUAL(host->serviceTime, timeout)) { return 0; } waitCondition = ENET_SOCKET_WAIT_RECEIVE | ENET_SOCKET_WAIT_INTERRUPT; if (enet_socket_wait(host->socket, &waitCondition, ENET_TIME_DIFFERENCE(timeout, host->serviceTime)) != 0) { return -1; } } while (waitCondition & ENET_SOCKET_WAIT_INTERRUPT); host->serviceTime = enet_time_get(); } while (waitCondition & ENET_SOCKET_WAIT_RECEIVE); return 0; } /* enet_host_service */ // =======================================================================// // ! // ! Peer // ! // =======================================================================// /** Configures throttle parameter for a peer. * * Unreliable packets are dropped by ENet in response to the varying conditions * of the Internet connection to the peer. The throttle represents a probability * that an unreliable packet should not be dropped and thus sent by ENet to the peer. * The lowest mean round trip time from the sending of a reliable packet to the * receipt of its acknowledgement is measured over an amount of time specified by * the interval parameter in milliseconds. If a measured round trip time happens to * be significantly less than the mean round trip time measured over the interval, * then the throttle probability is increased to allow more traffic by an amount * specified in the acceleration parameter, which is a ratio to the ENET_PEER_PACKET_THROTTLE_SCALE * constant. If a measured round trip time happens to be significantly greater than * the mean round trip time measured over the interval, then the throttle probability * is decreased to limit traffic by an amount specified in the deceleration parameter, which * is a ratio to the ENET_PEER_PACKET_THROTTLE_SCALE constant. When the throttle has * a value of ENET_PEER_PACKET_THROTTLE_SCALE, no unreliable packets are dropped by * ENet, and so 100% of all unreliable packets will be sent. When the throttle has a * value of 0, all unreliable packets are dropped by ENet, and so 0% of all unreliable * packets will be sent. Intermediate values for the throttle represent intermediate * probabilities between 0% and 100% of unreliable packets being sent. The bandwidth * limits of the local and foreign hosts are taken into account to determine a * sensible limit for the throttle probability above which it should not raise even in * the best of conditions. * * @param peer peer to configure * @param interval interval, in milliseconds, over which to measure lowest mean RTT; the default value is ENET_PEER_PACKET_THROTTLE_INTERVAL. * @param acceleration rate at which to increase the throttle probability as mean RTT declines * @param deceleration rate at which to decrease the throttle probability as mean RTT increases */ void enet_peer_throttle_configure(ENetPeer *peer, enet_uint32 interval, enet_uint32 acceleration, enet_uint32 deceleration) { ENetProtocol command; peer->packetThrottleInterval = interval; peer->packetThrottleAcceleration = acceleration; peer->packetThrottleDeceleration = deceleration; command.header.command = ENET_PROTOCOL_COMMAND_THROTTLE_CONFIGURE | ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE; command.header.channelID = 0xFF; command.throttleConfigure.packetThrottleInterval = ENET_HOST_TO_NET_32(interval); command.throttleConfigure.packetThrottleAcceleration = ENET_HOST_TO_NET_32(acceleration); command.throttleConfigure.packetThrottleDeceleration = ENET_HOST_TO_NET_32(deceleration); enet_peer_queue_outgoing_command(peer, &command, NULL, 0, 0); } int enet_peer_throttle(ENetPeer *peer, enet_uint32 rtt) { if (peer->lastRoundTripTime <= peer->lastRoundTripTimeVariance) { peer->packetThrottle = peer->packetThrottleLimit; } else if (rtt < peer->lastRoundTripTime) { peer->packetThrottle += peer->packetThrottleAcceleration; if (peer->packetThrottle > peer->packetThrottleLimit) { peer->packetThrottle = peer->packetThrottleLimit; } return 1; } else if (rtt > peer->lastRoundTripTime + 2 * peer->lastRoundTripTimeVariance) { if (peer->packetThrottle > peer->packetThrottleDeceleration) { peer->packetThrottle -= peer->packetThrottleDeceleration; } else { peer->packetThrottle = 0; } return -1; } return 0; } /* Extended functionality for easier binding in other programming languages */ enet_uint32 enet_host_get_peers_count(ENetHost *host) { return host->connectedPeers; } enet_uint32 enet_host_get_packets_sent(ENetHost *host) { return host->totalSentPackets; } enet_uint32 enet_host_get_packets_received(ENetHost *host) { return host->totalReceivedPackets; } enet_uint32 enet_host_get_bytes_sent(ENetHost *host) { return host->totalSentData; } enet_uint32 enet_host_get_bytes_received(ENetHost *host) { return host->totalReceivedData; } /** Gets received data buffer. Returns buffer length. * @param host host to access recevie buffer * @param data ouput parameter for recevied data * @retval buffer length */ enet_uint32 enet_host_get_received_data(ENetHost *host, /*out*/ enet_uint8** data) { *data = host->receivedData; return host->receivedDataLength; } enet_uint32 enet_host_get_mtu(ENetHost *host) { return host->mtu; } enet_uint32 enet_peer_get_id(ENetPeer *peer) { return peer->connectID; } enet_uint32 enet_peer_get_ip(ENetPeer *peer, char *ip, size_t ipLength) { return enet_address_get_host_ip(&peer->address, ip, ipLength); } enet_uint16 enet_peer_get_port(ENetPeer *peer) { return peer->address.port; } ENetPeerState enet_peer_get_state(ENetPeer *peer) { return peer->state; } enet_uint32 enet_peer_get_rtt(ENetPeer *peer) { return peer->roundTripTime; } enet_uint64 enet_peer_get_packets_sent(ENetPeer *peer) { return peer->totalPacketsSent; } enet_uint32 enet_peer_get_packets_lost(ENetPeer *peer) { return peer->totalPacketsLost; } enet_uint64 enet_peer_get_bytes_sent(ENetPeer *peer) { return peer->totalDataSent; } enet_uint64 enet_peer_get_bytes_received(ENetPeer *peer) { return peer->totalDataReceived; } void * enet_peer_get_data(ENetPeer *peer) { return (void *) peer->data; } void enet_peer_set_data(ENetPeer *peer, const void *data) { peer->data = (enet_uint32 *) data; } void * enet_packet_get_data(ENetPacket *packet) { return (void *) packet->data; } enet_uint32 enet_packet_get_length(ENetPacket *packet) { return packet->dataLength; } void enet_packet_set_free_callback(ENetPacket *packet, void *callback) { packet->freeCallback = (ENetPacketFreeCallback)callback; } /** Queues a packet to be sent. * @param peer destination for the packet * @param channelID channel on which to send * @param packet packet to send * @retval 0 on success * @retval < 0 on failure */ int enet_peer_send(ENetPeer *peer, enet_uint8 channelID, ENetPacket *packet) { ENetChannel *channel = &peer->channels[channelID]; ENetProtocol command; size_t fragmentLength; if (peer->state != ENET_PEER_STATE_CONNECTED || channelID >= peer->channelCount || packet->dataLength > peer->host->maximumPacketSize) { return -1; } fragmentLength = peer->mtu - sizeof(ENetProtocolHeader) - sizeof(ENetProtocolSendFragment); if (peer->host->checksum != NULL) { fragmentLength -= sizeof(enet_uint32); } if (packet->dataLength > fragmentLength) { enet_uint32 fragmentCount = (packet->dataLength + fragmentLength - 1) / fragmentLength, fragmentNumber, fragmentOffset; enet_uint8 commandNumber; enet_uint16 startSequenceNumber; ENetList fragments; ENetOutgoingCommand *fragment; if (fragmentCount > ENET_PROTOCOL_MAXIMUM_FRAGMENT_COUNT) { return -1; } if ((packet->flags & (ENET_PACKET_FLAG_RELIABLE | ENET_PACKET_FLAG_UNRELIABLE_FRAGMENT)) == ENET_PACKET_FLAG_UNRELIABLE_FRAGMENT && channel->outgoingUnreliableSequenceNumber < 0xFFFF) { commandNumber = ENET_PROTOCOL_COMMAND_SEND_UNRELIABLE_FRAGMENT; startSequenceNumber = ENET_HOST_TO_NET_16(channel->outgoingUnreliableSequenceNumber + 1); } else { commandNumber = ENET_PROTOCOL_COMMAND_SEND_FRAGMENT | ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE; startSequenceNumber = ENET_HOST_TO_NET_16(channel->outgoingReliableSequenceNumber + 1); } enet_list_clear(&fragments); for (fragmentNumber = 0, fragmentOffset = 0; fragmentOffset < packet->dataLength; ++fragmentNumber, fragmentOffset += fragmentLength) { if (packet->dataLength - fragmentOffset < fragmentLength) { fragmentLength = packet->dataLength - fragmentOffset; } fragment = (ENetOutgoingCommand *) enet_malloc(sizeof(ENetOutgoingCommand)); if (fragment == NULL) { while (!enet_list_empty(&fragments)) { fragment = (ENetOutgoingCommand *) enet_list_remove(enet_list_begin(&fragments)); enet_free(fragment); } return -1; } fragment->fragmentOffset = fragmentOffset; fragment->fragmentLength = fragmentLength; fragment->packet = packet; fragment->command.header.command = commandNumber; fragment->command.header.channelID = channelID; fragment->command.sendFragment.startSequenceNumber = startSequenceNumber; fragment->command.sendFragment.dataLength = ENET_HOST_TO_NET_16(fragmentLength); fragment->command.sendFragment.fragmentCount = ENET_HOST_TO_NET_32(fragmentCount); fragment->command.sendFragment.fragmentNumber = ENET_HOST_TO_NET_32(fragmentNumber); fragment->command.sendFragment.totalLength = ENET_HOST_TO_NET_32(packet->dataLength); fragment->command.sendFragment.fragmentOffset = ENET_NET_TO_HOST_32(fragmentOffset); enet_list_insert(enet_list_end(&fragments), fragment); } packet->referenceCount += fragmentNumber; while (!enet_list_empty(&fragments)) { fragment = (ENetOutgoingCommand *) enet_list_remove(enet_list_begin(&fragments)); enet_peer_setup_outgoing_command(peer, fragment); } return 0; } command.header.channelID = channelID; if ((packet->flags & (ENET_PACKET_FLAG_RELIABLE | ENET_PACKET_FLAG_UNSEQUENCED)) == ENET_PACKET_FLAG_UNSEQUENCED) { command.header.command = ENET_PROTOCOL_COMMAND_SEND_UNSEQUENCED | ENET_PROTOCOL_COMMAND_FLAG_UNSEQUENCED; command.sendUnsequenced.dataLength = ENET_HOST_TO_NET_16(packet->dataLength); } else if (packet->flags & ENET_PACKET_FLAG_RELIABLE || channel->outgoingUnreliableSequenceNumber >= 0xFFFF) { command.header.command = ENET_PROTOCOL_COMMAND_SEND_RELIABLE | ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE; command.sendReliable.dataLength = ENET_HOST_TO_NET_16(packet->dataLength); } else { command.header.command = ENET_PROTOCOL_COMMAND_SEND_UNRELIABLE; command.sendUnreliable.dataLength = ENET_HOST_TO_NET_16(packet->dataLength); } if (enet_peer_queue_outgoing_command(peer, &command, packet, 0, packet->dataLength) == NULL) { return -1; } return 0; } // enet_peer_send /** Attempts to dequeue any incoming queued packet. * @param peer peer to dequeue packets from * @param channelID holds the channel ID of the channel the packet was received on success * @returns a pointer to the packet, or NULL if there are no available incoming queued packets */ ENetPacket * enet_peer_receive(ENetPeer *peer, enet_uint8 *channelID) { ENetIncomingCommand *incomingCommand; ENetPacket *packet; if (enet_list_empty(&peer->dispatchedCommands)) { return NULL; } incomingCommand = (ENetIncomingCommand *) enet_list_remove(enet_list_begin(&peer->dispatchedCommands)); if (channelID != NULL) { *channelID = incomingCommand->command.header.channelID; } packet = incomingCommand->packet; --packet->referenceCount; if (incomingCommand->fragments != NULL) { enet_free(incomingCommand->fragments); } enet_free(incomingCommand); peer->totalWaitingData -= packet->dataLength; return packet; } static void enet_peer_reset_outgoing_commands(ENetList *queue) { ENetOutgoingCommand *outgoingCommand; while (!enet_list_empty(queue)) { outgoingCommand = (ENetOutgoingCommand *) enet_list_remove(enet_list_begin(queue)); if (outgoingCommand->packet != NULL) { --outgoingCommand->packet->referenceCount; if (outgoingCommand->packet->referenceCount == 0) { callbacks.packet_destroy(outgoingCommand->packet); } } enet_free(outgoingCommand); } } static void enet_peer_remove_incoming_commands(ENetList *queue, ENetListIterator startCommand, ENetListIterator endCommand) { ENET_UNUSED(queue) ENetListIterator currentCommand; for (currentCommand = startCommand; currentCommand != endCommand;) { ENetIncomingCommand *incomingCommand = (ENetIncomingCommand *) currentCommand; currentCommand = enet_list_next(currentCommand); enet_list_remove(&incomingCommand->incomingCommandList); if (incomingCommand->packet != NULL) { --incomingCommand->packet->referenceCount; if (incomingCommand->packet->referenceCount == 0) { callbacks.packet_destroy(incomingCommand->packet); } } if (incomingCommand->fragments != NULL) { enet_free(incomingCommand->fragments); } enet_free(incomingCommand); } } static void enet_peer_reset_incoming_commands(ENetList *queue) { enet_peer_remove_incoming_commands(queue, enet_list_begin(queue), enet_list_end(queue)); } void enet_peer_reset_queues(ENetPeer *peer) { ENetChannel *channel; if (peer->needsDispatch) { enet_list_remove(&peer->dispatchList); peer->needsDispatch = 0; } while (!enet_list_empty(&peer->acknowledgements)) { enet_free(enet_list_remove(enet_list_begin(&peer->acknowledgements))); } enet_peer_reset_outgoing_commands(&peer->sentReliableCommands); enet_peer_reset_outgoing_commands(&peer->sentUnreliableCommands); enet_peer_reset_outgoing_commands(&peer->outgoingReliableCommands); enet_peer_reset_outgoing_commands(&peer->outgoingUnreliableCommands); enet_peer_reset_incoming_commands(&peer->dispatchedCommands); if (peer->channels != NULL && peer->channelCount > 0) { for (channel = peer->channels; channel < &peer->channels[peer->channelCount]; ++channel) { enet_peer_reset_incoming_commands(&channel->incomingReliableCommands); enet_peer_reset_incoming_commands(&channel->incomingUnreliableCommands); } enet_free(peer->channels); } peer->channels = NULL; peer->channelCount = 0; } void enet_peer_on_connect(ENetPeer *peer) { if (peer->state != ENET_PEER_STATE_CONNECTED && peer->state != ENET_PEER_STATE_DISCONNECT_LATER) { if (peer->incomingBandwidth != 0) { ++peer->host->bandwidthLimitedPeers; } ++peer->host->connectedPeers; } } void enet_peer_on_disconnect(ENetPeer *peer) { if (peer->state == ENET_PEER_STATE_CONNECTED || peer->state == ENET_PEER_STATE_DISCONNECT_LATER) { if (peer->incomingBandwidth != 0) { --peer->host->bandwidthLimitedPeers; } --peer->host->connectedPeers; } } /** Forcefully disconnects a peer. * @param peer peer to forcefully disconnect * @remarks The foreign host represented by the peer is not notified of the disconnection and will timeout * on its connection to the local host. */ void enet_peer_reset(ENetPeer *peer) { enet_peer_on_disconnect(peer); // We don't want to reset connectID here, otherwise, we can't get it in the Disconnect event // peer->connectID = 0; peer->outgoingPeerID = ENET_PROTOCOL_MAXIMUM_PEER_ID; peer->state = ENET_PEER_STATE_DISCONNECTED; peer->incomingBandwidth = 0; peer->outgoingBandwidth = 0; peer->incomingBandwidthThrottleEpoch = 0; peer->outgoingBandwidthThrottleEpoch = 0; peer->incomingDataTotal = 0; peer->totalDataReceived = 0; peer->outgoingDataTotal = 0; peer->totalDataSent = 0; peer->lastSendTime = 0; peer->lastReceiveTime = 0; peer->nextTimeout = 0; peer->earliestTimeout = 0; peer->packetLossEpoch = 0; peer->packetsSent = 0; peer->totalPacketsSent = 0; peer->packetsLost = 0; peer->totalPacketsLost = 0; peer->packetLoss = 0; peer->packetLossVariance = 0; peer->packetThrottle = ENET_PEER_DEFAULT_PACKET_THROTTLE; peer->packetThrottleLimit = ENET_PEER_PACKET_THROTTLE_SCALE; peer->packetThrottleCounter = 0; peer->packetThrottleEpoch = 0; peer->packetThrottleAcceleration = ENET_PEER_PACKET_THROTTLE_ACCELERATION; peer->packetThrottleDeceleration = ENET_PEER_PACKET_THROTTLE_DECELERATION; peer->packetThrottleInterval = ENET_PEER_PACKET_THROTTLE_INTERVAL; peer->pingInterval = ENET_PEER_PING_INTERVAL; peer->timeoutLimit = ENET_PEER_TIMEOUT_LIMIT; peer->timeoutMinimum = ENET_PEER_TIMEOUT_MINIMUM; peer->timeoutMaximum = ENET_PEER_TIMEOUT_MAXIMUM; peer->lastRoundTripTime = ENET_PEER_DEFAULT_ROUND_TRIP_TIME; peer->lowestRoundTripTime = ENET_PEER_DEFAULT_ROUND_TRIP_TIME; peer->lastRoundTripTimeVariance = 0; peer->highestRoundTripTimeVariance = 0; peer->roundTripTime = ENET_PEER_DEFAULT_ROUND_TRIP_TIME; peer->roundTripTimeVariance = 0; peer->mtu = peer->host->mtu; peer->reliableDataInTransit = 0; peer->outgoingReliableSequenceNumber = 0; peer->windowSize = ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE; peer->incomingUnsequencedGroup = 0; peer->outgoingUnsequencedGroup = 0; peer->eventData = 0; peer->totalWaitingData = 0; memset(peer->unsequencedWindow, 0, sizeof(peer->unsequencedWindow)); enet_peer_reset_queues(peer); } /** Sends a ping request to a peer. * @param peer destination for the ping request * @remarks ping requests factor into the mean round trip time as designated by the * roundTripTime field in the ENetPeer structure. ENet automatically pings all connected * peers at regular intervals, however, this function may be called to ensure more * frequent ping requests. */ void enet_peer_ping(ENetPeer *peer) { ENetProtocol command; if (peer->state != ENET_PEER_STATE_CONNECTED) { return; } command.header.command = ENET_PROTOCOL_COMMAND_PING | ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE; command.header.channelID = 0xFF; enet_peer_queue_outgoing_command(peer, &command, NULL, 0, 0); } /** Sets the interval at which pings will be sent to a peer. * * Pings are used both to monitor the liveness of the connection and also to dynamically * adjust the throttle during periods of low traffic so that the throttle has reasonable * responsiveness during traffic spikes. * * @param peer the peer to adjust * @param pingInterval the interval at which to send pings; defaults to ENET_PEER_PING_INTERVAL if 0 */ void enet_peer_ping_interval(ENetPeer *peer, enet_uint32 pingInterval) { peer->pingInterval = pingInterval ? pingInterval : ENET_PEER_PING_INTERVAL; } /** Sets the timeout parameters for a peer. * * The timeout parameter control how and when a peer will timeout from a failure to acknowledge * reliable traffic. Timeout values use an exponential backoff mechanism, where if a reliable * packet is not acknowledge within some multiple of the average RTT plus a variance tolerance, * the timeout will be doubled until it reaches a set limit. If the timeout is thus at this * limit and reliable packets have been sent but not acknowledged within a certain minimum time * period, the peer will be disconnected. Alternatively, if reliable packets have been sent * but not acknowledged for a certain maximum time period, the peer will be disconnected regardless * of the current timeout limit value. * * @param peer the peer to adjust * @param timeoutLimit the timeout limit; defaults to ENET_PEER_TIMEOUT_LIMIT if 0 * @param timeoutMinimum the timeout minimum; defaults to ENET_PEER_TIMEOUT_MINIMUM if 0 * @param timeoutMaximum the timeout maximum; defaults to ENET_PEER_TIMEOUT_MAXIMUM if 0 */ void enet_peer_timeout(ENetPeer *peer, enet_uint32 timeoutLimit, enet_uint32 timeoutMinimum, enet_uint32 timeoutMaximum) { peer->timeoutLimit = timeoutLimit ? timeoutLimit : ENET_PEER_TIMEOUT_LIMIT; peer->timeoutMinimum = timeoutMinimum ? timeoutMinimum : ENET_PEER_TIMEOUT_MINIMUM; peer->timeoutMaximum = timeoutMaximum ? timeoutMaximum : ENET_PEER_TIMEOUT_MAXIMUM; } /** Force an immediate disconnection from a peer. * @param peer peer to disconnect * @param data data describing the disconnection * @remarks No ENET_EVENT_DISCONNECT event will be generated. The foreign peer is not * guaranteed to receive the disconnect notification, and is reset immediately upon * return from this function. */ void enet_peer_disconnect_now(ENetPeer *peer, enet_uint32 data) { ENetProtocol command; if (peer->state == ENET_PEER_STATE_DISCONNECTED) { return; } if (peer->state != ENET_PEER_STATE_ZOMBIE && peer->state != ENET_PEER_STATE_DISCONNECTING) { enet_peer_reset_queues(peer); command.header.command = ENET_PROTOCOL_COMMAND_DISCONNECT | ENET_PROTOCOL_COMMAND_FLAG_UNSEQUENCED; command.header.channelID = 0xFF; command.disconnect.data = ENET_HOST_TO_NET_32(data); enet_peer_queue_outgoing_command(peer, &command, NULL, 0, 0); enet_host_flush(peer->host); } enet_peer_reset(peer); } /** Request a disconnection from a peer. * @param peer peer to request a disconnection * @param data data describing the disconnection * @remarks An ENET_EVENT_DISCONNECT event will be generated by enet_host_service() * once the disconnection is complete. */ void enet_peer_disconnect(ENetPeer *peer, enet_uint32 data) { ENetProtocol command; if (peer->state == ENET_PEER_STATE_DISCONNECTING || peer->state == ENET_PEER_STATE_DISCONNECTED || peer->state == ENET_PEER_STATE_ACKNOWLEDGING_DISCONNECT || peer->state == ENET_PEER_STATE_ZOMBIE ) { return; } enet_peer_reset_queues(peer); command.header.command = ENET_PROTOCOL_COMMAND_DISCONNECT; command.header.channelID = 0xFF; command.disconnect.data = ENET_HOST_TO_NET_32(data); if (peer->state == ENET_PEER_STATE_CONNECTED || peer->state == ENET_PEER_STATE_DISCONNECT_LATER) { command.header.command |= ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE; } else { command.header.command |= ENET_PROTOCOL_COMMAND_FLAG_UNSEQUENCED; } enet_peer_queue_outgoing_command(peer, &command, NULL, 0, 0); if (peer->state == ENET_PEER_STATE_CONNECTED || peer->state == ENET_PEER_STATE_DISCONNECT_LATER) { enet_peer_on_disconnect(peer); peer->state = ENET_PEER_STATE_DISCONNECTING; } else { enet_host_flush(peer->host); enet_peer_reset(peer); } } /** Request a disconnection from a peer, but only after all queued outgoing packets are sent. * @param peer peer to request a disconnection * @param data data describing the disconnection * @remarks An ENET_EVENT_DISCONNECT event will be generated by enet_host_service() * once the disconnection is complete. */ void enet_peer_disconnect_later(ENetPeer *peer, enet_uint32 data) { if ((peer->state == ENET_PEER_STATE_CONNECTED || peer->state == ENET_PEER_STATE_DISCONNECT_LATER) && !(enet_list_empty(&peer->outgoingReliableCommands) && enet_list_empty(&peer->outgoingUnreliableCommands) && enet_list_empty(&peer->sentReliableCommands)) ) { peer->state = ENET_PEER_STATE_DISCONNECT_LATER; peer->eventData = data; } else { enet_peer_disconnect(peer, data); } } ENetAcknowledgement *enet_peer_queue_acknowledgement(ENetPeer *peer, const ENetProtocol *command, enet_uint16 sentTime) { ENetAcknowledgement *acknowledgement; if (command->header.channelID < peer->channelCount) { ENetChannel *channel = &peer->channels[command->header.channelID]; enet_uint16 reliableWindow = command->header.reliableSequenceNumber / ENET_PEER_RELIABLE_WINDOW_SIZE; enet_uint16 currentWindow = channel->incomingReliableSequenceNumber / ENET_PEER_RELIABLE_WINDOW_SIZE; if (command->header.reliableSequenceNumber < channel->incomingReliableSequenceNumber) { reliableWindow += ENET_PEER_RELIABLE_WINDOWS; } if (reliableWindow >= currentWindow + ENET_PEER_FREE_RELIABLE_WINDOWS - 1 && reliableWindow <= currentWindow + ENET_PEER_FREE_RELIABLE_WINDOWS) { return NULL; } } acknowledgement = (ENetAcknowledgement *) enet_malloc(sizeof(ENetAcknowledgement)); if (acknowledgement == NULL) { return NULL; } peer->outgoingDataTotal += sizeof(ENetProtocolAcknowledge); acknowledgement->sentTime = sentTime; acknowledgement->command = *command; enet_list_insert(enet_list_end(&peer->acknowledgements), acknowledgement); return acknowledgement; } void enet_peer_setup_outgoing_command(ENetPeer *peer, ENetOutgoingCommand *outgoingCommand) { ENetChannel *channel = &peer->channels[outgoingCommand->command.header.channelID]; peer->outgoingDataTotal += enet_protocol_command_size(outgoingCommand->command.header.command) + outgoingCommand->fragmentLength; if (outgoingCommand->command.header.channelID == 0xFF) { ++peer->outgoingReliableSequenceNumber; outgoingCommand->reliableSequenceNumber = peer->outgoingReliableSequenceNumber; outgoingCommand->unreliableSequenceNumber = 0; } else if (outgoingCommand->command.header.command & ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE) { ++channel->outgoingReliableSequenceNumber; channel->outgoingUnreliableSequenceNumber = 0; outgoingCommand->reliableSequenceNumber = channel->outgoingReliableSequenceNumber; outgoingCommand->unreliableSequenceNumber = 0; } else if (outgoingCommand->command.header.command & ENET_PROTOCOL_COMMAND_FLAG_UNSEQUENCED) { ++peer->outgoingUnsequencedGroup; outgoingCommand->reliableSequenceNumber = 0; outgoingCommand->unreliableSequenceNumber = 0; } else { if (outgoingCommand->fragmentOffset == 0) { ++channel->outgoingUnreliableSequenceNumber; } outgoingCommand->reliableSequenceNumber = channel->outgoingReliableSequenceNumber; outgoingCommand->unreliableSequenceNumber = channel->outgoingUnreliableSequenceNumber; } outgoingCommand->sendAttempts = 0; outgoingCommand->sentTime = 0; outgoingCommand->roundTripTimeout = 0; outgoingCommand->roundTripTimeoutLimit = 0; outgoingCommand->command.header.reliableSequenceNumber = ENET_HOST_TO_NET_16(outgoingCommand->reliableSequenceNumber); switch (outgoingCommand->command.header.command & ENET_PROTOCOL_COMMAND_MASK) { case ENET_PROTOCOL_COMMAND_SEND_UNRELIABLE: outgoingCommand->command.sendUnreliable.unreliableSequenceNumber = ENET_HOST_TO_NET_16(outgoingCommand->unreliableSequenceNumber); break; case ENET_PROTOCOL_COMMAND_SEND_UNSEQUENCED: outgoingCommand->command.sendUnsequenced.unsequencedGroup = ENET_HOST_TO_NET_16(peer->outgoingUnsequencedGroup); break; default: break; } if (outgoingCommand->command.header.command & ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE) { enet_list_insert(enet_list_end(&peer->outgoingReliableCommands), outgoingCommand); } else { enet_list_insert(enet_list_end(&peer->outgoingUnreliableCommands), outgoingCommand); } } ENetOutgoingCommand * enet_peer_queue_outgoing_command(ENetPeer *peer, const ENetProtocol *command, ENetPacket *packet, enet_uint32 offset, enet_uint16 length) { ENetOutgoingCommand *outgoingCommand = (ENetOutgoingCommand *) enet_malloc(sizeof(ENetOutgoingCommand)); if (outgoingCommand == NULL) { return NULL; } outgoingCommand->command = *command; outgoingCommand->fragmentOffset = offset; outgoingCommand->fragmentLength = length; outgoingCommand->packet = packet; if (packet != NULL) { ++packet->referenceCount; } enet_peer_setup_outgoing_command(peer, outgoingCommand); return outgoingCommand; } void enet_peer_dispatch_incoming_unreliable_commands(ENetPeer *peer, ENetChannel *channel) { ENetListIterator droppedCommand, startCommand, currentCommand; for (droppedCommand = startCommand = currentCommand = enet_list_begin(&channel->incomingUnreliableCommands); currentCommand != enet_list_end(&channel->incomingUnreliableCommands); currentCommand = enet_list_next(currentCommand) ) { ENetIncomingCommand *incomingCommand = (ENetIncomingCommand *) currentCommand; if ((incomingCommand->command.header.command & ENET_PROTOCOL_COMMAND_MASK) == ENET_PROTOCOL_COMMAND_SEND_UNSEQUENCED) { continue; } if (incomingCommand->reliableSequenceNumber == channel->incomingReliableSequenceNumber) { if (incomingCommand->fragmentsRemaining <= 0) { channel->incomingUnreliableSequenceNumber = incomingCommand->unreliableSequenceNumber; continue; } if (startCommand != currentCommand) { enet_list_move(enet_list_end(&peer->dispatchedCommands), startCommand, enet_list_previous(currentCommand)); if (!peer->needsDispatch) { enet_list_insert(enet_list_end(&peer->host->dispatchQueue), &peer->dispatchList); peer->needsDispatch = 1; } droppedCommand = currentCommand; } else if (droppedCommand != currentCommand) { droppedCommand = enet_list_previous(currentCommand); } } else { enet_uint16 reliableWindow = incomingCommand->reliableSequenceNumber / ENET_PEER_RELIABLE_WINDOW_SIZE; enet_uint16 currentWindow = channel->incomingReliableSequenceNumber / ENET_PEER_RELIABLE_WINDOW_SIZE; if (incomingCommand->reliableSequenceNumber < channel->incomingReliableSequenceNumber) { reliableWindow += ENET_PEER_RELIABLE_WINDOWS; } if (reliableWindow >= currentWindow && reliableWindow < currentWindow + ENET_PEER_FREE_RELIABLE_WINDOWS - 1) { break; } droppedCommand = enet_list_next(currentCommand); if (startCommand != currentCommand) { enet_list_move(enet_list_end(&peer->dispatchedCommands), startCommand, enet_list_previous(currentCommand)); if (!peer->needsDispatch) { enet_list_insert(enet_list_end(&peer->host->dispatchQueue), &peer->dispatchList); peer->needsDispatch = 1; } } } startCommand = enet_list_next(currentCommand); } if (startCommand != currentCommand) { enet_list_move(enet_list_end(&peer->dispatchedCommands), startCommand, enet_list_previous(currentCommand)); if (!peer->needsDispatch) { enet_list_insert(enet_list_end(&peer->host->dispatchQueue), &peer->dispatchList); peer->needsDispatch = 1; } droppedCommand = currentCommand; } enet_peer_remove_incoming_commands(&channel->incomingUnreliableCommands,enet_list_begin(&channel->incomingUnreliableCommands), droppedCommand); } void enet_peer_dispatch_incoming_reliable_commands(ENetPeer *peer, ENetChannel *channel) { ENetListIterator currentCommand; for (currentCommand = enet_list_begin(&channel->incomingReliableCommands); currentCommand != enet_list_end(&channel->incomingReliableCommands); currentCommand = enet_list_next(currentCommand) ) { ENetIncomingCommand *incomingCommand = (ENetIncomingCommand *) currentCommand; if (incomingCommand->fragmentsRemaining > 0 || incomingCommand->reliableSequenceNumber != (enet_uint16) (channel->incomingReliableSequenceNumber + 1)) { break; } channel->incomingReliableSequenceNumber = incomingCommand->reliableSequenceNumber; if (incomingCommand->fragmentCount > 0) { channel->incomingReliableSequenceNumber += incomingCommand->fragmentCount - 1; } } if (currentCommand == enet_list_begin(&channel->incomingReliableCommands)) { return; } channel->incomingUnreliableSequenceNumber = 0; enet_list_move(enet_list_end(&peer->dispatchedCommands), enet_list_begin(&channel->incomingReliableCommands), enet_list_previous(currentCommand)); if (!peer->needsDispatch) { enet_list_insert(enet_list_end(&peer->host->dispatchQueue), &peer->dispatchList); peer->needsDispatch = 1; } if (!enet_list_empty(&channel->incomingUnreliableCommands)) { enet_peer_dispatch_incoming_unreliable_commands(peer, channel); } } ENetIncomingCommand * enet_peer_queue_incoming_command(ENetPeer *peer, const ENetProtocol *command, const void *data, size_t dataLength, enet_uint32 flags, enet_uint32 fragmentCount) { static ENetIncomingCommand dummyCommand; ENetChannel *channel = &peer->channels[command->header.channelID]; enet_uint32 unreliableSequenceNumber = 0, reliableSequenceNumber = 0; enet_uint16 reliableWindow, currentWindow; ENetIncomingCommand *incomingCommand; ENetListIterator currentCommand; ENetPacket *packet = NULL; if (peer->state == ENET_PEER_STATE_DISCONNECT_LATER) { goto discardCommand; } if ((command->header.command & ENET_PROTOCOL_COMMAND_MASK) != ENET_PROTOCOL_COMMAND_SEND_UNSEQUENCED) { reliableSequenceNumber = command->header.reliableSequenceNumber; reliableWindow = reliableSequenceNumber / ENET_PEER_RELIABLE_WINDOW_SIZE; currentWindow = channel->incomingReliableSequenceNumber / ENET_PEER_RELIABLE_WINDOW_SIZE; if (reliableSequenceNumber < channel->incomingReliableSequenceNumber) { reliableWindow += ENET_PEER_RELIABLE_WINDOWS; } if (reliableWindow < currentWindow || reliableWindow >= currentWindow + ENET_PEER_FREE_RELIABLE_WINDOWS - 1) { goto discardCommand; } } switch (command->header.command & ENET_PROTOCOL_COMMAND_MASK) { case ENET_PROTOCOL_COMMAND_SEND_FRAGMENT: case ENET_PROTOCOL_COMMAND_SEND_RELIABLE: if (reliableSequenceNumber == channel->incomingReliableSequenceNumber) { goto discardCommand; } for (currentCommand = enet_list_previous(enet_list_end(&channel->incomingReliableCommands)); currentCommand != enet_list_end(&channel->incomingReliableCommands); currentCommand = enet_list_previous(currentCommand) ) { incomingCommand = (ENetIncomingCommand *) currentCommand; if (reliableSequenceNumber >= channel->incomingReliableSequenceNumber) { if (incomingCommand->reliableSequenceNumber < channel->incomingReliableSequenceNumber) { continue; } } else if (incomingCommand->reliableSequenceNumber >= channel->incomingReliableSequenceNumber) { break; } if (incomingCommand->reliableSequenceNumber <= reliableSequenceNumber) { if (incomingCommand->reliableSequenceNumber < reliableSequenceNumber) { break; } goto discardCommand; } } break; case ENET_PROTOCOL_COMMAND_SEND_UNRELIABLE: case ENET_PROTOCOL_COMMAND_SEND_UNRELIABLE_FRAGMENT: unreliableSequenceNumber = ENET_NET_TO_HOST_16(command->sendUnreliable.unreliableSequenceNumber); if (reliableSequenceNumber == channel->incomingReliableSequenceNumber && unreliableSequenceNumber <= channel->incomingUnreliableSequenceNumber) { goto discardCommand; } for (currentCommand = enet_list_previous(enet_list_end(&channel->incomingUnreliableCommands)); currentCommand != enet_list_end(&channel->incomingUnreliableCommands); currentCommand = enet_list_previous(currentCommand) ) { incomingCommand = (ENetIncomingCommand *) currentCommand; if ((command->header.command & ENET_PROTOCOL_COMMAND_MASK) == ENET_PROTOCOL_COMMAND_SEND_UNSEQUENCED) { continue; } if (reliableSequenceNumber >= channel->incomingReliableSequenceNumber) { if (incomingCommand->reliableSequenceNumber < channel->incomingReliableSequenceNumber) { continue; } } else if (incomingCommand->reliableSequenceNumber >= channel->incomingReliableSequenceNumber) { break; } if (incomingCommand->reliableSequenceNumber < reliableSequenceNumber) { break; } if (incomingCommand->reliableSequenceNumber > reliableSequenceNumber) { continue; } if (incomingCommand->unreliableSequenceNumber <= unreliableSequenceNumber) { if (incomingCommand->unreliableSequenceNumber < unreliableSequenceNumber) { break; } goto discardCommand; } } break; case ENET_PROTOCOL_COMMAND_SEND_UNSEQUENCED: currentCommand = enet_list_end(&channel->incomingUnreliableCommands); break; default: goto discardCommand; } if (peer->totalWaitingData >= peer->host->maximumWaitingData) { goto notifyError; } packet = callbacks.packet_create(data, dataLength, flags); if (packet == NULL) { goto notifyError; } incomingCommand = (ENetIncomingCommand *) enet_malloc(sizeof(ENetIncomingCommand)); if (incomingCommand == NULL) { goto notifyError; } incomingCommand->reliableSequenceNumber = command->header.reliableSequenceNumber; incomingCommand->unreliableSequenceNumber = unreliableSequenceNumber & 0xFFFF; incomingCommand->command = *command; incomingCommand->fragmentCount = fragmentCount; incomingCommand->fragmentsRemaining = fragmentCount; incomingCommand->packet = packet; incomingCommand->fragments = NULL; if (fragmentCount > 0) { if (fragmentCount <= ENET_PROTOCOL_MAXIMUM_FRAGMENT_COUNT) { incomingCommand->fragments = (enet_uint32 *) enet_malloc((fragmentCount + 31) / 32 * sizeof(enet_uint32)); } if (incomingCommand->fragments == NULL) { enet_free(incomingCommand); goto notifyError; } memset(incomingCommand->fragments, 0, (fragmentCount + 31) / 32 * sizeof(enet_uint32)); } if (packet != NULL) { ++packet->referenceCount; peer->totalWaitingData += packet->dataLength; } enet_list_insert(enet_list_next(currentCommand), incomingCommand); switch (command->header.command & ENET_PROTOCOL_COMMAND_MASK) { case ENET_PROTOCOL_COMMAND_SEND_FRAGMENT: case ENET_PROTOCOL_COMMAND_SEND_RELIABLE: enet_peer_dispatch_incoming_reliable_commands(peer, channel); break; default: enet_peer_dispatch_incoming_unreliable_commands(peer, channel); break; } return incomingCommand; discardCommand: if (fragmentCount > 0) { goto notifyError; } if (packet != NULL && packet->referenceCount == 0) { callbacks.packet_destroy(packet); } return &dummyCommand; notifyError: if (packet != NULL && packet->referenceCount == 0) { callbacks.packet_destroy(packet); } return NULL; } /* enet_peer_queue_incoming_command */ // =======================================================================// // ! // ! Host // ! // =======================================================================// /** Creates a host for communicating to peers. * * @param address the address at which other peers may connect to this host. If NULL, then no peers may connect to the host. * @param peerCount the maximum number of peers that should be allocated for the host. * @param channelLimit the maximum number of channels allowed; if 0, then this is equivalent to ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT * @param incomingBandwidth downstream bandwidth of the host in bytes/second; if 0, ENet will assume unlimited bandwidth. * @param outgoingBandwidth upstream bandwidth of the host in bytes/second; if 0, ENet will assume unlimited bandwidth. * * @returns the host on success and NULL on failure * * @remarks ENet will strategically drop packets on specific sides of a connection between hosts * to ensure the host's bandwidth is not overwhelmed. The bandwidth parameters also determine * the window size of a connection which limits the amount of reliable packets that may be in transit * at any given time. */ ENetHost * enet_host_create(const ENetAddress *address, size_t peerCount, size_t channelLimit, enet_uint32 incomingBandwidth, enet_uint32 outgoingBandwidth) { ENetHost *host; ENetPeer *currentPeer; if (peerCount > ENET_PROTOCOL_MAXIMUM_PEER_ID) { return NULL; } host = (ENetHost *) enet_malloc(sizeof(ENetHost)); if (host == NULL) { return NULL; } memset(host, 0, sizeof(ENetHost)); host->peers = (ENetPeer *) enet_malloc(peerCount * sizeof(ENetPeer)); if (host->peers == NULL) { enet_free(host); return NULL; } memset(host->peers, 0, peerCount * sizeof(ENetPeer)); host->socket = enet_socket_create(ENET_SOCKET_TYPE_DATAGRAM); if (host->socket != ENET_SOCKET_NULL) { enet_socket_set_option (host->socket, ENET_SOCKOPT_IPV6_V6ONLY, 0); } if (host->socket == ENET_SOCKET_NULL || (address != NULL && enet_socket_bind(host->socket, address) < 0)) { if (host->socket != ENET_SOCKET_NULL) { enet_socket_destroy(host->socket); } enet_free(host->peers); enet_free(host); return NULL; } enet_socket_set_option(host->socket, ENET_SOCKOPT_NONBLOCK, 1); enet_socket_set_option(host->socket, ENET_SOCKOPT_BROADCAST, 1); enet_socket_set_option(host->socket, ENET_SOCKOPT_RCVBUF, ENET_HOST_RECEIVE_BUFFER_SIZE); enet_socket_set_option(host->socket, ENET_SOCKOPT_SNDBUF, ENET_HOST_SEND_BUFFER_SIZE); enet_socket_set_option(host->socket, ENET_SOCKOPT_IPV6_V6ONLY, 0); if (address != NULL && enet_socket_get_address(host->socket, &host->address) < 0) { host->address = *address; } if (!channelLimit || channelLimit > ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT) { channelLimit = ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT; } else if (channelLimit < ENET_PROTOCOL_MINIMUM_CHANNEL_COUNT) { channelLimit = ENET_PROTOCOL_MINIMUM_CHANNEL_COUNT; } host->randomSeed = (enet_uint32) (size_t) host; host->randomSeed += enet_host_random_seed(); host->randomSeed = (host->randomSeed << 16) | (host->randomSeed >> 16); host->channelLimit = channelLimit; host->incomingBandwidth = incomingBandwidth; host->outgoingBandwidth = outgoingBandwidth; host->bandwidthThrottleEpoch = 0; host->recalculateBandwidthLimits = 0; host->mtu = ENET_HOST_DEFAULT_MTU; host->peerCount = peerCount; host->commandCount = 0; host->bufferCount = 0; host->checksum = NULL; host->receivedAddress.host = ENET_HOST_ANY; host->receivedAddress.port = 0; host->receivedData = NULL; host->receivedDataLength = 0; host->totalSentData = 0; host->totalSentPackets = 0; host->totalReceivedData = 0; host->totalReceivedPackets = 0; host->connectedPeers = 0; host->bandwidthLimitedPeers = 0; host->duplicatePeers = ENET_PROTOCOL_MAXIMUM_PEER_ID; host->maximumPacketSize = ENET_HOST_DEFAULT_MAXIMUM_PACKET_SIZE; host->maximumWaitingData = ENET_HOST_DEFAULT_MAXIMUM_WAITING_DATA; host->compressor.context = NULL; host->compressor.compress = NULL; host->compressor.decompress = NULL; host->compressor.destroy = NULL; host->intercept = NULL; enet_list_clear(&host->dispatchQueue); for (currentPeer = host->peers; currentPeer < &host->peers[host->peerCount]; ++currentPeer) { currentPeer->host = host; currentPeer->incomingPeerID = currentPeer - host->peers; currentPeer->outgoingSessionID = currentPeer->incomingSessionID = 0xFF; currentPeer->data = NULL; enet_list_clear(¤tPeer->acknowledgements); enet_list_clear(¤tPeer->sentReliableCommands); enet_list_clear(¤tPeer->sentUnreliableCommands); enet_list_clear(¤tPeer->outgoingReliableCommands); enet_list_clear(¤tPeer->outgoingUnreliableCommands); enet_list_clear(¤tPeer->dispatchedCommands); enet_peer_reset(currentPeer); } return host; } /* enet_host_create */ /** Destroys the host and all resources associated with it. * @param host pointer to the host to destroy */ void enet_host_destroy(ENetHost *host) { ENetPeer *currentPeer; if (host == NULL) { return; } enet_socket_destroy(host->socket); for (currentPeer = host->peers; currentPeer < &host->peers[host->peerCount]; ++currentPeer) { enet_peer_reset(currentPeer); } if (host->compressor.context != NULL && host->compressor.destroy) { (*host->compressor.destroy)(host->compressor.context); } enet_free(host->peers); enet_free(host); } /** Initiates a connection to a foreign host. * @param host host seeking the connection * @param address destination for the connection * @param channelCount number of channels to allocate * @param data user data supplied to the receiving host * @returns a peer representing the foreign host on success, NULL on failure * @remarks The peer returned will have not completed the connection until enet_host_service() * notifies of an ENET_EVENT_TYPE_CONNECT event for the peer. */ ENetPeer * enet_host_connect(ENetHost *host, const ENetAddress *address, size_t channelCount, enet_uint32 data) { ENetPeer *currentPeer; ENetChannel *channel; ENetProtocol command; if (channelCount < ENET_PROTOCOL_MINIMUM_CHANNEL_COUNT) { channelCount = ENET_PROTOCOL_MINIMUM_CHANNEL_COUNT; } else if (channelCount > ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT) { channelCount = ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT; } for (currentPeer = host->peers; currentPeer < &host->peers[host->peerCount]; ++currentPeer) { if (currentPeer->state == ENET_PEER_STATE_DISCONNECTED) { break; } } if (currentPeer >= &host->peers[host->peerCount]) { return NULL; } currentPeer->channels = (ENetChannel *) enet_malloc(channelCount * sizeof(ENetChannel)); if (currentPeer->channels == NULL) { return NULL; } currentPeer->channelCount = channelCount; currentPeer->state = ENET_PEER_STATE_CONNECTING; currentPeer->address = *address; currentPeer->connectID = ++host->randomSeed; if (host->outgoingBandwidth == 0) { currentPeer->windowSize = ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE; } else { currentPeer->windowSize = (host->outgoingBandwidth / ENET_PEER_WINDOW_SIZE_SCALE) * ENET_PROTOCOL_MINIMUM_WINDOW_SIZE; } if (currentPeer->windowSize < ENET_PROTOCOL_MINIMUM_WINDOW_SIZE) { currentPeer->windowSize = ENET_PROTOCOL_MINIMUM_WINDOW_SIZE; } else if (currentPeer->windowSize > ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE) { currentPeer->windowSize = ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE; } for (channel = currentPeer->channels; channel < ¤tPeer->channels[channelCount]; ++channel) { channel->outgoingReliableSequenceNumber = 0; channel->outgoingUnreliableSequenceNumber = 0; channel->incomingReliableSequenceNumber = 0; channel->incomingUnreliableSequenceNumber = 0; enet_list_clear(&channel->incomingReliableCommands); enet_list_clear(&channel->incomingUnreliableCommands); channel->usedReliableWindows = 0; memset(channel->reliableWindows, 0, sizeof(channel->reliableWindows)); } command.header.command = ENET_PROTOCOL_COMMAND_CONNECT | ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE; command.header.channelID = 0xFF; command.connect.outgoingPeerID = ENET_HOST_TO_NET_16(currentPeer->incomingPeerID); command.connect.incomingSessionID = currentPeer->incomingSessionID; command.connect.outgoingSessionID = currentPeer->outgoingSessionID; command.connect.mtu = ENET_HOST_TO_NET_32(currentPeer->mtu); command.connect.windowSize = ENET_HOST_TO_NET_32(currentPeer->windowSize); command.connect.channelCount = ENET_HOST_TO_NET_32(channelCount); command.connect.incomingBandwidth = ENET_HOST_TO_NET_32(host->incomingBandwidth); command.connect.outgoingBandwidth = ENET_HOST_TO_NET_32(host->outgoingBandwidth); command.connect.packetThrottleInterval = ENET_HOST_TO_NET_32(currentPeer->packetThrottleInterval); command.connect.packetThrottleAcceleration = ENET_HOST_TO_NET_32(currentPeer->packetThrottleAcceleration); command.connect.packetThrottleDeceleration = ENET_HOST_TO_NET_32(currentPeer->packetThrottleDeceleration); command.connect.connectID = currentPeer->connectID; command.connect.data = ENET_HOST_TO_NET_32(data); enet_peer_queue_outgoing_command(currentPeer, &command, NULL, 0, 0); return currentPeer; } /* enet_host_connect */ /** Queues a packet to be sent to all peers associated with the host. * @param host host on which to broadcast the packet * @param channelID channel on which to broadcast * @param packet packet to broadcast */ void enet_host_broadcast(ENetHost *host, enet_uint8 channelID, ENetPacket *packet) { ENetPeer *currentPeer; for (currentPeer = host->peers; currentPeer < &host->peers[host->peerCount]; ++currentPeer) { if (currentPeer->state != ENET_PEER_STATE_CONNECTED) { continue; } enet_peer_send(currentPeer, channelID, packet); } if (packet->referenceCount == 0) { callbacks.packet_destroy(packet); } } /** Sends raw data to specified address. Useful when you want to send unconnected data using host's socket. * @param host host sending data * @param address destination address * @param data data pointer * @param dataLength length of data to send * @retval >=0 bytes sent * @retval <0 error * @sa enet_socket_send */ int enet_host_send_raw(ENetHost *host, const ENetAddress* address, enet_uint8* data, size_t dataLength) { ENetBuffer buffer; buffer.data = data; buffer.dataLength = dataLength; return enet_socket_send(host->socket, address, &buffer, 1); } /** Sends raw data to specified address with extended arguments. Allows to send only part of data, handy for other programming languages. * I.e. if you have data =- { 0, 1, 2, 3 } and call function as enet_host_send_raw_ex(data, 1, 2) then it will skip 1 byte and send 2 bytes { 1, 2 }. * @param host host sending data * @param address destination address * @param data data pointer * @param skipBytes number of bytes to skip from start of data * @param bytesToSend number of bytes to send * @retval >=0 bytes sent * @retval <0 error * @sa enet_socket_send */ int enet_host_send_raw_ex(ENetHost *host, const ENetAddress* address, enet_uint8* data, size_t skipBytes, size_t bytesToSend) { ENetBuffer buffer; buffer.data = data + skipBytes; buffer.dataLength = bytesToSend; return enet_socket_send(host->socket, address, &buffer, 1); } /** Sets intercept callback for the host. * @param host host to set a callback * @param callback intercept callback */ void enet_host_set_intercept(ENetHost *host, const ENetInterceptCallback callback) { host->intercept = callback; } /** Sets the packet compressor the host should use to compress and decompress packets. * @param host host to enable or disable compression for * @param compressor callbacks for for the packet compressor; if NULL, then compression is disabled */ void enet_host_compress(ENetHost *host, const ENetCompressor *compressor) { if (host->compressor.context != NULL && host->compressor.destroy) { (*host->compressor.destroy)(host->compressor.context); } if (compressor) { host->compressor = *compressor; } else { host->compressor.context = NULL; } } /** Limits the maximum allowed channels of future incoming connections. * @param host host to limit * @param channelLimit the maximum number of channels allowed; if 0, then this is equivalent to ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT */ void enet_host_channel_limit(ENetHost *host, size_t channelLimit) { if (!channelLimit || channelLimit > ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT) { channelLimit = ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT; } else if (channelLimit < ENET_PROTOCOL_MINIMUM_CHANNEL_COUNT) { channelLimit = ENET_PROTOCOL_MINIMUM_CHANNEL_COUNT; } host->channelLimit = channelLimit; } /** Adjusts the bandwidth limits of a host. * @param host host to adjust * @param incomingBandwidth new incoming bandwidth * @param outgoingBandwidth new outgoing bandwidth * @remarks the incoming and outgoing bandwidth parameters are identical in function to those * specified in enet_host_create(). */ void enet_host_bandwidth_limit(ENetHost *host, enet_uint32 incomingBandwidth, enet_uint32 outgoingBandwidth) { host->incomingBandwidth = incomingBandwidth; host->outgoingBandwidth = outgoingBandwidth; host->recalculateBandwidthLimits = 1; } void enet_host_bandwidth_throttle(ENetHost *host) { enet_uint32 timeCurrent = enet_time_get(); enet_uint32 elapsedTime = timeCurrent - host->bandwidthThrottleEpoch; enet_uint32 peersRemaining = (enet_uint32) host->connectedPeers; enet_uint32 dataTotal = ~0; enet_uint32 bandwidth = ~0; enet_uint32 throttle = 0; enet_uint32 bandwidthLimit = 0; int needsAdjustment = host->bandwidthLimitedPeers > 0 ? 1 : 0; ENetPeer *peer; ENetProtocol command; if (elapsedTime < ENET_HOST_BANDWIDTH_THROTTLE_INTERVAL) { return; } if (host->outgoingBandwidth == 0 && host->incomingBandwidth == 0) { return; } host->bandwidthThrottleEpoch = timeCurrent; if (peersRemaining == 0) { return; } if (host->outgoingBandwidth != 0) { dataTotal = 0; bandwidth = (host->outgoingBandwidth * elapsedTime) / 1000; for (peer = host->peers; peer < &host->peers[host->peerCount]; ++peer) { if (peer->state != ENET_PEER_STATE_CONNECTED && peer->state != ENET_PEER_STATE_DISCONNECT_LATER) { continue; } dataTotal += peer->outgoingDataTotal; } } while (peersRemaining > 0 && needsAdjustment != 0) { needsAdjustment = 0; if (dataTotal <= bandwidth) { throttle = ENET_PEER_PACKET_THROTTLE_SCALE; } else { throttle = (bandwidth * ENET_PEER_PACKET_THROTTLE_SCALE) / dataTotal; } for (peer = host->peers; peer < &host->peers[host->peerCount]; ++peer) { enet_uint32 peerBandwidth; if ((peer->state != ENET_PEER_STATE_CONNECTED && peer->state != ENET_PEER_STATE_DISCONNECT_LATER) || peer->incomingBandwidth == 0 || peer->outgoingBandwidthThrottleEpoch == timeCurrent ) { continue; } peerBandwidth = (peer->incomingBandwidth * elapsedTime) / 1000; if ((throttle * peer->outgoingDataTotal) / ENET_PEER_PACKET_THROTTLE_SCALE <= peerBandwidth) { continue; } peer->packetThrottleLimit = (peerBandwidth * ENET_PEER_PACKET_THROTTLE_SCALE) / peer->outgoingDataTotal; if (peer->packetThrottleLimit == 0) { peer->packetThrottleLimit = 1; } if (peer->packetThrottle > peer->packetThrottleLimit) { peer->packetThrottle = peer->packetThrottleLimit; } peer->outgoingBandwidthThrottleEpoch = timeCurrent; peer->incomingDataTotal = 0; peer->outgoingDataTotal = 0; needsAdjustment = 1; --peersRemaining; bandwidth -= peerBandwidth; dataTotal -= peerBandwidth; } } if (peersRemaining > 0) { if (dataTotal <= bandwidth) { throttle = ENET_PEER_PACKET_THROTTLE_SCALE; } else { throttle = (bandwidth * ENET_PEER_PACKET_THROTTLE_SCALE) / dataTotal; } for (peer = host->peers; peer < &host->peers[host->peerCount]; ++peer) { if ((peer->state != ENET_PEER_STATE_CONNECTED && peer->state != ENET_PEER_STATE_DISCONNECT_LATER) || peer->outgoingBandwidthThrottleEpoch == timeCurrent) { continue; } peer->packetThrottleLimit = throttle; if (peer->packetThrottle > peer->packetThrottleLimit) { peer->packetThrottle = peer->packetThrottleLimit; } peer->incomingDataTotal = 0; peer->outgoingDataTotal = 0; } } if (host->recalculateBandwidthLimits) { host->recalculateBandwidthLimits = 0; peersRemaining = (enet_uint32) host->connectedPeers; bandwidth = host->incomingBandwidth; needsAdjustment = 1; if (bandwidth == 0) { bandwidthLimit = 0; } else { while (peersRemaining > 0 && needsAdjustment != 0) { needsAdjustment = 0; bandwidthLimit = bandwidth / peersRemaining; for (peer = host->peers; peer < &host->peers[host->peerCount]; ++peer) { if ((peer->state != ENET_PEER_STATE_CONNECTED && peer->state != ENET_PEER_STATE_DISCONNECT_LATER) || peer->incomingBandwidthThrottleEpoch == timeCurrent ) { continue; } if (peer->outgoingBandwidth > 0 && peer->outgoingBandwidth >= bandwidthLimit) { continue; } peer->incomingBandwidthThrottleEpoch = timeCurrent; needsAdjustment = 1; --peersRemaining; bandwidth -= peer->outgoingBandwidth; } } } for (peer = host->peers; peer < &host->peers[host->peerCount]; ++peer) { if (peer->state != ENET_PEER_STATE_CONNECTED && peer->state != ENET_PEER_STATE_DISCONNECT_LATER) { continue; } command.header.command = ENET_PROTOCOL_COMMAND_BANDWIDTH_LIMIT | ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE; command.header.channelID = 0xFF; command.bandwidthLimit.outgoingBandwidth = ENET_HOST_TO_NET_32(host->outgoingBandwidth); if (peer->incomingBandwidthThrottleEpoch == timeCurrent) { command.bandwidthLimit.incomingBandwidth = ENET_HOST_TO_NET_32(peer->outgoingBandwidth); } else { command.bandwidthLimit.incomingBandwidth = ENET_HOST_TO_NET_32(bandwidthLimit); } enet_peer_queue_outgoing_command(peer, &command, NULL, 0, 0); } } } /* enet_host_bandwidth_throttle */ // =======================================================================// // ! // ! Compat // ! // =======================================================================// #if (defined _MSC_VER && defined _WIN32 && !defined _WIN64) || \ (defined __MINGW32__ && !defined __MINGW64__) // (!defined _WIN32 && defined __MINGW32__ && defined ENET_MINGW_COMPAT) //< @r-lyeh #define MUST_DEFINE_NTOP_PTON 1 #else #define MUST_DEFINE_NTOP_PTON 0 #endif #if MUST_DEFINE_NTOP_PTON //< @r-lyeh // #if defined(__MINGW32__) && defined(ENET_MINGW_COMPAT) //< @r-lyeh // inet_ntop/inet_pton for MinGW from http://mingw-users.1079350.n2.nabble.com/IPv6-getaddrinfo-amp-inet-ntop-td5891996.html const char *inet_ntop(int af, const void *src, char *dst, socklen_t cnt) { if (af == AF_INET) { struct sockaddr_in in; memset(&in, 0, sizeof(in)); in.sin_family = AF_INET; memcpy(&in.sin_addr, src, sizeof(struct in_addr)); getnameinfo((struct sockaddr *)&in, sizeof(struct sockaddr_in), dst, cnt, NULL, 0, NI_NUMERICHOST); return dst; } else if (af == AF_INET6) { struct sockaddr_in6 in; memset(&in, 0, sizeof(in)); in.sin6_family = AF_INET6; memcpy(&in.sin6_addr, src, sizeof(struct in_addr6)); getnameinfo((struct sockaddr *)&in, sizeof(struct sockaddr_in6), dst, cnt, NULL, 0, NI_NUMERICHOST); return dst; } return NULL; } #define NS_INADDRSZ 4 #define NS_IN6ADDRSZ 16 #define NS_INT16SZ 2 int inet_pton4(const char *src, char *dst) { uint8_t tmp[NS_INADDRSZ], *tp; int saw_digit = 0; int octets = 0; *(tp = tmp) = 0; int ch; while ((ch = *src++) != '\0') { if (ch >= '0' && ch <= '9') { uint32_t n = *tp * 10 + (ch - '0'); if (saw_digit && *tp == 0) return 0; if (n > 255) return 0; *tp = n; if (!saw_digit) { if (++octets > 4) return 0; saw_digit = 1; } } else if (ch == '.' && saw_digit) { if (octets == 4) return 0; *++tp = 0; saw_digit = 0; } else return 0; } if (octets < 4) return 0; memcpy(dst, tmp, NS_INADDRSZ); return 1; } int inet_pton6(const char *src, char *dst) { static const char xdigits[] = "0123456789abcdef"; uint8_t tmp[NS_IN6ADDRSZ]; uint8_t *tp = (uint8_t*) memset(tmp, '\0', NS_IN6ADDRSZ); uint8_t *endp = tp + NS_IN6ADDRSZ; uint8_t *colonp = NULL; /* Leading :: requires some special handling. */ if (*src == ':') { if (*++src != ':') return 0; } const char *curtok = src; int saw_xdigit = 0; uint32_t val = 0; int ch; while ((ch = tolower(*src++)) != '\0') { const char *pch = strchr(xdigits, ch); if (pch != NULL) { val <<= 4; val |= (pch - xdigits); if (val > 0xffff) return 0; saw_xdigit = 1; continue; } if (ch == ':') { curtok = src; if (!saw_xdigit) { if (colonp) return 0; colonp = tp; continue; } else if (*src == '\0') { return 0; } if (tp + NS_INT16SZ > endp) return 0; *tp++ = (uint8_t) (val >> 8) & 0xff; *tp++ = (uint8_t) val & 0xff; saw_xdigit = 0; val = 0; continue; } if (ch == '.' && ((tp + NS_INADDRSZ) <= endp) && inet_pton4(curtok, (char*) tp) > 0) { tp += NS_INADDRSZ; saw_xdigit = 0; break; /* '\0' was seen by inet_pton4(). */ } return 0; } if (saw_xdigit) { if (tp + NS_INT16SZ > endp) return 0; *tp++ = (uint8_t) (val >> 8) & 0xff; *tp++ = (uint8_t) val & 0xff; } if (colonp != NULL) { /* * Since some memmove()'s erroneously fail to handle * overlapping regions, we'll do the shift by hand. */ const int n = tp - colonp; if (tp == endp) return 0; for (int i = 1; i <= n; i++) { endp[-i] = colonp[n - i]; colonp[n - i] = 0; } tp = endp; } if (tp != endp) return 0; memcpy(dst, tmp, NS_IN6ADDRSZ); return 1; } int inet_pton(int af, const char *src, struct in6_addr *dst) { switch (af) { case AF_INET: return inet_pton4(src, (char *)dst); case AF_INET6: return inet_pton6(src, (char *)dst); default: return -1; } } #endif // MUST_DEFINE_NTOP_PTON //< @r-lyeh // =======================================================================// // ! // ! Time // ! // =======================================================================// #ifdef _WIN32 static LARGE_INTEGER getFILETIMEoffset() { SYSTEMTIME s; FILETIME f; LARGE_INTEGER t; s.wYear = 1970; s.wMonth = 1; s.wDay = 1; s.wHour = 0; s.wMinute = 0; s.wSecond = 0; s.wMilliseconds = 0; SystemTimeToFileTime(&s, &f); t.QuadPart = f.dwHighDateTime; t.QuadPart <<= 32; t.QuadPart |= f.dwLowDateTime; return (t); } #ifdef __MINGW32__ #define clock_gettime clock_gettime2 // symbol defined in pthread_time.h instead. workaround to avoid user linking against -lpthread. #endif // #ifndef CLOCK_MONOTONIC_RAW // #define CLOCK_MONOTONIC_RAW 0 // for zig-cc // #endif int clock_gettime(int X, struct timespec *tv) { LARGE_INTEGER t; FILETIME f; double microseconds; static LARGE_INTEGER offset; static double frequencyToMicroseconds; static int initialized = 0; static BOOL usePerformanceCounter = 0; if (!initialized) { LARGE_INTEGER performanceFrequency; initialized = 1; usePerformanceCounter = QueryPerformanceFrequency(&performanceFrequency); if (usePerformanceCounter) { QueryPerformanceCounter(&offset); frequencyToMicroseconds = (double)performanceFrequency.QuadPart / 1000000.; } else { offset = getFILETIMEoffset(); frequencyToMicroseconds = 10.; } } if (usePerformanceCounter) { QueryPerformanceCounter(&t); } else { GetSystemTimeAsFileTime(&f); t.QuadPart = f.dwHighDateTime; t.QuadPart <<= 32; t.QuadPart |= f.dwLowDateTime; } t.QuadPart -= offset.QuadPart; microseconds = (double)t.QuadPart / frequencyToMicroseconds; t.QuadPart = (LONGLONG)microseconds; tv->tv_sec = (long)(t.QuadPart / 1000000); tv->tv_nsec = t.QuadPart % 1000000 * 1000; return (0); } #elif __APPLE__ && __MAC_OS_X_VERSION_MIN_REQUIRED < 101200 #define CLOCK_MONOTONIC 0 int clock_gettime(int X, struct timespec *ts) { clock_serv_t cclock; mach_timespec_t mts; host_get_clock_service(mach_host_self(), SYSTEM_CLOCK, &cclock); clock_get_time(cclock, &mts); mach_port_deallocate(mach_task_self(), cclock); ts->tv_sec = mts.tv_sec; ts->tv_nsec = mts.tv_nsec; return 0; } #endif enet_uint32 enet_time_get() { // TODO enet uses 32 bit timestamps. We should modify it to use // 64 bit timestamps, but this is not trivial since we'd end up // changing half the structs in enet. For now, retain 32 bits, but // use an offset so we don't run out of bits. Basically, the first // call of enet_time_get() will always return 1, and follow-up calls // indicate elapsed time since the first call. // // Note that we don't want to return 0 from the first call, in case // some part of enet uses 0 as a special value (meaning time not set // for example). static uint64_t start_time_ns = 0; struct timespec ts; #if defined(CLOCK_MONOTONIC_RAW) clock_gettime(CLOCK_MONOTONIC_RAW, &ts); #else clock_gettime(CLOCK_MONOTONIC, &ts); #endif static const uint64_t ns_in_s = 1000 * 1000 * 1000; static const uint64_t ns_in_ms = 1000 * 1000; uint64_t current_time_ns = ts.tv_nsec + (uint64_t)ts.tv_sec * ns_in_s; // Most of the time we just want to atomically read the start time. We // could just use a single CAS instruction instead of this if, but it // would be slower in the average case. // // Note that statics are auto-initialized to zero, and starting a thread // implies a memory barrier. So we know that whatever thread calls this, // it correctly sees the start_time_ns as 0 initially. uint64_t offset_ns = ENET_ATOMIC_READ(&start_time_ns); if (offset_ns == 0) { // We still need to CAS, since two different threads can get here // at the same time. // // We assume that current_time_ns is > 1ms. // // Set the value of the start_time_ns, such that the first timestamp // is at 1ms. This ensures 0 remains a special value. uint64_t want_value = current_time_ns - 1 * ns_in_ms; uint64_t old_value = ENET_ATOMIC_CAS(&start_time_ns, 0, want_value); offset_ns = old_value == 0 ? want_value : old_value; } uint64_t result_in_ns = current_time_ns - offset_ns; return (enet_uint32)(result_in_ns / ns_in_ms); } void enet_inaddr_map4to6(struct in_addr in, struct in6_addr *out) { if (in.s_addr == 0x00000000) { /* 0.0.0.0 */ *out = enet_v6_anyaddr; } else if (in.s_addr == 0xFFFFFFFF) { /* 255.255.255.255 */ *out = enet_v6_noaddr; } else { *out = enet_v4_anyaddr; out->s6_addr[10] = 0xFF; out->s6_addr[11] = 0xFF; out->s6_addr[12] = ((uint8_t *)&in.s_addr)[0]; out->s6_addr[13] = ((uint8_t *)&in.s_addr)[1]; out->s6_addr[14] = ((uint8_t *)&in.s_addr)[2]; out->s6_addr[15] = ((uint8_t *)&in.s_addr)[3]; } } void enet_inaddr_map6to4(const struct in6_addr *in, struct in_addr *out) { memset(out, 0, sizeof(struct in_addr)); ((uint8_t *)&out->s_addr)[0] = in->s6_addr[12]; ((uint8_t *)&out->s_addr)[1] = in->s6_addr[13]; ((uint8_t *)&out->s_addr)[2] = in->s6_addr[14]; ((uint8_t *)&out->s_addr)[3] = in->s6_addr[15]; } int enet_in6addr_lookup_host(const char *name, bool nodns, struct in6_addr *out) { struct addrinfo hints, *resultList = NULL, *result = NULL; memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_UNSPEC; if (nodns) { hints.ai_flags = AI_NUMERICHOST; /* prevent actual DNS lookups! */ } if (getaddrinfo(name, NULL, &hints, &resultList) != 0) { if (resultList != NULL) { freeaddrinfo(resultList); } return -1; } for (result = resultList; result != NULL; result = result->ai_next) { if (result->ai_addr != NULL) { if (result->ai_family == AF_INET || (result->ai_family == AF_UNSPEC && result->ai_addrlen == sizeof(struct sockaddr_in))) { enet_inaddr_map4to6(((struct sockaddr_in*)result->ai_addr)->sin_addr, out); if (resultList != NULL) { freeaddrinfo(resultList); } return 0; } else if (result->ai_family == AF_INET6 || (result->ai_family == AF_UNSPEC && result->ai_addrlen == sizeof(struct sockaddr_in6))) { memcpy(out, &((struct sockaddr_in6*)result->ai_addr)->sin6_addr, sizeof(struct in6_addr)); if (resultList != NULL) { freeaddrinfo(resultList); } return 0; } } } if (resultList != NULL) { freeaddrinfo(resultList); } return -1; } int enet_address_set_host_ip_new(ENetAddress *address, const char *name) { return enet_in6addr_lookup_host(name, true, &address->host); } int enet_address_set_host_new(ENetAddress *address, const char *name) { return enet_in6addr_lookup_host(name, false, &address->host); } int enet_address_get_host_ip_new(const ENetAddress *address, char *name, size_t nameLength) { if (IN6_IS_ADDR_V4MAPPED(&address->host)) { struct in_addr buf; enet_inaddr_map6to4(&address->host, &buf); if (inet_ntop(AF_INET, &buf, name, nameLength) == NULL) { return -1; } } else { if (inet_ntop(AF_INET6, &address->host, name, nameLength) == NULL) { return -1; } } return 0; } /* enet_address_get_host_ip_new */ int enet_address_get_host_new(const ENetAddress *address, char *name, size_t nameLength) { struct sockaddr_in6 sin; memset(&sin, 0, sizeof(struct sockaddr_in6)); int err; sin.sin6_family = AF_INET6; sin.sin6_port = ENET_HOST_TO_NET_16 (address->port); sin.sin6_addr = address->host; sin.sin6_scope_id = address->sin6_scope_id; err = getnameinfo((struct sockaddr *) &sin, sizeof(sin), name, nameLength, NULL, 0, NI_NAMEREQD); if (!err) { if (name != NULL && nameLength > 0 && !memchr(name, '\0', nameLength)) { return -1; } return 0; } if (err != EAI_NONAME) { return -1; } return enet_address_get_host_ip_new(address, name, nameLength); } /* enet_address_get_host_new */ // =======================================================================// // ! // ! Platform Specific (Unix) // ! // =======================================================================// #ifndef _WIN32 int enet_initialize(void) { return 0; } void enet_deinitialize(void) {} enet_uint64 enet_host_random_seed(void) { return (enet_uint64) time(NULL); } int enet_address_set_host_ip_old(ENetAddress *address, const char *name) { if (!inet_pton(AF_INET6, name, &address->host)) { return -1; } return 0; } int enet_address_set_host_old(ENetAddress *address, const char *name) { struct addrinfo hints, *resultList = NULL, *result = NULL; memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_UNSPEC; if (getaddrinfo(name, NULL, &hints, &resultList) != 0) { return -1; } for (result = resultList; result != NULL; result = result->ai_next) { if (result->ai_addr != NULL && result->ai_addrlen >= sizeof(struct sockaddr_in)) { if (result->ai_family == AF_INET) { struct sockaddr_in * sin = (struct sockaddr_in *) result->ai_addr; ((uint32_t *)&address->host.s6_addr)[0] = 0; ((uint32_t *)&address->host.s6_addr)[1] = 0; ((uint32_t *)&address->host.s6_addr)[2] = htonl(0xffff); ((uint32_t *)&address->host.s6_addr)[3] = sin->sin_addr.s_addr; freeaddrinfo(resultList); return 0; } else if(result->ai_family == AF_INET6) { struct sockaddr_in6 * sin = (struct sockaddr_in6 *)result->ai_addr; address->host = sin->sin6_addr; address->sin6_scope_id = sin->sin6_scope_id; freeaddrinfo(resultList); return 0; } } } if (resultList != NULL) { freeaddrinfo(resultList); } return enet_address_set_host_ip(address, name); } /* enet_address_set_host_old */ int enet_address_get_host_ip_old(const ENetAddress *address, char *name, size_t nameLength) { if (inet_ntop(AF_INET6, &address->host, name, nameLength) == NULL) { return -1; } return 0; } int enet_address_get_host_old(const ENetAddress *address, char *name, size_t nameLength) { struct sockaddr_in6 sin; int err; memset(&sin, 0, sizeof(struct sockaddr_in6)); sin.sin6_family = AF_INET6; sin.sin6_port = ENET_HOST_TO_NET_16 (address->port); sin.sin6_addr = address->host; sin.sin6_scope_id = address->sin6_scope_id; err = getnameinfo((struct sockaddr *) &sin, sizeof(sin), name, nameLength, NULL, 0, NI_NAMEREQD); if (!err) { if (name != NULL && nameLength > 0 && !memchr(name, '\0', nameLength)) { return -1; } return 0; } if (err != EAI_NONAME) { return -1; } return enet_address_get_host_ip(address, name, nameLength); } /* enet_address_get_host_old */ int enet_socket_bind(ENetSocket socket, const ENetAddress *address) { struct sockaddr_in6 sin; memset(&sin, 0, sizeof(struct sockaddr_in6)); sin.sin6_family = AF_INET6; if (address != NULL) { sin.sin6_port = ENET_HOST_TO_NET_16(address->port); sin.sin6_addr = address->host; sin.sin6_scope_id = address->sin6_scope_id; } else { sin.sin6_port = 0; sin.sin6_addr = ENET_HOST_ANY; sin.sin6_scope_id = 0; } return bind(socket, (struct sockaddr *)&sin, sizeof(struct sockaddr_in6)); } int enet_socket_get_address(ENetSocket socket, ENetAddress *address) { struct sockaddr_in6 sin; socklen_t sinLength = sizeof(struct sockaddr_in6); if (getsockname(socket, (struct sockaddr *) &sin, &sinLength) == -1) { return -1; } address->host = sin.sin6_addr; address->port = ENET_NET_TO_HOST_16(sin.sin6_port); address->sin6_scope_id = sin.sin6_scope_id; return 0; } int enet_socket_listen(ENetSocket socket, int backlog) { return listen(socket, backlog < 0 ? SOMAXCONN : backlog); } ENetSocket enet_socket_create(ENetSocketType type) { return socket(PF_INET6, type == ENET_SOCKET_TYPE_DATAGRAM ? SOCK_DGRAM : SOCK_STREAM, 0); } int enet_socket_set_option(ENetSocket socket, ENetSocketOption option, int value) { int result = -1; switch (option) { case ENET_SOCKOPT_NONBLOCK: result = fcntl(socket, F_SETFL, (value ? O_NONBLOCK : 0) | (fcntl(socket, F_GETFL) & ~O_NONBLOCK)); break; case ENET_SOCKOPT_BROADCAST: result = setsockopt(socket, SOL_SOCKET, SO_BROADCAST, (char *)&value, sizeof(int)); break; case ENET_SOCKOPT_REUSEADDR: result = setsockopt(socket, SOL_SOCKET, SO_REUSEADDR, (char *)&value, sizeof(int)); break; case ENET_SOCKOPT_RCVBUF: result = setsockopt(socket, SOL_SOCKET, SO_RCVBUF, (char *)&value, sizeof(int)); break; case ENET_SOCKOPT_SNDBUF: result = setsockopt(socket, SOL_SOCKET, SO_SNDBUF, (char *)&value, sizeof(int)); break; case ENET_SOCKOPT_RCVTIMEO: { struct timeval timeVal; timeVal.tv_sec = value / 1000; timeVal.tv_usec = (value % 1000) * 1000; result = setsockopt(socket, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeVal, sizeof(struct timeval)); break; } case ENET_SOCKOPT_SNDTIMEO: { struct timeval timeVal; timeVal.tv_sec = value / 1000; timeVal.tv_usec = (value % 1000) * 1000; result = setsockopt(socket, SOL_SOCKET, SO_SNDTIMEO, (char *)&timeVal, sizeof(struct timeval)); break; } case ENET_SOCKOPT_NODELAY: result = setsockopt(socket, IPPROTO_TCP, TCP_NODELAY, (char *)&value, sizeof(int)); break; case ENET_SOCKOPT_IPV6_V6ONLY: result = setsockopt(socket, IPPROTO_IPV6, IPV6_V6ONLY, (char *)&value, sizeof(int)); break; default: break; } return result == -1 ? -1 : 0; } /* enet_socket_set_option */ int enet_socket_get_option(ENetSocket socket, ENetSocketOption option, int *value) { int result = -1; socklen_t len; switch (option) { case ENET_SOCKOPT_ERROR: len = sizeof(int); result = getsockopt(socket, SOL_SOCKET, SO_ERROR, value, &len); break; default: break; } return result == -1 ? -1 : 0; } int enet_socket_connect(ENetSocket socket, const ENetAddress *address) { struct sockaddr_in6 sin; int result; memset(&sin, 0, sizeof(struct sockaddr_in6)); sin.sin6_family = AF_INET6; sin.sin6_port = ENET_HOST_TO_NET_16(address->port); sin.sin6_addr = address->host; sin.sin6_scope_id = address->sin6_scope_id; result = connect(socket, (struct sockaddr *)&sin, sizeof(struct sockaddr_in6)); if (result == -1 && errno == EINPROGRESS) { return 0; } return result; } ENetSocket enet_socket_accept(ENetSocket socket, ENetAddress *address) { int result; struct sockaddr_in6 sin; socklen_t sinLength = sizeof(struct sockaddr_in6); result = accept(socket,address != NULL ? (struct sockaddr *) &sin : NULL, address != NULL ? &sinLength : NULL); if (result == -1) { return ENET_SOCKET_NULL; } if (address != NULL) { address->host = sin.sin6_addr; address->port = ENET_NET_TO_HOST_16 (sin.sin6_port); address->sin6_scope_id = sin.sin6_scope_id; } return result; } int enet_socket_shutdown(ENetSocket socket, ENetSocketShutdown how) { return shutdown(socket, (int) how); } void enet_socket_destroy(ENetSocket socket) { if (socket != -1) { close(socket); } } int enet_socket_send(ENetSocket socket, const ENetAddress *address, const ENetBuffer *buffers, size_t bufferCount) { struct msghdr msgHdr; struct sockaddr_in6 sin; int sentLength; memset(&msgHdr, 0, sizeof(struct msghdr)); if (address != NULL) { memset(&sin, 0, sizeof(struct sockaddr_in6)); sin.sin6_family = AF_INET6; sin.sin6_port = ENET_HOST_TO_NET_16(address->port); sin.sin6_addr = address->host; sin.sin6_scope_id = address->sin6_scope_id; msgHdr.msg_name = &sin; msgHdr.msg_namelen = sizeof(struct sockaddr_in6); } msgHdr.msg_iov = (struct iovec *) buffers; msgHdr.msg_iovlen = bufferCount; sentLength = sendmsg(socket, &msgHdr, MSG_NOSIGNAL); if (sentLength == -1) { if (errno == EWOULDBLOCK) { return 0; } return -1; } return sentLength; } /* enet_socket_send */ int enet_socket_receive(ENetSocket socket, ENetAddress *address, ENetBuffer *buffers, size_t bufferCount) { struct msghdr msgHdr; struct sockaddr_in6 sin; int recvLength; memset(&msgHdr, 0, sizeof(struct msghdr)); if (address != NULL) { msgHdr.msg_name = &sin; msgHdr.msg_namelen = sizeof(struct sockaddr_in6); } msgHdr.msg_iov = (struct iovec *) buffers; msgHdr.msg_iovlen = bufferCount; recvLength = recvmsg(socket, &msgHdr, MSG_NOSIGNAL); if (recvLength == -1) { if (errno == EWOULDBLOCK) { return 0; } return -1; } if (msgHdr.msg_flags & MSG_TRUNC) { return -1; } if (address != NULL) { address->host = sin.sin6_addr; address->port = ENET_NET_TO_HOST_16(sin.sin6_port); address->sin6_scope_id = sin.sin6_scope_id; } return recvLength; } /* enet_socket_receive */ int enet_socketset_select(ENetSocket maxSocket, ENetSocketSet *readSet, ENetSocketSet *writeSet, enet_uint32 timeout) { struct timeval timeVal; timeVal.tv_sec = timeout / 1000; timeVal.tv_usec = (timeout % 1000) * 1000; return select(maxSocket + 1, readSet, writeSet, NULL, &timeVal); } int enet_socket_wait(ENetSocket socket, enet_uint32 *condition, enet_uint64 timeout) { struct pollfd pollSocket; int pollCount; pollSocket.fd = socket; pollSocket.events = 0; if (*condition & ENET_SOCKET_WAIT_SEND) { pollSocket.events |= POLLOUT; } if (*condition & ENET_SOCKET_WAIT_RECEIVE) { pollSocket.events |= POLLIN; } pollCount = poll(&pollSocket, 1, timeout); if (pollCount < 0) { if (errno == EINTR && *condition & ENET_SOCKET_WAIT_INTERRUPT) { *condition = ENET_SOCKET_WAIT_INTERRUPT; return 0; } return -1; } *condition = ENET_SOCKET_WAIT_NONE; if (pollCount == 0) { return 0; } if (pollSocket.revents & POLLOUT) { *condition |= ENET_SOCKET_WAIT_SEND; } if (pollSocket.revents & POLLIN) { *condition |= ENET_SOCKET_WAIT_RECEIVE; } return 0; } /* enet_socket_wait */ #endif // !_WIN32 // =======================================================================// // ! // ! Platform Specific (Win) // ! // =======================================================================// #ifdef _WIN32 int enet_initialize(void) { WORD versionRequested = MAKEWORD(1, 1); WSADATA wsaData; if (WSAStartup(versionRequested, &wsaData)) { return -1; } if (LOBYTE(wsaData.wVersion) != 1 || HIBYTE(wsaData.wVersion) != 1) { WSACleanup(); return -1; } timeBeginPeriod(1); return 0; } void enet_deinitialize(void) { timeEndPeriod(1); WSACleanup(); } enet_uint64 enet_host_random_seed(void) { return (enet_uint64) timeGetTime(); } int enet_address_set_host_ip_old(ENetAddress *address, const char *name) { enet_uint8 vals[4] = { 0, 0, 0, 0 }; int i; for (i = 0; i < 4; ++i) { const char *next = name + 1; if (*name != '0') { long val = strtol(name, (char **) &next, 10); if (val < 0 || val > 255 || next == name || next - name > 3) { return -1; } vals[i] = (enet_uint8) val; } if (*next != (i < 3 ? '.' : '\0')) { return -1; } name = next + 1; } memcpy(&address->host, vals, sizeof(enet_uint32)); return 0; } int enet_address_set_host_old(ENetAddress *address, const char *name) { struct hostent *hostEntry = NULL; hostEntry = gethostbyname(name); if (hostEntry == NULL || hostEntry->h_addrtype != AF_INET) { if (!inet_pton(AF_INET6, name, &address->host)) { return -1; } return 0; } ((enet_uint32 *)&address->host.s6_addr)[0] = 0; ((enet_uint32 *)&address->host.s6_addr)[1] = 0; ((enet_uint32 *)&address->host.s6_addr)[2] = htonl(0xffff); ((enet_uint32 *)&address->host.s6_addr)[3] = *(enet_uint32 *)hostEntry->h_addr_list[0]; return 0; } int enet_address_get_host_ip_old(const ENetAddress *address, char *name, size_t nameLength) { if (inet_ntop(AF_INET6, (PVOID)&address->host, name, nameLength) == NULL) { return -1; } return 0; } int enet_address_get_host_old(const ENetAddress *address, char *name, size_t nameLength) { struct in6_addr in; struct hostent *hostEntry = NULL; in = address->host; hostEntry = gethostbyaddr((char *)&in, sizeof(struct in6_addr), AF_INET6); if (hostEntry == NULL) { return enet_address_get_host_ip(address, name, nameLength); } else { size_t hostLen = strlen(hostEntry->h_name); if (hostLen >= nameLength) { return -1; } memcpy(name, hostEntry->h_name, hostLen + 1); } return 0; } int enet_socket_bind(ENetSocket socket, const ENetAddress *address) { struct sockaddr_in6 sin; memset(&sin, 0, sizeof(struct sockaddr_in6)); sin.sin6_family = AF_INET6; if (address != NULL) { sin.sin6_port = ENET_HOST_TO_NET_16 (address->port); sin.sin6_addr = address->host; sin.sin6_scope_id = address->sin6_scope_id; } else { sin.sin6_port = 0; sin.sin6_addr = in6addr_any; sin.sin6_scope_id = 0; } return bind(socket, (struct sockaddr *) &sin, sizeof(struct sockaddr_in6)) == SOCKET_ERROR ? -1 : 0; } int enet_socket_get_address(ENetSocket socket, ENetAddress *address) { struct sockaddr_in6 sin; int sinLength = sizeof(struct sockaddr_in6); if (getsockname(socket, (struct sockaddr *) &sin, &sinLength) == -1) { return -1; } address->host = sin.sin6_addr; address->port = ENET_NET_TO_HOST_16(sin.sin6_port); address->sin6_scope_id = sin.sin6_scope_id; return 0; } int enet_socket_listen(ENetSocket socket, int backlog) { return listen(socket, backlog < 0 ? SOMAXCONN : backlog) == SOCKET_ERROR ? -1 : 0; } ENetSocket enet_socket_create(ENetSocketType type) { return socket(PF_INET6, type == ENET_SOCKET_TYPE_DATAGRAM ? SOCK_DGRAM : SOCK_STREAM, 0); } int enet_socket_set_option(ENetSocket socket, ENetSocketOption option, int value) { int result = SOCKET_ERROR; switch (option) { case ENET_SOCKOPT_NONBLOCK: { u_long nonBlocking = (u_long) value; result = ioctlsocket(socket, FIONBIO, &nonBlocking); break; } case ENET_SOCKOPT_BROADCAST: result = setsockopt(socket, SOL_SOCKET, SO_BROADCAST, (char *)&value, sizeof(int)); break; case ENET_SOCKOPT_REUSEADDR: result = setsockopt(socket, SOL_SOCKET, SO_REUSEADDR, (char *)&value, sizeof(int)); break; case ENET_SOCKOPT_RCVBUF: result = setsockopt(socket, SOL_SOCKET, SO_RCVBUF, (char *)&value, sizeof(int)); break; case ENET_SOCKOPT_SNDBUF: result = setsockopt(socket, SOL_SOCKET, SO_SNDBUF, (char *)&value, sizeof(int)); break; case ENET_SOCKOPT_RCVTIMEO: result = setsockopt(socket, SOL_SOCKET, SO_RCVTIMEO, (char *)&value, sizeof(int)); break; case ENET_SOCKOPT_SNDTIMEO: result = setsockopt(socket, SOL_SOCKET, SO_SNDTIMEO, (char *)&value, sizeof(int)); break; case ENET_SOCKOPT_NODELAY: result = setsockopt(socket, IPPROTO_TCP, TCP_NODELAY, (char *)&value, sizeof(int)); break; case ENET_SOCKOPT_IPV6_V6ONLY: result = setsockopt(socket, IPPROTO_IPV6, IPV6_V6ONLY, (char *)&value, sizeof(int)); break; default: break; } return result == SOCKET_ERROR ? -1 : 0; } /* enet_socket_set_option */ int enet_socket_get_option(ENetSocket socket, ENetSocketOption option, int *value) { int result = SOCKET_ERROR, len; switch (option) { case ENET_SOCKOPT_ERROR: len = sizeof(int); result = getsockopt(socket, SOL_SOCKET, SO_ERROR, (char *)value, &len); break; default: break; } return result == SOCKET_ERROR ? -1 : 0; } int enet_socket_connect(ENetSocket socket, const ENetAddress *address) { struct sockaddr_in6 sin; int result; memset(&sin, 0, sizeof(struct sockaddr_in6)); sin.sin6_family = AF_INET6; sin.sin6_port = ENET_HOST_TO_NET_16(address->port); sin.sin6_addr = address->host; sin.sin6_scope_id = address->sin6_scope_id; result = connect(socket, (struct sockaddr *) &sin, sizeof(struct sockaddr_in6)); if (result == SOCKET_ERROR && WSAGetLastError() != WSAEWOULDBLOCK) { return -1; } return 0; } ENetSocket enet_socket_accept(ENetSocket socket, ENetAddress *address) { SOCKET result; struct sockaddr_in6 sin; int sinLength = sizeof(struct sockaddr_in6); result = accept(socket, address != NULL ? (struct sockaddr *)&sin : NULL, address != NULL ? &sinLength : NULL); if (result == INVALID_SOCKET) { return ENET_SOCKET_NULL; } if (address != NULL) { address->host = sin.sin6_addr; address->port = ENET_NET_TO_HOST_16(sin.sin6_port); address->sin6_scope_id = sin.sin6_scope_id; } return result; } int enet_socket_shutdown(ENetSocket socket, ENetSocketShutdown how) { return shutdown(socket, (int) how) == SOCKET_ERROR ? -1 : 0; } void enet_socket_destroy(ENetSocket socket) { if (socket != INVALID_SOCKET) { closesocket(socket); } } int enet_socket_send(ENetSocket socket, const ENetAddress *address, const ENetBuffer *buffers, size_t bufferCount) { struct sockaddr_in6 sin; DWORD sentLength; if (address != NULL) { memset(&sin, 0, sizeof(struct sockaddr_in6)); sin.sin6_family = AF_INET6; sin.sin6_port = ENET_HOST_TO_NET_16(address->port); sin.sin6_addr = address->host; sin.sin6_scope_id = address->sin6_scope_id; } if (WSASendTo(socket, (LPWSABUF) buffers, (DWORD) bufferCount, &sentLength, 0, address != NULL ? (struct sockaddr *) &sin : NULL, address != NULL ? sizeof(struct sockaddr_in6) : 0, NULL, NULL) == SOCKET_ERROR ) { return (WSAGetLastError() == WSAEWOULDBLOCK) ? 0 : -1; } return (int) sentLength; } int enet_socket_receive(ENetSocket socket, ENetAddress *address, ENetBuffer *buffers, size_t bufferCount) { INT sinLength = sizeof(struct sockaddr_in6); DWORD flags = 0, recvLength; struct sockaddr_in6 sin; if (WSARecvFrom(socket, (LPWSABUF) buffers, (DWORD) bufferCount, &recvLength, &flags, address != NULL ? (struct sockaddr *) &sin : NULL, address != NULL ? &sinLength : NULL, NULL, NULL) == SOCKET_ERROR ) { switch (WSAGetLastError()) { case WSAEWOULDBLOCK: case WSAECONNRESET: return 0; } return -1; } if (flags & MSG_PARTIAL) { return -1; } if (address != NULL) { address->host = sin.sin6_addr; address->port = ENET_NET_TO_HOST_16(sin.sin6_port); address->sin6_scope_id = sin.sin6_scope_id; } return (int) recvLength; } /* enet_socket_receive */ int enet_socketset_select(ENetSocket maxSocket, ENetSocketSet *readSet, ENetSocketSet *writeSet, enet_uint32 timeout) { struct timeval timeVal; timeVal.tv_sec = timeout / 1000; timeVal.tv_usec = (timeout % 1000) * 1000; return select(maxSocket + 1, readSet, writeSet, NULL, &timeVal); } int enet_socket_wait(ENetSocket socket, enet_uint32 *condition, enet_uint64 timeout) { fd_set readSet, writeSet; struct timeval timeVal; int selectCount; timeVal.tv_sec = timeout / 1000; timeVal.tv_usec = (timeout % 1000) * 1000; FD_ZERO(&readSet); FD_ZERO(&writeSet); if (*condition & ENET_SOCKET_WAIT_SEND) { FD_SET(socket, &writeSet); } if (*condition & ENET_SOCKET_WAIT_RECEIVE) { FD_SET(socket, &readSet); } selectCount = select(socket + 1, &readSet, &writeSet, NULL, &timeVal); if (selectCount < 0) { return -1; } *condition = ENET_SOCKET_WAIT_NONE; if (selectCount == 0) { return 0; } if (FD_ISSET(socket, &writeSet)) { *condition |= ENET_SOCKET_WAIT_SEND; } if (FD_ISSET(socket, &readSet)) { *condition |= ENET_SOCKET_WAIT_RECEIVE; } return 0; } /* enet_socket_wait */ #endif // _WIN32 #ifdef __cplusplus } #endif #endif // ENET_IMPLEMENTATION #endif // ENET_INCLUDE_H #line 0 #define tls_init tls_init2 #line 1 "3rd_bq_websocket.h" /* ------------------------------------------------------------------------------ This software is available under 2 licenses -- choose whichever you prefer. ------------------------------------------------------------------------------ ALTERNATIVE A - MIT License Copyright (c) 2020 Samuli Raivio 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. ------------------------------------------------------------------------------ ALTERNATIVE B - Public Domain (www.unlicense.org) This is free and unencumbered software released into the public domain. Anyone is free to copy, modify, publish, use, compile, sell, or distribute this software, either in source code form or as a compiled binary, for any purpose, commercial or non-commercial, and by any means. In jurisdictions that recognize copyright laws, the author or authors of this software dedicate any and all copyright interest in the software to the public domain. We make this dedication for the benefit of the public at large and to the detriment of our heirs and successors. We intend this dedication to be an overt act of relinquishment in perpetuity of all present and future rights to this software under copyright law. 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 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 BQ_WEBSOCKET_H_INCLUDED #define BQ_WEBSOCKET_H_INCLUDED #include #include #include #ifdef _MSC_VER #pragma warning(push) #pragma warning(disable: 4200) // warning C4200: nonstandard extension used: zero-sized array in struct/union #endif #ifdef __cplusplus extern "C" { #endif typedef struct bqws_socket bqws_socket; typedef enum bqws_error { BQWS_OK = 0, // Unknown error from non-BQWS peer BQWS_ERR_UNKNOWN, // Rejected with `bqws_server_reject()` BQWS_ERR_SERVER_REJECT, // Data over limits of `bqws_limits` BQWS_ERR_LIMIT_MAX_MEMORY_USED, BQWS_ERR_LIMIT_MAX_RECV_MSG_SIZE, BQWS_ERR_LIMIT_MAX_HANDSHAKE_SIZE, BQWS_ERR_LIMIT_MAX_PARTIAL_MESSAGE_PARTS, // Peer didn't respond to handshake, PING, or CLOSE message in time BQWS_ERR_CONNECT_TIMEOUT, BQWS_ERR_PING_TIMEOUT, BQWS_ERR_CLOSE_TIMEOUT, // Allocator returned NULL BQWS_ERR_ALLOCATOR, // Protocol errors BQWS_ERR_BAD_CONTINUATION, BQWS_ERR_UNFINISHED_PARTIAL, BQWS_ERR_PARTIAL_CONTROL, BQWS_ERR_BAD_OPCODE, BQWS_ERR_RESERVED_BIT, BQWS_ERR_IO_WRITE, BQWS_ERR_IO_READ, BQWS_ERR_BAD_HANDSHAKE, BQWS_ERR_UNSUPPORTED_VERSION, BQWS_ERR_TOO_MANY_HEADERS, BQWS_ERR_TOO_MANY_PROTOCOLS, BQWS_ERR_HEADER_KEY_TOO_LONG, BQWS_ERR_HEADER_BAD_ACCEPT, BQWS_ERR_HEADER_PARSE, } bqws_error; typedef enum bqws_state { BQWS_STATE_INVALID, BQWS_STATE_CONNECTING, BQWS_STATE_OPEN, BQWS_STATE_CLOSING, BQWS_STATE_CLOSED, } bqws_state; typedef enum bqws_close_reason { BQWS_CLOSE_INVALID = 0, BQWS_CLOSE_NORMAL = 1000, BQWS_CLOSE_GOING_AWAY = 1001, BQWS_CLOSE_PROTOCOL_ERROR = 1002, BQWS_CLOSE_UNSUPPORTED_TYPE = 1003, BQWS_CLOSE_NO_REASON = 1005, BQWS_CLOSE_ABNORMAL = 1006, BQWS_CLOSE_BAD_DATA = 1007, BQWS_CLOSE_GENERIC_ERROR = 1008, BQWS_CLOSE_MESSAGE_TOO_BIG = 1009, BQWS_CLOSE_EXTENSION_MISSING = 1010, BQWS_CLOSE_SERVER_ERROR = 1011, } bqws_close_reason; typedef enum bqws_msg_type { BQWS_MSG_INVALID = 0, // Basic full text/binary messages BQWS_MSG_TEXT = 0x0001, BQWS_MSG_BINARY = 0x0002, // Reported only if `bqws_opts.recv_partial_messages` is `true` BQWS_MSG_PARTIAL_TEXT = 0x0011, BQWS_MSG_PARTIAL_BINARY = 0x0012, BQWS_MSG_FINAL_TEXT = 0x0111, BQWS_MSG_FINAL_BINARY = 0x0112, // Reported only if `bqws_opts.recv_control_messages` is `true` BQWS_MSG_CONTROL_CLOSE = 0x1000, BQWS_MSG_CONTROL_PING = 0x2000, BQWS_MSG_CONTROL_PONG = 0x3000, // Masks for inspecting groups of types BQWS_MSG_TYPE_MASK = 0x000f, BQWS_MSG_PARTIAL_BIT = 0x0010, BQWS_MSG_FINAL_BIT = 0x0100, BQWS_MSG_CONTROL_MASK = 0xf000, } bqws_msg_type; // Message buffers managed by bq_websocket. typedef struct bqws_msg { // The socket that originally allocated this message bqws_socket *socket; // Type enum/bitmask bqws_msg_type type; // Size of the message in bytes, may be smaller than the // allocated buffer at `data` size_t size; // Size of `data` in bytes size_t capacity; char data[0]; } bqws_msg; // Message header // -- Allocaiton functions typedef void *bqws_alloc_fn(void *user, size_t size); typedef void *bqws_realloc_fn(void *user, void *ptr, size_t old_size, size_t new_size); typedef void bqws_free_fn(void *user, void *ptr, size_t size); // -- IO functions // Called once before anything else from the updating thread. typedef void bqws_io_init_fn(void *user, bqws_socket *ws); // Send `size` bytes of `data` to its peer. // Return the number of bytes actually sent or `SIZE_MAX` if an IO error occurred. typedef size_t bqws_io_send_fn(void *user, bqws_socket *ws, const void *data, size_t size); // Read up to `max_size` bytes to `data`. It's safe to read `min_bytes` with blocking IO. // Return the number of bytes actually read or `SIZE_MAX` if an IO error occurred. typedef size_t bqws_io_recv_fn(void *user, bqws_socket *ws, void *data, size_t max_size, size_t min_size); // Notification that there is more data to send from the socket. typedef void bqws_io_notify_fn(void *user, bqws_socket *ws); // Flush all buffered data to the peer. typedef bool bqws_io_flush_fn(void *user, bqws_socket *ws); // Close and free all the resources used by the IO typedef void bqws_io_close_fn(void *user, bqws_socket *ws); // -- Miscellaneous callback functions // Called when the socket receives a message. Return `true` to consume/filter the message // preventing it from entering the `bqws_recv()` queue. typedef bool bqws_message_fn(void *user, bqws_socket *ws, bqws_msg *msg); // Send a message directly without IO, for example using native WebSockets on web. // Called repeatedly with the same message until you return `true`. typedef bool bqws_send_message_fn(void *user, bqws_socket *ws, bqws_msg *msg); // Peek at all messages (including control messages). typedef void bqws_peek_fn(void *user, bqws_socket *ws, bqws_msg *msg, bool received); // Log state transitions, errors and optionally sent/received messages. typedef void bqws_log_fn(void *user, bqws_socket *ws, const char *line); // Called when the socket encounters an error. typedef void bqws_error_fn(void *user, bqws_socket *ws, bqws_error error); // Allocator callbacks with user context pointer typedef struct bqws_allocator { void *user; bqws_alloc_fn *alloc_fn; bqws_realloc_fn *realloc_fn; bqws_free_fn *free_fn; } bqws_allocator; // IO callbacks with user context pointer, // see prototypes above for description typedef struct bqws_io { void *user; bqws_io_init_fn *init_fn; bqws_io_send_fn *send_fn; bqws_io_recv_fn *recv_fn; bqws_io_notify_fn *notify_fn; bqws_io_flush_fn *flush_fn; bqws_io_close_fn *close_fn; } bqws_io; typedef struct bqws_limits { // Maximum total memory used // default: 262144 size_t max_memory_used; // Maximum received message length // default: 262144 size_t max_recv_msg_size; // Maximum handshake length // default: 262144 size_t max_handshake_size; // Maximum number of queued received messages // default: 1024 size_t max_recv_queue_messages; // Maximum size of queued received messages in bytes // default: 262144 size_t max_recv_queue_size; // Maximum number of parts in a chunked message // default: 16384 size_t max_partial_message_parts; } bqws_limits; typedef struct bqws_opts { // Name for the socket for debugging const char *name; bqws_io io; bqws_allocator allocator; bqws_limits limits; // Message callback bqws_message_fn *message_fn; void *message_user; // Peek at all control/partial incoming messages even if // `recv_partial_messages` and `recv_control_messages are disabled. bqws_peek_fn *peek_fn; void *peek_user; // Verbose log of all events for this socket bqws_log_fn *log_fn; void *log_user; // Log also send/receive events bool log_send; bool log_recv; // Error callback bqws_error_fn *error_fn; void *error_user; // Send messages from this socket manually without IO bqws_send_message_fn *send_message_fn; void *send_message_user; // User data block, if `user_size > 0` but `user_data == NULL` // the data will be zero-initialized void *user_data; size_t user_size; // How long to wait (milliseconds) for the connecting to succeed before giving up. // Use SIZE_MAX to disable the timeout. // default: 10000 size_t connect_timeout; // How often (milliseconds) to send PING messages if there is no traffic, // use SIZE_MAX to disable automatic PING // default: server: 20000, client: 10000 size_t ping_interval; // How long to wait (milliseconds) for the close response before forcing the // state to be BQWS_STATE_CLOSED. Use SIZE_MAX to disable // the close timeout. // default: 5000 size_t close_timeout; // How long to wait (milliseconds) for a ping response before forcing // the state to be BQWS_STATE_CLOSED. Use SIZE_MAX to disable. // the close timeout. // default: 4 * ping_interval size_t ping_response_timeout; // If set returns `BQWS_MSG_PARTIAL_*` messages from `bqws_recv()` bool recv_partial_messages; // If set returns `BQWS_MSG_CONTROL_*` messages from `bqws_recv()` bool recv_control_messages; // Mask messages sent by the server as well bool mask_server; // Don't mask client messages, violates the spec! bool unsafe_dont_mask_client; // Start the connection in BQWS_STATE_OPEN state bool skip_handshake; } bqws_opts; #define BQWS_MAX_HEADERS 64 #define BQWS_MAX_PROTOCOLS 64 // HTTP header key-value pair. typedef struct bqws_header { const char *name; const char *value; } bqws_header; typedef struct bqws_client_opts { // Standard HTTP headers used by the handshake const char *path; const char *host; const char *origin; // WebSocket protocols to request const char *protocols[BQWS_MAX_PROTOCOLS]; size_t num_protocols; // Extra HTTP headers bqws_header headers[BQWS_MAX_HEADERS]; size_t num_headers; // Random key (optional) bool use_random_key; uint8_t random_key[16]; } bqws_client_opts; // Call `bqws_server_accept()` or `bqws_server_reject()` here to handle the socket typedef void bqws_verify_fn(void *user, bqws_socket *ws, const bqws_client_opts *opts); typedef struct bqws_server_opts { // Automatically verify connections matching these client options. bqws_client_opts *verify_filter; // Verify callback, same as polling `bqws_server_get_client_options()` // and calling `bqws_server_accept()` bqws_verify_fn *verify_fn; void *verify_user; } bqws_server_opts; // [wss://][host.example.com][:12345][/directory] // scehme host port path typedef struct bqws_url { bool secure; uint16_t port; char scheme[16]; char host[256]; const char *path; } bqws_url; typedef struct bqws_io_stats { uint64_t total_messages; uint64_t total_bytes; size_t queued_messages; size_t queued_bytes; } bqws_io_stats; typedef struct bqws_stats { bqws_io_stats recv; bqws_io_stats send; } bqws_stats; // -- WebSocket management // Create a new client/server socket. `opts`, `client_opts`, `server_opts` are all optional. bqws_socket *bqws_new_client(const bqws_opts *opts, const bqws_client_opts *client_opts); bqws_socket *bqws_new_server(const bqws_opts *opts, const bqws_server_opts *server_opts); // Call at any point to destroy the socket and free all used resources. void bqws_free_socket(bqws_socket *ws); // Graceful shutdown: Prepare to close the socket by sending a close message. // `bqws_close()` sends the close message as soon as possible while `bqws_queue_close()` // sends all other queued messages first. void bqws_close(bqws_socket *ws, bqws_close_reason reason, const void *data, size_t size); void bqws_queue_close(bqws_socket *ws, bqws_close_reason reason, const void *data, size_t size); // -- Server connect // Accept or reject connections based on headers. // Valid only until you call `bqws_server_connect()` or `bqws_free_socket()`! bqws_client_opts *bqws_server_get_client_opts(bqws_socket *ws); void bqws_server_accept(bqws_socket *ws, const char *protocol); void bqws_server_reject(bqws_socket *ws); // -- Query state bqws_state bqws_get_state(const bqws_socket *ws); bqws_error bqws_get_error(const bqws_socket *ws); bool bqws_is_connecting(const bqws_socket *ws); bool bqws_is_closed(const bqws_socket *ws); size_t bqws_get_memory_used(const bqws_socket *ws); bool bqws_is_server(const bqws_socket *ws); void *bqws_user_data(const bqws_socket *ws); size_t bqws_user_data_size(const bqws_socket *ws); const char *bqws_get_name(const bqws_socket *ws); bqws_stats bqws_get_stats(const bqws_socket *ws); void *bqws_get_io_user(const bqws_socket *ws); bool bqws_get_io_closed(const bqws_socket *ws); // Get/update limits bqws_limits bqws_get_limits(const bqws_socket *ws); void bqws_set_limits(bqws_socket *ws, const bqws_limits *limits); // Peer closing bqws_close_reason bqws_get_peer_close_reason(const bqws_socket *ws); bqws_error bqws_get_peer_error(const bqws_socket *ws); // Get the chosen protocol, returns "" if none chosen but the connection is open // Returns NULL if the connection is not established const char *bqws_get_protocol(const bqws_socket *ws); // -- Communication // Receive a message, use `bqws_free_msg()` to free the returned pointer bqws_msg *bqws_recv(bqws_socket *ws); void bqws_free_msg(bqws_msg *msg); // Single message void bqws_send(bqws_socket *ws, bqws_msg_type type, const void *data, size_t size); void bqws_send_binary(bqws_socket *ws, const void *data, size_t size); void bqws_send_text(bqws_socket *ws, const char *str); void bqws_send_text_len(bqws_socket *ws, const void *str, size_t len); // Write to socket-provided memory bqws_msg *bqws_allocate_msg(bqws_socket *ws, bqws_msg_type type, size_t size); void bqws_send_msg(bqws_socket *ws, bqws_msg *msg); // Streaming messages void bqws_send_begin(bqws_socket *ws, bqws_msg_type type); void bqws_send_append(bqws_socket *ws, const void *data, size_t size); void bqws_send_append_str(bqws_socket *ws, const char *str); void bqws_send_append_msg(bqws_socket *ws, bqws_msg *msg); void bqws_send_finish(bqws_socket *ws); // Send manual control messages void bqws_send_ping(bqws_socket *ws, const void *data, size_t size); void bqws_send_pong(bqws_socket *ws, const void *data, size_t size); // -- Updating and IO // Keep the socket alive, reads/writes buffered data and responds to pings/pongs // Semantically equivalent to bqws_update_state() and bqws_update_io() void bqws_update(bqws_socket *ws); // Send/respond to PING/PONG, update close timeouts, etc... void bqws_update_state(bqws_socket *ws); // Call user-provided IO callbacks for reading/writing or both. void bqws_update_io(bqws_socket *ws); void bqws_update_io_read(bqws_socket *ws); void bqws_update_io_write(bqws_socket *ws); // Non-callback IO: Read data to send to the peer or write data received from the peer. size_t bqws_read_from(bqws_socket *ws, const void *data, size_t size); size_t bqws_write_to(bqws_socket *ws, void *data, size_t size); // Direct control void bqws_direct_push_msg(bqws_socket *ws, bqws_msg *msg); void bqws_direct_set_override_state(bqws_socket *ws, bqws_state state); void bqws_direct_fail(bqws_socket *ws, bqws_error err); // -- Utility // Parse `str` to `url` returning `true` on success. // `url->path` still refers to `str` and is not a copy! bool bqws_parse_url(bqws_url *url, const char *str); // Enum -> string conversion const char *bqws_error_str(bqws_error error); const char *bqws_msg_type_str(bqws_msg_type type); const char *bqws_state_str(bqws_state state); void *bqws_allocator_alloc(const bqws_allocator *at, size_t size); void *bqws_allocator_realloc(const bqws_allocator *at, void *ptr, size_t old_size, size_t new_size); void bqws_allocator_free(const bqws_allocator *at, void *ptr, size_t size); #ifdef __cplusplus } #endif #ifdef _MSC_VER #pragma warning(push) #endif #endif // BQ_WEBSOCKET_H_INCLUDED // ----------------------------------------------------------------------------- #ifndef BQ_WEBSOCKET_PLATFORM_H_INCLUDED #define BQ_WEBSOCKET_PLATFORM_H_INCLUDED #include #include #ifdef __cplusplus extern "C" { #endif #define BQWS_PT_MAX_ADDRESS_SIZE 16 #define BQWS_PT_MAX_ADDRESS_FORMAT_LENGTH 64 typedef struct bqws_pt_server bqws_pt_server; typedef enum bqws_pt_error_type { BQWS_PT_ERRTYPE_NONE, // bqws_pt_error_code BQWS_PT_ERRTYPE_PT, // Windows Sockets error codes // https://docs.microsoft.com/en-us/windows/win32/winsock/windows-sockets-error-codes-2 BQWS_PT_ERRTYPE_WSA, // POSIX errno codes // https://www-numi.fnal.gov/offline_software/srt_public_context/WebDocs/Errors/unix_system_errors.html BQWS_PT_ERRTYPE_POSIX, // getaddrinfo() error codes // http://man7.org/linux/man-pages/man3/getaddrinfo.3.html BQWS_PT_ERRTYPE_GETADDRINFO, // OpenSSL error codes BQWS_PT_ERRTYPE_OPENSSL, } bqws_pt_error_type; typedef enum bqws_pt_error_code { BQWS_PT_OK, BQWS_PT_ERR_NO_TLS, BQWS_PT_ERR_NO_SERVER_SUPPORT, BQWS_PT_ERR_OUT_OF_MEMORY, BQWS_PT_ERR_BAD_URL, } bqws_pt_error_code; typedef struct bqws_pt_error { const char *function; bqws_pt_error_type type; int64_t data; } bqws_pt_error; typedef enum bqws_pt_address_type { BQWS_PT_ADDRESS_UNKNOWN, BQWS_PT_ADDRESS_WEBSOCKET, BQWS_PT_ADDRESS_IPV4, BQWS_PT_ADDRESS_IPV6, } bqws_pt_address_type; typedef struct bqws_pt_address { bqws_pt_address_type type; uint16_t port; uint8_t address[BQWS_PT_MAX_ADDRESS_SIZE]; } bqws_pt_address; typedef struct bqws_pt_init_opts { // CA certificate file location // For example: https://curl.haxx.se/docs/caextract.html const char *ca_filename; } bqws_pt_init_opts; typedef struct bqws_pt_connect_opts { // Disable host verification for TLS (secure) connections bool insecure_no_verify_host; } bqws_pt_connect_opts; typedef struct bqws_pt_listen_opts { // Use TLS for incoming connections bool secure; // TLS certificate, used only if `secure` const char *certificate_file; // Passed to `SSL_CTX_use_certificate_file()` const char *private_key_file; // Passed to `SSL_CTX_use_PrivateKey_file()` // Port to bind to // default: 80 if `!secure`, 443 if `secure` uint16_t port; // Number of connections to queue for `bqws_pt_accept()` // default: 128 size_t backlog; // Attempt to share a port with other processes ie. `SO_REUSEPORT` bool reuse_port; // Allocator callbacks bqws_allocator allocator; } bqws_pt_listen_opts; // -- Global initialization // Call these before/after any other functions bool bqws_pt_init(const bqws_pt_init_opts *opts); void bqws_pt_shutdown(); // Thread local error void bqws_pt_clear_error(); bool bqws_pt_get_error(bqws_pt_error *err); // -- Platform socket creation // Client bqws_socket *bqws_pt_connect(const char *url, const bqws_pt_connect_opts *pt_opts, const bqws_opts *opts, const bqws_client_opts *client_opts); bqws_socket *bqws_pt_connect_url(const bqws_url *url, const bqws_pt_connect_opts *pt_opts, const bqws_opts *opts, const bqws_client_opts *client_opts); // Server bqws_pt_server *bqws_pt_listen(const bqws_pt_listen_opts *pt_opts); void bqws_pt_free_server(bqws_pt_server *sv); bqws_socket *bqws_pt_accept(bqws_pt_server *sv, const bqws_opts *opts, const bqws_server_opts *server_opts); // Query bqws_pt_address bqws_pt_get_address(const bqws_socket *ws); // -- Utility void bqws_pt_format_address(char *dst, size_t size, const bqws_pt_address *addr); void bqws_pt_get_error_desc(char *dst, size_t size, const bqws_pt_error *err); void bqws_pt_sleep_ms(uint32_t ms); const char *bqws_pt_error_type_str(bqws_pt_error_type type); const char *bqws_pt_error_code_str(bqws_pt_error_code err); #ifdef __cplusplus } #endif #endif // BQ_WEBSOCKET_PLATFORM_H_INCLUDED // ----------------------------------------------------------------------------- #ifdef BQ_WEBSOCKET_IMPLEMENTATION #include #include #include #include #include // -- Config #if defined(_MSC_VER) #define bqws_forceinline __forceinline #if defined(_M_IX86) || defined(_M_X64) #include #include #define BQWS_USE_SSE 1 #endif #elif defined(__GNUC__) || defined(__clang__) #define bqws_forceinline __attribute__((always_inline)) inline #if defined(__i386__) || defined(__x86_64__) #include #include #define BQWS_USE_SSE 1 #endif #else #define bqws_forceinline #endif #ifndef bqws_assert #include #define bqws_assert(x) assert(x) #endif #ifndef bqws_malloc #define bqws_malloc(size) malloc((size)) #endif #ifndef bqws_realloc #define bqws_realloc(ptr, old_size, new_size) realloc((ptr), (new_size)) #endif #ifndef bqws_free #define bqws_free(ptr, size) free((ptr)) #endif // TODO: QueryPerformanceCounter() or clock_gettime() might be faster typedef clock_t bqws_timestamp; static bqws_timestamp bqws_get_timestamp() { return clock(); } static size_t bqws_timestamp_delta_to_ms(bqws_timestamp begin, bqws_timestamp end) { return (size_t)((double)(end - begin) * 1000.0 / (double)CLOCKS_PER_SEC); } #ifndef BQWS_DEBUG #if defined(NDEBUG) #define BQWS_DEBUG 0 #else #define BQWS_DEBUG 1 #endif #endif #ifndef BQWS_SINGLE_THREAD #define BQWS_SINGLE_THREAD 0 #endif #ifndef bqws_mutex #if defined(_WIN32) && !BQWS_SINGLE_THREAD #define WIN32_LEAN_AND_MEAN #define NOMINMAX #include typedef struct { CRITICAL_SECTION cs; #if BQWS_DEBUG DWORD thread; #endif } bqws_mutex; static void bqws_mutex_init(bqws_mutex *m) { InitializeCriticalSection(&m->cs); #if BQWS_DEBUG m->thread = 0; #endif } static void bqws_mutex_free(bqws_mutex *m) { #if BQWS_DEBUG m->thread = 0; #endif DeleteCriticalSection(&m->cs); } static void bqws_mutex_lock(bqws_mutex *m) { EnterCriticalSection(&m->cs); #if BQWS_DEBUG m->thread = GetCurrentThreadId(); #endif } static void bqws_mutex_unlock(bqws_mutex *m) { #if BQWS_DEBUG m->thread = 0; #endif LeaveCriticalSection(&m->cs); } #if BQWS_DEBUG #define bqws_assert_locked(m) bqws_assert((m)->thread == GetCurrentThreadId()); #else #define bqws_assert_locked(m) (void)0 #endif #elif (defined(__APPLE__) || defined(__linux__) || defined(__unix__)) && (!defined(__EMSCRIPTEN__) || defined(__EMSCRIPTEN_PTHREADS__)) && !BQWS_SINGLE_THREAD #include typedef struct { pthread_mutex_t mutex; #if BQWS_DEBUG bool locked; pthread_t thread; #endif } bqws_mutex; static void bqws_mutex_init(bqws_mutex *m) { pthread_mutex_init(&m->mutex, NULL); #if BQWS_DEBUG m->locked = false; #endif } static void bqws_mutex_free(bqws_mutex *m) { #if BQWS_DEBUG m->locked = false; #endif pthread_mutex_destroy(&m->mutex); } static void bqws_mutex_lock(bqws_mutex *m) { pthread_mutex_lock(&m->mutex); #if BQWS_DEBUG m->locked = true; m->thread = pthread_self(); #endif } static void bqws_mutex_unlock(bqws_mutex *m) { #if BQWS_DEBUG m->locked = false; #endif pthread_mutex_unlock(&m->mutex); } #if BQWS_DEBUG #define bqws_assert_locked(m) ((m)->locked && (m)->thread == pthread_self()) #else #define bqws_assert_locked(m) (void)0 #endif #else typedef struct { bool is_locked; } bqws_mutex; static void bqws_mutex_init(bqws_mutex *m) { m->is_locked = false; } static void bqws_mutex_free(bqws_mutex *m) { bqws_assert(!m->is_locked); } static void bqws_mutex_lock(bqws_mutex *m) { bqws_assert(!m->is_locked); m->is_locked = true; } static void bqws_mutex_unlock(bqws_mutex *m) { bqws_assert(m->is_locked); m->is_locked = false; } #define bqws_assert_locked(m) bqws_assert((m)->is_locked) #endif #else // not defined bqws_mutex #ifndef bqws_assert_locked #define bqws_assert_locked(m) (void)0 #endif #endif // not defined bqws_mutex // -- Magic constants #define BQWS_DELETED_MAGIC 0xbdbdbdbd #define BQWS_SOCKET_MAGIC 0x7773636b #define BQWS_MSG_MAGIC 0x776d7367 #define BQWS_FILTER_MAGIC 0x77666c74 // -- Types // Message implementation struct, message data is always allocated // to follow the struct in memory. typedef struct bqws_msg_imp bqws_msg_imp; struct bqws_msg_imp { uint32_t magic; // = BQWS_MSG_MAGIC // Socket that is responsible of freeing this message // or NULL if it's owned by the user. bqws_socket *owner; // Allocator used to allocate this message bqws_allocator allocator; // Linked list in `bqws_msg_queue` bqws_msg_imp *prev; bqws_msg msg; }; #define msg_imp(msg) (bqws_msg_imp*)((char*)msg - offsetof(bqws_msg_imp, msg)) #define msg_alloc_size(msg) (sizeof(bqws_msg_imp) + (msg)->capacity) typedef struct { bqws_mutex mutex; bqws_msg_imp *first, *last; size_t num_messages; size_t byte_size; uint64_t total_messages; uint64_t total_size; } bqws_msg_queue; typedef struct { bqws_msg_imp *msg; size_t offset; size_t header_offset; size_t header_size; bool finished; bool masked; uint32_t mask_key; bqws_msg_type partial_type; } bqws_msg_buffer; typedef struct { char *data; size_t size; size_t capacity; size_t write_offset; size_t read_offset; } bqws_handshake_buffer; typedef struct { uint32_t magic; const char *path; const char *host; const char *origin; const char *protocols[BQWS_MAX_PROTOCOLS]; size_t num_protocols; bqws_verify_fn *verify_fn; void *verify_user; size_t text_size; char text_data[]; } bqws_verify_filter; // Random entropy source typedef struct { void (*function_pointer)(bqws_socket *ws, const bqws_client_opts *opts); void *stack_pointer; void *heap_pointer; clock_t clock; time_t time; uint32_t mask_key; } bqws_random_entropy; typedef struct { uint8_t code_be[2]; uint8_t magic[4]; uint8_t error_be[4]; } bqws_err_close_data; // Main socket/context type, passed everywhere as the first argument. struct bqws_socket { // -- Constant data uint32_t magic; // = BQWS_SOCKET_MAGIC char *name; // Name high up for debugging bool is_server; // Copied from `opts` bqws_allocator allocator; bqws_io user_io; bqws_limits limits; bool recv_partial_messages; bool recv_control_messages; bool mask_server; bool unsafe_dont_mask_client; bqws_verify_fn *verify_fn; void *verify_user; bqws_message_fn *message_fn; void *message_user; bqws_peek_fn *peek_fn; void *peek_user; bqws_log_fn *log_fn; void *log_user; bool log_send; bool log_recv; bqws_error_fn *error_fn; void *error_user; bqws_send_message_fn *send_message_fn; void *send_message_user; size_t user_size; size_t ping_interval; size_t connect_timeout; size_t close_timeout; size_t ping_response_timeout; // -- Internally synchronized // Current error state, set to the first error that occurs // Error writes are protected by `err_mutex` checking `err` can // be done without a mutex to check for errors from the same thread. bqws_mutex err_mutex; bqws_error err; // Message queues bqws_msg_queue recv_partial_queue; bqws_msg_queue recv_queue; bqws_msg_queue send_queue; // -- State of the socket, errors struct { bqws_mutex mutex; // Connection state bqws_state state; bqws_state override_state; // Pre-allocated error close message storage void *pointer_align; char error_msg_data[sizeof(bqws_msg_imp) + sizeof(bqws_err_close_data)]; bqws_close_reason peer_reason; bqws_error peer_err; bool stop_write; bool stop_read; bool close_sent; bool close_received; bool io_started; bool io_closed; char *chosen_protocol; bqws_timestamp start_closing_ts; // Priority messages bqws_msg_imp *close_to_send; bqws_msg_imp *pong_to_send; } state; // -- Allocation struct { bqws_mutex mutex; // TODO: Make this atomic? // Total memory allocated through `allocator` at the moment size_t memory_used; } alloc; // -- IO struct { bqws_mutex mutex; bqws_timestamp start_connect_ts; bqws_timestamp last_write_ts; bqws_timestamp last_read_ts; bqws_timestamp last_ping_ts; size_t recv_partial_size; // Handshake bqws_handshake_buffer handshake; bqws_handshake_buffer handshake_overflow; bqws_client_opts *opts_from_client; char client_key_base64[32]; bool client_handshake_done; bool client_has_protocol; // Masking random state uint64_t mask_random_state; uint64_t mask_random_stream; // Write/read buffers `recv_header` is also used to buffer // multiple small messages char recv_header[512]; bqws_msg_buffer recv_buf; char send_header[16]; bqws_msg_buffer send_buf; } io; // -- API struct { bqws_mutex mutex; bqws_msg_imp *next_partial_to_send; bqws_msg_type send_partial_type; } partial; // User data follows in memory char user_data[]; }; // -- Utility // Mark the socket as failed with an error. Only updates the // error flag if it's not set. static void null_free(void *user, void *ptr, size_t size) { } static void ws_log(bqws_socket *ws, const char *str) { if (ws->log_fn) ws->log_fn(ws->log_user, ws, str); } static void ws_log2(bqws_socket *ws, const char *a, const char *b) { if (!ws->log_fn) return; char line[256]; size_t len_a = strlen(a); size_t len_b = strlen(b); bqws_assert(len_a + len_b < sizeof(line)); char *ptr = line; memcpy(ptr, a, len_a); ptr += len_a; memcpy(ptr, b, len_b); ptr += len_b; *ptr = '\0'; ws->log_fn(ws->log_user, ws, line); } static void ws_close(bqws_socket *ws) { bqws_assert_locked(&ws->state.mutex); if (ws->state.state != BQWS_STATE_CLOSED) { ws_log(ws, "State: CLOSED"); if (ws->user_io.close_fn && !ws->state.io_closed) { ws->user_io.close_fn(ws->user_io.user, ws); } ws->state.io_closed = true; ws->state.state = BQWS_STATE_CLOSED; ws->state.stop_read = true; ws->state.stop_write = true; } } static void ws_fail(bqws_socket *ws, bqws_error err) { bool should_report = false; bqws_mutex_lock(&ws->state.mutex); bqws_assert(err != BQWS_OK); bqws_mutex_lock(&ws->err_mutex); if (!ws->err) { should_report = true; // vvv Breakpoint here to stop on first error ws->err = err; bqws_mutex_unlock(&ws->err_mutex); ws_log2(ws, "Fail: ", bqws_error_str(err)); // Try to send an error close message if (ws->state.state == BQWS_STATE_OPEN && !ws->state.close_to_send) { bqws_msg_imp *close_msg = (bqws_msg_imp*)ws->state.error_msg_data; close_msg->magic = BQWS_MSG_MAGIC; close_msg->allocator.free_fn = &null_free; close_msg->owner = ws; close_msg->prev = NULL; close_msg->msg.socket = ws; close_msg->msg.capacity = sizeof(bqws_err_close_data); close_msg->msg.size = sizeof(bqws_err_close_data); close_msg->msg.type = BQWS_MSG_CONTROL_CLOSE; bqws_close_reason reason; switch (err) { case BQWS_ERR_LIMIT_MAX_RECV_MSG_SIZE: reason = BQWS_CLOSE_MESSAGE_TOO_BIG; break; case BQWS_ERR_BAD_CONTINUATION: case BQWS_ERR_UNFINISHED_PARTIAL: case BQWS_ERR_PARTIAL_CONTROL: case BQWS_ERR_BAD_OPCODE: case BQWS_ERR_RESERVED_BIT: reason = BQWS_CLOSE_PROTOCOL_ERROR; break; default: reason = BQWS_CLOSE_SERVER_ERROR; break; } bqws_err_close_data *data = (bqws_err_close_data*)close_msg->msg.data; data->code_be[0] = (uint8_t)(reason >> 8); data->code_be[1] = (uint8_t)(reason >> 0); memcpy(data->magic, "BQWS", 4); data->error_be[0] = (uint8_t)(err >> 24); data->error_be[1] = (uint8_t)(err >> 16); data->error_be[2] = (uint8_t)(err >> 8); data->error_be[3] = (uint8_t)(err >> 0); ws->state.close_to_send = close_msg; ws->state.state = BQWS_STATE_CLOSING; ws->state.start_closing_ts = bqws_get_timestamp(); } else if (ws->state.state == BQWS_STATE_CONNECTING) { // If there's an error during connection close // the connection immediately ws_close(ws); } } else { bqws_mutex_unlock(&ws->err_mutex); } // IO errors should close their respective channels if (err == BQWS_ERR_IO_READ) ws->state.stop_read = true; if (err == BQWS_ERR_IO_WRITE) ws->state.stop_write = true; bqws_mutex_unlock(&ws->state.mutex); if (ws->error_fn && should_report) { ws->error_fn(ws->error_user, ws, err); } } static void bqws_sha1(uint8_t digest[20], const void *data, size_t size); // Callback writer typedef struct { char *ptr, *end; } bqws_mem_stream; static size_t mem_stream_send(void *user, bqws_socket *ws, const void *data, size_t size) { // Copy as many bytes as fit in the stream bqws_mem_stream *s = (bqws_mem_stream*)user; size_t left = s->end - s->ptr; size_t to_copy = size; if (to_copy > left) to_copy = left; memcpy(s->ptr, data, to_copy); s->ptr += to_copy; return to_copy; } static size_t mem_stream_recv(void *user, bqws_socket *ws, void *data, size_t max_size, size_t min_size) { // Copy as many bytes as fit in the stream bqws_mem_stream *s = (bqws_mem_stream*)user; size_t left = s->end - s->ptr; size_t to_copy = max_size; if (to_copy > left) to_copy = left; memcpy(data, s->ptr, to_copy); s->ptr += to_copy; return to_copy; } // -- Allocation // Direct allocator functions. Prefer using `ws_alloc()` if there is an `bqws_socket` // avaialable (which there should almost always be). These functions just call the // user callbacks or defaults passing in the user pointer. void *bqws_allocator_alloc(const bqws_allocator *at, size_t size) { if (at->alloc_fn) { // User defined alloc directly return at->alloc_fn(at->user, size); } else if (at->realloc_fn) { // Realloc with zero initial size return at->realloc_fn(at->user, NULL, 0, size); } else { // Default: malloc() return bqws_malloc(size); } } void *bqws_allocator_realloc(const bqws_allocator *at, void *ptr, size_t old_size, size_t new_size) { if (old_size == 0) { // Realloc with `old_size==0` is equivalent to malloc return bqws_allocator_alloc(at, new_size); } else if (new_size == 0) { // Realloc with `new_size==0` is equivalent to free bqws_allocator_free(at, ptr, old_size); return NULL; } if (at->realloc_fn) { // User defined realloc directly return at->realloc_fn(at->user, ptr, old_size, new_size); } else if (at->alloc_fn) { // No realloc, but alloc is defined. Allocate and copy the data // if it succeeded and free the old pointer (if free is defined) void *new_ptr = at->alloc_fn(at->user, new_size); if (!new_ptr) return NULL; memcpy(new_ptr, ptr, old_size); if (at->free_fn) { at->free_fn(at->user, ptr, old_size); } return new_ptr; } else { // Default: realloc() return bqws_realloc(ptr, old_size, new_size); } } void bqws_allocator_free(const bqws_allocator *at, void *ptr, size_t size) { if (size == 0) return; bqws_assert(ptr != NULL); if (at->free_fn) { // Use defined free directly at->free_fn(at->user, ptr, size); } else if (at->realloc_fn) { // Use realloc with zero new size at->realloc_fn(at->user, ptr, size, 0); } else { bqws_assert(at->alloc_fn == NULL); // Default: free(), only if there is no user defined allocator bqws_free(ptr, size); } } // WebSocket allocation functions. These keep track of total used memory and // update the error flag. static bool ws_add_memory_used(bqws_socket *ws, size_t size) { // TODO: Atomics bqws_mutex_lock(&ws->alloc.mutex); bool ok = (size <= ws->limits.max_memory_used - ws->alloc.memory_used); if (ok) { ws->alloc.memory_used += size; } else { ws_fail(ws, BQWS_ERR_LIMIT_MAX_MEMORY_USED); } bqws_mutex_unlock(&ws->alloc.mutex); return ok; } static void ws_remove_memory_used(bqws_socket *ws, size_t size) { if (size == 0) return; // TODO: Atomics bqws_mutex_lock(&ws->alloc.mutex); bqws_assert(ws->alloc.memory_used >= size); ws->alloc.memory_used -= size; bqws_mutex_unlock(&ws->alloc.mutex); } static void *ws_alloc(bqws_socket *ws, size_t size) { if (!ws_add_memory_used(ws, size)) return NULL; void *ptr = bqws_allocator_alloc(&ws->allocator, size); if (!ptr) ws_fail(ws, BQWS_ERR_ALLOCATOR); return ptr; } static void *ws_realloc(bqws_socket *ws, void *ptr, size_t old_size, size_t new_size) { if (!ws_add_memory_used(ws, new_size)) return NULL; ws_remove_memory_used(ws, old_size); void *new_ptr = bqws_allocator_realloc(&ws->allocator, ptr, old_size, new_size); if (!new_ptr) ws_fail(ws, BQWS_ERR_ALLOCATOR); return new_ptr; } static void ws_free(bqws_socket *ws, void *ptr, size_t size) { ws_remove_memory_used(ws, size); bqws_allocator_free(&ws->allocator, ptr, size); } static char *ws_copy_str(bqws_socket *ws, const char *str) { size_t len = strlen(str) + 1; char *dst = (char*)ws_alloc(ws, len); if (!dst) return NULL; memcpy(dst, str, len); return dst; } static void ws_free_str(bqws_socket *ws, char *ptr) { if (!ptr) return; ws_free(ws, ptr, strlen(ptr) + 1); } // Message allocation static bqws_msg_imp *msg_alloc(bqws_socket *ws, bqws_msg_type type, size_t size) { size_t capacity = size; // Space for NULL-terminator if (type & BQWS_MSG_TEXT) capacity += 1; size_t alloc_size = sizeof(bqws_msg_imp) + capacity; bqws_msg_imp *msg = (bqws_msg_imp*)ws_alloc(ws, alloc_size); if (!msg) return NULL; msg->magic = BQWS_MSG_MAGIC; msg->owner = ws; msg->allocator = ws->allocator; msg->prev = NULL; msg->msg.socket = ws; msg->msg.type = type; msg->msg.size = size; msg->msg.capacity = capacity; if (type & BQWS_MSG_TEXT) { msg->msg.data[size] = '\0'; } return msg; } static void msg_release_ownership(bqws_socket *ws, bqws_msg_imp *msg) { bqws_assert(ws && ws->magic == BQWS_SOCKET_MAGIC); bqws_assert(msg && msg->magic == BQWS_MSG_MAGIC); bqws_assert(msg->owner == ws); ws_remove_memory_used(ws, msg_alloc_size(&msg->msg)); msg->owner = NULL; } static bool msg_acquire_ownership(bqws_socket *ws, bqws_msg_imp *msg) { bqws_assert(ws && ws->magic == BQWS_SOCKET_MAGIC); bqws_assert(msg && msg->magic == BQWS_MSG_MAGIC); bqws_assert(msg->owner == NULL); if (!ws_add_memory_used(ws, msg_alloc_size(&msg->msg))) { // We still own the message so need to delete it bqws_free_msg(&msg->msg); return false; } msg->owner = ws; return true; } static void msg_free_owned(bqws_socket *ws, bqws_msg_imp *msg) { if (!msg) return; bqws_assert(ws && ws->magic == BQWS_SOCKET_MAGIC); bqws_assert(msg->magic == BQWS_MSG_MAGIC); bqws_assert(msg->owner == ws); msg->magic = BQWS_DELETED_MAGIC; msg->owner = NULL; size_t size = msg_alloc_size(&msg->msg); // no-mutex(state): We are only referring to the address of `error_msg_data` if ((char*)msg != ws->state.error_msg_data) { ws_remove_memory_used(ws, size); bqws_allocator at = msg->allocator; bqws_allocator_free(&at, msg, size); } } static void msg_enqueue(bqws_msg_queue *mq, bqws_msg_imp *msg) { bqws_mutex_lock(&mq->mutex); // Adjust the last message to point to `msg` and replace // it as the last in the queue bqws_assert(msg && msg->magic == BQWS_MSG_MAGIC && msg->prev == NULL); if (mq->last) { bqws_assert(mq->first); bqws_assert(mq->last->magic == BQWS_MSG_MAGIC && mq->last->prev == NULL); mq->last->prev = msg; } else { bqws_assert(!mq->first); mq->first = msg; } mq->last = msg; mq->byte_size += msg->msg.size; mq->num_messages++; mq->total_size += msg->msg.size; mq->total_messages++; bqws_mutex_unlock(&mq->mutex); } static bqws_msg_imp *msg_dequeue(bqws_msg_queue *mq) { bqws_mutex_lock(&mq->mutex); bqws_msg_imp *msg = mq->first; if (msg) { bqws_assert(mq->last); bqws_assert(msg->magic == BQWS_MSG_MAGIC); bqws_msg_imp *prev = msg->prev; msg->prev = NULL; mq->first = prev; if (prev) { bqws_assert(prev->magic == BQWS_MSG_MAGIC); } else { bqws_assert(mq->last == msg); mq->last = NULL; } bqws_assert(mq->byte_size >= msg->msg.size); bqws_assert(mq->num_messages > 0); mq->byte_size -= msg->msg.size; mq->num_messages--; } else { bqws_assert(!mq->last); } bqws_mutex_unlock(&mq->mutex); return msg; } static void msg_init_queue(bqws_socket *ws, bqws_msg_queue *mq) { bqws_mutex_init(&mq->mutex); } static void msg_free_queue(bqws_socket *ws, bqws_msg_queue *mq) { bqws_msg_imp *imp; while ((imp = msg_dequeue(mq)) != 0) { msg_free_owned(ws, imp); } bqws_mutex_free(&mq->mutex); } static void msg_queue_add_to_total(bqws_msg_queue *mq, size_t size) { bqws_mutex_lock(&mq->mutex); mq->total_messages++; mq->total_size += size; bqws_mutex_unlock(&mq->mutex); } static void msg_queue_get_stats(bqws_msg_queue *mq, bqws_io_stats *stats) { bqws_mutex_lock(&mq->mutex); stats->total_bytes = mq->total_size; stats->total_messages = mq->total_messages; stats->queued_bytes = mq->byte_size; stats->queued_messages = mq->num_messages; bqws_mutex_unlock(&mq->mutex); } // Masking static uint32_t mask_make_key(bqws_socket *ws) { bqws_assert_locked(&ws->io.mutex); // PCG Random step const uint64_t c = UINT64_C(6364136223846793005); uint64_t s = ws->io.mask_random_state * c + ws->io.mask_random_stream; uint32_t xs = (uint32_t)(((s >> 18u) ^ s) >> 27u), r = s >> 59u; ws->io.mask_random_state = s; uint32_t rng = (xs >> r) | (xs << (((uint32_t)-(int32_t)r) & 31)); return rng ^ (uint32_t)bqws_get_timestamp(); } static void mask_apply(void *data, size_t size, uint32_t mask) { size_t left = size; // Process SIMD width at a time char *data_simd = (char*)data; #if defined(BQWS_USE_SSE) { __m128i sse_mask = _mm_set1_epi32(mask); while (left >= 16) { __m128i w = _mm_loadu_si128((__m128i*)data_simd); w = _mm_xor_si128(w, sse_mask); _mm_storeu_si128((__m128i*)data_simd, w); data_simd += 16; left -= 16; } } #endif // Process word at a time uint32_t *dst32 = (uint32_t*)data_simd; while (left >= 4) { *dst32++ ^= mask; left -= 4; } // Mask rest if (left > 0) { bqws_assert(left < 4); uint8_t mask_bytes[4]; memcpy(mask_bytes, &mask, 4); uint8_t *dst8 = (uint8_t*)dst32; uint8_t *src = mask_bytes; while (left > 0) { *dst8++ ^= *src++; left--; } } } // -- Handshake static bqws_forceinline bool str_nonempty(const char *s) { return s && *s; } static void hs_push_size(bqws_socket *ws, const char *data, size_t size) { if (ws->err) return; bqws_assert_locked(&ws->io.mutex); if (size > ws->io.handshake.capacity - ws->io.handshake.size) { // Grow the buffer geometrically up to `max_handshake_size` size_t new_cap = ws->io.handshake.capacity * 2; if (new_cap == 0) new_cap = 512; if (new_cap > ws->limits.max_handshake_size) new_cap = ws->limits.max_handshake_size; if (new_cap == ws->io.handshake.capacity) { ws_fail(ws, BQWS_ERR_LIMIT_MAX_HANDSHAKE_SIZE); return; } char *new_data = (char*)ws_realloc(ws, ws->io.handshake.data, ws->io.handshake.capacity, new_cap); if (!new_data) return; ws->io.handshake.data = new_data; ws->io.handshake.capacity = new_cap; } memcpy(ws->io.handshake.data + ws->io.handshake.size, data, size); ws->io.handshake.size += size; } static void hs_push(bqws_socket *ws, const char *a) { hs_push_size(ws, a, strlen(a)); } static void hs_push2(bqws_socket *ws, const char *a, const char *b) { hs_push(ws, a); hs_push(ws, b); } static void hs_push3(bqws_socket *ws, const char *a, const char *b, const char *c) { hs_push(ws, a); hs_push(ws, b); hs_push(ws, c); } static const char *base64_tab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; static bool hs_to_base64(void *buf, size_t buf_size, const void *data, size_t size) { bqws_assert(size == 0 || data); const uint8_t *b = (const uint8_t*)data; char *dst = (char*)buf, *end = dst + buf_size; ptrdiff_t left = (ptrdiff_t)size; while (left > 0) { if (end - dst < 5) return false; uint32_t a = (uint32_t)b[0] << 16u | (left >= 2 ? (uint32_t)b[1] : 0u) << 8u | (left >= 3 ? (uint32_t)b[2] : 0u); dst[0] = base64_tab[a >> 18]; dst[1] = base64_tab[(a >> 12) & 0x3f]; dst[2] = left >= 2 ? base64_tab[(a >> 6) & 0x3f] : '='; dst[3] = left >= 3 ? base64_tab[a & 0x3f] : '='; dst += 4; b += 3; left -= 3; } *dst = '\0'; return true; } static const char *key_guid = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; static void hs_solve_challenge(char dst[32], const char *key_base64) { char challenge[128]; size_t base64_len = strlen(key_base64); size_t guid_len = strlen(key_guid); size_t challenge_len = base64_len + guid_len; bqws_assert(challenge_len <= sizeof(challenge)); memcpy(challenge, key_base64, base64_len); memcpy(challenge + base64_len, key_guid, guid_len); uint8_t digest[20]; bqws_sha1(digest, challenge, challenge_len); bool ret = hs_to_base64(dst, 32, digest, sizeof(digest)); bqws_assert(ret == true); // 32 bytes should always be enough } static void hs_client_handshake(bqws_socket *ws, const bqws_client_opts *opts) { bqws_assert_locked(&ws->io.mutex); bqws_assert(!ws->is_server); const char *path = str_nonempty(opts->path) ? opts->path : "/"; hs_push3(ws, "GET ", path, " HTTP/1.1\r\n"); // Static headers hs_push(ws, "Connection: Upgrade\r\n" "Upgrade: websocket\r\n" ); // User headers if (str_nonempty(opts->host)) hs_push3(ws, "Host: ", opts->host, "\r\n"); if (str_nonempty(opts->origin)) hs_push3(ws, "Origin: ", opts->origin, "\r\n"); if (opts->num_protocols > 0) { hs_push(ws, "Sec-WebSocket-Protocol: "); for (size_t i = 0; i < opts->num_protocols; i++) { hs_push2(ws, i > 0 ? ", " : "", opts->protocols[i]); } hs_push(ws, "\r\n"); } // Version (fixed currently, TODO multi-version support) hs_push(ws, "Sec-WebSocket-Version: 13\r\n"); // Random key bqws_random_entropy entropy; entropy.clock = clock(); entropy.time = time(NULL); entropy.function_pointer = &hs_client_handshake; entropy.stack_pointer = &entropy; entropy.heap_pointer = ws; entropy.mask_key = mask_make_key(ws); uint8_t digest[20]; bqws_sha1(digest, &entropy, sizeof(entropy)); const uint8_t *key = digest; if (opts->use_random_key) { key = (const uint8_t*)opts->random_key; } // We need to retain the key until we have parsed the server handshake bool ret = hs_to_base64(ws->io.client_key_base64, sizeof(ws->io.client_key_base64), key, 16); bqws_assert(ret == true); // 32 bytes should always be enough hs_push3(ws, "Sec-WebSocket-Key: ", ws->io.client_key_base64, "\r\n"); // Final CRLF hs_push(ws, "\r\n"); } static void hs_server_handshake(bqws_socket *ws) { bqws_assert_locked(&ws->io.mutex); bqws_assert(ws->is_server); bqws_assert(ws->io.opts_from_client); // Fixed header hs_push(ws, "HTTP/1.1 101 Switching Protocols\r\n" "Upgrade: websocket\r\n" "Connection: Upgrade\r\n" ); // Protocol bqws_mutex_lock(&ws->state.mutex); const char *protocol = ws->state.chosen_protocol; bqws_mutex_unlock(&ws->state.mutex); bqws_assert(protocol); if (*protocol && ws->io.client_has_protocol) { hs_push3(ws, "Sec-WebSocket-Protocol: ", protocol, "\r\n"); } // SHA-1 challenge char accept[32]; hs_solve_challenge(accept, ws->io.client_key_base64); hs_push3(ws, "Sec-WebSocket-Accept: ", accept, "\r\n"); // Final CRLF hs_push(ws, "\r\n"); // Free the handshake state ws_free(ws, ws->io.opts_from_client, sizeof(bqws_client_opts)); ws->io.opts_from_client = NULL; } // -- Handshake parsing static bool hs_parse_literal(bqws_socket *ws, size_t *pos, const char *str) { bqws_assert_locked(&ws->io.mutex); size_t len = strlen(str); if (ws->io.handshake.size - *pos < len) return false; const char *ref = ws->io.handshake.data + *pos; if (memcmp(ref, str, len) != 0) return false; *pos += len; return true; } static char *hs_parse_token(bqws_socket *ws, size_t *pos, char end) { bqws_assert_locked(&ws->io.mutex); size_t begin = *pos, p = begin; while (p != ws->io.handshake.size) { char c = ws->io.handshake.data[p]; if (c == end) { ws->io.handshake.data[p] = '\0'; *pos = p + 1; return ws->io.handshake.data + begin; } if (c == '\r' || c == '\n') return NULL; p++; } return NULL; } static void hs_skip_space(bqws_socket *ws, size_t *pos) { bqws_assert_locked(&ws->io.mutex); while (*pos < ws->io.handshake.size) { char c = ws->io.handshake.data[*pos]; if (c != ' ' && c != '\t') break; ++*pos; } } // Case-insensitive (ASCII) string compare static bool streq_ic(const char *sa, const char *sb) { for (;;) { char a = *sa++, b = *sb++; if ((unsigned)(unsigned char)a < 0x80u) a = (char)tolower(a); if ((unsigned)(unsigned char)b < 0x80u) b = (char)tolower(b); if (a != b) return false; if (a == 0) return true; } } static bool hs_parse_client_handshake(bqws_socket *ws) { bqws_assert_locked(&ws->io.mutex); bqws_assert(ws->is_server); bqws_assert(!ws->io.opts_from_client); size_t pos = 0; bqws_client_opts *opts = (bqws_client_opts*)ws_alloc(ws, sizeof(bqws_client_opts)); if (!opts) return false; memset(opts, 0, sizeof(bqws_client_opts)); ws->io.opts_from_client = opts; // GET /path HTTP/1.1 if (!hs_parse_literal(ws, &pos, "GET")) return false; hs_skip_space(ws, &pos); opts->path = hs_parse_token(ws, &pos, ' '); if (!opts->path) return false; hs_skip_space(ws, &pos); if (!hs_parse_literal(ws, &pos, "HTTP/1.1\r\n")) return false; // Headers while (!hs_parse_literal(ws, &pos, "\r\n")) { if (opts->num_headers >= BQWS_MAX_HEADERS) { ws_fail(ws, BQWS_ERR_TOO_MANY_HEADERS); return false; } bqws_header *header = &opts->headers[opts->num_headers]; header->name = hs_parse_token(ws, &pos, ':'); hs_skip_space(ws, &pos); size_t value_pos = pos; header->value = hs_parse_token(ws, &pos, '\r'); if (!header->name || !header->value) return false; if (!hs_parse_literal(ws, &pos, "\n")) return false; if (streq_ic(header->name, "Host")) { opts->host = header->value; opts->num_headers++; } else if (streq_ic(header->name, "Origin")) { opts->origin = header->value; opts->num_headers++; } else if (streq_ic(header->name, "Sec-Websocket-Protocol")) { size_t cur_pos = pos; ws->io.client_has_protocol = true; // Parse protocols pos = value_pos; while (pos < cur_pos) { // Either token ',' or final token that is zero-terminated // already since it's the last thing in `header->value`. char *protocol = hs_parse_token(ws, &pos, ','); hs_skip_space(ws, &pos); if (!protocol) { protocol = ws->io.handshake.data + pos; pos = cur_pos; } if (opts->num_protocols >= BQWS_MAX_PROTOCOLS) { ws_fail(ws, BQWS_ERR_TOO_MANY_PROTOCOLS); return false; } opts->protocols[opts->num_protocols++] = protocol; } pos = cur_pos; } else if (streq_ic(header->name, "Sec-Websocket-Key")) { size_t len = strlen(header->value) + 1; if (len > sizeof(ws->io.client_key_base64)) { ws_fail(ws, BQWS_ERR_HEADER_KEY_TOO_LONG); return false; } memcpy(ws->io.client_key_base64, header->value, len); } else if (streq_ic(header->name, "Sec-Websocket-Version")) { // TODO: Version negotiatoin if (strcmp(header->value, "13") != 0) { ws_fail(ws, BQWS_ERR_UNSUPPORTED_VERSION); return false; } } else { opts->num_headers++; } } // Store the end of the parsed header in case we read past the // header in the beginning. ws->io.handshake.read_offset = pos; if (!opts->host) opts->host = ""; if (!opts->origin) opts->origin = ""; return true; } static bool hs_parse_server_handshake(bqws_socket *ws) { bqws_assert_locked(&ws->io.mutex); bqws_assert(!ws->is_server); size_t pos = 0; // HTTP/1.1 101 Switching Protocols if (!hs_parse_literal(ws, &pos, "HTTP/1.1 101")) return false; hs_parse_token(ws, &pos, '\r'); // Skip description if (!hs_parse_literal(ws, &pos, "\n")) return false; // Headers while (!hs_parse_literal(ws, &pos, "\r\n")) { // TODO: Keep headers? bqws_header header; header.name = hs_parse_token(ws, &pos, ':'); hs_skip_space(ws, &pos); header.value = hs_parse_token(ws, &pos, '\r'); if (!header.name || !header.value) return false; if (!hs_parse_literal(ws, &pos, "\n")) return false; if (streq_ic(header.name, "Sec-Websocket-Accept")) { // Check the SHA of the challenge char reference[32]; hs_solve_challenge(reference, ws->io.client_key_base64); if (strcmp(header.value, reference) != 0) { ws_fail(ws, BQWS_ERR_HEADER_BAD_ACCEPT); return false; } } else if (streq_ic(header.name, "Sec-Websocket-Protocol")) { // Protocol that the server chose // Keep the first one if there's duplicates bqws_mutex_lock(&ws->state.mutex); if (!ws->state.chosen_protocol) { char *copy = ws_copy_str(ws, header.value); if (!ws->state.chosen_protocol) { ws->state.chosen_protocol = copy; } else { ws_free_str(ws, copy); } } bqws_mutex_unlock(&ws->state.mutex); } } // Store the end of the parsed header in case we read past the // header in the beginning. ws->io.handshake.read_offset = pos; // If the server didn't choose any protocol set it as "" bqws_mutex_lock(&ws->state.mutex); if (!ws->state.chosen_protocol) { char *copy = ws_copy_str(ws, ""); if (!ws->state.chosen_protocol) { ws->state.chosen_protocol = copy; } else { ws_free_str(ws, copy); } } bqws_mutex_unlock(&ws->state.mutex); return true; } static void hs_finish_handshake(bqws_socket *ws) { bqws_assert_locked(&ws->io.mutex); if (ws->err) return; ws_log(ws, "State: OPEN"); bqws_mutex_lock(&ws->state.mutex); ws->state.state = BQWS_STATE_OPEN; bqws_mutex_unlock(&ws->state.mutex); // Free the handshake buffer ws_free(ws, ws->io.handshake.data, ws->io.handshake.capacity); ws->io.handshake.data = NULL; ws->io.handshake.size = 0; ws->io.handshake.capacity = 0; // Notify IO that the connection is open if (ws->user_io.notify_fn) { ws->user_io.notify_fn(ws->user_io.user, ws); } } static void hs_store_handshake_overflow(bqws_socket *ws) { bqws_assert_locked(&ws->io.mutex); size_t offset = ws->io.handshake.read_offset; size_t left = ws->io.handshake.size - offset; if (left == 0) return; ws->io.handshake_overflow.data = (char*)ws_alloc(ws, left); if (!ws->io.handshake_overflow.data) return; memcpy(ws->io.handshake_overflow.data, ws->io.handshake.data + offset, left); ws->io.handshake_overflow.capacity = left; ws->io.handshake_overflow.size = left; } // Control messages static void ws_enqueue_send(bqws_socket *ws, bqws_msg_imp *msg) { msg_enqueue(&ws->send_queue, msg); if (ws->user_io.notify_fn) { ws->user_io.notify_fn(ws->user_io.user, ws); } } static void ws_enqueue_recv(bqws_socket *ws, bqws_msg_imp *msg) { // If the user callback returns true the message won't be // enqueued to the receive queue. if (ws->message_fn) { msg_release_ownership(ws, msg); if (ws->message_fn(ws->message_user, ws, &msg->msg)) { // Message was consumed and won't be processed so add // it to the total count msg_queue_add_to_total(&ws->recv_queue, msg->msg.size); } if (!msg_acquire_ownership(ws, msg)) return; } msg_enqueue(&ws->recv_queue, msg); } static void ws_handle_control(bqws_socket *ws, bqws_msg_imp *msg) { bqws_msg_type type = msg->msg.type; bqws_msg_imp *msg_to_enqueue = msg; if (type == BQWS_MSG_CONTROL_CLOSE) { bqws_mutex_lock(&ws->state.mutex); // Set peer close reason from the message if (msg->msg.size >= 2) { ws->state.peer_reason = (bqws_close_reason)( ((uint32_t)(uint8_t)msg->msg.data[0] << 8) | ((uint32_t)(uint8_t)msg->msg.data[1] << 0) ); } else { ws->state.peer_reason = BQWS_CLOSE_NO_REASON; } // Set unknown error if the connection was closed with an error if (ws->state.peer_reason != BQWS_CLOSE_NORMAL && ws->state.peer_reason != BQWS_CLOSE_GOING_AWAY) { ws->state.peer_err = BQWS_ERR_UNKNOWN; } // Potentially patch bqws-specific info if (msg->msg.size == sizeof(bqws_err_close_data)) { bqws_err_close_data *data = (bqws_err_close_data*)msg->msg.data; if (!memcmp(data->magic, "BQWS", 4)) { ws->state.peer_err = (bqws_error)( ((uint32_t)(uint8_t)data->error_be[0] << 24) | ((uint32_t)(uint8_t)data->error_be[1] << 16) | ((uint32_t)(uint8_t)data->error_be[2] << 8) | ((uint32_t)(uint8_t)data->error_be[3] << 0) ); } } // Echo the close message back if (!ws->state.close_to_send) { ws->state.close_to_send = msg; // Don't free the message as it will be re-sent msg = NULL; } // Peer has closed connection so we go directly to CLOSED if (ws->state.state == BQWS_STATE_OPEN) { ws_log(ws, "State: CLOSING (received Close from peer)"); ws->state.start_closing_ts = bqws_get_timestamp(); ws->state.state = BQWS_STATE_CLOSING; } ws->state.stop_read = true; ws->state.close_received = true; if (ws->state.close_sent) { ws_close(ws); } bqws_mutex_unlock(&ws->state.mutex); } else if (type == BQWS_MSG_CONTROL_PING) { if (ws->recv_control_messages) { // We want to re-use the PING message to send it back // so we need to copy it for receiving bqws_msg_imp *copy = msg_alloc(ws, type, msg->msg.size); if (!copy) return; memcpy(copy->msg.data, msg->msg.data, msg->msg.size); msg_to_enqueue = copy; } // Turn the PING message into a PONG msg->msg.type = BQWS_MSG_CONTROL_PONG; bqws_mutex_lock(&ws->state.mutex); // Only retain the latest PONG to send back if (ws->state.pong_to_send) { msg_free_owned(ws, ws->state.pong_to_send); } ws->state.pong_to_send = msg; bqws_mutex_unlock(&ws->state.mutex); // Don't free the message as it will be re-sent msg = NULL; } else if (type == BQWS_MSG_CONTROL_PONG) { // PONG messages don't require any kind of handling } else { bqws_assert(0 && "Unexpected control message"); } // Receive control messages if (ws->recv_control_messages) { ws_enqueue_recv(ws, msg_to_enqueue); } else if (msg) { msg_free_owned(ws, msg); } } // Input / output // Read data into a buffer, returns amount of bytes used read. // Returns 0 and sets `ws->err` if parsing fails. static size_t ws_recv_from_handshake_overflow(void *user, bqws_socket *ws, void *data, size_t max_size, size_t min_size) { bqws_assert(ws && ws->magic == BQWS_SOCKET_MAGIC); bqws_assert_locked(&ws->io.mutex); size_t offset = ws->io.handshake_overflow.read_offset; size_t left = ws->io.handshake_overflow.size - offset; size_t to_copy = max_size; if (to_copy > left) to_copy = left; memcpy(data, ws->io.handshake_overflow.data + offset, to_copy); ws->io.handshake_overflow.read_offset += to_copy; return to_copy; } static bool ws_read_handshake(bqws_socket *ws, bqws_io_recv_fn recv_fn, void *user) { bqws_assert_locked(&ws->io.mutex); for (;;) { if (ws->io.handshake.size == ws->io.handshake.capacity) { // Grow the buffer geometrically up to `max_handshake_size` size_t new_cap = ws->io.handshake.capacity * 2; if (new_cap == 0) new_cap = 512; if (new_cap > ws->limits.max_handshake_size) new_cap = ws->limits.max_handshake_size; if (new_cap == ws->io.handshake.capacity) { ws_fail(ws, BQWS_ERR_LIMIT_MAX_HANDSHAKE_SIZE); return false; } char *data = (char*)ws_realloc(ws, ws->io.handshake.data, ws->io.handshake.capacity, new_cap); if (!data) return false; ws->io.handshake.data = data; ws->io.handshake.capacity = new_cap; } // TODO: min_size can be up to 4 depending on the suffix of the buffer // Read some data size_t to_read = ws->io.handshake.capacity - ws->io.handshake.size; size_t num_read = recv_fn(user, ws, ws->io.handshake.data + ws->io.handshake.size, to_read, 1); if (num_read == 0) return false; if (num_read == SIZE_MAX) { ws_fail(ws, BQWS_ERR_IO_READ); return false; } bqws_assert(num_read <= to_read); ws->io.handshake.size += num_read; // Scan for \r\n\r\n ptrdiff_t begin = (ptrdiff_t)ws->io.handshake.size - num_read - 4; if (begin < 0) begin = 0; char *ptr = ws->io.handshake.data + begin; char *end = ws->io.handshake.data + ws->io.handshake.size; while ((ptr = (char*)memchr(ptr, '\r', end - ptr)) != NULL) { if (end - ptr >= 4 && !memcmp(ptr, "\r\n\r\n", 4)) { return true; } else { ptr++; } } if (num_read != to_read) break; } return false; } static bool ws_read_data(bqws_socket *ws, bqws_io_recv_fn recv_fn, void *user) { bqws_assert_locked(&ws->io.mutex); bqws_assert(ws && ws->magic == BQWS_SOCKET_MAGIC); bqws_msg_buffer *buf = &ws->io.recv_buf; bqws_state state; bqws_mutex_lock(&ws->state.mutex); if (ws->state.stop_read) { bqws_mutex_unlock(&ws->state.mutex); return false; } state = ws->state.state; bqws_mutex_unlock(&ws->state.mutex); if (state == BQWS_STATE_CONNECTING) { if (ws->is_server) { // Server: read the client handshake first, after it's done wait for // `ws_write_data()` to set `ws->state == BQWS_STATE_OPEN` if (!ws->io.client_handshake_done) { // Read the client handshake if (ws_read_handshake(ws, recv_fn, user)) { if (!hs_parse_client_handshake(ws)) { ws_fail(ws, BQWS_ERR_HEADER_PARSE); return false; } ws->io.client_handshake_done = true; // Re-use the handshake buffer for the response, but copy // remaining data to be read later hs_store_handshake_overflow(ws); ws->io.handshake.size = 0; // Notify IO that there is a handshake to send if (ws->user_io.notify_fn) { ws->user_io.notify_fn(ws->user_io.user, ws); } } } // Wait that the response is sent return false; } else { // Client: Send the request first before trying to read the response if (!ws->io.client_handshake_done) return false; if (!ws_read_handshake(ws, recv_fn, user)) return false; if (!hs_parse_server_handshake(ws)) { ws_fail(ws, BQWS_ERR_HEADER_PARSE); return false; } // Store remaining data before deleting the handshake hs_store_handshake_overflow(ws); // Client handshake is done! hs_finish_handshake(ws); } } // If there's still data in the handshake buffer empty it before // reading any new data if (ws->io.handshake_overflow.data && recv_fn != &ws_recv_from_handshake_overflow) { // Read from the handshake until we reach the end while (!ws->err && ws->io.handshake_overflow.read_offset < ws->io.handshake_overflow.size) { if (!ws_read_data(ws, &ws_recv_from_handshake_overflow, NULL)) { return false; } } if (ws->err) return false; // Free the handshake ws_free(ws, ws->io.handshake_overflow.data, ws->io.handshake_overflow.capacity); ws->io.handshake_overflow.data = NULL; ws->io.handshake_overflow.size = 0; ws->io.handshake_overflow.capacity = 0; // Continue with reading from the actual data source } // Header has not been parsed yet if (!buf->msg) { // Check if we can fit a new message to the receive queue if (ws->recv_queue.num_messages >= ws->limits.max_recv_queue_messages || ws->recv_queue.byte_size >= ws->limits.max_recv_queue_size) { return false; } // We need to read at least two bytes to determine // the header size if (buf->header_size == 0) { if (buf->header_offset < 2) { size_t to_read = sizeof(ws->io.recv_header) - buf->header_offset; size_t min_read = 2 - buf->header_offset; size_t num_read = recv_fn(user, ws, ws->io.recv_header + buf->header_offset, to_read, min_read); if (num_read == 0) return false; if (num_read == SIZE_MAX) { ws_fail(ws, BQWS_ERR_IO_READ); return false; } bqws_assert(num_read <= to_read); buf->header_offset += num_read; if (ws->ping_interval != SIZE_MAX) { ws->io.last_read_ts = bqws_get_timestamp(); } } if (buf->header_offset < 2) return false; uint8_t mask_len = ws->io.recv_header[1]; uint32_t len = mask_len & 0x7f; // Minimum header size size_t header_size = 2; // MASK bit set: contains 32-bit mask field if (mask_len & 0x80) header_size += 4; // 16/64-bit message length if (len == 126) header_size += 2; else if (len == 127) header_size += 8; buf->header_size = header_size; bqws_assert(buf->header_size <= sizeof(ws->io.recv_header)); } // Read more header data if we need it if (buf->header_offset < buf->header_size) { size_t to_read = sizeof(ws->io.recv_header) - buf->header_offset; size_t min_read = buf->header_size - buf->header_offset; size_t num_read = recv_fn(user, ws, ws->io.recv_header + buf->header_offset, to_read, min_read); if (num_read == 0) return false; if (num_read == SIZE_MAX) { ws_fail(ws, BQWS_ERR_IO_READ); return false; } bqws_assert(num_read <= to_read); buf->header_offset += num_read; if (ws->ping_interval != SIZE_MAX) { ws->io.last_read_ts = bqws_get_timestamp(); } return false; } if (buf->header_offset < buf->header_size) return false; // Parse the header and allocate the message const uint8_t *h = (const uint8_t*)ws->io.recv_header; // Static header bits bool fin = (h[0] & 0x80) != 0; if (h[0] & 0x70) { // Reserved bits RSV1-3 ws_fail(ws, BQWS_ERR_RESERVED_BIT); return false; } uint32_t opcode = (uint32_t)(h[0] & 0x0f); uint32_t mask = (uint32_t)(h[1] & 0x80) != 0; uint64_t payload_length = (uint64_t)(h[1] & 0x7f); h += 2; // Extended length: Read 2 or 8 bytes of big // endian payload length. size_t payload_ext = 0; if (payload_length == 126) { payload_ext = 2; payload_length = 0; } else if (payload_length == 127) { payload_ext = 8; payload_length = 0; } for (size_t i = 0; i < payload_ext; i++) { size_t shift = (payload_ext - i - 1) * 8; payload_length |= (uint64_t)h[i] << shift; } h += payload_ext; // Check the payload length and cast to `size_t` if (payload_length > (uint64_t)ws->limits.max_recv_msg_size) { ws_fail(ws, BQWS_ERR_LIMIT_MAX_RECV_MSG_SIZE); return false; } size_t msg_size = (size_t)payload_length; // Masking key buf->masked = mask; if (mask) { memcpy(&buf->mask_key, h, 4); h += 4; } bqws_assert((size_t)((const char*)h - ws->io.recv_header) == buf->header_size); bqws_msg_type type = BQWS_MSG_INVALID; // Resolve the type of the message if (opcode == 0x0) { // Continuation frame if (buf->partial_type == BQWS_MSG_INVALID) { // Continuation frame without a prior partial frame ws_fail(ws, BQWS_ERR_BAD_CONTINUATION); return false; } type = (bqws_msg_type)(buf->partial_type | BQWS_MSG_PARTIAL_BIT); if (fin) { type = (bqws_msg_type)(type | BQWS_MSG_FINAL_BIT); buf->partial_type = BQWS_MSG_INVALID; } } else if (opcode == 0x1 || opcode == 0x2) { // Text or Binary type = opcode == 0x1 ? BQWS_MSG_TEXT : BQWS_MSG_BINARY; if (!fin) { if (buf->partial_type != BQWS_MSG_INVALID) { // New partial message even though one is already // being sent ws_fail(ws, BQWS_ERR_UNFINISHED_PARTIAL); return false; } buf->partial_type = type; type = (bqws_msg_type)(type | BQWS_MSG_PARTIAL_BIT); } } else if (opcode >= 0x8 && opcode <= 0xa) { // Control frames if (opcode == 0x8) type = BQWS_MSG_CONTROL_CLOSE; else if (opcode == 0x9) type = BQWS_MSG_CONTROL_PING; else if (opcode == 0xa) type = BQWS_MSG_CONTROL_PONG; if (!fin) { // Control frames may not be fragmented ws_fail(ws, BQWS_ERR_PARTIAL_CONTROL); return false; } } else { // Unsupported opcode ws_fail(ws, BQWS_ERR_BAD_OPCODE); return false; } bqws_assert(type != BQWS_MSG_INVALID); // All good, allocate the message bqws_msg_imp *imp = msg_alloc(ws, type, msg_size); if (!imp) return false; buf->msg = imp; buf->offset = 0; // Copy rest of the header bytes to the message size_t offset = buf->header_size; size_t left = buf->header_offset - offset; if (left > 0) { size_t to_copy = left; if (to_copy > imp->msg.size) to_copy = imp->msg.size; memcpy(imp->msg.data, ws->io.recv_header + offset, to_copy); buf->offset += to_copy; offset += to_copy; left -= to_copy; } // If there's still some data shift it as the next header if (left > 0) { memmove(ws->io.recv_header, ws->io.recv_header + offset, left); } buf->header_offset = left; } bqws_msg_imp *msg = buf->msg; // Read message data if the message is not empty bqws_assert(buf->offset <= msg->msg.size); if (msg->msg.size > 0 && buf->offset < msg->msg.size) { size_t to_read = msg->msg.size - buf->offset; size_t num_read = recv_fn(user, ws, msg->msg.data + buf->offset, to_read, to_read); if (num_read == 0) return false; if (num_read == SIZE_MAX) { ws_fail(ws, BQWS_ERR_IO_READ); return false; } bqws_assert(num_read <= to_read); if (ws->ping_interval != SIZE_MAX) { ws->io.last_read_ts = bqws_get_timestamp(); } buf->offset += num_read; if (num_read < to_read) return false; } if (buf->masked) { mask_apply(msg->msg.data, msg->msg.size, buf->mask_key); } bqws_assert(buf->offset == msg->msg.size); // Peek at all incoming messages before processing if (ws->peek_fn) { ws->peek_fn(ws->peek_user, ws, &msg->msg, true); } // If we copied the last bytes of the message we can push it // to the queue and clear the buffer. bqws_msg_type type = msg->msg.type; if (ws->log_recv) { ws_log2(ws, "Received: ", bqws_msg_type_str(buf->msg->msg.type)); } if ((type & BQWS_MSG_PARTIAL_BIT) != 0 && !ws->recv_partial_messages) { // Only allow partial messages that combine up to the maximum message size bqws_assert(msg->msg.size <= ws->limits.max_recv_msg_size); if (ws->io.recv_partial_size >= ws->limits.max_recv_msg_size - msg->msg.size) { ws_fail(ws, BQWS_ERR_LIMIT_MAX_RECV_MSG_SIZE); return false; } ws->io.recv_partial_size += msg->msg.size; // If we dont expose partial messages collect them to `recv_partial_queue`. if (type & BQWS_MSG_FINAL_BIT) { // If this is the final message concatenate all the partial messages // in the queue and enqueue the final one> bqws_msg_type base_type = (bqws_msg_type)(msg->msg.type & BQWS_MSG_TYPE_MASK); bqws_msg_imp *combined = msg_alloc(ws, base_type, ws->io.recv_partial_size); if (!combined) return false; size_t offset = 0; // `recv_queue` with this message as the last part. bqws_msg_imp *part; while ((part = msg_dequeue(&ws->recv_partial_queue)) != NULL) { bqws_assert(part->magic == BQWS_MSG_MAGIC); bqws_assert((part->msg.type & BQWS_MSG_TYPE_MASK) == base_type); memcpy(combined->msg.data + offset, part->msg.data, part->msg.size); offset += part->msg.size; // Delete the part msg_free_owned(ws, part); } // Final part memcpy(combined->msg.data + offset, msg->msg.data, msg->msg.size); offset += msg->msg.size; msg_free_owned(ws, msg); bqws_assert(offset == combined->msg.size); ws_enqueue_recv(ws, combined); // Clear the partial total size ws->io.recv_partial_size = 0; } else { if (ws->recv_partial_queue.num_messages >= ws->limits.max_partial_message_parts) { ws_fail(ws, BQWS_ERR_LIMIT_MAX_PARTIAL_MESSAGE_PARTS); return false; } msg_enqueue(&ws->recv_partial_queue, msg); } } else { if (type & BQWS_MSG_CONTROL_MASK) { // Control message, handle it. `ws_handle_control()` enqueues the // message to `recv_queue` internally if required. ws_handle_control(ws, msg); } else { // Non-partial data message ws_enqueue_recv(ws, msg); } } buf->offset = 0; buf->header_size = 0; buf->msg = NULL; return true; } static bool ws_write_handshake(bqws_socket *ws, bqws_io_send_fn *send_fn, void *user) { bqws_assert_locked(&ws->io.mutex); size_t to_send = ws->io.handshake.size - ws->io.handshake.write_offset; size_t sent = send_fn(user, ws, ws->io.handshake.data + ws->io.handshake.write_offset, to_send); if (sent == SIZE_MAX) { ws_fail(ws, BQWS_ERR_IO_WRITE); return false; } bqws_assert(sent <= to_send); ws->io.handshake.write_offset += sent; return sent == to_send; } static bool ws_write_data(bqws_socket *ws, bqws_io_send_fn *send_fn, void *user) { bqws_assert(ws && ws->magic == BQWS_SOCKET_MAGIC); bqws_assert_locked(&ws->io.mutex); bqws_msg_buffer *buf = &ws->io.send_buf; bqws_state state; char *protocol; bqws_mutex_lock(&ws->state.mutex); if (ws->state.stop_write) { bqws_mutex_unlock(&ws->state.mutex); return false; } state = ws->state.state; protocol = ws->state.chosen_protocol; bqws_mutex_unlock(&ws->state.mutex); if (state == BQWS_STATE_CONNECTING) { if (ws->is_server) { // Server: read the client handshake first if (!ws->io.client_handshake_done) return false; // Wait for the user to accept/reject the connection if (!protocol) return false; // Write the server handshake on demand if (ws->io.handshake.size == 0) { hs_server_handshake(ws); if (ws->err) return false; } // Write the server handshake if (!ws_write_handshake(ws, send_fn, user)) return false; // Server handshake is done! hs_finish_handshake(ws); } else { // Client: Send the request and always wait for response if (!ws->io.client_handshake_done) { if (!ws_write_handshake(ws, send_fn, user)) return false; // Re-use the handshake buffer for the response, ws->io.handshake.size = 0; ws->io.client_handshake_done = true; } return false; } } if (!buf->msg) { // No message: Send high priority messages first. bqws_mutex_lock(&ws->state.mutex); if (ws->state.close_to_send && !ws->state.close_sent) { // First priority: Send close message buf->msg = ws->state.close_to_send; ws->state.close_to_send = NULL; bqws_assert(buf->msg->msg.type == BQWS_MSG_CONTROL_CLOSE); } else if (ws->state.state != BQWS_STATE_OPEN) { // Stop sending anything if the state is not open } else if (ws->state.pong_to_send) { // Try to respond to PING messages fast buf->msg = ws->state.pong_to_send; ws->state.pong_to_send = NULL; bqws_assert(buf->msg->msg.type == BQWS_MSG_CONTROL_PONG); } else { // Send user message if there is one buf->msg = msg_dequeue(&ws->send_queue); } bqws_mutex_unlock(&ws->state.mutex); // Did not find any message if (!buf->msg) return false; bqws_assert(buf->msg && buf->msg->magic == BQWS_MSG_MAGIC); } bqws_msg_imp *msg = buf->msg; bqws_assert(msg && msg->magic == BQWS_MSG_MAGIC); // Re-assign the public socket to be this one for the callback msg->msg.socket = ws; // Peek at all outgoing messages before processing if (ws->peek_fn) { ws->peek_fn(ws->peek_user, ws, &msg->msg, false); } if (ws->send_message_fn) { msg_release_ownership(ws, msg); if (ws->send_message_fn(ws->send_message_user, ws, &msg->msg)) { if (ws->log_send) { ws_log2(ws, "Direct send: ", bqws_msg_type_str(msg->msg.type)); } buf->msg = NULL; return true; } else { msg_acquire_ownership(ws, msg); return false; } } if (ws->ping_interval != SIZE_MAX) { ws->io.last_write_ts = bqws_get_timestamp(); } if (buf->header_size == 0) { bqws_msg_type type = msg->msg.type; bool mask = ws->is_server ? ws->mask_server : !ws->unsafe_dont_mask_client; bool fin = true; uint32_t opcode = ~0u; if (type & BQWS_MSG_TYPE_MASK) { bqws_msg_type base_type = (bqws_msg_type)(type & BQWS_MSG_TYPE_MASK); opcode = base_type == BQWS_MSG_TEXT ? 0x1 : 0x2; if (type & BQWS_MSG_PARTIAL_BIT) { if (buf->partial_type != BQWS_MSG_INVALID) { // Partial continuation bqws_assert(buf->partial_type == base_type); opcode = 0x0; } if (type & BQWS_MSG_FINAL_BIT) { // This can be either the end of a partial message // or just a single-part partial message. buf->partial_type = BQWS_MSG_INVALID; } else { // Partial begin or continuation buf->partial_type = base_type; fin = false; } } } else if (type & BQWS_MSG_CONTROL_MASK) { // Control message if (type == BQWS_MSG_CONTROL_CLOSE) opcode = 0x8; else if (type == BQWS_MSG_CONTROL_PING) opcode = 0x9; else if (type == BQWS_MSG_CONTROL_PONG) opcode = 0xa; } else { bqws_assert(0 && "Trying to send non-data non-control message"); } bqws_assert(opcode != ~0u); // Use the smallest payload length representation size_t payload_ext = 0; size_t payload_len = msg->msg.size; if (payload_len > 65535u) { payload_len = 127; payload_ext = 8; } else if (payload_len > 125) { payload_len = 126; payload_ext = 2; } uint8_t *h = (uint8_t*)ws->io.send_header; // Static header bits h[0] = (fin ? 0x80 : 0x0) | (uint8_t)opcode; h[1] = (mask ? 0x80 : 0x0) | (uint8_t)payload_len; h += 2; // Extended length: Read 2 or 8 bytes of big // endian payload length. for (size_t i = 0; i < payload_ext; i++) { size_t shift = (payload_ext - i - 1) * 8; h[i] = (uint8_t)((uint64_t)msg->msg.size >> shift); } h += payload_ext; // Masking key buf->masked = mask; if (mask) { uint32_t mask_key = mask_make_key(ws); buf->mask_key = mask_key; memcpy(h, &buf->mask_key, 4); h += 4; // Apply the mask mask_apply(msg->msg.data, msg->msg.size, mask_key); } buf->header_size = (char*)h - ws->io.send_header; bqws_assert(buf->header_size <= sizeof(ws->io.send_header)); } // Send the header if (buf->header_offset < buf->header_size) { size_t to_send = buf->header_size - buf->header_offset; size_t sent = send_fn(user, ws, ws->io.send_header + buf->header_offset, to_send); if (sent == SIZE_MAX) { ws_fail(ws, BQWS_ERR_IO_WRITE); return false; } bqws_assert(sent <= to_send); buf->header_offset += sent; if (sent < to_send) return false; } // Send the message { size_t to_send = msg->msg.size - buf->offset; size_t sent = send_fn(user, ws, msg->msg.data + buf->offset, to_send); if (sent == SIZE_MAX) { ws_fail(ws, BQWS_ERR_IO_WRITE); return false; } bqws_assert(sent <= to_send); buf->offset += sent; if (sent < to_send) return false; } if (ws->log_send) { ws_log2(ws, "Sent: ", bqws_msg_type_str(buf->msg->msg.type)); } // Mark close as been sent if (msg->msg.type == BQWS_MSG_CONTROL_CLOSE) { bqws_mutex_lock(&ws->state.mutex); if (ws->state.state == BQWS_STATE_OPEN) { ws_log(ws, "State: CLOSING (queued user close)"); ws->state.state = BQWS_STATE_CLOSING; ws->state.start_closing_ts = bqws_get_timestamp(); } ws->state.close_sent = true; if (ws->state.close_received) { ws_close(ws); } bqws_mutex_unlock(&ws->state.mutex); } // Delete the message msg_free_owned(ws, msg); // Sent everything, clear status buf->offset = 0; buf->header_offset = 0; buf->header_size = 0; buf->msg = NULL; return true; } // WebSocket initialization static char *verify_filter_str(bqws_verify_filter *f, size_t *offset, const char *str) { if (!str) return NULL; size_t len = strlen(str) + 1; char *dst = f->text_data + *offset; memcpy(dst, str, len); *offset += len; return dst; } static void bqws_internal_filter_verify(void *user, bqws_socket *ws, const bqws_client_opts *opts) { bqws_verify_filter *f = (bqws_verify_filter*)user; bool ok = true; // Check common headers ok = ok && (!f->path || !strcmp(f->path, opts->path)); ok = ok && (!f->host || streq_ic(f->host, opts->host)); ok = ok && (!f->origin || streq_ic(f->origin, opts->origin)); const char *protocol = NULL; if (f->num_protocols > 0) { // If the filter has protocols try to find one // O(n^2) but bounded by BQWS_MAX_PROTOCOLS for (size_t ci = 0; ci < opts->num_protocols && !protocol; ci++) { for (size_t fi = 0; fi < f->num_protocols; fi++) { if (!strcmp(f->protocols[fi], opts->protocols[ci])) { protocol = f->protocols[fi]; break; } } } ok = ok && protocol != NULL; } else { // If not don't use any protocol name protocol = ""; } if (ok) { bqws_assert(protocol != NULL); if (f->verify_fn) { f->verify_fn(f->verify_user, ws, opts); } else { bqws_server_accept(ws, protocol); } } else { bqws_server_reject(ws); } } static void ws_expand_default_limits(bqws_limits *limits) { #define WS_DEFAULT(p_name, p_value) if (!limits->p_name) limits->p_name = p_value WS_DEFAULT(max_memory_used, 262144); WS_DEFAULT(max_recv_msg_size, 262144); WS_DEFAULT(max_handshake_size, 262144); WS_DEFAULT(max_recv_queue_messages, 1024); WS_DEFAULT(max_recv_queue_size, 262144); WS_DEFAULT(max_partial_message_parts, 16384); #undef WS_DEFAULT } static bqws_socket *ws_new_socket(const bqws_opts *opts, bool is_server) { bqws_opts null_opts; if (!opts) { memset(&null_opts, 0, sizeof(null_opts)); opts = &null_opts; } bqws_socket *ws = (bqws_socket*)bqws_allocator_alloc(&opts->allocator, sizeof(bqws_socket) + opts->user_size); if (!ws) return NULL; memset(ws, 0, sizeof(bqws_socket)); ws->magic = BQWS_SOCKET_MAGIC; ws->is_server = is_server; ws->allocator = opts->allocator; ws->user_io = opts->io; ws->limits = opts->limits; ws->recv_partial_messages = opts->recv_partial_messages; ws->recv_control_messages = opts->recv_control_messages; ws->mask_server = opts->mask_server; ws->message_fn = opts->message_fn; ws->message_user = opts->message_user; ws->peek_fn = opts->peek_fn; ws->peek_user = opts->peek_user; ws->log_fn = opts->log_fn; ws->log_user = opts->log_user; ws->log_send = opts->log_send; ws->log_recv = opts->log_recv; ws->error_fn = opts->error_fn; ws->error_user = opts->error_user; ws->send_message_fn = opts->send_message_fn; ws->send_message_user = opts->send_message_user; ws->user_size = opts->user_size; ws_expand_default_limits(&ws->limits); bqws_mutex_init(&ws->err_mutex); bqws_mutex_init(&ws->state.mutex); bqws_mutex_init(&ws->io.mutex); bqws_mutex_init(&ws->alloc.mutex); bqws_mutex_init(&ws->partial.mutex); msg_init_queue(ws, &ws->recv_queue); msg_init_queue(ws, &ws->recv_partial_queue); msg_init_queue(ws, &ws->send_queue); if (opts->ping_interval) { ws->ping_interval = opts->ping_interval; } else { ws->ping_interval = is_server ? 20000 : 10000; } ws->connect_timeout = opts->connect_timeout ? opts->connect_timeout : 10000; ws->close_timeout = opts->close_timeout ? opts->close_timeout : 5000; ws->ping_response_timeout = opts->ping_response_timeout ? opts->ping_response_timeout : 4 * ws->ping_interval; bqws_assert(ws->ping_interval > 0); if (ws->ping_interval != SIZE_MAX || ws->connect_timeout != SIZE_MAX) { bqws_timestamp ts = bqws_get_timestamp(); ws->io.start_connect_ts = ts; ws->io.last_write_ts = ts; ws->io.last_read_ts = ts; ws->io.last_ping_ts = ts; } // Copy or zero-init user data if (opts->user_size > 0) { if (opts->user_data) { memcpy(ws->user_data, opts->user_data, opts->user_size); } else { memset(ws->user_data, 0, opts->user_size); } } if (opts->name) ws->name = ws_copy_str(ws, opts->name); if (opts->skip_handshake) { ws_log(ws, "State: OPEN (skip handhake)"); ws->state.state = BQWS_STATE_OPEN; } else { ws_log(ws, "State: CONNECTING"); ws->state.state = BQWS_STATE_CONNECTING; } ws->io.mask_random_state = (uint32_t)(uintptr_t)ws ^ (uint32_t)time(NULL); ws->io.mask_random_stream = (uint32_t)bqws_get_timestamp() | 1u; if (ws->err) { bqws_free_socket(ws); return NULL; } return ws; } // -- API bqws_socket *bqws_new_client(const bqws_opts *opts, const bqws_client_opts *client_opts) { bqws_socket *ws = ws_new_socket(opts, false); if (!ws) return NULL; // Setup client handshake immediately if the socket is not open already if (ws->state.state == BQWS_STATE_CONNECTING) { bqws_client_opts null_opts; if (!client_opts) { memset(&null_opts, 0, sizeof(null_opts)); client_opts = &null_opts; } bqws_mutex_lock(&ws->io.mutex); hs_client_handshake(ws, client_opts); bqws_mutex_unlock(&ws->io.mutex); // Notify IO that there's a client handshake to send if (ws->user_io.notify_fn) { ws->user_io.notify_fn(ws->user_io.user, ws); } } return ws; } bqws_socket *bqws_new_server(const bqws_opts *opts, const bqws_server_opts *server_opts) { bqws_socket *ws = ws_new_socket(opts, true); if (!ws) return NULL; { bqws_server_opts null_opts; if (!server_opts) { memset(&null_opts, 0, sizeof(null_opts)); server_opts = &null_opts; } ws->verify_fn = server_opts->verify_fn; ws->verify_user = server_opts->verify_user; // Setup automatic verify filter if needed if (server_opts->verify_filter) { bqws_client_opts *filter = server_opts->verify_filter; size_t text_size = 0; text_size += filter->path ? strlen(filter->path) + 1 : 0; text_size += filter->host ? strlen(filter->host) + 1 : 0; text_size += filter->origin ? strlen(filter->origin) + 1 : 0; for (size_t i = 0; i < filter->num_protocols; i++) { bqws_assert(filter->protocols[i] && *filter->protocols[i]); text_size += strlen(filter->protocols[i]) + 1; } bqws_verify_filter *copy = (bqws_verify_filter*)ws_alloc(ws, sizeof(bqws_verify_filter) + text_size); if (!copy) { bqws_free_socket(ws); return NULL; } memset(copy, 0, sizeof(bqws_verify_filter)); copy->magic = BQWS_FILTER_MAGIC; copy->text_size = text_size; size_t offset = 0; copy->path = verify_filter_str(copy, &offset, filter->path); copy->host = verify_filter_str(copy, &offset, filter->host); copy->origin = verify_filter_str(copy, &offset, filter->origin); copy->num_protocols = filter->num_protocols; for (size_t i = 0; i < filter->num_protocols; i++) { copy->protocols[i] = verify_filter_str(copy, &offset, filter->protocols[i]); } bqws_assert(offset == text_size); copy->verify_fn = ws->verify_fn; copy->verify_user = ws->verify_user; ws->verify_fn = &bqws_internal_filter_verify; ws->verify_user = copy; } } return ws; } void bqws_close(bqws_socket *ws, bqws_close_reason reason, const void *data, size_t size) { if (ws->err) return; bqws_mutex_lock(&ws->state.mutex); bqws_assert(ws && ws->magic == BQWS_SOCKET_MAGIC); bqws_assert(size == 0 || data); if (ws->state.close_to_send || ws->state.state >= BQWS_STATE_CLOSING) { bqws_mutex_unlock(&ws->state.mutex); return; } bqws_msg_imp *imp = msg_alloc(ws, BQWS_MSG_CONTROL_CLOSE, size + 2); if (imp) { imp->msg.data[0] = (uint8_t)(reason >> 8); imp->msg.data[1] = (uint8_t)(reason & 0xff); memcpy(imp->msg.data + 2, data, size); ws->state.close_to_send = imp; ws->state.start_closing_ts = bqws_get_timestamp(); ws->state.state = BQWS_STATE_CLOSING; ws_log(ws, "State: CLOSING (user close)"); } bqws_mutex_unlock(&ws->state.mutex); } void bqws_queue_close(bqws_socket *ws, bqws_close_reason reason, const void *data, size_t size) { if (ws->err) return; bqws_mutex_lock(&ws->state.mutex); bqws_assert(ws && ws->magic == BQWS_SOCKET_MAGIC); bqws_assert(size == 0 || data); if (ws->state.close_to_send || ws->state.state >= BQWS_STATE_CLOSING) { bqws_mutex_unlock(&ws->state.mutex); return; } bqws_msg_imp *imp = msg_alloc(ws, BQWS_MSG_CONTROL_CLOSE, size + 2); if (imp) { imp->msg.data[0] = (uint8_t)(reason >> 8); imp->msg.data[1] = (uint8_t)(reason & 0xff); memcpy(imp->msg.data + 2, data, size); ws_enqueue_send(ws, imp); } bqws_mutex_unlock(&ws->state.mutex); } void bqws_free_socket(bqws_socket *ws) { if (!ws) return; bqws_assert(ws && ws->magic == BQWS_SOCKET_MAGIC); ws_log(ws, "Freed"); if (ws->user_io.close_fn && !ws->state.io_closed) { ws->user_io.close_fn(ws->user_io.user, ws); } // Free everything, as the socket may have errored it can // be in almost any state // Pending messages msg_free_queue(ws, &ws->recv_queue); msg_free_queue(ws, &ws->recv_partial_queue); msg_free_queue(ws, &ws->send_queue); if (ws->state.pong_to_send) msg_free_owned(ws, ws->state.pong_to_send); if (ws->state.close_to_send) msg_free_owned(ws, ws->state.close_to_send); // Read/write buffers ws_free(ws, ws->io.handshake.data, ws->io.handshake.capacity); ws_free(ws, ws->io.handshake_overflow.data, ws->io.handshake_overflow.capacity); if (ws->io.recv_buf.msg) msg_free_owned(ws, ws->io.recv_buf.msg); if (ws->io.send_buf.msg) msg_free_owned(ws, ws->io.send_buf.msg); if (ws->partial.next_partial_to_send) msg_free_owned(ws, ws->partial.next_partial_to_send); // Misc buffers if (ws->io.opts_from_client) ws_free(ws, ws->io.opts_from_client, sizeof(bqws_client_opts)); // String copies ws_free_str(ws, ws->state.chosen_protocol); ws_free_str(ws, ws->name); // Verify filter copy if (ws->verify_fn == &bqws_internal_filter_verify) { bqws_verify_filter *filter = (bqws_verify_filter*)ws->verify_user; bqws_assert(filter->magic == BQWS_FILTER_MAGIC); filter->magic = BQWS_DELETED_MAGIC; ws_free(ws, filter, sizeof(bqws_verify_filter) + filter->text_size); } // Mutexes bqws_mutex_free(&ws->err_mutex); bqws_mutex_free(&ws->state.mutex); bqws_mutex_free(&ws->io.mutex); bqws_mutex_free(&ws->alloc.mutex); bqws_mutex_free(&ws->partial.mutex); bqws_assert(ws->alloc.memory_used == 0); ws->magic = BQWS_DELETED_MAGIC; bqws_allocator at = ws->allocator; bqws_allocator_free(&at, ws, sizeof(bqws_socket) + ws->user_size); } bqws_client_opts *bqws_server_get_client_opts(bqws_socket *ws) { bqws_assert(ws && ws->magic == BQWS_SOCKET_MAGIC); bqws_assert(ws->is_server); // no-mutex(state): There's an inherent race condition with multiple accepts bqws_assert(ws->state.state == BQWS_STATE_CONNECTING); bqws_mutex_lock(&ws->io.mutex); bqws_client_opts *opts = ws->io.opts_from_client; bqws_mutex_unlock(&ws->io.mutex); return opts; } void bqws_server_accept(bqws_socket *ws, const char *protocol) { bqws_assert(ws && ws->magic == BQWS_SOCKET_MAGIC); bqws_assert(ws->is_server); // no-mutex(state): There's an inherent race condition with multiple accepts bqws_assert(ws->state.state == BQWS_STATE_CONNECTING); if (ws->err) return; // Use emtpy string to differentiate from not set if (!protocol) protocol = ""; bqws_mutex_lock(&ws->state.mutex); if (!ws->state.chosen_protocol) { ws->state.chosen_protocol = ws_copy_str(ws, protocol); } bqws_mutex_unlock(&ws->state.mutex); } void bqws_server_reject(bqws_socket *ws) { bqws_assert(ws && ws->magic == BQWS_SOCKET_MAGIC); bqws_assert(ws->is_server); ws_fail(ws, BQWS_ERR_SERVER_REJECT); } bqws_state bqws_get_state(const bqws_socket *ws) { bqws_assert(ws && ws->magic == BQWS_SOCKET_MAGIC); // no-mutex(state): We can always underestimate the state bqws_state state = ws->state.state, override_state = ws->state.override_state; if (override_state > state) state = override_state; return state; } bqws_error bqws_get_error(const bqws_socket *ws) { bqws_assert(ws && ws->magic == BQWS_SOCKET_MAGIC); return ws->err; } bool bqws_is_connecting(const bqws_socket *ws) { bqws_assert(ws && ws->magic == BQWS_SOCKET_MAGIC); // no-mutex(state): We can always underestimate the state bqws_state state = ws->state.state, override_state = ws->state.override_state; if (override_state > state) state = override_state; return state == BQWS_STATE_CONNECTING; } bool bqws_is_closed(const bqws_socket *ws) { bqws_assert(ws && ws->magic == BQWS_SOCKET_MAGIC); // no-mutex(state): We can always underestimate the state bqws_state state = ws->state.state, override_state = ws->state.override_state; if (override_state > state) state = override_state; return state == BQWS_STATE_CLOSED; } size_t bqws_get_memory_used(const bqws_socket *ws) { bqws_assert(ws && ws->magic == BQWS_SOCKET_MAGIC); // no-mutex(alloc): This doesn't need to be accurate return ws->alloc.memory_used; } bool bqws_is_server(const bqws_socket *ws) { bqws_assert(ws && ws->magic == BQWS_SOCKET_MAGIC); return ws->is_server; } void *bqws_user_data(const bqws_socket *ws) { bqws_assert(ws && ws->magic == BQWS_SOCKET_MAGIC); return (void*)ws->user_data; } size_t bqws_user_data_size(const bqws_socket *ws) { bqws_assert(ws && ws->magic == BQWS_SOCKET_MAGIC); return ws->user_size; } const char *bqws_get_name(const bqws_socket *ws) { bqws_assert(ws && ws->magic == BQWS_SOCKET_MAGIC); return ws->name; } bqws_stats bqws_get_stats(const bqws_socket *ws) { bqws_assert(ws && ws->magic == BQWS_SOCKET_MAGIC); bqws_stats stats; msg_queue_get_stats((bqws_msg_queue*)&ws->recv_queue, &stats.recv); msg_queue_get_stats((bqws_msg_queue*)&ws->send_queue, &stats.send); return stats; } void *bqws_get_io_user(const bqws_socket *ws) { bqws_assert(ws && ws->magic == BQWS_SOCKET_MAGIC); return ws->user_io.user; } bool bqws_get_io_closed(const bqws_socket *ws) { bqws_assert(ws && ws->magic == BQWS_SOCKET_MAGIC); // no-mutex(state): This can be inaccurate return ws->state.io_closed; } bqws_limits bqws_get_limits(const bqws_socket *ws) { bqws_assert(ws && ws->magic == BQWS_SOCKET_MAGIC); return ws->limits; } void bqws_set_limits(bqws_socket *ws, const bqws_limits *limits) { bqws_assert(ws && ws->magic == BQWS_SOCKET_MAGIC); bqws_assert(limits); bqws_limits copy = *limits; ws_expand_default_limits(©); ws->limits = copy; } bqws_close_reason bqws_get_peer_close_reason(const bqws_socket *ws) { bqws_assert(ws && ws->magic == BQWS_SOCKET_MAGIC); bqws_mutex_lock((bqws_mutex*)&ws->state.mutex); bqws_close_reason reason = ws->state.peer_reason; bqws_mutex_unlock((bqws_mutex*)&ws->state.mutex); return reason; } bqws_error bqws_get_peer_error(const bqws_socket *ws) { bqws_assert(ws && ws->magic == BQWS_SOCKET_MAGIC); bqws_mutex_lock((bqws_mutex*)&ws->state.mutex); bqws_error err = ws->state.peer_err; bqws_mutex_unlock((bqws_mutex*)&ws->state.mutex); return err; } const char *bqws_get_protocol(const bqws_socket *ws) { bqws_assert(ws && ws->magic == BQWS_SOCKET_MAGIC); // TODO: Cache this pointer outside of the state mutex bqws_mutex_lock((bqws_mutex*)&ws->state.mutex); const char *protocol = ws->state.chosen_protocol; bqws_mutex_unlock((bqws_mutex*)&ws->state.mutex); return protocol; } bqws_msg *bqws_recv(bqws_socket *ws) { bqws_assert(ws && ws->magic == BQWS_SOCKET_MAGIC); if (ws->err) return NULL; // Messages are re-combined in `recv_queue` if // `recv_partial_messages` is disabled. bqws_msg_imp *imp = msg_dequeue(&ws->recv_queue); if (!imp) return NULL; bqws_assert(imp->magic == BQWS_MSG_MAGIC); msg_release_ownership(ws, imp); return &imp->msg; } void bqws_free_msg(bqws_msg *msg) { if (!msg) return; bqws_msg_imp *imp = msg_imp(msg); bqws_assert(imp->magic == BQWS_MSG_MAGIC); bqws_assert(imp->owner == NULL); imp->magic = BQWS_DELETED_MAGIC; bqws_allocator at = imp->allocator; bqws_allocator_free(&at, imp, msg_alloc_size(msg)); } void bqws_send(bqws_socket *ws, bqws_msg_type type, const void *data, size_t size) { bqws_assert(ws && ws->magic == BQWS_SOCKET_MAGIC); bqws_assert((type & BQWS_MSG_PARTIAL_BIT) == 0); if (ws->err) return; bqws_assert(size == 0 || data); bqws_msg_imp *imp = msg_alloc(ws, type, size); if (!imp) return; memcpy(imp->msg.data, data, size); ws_enqueue_send(ws, imp); } void bqws_send_binary(bqws_socket *ws, const void *data, size_t size) { bqws_send(ws, BQWS_MSG_BINARY, data, size); } void bqws_send_text(bqws_socket *ws, const char *str) { bqws_assert(str); bqws_send(ws, BQWS_MSG_TEXT, str, strlen(str)); } void bqws_send_text_len(bqws_socket *ws, const void *str, size_t len) { bqws_send(ws, BQWS_MSG_TEXT, str, len); } bqws_msg *bqws_allocate_msg(bqws_socket *ws, bqws_msg_type type, size_t size) { bqws_assert(ws && ws->magic == BQWS_SOCKET_MAGIC); if (ws->err) return NULL; bqws_msg_imp *imp = msg_alloc(ws, type, size); if (!imp) return NULL; msg_release_ownership(ws, imp); return &imp->msg; } void bqws_send_msg(bqws_socket *ws, bqws_msg *msg) { bqws_assert(ws && ws->magic == BQWS_SOCKET_MAGIC); bqws_assert(msg && (msg->type == BQWS_MSG_TEXT || msg->type == BQWS_MSG_BINARY)); bqws_assert(msg->size <= msg->capacity); bqws_msg_imp *imp = msg_imp(msg); bqws_assert(imp->magic == BQWS_MSG_MAGIC); if (ws->err) return; if (!msg_acquire_ownership(ws, imp)) return; ws_enqueue_send(ws, imp); } void bqws_send_begin(bqws_socket *ws, bqws_msg_type type) { bqws_assert(ws && ws->magic == BQWS_SOCKET_MAGIC); bqws_assert(type == BQWS_MSG_TEXT || type == BQWS_MSG_BINARY); if (ws->err) return; bqws_mutex_lock(&ws->partial.mutex); bqws_assert(ws->partial.send_partial_type == BQWS_MSG_INVALID); bqws_assert(ws->partial.next_partial_to_send == NULL); ws->partial.send_partial_type = type; bqws_mutex_unlock(&ws->partial.mutex); } void bqws_send_append(bqws_socket *ws, const void *data, size_t size) { bqws_assert(ws && ws->magic == BQWS_SOCKET_MAGIC); if (ws->err) return; bqws_mutex_lock(&ws->partial.mutex); bqws_assert(ws->partial.send_partial_type != BQWS_MSG_INVALID); if (ws->partial.next_partial_to_send) { bqws_assert(ws->partial.next_partial_to_send->magic == BQWS_MSG_MAGIC); ws_enqueue_send(ws, ws->partial.next_partial_to_send); } bqws_msg_type partial_type = (bqws_msg_type)(ws->partial.send_partial_type | BQWS_MSG_PARTIAL_BIT); bqws_msg_imp *imp = msg_alloc(ws, partial_type, size); if (imp) { memcpy(imp->msg.data, data, size); ws->partial.next_partial_to_send = imp; } bqws_mutex_unlock(&ws->partial.mutex); } void bqws_send_append_str(bqws_socket *ws, const char *str) { bqws_send_append(ws, str, strlen(str)); } void bqws_send_append_msg(bqws_socket *ws, bqws_msg *msg) { bqws_assert(ws && ws->magic == BQWS_SOCKET_MAGIC); bqws_assert(msg->type == BQWS_MSG_TEXT || msg->type == BQWS_MSG_BINARY); if (ws->err) return; bqws_mutex_lock(&ws->partial.mutex); bqws_assert(ws->partial.send_partial_type != BQWS_MSG_INVALID); bqws_assert((ws->partial.send_partial_type & BQWS_MSG_TYPE_MASK) == msg->type); if (ws->partial.next_partial_to_send) { bqws_assert(ws->partial.next_partial_to_send->magic == BQWS_MSG_MAGIC); ws_enqueue_send(ws, ws->partial.next_partial_to_send); } bqws_msg_imp *imp = msg_imp(msg); if (!msg_acquire_ownership(ws, imp)) return; msg->type = (bqws_msg_type)(ws->partial.send_partial_type | BQWS_MSG_PARTIAL_BIT); ws->partial.next_partial_to_send = imp; bqws_mutex_unlock(&ws->partial.mutex); } void bqws_send_finish(bqws_socket *ws) { bqws_assert(ws && ws->magic == BQWS_SOCKET_MAGIC); if (ws->err) return; bqws_mutex_lock(&ws->partial.mutex); bqws_assert(ws->partial.send_partial_type != BQWS_MSG_INVALID); if (ws->partial.next_partial_to_send) { bqws_assert(ws->partial.next_partial_to_send->magic == BQWS_MSG_MAGIC); bqws_msg_type type = ws->partial.next_partial_to_send->msg.type; ws->partial.next_partial_to_send->msg.type = (bqws_msg_type)(type | BQWS_MSG_FINAL_BIT); ws_enqueue_send(ws, ws->partial.next_partial_to_send); ws->partial.next_partial_to_send = NULL; } ws->partial.send_partial_type = BQWS_MSG_INVALID; bqws_mutex_unlock(&ws->partial.mutex); } void bqws_send_ping(bqws_socket *ws, const void *data, size_t size) { bqws_send(ws, BQWS_MSG_CONTROL_PING, data, size); } void bqws_send_pong(bqws_socket *ws, const void *data, size_t size) { bqws_send(ws, BQWS_MSG_CONTROL_PONG, data, size); } void bqws_update(bqws_socket *ws) { bqws_assert(ws && ws->magic == BQWS_SOCKET_MAGIC); bqws_update_state(ws); bqws_update_io(ws); } void bqws_update_state(bqws_socket *ws) { bqws_assert(ws && ws->magic == BQWS_SOCKET_MAGIC); bqws_mutex_lock(&ws->state.mutex); bqws_state state = ws->state.state; char *protocol = ws->state.chosen_protocol; bqws_timestamp start_closing_ts = ws->state.start_closing_ts; bqws_mutex_unlock(&ws->state.mutex); bqws_mutex_lock(&ws->io.mutex); if (state == BQWS_STATE_CONNECTING) { // If we're connecting but haven't set a protocol and the user // has provided a verify function or filter run it here. if (ws->io.client_handshake_done && !protocol && ws->verify_fn) { bqws_assert(ws->is_server); bqws_assert(ws->io.opts_from_client); ws->verify_fn(ws->verify_user, ws, ws->io.opts_from_client); } // Connect timeout if (ws->connect_timeout != SIZE_MAX && state == BQWS_STATE_CONNECTING) { bqws_timestamp time = bqws_get_timestamp(); size_t delta = bqws_timestamp_delta_to_ms(ws->io.start_connect_ts, time); if (delta > ws->connect_timeout) { ws_fail(ws, BQWS_ERR_CONNECT_TIMEOUT); bqws_mutex_lock(&ws->state.mutex); ws_close(ws); bqws_mutex_unlock(&ws->state.mutex); } } } else if (state == BQWS_STATE_OPEN) { // Automatic PING send if (ws->ping_interval != SIZE_MAX) { bqws_timestamp time = bqws_get_timestamp(); size_t delta_read = bqws_timestamp_delta_to_ms(ws->io.last_read_ts, time); size_t delta_ping = bqws_timestamp_delta_to_ms(ws->io.last_ping_ts, time); size_t delta_write = bqws_timestamp_delta_to_ms(ws->io.last_write_ts, time); size_t delta = delta_read >= delta_write ? delta_read : delta_write; size_t delta_from_ping = delta <= delta_ping ? delta : delta_ping; if (delta_from_ping > ws->ping_interval) { ws->io.last_ping_ts = time; // Maybe send PONG only? bqws_send_ping(ws, NULL, 0); } if (ws->ping_response_timeout != SIZE_MAX) { if (delta >= ws->ping_response_timeout) { ws_fail(ws, BQWS_ERR_PING_TIMEOUT); bqws_mutex_lock(&ws->state.mutex); ws_close(ws); bqws_mutex_unlock(&ws->state.mutex); } } } } else if (state == BQWS_STATE_CLOSING) { // Close timeout if (ws->close_timeout != SIZE_MAX) { bqws_timestamp time = bqws_get_timestamp(); size_t delta = bqws_timestamp_delta_to_ms(start_closing_ts, time); if (delta > ws->close_timeout) { ws_fail(ws, BQWS_ERR_CLOSE_TIMEOUT); bqws_mutex_lock(&ws->state.mutex); ws_close(ws); bqws_mutex_unlock(&ws->state.mutex); } } } bqws_mutex_unlock(&ws->io.mutex); } void bqws_update_io(bqws_socket *ws) { bqws_update_io_write(ws); bqws_update_io_read(ws); } void bqws_update_io_read(bqws_socket *ws) { bqws_assert(ws && ws->magic == BQWS_SOCKET_MAGIC); bool do_read = true; bqws_mutex_lock(&ws->io.mutex); // If read and write are stopped close the IO bqws_mutex_lock(&ws->state.mutex); if (!ws->state.io_started) { if (ws->user_io.init_fn) { ws->user_io.init_fn(ws->user_io.user, ws); } ws->state.io_started = true; } if (ws->state.stop_read && ws->state.stop_write) { if (ws->user_io.close_fn && !ws->state.io_closed) { ws->user_io.close_fn(ws->user_io.user, ws); } ws->state.io_closed = true; } do_read = !ws->state.stop_read; bqws_mutex_unlock(&ws->state.mutex); // TODO: Throttle reads if (do_read) { if (ws->user_io.recv_fn) { while (ws_read_data(ws, ws->user_io.recv_fn, ws->user_io.user)) { // Keep reading as long as there is space } } } bqws_mutex_unlock(&ws->io.mutex); } void bqws_update_io_write(bqws_socket *ws) { bqws_assert(ws && ws->magic == BQWS_SOCKET_MAGIC); bool do_write = true; bqws_mutex_lock(&ws->io.mutex); // If read and write are stopped close the IO bqws_mutex_lock(&ws->state.mutex); if (!ws->state.io_started) { if (ws->user_io.init_fn) { ws->user_io.init_fn(ws->user_io.user, ws); } ws->state.io_started = true; } if (ws->state.stop_read && ws->state.stop_write) { if (ws->user_io.close_fn && !ws->state.io_closed) { ws->user_io.close_fn(ws->user_io.user, ws); } ws->state.io_closed = true; } do_write = !ws->state.stop_write; bqws_mutex_unlock(&ws->state.mutex); if (do_write) { if (ws->user_io.send_fn) { while (ws_write_data(ws, ws->user_io.send_fn, ws->user_io.user)) { // Keep writing as long as there is space } } // Re-check if we should stop write if the socket got closed bqws_mutex_lock(&ws->state.mutex); do_write = !ws->state.stop_write; bqws_mutex_unlock(&ws->state.mutex); if (ws->user_io.flush_fn && do_write) { if (!ws->user_io.flush_fn(ws->user_io.user, ws)) { ws_fail(ws, BQWS_ERR_IO_WRITE); } } } bqws_mutex_unlock(&ws->io.mutex); } size_t bqws_read_from(bqws_socket *ws, const void *data, size_t size) { bqws_assert(ws && ws->magic == BQWS_SOCKET_MAGIC); bqws_assert(!ws->user_io.recv_fn); bqws_mutex_lock(&ws->io.mutex); bqws_mem_stream s; s.ptr = (char*)data; s.end = s.ptr + size; while (ws_read_data(ws, &mem_stream_recv, &s)) { // Keep reading as long as there is space } bqws_mutex_unlock(&ws->io.mutex); return s.ptr - (char*)data; } size_t bqws_write_to(bqws_socket *ws, void *data, size_t size) { bqws_assert(ws && ws->magic == BQWS_SOCKET_MAGIC); bqws_assert(!ws->user_io.send_fn); bqws_mutex_lock(&ws->io.mutex); bqws_mem_stream s; s.ptr = (char*)data; s.end = s.ptr + size; while (ws_write_data(ws, &mem_stream_send, &s)) { // Keep writing as long as there is space } if (ws->user_io.flush_fn) { if (!ws->user_io.flush_fn(ws->user_io.user, ws)) { ws_fail(ws, BQWS_ERR_IO_WRITE); } } bqws_mutex_unlock(&ws->io.mutex); return s.ptr - (char*)data; } void bqws_direct_push_msg(bqws_socket *ws, bqws_msg *msg) { bqws_assert(ws && ws->magic == BQWS_SOCKET_MAGIC); bqws_assert(msg && msg->size <= msg->capacity); bqws_msg_imp *imp = msg_imp(msg); bqws_assert(imp->magic == BQWS_MSG_MAGIC); if (ws->err) return; if (!msg_acquire_ownership(ws, imp)) return; if (ws->log_recv) { ws_log2(ws, "Direct recv: ", bqws_msg_type_str(msg->type)); } ws_enqueue_recv(ws, imp); } void bqws_direct_set_override_state(bqws_socket *ws, bqws_state state) { bqws_assert(ws && ws->magic == BQWS_SOCKET_MAGIC); ws_log2(ws, "Override state: ", bqws_state_str(state)); bqws_mutex_lock(&ws->state.mutex); ws->state.override_state = state; bqws_mutex_unlock(&ws->state.mutex); } void bqws_direct_fail(bqws_socket *ws, bqws_error err) { ws_fail(ws, err); } bool bqws_parse_url(bqws_url *url, const char *str) { // Format [wss://][host.example.com][:1234][/path] const char *scheme = str; const char *scheme_end = strstr(scheme, "://"); const char *host = scheme_end ? scheme_end + 3 : scheme; const char *port_start = host; if (*host == '[') { // Skip IPv6 address port_start = strstr(host, "]"); if (!port_start) return false; } const char *port = strstr(port_start, ":"); const char *path = strstr(port_start, "/"); if (!path) path = port_start + strlen(port_start); if (port && port > path) port = NULL; const char *host_end = port ? port : path; size_t scheme_len = scheme_end ? scheme_end - scheme : 0; size_t host_len = host_end - host; if (scheme_len >= sizeof(url->scheme)) return false; if (host_len >= sizeof(url->host)) return false; bool secure = scheme_len == 3 && !memcmp(scheme, "wss", 3); int port_num; if (port) { char *port_end; port_num = (int)strtol(port + 1, &port_end, 10); if (port_end != path) return false; if (port_num < 0 || port_num > UINT16_MAX) return false; port_num = (uint16_t)port_num; } else { port_num = secure ? 443 : 80; } // vv No fails below, no writes above ^^ url->port = (uint16_t)port_num; memcpy(url->scheme, scheme, scheme_len); url->scheme[scheme_len] = '\0'; memcpy(url->host, host, host_len); url->host[host_len] = '\0'; url->path = *path ? path : "/"; url->secure = secure; return true; } const char *bqws_error_str(bqws_error error) { switch (error) { case BQWS_OK: return "OK"; case BQWS_ERR_UNKNOWN: return "UNKNOWN"; case BQWS_ERR_SERVER_REJECT: return "SERVER_REJECT"; case BQWS_ERR_LIMIT_MAX_MEMORY_USED: return "LIMIT_MAX_MEMORY_USED"; case BQWS_ERR_LIMIT_MAX_RECV_MSG_SIZE: return "LIMIT_MAX_RECV_MSG_SIZE"; case BQWS_ERR_LIMIT_MAX_HANDSHAKE_SIZE: return "LIMIT_MAX_HANDSHAKE_SIZE"; case BQWS_ERR_LIMIT_MAX_PARTIAL_MESSAGE_PARTS: return "LIMIT_MAX_PARTIAL_MESSAGE_PARTS"; case BQWS_ERR_CONNECT_TIMEOUT: return "BQWS_ERR_CONNECT_TIMEOUT"; case BQWS_ERR_PING_TIMEOUT: return "BQWS_ERR_PING_TIMEOUT"; case BQWS_ERR_CLOSE_TIMEOUT: return "BQWS_ERR_CLOSE_TIMEOUT"; case BQWS_ERR_ALLOCATOR: return "ALLOCATOR"; case BQWS_ERR_BAD_CONTINUATION: return "BAD_CONTINUATION"; case BQWS_ERR_UNFINISHED_PARTIAL: return "UNFINISHED_PARTIAL"; case BQWS_ERR_PARTIAL_CONTROL: return "PARTIAL_CONTROL"; case BQWS_ERR_BAD_OPCODE: return "BAD_OPCODE"; case BQWS_ERR_RESERVED_BIT: return "RESERVED_BIT"; case BQWS_ERR_IO_WRITE: return "IO_WRITE"; case BQWS_ERR_IO_READ: return "IO_READ"; case BQWS_ERR_BAD_HANDSHAKE: return "BAD_HANDSHAKE"; case BQWS_ERR_UNSUPPORTED_VERSION: return "UNSUPPORTED_VERSION"; case BQWS_ERR_TOO_MANY_HEADERS: return "TOO_MANY_HEADERS"; case BQWS_ERR_TOO_MANY_PROTOCOLS: return "TOO_MANY_PROTOCOLS"; case BQWS_ERR_HEADER_KEY_TOO_LONG: return "HEADER_KEY_TOO_LONG"; case BQWS_ERR_HEADER_BAD_ACCEPT: return "HEADER_BAD_ACCEPT"; case BQWS_ERR_HEADER_PARSE: return "HEADER_PARSE"; default: return "(unknown)"; } } const char *bqws_msg_type_str(bqws_msg_type type) { switch (type) { case BQWS_MSG_TEXT: return "TEXT"; case BQWS_MSG_BINARY: return "BINARY"; case BQWS_MSG_PARTIAL_TEXT: return "PARTIAL_TEXT"; case BQWS_MSG_PARTIAL_BINARY: return "PARTIAL_BINARY"; case BQWS_MSG_FINAL_TEXT: return "FINAL_TEXT"; case BQWS_MSG_FINAL_BINARY: return "FINAL_BINARY"; case BQWS_MSG_CONTROL_CLOSE: return "CONTROL_CLOSE"; case BQWS_MSG_CONTROL_PING: return "CONTROL_PING"; case BQWS_MSG_CONTROL_PONG: return "CONTROL_PONG"; default: return "(unknown)"; } } const char *bqws_state_str(bqws_state state) { switch (state) { case BQWS_STATE_INVALID: return "INVALID"; case BQWS_STATE_CONNECTING: return "CONNECTING"; case BQWS_STATE_OPEN: return "OPEN"; case BQWS_STATE_CLOSING: return "CLOSING"; case BQWS_STATE_CLOSED: return "CLOSED"; default: return "(unknown)"; } } // TODO: Add a define for this /* ================ sha1.c ================ */ /* SHA-1 in C By Steve Reid 100% Public Domain Test Vectors (from FIPS PUB 180-1) "abc" A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" 84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1 A million repetitions of "a" 34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F */ typedef struct { uint32_t state[5]; uint32_t count[2]; unsigned char buffer[64]; } SHA1_CTX; #define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits)))) /* blk0() and blk() perform the initial expand. */ /* I got the idea of expanding during the round function from SSLeay */ #define blk0(i) (block->l[i] = (rol(block->l[i],24)&0xFF00FF00) \ |(rol(block->l[i],8)&0x00FF00FF)) #define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15] \ ^block->l[(i+2)&15]^block->l[i&15],1)) /* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */ #define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(i)+0x5A827999+rol(v,5);w=rol(w,30); #define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30); #define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30); #define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30); #define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30); /* Hash a single 512-bit block. This is the core of the algorithm. */ static void SHA1Transform(uint32_t state[5], const void *buffer) { uint32_t a, b, c, d, e; typedef union { unsigned char c[64]; uint32_t l[16]; } CHAR64LONG16; CHAR64LONG16 block_buf, *block = &block_buf; memcpy(block, buffer, 64); /* Copy context->state[] to working vars */ a = state[0]; b = state[1]; c = state[2]; d = state[3]; e = state[4]; /* 4 rounds of 20 operations each. Loop unrolled. */ R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3); R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7); R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11); R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15); R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19); R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23); R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27); R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31); R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35); R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39); R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43); R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47); R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51); R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55); R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59); R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63); R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67); R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71); R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75); R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79); /* Add the working vars back into context.state[] */ state[0] += a; state[1] += b; state[2] += c; state[3] += d; state[4] += e; } /* SHA1Init - Initialize new context */ static void SHA1Init(SHA1_CTX* context) { /* SHA1 initialization constants */ context->state[0] = 0x67452301; context->state[1] = 0xEFCDAB89; context->state[2] = 0x98BADCFE; context->state[3] = 0x10325476; context->state[4] = 0xC3D2E1F0; context->count[0] = context->count[1] = 0; } /* Run your data through this. */ static void SHA1Update(SHA1_CTX* context, const void* data, uint32_t len) { uint32_t i, j; const char *bytes = (const char *)data; j = context->count[0]; if ((context->count[0] += len << 3) < j) context->count[1]++; context->count[1] += (len>>29); j = (j >> 3) & 63; if ((j + len) > 63) { memcpy(&context->buffer[j], data, (i = 64-j)); SHA1Transform(context->state, context->buffer); for ( ; i + 63 < len; i += 64) { SHA1Transform(context->state, &bytes[i]); } j = 0; } else i = 0; memcpy(&context->buffer[j], &bytes[i], len - i); } /* Add padding and return the message digest. */ static void SHA1Final(unsigned char digest[20], SHA1_CTX* context) { unsigned i; unsigned char finalcount[8]; unsigned char c; for (i = 0; i < 8; i++) { finalcount[i] = (unsigned char)((context->count[(i >= 4 ? 0 : 1)] >> ((3-(i & 3)) * 8) ) & 255); /* Endian independent */ } c = 0200; SHA1Update(context, &c, 1); while ((context->count[0] & 504) != 448) { c = 0000; SHA1Update(context, &c, 1); } SHA1Update(context, finalcount, 8); /* Should cause a SHA1Transform() */ for (i = 0; i < 20; i++) { digest[i] = (unsigned char) ((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255); } } /* ================ end of sha1.c ================ */ static void bqws_sha1(uint8_t digest[20], const void *data, size_t size) { SHA1_CTX ctx; SHA1Init(&ctx); SHA1Update(&ctx, data, (uint32_t)size); SHA1Final((unsigned char*)digest, &ctx); } #endif // BQ_WEBSOCKET_IMPLEMENTATION //------------------------------------------------------------------------------ #ifdef BQ_PLATFORM_IMPLEMENTATION #include #include #include // -- Generic #ifndef BQWS_PT_USE_OPENSSL #define BQWS_PT_USE_OPENSSL 0 #endif #if defined(_WIN32) && !defined(__MINGW32__) //<@r-lyeh __declspec(thread) static bqws_pt_error t_err; #else static __thread bqws_pt_error t_err; #endif #define BQWS_PT_DELETED_MAGIC 0xbdbdbdbd #define BQWS_PT_IO_MAGIC 0x77737074 #define BQWS_PT_EM_MAGIC 0x7773656d #define BQWS_PT_SERVER_MAGIC 0x77737376 #ifndef bqws_assert #include #define bqws_assert(x) assert(x) #endif static void pt_fail_pt(const char *func, bqws_pt_error_code code) { t_err.function = func; t_err.type = BQWS_PT_ERRTYPE_PT; t_err.data = code; } #if defined(__EMSCRIPTEN__) #include #if defined(__EMSCRIPTEN_PTHREADS__) #include typedef pthread_mutex_t pt_em_mutex; #define pt_em_mutex_init(m) pthread_mutex_init((m), NULL) #define pt_em_mutex_free(m) pthread_mutex_destroy((m)) #define pt_em_mutex_lock(m) pthread_mutex_lock((m)) #define pt_em_mutex_unlock(m) pthread_mutex_unlock((m)) #else typedef int pt_em_mutex; #define pt_em_mutex_init(m) (void)0 #define pt_em_mutex_free(m) (void)0 #define pt_em_mutex_lock(m) (void)0 #define pt_em_mutex_unlock(m) (void)0 #endif typedef struct pt_em_partial { struct pt_em_partial *next; size_t size; char data[]; } pt_em_partial; typedef struct { uint32_t magic; int handle; pt_em_partial *partial_first; pt_em_partial *partial_last; size_t partial_size; pt_em_mutex ws_mutex; bqws_socket *ws; // Packed zero separated url + protocols size_t num_protocols; size_t connect_data_size; char *connect_data; bqws_allocator allocator; } pt_em_socket; static void pt_sleep_ms(uint32_t ms) { emscripten_sleep(ms); } static void pt_em_free(pt_em_socket *em) { bqws_assert(em->magic == BQWS_PT_EM_MAGIC); em->magic = BQWS_PT_DELETED_MAGIC; bqws_allocator allocator = em->allocator; bqws_allocator_free(&allocator, em, sizeof(pt_em_socket)); } static bool pt_em_try_lock(pt_em_socket *em) { pt_em_mutex_lock(&em->ws_mutex); if (!em->ws) { // TODO: Free the em context here pt_em_mutex_unlock(&em->ws_mutex); pt_em_free(em); return false; } return true; } static void pt_em_unlock(pt_em_socket *em) { pt_em_mutex_unlock(&em->ws_mutex); } EMSCRIPTEN_KEEPALIVE void *pt_em_msg_alloc(pt_em_socket *em, size_t size, int type) { if (!pt_em_try_lock(em)) return em; /* HACK: return the handle on close! */ bqws_msg *msg = bqws_allocate_msg(em->ws, (bqws_msg_type)type, size); pt_em_unlock(em); if (!msg) return NULL; return msg->data; } EMSCRIPTEN_KEEPALIVE int pt_em_msg_recv(pt_em_socket *em, void *ptr) { if (!pt_em_try_lock(em)) return 1; bqws_msg *msg = (bqws_msg*)((char*)ptr - offsetof(bqws_msg, data)); bqws_direct_push_msg(em->ws, msg); pt_em_unlock(em); return 0; } EMSCRIPTEN_KEEPALIVE int pt_em_on_open(pt_em_socket *em) { if (!pt_em_try_lock(em)) return 1; bqws_direct_set_override_state(em->ws, BQWS_STATE_OPEN); pt_em_unlock(em); return 0; } EMSCRIPTEN_KEEPALIVE int pt_em_on_close(pt_em_socket *em) { if (!pt_em_try_lock(em)) return 1; bqws_direct_set_override_state(em->ws, BQWS_STATE_CLOSED); em->handle = -1; pt_em_unlock(em); return 0; } EMSCRIPTEN_KEEPALIVE int pt_em_is_closed(pt_em_socket *em) { return em->ws == NULL; } EM_JS(int, pt_em_connect_websocket, (pt_em_socket *em, const char *url, const char **protocols, size_t num_protocols), { var webSocket = typeof WebSocket !== "undefined" ? WebSocket : require("ws"); var url_str = UTF8ToString(url); var protocols_str = []; for (var i = 0; i < num_protocols; i++) { var protocol = HEAPU32[(protocols >> 2) + i]; protocols_str.push(UTF8ToString(protocol)); } var ws = new webSocket(url_str, protocols_str); ws.binaryType = "arraybuffer"; if (Module.g_bqws_pt_sockets === undefined) { Module.g_bqws_pt_sockets = { sockets: [null], em_sockets: [null], free_list: [], }; } var handle = null; if (Module.g_bqws_pt_sockets.free_list.length > 0) { handle = Module.g_bqws_pt_sockets.free_list.pop(); Module.g_bqws_pt_sockets.sockets[handle] = ws; Module.g_bqws_pt_sockets.em_sockets[handle] = em; } else { handle = Module.g_bqws_pt_sockets.sockets.length; Module.g_bqws_pt_sockets.sockets.push(ws); Module.g_bqws_pt_sockets.em_sockets.push(em); } var interval = setInterval(function() { if (_pt_em_is_closed(em)) { ws.close(); } }, 1000); ws.onopen = function(e) { if (Module.g_bqws_pt_sockets.sockets[handle] !== ws) return; if (_pt_em_on_open(em)) { ws.close(); Module.g_bqws_pt_sockets.sockets[handle] = null; Module.g_bqws_pt_sockets.em_sockets[handle] = null; Module.g_bqws_pt_sockets.free_list.push(handle); } }; ws.onclose = function(e) { if (interval !== null) { clearInterval(interval); interval = null; } if (Module.g_bqws_pt_sockets.sockets[handle] !== ws) return; _pt_em_on_close(em); Module.g_bqws_pt_sockets.sockets[handle] = null; Module.g_bqws_pt_sockets.em_sockets[handle] = null; Module.g_bqws_pt_sockets.free_list.push(handle); }; ws.onerror = function(e) { if (Module.g_bqws_pt_sockets.sockets[handle] !== ws) return; _pt_em_on_close(em); ws.close(); Module.g_bqws_pt_sockets.sockets[handle] = null; Module.g_bqws_pt_sockets.em_sockets[handle] = null; Module.g_bqws_pt_sockets.free_list.push(handle); }; ws.onmessage = function(e) { if (Module.g_bqws_pt_sockets.sockets[handle] !== ws) return; var deleted = 0; if (typeof e.data === "string") { var size = lengthBytesUTF8(e.data); var ptr = _pt_em_msg_alloc(em, size, 1); if (ptr == em) { // HACK: pt_em_msg_alloc() returns `em` if deleted deleted = 1; } else if (ptr != 0) { stringToUTF8(e.data, ptr, size + 1); deleted = _pt_em_msg_recv(em, ptr); } } else { var size = e.data.byteLength; var ptr = _pt_em_msg_alloc(em, size, 2); if (ptr == em) { // HACK: pt_em_msg_alloc() returns `em` if deleted deleted = 1; } else if (ptr != 0) { HEAPU8.set(new Uint8Array(e.data), ptr); deleted = _pt_em_msg_recv(em, ptr); } } if (deleted != 0) { ws.close(); Module.g_bqws_pt_sockets.sockets[handle] = null; Module.g_bqws_pt_sockets.em_sockets[handle] = null; Module.g_bqws_pt_sockets.free_list.push(handle); } }; return handle; }); EM_JS(int, pt_em_websocket_send_binary, (int handle, pt_em_socket *em, const char *data, size_t size), { if (!Module.g_bqws_pt_sockets || em !== Module.g_bqws_pt_sockets.em_sockets[handle]) { console.error("WebSocket '0x" + em.toString(16) + "' not found in thread: Make sure to call bqws_update() only from a single thread per socket in WASM!"); return 0; } var ws = Module.g_bqws_pt_sockets.sockets[handle]; if (ws.readyState == 0) { return 0; } else if (ws.readyState != 1) { return 1; } #if defined(__EMSCRIPTEN_PTHREADS__) ws.send(new Uint8Array(HEAPU8.subarray(data, data + size))); #else ws.send(HEAPU8.subarray(data, data + size)); #endif return 1; }); EM_JS(int, pt_em_websocket_send_text, (int handle, pt_em_socket *em, const char *data, size_t size), { if (!Module.g_bqws_pt_sockets || em !== Module.g_bqws_pt_sockets.em_sockets[handle]) { console.error("WebSocket '0x" + em.toString(16) + "' not found in thread: Make sure to call bqws_update() only from a single thread per socket in WASM!"); return 0; } var ws = Module.g_bqws_pt_sockets.sockets[handle]; if (ws.readyState == 0) { return 0; } else if (ws.readyState != 1) { return 1; } var str = UTF8ToString(data, size); ws.send(str); return 1; }); EM_JS(void, pt_em_websocket_close, (int handle, pt_em_socket *em, int code), { if (!Module.g_bqws_pt_sockets || em !== Module.g_bqws_pt_sockets.em_sockets[handle]) { console.error("WebSocket '0x" + em.toString(16) + "' not found in thread: Make sure to call bqws_update() only from a single thread per socket in WASM!"); return 0; } var ws = Module.g_bqws_pt_sockets.sockets[handle]; if (ws.readyState >= 2) { return 0; } ws.close(code); return 1; }); EM_JS(int, pt_em_try_free_websocket, (int handle, pt_em_socket *em), { if (!Module.g_bqws_pt_sockets || em !== Module.g_bqws_pt_sockets.em_sockets[handle]) { return 0; } var ws = Module.g_bqws_pt_sockets.sockets[handle]; if (ws.readyState < 2) ws.close(); Module.g_bqws_pt_sockets.sockets[handle] = null; Module.g_bqws_pt_sockets.em_sockets[handle] = null; Module.g_bqws_pt_sockets.free_list.push(handle); return 1; }); static bool pt_send_message(void *user, bqws_socket *ws, bqws_msg *msg) { pt_em_socket *em = (pt_em_socket*)user; void *partial_buf = NULL; bqws_msg_type type = msg->type; size_t size = msg->size; void *data = msg->data; if (type & BQWS_MSG_PARTIAL_BIT) { pt_em_partial *part = (pt_em_partial*)bqws_allocator_alloc(&em->allocator, sizeof(pt_em_partial) + size); part->next = NULL; part->size = size; memcpy(part->data, data, size); em->partial_size += size; if (em->partial_last) { em->partial_last->next = part; em->partial_last = part; } else { em->partial_first = part; em->partial_last = part; } if (type & BQWS_MSG_FINAL_BIT) { char *ptr = (char*)bqws_allocator_alloc(&em->allocator, em->partial_size); partial_buf = ptr; data = ptr; size = em->partial_size; type = (bqws_msg_type)(type & BQWS_MSG_TYPE_MASK); pt_em_partial *next = em->partial_first; while (next) { pt_em_partial *part = next; next = part->next; memcpy(ptr, part->data, part->size); ptr += part->size; bqws_allocator_free(&em->allocator, part, sizeof(pt_em_partial) + part->size); } } else { bqws_free_msg(msg); return true; } } bool ret = true; if (type == BQWS_MSG_BINARY) { ret = (bool)pt_em_websocket_send_binary(em->handle, em, (const char *)data, size); } else if (type == BQWS_MSG_TEXT) { ret = (bool)pt_em_websocket_send_text(em->handle, em, (const char *)data, size); } else if (type == BQWS_MSG_CONTROL_CLOSE) { unsigned code = 1000; if (msg->size >= 2) { code = (unsigned)(uint8_t)msg->data[0] << 8 | (unsigned)(uint8_t)msg->data[1]; } pt_em_websocket_close(em->handle, em, (int)code); ret = true; } else { // Don't send control messages } if (partial_buf) { bqws_allocator_free(&em->allocator, partial_buf, size); if (ret) { em->partial_first = NULL; em->partial_last = NULL; em->partial_size = 0; } } if (ret) { bqws_free_msg(msg); } return ret; } static bool pt_init(const bqws_pt_init_opts *opts) { return true; } static void pt_shutdown() { } static size_t pt_io_send(void *user, bqws_socket *ws, const void *data, size_t size) { bqws_assert(0 && "Should never get here"); return SIZE_MAX; } static void pt_io_init(void *user, bqws_socket *ws) { pt_em_socket *em = (pt_em_socket*)user; if (!pt_em_try_lock(em)) return; const char *protocols[BQWS_MAX_PROTOCOLS]; const char *url_str = em->connect_data; const char *ptr = url_str; for (size_t i = 0; i < em->num_protocols; i++) { ptr += strlen(ptr); protocols[i] = ptr; } int handle = pt_em_connect_websocket(em, url_str, protocols, em->num_protocols); em->handle = handle; bqws_allocator_free(&em->allocator, em->connect_data, em->connect_data_size); em->connect_data = NULL; pt_em_unlock(em); } static void pt_io_close(void *user, bqws_socket *ws) { pt_em_socket *em = (pt_em_socket*)user; pt_em_mutex_lock(&em->ws_mutex); pt_em_partial *next = em->partial_first; while (next) { pt_em_partial *part = next; next = part->next; bqws_allocator_free(&em->allocator, part, sizeof(pt_em_partial) + part->size); } if (em->connect_data) { bqws_allocator_free(&em->allocator, em->connect_data, em->connect_data_size); } bool do_free = false; if (em->handle >= 0) { if (pt_em_try_free_websocket(em->handle, em)) { do_free = true; } } else { do_free = true; } em->ws = NULL; pt_em_mutex_unlock(&em->ws_mutex); if (do_free) { pt_em_free(em); } } static bqws_socket *pt_connect(const bqws_url *url, const bqws_pt_connect_opts *pt_opts, const bqws_opts *opts, const bqws_client_opts *client_opts) { char url_str[2048]; int len = snprintf(url_str, sizeof(url_str), "%s://%s:%d%s", url->scheme, url->host, url->port, url->path); if (len >= sizeof(url_str)) return NULL; bqws_opts opt; if (opts) { opt = *opts; } else { memset(&opt, 0, sizeof(opt)); } bqws_client_opts copt; if (client_opts) { copt = *client_opts; } else { memset(&copt, 0, sizeof(copt)); } opt.ping_interval = SIZE_MAX; opt.ping_response_timeout = SIZE_MAX; opt.close_timeout = SIZE_MAX; pt_em_socket *em = (pt_em_socket*)bqws_allocator_alloc(&opts->allocator, sizeof(pt_em_socket)); memset(em, 0, sizeof(pt_em_socket)); em->magic = BQWS_PT_EM_MAGIC; em->allocator = opts->allocator; opt.send_message_fn = &pt_send_message; opt.send_message_user = em; opt.io.user = em; opt.io.init_fn = &pt_io_init; opt.io.send_fn = &pt_io_send; opt.io.close_fn = &pt_io_close; opt.skip_handshake = true; bqws_socket *ws = bqws_new_client(&opt, &copt); if (!ws) { bqws_allocator_free(&opts->allocator, em, sizeof(pt_em_socket)); return NULL; } bqws_direct_set_override_state(ws, BQWS_STATE_CONNECTING); // Retain connect data and connect at the first update size_t url_size = strlen(url_str) + 1; size_t protocol_size[BQWS_MAX_PROTOCOLS]; size_t connect_data_size = url_size + 1; for (size_t i = 0; i < copt.num_protocols; i++) { protocol_size[i] = strlen(copt.protocols[i]) + 1; connect_data_size += protocol_size[i]; } em->connect_data = (char*)bqws_allocator_alloc(&em->allocator, connect_data_size); em->num_protocols = copt.num_protocols; { char *ptr = em->connect_data; memcpy(ptr, url_str, url_size); ptr += url_size; for (size_t i = 0; i < copt.num_protocols; i++) { memcpy(ptr, copt.protocols[i], protocol_size[i]); ptr += protocol_size[i]; } } pt_em_mutex_init(&em->ws_mutex); em->ws = ws; em->handle = -1; return ws; } static bqws_pt_server *pt_listen(const bqws_pt_listen_opts *opts) { pt_fail_pt("pt_listen()", BQWS_PT_ERR_NO_SERVER_SUPPORT); return NULL; } static bqws_socket *pt_accept(bqws_pt_server *sv, const bqws_opts *opts, const bqws_server_opts *server_opts) { return NULL; } static void pt_free_server(bqws_pt_server *sv) { } static bqws_pt_address pt_get_address(const bqws_socket *ws) { pt_em_socket *em = (pt_em_socket*)bqws_get_io_user(ws); bqws_assert(em && em->magic == BQWS_PT_EM_MAGIC); bqws_pt_address addr = { BQWS_PT_ADDRESS_WEBSOCKET }; memcpy(addr.address, &em->handle, sizeof(int)); return addr; } #elif (defined(_WIN32) || defined (__unix__) || (defined (__APPLE__) && defined (__MACH__))) // -- Shared for Windows and POSIX static const uint8_t ipv4_mapped_ipv6_prefix[] = { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff, }; static void addr_parse_ipv4(bqws_pt_address *dst, const void *addr, uint16_t port_native) { dst->port = port_native; dst->type = BQWS_PT_ADDRESS_IPV4; memcpy(dst->address, addr, 4); } static void addr_parse_ipv6(bqws_pt_address *dst, const void *addr, uint16_t port_native) { dst->port = port_native; if (!memcmp(addr, ipv4_mapped_ipv6_prefix, sizeof(ipv4_mapped_ipv6_prefix))) { dst->type = BQWS_PT_ADDRESS_IPV4; memcpy(dst->address, (const char*)addr + 12, 4); } else { dst->type = BQWS_PT_ADDRESS_IPV6; memcpy(dst->address, addr, 16); } } #if defined(_WIN32) // -- OS: Windows #include #include #define _WIN32_LEAN_AND_MEAN #include #pragma comment(lib, "ws2_32") //< @r-lyeh removed .lib (tcc support) typedef SOCKET os_socket; #define OS_BAD_SOCKET INVALID_SOCKET static void pt_fail_wsa(const char *func) { t_err.function = func; t_err.type = BQWS_PT_ERRTYPE_WSA; t_err.data = (uint32_t)WSAGetLastError(); } static void pt_sleep_ms(uint32_t ms) { Sleep((DWORD)ms); } static bool os_init(const bqws_pt_init_opts *opts) { WSADATA data; int res = WSAStartup(MAKEWORD(2,2), &data); if (res != 0) { pt_fail_wsa("WSAStartup()"); return false; } return true; } static void os_shutdown() { WSACleanup(); } static bool os_imp_config_data_socket(os_socket s) { int res; // Set the socket to be non-blocking u_long nb_flag = 1; res = ioctlsocket(s, FIONBIO, &nb_flag); if (res != 0) { pt_fail_wsa("ioctlsocket(FIONBIO)"); return false; } // Disable Nagle's algorithm to make writes immediate BOOL nd_flag = 1; res = setsockopt(s, IPPROTO_TCP, TCP_NODELAY, (const char *)&nd_flag, sizeof(nd_flag)); if (res != 0) { pt_fail_wsa("setsockopt(TCP_NODELAY)"); return false; } return true; } static os_socket os_imp_try_connect(ADDRINFOW *info, int family, ADDRINFOW **used) { for (; info; info = info->ai_next) { if (info->ai_family != family) continue; SOCKET s = socket(family, SOCK_STREAM, IPPROTO_TCP); if (s == INVALID_SOCKET) { pt_fail_wsa("socket()"); return s; } int res = connect(s, info->ai_addr, (int)info->ai_addrlen); if (res == 0) { *used = info; return s; } pt_fail_wsa("connect()"); closesocket(s); } return INVALID_SOCKET; } static void os_imp_parse_address(bqws_pt_address *dst, struct sockaddr *addr) { if (addr->sa_family == AF_INET) { struct sockaddr_in *sa = (struct sockaddr_in*)addr; addr_parse_ipv4(dst, &sa->sin_addr, ntohs(sa->sin_port)); } else if (addr->sa_family == AF_INET6) { struct sockaddr_in6 *sa = (struct sockaddr_in6*)addr; addr_parse_ipv6(dst, &sa->sin6_addr, ntohs(sa->sin6_port)); } } static os_socket os_socket_connect(const bqws_url *url, bqws_pt_address *addr) { wchar_t whost[256]; char service[32]; wchar_t wservice[32]; snprintf(service, sizeof(service), "%u", url->port); int res = MultiByteToWideChar(CP_UTF8, 0, service, -1, wservice, sizeof(wservice) / sizeof(*wservice)); if (res == 0) return OS_BAD_SOCKET; res = MultiByteToWideChar(CP_UTF8, 0, url->host, -1, whost, sizeof(whost) / sizeof(*whost)); if (res == 0) return OS_BAD_SOCKET; ADDRINFOW hints = { 0 }; hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = IPPROTO_TCP; ADDRINFOW *info; res = GetAddrInfoW(whost, wservice, &hints, &info); if (res != 0) { pt_fail_wsa("GetAddrInfoW()"); return INVALID_SOCKET; } ADDRINFOW *used_info = NULL; SOCKET s = os_imp_try_connect(info, AF_INET6, &used_info); if (s == INVALID_SOCKET) { s = os_imp_try_connect(info, AF_INET, &used_info); } if (s != INVALID_SOCKET) { os_imp_parse_address(addr, used_info->ai_addr); } FreeAddrInfoW(info); if (!os_imp_config_data_socket(s)) { closesocket(s); return INVALID_SOCKET; } return s; } static os_socket os_socket_listen(const bqws_pt_listen_opts *pt_opts) { os_socket s = OS_BAD_SOCKET; int res; do { s = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP); if (s == INVALID_SOCKET) { pt_fail_wsa("socket()"); break; } // Make sure the socket supports both IPv4 and IPv6 DWORD ipv6_flag = 0; res = setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, (const char*)&ipv6_flag, sizeof(ipv6_flag)); if (res != 0) { pt_fail_wsa("setsockopt(IPPROTO_IPV6)"); break; } // Set the socket to be non-blocking u_long nb_flag = 1; res = ioctlsocket(s, FIONBIO, &nb_flag); if (res != 0) { pt_fail_wsa("ioctlsocket(FIONBIO)"); break; } struct sockaddr_in6 addr = { 0 }; addr.sin6_family = AF_INET6; addr.sin6_addr = in6addr_any; addr.sin6_port = htons(pt_opts->port); res = bind(s, (struct sockaddr*)&addr, sizeof(addr)); if (res != 0) { pt_fail_wsa("bind()"); break; } res = listen(s, (int)pt_opts->backlog); if (res != 0) { pt_fail_wsa("listen()"); break; } return s; } while (false); if (s != INVALID_SOCKET) closesocket(s); return INVALID_SOCKET; } static os_socket os_socket_accept(os_socket listen_s, bqws_pt_address *addr) { struct sockaddr_in6 addr_in; int addr_len = sizeof(addr_in); SOCKET s = accept(listen_s, (struct sockaddr*)&addr_in, &addr_len); if (s == INVALID_SOCKET) return INVALID_SOCKET; os_imp_parse_address(addr, (struct sockaddr*)&addr_in); if (!os_imp_config_data_socket(s)) { closesocket(s); return INVALID_SOCKET; } return s; } static size_t os_socket_recv(os_socket s, void *data, size_t size) { if (size > INT_MAX) size = INT_MAX; int res = recv(s, (char*)data, (int)size, 0); if (res < 0) { int err = WSAGetLastError(); if (err == WSAEWOULDBLOCK) return 0; t_err.function = "recv()"; t_err.type = BQWS_PT_ERRTYPE_WSA; t_err.data = err; return SIZE_MAX; } return (size_t)res; } static size_t os_socket_send(os_socket s, const void *data, size_t size) { if (size > INT_MAX) size = INT_MAX; int res = send(s, (const char*)data, (int)size, 0); if (res < 0) { int err = WSAGetLastError(); if (err == WSAEWOULDBLOCK) return 0; t_err.function = "send()"; t_err.type = BQWS_PT_ERRTYPE_WSA; t_err.data = err; return SIZE_MAX; } return (size_t)res; } static void os_socket_close(os_socket s) { shutdown(s, SD_BOTH); closesocket(s); } #else #include #include #include #include #include #include #include // TODO: Guard this with macros? #if 1 #include #define BQWS_HAS_GAI_STRERROR #endif typedef int os_socket; #define OS_BAD_SOCKET -1 static void pt_fail_posix(const char *func) { t_err.function = func; t_err.type = BQWS_PT_ERRTYPE_POSIX; t_err.data = errno; } static void pt_sleep_ms(uint32_t ms) { struct timespec ts; ts.tv_sec = ms / 1000; ts.tv_nsec = (ms % 1000) * 1000000; while (nanosleep(&ts, &ts)) { } } static bool os_init(const bqws_pt_init_opts *opts) { return true; } static void os_shutdown() { } static bool os_imp_config_data_socket(os_socket s) { int res; // Set the socket to be non-blocking int nb_flag = 1; res = ioctl(s, FIONBIO, &nb_flag); if (res != 0) { pt_fail_posix("ioctl(FIONBIO)"); return false; } // Disable Nagle's algorithm to make writes immediate int nd_flag = 1; res = setsockopt(s, IPPROTO_TCP, TCP_NODELAY, &nd_flag, sizeof(nd_flag)); if (res != 0) { pt_fail_posix("setsockopt(TCP_NODELAY)"); return false; } return true; } static os_socket os_imp_try_connect(struct addrinfo *info, int family, struct addrinfo **used) { for (; info; info = info->ai_next) { if (info->ai_family != family) continue; int s = socket(family, SOCK_STREAM, IPPROTO_TCP); if (s == -1) { pt_fail_posix("socket()"); return s; } int res = connect(s, info->ai_addr, (int)info->ai_addrlen); if (res == 0) { *used = info; return s; } pt_fail_posix("connect()"); close(s); } return -1; } static void os_imp_parse_address(bqws_pt_address *dst, struct sockaddr *addr) { if (addr->sa_family == AF_INET) { struct sockaddr_in *sa = (struct sockaddr_in*)addr; addr_parse_ipv4(dst, &sa->sin_addr, ntohs(sa->sin_port)); } else if (addr->sa_family == AF_INET6) { struct sockaddr_in6 *sa = (struct sockaddr_in6*)addr; addr_parse_ipv6(dst, &sa->sin6_addr, ntohs(sa->sin6_port)); } } static os_socket os_socket_connect(const bqws_url *url, bqws_pt_address *addr) { char service[64]; snprintf(service, sizeof(service), "%d", (int)url->port); struct addrinfo hints = { 0 }; hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = IPPROTO_TCP; struct addrinfo *info; int res = getaddrinfo(url->host, service, &hints, &info); if (res != 0) { t_err.function = "getaddrinfo()"; t_err.type = BQWS_PT_ERRTYPE_GETADDRINFO; t_err.data = res; return -1; } struct addrinfo *used_info = NULL; int s = os_imp_try_connect(info, AF_INET6, &used_info); if (s == -1) { s = os_imp_try_connect(info, AF_INET, &used_info); } if (s != -1) { os_imp_parse_address(addr, used_info->ai_addr); } freeaddrinfo(info); if (!os_imp_config_data_socket(s)) { close(s); return -1; } return s; } static os_socket os_socket_listen(const bqws_pt_listen_opts *pt_opts) { os_socket s = -1; int res; do { s = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP); if (s == -1) { pt_fail_posix("socket()"); break; } // Make sure the socket supports both IPv4 and IPv6 int ipv6_flag = 0; res = setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, &ipv6_flag, sizeof(ipv6_flag)); if (res != 0) { pt_fail_posix("setsockopt(IPPROTO_IPV6)"); break; } if (pt_opts->reuse_port) { int reuse_flag = 1; setsockopt(s, SOL_SOCKET, SO_REUSEPORT, &reuse_flag, sizeof(reuse_flag)); } // Set the socket to be non-blocking int nb_flag = 1; res = ioctl(s, FIONBIO, &nb_flag); if (res != 0) { pt_fail_posix("ioctl(FIONBIO)"); break; } struct sockaddr_in6 addr = { 0 }; addr.sin6_family = AF_INET6; addr.sin6_addr = in6addr_any; addr.sin6_port = htons(pt_opts->port); res = bind(s, (struct sockaddr*)&addr, sizeof(addr)); if (res != 0) { pt_fail_posix("bind()"); break; } res = listen(s, (int)pt_opts->backlog); if (res != 0) { pt_fail_posix("listen()"); break; } return s; } while (false); if (s != -1) close(s); return -1; } static os_socket os_socket_accept(os_socket listen_s, bqws_pt_address *addr) { struct sockaddr_in6 addr_in; socklen_t addr_len = sizeof(addr_in); int s = accept(listen_s, (struct sockaddr*)&addr_in, &addr_len); if (s == -1) return -1; os_imp_parse_address(addr, (struct sockaddr*)&addr_in); if (!os_imp_config_data_socket(s)) { close(s); return -1; } return s; } static size_t os_socket_recv(os_socket s, void *data, size_t size) { int res = read(s, data, size); if (res < 0) { int err = errno; if (err == EAGAIN || err == EWOULDBLOCK) return 0; t_err.function = "read()"; t_err.type = BQWS_PT_ERRTYPE_POSIX; t_err.data = err; return SIZE_MAX; } return (size_t)res; } static size_t os_socket_send(os_socket s, const void *data, size_t size) { int res = write(s, data, size); if (res < 0) { int err = errno; if (err == EAGAIN || err == EWOULDBLOCK) return 0; t_err.function = "write()"; t_err.type = BQWS_PT_ERRTYPE_POSIX; t_err.data = err; return SIZE_MAX; } return (size_t)res; } static void os_socket_close(os_socket s) { shutdown(s, SHUT_RDWR); close(s); } #endif // -- TLS #if BQWS_PT_USE_OPENSSL #include #include #define BQWS_PT_HAS_OPENSSL typedef struct { bool connected; SSL *ssl; } pt_tls; typedef struct { SSL_CTX *ctx; } pt_tls_server; typedef struct { SSL_CTX *client_ctx; } pt_tls_global; static pt_tls_global g_tls; static void pt_fail_ssl(const char *func) { t_err.function = func; t_err.type = BQWS_PT_ERRTYPE_OPENSSL; t_err.data = ERR_get_error(); } static bool tls_init(const bqws_pt_init_opts *opts) { int res; SSL_library_init(); g_tls.client_ctx = SSL_CTX_new(SSLv23_client_method()); if (!g_tls.client_ctx) { pt_fail_ssl("SSL_CTX_new()"); return false; } if (opts->ca_filename) { res = SSL_CTX_load_verify_locations(g_tls.client_ctx, opts->ca_filename, NULL); if (!res) { pt_fail_ssl("SSL_CTX_load_verify_locations()"); return false; } } long flags = SSL_OP_NO_COMPRESSION; SSL_CTX_set_options(g_tls.client_ctx, flags); long mode = SSL_MODE_ENABLE_PARTIAL_WRITE; SSL_CTX_set_mode(g_tls.client_ctx, mode); return true; } static void tls_shutdown() { SSL_CTX_free(g_tls.client_ctx); } static bool tls_init_client(pt_tls *tls, os_socket s, const bqws_pt_connect_opts *pt_opts, const bqws_opts *opts, const bqws_client_opts *client_opts) { tls->ssl = SSL_new(g_tls.client_ctx); if (!tls->ssl) return false; BIO *bio = BIO_new_socket((int)s, 0); if (!bio) return false; // SSL_free() will free the BIO internally SSL_set_bio(tls->ssl, bio, bio); if (!pt_opts->insecure_no_verify_host) { const char *host = client_opts->host; if (!host || !*host) return false; X509_VERIFY_PARAM *param = SSL_get0_param(tls->ssl); X509_VERIFY_PARAM_set_hostflags(param, /* X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS */ 0x4); X509_VERIFY_PARAM_set1_host(param, host, 0); SSL_set_verify(tls->ssl, SSL_VERIFY_PEER, NULL); } return true; } static bool tls_init_server(pt_tls_server *tls, const bqws_pt_listen_opts *pt_opts) { tls->ctx = SSL_CTX_new(SSLv23_server_method()); if (!tls->ctx) { pt_fail_ssl("SSL_CTX_new()"); return false; } int res; if (pt_opts->certificate_file) { res = SSL_CTX_use_certificate_file(tls->ctx, pt_opts->certificate_file, SSL_FILETYPE_PEM); if (!res) { pt_fail_ssl("SSL_CTX_use_certificate_file()"); return false; } } if (pt_opts->private_key_file) { res = SSL_CTX_use_PrivateKey_file(tls->ctx, pt_opts->private_key_file, SSL_FILETYPE_PEM); if (!res) { pt_fail_ssl("SSL_CTX_use_PrivateKey_file()"); return false; } } long flags = SSL_OP_NO_COMPRESSION; SSL_CTX_set_options(tls->ctx, flags); long mode = SSL_MODE_ENABLE_PARTIAL_WRITE; SSL_CTX_set_mode(tls->ctx, mode); return true; } static void tls_free_server(pt_tls_server *tls) { if (tls->ctx) { SSL_CTX_free(tls->ctx); } } static bool tls_init_accept(pt_tls *tls, pt_tls_server *tls_server, os_socket s) { tls->ssl = SSL_new(tls_server->ctx); if (!tls->ssl) return false; BIO *bio = BIO_new_socket((int)s, 0); if (!bio) return false; // SSL_free() will free the BIO internally SSL_set_bio(tls->ssl, bio, bio); return true; } static void tls_free(pt_tls *tls) { if (tls->ssl) SSL_free(tls->ssl); } static bool tls_imp_connect(pt_tls *tls) { int res = SSL_connect(tls->ssl); if (res <= 0) { int err = SSL_get_error(tls->ssl, res); if (err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE) { // Did not fail, just did not connect yet return true; } else { pt_fail_ssl("SSL_connect()"); return false; } } tls->connected = true; return true; } static size_t tls_send(pt_tls *tls, const void *data, size_t size) { if (!tls->connected) { if (!tls_imp_connect(tls)) return SIZE_MAX; if (!tls->connected) return 0; } if (size > INT_MAX) size = INT_MAX; int res = SSL_write(tls->ssl, data, (int)size); if (res <= 0) { int err = SSL_get_error(tls->ssl, res); if (err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE) { return 0; } else { pt_fail_ssl("SSL_write()"); return SIZE_MAX; } } return (size_t)res; } static size_t tls_recv(pt_tls *tls, void *data, size_t size) { if (!tls->connected) { if (!tls_imp_connect(tls)) return SIZE_MAX; if (!tls->connected) return 0; } if (size > INT_MAX) size = INT_MAX; int res = SSL_read(tls->ssl, data, (int)size); if (res <= 0) { int err = SSL_get_error(tls->ssl, res); if (err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE) { return 0; } else { pt_fail_ssl("SSL_read()"); return SIZE_MAX; } } return (size_t)res; } #else typedef struct { int unused; } pt_tls; typedef struct { int unused; } pt_tls_server; static bool tls_init(const bqws_pt_init_opts *opts) { return true; } static void tls_shutdown() { } static bool tls_init_client(pt_tls *tls, os_socket s, const bqws_pt_connect_opts *pt_opts, const bqws_opts *opts, const bqws_client_opts *client_opts) { pt_fail_pt("tls_init_client()", BQWS_PT_ERR_NO_TLS); return false; } static bool tls_init_server(pt_tls_server *tls, const bqws_pt_listen_opts *pt_opts) { pt_fail_pt("tls_init_client()", BQWS_PT_ERR_NO_TLS); return false; } static void tls_free_server(pt_tls_server *tls) { } static bool tls_init_accept(pt_tls *tls, pt_tls_server *tls_server, os_socket s) { bqws_assert(0 && "Should never get here"); return false; } static void tls_free(pt_tls *tls) { } static size_t tls_send(pt_tls *tls, const void *data, size_t size) { bqws_assert(0 && "Should never get here"); return SIZE_MAX; } static size_t tls_recv(pt_tls *tls, void *data, size_t size) { bqws_assert(0 && "Should never get here"); return SIZE_MAX; } #endif #if defined(__APPLE__) // -- CF socket implementation #include #include #include #include #include #include #include #include #include typedef struct { bool enabled; bool has_address; bool set_nonblocking; CFWriteStreamRef write; CFReadStreamRef read; } pt_cf; static void cf_free(pt_cf *cf) { if (cf->read) CFRelease(cf->read); if (cf->write) CFRelease(cf->write); } static size_t cf_send(pt_cf *cf, const void *data, size_t size) { if (size == 0) return 0; switch (CFWriteStreamGetStatus(cf->write)) { case kCFStreamStatusOpening: return 0; case kCFStreamStatusError: case kCFStreamStatusClosed: return SIZE_MAX; default: if (!CFWriteStreamCanAcceptBytes(cf->write)) return 0; } if (!cf->set_nonblocking) { cf->set_nonblocking = true; CFDataRef socket_data = (CFDataRef)CFWriteStreamCopyProperty(cf->write, kCFStreamPropertySocketNativeHandle); if (socket_data) { CFSocketNativeHandle s = -1; CFDataGetBytes(socket_data, CFRangeMake(0, sizeof(CFSocketNativeHandle)), (UInt8*)&s); if (s >= 0) { int nd_flag = 1; setsockopt(s, IPPROTO_TCP, TCP_NODELAY, &nd_flag, sizeof(nd_flag)); } CFRelease(socket_data); } } CFIndex res = CFWriteStreamWrite(cf->write, (const UInt8*)data, size); if (res < 0) return SIZE_MAX; return (size_t)res; } static size_t cf_recv(pt_cf *cf, void *data, size_t max_size) { if (max_size == 0) return 0; switch (CFReadStreamGetStatus(cf->read)) { case kCFStreamStatusOpening: return 0; case kCFStreamStatusError: case kCFStreamStatusClosed: return SIZE_MAX; default: if (!CFReadStreamHasBytesAvailable(cf->read)) return 0; } CFIndex res = CFReadStreamRead(cf->read, (UInt8*)data, (CFIndex)max_size); if (res < 0) return SIZE_MAX; return (size_t)res; } static bool cf_connect(const bqws_url *url, pt_cf *cf) { CFAllocatorRef ator = kCFAllocatorDefault; do { memset(cf, 0, sizeof(pt_cf)); CFStringRef host_ref = CFStringCreateWithCString(ator, url->host, kCFStringEncodingUTF8); CFStreamCreatePairWithSocketToHost(ator, host_ref, url->port, &cf->read, &cf->write); CFRelease(host_ref); if (!cf->read || !cf->write) { pt_fail_pt("CFStreamCreatePairWithSocketToHost()", BQWS_PT_ERR_OUT_OF_MEMORY); break; } if (url->secure) { CFStringRef keys[] = { kCFStreamPropertySocketSecurityLevel }; CFStringRef values[] = { kCFStreamSocketSecurityLevelTLSv1 }; CFDictionaryRef dict = CFDictionaryCreate(ator, (const void**)keys, (const void**)values, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); CFWriteStreamSetProperty(cf->write, kCFStreamPropertySSLSettings, dict); CFReadStreamSetProperty(cf->read, kCFStreamPropertySSLSettings, dict); CFRelease(dict); } CFWriteStreamOpen(cf->write); CFReadStreamOpen(cf->read); cf->enabled = true; return true; } while (false); if (cf) cf_free(cf); return false; } static void cf_get_address(pt_cf *cf, bqws_pt_address *address) { if (!cf->has_address) { CFDataRef socket_data = (CFDataRef)CFWriteStreamCopyProperty(cf->write, kCFStreamPropertySocketNativeHandle); if (socket_data) { CFSocketNativeHandle s = -1; CFDataGetBytes(socket_data, CFRangeMake(0, sizeof(CFSocketNativeHandle)), (UInt8*)&s); if (s >= 0) { struct sockaddr_in6 addr; socklen_t addr_len = sizeof(addr); if (getsockname(s, (struct sockaddr*)&addr, &addr_len) == 0 && addr_len >= sizeof(struct sockaddr_in)) { os_imp_parse_address(address, (struct sockaddr*)&addr); } } CFRelease(socket_data); } cf->has_address = true; } } #define cf_enabled(cf) ((cf)->enabled) #else typedef struct { int unused; } pt_cf; static void cf_free(pt_cf *cf) { } static size_t cf_send(pt_cf *cf, const void *data, size_t size) { return SIZE_MAX; } static size_t cf_recv(pt_cf *cf, void *data, size_t max_size) { return SIZE_MAX; } static bool cf_connect(const bqws_url *url, pt_cf *cf) { return false; } static void cf_get_address(pt_cf *cf, bqws_pt_address *address) { } #define cf_enabled(cf) (false) #endif // -- POSIX socket implementation typedef struct { uint32_t magic; os_socket s; size_t send_size; char send_buf[512]; bool secure; pt_tls tls; pt_cf cf; bqws_pt_address address; bqws_allocator allocator; } pt_io; struct bqws_pt_server { uint32_t magic; os_socket s; bool secure; pt_tls_server tls; bqws_allocator allocator; }; static size_t io_imp_send(pt_io *io, const void *data, size_t size) { if (size == 0) return 0; if (cf_enabled(&io->cf)) { return cf_send(&io->cf, data, size); } else if (io->secure) { return tls_send(&io->tls, data, size); } else { return os_socket_send(io->s, data, size); } } static bool io_flush_imp(pt_io *io) { size_t size = io->send_size; size_t sent = io_imp_send(io, io->send_buf, size); if (sent == 0) return true; if (sent == SIZE_MAX) return false; size_t left = size - sent; io->send_size = left; if (left > 0) { memmove(io->send_buf, io->send_buf + sent, left); } return true; } static size_t io_push_imp(pt_io *io, const char *ptr, const char *end) { size_t size = end - ptr; size_t offset = io->send_size; size_t to_copy = sizeof(io->send_buf) - offset; if (to_copy > size) to_copy = size; memcpy(io->send_buf + offset, ptr, to_copy); io->send_size += to_copy; return to_copy; } static void io_free(pt_io *io) { if (cf_enabled(&io->cf)) cf_free(&io->cf); if (io->secure) tls_free(&io->tls); if (io->s != OS_BAD_SOCKET) os_socket_close(io->s); io->magic = BQWS_PT_DELETED_MAGIC; bqws_allocator allocator = io->allocator; bqws_allocator_free(&allocator, io, sizeof(pt_io)); } static size_t pt_io_send(void *user, bqws_socket *ws, const void *data, size_t size) { if (size == 0) return 0; pt_io *io = (pt_io*)user; const char *begin = (const char*)data, *end = begin + size; const char *ptr = begin; // TODO: Try 2*sizeof(io->send_buf) - io->send_size if (size <= sizeof(io->send_buf)) { ptr += io_push_imp(io, ptr, end); if (ptr != end) { if (!io_flush_imp(io)) return SIZE_MAX; ptr += io_push_imp(io, ptr, end); } } else { if (io->send_size > 0) { ptr += io_push_imp(io, ptr, end); if (!io_flush_imp(io)) return SIZE_MAX; } size_t sent = io_imp_send(io, ptr, end - ptr); if (sent == SIZE_MAX) return SIZE_MAX; ptr += sent; } return ptr - begin; } static size_t pt_io_recv(void *user, bqws_socket *ws, void *data, size_t max_size, size_t min_size) { if (max_size == 0) return 0; pt_io *io = (pt_io*)user; if (cf_enabled(&io->cf)) { return cf_recv(&io->cf, data, max_size); } else if (io->secure) { return tls_recv(&io->tls, data, max_size); } else { return os_socket_recv(io->s, data, max_size); } } static bool pt_io_flush(void *user, bqws_socket *ws) { pt_io *io = (pt_io*)user; return io_flush_imp(io); } static void pt_io_close(void *user, bqws_socket *ws) { pt_io *io = (pt_io*)user; io_free(io); } static bool pt_init(const bqws_pt_init_opts *opts) { if (!os_init(opts)) return false; if (!tls_init(opts)) { os_shutdown(); return false; } return true; } static void pt_shutdown() { tls_shutdown(); os_shutdown(); } static bqws_socket *pt_connect(const bqws_url *url, const bqws_pt_connect_opts *pt_opts, const bqws_opts *opts, const bqws_client_opts *client_opts) { pt_io *io = NULL; do { io = (pt_io*)bqws_allocator_alloc(&opts->allocator, sizeof(pt_io)); if (!io) break; memset(io, 0, sizeof(pt_io)); io->allocator = opts->allocator; io->s = OS_BAD_SOCKET; io->magic = BQWS_PT_IO_MAGIC; if (!cf_connect(url, &io->cf)) { bqws_pt_address addr = { BQWS_PT_ADDRESS_UNKNOWN }; io->s = os_socket_connect(url, &addr); if (io->s == OS_BAD_SOCKET) break; io->address = addr; if (url->secure) { io->secure = true; if (!tls_init_client(&io->tls, io->s, pt_opts, opts, client_opts)) break; } } bqws_opts opt; if (opts) { opt = *opts; } else { memset(&opt, 0, sizeof(opt)); } opt.io.user = io; opt.io.send_fn = &pt_io_send; opt.io.recv_fn = &pt_io_recv; opt.io.flush_fn = &pt_io_flush; opt.io.close_fn = &pt_io_close; bqws_socket *ws = bqws_new_client(&opt, client_opts); if (!ws) break; return ws; } while (false); if (io) io_free(io); return NULL; } static bqws_pt_server *pt_listen(const bqws_pt_listen_opts *pt_opts) { bqws_pt_server *sv = (bqws_pt_server*)bqws_allocator_alloc(&pt_opts->allocator, sizeof(bqws_pt_server)); if (!sv) { pt_fail_pt("pt_listen()", BQWS_PT_ERR_OUT_OF_MEMORY); return NULL; } memset(sv, 0, sizeof(bqws_pt_server)); sv->magic = BQWS_PT_SERVER_MAGIC; sv->allocator = pt_opts->allocator; if (pt_opts->secure) { sv->secure = true; if (!tls_init_server(&sv->tls, pt_opts)) { bqws_allocator_free(&pt_opts->allocator, sv, sizeof(bqws_pt_server)); return NULL; } } sv->s = os_socket_listen(pt_opts); if (sv->s == OS_BAD_SOCKET) { bqws_allocator_free(&pt_opts->allocator, sv, sizeof(bqws_pt_server)); return NULL; } return sv; } static bqws_socket *pt_accept(bqws_pt_server *sv, const bqws_opts *opts, const bqws_server_opts *server_opts) { bqws_assert(sv && sv->magic == BQWS_PT_SERVER_MAGIC); bqws_pt_address addr = { BQWS_PT_ADDRESS_UNKNOWN }; os_socket s = os_socket_accept(sv->s, &addr); if (s == OS_BAD_SOCKET) return NULL; pt_io *io = NULL; do { io = (pt_io*)bqws_allocator_alloc(&opts->allocator, sizeof(pt_io)); if (!io) break; memset(io, 0, sizeof(pt_io)); io->magic = BQWS_PT_IO_MAGIC; io->s = s; io->allocator = opts->allocator; io->address = addr; s = OS_BAD_SOCKET; if (sv->secure) { io->secure = true; if (!tls_init_accept(&io->tls, &sv->tls, s)) break; } bqws_opts opt; if (opts) { opt = *opts; } else { memset(&opt, 0, sizeof(opt)); } opt.io.user = io; opt.io.send_fn = &pt_io_send; opt.io.recv_fn = &pt_io_recv; opt.io.flush_fn = &pt_io_flush; opt.io.close_fn = &pt_io_close; bqws_socket *ws = bqws_new_server(&opt, server_opts); if (!ws) break; return ws; } while (false); if (io) io_free(io); os_socket_close(s); return NULL; } static void pt_free_server(bqws_pt_server *sv) { bqws_assert(sv && sv->magic == BQWS_PT_SERVER_MAGIC); if (sv->secure) { tls_free_server(&sv->tls); } os_socket_close(sv->s); sv->magic = BQWS_PT_DELETED_MAGIC; bqws_allocator allocator = sv->allocator; bqws_allocator_free(&allocator, sv, sizeof(bqws_pt_server)); } static bqws_pt_address pt_get_address(const bqws_socket *ws) { pt_io *io = (pt_io*)bqws_get_io_user(ws); bqws_assert(io && io->magic == BQWS_PT_IO_MAGIC); if (cf_enabled(&io->cf)) cf_get_address(&io->cf, &io->address); return io->address; } #else #error "Unsupported platform" #endif // -- API bool bqws_pt_init(const bqws_pt_init_opts *opts) { bqws_pt_init_opts opt; if (opts) { opt = *opts; } else { memset(&opt, 0, sizeof(opt)); } return pt_init(&opt); } void bqws_pt_shutdown() { pt_shutdown(); } void bqws_pt_clear_error() { t_err.function = NULL; t_err.type = BQWS_PT_ERRTYPE_NONE; t_err.data = 0; } bool bqws_pt_get_error(bqws_pt_error *err) { if (t_err.type == BQWS_PT_ERRTYPE_NONE) return false; if (err) *err = t_err; return true; } bqws_socket *bqws_pt_connect(const char *url, const bqws_pt_connect_opts *pt_opts, const bqws_opts *opts, const bqws_client_opts *client_opts) { bqws_pt_clear_error(); bqws_url parsed_url; if (!bqws_parse_url(&parsed_url, url)) { pt_fail_pt("bqws_parse_url()", BQWS_PT_ERR_BAD_URL); return NULL; } return bqws_pt_connect_url(&parsed_url, pt_opts, opts, client_opts); } bqws_socket *bqws_pt_connect_url(const bqws_url *url, const bqws_pt_connect_opts *pt_opts, const bqws_opts *opts, const bqws_client_opts *client_opts) { bqws_pt_clear_error(); bqws_pt_connect_opts popt; if (pt_opts) { popt = *pt_opts; } else { memset(&popt, 0, sizeof(popt)); } bqws_opts opt; if (opts) { opt = *opts; } else { memset(&opt, 0, sizeof(opt)); } bqws_client_opts copt; if (client_opts) { copt = *client_opts; } else { memset(&copt, 0, sizeof(copt)); } if (!copt.host) copt.host = url->host; if (!copt.path) copt.path = url->path; return pt_connect(url, &popt, &opt, &copt); } bqws_pt_server *bqws_pt_listen(const bqws_pt_listen_opts *pt_opts) { bqws_pt_clear_error(); bqws_pt_listen_opts opts; if (pt_opts) { opts = *pt_opts; } else { memset(&opts, 0, sizeof(opts)); } if (!opts.port) { opts.port = opts.secure ? 443 : 80; } if (!opts.backlog) { opts.backlog = 128; } else if (opts.backlog > INT32_MAX) { opts.backlog = INT32_MAX; } return pt_listen(&opts); } void bqws_pt_free_server(bqws_pt_server *sv) { if (!sv) return; pt_free_server(sv); } bqws_socket *bqws_pt_accept(bqws_pt_server *sv, const bqws_opts *opts, const bqws_server_opts *server_opts) { bqws_pt_clear_error(); bqws_opts opt; if (opts) { opt = *opts; } else { memset(&opt, 0, sizeof(opt)); } return pt_accept(sv, &opt, server_opts); } bqws_pt_address bqws_pt_get_address(const bqws_socket *ws) { bqws_assert(ws); if (bqws_get_io_closed(ws)) { bqws_pt_address null_addr = { BQWS_PT_ADDRESS_UNKNOWN }; return null_addr; } return pt_get_address(ws); } void bqws_pt_format_address(char *dst, size_t size, const bqws_pt_address *addr) { if (size == 0) return; switch (addr->type) { case BQWS_PT_ADDRESS_UNKNOWN: snprintf(dst, size, "(unknown)"); break; case BQWS_PT_ADDRESS_WEBSOCKET: snprintf(dst, size, "websocket[%d]", *(int*)addr->address); break; case BQWS_PT_ADDRESS_IPV4: snprintf(dst, size, "%u.%u.%u.%u:%u", (unsigned)addr->address[0], (unsigned)addr->address[1], (unsigned)addr->address[2], (unsigned)addr->address[3], (unsigned)addr->port); break; case BQWS_PT_ADDRESS_IPV6: { const uint8_t *a = addr->address; // Find the leftmost longest run of zeros that's longer than one size_t longest_begin = SIZE_MAX; size_t longest_zeros = 1; { size_t zeros = 0; size_t zero_begin = 0; for (size_t i = 0; i < 16; i += 2) { if (a[i] == 0 && a[i + 1] == 0) { if (zeros == 0) { zero_begin = i; } zeros++; if (zeros > longest_zeros) { longest_begin = zero_begin; longest_zeros = zeros; } } else { zeros = 0; } } } bool need_colon = false; char *ptr = dst, *end = dst + size; ptr += snprintf(ptr, end - ptr, "["); for (size_t i = 0; i < 16; i += 2) { if (i == longest_begin) { ptr += snprintf(ptr, end - ptr, "::"); need_colon = false; i += (longest_zeros - 1) * 2; continue; } unsigned v = (unsigned)a[i] << 8 | (unsigned)a[i + 1]; ptr += snprintf(ptr, end - ptr, need_colon ? ":%x" : "%x", v); need_colon = true; } ptr += snprintf(ptr, end - ptr, "]:%u", (unsigned)addr->port); } break; default: snprintf(dst, size, "(bad type)"); break; } } void bqws_pt_get_error_desc(char *dst, size_t size, const bqws_pt_error *err) { if (size == 0) return; *dst = '\0'; switch (err->type) { case BQWS_PT_ERRTYPE_NONE: // Nop, empty description break; case BQWS_PT_ERRTYPE_PT: { const char *str = bqws_pt_error_code_str((bqws_pt_error_code)err->data); size_t len = strlen(str); if (len > size) len = size; memcpy(dst, str, len); dst[len] = '\0'; } break; case BQWS_PT_ERRTYPE_WSA: #if defined(_WIN32) { wchar_t *buf; FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, (DWORD)err->data, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPWSTR)&buf, 0, NULL); int int_size = size < INT_MAX ? (int)size : INT_MAX; int res = WideCharToMultiByte(CP_UTF8, 0, buf, -1, dst, int_size, NULL, NULL); if (res == 0) { *dst = '\0'; } else if (res >= int_size) { dst[int_size] = '\0'; } } #endif break; case BQWS_PT_ERRTYPE_POSIX: { #if defined(_WIN32) strerror_s(dst, size, (int)err->data); #else const char *ptr = (const char*)(uintptr_t)strerror_r((int)err->data, dst, size); if (dst[0] == '\0' && ptr != dst) { const char *err_str = strerror((int)err->data); size_t len = strlen(err_str); if (len >= size - 1) len = size - 1; memcpy(dst, err_str, len); dst[len] = '\0'; } #endif } break; case BQWS_PT_ERRTYPE_GETADDRINFO: #if defined(BQWS_HAS_GAI_STRERROR) { const char *str = gai_strerror((int)err->data); size_t len = strlen(str); if (len > size) len = size; memcpy(dst, str, len); dst[len] = '\0'; } #endif break; case BQWS_PT_ERRTYPE_OPENSSL: #if defined(BQWS_PT_HAS_OPENSSL) && !defined(__EMSCRIPTEN__) ERR_error_string_n((unsigned long)err->data, dst, size); #endif break; } } void bqws_pt_sleep_ms(uint32_t ms) { pt_sleep_ms(ms); } const char *bqws_pt_error_type_str(bqws_pt_error_type type) { switch (type) { case BQWS_PT_ERRTYPE_NONE: return "NONE"; case BQWS_PT_ERRTYPE_PT: return "PT"; case BQWS_PT_ERRTYPE_WSA: return "WSA"; case BQWS_PT_ERRTYPE_POSIX: return "POSIX"; case BQWS_PT_ERRTYPE_GETADDRINFO: return "GETADDRINFO"; case BQWS_PT_ERRTYPE_OPENSSL: return "OPENSSL"; default: return "(unknown)"; } } const char *bqws_pt_error_code_str(bqws_pt_error_code err) { switch (err) { case BQWS_PT_OK: return "OK"; case BQWS_PT_ERR_NO_TLS: return "NO_TLS: bq_websocket_platform.c was built without TLS support"; case BQWS_PT_ERR_NO_SERVER_SUPPORT: return "NO_SERVER_SUPPORT: The platform doesn't support server sockets"; case BQWS_PT_ERR_OUT_OF_MEMORY: return "OUT_OF_MEMORY: Failed to allocate memory"; case BQWS_PT_ERR_BAD_URL: return "BAD_URL: Could not parse URL"; default: return "(unknown)"; } } /* ------------------------------------------------------------------------------ This software is available under 2 licenses -- choose whichever you prefer. ------------------------------------------------------------------------------ ALTERNATIVE A - MIT License Copyright (c) 2020 Samuli Raivio 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. ------------------------------------------------------------------------------ ALTERNATIVE B - Public Domain (www.unlicense.org) This is free and unencumbered software released into the public domain. Anyone is free to copy, modify, publish, use, compile, sell, or distribute this software, either in source code form or as a compiled binary, for any purpose, commercial or non-commercial, and by any means. In jurisdictions that recognize copyright laws, the author or authors of this software dedicate any and all copyright interest in the software to the public domain. We make this dedication for the benefit of the public at large and to the detriment of our heirs and successors. We intend this dedication to be an overt act of relinquishment in perpetuity of all present and future rights to this software under copyright law. 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 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. ---------------------------------------- */ #endif // BQ_PLATFORM_IMPLEMENTATION #line 0 #line 1 "3rd_simplex.h" /** 1D, 2D, 3D and 4D float Perlin Simplex noise */ /** Original code, stefan gustavson (PD). */ #ifdef SIMPLEX_C /* SimplexNoise1234, Simplex noise with true analytic * derivative in 1D to 4D. * * Author: Stefan Gustavson, 2003-2005 * Contact: stefan.gustavson@liu.se * * This code was GPL licensed until February 2011. * As the original author of this code, I hereby * release it into the public domain. * Please feel free to use it for whatever you want. * Credit is appreciated where appropriate, and I also * appreciate being told where this code finds any use, * but you may do as you like. */ /* * This implementation is "Simplex Noise" as presented by * Ken Perlin at a relatively obscure and not often cited course * session "Real-Time Shading" at Siggraph 2001 (before real * time shading actually took off), under the title "hardware noise". * The 3D function is numerically equivalent to his Java reference * code available in the PDF course notes, although I re-implemented * it from scratch to get more readable code. The 1D, 2D and 4D cases * were implemented from scratch by me from Ken Perlin's text. * * This file has no dependencies on any other file, not even its own * header file. The header file is made for use by external code only. */ // We don't really need to include this, but play nice and do it anyway. //#include "noise.c" #define FASTFLOOR(x) ( ((int)(x)<=(x)) ? ((int)x) : (((int)x)-1) ) //--------------------------------------------------------------------- // Static data /* * Permutation table. This is just a random jumble of all numbers 0-255, * repeated twice to avoid wrapping the index at 255 for each lookup. * This needs to be exactly the same for all instances on all platforms, * so it's easiest to just keep it as static explicit data. * This also removes the need for any initialisation of this class. * * Note that making this an int[] instead of a char[] might make the * code run faster on platforms with a high penalty for unaligned single * byte addressing. Intel x86 is generally single-byte-friendly, but * some other CPUs are faster with 4-aligned reads. * However, a char[] is smaller, which avoids cache trashing, and that * is probably the most important aspect on most architectures. * This array is accessed a *lot* by the noise functions. * A vector-valued noise over 3D accesses it 96 times, and a * float-valued 4D noise 64 times. We want this to fit in the cache! */ unsigned char perm[512] = {151,160,137,91,90,15, 131,13,201,95,96,53,194,233,7,225,140,36,103,30,69,142,8,99,37,240,21,10,23, 190, 6,148,247,120,234,75,0,26,197,62,94,252,219,203,117,35,11,32,57,177,33, 88,237,149,56,87,174,20,125,136,171,168, 68,175,74,165,71,134,139,48,27,166, 77,146,158,231,83,111,229,122,60,211,133,230,220,105,92,41,55,46,245,40,244, 102,143,54, 65,25,63,161, 1,216,80,73,209,76,132,187,208, 89,18,169,200,196, 135,130,116,188,159,86,164,100,109,198,173,186, 3,64,52,217,226,250,124,123, 5,202,38,147,118,126,255,82,85,212,207,206,59,227,47,16,58,17,182,189,28,42, 223,183,170,213,119,248,152, 2,44,154,163, 70,221,153,101,155,167, 43,172,9, 129,22,39,253, 19,98,108,110,79,113,224,232,178,185, 112,104,218,246,97,228, 251,34,242,193,238,210,144,12,191,179,162,241, 81,51,145,235,249,14,239,107, 49,192,214, 31,181,199,106,157,184, 84,204,176,115,121,50,45,127, 4,150,254, 138,236,205,93,222,114,67,29,24,72,243,141,128,195,78,66,215,61,156,180, 151,160,137,91,90,15, 131,13,201,95,96,53,194,233,7,225,140,36,103,30,69,142,8,99,37,240,21,10,23, 190, 6,148,247,120,234,75,0,26,197,62,94,252,219,203,117,35,11,32,57,177,33, 88,237,149,56,87,174,20,125,136,171,168, 68,175,74,165,71,134,139,48,27,166, 77,146,158,231,83,111,229,122,60,211,133,230,220,105,92,41,55,46,245,40,244, 102,143,54, 65,25,63,161, 1,216,80,73,209,76,132,187,208, 89,18,169,200,196, 135,130,116,188,159,86,164,100,109,198,173,186, 3,64,52,217,226,250,124,123, 5,202,38,147,118,126,255,82,85,212,207,206,59,227,47,16,58,17,182,189,28,42, 223,183,170,213,119,248,152, 2,44,154,163, 70,221,153,101,155,167, 43,172,9, 129,22,39,253, 19,98,108,110,79,113,224,232,178,185, 112,104,218,246,97,228, 251,34,242,193,238,210,144,12,191,179,162,241, 81,51,145,235,249,14,239,107, 49,192,214, 31,181,199,106,157,184, 84,204,176,115,121,50,45,127, 4,150,254, 138,236,205,93,222,114,67,29,24,72,243,141,128,195,78,66,215,61,156,180 }; //--------------------------------------------------------------------- /* * Helper functions to compute gradients-dot-residualvectors (1D to 4D) * Note that these generate gradients of more than unit length. To make * a close match with the value range of classic Perlin noise, the final * noise values need to be rescaled to fit nicely within [-1,1]. * (The simplex noise functions as such also have different scaling.) * Note also that these noise functions are the most practical and useful * signed version of Perlin noise. To return values according to the * RenderMan specification from the SL noise() and pnoise() functions, * the noise values need to be scaled and offset to [0,1], like this: * float SLnoise = (noise(x,y,z) + 1.0) * 0.5; */ float grad1( int hash, float x ) { int h = hash & 15; float grad = 1.0f + (h & 7); // Gradient value 1.0, 2.0, ..., 8.0 if (h&8) grad = -grad; // Set a random sign for the gradient return ( grad * x ); // Multiply the gradient with the distance } float grad2( int hash, float x, float y ) { int h = hash & 7; // Convert low 3 bits of hash code float u = h<4 ? x : y; // into 8 simple gradient directions, float v = h<4 ? y : x; // and compute the dot product with (x,y). return ((h&1)? -u : u) + ((h&2)? -2.0f*v : 2.0f*v); } float grad3( int hash, float x, float y , float z ) { int h = hash & 15; // Convert low 4 bits of hash code into 12 simple float u = h<8 ? x : y; // gradient directions, and compute dot product. float v = h<4 ? y : h==12||h==14 ? x : z; // Fix repeats at h = 12 to 15 return ((h&1)? -u : u) + ((h&2)? -v : v); } float grad4( int hash, float x, float y, float z, float t ) { int h = hash & 31; // Convert low 5 bits of hash code into 32 simple float u = h<24 ? x : y; // gradient directions, and compute dot product. float v = h<16 ? y : z; float w = h<8 ? z : t; return ((h&1)? -u : u) + ((h&2)? -v : v) + ((h&4)? -w : w); } // A lookup table to traverse the simplex around a given point in 4D. // Details can be found where this table is used, in the 4D noise method. /* TODO: This should not be required, backport it from Bill's GLSL code! */ static unsigned char simplex[64][4] = { {0,1,2,3},{0,1,3,2},{0,0,0,0},{0,2,3,1},{0,0,0,0},{0,0,0,0},{0,0,0,0},{1,2,3,0}, {0,2,1,3},{0,0,0,0},{0,3,1,2},{0,3,2,1},{0,0,0,0},{0,0,0,0},{0,0,0,0},{1,3,2,0}, {0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0}, {1,2,0,3},{0,0,0,0},{1,3,0,2},{0,0,0,0},{0,0,0,0},{0,0,0,0},{2,3,0,1},{2,3,1,0}, {1,0,2,3},{1,0,3,2},{0,0,0,0},{0,0,0,0},{0,0,0,0},{2,0,3,1},{0,0,0,0},{2,1,3,0}, {0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0}, {2,0,1,3},{0,0,0,0},{0,0,0,0},{0,0,0,0},{3,0,1,2},{3,0,2,1},{0,0,0,0},{3,1,2,0}, {2,1,0,3},{0,0,0,0},{0,0,0,0},{0,0,0,0},{3,1,0,2},{0,0,0,0},{3,2,0,1},{3,2,1,0}}; // 1D simplex noise float snoise1(float x) { int i0 = FASTFLOOR(x); int i1 = i0 + 1; float x0 = x - i0; float x1 = x0 - 1.0f; float n0, n1; float t0 = 1.0f - x0*x0; // if(t0 < 0.0f) t0 = 0.0f; // this never happens for the 1D case t0 *= t0; n0 = t0 * t0 * grad1(perm[i0 & 0xff], x0); float t1 = 1.0f - x1*x1; // if(t1 < 0.0f) t1 = 0.0f; // this never happens for the 1D case t1 *= t1; n1 = t1 * t1 * grad1(perm[i1 & 0xff], x1); // The maximum value of this noise is 8*(3/4)^4 = 2.53125 // A factor of 0.395 would scale to fit exactly within [-1,1], but // we want to match PRMan's 1D noise, so we scale it down some more. return 0.25f * (n0 + n1); } // 2D simplex noise float snoise2(float x, float y) { #define F2 0.366025403 // F2 = 0.5*(sqrt(3.0)-1.0) #define G2 0.211324865 // G2 = (3.0-Math.sqrt(3.0))/6.0 float n0, n1, n2; // Noise contributions from the three corners // Skew the input space to determine which simplex cell we're in float s = (x+y)*F2; // Hairy factor for 2D float xs = x + s; float ys = y + s; int i = FASTFLOOR(xs); int j = FASTFLOOR(ys); float t = (float)(i+j)*G2; float X0 = i-t; // Unskew the cell origin back to (x,y) space float Y0 = j-t; float x0 = x-X0; // The x,y distances from the cell origin float y0 = y-Y0; // For the 2D case, the simplex shape is an equilateral triangle. // Determine which simplex we are in. int i1, j1; // Offsets for second (middle) corner of simplex in (i,j) coords if(x0>y0) {i1=1; j1=0;} // lower triangle, XY order: (0,0)->(1,0)->(1,1) else {i1=0; j1=1;} // upper triangle, YX order: (0,0)->(0,1)->(1,1) // A step of (1,0) in (i,j) means a step of (1-c,-c) in (x,y), and // a step of (0,1) in (i,j) means a step of (-c,1-c) in (x,y), where // c = (3-sqrt(3))/6 float x1 = x0 - i1 + G2; // Offsets for middle corner in (x,y) unskewed coords float y1 = y0 - j1 + G2; float x2 = x0 - 1.0f + 2.0f * G2; // Offsets for last corner in (x,y) unskewed coords float y2 = y0 - 1.0f + 2.0f * G2; // Wrap the integer indices at 256, to avoid indexing perm[] out of bounds int ii = i & 0xff; int jj = j & 0xff; // Calculate the contribution from the three corners float t0 = 0.5f - x0*x0-y0*y0; if(t0 < 0.0f) n0 = 0.0f; else { t0 *= t0; n0 = t0 * t0 * grad2(perm[ii+perm[jj]], x0, y0); } float t1 = 0.5f - x1*x1-y1*y1; if(t1 < 0.0f) n1 = 0.0f; else { t1 *= t1; n1 = t1 * t1 * grad2(perm[ii+i1+perm[jj+j1]], x1, y1); } float t2 = 0.5f - x2*x2-y2*y2; if(t2 < 0.0f) n2 = 0.0f; else { t2 *= t2; n2 = t2 * t2 * grad2(perm[ii+1+perm[jj+1]], x2, y2); } // Add contributions from each corner to get the final noise value. // The result is scaled to return values in the interval [-1,1]. return 40.0f * (n0 + n1 + n2); // TODO: The scale factor is preliminary! } // 3D simplex noise float snoise3(float x, float y, float z) { // Simple skewing factors for the 3D case #define F3 0.333333333 #define G3 0.166666667 float n0, n1, n2, n3; // Noise contributions from the four corners // Skew the input space to determine which simplex cell we're in float s = (x+y+z)*F3; // Very nice and simple skew factor for 3D float xs = x+s; float ys = y+s; float zs = z+s; int i = FASTFLOOR(xs); int j = FASTFLOOR(ys); int k = FASTFLOOR(zs); float t = (float)(i+j+k)*G3; float X0 = i-t; // Unskew the cell origin back to (x,y,z) space float Y0 = j-t; float Z0 = k-t; float x0 = x-X0; // The x,y,z distances from the cell origin float y0 = y-Y0; float z0 = z-Z0; // For the 3D case, the simplex shape is a slightly irregular tetrahedron. // Determine which simplex we are in. int i1, j1, k1; // Offsets for second corner of simplex in (i,j,k) coords int i2, j2, k2; // Offsets for third corner of simplex in (i,j,k) coords /* This code would benefit from a backport from the GLSL version! */ if(x0>=y0) { if(y0>=z0) { i1=1; j1=0; k1=0; i2=1; j2=1; k2=0; } // X Y Z order else if(x0>=z0) { i1=1; j1=0; k1=0; i2=1; j2=0; k2=1; } // X Z Y order else { i1=0; j1=0; k1=1; i2=1; j2=0; k2=1; } // Z X Y order } else { // x0 y0) ? 32 : 0; int c2 = (x0 > z0) ? 16 : 0; int c3 = (y0 > z0) ? 8 : 0; int c4 = (x0 > w0) ? 4 : 0; int c5 = (y0 > w0) ? 2 : 0; int c6 = (z0 > w0) ? 1 : 0; int c = c1 + c2 + c3 + c4 + c5 + c6; int i1, j1, k1, l1; // The integer offsets for the second simplex corner int i2, j2, k2, l2; // The integer offsets for the third simplex corner int i3, j3, k3, l3; // The integer offsets for the fourth simplex corner // simplex[c] is a 4-vector with the numbers 0, 1, 2 and 3 in some order. // Many values of c will never occur, since e.g. x>y>z>w makes x=3 ? 1 : 0; j1 = simplex[c][1]>=3 ? 1 : 0; k1 = simplex[c][2]>=3 ? 1 : 0; l1 = simplex[c][3]>=3 ? 1 : 0; // The number 2 in the "simplex" array is at the second largest coordinate. i2 = simplex[c][0]>=2 ? 1 : 0; j2 = simplex[c][1]>=2 ? 1 : 0; k2 = simplex[c][2]>=2 ? 1 : 0; l2 = simplex[c][3]>=2 ? 1 : 0; // The number 1 in the "simplex" array is at the second smallest coordinate. i3 = simplex[c][0]>=1 ? 1 : 0; j3 = simplex[c][1]>=1 ? 1 : 0; k3 = simplex[c][2]>=1 ? 1 : 0; l3 = simplex[c][3]>=1 ? 1 : 0; // The fifth corner has all coordinate offsets = 1, so no need to look that up. float x1 = x0 - i1 + G4; // Offsets for second corner in (x,y,z,w) coords float y1 = y0 - j1 + G4; float z1 = z0 - k1 + G4; float w1 = w0 - l1 + G4; float x2 = x0 - i2 + 2.0f*G4; // Offsets for third corner in (x,y,z,w) coords float y2 = y0 - j2 + 2.0f*G4; float z2 = z0 - k2 + 2.0f*G4; float w2 = w0 - l2 + 2.0f*G4; float x3 = x0 - i3 + 3.0f*G4; // Offsets for fourth corner in (x,y,z,w) coords float y3 = y0 - j3 + 3.0f*G4; float z3 = z0 - k3 + 3.0f*G4; float w3 = w0 - l3 + 3.0f*G4; float x4 = x0 - 1.0f + 4.0f*G4; // Offsets for last corner in (x,y,z,w) coords float y4 = y0 - 1.0f + 4.0f*G4; float z4 = z0 - 1.0f + 4.0f*G4; float w4 = w0 - 1.0f + 4.0f*G4; // Wrap the integer indices at 256, to avoid indexing perm[] out of bounds int ii = i & 0xff; int jj = j & 0xff; int kk = k & 0xff; int ll = l & 0xff; // Calculate the contribution from the five corners float t0 = 0.5f - x0*x0 - y0*y0 - z0*z0 - w0*w0; if(t0 < 0.0f) n0 = 0.0f; else { t0 *= t0; n0 = t0 * t0 * grad4(perm[ii+perm[jj+perm[kk+perm[ll]]]], x0, y0, z0, w0); } float t1 = 0.5f - x1*x1 - y1*y1 - z1*z1 - w1*w1; if(t1 < 0.0f) n1 = 0.0f; else { t1 *= t1; n1 = t1 * t1 * grad4(perm[ii+i1+perm[jj+j1+perm[kk+k1+perm[ll+l1]]]], x1, y1, z1, w1); } float t2 = 0.5f - x2*x2 - y2*y2 - z2*z2 - w2*w2; if(t2 < 0.0f) n2 = 0.0f; else { t2 *= t2; n2 = t2 * t2 * grad4(perm[ii+i2+perm[jj+j2+perm[kk+k2+perm[ll+l2]]]], x2, y2, z2, w2); } float t3 = 0.5f - x3*x3 - y3*y3 - z3*z3 - w3*w3; if(t3 < 0.0f) n3 = 0.0f; else { t3 *= t3; n3 = t3 * t3 * grad4(perm[ii+i3+perm[jj+j3+perm[kk+k3+perm[ll+l3]]]], x3, y3, z3, w3); } float t4 = 0.5f - x4*x4 - y4*y4 - z4*z4 - w4*w4; if(t4 < 0.0f) n4 = 0.0f; else { t4 *= t4; n4 = t4 * t4 * grad4(perm[ii+1+perm[jj+1+perm[kk+1+perm[ll+1]]]], x4, y4, z4, w4); } // Sum up and scale the result to cover the range [-1,1] return 62.0f * (n0 + n1 + n2 + n3 + n4); } #undef F2 #undef G2 #undef F3 #undef G3 #undef F4 #undef G4 #endif #line 0 #line 1 "3rd_tfd.h" /* If you are using a C++ compiler to compile tinyfiledialogs.c (maybe renamed with an extension ".cpp") then comment out << extern "C" >> bellow in this header file) */ /*_________ / \ tinyfiledialogs.h v3.8.8 [Apr 22, 2021] zlib licence |tiny file| Unique header file created [November 9, 2014] | dialogs | Copyright (c) 2014 - 2021 Guillaume Vareille http://ysengrin.com \____ ___/ http://tinyfiledialogs.sourceforge.net \| git clone http://git.code.sf.net/p/tinyfiledialogs/code tinyfd ____________________________________________ | | | email: tinyfiledialogs at ysengrin.com | |____________________________________________| ________________________________________________________________________________ | ____________________________________________________________________________ | | | | | | | on windows: | | | | - for UTF-16, use the wchar_t functions at the bottom of the header file | | | | - _wfopen() requires wchar_t | | | | | | | | - in tinyfiledialogs, char is UTF-8 by default (since v3.6) | | | | - but fopen() expects MBCS (not UTF-8) | | | | - if you want char to be MBCS: set tinyfd_winUtf8 to 0 | | | | | | | | - alternatively, tinyfiledialogs provides | | | | functions to convert between UTF-8, UTF-16 and MBCS | | | |____________________________________________________________________________| | |________________________________________________________________________________| If you like tinyfiledialogs, please upvote my stackoverflow answer https://stackoverflow.com/a/47651444 - License - This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ #ifndef TINYFILEDIALOGS_H #define TINYFILEDIALOGS_H #ifdef __cplusplus /* if tinydialogs.c is compiled as C++ code rather than C code, you may need to comment this out and the corresponding closing bracket near the end of this file. */ extern "C" { #endif /******************************************************************************************************/ /**************************************** UTF-8 on Windows ********************************************/ /******************************************************************************************************/ #ifdef _WIN32 /* On windows, if you want to use UTF-8 ( instead of the UTF-16/wchar_t functions at the end of this file ) Make sure your code is really prepared for UTF-8 (on windows, functions like fopen() expect MBCS and not UTF-8) */ extern int tinyfd_winUtf8; /* on windows char strings can be 1:UTF-8(default) or 0:MBCS */ /* for MBCS change this to 0, in tinyfiledialogs.c or in your code */ /* Here are some functions to help you convert between UTF-16 UTF-8 MBSC */ char * tinyfd_utf8toMbcs(char const * aUtf8string); char * tinyfd_utf16toMbcs(wchar_t const * aUtf16string); wchar_t * tinyfd_mbcsTo16(char const * aMbcsString); char * tinyfd_mbcsTo8(char const * aMbcsString); wchar_t * tinyfd_utf8to16(char const * aUtf8string); char * tinyfd_utf16to8(wchar_t const * aUtf16string); #endif /******************************************************************************************************/ /******************************************************************************************************/ /******************************************************************************************************/ /************* 3 funtions for C# (you don't need this in C or C++) : */ char const * tinyfd_getGlobalChar(char const * aCharVariableName); /* returns NULL on error */ int tinyfd_getGlobalInt(char const * aIntVariableName); /* returns -1 on error */ int tinyfd_setGlobalInt(char const * aIntVariableName, int aValue); /* returns -1 on error */ /* aCharVariableName: "tinyfd_version" "tinyfd_needs" "tinyfd_response" aIntVariableName : "tinyfd_verbose" "tinyfd_silent" "tinyfd_allowCursesDialogs" "tinyfd_forceConsole" "tinyfd_assumeGraphicDisplay" "tinyfd_winUtf8" **************/ extern char tinyfd_version[8]; /* contains tinyfd current version number */ extern char tinyfd_needs[]; /* info about requirements */ extern int tinyfd_verbose; /* 0 (default) or 1 : on unix, prints the command line calls */ extern int tinyfd_silent; /* 1 (default) or 0 : on unix, hide errors and warnings from called dialogs */ /* Curses dialogs are difficult to use, on windows they are only ascii and uses the unix backslah */ extern int tinyfd_allowCursesDialogs; /* 0 (default) or 1 */ extern int tinyfd_forceConsole; /* 0 (default) or 1 */ /* for unix & windows: 0 (graphic mode) or 1 (console mode). 0: try to use a graphic solution, if it fails then it uses console mode. 1: forces all dialogs into console mode even when an X server is present, it can use the package dialog or dialog.exe. on windows it only make sense for console applications */ extern int tinyfd_assumeGraphicDisplay; /* 0 (default) or 1 */ /* some systems don't set the environment variable DISPLAY even when a graphic display is present. set this to 1 to tell tinyfiledialogs to assume the existence of a graphic display */ extern char tinyfd_response[1024]; /* if you pass "tinyfd_query" as aTitle, the functions will not display the dialogs but will return 0 for console mode, 1 for graphic mode. tinyfd_response is then filled with the retain solution. possible values for tinyfd_response are (all lowercase) for graphic mode: windows_wchar windows applescript kdialog zenity zenity3 matedialog shellementary qarma yad python2-tkinter python3-tkinter python-dbus perl-dbus gxmessage gmessage xmessage xdialog gdialog for console mode: dialog whiptail basicinput no_solution */ void tinyfd_beep(void); int tinyfd_notifyPopup( char const * aTitle, /* NULL or "" */ char const * aMessage, /* NULL or "" may contain \n \t */ char const * aIconType); /* "info" "warning" "error" */ /* return has only meaning for tinyfd_query */ int tinyfd_messageBox( char const * aTitle , /* NULL or "" */ char const * aMessage , /* NULL or "" may contain \n \t */ char const * aDialogType , /* "ok" "okcancel" "yesno" "yesnocancel" */ char const * aIconType , /* "info" "warning" "error" "question" */ int aDefaultButton ) ; /* 0 for cancel/no , 1 for ok/yes , 2 for no in yesnocancel */ char * tinyfd_inputBox( char const * aTitle , /* NULL or "" */ char const * aMessage , /* NULL or "" (\n and \t have no effect) */ char const * aDefaultInput ) ; /* NULL passwordBox, "" inputbox */ /* returns NULL on cancel */ char * tinyfd_saveFileDialog( char const * aTitle , /* NULL or "" */ char const * aDefaultPathAndFile , /* NULL or "" */ int aNumOfFilterPatterns , /* 0 (1 in the following example) */ char const * const * aFilterPatterns , /* NULL or char const * lFilterPatterns[1]={"*.txt"} */ char const * aSingleFilterDescription ) ; /* NULL or "text files" */ /* returns NULL on cancel */ char * tinyfd_openFileDialog( char const * aTitle, /* NULL or "" */ char const * aDefaultPathAndFile, /* NULL or "" */ int aNumOfFilterPatterns , /* 0 (2 in the following example) */ char const * const * aFilterPatterns, /* NULL or char const * lFilterPatterns[2]={"*.png","*.jpg"}; */ char const * aSingleFilterDescription, /* NULL or "image files" */ int aAllowMultipleSelects ) ; /* 0 or 1 */ /* in case of multiple files, the separator is | */ /* returns NULL on cancel */ char * tinyfd_selectFolderDialog( char const * aTitle, /* NULL or "" */ char const * aDefaultPath); /* NULL or "" */ /* returns NULL on cancel */ char * tinyfd_colorChooser( char const * aTitle, /* NULL or "" */ char const * aDefaultHexRGB, /* NULL or "#FF0000" */ unsigned char const aDefaultRGB[3] , /* unsigned char lDefaultRGB[3] = { 0 , 128 , 255 }; */ unsigned char aoResultRGB[3] ) ; /* unsigned char lResultRGB[3]; */ /* returns the hexcolor as a string "#FF0000" */ /* aoResultRGB also contains the result */ /* aDefaultRGB is used only if aDefaultHexRGB is NULL */ /* aDefaultRGB and aoResultRGB can be the same array */ /* returns NULL on cancel */ /************ WINDOWS ONLY SECTION ************************/ #ifdef _WIN32 /* windows only - utf-16 version */ int tinyfd_notifyPopupW( wchar_t const * aTitle, /* NULL or L"" */ wchar_t const * aMessage, /* NULL or L"" may contain \n \t */ wchar_t const * aIconType); /* L"info" L"warning" L"error" */ /* windows only - utf-16 version */ int tinyfd_messageBoxW( wchar_t const * aTitle, /* NULL or L"" */ wchar_t const * aMessage, /* NULL or L"" may contain \n \t */ wchar_t const * aDialogType, /* L"ok" L"okcancel" L"yesno" */ wchar_t const * aIconType, /* L"info" L"warning" L"error" L"question" */ int aDefaultButton ); /* 0 for cancel/no , 1 for ok/yes */ /* returns 0 for cancel/no , 1 for ok/yes */ /* windows only - utf-16 version */ wchar_t * tinyfd_inputBoxW( wchar_t const * aTitle, /* NULL or L"" */ wchar_t const * aMessage, /* NULL or L"" (\n nor \t not respected) */ wchar_t const * aDefaultInput); /* NULL passwordBox, L"" inputbox */ /* windows only - utf-16 version */ wchar_t * tinyfd_saveFileDialogW( wchar_t const * aTitle, /* NULL or L"" */ wchar_t const * aDefaultPathAndFile, /* NULL or L"" */ int aNumOfFilterPatterns, /* 0 (1 in the following example) */ wchar_t const * const * aFilterPatterns, /* NULL or wchar_t const * lFilterPatterns[1]={L"*.txt"} */ wchar_t const * aSingleFilterDescription); /* NULL or L"text files" */ /* returns NULL on cancel */ /* windows only - utf-16 version */ wchar_t * tinyfd_openFileDialogW( wchar_t const * aTitle, /* NULL or L"" */ wchar_t const * aDefaultPathAndFile, /* NULL or L"" */ int aNumOfFilterPatterns , /* 0 (2 in the following example) */ wchar_t const * const * aFilterPatterns, /* NULL or wchar_t const * lFilterPatterns[2]={L"*.png","*.jpg"} */ wchar_t const * aSingleFilterDescription, /* NULL or L"image files" */ int aAllowMultipleSelects ) ; /* 0 or 1 */ /* in case of multiple files, the separator is | */ /* returns NULL on cancel */ /* windows only - utf-16 version */ wchar_t * tinyfd_selectFolderDialogW( wchar_t const * aTitle, /* NULL or L"" */ wchar_t const * aDefaultPath); /* NULL or L"" */ /* returns NULL on cancel */ /* windows only - utf-16 version */ wchar_t * tinyfd_colorChooserW( wchar_t const * aTitle, /* NULL or L"" */ wchar_t const * aDefaultHexRGB, /* NULL or L"#FF0000" */ unsigned char const aDefaultRGB[3], /* unsigned char lDefaultRGB[3] = { 0 , 128 , 255 }; */ unsigned char aoResultRGB[3]); /* unsigned char lResultRGB[3]; */ /* returns the hexcolor as a string L"#FF0000" */ /* aoResultRGB also contains the result */ /* aDefaultRGB is used only if aDefaultHexRGB is NULL */ /* aDefaultRGB and aoResultRGB can be the same array */ /* returns NULL on cancel */ #endif /*_WIN32 */ #ifdef __cplusplus } /*extern "C"*/ #endif #endif /* TINYFILEDIALOGS_H */ /* ________________________________________________________________________________ | ____________________________________________________________________________ | | | | | | | on windows: | | | | - for UTF-16, use the wchar_t functions at the bottom of the header file | | | | - _wfopen() requires wchar_t | | | | | | | | - in tinyfiledialogs, char is UTF-8 by default (since v3.6) | | | | - but fopen() expects MBCS (not UTF-8) | | | | - if you want char to be MBCS: set tinyfd_winUtf8 to 0 | | | | | | | | - alternatively, tinyfiledialogs provides | | | | functions to convert between UTF-8, UTF-16 and MBCS | | | |____________________________________________________________________________| | |________________________________________________________________________________| - This is not for ios nor android (it works in termux though). - The files can be renamed with extension ".cpp" as the code is 100% compatible C C++ (just comment out << extern "C" >> in the header file) - Windows is fully supported from XP to 10 (maybe even older versions) - C# & LUA via dll, see files in the folder EXTRAS - OSX supported from 10.4 to latest (maybe even older versions) - Do not use " and ' as the dialogs will be displayed with a warning instead of the title, message, etc... - There's one file filter only, it may contain several patterns. - If no filter description is provided, the list of patterns will become the description. - On windows link against Comdlg32.lib and Ole32.lib (on windows the no linking claim is a lie) - On unix: it tries command line calls, so no such need (NO LINKING). - On unix you need one of the following: applescript, kdialog, zenity, matedialog, shellementary, qarma, yad, python (2 or 3)/tkinter/python-dbus (optional), Xdialog or curses dialogs (opens terminal if running without console). - One of those is already included on most (if not all) desktops. - In the absence of those it will use gdialog, gxmessage or whiptail with a textinputbox. If nothing is found, it switches to basic console input, it opens a console if needed (requires xterm + bash). - for curses dialogs you must set tinyfd_allowCursesDialogs=1 - You can query the type of dialog that will be used (pass "tinyfd_query" as aTitle) - String memory is preallocated statically for all the returned values. - File and path names are tested before return, they should be valid. - tinyfd_forceConsole=1; at run time, forces dialogs into console mode. - On windows, console mode only make sense for console applications. - On windows, console mode is not implemented for wchar_T UTF-16. - Mutiple selects are not possible in console mode. - The package dialog must be installed to run in curses dialogs in console mode. It is already installed on most unix systems. - On osx, the package dialog can be installed via http://macappstore.org/dialog or http://macports.org - On windows, for curses dialogs console mode, dialog.exe should be copied somewhere on your executable path. It can be found at the bottom of the following page: http://andrear.altervista.org/home/cdialog.php */ #ifdef TFD_IMPLEMENTATION /* this file can be renamed with extension ".cpp" and compiled as C++. The code is 100% compatible C C++ (just comment out << extern "C" >> in the header file) */ /*_________ / \ tinyfiledialogs.c v3.8.8 [Apr 22, 2021] zlib licence |tiny file| Unique code file created [November 9, 2014] | dialogs | Copyright (c) 2014 - 2021 Guillaume Vareille http://ysengrin.com \____ ___/ http://tinyfiledialogs.sourceforge.net \| git clone http://git.code.sf.net/p/tinyfiledialogs/code tinyfd ____________________________________________ | | | email: tinyfiledialogs at ysengrin.com | |____________________________________________| _________________________________________________________________________________ | | | the windows only wchar_t UTF-16 prototypes are at the bottom of the header file | |_________________________________________________________________________________| _________________________________________________________ | | | on windows: - since v3.6 char is UTF-8 by default | | - if you want MBCS set tinyfd_winUtf8 to 0 | | - functions like fopen expect MBCS | |_________________________________________________________| If you like tinyfiledialogs, please upvote my stackoverflow answer https://stackoverflow.com/a/47651444 - License - This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. ----------- Thanks for contributions, bug corrections & thorough testing to: - Don Heyse http://ldglite.sf.net for bug corrections & thorough testing! - Paul Rouget */ #ifndef __sun #ifndef _POSIX_C_SOURCE #define _POSIX_C_SOURCE 2 /* to accept POSIX 2 in old ANSI C standards */ #endif #endif #if !defined(_WIN32) && ( defined(__GNUC__) || defined(__clang__) ) #if !defined(_GNU_SOURCE) #define _GNU_SOURCE /* used only to resolve symbolic links. Can be commented out */ #endif #endif #include #include #include #include #include #ifdef _WIN32 #ifdef __BORLANDC__ #define _getch getch #endif #ifndef _WIN32_WINNT #define _WIN32_WINNT 0x0500 #endif #include #include #include #include #include #define TINYFD_NOCCSUNICODE #define SLASH "\\" #else #include #include #include /* on old systems try instead */ #include #include #include /* on old systems try instead */ #define SLASH "/" #endif /* _WIN32 */ //#include "tinyfiledialogs.h" #define MAX_PATH_OR_CMD 1024 /* _MAX_PATH or MAX_PATH */ #ifndef MAX_MULTIPLE_FILES #define MAX_MULTIPLE_FILES 1024 #endif #define LOW_MULTIPLE_FILES 32 char tinyfd_version[8] = "3.8.8"; /******************************************************************************************************/ /**************************************** UTF-8 on Windows ********************************************/ /******************************************************************************************************/ #ifdef _WIN32 /* if you want to use UTF-8 ( instead of the UTF-16/wchar_t functions at the end of tinyfiledialogs.h ) Make sure your code is really prepared for UTF-8 (on windows, functions like fopen() expect MBCS and not UTF-8) */ int tinyfd_winUtf8 = 1; /* on windows char strings can be 1:UTF-8(default) or 0:MBCS */ /* for MBCS change this to 0, here or in your code */ #endif /******************************************************************************************************/ /******************************************************************************************************/ /******************************************************************************************************/ int tinyfd_verbose = 0 ; /* on unix: prints the command line calls */ int tinyfd_silent = 1 ; /* 1 (default) or 0 : on unix, hide errors and warnings from called dialogs */ /* Curses dialogs are difficult to use, on windows they are only ascii and uses the unix backslah */ int tinyfd_allowCursesDialogs = 0 ; /* 0 (default) or 1 */ int tinyfd_forceConsole = 0 ; /* 0 (default) or 1 */ /* for unix & windows: 0 (graphic mode) or 1 (console mode). 0: try to use a graphic solution, if it fails then it uses console mode. 1: forces all dialogs into console mode even when the X server is present. it can use the package dialog or dialog.exe. on windows it only make sense for console applications */ int tinyfd_assumeGraphicDisplay = 0; /* 0 (default) or 1 */ /* some systems don't set the environment variable DISPLAY even when a graphic display is present. set this to 1 to tell tinyfiledialogs to assume the existence of a graphic display */ char tinyfd_response[1024]; /* if you pass "tinyfd_query" as aTitle, the functions will not display the dialogs but and return 0 for console mode, 1 for graphic mode. tinyfd_response is then filled with the retain solution. possible values for tinyfd_response are (all lowercase) for graphic mode: windows_wchar windows applescript kdialog zenity zenity3 matedialog shellementary qarma yad python2-tkinter python3-tkinter python-dbus perl-dbus gxmessage gmessage xmessage xdialog gdialog for console mode: dialog whiptail basicinput no_solution */ static int gWarningDisplayed = 0 ; static char gTitle[]="missing software! (we will try basic console input)"; #ifdef _WIN32 char tinyfd_needs[] = "\ ___________\n\ / \\ \n\ | tiny file |\n\ | dialogs |\n\ \\_____ ____/\n\ \\|\ \ntiny file dialogs on Windows needs:\ \n a graphic display\ \nor dialog.exe (curses console mode)\ \nor a console for basic input"; #else char tinyfd_needs[] = "\ ___________\n\ / \\ \n\ | tiny file |\n\ | dialogs |\n\ \\_____ ____/\n\ \\|\ \ntiny file dialogs on UNIX needs:\ \n applescript or kdialog or yad or Xdialog\ \nor zenity (or matedialog or shellementary or qarma)\ \nor python (2 or 3) + tkinter + python-dbus (optional)\ \nor dialog (opens console if needed)\ \nor xterm + bash (opens console for basic input)\ \nor existing console for basic input"; #endif #ifdef _MSC_VER #pragma warning(disable:4996) /* allows usage of strncpy, strcpy, strcat, sprintf, fopen */ #pragma warning(disable:4100) /* allows usage of strncpy, strcpy, strcat, sprintf, fopen */ #pragma warning(disable:4706) /* allows usage of strncpy, strcpy, strcat, sprintf, fopen */ #endif static int getenvDISPLAY(void) { return tinyfd_assumeGraphicDisplay || getenv("DISPLAY"); } static char * getCurDir(void) { static char lCurDir[MAX_PATH_OR_CMD]; return getcwd(lCurDir, sizeof(lCurDir)); } static char * getPathWithoutFinalSlash( char * aoDestination, /* make sure it is allocated, use _MAX_PATH */ char const * aSource) /* aoDestination and aSource can be the same */ { char const * lTmp ; if ( aSource ) { lTmp = strrchr(aSource, '/'); if (!lTmp) { lTmp = strrchr(aSource, '\\'); } if (lTmp) { strncpy(aoDestination, aSource, lTmp - aSource ); aoDestination[lTmp - aSource] = '\0'; } else { * aoDestination = '\0'; } } else { * aoDestination = '\0'; } return aoDestination; } static char * getLastName( char * aoDestination, /* make sure it is allocated */ char const * aSource) { /* copy the last name after '/' or '\' */ char const * lTmp ; if ( aSource ) { lTmp = strrchr(aSource, '/'); if (!lTmp) { lTmp = strrchr(aSource, '\\'); } if (lTmp) { strcpy(aoDestination, lTmp + 1); } else { strcpy(aoDestination, aSource); } } else { * aoDestination = '\0'; } return aoDestination; } static void ensureFinalSlash( char * aioString ) { if ( aioString && strlen( aioString ) ) { char * lastcar = aioString + strlen( aioString ) - 1 ; if ( strncmp( lastcar , SLASH , 1 ) ) { strcat( lastcar , SLASH ) ; } } } static void Hex2RGB( char const aHexRGB[8] , unsigned char aoResultRGB[3] ) { char lColorChannel[8] ; if ( aoResultRGB ) { if ( aHexRGB ) { strcpy(lColorChannel, aHexRGB ) ; aoResultRGB[2] = (unsigned char)strtoul(lColorChannel+5,NULL,16); lColorChannel[5] = '\0'; aoResultRGB[1] = (unsigned char)strtoul(lColorChannel+3,NULL,16); lColorChannel[3] = '\0'; aoResultRGB[0] = (unsigned char)strtoul(lColorChannel+1,NULL,16); /* printf("%d %d %d\n", aoResultRGB[0], aoResultRGB[1], aoResultRGB[2]); */ } else { aoResultRGB[0]=0; aoResultRGB[1]=0; aoResultRGB[2]=0; } } } static void RGB2Hex( unsigned char const aRGB[3], char aoResultHexRGB[8] ) { if ( aoResultHexRGB ) { if ( aRGB ) { #if (defined(__cplusplus ) && __cplusplus >= 201103L) || (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(__clang__) sprintf(aoResultHexRGB, "#%02hhx%02hhx%02hhx", aRGB[0], aRGB[1], aRGB[2]); #else sprintf(aoResultHexRGB, "#%02hx%02hx%02hx", aRGB[0], aRGB[1], aRGB[2]); #endif /*printf("aoResultHexRGB %s\n", aoResultHexRGB);*/ } else { aoResultHexRGB[0]=0; aoResultHexRGB[1]=0; aoResultHexRGB[2]=0; } } } void tfd_replaceSubStr( char const * aSource, char const * aOldSubStr, char const * aNewSubStr, char * aoDestination ) { char const * pOccurence ; char const * p ; char const * lNewSubStr = "" ; size_t lOldSubLen = strlen( aOldSubStr ) ; if ( ! aSource ) { * aoDestination = '\0' ; return ; } if ( ! aOldSubStr ) { strcpy( aoDestination , aSource ) ; return ; } if ( aNewSubStr ) { lNewSubStr = aNewSubStr ; } p = aSource ; * aoDestination = '\0' ; while ( ( pOccurence = strstr( p , aOldSubStr ) ) != NULL ) { strncat( aoDestination , p , pOccurence - p ) ; strcat( aoDestination , lNewSubStr ) ; p = pOccurence + lOldSubLen ; } strcat( aoDestination , p ) ; } static int filenameValid( char const * aFileNameWithoutPath ) { if ( ! aFileNameWithoutPath || ! strlen(aFileNameWithoutPath) || strpbrk(aFileNameWithoutPath , "\\/:*?\"<>|") ) { return 0 ; } return 1 ; } #ifndef _WIN32 static int fileExists( char const * aFilePathAndName ) { FILE * lIn ; if ( ! aFilePathAndName || ! strlen(aFilePathAndName) ) { return 0 ; } lIn = fopen( aFilePathAndName , "r" ) ; if ( ! lIn ) { return 0 ; } fclose( lIn ) ; return 1 ; } #endif static void wipefile(char const * aFilename) { int i; struct stat st; FILE * lIn; if (stat(aFilename, &st) == 0) { if ((lIn = fopen(aFilename, "w"))) { for (i = 0; i < st.st_size; i++) { fputc('A', lIn); } fclose(lIn); } } } int tfd_quoteDetected(char const * aString) { char const * p; if (!aString) return 0; p = aString; while ((p = strchr(p, '\''))) { return 1; } p = aString; while ((p = strchr(p, '\"'))) { return 1; } return 0; } char const * tinyfd_getGlobalChar(char const * aCharVariableName) /* to be called from C# (you don't need this in C or C++) */ { if (!aCharVariableName || !strlen(aCharVariableName)) return NULL; else if (!strcmp(aCharVariableName, "tinyfd_version")) return tinyfd_version; else if (!strcmp(aCharVariableName, "tinyfd_needs")) return tinyfd_needs; else if (!strcmp(aCharVariableName, "tinyfd_response")) return tinyfd_response; else return NULL ; } int tinyfd_getGlobalInt(char const * aIntVariableName) /* to be called from C# (you don't need this in C or C++) */ { if ( !aIntVariableName || !strlen(aIntVariableName) ) return -1 ; else if ( !strcmp(aIntVariableName, "tinyfd_verbose") ) return tinyfd_verbose ; else if ( !strcmp(aIntVariableName, "tinyfd_silent") ) return tinyfd_silent ; else if ( !strcmp(aIntVariableName, "tinyfd_allowCursesDialogs") ) return tinyfd_allowCursesDialogs ; else if ( !strcmp(aIntVariableName, "tinyfd_forceConsole") ) return tinyfd_forceConsole ; else if ( !strcmp(aIntVariableName, "tinyfd_assumeGraphicDisplay") ) return tinyfd_assumeGraphicDisplay ; #ifdef _WIN32 else if ( !strcmp(aIntVariableName, "tinyfd_winUtf8") ) return tinyfd_winUtf8 ; #endif else return -1; } int tinyfd_setGlobalInt(char const * aIntVariableName, int aValue) /* to be called from C# (you don't need this in C or C++) */ { if (!aIntVariableName || !strlen(aIntVariableName)) return -1 ; else if (!strcmp(aIntVariableName, "tinyfd_verbose")) { tinyfd_verbose = aValue; return tinyfd_verbose; } else if (!strcmp(aIntVariableName, "tinyfd_silent")) { tinyfd_silent = aValue; return tinyfd_silent; } else if (!strcmp(aIntVariableName, "tinyfd_allowCursesDialogs")) { tinyfd_allowCursesDialogs = aValue; return tinyfd_allowCursesDialogs; } else if (!strcmp(aIntVariableName, "tinyfd_forceConsole")) { tinyfd_forceConsole = aValue; return tinyfd_forceConsole; } else if (!strcmp(aIntVariableName, "tinyfd_assumeGraphicDisplay")) { tinyfd_assumeGraphicDisplay = aValue; return tinyfd_assumeGraphicDisplay; } #ifdef _WIN32 else if (!strcmp(aIntVariableName, "tinyfd_winUtf8")) { tinyfd_winUtf8 = aValue; return tinyfd_winUtf8; } #endif else return -1; } #ifdef _WIN32 static int powershellPresent(void) { /*only on vista and above (or installed on xp)*/ static int lPowershellPresent = -1; char lBuff[MAX_PATH_OR_CMD]; FILE* lIn; char const* lString = "powershell.exe"; if (lPowershellPresent < 0) { if (!(lIn = _popen("where powershell.exe", "r"))) { lPowershellPresent = 0; return 0; } while (fgets(lBuff, sizeof(lBuff), lIn) != NULL) { } _pclose(lIn); if (lBuff[strlen(lBuff) - 1] == '\n') { lBuff[strlen(lBuff) - 1] = '\0'; } if (strcmp(lBuff + strlen(lBuff) - strlen(lString), lString)) { lPowershellPresent = 0; } else { lPowershellPresent = 1; } } return lPowershellPresent; } static int windowsVersion(void) { #if !defined(__MINGW32__) || defined(__MINGW64_VERSION_MAJOR) typedef LONG NTSTATUS ; typedef NTSTATUS(WINAPI* RtlGetVersionPtr)(PRTL_OSVERSIONINFOW); HMODULE hMod; RtlGetVersionPtr lFxPtr; RTL_OSVERSIONINFOW lRovi = { 0 }; hMod = GetModuleHandleW(L"ntdll.dll"); if (hMod) { lFxPtr = (RtlGetVersionPtr)GetProcAddress(hMod, "RtlGetVersion"); if (lFxPtr) { lRovi.dwOSVersionInfoSize = sizeof(lRovi); if (!lFxPtr(&lRovi)) { return lRovi.dwMajorVersion; } } } #endif if (powershellPresent()) return 6; /*minimum is vista or installed on xp*/ return 0; } static void replaceChr(char * aString, char aOldChr, char aNewChr) { char * p; if (!aString) return; if (aOldChr == aNewChr) return; p = aString; while ((p = strchr(p, aOldChr))) { *p = aNewChr; p++; } return; } #if !defined(WC_ERR_INVALID_CHARS) /* undefined prior to Vista, so not yet in MINGW header file */ #define WC_ERR_INVALID_CHARS 0x00000000 /* 0x00000080 for MINGW maybe ? */ #endif static int sizeUtf16From8(char const * aUtf8string) { return MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, aUtf8string, -1, NULL, 0); } static int sizeUtf16FromMbcs(char const * aMbcsString) { return MultiByteToWideChar(CP_ACP, MB_ERR_INVALID_CHARS, aMbcsString, -1, NULL, 0); } static int sizeUtf8(wchar_t const * aUtf16string) { return WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, aUtf16string, -1, NULL, 0, NULL, NULL); } static int sizeMbcs(wchar_t const * aMbcsString) { int lRes = WideCharToMultiByte(CP_ACP, 0, aMbcsString, -1, NULL, 0, NULL, NULL); /* DWORD licic = GetLastError(); */ return lRes; } wchar_t* tinyfd_mbcsTo16(char const* aMbcsString) { static wchar_t* lMbcsString = NULL; int lSize; free(lMbcsString); if (!aMbcsString) { lMbcsString = NULL; return NULL; } lSize = sizeUtf16FromMbcs(aMbcsString); if (lSize) { lMbcsString = (wchar_t*)malloc(lSize * sizeof(wchar_t)); lSize = MultiByteToWideChar(CP_ACP, 0, aMbcsString, -1, lMbcsString, lSize); } else wcscpy(lMbcsString, L""); return lMbcsString; } wchar_t * tinyfd_utf8to16(char const * aUtf8string) { static wchar_t * lUtf16string = NULL; int lSize; free(lUtf16string); if (!aUtf8string) {lUtf16string = NULL; return NULL;} lSize = sizeUtf16From8(aUtf8string); if (lSize) { lUtf16string = (wchar_t*)malloc(lSize * sizeof(wchar_t)); lSize = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, aUtf8string, -1, lUtf16string, lSize); return lUtf16string; } else { /* let's try mbcs anyway */ lUtf16string = NULL; return tinyfd_mbcsTo16(aUtf8string); } } char * tinyfd_utf16toMbcs(wchar_t const * aUtf16string) { static char * lMbcsString = NULL; int lSize; free(lMbcsString); if (!aUtf16string) { lMbcsString = NULL; return NULL; } lSize = sizeMbcs(aUtf16string); if (lSize) { lMbcsString = (char*)malloc(lSize); lSize = WideCharToMultiByte(CP_ACP, 0, aUtf16string, -1, lMbcsString, lSize, NULL, NULL); } else strcpy(lMbcsString, ""); return lMbcsString; } char * tinyfd_utf8toMbcs(char const * aUtf8string) { wchar_t const * lUtf16string; lUtf16string = tinyfd_utf8to16(aUtf8string); return tinyfd_utf16toMbcs(lUtf16string); } char * tinyfd_utf16to8(wchar_t const * aUtf16string) { static char * lUtf8string = NULL; int lSize; free(lUtf8string); if (!aUtf16string) { lUtf8string = NULL; return NULL; } lSize = sizeUtf8(aUtf16string); if (lSize) { lUtf8string = (char*)malloc(lSize); lSize = WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, aUtf16string, -1, lUtf8string, lSize, NULL, NULL); } else strcpy(lUtf8string, ""); return lUtf8string; } char * tinyfd_mbcsTo8(char const * aMbcsString) { wchar_t const * lUtf16string; lUtf16string = tinyfd_mbcsTo16(aMbcsString); return tinyfd_utf16to8(lUtf16string); } void tinyfd_beep(void) { if (windowsVersion() > 5) Beep(440, 300); else MessageBeep(-1); } static void wipefileW(wchar_t const * aFilename) { int i; FILE * lIn; #if defined(__MINGW32_MAJOR_VERSION) && !defined(__MINGW64__) && (__MINGW32_MAJOR_VERSION <= 3) struct _stat st; if (_wstat(aFilename, &st) == 0) #else struct __stat64 st; if (_wstat64(aFilename, &st) == 0) #endif { if ((lIn = _wfopen(aFilename, L"w"))) { for (i = 0; i < st.st_size; i++) { fputc('A', lIn); } fclose(lIn); } } } static wchar_t * getPathWithoutFinalSlashW( wchar_t * aoDestination, /* make sure it is allocated, use _MAX_PATH */ wchar_t const * aSource) /* aoDestination and aSource can be the same */ { wchar_t const * lTmp; if (aSource) { lTmp = wcsrchr(aSource, L'/'); if (!lTmp) { lTmp = wcsrchr(aSource, L'\\'); } if (lTmp) { wcsncpy(aoDestination, aSource, lTmp - aSource); aoDestination[lTmp - aSource] = L'\0'; } else { *aoDestination = L'\0'; } } else { *aoDestination = L'\0'; } return aoDestination; } static wchar_t * getLastNameW( wchar_t * aoDestination, /* make sure it is allocated */ wchar_t const * aSource) { /* copy the last name after '/' or '\' */ wchar_t const * lTmp; if (aSource) { lTmp = wcsrchr(aSource, L'/'); if (!lTmp) { lTmp = wcsrchr(aSource, L'\\'); } if (lTmp) { wcscpy(aoDestination, lTmp + 1); } else { wcscpy(aoDestination, aSource); } } else { *aoDestination = L'\0'; } return aoDestination; } static void Hex2RGBW(wchar_t const aHexRGB[8], unsigned char aoResultRGB[3]) { wchar_t lColorChannel[8]; if (aoResultRGB) { if (aHexRGB) { wcscpy(lColorChannel, aHexRGB); aoResultRGB[2] = (unsigned char)wcstoul(lColorChannel + 5, NULL, 16); lColorChannel[5] = '\0'; aoResultRGB[1] = (unsigned char)wcstoul(lColorChannel + 3, NULL, 16); lColorChannel[3] = '\0'; aoResultRGB[0] = (unsigned char)wcstoul(lColorChannel + 1, NULL, 16); /* printf("%d %d %d\n", aoResultRGB[0], aoResultRGB[1], aoResultRGB[2]); */ } else { aoResultRGB[0] = 0; aoResultRGB[1] = 0; aoResultRGB[2] = 0; } } } static void RGB2HexW( unsigned char const aRGB[3], wchar_t aoResultHexRGB[8]) { #if (defined(__cplusplus ) && __cplusplus >= 201103L) || (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(__clang__) wchar_t const * const lPrintFormat = L"#%02hhx%02hhx%02hhx"; #else wchar_t const * const lPrintFormat = L"#%02hx%02hx%02hx"; #endif if (aoResultHexRGB) { if (aRGB) { /* wprintf(L"aoResultHexRGB %s\n", aoResultHexRGB); */ #if !defined(__BORLANDC__) && !defined(__TINYC__) && !(defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR)) swprintf(aoResultHexRGB, 8, lPrintFormat, aRGB[0], aRGB[1], aRGB[2]); #else swprintf(aoResultHexRGB, lPrintFormat, aRGB[0], aRGB[1], aRGB[2]); #endif } else { aoResultHexRGB[0] = 0; aoResultHexRGB[1] = 0; aoResultHexRGB[2] = 0; } } } static int dirExists(char const * aDirPath) { #if defined(__MINGW32_MAJOR_VERSION) && !defined(__MINGW64__) && (__MINGW32_MAJOR_VERSION <= 3) struct _stat lInfo; #else struct __stat64 lInfo; #endif wchar_t * lTmpWChar; int lStatRet; size_t lDirLen; if (!aDirPath) return 0; lDirLen = strlen(aDirPath); if (!lDirLen) return 1; if ( (lDirLen == 2) && (aDirPath[1] == ':') ) return 1; if (tinyfd_winUtf8) { lTmpWChar = tinyfd_utf8to16(aDirPath); #if defined(__MINGW32_MAJOR_VERSION) && !defined(__MINGW64__) && (__MINGW32_MAJOR_VERSION <= 3) lStatRet = _wstat(lTmpWChar, &lInfo); #else lStatRet = _wstat64(lTmpWChar, &lInfo); #endif if (lStatRet != 0) return 0; else if (lInfo.st_mode & S_IFDIR) return 1; else return 0; } #if defined(__MINGW32_MAJOR_VERSION) && !defined(__MINGW64__) && (__MINGW32_MAJOR_VERSION <= 3) else if (_stat(aDirPath, &lInfo) != 0) #else else if (_stat64(aDirPath, &lInfo) != 0) #endif return 0; else if (lInfo.st_mode & S_IFDIR) return 1; else return 0; } static int fileExists(char const * aFilePathAndName) { #if defined(__MINGW32_MAJOR_VERSION) && !defined(__MINGW64__) && (__MINGW32_MAJOR_VERSION <= 3) struct _stat lInfo; #else struct __stat64 lInfo; #endif wchar_t * lTmpWChar; int lStatRet; FILE * lIn; if (!aFilePathAndName || !strlen(aFilePathAndName)) { return 0; } if (tinyfd_winUtf8) { lTmpWChar = tinyfd_utf8to16(aFilePathAndName); #if defined(__MINGW32_MAJOR_VERSION) && !defined(__MINGW64__) && (__MINGW32_MAJOR_VERSION <= 3) lStatRet = _wstat(lTmpWChar, &lInfo); #else lStatRet = _wstat64(lTmpWChar, &lInfo); #endif if (lStatRet != 0) return 0; else if (lInfo.st_mode & _S_IFREG) return 1; else return 0; } else { lIn = fopen(aFilePathAndName, "r"); if (!lIn) { return 0; } fclose(lIn); return 1; } } static void replaceWchar(wchar_t * aString, wchar_t aOldChr, wchar_t aNewChr) { wchar_t * p; if (!aString) { return ; } if (aOldChr == aNewChr) { return ; } p = aString; while ((p = wcsrchr(p, aOldChr))) { *p = aNewChr; #ifdef TINYFD_NOCCSUNICODE p++; #endif p++; } return ; } static int quoteDetectedW(wchar_t const * aString) { wchar_t const * p; if (!aString) return 0; p = aString; while ((p = wcsrchr(p, L'\''))) { return 1; } p = aString; while ((p = wcsrchr(p, L'\"'))) { return 1; } return 0; } #endif /* _WIN32 */ /* source and destination can be the same or ovelap*/ static char * ensureFilesExist(char * aDestination, char const * aSourcePathsAndNames) { char * lDestination = aDestination; char const * p; char const * p2; size_t lLen; if (!aSourcePathsAndNames) { return NULL; } lLen = strlen(aSourcePathsAndNames); if (!lLen) { return NULL; } p = aSourcePathsAndNames; while ((p2 = strchr(p, '|')) != NULL) { lLen = p2 - p; memmove(lDestination, p, lLen); lDestination[lLen] = '\0'; if (fileExists(lDestination)) { lDestination += lLen; *lDestination = '|'; lDestination++; } p = p2 + 1; } if (fileExists(p)) { lLen = strlen(p); memmove(lDestination, p, lLen); lDestination[lLen] = '\0'; } else { *(lDestination - 1) = '\0'; } return aDestination; } #ifdef _WIN32 static int __stdcall EnumThreadWndProc(HWND hwnd, LPARAM lParam) { wchar_t lTitleName[MAX_PATH]; GetWindowTextW(hwnd, lTitleName, MAX_PATH); /* wprintf(L"lTitleName %ls \n", lTitleName); */ if (wcscmp(L"tinyfiledialogsTopWindow", lTitleName) == 0) { SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); return 0; } return 1; } static void hiddenConsoleW(wchar_t const * aString, wchar_t const * aDialogTitle, int aInFront) { STARTUPINFOW StartupInfo; PROCESS_INFORMATION ProcessInfo; if (!aString || !wcslen(aString) ) return; memset(&StartupInfo, 0, sizeof(StartupInfo)); StartupInfo.cb = sizeof(STARTUPINFOW); StartupInfo.dwFlags = STARTF_USESHOWWINDOW; StartupInfo.wShowWindow = SW_HIDE; if (!CreateProcessW(NULL, (LPWSTR)aString, NULL, NULL, FALSE, CREATE_NEW_CONSOLE, NULL, NULL, &StartupInfo, &ProcessInfo)) { return; /* GetLastError(); */ } WaitForInputIdle(ProcessInfo.hProcess, INFINITE); if (aInFront) { while (EnumWindows(EnumThreadWndProc, (LPARAM)NULL)) {} SetWindowTextW(GetForegroundWindow(), aDialogTitle); } WaitForSingleObject(ProcessInfo.hProcess, INFINITE); CloseHandle(ProcessInfo.hThread); CloseHandle(ProcessInfo.hProcess); } int tinyfd_messageBoxW( wchar_t const * aTitle, /* NULL or "" */ wchar_t const * aMessage, /* NULL or "" may contain \n and \t */ wchar_t const * aDialogType, /* "ok" "okcancel" "yesno" "yesnocancel" */ wchar_t const * aIconType, /* "info" "warning" "error" "question" */ int aDefaultButton) /* 0 for cancel/no , 1 for ok/yes , 2 for no in yesnocancel */ { int lBoxReturnValue; UINT aCode; if (aTitle&&!wcscmp(aTitle, L"tinyfd_query")){ strcpy(tinyfd_response, "windows_wchar"); return 1; } if (quoteDetectedW(aTitle)) return tinyfd_messageBoxW(L"INVALID TITLE WITH QUOTES", aMessage, aDialogType, aIconType, aDefaultButton); if (quoteDetectedW(aMessage)) return tinyfd_messageBoxW(aTitle, L"INVALID MESSAGE WITH QUOTES", aDialogType, aIconType, aDefaultButton); if (aIconType && !wcscmp(L"warning", aIconType)) { aCode = MB_ICONWARNING; } else if (aIconType && !wcscmp(L"error", aIconType)) { aCode = MB_ICONERROR; } else if (aIconType && !wcscmp(L"question", aIconType)) { aCode = MB_ICONQUESTION; } else { aCode = MB_ICONINFORMATION; } if (aDialogType && !wcscmp(L"okcancel", aDialogType)) { aCode += MB_OKCANCEL; if (!aDefaultButton) { aCode += MB_DEFBUTTON2; } } else if (aDialogType && !wcscmp(L"yesno", aDialogType)) { aCode += MB_YESNO; if (!aDefaultButton) { aCode += MB_DEFBUTTON2; } } else { aCode += MB_OK; } aCode += MB_TOPMOST; lBoxReturnValue = MessageBoxW(GetForegroundWindow(), aMessage, aTitle, aCode); if (((aDialogType && wcscmp(L"okcancel", aDialogType) && wcscmp(L"yesno", aDialogType))) || (lBoxReturnValue == IDOK) || (lBoxReturnValue == IDYES)) { return 1; } else { return 0; } } /* return has only meaning for tinyfd_query */ int tinyfd_notifyPopupW( wchar_t const * aTitle, /* NULL or L"" */ wchar_t const * aMessage, /* NULL or L"" may contain \n \t */ wchar_t const * aIconType) /* L"info" L"warning" L"error" */ { wchar_t * lDialogString; size_t lTitleLen; size_t lMessageLen; size_t lDialogStringLen; if (aTitle && !wcscmp(aTitle, L"tinyfd_query")) { strcpy(tinyfd_response, "windows_wchar"); return 1; } if (quoteDetectedW(aTitle)) return tinyfd_notifyPopupW(L"INVALID TITLE WITH QUOTES", aMessage, aIconType); if (quoteDetectedW(aMessage)) return tinyfd_notifyPopupW(aTitle, L"INVALID MESSAGE WITH QUOTES", aIconType); lTitleLen = aTitle ? wcslen(aTitle) : 0; lMessageLen = aMessage ? wcslen(aMessage) : 0; lDialogStringLen = 3 * MAX_PATH_OR_CMD + lTitleLen + lMessageLen; lDialogString = (wchar_t *)malloc(2 * lDialogStringLen); if (!lDialogString) return 0; wcscpy(lDialogString, L"powershell.exe -command \"\ function Show-BalloonTip {\ [cmdletbinding()] \ param( \ [string]$Title = ' ', \ [string]$Message = ' ', \ [ValidateSet('info', 'warning', 'error')] \ [string]$IconType = 'info');\ [system.Reflection.Assembly]::LoadWithPartialName('System.Windows.Forms') | Out-Null ; \ $balloon = New-Object System.Windows.Forms.NotifyIcon ; \ $path = Get-Process -id $pid | Select-Object -ExpandProperty Path ; \ $icon = [System.Drawing.Icon]::ExtractAssociatedIcon($path) ;"); wcscat(lDialogString, L"\ $balloon.Icon = $icon ; \ $balloon.BalloonTipIcon = $IconType ; \ $balloon.BalloonTipText = $Message ; \ $balloon.BalloonTipTitle = $Title ; \ $balloon.Text = 'tinyfiledialogs' ; \ $balloon.Visible = $true ; \ $balloon.ShowBalloonTip(5000)};\ Show-BalloonTip"); if (aTitle && wcslen(aTitle)) { wcscat(lDialogString, L" -Title '"); wcscat(lDialogString, aTitle); wcscat(lDialogString, L"'"); } if (aMessage && wcslen(aMessage)) { wcscat(lDialogString, L" -Message '"); wcscat(lDialogString, aMessage); wcscat(lDialogString, L"'"); } if (aMessage && wcslen(aIconType)) { wcscat(lDialogString, L" -IconType '"); wcscat(lDialogString, aIconType); wcscat(lDialogString, L"'"); } wcscat(lDialogString, L"\""); /* wprintf ( L"lDialogString: %ls\n" , lDialogString ) ; */ hiddenConsoleW(lDialogString, aTitle, 0); free(lDialogString); return 1; } wchar_t * tinyfd_inputBoxW( wchar_t const * aTitle, /* NULL or L"" */ wchar_t const * aMessage, /* NULL or L"" (\n and \t have no effect) */ wchar_t const * aDefaultInput) /* L"" , if NULL it's a passwordBox */ { static wchar_t lBuff[MAX_PATH_OR_CMD]; wchar_t * lDialogString; FILE * lIn; FILE * lFile; int lResult; size_t lTitleLen; size_t lMessageLen; size_t lDialogStringLen; if (aTitle&&!wcscmp(aTitle, L"tinyfd_query")){ strcpy(tinyfd_response, "windows_wchar"); return (wchar_t *)1; } if (quoteDetectedW(aTitle)) return tinyfd_inputBoxW(L"INVALID TITLE WITH QUOTES", aMessage, aDefaultInput); if (quoteDetectedW(aMessage)) return tinyfd_inputBoxW(aTitle, L"INVALID MESSAGE WITH QUOTES", aDefaultInput); if (quoteDetectedW(aDefaultInput)) return tinyfd_inputBoxW(aTitle, aMessage, L"INVALID DEFAULT_INPUT WITH QUOTES"); lTitleLen = aTitle ? wcslen(aTitle) : 0 ; lMessageLen = aMessage ? wcslen(aMessage) : 0 ; lDialogStringLen = 3 * MAX_PATH_OR_CMD + lTitleLen + lMessageLen; lDialogString = (wchar_t *)malloc(2 * lDialogStringLen); if (aDefaultInput) { swprintf(lDialogString, #if !defined(__BORLANDC__) && !defined(__TINYC__) && !(defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR)) lDialogStringLen, #endif L"%ls\\tinyfd.vbs", _wgetenv(L"TEMP")); } else { swprintf(lDialogString, #if !defined(__BORLANDC__) && !defined(__TINYC__) && !(defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR)) lDialogStringLen, #endif L"%ls\\tinyfd.hta", _wgetenv(L"TEMP")); } lIn = _wfopen(lDialogString, L"w"); if (!lIn) { free(lDialogString); return NULL; } if ( aDefaultInput ) { wcscpy(lDialogString, L"Dim result:result=InputBox(\""); if (aMessage && wcslen(aMessage)) { wcscpy(lBuff, aMessage); replaceWchar(lBuff, L'\n', L' '); wcscat(lDialogString, lBuff); } wcscat(lDialogString, L"\",\"tinyfiledialogsTopWindow\",\""); if (aDefaultInput && wcslen(aDefaultInput)) { wcscpy(lBuff, aDefaultInput); replaceWchar(lBuff, L'\n', L' '); wcscat(lDialogString, lBuff); } wcscat(lDialogString, L"\"):If IsEmpty(result) then:WScript.Echo 0"); wcscat(lDialogString, L":Else: WScript.Echo \"1\" & result : End If"); } else { wcscpy(lDialogString, L"\n\ \n\ \n\ "); wcscat(lDialogString, L"tinyfiledialogsTopWindow"); wcscat(lDialogString, L"\n\ \n\ \n\ \n\ \n\ \n\ \n\ \n\ \n\ \n\ \n\
\n"); wcscat(lDialogString, aMessage ? aMessage : L""); wcscat(lDialogString, L"\n\ \n\ \n\ \n\
\n\

\n\ \n\
\n\
\n"); wcscat(lDialogString, L"\n\ \n\ \n\ \n\
\n\
\n\
\n\ \n\ \n\ " ) ; } fputws(lDialogString, lIn); fclose(lIn); if (aDefaultInput) { swprintf(lDialogString, #if !defined(__BORLANDC__) && !defined(__TINYC__) && !(defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR)) lDialogStringLen, #endif L"%ls\\tinyfd.txt",_wgetenv(L"TEMP")); #ifdef TINYFD_NOCCSUNICODE lFile = _wfopen(lDialogString, L"w"); fputc(0xFF, lFile); fputc(0xFE, lFile); #else lFile = _wfopen(lDialogString, L"wt, ccs=UNICODE"); /*or ccs=UTF-16LE*/ #endif fclose(lFile); wcscpy(lDialogString, L"cmd.exe /c cscript.exe //U //Nologo "); wcscat(lDialogString, L"\"%TEMP%\\tinyfd.vbs\" "); wcscat(lDialogString, L">> \"%TEMP%\\tinyfd.txt\""); } else { wcscpy(lDialogString, L"cmd.exe /c mshta.exe \"%TEMP%\\tinyfd.hta\""); } /* wprintf ( "lDialogString: %ls\n" , lDialogString ) ; */ hiddenConsoleW(lDialogString, aTitle, 1); swprintf(lDialogString, #if !defined(__BORLANDC__) && !defined(__TINYC__) && !(defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR)) lDialogStringLen, #endif L"%ls\\tinyfd.txt", _wgetenv(L"TEMP")); /* wprintf(L"lDialogString: %ls\n", lDialogString); */ #ifdef TINYFD_NOCCSUNICODE if (!(lIn = _wfopen(lDialogString, L"r"))) #else if (!(lIn = _wfopen(lDialogString, L"rt, ccs=UNICODE"))) /*or ccs=UTF-16LE*/ #endif { _wremove(lDialogString); free(lDialogString); return NULL; } memset(lBuff, 0, MAX_PATH_OR_CMD * sizeof(wchar_t) ); #ifdef TINYFD_NOCCSUNICODE fgets((char *)lBuff, 2*MAX_PATH_OR_CMD, lIn); #else fgetws(lBuff, MAX_PATH_OR_CMD, lIn); #endif fclose(lIn); wipefileW(lDialogString); _wremove(lDialogString); if (aDefaultInput) { swprintf(lDialogString, #if !defined(__BORLANDC__) && !defined(__TINYC__) && !(defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR)) lDialogStringLen, #endif L"%ls\\tinyfd.vbs", _wgetenv(L"TEMP")); } else { swprintf(lDialogString, #if !defined(__BORLANDC__) && !defined(__TINYC__) && !(defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR)) lDialogStringLen, #endif L"%ls\\tinyfd.hta", _wgetenv(L"TEMP")); } _wremove(lDialogString); free(lDialogString); /* wprintf( L"lBuff: %ls\n" , lBuff ) ; */ #ifdef TINYFD_NOCCSUNICODE lResult = !wcsncmp(lBuff+1, L"1", 1); #else lResult = !wcsncmp(lBuff, L"1", 1); #endif /* printf( "lResult: %d \n" , lResult ) ; */ if (!lResult) { return NULL ; } /* wprintf( "lBuff+1: %ls\n" , lBuff+1 ) ; */ #ifdef TINYFD_NOCCSUNICODE if (aDefaultInput) { lDialogStringLen = wcslen(lBuff) ; lBuff[lDialogStringLen - 1] = L'\0'; lBuff[lDialogStringLen - 2] = L'\0'; } return lBuff + 2; #else if (aDefaultInput) lBuff[wcslen(lBuff) - 1] = L'\0'; return lBuff + 1; #endif } wchar_t * tinyfd_saveFileDialogW( wchar_t const * aTitle, /* NULL or "" */ wchar_t const * aDefaultPathAndFile, /* NULL or "" */ int aNumOfFilterPatterns, /* 0 */ wchar_t const * const * aFilterPatterns, /* NULL or {"*.jpg","*.png"} */ wchar_t const * aSingleFilterDescription) /* NULL or "image files" */ { static wchar_t lBuff[MAX_PATH_OR_CMD]; wchar_t lDirname[MAX_PATH_OR_CMD]; wchar_t lDialogString[MAX_PATH_OR_CMD]; wchar_t lFilterPatterns[MAX_PATH_OR_CMD] = L""; wchar_t * p; wchar_t * lRetval; wchar_t const * ldefExt = NULL; int i; HRESULT lHResult; OPENFILENAMEW ofn = {0}; if (aTitle&&!wcscmp(aTitle, L"tinyfd_query")){ strcpy(tinyfd_response, "windows_wchar"); return (wchar_t *)1; } if (quoteDetectedW(aTitle)) return tinyfd_saveFileDialogW(L"INVALID TITLE WITH QUOTES", aDefaultPathAndFile, aNumOfFilterPatterns, aFilterPatterns, aSingleFilterDescription); if (quoteDetectedW(aDefaultPathAndFile)) return tinyfd_saveFileDialogW(aTitle, L"INVALID DEFAULT_PATH WITH QUOTES", aNumOfFilterPatterns, aFilterPatterns, aSingleFilterDescription); if (quoteDetectedW(aSingleFilterDescription)) return tinyfd_saveFileDialogW(aTitle, aDefaultPathAndFile, aNumOfFilterPatterns, aFilterPatterns, L"INVALID FILTER_DESCRIPTION WITH QUOTES"); for (i = 0; i < aNumOfFilterPatterns; i++) { if (quoteDetectedW(aFilterPatterns[i])) return tinyfd_saveFileDialogW(L"INVALID FILTER_PATTERN WITH QUOTES", aDefaultPathAndFile, 0, NULL, NULL); } lHResult = CoInitializeEx(NULL, 0); getPathWithoutFinalSlashW(lDirname, aDefaultPathAndFile); getLastNameW(lBuff, aDefaultPathAndFile); if (aNumOfFilterPatterns > 0) { ldefExt = aFilterPatterns[0]; if (aSingleFilterDescription && wcslen(aSingleFilterDescription)) { wcscpy(lFilterPatterns, aSingleFilterDescription); wcscat(lFilterPatterns, L"\n"); } wcscat(lFilterPatterns, aFilterPatterns[0]); for (i = 1; i < aNumOfFilterPatterns; i++) { wcscat(lFilterPatterns, L";"); wcscat(lFilterPatterns, aFilterPatterns[i]); } wcscat(lFilterPatterns, L"\n"); if (!(aSingleFilterDescription && wcslen(aSingleFilterDescription))) { wcscpy(lDialogString, lFilterPatterns); wcscat(lFilterPatterns, lDialogString); } wcscat(lFilterPatterns, L"All Files\n*.*\n"); p = lFilterPatterns; while ((p = wcschr(p, L'\n')) != NULL) { *p = L'\0'; p++; } } ofn.lStructSize = sizeof(OPENFILENAMEW); ofn.hwndOwner = GetForegroundWindow(); ofn.hInstance = 0; ofn.lpstrFilter = wcslen(lFilterPatterns) ? lFilterPatterns : NULL; ofn.lpstrCustomFilter = NULL; ofn.nMaxCustFilter = 0; ofn.nFilterIndex = 1; ofn.lpstrFile = lBuff; ofn.nMaxFile = MAX_PATH_OR_CMD; ofn.lpstrFileTitle = NULL; ofn.nMaxFileTitle = MAX_PATH_OR_CMD/2; ofn.lpstrInitialDir = wcslen(lDirname) ? lDirname : NULL; ofn.lpstrTitle = aTitle && wcslen(aTitle) ? aTitle : NULL; ofn.Flags = OFN_OVERWRITEPROMPT | OFN_NOCHANGEDIR | OFN_PATHMUSTEXIST ; ofn.nFileOffset = 0; ofn.nFileExtension = 0; ofn.lpstrDefExt = ldefExt; ofn.lCustData = 0L; ofn.lpfnHook = NULL; ofn.lpTemplateName = NULL; if (GetSaveFileNameW(&ofn) == 0) { lRetval = NULL; } else { lRetval = lBuff; } if (lHResult == S_OK || lHResult == S_FALSE) { CoUninitialize(); } return lRetval; } wchar_t * tinyfd_openFileDialogW( wchar_t const * aTitle, /* NULL or "" */ wchar_t const * aDefaultPathAndFile, /* NULL or "" */ int aNumOfFilterPatterns, /* 0 */ wchar_t const * const * aFilterPatterns, /* NULL or {"*.jpg","*.png"} */ wchar_t const * aSingleFilterDescription, /* NULL or "image files" */ int aAllowMultipleSelects) /* 0 or 1 ; -1 to free allocated memory and return */ { size_t lLengths[MAX_MULTIPLE_FILES]; wchar_t lDirname[MAX_PATH_OR_CMD]; wchar_t lFilterPatterns[MAX_PATH_OR_CMD] = L""; wchar_t lDialogString[MAX_PATH_OR_CMD]; wchar_t * lPointers[MAX_MULTIPLE_FILES+1]; wchar_t * p; int i, j; size_t lBuffLen; DWORD lFullBuffLen; HRESULT lHResult; OPENFILENAMEW ofn = { 0 }; static wchar_t * lBuff = NULL; free(lBuff); lBuff = NULL; if (aAllowMultipleSelects < 0) return (wchar_t *)0; if (aTitle&&!wcscmp(aTitle, L"tinyfd_query")){ strcpy(tinyfd_response, "windows_wchar"); return (wchar_t *)1; } if (quoteDetectedW(aTitle)) return tinyfd_openFileDialogW(L"INVALID TITLE WITH QUOTES", aDefaultPathAndFile, aNumOfFilterPatterns, aFilterPatterns, aSingleFilterDescription, aAllowMultipleSelects); if (quoteDetectedW(aDefaultPathAndFile)) return tinyfd_openFileDialogW(aTitle, L"INVALID DEFAULT_PATH WITH QUOTES", aNumOfFilterPatterns, aFilterPatterns, aSingleFilterDescription, aAllowMultipleSelects); if (quoteDetectedW(aSingleFilterDescription)) return tinyfd_openFileDialogW(aTitle, aDefaultPathAndFile, aNumOfFilterPatterns, aFilterPatterns, L"INVALID FILTER_DESCRIPTION WITH QUOTES", aAllowMultipleSelects); for (i = 0; i < aNumOfFilterPatterns; i++) { if (quoteDetectedW(aFilterPatterns[i])) return tinyfd_openFileDialogW(L"INVALID FILTER_PATTERN WITH QUOTES", aDefaultPathAndFile, 0, NULL, NULL, aAllowMultipleSelects); } if (aAllowMultipleSelects) { lFullBuffLen = MAX_MULTIPLE_FILES * MAX_PATH_OR_CMD + 1; lBuff = (wchar_t*)(malloc(lFullBuffLen * sizeof(wchar_t))); if (!lBuff) { lFullBuffLen = LOW_MULTIPLE_FILES * MAX_PATH_OR_CMD + 1; lBuff = (wchar_t*)( malloc( lFullBuffLen * sizeof(wchar_t))); } } else { lFullBuffLen = MAX_PATH_OR_CMD + 1; lBuff = (wchar_t*)(malloc(lFullBuffLen * sizeof(wchar_t))); } if (!lBuff) return NULL; lHResult = CoInitializeEx(NULL, 0); getPathWithoutFinalSlashW(lDirname, aDefaultPathAndFile); getLastNameW(lBuff, aDefaultPathAndFile); if (aNumOfFilterPatterns > 0) { if (aSingleFilterDescription && wcslen(aSingleFilterDescription)) { wcscpy(lFilterPatterns, aSingleFilterDescription); wcscat(lFilterPatterns, L"\n"); } wcscat(lFilterPatterns, aFilterPatterns[0]); for (i = 1; i < aNumOfFilterPatterns; i++) { wcscat(lFilterPatterns, L";"); wcscat(lFilterPatterns, aFilterPatterns[i]); } wcscat(lFilterPatterns, L"\n"); if (!(aSingleFilterDescription && wcslen(aSingleFilterDescription))) { wcscpy(lDialogString, lFilterPatterns); wcscat(lFilterPatterns, lDialogString); } wcscat(lFilterPatterns, L"All Files\n*.*\n"); p = lFilterPatterns; while ((p = wcschr(p, L'\n')) != NULL) { *p = L'\0'; p++; } } ofn.lStructSize = sizeof(OPENFILENAME); ofn.hwndOwner = GetForegroundWindow(); ofn.hInstance = 0; ofn.lpstrFilter = wcslen(lFilterPatterns) ? lFilterPatterns : NULL; ofn.lpstrCustomFilter = NULL; ofn.nMaxCustFilter = 0; ofn.nFilterIndex = 1; ofn.lpstrFile = lBuff; ofn.nMaxFile = lFullBuffLen; ofn.lpstrFileTitle = NULL; ofn.nMaxFileTitle = MAX_PATH_OR_CMD / 2; ofn.lpstrInitialDir = wcslen(lDirname) ? lDirname : NULL; ofn.lpstrTitle = aTitle && wcslen(aTitle) ? aTitle : NULL; ofn.Flags = OFN_EXPLORER | OFN_NOCHANGEDIR | OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST; ofn.nFileOffset = 0; ofn.nFileExtension = 0; ofn.lpstrDefExt = NULL; ofn.lCustData = 0L; ofn.lpfnHook = NULL; ofn.lpTemplateName = NULL; if (aAllowMultipleSelects) { ofn.Flags |= OFN_ALLOWMULTISELECT; } if (GetOpenFileNameW(&ofn) == 0) { free(lBuff); lBuff = NULL; } else { lBuffLen = wcslen(lBuff); lPointers[0] = lBuff + lBuffLen + 1; if (aAllowMultipleSelects && (lPointers[0][0] != L'\0')) { i = 0; do { lLengths[i] = wcslen(lPointers[i]); lPointers[i + 1] = lPointers[i] + lLengths[i] + 1; i++; } while (lPointers[i][0] != L'\0' && i < MAX_MULTIPLE_FILES ); if (i > MAX_MULTIPLE_FILES) { free(lBuff); lBuff = NULL; } else { i--; p = lBuff + lFullBuffLen - 1; *p = L'\0'; for (j = i; j >= 0; j--) { p -= lLengths[j]; memmove(p, lPointers[j], lLengths[j] * sizeof(wchar_t)); p--; *p = L'\\'; p -= lBuffLen; memmove(p, lBuff, lBuffLen*sizeof(wchar_t)); p--; *p = L'|'; } p++; wcscpy(lBuff, p); lBuffLen = wcslen(lBuff); } } if (lBuff) lBuff = (wchar_t*)(realloc(lBuff, (lBuffLen + 1) * sizeof(wchar_t))); } if (lHResult == S_OK || lHResult == S_FALSE) { CoUninitialize(); } return lBuff; } BOOL CALLBACK BrowseCallbackProcW_enum(HWND hWndChild, LPARAM lParam) { wchar_t buf[255]; GetClassNameW(hWndChild, buf, sizeof(buf)); if (wcscmp(buf, L"SysTreeView32") == 0) { HTREEITEM hNode = TreeView_GetSelection(hWndChild); TreeView_EnsureVisible(hWndChild, hNode); return FALSE; } return TRUE; } static int __stdcall BrowseCallbackProcW(HWND hwnd, UINT uMsg, LPARAM lp, LPARAM pData) { switch (uMsg) { case BFFM_INITIALIZED: SendMessage(hwnd, BFFM_SETSELECTIONW, TRUE, (LPARAM)pData); break; case BFFM_SELCHANGED: EnumChildWindows(hwnd, BrowseCallbackProcW_enum, 0); } return 0; } wchar_t * tinyfd_selectFolderDialogW( wchar_t const * aTitle, /* NULL or "" */ wchar_t const * aDefaultPath) /* NULL or "" */ { static wchar_t lBuff[MAX_PATH_OR_CMD]; wchar_t * lRetval; BROWSEINFOW bInfo; LPITEMIDLIST lpItem; HRESULT lHResult; if (aTitle&&!wcscmp(aTitle, L"tinyfd_query")){ strcpy(tinyfd_response, "windows_wchar"); return (wchar_t *)1; } if (quoteDetectedW(aTitle)) return tinyfd_selectFolderDialogW(L"INVALID TITLE WITH QUOTES", aDefaultPath); if (quoteDetectedW(aDefaultPath)) return tinyfd_selectFolderDialogW(aTitle, L"INVALID DEFAULT_PATH WITH QUOTES"); lHResult = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED); bInfo.hwndOwner = GetForegroundWindow(); bInfo.pidlRoot = NULL; bInfo.pszDisplayName = lBuff; bInfo.lpszTitle = aTitle && wcslen(aTitle) ? aTitle : NULL; if (lHResult == S_OK || lHResult == S_FALSE) { bInfo.ulFlags = BIF_USENEWUI; } bInfo.lpfn = BrowseCallbackProcW; bInfo.lParam = (LPARAM)aDefaultPath; bInfo.iImage = -1; lpItem = SHBrowseForFolderW(&bInfo); if (!lpItem) { lRetval = NULL; } else { SHGetPathFromIDListW(lpItem, lBuff); lRetval = lBuff ; } if (lHResult == S_OK || lHResult == S_FALSE) { CoUninitialize(); } return lRetval; } wchar_t * tinyfd_colorChooserW( wchar_t const * aTitle, /* NULL or "" */ wchar_t const * aDefaultHexRGB, /* NULL or "#FF0000"*/ unsigned char const aDefaultRGB[3], /* { 0 , 255 , 255 } */ unsigned char aoResultRGB[3]) /* { 0 , 0 , 0 } */ { static wchar_t lResultHexRGB[8]; CHOOSECOLORW cc; COLORREF crCustColors[16]; unsigned char lDefaultRGB[3]; int lRet; HRESULT lHResult; if (aTitle&&!wcscmp(aTitle, L"tinyfd_query")){ strcpy(tinyfd_response, "windows_wchar"); return (wchar_t *)1; } if (quoteDetectedW(aTitle)) return tinyfd_colorChooserW(L"INVALID TITLE WITH QUOTES", aDefaultHexRGB, aDefaultRGB, aoResultRGB); if (quoteDetectedW(aDefaultHexRGB)) return tinyfd_colorChooserW(aTitle, L"INVALID DEFAULT_HEX_RGB WITH QUOTES", aDefaultRGB, aoResultRGB); lHResult = CoInitializeEx(NULL, 0); if ( aDefaultHexRGB ) { Hex2RGBW(aDefaultHexRGB, lDefaultRGB); } else { lDefaultRGB[0] = aDefaultRGB[0]; lDefaultRGB[1] = aDefaultRGB[1]; lDefaultRGB[2] = aDefaultRGB[2]; } /* we can't use aTitle */ cc.lStructSize = sizeof(CHOOSECOLOR); cc.hwndOwner = GetForegroundWindow(); cc.hInstance = NULL; cc.rgbResult = RGB(lDefaultRGB[0], lDefaultRGB[1], lDefaultRGB[2]); cc.lpCustColors = crCustColors; cc.Flags = CC_RGBINIT | CC_FULLOPEN | CC_ANYCOLOR ; cc.lCustData = 0; cc.lpfnHook = NULL; cc.lpTemplateName = NULL; lRet = ChooseColorW(&cc); if (!lRet) { return NULL; } aoResultRGB[0] = GetRValue(cc.rgbResult); aoResultRGB[1] = GetGValue(cc.rgbResult); aoResultRGB[2] = GetBValue(cc.rgbResult); RGB2HexW(aoResultRGB, lResultHexRGB); if (lHResult == S_OK || lHResult == S_FALSE) { CoUninitialize(); } return lResultHexRGB; } static int messageBoxWinGui( char const * aTitle, /* NULL or "" */ char const * aMessage, /* NULL or "" may contain \n and \t */ char const * aDialogType, /* "ok" "okcancel" "yesno" "yesnocancel" */ char const * aIconType, /* "info" "warning" "error" "question" */ int aDefaultButton) /* 0 for cancel/no , 1 for ok/yes , 2 for no in yesnocancel */ { int lIntRetVal; wchar_t lTitle[128] = L""; wchar_t * lMessage = NULL; wchar_t lDialogType[16] = L""; wchar_t lIconType[16] = L""; wchar_t * lTmpWChar; if (aTitle) { if (tinyfd_winUtf8) lTmpWChar = tinyfd_utf8to16(aTitle); else lTmpWChar = tinyfd_mbcsTo16(aTitle); wcscpy(lTitle, lTmpWChar); } if (aMessage) { if (tinyfd_winUtf8) lTmpWChar = tinyfd_utf8to16(aMessage); else lTmpWChar = tinyfd_mbcsTo16(aMessage); lMessage = (wchar_t *) malloc((wcslen(lTmpWChar) + 1)* sizeof(wchar_t)); if (lMessage) wcscpy(lMessage, lTmpWChar); } if (aDialogType) { if (tinyfd_winUtf8) lTmpWChar = tinyfd_utf8to16(aDialogType); else lTmpWChar = tinyfd_mbcsTo16(aDialogType); wcscpy(lDialogType, lTmpWChar); } if (aIconType) { if (tinyfd_winUtf8) lTmpWChar = tinyfd_utf8to16(aIconType); else lTmpWChar = tinyfd_mbcsTo16(aIconType); wcscpy(lIconType, lTmpWChar); } lIntRetVal = tinyfd_messageBoxW(lTitle, lMessage, lDialogType, lIconType, aDefaultButton); free(lMessage); return lIntRetVal; } static int notifyWinGui( char const * aTitle, /* NULL or "" */ char const * aMessage, /* NULL or "" may NOT contain \n nor \t */ char const * aIconType) { wchar_t lTitle[128] = L""; wchar_t * lMessage = NULL; wchar_t lIconType[16] = L""; wchar_t * lTmpWChar; if (aTitle) { if (tinyfd_winUtf8) lTmpWChar = tinyfd_utf8to16(aTitle); else lTmpWChar = tinyfd_mbcsTo16(aTitle); wcscpy(lTitle, lTmpWChar); } if (aMessage) { if (tinyfd_winUtf8) lTmpWChar = tinyfd_utf8to16(aMessage); else lTmpWChar = tinyfd_mbcsTo16(aMessage); lMessage = (wchar_t *) malloc((wcslen(lTmpWChar) + 1)* sizeof(wchar_t)); if (lMessage) wcscpy(lMessage, lTmpWChar); } if (aIconType) { if (tinyfd_winUtf8) lTmpWChar = tinyfd_utf8to16(aIconType); else lTmpWChar = tinyfd_mbcsTo16(aIconType); wcscpy(lIconType, lTmpWChar); } tinyfd_notifyPopupW(lTitle, lMessage, lIconType); free(lMessage); return 1; } static int inputBoxWinGui( char * aoBuff, char const * aTitle, /* NULL or "" */ char const * aMessage, /* NULL or "" may NOT contain \n nor \t */ char const * aDefaultInput) /* "" , if NULL it's a passwordBox */ { wchar_t lTitle[128] = L""; wchar_t * lMessage = NULL; wchar_t lDefaultInput[MAX_PATH_OR_CMD] = L""; wchar_t * lTmpWChar; char * lTmpChar; if (aTitle) { if (tinyfd_winUtf8) lTmpWChar = tinyfd_utf8to16(aTitle); else lTmpWChar = tinyfd_mbcsTo16(aTitle); wcscpy(lTitle, lTmpWChar); } if (aMessage) { if (tinyfd_winUtf8) lTmpWChar = tinyfd_utf8to16(aMessage); else lTmpWChar = tinyfd_mbcsTo16(aMessage); lMessage = (wchar_t *) malloc((wcslen(lTmpWChar) + 1)* sizeof(wchar_t)); if (lMessage) wcscpy(lMessage, lTmpWChar); } if (aDefaultInput) { if (tinyfd_winUtf8) lTmpWChar = tinyfd_utf8to16(aDefaultInput); else lTmpWChar = tinyfd_mbcsTo16(aDefaultInput); wcscpy(lDefaultInput, lTmpWChar); lTmpWChar = tinyfd_inputBoxW(lTitle, lMessage, lDefaultInput); } else lTmpWChar = tinyfd_inputBoxW(lTitle, lMessage, NULL); free(lMessage); if (!lTmpWChar) { aoBuff[0] = '\0'; return 0; } if (tinyfd_winUtf8) lTmpChar = tinyfd_utf16to8(lTmpWChar); else lTmpChar = tinyfd_utf16toMbcs(lTmpWChar); strcpy(aoBuff, lTmpChar); return 1; } static char * saveFileDialogWinGui( char * aoBuff, char const * aTitle, /* NULL or "" */ char const * aDefaultPathAndFile, /* NULL or "" */ int aNumOfFilterPatterns, /* 0 */ char const * const * aFilterPatterns, /* NULL or {"*.jpg","*.png"} */ char const * aSingleFilterDescription) /* NULL or "image files" */ { wchar_t lTitle[128] = L""; wchar_t lDefaultPathAndFile[MAX_PATH_OR_CMD] = L""; wchar_t lSingleFilterDescription[128] = L""; wchar_t * * lFilterPatterns; wchar_t * lTmpWChar; char * lTmpChar; int i; lFilterPatterns = (wchar_t **)malloc(aNumOfFilterPatterns*sizeof(wchar_t *)); for (i = 0; i < aNumOfFilterPatterns; i++) { if (tinyfd_winUtf8) lTmpWChar = tinyfd_utf8to16(aFilterPatterns[i]); else lTmpWChar = tinyfd_mbcsTo16(aFilterPatterns[i]); lFilterPatterns[i] = (wchar_t *)malloc((wcslen(lTmpWChar) + 1) * sizeof(wchar_t *)); if (lFilterPatterns[i]) wcscpy(lFilterPatterns[i], lTmpWChar); } if (aTitle) { if (tinyfd_winUtf8) lTmpWChar = tinyfd_utf8to16(aTitle); else lTmpWChar = tinyfd_mbcsTo16(aTitle); wcscpy(lTitle, lTmpWChar); } if (aDefaultPathAndFile) { if (tinyfd_winUtf8) lTmpWChar = tinyfd_utf8to16(aDefaultPathAndFile); else lTmpWChar = tinyfd_mbcsTo16(aDefaultPathAndFile); wcscpy(lDefaultPathAndFile, lTmpWChar); } if (aSingleFilterDescription) { if (tinyfd_winUtf8) lTmpWChar = tinyfd_utf8to16(aSingleFilterDescription); else lTmpWChar = tinyfd_mbcsTo16(aSingleFilterDescription); wcscpy(lSingleFilterDescription, lTmpWChar); } lTmpWChar = tinyfd_saveFileDialogW( lTitle, lDefaultPathAndFile, aNumOfFilterPatterns, (wchar_t const**) lFilterPatterns, /*stupid cast for gcc*/ lSingleFilterDescription); for (i = 0; i < aNumOfFilterPatterns; i++) { free(lFilterPatterns[i]); } free(lFilterPatterns); if (!lTmpWChar) { return NULL; } if (tinyfd_winUtf8) lTmpChar = tinyfd_utf16to8(lTmpWChar); else lTmpChar = tinyfd_utf16toMbcs(lTmpWChar); strcpy(aoBuff, lTmpChar); if (tinyfd_winUtf8) (void)tinyfd_utf16to8(NULL); else (void)tinyfd_utf16toMbcs(NULL); return aoBuff; } static char * openFileDialogWinGui( char const * aTitle, /* NULL or "" */ char const * aDefaultPathAndFile, /* NULL or "" */ int aNumOfFilterPatterns, /* 0 */ char const * const * aFilterPatterns, /* NULL or {"*.jpg","*.png"} */ char const * aSingleFilterDescription, /* NULL or "image files" */ int aAllowMultipleSelects) /* 0 or 1 */ { wchar_t lTitle[128] = L""; wchar_t lDefaultPathAndFile[MAX_PATH_OR_CMD] = L""; wchar_t lSingleFilterDescription[128] = L""; wchar_t * * lFilterPatterns; wchar_t * lTmpWChar; char * lTmpChar; int i; lFilterPatterns = (wchar_t * *)malloc(aNumOfFilterPatterns*sizeof(wchar_t *)); for (i = 0; i < aNumOfFilterPatterns; i++) { if (tinyfd_winUtf8) lTmpWChar = tinyfd_utf8to16(aFilterPatterns[i]); else lTmpWChar = tinyfd_mbcsTo16(aFilterPatterns[i]); lFilterPatterns[i] = (wchar_t *)malloc((wcslen(lTmpWChar) + 1)*sizeof(wchar_t *)); if (lFilterPatterns[i]) wcscpy(lFilterPatterns[i], lTmpWChar); } if (aTitle) { if (tinyfd_winUtf8) lTmpWChar = tinyfd_utf8to16(aTitle); else lTmpWChar = tinyfd_mbcsTo16(aTitle); wcscpy(lTitle, lTmpWChar); } if (aDefaultPathAndFile) { if (tinyfd_winUtf8) lTmpWChar = tinyfd_utf8to16(aDefaultPathAndFile); else lTmpWChar = tinyfd_mbcsTo16(aDefaultPathAndFile); wcscpy(lDefaultPathAndFile, lTmpWChar); } if (aSingleFilterDescription) { if (tinyfd_winUtf8) lTmpWChar = tinyfd_utf8to16(aSingleFilterDescription); else lTmpWChar = tinyfd_mbcsTo16(aSingleFilterDescription); wcscpy(lSingleFilterDescription, lTmpWChar); } lTmpWChar = tinyfd_openFileDialogW( lTitle, lDefaultPathAndFile, aNumOfFilterPatterns, (wchar_t const**) lFilterPatterns, /*stupid cast for gcc*/ lSingleFilterDescription, aAllowMultipleSelects); for (i = 0; i < aNumOfFilterPatterns; i++) { free(lFilterPatterns[i]); } free(lFilterPatterns); if (!lTmpWChar) return NULL; if (tinyfd_winUtf8) lTmpChar = tinyfd_utf16to8(lTmpWChar); else lTmpChar = tinyfd_utf16toMbcs(lTmpWChar); (void)tinyfd_openFileDialogW(NULL, NULL, 0, NULL, NULL, -1); return lTmpChar; } static char * selectFolderDialogWinGui( char * aoBuff, char const * aTitle, /* NULL or "" */ char const * aDefaultPath) /* NULL or "" */ { wchar_t lTitle[128] = L""; wchar_t lDefaultPath[MAX_PATH_OR_CMD] = L""; wchar_t * lTmpWChar; char * lTmpChar; if (aTitle) { if (tinyfd_winUtf8) lTmpWChar = tinyfd_utf8to16(aTitle); else lTmpWChar = tinyfd_mbcsTo16(aTitle); wcscpy(lTitle, lTmpWChar); } if (aDefaultPath) { if (tinyfd_winUtf8) lTmpWChar = tinyfd_utf8to16(aDefaultPath); else lTmpWChar = tinyfd_mbcsTo16(aDefaultPath); wcscpy(lDefaultPath, lTmpWChar); } lTmpWChar = tinyfd_selectFolderDialogW( lTitle, lDefaultPath); if (!lTmpWChar) { return NULL; } if (tinyfd_winUtf8) lTmpChar = tinyfd_utf16to8(lTmpWChar); else lTmpChar = tinyfd_utf16toMbcs(lTmpWChar); strcpy(aoBuff, lTmpChar); return aoBuff; } static char * colorChooserWinGui( char const * aTitle, /* NULL or "" */ char const * aDefaultHexRGB, /* NULL or "#FF0000"*/ unsigned char const aDefaultRGB[3], /* { 0 , 255 , 255 } */ unsigned char aoResultRGB[3]) /* { 0 , 0 , 0 } */ { static char lResultHexRGB[8]; wchar_t lTitle[128]; wchar_t lDefaultHexRGB[16]; wchar_t * lTmpWChar; char * lTmpChar; if (aTitle) { if (tinyfd_winUtf8) lTmpWChar = tinyfd_utf8to16(aTitle); else lTmpWChar = tinyfd_mbcsTo16(aTitle); wcscpy(lTitle, lTmpWChar); } if (aDefaultHexRGB) { if (tinyfd_winUtf8) lTmpWChar = tinyfd_utf8to16(aDefaultHexRGB); else lTmpWChar = tinyfd_mbcsTo16(aDefaultHexRGB); wcscpy(lDefaultHexRGB, lTmpWChar); } lTmpWChar = tinyfd_colorChooserW( lTitle, lDefaultHexRGB, aDefaultRGB, aoResultRGB ); if (!lTmpWChar) { return NULL; } if (tinyfd_winUtf8) lTmpChar = tinyfd_utf16to8(lTmpWChar); else lTmpChar = tinyfd_utf16toMbcs(lTmpWChar); strcpy(lResultHexRGB, lTmpChar); return lResultHexRGB; } static int dialogPresent(void) { static int lDialogPresent = -1 ; char lBuff[MAX_PATH_OR_CMD] ; FILE * lIn ; char const * lString = "dialog.exe"; if (!tinyfd_allowCursesDialogs) return 0; if (lDialogPresent < 0) { if (!(lIn = _popen("where dialog.exe","r"))) { lDialogPresent = 0 ; return 0 ; } while ( fgets( lBuff , sizeof( lBuff ) , lIn ) != NULL ) {} _pclose( lIn ) ; if ( lBuff[strlen( lBuff ) -1] == '\n' ) { lBuff[strlen( lBuff ) -1] = '\0' ; } if ( strcmp(lBuff+strlen(lBuff)-strlen(lString),lString) ) { lDialogPresent = 0 ; } else { lDialogPresent = 1 ; } } return lDialogPresent; } static int messageBoxWinConsole( char const * aTitle , /* NULL or "" */ char const * aMessage , /* NULL or "" may contain \n and \t */ char const * aDialogType , /* "ok" "okcancel" "yesno" "yesnocancel" */ char const * aIconType , /* "info" "warning" "error" "question" */ int aDefaultButton ) /* 0 for cancel/no , 1 for ok/yes , 2 for no in yesnocancel */ { char lDialogString[MAX_PATH_OR_CMD]; char lDialogFile[MAX_PATH_OR_CMD]; FILE * lIn; char lBuff[MAX_PATH_OR_CMD] = ""; strcpy(lDialogString, "dialog "); if (aTitle && strlen(aTitle)) { strcat(lDialogString, "--title \"") ; strcat(lDialogString, aTitle) ; strcat(lDialogString, "\" ") ; } if ( aDialogType && ( !strcmp( "okcancel" , aDialogType ) || !strcmp("yesno", aDialogType) || !strcmp("yesnocancel", aDialogType) ) ) { strcat(lDialogString, "--backtitle \"") ; strcat(lDialogString, "tab: move focus") ; strcat(lDialogString, "\" ") ; } if ( aDialogType && ! strcmp( "okcancel" , aDialogType ) ) { if ( ! aDefaultButton ) { strcat( lDialogString , "--defaultno " ) ; } strcat( lDialogString , "--yes-label \"Ok\" --no-label \"Cancel\" --yesno " ) ; } else if ( aDialogType && ! strcmp( "yesno" , aDialogType ) ) { if ( ! aDefaultButton ) { strcat( lDialogString , "--defaultno " ) ; } strcat( lDialogString , "--yesno " ) ; } else if (aDialogType && !strcmp("yesnocancel", aDialogType)) { if (!aDefaultButton) { strcat(lDialogString, "--defaultno "); } strcat(lDialogString, "--menu "); } else { strcat( lDialogString , "--msgbox " ) ; } strcat( lDialogString , "\"" ) ; if ( aMessage && strlen(aMessage) ) { tfd_replaceSubStr( aMessage , "\n" , "\\n" , lBuff ) ; strcat(lDialogString, lBuff) ; lBuff[0]='\0'; } strcat(lDialogString, "\" "); if (aDialogType && !strcmp("yesnocancel", aDialogType)) { strcat(lDialogString, "0 60 0 Yes \"\" No \"\""); strcat(lDialogString, "2>>"); } else { strcat(lDialogString, "10 60"); strcat(lDialogString, " && echo 1 > "); } strcpy(lDialogFile, getenv("TEMP")); strcat(lDialogFile, "\\tinyfd.txt"); strcat(lDialogString, lDialogFile); /*if (tinyfd_verbose) printf( "lDialogString: %s\n" , lDialogString ) ;*/ system( lDialogString ) ; if (!(lIn = fopen(lDialogFile, "r"))) { remove(lDialogFile); return 0 ; } while (fgets(lBuff, sizeof(lBuff), lIn) != NULL) {} fclose(lIn); remove(lDialogFile); if ( lBuff[strlen( lBuff ) -1] == '\n' ) { lBuff[strlen( lBuff ) -1] = '\0' ; } /* if (tinyfd_verbose) printf("lBuff: %s\n", lBuff); */ if ( ! strlen(lBuff) ) { return 0; } if (aDialogType && !strcmp("yesnocancel", aDialogType)) { if (lBuff[0] == 'Y') return 1; else return 2; } return 1; } static int inputBoxWinConsole( char * aoBuff , char const * aTitle , /* NULL or "" */ char const * aMessage , /* NULL or "" may NOT contain \n nor \t */ char const * aDefaultInput ) /* "" , if NULL it's a passwordBox */ { char lDialogString[MAX_PATH_OR_CMD]; char lDialogFile[MAX_PATH_OR_CMD]; FILE * lIn; int lResult; strcpy(lDialogFile, getenv("TEMP")); strcat(lDialogFile, "\\tinyfd.txt"); strcpy(lDialogString , "echo|set /p=1 >" ) ; strcat(lDialogString, lDialogFile); strcat( lDialogString , " & " ) ; strcat( lDialogString , "dialog " ) ; if ( aTitle && strlen(aTitle) ) { strcat(lDialogString, "--title \"") ; strcat(lDialogString, aTitle) ; strcat(lDialogString, "\" ") ; } strcat(lDialogString, "--backtitle \"") ; strcat(lDialogString, "tab: move focus") ; if ( ! aDefaultInput ) { strcat(lDialogString, " (sometimes nothing, no blink nor star, is shown in text field)") ; } strcat(lDialogString, "\" ") ; if ( ! aDefaultInput ) { strcat( lDialogString , "--insecure --passwordbox" ) ; } else { strcat( lDialogString , "--inputbox" ) ; } strcat( lDialogString , " \"" ) ; if ( aMessage && strlen(aMessage) ) { strcat(lDialogString, aMessage) ; } strcat(lDialogString,"\" 10 60 ") ; if ( aDefaultInput && strlen(aDefaultInput) ) { strcat(lDialogString, "\"") ; strcat(lDialogString, aDefaultInput) ; strcat(lDialogString, "\" ") ; } strcat(lDialogString, "2>>"); strcpy(lDialogFile, getenv("TEMP")); strcat(lDialogFile, "\\tinyfd.txt"); strcat(lDialogString, lDialogFile); strcat(lDialogString, " || echo 0 > "); strcat(lDialogString, lDialogFile); /* printf( "lDialogString: %s\n" , lDialogString ) ; */ system( lDialogString ) ; if (!(lIn = fopen(lDialogFile, "r"))) { remove(lDialogFile); aoBuff[0] = '\0'; return 0; } while (fgets(aoBuff, MAX_PATH_OR_CMD, lIn) != NULL) {} fclose(lIn); wipefile(lDialogFile); remove(lDialogFile); if ( aoBuff[strlen( aoBuff ) -1] == '\n' ) { aoBuff[strlen( aoBuff ) -1] = '\0' ; } /* printf( "aoBuff: %s\n" , aoBuff ) ; */ /* printf( "aoBuff: %s len: %lu \n" , aoBuff , strlen(aoBuff) ) ; */ lResult = strncmp( aoBuff , "1" , 1) ? 0 : 1 ; /* printf( "lResult: %d \n" , lResult ) ; */ if ( ! lResult ) { aoBuff[0] = '\0'; return 0 ; } /* printf( "aoBuff+1: %s\n" , aoBuff+1 ) ; */ strcpy(aoBuff, aoBuff+3); return 1; } static char * saveFileDialogWinConsole( char * aoBuff , char const * aTitle , /* NULL or "" */ char const * aDefaultPathAndFile ) /* NULL or "" */ { char lDialogString[MAX_PATH_OR_CMD]; char lPathAndFile[MAX_PATH_OR_CMD] = ""; FILE * lIn; strcpy( lDialogString , "dialog " ) ; if ( aTitle && strlen(aTitle) ) { strcat(lDialogString, "--title \"") ; strcat(lDialogString, aTitle) ; strcat(lDialogString, "\" ") ; } strcat(lDialogString, "--backtitle \"") ; strcat(lDialogString, "tab: focus | /: populate | spacebar: fill text field | ok: TEXT FIELD ONLY") ; strcat(lDialogString, "\" ") ; strcat( lDialogString , "--fselect \"" ) ; if ( aDefaultPathAndFile && strlen(aDefaultPathAndFile) ) { /* dialog.exe uses unix separators even on windows */ strcpy(lPathAndFile, aDefaultPathAndFile); replaceChr( lPathAndFile , '\\' , '/' ) ; } /* dialog.exe needs at least one separator */ if ( ! strchr(lPathAndFile, '/') ) { strcat(lDialogString, "./") ; } strcat(lDialogString, lPathAndFile) ; strcat(lDialogString, "\" 0 60 2>"); strcpy(lPathAndFile, getenv("TEMP")); strcat(lPathAndFile, "\\tinyfd.txt"); strcat(lDialogString, lPathAndFile); /* printf( "lDialogString: %s\n" , lDialogString ) ; */ system( lDialogString ) ; if (!(lIn = fopen(lPathAndFile, "r"))) { remove(lPathAndFile); return NULL; } while (fgets(aoBuff, MAX_PATH_OR_CMD, lIn) != NULL) {} fclose(lIn); remove(lPathAndFile); replaceChr( aoBuff , '/' , '\\' ) ; /* printf( "aoBuff: %s\n" , aoBuff ) ; */ getLastName(lDialogString,aoBuff); if ( ! strlen(lDialogString) ) { return NULL; } return aoBuff; } static char * openFileDialogWinConsole( char const * aTitle , /* NULL or "" */ char const * aDefaultPathAndFile ) /* NULL or "" */ { char lFilterPatterns[MAX_PATH_OR_CMD] = ""; char lDialogString[MAX_PATH_OR_CMD] ; FILE * lIn; static char aoBuff[MAX_PATH_OR_CMD]; strcpy( lDialogString , "dialog " ) ; if ( aTitle && strlen(aTitle) ) { strcat(lDialogString, "--title \"") ; strcat(lDialogString, aTitle) ; strcat(lDialogString, "\" ") ; } strcat(lDialogString, "--backtitle \"") ; strcat(lDialogString, "tab: focus | /: populate | spacebar: fill text field | ok: TEXT FIELD ONLY") ; strcat(lDialogString, "\" ") ; strcat( lDialogString , "--fselect \"" ) ; if ( aDefaultPathAndFile && strlen(aDefaultPathAndFile) ) { /* dialog.exe uses unix separators even on windows */ strcpy(lFilterPatterns, aDefaultPathAndFile); replaceChr( lFilterPatterns , '\\' , '/' ) ; } /* dialog.exe needs at least one separator */ if ( ! strchr(lFilterPatterns, '/') ) { strcat(lDialogString, "./") ; } strcat(lDialogString, lFilterPatterns) ; strcat(lDialogString, "\" 0 60 2>"); strcpy(lFilterPatterns, getenv("TEMP")); strcat(lFilterPatterns, "\\tinyfd.txt"); strcat(lDialogString, lFilterPatterns); /* printf( "lDialogString: %s\n" , lDialogString ) ; */ system( lDialogString ) ; if (!(lIn = fopen(lFilterPatterns, "r"))) { remove(lFilterPatterns); return NULL; } while (fgets(aoBuff, MAX_PATH_OR_CMD, lIn) != NULL) {} fclose(lIn); remove(lFilterPatterns); replaceChr( aoBuff , '/' , '\\' ) ; /* printf( "aoBuff: %s\n" , aoBuff ) ; */ return aoBuff; } static char * selectFolderDialogWinConsole( char * aoBuff , char const * aTitle , /* NULL or "" */ char const * aDefaultPath ) /* NULL or "" */ { char lDialogString[MAX_PATH_OR_CMD] ; char lString[MAX_PATH_OR_CMD] ; FILE * lIn ; strcpy( lDialogString , "dialog " ) ; if ( aTitle && strlen(aTitle) ) { strcat(lDialogString, "--title \"") ; strcat(lDialogString, aTitle) ; strcat(lDialogString, "\" ") ; } strcat(lDialogString, "--backtitle \"") ; strcat(lDialogString, "tab: focus | /: populate | spacebar: fill text field | ok: TEXT FIELD ONLY") ; strcat(lDialogString, "\" ") ; strcat( lDialogString , "--dselect \"" ) ; if ( aDefaultPath && strlen(aDefaultPath) ) { /* dialog.exe uses unix separators even on windows */ strcpy(lString, aDefaultPath) ; ensureFinalSlash(lString); replaceChr( lString , '\\' , '/' ) ; strcat(lDialogString, lString) ; } else { /* dialog.exe needs at least one separator */ strcat(lDialogString, "./") ; } strcat(lDialogString, "\" 0 60 2>"); strcpy(lString, getenv("TEMP")); strcat(lString, "\\tinyfd.txt"); strcat(lDialogString, lString); /* printf( "lDialogString: %s\n" , lDialogString ) ; */ system( lDialogString ) ; if (!(lIn = fopen(lString, "r"))) { remove(lString); return NULL; } while (fgets(aoBuff, MAX_PATH_OR_CMD, lIn) != NULL) {} fclose(lIn); remove(lString); replaceChr( aoBuff , '/' , '\\' ) ; /* printf( "aoBuff: %s\n" , aoBuff ) ; */ return aoBuff; } static void writeUtf8( char const * aUtf8String ) { unsigned long lNum; void * lConsoleHandle; wchar_t * lTmpWChar; lConsoleHandle = GetStdHandle(STD_OUTPUT_HANDLE); lTmpWChar = tinyfd_utf8to16(aUtf8String); (void)WriteConsoleW(lConsoleHandle, lTmpWChar, (DWORD) wcslen(lTmpWChar), &lNum, NULL); } int tinyfd_messageBox( char const * aTitle, /* NULL or "" */ char const * aMessage, /* NULL or "" may contain \n and \t */ char const * aDialogType, /* "ok" "okcancel" "yesno" "yesnocancel" */ char const * aIconType, /* "info" "warning" "error" "question" */ int aDefaultButton) /* 0 for cancel/no , 1 for ok/yes , 2 for no in yesnocancel */ { char lChar; UINT lOriginalCP = 0; UINT lOriginalOutputCP = 0; if (tfd_quoteDetected(aTitle)) return tinyfd_messageBox("INVALID TITLE WITH QUOTES", aMessage, aDialogType, aIconType, aDefaultButton); if (tfd_quoteDetected(aMessage)) return tinyfd_messageBox(aTitle, "INVALID MESSAGE WITH QUOTES", aDialogType, aIconType, aDefaultButton); if ((!tinyfd_forceConsole || !(GetConsoleWindow() || dialogPresent())) && (!getenv("SSH_CLIENT") || getenvDISPLAY())) { if (aTitle&&!strcmp(aTitle, "tinyfd_query")){ strcpy(tinyfd_response, "windows"); return 1; } return messageBoxWinGui(aTitle, aMessage, aDialogType, aIconType, aDefaultButton); } else if (dialogPresent()) { if (aTitle&&!strcmp(aTitle, "tinyfd_query")){ strcpy(tinyfd_response, "dialog"); return 0; } return messageBoxWinConsole( aTitle, aMessage, aDialogType, aIconType, aDefaultButton); } else { if (!tinyfd_winUtf8) { lOriginalCP = GetConsoleCP(); lOriginalOutputCP = GetConsoleOutputCP(); (void)SetConsoleCP(GetACP()); (void)SetConsoleOutputCP(GetACP()); } if (aTitle&&!strcmp(aTitle, "tinyfd_query")){ strcpy(tinyfd_response, "basicinput"); return 0; } if (!gWarningDisplayed && !tinyfd_forceConsole) { gWarningDisplayed = 1; printf("\n\n%s\n", gTitle); printf("%s\n\n", tinyfd_needs); } if (aTitle && strlen(aTitle)) { printf("\n"); if (tinyfd_winUtf8) writeUtf8(aTitle); else printf("%s", aTitle); printf("\n\n"); } if (aDialogType && !strcmp("yesno", aDialogType)) { do { if (aMessage && strlen(aMessage)) { if (tinyfd_winUtf8) writeUtf8(aMessage); else printf("%s", aMessage); printf("\n"); } printf("y/n: "); lChar = (char)tolower(_getch()); printf("\n\n"); } while (lChar != 'y' && lChar != 'n'); if (!tinyfd_winUtf8) { (void)SetConsoleCP(lOriginalCP); (void)SetConsoleOutputCP(lOriginalOutputCP); } return lChar == 'y' ? 1 : 0; } else if (aDialogType && !strcmp("okcancel", aDialogType)) { do { if (aMessage && strlen(aMessage)) { if (tinyfd_winUtf8) writeUtf8(aMessage); else printf("%s", aMessage); printf("\n"); } printf("[O]kay/[C]ancel: "); lChar = (char)tolower(_getch()); printf("\n\n"); } while (lChar != 'o' && lChar != 'c'); if (!tinyfd_winUtf8) { (void)SetConsoleCP(lOriginalCP); (void)SetConsoleOutputCP(lOriginalOutputCP); } return lChar == 'o' ? 1 : 0; } else if (aDialogType && !strcmp("yesnocancel", aDialogType)) { do { if (aMessage && strlen(aMessage)) { if (tinyfd_winUtf8) writeUtf8(aMessage); else printf("%s", aMessage); printf("\n"); } printf("[Y]es/[N]o/[C]ancel: "); lChar = (char)tolower(_getch()); printf("\n\n"); } while (lChar != 'y' && lChar != 'n' && lChar != 'c'); if (!tinyfd_winUtf8) { (void)SetConsoleCP(lOriginalCP); (void)SetConsoleOutputCP(lOriginalOutputCP); } return (lChar == 'y') ? 1 : (lChar == 'n') ? 2 : 0; } else { if (aMessage && strlen(aMessage)) { if (tinyfd_winUtf8) writeUtf8(aMessage); else printf("%s", aMessage); printf("\n\n"); } printf("press enter to continue "); lChar = (char)_getch(); printf("\n\n"); if (!tinyfd_winUtf8) { (void)SetConsoleCP(lOriginalCP); (void)SetConsoleOutputCP(lOriginalOutputCP); } return 1; } } } /* return has only meaning for tinyfd_query */ int tinyfd_notifyPopup( char const * aTitle, /* NULL or "" */ char const * aMessage , /* NULL or "" may contain \n \t */ char const * aIconType ) /* "info" "warning" "error" */ { if (tfd_quoteDetected(aTitle)) return tinyfd_notifyPopup("INVALID TITLE WITH QUOTES", aMessage, aIconType); if (tfd_quoteDetected(aMessage)) return tinyfd_notifyPopup(aTitle, "INVALID MESSAGE WITH QUOTES", aIconType); if ( powershellPresent() && (!tinyfd_forceConsole || !( GetConsoleWindow() || dialogPresent())) && (!getenv("SSH_CLIENT") || getenvDISPLAY())) { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"windows");return 1;} return notifyWinGui(aTitle, aMessage, aIconType); } else return tinyfd_messageBox(aTitle, aMessage, "ok" , aIconType, 0); } /* returns NULL on cancel */ char * tinyfd_inputBox( char const * aTitle , /* NULL or "" */ char const * aMessage , /* NULL or "" (\n and \t have no effect) */ char const * aDefaultInput ) /* "" , if NULL it's a passwordBox */ { static char lBuff[MAX_PATH_OR_CMD] = ""; char * lEOF; DWORD mode = 0; HANDLE hStdin = GetStdHandle(STD_INPUT_HANDLE); unsigned long lNum; void * lConsoleHandle; char * lTmpChar; wchar_t lBuffW[1024]; UINT lOriginalCP = 0; UINT lOriginalOutputCP = 0; if (!aTitle && !aMessage && !aDefaultInput) return lBuff; /* now I can fill lBuff from outside */ if (tfd_quoteDetected(aTitle)) return tinyfd_inputBox("INVALID TITLE WITH QUOTES", aMessage, aDefaultInput); if (tfd_quoteDetected(aMessage)) return tinyfd_inputBox(aTitle, "INVALID MESSAGE WITH QUOTES", aDefaultInput); if (tfd_quoteDetected(aDefaultInput)) return tinyfd_inputBox(aTitle, aMessage, "INVALID DEFAULT_INPUT WITH QUOTES"); mode = 0; hStdin = GetStdHandle(STD_INPUT_HANDLE); if ((!tinyfd_forceConsole || !( GetConsoleWindow() || dialogPresent())) && (!getenv("SSH_CLIENT") || getenvDISPLAY())) { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"windows");return (char *)1;} lBuff[0]='\0'; if (inputBoxWinGui(lBuff, aTitle, aMessage, aDefaultInput)) return lBuff; else return NULL; } else if ( dialogPresent() ) { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"dialog");return (char *)0;} lBuff[0]='\0'; if (inputBoxWinConsole(lBuff, aTitle, aMessage, aDefaultInput) ) return lBuff; else return NULL; } else { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"basicinput");return (char *)0;} lBuff[0]='\0'; if (!gWarningDisplayed && !tinyfd_forceConsole) { gWarningDisplayed = 1 ; printf("\n\n%s\n", gTitle); printf("%s\n\n", tinyfd_needs); } if (!tinyfd_winUtf8) { lOriginalCP = GetConsoleCP(); lOriginalOutputCP = GetConsoleOutputCP(); (void)SetConsoleCP(GetACP()); (void)SetConsoleOutputCP(GetACP()); } if (aTitle && strlen(aTitle)) { printf("\n"); if (tinyfd_winUtf8) writeUtf8(aTitle); else printf("%s", aTitle); printf("\n\n"); } if ( aMessage && strlen(aMessage) ) { if (tinyfd_winUtf8) writeUtf8(aMessage); else printf("%s", aMessage); printf("\n"); } printf("(ctrl-Z + enter to cancel): "); if ( ! aDefaultInput ) { (void) GetConsoleMode(hStdin, &mode); (void) SetConsoleMode(hStdin, mode & (~ENABLE_ECHO_INPUT)); } if (tinyfd_winUtf8) { lConsoleHandle = GetStdHandle(STD_INPUT_HANDLE); (void) ReadConsoleW(lConsoleHandle, lBuffW, MAX_PATH_OR_CMD, &lNum, NULL); if (!aDefaultInput) { (void)SetConsoleMode(hStdin, mode); printf("\n"); } lBuffW[lNum] = '\0'; if (lBuffW[wcslen(lBuffW) - 1] == '\n') lBuffW[wcslen(lBuffW) - 1] = '\0'; if (lBuffW[wcslen(lBuffW) - 1] == '\r') lBuffW[wcslen(lBuffW) - 1] = '\0'; lTmpChar = tinyfd_utf16to8(lBuffW); if (lTmpChar) { strcpy(lBuff, lTmpChar); return lBuff; } else return NULL; } else { lEOF = fgets(lBuff, MAX_PATH_OR_CMD, stdin); if (!aDefaultInput) { (void)SetConsoleMode(hStdin, mode); printf("\n"); } if (!tinyfd_winUtf8) { (void)SetConsoleCP(lOriginalCP); (void)SetConsoleOutputCP(lOriginalOutputCP); } if (!lEOF) { return NULL; } printf("\n"); if (strchr(lBuff, 27)) { return NULL; } if (lBuff[strlen(lBuff) - 1] == '\n') { lBuff[strlen(lBuff) - 1] = '\0'; } return lBuff; } } } char * tinyfd_saveFileDialog( char const * aTitle , /* NULL or "" */ char const * aDefaultPathAndFile , /* NULL or "" */ int aNumOfFilterPatterns , /* 0 */ char const * const * aFilterPatterns , /* NULL or {"*.jpg","*.png"} */ char const * aSingleFilterDescription ) /* NULL or "image files" */ { static char lBuff[MAX_PATH_OR_CMD] ; char lString[MAX_PATH_OR_CMD] ; char * p ; char * lPointerInputBox; int i; lBuff[0]='\0'; if (tfd_quoteDetected(aTitle)) return tinyfd_saveFileDialog("INVALID TITLE WITH QUOTES", aDefaultPathAndFile, aNumOfFilterPatterns, aFilterPatterns, aSingleFilterDescription); if (tfd_quoteDetected(aDefaultPathAndFile)) return tinyfd_saveFileDialog(aTitle, "INVALID DEFAULT_PATH WITH QUOTES", aNumOfFilterPatterns, aFilterPatterns, aSingleFilterDescription); if (tfd_quoteDetected(aSingleFilterDescription)) return tinyfd_saveFileDialog(aTitle, aDefaultPathAndFile, aNumOfFilterPatterns, aFilterPatterns, "INVALID FILTER_DESCRIPTION WITH QUOTES"); for (i = 0; i < aNumOfFilterPatterns; i++) { if (tfd_quoteDetected(aFilterPatterns[i])) return tinyfd_saveFileDialog("INVALID FILTER_PATTERN WITH QUOTES", aDefaultPathAndFile, 0, NULL, NULL); } if ( ( !tinyfd_forceConsole || !( GetConsoleWindow() || dialogPresent() ) ) && (!getenv("SSH_CLIENT") || getenvDISPLAY())) { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"windows");return (char *)1;} p = saveFileDialogWinGui(lBuff, aTitle, aDefaultPathAndFile, aNumOfFilterPatterns, (char const * const *)aFilterPatterns, aSingleFilterDescription); } else if (dialogPresent()) { if (aTitle&&!strcmp(aTitle, "tinyfd_query")){ strcpy(tinyfd_response, "dialog"); return (char *)0; } p = saveFileDialogWinConsole(lBuff, aTitle, aDefaultPathAndFile); } else { if (aTitle&&!strcmp(aTitle, "tinyfd_query")){ strcpy(tinyfd_response, "basicinput"); return (char *)0; } strcpy(lBuff, "Save file in "); strcat(lBuff, getCurDir()); lPointerInputBox = tinyfd_inputBox(NULL,NULL,NULL); /* obtain a pointer on the current content of tinyfd_inputBox */ if (lPointerInputBox) strcpy(lString, lPointerInputBox); /* preserve the current content of tinyfd_inputBox */ p = tinyfd_inputBox(aTitle, lBuff, ""); if (p) strcpy(lBuff, p); else lBuff[0] = '\0'; if (lPointerInputBox) strcpy(lPointerInputBox, lString); /* restore its previous content to tinyfd_inputBox */ p = lBuff; } if ( ! p || ! strlen( p ) ) { return NULL; } getPathWithoutFinalSlash( lString , p ) ; if ( strlen( lString ) && ! dirExists( lString ) ) { return NULL ; } getLastName(lString,p); if ( ! filenameValid(lString) ) { return NULL; } return p ; } /* in case of multiple files, the separator is | */ char * tinyfd_openFileDialog( char const * aTitle , /* NULL or "" */ char const * aDefaultPathAndFile, /* NULL or "" */ int aNumOfFilterPatterns , /* 0 */ char const * const * aFilterPatterns, /* NULL or {"*.jpg","*.png"} */ char const * aSingleFilterDescription, /* NULL or "image files" */ int aAllowMultipleSelects ) /* 0 or 1 */ { char lString[MAX_PATH_OR_CMD]; char lBuff[MAX_PATH_OR_CMD]; char * p; char * lPointerInputBox; int i; if (tfd_quoteDetected(aTitle)) return tinyfd_openFileDialog("INVALID TITLE WITH QUOTES", aDefaultPathAndFile, aNumOfFilterPatterns, aFilterPatterns, aSingleFilterDescription, aAllowMultipleSelects); if (tfd_quoteDetected(aDefaultPathAndFile)) return tinyfd_openFileDialog(aTitle, "INVALID DEFAULT_PATH WITH QUOTES", aNumOfFilterPatterns, aFilterPatterns, aSingleFilterDescription, aAllowMultipleSelects); if (tfd_quoteDetected(aSingleFilterDescription)) return tinyfd_openFileDialog(aTitle, aDefaultPathAndFile, aNumOfFilterPatterns, aFilterPatterns, "INVALID FILTER_DESCRIPTION WITH QUOTES", aAllowMultipleSelects); for (i = 0; i < aNumOfFilterPatterns; i++) { if (tfd_quoteDetected(aFilterPatterns[i])) return tinyfd_openFileDialog("INVALID FILTER_PATTERN WITH QUOTES", aDefaultPathAndFile, 0, NULL, NULL, aAllowMultipleSelects); } if ( ( !tinyfd_forceConsole || !( GetConsoleWindow() || dialogPresent() ) ) && (!getenv("SSH_CLIENT") || getenvDISPLAY())) { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"windows");return (char *)1;} p = openFileDialogWinGui( aTitle, aDefaultPathAndFile, aNumOfFilterPatterns, (char const * const *)aFilterPatterns, aSingleFilterDescription, aAllowMultipleSelects); } else if (dialogPresent()) { if (aTitle&&!strcmp(aTitle, "tinyfd_query")){ strcpy(tinyfd_response, "dialog"); return (char *)0; } p = openFileDialogWinConsole(aTitle, aDefaultPathAndFile); } else { if (aTitle&&!strcmp(aTitle, "tinyfd_query")){ strcpy(tinyfd_response, "basicinput"); return (char *)0; } strcpy(lBuff, "Open file from "); strcat(lBuff, getCurDir()); lPointerInputBox = tinyfd_inputBox(NULL, NULL, NULL); /* obtain a pointer on the current content of tinyfd_inputBox */ if (lPointerInputBox) strcpy(lString, lPointerInputBox); /* preserve the current content of tinyfd_inputBox */ p = tinyfd_inputBox(aTitle, lBuff, ""); if (p) strcpy(lBuff, p); else lBuff[0] = '\0'; if (lPointerInputBox) strcpy(lPointerInputBox, lString); /* restore its previous content to tinyfd_inputBox */ p = lBuff; } if ( ! p || ! strlen( p ) ) { return NULL; } if ( aAllowMultipleSelects && strchr(p, '|') ) { p = ensureFilesExist( (char *) p , p ) ; } else if ( ! fileExists(p) ) { return NULL ; } /* printf( "lBuff3: %s\n" , p ) ; */ return p ; } char * tinyfd_selectFolderDialog( char const * aTitle , /* NULL or "" */ char const * aDefaultPath ) /* NULL or "" */ { static char lBuff[MAX_PATH_OR_CMD]; char * p; char * lPointerInputBox; char lString[MAX_PATH_OR_CMD]; if (tfd_quoteDetected(aTitle)) return tinyfd_selectFolderDialog("INVALID TITLE WITH QUOTES", aDefaultPath); if (tfd_quoteDetected(aDefaultPath)) return tinyfd_selectFolderDialog(aTitle, "INVALID DEFAULT_PATH WITH QUOTES"); if ( ( !tinyfd_forceConsole || !( GetConsoleWindow() || dialogPresent() ) ) && (!getenv("SSH_CLIENT") || getenvDISPLAY())) { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"windows");return (char *)1;} p = selectFolderDialogWinGui(lBuff, aTitle, aDefaultPath); } else if (dialogPresent()) { if (aTitle&&!strcmp(aTitle, "tinyfd_query")){ strcpy(tinyfd_response, "dialog"); return (char *)0; } p = selectFolderDialogWinConsole(lBuff, aTitle, aDefaultPath); } else { if (aTitle&&!strcmp(aTitle, "tinyfd_query")){ strcpy(tinyfd_response, "basicinput"); return (char *)0; } strcpy(lBuff, "Select folder from "); strcat(lBuff, getCurDir()); lPointerInputBox = tinyfd_inputBox(NULL, NULL, NULL); /* obtain a pointer on the current content of tinyfd_inputBox */ if (lPointerInputBox) strcpy(lString, lPointerInputBox); /* preserve the current content of tinyfd_inputBox */ p = tinyfd_inputBox(aTitle, lBuff, ""); if (p) strcpy(lBuff, p); else lBuff[0] = '\0'; if (lPointerInputBox) strcpy(lPointerInputBox, lString); /* restore its previous content to tinyfd_inputBox */ p = lBuff; } if ( ! p || ! strlen( p ) || ! dirExists( p ) ) { return NULL ; } return p ; } /* returns the hexcolor as a string "#FF0000" */ /* aoResultRGB also contains the result */ /* aDefaultRGB is used only if aDefaultHexRGB is NULL */ /* aDefaultRGB and aoResultRGB can be the same array */ char * tinyfd_colorChooser( char const * aTitle, /* NULL or "" */ char const * aDefaultHexRGB, /* NULL or "#FF0000"*/ unsigned char const aDefaultRGB[3], /* { 0 , 255 , 255 } */ unsigned char aoResultRGB[3]) /* { 0 , 0 , 0 } */ { static char lDefaultHexRGB[16]; int i; char * p ; char * lPointerInputBox; char lString[MAX_PATH_OR_CMD]; lDefaultHexRGB[0] = '\0'; if (tfd_quoteDetected(aTitle)) return tinyfd_colorChooser("INVALID TITLE WITH QUOTES", aDefaultHexRGB, aDefaultRGB, aoResultRGB); if (tfd_quoteDetected(aDefaultHexRGB)) return tinyfd_colorChooser(aTitle, "INVALID DEFAULT_HEX_RGB WITH QUOTES", aDefaultRGB, aoResultRGB); if ( (!tinyfd_forceConsole || !( GetConsoleWindow() || dialogPresent()) ) && (!getenv("SSH_CLIENT") || getenvDISPLAY())) { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"windows");return (char *)1;} p = colorChooserWinGui(aTitle, aDefaultHexRGB, aDefaultRGB, aoResultRGB); if (p) { strcpy(lDefaultHexRGB, p); return lDefaultHexRGB; } return NULL; } else if (dialogPresent()) { if (aTitle&&!strcmp(aTitle, "tinyfd_query")){ strcpy(tinyfd_response, "dialog"); return (char *)0; } } else { if (aTitle&&!strcmp(aTitle, "tinyfd_query")){ strcpy(tinyfd_response, "basicinput"); return (char *)0; } } if (aDefaultHexRGB) { strncpy(lDefaultHexRGB, aDefaultHexRGB,7); lDefaultHexRGB[7]='\0'; } else { RGB2Hex(aDefaultRGB, lDefaultHexRGB); } lPointerInputBox = tinyfd_inputBox(NULL, NULL, NULL); /* obtain a pointer on the current content of tinyfd_inputBox */ if (lPointerInputBox) strcpy(lString, lPointerInputBox); /* preserve the current content of tinyfd_inputBox */ p = tinyfd_inputBox(aTitle, "Enter hex rgb color (i.e. #f5ca20)", lDefaultHexRGB); if ( !p || (strlen(p) != 7) || (p[0] != '#') ) { return NULL ; } for ( i = 1 ; i < 7 ; i ++ ) { if ( ! isxdigit( (int) p[i] ) ) { return NULL ; } } Hex2RGB(p,aoResultRGB); strcpy(lDefaultHexRGB, p); if (lPointerInputBox) strcpy(lPointerInputBox, lString); /* restore its previous content to tinyfd_inputBox */ return lDefaultHexRGB; } #else /* unix */ static char gPython2Name[16]; static char gPython3Name[16]; static char gPythonName[16]; int tfd_isDarwin(void) { static int lsIsDarwin = -1 ; struct utsname lUtsname ; if ( lsIsDarwin < 0 ) { lsIsDarwin = !uname(&lUtsname) && !strcmp(lUtsname.sysname,"Darwin") ; } return lsIsDarwin ; } static int dirExists( char const * aDirPath ) { DIR * lDir ; if ( ! aDirPath || ! strlen( aDirPath ) ) return 0 ; lDir = opendir( aDirPath ) ; if ( ! lDir ) { return 0 ; } closedir( lDir ) ; return 1 ; } static int detectPresence( char const * aExecutable ) { char lBuff[MAX_PATH_OR_CMD] ; char lTestedString[MAX_PATH_OR_CMD] = "which " ; FILE * lIn ; #ifdef _GNU_SOURCE char* lAllocatedCharString; int lSubstringUndetected; #endif strcat( lTestedString , aExecutable ) ; strcat( lTestedString, " 2>/dev/null "); lIn = popen( lTestedString , "r" ) ; if ( ( fgets( lBuff , sizeof( lBuff ) , lIn ) != NULL ) && ( ! strchr( lBuff , ':' ) ) && ( strncmp(lBuff, "no ", 3) ) ) { /* present */ pclose( lIn ) ; #ifdef _GNU_SOURCE /*to bypass this, just comment out "#define _GNU_SOURCE" at the top of the file*/ if ( lBuff[strlen( lBuff ) -1] == '\n' ) lBuff[strlen( lBuff ) -1] = '\0' ; lAllocatedCharString = realpath(lBuff,NULL); /*same as canonicalize_file_name*/ lSubstringUndetected = ! strstr(lAllocatedCharString, aExecutable); free(lAllocatedCharString); if (lSubstringUndetected) { if (tinyfd_verbose) printf("detectPresence %s %d\n", aExecutable, 0); return 0; } #endif /*_GNU_SOURCE*/ if (tinyfd_verbose) printf("detectPresence %s %d\n", aExecutable, 1); return 1 ; } else { pclose( lIn ) ; if (tinyfd_verbose) printf("detectPresence %s %d\n", aExecutable, 0); return 0 ; } } static char * getVersion( char const * aExecutable ) /*version must be first numeral*/ { static char lBuff[MAX_PATH_OR_CMD] ; char lTestedString[MAX_PATH_OR_CMD] ; FILE * lIn ; char * lTmp ; strcpy( lTestedString , aExecutable ) ; strcat( lTestedString , " --version" ) ; lIn = popen( lTestedString , "r" ) ; lTmp = fgets( lBuff , sizeof( lBuff ) , lIn ) ; pclose( lIn ) ; lTmp += strcspn(lTmp,"0123456789"); /* printf("lTmp:%s\n", lTmp); */ return lTmp ; } static int * getMajorMinorPatch( char const * aExecutable ) { static int lArray[3] ; char * lTmp ; lTmp = (char *) getVersion(aExecutable); lArray[0] = atoi( strtok(lTmp," ,.-") ) ; /* printf("lArray0 %d\n", lArray[0]); */ lArray[1] = atoi( strtok(0," ,.-") ) ; /* printf("lArray1 %d\n", lArray[1]); */ lArray[2] = atoi( strtok(0," ,.-") ) ; /* printf("lArray2 %d\n", lArray[2]); */ if ( !lArray[0] && !lArray[1] && !lArray[2] ) return NULL; return lArray ; } static int tryCommand( char const * aCommand ) { char lBuff[MAX_PATH_OR_CMD] ; FILE * lIn ; lIn = popen( aCommand , "r" ) ; if ( fgets( lBuff , sizeof( lBuff ) , lIn ) == NULL ) { /* present */ pclose( lIn ) ; return 1 ; } else { pclose( lIn ) ; return 0 ; } } static int isTerminalRunning(void) { static int lIsTerminalRunning = -1 ; if ( lIsTerminalRunning < 0 ) { lIsTerminalRunning = isatty(1); if (tinyfd_verbose) printf("isTerminalRunning %d\n", lIsTerminalRunning ); } return lIsTerminalRunning; } static char * dialogNameOnly(void) { static char lDialogName[128] = "*" ; if ( lDialogName[0] == '*' ) { if (!tinyfd_allowCursesDialogs) { strcpy(lDialogName , "" ); } else if ( tfd_isDarwin() && * strcpy(lDialogName , "/opt/local/bin/dialog" ) && detectPresence( lDialogName ) ) {} else if ( * strcpy(lDialogName , "dialog" ) && detectPresence( lDialogName ) ) {} else { strcpy(lDialogName , "" ); } } return lDialogName ; } int isDialogVersionBetter09b(void) { char const * lDialogName ; char * lVersion ; int lMajor ; int lMinor ; int lDate ; int lResult ; char * lMinorP ; char * lLetter ; char lBuff[128] ; /*char lTest[128] = " 0.9b-20031126" ;*/ lDialogName = dialogNameOnly() ; if ( ! strlen(lDialogName) || !(lVersion = (char *) getVersion(lDialogName)) ) return 0 ; /*lVersion = lTest ;*/ /*printf("lVersion %s\n", lVersion);*/ strcpy(lBuff,lVersion); lMajor = atoi( strtok(lVersion," ,.-") ) ; /*printf("lMajor %d\n", lMajor);*/ lMinorP = strtok(0," ,.-abcdefghijklmnopqrstuvxyz"); lMinor = atoi( lMinorP ) ; /*printf("lMinor %d\n", lMinor );*/ lDate = atoi( strtok(0," ,.-") ) ; if (lDate<0) lDate = - lDate; /*printf("lDate %d\n", lDate);*/ lLetter = lMinorP + strlen(lMinorP) ; strcpy(lVersion,lBuff); strtok(lLetter," ,.-"); /*printf("lLetter %s\n", lLetter);*/ lResult = (lMajor > 0) || ( ( lMinor == 9 ) && (*lLetter == 'b') && (lDate >= 20031126) ); /*printf("lResult %d\n", lResult);*/ return lResult; } static int whiptailPresentOnly(void) { static int lWhiptailPresent = -1 ; if (!tinyfd_allowCursesDialogs) return 0; if ( lWhiptailPresent < 0 ) { lWhiptailPresent = detectPresence( "whiptail" ) ; } return lWhiptailPresent ; } static char * terminalName(void) { static char lTerminalName[128] = "*" ; char lShellName[64] = "*" ; int * lArray; if ( lTerminalName[0] == '*' ) { if ( detectPresence( "bash" ) ) { strcpy(lShellName , "bash -c " ) ; /*good for basic input*/ } else if ( strlen(dialogNameOnly()) || whiptailPresentOnly() ) { strcpy(lShellName , "sh -c " ) ; /*good enough for dialog & whiptail*/ } else { strcpy(lTerminalName , "" ) ; return NULL ; } if ( tfd_isDarwin() ) { if ( * strcpy(lTerminalName , "/opt/X11/bin/xterm" ) && detectPresence( lTerminalName ) ) { strcat(lTerminalName , " -fa 'DejaVu Sans Mono' -fs 10 -title tinyfiledialogs -e " ) ; strcat(lTerminalName , lShellName ) ; } else { strcpy(lTerminalName , "" ) ; } } else if ( * strcpy(lTerminalName,"xterm") /*good (small without parameters)*/ && detectPresence(lTerminalName) ) { strcat(lTerminalName , " -fa 'DejaVu Sans Mono' -fs 10 -title tinyfiledialogs -e " ) ; strcat(lTerminalName , lShellName ) ; } else if ( * strcpy(lTerminalName,"terminator") /*good*/ && detectPresence(lTerminalName) ) { strcat(lTerminalName , " -x " ) ; strcat(lTerminalName , lShellName ) ; } else if ( * strcpy(lTerminalName,"lxterminal") /*good*/ && detectPresence(lTerminalName) ) { strcat(lTerminalName , " -e " ) ; strcat(lTerminalName , lShellName ) ; } else if ( * strcpy(lTerminalName,"konsole") /*good*/ && detectPresence(lTerminalName) ) { strcat(lTerminalName , " -e " ) ; strcat(lTerminalName , lShellName ) ; } else if ( * strcpy(lTerminalName,"kterm") /*good*/ && detectPresence(lTerminalName) ) { strcat(lTerminalName , " -e " ) ; strcat(lTerminalName , lShellName ) ; } else if ( * strcpy(lTerminalName,"tilix") /*good*/ && detectPresence(lTerminalName) ) { strcat(lTerminalName , " -e " ) ; strcat(lTerminalName , lShellName ) ; } else if ( * strcpy(lTerminalName,"xfce4-terminal") /*good*/ && detectPresence(lTerminalName) ) { strcat(lTerminalName , " -x " ) ; strcat(lTerminalName , lShellName ) ; } else if ( * strcpy(lTerminalName,"mate-terminal") /*good*/ && detectPresence(lTerminalName) ) { strcat(lTerminalName , " -x " ) ; strcat(lTerminalName , lShellName ) ; } else if ( * strcpy(lTerminalName,"Eterm") /*good*/ && detectPresence(lTerminalName) ) { strcat(lTerminalName , " -e " ) ; strcat(lTerminalName , lShellName ) ; } else if ( * strcpy(lTerminalName,"evilvte") /*good*/ && detectPresence(lTerminalName) ) { strcat(lTerminalName , " -e " ) ; strcat(lTerminalName , lShellName ) ; } else if ( * strcpy(lTerminalName,"pterm") /*good (only letters)*/ && detectPresence(lTerminalName) ) { strcat(lTerminalName , " -e " ) ; strcat(lTerminalName , lShellName ) ; } else if ( * strcpy(lTerminalName,"gnome-terminal") && detectPresence(lTerminalName) && (lArray = getMajorMinorPatch(lTerminalName)) && ((lArray[0]<3) || (lArray[0]==3 && lArray[1]<=6)) ) { strcat(lTerminalName , " --disable-factory -x " ) ; strcat(lTerminalName , lShellName ) ; } else { strcpy(lTerminalName , "" ) ; } /* bad: koi rxterm guake tilda vala-terminal qterminal aterm Terminal terminology sakura lilyterm weston-terminal roxterm termit xvt rxvt mrxvt urxvt */ } if ( strlen(lTerminalName) ) { return lTerminalName ; } else { return NULL ; } } static char * dialogName(void) { char * lDialogName ; lDialogName = dialogNameOnly( ) ; if ( strlen(lDialogName) && ( isTerminalRunning() || terminalName() ) ) { return lDialogName ; } else { return NULL ; } } static int whiptailPresent(void) { int lWhiptailPresent ; lWhiptailPresent = whiptailPresentOnly( ) ; if ( lWhiptailPresent && ( isTerminalRunning() || terminalName() ) ) { return lWhiptailPresent ; } else { return 0 ; } } static int graphicMode(void) { return !( tinyfd_forceConsole && (isTerminalRunning() || terminalName()) ) && ( getenvDISPLAY() || (tfd_isDarwin() && (!getenv("SSH_TTY") || getenvDISPLAY() ) ) ) ; } static int pactlPresent(void) { static int lPactlPresent = -1 ; if ( lPactlPresent < 0 ) { lPactlPresent = detectPresence("pactl") ; } return lPactlPresent ; } static int speakertestPresent(void) { static int lSpeakertestPresent = -1 ; if ( lSpeakertestPresent < 0 ) { lSpeakertestPresent = detectPresence("speaker-test") ; } return lSpeakertestPresent ; } static int playPresent() { static int lPlayPresent = -1; if (lPlayPresent < 0) { lPlayPresent = detectPresence("sox"); /*if sox is present, play is ready*/ } return lPlayPresent; } static int beepexePresent() { static int lBeepexePresent = -1; if (lBeepexePresent < 0) { lBeepexePresent = detectPresence("beep.exe"); } return lBeepexePresent; } static int beepPresent(void) { static int lBeepPresent = -1 ; if ( lBeepPresent < 0 ) { lBeepPresent = detectPresence("beep") ; } return lBeepPresent ; } static int xmessagePresent(void) { static int lXmessagePresent = -1 ; if ( lXmessagePresent < 0 ) { lXmessagePresent = detectPresence("xmessage");/*if not tty,not on osxpath*/ } return lXmessagePresent && graphicMode( ) ; } static int gxmessagePresent(void) { static int lGxmessagePresent = -1 ; if ( lGxmessagePresent < 0 ) { lGxmessagePresent = detectPresence("gxmessage") ; } return lGxmessagePresent && graphicMode( ) ; } static int gmessagePresent(void) { static int lGmessagePresent = -1 ; if ( lGmessagePresent < 0 ) { lGmessagePresent = detectPresence("gmessage") ; } return lGmessagePresent && graphicMode( ) ; } static int notifysendPresent(void) { static int lNotifysendPresent = -1 ; if ( lNotifysendPresent < 0 ) { lNotifysendPresent = detectPresence("notify-send") ; } return lNotifysendPresent && graphicMode( ) ; } static int perlPresent(void) { static int lPerlPresent = -1 ; char lBuff[MAX_PATH_OR_CMD] ; FILE * lIn ; if ( lPerlPresent < 0 ) { lPerlPresent = detectPresence("perl") ; if (lPerlPresent) { lIn = popen("perl -MNet::DBus -e \"Net::DBus->session->get_service('org.freedesktop.Notifications')\" 2>&1", "r"); if (fgets(lBuff, sizeof(lBuff), lIn) == NULL) { lPerlPresent = 2; } pclose(lIn); if (tinyfd_verbose) printf("perl-dbus %d\n", lPerlPresent); } } return graphicMode() ? lPerlPresent : 0 ; } static int afplayPresent(void) { static int lAfplayPresent = -1 ; char lBuff[MAX_PATH_OR_CMD] ; FILE * lIn ; if ( lAfplayPresent < 0 ) { lAfplayPresent = detectPresence("afplay") ; if ( lAfplayPresent ) { lIn = popen( "test -e /System/Library/Sounds/Ping.aiff || echo Ping" , "r" ) ; if ( fgets( lBuff , sizeof( lBuff ) , lIn ) == NULL ) { lAfplayPresent = 2 ; } pclose( lIn ) ; if (tinyfd_verbose) printf("afplay %d\n", lAfplayPresent); } } return graphicMode() ? lAfplayPresent : 0 ; } static int xdialogPresent(void) { static int lXdialogPresent = -1 ; if ( lXdialogPresent < 0 ) { lXdialogPresent = detectPresence("Xdialog") ; } return lXdialogPresent && graphicMode( ) ; } static int gdialogPresent(void) { static int lGdialoglPresent = -1 ; if ( lGdialoglPresent < 0 ) { lGdialoglPresent = detectPresence( "gdialog" ) ; } return lGdialoglPresent && graphicMode( ) ; } static int osascriptPresent(void) { static int lOsascriptPresent = -1 ; if ( lOsascriptPresent < 0 ) { gWarningDisplayed |= !!getenv("SSH_TTY"); lOsascriptPresent = detectPresence( "osascript" ) ; } return lOsascriptPresent && graphicMode() && !getenv("SSH_TTY") ; } int tfd_qarmaPresent(void) { static int lQarmaPresent = -1 ; if ( lQarmaPresent < 0 ) { lQarmaPresent = detectPresence("qarma") ; } return lQarmaPresent && graphicMode( ) ; } int tfd_matedialogPresent(void) { static int lMatedialogPresent = -1 ; if ( lMatedialogPresent < 0 ) { lMatedialogPresent = detectPresence("matedialog") ; } return lMatedialogPresent && graphicMode( ) ; } int tfd_shellementaryPresent(void) { static int lShellementaryPresent = -1 ; if ( lShellementaryPresent < 0 ) { lShellementaryPresent = 0 ; /*detectPresence("shellementary"); shellementary is not ready yet */ } return lShellementaryPresent && graphicMode( ) ; } int tfd_xpropPresent(void) { static int lXpropPresent = -1 ; if ( lXpropPresent < 0 ) { lXpropPresent = detectPresence("xprop") ; } return lXpropPresent && graphicMode( ) ; } int tfd_zenityPresent(void) { static int lZenityPresent = -1 ; if ( lZenityPresent < 0 ) { lZenityPresent = detectPresence("zenity") ; } return lZenityPresent && graphicMode( ) ; } int tfd_yadPresent(void) { static int lYadPresent = -1; if (lYadPresent < 0) { lYadPresent = detectPresence("yad"); } return lYadPresent && graphicMode(); } int tfd_zenity3Present(void) { static int lZenity3Present = -1 ; char lBuff[MAX_PATH_OR_CMD] ; FILE * lIn ; int lIntTmp ; if ( lZenity3Present < 0 ) { lZenity3Present = 0 ; if ( tfd_zenityPresent() ) { lIn = popen( "zenity --version" , "r" ) ; if ( fgets( lBuff , sizeof( lBuff ) , lIn ) != NULL ) { if ( atoi(lBuff) >= 3 ) { lZenity3Present = 3 ; lIntTmp = atoi(strtok(lBuff,".")+2 ) ; if ( lIntTmp >= 18 ) { lZenity3Present = 5 ; } else if ( lIntTmp >= 10 ) { lZenity3Present = 4 ; } } else if ( ( atoi(lBuff) == 2 ) && ( atoi(strtok(lBuff,".")+2 ) >= 32 ) ) { lZenity3Present = 2 ; } if (tinyfd_verbose) printf("zenity type %d\n", lZenity3Present); } pclose( lIn ) ; } } return graphicMode() ? lZenity3Present : 0 ; } int tfd_kdialogPresent(void) { static int lKdialogPresent = -1 ; char lBuff[MAX_PATH_OR_CMD] ; FILE * lIn ; char * lDesktop; if ( lKdialogPresent < 0 ) { if ( tfd_zenityPresent() ) { lDesktop = getenv("XDG_SESSION_DESKTOP"); if ( !lDesktop || ( strcmp(lDesktop, "KDE") && strcmp(lDesktop, "lxqt") ) ) { lKdialogPresent = 0 ; return lKdialogPresent ; } } lKdialogPresent = detectPresence("kdialog") ; if ( lKdialogPresent && !getenv("SSH_TTY") ) { lIn = popen( "kdialog --attach 2>&1" , "r" ) ; if ( fgets( lBuff , sizeof( lBuff ) , lIn ) != NULL ) { if ( ! strstr( "Unknown" , lBuff ) ) { lKdialogPresent = 2 ; if (tinyfd_verbose) printf("kdialog-attach %d\n", lKdialogPresent); } } pclose( lIn ) ; if (lKdialogPresent == 2) { lKdialogPresent = 1 ; lIn = popen( "kdialog --passivepopup 2>&1" , "r" ) ; if ( fgets( lBuff , sizeof( lBuff ) , lIn ) != NULL ) { if ( ! strstr( "Unknown" , lBuff ) ) { lKdialogPresent = 2 ; if (tinyfd_verbose) printf("kdialog-popup %d\n", lKdialogPresent); } } pclose( lIn ) ; } } } return graphicMode() ? lKdialogPresent : 0 ; } static int osx9orBetter(void) { static int lOsx9orBetter = -1 ; char lBuff[MAX_PATH_OR_CMD] ; FILE * lIn ; int V,v; if ( lOsx9orBetter < 0 ) { lOsx9orBetter = 0 ; lIn = popen( "osascript -e 'set osver to system version of (system info)'" , "r" ) ; if ( ( fgets( lBuff , sizeof( lBuff ) , lIn ) != NULL ) && ( 2 == sscanf(lBuff, "%d.%d", &V, &v) ) ) { V = V * 100 + v; if ( V >= 1009 ) { lOsx9orBetter = 1 ; } } pclose( lIn ) ; if (tinyfd_verbose) printf("Osx10 = %d, %d = %s\n", lOsx9orBetter, V, lBuff) ; } return lOsx9orBetter ; } static int python3Present(void) { static int lPython3Present = -1 ; int i; if ( lPython3Present < 0 ) { lPython3Present = 0 ; strcpy(gPython3Name , "python3" ) ; if ( detectPresence(gPython3Name) ) lPython3Present = 1; else { for ( i = 9 ; i >= 0 ; i -- ) { sprintf( gPython3Name , "python3.%d" , i ) ; if ( detectPresence(gPython3Name) ) { lPython3Present = 1; break; } } } if (tinyfd_verbose) printf("lPython3Present %d\n", lPython3Present) ; if (tinyfd_verbose) printf("gPython3Name %s\n", gPython3Name) ; } return lPython3Present ; } static int python2Present(void) { static int lPython2Present = -1 ; int i; if ( lPython2Present < 0 ) { lPython2Present = 0 ; strcpy(gPython2Name , "python2" ) ; if ( detectPresence(gPython2Name) ) lPython2Present = 1; else { for ( i = 9 ; i >= 0 ; i -- ) { sprintf( gPython2Name , "python2.%d" , i ) ; if ( detectPresence(gPython2Name) ) { lPython2Present = 1; break; } } } if (tinyfd_verbose) printf("lPython2Present %d\n", lPython2Present) ; if (tinyfd_verbose) printf("gPython2Name %s\n", gPython2Name) ; } return lPython2Present ; } static int tkinter3Present(void) { static int lTkinter3Present = -1 ; char lPythonCommand[256]; char lPythonParams[128] = "-S -c \"try:\n\timport tkinter;\nexcept:\n\tprint(0);\""; if ( lTkinter3Present < 0 ) { lTkinter3Present = 0 ; if ( python3Present() ) { sprintf( lPythonCommand , "%s %s" , gPython3Name , lPythonParams ) ; lTkinter3Present = tryCommand(lPythonCommand) ; } if (tinyfd_verbose) printf("lTkinter3Present %d\n", lTkinter3Present) ; } return lTkinter3Present && graphicMode() && !(tfd_isDarwin() && getenv("SSH_TTY") ); } static int tkinter2Present(void) { static int lTkinter2Present = -1 ; char lPythonCommand[256]; char lPythonParams[128] = "-S -c \"try:\n\timport Tkinter;\nexcept:\n\tprint 0;\""; if ( lTkinter2Present < 0 ) { lTkinter2Present = 0 ; if ( python2Present() ) { sprintf( lPythonCommand , "%s %s" , gPython2Name , lPythonParams ) ; lTkinter2Present = tryCommand(lPythonCommand) ; } if (tinyfd_verbose) printf("lTkinter2Present %d\n", lTkinter2Present) ; } return lTkinter2Present && graphicMode() && !(tfd_isDarwin() && getenv("SSH_TTY") ); } static int pythonDbusPresent(void) { static int lPythonDbusPresent = -1 ; char lPythonCommand[384]; char lPythonParams[256] = "-c \"try:\n\timport dbus;bus=dbus.SessionBus();\ notif=bus.get_object('org.freedesktop.Notifications','/org/freedesktop/Notifications');\ notify=dbus.Interface(notif,'org.freedesktop.Notifications');\nexcept:\n\tprint(0);\""; if (lPythonDbusPresent < 0 ) { lPythonDbusPresent = 0 ; if ( python2Present() ) { strcpy(gPythonName , gPython2Name ) ; sprintf( lPythonCommand , "%s %s" , gPythonName , lPythonParams ) ; lPythonDbusPresent = tryCommand(lPythonCommand) ; } if ( !lPythonDbusPresent && python3Present() ) { strcpy(gPythonName , gPython3Name ) ; sprintf( lPythonCommand , "%s %s" , gPythonName , lPythonParams ) ; lPythonDbusPresent = tryCommand(lPythonCommand) ; } if (tinyfd_verbose) printf("lPythonDbusPresent %d\n", lPythonDbusPresent) ; if (tinyfd_verbose) printf("gPythonName %s\n", gPythonName) ; } return lPythonDbusPresent && graphicMode() && !(tfd_isDarwin() && getenv("SSH_TTY") ); } static void sigHandler(int signum) { FILE * lIn ; if ( ( lIn = popen( "pactl unload-module module-sine" , "r" ) ) ) { pclose( lIn ) ; } if (tinyfd_verbose) printf("tinyfiledialogs caught signal %d\n", signum); } void tinyfd_beep(void) { char lDialogString[256] ; FILE * lIn ; if ( osascriptPresent() ) { if ( afplayPresent() >= 2 ) { strcpy( lDialogString , "afplay /System/Library/Sounds/Ping.aiff") ; } else { strcpy( lDialogString , "osascript -e 'tell application \"System Events\" to beep'") ; } } else if ( pactlPresent() ) { signal(SIGINT, sigHandler); /*strcpy( lDialogString , "pactl load-module module-sine frequency=440;sleep .3;pactl unload-module module-sine" ) ;*/ strcpy( lDialogString , "thnum=$(pactl load-module module-sine frequency=440);sleep .3;pactl unload-module $thnum" ) ; } else if ( speakertestPresent() ) { /*strcpy( lDialogString , "timeout -k .3 .3 speaker-test --frequency 440 --test sine > /dev/tty" ) ;*/ strcpy( lDialogString , "( speaker-test -t sine -f 440 > /dev/tty )& pid=$!;sleep .4; kill -9 $pid" ) ; /*.3 was too short for mac g3*/ } else if (beepexePresent()) { strcpy(lDialogString, "beep.exe 440 300"); } else if (playPresent()) /* play is part of sox */ { strcpy(lDialogString, "play -q -n synth .3 sine 440"); } else if ( beepPresent() ) { strcpy( lDialogString , "beep -f 440 -l 300" ) ; } else { strcpy( lDialogString , "printf '\a' > /dev/tty" ) ; } if (tinyfd_verbose) printf( "lDialogString: %s\n" , lDialogString ) ; if ( ( lIn = popen( lDialogString , "r" ) ) ) { pclose( lIn ) ; } if ( pactlPresent() ) { signal(SIGINT, SIG_DFL); } } int tinyfd_messageBox( char const * aTitle , /* NULL or "" */ char const * aMessage , /* NULL or "" may contain \n and \t */ char const * aDialogType , /* "ok" "okcancel" "yesno" "yesnocancel" */ char const * aIconType , /* "info" "warning" "error" "question" */ int aDefaultButton ) /* 0 for cancel/no , 1 for ok/yes , 2 for no in yesnocancel */ { char lBuff[MAX_PATH_OR_CMD] ; char * lDialogString = NULL ; char * lpDialogString; FILE * lIn ; int lWasGraphicDialog = 0 ; int lWasXterm = 0 ; int lResult ; char lChar ; struct termios infoOri; struct termios info; size_t lTitleLen ; size_t lMessageLen ; lBuff[0]='\0'; if (tfd_quoteDetected(aTitle)) return tinyfd_messageBox("INVALID TITLE WITH QUOTES", aMessage, aDialogType, aIconType, aDefaultButton); if (tfd_quoteDetected(aMessage)) return tinyfd_messageBox(aTitle, "INVALID MESSAGE WITH QUOTES", aDialogType, aIconType, aDefaultButton); lTitleLen = aTitle ? strlen(aTitle) : 0 ; lMessageLen = aMessage ? strlen(aMessage) : 0 ; if ( !aTitle || strcmp(aTitle,"tinyfd_query") ) { lDialogString = (char *) malloc( MAX_PATH_OR_CMD + lTitleLen + lMessageLen ); } if ( osascriptPresent( ) ) { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"applescript");return 1;} strcpy( lDialogString , "osascript "); if ( ! osx9orBetter() ) strcat( lDialogString , " -e 'tell application \"System Events\"' -e 'Activate'"); strcat( lDialogString , " -e 'try' -e 'set {vButton} to {button returned} of ( display dialog \"") ; if ( aMessage && strlen(aMessage) ) { strcat(lDialogString, aMessage) ; } strcat(lDialogString, "\" ") ; if ( aTitle && strlen(aTitle) ) { strcat(lDialogString, "with title \"") ; strcat(lDialogString, aTitle) ; strcat(lDialogString, "\" ") ; } strcat(lDialogString, "with icon ") ; if ( aIconType && ! strcmp( "error" , aIconType ) ) { strcat(lDialogString, "stop " ) ; } else if ( aIconType && ! strcmp( "warning" , aIconType ) ) { strcat(lDialogString, "caution " ) ; } else /* question or info */ { strcat(lDialogString, "note " ) ; } if ( aDialogType && ! strcmp( "okcancel" , aDialogType ) ) { if ( ! aDefaultButton ) { strcat( lDialogString ,"default button \"Cancel\" " ) ; } } else if ( aDialogType && ! strcmp( "yesno" , aDialogType ) ) { strcat( lDialogString ,"buttons {\"No\", \"Yes\"} " ) ; if (aDefaultButton) { strcat( lDialogString ,"default button \"Yes\" " ) ; } else { strcat( lDialogString ,"default button \"No\" " ) ; } strcat( lDialogString ,"cancel button \"No\"" ) ; } else if ( aDialogType && ! strcmp( "yesnocancel" , aDialogType ) ) { strcat( lDialogString ,"buttons {\"No\", \"Yes\", \"Cancel\"} " ) ; switch (aDefaultButton) { case 1: strcat( lDialogString ,"default button \"Yes\" " ) ; break; case 2: strcat( lDialogString ,"default button \"No\" " ) ; break; case 0: strcat( lDialogString ,"default button \"Cancel\" " ) ; break; } strcat( lDialogString ,"cancel button \"Cancel\"" ) ; } else { strcat( lDialogString ,"buttons {\"OK\"} " ) ; strcat( lDialogString ,"default button \"OK\" " ) ; } strcat( lDialogString, ")' ") ; strcat( lDialogString, "-e 'if vButton is \"Yes\" then' -e 'return 1'\ -e 'else if vButton is \"OK\" then' -e 'return 1'\ -e 'else if vButton is \"No\" then' -e 'return 2'\ -e 'else' -e 'return 0' -e 'end if' " ); strcat( lDialogString, "-e 'on error number -128' " ) ; strcat( lDialogString, "-e '0' " ); strcat( lDialogString, "-e 'end try'") ; if ( ! osx9orBetter() ) strcat( lDialogString, " -e 'end tell'") ; } else if ( tfd_kdialogPresent() ) { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"kdialog");return 1;} strcpy( lDialogString , "kdialog" ) ; if ( (tfd_kdialogPresent() == 2) && tfd_xpropPresent() ) { strcat(lDialogString, " --attach=$(xprop -root 32x '\t$0' _NET_ACTIVE_WINDOW | cut -f 2)"); /* contribution: Paul Rouget */ } strcat( lDialogString , " --" ) ; if ( aDialogType && ( ! strcmp( "okcancel" , aDialogType ) || ! strcmp( "yesno" , aDialogType ) || ! strcmp( "yesnocancel" , aDialogType ) ) ) { if ( aIconType && ( ! strcmp( "warning" , aIconType ) || ! strcmp( "error" , aIconType ) ) ) { strcat( lDialogString , "warning" ) ; } if ( ! strcmp( "yesnocancel" , aDialogType ) ) { strcat( lDialogString , "yesnocancel" ) ; } else { strcat( lDialogString , "yesno" ) ; } } else if ( aIconType && ! strcmp( "error" , aIconType ) ) { strcat( lDialogString , "error" ) ; } else if ( aIconType && ! strcmp( "warning" , aIconType ) ) { strcat( lDialogString , "sorry" ) ; } else { strcat( lDialogString , "msgbox" ) ; } strcat( lDialogString , " \"" ) ; if ( aMessage ) { strcat( lDialogString , aMessage ) ; } strcat( lDialogString , "\"" ) ; if ( aDialogType && ! strcmp( "okcancel" , aDialogType ) ) { strcat( lDialogString , " --yes-label Ok --no-label Cancel" ) ; } if ( aTitle && strlen(aTitle) ) { strcat(lDialogString, " --title \"") ; strcat(lDialogString, aTitle) ; strcat(lDialogString, "\"") ; } if ( ! strcmp( "yesnocancel" , aDialogType ) ) { strcat( lDialogString , "; x=$? ;if [ $x = 0 ] ;then echo 1;elif [ $x = 1 ] ;then echo 2;else echo 0;fi"); } else { strcat( lDialogString , ";if [ $? = 0 ];then echo 1;else echo 0;fi"); } } else if ( tfd_zenityPresent() || tfd_matedialogPresent() || tfd_shellementaryPresent() || tfd_qarmaPresent() ) { if ( tfd_zenityPresent() ) { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"zenity");return 1;} strcpy( lDialogString , "szAnswer=$(zenity" ) ; if ( (tfd_zenity3Present() >= 4) && !getenv("SSH_TTY") && tfd_xpropPresent() ) { strcat(lDialogString, " --attach=$(sleep .01;xprop -root 32x '\t$0' _NET_ACTIVE_WINDOW | cut -f 2)"); /* contribution: Paul Rouget */ } } else if ( tfd_matedialogPresent() ) { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"matedialog");return 1;} strcpy( lDialogString , "szAnswer=$(matedialog" ) ; } else if ( tfd_shellementaryPresent() ) { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"shellementary");return 1;} strcpy( lDialogString , "szAnswer=$(shellementary" ) ; } else { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"qarma");return 1;} strcpy( lDialogString , "szAnswer=$(qarma" ) ; if ( !getenv("SSH_TTY") && tfd_xpropPresent() ) { strcat(lDialogString, " --attach=$(xprop -root 32x '\t$0' _NET_ACTIVE_WINDOW | cut -f 2)"); /* contribution: Paul Rouget */ } } strcat(lDialogString, " --"); if ( aDialogType && ! strcmp( "okcancel" , aDialogType ) ) { strcat( lDialogString , "question --ok-label=Ok --cancel-label=Cancel" ) ; } else if ( aDialogType && ! strcmp( "yesno" , aDialogType ) ) { strcat( lDialogString , "question" ) ; } else if ( aDialogType && ! strcmp( "yesnocancel" , aDialogType ) ) { strcat( lDialogString , "list --column \"\" --hide-header \"Yes\" \"No\"" ) ; } else if ( aIconType && ! strcmp( "error" , aIconType ) ) { strcat( lDialogString , "error" ) ; } else if ( aIconType && ! strcmp( "warning" , aIconType ) ) { strcat( lDialogString , "warning" ) ; } else { strcat( lDialogString , "info" ) ; } if ( aTitle && strlen(aTitle) ) { strcat(lDialogString, " --title=\"") ; strcat(lDialogString, aTitle) ; strcat(lDialogString, "\"") ; } if ( aMessage && strlen(aMessage) ) { if (strcmp("yesnocancel", aDialogType)) strcat(lDialogString, " --no-wrap"); strcat(lDialogString, " --text=\"") ; strcat(lDialogString, aMessage) ; strcat(lDialogString, "\"") ; } if ( (tfd_zenity3Present() >= 3) || (!tfd_zenityPresent() && (tfd_shellementaryPresent() || tfd_qarmaPresent()) ) ) { strcat( lDialogString , " --icon-name=dialog-" ) ; if ( aIconType && (! strcmp( "question" , aIconType ) || ! strcmp( "error" , aIconType ) || ! strcmp( "warning" , aIconType ) ) ) { strcat( lDialogString , aIconType ) ; } else { strcat( lDialogString , "information" ) ; } } if (tinyfd_silent) strcat( lDialogString , " 2>/dev/null "); if ( ! strcmp( "yesnocancel" , aDialogType ) ) { strcat( lDialogString , ");if [ $? = 1 ];then echo 0;elif [ $szAnswer = \"No\" ];then echo 2;else echo 1;fi"); } else { strcat( lDialogString , ");if [ $? = 0 ];then echo 1;else echo 0;fi"); } } else if (tfd_yadPresent()) { if (aTitle && !strcmp(aTitle, "tinyfd_query")) { strcpy(tinyfd_response, "yad"); return 1; } strcpy(lDialogString, "szAnswer=$(yad --"); if (aDialogType && !strcmp("ok", aDialogType)) { strcat(lDialogString,"button=Ok:1"); } else if (aDialogType && !strcmp("okcancel", aDialogType)) { strcat(lDialogString,"button=Ok:1 --button=Cancel:0"); } else if (aDialogType && !strcmp("yesno", aDialogType)) { strcat(lDialogString, "button=Yes:1 --button=No:0"); } else if (aDialogType && !strcmp("yesnocancel", aDialogType)) { strcat(lDialogString, "button=Yes:1 --button=No:2 --button=Cancel:0"); } else if (aIconType && !strcmp("error", aIconType)) { strcat(lDialogString, "error"); } else if (aIconType && !strcmp("warning", aIconType)) { strcat(lDialogString, "warning"); } else { strcat(lDialogString, "info"); } if (aTitle && strlen(aTitle)) { strcat(lDialogString, " --title=\""); strcat(lDialogString, aTitle); strcat(lDialogString, "\""); } if (aMessage && strlen(aMessage)) { strcat(lDialogString, " --text=\""); strcat(lDialogString, aMessage); strcat(lDialogString, "\""); } strcat(lDialogString, " --icon-name=dialog-"); if (aIconType && (!strcmp("question", aIconType) || !strcmp("error", aIconType) || !strcmp("warning", aIconType))) { strcat(lDialogString, aIconType); } else { strcat(lDialogString, "information"); } if (tinyfd_silent) strcat(lDialogString, " 2>/dev/null "); strcat(lDialogString,");echo $?"); } else if ( !gxmessagePresent() && !gmessagePresent() && !gdialogPresent() && !xdialogPresent() && tkinter3Present() ) { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"python3-tkinter");return 1;} strcpy( lDialogString , gPython3Name ) ; strcat( lDialogString , " -S -c \"import tkinter;from tkinter import messagebox;root=tkinter.Tk();root.withdraw();"); strcat( lDialogString ,"res=messagebox." ) ; if ( aDialogType && ! strcmp( "okcancel" , aDialogType ) ) { strcat( lDialogString , "askokcancel(" ) ; if ( aDefaultButton ) { strcat( lDialogString , "default=messagebox.OK," ) ; } else { strcat( lDialogString , "default=messagebox.CANCEL," ) ; } } else if ( aDialogType && ! strcmp( "yesno" , aDialogType ) ) { strcat( lDialogString , "askyesno(" ) ; if ( aDefaultButton ) { strcat( lDialogString , "default=messagebox.YES," ) ; } else { strcat( lDialogString , "default=messagebox.NO," ) ; } } else if ( aDialogType && ! strcmp( "yesnocancel" , aDialogType ) ) { strcat( lDialogString , "askyesnocancel(" ) ; switch ( aDefaultButton ) { case 1: strcat( lDialogString , "default=messagebox.YES," ); break; case 2: strcat( lDialogString , "default=messagebox.NO," ); break; case 0: strcat( lDialogString , "default=messagebox.CANCEL," ); break; } } else { strcat( lDialogString , "showinfo(" ) ; } strcat( lDialogString , "icon='" ) ; if ( aIconType && (! strcmp( "question" , aIconType ) || ! strcmp( "error" , aIconType ) || ! strcmp( "warning" , aIconType ) ) ) { strcat( lDialogString , aIconType ) ; } else { strcat( lDialogString , "info" ) ; } strcat(lDialogString, "',") ; if ( aTitle && strlen(aTitle) ) { strcat(lDialogString, "title='") ; strcat(lDialogString, aTitle) ; strcat(lDialogString, "',") ; } if ( aMessage && strlen(aMessage) ) { strcat(lDialogString, "message='") ; lpDialogString = lDialogString + strlen(lDialogString); tfd_replaceSubStr( aMessage , "\n" , "\\n" , lpDialogString ) ; strcat(lDialogString, "'") ; } if ( aDialogType && ! strcmp( "yesnocancel" , aDialogType ) ) { strcat(lDialogString, ");\n\ if res is None :\n\tprint(0)\n\ elif res is False :\n\tprint(2)\n\ else :\n\tprint (1)\n\"" ) ; } else { strcat(lDialogString, ");\n\ if res is False :\n\tprint(0)\n\ else :\n\tprint(1)\n\"" ) ; } } else if ( !gxmessagePresent() && !gmessagePresent() && !gdialogPresent() && !xdialogPresent() && tkinter2Present() ) { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"python2-tkinter");return 1;} strcpy( lDialogString , "export PYTHONIOENCODING=utf-8;" ) ; strcat( lDialogString , gPython2Name ) ; if ( ! isTerminalRunning( ) && tfd_isDarwin( ) ) { strcat( lDialogString , " -i" ) ; /* for osx without console */ } strcat( lDialogString , " -S -c \"import Tkinter,tkMessageBox;root=Tkinter.Tk();root.withdraw();"); if ( tfd_isDarwin( ) ) { strcat( lDialogString , "import os;os.system('''/usr/bin/osascript -e 'tell app \\\"Finder\\\" to set \ frontmost of process \\\"Python\\\" to true' ''');"); } strcat( lDialogString ,"res=tkMessageBox." ) ; if ( aDialogType && ! strcmp( "okcancel" , aDialogType ) ) { strcat( lDialogString , "askokcancel(" ) ; if ( aDefaultButton ) { strcat( lDialogString , "default=tkMessageBox.OK," ) ; } else { strcat( lDialogString , "default=tkMessageBox.CANCEL," ) ; } } else if ( aDialogType && ! strcmp( "yesno" , aDialogType ) ) { strcat( lDialogString , "askyesno(" ) ; if ( aDefaultButton ) { strcat( lDialogString , "default=tkMessageBox.YES," ) ; } else { strcat( lDialogString , "default=tkMessageBox.NO," ) ; } } else if ( aDialogType && ! strcmp( "yesnocancel" , aDialogType ) ) { strcat( lDialogString , "askyesnocancel(" ) ; switch ( aDefaultButton ) { case 1: strcat( lDialogString , "default=tkMessageBox.YES," ); break; case 2: strcat( lDialogString , "default=tkMessageBox.NO," ); break; case 0: strcat( lDialogString , "default=tkMessageBox.CANCEL," ); break; } } else { strcat( lDialogString , "showinfo(" ) ; } strcat( lDialogString , "icon='" ) ; if ( aIconType && (! strcmp( "question" , aIconType ) || ! strcmp( "error" , aIconType ) || ! strcmp( "warning" , aIconType ) ) ) { strcat( lDialogString , aIconType ) ; } else { strcat( lDialogString , "info" ) ; } strcat(lDialogString, "',") ; if ( aTitle && strlen(aTitle) ) { strcat(lDialogString, "title='") ; strcat(lDialogString, aTitle) ; strcat(lDialogString, "',") ; } if ( aMessage && strlen(aMessage) ) { strcat(lDialogString, "message='") ; lpDialogString = lDialogString + strlen(lDialogString); tfd_replaceSubStr( aMessage , "\n" , "\\n" , lpDialogString ) ; strcat(lDialogString, "'") ; } if ( aDialogType && ! strcmp( "yesnocancel" , aDialogType ) ) { strcat(lDialogString, ");\n\ if res is None :\n\tprint 0\n\ elif res is False :\n\tprint 2\n\ else :\n\tprint 1\n\"" ) ; } else { strcat(lDialogString, ");\n\ if res is False :\n\tprint 0\n\ else :\n\tprint 1\n\"" ) ; } } else if ( gxmessagePresent() || gmessagePresent() || (!gdialogPresent() && !xdialogPresent() && xmessagePresent()) ) { if ( gxmessagePresent() ) { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"gxmessage");return 1;} strcpy( lDialogString , "gxmessage"); } else if ( gmessagePresent() ) { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"gmessage");return 1;} strcpy( lDialogString , "gmessage"); } else { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"xmessage");return 1;} strcpy( lDialogString , "xmessage"); } if ( aDialogType && ! strcmp("okcancel" , aDialogType) ) { strcat( lDialogString , " -buttons Ok:1,Cancel:0"); switch ( aDefaultButton ) { case 1: strcat( lDialogString , " -default Ok"); break; case 0: strcat( lDialogString , " -default Cancel"); break; } } else if ( aDialogType && ! strcmp("yesno" , aDialogType) ) { strcat( lDialogString , " -buttons Yes:1,No:0"); switch ( aDefaultButton ) { case 1: strcat( lDialogString , " -default Yes"); break; case 0: strcat( lDialogString , " -default No"); break; } } else if ( aDialogType && ! strcmp("yesnocancel" , aDialogType) ) { strcat( lDialogString , " -buttons Yes:1,No:2,Cancel:0"); switch ( aDefaultButton ) { case 1: strcat( lDialogString , " -default Yes"); break; case 2: strcat( lDialogString , " -default No"); break; case 0: strcat( lDialogString , " -default Cancel"); break; } } else { strcat( lDialogString , " -buttons Ok:1"); strcat( lDialogString , " -default Ok"); } strcat( lDialogString , " -center \""); if ( aMessage && strlen(aMessage) ) { strcat( lDialogString , aMessage ) ; } strcat(lDialogString, "\"" ) ; if ( aTitle && strlen(aTitle) ) { strcat( lDialogString , " -title \""); strcat( lDialogString , aTitle ) ; strcat( lDialogString, "\"" ) ; } strcat( lDialogString , " ; echo $? "); } else if ( xdialogPresent() || gdialogPresent() || dialogName() || whiptailPresent() ) { if ( gdialogPresent( ) ) { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"gdialog");return 1;} lWasGraphicDialog = 1 ; strcpy( lDialogString , "(gdialog " ) ; } else if ( xdialogPresent( ) ) { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"xdialog");return 1;} lWasGraphicDialog = 1 ; strcpy( lDialogString , "(Xdialog " ) ; } else if ( dialogName( ) ) { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"dialog");return 0;} if ( isTerminalRunning( ) ) { strcpy( lDialogString , "(dialog " ) ; } else { lWasXterm = 1 ; strcpy( lDialogString , terminalName() ) ; strcat( lDialogString , "'(" ) ; strcat( lDialogString , dialogName() ) ; strcat( lDialogString , " " ) ; } } else if ( isTerminalRunning( ) ) { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"whiptail");return 0;} strcpy( lDialogString , "(whiptail " ) ; } else { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"whiptail");return 0;} lWasXterm = 1 ; strcpy( lDialogString , terminalName() ) ; strcat( lDialogString , "'(whiptail " ) ; } if ( aTitle && strlen(aTitle) ) { strcat(lDialogString, "--title \"") ; strcat(lDialogString, aTitle) ; strcat(lDialogString, "\" ") ; } if ( !xdialogPresent() && !gdialogPresent() ) { if ( aDialogType && ( !strcmp( "okcancel" , aDialogType ) || !strcmp( "yesno" , aDialogType ) || !strcmp( "yesnocancel" , aDialogType ) ) ) { strcat(lDialogString, "--backtitle \"") ; strcat(lDialogString, "tab: move focus") ; strcat(lDialogString, "\" ") ; } } if ( aDialogType && ! strcmp( "okcancel" , aDialogType ) ) { if ( ! aDefaultButton ) { strcat( lDialogString , "--defaultno " ) ; } strcat( lDialogString , "--yes-label \"Ok\" --no-label \"Cancel\" --yesno " ) ; } else if ( aDialogType && ! strcmp( "yesno" , aDialogType ) ) { if ( ! aDefaultButton ) { strcat( lDialogString , "--defaultno " ) ; } strcat( lDialogString , "--yesno " ) ; } else if (aDialogType && !strcmp("yesnocancel", aDialogType)) { if (!aDefaultButton) { strcat(lDialogString, "--defaultno "); } strcat(lDialogString, "--menu "); } else { strcat( lDialogString , "--msgbox " ) ; } strcat( lDialogString , "\"" ) ; if ( aMessage && strlen(aMessage) ) { strcat(lDialogString, aMessage) ; } strcat(lDialogString, "\" "); if ( lWasGraphicDialog ) { if (aDialogType && !strcmp("yesnocancel", aDialogType)) { strcat(lDialogString,"0 60 0 Yes \"\" No \"\") 2>/tmp/tinyfd.txt;\ if [ $? = 0 ];then tinyfdBool=1;else tinyfdBool=0;fi;\ tinyfdRes=$(cat /tmp/tinyfd.txt);echo $tinyfdBool$tinyfdRes") ; } else { strcat(lDialogString, "10 60 ) 2>&1;if [ $? = 0 ];then echo 1;else echo 0;fi"); } } else { if (aDialogType && !strcmp("yesnocancel", aDialogType)) { strcat(lDialogString,"0 60 0 Yes \"\" No \"\" >/dev/tty ) 2>/tmp/tinyfd.txt;\ if [ $? = 0 ];then tinyfdBool=1;else tinyfdBool=0;fi;\ tinyfdRes=$(cat /tmp/tinyfd.txt);echo $tinyfdBool$tinyfdRes") ; if ( lWasXterm ) { strcat(lDialogString," >/tmp/tinyfd0.txt';cat /tmp/tinyfd0.txt"); } else { strcat(lDialogString, "; clear >/dev/tty") ; } } else { strcat(lDialogString, "10 60 >/dev/tty) 2>&1;if [ $? = 0 ];"); if ( lWasXterm ) { strcat( lDialogString , "then\n\techo 1\nelse\n\techo 0\nfi >/tmp/tinyfd.txt';cat /tmp/tinyfd.txt;rm /tmp/tinyfd.txt"); } else { strcat(lDialogString, "then echo 1;else echo 0;fi;clear >/dev/tty"); } } } } else if ( !isTerminalRunning() && terminalName() ) { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"basicinput");return 0;} strcpy( lDialogString , terminalName() ) ; strcat( lDialogString , "'" ) ; if ( !gWarningDisplayed && !tinyfd_forceConsole) { gWarningDisplayed = 1 ; strcat( lDialogString , "echo \"" ) ; strcat( lDialogString, gTitle) ; strcat( lDialogString , "\";" ) ; strcat( lDialogString , "echo \"" ) ; strcat( lDialogString, tinyfd_needs) ; strcat( lDialogString , "\";echo;echo;" ) ; } if ( aTitle && strlen(aTitle) ) { strcat( lDialogString , "echo \"" ) ; strcat( lDialogString, aTitle) ; strcat( lDialogString , "\";echo;" ) ; } if ( aMessage && strlen(aMessage) ) { strcat( lDialogString , "echo \"" ) ; strcat( lDialogString, aMessage) ; strcat( lDialogString , "\"; " ) ; } if ( aDialogType && !strcmp("yesno",aDialogType) ) { strcat( lDialogString , "echo -n \"y/n: \"; " ) ; strcat( lDialogString , "stty sane -echo;" ) ; strcat( lDialogString , "answer=$( while ! head -c 1 | grep -i [ny];do true ;done);"); strcat( lDialogString , "if echo \"$answer\" | grep -iq \"^y\";then\n"); strcat( lDialogString , "\techo 1\nelse\n\techo 0\nfi" ) ; } else if ( aDialogType && !strcmp("okcancel",aDialogType) ) { strcat( lDialogString , "echo -n \"[O]kay/[C]ancel: \"; " ) ; strcat( lDialogString , "stty sane -echo;" ) ; strcat( lDialogString , "answer=$( while ! head -c 1 | grep -i [oc];do true ;done);"); strcat( lDialogString , "if echo \"$answer\" | grep -iq \"^o\";then\n"); strcat( lDialogString , "\techo 1\nelse\n\techo 0\nfi" ) ; } else if ( aDialogType && !strcmp("yesnocancel",aDialogType) ) { strcat( lDialogString , "echo -n \"[Y]es/[N]o/[C]ancel: \"; " ) ; strcat( lDialogString , "stty sane -echo;" ) ; strcat( lDialogString , "answer=$( while ! head -c 1 | grep -i [nyc];do true ;done);"); strcat( lDialogString , "if echo \"$answer\" | grep -iq \"^y\";then\n\techo 1\n"); strcat( lDialogString , "elif echo \"$answer\" | grep -iq \"^n\";then\n\techo 2\n" ) ; strcat( lDialogString , "else\n\techo 0\nfi" ) ; } else { strcat(lDialogString , "echo -n \"press enter to continue \"; "); strcat( lDialogString , "stty sane -echo;" ) ; strcat( lDialogString , "answer=$( while ! head -c 1;do true ;done);echo 1"); } strcat( lDialogString , " >/tmp/tinyfd.txt';cat /tmp/tinyfd.txt;rm /tmp/tinyfd.txt"); } else if ( !isTerminalRunning() && pythonDbusPresent() && !strcmp("ok" , aDialogType) ) { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"python-dbus");return 1;} strcpy( lDialogString , gPythonName ) ; strcat( lDialogString ," -c \"import dbus;bus=dbus.SessionBus();"); strcat( lDialogString ,"notif=bus.get_object('org.freedesktop.Notifications','/org/freedesktop/Notifications');" ) ; strcat( lDialogString ,"notify=dbus.Interface(notif,'org.freedesktop.Notifications');" ) ; strcat( lDialogString ,"notify.Notify('',0,'" ) ; if ( aIconType && strlen(aIconType) ) { strcat( lDialogString , aIconType ) ; } strcat(lDialogString, "','") ; if ( aTitle && strlen(aTitle) ) { strcat(lDialogString, aTitle) ; } strcat(lDialogString, "','") ; if ( aMessage && strlen(aMessage) ) { lpDialogString = lDialogString + strlen(lDialogString); tfd_replaceSubStr( aMessage , "\n" , "\\n" , lpDialogString ) ; } strcat(lDialogString, "','','',5000)\"") ; } else if ( !isTerminalRunning() && (perlPresent() >= 2) && !strcmp("ok" , aDialogType) ) { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"perl-dbus");return 1;} strcpy( lDialogString , "perl -e \"use Net::DBus;\ my \\$sessionBus = Net::DBus->session;\ my \\$notificationsService = \\$sessionBus->get_service('org.freedesktop.Notifications');\ my \\$notificationsObject = \\$notificationsService->get_object('/org/freedesktop/Notifications',\ 'org.freedesktop.Notifications');"); sprintf( lDialogString + strlen(lDialogString), "my \\$notificationId;\\$notificationId = \\$notificationsObject->Notify(shift, 0, '%s', '%s', '%s', [], {}, -1);\" ", aIconType?aIconType:"", aTitle?aTitle:"", aMessage?aMessage:"" ) ; } else if ( !isTerminalRunning() && notifysendPresent() && !strcmp("ok" , aDialogType) ) { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"notifysend");return 1;} strcpy( lDialogString , "notify-send" ) ; if ( aIconType && strlen(aIconType) ) { strcat( lDialogString , " -i '" ) ; strcat( lDialogString , aIconType ) ; strcat( lDialogString , "'" ) ; } strcat( lDialogString , " \"" ) ; if ( aTitle && strlen(aTitle) ) { strcat(lDialogString, aTitle) ; strcat( lDialogString , " | " ) ; } if ( aMessage && strlen(aMessage) ) { tfd_replaceSubStr( aMessage , "\n\t" , " | " , lBuff ) ; tfd_replaceSubStr( aMessage , "\n" , " | " , lBuff ) ; tfd_replaceSubStr( aMessage , "\t" , " " , lBuff ) ; strcat(lDialogString, lBuff) ; } strcat( lDialogString , "\"" ) ; } else { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"basicinput");return 0;} if ( !gWarningDisplayed && !tinyfd_forceConsole) { gWarningDisplayed = 1 ; printf("\n\n%s\n", gTitle); printf("%s\n\n", tinyfd_needs); } if ( aTitle && strlen(aTitle) ) { printf("\n%s\n", aTitle); } tcgetattr(0, &infoOri); tcgetattr(0, &info); info.c_lflag &= ~ICANON; info.c_cc[VMIN] = 1; info.c_cc[VTIME] = 0; tcsetattr(0, TCSANOW, &info); if ( aDialogType && !strcmp("yesno",aDialogType) ) { do { if ( aMessage && strlen(aMessage) ) { printf("\n%s\n",aMessage); } printf("y/n: "); fflush(stdout); lChar = tolower( getchar() ) ; printf("\n\n"); } while ( lChar != 'y' && lChar != 'n' ); lResult = lChar == 'y' ? 1 : 0 ; } else if ( aDialogType && !strcmp("okcancel",aDialogType) ) { do { if ( aMessage && strlen(aMessage) ) { printf("\n%s\n",aMessage); } printf("[O]kay/[C]ancel: "); fflush(stdout); lChar = tolower( getchar() ) ; printf("\n\n"); } while ( lChar != 'o' && lChar != 'c' ); lResult = lChar == 'o' ? 1 : 0 ; } else if ( aDialogType && !strcmp("yesnocancel",aDialogType) ) { do { if ( aMessage && strlen(aMessage) ) { printf("\n%s\n",aMessage); } printf("[Y]es/[N]o/[C]ancel: "); fflush(stdout); lChar = tolower( getchar() ) ; printf("\n\n"); } while ( lChar != 'y' && lChar != 'n' && lChar != 'c' ); lResult = (lChar == 'y') ? 1 : (lChar == 'n') ? 2 : 0 ; } else { if ( aMessage && strlen(aMessage) ) { printf("\n%s\n\n",aMessage); } printf("press enter to continue "); fflush(stdout); getchar() ; printf("\n\n"); lResult = 1 ; } tcsetattr(0, TCSANOW, &infoOri); free(lDialogString); return lResult ; } if (tinyfd_verbose) printf( "lDialogString: %s\n" , lDialogString ) ; if ( ! ( lIn = popen( lDialogString , "r" ) ) ) { free(lDialogString); return 0 ; } while ( fgets( lBuff , sizeof( lBuff ) , lIn ) != NULL ) {} pclose( lIn ) ; /* printf( "lBuff: %s len: %lu \n" , lBuff , strlen(lBuff) ) ; */ if ( lBuff[strlen( lBuff ) -1] == '\n' ) { lBuff[strlen( lBuff ) -1] = '\0' ; } /* printf( "lBuff1: %s len: %lu \n" , lBuff , strlen(lBuff) ) ; */ if (aDialogType && !strcmp("yesnocancel", aDialogType)) { if ( lBuff[0]=='1' ) { if ( !strcmp( lBuff+1 , "Yes" )) strcpy(lBuff,"1"); else if ( !strcmp( lBuff+1 , "No" )) strcpy(lBuff,"2"); } } /* printf( "lBuff2: %s len: %lu \n" , lBuff , strlen(lBuff) ) ; */ lResult = !strcmp( lBuff , "2" ) ? 2 : !strcmp( lBuff , "1" ) ? 1 : 0; /* printf( "lResult: %d\n" , lResult ) ; */ free(lDialogString); return lResult ; } /* return has only meaning for tinyfd_query */ int tinyfd_notifyPopup( char const * aTitle , /* NULL or "" */ char const * aMessage , /* NULL or "" may contain \n and \t */ char const * aIconType ) /* "info" "warning" "error" */ { char lBuff[MAX_PATH_OR_CMD]; char * lDialogString = NULL ; char * lpDialogString ; FILE * lIn ; size_t lTitleLen ; size_t lMessageLen ; if (tfd_quoteDetected(aTitle)) return tinyfd_notifyPopup("INVALID TITLE WITH QUOTES", aMessage, aIconType); if (tfd_quoteDetected(aMessage)) return tinyfd_notifyPopup(aTitle, "INVALID MESSAGE WITH QUOTES", aIconType); if ( getenv("SSH_TTY") ) { return tinyfd_messageBox(aTitle, aMessage, "ok", aIconType, 0); } lTitleLen = aTitle ? strlen(aTitle) : 0 ; lMessageLen = aMessage ? strlen(aMessage) : 0 ; if ( !aTitle || strcmp(aTitle,"tinyfd_query") ) { lDialogString = (char *) malloc( MAX_PATH_OR_CMD + lTitleLen + lMessageLen ); } if ( osascriptPresent( ) ) { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"applescript");return 1;} strcpy( lDialogString , "osascript "); if ( ! osx9orBetter() ) strcat( lDialogString , " -e 'tell application \"System Events\"' -e 'Activate'"); strcat( lDialogString , " -e 'try' -e 'display notification \"") ; if ( aMessage && strlen(aMessage) ) { strcat(lDialogString, aMessage) ; } strcat(lDialogString, " \" ") ; if ( aTitle && strlen(aTitle) ) { strcat(lDialogString, "with title \"") ; strcat(lDialogString, aTitle) ; strcat(lDialogString, "\" ") ; } strcat( lDialogString, "' -e 'end try'") ; if ( ! osx9orBetter() ) strcat( lDialogString, " -e 'end tell'") ; } else if ( tfd_kdialogPresent() ) { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"kdialog");return 1;} strcpy( lDialogString , "kdialog" ) ; if ( aIconType && strlen(aIconType) ) { strcat( lDialogString , " --icon '" ) ; strcat( lDialogString , aIconType ) ; strcat( lDialogString , "'" ) ; } if ( aTitle && strlen(aTitle) ) { strcat( lDialogString , " --title \"" ) ; strcat( lDialogString , aTitle ) ; strcat( lDialogString , "\"" ) ; } strcat( lDialogString , " --passivepopup" ) ; strcat( lDialogString , " \"" ) ; if ( aMessage ) { strcat( lDialogString , aMessage ) ; } strcat( lDialogString , " \" 5" ) ; } else if ( (tfd_zenity3Present()>=5) ) { /* zenity 2.32 & 3.14 has the notification but with a bug: it doesnt return from it */ /* zenity 3.8 show the notification as an alert ok cancel box */ if ( tfd_zenity3Present()>=5 ) { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"zenity");return 1;} strcpy( lDialogString , "zenity" ) ; } strcat( lDialogString , " --notification"); if ( aIconType && strlen( aIconType ) ) { strcat( lDialogString , " --window-icon '"); strcat( lDialogString , aIconType ) ; strcat( lDialogString , "'" ) ; } strcat( lDialogString , " --text \"" ) ; if ( aTitle && strlen(aTitle) ) { strcat(lDialogString, aTitle) ; strcat(lDialogString, "\n") ; } if ( aMessage && strlen( aMessage ) ) { strcat( lDialogString , aMessage ) ; } strcat( lDialogString , " \"" ) ; } else if ( perlPresent() >= 2 ) { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"perl-dbus");return 1;} strcpy( lDialogString , "perl -e \"use Net::DBus;\ my \\$sessionBus = Net::DBus->session;\ my \\$notificationsService = \\$sessionBus->get_service('org.freedesktop.Notifications');\ my \\$notificationsObject = \\$notificationsService->get_object('/org/freedesktop/Notifications',\ 'org.freedesktop.Notifications');"); sprintf( lDialogString + strlen(lDialogString) , "my \\$notificationId;\\$notificationId = \\$notificationsObject->Notify(shift, 0, '%s', '%s', '%s', [], {}, -1);\" ", aIconType?aIconType:"", aTitle?aTitle:"", aMessage?aMessage:"" ) ; } else if ( pythonDbusPresent( ) ) { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"python-dbus");return 1;} strcpy( lDialogString , gPythonName ) ; strcat( lDialogString ," -c \"import dbus;bus=dbus.SessionBus();"); strcat( lDialogString ,"notif=bus.get_object('org.freedesktop.Notifications','/org/freedesktop/Notifications');" ) ; strcat( lDialogString ,"notify=dbus.Interface(notif,'org.freedesktop.Notifications');" ) ; strcat( lDialogString ,"notify.Notify('',0,'" ) ; if ( aIconType && strlen(aIconType) ) { strcat( lDialogString , aIconType ) ; } strcat(lDialogString, "','") ; if ( aTitle && strlen(aTitle) ) { strcat(lDialogString, aTitle) ; } strcat(lDialogString, "','") ; if ( aMessage && strlen(aMessage) ) { lpDialogString = lDialogString + strlen(lDialogString); tfd_replaceSubStr( aMessage , "\n" , "\\n" , lpDialogString ) ; } strcat(lDialogString, "','','',5000)\"") ; } else if ( notifysendPresent() ) { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"notifysend");return 1;} strcpy( lDialogString , "notify-send" ) ; if ( aIconType && strlen(aIconType) ) { strcat( lDialogString , " -i '" ) ; strcat( lDialogString , aIconType ) ; strcat( lDialogString , "'" ) ; } strcat( lDialogString , " \"" ) ; if ( aTitle && strlen(aTitle) ) { strcat(lDialogString, aTitle) ; strcat( lDialogString , " | " ) ; } if ( aMessage && strlen(aMessage) ) { tfd_replaceSubStr( aMessage , "\n\t" , " | " , lBuff ) ; tfd_replaceSubStr( aMessage , "\n" , " | " , lBuff ) ; tfd_replaceSubStr( aMessage , "\t" , " " , lBuff ) ; strcat(lDialogString, lBuff) ; } strcat( lDialogString , "\"" ) ; } else { return tinyfd_messageBox(aTitle, aMessage, "ok", aIconType, 0); } if (tinyfd_verbose) printf( "lDialogString: %s\n" , lDialogString ) ; if ( ! ( lIn = popen( lDialogString , "r" ) ) ) { free(lDialogString); return 0 ; } pclose( lIn ) ; free(lDialogString); return 1; } /* returns NULL on cancel */ char * tinyfd_inputBox( char const * aTitle , /* NULL or "" */ char const * aMessage , /* NULL or "" (\n and \t have no effect) */ char const * aDefaultInput ) /* "" , if NULL it's a passwordBox */ { static char lBuff[MAX_PATH_OR_CMD]; char * lDialogString = NULL; char * lpDialogString; FILE * lIn ; int lResult ; int lWasGdialog = 0 ; int lWasGraphicDialog = 0 ; int lWasXterm = 0 ; int lWasBasicXterm = 0 ; struct termios oldt ; struct termios newt ; char * lEOF; size_t lTitleLen ; size_t lMessageLen ; if (!aTitle && !aMessage && !aDefaultInput) return lBuff; /* now I can fill lBuff from outside */ lBuff[0]='\0'; if (tfd_quoteDetected(aTitle)) return tinyfd_inputBox("INVALID TITLE WITH QUOTES", aMessage, aDefaultInput); if (tfd_quoteDetected(aMessage)) return tinyfd_inputBox(aTitle, "INVALID MESSAGE WITH QUOTES", aDefaultInput); if (tfd_quoteDetected(aDefaultInput)) return tinyfd_inputBox(aTitle, aMessage, "INVALID DEFAULT_INPUT WITH QUOTES"); lTitleLen = aTitle ? strlen(aTitle) : 0 ; lMessageLen = aMessage ? strlen(aMessage) : 0 ; if ( !aTitle || strcmp(aTitle,"tinyfd_query") ) { lDialogString = (char *) malloc( MAX_PATH_OR_CMD + lTitleLen + lMessageLen ); } if ( osascriptPresent( ) ) { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"applescript");return (char *)1;} strcpy( lDialogString , "osascript "); if ( ! osx9orBetter() ) strcat( lDialogString , " -e 'tell application \"System Events\"' -e 'Activate'"); strcat( lDialogString , " -e 'try' -e 'display dialog \"") ; if ( aMessage && strlen(aMessage) ) { strcat(lDialogString, aMessage) ; } strcat(lDialogString, "\" ") ; strcat(lDialogString, "default answer \"") ; if ( aDefaultInput && strlen(aDefaultInput) ) { strcat(lDialogString, aDefaultInput) ; } strcat(lDialogString, "\" ") ; if ( ! aDefaultInput ) { strcat(lDialogString, "hidden answer true ") ; } if ( aTitle && strlen(aTitle) ) { strcat(lDialogString, "with title \"") ; strcat(lDialogString, aTitle) ; strcat(lDialogString, "\" ") ; } strcat(lDialogString, "with icon note' ") ; strcat(lDialogString, "-e '\"1\" & text returned of result' " ); strcat(lDialogString, "-e 'on error number -128' " ) ; strcat(lDialogString, "-e '0' " ); strcat(lDialogString, "-e 'end try'") ; if ( ! osx9orBetter() ) strcat(lDialogString, " -e 'end tell'") ; } else if ( tfd_kdialogPresent() ) { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"kdialog");return (char *)1;} strcpy( lDialogString , "szAnswer=$(kdialog" ) ; if ( (tfd_kdialogPresent() == 2) && tfd_xpropPresent() ) { strcat(lDialogString, " --attach=$(xprop -root 32x '\t$0' _NET_ACTIVE_WINDOW | cut -f 2)"); /* contribution: Paul Rouget */ } if ( ! aDefaultInput ) { strcat(lDialogString, " --password ") ; } else { strcat(lDialogString, " --inputbox ") ; } strcat(lDialogString, "\"") ; if ( aMessage && strlen(aMessage) ) { strcat(lDialogString, aMessage ) ; } strcat(lDialogString , "\" \"" ) ; if ( aDefaultInput && strlen(aDefaultInput) ) { strcat(lDialogString, aDefaultInput ) ; } strcat(lDialogString , "\"" ) ; if ( aTitle && strlen(aTitle) ) { strcat(lDialogString, " --title \"") ; strcat(lDialogString, aTitle) ; strcat(lDialogString, "\"") ; } strcat( lDialogString , ");if [ $? = 0 ];then echo 1$szAnswer;else echo 0$szAnswer;fi"); } else if ( tfd_zenityPresent() || tfd_matedialogPresent() || tfd_shellementaryPresent() || tfd_qarmaPresent() ) { if ( tfd_zenityPresent() ) { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"zenity");return (char *)1;} strcpy( lDialogString , "szAnswer=$(zenity" ) ; if ( (tfd_zenity3Present() >= 4) && !getenv("SSH_TTY") && tfd_xpropPresent() ) { strcat( lDialogString, " --attach=$(sleep .01;xprop -root 32x '\t$0' _NET_ACTIVE_WINDOW | cut -f 2)"); /* contribution: Paul Rouget */ } } else if ( tfd_matedialogPresent() ) { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"matedialog");return (char *)1;} strcpy( lDialogString , "szAnswer=$(matedialog" ) ; } else if ( tfd_shellementaryPresent() ) { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"shellementary");return (char *)1;} strcpy( lDialogString , "szAnswer=$(shellementary" ) ; } else { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"qarma");return (char *)1;} strcpy( lDialogString , "szAnswer=$(qarma" ) ; if ( !getenv("SSH_TTY") && tfd_xpropPresent() ) { strcat(lDialogString, " --attach=$(xprop -root 32x '\t$0' _NET_ACTIVE_WINDOW | cut -f 2)"); /* contribution: Paul Rouget */ } } strcat( lDialogString ," --entry" ) ; if ( aTitle && strlen(aTitle) ) { strcat(lDialogString, " --title=\"") ; strcat(lDialogString, aTitle) ; strcat(lDialogString, "\"") ; } if ( aMessage && strlen(aMessage) ) { strcat(lDialogString, " --text=\"") ; strcat(lDialogString, aMessage) ; strcat(lDialogString, "\"") ; } if ( aDefaultInput && strlen(aDefaultInput) ) { strcat(lDialogString, " --entry-text=\"") ; strcat(lDialogString, aDefaultInput) ; strcat(lDialogString, "\"") ; } else { strcat(lDialogString, " --hide-text") ; } if (tinyfd_silent) strcat( lDialogString , " 2>/dev/null "); strcat( lDialogString , ");if [ $? = 0 ];then echo 1$szAnswer;else echo 0$szAnswer;fi"); } else if (tfd_yadPresent()) { if (aTitle && !strcmp(aTitle, "tinyfd_query")) { strcpy(tinyfd_response, "yad"); return (char*)1; } strcpy(lDialogString, "szAnswer=$(yad --entry"); if (aTitle && strlen(aTitle)) { strcat(lDialogString, " --title=\""); strcat(lDialogString, aTitle); strcat(lDialogString, "\""); } if (aMessage && strlen(aMessage)) { strcat(lDialogString, " --text=\""); strcat(lDialogString, aMessage); strcat(lDialogString, "\""); } if (aDefaultInput && strlen(aDefaultInput)) { strcat(lDialogString, " --entry-text=\""); strcat(lDialogString, aDefaultInput); strcat(lDialogString, "\""); } else { strcat(lDialogString, " --hide-text"); } if (tinyfd_silent) strcat(lDialogString, " 2>/dev/null "); strcat(lDialogString, ");if [ $? = 0 ];then echo 1$szAnswer;else echo 0$szAnswer;fi"); } else if ( gxmessagePresent() || gmessagePresent() ) { if ( gxmessagePresent() ) { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"gxmessage");return (char *)1;} strcpy( lDialogString , "szAnswer=$(gxmessage -buttons Ok:1,Cancel:0 -center \""); } else { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"gmessage");return (char *)1;} strcpy( lDialogString , "szAnswer=$(gmessage -buttons Ok:1,Cancel:0 -center \""); } if ( aMessage && strlen(aMessage) ) { strcat( lDialogString , aMessage ) ; } strcat(lDialogString, "\"" ) ; if ( aTitle && strlen(aTitle) ) { strcat( lDialogString , " -title \""); strcat( lDialogString , aTitle ) ; strcat(lDialogString, "\" " ) ; } strcat(lDialogString, " -entrytext \"" ) ; if ( aDefaultInput && strlen(aDefaultInput) ) { strcat( lDialogString , aDefaultInput ) ; } strcat(lDialogString, "\"" ) ; strcat( lDialogString , ");echo $?$szAnswer"); } else if ( !gdialogPresent() && !xdialogPresent() && tkinter3Present( ) ) { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"python3-tkinter");return (char *)1;} strcpy( lDialogString , gPython3Name ) ; strcat( lDialogString , " -S -c \"import tkinter; from tkinter import simpledialog;root=tkinter.Tk();root.withdraw();"); strcat( lDialogString ,"res=simpledialog.askstring(" ) ; if ( aTitle && strlen(aTitle) ) { strcat(lDialogString, "title='") ; strcat(lDialogString, aTitle) ; strcat(lDialogString, "',") ; } if ( aMessage && strlen(aMessage) ) { strcat(lDialogString, "prompt='") ; lpDialogString = lDialogString + strlen(lDialogString); tfd_replaceSubStr( aMessage , "\n" , "\\n" , lpDialogString ) ; strcat(lDialogString, "',") ; } if ( aDefaultInput ) { if ( strlen(aDefaultInput) ) { strcat(lDialogString, "initialvalue='") ; strcat(lDialogString, aDefaultInput) ; strcat(lDialogString, "',") ; } } else { strcat(lDialogString, "show='*'") ; } strcat(lDialogString, ");\nif res is None :\n\tprint(0)"); strcat(lDialogString, "\nelse :\n\tprint('1'+res)\n\"" ) ; } else if ( !gdialogPresent() && !xdialogPresent() && tkinter2Present( ) ) { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"python2-tkinter");return (char *)1;} strcpy( lDialogString , "export PYTHONIOENCODING=utf-8;" ) ; strcat( lDialogString , gPython2Name ) ; if ( ! isTerminalRunning( ) && tfd_isDarwin( ) ) { strcat( lDialogString , " -i" ) ; /* for osx without console */ } strcat( lDialogString , " -S -c \"import Tkinter,tkSimpleDialog;root=Tkinter.Tk();root.withdraw();"); if ( tfd_isDarwin( ) ) { strcat( lDialogString , "import os;os.system('''/usr/bin/osascript -e 'tell app \\\"Finder\\\" to set \ frontmost of process \\\"Python\\\" to true' ''');"); } strcat( lDialogString ,"res=tkSimpleDialog.askstring(" ) ; if ( aTitle && strlen(aTitle) ) { strcat(lDialogString, "title='") ; strcat(lDialogString, aTitle) ; strcat(lDialogString, "',") ; } if ( aMessage && strlen(aMessage) ) { strcat(lDialogString, "prompt='") ; lpDialogString = lDialogString + strlen(lDialogString); tfd_replaceSubStr( aMessage , "\n" , "\\n" , lpDialogString ) ; strcat(lDialogString, "',") ; } if ( aDefaultInput ) { if ( strlen(aDefaultInput) ) { strcat(lDialogString, "initialvalue='") ; strcat(lDialogString, aDefaultInput) ; strcat(lDialogString, "',") ; } } else { strcat(lDialogString, "show='*'") ; } strcat(lDialogString, ");\nif res is None :\n\tprint 0"); strcat(lDialogString, "\nelse :\n\tprint '1'+res\n\"" ) ; } else if ( gdialogPresent() || xdialogPresent() || dialogName() || whiptailPresent() ) { if ( gdialogPresent( ) ) { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"gdialog");return (char *)1;} lWasGraphicDialog = 1 ; lWasGdialog = 1 ; strcpy( lDialogString , "(gdialog " ) ; } else if ( xdialogPresent( ) ) { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"xdialog");return (char *)1;} lWasGraphicDialog = 1 ; strcpy( lDialogString , "(Xdialog " ) ; } else if ( dialogName( ) ) { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"dialog");return (char *)0;} if ( isTerminalRunning( ) ) { strcpy( lDialogString , "(dialog " ) ; } else { lWasXterm = 1 ; strcpy( lDialogString , terminalName() ) ; strcat( lDialogString , "'(" ) ; strcat( lDialogString , dialogName() ) ; strcat( lDialogString , " " ) ; } } else if ( isTerminalRunning( ) ) { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"whiptail");return (char *)0;} strcpy( lDialogString , "(whiptail " ) ; } else { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"whiptail");return (char *)0;} lWasXterm = 1 ; strcpy( lDialogString , terminalName() ) ; strcat( lDialogString , "'(whiptail " ) ; } if ( aTitle && strlen(aTitle) ) { strcat(lDialogString, "--title \"") ; strcat(lDialogString, aTitle) ; strcat(lDialogString, "\" ") ; } if ( !xdialogPresent() && !gdialogPresent() ) { strcat(lDialogString, "--backtitle \"") ; strcat(lDialogString, "tab: move focus") ; if ( ! aDefaultInput && !lWasGdialog ) { strcat(lDialogString, " (sometimes nothing, no blink nor star, is shown in text field)") ; } strcat(lDialogString, "\" ") ; } if ( aDefaultInput || lWasGdialog ) { strcat( lDialogString , "--inputbox" ) ; } else { if ( !lWasGraphicDialog && dialogName() && isDialogVersionBetter09b() ) { strcat( lDialogString , "--insecure " ) ; } strcat( lDialogString , "--passwordbox" ) ; } strcat( lDialogString , " \"" ) ; if ( aMessage && strlen(aMessage) ) { strcat(lDialogString, aMessage) ; } strcat(lDialogString,"\" 10 60 ") ; if ( aDefaultInput && strlen(aDefaultInput) ) { strcat(lDialogString, "\"") ; strcat(lDialogString, aDefaultInput) ; strcat(lDialogString, "\" ") ; } if ( lWasGraphicDialog ) { strcat(lDialogString,") 2>/tmp/tinyfd.txt;\ if [ $? = 0 ];then tinyfdBool=1;else tinyfdBool=0;fi;\ tinyfdRes=$(cat /tmp/tinyfd.txt);echo $tinyfdBool$tinyfdRes") ; } else { strcat(lDialogString,">/dev/tty ) 2>/tmp/tinyfd.txt;\ if [ $? = 0 ];then tinyfdBool=1;else tinyfdBool=0;fi;\ tinyfdRes=$(cat /tmp/tinyfd.txt);echo $tinyfdBool$tinyfdRes") ; if ( lWasXterm ) { strcat(lDialogString," >/tmp/tinyfd0.txt';cat /tmp/tinyfd0.txt"); } else { strcat(lDialogString, "; clear >/dev/tty") ; } } } else if ( ! isTerminalRunning( ) && terminalName() ) { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"basicinput");return (char *)0;} lWasBasicXterm = 1 ; strcpy( lDialogString , terminalName() ) ; strcat( lDialogString , "'" ) ; if ( !gWarningDisplayed && !tinyfd_forceConsole) { gWarningDisplayed = 1 ; tinyfd_messageBox(gTitle,tinyfd_needs,"ok","warning",0); } if ( aTitle && strlen(aTitle) && !tinyfd_forceConsole) { strcat( lDialogString , "echo \"" ) ; strcat( lDialogString, aTitle) ; strcat( lDialogString , "\";echo;" ) ; } strcat( lDialogString , "echo \"" ) ; if ( aMessage && strlen(aMessage) ) { strcat( lDialogString, aMessage) ; } strcat( lDialogString , "\";read " ) ; if ( ! aDefaultInput ) { strcat( lDialogString , "-s " ) ; } strcat( lDialogString , "-p \"" ) ; strcat( lDialogString , "(esc+enter to cancel): \" ANSWER " ) ; strcat( lDialogString , ";echo 1$ANSWER >/tmp/tinyfd.txt';" ) ; strcat( lDialogString , "cat -v /tmp/tinyfd.txt"); } else if ( !gWarningDisplayed && ! isTerminalRunning( ) && ! terminalName() ) { gWarningDisplayed = 1 ; tinyfd_messageBox(gTitle,tinyfd_needs,"ok","warning",0); if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"no_solution");return (char *)0;} free(lDialogString); return NULL; } else { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"basicinput");return (char *)0;} if ( !gWarningDisplayed && !tinyfd_forceConsole) { gWarningDisplayed = 1 ; tinyfd_messageBox(gTitle,tinyfd_needs,"ok","warning",0); } if ( aTitle && strlen(aTitle) ) { printf("\n%s\n", aTitle); } if ( aMessage && strlen(aMessage) ) { printf("\n%s\n",aMessage); } printf("(esc+enter to cancel): "); fflush(stdout); if ( ! aDefaultInput ) { tcgetattr(STDIN_FILENO, & oldt) ; newt = oldt ; newt.c_lflag &= ~ECHO ; tcsetattr(STDIN_FILENO, TCSANOW, & newt); } lEOF = fgets(lBuff, MAX_PATH_OR_CMD, stdin); /* printf("lbuff<%c><%d>\n",lBuff[0],lBuff[0]); */ if ( ! lEOF || (lBuff[0] == '\0') ) { free(lDialogString); return NULL; } if ( lBuff[0] == '\n' ) { lEOF = fgets(lBuff, MAX_PATH_OR_CMD, stdin); /* printf("lbuff<%c><%d>\n",lBuff[0],lBuff[0]); */ if ( ! lEOF || (lBuff[0] == '\0') ) { free(lDialogString); return NULL; } } if ( ! aDefaultInput ) { tcsetattr(STDIN_FILENO, TCSANOW, & oldt); printf("\n"); } printf("\n"); if ( strchr(lBuff,27) ) { free(lDialogString); return NULL ; } if ( lBuff[strlen( lBuff ) -1] == '\n' ) { lBuff[strlen( lBuff ) -1] = '\0' ; } free(lDialogString); return lBuff ; } if (tinyfd_verbose) printf( "lDialogString: %s\n" , lDialogString ) ; lIn = popen( lDialogString , "r" ); if ( ! lIn ) { if ( fileExists("/tmp/tinyfd.txt") ) { wipefile("/tmp/tinyfd.txt"); remove("/tmp/tinyfd.txt"); } if ( fileExists("/tmp/tinyfd0.txt") ) { wipefile("/tmp/tinyfd0.txt"); remove("/tmp/tinyfd0.txt"); } free(lDialogString); return NULL ; } while ( fgets( lBuff , sizeof( lBuff ) , lIn ) != NULL ) {} pclose( lIn ) ; if ( fileExists("/tmp/tinyfd.txt") ) { wipefile("/tmp/tinyfd.txt"); remove("/tmp/tinyfd.txt"); } if ( fileExists("/tmp/tinyfd0.txt") ) { wipefile("/tmp/tinyfd0.txt"); remove("/tmp/tinyfd0.txt"); } /* printf( "len Buff: %lu\n" , strlen(lBuff) ) ; */ /* printf( "lBuff0: %s\n" , lBuff ) ; */ if ( lBuff[strlen( lBuff ) -1] == '\n' ) { lBuff[strlen( lBuff ) -1] = '\0' ; } /* printf( "lBuff1: %s len: %lu \n" , lBuff , strlen(lBuff) ) ; */ if ( lWasBasicXterm ) { if ( strstr(lBuff,"^[") ) /* esc was pressed */ { free(lDialogString); return NULL ; } } lResult = strncmp( lBuff , "1" , 1) ? 0 : 1 ; /* printf( "lResult: %d \n" , lResult ) ; */ if ( ! lResult ) { free(lDialogString); return NULL ; } /* printf( "lBuff+1: %s\n" , lBuff+1 ) ; */ free(lDialogString); return lBuff+1 ; } char * tinyfd_saveFileDialog( char const * aTitle , /* NULL or "" */ char const * aDefaultPathAndFile , /* NULL or "" */ int aNumOfFilterPatterns , /* 0 */ char const * const * aFilterPatterns , /* NULL or {"*.jpg","*.png"} */ char const * aSingleFilterDescription ) /* NULL or "image files" */ { static char lBuff[MAX_PATH_OR_CMD] ; char lDialogString[MAX_PATH_OR_CMD] ; char lString[MAX_PATH_OR_CMD] ; int i ; int lWasGraphicDialog = 0 ; int lWasXterm = 0 ; char * p ; char * lPointerInputBox ; FILE * lIn ; lBuff[0]='\0'; if (tfd_quoteDetected(aTitle)) return tinyfd_saveFileDialog("INVALID TITLE WITH QUOTES", aDefaultPathAndFile, aNumOfFilterPatterns, aFilterPatterns, aSingleFilterDescription); if (tfd_quoteDetected(aDefaultPathAndFile)) return tinyfd_saveFileDialog(aTitle, "INVALID DEFAULT_PATH WITH QUOTES", aNumOfFilterPatterns, aFilterPatterns, aSingleFilterDescription); if (tfd_quoteDetected(aSingleFilterDescription)) return tinyfd_saveFileDialog(aTitle, aDefaultPathAndFile, aNumOfFilterPatterns, aFilterPatterns, "INVALID FILTER_DESCRIPTION WITH QUOTES"); for (i = 0; i < aNumOfFilterPatterns; i++) { if (tfd_quoteDetected(aFilterPatterns[i])) return tinyfd_saveFileDialog("INVALID FILTER_PATTERN WITH QUOTES", aDefaultPathAndFile, 0, NULL, NULL); } if ( osascriptPresent( ) ) { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"applescript");return (char *)1;} strcpy( lDialogString , "osascript "); if ( ! osx9orBetter() ) strcat( lDialogString , " -e 'tell application \"Finder\"' -e 'Activate'"); strcat( lDialogString , " -e 'try' -e 'POSIX path of ( choose file name " ); if ( aTitle && strlen(aTitle) ) { strcat(lDialogString, "with prompt \"") ; strcat(lDialogString, aTitle) ; strcat(lDialogString, "\" ") ; } getPathWithoutFinalSlash( lString , aDefaultPathAndFile ) ; if ( strlen(lString) ) { strcat(lDialogString, "default location \"") ; strcat(lDialogString, lString ) ; strcat(lDialogString , "\" " ) ; } getLastName( lString , aDefaultPathAndFile ) ; if ( strlen(lString) ) { strcat(lDialogString, "default name \"") ; strcat(lDialogString, lString ) ; strcat(lDialogString , "\" " ) ; } strcat( lDialogString , ")' " ) ; strcat(lDialogString, "-e 'on error number -128' " ) ; strcat(lDialogString, "-e 'end try'") ; if ( ! osx9orBetter() ) strcat( lDialogString, " -e 'end tell'") ; } else if ( tfd_kdialogPresent() ) { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"kdialog");return (char *)1;} strcpy( lDialogString , "kdialog" ) ; if ( (tfd_kdialogPresent() == 2) && tfd_xpropPresent() ) { strcat(lDialogString, " --attach=$(xprop -root 32x '\t$0' _NET_ACTIVE_WINDOW | cut -f 2)"); /* contribution: Paul Rouget */ } strcat( lDialogString , " --getsavefilename " ) ; if ( aDefaultPathAndFile && strlen(aDefaultPathAndFile) ) { if ( aDefaultPathAndFile[0] != '/' ) { strcat(lDialogString, "$PWD/") ; } strcat(lDialogString, "\"") ; strcat(lDialogString, aDefaultPathAndFile ) ; strcat(lDialogString , "\"" ) ; } else { strcat(lDialogString, "$PWD/") ; } if ( aNumOfFilterPatterns > 0 ) { strcat(lDialogString , " \"" ) ; strcat( lDialogString , aFilterPatterns[0] ) ; for ( i = 1 ; i < aNumOfFilterPatterns ; i ++ ) { strcat( lDialogString , " " ) ; strcat( lDialogString , aFilterPatterns[i] ) ; } if ( aSingleFilterDescription && strlen(aSingleFilterDescription) ) { strcat( lDialogString , " | " ) ; strcat( lDialogString , aSingleFilterDescription ) ; } strcat( lDialogString , "\"" ) ; } if ( aTitle && strlen(aTitle) ) { strcat(lDialogString, " --title \"") ; strcat(lDialogString, aTitle) ; strcat(lDialogString, "\"") ; } } else if ( tfd_zenityPresent() || tfd_matedialogPresent() || tfd_shellementaryPresent() || tfd_qarmaPresent() ) { if ( tfd_zenityPresent() ) { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"zenity");return (char *)1;} strcpy( lDialogString , "zenity" ) ; if ( (tfd_zenity3Present() >= 4) && !getenv("SSH_TTY") && tfd_xpropPresent() ) { strcat( lDialogString, " --attach=$(sleep .01;xprop -root 32x '\t$0' _NET_ACTIVE_WINDOW | cut -f 2)"); /* contribution: Paul Rouget */ } } else if ( tfd_matedialogPresent() ) { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"matedialog");return (char *)1;} strcpy( lDialogString , "matedialog" ) ; } else if ( tfd_shellementaryPresent() ) { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"shellementary");return (char *)1;} strcpy( lDialogString , "shellementary" ) ; } else { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"qarma");return (char *)1;} strcpy( lDialogString , "qarma" ) ; if ( !getenv("SSH_TTY") && tfd_xpropPresent() ) { strcat(lDialogString, " --attach=$(xprop -root 32x '\t$0' _NET_ACTIVE_WINDOW | cut -f 2)"); /* contribution: Paul Rouget */ } } strcat(lDialogString, " --file-selection --save --confirm-overwrite" ) ; if ( aTitle && strlen(aTitle) ) { strcat(lDialogString, " --title=\"") ; strcat(lDialogString, aTitle) ; strcat(lDialogString, "\"") ; } if ( aDefaultPathAndFile && strlen(aDefaultPathAndFile) ) { strcat(lDialogString, " --filename=\"") ; strcat(lDialogString, aDefaultPathAndFile) ; strcat(lDialogString, "\"") ; } if ( aNumOfFilterPatterns > 0 ) { strcat( lDialogString , " --file-filter='" ) ; if ( aSingleFilterDescription && strlen(aSingleFilterDescription) ) { strcat( lDialogString , aSingleFilterDescription ) ; strcat( lDialogString , " |" ) ; } for ( i = 0 ; i < aNumOfFilterPatterns ; i ++ ) { strcat( lDialogString , " " ) ; strcat( lDialogString , aFilterPatterns[i] ) ; } strcat( lDialogString , "' --file-filter='All files | *'" ) ; } if (tinyfd_silent) strcat( lDialogString , " 2>/dev/null "); } else if (tfd_yadPresent()) { if (aTitle && !strcmp(aTitle, "tinyfd_query")) { strcpy(tinyfd_response, "yad"); return (char*)1; } strcpy(lDialogString, "yad --file-selection --save --confirm-overwrite"); if (aTitle && strlen(aTitle)) { strcat(lDialogString, " --title=\""); strcat(lDialogString, aTitle); strcat(lDialogString, "\""); } if (aDefaultPathAndFile && strlen(aDefaultPathAndFile)) { strcat(lDialogString, " --filename=\""); strcat(lDialogString, aDefaultPathAndFile); strcat(lDialogString, "\""); } if (aNumOfFilterPatterns > 0) { strcat(lDialogString, " --file-filter='"); if (aSingleFilterDescription && strlen(aSingleFilterDescription)) { strcat(lDialogString, aSingleFilterDescription); strcat(lDialogString, " |"); } for (i = 0; i < aNumOfFilterPatterns; i++) { strcat(lDialogString, " "); strcat(lDialogString, aFilterPatterns[i]); } strcat(lDialogString, "' --file-filter='All files | *'"); } if (tinyfd_silent) strcat(lDialogString, " 2>/dev/null "); } else if ( !xdialogPresent() && tkinter3Present( ) ) { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"python3-tkinter");return (char *)1;} strcpy( lDialogString , gPython3Name ) ; strcat( lDialogString , " -S -c \"import tkinter;from tkinter import filedialog;root=tkinter.Tk();root.withdraw();"); strcat( lDialogString , "res=filedialog.asksaveasfilename("); if ( aTitle && strlen(aTitle) ) { strcat(lDialogString, "title='") ; strcat(lDialogString, aTitle) ; strcat(lDialogString, "',") ; } if ( aDefaultPathAndFile && strlen(aDefaultPathAndFile) ) { getPathWithoutFinalSlash( lString , aDefaultPathAndFile ) ; if ( strlen(lString) ) { strcat(lDialogString, "initialdir='") ; strcat(lDialogString, lString ) ; strcat(lDialogString , "'," ) ; } getLastName( lString , aDefaultPathAndFile ) ; if ( strlen(lString) ) { strcat(lDialogString, "initialfile='") ; strcat(lDialogString, lString ) ; strcat(lDialogString , "'," ) ; } } if ( ( aNumOfFilterPatterns > 1 ) || ( (aNumOfFilterPatterns == 1) /* test because poor osx behaviour */ && ( aFilterPatterns[0][strlen(aFilterPatterns[0])-1] != '*' ) ) ) { strcat(lDialogString , "filetypes=(" ) ; strcat( lDialogString , "('" ) ; if ( aSingleFilterDescription && strlen(aSingleFilterDescription) ) { strcat( lDialogString , aSingleFilterDescription ) ; } strcat( lDialogString , "',(" ) ; for ( i = 0 ; i < aNumOfFilterPatterns ; i ++ ) { strcat( lDialogString , "'" ) ; strcat( lDialogString , aFilterPatterns[i] ) ; strcat( lDialogString , "'," ) ; } strcat( lDialogString , "))," ) ; strcat( lDialogString , "('All files','*'))" ) ; } strcat( lDialogString, ");\nif not isinstance(res, tuple):\n\tprint(res)\n\"" ) ; } else if ( !xdialogPresent() && tkinter2Present( ) ) { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"python2-tkinter");return (char *)1;} strcpy( lDialogString , "export PYTHONIOENCODING=utf-8;" ) ; strcat( lDialogString , gPython2Name ) ; if ( ! isTerminalRunning( ) && tfd_isDarwin( )) { strcat( lDialogString , " -i" ) ; /* for osx without console */ } strcat( lDialogString , " -S -c \"import Tkinter,tkFileDialog;root=Tkinter.Tk();root.withdraw();"); if ( tfd_isDarwin( ) ) { strcat( lDialogString , "import os;os.system('''/usr/bin/osascript -e 'tell app \\\"Finder\\\" to set\ frontmost of process \\\"Python\\\" to true' ''');"); } strcat( lDialogString , "res=tkFileDialog.asksaveasfilename("); if ( aTitle && strlen(aTitle) ) { strcat(lDialogString, "title='") ; strcat(lDialogString, aTitle) ; strcat(lDialogString, "',") ; } if ( aDefaultPathAndFile && strlen(aDefaultPathAndFile) ) { getPathWithoutFinalSlash( lString , aDefaultPathAndFile ) ; if ( strlen(lString) ) { strcat(lDialogString, "initialdir='") ; strcat(lDialogString, lString ) ; strcat(lDialogString , "'," ) ; } getLastName( lString , aDefaultPathAndFile ) ; if ( strlen(lString) ) { strcat(lDialogString, "initialfile='") ; strcat(lDialogString, lString ) ; strcat(lDialogString , "'," ) ; } } if ( ( aNumOfFilterPatterns > 1 ) || ( (aNumOfFilterPatterns == 1) /* test because poor osx behaviour */ && ( aFilterPatterns[0][strlen(aFilterPatterns[0])-1] != '*' ) ) ) { strcat(lDialogString , "filetypes=(" ) ; strcat( lDialogString , "('" ) ; if ( aSingleFilterDescription && strlen(aSingleFilterDescription) ) { strcat( lDialogString , aSingleFilterDescription ) ; } strcat( lDialogString , "',(" ) ; for ( i = 0 ; i < aNumOfFilterPatterns ; i ++ ) { strcat( lDialogString , "'" ) ; strcat( lDialogString , aFilterPatterns[i] ) ; strcat( lDialogString , "'," ) ; } strcat( lDialogString , "))," ) ; strcat( lDialogString , "('All files','*'))" ) ; } strcat( lDialogString, ");\nif not isinstance(res, tuple):\n\tprint res \n\"" ) ; } else if ( xdialogPresent() || dialogName() ) { if ( xdialogPresent( ) ) { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"xdialog");return (char *)1;} lWasGraphicDialog = 1 ; strcpy( lDialogString , "(Xdialog " ) ; } else if ( isTerminalRunning( ) ) { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"dialog");return (char *)0;} strcpy( lDialogString , "(dialog " ) ; } else { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"dialog");return (char *)0;} lWasXterm = 1 ; strcpy( lDialogString , terminalName() ) ; strcat( lDialogString , "'(" ) ; strcat( lDialogString , dialogName() ) ; strcat( lDialogString , " " ) ; } if ( aTitle && strlen(aTitle) ) { strcat(lDialogString, "--title \"") ; strcat(lDialogString, aTitle) ; strcat(lDialogString, "\" ") ; } if ( !xdialogPresent() && !gdialogPresent() ) { strcat(lDialogString, "--backtitle \"") ; strcat(lDialogString, "tab: focus | /: populate | spacebar: fill text field | ok: TEXT FIELD ONLY") ; strcat(lDialogString, "\" ") ; } strcat( lDialogString , "--fselect \"" ) ; if ( aDefaultPathAndFile && strlen(aDefaultPathAndFile) ) { if ( ! strchr(aDefaultPathAndFile, '/') ) { strcat(lDialogString, "./") ; } strcat(lDialogString, aDefaultPathAndFile) ; } else if ( ! isTerminalRunning( ) && !lWasGraphicDialog ) { strcat(lDialogString, getenv("HOME")) ; strcat(lDialogString, "/") ; } else { strcat(lDialogString, "./") ; } if ( lWasGraphicDialog ) { strcat(lDialogString, "\" 0 60 ) 2>&1 ") ; } else { strcat(lDialogString, "\" 0 60 >/dev/tty) ") ; if ( lWasXterm ) { strcat( lDialogString , "2>/tmp/tinyfd.txt';cat /tmp/tinyfd.txt;rm /tmp/tinyfd.txt"); } else { strcat(lDialogString, "2>&1 ; clear >/dev/tty") ; } } } else { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){return tinyfd_inputBox(aTitle,NULL,NULL);} strcpy(lBuff, "Save file in "); strcat(lBuff, getCurDir()); lPointerInputBox = tinyfd_inputBox(NULL, NULL, NULL); /* obtain a pointer on the current content of tinyfd_inputBox */ if (lPointerInputBox) strcpy(lString, lPointerInputBox); /* preserve the current content of tinyfd_inputBox */ p = tinyfd_inputBox(aTitle, lBuff, ""); if (p) strcpy(lBuff, p); else lBuff[0] = '\0'; if (lPointerInputBox) strcpy(lPointerInputBox, lString); /* restore its previous content to tinyfd_inputBox */ p = lBuff; getPathWithoutFinalSlash( lString , p ) ; if ( strlen( lString ) && ! dirExists( lString ) ) { return NULL ; } getLastName(lString,p); if ( ! strlen(lString) ) { return NULL; } return p ; } if (tinyfd_verbose) printf( "lDialogString: %s\n" , lDialogString ) ; if ( ! ( lIn = popen( lDialogString , "r" ) ) ) { return NULL ; } while ( fgets( lBuff , sizeof( lBuff ) , lIn ) != NULL ) {} pclose( lIn ) ; if ( lBuff[strlen( lBuff ) -1] == '\n' ) { lBuff[strlen( lBuff ) -1] = '\0' ; } /* printf( "lBuff: %s\n" , lBuff ) ; */ if ( ! strlen(lBuff) ) { return NULL; } getPathWithoutFinalSlash( lString , lBuff ) ; if ( strlen( lString ) && ! dirExists( lString ) ) { return NULL ; } getLastName(lString,lBuff); if ( ! filenameValid(lString) ) { return NULL; } return lBuff ; } /* in case of multiple files, the separator is | */ char * tinyfd_openFileDialog( char const * aTitle , /* NULL or "" */ char const * aDefaultPathAndFile , /* NULL or "" */ int aNumOfFilterPatterns , /* 0 */ char const * const * aFilterPatterns , /* NULL or {"*.jpg","*.png"} */ char const * aSingleFilterDescription , /* NULL or "image files" */ int aAllowMultipleSelects ) /* 0 or 1 */ { char lDialogString[MAX_PATH_OR_CMD] ; char lString[MAX_PATH_OR_CMD] ; int i ; FILE * lIn ; char * p ; char * lPointerInputBox ; int lWasKdialog = 0 ; int lWasGraphicDialog = 0 ; int lWasXterm = 0 ; size_t lFullBuffLen ; static char * lBuff = NULL; if (tfd_quoteDetected(aTitle)) return tinyfd_openFileDialog("INVALID TITLE WITH QUOTES", aDefaultPathAndFile, aNumOfFilterPatterns, aFilterPatterns, aSingleFilterDescription, aAllowMultipleSelects); if (tfd_quoteDetected(aDefaultPathAndFile)) return tinyfd_openFileDialog(aTitle, "INVALID DEFAULT_PATH WITH QUOTES", aNumOfFilterPatterns, aFilterPatterns, aSingleFilterDescription, aAllowMultipleSelects); if (tfd_quoteDetected(aSingleFilterDescription)) return tinyfd_openFileDialog(aTitle, aDefaultPathAndFile, aNumOfFilterPatterns, aFilterPatterns, "INVALID FILTER_DESCRIPTION WITH QUOTES", aAllowMultipleSelects); for (i = 0; i < aNumOfFilterPatterns; i++) { if (tfd_quoteDetected(aFilterPatterns[i])) return tinyfd_openFileDialog("INVALID FILTER_PATTERN WITH QUOTES", aDefaultPathAndFile, 0, NULL, NULL, aAllowMultipleSelects); } free(lBuff); if (aTitle&&!strcmp(aTitle,"tinyfd_query")) { lBuff = NULL; } else { if (aAllowMultipleSelects) { lFullBuffLen = MAX_MULTIPLE_FILES * MAX_PATH_OR_CMD + 1; lBuff = (char *)(malloc(lFullBuffLen * sizeof(char))); if (!lBuff) { lFullBuffLen = LOW_MULTIPLE_FILES * MAX_PATH_OR_CMD + 1; lBuff = (char *)( malloc( lFullBuffLen * sizeof(char))); } } else { lFullBuffLen = MAX_PATH_OR_CMD + 1; lBuff = (char *)(malloc(lFullBuffLen * sizeof(char))); } if (!lBuff) return NULL; lBuff[0]='\0'; } if ( osascriptPresent( ) ) { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"applescript");return (char *)1;} strcpy( lDialogString , "osascript "); if ( ! osx9orBetter() ) strcat( lDialogString , " -e 'tell application \"System Events\"' -e 'Activate'"); strcat( lDialogString , " -e 'try' -e '" ); if ( ! aAllowMultipleSelects ) { strcat( lDialogString , "POSIX path of ( " ); } else { strcat( lDialogString , "set mylist to " ); } strcat( lDialogString , "choose file " ); if ( aTitle && strlen(aTitle) ) { strcat(lDialogString, "with prompt \"") ; strcat(lDialogString, aTitle) ; strcat(lDialogString, "\" ") ; } getPathWithoutFinalSlash( lString , aDefaultPathAndFile ) ; if ( strlen(lString) ) { strcat(lDialogString, "default location \"") ; strcat(lDialogString, lString ) ; strcat(lDialogString , "\" " ) ; } if ( aNumOfFilterPatterns > 0 ) { strcat(lDialogString , "of type {\"" ); strcat( lDialogString , aFilterPatterns[0] + 2 ) ; strcat( lDialogString , "\"" ) ; for ( i = 1 ; i < aNumOfFilterPatterns ; i ++ ) { strcat( lDialogString , ",\"" ) ; strcat( lDialogString , aFilterPatterns[i] + 2) ; strcat( lDialogString , "\"" ) ; } strcat( lDialogString , "} " ) ; } if ( aAllowMultipleSelects ) { strcat( lDialogString , "multiple selections allowed true ' " ) ; strcat( lDialogString , "-e 'set mystring to POSIX path of item 1 of mylist' " ); strcat( lDialogString , "-e 'repeat with i from 2 to the count of mylist' " ); strcat( lDialogString , "-e 'set mystring to mystring & \"|\"' " ); strcat( lDialogString , "-e 'set mystring to mystring & POSIX path of item i of mylist' " ); strcat( lDialogString , "-e 'end repeat' " ); strcat( lDialogString , "-e 'mystring' " ); } else { strcat( lDialogString , ")' " ) ; } strcat(lDialogString, "-e 'on error number -128' " ) ; strcat(lDialogString, "-e 'end try'") ; if ( ! osx9orBetter() ) strcat( lDialogString, " -e 'end tell'") ; } else if ( tfd_kdialogPresent() ) { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"kdialog");return (char *)1;} lWasKdialog = 1 ; strcpy( lDialogString , "kdialog" ) ; if ( (tfd_kdialogPresent() == 2) && tfd_xpropPresent() ) { strcat(lDialogString, " --attach=$(xprop -root 32x '\t$0' _NET_ACTIVE_WINDOW | cut -f 2)"); /* contribution: Paul Rouget */ } strcat( lDialogString , " --getopenfilename " ) ; if ( aDefaultPathAndFile && strlen(aDefaultPathAndFile) ) { if ( aDefaultPathAndFile[0] != '/' ) { strcat(lDialogString, "$PWD/") ; } strcat(lDialogString, "\"") ; strcat(lDialogString, aDefaultPathAndFile ) ; strcat(lDialogString , "\"" ) ; } else { strcat(lDialogString, "$PWD/") ; } if ( aNumOfFilterPatterns > 0 ) { strcat(lDialogString , " \"" ) ; strcat( lDialogString , aFilterPatterns[0] ) ; for ( i = 1 ; i < aNumOfFilterPatterns ; i ++ ) { strcat( lDialogString , " " ) ; strcat( lDialogString , aFilterPatterns[i] ) ; } if ( aSingleFilterDescription && strlen(aSingleFilterDescription) ) { strcat( lDialogString , " | " ) ; strcat( lDialogString , aSingleFilterDescription ) ; } strcat( lDialogString , "\"" ) ; } if ( aAllowMultipleSelects ) { strcat( lDialogString , " --multiple --separate-output" ) ; } if ( aTitle && strlen(aTitle) ) { strcat(lDialogString, " --title \"") ; strcat(lDialogString, aTitle) ; strcat(lDialogString, "\"") ; } } else if ( tfd_zenityPresent() || tfd_matedialogPresent() || tfd_shellementaryPresent() || tfd_qarmaPresent() ) { if ( tfd_zenityPresent() ) { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"zenity");return (char *)1;} strcpy( lDialogString , "zenity" ) ; if ( (tfd_zenity3Present() >= 4) && !getenv("SSH_TTY") && tfd_xpropPresent() ) { strcat( lDialogString, " --attach=$(sleep .01;xprop -root 32x '\t$0' _NET_ACTIVE_WINDOW | cut -f 2)"); /* contribution: Paul Rouget */ } } else if ( tfd_matedialogPresent() ) { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"matedialog");return (char *)1;} strcpy( lDialogString , "matedialog" ) ; } else if ( tfd_shellementaryPresent() ) { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"shellementary");return (char *)1;} strcpy( lDialogString , "shellementary" ) ; } else { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"qarma");return (char *)1;} strcpy( lDialogString , "qarma" ) ; if ( !getenv("SSH_TTY") && tfd_xpropPresent() ) { strcat(lDialogString, " --attach=$(xprop -root 32x '\t$0' _NET_ACTIVE_WINDOW | cut -f 2)"); /* contribution: Paul Rouget */ } } strcat( lDialogString , " --file-selection" ) ; if ( aAllowMultipleSelects ) { strcat( lDialogString , " --multiple" ) ; } if ( aTitle && strlen(aTitle) ) { strcat(lDialogString, " --title=\"") ; strcat(lDialogString, aTitle) ; strcat(lDialogString, "\"") ; } if ( aDefaultPathAndFile && strlen(aDefaultPathAndFile) ) { strcat(lDialogString, " --filename=\"") ; strcat(lDialogString, aDefaultPathAndFile) ; strcat(lDialogString, "\"") ; } if ( aNumOfFilterPatterns > 0 ) { strcat( lDialogString , " --file-filter='" ) ; if ( aSingleFilterDescription && strlen(aSingleFilterDescription) ) { strcat( lDialogString , aSingleFilterDescription ) ; strcat( lDialogString , " |" ) ; } for ( i = 0 ; i < aNumOfFilterPatterns ; i ++ ) { strcat( lDialogString , " " ) ; strcat( lDialogString , aFilterPatterns[i] ) ; } strcat( lDialogString , "' --file-filter='All files | *'" ) ; } if (tinyfd_silent) strcat( lDialogString , " 2>/dev/null "); } else if (tfd_yadPresent()) { if (aTitle && !strcmp(aTitle, "tinyfd_query")) { strcpy(tinyfd_response, "yad"); return (char*)1; } strcpy(lDialogString, "yad --file-selection"); if (aAllowMultipleSelects) { strcat(lDialogString, " --multiple"); } if (aTitle && strlen(aTitle)) { strcat(lDialogString, " --title=\""); strcat(lDialogString, aTitle); strcat(lDialogString, "\""); } if (aDefaultPathAndFile && strlen(aDefaultPathAndFile)) { strcat(lDialogString, " --filename=\""); strcat(lDialogString, aDefaultPathAndFile); strcat(lDialogString, "\""); } if (aNumOfFilterPatterns > 0) { strcat(lDialogString, " --file-filter='"); if (aSingleFilterDescription && strlen(aSingleFilterDescription)) { strcat(lDialogString, aSingleFilterDescription); strcat(lDialogString, " |"); } for (i = 0; i < aNumOfFilterPatterns; i++) { strcat(lDialogString, " "); strcat(lDialogString, aFilterPatterns[i]); } strcat(lDialogString, "' --file-filter='All files | *'"); } if (tinyfd_silent) strcat(lDialogString, " 2>/dev/null "); } else if ( tkinter3Present( ) ) { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"python3-tkinter");return (char *)1;} strcpy( lDialogString , gPython3Name ) ; strcat( lDialogString , " -S -c \"import tkinter;from tkinter import filedialog;root=tkinter.Tk();root.withdraw();"); strcat( lDialogString , "lFiles=filedialog.askopenfilename("); if ( aAllowMultipleSelects ) { strcat( lDialogString , "multiple=1," ) ; } if ( aTitle && strlen(aTitle) ) { strcat(lDialogString, "title='") ; strcat(lDialogString, aTitle) ; strcat(lDialogString, "',") ; } if ( aDefaultPathAndFile && strlen(aDefaultPathAndFile) ) { getPathWithoutFinalSlash( lString , aDefaultPathAndFile ) ; if ( strlen(lString) ) { strcat(lDialogString, "initialdir='") ; strcat(lDialogString, lString ) ; strcat(lDialogString , "'," ) ; } getLastName( lString , aDefaultPathAndFile ) ; if ( strlen(lString) ) { strcat(lDialogString, "initialfile='") ; strcat(lDialogString, lString ) ; strcat(lDialogString , "'," ) ; } } if ( ( aNumOfFilterPatterns > 1 ) || ( ( aNumOfFilterPatterns == 1 ) /*test because poor osx behaviour*/ && ( aFilterPatterns[0][strlen(aFilterPatterns[0])-1] != '*' ) ) ) { strcat(lDialogString , "filetypes=(" ) ; strcat( lDialogString , "('" ) ; if ( aSingleFilterDescription && strlen(aSingleFilterDescription) ) { strcat( lDialogString , aSingleFilterDescription ) ; } strcat( lDialogString , "',(" ) ; for ( i = 0 ; i < aNumOfFilterPatterns ; i ++ ) { strcat( lDialogString , "'" ) ; strcat( lDialogString , aFilterPatterns[i] ) ; strcat( lDialogString , "'," ) ; } strcat( lDialogString , "))," ) ; strcat( lDialogString , "('All files','*'))" ) ; } strcat( lDialogString , ");\ \nif not isinstance(lFiles, tuple):\n\tprint(lFiles)\nelse:\ \n\tlFilesString=''\n\tfor lFile in lFiles:\n\t\tlFilesString+=str(lFile)+'|'\ \n\tprint(lFilesString[:-1])\n\"" ) ; } else if ( tkinter2Present( ) ) { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"python2-tkinter");return (char *)1;} strcpy( lDialogString , "export PYTHONIOENCODING=utf-8;" ) ; strcat( lDialogString , gPython2Name ) ; if ( ! isTerminalRunning( ) && tfd_isDarwin( ) ) { strcat( lDialogString , " -i" ) ; /* for osx without console */ } strcat( lDialogString , " -S -c \"import Tkinter,tkFileDialog;root=Tkinter.Tk();root.withdraw();"); if ( tfd_isDarwin( ) ) { strcat( lDialogString , "import os;os.system('''/usr/bin/osascript -e 'tell app \\\"Finder\\\" to set \ frontmost of process \\\"Python\\\" to true' ''');"); } strcat( lDialogString , "lFiles=tkFileDialog.askopenfilename("); if ( aAllowMultipleSelects ) { strcat( lDialogString , "multiple=1," ) ; } if ( aTitle && strlen(aTitle) ) { strcat(lDialogString, "title='") ; strcat(lDialogString, aTitle) ; strcat(lDialogString, "',") ; } if ( aDefaultPathAndFile && strlen(aDefaultPathAndFile) ) { getPathWithoutFinalSlash( lString , aDefaultPathAndFile ) ; if ( strlen(lString) ) { strcat(lDialogString, "initialdir='") ; strcat(lDialogString, lString ) ; strcat(lDialogString , "'," ) ; } getLastName( lString , aDefaultPathAndFile ) ; if ( strlen(lString) ) { strcat(lDialogString, "initialfile='") ; strcat(lDialogString, lString ) ; strcat(lDialogString , "'," ) ; } } if ( ( aNumOfFilterPatterns > 1 ) || ( ( aNumOfFilterPatterns == 1 ) /*test because poor osx behaviour*/ && ( aFilterPatterns[0][strlen(aFilterPatterns[0])-1] != '*' ) ) ) { strcat(lDialogString , "filetypes=(" ) ; strcat( lDialogString , "('" ) ; if ( aSingleFilterDescription && strlen(aSingleFilterDescription) ) { strcat( lDialogString , aSingleFilterDescription ) ; } strcat( lDialogString , "',(" ) ; for ( i = 0 ; i < aNumOfFilterPatterns ; i ++ ) { strcat( lDialogString , "'" ) ; strcat( lDialogString , aFilterPatterns[i] ) ; strcat( lDialogString , "'," ) ; } strcat( lDialogString , "))," ) ; strcat( lDialogString , "('All files','*'))" ) ; } strcat( lDialogString , ");\ \nif not isinstance(lFiles, tuple):\n\tprint lFiles\nelse:\ \n\tlFilesString=''\n\tfor lFile in lFiles:\n\t\tlFilesString+=str(lFile)+'|'\ \n\tprint lFilesString[:-1]\n\"" ) ; } else if ( xdialogPresent() || dialogName() ) { if ( xdialogPresent( ) ) { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"xdialog");return (char *)1;} lWasGraphicDialog = 1 ; strcpy( lDialogString , "(Xdialog " ) ; } else if ( isTerminalRunning( ) ) { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"dialog");return (char *)0;} strcpy( lDialogString , "(dialog " ) ; } else { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"dialog");return (char *)0;} lWasXterm = 1 ; strcpy( lDialogString , terminalName() ) ; strcat( lDialogString , "'(" ) ; strcat( lDialogString , dialogName() ) ; strcat( lDialogString , " " ) ; } if ( aTitle && strlen(aTitle) ) { strcat(lDialogString, "--title \"") ; strcat(lDialogString, aTitle) ; strcat(lDialogString, "\" ") ; } if ( !xdialogPresent() && !gdialogPresent() ) { strcat(lDialogString, "--backtitle \"") ; strcat(lDialogString, "tab: focus | /: populate | spacebar: fill text field | ok: TEXT FIELD ONLY") ; strcat(lDialogString, "\" ") ; } strcat( lDialogString , "--fselect \"" ) ; if ( aDefaultPathAndFile && strlen(aDefaultPathAndFile) ) { if ( ! strchr(aDefaultPathAndFile, '/') ) { strcat(lDialogString, "./") ; } strcat(lDialogString, aDefaultPathAndFile) ; } else if ( ! isTerminalRunning( ) && !lWasGraphicDialog ) { strcat(lDialogString, getenv("HOME")) ; strcat(lDialogString, "/"); } else { strcat(lDialogString, "./") ; } if ( lWasGraphicDialog ) { strcat(lDialogString, "\" 0 60 ) 2>&1 ") ; } else { strcat(lDialogString, "\" 0 60 >/dev/tty) ") ; if ( lWasXterm ) { strcat( lDialogString , "2>/tmp/tinyfd.txt';cat /tmp/tinyfd.txt;rm /tmp/tinyfd.txt"); } else { strcat(lDialogString, "2>&1 ; clear >/dev/tty") ; } } } else { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){return tinyfd_inputBox(aTitle,NULL,NULL);} strcpy(lBuff, "Open file from "); strcat(lBuff, getCurDir()); lPointerInputBox = tinyfd_inputBox(NULL, NULL, NULL); /* obtain a pointer on the current content of tinyfd_inputBox */ if (lPointerInputBox) strcpy(lDialogString, lPointerInputBox); /* preserve the current content of tinyfd_inputBox */ p = tinyfd_inputBox(aTitle, lBuff, ""); if ( p ) strcpy(lBuff, p); else lBuff[0] = '\0'; if (lPointerInputBox) strcpy(lPointerInputBox, lDialogString); /* restore its previous content to tinyfd_inputBox */ if ( ! fileExists(lBuff) ) { free(lBuff); lBuff = NULL; } else { lBuff = (char *)( realloc( lBuff, (strlen(lBuff)+1) * sizeof(char))); } return lBuff ; } if (tinyfd_verbose) printf( "lDialogString: %s\n" , lDialogString ) ; if ( ! ( lIn = popen( lDialogString , "r" ) ) ) { free(lBuff); lBuff = NULL; return NULL ; } lBuff[0]='\0'; p = lBuff; while ( fgets( p , sizeof( lBuff ) , lIn ) != NULL ) { p += strlen( p ); } pclose( lIn ) ; if ( lBuff[strlen( lBuff ) -1] == '\n' ) { lBuff[strlen( lBuff ) -1] = '\0' ; } /* printf( "lBuff: %s\n" , lBuff ) ; */ if ( lWasKdialog && aAllowMultipleSelects ) { p = lBuff ; while ( ( p = strchr( p , '\n' ) ) ) * p = '|' ; } /* printf( "lBuff2: %s\n" , lBuff ) ; */ if ( ! strlen( lBuff ) ) { free(lBuff); lBuff = NULL; return NULL; } if ( aAllowMultipleSelects && strchr(lBuff, '|') ) { if( ! ensureFilesExist( lBuff , lBuff ) ) { free(lBuff); lBuff = NULL; return NULL; } } else if ( !fileExists(lBuff) ) { free(lBuff); lBuff = NULL; return NULL; } lBuff = (char *)( realloc( lBuff, (strlen(lBuff)+1) * sizeof(char))); /*printf( "lBuff3: %s\n" , lBuff ) ; */ return lBuff ; } char * tinyfd_selectFolderDialog( char const * aTitle , /* "" */ char const * aDefaultPath ) /* "" */ { static char lBuff[MAX_PATH_OR_CMD] ; char lDialogString[MAX_PATH_OR_CMD] ; FILE * lIn ; char * p ; char * lPointerInputBox ; int lWasGraphicDialog = 0 ; int lWasXterm = 0 ; lBuff[0]='\0'; if (tfd_quoteDetected(aTitle)) return tinyfd_selectFolderDialog("INVALID TITLE WITH QUOTES", aDefaultPath); if (tfd_quoteDetected(aDefaultPath)) return tinyfd_selectFolderDialog(aTitle, "INVALID DEFAULT_PATH WITH QUOTES"); if ( osascriptPresent( )) { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"applescript");return (char *)1;} strcpy( lDialogString , "osascript "); if ( ! osx9orBetter() ) strcat( lDialogString , " -e 'tell application \"System Events\"' -e 'Activate'"); strcat( lDialogString , " -e 'try' -e 'POSIX path of ( choose folder "); if ( aTitle && strlen(aTitle) ) { strcat(lDialogString, "with prompt \"") ; strcat(lDialogString, aTitle) ; strcat(lDialogString, "\" ") ; } if ( aDefaultPath && strlen(aDefaultPath) ) { strcat(lDialogString, "default location \"") ; strcat(lDialogString, aDefaultPath ) ; strcat(lDialogString , "\" " ) ; } strcat( lDialogString , ")' " ) ; strcat(lDialogString, "-e 'on error number -128' " ) ; strcat(lDialogString, "-e 'end try'") ; if ( ! osx9orBetter() ) strcat( lDialogString, " -e 'end tell'") ; } else if ( tfd_kdialogPresent() ) { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"kdialog");return (char *)1;} strcpy( lDialogString , "kdialog" ) ; if ( (tfd_kdialogPresent() == 2) && tfd_xpropPresent() ) { strcat(lDialogString, " --attach=$(xprop -root 32x '\t$0' _NET_ACTIVE_WINDOW | cut -f 2)"); /* contribution: Paul Rouget */ } strcat( lDialogString , " --getexistingdirectory " ) ; if ( aDefaultPath && strlen(aDefaultPath) ) { if ( aDefaultPath[0] != '/' ) { strcat(lDialogString, "$PWD/") ; } strcat(lDialogString, "\"") ; strcat(lDialogString, aDefaultPath ) ; strcat(lDialogString , "\"" ) ; } else { strcat(lDialogString, "$PWD/") ; } if ( aTitle && strlen(aTitle) ) { strcat(lDialogString, " --title \"") ; strcat(lDialogString, aTitle) ; strcat(lDialogString, "\"") ; } } else if ( tfd_zenityPresent() || tfd_matedialogPresent() || tfd_shellementaryPresent() || tfd_qarmaPresent() ) { if ( tfd_zenityPresent() ) { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"zenity");return (char *)1;} strcpy( lDialogString , "zenity" ) ; if ( (tfd_zenity3Present() >= 4) && !getenv("SSH_TTY") && tfd_xpropPresent() ) { strcat( lDialogString, " --attach=$(sleep .01;xprop -root 32x '\t$0' _NET_ACTIVE_WINDOW | cut -f 2)"); /* contribution: Paul Rouget */ } } else if ( tfd_matedialogPresent() ) { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"matedialog");return (char *)1;} strcpy( lDialogString , "matedialog" ) ; } else if ( tfd_shellementaryPresent() ) { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"shellementary");return (char *)1;} strcpy( lDialogString , "shellementary" ) ; } else { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"qarma");return (char *)1;} strcpy( lDialogString , "qarma" ) ; if ( !getenv("SSH_TTY") && tfd_xpropPresent() ) { strcat(lDialogString, " --attach=$(xprop -root 32x '\t$0' _NET_ACTIVE_WINDOW | cut -f 2)"); /* contribution: Paul Rouget */ } } strcat( lDialogString , " --file-selection --directory" ) ; if ( aTitle && strlen(aTitle) ) { strcat(lDialogString, " --title=\"") ; strcat(lDialogString, aTitle) ; strcat(lDialogString, "\"") ; } if ( aDefaultPath && strlen(aDefaultPath) ) { strcat(lDialogString, " --filename=\"") ; strcat(lDialogString, aDefaultPath) ; strcat(lDialogString, "\"") ; } if (tinyfd_silent) strcat( lDialogString , " 2>/dev/null "); } else if (tfd_yadPresent()) { if (aTitle && !strcmp(aTitle, "tinyfd_query")) { strcpy(tinyfd_response, "yad"); return (char*)1; } strcpy(lDialogString, "yad --file-selection --directory"); if (aTitle && strlen(aTitle)) { strcat(lDialogString, " --title=\""); strcat(lDialogString, aTitle); strcat(lDialogString, "\""); } if (aDefaultPath && strlen(aDefaultPath)) { strcat(lDialogString, " --filename=\""); strcat(lDialogString, aDefaultPath); strcat(lDialogString, "\""); } if (tinyfd_silent) strcat(lDialogString, " 2>/dev/null "); } else if ( !xdialogPresent() && tkinter3Present( ) ) { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"python3-tkinter");return (char *)1;} strcpy( lDialogString , gPython3Name ) ; strcat( lDialogString , " -S -c \"import tkinter;from tkinter import filedialog;root=tkinter.Tk();root.withdraw();"); strcat( lDialogString , "res=filedialog.askdirectory("); if ( aTitle && strlen(aTitle) ) { strcat(lDialogString, "title='") ; strcat(lDialogString, aTitle) ; strcat(lDialogString, "',") ; } if ( aDefaultPath && strlen(aDefaultPath) ) { strcat(lDialogString, "initialdir='") ; strcat(lDialogString, aDefaultPath ) ; strcat(lDialogString , "'" ) ; } strcat( lDialogString, ");\nif not isinstance(res, tuple):\n\tprint(res)\n\"" ) ; } else if ( !xdialogPresent() && tkinter2Present( ) ) { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"python2-tkinter");return (char *)1;} strcpy( lDialogString , "export PYTHONIOENCODING=utf-8;" ) ; strcat( lDialogString , gPython2Name ) ; if ( ! isTerminalRunning( ) && tfd_isDarwin( ) ) { strcat( lDialogString , " -i" ) ; /* for osx without console */ } strcat( lDialogString , " -S -c \"import Tkinter,tkFileDialog;root=Tkinter.Tk();root.withdraw();"); if ( tfd_isDarwin( ) ) { strcat( lDialogString , "import os;os.system('''/usr/bin/osascript -e 'tell app \\\"Finder\\\" to set \ frontmost of process \\\"Python\\\" to true' ''');"); } strcat( lDialogString , "print tkFileDialog.askdirectory("); if ( aTitle && strlen(aTitle) ) { strcat(lDialogString, "title='") ; strcat(lDialogString, aTitle) ; strcat(lDialogString, "',") ; } if ( aDefaultPath && strlen(aDefaultPath) ) { strcat(lDialogString, "initialdir='") ; strcat(lDialogString, aDefaultPath ) ; strcat(lDialogString , "'" ) ; } strcat( lDialogString , ")\"" ) ; } else if ( xdialogPresent() || dialogName() ) { if ( xdialogPresent( ) ) { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"xdialog");return (char *)1;} lWasGraphicDialog = 1 ; strcpy( lDialogString , "(Xdialog " ) ; } else if ( isTerminalRunning( ) ) { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"dialog");return (char *)0;} strcpy( lDialogString , "(dialog " ) ; } else { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"dialog");return (char *)0;} lWasXterm = 1 ; strcpy( lDialogString , terminalName() ) ; strcat( lDialogString , "'(" ) ; strcat( lDialogString , dialogName() ) ; strcat( lDialogString , " " ) ; } if ( aTitle && strlen(aTitle) ) { strcat(lDialogString, "--title \"") ; strcat(lDialogString, aTitle) ; strcat(lDialogString, "\" ") ; } if ( !xdialogPresent() && !gdialogPresent() ) { strcat(lDialogString, "--backtitle \"") ; strcat(lDialogString, "tab: focus | /: populate | spacebar: fill text field | ok: TEXT FIELD ONLY") ; strcat(lDialogString, "\" ") ; } strcat( lDialogString , "--dselect \"" ) ; if ( aDefaultPath && strlen(aDefaultPath) ) { strcat(lDialogString, aDefaultPath) ; ensureFinalSlash(lDialogString); } else if ( ! isTerminalRunning( ) && !lWasGraphicDialog ) { strcat(lDialogString, getenv("HOME")) ; strcat(lDialogString, "/"); } else { strcat(lDialogString, "./") ; } if ( lWasGraphicDialog ) { strcat(lDialogString, "\" 0 60 ) 2>&1 ") ; } else { strcat(lDialogString, "\" 0 60 >/dev/tty) ") ; if ( lWasXterm ) { strcat( lDialogString , "2>/tmp/tinyfd.txt';cat /tmp/tinyfd.txt;rm /tmp/tinyfd.txt"); } else { strcat(lDialogString, "2>&1 ; clear >/dev/tty") ; } } } else { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){return tinyfd_inputBox(aTitle,NULL,NULL);} strcpy(lBuff, "Select folder from "); strcat(lBuff, getCurDir()); lPointerInputBox = tinyfd_inputBox(NULL, NULL, NULL); /* obtain a pointer on the current content of tinyfd_inputBox */ if (lPointerInputBox) strcpy(lDialogString, lPointerInputBox); /* preserve the current content of tinyfd_inputBox */ p = tinyfd_inputBox(aTitle, lBuff, ""); if (p) strcpy(lBuff, p); else lBuff[0] = '\0'; if (lPointerInputBox) strcpy(lPointerInputBox, lDialogString); /* restore its previous content to tinyfd_inputBox */ p = lBuff; if ( !p || ! strlen( p ) || ! dirExists( p ) ) { return NULL ; } return p ; } if (tinyfd_verbose) printf( "lDialogString: %s\n" , lDialogString ) ; if ( ! ( lIn = popen( lDialogString , "r" ) ) ) { return NULL ; } while ( fgets( lBuff , sizeof( lBuff ) , lIn ) != NULL ) {} pclose( lIn ) ; if ( lBuff[strlen( lBuff ) -1] == '\n' ) { lBuff[strlen( lBuff ) -1] = '\0' ; } /* printf( "lBuff: %s\n" , lBuff ) ; */ if ( ! strlen( lBuff ) || ! dirExists( lBuff ) ) { return NULL ; } return lBuff ; } /* returns the hexcolor as a string "#FF0000" */ /* aoResultRGB also contains the result */ /* aDefaultRGB is used only if aDefaultHexRGB is NULL */ /* aDefaultRGB and aoResultRGB can be the same array */ char * tinyfd_colorChooser( char const * aTitle , /* NULL or "" */ char const * aDefaultHexRGB , /* NULL or "#FF0000"*/ unsigned char const aDefaultRGB[3] , /* { 0 , 255 , 255 } */ unsigned char aoResultRGB[3] ) /* { 0 , 0 , 0 } */ { static char lDefaultHexRGB[16]; char lBuff[128] ; char lTmp[128] ; #if !((defined(__cplusplus ) && __cplusplus >= 201103L) || (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(__clang__)) char * lTmp2 ; #endif char lDialogString[MAX_PATH_OR_CMD] ; unsigned char lDefaultRGB[3]; char * p; char * lPointerInputBox; FILE * lIn ; int i ; int lWasZenity3 = 0 ; int lWasOsascript = 0 ; int lWasXdialog = 0 ; lBuff[0]='\0'; if (tfd_quoteDetected(aTitle)) return tinyfd_colorChooser("INVALID TITLE WITH QUOTES", aDefaultHexRGB, aDefaultRGB, aoResultRGB); if (tfd_quoteDetected(aDefaultHexRGB)) return tinyfd_colorChooser(aTitle, "INVALID DEFAULT_HEX_RGB WITH QUOTES", aDefaultRGB, aoResultRGB); if (aDefaultHexRGB) { Hex2RGB(aDefaultHexRGB, lDefaultRGB); strcpy(lDefaultHexRGB, aDefaultHexRGB); } else { lDefaultRGB[0] = aDefaultRGB[0]; lDefaultRGB[1] = aDefaultRGB[1]; lDefaultRGB[2] = aDefaultRGB[2]; RGB2Hex(aDefaultRGB, lDefaultHexRGB); } if ( osascriptPresent( ) ) { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"applescript");return (char *)1;} lWasOsascript = 1 ; strcpy( lDialogString , "osascript"); if ( ! osx9orBetter() ) { strcat( lDialogString , " -e 'tell application \"System Events\"' -e 'Activate'"); strcat( lDialogString , " -e 'try' -e 'set mycolor to choose color default color {"); } else { strcat( lDialogString , " -e 'try' -e 'tell app (path to frontmost application as Unicode text) \ to set mycolor to choose color default color {"); } sprintf(lTmp, "%d", 256 * lDefaultRGB[0] ) ; strcat(lDialogString, lTmp ) ; strcat(lDialogString, "," ) ; sprintf(lTmp, "%d", 256 * lDefaultRGB[1] ) ; strcat(lDialogString, lTmp ) ; strcat(lDialogString, "," ) ; sprintf(lTmp, "%d", 256 * lDefaultRGB[2] ) ; strcat(lDialogString, lTmp ) ; strcat(lDialogString, "}' " ) ; strcat( lDialogString , "-e 'set mystring to ((item 1 of mycolor) div 256 as integer) as string' " ); strcat( lDialogString , "-e 'repeat with i from 2 to the count of mycolor' " ); strcat( lDialogString , "-e 'set mystring to mystring & \" \" & ((item i of mycolor) div 256 as integer) as string' " ); strcat( lDialogString , "-e 'end repeat' " ); strcat( lDialogString , "-e 'mystring' "); strcat(lDialogString, "-e 'on error number -128' " ) ; strcat(lDialogString, "-e 'end try'") ; if ( ! osx9orBetter() ) strcat( lDialogString, " -e 'end tell'") ; } else if ( tfd_kdialogPresent() ) { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"kdialog");return (char *)1;} strcpy( lDialogString , "kdialog" ) ; if ( (tfd_kdialogPresent() == 2) && tfd_xpropPresent() ) { strcat(lDialogString, " --attach=$(xprop -root 32x '\t$0' _NET_ACTIVE_WINDOW | cut -f 2)"); /* contribution: Paul Rouget */ } sprintf( lDialogString + strlen(lDialogString) , " --getcolor --default '%s'" , lDefaultHexRGB ) ; if ( aTitle && strlen(aTitle) ) { strcat(lDialogString, " --title \"") ; strcat(lDialogString, aTitle) ; strcat(lDialogString, "\"") ; } } else if ( tfd_zenity3Present() || tfd_matedialogPresent() || tfd_shellementaryPresent() || tfd_qarmaPresent() ) { lWasZenity3 = 1 ; if ( tfd_zenity3Present() ) { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"zenity3");return (char *)1;} strcpy( lDialogString , "zenity" ); if ( (tfd_zenity3Present() >= 4) && !getenv("SSH_TTY") && tfd_xpropPresent() ) { strcat( lDialogString, " --attach=$(sleep .01;xprop -root 32x '\t$0' _NET_ACTIVE_WINDOW | cut -f 2)"); /* contribution: Paul Rouget */ } } else if ( tfd_matedialogPresent() ) { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"matedialog");return (char *)1;} strcpy( lDialogString , "matedialog" ) ; } else if ( tfd_shellementaryPresent() ) { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"shellementary");return (char *)1;} strcpy( lDialogString , "shellementary" ) ; } else { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"qarma");return (char *)1;} strcpy( lDialogString , "qarma" ) ; if ( !getenv("SSH_TTY") && tfd_xpropPresent() ) { strcat(lDialogString, " --attach=$(xprop -root 32x '\t$0' _NET_ACTIVE_WINDOW | cut -f 2)"); /* contribution: Paul Rouget */ } } strcat( lDialogString , " --color-selection --show-palette" ) ; sprintf( lDialogString + strlen(lDialogString), " --color=%s" , lDefaultHexRGB ) ; if ( aTitle && strlen(aTitle) ) { strcat(lDialogString, " --title=\"") ; strcat(lDialogString, aTitle) ; strcat(lDialogString, "\"") ; } if (tinyfd_silent) strcat( lDialogString , " 2>/dev/null "); } else if (tfd_yadPresent()) { if (aTitle && !strcmp(aTitle, "tinyfd_query")) { strcpy(tinyfd_response, "yad"); return (char*)1; } strcpy(lDialogString, "yad --color"); sprintf(lDialogString + strlen(lDialogString), " --init-color=%s", lDefaultHexRGB); if (aTitle && strlen(aTitle)) { strcat(lDialogString, " --title=\""); strcat(lDialogString, aTitle); strcat(lDialogString, "\""); } if (tinyfd_silent) strcat(lDialogString, " 2>/dev/null "); } else if ( xdialogPresent() ) { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"xdialog");return (char *)1;} lWasXdialog = 1 ; strcpy( lDialogString , "Xdialog --colorsel \"" ) ; if ( aTitle && strlen(aTitle) ) { strcat(lDialogString, aTitle) ; } strcat(lDialogString, "\" 0 60 ") ; #if (defined(__cplusplus ) && __cplusplus >= 201103L) || (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(__clang__) sprintf(lTmp,"%hhu %hhu %hhu",lDefaultRGB[0],lDefaultRGB[1],lDefaultRGB[2]); #else sprintf(lTmp,"%hu %hu %hu",lDefaultRGB[0],lDefaultRGB[1],lDefaultRGB[2]); #endif strcat(lDialogString, lTmp) ; strcat(lDialogString, " 2>&1"); } else if ( tkinter3Present( ) ) { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"python3-tkinter");return (char *)1;} strcpy( lDialogString , gPython3Name ) ; strcat( lDialogString , " -S -c \"import tkinter;from tkinter import colorchooser;root=tkinter.Tk();root.withdraw();"); strcat( lDialogString , "res=colorchooser.askcolor(color='" ) ; strcat(lDialogString, lDefaultHexRGB ) ; strcat(lDialogString, "'") ; if ( aTitle && strlen(aTitle) ) { strcat(lDialogString, ",title='") ; strcat(lDialogString, aTitle) ; strcat(lDialogString, "'") ; } strcat( lDialogString , ");\ \nif res[1] is not None:\n\tprint(res[1])\"" ) ; } else if ( tkinter2Present( ) ) { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){strcpy(tinyfd_response,"python2-tkinter");return (char *)1;} strcpy( lDialogString , "export PYTHONIOENCODING=utf-8;" ) ; strcat( lDialogString , gPython2Name ) ; if ( ! isTerminalRunning( ) && tfd_isDarwin( ) ) { strcat( lDialogString , " -i" ) ; /* for osx without console */ } strcat( lDialogString , " -S -c \"import Tkinter,tkColorChooser;root=Tkinter.Tk();root.withdraw();"); if ( tfd_isDarwin( ) ) { strcat( lDialogString , "import os;os.system('''osascript -e 'tell app \\\"Finder\\\" to set \ frontmost of process \\\"Python\\\" to true' ''');"); } strcat( lDialogString , "res=tkColorChooser.askcolor(color='" ) ; strcat(lDialogString, lDefaultHexRGB ) ; strcat(lDialogString, "'") ; if ( aTitle && strlen(aTitle) ) { strcat(lDialogString, ",title='") ; strcat(lDialogString, aTitle) ; strcat(lDialogString, "'") ; } strcat( lDialogString , ");\ \nif res[1] is not None:\n\tprint res[1]\"" ) ; } else { if (aTitle&&!strcmp(aTitle,"tinyfd_query")){return tinyfd_inputBox(aTitle,NULL,NULL);} lPointerInputBox = tinyfd_inputBox(NULL, NULL, NULL); /* obtain a pointer on the current content of tinyfd_inputBox */ if (lPointerInputBox) strcpy(lDialogString, lPointerInputBox); /* preserve the current content of tinyfd_inputBox */ p = tinyfd_inputBox(aTitle, "Enter hex rgb color (i.e. #f5ca20)", lDefaultHexRGB); if ( !p || (strlen(p) != 7) || (p[0] != '#') ) { return NULL ; } for ( i = 1 ; i < 7 ; i ++ ) { if ( ! isxdigit( (int) p[i] ) ) { return NULL ; } } Hex2RGB(p,aoResultRGB); strcpy(lDefaultHexRGB, p); if (lPointerInputBox) strcpy(lPointerInputBox, lDialogString); /* restore its previous content to tinyfd_inputBox */ return lDefaultHexRGB; } if (tinyfd_verbose) printf( "lDialogString: %s\n" , lDialogString ) ; if ( ! ( lIn = popen( lDialogString , "r" ) ) ) { return NULL ; } while ( fgets( lBuff , sizeof( lBuff ) , lIn ) != NULL ) { } pclose( lIn ) ; if ( ! strlen( lBuff ) ) { return NULL ; } /* printf( "len Buff: %lu\n" , strlen(lBuff) ) ; */ /* printf( "lBuff0: %s\n" , lBuff ) ; */ if ( lBuff[strlen( lBuff ) -1] == '\n' ) { lBuff[strlen( lBuff ) -1] = '\0' ; } if ( lWasZenity3 ) { if ( lBuff[0] == '#' ) { if ( strlen(lBuff)>7 ) { lBuff[3]=lBuff[5]; lBuff[4]=lBuff[6]; lBuff[5]=lBuff[9]; lBuff[6]=lBuff[10]; lBuff[7]='\0'; } Hex2RGB(lBuff,aoResultRGB); } else if ( lBuff[3] == '(' ) { #if (defined(__cplusplus ) && __cplusplus >= 201103L) || (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(__clang__) sscanf(lBuff,"rgb(%hhu,%hhu,%hhu", & aoResultRGB[0], & aoResultRGB[1],& aoResultRGB[2]); #else aoResultRGB[0] = strtol(lBuff+4, & lTmp2, 10 ); aoResultRGB[1] = strtol(lTmp2+1, & lTmp2, 10 ); aoResultRGB[2] = strtol(lTmp2+1, NULL, 10 ); #endif RGB2Hex(aoResultRGB,lBuff); } else if ( lBuff[4] == '(' ) { #if (defined(__cplusplus ) && __cplusplus >= 201103L) || (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(__clang__) sscanf(lBuff,"rgba(%hhu,%hhu,%hhu", & aoResultRGB[0], & aoResultRGB[1],& aoResultRGB[2]); #else aoResultRGB[0] = strtol(lBuff+5, & lTmp2, 10 ); aoResultRGB[1] = strtol(lTmp2+1, & lTmp2, 10 ); aoResultRGB[2] = strtol(lTmp2+1, NULL, 10 ); #endif RGB2Hex(aoResultRGB,lBuff); } } else if ( lWasOsascript || lWasXdialog ) { /* printf( "lBuff: %s\n" , lBuff ) ; */ #if (defined(__cplusplus ) && __cplusplus >= 201103L) || (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(__clang__) sscanf(lBuff,"%hhu %hhu %hhu", & aoResultRGB[0], & aoResultRGB[1],& aoResultRGB[2]); #else aoResultRGB[0] = strtol(lBuff, & lTmp2, 10 ); aoResultRGB[1] = strtol(lTmp2+1, & lTmp2, 10 ); aoResultRGB[2] = strtol(lTmp2+1, NULL, 10 ); #endif RGB2Hex(aoResultRGB,lBuff); } else { Hex2RGB(lBuff,aoResultRGB); } /* printf("%d %d %d\n", aoResultRGB[0],aoResultRGB[1],aoResultRGB[2]); */ /* printf( "lBuff: %s\n" , lBuff ) ; */ strcpy(lDefaultHexRGB,lBuff); return lDefaultHexRGB ; } #endif /* _WIN32 */ /* int main( int argc , char * argv[] ) { char const * lTmp; char const * lTheSaveFileName; char const * lTheOpenFileName; char const * lTheSelectFolderName; char const * lTheHexColor; char const * lWillBeGraphicMode; unsigned char lRgbColor[3]; FILE * lIn; char lBuffer[1024]; char lString[1024]; char const * lFilterPatterns[2] = { "*.txt", "*.text" }; tinyfd_verbose = argc - 1; tinyfd_silent = 1; lWillBeGraphicMode = tinyfd_inputBox("tinyfd_query", NULL, NULL); strcpy(lBuffer, "v"); strcat(lBuffer, tinyfd_version); if (lWillBeGraphicMode) { strcat(lBuffer, "\ngraphic mode: "); } else { strcat(lBuffer, "\nconsole mode: "); } strcat(lBuffer, tinyfd_response); strcat(lBuffer, "\n"); strcat(lBuffer, tinyfd_needs+78); strcpy(lString, "tinyfiledialogs"); tinyfd_messageBox(lString, lBuffer, "ok", "info", 0); tinyfd_notifyPopup("the title", "the message\n\tfrom outer-space", "info"); if (lWillBeGraphicMode && !tinyfd_forceConsole) { tinyfd_forceConsole = ! tinyfd_messageBox("Hello World", "graphic dialogs [yes] / console mode [no]?", "yesno", "question", 1); } lTmp = tinyfd_inputBox( "a password box", "your password will be revealed", NULL); if (!lTmp) return 1; strcpy(lString, lTmp); lTheSaveFileName = tinyfd_saveFileDialog( "let us save this password", "passwordFile.txt", 2, lFilterPatterns, NULL); if (!lTheSaveFileName) { tinyfd_messageBox( "Error", "Save file name is NULL", "ok", "error", 1); return 1; } lIn = fopen(lTheSaveFileName, "w"); if (!lIn) { tinyfd_messageBox( "Error", "Can not open this file in write mode", "ok", "error", 1); return 1; } fputs(lString, lIn); fclose(lIn); lTheOpenFileName = tinyfd_openFileDialog( "let us read the password back", "", 2, lFilterPatterns, NULL, 0); if (!lTheOpenFileName) { tinyfd_messageBox( "Error", "Open file name is NULL", "ok", "error", 1); return 1; } lIn = fopen(lTheOpenFileName, "r"); if (!lIn) { tinyfd_messageBox( "Error", "Can not open this file in read mode", "ok", "error", 1); return(1); } lBuffer[0] = '\0'; fgets(lBuffer, sizeof(lBuffer), lIn); fclose(lIn); tinyfd_messageBox("your password is", lBuffer, "ok", "info", 1); lTheSelectFolderName = tinyfd_selectFolderDialog( "let us just select a directory", NULL); if (!lTheSelectFolderName) { tinyfd_messageBox( "Error", "Select folder name is NULL", "ok", "error", 1); return 1; } tinyfd_messageBox("The selected folder is", lTheSelectFolderName, "ok", "info", 1); lTheHexColor = tinyfd_colorChooser( "choose a nice color", "#FF0077", lRgbColor, lRgbColor); if (!lTheHexColor) { tinyfd_messageBox( "Error", "hexcolor is NULL", "ok", "error", 1); return 1; } tinyfd_messageBox("The selected hexcolor is", lTheHexColor, "ok", "info", 1); tinyfd_beep(); return 0; } */ #ifdef _MSC_VER #pragma warning(default:4996) #pragma warning(default:4100) #pragma warning(default:4706) #endif #endif // TFD_IMPLEMENTATION #line 0 #line 1 "3rd_stb_sprintf.h" // stb_sprintf - v1.10 - public domain snprintf() implementation // originally by Jeff Roberts / RAD Game Tools, 2015/10/20 // http://github.com/nothings/stb // // allowed types: sc uidBboXx p AaGgEef n // lengths : hh h ll j z t I64 I32 I // // Contributors: // Fabian "ryg" Giesen (reformatting) // github:aganm (attribute format) // // Contributors (bugfixes): // github:d26435 // github:trex78 // github:account-login // Jari Komppa (SI suffixes) // Rohit Nirmal // Marcin Wojdyr // Leonard Ritter // Stefano Zanotti // Adam Allison // Arvid Gerstmann // Markus Kolb // // LICENSE: // // See end of file for license information. #ifndef STB_SPRINTF_H_INCLUDE #define STB_SPRINTF_H_INCLUDE /* Single file sprintf replacement. Originally written by Jeff Roberts at RAD Game Tools - 2015/10/20. Hereby placed in public domain. This is a full sprintf replacement that supports everything that the C runtime sprintfs support, including float/double, 64-bit integers, hex floats, field parameters (%*.*d stuff), length reads backs, etc. Why would you need this if sprintf already exists? Well, first off, it's *much* faster (see below). It's also much smaller than the CRT versions code-space-wise. We've also added some simple improvements that are super handy (commas in thousands, callbacks at buffer full, for example). Finally, the format strings for MSVC and GCC differ for 64-bit integers (among other small things), so this lets you use the same format strings in cross platform code. It uses the standard single file trick of being both the header file and the source itself. If you just include it normally, you just get the header file function definitions. To get the code, you include it from a C or C++ file and define STB_SPRINTF_IMPLEMENTATION first. It only uses va_args macros from the C runtime to do it's work. It does cast doubles to S64s and shifts and divides U64s, which does drag in CRT code on most platforms. It compiles to roughly 8K with float support, and 4K without. As a comparison, when using MSVC static libs, calling sprintf drags in 16K. API: ==== int stbsp_sprintf( char * buf, char const * fmt, ... ) int stbsp_snprintf( char * buf, int count, char const * fmt, ... ) Convert an arg list into a buffer. stbsp_snprintf always returns a zero-terminated string (unlike regular snprintf). int stbsp_vsprintf( char * buf, char const * fmt, va_list va ) int stbsp_vsnprintf( char * buf, int count, char const * fmt, va_list va ) Convert a va_list arg list into a buffer. stbsp_vsnprintf always returns a zero-terminated string (unlike regular snprintf). int stbsp_vsprintfcb( STBSP_SPRINTFCB * callback, void * user, char * buf, char const * fmt, va_list va ) typedef char * STBSP_SPRINTFCB( char const * buf, void * user, int len ); Convert into a buffer, calling back every STB_SPRINTF_MIN chars. Your callback can then copy the chars out, print them or whatever. This function is actually the workhorse for everything else. The buffer you pass in must hold at least STB_SPRINTF_MIN characters. // you return the next buffer to use or 0 to stop converting void stbsp_set_separators( char comma, char period ) Set the comma and period characters to use. FLOATS/DOUBLES: =============== This code uses a internal float->ascii conversion method that uses doubles with error correction (double-doubles, for ~105 bits of precision). This conversion is round-trip perfect - that is, an atof of the values output here will give you the bit-exact double back. One difference is that our insignificant digits will be different than with MSVC or GCC (but they don't match each other either). We also don't attempt to find the minimum length matching float (pre-MSVC15 doesn't either). If you don't need float or doubles at all, define STB_SPRINTF_NOFLOAT and you'll save 4K of code space. 64-BIT INTS: ============ This library also supports 64-bit integers and you can use MSVC style or GCC style indicators (%I64d or %lld). It supports the C99 specifiers for size_t and ptr_diff_t (%jd %zd) as well. EXTRAS: ======= Like some GCCs, for integers and floats, you can use a ' (single quote) specifier and commas will be inserted on the thousands: "%'d" on 12345 would print 12,345. For integers and floats, you can use a "$" specifier and the number will be converted to float and then divided to get kilo, mega, giga or tera and then printed, so "%$d" 1000 is "1.0 k", "%$.2d" 2536000 is "2.53 M", etc. For byte values, use two $:s, like "%$$d" to turn 2536000 to "2.42 Mi". If you prefer JEDEC suffixes to SI ones, use three $:s: "%$$$d" -> "2.42 M". To remove the space between the number and the suffix, add "_" specifier: "%_$d" -> "2.53M". In addition to octal and hexadecimal conversions, you can print integers in binary: "%b" for 256 would print 100. PERFORMANCE vs MSVC 2008 32-/64-bit (GCC is even slower than MSVC): =================================================================== "%d" across all 32-bit ints (4.8x/4.0x faster than 32-/64-bit MSVC) "%24d" across all 32-bit ints (4.5x/4.2x faster) "%x" across all 32-bit ints (4.5x/3.8x faster) "%08x" across all 32-bit ints (4.3x/3.8x faster) "%f" across e-10 to e+10 floats (7.3x/6.0x faster) "%e" across e-10 to e+10 floats (8.1x/6.0x faster) "%g" across e-10 to e+10 floats (10.0x/7.1x faster) "%f" for values near e-300 (7.9x/6.5x faster) "%f" for values near e+300 (10.0x/9.1x faster) "%e" for values near e-300 (10.1x/7.0x faster) "%e" for values near e+300 (9.2x/6.0x faster) "%.320f" for values near e-300 (12.6x/11.2x faster) "%a" for random values (8.6x/4.3x faster) "%I64d" for 64-bits with 32-bit values (4.8x/3.4x faster) "%I64d" for 64-bits > 32-bit values (4.9x/5.5x faster) "%s%s%s" for 64 char strings (7.1x/7.3x faster) "...512 char string..." ( 35.0x/32.5x faster!) */ #if defined(__clang__) #if defined(__has_feature) && defined(__has_attribute) #if __has_feature(address_sanitizer) #if __has_attribute(__no_sanitize__) #define STBSP__ASAN __attribute__((__no_sanitize__("address"))) #elif __has_attribute(__no_sanitize_address__) #define STBSP__ASAN __attribute__((__no_sanitize_address__)) #elif __has_attribute(__no_address_safety_analysis__) #define STBSP__ASAN __attribute__((__no_address_safety_analysis__)) #endif #endif #endif #elif defined(__GNUC__) && (__GNUC__ >= 5 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)) #if defined(__SANITIZE_ADDRESS__) && __SANITIZE_ADDRESS__ #define STBSP__ASAN __attribute__((__no_sanitize_address__)) #endif #endif #ifndef STBSP__ASAN #define STBSP__ASAN #endif #ifdef STB_SPRINTF_STATIC #define STBSP__PUBLICDEC static #define STBSP__PUBLICDEF static STBSP__ASAN #else #ifdef __cplusplus #define STBSP__PUBLICDEC extern "C" #define STBSP__PUBLICDEF extern "C" STBSP__ASAN #else #define STBSP__PUBLICDEC extern #define STBSP__PUBLICDEF STBSP__ASAN #endif #endif #if defined(__has_attribute) #if __has_attribute(format) #define STBSP__ATTRIBUTE_FORMAT(fmt,va) __attribute__((format(printf,fmt,va))) #endif #endif #ifndef STBSP__ATTRIBUTE_FORMAT #define STBSP__ATTRIBUTE_FORMAT(fmt,va) #endif #ifdef _MSC_VER #define STBSP__NOTUSED(v) (void)(v) #else #define STBSP__NOTUSED(v) (void)sizeof(v) #endif #include // for va_arg(), va_list() #include // size_t, ptrdiff_t #ifndef STB_SPRINTF_MIN #define STB_SPRINTF_MIN 512 // how many characters per callback #endif typedef char *STBSP_SPRINTFCB(const char *buf, void *user, int len); #ifndef STB_SPRINTF_DECORATE #define STB_SPRINTF_DECORATE(name) stbsp_##name // define this before including if you want to change the names #endif STBSP__PUBLICDEC int STB_SPRINTF_DECORATE(vsprintf)(char *buf, char const *fmt, va_list va); STBSP__PUBLICDEC int STB_SPRINTF_DECORATE(vsnprintf)(char *buf, int count, char const *fmt, va_list va); STBSP__PUBLICDEC int STB_SPRINTF_DECORATE(sprintf)(char *buf, char const *fmt, ...) STBSP__ATTRIBUTE_FORMAT(2,3); STBSP__PUBLICDEC int STB_SPRINTF_DECORATE(snprintf)(char *buf, int count, char const *fmt, ...) STBSP__ATTRIBUTE_FORMAT(3,4); STBSP__PUBLICDEC int STB_SPRINTF_DECORATE(vsprintfcb)(STBSP_SPRINTFCB *callback, void *user, char *buf, char const *fmt, va_list va); STBSP__PUBLICDEC void STB_SPRINTF_DECORATE(set_separators)(char comma, char period); #endif // STB_SPRINTF_H_INCLUDE #ifdef STB_SPRINTF_IMPLEMENTATION #define stbsp__uint32 unsigned int #define stbsp__int32 signed int #ifdef _MSC_VER #define stbsp__uint64 unsigned __int64 #define stbsp__int64 signed __int64 #else #define stbsp__uint64 unsigned long long #define stbsp__int64 signed long long #endif #define stbsp__uint16 unsigned short #ifndef stbsp__uintptr #if defined(__ppc64__) || defined(__powerpc64__) || defined(__aarch64__) || defined(_M_X64) || defined(__x86_64__) || defined(__x86_64) || defined(__s390x__) #define stbsp__uintptr stbsp__uint64 #else #define stbsp__uintptr stbsp__uint32 #endif #endif #ifndef STB_SPRINTF_MSVC_MODE // used for MSVC2013 and earlier (MSVC2015 matches GCC) #if defined(_MSC_VER) && (_MSC_VER < 1900) #define STB_SPRINTF_MSVC_MODE #endif #endif #ifdef STB_SPRINTF_NOUNALIGNED // define this before inclusion to force stbsp_sprintf to always use aligned accesses #define STBSP__UNALIGNED(code) #else #define STBSP__UNALIGNED(code) code #endif #ifndef STB_SPRINTF_NOFLOAT // internal float utility functions static stbsp__int32 stbsp__real_to_str(char const **start, stbsp__uint32 *len, char *out, stbsp__int32 *decimal_pos, double value, stbsp__uint32 frac_digits); static stbsp__int32 stbsp__real_to_parts(stbsp__int64 *bits, stbsp__int32 *expo, double value); #define STBSP__SPECIAL 0x7000 #endif static char stbsp__period = '.'; static char stbsp__comma = ','; static struct { short temp; // force next field to be 2-byte aligned char pair[201]; } stbsp__digitpair = { 0, "00010203040506070809101112131415161718192021222324" "25262728293031323334353637383940414243444546474849" "50515253545556575859606162636465666768697071727374" "75767778798081828384858687888990919293949596979899" }; STBSP__PUBLICDEF void STB_SPRINTF_DECORATE(set_separators)(char pcomma, char pperiod) { stbsp__period = pperiod; stbsp__comma = pcomma; } #define STBSP__LEFTJUST 1 #define STBSP__LEADINGPLUS 2 #define STBSP__LEADINGSPACE 4 #define STBSP__LEADING_0X 8 #define STBSP__LEADINGZERO 16 #define STBSP__INTMAX 32 #define STBSP__TRIPLET_COMMA 64 #define STBSP__NEGATIVE 128 #define STBSP__METRIC_SUFFIX 256 #define STBSP__HALFWIDTH 512 #define STBSP__METRIC_NOSPACE 1024 #define STBSP__METRIC_1024 2048 #define STBSP__METRIC_JEDEC 4096 static void stbsp__lead_sign(stbsp__uint32 fl, char *sign) { sign[0] = 0; if (fl & STBSP__NEGATIVE) { sign[0] = 1; sign[1] = '-'; } else if (fl & STBSP__LEADINGSPACE) { sign[0] = 1; sign[1] = ' '; } else if (fl & STBSP__LEADINGPLUS) { sign[0] = 1; sign[1] = '+'; } } static STBSP__ASAN stbsp__uint32 stbsp__strlen_limited(char const *s, stbsp__uint32 limit) { char const * sn = s; // get up to 4-byte alignment for (;;) { if (((stbsp__uintptr)sn & 3) == 0) break; if (!limit || *sn == 0) return (stbsp__uint32)(sn - s); ++sn; --limit; } // scan over 4 bytes at a time to find terminating 0 // this will intentionally scan up to 3 bytes past the end of buffers, // but becase it works 4B aligned, it will never cross page boundaries // (hence the STBSP__ASAN markup; the over-read here is intentional // and harmless) while (limit >= 4) { stbsp__uint32 v = *(stbsp__uint32 *)sn; // bit hack to find if there's a 0 byte in there if ((v - 0x01010101) & (~v) & 0x80808080UL) break; sn += 4; limit -= 4; } // handle the last few characters to find actual size while (limit && *sn) { ++sn; --limit; } return (stbsp__uint32)(sn - s); } STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(vsprintfcb)(STBSP_SPRINTFCB *callback, void *user, char *buf, char const *fmt, va_list va) { static char hex[] = "0123456789abcdefxp"; static char hexu[] = "0123456789ABCDEFXP"; char *bf; char const *f; int tlen = 0; bf = buf; f = fmt; for (;;) { stbsp__int32 fw, pr, tz; stbsp__uint32 fl; // macros for the callback buffer stuff #define stbsp__chk_cb_bufL(bytes) \ { \ int len = (int)(bf - buf); \ if ((len + (bytes)) >= STB_SPRINTF_MIN) { \ tlen += len; \ if (0 == (bf = buf = callback(buf, user, len))) \ goto done; \ } \ } #define stbsp__chk_cb_buf(bytes) \ { \ if (callback) { \ stbsp__chk_cb_bufL(bytes); \ } \ } #define stbsp__flush_cb() \ { \ stbsp__chk_cb_bufL(STB_SPRINTF_MIN - 1); \ } // flush if there is even one byte in the buffer #define stbsp__cb_buf_clamp(cl, v) \ cl = v; \ if (callback) { \ int lg = STB_SPRINTF_MIN - (int)(bf - buf); \ if (cl > lg) \ cl = lg; \ } // fast copy everything up to the next % (or end of string) for (;;) { while (((stbsp__uintptr)f) & 3) { schk1: if (f[0] == '%') goto scandd; schk2: if (f[0] == 0) goto endfmt; stbsp__chk_cb_buf(1); *bf++ = f[0]; ++f; } for (;;) { // Check if the next 4 bytes contain %(0x25) or end of string. // Using the 'hasless' trick: // https://graphics.stanford.edu/~seander/bithacks.html#HasLessInWord stbsp__uint32 v, c; v = *(stbsp__uint32 *)f; c = (~v) & 0x80808080; if (((v ^ 0x25252525) - 0x01010101) & c) goto schk1; if ((v - 0x01010101) & c) goto schk2; if (callback) if ((STB_SPRINTF_MIN - (int)(bf - buf)) < 4) goto schk1; #ifdef STB_SPRINTF_NOUNALIGNED if(((stbsp__uintptr)bf) & 3) { bf[0] = f[0]; bf[1] = f[1]; bf[2] = f[2]; bf[3] = f[3]; } else #endif { *(stbsp__uint32 *)bf = v; } bf += 4; f += 4; } } scandd: ++f; // ok, we have a percent, read the modifiers first fw = 0; pr = -1; fl = 0; tz = 0; // flags for (;;) { switch (f[0]) { // if we have left justify case '-': fl |= STBSP__LEFTJUST; ++f; continue; // if we have leading plus case '+': fl |= STBSP__LEADINGPLUS; ++f; continue; // if we have leading space case ' ': fl |= STBSP__LEADINGSPACE; ++f; continue; // if we have leading 0x case '#': fl |= STBSP__LEADING_0X; ++f; continue; // if we have thousand commas case '\'': fl |= STBSP__TRIPLET_COMMA; ++f; continue; // if we have kilo marker (none->kilo->kibi->jedec) case '$': if (fl & STBSP__METRIC_SUFFIX) { if (fl & STBSP__METRIC_1024) { fl |= STBSP__METRIC_JEDEC; } else { fl |= STBSP__METRIC_1024; } } else { fl |= STBSP__METRIC_SUFFIX; } ++f; continue; // if we don't want space between metric suffix and number case '_': fl |= STBSP__METRIC_NOSPACE; ++f; continue; // if we have leading zero case '0': fl |= STBSP__LEADINGZERO; ++f; goto flags_done; default: goto flags_done; } } flags_done: // get the field width if (f[0] == '*') { fw = va_arg(va, stbsp__uint32); ++f; } else { while ((f[0] >= '0') && (f[0] <= '9')) { fw = fw * 10 + f[0] - '0'; f++; } } // get the precision if (f[0] == '.') { ++f; if (f[0] == '*') { pr = va_arg(va, stbsp__uint32); ++f; } else { pr = 0; while ((f[0] >= '0') && (f[0] <= '9')) { pr = pr * 10 + f[0] - '0'; f++; } } } // handle integer size overrides switch (f[0]) { // are we halfwidth? case 'h': fl |= STBSP__HALFWIDTH; ++f; if (f[0] == 'h') ++f; // QUARTERWIDTH break; // are we 64-bit (unix style) case 'l': fl |= ((sizeof(long) == 8) ? STBSP__INTMAX : 0); ++f; if (f[0] == 'l') { fl |= STBSP__INTMAX; ++f; } break; // are we 64-bit on intmax? (c99) case 'j': fl |= (sizeof(size_t) == 8) ? STBSP__INTMAX : 0; ++f; break; // are we 64-bit on size_t or ptrdiff_t? (c99) case 'z': fl |= (sizeof(ptrdiff_t) == 8) ? STBSP__INTMAX : 0; ++f; break; case 't': fl |= (sizeof(ptrdiff_t) == 8) ? STBSP__INTMAX : 0; ++f; break; // are we 64-bit (msft style) case 'I': if ((f[1] == '6') && (f[2] == '4')) { fl |= STBSP__INTMAX; f += 3; } else if ((f[1] == '3') && (f[2] == '2')) { f += 3; } else { fl |= ((sizeof(void *) == 8) ? STBSP__INTMAX : 0); ++f; } break; default: break; } // handle each replacement switch (f[0]) { #define STBSP__NUMSZ 512 // big enough for e308 (with commas) or e-307 char num[STBSP__NUMSZ]; char lead[8]; char tail[8]; char *s; char const *h; stbsp__uint32 l, n, cs; stbsp__uint64 n64; #ifndef STB_SPRINTF_NOFLOAT double fv; #endif stbsp__int32 dp; char const *sn; case 's': // get the string s = va_arg(va, char *); if (s == 0) s = (char *)"null"; // get the length, limited to desired precision // always limit to ~0u chars since our counts are 32b l = stbsp__strlen_limited(s, (pr >= 0) ? pr : ~0u); lead[0] = 0; tail[0] = 0; pr = 0; dp = 0; cs = 0; // copy the string in goto scopy; case 'c': // char // get the character s = num + STBSP__NUMSZ - 1; *s = (char)va_arg(va, int); l = 1; lead[0] = 0; tail[0] = 0; pr = 0; dp = 0; cs = 0; goto scopy; case 'n': // weird write-bytes specifier { int *d = va_arg(va, int *); *d = tlen + (int)(bf - buf); } break; #ifdef STB_SPRINTF_NOFLOAT case 'A': // float case 'a': // hex float case 'G': // float case 'g': // float case 'E': // float case 'e': // float case 'f': // float va_arg(va, double); // eat it s = (char *)"No float"; l = 8; lead[0] = 0; tail[0] = 0; pr = 0; cs = 0; STBSP__NOTUSED(dp); goto scopy; #else case 'A': // hex float case 'a': // hex float h = (f[0] == 'A') ? hexu : hex; fv = va_arg(va, double); if (pr == -1) pr = 6; // default is 6 // read the double into a string if (stbsp__real_to_parts((stbsp__int64 *)&n64, &dp, fv)) fl |= STBSP__NEGATIVE; s = num + 64; stbsp__lead_sign(fl, lead); if (dp == -1023) dp = (n64) ? -1022 : 0; else n64 |= (((stbsp__uint64)1) << 52); n64 <<= (64 - 56); if (pr < 15) n64 += ((((stbsp__uint64)8) << 56) >> (pr * 4)); // add leading chars #ifdef STB_SPRINTF_MSVC_MODE *s++ = '0'; *s++ = 'x'; #else lead[1 + lead[0]] = '0'; lead[2 + lead[0]] = 'x'; lead[0] += 2; #endif *s++ = h[(n64 >> 60) & 15]; n64 <<= 4; if (pr) *s++ = stbsp__period; sn = s; // print the bits n = pr; if (n > 13) n = 13; if (pr > (stbsp__int32)n) tz = pr - n; pr = 0; while (n--) { *s++ = h[(n64 >> 60) & 15]; n64 <<= 4; } // print the expo tail[1] = h[17]; if (dp < 0) { tail[2] = '-'; dp = -dp; } else tail[2] = '+'; n = (dp >= 1000) ? 6 : ((dp >= 100) ? 5 : ((dp >= 10) ? 4 : 3)); tail[0] = (char)n; for (;;) { tail[n] = '0' + dp % 10; if (n <= 3) break; --n; dp /= 10; } dp = (int)(s - sn); l = (int)(s - (num + 64)); s = num + 64; cs = 1 + (3 << 24); goto scopy; case 'G': // float case 'g': // float h = (f[0] == 'G') ? hexu : hex; fv = va_arg(va, double); if (pr == -1) pr = 6; else if (pr == 0) pr = 1; // default is 6 // read the double into a string if (stbsp__real_to_str(&sn, &l, num, &dp, fv, (pr - 1) | 0x80000000)) fl |= STBSP__NEGATIVE; // clamp the precision and delete extra zeros after clamp n = pr; if (l > (stbsp__uint32)pr) l = pr; while ((l > 1) && (pr) && (sn[l - 1] == '0')) { --pr; --l; } // should we use %e if ((dp <= -4) || (dp > (stbsp__int32)n)) { if (pr > (stbsp__int32)l) pr = l - 1; else if (pr) --pr; // when using %e, there is one digit before the decimal goto doexpfromg; } // this is the insane action to get the pr to match %g semantics for %f if (dp > 0) { pr = (dp < (stbsp__int32)l) ? l - dp : 0; } else { pr = -dp + ((pr > (stbsp__int32)l) ? (stbsp__int32) l : pr); } goto dofloatfromg; case 'E': // float case 'e': // float h = (f[0] == 'E') ? hexu : hex; fv = va_arg(va, double); if (pr == -1) pr = 6; // default is 6 // read the double into a string if (stbsp__real_to_str(&sn, &l, num, &dp, fv, pr | 0x80000000)) fl |= STBSP__NEGATIVE; doexpfromg: tail[0] = 0; stbsp__lead_sign(fl, lead); if (dp == STBSP__SPECIAL) { s = (char *)sn; cs = 0; pr = 0; goto scopy; } s = num + 64; // handle leading chars *s++ = sn[0]; if (pr) *s++ = stbsp__period; // handle after decimal if ((l - 1) > (stbsp__uint32)pr) l = pr + 1; for (n = 1; n < l; n++) *s++ = sn[n]; // trailing zeros tz = pr - (l - 1); pr = 0; // dump expo tail[1] = h[0xe]; dp -= 1; if (dp < 0) { tail[2] = '-'; dp = -dp; } else tail[2] = '+'; #ifdef STB_SPRINTF_MSVC_MODE n = 5; #else n = (dp >= 100) ? 5 : 4; #endif tail[0] = (char)n; for (;;) { tail[n] = '0' + dp % 10; if (n <= 3) break; --n; dp /= 10; } cs = 1 + (3 << 24); // how many tens goto flt_lead; case 'f': // float fv = va_arg(va, double); doafloat: // do kilos if (fl & STBSP__METRIC_SUFFIX) { double divisor; divisor = 1000.0f; if (fl & STBSP__METRIC_1024) divisor = 1024.0; while (fl < 0x4000000) { if ((fv < divisor) && (fv > -divisor)) break; fv /= divisor; fl += 0x1000000; } } if (pr == -1) pr = 6; // default is 6 // read the double into a string if (stbsp__real_to_str(&sn, &l, num, &dp, fv, pr)) fl |= STBSP__NEGATIVE; dofloatfromg: tail[0] = 0; stbsp__lead_sign(fl, lead); if (dp == STBSP__SPECIAL) { s = (char *)sn; cs = 0; pr = 0; goto scopy; } s = num + 64; // handle the three decimal varieties if (dp <= 0) { stbsp__int32 i; // handle 0.000*000xxxx *s++ = '0'; if (pr) *s++ = stbsp__period; n = -dp; if ((stbsp__int32)n > pr) n = pr; i = n; while (i) { if ((((stbsp__uintptr)s) & 3) == 0) break; *s++ = '0'; --i; } while (i >= 4) { *(stbsp__uint32 *)s = 0x30303030; s += 4; i -= 4; } while (i) { *s++ = '0'; --i; } if ((stbsp__int32)(l + n) > pr) l = pr - n; i = l; while (i) { *s++ = *sn++; --i; } tz = pr - (n + l); cs = 1 + (3 << 24); // how many tens did we write (for commas below) } else { cs = (fl & STBSP__TRIPLET_COMMA) ? ((600 - (stbsp__uint32)dp) % 3) : 0; if ((stbsp__uint32)dp >= l) { // handle xxxx000*000.0 n = 0; for (;;) { if ((fl & STBSP__TRIPLET_COMMA) && (++cs == 4)) { cs = 0; *s++ = stbsp__comma; } else { *s++ = sn[n]; ++n; if (n >= l) break; } } if (n < (stbsp__uint32)dp) { n = dp - n; if ((fl & STBSP__TRIPLET_COMMA) == 0) { while (n) { if ((((stbsp__uintptr)s) & 3) == 0) break; *s++ = '0'; --n; } while (n >= 4) { *(stbsp__uint32 *)s = 0x30303030; s += 4; n -= 4; } } while (n) { if ((fl & STBSP__TRIPLET_COMMA) && (++cs == 4)) { cs = 0; *s++ = stbsp__comma; } else { *s++ = '0'; --n; } } } cs = (int)(s - (num + 64)) + (3 << 24); // cs is how many tens if (pr) { *s++ = stbsp__period; tz = pr; } } else { // handle xxxxx.xxxx000*000 n = 0; for (;;) { if ((fl & STBSP__TRIPLET_COMMA) && (++cs == 4)) { cs = 0; *s++ = stbsp__comma; } else { *s++ = sn[n]; ++n; if (n >= (stbsp__uint32)dp) break; } } cs = (int)(s - (num + 64)) + (3 << 24); // cs is how many tens if (pr) *s++ = stbsp__period; if ((l - dp) > (stbsp__uint32)pr) l = pr + dp; while (n < l) { *s++ = sn[n]; ++n; } tz = pr - (l - dp); } } pr = 0; // handle k,m,g,t if (fl & STBSP__METRIC_SUFFIX) { char idx; idx = 1; if (fl & STBSP__METRIC_NOSPACE) idx = 0; tail[0] = idx; tail[1] = ' '; { if (fl >> 24) { // SI kilo is 'k', JEDEC and SI kibits are 'K'. if (fl & STBSP__METRIC_1024) tail[idx + 1] = "_KMGT"[fl >> 24]; else tail[idx + 1] = "_kMGT"[fl >> 24]; idx++; // If printing kibits and not in jedec, add the 'i'. if (fl & STBSP__METRIC_1024 && !(fl & STBSP__METRIC_JEDEC)) { tail[idx + 1] = 'i'; idx++; } tail[0] = idx; } } }; flt_lead: // get the length that we copied l = (stbsp__uint32)(s - (num + 64)); s = num + 64; goto scopy; #endif case 'B': // upper binary case 'b': // lower binary h = (f[0] == 'B') ? hexu : hex; lead[0] = 0; if (fl & STBSP__LEADING_0X) { lead[0] = 2; lead[1] = '0'; lead[2] = h[0xb]; } l = (8 << 4) | (1 << 8); goto radixnum; case 'o': // octal h = hexu; lead[0] = 0; if (fl & STBSP__LEADING_0X) { lead[0] = 1; lead[1] = '0'; } l = (3 << 4) | (3 << 8); goto radixnum; case 'p': // pointer fl |= (sizeof(void *) == 8) ? STBSP__INTMAX : 0; pr = sizeof(void *) * 2; fl &= ~STBSP__LEADINGZERO; // 'p' only prints the pointer with zeros // fall through - to X case 'X': // upper hex case 'x': // lower hex h = (f[0] == 'X') ? hexu : hex; l = (4 << 4) | (4 << 8); lead[0] = 0; if (fl & STBSP__LEADING_0X) { lead[0] = 2; lead[1] = '0'; lead[2] = h[16]; } radixnum: // get the number if (fl & STBSP__INTMAX) n64 = va_arg(va, stbsp__uint64); else n64 = va_arg(va, stbsp__uint32); s = num + STBSP__NUMSZ; dp = 0; // clear tail, and clear leading if value is zero tail[0] = 0; if (n64 == 0) { lead[0] = 0; if (pr == 0) { l = 0; cs = 0; goto scopy; } } // convert to string for (;;) { *--s = h[n64 & ((1 << (l >> 8)) - 1)]; n64 >>= (l >> 8); if (!((n64) || ((stbsp__int32)((num + STBSP__NUMSZ) - s) < pr))) break; if (fl & STBSP__TRIPLET_COMMA) { ++l; if ((l & 15) == ((l >> 4) & 15)) { l &= ~15; *--s = stbsp__comma; } } }; // get the tens and the comma pos cs = (stbsp__uint32)((num + STBSP__NUMSZ) - s) + ((((l >> 4) & 15)) << 24); // get the length that we copied l = (stbsp__uint32)((num + STBSP__NUMSZ) - s); // copy it goto scopy; case 'u': // unsigned case 'i': case 'd': // integer // get the integer and abs it if (fl & STBSP__INTMAX) { stbsp__int64 i64 = va_arg(va, stbsp__int64); n64 = (stbsp__uint64)i64; if ((f[0] != 'u') && (i64 < 0)) { n64 = (stbsp__uint64)-i64; fl |= STBSP__NEGATIVE; } } else { stbsp__int32 i = va_arg(va, stbsp__int32); n64 = (stbsp__uint32)i; if ((f[0] != 'u') && (i < 0)) { n64 = (stbsp__uint32)-i; fl |= STBSP__NEGATIVE; } } #ifndef STB_SPRINTF_NOFLOAT if (fl & STBSP__METRIC_SUFFIX) { if (n64 < 1024) pr = 0; else if (pr == -1) pr = 1; fv = (double)(stbsp__int64)n64; goto doafloat; } #endif // convert to string s = num + STBSP__NUMSZ; l = 0; for (;;) { // do in 32-bit chunks (avoid lots of 64-bit divides even with constant denominators) char *o = s - 8; if (n64 >= 100000000) { n = (stbsp__uint32)(n64 % 100000000); n64 /= 100000000; } else { n = (stbsp__uint32)n64; n64 = 0; } if ((fl & STBSP__TRIPLET_COMMA) == 0) { do { s -= 2; *(stbsp__uint16 *)s = *(stbsp__uint16 *)&stbsp__digitpair.pair[(n % 100) * 2]; n /= 100; } while (n); } while (n) { if ((fl & STBSP__TRIPLET_COMMA) && (l++ == 3)) { l = 0; *--s = stbsp__comma; --o; } else { *--s = (char)(n % 10) + '0'; n /= 10; } } if (n64 == 0) { if ((s[0] == '0') && (s != (num + STBSP__NUMSZ))) ++s; break; } while (s != o) if ((fl & STBSP__TRIPLET_COMMA) && (l++ == 3)) { l = 0; *--s = stbsp__comma; --o; } else { *--s = '0'; } } tail[0] = 0; stbsp__lead_sign(fl, lead); // get the length that we copied l = (stbsp__uint32)((num + STBSP__NUMSZ) - s); if (l == 0) { *--s = '0'; l = 1; } cs = l + (3 << 24); if (pr < 0) pr = 0; scopy: // get fw=leading/trailing space, pr=leading zeros if (pr < (stbsp__int32)l) pr = l; n = pr + lead[0] + tail[0] + tz; if (fw < (stbsp__int32)n) fw = n; fw -= n; pr -= l; // handle right justify and leading zeros if ((fl & STBSP__LEFTJUST) == 0) { if (fl & STBSP__LEADINGZERO) // if leading zeros, everything is in pr { pr = (fw > pr) ? fw : pr; fw = 0; } else { fl &= ~STBSP__TRIPLET_COMMA; // if no leading zeros, then no commas } } // copy the spaces and/or zeros if (fw + pr) { stbsp__int32 i; stbsp__uint32 c; // copy leading spaces (or when doing %8.4d stuff) if ((fl & STBSP__LEFTJUST) == 0) while (fw > 0) { stbsp__cb_buf_clamp(i, fw); fw -= i; while (i) { if ((((stbsp__uintptr)bf) & 3) == 0) break; *bf++ = ' '; --i; } while (i >= 4) { *(stbsp__uint32 *)bf = 0x20202020; bf += 4; i -= 4; } while (i) { *bf++ = ' '; --i; } stbsp__chk_cb_buf(1); } // copy leader sn = lead + 1; while (lead[0]) { stbsp__cb_buf_clamp(i, lead[0]); lead[0] -= (char)i; while (i) { *bf++ = *sn++; --i; } stbsp__chk_cb_buf(1); } // copy leading zeros c = cs >> 24; cs &= 0xffffff; cs = (fl & STBSP__TRIPLET_COMMA) ? ((stbsp__uint32)(c - ((pr + cs) % (c + 1)))) : 0; while (pr > 0) { stbsp__cb_buf_clamp(i, pr); pr -= i; if ((fl & STBSP__TRIPLET_COMMA) == 0) { while (i) { if ((((stbsp__uintptr)bf) & 3) == 0) break; *bf++ = '0'; --i; } while (i >= 4) { *(stbsp__uint32 *)bf = 0x30303030; bf += 4; i -= 4; } } while (i) { if ((fl & STBSP__TRIPLET_COMMA) && (cs++ == c)) { cs = 0; *bf++ = stbsp__comma; } else *bf++ = '0'; --i; } stbsp__chk_cb_buf(1); } } // copy leader if there is still one sn = lead + 1; while (lead[0]) { stbsp__int32 i; stbsp__cb_buf_clamp(i, lead[0]); lead[0] -= (char)i; while (i) { *bf++ = *sn++; --i; } stbsp__chk_cb_buf(1); } // copy the string n = l; while (n) { stbsp__int32 i; stbsp__cb_buf_clamp(i, n); n -= i; STBSP__UNALIGNED(while (i >= 4) { *(stbsp__uint32 volatile *)bf = *(stbsp__uint32 volatile *)s; bf += 4; s += 4; i -= 4; }) while (i) { *bf++ = *s++; --i; } stbsp__chk_cb_buf(1); } // copy trailing zeros while (tz) { stbsp__int32 i; stbsp__cb_buf_clamp(i, tz); tz -= i; while (i) { if ((((stbsp__uintptr)bf) & 3) == 0) break; *bf++ = '0'; --i; } while (i >= 4) { *(stbsp__uint32 *)bf = 0x30303030; bf += 4; i -= 4; } while (i) { *bf++ = '0'; --i; } stbsp__chk_cb_buf(1); } // copy tail if there is one sn = tail + 1; while (tail[0]) { stbsp__int32 i; stbsp__cb_buf_clamp(i, tail[0]); tail[0] -= (char)i; while (i) { *bf++ = *sn++; --i; } stbsp__chk_cb_buf(1); } // handle the left justify if (fl & STBSP__LEFTJUST) if (fw > 0) { while (fw) { stbsp__int32 i; stbsp__cb_buf_clamp(i, fw); fw -= i; while (i) { if ((((stbsp__uintptr)bf) & 3) == 0) break; *bf++ = ' '; --i; } while (i >= 4) { *(stbsp__uint32 *)bf = 0x20202020; bf += 4; i -= 4; } while (i--) *bf++ = ' '; stbsp__chk_cb_buf(1); } } break; default: // unknown, just copy code s = num + STBSP__NUMSZ - 1; *s = f[0]; l = 1; fw = fl = 0; lead[0] = 0; tail[0] = 0; pr = 0; dp = 0; cs = 0; goto scopy; } ++f; } endfmt: if (!callback) *bf = 0; else stbsp__flush_cb(); done: return tlen + (int)(bf - buf); } // cleanup #undef STBSP__LEFTJUST #undef STBSP__LEADINGPLUS #undef STBSP__LEADINGSPACE #undef STBSP__LEADING_0X #undef STBSP__LEADINGZERO #undef STBSP__INTMAX #undef STBSP__TRIPLET_COMMA #undef STBSP__NEGATIVE #undef STBSP__METRIC_SUFFIX #undef STBSP__NUMSZ #undef stbsp__chk_cb_bufL #undef stbsp__chk_cb_buf #undef stbsp__flush_cb #undef stbsp__cb_buf_clamp // ============================================================================ // wrapper functions STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(sprintf)(char *buf, char const *fmt, ...) { int result; va_list va; va_start(va, fmt); result = STB_SPRINTF_DECORATE(vsprintfcb)(0, 0, buf, fmt, va); va_end(va); return result; } typedef struct stbsp__context { char *buf; int count; int length; char tmp[STB_SPRINTF_MIN]; } stbsp__context; static char *stbsp__clamp_callback(const char *buf, void *user, int len) { stbsp__context *c = (stbsp__context *)user; c->length += len; if (len > c->count) len = c->count; if (len) { if (buf != c->buf) { const char *s, *se; char *d; d = c->buf; s = buf; se = buf + len; do { *d++ = *s++; } while (s < se); } c->buf += len; c->count -= len; } if (c->count <= 0) return c->tmp; return (c->count >= STB_SPRINTF_MIN) ? c->buf : c->tmp; // go direct into buffer if you can } static char * stbsp__count_clamp_callback( const char * buf, void * user, int len ) { stbsp__context * c = (stbsp__context*)user; (void) sizeof(buf); c->length += len; return c->tmp; // go direct into buffer if you can } STBSP__PUBLICDEF int STB_SPRINTF_DECORATE( vsnprintf )( char * buf, int count, char const * fmt, va_list va ) { stbsp__context c; if ( (count == 0) && !buf ) { c.length = 0; STB_SPRINTF_DECORATE( vsprintfcb )( stbsp__count_clamp_callback, &c, c.tmp, fmt, va ); } else { int l; c.buf = buf; c.count = count; c.length = 0; STB_SPRINTF_DECORATE( vsprintfcb )( stbsp__clamp_callback, &c, stbsp__clamp_callback(0,&c,0), fmt, va ); // zero-terminate l = (int)( c.buf - buf ); if ( l >= count ) // should never be greater, only equal (or less) than count l = count - 1; buf[l] = 0; } return c.length; } STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(snprintf)(char *buf, int count, char const *fmt, ...) { int result; va_list va; va_start(va, fmt); result = STB_SPRINTF_DECORATE(vsnprintf)(buf, count, fmt, va); va_end(va); return result; } STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(vsprintf)(char *buf, char const *fmt, va_list va) { return STB_SPRINTF_DECORATE(vsprintfcb)(0, 0, buf, fmt, va); } // ======================================================================= // low level float utility functions #ifndef STB_SPRINTF_NOFLOAT // copies d to bits w/ strict aliasing (this compiles to nothing on /Ox) #define STBSP__COPYFP(dest, src) \ { \ int cn; \ for (cn = 0; cn < 8; cn++) \ ((char *)&dest)[cn] = ((char *)&src)[cn]; \ } // get float info static stbsp__int32 stbsp__real_to_parts(stbsp__int64 *bits, stbsp__int32 *expo, double value) { double d; stbsp__int64 b = 0; // load value and round at the frac_digits d = value; STBSP__COPYFP(b, d); *bits = b & ((((stbsp__uint64)1) << 52) - 1); *expo = (stbsp__int32)(((b >> 52) & 2047) - 1023); return (stbsp__int32)((stbsp__uint64) b >> 63); } static double const stbsp__bot[23] = { 1e+000, 1e+001, 1e+002, 1e+003, 1e+004, 1e+005, 1e+006, 1e+007, 1e+008, 1e+009, 1e+010, 1e+011, 1e+012, 1e+013, 1e+014, 1e+015, 1e+016, 1e+017, 1e+018, 1e+019, 1e+020, 1e+021, 1e+022 }; static double const stbsp__negbot[22] = { 1e-001, 1e-002, 1e-003, 1e-004, 1e-005, 1e-006, 1e-007, 1e-008, 1e-009, 1e-010, 1e-011, 1e-012, 1e-013, 1e-014, 1e-015, 1e-016, 1e-017, 1e-018, 1e-019, 1e-020, 1e-021, 1e-022 }; static double const stbsp__negboterr[22] = { -5.551115123125783e-018, -2.0816681711721684e-019, -2.0816681711721686e-020, -4.7921736023859299e-021, -8.1803053914031305e-022, 4.5251888174113741e-023, 4.5251888174113739e-024, -2.0922560830128471e-025, -6.2281591457779853e-026, -3.6432197315497743e-027, 6.0503030718060191e-028, 2.0113352370744385e-029, -3.0373745563400371e-030, 1.1806906454401013e-032, -7.7705399876661076e-032, 2.0902213275965398e-033, -7.1542424054621921e-034, -7.1542424054621926e-035, 2.4754073164739869e-036, 5.4846728545790429e-037, 9.2462547772103625e-038, -4.8596774326570872e-039 }; static double const stbsp__top[13] = { 1e+023, 1e+046, 1e+069, 1e+092, 1e+115, 1e+138, 1e+161, 1e+184, 1e+207, 1e+230, 1e+253, 1e+276, 1e+299 }; static double const stbsp__negtop[13] = { 1e-023, 1e-046, 1e-069, 1e-092, 1e-115, 1e-138, 1e-161, 1e-184, 1e-207, 1e-230, 1e-253, 1e-276, 1e-299 }; static double const stbsp__toperr[13] = { 8388608, 6.8601809640529717e+028, -7.253143638152921e+052, -4.3377296974619174e+075, -1.5559416129466825e+098, -3.2841562489204913e+121, -3.7745893248228135e+144, -1.7356668416969134e+167, -3.8893577551088374e+190, -9.9566444326005119e+213, 6.3641293062232429e+236, -5.2069140800249813e+259, -5.2504760255204387e+282 }; static double const stbsp__negtoperr[13] = { 3.9565301985100693e-040, -2.299904345391321e-063, 3.6506201437945798e-086, 1.1875228833981544e-109, -5.0644902316928607e-132, -6.7156837247865426e-155, -2.812077463003139e-178, -5.7778912386589953e-201, 7.4997100559334532e-224, -4.6439668915134491e-247, -6.3691100762962136e-270, -9.436808465446358e-293, 8.0970921678014997e-317 }; #if defined(_MSC_VER) && (_MSC_VER <= 1200) static stbsp__uint64 const stbsp__powten[20] = { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000, 10000000000, 100000000000, 1000000000000, 10000000000000, 100000000000000, 1000000000000000, 10000000000000000, 100000000000000000, 1000000000000000000, 10000000000000000000U }; #define stbsp__tento19th ((stbsp__uint64)1000000000000000000) #else static stbsp__uint64 const stbsp__powten[20] = { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000, 10000000000ULL, 100000000000ULL, 1000000000000ULL, 10000000000000ULL, 100000000000000ULL, 1000000000000000ULL, 10000000000000000ULL, 100000000000000000ULL, 1000000000000000000ULL, 10000000000000000000ULL }; #define stbsp__tento19th (1000000000000000000ULL) #endif #define stbsp__ddmulthi(oh, ol, xh, yh) \ { \ double ahi = 0, alo, bhi = 0, blo; \ stbsp__int64 bt; \ oh = xh * yh; \ STBSP__COPYFP(bt, xh); \ bt &= ((~(stbsp__uint64)0) << 27); \ STBSP__COPYFP(ahi, bt); \ alo = xh - ahi; \ STBSP__COPYFP(bt, yh); \ bt &= ((~(stbsp__uint64)0) << 27); \ STBSP__COPYFP(bhi, bt); \ blo = yh - bhi; \ ol = ((ahi * bhi - oh) + ahi * blo + alo * bhi) + alo * blo; \ } #define stbsp__ddtoS64(ob, xh, xl) \ { \ double ahi = 0, alo, vh, t; \ ob = (stbsp__int64)xh; \ vh = (double)ob; \ ahi = (xh - vh); \ t = (ahi - xh); \ alo = (xh - (ahi - t)) - (vh + t); \ ob += (stbsp__int64)(ahi + alo + xl); \ } #define stbsp__ddrenorm(oh, ol) \ { \ double s; \ s = oh + ol; \ ol = ol - (s - oh); \ oh = s; \ } #define stbsp__ddmultlo(oh, ol, xh, xl, yh, yl) ol = ol + (xh * yl + xl * yh); #define stbsp__ddmultlos(oh, ol, xh, yl) ol = ol + (xh * yl); static void stbsp__raise_to_power10(double *ohi, double *olo, double d, stbsp__int32 power) // power can be -323 to +350 { double ph, pl; if ((power >= 0) && (power <= 22)) { stbsp__ddmulthi(ph, pl, d, stbsp__bot[power]); } else { stbsp__int32 e, et, eb; double p2h, p2l; e = power; if (power < 0) e = -e; et = (e * 0x2c9) >> 14; /* %23 */ if (et > 13) et = 13; eb = e - (et * 23); ph = d; pl = 0.0; if (power < 0) { if (eb) { --eb; stbsp__ddmulthi(ph, pl, d, stbsp__negbot[eb]); stbsp__ddmultlos(ph, pl, d, stbsp__negboterr[eb]); } if (et) { stbsp__ddrenorm(ph, pl); --et; stbsp__ddmulthi(p2h, p2l, ph, stbsp__negtop[et]); stbsp__ddmultlo(p2h, p2l, ph, pl, stbsp__negtop[et], stbsp__negtoperr[et]); ph = p2h; pl = p2l; } } else { if (eb) { e = eb; if (eb > 22) eb = 22; e -= eb; stbsp__ddmulthi(ph, pl, d, stbsp__bot[eb]); if (e) { stbsp__ddrenorm(ph, pl); stbsp__ddmulthi(p2h, p2l, ph, stbsp__bot[e]); stbsp__ddmultlos(p2h, p2l, stbsp__bot[e], pl); ph = p2h; pl = p2l; } } if (et) { stbsp__ddrenorm(ph, pl); --et; stbsp__ddmulthi(p2h, p2l, ph, stbsp__top[et]); stbsp__ddmultlo(p2h, p2l, ph, pl, stbsp__top[et], stbsp__toperr[et]); ph = p2h; pl = p2l; } } } stbsp__ddrenorm(ph, pl); *ohi = ph; *olo = pl; } // given a float value, returns the significant bits in bits, and the position of the // decimal point in decimal_pos. +/-INF and NAN are specified by special values // returned in the decimal_pos parameter. // frac_digits is absolute normally, but if you want from first significant digits (got %g and %e), or in 0x80000000 static stbsp__int32 stbsp__real_to_str(char const **start, stbsp__uint32 *len, char *out, stbsp__int32 *decimal_pos, double value, stbsp__uint32 frac_digits) { double d; stbsp__int64 bits = 0; stbsp__int32 expo, e, ng, tens; d = value; STBSP__COPYFP(bits, d); expo = (stbsp__int32)((bits >> 52) & 2047); ng = (stbsp__int32)((stbsp__uint64) bits >> 63); if (ng) d = -d; if (expo == 2047) // is nan or inf? { *start = (bits & ((((stbsp__uint64)1) << 52) - 1)) ? "NaN" : "Inf"; *decimal_pos = STBSP__SPECIAL; *len = 3; return ng; } if (expo == 0) // is zero or denormal { if (((stbsp__uint64) bits << 1) == 0) // do zero { *decimal_pos = 1; *start = out; out[0] = '0'; *len = 1; return ng; } // find the right expo for denormals { stbsp__int64 v = ((stbsp__uint64)1) << 51; while ((bits & v) == 0) { --expo; v >>= 1; } } } // find the decimal exponent as well as the decimal bits of the value { double ph, pl; // log10 estimate - very specifically tweaked to hit or undershoot by no more than 1 of log10 of all expos 1..2046 tens = expo - 1023; tens = (tens < 0) ? ((tens * 617) / 2048) : (((tens * 1233) / 4096) + 1); // move the significant bits into position and stick them into an int stbsp__raise_to_power10(&ph, &pl, d, 18 - tens); // get full as much precision from double-double as possible stbsp__ddtoS64(bits, ph, pl); // check if we undershot if (((stbsp__uint64)bits) >= stbsp__tento19th) ++tens; } // now do the rounding in integer land frac_digits = (frac_digits & 0x80000000) ? ((frac_digits & 0x7ffffff) + 1) : (tens + frac_digits); if ((frac_digits < 24)) { stbsp__uint32 dg = 1; if ((stbsp__uint64)bits >= stbsp__powten[9]) dg = 10; while ((stbsp__uint64)bits >= stbsp__powten[dg]) { ++dg; if (dg == 20) goto noround; } if (frac_digits < dg) { stbsp__uint64 r; // add 0.5 at the right position and round e = dg - frac_digits; if ((stbsp__uint32)e >= 24) goto noround; r = stbsp__powten[e]; bits = bits + (r / 2); if ((stbsp__uint64)bits >= stbsp__powten[dg]) ++tens; bits /= r; } noround:; } // kill long trailing runs of zeros if (bits) { stbsp__uint32 n; for (;;) { if (bits <= 0xffffffff) break; if (bits % 1000) goto donez; bits /= 1000; } n = (stbsp__uint32)bits; while ((n % 1000) == 0) n /= 1000; bits = n; donez:; } // convert to string out += 64; e = 0; for (;;) { stbsp__uint32 n; char *o = out - 8; // do the conversion in chunks of U32s (avoid most 64-bit divides, worth it, constant denomiators be damned) if (bits >= 100000000) { n = (stbsp__uint32)(bits % 100000000); bits /= 100000000; } else { n = (stbsp__uint32)bits; bits = 0; } while (n) { out -= 2; *(stbsp__uint16 *)out = *(stbsp__uint16 *)&stbsp__digitpair.pair[(n % 100) * 2]; n /= 100; e += 2; } if (bits == 0) { if ((e) && (out[0] == '0')) { ++out; --e; } break; } while (out != o) { *--out = '0'; ++e; } } *decimal_pos = tens; *start = out; *len = e; return ng; } #undef stbsp__ddmulthi #undef stbsp__ddrenorm #undef stbsp__ddmultlo #undef stbsp__ddmultlos #undef STBSP__SPECIAL #undef STBSP__COPYFP #endif // STB_SPRINTF_NOFLOAT // clean up #undef stbsp__uint16 #undef stbsp__uint32 #undef stbsp__int32 #undef stbsp__uint64 #undef stbsp__int64 #undef STBSP__UNALIGNED #endif // STB_SPRINTF_IMPLEMENTATION /* ------------------------------------------------------------------------------ This software is available under 2 licenses -- choose whichever you prefer. ------------------------------------------------------------------------------ ALTERNATIVE A - MIT License Copyright (c) 2017 Sean Barrett 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. ------------------------------------------------------------------------------ ALTERNATIVE B - Public Domain (www.unlicense.org) This is free and unencumbered software released into the public domain. Anyone is free to copy, modify, publish, use, compile, sell, or distribute this software, either in source code form or as a compiled binary, for any purpose, commercial or non-commercial, and by any means. In jurisdictions that recognize copyright laws, the author or authors of this software dedicate any and all copyright interest in the software to the public domain. We make this dedication for the benefit of the public at large and to the detriment of our heirs and successors. We intend this dedication to be an overt act of relinquishment in perpetuity of all present and future rights to this software under copyright law. 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 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. ------------------------------------------------------------------------------ */ #line 0 #define g g2 #line 1 "3rd_xml.h" // original xml.h/xml.c files by tor andersson, public domain #ifndef xml_h #define xml_h /* Parse UTF-8 string and return the XML root node, or NULL if there is a parse error. */ struct xml *xml_parse(char *buf, int preserve_white, char **error); /* Free an XML node and all its children and siblings. */ void xml_free(struct xml *item); /* Navigate the XML tree. */ struct xml *xml_prev(struct xml *item); struct xml *xml_next(struct xml *item); struct xml *xml_up(struct xml *item); struct xml *xml_down(struct xml *item); /* Return true if the tag name matches. */ int xml_is_tag(struct xml *item, const char *name); /* Return tag name of XML node, or NULL if it's a text node. */ char *xml_tag(struct xml *item); /* Return the value of an attribute of an XML node, or NULL if the attribute doesn't exist. */ char *xml_att(struct xml *item, const char *att); /* Return the name of an attribute of an XML node, or NULL if the attribute doesn't exist. */ char *xml_att_name(struct xml *item, int index); //< @r-lyeh: new function /* Return the text content of an XML node, or NULL if the node is a tag. */ char *xml_text(struct xml *item); /* Find the first sibling with the given tag name (may be the same node). */ struct xml *xml_find(struct xml *item, const char *tag); /* Find the next sibling with the given tag name (never the same node). */ struct xml *xml_find_next(struct xml *item, const char *tag); /* Find the first child with the given tag name. */ struct xml *xml_find_down(struct xml *item, const char *tag); #endif #ifdef XML_C #include /* malloc, free, strtol */ #include /* memmove, strcmp */ static int runetochar(char *s, int c) { if (c < 0x80) { s[0] = c; return 1; } if (c < 0x800) { s[0] = 0xC0 | (c >> 6); s[1] = 0x80 | (c & 0x3F); return 2; } if (c > 0x10FFFF) c = 0xFFFD; if (c < 0x1000) { s[0] = 0xE0 | (c >> 12); s[1] = 0x80 | ((c >> 6) & 0x3F); s[2] = 0x80 | (c & 0x3F); return 3; } s[0] = 0xf0 | (c >> 18); s[1] = 0x80 | ((c >> 12) & 0x3F); s[2] = 0x80 | ((c >> 6) & 0x3F); s[3] = 0x80 | (c & 0x3F); return 4; } struct { struct xml *head; int preserve_white; int depth; } g; struct xmlatt { char name[40]; char *value; struct xmlatt *next; }; struct xml { char name[40]; char *text; struct xmlatt *atts; struct xml *up, *down, *tail, *prev, *next; }; struct xml *xml_prev(struct xml *item) { return item ? item->prev : NULL; } struct xml *xml_next(struct xml *item) { return item ? item->next : NULL; } struct xml *xml_up(struct xml *item) { return item ? item->up : NULL; } struct xml *xml_down(struct xml *item) { return item ? item->down : NULL; } char *xml_text(struct xml *item) { return item ? item->text : NULL; } char *xml_tag(struct xml *item) { return item && item->name[0] ? item->name : NULL; } int xml_is_tag(struct xml *item, const char *name) { if (!item) return 0; return !strcmp(item->name, name); } char *xml_att(struct xml *item, const char *name) { struct xmlatt *att; if (!item) return NULL; for (att = item->atts; att; att = att->next) if (!strcmp(att->name, name)) return att->value; return NULL; } char *xml_att_name(struct xml *item, int index) //< @r-lyeh: new function { int i; struct xmlatt *att; if (!item) return NULL; for (att = item->atts, i = 0; att && i < index; att = att->next, ++i) {} return att ? att->name : NULL; } struct xml *xml_find(struct xml *item, const char *tag) { while (item) { if (!strcmp(item->name, tag)) return item; item = item->next; } return NULL; } struct xml *xml_find_next(struct xml *item, const char *tag) { if (item) item = item->next; return xml_find(item, tag); } struct xml *xml_find_down(struct xml *item, const char *tag) { if (item) item = item->down; return xml_find(item, tag); } static void xml_free_att(struct xmlatt *att) { while (att) { struct xmlatt *next = att->next; if (att->value) free(att->value); free(att); att = next; } } void xml_free(struct xml *item) { while (item) { struct xml *next = item->next; if (item->text) free(item->text); if (item->atts) xml_free_att(item->atts); if (item->down) xml_free(item->down); free(item); item = next; } } static int xml_parse_entity(int *c, char *a) { char *b; if (a[1] == '#') { if (a[2] == 'x') *c = strtol(a + 3, &b, 16); else *c = strtol(a + 2, &b, 10); if (*b == ';') return b - a + 1; } else if (a[1] == 'l' && a[2] == 't' && a[3] == ';') { *c = '<'; return 4; } else if (a[1] == 'g' && a[2] == 't' && a[3] == ';') { *c = '>'; return 4; } else if (a[1] == 'a' && a[2] == 'm' && a[3] == 'p' && a[4] == ';') { *c = '&'; return 5; } else if (a[1] == 'a' && a[2] == 'p' && a[3] == 'o' && a[4] == 's' && a[5] == ';') { *c = '\''; return 6; } else if (a[1] == 'q' && a[2] == 'u' && a[3] == 'o' && a[4] == 't' && a[5] == ';') { *c = '"'; return 6; } *c = *a; return 1; } static int isname(int c) { return c == '.' || c == '-' || c == '_' || c == ':' || (c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'); } static int is_white(int c) { return c == ' ' || c == '\r' || c == '\n' || c == '\t'; } static void xml_emit_open_tag(char *a, char *b) { struct xml *head, *tail; char *ns; /* skip namespace prefix */ for (ns = a; ns < b; ++ns) if (*ns == ':') a = ns + 1; head = malloc(sizeof *head); if (b - a > (int)sizeof(head->name) - 1) b = a + sizeof(head->name) - 1; memmove(head->name, a, b - a); head->name[b - a] = 0; head->atts = NULL; head->text = NULL; head->up = g.head; head->down = NULL; head->prev = NULL; head->next = NULL; if (!g.head->down) { g.head->down = head; g.head->tail = head; } else { tail = g.head->tail; tail->next = head; head->prev = tail; g.head->tail = head; } g.head = head; g.depth++; } static void xml_emit_att_name(char *a, char *b) { struct xml *head = g.head; struct xmlatt *att; att = malloc(sizeof *att); if (b - a > (int)sizeof(att->name) - 1) b = a + sizeof(att->name) - 1; memmove(att->name, a, b - a); att->name[b - a] = 0; att->value = NULL; att->next = head->atts; head->atts = att; } static void xml_emit_att_value(char *a, char *b) { struct xml *head = g.head; struct xmlatt *att = head->atts; char *s; int c; /* entities are all longer than UTFmax so runetochar is safe */ s = att->value = malloc(b - a + 1); while (a < b) { if (*a == '&') { a += xml_parse_entity(&c, a); s += runetochar(s, c); } else { *s++ = *a++; } } *s = 0; } static void xml_emit_close_tag(void) { g.depth--; if (g.head->up) g.head = g.head->up; } static void xml_emit_text(char *a, char *b) { static char *empty = ""; struct xml *head; char *s; int c; /* Skip text outside the root tag */ if (g.depth == 0) return; /* Skip all-whitespace text nodes */ if (!g.preserve_white) { for (s = a; s < b; s++) if (!is_white(*s)) break; if (s == b) return; } xml_emit_open_tag(empty, empty); head = g.head; /* entities are all longer than UTFmax so runetochar is safe */ s = head->text = malloc(b - a + 1); while (a < b) { if (*a == '&') { a += xml_parse_entity(&c, a); s += runetochar(s, c); } else { *s++ = *a++; } } *s = 0; xml_emit_close_tag(); } static void xml_emit_cdata(char *a, char *b) { static char *empty = ""; struct xml *head; char *s; xml_emit_open_tag(empty, empty); head = g.head; s = head->text = malloc(b - a + 1); while (a < b) *s++ = *a++; *s = 0; xml_emit_close_tag(); } static char *xml_parse_imp(char *p) { char *mark; int quote; parse_text: mark = p; while (*p && *p != '<') ++p; if (mark != p) xml_emit_text(mark, p); if (*p == '<') { ++p; goto parse_element; } return NULL; parse_element: if (*p == '/') { ++p; goto parse_closing_element; } if (*p == '!') { ++p; goto parse_comment; } if (*p == '?') { ++p; goto parse_processing_instruction; } while (is_white(*p)) ++p; if (isname(*p)) goto parse_element_name; return "syntax error in element"; parse_comment: if (*p == '[') goto parse_cdata; if (*p == 'D' && !memcmp(p, "DOCTYPE", 7)) goto parse_declaration; if (*p == 'E' && !memcmp(p, "ENTITY", 6)) goto parse_declaration; if (*p++ != '-') return "syntax error in comment (') { p += 3; goto parse_text; } ++p; } return "end of data in comment"; parse_declaration: while (*p) if (*p++ == '>') goto parse_text; return "end of data in declaration"; parse_cdata: if (p[1] != 'C' || p[2] != 'D' || p[3] != 'A' || p[4] != 'T' || p[5] != 'A' || p[6] != '[') return "syntax error in CDATA section"; p += 7; mark = p; while (*p) { if (p[0] == ']' && p[1] == ']' && p[2] == '>') { xml_emit_cdata(mark, p); p += 3; goto parse_text; } ++p; } return "end of data in CDATA section"; parse_processing_instruction: while (*p) { if (p[0] == '?' && p[1] == '>') { p += 2; goto parse_text; } ++p; } return "end of data in processing instruction"; parse_closing_element: while (is_white(*p)) ++p; while (isname(*p)) ++p; while (is_white(*p)) ++p; if (*p != '>') return "syntax error in closing element"; xml_emit_close_tag(); ++p; goto parse_text; parse_element_name: mark = p; while (isname(*p)) ++p; xml_emit_open_tag(mark, p); if (*p == '>') { ++p; goto parse_text; } if (p[0] == '/' && p[1] == '>') { xml_emit_close_tag(); p += 2; goto parse_text; } if (is_white(*p)) goto parse_attributes; return "syntax error after element name"; parse_attributes: while (is_white(*p)) ++p; if (isname(*p)) goto parse_attribute_name; if (*p == '>') { ++p; goto parse_text; } if (p[0] == '/' && p[1] == '>') { xml_emit_close_tag(); p += 2; goto parse_text; } return "syntax error in attributes"; parse_attribute_name: mark = p; while (isname(*p)) ++p; xml_emit_att_name(mark, p); while (is_white(*p)) ++p; if (*p == '=') { ++p; goto parse_attribute_value; } return "syntax error after attribute name"; parse_attribute_value: while (is_white(*p)) ++p; quote = *p++; if (quote != '"' && quote != '\'') return "missing quote character"; mark = p; while (*p && *p != quote) ++p; if (*p == quote) { xml_emit_att_value(mark, p++); goto parse_attributes; } return "end of data in attribute value"; } struct xml *xml_parse(char *s, int preserve_white, char **errorp) { struct xml root, *node; char *error; memset(&root, 0, sizeof root); g.head = &root; g.preserve_white = preserve_white; g.depth = 0; error = xml_parse_imp(s); if (error) { if (errorp) *errorp = error; xml_free(root.down); return NULL; } for (node = root.down; node; node = node->next) node->up = NULL; if (errorp) *errorp = NULL; return root.down; } #endif #line 0 #undef g #line 1 "3rd_polychop.h" /* Progressive Mesh type Polygon Reduction Algorithm * * 1998: Original version by Stan Melax (c) 1998 * Permission to use any of this code wherever you want is granted.. * Although, please do acknowledge authorship if appropriate. * * 2014: Code style upgraded to be more consistent with graphics/gamedev conventions. Relicensed as MIT/PD. * Stan Melax: "Yes, this code can be licensed with the same license as the original. That should be fine." * * 2020: C version by Cloud Wu (c) 2020. Licensed as MIT/PD. */ static inline void array_find_and_remove(array(int) arr, int v) { for( int i = 0, end = array_count(arr); i < end; i++ ) if( arr[i] == v ) { array_erase(arr, i); --end; break; } } #include #include #include struct triangle_n { int vertex[3]; // the 3 points (id) that make this tri vec3 normal; // unit vector othogonal to this face }; struct vertex { vec3 position; // location of point in euclidean space array(int) neighbor; // adjacent vertices array(int) face; // adjacent triangles int id; // place of vertex in original Array int collapse; // candidate vertex (id) for collapse float objdist; // cached cost of collapsing edge }; struct mesh { struct vertex *v; struct triangle_n *t; int n_face; int n_vertex; }; // array static inline struct vertex *Vertex(struct mesh *M, int id) { return M->v + id; } static inline struct triangle_n *Triangle(struct mesh *M, int id) { return M->t + id; } static inline struct triangle_n *Face(struct mesh *M, struct vertex *v, int idx) { return M->t + v->face[idx]; } static void AddVertex(struct mesh *M, const float *v) { int id = M->n_vertex++; struct vertex * tmp = Vertex(M, id); tmp->position = ptr3(v); tmp->neighbor = NULL; tmp->face = NULL; tmp->id = id; tmp->collapse = -1; tmp->objdist = 0; } static void RemoveVertex(struct mesh *M, int id) { struct vertex * v = Vertex(M, id); ASSERT(v->id == id); ASSERT(array_count(v->face) == 0); for (int i=0;iface);i++) { struct vertex * nv = Vertex(M, v->face[i]); array_find_and_remove(nv->neighbor, id); } v->id = -1; // invalid vertex id array_free(v->neighbor); array_free(v->face); } static void ComputeNormal(struct mesh *M, struct triangle_n *t) { struct vertex * v0 = Vertex(M, t->vertex[0]); struct vertex * v1 = Vertex(M, t->vertex[1]); struct vertex * v2 = Vertex(M, t->vertex[2]); vec3 a = sub3(v1->position, v0->position); vec3 b = sub3(v2->position, v1->position); t->normal = norm3(cross3(a,b)); } static void AddNeighbor(struct mesh *M, int vid, int id) { struct vertex *v = Vertex(M, vid); for (int i=0;ineighbor);i++) { if (v->neighbor[i] == id) return; } array_push(v->neighbor, id); } static void AddTriangle(struct mesh *M, const int v[3]) { if (v[0] == v[1] || v[0] == v[2] || v[1] == v[2]) return; ASSERT(v[0] < M->n_vertex); ASSERT(v[1] < M->n_vertex); ASSERT(v[2] < M->n_vertex); int id = M->n_face++; struct triangle_n * tmp = Triangle(M, id); tmp->vertex[0] = v[0]; tmp->vertex[1] = v[1]; tmp->vertex[2] = v[2]; ComputeNormal(M, tmp); for(int i=0;i<3;i++) { struct vertex *obj = Vertex(M, v[i]); array_push(obj->face, id); } AddNeighbor(M, v[0], v[1]); AddNeighbor(M, v[0], v[2]); AddNeighbor(M, v[1], v[0]); AddNeighbor(M, v[1], v[2]); AddNeighbor(M, v[2], v[0]); AddNeighbor(M, v[2], v[1]); } static int HasVertex(struct triangle_n * t, int vid) { return (t->vertex[0] == vid || t->vertex[1] == vid || t->vertex[2] == vid); } static void RemoveIfNonNeighbor_(struct mesh *M, struct vertex *v, int id) { for (int i=0;ineighbor);i++) { if (v->neighbor[i] == id) { for (int j=0;jface);j++) { if (HasVertex(Face(M, v, j), id)) return; } // remove from neighbors array_erase(v->neighbor, i); return; } } } static void RemoveIfNonNeighbor(struct mesh *M, struct vertex *v0, struct vertex *v1) { if (v0 == NULL || v1 == NULL) return; RemoveIfNonNeighbor_(M, v0, v1->id); RemoveIfNonNeighbor_(M, v1, v0->id); } static void RemoveTriangle(struct mesh *M, int id) { struct triangle_n * face = Triangle(M, id); struct vertex * v[3]; for (int i=0;i<3;i++) { v[i] = Vertex(M, face->vertex[i]); if (v[i]->id < 0) v[i] = NULL; else { array_find_and_remove(v[i]->face, id); } } RemoveIfNonNeighbor(M, v[0], v[1]); RemoveIfNonNeighbor(M, v[1], v[2]); RemoveIfNonNeighbor(M, v[2], v[0]); } static void ReplaceVertex(struct mesh *M, int faceid, int oldid, int newid) { struct triangle_n * face = Triangle(M, faceid); ASSERT(oldid >=0 && newid >= 0); ASSERT(HasVertex(face, oldid)); ASSERT(!HasVertex(face, newid)); if(oldid==face->vertex[0]){ face->vertex[0]=newid; } else if(oldid==face->vertex[1]){ face->vertex[1]=newid; } else { face->vertex[2]=newid; } struct vertex *vold = Vertex(M, oldid); struct vertex *vnew = Vertex(M, newid); array_find_and_remove(vold->face, faceid); array_push(vnew->face, faceid); RemoveIfNonNeighbor(M, vold, Vertex(M, face->vertex[0])); RemoveIfNonNeighbor(M, vold, Vertex(M, face->vertex[1])); RemoveIfNonNeighbor(M, vold, Vertex(M, face->vertex[2])); AddNeighbor(M, face->vertex[0], face->vertex[1]); AddNeighbor(M, face->vertex[0], face->vertex[2]); AddNeighbor(M, face->vertex[1], face->vertex[0]); AddNeighbor(M, face->vertex[1], face->vertex[2]); AddNeighbor(M, face->vertex[2], face->vertex[0]); AddNeighbor(M, face->vertex[2], face->vertex[1]); ComputeNormal(M, face); } static void MeshInit(struct mesh *M, int vert_n, int tri_n) { M->n_face = 0; M->n_vertex = 0; M->v = (struct vertex *)MALLOC(vert_n * sizeof(struct vertex)); M->t = (struct triangle_n *)MALLOC(tri_n * sizeof(struct triangle)); } static void MeshFree(struct mesh *M) { FREE(M->v); FREE(M->t); } static float ComputeEdgeCollapseCost(struct mesh *M, struct vertex *u, int vid) { // if we collapse edge uv by moving u to v then how // much different will the model change, i.e. how much "error". // Texture, vertex normal, and border vertex code was removed // to keep this demo as simple as possible. // The method of determining cost was designed in order // to exploit small and coplanar regions for // effective polygon reduction. // Is is possible to add some checks here to see if "folds" // would be generated. i.e. normal of a remaining face gets // flipped. I never seemed to run into this problem and // therefore never added code to detect this case. struct vertex *v = Vertex(M, vid); vec3 tmp = sub3(v->position, u->position); float edgelength = len3(tmp); float curvature=0; // find the "sides" triangles that are on the edge uv array(int) sides = 0; for (int i = 0; iface); i++) { if (HasVertex(Face(M, u, i), vid)) { array_push(sides, u->face[i]); } } // use the triangle facing most away from the sides // to determine our curvature term for (int i = 0; iface); i++) { float mincurv=1; // curve for face i and closer side to it for (int j = 0; jface[i])->normal, Triangle(M, sides[j])->normal); // use dot product of face normals. float t = (1-dotprod)/2.0f; if (t < mincurv) { mincurv = t; } } if (mincurv > curvature) curvature = mincurv; } array_free(sides); // the more coplanar the lower the curvature term return edgelength * curvature; } static void ComputeEdgeCostAtVertex(struct mesh *M, struct vertex *v) { // compute the edge collapse cost for all edges that start // from vertex v. Since we are only interested in reducing // the object by selecting the min cost edge at each step, we // only cache the cost of the least cost edge at this vertex // (in member variable collapse) as well as the value of the // cost (in member variable objdist). if (array_count(v->neighbor) == 0) { // v doesn't have neighbors so it costs nothing to collapse v->collapse=-1; v->objdist=-0.01f; return; } v->objdist = 1000000; v->collapse=-1; // search all neighboring edges for "least cost" edge for (int i = 0; ineighbor); i++) { float dist = ComputeEdgeCollapseCost(M, v, v->neighbor[i]); if(distobjdist) { v->collapse=v->neighbor[i]; // candidate for edge collapse v->objdist=dist; // cost of the collapse } } } static void ComputeAllEdgeCollapseCosts(struct mesh *M) { // For all the edges, compute the difference it would make // to the model if it was collapsed. The least of these // per vertex is cached in each vertex object. for (int i = 0; in_vertex; i++) { ComputeEdgeCostAtVertex(M, Vertex(M, i)); } } static void Collapse(struct mesh *M, int uid, int vid) { // Collapse the edge uv by moving vertex u onto v // Actually remove tris on uv, then update tris that // have u to have v, and then remove u. struct vertex *u = Vertex(M, uid); if(vid < 0) { // u is a vertex all by itself so just delete it RemoveVertex(M, uid); return; } array(int) tmp = 0; // make tmp a Array of all the neighbors of u for (int i = 0; ineighbor); i++) { array_push(tmp, u->neighbor[i]); } // delete triangles on edge uv: for( int i = array_count(u->face); i--; ) { if (HasVertex(Face(M, u, i), vid)) { RemoveTriangle(M, u->face[i]); } } // update remaining triangles to have v instead of u for( int i = array_count(u->face); i--; ) { ReplaceVertex(M, u->face[i], uid, vid); } RemoveVertex(M, uid); // recompute the edge collapse costs for neighboring vertices for (int i = 0; in_vertex; i++) { struct vertex *v = Vertex(M, i); if (v->id >=0) { if (mn == NULL || v->objdist < mn->objdist) { mn = v; } } } return mn; } /* * The function ProgressiveMesh() takes a model in an "indexed face * set" sort of way. i.e. Array of vertices and Array of triangles. * The function then does the polygon reduction algorithm * internally and reduces the model all the way down to 0 * vertices and then returns the order in which the * vertices are collapsed and to which neighbor each vertex * is collapsed to. More specifically the returned "permutation" * indicates how to reorder your vertices so you can render * an object by using the first n vertices (for the n * vertex version). After permuting your vertices, the * map Array indicates to which vertex each vertex is collapsed to. */ API void ProgressiveMesh(int vert_n, int vert_stride, const float *v, int tri_n, const int *tri, int *map, int *permutation) { struct mesh M; MeshInit(&M, vert_n, tri_n); // put input data into our data structures M const char * tmp = (const char *)v; for (int i=0;i=0; i--) { // get the next vertex to collapse struct vertex *mn = MinimumCostEdge(&M); // keep track of this vertex, i.e. the collapse ordering permutation[mn->id] = i; // keep track of vertex to which we collapse to map[i] = mn->collapse; // Collapse this edge Collapse(&M, mn->id, mn->collapse); } // reorder the map Array based on the collapse ordering for (int i = 0; i